From fc9b9d3fe78f4865181b7d34d0ae68c10858f0d3 Mon Sep 17 00:00:00 2001 From: DuckLabs GitHub Bot Date: Fri, 16 Oct 2026 04:19:22 +0000 Subject: [PATCH 1/6] Update vendored DuckDB sources to e08224d653f --- CMakeLists.txt | 12 +- .../aggregate/algebraic/avg.cpp | 32 +- .../aggregate/distributive/approx_count.cpp | 94 +- .../aggregate/distributive/arg_min_max.cpp | 18 +- .../aggregate/distributive/bitagg.cpp | 6 +- .../aggregate/distributive/bitstring_agg.cpp | 11 +- .../aggregate/distributive/kurtosis.cpp | 5 +- .../aggregate/distributive/sum.cpp | 96 +- .../aggregate/holistic/approx_top_k.cpp | 24 +- .../holistic/approximate_quantile.cpp | 107 +- .../aggregate/holistic/lttb.cpp | 5 +- .../core_functions/aggregate/holistic/mad.cpp | 18 +- .../aggregate/holistic/mode.cpp | 20 +- .../aggregate/holistic/quantile.cpp | 16 +- .../aggregate/holistic/reservoir_quantile.cpp | 50 +- .../aggregate/nested/histogram.cpp | 7 +- .../variant/variant_group_object.cpp | 14 +- .../core_functions_extension.cpp | 2 + .../aggregate/algebraic/covar.hpp | 7 +- .../aggregate/algebraic/stddev.hpp | 7 +- .../aggregate/distributive_functions.hpp | 6 +- .../aggregate/histogram_helpers.hpp | 8 - .../aggregate/holistic_functions.hpp | 4 +- .../aggregate/nested_functions.hpp | 2 +- .../aggregate/quantile_sort_tree.hpp | 18 +- .../aggregate/quantile_state.hpp | 5 +- .../aggregate/regression/regr_count.hpp | 8 +- .../core_functions/aggregate/sum_helpers.hpp | 2 +- .../include/core_functions/array_kernels.hpp | 43 +- .../core_functions/scalar/date_functions.hpp | 4 +- .../core_functions/scalar/debug_functions.hpp | 2 +- .../scalar/generic_functions.hpp | 4 +- .../core_functions/scalar/list_functions.hpp | 14 +- .../core_functions/scalar/math_functions.hpp | 2 +- .../scalar/operators_functions.hpp | 4 +- .../scalar/string_functions.hpp | 4 +- .../scalar/array/array_functions.cpp | 83 +- .../scalar/array/array_value.cpp | 11 +- .../core_functions/scalar/bit/bitstring.cpp | 4 +- .../core_functions/scalar/blob/encode.cpp | 2 +- .../core_functions/scalar/date/date_part.cpp | 43 +- .../core_functions/scalar/date/date_trunc.cpp | 16 +- .../core_functions/scalar/debug/index_key.cpp | 50 +- .../scalar/debug/vector_type.cpp | 1 + .../scalar/enum/enum_functions.cpp | 62 +- .../core_functions/scalar/generic/alias.cpp | 35 +- .../core_functions/scalar/generic/binning.cpp | 14 +- .../scalar/generic/cast_to_type.cpp | 3 +- .../scalar/generic/current_setting.cpp | 2 + .../core_functions/scalar/generic/hash.cpp | 2 +- .../core_functions/scalar/generic/least.cpp | 28 +- .../scalar/generic/system_functions.cpp | 11 +- .../scalar/generic/type_functions.cpp | 8 +- .../scalar/list/array_slice.cpp | 101 +- .../scalar/list/list_aggregates.cpp | 257 +- .../core_functions/scalar/list/list_sort.cpp | 162 +- .../core_functions/scalar/list/list_value.cpp | 18 +- .../core_functions/scalar/map/cardinality.cpp | 19 +- .../core_functions/scalar/map/map_concat.cpp | 2 +- .../core_functions/scalar/map/switch.cpp | 108 +- .../core_functions/scalar/math/numeric.cpp | 115 +- .../core_functions/scalar/string/instr.cpp | 15 +- .../core_functions/scalar/string/pad.cpp | 40 +- .../scalar/string/parse_path.cpp | 223 +- .../core_functions/scalar/string/printf.cpp | 20 +- .../core_functions/scalar/string/replace.cpp | 13 +- .../core_functions/scalar/string/to_base.cpp | 23 +- .../scalar/string/translate.cpp | 6 + .../core_functions/scalar/string/trim.cpp | 38 +- .../scalar/struct/struct_insert.cpp | 46 +- .../scalar/struct/struct_update.cpp | 149 +- .../scalar/struct/struct_values.cpp | 11 +- .../scalar/union/union_extract.cpp | 27 +- .../core_functions/scalar/union/union_tag.cpp | 7 +- .../scalar/union/union_value.cpp | 23 +- .../icu/collation/collation_collator.cpp | 6 + .../icu/collation/collation_normalizer.cpp | 2 +- .../collation/include/collation_collator.hpp | 8 + .../extension/icu/datetime/calendar.cpp | 7 +- src/duckdb/extension/icu/datetime/chinese.cpp | 9 +- src/duckdb/extension/icu/datetime/coptic.cpp | 2 +- .../icu/datetime/generated/tz_data.cpp | 3742 ++--- src/duckdb/extension/icu/datetime/islamic.cpp | 4 +- .../extension/icu/datetime/tz_loader.cpp | 2 +- src/duckdb/extension/icu/icu-current.cpp | 1 + src/duckdb/extension/icu/icu-dateadd.cpp | 30 +- src/duckdb/extension/icu/icu-datefunc.cpp | 57 +- src/duckdb/extension/icu/icu-datepart.cpp | 52 +- src/duckdb/extension/icu/icu-datesub.cpp | 14 + src/duckdb/extension/icu/icu-datetrunc.cpp | 15 + src/duckdb/extension/icu/icu-makedate.cpp | 33 +- src/duckdb/extension/icu/icu-strptime.cpp | 74 +- src/duckdb/extension/icu/icu-table-range.cpp | 75 +- src/duckdb/extension/icu/icu-timebucket.cpp | 33 +- src/duckdb/extension/icu/icu-timezone.cpp | 53 +- src/duckdb/extension/icu/icu_extension.cpp | 14 +- .../extension/icu/include/icu-casts.hpp | 7 +- .../extension/icu/include/icu-datefunc.hpp | 9 + .../extension/json/include/json_common.hpp | 4 + .../extension/json/include/json_functions.hpp | 1 + .../extension/json/include/json_reader.hpp | 8 +- .../extension/json/include/json_scan.hpp | 4 +- .../extension/json/include/json_stack.hpp | 24 + .../extension/json/include/json_structure.hpp | 6 +- src/duckdb/extension/json/json_common.cpp | 77 + .../extension/json/json_deserializer.cpp | 6 +- src/duckdb/extension/json/json_extension.cpp | 7 +- src/duckdb/extension/json/json_functions.cpp | 17 +- .../json/json_functions/json_array_length.cpp | 1 + .../json/json_functions/json_create.cpp | 108 +- .../json/json_functions/json_deep_merge.cpp | 32 +- .../json/json_functions/json_exists.cpp | 1 + .../json/json_functions/json_extract.cpp | 4 + .../json/json_functions/json_keys.cpp | 1 + .../json/json_functions/json_merge_patch.cpp | 60 +- .../json_functions/json_merge_patch_diff.cpp | 18 +- .../json/json_functions/json_normalize.cpp | 17 +- .../json_functions/json_serialize_plan.cpp | 2 +- .../json_functions/json_serialize_sql.cpp | 14 +- .../json/json_functions/json_strip_nulls.cpp | 54 +- .../json/json_functions/json_structure.cpp | 38 +- .../json/json_functions/json_table_in_out.cpp | 58 +- .../json/json_functions/json_transform.cpp | 42 +- .../json/json_functions/json_type.cpp | 1 + .../json/json_functions/json_value.cpp | 2 + .../json/json_functions/read_json.cpp | 3 +- .../json_functions/read_single_json_file.cpp | 36 +- src/duckdb/extension/json/json_reader.cpp | 65 +- src/duckdb/extension/json/json_scan.cpp | 73 +- src/duckdb/extension/json/json_serializer.cpp | 3 +- .../loader/dummy_static_extension_loader.cpp | 20 - .../loader/static_extension_autoregister.cpp | 16 + .../extension/parquet/column_reader.cpp | 103 +- .../extension/parquet/column_writer.cpp | 39 +- .../delta_length_byte_array_decoder.cpp | 49 +- .../parquet/include/column_reader.hpp | 4 +- .../parquet/include/column_writer.hpp | 5 + .../delta_length_byte_array_decoder.hpp | 2 +- .../parquet/include/parquet_column_schema.hpp | 48 + .../parquet/include/parquet_dbp_decoder.hpp | 5 +- .../parquet/include/parquet_dlba_encoder.hpp | 18 +- .../parquet/include/parquet_int96.hpp | 26 + .../include/parquet_multi_file_info.hpp | 33 +- .../parquet/include/parquet_reader.hpp | 51 +- .../parquet/include/parquet_statistics.hpp | 3 + .../parquet/include/parquet_timestamp.hpp | 8 +- .../parquet/include/parquet_writer.hpp | 1 + .../reader/expression_column_reader.hpp | 6 +- .../include/reader/list_column_reader.hpp | 4 +- .../reader/row_number_column_reader.hpp | 6 +- .../include/reader/struct_column_reader.hpp | 2 +- .../include/reader/variant_column_reader.hpp | 2 +- .../parquet/include/thrift_tools.hpp | 23 +- .../include/writer/list_column_writer.hpp | 4 + .../writer/parquet_write_operators.hpp | 16 +- .../include/writer/parquet_write_stats.hpp | 8 +- .../writer/templated_column_writer.hpp | 27 +- .../include/writer/variant_column_writer.hpp | 14 +- .../extension/parquet/parquet_crypto.cpp | 3 + .../extension/parquet/parquet_extension.cpp | 72 +- .../extension/parquet/parquet_field_id.cpp | 69 +- .../parquet/parquet_file_metadata_cache.cpp | 12 +- .../extension/parquet/parquet_geometry.cpp | 12 +- .../extension/parquet/parquet_int96.cpp | 66 + .../extension/parquet/parquet_metadata.cpp | 66 +- .../parquet/parquet_multi_file_info.cpp | 482 +- .../extension/parquet/parquet_reader.cpp | 604 +- .../extension/parquet/parquet_shredding.cpp | 17 +- .../extension/parquet/parquet_statistics.cpp | 112 +- .../extension/parquet/parquet_timestamp.cpp | 62 +- .../extension/parquet/parquet_writer.cpp | 63 +- .../parquet/read_single_parquet_file.cpp | 576 + .../parquet/reader/decimal_column_reader.cpp | 9 +- .../parquet/reader/list_column_reader.cpp | 6 +- .../reader/row_number_column_reader.cpp | 9 + .../parquet/reader/string_column_reader.cpp | 7 +- .../parquet/reader/struct_column_reader.cpp | 7 +- .../variant/parquet_variant_shredding.cpp | 2 +- .../parquet/reader/variant_column_reader.cpp | 24 +- .../extension/parquet/serialize_parquet.cpp | 10 +- .../parquet/writer/array_column_writer.cpp | 3 + .../parquet/writer/list_column_writer.cpp | 5 + .../writer/primitive_column_writer.cpp | 16 +- .../parquet/writer/struct_column_writer.cpp | 8 +- .../writer/variant/convert_variant.cpp | 970 +- ...nerated_extension_loader_package_build.cpp | 197 +- src/duckdb/src/catalog/catalog.cpp | 218 +- .../aggregate_function_catalog_entry.cpp | 6 +- .../column_dependency_manager.cpp | 9 + .../catalog_entry/duck_index_entry.cpp | 9 +- .../catalog_entry/duck_schema_entry.cpp | 93 +- .../catalog_entry/duck_table_entry.cpp | 200 +- .../catalog_entry/index_catalog_entry.cpp | 1 + .../catalog_entry/macro_catalog_entry.cpp | 8 + .../scalar_function_catalog_entry.cpp | 4 +- .../catalog_entry/schema_catalog_entry.cpp | 56 +- .../catalog_entry/sequence_catalog_entry.cpp | 31 +- .../catalog_entry/table_catalog_entry.cpp | 40 +- .../table_function_catalog_entry.cpp | 14 +- .../catalog_entry/view_catalog_entry.cpp | 39 +- .../window_function_catalog_entry.cpp | 6 +- .../src/catalog/catalog_entry_retriever.cpp | 5 +- .../src/catalog/catalog_search_path.cpp | 41 +- src/duckdb/src/catalog/catalog_set.cpp | 9 +- .../src/catalog/default/default_functions.cpp | 17 +- .../default/default_table_functions.cpp | 54 +- .../src/catalog/default/default_types.cpp | 59 +- .../src/catalog/default/default_views.cpp | 4 +- src/duckdb/src/catalog/dependency_manager.cpp | 52 +- src/duckdb/src/catalog/duck_catalog.cpp | 130 +- src/duckdb/src/catalog/entry_lookup_info.cpp | 9 +- src/duckdb/src/common/adbc/adbc.cpp | 650 +- .../src/common/adbc/nanoarrow/schema.cpp | 2 +- src/duckdb/src/common/allocator/allocator.cpp | 44 + .../common/allocator/allocator_jemalloc.cpp | 17 + .../common/allocator/allocator_standard.cpp | 4 + .../src/common/arrow/arrow_appender.cpp | 19 +- src/duckdb/src/common/arrow/arrow_format.cpp | 225 + .../src/common/arrow/arrow_merge_event.cpp | 144 - .../src/common/arrow/arrow_query_result.cpp | 55 - .../src/common/arrow/arrow_type_extension.cpp | 222 + src/duckdb/src/common/arrow/arrow_util.cpp | 64 - src/duckdb/src/common/arrow/arrow_wrapper.cpp | 134 - .../arrow/physical_arrow_batch_collector.cpp | 37 - .../common/arrow/physical_arrow_collector.cpp | 138 - src/duckdb/src/common/bind_helpers.cpp | 31 +- src/duckdb/src/common/clustered_aggregate.cpp | 41 +- .../src/common/compressed_file_system.cpp | 52 +- src/duckdb/src/common/csv_writer.cpp | 17 +- .../src/common/encryption_functions.cpp | 13 +- src/duckdb/src/common/enum_util.cpp | 579 +- .../src/common/enums/compression_type.cpp | 2 +- .../src/common/enums/statement_type.cpp | 3 + src/duckdb/src/common/exception.cpp | 1 - src/duckdb/src/common/file_buffer.cpp | 6 + src/duckdb/src/common/file_system.cpp | 8 + src/duckdb/src/common/filename_pattern.cpp | 5 + src/duckdb/src/common/fsst.cpp | 19 +- src/duckdb/src/common/gzip_file_system.cpp | 199 +- src/duckdb/src/common/hive_partitioning.cpp | 38 +- src/duckdb/src/common/identifier.cpp | 5 + src/duckdb/src/common/json_document.cpp | 8 + src/duckdb/src/common/local_file_system.cpp | 178 +- ...ra_type_info.cpp => logical_type_info.cpp} | 248 +- .../common/multi_file/base_file_reader.cpp | 5 + .../multi_file/multi_file_column_mapper.cpp | 143 +- .../common/multi_file/multi_file_function.cpp | 9 +- .../src/common/multi_file/multi_file_list.cpp | 15 +- .../common/multi_file/multi_file_reader.cpp | 788 +- .../multi_file/table_function_multi_file.cpp | 547 +- .../src/common/multi_file/union_by_name.cpp | 2 +- src/duckdb/src/common/open_file_info.cpp | 135 +- src/duckdb/src/common/opener_file_system.cpp | 9 +- .../src/common/operator/cast_operators.cpp | 96 +- src/duckdb/src/common/optional_idx.cpp | 4 - .../progress_bar/unscented_kalman_filter.cpp | 287 - .../serializer/async_memory_governor.cpp | 8 +- .../common/serializer/async_task_queue.cpp | 20 +- .../common/serializer/async_write_queue.cpp | 51 +- src/duckdb/src/common/sort/full_sort.cpp | 23 +- src/duckdb/src/common/sort/hashed_sort.cpp | 34 +- src/duckdb/src/common/sort/natural_sort.cpp | 8 +- src/duckdb/src/common/sort/sort.cpp | 108 +- src/duckdb/src/common/sort/sorted_run.cpp | 18 +- .../src/common/sort/sorted_run_merger.cpp | 27 +- src/duckdb/src/common/string_util.cpp | 45 +- src/duckdb/src/common/tree_renderer.cpp | 2 + .../tree_renderer/compact_tree_renderer.cpp | 134 + .../tree_renderer/html_tree_renderer.cpp | 2 +- src/duckdb/src/common/types.cpp | 981 +- .../common/types/batched_data_collection.cpp | 11 - src/duckdb/src/common/types/bignum.cpp | 287 +- .../types/column/column_data_allocator.cpp | 27 +- .../types/column/column_data_collection.cpp | 16 +- .../types/column/column_data_consumer.cpp | 1 - .../src/common/types/conflict_manager.cpp | 30 +- src/duckdb/src/common/types/data_chunk.cpp | 15 +- src/duckdb/src/common/types/date.cpp | 11 +- src/duckdb/src/common/types/geometry.cpp | 331 +- src/duckdb/src/common/types/hugeint.cpp | 259 +- src/duckdb/src/common/types/hyperloglog.cpp | 6 + src/duckdb/src/common/types/interval.cpp | 4 +- .../types/row/partitioned_tuple_data.cpp | 8 + .../common/types/row/tuple_data_allocator.cpp | 8 + .../types/row/tuple_data_collection.cpp | 31 +- .../types/row/tuple_data_scatter_gather.cpp | 9 +- src/duckdb/src/common/types/time.cpp | 35 +- src/duckdb/src/common/types/timestamp.cpp | 52 +- src/duckdb/src/common/types/type_manager.cpp | 20 +- src/duckdb/src/common/types/value.cpp | 294 +- .../variant/parquet_variant_encoding.cpp | 910 + .../variant/parquet_variant_iterator.cpp | 62 +- .../types}/variant/variant_binary_decoder.cpp | 9 +- src/duckdb/src/common/types/vector.cpp | 123 +- src/duckdb/src/common/types/vector_buffer.cpp | 39 +- src/duckdb/src/common/types/vector_cache.cpp | 10 + .../comparison_operations.cpp | 2 +- .../src/common/vector/constant_vector.cpp | 60 +- .../src/common/vector/dictionary_vector.cpp | 75 +- src/duckdb/src/common/vector/list_vector.cpp | 2 +- .../src/common/vector/sequence_vector.cpp | 19 + .../src/common/vector/shredded_vector.cpp | 33 + .../src/common/vector/struct_vector.cpp | 2 +- .../comparison_operators.cpp | 7 + src/duckdb/src/common/virtual_file_system.cpp | 24 +- .../src/execution/aggregate_hashtable.cpp | 117 +- .../src/execution/column_binding_resolver.cpp | 31 +- .../src/execution/expression_executor.cpp | 4 +- .../expression_executor/execute_case.cpp | 54 +- .../expression_executor/execute_function.cpp | 43 +- .../expression_executor/execute_operator.cpp | 61 + src/duckdb/src/execution/ie_join_union.cpp | 363 + .../execution/index/fixed_size_allocator.cpp | 1 + .../src/execution/index/fixed_size_buffer.cpp | 6 +- .../src/execution/index/unbound_index.cpp | 4 +- src/duckdb/src/execution/join_hashtable.cpp | 231 +- .../execution/mark_join_row_comparison.cpp | 27 + .../nested_loop_join_mark.cpp | 18 +- .../aggregate/physical_limited_distinct.cpp | 24 +- .../physical_perfecthash_aggregate.cpp | 19 +- .../physical_ungrouped_aggregate.cpp | 17 + .../operator/aggregate/physical_window.cpp | 127 +- .../csv_scanner/buffer_manager/csv_buffer.cpp | 25 +- .../buffer_manager/csv_file_handle.cpp | 2 +- .../csv_scanner/encode/csv_encoder.cpp | 6 +- .../scanner/string_value_scanner.cpp | 49 +- .../csv_scanner/sniffer/csv_sniffer.cpp | 8 +- .../table_function/csv_file_scanner.cpp | 41 +- .../table_function/csv_multi_file_info.cpp | 490 - .../table_function/csv_schema_discovery.cpp | 115 + .../table_function/global_csv_state.cpp | 66 +- .../table_function/read_single_csv_file.cpp | 509 + .../operator/csv_scanner/util/csv_error.cpp | 24 +- .../csv_scanner/util/csv_reader_options.cpp | 40 +- .../csv_scanner/util/csv_validator.cpp | 2 +- .../operator/filter/physical_filter.cpp | 15 +- .../helper/launch_external_resource.cpp | 40 +- .../helper/physical_batch_collector.cpp | 56 - .../operator/helper/physical_connect.cpp | 99 +- .../helper/physical_external_resource.cpp | 13 +- .../operator/helper/physical_limit.cpp | 55 +- .../helper/physical_limit_percent.cpp | 40 +- .../operator/helper/physical_pragma.cpp | 2 +- .../helper/physical_reservoir_sample.cpp | 42 +- .../operator/helper/physical_reset.cpp | 5 +- .../helper/physical_result_collector.cpp | 28 +- .../operator/helper/physical_result_sink.cpp | 263 +- .../operator/helper/physical_set.cpp | 13 +- .../operator/helper/physical_transaction.cpp | 79 +- .../helper/physical_verify_vector.cpp | 44 +- .../operator/join/outer_join_marker.cpp | 17 +- .../operator/join/physical_asof_join.cpp | 43 +- .../join/physical_blockwise_nl_join.cpp | 4 + .../operator/join/physical_hash_join.cpp | 502 +- .../operator/join/physical_iejoin.cpp | 575 +- .../join/physical_nested_loop_join.cpp | 117 +- .../join/physical_piecewise_merge_join.cpp | 14 + .../join/physical_positional_join.cpp | 20 +- .../operator/join/physical_range_join.cpp | 2 +- .../operator/order/physical_top_n.cpp | 34 +- .../operator/persistent/copy_batch_slicer.cpp | 110 + .../persistent/copy_output_lifecycle.cpp | 16 +- .../operator/persistent/csv_rejects_table.cpp | 36 +- .../physical_batch_copy_to_file.cpp | 67 +- .../persistent/physical_batch_insert.cpp | 4 +- .../persistent/physical_copy_database.cpp | 5 +- .../persistent/physical_copy_to_file.cpp | 610 +- .../operator/persistent/physical_delete.cpp | 163 +- .../operator/persistent/physical_export.cpp | 4 + .../operator/persistent/physical_insert.cpp | 21 +- .../persistent/physical_merge_into.cpp | 4 +- .../operator/persistent/physical_update.cpp | 15 +- .../projection/physical_projection.cpp | 10 + .../physical_tableinout_function.cpp | 15 +- .../scan/physical_column_data_scan.cpp | 99 +- .../operator/scan/physical_table_scan.cpp | 24 +- .../operator/schema/physical_attach.cpp | 98 +- .../operator/schema/physical_create_index.cpp | 14 +- .../execution/operator/set/physical_cte.cpp | 11 +- .../operator/set/physical_recursive_cte.cpp | 121 +- .../set/physical_recursive_cte_runtime.cpp | 38 +- .../set/physical_recursive_cte_state.cpp | 7 + .../execution/operator/set/physical_union.cpp | 13 +- .../execution/perfect_aggregate_hashtable.cpp | 7 +- .../physical_plan/plan_asof_join.cpp | 34 +- .../physical_plan/plan_comparison_join.cpp | 4 +- .../physical_plan/plan_copy_to_file.cpp | 20 +- .../physical_plan/plan_create_index.cpp | 17 +- .../execution/physical_plan/plan_distinct.cpp | 9 +- .../src/execution/physical_plan/plan_get.cpp | 27 +- .../physical_plan/plan_merge_into.cpp | 6 +- .../physical_plan/plan_projection.cpp | 2 +- .../physical_plan/plan_recursive_cte.cpp | 5 +- .../physical_plan/plan_set_operation.cpp | 23 +- .../execution/physical_plan/plan_window.cpp | 6 +- .../src/execution/radix_ht_adaptivity.cpp | 107 + .../execution/radix_partitioned_hashtable.cpp | 398 +- .../src/execution/sample/reservoir_sample.cpp | 15 +- .../function/aggregate/distributive/count.cpp | 19 +- .../aggregate/distributive/first_last_any.cpp | 26 +- .../aggregate/distributive/minmax.cpp | 49 +- .../aggregate/sorted_aggregate_function.cpp | 118 +- .../src/function/aggregate_function.cpp | 38 +- .../aggregate_state_serialization.cpp | 13 + .../src/function/built_in_functions.cpp | 2 +- .../builtin_function_lookup.cpp | 41 +- src/duckdb/src/function/cast/array_casts.cpp | 24 +- src/duckdb/src/function/cast/bignum_casts.cpp | 53 +- src/duckdb/src/function/cast/blob_cast.cpp | 5 +- .../src/function/cast/cast_function_set.cpp | 36 +- .../src/function/cast/cast_statistics.cpp | 16 +- .../src/function/cast/default_casts.cpp | 4 +- src/duckdb/src/function/cast/enum_casts.cpp | 11 +- src/duckdb/src/function/cast/geo_casts.cpp | 26 + .../function/cast/nested_to_varchar_cast.cpp | 2 +- src/duckdb/src/function/cast/string_cast.cpp | 40 +- src/duckdb/src/function/cast/time_casts.cpp | 9 +- .../src/function/cast/union/from_struct.cpp | 61 +- src/duckdb/src/function/cast/union_casts.cpp | 59 +- .../function/cast/variant/from_variant.cpp | 100 +- .../src/function/cast/variant/to_variant.cpp | 14 +- src/duckdb/src/function/cast_rules.cpp | 12 +- src/duckdb/src/function/copy_blob.cpp | 3 + src/duckdb/src/function/encoding_function.cpp | 7 + src/duckdb/src/function/function.cpp | 481 +- src/duckdb/src/function/function_binder.cpp | 1080 +- src/duckdb/src/function/function_list.cpp | 2 + src/duckdb/src/function/function_set.cpp | 19 - .../src/function/pragma/pragma_functions.cpp | 34 +- .../src/function/pragma/pragma_queries.cpp | 20 +- src/duckdb/src/function/pragma_function.cpp | 34 +- .../function/scalar/comparison/between.cpp | 6 +- .../compress_geometry.cpp | 10 + .../compress_integral.cpp | 10 + .../compress_string.cpp | 10 + .../compressed_materialization_utils.cpp | 51 + .../src/function/scalar/create_sort_key.cpp | 89 +- .../scalar/generic/constant_or_null.cpp | 2 +- .../function/scalar/generic/getvariable.cpp | 2 + .../src/function/scalar/generic/invoke.cpp | 18 +- .../scalar/geometry/geometry_functions.cpp | 16 +- .../src/function/scalar/list/list_extract.cpp | 3 + .../src/function/scalar/list/list_resize.cpp | 43 +- .../src/function/scalar/list/list_select.cpp | 14 +- .../src/function/scalar/list/list_zip.cpp | 2 +- .../src/function/scalar/map/map_contains.cpp | 41 + .../function/scalar/operator/arithmetic.cpp | 245 +- .../src/function/scalar/operator/cast.cpp | 15 +- .../scalar/operator/decimal_division.cpp | 30 +- .../src/function/scalar/operator/multiply.cpp | 12 +- .../src/function/scalar/sequence/nextval.cpp | 18 +- .../src/function/scalar/strftime_format.cpp | 60 +- .../function/scalar/string/caseconvert.cpp | 9 +- .../src/function/scalar/string/concat.cpp | 77 +- .../src/function/scalar/string/concat_ws.cpp | 2 +- .../src/function/scalar/string/contains.cpp | 69 +- .../string/hive_partition_component.cpp | 33 + .../src/function/scalar/string/length.cpp | 70 +- .../src/function/scalar/string/like.cpp | 74 +- .../function/scalar/string/nfc_normalize.cpp | 5 +- .../src/function/scalar/string/path_join.cpp | 2 +- .../src/function/scalar/string/regexp.cpp | 27 +- .../string/regexp/regexp_extract_all.cpp | 18 +- .../scalar/string/regexp/regexp_util.cpp | 16 +- .../function/scalar/string/string_split.cpp | 75 +- .../function/scalar/string/strip_accents.cpp | 14 + .../src/function/scalar/string/substring.cpp | 6 +- .../function/scalar/struct/remap_struct.cpp | 118 +- .../function/scalar/struct/struct_concat.cpp | 2 +- .../scalar/struct/struct_contains.cpp | 198 +- .../function/scalar/struct/struct_extract.cpp | 56 +- .../function/scalar/struct/struct_pack.cpp | 68 +- .../scalar/system/aggregate_export.cpp | 191 +- .../scalar/system/parse_log_message.cpp | 28 +- .../src/function/scalar/system/write_log.cpp | 56 +- .../scalar/variant/variant_extract.cpp | 15 +- .../scalar/variant/variant_extract_string.cpp | 2 +- .../scalar/variant/variant_typeof.cpp | 2 + .../function/scalar/variant/variant_utils.cpp | 8 + src/duckdb/src/function/scalar_function.cpp | 62 +- src/duckdb/src/function/table/arrow.cpp | 39 +- .../table/arrow/arrow_duck_schema.cpp | 13 +- .../src/function/table/arrow_conversion.cpp | 286 +- src/duckdb/src/function/table/checkpoint.cpp | 11 +- src/duckdb/src/function/table/copy_csv.cpp | 16 +- .../src/function/table/direct_file_reader.cpp | 49 +- src/duckdb/src/function/table/glob.cpp | 33 +- .../src/function/table/query_function.cpp | 63 +- src/duckdb/src/function/table/range.cpp | 135 +- src/duckdb/src/function/table/read_csv.cpp | 128 +- src/duckdb/src/function/table/read_duckdb.cpp | 46 +- src/duckdb/src/function/table/read_file.cpp | 17 +- src/duckdb/src/function/table/repeat.cpp | 21 +- src/duckdb/src/function/table/repeat_row.cpp | 34 +- src/duckdb/src/function/table/sniff_csv.cpp | 16 +- src/duckdb/src/function/table/summary.cpp | 3 +- .../table/system/create_external_resource.cpp | 42 +- .../function/table/system/duckdb_columns.cpp | 18 +- .../function/table/system/duckdb_dialects.cpp | 4 +- .../table/system/duckdb_extensions.cpp | 18 +- .../system/duckdb_external_file_cache.cpp | 20 +- .../table/system/duckdb_functions.cpp | 160 +- .../function/table/system/duckdb_keywords.cpp | 25 +- .../src/function/table/system/duckdb_log.cpp | 42 +- .../table/system/duckdb_log_contexts.cpp | 16 +- .../table/system/duckdb_optimizers.cpp | 5 +- .../function/table/system/duckdb_secrets.cpp | 3 +- .../table/system/duckdb_sequences.cpp | 5 +- .../function/table/system/duckdb_settings.cpp | 21 +- .../function/table/system/duckdb_tables.cpp | 3 +- .../function/table/system/duckdb_types.cpp | 12 +- .../table/system/duckdb_which_secret.cpp | 5 +- .../table/system/enable_profiling.cpp | 79 +- .../table/system/external_resource_types.cpp | 21 +- .../table/system/external_resources.cpp | 40 +- .../function/table/system/logging_utils.cpp | 64 +- .../table/system/pragma_metadata_info.cpp | 3 +- .../table/system/pragma_storage_info.cpp | 47 +- .../table/system/pragma_table_info.cpp | 16 +- .../table/system/pragma_table_sample.cpp | 11 +- .../table/system/pragma_user_agent.cpp | 5 +- .../function/table/system/test_all_types.cpp | 5 +- .../table/system/test_vector_types.cpp | 25 +- src/duckdb/src/function/table/table_scan.cpp | 169 +- src/duckdb/src/function/table/unnest.cpp | 3 +- .../function/table/version/pragma_version.cpp | 6 +- src/duckdb/src/function/table_function.cpp | 182 +- src/duckdb/src/function/type_constructor.cpp | 36 +- .../window/window_aggregate_states.cpp | 18 +- .../src/function/window/window_aggregator.cpp | 4 +- .../src/function/window/window_collection.cpp | 20 +- .../window/window_distinct_aggregator.cpp | 13 +- .../window/window_match_recognize.cpp | 3 +- .../window/window_merge_sort_tree.cpp | 4 +- .../window/window_naive_aggregator.cpp | 1 + .../window/window_scalar_function.cpp | 87 +- .../function/window/window_segment_tree.cpp | 101 +- .../src/function/window/window_token_tree.cpp | 5 +- .../function/window/window_value_function.cpp | 72 +- src/duckdb/src/function/window_function.cpp | 15 +- src/duckdb/src/include/duckdb.h | 24 +- src/duckdb/src/include/duckdb.hpp | 1 + .../src/include/duckdb/catalog/catalog.hpp | 20 + .../aggregate_function_catalog_entry.hpp | 2 + .../column_dependency_manager.hpp | 3 + .../catalog_entry/duck_index_entry.hpp | 4 +- .../catalog_entry/duck_schema_entry.hpp | 13 +- .../catalog_entry/duck_table_entry.hpp | 6 + .../catalog_entry/macro_catalog_entry.hpp | 2 + .../catalog_entry/schema_catalog_entry.hpp | 34 +- .../catalog_entry/sequence_catalog_entry.hpp | 2 + .../catalog_entry/table_catalog_entry.hpp | 8 + .../table_function_catalog_entry.hpp | 4 + .../catalog_entry/view_catalog_entry.hpp | 2 + .../duckdb/catalog/catalog_search_path.hpp | 2 + .../catalog/default/default_functions.hpp | 4 - .../default/default_table_functions.hpp | 10 +- .../src/include/duckdb/catalog/dependency.hpp | 4 +- .../duckdb/catalog/dependency_manager.hpp | 3 + .../include/duckdb/catalog/duck_catalog.hpp | 5 + .../include/duckdb/common/adbc/wrappers.hpp | 6 + .../src/include/duckdb/common/allocator.hpp | 5 + .../common/arrow/appender/append_data.hpp | 12 + .../duckdb/common/arrow/arrow_appender.hpp | 2 + .../duckdb/common/arrow/arrow_buffer.hpp | 2 +- .../duckdb/common/arrow/arrow_format.hpp | 112 + .../duckdb/common/arrow/arrow_merge_event.hpp | 61 - .../common/arrow/arrow_query_result.hpp | 52 - .../duckdb/common/arrow/arrow_util.hpp | 34 - .../duckdb/common/arrow/arrow_wrapper.hpp | 5 + .../duckdb/common/arrow/nanoarrow/nanoarrow.h | 2 +- .../arrow/physical_arrow_batch_collector.hpp | 30 - .../common/arrow/physical_arrow_collector.hpp | 65 - .../common/arrow/result_arrow_wrapper.hpp | 37 - .../include/duckdb/common/bind_helpers.hpp | 6 + .../duckdb/common/clustered_aggregate.hpp | 66 +- .../duckdb/common/compressed_file_system.hpp | 12 +- .../common/enable_shared_from_this_ipp.hpp | 2 +- .../duckdb/common/encryption_functions.hpp | 2 + .../src/include/duckdb/common/enum_util.hpp | 182 +- .../duckdb/common/enums/checkpoint_abort.hpp | 3 +- .../duckdb/common/enums/compression_type.hpp | 2 +- ...ecution_result.hpp => connection_type.hpp} | 14 +- .../enums/database_modification_type.hpp | 4 + .../enums/debug_progress_verification.hpp | 26 + .../enums/debug_statement_verification.hpp | 4 +- ...ansaction_state.hpp => file_sync_mode.hpp} | 6 +- ...esult.hpp => query_result_memory_type.hpp} | 13 +- .../common/enums/query_result_state.hpp | 29 + .../duckdb/common/enums/result_ordering.hpp | 25 + .../duckdb/common/enums/statement_type.hpp | 14 +- .../common/exception/http_exception.hpp | 5 + .../duckdb/common/extra_operator_info.hpp | 31 +- .../include/duckdb/common/extra_type_info.hpp | 296 - .../include/duckdb/common/file_open_flags.hpp | 17 +- .../src/include/duckdb/common/file_system.hpp | 3 + .../duckdb/common/filename_pattern.hpp | 4 + src/duckdb/src/include/duckdb/common/fsst.hpp | 8 + .../duckdb/common/hive_partitioning.hpp | 4 +- .../src/include/duckdb/common/hugeint.hpp | 343 +- .../src/include/duckdb/common/identifier.hpp | 3 + .../include/duckdb/common/json_document.hpp | 4 + .../duckdb/common/local_file_system.hpp | 2 + .../duckdb/common/logical_type_info.hpp | 233 + .../enum_type_info.hpp | 14 +- .../common/multi_file/base_file_reader.hpp | 3 + .../common/multi_file/multi_file_data.hpp | 34 +- .../common/multi_file/multi_file_function.hpp | 184 +- .../common/multi_file/multi_file_list.hpp | 3 +- .../common/multi_file/multi_file_options.hpp | 45 +- .../common/multi_file/multi_file_reader.hpp | 19 +- .../common/multi_file/multi_file_states.hpp | 18 +- .../multi_file/table_function_multi_file.hpp | 268 +- .../src/include/duckdb/common/mutex.hpp | 70 + .../duckdb/common/named_parameter_map.hpp | 4 + .../src/include/duckdb/common/once_flag.hpp | 48 + .../include/duckdb/common/open_file_info.hpp | 20 +- .../duckdb/common/opener_file_system.hpp | 15 +- .../common/operator/aggregate_operators.hpp | 43 + .../duckdb/common/operator/cast_operators.hpp | 30 +- .../operator/decimal_cast_operators.hpp | 15 +- .../common/operator/integer_cast_operator.hpp | 25 +- .../duckdb/common/operator/interpolate.hpp | 2 + .../operator/numeric_binary_operators.hpp | 6 +- .../include/duckdb/common/optional_idx.hpp | 24 +- .../progress_bar/unscented_kalman_filter.hpp | 65 - .../duckdb/common/query_parameters.hpp | 33 - .../serializer/async_memory_governor.hpp | 4 + .../duckdb/common/serializer/serializer.hpp | 16 +- .../include/duckdb/common/shared_ptr_ipp.hpp | 6 +- .../duckdb/common/sorting/buffered_merge.hpp | 192 + .../include/duckdb/common/sorting/sort.hpp | 6 + .../duckdb/common/sorting/sorted_run.hpp | 2 + .../common/sorting/sorted_run_merger.hpp | 2 + .../include/duckdb/common/stl_allocator.hpp | 93 + .../src/include/duckdb/common/string_util.hpp | 24 +- .../src/include/duckdb/common/table_index.hpp | 33 +- .../duckdb/common/thread_annotation/mutex.hpp | 16 + .../tree_renderer/compact_tree_renderer.hpp | 53 + .../include/duckdb/common/type_visitor.hpp | 195 +- .../src/include/duckdb/common/typed_index.hpp | 50 + .../src/include/duckdb/common/types.hpp | 191 +- .../common/types/arrow_string_view_type.hpp | 4 +- .../common/types/batched_data_collection.hpp | 3 - .../duckdb/common/types/cast_helpers.hpp | 5 +- .../types/column/column_data_allocator.hpp | 1 + .../types/column/column_data_collection.hpp | 6 + .../column/column_data_collection_segment.hpp | 6 +- .../types/column/column_data_consumer.hpp | 2 +- .../duckdb/common/types/conflict_manager.hpp | 36 +- .../duckdb/common/types/data_chunk.hpp | 2 + .../src/include/duckdb/common/types/date.hpp | 2 + .../duckdb/common/types/double_na_equal.hpp | 4 +- .../include/duckdb/common/types/geometry.hpp | 75 +- .../duckdb/common/types/hyperloglog.hpp | 8 + .../types/row/partitioned_tuple_data.hpp | 2 + .../common/types/row/tuple_data_allocator.hpp | 13 +- .../types/row/tuple_data_collection.hpp | 4 +- .../src/include/duckdb/common/types/time.hpp | 5 +- .../include/duckdb/common/types/timestamp.hpp | 10 + .../duckdb/common/types/type_manager.hpp | 3 + .../duckdb/common/types/validity_mask.hpp | 7 + .../src/include/duckdb/common/types/value.hpp | 12 +- .../variant/parquet_variant_iterator.hpp | 58 +- .../types}/variant/variant_binary_decoder.hpp | 6 +- .../common/types/variant/variant_builder.hpp | 166 +- .../duckdb/common/types/variant_visitor.hpp | 24 +- .../duckdb/common/types/vector_buffer.hpp | 7 + .../duckdb/common/types/vector_cache.hpp | 3 + .../common/vector/dictionary_vector.hpp | 5 +- .../duckdb/common/vector/sequence_vector.hpp | 2 + .../duckdb/common/vector/shredded_vector.hpp | 2 + .../duckdb/common/vector/vector_writer.hpp | 2 +- .../vector_operations/aggregate_executor.hpp | 129 +- .../duckdb/common/virtual_file_system.hpp | 2 + .../src/include/duckdb/common/winapi.hpp | 2 +- .../duckdb/execution/aggregate_hashtable.hpp | 40 +- .../aggregate_ht_adaptivity_state.hpp | 89 + .../execution/column_binding_resolver.hpp | 1 + .../src/include/duckdb/execution/executor.hpp | 40 +- .../duckdb/execution/expression_executor.hpp | 2 + .../execution/expression_executor_state.hpp | 2 + .../duckdb/execution/ie_join_union.hpp | 209 + .../duckdb/execution/index/unbound_index.hpp | 2 +- .../duckdb/execution/join_hashtable.hpp | 118 +- .../execution/mark_join_row_comparison.hpp | 4 +- .../duckdb/execution/merge_sort_tree.hpp | 28 +- .../aggregate/physical_limited_distinct.hpp | 3 + .../physical_perfecthash_aggregate.hpp | 2 + .../physical_ungrouped_aggregate.hpp | 2 + .../operator/csv_scanner/csv_error.hpp | 7 +- .../operator/csv_scanner/csv_file_handle.hpp | 7 +- .../operator/csv_scanner/csv_file_scanner.hpp | 48 +- .../csv_scanner/csv_multi_file_info.hpp | 71 - .../operator/csv_scanner/csv_option.hpp | 2 +- .../csv_scanner/csv_reader_options.hpp | 2 +- .../operator/csv_scanner/csv_schema.hpp | 5 + .../csv_scanner/csv_schema_discovery.hpp | 27 + .../operator/csv_scanner/global_csv_state.hpp | 18 +- .../csv_scanner/sniffer/csv_sniffer.hpp | 6 +- .../helper/physical_batch_collector.hpp | 68 - .../operator/helper/physical_limit.hpp | 4 + .../helper/physical_limit_percent.hpp | 3 + .../helper/physical_reservoir_sample.hpp | 3 + .../helper/physical_result_collector.hpp | 13 +- .../operator/helper/physical_result_sink.hpp | 57 +- .../operator/helper/physical_set.hpp | 2 + .../operator/join/join_filter_pushdown.hpp | 10 +- .../operator/join/outer_join_marker.hpp | 4 + .../join/physical_blockwise_nl_join.hpp | 1 + .../join/physical_nested_loop_join.hpp | 5 + .../join/physical_piecewise_merge_join.hpp | 1 + .../join/physical_positional_join.hpp | 3 + .../operator/join/physical_range_join.hpp | 4 +- .../operator/order/physical_top_n.hpp | 1 + .../operator/persistent/copy_batch_slicer.hpp | 42 + .../persistent/copy_output_lifecycle.hpp | 13 +- .../operator/persistent/csv_rejects_table.hpp | 14 +- .../persistent/physical_batch_insert.hpp | 4 +- .../persistent/physical_copy_to_file.hpp | 3 + .../operator/persistent/physical_delete.hpp | 1 + .../operator/persistent/physical_insert.hpp | 6 +- .../operator/persistent/physical_update.hpp | 1 + .../projection/physical_projection.hpp | 1 + .../physical_tableinout_function.hpp | 10 +- .../operator/scan/physical_table_scan.hpp | 4 +- .../operator/set/physical_recursive_cte.hpp | 3 + .../set/physical_recursive_cte_state.hpp | 56 +- .../execution/perfect_aggregate_hashtable.hpp | 4 + .../duckdb/execution/physical_operator.hpp | 5 + .../execution/physical_plan_generator.hpp | 4 +- .../duckdb/execution/progress_data.hpp | 87 + .../duckdb/execution/radix_ht_adaptivity.hpp | 36 + .../duckdb/execution/radix_ht_sink_state.hpp | 183 + .../duckdb/execution/reservoir_sample.hpp | 1 + .../aggregate/distributive_functions.hpp | 6 +- .../function/aggregate/list_aggregate.hpp | 3 +- .../duckdb/function/aggregate_function.hpp | 95 +- .../function/aggregate_state_layout.hpp | 7 + .../builtin_function_lookup.hpp | 11 +- .../function/cast/cast_function_set.hpp | 8 + .../duckdb/function/cast/default_casts.hpp | 1 + .../function/cast/variant/json_to_variant.hpp | 42 +- .../cast/variant/primitive_to_variant.hpp | 26 +- .../cast/variant/struct_to_variant.hpp | 5 +- .../function/cast/variant/to_variant_fwd.hpp | 23 +- .../cast/variant/variant_to_variant.hpp | 9 +- .../function/cast/vector_cast_helpers.hpp | 4 +- .../duckdb/function/compression_function.hpp | 6 +- .../duckdb/function/create_sort_key.hpp | 5 + .../src/include/duckdb/function/function.hpp | 429 +- .../duckdb/function/function_binder.hpp | 47 +- .../function/function_serialization.hpp | 329 +- .../duckdb/function/function_set_base.hpp | 8 +- .../duckdb/function/pragma_function.hpp | 41 +- .../duckdb/function/replacement_scan.hpp | 14 +- .../compressed_materialization_functions.hpp | 52 +- .../compressed_materialization_utils.hpp | 11 + .../duckdb/function/scalar/generic_common.hpp | 4 + .../function/scalar/generic_functions.hpp | 4 +- .../duckdb/function/scalar/list_functions.hpp | 4 +- .../function/scalar/nested_functions.hpp | 4 + .../function/scalar/operator_functions.hpp | 14 +- .../include/duckdb/function/scalar/regexp.hpp | 2 + .../function/scalar/sequence_functions.hpp | 2 +- .../function/scalar/strftime_format.hpp | 2 + .../duckdb/function/scalar/string_common.hpp | 2 + .../function/scalar/string_functions.hpp | 22 +- .../function/scalar/struct_functions.hpp | 8 +- .../duckdb/function/scalar/struct_utils.hpp | 28 + .../function/scalar/system_functions.hpp | 2 +- .../function/scalar/tablefilter_functions.hpp | 10 +- .../duckdb/function/scalar/variant_utils.hpp | 2 + .../duckdb/function/scalar_function.hpp | 92 +- .../include/duckdb/function/table/arrow.hpp | 22 +- .../function/table/direct_file_reader.hpp | 4 +- .../duckdb/function/table/read_csv.hpp | 6 +- .../duckdb/function/table/read_file.hpp | 1 + .../duckdb/function/table_function.hpp | 251 +- .../variant/variant_value_convert.hpp | 5 + .../function/window/value_functions.hpp | 4 +- .../window/window_aggregate_states.hpp | 12 +- .../function/window/window_aggregator.hpp | 2 + .../function/window/window_collection.hpp | 14 +- .../duckdb/function/window_function.hpp | 40 +- .../include/duckdb/logging/log_storage.hpp | 3 + .../src/include/duckdb/logging/log_type.hpp | 22 +- .../src/include/duckdb/logging/logger.hpp | 2 +- .../duckdb/main/active_query_context.hpp | 111 + .../src/include/duckdb/main/appender.hpp | 4 +- .../include/duckdb/main/attached_database.hpp | 41 + .../buffered_data/batched_buffered_data.hpp | 45 +- .../main/buffered_data/buffered_data.hpp | 85 +- .../buffered_data/simple_buffered_data.hpp | 26 +- .../main/capi/capi_function_signature.hpp | 84 + .../duckdb/main/capi/capi_internal.hpp | 51 +- .../duckdb/main/capi/capi_internal_table.hpp | 4 +- .../capi_v2/capi_v2_function_internal.hpp | 144 + .../duckdb/main/capi_v2/capi_v2_internal.hpp | 464 +- .../main/capi_v2/capi_v2_result_internal.hpp | 171 +- .../duckdb/main/capi_v2/extension_api_v2.hpp | 1361 +- .../include/duckdb/main/chunk_scan_state.hpp | 43 - .../batched_data_collection.hpp | 35 - .../main/chunk_scan_state/query_result.hpp | 29 - .../src/include/duckdb/main/client_config.hpp | 8 +- .../include/duckdb/main/client_context.hpp | 275 +- .../duckdb/main/client_context_state.hpp | 55 +- .../src/include/duckdb/main/client_data.hpp | 3 + src/duckdb/src/include/duckdb/main/config.hpp | 87 +- .../src/include/duckdb/main/connection.hpp | 88 +- .../src/include/duckdb/main/database.hpp | 61 +- .../include/duckdb/main/database_manager.hpp | 33 +- .../include/duckdb/main/db_instance_cache.hpp | 5 + .../src/include/duckdb/main/error_manager.hpp | 2 +- .../main/extension/extension_loader.hpp | 2 + .../extension/external_extension_provider.hpp | 43 + .../extension/generated_extension_loader.hpp | 25 - .../extension/linked_extension_registry.hpp | 49 + .../main/extension_callback_manager.hpp | 6 +- .../include/duckdb/main/extension_entries.hpp | 75 +- .../include/duckdb/main/extension_helper.hpp | 17 +- .../duckdb/main/extension_install_info.hpp | 9 + .../include/duckdb/main/extension_manager.hpp | 10 +- .../duckdb/main/http/http_client_pool.hpp | 5 +- .../duckdb/main/http/http_retry_budget.hpp | 168 +- .../main/http/http_transport_manager.hpp | 10 +- .../include/duckdb/main/http/http_util.hpp | 30 +- .../duckdb/main/materialized_query_result.hpp | 67 - .../duckdb/main/pending_query_result.hpp | 70 - .../duckdb/main/prepared_statement.hpp | 51 +- .../duckdb/main/prepared_statement_data.hpp | 7 +- .../duckdb/main/profiler/gathered_metrics.hpp | 38 +- .../include/duckdb/main/profiler/metrics.hpp | 2 +- .../duckdb/main/profiler/profiling_utils.hpp | 21 +- .../include/duckdb/main/query_parameters.hpp | 31 + .../include/duckdb/main/query_profiler.hpp | 15 +- .../src/include/duckdb/main/query_result.hpp | 212 +- .../duckdb/main/query_result_stream.hpp | 122 + .../src/include/duckdb/main/relation.hpp | 3 + .../main/relation/table_function_relation.hpp | 9 +- .../src/include/duckdb/main/result_format.hpp | 158 + .../src/include/duckdb/main/result_unit.hpp | 60 + .../main/retained_result_collection.hpp | 246 + .../src/include/duckdb/main/secret/secret.hpp | 4 +- .../src/include/duckdb/main/setting_info.hpp | 36 +- .../src/include/duckdb/main/settings.hpp | 664 +- .../duckdb/main/statement_iterator.hpp | 42 +- .../duckdb/main/stream_query_result.hpp | 86 - .../optimizer/aggregate_function_rewriter.hpp | 2 - .../optimizer/column_binding_replacer.hpp | 10 +- .../optimizer/compressed_materialization.hpp | 9 +- .../duckdb/optimizer/cte_filter_pusher.hpp | 9 +- .../optimizer/cte_join_filter_collector.hpp | 46 + .../optimizer/cte_join_filter_pusher.hpp | 43 + .../duckdb/optimizer/filter_combiner.hpp | 2 + .../join_order/join_order_optimizer.hpp | 6 + .../optimizer/join_order/join_predicate.hpp | 21 +- .../join_order/join_relation_set.hpp | 2 + .../join_order/query_graph_manager.hpp | 1 + .../optimizer/join_order/relation_index.hpp | 32 +- .../relation_statistics.hpp | 15 + .../optimizer/remote_pushdown_optimizer.hpp | 19 +- .../optimizer/remove_unused_columns.hpp | 14 + .../optimizer/statistics_propagator.hpp | 4 + .../duckdb/optimizer/type_pushdown.hpp | 10 +- .../duckdb/optimizer/unnest_rewriter.hpp | 3 +- .../src/include/duckdb/parallel/interrupt.hpp | 7 + .../src/include/duckdb/parallel/pipeline.hpp | 14 + .../parallel/pipeline_broadcast_exchange.hpp | 2 +- .../duckdb/parallel/pipeline_executor.hpp | 4 + .../duckdb/parallel/progress_verifier.hpp | 116 + .../duckdb/parallel/scan_read_ahead.hpp | 43 +- .../include/duckdb/parallel/task_executor.hpp | 81 +- .../duckdb/parallel/task_scheduler.hpp | 6 +- .../duckdb/parallel/task_scheduler_queue.hpp | 2 +- .../duckdb/parallel/task_scheduler_token.hpp | 14 + .../src/include/duckdb/parser/constraint.hpp | 11 + .../duckdb/parser/dialect_extension.hpp | 27 - .../parser/expression/constant_expression.hpp | 2 + .../parser/expression/type_expression.hpp | 4 + .../parser/expression/window_expression.hpp | 3 +- .../include/duckdb/parser/grammar_change.hpp | 18 +- .../duckdb/parser/parsed_data/alter_info.hpp | 3 +- .../parser/parsed_data/alter_schema_info.hpp | 86 + .../parsed_data/alter_table_function_info.hpp | 6 +- .../parser/parsed_data/alter_table_info.hpp | 41 +- .../duckdb/parser/parsed_data/attach_info.hpp | 3 + .../parser/parsed_data/bound_pragma_info.hpp | 4 +- .../parser/parsed_data/connect_info.hpp | 3 + .../parser/parsed_data/create_schema_info.hpp | 7 + .../parser/parsed_data/create_table_info.hpp | 2 + .../parser/parsed_data/create_view_info.hpp | 5 +- .../parsed_data/external_resource_options.hpp | 43 + .../duckdb/parser/parsed_data/parse_info.hpp | 10 + .../duckdb/parser/parsed_expression.hpp | 3 + .../src/include/duckdb/parser/parser.hpp | 36 +- .../include/duckdb/parser/parser_options.hpp | 12 +- .../parser/peg/ast/add_column_entry.hpp | 5 +- .../parser/peg/ast/expression_chain.hpp | 10 + .../peg/autocomplete_catalog_provider.hpp | 1 - .../duckdb/parser/peg/compiled_grammar.hpp | 62 +- .../duckdb/parser/peg/dialect_extension.hpp | 86 + .../parser/peg/grammar_literal_table.hpp | 41 + .../duckdb/parser/peg/inlined_grammar.hpp | 5833 +++---- .../duckdb/parser/peg/keyword_categories.hpp | 63 + .../duckdb/parser/peg/keyword_helper.hpp | 25 +- .../keyword_helper/default_keyword_maps.hpp | 14 +- .../keyword_helper/duckdb_keyword_helper.hpp | 13 +- .../parsed_grammar_keyword_helper.hpp | 8 +- .../duckdb/parser/peg/literal_info.hpp | 58 + .../src/include/duckdb/parser/peg/matcher.hpp | 117 +- .../matcher/arithmetic_operator_matcher.hpp | 4 + .../parser/peg/matcher/choice_matcher.hpp | 2 +- .../parser/peg/matcher/identifier_matcher.hpp | 62 +- .../parser/peg/matcher/keyword_matcher.hpp | 32 +- .../parser/peg/matcher/list_matcher.hpp | 2 +- .../peg/matcher/literal_choice_matcher.hpp | 21 + .../peg/matcher/number_literal_matcher.hpp | 6 +- .../parser/peg/matcher/operator_matcher.hpp | 56 +- .../parser/peg/matcher/optional_matcher.hpp | 5 +- .../parser/peg/matcher/repeat_matcher.hpp | 5 +- .../peg/matcher/string_literal_matcher.hpp | 6 +- .../duckdb/parser/peg/matcher_factory.hpp | 18 +- .../duckdb/parser/peg/matcher_stack.hpp | 3 + .../duckdb/parser/peg/matcher_token.hpp | 36 + .../duckdb/parser/peg/parsed_grammar.hpp | 52 +- .../duckdb/parser/peg/parser_packrat.hpp | 5 +- .../duckdb/parser/peg/passthrough_dialect.hpp | 69 + .../include/duckdb/parser/peg/peg_parser.hpp | 2 +- .../duckdb/parser/peg/tokenizer/tokenizer.hpp | 13 +- .../parser/peg/transformer/parse_result.hpp | 102 +- .../peg/transformer/peg_transformer.hpp | 6927 +++----- .../peg/transformer/transform_result.hpp | 1 + .../include/duckdb/parser/qualified_name.hpp | 26 + .../src/include/duckdb/parser/query_node.hpp | 2 + .../parser/query_node/statement_node.hpp | 4 + .../duckdb/parser/simplified_token.hpp | 5 +- .../parser/statement/explain_statement.hpp | 4 +- .../include/duckdb/parser/statement/list.hpp | 1 + .../statement/passthrough_statement.hpp | 31 + .../src/include/duckdb/parser/tableref.hpp | 2 + .../parser/tableref/table_function_ref.hpp | 6 + .../include/duckdb/parser/token_iterator.hpp | 6 + .../src/include/duckdb/planner/binder.hpp | 40 +- .../planner/bound_expression_sql_exporter.hpp | 25 + .../src/include/duckdb/planner/expression.hpp | 7 +- .../bound_conjunction_expression.hpp | 3 + .../expression/bound_constant_expression.hpp | 6 +- .../expression/bound_window_expression.hpp | 40 + .../planner/expression/window_range_info.hpp | 55 + .../duckdb/planner/expression_binder.hpp | 5 + .../table_function_binder.hpp | 20 +- .../duckdb/planner/expression_iterator.hpp | 2 + .../duckdb/planner/expression_nullability.hpp | 10 + .../duckdb/planner/filter/bloom_filter.hpp | 6 +- .../planner/filter/expression_filter.hpp | 10 + .../filter/table_filter_function_helpers.hpp | 12 + .../planner/filter/table_filter_functions.hpp | 43 +- .../duckdb/planner/logical_operator.hpp | 8 + .../logical_plan_sql_export_context.hpp | 109 + .../planner/logical_plan_sql_exporter.hpp | 67 + .../logical_plan_verification_result.hpp | 21 +- .../duckdb/planner/logical_plan_verifier.hpp | 8 +- .../planner/operator/logical_aggregate.hpp | 3 + .../operator/logical_column_data_get.hpp | 3 + .../operator/logical_comparison_join.hpp | 3 + .../planner/operator/logical_copy_to_file.hpp | 5 + .../planner/operator/logical_create_table.hpp | 3 + .../planner/operator/logical_cteref.hpp | 3 + .../planner/operator/logical_delete.hpp | 3 + .../planner/operator/logical_distinct.hpp | 3 + .../planner/operator/logical_dummy_scan.hpp | 3 + .../planner/operator/logical_empty_result.hpp | 3 + .../planner/operator/logical_explain.hpp | 4 + .../operator/logical_expression_get.hpp | 10 + .../operator/logical_extension_operator.hpp | 3 + .../planner/operator/logical_filter.hpp | 3 + .../duckdb/planner/operator/logical_get.hpp | 28 +- .../planner/operator/logical_insert.hpp | 3 + .../duckdb/planner/operator/logical_join.hpp | 7 + .../duckdb/planner/operator/logical_limit.hpp | 5 + .../operator/logical_materialized_cte.hpp | 19 + .../planner/operator/logical_merge_into.hpp | 3 + .../duckdb/planner/operator/logical_order.hpp | 3 + .../duckdb/planner/operator/logical_pivot.hpp | 3 + .../planner/operator/logical_projection.hpp | 6 + .../operator/logical_recursive_cte.hpp | 9 + .../planner/operator/logical_sample.hpp | 3 + .../planner/operator/logical_secure_view.hpp | 28 + .../operator/logical_set_operation.hpp | 3 + .../duckdb/planner/operator/logical_top_n.hpp | 5 + .../operator/logical_unconditional_join.hpp | 3 + .../planner/operator/logical_unnest.hpp | 3 + .../planner/operator/logical_update.hpp | 8 +- .../planner/operator/logical_window.hpp | 3 + .../src/include/duckdb/planner/planner.hpp | 2 + ...bound_expression_sql_exporter_internal.hpp | 175 + .../logical_plan_sql_exporter_internal.hpp | 146 + .../duckdb/planner/sql_export_helpers.hpp | 151 +- .../duckdb/planner/statement_preprocessor.hpp | 20 +- .../include/duckdb/planner/table_binding.hpp | 5 + .../planner/tableref/bound_at_clause.hpp | 6 + .../duckdb/storage/arena_allocator.hpp | 15 + .../duckdb/storage/block_allocator.hpp | 6 + .../include/duckdb/storage/block_manager.hpp | 5 + .../duckdb/storage/buffer/block_handle.hpp | 61 +- .../duckdb/storage/buffer/buffer_pool.hpp | 22 +- .../include/duckdb/storage/caching_mode.hpp | 15 + .../checkpoint/string_checkpoint_state.hpp | 15 +- .../storage/checkpoint/table_data_writer.hpp | 1 + .../duckdb/storage/checkpoint_manager.hpp | 5 + .../storage/compression/alp/alp_utils.hpp | 25 +- .../chimp/algorithm/bit_reader.hpp | 69 +- .../compression/chimp/algorithm/chimp128.hpp | 23 +- .../chimp/algorithm/flag_buffer.hpp | 17 +- .../chimp/algorithm/leading_zero_buffer.hpp | 28 +- .../chimp/algorithm/packed_data.hpp | 13 +- .../storage/compression/chimp/chimp.hpp | 4 +- .../storage/compression/chimp/chimp_fetch.hpp | 18 +- .../storage/compression/chimp/chimp_scan.hpp | 203 +- .../compression_segment_reader.hpp | 25 + .../compression/dict_fsst/decompression.hpp | 2 +- .../compression/dictionary/decompression.hpp | 54 +- .../storage/compression/empty_validity.hpp | 2 +- .../storage/compression/patas/patas_fetch.hpp | 6 +- .../storage/compression/patas/patas_scan.hpp | 152 +- .../storage/compression/patas/shared.hpp | 8 +- .../src/include/duckdb/storage/data_table.hpp | 8 +- .../duckdb/storage/database_handle.hpp | 10 +- .../caching_file_system.hpp | 16 +- .../caching_file_system_wrapper.hpp | 2 + .../external_file_cache.hpp | 32 +- .../external_file_cache_block.hpp | 6 + .../include/duckdb/storage/object_cache.hpp | 2 +- .../duckdb/storage/optimistic_data_writer.hpp | 2 + .../storage/single_file_block_manager.hpp | 6 + .../storage/standard_buffer_manager.hpp | 6 +- .../storage/statistics/geometry_stats.hpp | 2 + .../storage/statistics/numeric_stats.hpp | 15 +- .../storage/statistics/stats_writer.hpp | 58 +- .../storage/statistics/variant_stats.hpp | 2 + .../duckdb/storage/string_uncompressed.hpp | 84 +- .../duckdb/storage/table/append_state.hpp | 2 - .../duckdb/storage/table/column_data.hpp | 2 +- .../duckdb/storage/table/column_segment.hpp | 3 +- .../duckdb/storage/table/index_entry.hpp | 9 +- .../duckdb/storage/table/row_group.hpp | 5 + .../storage/table/row_group_reorderer.hpp | 2 + .../duckdb/storage/table/scan_state.hpp | 16 +- .../duckdb/storage/table/segment_base.hpp | 5 +- .../duckdb/storage/table/table_index_list.hpp | 16 +- .../duckdb/storage/temporary_file_manager.hpp | 4 +- .../duckdb/transaction/commit_state.hpp | 4 +- .../duckdb/transaction/local_storage.hpp | 12 +- .../duckdb/transaction/undo_buffer.hpp | 4 +- src/duckdb/src/include/duckdb_extension_v2.h | 1702 +- .../src/include/duckdb_static_extension.h | 86 + src/duckdb/src/include/duckdb_v2.h | 13665 +++++++++------- src/duckdb/src/logging/log_manager.cpp | 2 + src/duckdb/src/logging/log_storage.cpp | 10 +- src/duckdb/src/logging/log_types.cpp | 213 +- src/duckdb/src/main/appender.cpp | 10 +- src/duckdb/src/main/attached_database.cpp | 65 +- .../buffered_data/batched_buffered_data.cpp | 95 +- .../src/main/buffered_data/buffered_data.cpp | 103 +- .../buffered_data/simple_buffered_data.cpp | 73 +- .../src/main/capi/v1/aggregate_function-c.cpp | 12 +- src/duckdb/src/main/capi/v1/arrow-c.cpp | 142 +- .../src/main/capi/v1/copy_function-c.cpp | 23 +- src/duckdb/src/main/capi/v1/helper-c.cpp | 4 + src/duckdb/src/main/capi/v1/pending-c.cpp | 66 +- src/duckdb/src/main/capi/v1/prepared-c.cpp | 15 +- src/duckdb/src/main/capi/v1/result-c.cpp | 115 +- .../src/main/capi/v1/scalar_function-c.cpp | 7 +- src/duckdb/src/main/capi/v1/stream-c.cpp | 17 +- .../src/main/capi/v1/table_function-c.cpp | 37 +- src/duckdb/src/main/capi/v1/value-c.cpp | 3 +- src/duckdb/src/main/capi/v2/capi_v2.cpp | 110 +- src/duckdb/src/main/capi/v2/capi_v2_arrow.cpp | 497 +- .../src/main/capi/v2/capi_v2_catalog.cpp | 2 +- .../v2/capi_v2_column_data_collection.cpp | 42 +- .../src/main/capi/v2/capi_v2_config.cpp | 153 + .../src/main/capi/v2/capi_v2_connection.cpp | 85 +- .../main/capi/v2/capi_v2_custom_option.cpp | 303 + .../src/main/capi/v2/capi_v2_custom_type.cpp | 123 +- .../src/main/capi/v2/capi_v2_data_chunk.cpp | 61 +- .../src/main/capi/v2/capi_v2_database.cpp | 120 - .../src/main/capi/v2/capi_v2_environment.cpp | 14 +- src/duckdb/src/main/capi/v2/capi_v2_error.cpp | 12 +- .../src/main/capi/v2/capi_v2_expression.cpp | 3 +- .../src/main/capi/v2/capi_v2_extension.cpp | 5 +- .../src/main/capi/v2/capi_v2_factory.cpp | 29 + .../src/main/capi/v2/capi_v2_file_system.cpp | 138 +- src/duckdb/src/main/capi/v2/capi_v2_func.cpp | 169 + .../main/capi/v2/capi_v2_func_aggregate.cpp | 235 +- .../src/main/capi/v2/capi_v2_func_cast.cpp | 120 +- .../src/main/capi/v2/capi_v2_func_copy.cpp | 335 +- .../src/main/capi/v2/capi_v2_func_docs.cpp | 35 + .../src/main/capi/v2/capi_v2_func_macro.cpp | 198 + .../src/main/capi/v2/capi_v2_func_pragma.cpp | 240 + .../src/main/capi/v2/capi_v2_func_scalar.cpp | 316 +- .../main/capi/v2/capi_v2_func_signature.cpp | 54 +- .../src/main/capi/v2/capi_v2_func_table.cpp | 1148 +- .../src/main/capi/v2/capi_v2_identifier.cpp | 7 +- .../src/main/capi/v2/capi_v2_instance.cpp | 234 + .../src/main/capi/v2/capi_v2_log_sink.cpp | 243 + .../src/main/capi/v2/capi_v2_logging.cpp | 44 +- .../src/main/capi/v2/capi_v2_logical_type.cpp | 232 +- .../src/main/capi/v2/capi_v2_option.cpp | 63 +- .../capi/v2/capi_v2_prepared_statement.cpp | 40 +- src/duckdb/src/main/capi/v2/capi_v2_qname.cpp | 13 +- .../main/capi/v2/capi_v2_replacement_scan.cpp | 141 +- .../src/main/capi/v2/capi_v2_result.cpp | 564 +- .../src/main/capi/v2/capi_v2_schema.cpp | 2 +- .../src/main/capi/v2/capi_v2_statement.cpp | 104 +- .../src/main/capi/v2/capi_v2_tokenizer.cpp | 148 + src/duckdb/src/main/capi/v2/capi_v2_utf8.cpp | 26 + src/duckdb/src/main/capi/v2/capi_v2_value.cpp | 764 +- .../src/main/capi/v2/capi_v2_vector.cpp | 91 +- src/duckdb/src/main/chunk_scan_state.cpp | 48 - .../batched_data_collection.cpp | 57 - .../main/chunk_scan_state/query_result.cpp | 53 - src/duckdb/src/main/client_context.cpp | 998 +- .../src/main/client_context_execution.cpp | 587 + src/duckdb/src/main/client_data.cpp | 1 + src/duckdb/src/main/client_verify.cpp | 90 +- src/duckdb/src/main/config.cpp | 134 +- src/duckdb/src/main/connection.cpp | 135 +- src/duckdb/src/main/database.cpp | 65 +- src/duckdb/src/main/database_manager.cpp | 156 +- src/duckdb/src/main/db_instance_cache.cpp | 5 + src/duckdb/src/main/extension.cpp | 4 + .../src/main/extension/extension_helper.cpp | 203 +- .../src/main/extension/extension_install.cpp | 414 +- .../extension/extension_install_dynamic.cpp | 502 + .../src/main/extension/extension_load.cpp | 131 +- .../main/extension/extension_load_dynamic.cpp | 105 + .../src/main/extension/extension_loader.cpp | 14 +- .../extension/external_extension_provider.cpp | 37 + .../extension/linked_extension_registry.cpp | 147 + .../src/main/extension_callback_manager.cpp | 33 +- .../src/main/extension_install_info.cpp | 34 + src/duckdb/src/main/extension_manager.cpp | 14 +- .../main/external_resource_type_registry.cpp | 20 +- .../src/main/http/http_client_httplib.cpp | 145 + src/duckdb/src/main/http/http_client_pool.cpp | 24 +- .../src/main/http/http_retry_budget.cpp | 340 +- .../src/main/http/http_transport_manager.cpp | 51 +- src/duckdb/src/main/http/http_util.cpp | 329 +- .../src/main/materialized_query_result.cpp | 107 - src/duckdb/src/main/parser_context.cpp | 110 + src/duckdb/src/main/pending_query_result.cpp | 121 - src/duckdb/src/main/prepared_statement.cpp | 65 +- .../src/main/profiler/gathered_metrics.cpp | 7 +- .../src/main/profiler/profiling_utils.cpp | 4 +- src/duckdb/src/main/query_profiler.cpp | 12 +- src/duckdb/src/main/query_result.cpp | 484 +- src/duckdb/src/main/query_result_stream.cpp | 307 + src/duckdb/src/main/relation.cpp | 51 +- .../src/main/relation/query_relation.cpp | 2 +- .../src/main/relation/read_csv_relation.cpp | 27 +- .../main/relation/table_function_relation.cpp | 16 +- .../src/main/relation/table_relation.cpp | 4 +- .../src/main/relation/value_relation.cpp | 2 +- src/duckdb/src/main/result_format.cpp | 95 + src/duckdb/src/main/result_unit.cpp | 15 + .../src/main/retained_result_collection.cpp | 118 + src/duckdb/src/main/secret/secret_manager.cpp | 4 +- .../main/settings/autogenerated_settings.cpp | 46 + .../src/main/settings/custom_settings.cpp | 255 +- src/duckdb/src/main/statement_iterator.cpp | 57 +- src/duckdb/src/main/stream_query_result.cpp | 272 - .../optimizer/aggregate_function_rewriter.cpp | 85 +- src/duckdb/src/optimizer/aggregate_reuse.cpp | 2 +- .../src/optimizer/aggregate_rewrite.cpp | 10 +- .../optimizer/aggregate_rewrite_helper.cpp | 16 +- .../optimizer/build_probe_side_optimizer.cpp | 2 +- .../src/optimizer/column_binding_replacer.cpp | 41 +- .../optimizer/common_subplan_optimizer.cpp | 171 +- .../optimizer/compressed_materialization.cpp | 48 +- .../compress_aggregate.cpp | 15 + .../src/optimizer/cte_filter_pusher.cpp | 68 +- src/duckdb/src/optimizer/cte_inlining.cpp | 21 +- .../optimizer/cte_join_filter_collector.cpp | 228 + .../src/optimizer/cte_join_filter_pusher.cpp | 178 + .../src/optimizer/expression_heuristics.cpp | 4 +- .../src/optimizer/expression_rewriter.cpp | 7 +- src/duckdb/src/optimizer/filter_combiner.cpp | 125 +- src/duckdb/src/optimizer/filter_pushdown.cpp | 58 +- .../src/optimizer/grouping_sets_optimizer.cpp | 15 +- .../join_filter_pushdown_optimizer.cpp | 5 +- .../join_order/cardinality_estimator.cpp | 9 + .../join_order/join_order_operator.cpp | 50 +- .../join_order/join_order_optimizer.cpp | 16 +- .../optimizer/join_order/join_predicate.cpp | 59 +- .../join_order/join_relation_set.cpp | 36 +- .../optimizer/join_order/plan_enumerator.cpp | 121 +- .../join_order/query_graph_manager.cpp | 103 +- .../optimizer/join_order/relation_manager.cpp | 34 +- .../multi_stage_aggregate_rewriter.cpp | 99 +- src/duckdb/src/optimizer/optimizer.cpp | 16 +- .../optimizer/outer_join_simplification.cpp | 9 +- .../optimizer/partial_aggregate_pushdown.cpp | 2 +- .../src/optimizer/projection_placement.cpp | 60 +- .../src/optimizer/projection_pullup.cpp | 19 +- .../optimizer/pullup/pullup_projection.cpp | 7 + .../pushdown/pushdown_cross_product.cpp | 54 + .../src/optimizer/pushdown/pushdown_get.cpp | 13 +- .../pushdown/pushdown_inner_join.cpp | 16 +- .../pushdown/pushdown_secure_view.cpp | 37 +- .../pushdown/pushdown_semi_anti_join.cpp | 4 +- .../relation_statistics.cpp | 18 + .../relation_statistics_helper.cpp | 27 +- .../relation_statistics_operator.cpp | 21 +- .../relation_statistics_scan.cpp | 7 +- .../optimizer/remote_pushdown_optimizer.cpp | 15 +- .../src/optimizer/remove_duplicate_groups.cpp | 6 +- .../optimizer/remove_redundant_order_keys.cpp | 1 + .../src/optimizer/remove_unused_columns.cpp | 271 +- src/duckdb/src/optimizer/row_group_pruner.cpp | 3 + .../rule/arithmetic_simplification.cpp | 34 +- .../optimizer/rule/case_simplification.cpp | 7 +- .../rule/comparison_simplification.cpp | 52 +- .../rule/constant_order_normalization.cpp | 8 +- .../rule/date_part_simplification.cpp | 8 +- .../rule/date_trunc_simplification.cpp | 28 + .../optimizer/rule/empty_needle_removal.cpp | 4 +- .../rule/least_greatest_simplification.cpp | 2 +- .../src/optimizer/rule/like_optimizations.cpp | 14 +- .../src/optimizer/rule/monotone_preimage.cpp | 51 +- .../src/optimizer/rule/move_constants.cpp | 30 +- .../optimizer/rule/regex_optimizations.cpp | 20 +- .../src/optimizer/rule/string_prefix.cpp | 2 +- .../src/optimizer/scalar_fn_pushdown.cpp | 8 +- .../expression/propagate_conjunction.cpp | 60 + .../expression/propagate_function.cpp | 7 +- .../expression/propagate_operator.cpp | 32 +- .../statistics/operator/propagate_filter.cpp | 3 +- .../statistics/operator/propagate_get.cpp | 35 +- .../statistics/operator/propagate_join.cpp | 58 +- .../operator/propagate_secure_view.cpp | 33 +- .../src/optimizer/statistics_propagator.cpp | 8 +- src/duckdb/src/optimizer/topn_optimizer.cpp | 1 + .../src/optimizer/topn_window_elimination.cpp | 26 +- src/duckdb/src/optimizer/type_pushdown.cpp | 32 +- src/duckdb/src/optimizer/unnest_rewriter.cpp | 21 +- src/duckdb/src/parallel/executor.cpp | 158 +- src/duckdb/src/parallel/pipeline.cpp | 37 +- .../parallel/pipeline_broadcast_exchange.cpp | 93 +- src/duckdb/src/parallel/pipeline_executor.cpp | 26 +- src/duckdb/src/parallel/progress_verifier.cpp | 374 + src/duckdb/src/parallel/scan_read_ahead.cpp | 144 +- src/duckdb/src/parallel/task_executor.cpp | 215 +- src/duckdb/src/parallel/task_notifier.cpp | 8 +- src/duckdb/src/parallel/task_scheduler.cpp | 23 +- .../src/parallel/task_scheduler_queue.cpp | 40 +- src/duckdb/src/parser/column_definition.cpp | 23 +- src/duckdb/src/parser/constraint.cpp | 23 + .../constraints/foreign_key_constraint.cpp | 2 +- .../parser/constraints/unique_constraint.cpp | 6 +- .../src/parser/expression/cast_expression.cpp | 6 + .../parser/expression/function_expression.cpp | 6 +- .../src/parser/expression/star_expression.cpp | 35 +- .../src/parser/expression/type_expression.cpp | 184 +- .../parser/expression/value_expression.cpp | 243 +- src/duckdb/src/parser/keyword_helper.cpp | 31 +- src/duckdb/src/parser/literal.cpp | 5 + .../parser/parsed_data/alter_schema_info.cpp | 129 + .../parsed_data/alter_table_function_info.cpp | 8 +- .../parser/parsed_data/alter_table_info.cpp | 82 +- .../src/parser/parsed_data/attach_info.cpp | 28 +- .../src/parser/parsed_data/connect_info.cpp | 20 +- .../src/parser/parsed_data/copy_info.cpp | 19 +- .../create_aggregate_function_info.cpp | 4 + .../parser/parsed_data/create_index_info.cpp | 7 +- .../create_pragma_function_info.cpp | 4 + .../create_scalar_function_info.cpp | 4 + .../parser/parsed_data/create_schema_info.cpp | 44 +- .../create_table_function_info.cpp | 6 +- .../parser/parsed_data/create_table_info.cpp | 11 + .../parser/parsed_data/create_type_info.cpp | 2 +- .../parser/parsed_data/create_view_info.cpp | 9 +- .../create_window_function_info.cpp | 4 + .../parsed_data/external_resource_options.cpp | 49 + .../parser/parsed_data/extra_drop_info.cpp | 3 +- .../src/parser/parsed_data/parse_info.cpp | 19 + src/duckdb/src/parser/parsed_expression.cpp | 110 + .../src/parser/parsed_expression_iterator.cpp | 12 +- src/duckdb/src/parser/parser.cpp | 67 +- .../src/parser/peg/autocomplete_core.cpp | 4 +- .../src/parser/peg/compiled_grammar.cpp | 87 +- .../src/parser/peg/dialect_extension.cpp | 84 + src/duckdb/src/parser/peg/grammar_change.cpp | 21 +- .../keyword_helper/default_keyword_maps.cpp | 107 +- .../keyword_helper/duckdb_keyword_helper.cpp | 16 +- .../keyword_helper/grammar_literal_table.cpp | 50 + .../parsed_grammar_keyword_helper.cpp | 42 +- src/duckdb/src/parser/peg/keyword_map.cpp | 372 +- src/duckdb/src/parser/peg/matcher.cpp | 43 +- src/duckdb/src/parser/peg/matcher_factory.cpp | 96 +- .../src/parser/peg/matcher_first_set.cpp | 196 + src/duckdb/src/parser/peg/matcher_process.cpp | 90 +- src/duckdb/src/parser/peg/matcher_stack.cpp | 23 + .../src/parser/peg/operator_matcher.cpp | 30 + src/duckdb/src/parser/peg/parsed_grammar.cpp | 14 +- src/duckdb/src/parser/peg/parser_packrat.cpp | 3 +- .../src/parser/peg/passthrough_dialect.cpp | 49 + src/duckdb/src/parser/peg/peg_parser.cpp | 1 + src/duckdb/src/parser/peg/sql_formatter.cpp | 152 +- .../parser/peg/tokenizer/base_tokenizer.cpp | 164 +- .../parser/peg/tokenizer/parser_tokenizer.cpp | 5 +- .../peg/transformer/peg_transformer.cpp | 144 +- .../transformer/peg_transformer_factory.cpp | 183 +- .../peg/transformer/transform_alter.cpp | 114 +- .../peg/transformer/transform_comment.cpp | 12 +- .../peg/transformer/transform_common.cpp | 72 +- .../parser/peg/transformer/transform_copy.cpp | 28 +- .../transformer/transform_create_index.cpp | 44 +- .../transformer/transform_create_schema.cpp | 15 +- .../transformer/transform_create_sequence.cpp | 10 + .../transformer/transform_create_table.cpp | 103 +- .../peg/transformer/transform_create_type.cpp | 7 +- .../peg/transformer/transform_create_view.cpp | 3 + .../peg/transformer/transform_delete.cpp | 28 +- .../peg/transformer/transform_describe.cpp | 3 + .../parser/peg/transformer/transform_drop.cpp | 3 + .../peg/transformer/transform_explain.cpp | 13 + .../peg/transformer/transform_expression.cpp | 638 +- .../transform_external_resource.cpp | 11 +- .../transform_external_resource_source.cpp | 70 + .../peg/transformer/transform_generated.cpp | 12706 -------------- .../transform_generated_trampoline.cpp | 7197 ++++---- .../transform_generic_copy_option.cpp | 53 +- .../peg/transformer/transform_insert.cpp | 6 +- .../peg/transformer/transform_pivot.cpp | 178 +- .../peg/transformer/transform_select.cpp | 223 +- .../peg/transformer/transform_update.cpp | 24 +- .../parser/peg/transformer/transform_use.cpp | 16 +- .../peg/transformer/transform_vacuum.cpp | 2 + src/duckdb/src/parser/qualified_name.cpp | 107 +- src/duckdb/src/parser/query_node.cpp | 61 +- .../parser/query_node/recursive_cte_node.cpp | 2 +- .../parser/query_node/set_operation_node.cpp | 6 + .../src/parser/query_node/statement_node.cpp | 7 + .../parser/statement/explain_statement.cpp | 6 +- .../statement/external_resource_statement.cpp | 12 +- .../src/parser/statement/multi_statement.cpp | 6 +- .../statement/passthrough_statement.cpp | 16 + src/duckdb/src/parser/tableref.cpp | 55 + .../src/parser/tableref/column_data_ref.cpp | 4 +- .../src/parser/tableref/table_function.cpp | 10 +- src/duckdb/src/parser/token_iterator.cpp | 7 + src/duckdb/src/planner/bind_context.cpp | 17 +- src/duckdb/src/planner/binder.cpp | 23 +- .../expression/bind_aggregate_expression.cpp | 19 + .../expression/bind_columnref_expression.cpp | 3 + .../expression/bind_function_expression.cpp | 40 +- .../expression/bind_macro_expression.cpp | 3 + .../expression/bind_operator_expression.cpp | 19 +- .../bind_positional_reference_expression.cpp | 7 +- .../expression/bind_star_expression.cpp | 159 + .../expression/bind_type_expression.cpp | 1 + .../expression/bind_unnest_expression.cpp | 39 +- .../expression/bind_window_expression.cpp | 74 +- .../binder/query_node/bind_cte_node.cpp | 3 + .../query_node/bind_recursive_cte_node.cpp | 22 +- .../binder/query_node/bind_select_node.cpp | 7 +- .../binder/query_node/bind_setop_node.cpp | 81 + .../query_node/bind_table_macro_node.cpp | 3 + .../query_node/bind_trigger_expansion.cpp | 3 - .../binder/query_node/plan_subquery.cpp | 62 +- .../planner/binder/statement/bind_alter.cpp | 25 +- .../planner/binder/statement/bind_attach.cpp | 24 +- .../planner/binder/statement/bind_call.cpp | 6 +- .../planner/binder/statement/bind_connect.cpp | 9 +- .../planner/binder/statement/bind_copy.cpp | 218 +- .../binder/statement/bind_copy_database.cpp | 12 +- .../planner/binder/statement/bind_create.cpp | 57 +- .../binder/statement/bind_create_table.cpp | 10 +- .../planner/binder/statement/bind_delete.cpp | 7 +- .../planner/binder/statement/bind_detach.cpp | 1 - .../planner/binder/statement/bind_drop.cpp | 6 +- .../planner/binder/statement/bind_execute.cpp | 6 +- .../planner/binder/statement/bind_explain.cpp | 30 +- .../planner/binder/statement/bind_export.cpp | 21 +- .../statement/bind_external_resource.cpp | 30 +- .../planner/binder/statement/bind_insert.cpp | 10 +- .../planner/binder/statement/bind_load.cpp | 1 - .../binder/statement/bind_logical_plan.cpp | 1 - .../binder/statement/bind_merge_into.cpp | 108 +- .../planner/binder/statement/bind_pragma.cpp | 24 +- .../planner/binder/statement/bind_prepare.cpp | 1 - .../planner/binder/statement/bind_select.cpp | 1 - .../planner/binder/statement/bind_update.cpp | 11 +- .../binder/tableref/bind_basetableref.cpp | 23 +- .../tableref/bind_expressionlistref.cpp | 57 +- .../planner/binder/tableref/bind_joinref.cpp | 12 + .../binder/tableref/bind_named_parameters.cpp | 49 - .../planner/binder/tableref/bind_pivot.cpp | 52 +- .../planner/binder/tableref/bind_showref.cpp | 7 +- .../binder/tableref/bind_table_function.cpp | 150 +- .../planner/binder/tableref/plan_joinref.cpp | 41 +- .../planner/bound_expression_sql_exporter.cpp | 805 - src/duckdb/src/planner/collation_binding.cpp | 81 +- src/duckdb/src/planner/expression.cpp | 7 + .../expression/bound_aggregate_expression.cpp | 10 +- .../expression/bound_cast_expression.cpp | 73 +- .../bound_conjunction_expression.cpp | 11 + .../expression/bound_constant_expression.cpp | 31 +- .../expression/bound_function_expression.cpp | 44 +- .../expression/bound_window_expression.cpp | 42 + .../planner/expression/window_range_info.cpp | 154 + src/duckdb/src/planner/expression_binder.cpp | 4 + .../expression_binder/check_binder.cpp | 5 +- .../expression_binder/index_binder.cpp | 23 +- .../table_function_binder.cpp | 14 +- .../src/planner/expression_iterator.cpp | 17 + .../src/planner/expression_nullability.cpp | 23 +- .../src/planner/filter/dynamic_filter.cpp | 13 +- .../src/planner/filter/expression_filter.cpp | 139 +- .../filter/table_filter_bloom_function.cpp | 194 +- .../filter/table_filter_dynamic_function.cpp | 18 +- .../planner/filter/table_filter_functions.cpp | 6 +- .../table_filter_prefix_range_function.cpp | 41 +- ...e_filter_selectivity_optional_function.cpp | 10 +- .../planner/logical_operator_deep_copy.cpp | 29 + .../logical_plan_verification_result.cpp | 3 +- .../src/planner/logical_plan_verifier.cpp | 109 +- .../operator/logical_comparison_join.cpp | 94 + .../planner/operator/logical_copy_to_file.cpp | 6 + .../src/planner/operator/logical_explain.cpp | 66 + .../src/planner/operator/logical_get.cpp | 92 +- .../src/planner/operator/logical_join.cpp | 16 + .../operator/logical_materialized_cte.cpp | 14 + .../planner/operator/logical_projection.cpp | 15 + .../planner/operator/logical_secure_view.cpp | 75 +- src/duckdb/src/planner/planner.cpp | 53 +- .../bound_expression_sql_exporter.cpp | 681 + .../sql_export/logical_plan_sql_exporter.cpp | 479 + .../sql_export/sql_export_composition.cpp | 370 + .../sql_export/sql_export_constants.cpp | 71 + .../src/planner/sql_export/sql_export_cte.cpp | 328 + .../src/planner/sql_export/sql_export_dml.cpp | 666 + .../sql_export/sql_export_functions.cpp | 387 + .../planner/sql_export/sql_export_joins.cpp | 239 + .../planner/sql_export/sql_export_limit.cpp | 306 + .../planner/sql_export/sql_export_pivot.cpp | 215 + .../sql_export/sql_export_relations.cpp | 547 + .../planner/sql_export/sql_export_scope.cpp | 270 + .../planner/sql_export/sql_export_sources.cpp | 260 + .../sql_export/sql_export_statements.cpp | 293 + .../planner/sql_export/sql_export_values.cpp | 458 + .../planner/sql_export/sql_export_window.cpp | 259 + .../sql_export/table_function_sql_export.cpp | 343 + .../src/planner/statement_preprocessor.cpp | 177 +- .../subquery/delim_join_cte_rewriter.cpp | 223 +- .../subquery/flatten_dependent_join.cpp | 117 +- .../pair_dependent_full_outer_join.cpp | 17 +- src/duckdb/src/planner/table_binding.cpp | 8 + src/duckdb/src/planner/table_filter_set.cpp | 70 +- src/duckdb/src/planner/table_filter_state.cpp | 12 +- src/duckdb/src/storage/arena_allocator.cpp | 27 + src/duckdb/src/storage/block_allocator.cpp | 35 + .../src/storage/buffer/block_handle.cpp | 40 +- .../src/storage/buffer/block_manager.cpp | 18 +- src/duckdb/src/storage/buffer/buffer_pool.cpp | 83 +- .../write_overflow_strings_to_disk.cpp | 22 +- src/duckdb/src/storage/checkpoint_manager.cpp | 53 +- .../src/storage/compression/bitpacking.cpp | 73 +- .../src/storage/compression/chimp/chimp.cpp | 25 + .../compression_segment_reader.cpp | 8 + .../src/storage/compression/dict_fsst.cpp | 14 +- .../compression/dict_fsst/decompression.cpp | 41 +- .../compression/dictionary/analyze.cpp | 2 +- .../compression/dictionary/compression.cpp | 5 +- .../compression/dictionary/decompression.cpp | 184 +- .../compression/dictionary_compression.cpp | 11 +- src/duckdb/src/storage/compression/fsst.cpp | 454 +- .../storage/compression/numeric_constant.cpp | 50 +- src/duckdb/src/storage/compression/patas.cpp | 20 + src/duckdb/src/storage/compression/rle.cpp | 40 +- .../compression/string_uncompressed.cpp | 325 +- .../compression/validity_uncompressed.cpp | 19 +- src/duckdb/src/storage/compression/zstd.cpp | 8 +- src/duckdb/src/storage/data_table.cpp | 32 +- src/duckdb/src/storage/database_handle.cpp | 34 +- .../caching_file_system.cpp | 148 +- .../caching_file_system_wrapper.cpp | 5 + .../external_file_cache.cpp | 234 +- src/duckdb/src/storage/index_entry.cpp | 20 +- src/duckdb/src/storage/local_storage.cpp | 27 +- .../src/storage/metadata/metadata_manager.cpp | 55 +- .../storage/open_file_storage_extension.cpp | 4 +- .../src/storage/optimistic_data_writer.cpp | 7 + .../serialization/serialize_create_info.cpp | 2 + .../serialize_logical_operator.cpp | 24 + .../storage/serialization/serialize_nodes.cpp | 46 + .../serialization/serialize_parse_info.cpp | 93 +- .../serialize_parsed_expression.cpp | 1 + .../serialization/serialize_query_node.cpp | 12 + .../serialization/serialize_tableref.cpp | 6 +- .../storage/serialization/serialize_types.cpp | 102 +- .../src/storage/single_file_block_manager.cpp | 60 +- .../src/storage/standard_buffer_manager.cpp | 106 +- .../statistics/distinct_statistics.cpp | 103 +- .../src/storage/statistics/geometry_stats.cpp | 10 + .../src/storage/statistics/numeric_stats.cpp | 74 +- .../src/storage/statistics/string_stats.cpp | 12 +- .../src/storage/statistics/variant_stats.cpp | 17 +- src/duckdb/src/storage/storage_manager.cpp | 30 +- src/duckdb/src/storage/table/column_data.cpp | 105 +- .../src/storage/table/column_segment.cpp | 13 +- .../src/storage/table/list_column_data.cpp | 6 +- src/duckdb/src/storage/table/row_group.cpp | 28 +- .../storage/table/row_group_collection.cpp | 69 +- .../src/storage/table/row_group_reorderer.cpp | 9 + .../src/storage/table/row_id_column_data.cpp | 2 +- src/duckdb/src/storage/table/scan_state.cpp | 67 +- .../storage/table/standard_column_data.cpp | 9 +- .../src/storage/table/struct_column_data.cpp | 5 +- .../src/storage/table/table_statistics.cpp | 4 +- .../src/storage/table/update_segment.cpp | 54 +- .../table/variant/variant_shredding.cpp | 6 + .../src/storage/table/variant_column_data.cpp | 4 + src/duckdb/src/storage/table_index_list.cpp | 65 +- .../src/storage/temporary_file_manager.cpp | 43 +- src/duckdb/src/storage/wal_replay.cpp | 180 +- src/duckdb/src/storage/write_ahead_log.cpp | 52 +- src/duckdb/src/transaction/commit_state.cpp | 6 +- .../transaction/duck_transaction_manager.cpp | 24 +- .../src/transaction/meta_transaction.cpp | 10 +- src/duckdb/src/transaction/rollback_state.cpp | 12 +- .../src/transaction/transaction_context.cpp | 10 +- src/duckdb/src/transaction/undo_buffer.cpp | 58 +- .../third_party/fmt/include/fmt/format.h | 2 + src/duckdb/third_party/fsst/fsst.h | 38 +- src/duckdb/third_party/fsst/libfsst.cpp | 2 +- src/duckdb/third_party/httplib/httplib.hpp | 21 +- .../third_party/miniz/miniz_wrapper.hpp | 3 + .../third_party/parquet/parquet_types.cpp | 27 +- .../third_party/parquet/parquet_types.h | 12 +- src/duckdb/third_party/ska_sort/ska_sort.hpp | 64 +- src/duckdb/third_party/tdigest/t_digest.hpp | 52 +- .../third_party/utf8proc/utf8proc_wrapper.cpp | 13 +- src/duckdb/third_party/vergesort/vergesort.h | 30 +- .../zstd/include/zstd/common/cpu.h | 2 +- .../ub_extension_parquet_reader_variant.cpp | 6 - src/duckdb/ub_src_common.cpp | 4 +- src/duckdb/ub_src_common_arrow.cpp | 10 +- src/duckdb/ub_src_common_progress_bar.cpp | 2 - src/duckdb/ub_src_common_tree_renderer.cpp | 2 + src/duckdb/ub_src_common_types_variant.cpp | 6 + src/duckdb/ub_src_execution.cpp | 4 + ...on_operator_csv_scanner_table_function.cpp | 4 +- .../ub_src_execution_operator_helper.cpp | 2 - .../ub_src_execution_operator_persistent.cpp | 2 + src/duckdb/ub_src_function.cpp | 2 + src/duckdb/ub_src_function_scalar_string.cpp | 2 + src/duckdb/ub_src_main.cpp | 18 +- src/duckdb/ub_src_main_capi_v2.cpp | 24 +- src/duckdb/ub_src_main_chunk_scan_state.cpp | 4 - src/duckdb/ub_src_optimizer.cpp | 6 +- src/duckdb/ub_src_parallel.cpp | 2 + src/duckdb/ub_src_parser_parsed_data.cpp | 4 + src/duckdb/ub_src_parser_peg.cpp | 8 + .../ub_src_parser_peg_keyword_helper.cpp | 2 + src/duckdb/ub_src_parser_peg_transformer.cpp | 2 +- src/duckdb/ub_src_parser_statement.cpp | 2 + src/duckdb/ub_src_planner.cpp | 2 - src/duckdb/ub_src_planner_binder_tableref.cpp | 2 - src/duckdb/ub_src_planner_expression.cpp | 2 + src/duckdb/ub_src_planner_sql_export.cpp | 34 + 1572 files changed, 81800 insertions(+), 58992 deletions(-) create mode 100644 src/duckdb/extension/json/include/json_stack.hpp delete mode 100644 src/duckdb/extension/loader/dummy_static_extension_loader.cpp create mode 100644 src/duckdb/extension/loader/static_extension_autoregister.cpp create mode 100644 src/duckdb/extension/parquet/include/parquet_int96.hpp create mode 100644 src/duckdb/extension/parquet/parquet_int96.cpp create mode 100644 src/duckdb/extension/parquet/read_single_parquet_file.cpp create mode 100644 src/duckdb/src/common/arrow/arrow_format.cpp delete mode 100644 src/duckdb/src/common/arrow/arrow_merge_event.cpp delete mode 100644 src/duckdb/src/common/arrow/arrow_query_result.cpp delete mode 100644 src/duckdb/src/common/arrow/arrow_util.cpp delete mode 100644 src/duckdb/src/common/arrow/physical_arrow_batch_collector.cpp delete mode 100644 src/duckdb/src/common/arrow/physical_arrow_collector.cpp rename src/duckdb/src/common/{extra_type_info.cpp => logical_type_info.cpp} (63%) delete mode 100644 src/duckdb/src/common/progress_bar/unscented_kalman_filter.cpp create mode 100644 src/duckdb/src/common/tree_renderer/compact_tree_renderer.cpp create mode 100644 src/duckdb/src/common/types/variant/parquet_variant_encoding.cpp rename src/duckdb/{extension/parquet/reader => src/common/types}/variant/parquet_variant_iterator.cpp (93%) rename src/duckdb/{extension/parquet/reader => src/common/types}/variant/variant_binary_decoder.cpp (92%) create mode 100644 src/duckdb/src/execution/ie_join_union.cpp delete mode 100644 src/duckdb/src/execution/operator/csv_scanner/table_function/csv_multi_file_info.cpp create mode 100644 src/duckdb/src/execution/operator/csv_scanner/table_function/csv_schema_discovery.cpp create mode 100644 src/duckdb/src/execution/operator/csv_scanner/table_function/read_single_csv_file.cpp delete mode 100644 src/duckdb/src/execution/operator/helper/physical_batch_collector.cpp create mode 100644 src/duckdb/src/execution/operator/persistent/copy_batch_slicer.cpp create mode 100644 src/duckdb/src/execution/radix_ht_adaptivity.cpp rename src/duckdb/src/{optimizer => function}/builtin_function_lookup.cpp (59%) create mode 100644 src/duckdb/src/function/scalar/string/hive_partition_component.cpp create mode 100644 src/duckdb/src/include/duckdb/common/arrow/arrow_format.hpp delete mode 100644 src/duckdb/src/include/duckdb/common/arrow/arrow_merge_event.hpp delete mode 100644 src/duckdb/src/include/duckdb/common/arrow/arrow_query_result.hpp delete mode 100644 src/duckdb/src/include/duckdb/common/arrow/arrow_util.hpp delete mode 100644 src/duckdb/src/include/duckdb/common/arrow/physical_arrow_batch_collector.hpp delete mode 100644 src/duckdb/src/include/duckdb/common/arrow/physical_arrow_collector.hpp delete mode 100644 src/duckdb/src/include/duckdb/common/arrow/result_arrow_wrapper.hpp rename src/duckdb/src/include/duckdb/common/enums/{pending_execution_result.hpp => connection_type.hpp} (60%) create mode 100644 src/duckdb/src/include/duckdb/common/enums/debug_progress_verification.hpp rename src/duckdb/src/include/duckdb/common/enums/{current_transaction_state.hpp => file_sync_mode.hpp} (64%) rename src/duckdb/src/include/duckdb/common/enums/{stream_execution_result.hpp => query_result_memory_type.hpp} (57%) create mode 100644 src/duckdb/src/include/duckdb/common/enums/query_result_state.hpp create mode 100644 src/duckdb/src/include/duckdb/common/enums/result_ordering.hpp delete mode 100644 src/duckdb/src/include/duckdb/common/extra_type_info.hpp create mode 100644 src/duckdb/src/include/duckdb/common/logical_type_info.hpp rename src/duckdb/src/include/duckdb/common/{extra_type_info => logical_type_info}/enum_type_info.hpp (76%) create mode 100644 src/duckdb/src/include/duckdb/common/once_flag.hpp delete mode 100644 src/duckdb/src/include/duckdb/common/progress_bar/unscented_kalman_filter.hpp delete mode 100644 src/duckdb/src/include/duckdb/common/query_parameters.hpp create mode 100644 src/duckdb/src/include/duckdb/common/sorting/buffered_merge.hpp create mode 100644 src/duckdb/src/include/duckdb/common/stl_allocator.hpp create mode 100644 src/duckdb/src/include/duckdb/common/tree_renderer/compact_tree_renderer.hpp create mode 100644 src/duckdb/src/include/duckdb/common/typed_index.hpp rename src/duckdb/{extension/parquet/include/reader => src/include/duckdb/common/types}/variant/parquet_variant_iterator.hpp (79%) rename src/duckdb/{extension/parquet/include/reader => src/include/duckdb/common/types}/variant/variant_binary_decoder.hpp (92%) create mode 100644 src/duckdb/src/include/duckdb/execution/aggregate_ht_adaptivity_state.hpp create mode 100644 src/duckdb/src/include/duckdb/execution/ie_join_union.hpp delete mode 100644 src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_multi_file_info.hpp create mode 100644 src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_schema_discovery.hpp delete mode 100644 src/duckdb/src/include/duckdb/execution/operator/helper/physical_batch_collector.hpp create mode 100644 src/duckdb/src/include/duckdb/execution/operator/persistent/copy_batch_slicer.hpp create mode 100644 src/duckdb/src/include/duckdb/execution/radix_ht_adaptivity.hpp create mode 100644 src/duckdb/src/include/duckdb/execution/radix_ht_sink_state.hpp rename src/duckdb/src/include/duckdb/{optimizer => function}/builtin_function_lookup.hpp (73%) create mode 100644 src/duckdb/src/include/duckdb/main/active_query_context.hpp create mode 100644 src/duckdb/src/include/duckdb/main/capi/capi_function_signature.hpp create mode 100644 src/duckdb/src/include/duckdb/main/capi_v2/capi_v2_function_internal.hpp delete mode 100644 src/duckdb/src/include/duckdb/main/chunk_scan_state.hpp delete mode 100644 src/duckdb/src/include/duckdb/main/chunk_scan_state/batched_data_collection.hpp delete mode 100644 src/duckdb/src/include/duckdb/main/chunk_scan_state/query_result.hpp create mode 100644 src/duckdb/src/include/duckdb/main/extension/external_extension_provider.hpp delete mode 100644 src/duckdb/src/include/duckdb/main/extension/generated_extension_loader.hpp create mode 100644 src/duckdb/src/include/duckdb/main/extension/linked_extension_registry.hpp delete mode 100644 src/duckdb/src/include/duckdb/main/materialized_query_result.hpp delete mode 100644 src/duckdb/src/include/duckdb/main/pending_query_result.hpp create mode 100644 src/duckdb/src/include/duckdb/main/query_parameters.hpp create mode 100644 src/duckdb/src/include/duckdb/main/query_result_stream.hpp create mode 100644 src/duckdb/src/include/duckdb/main/result_format.hpp create mode 100644 src/duckdb/src/include/duckdb/main/result_unit.hpp create mode 100644 src/duckdb/src/include/duckdb/main/retained_result_collection.hpp delete mode 100644 src/duckdb/src/include/duckdb/main/stream_query_result.hpp create mode 100644 src/duckdb/src/include/duckdb/optimizer/cte_join_filter_collector.hpp create mode 100644 src/duckdb/src/include/duckdb/optimizer/cte_join_filter_pusher.hpp create mode 100644 src/duckdb/src/include/duckdb/parallel/progress_verifier.hpp delete mode 100644 src/duckdb/src/include/duckdb/parser/dialect_extension.hpp create mode 100644 src/duckdb/src/include/duckdb/parser/parsed_data/alter_schema_info.hpp create mode 100644 src/duckdb/src/include/duckdb/parser/parsed_data/external_resource_options.hpp create mode 100644 src/duckdb/src/include/duckdb/parser/peg/dialect_extension.hpp create mode 100644 src/duckdb/src/include/duckdb/parser/peg/grammar_literal_table.hpp create mode 100644 src/duckdb/src/include/duckdb/parser/peg/keyword_categories.hpp create mode 100644 src/duckdb/src/include/duckdb/parser/peg/literal_info.hpp create mode 100644 src/duckdb/src/include/duckdb/parser/peg/matcher/literal_choice_matcher.hpp create mode 100644 src/duckdb/src/include/duckdb/parser/peg/passthrough_dialect.hpp create mode 100644 src/duckdb/src/include/duckdb/parser/statement/passthrough_statement.hpp create mode 100644 src/duckdb/src/include/duckdb/planner/expression/window_range_info.hpp create mode 100644 src/duckdb/src/include/duckdb/planner/logical_plan_sql_export_context.hpp create mode 100644 src/duckdb/src/include/duckdb/planner/logical_plan_sql_exporter.hpp create mode 100644 src/duckdb/src/include/duckdb/planner/sql_export/bound_expression_sql_exporter_internal.hpp create mode 100644 src/duckdb/src/include/duckdb/planner/sql_export/logical_plan_sql_exporter_internal.hpp create mode 100644 src/duckdb/src/include/duckdb_static_extension.h create mode 100644 src/duckdb/src/main/capi/v2/capi_v2_config.cpp create mode 100644 src/duckdb/src/main/capi/v2/capi_v2_custom_option.cpp delete mode 100644 src/duckdb/src/main/capi/v2/capi_v2_database.cpp create mode 100644 src/duckdb/src/main/capi/v2/capi_v2_factory.cpp create mode 100644 src/duckdb/src/main/capi/v2/capi_v2_func.cpp create mode 100644 src/duckdb/src/main/capi/v2/capi_v2_func_docs.cpp create mode 100644 src/duckdb/src/main/capi/v2/capi_v2_func_macro.cpp create mode 100644 src/duckdb/src/main/capi/v2/capi_v2_func_pragma.cpp create mode 100644 src/duckdb/src/main/capi/v2/capi_v2_instance.cpp create mode 100644 src/duckdb/src/main/capi/v2/capi_v2_log_sink.cpp create mode 100644 src/duckdb/src/main/capi/v2/capi_v2_tokenizer.cpp create mode 100644 src/duckdb/src/main/capi/v2/capi_v2_utf8.cpp delete mode 100644 src/duckdb/src/main/chunk_scan_state.cpp delete mode 100644 src/duckdb/src/main/chunk_scan_state/batched_data_collection.cpp delete mode 100644 src/duckdb/src/main/chunk_scan_state/query_result.cpp create mode 100644 src/duckdb/src/main/client_context_execution.cpp create mode 100644 src/duckdb/src/main/extension/extension_install_dynamic.cpp create mode 100644 src/duckdb/src/main/extension/extension_load_dynamic.cpp create mode 100644 src/duckdb/src/main/extension/external_extension_provider.cpp create mode 100644 src/duckdb/src/main/extension/linked_extension_registry.cpp create mode 100644 src/duckdb/src/main/http/http_client_httplib.cpp delete mode 100644 src/duckdb/src/main/materialized_query_result.cpp create mode 100644 src/duckdb/src/main/parser_context.cpp delete mode 100644 src/duckdb/src/main/pending_query_result.cpp create mode 100644 src/duckdb/src/main/query_result_stream.cpp create mode 100644 src/duckdb/src/main/result_format.cpp create mode 100644 src/duckdb/src/main/result_unit.cpp create mode 100644 src/duckdb/src/main/retained_result_collection.cpp delete mode 100644 src/duckdb/src/main/stream_query_result.cpp create mode 100644 src/duckdb/src/optimizer/cte_join_filter_collector.cpp create mode 100644 src/duckdb/src/optimizer/cte_join_filter_pusher.cpp create mode 100644 src/duckdb/src/parallel/progress_verifier.cpp create mode 100644 src/duckdb/src/parser/parsed_data/alter_schema_info.cpp create mode 100644 src/duckdb/src/parser/parsed_data/external_resource_options.cpp create mode 100644 src/duckdb/src/parser/peg/dialect_extension.cpp create mode 100644 src/duckdb/src/parser/peg/keyword_helper/grammar_literal_table.cpp create mode 100644 src/duckdb/src/parser/peg/matcher_first_set.cpp create mode 100644 src/duckdb/src/parser/peg/operator_matcher.cpp create mode 100644 src/duckdb/src/parser/peg/passthrough_dialect.cpp create mode 100644 src/duckdb/src/parser/peg/transformer/transform_external_resource_source.cpp delete mode 100644 src/duckdb/src/parser/peg/transformer/transform_generated.cpp create mode 100644 src/duckdb/src/parser/statement/passthrough_statement.cpp delete mode 100644 src/duckdb/src/planner/binder/tableref/bind_named_parameters.cpp delete mode 100644 src/duckdb/src/planner/bound_expression_sql_exporter.cpp create mode 100644 src/duckdb/src/planner/expression/window_range_info.cpp create mode 100644 src/duckdb/src/planner/sql_export/bound_expression_sql_exporter.cpp create mode 100644 src/duckdb/src/planner/sql_export/logical_plan_sql_exporter.cpp create mode 100644 src/duckdb/src/planner/sql_export/sql_export_composition.cpp create mode 100644 src/duckdb/src/planner/sql_export/sql_export_constants.cpp create mode 100644 src/duckdb/src/planner/sql_export/sql_export_cte.cpp create mode 100644 src/duckdb/src/planner/sql_export/sql_export_dml.cpp create mode 100644 src/duckdb/src/planner/sql_export/sql_export_functions.cpp create mode 100644 src/duckdb/src/planner/sql_export/sql_export_joins.cpp create mode 100644 src/duckdb/src/planner/sql_export/sql_export_limit.cpp create mode 100644 src/duckdb/src/planner/sql_export/sql_export_pivot.cpp create mode 100644 src/duckdb/src/planner/sql_export/sql_export_relations.cpp create mode 100644 src/duckdb/src/planner/sql_export/sql_export_scope.cpp create mode 100644 src/duckdb/src/planner/sql_export/sql_export_sources.cpp create mode 100644 src/duckdb/src/planner/sql_export/sql_export_statements.cpp create mode 100644 src/duckdb/src/planner/sql_export/sql_export_values.cpp create mode 100644 src/duckdb/src/planner/sql_export/sql_export_window.cpp create mode 100644 src/duckdb/src/planner/sql_export/table_function_sql_export.cpp delete mode 100644 src/duckdb/ub_extension_parquet_reader_variant.cpp delete mode 100644 src/duckdb/ub_src_main_chunk_scan_state.cpp create mode 100644 src/duckdb/ub_src_planner_sql_export.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 910082e16..e57da4cce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,6 +142,7 @@ set(DUCKDB_SRC_FILES src/duckdb/extension/json/json_scan.cpp src/duckdb/extension/json/json_serializer.cpp src/duckdb/extension/json/serialize_json.cpp + src/duckdb/extension/loader/static_extension_autoregister.cpp src/duckdb/extension/parquet/column_reader.cpp src/duckdb/extension/parquet/column_writer.cpp src/duckdb/extension/parquet/parquet_column_schema.cpp @@ -151,6 +152,7 @@ set(DUCKDB_SRC_FILES src/duckdb/extension/parquet/parquet_file_metadata_cache.cpp src/duckdb/extension/parquet/parquet_float16.cpp src/duckdb/extension/parquet/parquet_geometry.cpp + src/duckdb/extension/parquet/parquet_int96.cpp src/duckdb/extension/parquet/parquet_metadata.cpp src/duckdb/extension/parquet/parquet_multi_file_info.cpp src/duckdb/extension/parquet/parquet_prefetch_cost_model.cpp @@ -159,6 +161,8 @@ set(DUCKDB_SRC_FILES src/duckdb/extension/parquet/parquet_statistics.cpp src/duckdb/extension/parquet/parquet_timestamp.cpp src/duckdb/extension/parquet/parquet_writer.cpp + src/duckdb/extension/parquet/read_single_parquet_file.cpp + src/duckdb/extension/parquet/reader/variant/parquet_variant_shredding.cpp src/duckdb/extension/parquet/serialize_parquet.cpp src/duckdb/extension/parquet/zstd_file_system.cpp src/duckdb/generated_extension_loader_package_build.cpp @@ -222,9 +226,14 @@ set(DUCKDB_SRC_FILES src/duckdb/src/main/extension/extension_alias.cpp src/duckdb/src/main/extension/extension_helper.cpp src/duckdb/src/main/extension/extension_install.cpp + src/duckdb/src/main/extension/extension_install_dynamic.cpp src/duckdb/src/main/extension/extension_load.cpp + src/duckdb/src/main/extension/extension_load_dynamic.cpp src/duckdb/src/main/extension/extension_loader.cpp src/duckdb/src/main/extension/extension_repository_manager.cpp + src/duckdb/src/main/extension/external_extension_provider.cpp + src/duckdb/src/main/extension/linked_extension_registry.cpp + src/duckdb/src/main/http/http_client_httplib.cpp src/duckdb/src/optimizer/matcher/expression_matcher.cpp src/duckdb/third_party/brotli/common/constants.cpp src/duckdb/third_party/brotli/common/context.cpp @@ -371,7 +380,6 @@ set(DUCKDB_SRC_FILES src/duckdb/ub_extension_json_json_functions.cpp src/duckdb/ub_extension_parquet_decoder.cpp src/duckdb/ub_extension_parquet_reader.cpp - src/duckdb/ub_extension_parquet_reader_variant.cpp src/duckdb/ub_extension_parquet_writer.cpp src/duckdb/ub_extension_parquet_writer_variant.cpp src/duckdb/ub_src_catalog.cpp @@ -443,7 +451,6 @@ set(DUCKDB_SRC_FILES src/duckdb/ub_src_main.cpp src/duckdb/ub_src_main_buffered_data.cpp src/duckdb/ub_src_main_capi_v2.cpp - src/duckdb/ub_src_main_chunk_scan_state.cpp src/duckdb/ub_src_main_http.cpp src/duckdb/ub_src_main_profiler.cpp src/duckdb/ub_src_main_relation.cpp @@ -479,6 +486,7 @@ set(DUCKDB_SRC_FILES src/duckdb/ub_src_planner_expression_binder.cpp src/duckdb/ub_src_planner_filter.cpp src/duckdb/ub_src_planner_operator.cpp + src/duckdb/ub_src_planner_sql_export.cpp src/duckdb/ub_src_planner_subquery.cpp src/duckdb/ub_src_storage.cpp src/duckdb/ub_src_storage_buffer.cpp diff --git a/src/duckdb/extension/core_functions/aggregate/algebraic/avg.cpp b/src/duckdb/extension/core_functions/aggregate/algebraic/avg.cpp index 6d5b67faf..2b7768041 100644 --- a/src/duckdb/extension/core_functions/aggregate/algebraic/avg.cpp +++ b/src/duckdb/extension/core_functions/aggregate/algebraic/avg.cpp @@ -48,7 +48,7 @@ struct KahanAvgState { void Combine(const KahanAvgState &other) { this->count += other.count; KahanAddInternal(other.value, this->value, this->err); - KahanAddInternal(other.err, this->value, this->err); + KahanAddInternal(-other.err, this->value, this->err); } }; @@ -143,6 +143,27 @@ struct DiscreteAverageOperation : public BaseSumOperation>(states + i * stride); + if (state.count == 0) { + continue; + } + const auto max_value = hugeint_t(0, state.count) * hugeint_t(Interval::MICROS_PER_DAY); + if (state.value < 0 || state.value > max_value) { + throw InvalidInputException("Invalid avg state - the average of %s over %llu values is not a valid time", + state.value.ToString(), state.count); + } + } +} + +AggregateStateLayout TimeAverageStateLayout(AggregateLayoutInput &input) { + auto layout = AggregateFunction::StructStateLayout>(input); + layout.validate_state = ValidateTimeAverageStates; + return layout; +} + struct HugeintAverageOperation : public BaseSumOperation { template static void RepeatedCombine(const STATE &source, STATE &target, AggregateInputData &input, idx_t count) { @@ -182,7 +203,7 @@ struct KahanAverageOperation : public BaseSumOperation(shifted_days / hugeint_t(count)); target.micros += micros_remainder; } } @@ -343,12 +365,14 @@ AggregateFunctionSet AvgFun::GetFunctions() { auto time_avg = AggregateFunction::UnaryAggregate, int64_t, int64_t, DiscreteAverageOperation>( LogicalType::TIME, LogicalType::TIME); time_avg.GetSignature().GetParameter(0).SetName("x"); + time_avg.SetStructStateExport(TimeAverageStateLayout); avg.AddFunction(time_avg); auto time_tz_avg = AggregateFunction::UnaryAggregate, dtime_tz_t, dtime_tz_t, TimeTZAverageOperation>( LogicalType::TIME_TZ, LogicalType::TIME_TZ); time_tz_avg.GetSignature().GetParameter(0).SetName("x"); + time_tz_avg.SetStructStateExport(TimeAverageStateLayout); avg.AddFunction(time_tz_avg); return avg; diff --git a/src/duckdb/extension/core_functions/aggregate/distributive/approx_count.cpp b/src/duckdb/extension/core_functions/aggregate/distributive/approx_count.cpp index 4b01f68e3..f2522393a 100644 --- a/src/duckdb/extension/core_functions/aggregate/distributive/approx_count.cpp +++ b/src/duckdb/extension/core_functions/aggregate/distributive/approx_count.cpp @@ -1,5 +1,8 @@ #include "duckdb/common/exception.hpp" #include "duckdb/common/types/hyperloglog.hpp" +#include "duckdb/common/vector/flat_vector.hpp" +#include "duckdb/common/vector/vector_iterator.hpp" +#include "duckdb/common/vector/vector_writer.hpp" #include "core_functions/aggregate/distributive_functions.hpp" namespace duckdb { @@ -9,8 +12,10 @@ namespace duckdb { // Otmar Ertl, arXiv:1702.01284 namespace { +using ApproxCountDistinctHLL = HyperLogLogP<10>; + struct ApproxDistinctCountState { - HyperLogLogP<10> hll; + ApproxCountDistinctHLL hll; }; struct ApproxCountDistinctFunction { @@ -54,6 +59,91 @@ void ApproxCountDistinctUpdateFunction(Vector inputs[], AggregateInputData &, id } } +//===--------------------------------------------------------------------===// +// State Export +//===--------------------------------------------------------------------===// +//! Exported state: STRUCT(precision UTINYINT, registers UTINYINT[]) - the sketch itself. A HyperLogLog is a fixed +//! array of 2^precision one-byte registers, and merging two sketches takes the per-register maximum, so an exported +//! state combines (combine_aggr) and finalizes exactly as the in-memory one does. The static field layouts describe +//! scalars and linked lists, not a fixed array, hence the explicit callbacks. +LogicalType ApproxCountDistinctExportType() { + child_list_t children; + children.emplace_back("precision", LogicalType::UTINYINT); + children.emplace_back("registers", LogicalType::LIST(LogicalType::UTINYINT)); + return LogicalType::STRUCT(std::move(children)); +} + +//! The shape of the exported state: STRUCT(precision, registers UTINYINT[]) +using APPROX_COUNT_DISTINCT_EXPORT_TYPE = VectorStructType>; + +AggregateStateLayout ApproxCountDistinctGetStateType(AggregateLayoutInput &) { + AggregateStateLayout layout; + layout.type = ApproxCountDistinctExportType(); + layout.total_state_size = AlignValue(sizeof(ApproxDistinctCountState)); + return layout; +} + +void ApproxCountDistinctExportState(Vector &state_vector, AggregateFinalizeInputData &, Vector &result, idx_t count, + idx_t offset) { + auto states = state_vector.Values(); + auto writer = FlatVector::Writer(result, count, offset); + for (idx_t i = 0; i < count; i++) { + // an empty sketch (all registers zero) is exported as-is rather than as NULL: finalize does not import NULL + // states, and approx_count_distinct of no values is 0, not NULL + auto &hll = states[i].GetValue()->hll; + writer.WriteValue([&](auto &precision_writer, auto ®isters_writer) { + precision_writer.WriteValue(UnsafeNumericCast(ApproxCountDistinctHLL::PRECISION)); + idx_t register_idx = 0; + for (auto ®ister_writer : registers_writer.WriteList(ApproxCountDistinctHLL::M)) { + register_writer.WriteValue(hll.GetRegister(register_idx++)); + } + }); + } +} + +void ApproxCountDistinctImportState(AggregateImportInputData &input) { + const auto &layout = input.layout; + const auto &input_vec = input.input_vec; + const auto count = input_vec.size(); + const auto dest_buffer = input.dest_buffer; + auto entries = input_vec.Values(); + for (idx_t i = 0; i < count; i++) { + auto &state = *reinterpret_cast(dest_buffer + i * layout.total_state_size); + state.hll = ApproxCountDistinctHLL(); + const auto entry = entries[i]; + if (!entry.IsValid()) { + // NULL input (e.g. to_aggregate_state(NULL, ...)) - leave the sketch empty + continue; + } + const auto precision_entry = entry.template GetChildValue<0>(); + const auto register_list = entry.template GetChildValue<1>(); + if (!precision_entry.IsValid() || !register_list.IsValid()) { + throw InvalidInputException("Invalid approx_count_distinct state - the state fields cannot be NULL"); + } + if (precision_entry.GetValue() != ApproxCountDistinctHLL::PRECISION) { + throw InvalidInputException("Invalid approx_count_distinct state - expected precision %d, got %d", + ApproxCountDistinctHLL::PRECISION, precision_entry.GetValue()); + } + if (register_list.GetListLength() != ApproxCountDistinctHLL::M) { + throw InvalidInputException("Invalid approx_count_distinct state - expected %llu registers, got %llu", + ApproxCountDistinctHLL::M, register_list.GetListLength()); + } + idx_t register_idx = 0; + for (const auto register_entry : register_list.GetChildValues()) { + if (!register_entry.IsValid()) { + throw InvalidInputException("Invalid approx_count_distinct state - the registers cannot be NULL"); + } + const auto value = register_entry.GetValue(); + // a register holds the position of the first set bit of a hash, which is at most Q + 1 + if (value > ApproxCountDistinctHLL::Q + 1) { + throw InvalidInputException("Invalid approx_count_distinct state - register value %d exceeds %d", value, + ApproxCountDistinctHLL::Q + 1); + } + state.hll.SetRegister(register_idx++, value); + } + } +} + AggregateFunction GetApproxCountDistinctFunction(const LogicalType &input_type) { auto fun = AggregateFunction( {}, LogicalTypeId::BIGINT, AggregateFunction::StateSize, @@ -63,6 +153,8 @@ AggregateFunction GetApproxCountDistinctFunction(const LogicalType &input_type) AggregateFunction::StateFinalize, nullptr); fun.GetSignature().AddParameter("any", input_type); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); + fun.SetStateExportCallbacks(ApproxCountDistinctGetStateType, ApproxCountDistinctExportState, + ApproxCountDistinctImportState); return fun; } diff --git a/src/duckdb/extension/core_functions/aggregate/distributive/arg_min_max.cpp b/src/duckdb/extension/core_functions/aggregate/distributive/arg_min_max.cpp index 035bd7342..4059847d2 100644 --- a/src/duckdb/extension/core_functions/aggregate/distributive/arg_min_max.cpp +++ b/src/duckdb/extension/core_functions/aggregate/distributive/arg_min_max.cpp @@ -29,19 +29,19 @@ struct ArgMinMaxValueAssign { template <> struct ArgMinMaxValueAssign { //! The size of the arena allocation for a non-inlined string value - uint32_t alloc_size; + idx_t alloc_size; void Assign(string_t &target, string_t new_value, AggregateInputData &aggregate_input_data) { if (new_value.IsInlined()) { target = new_value; alloc_size = 0; } else { - auto len = UnsafeNumericCast(new_value.GetSize()); + auto len = UnsafeNumericCast(new_value.GetSize()); char *ptr; if (alloc_size >= len) { ptr = target.GetDataWriteable(); } else { - alloc_size = UnsafeNumericCast(NextPowerOfTwo(len)); + alloc_size = UnsafeNumericCast(NextPowerOfTwo(len)); ptr = char_ptr_cast(aggregate_input_data.allocator.Allocate(alloc_size)); } memcpy(ptr, new_value.GetData(), len); @@ -210,9 +210,7 @@ struct ArgMinMaxBase { auto &context = input.GetClientContext(); auto &function = input.GetBoundFunction(); auto &arguments = input.GetArguments(); - if (arguments[1]->GetReturnType().InternalType() == PhysicalType::VARCHAR) { - ExpressionBinder::PushCollation(context, arguments[1], arguments[1]->GetReturnType()); - } + ExpressionBinder::PushCollation(context, arguments[1], arguments[1]->GetReturnType()); function.GetArguments()[0] = arguments[0]->GetReturnType(); function.GetArguments()[1] = arguments[1]->GetReturnType(); function.SetReturnType(arguments[0]->GetReturnType()); @@ -379,9 +377,7 @@ struct VectorArgMinMaxBase : ArgMinMaxBase { auto &context = input.GetClientContext(); auto &function = input.GetBoundFunction(); auto &arguments = input.GetArguments(); - if (arguments[1]->GetReturnType().InternalType() == PhysicalType::VARCHAR) { - ExpressionBinder::PushCollation(context, arguments[1], arguments[1]->GetReturnType()); - } + ExpressionBinder::PushCollation(context, arguments[1], arguments[1]->GetReturnType()); function.GetArguments()[0] = arguments[0]->GetReturnType(); function.GetArguments()[1] = arguments[1]->GetReturnType(); function.SetReturnType(arguments[0]->GetReturnType()); @@ -886,6 +882,7 @@ void SpecializeArgMinMaxNullNFunction(PhysicalType val_type, PhysicalType arg_ty template unique_ptr ArgMinMaxNBind(BindAggregateFunctionInput &input) { + auto &context = input.GetClientContext(); auto &function = input.GetBoundFunction(); auto &arguments = input.GetArguments(); for (auto &arg : arguments) { @@ -893,6 +890,7 @@ unique_ptr ArgMinMaxNBind(BindAggregateFunctionInput &input) { throw ParameterNotResolvedException(); } } + ExpressionBinder::PushCollation(context, arguments[1], arguments[1]->GetReturnType()); const auto val_type = arguments[0]->GetReturnType().InternalType(); const auto arg_type = arguments[1]->GetReturnType().InternalType(); @@ -918,7 +916,7 @@ void AddArgMinMaxNFunction(AggregateFunctionSet &set) { .AddParameter("val", LogicalTypeId::ANY) .AddParameter("N", LogicalType::BIGINT); - return set.AddFunction(function); + set.AddFunction(function); } } // namespace diff --git a/src/duckdb/extension/core_functions/aggregate/distributive/bitagg.cpp b/src/duckdb/extension/core_functions/aggregate/distributive/bitagg.cpp index ef7f2f19b..e2e94fbee 100644 --- a/src/duckdb/extension/core_functions/aggregate/distributive/bitagg.cpp +++ b/src/duckdb/extension/core_functions/aggregate/distributive/bitagg.cpp @@ -24,7 +24,7 @@ static AggregateFunction NameArgParameter(AggregateFunction fun) { } template -AggregateFunction GetBitfieldUnaryAggregate(LogicalType type) { +AggregateFunction GetBitfieldUnaryAggregate(const LogicalType &type) { switch (type.id()) { case LogicalTypeId::TINYINT: return NameArgParameter(AggregateFunction::UnaryAggregate, int8_t, int8_t, OP>(type, type)); @@ -111,7 +111,9 @@ struct BitwiseOperation { }; template -struct NumericBitwiseOperation : public BitwiseOperation, public ClusteredStateCopy { +struct NumericBitwiseOperation // NOLINT(bugprone-crtp-constructor-accessibility) + : public BitwiseOperation, + public ClusteredStateCopy { template static void UpdateClusteredLocal(STATE &local, const INPUT_TYPE &input) { if (!local.is_set) { diff --git a/src/duckdb/extension/core_functions/aggregate/distributive/bitstring_agg.cpp b/src/duckdb/extension/core_functions/aggregate/distributive/bitstring_agg.cpp index 3f1b8affc..343915caa 100644 --- a/src/duckdb/extension/core_functions/aggregate/distributive/bitstring_agg.cpp +++ b/src/duckdb/extension/core_functions/aggregate/distributive/bitstring_agg.cpp @@ -123,15 +123,12 @@ struct BitStringAggOperation { if (min > max) { throw InvalidInputException("Invalid explicit bitstring range: Minimum (%d) > maximum (%d)", min, max); } - INPUT_TYPE result; - if (!TrySubtractOperator::Operation(max, min, result)) { + // the difference of two (at most 64-bit) values does not always fit in their own type, but it fits in a hugeint + const auto range = Hugeint::Convert(max) - Hugeint::Convert(min); + if (range >= Hugeint::Convert(NumericLimits::Maximum())) { return NumericLimits::Maximum(); } - auto val = NumericCast(result); - if (val == NumericLimits::Maximum()) { - return val; - } - return val + 1; + return Hugeint::Cast(range) + 1; } template diff --git a/src/duckdb/extension/core_functions/aggregate/distributive/kurtosis.cpp b/src/duckdb/extension/core_functions/aggregate/distributive/kurtosis.cpp index c7b4853a5..e90d27661 100644 --- a/src/duckdb/extension/core_functions/aggregate/distributive/kurtosis.cpp +++ b/src/duckdb/extension/core_functions/aggregate/distributive/kurtosis.cpp @@ -1,3 +1,4 @@ +#include "duckdb/common/operator/add.hpp" #include "core_functions/aggregate/distributive_functions.hpp" #include "duckdb/common/exception.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" @@ -47,7 +48,9 @@ struct KurtosisOperation { if (source.n == 0) { return; } - target.n += source.n; + if (!TryAddOperator::Operation(target.n, source.n, target.n)) { + throw OutOfRangeException("Overflow in aggregate state combine"); + } target.sum += source.sum; target.sum_sqr += source.sum_sqr; target.sum_cub += source.sum_cub; diff --git a/src/duckdb/extension/core_functions/aggregate/distributive/sum.cpp b/src/duckdb/extension/core_functions/aggregate/distributive/sum.cpp index a2ec57f6d..bcb7594c2 100644 --- a/src/duckdb/extension/core_functions/aggregate/distributive/sum.cpp +++ b/src/duckdb/extension/core_functions/aggregate/distributive/sum.cpp @@ -95,10 +95,10 @@ struct ClusteredSumOperation : public ClusteredSumStateCopy { static void ExecuteFlatI64HugeintSum(const INPUT_TYPE *vals, const ClusteredAggr &clustered, const SelectionVector *isel, const sel_t *cluster_iter) { idx_t pos = 0; - for (idx_t r = 0; r < clustered.n_group_runs; r++) { - auto &state = *reinterpret_cast(clustered.group_runs[r].state); - const auto *run_sel = clustered.group_runs[r].sel; - const idx_t run_count = clustered.group_runs[r].count; + for (auto &run : clustered.runs()) { + auto &state = *reinterpret_cast(run.state); + const auto *run_sel = run.sel; + const idx_t run_count = run.count; if (run_count == 0) { continue; } @@ -144,10 +144,10 @@ struct ClusteredSumOperation : public ClusteredSumStateCopy { input.ToUnifiedFormat(idata); const auto *dict_sel = idata.sel->data(); auto &validity = idata.validity; - for (idx_t r = 0; r < clustered.n_group_runs; r++) { - auto &state = *reinterpret_cast(clustered.group_runs[r].state); - const auto *run_sel = clustered.group_runs[r].sel; - const idx_t run_count = clustered.group_runs[r].count; + for (auto &run : clustered.runs()) { + auto &state = *reinterpret_cast(run.state); + const auto *run_sel = run.sel; + const idx_t run_count = run.count; int64_t local64 = 0; if (run_sel) { for (idx_t k = 0; k < run_count; k++) { @@ -230,8 +230,34 @@ struct HugeintSumOperation } }; -unique_ptr SumNoOverflowBind(BindAggregateFunctionInput &input) { - throw BinderException("sum_no_overflow is for internal use only!"); +//! The sum of a DECIMAL is a DECIMAL(38) - finalizing throws if the sum does not fit in it +template +struct DecimalSumOperation : public OP { + template + static void Finalize(STATE &state, T &target, AggregateFinalizeData &finalize_data) { + OP::template Finalize(state, target, finalize_data); + if (!state.is_set) { + return; + } + const auto &max_value = Hugeint::POWERS_OF_TEN[Decimal::MAX_WIDTH_DECIMAL]; + if (target >= max_value || target <= -max_value) { + throw OutOfRangeException("Overflow in SUM of DECIMAL: the result does not fit in DECIMAL(%d)", + Decimal::MAX_WIDTH_DECIMAL); + } + } +}; + +//! Binds the regular sum that sum_no_overflow is a specialized implementation of +pair> BindLogicalSum(ClientContext &context, + vector> &arguments) { + vector argument_types; + for (auto &argument : arguments) { + argument_types.push_back(argument->GetReturnType()); + } + auto &entry = Catalog::GetEntry( + context, QualifiedName(Identifier::SystemCatalog(), Identifier::DefaultSchema(), Identifier("sum"))); + FunctionBinder binder(context); + return binder.ResolveFunction(entry.functions.GetFunctionByArguments(context, argument_types), arguments); } void SumNoOverflowSerialize(Serializer &serializer, const optional_ptr bind_data, @@ -244,15 +270,10 @@ unique_ptr SumNoOverflowDeserialize(Deserializer &deserializer, Bo auto &return_type = deserializer.Get(); auto &children = deserializer.Get(); vector> arguments; - vector argument_types; for (auto &child : children) { arguments.push_back(child.get().Copy()); - argument_types.push_back(child.get().GetReturnType()); } - auto &entry = Catalog::GetEntry( - context, QualifiedName(Identifier::SystemCatalog(), Identifier::DefaultSchema(), Identifier("sum"))); - FunctionBinder binder(context); - auto logical = binder.ResolveFunction(entry.functions.GetFunctionByArguments(context, argument_types), arguments); + auto logical = BindLogicalSum(context, arguments); if (logical.second || (!return_type.IsAggregateState() && logical.first.GetReturnType() != return_type)) { throw SerializationException("Cannot reconstruct the logical sum signature"); } @@ -262,6 +283,8 @@ unique_ptr SumNoOverflowDeserialize(Deserializer &deserializer, Bo return nullptr; } +unique_ptr SumNoOverflowBind(BindAggregateFunctionInput &input); + AggregateFunction GetSumAggregateNoOverflow(PhysicalType type) { switch (type) { case PhysicalType::INT32: { @@ -301,6 +324,20 @@ AggregateFunction GetSumAggregateNoOverflowDecimal() { return aggr; } +//! Binding sum_no_overflow by name (e.g. when re-binding an exported aggregate state) delegates to the regular sum, +//! with the no-overflow implementation swapped in as statistics propagation does +unique_ptr SumNoOverflowBind(BindAggregateFunctionInput &input) { + auto &function = input.GetBoundFunction(); + auto &arguments = input.GetArguments(); + const auto parameter_type = function.GetArguments()[0]; + auto logical = BindLogicalSum(input.GetClientContext(), arguments); + if (parameter_type.id() != LogicalTypeId::DECIMAL) { + logical.first.ReplaceImplementation(GetSumAggregateNoOverflow(parameter_type.InternalType())); + } + function = std::move(logical.first); + return std::move(logical.second); +} + unique_ptr SumPropagateStats(ClientContext &context, BoundAggregateExpression &expr, AggregateStatisticsInput &input) { if (!input.node_stats || !input.node_stats->has_max_cardinality) { @@ -416,11 +453,36 @@ AggregateFunction GetSumAggregate(PhysicalType type) { } } +template +AggregateFunction GetDecimalSumAggregate(const LogicalType &input_type) { + auto function = + AggregateFunction::UnaryAggregate, INPUT_TYPE, hugeint_t, DecimalSumOperation>( + input_type, LogicalType::HUGEINT); + function.GetSignature().GetParameter(0).SetName("arg"); + function.SetStatisticsCallback(SumPropagateStats); + function.SetOrderDependent(AggregateOrderDependent::NOT_ORDER_DEPENDENT); + return function; +} + +AggregateFunction GetDecimalSumAggregate(PhysicalType type) { + switch (type) { + case PhysicalType::INT32: + return GetDecimalSumAggregate(LogicalType::INTEGER); + case PhysicalType::INT64: + return GetDecimalSumAggregate(LogicalType::BIGINT); + case PhysicalType::INT128: + return GetDecimalSumAggregate(LogicalType::HUGEINT); + default: + // the sum of smaller decimals is accumulated in an int64_t, which always fits in a DECIMAL(38) + return GetSumAggregate(type); + } +} + unique_ptr BindDecimalSum(BindAggregateFunctionInput &input) { auto &function = input.GetBoundFunction(); auto &arguments = input.GetArguments(); auto decimal_type = arguments[0]->GetReturnType(); - function.ReplaceImplementation(GetSumAggregate(decimal_type.InternalType())); + function.ReplaceImplementation(GetDecimalSumAggregate(decimal_type.InternalType())); function.SetName("sum"); function.GetArguments()[0] = decimal_type; function.SetReturnType(LogicalType::DECIMAL(Decimal::MAX_WIDTH_DECIMAL, DecimalType::GetScale(decimal_type))); diff --git a/src/duckdb/extension/core_functions/aggregate/holistic/approx_top_k.cpp b/src/duckdb/extension/core_functions/aggregate/holistic/approx_top_k.cpp index 3eb96ad80..4f553ece8 100644 --- a/src/duckdb/extension/core_functions/aggregate/holistic/approx_top_k.cpp +++ b/src/duckdb/extension/core_functions/aggregate/holistic/approx_top_k.cpp @@ -49,7 +49,7 @@ struct ApproxTopKValue { //! Allocated data char *dataptr = nullptr; uint32_t size = 0; - uint32_t capacity = 0; + idx_t capacity = 0; }; struct InternalApproxTopKState { @@ -94,7 +94,7 @@ struct InternalApproxTopKState { value.size = UnsafeNumericCast(input.str.GetSize()); if (value.size > value.capacity) { // need to re-allocate for this value - value.capacity = UnsafeNumericCast(NextPowerOfTwo(value.size)); + value.capacity = NextPowerOfTwo(value.size); value.dataptr = char_ptr_cast(allocator.Allocate(value.capacity)); } // copy over the data @@ -510,6 +510,10 @@ void ApproxTopKImportState(AggregateImportInputData &input) { // NULL input - leave the state empty continue; } + if (FlatVector::IsNull(fields[0], i) || FlatVector::IsNull(value_lists, i) || + FlatVector::IsNull(filter_lists, i)) { + throw InvalidInputException("Invalid approx_top_k state - the k, values and filter cannot be NULL"); + } auto internal_state = make_uniq(); auto &target = *internal_state; target.Initialize(k_data[i]); @@ -537,10 +541,24 @@ void ApproxTopKImportState(AggregateImportInputData &input) { if (!inserted) { throw InvalidInputException("Invalid approx_top_k state - the state values must be unique"); } + if (FlatVector::IsNull(value_fields[1], idx)) { + throw InvalidInputException("Invalid approx_top_k state - the state counts cannot be NULL"); + } val.count = count_data[idx]; + if (val.count == 0) { + throw InvalidInputException("Invalid approx_top_k state - the state values must have a count > 0"); + } + if (value_idx > 0 && val.count > target.values[value_idx - 1].get().count) { + throw InvalidInputException( + "Invalid approx_top_k state - the state values must be ordered by descending count"); + } } for (idx_t filter_idx = 0; filter_idx < filter_entries[i].length; filter_idx++) { - target.filter[filter_idx] = filter_data[filter_entries[i].offset + filter_idx]; + const auto filter_entry_idx = filter_entries[i].offset + filter_idx; + if (FlatVector::IsNull(ListVector::GetChild(filter_lists), filter_entry_idx)) { + throw InvalidInputException("Invalid approx_top_k state - the filter values cannot be NULL"); + } + target.filter[filter_idx] = filter_data[filter_entry_idx]; } target.Verify(); state.state = internal_state.release(); diff --git a/src/duckdb/extension/core_functions/aggregate/holistic/approximate_quantile.cpp b/src/duckdb/extension/core_functions/aggregate/holistic/approximate_quantile.cpp index 23bc8ec38..b545e937a 100644 --- a/src/duckdb/extension/core_functions/aggregate/holistic/approximate_quantile.cpp +++ b/src/duckdb/extension/core_functions/aggregate/holistic/approximate_quantile.cpp @@ -1,3 +1,4 @@ +#include "duckdb/common/operator/add.hpp" #include "duckdb/common/vector/flat_vector.hpp" #include "duckdb/common/vector/vector_iterator.hpp" #include "duckdb/common/vector/list_vector.hpp" @@ -38,6 +39,22 @@ struct ApproxQuantileCoding { } return false; } + + template + static void Decode(const SAVE_TYPE &source, TARGET_TYPE &target, const LogicalType &type) { + Decode(source, target); + if (type.id() != LogicalTypeId::DECIMAL) { + return; + } + // The unscaled decimal range can be narrower than its physical integer range. + const auto limit = Hugeint::POWERS_OF_TEN[DecimalType::GetWidth(type)] - 1; + const auto value = Cast::Operation(target); + if (value > limit) { + target = Cast::Operation(limit); + } else if (value < -limit) { + target = Cast::Operation(-limit); + } + } }; template <> @@ -64,6 +81,26 @@ bool ApproxQuantileCoding::Decode(const double &source, dtime_tz_t &target) { return decoded; } +template <> +double ApproxQuantileCoding::Encode(const timestamp_t &input) { + return Encode(input.value); +} + +template <> +bool ApproxQuantileCoding::Decode(const double &source, timestamp_t &target) { + int64_t value; + const auto decoded = Decode(source, value); + // the infinities are the largest and smallest timestamps - do not return anything beyond them + if (value >= timestamp_t::infinity().value) { + target = timestamp_t::infinity(); + } else if (value <= timestamp_t::ninfinity().value) { + target = timestamp_t::ninfinity(); + } else { + target = timestamp_t(value); + } + return decoded; +} + struct ApproximateQuantileBindData : public FunctionData { ApproximateQuantileBindData() { } @@ -119,7 +156,9 @@ struct ApproxQuantileOperation { return; } if (!state.h) { - state.h = new duckdb_tdigest::TDigest(unary_input.input.allocator, 100); + // the digest and its buffers live in the aggregate's arena, so an aborted query does not leak them + auto &allocator = unary_input.input.allocator; + state.h = allocator.Make(allocator, 100); } state.h->add(val); state.pos++; @@ -132,17 +171,13 @@ struct ApproxQuantileOperation { } D_ASSERT(source.h); if (!target.h) { - target.h = new duckdb_tdigest::TDigest(aggr_input_data.allocator, 100); + auto &allocator = aggr_input_data.allocator; + target.h = allocator.Make(allocator, 100); } - target.h->merge(source.h); - target.pos += source.pos; - } - - template - static void Destroy(STATE &state, AggregateInputData &aggr_input_data) { - if (state.h) { - delete state.h; + if (!TryAddOperator::Operation(target.pos, source.pos, target.pos)) { + throw OutOfRangeException("Overflow in aggregate state combine"); } + target.h->merge(source.h); } static bool IgnoreNull() { @@ -163,7 +198,7 @@ struct ApproxQuantileScalarOperation : public ApproxQuantileOperation { auto &bind_data = finalize_data.input.bind_data->template Cast(); D_ASSERT(bind_data.quantiles.size() == 1); const auto source = state.h->quantile(bind_data.quantiles[0]); - ApproxQuantileCoding::Decode(source, target); + ApproxQuantileCoding::Decode(source, target, finalize_data.result.GetType()); } }; @@ -274,19 +309,36 @@ void ApproxQuantileImportState(AggregateImportInputData &input) { } arena_vector centroids(input.allocator); centroids.reserve(centroid_list.GetListLength()); + double total_weight = 0; for (const auto centroid_entry : centroid_list.GetChildValues()) { const auto mean_entry = centroid_entry.template GetChildValue<0>(); const auto weight_entry = centroid_entry.template GetChildValue<1>(); if (!centroid_entry.IsValid() || !mean_entry.IsValid() || !weight_entry.IsValid()) { throw InvalidInputException("Invalid approx_quantile state - the centroids cannot be NULL"); } - centroids.emplace_back(mean_entry.GetValue(), weight_entry.GetValue()); + const auto mean = mean_entry.GetValue(); + const auto weight = weight_entry.GetValue(); + if (Value::IsNan(mean) || !Value::IsFinite(weight) || weight <= 0) { + throw InvalidInputException( + "Invalid approx_quantile state - centroids must have a mean and a positive finite weight"); + } + if (!centroids.empty() && mean < centroids.back().mean()) { + throw InvalidInputException("Invalid approx_quantile state - centroids must be sorted by their mean"); + } + total_weight += weight; + centroids.emplace_back(mean, weight); + } + if (!Value::IsFinite(total_weight)) { + throw InvalidInputException("Invalid approx_quantile state - the total centroid weight must be finite"); + } + if (Value::IsNan(min_entry.GetValue()) || Value::IsNan(max_entry.GetValue())) { + throw InvalidInputException("Invalid approx_quantile state - min and max cannot be NaN"); } - auto digest = make_uniq( + auto digest = input.allocator.Make( std::move(centroids), duckdb::arena_vector(input.allocator), 100, 0, 0); digest->setMinMax(min_entry.GetValue(), max_entry.GetValue()); state.pos = count_entry.GetValue(); - state.h = digest.release(); + state.h = digest; } } @@ -296,6 +348,10 @@ AggregateFunction GetApproximateQuantileAggregateFunction(const LogicalType &typ return AggregateFunction::UnaryAggregate(type, type); } + if (type.id() == LogicalTypeId::TIMESTAMP || type.id() == LogicalTypeId::TIMESTAMP_TZ) { + return AggregateFunction::UnaryAggregate(type, type); + } switch (type.InternalType()) { case PhysicalType::INT8: return AggregateFunction::UnaryAggregate( @@ -326,15 +382,12 @@ AggregateFunction GetApproximateQuantileAggregateFunction(const LogicalType &typ AggregateFunction GetApproximateQuantileDecimalAggregateFunction(const LogicalType &type) { switch (type.InternalType()) { case PhysicalType::INT8: - return GetApproximateQuantileAggregateFunction(LogicalType::TINYINT); case PhysicalType::INT16: - return GetApproximateQuantileAggregateFunction(LogicalType::SMALLINT); case PhysicalType::INT32: - return GetApproximateQuantileAggregateFunction(LogicalType::INTEGER); case PhysicalType::INT64: - return GetApproximateQuantileAggregateFunction(LogicalType::BIGINT); case PhysicalType::INT128: - return GetApproximateQuantileAggregateFunction(LogicalType::HUGEINT); + // keep the DECIMAL type as the input and return type so that the width and scale survive the bind + return GetApproximateQuantileAggregateFunction(type); default: throw InternalException("Unimplemented quantile decimal aggregate"); } @@ -345,6 +398,9 @@ float CheckApproxQuantile(const Value &quantile_val) { throw BinderException("APPROXIMATE QUANTILE parameter cannot be NULL"); } auto quantile = quantile_val.GetValue(); + if (Value::IsNan(quantile)) { + throw BinderException("APPROXIMATE QUANTILE parameter cannot be NaN"); + } if (quantile < 0 || quantile > 1) { throw BinderException("APPROXIMATE QUANTILE can only take parameters in range [0, 1]"); } @@ -445,7 +501,7 @@ struct ApproxQuantileListOperation : public ApproxQuantileOperation { const auto &quantile = bind_data.quantiles[q]; const auto &source = state.h->quantile(quantile); auto &target = rdata[ridx + q]; - ApproxQuantileCoding::Decode(source, target); + ApproxQuantileCoding::Decode(source, target, result.GetType()); } ListVector::SetListSize(finalize_data.result, entry.offset + entry.length); @@ -459,7 +515,7 @@ AggregateFunction ApproxQuantileListAggregate(const LogicalType &input_type, con {input_type}, result_type, AggregateFunction::StateSize, AggregateFunction::StateInitialize, AggregateFunction::UnaryScatterUpdate, AggregateFunction::StateCombine, AggregateFunction::StateFinalize, FunctionNullHandling::DEFAULT_NULL_HANDLING, - AggregateFunction::NoClusterUpdate(), AggregateFunction::NoBind(), AggregateFunction::StateDestroy); + AggregateFunction::NoClusterUpdate(), AggregateFunction::NoBind()); } template @@ -482,10 +538,11 @@ AggregateFunction GetApproxQuantileListAggregateFunction(const LogicalType &type case LogicalTypeId::DATE: return GetTypedApproxQuantileListAggregateFunction(type); case LogicalTypeId::BIGINT: - case LogicalTypeId::TIMESTAMP: - case LogicalTypeId::TIMESTAMP_TZ: case LogicalTypeId::TIME: return GetTypedApproxQuantileListAggregateFunction(type); + case LogicalTypeId::TIMESTAMP: + case LogicalTypeId::TIMESTAMP_TZ: + return GetTypedApproxQuantileListAggregateFunction(type); case LogicalTypeId::TIME_TZ: // Not binary comparable return GetTypedApproxQuantileListAggregateFunction(type); @@ -549,9 +606,9 @@ unique_ptr ApproxQuantileDecimalDeserialize(Deserializer &deserial auto bind_data = ApproximateQuantileBindData::Deserialize(deserializer, function); auto &return_type = deserializer.Get(); if (return_type.id() == LogicalTypeId::LIST) { - function.ReplaceImplementation(ApproxQuantileDecimalListFunction(function.GetArguments()[0])); + ReplaceApproxQuantileDecimal(function, ApproxQuantileDecimalListFunction(function.GetArguments()[0])); } else { - function.ReplaceImplementation(ApproxQuantileDecimalFunction(function.GetArguments()[0])); + ReplaceApproxQuantileDecimal(function, ApproxQuantileDecimalFunction(function.GetArguments()[0])); } return bind_data; } diff --git a/src/duckdb/extension/core_functions/aggregate/holistic/lttb.cpp b/src/duckdb/extension/core_functions/aggregate/holistic/lttb.cpp index d653532fd..535dfc0fc 100644 --- a/src/duckdb/extension/core_functions/aggregate/holistic/lttb.cpp +++ b/src/duckdb/extension/core_functions/aggregate/holistic/lttb.cpp @@ -207,7 +207,10 @@ auto LTTBFinalize(Vector &vec, AggregateFinalizeInputData &data, Vector &result, } // Reusable vector for materializing each groups points, sized up-front to the largest group. - Vector points(struct_type, MaxValue(max_v, 1)); + // A group can be the whole input, so the vector is allocated through the (tracked) base allocator. + DataChunk points_chunk; + points_chunk.Initialize(data.allocator.GetUnderlyingAllocator(), {struct_type}, MaxValue(max_v, 1)); + auto &points = points_chunk.data[0]; // Now do the selection for each list for (idx_t state_idx = 0; state_idx < count; state_idx++) { diff --git a/src/duckdb/extension/core_functions/aggregate/holistic/mad.cpp b/src/duckdb/extension/core_functions/aggregate/holistic/mad.cpp index cd9b96f41..51fd31c32 100644 --- a/src/duckdb/extension/core_functions/aggregate/holistic/mad.cpp +++ b/src/duckdb/extension/core_functions/aggregate/holistic/mad.cpp @@ -24,8 +24,20 @@ struct MadAccessor { } inline RESULT_TYPE operator()(const INPUT_TYPE &input) const { - const RESULT_TYPE delta = input - UnsafeNumericCast(median); - return TryAbsOperator::Operation(delta); + if constexpr (std::is_same::value) { + // values of opposite sign (e.g. DECIMAL(38)) can be further apart than the range of a hugeint - such + // deviations are larger than any other, so saturate them + RESULT_TYPE delta; + if (!TrySubtractOperator::Operation( + input, UnsafeNumericCast(median), delta) || + delta == NumericLimits::Minimum()) { + return NumericLimits::Maximum(); + } + return TryAbsOperator::Operation(delta); + } else { + const RESULT_TYPE delta = input - UnsafeNumericCast(median); + return TryAbsOperator::Operation(delta); + } } }; @@ -268,6 +280,7 @@ AggregateFunction GetTypedMedianAbsoluteDeviationAggregateFunction(const Logical fun.SetBindCallback(BindMAD); fun.SetStructStateExport(QuantileStateLayout); fun.SetOrderDependent(AggregateOrderDependent::NOT_ORDER_DEPENDENT); + fun.SetIsHolistic(true); #if !DUCKDB_SMALLER_BINARY(mad_window) fun.SetWindowBatchCallback(OP::template Window); fun.SetWindowInitCallback(OP::template WindowInit); @@ -326,6 +339,7 @@ unique_ptr BindMedianAbsoluteDeviationDecimal(BindAggregateFunctio function.ReplaceImplementation(impl); function.SetName("mad"); function.SetOrderDependent(AggregateOrderDependent::NOT_ORDER_DEPENDENT); + function.SetIsHolistic(true); return BindMAD(input); } diff --git a/src/duckdb/extension/core_functions/aggregate/holistic/mode.cpp b/src/duckdb/extension/core_functions/aggregate/holistic/mode.cpp index 13c36ecb2..68f27d6ad 100644 --- a/src/duckdb/extension/core_functions/aggregate/holistic/mode.cpp +++ b/src/duckdb/extension/core_functions/aggregate/holistic/mode.cpp @@ -46,12 +46,7 @@ static unique_ptr BindAggregate(ClientContext &context static unique_ptr BindScalar(ClientContext &context, const char *name, vector> children) { FunctionBinder function_binder(context); - ErrorData error; - auto result = function_binder.BindScalarFunction(Identifier::DefaultSchema(), name, std::move(children), error); - if (!result) { - error.Throw(); - } - return result; + return function_binder.BindScalarFunction(Identifier::DefaultSchema(), name, std::move(children)); } static unique_ptr BindScalar(ClientContext &context, const char *name, unique_ptr child) { @@ -127,7 +122,7 @@ struct ModeStandard { return Equals::Operation(left, right); } - static T *Update(T *mode, uint32_t &, const T &key, AggregateInputData &) { + static T *Update(T *mode, idx_t &, const T &key, AggregateInputData &) { if (!mode) { mode = new T(key); } @@ -156,7 +151,7 @@ struct ModeString { return Equals::Operation(left, right); } - static string_t *Update(string_t *mode, uint32_t &alloc_size, const string_t &key, + static string_t *Update(string_t *mode, idx_t &alloc_size, const string_t &key, AggregateInputData &aggr_input_data) { if (key.IsInlined()) { if (!mode) { @@ -168,14 +163,14 @@ struct ModeString { } // non-inlined string, need to allocate space for it somehow - const auto len = UnsafeNumericCast(key.GetSize()); + const auto len = key.GetSize(); char *ptr; if (mode && alloc_size >= len) { // this fits into the current arena allocation - reuse it ptr = mode->GetDataWriteable(); } else { // round up so repeatedly growing keys don't churn through a new arena allocation every time - alloc_size = UnsafeNumericCast(NextPowerOfTwo(len)); + alloc_size = NextPowerOfTwo(len); ptr = char_ptr_cast(aggr_input_data.allocator.Allocate(alloc_size)); if (!mode) { mode = new string_t(nullptr, 0); @@ -197,7 +192,8 @@ struct ModeState { SubFrames prevs; Counts *frequency_map = nullptr; KEY_TYPE *mode = nullptr; - uint32_t mode_alloc_size = 0; + //! Must be wide enough for NextPowerOfTwo(MAX_STRING_SIZE), which does not fit in a uint32_t + idx_t mode_alloc_size = 0; size_t nonzero = 0; bool valid = false; size_t count = 0; @@ -568,6 +564,7 @@ AggregateFunction GetFallbackModeFunction(const LogicalType &type) { AggregateFunction::StateVoidFinalize, FunctionNullHandling::DEFAULT_NULL_HANDLING, AggregateFunction::NoClusterUpdate()); aggr.SetStateDestructorCallback(AggregateFunction::StateDestroy); + aggr.SetIsHolistic(true); return aggr; } @@ -578,6 +575,7 @@ AggregateFunction GetTypedModeFunction(const LogicalType &type) { auto func = AggregateFunction::UnaryAggregate( type, type); func.SetWindowBatchCallback(OP::template Window); + func.SetIsHolistic(true); return func; } diff --git a/src/duckdb/extension/core_functions/aggregate/holistic/quantile.cpp b/src/duckdb/extension/core_functions/aggregate/holistic/quantile.cpp index f217c53e9..eca20a518 100644 --- a/src/duckdb/extension/core_functions/aggregate/holistic/quantile.cpp +++ b/src/duckdb/extension/core_functions/aggregate/holistic/quantile.cpp @@ -405,6 +405,10 @@ struct QuantileListFallback : QuantileOperation { //===--------------------------------------------------------------------===// template AggregateFunction GetDiscreteQuantileTemplated(const LogicalType &type) { + if (type.id() == LogicalTypeId::TIME_TZ) { + // TIMETZ is ordered by its normalized time, not by its raw bits + return OP::template GetFunction(type); + } switch (type.InternalType()) { #if !DUCKDB_SMALLER_BINARY(quantile_types) case PhysicalType::INT8: @@ -468,6 +472,7 @@ struct ListDiscreteQuantile { auto fun = QuantileBufferingAggregate(type, LogicalType::LIST(type)); fun.SetStructStateExport(QuantileStateLayout); fun.SetOrderDependent(AggregateOrderDependent::NOT_ORDER_DEPENDENT); + fun.SetIsHolistic(true); #if !DUCKDB_SMALLER_BINARY(quantile_window) fun.SetWindowBatchCallback(OP::template Window); fun.SetWindowInitCallback(OP::template WindowInit); @@ -549,8 +554,10 @@ AggregateFunction GetContinuousQuantileTemplated(const LogicalType &type) { case LogicalTypeId::TIMESTAMP_NS: return OP::template GetFunction(type, type); case LogicalTypeId::TIME: - case LogicalTypeId::TIME_TZ: return OP::template GetFunction(type, type); + case LogicalTypeId::TIME_TZ: + // TIMETZ is ordered and interpolated by its normalized time, not by its raw bits + return OP::template GetFunction(type, type); case LogicalTypeId::INTERVAL: return OP::template GetFunction(type, type); default: @@ -566,6 +573,7 @@ struct ScalarContinuousQuantile { auto fun = QuantileBufferingAggregate(input_type, target_type); fun.SetStructStateExport(QuantileStateLayout); fun.SetOrderDependent(AggregateOrderDependent::NOT_ORDER_DEPENDENT); + fun.SetIsHolistic(true); #if !DUCKDB_SMALLER_BINARY(quantile_window) fun.SetWindowBatchCallback(OP::template Window); fun.SetWindowInitCallback(OP::template WindowInit); @@ -582,6 +590,7 @@ struct ListContinuousQuantile { auto fun = QuantileBufferingAggregate(input_type, LogicalType::LIST(target_type)); fun.SetStructStateExport(QuantileStateLayout); fun.SetOrderDependent(AggregateOrderDependent::NOT_ORDER_DEPENDENT); + fun.SetIsHolistic(true); #if !DUCKDB_SMALLER_BINARY(quantile_window) fun.SetWindowBatchCallback(OP::template Window); fun.SetWindowInitCallback(OP::template WindowInit); @@ -719,6 +728,7 @@ struct DiscreteQuantileListFunction { // temporarily push an argument so we can bind the actual quantile fun.GetSignature().AddParameter("quantile", LogicalType::LIST(LogicalType::DOUBLE)); fun.SetOrderDependent(AggregateOrderDependent::NOT_ORDER_DEPENDENT); + fun.SetIsHolistic(true); return fun; } @@ -748,6 +758,7 @@ struct DiscreteQuantileFunction { // temporarily push an argument so we can bind the actual quantile fun.GetSignature().AddParameter("quantile", LogicalType::DOUBLE); fun.SetOrderDependent(AggregateOrderDependent::NOT_ORDER_DEPENDENT); + fun.SetIsHolistic(true); return fun; } @@ -782,6 +793,7 @@ struct ContinuousQuantileFunction { // temporarily push an argument so we can bind the actual quantile fun.GetSignature().AddParameter("quantile", LogicalType::DOUBLE); fun.SetOrderDependent(AggregateOrderDependent::NOT_ORDER_DEPENDENT); + fun.SetIsHolistic(true); return fun; } @@ -815,6 +827,7 @@ struct ContinuousQuantileListFunction { auto list_of_double = LogicalType::LIST(LogicalType::DOUBLE); fun.GetSignature().AddParameter("quantile", list_of_double); fun.SetOrderDependent(AggregateOrderDependent::NOT_ORDER_DEPENDENT); + fun.SetIsHolistic(true); return fun; } @@ -848,6 +861,7 @@ static AggregateFunction EmptyQuantileFunction(LogicalType input, const LogicalT fun.SetSerializeCallback(QuantileBindData::Serialize); fun.SetDeserializeCallback(OP::Deserialize); fun.SetOrderDependent(AggregateOrderDependent::NOT_ORDER_DEPENDENT); + fun.SetIsHolistic(true); return fun; } diff --git a/src/duckdb/extension/core_functions/aggregate/holistic/reservoir_quantile.cpp b/src/duckdb/extension/core_functions/aggregate/holistic/reservoir_quantile.cpp index fcaf523e7..5947345a8 100644 --- a/src/duckdb/extension/core_functions/aggregate/holistic/reservoir_quantile.cpp +++ b/src/duckdb/extension/core_functions/aggregate/holistic/reservoir_quantile.cpp @@ -17,6 +17,10 @@ namespace duckdb { namespace { +//! The quantile defaults to the median +constexpr double DEFAULT_QUANTILE = 0.5; +constexpr int32_t DEFAULT_SAMPLE_SIZE = 8192; + template struct ReservoirQuantileState { T *v; @@ -303,6 +307,9 @@ double CheckReservoirQuantile(const Value &quantile_val) { throw BinderException("RESERVOIR_QUANTILE QUANTILE parameter cannot be NULL"); } auto quantile = quantile_val.GetValue(); + if (Value::IsNan(quantile)) { + throw BinderException("RESERVOIR_QUANTILE parameter cannot be NaN"); + } if (quantile < 0 || quantile > 1) { throw BinderException("RESERVOIR_QUANTILE can only take parameters in the range [0, 1]"); } @@ -312,8 +319,6 @@ double CheckReservoirQuantile(const Value &quantile_val) { //! Binds the quantile parameter and the sample size into the bind data. They stay part of the expression tree, and //! the aggregate is handed them along with the input - the update callbacks only consume the leading input argument unique_ptr BindReservoirQuantile(BindAggregateFunctionInput &input) { - auto &arguments = input.GetArguments(); - D_ASSERT(arguments.size() >= 2); Value quantile_val = input.GetConstant(1); vector quantiles; if (quantile_val.type().id() != LogicalTypeId::LIST) { @@ -324,19 +329,13 @@ unique_ptr BindReservoirQuantile(BindAggregateFunctionInput &input } } - idx_t sample_size = 8192ULL; - - if (arguments.size() == 3) { - auto sample_size_val = input.GetNonNullConstant(2); - auto sample_size_int = sample_size_val.GetValue(); - - if (sample_size_val.IsNull() || sample_size_int <= 0) { - throw BinderException("Size of the RESERVOIR_QUANTILE sample must be bigger than 0"); - } - sample_size = NumericCast(sample_size_int); + auto sample_size_val = input.GetNonNullConstant(2); + auto sample_size = sample_size_val.GetValue(); + if (sample_size <= 0) { + throw BinderException("Size of the RESERVOIR_QUANTILE sample must be bigger than 0"); } - return make_uniq(quantiles, sample_size); + return make_uniq(quantiles, NumericCast(sample_size)); } static unique_ptr DeserializeReservoirQuantileDecimal(Deserializer &deserializer, @@ -421,7 +420,7 @@ AggregateFunction GetReservoirQuantileAggregate(PhysicalType type) { fun.SetDeserializeCallback(ReservoirQuantileBindData::Deserialize); // temporarily push an argument so we can bind the actual quantile fun.GetSignature().GetParameter(0).SetName("x"); - fun.GetSignature().AddParameter("quantile", LogicalType::DOUBLE); + fun.GetSignature().AddParameter("quantile", LogicalType::DOUBLE, Value::DOUBLE(DEFAULT_QUANTILE)); return fun; } @@ -438,18 +437,14 @@ AggregateFunction GetReservoirQuantileListAggregate(const LogicalType &type) { } void DefineReservoirQuantile(AggregateFunctionSet &set, const LogicalType &type) { - // Four versions: type, scalar/list[, count] + // Two versions: type, scalar/list auto fun = GetReservoirQuantileAggregate(type.InternalType()); + fun.GetSignature().AddParameter("sample_size", LogicalType::INTEGER, Value::INTEGER(DEFAULT_SAMPLE_SIZE)); set.AddFunction(fun); - fun.GetSignature().AddParameter("sample_size", LogicalType::INTEGER); - set.AddFunction(fun); - - // List variants + // List variant fun = GetReservoirQuantileListAggregate(type); - set.AddFunction(fun); - - fun.GetSignature().AddParameter("sample_size", LogicalType::INTEGER); + fun.GetSignature().AddParameter("sample_size", LogicalType::INTEGER, Value::INTEGER(DEFAULT_SAMPLE_SIZE)); set.AddFunction(fun); } @@ -462,11 +457,12 @@ void GetReservoirQuantileDecimalFunction(AggregateFunctionSet &set, const vector fun.SetDeserializeCallback(DeserializeReservoirQuantileDecimal); fun.GetSignature().GetParameter(0).SetName("x"); - fun.GetSignature().AddParameter("quantile", arguments[1]); - - set.AddFunction(fun); - - fun.GetSignature().AddParameter("sample_size", LogicalType::INTEGER); + if (arguments[1].id() == LogicalTypeId::LIST) { + fun.GetSignature().AddParameter("quantile", arguments[1]); + } else { + fun.GetSignature().AddParameter("quantile", arguments[1], Value::DOUBLE(DEFAULT_QUANTILE)); + } + fun.GetSignature().AddParameter("sample_size", LogicalType::INTEGER, Value::INTEGER(DEFAULT_SAMPLE_SIZE)); set.AddFunction(fun); } diff --git a/src/duckdb/extension/core_functions/aggregate/nested/histogram.cpp b/src/duckdb/extension/core_functions/aggregate/nested/histogram.cpp index ea6d07c80..889170860 100644 --- a/src/duckdb/extension/core_functions/aggregate/nested/histogram.cpp +++ b/src/duckdb/extension/core_functions/aggregate/nested/histogram.cpp @@ -38,12 +38,7 @@ static unique_ptr BindAggregate(ClientContext &context static unique_ptr BindScalar(ClientContext &context, const char *name, vector> children) { FunctionBinder function_binder(context); - ErrorData error; - auto result = function_binder.BindScalarFunction(Identifier::DefaultSchema(), name, std::move(children), error); - if (!result) { - error.Throw(); - } - return result; + return function_binder.BindScalarFunction(Identifier::DefaultSchema(), name, std::move(children)); } template diff --git a/src/duckdb/extension/core_functions/aggregate/variant/variant_group_object.cpp b/src/duckdb/extension/core_functions/aggregate/variant/variant_group_object.cpp index da6268ba7..64c724d33 100644 --- a/src/duckdb/extension/core_functions/aggregate/variant/variant_group_object.cpp +++ b/src/duckdb/extension/core_functions/aggregate/variant/variant_group_object.cpp @@ -81,7 +81,10 @@ class VariantObjSource { return true; } - Vector packed(GetInternalBufferType(), logical_count); + // a group can be the whole input, so the flattened pairs are allocated through the (tracked) base allocator + DataChunk packed_chunk; + packed_chunk.Initialize(allocator.GetUnderlyingAllocator(), {GetInternalBufferType()}, logical_count); + auto &packed = packed_chunk.data[0]; functions.BuildListVector(state.linked_list, packed, 0); FlatVector::SetSize(packed, logical_count); @@ -89,6 +92,13 @@ class VariantObjSource { const auto &keys = entries[0]; const auto &values = entries[1]; + // keys are verified to be non-NULL on update, but the state can also be imported + const auto key_validity = keys.Validity(); + for (idx_t i = 0; key_validity.CanHaveNull() && i < key_validity.size(); i++) { + if (!key_validity.IsValid(i)) { + throw InvalidInputException("variant_group_object key cannot be NULL"); + } + } // The VARIANT type does not support duplicate keys if (const auto duplicate = HasDuplicateKeys(keys)) { throw InvalidInputException("variant_group_object contains duplicate key \"%s\"", *duplicate); @@ -186,6 +196,8 @@ AggregateFunction VariantGroupObjectFun::GetFunction() { AggregateFunction::StateSize, AggregateFunction::StateInitialize, VariantObjUpdate, ListCombineFunction, VariantObjFinalize, VariantObjClusterUpdate); + function.GetSignature().GetParameter(0).SetName("key"); + function.GetSignature().GetParameter(1).SetName("value"); function.SetStructStateExport(VariantObjStateLayout); return function; } diff --git a/src/duckdb/extension/core_functions/core_functions_extension.cpp b/src/duckdb/extension/core_functions/core_functions_extension.cpp index fd7bad552..181229334 100644 --- a/src/duckdb/extension/core_functions/core_functions_extension.cpp +++ b/src/duckdb/extension/core_functions/core_functions_extension.cpp @@ -7,6 +7,7 @@ static void LoadInternal(ExtensionLoader &loader) { FunctionList::RegisterExtensionFunctions(loader, CoreFunctionList::GetFunctionList()); } +// LCOV_EXCL_START void CoreFunctionsExtension::Load(ExtensionLoader &loader) { LoadInternal(loader); } @@ -22,6 +23,7 @@ std::string CoreFunctionsExtension::Version() const { return ""; #endif } +// LCOV_EXCL_STOP } // namespace duckdb diff --git a/src/duckdb/extension/core_functions/include/core_functions/aggregate/algebraic/covar.hpp b/src/duckdb/extension/core_functions/include/core_functions/aggregate/algebraic/covar.hpp index 719736066..34dcb6f6d 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/aggregate/algebraic/covar.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/aggregate/algebraic/covar.hpp @@ -9,6 +9,7 @@ #pragma once +#include "duckdb/common/operator/add.hpp" #include "duckdb/function/aggregate_function.hpp" namespace duckdb { @@ -48,8 +49,10 @@ struct CovarOperation { if (target.count == 0) { target = source; } else if (source.count > 0) { - const auto count = target.count + source.count; - D_ASSERT(count >= target.count); // This is a check that we are not overflowing + uint64_t count; + if (!TryAddOperator::Operation(target.count, source.count, count)) { + throw OutOfRangeException("Overflow in aggregate state combine"); + } const auto target_count = static_cast(target.count); const auto source_count = static_cast(source.count); const auto total_count = static_cast(count); diff --git a/src/duckdb/extension/core_functions/include/core_functions/aggregate/algebraic/stddev.hpp b/src/duckdb/extension/core_functions/include/core_functions/aggregate/algebraic/stddev.hpp index 0150248c1..0068b906d 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/aggregate/algebraic/stddev.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/aggregate/algebraic/stddev.hpp @@ -8,6 +8,7 @@ #pragma once +#include "duckdb/common/operator/add.hpp" #include "duckdb/function/aggregate_function.hpp" #include #include @@ -57,8 +58,10 @@ struct STDDevBaseOperation { if (target.count == 0) { target = source; } else if (source.count > 0) { - const auto count = target.count + source.count; - D_ASSERT(count >= target.count); // This is a check that we are not overflowing + uint64_t count; + if (!TryAddOperator::Operation(target.count, source.count, count)) { + throw OutOfRangeException("Overflow in aggregate state combine"); + } const double target_count = static_cast(target.count); const double source_count = static_cast(source.count); const double total_count = static_cast(count); diff --git a/src/duckdb/extension/core_functions/include/core_functions/aggregate/distributive_functions.hpp b/src/duckdb/extension/core_functions/include/core_functions/aggregate/distributive_functions.hpp index 4add0a00d..b35c0efcb 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/aggregate/distributive_functions.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/aggregate/distributive_functions.hpp @@ -27,7 +27,7 @@ struct ApproxCountDistinctFun { struct ArgMinFun { static constexpr const char *Name = "arg_min"; - static constexpr const char *Parameters = "arg,val"; + static constexpr const char *Parameters = "arg,val,N"; static constexpr const char *Description = "Finds the row with the minimum val. Calculates the non-NULL arg expression at that row."; static constexpr const char *Example = "arg_min(A, B)"; static constexpr const char *Categories = ""; @@ -69,7 +69,7 @@ struct ArgMinNullsLastFun { struct ArgMaxFun { static constexpr const char *Name = "arg_max"; - static constexpr const char *Parameters = "arg,val"; + static constexpr const char *Parameters = "arg,val,N"; static constexpr const char *Description = "Finds the row with the maximum val. Calculates the non-NULL arg expression at that row."; static constexpr const char *Example = "arg_max(A, B)"; static constexpr const char *Categories = ""; @@ -141,7 +141,7 @@ struct BitXorFun { struct BitstringAggFun { static constexpr const char *Name = "bitstring_agg"; - static constexpr const char *Parameters = "arg"; + static constexpr const char *Parameters = "arg,min,max"; static constexpr const char *Description = "Returns a bitstring with bits set for each distinct value."; static constexpr const char *Example = "bitstring_agg(A)"; static constexpr const char *Categories = ""; diff --git a/src/duckdb/extension/core_functions/include/core_functions/aggregate/histogram_helpers.hpp b/src/duckdb/extension/core_functions/include/core_functions/aggregate/histogram_helpers.hpp index 690565cb7..e92640775 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/aggregate/histogram_helpers.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/aggregate/histogram_helpers.hpp @@ -31,10 +31,6 @@ struct HistogramFunctor { static T ExtractValue(UnifiedVectorFormat &bin_data, idx_t offset, AggregateInputData &) { return UnifiedVectorFormat::GetData(bin_data)[bin_data.sel->get_index(offset)]; } - - static bool RequiresExtract() { - return false; - } }; struct HistogramStringFunctorBase { @@ -54,10 +50,6 @@ struct HistogramStringFunctorBase { string_t histogram_str(char_ptr_cast(string_memory), input_str_size); return histogram_str; } - - static bool RequiresExtract() { - return true; - } }; struct HistogramStringFunctor : HistogramStringFunctorBase { diff --git a/src/duckdb/extension/core_functions/include/core_functions/aggregate/holistic_functions.hpp b/src/duckdb/extension/core_functions/include/core_functions/aggregate/holistic_functions.hpp index 44a487106..871f9c2b9 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/aggregate/holistic_functions.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/aggregate/holistic_functions.hpp @@ -57,7 +57,7 @@ struct ModeFun { struct QuantileDiscFun { static constexpr const char *Name = "quantile_disc"; - static constexpr const char *Parameters = "x,pos"; + static constexpr const char *Parameters = "x,quantile"; static constexpr const char *Description = "Returns the exact quantile number between 0 and 1 . If pos is a LIST of FLOATs, then the result is a LIST of the corresponding exact quantiles."; static constexpr const char *Example = "quantile_disc(x, 0.5)"; static constexpr const char *Categories = ""; @@ -73,7 +73,7 @@ struct QuantileFun { struct QuantileContFun { static constexpr const char *Name = "quantile_cont"; - static constexpr const char *Parameters = "x,pos"; + static constexpr const char *Parameters = "x,quantile"; static constexpr const char *Description = "Returns the interpolated quantile number between 0 and 1 . If pos is a LIST of FLOATs, then the result is a LIST of the corresponding interpolated quantiles. "; static constexpr const char *Example = "quantile_cont(x, 0.5)"; static constexpr const char *Categories = ""; diff --git a/src/duckdb/extension/core_functions/include/core_functions/aggregate/nested_functions.hpp b/src/duckdb/extension/core_functions/include/core_functions/aggregate/nested_functions.hpp index 84bd4ad6b..8863ac602 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/aggregate/nested_functions.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/aggregate/nested_functions.hpp @@ -17,7 +17,7 @@ namespace duckdb { struct HistogramFun { static constexpr const char *Name = "histogram"; - static constexpr const char *Parameters = "arg"; + static constexpr const char *Parameters = "arg,bins"; static constexpr const char *Description = "Returns a LIST of STRUCTs with the fields bucket and count."; static constexpr const char *Example = "histogram(A)"; static constexpr const char *Categories = ""; diff --git a/src/duckdb/extension/core_functions/include/core_functions/aggregate/quantile_sort_tree.hpp b/src/duckdb/extension/core_functions/include/core_functions/aggregate/quantile_sort_tree.hpp index 0e45ee807..1103aab7c 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/aggregate/quantile_sort_tree.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/aggregate/quantile_sort_tree.hpp @@ -18,6 +18,8 @@ #include "duckdb/planner/expression/bound_constant_expression.hpp" #include "duckdb/function/window/window_index_tree.hpp" #include +#include +#include #include #include @@ -239,11 +241,23 @@ struct QuantileInterpolator { floored = Cast::Operation((scaled_n - scaled_q) / scaling); break; } - default: + default: { + // The target is ceil(n * q) evaluated as if exactly, matching the DECIMAL path. + // A binary double for a decimal fraction is slightly off, so when n * q is mathematically an + // integer (n = 10, q = 0.9) the product lands a few ulps away from it and ceil() would + // pick the next element. Worse, the outcome of floor(n - n * q) depended on whether the + // compiler fused the multiply and subtract into an FMA, so different call sites of this + // function disagreed. Snap to the nearest integer when the product is within a few ulps + // of it; the result is then independent of contraction and of the rounding of q. const auto scaled_q = double(n) * q.dbl; - floored = LossyNumericCast(floor(double(n) - scaled_q)); + const auto nearest = std::round(scaled_q); + const auto tolerance = double(n) * 4 * std::numeric_limits::epsilon(); + const auto ceiled = (std::fabs(scaled_q - nearest) <= tolerance) ? nearest : std::ceil(scaled_q); + // 0 <= q <= 1, so 0 <= ceiled <= n + floored = n - LossyNumericCast(ceiled); break; } + } return MaxValue(1, n - floored) - 1; } diff --git a/src/duckdb/extension/core_functions/include/core_functions/aggregate/quantile_state.hpp b/src/duckdb/extension/core_functions/include/core_functions/aggregate/quantile_state.hpp index 50ef8a7b3..b2ffb3836 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/aggregate/quantile_state.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/aggregate/quantile_state.hpp @@ -42,7 +42,8 @@ struct FlattenedQuantileValues : FunctionLocalState { // (re-)allocate the cached chunk values.capacity = NextPowerOfTwo(required_capacity); values.chunk.Destroy(); - values.chunk.Initialize(Allocator::DefaultAllocator(), {type}, values.capacity); + // the flattened group can be the whole input, so it is allocated through the (tracked) base allocator + values.chunk.Initialize(finalize_data.input.allocator.GetUnderlyingAllocator(), {type}, values.capacity); } else { // re-use the allocated chunk - resetting clears the string heap of the previously flattened state values.chunk.Reset(); @@ -393,6 +394,8 @@ AggregateStateLayout QuantileStateLayout(AggregateLayoutInput &input) { } layout.total_state_size = AlignValue(sizeof(STATE)); layout.field = BuildStateField(); + // NULL inputs are never buffered, so the finalize assumes all values are valid + layout.field.reject_null_elements = true; AggregateStateField::PopulateListFunctions(layout.type, layout.field); if (function.GetArguments().size() == 2) { // the quantile parameter must be a constant at bind time (BindQuantile folds it into the bind data) - diff --git a/src/duckdb/extension/core_functions/include/core_functions/aggregate/regression/regr_count.hpp b/src/duckdb/extension/core_functions/include/core_functions/aggregate/regression/regr_count.hpp index ac9836499..dbe33743d 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/aggregate/regression/regr_count.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/aggregate/regression/regr_count.hpp @@ -9,6 +9,7 @@ #pragma once +#include "duckdb/common/operator/add.hpp" #include "duckdb/function/aggregate_function.hpp" #include "core_functions/aggregate/algebraic/covar.hpp" #include "core_functions/aggregate/algebraic/stddev.hpp" @@ -18,11 +19,16 @@ namespace duckdb { struct RegrCountFunction { template static void Combine(const STATE &source, STATE &target, AggregateInputData &) { - target += source; + if (!TryAddOperator::Operation(target, source, target)) { + throw OutOfRangeException("Overflow in aggregate state combine"); + } } template static void Finalize(STATE &state, T &target, AggregateFinalizeData &finalize_data) { + if (state > NumericLimits::Maximum()) { + throw OutOfRangeException("regr_count result %llu does not fit in the result type", state); + } target = static_cast(state); } static bool IgnoreNull() { diff --git a/src/duckdb/extension/core_functions/include/core_functions/aggregate/sum_helpers.hpp b/src/duckdb/extension/core_functions/include/core_functions/aggregate/sum_helpers.hpp index d6409dd9c..7c35853ae 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/aggregate/sum_helpers.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/aggregate/sum_helpers.hpp @@ -100,7 +100,7 @@ struct KahanSumState { void Combine(const KahanSumState &other) { this->is_set = other.is_set || this->is_set; KahanAddInternal(other.value, this->value, this->err); - KahanAddInternal(other.err, this->value, this->err); + KahanAddInternal(-other.err, this->value, this->err); } }; diff --git a/src/duckdb/extension/core_functions/include/core_functions/array_kernels.hpp b/src/duckdb/extension/core_functions/include/core_functions/array_kernels.hpp index ddcdb92b6..3d22d6216 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/array_kernels.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/array_kernels.hpp @@ -57,9 +57,50 @@ struct CosineSimilarityOp { norm_r += y * y; } - auto similarity = distance / std::sqrt(norm_l * norm_r); + const auto norm_product = norm_l * norm_r; + if (!std::isfinite(distance) || !std::isfinite(norm_product) || + norm_product < std::numeric_limits::min()) { + // the computation overflowed or underflowed (or there are NaN / infinite values) + return ScaledSimilarity(lhs_data, rhs_data, count); + } + return Clamp(distance / std::sqrt(norm_product)); + } + +private: + template + static TYPE Clamp(TYPE similarity) { + if (std::isnan(similarity)) { + return similarity; + } return std::max(static_cast(-1.0), std::min(similarity, static_cast(1.0))); } + + //! Computes the similarity with both vectors scaled by their largest absolute value, which does not change the + //! similarity, but avoids overflow and underflow + template + static TYPE ScaledSimilarity(const TYPE *lhs_data, const TYPE *rhs_data, const idx_t count) { + TYPE max_l = 0; + TYPE max_r = 0; + for (idx_t i = 0; i < count; i++) { + max_l = std::max(max_l, std::abs(lhs_data[i])); + max_r = std::max(max_r, std::abs(rhs_data[i])); + } + if (max_l == 0 || max_r == 0 || !std::isfinite(max_l) || !std::isfinite(max_r)) { + // the similarity is undefined for zero and infinite vectors + return std::numeric_limits::quiet_NaN(); + } + TYPE distance = 0; + TYPE norm_l = 0; + TYPE norm_r = 0; + for (idx_t i = 0; i < count; i++) { + const auto x = lhs_data[i] / max_l; + const auto y = rhs_data[i] / max_r; + distance += x * y; + norm_l += x * x; + norm_r += y * y; + } + return Clamp(distance / std::sqrt(norm_l * norm_r)); + } }; struct CosineDistanceOp { diff --git a/src/duckdb/extension/core_functions/include/core_functions/scalar/date_functions.hpp b/src/duckdb/extension/core_functions/include/core_functions/scalar/date_functions.hpp index e0d4c5fda..b58842cd4 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/scalar/date_functions.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/scalar/date_functions.hpp @@ -17,7 +17,7 @@ namespace duckdb { struct AgeFun { static constexpr const char *Name = "age"; - static constexpr const char *Parameters = "timestamp,timestamp"; + static constexpr const char *Parameters = "timestamp1,timestamp2"; static constexpr const char *Description = "Subtract arguments, resulting in the time difference between the two timestamps"; static constexpr const char *Example = "age(TIMESTAMP '2001-04-10', TIMESTAMP '1992-09-20')"; static constexpr const char *Categories = ""; @@ -53,7 +53,7 @@ struct DatediffFun { struct DatePartFun { static constexpr const char *Name = "date_part"; - static constexpr const char *Parameters = "ts"; + static constexpr const char *Parameters = "part,ts"; static constexpr const char *Description = "Get subfield (equivalent to extract)"; static constexpr const char *Example = "date_part('minute', TIMESTAMP '1992-09-20 20:38:40')"; static constexpr const char *Categories = ""; diff --git a/src/duckdb/extension/core_functions/include/core_functions/scalar/debug_functions.hpp b/src/duckdb/extension/core_functions/include/core_functions/scalar/debug_functions.hpp index ab6b7cb9d..b5ab6002d 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/scalar/debug_functions.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/scalar/debug_functions.hpp @@ -37,7 +37,7 @@ struct SleepMsFun { struct IndexKeyFun { static constexpr const char *Name = "index_key"; - static constexpr const char *Parameters = "path,name,[key_column_1,key_column_2,...]"; + static constexpr const char *Parameters = "path,name,..."; static constexpr const char *Description = "Generate index keys for a named index on a table (specified by a path struct with required 'table' and optional 'catalog'/'schema' fields) from key column arguments. Currently only supports ART indexes."; static constexpr const char *Example = "index_key({'schema': 'main', 'table': 'tbl3'}, 'tbl3_idx', key1, key2, key3)"; static constexpr const char *Categories = ""; diff --git a/src/duckdb/extension/core_functions/include/core_functions/scalar/generic_functions.hpp b/src/duckdb/extension/core_functions/include/core_functions/scalar/generic_functions.hpp index f8642e689..bab3b7f07 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/scalar/generic_functions.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/scalar/generic_functions.hpp @@ -47,7 +47,7 @@ struct HashFun { struct LeastFun { static constexpr const char *Name = "least"; - static constexpr const char *Parameters = "arg1,arg2,..."; + static constexpr const char *Parameters = "arg1,..."; static constexpr const char *Description = "Returns the smallest value. For strings lexicographical ordering is used. Note that uppercase characters are considered “smaller” than lowercase characters, and collations are not supported."; static constexpr const char *Example = "least(42, 84)\002least('abc', 'bcd', 'cde', 'EFG')"; static constexpr const char *Categories = "string,numeric,date,timestamp,aggregate"; @@ -57,7 +57,7 @@ struct LeastFun { struct GreatestFun { static constexpr const char *Name = "greatest"; - static constexpr const char *Parameters = "arg1,arg2,..."; + static constexpr const char *Parameters = "arg1,..."; static constexpr const char *Description = "Returns the largest value. For strings lexicographical ordering is used. Note that lowercase characters are considered “larger” than uppercase characters and collations are not supported."; static constexpr const char *Example = "greatest(42, 84)\002greatest('abc', 'bcd', 'cde', 'EFG')"; static constexpr const char *Categories = "string,numeric,date,timestamp,aggregate"; diff --git a/src/duckdb/extension/core_functions/include/core_functions/scalar/list_functions.hpp b/src/duckdb/extension/core_functions/include/core_functions/scalar/list_functions.hpp index 96ffd39c2..c06f4cf94 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/scalar/list_functions.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/scalar/list_functions.hpp @@ -93,7 +93,7 @@ struct ArrayUniqueFun { struct ListValueFun { static constexpr const char *Name = "list_value"; - static constexpr const char *Parameters = "any,..."; + static constexpr const char *Parameters = "value,..."; static constexpr const char *Description = "Creates a LIST containing the argument values."; static constexpr const char *Example = "list_value(4, 5, 6)"; static constexpr const char *Categories = "list"; @@ -125,7 +125,7 @@ struct ArraySliceFun { struct ListSortFun { static constexpr const char *Name = "list_sort"; - static constexpr const char *Parameters = "list"; + static constexpr const char *Parameters = "list,sort_order,null_order"; static constexpr const char *Description = "Sorts the elements of the list."; static constexpr const char *Example = "list_sort([3, 6, 1, 2])"; static constexpr const char *Categories = "list"; @@ -141,7 +141,7 @@ struct ArraySortFun { struct ListGradeUpFun { static constexpr const char *Name = "list_grade_up"; - static constexpr const char *Parameters = "list"; + static constexpr const char *Parameters = "list,sort_order,null_order"; static constexpr const char *Description = "Works like list_sort, but the results are the indexes that correspond to the position in the original list instead of the actual values."; static constexpr const char *Example = "list_grade_up([3, 6, 1, 2])"; static constexpr const char *Categories = "list"; @@ -163,7 +163,7 @@ struct GradeUpFun { struct ListReverseSortFun { static constexpr const char *Name = "list_reverse_sort"; - static constexpr const char *Parameters = "list"; + static constexpr const char *Parameters = "list,null_order"; static constexpr const char *Description = "Sorts the elements of the list in reverse order."; static constexpr const char *Example = "list_reverse_sort([3, 6, 1, 2])"; static constexpr const char *Categories = "list"; @@ -179,7 +179,7 @@ struct ArrayReverseSortFun { struct ListTransformFun { static constexpr const char *Name = "list_transform"; - static constexpr const char *Parameters = "list,lambda(x)"; + static constexpr const char *Parameters = "list,lambda"; static constexpr const char *Description = "Returns a list that is the result of applying the `lambda` function to each element of the input `list`. The return type is defined by the return type of the `lambda` function."; static constexpr const char *Example = "list_transform([1, 2, 3], lambda x : x + 1)"; static constexpr const char *Categories = "list,lambda"; @@ -213,7 +213,7 @@ struct ApplyFun { struct ListFilterFun { static constexpr const char *Name = "list_filter"; - static constexpr const char *Parameters = "list,lambda(x)"; + static constexpr const char *Parameters = "list,lambda"; static constexpr const char *Description = "Constructs a list from those elements of the input `list` for which the `lambda` function returns `true`. DuckDB must be able to cast the `lambda` function's return type to `BOOL`. The return type of `list_filter` is the same as the input list's."; static constexpr const char *Example = "list_filter([3, 4, 5], lambda x : x > 4)"; static constexpr const char *Categories = "list,lambda"; @@ -235,7 +235,7 @@ struct FilterFun { struct ListReduceFun { static constexpr const char *Name = "list_reduce"; - static constexpr const char *Parameters = "list,lambda(x,y),initial_value"; + static constexpr const char *Parameters = "list,lambda,initial_value"; static constexpr const char *Description = "Reduces all elements of the input `list` into a single scalar value by executing the `lambda` function on a running result and the next list element. The `lambda` function has an optional `initial_value` argument."; static constexpr const char *Example = "list_reduce([1, 2, 3], lambda x, y : x + y)"; static constexpr const char *Categories = "list,lambda"; diff --git a/src/duckdb/extension/core_functions/include/core_functions/scalar/math_functions.hpp b/src/duckdb/extension/core_functions/include/core_functions/scalar/math_functions.hpp index ee55fa553..ade64790f 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/scalar/math_functions.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/scalar/math_functions.hpp @@ -451,7 +451,7 @@ struct TanFun { struct TruncFun { static constexpr const char *Name = "trunc"; - static constexpr const char *Parameters = "x"; + static constexpr const char *Parameters = "x,precision"; static constexpr const char *Description = "Truncates the number"; static constexpr const char *Example = "trunc(17.4)"; static constexpr const char *Categories = ""; diff --git a/src/duckdb/extension/core_functions/include/core_functions/scalar/operators_functions.hpp b/src/duckdb/extension/core_functions/include/core_functions/scalar/operators_functions.hpp index a92258f0a..2764eecad 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/scalar/operators_functions.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/scalar/operators_functions.hpp @@ -47,7 +47,7 @@ struct BitwiseNotFun { struct LeftShiftFun { static constexpr const char *Name = "<<"; - static constexpr const char *Parameters = "input"; + static constexpr const char *Parameters = "input,shift"; static constexpr const char *Description = "Bitwise shift left"; static constexpr const char *Example = "1 << 4"; static constexpr const char *Categories = ""; @@ -57,7 +57,7 @@ struct LeftShiftFun { struct RightShiftFun { static constexpr const char *Name = ">>"; - static constexpr const char *Parameters = "input"; + static constexpr const char *Parameters = "input,shift"; static constexpr const char *Description = "Bitwise shift right"; static constexpr const char *Example = "8 >> 2"; static constexpr const char *Categories = ""; diff --git a/src/duckdb/extension/core_functions/include/core_functions/scalar/string_functions.hpp b/src/duckdb/extension/core_functions/include/core_functions/scalar/string_functions.hpp index 81b33c693..63ccd516b 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/scalar/string_functions.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/scalar/string_functions.hpp @@ -89,7 +89,7 @@ struct DamerauLevenshteinFun { struct FormatFun { static constexpr const char *Name = "format"; - static constexpr const char *Parameters = "format,parameters..."; + static constexpr const char *Parameters = "format,..."; static constexpr const char *Description = "Formats a string using the fmt syntax."; static constexpr const char *Example = "format('Benchmark \"{}\" took {} seconds', 'CSV', 42)"; static constexpr const char *Categories = "string"; @@ -315,7 +315,7 @@ struct ParsePathFun { struct PrintfFun { static constexpr const char *Name = "printf"; - static constexpr const char *Parameters = "format,parameters..."; + static constexpr const char *Parameters = "format,..."; static constexpr const char *Description = "Formats a `string` using printf syntax."; static constexpr const char *Example = "printf('Benchmark \"%s\" took %d seconds', 'CSV', 42)"; static constexpr const char *Categories = "string"; diff --git a/src/duckdb/extension/core_functions/scalar/array/array_functions.cpp b/src/duckdb/extension/core_functions/scalar/array/array_functions.cpp index d36d30082..e794f2a29 100644 --- a/src/duckdb/extension/core_functions/scalar/array/array_functions.cpp +++ b/src/duckdb/extension/core_functions/scalar/array/array_functions.cpp @@ -6,7 +6,7 @@ namespace duckdb { -static unique_ptr ArrayGenericBinaryBind(BindScalarFunctionInput &input) { +static void ArrayGenericBinaryResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &context = input.GetClientContext(); auto &bound_function = input.GetBoundFunction(); auto &arguments = input.GetArguments(); @@ -17,7 +17,7 @@ static unique_ptr ArrayGenericBinaryBind(BindScalarFunctionInput & bound_function.GetArguments()[0] = rhs_type; bound_function.GetArguments()[1] = lhs_type; bound_function.SetReturnType(LogicalType::UNKNOWN); - return nullptr; + return; } bound_function.GetArguments()[0] = lhs_type.IsUnknown() ? rhs_type : lhs_type; @@ -56,8 +56,20 @@ static unique_ptr ArrayGenericBinaryBind(BindScalarFunctionInput & // The important part is just that we resolve the size of the input arrays bound_function.GetArguments()[0] = LogicalType::ARRAY(common_type, lhs_size); bound_function.GetArguments()[1] = LogicalType::ARRAY(common_type, rhs_size); +} - return nullptr; +//! Read the elements of an array into a contiguous buffer - throws if any of them are NULL +template +static void ReadArrayElements(const VectorIterator &child_values, idx_t offset, idx_t array_size, TYPE *target, + const char *side, const Identifier &func_name) { + for (idx_t i = 0; i < array_size; i++) { + auto entry = child_values[offset + i]; + if (!entry.IsValid()) { + throw InvalidInputException( + StringUtil::Format("%s: %s argument can not contain NULL values", SQLIdentifier(func_name), side)); + } + target[i] = entry.GetValue(); + } } //------------------------------------------------------------------------------ @@ -93,22 +105,19 @@ static void ArrayFixedCombine(DataChunk &args, ExpressionState &state, Vector &r const auto &func_name = expr.Function().GetName(); const auto count = args.size(); - auto &lhs_child = ArrayVector::GetChildMutable(args.data[0]); - auto &rhs_child = ArrayVector::GetChildMutable(args.data[1]); + auto lhs_values = ArrayVector::GetChild(args.data[0]).Values(); + auto rhs_values = ArrayVector::GetChild(args.data[1]).Values(); auto &res_child = ArrayVector::GetChildMutable(result); - const auto &lhs_child_validity = FlatVector::Validity(lhs_child); - const auto &rhs_child_validity = FlatVector::Validity(rhs_child); - UnifiedVectorFormat lhs_format; UnifiedVectorFormat rhs_format; args.data[0].ToUnifiedFormat(lhs_format); args.data[1].ToUnifiedFormat(rhs_format); - auto lhs_data = FlatVector::GetData(lhs_child); - auto rhs_data = FlatVector::GetData(rhs_child); auto res_data = FlatVector::GetDataMutable(res_child); + TYPE lhs_data[N]; + TYPE rhs_data[N]; for (idx_t i = 0; i < count; i++) { const auto lhs_idx = lhs_format.sel->get_index(i); @@ -119,24 +128,9 @@ static void ArrayFixedCombine(DataChunk &args, ExpressionState &state, Vector &r continue; } - const auto left_offset = lhs_idx * N; - if (!lhs_child_validity.CheckAllValid(left_offset + N, left_offset)) { - throw InvalidInputException( - StringUtil::Format("%s: left argument can not contain NULL values", SQLIdentifier(func_name))); - } - - const auto right_offset = rhs_idx * N; - if (!rhs_child_validity.CheckAllValid(right_offset + N, right_offset)) { - throw InvalidInputException( - StringUtil::Format("%s: right argument can not contain NULL values", SQLIdentifier(func_name))); - } - const auto result_offset = i * N; - - const auto lhs_data_ptr = lhs_data + left_offset; - const auto rhs_data_ptr = rhs_data + right_offset; - const auto res_data_ptr = res_data + result_offset; - - OP::Operation(lhs_data_ptr, rhs_data_ptr, res_data_ptr, N); + ReadArrayElements(lhs_values, lhs_idx * N, N, lhs_data, "left", func_name); + ReadArrayElements(rhs_values, rhs_idx * N, N, rhs_data, "right", func_name); + OP::Operation(lhs_data, rhs_data, res_data + i * N, N); } if (count == 1) { @@ -156,11 +150,8 @@ static void ArrayGenericFold(DataChunk &args, ExpressionState &state, Vector &re const auto &func_name = expr.Function().GetName(); const auto count = args.size(); - auto &lhs_child = ArrayVector::GetChildMutable(args.data[0]); - auto &rhs_child = ArrayVector::GetChildMutable(args.data[1]); - - const auto &lhs_child_validity = FlatVector::Validity(lhs_child); - const auto &rhs_child_validity = FlatVector::Validity(rhs_child); + auto lhs_values = ArrayVector::GetChild(args.data[0]).Values(); + auto rhs_values = ArrayVector::GetChild(args.data[1]).Values(); UnifiedVectorFormat lhs_format; UnifiedVectorFormat rhs_format; @@ -168,12 +159,12 @@ static void ArrayGenericFold(DataChunk &args, ExpressionState &state, Vector &re args.data[0].ToUnifiedFormat(lhs_format); args.data[1].ToUnifiedFormat(rhs_format); - auto lhs_data = FlatVector::GetData(lhs_child); - auto rhs_data = FlatVector::GetData(rhs_child); auto res_data = FlatVector::GetDataMutable(result); const auto array_size = ArrayType::GetSize(args.data[0].GetType()); D_ASSERT(array_size == ArrayType::GetSize(args.data[1].GetType())); + vector lhs_data(array_size); + vector rhs_data(array_size); for (idx_t i = 0; i < count; i++) { const auto lhs_idx = lhs_format.sel->get_index(i); @@ -184,22 +175,9 @@ static void ArrayGenericFold(DataChunk &args, ExpressionState &state, Vector &re continue; } - const auto left_offset = lhs_idx * array_size; - if (!lhs_child_validity.CheckAllValid(left_offset + array_size, left_offset)) { - throw InvalidInputException( - StringUtil::Format("%s: left argument can not contain NULL values", SQLIdentifier(func_name))); - } - - const auto right_offset = rhs_idx * array_size; - if (!rhs_child_validity.CheckAllValid(right_offset + array_size, right_offset)) { - throw InvalidInputException( - StringUtil::Format("%s: right argument can not contain NULL values", SQLIdentifier(func_name))); - } - - const auto lhs_data_ptr = lhs_data + left_offset; - const auto rhs_data_ptr = rhs_data + right_offset; - - res_data[i] = OP::Operation(lhs_data_ptr, rhs_data_ptr, array_size); + ReadArrayElements(lhs_values, lhs_idx * array_size, array_size, lhs_data.data(), "left", func_name); + ReadArrayElements(rhs_values, rhs_idx * array_size, array_size, rhs_data.data(), "right", func_name); + res_data[i] = OP::Operation(lhs_data.data(), rhs_data.data(), array_size); } if (count == 1) { @@ -255,7 +233,8 @@ static scalar_function_t GetArrayFoldFunction(const LogicalType &type) { template static void AddArrayFoldFunction(ScalarFunctionSet &set, const LogicalType &type) { - ScalarFunction func({}, type, GetArrayFoldFunction(type), ArrayGenericBinaryBind, ArrayGenericFoldStats); + ScalarFunction func({}, type, GetArrayFoldFunction(type), nullptr, ArrayGenericFoldStats); + func.SetResolveTypesCallback(ArrayGenericBinaryResolveTypes); auto array = LogicalType::ARRAY(type, optional_idx()); func.SetFallible(); diff --git a/src/duckdb/extension/core_functions/scalar/array/array_value.cpp b/src/duckdb/extension/core_functions/scalar/array/array_value.cpp index 47b682ba2..08e1a0abf 100644 --- a/src/duckdb/extension/core_functions/scalar/array/array_value.cpp +++ b/src/duckdb/extension/core_functions/scalar/array/array_value.cpp @@ -45,7 +45,7 @@ void ArrayValueFunction(DataChunk &args, ExpressionState &state, Vector &result) result.Verify(); } -unique_ptr ArrayValueBind(BindScalarFunctionInput &input) { +void ArrayValueResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &context = input.GetClientContext(); auto &bound_function = input.GetBoundFunction(); auto &arguments = input.GetArguments(); @@ -72,7 +72,6 @@ unique_ptr ArrayValueBind(BindScalarFunctionInput &input) { } bound_function.SetReturnType(LogicalType::ARRAY(child_type, arguments.size())); - return make_uniq(bound_function.GetReturnType()); } unique_ptr ArrayValueStats(ClientContext &context, FunctionStatisticsInput &input) { @@ -91,9 +90,13 @@ unique_ptr ArrayValueStats(ClientContext &context, FunctionStati ScalarFunction ArrayValueFun::GetFunction() { // the arguments and return types are actually set in the binder function - ScalarFunction fun("array_value", {}, LogicalTypeId::ARRAY, ArrayValueFunction, ArrayValueBind, ArrayValueStats); - fun.SetVarArgs(LogicalType::ANY); + ScalarFunction fun("array_value", {}, LogicalTypeId::ARRAY, ArrayValueFunction, VariableReturnBindData::Bind, + ArrayValueStats); + fun.SetResolveTypesCallback(ArrayValueResolveTypes); + fun.GetSignature().AddArgs("args", LogicalType::ANY); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); + // casting the arguments to the common child type can fail, e.g. for arrays of different sizes + fun.SetFallible(); return fun; } diff --git a/src/duckdb/extension/core_functions/scalar/bit/bitstring.cpp b/src/duckdb/extension/core_functions/scalar/bit/bitstring.cpp index a74e5568b..2a3ece121 100644 --- a/src/duckdb/extension/core_functions/scalar/bit/bitstring.cpp +++ b/src/duckdb/extension/core_functions/scalar/bit/bitstring.cpp @@ -91,7 +91,7 @@ namespace { struct GetBitOperator { template static inline TR Operation(TA input, TB n) { - if (n < 0 || (idx_t)n > Bit::BitLength(input) - 1) { + if (n < 0 || UnsafeNumericCast(n) >= Bit::BitLength(input)) { throw OutOfRangeException("bit index %s out of valid range (0..%s)", NumericHelper::ToString(n), NumericHelper::ToString(Bit::BitLength(input) - 1)); } @@ -117,7 +117,7 @@ static void SetBitOperation(DataChunk &args, ExpressionState &state, Vector &res if (new_value != 0 && new_value != 1) { throw InvalidInputException("The new bit must be 1 or 0"); } - if (n < 0 || (idx_t)n > Bit::BitLength(input) - 1) { + if (n < 0 || UnsafeNumericCast(n) >= Bit::BitLength(input)) { throw OutOfRangeException("bit index %s out of valid range (0..%s)", NumericHelper::ToString(n), NumericHelper::ToString(Bit::BitLength(input) - 1)); } diff --git a/src/duckdb/extension/core_functions/scalar/blob/encode.cpp b/src/duckdb/extension/core_functions/scalar/blob/encode.cpp index 05eba11df..9eb915131 100644 --- a/src/duckdb/extension/core_functions/scalar/blob/encode.cpp +++ b/src/duckdb/extension/core_functions/scalar/blob/encode.cpp @@ -82,7 +82,7 @@ void BinaryDecodeFunction(DataChunk &args, ExpressionState &state, Vector &resul auto new_str = Utf8Proc::RemoveInvalid(input_data, input_length); auto target = StringVector::EmptyString(result, new_str.size()); auto output = target.GetDataWriteable(); - memcpy(output, new_str.data(), new_str.size()); + memcpy(output, new_str.data(), new_str.size()); // NOLINT(bugprone-not-null-terminated-result) target.Finalize(); return target; } diff --git a/src/duckdb/extension/core_functions/scalar/date/date_part.cpp b/src/duckdb/extension/core_functions/scalar/date/date_part.cpp index de8e5aa30..a0117e273 100644 --- a/src/duckdb/extension/core_functions/scalar/date/date_part.cpp +++ b/src/duckdb/extension/core_functions/scalar/date/date_part.cpp @@ -1,3 +1,4 @@ +#include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/common/vector/struct_vector.hpp" #include "core_functions/scalar/date_functions.hpp" #include "duckdb/common/case_insensitive_map.hpp" @@ -650,11 +651,14 @@ struct DatePart { template static TR Operation(TA interval, TB timetz) { + auto offset = interval.micros / Interval::MICROS_PER_SEC; + if (offset < dtime_tz_t::MIN_OFFSET || offset > dtime_tz_t::MAX_OFFSET) { + throw OutOfRangeException("Timezone offset out of range: %s", Interval::ToString(interval)); + } auto time = Time::NormalizeTimeTZ(timetz); date_t date(0); time = Interval::Add(time, interval, date); - auto offset = UnsafeNumericCast(interval.micros / Interval::MICROS_PER_SEC); - return TR(time, offset); + return TR(time, UnsafeNumericCast(offset)); } template @@ -2455,11 +2459,20 @@ ScalarFunctionSet GetCachedDatepartFunction() { OP::template PropagateStatistics); } +void SetNonDecreasingExceptInterval(ScalarFunctionSet &functions) { + functions.ApplyToFunctions([](ScalarFunction &function) { + // Interval components need not preserve the normalized interval ordering. + if (function.GetSignature().GetParameter(0).GetType() != LogicalType::INTERVAL) { + function.SetUnaryArgProperties(ArgProperties().NonDecreasing()); + } + }); +} + } // namespace ScalarFunctionSet YearFun::GetFunctions() { auto set = GetCachedDatepartFunction(); - set.SetUnaryArgProperties(ArgProperties().NonDecreasing()); + SetNonDecreasingExceptInterval(set); return set; } @@ -2473,19 +2486,19 @@ ScalarFunctionSet DayFun::GetFunctions() { ScalarFunctionSet DecadeFun::GetFunctions() { auto set = GetDatePartFunction(); - set.SetUnaryArgProperties(ArgProperties().NonDecreasing()); + SetNonDecreasingExceptInterval(set); return set; } ScalarFunctionSet CenturyFun::GetFunctions() { auto set = GetDatePartFunction(); - set.SetUnaryArgProperties(ArgProperties().NonDecreasing()); + SetNonDecreasingExceptInterval(set); return set; } ScalarFunctionSet MillenniumFun::GetFunctions() { auto set = GetDatePartFunction(); - set.SetUnaryArgProperties(ArgProperties().NonDecreasing()); + SetNonDecreasingExceptInterval(set); return set; } @@ -2548,7 +2561,7 @@ ScalarFunctionSet TimezoneMinuteFun::GetFunctions() { ScalarFunctionSet EpochFun::GetFunctions() { auto set = GetTimePartFunction(LogicalType::DOUBLE, "temporal"); - set.SetUnaryArgProperties(ArgProperties().NonDecreasing()); + SetNonDecreasingExceptInterval(set); return set; } @@ -2607,7 +2620,7 @@ ScalarFunctionSet EpochMsFun::GetFunctions() { inverse_fun.GetSignature().AddParameter("temporal", LogicalType::BIGINT); operator_set.AddFunction(inverse_fun); - operator_set.SetUnaryArgProperties(ArgProperties().NonDecreasing()); + SetNonDecreasingExceptInterval(operator_set); // these overflow at the representable extremes, so the failure must be reportable operator_set.SetFallible(); return operator_set; @@ -2739,9 +2752,23 @@ ScalarFunctionSet JulianDayFun::GetFunctions() { return operator_set; } +//! Binding date_part with a constant part replaces it with the unary part function (year, month, ...), so the bound +//! call has one argument and must be rendered under the replacement's own name +static unique_ptr DatePartUnbind(FunctionUnbindInput &input) { + auto &function = input.expression.Function(); + if (input.children.size() == 1) { + return make_uniq(function.GetQualifiedName(), std::move(input.children)); + } + if (input.children.size() != 2) { + return nullptr; + } + return make_uniq(function.GetDefinition()->GetQualifiedName(), std::move(input.children)); +} + // Names the "part,ts" pair shared by date_part's per-type overloads. static ScalarFunction NamePartTsArguments(ScalarFunction fun, const LogicalType &type) { fun.GetSignature().AddParameter("part", LogicalType::VARCHAR).AddParameter("ts", type); + fun.SetUnbindCallback(DatePartUnbind); return fun; } diff --git a/src/duckdb/extension/core_functions/scalar/date/date_trunc.cpp b/src/duckdb/extension/core_functions/scalar/date/date_trunc.cpp index 41643d8c0..9998eb3d5 100644 --- a/src/duckdb/extension/core_functions/scalar/date/date_trunc.cpp +++ b/src/duckdb/extension/core_functions/scalar/date/date_trunc.cpp @@ -83,9 +83,7 @@ struct DateTrunc { template static inline TR Operation(TA input) { date_t date = Date::GetMondayOfCurrentWeek(input); - date.days -= (Date::ExtractISOWeekNumber(date) - 1) * Interval::DAYS_PER_WEEK; - - return date; + return Date::SubtractDays(date, (Date::ExtractISOWeekNumber(date) - 1) * Interval::DAYS_PER_WEEK); } }; @@ -496,8 +494,16 @@ unique_ptr DateTruncStatistics(vector &child_sta } // Infinite values are unmodified - auto min_part = DateTrunc::UnaryFunction(min); - auto max_part = DateTrunc::UnaryFunction(max); + TR min_part; + TR max_part; + try { + min_part = DateTrunc::UnaryFunction(min); + max_part = DateTrunc::UnaryFunction(max); + } catch (const ConversionException &) { + // the bounds are not in the range of the result type (e.g. a DATE beyond the TIMESTAMP range) - the rows + // holding them may not even reach the function, so do not propagate statistics + return nullptr; + } auto min_value = Value::CreateValue(min_part); auto max_value = Value::CreateValue(max_part); diff --git a/src/duckdb/extension/core_functions/scalar/debug/index_key.cpp b/src/duckdb/extension/core_functions/scalar/debug/index_key.cpp index 1d5204e03..e85e190f2 100644 --- a/src/duckdb/extension/core_functions/scalar/debug/index_key.cpp +++ b/src/duckdb/extension/core_functions/scalar/debug/index_key.cpp @@ -115,31 +115,42 @@ struct IndexKeyBindData : public FunctionData { vector key_types; }; -static unique_ptr IndexKeyBind(BindScalarFunctionInput &input) { - auto &context = input.GetClientContext(); - auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); +// index_key resolves index metadata during binding. +// Register the owning catalog so cached prepared statements rebind after catalog changes. +static void RegisterIndexKeyRead(BindScalarFunctionInput &input, TableCatalogEntry &table_entry) { + if (input.HasBinder()) { + auto &binder = input.GetBinder(); + binder.GetStatementProperties().RegisterDBRead(table_entry.ParentCatalog(), input.GetClientContext()); + } +} + +static void RegisterIndexKeyRead(ResolveScalarFunctionTypesInput &input, TableCatalogEntry &table_entry) { +} + +//! Called from both resolve_types and bind - the key types and the bind data both come from the index +template +static unique_ptr IndexKeyBind(INPUT &input) { + ClientContext &context = input.GetClientContext(); + BoundScalarFunction &bound_function = input.GetBoundFunction(); + const vector> &arguments = input.GetArguments(); if (arguments.size() < INDEX_KEY_FIXED_ARGS) { throw BinderException("index_key: requires at least two arguments - path (STRUCT), index_name"); } - auto path = EvaluateTableDescription(input.GetConstant(0)); - auto index_name = GetStringArgument(input.GetConstant(1), "index_name"); + const Value path_value = input.GetConstant(0); + const Value index_name_value = input.GetConstant(1); + auto path = EvaluateTableDescription(path_value); + auto index_name = GetStringArgument(index_name_value, "index_name"); auto qualified_table = path.qualified_name.ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); auto &table_entry = Catalog::GetEntry(context, path.qualified_name); - if (!table_entry.IsDuckTable()) { + auto duck_table = table_entry.TryGetDuckTableEntry(); + if (!duck_table) { throw BinderException("index_key: table '%s' is not a DuckDB table", qualified_table); } - // index_key resolves index metadata during binding. - // Register the owning catalog so cached prepared statements rebind after catalog changes. - if (input.HasBinder()) { - auto &binder = input.GetBinder(); - binder.GetStatementProperties().RegisterDBRead(table_entry.ParentCatalog(), context); - } - auto &duck_table = table_entry.Cast(); - auto &data_table = duck_table.GetStorage(); + RegisterIndexKeyRead(input, table_entry); + auto &data_table = duck_table->GetStorage(); auto &data_table_info = *data_table.GetDataTableInfo(); // Note: It may come up in testing that we don't want to force binding here, e.g. if the test should explicitly @@ -180,6 +191,10 @@ static unique_ptr IndexKeyBind(BindScalarFunctionInput &input) { return make_uniq(index_entry, std::move(key_types)); } +static void IndexKeyResolveTypes(ResolveScalarFunctionTypesInput &input) { + IndexKeyBind(input); +} + static void IndexKeyFunction(DataChunk &args, ExpressionState &state, Vector &result) { auto &func_expr = state.expr.Cast(); auto &bind_data = func_expr.BindInfo()->Cast(); @@ -216,9 +231,10 @@ static void IndexKeyFunction(DataChunk &args, ExpressionState &state, Vector &re } // namespace ScalarFunction IndexKeyFun::GetFunction() { - ScalarFunction fun("index_key", {}, LogicalType::BLOB, IndexKeyFunction, IndexKeyBind); + ScalarFunction fun("index_key", {}, LogicalType::BLOB, IndexKeyFunction, IndexKeyBind); fun.GetSignature().AddParameter("path", LogicalTypeId::STRUCT).AddParameter("name", LogicalType::VARCHAR); - fun.SetVarArgs(LogicalTypeId::ANY); + fun.GetSignature().AddArgs("args", LogicalTypeId::ANY); + fun.SetResolveTypesCallback(IndexKeyResolveTypes); return fun; } diff --git a/src/duckdb/extension/core_functions/scalar/debug/vector_type.cpp b/src/duckdb/extension/core_functions/scalar/debug/vector_type.cpp index f99e10358..51730f141 100644 --- a/src/duckdb/extension/core_functions/scalar/debug/vector_type.cpp +++ b/src/duckdb/extension/core_functions/scalar/debug/vector_type.cpp @@ -17,6 +17,7 @@ ScalarFunction VectorTypeFun::GetFunction() { auto vector_type_fun = ScalarFunction({}, LogicalType::VARCHAR, VectorTypeFunction); vector_type_fun.GetSignature().AddParameter("col", LogicalType::ANY); vector_type_fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); + vector_type_fun.SetStability(FunctionStability::VOLATILE); return vector_type_fun; } diff --git a/src/duckdb/extension/core_functions/scalar/enum/enum_functions.cpp b/src/duckdb/extension/core_functions/scalar/enum/enum_functions.cpp index a5f897923..1b2fa8618 100644 --- a/src/duckdb/extension/core_functions/scalar/enum/enum_functions.cpp +++ b/src/duckdb/extension/core_functions/scalar/enum/enum_functions.cpp @@ -21,44 +21,46 @@ static void EnumLastFunction(DataChunk &input, ExpressionState &state, Vector &r } static void EnumRangeFunction(DataChunk &input, ExpressionState &state, Vector &result) { - auto types = input.GetTypes(); - D_ASSERT(types.size() == 1); - auto enum_size = EnumType::GetSize(types[0]); - auto &enum_vector = EnumType::GetValuesInsertOrder(types[0]); - vector enum_values; - for (idx_t i = 0; i < enum_size; i++) { - enum_values.emplace_back(enum_vector.GetValue(i)); + D_ASSERT(input.ColumnCount() == 1); + auto &enum_type = input.data[0].GetType(); + auto &enum_vector = EnumType::GetValuesInsertOrder(enum_type); + auto enum_strings = enum_vector.Values(); + const auto enum_size = EnumType::GetSize(enum_type); + + // every row gets the same list: write the enum's values into the result once and make it a constant, instead of + // materializing them as Values (three copies of the enum per call, dozens of bytes per value) + { + auto writer = FlatVector::Writer>(result, 1); + idx_t enum_idx = 0; + for (auto &child_writer : writer.WriteList(enum_size)) { + child_writer.WriteValue(enum_strings[enum_idx++].GetValue()); + } } - auto val = Value::LIST(LogicalType::VARCHAR, enum_values); - result.Reference(val, count_t(input.size())); + result.SetVectorType(VectorType::CONSTANT_VECTOR); } static void EnumRangeBoundaryFunction(DataChunk &input, ExpressionState &state, Vector &result) { auto types = input.GetTypes(); D_ASSERT(types.size() == 2); - idx_t start, end; - auto first_param = input.GetValue(0, 0); - auto second_param = input.GetValue(1, 0); - auto &enum_vector = - first_param.IsNull() ? EnumType::GetValuesInsertOrder(types[1]) : EnumType::GetValuesInsertOrder(types[0]); - - if (first_param.IsNull()) { - start = 0; - } else { - start = first_param.GetValue(); - } - if (second_param.IsNull()) { - end = EnumType::GetSize(types[0]); - } else { - end = second_param.GetValue() + 1; + // the binder guarantees that at least one of the parameters is an ENUM + auto &enum_type = types[0].id() == LogicalTypeId::ENUM ? types[0] : types[1]; + auto &enum_vector = EnumType::GetValuesInsertOrder(enum_type); + auto enum_strings = enum_vector.Values(); + + const auto count = input.size(); + auto writer = FlatVector::Writer>(result, count); + for (idx_t row = 0; row < count; row++) { + // a NULL boundary means that the range starts at the first / ends at the last value of the enum + auto first_param = input.GetValue(0, row); + auto second_param = input.GetValue(1, row); + idx_t start = first_param.IsNull() ? 0 : first_param.GetValue(); + idx_t end = second_param.IsNull() ? EnumType::GetSize(enum_type) : second_param.GetValue() + 1; + idx_t enum_idx = start; + for (auto &child_writer : writer.WriteList(end > start ? end - start : 0)) { + child_writer.WriteValue(enum_strings[enum_idx++].GetValue()); + } } - vector enum_values; - for (idx_t i = start; i < end; i++) { - enum_values.emplace_back(enum_vector.GetValue(i)); - } - auto val = Value::LIST(LogicalType::VARCHAR, enum_values); - result.Reference(val, count_t(input.size())); } static void EnumCodeFunction(DataChunk &input, ExpressionState &state, Vector &result) { diff --git a/src/duckdb/extension/core_functions/scalar/generic/alias.cpp b/src/duckdb/extension/core_functions/scalar/generic/alias.cpp index 722c4d42f..9ea3fc3f9 100644 --- a/src/duckdb/extension/core_functions/scalar/generic/alias.cpp +++ b/src/duckdb/extension/core_functions/scalar/generic/alias.cpp @@ -1,10 +1,16 @@ #include "core_functions/scalar/generic_functions.hpp" +#include "duckdb/common/serializer/serializer.hpp" +#include "duckdb/common/serializer/deserializer.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" +#include "duckdb/parser/expression/case_expression.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/parser/expression/function_expression.hpp" +#include "duckdb/parser/expression/operator_expression.hpp" namespace duckdb { namespace { -struct AliasBindData : public FunctionData { +struct AliasBindData final : public FunctionData { explicit AliasBindData(Identifier alias_p) : alias(std::move(alias_p)) { } @@ -23,6 +29,30 @@ struct AliasBindData : public FunctionData { unique_ptr AliasBind(BindScalarFunctionInput &input) { return make_uniq(input.GetArguments()[0]->GetName()); } + +void AliasSerialize(Serializer &serializer, const optional_ptr data, const BoundScalarFunction &) { + serializer.WriteProperty(100, "alias", data->Cast().alias); +} + +unique_ptr AliasDeserialize(Deserializer &deserializer, BoundScalarFunction &) { + return make_uniq(deserializer.ReadProperty(100, "alias")); +} +unique_ptr AliasUnbind(FunctionUnbindInput &input) { + auto &expression = input.expression; + if (input.children.size() != 1 || (expression.GetAlias().empty() && !expression.BindInfo())) { + return nullptr; + } + auto value = Value(expression.GetAlias().empty() ? expression.BindInfo()->Cast().alias + : expression.GetAlias()); + auto call = make_uniq(expression.Function().GetDefinition()->GetQualifiedName(), + std::move(input.children)); + auto result = make_uniq(); + result->CaseChecksMutable().push_back( + {make_uniq(ExpressionType::OPERATOR_IS_NULL, std::move(call)), + ConstantExpression::FromValue(value)}); + result->ElseMutable() = ConstantExpression::FromValue(value); + return std::move(result); +} } // namespace static void AliasFunction(DataChunk &args, ExpressionState &state, Vector &result) { @@ -37,6 +67,9 @@ ScalarFunction AliasFun::GetFunction() { fun.GetSignature().AddParameter("expr", LogicalType::ANY); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); fun.GetProperties().SetRequiresExpressionNames(true); + fun.SetSerializeCallback(AliasSerialize); + fun.SetDeserializeCallback(AliasDeserialize); + fun.SetUnbindCallback(AliasUnbind); return fun; } diff --git a/src/duckdb/extension/core_functions/scalar/generic/binning.cpp b/src/duckdb/extension/core_functions/scalar/generic/binning.cpp index 76a6c48ee..f393f61ef 100644 --- a/src/duckdb/extension/core_functions/scalar/generic/binning.cpp +++ b/src/duckdb/extension/core_functions/scalar/generic/binning.cpp @@ -414,26 +414,25 @@ struct EquiWidthBinsTimestamp { } }; -unique_ptr BindEquiWidthFunction(BindScalarFunctionInput &input) { +void EquiWidthResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); + auto &max_type = input.GetArgumentType(1); // while internally the bins are computed over a unified type // the equi_width_bins function returns the same type as the input MAX LogicalType child_type; - switch (arguments[1]->GetReturnType().id()) { + switch (max_type.id()) { case LogicalTypeId::UNKNOWN: case LogicalTypeId::SQLNULL: - return nullptr; + return; case LogicalTypeId::DECIMAL: // for decimals we promote to double because child_type = LogicalType::DOUBLE; break; default: - child_type = arguments[1]->GetReturnType(); + child_type = max_type; break; } bound_function.SetReturnType(LogicalType::LIST(child_type)); - return nullptr; } template @@ -496,7 +495,8 @@ unique_ptr EquiWidthBinDeserialize(Deserializer &deserializer, Bou static void AddEquiWidthBinFunction(ScalarFunctionSet &functions, const LogicalType &min_max_type, scalar_function_t function) { - ScalarFunction fun({}, LogicalType::LIST(LogicalType::ANY), function, BindEquiWidthFunction); + ScalarFunction fun({}, LogicalType::LIST(LogicalType::ANY), std::move(function)); + fun.SetResolveTypesCallback(EquiWidthResolveTypes); fun.GetSignature() .AddParameter("min", min_max_type) .AddParameter("max", min_max_type) diff --git a/src/duckdb/extension/core_functions/scalar/generic/cast_to_type.cpp b/src/duckdb/extension/core_functions/scalar/generic/cast_to_type.cpp index 5db338cc4..dae0f1d78 100644 --- a/src/duckdb/extension/core_functions/scalar/generic/cast_to_type.cpp +++ b/src/duckdb/extension/core_functions/scalar/generic/cast_to_type.cpp @@ -18,7 +18,8 @@ unique_ptr BindCastToTypeFunction(FunctionBindExpressionInput &input if (return_type.id() == LogicalTypeId::SQLNULL) { throw InvalidInputException("cast_to_type cannot be used to cast to NULL"); } - return BoundCastExpression::AddCastToType(input.context, std::move(input.children[0]), return_type); + auto result = BoundCastExpression::AddCastToType(input.context, std::move(input.children[0]), return_type); + return Expression::PreserveReturnType(return_type, std::move(result)); } } // namespace diff --git a/src/duckdb/extension/core_functions/scalar/generic/current_setting.cpp b/src/duckdb/extension/core_functions/scalar/generic/current_setting.cpp index 48b0adbea..c1da6d140 100644 --- a/src/duckdb/extension/core_functions/scalar/generic/current_setting.cpp +++ b/src/duckdb/extension/core_functions/scalar/generic/current_setting.cpp @@ -59,6 +59,8 @@ ScalarFunction CurrentSettingFun::GetFunction() { auto fun = ScalarFunction({}, LogicalType::ANY, CurrentSettingFunction, CurrentSettingBind); fun.GetSignature().AddParameter("setting_name", LogicalType::VARCHAR); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); + // the value of the setting can change between queries + fun.SetStability(FunctionStability::CONSISTENT_WITHIN_QUERY); return fun; } diff --git a/src/duckdb/extension/core_functions/scalar/generic/hash.cpp b/src/duckdb/extension/core_functions/scalar/generic/hash.cpp index e6d9b26cd..31021ba40 100644 --- a/src/duckdb/extension/core_functions/scalar/generic/hash.cpp +++ b/src/duckdb/extension/core_functions/scalar/generic/hash.cpp @@ -9,7 +9,7 @@ static void HashFunction(DataChunk &args, ExpressionState &state, Vector &result ScalarFunction HashFun::GetFunction() { auto hash_fun = ScalarFunction({}, LogicalType::HASH, HashFunction); hash_fun.GetSignature().AddParameter("value", LogicalType::ANY); - hash_fun.SetVarArgs(LogicalType::ANY); + hash_fun.GetSignature().AddArgs("args", LogicalType::ANY); hash_fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); return hash_fun; } diff --git a/src/duckdb/extension/core_functions/scalar/generic/least.cpp b/src/duckdb/extension/core_functions/scalar/generic/least.cpp index 109897987..00bc99451 100644 --- a/src/duckdb/extension/core_functions/scalar/generic/least.cpp +++ b/src/duckdb/extension/core_functions/scalar/generic/least.cpp @@ -223,8 +223,13 @@ unique_ptr PropagateLeastGreatestStats(ClientContext &context, F } auto result = NumericStats::CreateEmpty(return_type); - NumericStats::SetMin(result, IS_LEAST ? std::move(loose) : std::move(anchored)); - NumericStats::SetMax(result, IS_LEAST ? std::move(anchored) : std::move(loose)); + if constexpr (IS_LEAST) { + NumericStats::SetMin(result, loose); + NumericStats::SetMax(result, anchored); + } else { + NumericStats::SetMin(result, anchored); + NumericStats::SetMax(result, loose); + } result.Set(StatsInfo::CAN_HAVE_VALID_VALUES); if (!has_nonnull_input) { // the result is NULL only if all inputs are NULL @@ -234,15 +239,14 @@ unique_ptr PropagateLeastGreatestStats(ClientContext &context, F } template -unique_ptr BindLeastGreatest(BindScalarFunctionInput &input) { +void LeastGreatestResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &context = input.GetClientContext(); auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - LogicalType child_type = ExpressionBinder::GetExpressionReturnType(*arguments[0]); - for (idx_t i = 1; i < arguments.size(); i++) { - auto arg_type = ExpressionBinder::GetExpressionReturnType(*arguments[i]); + LogicalType child_type = ExpressionBinder::GetExpressionReturnType(input.GetArgument(0)); + for (idx_t i = 1; i < input.GetArgumentCount(); i++) { + auto arg_type = ExpressionBinder::GetExpressionReturnType(input.GetArgument(i)); if (!LogicalType::TryGetMaxLogicalType(context, child_type, arg_type, child_type)) { - throw BinderException(arguments[i]->GetQueryLocation(), + throw BinderException(input.GetArgument(i).GetQueryLocation(), "Cannot combine types of %s and %s - an explicit cast is required", child_type.ToString(), arg_type.ToString()); } @@ -295,14 +299,14 @@ unique_ptr BindLeastGreatest(BindScalarFunctionInput &input) { arg = child_type; } bound_function.SetReturnType(child_type); - return nullptr; } template ScalarFunction GetLeastGreatestFunction() { - ScalarFunction fun({}, LogicalType::ANY, nullptr, BindLeastGreatest, PropagateLeastGreatestStats, nullptr, - LogicalType::ANY, FunctionStability::CONSISTENT, FunctionNullHandling::SPECIAL_HANDLING); - fun.GetSignature().AddParameter("arg1", LogicalType::ANY); + ScalarFunction fun({{"arg1", LogicalType::ANY}}, LogicalType::ANY, nullptr, nullptr, + PropagateLeastGreatestStats, nullptr, LogicalType::ANY, FunctionStability::CONSISTENT, + FunctionNullHandling::SPECIAL_HANDLING); + fun.SetResolveTypesCallback(LeastGreatestResolveTypes); return fun; } diff --git a/src/duckdb/extension/core_functions/scalar/generic/system_functions.cpp b/src/duckdb/extension/core_functions/scalar/generic/system_functions.cpp index b1bf0fa8e..ccb4a19af 100644 --- a/src/duckdb/extension/core_functions/scalar/generic/system_functions.cpp +++ b/src/duckdb/extension/core_functions/scalar/generic/system_functions.cpp @@ -48,12 +48,14 @@ struct CurrentSchemasBindData : public FunctionData { } }; -unique_ptr CurrentSchemasBind(BindScalarFunctionInput &input) { - auto &context = input.GetClientContext(); - auto &arguments = input.GetArguments(); - if (arguments[0]->GetReturnType().id() != LogicalTypeId::BOOLEAN) { +void CurrentSchemasResolveTypes(ResolveScalarFunctionTypesInput &input) { + if (input.GetArgumentType(0).id() != LogicalTypeId::BOOLEAN) { throw BinderException("current_schemas requires a boolean input"); } +} + +unique_ptr CurrentSchemasBind(BindScalarFunctionInput &input) { + auto &context = input.GetClientContext(); Value schema_value = input.GetConstant(0); Value result_val; if (schema_value.IsNull()) { @@ -128,6 +130,7 @@ ScalarFunction CurrentSchemasFun::GetFunction() { auto varchar_list_type = LogicalType::LIST(LogicalType::VARCHAR); ScalarFunction current_schemas({}, varchar_list_type, CurrentSchemasFunction, CurrentSchemasBind); current_schemas.GetSignature().AddParameter("include_implicit", LogicalType::BOOLEAN); + current_schemas.SetResolveTypesCallback(CurrentSchemasResolveTypes); current_schemas.SetStability(FunctionStability::CONSISTENT_WITHIN_QUERY); return current_schemas; } diff --git a/src/duckdb/extension/core_functions/scalar/generic/type_functions.cpp b/src/duckdb/extension/core_functions/scalar/generic/type_functions.cpp index e0aeab055..03fd01d40 100644 --- a/src/duckdb/extension/core_functions/scalar/generic/type_functions.cpp +++ b/src/duckdb/extension/core_functions/scalar/generic/type_functions.cpp @@ -111,7 +111,11 @@ static unique_ptr BindMakeTypeFunctionExpression(FunctionBindExpress } auto type_name = args.front().second.GetValue(); - auto qualified_name = QualifiedName::Parse(type_name); + QualifiedName qualified_name; + string error; + if (!QualifiedName::TryParse(type_name, qualified_name, error)) { + throw ParserException(error); + } auto unbound_type = LogicalType::UNBOUND(make_uniq(std::move(qualified_name), std::move(type_args))); @@ -128,7 +132,7 @@ ScalarFunction MakeTypeFun::GetFunction() { fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); fun.SetBindExpressionCallback(BindMakeTypeFunctionExpression); fun.GetProperties().SetRequiresExpressionNames(true); - fun.SetVarArgs(LogicalType::ANY); + fun.GetSignature().AddArgs("args", LogicalType::ANY).AddKwargs("kwargs", LogicalType::ANY); return fun; } diff --git a/src/duckdb/extension/core_functions/scalar/list/array_slice.cpp b/src/duckdb/extension/core_functions/scalar/list/array_slice.cpp index 51f965218..32cfada0a 100644 --- a/src/duckdb/extension/core_functions/scalar/list/array_slice.cpp +++ b/src/duckdb/extension/core_functions/scalar/list/array_slice.cpp @@ -1,3 +1,4 @@ +#include "duckdb/planner/expression/bound_constant_expression.hpp" #include "core_functions/scalar/list_functions.hpp" #include "duckdb/common/string_util.hpp" #include "duckdb/common/swap.hpp" @@ -6,13 +7,11 @@ #include "duckdb/function/scalar/string_functions.hpp" #include "duckdb/function/scalar/string_common.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" -#include "duckdb/planner/expression/bound_constant_expression.hpp" #include "duckdb/planner/expression/bound_cast_expression.hpp" +#include "duckdb/parser/expression/operator_expression.hpp" namespace duckdb { -namespace { - struct ListSliceBindData : public FunctionData { ListSliceBindData(const LogicalType &return_type_p, bool begin_is_empty_p, bool end_is_empty_p) : return_type(return_type_p), begin_is_empty(begin_is_empty_p), end_is_empty(end_is_empty_p) { @@ -36,6 +35,8 @@ unique_ptr ListSliceBindData::Copy() const { return make_uniq(return_type, begin_is_empty, end_is_empty); } +namespace { + template idx_t CalculateSliceLength(idx_t begin, idx_t end, INDEX_TYPE step, bool svalid) { if (step < 0) { @@ -193,6 +194,10 @@ void ExecuteFlatSlice(Vector &result, Vector &list_vector, Vector &begin_vector, auto begin = begin_is_empty ? 0 : UnifiedVectorFormat::GetData(begin_data)[begin_idx]; auto end = end_is_empty ? OP::ValueLength(sliced) : UnifiedVectorFormat::GetData(end_data)[end_idx]; auto step = step_vector ? UnifiedVectorFormat::GetData(step_data)[step_idx] : 1; + if (step == NumericLimits::Minimum()) { + // the minimum cannot be negated - any step that is at least as large as the list selects the same entry + step = -NumericLimits::Maximum(); + } if (step < 0) { swap(begin, end); @@ -299,18 +304,18 @@ void ArraySliceFunction(DataChunk &args, ExpressionState &state, Vector &result) } //! An omitted slice bound is parsed as an empty list constructor (see OperatorExpression::EmptySliceBound) -bool CheckIfParamIsEmpty(duckdb::unique_ptr ¶m) { - if (param->GetReturnType().id() != LogicalTypeId::LIST) { +bool CheckIfParamIsEmpty(const Expression ¶m) { + if (param.GetReturnType().id() != LogicalTypeId::LIST) { return false; } - if (param->GetExpressionClass() == ExpressionClass::BOUND_FUNCTION) { - auto &function = param->Cast(); + if (param.GetExpressionClass() == ExpressionClass::BOUND_FUNCTION) { + auto &function = param.Cast(); if (function.Function().GetName() == "list_value" && function.GetChildren().empty()) { return true; } } - if (param->GetExpressionClass() == ExpressionClass::BOUND_CONSTANT) { - auto &value = param->Cast().GetValue(); + if (param.GetExpressionClass() == ExpressionClass::BOUND_CONSTANT) { + auto &value = param.Cast().GetValue(); if (!value.IsNull() && ListValue::GetChildren(value).empty()) { return true; } @@ -319,24 +324,22 @@ bool CheckIfParamIsEmpty(duckdb::unique_ptr ¶m) { throw BinderException("The upper and lower bounds of the slice must be a BIGINT"); } -unique_ptr ArraySliceBind(BindScalarFunctionInput &input) { - auto &context = input.GetClientContext(); +void ArraySliceResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - D_ASSERT(arguments.size() == 3 || arguments.size() == 4); + D_ASSERT(input.GetArgumentCount() == 3 || input.GetArgumentCount() == 4); D_ASSERT(bound_function.GetArguments().size() == 3 || bound_function.GetArguments().size() == 4); - switch (arguments[0]->GetReturnType().id()) { + auto &list_type = input.GetArgumentType(0); + switch (list_type.id()) { case LogicalTypeId::ARRAY: { // Cast to list - auto child_type = ArrayType::GetChildType(arguments[0]->GetReturnType()); - auto target_type = LogicalType::LIST(child_type); - arguments[0] = BoundCastExpression::AddCastToType(context, std::move(arguments[0]), target_type); - bound_function.SetReturnType(arguments[0]->GetReturnType()); + auto target_type = LogicalType::LIST(ArrayType::GetChildType(list_type)); + bound_function.GetArguments()[0] = target_type; + bound_function.SetReturnType(target_type); } break; case LogicalTypeId::LIST: // The result is the same type - bound_function.SetReturnType(arguments[0]->GetReturnType()); + bound_function.SetReturnType(list_type); break; case LogicalTypeId::BLOB: case LogicalTypeId::VARCHAR: @@ -346,15 +349,15 @@ unique_ptr ArraySliceBind(BindScalarFunctionInput &input) { "Slice with steps has not been implemented for string types, you can consider rewriting your query as " "follows:\n SELECT array_to_string((str_split(string, '')[begin:end:step], '');"); } - if (arguments[0]->GetReturnType().IsJSONType()) { + if (list_type.IsJSONType()) { // This is needed to avoid producing invalid JSON bound_function.GetArguments()[0] = LogicalType::VARCHAR; bound_function.SetReturnType(LogicalType::VARCHAR); } else { - bound_function.SetReturnType(arguments[0]->GetReturnType()); + bound_function.SetReturnType(list_type); } for (idx_t i = 1; i < 3; i++) { - if (arguments[i]->GetReturnType().id() != LogicalTypeId::LIST) { + if (input.GetArgumentType(i).id() != LogicalTypeId::LIST) { bound_function.GetArguments()[i] = LogicalType::BIGINT; } } @@ -368,15 +371,19 @@ unique_ptr ArraySliceBind(BindScalarFunctionInput &input) { throw BinderException("ARRAY_SLICE can only operate on LISTs and VARCHARs"); } - bool begin_is_empty = CheckIfParamIsEmpty(arguments[1]); - if (!begin_is_empty) { - bound_function.GetArguments()[1] = LogicalType::BIGINT; - } - bool end_is_empty = CheckIfParamIsEmpty(arguments[2]); - if (!end_is_empty) { - bound_function.GetArguments()[2] = LogicalType::BIGINT; + // empty bounds keep their (empty list) type - the other bounds are cast to BIGINT + for (idx_t i = 1; i < 3; i++) { + if (!CheckIfParamIsEmpty(input.GetArgument(i))) { + bound_function.GetArguments()[i] = LogicalType::BIGINT; + } } +} +unique_ptr ArraySliceBind(BindScalarFunctionInput &input) { + auto &bound_function = input.GetBoundFunction(); + auto &arguments = input.GetArguments(); + bool begin_is_empty = arguments[1]->GetReturnType().id() == LogicalTypeId::LIST; + bool end_is_empty = arguments[2]->GetReturnType().id() == LogicalTypeId::LIST; return make_uniq(bound_function.GetReturnType(), begin_is_empty, end_is_empty); } @@ -419,6 +426,40 @@ unique_ptr ArraySlicePropagateStats(ClientContext &context, Func return PropagateStringSliceStats(input, start_character_index, character_count); } +unique_ptr ArraySliceUnbind(FunctionUnbindInput &input) { + if ((input.children.size() != 3 && input.children.size() != 4) || !input.expression.BindInfo()) { + return nullptr; + } + auto &data = input.expression.BindInfo()->Cast(); + for (idx_t i = 1; i < 3; i++) { + auto &child = *input.expression.GetChildren()[i]; + if (child.GetReturnType().id() != LogicalTypeId::LIST) { + continue; + } + if (child.GetExpressionClass() == ExpressionClass::BOUND_CONSTANT) { + auto &value = child.Cast().GetValue(); + if (!value.IsNull() && ListValue::GetChildren(value).empty()) { + continue; + } + } else if (child.GetExpressionClass() == ExpressionClass::BOUND_FUNCTION) { + auto &function = child.Cast(); + auto &definition = function.Function().GetDefinition(); + if (definition && definition->GetQualifiedName() == QualifiedName("system", "main", "list_value") && + function.GetChildren().empty()) { + continue; + } + } + return nullptr; + } + if (data.begin_is_empty) { + input.children[1] = OperatorExpression::EmptySliceBound(); + } + if (data.end_is_empty) { + input.children[2] = OperatorExpression::EmptySliceBound(); + } + return make_uniq(ExpressionType::ARRAY_SLICE, std::move(input.children)); +} + } // namespace ScalarFunctionSet ListSliceFun::GetFunctions() { // the arguments and return types are actually set in the binder function @@ -427,7 +468,9 @@ ScalarFunctionSet ListSliceFun::GetFunctions() { .AddParameter("list", LogicalType::ANY) .AddParameter("begin", LogicalType::ANY) .AddParameter("end", LogicalType::ANY); + fun.SetResolveTypesCallback(ArraySliceResolveTypes); fun.SetStatisticsCallback(ArraySlicePropagateStats); + fun.SetUnbindCallback(ArraySliceUnbind); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); fun.SetFallible(); ScalarFunctionSet set; diff --git a/src/duckdb/extension/core_functions/scalar/list/list_aggregates.cpp b/src/duckdb/extension/core_functions/scalar/list/list_aggregates.cpp index f6a4315b8..e7c38d8f2 100644 --- a/src/duckdb/extension/core_functions/scalar/list/list_aggregates.cpp +++ b/src/duckdb/extension/core_functions/scalar/list/list_aggregates.cpp @@ -13,23 +13,13 @@ #include "duckdb/planner/expression/bound_cast_expression.hpp" #include "duckdb/planner/expression/bound_constant_expression.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" +#include "duckdb/planner/expression/bound_reference_expression.hpp" #include "duckdb/planner/expression_binder.hpp" namespace duckdb { namespace { -struct ListAggregatesLocalState : public FunctionLocalState { - explicit ListAggregatesLocalState(Allocator &allocator) : arena_allocator(allocator) { - } - - ArenaAllocator arena_allocator; -}; - -unique_ptr ListAggregatesInitLocalState(ExpressionState &state, const BoundFunctionExpression &expr, - FunctionData *bind_data) { - return make_uniq(BufferAllocator::Get(state.GetContext())); -} // FIXME: benchmark the use of cluster_update against using update (if applicable) unique_ptr ListAggregatesBindFailure(BoundScalarFunction &bound_function) { @@ -93,6 +83,36 @@ ListAggregatesBindData::ListAggregatesBindData(const LogicalType &stype_p, uniqu ListAggregatesBindData::~ListAggregatesBindData() { } +struct ListAggregatesLocalState : public FunctionLocalState { + ListAggregatesLocalState(ClientContext &context, optional_ptr bind_data) + : arena_allocator(BufferAllocator::Get(context)) { + if (!bind_data) { + return; + } + // arguments that the bind of the aggregate derived from its input (e.g. a collation of the compared value) are + // evaluated over the list elements + auto &aggr = bind_data->Cast().aggr_expr->Cast(); + auto &children = aggr.GetChildren(); + for (idx_t child_idx = 1; child_idx < children.size(); child_idx++) { + if (children[child_idx]->GetExpressionClass() == ExpressionClass::BOUND_CONSTANT) { + continue; + } + if (!derived_executor) { + derived_executor = make_uniq(context); + } + derived_executor->AddExpression(*children[child_idx]); + } + } + + ArenaAllocator arena_allocator; + unique_ptr derived_executor; +}; + +unique_ptr ListAggregatesInitLocalState(ExpressionState &state, const BoundFunctionExpression &expr, + FunctionData *bind_data) { + return make_uniq(state.GetContext(), bind_data); +} + struct StateVector { StateVector(idx_t count_p, unique_ptr aggr_expr_p) : count(count_p), aggr_expr(std::move(aggr_expr_p)), state_vector(Vector(LogicalType::POINTER, count_p)) { @@ -226,7 +246,8 @@ void ListAggregatesFunction(DataChunk &args, ExpressionState &state, Vector &res auto &func_expr = state.expr.Cast(); auto &info = func_expr.BindInfo()->Cast(); auto &aggr = info.aggr_expr->Cast(); - auto &allocator = ExecuteFunctionState::GetFunctionState(state)->Cast().arena_allocator; + auto &local_state = ExecuteFunctionState::GetFunctionState(state)->Cast(); + auto &allocator = local_state.arena_allocator; allocator.Reset(); AggregateFinalizeInputData aggr_input_data(aggr, allocator); @@ -260,12 +281,34 @@ void ListAggregatesFunction(DataChunk &args, ExpressionState &state, Vector &res // the aggregate's trailing arguments (e.g. the separator of string_agg) are constants that its bind folded into // the bind data - they are still part of its argument list, so they are passed along as constant vectors + DataChunk derived_input; + DataChunk derived_result; + if (local_state.derived_executor) { + derived_input.InitializeEmpty({child_vector.GetType()}); + vector derived_types; + for (auto &derived_expr : local_state.derived_executor->expressions) { + derived_types.push_back(derived_expr->GetReturnType()); + } + derived_result.Initialize(Allocator::DefaultAllocator(), derived_types); + } auto update_states = [&](idx_t update_count) { vector inputs; inputs.reserve(aggr.GetChildren().size()); inputs.emplace_back(child_vector, sel_vector, update_count); + if (local_state.derived_executor) { + derived_input.data[0].Slice(child_vector, sel_vector, update_count); + derived_input.SetChildCardinality(update_count); + derived_result.Reset(); + local_state.derived_executor->Execute(derived_input, derived_result); + } + idx_t derived_idx = 0; for (idx_t child_idx = 1; child_idx < aggr.GetChildren().size(); child_idx++) { - auto &constant = aggr.GetChildren()[child_idx]->Cast().GetValue(); + auto &child = *aggr.GetChildren()[child_idx]; + if (child.GetExpressionClass() != ExpressionClass::BOUND_CONSTANT) { + inputs.emplace_back(Vector::Ref(derived_result.data[derived_idx++])); + continue; + } + auto &constant = child.Cast().GetValue(); inputs.emplace_back(constant, count_t(update_count)); } aggr.Function().GetStateUpdateCallback()(inputs.data(), aggr_input_data, inputs.size(), state_vector_update, @@ -401,114 +444,143 @@ void ListUniqueFunction(DataChunk &args, ExpressionState &state, Vector &result) ListAggregatesFunction(args, state, result); } -template -unique_ptr ListAggregatesBindFunction(ClientContext &context, BoundScalarFunction &bound_function, - const LogicalType &list_child_type, - const AggregateFunction &aggr_function, - vector> &arguments) { - // create the child expression and its type - vector> children; - auto expr = make_uniq(Value(list_child_type)); - children.push_back(std::move(expr)); - // push any extra arguments into the list aggregate bind - if (arguments.size() > 2) { - for (idx_t i = 2; i < arguments.size(); i++) { - children.push_back(std::move(arguments[i])); - } - arguments.resize(2); +AggregateFunctionCatalogEntry &GetListAggregateEntry(ClientContext &context, const string &function_name) { + auto &func = Catalog::GetSystemCatalog(context).GetEntry( + context, QualifiedName(Catalog::GetSystemCatalog(context).GetName(), Identifier::DefaultSchema(), + Identifier(function_name))); + D_ASSERT(func.type == CatalogType::AGGREGATE_FUNCTION_ENTRY); + return func; +} + +//! Binds the aggregate that is applied to the elements of the list +template +unique_ptr BindListAggregate(ClientContext &context, LogicalType child_type, + const string &function_name, + vector> extra_arguments) { + auto &func = GetListAggregateEntry(context, function_name); + + // find a matching aggregate function + ErrorData error; + vector types; + types.push_back(child_type); + // push any extra arguments into the type list + for (auto &argument : extra_arguments) { + types.push_back(argument->GetReturnType()); } FunctionBinder function_binder(context); - auto bound_aggr_function = function_binder.BindAggregateFunction(aggr_function, std::move(children)); - bound_function.GetArguments()[0] = LogicalType::LIST(bound_aggr_function->Function().GetArguments()[0]); - - if (IS_AGGR) { - bound_function.SetReturnType(bound_aggr_function->Function().GetReturnType()); + auto best_function_idx = function_binder.BindFunction(func.name, func.functions, types, error); + if (!best_function_idx.IsValid()) { + throw BinderException("No matching aggregate function\n%s", error.Message()); } - // the extra arguments are passed to the aggregate as constant vectors, so they have to be constant - auto &aggr_children = bound_aggr_function->GetChildrenMutable(); - for (idx_t child_idx = 1; child_idx < aggr_children.size(); child_idx++) { - auto &child = aggr_children[child_idx]; - if (!child->IsFoldable()) { - throw InvalidInputException( - "Aggregate function %s is not supported for list_aggr: extra arguments must be constant", - bound_aggr_function->ToString()); - } - child = make_uniq(ExpressionExecutor::EvaluateScalar(context, *child)); + + // create the child expression and its type - it refers to the list elements + vector> children; + children.push_back(make_uniq(child_type, idx_t(0))); + for (auto &argument : extra_arguments) { + children.push_back(std::move(argument)); } - return make_uniq(bound_function.GetReturnType(), std::move(bound_aggr_function)); + // found a matching function, bind it as an aggregate + const auto &best_function = *func.functions.GetFunctionByOffset(best_function_idx.GetIndex()); + if (IS_AGGR) { + return function_binder.BindAggregateFunction(best_function, std::move(children)); + } + // create the unordered map histogram function + D_ASSERT(best_function.GetSignature().GetParameterCount() == 1); + auto aggr_function = HistogramFun::GetHistogramUnorderedMap(child_type); + return function_binder.BindAggregateFunction(aggr_function, std::move(children)); } template -unique_ptr ListAggregatesBind(BindScalarFunctionInput &input) { +void ListAggregatesResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &context = input.GetClientContext(); auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - arguments[0] = BoundCastExpression::AddArrayCastToList(context, std::move(arguments[0])); - - if (arguments[0]->GetReturnType().id() == LogicalTypeId::SQLNULL) { - return ListAggregatesBindFailure(bound_function); + auto list_type = input.GetArgumentType(0); + if (list_type.id() == LogicalTypeId::ARRAY) { + list_type = LogicalType::LIST(ArrayType::GetChildType(list_type)); } - bool is_parameter = arguments[0]->GetReturnType().id() == LogicalTypeId::UNKNOWN; - LogicalType child_type; - if (is_parameter) { - child_type = LogicalType::ANY; - } else if (arguments[0]->GetReturnType().id() == LogicalTypeId::LIST || - arguments[0]->GetReturnType().id() == LogicalTypeId::MAP) { - child_type = ListType::GetChildType(arguments[0]->GetReturnType()); - } else { + if (list_type.id() == LogicalTypeId::SQLNULL) { + ListAggregatesBindFailure(bound_function); + return; + } + auto function_name = IS_AGGR ? input.GetConstant(1).ToString() : string("histogram"); + if (list_type.id() == LogicalTypeId::UNKNOWN) { + // verify the aggregate exists, even if we cannot bind it yet + GetListAggregateEntry(context, function_name); + bound_function.GetArguments()[0] = LogicalTypeId::UNKNOWN; + bound_function.SetReturnType(LogicalType::SQLNULL); + return; + } + if (list_type.id() != LogicalTypeId::LIST && list_type.id() != LogicalTypeId::MAP) { // Unreachable throw InvalidInputException("First argument of list aggregate must be a list, map or array"); } - string function_name = "histogram"; - if (IS_AGGR) { // get the name of the aggregate function - function_name = input.GetConstant(1).ToString(); + vector> extra_arguments; + for (idx_t i = 2; i < input.GetArgumentCount(); i++) { + extra_arguments.push_back(input.GetArgument(i).Copy()); } + auto bound_aggr_function = BindListAggregate(context, ListType::GetChildType(list_type), function_name, + std::move(extra_arguments)); - // look up the aggregate function in the catalog - auto &func = Catalog::GetSystemCatalog(context).GetEntry( - context, QualifiedName(Catalog::GetSystemCatalog(context).GetName(), Identifier::DefaultSchema(), - Identifier(function_name))); - D_ASSERT(func.type == CatalogType::AGGREGATE_FUNCTION_ENTRY); + bound_function.GetArguments()[0] = LogicalType::LIST(bound_aggr_function->Function().GetArguments()[0]); + if (IS_AGGR) { + if (bound_aggr_function->Function().GetErrorMode() == FunctionErrors::CAN_THROW_RUNTIME_ERROR) { + // never clear the error mode here - executing the aggregate can throw regardless of how it is declared + bound_function.SetErrorMode(FunctionErrors::CAN_THROW_RUNTIME_ERROR); + } + bound_function.SetReturnType(bound_aggr_function->Function().GetReturnType()); + } +} - if (is_parameter) { - bound_function.GetArguments()[0] = LogicalTypeId::UNKNOWN; - bound_function.SetReturnType(LogicalType::SQLNULL); +template +unique_ptr ListAggregatesBind(BindScalarFunctionInput &input) { + auto &context = input.GetClientContext(); + auto &bound_function = input.GetBoundFunction(); + auto &arguments = input.GetArguments(); + auto list_type_id = arguments[0]->GetReturnType().id(); + if (list_type_id == LogicalTypeId::SQLNULL || list_type_id == LogicalTypeId::UNKNOWN) { + // no bind data, the bind data of this function is always a ListAggregatesBindData return nullptr; } - // find a matching aggregate function - ErrorData error; - vector types; - types.push_back(child_type); - // push any extra arguments into the type list + // the extra arguments are pushed into the list aggregate bind + vector> extra_arguments; for (idx_t i = 2; i < arguments.size(); i++) { - types.push_back(arguments[i]->GetReturnType()); + extra_arguments.push_back(std::move(arguments[i])); } + const idx_t extra_argument_count = extra_arguments.size(); + if (arguments.size() > 2) { + arguments.resize(2); + } + auto function_name = IS_AGGR ? input.GetConstant(1).ToString() : string("histogram"); + auto child_type = ListType::GetChildType(arguments[0]->GetReturnType()); + auto bound_aggr_function = + BindListAggregate(context, child_type, function_name, std::move(extra_arguments)); - FunctionBinder function_binder(context); - auto best_function_idx = function_binder.BindFunction(func.name, func.functions, types, error); - if (!best_function_idx.IsValid()) { - throw BinderException("No matching aggregate function\n%s", error.Message()); + // the extra arguments are passed to the aggregate as constant vectors, so they have to be constant - arguments + // that the bind of the aggregate added after them are derived from the list elements + auto &aggr_children = bound_aggr_function->GetChildrenMutable(); + for (idx_t child_idx = 1; child_idx < aggr_children.size() && child_idx <= extra_argument_count; child_idx++) { + auto &child = aggr_children[child_idx]; + if (!child->IsFoldable()) { + throw InvalidInputException( + "Aggregate function %s is not supported for list_aggr: extra arguments must be constant", + bound_aggr_function->ToString()); + } + child = make_uniq(ExpressionExecutor::EvaluateScalar(context, *child)); } - // found a matching function, bind it as an aggregate - const auto &best_function = *func.functions.GetFunctionByOffset(best_function_idx.GetIndex()); - if (IS_AGGR) { - if (best_function.GetErrorMode() == FunctionErrors::CAN_THROW_RUNTIME_ERROR) { - // never clear the error mode here - executing the aggregate can throw regardless of how it is declared - bound_function.SetErrorMode(FunctionErrors::CAN_THROW_RUNTIME_ERROR); + // Preserve folded SQL arguments without evaluating them again. + if (extra_argument_count + 2 == bound_function.GetLogicalArguments().size()) { + for (idx_t child_idx = 1; child_idx <= extra_argument_count && child_idx < aggr_children.size(); child_idx++) { + arguments.push_back(aggr_children[child_idx]->Copy()); } - return ListAggregatesBindFunction(context, bound_function, child_type, best_function, arguments); } - // create the unordered map histogram function - D_ASSERT(best_function.GetSignature().GetParameterCount() == 1); - auto aggr_function = HistogramFun::GetHistogramUnorderedMap(child_type); - return ListAggregatesBindFunction(context, bound_function, child_type, aggr_function, arguments); + return make_uniq(bound_function.GetReturnType(), std::move(bound_aggr_function)); } unique_ptr ListAggregateBind(BindScalarFunctionInput &input) { @@ -529,7 +601,8 @@ ScalarFunction ListAggregateFun::GetFunction() { .AddParameter("function_name", LogicalType::VARCHAR); result.SetFallible(); result.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); - result.SetVarArgs(LogicalType::ANY); + result.GetSignature().AddArgs("args", LogicalType::ANY); + result.SetResolveTypesCallback(ListAggregatesResolveTypes); result.SetSerializeCallback(ListAggregatesBindData::SerializeFunction); result.SetDeserializeCallback(ListAggregatesBindData::DeserializeFunction); return result; @@ -539,6 +612,7 @@ ScalarFunction ListDistinctFun::GetFunction() { ScalarFunction fun({}, LogicalType::LIST(LogicalType::TEMPLATE("T")), ListDistinctFunction, ListAggregatesBind, nullptr, ListAggregatesInitLocalState); fun.GetSignature().AddParameter("list", LogicalType::LIST(LogicalType::TEMPLATE("T"))); + fun.SetResolveTypesCallback(ListAggregatesResolveTypes); return fun; } @@ -546,6 +620,7 @@ ScalarFunction ListUniqueFun::GetFunction() { ScalarFunction fun({}, LogicalType::UBIGINT, ListUniqueFunction, ListAggregatesBind, nullptr, ListAggregatesInitLocalState); fun.GetSignature().AddParameter("list", LogicalType::LIST(LogicalType::ANY)); + fun.SetResolveTypesCallback(ListAggregatesResolveTypes); return fun; } diff --git a/src/duckdb/extension/core_functions/scalar/list/list_sort.cpp b/src/duckdb/extension/core_functions/scalar/list/list_sort.cpp index 255717fe1..a2281bdca 100644 --- a/src/duckdb/extension/core_functions/scalar/list/list_sort.cpp +++ b/src/duckdb/extension/core_functions/scalar/list/list_sort.cpp @@ -1,13 +1,21 @@ +#include "duckdb/common/type_visitor.hpp" +#include "duckdb/parser/expression/function_expression.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/planner/bound_expression_sql_exporter.hpp" +#include "duckdb/planner/expression/bound_constant_expression.hpp" #include "duckdb/common/vector/list_vector.hpp" #include "duckdb/main/client_context.hpp" #include "core_functions/scalar/list_functions.hpp" #include "duckdb/common/enum_util.hpp" #include "duckdb/common/numeric_utils.hpp" +#include "duckdb/common/serializer/deserializer.hpp" +#include "duckdb/common/serializer/serializer.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" #include "duckdb/planner/expression/bound_cast_expression.hpp" #include "duckdb/execution/expression_executor.hpp" #include "duckdb/planner/expression/bound_reference_expression.hpp" #include "duckdb/main/config.hpp" +#include "duckdb/main/settings.hpp" #include "duckdb/common/sorting/sort.hpp" #include "duckdb/planner/expression_binder.hpp" #include "duckdb/parallel/thread_context.hpp" @@ -16,7 +24,8 @@ namespace duckdb { struct ListSortBindData : public FunctionData { ListSortBindData(OrderType order_type_p, OrderByNullType null_order_p, bool is_grade_up, - const LogicalType &return_type_p, const LogicalType &child_type_p, ClientContext &context_p); + const LogicalType &return_type_p, const LogicalType &child_type_p, ClientContext &context_p, + unique_ptr sort_key_p = nullptr, optional default_collation_p = {}); ~ListSortBindData() override; OrderType order_type; @@ -28,18 +37,24 @@ struct ListSortBindData : public FunctionData { vector types; ClientContext &context; + unique_ptr sort_key; + optional default_collation; unique_ptr sort; public: bool Equals(const FunctionData &other_p) const override; unique_ptr Copy() const override; + static void Serialize(Serializer &serializer, const optional_ptr bind_data, + const BoundScalarFunction &); + static unique_ptr Deserialize(Deserializer &deserializer, BoundScalarFunction &); }; ListSortBindData::ListSortBindData(OrderType order_type_p, OrderByNullType null_order_p, bool is_grade_up_p, const LogicalType &return_type_p, const LogicalType &child_type_p, - ClientContext &context_p) + ClientContext &context_p, unique_ptr sort_key_p, + optional default_collation_p) : order_type(order_type_p), null_order(null_order_p), return_type(return_type_p), child_type(child_type_p), - is_grade_up(is_grade_up_p), context(context_p) { + is_grade_up(is_grade_up_p), context(context_p), default_collation(std::move(default_collation_p)) { // get the vector types types.emplace_back(LogicalType::USMALLINT); types.emplace_back(child_type); @@ -48,28 +63,60 @@ ListSortBindData::ListSortBindData(OrderType order_type_p, OrderByNullType null_ // get the BoundOrderByNode auto idx_col_expr = make_uniq_base(LogicalType::USMALLINT, 0U); - auto lists_col_expr = make_uniq_base(child_type, 1U); - // Normalize the sort key without changing the sorted values (#25108): push the - // type's collation (for INTERVAL this wraps the expression in - // normalized_interval(...), the same normalization comparison operators, ORDER BY - // and aggregates apply) onto the key expression BEFORE constructing its - // BoundOrderByNode. The key then byte-compares like ORDER BY, while the list - // payload keeps the original (non-normalized) values. - ExpressionBinder::PushCollation(context, lists_col_expr, child_type); + if (!sort_key_p) { + default_collation = Settings::Get(context); + sort_key_p = make_uniq_base(child_type, 1U); + // Normalize the sort key without changing the sorted values (#25108): push the + // type's collation (for INTERVAL this wraps the expression in + // normalized_interval(...), the same normalization comparison operators, ORDER BY + // and aggregates apply) onto the key expression BEFORE constructing its + // BoundOrderByNode. The key then byte-compares like ORDER BY, while the list + // payload keeps the original (non-normalized) values. + ExpressionBinder::PushCollation(context, sort_key_p, child_type); + } + sort_key = sort_key_p->Copy(); vector orders; orders.emplace_back(OrderType::ASCENDING, OrderByNullType::ORDER_DEFAULT, std::move(idx_col_expr)); - orders.emplace_back(order_type, null_order, std::move(lists_col_expr)); + orders.emplace_back(order_type, null_order, std::move(sort_key_p)); sort = unique_ptr(new Sort(context, orders, {LogicalType::USMALLINT, child_type, LogicalType::UINTEGER}, {2})); } unique_ptr ListSortBindData::Copy() const { - return make_uniq(order_type, null_order, is_grade_up, return_type, child_type, context); + return make_uniq(order_type, null_order, is_grade_up, return_type, child_type, context, + sort_key->Copy(), default_collation); } bool ListSortBindData::Equals(const FunctionData &other_p) const { auto &other = other_p.Cast(); - return order_type == other.order_type && null_order == other.null_order && is_grade_up == other.is_grade_up; + return order_type == other.order_type && null_order == other.null_order && return_type == other.return_type && + child_type == other.child_type && is_grade_up == other.is_grade_up && + Expression::Equals(sort_key, other.sort_key) && default_collation == other.default_collation; +} + +void ListSortBindData::Serialize(Serializer &serializer, const optional_ptr bind_data, + const BoundScalarFunction &) { + auto &data = bind_data->Cast(); + serializer.WriteProperty(100, "order_type", data.order_type); + serializer.WriteProperty(101, "null_order", data.null_order); + serializer.WriteProperty(102, "return_type", data.return_type); + serializer.WriteProperty(103, "child_type", data.child_type); + serializer.WriteProperty(104, "is_grade_up", data.is_grade_up); + serializer.WriteProperty(105, "sort_key", data.sort_key); + serializer.WritePropertyWithDefault(106, "default_collation", data.default_collation); +} + +unique_ptr ListSortBindData::Deserialize(Deserializer &deserializer, BoundScalarFunction &) { + auto order_type = deserializer.ReadProperty(100, "order_type"); + auto null_order = deserializer.ReadProperty(101, "null_order"); + auto return_type = deserializer.ReadProperty(102, "return_type"); + auto child_type = deserializer.ReadProperty(103, "child_type"); + auto is_grade_up = deserializer.ReadProperty(104, "is_grade_up"); + auto sort_key = deserializer.ReadProperty>(105, "sort_key"); + auto default_collation = deserializer.ReadPropertyWithDefault>(106, "default_collation"); + auto &context = deserializer.Get(); + return make_uniq(order_type, null_order, is_grade_up, return_type, child_type, context, + std::move(sort_key), std::move(default_collation)); } ListSortBindData::~ListSortBindData() { @@ -270,6 +317,9 @@ static unique_ptr ListSortBind(ClientContext &context, BoundScalar } arguments[0] = BoundCastExpression::AddArrayCastToList(context, std::move(arguments[0])); + if (arguments[0]->GetReturnType().id() != LogicalTypeId::LIST) { + throw BinderException("list_sort can only operate on LISTs"); + } child_type = ListType::GetChildType(arguments[0]->GetReturnType()); bound_function.GetArguments()[0] = arguments[0]->GetReturnType(); @@ -304,7 +354,17 @@ static unique_ptr ListGradeUpBind(BindScalarFunctionInput &input) order = config.ResolveOrder(context, order); null_order = config.ResolveNullOrder(context, order, null_order); + if (arguments[0]->GetReturnType().id() == LogicalTypeId::UNKNOWN) { + bound_function.GetArguments()[0] = LogicalTypeId::UNKNOWN; + bound_function.SetReturnType(LogicalType::SQLNULL); + return make_uniq(order, null_order, true, bound_function.GetReturnType(), + bound_function.GetReturnType(), context); + } + arguments[0] = BoundCastExpression::AddArrayCastToList(context, std::move(arguments[0])); + if (arguments[0]->GetReturnType().id() != LogicalTypeId::LIST) { + throw BinderException("list_grade_up can only operate on LISTs"); + } bound_function.GetArguments()[0] = arguments[0]->GetReturnType(); bound_function.SetReturnType(LogicalType::LIST(LogicalTypeId::BIGINT)); @@ -360,6 +420,61 @@ static unique_ptr ListReverseSortBind(BindScalarFunctionInput &inp return ListSortBind(context, bound_function, arguments, order, null_order); } +static unique_ptr ListSortUnbind(FunctionUnbindInput &input) { + if (input.children.empty() || !input.expression.BindInfo()) { + return nullptr; + } + auto &data = input.expression.BindInfo()->Cast(); + if (!data.default_collation || + TypeVisitor::Contains(data.child_type, [](const LogicalType &type) { return type.HasAlias(); })) { + return nullptr; + } + auto type = TypeVisitor::VisitReplace(data.child_type, [&](const LogicalType &child) { + if (child.id() == LogicalTypeId::VARCHAR && StringType::GetCollation(child).empty()) { + return LogicalType::VARCHAR_COLLATION(data.default_collation->empty() ? "binary" : *data.default_collation); + } + return child; + }); + auto witness = BoundExpressionSQLExporter::Export(BoundConstantExpression(Value(LogicalType::LIST(type))), {}); + if (witness.HasError()) { + return nullptr; + } + vector> cast_children; + cast_children.push_back(std::move(input.children[0])); + cast_children.push_back(std::move(witness.GetValue())); + vector> children; + children.push_back( + make_uniq(QualifiedName("system", "main", "cast_to_type"), std::move(cast_children))); + children.push_back(ConstantExpression::String(data.order_type == OrderType::ASCENDING ? "ASC" : "DESC")); + children.push_back( + ConstantExpression::String(data.null_order == OrderByNullType::NULLS_FIRST ? "NULLS FIRST" : "NULLS LAST")); + auto result_type = BoundExpressionSQLExporter::Export(BoundConstantExpression(Value(data.return_type)), {}); + if (result_type.HasError()) { + return nullptr; + } + vector> result; + result.push_back(make_uniq( + QualifiedName("system", "main", data.is_grade_up ? "list_grade_up" : "list_sort"), std::move(children))); + result.push_back(std::move(result_type.GetValue())); + return make_uniq(QualifiedName("system", "main", "cast_to_type"), std::move(result)); +} + +static void ListSortResolveTypes(ResolveScalarFunctionTypesInput &input) { + auto list_type = input.GetArgumentType(0); + if (list_type.id() == LogicalTypeId::ARRAY) { + list_type = LogicalType::LIST(ArrayType::GetChildType(list_type)); + } + if (list_type.id() == LogicalTypeId::LIST) { + input.GetBoundFunction().SetReturnType(list_type); + } +} + +static void SetListSortSerialization(ScalarFunction &function) { + function.SetUnbindCallback(ListSortUnbind); + function.SetSerializeCallback(ListSortBindData::Serialize); + function.SetDeserializeCallback(ListSortBindData::Deserialize); +} + ScalarFunctionSet ListSortFun::GetFunctions() { // one parameter: list ScalarFunction sort({}, LogicalType::LIST(LogicalType::ANY), ListSortFunction, ListNormalSortBind); @@ -377,6 +492,12 @@ ScalarFunctionSet ListSortFun::GetFunctions() { .AddParameter("list", LogicalType::LIST(LogicalType::ANY)) .AddParameter("sort_order", LogicalType::VARCHAR) .AddParameter("null_order", LogicalType::VARCHAR); + SetListSortSerialization(sort); + sort.SetResolveTypesCallback(ListSortResolveTypes); + SetListSortSerialization(sort_order); + sort_order.SetResolveTypesCallback(ListSortResolveTypes); + SetListSortSerialization(sort_orders); + sort_orders.SetResolveTypesCallback(ListSortResolveTypes); ScalarFunctionSet list_sort; list_sort.AddFunction(sort); @@ -387,21 +508,24 @@ ScalarFunctionSet ListSortFun::GetFunctions() { ScalarFunctionSet ListGradeUpFun::GetFunctions() { // one parameter: list - ScalarFunction sort({}, LogicalType::LIST(LogicalType::ANY), ListSortFunction, ListGradeUpBind); + ScalarFunction sort({}, LogicalType::LIST(LogicalType::BIGINT), ListSortFunction, ListGradeUpBind); sort.GetSignature().AddParameter("list", LogicalType::LIST(LogicalType::ANY)); // two parameters: list, order - ScalarFunction sort_order({}, LogicalType::LIST(LogicalType::ANY), ListSortFunction, ListGradeUpBind); + ScalarFunction sort_order({}, LogicalType::LIST(LogicalType::BIGINT), ListSortFunction, ListGradeUpBind); sort_order.GetSignature() .AddParameter("list", LogicalType::LIST(LogicalType::ANY)) .AddParameter("sort_order", LogicalType::VARCHAR); // three parameters: list, order, null order - ScalarFunction sort_orders({}, LogicalType::LIST(LogicalType::ANY), ListSortFunction, ListGradeUpBind); + ScalarFunction sort_orders({}, LogicalType::LIST(LogicalType::BIGINT), ListSortFunction, ListGradeUpBind); sort_orders.GetSignature() .AddParameter("list", LogicalType::LIST(LogicalType::ANY)) .AddParameter("sort_order", LogicalType::VARCHAR) .AddParameter("null_order", LogicalType::VARCHAR); + SetListSortSerialization(sort); + SetListSortSerialization(sort_order); + SetListSortSerialization(sort_orders); ScalarFunctionSet list_grade_up; list_grade_up.AddFunction(sort); @@ -421,6 +545,10 @@ ScalarFunctionSet ListReverseSortFun::GetFunctions() { sort_reverse_null_order.GetSignature() .AddParameter("list", LogicalType::LIST(LogicalType::ANY)) .AddParameter("null_order", LogicalType::VARCHAR); + SetListSortSerialization(sort_reverse); + sort_reverse.SetResolveTypesCallback(ListSortResolveTypes); + SetListSortSerialization(sort_reverse_null_order); + sort_reverse_null_order.SetResolveTypesCallback(ListSortResolveTypes); ScalarFunctionSet list_reverse_sort; list_reverse_sort.AddFunction(sort_reverse); diff --git a/src/duckdb/extension/core_functions/scalar/list/list_value.cpp b/src/duckdb/extension/core_functions/scalar/list/list_value.cpp index 7a4211fea..53256134b 100644 --- a/src/duckdb/extension/core_functions/scalar/list/list_value.cpp +++ b/src/duckdb/extension/core_functions/scalar/list/list_value.cpp @@ -237,7 +237,7 @@ void ListValueFunction(DataChunk &args, ExpressionState &state, Vector &result) ListVector::SetListSize(result, column_count * args.size()); } -unique_ptr UnpivotBind(BindScalarFunctionInput &input) { +void UnpivotResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &context = input.GetClientContext(); auto &bound_function = input.GetBoundFunction(); auto &arguments = input.GetArguments(); @@ -273,7 +273,6 @@ unique_ptr UnpivotBind(BindScalarFunctionInput &input) { function_args.push_back(child_type); } bound_function.SetReturnType(LogicalType::LIST(child_type)); - return make_uniq(bound_function.GetReturnType()); } unique_ptr ListValueStats(ClientContext &context, FunctionStatisticsInput &input) { @@ -292,7 +291,7 @@ unique_ptr ListValueStats(ClientContext &context, FunctionStatis //! The arguments are cast to the child type of the list at execution time - if any of those casts can throw the //! function is fallible -unique_ptr ListValueBind(BindScalarFunctionInput &input) { +void ListValueResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); auto &child_type = ListType::GetChildType(bound_function.GetReturnType()); for (auto &argument : input.GetArguments()) { @@ -302,7 +301,6 @@ unique_ptr ListValueBind(BindScalarFunctionInput &input) { break; } } - return nullptr; } ScalarFunctionSet ListValueFun::GetFunctions() { @@ -314,9 +312,11 @@ ScalarFunctionSet ListValueFun::GetFunctions() { // Overload for 1 + N arguments, which returns a list of the arguments. auto element_type = LogicalType::TEMPLATE("T"); - ScalarFunction value_fun({element_type}, LogicalType::LIST(element_type), ListValueFunction, ListValueBind, + ScalarFunction value_fun({element_type}, LogicalType::LIST(element_type), ListValueFunction, nullptr, ListValueStats); - value_fun.SetVarArgs(element_type); + value_fun.SetResolveTypesCallback(ListValueResolveTypes); + value_fun.GetSignature().GetParameter(0).SetName("value"); + value_fun.GetSignature().AddArgs("args", element_type); value_fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); set.AddFunction(value_fun); @@ -324,8 +324,10 @@ ScalarFunctionSet ListValueFun::GetFunctions() { } ScalarFunction UnpivotListFun::GetFunction() { - ScalarFunction fun("unpivot_list", {}, LogicalTypeId::LIST, ListValueFunction, UnpivotBind, ListValueStats); - fun.SetVarArgs(LogicalTypeId::ANY); + ScalarFunction fun("unpivot_list", {}, LogicalTypeId::LIST, ListValueFunction, VariableReturnBindData::Bind, + ListValueStats); + fun.SetResolveTypesCallback(UnpivotResolveTypes); + fun.GetSignature().AddArgs("args", LogicalTypeId::ANY); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); return fun; } diff --git a/src/duckdb/extension/core_functions/scalar/map/cardinality.cpp b/src/duckdb/extension/core_functions/scalar/map/cardinality.cpp index d9e7a3e7e..168d2ad88 100644 --- a/src/duckdb/extension/core_functions/scalar/map/cardinality.cpp +++ b/src/duckdb/extension/core_functions/scalar/map/cardinality.cpp @@ -3,9 +3,11 @@ #include "duckdb/common/string_util.hpp" #include "duckdb/common/types/data_chunk.hpp" #include "duckdb/function/scalar/nested_functions.hpp" +#include "duckdb/planner/expression/bound_cast_expression.hpp" namespace duckdb { +// the number of entries of a MAP or LIST (both are stored as a list of entries) static void CardinalityFunction(DataChunk &args, ExpressionState &state, Vector &result) { const auto &map = args.data[0]; auto entries = map.Values(); @@ -28,8 +30,19 @@ static unique_ptr CardinalityBind(BindScalarFunctionInput &input) throw BinderException("Cardinality must have exactly one arguments"); } - if (arguments[0]->GetReturnType().id() != LogicalTypeId::MAP) { - throw BinderException("Cardinality can only operate on MAPs"); + auto &context = input.GetClientContext(); + switch (arguments[0]->GetReturnType().id()) { + case LogicalTypeId::MAP: + case LogicalTypeId::LIST: + break; + case LogicalTypeId::ARRAY: { + // a fixed-size array is read as a list + auto target_type = LogicalType::LIST(ArrayType::GetChildType(arguments[0]->GetReturnType())); + arguments[0] = BoundCastExpression::AddCastToType(context, std::move(arguments[0]), target_type); + break; + } + default: + throw BinderException("Cardinality can only operate on MAPs, LISTs and ARRAYs"); } bound_function.SetReturnType(LogicalType::UBIGINT); @@ -39,7 +52,7 @@ static unique_ptr CardinalityBind(BindScalarFunctionInput &input) ScalarFunction CardinalityFun::GetFunction() { ScalarFunction fun({}, LogicalType::UBIGINT, CardinalityFunction, CardinalityBind); fun.GetSignature().AddParameter("map", LogicalType::ANY); - fun.SetVarArgs(LogicalType::ANY); + fun.GetSignature().AddArgs("args", LogicalType::ANY); fun.SetNullHandling(FunctionNullHandling::DEFAULT_NULL_HANDLING); return fun; } diff --git a/src/duckdb/extension/core_functions/scalar/map/map_concat.cpp b/src/duckdb/extension/core_functions/scalar/map/map_concat.cpp index 37dd202f3..1e2a5b963 100644 --- a/src/duckdb/extension/core_functions/scalar/map/map_concat.cpp +++ b/src/duckdb/extension/core_functions/scalar/map/map_concat.cpp @@ -185,7 +185,7 @@ ScalarFunction MapConcatFun::GetFunction() { //! the arguments and return types are actually set in the binder function ScalarFunction fun("map_concat", {}, LogicalTypeId::LIST, MapConcatFunction, MapConcatBind); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); - fun.SetVarArgs(LogicalType::ANY); + fun.GetSignature().AddArgs("args", LogicalType::ANY); return fun; } diff --git a/src/duckdb/extension/core_functions/scalar/map/switch.cpp b/src/duckdb/extension/core_functions/scalar/map/switch.cpp index 9513cd7ad..1eec73243 100644 --- a/src/duckdb/extension/core_functions/scalar/map/switch.cpp +++ b/src/duckdb/extension/core_functions/scalar/map/switch.cpp @@ -9,26 +9,24 @@ namespace duckdb { namespace { struct SwitchFunctionBindData : FunctionData { - explicit SwitchFunctionBindData(const LogicalType &return_type_p, idx_t map_index_p) - : return_type(return_type_p), map_index(map_index_p) { + SwitchFunctionBindData(Value cases_p, idx_t map_index_p) : cases(std::move(cases_p)), map_index(map_index_p) { } - LogicalType return_type; + //! The constant MAP of (WHEN, THEN) pairs + Value cases; idx_t map_index; + const LogicalType &ReturnType() const { + return MapType::ValueType(cases.type()); + } + bool Equals(const FunctionData &other_p) const override { const auto &other = other_p.Cast(); - if (return_type != other.return_type) { - return false; - } - if (map_index != other.map_index) { - return false; - } - return true; + return cases == other.cases && map_index == other.map_index; } unique_ptr Copy() const override { - return make_uniq(return_type, map_index); + return make_uniq(cases, map_index); } }; @@ -40,41 +38,26 @@ unique_ptr SwitchBindReturnType(BindScalarFunctionInput &input) { auto &arguments = input.GetArguments(); constexpr idx_t map_index = MAP_INDEX; D_ASSERT(map_index < arguments.size()); - auto &cases = arguments[map_index]; - if (cases->GetExpressionClass() != ExpressionClass::BOUND_FUNCTION) { + // the cases are a constant MAP - usually folded to a constant by the function binder already, otherwise a + // foldable expression (including the cast to the argument type of the function) that is evaluated here + auto &cases = *arguments[map_index]; + if (!cases.IsFoldable()) { throw BinderException("SWITCH expected a constant map for the cases"); } - auto &func = cases->Cast(); - if (func.Function().GetName() != "map" || !cases->IsFoldable()) { + auto map_value = ExpressionExecutor::EvaluateScalar(context, cases); + if (map_value.IsNull()) { throw BinderException("SWITCH expected a constant map for the cases"); } - auto map_value = ExpressionExecutor::EvaluateScalar(context, *cases); - auto values_type = MapType::ValueType(map_value.type()); - return make_uniq(values_type, map_index); -} - -void ExtractConstantExprFromList(unique_ptr &expr, vector> &result) { - if (expr->GetExpressionClass() != ExpressionClass::BOUND_FUNCTION) { - throw BinderException("Expected a function for the cases"); - } - auto &list_function = expr->Cast(); - if (list_function.Function().GetName() != "list_value") { - throw BinderException("Expected a list function"); - } - if (list_function.GetChildren().empty()) { + if (MapValue::GetChildren(map_value).empty()) { throw BinderException("No values provided for SWITCH expression"); } - for (auto &list_child : list_function.GetChildrenMutable()) { - if (list_child->GetExpressionClass() != ExpressionClass::BOUND_CONSTANT) { - throw NotImplementedException("Only constant expressions are supported for keys inside SWITCH"); - } - result.push_back(std::move(list_child)); - } + return make_uniq(std::move(map_value), map_index); } unique_ptr SwitchBindExpression(FunctionBindExpressionInput &input) { - auto function_data = input.bind_data->Cast(); - auto result = make_uniq(function_data.return_type); + auto &function_data = input.bind_data->Cast(); + auto &return_type = function_data.ReturnType(); + auto result = make_uniq(return_type); idx_t map_index = function_data.map_index; unique_ptr base_expr = nullptr; unique_ptr default_expr = nullptr; @@ -87,54 +70,33 @@ unique_ptr SwitchBindExpression(FunctionBindExpressionInput &input) // If there is an argument after the map_index, we have a default expression default_expr = std::move(input.children[map_index + 1]); } - unique_ptr cases; - if (BoundCastExpression::IsCast(*input.children[map_index])) { - auto &cast_expr = input.children[map_index]->Cast(); - if (BoundCastExpression::Child(cast_expr).GetExpressionClass() != ExpressionClass::BOUND_FUNCTION) { - throw BinderException("SWITCH expected a map function for the cases"); - } - cases = std::move(BoundCastExpression::ChildMutable(cast_expr)); - } else if (input.children[map_index]->GetExpressionClass() == ExpressionClass::BOUND_FUNCTION) { - cases = std::move(input.children[map_index]); - } else { - throw BinderException("SWITCH expected a map function for the cases"); - } - auto &cases_func = cases->Cast(); - D_ASSERT(cases_func.GetChildren().size() == 2); - - vector> keys_unpacked; - vector> values_unpacked; - ExtractConstantExprFromList(cases_func.GetChildrenMutable()[0], keys_unpacked); - ExtractConstantExprFromList(cases_func.GetChildrenMutable()[1], values_unpacked); - result->CaseChecksMutable().reserve(keys_unpacked.size()); - for (idx_t i = 0; i < keys_unpacked.size(); i++) { + // every entry of the cases map becomes a WHEN key THEN value check + auto &entries = MapValue::GetChildren(function_data.cases); + result->CaseChecksMutable().reserve(entries.size()); + for (auto &entry : entries) { + auto &key_value = StructValue::GetChildren(entry)[0]; + auto &then_value = StructValue::GetChildren(entry)[1]; + auto key = make_uniq(key_value); BoundCaseCheck case_check; if (base_expr) { - auto max_type = LogicalType::MaxLogicalType(input.context, base_expr->GetReturnType(), - keys_unpacked[i]->GetReturnType()); + auto max_type = + LogicalType::MaxLogicalType(input.context, base_expr->GetReturnType(), key->GetReturnType()); case_check.when_expr = BoundComparisonExpression::Create( ExpressionType::COMPARE_EQUAL, base_expr->Copy(), - BoundCastExpression::AddCastToType(input.context, std::move(keys_unpacked[i]), max_type)); + BoundCastExpression::AddCastToType(input.context, std::move(key), max_type)); } else { case_check.when_expr = - BoundCastExpression::AddCastToType(input.context, std::move(keys_unpacked[i]), LogicalType::BOOLEAN); - } - auto then_type = values_unpacked[i]->GetReturnType(); - if (!LogicalType::TryGetMaxLogicalType(input.context, function_data.return_type, then_type, - function_data.return_type)) { - throw BinderException( - "Cannot mix values of type %s and %s in CASE expression - an explicit cast is required", - function_data.return_type.ToString(), then_type.ToString()); + BoundCastExpression::AddCastToType(input.context, std::move(key), LogicalType::BOOLEAN); } - case_check.then_expr = std::move(values_unpacked[i]); + case_check.then_expr = make_uniq(then_value); result->CaseChecksMutable().push_back(std::move(case_check)); } if (default_expr) { result->ElseMutable() = std::move(default_expr); } else { - result->ElseMutable() = BoundCastExpression::AddCastToType( - input.context, make_uniq(Value()), function_data.return_type); + result->ElseMutable() = + BoundCastExpression::AddCastToType(input.context, make_uniq(Value()), return_type); } return std::move(result); } @@ -160,6 +122,8 @@ ScalarFunctionSet SwitchFun::GetFunctions() { switch_expression.GetSignature().AddParameter(param.first, param.second); } switch_expression.SetBindExpressionCallback(SwitchBindExpression); + // switch is rewritten into a CASE expression, so NULL arguments do not make the result NULL + switch_expression.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); func_set.AddFunction(std::move(switch_expression)); } diff --git a/src/duckdb/extension/core_functions/scalar/math/numeric.cpp b/src/duckdb/extension/core_functions/scalar/math/numeric.cpp index fbb116718..a69ceeede 100644 --- a/src/duckdb/extension/core_functions/scalar/math/numeric.cpp +++ b/src/duckdb/extension/core_functions/scalar/math/numeric.cpp @@ -223,10 +223,9 @@ static unique_ptr PropagateAbsStats(ClientContext &context, Func } template -static unique_ptr DecimalUnaryOpBind(BindScalarFunctionInput &input) { +static void DecimalUnaryOpResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - auto decimal_type = arguments[0]->GetReturnType(); + auto &decimal_type = input.GetArgumentType(0); switch (decimal_type.InternalType()) { case PhysicalType::INT16: bound_function.SetFunctionCallback(ScalarFunction::GetScalarUnaryFunction(LogicalTypeId::SMALLINT)); @@ -243,16 +242,18 @@ static unique_ptr DecimalUnaryOpBind(BindScalarFunctionInput &inpu } bound_function.GetArguments()[0] = decimal_type; bound_function.SetReturnType(decimal_type); - return nullptr; } ScalarFunctionSet AbsOperatorFun::GetFunctions() { ScalarFunctionSet abs; for (auto &type : LogicalType::Numeric()) { switch (type.id()) { - case LogicalTypeId::DECIMAL: - abs.AddFunction(NameXArgument(ScalarFunction({}, type, nullptr, DecimalUnaryOpBind), type)); + case LogicalTypeId::DECIMAL: { + ScalarFunction function({}, type, nullptr); + function.SetResolveTypesCallback(DecimalUnaryOpResolveTypes); + abs.AddFunction(NameXArgument(std::move(function), type)); break; + } case LogicalTypeId::TINYINT: case LogicalTypeId::SMALLINT: case LogicalTypeId::INTEGER: @@ -514,11 +515,10 @@ static void GenericRoundFunctionDecimal(DataChunk &input, ExpressionState &state } template -static unique_ptr BindGenericRoundFunctionDecimal(BindScalarFunctionInput &input) { +static void GenericRoundFunctionDecimalResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); // ceil essentially removes the scale - auto &decimal_type = arguments[0]->GetReturnType(); + auto &decimal_type = input.GetArgumentType(0); auto scale = DecimalType::GetScale(decimal_type); auto width = DecimalType::GetWidth(decimal_type); if (scale == 0) { @@ -541,7 +541,6 @@ static unique_ptr BindGenericRoundFunctionDecimal(BindScalarFuncti } bound_function.GetArguments()[0] = decimal_type; bound_function.SetReturnType(LogicalType::DECIMAL(width, 0)); - return nullptr; } namespace { @@ -566,7 +565,7 @@ ScalarFunctionSet CeilFun::GetFunctions() { ScalarFunctionSet ceil; for (auto &type : LogicalType::Numeric()) { scalar_function_t func = nullptr; - bind_scalar_function_t bind_func = nullptr; + resolve_scalar_types_t resolve_func = nullptr; if (type.IsIntegral()) { // no ceil for integral numbers continue; @@ -579,12 +578,14 @@ ScalarFunctionSet CeilFun::GetFunctions() { func = ScalarFunction::UnaryFunction; break; case LogicalTypeId::DECIMAL: - bind_func = BindGenericRoundFunctionDecimal; + resolve_func = GenericRoundFunctionDecimalResolveTypes; break; default: throw InternalException("Unimplemented numeric type for function \"ceil\""); } - ceil.AddFunction(NameXArgument(ScalarFunction({}, type, func, bind_func), type)); + ScalarFunction ceil_function({}, type, func); + ceil_function.SetResolveTypesCallback(resolve_func); + ceil.AddFunction(NameXArgument(std::move(ceil_function), type)); } ceil.SetUnaryArgProperties(ArgProperties().NonDecreasing()); return ceil; @@ -622,7 +623,7 @@ ScalarFunctionSet FloorFun::GetFunctions() { ScalarFunctionSet floor; for (auto &type : LogicalType::Numeric()) { scalar_function_t func = nullptr; - bind_scalar_function_t bind_func = nullptr; + resolve_scalar_types_t resolve_func = nullptr; if (type.IsIntegral()) { // no floor for integral numbers continue; @@ -635,12 +636,14 @@ ScalarFunctionSet FloorFun::GetFunctions() { func = ScalarFunction::UnaryFunction; break; case LogicalTypeId::DECIMAL: - bind_func = BindGenericRoundFunctionDecimal; + resolve_func = GenericRoundFunctionDecimalResolveTypes; break; default: throw InternalException("Unimplemented numeric type for function \"floor\""); } - floor.AddFunction(NameXArgument(ScalarFunction({}, type, func, bind_func), type)); + ScalarFunction floor_function({}, type, func); + floor_function.SetResolveTypesCallback(resolve_func); + floor.AddFunction(NameXArgument(std::move(floor_function), type)); } floor.SetUnaryArgProperties(ArgProperties().NonDecreasing()); return floor; @@ -676,18 +679,15 @@ void GenericRoundPrecisionDecimal(DataChunk &input, ExpressionState &state, Vect OP::template Operation(input, state, result); } -template -unique_ptr BindDecimalRoundPrecision(BindScalarFunctionInput &input) { - auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - auto &decimal_type = arguments[0]->GetReturnType(); - auto val = input.GetNonNullConstant(1).DefaultCastAs(LogicalType::INTEGER); +//! Resolves the types and the function of a decimal round with a precision, given the original decimal type +template +unique_ptr ResolveDecimalRoundPrecision(BoundScalarFunction &bound_function, + const LogicalType &decimal_type, int32_t round_value) { // our new precision becomes the round value // e.g. ROUND(DECIMAL(18,3), 1) -> DECIMAL(18,1) // but ONLY if the round value is positive // if it is negative the scale becomes zero // i.e. ROUND(DECIMAL(18,3), -1) -> DECIMAL(18,0) - int32_t round_value = IntegerValue::Get(val); uint8_t target_scale; auto width = DecimalType::GetWidth(decimal_type); auto scale = DecimalType::GetScale(decimal_type); @@ -749,6 +749,22 @@ unique_ptr BindDecimalRoundPrecision(BindScalarFunctionInput &inpu return make_uniq(round_value, width, check_overflow); } +template +void DecimalRoundPrecisionResolveTypes(ResolveScalarFunctionTypesInput &input) { + auto val = input.GetNonNullConstant(1).DefaultCastAs(LogicalType::INTEGER); + ResolveDecimalRoundPrecision(input.GetBoundFunction(), input.GetArgumentType(0), + IntegerValue::Get(val)); +} + +//! The bind data is derived from the type the function was called with, before it was cast +template +unique_ptr BindDecimalRoundPrecision(BindScalarFunctionInput &input) { + auto &bound_function = input.GetBoundFunction(); + auto val = input.GetNonNullConstant(1).DefaultCastAs(LogicalType::INTEGER); + return ResolveDecimalRoundPrecision( + bound_function, bound_function.GetLogicalArguments()[0], IntegerValue::Get(val)); +} + struct TruncOperatorPrecision { template static inline TR Operation(TA input, TB precision) { @@ -855,8 +871,9 @@ ScalarFunctionSet TruncFun::GetFunctions() { for (auto &type : LogicalType::Numeric()) { scalar_function_t trunc_func = nullptr; scalar_function_t trunc_prec_func = nullptr; - bind_scalar_function_t bind_func = nullptr; + resolve_scalar_types_t resolve_func = nullptr; bind_scalar_function_t bind_prec_func = nullptr; + resolve_scalar_types_t resolve_prec_func = nullptr; // Truncation of integers gets generated by some tools (e.g., Tableau/JDBC:Postgres) switch (type.id()) { case LogicalTypeId::FLOAT: @@ -868,9 +885,11 @@ ScalarFunctionSet TruncFun::GetFunctions() { trunc_prec_func = ScalarFunction::BinaryFunction; break; case LogicalTypeId::DECIMAL: - bind_func = BindGenericRoundFunctionDecimal; + resolve_func = GenericRoundFunctionDecimalResolveTypes; bind_prec_func = BindDecimalRoundPrecision; + resolve_prec_func = DecimalRoundPrecisionResolveTypes; break; case LogicalTypeId::TINYINT: trunc_func = ScalarFunction::NopFunction; @@ -915,8 +934,12 @@ ScalarFunctionSet TruncFun::GetFunctions() { default: throw InternalException("Unimplemented numeric type for function \"trunc\""); } - trunc.AddFunction(NameXArgument(ScalarFunction({}, type, trunc_func, bind_func), type)); - trunc.AddFunction(NameXPrecisionArguments(ScalarFunction({}, type, trunc_prec_func, bind_prec_func), type)); + ScalarFunction trunc_function({}, type, trunc_func); + trunc_function.SetResolveTypesCallback(resolve_func); + trunc.AddFunction(NameXArgument(std::move(trunc_function), type)); + ScalarFunction trunc_prec_function({}, type, trunc_prec_func, bind_prec_func); + trunc_prec_function.SetResolveTypesCallback(resolve_prec_func); + trunc.AddFunction(NameXPrecisionArguments(std::move(trunc_prec_function), type)); } trunc.SetUnaryArgProperties(ArgProperties().NonDecreasing()); return trunc; @@ -982,9 +1005,14 @@ struct RoundOperatorPrecision { double rounded_value; if (precision < 0) { double modifier = std::pow(10, -TA(precision)); + if (std::isinf(modifier)) { + // every finite value rounds to zero + return Value::IsFinite(input) ? 0 : input; + } rounded_value = ROUND_POLICY::Nearest(input / modifier) * modifier; if (std::isinf(rounded_value) || std::isnan(rounded_value)) { - return 0; + // the input is too large to be rounded to this precision + return input; } } else { double modifier = std::pow(10, TA(precision)); @@ -1126,8 +1154,9 @@ ScalarFunctionSet RoundFun::GetFunctions() { for (auto &type : LogicalType::Numeric()) { scalar_function_t round_prec_func = nullptr; scalar_function_t round_func = nullptr; - bind_scalar_function_t bind_func = nullptr; + resolve_scalar_types_t resolve_func = nullptr; bind_scalar_function_t bind_prec_func = nullptr; + resolve_scalar_types_t resolve_prec_func = nullptr; switch (type.id()) { case LogicalTypeId::FLOAT: round_func = ScalarFunction::UnaryFunction; @@ -1140,10 +1169,13 @@ ScalarFunctionSet RoundFun::GetFunctions() { ScalarFunction::BinaryFunction>; break; case LogicalTypeId::DECIMAL: - bind_func = BindGenericRoundFunctionDecimal; + resolve_func = GenericRoundFunctionDecimalResolveTypes; bind_prec_func = BindDecimalRoundPrecision, DecimalRoundPositivePrecisionOperator, true>; + resolve_prec_func = + DecimalRoundPrecisionResolveTypes, + DecimalRoundPositivePrecisionOperator, true>; break; case LogicalTypeId::TINYINT: round_func = ScalarFunction::NopFunction; @@ -1177,9 +1209,12 @@ ScalarFunctionSet RoundFun::GetFunctions() { } throw InternalException("Unimplemented numeric type for function \"round\""); } - auto round_function = NameXArgument(ScalarFunction({}, type, round_func, bind_func), type); - auto round_prec_function = - NameXPrecisionArguments(ScalarFunction({}, type, round_prec_func, bind_prec_func), type); + ScalarFunction round_unary_function({}, type, round_func); + round_unary_function.SetResolveTypesCallback(resolve_func); + auto round_function = NameXArgument(std::move(round_unary_function), type); + ScalarFunction round_binary_function({}, type, round_prec_func, bind_prec_func); + round_binary_function.SetResolveTypesCallback(resolve_prec_func); + auto round_prec_function = NameXPrecisionArguments(std::move(round_binary_function), type); if (type.id() == LogicalTypeId::DECIMAL) { // rounding a DECIMAL can overflow round_function.SetFallible(); @@ -1203,6 +1238,7 @@ ScalarFunctionSet RoundEvenFun::GetFunctions() { for (auto &type : LogicalType::Numeric()) { scalar_function_t round_prec_func = nullptr; bind_scalar_function_t bind_prec_func = nullptr; + resolve_scalar_types_t resolve_prec_func = nullptr; switch (type.id()) { case LogicalTypeId::FLOAT: round_prec_func = @@ -1215,6 +1251,9 @@ ScalarFunctionSet RoundEvenFun::GetFunctions() { case LogicalTypeId::DECIMAL: bind_prec_func = BindDecimalRoundPrecision, DecimalRoundPositivePrecisionOperator, true>; + resolve_prec_func = + DecimalRoundPrecisionResolveTypes, + DecimalRoundPositivePrecisionOperator, true>; break; case LogicalTypeId::TINYINT: round_prec_func = @@ -1243,8 +1282,9 @@ ScalarFunctionSet RoundEvenFun::GetFunctions() { } throw InternalException("Unimplemented numeric type for function \"round_even\""); } - auto round_even_function = - NameXPrecisionArguments(ScalarFunction({}, type, round_prec_func, bind_prec_func), type); + ScalarFunction round_even_binary_function({}, type, round_prec_func, bind_prec_func); + round_even_binary_function.SetResolveTypesCallback(resolve_prec_func); + auto round_even_function = NameXPrecisionArguments(std::move(round_even_binary_function), type); if (type.id() == LogicalTypeId::DECIMAL) { // rounding a DECIMAL can overflow round_even_function.SetFallible(); @@ -1273,11 +1313,10 @@ struct ExpOperator { } // namespace ScalarFunction ExpFun::GetFunction() { - auto func = NameXArgument( + // not annotated monotone: libm exp is not monotone on every platform (Apple libm inverts adjacent doubles) + return NameXArgument( ScalarFunction({}, LogicalType::DOUBLE, ScalarFunction::UnaryFunction), LogicalType::DOUBLE); - func.SetUnaryArgProperties(ArgProperties().NonDecreasing()); - return func; } //===--------------------------------------------------------------------===// diff --git a/src/duckdb/extension/core_functions/scalar/string/instr.cpp b/src/duckdb/extension/core_functions/scalar/string/instr.cpp index ab8b9c392..58c66873b 100644 --- a/src/duckdb/extension/core_functions/scalar/string/instr.cpp +++ b/src/duckdb/extension/core_functions/scalar/string/instr.cpp @@ -4,7 +4,6 @@ #include "duckdb/common/vector_operations/vector_operations.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" #include "duckdb/function/scalar/string_common.hpp" -#include "utf8proc.hpp" namespace duckdb { @@ -15,15 +14,13 @@ struct InstrOperator { auto location = FindStrInStr(haystack, needle); if (location != DConstants::INVALID_INDEX) { - auto len = (utf8proc_ssize_t)location; - auto str = reinterpret_cast(haystack.GetData()); - D_ASSERT(len <= (utf8proc_ssize_t)haystack.GetSize()); - for (++string_position; len > 0; ++string_position) { - utf8proc_int32_t codepoint; - auto bytes = utf8proc_iterate(str, len, &codepoint); - str += bytes; - len -= bytes; + auto str = haystack.GetData(); + D_ASSERT(location <= haystack.GetSize()); + for (idx_t pos = 0; pos < location; ++string_position) { + int32_t codepoint; + pos += DecodeCodepoint(str + pos, location - pos, codepoint); } + ++string_position; } return string_position; } diff --git a/src/duckdb/extension/core_functions/scalar/string/pad.cpp b/src/duckdb/extension/core_functions/scalar/string/pad.cpp index 58896086e..36832fcb8 100644 --- a/src/duckdb/extension/core_functions/scalar/string/pad.cpp +++ b/src/duckdb/extension/core_functions/scalar/string/pad.cpp @@ -6,25 +6,44 @@ #include "duckdb/common/vector_operations/ternary_executor.hpp" #include "duckdb/common/pair.hpp" -#include "utf8proc.hpp" +#include "duckdb/function/scalar/string_common.hpp" namespace duckdb { static pair PadCountChars(const idx_t len, const char *data, const idx_t size) { // Count how much of str will fit in the output - auto str = reinterpret_cast(data); idx_t nbytes = 0; idx_t nchars = 0; for (; nchars < len && nbytes < size; ++nchars) { - utf8proc_int32_t codepoint; - auto bytes = utf8proc_iterate(str + nbytes, UnsafeNumericCast(size - nbytes), &codepoint); - D_ASSERT(bytes > 0); - nbytes += UnsafeNumericCast(bytes); + int32_t codepoint; + nbytes += DecodeCodepoint(data + nbytes, size - nbytes, codepoint); } return pair(nbytes, nchars); } +static idx_t PaddingByteCount(const idx_t len, const string_t &pad) { + // Count the bytes InsertPadding will write + auto data = pad.GetData(); + auto size = pad.GetSize(); + if (len == 0 || size == 0) { + return 0; + } + auto pad_chars = PadCountChars(NumericLimits::Maximum(), data, size).second; + auto remainder = PadCountChars(len % pad_chars, data, size).first; + return (len / pad_chars) * size + remainder; +} + +static void CheckPadResultSize(const idx_t len, const string_t &pad, const pair &written, + const char *name) { + auto result_size = written.first + PaddingByteCount(len - written.second, pad); + if (result_size > string_t::MAX_STRING_SIZE) { + throw OutOfRangeException( + "Cannot create a string of size: '%d' in %s, the maximum supported string size is: '%d'", result_size, name, + string_t::MAX_STRING_SIZE); + } +} + static bool InsertPadding(const idx_t len, const string_t &pad, vector &result) { // Copy the padding until the output is long enough auto data = pad.GetData(); @@ -36,7 +55,6 @@ static bool InsertPadding(const idx_t len, const string_t &pad, vector &re } // Insert characters until we have all we need. - auto str = reinterpret_cast(data); idx_t nbytes = 0; for (idx_t nchars = 0; nchars < len; ++nchars) { // If we are at the end of the pad, flush all of it and loop back @@ -46,10 +64,8 @@ static bool InsertPadding(const idx_t len, const string_t &pad, vector &re } // Write the next character - utf8proc_int32_t codepoint; - auto bytes = utf8proc_iterate(str + nbytes, UnsafeNumericCast(size - nbytes), &codepoint); - D_ASSERT(bytes > 0); - nbytes += UnsafeNumericCast(bytes); + int32_t codepoint; + nbytes += DecodeCodepoint(data + nbytes, size - nbytes, codepoint); } // Flush the remaining pad @@ -68,6 +84,7 @@ static string_t LeftPadFunction(const string_t &str, const int32_t len, const st // Count how much of str will fit in the output auto written = PadCountChars(UnsafeNumericCast(len), data_str, size_str); + CheckPadResultSize(UnsafeNumericCast(len), pad, written, "LPAD"); // Left pad by the number of characters still needed if (!InsertPadding(UnsafeNumericCast(len) - written.second, pad, result)) { @@ -97,6 +114,7 @@ static string_t RightPadFunction(const string_t &str, const int32_t len, const s // Count how much of str will fit in the output auto written = PadCountChars(UnsafeNumericCast(len), data_str, size_str); + CheckPadResultSize(UnsafeNumericCast(len), pad, written, "RPAD"); // Append as much of the original string as fits result.insert(result.end(), data_str, data_str + written.first); diff --git a/src/duckdb/extension/core_functions/scalar/string/parse_path.cpp b/src/duckdb/extension/core_functions/scalar/string/parse_path.cpp index 3005158fe..4abc7d07c 100644 --- a/src/duckdb/extension/core_functions/scalar/string/parse_path.cpp +++ b/src/duckdb/extension/core_functions/scalar/string/parse_path.cpp @@ -1,4 +1,6 @@ #include "duckdb/common/vector/flat_vector.hpp" +#include "duckdb/common/vector/vector_iterator.hpp" +#include "duckdb/common/vector/vector_writer.hpp" #include "core_functions/scalar/string_functions.hpp" #include "duckdb/function/scalar/string_common.hpp" #include "duckdb/common/local_file_system.hpp" @@ -104,157 +106,144 @@ static idx_t SplitPath(string_t input, const string &sep, CB &&emit) { return list_idx; } -static void ReadOptionalArgs(DataChunk &args, Vector &sep, Vector &trim, const bool &front_trim) { +//! The optional separator and trim_extension arguments - NULL values use the default options +struct PathOptionArguments { + optional_ptr separator; + optional_ptr trim_extension; +}; + +static PathOptionArguments GetPathOptionArguments(DataChunk &args, const bool front_trim) { + PathOptionArguments result; switch (args.ColumnCount()) { - case 1: { + case 1: // use default values break; - } - case 2: { - UnifiedVectorFormat sec_arg; - args.data[1].ToUnifiedFormat(sec_arg); - if (sec_arg.validity.RowIsValid(0)) { // if not NULL - switch (args.data[1].GetType().id()) { - case LogicalTypeId::VARCHAR: { - sep.Reinterpret(args.data[1]); - break; - } - case LogicalTypeId::BOOLEAN: { // parse_path and parse_driname won't get in here - trim.Reinterpret(args.data[1]); - break; - } - default: - throw InvalidInputException("Invalid argument type"); - } + case 2: + switch (args.data[1].GetType().id()) { + case LogicalTypeId::VARCHAR: + result.separator = args.data[1]; + break; + case LogicalTypeId::BOOLEAN: // parse_path and parse_dirname won't get in here + result.trim_extension = args.data[1]; + break; + default: + throw InvalidInputException("Invalid argument type"); } break; - } - case 3: { - if (!front_trim) { - // set trim_extension - UnifiedVectorFormat sec_arg; - args.data[1].ToUnifiedFormat(sec_arg); - if (sec_arg.validity.RowIsValid(0)) { - trim.Reinterpret(args.data[1]); - } - UnifiedVectorFormat third_arg; - args.data[2].ToUnifiedFormat(third_arg); - if (third_arg.validity.RowIsValid(0)) { - sep.Reinterpret(args.data[2]); - } - } else { + case 3: + if (front_trim) { throw InvalidInputException("Invalid number of arguments"); } + result.trim_extension = args.data[1]; + result.separator = args.data[2]; break; - } default: throw InvalidInputException("Invalid number of arguments"); } + return result; +} + +template +static void ExecutePathFunction(DataChunk &args, Vector &result, const bool front_trim, OP &&op) { + const auto count = args.size(); + auto options = GetPathOptionArguments(args, front_trim); + Vector default_separator(string_t("default"), count_t(count)); + Vector default_trim_extension(Value::BOOLEAN(false), count_t(count)); + auto paths = args.data[0].Values(); + auto separators = (options.separator ? *options.separator : default_separator).Values(); + auto trim_extensions = (options.trim_extension ? *options.trim_extension : default_trim_extension).Values(); + + auto writer = FlatVector::Writer(result, count); + for (idx_t i = 0; i < count; i++) { + auto path = paths[i]; + if (!path.IsValid()) { + writer.WriteNull(); + continue; + } + auto separator = separators[i]; + auto sep = GetSeparator(separator.IsValid() ? separator.GetValue() : string_t("default")); + auto trim_extension = trim_extensions[i]; + writer.WriteValue(op(path.GetValue(), sep, trim_extension.IsValid() && trim_extension.GetValue())); + } } template static void TrimPathFunction(DataChunk &args, ExpressionState &state, Vector &result) { - // set default values - const Vector &path = args.data[0]; - Vector separator(string_t("default"), count_t(args.size())); - Vector trim_extension(Value::BOOLEAN(false), count_t(args.size())); - ReadOptionalArgs(args, separator, trim_extension, FRONT_TRIM); - - TernaryExecutor::Execute( - path, separator, trim_extension, result, [&](string_t &inputs, string_t input_sep, bool trim_extension) { - auto data = inputs.GetData(); - auto input_size = inputs.GetSize(); - auto sep = GetSeparator(input_sep.GetString()); - - // find the beginning idx and the size of the result string - idx_t begin = 0; - idx_t new_size = input_size; - if (FRONT_TRIM) { // left trim - auto pos = Find(data, input_size, sep); - if (pos == 0) { // path starts with separator - pos = 1; - } - new_size = (IsIdxValid(pos, input_size)) ? pos : 0; - } else { // right trim - auto idx_last_sep = FindLast(data, input_size, sep); - if (IsIdxValid(idx_last_sep, input_size)) { - begin = idx_last_sep + 1; - } - if (trim_extension) { - auto idx_extension_sep = FindLast(data, input_size, "."); - if (begin <= idx_extension_sep && IsIdxValid(idx_extension_sep, input_size)) { - new_size = idx_extension_sep; - } - } - } - // copy the trimmed string - D_ASSERT(begin <= new_size); - auto target = StringVector::EmptyString(result, new_size - begin); - auto output = target.GetDataWriteable(); - memcpy(output, data + begin, new_size - begin); - - target.Finalize(); - return target; - }); + ExecutePathFunction(args, result, FRONT_TRIM, [&](const string_t &input, const string &sep, bool trim_extension) { + auto data = input.GetData(); + auto input_size = input.GetSize(); + + // find the beginning idx and the size of the result string + idx_t begin = 0; + idx_t new_size = input_size; + if (FRONT_TRIM) { // left trim + auto pos = Find(data, input_size, sep); + if (pos == 0) { // path starts with separator + pos = 1; + } + new_size = (IsIdxValid(pos, input_size)) ? pos : 0; + } else { // right trim + auto idx_last_sep = FindLast(data, input_size, sep); + if (IsIdxValid(idx_last_sep, input_size)) { + begin = idx_last_sep + 1; + } + if (trim_extension) { + auto idx_extension_sep = FindLast(data, input_size, "."); + if (begin <= idx_extension_sep && IsIdxValid(idx_extension_sep, input_size)) { + new_size = idx_extension_sep; + } + } + } + D_ASSERT(begin <= new_size); + return string_t(data + begin, UnsafeNumericCast(new_size - begin)); + }); } static void ParseDirpathFunction(DataChunk &args, ExpressionState &state, Vector &result) { - // set default values - const Vector &path = args.data[0]; - Vector separator(string_t("default"), count_t(args.size())); - Vector trim_extension(Value::BOOLEAN(false), count_t(args.size())); - ReadOptionalArgs(args, separator, trim_extension, true); - - auto &heap = StringVector::GetStringHeap(result); - BinaryExecutor::Execute( - path, separator, result, [&](string_t input_path, string_t input_sep) { - auto path = input_path.GetData(); - auto path_size = input_path.GetSize(); - auto sep = GetSeparator(input_sep.GetString()); + ExecutePathFunction(args, result, true, [&](const string_t &input, const string &sep, bool) { + auto path = input.GetData(); + auto path_size = input.GetSize(); - auto last_sep = FindLast(path, path_size, sep); - if (last_sep == 0 && path_size == 1) { - last_sep = 1; - } - idx_t new_size = (IsIdxValid(last_sep, path_size)) ? last_sep : 0; - - auto target = heap.EmptyString(new_size); - auto output = target.GetDataWriteable(); - memcpy(output, path, new_size); - target.Finalize(); - return target; - }); + auto last_sep = FindLast(path, path_size, sep); + if (last_sep == 0 && path_size == 1) { + last_sep = 1; + } + idx_t new_size = (IsIdxValid(last_sep, path_size)) ? last_sep : 0; + return string_t(path, UnsafeNumericCast(new_size)); + }); } static void ParsePathFunction(DataChunk &args, ExpressionState &state, Vector &result) { D_ASSERT(args.ColumnCount() == 1 || args.ColumnCount() == 2); - UnifiedVectorFormat input_data; - args.data[0].ToUnifiedFormat(input_data); - auto inputs = UnifiedVectorFormat::GetData(input_data); - - // set the separator - string input_sep = "default"; - if (args.ColumnCount() == 2) { - UnifiedVectorFormat sep_data; - args.data[1].ToUnifiedFormat(sep_data); - if (sep_data.validity.RowIsValid(0)) { - input_sep = UnifiedVectorFormat::GetData(sep_data)->GetString(); - } + auto inputs = args.data[0].Values(); + Vector default_separator(string_t("default"), count_t(args.size())); + const auto &separator = args.ColumnCount() == 2 ? args.data[1] : default_separator; + auto separator_values = separator.Values(); + const auto constant_separator = separator.GetVectorType() == VectorType::CONSTANT_VECTOR; + auto read_separator = [&](idx_t row_idx) { + auto entry = separator_values[row_idx]; + return GetSeparator(entry.IsValid() ? entry.GetValue() : string_t("default")); + }; + string sep; + if (constant_separator) { + sep = read_separator(0); } - const string sep = GetSeparator(input_sep); D_ASSERT(result.GetType().id() == LogicalTypeId::LIST); result.SetVectorType(VectorType::FLAT_VECTOR); auto list_writer = FlatVector::Writer>(result, args.size()); for (idx_t i = 0; i < args.size(); i++) { - auto input_idx = input_data.sel->get_index(i); - if (!input_data.validity.RowIsValid(input_idx)) { + auto input = inputs[i]; + if (!input.IsValid()) { list_writer.WriteNull(); continue; } + if (!constant_separator) { + sep = read_separator(i); + } auto list = list_writer.WriteDynamicList(); - SplitPath(inputs[input_idx], sep, [&](const char *split_data, idx_t split_size) { + SplitPath(input.GetValue(), sep, [&](const char *split_data, idx_t split_size) { list.WriteElement().WriteValue(string_t(split_data, UnsafeNumericCast(split_size))); }); } diff --git a/src/duckdb/extension/core_functions/scalar/string/printf.cpp b/src/duckdb/extension/core_functions/scalar/string/printf.cpp index c20ee1951..ac89c8ce0 100644 --- a/src/duckdb/extension/core_functions/scalar/string/printf.cpp +++ b/src/duckdb/extension/core_functions/scalar/string/printf.cpp @@ -23,11 +23,10 @@ struct FMTFormat { } }; -static unique_ptr BindPrintfFunction(BindScalarFunctionInput &input) { +static void PrintfResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - for (idx_t i = 1; i < arguments.size(); i++) { - switch (arguments[i]->GetReturnType().id()) { + for (idx_t i = 1; i < input.GetArgumentCount(); i++) { + switch (input.GetArgumentType(i).id()) { case LogicalTypeId::BOOLEAN: bound_function.GetArguments()[i] = LogicalType::BOOLEAN; break; @@ -70,7 +69,6 @@ static unique_ptr BindPrintfFunction(BindScalarFunctionInput &inpu break; } } - return nullptr; } struct StandardConstructArgument { @@ -184,20 +182,20 @@ static void PrintfFunction(DataChunk &args, ExpressionState &state, Vector &resu ScalarFunction PrintfFun::GetFunction() { // duckdb_fmt::printf_context, duckdb_fmt::vsprintf - ScalarFunction printf_fun({}, LogicalType::VARCHAR, PrintfFunction, - BindPrintfFunction); + ScalarFunction printf_fun({}, LogicalType::VARCHAR, PrintfFunction); + printf_fun.SetResolveTypesCallback(PrintfResolveTypes); printf_fun.GetSignature().AddParameter("format", LogicalType::VARCHAR); - printf_fun.SetVarArgs(LogicalType::ANY); + printf_fun.GetSignature().AddArgs("args", LogicalType::ANY); printf_fun.SetFallible(); return printf_fun; } ScalarFunction FormatFun::GetFunction() { // duckdb_fmt::format_context, duckdb_fmt::vformat - ScalarFunction format_fun({}, LogicalType::VARCHAR, PrintfFunction, - BindPrintfFunction); + ScalarFunction format_fun({}, LogicalType::VARCHAR, PrintfFunction); + format_fun.SetResolveTypesCallback(PrintfResolveTypes); format_fun.GetSignature().AddParameter("format", LogicalType::VARCHAR); - format_fun.SetVarArgs(LogicalType::ANY); + format_fun.GetSignature().AddArgs("args", LogicalType::ANY); format_fun.SetFallible(); return format_fun; } diff --git a/src/duckdb/extension/core_functions/scalar/string/replace.cpp b/src/duckdb/extension/core_functions/scalar/string/replace.cpp index e2d70be15..91f72baf2 100644 --- a/src/duckdb/extension/core_functions/scalar/string/replace.cpp +++ b/src/duckdb/extension/core_functions/scalar/string/replace.cpp @@ -3,6 +3,7 @@ #include "duckdb/common/exception.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" #include "duckdb/common/vector_operations/ternary_executor.hpp" +#include "duckdb/function/scalar/string_common.hpp" #include #include @@ -13,13 +14,11 @@ namespace duckdb { static idx_t NextNeedle(const char *input_haystack, idx_t size_haystack, const char *input_needle, const idx_t size_needle) { // Needle needs something to proceed - if (size_needle > 0) { - // Haystack should be bigger or equal size to the needle - for (idx_t string_position = 0; (size_haystack - string_position) >= size_needle; ++string_position) { - // Compare Needle to the Haystack - if ((memcmp(input_haystack + string_position, input_needle, size_needle) == 0)) { - return string_position; - } + if (size_needle > 0 && size_haystack > 0) { + auto position = FindStrInStr(const_uchar_ptr_cast(input_haystack), size_haystack, + const_uchar_ptr_cast(input_needle), size_needle); + if (position != DConstants::INVALID_INDEX) { + return position; } } // Did not find the needle diff --git a/src/duckdb/extension/core_functions/scalar/string/to_base.cpp b/src/duckdb/extension/core_functions/scalar/string/to_base.cpp index e75a3bfbb..2c76139a4 100644 --- a/src/duckdb/extension/core_functions/scalar/string/to_base.cpp +++ b/src/duckdb/extension/core_functions/scalar/string/to_base.cpp @@ -1,22 +1,10 @@ #include "core_functions/scalar/string_functions.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" -#include "duckdb/planner/expression/bound_constant_expression.hpp" namespace duckdb { static const char alphabet[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -static unique_ptr ToBaseBind(BindScalarFunctionInput &input) { - auto &arguments = input.GetArguments(); - // If no min_length is specified, default to 0 - D_ASSERT(arguments.size() == 2 || arguments.size() == 3); - if (arguments.size() == 2) { - arguments.push_back(make_uniq_base(Value::INTEGER(0))); - input.GetBoundFunction().GetArguments().push_back(LogicalType::INTEGER); - } - return nullptr; -} - static void ToBaseFunction(DataChunk &args, ExpressionState &state, Vector &result) { const auto &input = args.data[0]; const auto &radix = args.data[1]; @@ -56,16 +44,13 @@ static void ToBaseFunction(DataChunk &args, ExpressionState &state, Vector &resu ScalarFunctionSet ToBaseFun::GetFunctions() { ScalarFunctionSet set("to_base"); - ScalarFunction to_base({}, LogicalType::VARCHAR, ToBaseFunction, ToBaseBind); - to_base.GetSignature().AddParameter("number", LogicalType::BIGINT).AddParameter("radix", LogicalType::INTEGER); - set.AddFunction(to_base); - - ScalarFunction to_base_padded({}, LogicalType::VARCHAR, ToBaseFunction, ToBaseBind); - to_base_padded.GetSignature() + auto function = ScalarFunction({}, LogicalType::VARCHAR, ToBaseFunction); + function.GetSignature() .AddParameter("number", LogicalType::BIGINT) .AddParameter("radix", LogicalType::INTEGER) .AddParameter("min_length", LogicalType::INTEGER); - set.AddFunction(to_base_padded); + function.GetSignature().GetParameter(2).SetDefaultValue(Value::INTEGER(0)); + set.AddFunction(std::move(function)); // throws if the number, radix or min_length are out of range set.SetFallible(); diff --git a/src/duckdb/extension/core_functions/scalar/string/translate.cpp b/src/duckdb/extension/core_functions/scalar/string/translate.cpp index 1a2770e30..7e7568138 100644 --- a/src/duckdb/extension/core_functions/scalar/string/translate.cpp +++ b/src/duckdb/extension/core_functions/scalar/string/translate.cpp @@ -71,6 +71,11 @@ static string_t TranslateScalarFunction(const string_t &haystack, const string_t input_haystack += sz; } + if (result.size() > string_t::MAX_STRING_SIZE) { + throw OutOfRangeException( + "Cannot create a string of size: '%d' in TRANSLATE, the maximum supported string size is: '%d'", + result.size(), string_t::MAX_STRING_SIZE); + } return string_t(result.data(), UnsafeNumericCast(result.size())); } @@ -94,6 +99,7 @@ ScalarFunction TranslateFun::GetFunction() { .AddParameter("string", LogicalType::VARCHAR) .AddParameter("from", LogicalType::VARCHAR) .AddParameter("to", LogicalType::VARCHAR); + fun.SetFallible(); return fun; } diff --git a/src/duckdb/extension/core_functions/scalar/string/trim.cpp b/src/duckdb/extension/core_functions/scalar/string/trim.cpp index c640d0ba2..4d4795903 100644 --- a/src/duckdb/extension/core_functions/scalar/string/trim.cpp +++ b/src/duckdb/extension/core_functions/scalar/string/trim.cpp @@ -3,6 +3,7 @@ #include "duckdb/common/exception.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" #include "duckdb/common/vector_operations/unary_executor.hpp" +#include "duckdb/function/scalar/string_common.hpp" #include "utf8proc.hpp" #include @@ -16,20 +17,17 @@ struct TrimOperator { auto data = input.GetData(); auto size = input.GetSize(); - utf8proc_int32_t codepoint; - auto str = reinterpret_cast(data); + int32_t codepoint; // Find the first character that is not left trimmed idx_t begin = 0; if (LTRIM) { while (begin < size) { - auto bytes = - utf8proc_iterate(str + begin, UnsafeNumericCast(size - begin), &codepoint); - D_ASSERT(bytes > 0); + auto bytes = DecodeCodepoint(data + begin, size - begin, codepoint); if (utf8proc_category(codepoint) != UTF8PROC_CATEGORY_ZS) { break; } - begin += UnsafeNumericCast(bytes); + begin += bytes; } } @@ -38,9 +36,7 @@ struct TrimOperator { if (RTRIM) { end = begin; for (auto next = begin; next < size;) { - auto bytes = utf8proc_iterate(str + next, UnsafeNumericCast(size - next), &codepoint); - D_ASSERT(bytes > 0); - next += UnsafeNumericCast(bytes); + next += DecodeCodepoint(data + next, size - next, codepoint); if (utf8proc_category(codepoint) != UTF8PROC_CATEGORY_ZS) { end = next; } @@ -64,14 +60,13 @@ static void UnaryTrimFunction(DataChunk &args, ExpressionState &state, Vector &r UnaryExecutor::ExecuteString>(args.data[0], result); } -static void GetIgnoredCodepoints(string_t ignored, unordered_set &ignored_codepoints) { - auto dataptr = reinterpret_cast(ignored.GetData()); +static void GetIgnoredCodepoints(string_t ignored, unordered_set &ignored_codepoints) { + auto dataptr = ignored.GetData(); auto size = ignored.GetSize(); idx_t pos = 0; while (pos < size) { - utf8proc_int32_t codepoint; - pos += UnsafeNumericCast( - utf8proc_iterate(dataptr + pos, UnsafeNumericCast(size - pos), &codepoint)); + int32_t codepoint; + pos += DecodeCodepoint(dataptr + pos, size - pos, codepoint); ignored_codepoints.insert(codepoint); } } @@ -83,22 +78,20 @@ static void BinaryTrimFunction(DataChunk &input, ExpressionState &state, Vector auto data = input.GetData(); auto size = input.GetSize(); - unordered_set ignored_codepoints; + unordered_set ignored_codepoints; GetIgnoredCodepoints(ignored, ignored_codepoints); - utf8proc_int32_t codepoint; - auto str = reinterpret_cast(data); + int32_t codepoint; // Find the first character that is not left trimmed idx_t begin = 0; if (LTRIM) { while (begin < size) { - auto bytes = - utf8proc_iterate(str + begin, UnsafeNumericCast(size - begin), &codepoint); + auto bytes = DecodeCodepoint(data + begin, size - begin, codepoint); if (ignored_codepoints.find(codepoint) == ignored_codepoints.end()) { break; } - begin += UnsafeNumericCast(bytes); + begin += bytes; } } @@ -107,10 +100,7 @@ static void BinaryTrimFunction(DataChunk &input, ExpressionState &state, Vector if (RTRIM) { end = begin; for (auto next = begin; next < size;) { - auto bytes = - utf8proc_iterate(str + next, UnsafeNumericCast(size - next), &codepoint); - D_ASSERT(bytes > 0); - next += UnsafeNumericCast(bytes); + next += DecodeCodepoint(data + next, size - next, codepoint); if (ignored_codepoints.find(codepoint) == ignored_codepoints.end()) { end = next; } diff --git a/src/duckdb/extension/core_functions/scalar/struct/struct_insert.cpp b/src/duckdb/extension/core_functions/scalar/struct/struct_insert.cpp index f09874b19..9a6f6cb3b 100644 --- a/src/duckdb/extension/core_functions/scalar/struct/struct_insert.cpp +++ b/src/duckdb/extension/core_functions/scalar/struct/struct_insert.cpp @@ -7,6 +7,7 @@ #include "duckdb/common/case_insensitive_map.hpp" #include "duckdb/storage/statistics/struct_stats.hpp" #include "duckdb/planner/expression_binder.hpp" +#include "duckdb/parser/expression/function_expression.hpp" namespace duckdb { @@ -32,8 +33,11 @@ static void StructInsertFunction(DataChunk &args, ExpressionState &state, Vector static unique_ptr StructInsertBind(BindScalarFunctionInput &input) { auto &bound_function = input.GetBoundFunction(); auto &arguments = input.GetArguments(); - if (arguments.empty()) { - throw InvalidInputException("Missing required arguments for struct_insert function."); + auto &names = *input.GetArgumentNames(); + for (auto &argument : arguments) { + if (argument->GetReturnType().id() == LogicalTypeId::UNKNOWN) { + throw ParameterNotResolvedException(); + } } if (LogicalTypeId::STRUCT != arguments[0]->GetReturnType().id()) { throw InvalidInputException("The first argument to struct_insert must be a STRUCT"); @@ -52,17 +56,13 @@ static unique_ptr StructInsertBind(BindScalarFunctionInput &input) new_children.push_back(make_pair(child.first, child.second)); } - // Loop through the additional arguments (name/value pairs) + // Loop through the fields that are inserted for (idx_t i = 1; i < arguments.size(); i++) { - auto &child = arguments[i]; - if (child->GetAlias().empty()) { - throw BinderException("Need named argument for struct insert, e.g., a := b"); - } - if (name_collision_set.find(child->GetAlias()) != name_collision_set.end()) { - throw BinderException("Duplicate struct entry name \"%s\"", child->GetAlias()); + if (name_collision_set.find(names[i]) != name_collision_set.end()) { + throw BinderException("Duplicate struct entry name \"%s\"", names[i]); } - name_collision_set.insert(child->GetAlias()); - new_children.emplace_back(make_pair(child->GetAlias(), arguments[i]->GetReturnType())); + name_collision_set.insert(names[i]); + new_children.emplace_back(make_pair(names[i], arguments[i]->GetReturnType())); } bound_function.SetReturnType(LogicalType::STRUCT(new_children)); @@ -89,13 +89,35 @@ static unique_ptr StructInsertStats(ClientContext &context, Func return new_stats.ToUnique(); } +static unique_ptr StructInsertUnbind(FunctionUnbindInput &input) { + auto &function = input.expression.Function(); + auto &types = function.GetLogicalArguments(); + auto &return_type = function.GetLogicalReturnType(); + if (types.empty() || types[0].id() != LogicalTypeId::STRUCT || return_type.id() != LogicalTypeId::STRUCT || + input.children.size() != types.size()) { + return nullptr; + } + auto existing_count = StructType::GetChildCount(types[0]); + auto &return_children = StructType::GetChildTypes(return_type); + if (return_children.size() != existing_count + types.size() - 1) { + return nullptr; + } + vector arguments; + arguments.emplace_back(std::move(input.children[0])); + for (idx_t i = 1; i < input.children.size(); i++) { + arguments.emplace_back(return_children[existing_count + i - 1].first, std::move(input.children[i])); + } + return make_uniq(function.GetDefinition()->GetQualifiedName(), std::move(arguments)); +} + ScalarFunction StructInsertFun::GetFunction() { ScalarFunction fun({}, LogicalTypeId::STRUCT, StructInsertFunction, StructInsertBind, StructInsertStats); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); - fun.SetVarArgs(LogicalType::ANY); + fun.GetSignature().AddParameter("struct", LogicalType::ANY).AddKwargs("kwargs", LogicalType::ANY); fun.GetProperties().SetRequiresExpressionNames(true); fun.SetSerializeCallback(VariableReturnBindData::Serialize); fun.SetDeserializeCallback(VariableReturnBindData::Deserialize); + fun.SetUnbindCallback(StructInsertUnbind); return fun; } diff --git a/src/duckdb/extension/core_functions/scalar/struct/struct_update.cpp b/src/duckdb/extension/core_functions/scalar/struct/struct_update.cpp index dc2f0b437..1ebd026cd 100644 --- a/src/duckdb/extension/core_functions/scalar/struct/struct_update.cpp +++ b/src/duckdb/extension/core_functions/scalar/struct/struct_update.cpp @@ -1,3 +1,4 @@ +#include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/common/vector/map_vector.hpp" #include "duckdb/common/vector/struct_vector.hpp" #include "core_functions/scalar/struct_functions.hpp" @@ -10,44 +11,62 @@ namespace duckdb { -static void StructUpdateFunction(DataChunk &args, ExpressionState &state, Vector &result) { - const auto &starting_vec = args.data[0]; - starting_vec.Verify(); - - auto &starting_child_entries = StructVector::GetEntries(starting_vec); - auto &result_child_entries = StructVector::GetEntries(result); +//! The result field that every argument after the struct writes to +struct StructUpdateBindData : public FunctionData { + StructUpdateBindData(LogicalType stype_p, vector field_indexes_p) + : stype(std::move(stype_p)), field_indexes(std::move(field_indexes_p)) { + } - auto &starting_types = StructType::GetChildTypes(starting_vec.GetType()); + LogicalType stype; + //! Indexed by argument index minus one + vector field_indexes; - auto &func_args = state.expr.Cast().GetChildren(); - auto new_entries = identifier_tree_t(); - auto is_new_field = vector(args.ColumnCount(), true); + unique_ptr Copy() const override { + return make_uniq(stype, field_indexes); + } + bool Equals(const FunctionData &other_p) const override { + auto &other = other_p.Cast(); + return stype == other.stype && field_indexes == other.field_indexes; + } - for (idx_t arg_idx = 1; arg_idx < func_args.size(); arg_idx++) { - auto &new_child = func_args[arg_idx]; - new_entries.emplace(new_child->GetAlias(), arg_idx); + static void Serialize(Serializer &serializer, const optional_ptr bind_data, + const BoundScalarFunction &function) { + auto &info = bind_data->Cast(); + serializer.WriteProperty(100, "variable_return_type", info.stype); + serializer.WritePropertyWithDefault(101, "field_indexes", info.field_indexes); } - // Assign the original child entries to the STRUCT. - for (idx_t field_idx = 0; field_idx < starting_child_entries.size(); field_idx++) { - auto &starting_child = starting_child_entries[field_idx]; - auto update = new_entries.find(starting_types[field_idx].first.c_str()); + static unique_ptr Deserialize(Deserializer &deserializer, BoundScalarFunction &bound_function) { + auto stype = deserializer.ReadProperty(100, "variable_return_type"); + auto field_indexes = deserializer.ReadPropertyWithDefault>(101, "field_indexes"); + return make_uniq(std::move(stype), std::move(field_indexes)); + } +}; - if (update == new_entries.end()) { - // No update present, copy from source - result_child_entries[field_idx].Reference(starting_child); - } else { - // We found a replacement of the same name to update - auto arg_idx = update->second; - result_child_entries[field_idx].Reference(args.data[arg_idx]); - is_new_field[arg_idx] = false; - } +//! For every result field, the argument that provides it - fields without one keep the value of the source struct +static vector FieldArguments(const StructUpdateBindData &info, idx_t field_count) { + vector field_arguments(field_count); + for (idx_t arg_idx = 1; arg_idx <= info.field_indexes.size(); arg_idx++) { + field_arguments[info.field_indexes[arg_idx - 1]] = arg_idx; } + return field_arguments; +} - // Assign the new (not updated) children to the end of the result vector. - for (idx_t arg_idx = 1, field_idx = starting_child_entries.size(); arg_idx < args.ColumnCount(); arg_idx++) { - if (is_new_field[arg_idx]) { - result_child_entries[field_idx++].Reference(args.data[arg_idx]); +static void StructUpdateFunction(DataChunk &args, ExpressionState &state, Vector &result) { + const auto &starting_vec = args.data[0]; + starting_vec.Verify(); + + auto &starting_child_entries = StructVector::GetEntries(starting_vec); + auto &result_child_entries = StructVector::GetEntries(result); + auto &info = state.expr.Cast().BindInfo()->Cast(); + auto field_arguments = FieldArguments(info, result_child_entries.size()); + + for (idx_t field_idx = 0; field_idx < result_child_entries.size(); field_idx++) { + auto &argument = field_arguments[field_idx]; + if (argument.IsValid()) { + result_child_entries[field_idx].Reference(args.data[argument.GetIndex()]); + } else { + result_child_entries[field_idx].Reference(starting_child_entries[field_idx]); } } } @@ -70,16 +89,12 @@ static unique_ptr StructUpdateBind(BindScalarFunctionInput &input) auto incoming_children = identifier_tree_t(); auto is_new_field = vector(arguments.size(), true); + vector field_indexes(arguments.size() - 1); - // Validate incoming arguments and record names + // Record the names of the fields that are updated + auto &names = *input.GetArgumentNames(); for (idx_t arg_idx = 1; arg_idx < arguments.size(); arg_idx++) { - auto &child = arguments[arg_idx]; - if (child->GetAlias().empty()) { - throw BinderException("Need named argument for struct update, e.g., a := b"); - } else if (incoming_children.find(child->GetAlias()) != incoming_children.end()) { - throw InvalidInputException("Duplicate named argument provided for %s", child->GetAlias().c_str()); - } - incoming_children.emplace(child->GetAlias(), arg_idx); + incoming_children.emplace(names[arg_idx], arg_idx); } for (idx_t field_idx = 0; field_idx < existing_children.size(); field_idx++) { @@ -91,69 +106,65 @@ static unique_ptr StructUpdateBind(BindScalarFunctionInput &input) } else { // Update the struct with the new data of the same name auto arg_idx = update->second; - auto &new_child = arguments[arg_idx]; - new_children.emplace_back(make_pair(new_child->GetAlias(), new_child->GetReturnType())); + new_children.emplace_back(make_pair(names[arg_idx], arguments[arg_idx]->GetReturnType())); is_new_field[arg_idx] = false; + field_indexes[arg_idx - 1] = field_idx; } } // Loop through the additional arguments (name/value pairs) for (idx_t arg_idx = 1; arg_idx < arguments.size(); arg_idx++) { if (is_new_field[arg_idx]) { - auto &child = arguments[arg_idx]; - new_children.emplace_back(make_pair(child->GetAlias(), child->GetReturnType())); + field_indexes[arg_idx - 1] = new_children.size(); + new_children.emplace_back(make_pair(names[arg_idx], arguments[arg_idx]->GetReturnType())); } } bound_function.SetReturnType(LogicalType::STRUCT(new_children)); - return make_uniq(bound_function.GetReturnType()); + return make_uniq(bound_function.GetReturnType(), std::move(field_indexes)); } static unique_ptr StructUpdateStats(ClientContext &context, FunctionStatisticsInput &input) { auto &child_stats = input.child_stats; auto &expr = input.expr; + auto &info = input.bind_data->Cast(); - auto incoming_children = identifier_tree_t(); - auto is_new_field = vector(expr.GetChildren().size(), true); auto new_stats = StructStats::CreateUnknown(expr.GetReturnType()); new_stats.Set(StatsInfo::CANNOT_HAVE_NULL_VALUES); - for (idx_t arg_idx = 1; arg_idx < expr.GetChildren().size(); arg_idx++) { - auto &new_child = expr.GetChildren()[arg_idx]; - incoming_children.emplace(new_child->GetAlias(), arg_idx); - } - - auto existing_type = child_stats[0].GetType(); - auto existing_count = StructType::GetChildCount(existing_type); auto existing_stats = StructStats::GetChildStats(child_stats[0]); - for (idx_t field_idx = 0; field_idx < existing_count; field_idx++) { - auto &existing_child = existing_stats[field_idx]; - auto update = incoming_children.find(StructType::GetChildName(existing_type, field_idx)); - if (update == incoming_children.end()) { - StructStats::SetChildStats(new_stats, field_idx, existing_child); + auto field_count = StructType::GetChildCount(expr.GetReturnType()); + auto field_arguments = FieldArguments(info, field_count); + for (idx_t field_idx = 0; field_idx < field_count; field_idx++) { + auto &argument = field_arguments[field_idx]; + if (argument.IsValid()) { + StructStats::SetChildStats(new_stats, field_idx, child_stats[argument.GetIndex()]); } else { - auto arg_idx = update->second; - StructStats::SetChildStats(new_stats, field_idx, child_stats[arg_idx]); - is_new_field[arg_idx] = false; + StructStats::SetChildStats(new_stats, field_idx, existing_stats[field_idx]); } } + return new_stats.ToUnique(); +} - for (idx_t arg_idx = 1, field_idx = existing_count; arg_idx < expr.GetChildren().size(); arg_idx++) { - if (is_new_field[arg_idx]) { - StructStats::SetChildStats(new_stats, field_idx++, child_stats[arg_idx]); - } +static unique_ptr StructUpdateUnbind(FunctionUnbindInput &input) { + auto &info = input.expression.BindInfo()->Cast(); + vector arguments; + for (idx_t i = 0; i < input.children.size(); i++) { + auto name = i == 0 ? Identifier() : StructType::GetChildName(info.stype, info.field_indexes[i - 1]); + arguments.emplace_back(std::move(name), std::move(input.children[i])); } - - return new_stats.ToUnique(); + return make_uniq(input.expression.Function().GetDefinition()->GetQualifiedName(), + std::move(arguments)); } ScalarFunction StructUpdateFun::GetFunction() { ScalarFunction fun({}, LogicalTypeId::STRUCT, StructUpdateFunction, StructUpdateBind, StructUpdateStats); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); - fun.SetVarArgs(LogicalType::ANY); + fun.GetSignature().AddParameter("struct", LogicalType::ANY).AddKwargs("kwargs", LogicalType::ANY); fun.GetProperties().SetRequiresExpressionNames(true); - fun.SetSerializeCallback(VariableReturnBindData::Serialize); - fun.SetDeserializeCallback(VariableReturnBindData::Deserialize); + fun.SetUnbindCallback(StructUpdateUnbind); + fun.SetSerializeCallback(StructUpdateBindData::Serialize); + fun.SetDeserializeCallback(StructUpdateBindData::Deserialize); return fun; } diff --git a/src/duckdb/extension/core_functions/scalar/struct/struct_values.cpp b/src/duckdb/extension/core_functions/scalar/struct/struct_values.cpp index 5ef9e57f9..3d3c865f7 100644 --- a/src/duckdb/extension/core_functions/scalar/struct/struct_values.cpp +++ b/src/duckdb/extension/core_functions/scalar/struct/struct_values.cpp @@ -53,10 +53,9 @@ static void StructValuesFunction(DataChunk &args, ExpressionState &state, Vector } // Ensure input is a STRUCT, set return type to an unnamed STRUCT with same child types -static unique_ptr StructValuesBind(BindScalarFunctionInput &input) { +static void StructValuesResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - const auto arg_type = arguments[0]->GetReturnType(); + const auto &arg_type = input.GetArgumentType(0); if (arg_type == LogicalTypeId::UNKNOWN) { throw ParameterNotResolvedException(); } @@ -67,17 +66,17 @@ static unique_ptr StructValuesBind(BindScalarFunctionInput &input) // Build unnamed TUPLE child list using only the types vector unnamed_children; - auto &children = StructType::GetChildTypes(arguments[0]->GetReturnType()); + auto &children = StructType::GetChildTypes(arg_type); unnamed_children.reserve(children.size()); for (auto &child : children) { unnamed_children.emplace_back(child.second); } bound_function.SetReturnType(LogicalType::TUPLE(std::move(unnamed_children))); - return nullptr; } ScalarFunction StructValuesFun::GetFunction() { - ScalarFunction func({}, LogicalTypeId::TUPLE, StructValuesFunction, StructValuesBind); + ScalarFunction func({}, LogicalTypeId::TUPLE, StructValuesFunction); + func.SetResolveTypesCallback(StructValuesResolveTypes); func.GetSignature().AddParameter("struct", LogicalTypeId::STRUCT); return func; } diff --git a/src/duckdb/extension/core_functions/scalar/union/union_extract.cpp b/src/duckdb/extension/core_functions/scalar/union/union_extract.cpp index ff578b38e..7edb6ecc9 100644 --- a/src/duckdb/extension/core_functions/scalar/union/union_extract.cpp +++ b/src/duckdb/extension/core_functions/scalar/union/union_extract.cpp @@ -10,8 +10,8 @@ namespace duckdb { namespace { struct UnionExtractBindData : public FunctionData { - UnionExtractBindData(Identifier key, idx_t index, LogicalType type) - : key(std::move(key)), index(index), type(std::move(type)) { + UnionExtractBindData(const Identifier &key, idx_t index, LogicalType type) + : key(key), index(index), type(std::move(type)) { } string key; @@ -42,21 +42,27 @@ void UnionExtractFunction(DataChunk &args, ExpressionState &state, Vector &resul result.Verify(); } -unique_ptr UnionExtractBind(BindScalarFunctionInput &input) { +//! Validates the union argument - the extracted member and the return type are resolved in the bind +void UnionExtractResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); D_ASSERT(bound_function.GetArguments().size() == 2); - if (arguments[0]->GetReturnType().id() == LogicalTypeId::UNKNOWN) { + auto &union_type = input.GetArgumentType(0); + if (union_type.id() == LogicalTypeId::UNKNOWN) { throw ParameterNotResolvedException(); } - if (arguments[0]->GetReturnType().id() != LogicalTypeId::UNION) { + if (union_type.id() != LogicalTypeId::UNION) { throw BinderException("union_extract can only take a union parameter"); } - idx_t union_member_count = UnionType::GetMemberCount(arguments[0]->GetReturnType()); - if (union_member_count == 0) { + if (UnionType::GetMemberCount(union_type) == 0) { throw InternalException("Can't extract something from an empty union"); } - bound_function.GetArguments()[0] = arguments[0]->GetReturnType(); + bound_function.GetArguments()[0] = union_type; +} + +unique_ptr UnionExtractBind(BindScalarFunctionInput &input) { + auto &bound_function = input.GetBoundFunction(); + auto &arguments = input.GetArguments(); + idx_t union_member_count = UnionType::GetMemberCount(arguments[0]->GetReturnType()); auto key_val = input.GetNonNullConstant(1); D_ASSERT(key_val.type().id() == LogicalTypeId::VARCHAR); @@ -81,6 +87,7 @@ unique_ptr UnionExtractBind(BindScalarFunctionInput &input) { } if (!found_key) { + // LCOV_EXCL_START vector candidates; candidates.reserve(union_member_count); for (idx_t i = 0; i < union_member_count; i++) { @@ -89,6 +96,7 @@ unique_ptr UnionExtractBind(BindScalarFunctionInput &input) { auto closest_settings = StringUtil::TopNJaroWinkler(candidates, key); auto message = StringUtil::CandidatesMessage(closest_settings, "Candidate Entries"); throw BinderException("Could not find key %s in union\n%s", key, message); + // LCOV_EXCL_STOP } bound_function.SetReturnType(return_type); @@ -101,6 +109,7 @@ ScalarFunction UnionExtractFun::GetFunction() { // the arguments and return types are actually set in the binder function ScalarFunction fun({}, LogicalType::ANY, UnionExtractFunction, UnionExtractBind, nullptr, nullptr); fun.GetSignature().AddParameter("union", LogicalTypeId::UNION).AddParameter("tag", LogicalType::VARCHAR); + fun.SetResolveTypesCallback(UnionExtractResolveTypes); return fun; } diff --git a/src/duckdb/extension/core_functions/scalar/union/union_tag.cpp b/src/duckdb/extension/core_functions/scalar/union/union_tag.cpp index a9ceebfd0..fd88729a4 100644 --- a/src/duckdb/extension/core_functions/scalar/union/union_tag.cpp +++ b/src/duckdb/extension/core_functions/scalar/union/union_tag.cpp @@ -9,7 +9,7 @@ namespace duckdb { namespace { -unique_ptr UnionTagBind(BindScalarFunctionInput &input) { +void UnionTagResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); auto &arguments = input.GetArguments(); if (arguments.empty()) { @@ -44,8 +44,6 @@ unique_ptr UnionTagBind(BindScalarFunctionInput &input) { } auto enum_type = LogicalType::ENUM(varchar_vector, member_count); bound_function.SetReturnType(enum_type); - - return nullptr; } void UnionTagFunction(DataChunk &args, ExpressionState &state, Vector &result) { @@ -57,8 +55,9 @@ void UnionTagFunction(DataChunk &args, ExpressionState &state, Vector &result) { ScalarFunction UnionTagFun::GetFunction() { // TODO: Statistics? - ScalarFunction fun({}, LogicalTypeId::ANY, UnionTagFunction, UnionTagBind, nullptr, nullptr); + ScalarFunction fun({}, LogicalTypeId::ANY, UnionTagFunction, nullptr, nullptr, nullptr); fun.GetSignature().AddParameter("union", LogicalTypeId::UNION); + fun.SetResolveTypesCallback(UnionTagResolveTypes); return fun; } diff --git a/src/duckdb/extension/core_functions/scalar/union/union_value.cpp b/src/duckdb/extension/core_functions/scalar/union/union_value.cpp index 5b081da2a..443100fef 100644 --- a/src/duckdb/extension/core_functions/scalar/union/union_value.cpp +++ b/src/duckdb/extension/core_functions/scalar/union/union_value.cpp @@ -3,6 +3,7 @@ #include "duckdb/common/string_util.hpp" #include "duckdb/execution/expression_executor.hpp" #include "duckdb/function/scalar/nested_functions.hpp" +#include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" #include "duckdb/planner/expression/bound_parameter_expression.hpp" @@ -39,29 +40,33 @@ unique_ptr UnionValueBind(BindScalarFunctionInput &input) { if (arguments.size() != 1) { throw BinderException("union_value takes exactly one argument"); } - auto &child = arguments[0]; - - if (child->GetAlias().empty()) { - throw BinderException("Need named argument for union tag, e.g. UNION_VALUE(a := b)"); - } - child_list_t union_members; - - union_members.emplace_back(make_pair(child->GetAlias(), child->GetReturnType())); + union_members.emplace_back(make_pair(input.GetArgumentNames()->at(0), arguments[0]->GetReturnType())); bound_function.SetReturnType(LogicalType::UNION(std::move(union_members))); return make_uniq(bound_function.GetReturnType()); } +unique_ptr UnionValueUnbind(FunctionUnbindInput &input) { + auto &expression = input.expression; + D_ASSERT(input.children.size() == 1); + D_ASSERT(UnionType::GetMemberCount(expression.GetReturnType()) == 1); + vector arguments; + arguments.emplace_back(UnionType::GetMemberName(expression.GetReturnType(), 0), std::move(input.children[0])); + return make_uniq(expression.Function().GetDefinition()->GetQualifiedName(), + std::move(arguments)); +} + } // namespace ScalarFunction UnionValueFun::GetFunction() { ScalarFunction fun("union_value", {}, LogicalTypeId::UNION, UnionValueFunction, UnionValueBind, nullptr, nullptr); - fun.SetVarArgs(LogicalType::ANY); + fun.GetSignature().AddKwargs("kwargs", LogicalType::ANY); fun.GetProperties().SetRequiresExpressionNames(true); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); fun.SetSerializeCallback(VariableReturnBindData::Serialize); fun.SetDeserializeCallback(VariableReturnBindData::Deserialize); + fun.SetUnbindCallback(UnionValueUnbind); return fun; } diff --git a/src/duckdb/extension/icu/collation/collation_collator.cpp b/src/duckdb/extension/icu/collation/collation_collator.cpp index 307eb7a3e..d52e5e530 100644 --- a/src/duckdb/extension/icu/collation/collation_collator.cpp +++ b/src/duckdb/extension/icu/collation/collation_collator.cpp @@ -85,6 +85,9 @@ static collation_element_t MakeElement(uint32_t primary, uint32_t secondary, uin //! Returns the position of a Han character in radical/stroke order, or false if it is not a Han character static bool GetHanIndex(const CollationRoot &root, uint32_t codepoint, uint32_t &result) { // only the ranges of the block the code point is in have to be searched + if (codepoint >= 0x110000) { + return false; + } uint32_t lower = root.han_block_lower[codepoint >> HAN_BLOCK_SHIFT]; uint32_t upper = root.han_block_upper[codepoint >> HAN_BLOCK_SHIFT]; while (lower < upper) { @@ -124,6 +127,9 @@ static collation_element_t ImplicitElement(const CollationRoot &root, uint32_t c } static uint32_t LookupValue(const CollationTable &table, uint32_t codepoint) { + if (codepoint >= 0x110000) { + return 0; + } auto block = table.trie_stage1[codepoint >> COLLATION_TRIE_SHIFT]; return table.trie_stage2[block + (codepoint & COLLATION_TRIE_MASK)]; } diff --git a/src/duckdb/extension/icu/collation/collation_normalizer.cpp b/src/duckdb/extension/icu/collation/collation_normalizer.cpp index 8a9412909..e354a96cd 100644 --- a/src/duckdb/extension/icu/collation/collation_normalizer.cpp +++ b/src/duckdb/extension/icu/collation/collation_normalizer.cpp @@ -66,7 +66,7 @@ uint32_t Normalizer::Decode(const char *data, idx_t size, vector &resu } codepoint = (codepoint << 6) | (trail & 0x3F); } - if (!valid) { + if (!valid || codepoint > 0x10FFFF) { result.push_back(REPLACEMENT_CHARACTER); pos++; continue; diff --git a/src/duckdb/extension/icu/collation/include/collation_collator.hpp b/src/duckdb/extension/icu/collation/include/collation_collator.hpp index c3639dc58..53ed16bc2 100644 --- a/src/duckdb/extension/icu/collation/include/collation_collator.hpp +++ b/src/duckdb/extension/icu/collation/include/collation_collator.hpp @@ -48,11 +48,19 @@ struct TailoredBlocks { void Build(const uint32_t *codepoints, uint32_t count); bool Contains(uint32_t codepoint) const { + if (codepoint >= 0x110000) { + return false; + } auto block = codepoint >> BITMAP_SHIFT; return (bitmap[block / 64] & (1ULL << (block % 64))) != 0; } //! The part of the tailoring that can hold the code point void GetRange(uint32_t codepoint, uint32_t &lower, uint32_t &upper) const { + if (codepoint >= 0x110000) { + lower = 0; + upper = 0; + return; + } lower = bounds[codepoint >> BOUND_SHIFT]; upper = bounds[(codepoint >> BOUND_SHIFT) + 1]; } diff --git a/src/duckdb/extension/icu/datetime/calendar.cpp b/src/duckdb/extension/icu/datetime/calendar.cpp index 0497ad583..322b2a1f2 100644 --- a/src/duckdb/extension/icu/datetime/calendar.cpp +++ b/src/duckdb/extension/icu/datetime/calendar.cpp @@ -1005,12 +1005,13 @@ int32_t FieldCalendar::TryGuessDifference(int64_t start, int64_t target, Calenda if (length <= 0) { return false; } - const auto estimate = (target - start) / length; + const auto estimate = (static_cast(target) - static_cast(start)) / length; // a small difference is found by the search below in about as many steps as the guess would // take to check, so it is only worth guessing once the two instants are further apart static constexpr double WORTH_GUESSING = 8; - if (!(std::fabs(estimate) >= WORTH_GUESSING) || estimate < NumericLimits::Minimum() / 2 || - estimate > NumericLimits::Maximum() / 2) { + if (!(std::fabs(estimate) >= WORTH_GUESSING) || + estimate < static_cast(NumericLimits::Minimum()) / 2.0 || + estimate > static_cast(NumericLimits::Maximum()) / 2.0) { return false; } diff --git a/src/duckdb/extension/icu/datetime/chinese.cpp b/src/duckdb/extension/icu/datetime/chinese.cpp index 6982145eb..ebd76d4a2 100644 --- a/src/duckdb/extension/icu/datetime/chinese.cpp +++ b/src/duckdb/extension/icu/datetime/chinese.cpp @@ -141,7 +141,10 @@ struct YearCache { }; //! Two calendar systems, each with a cache of solstices and one of new years -static thread_local YearCache YEAR_CACHES[2][2] = {}; +static YearCache &GetYearCache(idx_t setting, idx_t event) { + static thread_local YearCache year_caches[2][2] = {}; + return year_caches[setting][event]; +} static bool TryGetCached(YearCache &cache, int32_t year, int32_t &day) { const auto &entry = cache.entries[idx_t(uint32_t(year)) % YearCache::SIZE]; @@ -168,7 +171,7 @@ static int32_t SynodicMonthsBetween(int32_t day1, int32_t day2) { } int32_t ChineseCalendar::WinterSolstice(int32_t gyear) const { - auto &cache = YEAR_CACHES[GetSettingIndex()][0]; + auto &cache = GetYearCache(GetSettingIndex(), 0); int32_t cached; if (TryGetCached(cache, gyear, cached)) { return cached; @@ -211,7 +214,7 @@ bool ChineseCalendar::IsLeapMonthBetween(int32_t new_moon1, int32_t new_moon2) c } int32_t ChineseCalendar::NewYear(int32_t gyear) const { - auto &cache = YEAR_CACHES[GetSettingIndex()][1]; + auto &cache = GetYearCache(GetSettingIndex(), 1); int32_t cached; if (TryGetCached(cache, gyear, cached)) { return cached; diff --git a/src/duckdb/extension/icu/datetime/coptic.cpp b/src/duckdb/extension/icu/datetime/coptic.cpp index f757a588d..08ac67415 100644 --- a/src/duckdb/extension/icu/datetime/coptic.cpp +++ b/src/duckdb/extension/icu/datetime/coptic.cpp @@ -54,7 +54,7 @@ int64_t CopticEthiopicCalendar::HandleComputeMonthStart(int32_t eyear, int32_t m return int64_t(GetEpochOffset()) // the day the first year starts on + 365 * year // the whole years + FloorDiv::Divide(year, int64_t(4)) // their leap days - + 30 * month // the whole months, which are all thirty days long + + 30 * int64_t(month) // the whole months, which are all thirty days long - 1; // the day before the first of the month } diff --git a/src/duckdb/extension/icu/datetime/generated/tz_data.cpp b/src/duckdb/extension/icu/datetime/generated/tz_data.cpp index fb36e4a57..e2f860ea8 100644 --- a/src/duckdb/extension/icu/datetime/generated/tz_data.cpp +++ b/src/duckdb/extension/icu/datetime/generated/tz_data.cpp @@ -4,7 +4,7 @@ // tz_data.cpp // // This file is generated by scripts/generate_tz_data.py - do not edit. -// Source: Unicode Consortium time zone dataset (IANA release 2026c) +// Source: Unicode Consortium time zone dataset (IANA release 2026e) //===----------------------------------------------------------------------===// #include "tz_data.hpp" @@ -19,1873 +19,1883 @@ namespace datetime { // zone on its own, because the block is kept once it has been decompressed. const uint8_t TZ_COMPRESSED[] = { - 0x28, 0xB5, 0x2F, 0xFD, 0xA4, 0x39, 0x23, 0x03, 0x00, 0x8C, 0x26, 0x03, 0xAE, 0x54, 0xC4, 0x09, 0x11, 0x29, 0x10, 0x38, 0x7C, 0x31, 0xFF, 0xDA, - 0x6A, 0x32, 0xFE, 0x0B, 0x93, 0xDA, 0x5F, 0x09, 0x3F, 0x85, 0x72, 0xAC, 0x0F, 0x10, 0x62, 0xF1, 0x22, 0x18, 0x87, 0x6E, 0xF6, 0x41, 0xB9, 0x72, - 0x52, 0xE9, 0xAD, 0x49, 0xF3, 0xA9, 0x8F, 0xD6, 0x5E, 0xB9, 0x53, 0xD2, 0x10, 0x1D, 0x11, 0xEA, 0x10, 0x8F, 0xB9, 0x9A, 0xBD, 0xBE, 0x99, 0xA2, - 0x9F, 0x99, 0x1A, 0x19, 0x97, 0xBE, 0xFA, 0x4B, 0x97, 0x5F, 0xCB, 0x0D, 0xE9, 0x15, 0x4D, 0x1A, 0x95, 0x39, 0x2F, 0x94, 0x69, 0x35, 0x93, 0x57, - 0x4B, 0x92, 0x77, 0x51, 0x91, 0x71, 0x66, 0x90, 0x83, 0x6B, 0x77, 0x3C, 0xA6, 0x47, 0xC9, 0x29, 0x15, 0x84, 0x67, 0x8E, 0xAE, 0x6C, 0x8D, 0x9C, - 0x0F, 0x52, 0xC0, 0xA0, 0x80, 0x22, 0xF7, 0x03, 0x8A, 0x93, 0x12, 0x8B, 0xD2, 0x92, 0x8B, 0x44, 0x99, 0x8A, 0x28, 0x21, 0x12, 0xC5, 0x68, 0x4F, - 0xB4, 0xD1, 0x22, 0x56, 0xB5, 0x1E, 0x5E, 0x58, 0x36, 0x71, 0x5A, 0xF7, 0x49, 0xD8, 0xCE, 0xF5, 0x46, 0xE8, 0x98, 0xCD, 0xF3, 0x44, 0x1F, 0x3A, - 0x5B, 0xDA, 0xFE, 0xF8, 0xD7, 0xAA, 0xFB, 0xBF, 0xE4, 0x1E, 0x4F, 0xAA, 0x69, 0xBC, 0xE5, 0xF4, 0xB4, 0x27, 0x3C, 0xC0, 0x0D, 0xA3, 0x00, 0xC0, - 0xB9, 0xB2, 0x88, 0x61, 0x2C, 0x0F, 0x31, 0x3E, 0x4D, 0x72, 0x7B, 0x2D, 0xF0, 0xFF, 0x7F, 0x06, 0x7B, 0xAD, 0x62, 0x31, 0x15, 0xF7, 0x5F, 0x69, - 0xF1, 0x2E, 0x67, 0xCD, 0xBE, 0x65, 0x65, 0xA0, 0x5C, 0x31, 0x5F, 0xB8, 0xB2, 0x21, 0xB2, 0x22, 0x81, 0xAB, 0xA2, 0x02, 0xA3, 0x0A, 0xCA, 0x9B, - 0x72, 0x9B, 0x93, 0xFA, 0x32, 0x11, 0x95, 0xC7, 0x01, 0x3E, 0x87, 0x87, 0x12, 0x9C, 0x3F, 0xF9, 0xE5, 0x3A, 0x65, 0x76, 0x68, 0x7A, 0x84, 0x66, - 0xAA, 0xFC, 0xDC, 0xC7, 0xC8, 0xA0, 0x03, 0xEF, 0x39, 0x5E, 0xFE, 0x10, 0xEA, 0x1C, 0xF4, 0xCB, 0x8D, 0x72, 0x7A, 0xF6, 0x61, 0xC2, 0xF3, 0x2B, - 0xB3, 0xCF, 0xA0, 0x63, 0x1E, 0xE7, 0xE0, 0xAF, 0x3C, 0x40, 0xCD, 0x6B, 0xF0, 0xFD, 0x95, 0xB5, 0xF2, 0x9E, 0x8F, 0x8A, 0x5A, 0x40, 0xF5, 0x9D, - 0xAB, 0x00, 0x2D, 0xC3, 0xF0, 0xD9, 0x2E, 0xAE, 0x19, 0xE3, 0x87, 0xD9, 0xFA, 0xB9, 0x54, 0xE7, 0x5D, 0xC3, 0x32, 0x16, 0x6C, 0x35, 0x36, 0x85, - 0x24, 0xF6, 0x77, 0xC2, 0xDA, 0xC8, 0xC0, 0xDC, 0x57, 0xF1, 0x0B, 0x0C, 0xBE, 0xB2, 0xEC, 0x5E, 0x24, 0xCA, 0xE4, 0x17, 0xEE, 0x98, 0x75, 0x5B, - 0x74, 0xC3, 0x10, 0x18, 0x56, 0x1C, 0x5E, 0x95, 0x04, 0x56, 0xE9, 0x9F, 0xBB, 0xD8, 0xF0, 0xCB, 0x2D, 0xDE, 0x2A, 0xD1, 0xEF, 0xBD, 0x0D, 0x38, - 0x90, 0x1E, 0x0A, 0x30, 0xC4, 0x4B, 0xFA, 0x0E, 0x5A, 0x08, 0x9F, 0x7B, 0xB9, 0x97, 0x8A, 0xE0, 0xFD, 0x96, 0x8E, 0x56, 0x1D, 0x1C, 0xB9, 0x7F, - 0x92, 0x08, 0x10, 0x6D, 0xD8, 0xC1, 0x08, 0x78, 0xD3, 0xF7, 0x82, 0xAC, 0x26, 0x81, 0xEC, 0x27, 0x7E, 0xE0, 0x5A, 0x7D, 0xF0, 0x2C, 0xE0, 0x07, - 0xE0, 0x77, 0x61, 0x97, 0x9D, 0xC0, 0x11, 0x9B, 0xC7, 0x39, 0x33, 0x30, 0x37, 0xAF, 0xB0, 0x37, 0x49, 0xBF, 0x66, 0x17, 0xAF, 0xC6, 0x2C, 0x26, - 0x42, 0x91, 0x00, 0xB6, 0x46, 0x46, 0x46, 0x70, 0xFC, 0xD9, 0x74, 0xEF, 0x2B, 0xFD, 0x3F, 0x7E, 0x67, 0xBD, 0xC3, 0x8D, 0x6B, 0x88, 0x9C, 0xC1, - 0xF5, 0x47, 0xDA, 0x7E, 0xD3, 0xD3, 0x1E, 0x48, 0x7E, 0xEE, 0xAA, 0x60, 0x84, 0xDA, 0x66, 0x2E, 0x89, 0x16, 0xBF, 0x3D, 0x14, 0x05, 0xE1, 0xC5, - 0x01, 0x49, 0x86, 0xFD, 0x10, 0x62, 0xF0, 0x71, 0x9D, 0xF7, 0x70, 0x1E, 0xF2, 0x08, 0x8F, 0xED, 0xE4, 0x7A, 0xEE, 0x7C, 0xD0, 0xE5, 0x48, 0xB7, - 0xC8, 0xE1, 0xA9, 0xE3, 0xA0, 0xB0, 0xB8, 0xD1, 0x52, 0x61, 0x63, 0xA4, 0x5B, 0x63, 0x00, 0x95, 0x86, 0x6C, 0x7A, 0xA6, 0xC2, 0xA3, 0xDB, 0x01, - 0x8C, 0x8C, 0x61, 0x6A, 0x0C, 0xA4, 0x30, 0x8C, 0xE7, 0x8B, 0x97, 0xD6, 0x0E, 0x97, 0x5D, 0x8F, 0x16, 0x64, 0x9C, 0x96, 0x75, 0x0E, 0x8B, 0xC7, - 0xC7, 0x0A, 0x4D, 0x95, 0xCA, 0x8E, 0x4E, 0x8A, 0xD2, 0x0E, 0x4A, 0x96, 0x15, 0x80, 0xB3, 0xAB, 0x13, 0x39, 0x37, 0x13, 0x08, 0x6F, 0x25, 0x7C, - 0x16, 0xDA, 0x23, 0x2E, 0x90, 0x30, 0xDA, 0x47, 0x2E, 0x0D, 0x45, 0xF0, 0x17, 0x42, 0x68, 0x3B, 0x3F, 0x88, 0xDE, 0xF0, 0x63, 0x6F, 0xC2, 0xC3, - 0x11, 0xD2, 0xA3, 0x8D, 0xE7, 0x88, 0x94, 0xE1, 0x78, 0xE7, 0x6E, 0xC0, 0x9F, 0x35, 0xE2, 0xD0, 0x19, 0x6C, 0x2C, 0x75, 0xF6, 0x82, 0x16, 0xFA, - 0xFA, 0x81, 0x7A, 0x4F, 0xFA, 0x8B, 0x70, 0x99, 0x5E, 0x7C, 0x91, 0xB6, 0x54, 0x8A, 0x26, 0x5D, 0x82, 0x9E, 0x0D, 0xF0, 0x0C, 0x6C, 0xE6, 0xCC, - 0xDC, 0xA0, 0xDD, 0xCA, 0xB6, 0xF2, 0x4F, 0xF9, 0x42, 0x16, 0xAD, 0xB8, 0xDC, 0x86, 0x24, 0x96, 0xED, 0x7A, 0x3C, 0x85, 0x99, 0x28, 0x75, 0xC2, - 0x44, 0x2C, 0x7B, 0x46, 0x0B, 0xBE, 0x44, 0xFD, 0xA3, 0x5D, 0xDE, 0xA7, 0x31, 0xEB, 0x3D, 0x75, 0x7C, 0x3C, 0x6C, 0x06, 0x71, 0x11, 0x66, 0x64, - 0xF3, 0x51, 0x77, 0x30, 0x4C, 0x8B, 0x63, 0xB8, 0xA5, 0x87, 0x77, 0xEC, 0xFD, 0x3F, 0x80, 0x7F, 0xDC, 0xFB, 0x9F, 0xC2, 0x7F, 0x17, 0xF7, 0x3F, - 0x08, 0xBD, 0x61, 0xC3, 0xF3, 0x30, 0x00, 0x67, 0xC6, 0x0D, 0xE7, 0x0F, 0xFD, 0x12, 0xDC, 0x47, 0x19, 0x4F, 0x27, 0x42, 0x04, 0x3F, 0x16, 0xF5, - 0xC6, 0x5D, 0x84, 0x0B, 0x0E, 0xBF, 0x9F, 0x6B, 0xE0, 0x97, 0x71, 0x6F, 0x16, 0x63, 0xFC, 0x70, 0x07, 0x3C, 0x49, 0x13, 0x9E, 0x6B, 0xE1, 0x7D, - 0x2C, 0x05, 0x8F, 0xF6, 0xBA, 0x93, 0x04, 0xD8, 0x53, 0xD0, 0xCF, 0x7C, 0x8E, 0x0E, 0xC7, 0x95, 0x14, 0x48, 0x2B, 0x6C, 0x0B, 0xE4, 0x1C, 0x97, - 0x79, 0x0E, 0x02, 0xB6, 0xD0, 0x74, 0x26, 0x9E, 0x51, 0xFB, 0x9C, 0x01, 0xF1, 0xCD, 0xBC, 0x73, 0xCC, 0x91, 0xB9, 0xCC, 0x6C, 0x0E, 0xCA, 0xAD, - 0xD0, 0xCA, 0x35, 0x29, 0x92, 0x83, 0x5B, 0x24, 0x4F, 0x95, 0xC2, 0xF0, 0x88, 0x46, 0x6E, 0xFB, 0x3D, 0xE6, 0x71, 0xD9, 0xE7, 0x69, 0x18, 0x6B, - 0x29, 0x15, 0x93, 0x08, 0x26, 0x36, 0x10, 0x60, 0x38, 0x93, 0xE3, 0x85, 0x86, 0x06, 0xC2, 0xB6, 0x37, 0xC1, 0x86, 0x67, 0xC0, 0xCF, 0x53, 0xBF, - 0x9E, 0x0F, 0x7E, 0x5D, 0xD8, 0x5C, 0xE8, 0x7E, 0x70, 0x31, 0x44, 0xD5, 0xAB, 0x96, 0xE4, 0x0D, 0x23, 0xB0, 0x3B, 0xC3, 0xAA, 0x4B, 0x47, 0xD7, - 0x47, 0x19, 0xB7, 0x00, 0x24, 0xCC, 0x35, 0x6B, 0xC5, 0x8D, 0xD4, 0xBC, 0xB5, 0xDB, 0xDB, 0xB6, 0xD6, 0xD6, 0x8E, 0x18, 0xA1, 0x0D, 0xE6, 0x9C, - 0xBD, 0xF4, 0x99, 0xF5, 0xB8, 0x8D, 0xA5, 0xD5, 0x8A, 0xDD, 0x71, 0x6E, 0x15, 0x5D, 0x10, 0x47, 0x85, 0xD3, 0x9C, 0xD6, 0x41, 0x4D, 0x75, 0x19, - 0x94, 0x2A, 0x2A, 0x96, 0xAA, 0xFF, 0x46, 0x47, 0x43, 0x47, 0x67, 0x1E, 0x43, 0x7F, 0x79, 0x43, 0x93, 0x3C, 0x80, 0x76, 0xA8, 0x59, 0x98, 0x41, - 0x7E, 0xEA, 0xC8, 0x78, 0xDA, 0xAD, 0xE2, 0xA1, 0xD8, 0x9D, 0x22, 0x88, 0x39, 0xD7, 0x66, 0x36, 0x2F, 0x41, 0x34, 0x29, 0x40, 0x65, 0xD2, 0xEE, - 0x60, 0x16, 0x0E, 0xB8, 0xDC, 0x87, 0x58, 0x76, 0x4A, 0xB0, 0x6C, 0xF3, 0x54, 0xB6, 0x18, 0x51, 0xF9, 0xC6, 0x9A, 0x54, 0xBE, 0x9B, 0x8C, 0xC3, - 0x49, 0xA6, 0x90, 0x88, 0x9C, 0xE7, 0x41, 0x1E, 0x59, 0xDE, 0xF7, 0x0A, 0x3C, 0x96, 0xC5, 0xAA, 0x50, 0xD4, 0x73, 0x74, 0x90, 0xDC, 0x38, 0x8A, - 0x98, 0x22, 0x84, 0x09, 0x8A, 0xDF, 0x4F, 0x46, 0x47, 0x65, 0x46, 0x0E, 0xBD, 0xA2, 0x2A, 0x00, 0xC5, 0x1C, 0x69, 0x79, 0xF1, 0xD0, 0x89, 0x27, - 0x5D, 0xC4, 0x91, 0xF8, 0xA1, 0xDA, 0xB4, 0x1E, 0x48, 0x81, 0x99, 0x21, 0x05, 0x18, 0x7B, 0x15, 0xD8, 0x6C, 0x61, 0x21, 0x93, 0xDE, 0x83, 0x1A, - 0xED, 0x9D, 0xED, 0xFF, 0xDF, 0x57, 0x57, 0x07, 0xF3, 0x47, 0xDA, 0x9F, 0xA3, 0x83, 0xD4, 0x05, 0xC0, 0xFB, 0x29, 0x7E, 0xB2, 0x36, 0x2D, 0x37, - 0x19, 0x8B, 0x97, 0x38, 0xD8, 0x6D, 0xD7, 0x29, 0x20, 0x09, 0xF1, 0xF5, 0x81, 0x7D, 0x23, 0x1F, 0x39, 0xFF, 0x66, 0x40, 0xBE, 0x2A, 0x98, 0xA8, - 0x7A, 0xD7, 0x95, 0x1A, 0x4B, 0x83, 0x1A, 0x68, 0x41, 0xCF, 0x7B, 0x9A, 0x07, 0x84, 0x33, 0xBE, 0x91, 0xFC, 0xFF, 0xD9, 0x19, 0xEC, 0x14, 0xF6, - 0xE9, 0xE9, 0x12, 0x6D, 0x6A, 0x6E, 0x02, 0x59, 0x63, 0x6C, 0x78, 0x4F, 0x72, 0x39, 0xBA, 0xB5, 0x0F, 0x34, 0x6A, 0x7C, 0xF5, 0x01, 0x94, 0xF0, - 0x34, 0x46, 0x15, 0x5F, 0x00, 0xAC, 0x1A, 0x1A, 0xFE, 0xEF, 0x2C, 0xC6, 0x9D, 0x99, 0xFE, 0xD1, 0x0C, 0x85, 0xC4, 0x3C, 0xC0, 0x4F, 0x2D, 0xF3, - 0x99, 0x40, 0xE6, 0x7E, 0x31, 0xC6, 0x25, 0x28, 0x16, 0xE5, 0x11, 0x86, 0xE0, 0x12, 0x8C, 0x2D, 0x27, 0xDE, 0x27, 0xFB, 0x65, 0x07, 0xE2, 0xD5, - 0x18, 0xA3, 0x4B, 0xC4, 0x0E, 0x57, 0xEA, 0xC9, 0x7E, 0xCB, 0x92, 0x4C, 0xAB, 0x57, 0x23, 0x6B, 0x96, 0x41, 0x56, 0x46, 0xCD, 0x15, 0xF3, 0x5F, - 0xFB, 0xCA, 0x26, 0xCA, 0x8A, 0xA4, 0xAE, 0x8A, 0x6A, 0x8D, 0x2A, 0x08, 0x70, 0xCA, 0x6D, 0x82, 0xD4, 0xD7, 0x3D, 0x2A, 0x4F, 0x10, 0x3E, 0xA7, - 0x0A, 0x25, 0x48, 0xE1, 0xFB, 0x0D, 0xD7, 0x33, 0x4B, 0xFC, 0xFB, 0x47, 0xBD, 0x92, 0xF5, 0xC7, 0x30, 0xA9, 0x3F, 0xD3, 0x26, 0xFD, 0x86, 0x24, - 0xB9, 0x2C, 0x7F, 0x24, 0x0E, 0x9E, 0xA3, 0xA1, 0x5A, 0xA3, 0x1C, 0xAC, 0x76, 0x18, 0x33, 0xFD, 0x0A, 0xAE, 0xCD, 0x20, 0x85, 0x1E, 0x97, 0xD7, - 0x3E, 0x15, 0x5E, 0x60, 0x19, 0xAD, 0xB7, 0xB7, 0x0C, 0x39, 0x5A, 0x11, 0xE5, 0x54, 0x09, 0x57, 0x26, 0x7F, 0x3C, 0xEE, 0x39, 0x51, 0x09, 0x3C, - 0xD7, 0xF8, 0xDF, 0xB6, 0xF7, 0x2B, 0x0B, 0xD0, 0x8B, 0x04, 0xD6, 0x3D, 0xC8, 0x2A, 0x11, 0xC8, 0x1E, 0xE2, 0x07, 0x0E, 0xD4, 0x07, 0x4F, 0xF9, - 0x3E, 0x30, 0x64, 0x17, 0x16, 0xB9, 0x07, 0x6E, 0xD8, 0xE4, 0xCD, 0x99, 0x21, 0x39, 0xF0, 0xE2, 0xF4, 0xD9, 0xA5, 0xFB, 0x38, 0x91, 0xDA, 0xB8, - 0x35, 0xC8, 0xDB, 0x74, 0x88, 0xB7, 0x95, 0x62, 0x37, 0x19, 0xCA, 0x36, 0xA5, 0x10, 0x02, 0x50, 0xAE, 0x6C, 0x67, 0x3C, 0xF2, 0xB5, 0xE3, 0x35, - 0x08, 0x58, 0x6A, 0x0E, 0x5E, 0x07, 0x8C, 0x80, 0x4B, 0x03, 0x81, 0x2C, 0xE0, 0x32, 0x0F, 0x6D, 0x45, 0xFB, 0x2C, 0x5F, 0xC0, 0x4C, 0x16, 0x28, - 0xC0, 0x5E, 0xAF, 0x6C, 0x99, 0xAB, 0xAC, 0x8D, 0x48, 0x36, 0xF2, 0x1E, 0x7B, 0x7F, 0x8A, 0x01, 0x79, 0x85, 0xC5, 0xF5, 0x60, 0x5E, 0x08, 0xE2, - 0x73, 0xE9, 0x2F, 0xFA, 0xB4, 0xD7, 0x9E, 0x6E, 0x97, 0x21, 0x0C, 0x97, 0xE2, 0xF4, 0x96, 0x27, 0xD3, 0xBB, 0x16, 0xD0, 0xF9, 0xDE, 0xA2, 0x5A, - 0x05, 0x63, 0x5E, 0x25, 0x14, 0x49, 0x75, 0xD2, 0x61, 0x0A, 0x2C, 0x2F, 0x45, 0xD4, 0x18, 0xB5, 0xA0, 0x18, 0x3E, 0x17, 0x01, 0x0A, 0xCD, 0xF1, - 0xC9, 0x4F, 0x84, 0xD3, 0x82, 0x7A, 0xD3, 0x63, 0x25, 0x53, 0xA7, 0xEB, 0x92, 0xE1, 0xA3, 0x52, 0x6C, 0x65, 0x92, 0x67, 0x2A, 0x92, 0xF2, 0xEB, - 0x91, 0xEE, 0x8B, 0xA3, 0x14, 0xE4, 0x22, 0xE6, 0xD5, 0x22, 0xA4, 0x36, 0x11, 0x49, 0xE4, 0x50, 0x00, 0x20, 0xA1, 0xA3, 0x02, 0x6F, 0x05, 0x26, - 0x41, 0x5F, 0xC5, 0x40, 0x7D, 0xB9, 0x3F, 0xF3, 0x10, 0x9F, 0x14, 0x29, 0xE0, 0x97, 0x1E, 0xEF, 0xD5, 0x4D, 0x7C, 0xB1, 0x86, 0x1C, 0x65, 0x13, - 0x39, 0x1B, 0x9D, 0x38, 0x28, 0x0A, 0x6E, 0xA2, 0x98, 0x6C, 0x8A, 0x98, 0x6A, 0xA8, 0xCC, 0x68, 0xC4, 0x38, 0x66, 0xD8, 0x5C, 0xDD, 0x73, 0x0B, - 0x32, 0xF4, 0xC2, 0x18, 0xBF, 0x2F, 0x98, 0x42, 0x04, 0x2F, 0x2A, 0x5C, 0x5C, 0x38, 0x93, 0x5A, 0x50, 0x81, 0x59, 0x6C, 0x25, 0x57, 0x84, 0xDB, - 0x54, 0x9C, 0xE9, 0x2A, 0x1E, 0x41, 0x29, 0x2C, 0x5C, 0x28, 0x39, 0xD0, 0x00, 0x92, 0x58, 0x4E, 0xAC, 0x48, 0x4D, 0xC8, 0x02, 0x4B, 0xDC, 0xA6, - 0xDA, 0x75, 0x72, 0x12, 0x34, 0xA1, 0x11, 0x0F, 0x3F, 0x70, 0x11, 0x3B, 0xE9, 0x77, 0x08, 0x64, 0x33, 0x48, 0x08, 0xD3, 0x8F, 0xD5, 0x3A, 0x3C, - 0x07, 0xF4, 0x5E, 0xA3, 0xA7, 0x16, 0xAE, 0x12, 0xA3, 0x1E, 0xC7, 0x7C, 0xE9, 0x95, 0xC1, 0xE9, 0xDF, 0xC2, 0x4F, 0x59, 0x11, 0xA1, 0xFE, 0x47, - 0xD5, 0x62, 0x35, 0xCB, 0x92, 0x2C, 0xC2, 0x3A, 0x76, 0x00, 0x77, 0x0D, 0xBB, 0x7B, 0x80, 0xB6, 0xEB, 0x5C, 0xB9, 0x44, 0x9F, 0x98, 0xEC, 0x04, - 0x8D, 0x62, 0xBF, 0xE1, 0x98, 0x85, 0x77, 0x48, 0xCD, 0xDF, 0xF8, 0xCA, 0xF4, 0x3B, 0x5F, 0xDC, 0x9C, 0xFE, 0x3F, 0xFC, 0x56, 0x9D, 0x77, 0x51, - 0x65, 0x4F, 0xF0, 0xFB, 0x89, 0x2E, 0xFC, 0x8E, 0x75, 0x77, 0x8E, 0xC3, 0xA7, 0xB2, 0x60, 0x29, 0x82, 0x47, 0x66, 0x38, 0x65, 0x78, 0x1E, 0xBF, - 0x71, 0x70, 0x72, 0x52, 0x29, 0xEF, 0x3E, 0x4F, 0x3F, 0x43, 0x83, 0xBD, 0xFE, 0xFB, 0x54, 0xA4, 0x3A, 0xC5, 0x1A, 0x76, 0x6D, 0xEF, 0x3C, 0x01, - 0xF0, 0x2A, 0x2C, 0xD7, 0xFF, 0x33, 0xFD, 0xB5, 0x9A, 0xF4, 0xC3, 0xFD, 0xA9, 0x69, 0x78, 0xD3, 0x7A, 0xED, 0x4B, 0xFD, 0xA8, 0xCF, 0x31, 0x38, - 0x2C, 0x08, 0x1E, 0x9E, 0x79, 0xFC, 0x9E, 0x86, 0x3F, 0xFA, 0xB0, 0xD1, 0x3E, 0xFC, 0xAF, 0x47, 0xDA, 0xA3, 0xF8, 0xCF, 0xA2, 0xB4, 0x28, 0x4B, - 0x93, 0x0D, 0xCB, 0xD9, 0xC4, 0xD5, 0x2D, 0x85, 0xAB, 0x07, 0x1E, 0xF7, 0x4E, 0x9E, 0x7B, 0xCF, 0x88, 0x00, 0x6E, 0x1E, 0xBD, 0xEB, 0xCE, 0x7D, - 0x69, 0xE7, 0x1F, 0xE7, 0xE4, 0xCD, 0x72, 0x66, 0x48, 0xDE, 0x37, 0x7B, 0x07, 0x37, 0x48, 0xC8, 0x36, 0xED, 0xC0, 0x9A, 0x40, 0xCA, 0x7B, 0x87, - 0xCD, 0x1C, 0xC7, 0xA1, 0xD6, 0x74, 0x5F, 0x91, 0xF4, 0xA0, 0x51, 0x74, 0x63, 0x18, 0xB4, 0xA6, 0xD8, 0x33, 0x0B, 0x23, 0x67, 0xD7, 0xA3, 0xE6, - 0xE4, 0x07, 0xCC, 0xA3, 0xAE, 0xCC, 0x27, 0x53, 0xCA, 0xBE, 0x85, 0xCA, 0x43, 0xED, 0x18, 0x1A, 0x08, 0x92, 0x0B, 0x95, 0x93, 0x9D, 0x3A, 0x5E, - 0xA7, 0xDC, 0x5C, 0x77, 0x8C, 0x54, 0xEF, 0xC4, 0xAA, 0x5D, 0xD3, 0xA6, 0xC5, 0x7A, 0xA6, 0x5D, 0x86, 0xA4, 0x15, 0x02, 0x47, 0xF5, 0x8F, 0xA3, - 0x51, 0x48, 0xA1, 0xD2, 0x3C, 0xA1, 0xF2, 0x0C, 0xD0, 0x17, 0x65, 0x16, 0x78, 0xF9, 0x4F, 0x26, 0x16, 0x9E, 0x25, 0x4A, 0xE0, 0xD1, 0xFC, 0x3A, - 0x33, 0x57, 0x38, 0xED, 0x3E, 0x36, 0x69, 0x7B, 0x35, 0x41, 0xC8, 0x66, 0x32, 0x43, 0x62, 0x52, 0x1E, 0x62, 0xAA, 0x4C, 0xC0, 0x5B, 0xBE, 0xCE, - 0x2D, 0x63, 0xFC, 0x56, 0xB4, 0x94, 0xDE, 0x2B, 0x25, 0x84, 0xC1, 0x4D, 0x00, 0xFA, 0xBD, 0xE9, 0x06, 0x17, 0xB9, 0x36, 0x60, 0xA7, 0xA7, 0x5B, - 0xF3, 0x60, 0x6E, 0xA5, 0xF8, 0x85, 0xB7, 0x27, 0xAF, 0xBD, 0x13, 0xE8, 0x5F, 0xD3, 0xDC, 0x08, 0x13, 0xE9, 0xF4, 0x92, 0x9F, 0x8F, 0x52, 0x0B, - 0xDE, 0x24, 0x4B, 0x0C, 0x48, 0xCA, 0xA2, 0x47, 0xAB, 0x05, 0x8E, 0xDC, 0x32, 0x45, 0xCC, 0x9E, 0x45, 0x49, 0x63, 0x44, 0x1B, 0x06, 0x86, 0x02, - 0xB8, 0x85, 0x60, 0xBA, 0xDE, 0x74, 0x08, 0x37, 0xAF, 0xA3, 0x37, 0x48, 0xC8, 0x35, 0xBB, 0x73, 0x77, 0x8D, 0x59, 0x9B, 0x06, 0x84, 0x74, 0xA6, - 0x82, 0xDC, 0xDB, 0x4D, 0xB4, 0x2A, 0x63, 0x37, 0xBA, 0xC7, 0xD8, 0xC9, 0xC2, 0x94, 0x76, 0x80, 0xDE, 0x30, 0xE1, 0x79, 0x78, 0xC3, 0x79, 0x91, - 0xD9, 0xF9, 0xA3, 0x95, 0x79, 0x34, 0x94, 0x19, 0xCC, 0x67, 0x79, 0x24, 0x46, 0x19, 0xEF, 0x16, 0x43, 0x43, 0x2D, 0x39, 0xC4, 0xB4, 0x7E, 0x8C, - 0xA8, 0x05, 0x2E, 0x89, 0x5C, 0xC1, 0x5C, 0xDC, 0x67, 0x94, 0xDB, 0xCB, 0xB3, 0x5B, 0x16, 0xDD, 0xFB, 0x0B, 0xE7, 0xDA, 0x24, 0x15, 0x98, 0xB9, - 0x75, 0xEE, 0xCB, 0xE3, 0x22, 0xCB, 0x68, 0xB8, 0xDD, 0x92, 0x24, 0x4B, 0x0F, 0x02, 0x45, 0xDD, 0x9B, 0x68, 0x8D, 0xE0, 0x37, 0xFD, 0x0C, 0x3B, - 0x50, 0xE6, 0x2F, 0xC2, 0xFD, 0x75, 0xDF, 0x54, 0x12, 0xEC, 0x41, 0x2A, 0xEE, 0x6E, 0x23, 0xEC, 0x5F, 0xA8, 0xF7, 0xBC, 0x15, 0xF6, 0x39, 0xE1, - 0xBB, 0xA0, 0xA0, 0xBB, 0xDE, 0x71, 0xCF, 0xBC, 0x74, 0xDF, 0x71, 0xEE, 0xB6, 0xB2, 0xEE, 0xD1, 0x8F, 0xF7, 0x73, 0x0C, 0xF7, 0xAF, 0xCE, 0xFB, - 0xE5, 0xC4, 0xCD, 0x61, 0xF5, 0x1E, 0x52, 0xB6, 0x7F, 0xB7, 0xF7, 0x07, 0x60, 0xFF, 0x16, 0xDE, 0x7F, 0xC8, 0xFE, 0xED, 0xEF, 0x1F, 0xB7, 0xFF, - 0x0B, 0xF7, 0xAF, 0xC0, 0xFE, 0x0D, 0xEF, 0xBF, 0xCF, 0xFE, 0x3B, 0xEF, 0xC2, 0x73, 0x77, 0x2E, 0xEE, 0xFD, 0xA3, 0x83, 0x5B, 0xFA, 0xF9, 0xCE, - 0xD3, 0xE8, 0x46, 0x69, 0xF1, 0xD6, 0x32, 0x75, 0x1B, 0x45, 0xBF, 0x3F, 0xB0, 0xFB, 0xBB, 0xBC, 0xFF, 0xA9, 0xFD, 0x2B, 0xBD, 0x7F, 0xB0, 0xFD, - 0x6F, 0xDC, 0x3F, 0x87, 0xFD, 0xDB, 0xDC, 0xFF, 0x8D, 0xFD, 0x4B, 0xDD, 0x3F, 0xD8, 0xFD, 0x17, 0xC1, 0xFF, 0xDC, 0xFD, 0x87, 0xC1, 0xBF, 0xD8, - 0xFB, 0xCF, 0xE1, 0x7F, 0xC1, 0xFB, 0x77, 0x80, 0x7F, 0xBF, 0xF7, 0x6F, 0x84, 0x7F, 0xC7, 0xF7, 0x37, 0x85, 0xFF, 0xD3, 0xF7, 0x77, 0xC4, 0xFF, - 0xE0, 0xF7, 0x3F, 0xC5, 0x7F, 0xD4, 0xFB, 0xB7, 0x85, 0x7F, 0xE1, 0xFB, 0x5F, 0xC3, 0xFF, 0x8E, 0xFB, 0xBF, 0xC1, 0x7F, 0x9C, 0xFB, 0x9F, 0xC2, - 0x7F, 0xAA, 0xFB, 0xCF, 0x81, 0x7F, 0x9D, 0xF7, 0xAF, 0x82, 0x2F, 0x5A, 0xBD, 0x2D, 0xDB, 0xE0, 0x6C, 0x52, 0x6F, 0xBE, 0x23, 0xFC, 0x5E, 0x14, - 0x56, 0x85, 0xB9, 0x46, 0xC6, 0x3A, 0xC1, 0x8F, 0x7F, 0xA6, 0xFF, 0xFF, 0x48, 0xFB, 0x6D, 0x7A, 0x1A, 0x8D, 0xC0, 0x4F, 0xB1, 0x7B, 0xF5, 0x93, - 0x58, 0x05, 0xC3, 0xB8, 0x8A, 0x8A, 0x8F, 0x2A, 0x48, 0x73, 0xCA, 0x4D, 0x51, 0xEA, 0xAB, 0x36, 0x2A, 0x2F, 0x14, 0x3E, 0xC7, 0x0C, 0x25, 0xC8, - 0xF8, 0x54, 0x4F, 0x71, 0xCA, 0x1C, 0x6D, 0xBA, 0x17, 0x31, 0xD9, 0xBA, 0x58, 0x1A, 0x5D, 0x51, 0x82, 0x66, 0x27, 0xFD, 0x1E, 0x25, 0xBD, 0xCC, - 0x20, 0x71, 0x8C, 0x1D, 0x11, 0x65, 0x18, 0xE5, 0xD0, 0x15, 0x89, 0xD1, 0x10, 0x5D, 0xD5, 0x87, 0x18, 0xC0, 0x85, 0xE2, 0x54, 0xBC, 0x3D, 0xA8, - 0x05, 0xE1, 0xFD, 0x01, 0x49, 0xDE, 0xFD, 0x10, 0x3E, 0xFA, 0xB8, 0x92, 0xE8, 0xE1, 0x4C, 0xE5, 0x11, 0xC6, 0xDB, 0xC9, 0x7D, 0xB1, 0xF3, 0x31, - 0x98, 0x33, 0xED, 0x91, 0xC3, 0x83, 0x89, 0x83, 0x42, 0x7C, 0xA3, 0x35, 0x6B, 0x73, 0x04, 0x52, 0x63, 0x40, 0xF9, 0x4D, 0x53, 0xB6, 0x33, 0x53, - 0x81, 0xD6, 0xED, 0xE0, 0x4A, 0xC6, 0x70, 0x39, 0x26, 0x12, 0x00, 0xCC, 0x27, 0xCA, 0x4B, 0xEB, 0x90, 0xCB, 0x2E, 0xD1, 0x1D, 0x39, 0xDE, 0xB2, - 0x5E, 0x63, 0xF1, 0x90, 0x5A, 0xA1, 0x99, 0xA0, 0xB2, 0x23, 0x4F, 0x51, 0x62, 0x43, 0xC9, 0xC2, 0x03, 0x70, 0x46, 0xE1, 0x44, 0x6E, 0xD0, 0x04, - 0x42, 0x89, 0x12, 0x3E, 0xCC, 0xF6, 0x88, 0x4C, 0x12, 0xC6, 0xD9, 0x91, 0x4B, 0x5A, 0x11, 0x7C, 0x66, 0x08, 0xED, 0x32, 0x88, 0x9E, 0xF6, 0x63, - 0x5F, 0xEB, 0xE1, 0x18, 0xEC, 0xD1, 0x66, 0xE6, 0xE0, 0x90, 0x80, 0x43, 0x28, 0x65, 0x23, 0x8A, 0x48, 0x03, 0xAC, 0x28, 0x83, 0x0D, 0x2C, 0xCF, - 0x5E, 0x10, 0x86, 0x5F, 0xDB, 0x19, 0xC5, 0xD5, 0x19, 0x8E, 0xE0, 0x5F, 0x84, 0xD8, 0xF4, 0xE2, 0x98, 0xB4, 0xE5, 0x82, 0x38, 0x5B, 0x1C, 0xCE, - 0x47, 0x95, 0x9F, 0xB0, 0x87, 0xA6, 0x06, 0xF5, 0x19, 0xCD, 0xFA, 0xFF, 0x3F, 0xF9, 0x9F, 0x63, 0x76, 0x7A, 0x9A, 0x81, 0xFC, 0x87, 0xFD, 0xFF, - 0x33, 0xD8, 0xED, 0x8E, 0x62, 0x38, 0xF9, 0x59, 0x8A, 0x31, 0x33, 0x3D, 0x54, 0x80, 0x53, 0xBA, 0x00, 0x7F, 0x00, 0xD9, 0x8B, 0x3A, 0x99, 0x5B, - 0xC2, 0x18, 0x88, 0x0A, 0x31, 0xF8, 0x88, 0x30, 0x9A, 0x3F, 0x30, 0xF9, 0x8A, 0xB8, 0xD4, 0xE3, 0xD7, 0x03, 0x12, 0x5E, 0x04, 0x52, 0x2D, 0xBC, - 0xD8, 0x5A, 0x11, 0xAB, 0x75, 0xE8, 0x91, 0x25, 0x9B, 0x8C, 0xC5, 0x22, 0xE7, 0x8A, 0xD9, 0xC4, 0xAA, 0x16, 0xC4, 0x6A, 0x44, 0x99, 0xCA, 0x79, - 0x87, 0x2A, 0x08, 0xC0, 0x54, 0xCB, 0x95, 0xD4, 0xD7, 0x50, 0xD4, 0xD2, 0x1B, 0x7C, 0x2E, 0x0E, 0x85, 0x76, 0xE1, 0xC9, 0x4F, 0x81, 0xD3, 0x02, - 0xB8, 0xA6, 0xC7, 0x3E, 0xA6, 0xBF, 0xBC, 0x25, 0xD1, 0x39, 0x25, 0x48, 0x13, 0x49, 0xBE, 0x1C, 0x90, 0x58, 0xF1, 0x48, 0x1B, 0x29, 0x8C, 0x8E, - 0x73, 0x8D, 0x70, 0x4A, 0x8B, 0x4A, 0xA0, 0xEB, 0x44, 0x56, 0xBF, 0x43, 0x3A, 0x7A, 0xA1, 0x38, 0x0D, 0xFA, 0x1B, 0x4B, 0x3A, 0x68, 0x42, 0x30, - 0x90, 0xDC, 0xCB, 0x4F, 0xDF, 0xA8, 0x7C, 0xCB, 0xCE, 0xE8, 0xF7, 0x65, 0xB1, 0x3D, 0xAF, 0x8A, 0x7D, 0xAF, 0x6A, 0x5A, 0x58, 0x69, 0x94, 0x56, - 0x4D, 0x45, 0xB7, 0xA9, 0x6E, 0x7E, 0xEB, 0x53, 0x4D, 0xF4, 0x2F, 0x55, 0x27, 0x92, 0xA3, 0x91, 0xF3, 0xF2, 0x61, 0xE3, 0xC3, 0x52, 0xD3, 0xA4, - 0x43, 0x73, 0xB8, 0xD4, 0xAE, 0x6B, 0xE1, 0x6B, 0x1C, 0x6D, 0xB1, 0xF7, 0x4A, 0x91, 0xF3, 0xF1, 0xEA, 0x55, 0x4D, 0x18, 0x7D, 0xD4, 0x7B, 0xFB, - 0x33, 0xE5, 0xA7, 0x32, 0xAA, 0xD4, 0x51, 0x7F, 0xB7, 0xDA, 0xA7, 0x8E, 0x72, 0x7B, 0xD3, 0xDD, 0xB1, 0x51, 0x5B, 0x9A, 0x51, 0x33, 0x42, 0xB9, - 0xE8, 0x52, 0x06, 0xFD, 0x99, 0xD5, 0x49, 0x32, 0xCD, 0x5F, 0xF9, 0x49, 0xFF, 0xC5, 0x1D, 0x92, 0x16, 0x26, 0x37, 0x09, 0x36, 0x46, 0x1B, 0x2F, - 0x31, 0xE2, 0x17, 0x19, 0x19, 0x20, 0xA2, 0x3A, 0x1A, 0xF2, 0x3A, 0x96, 0xC3, 0x57, 0xEC, 0x6F, 0x90, 0xE4, 0x45, 0xEB, 0xFD, 0x67, 0xD3, 0x84, - 0xAD, 0xE2, 0xE9, 0xE7, 0x5A, 0x16, 0x8A, 0xE5, 0x1E, 0x40, 0xE6, 0x85, 0x2A, 0xFA, 0xC9, 0x08, 0xFB, 0xCD, 0x5E, 0x39, 0xEA, 0xE3, 0x9A, 0x7C, - 0x45, 0x98, 0x47, 0xC5, 0xB9, 0x47, 0xAF, 0xD2, 0xB1, 0x9C, 0x02, 0x28, 0xF5, 0xC2, 0x4B, 0x74, 0xCF, 0x88, 0xEA, 0x22, 0x08, 0x6A, 0xFE, 0xFB, - 0xA6, 0xD7, 0xB5, 0x6B, 0xCD, 0xAD, 0x71, 0x94, 0xE3, 0x66, 0x42, 0x1A, 0x22, 0x7B, 0xDA, 0xAC, 0x97, 0xDB, 0xFE, 0x46, 0x3C, 0x8D, 0x27, 0xB3, - 0x8E, 0x96, 0x95, 0xA1, 0x03, 0x5D, 0xAD, 0x29, 0xFA, 0xFD, 0xFF, 0x7F, 0xC1, 0xC9, 0x85, 0x5B, 0x30, 0x07, 0xF4, 0xA5, 0xBB, 0xB5, 0xD7, 0x20, - 0x6B, 0x0E, 0xB5, 0xDF, 0x7F, 0x7F, 0xA0, 0xEF, 0xFE, 0x2B, 0x5F, 0xE5, 0x7F, 0x7C, 0x8F, 0xCF, 0x81, 0xD3, 0xF8, 0x6B, 0x74, 0x3E, 0x4D, 0x1E, - 0x2A, 0x82, 0x08, 0xAF, 0x9E, 0xC4, 0x8E, 0x93, 0xA3, 0x7A, 0xC8, 0xE2, 0x5A, 0xB3, 0x0D, 0xD3, 0xAC, 0xD0, 0x21, 0xFF, 0x6B, 0xC4, 0x00, 0x9C, - 0x8E, 0xD1, 0x6B, 0x8A, 0xA6, 0xC0, 0xBE, 0x86, 0x60, 0x50, 0xAD, 0x9E, 0xF0, 0x7B, 0xFE, 0xCE, 0xD7, 0xF9, 0x34, 0x1F, 0xE6, 0xBF, 0xFC, 0x92, - 0x4F, 0xA2, 0x81, 0x83, 0x14, 0xAC, 0x83, 0xFF, 0xD7, 0xEB, 0xAF, 0xFE, 0x46, 0x7F, 0xE8, 0xF3, 0xFC, 0x9C, 0x7F, 0xF3, 0x6D, 0x7E, 0xCD, 0x67, - 0xF9, 0xED, 0x1F, 0xC2, 0xD1, 0xF1, 0xD9, 0x41, 0x86, 0x60, 0xC6, 0x76, 0x20, 0x50, 0x4C, 0xF5, 0xC5, 0xE5, 0x5F, 0x2F, 0xC5, 0x9C, 0xF5, 0xE3, - 0xFF, 0x8C, 0x18, 0xFA, 0x19, 0x73, 0xEF, 0xFF, 0x6A, 0xF6, 0xE7, 0x79, 0x23, 0xCA, 0x55, 0x5A, 0x92, 0xE8, 0x55, 0x6E, 0x48, 0xBC, 0x3C, 0x6B, - 0x02, 0x7D, 0x7E, 0x2B, 0x7E, 0xBC, 0x41, 0x4F, 0x2C, 0x1A, 0xEF, 0x98, 0x5B, 0x09, 0x01, 0xCC, 0xEB, 0x70, 0x90, 0xF7, 0xAC, 0xC5, 0xB1, 0x52, - 0xC8, 0xB5, 0x7A, 0xF2, 0x05, 0x4A, 0xE5, 0x71, 0x89, 0xF9, 0x0C, 0xBF, 0x72, 0x3D, 0x06, 0x71, 0x46, 0x0D, 0xF9, 0x09, 0xA8, 0x7C, 0x27, 0xB0, - 0x1E, 0x34, 0x24, 0xAE, 0xDF, 0xD0, 0xEB, 0xEE, 0xC5, 0xBF, 0x8D, 0xC7, 0x17, 0xCB, 0xBB, 0xA5, 0xF4, 0x32, 0xEE, 0x38, 0x5C, 0xAE, 0x77, 0x1A, - 0xCE, 0xCB, 0xF1, 0xF4, 0x02, 0xE2, 0xF2, 0x13, 0x9A, 0xF5, 0x11, 0x8A, 0xE2, 0x84, 0x2A, 0xFA, 0xCA, 0xC3, 0xDA, 0xDB, 0x1A, 0xF5, 0xFF, 0xF9, - 0x3E, 0x3F, 0x06, 0x65, 0xE5, 0xB7, 0xAC, 0xE2, 0x81, 0xA9, 0x76, 0x50, 0x90, 0x4A, 0x38, 0x87, 0xAA, 0xC2, 0x82, 0xD5, 0x6A, 0x81, 0x04, 0x12, - 0xA2, 0xE0, 0xEC, 0xFF, 0x37, 0xD8, 0xC7, 0xFB, 0x69, 0x18, 0x76, 0x10, 0xD6, 0x7B, 0x5D, 0x13, 0x6B, 0xAF, 0x6F, 0xFA, 0xFF, 0xFF, 0x0C, 0xF6, - 0x73, 0xC6, 0x10, 0xDD, 0xA4, 0xD0, 0x00, 0xDA, 0xB7, 0x9B, 0x63, 0x50, 0x9D, 0x26, 0x50, 0xDE, 0xE9, 0x4F, 0x60, 0xAD, 0x0F, 0x62, 0x70, 0xCF, - 0x24, 0x33, 0x4F, 0x28, 0xC7, 0x0E, 0x6C, 0x8A, 0x8E, 0x8B, 0x9B, 0x9C, 0x18, 0x45, 0x39, 0x1A, 0x53, 0x75, 0x9C, 0x90, 0x95, 0x9B, 0x97, 0x1C, - 0x1B, 0x9F, 0xA3, 0x9A, 0xA6, 0x2A, 0x1A, 0xAE, 0xB1, 0x99, 0xB4, 0x36, 0x37, 0x02, 0x3A, 0x99, 0xBB, 0x0A, 0xED, 0xBE, 0x7A, 0x3B, 0x72, 0xFE, - 0xA6, 0x43, 0xF2, 0x45, 0xB8, 0xEF, 0x01, 0x78, 0xB4, 0x7F, 0xA9, 0x50, 0x1C, 0x72, 0x2C, 0x55, 0xDC, 0xFF, 0x2A, 0xF8, 0x28, 0x34, 0x8C, 0x0B, - 0x0D, 0x15, 0xC8, 0x09, 0x63, 0x1B, 0xE0, 0x1D, 0x51, 0xFE, 0xCF, 0x91, 0xEA, 0xC3, 0x66, 0xEE, 0x35, 0xF0, 0xF8, 0x6E, 0x77, 0x76, 0xC9, 0xFD, - 0xE9, 0xC4, 0x1D, 0xEB, 0xE3, 0x30, 0x2B, 0xC0, 0xB9, 0x24, 0xBD, 0x11, 0x03, 0x70, 0xA3, 0xFA, 0x73, 0x4B, 0x41, 0x40, 0xE0, 0x81, 0x0D, 0x01, - 0xA8, 0x50, 0xB9, 0xFD, 0x4E, 0x4E, 0x67, 0xA6, 0xE6, 0x4A, 0xA2, 0x26, 0x78, 0x4F, 0x83, 0x2E, 0x90, 0xA6, 0x3A, 0x25, 0xE0, 0x06, 0x8F, 0xB6, - 0x3C, 0x88, 0xB6, 0x62, 0x6D, 0x8F, 0x71, 0xE4, 0x36, 0x13, 0x3B, 0x73, 0x27, 0x9B, 0xC9, 0xBA, 0xCD, 0x50, 0xC6, 0x02, 0x54, 0x68, 0x0C, 0x20, - 0xB6, 0x57, 0xB6, 0xFC, 0x54, 0xE6, 0x83, 0x44, 0x96, 0x22, 0x83, 0xEC, 0x4E, 0x4F, 0xA6, 0x92, 0x36, 0xC6, 0x45, 0x26, 0x76, 0x6B, 0x25, 0xA6, - 0x62, 0x46, 0x0C, 0x29, 0x42, 0x18, 0x02, 0x06, 0x61, 0x1A, 0x84, 0x60, 0xB3, 0xA7, 0x60, 0x77, 0x16, 0xE2, 0x14, 0xED, 0x42, 0x24, 0xDC, 0xF1, - 0x8E, 0xCC, 0xAF, 0x41, 0xBB, 0xDD, 0xAB, 0xA8, 0xAF, 0x2B, 0x84, 0x25, 0xD7, 0x00, 0x17, 0x5B, 0xB7, 0x78, 0x3A, 0x07, 0x58, 0xAD, 0x9A, 0x96, - 0x9F, 0x49, 0x83, 0x15, 0xE5, 0x1B, 0xB3, 0x32, 0xD7, 0xB2, 0x22, 0xF1, 0xB7, 0x73, 0x0E, 0x49, 0x5C, 0xC1, 0x46, 0x3A, 0x20, 0xCF, 0x45, 0x4A, - 0xA1, 0xE8, 0x21, 0x6C, 0x57, 0x20, 0xC0, 0x21, 0x5F, 0x34, 0x41, 0xC7, 0x81, 0xBC, 0xE7, 0x4F, 0xE3, 0xE7, 0x1B, 0x3A, 0x78, 0x71, 0x8E, 0xAD, - 0x11, 0xE2, 0x6F, 0x35, 0x1C, 0xDA, 0xD3, 0x12, 0x8F, 0x9A, 0x35, 0xCE, 0x45, 0xA7, 0x01, 0xD0, 0x41, 0x80, 0x35, 0x0F, 0xFE, 0x9F, 0xF1, 0xFF, - 0xBF, 0xFA, 0x6B, 0xB5, 0x3A, 0xF0, 0x9B, 0xFC, 0x17, 0xF7, 0x87, 0x21, 0x7B, 0x5B, 0x72, 0x75, 0xD3, 0xDA, 0x8F, 0xD9, 0x58, 0x10, 0xAB, 0xD5, - 0x14, 0x6B, 0xE4, 0x13, 0x6B, 0x90, 0xC4, 0x85, 0xE8, 0xCC, 0xC5, 0x67, 0xC6, 0xC5, 0xB6, 0xB9, 0x55, 0xAB, 0xD6, 0xE2, 0x70, 0x99, 0xB5, 0xEF, - 0xC5, 0x5A, 0x51, 0x82, 0x55, 0xB2, 0xB6, 0xD2, 0x1B, 0xB0, 0x12, 0x72, 0x57, 0x99, 0x44, 0x53, 0x81, 0x3D, 0x4D, 0x35, 0x91, 0x49, 0x2D, 0x48, - 0x8B, 0x4A, 0x63, 0x00, 0xFF, 0x8B, 0x00, 0xC5, 0x20, 0xC0, 0xD3, 0xA2, 0x9A, 0x93, 0x87, 0x06, 0x4D, 0xA4, 0x77, 0x4C, 0x92, 0x1E, 0x96, 0x80, - 0x49, 0x28, 0xB9, 0x42, 0x2A, 0x75, 0x58, 0x22, 0xC1, 0x58, 0x8D, 0x23, 0xE9, 0xC4, 0xCA, 0x8F, 0x3E, 0x22, 0x8D, 0xA0, 0x2E, 0x8B, 0x78, 0xF2, - 0x44, 0x6B, 0xB2, 0x43, 0x5A, 0xC5, 0x42, 0x14, 0x62, 0xBC, 0x0F, 0x84, 0xE4, 0x83, 0xEC, 0x27, 0x7A, 0xA0, 0xBE, 0xC4, 0x9F, 0xCF, 0x5D, 0x9F, - 0x14, 0x0C, 0x7A, 0x74, 0x1F, 0x79, 0x4C, 0xA1, 0x78, 0xBE, 0x04, 0x9D, 0x5F, 0x4C, 0x1D, 0x18, 0xE5, 0x9C, 0x8D, 0x3D, 0x9C, 0x26, 0x16, 0x37, - 0x37, 0x62, 0x36, 0x04, 0x72, 0x6A, 0xA8, 0xDC, 0x34, 0x11, 0xD2, 0x66, 0xD8, 0x04, 0xDD, 0x21, 0x64, 0x65, 0xE8, 0x71, 0x31, 0x95, 0x87, 0x30, - 0x85, 0x45, 0x2F, 0xB0, 0x73, 0x17, 0xCE, 0xDF, 0x92, 0xDC, 0xC4, 0x62, 0x2B, 0x81, 0x65, 0x7E, 0xA8, 0xE2, 0x8C, 0xA5, 0x62, 0xF7, 0x90, 0xC2, - 0x02, 0x00, 0xE5, 0x2B, 0x0E, 0x00, 0x49, 0xF7, 0x44, 0x01, 0x8C, 0x09, 0x59, 0x5A, 0xC9, 0x06, 0x11, 0xED, 0x3A, 0x1D, 0x12, 0x14, 0x6E, 0x23, - 0x80, 0x31, 0x44, 0x4A, 0x85, 0x42, 0x20, 0x3D, 0x41, 0x7E, 0xA3, 0x3F, 0x56, 0x9B, 0x3E, 0x34, 0xEC, 0x3A, 0x8A, 0x3D, 0x3A, 0x6C, 0xAA, 0x38, - 0x42, 0x60, 0x37, 0xA6, 0x4A, 0xF6, 0x1A, 0x3D, 0x26, 0x32, 0xD8, 0x72, 0xD9, 0x59, 0x30, 0x6B, 0x0A, 0x67, 0x73, 0x75, 0x5C, 0xF0, 0x27, 0xBC, - 0x99, 0xD6, 0xAB, 0x94, 0xD6, 0x44, 0x8E, 0x3E, 0x61, 0x01, 0xDA, 0x96, 0xF3, 0xF9, 0x33, 0xDA, 0xB9, 0x59, 0xA9, 0x59, 0xD7, 0xB4, 0x79, 0x65, - 0x63, 0x79, 0xBA, 0x6A, 0xF9, 0xA8, 0x55, 0x39, 0xA6, 0x24, 0x06, 0x59, 0x61, 0x32, 0x53, 0x1F, 0x0C, 0x06, 0x2D, 0xC8, 0x68, 0x67, 0xC7, 0x7F, - 0xBE, 0x7B, 0x08, 0x37, 0x74, 0xE3, 0xBB, 0xE4, 0xBC, 0x38, 0x04, 0x32, 0xF1, 0x2A, 0x85, 0x61, 0xCA, 0x04, 0xFF, 0xC5, 0xB0, 0xF3, 0x1E, 0x56, - 0x2E, 0x0C, 0x72, 0x09, 0x9C, 0xFC, 0x03, 0x7C, 0xD4, 0xD6, 0xAA, 0xC1, 0x31, 0xCE, 0x9A, 0x27, 0xCF, 0x33, 0x42, 0x58, 0xF5, 0x83, 0x53, 0x15, - 0x4D, 0x4E, 0x6D, 0xD4, 0x45, 0x6D, 0xB1, 0x73, 0x7A, 0xAA, 0x42, 0x6E, 0x3A, 0x0A, 0x5C, 0x0A, 0x6D, 0xAC, 0x77, 0x96, 0x90, 0x27, 0xF5, 0x30, - 0xA9, 0x9F, 0xB0, 0xD9, 0x95, 0xE2, 0xFF, 0xFF, 0xF1, 0x3F, 0x07, 0xF0, 0xFF, 0x2F, 0xEE, 0x5F, 0xE1, 0xAF, 0xD5, 0xFB, 0xE9, 0xE9, 0x63, 0x37, - 0x8F, 0x38, 0xB6, 0xB6, 0xEF, 0x60, 0x9D, 0x9D, 0xA1, 0xD6, 0xC8, 0x58, 0xAB, 0xD9, 0x42, 0xFF, 0x8F, 0xB4, 0xFF, 0xFA, 0x16, 0xE5, 0x6B, 0xED, - 0x03, 0x58, 0xBB, 0x3B, 0x81, 0x93, 0xA2, 0x9B, 0x38, 0x98, 0x9A, 0xB7, 0x0B, 0x9C, 0xB5, 0x49, 0xEC, 0xD0, 0x46, 0x1C, 0xA9, 0x44, 0x56, 0x65, - 0x3F, 0x7B, 0x8A, 0x74, 0xBF, 0xB6, 0xF3, 0x84, 0x91, 0x76, 0x77, 0xA1, 0xEA, 0xFA, 0x5F, 0xB7, 0x38, 0x5A, 0xBD, 0x6C, 0x58, 0xE9, 0x52, 0xF2, - 0x1A, 0xFE, 0x5F, 0x05, 0x2C, 0x6A, 0xD7, 0x7A, 0xD8, 0x47, 0xCD, 0x30, 0xE9, 0xA2, 0xD3, 0x96, 0x1B, 0x88, 0x46, 0x15, 0x0E, 0x23, 0x70, 0x99, - 0x00, 0x32, 0x31, 0xA4, 0x87, 0xD8, 0xE5, 0x27, 0x0A, 0xED, 0x3C, 0x84, 0x7C, 0xD2, 0x25, 0xF0, 0x58, 0x83, 0x9A, 0x7F, 0xB2, 0x3E, 0x73, 0xC1, - 0x2B, 0xBB, 0xFD, 0x9F, 0x3C, 0xFE, 0x75, 0xA0, 0x54, 0xD0, 0x66, 0xE3, 0xFF, 0x4F, 0xB3, 0xDE, 0x50, 0xAB, 0xA9, 0xD3, 0x3E, 0xD2, 0x2F, 0xFA, - 0x44, 0x1F, 0xE7, 0xBB, 0xFC, 0x94, 0x8F, 0xF2, 0x45, 0x3E, 0xC8, 0xDF, 0xF8, 0x1A, 0x7F, 0x7A, 0x1A, 0x3A, 0x09, 0xD0, 0x17, 0x10, 0x6B, 0xFA, - 0x8F, 0xFC, 0xFF, 0xD7, 0xFF, 0x7F, 0xF5, 0x5F, 0x77, 0x26, 0xFD, 0x5F, 0x14, 0xE0, 0x7F, 0x75, 0xF2, 0xFF, 0x1F, 0xE5, 0x7F, 0x75, 0xDB, 0xFF, - 0x6B, 0x0A, 0x01, 0xAF, 0xC2, 0x1B, 0xC7, 0xC1, 0x29, 0xC8, 0xC0, 0x39, 0x24, 0xBF, 0x48, 0x6D, 0xDF, 0xAB, 0x1F, 0x17, 0x01, 0x80, 0x7A, 0xD5, - 0x4A, 0x78, 0xB3, 0x74, 0x76, 0xF1, 0x82, 0x74, 0xE5, 0xAC, 0xF4, 0x84, 0x5D, 0x2D, 0xF8, 0xDC, 0x72, 0x39, 0x0F, 0xE0, 0x32, 0x5A, 0xB8, 0xCD, - 0xE5, 0xB3, 0xC5, 0xBF, 0xD5, 0x4A, 0x2B, 0xD2, 0xEA, 0x2F, 0xD0, 0xA2, 0x68, 0x88, 0x75, 0x77, 0xC2, 0x92, 0x89, 0xBE, 0xC2, 0x71, 0x5D, 0x1B, - 0xF4, 0x9A, 0xCE, 0x2A, 0x99, 0x8A, 0xC0, 0x49, 0x99, 0x03, 0x90, 0xFE, 0xA8, 0x8D, 0x92, 0x2C, 0x89, 0x76, 0xDD, 0x85, 0x06, 0x99, 0x81, 0xBA, - 0xAD, 0xB1, 0xF8, 0x72, 0xF0, 0x99, 0x37, 0xC1, 0x73, 0x2E, 0x10, 0x8F, 0x28, 0xD8, 0xE9, 0x17, 0xC9, 0x09, 0x7A, 0xB9, 0xF9, 0x98, 0xD7, 0xEC, - 0x95, 0xCD, 0x14, 0x9D, 0xCC, 0xCC, 0x88, 0x71, 0xE9, 0xDB, 0x7A, 0xE9, 0x42, 0x6B, 0xB9, 0xB1, 0x5F, 0xC1, 0xB4, 0x5C, 0x99, 0x33, 0x42, 0x99, - 0x86, 0x32, 0x79, 0xE5, 0x23, 0x79, 0x47, 0x14, 0x19, 0xA7, 0x05, 0x39, 0x08, 0x76, 0xC7, 0xFB, 0x79, 0x94, 0x2C, 0x52, 0x41, 0xB8, 0xE5, 0xE8, - 0x1A, 0xD6, 0xC8, 0x29, 0x97, 0x02, 0xC6, 0x1C, 0x45, 0xEE, 0x1E, 0x8A, 0x93, 0x0E, 0x8B, 0xD2, 0x27, 0x16, 0x89, 0x1A, 0x15, 0x51, 0xF8, 0x12, - 0xC5, 0x4C, 0xF7, 0x27, 0x8C, 0x14, 0x11, 0x0D, 0xA8, 0x38, 0xBC, 0x10, 0xAC, 0x7F, 0xDB, 0x9D, 0x93, 0x48, 0x95, 0xEB, 0xFD, 0xF9, 0x0B, 0x79, - 0x62, 0xCC, 0x9C, 0xBD, 0xAF, 0x3F, 0xFE, 0xFF, 0x39, 0x36, 0x32, 0x36, 0xD4, 0xFF, 0xA3, 0xAC, 0x70, 0x74, 0xA8, 0x92, 0x76, 0x3D, 0x97, 0xC2, - 0xDF, 0xF9, 0xB8, 0xCB, 0x2F, 0x3C, 0x5E, 0x1C, 0x6F, 0xD2, 0x80, 0x2F, 0x08, 0x89, 0xA7, 0xD5, 0xC0, 0xF7, 0x8C, 0xF3, 0x77, 0x51, 0xF0, 0x45, - 0x91, 0xFC, 0xE0, 0x1A, 0x9C, 0xB4, 0x27, 0x2F, 0x29, 0x85, 0x05, 0x9C, 0x95, 0x77, 0x95, 0xE3, 0x3B, 0xE2, 0xF0, 0xF0, 0x27, 0x83, 0x78, 0xAC, - 0xC3, 0xBC, 0x55, 0x10, 0x9E, 0x4B, 0xF9, 0x5D, 0x7F, 0xF9, 0xE8, 0xF9, 0x6D, 0xBA, 0xD1, 0x8A, 0x33, 0xEC, 0xA5, 0xC9, 0xB1, 0x9B, 0x61, 0x31, - 0x76, 0x22, 0x64, 0xA2, 0x84, 0xEF, 0xA7, 0x04, 0x6A, 0xAB, 0x4D, 0x1B, 0x4B, 0xB2, 0x26, 0x37, 0xF2, 0x4B, 0xE1, 0x4D, 0x61, 0x4F, 0xA4, 0xC5, - 0xE4, 0x6D, 0xCB, 0xC0, 0x03, 0xCD, 0xEB, 0x2D, 0x5A, 0xA8, 0xD3, 0xA2, 0x56, 0x04, 0x07, 0x08, 0x97, 0x0C, 0xD1, 0x25, 0x7B, 0x46, 0x00, 0x0D, - 0xF7, 0x3F, 0x1B, 0xF6, 0x41, 0x34, 0x88, 0x56, 0x2E, 0x81, 0x96, 0x7E, 0x5A, 0x76, 0x76, 0x59, 0x3E, 0xA1, 0x23, 0xC6, 0x10, 0xEB, 0x26, 0xAE, - 0xD2, 0x1A, 0x46, 0x36, 0x1A, 0x06, 0xC1, 0x90, 0xFA, 0x01, 0x4E, 0x27, 0x35, 0x46, 0xD4, 0x74, 0x43, 0x61, 0x29, 0x70, 0x4C, 0x29, 0x4C, 0x0C, - 0x69, 0x47, 0xD3, 0xE8, 0x4A, 0x98, 0xA8, 0x8C, 0x4C, 0xE8, 0x00, 0x34, 0x50, 0x28, 0xA7, 0x09, 0x76, 0x49, 0x6B, 0x02, 0x3C, 0x03, 0x05, 0xE0, - 0x29, 0x40, 0xE8, 0x9C, 0x84, 0xC0, 0x59, 0x18, 0x62, 0xF3, 0x35, 0x84, 0x66, 0x27, 0x65, 0x66, 0x32, 0xA5, 0xA3, 0xA4, 0x83, 0x46, 0x16, 0x7D, - 0x96, 0x29, 0x5A, 0xA7, 0x50, 0xEC, 0x9E, 0x25, 0x31, 0x1E, 0x93, 0x2C, 0x4E, 0xDB, 0xEE, 0x49, 0x92, 0xFD, 0x6F, 0x98, 0xFC, 0xF4, 0x3F, 0x9B, - 0x05, 0xD5, 0xBE, 0x50, 0xD1, 0xF8, 0x11, 0xB4, 0xB8, 0x33, 0x94, 0x58, 0x54, 0x71, 0x38, 0x76, 0x51, 0x59, 0x34, 0x38, 0xA5, 0xD7, 0x85, 0x0E, - 0x74, 0xAF, 0x1A, 0xDB, 0x16, 0x1F, 0xA0, 0x56, 0x7A, 0x44, 0xEC, 0x29, 0xDE, 0x34, 0x45, 0x8D, 0x94, 0xF8, 0x91, 0x14, 0xEB, 0x78, 0x54, 0x24, - 0x55, 0x74, 0xCE, 0x1C, 0x7A, 0xC2, 0x01, 0x54, 0xD0, 0x04, 0x0B, 0xA2, 0x14, 0x9F, 0x99, 0x78, 0x4F, 0xB1, 0x52, 0xBC, 0x6D, 0x6E, 0x27, 0x83, - 0x5F, 0xCE, 0x67, 0xE8, 0xCD, 0x40, 0x8D, 0xCD, 0x97, 0x96, 0xFC, 0x64, 0x0D, 0x3C, 0xB3, 0x29, 0xA9, 0x32, 0xAE, 0x16, 0x43, 0xAC, 0x69, 0xF6, - 0x33, 0x95, 0x43, 0x15, 0x84, 0x36, 0xE5, 0x56, 0x25, 0xF5, 0x65, 0x17, 0x95, 0xD7, 0x06, 0x9F, 0x03, 0x01, 0x25, 0x68, 0xE5, 0x66, 0xC1, 0xCE, - 0x89, 0xB4, 0x71, 0x7C, 0x34, 0x64, 0xC2, 0x28, 0x87, 0xA4, 0x28, 0x0C, 0x4F, 0x97, 0x0B, 0x08, 0x2D, 0xD9, 0xE2, 0x9A, 0xB4, 0xF3, 0xA1, 0xC4, - 0x13, 0x40, 0xAB, 0x81, 0x8D, 0xD1, 0x62, 0x8D, 0x01, 0x0F, 0x78, 0x32, 0xD1, 0x7C, 0x83, 0xDE, 0x13, 0x05, 0x6D, 0xF8, 0xBF, 0x16, 0x2A, 0x3A, - 0xB0, 0xF9, 0xEF, 0x23, 0xF3, 0x15, 0x0F, 0x43, 0x6C, 0x1D, 0x5C, 0xC8, 0xD7, 0x8E, 0x40, 0x59, 0x5D, 0x51, 0xB6, 0xBA, 0x16, 0x2D, 0x15, 0xC6, - 0x2D, 0x25, 0x3B, 0xD6, 0x87, 0x28, 0x8A, 0x87, 0xE6, 0xDE, 0xA9, 0xF2, 0x12, 0x63, 0x71, 0x06, 0x70, 0x9E, 0x48, 0xCB, 0x2B, 0x4E, 0xA3, 0xB2, - 0x12, 0x90, 0xCB, 0x02, 0xED, 0x44, 0xC8, 0x99, 0xB4, 0xAE, 0x59, 0x28, 0x8F, 0x59, 0xCC, 0xB4, 0xDB, 0x05, 0xD6, 0xBB, 0xCC, 0x68, 0xF7, 0xDD, - 0xD2, 0xEB, 0x93, 0x02, 0x3A, 0x67, 0xE9, 0xDB, 0xD4, 0xCE, 0x71, 0x5B, 0xB0, 0x3D, 0xC8, 0x6A, 0x0B, 0x8B, 0xF9, 0x26, 0x9A, 0x10, 0x45, 0xC4, - 0x7A, 0xCD, 0x07, 0x43, 0xD0, 0x6D, 0x45, 0x01, 0x14, 0xA5, 0xDC, 0xBB, 0x14, 0xC0, 0xFF, 0x0E, 0x49, 0xA9, 0xEE, 0x3E, 0xA1, 0x26, 0x95, 0x4C, - 0x86, 0x9A, 0xFD, 0xB8, 0xF2, 0x22, 0x73, 0x36, 0xA6, 0x80, 0x3E, 0x05, 0x25, 0x27, 0x65, 0x86, 0x1F, 0x2A, 0x35, 0xEE, 0x99, 0xC2, 0xE5, 0xF1, - 0xDC, 0xDA, 0x61, 0xFB, 0x73, 0x4F, 0x82, 0x9D, 0x08, 0xD5, 0x96, 0xBC, 0xCE, 0x95, 0x80, 0x31, 0xEB, 0x3D, 0x10, 0x9F, 0x36, 0xA6, 0xEA, 0x36, - 0x7F, 0xB0, 0x80, 0xE5, 0xF6, 0x7C, 0xDC, 0xDA, 0xB9, 0x79, 0x05, 0xEE, 0x76, 0x1E, 0xAC, 0x1D, 0xAC, 0x79, 0xD6, 0x3B, 0x7F, 0xC4, 0x1F, 0x4E, - 0x60, 0x64, 0xBE, 0x5E, 0x5C, 0x6F, 0xDD, 0x8F, 0xF8, 0x68, 0xB5, 0x7F, 0xB6, 0xE4, 0xF6, 0x4E, 0xD4, 0x59, 0xCC, 0xD2, 0x1D, 0x63, 0xA5, 0xEB, - 0xD2, 0xFC, 0xE8, 0x7F, 0xC2, 0x8A, 0xC6, 0xE9, 0x15, 0xA0, 0x3F, 0x82, 0x83, 0x3F, 0x37, 0xEC, 0x38, 0x0E, 0xCA, 0xDD, 0x0D, 0x21, 0xAF, 0x7B, - 0x5E, 0x97, 0x69, 0x5E, 0xCE, 0xFB, 0xC5, 0xAC, 0x80, 0x8E, 0x4E, 0x93, 0x90, 0xF8, 0xE4, 0x65, 0x7E, 0xF0, 0x41, 0x5A, 0xF9, 0x2A, 0xDE, 0x55, - 0xAD, 0x78, 0x5F, 0x1D, 0x5C, 0x6E, 0xAF, 0x46, 0xB3, 0xFD, 0xA7, 0xF1, 0x2F, 0xC5, 0xB0, 0xAF, 0x96, 0x31, 0x20, 0x7A, 0xEF, 0x75, 0x01, 0xFE, - 0x27, 0xF1, 0x3D, 0x90, 0x88, 0xA9, 0xBB, 0x22, 0x7A, 0xCC, 0x1B, 0xC8, 0xBE, 0x9C, 0x48, 0x29, 0x41, 0x64, 0xB0, 0xDC, 0x60, 0xFD, 0xEC, 0x2A, - 0xA1, 0xCD, 0xDD, 0xE9, 0x9E, 0x1A, 0x0A, 0x45, 0xF9, 0x75, 0x94, 0xB5, 0xFA, 0x5F, 0x7E, 0xBF, 0x0E, 0xB7, 0x27, 0x2A, 0x02, 0x43, 0x2D, 0xFD, - 0x86, 0x57, 0x96, 0x8A, 0xD5, 0x35, 0xFD, 0xDB, 0xF1, 0x78, 0x01, 0x21, 0xCF, 0x1D, 0x0E, 0xE0, 0x33, 0xFE, 0x9B, 0xAF, 0xE2, 0x27, 0xCE, 0x49, - 0x4F, 0xAB, 0x24, 0x12, 0xB8, 0xD7, 0x88, 0xEB, 0x33, 0xE4, 0xD1, 0x73, 0xF6, 0x5C, 0x7A, 0xFD, 0x40, 0xBF, 0xFF, 0xD3, 0x84, 0xAC, 0x35, 0x29, - 0xDA, 0x59, 0xE1, 0xF9, 0x6C, 0x06, 0xFF, 0xC3, 0xD7, 0x53, 0x61, 0xEA, 0x60, 0x9E, 0xBC, 0xB6, 0xC7, 0xCA, 0xCC, 0xFE, 0x7F, 0xC0, 0x7C, 0x9D, - 0xF1, 0x39, 0x7C, 0x0C, 0xFF, 0xC1, 0xE3, 0x92, 0x7A, 0xEF, 0x87, 0x90, 0x9E, 0x56, 0x9C, 0xD6, 0x4B, 0x5B, 0xBE, 0x08, 0x73, 0x7C, 0x60, 0xE4, - 0x94, 0x00, 0x34, 0x74, 0x9C, 0xFF, 0x7F, 0x08, 0x66, 0x11, 0x75, 0x87, 0x7F, 0x21, 0x92, 0x34, 0xAC, 0xC1, 0x2C, 0x58, 0xD3, 0xC9, 0x03, 0xBD, - 0x57, 0xA7, 0x57, 0x17, 0x06, 0x3E, 0x2E, 0xD4, 0xE5, 0xAD, 0xC1, 0x04, 0x5A, 0xBC, 0xB6, 0x59, 0x92, 0xF1, 0x2C, 0x76, 0xF3, 0xD5, 0x2D, 0x63, - 0xDC, 0x46, 0x25, 0xFE, 0xFB, 0xE0, 0x04, 0x4C, 0xB6, 0x52, 0xBA, 0x1E, 0x45, 0xAF, 0x80, 0x81, 0x8D, 0xC3, 0xCF, 0xE2, 0x57, 0x3F, 0xF9, 0x00, - 0x4D, 0xF9, 0xB8, 0xAD, 0xFC, 0x03, 0xB4, 0x3C, 0x5E, 0x50, 0x3C, 0x84, 0x5D, 0x9E, 0xB0, 0x2B, 0xBE, 0x02, 0x44, 0x9E, 0xF3, 0x40, 0x5C, 0x76, - 0x32, 0x9F, 0xCB, 0x17, 0x5F, 0x6E, 0xCD, 0x4B, 0x2B, 0xE6, 0xA8, 0xEC, 0xE2, 0xA0, 0x68, 0xB8, 0x01, 0x12, 0xB5, 0xA9, 0x21, 0x9A, 0x02, 0x43, - 0x33, 0x8A, 0x20, 0x16, 0xB7, 0xFD, 0x78, 0xD6, 0x61, 0x71, 0xB1, 0x52, 0x61, 0x72, 0x13, 0x60, 0x41, 0x08, 0x15, 0x4C, 0x38, 0xDF, 0x9E, 0x20, - 0x48, 0x12, 0x69, 0x62, 0xE4, 0xB2, 0x80, 0x08, 0x7E, 0x2B, 0x84, 0x56, 0x0F, 0x44, 0x2F, 0xF7, 0xB1, 0x37, 0xF0, 0xF1, 0xF7, 0x3A, 0x96, 0x25, - 0x74, 0xB8, 0x4F, 0x70, 0xA4, 0x60, 0x6D, 0xC0, 0x59, 0x68, 0x18, 0x01, 0xC8, 0x90, 0xEB, 0xC6, 0xD9, 0xF3, 0x5D, 0xF8, 0xFA, 0x01, 0xA8, 0x7A, - 0x2F, 0xF4, 0x87, 0xD8, 0x9D, 0x56, 0xE4, 0x95, 0xB6, 0xFC, 0xD1, 0x7B, 0x05, 0xA0, 0x43, 0x19, 0x9F, 0x15, 0xF5, 0x9C, 0x6D, 0x99, 0x9A, 0x79, - 0xC5, 0x9A, 0x9D, 0xA8, 0x96, 0xA7, 0x27, 0x2C, 0xCB, 0x35, 0x5E, 0x5D, 0x64, 0x5D, 0x0F, 0x89, 0x5B, 0x99, 0x62, 0xB0, 0x82, 0x82, 0xA0, 0x0E, - 0x7E, 0xA1, 0xDA, 0xBC, 0xC5, 0x35, 0x13, 0xE0, 0x4D, 0x99, 0xB0, 0x48, 0x5D, 0xC1, 0x48, 0x05, 0xC0, 0xE5, 0xEB, 0x06, 0xC0, 0x5D, 0x03, 0xE3, - 0x8D, 0x04, 0x3E, 0xE9, 0x7B, 0x3A, 0x96, 0x9F, 0x23, 0xF7, 0x5F, 0xF8, 0x16, 0x40, 0xE5, 0x3A, 0x72, 0xCB, 0x41, 0x5C, 0xCA, 0x50, 0x5D, 0x31, - 0xBE, 0x0C, 0x24, 0xAF, 0x1D, 0x80, 0x31, 0x97, 0x01, 0x99, 0x02, 0xDF, 0xB1, 0x5F, 0xE2, 0x30, 0x24, 0xE7, 0x0B, 0xDF, 0x50, 0x61, 0xD6, 0x5B, - 0xE1, 0x0D, 0x5E, 0xE0, 0x63, 0x67, 0xE0, 0x1C, 0xF5, 0xAF, 0xC9, 0x8A, 0xEF, 0x95, 0x0C, 0x6F, 0x56, 0xCB, 0x2E, 0xDE, 0x4C, 0x0B, 0xBE, 0xB2, - 0x5C, 0xCE, 0x01, 0xB8, 0x8C, 0x75, 0xF1, 0xDB, 0x5C, 0x09, 0xB4, 0xFA, 0x06, 0xB4, 0x28, 0x77, 0xB3, 0xDF, 0x83, 0xAC, 0x11, 0x89, 0x58, 0x37, - 0x24, 0x2C, 0x99, 0xE5, 0x2B, 0x7C, 0xD5, 0xB5, 0x41, 0xAC, 0xDE, 0x94, 0x56, 0x6E, 0x9A, 0x12, 0x27, 0x6D, 0xC6, 0x23, 0x3D, 0x52, 0x30, 0xDA, - 0x26, 0x20, 0x4A, 0xD7, 0x14, 0xFA, 0xA6, 0x83, 0x26, 0x98, 0xB3, 0x88, 0x13, 0x7E, 0x72, 0xE8, 0xE0, 0xA9, 0x76, 0x85, 0x77, 0xFD, 0xED, 0xAC, - 0xD7, 0x72, 0x22, 0x4F, 0x6E, 0x96, 0x10, 0xDA, 0xFC, 0x03, 0x32, 0x59, 0x53, 0x32, 0x33, 0x4E, 0x2F, 0x7F, 0x9B, 0x2F, 0x6D, 0x82, 0xF1, 0x96, - 0x1D, 0x2A, 0x2B, 0xBC, 0xA4, 0x2A, 0x51, 0xF0, 0x94, 0x0A, 0xE4, 0x93, 0x36, 0x99, 0x92, 0x1D, 0xF0, 0x22, 0xB7, 0xE6, 0x90, 0x2A, 0x06, 0xEF, - 0x14, 0xC8, 0x1E, 0x35, 0xAB, 0x54, 0xF4, 0x49, 0x3B, 0x16, 0xCF, 0x36, 0x76, 0xFE, 0xA5, 0x80, 0xAE, 0x80, 0x22, 0x75, 0x09, 0xC5, 0x4B, 0xE1, - 0xE2, 0x6D, 0xD1, 0x62, 0xD3, 0x66, 0x22, 0x74, 0xD8, 0x3D, 0x51, 0xCC, 0x7E, 0xC2, 0x45, 0x06, 0x31, 0x42, 0xD9, 0xE1, 0x1B, 0xD2, 0x44, 0x09, - 0x15, 0xB8, 0x60, 0xEC, 0xC9, 0x9B, 0x44, 0x6F, 0x2E, 0xFE, 0x38, 0x85, 0x7D, 0xFD, 0xBF, 0xA2, 0xFA, 0x94, 0x0A, 0xC2, 0x0E, 0x2C, 0x1C, 0xFF, - 0x41, 0x41, 0x43, 0x53, 0x04, 0xAD, 0x04, 0x38, 0x20, 0x0C, 0xC9, 0x5E, 0x65, 0x81, 0x69, 0x4A, 0x9D, 0x05, 0xBE, 0xF0, 0x14, 0xE8, 0x7C, 0x25, - 0xA2, 0xD4, 0xCA, 0x1E, 0x8E, 0x59, 0x0A, 0x9B, 0x2A, 0x99, 0x2D, 0xAA, 0xB1, 0x30, 0x1D, 0xD2, 0x34, 0xE1, 0x62, 0x3D, 0xCB, 0x6A, 0x2F, 0x8B, - 0x8F, 0x1D, 0x47, 0x2A, 0x88, 0x3C, 0x07, 0x59, 0x63, 0x61, 0x09, 0xE1, 0xDB, 0x0C, 0xCB, 0xCD, 0xB8, 0x80, 0x5C, 0x0D, 0x68, 0x88, 0xAE, 0x63, - 0xCF, 0x30, 0xC6, 0xC8, 0x48, 0xC5, 0x5E, 0x4A, 0xC2, 0x1A, 0xF8, 0x82, 0x35, 0x05, 0xC5, 0x41, 0x64, 0x7C, 0xA5, 0x75, 0x79, 0x55, 0xD2, 0x74, - 0xDD, 0x1D, 0xE0, 0x8A, 0x85, 0x6F, 0x0D, 0xF2, 0x69, 0x11, 0xD7, 0x64, 0x49, 0x22, 0xC8, 0x12, 0x89, 0xD2, 0xBB, 0x6A, 0xD8, 0x7B, 0x20, 0x99, - 0x94, 0x96, 0xDB, 0x8B, 0x79, 0xF3, 0x76, 0x71, 0x6C, 0x64, 0x68, 0x64, 0x90, 0xAA, 0x06, 0xA6, 0xC5, 0x3F, 0x28, 0x7F, 0xA5, 0x41, 0xC9, 0xBD, - 0x2A, 0x01, 0xC4, 0xBB, 0xEE, 0xA2, 0xB8, 0x62, 0x53, 0xB6, 0x06, 0xD7, 0xB4, 0x86, 0x9B, 0xB3, 0x24, 0xA5, 0xB2, 0x44, 0x54, 0xAE, 0x5C, 0xE5, - 0xD0, 0x3F, 0x46, 0xC3, 0x30, 0xF0, 0xA0, 0xA9, 0x58, 0x10, 0x04, 0x71, 0xDD, 0xB9, 0x21, 0x21, 0xF8, 0x32, 0x70, 0x97, 0x47, 0x86, 0x60, 0x2E, - 0xC3, 0x9D, 0x60, 0xD2, 0xB0, 0x5F, 0xE2, 0x5D, 0x66, 0xD9, 0xF0, 0x23, 0xE8, 0x9D, 0x6D, 0xDD, 0xB0, 0xA8, 0x22, 0xF4, 0x33, 0xE0, 0xB0, 0x6F, - 0x28, 0xB4, 0xCB, 0x7D, 0x78, 0x83, 0x17, 0x7A, 0x28, 0x10, 0x41, 0x8E, 0x1A, 0x3A, 0x2C, 0x12, 0xC1, 0x95, 0x0C, 0x34, 0x5D, 0x1E, 0x41, 0x9C, - 0x13, 0x34, 0x60, 0x31, 0x02, 0xBC, 0x99, 0x3B, 0xC9, 0x10, 0x04, 0x84, 0x50, 0x77, 0xAE, 0x3C, 0x08, 0x38, 0x07, 0xDC, 0x09, 0x8B, 0x0C, 0xE7, - 0x4A, 0xB8, 0xFB, 0xE8, 0x19, 0x96, 0x1E, 0x71, 0xC7, 0x93, 0x35, 0x8C, 0x72, 0x07, 0xAD, 0x15, 0x37, 0x6C, 0x44, 0x02, 0xDA, 0xC0, 0x83, 0x5B, - 0x32, 0x4B, 0xE8, 0x0A, 0x72, 0x08, 0x1A, 0xC4, 0x42, 0xB3, 0xE7, 0xFB, 0xF8, 0xFA, 0x41, 0xAB, 0x5E, 0xBD, 0xE7, 0xFA, 0x43, 0xEC, 0xA7, 0x15, - 0x8F, 0xA5, 0x2D, 0x47, 0xA2, 0x49, 0x49, 0xA0, 0x67, 0x7D, 0x9E, 0x81, 0x7F, 0x9C, 0x99, 0xB5, 0xF4, 0xCE, 0x1D, 0x5B, 0xF9, 0x66, 0x12, 0x20, - 0x39, 0x73, 0xED, 0x01, 0x66, 0x1D, 0x02, 0xCB, 0x41, 0x55, 0xCA, 0x50, 0x56, 0x31, 0xBE, 0xDA, 0x92, 0xD7, 0xDC, 0x60, 0xCC, 0x55, 0x40, 0xA6, - 0x60, 0xE1, 0xD8, 0xAF, 0x70, 0x5F, 0x68, 0x37, 0x7E, 0xDC, 0x2C, 0xEE, 0xE4, 0x24, 0x16, 0xB5, 0x0F, 0x43, 0xAE, 0x78, 0xE1, 0xCB, 0x29, 0xCC, - 0x0A, 0x2B, 0xBC, 0x71, 0x0B, 0x7C, 0x0C, 0x03, 0x18, 0xC7, 0xF9, 0xD7, 0xA4, 0xC4, 0xF7, 0x6A, 0xC5, 0x85, 0x4E, 0x8C, 0xDE, 0x01, 0xD0, 0xBC, - 0x59, 0x27, 0xBB, 0x78, 0x2D, 0xBA, 0x72, 0x42, 0x7A, 0xC2, 0xA4, 0x16, 0x7C, 0x5F, 0xB9, 0x9C, 0x65, 0xB8, 0x8C, 0x09, 0x6E, 0x73, 0xEB, 0x6C, - 0x5B, 0x43, 0x6D, 0x4B, 0x38, 0xCC, 0x68, 0x83, 0x19, 0x69, 0x73, 0x84, 0x67, 0x3D, 0x46, 0x65, 0xB1, 0x3A, 0x62, 0x77, 0x5C, 0xC0, 0x16, 0xC0, - 0x79, 0xD5, 0x62, 0x63, 0xB9, 0x66, 0xA0, 0x9A, 0x95, 0xFB, 0x92, 0xA2, 0xFE, 0x15, 0xA6, 0xBB, 0x81, 0xA6, 0x6E, 0xD8, 0xD2, 0x75, 0xA4, 0x0E, - 0x29, 0x1C, 0xC6, 0x28, 0xCB, 0x07, 0x51, 0xB6, 0x12, 0xCB, 0x50, 0x25, 0x49, 0xD0, 0x07, 0x31, 0x58, 0x9C, 0xB1, 0x7E, 0x8A, 0x38, 0xF1, 0x84, - 0x60, 0xBC, 0x88, 0xB7, 0x14, 0x4A, 0xE2, 0x24, 0xEC, 0x42, 0x80, 0x19, 0x7B, 0x67, 0x89, 0xD7, 0xA7, 0xE6, 0xD4, 0x64, 0x83, 0xFB, 0x47, 0xFC, - 0x51, 0xD4, 0xAB, 0x1F, 0xB0, 0x2A, 0x18, 0x24, 0xAA, 0xA8, 0x8C, 0xA8, 0x82, 0x0A, 0x4C, 0xB9, 0x89, 0x49, 0x7D, 0xE9, 0x45, 0xE5, 0x2D, 0x80, - 0xCF, 0x21, 0x42, 0x09, 0x8E, 0x3D, 0xF9, 0x7D, 0x3A, 0x65, 0xD2, 0x35, 0x3D, 0xEA, 0x99, 0x6C, 0xEB, 0x4B, 0xA2, 0x2C, 0x94, 0x9E, 0x55, 0x24, - 0xF9, 0xEA, 0x26, 0xB9, 0xA0, 0x22, 0x6D, 0x4C, 0x1F, 0x0D, 0xDD, 0x8D, 0x72, 0x60, 0x8A, 0xC2, 0x54, 0x89, 0xAE, 0x98, 0x86, 0xE8, 0x86, 0x85, - 0xE2, 0xCE, 0xDE, 0x1E, 0x3E, 0x82, 0xF0, 0x9C, 0x80, 0x24, 0x75, 0x7E, 0x08, 0x05, 0xF8, 0xB8, 0x5A, 0xF5, 0x70, 0x42, 0xE0, 0x11, 0xDE, 0xD9, - 0xC9, 0x0D, 0xB1, 0xF3, 0x71, 0x95, 0x23, 0x4D, 0x99, 0xC3, 0xF3, 0x86, 0x83, 0x22, 0xE2, 0x46, 0xEB, 0x82, 0x8D, 0x51, 0x65, 0x8D, 0x01, 0x43, - 0x1A, 0xB2, 0xD7, 0x06, 0xC8, 0x32, 0x86, 0x87, 0x31, 0x90, 0x78, 0x18, 0x4F, 0xCF, 0x17, 0xD6, 0x5C, 0x17, 0xDD, 0x16, 0x2D, 0xC7, 0xD0, 0x2D, - 0xEA, 0x68, 0x96, 0x8E, 0x16, 0x2B, 0x34, 0x2E, 0x2A, 0x3A, 0xD1, 0x29, 0x49, 0xD2, 0x28, 0x59, 0x2E, 0x00, 0xCE, 0x5E, 0x4E, 0xE4, 0x94, 0x4C, - 0x20, 0x2C, 0x95, 0xF0, 0x05, 0x68, 0x8F, 0x30, 0x23, 0x61, 0x6C, 0xA0, 0x1F, 0xB9, 0x74, 0x20, 0x82, 0x3F, 0x0B, 0xA1, 0x0D, 0xCC, 0x83, 0xE8, - 0x1F, 0x7C, 0xEC, 0xED, 0x3E, 0xFE, 0x07, 0x8F, 0x36, 0x88, 0x0E, 0xB7, 0x24, 0xC7, 0x7B, 0x72, 0x03, 0xCE, 0x2A, 0xD7, 0x88, 0xB7, 0xC6, 0x33, - 0xE4, 0xC6, 0xBA, 0x72, 0x49, 0x7A, 0x42, 0xA8, 0x56, 0x39, 0x71, 0xFE, 0x34, 0xC3, 0x6E, 0x21, 0x26, 0x6E, 0x9C, 0xD0, 0x12, 0x83, 0x9C, 0x7E, - 0xBC, 0x58, 0x34, 0x8A, 0xFF, 0x5F, 0x1F, 0x58, 0x09, 0x59, 0x6B, 0x25, 0xE7, 0x7F, 0x76, 0x5F, 0xB3, 0x09, 0x04, 0x06, 0x25, 0x7C, 0xF5, 0xF6, - 0x88, 0x3F, 0x12, 0x46, 0x15, 0x23, 0x97, 0x8F, 0x22, 0xF8, 0xBF, 0x10, 0xDA, 0x54, 0x10, 0xFD, 0xE3, 0xC7, 0xFE, 0xFE, 0xF1, 0x53, 0x3D, 0xDA, - 0xAA, 0x1D, 0xEE, 0x17, 0x1B, 0x51, 0x1E, 0x76, 0xB0, 0x0F, 0x07, 0x7B, 0x1D, 0xFE, 0xFF, 0x8D, 0xF2, 0x9A, 0xA6, 0x09, 0x11, 0x38, 0xF5, 0x69, - 0x82, 0x24, 0x6E, 0xF5, 0x87, 0xB0, 0x89, 0x6E, 0x8D, 0xFF, 0x39, 0xE9, 0x61, 0x14, 0x32, 0xB0, 0xE4, 0x14, 0x7E, 0xE0, 0xF4, 0x70, 0x1F, 0x3C, - 0x2B, 0x0D, 0x48, 0xEF, 0x4C, 0x05, 0x0C, 0x71, 0x37, 0xD1, 0x0B, 0x19, 0x7B, 0x8C, 0x1D, 0x0C, 0x98, 0x52, 0x14, 0xD0, 0x1B, 0x46, 0x3C, 0x0F, - 0xD3, 0x38, 0x33, 0x02, 0x70, 0xFE, 0x28, 0x66, 0x1E, 0xD5, 0x65, 0x06, 0xC3, 0x5A, 0x1E, 0x59, 0x52, 0xC6, 0xD3, 0x63, 0x68, 0x44, 0x48, 0x16, - 0xE1, 0xA2, 0x1F, 0xFB, 0x6A, 0x81, 0x5B, 0x99, 0x2B, 0xE8, 0x8C, 0xFB, 0x2C, 0x14, 0xBF, 0x6C, 0x70, 0xCB, 0x12, 0x9D, 0x1F, 0x4E, 0xA9, 0x4D, - 0x32, 0xC5, 0xCC, 0xAD, 0x5F, 0x1E, 0xC7, 0x2D, 0x49, 0xE0, 0x3E, 0x8A, 0x0C, 0xF4, 0x99, 0xFF, 0xFF, 0x7A, 0xEF, 0xF3, 0x7E, 0xBA, 0x0F, 0x76, - 0x03, 0xBE, 0xC7, 0x72, 0x8E, 0x4B, 0x72, 0x1C, 0x24, 0x71, 0xA9, 0x66, 0x71, 0x39, 0x3F, 0x6F, 0xC4, 0x0E, 0xE2, 0xE4, 0x88, 0xE1, 0x59, 0x92, - 0x7C, 0x59, 0x22, 0x6A, 0xDF, 0x55, 0xB1, 0x1D, 0xEB, 0xBE, 0x85, 0x25, 0xE3, 0x95, 0xFE, 0x5C, 0x24, 0x2A, 0x55, 0x44, 0x39, 0x91, 0x28, 0xF6, - 0xDB, 0x3E, 0x61, 0xC4, 0x02, 0xD1, 0x80, 0xA9, 0x06, 0x60, 0x7B, 0x2B, 0x06, 0xBB, 0x88, 0x3F, 0xCB, 0xC7, 0x7A, 0x86, 0x5C, 0xD5, 0xBA, 0x3F, - 0x8F, 0xBC, 0xB9, 0xFA, 0x59, 0xAB, 0x82, 0xB1, 0x58, 0x45, 0x35, 0x45, 0x15, 0xE4, 0x60, 0xCA, 0x8D, 0x4D, 0xEA, 0x2B, 0x30, 0x2A, 0x8F, 0x02, - 0x7C, 0x2E, 0x12, 0x4A, 0x90, 0xC0, 0x93, 0x5F, 0xDE, 0x29, 0x93, 0xDE, 0xF4, 0x58, 0xCC, 0x64, 0x4B, 0xB8, 0x24, 0xCA, 0xAA, 0xF4, 0xAC, 0x99, - 0xE4, 0x6B, 0x9C, 0xE4, 0x52, 0x8B, 0xC4, 0x21, 0x72, 0x34, 0xC4, 0x79, 0x4B, 0x74, 0x95, 0x35, 0x44, 0xE7, 0x2C, 0x14, 0x17, 0xF7, 0xF6, 0xB0, - 0x12, 0x84, 0xB7, 0x05, 0x24, 0x79, 0xF4, 0x43, 0x28, 0xE7, 0xE3, 0x3A, 0xD6, 0xC3, 0xB9, 0xC7, 0x23, 0x8C, 0xB4, 0x93, 0x7B, 0x62, 0xE7, 0x83, - 0x2C, 0x47, 0x9A, 0x45, 0x0E, 0x0F, 0x03, 0x1C, 0x94, 0xC4, 0x1B, 0xAD, 0x3F, 0x1B, 0x23, 0xD0, 0x1A, 0x03, 0x8F, 0x34, 0x64, 0xBB, 0x33, 0x15, - 0x3C, 0xB8, 0x1D, 0x7C, 0x19, 0x43, 0xC8, 0x18, 0x48, 0x55, 0x18, 0xCF, 0xD5, 0x17, 0xD6, 0x61, 0x17, 0x5D, 0xE2, 0x16, 0xE4, 0x8F, 0x96, 0x35, - 0x08, 0x4B, 0x07, 0xF1, 0x0A, 0xCD, 0x8E, 0xCA, 0x0E, 0x48, 0x8A, 0x52, 0x08, 0x4A, 0xD6, 0x0E, 0x80, 0x33, 0x9E, 0x13, 0xB9, 0x2B, 0x13, 0x08, - 0x58, 0x25, 0x7C, 0x6F, 0xED, 0x11, 0x70, 0x24, 0x8C, 0x17, 0xF6, 0x23, 0x97, 0x79, 0x22, 0xF8, 0xB8, 0x10, 0x5A, 0x13, 0x7A, 0x10, 0x3D, 0x86, - 0x8F, 0xBD, 0xDE, 0xC7, 0x3F, 0xF4, 0x68, 0x23, 0x72, 0x70, 0xF8, 0x1C, 0xEF, 0x0D, 0x78, 0x8B, 0x68, 0x52, 0x18, 0xE8, 0xD9, 0xA5, 0x67, 0x60, - 0xA6, 0x67, 0xA2, 0x7A, 0xDF, 0xEC, 0x37, 0x94, 0x35, 0x5A, 0xDC, 0xCB, 0x9F, 0xF5, 0x19, 0xF6, 0xF3, 0x11, 0x4D, 0x81, 0x1F, 0xC3, 0x26, 0x8D, - 0x80, 0xCB, 0xFB, 0x8F, 0x37, 0x58, 0xDB, 0xAC, 0x5F, 0xFE, 0xEA, 0x9F, 0x0B, 0xE4, 0x60, 0xE4, 0xDC, 0xE1, 0xA0, 0x99, 0xC4, 0x2C, 0xF0, 0x23, - 0xFA, 0x25, 0x22, 0x56, 0xE9, 0x0F, 0xF3, 0x36, 0xBE, 0x89, 0x37, 0xE9, 0x3A, 0x89, 0x60, 0xBA, 0xDE, 0x4B, 0x3D, 0x93, 0x83, 0x9E, 0xB8, 0x54, - 0xCF, 0x08, 0xB9, 0x6B, 0xF8, 0x61, 0x69, 0xA8, 0xF7, 0x19, 0x6C, 0x39, 0x76, 0xF6, 0x02, 0x9D, 0xB5, 0x1D, 0x88, 0x5B, 0x9D, 0xE1, 0x8B, 0xD8, - 0x99, 0x5E, 0xE4, 0x91, 0xB6, 0xBC, 0x8A, 0x26, 0xC5, 0x82, 0x9E, 0x7D, 0x7B, 0x06, 0x86, 0x73, 0x66, 0xBE, 0xE0, 0xDC, 0x91, 0xD7, 0x4C, 0x02, - 0x97, 0xA7, 0x0C, 0x85, 0x21, 0xC6, 0x97, 0x89, 0xE4, 0xB5, 0x43, 0x18, 0x73, 0x78, 0x64, 0x0A, 0x9E, 0xC7, 0x7E, 0xB9, 0xFB, 0x05, 0x99, 0xC6, - 0x8F, 0xD0, 0xC5, 0x7F, 0xB4, 0xC4, 0xA2, 0xFA, 0x61, 0xC8, 0xE5, 0x17, 0xBE, 0xD2, 0xC2, 0xAD, 0x28, 0xC2, 0x1C, 0x2F, 0xC0, 0xC8, 0x44, 0xC0, - 0x39, 0x4B, 0xBF, 0x26, 0x60, 0xBE, 0x57, 0x66, 0x2E, 0x76, 0x8A, 0x7A, 0xD5, 0x98, 0x78, 0xB3, 0xC2, 0x76, 0xF1, 0xCE, 0x74, 0xE5, 0xE6, 0x7A, - 0xC2, 0x07, 0x2D, 0xF8, 0x3A, 0xE4, 0x72, 0x7E, 0xC0, 0x65, 0x6C, 0xBC, 0xCD, 0x95, 0xB4, 0xC5, 0xD7, 0x6B, 0xA5, 0x59, 0x69, 0xF5, 0x2A, 0x68, - 0x51, 0x28, 0x67, 0x3F, 0xAA, 0xAC, 0x91, 0x6B, 0xAC, 0x5B, 0x18, 0x96, 0xEC, 0xB8, 0x95, 0x0D, 0xA7, 0x7D, 0xCA, 0xD7, 0x62, 0xB9, 0x69, 0x57, - 0x73, 0xAA, 0x4B, 0x52, 0xFA, 0x2F, 0xEE, 0x09, 0x5C, 0x6B, 0x84, 0x69, 0x7B, 0x35, 0xB7, 0xCF, 0x01, 0x9C, 0x21, 0xD2, 0xF2, 0xF4, 0x02, 0x72, - 0x29, 0xD1, 0x10, 0xA5, 0xCF, 0xA4, 0x5D, 0xCD, 0x42, 0x5F, 0xCC, 0x62, 0x8E, 0xCB, 0x5C, 0x2C, 0x95, 0x19, 0xBD, 0x8E, 0x41, 0xC3, 0x8C, 0x91, - 0x21, 0x8B, 0xBD, 0x30, 0x85, 0x39, 0x68, 0x83, 0x35, 0x9D, 0xC5, 0x41, 0x00, 0x7D, 0xA5, 0x3D, 0xF0, 0xAA, 0xE4, 0xEA, 0xBA, 0xFB, 0xC0, 0x15, - 0x6B, 0x60, 0x6B, 0x50, 0x52, 0x8B, 0xD8, 0x2B, 0x4B, 0x32, 0x44, 0x96, 0x08, 0x82, 0x2B, 0x57, 0xCA, 0xAB, 0x1F, 0x0D, 0x55, 0x30, 0x5C, 0xAB, - 0xA8, 0x12, 0x50, 0x05, 0xD9, 0xA7, 0xDC, 0x36, 0x48, 0x7D, 0x71, 0x46, 0xE5, 0x31, 0xC2, 0xE7, 0x6E, 0xA1, 0x04, 0x39, 0x3C, 0xF9, 0x39, 0x3B, - 0x65, 0xA6, 0x68, 0xBA, 0xAF, 0x30, 0xD9, 0x96, 0x2E, 0x89, 0x42, 0x2B, 0x3D, 0xFF, 0x26, 0xFD, 0xB6, 0x24, 0xBD, 0x68, 0x20, 0x71, 0x24, 0x1D, - 0x0D, 0x01, 0x1B, 0xE5, 0x94, 0x15, 0x89, 0x6D, 0x10, 0x5D, 0xD5, 0x0D, 0x31, 0xB0, 0x11, 0x8A, 0x03, 0x74, 0x7B, 0x90, 0x0A, 0xC2, 0x6B, 0x00, - 0x24, 0x29, 0xFF, 0x21, 0xF4, 0xE0, 0xE3, 0x8A, 0xD7, 0xC3, 0x09, 0xC9, 0x23, 0x4C, 0x60, 0x27, 0x77, 0x75, 0xE7, 0x83, 0xDE, 0x96, 0x26, 0xB6, - 0xF3, 0x10, 0xE2, 0xA0, 0xB0, 0xDE, 0x68, 0x69, 0xDA, 0x18, 0x19, 0xD7, 0x18, 0xA8, 0x2D, 0x13, 0x71, 0x57, 0xD8, 0xAC, 0x3B, 0xE8, 0x71, 0x1B, - 0xDA, 0xC6, 0x40, 0x32, 0xC3, 0x78, 0x36, 0xBF, 0xB4, 0xA6, 0xB8, 0xEC, 0xAA, 0xB4, 0x20, 0x17, 0xD9, 0x5B, 0xD6, 0x46, 0x2C, 0x1E, 0x2B, 0x2B, - 0x34, 0x62, 0x3B, 0x47, 0x6E, 0x25, 0xA4, 0x76, 0xD6, 0x98, 0xFB, 0x8C, 0xAC, 0x2D, 0x47, 0xE7, 0xE6, 0x78, 0x0F, 0x33, 0xEB, 0x98, 0x68, 0x2F, - 0x07, 0x39, 0x28, 0x72, 0x9F, 0xF2, 0xE9, 0x18, 0x9A, 0x68, 0xA5, 0xDB, 0xF3, 0x21, 0x85, 0x99, 0x35, 0x76, 0x42, 0xCB, 0xD3, 0xFB, 0x04, 0xBD, - 0x0E, 0x93, 0x69, 0xBF, 0x98, 0xFE, 0xEB, 0xF5, 0x1E, 0x2C, 0x6C, 0x37, 0xAA, 0x68, 0x6B, 0x6E, 0xB6, 0x59, 0xFC, 0xB1, 0xB8, 0xDA, 0x57, 0x78, - 0x1F, 0x55, 0x60, 0x70, 0xAA, 0xB0, 0x53, 0xEB, 0x27, 0xB7, 0xF5, 0x9A, 0x17, 0x75, 0x22, 0xE7, 0x7A, 0xD7, 0x5D, 0x5C, 0x90, 0x9E, 0xF5, 0xFF, - 0xFF, 0xA7, 0x08, 0x24, 0x04, 0x9E, 0xFD, 0xFF, 0x37, 0x32, 0xD4, 0x19, 0x6A, 0xF6, 0x66, 0xA6, 0xE6, 0x3C, 0xCE, 0x36, 0x0F, 0x0E, 0x77, 0x06, - 0xCE, 0x24, 0xC4, 0x2D, 0x39, 0xC3, 0x4D, 0xC7, 0xB8, 0x0D, 0x66, 0x00, 0x81, 0x2C, 0x11, 0xB6, 0x22, 0xB8, 0x5C, 0xAE, 0x67, 0x6D, 0x4C, 0x4D, - 0x8D, 0x2F, 0x6B, 0x40, 0xDD, 0x5D, 0x1A, 0x63, 0x9C, 0x80, 0xB9, 0x3B, 0xDA, 0x32, 0x8A, 0x33, 0x3F, 0xD5, 0x33, 0x15, 0xD2, 0x00, 0x8F, 0xC3, - 0x01, 0x90, 0x7C, 0xC7, 0x40, 0x66, 0xC5, 0xD2, 0xE8, 0xC2, 0x7A, 0x94, 0xC1, 0xEE, 0xEA, 0x52, 0xB4, 0xF8, 0x41, 0x15, 0xF7, 0xB8, 0x05, 0x51, - 0x8E, 0x37, 0xE4, 0x12, 0xDC, 0xF2, 0x6B, 0xCE, 0x19, 0xAF, 0xB1, 0x3E, 0x14, 0x90, 0x84, 0x8B, 0x99, 0x42, 0x65, 0x60, 0x7D, 0x32, 0xA0, 0xC2, - 0x69, 0xD1, 0xB8, 0x49, 0x03, 0x2A, 0x0A, 0x61, 0xA5, 0xE0, 0x9E, 0xA4, 0xD3, 0x8E, 0xA4, 0x4F, 0x82, 0xB4, 0x01, 0x7B, 0x24, 0x62, 0x69, 0x04, - 0x42, 0x55, 0x04, 0xDF, 0x22, 0x72, 0x92, 0x95, 0xCB, 0xC1, 0xA5, 0xAA, 0x08, 0xB4, 0x01, 0x85, 0x30, 0xF0, 0x63, 0xA1, 0x82, 0xCF, 0x8A, 0x0C, - 0x3D, 0x26, 0x0A, 0x79, 0x64, 0x13, 0x77, 0x3E, 0x37, 0x75, 0x96, 0x41, 0x73, 0x68, 0x0D, 0x71, 0x90, 0x99, 0x6F, 0x9A, 0x3B, 0x6D, 0x58, 0x70, - 0x6B, 0xBC, 0x7A, 0x69, 0x94, 0x9E, 0x67, 0xE6, 0xD6, 0x6E, 0x0A, 0x6C, 0x19, 0x3F, 0x77, 0xCC, 0x1C, 0x1A, 0xE6, 0xF1, 0xE9, 0xE5, 0xAF, 0xC5, - 0x65, 0x35, 0xAD, 0x05, 0x32, 0x89, 0xC5, 0x17, 0x6A, 0x85, 0xB5, 0x6C, 0x65, 0x63, 0x40, 0xCA, 0x71, 0x5D, 0x0A, 0x8E, 0x04, 0x94, 0x12, 0x40, - 0x00, 0x56, 0x23, 0x4E, 0x74, 0x1C, 0x98, 0xC0, 0xAD, 0x28, 0xC1, 0x0A, 0x6C, 0xEB, 0xC1, 0x92, 0x14, 0x88, 0x1C, 0x29, 0x71, 0x29, 0x92, 0x21, - 0x1F, 0xB2, 0x29, 0x0B, 0xB2, 0x58, 0xFC, 0xFE, 0xC0, 0x8D, 0xE1, 0xD1, 0xC9, 0xDA, 0x21, 0x7D, 0xC2, 0xD1, 0xC2, 0x98, 0x03, 0x44, 0x73, 0xE3, - 0x8B, 0x68, 0x3C, 0x7D, 0xC1, 0x33, 0xDC, 0x3D, 0x6C, 0xB2, 0xA1, 0x35, 0x3C, 0xEB, 0xAE, 0x6E, 0x10, 0xFF, 0x74, 0xB7, 0xD3, 0xA8, 0xA9, 0xD2, - 0x80, 0xCE, 0xF7, 0x68, 0xD6, 0x26, 0x58, 0x07, 0x80, 0xB0, 0x44, 0x64, 0xAF, 0x2A, 0xBC, 0xA8, 0x08, 0x7E, 0x51, 0x5F, 0x14, 0x5C, 0x8E, 0xBA, - 0x2D, 0xA3, 0xD3, 0x56, 0xF4, 0x0A, 0xAE, 0x68, 0x91, 0xAD, 0x64, 0xC7, 0x51, 0x2A, 0x21, 0x4E, 0xD2, 0x14, 0x49, 0x9A, 0xCD, 0x23, 0xA3, 0x7E, - 0x1E, 0xF2, 0x81, 0x13, 0xFC, 0xAE, 0xF6, 0xC0, 0xF0, 0x2F, 0xC0, 0x17, 0xDE, 0xED, 0x86, 0x0A, 0xA6, 0xB9, 0x88, 0xFB, 0xBD, 0xAF, 0xAA, 0xE4, - 0xF6, 0x64, 0xDA, 0x1C, 0xFF, 0x7F, 0x0C, 0x90, 0x44, 0x9F, 0xB4, 0x3B, 0x0F, 0x3E, 0x75, 0x14, 0x54, 0xE6, 0x73, 0x42, 0xE8, 0xE5, 0x21, 0xE2, - 0xF1, 0x94, 0x1E, 0xF9, 0x0A, 0x05, 0xF1, 0xA3, 0x2C, 0xF9, 0x21, 0x70, 0x4A, 0x97, 0xC7, 0xF7, 0x79, 0xBB, 0xDA, 0x72, 0x13, 0x09, 0x6C, 0xD3, - 0x3B, 0x96, 0xFF, 0x44, 0xD6, 0xF7, 0xF6, 0xE8, 0x5D, 0xF9, 0x3A, 0xE1, 0x81, 0xEF, 0x36, 0xC9, 0xC9, 0xF7, 0xF3, 0x5A, 0x73, 0x15, 0x3F, 0xE2, - 0xBE, 0x2D, 0x1D, 0xFA, 0x43, 0x47, 0xDA, 0x31, 0xBC, 0xDF, 0x85, 0x4F, 0xDB, 0x48, 0x74, 0x0C, 0x9A, 0x64, 0x8C, 0xAC, 0x57, 0xEC, 0xE5, 0x27, - 0xCC, 0xC1, 0x1A, 0xAC, 0x69, 0x0B, 0x84, 0x9B, 0x9E, 0x26, 0x57, 0xAF, 0x7C, 0xA2, 0xDF, 0xED, 0xD5, 0x63, 0xD3, 0xE8, 0x83, 0x27, 0xEA, 0xC0, - 0x50, 0xFF, 0xFF, 0xC1, 0xBE, 0x1F, 0xF4, 0x3F, 0x81, 0xF6, 0xEF, 0xFA, 0x9F, 0xDE, 0xFE, 0x6F, 0x3A, 0x1F, 0x61, 0x9B, 0xD2, 0x8B, 0xCE, 0xA8, - 0xD9, 0x86, 0x45, 0x40, 0xC7, 0x17, 0xD6, 0x63, 0x68, 0xA0, 0xEB, 0x37, 0xEB, 0x28, 0x8B, 0xF4, 0x6F, 0x5B, 0xF7, 0xE9, 0xFB, 0xEE, 0xE6, 0x3A, - 0x17, 0x8A, 0x1F, 0xA5, 0x52, 0x47, 0x40, 0xFA, 0xD9, 0x8C, 0xEA, 0x7D, 0xBE, 0xDF, 0x8E, 0xA5, 0xED, 0x98, 0xF1, 0x45, 0x20, 0xB5, 0x53, 0x69, - 0x7E, 0xD3, 0x9F, 0x53, 0x57, 0x50, 0xD3, 0x3F, 0x01, 0xC7, 0xD7, 0x9F, 0x04, 0x7B, 0x18, 0x2B, 0xBE, 0x6D, 0xA3, 0xA8, 0xFE, 0xE7, 0x2C, 0xBD, - 0x3D, 0xE0, 0x04, 0xE1, 0x9D, 0x01, 0x49, 0x52, 0xFD, 0x10, 0x3E, 0xF0, 0x71, 0x25, 0xD0, 0xC3, 0xF9, 0x81, 0x47, 0xB8, 0x6A, 0x27, 0x57, 0x72, - 0xE7, 0xA3, 0x2D, 0x47, 0x9A, 0x35, 0x87, 0xA7, 0x02, 0x0E, 0x0A, 0xE5, 0x8D, 0x56, 0xA1, 0x8D, 0x91, 0x6B, 0x8D, 0x81, 0x4D, 0x1A, 0xB2, 0xE3, - 0x99, 0x0A, 0x86, 0x6E, 0x07, 0x24, 0x32, 0x86, 0x9D, 0x31, 0x90, 0xB7, 0x30, 0x9E, 0xB8, 0x2F, 0xAC, 0xCE, 0x2E, 0xBA, 0xCF, 0x2D, 0xC8, 0x2B, - 0x2D, 0x6B, 0x16, 0x96, 0x0E, 0xE8, 0x15, 0x1A, 0x24, 0x95, 0x9D, 0x96, 0x14, 0xA5, 0x16, 0x94, 0xAC, 0x24, 0x00, 0x67, 0x49, 0x27, 0x72, 0x65, - 0x26, 0x10, 0xCA, 0x4A, 0xF8, 0xEA, 0xDA, 0x23, 0xEA, 0x48, 0x18, 0x01, 0xFF, 0xC8, 0x25, 0xA0, 0x08, 0xBE, 0x41, 0x08, 0xED, 0x1D, 0x44, 0xEF, - 0xE1, 0x63, 0x1F, 0xE8, 0xE1, 0x78, 0xF8, 0x3D, 0xDA, 0x91, 0x1C, 0x1C, 0x72, 0x38, 0xDE, 0xF9, 0x0D, 0x38, 0xB1, 0x46, 0xBC, 0x38, 0x83, 0x0D, - 0x84, 0xCE, 0x5E, 0x10, 0x32, 0x5F, 0x3F, 0x30, 0x56, 0xEF, 0x19, 0x7F, 0x11, 0x22, 0xD3, 0x8B, 0x22, 0xD2, 0x96, 0x3D, 0xD1, 0xA4, 0x3D, 0xD0, - 0xB3, 0x59, 0xCF, 0xC0, 0x59, 0xCE, 0xCC, 0x71, 0xCE, 0x1D, 0x71, 0xCD, 0x24, 0x5A, 0xCC, 0x3A, 0x18, 0x96, 0x83, 0x0A, 0x28, 0x43, 0xC9, 0xC5, - 0xF8, 0xD2, 0x4B, 0x5E, 0xDB, 0x83, 0x31, 0x87, 0x88, 0x4C, 0xE1, 0xC4, 0xB1, 0xDF, 0xE7, 0xBE, 0x90, 0x85, 0xF1, 0xA3, 0xBE, 0xB8, 0x53, 0x97, - 0x58, 0x14, 0x74, 0x38, 0x16, 0xF4, 0x85, 0xEF, 0xAE, 0xB0, 0xF2, 0xAE, 0x30, 0xC7, 0x0F, 0x56, 0xF9, 0xC0, 0x39, 0x31, 0xBF, 0x48, 0x31, 0xBE, - 0x57, 0x4D, 0x2E, 0x02, 0x34, 0xF5, 0xAA, 0xC9, 0xF0, 0x66, 0x1D, 0xED, 0xE2, 0x39, 0xE9, 0xCA, 0x8D, 0xE9, 0x09, 0x05, 0xB4, 0xE0, 0xA3, 0xCB, - 0xE5, 0x84, 0x80, 0xCB, 0x58, 0xBF, 0xCD, 0x0D, 0x61, 0x8B, 0x67, 0x50, 0x2B, 0x4D, 0x49, 0xAB, 0xFF, 0xA3, 0x45, 0x19, 0x31, 0xFB, 0x1F, 0x65, - 0x8D, 0x56, 0xC4, 0xBA, 0x53, 0x61, 0xC9, 0x60, 0x5F, 0xE1, 0x2F, 0x5C, 0x1B, 0xC0, 0x68, 0x65, 0xF3, 0x38, 0xCD, 0x20, 0x6D, 0x3A, 0x7B, 0x65, - 0x2A, 0xE2, 0x45, 0xCA, 0xFC, 0x46, 0xFA, 0x83, 0x60, 0x94, 0x04, 0x4B, 0xB4, 0x4B, 0x43, 0x68, 0x10, 0x1C, 0xA8, 0x9B, 0x02, 0x16, 0x5F, 0x79, - 0x9F, 0x79, 0x7D, 0x9E, 0xF7, 0x26, 0x3C, 0x5A, 0x6C, 0xA7, 0x1F, 0x9E, 0x13, 0x94, 0x74, 0x33, 0x44, 0x1E, 0xCA, 0xC2, 0xF2, 0xE8, 0xFB, 0xDD, - 0x06, 0x7D, 0x8E, 0x54, 0xFC, 0x15, 0x01, 0x88, 0xEB, 0xDB, 0xBD, 0x55, 0x83, 0x2C, 0x6B, 0xDE, 0x84, 0x3C, 0x23, 0xE2, 0xAA, 0xDF, 0x05, 0xD5, - 0x51, 0x99, 0xD4, 0x46, 0x69, 0x54, 0x17, 0x2F, 0x4A, 0x93, 0x06, 0xE9, 0xCE, 0x89, 0xA4, 0x67, 0xAB, 0x7F, 0xC2, 0x26, 0xC9, 0xFF, 0x1D, 0x3A, - 0x6A, 0x23, 0x99, 0xD7, 0x00, 0x79, 0x0A, 0xF9, 0x61, 0x62, 0x16, 0x71, 0x2C, 0xAE, 0x05, 0x56, 0x78, 0x31, 0xA8, 0xC0, 0xD0, 0x53, 0x61, 0x1F, - 0xA6, 0x9C, 0x20, 0xA2, 0xD4, 0x78, 0x02, 0x20, 0xFA, 0x3C, 0xE9, 0x62, 0x31, 0x11, 0xD3, 0x97, 0xB4, 0x45, 0xB7, 0x1B, 0x7C, 0x91, 0xE4, 0xB9, - 0x8C, 0x68, 0xD0, 0x15, 0x41, 0x54, 0x0A, 0xE1, 0x04, 0x71, 0x76, 0xD3, 0x7F, 0xE0, 0x02, 0x7A, 0x10, 0x3F, 0x3E, 0x88, 0xF1, 0xF9, 0x1C, 0xBC, - 0x66, 0x70, 0xBA, 0xA9, 0xDF, 0x05, 0xFA, 0x4A, 0xC9, 0x5A, 0x44, 0xDD, 0xA3, 0x0E, 0x21, 0x9A, 0x35, 0x44, 0x1E, 0xB1, 0x88, 0x57, 0x6F, 0xA0, - 0xC9, 0x62, 0xC4, 0x17, 0x7E, 0xCF, 0x47, 0x0C, 0xED, 0x01, 0x7D, 0x63, 0xBE, 0x86, 0x5A, 0x63, 0xAD, 0xB6, 0x53, 0x7F, 0xA8, 0x3B, 0xC2, 0xF2, - 0x63, 0xA0, 0x4D, 0xB3, 0x4C, 0xA0, 0x6D, 0xDC, 0xC5, 0x56, 0x11, 0x1F, 0x86, 0x16, 0x51, 0x35, 0x16, 0xAC, 0x98, 0xDA, 0xEA, 0xDD, 0xE5, 0xF4, - 0xF4, 0x34, 0x39, 0xFE, 0x67, 0xD3, 0x74, 0x1C, 0xC7, 0x9B, 0x82, 0x49, 0x7E, 0xE2, 0x6D, 0x20, 0x2E, 0x03, 0x65, 0xC9, 0x6D, 0x65, 0x43, 0x19, - 0x12, 0xF3, 0x72, 0x1A, 0xC0, 0xC6, 0x36, 0xE3, 0x7C, 0x8B, 0xA2, 0xBD, 0x71, 0x4A, 0x15, 0xB9, 0xF0, 0x3A, 0x0E, 0xB2, 0x20, 0x1F, 0x43, 0x4C, - 0xCE, 0xF3, 0x64, 0x12, 0x1D, 0xFF, 0x49, 0x94, 0x3B, 0x29, 0xC6, 0xD3, 0x18, 0xC0, 0x0F, 0x40, 0x41, 0x28, 0xCE, 0x67, 0xC0, 0x14, 0x91, 0x19, - 0x57, 0xD4, 0xD3, 0xAF, 0xD8, 0xCB, 0x8B, 0x8A, 0x29, 0xC5, 0x8E, 0x25, 0xB9, 0x90, 0x81, 0xB1, 0x1A, 0x5F, 0x93, 0xD0, 0x9B, 0x08, 0x1A, 0xC5, - 0x80, 0x0A, 0x15, 0x88, 0x3F, 0xED, 0x0E, 0xC5, 0x8B, 0x84, 0xD6, 0x7E, 0x82, 0xC7, 0xFC, 0x9D, 0x04, 0xA0, 0xBE, 0xBB, 0xC3, 0xB5, 0x7C, 0x4F, - 0x64, 0x0F, 0x8F, 0x29, 0xCA, 0xCE, 0xAE, 0xCB, 0x6F, 0x3D, 0xAE, 0x2C, 0x40, 0x8F, 0x42, 0x75, 0x73, 0x53, 0x65, 0x33, 0xD4, 0x59, 0x73, 0x80, - 0x90, 0xA6, 0xA9, 0x6E, 0x86, 0x8F, 0xC5, 0x7D, 0x15, 0x41, 0x46, 0x83, 0x8B, 0x18, 0xB2, 0x1A, 0x97, 0x4D, 0xE4, 0x16, 0x59, 0x2D, 0x16, 0xDB, - 0x04, 0x2B, 0xC9, 0xA1, 0x6D, 0x2E, 0x0E, 0x24, 0x28, 0x18, 0x8C, 0x1C, 0x38, 0x13, 0x29, 0x85, 0x0A, 0x81, 0x94, 0x04, 0x01, 0x0E, 0xF9, 0x58, - 0x05, 0xFA, 0xD0, 0xD8, 0xFB, 0x1D, 0xC4, 0xCE, 0x1D, 0x36, 0x62, 0x1C, 0x21, 0xA3, 0x1B, 0x53, 0xEE, 0xAF, 0xD1, 0xE3, 0x21, 0x83, 0x6D, 0x98, - 0x9D, 0x35, 0xB2, 0x5F, 0x53, 0x88, 0x53, 0xC7, 0xD9, 0x7E, 0xC2, 0xA2, 0x69, 0x3D, 0x45, 0xE9, 0xCD, 0x1F, 0x7D, 0xA2, 0x0E, 0xDA, 0x36, 0xF5, - 0x3B, 0x37, 0x63, 0x35, 0xEB, 0x66, 0x36, 0x8F, 0xD4, 0x2E, 0x4F, 0x1F, 0x2D, 0x1F, 0xE9, 0x2A, 0xCB, 0x8C, 0xC5, 0x20, 0x73, 0x4C, 0x66, 0xC2, - 0x83, 0xC1, 0x40, 0x07, 0x59, 0xC0, 0xE0, 0xB1, 0x5F, 0xD8, 0x67, 0xE4, 0x98, 0xB0, 0x5F, 0x74, 0xC3, 0x2A, 0xC6, 0xB0, 0x25, 0xC2, 0x61, 0x9C, - 0x78, 0xA2, 0x80, 0xC8, 0x56, 0x72, 0xA8, 0x56, 0xC2, 0x44, 0x85, 0x20, 0x3A, 0x72, 0x0A, 0x3B, 0x08, 0x6B, 0x8D, 0x8D, 0x2F, 0xA0, 0x00, 0x47, - 0x47, 0x97, 0xD9, 0xD7, 0xEA, 0xFD, 0x5A, 0xB7, 0x50, 0xC2, 0x3A, 0x43, 0x0B, 0xBE, 0x50, 0xE2, 0x6F, 0x6D, 0x40, 0x4F, 0x42, 0xFD, 0xD0, 0x63, - 0x3F, 0x45, 0x7B, 0xD1, 0xD9, 0x2B, 0xB1, 0xF9, 0x52, 0x39, 0xC2, 0xA6, 0xE3, 0xB3, 0xD5, 0x1A, 0x78, 0xF5, 0x39, 0xFD, 0x1B, 0x90, 0x2B, 0xAB, - 0xBA, 0xB7, 0x4A, 0x7F, 0x57, 0x07, 0x17, 0x2E, 0xD4, 0xAF, 0x2D, 0x15, 0x4A, 0x5A, 0xBC, 0x43, 0x59, 0xAA, 0x01, 0xB2, 0xD8, 0xF1, 0xAB, 0xE2, - 0x06, 0x57, 0x35, 0x0A, 0xAC, 0x34, 0xCE, 0xAA, 0xA6, 0xFA, 0xA8, 0x6E, 0x1E, 0x4C, 0xB5, 0xA1, 0x90, 0xAA, 0xFA, 0xD0, 0xBE, 0x40, 0x5A, 0x97, - 0x6C, 0x37, 0xB6, 0x31, 0xB0, 0xB6, 0x31, 0x4D, 0xDB, 0x27, 0x8C, 0xDB, 0xB5, 0xC0, 0x6D, 0xCD, 0xD6, 0xFA, 0xF3, 0x46, 0xFB, 0x76, 0x5F, 0x77, - 0xB1, 0x69, 0x47, 0x8F, 0xD4, 0x87, 0x38, 0xEA, 0x30, 0x3E, 0xF5, 0xB0, 0x9B, 0xBA, 0x93, 0x56, 0x7D, 0xAA, 0x5E, 0xA0, 0xEE, 0xE6, 0xD4, 0xAE, - 0xE3, 0x6A, 0xE7, 0x09, 0x68, 0x07, 0x1A, 0x68, 0x23, 0x42, 0x68, 0x4F, 0x66, 0x68, 0x83, 0x86, 0x68, 0x27, 0xB6, 0xED, 0x0E, 0xB8, 0x7B, 0x59, - 0xB8, 0x41, 0xDB, 0x19, 0xAE, 0x6D, 0x10, 0xC5, 0xBA, 0x4B, 0x65, 0xBD, 0x27, 0xB4, 0xCE, 0xF4, 0x5A, 0xDF, 0xE2, 0xAD, 0x0B, 0x40, 0xAE, 0xAB, - 0xEB, 0x77, 0xED, 0x7A, 0x9F, 0x4C, 0x7D, 0x10, 0xA8, 0xEE, 0x54, 0xD7, 0xE4, 0xAA, 0xA3, 0x42, 0xCD, 0x6B, 0x8B, 0xF5, 0x98, 0xF3, 0xE6, 0x4C, - 0x79, 0x87, 0xDE, 0x7E, 0x65, 0x4F, 0x9F, 0x23, 0x36, 0x3F, 0x0C, 0xFD, 0x86, 0xB6, 0xCD, 0x76, 0xA9, 0xAA, 0xFD, 0x33, 0xA0, 0x5B, 0xAA, 0xEA, - 0x35, 0xEA, 0x42, 0x33, 0xFC, 0x0D, 0x19, 0x8A, 0x04, 0xEE, 0x93, 0x5E, 0xDB, 0x09, 0xF3, 0xB9, 0x29, 0x11, 0xC8, 0x8C, 0xC9, 0x96, 0x65, 0x69, - 0xB3, 0x44, 0xE9, 0x19, 0x09, 0x0B, 0x37, 0x68, 0xD6, 0x85, 0x0C, 0x2B, 0xBA, 0x6B, 0x76, 0x28, 0x89, 0x14, 0xE6, 0xC0, 0x6C, 0x58, 0x19, 0xA9, - 0x13, 0xDF, 0x6C, 0x57, 0x9C, 0x5D, 0x08, 0xAE, 0xF3, 0xED, 0xB6, 0xE5, 0xD6, 0xC3, 0x95, 0x73, 0x27, 0x3E, 0xEF, 0xB9, 0x47, 0x59, 0x4E, 0xB8, - 0x7F, 0x91, 0xDE, 0x9C, 0x87, 0x78, 0xBC, 0x36, 0x96, 0x4B, 0x5A, 0x5C, 0x84, 0xEC, 0x87, 0x41, 0xD7, 0xDF, 0x5D, 0x95, 0x47, 0xD5, 0x83, 0xE7, - 0x27, 0xD0, 0x79, 0x2E, 0xD3, 0x81, 0x01, 0xCE, 0x69, 0xA9, 0xC1, 0x69, 0xA2, 0xBC, 0x49, 0x32, 0xB6, 0x89, 0x5B, 0xAB, 0x09, 0xA0, 0x49, 0x73, - 0xE8, 0x33, 0xD3, 0x00, 0xC9, 0x1D, 0xA2, 0x54, 0x26, 0x90, 0x1D, 0x53, 0xF9, 0x01, 0x26, 0x32, 0xFF, 0x02, 0x4B, 0xC0, 0x85, 0x75, 0xB0, 0x25, - 0x19, 0x48, 0x0F, 0x19, 0x22, 0x3E, 0xC5, 0xD6, 0xF7, 0x3C, 0xC8, 0x27, 0xAA, 0xC9, 0x23, 0x70, 0xE5, 0xEF, 0x27, 0xE4, 0x23, 0x58, 0xE2, 0xAB, - 0x0B, 0xF4, 0x95, 0x81, 0xF1, 0xE1, 0x61, 0x3D, 0x0F, 0x06, 0xFD, 0x8B, 0xC8, 0xF8, 0x0E, 0x0C, 0x7D, 0x0E, 0x85, 0xFC, 0x4D, 0x8E, 0x1E, 0x6A, - 0x32, 0xEF, 0x45, 0x97, 0x4B, 0xB0, 0x10, 0x7F, 0x6B, 0xD3, 0x97, 0x55, 0x7E, 0x5A, 0xFE, 0xCF, 0x91, 0x8F, 0x62, 0xE7, 0x95, 0x29, 0x0B, 0x8F, - 0x6D, 0xC1, 0x78, 0x77, 0x75, 0xA8, 0xE2, 0x42, 0x85, 0xB0, 0xA5, 0xA2, 0x4D, 0x8B, 0x37, 0x2D, 0x4B, 0xB5, 0x42, 0x16, 0x3B, 0xC2, 0x55, 0x71, - 0xE1, 0x55, 0x8D, 0x08, 0x2B, 0x8D, 0x0B, 0x55, 0x53, 0x95, 0x54, 0x37, 0x86, 0x53, 0x6D, 0xAF, 0x52, 0x55, 0x9D, 0xF0, 0x51, 0xAE, 0xA0, 0xBA, - 0x02, 0x40, 0x79, 0x0F, 0x7E, 0x02, 0xF5, 0x7C, 0x10, 0x3F, 0xF4, 0x4C, 0x22, 0xF2, 0x80, 0x4A, 0xBE, 0x7E, 0xBE, 0xED, 0xF6, 0xE0, 0xBD, 0x2D, - 0xE0, 0xC9, 0xFA, 0x77, 0x86, 0xF1, 0x1B, 0x84, 0xC5, 0xDD, 0xA5, 0xF5, 0xDE, 0xA3, 0x7B, 0x67, 0xDA, 0xDF, 0xB7, 0xA6, 0xEF, 0x69, 0x23, 0x77, - 0x04, 0x30, 0xF7, 0x3B, 0x9F, 0x7B, 0x1F, 0xD3, 0x7D, 0x50, 0xEB, 0xDE, 0x78, 0x76, 0xD7, 0x2C, 0x70, 0x47, 0x15, 0x50, 0xB7, 0x05, 0xF9, 0xC1, - 0xB1, 0xF5, 0x66, 0x59, 0x7A, 0xC7, 0x61, 0x7B, 0x05, 0xF1, 0xE7, 0xCC, 0xD5, 0x29, 0x0F, 0x75, 0xDD, 0x86, 0xB0, 0x13, 0xA0, 0xA7, 0x3C, 0xD4, - 0x5E, 0x6A, 0xA1, 0x1F, 0xAD, 0xD6, 0x64, 0x20, 0x0E, 0x34, 0x03, 0x48, 0xD3, 0xC7, 0x85, 0xAE, 0x02, 0xA7, 0x73, 0x29, 0xD1, 0x91, 0xD4, 0xE8, - 0x08, 0x02, 0xD0, 0xEF, 0xE8, 0x7D, 0x0D, 0xE8, 0xF2, 0xDF, 0xE8, 0x81, 0x8E, 0xF7, 0x51, 0x21, 0xE9, 0x2A, 0x06, 0xFF, 0x87, 0x8F, 0xF9, 0xD9, - 0x92, 0xBF, 0x78, 0xF6, 0xD7, 0xB0, 0x7E, 0x8D, 0x4C, 0xFA, 0x94, 0xF1, 0xBF, 0xA1, 0xA5, 0xBB, 0x89, 0xFC, 0xAA, 0x65, 0x7A, 0x5E, 0xCF, 0x87, - 0x43, 0xA1, 0x0B, 0x8E, 0xD0, 0xF5, 0x88, 0xE8, 0x99, 0x3E, 0x74, 0x30, 0xBA, 0xAD, 0x16, 0x7D, 0xD3, 0xE8, 0x3F, 0x17, 0xA0, 0xE3, 0x8A, 0x7D, - 0x17, 0x3D, 0x7A, 0xF4, 0x82, 0x3F, 0x64, 0xE0, 0xC3, 0xF8, 0xFD, 0xB0, 0x0C, 0xDF, 0x11, 0xF4, 0xC5, 0xFC, 0xD5, 0x79, 0xEE, 0xC2, 0x63, 0x1E, - 0xE3, 0x43, 0x7E, 0x41, 0xAF, 0x09, 0xA8, 0x47, 0xC5, 0x80, 0xB7, 0x35, 0x7A, 0x0B, 0x7F, 0xBC, 0xB3, 0xFA, 0xDE, 0x91, 0x0A, 0x5F, 0x51, 0x7D, - 0xE7, 0x8C, 0xC0, 0xFF, 0xD6, 0x86, 0xC4, 0x59, 0x3E, 0x50, 0x49, 0xFD, 0xBF, 0x63, 0xFF, 0x3B, 0x88, 0x5F, 0x46, 0xE3, 0xFF, 0x7C, 0xD2, 0x9D, - 0x58, 0xE9, 0x5C, 0x7E, 0x3F, 0x8D, 0xF0, 0x1F, 0x10, 0x40, 0x8F, 0xC0, 0xF2, 0x33, 0xC4, 0xD2, 0x45, 0xB8, 0xA0, 0x2B, 0x1E, 0xFE, 0x15, 0x91, - 0x5F, 0x74, 0x8E, 0x1E, 0x8B, 0x47, 0x77, 0xD1, 0xF4, 0x61, 0x34, 0xD3, 0xD7, 0x2B, 0xBE, 0x89, 0xE8, 0x9F, 0xB9, 0x40, 0x17, 0x02, 0xA4, 0xC3, - 0xE3, 0x3F, 0xBE, 0xF6, 0xD7, 0x5E, 0xE8, 0x09, 0x6A, 0xFF, 0x5D, 0x08, 0x7A, 0x5F, 0x22, 0x5D, 0xD0, 0xED, 0xA7, 0x60, 0xF0, 0x31, 0xDF, 0x74, - 0x53, 0x28, 0xFA, 0x6C, 0xF4, 0xFF, 0xC5, 0xA4, 0x2B, 0xDB, 0x7D, 0x0D, 0x16, 0x39, 0x08, 0xB1, 0xDE, 0x05, 0x5A, 0xEF, 0x03, 0xA2, 0x6F, 0x6A, - 0xD1, 0x73, 0xED, 0xE8, 0xD3, 0x92, 0xF4, 0x08, 0xAE, 0xF4, 0x77, 0x65, 0xFA, 0xBE, 0x37, 0xFD, 0x60, 0x9D, 0xDE, 0x71, 0x34, 0xBE, 0x49, 0x8A, - 0xEA, 0x21, 0x6F, 0x3B, 0x05, 0x07, 0xDE, 0x8C, 0x3B, 0x93, 0x14, 0x20, 0x7C, 0x37, 0xC9, 0xE9, 0x41, 0xDE, 0x1E, 0xFD, 0x8C, 0x8D, 0x0C, 0x9E, - 0xF7, 0x7F, 0xDF, 0x55, 0x11, 0x41, 0xE2, 0x2C, 0x1E, 0xE2, 0x43, 0xFF, 0x7F, 0xE6, 0xBF, 0x7C, 0xA4, 0xD9, 0xF9, 0xE6, 0x40, 0xC5, 0x92, 0x61, - 0x24, 0x22, 0xEF, 0xE7, 0xDF, 0x5E, 0xC4, 0x7B, 0xE3, 0x7A, 0x1E, 0xF1, 0xCC, 0x75, 0x3C, 0x31, 0x27, 0x6E, 0xEB, 0x8E, 0x6F, 0x7E, 0xE4, 0x9F, - 0x9F, 0xE2, 0xB8, 0x3A, 0x79, 0x97, 0xB4, 0x78, 0xB4, 0x55, 0x7E, 0x08, 0x2A, 0x0F, 0xE3, 0x96, 0x0F, 0x1B, 0x90, 0x77, 0xB2, 0x90, 0xA7, 0x93, - 0x90, 0x1F, 0x60, 0xCF, 0xBB, 0x35, 0x88, 0xD7, 0xE5, 0xC5, 0xF1, 0x28, 0x39, 0x1B, 0xEE, 0x1D, 0x0A, 0x10, 0xFD, 0x91, 0x76, 0x12, 0xFC, 0xFF, - 0xA0, 0xBC, 0x11, 0xFD, 0x5B, 0xB3, 0x1C, 0x59, 0xAE, 0xA7, 0x22, 0xCF, 0x6C, 0xCB, 0x13, 0x8D, 0xE4, 0xB6, 0x40, 0xF2, 0xCD, 0x15, 0xFD, 0x33, - 0x97, 0x1C, 0xF7, 0x48, 0xEF, 0x62, 0x40, 0x1E, 0x3D, 0xA6, 0x1F, 0xEA, 0xD2, 0xC3, 0xD0, 0xE9, 0xC3, 0x0E, 0xE8, 0x9D, 0xEA, 0x7A, 0x3A, 0x3E, - 0xFD, 0x00, 0x15, 0x7A, 0xB7, 0x0B, 0xF2, 0x3A, 0x40, 0x79, 0x9E, 0x87, 0x78, 0xA0, 0x89, 0x38, 0x22, 0x64, 0x7C, 0x12, 0x1F, 0x07, 0x5D, 0x8D, - 0xBF, 0xE6, 0xC6, 0xED, 0x0E, 0xE5, 0xBD, 0xCF, 0xF2, 0x64, 0xE8, 0xB8, 0x06, 0x48, 0x1C, 0xA4, 0x45, 0xDE, 0xA5, 0x38, 0xDF, 0x13, 0x9D, 0x6F, - 0x0A, 0xC9, 0x73, 0xB9, 0xE4, 0xD3, 0x78, 0xF2, 0x08, 0x94, 0xF2, 0x77, 0x58, 0xF9, 0xBE, 0x00, 0xF9, 0x41, 0xB8, 0x7C, 0x63, 0x83, 0xBC, 0x66, - 0x5E, 0x1E, 0xF5, 0xC1, 0xDD, 0x36, 0xC7, 0x1D, 0x8C, 0x7C, 0x6F, 0x86, 0x61, 0x57, 0x8D, 0x86, 0xB3, 0x04, 0xEA, 0x87, 0x34, 0x36, 0x34, 0x2A, - 0xF3, 0xFF, 0x2F, 0x26, 0xBA, 0x71, 0x98, 0x9E, 0xA3, 0x0E, 0xED, 0xB7, 0x54, 0x90, 0x68, 0xF1, 0x76, 0x64, 0xA9, 0xFE, 0x64, 0xB1, 0x71, 0xAE, - 0x8A, 0xB7, 0xAE, 0x6A, 0xA8, 0xAC, 0x34, 0x80, 0xAA, 0xA6, 0x08, 0x50, 0xDD, 0x70, 0x4D, 0xB5, 0xD1, 0xA5, 0xAA, 0x0C, 0xE8, 0x09, 0xE5, 0x6F, - 0x19, 0xF4, 0x15, 0xF4, 0x89, 0xFA, 0x42, 0xFD, 0x09, 0x50, 0x7D, 0xED, 0xA6, 0x5E, 0x73, 0x51, 0xEE, 0x6C, 0xAB, 0xBF, 0x15, 0x95, 0xB7, 0x34, - 0xEB, 0xA1, 0x7B, 0xE5, 0x42, 0xB8, 0x72, 0x98, 0xB5, 0x1C, 0xEC, 0x59, 0xEE, 0xE4, 0x96, 0xD3, 0xB1, 0xC8, 0x07, 0x3C, 0xC9, 0xDD, 0x4C, 0xF4, - 0x75, 0x33, 0xFA, 0xBC, 0xB7, 0x3E, 0x10, 0x4A, 0x8F, 0xE8, 0xA5, 0x9F, 0x1C, 0xA0, 0x07, 0x45, 0xA0, 0x7F, 0x95, 0xA0, 0xB7, 0x83, 0xEF, 0x7B, - 0x45, 0xEC, 0x93, 0x47, 0xE8, 0x33, 0x16, 0xF5, 0x20, 0x13, 0xE4, 0x2E, 0x7D, 0xF2, 0x1E, 0x41, 0x39, 0x13, 0x0A, 0xF9, 0xD6, 0xA5, 0x5C, 0x00, - 0xA9, 0xFC, 0x80, 0xAC, 0x5C, 0x82, 0xAF, 0x7C, 0x43, 0x0C, 0xF9, 0x60, 0x86, 0xBC, 0xF1, 0x44, 0xAE, 0x49, 0x23, 0x47, 0x45, 0x81, 0xD7, 0x82, - 0xDE, 0x83, 0xEB, 0xF0, 0xE6, 0xBF, 0x77, 0x87, 0xD7, 0x7D, 0xE5, 0xCB, 0x9E, 0xE3, 0xF2, 0x3E, 0xEC, 0xC3, 0xB3, 0xC0, 0x0C, 0xC2, 0x3F, 0x7B, - 0xE6, 0xDE, 0x86, 0x36, 0x4D, 0x40, 0x65, 0x9A, 0x4D, 0x02, 0x8C, 0x7C, 0x00, 0xC7, 0x9A, 0x20, 0x93, 0xDA, 0x84, 0x9B, 0x01, 0x91, 0x31, 0x69, - 0x25, 0xA6, 0x04, 0xB4, 0x36, 0xA1, 0x6D, 0xB2, 0x9D, 0x21, 0xA3, 0x99, 0xE1, 0x52, 0x30, 0x33, 0xB1, 0x2B, 0x8B, 0x2E, 0x51, 0x36, 0x04, 0x82, - 0x0C, 0x40, 0x85, 0xB1, 0xB0, 0x49, 0xB1, 0xA2, 0xC2, 0x30, 0x49, 0xB5, 0xF8, 0x57, 0x9D, 0xAD, 0x17, 0xE3, 0x6B, 0x57, 0x60, 0x33, 0x57, 0x29, - 0xF2, 0x56, 0x8A, 0x5B, 0x2D, 0xE1, 0x76, 0xD6, 0xA9, 0x73, 0xD6, 0x07, 0xD0, 0xD5, 0xF0, 0xCA, 0xD5, 0x4C, 0x89, 0x55, 0xC8, 0x51, 0x55, 0x54, - 0x10, 0x95, 0xCF, 0xD8, 0x54, 0x53, 0x9B, 0x14, 0x17, 0x5D, 0x94, 0x02, 0x65, 0x78, 0x5B, 0x19, 0x94, 0x88, 0xAF, 0xA7, 0x0B, 0x75, 0x4E, 0x1E, - 0x2E, 0x34, 0x1D, 0x02, 0x32, 0x49, 0x1A, 0x2E, 0x7D, 0xAA, 0x29, 0xB9, 0x76, 0x26, 0xED, 0xDE, 0x21, 0x09, 0x9B, 0x40, 0x8A, 0xE0, 0xE1, 0x48, - 0xAD, 0x67, 0x64, 0xF3, 0x51, 0x34, 0x83, 0x48, 0x44, 0xA5, 0x33, 0x14, 0xF5, 0x29, 0xE4, 0x66, 0xF5, 0x36, 0x3B, 0x11, 0x94, 0xE7, 0x06, 0x64, - 0x47, 0xF9, 0x23, 0x78, 0xE5, 0x93, 0x02, 0xAB, 0x27, 0xF3, 0x8D, 0xE7, 0x14, 0x6E, 0xA7, 0x97, 0x53, 0x67, 0x57, 0x4B, 0x07, 0x84, 0x81, 0x3E, - 0x07, 0xC4, 0x04, 0x0E, 0xD2, 0xDC, 0x4D, 0x0F, 0xDE, 0xC6, 0x4D, 0xB3, 0x66, 0x2B, 0x97, 0x26, 0xCC, 0x73, 0x86, 0xC0, 0xA7, 0x9B, 0xAE, 0x85, - 0x8C, 0x05, 0x7D, 0x0C, 0x60, 0x34, 0x4C, 0xA1, 0xC7, 0x8B, 0x23, 0x6C, 0x9B, 0xAE, 0x91, 0x84, 0x51, 0x63, 0x04, 0xD0, 0x54, 0xA4, 0xB4, 0x26, - 0xA4, 0xB6, 0x01, 0xC8, 0xAF, 0xF1, 0xC7, 0xAA, 0xC9, 0x87, 0x86, 0x01, 0x7D, 0x07, 0x71, 0x6E, 0x87, 0x0D, 0x12, 0x47, 0x48, 0x8A, 0x0D, 0x3A, - 0x08, 0xFA, 0xCA, 0xC3, 0xD8, 0x7C, 0x00, 0x2C, 0x1B, 0xBD, 0xA8, 0x3B, 0xEA, 0x02, 0x32, 0xFC, 0x0D, 0x17, 0x8A, 0x44, 0xEC, 0x13, 0x86, 0x67, - 0x27, 0xCC, 0xE4, 0xA6, 0x44, 0x16, 0x26, 0x5B, 0x93, 0x25, 0x4D, 0x68, 0xA5, 0xE7, 0x77, 0x12, 0x6E, 0x4B, 0x92, 0x8B, 0xD7, 0x51, 0x74, 0xD2, - 0xD1, 0x90, 0x90, 0x11, 0x4C, 0x59, 0x51, 0xD8, 0x17, 0x91, 0x53, 0xDD, 0x10, 0x5D, 0x02, 0xA1, 0x01, 0x80, 0x6F, 0xB7, 0xB8, 0xA0, 0x3A, 0x07, - 0x40, 0x79, 0x18, 0x7E, 0x02, 0x03, 0x7D, 0x10, 0x0F, 0x7B, 0x26, 0x23, 0x79, 0x40, 0x3D, 0x77, 0x5E, 0xF3, 0x3B, 0x76, 0x1F, 0x74, 0x7A, 0x89, - 0xE3, 0xC9, 0xC5, 0x39, 0x19, 0xD1, 0x70, 0x1C, 0x10, 0xD6, 0x1B, 0x17, 0xBD, 0x4D, 0x8F, 0x71, 0x0D, 0x93, 0x34, 0x0D, 0x97, 0xC8, 0x4C, 0xDA, - 0x8B, 0x1B, 0x41, 0x8F, 0xCC, 0xDD, 0x52, 0x4C, 0x5F, 0x16, 0xCC, 0xA0, 0xD9, 0x4B, 0x63, 0x9C, 0x8B, 0xA6, 0x03, 0x7C, 0x0B, 0xAA, 0x1C, 0x8B, - 0x2D, 0xCB, 0x4A, 0x70, 0xAF, 0xBD, 0xD9, 0x45, 0xBD, 0x03, 0xD2, 0xBD, 0x12, 0xD9, 0xCE, 0x99, 0x60, 0xA7, 0x3C, 0x34, 0x46, 0xC1, 0x22, 0xE1, - 0xB6, 0xE8, 0x8D, 0x42, 0xD5, 0x1B, 0x88, 0x12, 0xC3, 0x4F, 0x97, 0x52, 0x3B, 0xD8, 0x8C, 0x2F, 0xE3, 0xC5, 0x6D, 0x22, 0xB2, 0xFE, 0x51, 0x79, - 0x6B, 0x8A, 0x70, 0xFC, 0x61, 0x7F, 0x4C, 0x4E, 0x03, 0xDF, 0x75, 0x0F, 0x79, 0xED, 0x29, 0x75, 0x85, 0xC0, 0xD2, 0x1A, 0x6D, 0x90, 0x25, 0x9B, - 0x87, 0xF5, 0xEB, 0xE1, 0x6A, 0xD9, 0xC4, 0x15, 0x07, 0xA4, 0x95, 0xC6, 0xD6, 0xAF, 0x8A, 0x09, 0xA4, 0x02, 0x3A, 0x9C, 0x7A, 0x8A, 0x94, 0xAA, - 0xDA, 0x8B, 0x52, 0xCB, 0xC3, 0xDF, 0x4E, 0x40, 0xE1, 0x15, 0x3F, 0x61, 0x60, 0xE1, 0x54, 0x18, 0xD2, 0x74, 0x42, 0x97, 0xA9, 0x93, 0x67, 0x49, - 0x13, 0x54, 0x49, 0x31, 0x38, 0xE9, 0x76, 0x17, 0x49, 0xD9, 0xE7, 0x28, 0xFA, 0x1F, 0xA5, 0x68, 0x19, 0xC1, 0xC4, 0x14, 0x1D, 0x15, 0x20, 0x72, - 0x6A, 0x1A, 0x2A, 0xD3, 0x13, 0x1A, 0x80, 0xF6, 0x4E, 0x20, 0x17, 0x54, 0xF7, 0x04, 0xC4, 0x61, 0xEF, 0x27, 0x30, 0xCD, 0xC7, 0xF1, 0x44, 0xCF, - 0x64, 0x04, 0x9E, 0x54, 0x16, 0x3B, 0xAF, 0x22, 0x74, 0x88, 0x75, 0x73, 0x92, 0x2B, 0x73, 0x76, 0x1E, 0x71, 0xD6, 0x6B, 0x37, 0x59, 0xA9, 0x36, - 0x34, 0x13, 0x6A, 0xE4, 0x5E, 0xD0, 0x48, 0x09, 0xCE, 0xD0, 0x55, 0xDD, 0x67, 0x78, 0x32, 0x21, 0x22, 0x63, 0xE2, 0x72, 0x60, 0x3C, 0x3F, 0x5E, - 0xF6, 0xAA, 0x5C, 0x74, 0x75, 0x5A, 0x14, 0xDB, 0x5A, 0xD4, 0x56, 0x2B, 0xBC, 0x42, 0x2B, 0x33, 0x6E, 0x2A, 0xEC, 0xAF, 0x94, 0x24, 0x2C, 0x00, - 0xD2, 0x17, 0x00, 0x98, 0xC1, 0x9D, 0xC4, 0x18, 0x9A, 0xD8, 0xE5, 0x95, 0x3C, 0x75, 0xB6, 0x45, 0x98, 0x20, 0x51, 0xC3, 0x1D, 0xB1, 0xC4, 0x8B, - 0xE0, 0x3D, 0x87, 0xCC, 0xE6, 0x82, 0x14, 0xB2, 0xFC, 0x0F, 0x63, 0x4F, 0x0F, 0x4A, 0x68, 0x0F, 0x92, 0x16, 0x1C, 0xB1, 0x2D, 0x1C, 0x41, 0xD1, - 0x1A, 0xCA, 0x49, 0x1A, 0x71, 0x97, 0x0C, 0x91, 0x17, 0xF6, 0x5C, 0x89, 0xBE, 0x86, 0x7A, 0x54, 0xEF, 0xC1, 0xFC, 0x32, 0xC0, 0x69, 0x45, 0x26, - 0x69, 0x07, 0xF7, 0xE8, 0xD1, 0x33, 0xE8, 0x48, 0xBC, 0xE7, 0x4F, 0x0E, 0xB2, 0x21, 0x05, 0xFE, 0xDE, 0xA4, 0x7C, 0x50, 0x33, 0xE8, 0x0B, 0xC2, - 0xDA, 0xDD, 0x69, 0xA4, 0xFC, 0xDF, 0x67, 0xFF, 0x69, 0xFF, 0xAF, 0x3F, 0x81, 0xF9, 0xB7, 0x8A, 0x2F, 0xE3, 0x19, 0x51, 0xBC, 0x43, 0x38, 0xC3, - 0x6B, 0x01, 0xBC, 0xA5, 0xAF, 0x52, 0x28, 0x5F, 0xB5, 0x68, 0xFF, 0x0E, 0xC2, 0xFF, 0x27, 0x71, 0xFF, 0x73, 0xBA, 0x5B, 0x07, 0xE4, 0x6F, 0x4B, - 0x72, 0x23, 0xE0, 0x33, 0x82, 0xCD, 0xCC, 0x82, 0x9C, 0x6B, 0x45, 0x7C, 0xCD, 0xCE, 0xDE, 0xA4, 0xD6, 0x16, 0xFA, 0x07, 0x80, 0xA8, 0x7A, 0xA7, - 0x71, 0x80, 0x11, 0x70, 0x09, 0x00, 0x8E, 0x86, 0x48, 0xD2, 0x36, 0xFB, 0x60, 0xFA, 0x9A, 0x4A, 0xAD, 0x7B, 0x31, 0x0E, 0x6E, 0xF9, 0x69, 0xD5, - 0x6E, 0xC4, 0xA0, 0xD2, 0x4B, 0xD4, 0x13, 0xD4, 0x5F, 0xB5, 0xC2, 0x94, 0xDD, 0x03, 0xE7, 0x4D, 0xC3, 0x66, 0xDF, 0xA1, 0x7A, 0x2B, 0x55, 0xB9, - 0xB3, 0x08, 0xBC, 0xE9, 0xEF, 0x0A, 0x6E, 0x3E, 0xC2, 0xF7, 0x88, 0x42, 0x37, 0xA3, 0xE6, 0xFB, 0x72, 0xD3, 0x8D, 0x2F, 0xBC, 0xD3, 0xDA, 0xBA, - 0xF5, 0x9B, 0xF7, 0x3D, 0xA7, 0xDD, 0x31, 0xF5, 0xDD, 0xBE, 0xB5, 0x83, 0xDD, 0xDF, 0xCE, 0xF6, 0x28, 0x95, 0x3B, 0xD8, 0x8A, 0x9D, 0xCD, 0xE8, - 0xBE, 0xE0, 0xC8, 0x6E, 0xC7, 0xF2, 0x6E, 0x71, 0x23, 0xBD, 0x17, 0x93, 0xDC, 0x96, 0x64, 0x6F, 0xA4, 0xCE, 0xBC, 0xAF, 0xFB, 0x3F, 0x11, 0x87, - 0xCD, 0xBE, 0x83, 0x9F, 0x2C, 0x44, 0x00, 0xC6, 0x1C, 0x62, 0x2C, 0x28, 0x69, 0x9A, 0x98, 0xDC, 0x35, 0x43, 0x01, 0x3B, 0x93, 0xA5, 0x21, 0x9D, - 0x05, 0xA6, 0xFA, 0x59, 0xEF, 0xD6, 0x7B, 0x0F, 0x54, 0x6F, 0xF8, 0xFA, 0x0A, 0x6E, 0xA1, 0x79, 0x1A, 0x06, 0xD7, 0x7C, 0x3F, 0x37, 0xD0, 0x80, - 0xEF, 0xB4, 0x9C, 0x23, 0x96, 0x1C, 0x07, 0x4C, 0x5C, 0x2A, 0x5C, 0x5C, 0x0E, 0xD3, 0x1B, 0x71, 0xDE, 0xDB, 0xD5, 0x99, 0x9B, 0x88, 0x89, 0xB6, - 0xB8, 0x07, 0x04, 0x7E, 0xF0, 0x6C, 0x7B, 0x1F, 0xE4, 0x5D, 0xB9, 0x76, 0x42, 0x08, 0xED, 0xD7, 0xA8, 0x0E, 0x57, 0x07, 0x24, 0xC3, 0x8A, 0x5F, - 0xAD, 0xF7, 0x6F, 0x7F, 0xED, 0xBF, 0x7F, 0x15, 0xBF, 0x91, 0x32, 0xD0, 0x04, 0x67, 0xE7, 0xE3, 0xF6, 0xA4, 0x4E, 0x5E, 0x4B, 0x04, 0x28, 0xD7, - 0x9C, 0x9B, 0x7F, 0x28, 0x91, 0xDF, 0x72, 0x98, 0x53, 0x06, 0xC9, 0xA1, 0x53, 0xCC, 0x61, 0xBB, 0xE4, 0x2F, 0xA5, 0xF3, 0xE1, 0xF7, 0xA2, 0xD5, - 0x4C, 0x88, 0x97, 0x1E, 0x0E, 0x4A, 0xC7, 0x4E, 0x6F, 0xC0, 0x9E, 0x8C, 0x11, 0x1E, 0xB8, 0xB1, 0x87, 0x84, 0xEF, 0x01, 0x91, 0x5A, 0x7A, 0x98, - 0xD3, 0x15, 0xDB, 0x0D, 0x6F, 0xFC, 0x9F, 0x82, 0xBD, 0x86, 0xF9, 0xB6, 0xBF, 0xF4, 0x37, 0x00, 0xE4, 0xEA, 0x3D, 0xC9, 0x9A, 0xAD, 0x90, 0x95, - 0x21, 0x74, 0xC5, 0x5C, 0x78, 0x65, 0xF3, 0x65, 0x45, 0x62, 0xC1, 0xCE, 0xC0, 0xF6, 0x73, 0xCC, 0xFE, 0x4D, 0xCA, 0xBA, 0x9C, 0xFD, 0xD6, 0x12, - 0x2A, 0x4E, 0x85, 0x39, 0xD0, 0x3F, 0x90, 0x57, 0xB8, 0xB4, 0x7F, 0x25, 0x9C, 0xDB, 0xE3, 0xB1, 0x12, 0x9D, 0x11, 0xC2, 0x97, 0x6D, 0x5E, 0x67, - 0x5E, 0x28, 0xD4, 0x0E, 0x6C, 0x9A, 0x8E, 0x8B, 0x01, 0x39, 0x31, 0x22, 0x08, 0xD2, 0x1F, 0x40, 0xD7, 0x7C, 0x15, 0x4D, 0xB1, 0xA4, 0x79, 0xAA, - 0x01, 0x7D, 0x7A, 0x44, 0xAE, 0x2D, 0x0E, 0x12, 0xC0, 0x8E, 0xFF, 0xF4, 0x07, 0xAB, 0x90, 0x38, 0x50, 0x83, 0x58, 0x49, 0xC9, 0x60, 0xAF, 0xBF, - 0x5A, 0x9D, 0x41, 0x4D, 0xE1, 0xB7, 0xC6, 0x3F, 0x18, 0x7F, 0xA5, 0x25, 0x99, 0x79, 0x55, 0x8A, 0x60, 0x75, 0xDD, 0x2D, 0x71, 0xC5, 0x92, 0x6C, - 0x0D, 0x9A, 0x69, 0x0D, 0x23, 0x67, 0x49, 0x32, 0x65, 0x89, 0xB4, 0xAF, 0x5C, 0xCF, 0xAE, 0x7E, 0x80, 0xAA, 0x60, 0x3C, 0xA8, 0xA2, 0x32, 0xA1, - 0x0A, 0xAA, 0x9A, 0x72, 0x7B, 0x4B, 0x7D, 0xB5, 0x45, 0xE5, 0x79, 0xC1, 0xE7, 0x2A, 0x40, 0x09, 0x0A, 0x3D, 0xF9, 0x15, 0x3A, 0x65, 0x7E, 0x35, - 0x3D, 0x6E, 0x32, 0xD9, 0x26, 0x58, 0x12, 0xB5, 0x2B, 0x3D, 0x6F, 0x48, 0xF2, 0xE5, 0x4C, 0x72, 0x61, 0x44, 0xDA, 0xB8, 0x3D, 0x1A, 0xE2, 0x60, - 0x84, 0xE3, 0x5C, 0x14, 0x96, 0x82, 0xE8, 0x6A, 0x65, 0x88, 0xAE, 0x54, 0x28, 0xEE, 0xE8, 0x0D, 0xF8, 0x1B, 0x84, 0xD7, 0x02, 0x24, 0xA9, 0xF1, - 0x43, 0x98, 0xE4, 0xE3, 0x5A, 0xD3, 0xC3, 0x59, 0xC6, 0x23, 0xBC, 0xB1, 0x93, 0x5B, 0xB7, 0xF3, 0x61, 0x93, 0x23, 0x0D, 0x58, 0x97, 0xC3, 0x23, - 0x85, 0x83, 0xD2, 0xE0, 0x46, 0x4B, 0xDE, 0x6B, 0x63, 0xE4, 0xA1, 0xC6, 0xC0, 0xFC, 0x95, 0x86, 0x0C, 0x72, 0xA6, 0x02, 0x01, 0x37, 0x77, 0x83, - 0xD5, 0x64, 0x19, 0xC3, 0xC1, 0x18, 0xC8, 0x4B, 0x18, 0x4F, 0xCC, 0x17, 0x56, 0x59, 0x17, 0xDD, 0xD9, 0x96, 0xE3, 0x18, 0x2D, 0x6A, 0x65, 0x96, - 0x8E, 0xD9, 0x15, 0x1A, 0x00, 0x2A, 0x3A, 0xCA, 0x29, 0x49, 0xCB, 0x28, 0x59, 0x00, 0x00, 0x9C, 0xA1, 0x9C, 0xC8, 0x0D, 0x99, 0x40, 0x20, 0x2A, - 0xE1, 0x8B, 0xB7, 0x47, 0x80, 0x91, 0x30, 0x2A, 0x18, 0xB9, 0x9C, 0x13, 0xC1, 0x87, 0x85, 0xD0, 0x72, 0x00, 0xD1, 0xCF, 0x3F, 0xF6, 0x0B, 0x3E, - 0x7E, 0x07, 0x8F, 0xB6, 0x5F, 0x87, 0xBB, 0x91, 0xE3, 0xED, 0xB8, 0x01, 0x37, 0xD5, 0x88, 0x9F, 0x66, 0xC8, 0x81, 0xCD, 0xD8, 0xF3, 0x60, 0xAC, - 0xF5, 0x83, 0xD3, 0x59, 0xF5, 0x1E, 0xEA, 0x0F, 0x11, 0x3C, 0xAD, 0x28, 0x2C, 0x6D, 0xC9, 0x11, 0x4D, 0xDA, 0x01, 0x3D, 0x7B, 0xF3, 0x0C, 0x8C, - 0xE3, 0xCC, 0x3C, 0xE5, 0xDC, 0x41, 0xD5, 0x4C, 0x92, 0xD3, 0x49, 0xF9, 0xB8, 0xB2, 0xBA, 0xB1, 0xB8, 0x09, 0x9B, 0xAA, 0x69, 0x6C, 0x93, 0x19, - 0xE3, 0x45, 0x26, 0x89, 0x14, 0x66, 0x8A, 0xC7, 0x25, 0x0E, 0x8E, 0xA5, 0xCF, 0xF0, 0x0A, 0x2B, 0xAA, 0xCA, 0xAE, 0x0F, 0x4A, 0x38, 0x02, 0x93, - 0x09, 0x74, 0x24, 0xF5, 0xE8, 0x91, 0x1C, 0xAC, 0x20, 0x13, 0x09, 0xEF, 0x8E, 0x6E, 0x8F, 0x9B, 0x15, 0x54, 0xA4, 0x5A, 0x40, 0x75, 0xC4, 0x2D, - 0x6C, 0xEC, 0x85, 0x4B, 0x11, 0xBD, 0x99, 0x22, 0x23, 0x0F, 0x05, 0x8C, 0x2D, 0xA3, 0x8D, 0x89, 0x45, 0xA7, 0x66, 0x45, 0x2A, 0x7C, 0xA2, 0x9A, - 0xCA, 0x09, 0xB7, 0x5C, 0xC4, 0xBA, 0xE7, 0xC3, 0x3C, 0x68, 0x13, 0x1F, 0x58, 0xD2, 0x0C, 0x93, 0xA6, 0xB9, 0xEC, 0x95, 0x19, 0x6E, 0x4B, 0x20, - 0xFA, 0x42, 0x0F, 0xAE, 0x12, 0x08, 0x61, 0xFF, 0x9F, 0x99, 0xF3, 0x5B, 0x15, 0x89, 0x46, 0xA8, 0xD7, 0xC7, 0xFB, 0x17, 0x05, 0xDE, 0x9C, 0x54, - 0x34, 0xDB, 0x8B, 0x6C, 0xF0, 0x5C, 0x2B, 0x12, 0xD4, 0x0F, 0x7E, 0xEA, 0x2B, 0x17, 0xDE, 0x9D, 0x2B, 0xBF, 0xE7, 0xBA, 0x77, 0x57, 0xDF, 0x8B, - 0xD0, 0xEE, 0x35, 0xF1, 0xBB, 0xB4, 0xC5, 0x5D, 0x03, 0xF2, 0x0E, 0x4E, 0x72, 0x4F, 0xED, 0xBC, 0xBF, 0x8B, 0xBD, 0xED, 0xEA, 0xDD, 0xC0, 0x0E, - 0x77, 0xA7, 0x7B, 0xB0, 0xD7, 0xE6, 0xDD, 0x43, 0x3C, 0xF8, 0xE0, 0x2E, 0xD1, 0x7B, 0xEB, 0x89, 0xED, 0x5D, 0x62, 0xF7, 0x65, 0x56, 0x37, 0xD8, - 0xE7, 0x5B, 0xC5, 0xD8, 0xAD, 0xC7, 0xFC, 0x06, 0x6A, 0xB5, 0xFF, 0x49, 0xBF, 0xD1, 0x3C, 0xDE, 0x74, 0x93, 0x6A, 0x8D, 0x0C, 0x4E, 0x39, 0x47, - 0x07, 0xA7, 0xDD, 0x8E, 0xAE, 0x9C, 0xC3, 0xAE, 0x49, 0xAA, 0xD1, 0xC9, 0x87, 0xC5, 0xC6, 0xCD, 0xC0, 0x52, 0x3B, 0x53, 0xC0, 0x9A, 0x92, 0x40, - 0xAA, 0xC8, 0x02, 0x8E, 0xD9, 0x42, 0x4A, 0xDE, 0x51, 0xF2, 0xFF, 0x67, 0x7F, 0xB5, 0xBA, 0xEF, 0x9F, 0x66, 0xC2, 0xBE, 0x5D, 0x6F, 0xFD, 0xF0, - 0x9F, 0x03, 0xD8, 0x02, 0xAA, 0x25, 0x96, 0x7C, 0x41, 0x04, 0xAC, 0x18, 0x35, 0x84, 0x3F, 0xFA, 0x39, 0xF1, 0x73, 0xBD, 0xBA, 0x0C, 0x1B, 0xF6, - 0xB9, 0x02, 0xFD, 0xAB, 0x35, 0xA8, 0x31, 0xFC, 0xD0, 0xDE, 0x58, 0xD7, 0x9B, 0xA9, 0x8F, 0x43, 0x45, 0x80, 0x4E, 0x48, 0xB7, 0xF0, 0x69, 0xF5, - 0x76, 0x5A, 0x14, 0xC5, 0xD9, 0xCF, 0x29, 0x6B, 0x14, 0xFA, 0x2A, 0x36, 0xE7, 0xDA, 0xD6, 0x67, 0x2A, 0xAC, 0xA2, 0xD4, 0xF9, 0xB4, 0xF4, 0x03, - 0x95, 0xB4, 0x25, 0x78, 0x2E, 0x2A, 0x84, 0x0A, 0x26, 0xCC, 0x82, 0xED, 0xC5, 0x27, 0x5D, 0xC7, 0xBE, 0xD8, 0x24, 0x73, 0xB5, 0x38, 0x73, 0xE4, - 0x3D, 0xBF, 0xBC, 0x39, 0xD9, 0xAF, 0xA8, 0x12, 0xDA, 0x53, 0xBE, 0x7D, 0xED, 0xF5, 0xA2, 0x3D, 0xBC, 0x14, 0xB5, 0xC0, 0x0A, 0x26, 0xB1, 0xC7, - 0x59, 0xE6, 0x7C, 0x7C, 0x48, 0x7A, 0x87, 0x7B, 0xF5, 0xB7, 0x4F, 0x41, 0xD6, 0x66, 0x9E, 0xD9, 0x94, 0xDE, 0x1D, 0x06, 0xBA, 0x81, 0xCE, 0x6F, - 0xF1, 0x71, 0x7F, 0xEC, 0x87, 0xFD, 0xAF, 0xDF, 0xF5, 0xB3, 0x7E, 0xFC, 0xDF, 0xFC, 0x8E, 0xBF, 0xF0, 0x0B, 0x7E, 0x10, 0x1F, 0xEF, 0x4F, 0xF8, - 0x0B, 0xFE, 0x81, 0x5F, 0xF6, 0xBB, 0xFE, 0xD3, 0x77, 0xFA, 0xE4, 0x50, 0x1E, 0xC2, 0x47, 0xF0, 0xFD, 0x70, 0x99, 0xB2, 0x62, 0x01, 0xF8, 0x4F, - 0x6B, 0x52, 0x69, 0x2E, 0xEA, 0x41, 0x8F, 0xF7, 0x46, 0x28, 0xE4, 0x0F, 0xDC, 0x7F, 0x81, 0x32, 0x86, 0x1F, 0x62, 0x20, 0x23, 0x61, 0x3C, 0x25, - 0x5F, 0x58, 0x5D, 0x74, 0x59, 0x5B, 0x8E, 0x7F, 0x8B, 0x7A, 0x50, 0xC1, 0x02, 0xE8, 0x3D, 0x99, 0xF8, 0xE7, 0xD8, 0x05, 0x52, 0xB0, 0xC6, 0x59, - 0xBB, 0xFC, 0xCC, 0x2F, 0x07, 0xA2, 0x38, 0x5F, 0xA1, 0xE5, 0xC5, 0xCF, 0x45, 0xE3, 0xAA, 0x4A, 0x8B, 0x17, 0x90, 0x0C, 0x54, 0xAE, 0x69, 0xD4, - 0xA6, 0x69, 0x54, 0x2C, 0x17, 0x4E, 0xA1, 0x56, 0x02, 0x3E, 0x93, 0x9C, 0x3C, 0x5B, 0xA0, 0x7F, 0x1A, 0x1D, 0x2C, 0x35, 0xD4, 0x6A, 0xC0, 0x6F, - 0xF9, 0xDF, 0x3E, 0xDB, 0xFF, 0xA2, 0x5C, 0x99, 0xA6, 0x9C, 0x6F, 0x48, 0x66, 0xC3, 0x69, 0x6B, 0xFC, 0xBF, 0x26, 0x7C, 0x79, 0xBB, 0x72, 0x9A, - 0x8B, 0xE8, 0xD1, 0x9F, 0x2A, 0x7F, 0x84, 0x55, 0x7A, 0x1C, 0x4C, 0x1D, 0xAE, 0x83, 0xD2, 0x4D, 0x66, 0x44, 0xDB, 0x00, 0x54, 0x02, 0x50, 0x2B, - 0xD8, 0x26, 0x08, 0xCB, 0xD7, 0x60, 0xD7, 0x02, 0x55, 0xD5, 0x28, 0xC0, 0x18, 0x30, 0x39, 0x9C, 0x66, 0x2F, 0x11, 0x00, 0xFB, 0x81, 0x56, 0x99, - 0x7C, 0x16, 0x43, 0xC7, 0x4C, 0xD6, 0xC4, 0x0C, 0xC5, 0xAA, 0xBD, 0xFC, 0x2E, 0xF3, 0xD9, 0x21, 0x53, 0xF1, 0x1A, 0xE3, 0xBA, 0x12, 0x43, 0x42, - 0x10, 0x86, 0xE0, 0x0D, 0x2C, 0x80, 0x86, 0x78, 0xDF, 0x85, 0x2F, 0x05, 0x0A, 0xBC, 0x1A, 0x0D, 0xBB, 0x3A, 0xF4, 0x71, 0xA1, 0x76, 0x6E, 0xA9, - 0x40, 0xA1, 0xC5, 0x7B, 0x98, 0xA5, 0x4A, 0xEA, 0xD2, 0xB6, 0xD2, 0xA8, 0xE7, 0x29, 0xB5, 0xE1, 0x6A, 0xA0, 0x04, 0x5F, 0x9E, 0xF4, 0xA2, 0x96, - 0x9A, 0x12, 0xD7, 0xCC, 0xEA, 0x9B, 0x53, 0x0E, 0xEA, 0xB8, 0x6C, 0x81, 0xF5, 0x68, 0x0A, 0x47, 0x43, 0x76, 0x46, 0x30, 0x25, 0x8A, 0xC2, 0x40, - 0x10, 0x39, 0x65, 0xB6, 0x0B, 0x70, 0x04, 0xFF, 0xBF, 0xFE, 0x1F, 0x8D, 0xD8, 0xB0, 0xE3, 0xCC, 0xFE, 0xA1, 0xF2, 0x3D, 0xBF, 0x83, 0x40, 0xBF, - 0x65, 0x53, 0x47, 0xF8, 0x91, 0xBC, 0xFA, 0xE9, 0x69, 0xDD, 0xC1, 0xA6, 0xB9, 0xCF, 0x44, 0x7A, 0xAC, 0x14, 0xAC, 0x56, 0x6A, 0x01, 0x9B, 0x64, - 0xF9, 0x00, 0x49, 0xD9, 0xD2, 0xFE, 0x7A, 0xD9, 0x46, 0xEA, 0xB5, 0xEF, 0x12, 0x68, 0x9B, 0xAD, 0xED, 0x20, 0x98, 0xF5, 0x0A, 0x02, 0xDC, 0x44, - 0x9E, 0x75, 0x7B, 0x8E, 0xDD, 0xAE, 0xB4, 0x5E, 0x4A, 0x77, 0xCF, 0xD5, 0x5F, 0x30, 0xBB, 0x17, 0x85, 0xEB, 0x1F, 0xA0, 0xF6, 0xD1, 0x15, 0x6D, - 0xB0, 0x18, 0xF7, 0x48, 0xBB, 0x8E, 0x2F, 0x73, 0x6B, 0xC4, 0xDB, 0x21, 0x8E, 0xED, 0x63, 0x37, 0x6D, 0xDC, 0x3E, 0xB7, 0x60, 0xBE, 0xFD, 0xCC, - 0xE1, 0xBE, 0xDC, 0x6A, 0xB3, 0xB8, 0xDA, 0x5F, 0x0C, 0xBB, 0x93, 0xEC, 0xEE, 0x5C, 0xB8, 0xF6, 0x31, 0x8E, 0x3B, 0x7A, 0xA8, 0x4E, 0xC2, 0x94, - 0x7F, 0x73, 0xCB, 0x71, 0x2B, 0xFE, 0x95, 0x0A, 0xBE, 0x5A, 0xD5, 0xEB, 0x05, 0x6A, 0x0F, 0xFF, 0x91, 0x10, 0x51, 0xEE, 0xCC, 0x6C, 0xE6, 0xDC, - 0x71, 0xD6, 0x4C, 0xD2, 0xC4, 0xAC, 0xF3, 0x60, 0x39, 0x08, 0x4D, 0x19, 0x4A, 0x40, 0x8C, 0x2F, 0xBB, 0xE4, 0x35, 0x08, 0x30, 0xE6, 0x40, 0x20, - 0x53, 0x08, 0x71, 0xEC, 0x37, 0xB9, 0x2F, 0xA4, 0x34, 0x7E, 0x6C, 0x2D, 0xEE, 0x74, 0x25, 0x16, 0xA5, 0x1C, 0x8E, 0xAD, 0x7C, 0xE1, 0xEB, 0x2A, - 0xAC, 0x1C, 0xFF, 0xFF, 0x7F, 0x63, 0x2C, 0xBC, 0x43, 0xB7, 0x33, 0xF8, 0x57, 0xF4, 0x32, 0xFA, 0x1B, 0x5D, 0xF2, 0x07, 0xFD, 0x4F, 0x3A, 0x1D, - 0x71, 0x61, 0xC7, 0xFA, 0x2F, 0x50, 0xB3, 0x55, 0x18, 0xC6, 0xFA, 0x25, 0xB1, 0xCC, 0x78, 0x55, 0x13, 0x67, 0xA5, 0x11, 0x56, 0x35, 0x85, 0x81, - 0x0A, 0x08, 0xC5, 0x54, 0xDB, 0xA1, 0x54, 0x55, 0x87, 0xA8, 0x0B, 0x9E, 0xF0, 0xB7, 0x46, 0x28, 0x12, 0x27, 0x9E, 0x30, 0x98, 0x3A, 0x61, 0xB2, - 0x68, 0x3A, 0x01, 0xCC, 0x54, 0xAB, 0xBB, 0xA4, 0x79, 0xAA, 0xE4, 0x3C, 0x9D, 0x74, 0x1B, 0x9C, 0xE4, 0x02, 0x73, 0x14, 0xCD, 0x71, 0x34, 0xF4, - 0x8D, 0x60, 0x6E, 0x8A, 0xC2, 0x62, 0x88, 0x9C, 0xA6, 0x86, 0xE8, 0x9A, 0x84, 0x06, 0xB0, 0xBD, 0x01, 0xD1, 0x82, 0xEA, 0xAA, 0x80, 0x22, 0xED, - 0x7E, 0x36, 0xE8, 0xF8, 0xB4, 0x82, 0xE8, 0x11, 0xC1, 0xCA, 0x13, 0x1C, 0x61, 0xE7, 0xD4, 0x6D, 0xC7, 0x43, 0xC4, 0xAA, 0x6B, 0xCE, 0x4E, 0x25, - 0xCE, 0x1A, 0xEE, 0x26, 0xEB, 0xD5, 0x86, 0x26, 0xAF, 0x46, 0x0E, 0x99, 0x46, 0xCA, 0xC4, 0x0C, 0x1D, 0xD6, 0x7D, 0x86, 0x41, 0x26, 0xC4, 0x65, - 0x0C, 0x84, 0x43, 0x18, 0xCF, 0x92, 0x97, 0x3D, 0x2E, 0x17, 0xDD, 0x20, 0x45, 0x03, 0x2D, 0xEA, 0xAE, 0xD1, 0x0C, 0x2B, 0x33, 0x07, 0x7E, 0x13, - 0x53, 0x92, 0xBE, 0xA4, 0x35, 0x00, 0x30, 0x4B, 0x70, 0x12, 0xB3, 0x68, 0x62, 0xB7, 0xA1, 0xE4, 0x49, 0xB4, 0x2D, 0x82, 0x91, 0x44, 0xAD, 0x77, - 0xC4, 0x72, 0x54, 0x84, 0xC2, 0x3A, 0x64, 0xD6, 0x05, 0x48, 0xA1, 0xFB, 0xC3, 0x98, 0xB4, 0x92, 0xED, 0x41, 0xE2, 0x1B, 0x3B, 0xC3, 0xF1, 0xCE, - 0x50, 0x86, 0xD2, 0x88, 0xD3, 0x64, 0x88, 0xD4, 0xB0, 0xE7, 0xF0, 0xD5, 0xBD, 0xB7, 0xAA, 0x3D, 0x42, 0xAA, 0xA2, 0x17, 0xEA, 0x8A, 0x01, 0xA3, - 0x08, 0x4E, 0xC7, 0x30, 0x35, 0x8F, 0x3A, 0x06, 0xBD, 0x4A, 0x33, 0x48, 0x30, 0x61, 0xCF, 0x19, 0xA4, 0x6C, 0x00, 0xF0, 0x39, 0x68, 0xC9, 0x4A, - 0x41, 0x2F, 0x08, 0x6D, 0x99, 0xFE, 0x69, 0x1D, 0x7E, 0x65, 0x09, 0x3F, 0x62, 0xB1, 0x48, 0x80, 0xF5, 0x91, 0x6A, 0x55, 0x0B, 0x66, 0x35, 0x03, - 0x49, 0xE5, 0x3C, 0x47, 0x15, 0xB5, 0x3A, 0xD5, 0xC2, 0x41, 0x8A, 0x8D, 0x38, 0x6A, 0x29, 0x05, 0xDC, 0x02, 0x08, 0x5A, 0xA9, 0xDC, 0xC4, 0x8E, - 0xD3, 0x82, 0xC6, 0xA6, 0xCF, 0xAB, 0x3F, 0x53, 0xFD, 0x12, 0x2D, 0x4F, 0x5E, 0x29, 0x93, 0x23, 0xA9, 0xD9, 0x07, 0xC9, 0xF6, 0x06, 0x89, 0x45, - 0xCB, 0xE8, 0x79, 0xCA, 0x48, 0xA9, 0x40, 0x91, 0x0B, 0x1B, 0xD1, 0x99, 0xDE, 0x9D, 0x89, 0x02, 0x1C, 0x02, 0xE2, 0x3D, 0x8C, 0x00, 0xBE, 0x9E, - 0xE1, 0x4D, 0x47, 0xC7, 0xDF, 0x13, 0xDF, 0x80, 0x84, 0xF8, 0x3C, 0xE8, 0x3B, 0x52, 0x13, 0xBF, 0xDF, 0x7D, 0xB7, 0xD6, 0xE2, 0x4B, 0xFE, 0x3B, - 0x98, 0x13, 0x2E, 0x14, 0xF3, 0xF6, 0xB8, 0x85, 0x63, 0xA9, 0xEF, 0x3B, 0xCE, 0xF0, 0x02, 0x32, 0x77, 0xAD, 0x15, 0x78, 0x06, 0xA7, 0xBB, 0x81, - 0x23, 0xF8, 0xA2, 0x80, 0xBB, 0x06, 0xAB, 0x37, 0x6B, 0xA2, 0x1B, 0x85, 0xDB, 0xFB, 0x18, 0xD4, 0xED, 0xAA, 0x79, 0x67, 0x29, 0x91, 0x4E, 0x3D, - 0xAA, 0x80, 0xBA, 0x62, 0xFA, 0x94, 0x33, 0xD4, 0x74, 0x16, 0xA6, 0x34, 0x97, 0x97, 0x94, 0x99, 0x69, 0x74, 0x5A, 0x3D, 0xBA, 0x72, 0x06, 0x17, - 0x45, 0x71, 0x11, 0x1A, 0xD4, 0x04, 0x7A, 0xC4, 0x66, 0xF1, 0x85, 0xE6, 0x81, 0x6A, 0xAD, 0xA9, 0x0A, 0x57, 0x4E, 0x2F, 0x1A, 0xFF, 0xE2, 0xDE, - 0xEB, 0x58, 0xF8, 0x7F, 0xDF, 0xFE, 0x1F, 0x3F, 0x8B, 0x0B, 0x2C, 0xAE, 0x80, 0x2B, 0xBC, 0x86, 0x30, 0xEE, 0x21, 0xCE, 0xBF, 0x99, 0xB7, 0x0A, - 0xC6, 0x18, 0x33, 0xBB, 0x6D, 0xF9, 0x84, 0x4E, 0xB9, 0xA6, 0x42, 0x0C, 0xA2, 0x7A, 0xF2, 0x54, 0x21, 0x0C, 0x31, 0x15, 0xC8, 0x6D, 0x9B, 0xC7, - 0x06, 0x00, 0xF7, 0x11, 0x0C, 0x8D, 0x35, 0xDC, 0x8B, 0x49, 0x74, 0x12, 0xA3, 0x50, 0x1D, 0xC6, 0xC4, 0x7D, 0x61, 0x3A, 0x2C, 0x5C, 0xFB, 0x1C, - 0xFC, 0xE2, 0xE3, 0x99, 0x05, 0x18, 0x44, 0xE7, 0xD7, 0x05, 0xC9, 0x97, 0x06, 0xC4, 0xC5, 0x50, 0x4B, 0xAF, 0xD4, 0x0E, 0x6F, 0x58, 0xD2, 0x2E, - 0x81, 0x2B, 0x5D, 0xBA, 0xB2, 0xBB, 0x85, 0xB7, 0x3B, 0x60, 0xDD, 0xDD, 0xC4, 0x85, 0x7B, 0x24, 0xE0, 0xDD, 0xB6, 0xD4, 0x41, 0x2D, 0x2D, 0x06, - 0xDA, 0xE0, 0xC0, 0xE6, 0xC3, 0x59, 0x8F, 0x48, 0x59, 0x16, 0xCF, 0xD8, 0x9D, 0x3C, 0xAC, 0x52, 0x8B, 0xF7, 0x6B, 0x16, 0xA8, 0xEB, 0x99, 0x6B, - 0x2B, 0x86, 0xBF, 0xD6, 0xA1, 0x3A, 0xD6, 0xBE, 0x00, 0xAC, 0x64, 0x1C, 0xF9, 0x13, 0x8F, 0xF9, 0x30, 0xC0, 0xC6, 0x06, 0xAB, 0x0D, 0x6E, 0x54, - 0x43, 0x53, 0x55, 0xE9, 0xEE, 0x7B, 0x16, 0x67, 0xF3, 0xD4, 0x0F, 0x53, 0xB7, 0xA8, 0xF4, 0xB3, 0x61, 0x48, 0x4A, 0x36, 0xD3, 0xDD, 0x05, 0xFF, - 0x18, 0x88, 0xAD, 0xA7, 0x63, 0x8A, 0x2E, 0x9D, 0x08, 0x4D, 0x82, 0x02, 0x85, 0x32, 0x1B, 0xF0, 0x59, 0x00, 0x81, 0xA7, 0x9D, 0x04, 0x3C, 0x89, - 0xE9, 0x4E, 0x11, 0x23, 0x38, 0x0B, 0x13, 0x6C, 0x5A, 0xAE, 0xD0, 0x24, 0x15, 0xFD, 0x97, 0xC6, 0x96, 0xD3, 0xA3, 0x96, 0x24, 0xB1, 0x2B, 0x86, - 0xD0, 0x26, 0xE3, 0x26, 0x92, 0x74, 0x20, 0xE8, 0x03, 0xE8, 0x95, 0xD1, 0x27, 0xBE, 0x20, 0x11, 0x6B, 0x76, 0x0E, 0x15, 0x4C, 0x30, 0x11, 0x15, - 0xC3, 0x70, 0x03, 0xB3, 0x7D, 0x6D, 0x5E, 0x88, 0x22, 0xF4, 0x13, 0xE2, 0x39, 0xB5, 0x4B, 0x94, 0xFE, 0x0D, 0x96, 0xF0, 0x90, 0x86, 0xAD, 0x0B, - 0x6E, 0x5D, 0x50, 0x9F, 0xA9, 0xCD, 0x9A, 0x17, 0x37, 0x24, 0x40, 0x34, 0x5B, 0x07, 0x27, 0xAB, 0x2C, 0xC3, 0x5B, 0xEB, 0xEE, 0x3E, 0x0C, 0xBA, - 0x8F, 0xEB, 0x47, 0xFF, 0x35, 0x6A, 0xD9, 0x9D, 0x98, 0x61, 0xF3, 0x97, 0x7E, 0x91, 0xD6, 0xBE, 0x08, 0x3C, 0x5A, 0x24, 0x48, 0xCC, 0xE5, 0xBD, - 0xC2, 0xCD, 0xD3, 0x80, 0xCB, 0x7E, 0xB3, 0x45, 0x5C, 0xB4, 0xAD, 0xB9, 0x40, 0x0B, 0x2A, 0x4A, 0xBB, 0x04, 0x27, 0xDB, 0xDB, 0x2B, 0x8B, 0x96, - 0x17, 0x3B, 0xD2, 0x04, 0xBB, 0xA0, 0x40, 0xAB, 0x9F, 0x09, 0x2B, 0x99, 0x80, 0xFC, 0xE3, 0x3B, 0x0E, 0x41, 0xAE, 0x2A, 0x7A, 0x92, 0x4A, 0xC8, - 0x8A, 0xEA, 0xAA, 0x79, 0x4A, 0x20, 0x51, 0x3F, 0xC9, 0x02, 0xC6, 0x37, 0xCF, 0xF2, 0xC8, 0x32, 0x81, 0xE8, 0x8A, 0xAD, 0xA3, 0x41, 0x54, 0x15, - 0x27, 0x6B, 0xFF, 0x77, 0x70, 0xB8, 0xF8, 0xDD, 0xFB, 0xC8, 0x7C, 0xF6, 0x4C, 0x32, 0x3B, 0x8D, 0xB8, 0x9B, 0x79, 0xC5, 0x60, 0x10, 0x60, 0x66, - 0x2F, 0x58, 0x3E, 0xA1, 0x2B, 0xD7, 0xF8, 0xC5, 0x20, 0x22, 0x4C, 0x9E, 0x72, 0x84, 0x21, 0xA6, 0x89, 0xDC, 0x76, 0x7A, 0x6C, 0xC0, 0xBE, 0x8F, - 0xF0, 0x68, 0xAC, 0x81, 0xB3, 0x98, 0x44, 0x2A, 0x31, 0x8A, 0xDB, 0x61, 0xCC, 0x17, 0x2F, 0x4C, 0x9D, 0x85, 0x6D, 0x3D, 0x82, 0x5F, 0xAC, 0x3C, - 0xE7, 0x00, 0x83, 0x1C, 0xFD, 0xBA, 0x54, 0xF9, 0xD2, 0xB0, 0xB8, 0x18, 0xD2, 0xE9, 0x95, 0x42, 0xE2, 0x0D, 0xB3, 0xDA, 0x25, 0x40, 0xA6, 0x4B, - 0xE7, 0x86, 0xB7, 0xC0, 0x00, 0x0F, 0x68, 0x01, 0x6F, 0xE2, 0x0F, 0x1F, 0x69, 0xC7, 0xDB, 0x96, 0xE6, 0xD5, 0xD2, 0x7A, 0xA0, 0x0D, 0x36, 0xD1, - 0x0C, 0x39, 0xEB, 0x81, 0x97, 0x65, 0x59, 0x8D, 0xDD, 0xC9, 0x85, 0x55, 0x22, 0x86, 0xBF, 0x66, 0xB5, 0xBA, 0x9E, 0xE9, 0xB6, 0x62, 0x28, 0x6C, - 0x1D, 0xBA, 0xC0, 0xDA, 0x07, 0xC7, 0x4A, 0x86, 0x92, 0x3F, 0xB1, 0x99, 0x0F, 0x33, 0x6C, 0xCC, 0x5B, 0x6D, 0x40, 0xA4, 0x1A, 0x6A, 0xAB, 0x4A, - 0xD7, 0xF9, 0x59, 0xC4, 0xD1, 0x53, 0x3F, 0x80, 0xDD, 0x76, 0xA2, 0xD2, 0x0B, 0x0C, 0xE3, 0xFF, 0x29, 0xE5, 0x9C, 0xE9, 0x2E, 0x85, 0xD2, 0xE3, - 0xA6, 0x52, 0x76, 0x31, 0xED, 0xF3, 0xA8, 0x19, 0xA9, 0xE8, 0x92, 0x64, 0x68, 0x52, 0x17, 0x28, 0xD4, 0x0C, 0x0B, 0xB3, 0x03, 0x9F, 0x05, 0x22, - 0x78, 0xDA, 0xF1, 0xE1, 0x05, 0x45, 0x3A, 0x45, 0x28, 0x72, 0x16, 0x4A, 0xD8, 0xB4, 0x0C, 0xD5, 0x24, 0x7D, 0xBD, 0x34, 0x16, 0xB5, 0x9C, 0x86, - 0xB5, 0x24, 0xE9, 0x5D, 0x31, 0x54, 0x60, 0x32, 0xEE, 0x96, 0xA4, 0x43, 0x8C, 0x9C, 0x3F, 0x41, 0xF2, 0x65, 0xB8, 0xEF, 0xD1, 0x3C, 0xDA, 0x0F, - 0xA8, 0x50, 0x84, 0x3B, 0x96, 0x82, 0xA2, 0xF8, 0xBD, 0x43, 0xB1, 0x01, 0x72, 0xE2, 0xAB, 0x13, 0xB1, 0x06, 0xE9, 0x50, 0xC1, 0x06, 0x13, 0x52, - 0x6C, 0xC3, 0xBE, 0x0D, 0xFB, 0x1A, 0x5E, 0x21, 0x0A, 0xD7, 0x36, 0x21, 0xDE, 0x96, 0xBB, 0x44, 0x69, 0xE1, 0x60, 0x09, 0x15, 0x69, 0x5E, 0xC1, - 0x2D, 0x3E, 0x40, 0x5F, 0x14, 0x5F, 0xCF, 0xAD, 0x16, 0xB3, 0xF4, 0x6A, 0x7C, 0xD0, 0xD5, 0x81, 0x8D, 0x0B, 0xF5, 0xC3, 0x96, 0x0A, 0x0A, 0x5A, - 0xBC, 0x76, 0xAA, 0x25, 0xB2, 0xD8, 0x20, 0x8A, 0x33, 0xAF, 0x6A, 0x26, 0xAD, 0x34, 0x0A, 0xAB, 0xA6, 0x5A, 0xA9, 0x6E, 0x36, 0xA7, 0xDA, 0xF6, - 0x52, 0x55, 0xB6, 0xF2, 0xB7, 0xDB, 0x5F, 0x50, 0x9D, 0x1D, 0x28, 0xEF, 0xF1, 0x27, 0x90, 0xD3, 0x07, 0x31, 0xB5, 0x67, 0xF2, 0x76, 0x4E, 0xE2, - 0xE9, 0x67, 0x6C, 0xAC, 0x8B, 0x3C, 0x7F, 0x12, 0x2D, 0x97, 0x1A, 0xDE, 0xD5, 0x41, 0x8C, 0x0B, 0x75, 0x6F, 0x4B, 0xC5, 0x02, 0x2D, 0x5E, 0x03, - 0xAA, 0x7E, 0x59, 0xEC, 0x0C, 0xC5, 0x8E, 0x57, 0x35, 0x89, 0x56, 0x1A, 0x7F, 0x55, 0x53, 0xA0, 0x54, 0x37, 0x8F, 0x53, 0x6D, 0xBB, 0x52, 0x55, - 0xA9, 0x71, 0x43, 0x13, 0xC8, 0x69, 0xC1, 0x35, 0x8F, 0x1C, 0xA9, 0x0F, 0x35, 0x98, 0x1A, 0x09, 0x2D, 0x88, 0x95, 0x44, 0xDC, 0x29, 0x32, 0xF4, - 0x0A, 0x32, 0x03, 0x30, 0x36, 0x04, 0x13, 0xC2, 0x6A, 0x51, 0x12, 0x0E, 0x8D, 0x06, 0x50, 0xB6, 0x85, 0xE4, 0xFA, 0x7C, 0xA3, 0xDC, 0x31, 0x6E, - 0xA7, 0x87, 0x90, 0xAF, 0x92, 0x57, 0xEE, 0x15, 0xCF, 0xAE, 0x06, 0xB0, 0x35, 0x38, 0x34, 0x36, 0xD6, 0x7A, 0x56, 0x5E, 0x09, 0x92, 0xE1, 0xB8, - 0x98, 0x52, 0x5C, 0x6A, 0x98, 0x97, 0xBB, 0x34, 0xA6, 0xDB, 0x96, 0x5A, 0x4B, 0x9D, 0x51, 0x14, 0xC0, 0xC5, 0x54, 0x59, 0x8C, 0xBB, 0x32, 0x0D, - 0x36, 0x32, 0x14, 0x12, 0x55, 0x30, 0x60, 0x6D, 0x2A, 0x1B, 0xA2, 0xA9, 0xA2, 0x22, 0x29, 0x2A, 0xB0, 0xA8, 0xB1, 0x30, 0xB8, 0xDB, 0x1C, 0x54, - 0xDD, 0xFC, 0x29, 0x8F, 0x83, 0x53, 0x60, 0x87, 0x3A, 0x62, 0x0A, 0xFB, 0x64, 0x63, 0x3D, 0x58, 0xD2, 0x2E, 0xA2, 0x8E, 0xA1, 0xFF, 0xC8, 0x34, - 0xD1, 0xBB, 0x9C, 0xD0, 0xE8, 0x4F, 0x3F, 0xED, 0x47, 0xF8, 0x22, 0x7E, 0xE5, 0x08, 0xFA, 0x0A, 0x3A, 0x2C, 0x0C, 0x7A, 0x09, 0x3D, 0x88, 0xFE, - 0x9E, 0x93, 0x80, 0xDC, 0xBA, 0x16, 0x73, 0x0D, 0x7D, 0x29, 0x66, 0x31, 0xE3, 0x55, 0x83, 0xEF, 0x2E, 0xBC, 0x68, 0x53, 0xD6, 0x00, 0xD4, 0x57, - 0xEC, 0x69, 0x18, 0x5B, 0x8E, 0xCF, 0xB4, 0x27, 0x3E, 0xDB, 0xCB, 0x3E, 0xAD, 0xDF, 0x3E, 0x11, 0xE7, 0x1E, 0x56, 0xEA, 0x2A, 0xB3, 0x3F, 0x16, - 0x32, 0xC3, 0xD6, 0xB5, 0x08, 0xEB, 0x8B, 0x52, 0x67, 0xA2, 0x9E, 0x12, 0xFA, 0xFF, 0xDD, 0xD6, 0x03, 0x47, 0x20, 0x49, 0xCF, 0xA6, 0x35, 0x24, - 0x0F, 0xD0, 0xB8, 0x16, 0xE7, 0xA8, 0xD6, 0x20, 0x67, 0x80, 0x5E, 0x84, 0x34, 0x0F, 0xC9, 0x02, 0x4E, 0x58, 0xA1, 0x45, 0xE2, 0xCF, 0x40, 0x4F, - 0xCC, 0x58, 0x39, 0x04, 0xD0, 0xED, 0x50, 0x66, 0xDC, 0x51, 0xF6, 0xD1, 0xC8, 0x44, 0x5E, 0xC7, 0x64, 0x72, 0xC4, 0x7C, 0x0C, 0xC3, 0x9C, 0x60, - 0x80, 0x6D, 0x25, 0x88, 0x0B, 0x50, 0xF0, 0x55, 0x00, 0xD8, 0x2B, 0x02, 0x53, 0xD7, 0x1E, 0x25, 0xD7, 0x08, 0x8E, 0xAD, 0xC4, 0x0A, 0x5A, 0x2B, - 0x5E, 0xB3, 0x42, 0xBD, 0xB0, 0x60, 0xBF, 0xAE, 0x76, 0x4B, 0xA7, 0x84, 0x26, 0xA5, 0xA0, 0x80, 0xA2, 0xBE, 0x82, 0xE0, 0x69, 0x8C, 0x50, 0x09, - 0xA0, 0x9F, 0x24, 0x38, 0x70, 0xEA, 0x23, 0x6D, 0x22, 0x21, 0xC5, 0x94, 0xE2, 0xC3, 0xD2, 0x25, 0x89, 0x52, 0x69, 0x3B, 0x29, 0x56, 0x03, 0xD2, - 0xEF, 0xED, 0x51, 0x8C, 0xB2, 0xA3, 0x0D, 0x46, 0x23, 0x00, 0x35, 0x45, 0x37, 0x0D, 0x88, 0x86, 0x8C, 0x87, 0xA4, 0xF6, 0x42, 0x71, 0x03, 0x6F, - 0xB6, 0xCD, 0xA0, 0x39, 0x24, 0x20, 0x3A, 0xDF, 0x4F, 0x07, 0xB6, 0x4F, 0x61, 0x59, 0x52, 0x4F, 0x63, 0x9E, 0x87, 0x93, 0x66, 0xE7, 0x74, 0x50, - 0xC7, 0xB6, 0x84, 0x8E, 0xDB, 0x01, 0x80, 0x1F, 0x94, 0x13, 0xA4, 0x61, 0x52, 0x93, 0x2A, 0xAD, 0x12, 0x47, 0x4D, 0x2C, 0xC6, 0x68, 0xBC, 0x8E, - 0x29, 0x1E, 0x02, 0x03, 0xB8, 0xE3, 0x89, 0x65, 0x20, 0x55, 0x52, 0xE7, 0x47, 0xC5, 0xB2, 0xDA, 0x99, 0xAE, 0x0A, 0x7E, 0xD9, 0xC3, 0x71, 0x47, - 0xCB, 0xCE, 0xDE, 0xB9, 0x71, 0x12, 0xEA, 0xA1, 0xE5, 0x0B, 0x1E, 0x67, 0x6A, 0xF8, 0x02, 0x60, 0x93, 0x31, 0xDC, 0x12, 0x5B, 0xC3, 0x0F, 0xD5, - 0xCD, 0xBD, 0x07, 0x2E, 0x1F, 0xBD, 0x83, 0x58, 0x03, 0x07, 0x74, 0xD9, 0x87, 0xC0, 0x3C, 0xA7, 0x0D, 0xA2, 0x8B, 0x00, 0xDD, 0x2F, 0x43, 0xFF, - 0x4A, 0xA8, 0x7D, 0x28, 0x08, 0x1D, 0x36, 0x64, 0xCE, 0x3B, 0xFB, 0x61, 0xCC, 0x02, 0xAF, 0x11, 0xE9, 0x26, 0x27, 0xE6, 0x33, 0x0B, 0xFE, 0xDB, - 0xFD, 0x34, 0xFC, 0x1F, 0x9C, 0xCE, 0x07, 0x58, 0xD2, 0xEF, 0x30, 0xB4, 0xA0, 0x74, 0xCB, 0xA5, 0x63, 0x80, 0x8B, 0xE2, 0xDF, 0xEA, 0xCF, 0x6C, - 0x49, 0x53, 0xE7, 0xB4, 0xBF, 0x23, 0xC6, 0xCE, 0x58, 0x90, 0x65, 0xD5, 0xA9, 0xEE, 0x57, 0x76, 0xEA, 0x2F, 0x44, 0xBD, 0x1A, 0x19, 0x1B, 0x69, - 0xEA, 0x27, 0x75, 0xCD, 0x80, 0xBF, 0xE2, 0xBF, 0xD6, 0x61, 0x2A, 0xE9, 0x02, 0xFF, 0xD1, 0x2B, 0xC7, 0x8C, 0xF3, 0xAA, 0xEB, 0x5D, 0xC0, 0x70, - 0xC8, 0x91, 0xA3, 0xB6, 0xA0, 0x76, 0x52, 0xB7, 0xD5, 0x1B, 0x6A, 0x8D, 0x05, 0xB2, 0xAD, 0x70, 0x1D, 0x8E, 0x2B, 0x37, 0xB2, 0x5A, 0xCE, 0x9B, - 0xDB, 0xE4, 0xAE, 0x7D, 0x75, 0xC1, 0xBD, 0xB3, 0x16, 0x67, 0xE8, 0xCE, 0x9A, 0x80, 0x97, 0x67, 0x97, 0x13, 0xC4, 0x0C, 0x73, 0x47, 0xDD, 0xC1, - 0x20, 0xD3, 0x37, 0xE0, 0xE6, 0x4D, 0x9D, 0x9F, 0xAB, 0xB3, 0x8A, 0x66, 0x83, 0x04, 0xFA, 0xE9, 0x1D, 0xFC, 0x54, 0x43, 0xC3, 0xDE, 0x75, 0xF3, - 0x97, 0x7A, 0x34, 0xCF, 0x49, 0xCF, 0xD4, 0x0C, 0x21, 0x64, 0xEC, 0xED, 0x21, 0x4C, 0xD0, 0xF1, 0x8E, 0xA6, 0x76, 0xD6, 0x68, 0xEA, 0xA6, 0x07, - 0x1D, 0x5E, 0x59, 0x17, 0xA4, 0x14, 0xC4, 0x1D, 0xCA, 0xEB, 0x7B, 0xA3, 0x05, 0xC5, 0x7B, 0x24, 0x88, 0x57, 0x53, 0x86, 0x15, 0x2D, 0x5F, 0x18, - 0x17, 0x09, 0x8F, 0xC6, 0x0A, 0x93, 0x84, 0x01, 0xFF, 0x26, 0x03, 0x07, 0xCD, 0x7F, 0x4B, 0xB8, 0x80, 0x7D, 0x09, 0xB0, 0x5D, 0x9C, 0xAC, 0x14, - 0xE8, 0x3D, 0xB0, 0xA2, 0xEF, 0x58, 0xEF, 0xBB, 0xBB, 0x77, 0x24, 0xD1, 0xCB, 0x6A, 0xD8, 0xD3, 0xA6, 0xFE, 0xE8, 0x65, 0x8B, 0x4B, 0xC8, 0x65, - 0xDF, 0xFC, 0xF6, 0x76, 0x2F, 0xEC, 0xF5, 0x53, 0x29, 0x06, 0xA4, 0x0C, 0xBC, 0xFA, 0xC7, 0x40, 0x5A, 0xA6, 0x1C, 0x89, 0xB9, 0x27, 0xD4, 0xAC, - 0xFD, 0xCE, 0xCC, 0x3F, 0x45, 0xA8, 0x59, 0x85, 0xDE, 0xFA, 0x8E, 0xDC, 0xEB, 0xED, 0x53, 0x2D, 0xD5, 0x49, 0xEA, 0x9B, 0x7F, 0xFC, 0x89, 0x7B, - 0x50, 0x61, 0x5D, 0x8E, 0x26, 0x35, 0xD8, 0x4E, 0xF2, 0x34, 0x97, 0x59, 0xBB, 0x9E, 0x9D, 0x09, 0x31, 0x43, 0x5A, 0x51, 0xD1, 0x7F, 0x8E, 0x67, - 0x68, 0x00, 0xC4, 0x97, 0x78, 0x3F, 0xC8, 0xF0, 0x1B, 0xF4, 0xA2, 0x0D, 0x37, 0x20, 0x53, 0x5D, 0xCF, 0x01, 0x7D, 0x53, 0x98, 0x37, 0xC8, 0x1E, - 0x0F, 0x66, 0x10, 0x98, 0x7F, 0x38, 0x7A, 0x6D, 0x1D, 0x1D, 0x02, 0x62, 0x07, 0x66, 0x4A, 0x8B, 0xCF, 0x91, 0xB1, 0xA1, 0x61, 0x63, 0x28, 0xEF, - 0x94, 0xE3, 0xCA, 0x6F, 0x89, 0xA8, 0x4C, 0x0D, 0x25, 0xE1, 0x95, 0x5D, 0x9A, 0x01, 0x3C, 0xA0, 0xE2, 0xA5, 0xBD, 0x6C, 0x59, 0x6C, 0xC0, 0xF0, - 0x28, 0x56, 0xDC, 0x78, 0x5C, 0xB5, 0x0F, 0x96, 0x07, 0x51, 0xC6, 0x82, 0x02, 0xD3, 0xD3, 0x32, 0xC0, 0x90, 0x8B, 0xF3, 0xB2, 0xCE, 0x3F, 0xA2, - 0x27, 0x22, 0x08, 0xFD, 0x25, 0x05, 0x20, 0x50, 0xFA, 0xAD, 0x90, 0x2E, 0x58, 0xB7, 0xBA, 0x4E, 0xFB, 0xD8, 0x7B, 0x28, 0xC1, 0xFF, 0xA9, 0x5E, - 0xF1, 0x1C, 0x4C, 0x96, 0x90, 0x19, 0x01, 0x0E, 0x40, 0xF7, 0x76, 0x65, 0x44, 0x9D, 0xA5, 0xDE, 0x99, 0xDB, 0x67, 0x80, 0x49, 0x81, 0xAD, 0xB3, - 0x39, 0x04, 0x70, 0x19, 0xFC, 0x8D, 0x7A, 0x98, 0x79, 0xCA, 0x9C, 0x69, 0x5E, 0x34, 0x3F, 0x9B, 0x87, 0xCD, 0x9B, 0xF6, 0xF3, 0x39, 0xDA, 0xBE, - 0xC3, 0xDC, 0x6E, 0xDF, 0x38, 0x07, 0xB1, 0xF7, 0xDB, 0x4F, 0xEE, 0x1D, 0xF7, 0xAD, 0xFB, 0xD3, 0x7D, 0xE5, 0xDC, 0x39, 0x78, 0xFF, 0x3A, 0xE7, - 0xD8, 0x43, 0x23, 0xCF, 0x85, 0x5E, 0xCC, 0xDB, 0x73, 0x8F, 0x39, 0xD0, 0x3C, 0x67, 0xCE, 0x35, 0x77, 0xEF, 0x83, 0xF6, 0x4F, 0xFB, 0xAE, 0x7D, - 0xDA, 0xFE, 0xC0, 0x3E, 0xC2, 0x9E, 0x6F, 0x6F, 0xF7, 0xF5, 0x15, 0xF7, 0x19, 0x95, 0xFB, 0x0D, 0x73, 0x17, 0x12, 0x73, 0xC6, 0xB9, 0xE5, 0x7C, - 0x74, 0x1E, 0x3B, 0x3F, 0xE0, 0x62, 0x2E, 0x21, 0x79, 0xCE, 0x9E, 0x9B, 0xCC, 0x67, 0xE6, 0xEF, 0x39, 0x6A, 0x4D, 0xBC, 0x15, 0x4F, 0x18, 0x2F, - 0x84, 0x77, 0x18, 0xC8, 0xAF, 0x1C, 0xC0, 0xE7, 0x20, 0xC5, 0x17, 0xF5, 0x28, 0x58, 0x8E, 0xD4, 0x39, 0x6A, 0x1C, 0x3A, 0xA3, 0x15, 0xB1, 0x95, - 0x11, 0x62, 0x17, 0x6B, 0x20, 0x7A, 0x83, 0x40, 0x78, 0x1D, 0x40, 0xD8, 0xFD, 0xDA, 0xD1, 0x05, 0x11, 0x42, 0x5F, 0x41, 0xB3, 0x45, 0x25, 0x65, - 0xAE, 0x61, 0x2D, 0xAE, 0xAA, 0xF0, 0xAD, 0x25, 0xA7, 0x2D, 0xF1, 0x8A, 0xD9, 0xDB, 0x5A, 0x11, 0xD8, 0x59, 0x66, 0xE7, 0xD9, 0x20, 0x2A, 0x59, - 0x28, 0xB1, 0xD8, 0x3A, 0x2E, 0xD8, 0xAD, 0x14, 0xAF, 0x52, 0x8E, 0xAE, 0x76, 0xCC, 0xAD, 0x6C, 0x11, 0xE6, 0xAA, 0x25, 0x4E, 0xE4, 0x53, 0x5D, - 0xA5, 0xE5, 0xA8, 0xCF, 0x98, 0xA7, 0xC8, 0xA6, 0xFA, 0xD3, 0xAB, 0x06, 0x18, 0xE4, 0x04, 0x74, 0xB8, 0xFA, 0x5F, 0xAD, 0xB6, 0x51, 0xFF, 0x8C, - 0xFB, 0x1C, 0x2B, 0xC1, 0xFE, 0xCF, 0xEC, 0xB4, 0x09, 0x86, 0x29, 0xE8, 0x74, 0xA9, 0x80, 0x4E, 0x52, 0x5E, 0xD4, 0x51, 0x08, 0x4B, 0xA3, 0xEA, - 0xAB, 0x50, 0x11, 0x1C, 0xA1, 0xEE, 0x13, 0x7A, 0xA5, 0x6B, 0x9E, 0xC5, 0x93, 0xFE, 0xC3, 0xDA, 0x3D, 0x07, 0x34, 0xD2, 0x61, 0xB8, 0xC4, 0x3B, - 0x25, 0xE4, 0xD0, 0xE3, 0xAE, 0x32, 0x38, 0x6A, 0xD8, 0x30, 0x99, 0xD3, 0x03, 0x8C, 0xB8, 0x70, 0x23, 0xDD, 0x51, 0x0F, 0xBC, 0xC9, 0xEE, 0x5E, - 0x77, 0x06, 0x77, 0x36, 0x82, 0xFB, 0xDF, 0xBD, 0x46, 0xC3, 0x5D, 0xA3, 0xC1, 0x7B, 0x6A, 0xF1, 0xEE, 0xE1, 0x2D, 0x7A, 0xAF, 0x82, 0x7C, 0xF7, - 0xDE, 0x57, 0xDF, 0xA4, 0x6F, 0xE2, 0xB7, 0xEF, 0x3B, 0x11, 0xE4, 0x6D, 0x9B, 0xF1, 0xD6, 0x2C, 0xBE, 0x3F, 0xD3, 0xBC, 0x71, 0x43, 0xEE, 0x2E, - 0xF0, 0x77, 0x34, 0xCF, 0x7D, 0xA8, 0xE6, 0x0E, 0x83, 0x75, 0x0F, 0x8B, 0xBA, 0x3B, 0xC1, 0xDD, 0xD7, 0xEE, 0x79, 0x77, 0xB7, 0xB3, 0x77, 0xB3, - 0x08, 0x72, 0xC6, 0x5A, 0x43, 0x3D, 0xA3, 0x5E, 0xFF, 0xB0, 0xFC, 0xDF, 0xB2, 0xFA, 0x61, 0xB3, 0x7D, 0x9A, 0x98, 0x53, 0xBF, 0x2F, 0xDE, 0x38, - 0x98, 0x2B, 0x3A, 0x6C, 0x0D, 0x8E, 0x0D, 0x0D, 0x12, 0x77, 0x0A, 0x28, 0x5E, 0x0A, 0xD6, 0xF1, 0xAE, 0x0E, 0x22, 0x5C, 0xA8, 0x51, 0x5B, 0x2A, - 0x7A, 0xE8, 0xBC, 0x36, 0x74, 0xD5, 0x2C, 0x3A, 0x5B, 0x8A, 0x5E, 0x1C, 0x47, 0xAF, 0x91, 0xD3, 0xA7, 0xF0, 0x6E, 0xE4, 0x7E, 0xDB, 0xE0, 0x05, - 0x4C, 0xE4, 0xDE, 0x79, 0xEF, 0x8B, 0x29, 0x19, 0x75, 0xCC, 0x8F, 0x7F, 0x41, 0xAF, 0xBC, 0x17, 0x0D, 0x91, 0x27, 0xE4, 0xAD, 0xFF, 0x5F, 0xDF, - 0xA8, 0x5F, 0x9E, 0x6E, 0xB5, 0x86, 0x25, 0xB8, 0xB3, 0x7A, 0x71, 0xB3, 0x40, 0xAE, 0xB0, 0xA2, 0xEC, 0xEF, 0x42, 0x9D, 0xC7, 0xAA, 0x3B, 0xEE, - 0xA2, 0xBE, 0xB5, 0xCE, 0x84, 0x0F, 0x0F, 0x1A, 0xF1, 0x23, 0x50, 0x33, 0x0D, 0xBF, 0x32, 0xE0, 0x9E, 0xB3, 0x03, 0x27, 0x91, 0x57, 0x0C, 0x03, - 0x01, 0x0A, 0xFF, 0x6B, 0xF5, 0x87, 0xC2, 0x82, 0xE7, 0x9B, 0x65, 0xE5, 0xF4, 0x48, 0xD8, 0x33, 0xD6, 0x27, 0x6A, 0x2A, 0x35, 0xCB, 0x77, 0x33, - 0x6F, 0x20, 0xC8, 0x9A, 0xD3, 0xD2, 0xD5, 0xA4, 0xF6, 0x75, 0xCB, 0xF4, 0x3D, 0xC1, 0xF9, 0x07, 0x14, 0x7A, 0x9F, 0xC7, 0x3E, 0x45, 0xF3, 0xE7, - 0x1C, 0xDC, 0xA3, 0xAA, 0xFC, 0xDF, 0x18, 0xF3, 0x61, 0xA3, 0x3F, 0xDC, 0xB6, 0x0F, 0x11, 0x8B, 0x51, 0xDE, 0xFB, 0x2C, 0xF8, 0x58, 0x5F, 0xF3, - 0x38, 0xA2, 0x9C, 0xC1, 0xD1, 0xFC, 0xAE, 0x50, 0x7F, 0xC1, 0xC2, 0xDC, 0x1E, 0x29, 0x3F, 0xB4, 0xCF, 0x55, 0x08, 0x90, 0xE7, 0x89, 0xE7, 0xB7, - 0xCD, 0x7A, 0xE5, 0x35, 0xFA, 0xAA, 0x7A, 0x9E, 0xD1, 0x41, 0x3F, 0x4E, 0x8C, 0x97, 0x7C, 0x68, 0x91, 0xCE, 0x0B, 0x49, 0x36, 0x29, 0x88, 0x63, - 0x81, 0xD0, 0x03, 0x03, 0x47, 0x00, 0xC6, 0x71, 0xFC, 0x2D, 0x69, 0xF5, 0xAD, 0xDB, 0xBB, 0x48, 0x89, 0xEA, 0x25, 0xC4, 0xE2, 0x3D, 0x5A, 0xDB, - 0xE5, 0x84, 0xD3, 0x2D, 0x9B, 0xA0, 0xDF, 0xE0, 0xA0, 0x05, 0x62, 0x67, 0xEE, 0xA4, 0x2A, 0xAE, 0x9E, 0xC3, 0xED, 0xA5, 0x93, 0xED, 0x64, 0x51, - 0xED, 0x8B, 0x7C, 0x6D, 0x7B, 0x99, 0x36, 0xA6, 0x6D, 0x36, 0x88, 0x85, 0x6C, 0x0B, 0x33, 0xB4, 0x37, 0x9D, 0xF0, 0x5D, 0x95, 0xC1, 0xF9, 0x73, - 0xE4, 0xBE, 0x61, 0x10, 0x71, 0x01, 0xD8, 0x44, 0x81, 0xBC, 0xD9, 0xE5, 0x1E, 0x33, 0xC5, 0x53, 0x84, 0x06, 0x7B, 0x2D, 0x67, 0xF7, 0xE3, 0xDC, - 0x6E, 0xEE, 0x64, 0x4A, 0xCE, 0xDA, 0x69, 0x9C, 0x2F, 0xDB, 0x24, 0xDA, 0xD1, 0x5A, 0x56, 0x3C, 0xEF, 0xBC, 0x90, 0x01, 0x33, 0x25, 0x57, 0xCE, - 0x68, 0x87, 0x41, 0xCF, 0xE6, 0xB9, 0x25, 0xDB, 0xDE, 0xB1, 0xA3, 0x0E, 0x70, 0x5F, 0x69, 0xF1, 0xC2, 0xB3, 0x54, 0xDF, 0xB2, 0xD8, 0x6A, 0xC5, - 0x17, 0xEC, 0x35, 0x77, 0x76, 0x8D, 0x38, 0xF7, 0x14, 0x09, 0xFB, 0x0D, 0x06, 0xB7, 0xA5, 0xBD, 0xEA, 0xD0, 0x1D, 0x6B, 0xF7, 0x74, 0x63, 0xBA, - 0x31, 0xB4, 0x70, 0xCF, 0xBA, 0x4F, 0x44, 0xBB, 0x6B, 0x95, 0xDD, 0x9A, 0x7A, 0xFB, 0x33, 0x00, 0xF7, 0xAD, 0xB4, 0xDD, 0x65, 0xC8, 0x1D, 0xFD, - 0x72, 0x34, 0x84, 0x62, 0x04, 0xB3, 0x54, 0x14, 0x46, 0x44, 0xE4, 0x64, 0x36, 0x44, 0x07, 0x36, 0xC0, 0x81, 0xBD, 0x0E, 0xCD, 0x0E, 0xC4, 0xA4, - 0xDB, 0x0E, 0xC2, 0xBB, 0x97, 0xF5, 0xDD, 0xEA, 0xCE, 0xD0, 0x75, 0x83, 0x50, 0xDA, 0x5D, 0x40, 0xED, 0x3D, 0xAE, 0x76, 0xA6, 0x5D, 0xFB, 0xD6, - 0xB1, 0x3D, 0xED, 0xDB, 0x49, 0xEC, 0x77, 0x31, 0xF6, 0xBE, 0x1F, 0xFB, 0x60, 0x93, 0x7D, 0xCB, 0xAE, 0x89, 0x66, 0x47, 0xED, 0xA2, 0xD7, 0x42, - 0xEF, 0x49, 0xE9, 0xCD, 0xA5, 0xF3, 0x8E, 0x0F, 0x7F, 0xE5, 0xC3, 0x3E, 0x87, 0x8D, 0x7E, 0xE8, 0xDA, 0x36, 0x61, 0x42, 0xD2, 0x19, 0x45, 0x36, - 0x1C, 0xC0, 0x05, 0x58, 0x59, 0x0C, 0x0D, 0x65, 0x1A, 0x73, 0x64, 0x28, 0x8B, 0x55, 0x30, 0x6B, 0x6E, 0x2A, 0x9B, 0xA8, 0xA9, 0x22, 0x29, 0x29, - 0xAA, 0xB5, 0xA8, 0x31, 0x39, 0xDC, 0x6D, 0x02, 0x54, 0x1D, 0xDE, 0x53, 0x9E, 0xA0, 0x53, 0xE0, 0x60, 0x13, 0xE2, 0x25, 0xD3, 0x24, 0xA6, 0x7D, - 0x29, 0xD8, 0xF3, 0x2E, 0x42, 0x42, 0xFF, 0xF9, 0xA1, 0x77, 0x6D, 0xD1, 0x83, 0x7E, 0xD7, 0x3F, 0xF0, 0xF9, 0xBE, 0xE2, 0x0F, 0xFD, 0x8B, 0x74, - 0x51, 0x7A, 0x2F, 0x7D, 0x4D, 0x77, 0xD3, 0x9F, 0xE8, 0x24, 0x20, 0xC2, 0x76, 0x3D, 0x57, 0xE8, 0xC0, 0xA1, 0xFF, 0xD9, 0xEA, 0x0C, 0xB6, 0xFA, - 0x84, 0xA3, 0xE3, 0xC0, 0x87, 0xC9, 0x1C, 0x82, 0x12, 0x41, 0xA5, 0x10, 0xD1, 0x3E, 0x90, 0x58, 0xC6, 0x0F, 0x5F, 0x14, 0x1E, 0xCA, 0xA3, 0x1E, - 0x1B, 0xA4, 0x1C, 0x17, 0x9C, 0x38, 0x70, 0x7C, 0x37, 0x90, 0xD8, 0x49, 0x1A, 0x57, 0x19, 0x3A, 0x19, 0x5D, 0xBE, 0xEC, 0x01, 0xBF, 0x6B, 0x05, - 0x19, 0x6A, 0x3C, 0x8D, 0xDF, 0x81, 0x66, 0x9A, 0xB0, 0x46, 0x5A, 0x05, 0x3C, 0x9A, 0x33, 0x0A, 0x5A, 0x55, 0xED, 0x39, 0x77, 0x6D, 0xD1, 0xD9, - 0x85, 0x54, 0x73, 0x8C, 0xA9, 0x66, 0x0D, 0x39, 0x33, 0x0A, 0xDB, 0xF2, 0x0D, 0x9E, 0xF2, 0x55, 0x72, 0x0C, 0x77, 0x64, 0x72, 0x01, 0x13, 0x18, - 0xF0, 0x3C, 0x72, 0x86, 0xF7, 0x71, 0x00, 0xC9, 0xFD, 0xA2, 0x97, 0xF1, 0x03, 0xD6, 0x62, 0x52, 0x04, 0xC4, 0x21, 0x8C, 0x87, 0x5B, 0x7C, 0xBD, - 0xB0, 0x14, 0x11, 0x9E, 0x4E, 0x10, 0x3C, 0xDB, 0x13, 0x2C, 0xB3, 0xE1, 0x97, 0x39, 0xEB, 0x17, 0x8A, 0xD1, 0x97, 0xE4, 0xC7, 0xC5, 0xDA, 0xE8, - 0x62, 0x6F, 0xD0, 0x16, 0xAF, 0x83, 0x14, 0xBB, 0x5F, 0x72, 0xBA, 0x20, 0x4A, 0x75, 0xF4, 0x15, 0xA0, 0x5B, 0x54, 0xC6, 0xC8, 0x35, 0xD1, 0x82, - 0x9B, 0x3A, 0x7B, 0x6B, 0x19, 0x6D, 0x4B, 0xAC, 0x5C, 0x4B, 0xEB, 0x52, 0x3B, 0x02, 0xA0, 0x59, 0x69, 0x16, 0x68, 0x48, 0xF6, 0x29, 0x2A, 0x96, - 0xC1, 0x12, 0xAC, 0xD4, 0x3D, 0xE9, 0xD5, 0x83, 0x99, 0xA5, 0xEB, 0x01, 0x59, 0x7A, 0xAB, 0x3D, 0xF7, 0x74, 0x6B, 0x81, 0x9D, 0xAD, 0x07, 0xD3, - 0x2E, 0xA1, 0x37, 0x06, 0xB9, 0xB6, 0x7A, 0x95, 0x04, 0xB5, 0x92, 0xDA, 0xA4, 0xED, 0xC3, 0x69, 0x94, 0x38, 0x48, 0x44, 0x6F, 0x25, 0x72, 0x1D, - 0x76, 0x28, 0xB3, 0xDD, 0xCA, 0xF4, 0xB4, 0x67, 0x3D, 0x53, 0x9D, 0xA7, 0x1A, 0xF9, 0xD2, 0xFF, 0x0E, 0x7B, 0x3E, 0x3A, 0xBE, 0x7A, 0xFD, 0x9B, - 0x77, 0x1B, 0x4F, 0x03, 0xD1, 0xA7, 0xB8, 0xD5, 0x67, 0x6E, 0xC4, 0x59, 0x90, 0xFE, 0xFF, 0xDF, 0x1D, 0xC7, 0x25, 0x41, 0x75, 0x8B, 0xE1, 0x81, - 0x2D, 0x26, 0x81, 0x5A, 0x63, 0xB9, 0xDA, 0x15, 0xD0, 0xB5, 0x27, 0x3E, 0xD0, 0xD6, 0xD2, 0x69, 0x67, 0xA0, 0x67, 0x75, 0xC7, 0x66, 0x35, 0x21, - 0x38, 0x13, 0xCA, 0x6E, 0x91, 0xC9, 0x7E, 0x38, 0xC6, 0xDE, 0x86, 0x88, 0x6D, 0xD1, 0x84, 0xAD, 0x73, 0x84, 0x75, 0x82, 0xBF, 0x42, 0x53, 0xBE, - 0x0A, 0x15, 0xBA, 0x2A, 0xC2, 0x5D, 0xE1, 0x44, 0xB8, 0xBE, 0xB8, 0xB6, 0x82, 0x6D, 0xB2, 0xE6, 0x47, 0xB0, 0x8E, 0xE8, 0x61, 0x6D, 0x3A, 0xCE, - 0x2F, 0xB0, 0x5D, 0x6D, 0x3E, 0x5E, 0xFD, 0xCC, 0x5B, 0x1D, 0x40, 0xA2, 0xFA, 0xD7, 0xAC, 0xEA, 0x93, 0x97, 0xFA, 0xE2, 0x36, 0x95, 0x01, 0x32, - 0x6A, 0xA4, 0x0A, 0x2A, 0x19, 0x2E, 0x6A, 0xCC, 0xDC, 0x69, 0x60, 0xDB, 0xB4, 0xD5, 0xC8, 0x34, 0xC3, 0xDC, 0xF4, 0x8A, 0xB4, 0x74, 0x12, 0x88, - 0x34, 0x98, 0x85, 0x14, 0x92, 0x94, 0x74, 0x81, 0x7B, 0xF4, 0x95, 0x6B, 0xD4, 0x03, 0x49, 0xD4, 0x76, 0x46, 0xF4, 0x50, 0x32, 0x34, 0xD9, 0x40, - 0xE8, 0x0E, 0x59, 0xA8, 0xC8, 0x24, 0x68, 0x6A, 0x08, 0x28, 0x88, 0x17, 0x8B, 0x2C, 0x06, 0x2C, 0x90, 0x42, 0x3E, 0xA7, 0x0B, 0x3F, 0x7B, 0x10, - 0x78, 0xCA, 0xFD, 0x79, 0xCA, 0xDF, 0x3C, 0x75, 0x96, 0xF1, 0x5B, 0x1A, 0x3A, 0xC3, 0xE2, 0x9C, 0x07, 0x16, 0x37, 0xF3, 0x78, 0x35, 0x37, 0x88, - 0x66, 0xAA, 0x60, 0xC2, 0x4C, 0xD1, 0x7B, 0x29, 0x1B, 0x68, 0xF9, 0x39, 0x6A, 0xF9, 0x87, 0x2B, 0x77, 0x5D, 0x54, 0xCE, 0x7C, 0x50, 0xB2, 0xD9, - 0x93, 0x48, 0xC5, 0x92, 0x35, 0x33, 0x91, 0x66, 0x21, 0x90, 0x53, 0x50, 0x77, 0x3B, 0x9E, 0x47, 0xB4, 0x1A, 0x15, 0x22, 0xB0, 0x1C, 0x23, 0x44, - 0x35, 0x5A, 0xC2, 0xA5, 0x20, 0xB1, 0x86, 0x62, 0x36, 0x8F, 0x11, 0x73, 0xC2, 0xA2, 0x71, 0x9C, 0x62, 0xAD, 0xA0, 0x22, 0x09, 0x86, 0x44, 0x36, - 0x8C, 0x13, 0x41, 0x87, 0x88, 0x3F, 0x5C, 0x87, 0x5F, 0x9E, 0x26, 0xD6, 0x5A, 0x7C, 0xC3, 0xB9, 0x16, 0x76, 0x3E, 0xA4, 0x42, 0x3D, 0x16, 0x84, - 0x99, 0xA6, 0x84, 0x12, 0x68, 0x08, 0x17, 0x28, 0x97, 0x48, 0xD4, 0x1D, 0x74, 0x51, 0x33, 0x88, 0x28, 0x61, 0x10, 0x10, 0x80, 0xA0, 0xE8, 0xB4, - 0xE0, 0x88, 0x04, 0x12, 0xB2, 0x23, 0x24, 0x22, 0x87, 0x7E, 0x61, 0x46, 0x44, 0x8D, 0x8C, 0xF8, 0x70, 0x22, 0x4A, 0x53, 0x44, 0x70, 0x30, 0x85, - 0x30, 0x10, 0x17, 0x62, 0xA8, 0x19, 0x44, 0x70, 0x11, 0x88, 0x9C, 0x33, 0x0F, 0x9D, 0x1F, 0xD2, 0xC2, 0x3E, 0x78, 0x84, 0x05, 0x3E, 0x35, 0x79, - 0x10, 0x0D, 0xF3, 0x80, 0x01, 0x85, 0x87, 0x9D, 0x39, 0xA0, 0x1A, 0x9A, 0x71, 0x8A, 0x3F, 0x4B, 0xA7, 0xBF, 0xAC, 0x0E, 0x75, 0x3C, 0xBB, 0x3A, - 0xCD, 0x93, 0x9F, 0x2E, 0x08, 0x3F, 0xF9, 0x82, 0x0E, 0x84, 0x20, 0x3A, 0x6C, 0x81, 0xE8, 0x00, 0x60, 0x87, 0x03, 0xDD, 0xAF, 0x8F, 0x73, 0xB2, - 0xAF, 0xCE, 0xB8, 0xCF, 0xA9, 0xC2, 0x06, 0xBB, 0xA3, 0x06, 0x58, 0x11, 0x1A, 0x14, 0x2D, 0x68, 0x80, 0xAB, 0xE5, 0xC3, 0x9F, 0xC6, 0xA8, 0xCC, - 0x90, 0xAF, 0xCD, 0x30, 0x22, 0xC1, 0x9E, 0x3E, 0x78, 0x0F, 0xE5, 0x55, 0xCF, 0x99, 0x48, 0xEF, 0x33, 0x54, 0x4F, 0xCD, 0x41, 0x37, 0x42, 0xC6, - 0xE0, 0x23, 0x9A, 0x37, 0x1C, 0x93, 0xA7, 0xF2, 0xE2, 0x42, 0x0C, 0xA4, 0x85, 0x24, 0x8F, 0x0A, 0x58, 0x1E, 0x74, 0xB3, 0x1C, 0x0A, 0x11, 0x06, - 0xF1, 0xEC, 0xAA, 0x26, 0x1C, 0xC6, 0xB8, 0x13, 0xD1, 0xF6, 0x25, 0xA4, 0x76, 0x45, 0x78, 0xB4, 0x82, 0x40, 0x7C, 0xC0, 0x6E, 0xD5, 0xCD, 0x4E, - 0x84, 0xCF, 0x81, 0xB1, 0x05, 0x07, 0x3F, 0x8A, 0x0D, 0x48, 0xEC, 0x75, 0x54, 0x1B, 0x18, 0x04, 0x71, 0xD2, 0xB9, 0x31, 0x2E, 0xF8, 0xBA, 0x9D, - 0xE7, 0x8D, 0xCE, 0xC1, 0xE1, 0x15, 0x20, 0xBA, 0x48, 0xA0, 0x97, 0x8B, 0x20, 0x94, 0x3D, 0x3F, 0x90, 0x08, 0x6D, 0xC0, 0xD6, 0x57, 0x6E, 0xB4, - 0xA5, 0x00, 0x74, 0x46, 0x5C, 0xB3, 0xE3, 0x8F, 0x7B, 0xA1, 0x81, 0xAB, 0xD1, 0xA0, 0xBF, 0x35, 0xC1, 0xDD, 0xB8, 0xD4, 0x7E, 0x9A, 0xDA, 0x1E, - 0xAA, 0x4F, 0xED, 0x4C, 0xEE, 0x21, 0x4C, 0x8E, 0xF7, 0x40, 0x2E, 0x49, 0x20, 0xEE, 0xDA, 0x41, 0x4E, 0xA9, 0xB7, 0x87, 0xD1, 0x28, 0x87, 0x45, - 0xDC, 0x9F, 0x98, 0xCA, 0x63, 0x7C, 0xEE, 0x77, 0xD2, 0xE4, 0x28, 0xAD, 0x63, 0x59, 0x08, 0xA8, 0xEC, 0x02, 0xFE, 0xF4, 0x46, 0x9F, 0x83, 0x8E, - 0x40, 0x83, 0xF6, 0x26, 0xE0, 0xB9, 0xF9, 0x08, 0x6D, 0xD4, 0x8A, 0xBD, 0xDA, 0x6F, 0x5A, 0x05, 0x0A, 0x1A, 0x7A, 0xBF, 0x8D, 0x7C, 0x61, 0x8D, - 0xFE, 0xD1, 0x26, 0xDF, 0x19, 0xA5, 0x5F, 0x95, 0xCA, 0xB7, 0x58, 0x33, 0x96, 0xE7, 0xA7, 0x27, 0x24, 0x6D, 0x50, 0xC5, 0x53, 0xD8, 0xB0, 0x42, - 0xF3, 0x12, 0x76, 0xF4, 0x6B, 0x8C, 0x7C, 0x6A, 0x46, 0x99, 0x3C, 0x9E, 0xFD, 0x62, 0x55, 0x1F, 0xEB, 0x9B, 0xFF, 0x50, 0x36, 0x68, 0xEF, 0x1A, - 0xF7, 0x00, 0x1A, 0x35, 0xC2, 0x99, 0x2C, 0x4A, 0xB7, 0x05, 0x48, 0x19, 0xF9, 0x88, 0x78, 0x0C, 0xEA, 0x34, 0xCC, 0x83, 0xBB, 0x17, 0x4E, 0x66, - 0xBD, 0x4B, 0x07, 0x05, 0x2D, 0xCE, 0xA2, 0x00, 0x66, 0x04, 0x9C, 0x04, 0x43, 0x7F, 0x24, 0x06, 0x9B, 0xA1, 0xA8, 0xA4, 0xE4, 0x22, 0xA7, 0x38, - 0x3C, 0x00, 0x00, 0x20, 0x82, 0x20, 0x00, 0x53, 0x89, 0x22, 0x8C, 0xC3, 0x18, 0x40, 0x8E, 0x31, 0xE8, 0xD4, 0x00, 0x04, 0x20, 0x05, 0x60, 0x4A, - 0x01, 0x1C, 0x07, 0x40, 0x20, 0x0A, 0x21, 0x0C, 0x03, 0x30, 0x0C, 0x44, 0x22, 0x00, 0x00, 0x42, 0x10, 0x00, 0x06, 0x00, 0x03, 0xC0, 0x10, 0x22, - 0x1A, 0x2A, 0x0B, 0x63, 0xAF, 0x8B, 0xC3, 0x1C, 0x0C, 0x16, 0x62, 0xFF, 0x0D, 0xDD, 0x72, 0xEC, 0x50, 0x3B, 0x2B, 0x4B, 0x76, 0xC9, 0xB4, 0xE9, - 0x58, 0x17, 0xCD, 0xD9, 0xF6, 0xF2, 0x35, 0xA5, 0x10, 0xC6, 0x4E, 0x83, 0xA8, 0x62, 0xB7, 0x53, 0x06, 0x31, 0x49, 0x35, 0xA8, 0xA8, 0x2E, 0x6C, - 0x3D, 0xB4, 0xA5, 0xE2, 0x9C, 0x1D, 0x67, 0xAE, 0x91, 0xDB, 0x20, 0xF5, 0x56, 0xD8, 0x27, 0x0A, 0x42, 0xE0, 0xAB, 0x7C, 0xA9, 0x0A, 0x5A, 0x67, - 0xD8, 0xF4, 0x94, 0x85, 0xEB, 0xA0, 0xBD, 0xC9, 0xBA, 0xC1, 0xF4, 0x58, 0x5B, 0x93, 0x13, 0x19, 0xA6, 0xE5, 0xF7, 0x4C, 0xEC, 0x83, 0xB7, 0xB8, - 0x7C, 0xC9, 0x59, 0x47, 0x9A, 0x8B, 0xFA, 0xFA, 0xD0, 0x28, 0xE7, 0x36, 0xE7, 0x88, 0xAF, 0xCD, 0x36, 0xC6, 0x1D, 0x85, 0x59, 0x55, 0xAD, 0x94, - 0xC7, 0xD9, 0x29, 0x0B, 0x0B, 0x08, 0xEC, 0x65, 0x44, 0xDB, 0x03, 0x1A, 0x7A, 0x09, 0x76, 0x22, 0xD0, 0xE1, 0x9F, 0x52, 0xAE, 0xE4, 0xC0, 0x21, - 0xB6, 0x28, 0xAB, 0xDF, 0x93, 0xDE, 0x82, 0xC4, 0x3E, 0x06, 0xB0, 0x33, 0x74, 0x42, 0x11, 0x73, 0x51, 0xB0, 0xCF, 0xF0, 0xD5, 0xC5, 0x11, 0xBD, - 0xBE, 0xD5, 0xE4, 0x7E, 0xA0, 0xF5, 0xC1, 0xDD, 0xB1, 0x4F, 0xAF, 0xE6, 0xEC, 0x92, 0xD3, 0x57, 0x73, 0x40, 0x66, 0x2D, 0x4E, 0x92, 0xCD, 0x24, - 0xA3, 0x23, 0x41, 0x77, 0x6F, 0x18, 0x00, 0x5B, 0x55, 0x2D, 0x1B, 0xA9, 0xFA, 0xDB, 0x4C, 0x85, 0xCB, 0xDF, 0xB1, 0x01, 0x06, 0x59, 0x84, 0xE3, - 0x8E, 0x99, 0xB4, 0x31, 0xF8, 0x9C, 0x75, 0xDD, 0x42, 0x41, 0x5F, 0xB3, 0x7B, 0x22, 0xB9, 0xDC, 0x49, 0x31, 0xCF, 0xBA, 0xCF, 0x39, 0x46, 0xDD, - 0xA1, 0x39, 0x4F, 0x70, 0x33, 0xD1, 0x78, 0xE8, 0x3C, 0x9C, 0xAD, 0xBF, 0xF3, 0x50, 0x3A, 0xA6, 0x8C, 0x3F, 0xB7, 0xB1, 0xEE, 0xDB, 0xC0, 0xC9, - 0xD1, 0xBC, 0x6E, 0x60, 0xE6, 0xF1, 0xBE, 0xE9, 0xDC, 0x19, 0xD7, 0xCC, 0xDC, 0x17, 0xE5, 0x7D, 0xC2, 0x0D, 0x15, 0x64, 0x7F, 0x64, 0x75, 0x5D, - 0x26, 0xD0, 0xFA, 0x1C, 0xCE, 0x5A, 0xF5, 0x97, 0x95, 0xBC, 0x30, 0x35, 0x9E, 0x95, 0xD0, 0x71, 0x13, 0x7F, 0xF4, 0xD0, 0x4A, 0xF5, 0x43, 0xCF, - 0x6B, 0x86, 0x2B, 0xB7, 0xBB, 0x50, 0xD1, 0x23, 0x7A, 0xB6, 0xD3, 0x3D, 0x2E, 0xFF, 0xEB, 0x70, 0x9F, 0xDC, 0x3F, 0xB6, 0xB0, 0x4C, 0xA7, 0x39, - 0xAB, 0xCE, 0x91, 0xDA, 0xB0, 0x0A, 0xB6, 0xE5, 0x73, 0xA6, 0x24, 0xC0, 0x35, 0x43, 0x7A, 0x22, 0xFB, 0xD4, 0xAE, 0x07, 0xD5, 0x33, 0xA0, 0x3D, - 0xFD, 0xBB, 0x28, 0xB1, 0x65, 0x0D, 0xC2, 0x63, 0xEC, 0xD3, 0xDC, 0x4C, 0xAF, 0x79, 0x8D, 0x80, 0x61, 0x36, 0x5A, 0xA9, 0x1F, 0x0E, 0xEB, 0x0C, - 0x98, 0xB9, 0xA7, 0xF6, 0x21, 0x28, 0xF3, 0xC7, 0xA0, 0xC2, 0xDC, 0x80, 0x04, 0x26, 0xAA, 0xFD, 0x1F, 0xF9, 0x6C, 0x91, 0xEA, 0x9A, 0x3F, 0xBD, - 0x83, 0xDC, 0x2D, 0xBA, 0x2D, 0xA9, 0xD0, 0x72, 0x7D, 0xF5, 0x4E, 0xB2, 0xC3, 0xAD, 0x1D, 0x13, 0x51, 0x5C, 0xB7, 0xF4, 0x95, 0x24, 0xAD, 0x96, - 0x99, 0x77, 0x8B, 0x6B, 0x55, 0x5B, 0xDB, 0x4A, 0x25, 0x7E, 0xC8, 0x5F, 0xFC, 0x8A, 0xC8, 0x4F, 0x36, 0x21, 0xDB, 0x14, 0x81, 0x99, 0x7E, 0xE8, - 0x93, 0x55, 0xC9, 0xE8, 0x42, 0x0F, 0xDC, 0xB9, 0x0D, 0xC3, 0xAD, 0xD0, 0x5A, 0x20, 0xD0, 0xB6, 0x53, 0xC3, 0xEF, 0x71, 0x6E, 0xD9, 0xFE, 0x74, - 0x56, 0xBB, 0x36, 0xC5, 0xF4, 0x07, 0xD0, 0x5F, 0xA4, 0x40, 0x46, 0xB2, 0x50, 0x60, 0x5D, 0x4F, 0x83, 0xC1, 0x91, 0x91, 0xF0, 0xF1, 0x8B, 0x45, - 0x99, 0x0E, 0xBB, 0x87, 0x58, 0x7A, 0x36, 0xD4, 0xC2, 0xB8, 0xB7, 0xCF, 0x67, 0xA2, 0xDE, 0xDB, 0x5F, 0x09, 0x01, 0x32, 0xE1, 0x9B, 0x96, 0xA3, - 0x45, 0x05, 0x9E, 0xD9, 0x42, 0xF0, 0x8D, 0x5D, 0xAF, 0xA7, 0x6D, 0x7A, 0x04, 0xC3, 0xA0, 0x05, 0x66, 0x52, 0x07, 0x92, 0x06, 0xA4, 0xB5, 0xDC, - 0xEF, 0x57, 0xA9, 0xBB, 0x34, 0x48, 0x4B, 0x57, 0xD4, 0x84, 0x62, 0xF7, 0xF3, 0x7A, 0xD8, 0x0D, 0x09, 0xF6, 0x5D, 0xA8, 0xE5, 0x85, 0x58, 0x6B, - 0x61, 0x62, 0x40, 0x98, 0x72, 0x73, 0x6B, 0x43, 0xD1, 0xFB, 0x2E, 0x40, 0x43, 0xEF, 0x64, 0xD4, 0x40, 0x3C, 0x39, 0x1B, 0xCA, 0x4E, 0x2D, 0x45, - 0x62, 0xF7, 0xDB, 0x05, 0x15, 0xDD, 0xE4, 0xCC, 0x6C, 0xA8, 0x25, 0x3E, 0x42, 0x61, 0xAC, 0x25, 0xB7, 0x7C, 0xF0, 0x80, 0x29, 0x77, 0x3F, 0xF9, - 0xF6, 0xA2, 0xDD, 0xFA, 0x5D, 0xF3, 0x78, 0xD2, 0x5B, 0x39, 0x27, 0xCC, 0x1A, 0x02, 0x7E, 0x95, 0xD3, 0x07, 0xCD, 0x71, 0xA5, 0x28, 0x38, 0x11, - 0x6A, 0xA1, 0x04, 0xC8, 0x21, 0x7E, 0x9E, 0x87, 0x8D, 0xA3, 0x3A, 0xED, 0x52, 0x5A, 0x08, 0x38, 0x1B, 0x29, 0x10, 0x24, 0xF5, 0x3B, 0x70, 0xC5, - 0x05, 0xF4, 0xAA, 0x96, 0x34, 0x30, 0xEC, 0xA2, 0x38, 0x74, 0xEA, 0xCC, 0x1E, 0xBD, 0xC9, 0xD6, 0x9F, 0x9B, 0x87, 0x59, 0x91, 0x96, 0xB3, 0x16, - 0x42, 0x5C, 0x9F, 0x5E, 0x6E, 0x76, 0x93, 0x6A, 0xAB, 0xB6, 0x5F, 0x6D, 0x4F, 0x6C, 0x1B, 0x9C, 0xFD, 0x64, 0x87, 0x6F, 0x67, 0x4D, 0x0E, 0x4D, - 0x8B, 0x3E, 0x1D, 0x7F, 0xAF, 0xA9, 0x25, 0xF0, 0xB2, 0xCD, 0x95, 0x82, 0x1B, 0xCC, 0x9A, 0x89, 0x27, 0x18, 0x5B, 0x7C, 0xD0, 0xA7, 0x89, 0x6D, - 0x21, 0x41, 0x38, 0x78, 0x35, 0x40, 0x37, 0xD7, 0xC4, 0x48, 0x6B, 0xF6, 0x21, 0x7E, 0xD0, 0x31, 0xD1, 0xCA, 0x6E, 0x1F, 0x18, 0x36, 0x45, 0xAF, - 0x09, 0xB2, 0xFB, 0x1D, 0xA2, 0x33, 0x4E, 0xAA, 0xDF, 0x1A, 0x3A, 0x10, 0xF4, 0xF0, 0x6E, 0x47, 0xD9, 0x8E, 0x20, 0x8D, 0x8F, 0xAE, 0x36, 0xD4, - 0x14, 0x3D, 0xAC, 0xAD, 0xD1, 0x7B, 0x24, 0xEC, 0xBF, 0xD3, 0x75, 0x26, 0x39, 0x80, 0xAD, 0x9E, 0xAA, 0x92, 0x3E, 0x86, 0xA7, 0xD1, 0x62, 0x4B, - 0x87, 0x03, 0x2C, 0xBD, 0x88, 0xEB, 0xF6, 0x5F, 0xE6, 0xD1, 0xFD, 0x3D, 0x9C, 0xC4, 0xAC, 0xC6, 0xE5, 0xDA, 0x14, 0x43, 0xC5, 0xE4, 0x50, 0x5A, - 0xD1, 0x5A, 0x45, 0x75, 0xF4, 0xD6, 0x20, 0xBB, 0x7F, 0x92, 0xA5, 0xB6, 0x87, 0xF9, 0x24, 0xAE, 0x8C, 0xB0, 0xA6, 0x01, 0xF8, 0x4B, 0x72, 0x5A, - 0x59, 0xDA, 0xA8, 0xBC, 0xF5, 0x40, 0xFD, 0x7F, 0x30, 0x27, 0xD7, 0x71, 0x96, 0x75, 0xF3, 0xCB, 0x35, 0x9C, 0x17, 0x95, 0x14, 0x9D, 0x05, 0x7A, - 0xA1, 0xEE, 0xDD, 0x2D, 0xB6, 0x82, 0xAE, 0x07, 0xA3, 0x8D, 0x4B, 0xB0, 0x8F, 0x30, 0xB9, 0xF8, 0x37, 0x68, 0x7A, 0xED, 0x05, 0x7B, 0x76, 0x20, - 0x04, 0xE6, 0xCD, 0x05, 0xA8, 0x25, 0x4F, 0xC8, 0xBF, 0x93, 0x66, 0x62, 0x17, 0x6D, 0x0F, 0xB0, 0x5B, 0x45, 0xAF, 0xAE, 0xE1, 0x9F, 0x39, 0x71, - 0x94, 0x72, 0x07, 0xEC, 0x16, 0x72, 0xAE, 0xD4, 0x07, 0xC1, 0xE6, 0xF7, 0xF8, 0x7F, 0xE7, 0x33, 0x07, 0x76, 0x6E, 0xEC, 0x34, 0x3B, 0xBC, 0xD3, - 0xE1, 0x6E, 0x2A, 0x77, 0x3F, 0xC0, 0x0E, 0x35, 0x64, 0xF0, 0xB5, 0x0F, 0x05, 0x3B, 0x2A, 0x3E, 0xE4, 0x08, 0xBD, 0x74, 0x06, 0xA1, 0x2D, 0xD1, - 0x5B, 0x24, 0xE7, 0x48, 0x5E, 0x63, 0x08, 0xAC, 0x06, 0x69, 0x7F, 0x25, 0x37, 0x0F, 0x18, 0xE0, 0xD0, 0xEB, 0x4C, 0x9C, 0x57, 0xB4, 0x41, 0xF7, - 0xDE, 0xAD, 0xA6, 0xF8, 0x2A, 0xBA, 0xE1, 0x14, 0x76, 0xAF, 0xDD, 0xBB, 0x9E, 0x46, 0x8E, 0x27, 0x74, 0xE8, 0xAF, 0xF2, 0xC4, 0xF3, 0x4D, 0x4D, - 0x76, 0xB5, 0xC5, 0xD2, 0xCD, 0x05, 0x6A, 0x1C, 0x7A, 0x4C, 0xA2, 0x72, 0x57, 0x0C, 0x0C, 0xD3, 0xA3, 0x7D, 0x7A, 0x7B, 0xB2, 0x39, 0x04, 0xB9, - 0x0E, 0xF6, 0x79, 0xB3, 0x5F, 0xA0, 0x81, 0xB6, 0x41, 0xC1, 0x11, 0xE6, 0xBB, 0x3F, 0x77, 0x86, 0x7F, 0x28, 0x0D, 0x10, 0xAF, 0x96, 0xC9, 0x22, - 0xFA, 0x61, 0x5B, 0x53, 0xBA, 0x75, 0x00, 0xC1, 0x9B, 0x31, 0x8E, 0xD9, 0xF8, 0xEE, 0x0A, 0x0E, 0xD1, 0x48, 0x70, 0x90, 0x75, 0x1E, 0xB0, 0x95, - 0xCF, 0x9B, 0x26, 0x0D, 0x2D, 0x24, 0x23, 0x3E, 0x9B, 0xC7, 0x76, 0x9D, 0x29, 0xD8, 0x36, 0x4C, 0x06, 0xAD, 0x6E, 0x55, 0x24, 0x96, 0xEA, 0x76, - 0x22, 0x8D, 0x0D, 0xDE, 0xEB, 0x3E, 0x07, 0x03, 0xC5, 0x06, 0xA2, 0x62, 0x35, 0xC2, 0x14, 0xDD, 0x6F, 0x64, 0xBA, 0x86, 0x49, 0xC2, 0x45, 0x47, - 0x01, 0xBA, 0xB7, 0x33, 0xB5, 0x2B, 0xE0, 0xF0, 0xD1, 0xED, 0xAA, 0xA7, 0xD7, 0x08, 0xDC, 0x4B, 0xC4, 0x2B, 0xF0, 0xA5, 0x79, 0x63, 0x82, 0x70, - 0x7A, 0x66, 0x69, 0x04, 0x6A, 0x84, 0x67, 0x9F, 0xCB, 0x95, 0x4A, 0x11, 0xAC, 0xB7, 0x07, 0x19, 0x66, 0x00, 0x32, 0x98, 0xD8, 0x7F, 0x17, 0x42, - 0xED, 0x85, 0x4B, 0x64, 0xFD, 0xFE, 0x3A, 0xDC, 0xB2, 0x95, 0x1E, 0x21, 0xC4, 0x2C, 0x0A, 0xDF, 0x9E, 0xFB, 0x8A, 0x15, 0xAF, 0xA5, 0xCB, 0xDF, - 0xC9, 0xB5, 0x2F, 0xA2, 0xDF, 0x76, 0xE0, 0xFB, 0x1C, 0xA6, 0x9F, 0x63, 0xCF, 0x5B, 0x23, 0x6D, 0x46, 0x43, 0x14, 0xF6, 0x09, 0x92, 0xB9, 0x9B, - 0xAD, 0xB6, 0x47, 0x28, 0xB2, 0x37, 0x5D, 0x91, 0x79, 0x09, 0xC2, 0xED, 0x0C, 0xCF, 0x5A, 0x98, 0xD3, 0xDF, 0xB4, 0x35, 0x5A, 0x89, 0xDA, 0xE2, - 0x69, 0xA4, 0xED, 0x37, 0xE6, 0xC8, 0xD1, 0xF3, 0x18, 0xDF, 0x6B, 0x34, 0xE4, 0x5D, 0xF9, 0xE8, 0x5A, 0x8B, 0x35, 0x11, 0xD7, 0x9D, 0xA5, 0x4A, - 0xF7, 0x83, 0x15, 0x6E, 0x95, 0xBA, 0x8E, 0x45, 0xEB, 0x1F, 0x40, 0x5C, 0x51, 0xB9, 0x11, 0x31, 0xE0, 0xC6, 0x1C, 0xC0, 0x59, 0xB6, 0x4D, 0x4E, - 0x46, 0x23, 0x27, 0x47, 0x47, 0x73, 0x5A, 0x96, 0x3D, 0x09, 0x7F, 0xB0, 0xFB, 0x70, 0x39, 0xA7, 0x60, 0xDF, 0xF8, 0x3E, 0xB8, 0xB8, 0x5B, 0xE5, - 0x4A, 0x46, 0x99, 0x15, 0x35, 0x19, 0x15, 0xBC, 0xF1, 0xF3, 0x1B, 0x26, 0xBE, 0xA1, 0x2C, 0xD2, 0x4B, 0xDF, 0xDF, 0xB8, 0xF1, 0xA8, 0xC8, 0xFD, - 0x89, 0x56, 0x45, 0x7F, 0x2E, 0x57, 0xEB, 0x88, 0x72, 0xDE, 0x56, 0xCC, 0x36, 0x06, 0x0B, 0xEE, 0x68, 0x13, 0x3C, 0x6D, 0xB3, 0x9B, 0xD4, 0x9F, - 0x50, 0x10, 0x3B, 0xE7, 0xA7, 0xDC, 0x2C, 0x38, 0xE7, 0x71, 0xF0, 0xBC, 0x7B, 0x0E, 0x05, 0x90, 0x07, 0x99, 0x15, 0xAA, 0xC1, 0x8D, 0x13, 0x03, - 0x7B, 0xFC, 0x49, 0xFD, 0xDE, 0x92, 0xEC, 0x14, 0xED, 0x76, 0x83, 0x24, 0x8B, 0x4B, 0x22, 0x34, 0x8A, 0xAA, 0xBE, 0xF1, 0xCD, 0x27, 0x6A, 0xF2, - 0x71, 0xF4, 0xD3, 0x17, 0x8A, 0x9D, 0x32, 0xCF, 0x9D, 0x72, 0x4E, 0x87, 0x5A, 0x3A, 0x81, 0x21, 0x8E, 0xD7, 0x6F, 0xD8, 0x05, 0x67, 0xA1, 0x65, - 0xB9, 0xFE, 0x09, 0x8F, 0x58, 0x7E, 0x69, 0x24, 0xFA, 0x41, 0xE8, 0xAD, 0x23, 0x28, 0x92, 0xDC, 0x7F, 0x87, 0xDE, 0xD9, 0x01, 0x76, 0xF0, 0xA8, - 0x29, 0x23, 0x73, 0xA1, 0x24, 0xB8, 0x8F, 0x49, 0x78, 0xC5, 0xB8, 0x9F, 0x43, 0xE8, 0x6F, 0x8F, 0xE8, 0x26, 0x1E, 0xED, 0x74, 0xE5, 0xFA, 0xDF, - 0xE9, 0x38, 0x63, 0x76, 0x9C, 0x5E, 0x9B, 0x3A, 0x3D, 0x33, 0x34, 0xFD, 0xFE, 0x92, 0x41, 0x8B, 0xB4, 0x60, 0x54, 0xAC, 0x26, 0x35, 0xEC, 0xCA, - 0xAB, 0x79, 0xDD, 0x81, 0x06, 0xF0, 0x27, 0xC2, 0xF8, 0x25, 0xAB, 0x2D, 0x66, 0xFD, 0x2F, 0x0B, 0xA4, 0xEF, 0xAA, 0xA9, 0x1B, 0x8A, 0x5E, 0x3F, - 0xE8, 0xB1, 0xD7, 0xB1, 0xAB, 0x68, 0xC6, 0x5F, 0xF3, 0xA1, 0xCE, 0xB6, 0xB3, 0xF3, 0x1A, 0x71, 0x60, 0xCC, 0x1C, 0x5B, 0x2C, 0x44, 0x69, 0xB5, - 0x46, 0xDE, 0xF0, 0x98, 0x48, 0x30, 0x46, 0x0F, 0xAD, 0x29, 0x9D, 0x47, 0x59, 0x27, 0x90, 0x73, 0xCF, 0x15, 0x2C, 0xEB, 0x94, 0xEC, 0xAD, 0x5E, - 0xB3, 0x9A, 0xFA, 0x15, 0x9F, 0xAF, 0xF4, 0x8E, 0x87, 0x1C, 0x47, 0xCD, 0xE0, 0x16, 0x83, 0xC1, 0x68, 0x70, 0x31, 0x0C, 0x77, 0xCC, 0x3A, 0xEC, - 0x5A, 0x2E, 0xD3, 0x93, 0xE9, 0x3F, 0x26, 0x6C, 0x0F, 0x29, 0x40, 0xFF, 0x8B, 0x2E, 0xCB, 0x72, 0x63, 0xD0, 0x60, 0xE3, 0xD0, 0xEE, 0xB4, 0x32, - 0x1E, 0x9E, 0x27, 0x33, 0xB3, 0xA5, 0x04, 0x72, 0xFC, 0x4C, 0xA2, 0x57, 0x0B, 0x56, 0xA1, 0x3D, 0x38, 0x3B, 0x73, 0x3D, 0xAD, 0x09, 0xE9, 0x7A, - 0x46, 0x4B, 0x6E, 0x6E, 0xB2, 0x2C, 0xF0, 0xB2, 0x85, 0x91, 0x58, 0x4A, 0xD1, 0x5F, 0x0B, 0x20, 0x19, 0x49, 0x87, 0xE0, 0x2C, 0xFF, 0x5E, 0x12, - 0xE9, 0x4A, 0xE6, 0x55, 0x98, 0xD8, 0x68, 0x07, 0x37, 0x3B, 0xB0, 0x72, 0xAB, 0x96, 0x3D, 0xB8, 0x0A, 0x3A, 0xCF, 0xE0, 0x81, 0x90, 0xA5, 0xB5, - 0x3D, 0x67, 0xB0, 0x09, 0x55, 0x49, 0x58, 0x75, 0x98, 0x5C, 0x3B, 0xD0, 0xD3, 0xBC, 0x15, 0xCF, 0xF2, 0x53, 0x28, 0x49, 0x22, 0xEC, 0x62, 0x79, - 0x3A, 0xA2, 0x16, 0xAE, 0x98, 0xF6, 0x0C, 0xC5, 0x1F, 0x8F, 0xA5, 0x59, 0xB1, 0x5C, 0x9E, 0x10, 0x5B, 0xBD, 0x73, 0xD9, 0xD3, 0x1C, 0x70, 0x40, - 0x1F, 0xF3, 0x13, 0x8E, 0x48, 0x7C, 0xDC, 0x23, 0xCB, 0x20, 0x30, 0xE7, 0xC5, 0x1E, 0x01, 0x6B, 0xB7, 0x29, 0x0E, 0x66, 0xB0, 0x66, 0x85, 0x8F, - 0x20, 0xAC, 0x7B, 0xCD, 0xDC, 0x61, 0x38, 0xFF, 0x23, 0xEF, 0xFE, 0x1F, 0xB6, 0xD6, 0x79, 0x9B, 0x60, 0x92, 0x9E, 0xF9, 0x4D, 0xDE, 0x32, 0x09, - 0xAB, 0xC9, 0x33, 0xB2, 0x9F, 0x38, 0x22, 0xBD, 0x39, 0x15, 0x05, 0xBD, 0xE3, 0x73, 0x65, 0xC1, 0x65, 0x0F, 0xF3, 0xF8, 0xBF, 0x33, 0x0D, 0xF7, - 0x19, 0xE6, 0xD6, 0xCD, 0xA1, 0xAC, 0xEB, 0x59, 0x64, 0x09, 0x0F, 0xA3, 0xB9, 0xCC, 0x9C, 0x64, 0x2B, 0x84, 0x38, 0x8D, 0x9F, 0xDA, 0x8D, 0x73, - 0x57, 0x2E, 0x10, 0x23, 0xEA, 0xD0, 0xD3, 0xCC, 0xB9, 0x76, 0x59, 0x5C, 0xF5, 0x49, 0xA6, 0xAB, 0x87, 0xE9, 0xB2, 0xAD, 0xE2, 0x96, 0x31, 0x11, - 0xBE, 0xCC, 0xFC, 0x10, 0x02, 0xAE, 0xBF, 0xCC, 0xC5, 0x77, 0x11, 0xC4, 0x87, 0x33, 0x8D, 0xDB, 0xE7, 0x58, 0x53, 0x6A, 0x97, 0x80, 0x1B, 0x64, - 0xCC, 0xAB, 0x62, 0x3A, 0xA5, 0x38, 0x5B, 0x73, 0x96, 0xE5, 0x72, 0x03, 0xC0, 0xD8, 0xFE, 0xE0, 0xA4, 0x79, 0xF8, 0xBE, 0x8E, 0x8A, 0xA1, 0x37, - 0x8E, 0xFB, 0x86, 0x6E, 0x1A, 0x65, 0x0D, 0xA7, 0x1B, 0xE1, 0x16, 0x8D, 0xC1, 0x71, 0x02, 0x42, 0x76, 0xBD, 0xB9, 0xD8, 0x6B, 0x3C, 0x42, 0x72, - 0x6F, 0xDF, 0x20, 0xBE, 0x31, 0xAA, 0x1D, 0x6B, 0xD3, 0xBF, 0xCA, 0xB3, 0x00, 0x8A, 0x30, 0x04, 0x45, 0x06, 0x92, 0x87, 0x9A, 0x75, 0x4E, 0x1C, - 0x63, 0xAC, 0xEA, 0x59, 0x6C, 0x81, 0x49, 0xE7, 0x5D, 0xE3, 0x9E, 0x8C, 0xD0, 0x95, 0x7B, 0x6F, 0x9A, 0xA0, 0xE5, 0xDE, 0x47, 0x33, 0xDD, 0x64, - 0xA6, 0xD5, 0xDC, 0xB4, 0xBF, 0xAC, 0x7B, 0x52, 0xDD, 0x5A, 0xCD, 0xD1, 0xD6, 0x12, 0xB5, 0x0C, 0xA3, 0x9C, 0x92, 0x6B, 0x44, 0xD6, 0x23, 0xB8, - 0x01, 0xF8, 0x0C, 0xBF, 0x0B, 0xF8, 0xD3, 0x49, 0x84, 0x9E, 0xA8, 0x23, 0x33, 0xD9, 0x75, 0x5C, 0x7F, 0x7A, 0x98, 0x91, 0xFE, 0x26, 0xF6, 0xE4, - 0x0B, 0x53, 0x8C, 0x07, 0x13, 0x76, 0xFA, 0x67, 0x5B, 0xFE, 0x88, 0x8C, 0x7B, 0xDE, 0xBE, 0x1F, 0x6D, 0x1D, 0x60, 0xE9, 0x09, 0xC0, 0x4C, 0x22, - 0xB7, 0x0D, 0x62, 0x8E, 0xE6, 0x8D, 0x56, 0xDB, 0x38, 0x8F, 0x9E, 0x13, 0x55, 0xB3, 0xAD, 0xB1, 0x7A, 0x41, 0x99, 0x41, 0xCB, 0x0E, 0xF8, 0x6D, - 0x23, 0xC2, 0x6B, 0x4E, 0x71, 0x2A, 0xBF, 0x18, 0xF1, 0x6F, 0x07, 0xD7, 0x00, 0x86, 0xB0, 0xAB, 0x07, 0x23, 0xEA, 0x7A, 0xFA, 0x14, 0xD0, 0x19, - 0x81, 0x1E, 0x7B, 0x26, 0xA0, 0x91, 0xAD, 0x9E, 0x98, 0xA7, 0x62, 0x8F, 0x1E, 0x78, 0x80, 0x3D, 0x79, 0xBC, 0xE6, 0x2E, 0xD5, 0x31, 0x2C, 0xD2, - 0x0D, 0x4A, 0x18, 0x82, 0x7B, 0x8C, 0x7C, 0x8D, 0x0D, 0xC8, 0xD1, 0xD0, 0x01, 0xC7, 0x65, 0xF7, 0x8C, 0xBA, 0xA4, 0xBA, 0xB2, 0x5A, 0x74, 0x61, - 0xF0, 0x01, 0x6D, 0x38, 0x08, 0x9F, 0x2C, 0x9C, 0x73, 0x66, 0x4C, 0x7C, 0x8E, 0xCF, 0xE0, 0x9E, 0x6B, 0x1A, 0x07, 0x64, 0x24, 0x1D, 0xE8, 0x58, - 0xF5, 0x10, 0x4C, 0x76, 0x1A, 0x5E, 0xE5, 0x7F, 0xAF, 0x9E, 0x3A, 0x82, 0x70, 0x37, 0x1E, 0x39, 0x00, 0x22, 0xA0, 0xE2, 0x78, 0xC2, 0xD7, 0x05, - 0x9E, 0x95, 0x9A, 0x18, 0x8E, 0x17, 0x73, 0x48, 0x1E, 0x21, 0x4C, 0x0D, 0xF5, 0x59, 0x54, 0x39, 0xA7, 0xE0, 0xDD, 0x0A, 0x4F, 0x37, 0x92, 0xF2, - 0x76, 0x85, 0xB0, 0xA3, 0xF1, 0x38, 0xE9, 0xF5, 0xD4, 0xE6, 0xFA, 0x76, 0xEB, 0x3F, 0xFC, 0xEC, 0xE6, 0x52, 0x36, 0x32, 0x00, 0xB8, 0xB7, 0xFF, - 0x88, 0x1F, 0x25, 0x36, 0xB1, 0x38, 0x28, 0x82, 0xE5, 0x90, 0x99, 0xFA, 0x2D, 0xEA, 0xB9, 0x50, 0xA6, 0x1F, 0xA3, 0x5A, 0xC4, 0x28, 0x22, 0xFC, - 0x51, 0x7D, 0x98, 0x93, 0xA3, 0x11, 0xC9, 0x80, 0xC6, 0xE7, 0xF4, 0x32, 0xB6, 0x60, 0x89, 0x39, 0x9B, 0xCE, 0xCD, 0xBA, 0x1D, 0xDF, 0x56, 0x70, - 0xF5, 0xE9, 0xB9, 0x0D, 0xCD, 0x86, 0x19, 0x6D, 0x8D, 0x5C, 0x43, 0x04, 0x4B, 0x38, 0xD9, 0xB7, 0x0A, 0x89, 0xE7, 0x66, 0x03, 0x08, 0xEA, 0x63, - 0xFC, 0x81, 0x2D, 0xC1, 0x96, 0x2D, 0xFB, 0x11, 0x46, 0xB3, 0x0D, 0xE1, 0x5A, 0x21, 0xCA, 0x63, 0xC6, 0x6F, 0x93, 0x70, 0x3C, 0xD2, 0x24, 0xCC, - 0x42, 0xFC, 0xC0, 0x01, 0x7B, 0xBC, 0xE6, 0xB1, 0xE2, 0x5D, 0x17, 0xC0, 0xAA, 0x95, 0x61, 0x45, 0xCD, 0x32, 0xC3, 0x75, 0x9B, 0x38, 0x99, 0x89, - 0x47, 0x58, 0x2F, 0x37, 0xB3, 0xED, 0xB6, 0x62, 0xD2, 0xF3, 0x8D, 0x1A, 0xC5, 0xBB, 0x4D, 0x51, 0xCE, 0xAB, 0xE1, 0x80, 0xBD, 0x9F, 0xEB, 0x21, - 0x83, 0xD1, 0xFD, 0x62, 0x71, 0x2C, 0x86, 0xF4, 0x9B, 0x81, 0x4D, 0x37, 0x99, 0x70, 0x26, 0xE2, 0xBE, 0xDA, 0x98, 0x3E, 0x38, 0x72, 0x54, 0x70, - 0x67, 0x28, 0x71, 0x32, 0xCC, 0xF3, 0xEA, 0x79, 0x1A, 0x8B, 0xE9, 0x56, 0x51, 0xFE, 0x98, 0x45, 0x86, 0x29, 0x04, 0x26, 0x5B, 0xBF, 0x83, 0xB7, - 0x69, 0xB2, 0xB0, 0x41, 0xDE, 0x5F, 0x53, 0x16, 0x26, 0x93, 0xB1, 0x0D, 0x83, 0x7E, 0x5D, 0x43, 0xCD, 0x63, 0x77, 0x96, 0xB8, 0xD7, 0x9E, 0x88, - 0xF7, 0x0D, 0x8B, 0x03, 0xBA, 0x76, 0x84, 0x4E, 0x3B, 0x33, 0xD1, 0x99, 0x35, 0x1E, 0xED, 0xFB, 0x4E, 0xB0, 0xE2, 0x1C, 0x5C, 0x87, 0x34, 0x0B, - 0x95, 0xD1, 0xE3, 0xBE, 0xB3, 0xBE, 0x2C, 0x8A, 0xAA, 0xE5, 0xE2, 0x59, 0x5E, 0x45, 0x33, 0xD3, 0x65, 0x86, 0x91, 0xDB, 0x52, 0x59, 0xAB, 0x84, - 0xC7, 0x69, 0x97, 0x85, 0xE1, 0x8E, 0x0D, 0xAA, 0xB1, 0x09, 0x23, 0xE7, 0xED, 0x8A, 0xB2, 0xE6, 0x5E, 0x19, 0x51, 0x46, 0x24, 0x2A, 0x4B, 0xF8, - 0x8B, 0x41, 0x7E, 0xD1, 0xC5, 0x72, 0x6C, 0xCD, 0x65, 0x0E, 0x07, 0x6C, 0x44, 0xCF, 0x5E, 0x75, 0x52, 0xF0, 0xD9, 0xEE, 0x3A, 0x36, 0x06, 0xD4, - 0xAB, 0xA8, 0x79, 0x9A, 0xB4, 0x11, 0xA1, 0x69, 0xA6, 0xD0, 0x6F, 0x2E, 0x4F, 0x0A, 0x02, 0x41, 0x37, 0x28, 0x56, 0x68, 0x17, 0xA0, 0x40, 0x8B, - 0x96, 0x25, 0x58, 0xD1, 0x96, 0xF4, 0xBA, 0xFC, 0xC4, 0x8F, 0xF8, 0x9E, 0xBC, 0xA0, 0xAF, 0x69, 0x75, 0x47, 0x85, 0xB9, 0xFB, 0x87, 0xBF, 0x16, - 0x28, 0x69, 0xB4, 0x57, 0x44, 0xAB, 0xB3, 0x63, 0x16, 0x75, 0x2F, 0x39, 0xB4, 0x26, 0x70, 0x03, 0xD7, 0xAE, 0xB5, 0x2E, 0x9B, 0x79, 0xC8, 0x48, - 0x9B, 0x8D, 0x03, 0x91, 0x6D, 0xE9, 0x42, 0x58, 0x36, 0x8A, 0x73, 0xEC, 0xF6, 0xB6, 0x4C, 0x72, 0x0C, 0x1C, 0x8C, 0x71, 0x54, 0xDA, 0xA5, 0x5A, - 0x53, 0xAE, 0x58, 0xF7, 0xE6, 0x6C, 0x36, 0x8B, 0x35, 0x49, 0x70, 0xB0, 0xA1, 0x9A, 0x2D, 0x12, 0x39, 0xD9, 0x7A, 0x0D, 0x49, 0x58, 0xA6, 0xD6, - 0xF7, 0x6A, 0x15, 0x13, 0x12, 0xB7, 0x39, 0x45, 0xAE, 0x39, 0xAE, 0xAF, 0x2F, 0x11, 0x31, 0x48, 0x23, 0x8E, 0x4E, 0x32, 0x4E, 0xA1, 0xAE, 0xB4, - 0x5D, 0xD1, 0xF9, 0xD7, 0x23, 0xEC, 0x36, 0x8C, 0x0E, 0x72, 0x0F, 0x8B, 0x2E, 0x5E, 0x64, 0x39, 0x46, 0xDE, 0xB1, 0x12, 0xEA, 0x91, 0x4E, 0xD1, - 0x63, 0xE8, 0xFB, 0x42, 0x01, 0x7A, 0xE5, 0xF8, 0xFE, 0xC0, 0xD8, 0x0C, 0xFA, 0xE6, 0x97, 0x85, 0x84, 0xD9, 0x0B, 0xB9, 0xFC, 0x8B, 0x1A, 0xDA, - 0x0C, 0xA0, 0x43, 0xD6, 0xF9, 0x5B, 0x6A, 0x06, 0xB7, 0x30, 0x07, 0xA5, 0x1C, 0x60, 0xB9, 0xA5, 0x83, 0x3E, 0xC7, 0x67, 0xFD, 0xAA, 0x27, 0xD0, - 0x5E, 0x8A, 0xFF, 0x8C, 0xB4, 0xCD, 0xE4, 0x6B, 0x5F, 0x70, 0x47, 0xA4, 0x40, 0xD7, 0x50, 0xEF, 0x05, 0x9D, 0x8B, 0xB5, 0x24, 0x58, 0x2D, 0xFE, - 0x8F, 0x02, 0x14, 0xAC, 0x62, 0xE8, 0x81, 0xF3, 0x35, 0xD5, 0x3D, 0x81, 0x91, 0x9D, 0x5D, 0x3C, 0xA1, 0xF1, 0x35, 0x81, 0x8E, 0xC6, 0xEA, 0xFB, - 0xD2, 0x74, 0x46, 0x13, 0x7B, 0x67, 0xD7, 0x8B, 0x0B, 0x70, 0x57, 0xBD, 0x19, 0xD4, 0xA9, 0x70, 0x45, 0xED, 0xE2, 0x3A, 0x8B, 0x8B, 0x75, 0x6E, - 0x37, 0x44, 0xC2, 0xD0, 0x9A, 0x27, 0xC4, 0x45, 0x34, 0x2D, 0x96, 0x3F, 0x0D, 0xC8, 0xF9, 0xE4, 0xF9, 0x7B, 0xDF, 0xD8, 0xAF, 0xD0, 0x89, 0xB3, - 0xE6, 0xAF, 0x31, 0xDB, 0x55, 0xFD, 0xB7, 0xF1, 0x1E, 0x2A, 0x06, 0xAD, 0x5F, 0x87, 0xEC, 0xA4, 0x52, 0x04, 0x33, 0x82, 0x73, 0xAB, 0xDD, 0xD5, - 0xD3, 0x81, 0x3B, 0x75, 0x20, 0xE0, 0x90, 0xAD, 0x57, 0x08, 0xBB, 0x26, 0x68, 0x37, 0xF9, 0xE1, 0xCF, 0xDD, 0x29, 0xF6, 0x9D, 0xE7, 0x67, 0x0E, - 0xBC, 0xC9, 0x61, 0x49, 0x31, 0xFF, 0x10, 0xAA, 0xDD, 0x92, 0x38, 0x14, 0xEF, 0x73, 0xB8, 0x8F, 0x73, 0xAC, 0x5E, 0x39, 0x4D, 0x41, 0xEC, 0x07, - 0x7C, 0xFF, 0xA6, 0x92, 0x45, 0xDB, 0xF4, 0xDA, 0xF2, 0xDF, 0x46, 0xB6, 0x4A, 0x06, 0x78, 0x4B, 0x0C, 0xFB, 0x22, 0x5D, 0x86, 0xC9, 0xDB, 0x00, - 0xF4, 0x9C, 0x38, 0x33, 0x0E, 0x8A, 0x10, 0x4A, 0x26, 0xBD, 0x0F, 0x49, 0xAA, 0x47, 0x6E, 0xC9, 0x4B, 0xE4, 0xA3, 0x35, 0x05, 0xCC, 0x58, 0xE6, - 0x79, 0xE7, 0xAD, 0x63, 0x58, 0xFB, 0x62, 0xF3, 0xFC, 0x5D, 0x61, 0x4E, 0xC9, 0x9E, 0x82, 0xD8, 0x20, 0x10, 0x72, 0x95, 0xE7, 0xD7, 0xA5, 0xE4, - 0xF6, 0x03, 0x67, 0x08, 0x8A, 0xEC, 0xD4, 0xB6, 0x30, 0xDF, 0xB8, 0x9D, 0xD9, 0xC2, 0x97, 0xCE, 0x28, 0x4B, 0xE2, 0xDC, 0xD2, 0x0A, 0xDE, 0xA4, - 0x2E, 0x7D, 0x9C, 0x8F, 0xFD, 0x36, 0xCD, 0xB6, 0x49, 0x3C, 0x76, 0x8B, 0xC8, 0xD0, 0x18, 0x9E, 0xE5, 0xD0, 0x7E, 0x89, 0xC7, 0x50, 0x66, 0x77, - 0x09, 0xAA, 0xAA, 0xCB, 0xFA, 0x11, 0x32, 0xD8, 0x36, 0x45, 0x64, 0xF2, 0xBB, 0x73, 0x16, 0x07, 0x4A, 0x91, 0x6F, 0xB2, 0xAF, 0x57, 0x48, 0xCF, - 0x36, 0xFD, 0x94, 0x4F, 0x06, 0xE2, 0x57, 0x0A, 0xB9, 0xEB, 0xC8, 0x5F, 0x1B, 0x80, 0x27, 0x6C, 0x19, 0x0A, 0xA5, 0x6A, 0xE5, 0xC2, 0xE1, 0xD1, - 0x0B, 0xBF, 0x29, 0xE2, 0x63, 0x4A, 0xD1, 0x7B, 0x86, 0xC1, 0x75, 0x57, 0xEF, 0x18, 0xB0, 0xF0, 0xB3, 0x59, 0x80, 0x1C, 0x62, 0xB3, 0xD0, 0x77, - 0x5E, 0xBB, 0x4B, 0xF2, 0x78, 0x98, 0x45, 0xE7, 0x5E, 0x87, 0xAE, 0xF6, 0xCC, 0x39, 0xEB, 0x17, 0x82, 0x11, 0x4F, 0xE9, 0x9E, 0xDB, 0xD7, 0x99, - 0x29, 0x43, 0x3E, 0xF1, 0xDA, 0x0C, 0xB8, 0xAD, 0xB7, 0x46, 0x36, 0xBA, 0xA5, 0x27, 0xDE, 0x3D, 0xAB, 0xB9, 0x6E, 0x42, 0x9B, 0xF5, 0x9B, 0x46, - 0x43, 0xD8, 0x6C, 0xF1, 0x1E, 0xC3, 0x01, 0x43, 0x1B, 0xAF, 0xDB, 0x47, 0x4C, 0x7E, 0x1F, 0x75, 0x4B, 0x1F, 0x7A, 0xE5, 0x86, 0x81, 0x6E, 0xB1, - 0x67, 0x15, 0xFA, 0x8A, 0x46, 0x63, 0x28, 0xA3, 0x71, 0x86, 0x6B, 0x1E, 0xAF, 0x08, 0x1B, 0xFA, 0xAA, 0xFF, 0xB0, 0x86, 0xC1, 0xBF, 0x1C, 0x58, - 0xDA, 0x80, 0x43, 0xFC, 0x0F, 0x6D, 0x6E, 0xDF, 0x18, 0xF8, 0x9E, 0xBC, 0xDB, 0xCE, 0x6D, 0x0D, 0x44, 0x8E, 0xCD, 0x39, 0xCE, 0xB1, 0x34, 0xCB, - 0x62, 0x42, 0x4C, 0xD3, 0x20, 0x0B, 0xB5, 0x90, 0x89, 0x89, 0xA1, 0xAC, 0x50, 0x96, 0x55, 0xD0, 0x0E, 0x0A, 0xE4, 0x9C, 0x86, 0x6F, 0x00, 0x48, - 0x29, 0x63, 0xC6, 0x0A, 0xDE, 0xD4, 0x0B, 0x5D, 0xCA, 0xEE, 0x02, 0x99, 0x55, 0xEC, 0x8D, 0xF9, 0x98, 0x5D, 0x48, 0x6E, 0xEB, 0x05, 0x68, 0x7E, - 0x11, 0xCC, 0x41, 0xFD, 0x08, 0x46, 0x87, 0x06, 0x17, 0xB2, 0x3A, 0x7C, 0xD1, 0x90, 0x07, 0x42, 0x53, 0x87, 0x0F, 0x1A, 0xD6, 0x40, 0xF8, 0xE8, - 0xB0, 0x8F, 0x43, 0x91, 0x0B, 0x51, 0x1C, 0xF6, 0xC1, 0xB0, 0x62, 0x21, 0xDE, 0xC1, 0x4E, 0xD6, 0xA7, 0xEE, 0xFC, 0x56, 0xD9, 0x1E, 0x34, 0xC3, - 0x7A, 0x0C, 0x28, 0x75, 0xC1, 0xC9, 0xF8, 0x01, 0x32, 0xE1, 0xE9, 0x71, 0xFB, 0xF6, 0x3F, 0xD5, 0xC1, 0xE5, 0xC7, 0x3F, 0x8D, 0xC7, 0xD8, 0x2F, - 0x14, 0x1E, 0xAD, 0x8C, 0x5C, 0xD4, 0x44, 0x72, 0xA2, 0x4D, 0xF5, 0x4D, 0x17, 0xEC, 0x82, 0x06, 0xDC, 0x62, 0xF2, 0xFC, 0x3D, 0xDC, 0x9E, 0x39, - 0x8F, 0x72, 0x4F, 0x70, 0xA1, 0x55, 0x3E, 0x4A, 0xAD, 0x9D, 0x7D, 0x6B, 0x64, 0xFD, 0x9C, 0x18, 0xA1, 0x34, 0x8B, 0x64, 0x8B, 0xD2, 0xFB, 0x4D, - 0xDA, 0xF0, 0x16, 0xE9, 0x8E, 0x4E, 0x82, 0x89, 0x7D, 0x9A, 0xD7, 0xC7, 0xB1, 0x90, 0x7B, 0xCD, 0xA9, 0x2B, 0x3D, 0xAB, 0xC1, 0x1C, 0xDD, 0xA3, - 0x39, 0x8F, 0x4C, 0x12, 0xE5, 0xE7, 0x72, 0x97, 0xD6, 0xFC, 0xD2, 0x53, 0xC4, 0x33, 0xB2, 0x92, 0x5A, 0xF4, 0x89, 0x2B, 0xCE, 0xB5, 0xC2, 0x9B, - 0x13, 0xE8, 0x66, 0xBD, 0xA6, 0x28, 0x31, 0x9C, 0xFE, 0x91, 0x07, 0x51, 0xA8, 0xA3, 0x42, 0xC5, 0xAE, 0x91, 0xD0, 0xCE, 0xDB, 0xF0, 0xE3, 0xB3, - 0x72, 0x18, 0x4D, 0xC7, 0x43, 0x4F, 0xE6, 0xA1, 0x4A, 0x63, 0x82, 0x6A, 0x36, 0x13, 0x09, 0x30, 0xFF, 0x60, 0x55, 0x3A, 0x93, 0xF7, 0x5B, 0xED, - 0x22, 0x6E, 0x93, 0x2F, 0x4A, 0xC2, 0xDA, 0xD9, 0xD6, 0xED, 0xC5, 0x87, 0x5C, 0x71, 0xAB, 0x97, 0x49, 0xC1, 0xE9, 0x94, 0x30, 0x84, 0x09, 0x67, - 0x87, 0x2F, 0x62, 0xF7, 0x65, 0xC6, 0x39, 0x4B, 0x3E, 0xF3, 0xD3, 0xB3, 0xD0, 0xA2, 0x7C, 0x9F, 0x72, 0x0D, 0x32, 0x4E, 0xCD, 0xCD, 0xB0, 0x67, - 0x13, 0x4A, 0xED, 0x84, 0x89, 0x6E, 0xEA, 0x90, 0x28, 0x3E, 0xC6, 0x06, 0x89, 0xCB, 0xAC, 0x5E, 0x1F, 0xC7, 0xC6, 0x15, 0xB5, 0xBB, 0x87, 0xA3, - 0x3C, 0xD5, 0x67, 0x87, 0x01, 0x99, 0xDD, 0x1B, 0xAC, 0xA9, 0xA2, 0xC1, 0xC2, 0xCE, 0xF4, 0xA6, 0x33, 0x02, 0x37, 0xDA, 0x1B, 0x0B, 0x43, 0x2E, - 0x5B, 0x35, 0x80, 0x21, 0x11, 0x38, 0xC4, 0xF5, 0x05, 0x64, 0xB9, 0xE9, 0x2B, 0xD2, 0xF1, 0x8D, 0x71, 0x2E, 0xEF, 0x6E, 0xA8, 0xE9, 0x10, 0x1B, - 0x75, 0x87, 0x26, 0x51, 0x40, 0xD1, 0x82, 0x93, 0x86, 0xFE, 0xD2, 0x50, 0x2F, 0xDA, 0xFE, 0x29, 0xA8, 0x37, 0x65, 0x92, 0xCC, 0x7D, 0xBD, 0x1B, - 0x35, 0xF9, 0xAC, 0xEB, 0xCE, 0x8D, 0xFF, 0xB8, 0xF3, 0xBD, 0x30, 0xFF, 0x92, 0xFA, 0x72, 0x85, 0x83, 0xDC, 0xF3, 0x34, 0x58, 0x09, 0x65, 0xC7, - 0x37, 0xDD, 0x80, 0xC9, 0xD3, 0x9B, 0x9B, 0x07, 0x41, 0xDB, 0xE8, 0xCE, 0xE5, 0xE7, 0xE7, 0x80, 0x77, 0x46, 0xB5, 0x43, 0xC0, 0x39, 0x0A, 0xFF, - 0xB4, 0x66, 0x28, 0xF9, 0xB7, 0xAC, 0xB9, 0xE5, 0x1F, 0x29, 0x1B, 0x84, 0x63, 0x06, 0xF7, 0x11, 0x7A, 0x99, 0x30, 0x57, 0x74, 0xDF, 0x3A, 0x1D, - 0xE8, 0x63, 0x63, 0x1D, 0xD8, 0x76, 0x1C, 0x3D, 0xCC, 0xB2, 0x64, 0xF9, 0x4C, 0xD0, 0x37, 0x18, 0x10, 0x4C, 0x28, 0x1C, 0x43, 0x07, 0xA4, 0x6D, - 0x31, 0x45, 0x46, 0xA6, 0xA4, 0x58, 0x25, 0x6F, 0x53, 0x09, 0xAE, 0xB1, 0x44, 0xE6, 0xFC, 0x2D, 0x81, 0x78, 0xAE, 0xD0, 0x5F, 0x9D, 0x55, 0x84, - 0x3E, 0xD1, 0x3D, 0xD0, 0xBA, 0xC6, 0xDD, 0xE5, 0x2A, 0x1B, 0x79, 0xED, 0x4F, 0x93, 0xDF, 0xDE, 0x8E, 0xF6, 0x25, 0xAF, 0x85, 0xC6, 0xC8, 0x04, - 0x72, 0x8F, 0x2F, 0xB7, 0x6A, 0x5B, 0x5B, 0xFD, 0x96, 0xB1, 0xED, 0x1C, 0x9A, 0xFE, 0x02, 0x3E, 0x8E, 0xEC, 0x06, 0x9E, 0x3E, 0x73, 0x20, 0x45, - 0xFB, 0x4D, 0x0D, 0xB4, 0x95, 0xF9, 0xC1, 0x14, 0xEF, 0xC6, 0x89, 0xE8, 0xB7, 0x8D, 0xD0, 0xED, 0x06, 0x88, 0x38, 0x6D, 0x48, 0xB6, 0x64, 0xD5, - 0x4F, 0x19, 0xC3, 0x65, 0xAF, 0x20, 0x79, 0xA6, 0x70, 0xD3, 0x2E, 0x98, 0xCE, 0x39, 0x45, 0x3F, 0xEF, 0x3C, 0xA9, 0x87, 0xF0, 0xB8, 0x8B, 0xD7, - 0x3D, 0x7A, 0x69, 0x0E, 0x43, 0xB5, 0x54, 0xDB, 0x8B, 0xDD, 0xCC, 0x59, 0xB8, 0x05, 0x9A, 0x38, 0xF5, 0xC2, 0x14, 0x10, 0xC8, 0xCE, 0xFE, 0xEF, - 0x9E, 0x6C, 0x6C, 0xE5, 0xD7, 0x7B, 0x8F, 0x7B, 0xEF, 0x21, 0xAE, 0xB7, 0x1B, 0x79, 0x76, 0x89, 0x50, 0xB9, 0x32, 0xB8, 0xBF, 0x97, 0x3C, 0x4F, - 0xEC, 0x9F, 0xEB, 0xB2, 0x3C, 0xD1, 0x13, 0x42, 0xDE, 0x9E, 0xD0, 0x22, 0xAD, 0x93, 0xBB, 0xE5, 0xE8, 0x14, 0xD5, 0x0B, 0xEF, 0xA9, 0xE5, 0xE7, - 0xDA, 0x81, 0xA2, 0xF3, 0xF1, 0xE7, 0x96, 0xC5, 0x83, 0x10, 0xFA, 0x4E, 0x23, 0xCA, 0x88, 0xEB, 0x78, 0x14, 0x6F, 0x78, 0xF7, 0x3E, 0x98, 0xE4, - 0xA6, 0x8E, 0xFE, 0x52, 0xFC, 0x52, 0xD3, 0x96, 0x4E, 0x49, 0x0E, 0x50, 0x22, 0x19, 0x47, 0xBD, 0x8F, 0x5D, 0x6E, 0x78, 0x31, 0x34, 0x69, 0xFC, - 0x0A, 0x86, 0xF1, 0x19, 0x38, 0x33, 0xE4, 0x10, 0x7A, 0x4C, 0x3D, 0x6B, 0xBC, 0x20, 0x72, 0x74, 0x72, 0x11, 0xD6, 0x8C, 0x00, 0x81, 0x9E, 0x03, - 0x5B, 0xFD, 0x1B, 0x63, 0x86, 0xDE, 0xCE, 0x38, 0x60, 0x1C, 0xE0, 0xD4, 0x2D, 0x0B, 0xBA, 0xCE, 0x12, 0xF4, 0x65, 0x35, 0x58, 0xBF, 0xCE, 0xA4, - 0xD7, 0x4C, 0x79, 0x83, 0xD1, 0xAC, 0x4F, 0xED, 0xE2, 0x57, 0xB7, 0xE5, 0x92, 0x8D, 0x49, 0x3C, 0x8D, 0x82, 0x8A, 0x93, 0xAF, 0xB8, 0x79, 0x24, - 0x74, 0xF5, 0x79, 0xEB, 0x72, 0x97, 0xAC, 0xB7, 0x51, 0x67, 0x55, 0xED, 0x9C, 0x22, 0x77, 0xCC, 0x02, 0x13, 0xBC, 0x3F, 0x09, 0xF4, 0xB4, 0xBE, - 0xA9, 0x16, 0xC1, 0xC6, 0xF3, 0x3A, 0x17, 0x3B, 0x13, 0x3E, 0xBA, 0xDF, 0x75, 0xA0, 0x81, 0xDE, 0x14, 0x4E, 0x8B, 0x2E, 0x6D, 0x76, 0x49, 0x1B, - 0x4C, 0xC1, 0xEE, 0xC0, 0xA3, 0x89, 0xAA, 0xAB, 0x36, 0xBF, 0x55, 0xD0, 0x7B, 0xEC, 0xA5, 0x0A, 0xA7, 0x16, 0x17, 0x5A, 0xB4, 0x53, 0x2C, 0x64, - 0x2D, 0x7E, 0xA3, 0x7C, 0xDE, 0x80, 0x4B, 0x81, 0xE9, 0xAE, 0x7A, 0x1C, 0x7D, 0xBD, 0x57, 0xB6, 0x60, 0xB6, 0x3E, 0xBB, 0xFD, 0x7F, 0xC9, 0xCE, - 0xB5, 0x80, 0xF2, 0x0E, 0x5E, 0x6F, 0x9C, 0x13, 0x78, 0x32, 0xB7, 0x70, 0x25, 0xE4, 0x43, 0xC8, 0x9C, 0xD9, 0x80, 0x9E, 0x6B, 0xB9, 0xDB, 0x80, - 0x92, 0x9D, 0x63, 0x51, 0xAE, 0xF2, 0x33, 0x27, 0xD3, 0xDB, 0xF7, 0x1D, 0x5E, 0x08, 0xFC, 0x25, 0x6B, 0xAC, 0x4B, 0x75, 0xC5, 0xB6, 0xDD, 0x63, - 0x4E, 0x4C, 0xE2, 0x60, 0xEC, 0xE2, 0xE6, 0xAC, 0xDD, 0xE3, 0xCB, 0x9C, 0x31, 0x77, 0x93, 0x10, 0xE2, 0xF9, 0x34, 0xA7, 0xA3, 0x65, 0x48, 0xAC, - 0xF1, 0x22, 0x80, 0xEA, 0x49, 0x83, 0x15, 0x86, 0xCB, 0x2A, 0x36, 0x40, 0xE8, 0x7F, 0x1D, 0x94, 0x00, 0x6C, 0x89, 0x7F, 0x4D, 0xB3, 0xC3, 0x09, - 0x6E, 0xD7, 0xC9, 0x4D, 0xB1, 0x73, 0xE8, 0x4F, 0x65, 0x4B, 0x1E, 0x01, 0x51, 0xB4, 0x36, 0x11, 0xA1, 0x42, 0xD6, 0xF4, 0x6F, 0xE1, 0x8D, 0x8B, - 0x3E, 0x28, 0xD7, 0xA5, 0x7C, 0x60, 0xC1, 0x41, 0x6F, 0x9A, 0xC8, 0xD0, 0xB1, 0xAA, 0x02, 0x49, 0x4A, 0xE9, 0xB6, 0x32, 0x75, 0xF2, 0xD0, 0x6A, - 0x63, 0x5A, 0xD2, 0xFD, 0xDA, 0x78, 0x63, 0x94, 0x15, 0x52, 0xB6, 0xE7, 0x2E, 0x6B, 0xDA, 0xE3, 0x45, 0x29, 0x9D, 0x7B, 0xF6, 0xBA, 0x01, 0x0B, - 0x0B, 0x97, 0xBC, 0x68, 0x73, 0x3F, 0x6F, 0x30, 0xC6, 0x04, 0x2E, 0x88, 0x43, 0xB7, 0x6D, 0xB2, 0x2E, 0x75, 0x1A, 0x69, 0x81, 0x5F, 0xBD, 0xA7, - 0xEA, 0x08, 0x05, 0x5F, 0xE2, 0xDD, 0x7B, 0xDF, 0x3A, 0x38, 0x53, 0xC4, 0xED, 0xB2, 0x87, 0x79, 0x4F, 0xF7, 0xED, 0xAA, 0xED, 0x41, 0x8A, 0x9D, - 0x20, 0x8A, 0x66, 0x18, 0x16, 0x28, 0xD5, 0x57, 0x40, 0xB7, 0x6B, 0xDD, 0xAE, 0x00, 0x70, 0x78, 0xB9, 0xCC, 0x14, 0x22, 0xFB, 0x62, 0xEA, 0x85, - 0xF6, 0x46, 0x12, 0xEB, 0x42, 0x53, 0x9F, 0x74, 0x6B, 0x60, 0x99, 0x4A, 0xF5, 0xD6, 0x6A, 0x85, 0xD6, 0x07, 0xAC, 0x8A, 0x50, 0xBF, 0xE7, 0xF9, - 0x8D, 0x09, 0x5C, 0x2F, 0x74, 0xBD, 0x35, 0xEB, 0x0C, 0xCC, 0xB6, 0xAD, 0x03, 0xE8, 0x8E, 0xE2, 0x62, 0xAD, 0x78, 0x34, 0xA9, 0x9A, 0x11, 0xCA, - 0xA4, 0x62, 0x18, 0x4C, 0xBC, 0x79, 0x56, 0x4B, 0xF3, 0x2E, 0x2F, 0xE3, 0x12, 0xF8, 0x47, 0x0C, 0x36, 0x3A, 0x68, 0x1D, 0xDB, 0x33, 0x90, 0xBC, - 0xAC, 0x66, 0x08, 0x8F, 0x39, 0x0E, 0x1D, 0x79, 0xF0, 0x5D, 0xCF, 0xA4, 0xF9, 0xEC, 0xC9, 0x9C, 0xBF, 0xEB, 0x59, 0xD1, 0x8A, 0xA7, 0xC6, 0xF5, - 0x74, 0xF0, 0xB6, 0x0F, 0x8F, 0x82, 0x64, 0xCD, 0x66, 0xA7, 0xD0, 0x4E, 0xBD, 0x76, 0x5E, 0x99, 0x24, 0x83, 0x8D, 0x2B, 0x69, 0x64, 0xD4, 0x56, - 0x6B, 0x81, 0x6E, 0x6B, 0xC1, 0xB9, 0xAE, 0x79, 0xFF, 0x71, 0xE6, 0xE8, 0x96, 0xA2, 0x1A, 0x48, 0xF7, 0x6C, 0xF9, 0x92, 0x17, 0x50, 0x43, 0x99, - 0x60, 0x47, 0x78, 0x6C, 0x62, 0x66, 0xCA, 0x98, 0xC0, 0xE2, 0x6E, 0x0F, 0xAD, 0x8C, 0x41, 0x80, 0x1A, 0x80, 0xD8, 0x2E, 0x99, 0x3D, 0x28, 0x19, - 0x00, 0xD3, 0xE3, 0x4A, 0xE9, 0x51, 0x87, 0x20, 0x54, 0xDE, 0xC8, 0xB6, 0xF9, 0x67, 0xBD, 0x86, 0x4F, 0x04, 0x0F, 0xBF, 0x6D, 0x71, 0xD5, 0x67, - 0xD4, 0xE7, 0xAE, 0xF7, 0xA8, 0x2C, 0xB1, 0x13, 0x4E, 0xEE, 0xF7, 0x09, 0xCF, 0xE6, 0xE9, 0x0C, 0xF4, 0x7A, 0x26, 0xB1, 0x21, 0x77, 0x91, 0xD1, - 0x45, 0xD7, 0x10, 0xC9, 0x0C, 0x8D, 0xF9, 0xB2, 0xAB, 0x85, 0x1D, 0x79, 0xD0, 0x95, 0x63, 0x36, 0x28, 0xE7, 0xD6, 0x9A, 0x23, 0x34, 0xAD, 0x63, - 0xF4, 0x2F, 0xCA, 0x29, 0x42, 0xEE, 0x37, 0x3B, 0x44, 0x00, 0xFD, 0x60, 0x66, 0x88, 0x4E, 0x43, 0x75, 0xBE, 0x65, 0x46, 0xB2, 0xA8, 0xA0, 0x9E, - 0x17, 0x24, 0xCB, 0x12, 0xA1, 0x01, 0x49, 0xF4, 0xE5, 0x1C, 0x9A, 0x2B, 0x2B, 0x5A, 0x53, 0x6F, 0x76, 0xE8, 0x6E, 0xC7, 0x5A, 0x1D, 0x4F, 0x69, - 0xE9, 0xE2, 0x76, 0x84, 0xCE, 0x1F, 0x2E, 0x90, 0x5E, 0xAA, 0x93, 0xCE, 0xA4, 0x3A, 0x8E, 0x01, 0xA5, 0x02, 0x0A, 0x0E, 0x55, 0x2A, 0x99, 0x19, - 0x0F, 0x5F, 0xE7, 0x2F, 0xB8, 0x20, 0x21, 0x9D, 0xD2, 0xAC, 0x6D, 0x21, 0x3F, 0x57, 0xB7, 0x16, 0x9D, 0xF4, 0x59, 0x2F, 0xDC, 0x33, 0xA6, 0x05, - 0x1C, 0xB6, 0xF7, 0xD3, 0x0F, 0x11, 0x0E, 0xD8, 0xFF, 0xBC, 0x44, 0x8B, 0xE9, 0xB6, 0xCE, 0xC1, 0x49, 0x12, 0x8A, 0xF3, 0x4E, 0xD2, 0x4B, 0x62, - 0x02, 0xA3, 0x61, 0xEA, 0x5C, 0x01, 0x52, 0x29, 0x03, 0xEB, 0x76, 0xF2, 0xCB, 0x3E, 0xB5, 0xCC, 0x56, 0xB9, 0xD1, 0x73, 0xF7, 0x41, 0x8C, 0x7C, - 0x97, 0xB5, 0x6F, 0x2F, 0x02, 0xD4, 0x7E, 0xF3, 0x61, 0xEA, 0x76, 0xCB, 0x09, 0xED, 0x58, 0xF4, 0xB0, 0x2D, 0xB1, 0x06, 0xFA, 0xAC, 0xA3, 0x7F, - 0x15, 0x86, 0xF6, 0x7A, 0xBD, 0xB4, 0x5B, 0xDA, 0xD7, 0xCD, 0x7E, 0x11, 0xBB, 0x49, 0x4B, 0x3A, 0xE8, 0x26, 0x08, 0x53, 0xEC, 0xE1, 0xB7, 0x2D, - 0xCB, 0xEB, 0x73, 0xBB, 0xBB, 0x00, 0xD2, 0x98, 0x87, 0xA0, 0x6C, 0x64, 0x46, 0xEF, 0x48, 0xB6, 0x45, 0x6B, 0xF4, 0x58, 0x31, 0xAF, 0x1A, 0x98, - 0x0C, 0xFB, 0xF9, 0x1C, 0x73, 0x0C, 0x8D, 0x4F, 0x71, 0x9D, 0x15, 0xD1, 0x61, 0x59, 0x4A, 0x50, 0x8F, 0x43, 0x48, 0xC6, 0x56, 0x3C, 0xEA, 0xA7, - 0x36, 0xF6, 0xBE, 0x5E, 0x80, 0xE8, 0xB6, 0x68, 0x11, 0x5D, 0x76, 0x36, 0xA4, 0x5B, 0x1D, 0x2B, 0x77, 0x8C, 0x66, 0x2A, 0x9A, 0x7C, 0xFD, 0xC5, - 0xC7, 0xFF, 0xB0, 0xCA, 0x58, 0xDC, 0x3C, 0x9D, 0x0F, 0x1B, 0x05, 0x37, 0xE3, 0x8B, 0xD7, 0xED, 0x3E, 0x7B, 0xF3, 0x8A, 0x9E, 0xBF, 0x9F, 0xF9, - 0x44, 0xC8, 0x81, 0x87, 0x7C, 0x54, 0xA4, 0x05, 0x0A, 0x6D, 0x0C, 0xFA, 0xCF, 0xC9, 0xCC, 0x70, 0x59, 0x5F, 0xDF, 0x6F, 0xC5, 0x20, 0xCA, 0x42, - 0xBA, 0xE1, 0xA9, 0xDE, 0x78, 0xD4, 0x54, 0xD0, 0xAE, 0x39, 0xD9, 0xBC, 0x69, 0x22, 0x5D, 0xBC, 0x86, 0x44, 0x36, 0xD6, 0xB8, 0xC4, 0x06, 0x20, - 0xFA, 0x6C, 0x17, 0xA8, 0x88, 0x07, 0x16, 0x00, 0xF1, 0xE6, 0xD9, 0xB4, 0xE3, 0x24, 0x5F, 0x3B, 0x17, 0x2E, 0xEF, 0x34, 0x5C, 0x12, 0xB4, 0x5E, - 0x53, 0xCD, 0xE7, 0xCC, 0xFD, 0x78, 0x66, 0x9D, 0xB6, 0x8C, 0x30, 0x0D, 0x3B, 0x73, 0xE5, 0x5A, 0x2D, 0xC9, 0x2F, 0xD8, 0xFA, 0xD3, 0x62, 0x81, - 0xD0, 0x9A, 0xC1, 0xF4, 0x64, 0xCC, 0x34, 0x19, 0x3A, 0xFD, 0x69, 0x2D, 0xA8, 0xC3, 0x27, 0x01, 0xF1, 0x19, 0x3D, 0x0E, 0x1C, 0x27, 0x38, 0x42, - 0xE4, 0xBD, 0x7A, 0xBD, 0x3B, 0xDD, 0x44, 0xF7, 0x16, 0x45, 0x6D, 0xA7, 0x6D, 0x50, 0xDD, 0xF4, 0x2D, 0x86, 0x8A, 0xE0, 0x33, 0xC5, 0x3C, 0x16, - 0x31, 0x45, 0xA2, 0x5F, 0x59, 0x57, 0x12, 0x2B, 0x52, 0x2D, 0xDE, 0x88, 0x29, 0xAF, 0xB6, 0x69, 0x10, 0x8F, 0xD6, 0xE6, 0x62, 0x0D, 0xBD, 0xDF, - 0xC7, 0xAC, 0x2E, 0xD7, 0x4A, 0xD6, 0xE3, 0xB5, 0xBD, 0x65, 0x66, 0x44, 0x98, 0x10, 0x5D, 0x4F, 0x70, 0x5E, 0xFA, 0x18, 0x3D, 0xBB, 0x01, 0x3A, - 0x9A, 0x9C, 0x73, 0xF8, 0xAF, 0x99, 0x76, 0xD8, 0xB3, 0x4A, 0x3F, 0x09, 0x16, 0x85, 0xD2, 0x0B, 0xD9, 0x24, 0x99, 0x79, 0x49, 0x30, 0x85, 0xC3, - 0xDD, 0xF7, 0xC4, 0x1A, 0xEF, 0x70, 0xE4, 0x37, 0x3C, 0x22, 0xEE, 0x2B, 0x5F, 0x79, 0x2A, 0x5B, 0x58, 0x03, 0x51, 0x5C, 0x2E, 0x0C, 0x72, 0x4C, - 0x2E, 0xB4, 0xCD, 0x16, 0xE6, 0x7E, 0x0F, 0x7D, 0x6E, 0x66, 0x98, 0x6D, 0x6D, 0x1D, 0x22, 0xE1, 0x20, 0x37, 0x6B, 0x7D, 0x0E, 0x01, 0xB3, 0xF5, - 0xB0, 0x05, 0x19, 0x9A, 0xD6, 0xF2, 0x28, 0x3D, 0x3B, 0xCC, 0xC0, 0xF9, 0x8D, 0x7E, 0xCF, 0x4A, 0xB1, 0xD7, 0xBF, 0x26, 0x46, 0x93, 0xC8, 0x9C, - 0xB7, 0x29, 0xD8, 0x38, 0x6F, 0x8F, 0x5F, 0x21, 0x8A, 0xC9, 0xC4, 0x20, 0xE6, 0x9F, 0xB0, 0x1F, 0x9D, 0x83, 0xE5, 0xC6, 0x26, 0xF8, 0xE0, 0xCD, - 0xA8, 0x20, 0xBB, 0x17, 0x32, 0xDD, 0xB1, 0x38, 0x27, 0x73, 0x45, 0xE7, 0x7A, 0xD9, 0xE1, 0x18, 0x2F, 0xE9, 0xA7, 0x9F, 0x6D, 0x69, 0xA8, 0x6E, - 0x83, 0xC6, 0x63, 0x74, 0xC4, 0x22, 0xD0, 0x70, 0x1C, 0x7E, 0x68, 0x9B, 0xE5, 0x25, 0x11, 0xA5, 0xF7, 0x2D, 0xB1, 0x59, 0x54, 0xD3, 0xD8, 0x57, - 0xF0, 0xC9, 0x33, 0x06, 0x9D, 0xBF, 0x78, 0x10, 0x15, 0x87, 0xDB, 0xE7, 0x33, 0x9F, 0xB7, 0xA1, 0x9A, 0x92, 0xEB, 0x7B, 0x8A, 0x2D, 0x1C, 0xCE, - 0x26, 0xC0, 0x04, 0xA1, 0x80, 0xE0, 0x2E, 0x81, 0x64, 0x0B, 0xCA, 0x97, 0xDD, 0x6B, 0x98, 0x0D, 0x68, 0xE9, 0xE5, 0x7D, 0x97, 0x00, 0x8D, 0xE5, - 0xB0, 0xAE, 0xD5, 0x99, 0xC5, 0x3A, 0xD3, 0x00, 0xA5, 0xD4, 0x6E, 0xB0, 0x60, 0xF9, 0xBE, 0x75, 0xB4, 0xB1, 0xDF, 0x88, 0xD6, 0x62, 0x37, 0x86, - 0xBC, 0x99, 0xC6, 0x9D, 0xD1, 0x14, 0x1F, 0x3B, 0x95, 0x5F, 0x71, 0x5C, 0xCD, 0x0E, 0xBC, 0xCB, 0x9B, 0x9E, 0x01, 0x74, 0xC0, 0x8A, 0x26, 0xA7, - 0x41, 0x93, 0xDF, 0x63, 0x4B, 0xD6, 0x4E, 0x25, 0x88, 0x1F, 0xA0, 0xD5, 0x1A, 0x81, 0x6A, 0x94, 0x77, 0x44, 0x9F, 0xA6, 0x2B, 0x54, 0x6B, 0x03, - 0x0D, 0xB2, 0x6D, 0x81, 0x59, 0xE8, 0xF8, 0x06, 0x13, 0x3D, 0xE0, 0xD1, 0xD6, 0x4D, 0x91, 0x18, 0x87, 0x3F, 0x18, 0x53, 0xE6, 0x00, 0xBA, 0xDA, - 0xEE, 0x00, 0x76, 0x79, 0x84, 0x7E, 0x2F, 0x35, 0xEF, 0xA2, 0xF6, 0x3C, 0xC4, 0xEB, 0xF3, 0xCD, 0x46, 0x33, 0x7E, 0xB7, 0xBD, 0x7D, 0x74, 0x87, - 0xE4, 0xBC, 0x5C, 0x8E, 0x0C, 0x18, 0x20, 0x5D, 0x19, 0x1D, 0x2C, 0x2D, 0x45, 0xF8, 0x62, 0x11, 0x90, 0x1D, 0x12, 0x4F, 0xA4, 0xCA, 0xD3, 0xCE, - 0x62, 0x8C, 0x56, 0x61, 0x0D, 0x9F, 0x31, 0x4A, 0x17, 0x39, 0x71, 0x2C, 0x96, 0xE7, 0x3F, 0x68, 0xF4, 0xAB, 0x57, 0x9A, 0x3A, 0xA2, 0x2B, 0x27, - 0xB3, 0xA5, 0xDB, 0x30, 0xB2, 0xBF, 0x89, 0xD0, 0x88, 0xCB, 0x0B, 0x86, 0x83, 0xD4, 0xE0, 0x73, 0x89, 0x98, 0x96, 0xC7, 0x3C, 0x54, 0x23, 0x7B, - 0xA5, 0x0F, 0x62, 0x36, 0x5D, 0xF0, 0x52, 0x6F, 0xE4, 0xF8, 0xC7, 0xA7, 0x31, 0xF1, 0xB1, 0xDE, 0xBA, 0xB6, 0x70, 0xB0, 0xC1, 0xAD, 0x46, 0xEE, - 0x1C, 0x57, 0xD8, 0x02, 0x14, 0x48, 0x49, 0x97, 0x3F, 0x8D, 0x81, 0x78, 0x43, 0x4C, 0x6E, 0xBB, 0x03, 0x50, 0xA9, 0x04, 0x46, 0xBD, 0x66, 0xAB, - 0x68, 0x3D, 0x19, 0x3F, 0x79, 0xC9, 0x0A, 0x6B, 0x7C, 0x67, 0x8D, 0xAC, 0x00, 0x4D, 0xA6, 0x9D, 0xEE, 0x50, 0x2D, 0x34, 0xD6, 0xA9, 0xA7, 0xF5, - 0x06, 0x80, 0x29, 0xEA, 0x97, 0xC9, 0x61, 0x28, 0xF7, 0x1B, 0x1C, 0x4A, 0xDE, 0xE6, 0x6E, 0x79, 0x89, 0x73, 0x8E, 0xE0, 0x96, 0x28, 0x8B, 0x1F, - 0x1F, 0x4C, 0x37, 0xDB, 0xF1, 0x9B, 0x51, 0xEB, 0x1F, 0xB3, 0x88, 0x7D, 0x9F, 0x8E, 0xFD, 0x5C, 0xE1, 0xDF, 0xDF, 0xE5, 0xBF, 0x59, 0x0B, 0x05, - 0x8F, 0x51, 0xE2, 0xA9, 0x79, 0x90, 0xBD, 0xEA, 0x27, 0x5B, 0x7D, 0xE0, 0x18, 0x4C, 0x6F, 0xDC, 0xB6, 0x6F, 0x9D, 0x35, 0x0B, 0xC6, 0x86, 0x52, - 0xC7, 0x36, 0x84, 0x20, 0x8A, 0xAE, 0x94, 0xB7, 0xFF, 0x49, 0xB6, 0x6B, 0xDA, 0xFD, 0xA0, 0x97, 0xD4, 0xB0, 0x68, 0x51, 0xF9, 0x71, 0x60, 0xEE, - 0x9F, 0xD3, 0x5C, 0xF6, 0x4E, 0xDC, 0x6A, 0x62, 0x50, 0x77, 0x26, 0xE9, 0x9A, 0xA1, 0x96, 0xF1, 0x1F, 0xA5, 0xCF, 0xB7, 0xBC, 0xAF, 0x07, 0x08, - 0xD8, 0x5C, 0xC6, 0x46, 0x9B, 0xA6, 0x2B, 0xE5, 0xF6, 0x49, 0x0A, 0x0C, 0xCA, 0x0D, 0xF3, 0xE1, 0xB5, 0xFC, 0x42, 0x07, 0x49, 0xAC, 0x70, 0x9F, - 0x09, 0x4B, 0x23, 0x3E, 0xEE, 0xBC, 0xB5, 0x92, 0x85, 0xCA, 0x53, 0x1F, 0xF0, 0x2C, 0x60, 0xCB, 0x23, 0xFC, 0x0C, 0x7F, 0xB9, 0x3B, 0x59, 0xDB, - 0x74, 0xA3, 0x51, 0x72, 0x85, 0xFC, 0xD5, 0xB4, 0x40, 0xD7, 0x70, 0xD8, 0x31, 0x44, 0xC5, 0x29, 0xA1, 0x58, 0x64, 0xF9, 0xE5, 0x9D, 0x1E, 0x4F, - 0xFA, 0xEE, 0x1E, 0xAB, 0x12, 0xCC, 0x0A, 0xE9, 0x62, 0x5D, 0x5F, 0x1D, 0xD7, 0x56, 0xB7, 0x68, 0x82, 0x33, 0x40, 0x26, 0xCE, 0x9C, 0xD3, 0xBC, - 0x86, 0x1C, 0x29, 0xA7, 0xD0, 0x0B, 0x29, 0xF9, 0x04, 0x9A, 0xF4, 0xE7, 0x67, 0x3E, 0x77, 0x7E, 0x28, 0xB8, 0xBF, 0x42, 0xC3, 0xBB, 0xA7, 0x52, - 0x8C, 0xEA, 0x98, 0xBC, 0x16, 0x91, 0xAB, 0x1E, 0x92, 0x8C, 0x2D, 0x75, 0xBB, 0x87, 0xB5, 0xB1, 0x0D, 0xA6, 0x3E, 0x5D, 0x33, 0x61, 0x93, 0x6E, - 0xFC, 0x62, 0x4D, 0x7F, 0x4F, 0xD3, 0xAD, 0xB5, 0xF7, 0xB9, 0xF7, 0x17, 0x73, 0xAB, 0xF2, 0x0A, 0xB1, 0xEF, 0xC2, 0x59, 0x31, 0xDF, 0x56, 0xD9, - 0xB4, 0x5E, 0x8E, 0xB0, 0x33, 0xE9, 0x5D, 0x8A, 0x43, 0x72, 0xE2, 0x54, 0x53, 0xDC, 0x37, 0xC7, 0x0E, 0xA4, 0xE5, 0x62, 0xF1, 0xC0, 0xA6, 0x25, - 0xB7, 0x6F, 0x8F, 0x9C, 0x37, 0x62, 0x9F, 0xC0, 0xB8, 0xA0, 0xD7, 0x28, 0x86, 0x1C, 0x0F, 0xDA, 0x35, 0x68, 0xC9, 0xF1, 0x53, 0xF9, 0x68, 0xA9, - 0x05, 0x0C, 0xFE, 0x14, 0x9E, 0x69, 0x50, 0x69, 0xD6, 0x31, 0xE0, 0xE7, 0x81, 0xC0, 0x33, 0x0E, 0xBA, 0x6C, 0x12, 0xDA, 0xE3, 0x24, 0xC8, 0xB3, - 0xD5, 0x01, 0x46, 0x51, 0x03, 0x0D, 0x19, 0x47, 0x9D, 0xC1, 0xB9, 0xDD, 0x64, 0x56, 0xC4, 0xE6, 0x8A, 0x05, 0x36, 0x57, 0x14, 0x26, 0x8B, 0x42, - 0x3E, 0xC1, 0x88, 0xB1, 0xC4, 0xB7, 0x99, 0x10, 0xAB, 0x99, 0x8F, 0xBD, 0x21, 0x5B, 0xE6, 0x1C, 0xEB, 0xE4, 0x6C, 0x3E, 0x13, 0x31, 0xA3, 0x04, - 0x9A, 0xA9, 0x6A, 0x91, 0x6A, 0x51, 0xF3, 0x14, 0xEF, 0xDC, 0x59, 0x3D, 0x96, 0x7F, 0x4E, 0x4E, 0xB3, 0x84, 0xE4, 0x8C, 0x33, 0x7A, 0x0C, 0x71, - 0x6B, 0x77, 0x16, 0x71, 0x50, 0xEC, 0xD8, 0xA7, 0x68, 0xAB, 0xED, 0x90, 0x27, 0x22, 0x98, 0x1B, 0x02, 0x5B, 0x72, 0x2B, 0x29, 0x6A, 0x7D, 0x92, - 0x30, 0x8F, 0x04, 0xE9, 0x99, 0xF9, 0xB3, 0xA0, 0x87, 0xE8, 0x6E, 0xB9, 0xD1, 0x04, 0x40, 0x1C, 0x55, 0x77, 0x81, 0x9D, 0xA3, 0x97, 0x8F, 0xE8, - 0x8E, 0xDB, 0xD5, 0x83, 0x44, 0x53, 0x41, 0xCA, 0xC8, 0x42, 0xDE, 0xA7, 0x71, 0x65, 0x8F, 0x31, 0x07, 0x0A, 0xD1, 0xF7, 0xEC, 0xB9, 0x83, 0xC7, - 0xD6, 0x82, 0xE8, 0x9B, 0x6E, 0x40, 0x79, 0x34, 0x96, 0x45, 0x6A, 0xEE, 0xDA, 0xBD, 0xBE, 0x6C, 0x9D, 0xE8, 0xD6, 0x29, 0x24, 0x7E, 0x95, 0x4F, - 0xBB, 0x60, 0x9C, 0x94, 0x41, 0xA3, 0x41, 0x4E, 0xD9, 0x2F, 0x19, 0xB8, 0xCA, 0xC0, 0x34, 0x83, 0xAE, 0x2E, 0x10, 0xE9, 0xAF, 0x8C, 0x2B, 0xCA, - 0x3A, 0x4A, 0xA5, 0xDA, 0x61, 0xAC, 0xD1, 0x15, 0x4D, 0x0A, 0x95, 0x3F, 0xA3, 0xBE, 0xFF, 0x3A, 0x2C, 0xAA, 0xCF, 0xE0, 0xBD, 0x95, 0xDF, 0xFA, - 0x84, 0x0F, 0x04, 0x43, 0x2F, 0x45, 0x07, 0x87, 0xB9, 0xB1, 0x0D, 0xDE, 0xE9, 0xEA, 0xE5, 0xDF, 0x7A, 0xC9, 0x30, 0xFA, 0xAF, 0xFE, 0x13, 0xB7, - 0xC5, 0x07, 0xE5, 0x22, 0xD0, 0xA0, 0x7B, 0xE4, 0xF8, 0x41, 0xA3, 0xB0, 0x80, 0x9B, 0x8F, 0x0E, 0x7B, 0x64, 0x3E, 0x02, 0x9D, 0x51, 0x91, 0x7F, - 0xAB, 0x5A, 0x3D, 0x39, 0xBF, 0x91, 0x03, 0xCD, 0x6A, 0x6E, 0xA0, 0xAF, 0x9D, 0xF9, 0x3E, 0x63, 0x04, 0xB6, 0xAF, 0xEE, 0x47, 0xB8, 0x18, 0xAC, - 0xA7, 0xDA, 0x42, 0x70, 0x1F, 0xA5, 0x42, 0x2D, 0x44, 0x44, 0xB5, 0xB4, 0x9B, 0xB3, 0x0B, 0x0F, 0x87, 0xC6, 0x95, 0x79, 0x60, 0x44, 0xCF, 0xD1, - 0xA6, 0xBA, 0x56, 0x0E, 0x74, 0xE0, 0x07, 0xA2, 0x0E, 0x8D, 0x16, 0x1E, 0x6B, 0xED, 0x52, 0x5F, 0x72, 0x40, 0xE4, 0x0C, 0xCF, 0x41, 0x8A, 0xDE, - 0xAD, 0x18, 0x1B, 0xBD, 0xC7, 0x1F, 0x90, 0x7F, 0x28, 0x8F, 0x73, 0xEB, 0x11, 0x59, 0x01, 0xD8, 0x42, 0x66, 0x06, 0xA5, 0xB1, 0xC8, 0x13, 0x22, - 0x2E, 0x88, 0x22, 0x9A, 0x57, 0x63, 0x2F, 0x3C, 0xE0, 0x39, 0x1C, 0x75, 0x97, 0x8D, 0xCB, 0x3A, 0x78, 0x2F, 0xFA, 0x7D, 0xAA, 0xEE, 0x69, 0x0A, - 0x4F, 0xA3, 0x58, 0x4E, 0xDB, 0x3A, 0x7F, 0xD4, 0xEB, 0xBD, 0x76, 0x8C, 0x9C, 0x95, 0xD3, 0xD4, 0xD9, 0x23, 0x55, 0x41, 0x16, 0xB8, 0x65, 0xE4, - 0x81, 0x0B, 0x73, 0x8A, 0xA3, 0x19, 0x8A, 0x58, 0xC9, 0x80, 0xBD, 0x69, 0x01, 0x6D, 0xEA, 0x55, 0x3B, 0x69, 0x95, 0xC5, 0x40, 0x4B, 0xA9, 0xE2, - 0xCF, 0x68, 0x81, 0xCA, 0x27, 0x8A, 0xA2, 0x81, 0x34, 0xBB, 0x9A, 0xE3, 0x43, 0xE9, 0x68, 0x2D, 0xE4, 0x9C, 0xFB, 0xB3, 0x1C, 0x44, 0x3D, 0x96, - 0x3C, 0x65, 0xEB, 0x11, 0x7C, 0xC1, 0x21, 0x60, 0xDF, 0xC0, 0x2C, 0x84, 0x6C, 0x7B, 0x09, 0x7A, 0x59, 0xD8, 0x47, 0x83, 0xFB, 0x6C, 0xC7, 0x5F, - 0xDA, 0x99, 0x76, 0xB3, 0x3F, 0x61, 0xD0, 0x45, 0x94, 0x25, 0x17, 0xAB, 0xB2, 0x38, 0x04, 0x0A, 0x05, 0xAB, 0x80, 0xB6, 0xF2, 0x9D, 0xCD, 0xE4, - 0x6D, 0x95, 0x22, 0x5E, 0x96, 0x3A, 0xCC, 0x2B, 0x84, 0xB1, 0x14, 0x4B, 0x2E, 0x2F, 0x78, 0x6E, 0xF6, 0xC9, 0xBD, 0x03, 0x18, 0x89, 0xF5, 0xB8, - 0x99, 0xF5, 0x18, 0xFA, 0x00, 0x5B, 0xAC, 0x87, 0x7F, 0xC7, 0x90, 0x90, 0x66, 0x42, 0x8C, 0x8D, 0x8F, 0xE1, 0x19, 0xDE, 0x74, 0x46, 0xC7, 0x4A, - 0x46, 0x5F, 0x1C, 0xB3, 0xFB, 0xCE, 0x03, 0xBF, 0xC7, 0x68, 0x96, 0xE8, 0x0A, 0xC0, 0xF0, 0x54, 0xE6, 0xE0, 0xD7, 0x95, 0x01, 0xA8, 0xDE, 0x3D, - 0x0C, 0x1A, 0xC6, 0x70, 0x7F, 0x1B, 0xF5, 0x44, 0x91, 0x04, 0x02, 0x24, 0x64, 0xCC, 0xC5, 0x26, 0x84, 0x4B, 0x67, 0x11, 0xDC, 0x87, 0xA1, 0xEA, - 0x8E, 0xC7, 0x31, 0x26, 0x4A, 0x1D, 0xE7, 0x41, 0x4B, 0x6B, 0x5C, 0x35, 0x10, 0xE8, 0xFA, 0x00, 0x2C, 0x89, 0xA4, 0xE0, 0x9C, 0xDE, 0x37, 0x15, - 0x7F, 0x12, 0xC2, 0x80, 0xD4, 0x73, 0xE2, 0xC8, 0xD0, 0xDD, 0x58, 0xD8, 0x14, 0xA2, 0x19, 0x4F, 0xDD, 0xDB, 0xF2, 0x7A, 0xCB, 0xF4, 0x99, 0xC3, - 0x7B, 0xF9, 0xF3, 0xBB, 0x63, 0x63, 0x88, 0xB9, 0x15, 0xE1, 0xD0, 0x30, 0x9D, 0xE2, 0xB5, 0x11, 0x70, 0xC9, 0xD2, 0x25, 0x06, 0xE0, 0x22, 0x19, - 0x30, 0x36, 0x30, 0x5A, 0x0B, 0xDE, 0x3A, 0x88, 0x09, 0x42, 0x5A, 0xCB, 0xF4, 0x30, 0x9A, 0x8C, 0x51, 0x0A, 0x30, 0x6A, 0xDA, 0x24, 0xC6, 0xE6, - 0x0C, 0x7D, 0x22, 0x1B, 0x1D, 0x93, 0x2B, 0x46, 0x32, 0xC8, 0xB2, 0x92, 0x4C, 0x72, 0x5E, 0x22, 0xCB, 0x83, 0x9F, 0xF6, 0xA3, 0x23, 0x5A, 0xC4, - 0x34, 0xBB, 0x48, 0xEC, 0x39, 0x4C, 0xD1, 0x4C, 0x93, 0xE6, 0xD9, 0xC0, 0x16, 0xE2, 0x76, 0x2C, 0x9E, 0xE4, 0xBC, 0x70, 0x19, 0x0F, 0x03, 0x41, - 0x83, 0x79, 0xDE, 0x8F, 0x7A, 0xEC, 0x40, 0x2D, 0x63, 0x19, 0x5B, 0x3D, 0xFB, 0xB2, 0x98, 0x64, 0x4B, 0xF5, 0x77, 0x3C, 0x3B, 0x30, 0xF8, 0x76, - 0x6E, 0xD8, 0xE5, 0xF2, 0x0E, 0x0B, 0x23, 0xFE, 0xCF, 0xC0, 0x49, 0x76, 0x1D, 0x0C, 0x68, 0x2D, 0xCD, 0xEF, 0x88, 0xEC, 0x61, 0xB7, 0xE5, 0x01, - 0x2F, 0x4E, 0x3A, 0x2C, 0x79, 0x54, 0x1A, 0x7D, 0x3D, 0x18, 0xAF, 0xEC, 0x91, 0xC3, 0xD9, 0xC1, 0x3C, 0xBD, 0x68, 0xF4, 0x51, 0x01, 0xD2, 0x02, - 0x5E, 0x41, 0x39, 0x00, 0x59, 0x28, 0x99, 0x1C, 0x3F, 0xAB, 0x97, 0x31, 0xD4, 0x16, 0xC5, 0x0F, 0x72, 0xCA, 0x04, 0x71, 0x0F, 0x77, 0xDC, 0xB2, - 0xE5, 0x12, 0xC3, 0xAA, 0xCE, 0x9C, 0x35, 0xC4, 0xD2, 0xC9, 0x1B, 0x30, 0x45, 0xDF, 0x15, 0xCC, 0xE1, 0xAB, 0xF6, 0x06, 0xCE, 0x44, 0x3E, 0xB0, - 0x22, 0x55, 0x64, 0xB4, 0xCF, 0x12, 0x55, 0xDA, 0x06, 0x8E, 0xE8, 0x4E, 0xEC, 0x4E, 0x10, 0xDD, 0xE7, 0x8C, 0x73, 0xA9, 0x60, 0xF6, 0x79, 0x22, - 0x73, 0x85, 0x2C, 0x97, 0x86, 0x8C, 0x57, 0xAE, 0x21, 0x23, 0xCD, 0x28, 0x9D, 0x15, 0xF3, 0xBA, 0x01, 0xBF, 0xB3, 0x98, 0xD8, 0xA1, 0x76, 0x2E, - 0xC7, 0xC9, 0x59, 0x6D, 0x52, 0x3F, 0x18, 0x76, 0x2A, 0xAF, 0x49, 0xFB, 0x3B, 0xD8, 0x7F, 0x9E, 0xEF, 0x0C, 0xCE, 0x64, 0xAF, 0xDB, 0xC3, 0xAA, - 0xD8, 0xF8, 0x90, 0xAB, 0x0A, 0x4D, 0x63, 0xA8, 0x64, 0xE5, 0xB4, 0x2A, 0xE1, 0xFD, 0x18, 0x67, 0xF7, 0xB9, 0x9B, 0x93, 0xF0, 0xF8, 0x6E, 0x98, - 0x0D, 0xFB, 0x5A, 0x81, 0x7C, 0x86, 0x95, 0x0A, 0xDF, 0x7D, 0x64, 0x5B, 0xDD, 0xD7, 0x50, 0xFE, 0xE2, 0x59, 0x4B, 0x9B, 0x05, 0x9A, 0x5E, 0x5A, - 0x3C, 0x7E, 0xB1, 0x88, 0xC2, 0xD7, 0x43, 0xB1, 0x9A, 0x19, 0x97, 0x09, 0x37, 0x1C, 0x81, 0x4E, 0x39, 0xE4, 0x45, 0xCD, 0x28, 0xE3, 0x50, 0xE7, - 0xE2, 0x6E, 0x68, 0x6D, 0x9D, 0xD4, 0x23, 0x13, 0x8D, 0x6F, 0x9A, 0x93, 0x72, 0xAC, 0xD5, 0xB7, 0x42, 0x04, 0xDC, 0xA3, 0x33, 0xBC, 0x55, 0x35, - 0x62, 0x5A, 0x06, 0x16, 0x35, 0x54, 0x0B, 0xFB, 0x3F, 0x47, 0xEB, 0x4B, 0xB6, 0x89, 0x6F, 0x8D, 0x11, 0x62, 0xC0, 0xAF, 0xB8, 0xAB, 0x6A, 0x55, - 0x39, 0xEF, 0xFC, 0x59, 0x8E, 0xC0, 0xC5, 0xC0, 0x71, 0x0D, 0x85, 0x29, 0x4E, 0x3E, 0x72, 0x37, 0x77, 0x3F, 0xE7, 0x84, 0x0C, 0xA0, 0xBE, 0x8C, - 0xF5, 0x52, 0x83, 0x51, 0xFC, 0x74, 0xD5, 0xFA, 0x19, 0xAB, 0xDE, 0xBC, 0x9D, 0x4B, 0xBE, 0xE7, 0xC3, 0x06, 0xCB, 0x81, 0x0B, 0x1D, 0xDA, 0x66, - 0x36, 0x86, 0x61, 0x08, 0x40, 0x53, 0xBF, 0x89, 0x27, 0xE2, 0x15, 0x35, 0x58, 0xB5, 0x0E, 0x71, 0xCE, 0x76, 0xDE, 0xB7, 0x94, 0x21, 0xAF, 0xAB, - 0x8F, 0xE4, 0x1A, 0x8E, 0x8C, 0x42, 0x2C, 0x18, 0xE2, 0x0C, 0x9A, 0xBD, 0xE1, 0x34, 0xE5, 0xB1, 0x4D, 0x9D, 0x2C, 0x46, 0x00, 0x4E, 0x2E, 0x36, - 0x9C, 0x1E, 0x23, 0x62, 0xE6, 0x9A, 0x83, 0x1F, 0x87, 0x2F, 0x59, 0xD6, 0x44, 0xE0, 0x96, 0xB2, 0xCC, 0x5A, 0x2A, 0x06, 0x63, 0x06, 0x65, 0x19, - 0x18, 0x1C, 0xF2, 0x39, 0x3D, 0xB8, 0x82, 0x36, 0x42, 0xCB, 0xC9, 0x37, 0x02, 0x79, 0x9F, 0x19, 0x0A, 0x87, 0x7B, 0x11, 0xE1, 0x6C, 0xC6, 0x61, - 0xF7, 0xBA, 0x22, 0x5A, 0x08, 0x51, 0x19, 0x7C, 0xE9, 0xE6, 0x8B, 0x9C, 0xE5, 0xA8, 0x3A, 0x99, 0x06, 0x44, 0xEB, 0x94, 0x34, 0x76, 0xEF, 0x4D, - 0xC2, 0xE6, 0xC4, 0xB8, 0x9E, 0x61, 0x18, 0x27, 0x22, 0x0C, 0xDE, 0x32, 0x0C, 0x47, 0x74, 0x55, 0x3D, 0xAB, 0x30, 0x71, 0x82, 0x90, 0xEC, 0x0B, - 0xBC, 0x0E, 0x2F, 0x9D, 0xC1, 0xE1, 0xC4, 0xAE, 0xD3, 0x0E, 0xDB, 0x1A, 0x82, 0xA3, 0x44, 0x74, 0xF5, 0xBF, 0x44, 0x5C, 0x3A, 0x95, 0x2E, 0x3D, - 0x93, 0x59, 0xC7, 0xF5, 0x3A, 0x44, 0x3E, 0x36, 0xAA, 0xBB, 0x38, 0x3B, 0x32, 0x42, 0xCA, 0xDC, 0x37, 0xF3, 0x93, 0x73, 0x6E, 0x7B, 0xBC, 0xE1, - 0x0C, 0x4F, 0x23, 0x9E, 0x9A, 0xC6, 0xF6, 0x44, 0x19, 0xA3, 0x3B, 0x02, 0x9A, 0x00, 0x03, 0xE0, 0x1D, 0x1A, 0x7D, 0x1C, 0xB9, 0x64, 0xE9, 0xDA, - 0xB2, 0xCC, 0x8B, 0xED, 0xEE, 0xDB, 0x9B, 0xAA, 0x97, 0x3D, 0xA3, 0x78, 0xF9, 0xA8, 0x04, 0x2C, 0x9F, 0x9E, 0xD8, 0x1F, 0x06, 0x81, 0xF3, 0x8D, - 0x1E, 0xAE, 0xD9, 0x7B, 0x57, 0xCF, 0xF0, 0x69, 0x24, 0xE1, 0x70, 0x92, 0xFB, 0x73, 0x76, 0x9E, 0x28, 0x3B, 0xB3, 0xDE, 0xD6, 0xD1, 0x94, 0xAF, - 0x9A, 0xE5, 0x92, 0x70, 0x0F, 0xE2, 0x9E, 0x47, 0x2B, 0x98, 0x1B, 0x16, 0xD6, 0x3B, 0x78, 0x47, 0xDD, 0xDE, 0x64, 0xE6, 0xC4, 0xEF, 0x51, 0xEA, - 0xCA, 0x1D, 0xD9, 0xA0, 0x7A, 0xD3, 0x36, 0xA0, 0xA3, 0x1B, 0xAD, 0x41, 0x24, 0x45, 0xFB, 0x84, 0x99, 0x8C, 0xF3, 0xD2, 0x30, 0x83, 0x91, 0x69, - 0x7A, 0x36, 0x07, 0xC7, 0x08, 0xDA, 0x87, 0xC8, 0x92, 0x19, 0x5C, 0xA3, 0xE9, 0xEB, 0x2C, 0xD3, 0x80, 0xF5, 0x99, 0x93, 0xC0, 0xBE, 0x41, 0xEE, - 0x42, 0x1E, 0x07, 0x9A, 0xCD, 0x0B, 0xB5, 0x2A, 0x65, 0x99, 0x23, 0x45, 0xD4, 0x94, 0xB5, 0xAD, 0x29, 0x93, 0xC3, 0x8D, 0x45, 0xAE, 0xF3, 0xFE, - 0x81, 0xBF, 0x09, 0x66, 0x23, 0xC2, 0xAE, 0x10, 0xAE, 0x26, 0xBF, 0xFB, 0xA2, 0x31, 0x7F, 0x9E, 0xAC, 0x93, 0xDC, 0x0D, 0xF7, 0x94, 0x33, 0x9D, - 0x65, 0x38, 0xF4, 0x97, 0x39, 0x69, 0x25, 0xDF, 0xBB, 0xA6, 0xD7, 0x68, 0x33, 0x92, 0xB5, 0xA4, 0xF5, 0xED, 0x1E, 0xB3, 0x16, 0x99, 0x73, 0xBC, - 0x18, 0xAA, 0xDC, 0xC1, 0xD7, 0xD3, 0x49, 0x16, 0xDE, 0x24, 0x38, 0x21, 0x1D, 0xB8, 0xA7, 0xB1, 0x4C, 0xDC, 0x0C, 0xCC, 0x1A, 0xF9, 0xC9, 0x46, - 0x8F, 0x90, 0xB3, 0x55, 0x6A, 0x22, 0x5F, 0xAA, 0x79, 0x21, 0xD8, 0xF0, 0xE4, 0xEF, 0xA9, 0x78, 0xDC, 0x0D, 0xD5, 0xDF, 0x22, 0x61, 0xD2, 0xC3, - 0x25, 0x68, 0x92, 0x89, 0x0A, 0x05, 0xA7, 0x57, 0xC2, 0x36, 0x50, 0x1C, 0x69, 0xFA, 0x6D, 0xAF, 0x83, 0xDD, 0x30, 0x7B, 0x9D, 0x25, 0x3A, 0x49, - 0x39, 0x38, 0x3C, 0x6E, 0xE3, 0xCC, 0xD6, 0xE4, 0x71, 0x5D, 0x70, 0x2D, 0xF0, 0x71, 0x70, 0x67, 0x8A, 0xBA, 0x6F, 0x00, 0x25, 0xBF, 0x8C, 0x6A, - 0xEB, 0xFC, 0x6B, 0x21, 0xCC, 0xAB, 0xEE, 0xC6, 0x9B, 0x94, 0xFF, 0x2D, 0xB9, 0x3F, 0x85, 0xEA, 0x6D, 0x63, 0x3E, 0x10, 0xBC, 0xC3, 0xCB, 0x7F, - 0x17, 0xB6, 0x55, 0xF8, 0x85, 0x0B, 0xBF, 0x68, 0x6B, 0x36, 0xCC, 0x2C, 0x37, 0x4F, 0x8A, 0x75, 0x2C, 0x62, 0xBA, 0x3F, 0x9A, 0xE4, 0x2F, 0x81, - 0x4E, 0x06, 0x68, 0x94, 0x0F, 0xE6, 0x51, 0xA1, 0xC5, 0xAD, 0xC2, 0x57, 0xE5, 0x39, 0xBF, 0x63, 0xB5, 0x01, 0x3D, 0xE6, 0xEA, 0x9B, 0xCC, 0x2D, - 0xC4, 0x96, 0x1B, 0xBC, 0x04, 0xEF, 0xC1, 0xAD, 0x67, 0xC5, 0xAB, 0xEA, 0xE7, 0x4A, 0x95, 0x63, 0x52, 0x06, 0x02, 0xBC, 0xE9, 0x02, 0xE7, 0xA4, - 0x69, 0x99, 0x64, 0x27, 0x62, 0xEF, 0x8B, 0xCA, 0x04, 0x31, 0x7E, 0x4D, 0x83, 0x5F, 0xE1, 0xF4, 0xEB, 0x45, 0x19, 0x65, 0x84, 0x17, 0xCF, 0x87, - 0x45, 0x79, 0x00, 0x62, 0x68, 0xE7, 0x83, 0xA8, 0x3E, 0x06, 0x1C, 0xCD, 0xE0, 0xD0, 0x63, 0xFB, 0xC6, 0x23, 0x55, 0xC0, 0x53, 0xFE, 0x45, 0x7D, - 0x3C, 0x9D, 0x34, 0xDE, 0xED, 0x0C, 0x5F, 0xBA, 0x06, 0xA6, 0x71, 0xBA, 0xD2, 0xE0, 0xB9, 0x41, 0x98, 0xAC, 0x53, 0x54, 0x29, 0x11, 0x71, 0x56, - 0x0A, 0x66, 0xDD, 0x76, 0x86, 0x50, 0xBA, 0x8F, 0x83, 0x77, 0xD6, 0x0F, 0xA8, 0x30, 0x17, 0xDB, 0x3B, 0x1B, 0x8E, 0x9D, 0x53, 0x3B, 0x54, 0xF2, - 0x7A, 0x83, 0xA6, 0x6F, 0x20, 0x27, 0x08, 0x46, 0x7A, 0x8C, 0x42, 0x90, 0x86, 0xB3, 0xE0, 0x12, 0x1C, 0xAD, 0x54, 0x68, 0xCF, 0x24, 0xD1, 0x6D, - 0xF3, 0x08, 0x93, 0x60, 0x4E, 0x60, 0xD1, 0x96, 0x69, 0x68, 0x2E, 0x17, 0x4E, 0xDD, 0xB6, 0xF8, 0xAD, 0x0A, 0x6B, 0x7A, 0x50, 0xE6, 0x93, 0x83, - 0xB3, 0x38, 0x8B, 0xB1, 0x24, 0xB5, 0x89, 0x1F, 0x98, 0xF9, 0xD8, 0xE4, 0x66, 0xC6, 0x25, 0x55, 0x9B, 0x1D, 0x59, 0x89, 0x74, 0x71, 0xCD, 0xB5, - 0x12, 0x0D, 0xBD, 0x26, 0x9A, 0x69, 0xDE, 0x92, 0xBB, 0x83, 0xDE, 0xB1, 0x26, 0xE6, 0x18, 0x7E, 0xA7, 0x6B, 0xCB, 0x1F, 0x83, 0x50, 0x3D, 0x39, - 0x94, 0x67, 0xED, 0xC5, 0x49, 0x3C, 0xF3, 0x90, 0xF9, 0x03, 0x1F, 0xD2, 0xBB, 0x81, 0x61, 0x24, 0x50, 0xE1, 0x35, 0x76, 0x17, 0xB9, 0x00, 0xA1, - 0xD7, 0x77, 0x9B, 0x00, 0x8F, 0x3E, 0xAB, 0x11, 0xBF, 0x33, 0xE7, 0x0A, 0xB1, 0xED, 0x9B, 0x57, 0xF2, 0xE0, 0x54, 0x13, 0x31, 0x45, 0x0F, 0xF4, - 0x68, 0x17, 0xD9, 0x80, 0xBA, 0xB1, 0x97, 0x72, 0x22, 0x2E, 0xB5, 0x57, 0x42, 0x8A, 0x1D, 0x1E, 0xA3, 0x70, 0x49, 0x0B, 0x39, 0x98, 0x9E, 0xA5, - 0x13, 0x2F, 0xB8, 0xAE, 0x6B, 0xBC, 0xDA, 0x2A, 0x3D, 0x36, 0xD5, 0x12, 0xB4, 0xC3, 0x29, 0x9F, 0x9E, 0x42, 0x2E, 0x33, 0x98, 0x82, 0xB1, 0x70, - 0x5C, 0xC2, 0xF0, 0x38, 0x43, 0xFA, 0x17, 0xB8, 0x88, 0xD0, 0xEE, 0x2D, 0x3A, 0x04, 0x85, 0xCE, 0x40, 0xD1, 0x53, 0x6B, 0xBB, 0xA3, 0x66, 0xB5, - 0x08, 0xF5, 0xDB, 0xAE, 0x53, 0xE5, 0xF2, 0x63, 0xB7, 0x0E, 0xC0, 0x78, 0x32, 0xF0, 0x51, 0xAF, 0x78, 0xB0, 0x00, 0xB5, 0x04, 0x01, 0x1C, 0xB9, - 0xCD, 0xF4, 0x55, 0xC3, 0x42, 0xF5, 0x80, 0xA9, 0x91, 0xC4, 0x20, 0xA4, 0x90, 0x59, 0xFB, 0xBE, 0x23, 0x67, 0x19, 0x87, 0x67, 0xBB, 0x1C, 0x1F, - 0x15, 0x12, 0x80, 0x96, 0xF6, 0xB9, 0x66, 0xC9, 0x95, 0x77, 0xC1, 0xC6, 0xBE, 0x90, 0xCF, 0x73, 0xB6, 0xA3, 0xED, 0x64, 0x13, 0x86, 0x19, 0x48, - 0xD6, 0xFD, 0xC6, 0x08, 0xFF, 0xF3, 0xDD, 0xAA, 0xC1, 0xF1, 0x0A, 0x1D, 0xB0, 0xDF, 0x32, 0x71, 0xDE, 0x85, 0x07, 0x36, 0x59, 0xF1, 0x16, 0x34, - 0x4D, 0x02, 0x65, 0xA6, 0x4B, 0x37, 0x72, 0xFE, 0x17, 0x85, 0x47, 0x92, 0x0F, 0x4E, 0x7F, 0x1E, 0xCD, 0xAB, 0x68, 0xE0, 0x06, 0x44, 0x93, 0x99, - 0x86, 0x13, 0xF5, 0x36, 0x76, 0x33, 0x46, 0xA6, 0x2F, 0xAB, 0x80, 0xF6, 0x08, 0xAD, 0x98, 0x61, 0xE1, 0xE5, 0xA6, 0xD3, 0x52, 0xFE, 0x03, 0x69, - 0x4D, 0x3F, 0xCF, 0x8F, 0x7A, 0x82, 0xF4, 0x81, 0x40, 0x17, 0x29, 0xF2, 0x90, 0x56, 0x0D, 0x9E, 0x92, 0x6B, 0x6D, 0x76, 0x13, 0x8A, 0x55, 0x60, - 0x4A, 0xBE, 0x3B, 0x6F, 0xCE, 0x24, 0x63, 0xD1, 0x94, 0x8E, 0x7E, 0x6D, 0xCB, 0x0C, 0xD6, 0xE0, 0x44, 0xBA, 0x79, 0xEC, 0xCD, 0x65, 0x10, 0xEC, - 0xD3, 0x8B, 0xC0, 0x89, 0xE3, 0x2D, 0xE6, 0x22, 0x42, 0xD7, 0x51, 0xC6, 0x70, 0xA6, 0x3F, 0xC7, 0xD4, 0x60, 0x26, 0xCF, 0xBC, 0x48, 0x2F, 0x93, - 0x73, 0x37, 0x4F, 0x48, 0xDE, 0xF2, 0xCF, 0x70, 0x4E, 0x07, 0xF8, 0xC0, 0x14, 0x4F, 0x75, 0xB6, 0x03, 0xB6, 0xFD, 0x2F, 0xFD, 0x9C, 0x3C, 0x85, - 0xB1, 0xCD, 0xE7, 0x6F, 0x5D, 0x6F, 0x40, 0xBA, 0x69, 0xD5, 0xEB, 0xDD, 0xE1, 0xD1, 0x00, 0xF6, 0x8D, 0x8C, 0x73, 0x14, 0xB7, 0x3A, 0x3D, 0x11, - 0xDB, 0x94, 0xE1, 0x4E, 0x24, 0xC3, 0xEB, 0xE5, 0xB1, 0xA1, 0x7A, 0x6C, 0x0D, 0xC9, 0x66, 0x77, 0xA9, 0x18, 0x60, 0xF4, 0x03, 0x2C, 0xAC, 0x00, - 0x8B, 0xC4, 0x68, 0x30, 0x05, 0x03, 0x0A, 0x03, 0x06, 0x03, 0x8A, 0x8D, 0xA3, 0x51, 0x29, 0x64, 0xE3, 0x28, 0x7E, 0xD7, 0xF6, 0xC5, 0x45, 0x73, - 0x1E, 0xB5, 0x62, 0x8E, 0x60, 0xE2, 0x8D, 0xD1, 0x09, 0xB5, 0x1A, 0xBC, 0xCC, 0xB9, 0x2A, 0xF3, 0xFB, 0x66, 0x2E, 0x82, 0x2A, 0x8B, 0x9E, 0x00, - 0x98, 0xF4, 0xC3, 0xB1, 0xD9, 0xEA, 0x17, 0xEA, 0x5C, 0xF5, 0xA8, 0x1A, 0xAF, 0x46, 0xFB, 0xFB, 0x90, 0x0C, 0x54, 0xB4, 0xC1, 0x76, 0xB4, 0x6B, - 0xCF, 0x59, 0x0F, 0x10, 0xB2, 0x3E, 0x2D, 0xB1, 0x3B, 0x93, 0xED, 0x45, 0x06, 0x3F, 0xEE, 0x17, 0x56, 0x89, 0xD5, 0xFD, 0xBA, 0x55, 0xE3, 0xDA, - 0xE6, 0x52, 0x37, 0xFC, 0x6A, 0x35, 0xE3, 0xA4, 0x57, 0x82, 0xF8, 0x74, 0x29, 0xA4, 0x1E, 0x0B, 0x0A, 0xF3, 0x42, 0x10, 0x5E, 0xD6, 0x4B, 0x20, - 0x52, 0x0E, 0xD2, 0xAA, 0x10, 0x84, 0xAD, 0xDD, 0x87, 0xA8, 0xC8, 0xE7, 0x4C, 0xB0, 0x31, 0x2F, 0xAB, 0x83, 0xD5, 0xBE, 0x6A, 0xBD, 0xFA, 0x55, - 0xDD, 0x79, 0x92, 0x04, 0xAF, 0x1B, 0xF6, 0x59, 0xB8, 0x69, 0x4F, 0xB5, 0x4D, 0xD7, 0x46, 0x7A, 0xB5, 0xA6, 0x3D, 0xD6, 0xCD, 0xA6, 0xFA, 0x2F, - 0xA5, 0xE9, 0x33, 0x7E, 0x62, 0x65, 0xC1, 0x24, 0x0C, 0x45, 0x89, 0x9B, 0x84, 0x7D, 0xDB, 0x00, 0x74, 0x9B, 0x24, 0x0F, 0x69, 0xA4, 0x6F, 0x80, - 0x08, 0x22, 0x19, 0x3D, 0x64, 0xB3, 0x30, 0x55, 0xC5, 0x07, 0x59, 0x49, 0xF6, 0x18, 0x56, 0x6B, 0xB1, 0xE2, 0xA3, 0xC5, 0x09, 0xA1, 0x7C, 0xD5, - 0x83, 0xDC, 0x5C, 0xD5, 0xC4, 0x8A, 0x1E, 0x05, 0xD7, 0xDE, 0xB4, 0xBD, 0x18, 0x2F, 0x45, 0x6E, 0xBB, 0x14, 0x7D, 0xBC, 0xBC, 0x0B, 0x16, 0xA4, - 0xCF, 0xD0, 0x5F, 0xDC, 0x48, 0x6C, 0x35, 0xFB, 0x27, 0xA4, 0x6C, 0x19, 0xC0, 0x18, 0x17, 0xCC, 0x1C, 0x17, 0xEA, 0xE1, 0x96, 0x0A, 0x0E, 0x5A, - 0xBC, 0x7A, 0xAA, 0x29, 0xB2, 0xD8, 0x88, 0xC5, 0xA5, 0x57, 0x35, 0x9F, 0x56, 0x1A, 0x8E, 0x55, 0x53, 0x7A, 0xAA, 0x9B, 0xD3, 0xA9, 0x36, 0x69, - 0x38, 0x0E, 0x28, 0xC1, 0xFF, 0xA4, 0x77, 0xE3, 0x94, 0x19, 0xD3, 0x94, 0x38, 0xC5, 0x64, 0xDB, 0xB4, 0xB4, 0x89, 0xA3, 0xF4, 0x8C, 0x96, 0x84, - 0x4B, 0x95, 0xE4, 0x62, 0x37, 0x8F, 0x26, 0x70, 0x34, 0x24, 0xC0, 0x08, 0x86, 0x5E, 0x14, 0x06, 0x81, 0xC8, 0x89, 0x70, 0x88, 0x2E, 0x84, 0xD0, - 0x00, 0xCD, 0xFD, 0xF0, 0xCA, 0x3C, 0x67, 0xFE, 0xBC, 0x7F, 0xF9, 0xD8, 0xAF, 0x54, 0xD5, 0x21, 0x78, 0x8D, 0xB2, 0xFD, 0x92, 0x81, 0x2D, 0x6B, - 0xBC, 0x75, 0x84, 0x35, 0x84, 0xF5, 0x01, 0xF5, 0xA5, 0x7A, 0x85, 0xBA, 0x52, 0x4D, 0xD9, 0x81, 0x57, 0x9F, 0xD3, 0x09, 0xCC, 0xC7, 0xE4, 0x56, - 0xF1, 0x24, 0x38, 0xCF, 0xFB, 0xC5, 0x0D, 0xAA, 0xAF, 0xCC, 0x2F, 0xE2, 0x49, 0xDC, 0xFB, 0xEC, 0x15, 0xE6, 0x7E, 0xF4, 0x12, 0xF5, 0x14, 0xF6, - 0xCB, 0xFB, 0x28, 0x1E, 0x36, 0xCF, 0xAB, 0x0F, 0x00, 0x7F, 0x8B, 0x33, 0x90, 0x7F, 0xF5, 0xB4, 0x5A, 0x5A, 0x1D, 0xAD, 0x9E, 0x54, 0x4B, 0xAA, - 0x11, 0xD5, 0x21, 0xD4, 0x20, 0x3E, 0xD4, 0x74, 0xEA, 0x04, 0x6A, 0x37, 0xB5, 0xFC, 0xAB, 0x39, 0x80, 0x9A, 0x75, 0xA0, 0xAB, 0x84, 0xF0, 0x51, - 0x68, 0xD2, 0x68, 0xA0, 0x9E, 0x43, 0x30, 0xB8, 0xA0, 0xF3, 0xD1, 0xD4, 0xBD, 0x73, 0x93, 0xA6, 0x48, 0xA1, 0x45, 0x4D, 0xC0, 0x5E, 0xB9, 0x62, - 0x7F, 0xB6, 0xB7, 0xD9, 0xE9, 0xEC, 0x15, 0xEC, 0xAB, 0xEE, 0x2D, 0x77, 0xAE, 0xBD, 0x2E, 0x31, 0xA5, 0xF7, 0xA4, 0x4E, 0x8D, 0x60, 0x0F, 0x18, - 0x50, 0x2C, 0x72, 0xC1, 0x83, 0xC0, 0x0F, 0x61, 0x70, 0xB4, 0xC2, 0x6F, 0x21, 0x9D, 0x41, 0x0A, 0x86, 0x95, 0x31, 0xF5, 0x01, 0xB5, 0x1C, 0x47, - 0x87, 0x87, 0xC5, 0x0F, 0x36, 0x1B, 0x98, 0x3D, 0x5C, 0xD5, 0x9C, 0x3D, 0xB0, 0x03, 0xFA, 0x30, 0xFF, 0x20, 0x69, 0x07, 0x62, 0xB2, 0x95, 0x59, - 0x71, 0xD7, 0x6E, 0x84, 0x19, 0xB9, 0x0D, 0x04, 0xC8, 0x65, 0x67, 0x46, 0x9F, 0x47, 0x1A, 0xB0, 0xB3, 0x23, 0x9C, 0xB7, 0x66, 0xB0, 0x13, 0xA1, - 0x39, 0x0F, 0x3E, 0xB0, 0xF3, 0x58, 0xB8, 0xE3, 0xC1, 0xC7, 0xBC, 0xBA, 0xB9, 0x57, 0x64, 0x36, 0xB6, 0xDD, 0x87, 0x32, 0x2B, 0xD8, 0xF3, 0x32, - 0xF7, 0xED, 0xE9, 0xA3, 0x76, 0xF3, 0x39, 0x0A, 0x66, 0x08, 0x1D, 0xE6, 0x6C, 0xBB, 0x77, 0x81, 0x19, 0x3B, 0xAF, 0x02, 0x6A, 0x28, 0xBB, 0x51, - 0x04, 0x05, 0x06, 0xD8, 0xFD, 0x6A, 0x28, 0x00, 0x67, 0x86, 0x0B, 0xF9, 0x1E, 0x16, 0x95, 0x36, 0x34, 0x36, 0xD2, 0xFD, 0xE8, 0xA6, 0x74, 0x06, - 0xDF, 0xC4, 0x2B, 0xCA, 0xFF, 0x0C, 0xD4, 0x05, 0xD4, 0x6F, 0x1A, 0x5E, 0xEC, 0xF2, 0xA9, 0x42, 0x4C, 0x1C, 0x42, 0x3A, 0x5B, 0x89, 0x5A, 0x2A, - 0x7A, 0x1D, 0xD0, 0xEF, 0xED, 0x93, 0x4B, 0x08, 0xF9, 0x76, 0x79, 0x55, 0x01, 0x59, 0xEC, 0xB1, 0x62, 0x08, 0x57, 0x35, 0x74, 0x56, 0x1A, 0x69, - 0x55, 0x0B, 0x26, 0x1F, 0x3B, 0x03, 0x77, 0x6E, 0xC2, 0x14, 0x1B, 0xB4, 0xA8, 0xCD, 0x00, 0x8C, 0x00, 0x38, 0x06, 0x70, 0x06, 0xA0, 0x00, 0x80, - 0x07, 0x00, 0x40, 0x03, 0xE1, 0x81, 0xAF, 0x00, 0xEA, 0x12, 0x3F, 0xEE, 0x27, 0x0A, 0xD4, 0x70, 0xFF, 0x47, 0x58, 0x04, 0x34, 0xFB, 0xC8, 0x50, - 0x3B, 0x50, 0xB7, 0xE5, 0x0D, 0x9A, 0x7D, 0xB0, 0x93, 0x7C, 0xA4, 0xBC, 0x31, 0x3E, 0x56, 0x09, 0xA9, 0x3B, 0xC8, 0x38, 0xA1, 0xCF, 0xE0, 0x56, - 0xA6, 0x9E, 0xAB, 0x09, 0xF8, 0xD9, 0x3D, 0xAA, 0xD8, 0xF1, 0xF8, 0xFB, 0xAB, 0xA8, 0x3F, 0xA5, 0x5A, 0x31, 0x4A, 0xD5, 0x27, 0xA4, 0x15, 0xA0, - 0x1A, 0x59, 0x08, 0xA5, 0x68, 0xE6, 0x43, 0xA1, 0x1B, 0x8D, 0x42, 0x47, 0x47, 0xCE, 0x99, 0xAF, 0x6F, 0x88, 0x21, 0x62, 0x96, 0xC1, 0xA7, 0x55, - 0x9F, 0xAA, 0xBC, 0xA1, 0xE2, 0x5D, 0x9C, 0xAA, 0x82, 0xC9, 0x11, 0x19, 0xCC, 0xB9, 0xBC, 0xB8, 0xA1, 0xA5, 0xD9, 0xF6, 0x16, 0xFA, 0xB6, 0x54, - 0xB4, 0x6B, 0xDB, 0x4E, 0xAB, 0x9C, 0xB4, 0xFB, 0x79, 0xA8, 0xBE, 0xAB, 0x2A, 0x37, 0x6B, 0xA7, 0x42, 0x7E, 0xD4, 0x07, 0x4C, 0xA7, 0xAC, 0x31, - 0x21, 0xCE, 0x4C, 0x53, 0x70, 0xEA, 0x1A, 0x6E, 0x1C, 0x47, 0x7B, 0x00, 0xD6, 0x8F, 0x8C, 0xBA, 0x0C, 0x52, 0xF5, 0xF4, 0xB4, 0x33, 0x09, 0x5F, - 0x08, 0x46, 0xCA, 0x52, 0xD7, 0xE0, 0xE3, 0x9D, 0x0F, 0x12, 0x9E, 0x1F, 0xAD, 0xAD, 0x3A, 0x5B, 0x50, 0x85, 0x2E, 0xC3, 0x03, 0x9E, 0x7C, 0xBA, - 0x8A, 0xCC, 0x1A, 0x56, 0x56, 0x16, 0x93, 0x44, 0xCD, 0x90, 0x4F, 0x84, 0x4C, 0x98, 0x40, 0x68, 0x97, 0x80, 0xE1, 0xDB, 0x23, 0x80, 0x48, 0x16, - 0xE4, 0x8E, 0xDC, 0x4D, 0x44, 0xFE, 0xA0, 0x43, 0xF2, 0x6F, 0x90, 0xC8, 0x16, 0xFB, 0xC7, 0xDE, 0x8B, 0x54, 0xDA, 0xA3, 0x8D, 0x20, 0xF8, 0x85, - 0xE3, 0xAD, 0x61, 0xC3, 0x63, 0x49, 0x23, 0xBE, 0x98, 0xB1, 0x63, 0xC6, 0x9E, 0x47, 0x8F, 0x66, 0xCE, 0xA1, 0x25, 0xA4, 0x78, 0x06, 0x66, 0x3B, - 0xE3, 0x25, 0x9B, 0xA5, 0x7F, 0x99, 0xF5, 0xAC, 0xCC, 0x32, 0x1A, 0xCB, 0xDF, 0x58, 0x19, 0xAA, 0x26, 0xC6, 0x8C, 0x48, 0xF2, 0x5A, 0x14, 0x0C, - 0x2D, 0x38, 0x64, 0x0A, 0x6B, 0xC7, 0x6C, 0x59, 0xFB, 0xC2, 0x09, 0xC6, 0x15, 0xE2, 0x8A, 0x49, 0x40, 0x12, 0x4B, 0x3A, 0x0F, 0x63, 0xAE, 0xBE, - 0xC0, 0x15, 0x13, 0xAE, 0x25, 0x0E, 0x8E, 0x5E, 0xF0, 0x0C, 0x02, 0x0C, 0x60, 0xEC, 0xD7, 0x65, 0xC7, 0xD7, 0xC9, 0xD1, 0xC5, 0x50, 0x48, 0xEF, - 0x58, 0x9D, 0x37, 0x8C, 0x67, 0xF7, 0x40, 0x9D, 0x2E, 0x1D, 0x56, 0x20, 0xAA, 0x5D, 0x45, 0xCA, 0xA4, 0xB0, 0x1D, 0xB4, 0xF9, 0xF6, 0x54, 0xD5, - 0xD6, 0x85, 0xCA, 0xC8, 0x7C, 0xB6, 0xF9, 0xC3, 0x2C, 0x50, 0x07, 0x59, 0x1F, 0x9D, 0x58, 0xAF, 0x46, 0xD8, 0x98, 0x99, 0xD7, 0x2C, 0x3D, 0x18, - 0x62, 0x84, 0x68, 0x48, 0x69, 0xDB, 0x00, 0xC1, 0xFF, 0xD2, 0xC4, 0x0C, 0x30, 0x7F, 0xDF, 0x7B, 0x3F, 0xDA, 0x21, 0xA8, 0x50, 0xF4, 0x72, 0x2C, - 0x6D, 0x6D, 0x1C, 0x45, 0x90, 0x22, 0x78, 0x8F, 0x02, 0x16, 0x05, 0xC5, 0x77, 0x7D, 0xC1, 0xBC, 0xC8, 0xE3, 0xA3, 0x98, 0x23, 0x9A, 0x78, 0x23, - 0x3F, 0x01, 0xF5, 0x8C, 0xE8, 0x65, 0x79, 0x28, 0x81, 0xD4, 0xC4, 0xDB, 0xA0, 0xA1, 0x03, 0x68, 0x3B, 0x06, 0x4C, 0x77, 0x61, 0x88, 0x16, 0xC2, - 0xC1, 0x12, 0x25, 0x10, 0x59, 0x0C, 0xAE, 0xBE, 0xED, 0xB1, 0x20, 0x25, 0x82, 0x38, 0x92, 0x7F, 0x1D, 0x1C, 0xEA, 0x62, 0xB5, 0x68, 0x4B, 0xB2, - 0x4B, 0xC7, 0xAD, 0x7C, 0x6D, 0x74, 0x2D, 0x10, 0xAF, 0x46, 0xC1, 0xC5, 0x80, 0xC9, 0xDC, 0x34, 0x3B, 0x5B, 0xC0, 0x2B, 0x34, 0x5A, 0xA5, 0xEF, - 0x59, 0x8C, 0x16, 0x33, 0xD9, 0x0C, 0x33, 0x94, 0xA4, 0xBF, 0x4C, 0x43, 0x77, 0xA1, 0x42, 0xC7, 0xA1, 0x4F, 0x71, 0xE9, 0x91, 0xF6, 0x56, 0x7B, - 0xB2, 0xBD, 0xDB, 0x9E, 0xC1, 0xDE, 0xC2, 0xBE, 0xF1, 0x6F, 0xAF, 0x69, 0xDF, 0x0B, 0x8F, 0xE4, 0x3B, 0x6C, 0xE3, 0x2B, 0xB7, 0xF0, 0x1C, 0xC5, - 0xBC, 0x57, 0x5D, 0xFE, 0x60, 0x42, 0x9C, 0x54, 0xD3, 0xC3, 0x81, 0xC2, 0x90, 0xB2, 0x4A, 0x52, 0xE6, 0xDF, 0xB9, 0x92, 0xDE, 0xBC, 0xB9, 0x87, - 0xB9, 0x89, 0x39, 0xE4, 0x1C, 0x3F, 0x5F, 0x9D, 0xE7, 0xCE, 0x73, 0x80, 0x8F, 0x3F, 0xEB, 0x63, 0xFA, 0x76, 0x89, 0xBE, 0x5E, 0x27, 0xDD, 0x16, - 0x92, 0xAE, 0x5B, 0x50, 0x6C, 0x83, 0x21, 0x9B, 0x58, 0xDC, 0xB3, 0x9C, 0x60, 0xD1, 0x46, 0xA1, 0xC7, 0x13, 0x35, 0x94, 0xCE, 0x47, 0xD8, 0xE9, - 0x4B, 0x61, 0x50, 0x1E, 0x76, 0x81, 0x30, 0x31, 0x6F, 0x31, 0x74, 0x83, 0x60, 0x27, 0x01, 0xDA, 0x3E, 0xA0, 0xF2, 0x37, 0x58, 0xF5, 0x57, 0xEC, - 0x24, 0x18, 0xD1, 0x3F, 0x3E, 0xCB, 0xD7, 0xF9, 0x48, 0xDF, 0xEA, 0x93, 0x7D, 0xB7, 0xCF, 0xE0, 0x77, 0xD2, 0x55, 0xE9, 0xB8, 0xF4, 0xBC, 0x1C, - 0x24, 0xEF, 0xC8, 0x3A, 0xBA, 0x92, 0x21, 0xAA, 0x73, 0x12, 0x9A, 0x44, 0x03, 0x8A, 0x25, 0xC4, 0xC2, 0x0C, 0xFE, 0x59, 0xE0, 0xCB, 0xD3, 0x4E, - 0x0D, 0x9F, 0x21, 0x41, 0xA7, 0x88, 0x3B, 0xCE, 0xC5, 0x0D, 0x9B, 0x96, 0x24, 0x34, 0x49, 0x19, 0xF5, 0x99, 0xAB, 0x78, 0x98, 0x2D, 0x38, 0x18, - 0x0F, 0x5E, 0x3E, 0xC1, 0x90, 0xDF, 0x5B, 0x3B, 0x54, 0x91, 0x58, 0xD4, 0xB1, 0xEA, 0x4F, 0x35, 0xA6, 0x1A, 0xEF, 0x67, 0xB4, 0x98, 0x8C, 0xAD, - 0x94, 0xB8, 0xE5, 0xB4, 0xEB, 0x8E, 0x48, 0xF2, 0xAA, 0xEA, 0x23, 0x1B, 0x5A, 0x44, 0x10, 0xDB, 0xEA, 0x7F, 0x0A, 0xD4, 0xB4, 0x58, 0x62, 0x45, - 0x97, 0x3C, 0x43, 0x93, 0x04, 0x80, 0x42, 0x05, 0xB1, 0x30, 0x83, 0xF0, 0x59, 0x20, 0x83, 0xA7, 0x5D, 0x08, 0xBC, 0xA0, 0x4B, 0xA7, 0x08, 0x4A, - 0xCE, 0x42, 0xBE, 0x4D, 0x4B, 0x57, 0x4D, 0xD2, 0xDD, 0xCC, 0xD9, 0x63, 0xCC, 0xD8, 0x0D, 0x97, 0xC6, 0xAF, 0xD3, 0xBC, 0x96, 0x24, 0xC8, 0x2B, - 0x86, 0x1C, 0x54, 0xBE, 0xDB, 0x94, 0x59, 0x68, 0xEE, 0xC9, 0x38, 0x90, 0x24, 0x5D, 0xA4, 0x9D, 0xCB, 0x7E, 0xE0, 0xBE, 0xA7, 0x51, 0xE3, 0x58, - 0x1A, 0xB1, 0xF2, 0x20, 0xDF, 0x79, 0xD0, 0xDE, 0x0C, 0x67, 0xFE, 0xB6, 0x56, 0x6F, 0x70, 0x74, 0x68, 0x64, 0xDC, 0xA9, 0x6F, 0x7F, 0x12, 0xBC, - 0xE6, 0x41, 0x8D, 0x92, 0xF3, 0xDC, 0xAE, 0x53, 0x31, 0xA6, 0x2A, 0xEE, 0xC4, 0x3E, 0x81, 0x52, 0xC0, 0x69, 0xE8, 0x8E, 0xD8, 0x4E, 0x60, 0xD8, - 0x94, 0x08, 0xB7, 0xCF, 0x22, 0x08, 0x2E, 0xCC, 0x0B, 0xD0, 0xBC, 0xAC, 0x24, 0xC7, 0x53, 0xDC, 0xFC, 0x84, 0x0C, 0x41, 0x70, 0x2D, 0x66, 0xF4, - 0x63, 0x23, 0x83, 0x4D, 0xCA, 0x32, 0xBB, 0x32, 0xFF, 0x58, 0xAB, 0xCD, 0x51, 0x24, 0x99, 0xEA, 0x50, 0x7D, 0x91, 0x41, 0x9E, 0xC8, 0x5F, 0x25, - 0x20, 0xEA, 0x43, 0x89, 0x83, 0xC1, 0x3A, 0x9B, 0x24, 0x1D, 0x04, 0x66, 0xBD, 0x02, 0x2B, 0xBD, 0x72, 0xB6, 0x6E, 0x68, 0x4C, 0x8F, 0x55, 0xAE, - 0x7F, 0x8A, 0xD0, 0x3D, 0x96, 0x01, 0xD4, 0x6B, 0x8B, 0xE6, 0x2A, 0x29, 0xF3, 0x0F, 0x9B, 0x16, 0xA2, 0x39, 0x06, 0x07, 0x7B, 0xB6, 0xD4, 0x83, - 0xF5, 0x20, 0x9A, 0x9A, 0xC0, 0x0F, 0x8D, 0x9C, 0xB6, 0x20, 0x29, 0xED, 0x22, 0x1F, 0x3D, 0x02, 0x04, 0x34, 0xF4, 0xE4, 0xFC, 0x72, 0xF2, 0xF9, - 0xA5, 0x75, 0x4E, 0x0B, 0xAA, 0x6F, 0x26, 0xA2, 0xDC, 0xC7, 0xBE, 0xA0, 0xD7, 0x43, 0xEC, 0x61, 0x96, 0xE7, 0x4F, 0x42, 0x5C, 0xAD, 0x7B, 0x52, - 0x04, 0x46, 0x5C, 0x2D, 0xF2, 0x34, 0xA4, 0xB9, 0xA3, 0xDC, 0xFE, 0xA2, 0xE7, 0x93, 0x9E, 0xAF, 0xF8, 0xF2, 0xD0, 0xD3, 0x3D, 0xAA, 0x3E, 0x89, - 0x79, 0x3E, 0xDF, 0x74, 0x75, 0x0B, 0xC0, 0x86, 0xEE, 0xD3, 0x10, 0x9D, 0xC2, 0x65, 0x9F, 0xF9, 0xF2, 0xF1, 0x5A, 0x97, 0x0C, 0x38, 0x17, 0x49, - 0xA5, 0x0D, 0x16, 0x40, 0x1B, 0x63, 0xF5, 0x0A, 0x97, 0xE0, 0x4A, 0xE0, 0xB8, 0x55, 0x3E, 0x82, 0xF5, 0x02, 0x49, 0x9E, 0xBE, 0x62, 0xF5, 0xF0, - 0x47, 0x6E, 0x87, 0x9D, 0xA3, 0x7E, 0x58, 0x70, 0x1A, 0x23, 0x57, 0x1E, 0xA3, 0xC3, 0x7E, 0x46, 0x40, 0xA8, 0x52, 0x9D, 0x3C, 0x81, 0x8B, 0x1E, - 0x0B, 0x6A, 0x5F, 0x3A, 0xBC, 0xCF, 0x30, 0x37, 0xAC, 0x80, 0x80, 0x6E, 0xAF, 0xBB, 0xEC, 0xED, 0x4A, 0x1F, 0x33, 0x74, 0xBF, 0xA1, 0x86, 0x56, - 0x3F, 0xAB, 0x61, 0xD5, 0xAA, 0x6A, 0x54, 0xB5, 0xA9, 0x9A, 0x73, 0x9A, 0xB2, 0x65, 0x80, 0xC7, 0x29, 0x87, 0x7A, 0xBD, 0x9E, 0xB0, 0x36, 0x53, - 0xBB, 0x2E, 0xB7, 0x85, 0x88, 0x08, 0xBA, 0xA4, 0xE0, 0xF9, 0xBC, 0x48, 0x30, 0xAC, 0xD8, 0x64, 0xF6, 0x13, 0xB7, 0x1A, 0x06, 0x76, 0x22, 0x0B, - 0x53, 0x7F, 0x76, 0x31, 0xBB, 0xBD, 0xED, 0xD1, 0x6E, 0x20, 0x6F, 0x1F, 0xC1, 0x83, 0xB1, 0x06, 0x13, 0x28, 0xF0, 0xA6, 0xB9, 0xC2, 0xD0, 0x20, - 0x2D, 0x33, 0xD1, 0x52, 0x2E, 0x04, 0x94, 0x2C, 0x04, 0x00, 0x84, 0xB3, 0x9B, 0x68, 0x10, 0x99, 0x77, 0x79, 0x56, 0x5A, 0xC0, 0x4D, 0xFB, 0x80, - 0x36, 0x58, 0xCF, 0x03, 0x51, 0x96, 0x45, 0x32, 0x76, 0xE7, 0x33, 0x8B, 0xD2, 0xF5, 0x0C, 0xB4, 0x15, 0xC3, 0x85, 0xD6, 0x21, 0x38, 0xD6, 0xBE, - 0x2C, 0xD6, 0x31, 0xE6, 0xFC, 0x09, 0xC3, 0x7C, 0x58, 0x5E, 0x63, 0xDC, 0x6A, 0x83, 0x94, 0x7D, 0x68, 0x27, 0x9D, 0x09, 0x3D, 0x8B, 0x2D, 0x79, - 0xEA, 0x87, 0xA2, 0x5B, 0x54, 0xFA, 0xD6, 0xF0, 0xA1, 0xFE, 0x94, 0x32, 0xCC, 0x74, 0x97, 0xC0, 0xFE, 0xD8, 0x5D, 0xCA, 0x6E, 0xA5, 0x6D, 0xB1, - 0x04, 0x22, 0x34, 0xE9, 0x0D, 0x0A, 0xA5, 0xC1, 0xC2, 0xCC, 0xEB, 0xB3, 0xC0, 0x00, 0x4F, 0x3B, 0x04, 0x78, 0x12, 0xFB, 0x4E, 0x11, 0x1A, 0x38, - 0x0B, 0xD7, 0x36, 0x2D, 0x17, 0x35, 0x49, 0x57, 0x5C, 0x1A, 0xA3, 0x98, 0xBE, 0xB4, 0x24, 0x21, 0x5D, 0x31, 0xC4, 0x35, 0xCF, 0x98, 0x67, 0x98, - 0xD3, 0xCC, 0x4F, 0x7C, 0x99, 0x40, 0xAC, 0x71, 0xF9, 0x87, 0x0A, 0xE6, 0x4C, 0x44, 0xFD, 0x1A, 0x6E, 0x10, 0xB6, 0xAF, 0x11, 0x28, 0x44, 0x51, - 0x49, 0x88, 0x17, 0xF4, 0x4B, 0x94, 0x5E, 0x18, 0x1C, 0x7C, 0xA6, 0x4B, 0x8F, 0xD2, 0x05, 0xB7, 0x12, 0xCC, 0x01, 0x7D, 0x3B, 0x74, 0xD0, 0x60, - 0xAB, 0x99, 0xD5, 0x7B, 0x75, 0xAB, 0x3A, 0xFF, 0x32, 0xC8, 0xC1, 0xAD, 0x9A, 0xBA, 0x38, 0x4F, 0x05, 0xA2, 0x44, 0x64, 0x41, 0xC1, 0xA2, 0xE8, - 0x96, 0xA1, 0xF3, 0x81, 0x44, 0x18, 0x7E, 0x25, 0x28, 0xB6, 0x06, 0x7B, 0x8C, 0xAB, 0x67, 0x9E, 0xF8, 0x4F, 0x7F, 0x21, 0x5D, 0x05, 0x7D, 0x93, - 0x8E, 0x4A, 0xB7, 0xA5, 0x0B, 0xD3, 0x9B, 0xE9, 0xB0, 0x1D, 0x67, 0x95, 0xFA, 0x0F, 0x79, 0xF2, 0xBB, 0x07, 0x7A, 0x01, 0x58, 0xF1, 0xFF, 0x33, - 0x34, 0xD4, 0x6B, 0x03, 0xEA, 0x54, 0x75, 0x8A, 0x4B, 0x81, 0xEF, 0xA2, 0xB3, 0x0A, 0x14, 0x7F, 0x22, 0x4C, 0xE2, 0x6C, 0xFB, 0xE1, 0xD6, 0x37, - 0x5C, 0xD8, 0x0B, 0x0A, 0xCD, 0x45, 0x8D, 0x5D, 0x7C, 0xF3, 0xFB, 0xDB, 0xBB, 0x9E, 0xEF, 0x3E, 0x67, 0x2E, 0x89, 0x27, 0x3A, 0xD2, 0xC2, 0x41, - 0x20, 0xA5, 0xE9, 0x1F, 0xCD, 0x6A, 0x1E, 0xF9, 0x53, 0x0F, 0x96, 0x3B, 0x8E, 0xE7, 0x06, 0x4A, 0x24, 0xE6, 0x0A, 0x3C, 0xCC, 0x95, 0x65, 0x92, - 0x62, 0x14, 0xD8, 0x48, 0x26, 0x60, 0x7C, 0x9C, 0x7C, 0x89, 0x1B, 0xF9, 0x74, 0xFB, 0x9A, 0xB7, 0x6D, 0x4D, 0xAB, 0x7D, 0x21, 0x49, 0x1B, 0x0C, - 0x45, 0xBB, 0x0E, 0x31, 0xEB, 0x31, 0x40, 0xB6, 0xC7, 0x38, 0x76, 0x47, 0x02, 0x2C, 0x97, 0xC8, 0xEB, 0xD4, 0xDA, 0xD5, 0x84, 0x52, 0xEB, 0x01, - 0x8F, 0xD3, 0x53, 0x15, 0xA6, 0xAA, 0xF4, 0x39, 0x22, 0xBC, 0x38, 0x6B, 0xA9, 0x1B, 0x20, 0xB5, 0x0D, 0xC9, 0x8B, 0x1A, 0x39, 0x55, 0x9A, 0xB7, - 0xC7, 0x0C, 0x3D, 0xD2, 0x6D, 0x45, 0x2C, 0x63, 0xCF, 0xC7, 0xC4, 0x81, 0xC0, 0xE0, 0x8B, 0x5F, 0xF6, 0x76, 0x5C, 0x74, 0x43, 0x14, 0xAF, 0x5A, - 0xD4, 0x3C, 0xA3, 0x6F, 0x2B, 0x33, 0x58, 0xBF, 0x29, 0x49, 0x3D, 0xD2, 0x70, 0x00, 0xCC, 0xB2, 0x4E, 0x62, 0xF2, 0x4C, 0xEC, 0xE2, 0x4A, 0x9E, - 0x4C, 0xB4, 0x45, 0x60, 0x20, 0x51, 0xC3, 0x8F, 0x58, 0x1E, 0x8A, 0xE0, 0xE5, 0x86, 0xCC, 0x7A, 0x82, 0x10, 0x42, 0x7F, 0x18, 0x9F, 0x90, 0x37, - 0x9E, 0xF0, 0x58, 0x89, 0x6E, 0xAD, 0x25, 0x01, 0x5F, 0x9E, 0x40, 0xEA, 0x33, 0x37, 0x3A, 0x22, 0x17, 0xE5, 0xE2, 0x20, 0x0F, 0x05, 0xC2, 0x03, - 0x6D, 0x9B, 0xB3, 0xF0, 0x1A, 0xFC, 0xBC, 0xD0, 0xC1, 0x53, 0x01, 0x26, 0xFE, 0xB0, 0xB1, 0xB3, 0xC3, 0x2C, 0x27, 0x0B, 0xD3, 0x4D, 0x15, 0xCA, - 0x9A, 0xBC, 0xC0, 0x99, 0xB3, 0x5F, 0x98, 0xA8, 0x8A, 0x97, 0xCC, 0x09, 0x2C, 0x87, 0x43, 0xF5, 0x96, 0x2B, 0x1B, 0x56, 0xB0, 0x19, 0x2B, 0x93, - 0x18, 0x29, 0x6B, 0x46, 0x27, 0xCD, 0x46, 0x25, 0xA7, 0x5C, 0xC8, 0x23, 0xED, 0x5C, 0x14, 0x80, 0x04, 0xDB, 0x33, 0xBB, 0x4F, 0x46, 0x46, 0xAD, - 0xE0, 0x5A, 0xFC, 0x8B, 0xF8, 0x4F, 0x60, 0xDD, 0xB3, 0x6E, 0x59, 0x53, 0xC3, 0xFA, 0x77, 0xBA, 0xBC, 0x40, 0xDD, 0xDA, 0x5C, 0x96, 0x5A, 0x06, - 0xC0, 0x01, 0x7E, 0x71, 0x69, 0x8B, 0x77, 0x01, 0x8F, 0x18, 0xD0, 0x3A, 0x03, 0xCF, 0x44, 0x0B, 0x98, 0xB5, 0x08, 0xE0, 0xAB, 0x57, 0x36, 0x9D, - 0x2F, 0xDB, 0x40, 0x24, 0x93, 0x19, 0xC3, 0xF9, 0x14, 0x83, 0x87, 0x59, 0xE9, 0xC1, 0xE4, 0x71, 0x38, 0xE9, 0xAF, 0x07, 0x22, 0xFA, 0xE8, 0x0D, - 0x76, 0xAF, 0x09, 0xBB, 0x21, 0x7A, 0xB8, 0x0A, 0x73, 0x6A, 0x09, 0x64, 0x24, 0xFD, 0x69, 0x40, 0x2B, 0x70, 0x2A, 0xFD, 0x2D, 0xC0, 0x20, 0xF4, - 0xB6, 0x74, 0x1A, 0xC4, 0x70, 0x3E, 0x0A, 0x1A, 0x1A, 0x23, 0xEB, 0x10, 0x2A, 0xE0, 0x20, 0x16, 0x22, 0xA6, 0xDE, 0xBE, 0xB8, 0x77, 0x3E, 0xBC, - 0xE5, 0xFF, 0x7F, 0xB3, 0xFA, 0x58, 0x1D, 0xBA, 0xE1, 0xFC, 0x3B, 0x3E, 0xEA, 0xA7, 0xD8, 0x5F, 0xE6, 0x32, 0x55, 0x31, 0xF0, 0x17, 0xBC, 0x6E, - 0xE8, 0xD9, 0xEC, 0x85, 0xE9, 0x38, 0x01, 0xD4, 0xCA, 0xF7, 0x61, 0xFA, 0xD1, 0xFF, 0xA0, 0xB3, 0xD0, 0x75, 0xE8, 0x2D, 0x30, 0x7F, 0xC2, 0x12, - 0x7D, 0x89, 0x09, 0x46, 0x60, 0xCC, 0x64, 0xD3, 0x3E, 0xC2, 0x03, 0xE3, 0x57, 0xB4, 0x62, 0x12, 0x1F, 0x88, 0x51, 0x20, 0x62, 0x4A, 0x0E, 0x03, - 0x00, 0xF1, 0x85, 0x59, 0x41, 0x04, 0xEB, 0x9E, 0xA0, 0x4F, 0xD2, 0x5B, 0xBF, 0x9A, 0x23, 0x81, 0xE9, 0x56, 0x0D, 0x99, 0xF2, 0x0F, 0x80, 0xF3, - 0x3C, 0xCC, 0xEF, 0x20, 0x37, 0x71, 0xC2, 0x9D, 0xE1, 0xDB, 0xA6, 0x9D, 0x44, 0xE5, 0x1F, 0xB1, 0xC8, 0x93, 0xFF, 0xEA, 0x1D, 0xC4, 0xB5, 0xDF, - 0x48, 0x01, 0xAF, 0xC4, 0xFB, 0x18, 0x56, 0x1C, 0x6A, 0x0D, 0x75, 0x4A, 0x81, 0x7F, 0x72, 0xD7, 0x7C, 0x3B, 0x9D, 0xF1, 0xE4, 0x1B, 0x57, 0xE7, - 0x70, 0x76, 0xF2, 0xE3, 0x37, 0x06, 0x2D, 0x11, 0x5F, 0x32, 0x71, 0x54, 0xAA, 0xD7, 0xEF, 0x41, 0x02, 0x96, 0x97, 0xB3, 0x68, 0xB3, 0x52, 0xA8, - 0x3A, 0xA4, 0x12, 0xB0, 0x16, 0xA4, 0xF4, 0x35, 0x38, 0x4C, 0x44, 0xFC, 0xC0, 0x39, 0xB4, 0xAA, 0x5E, 0x08, 0xF6, 0x24, 0x52, 0x8E, 0x75, 0x39, - 0x67, 0xBA, 0x94, 0xCF, 0xF1, 0x62, 0xC5, 0xF2, 0x7A, 0x6E, 0x88, 0xFC, 0x0E, 0x3D, 0x0F, 0xE4, 0x91, 0xB7, 0x1E, 0xD8, 0x4F, 0x62, 0xC9, 0x83, - 0xBF, 0xE6, 0xF9, 0x05, 0x7A, 0xE3, 0x27, 0x1D, 0xA5, 0xBD, 0x65, 0x70, 0x3D, 0xC1, 0x1E, 0x08, 0x14, 0x45, 0x05, 0x34, 0xD8, 0x5F, 0xA5, 0x28, - 0x5F, 0x77, 0xAA, 0x17, 0x0D, 0x71, 0xA7, 0xD4, 0x73, 0x19, 0xFE, 0x9E, 0x39, 0xE1, 0xD9, 0x0F, 0xAA, 0xEB, 0xE7, 0xC8, 0x60, 0xA3, 0xDC, 0x86, - 0xAC, 0xE6, 0xF1, 0x73, 0x1F, 0xE2, 0xBC, 0xD1, 0x8C, 0x9E, 0x0C, 0x4A, 0x5F, 0xD2, 0x66, 0x4C, 0x67, 0xE3, 0xA6, 0x63, 0xC1, 0xBB, 0xA2, 0x6A, - 0x03, 0xB4, 0xD1, 0x91, 0xC1, 0xD6, 0x90, 0xD1, 0xCB, 0x30, 0xAB, 0xD1, 0xED, 0xF9, 0x4F, 0xD1, 0x3C, 0x0E, 0x07, 0xA0, 0xFD, 0x6A, 0x61, 0x92, - 0xFE, 0x34, 0x83, 0x03, 0xA2, 0xBB, 0xAC, 0x12, 0xD4, 0x63, 0x8B, 0xAD, 0x5E, 0xF7, 0x0C, 0x99, 0x4D, 0x79, 0xCD, 0xE1, 0x40, 0xC9, 0xC3, 0xF2, - 0x3F, 0xFA, 0x37, 0x90, 0xD5, 0x2E, 0xD4, 0xBC, 0x20, 0x93, 0x62, 0xD0, 0x73, 0xC7, 0x16, 0xF3, 0x96, 0x79, 0x6A, 0xF4, 0xBC, 0x57, 0xBF, 0x0F, - 0x19, 0x51, 0xFA, 0x38, 0x55, 0xFA, 0x00, 0x0A, 0x30, 0x7F, 0x11, 0x60, 0x5F, 0xB5, 0xB4, 0x07, 0x69, 0xB0, 0x57, 0xE0, 0x41, 0xB3, 0xC2, 0xFC, - 0xFF, 0xAF, 0xFE, 0xEC, 0xCF, 0x36, 0xD1, 0xA5, 0x99, 0xAF, 0xF3, 0xCD, 0x09, 0xBC, 0x3D, 0xBA, 0xC9, 0x1D, 0x7F, 0x8F, 0x73, 0xED, 0xA7, 0x16, - 0xEC, 0x19, 0xAC, 0xF0, 0x3A, 0x48, 0xEF, 0x49, 0x7F, 0x8E, 0x56, 0x23, 0xB5, 0xAE, 0xDA, 0x53, 0x24, 0x83, 0x7A, 0xCE, 0x95, 0x04, 0xBB, 0xA6, - 0xF3, 0x30, 0xE9, 0xA6, 0xBF, 0x10, 0xE0, 0x68, 0x23, 0x58, 0x15, 0x6E, 0xA1, 0xB6, 0x55, 0xD7, 0xD7, 0x42, 0x4A, 0x9F, 0xEF, 0x38, 0xFF, 0xA5, - 0x8B, 0x20, 0x47, 0x2F, 0x8A, 0x19, 0x85, 0x86, 0xA8, 0x03, 0x03, 0x11, 0x12, 0x63, 0x49, 0x41, 0x92, 0x61, 0x0C, 0x03, 0xE8, 0x21, 0x8E, 0xA4, - 0x20, 0x04, 0x42, 0x51, 0x96, 0xA6, 0xD4, 0xD9, 0x01, 0x23, 0xF0, 0x60, 0xF7, 0x21, 0x84, 0xA2, 0x10, 0x06, 0x23, 0x11, 0x84, 0x81, 0x10, 0x83, - 0x14, 0x23, 0x44, 0x40, 0x04, 0xC4, 0x09, 0x09, 0x21, 0x86, 0x10, 0x66, 0x66, 0x68, 0x64, 0x1E, 0x08, 0xCF, 0x0B, 0x97, 0xFD, 0xF5, 0x94, 0x09, - 0xD2, 0xA8, 0xDA, 0xCD, 0x90, 0x5D, 0x79, 0x8D, 0xA8, 0xB1, 0x15, 0x48, 0x9A, 0x6A, 0xDA, 0x00, 0xD6, 0xFE, 0xDA, 0xF0, 0x63, 0x21, 0x88, 0xB5, - 0x0D, 0x86, 0x80, 0x18, 0x9D, 0x2D, 0x5F, 0x34, 0x66, 0x07, 0xEA, 0x69, 0x6F, 0x64, 0xE9, 0xD5, 0xB7, 0x85, 0xDC, 0x12, 0x75, 0xD8, 0x56, 0xEC, - 0xEA, 0x77, 0xD7, 0x2E, 0xBF, 0x3F, 0xA6, 0xFE, 0x0C, 0x07, 0x9A, 0x0F, 0xA1, 0xB8, 0xB1, 0x4D, 0x5A, 0xA2, 0x31, 0xBF, 0xFF, 0xF1, 0x0E, 0xC4, - 0x14, 0xDD, 0xE8, 0x9D, 0x8C, 0xE1, 0x48, 0x33, 0x20, 0x14, 0x7E, 0x13, 0x6A, 0x2A, 0x38, 0x91, 0x7D, 0xC3, 0x2D, 0x5A, 0xE3, 0x6A, 0x66, 0xE2, - 0x3E, 0x90, 0x8E, 0x89, 0xA1, 0x05, 0xE7, 0x47, 0x05, 0x2F, 0x06, 0x46, 0x9C, 0x7D, 0xFD, 0x23, 0x72, 0x06, 0xE3, 0x7E, 0x2B, 0x30, 0x64, 0xE2, - 0x5C, 0xF8, 0xA2, 0x29, 0x3F, 0xCC, 0xAF, 0x60, 0x88, 0x3C, 0x9E, 0x02, 0x32, 0xCB, 0x62, 0x46, 0xA6, 0x1D, 0xA8, 0xC7, 0xA8, 0x9C, 0xBB, 0xFD, - 0x46, 0xEC, 0xCB, 0x19, 0x8B, 0x23, 0xA8, 0xD9, 0xF4, 0x4F, 0x34, 0xC3, 0x03, 0xE6, 0x1F, 0x82, 0x63, 0x66, 0x1A, 0x72, 0xC0, 0xBE, 0xA1, 0x1D, - 0x2F, 0x70, 0x5B, 0xE0, 0xF8, 0x48, 0xAF, 0xCB, 0xFA, 0x4C, 0x68, 0x27, 0x6E, 0x4F, 0x98, 0x30, 0x79, 0x45, 0xB7, 0xF9, 0xED, 0xBF, 0x63, 0x61, - 0x4E, 0xB4, 0x73, 0x26, 0xB3, 0xEF, 0x1A, 0x6B, 0x44, 0x66, 0x05, 0x79, 0x15, 0xC0, 0xBC, 0xAC, 0x8D, 0x2A, 0x9E, 0xD5, 0x5D, 0xCC, 0x62, 0x7B, - 0xD0, 0xA9, 0x90, 0x3B, 0x6A, 0x47, 0xBC, 0x5D, 0x5A, 0x18, 0x1A, 0xE0, 0x60, 0x77, 0x69, 0x96, 0x41, 0x64, 0xF6, 0x05, 0xE0, 0x85, 0x16, 0xFF, - 0x79, 0xEC, 0xCA, 0xEF, 0xC5, 0xD9, 0x72, 0xF2, 0xB3, 0x42, 0x2F, 0x3A, 0x68, 0x12, 0xF1, 0x2A, 0xA7, 0xC4, 0xA0, 0xCC, 0x3E, 0x44, 0x6C, 0x8C, - 0x65, 0x3E, 0xF0, 0x90, 0x3B, 0xC4, 0x96, 0x42, 0x94, 0x3D, 0x5F, 0xC8, 0x7C, 0xC4, 0x56, 0x12, 0x72, 0xE1, 0x42, 0x5A, 0x1B, 0x8E, 0xA7, 0x6D, - 0x3A, 0xB9, 0x42, 0xBB, 0x79, 0x1D, 0xBB, 0xDC, 0x71, 0x75, 0x4B, 0xC3, 0x43, 0x6C, 0x2B, 0x80, 0x5A, 0x64, 0x03, 0x23, 0xA3, 0xA3, 0x2C, 0x46, - 0x14, 0x36, 0x8B, 0x28, 0x21, 0xB7, 0xB4, 0x50, 0x43, 0xC3, 0xB8, 0x30, 0x9E, 0xB7, 0xA3, 0x36, 0x45, 0x6B, 0xBB, 0xCE, 0x5D, 0x25, 0x68, 0x28, - 0x46, 0x2B, 0x65, 0x7B, 0x08, 0x73, 0x40, 0x77, 0xDC, 0xE3, 0x32, 0x83, 0xCB, 0x05, 0x0F, 0x8E, 0x9C, 0x9B, 0x65, 0xA1, 0x9F, 0x68, 0xC4, 0xB3, - 0x68, 0x56, 0x8D, 0x5A, 0xBC, 0xE2, 0x71, 0xC9, 0x13, 0x7A, 0x20, 0x4C, 0x55, 0xE4, 0x7E, 0xAC, 0xDF, 0x6D, 0x3F, 0xDB, 0x7F, 0x3D, 0x8B, 0x70, - 0x74, 0x1F, 0xA6, 0xDA, 0x8E, 0x35, 0xDA, 0x38, 0x16, 0x38, 0x26, 0x42, 0xDD, 0xD0, 0x8D, 0x8F, 0x5C, 0xED, 0x64, 0xB8, 0x20, 0x49, 0xBC, 0x3B, - 0x0D, 0xF1, 0x7D, 0x18, 0xC0, 0x8D, 0xEA, 0xB4, 0x8C, 0x35, 0xFA, 0x02, 0xB6, 0xF6, 0x1E, 0x28, 0x58, 0xD4, 0xD2, 0x3E, 0x80, 0xC3, 0xF1, 0x91, - 0x66, 0xFD, 0x67, 0x3D, 0x66, 0x1E, 0x6A, 0x5E, 0x65, 0x1E, 0xE5, 0x9D, 0xF4, 0x3E, 0x7D, 0xFC, 0x23, 0x92, 0x58, 0x70, 0x7F, 0x9C, 0x07, 0x1A, - 0x31, 0xBE, 0x52, 0x5C, 0x58, 0x7C, 0xE2, 0x7A, 0x4E, 0x3E, 0x86, 0xAD, 0x15, 0xC2, 0x85, 0xE0, 0xEB, 0xD4, 0x42, 0xFE, 0x04, 0x26, 0x4D, 0x33, - 0x28, 0x62, 0x09, 0xF8, 0x6E, 0xE2, 0x10, 0xF6, 0xED, 0x7E, 0x0C, 0x74, 0x14, 0xAE, 0xAC, 0x31, 0x78, 0x9C, 0x85, 0xFF, 0x89, 0x51, 0x38, 0xD4, - 0x7D, 0x3F, 0x98, 0xB5, 0xF2, 0x4A, 0x66, 0x84, 0xCB, 0x21, 0xE8, 0xBB, 0x46, 0x8B, 0x30, 0x3D, 0x09, 0x45, 0x62, 0x20, 0x23, 0xAE, 0x4D, 0xA8, - 0xE7, 0x62, 0x8C, 0xD9, 0xF5, 0xEC, 0x23, 0x0A, 0x66, 0x41, 0xE1, 0x22, 0x91, 0x04, 0x9B, 0xAD, 0x8F, 0x14, 0xF0, 0xF1, 0x01, 0xE2, 0xA5, 0xCB, - 0x0D, 0xBB, 0x3D, 0xF8, 0x49, 0xAB, 0xE0, 0xE9, 0x7C, 0x40, 0x74, 0xBF, 0x91, 0x95, 0x63, 0x6C, 0xA5, 0x5D, 0xB4, 0x70, 0xF0, 0xE0, 0x02, 0xDB, - 0x8F, 0x4A, 0x66, 0xD4, 0xD0, 0x37, 0xF3, 0x96, 0x3A, 0x4A, 0xE4, 0xF9, 0xA2, 0xEE, 0xD3, 0x9D, 0x35, 0x81, 0x41, 0x31, 0x85, 0x3D, 0x93, 0x3B, - 0x04, 0x63, 0x1C, 0x80, 0xBF, 0x3B, 0x5D, 0xB7, 0x1A, 0xC4, 0x71, 0xF5, 0xCE, 0xB2, 0xF5, 0x5D, 0xB2, 0xB7, 0xFF, 0xE5, 0x16, 0x87, 0xB7, 0x7C, - 0x40, 0x7B, 0x77, 0xCC, 0x45, 0x0D, 0x97, 0xCC, 0x9C, 0x4F, 0xFA, 0x7D, 0x52, 0x0B, 0x2E, 0x17, 0x7E, 0x77, 0x92, 0xF0, 0x8B, 0xA2, 0x47, 0xBB, - 0x21, 0x9D, 0xD9, 0xB4, 0xEF, 0x5E, 0x9D, 0xE1, 0xFC, 0xCB, 0x2B, 0x34, 0x31, 0x04, 0x56, 0x01, 0xE9, 0x19, 0x45, 0x21, 0x74, 0x8C, 0xBA, 0x3E, - 0xE0, 0x77, 0x6D, 0x06, 0x7B, 0x5B, 0x63, 0xC1, 0x06, 0xAD, 0x53, 0xAC, 0x09, 0xFD, 0xAD, 0x06, 0x1B, 0xC7, 0x73, 0x92, 0x6E, 0x7C, 0xD9, 0x37, - 0x5C, 0x11, 0x76, 0x6C, 0x81, 0xF3, 0xB4, 0xC4, 0x3D, 0x8A, 0x5E, 0xE7, 0xB8, 0x4C, 0x6D, 0xE3, 0xFC, 0x91, 0xB8, 0x44, 0x91, 0xA7, 0x2F, 0x55, - 0xD1, 0x01, 0x0F, 0x1F, 0x96, 0x51, 0xB6, 0xD9, 0x4B, 0xE5, 0x3B, 0xB3, 0xB4, 0xE5, 0x89, 0x7C, 0x76, 0x8B, 0xE6, 0xAD, 0x24, 0xAF, 0x1F, 0xBC, - 0x91, 0x3E, 0x48, 0x1F, 0xD4, 0x15, 0xC5, 0xB4, 0x12, 0xF6, 0x3F, 0xD9, 0x61, 0xED, 0x30, 0x48, 0x5D, 0x60, 0xD9, 0xB1, 0x63, 0x58, 0xD8, 0xDD, - 0x3F, 0x9C, 0x96, 0x7C, 0x00, 0x98, 0xE7, 0x45, 0xF0, 0xCE, 0x40, 0x22, 0x93, 0xB8, 0xEF, 0x38, 0x8F, 0x05, 0xDF, 0x64, 0xCA, 0xB4, 0xCB, 0x46, - 0x04, 0x99, 0x7D, 0xDE, 0xC4, 0xDA, 0xE7, 0x30, 0x02, 0x06, 0x9C, 0xCD, 0xEB, 0x31, 0x81, 0xD2, 0xBC, 0x9E, 0x10, 0x7D, 0x20, 0x3C, 0xAB, 0xBC, - 0x61, 0xFB, 0x87, 0x25, 0x8E, 0xDB, 0xD8, 0x78, 0x84, 0x59, 0x6C, 0xCD, 0x58, 0x85, 0x6A, 0x43, 0x9B, 0xF8, 0x26, 0xB9, 0xA2, 0x2E, 0x31, 0x31, - 0x6F, 0x6C, 0xBA, 0x36, 0xD5, 0xBA, 0xB5, 0xB0, 0xD3, 0x8E, 0xCA, 0x3E, 0xC3, 0x2C, 0x57, 0x00, 0xAC, 0x9A, 0x1A, 0xD5, 0x14, 0xFF, 0x40, 0x75, - 0x60, 0x64, 0x47, 0x6C, 0xDB, 0x5D, 0x39, 0x1F, 0x03, 0x4D, 0x23, 0xD9, 0x26, 0x27, 0x4D, 0x80, 0x06, 0xFB, 0x3A, 0x15, 0x7B, 0x1F, 0x70, 0x8D, - 0x5D, 0x49, 0x97, 0x08, 0x0D, 0x0C, 0xFF, 0x32, 0x88, 0x4C, 0xFC, 0x1C, 0x91, 0x0C, 0xFB, 0x33, 0xFE, 0x1E, 0x3B, 0xC3, 0x86, 0x49, 0x8A, 0xF0, - 0xC8, 0x5B, 0x75, 0x75, 0x7F, 0xBB, 0xCC, 0x9A, 0x53, 0x63, 0xBE, 0xD0, 0x9E, 0xCD, 0x5F, 0xEF, 0xD8, 0x1C, 0x5B, 0x0D, 0x69, 0xE3, 0xB4, 0x81, - 0xF9, 0x0C, 0xA2, 0xE3, 0x1C, 0x94, 0xB7, 0xE9, 0x15, 0x33, 0xB2, 0xDD, 0xEB, 0x71, 0xAD, 0x59, 0x66, 0xB6, 0xF6, 0x7A, 0x2C, 0x19, 0x09, 0xC1, - 0xF1, 0xCD, 0xC4, 0x8E, 0x11, 0xAD, 0x37, 0x65, 0xA5, 0xDE, 0x74, 0xE8, 0xEB, 0xFD, 0x56, 0xC9, 0x41, 0xC8, 0x5B, 0x1E, 0x8F, 0x0D, 0xB1, 0xC9, - 0x2C, 0x4B, 0x57, 0x52, 0x6D, 0x9B, 0xEB, 0x82, 0xA9, 0xAA, 0xDE, 0x8B, 0xC9, 0xA4, 0x28, 0xCE, 0xD1, 0xD5, 0x8A, 0x4B, 0xC4, 0x13, 0x91, 0x90, - 0xFF, 0x79, 0x4B, 0xC8, 0x9B, 0x45, 0xD7, 0x1E, 0x16, 0x6C, 0xFE, 0x6D, 0x94, 0x59, 0x6D, 0x3C, 0x07, 0x56, 0x30, 0x8A, 0x5E, 0x3A, 0x54, 0x83, - 0xB4, 0x5A, 0x97, 0x9D, 0xFD, 0x94, 0x87, 0x6E, 0x7D, 0x2A, 0x5E, 0x1D, 0x0E, 0xB3, 0x48, 0xAF, 0xE2, 0x43, 0x0D, 0xF2, 0x5B, 0x8B, 0xD1, 0x0A, - 0xA7, 0xCD, 0xB3, 0xF8, 0x20, 0x08, 0x6E, 0xDC, 0xE5, 0xBE, 0x80, 0xDB, 0xC8, 0x58, 0xFB, 0x61, 0xEC, 0x89, 0x6B, 0x7F, 0x8B, 0x00, 0xC8, 0x0B, - 0x7F, 0xDA, 0xC7, 0x38, 0x6B, 0xC2, 0x76, 0x3D, 0x1A, 0x4B, 0x84, 0xD9, 0xE0, 0x64, 0x99, 0xD5, 0x2D, 0xFE, 0x39, 0xD9, 0x1C, 0xA9, 0xAB, 0x43, - 0x96, 0xBF, 0xB4, 0x43, 0xEF, 0xD9, 0xF3, 0xF8, 0xA8, 0xDF, 0xF8, 0x88, 0xDF, 0xB2, 0x56, 0x17, 0xA9, 0xC0, 0x34, 0xA1, 0xC6, 0xD1, 0xDD, 0x0D, - 0xAD, 0x50, 0x1F, 0xB7, 0x8F, 0x08, 0x90, 0x7E, 0x63, 0xDB, 0x42, 0x92, 0x2D, 0x1A, 0xC5, 0xDB, 0xB7, 0x53, 0xB6, 0xE8, 0x9A, 0x71, 0x5C, 0xF4, - 0xD6, 0xF1, 0x99, 0x44, 0xC7, 0xDF, 0x71, 0xA1, 0x6F, 0xB3, 0x64, 0xA2, 0x98, 0x42, 0xF5, 0x8B, 0xDF, 0x42, 0x66, 0xB4, 0xB1, 0x62, 0xF1, 0x2C, - 0x78, 0x9D, 0x1C, 0x98, 0x8B, 0xB7, 0x76, 0xDB, 0xD4, 0xD0, 0xDA, 0x76, 0x20, 0xB0, 0xE6, 0xD4, 0xFB, 0x3C, 0x8A, 0xB9, 0xEB, 0x53, 0xA0, 0xCF, - 0xCD, 0xF9, 0xF6, 0x45, 0x24, 0x5A, 0xC2, 0xE8, 0x20, 0x62, 0xA2, 0x9D, 0x1D, 0x83, 0x5F, 0xAF, 0x79, 0x47, 0x3F, 0x8C, 0x67, 0x33, 0xB8, 0x90, - 0x2C, 0xA6, 0xA3, 0xA8, 0xE1, 0x43, 0xC1, 0xFF, 0xC5, 0x4F, 0x06, 0xF7, 0xCC, 0x1E, 0x0A, 0x6D, 0xFD, 0x6E, 0xCF, 0x5A, 0xC8, 0x15, 0x61, 0x50, - 0x1F, 0xA1, 0xA0, 0xCC, 0x88, 0x00, 0x13, 0xBA, 0x00, 0x1B, 0x5F, 0x58, 0xCE, 0x02, 0x5D, 0x7A, 0x80, 0xBC, 0x1F, 0x7A, 0xF1, 0x65, 0x4B, 0x4F, - 0x65, 0x2D, 0x8A, 0xA1, 0xB0, 0xAC, 0x56, 0xE2, 0x74, 0xC9, 0x31, 0x8E, 0x06, 0xFB, 0xA7, 0x38, 0xA3, 0xE5, 0x90, 0xA3, 0xFB, 0x34, 0x4D, 0x0C, - 0xE1, 0xB2, 0x77, 0xBC, 0x39, 0xCE, 0x2D, 0x85, 0xC2, 0x45, 0x6D, 0x0E, 0x1E, 0x26, 0x2C, 0xFC, 0x3E, 0xDC, 0x37, 0x55, 0x83, 0x4C, 0x6E, 0x3A, - 0xFA, 0x6E, 0x97, 0xF2, 0x90, 0xB8, 0x97, 0x9C, 0x4B, 0x7B, 0x79, 0x9D, 0xF9, 0x5E, 0x3E, 0x58, 0x8F, 0x20, 0xF1, 0x02, 0xEA, 0xAA, 0x25, 0x10, - 0x36, 0xC3, 0x78, 0x69, 0x8C, 0x13, 0xEE, 0xF1, 0xE0, 0xED, 0x61, 0xC1, 0xBD, 0x01, 0x18, 0xC7, 0x76, 0xFA, 0xE7, 0x05, 0x2E, 0xB7, 0x9F, 0x23, - 0xFC, 0xEC, 0x81, 0x29, 0xE1, 0x30, 0xE0, 0x69, 0xDE, 0x85, 0x75, 0xB0, 0xBD, 0xB5, 0x4C, 0xC0, 0x3C, 0x4D, 0xD1, 0x93, 0x5F, 0xA2, 0x71, 0x9E, - 0x5B, 0x99, 0x3B, 0xBF, 0x3B, 0x92, 0xAE, 0x62, 0x4A, 0x28, 0x03, 0x63, 0x45, 0x2D, 0x62, 0xC4, 0xBB, 0xBE, 0xB9, 0xF6, 0x81, 0x42, 0xC9, 0x10, - 0x13, 0x11, 0x11, 0x10, 0xE1, 0xA3, 0xF2, 0x92, 0x5F, 0xD6, 0x43, 0xD9, 0x36, 0xCE, 0x0F, 0x5B, 0x0F, 0x4C, 0xE1, 0xD8, 0xE8, 0xF8, 0x17, 0x49, - 0x18, 0x08, 0xEE, 0x9E, 0xB4, 0xAF, 0xB1, 0x4A, 0x69, 0xCB, 0xCE, 0x59, 0x8E, 0x1D, 0x53, 0x98, 0x33, 0x35, 0xF0, 0x39, 0x9F, 0x1A, 0x32, 0x8A, - 0x66, 0x3D, 0xCD, 0xFB, 0xD0, 0xA3, 0x41, 0x72, 0xCE, 0x68, 0xE0, 0xFE, 0x8A, 0x8A, 0x66, 0xAC, 0x6E, 0x9B, 0xF9, 0x59, 0x87, 0x19, 0x98, 0x23, - 0x31, 0x06, 0xE8, 0x2A, 0x0C, 0xE2, 0x1D, 0x0D, 0x01, 0x9D, 0x55, 0x35, 0x59, 0x2C, 0xEF, 0x1B, 0xB0, 0xAA, 0xCB, 0xDF, 0x69, 0x81, 0x02, 0x22, - 0x15, 0x84, 0xE1, 0x54, 0x9D, 0x1E, 0xFF, 0xAB, 0xA3, 0xCC, 0x87, 0x24, 0x0D, 0x8C, 0x03, 0x1C, 0xB5, 0xEE, 0xDE, 0xD4, 0xE0, 0xCD, 0xBF, 0xB0, - 0x39, 0x97, 0xE0, 0xBE, 0xD9, 0x1D, 0x51, 0x11, 0x7B, 0xCC, 0x73, 0xEC, 0xD7, 0xCC, 0x92, 0x70, 0x82, 0xC4, 0x2D, 0xDB, 0xBD, 0x2A, 0xCC, 0xA3, - 0xCB, 0x6E, 0x88, 0xF0, 0x1F, 0xAA, 0x80, 0xF2, 0x7B, 0xCB, 0x92, 0x7D, 0x80, 0x1E, 0x73, 0x88, 0x28, 0x7C, 0x89, 0xD2, 0x0A, 0x3B, 0xBD, 0x09, - 0xC4, 0x19, 0xA6, 0x31, 0x18, 0x74, 0x78, 0x90, 0x8B, 0xC5, 0xB6, 0x47, 0x8E, 0x03, 0xFE, 0x37, 0x15, 0x1D, 0x0A, 0x7A, 0x26, 0x1C, 0xEC, 0x74, - 0x9F, 0x76, 0x39, 0x6E, 0x8B, 0x32, 0xE1, 0x51, 0x54, 0x26, 0x0D, 0x06, 0x6E, 0xF4, 0x3D, 0x6C, 0x50, 0x32, 0x07, 0xB5, 0x44, 0x8C, 0x83, 0x48, - 0x46, 0x35, 0x09, 0x72, 0x93, 0xA5, 0x3C, 0x86, 0xF8, 0x3F, 0x73, 0x08, 0x04, 0xE3, 0x1F, 0x8F, 0xD0, 0xE3, 0xEA, 0x8A, 0xA4, 0xB0, 0x64, 0x19, - 0x85, 0x7A, 0xF0, 0x79, 0x0E, 0x2D, 0x49, 0x88, 0x3D, 0x54, 0x10, 0xAA, 0xA1, 0x7D, 0xED, 0xE9, 0xE1, 0xDD, 0xCB, 0x4D, 0x33, 0x7D, 0x14, 0x31, - 0x6E, 0x3B, 0x6E, 0xB1, 0xDE, 0xB3, 0x28, 0x23, 0xC9, 0xE9, 0xC9, 0x35, 0xC5, 0x4C, 0xDB, 0xC2, 0x9E, 0xF1, 0x3A, 0xDB, 0x6D, 0xDC, 0x0A, 0xDD, - 0x76, 0xBB, 0xAA, 0xC8, 0x9B, 0xA9, 0x83, 0x18, 0xB2, 0x1B, 0xD9, 0x8B, 0xC1, 0x12, 0x34, 0x40, 0xC3, 0xEE, 0x64, 0x3A, 0xC5, 0xC5, 0x76, 0xAB, - 0xA2, 0xCB, 0x69, 0x6C, 0xE6, 0xB4, 0xCD, 0xBE, 0xCB, 0xE9, 0x57, 0xDB, 0xE3, 0x93, 0xFF, 0x5E, 0x08, 0xCD, 0x8A, 0x16, 0x48, 0x16, 0xD4, 0x42, - 0x6D, 0xC6, 0x4D, 0xEC, 0xB3, 0x19, 0x3D, 0x5A, 0x8C, 0x9A, 0x90, 0xB2, 0x20, 0x2F, 0xD7, 0xB3, 0x17, 0xDE, 0x3F, 0x33, 0xFB, 0x7A, 0x77, 0x45, - 0xF2, 0x99, 0x0D, 0x17, 0x4E, 0x5B, 0x03, 0xD1, 0xE7, 0xDE, 0x63, 0x9F, 0xA1, 0xF7, 0xE6, 0xE8, 0x79, 0xC5, 0x4D, 0x98, 0x5D, 0x58, 0x67, 0x79, - 0xC9, 0x8F, 0xFE, 0xDF, 0xAB, 0xA6, 0xDC, 0xFD, 0x16, 0xE6, 0x9B, 0xFC, 0xA5, 0x56, 0x8B, 0x9F, 0xAD, 0x3D, 0xB8, 0x88, 0x50, 0xBB, 0x8C, 0xB0, - 0xD9, 0xE1, 0xE3, 0x67, 0x9B, 0x00, 0x9F, 0xAE, 0xE5, 0x54, 0x3F, 0x29, 0x26, 0xBF, 0x0C, 0xB5, 0x62, 0x94, 0xAD, 0x4C, 0x4E, 0xDE, 0x5D, 0xBD, - 0x64, 0xC9, 0x6D, 0xCB, 0x07, 0x74, 0x62, 0x6C, 0x1F, 0x58, 0x81, 0xAA, 0xF1, 0xB6, 0xE8, 0x4B, 0x13, 0x84, 0x47, 0x6A, 0xBB, 0xD6, 0xF9, 0x51, - 0x9C, 0xF8, 0x66, 0xCF, 0x3D, 0x7E, 0x91, 0x84, 0x3D, 0xB0, 0x9F, 0x52, 0x0B, 0x4B, 0xA8, 0x39, 0x5A, 0xA7, 0x37, 0x69, 0x8E, 0x20, 0xCF, 0x89, - 0xC7, 0x4E, 0x9C, 0xCA, 0xF9, 0x23, 0x76, 0xB7, 0xC0, 0x0E, 0x3F, 0xFF, 0x52, 0x83, 0x68, 0x84, 0xD5, 0xEB, 0x04, 0xF3, 0x20, 0x14, 0xD3, 0x89, - 0x13, 0xB9, 0x1D, 0x3E, 0x50, 0xD3, 0xAD, 0x12, 0x59, 0x90, 0x7C, 0xBF, 0xFD, 0xB5, 0x57, 0x16, 0xF0, 0x14, 0xDB, 0xCC, 0xD4, 0xD1, 0x46, 0x64, - 0x99, 0x48, 0x62, 0x1E, 0x3B, 0x01, 0x24, 0x4B, 0x9C, 0xDF, 0x7E, 0x44, 0x1B, 0xE4, 0xA0, 0xFF, 0x8B, 0x18, 0x90, 0xA9, 0x68, 0x69, 0x9B, 0xDE, - 0xD1, 0x7A, 0x43, 0x60, 0x3C, 0x80, 0x6E, 0x13, 0xC2, 0xBF, 0xDC, 0x1B, 0x1D, 0xFD, 0xBF, 0x41, 0x69, 0xA8, 0x41, 0xFB, 0xA5, 0x19, 0x27, 0xB6, - 0xC6, 0x30, 0x55, 0xD9, 0x7B, 0xE7, 0x5C, 0xDE, 0xA4, 0x8B, 0x71, 0xB2, 0x80, 0x80, 0x22, 0xC2, 0xC1, 0x02, 0x02, 0x0A, 0x20, 0x3E, 0x83, 0x70, - 0x67, 0x6B, 0x88, 0xA9, 0xA2, 0x1C, 0x2D, 0x21, 0xA2, 0x8A, 0x0A, 0xCE, 0x0E, 0xE0, 0xB2, 0x0A, 0x8F, 0x1C, 0xF1, 0xBB, 0x2D, 0x30, 0x1F, 0xBB, - 0x3E, 0x1E, 0x66, 0x82, 0xA7, 0xFB, 0x0E, 0xF5, 0xE1, 0xA3, 0x28, 0xCF, 0xB1, 0xB6, 0x45, 0x69, 0x74, 0x0A, 0x6D, 0xF6, 0x3D, 0x91, 0x29, 0x20, - 0xD9, 0x30, 0x07, 0x35, 0x1D, 0xF1, 0x02, 0xA9, 0xEC, 0x91, 0x52, 0xBB, 0xD2, 0xE4, 0x81, 0xEB, 0xF2, 0x1D, 0x0C, 0xB5, 0x2C, 0x83, 0xAF, 0x3B, - 0xED, 0xF3, 0x26, 0x05, 0x9E, 0x30, 0xF8, 0xC9, 0x07, 0x5B, 0x89, 0x73, 0xDE, 0xC3, 0x31, 0xBE, 0x91, 0x01, 0x55, 0x1F, 0xA7, 0x1D, 0x98, 0x32, - 0xF8, 0xB6, 0x04, 0x05, 0xB7, 0xB2, 0xBB, 0x72, 0x7C, 0x5D, 0x6F, 0x65, 0x99, 0x53, 0x0B, 0xC2, 0xF2, 0xF1, 0xDA, 0xA6, 0x15, 0xFC, 0xD6, 0x68, - 0xF1, 0xC2, 0x09, 0xD9, 0xEC, 0x5A, 0x58, 0x31, 0x18, 0x23, 0xDA, 0x2A, 0x1F, 0x3F, 0xD8, 0xDA, 0x19, 0xC0, 0xD6, 0xC7, 0xE9, 0x52, 0x98, 0x5A, - 0x0B, 0x67, 0xE6, 0x9C, 0xE7, 0x21, 0x5A, 0x55, 0x41, 0x76, 0xE6, 0x87, 0x45, 0x72, 0xC6, 0xDC, 0x5B, 0x34, 0xB2, 0xF9, 0x3F, 0x85, 0x41, 0xE2, - 0x1E, 0x60, 0x46, 0xC8, 0x96, 0xE6, 0x41, 0x0A, 0x85, 0x9C, 0xC9, 0xC6, 0xB1, 0x38, 0xB5, 0x26, 0x8B, 0x2F, 0xE7, 0xD9, 0x3E, 0xE1, 0xA9, 0xA8, - 0x72, 0x33, 0xF5, 0xE1, 0x1D, 0x4A, 0x60, 0xC9, 0x24, 0x43, 0xCB, 0xC4, 0x60, 0x26, 0xD6, 0xD9, 0xC3, 0x68, 0xDE, 0xFA, 0x90, 0xCF, 0xF2, 0x26, - 0x60, 0x1F, 0x96, 0x69, 0xCD, 0x66, 0x5D, 0xDA, 0x6D, 0x8C, 0xA1, 0x75, 0x04, 0x57, 0x6C, 0xC2, 0xAF, 0x31, 0x6D, 0x99, 0x77, 0x93, 0x95, 0xDF, - 0x0F, 0x43, 0x89, 0x67, 0x88, 0x46, 0x99, 0xE0, 0x90, 0x44, 0xEB, 0xA3, 0xD4, 0xD3, 0xA4, 0x7D, 0x82, 0x61, 0x9B, 0xCD, 0xFB, 0x2C, 0x04, 0x19, - 0x3F, 0x71, 0x63, 0xB1, 0x99, 0x26, 0xA6, 0x6D, 0x41, 0x43, 0x58, 0x4A, 0x73, 0xBE, 0xB6, 0x63, 0xBF, 0xD9, 0x56, 0xD5, 0x98, 0x2C, 0x21, 0x09, - 0x89, 0x6B, 0xE2, 0xCD, 0x53, 0xAA, 0xA5, 0xAA, 0x29, 0x23, 0x80, 0x81, 0x3F, 0x3D, 0x7F, 0xCB, 0x37, 0xB0, 0xF4, 0xD5, 0x09, 0xC6, 0x19, 0x96, - 0xE9, 0x38, 0x3E, 0x28, 0xB1, 0x2B, 0x0A, 0x01, 0x7E, 0xF1, 0x30, 0x43, 0x13, 0x64, 0x2D, 0x00, 0x57, 0xB4, 0xCF, 0xCF, 0x00, 0xD1, 0x00, 0xCD, - 0x00, 0x12, 0x66, 0xE3, 0x16, 0x08, 0xF1, 0x82, 0xCA, 0x71, 0x13, 0x9A, 0xF8, 0x48, 0xDA, 0xFC, 0x67, 0x6D, 0xEB, 0x72, 0xDE, 0x19, 0x15, 0xC2, - 0x08, 0xC0, 0x18, 0x9F, 0x93, 0x2A, 0xFF, 0xBF, 0xD5, 0x1C, 0x46, 0xD8, 0x5D, 0x8E, 0xC2, 0x33, 0xA4, 0xB0, 0x7F, 0x05, 0x40, 0xEB, 0x1A, 0x49, - 0xAE, 0xDF, 0x48, 0xF5, 0x53, 0x40, 0x83, 0xD0, 0xA4, 0xCA, 0x93, 0xE4, 0xFA, 0x57, 0x6A, 0xF6, 0xBA, 0xCF, 0xFE, 0xF9, 0xF6, 0x7F, 0xB3, 0x84, - 0xB2, 0xF4, 0xD6, 0x1E, 0x04, 0xA8, 0x53, 0x47, 0x72, 0x3A, 0x62, 0xC4, 0x7A, 0x98, 0x8E, 0xCF, 0x9A, 0x66, 0x3D, 0xF3, 0x87, 0x7F, 0x30, 0x47, - 0x87, 0xA1, 0xE5, 0x3F, 0x9C, 0xC5, 0xCA, 0x6D, 0xBA, 0x5A, 0xB6, 0x92, 0x46, 0x7B, 0x4E, 0xAE, 0x8A, 0xB1, 0xA3, 0x7A, 0x31, 0x99, 0x7A, 0x02, - 0x21, 0xD5, 0xF4, 0x8E, 0x52, 0x9B, 0x84, 0xA7, 0x79, 0x41, 0xE1, 0xB5, 0x3E, 0x31, 0x10, 0x72, 0x22, 0xDC, 0x37, 0x79, 0xF8, 0x62, 0xEA, 0x1C, - 0x59, 0x92, 0x4C, 0xA0, 0x04, 0x7B, 0x9C, 0xE4, 0xBA, 0x01, 0x49, 0x99, 0x04, 0x06, 0xE3, 0x51, 0xCA, 0x8F, 0xD1, 0x07, 0x87, 0xA2, 0xA3, 0x2D, - 0x22, 0x9E, 0x14, 0x43, 0x65, 0x72, 0x42, 0x5A, 0xA5, 0xEF, 0x04, 0x5D, 0x41, 0x06, 0x7E, 0x81, 0x38, 0x1C, 0xF8, 0xD9, 0xA0, 0xE1, 0xE3, 0xC8, - 0xD7, 0x93, 0xA2, 0x86, 0x27, 0x55, 0x71, 0xE7, 0x73, 0x43, 0x87, 0x38, 0x34, 0x67, 0xD7, 0x26, 0x38, 0x01, 0xD6, 0x0F, 0x46, 0xD4, 0x56, 0x3F, - 0x84, 0xCF, 0xF4, 0x00, 0xB3, 0x69, 0x75, 0x8C, 0x34, 0x54, 0xCE, 0xB3, 0xE0, 0x37, 0x3E, 0x62, 0x1E, 0x46, 0xE2, 0xFC, 0x79, 0x20, 0x3E, 0xA4, - 0x0F, 0xFA, 0xA3, 0x1F, 0x2E, 0x00, 0xBA, 0xA1, 0xA8, 0x00, 0xD6, 0x02, 0xD6, 0x5B, 0x6B, 0xAD, 0x75, 0xD2, 0x87, 0xE4, 0x2F, 0xA2, 0xBE, 0xBB, - 0xC0, 0x38, 0x56, 0x46, 0xE3, 0x5F, 0x90, 0xAC, 0xF2, 0xA1, 0xAF, 0x44, 0xE4, 0xA2, 0x2B, 0xE2, 0xE5, 0xEB, 0xE3, 0xB5, 0x5C, 0x0E, 0x35, 0x96, - 0xD7, 0x01, 0xA2, 0xA2, 0x69, 0x4A, 0xC8, 0x02, 0x70, 0x19, 0xD1, 0xD0, 0xDA, 0x67, 0xD1, 0xA3, 0x11, 0x80, 0xCC, 0x60, 0x5C, 0x90, 0xE0, 0x2A, - 0x2B, 0x2A, 0x1D, 0xC3, 0x34, 0x30, 0x36, 0xF6, 0x2B, 0xAF, 0x8D, 0x90, 0x0F, 0x3B, 0x60, 0xEA, 0x9C, 0x18, 0xB7, 0xA0, 0xE2, 0xAB, 0x05, 0xD4, - 0xCB, 0x04, 0x6A, 0x17, 0x5D, 0x14, 0xD7, 0xA7, 0xB0, 0x0A, 0x92, 0xAD, 0x12, 0x1F, 0xFD, 0x8A, 0xDB, 0x24, 0x9B, 0x22, 0x59, 0x67, 0x26, 0x15, - 0x3D, 0x89, 0x13, 0x48, 0x3C, 0x4C, 0x65, 0xDE, 0xCE, 0x2B, 0x7A, 0x04, 0x1E, 0x12, 0x59, 0x86, 0x12, 0xA8, 0x3D, 0x6C, 0x44, 0x3C, 0xC1, 0x58, - 0xF9, 0x0A, 0x60, 0xF0, 0xDE, 0xBB, 0xFC, 0x8B, 0x29, 0xF8, 0xB3, 0x3C, 0xCE, 0x06, 0x77, 0xC1, 0x8B, 0xF7, 0x50, 0x80, 0x33, 0x0D, 0xC6, 0xB7, - 0x4A, 0xC0, 0x05, 0x60, 0xC6, 0x0F, 0x64, 0xC2, 0x03, 0x24, 0xBE, 0x94, 0xBF, 0x05, 0x3C, 0x48, 0x4C, 0x0B, 0xDC, 0x46, 0x31, 0xE3, 0x1D, 0xA9, - 0x64, 0xF8, 0x1C, 0x2E, 0x3A, 0xFE, 0x5A, 0x2D, 0x97, 0xC7, 0x48, 0xB4, 0x34, 0x34, 0xC0, 0x65, 0x68, 0x39, 0x78, 0x57, 0x32, 0xB3, 0x08, 0x3C, - 0x95, 0x53, 0x0F, 0x26, 0x3D, 0xFC, 0xED, 0xC9, 0x88, 0x50, 0x60, 0x42, 0x12, 0x17, 0x96, 0x86, 0x7A, 0x0D, 0xB5, 0x56, 0x19, 0xD4, 0xFB, 0x38, - 0x18, 0x59, 0xB5, 0xAA, 0x80, 0x77, 0xB4, 0xE2, 0xDE, 0xF8, 0x6C, 0x12, 0xF7, 0x2F, 0x02, 0xF5, 0xF7, 0xD9, 0xAF, 0xB8, 0x8E, 0x36, 0xC4, 0xC1, - 0xF5, 0x5C, 0xE2, 0xAD, 0x25, 0x23, 0xA5, 0xC2, 0x1F, 0x72, 0x4F, 0x62, 0x23, 0xBB, 0x52, 0xB3, 0x88, 0x92, 0x0F, 0xF9, 0x97, 0x22, 0x00, 0xBA, - 0x93, 0x1E, 0xEC, 0xB5, 0xFA, 0x83, 0xEA, 0x0D, 0x7C, 0x04, 0xF4, 0x1E, 0x3B, 0x19, 0xF2, 0xC7, 0x43, 0xC2, 0x6A, 0x5A, 0x0F, 0x05, 0x80, 0x07, - 0x69, 0x28, 0xDD, 0xCB, 0x32, 0xF0, 0x2C, 0x7C, 0x6C, 0xE9, 0x6C, 0xBA, 0x35, 0xD3, 0x7A, 0x69, 0xBD, 0x36, 0x43, 0xB0, 0x86, 0xE5, 0xC0, 0xA0, - 0x95, 0x2D, 0x2B, 0x89, 0xCF, 0x89, 0x74, 0x66, 0x81, 0x99, 0xC6, 0x14, 0x56, 0x9D, 0x7E, 0xCE, 0xB9, 0xB1, 0xF6, 0x5A, 0x2B, 0x4D, 0x4E, 0x4F, - 0x1F, 0x15, 0x8D, 0xBC, 0x89, 0x74, 0x06, 0xD2, 0xEF, 0xF6, 0x7C, 0x42, 0x3A, 0xFE, 0x38, 0x50, 0x59, 0xF7, 0x1C, 0xF3, 0x10, 0xEC, 0x74, 0x48, - 0x1E, 0xFD, 0xE7, 0xB8, 0x04, 0xA1, 0x9E, 0xFF, 0xEF, 0x68, 0x16, 0xC1, 0x6D, 0x06, 0x23, 0xDB, 0x14, 0x46, 0x82, 0x89, 0x57, 0x38, 0x4E, 0x67, - 0x09, 0x5F, 0xCA, 0xE8, 0x88, 0xBB, 0x4E, 0x78, 0xBE, 0xCA, 0x1B, 0x97, 0xE8, 0xAB, 0x06, 0xE1, 0x1F, 0xEA, 0x35, 0xD9, 0x7A, 0x6C, 0x0D, 0xB6, - 0x86, 0xAF, 0x8D, 0x4C, 0x14, 0x99, 0xA0, 0x6A, 0x90, 0x92, 0x59, 0x1A, 0x48, 0xFA, 0x99, 0x96, 0xF9, 0xCF, 0xBD, 0xBE, 0x2D, 0x2D, 0xF4, 0xB2, - 0x72, 0x35, 0x5D, 0x00, 0x26, 0x03, 0xD3, 0xE2, 0x19, 0xE9, 0x03, 0xE0, 0x0F, 0x22, 0xA6, 0x21, 0xAC, 0xC8, 0x84, 0x58, 0x81, 0xA2, 0x5C, 0x69, - 0x74, 0xD1, 0x69, 0x61, 0x28, 0xBF, 0xE3, 0xBA, 0x25, 0x7D, 0x29, 0x5B, 0x8B, 0xAD, 0xB9, 0xD6, 0x41, 0xDF, 0x97, 0x8F, 0x19, 0xAD, 0x43, 0x23, - 0x83, 0x9D, 0x5E, 0x88, 0x11, 0x95, 0x54, 0x07, 0x12, 0x50, 0x32, 0x1E, 0x00, 0x9F, 0xC0, 0xA3, 0xE1, 0x55, 0x04, 0x6B, 0xF5, 0xDA, 0x6E, 0xFD, - 0xB5, 0xBE, 0x5A, 0x4B, 0xAD, 0x31, 0x7F, 0x48, 0xBB, 0x3F, 0x8D, 0x67, 0x05, 0x4E, 0xA4, 0x1D, 0x19, 0x83, 0x21, 0x44, 0x2B, 0x54, 0xF3, 0x21, - 0xFB, 0xD6, 0xBE, 0x04, 0xC9, 0xB8, 0x2D, 0x4A, 0x52, 0x96, 0x89, 0x1F, 0xF1, 0xC5, 0x8C, 0x83, 0xD6, 0x71, 0x94, 0xA0, 0xDB, 0xAD, 0xD9, 0xFD, - 0xA9, 0x65, 0xB0, 0x0F, 0x10, 0xF1, 0xB8, 0x2F, 0x0B, 0x81, 0x61, 0xB8, 0xB1, 0x18, 0x27, 0x6B, 0x92, 0x74, 0x12, 0x40, 0x72, 0x83, 0x84, 0x28, - 0x23, 0x20, 0x06, 0x9A, 0x40, 0x24, 0x92, 0x29, 0x91, 0xA0, 0x49, 0x41, 0x0A, 0xD9, 0xDA, 0x0C, 0x33, 0x8E, 0x23, 0x6A, 0xBA, 0x9E, 0xD8, 0x2F, - 0xF6, 0x9C, 0xCE, 0xBB, 0x09, 0x0B, 0x0B, 0x4B, 0x24, 0xF1, 0xCC, 0xF1, 0x94, 0xDF, 0x6C, 0xEA, 0x3A, 0x38, 0x5E, 0xEB, 0x0C, 0x7A, 0x87, 0x5A, - 0x3F, 0x80, 0x4D, 0xFD, 0x3C, 0x9A, 0x84, 0x33, 0x36, 0x5A, 0x81, 0xD1, 0x29, 0xFD, 0xB0, 0xB8, 0x26, 0x10, 0xA8, 0x4C, 0xE1, 0x69, 0x35, 0x05, - 0xAB, 0xF6, 0x82, 0x87, 0x5F, 0xFC, 0x03, 0xB6, 0x8D, 0x85, 0x13, 0x1B, 0xA6, 0x40, 0xF5, 0x54, 0xC5, 0x23, 0x78, 0x51, 0x26, 0x12, 0x59, 0x29, - 0x50, 0xEE, 0x0B, 0xF4, 0xD1, 0xA5, 0x0F, 0x7B, 0x02, 0x23, 0xA1, 0x62, 0xC0, 0xB2, 0xE8, 0x1B, 0x4B, 0x50, 0x6E, 0x36, 0xB3, 0x87, 0x03, 0xA9, - 0x17, 0x42, 0x6B, 0xA4, 0x95, 0xB9, 0xF8, 0x4D, 0xA0, 0x7B, 0x17, 0xCC, 0x81, 0xBB, 0xE3, 0x3D, 0x98, 0xC5, 0x22, 0x46, 0x34, 0xD2, 0xB3, 0x58, - 0xCE, 0xBC, 0x38, 0x0C, 0xF8, 0x1B, 0xB6, 0x22, 0xCF, 0x98, 0xB0, 0xAF, 0x1E, 0x8B, 0xE4, 0x68, 0xB5, 0x1B, 0xAE, 0x2C, 0xFD, 0x40, 0x93, 0x03, - 0xF4, 0x18, 0xCD, 0x3A, 0xFC, 0x33, 0xA7, 0x13, 0x80, 0xBB, 0xBD, 0xE5, 0xF4, 0xCC, 0x06, 0xBA, 0xE6, 0xD6, 0x6F, 0x7D, 0x17, 0x66, 0xF3, 0x56, - 0x18, 0x87, 0x14, 0xAC, 0x23, 0xA3, 0x0F, 0xA5, 0xB6, 0x05, 0x79, 0x3C, 0xD6, 0x20, 0x49, 0x8E, 0x22, 0x69, 0xBA, 0xB8, 0x15, 0x7F, 0x1E, 0xEE, - 0x51, 0x9F, 0x18, 0x68, 0xFB, 0x91, 0xC4, 0x17, 0xA3, 0xAE, 0x78, 0x94, 0xBB, 0xDD, 0x42, 0x8E, 0x9A, 0x67, 0xF8, 0x52, 0x70, 0x54, 0x68, 0x2D, - 0xFB, 0x4D, 0xB0, 0xCB, 0x0C, 0x27, 0x9C, 0xC2, 0x5A, 0x41, 0x51, 0x03, 0xF2, 0x82, 0x43, 0x7A, 0x5F, 0xCD, 0x35, 0x45, 0x5B, 0xB1, 0x94, 0x91, - 0x96, 0x67, 0xEB, 0xD5, 0xE2, 0x72, 0xCF, 0x67, 0x82, 0xCC, 0x68, 0x11, 0xA6, 0xF1, 0x58, 0x8F, 0xB8, 0x93, 0x8F, 0x37, 0xC1, 0xF4, 0x63, 0xB0, - 0xAD, 0x33, 0xD6, 0x51, 0x2C, 0xF8, 0x9C, 0xBE, 0x51, 0x6B, 0xB1, 0xE5, 0xCD, 0x8B, 0x7D, 0x6F, 0xEF, 0xAA, 0x78, 0x70, 0x35, 0x84, 0x2C, 0xA5, - 0x64, 0x14, 0xC9, 0x07, 0xC1, 0xA5, 0xC9, 0x61, 0xB6, 0xF6, 0xDC, 0xBC, 0xB1, 0xC5, 0x0B, 0x6F, 0xC9, 0x68, 0x35, 0x20, 0xBE, 0x86, 0x89, 0xFB, - 0x69, 0xF9, 0x77, 0xFE, 0xBA, 0x29, 0x48, 0x8E, 0xDA, 0xCB, 0x26, 0x9F, 0xA9, 0x03, 0xB7, 0x73, 0x65, 0xB5, 0xAE, 0xC3, 0x16, 0x65, 0x3E, 0x52, - 0x65, 0x9D, 0x30, 0xD9, 0xF5, 0x80, 0x55, 0x64, 0x47, 0x85, 0x91, 0xDD, 0x0F, 0x27, 0x72, 0xBE, 0x4A, 0x10, 0x8C, 0x91, 0x07, 0x72, 0x1B, 0x2F, - 0xF5, 0xB3, 0xA8, 0x58, 0xD3, 0x65, 0xB7, 0x64, 0xA3, 0xD0, 0x1F, 0x08, 0xF6, 0x9E, 0xD6, 0xD3, 0xE4, 0xAD, 0x4D, 0xF5, 0xEA, 0x8D, 0xC9, 0x5F, - 0x97, 0x8D, 0x5F, 0x1A, 0x32, 0x3C, 0xD7, 0xA2, 0x9D, 0x5E, 0xA3, 0x21, 0xF1, 0xC6, 0x62, 0x26, 0x64, 0x68, 0x44, 0xA9, 0x5F, 0x0A, 0x88, 0x43, - 0xF0, 0xC4, 0x34, 0xD2, 0xD7, 0x55, 0x5D, 0x97, 0xFC, 0xE2, 0x9D, 0xA3, 0xD8, 0x6F, 0x3C, 0xF0, 0xE9, 0xDC, 0xB8, 0x03, 0x8A, 0x5D, 0xAB, 0x5B, - 0x72, 0xE2, 0x06, 0x4C, 0x12, 0x94, 0x53, 0xAA, 0x21, 0x07, 0x9C, 0x2D, 0x71, 0x64, 0xC3, 0x5D, 0xB8, 0x7B, 0x0A, 0x34, 0xE0, 0x21, 0x29, 0x28, - 0x16, 0x14, 0x37, 0xBD, 0xBA, 0xAB, 0x6C, 0xF0, 0xE9, 0xE5, 0xCD, 0x1B, 0x66, 0xA4, 0x14, 0xB1, 0x69, 0x5A, 0x1B, 0x95, 0x74, 0xF8, 0x97, 0x1E, - 0xDD, 0x83, 0xD3, 0xEE, 0x9B, 0xE8, 0xDA, 0x47, 0xEC, 0xFC, 0x6C, 0xBF, 0xCD, 0xD9, 0xAC, 0xA3, 0x9A, 0x8A, 0xC0, 0xF3, 0xC2, 0x89, 0x3B, 0xC7, - 0x8A, 0xB1, 0xDA, 0x38, 0xC8, 0x80, 0xE3, 0x0E, 0xCF, 0x70, 0x5D, 0x8D, 0x9D, 0x36, 0xE4, 0x5C, 0x32, 0xC7, 0xDE, 0x9A, 0x35, 0xD8, 0xCB, 0xB4, - 0xE8, 0xFF, 0xDE, 0xC9, 0x9F, 0xF1, 0x56, 0xC3, 0x4D, 0xAB, 0x33, 0x79, 0x24, 0x89, 0x16, 0x2D, 0x96, 0xA9, 0xB3, 0xE8, 0x6F, 0x95, 0xD9, 0x7B, - 0x2E, 0x39, 0x9C, 0x7B, 0xCD, 0xDB, 0x40, 0xE5, 0x00, 0xD5, 0x4C, 0x0E, 0xFB, 0x84, 0xC3, 0x8D, 0xFE, 0xD6, 0x03, 0x9B, 0x8B, 0x4C, 0xAE, 0x70, - 0x71, 0xFD, 0x94, 0x0C, 0xBC, 0x21, 0x00, 0xA6, 0xEE, 0xB8, 0x1E, 0x40, 0xB8, 0x0E, 0xB7, 0xAE, 0x18, 0x17, 0xD3, 0x6B, 0x9A, 0xE7, 0x11, 0xED, - 0xB6, 0x3B, 0x15, 0x61, 0x7B, 0x05, 0xEE, 0xD1, 0x05, 0x75, 0xBC, 0xE9, 0xA0, 0x0E, 0x0C, 0x40, 0x06, 0xBA, 0x00, 0xAF, 0x00, 0xAD, 0x00, 0xA0, - 0xD0, 0xAF, 0xA0, 0x02, 0xC6, 0x9F, 0x10, 0xC7, 0x9E, 0x17, 0xB0, 0x9D, 0x23, 0x6E, 0x4E, 0x19, 0xE4, 0x4D, 0xA0, 0x64, 0x4D, 0xA7, 0x82, 0x4C, - 0x2C, 0x03, 0xCC, 0x31, 0x8F, 0x4B, 0xB9, 0x0F, 0xCB, 0xC0, 0x9C, 0xCA, 0x48, 0x1D, 0x4A, 0xCC, 0x8F, 0x49, 0xF7, 0x94, 0xE4, 0xAD, 0x4E, 0xA4, - 0xF0, 0x0E, 0xE4, 0xF3, 0x14, 0x64, 0x00, 0xD5, 0xE3, 0xC3, 0x77, 0xE4, 0x38, 0x6B, 0x54, 0x19, 0x64, 0xCC, 0x21, 0x5C, 0x44, 0x0A, 0x55, 0xA4, - 0x12, 0x4D, 0xBC, 0x2A, 0x44, 0x4C, 0xF3, 0x3D, 0x84, 0x1B, 0x35, 0xFC, 0x23, 0x2D, 0x7C, 0xFC, 0x7D, 0xB5, 0x16, 0xEC, 0xBD, 0x0E, 0x54, 0xA6, - 0x00, 0xBC, 0xAE, 0x00, 0x4C, 0x68, 0xF9, 0xBB, 0x70, 0xF1, 0xDB, 0xA8, 0xE8, 0x4B, 0xB1, 0xE0, 0xC3, 0x71, 0xD9, 0x3B, 0x7A, 0xD1, 0x9B, 0x52, - 0xCA, 0xEB, 0x5A, 0xC2, 0x43, 0xF3, 0xBA, 0xBB, 0xFB, 0xB2, 0x3B, 0xB4, 0xD3, 0x2F, 0xEF, 0xEA, 0x3C, 0xC1, 0xE8, 0x6C, 0x2B, 0xE7, 0x78, 0xF1, - 0xE4, 0x90, 0xF3, 0xE2, 0xAA, 0x2D, 0xE3, 0x0E, 0x2E, 0xE1, 0x2A, 0x66, 0xDF, 0x44, 0x68, 0xDD, 0x60, 0x98, 0xDB, 0x7C, 0x9A, 0xD9, 0x9A, 0x12, - 0xF2, 0xB5, 0x2B, 0x85, 0x22, 0x35, 0xDF, 0x9C, 0xF6, 0x9D, 0xD1, 0xEC, 0x14, 0xCE, 0x0C, 0x17, 0x02, 0x90, 0x01, 0x66, 0x8E, 0x1C, 0x82, 0x39, - 0xA0, 0x57, 0xA1, 0x70, 0xBD, 0x8F, 0xD4, 0xB0, 0x12, 0xDF, 0x77, 0xC2, 0xAF, 0x7C, 0x16, 0x7F, 0xF4, 0x91, 0x7C, 0x0B, 0xDE, 0xB2, 0x27, 0xE1, - 0x49, 0x7B, 0x17, 0xDE, 0xB5, 0xEB, 0xB8, 0xBE, 0x4F, 0xE2, 0x93, 0xBB, 0xDD, 0x77, 0xEF, 0x94, 0xBF, 0xD2, 0xC7, 0x7E, 0x59, 0xAF, 0xFC, 0x97, - 0x7D, 0xCC, 0xC1, 0xFF, 0x41, 0xF0, 0x20, 0x3F, 0xC4, 0x73, 0x25, 0xE0, 0x67, 0x09, 0xBB, 0x08, 0x2E, 0xB2, 0xF7, 0xE0, 0x90, 0x85, 0x92, 0x6C, - 0x6F, 0x96, 0x5F, 0x30, 0x97, 0xE0, 0x02, 0xC0, 0x94, 0x11, 0x56, 0x43, 0xB7, 0x31, 0x69, 0x23, 0x0B, 0xD5, 0x3C, 0x56, 0xD4, 0xCB, 0xF0, 0x57, - 0xD2, 0xB9, 0x03, 0x68, 0xE5, 0xB9, 0xA6, 0x5B, 0xAB, 0x07, 0x4D, 0xAB, 0x18, 0x3D, 0xBB, 0xE9, 0xE0, 0x96, 0x76, 0xFF, 0x4E, 0xC3, 0x32, 0x43, - 0xBC, 0x66, 0x47, 0xBC, 0x2E, 0xA4, 0xB8, 0x62, 0xA8, 0xB4, 0x92, 0x14, 0xB1, 0xD2, 0x18, 0xAD, 0x0A, 0x69, 0xA9, 0x3E, 0x6D, 0xA5, 0x56, 0x19, - 0xA1, 0x92, 0xCD, 0x9D, 0xAA, 0x6D, 0x99, 0xE6, 0xF9, 0xD2, 0xC4, 0x39, 0xD2, 0x4C, 0xBA, 0xD1, 0x54, 0x48, 0x51, 0xDC, 0xC8, 0xD0, 0x61, 0xD7, - 0x72, 0x2B, 0x91, 0x47, 0xB9, 0xDB, 0xE7, 0xC2, 0x48, 0x5C, 0x75, 0x39, 0x92, 0x6F, 0x2A, 0xD8, 0x37, 0x9C, 0xFB, 0x31, 0xD9, 0xC0, 0x1F, 0xE4, - 0x1F, 0x1E, 0x24, 0xD7, 0xF1, 0x3B, 0x39, 0xC8, 0x1E, 0xF2, 0xDE, 0xD9, 0x79, 0xDE, 0x54, 0x7B, 0xD5, 0xFB, 0x6C, 0x47, 0x7B, 0x13, 0xEE, 0x85, - 0xEF, 0x34, 0x3F, 0xFA, 0x36, 0xF4, 0x87, 0x39, 0xA9, 0x2F, 0xCD, 0x81, 0x7D, 0x70, 0x0E, 0x73, 0xED, 0x7D, 0xB0, 0xF3, 0x0A, 0x6F, 0x91, 0x7B, - 0xEC, 0xFD, 0x67, 0xBF, 0x2D, 0xF6, 0x8B, 0xF7, 0xCB, 0xEE, 0x59, 0xE9, 0x59, 0xC9, 0x12, 0x18, 0xD6, 0xB4, 0xD3, 0xD5, 0x4C, 0x3A, 0xC9, 0x8A, - 0x35, 0xF7, 0xF9, 0x5E, 0x86, 0x4D, 0x5F, 0x82, 0xBB, 0xC3, 0x69, 0x95, 0xBB, 0xF6, 0x5C, 0xBB, 0xD7, 0x40, 0xBB, 0xD8, 0x30, 0xBB, 0x69, 0x22, - 0xBB, 0x6A, 0x12, 0x1B, 0xEB, 0xE0, 0x06, 0x7E, 0xF0, 0xCC, 0x27, 0xD9, 0x28, 0xAE, 0x18, 0x44, 0x34, 0x59, 0xFE, 0x29, 0xCE, 0x93, 0x39, 0xFA, - 0x3C, 0xB9, 0x3D, 0xF1, 0x31, 0xF3, 0x11, 0xB7, 0x15, 0xBA, 0xF8, 0x33, 0x42, 0x60, 0xE4, 0x38, 0x88, 0x00, 0xAB, 0x43, 0x0E, 0x27, 0x32, 0xF7, - 0xEE, 0x32, 0x2F, 0x2C, 0x15, 0xD0, 0xED, 0x68, 0x7C, 0xC0, 0xA1, 0x1D, 0xF1, 0x80, 0x47, 0xEB, 0xD2, 0x7F, 0xE6, 0x41, 0x0F, 0x82, 0xF8, 0x28, - 0x7A, 0xCA, 0x27, 0xD2, 0x8B, 0x7E, 0x96, 0xAE, 0xF5, 0x8D, 0xDA, 0x72, 0x7F, 0xAB, 0xED, 0xF1, 0xB9, 0x36, 0x1D, 0x0E, 0xD9, 0x8E, 0xCC, 0x79, - 0x3D, 0x46, 0xFE, 0x90, 0x04, 0x1D, 0xBA, 0xC7, 0xC7, 0xD1, 0x73, 0x7E, 0xAE, 0x4B, 0x7D, 0x9D, 0xF6, 0x0E, 0x3E, 0xD5, 0xA6, 0xC2, 0x7F, 0xFB, - 0x0C, 0x1F, 0x6C, 0x13, 0xE2, 0xA1, 0x6D, 0x51, 0x9C, 0x50, 0xF7, 0xC5, 0x45, 0xF5, 0xD1, 0xEF, 0xAB, 0xFF, 0xFE, 0x6A, 0xDD, 0xFA, 0xC7, 0x4C, - 0xE8, 0x3F, 0x14, 0x9F, 0x45, 0x6F, 0xF9, 0x5C, 0x15, 0xED, 0x33, 0x0B, 0xDC, 0xA5, 0xFD, 0x82, 0x2B, 0xB5, 0x05, 0x75, 0x41, 0x9D, 0x85, 0x2E, - 0x2C, 0xE8, 0x64, 0x72, 0x48, 0x16, 0x0B, 0x8E, 0xA1, 0xB1, 0x64, 0xD5, 0xD1, 0x8A, 0x95, 0xF8, 0x0D, 0xB0, 0x07, 0xBC, 0x33, 0x76, 0x8D, 0xB7, - 0x80, 0x1F, 0xC8, 0x37, 0xB2, 0x39, 0x6E, 0x68, 0xA2, 0xC8, 0x00, 0xF3, 0xBC, 0xB2, 0xCC, 0x40, 0xDC, 0x4F, 0xE8, 0x5D, 0x08, 0xE9, 0xF7, 0x48, - 0x2F, 0x10, 0x35, 0x96, 0x44, 0x81, 0x62, 0xA8, 0x81, 0x0C, 0xD4, 0x0F, 0xC1, 0x2E, 0x49, 0x41, 0x51, 0x52, 0x69, 0x03, 0x12, 0x60, 0x40, 0x43, - 0x43, 0xA4, 0x04, 0x84, 0x79, 0x41, 0x52, 0x52, 0x50, 0xAF, 0xFA, 0x4C, 0x07, 0x0B, 0x06, 0xEF, 0xB5, 0x8C, 0x0A, 0x59, 0x35, 0x61, 0x4A, 0xB8, - 0x84, 0x9B, 0xCD, 0x60, 0x9E, 0xBF, 0x35, 0x03, 0xC8, 0x1C, 0xED, 0x05, 0x7E, 0xC7, 0x34, 0xDF, 0x6E, 0xFF, 0x6D, 0xDB, 0x6E, 0xFF, 0xDF, 0x0B, - 0xBD, 0xD7, 0xB7, 0x00, 0x99, 0x6D, 0xDA, 0x19, 0x0B, 0x1B, 0xEA, 0x90, 0x48, 0x47, 0x9B, 0x41, 0xF5, 0x8E, 0x9B, 0x76, 0x67, 0x6E, 0x58, 0x09, - 0x79, 0x60, 0x82, 0x5D, 0xBF, 0x61, 0xA1, 0x8C, 0xB3, 0xD5, 0x8E, 0xD5, 0x14, 0x9D, 0x7B, 0xC6, 0x93, 0x37, 0x30, 0xF2, 0x3B, 0xC0, 0x25, 0xDC, - 0x6C, 0x06, 0x9F, 0xBE, 0x8B, 0x36, 0x41, 0x6F, 0xBE, 0x7C, 0xE0, 0xC2, 0x39, 0x82, 0x30, 0x12, 0x03, 0x58, 0x50, 0xD7, 0xC8, 0xD8, 0x5B, 0x1B, - 0x34, 0x34, 0x8C, 0x16, 0xCD, 0xB7, 0xDB, 0x7F, 0xDB, 0xB6, 0xDB, 0x7F, 0x9B, 0x43, 0x19, 0x7A, 0x7A, 0x8E, 0xD9, 0x79, 0x36, 0xD9, 0xA4, 0xEC, - 0xCE, 0xFE, 0x84, 0x76, 0x67, 0x4C, 0x49, 0x4B, 0x49, 0xEA, 0xEF, 0x67, 0x6D, 0x5D, 0x19, 0x7A, 0x0D, 0x5A, 0x00, 0xFB, 0x76, 0xC2, 0xFA, 0x01, - 0x62, 0xF4, 0xA3, 0xB9, 0x4F, 0x2C, 0x51, 0xFA, 0xD5, 0x2D, 0xA5, 0xF3, 0x99, 0x76, 0x33, 0x93, 0xB9, 0x8B, 0x80, 0x11, 0x18, 0x78, 0x5F, 0xC2, - 0x88, 0x66, 0x94, 0x54, 0x1E, 0xA4, 0x56, 0xBC, 0xE7, 0x35, 0x11, 0xD3, 0xB2, 0x1C, 0xA7, 0x25, 0x28, 0x05, 0xA7, 0xE9, 0x52, 0xCE, 0x36, 0x0C, - 0xE1, 0xC9, 0x26, 0x61, 0x16, 0x1E, 0x14, 0x2D, 0x8E, 0x4E, 0x6D, 0xF6, 0x63, 0x8E, 0xDC, 0x1D, 0x67, 0x9D, 0x31, 0x8C, 0xA0, 0x62, 0x00, 0xCD, - 0x25, 0x7C, 0x43, 0xFA, 0xC5, 0xA3, 0xCF, 0x7B, 0x80, 0x0E, 0xE9, 0x2F, 0x99, 0x22, 0xEE, 0xB1, 0xB9, 0x99, 0xD0, 0xAA, 0x8C, 0x5F, 0xB0, 0x0F, - 0xA2, 0x19, 0x66, 0x33, 0xFB, 0xDC, 0x86, 0xB1, 0xC6, 0x3E, 0xB7, 0x19, 0xD6, 0x31, 0xAE, 0x63, 0x36, 0xF3, 0x1C, 0xD3, 0x30, 0xB6, 0x69, 0x6C, - 0x3F, 0x4B, 0x15, 0xCC, 0x14, 0x1C, 0x2C, 0xC5, 0x99, 0x03, 0x53, 0x7E, 0x96, 0x66, 0xFF, 0x28, 0xF3, 0xD8, 0x67, 0xF9, 0xAC, 0xA6, 0x97, 0xD1, - 0xF4, 0x2F, 0xC3, 0xEA, 0xF3, 0x01, 0xEC, 0x2E, 0x00, 0x4A, 0x5B, 0x70, 0x12, 0x32, 0x30, 0x2C, 0x27, 0x1D, 0x31, 0x87, 0x47, 0x36, 0x76, 0xF1, - 0x52, 0xB9, 0xDE, 0x78, 0xC5, 0xF6, 0x75, 0x10, 0x22, 0x2A, 0xC1, 0x19, 0x94, 0x72, 0x08, 0x37, 0x93, 0x08, 0xE1, 0x94, 0x0C, 0x0E, 0x5F, 0xC7, - 0xF3, 0xE7, 0x5C, 0x1D, 0xDE, 0xD3, 0xBB, 0x82, 0x70, 0x8A, 0x44, 0x43, 0xB3, 0xA3, 0x26, 0x53, 0x43, 0x01, 0x1F, 0x01, 0xF9, 0x00, 0xD4, 0x51, - 0x1F, 0xA9, 0xA9, 0x76, 0x74, 0x7D, 0xB5, 0xA9, 0x7B, 0xCA, 0xFA, 0xBA, 0x49, 0xCB, 0xE0, 0x36, 0xB0, 0x22, 0xEE, 0x08, 0x7B, 0xE3, 0xE2, 0xB0, - 0x24, 0x68, 0x89, 0xCE, 0x41, 0x75, 0xB4, 0x14, 0xEA, 0x25, 0xE8, 0x25, 0x38, 0xD7, 0x72, 0x03, 0xCE, 0xB8, 0xB8, 0xA3, 0xAA, 0xCE, 0x05, 0xAB, - 0xA4, 0x56, 0xF5, 0x1A, 0x26, 0xC1, 0xC4, 0x89, 0x1E, 0x3E, 0xE6, 0x95, 0x2B, 0x13, 0xCD, 0x8F, 0x41, 0xC2, 0x4E, 0x6F, 0x6C, 0x21, 0xAA, 0xA0, - 0x2A, 0x1A, 0xB2, 0xC2, 0x10, 0x95, 0x24, 0xD7, 0x9C, 0x1F, 0x1A, 0x6D, 0xD2, 0xE8, 0x1A, 0x13, 0xCD, 0xA6, 0xD5, 0x1A, 0x96, 0xAD, 0xBE, 0xD9, - 0x26, 0x75, 0x68, 0xC7, 0x54, 0x3E, 0xB6, 0x4F, 0x25, 0xD3, 0x95, 0x6A, 0xB1, 0xFD, 0x4C, 0x4B, 0x7D, 0x69, 0xBA, 0xEA, 0x57, 0xF3, 0x15, 0xAF, - 0x79, 0x89, 0xCD, 0xEE, 0x0A, 0x33, 0x82, 0x98, 0xF5, 0x00, 0xA0, 0x56, 0xB0, 0x16, 0xEA, 0x0A, 0xBB, 0x56, 0x85, 0xD8, 0x21, 0x55, 0x8B, 0x85, - 0xAB, 0x44, 0x5A, 0x4E, 0xDD, 0xD2, 0x5C, 0xE6, 0x9C, 0xA6, 0x33, 0x19, 0x75, 0xA3, 0x59, 0xAA, 0x8F, 0x93, 0x57, 0xF7, 0x9A, 0x47, 0x6B, 0x16, - 0x47, 0xA6, 0x17, 0x20, 0xB3, 0x10, 0x56, 0x44, 0x6D, 0xB3, 0x37, 0x2A, 0x14, 0x1B, 0xA5, 0xE2, 0xE8, 0x1C, 0x33, 0xD8, 0x52, 0x26, 0x9A, 0x4E, - 0x33, 0x03, 0xB5, 0xA0, 0x99, 0xA9, 0x35, 0x4D, 0x41, 0x56, 0xD6, 0xD4, 0x64, 0x37, 0x55, 0x59, 0x76, 0x56, 0x7D, 0x30, 0xC0, 0x72, 0x18, 0xAB, - 0x19, 0xAC, 0x86, 0xEA, 0xC2, 0xBE, 0xA8, 0x1B, 0xFA, 0x6C, 0xC2, 0xE8, 0x18, 0xF3, 0x48, 0xA7, 0x29, 0xD3, 0x94, 0x7F, 0xAE, 0x94, 0x6D, 0xAA, - 0x9B, 0xD2, 0x50, 0x3D, 0x54, 0xD6, 0xDD, 0xF9, 0x54, 0xCE, 0xDD, 0x2A, 0x71, 0xCD, 0x1A, 0x90, 0xE0, 0x43, 0x0F, 0x1A, 0xBF, 0xC2, 0xE4, 0x68, - 0x6A, 0x0C, 0x7A, 0xEB, 0x34, 0xE4, 0x53, 0x4E, 0xAA, 0x94, 0x4A, 0x34, 0x56, 0x25, 0x0C, 0x4B, 0x49, 0x5C, 0x85, 0xE1, 0x9D, 0xC2, 0xEC, 0xA7, - 0x98, 0x2A, 0xDE, 0xB5, 0x42, 0xA9, 0xA2, 0xAE, 0x28, 0x83, 0x54, 0x21, 0x65, 0x96, 0xAA, 0xA5, 0x24, 0xD6, 0x38, 0x25, 0xDC, 0x5C, 0x54, 0xCA, - 0x99, 0x73, 0x2E, 0x37, 0x19, 0x9D, 0xD4, 0x2C, 0xF5, 0xBF, 0x8E, 0xB6, 0xFF, 0xC6, 0xEF, 0x67, 0xBB, 0xFB, 0xD9, 0x88, 0x7F, 0x0D, 0xB5, 0xDF, - 0x57, 0xFF, 0x18, 0xC4, 0x9F, 0xC5, 0xF1, 0x44, 0xFD, 0x4B, 0x8C, 0x00, 0x41, 0xF7, 0x7B, 0x38, 0xFE, 0x67, 0x19, 0x88, 0xFF, 0x85, 0xFE, 0xBB, - 0xF8, 0x4F, 0x28, 0xFE, 0x10, 0x84, 0x7F, 0xCF, 0xF7, 0x37, 0xE8, 0xDF, 0xEC, 0xFE, 0x0B, 0xDA, 0xFE, 0x32, 0xE4, 0x5F, 0x66, 0xFF, 0xB2, 0xFE, - 0xC9, 0x8E, 0x3F, 0x59, 0xFD, 0xC9, 0xFA, 0x57, 0x50, 0xFC, 0x13, 0x20, 0x90, 0xFB, 0x09, 0xD4, 0x5F, 0xC0, 0xF1, 0xE7, 0xF5, 0x7E, 0xAE, 0x6B, - 0x23, 0x61, 0x89, 0x47, 0x53, 0xA1, 0x55, 0x5A, 0x0D, 0x5D, 0xD3, 0x55, 0xEE, 0xED, 0x35, 0x97, 0x8D, 0xBD, 0xB7, 0x90, 0xED, 0x74, 0x3D, 0x59, - 0x5A, 0xB7, 0x96, 0xBD, 0x05, 0x2E, 0x9D, 0x01, 0x6B, 0x8B, 0xDE, 0xEA, 0x58, 0x07, 0xA8, 0x2F, 0x04, 0xCD, 0x81, 0x76, 0x68, 0x1F, 0x95, 0xD1, - 0x4A, 0xE8, 0x92, 0x96, 0x71, 0xC1, 0xF4, 0x92, 0xEB, 0x66, 0xC1, 0xDC, 0x30, 0x36, 0xCF, 0xD5, 0x63, 0x23, 0x5D, 0x49, 0x56, 0xD5, 0xB5, 0xB3, - 0x93, 0xA8, 0x30, 0xBB, 0x8A, 0xB2, 0xEA, 0x61, 0x54, 0xC9, 0x5B, 0x02, 0x9D, 0xD0, 0x27, 0x28, 0x8A, 0xF6, 0x41, 0x7F, 0x34, 0x8A, 0x9B, 0xC2, - 0xFE, 0xB8, 0x40, 0xAC, 0xF0, 0x62, 0xB1, 0x6F, 0x2E, 0x1C, 0x8B, 0xE8, 0x26, 0xB2, 0x44, 0x1A, 0xCA, 0x2A, 0xA2, 0x8B, 0x5A, 0x14, 0x1D, 0xD5, - 0xB9, 0xA8, 0xAE, 0x9E, 0x94, 0xAC, 0xD1, 0x68, 0x5A, 0x93, 0xDD, 0x32, 0x9B, 0x76, 0xA9, 0x65, 0xBB, 0x36, 0x6C, 0x93, 0xEB, 0xC4, 0x8E, 0xB9, - 0x64, 0xEC, 0x13, 0x5A, 0xB4, 0x65, 0xA8, 0x99, 0xF6, 0x43, 0xFF, 0xF4, 0x25, 0x6A, 0xEC, 0x57, 0x14, 0x56, 0xEB, 0x10, 0xA3, 0x48, 0x00, 0x34, - 0xAC, 0x6B, 0x21, 0xAE, 0x0C, 0x80, 0x39, 0x01, 0xAC, 0x85, 0xB5, 0xCB, 0xC2, 0x0E, 0xB9, 0x44, 0x2C, 0xDC, 0xDD, 0x62, 0x81, 0xB4, 0x48, 0x73, - 0xA1, 0x5D, 0x9A, 0x0E, 0xAD, 0xD3, 0x8D, 0x02, 0x8C, 0xA3, 0x9E, 0x71, 0xF8, 0xCE, 0x36, 0x8E, 0x67, 0x1B, 0x6E, 0x67, 0x36, 0xE2, 0x79, 0x86, - 0xF0, 0x0C, 0xE3, 0x78, 0x6E, 0xE1, 0x3B, 0xA3, 0xA0, 0x67, 0x12, 0xC8, 0x33, 0x88, 0xB1, 0x73, 0x1A, 0xF1, 0x9C, 0x76, 0x76, 0x46, 0x3B, 0x3B, - 0x6F, 0xF0, 0x9D, 0x31, 0xF4, 0xCE, 0x6D, 0xCF, 0x12, 0xE8, 0x39, 0x82, 0xEF, 0xEC, 0x20, 0x78, 0x6E, 0x70, 0x76, 0x36, 0xAB, 0x3B, 0x97, 0xFD, - 0xAC, 0xE0, 0xEE, 0x9C, 0xE0, 0x77, 0x36, 0x30, 0x3C, 0x0F, 0xE8, 0x9D, 0x05, 0xD8, 0x73, 0x00, 0xB7, 0xB3, 0x58, 0x3D, 0xF3, 0x90, 0x67, 0xDE, - 0xF0, 0xCC, 0x53, 0xFB, 0x87, 0x19, 0x7F, 0x60, 0xBF, 0x3F, 0x89, 0xFC, 0x79, 0x0D, 0x7F, 0x5C, 0xC7, 0x1F, 0xB9, 0xF6, 0xD7, 0x9A, 0x3F, 0xAC, - 0xFB, 0xB7, 0xFA, 0xFD, 0x76, 0x77, 0x3F, 0xAA, 0xE1, 0x6F, 0x8A, 0xEE, 0x2F, 0xD5, 0xFB, 0x47, 0xAD, 0xFD, 0xA1, 0xFE, 0x7B, 0xB2, 0xFB, 0x39, - 0x09, 0xFF, 0x4D, 0xBD, 0x5F, 0x93, 0xF1, 0xCF, 0x24, 0xFC, 0x31, 0x15, 0x7F, 0xE3, 0xFF, 0x49, 0xC0, 0x5F, 0x12, 0xF1, 0x97, 0xD4, 0xF6, 0xCF, - 0xFD, 0x6F, 0x44, 0xFC, 0x17, 0xB9, 0xFD, 0x8A, 0x82, 0xFF, 0xA1, 0xFA, 0x5F, 0xB9, 0x7F, 0x90, 0xF1, 0x17, 0x34, 0xF6, 0x07, 0x9A, 0x7F, 0x51, - 0xED, 0xE7, 0x23, 0xFC, 0xE3, 0x90, 0x3F, 0x9E, 0xB6, 0xDF, 0xDA, 0x9F, 0x0E, 0xF1, 0x7F, 0xD3, 0xBF, 0x1B, 0xB5, 0x7F, 0xCD, 0xDA, 0x6F, 0xA6, - 0xFC, 0xC7, 0x0C, 0x7F, 0x31, 0xC9, 0xFF, 0x0B, 0xF0, 0x2F, 0x93, 0x3F, 0x9C, 0xEF, 0xC7, 0xA2, 0xF6, 0x53, 0x89, 0x42, 0xF7, 0xBF, 0x25, 0xFF, - 0x24, 0xC1, 0xDF, 0x48, 0xF2, 0x07, 0x12, 0xFF, 0x1F, 0x3E, 0xEE, 0x7E, 0x1F, 0xE5, 0xDF, 0x43, 0x2E, 0x74, 0xB6, 0x04, 0xAE, 0x99, 0xDD, 0x8E, - 0xCE, 0x96, 0xC0, 0xB5, 0xF3, 0x16, 0x8C, 0x9D, 0xA7, 0xE0, 0x78, 0x5E, 0x82, 0xE4, 0x79, 0x06, 0xEA, 0xCE, 0x23, 0x30, 0x3C, 0x6F, 0x8C, 0x78, - 0x5E, 0x80, 0x9F, 0x07, 0xA0, 0xCF, 0x13, 0x1B, 0x9E, 0x77, 0xB5, 0xF3, 0x30, 0x60, 0x67, 0xC2, 0xF3, 0xAF, 0x5D, 0xBF, 0x5C, 0xC4, 0x33, 0xAE, - 0xDE, 0xB9, 0xD6, 0xF0, 0x7C, 0x17, 0x3C, 0xBF, 0x4A, 0x9E, 0x5B, 0xB5, 0x9D, 0x59, 0x25, 0xCF, 0x76, 0xC0, 0x73, 0x2A, 0xE3, 0x19, 0x55, 0xEF, - 0x7C, 0xEA, 0x48, 0x6A, 0x4D, 0xEE, 0x3C, 0x2A, 0x78, 0x06, 0x75, 0xCF, 0x75, 0xC5, 0xB3, 0xA7, 0x7A, 0xE6, 0x64, 0x77, 0xD6, 0xE4, 0x3B, 0x67, - 0xBA, 0x67, 0x4C, 0x6E, 0x67, 0xE3, 0xD9, 0xF9, 0xD2, 0x0C, 0x9E, 0x29, 0xD1, 0xB3, 0xA4, 0x89, 0x2C, 0x9E, 0xE7, 0xDA, 0xCE, 0x90, 0xEA, 0xCE, - 0x8F, 0xEE, 0xB9, 0x51, 0xEF, 0xBC, 0x48, 0x78, 0x46, 0x94, 0x3C, 0x1B, 0x52, 0x3B, 0x17, 0xBA, 0x3B, 0x13, 0xF2, 0x9D, 0x03, 0x95, 0x67, 0x40, - 0xC4, 0xB3, 0x9F, 0x9F, 0x8B, 0xC2, 0xF3, 0x1E, 0xBA, 0xF3, 0x1E, 0xE1, 0x59, 0xCF, 0xF1, 0x1C, 0xF7, 0x3B, 0xC7, 0xCD, 0xB3, 0x1D, 0xB5, 0x73, - 0x9D, 0xBB, 0x73, 0x1D, 0xE0, 0x59, 0x8E, 0xF1, 0x4C, 0x34, 0x9E, 0xDF, 0xCC, 0xB3, 0x9B, 0xE1, 0x99, 0x8C, 0xDB, 0xB9, 0xCB, 0xEF, 0x0C, 0x37, - 0x76, 0xCE, 0x82, 0x3C, 0x67, 0xB9, 0xE7, 0x2C, 0xBF, 0x33, 0x16, 0xE3, 0xD9, 0x8A, 0xEF, 0x5C, 0xE5, 0x9E, 0xA7, 0xCC, 0x73, 0x94, 0xE4, 0xF9, - 0x89, 0xF0, 0xFC, 0x76, 0x3C, 0xBF, 0x11, 0xCF, 0x6E, 0x3F, 0x27, 0xB1, 0x3B, 0x1F, 0x31, 0x9E, 0x8D, 0xD8, 0x33, 0x70, 0x78, 0x1E, 0x22, 0x3C, - 0x03, 0xB9, 0x3B, 0xFF, 0xEC, 0x99, 0xC7, 0xF0, 0xCC, 0x83, 0x9E, 0x77, 0x20, 0xCF, 0x3B, 0xE4, 0x1E, 0x81, 0x61, 0xA8, 0x51, 0x18, 0xAF, 0x4D, - 0x46, 0x19, 0x05, 0x05, 0x05, 0x85, 0x0C, 0x63, 0xE1, 0x96, 0x88, 0x88, 0xA2, 0x24, 0x49, 0x1E, 0xB2, 0x30, 0x96, 0x74, 0x24, 0x14, 0xD5, 0x45, - 0x65, 0x14, 0x94, 0x51, 0xA9, 0x1F, 0x63, 0x90, 0x61, 0x3B, 0xD0, 0xB0, 0x9A, 0xB8, 0x61, 0xBB, 0xD0, 0xB0, 0xD8, 0x45, 0x25, 0xBE, 0x37, 0x44, - 0x69, 0x3F, 0x3C, 0x0E, 0x10, 0x60, 0x0F, 0x82, 0x1F, 0x24, 0xED, 0x47, 0x1F, 0x0C, 0xC4, 0xA4, 0xC3, 0xEF, 0x5A, 0x71, 0xD2, 0xEE, 0xD3, 0xDA, - 0x0E, 0xA5, 0xA7, 0x25, 0x38, 0x79, 0xDF, 0xF4, 0x9B, 0x74, 0xBE, 0xBB, 0xA9, 0x8B, 0x19, 0x94, 0xD9, 0x0F, 0x8A, 0x08, 0xDE, 0x9B, 0x48, 0x23, - 0x55, 0xB5, 0x23, 0x62, 0x77, 0xED, 0x5F, 0x98, 0xB3, 0x29, 0xC8, 0x46, 0x84, 0x94, 0x15, 0xFB, 0xF8, 0x33, 0x54, 0xFE, 0x93, 0xDB, 0xBB, 0x77, - 0x05, 0x03, 0x55, 0xCF, 0xE7, 0x38, 0x3B, 0x3C, 0xEC, 0x3E, 0xBE, 0x63, 0x2C, 0xCE, 0x1B, 0x69, 0x75, 0x63, 0x73, 0x0E, 0x92, 0xDE, 0x2C, 0x85, - 0x8F, 0x50, 0x00, 0xD3, 0x22, 0x37, 0xAB, 0xD9, 0x6D, 0x12, 0x23, 0xD5, 0x69, 0x4E, 0x77, 0x93, 0x92, 0x94, 0xA4, 0x24, 0xA5, 0xA9, 0x4D, 0xA5, - 0x67, 0x10, 0x14, 0xE8, 0x4F, 0xF6, 0xF0, 0x48, 0x72, 0xE6, 0x39, 0x8A, 0xD9, 0xEC, 0x76, 0x20, 0xA1, 0xA2, 0x11, 0x03, 0xDA, 0x46, 0xE7, 0x56, - 0x62, 0x53, 0x91, 0xB3, 0x60, 0x58, 0x03, 0x04, 0xD6, 0xA0, 0x58, 0xE5, 0x7B, 0xBE, 0x03, 0xFD, 0x32, 0x24, 0x4D, 0x1D, 0x78, 0xAA, 0x5A, 0x6F, - 0x8F, 0xEA, 0x91, 0xE4, 0xCC, 0x73, 0x14, 0x33, 0x45, 0x85, 0x7C, 0xBC, 0x99, 0x53, 0xCE, 0x62, 0x94, 0x21, 0xDE, 0x3D, 0x64, 0x3A, 0x9E, 0x2A, - 0x0D, 0xE4, 0x0D, 0x8B, 0xD4, 0xE7, 0x39, 0xDD, 0xB3, 0x8D, 0x9E, 0xA0, 0xAB, 0x5D, 0x27, 0x13, 0x34, 0x65, 0x20, 0xBD, 0x53, 0xA2, 0x62, 0x13, - 0xA1, 0x23, 0x7C, 0x99, 0xE5, 0xA1, 0xF2, 0x83, 0x42, 0x07, 0xAF, 0x82, 0xCD, 0xC1, 0x77, 0x68, 0x3E, 0x58, 0x2C, 0x0E, 0x3E, 0x06, 0xC4, 0x35, - 0x0E, 0xEA, 0x55, 0xDE, 0xBB, 0xA0, 0x02, 0xE1, 0x12, 0x14, 0x96, 0xEA, 0xC6, 0xA0, 0x12, 0xA5, 0x08, 0x46, 0x81, 0x86, 0x26, 0x06, 0x84, 0x11, - 0x00, 0xC6, 0x5D, 0x68, 0x39, 0xC0, 0x1E, 0xA5, 0x03, 0x58, 0x8B, 0x56, 0x2A, 0xBF, 0x0B, 0xE6, 0x71, 0xD2, 0xF4, 0xD5, 0xCE, 0x06, 0x85, 0x28, - 0xA1, 0x18, 0x46, 0xB6, 0x02, 0x81, 0x26, 0x7A, 0x33, 0x09, 0xAD, 0xA6, 0xEF, 0x34, 0x89, 0x56, 0xCC, 0x92, 0xE9, 0x73, 0x91, 0x05, 0x05, 0xE2, - 0x6E, 0x88, 0x9E, 0xA7, 0x26, 0x47, 0xA5, 0x22, 0x92, 0x59, 0x7D, 0x44, 0x72, 0xA7, 0x54, 0x00, 0x59, 0x00, 0x49, 0x00, 0xD8, 0xF7, 0x8E, 0xFB, - 0xCE, 0x81, 0xDF, 0x37, 0xD0, 0xB7, 0xCD, 0xF2, 0x5D, 0xE3, 0xF7, 0x9E, 0x69, 0xDF, 0x6E, 0xFC, 0x86, 0x31, 0xBE, 0x5B, 0xE2, 0x37, 0x4B, 0xDC, - 0x5B, 0x05, 0xFB, 0x36, 0x81, 0xBE, 0x4B, 0xE2, 0xDE, 0xC9, 0xE5, 0x9B, 0xE4, 0xF8, 0x46, 0xC2, 0x6F, 0x90, 0xF7, 0xDD, 0xF1, 0x37, 0xC7, 0xD7, - 0x3B, 0x63, 0xF9, 0xB6, 0xA8, 0x88, 0x7A, 0x1B, 0xAB, 0xEF, 0x88, 0x87, 0xEA, 0xBB, 0xA1, 0xF9, 0x5E, 0x20, 0x84, 0xAB, 0xCE, 0x37, 0xA9, 0xF0, - 0xF8, 0xED, 0xCB, 0xF9, 0x26, 0x13, 0x1E, 0x07, 0x7F, 0xAF, 0x3C, 0x1E, 0xE1, 0x05, 0x6B, 0xD4, 0x33, 0x06, 0x6A, 0x19, 0x93, 0xF0, 0x4D, 0xC5, - 0x82, 0xA5, 0x60, 0xB9, 0x8C, 0xF1, 0x7B, 0x8B, 0xCA, 0x7C, 0x6B, 0x31, 0xC0, 0xD0, 0x77, 0x16, 0x32, 0xEE, 0x8D, 0x85, 0xCC, 0x4E, 0xDD, 0xAB, - 0x23, 0xD6, 0xF9, 0xB6, 0xAA, 0x3A, 0x80, 0xA7, 0x6F, 0x2A, 0xE4, 0x5B, 0xFA, 0xF7, 0x94, 0x94, 0xDA, 0xFB, 0x29, 0x0A, 0x7E, 0x47, 0xDF, 0x77, - 0x13, 0x93, 0x4B, 0xF8, 0x7E, 0x3E, 0x9B, 0xEF, 0xA7, 0xFB, 0xEE, 0x24, 0xA4, 0xF6, 0x6D, 0xD4, 0x9C, 0xBE, 0x87, 0x84, 0xB2, 0x6F, 0xA0, 0x9F, - 0x9E, 0x07, 0x20, 0x0A, 0xEE, 0xC1, 0x57, 0x33, 0xB0, 0x2B, 0xFD, 0x2D, 0xF4, 0x37, 0xCE, 0xDF, 0x31, 0x7F, 0xAB, 0xFC, 0x2D, 0xF2, 0x77, 0xC6, - 0xDF, 0x0E, 0x7F, 0x1B, 0xFC, 0xD9, 0xF0, 0xCF, 0xD9, 0xF8, 0xCF, 0x75, 0x7F, 0xBE, 0xFE, 0xD9, 0xFA, 0xE7, 0xB1, 0x3F, 0x4B, 0xFF, 0xCC, 0xF4, - 0xE7, 0xA1, 0xFF, 0x19, 0x25, 0x53, 0xFC, 0x39, 0xE1, 0xCF, 0x03, 0xFF, 0x0B, 0xFB, 0xFE, 0xE3, 0xFD, 0x97, 0x4B, 0xFB, 0x2F, 0xF6, 0x7F, 0x1A, - 0x3D, 0x0A, 0x57, 0x2B, 0x57, 0x94, 0x48, 0x47, 0x06, 0x05, 0xE4, 0x27, 0x70, 0x38, 0xCC, 0xEE, 0xD5, 0x5D, 0xAD, 0x5D, 0x4D, 0x3D, 0x30, 0x23, - 0x13, 0x7F, 0xB1, 0x18, 0x78, 0x07, 0x87, 0xD6, 0x5A, 0x3D, 0x83, 0x6A, 0x58, 0x5A, 0x8D, 0x86, 0xBF, 0x6D, 0x07, 0xF9, 0x88, 0x6E, 0x64, 0x44, - 0x68, 0x51, 0x8E, 0xAB, 0xF4, 0x90, 0x30, 0xF8, 0x89, 0xCF, 0xEA, 0xCD, 0xB5, 0x85, 0x41, 0x21, 0xF9, 0xD0, 0xAC, 0x8C, 0x6C, 0x40, 0xFC, 0x03, - 0x2D, 0x60, 0x1E, 0xDD, 0x5B, 0x19, 0x56, 0xF4, 0x88, 0xE7, 0xE5, 0x24, 0x04, 0x43, 0xA0, 0xF8, 0x67, 0xC7, 0xE6, 0xE5, 0xC4, 0x5C, 0xE9, 0x08, - 0xC8, 0x31, 0x5C, 0x98, 0x74, 0x29, 0xE8, 0x75, 0x51, 0x7E, 0x5C, 0x2C, 0x3C, 0x80, 0xB1, 0xA8, 0x71, 0xA0, 0x29, 0x10, 0x53, 0x90, 0x14, 0x14, - 0x6A, 0x0D, 0x63, 0xF1, 0x9A, 0x88, 0xB4, 0x20, 0x05, 0x85, 0x74, 0xE0, 0x19, 0x03, 0xDD, 0x4E, 0x9B, 0xEF, 0x91, 0x2F, 0x84, 0x6D, 0xC0, 0x0E, - 0x04, 0x38, 0xDE, 0x19, 0xAD, 0x52, 0xC8, 0x5A, 0x0A, 0xA7, 0xFD, 0xA3, 0x57, 0x7E, 0xDF, 0xD4, 0x3D, 0x88, 0x0A, 0x70, 0x54, 0x64, 0x9C, 0x46, - 0x0D, 0x74, 0x3B, 0xF6, 0xCB, 0xAE, 0x7D, 0xD8, 0x7F, 0x75, 0x8A, 0x3A, 0x29, 0xE4, 0x8D, 0x5D, 0x4A, 0x3C, 0x55, 0x7C, 0x9D, 0xFF, 0x1A, 0xAA, - 0x78, 0x5F, 0xA9, 0xFC, 0x72, 0x51, 0xF2, 0x1D, 0xB6, 0x12, 0xD7, 0xB6, 0x5F, 0xB8, 0xE7, 0x0D, 0xA2, 0xEC, 0xBF, 0xCE, 0x51, 0x89, 0x77, 0x61, - 0xFD, 0x06, 0xBC, 0xBD, 0x1B, 0x6D, 0x4F, 0xB0, 0x94, 0xE9, 0x8F, 0xF0, 0xCB, 0xDA, 0xE9, 0xB8, 0xEE, 0xD7, 0x59, 0x5A, 0xAC, 0x1F, 0x55, 0xC5, - 0x26, 0x90, 0x22, 0xE3, 0xC5, 0x8A, 0x63, 0x81, 0xB7, 0xDC, 0x07, 0x00, 0xD4, 0x0D, 0xD2, 0xE1, 0xF1, 0x04, 0x0B, 0x00, 0x00, 0x16, 0x27, 0x37, - 0x49, 0x58, 0x6B, 0x7F, 0x90, 0xA0, 0xB2, 0xC5, 0xD6, 0xE9, 0xF8, 0x09, 0x0C, 0x00, 0x00, 0x19, 0x26, 0x36, 0x52, 0x6E, 0x8D, 0x9A, 0xAA, 0xB9, - 0xCD, 0xE0, 0xF0, 0x07, 0x0D, 0x00, 0x00, 0x1D, 0x30, 0x44, 0x58, 0x6D, 0x81, 0x96, 0xA5, 0xB4, 0xC5, 0xD8, 0xE8, 0xFD, 0x0E, 0x0E, 0x00, 0x00, - 0x1F, 0x35, 0x47, 0x5C, 0x6D, 0x7B, 0x91, 0xA2, 0xB3, 0xC4, 0xD6, 0xE9, 0xFF, 0x13, 0x0F, 0x00, 0x00, 0x21, 0x35, 0x45, 0x55, 0x65, 0x77, 0x86, - 0x99, 0xAA, 0xBA, 0xCE, 0xDF, 0xF0, 0x0A, 0x10, 0x31, 0x44, 0x56, 0x69, 0x7F, 0x90, 0xA1, 0x10, 0xC7, 0xD1, 0xDD, 0xE8, 0xF4, 0xFF, 0x0B, 0x11, - 0x00, 0x00, 0x19, 0x28, 0x34, 0x41, 0x4E, 0x58, 0x65, 0x72, 0x7E, 0x8B, 0x97, 0xA5, 0xB0, 0xC0, 0xCF, 0xDE, 0xEB, 0xF6, 0x04, 0x12, 0x00, 0x00, - 0x0F, 0x19, 0x24, 0x32, 0x41, 0x4B, 0x57, 0x63, 0x74, 0x83, 0x8D, 0x9A, 0xA8, 0xB5, 0xC3, 0xD2, 0xDD, 0xEC, 0xF9, 0x06, 0x13, 0x00, 0x00, 0x15, - 0x23, 0x31, 0x3E, 0x4F, 0x61, 0x6E, 0x7A, 0x85, 0x90, 0x9D, 0xAB, 0xB7, 0xC3, 0xD0, 0xE2, 0xF3, 0xFD, 0x07, 0x14, 0x00, 0x00, 0x17, 0x26, 0x35, - 0x40, 0x4E, 0x5D, 0x6A, 0x76, 0x82, 0x90, 0x9F, 0xAA, 0xB8, 0xC7, 0xDA, 0xE6, 0xF4, 0x01, 0x15, 0x00, 0x00, 0x0D, 0x1B, 0x27, 0x34, 0x3F, 0x4A, - 0x5D, 0x6E, 0x7E, 0x8A, 0x98, 0xA7, 0xB8, 0xC5, 0xD1, 0xE4, 0xF1, 0x01, 0x16, 0x00, 0x00, 0x12, 0x22, 0x36, 0x46, 0x80, 0xB1, 0xA4, 0x20, 0xBA, - 0x35, 0xF0, 0xD9, 0x0C, 0x00, 0xEC, 0x30, 0x7B, 0x1E, 0xFF, 0x79, 0x84, 0x00, 0xC8, 0x38, 0x60, 0x6E, 0xE4, 0xCD, 0x31, 0x14, 0x46, 0x1F, 0x5C, - 0x6B, 0x00, 0xFA, 0x7F, 0x5C, 0x1D, 0x38, 0x20, 0x1E, 0xD7, 0x1A, 0xAF, 0x65, 0x60, 0xB8, 0x1E, 0x92, 0x0F, 0x23, 0xD0, 0x7E, 0x9E, 0x1C, 0x6D, - 0x3F, 0x0F, 0x7C, 0x87, 0x4B, 0xCA, 0xD9, 0x16, 0x81, 0xD7, 0x09, 0x04, 0xD0, 0xCF, 0x63, 0x02, 0xBF, 0xF0, 0x1B, 0x20, 0xC3, 0x27, 0x2A, 0x6A, - 0x23, 0x49, 0x88, 0x18, 0x5E, 0xAA, 0x71, 0x32, 0x3F, 0x1E, 0xB7, 0x0A, 0x6E, 0x4D, 0xA6, 0xE0, 0x01, 0xD7, 0x01, 0xD4, 0x01, 0x89, 0x75, 0xD2, - 0x42, 0x37, 0xBA, 0x72, 0xDA, 0xAA, 0x37, 0xA2, 0x31, 0x34, 0xBA, 0x32, 0x3E, 0xD8, 0xDF, 0xEA, 0x22, 0xE0, 0xDA, 0x88, 0x5D, 0x6D, 0xD3, 0xB6, - 0x1B, 0xBD, 0xBB, 0x78, 0xEE, 0x4A, 0x32, 0xB7, 0x18, 0xEC, 0xD0, 0xA7, 0x74, 0xA3, 0xDC, 0x4F, 0xFA, 0xEE, 0xBD, 0x21, 0xF3, 0xED, 0x26, 0xED, - 0x62, 0xC2, 0xBC, 0x89, 0x9B, 0xC4, 0x97, 0x7D, 0xA7, 0x08, 0xB7, 0xBA, 0xC1, 0x77, 0xF7, 0xE6, 0x46, 0x91, 0xDC, 0x49, 0xB6, 0x59, 0xD3, 0xCE, - 0x6C, 0x63, 0x43, 0x52, 0xB5, 0x9E, 0x68, 0x23, 0x2E, 0x55, 0x39, 0xB4, 0xD1, 0x81, 0x36, 0xA0, 0x3D, 0xC4, 0x2A, 0x20, 0x88, 0xFD, 0xE3, 0x0D, - 0x7A, 0xC4, 0xD9, 0x66, 0xB4, 0xB0, 0x87, 0xD9, 0xE5, 0xA7, 0x88, 0x1F, 0xB0, 0x09, 0x60, 0x9F, 0x9E, 0xF6, 0xD3, 0x3E, 0x97, 0x3B, 0x09, 0xC8, - 0x49, 0x2A, 0x2B, 0xE7, 0x59, 0xB4, 0x79, 0x68, 0xF4, 0xF5, 0x05, 0xB8, 0x40, 0xFF, 0x01, 0x4D, 0x3E, 0x8B, 0xE4, 0xFC, 0x3F, 0xF8, 0xAA, 0x00, - 0x83, 0x3B, 0xEF, 0x26, 0x93, 0x46, 0x51, 0xA7, 0x54, 0xE9, 0x59, 0xFF, 0x2F, 0x82, 0xC3, 0x26, 0x4D, 0xA3, 0x62, 0x22, 0x7A, 0xF0, 0xCC, 0x87, - 0x83, 0x2D, 0x4D, 0x44, 0x99, 0xEB, 0x80, 0x98, 0x9B, 0x85, 0xFA, 0xB9, 0x45, 0x82, 0x2C, 0xE3, 0x98, 0x49, 0x9C, 0x72, 0xE4, 0x07, 0x8E, 0x19, - 0x68, 0x21, 0xC2, 0x2C, 0x80, 0x75, 0x6D, 0x11, 0x08, 0x4B, 0x4D, 0xFF, 0x00, 0xCE, 0xB4, 0x1C, 0x47, 0x2F, 0x86, 0x81, 0x78, 0x6F, 0x2B, 0xC6, - 0xCA, 0x5B, 0x63, 0xAA, 0xEA, 0xBF, 0x05, 0xA0, 0x39, 0x27, 0x4D, 0x62, 0x21, 0xA0, 0x1E, 0xDC, 0x8A, 0xB2, 0x0A, 0x5B, 0x55, 0xD7, 0x5F, 0x2B, - 0x25, 0x64, 0x19, 0x69, 0x86, 0x4B, 0x94, 0x3F, 0x3B, 0x33, 0x8C, 0x35, 0x15, 0x68, 0x26, 0xCA, 0x3E, 0x84, 0x95, 0x27, 0x78, 0x19, 0x00, 0x43, - 0x51, 0x93, 0x8A, 0xFE, 0xEC, 0x50, 0x21, 0xCD, 0xCB, 0x3A, 0x73, 0xDE, 0x40, 0x82, 0x41, 0x2B, 0xF1, 0xBB, 0x73, 0x53, 0x13, 0x61, 0xA1, 0x81, - 0x95, 0xAD, 0xC5, 0x5A, 0x95, 0x88, 0x7E, 0xAA, 0x3A, 0x90, 0x26, 0xA6, 0x1D, 0xCA, 0xC8, 0x46, 0x2D, 0x84, 0x46, 0xF4, 0xE4, 0x5E, 0x98, 0x71, - 0xF0, 0x22, 0xD0, 0x85, 0xFA, 0xA7, 0x8C, 0x36, 0x2D, 0x23, 0x19, 0xB3, 0x38, 0x11, 0xB2, 0x23, 0x8E, 0xED, 0x4C, 0x4C, 0x82, 0x02, 0x71, 0xB6, - 0xFA, 0x63, 0x87, 0xF8, 0xD4, 0x61, 0xB3, 0x88, 0x56, 0x12, 0x04, 0x2F, 0x8E, 0xAD, 0x2C, 0x03, 0x04, 0xB2, 0x56, 0xEA, 0x99, 0x92, 0x8D, 0x5B, - 0x08, 0xCF, 0x79, 0x35, 0x56, 0x90, 0x95, 0x1D, 0x90, 0xB7, 0xD4, 0xFF, 0x1A, 0x15, 0xA4, 0x87, 0x32, 0xB2, 0x71, 0x31, 0x4B, 0xE1, 0x92, 0x73, - 0x9F, 0x5D, 0x5C, 0x5B, 0x8A, 0x11, 0x62, 0x60, 0x0C, 0x06, 0x54, 0x6D, 0x9F, 0xA2, 0x00, 0x00, 0x4A, 0x53, 0xBE, 0x39, 0xA3, 0x88, 0x25, 0x8A, - 0xE0, 0x39, 0x48, 0x8F, 0xBB, 0xBA, 0x39, 0x38, 0x1A, 0x6D, 0x25, 0x30, 0x70, 0x91, 0xB7, 0x3F, 0x94, 0x94, 0xD0, 0xE5, 0xE3, 0x62, 0x95, 0x42, - 0x26, 0x82, 0x81, 0x67, 0x21, 0x79, 0x36, 0xD6, 0x4C, 0x94, 0x8D, 0x49, 0x38, 0xD0, 0xB5, 0xFE, 0x4F, 0xCF, 0x3D, 0x91, 0x65, 0xB4, 0x10, 0xFE, - 0xD5, 0xB1, 0x9D, 0x89, 0x45, 0x10, 0xC0, 0xFD, 0x9D, 0xA8, 0x81, 0x2A, 0xD5, 0x2C, 0x52, 0x09, 0xBC, 0x93, 0x71, 0x42, 0x43, 0x30, 0x00, 0x19, - 0xA1, 0x70, 0x81, 0x34, 0xC8, 0x82, 0x68, 0x4C, 0x6D, 0x8C, 0x16, 0x9D, 0x7B, 0x1B, 0x9F, 0x11, 0x25, 0x7D, 0x19, 0xDA, 0xF2, 0x0A, 0x36, 0x22, - 0x12, 0x19, 0xB0, 0xB1, 0xD7, 0x98, 0xEB, 0xDE, 0x13, 0x23, 0xAA, 0x50, 0xC5, 0x98, 0x7D, 0x13, 0x76, 0xC7, 0xF8, 0x79, 0x69, 0x9B, 0xAD, 0x8B, - 0x77, 0x23, 0x5E, 0x23, 0xC5, 0x46, 0x52, 0xCD, 0x35, 0xCF, 0xF1, 0x50, 0x8C, 0x2E, 0x22, 0x26, 0x82, 0x43, 0x3C, 0xA7, 0xE6, 0x5D, 0xAB, 0x60, - 0x97, 0xEF, 0x6E, 0x81, 0x2F, 0x20, 0xD5, 0xD2, 0x1E, 0x31, 0x92, 0xB8, 0xE8, 0x72, 0x69, 0xEB, 0x8F, 0xB6, 0xD9, 0x8A, 0x95, 0xE1, 0xF9, 0xEE, - 0x4A, 0xF5, 0x3E, 0x97, 0x2D, 0x93, 0xB3, 0x76, 0x47, 0xB3, 0x3A, 0x0B, 0xB3, 0x2A, 0x1F, 0xB0, 0xCD, 0x4B, 0x93, 0x97, 0x82, 0xB2, 0x6A, 0xB2, - 0x86, 0xB0, 0xFA, 0x51, 0xD3, 0x25, 0xED, 0x72, 0x5E, 0x1D, 0x34, 0x4C, 0x95, 0x62, 0xAD, 0xA1, 0xCB, 0xEB, 0xDC, 0x49, 0xD5, 0xA0, 0x5E, 0x15, - 0xDE, 0x8B, 0xBB, 0x16, 0x07, 0xA3, 0xA2, 0x2A, 0x2D, 0x2C, 0x2C, 0x46, 0xF0, 0x05, 0xDC, 0xDA, 0xE6, 0x25, 0x1E, 0x55, 0x48, 0xF6, 0x9D, 0xEA, - 0xCE, 0xC7, 0x46, 0x0C, 0x63, 0x3E, 0x86, 0xD1, 0x3C, 0xBB, 0x47, 0x0C, 0x1F, 0x2A, 0xB1, 0xCD, 0x78, 0x61, 0x3B, 0x18, 0x61, 0x17, 0x63, 0xE1, - 0x0F, 0x4C, 0xF5, 0xBD, 0xC2, 0x5C, 0xD4, 0x1F, 0x27, 0xDB, 0x88, 0x76, 0x59, 0x1C, 0x0E, 0x6B, 0x57, 0x84, 0xAD, 0x61, 0x2A, 0x52, 0xFA, 0x83, - 0xA6, 0x80, 0x6A, 0x26, 0x3F, 0xB5, 0x19, 0x9F, 0xA6, 0x40, 0x4C, 0x67, 0xA3, 0x9E, 0xAB, 0xC0, 0x73, 0xBE, 0x4A, 0x30, 0x9D, 0xFB, 0x4D, 0xD6, - 0x44, 0x71, 0xC0, 0x6F, 0xA0, 0x54, 0x01, 0xD8, 0xDC, 0xFA, 0x08, 0x46, 0x83, 0xE5, 0x06, 0x07, 0x82, 0x01, 0x73, 0x9C, 0xE9, 0xAE, 0x0E, 0x6C, - 0x80, 0xE1, 0xAE, 0x67, 0xB0, 0x8A, 0x0C, 0x69, 0x27, 0x9F, 0xB8, 0x66, 0x9A, 0x9D, 0x5A, 0x36, 0x80, 0x0C, 0x0C, 0xE2, 0x0B, 0xDC, 0xA5, 0x05, - 0x26, 0x63, 0xD9, 0xC4, 0x97, 0x0B, 0x4B, 0x69, 0xBD, 0x53, 0x46, 0x36, 0xD6, 0x72, 0x0B, 0xA6, 0x92, 0x62, 0x03, 0x27, 0xD9, 0xE6, 0xCC, 0xEF, - 0xE5, 0xDB, 0xDC, 0x8D, 0x77, 0x8D, 0x08, 0xFB, 0x43, 0xBB, 0xCC, 0x65, 0x07, 0x1D, 0xF8, 0x05, 0x9C, 0xBB, 0xF7, 0xCC, 0xBE, 0x65, 0x5F, 0xF6, - 0x9B, 0x2A, 0xBF, 0x63, 0x74, 0xD5, 0x0B, 0xBF, 0xF5, 0xF6, 0x76, 0xCA, 0x05, 0xB8, 0x55, 0x96, 0x4A, 0x0D, 0xFB, 0x8D, 0xFA, 0x95, 0xD3, 0x45, - 0x58, 0xBF, 0x54, 0xA5, 0x37, 0x67, 0xE0, 0x37, 0x6D, 0xC4, 0x49, 0x6F, 0x9C, 0xA8, 0xE1, 0x19, 0xBD, 0x61, 0xBC, 0x8A, 0xDD, 0x26, 0x52, 0xB2, - 0xE5, 0x1E, 0x47, 0x69, 0x67, 0xB3, 0xCB, 0xDE, 0xB5, 0xBB, 0x4B, 0xA4, 0xE6, 0x05, 0x01, 0x96, 0x8D, 0xEC, 0xE0, 0x15, 0xFB, 0x38, 0xCC, 0x5B, - 0x9E, 0x96, 0xD1, 0x62, 0x79, 0xC7, 0xD8, 0xC8, 0xAA, 0x29, 0xC2, 0xAF, 0xF7, 0x9E, 0x32, 0xE2, 0x85, 0x7A, 0x01, 0x78, 0xEB, 0x2B, 0x19, 0x21, - 0xAC, 0x58, 0xA0, 0xEE, 0x1F, 0xEC, 0x99, 0x66, 0x37, 0x53, 0xCD, 0xBD, 0x07, 0xFD, 0x0D, 0xCF, 0x52, 0xA8, 0x1A, 0x40, 0x61, 0x32, 0x37, 0x51, - 0x2A, 0xB2, 0x84, 0x6A, 0x3E, 0x9E, 0x8D, 0x08, 0x1D, 0xB4, 0x33, 0x0A, 0x50, 0x7F, 0x72, 0x62, 0x3B, 0xDB, 0xD2, 0xAB, 0xA7, 0x4E, 0xA5, 0x48, - 0xD0, 0x43, 0x5F, 0x14, 0x3D, 0xF4, 0x88, 0xE8, 0x46, 0x00, 0xFE, 0x34, 0xFA, 0x51, 0x52, 0xFD, 0xA4, 0xFF, 0xF9, 0xA6, 0xA7, 0x6F, 0xE7, 0xEE, - 0x4E, 0xE9, 0xFE, 0xC0, 0x36, 0x33, 0x59, 0xD1, 0xB2, 0x62, 0x70, 0x57, 0x6E, 0x0D, 0xC1, 0x5A, 0x5D, 0x6F, 0x79, 0x9A, 0xDE, 0xE2, 0x69, 0x21, - 0x69, 0x15, 0x55, 0x37, 0x1C, 0xB3, 0x4F, 0x0F, 0xF5, 0x08, 0x4F, 0x95, 0x5B, 0x79, 0x37, 0x3A, 0x59, 0x93, 0x62, 0xB4, 0x12, 0x95, 0x55, 0x0C, - 0x16, 0x9A, 0xD0, 0x49, 0xD6, 0xA0, 0x52, 0xA1, 0xE0, 0x37, 0x8F, 0x60, 0x15, 0x14, 0xB9, 0x52, 0x89, 0x48, 0xD5, 0x11, 0x82, 0xAD, 0xFA, 0x49, - 0x2D, 0xF6, 0x9D, 0xC3, 0x1E, 0x36, 0x2E, 0x4F, 0xEA, 0x77, 0x59, 0x39, 0xDE, 0x48, 0x37, 0xF5, 0xC2, 0x46, 0x16, 0xDC, 0xF5, 0x84, 0x8D, 0xF8, - 0x1C, 0x60, 0xE6, 0x94, 0x52, 0x56, 0xA7, 0xC9, 0x9E, 0x08, 0xC2, 0x29, 0xA1, 0x43, 0x3D, 0x48, 0xEC, 0x0F, 0xC2, 0xEA, 0xAE, 0xF2, 0xA9, 0x50, - 0x38, 0x8D, 0xAF, 0x5A, 0x52, 0xB3, 0x2B, 0x0B, 0x42, 0x9D, 0x3E, 0xD7, 0xBE, 0x53, 0x99, 0xAC, 0xA2, 0x29, 0xBD, 0x63, 0xDA, 0x66, 0x48, 0xAC, - 0x34, 0x44, 0x55, 0x3F, 0xC4, 0x42, 0x33, 0x85, 0xF8, 0x59, 0xAB, 0x59, 0xFD, 0xC3, 0x02, 0xB2, 0xB7, 0xDD, 0xDA, 0xDE, 0x6E, 0x3B, 0x56, 0x17, - 0xF5, 0xF4, 0xB8, 0xA6, 0x3B, 0x33, 0xD2, 0x0D, 0xF5, 0xE6, 0x6D, 0x84, 0x5E, 0x64, 0x2D, 0x47, 0x5C, 0x4D, 0x0A, 0x2D, 0xC0, 0xF4, 0x4C, 0x7B, - 0x2D, 0x31, 0x02, 0x6B, 0x2C, 0x8C, 0x79, 0x31, 0xAC, 0x0D, 0x94, 0xDC, 0xC4, 0xA8, 0x17, 0xA2, 0x34, 0x92, 0xE4, 0x15, 0xD8, 0x65, 0x2E, 0x96, - 0x36, 0x9A, 0x00, 0xCB, 0x49, 0x2E, 0x74, 0xC7, 0x36, 0x37, 0x92, 0xC9, 0x6C, 0xD3, 0x47, 0xDB, 0x66, 0xA6, 0x05, 0x64, 0x24, 0x59, 0xAA, 0x53, - 0x23, 0xDB, 0xF2, 0x99, 0xFC, 0x37, 0x4E, 0xD7, 0xDD, 0x7B, 0x2C, 0xEC, 0x77, 0x20, 0x8B, 0x35, 0x20, 0x6F, 0x81, 0xC9, 0xDD, 0x3A, 0x91, 0xBC, - 0x44, 0x56, 0xFA, 0x73, 0xA5, 0x59, 0x0B, 0xDE, 0x4D, 0x5B, 0x7D, 0x63, 0x8D, 0x13, 0x81, 0x9A, 0x9A, 0xCF, 0xD1, 0xE9, 0xC2, 0x4C, 0xED, 0xC8, - 0xE8, 0xDC, 0x10, 0xE6, 0x49, 0x14, 0x6F, 0xD6, 0xC5, 0xF9, 0xDD, 0x52, 0xB3, 0x65, 0x79, 0x98, 0x2B, 0x35, 0xDF, 0xD5, 0xB9, 0xF0, 0xEF, 0x4C, - 0x80, 0x5C, 0xF9, 0x18, 0xA7, 0xAF, 0xF8, 0xDC, 0x6C, 0xB3, 0xD9, 0x55, 0xD9, 0x13, 0x7A, 0xB0, 0x2B, 0xAF, 0xAC, 0xF9, 0x5A, 0xB6, 0xD9, 0x2A, - 0x77, 0x85, 0xBD, 0x8A, 0x2A, 0x04, 0x43, 0x10, 0x95, 0xC3, 0x26, 0x93, 0x5F, 0x1F, 0x2B, 0x74, 0x4F, 0x55, 0x49, 0x7A, 0x9C, 0x67, 0x1A, 0x87, - 0xCA, 0xE1, 0x90, 0x69, 0xA9, 0x97, 0x34, 0x53, 0x0D, 0x67, 0xA4, 0x8F, 0x65, 0x92, 0xA0, 0x63, 0x8C, 0x86, 0x80, 0x49, 0x82, 0xF0, 0xE5, 0xE8, - 0x31, 0xF1, 0x98, 0x88, 0x46, 0x72, 0x0E, 0x5F, 0x88, 0x96, 0x1A, 0xA1, 0x72, 0x1F, 0x85, 0x27, 0x23, 0x8C, 0x5A, 0x70, 0x22, 0xA5, 0x1A, 0x7C, - 0x9B, 0x20, 0x74, 0x7A, 0xE4, 0x40, 0x1C, 0xBB, 0x8A, 0x8C, 0x89, 0x48, 0xDD, 0x84, 0x3C, 0xCD, 0x6F, 0xA8, 0xA8, 0xEF, 0x6B, 0x7B, 0xE3, 0xBC, - 0x50, 0x4F, 0xB5, 0x53, 0x53, 0x9D, 0x55, 0x33, 0xD5, 0x56, 0xBB, 0xAE, 0xD6, 0xD4, 0x9A, 0x6A, 0x5A, 0x4B, 0xEB, 0xA5, 0xA4, 0x1A, 0xE9, 0xA8, - 0x36, 0xAA, 0x8B, 0xEA, 0x68, 0x0D, 0xAD, 0xEB, 0xFA, 0x2F, 0x74, 0x42, 0xC7, 0xFC, 0xA0, 0x0B, 0x7A, 0xA0, 0x03, 0xFA, 0xE6, 0xFF, 0xDC, 0xCF, - 0xFB, 0x5C, 0xF3, 0x7C, 0xBE, 0xE7, 0x99, 0xC7, 0x3C, 0x66, 0xED, 0xF4, 0xE7, 0x4F, 0x9C, 0x45, 0xAD, 0x54, 0x73, 0x9F, 0xF4, 0xAE, 0x3A, 0xC1, - 0x6B, 0x3C, 0x47, 0xDD, 0xE5, 0x70, 0x5C, 0x46, 0x84, 0x33, 0x5E, 0x5D, 0xAE, 0x5E, 0xFA, 0xD1, 0x41, 0xA0, 0x1D, 0xF9, 0xDB, 0x25, 0xE5, 0x65, - 0xFA, 0x7A, 0x2C, 0x9F, 0xE7, 0xCE, 0xED, 0x1C, 0x4F, 0xE5, 0xEB, 0x9C, 0x92, 0xCE, 0x3F, 0xA7, 0xAE, 0x4E, 0x5E, 0xCE, 0x25, 0x1F, 0xE7, 0x91, - 0x87, 0xF3, 0x37, 0x6D, 0xCE, 0x66, 0xCD, 0x21, 0x1F, 0x7F, 0x78, 0x34, 0x8E, 0x3F, 0xF3, 0xC6, 0x9B, 0x79, 0x19, 0xC6, 0x8F, 0xB9, 0x98, 0x87, - 0x39, 0x13, 0x37, 0x5F, 0xBC, 0xA2, 0x97, 0x77, 0x49, 0x3C, 0x97, 0x6F, 0x39, 0xE2, 0xB5, 0x64, 0xF9, 0xE1, 0xB1, 0x5C, 0x39, 0x18, 0x32, 0xB7, - 0x42, 0xC5, 0xF0, 0x90, 0x9F, 0xF2, 0xC2, 0x4B, 0x79, 0x94, 0x13, 0x1E, 0x4A, 0x1D, 0xF5, 0x27, 0x77, 0xF2, 0x26, 0x67, 0xF2, 0xC1, 0x25, 0x57, - 0x72, 0xC1, 0x27, 0x39, 0x92, 0xC0, 0x1F, 0xB9, 0x11, 0xC6, 0x3F, 0xBE, 0xC8, 0xAB, 0x1C, 0x7D, 0xC0, 0x9F, 0x4F, 0xE4, 0x43, 0xFE, 0xF7, 0x2F, - 0x17, 0xF2, 0x20, 0x40, 0xEE, 0xF7, 0x1F, 0xF7, 0xF1, 0xBE, 0xF7, 0xA8, 0x9E, 0xEF, 0x7B, 0x07, 0xA3, 0x77, 0x1E, 0xB5, 0x81, 0xE7, 0x1D, 0xAF, - 0xBE, 0xF0, 0x1D, 0xBF, 0x3B, 0x82, 0xDB, 0xE5, 0xA8, 0xA3, 0xBB, 0xF1, 0xB9, 0xDB, 0x90, 0x8B, 0x83, 0xFB, 0x1B, 0x8D, 0xD3, 0xFD, 0x69, 0xC6, - 0x65, 0x3C, 0xC6, 0xDB, 0xAE, 0xE3, 0x30, 0x5E, 0xDC, 0xCD, 0xC5, 0xD7, 0xDE, 0x82, 0xC5, 0x57, 0xA8, 0x3D, 0xC5, 0xD3, 0x8E, 0x76, 0x14, 0x27, - 0xD8, 0x8E, 0xE3, 0x67, 0x37, 0x71, 0xB3, 0x97, 0x38, 0x89, 0x97, 0x7D, 0xC4, 0x45, 0xFC, 0x7A, 0xB2, 0x8F, 0x3D, 0x04, 0x88, 0x7F, 0xB8, 0x87, - 0xB3, 0x75, 0x38, 0x87, 0x6F, 0xF8, 0xBA, 0xD8, 0x35, 0x3C, 0xC3, 0xA1, 0x1E, 0x76, 0x0C, 0xBF, 0x70, 0x09, 0x2F, 0xDF, 0xC2, 0x2B, 0x9C, 0xC2, - 0x27, 0x1C, 0xEC, 0x5F, 0x5E, 0xEF, 0x7A, 0x84, 0x73, 0x1D, 0xC2, 0x1F, 0x38, 0x78, 0x83, 0xAB, 0x38, 0x83, 0x2F, 0xB8, 0x84, 0x6F, 0x9D, 0xEE, - 0x0A, 0x9E, 0xE0, 0x08, 0x0E, 0xDC, 0xC0, 0x0B, 0x9C, 0xC0, 0x07, 0x08, 0xF8, 0xDA, 0x13, 0xD6, 0xC9, 0xBF, 0x1A, 0x8B, 0xC3, 0xE0, 0x7F, 0xEF, - 0xED, 0xFE, 0x66, 0xF1, 0xAD, 0xF6, 0xD9, 0x53, 0x7D, 0xEA, 0xFA, 0xA8, 0x07, 0x85, 0x86, 0xA8, 0xB3, 0xD3, 0x46, 0x76, 0x81, 0x05, 0x05, 0x05, - 0x05, 0x45, 0x29, 0xB4, 0x06, 0xA3, 0x48, 0x23, 0x8A, 0x84, 0x39, 0x84, 0x08, 0xA2, 0x9A, 0xA4, 0x03, 0xC3, 0x65, 0x80, 0xC6, 0x25, 0x10, 0x49, - 0x52, 0x92, 0x14, 0x94, 0x7F, 0xA4, 0xD2, 0x6E, 0x5F, 0xB6, 0xF8, 0x56, 0xE0, 0x8B, 0x06, 0x2B, 0x36, 0xA7, 0xBE, 0x8C, 0xD2, 0xB1, 0x3A, 0x9C, - 0xCD, 0xA9, 0x88, 0x2E, 0x21, 0x0B, 0x7A, 0xAA, 0xD3, 0xED, 0xC4, 0x4B, 0x83, 0x4A, 0xEA, 0x27, 0xD7, 0x95, 0x19, 0x8C, 0xB3, 0x31, 0x21, 0x93, - 0xD1, 0xAF, 0x76, 0x6A, 0xEB, 0x97, 0x33, 0x06, 0xE2, 0x3A, 0x73, 0x30, 0xF3, 0x71, 0xDC, 0xF9, 0x15, 0x2D, 0xBF, 0xFC, 0x74, 0x18, 0x98, 0xFE, - 0x55, 0x95, 0xEB, 0x7F, 0xB2, 0x1D, 0x3D, 0x5B, 0x68, 0x0A, 0x89, 0xDA, 0x51, 0xB1, 0x1A, 0x9D, 0xC4, 0xB6, 0x92, 0xA8, 0xA0, 0x62, 0x8F, 0xB8, - 0xB8, 0x34, 0xBA, 0x45, 0x9B, 0xBC, 0xE8, 0x6A, 0x71, 0x86, 0xA2, 0xE9, 0x16, 0xAC, 0x99, 0xEE, 0xB8, 0xEA, 0x17, 0x42, 0xBB, 0x3E, 0xAD, 0xD7, - 0x90, 0xE2, 0x1A, 0x47, 0xAE, 0xCB, 0xAD, 0x5E, 0xC0, 0x39, 0x3E, 0xE2, 0x5A, 0x2B, 0x76, 0x70, 0x0E, 0x79, 0xDD, 0x8B, 0x0D, 0x45, 0x0C, 0x8B, - 0xF5, 0xBA, 0x9F, 0xBE, 0x78, 0xA0, 0xA5, 0xBB, 0x09, 0x35, 0x95, 0x75, 0xA9, 0xEB, 0xFE, 0x33, 0x39, 0x6A, 0xB7, 0x75, 0x19, 0xB7, 0x76, 0x5C, - 0x8E, 0xA8, 0x7D, 0x9A, 0xC7, 0xD6, 0xBF, 0xF1, 0xE6, 0x02, 0xDC, 0xA2, 0x4C, 0x6C, 0x1D, 0xD3, 0xFF, 0x15, 0xEA, 0xBA, 0xD0, 0xEC, 0xDC, 0x5A, - 0x89, 0xE5, 0x19, 0xA6, 0x99, 0x79, 0x2B, 0xFE, 0x8D, 0x8A, 0x64, 0x76, 0x9B, 0xC4, 0xC9, 0x79, 0xB4, 0xF3, 0xD7, 0x66, 0x0F, 0x0D, 0x43, 0xDE, - 0x1B, 0x07, 0x22, 0x57, 0xE6, 0xB4, 0xC5, 0xDE, 0x1D, 0x98, 0x80, 0xD3, 0xDF, 0xD0, 0xFB, 0x46, 0xE4, 0xFE, 0xFE, 0x78, 0xFA, 0xBC, 0x0F, 0x67, - 0x6B, 0x4F, 0x4B, 0x1E, 0x78, 0xD6, 0xD7, 0x76, 0xE7, 0x55, 0x76, 0xAA, 0xC7, 0x0B, 0x66, 0x06, 0x3C, 0xED, 0xC4, 0xFD, 0xDE, 0x4A, 0x63, 0x2F, - 0xB0, 0xD0, 0xBF, 0x4F, 0x09, 0xBD, 0x65, 0x1B, 0xEF, 0x7D, 0x47, 0xDF, 0xF2, 0x53, 0xF1, 0x3E, 0x18, 0xDE, 0xFB, 0x65, 0x47, 0x07, 0x9A, 0x79, - 0x0D, 0xB6, 0xBB, 0x24, 0x49, 0x8A, 0xE8, 0x9C, 0x05, 0x19, 0x96, 0xFE, 0x88, 0x61, 0x13, 0x9F, 0xE9, 0x6C, 0x1E, 0xF2, 0xD9, 0x4C, 0x8F, 0xB9, - 0xAD, 0xB0, 0xDC, 0x65, 0xFC, 0x78, 0x68, 0x7C, 0x12, 0x8B, 0x17, 0x00, 0x8F, 0xDB, 0xC6, 0x43, 0x06, 0x5E, 0x10, 0x22, 0xE0, 0x6A, 0x8B, 0xE8, - 0xA2, 0xC4, 0xF7, 0xCE, 0x16, 0x45, 0x44, 0x64, 0x45, 0x4D, 0x04, 0xD6, 0xB4, 0xF9, 0xEB, 0x90, 0xA1, 0x39, 0x60, 0x37, 0xBC, 0x62, 0x12, 0xEA, - 0xD5, 0x12, 0x5B, 0x61, 0xF7, 0xE6, 0x83, 0x18, 0xAA, 0x2D, 0x0F, 0x02, 0x57, 0x2F, 0xE6, 0xE4, 0xE4, 0x11, 0xFC, 0x8A, 0x78, 0xAE, 0x47, 0xE6, - 0xFA, 0xCF, 0xF1, 0x74, 0x59, 0xBA, 0x9B, 0x1E, 0xE0, 0xBF, 0x96, 0x8D, 0x54, 0xEB, 0xC7, 0x59, 0xED, 0x3C, 0x9E, 0x0F, 0x2D, 0xFB, 0xDF, 0x65, - 0x85, 0xF0, 0xB8, 0xF5, 0x5F, 0xD5, 0xFB, 0xA0, 0x5C, 0x3E, 0xEF, 0x9C, 0x90, 0x49, 0x40, 0xFD, 0xD0, 0x8C, 0x9D, 0x47, 0x40, 0x51, 0x0F, 0x87, - 0xE8, 0x85, 0x4F, 0x13, 0xE5, 0xD2, 0xEC, 0xC6, 0xCF, 0xD1, 0x64, 0x18, 0xC2, 0xFB, 0xD5, 0x31, 0x1C, 0xA8, 0x7A, 0xCB, 0x35, 0xF5, 0xDC, 0x81, - 0x82, 0x67, 0xD9, 0xF4, 0x70, 0xB3, 0xA1, 0xD0, 0x37, 0xDD, 0x1F, 0xE2, 0xF3, 0xCE, 0xB9, 0x49, 0xC3, 0xBB, 0x9B, 0x39, 0x3B, 0x7E, 0x0E, 0xA9, - 0xB7, 0x7D, 0x27, 0x0F, 0xD6, 0x6B, 0x87, 0x7B, 0x5D, 0xCD, 0xFC, 0x56, 0xE8, 0x65, 0x29, 0x78, 0xA4, 0xFF, 0xA1, 0xA9, 0xE8, 0x51, 0x8A, 0x64, - 0x5E, 0xC7, 0xEF, 0x23, 0x9C, 0x9A, 0x15, 0x16, 0xD4, 0xE8, 0x27, 0xF1, 0x3F, 0x1F, 0x1A, 0xB9, 0x98, 0xEC, 0xF3, 0x2C, 0x1B, 0xC6, 0x7E, 0x8A, - 0x42, 0xEE, 0xA3, 0xCC, 0x00, 0xB2, 0x3F, 0xA6, 0x41, 0xB0, 0x28, 0xF5, 0xAE, 0x6A, 0x1C, 0x46, 0xAD, 0xE8, 0x7F, 0x33, 0xDB, 0xAA, 0x31, 0xBD, - 0x1A, 0x84, 0xAF, 0xE4, 0x10, 0xC6, 0x86, 0x30, 0xF6, 0x9D, 0x7B, 0xA0, 0x3A, 0x81, 0xFD, 0xC1, 0x0C, 0xCB, 0x9B, 0xA6, 0x5E, 0xCE, 0x78, 0x98, - 0xA6, 0x21, 0xCE, 0xF2, 0xBF, 0x92, 0xCA, 0x26, 0x8E, 0xEE, 0xA8, 0x2E, 0xED, 0x10, 0xD1, 0x05, 0xDD, 0xB4, 0xCF, 0x74, 0xFD, 0xB8, 0x60, 0xE1, - 0x80, 0x35, 0x12, 0xC2, 0xA7, 0x55, 0x24, 0xD0, 0x58, 0x52, 0xDB, 0x76, 0xF2, 0x5A, 0x2A, 0x75, 0x7E, 0x28, 0xC1, 0x76, 0x5F, 0xEA, 0x4C, 0x15, - 0xE6, 0xC6, 0x68, 0xBF, 0x6C, 0xE9, 0x7C, 0x95, 0x38, 0xA6, 0xF2, 0x3B, 0x3E, 0x9F, 0xED, 0x66, 0x29, 0x91, 0x95, 0x24, 0x3B, 0x03, 0xF6, 0x11, - 0x2A, 0x4C, 0x65, 0xFA, 0x95, 0xB1, 0x1C, 0xBB, 0x3A, 0x3D, 0x46, 0xC9, 0x32, 0x46, 0xCF, 0x8C, 0x71, 0xAC, 0x8D, 0x96, 0xF4, 0x6B, 0x4E, 0x07, - 0xA8, 0x5E, 0xFB, 0xCB, 0xFF, 0x1E, 0xB2, 0x67, 0x01, 0xB9, 0xD4, 0xE8, 0x56, 0x81, 0x54, 0x30, 0x3A, 0x8E, 0x30, 0x95, 0x1A, 0x9D, 0x2C, 0x80, - 0x0A, 0x62, 0xDA, 0xBE, 0x72, 0xCF, 0x6F, 0xFC, 0xDA, 0xB2, 0xAC, 0xC0, 0xD8, 0xDD, 0x05, 0xE6, 0x39, 0x68, 0x78, 0x27, 0x31, 0xC5, 0x23, 0x85, - 0x68, 0xDF, 0x59, 0x82, 0x16, 0x48, 0x1F, 0xF6, 0x2D, 0x9B, 0xDB, 0x27, 0xAC, 0xB3, 0x0D, 0x8E, 0x00, 0x8E, 0x14, 0x1C, 0x1E, 0xCE, 0x37, 0x72, - 0x68, 0xC6, 0x52, 0x9C, 0x1C, 0xC1, 0x3D, 0x23, 0xF8, 0xB6, 0x5B, 0x8C, 0x1F, 0x7E, 0x6C, 0x5C, 0x72, 0x6E, 0xF5, 0xDD, 0x34, 0x07, 0xBF, 0x05, - 0x2B, 0x1E, 0x71, 0x29, 0x69, 0xF2, 0x3E, 0x14, 0x52, 0xBB, 0x2F, 0x1A, 0x3B, 0xEF, 0x80, 0x03, 0x76, 0x13, 0x9A, 0x9B, 0xB2, 0xB2, 0x44, 0xE1, - 0xFD, 0xAA, 0xD2, 0xDA, 0x02, 0xC1, 0xFB, 0xB1, 0x67, 0xBC, 0xE2, 0x4A, 0x4A, 0xD4, 0x1E, 0xFE, 0x15, 0x1A, 0xCC, 0x53, 0xC3, 0xAD, 0x86, 0xF5, - 0x91, 0x64, 0x26, 0xC3, 0x6D, 0xE7, 0xF5, 0xE9, 0xC3, 0xEB, 0x15, 0xB6, 0x31, 0x08, 0x5D, 0xB2, 0xB7, 0x20, 0xB0, 0x8B, 0xC3, 0xE8, 0x87, 0xD8, - 0x14, 0x8B, 0x70, 0xF7, 0x6A, 0x85, 0x78, 0x4E, 0x0C, 0x5E, 0xB5, 0x30, 0xC7, 0x24, 0xA1, 0x8B, 0xBE, 0xAC, 0x48, 0xF3, 0xF3, 0x4F, 0x9D, 0x80, - 0x96, 0x9D, 0x91, 0xCE, 0xAE, 0x66, 0x6A, 0xD6, 0x65, 0xF3, 0x32, 0x38, 0xC0, 0xE1, 0x71, 0x5C, 0x29, 0x82, 0x81, 0xCB, 0x59, 0x10, 0x4E, 0x60, - 0x8C, 0x32, 0xC7, 0xE5, 0x17, 0xEB, 0xD7, 0xFC, 0x20, 0xEB, 0x8C, 0xF9, 0x97, 0x6C, 0x66, 0x13, 0x80, 0x43, 0xAD, 0xD1, 0xC1, 0x81, 0xAD, 0x4D, - 0x78, 0x5C, 0x02, 0x7E, 0xC2, 0x13, 0xA6, 0xEE, 0x3F, 0x80, 0xFA, 0x23, 0x08, 0x2C, 0xB7, 0xAA, 0xD7, 0x5A, 0x9F, 0x5C, 0xC8, 0xFD, 0xCE, 0xE3, - 0x4C, 0xAD, 0x13, 0x71, 0x41, 0x5B, 0xBB, 0xB1, 0x76, 0xAC, 0xA1, 0xFB, 0x26, 0xBD, 0xDF, 0xE7, 0x49, 0x8F, 0x25, 0x5D, 0xE8, 0xD8, 0xC7, 0x08, - 0x47, 0x02, 0x43, 0xCE, 0x39, 0x50, 0x19, 0x7C, 0xDC, 0xDF, 0x4E, 0x18, 0x26, 0xE2, 0x2B, 0x6A, 0x83, 0x4E, 0x58, 0x67, 0xFB, 0xB2, 0xBD, 0xBC, - 0xE5, 0x11, 0x82, 0x85, 0x00, 0x43, 0xC5, 0xE4, 0x77, 0x71, 0xB0, 0xF9, 0x38, 0x06, 0x7E, 0xDA, 0x1D, 0x38, 0x63, 0x78, 0xC3, 0x64, 0x2F, 0xE9, - 0x5F, 0xE5, 0x19, 0x3E, 0x8C, 0x52, 0x2A, 0xCA, 0xEA, 0xF9, 0x4E, 0x93, 0xFE, 0x93, 0x83, 0x9E, 0x62, 0x50, 0x20, 0x33, 0x5F, 0xFC, 0x88, 0x4A, - 0x2C, 0xDE, 0x23, 0xD6, 0x2C, 0x45, 0xEC, 0x32, 0x5B, 0xCC, 0x77, 0xFB, 0xDC, 0x78, 0xB7, 0x22, 0xF2, 0xBB, 0xBA, 0x13, 0xE2, 0xB9, 0x99, 0x63, - 0xDB, 0x4C, 0xE2, 0xB8, 0x71, 0x0E, 0x9C, 0xB1, 0xEF, 0xF3, 0x51, 0xC7, 0xA4, 0x43, 0xD0, 0x5E, 0x2E, 0x68, 0x96, 0x69, 0x4E, 0xA9, 0xF8, 0x35, - 0x8A, 0x89, 0x4C, 0xFF, 0xB8, 0x55, 0xFF, 0x58, 0xD0, 0x19, 0xCE, 0x10, 0x87, 0xBF, 0x27, 0xDC, 0xF5, 0x18, 0x73, 0x4D, 0x8F, 0xE6, 0x84, 0x9A, - 0x79, 0x71, 0xFE, 0xD3, 0x91, 0x9E, 0xE7, 0xE2, 0xB0, 0xB8, 0x20, 0xCC, 0x6D, 0xD3, 0xEA, 0x5A, 0x5E, 0x16, 0x4D, 0x02, 0x7D, 0x81, 0x19, 0x22, - 0x44, 0x7C, 0x91, 0xBD, 0x48, 0xEF, 0x31, 0x04, 0x24, 0x9E, 0x99, 0x39, 0x50, 0xB9, 0x83, 0x6B, 0xD6, 0xA6, 0x0C, 0x7F, 0x3D, 0x1E, 0xB3, 0xAA, - 0x5F, 0x00, 0xE8, 0x60, 0xA7, 0x45, 0x3E, 0xC4, 0x67, 0x23, 0x17, 0xEB, 0xFB, 0x92, 0xB4, 0xFF, 0x33, 0x9F, 0x74, 0x31, 0xF1, 0xE2, 0xDB, 0x96, - 0x7B, 0x7C, 0x58, 0x23, 0xEC, 0xC9, 0xEF, 0xD9, 0x3A, 0x85, 0x25, 0xEC, 0x3A, 0x9D, 0x11, 0xDF, 0xB2, 0xA6, 0x27, 0x60, 0x8F, 0x4B, 0x3B, 0xEE, - 0xB2, 0xA0, 0x71, 0xDA, 0x99, 0x1D, 0x40, 0xE7, 0xB1, 0xD5, 0x28, 0xAC, 0x1D, 0x02, 0xBB, 0xF5, 0xEF, 0x35, 0x31, 0xB9, 0x55, 0xF2, 0x95, 0x59, - 0x12, 0x4D, 0xB5, 0xD7, 0x1F, 0x13, 0x40, 0x99, 0x1D, 0x6C, 0x49, 0xE4, 0x8B, 0xCB, 0x55, 0x15, 0xCF, 0x59, 0x1D, 0x9E, 0x72, 0x1B, 0xA4, 0xD3, - 0x8A, 0x4D, 0xA5, 0xB4, 0x00, 0xD9, 0x11, 0x69, 0x49, 0x25, 0x6E, 0x02, 0x01, 0x51, 0x22, 0xFE, 0xD7, 0x72, 0x03, 0x78, 0xB6, 0x1D, 0x3B, 0x17, - 0x71, 0xF0, 0x02, 0x88, 0xF2, 0xBF, 0x39, 0xA9, 0x19, 0x65, 0x8D, 0x78, 0x73, 0xEA, 0xFA, 0xE3, 0x70, 0x51, 0x16, 0x7A, 0x71, 0xA2, 0x29, 0x7A, - 0x29, 0x34, 0x07, 0x95, 0xEF, 0xFB, 0xE8, 0xE4, 0x7C, 0x3A, 0x44, 0x47, 0x29, 0xAD, 0x77, 0xD3, 0x0C, 0xD1, 0x00, 0x5E, 0xC9, 0x23, 0xC8, 0x45, - 0x6E, 0x8E, 0x8B, 0x34, 0xEE, 0x5C, 0x74, 0x48, 0xFA, 0xA0, 0xEE, 0x0B, 0x0F, 0x86, 0x86, 0x64, 0x16, 0xCF, 0x88, 0xB2, 0xEB, 0x6B, 0x7A, 0xD0, - 0xE2, 0x6A, 0x06, 0xEB, 0x9A, 0xFE, 0xE5, 0x8A, 0xF1, 0xCF, 0xCB, 0x79, 0x8B, 0x42, 0xF0, 0xCB, 0x4B, 0xF0, 0x0C, 0x8C, 0x52, 0xD0, 0xD3, 0x1F, - 0xBC, 0xDF, 0xF5, 0xBC, 0x7B, 0x27, 0x4F, 0x7E, 0x93, 0x4B, 0xDB, 0x99, 0x3B, 0x17, 0xA6, 0xD6, 0x09, 0xF2, 0xC6, 0xE1, 0xF6, 0x0D, 0xF0, 0xC9, - 0x74, 0x43, 0x4C, 0xD0, 0x2F, 0x4D, 0x5C, 0x2E, 0x88, 0xDF, 0x08, 0x1B, 0xA7, 0xD6, 0xBC, 0x51, 0x23, 0xF3, 0x8A, 0x03, 0xFB, 0x35, 0xB6, 0x23, - 0xE0, 0xCC, 0x36, 0x23, 0x56, 0xC0, 0x6F, 0x6E, 0xC8, 0x3D, 0x49, 0xEE, 0x48, 0x19, 0x79, 0x45, 0xD8, 0x32, 0x44, 0xDB, 0x77, 0x62, 0xE7, 0x93, - 0x2D, 0x31, 0x41, 0x8F, 0xB6, 0x75, 0xBD, 0x90, 0x77, 0x53, 0x78, 0xE4, 0xAA, 0xF2, 0x2E, 0xA9, 0xC6, 0x3A, 0x40, 0xD7, 0x75, 0xD0, 0x5E, 0x54, - 0x14, 0x3F, 0x49, 0x35, 0x6E, 0xAD, 0xE8, 0x99, 0x66, 0xE6, 0x04, 0xB8, 0xCD, 0x74, 0x42, 0xD6, 0xF1, 0xC4, 0xBF, 0x24, 0x36, 0x2F, 0xC0, 0x6D, - 0xA6, 0x13, 0x72, 0xC0, 0xED, 0x7D, 0x29, 0x39, 0x45, 0xFC, 0x44, 0x9C, 0x58, 0x27, 0x85, 0x7C, 0x8B, 0x99, 0x17, 0xC0, 0xE8, 0xBC, 0xFD, 0x15, - 0x39, 0x66, 0x78, 0xA9, 0x02, 0x94, 0x4F, 0x00, 0x0A, 0x60, 0x98, 0x11, 0x44, 0x10, 0x53, 0x9B, 0x0E, 0x00, 0x00, 0xB0, 0x82, 0x62, 0x7E, 0x00, - 0x14, 0x64, 0x01, 0x8F, 0x02, 0x0A, 0x00, 0x00, 0x0A, 0x00, 0x40, 0x16, 0x3F, 0x49, 0x52, 0xF2, 0x93, 0x94, 0xB6, 0xEC, 0x96, 0xE4, 0x46, 0x69, - 0xEF, 0x93, 0x9A, 0x51, 0x54, 0xFB, 0xD8, 0x64, 0xB6, 0x91, 0xC8, 0xEC, 0xBF, 0x3D, 0xBE, 0x75, 0x70, 0x77, 0x22, 0xA2, 0xAA, 0xE8, 0xCC, 0x0E, - 0x33, 0x33, 0x27, 0xF8, 0xF4, 0x26, 0x80, 0x93, 0x32, 0x2F, 0x01, 0xF3, 0x00, 0xFE, 0x00, 0x1B, 0x0E, 0xF2, 0xBE, 0x94, 0x96, 0xFA, 0x8E, 0x9E, - 0x4E, 0x63, 0x43, 0xA5, 0xC6, 0x4B, 0x50, 0x99, 0xD3, 0x75, 0x06, 0x45, 0xB9, 0xBD, 0x9F, 0xBC, 0x08, 0xE9, 0xA8, 0xEA, 0x22, 0x7E, 0x33, 0xD2, - 0x9E, 0xC5, 0xF7, 0xC6, 0xEF, 0x67, 0x35, 0x54, 0xE6, 0x4B, 0x4C, 0x83, 0xDA, 0xDC, 0x73, 0xCA, 0x8A, 0xCA, 0xA7, 0xE4, 0x8E, 0xAF, 0xD0, 0x93, - 0xB0, 0x7D, 0x73, 0xF2, 0x0F, 0xF5, 0x9A, 0x7F, 0x6F, 0x39, 0xD9, 0xE9, 0xA4, 0xAB, 0x57, 0x91, 0xF3, 0x7D, 0x42, 0xB8, 0xEF, 0x35, 0xFB, 0x12, - 0xF7, 0x94, 0x54, 0x4A, 0x34, 0x29, 0x08, 0x5D, 0x57, 0x7A, 0x4F, 0x3E, 0x91, 0x06, 0x37, 0x8A, 0xD2, 0x21, 0x88, 0x53, 0x13, 0xED, 0x13, 0xC1, - 0x20, 0xA1, 0x75, 0x13, 0x98, 0x8E, 0x0B, 0x8B, 0x48, 0x84, 0x01, 0xF0, 0xC0, 0x9A, 0xC1, 0x12, 0x62, 0x89, 0xB0, 0x32, 0x58, 0xC5, 0x72, 0x0B, - 0x63, 0x65, 0xB0, 0x7C, 0x11, 0xB0, 0x0A, 0x60, 0x00, 0x30, 0x03, 0xD8, 0xC3, 0x92, 0xBD, 0xC0, 0x1A, 0x73, 0x81, 0xB5, 0xC0, 0x58, 0x60, 0x2B, - 0x30, 0x15, 0x58, 0x09, 0x96, 0x02, 0xFB, 0x60, 0x1E, 0x4B, 0x07, 0x26, 0x59, 0x38, 0xAC, 0x1D, 0x36, 0xC8, 0x70, 0x30, 0x12, 0x98, 0x09, 0xEB, - 0x90, 0x5C, 0xD8, 0x88, 0x0A, 0xDB, 0xB0, 0x30, 0x0C, 0x0A, 0xBB, 0xA8, 0x60, 0x13, 0x26, 0x61, 0x11, 0x86, 0x02, 0xCB, 0xB0, 0x13, 0x98, 0x09, - 0xCC, 0x82, 0x95, 0xC0, 0x48, 0x60, 0x1C, 0x22, 0x84, 0x50, 0x62, 0xFD, 0xCA, 0x61, 0xF9, 0xAA, 0x61, 0x85, 0x60, 0x23, 0xB0, 0x10, 0x3E, 0xB4, - 0x05, 0x83, 0xB3, 0x10, 0x04, 0x60, 0x14, 0xCC, 0xE2, 0x41, 0x86, 0x15, 0x00, 0x03, 0x41, 0x06, 0xF6, 0x41, 0x0F, 0x2B, 0x06, 0xE6, 0x81, 0x0D, - 0x34, 0xB0, 0x8A, 0xF5, 0xC3, 0x02, 0xAC, 0x03, 0x10, 0x6C, 0x03, 0xA6, 0x01, 0xCB, 0x80, 0x61, 0xC0, 0xC1, 0x05, 0x14, 0xCC, 0x82, 0x0A, 0x28, - 0x60, 0x13, 0x44, 0xC0, 0x24, 0x00, 0x31, 0xC0, 0xBA, 0x81, 0x3D, 0xC0, 0x20, 0x60, 0x0E, 0x34, 0xC0, 0x18, 0x60, 0x04, 0x80, 0x60, 0x0A, 0x20, - 0xC0, 0x12, 0x60, 0x07, 0x18, 0x10, 0x69, 0x82, 0xC2, 0x59, 0x81, 0x64, 0x45, 0x69, 0xD4, 0xCD, 0xF7, 0xD3, 0x28, 0xFF, 0x62, 0xF2, 0xCF, 0x34, - 0xD8, 0x4F, 0xBA, 0x18, 0xF5, 0x05, 0x94, 0xEF, 0x42, 0x5F, 0x56, 0xF9, 0xF1, 0xD4, 0xD2, 0x44, 0x44, 0x90, 0xBD, 0x9F, 0x6E, 0x94, 0xDF, 0xD7, - 0x82, 0x9B, 0x8A, 0x2C, 0x78, 0xA9, 0x8E, 0xDB, 0x1E, 0x5D, 0x39, 0xAC, 0x93, 0xB6, 0x86, 0x21, 0x8B, 0x39, 0x13, 0x67, 0x45, 0x7F, 0xEE, 0x73, - 0x47, 0x7D, 0x90, 0x4D, 0x57, 0x41, 0x11, 0xFA, 0x37, 0xA5, 0xBD, 0x8E, 0x7A, 0x94, 0xD1, 0x6F, 0x4B, 0x6E, 0xA2, 0x15, 0x94, 0xCE, 0x0F, 0xB1, - 0x6F, 0x2A, 0x95, 0xA2, 0xF5, 0x87, 0xCA, 0x09, 0xDC, 0x96, 0x56, 0x46, 0xB8, 0x1E, 0x2D, 0x9B, 0x1B, 0x43, 0xA1, 0x9D, 0xA5, 0xF4, 0xE9, 0xE3, - 0x0B, 0x4A, 0xDA, 0xBA, 0x94, 0x4C, 0x39, 0xF5, 0xB4, 0x91, 0x7B, 0x21, 0x7E, 0xF8, 0x0E, 0x92, 0xA0, 0x1E, 0xFA, 0xCC, 0xFE, 0x46, 0x46, 0xE3, - 0x8C, 0x79, 0xD1, 0x3A, 0x1D, 0xB1, 0x56, 0x34, 0xBD, 0x1D, 0x2D, 0x37, 0xAA, 0x8C, 0xA7, 0x23, 0x8F, 0x92, 0xB3, 0x1D, 0x51, 0x53, 0x9D, 0x90, - 0xC7, 0x9C, 0x40, 0xF5, 0x6D, 0xD6, 0xC8, 0x2A, 0xF1, 0xA6, 0xBF, 0xB4, 0x31, 0xDF, 0x53, 0x29, 0x9D, 0x33, 0xB2, 0x26, 0x6D, 0x17, 0xF1, 0x7E, - 0x2C, 0x7F, 0xA6, 0x37, 0xEB, 0x1F, 0xA7, 0xCD, 0x64, 0x5A, 0x27, 0x86, 0xD3, 0x16, 0x8A, 0xBE, 0xB4, 0xF4, 0x9D, 0x3F, 0x22, 0xC6, 0x76, 0xD0, - 0x54, 0x2D, 0x2B, 0xB5, 0x8F, 0x65, 0x39, 0xED, 0xFC, 0x2E, 0xA5, 0xAE, 0xBC, 0x2B, 0x8C, 0xBF, 0x79, 0x59, 0x99, 0x12, 0xAE, 0x83, 0xF9, 0x7C, - 0xC4, 0xF9, 0x54, 0x6F, 0xA8, 0x3F, 0x0B, 0x5B, 0xCA, 0x92, 0x83, 0xFA, 0xF4, 0x67, 0x04, 0x59, 0x5E, 0x51, 0x49, 0xBD, 0x85, 0x91, 0xA5, 0x95, - 0xC4, 0x91, 0x35, 0xB5, 0xC7, 0x7B, 0xED, 0x99, 0x90, 0xAE, 0x30, 0x7D, 0xF9, 0xC6, 0xA4, 0x77, 0xC7, 0xCB, 0xCB, 0x57, 0xA0, 0x07, 0x71, 0x12, - 0x15, 0x0F, 0x2B, 0x2A, 0x8D, 0x10, 0x4F, 0x21, 0x71, 0xCA, 0x12, 0xBA, 0x59, 0xD6, 0xB8, 0xEE, 0xC3, 0xD7, 0xE3, 0x3D, 0xBA, 0xA3, 0x66, 0x33, - 0xFA, 0xDE, 0xE5, 0x46, 0xD2, 0x8C, 0xB7, 0x68, 0x62, 0x43, 0x62, 0x52, 0xED, 0x32, 0x0B, 0xFD, 0x46, 0x8C, 0xAB, 0x01, 0xB9, 0x8A, 0x98, 0x9D, - 0xA0, 0xF6, 0xCD, 0xBA, 0xA8, 0xF2, 0x86, 0xDE, 0x77, 0xEB, 0xA5, 0xD4, 0x9F, 0x62, 0x24, 0x06, 0x76, 0xBB, 0x24, 0xFE, 0x6B, 0x28, 0xA9, 0x89, - 0x85, 0x34, 0xEA, 0x63, 0x8C, 0xC6, 0x3B, 0x71, 0xD5, 0x11, 0x61, 0x7D, 0xF7, 0xD4, 0x03, 0x8A, 0xB4, 0xE6, 0xCD, 0x0D, 0xA6, 0x99, 0xE8, 0x46, - 0xF1, 0x49, 0x71, 0x62, 0xB9, 0x9F, 0xF5, 0x25, 0xF1, 0x47, 0x5F, 0x96, 0xA8, 0xCB, 0x6A, 0x46, 0xFC, 0x5B, 0x12, 0xA3, 0x13, 0x97, 0xEF, 0x63, - 0x37, 0x17, 0xD3, 0xF1, 0x66, 0x2D, 0x5D, 0xA2, 0x85, 0x68, 0x5A, 0x62, 0xC2, 0x97, 0xEA, 0xAD, 0xD1, 0x72, 0x2A, 0xF5, 0x08, 0x55, 0x1A, 0xDA, - 0x13, 0x53, 0xA4, 0x2F, 0x66, 0xA7, 0xBD, 0x2B, 0xEB, 0xCF, 0xAB, 0xB7, 0xE2, 0x97, 0xE6, 0x1A, 0xF2, 0x54, 0xE2, 0xA4, 0xB6, 0x93, 0x17, 0x8D, - 0x5A, 0x30, 0x9A, 0x42, 0x1B, 0xAB, 0x09, 0x7A, 0x8D, 0x4F, 0xE5, 0x8C, 0x7F, 0x09, 0x32, 0x2D, 0xE7, 0x21, 0x08, 0xF1, 0x26, 0x47, 0x3D, 0xAA, - 0xB8, 0x93, 0xF6, 0x9E, 0x66, 0x5C, 0xA3, 0x5E, 0xA4, 0xAC, 0x9C, 0x68, 0xD6, 0x80, 0x1B, 0x31, 0x46, 0xC3, 0xA6, 0x5C, 0x39, 0x27, 0x02, 0xCB, - 0x51, 0xF5, 0x20, 0xAB, 0x3E, 0x87, 0x5E, 0x3E, 0x30, 0x96, 0xA7, 0x95, 0x90, 0xA6, 0xB4, 0xC5, 0xA0, 0x6C, 0x3C, 0x4D, 0x79, 0xD0, 0x5B, 0xF0, - 0x86, 0xAC, 0xFB, 0xA7, 0xFB, 0x84, 0xD2, 0x1A, 0xAE, 0x4B, 0xF2, 0x69, 0x94, 0x99, 0x44, 0xF8, 0x8F, 0x93, 0x29, 0x2D, 0x66, 0xEB, 0xC5, 0xA4, - 0x99, 0x2A, 0xED, 0x88, 0xB2, 0x52, 0xC9, 0x68, 0xD7, 0x14, 0xA1, 0x91, 0xEB, 0xA6, 0xF2, 0x49, 0x46, 0x2F, 0xE9, 0x44, 0x38, 0xEA, 0xAA, 0x0C, - 0x39, 0x6F, 0xA9, 0xAE, 0x87, 0x5A, 0x28, 0x22, 0x5C, 0x10, 0xEA, 0xBD, 0x23, 0x67, 0xBE, 0xCB, 0x6B, 0x48, 0x74, 0x35, 0xB5, 0xDB, 0x38, 0xCE, - 0x72, 0x22, 0x4D, 0xE1, 0xB3, 0xD9, 0xCC, 0x54, 0xAF, 0x27, 0xE5, 0x48, 0x7D, 0x6D, 0x12, 0xB3, 0xE9, 0x1C, 0xA6, 0x75, 0xED, 0x4B, 0x8C, 0x8B, - 0xB2, 0x5A, 0xEE, 0xEE, 0xC8, 0x57, 0x75, 0x2A, 0x8E, 0x6A, 0x25, 0x47, 0x56, 0x97, 0x29, 0xB9, 0x2F, 0x7A, 0x69, 0xEE, 0xF2, 0xD2, 0xA9, 0x11, - 0xFB, 0x28, 0x77, 0x7E, 0xB2, 0x40, 0xF0, 0xF3, 0x2D, 0xFB, 0x14, 0xA5, 0xF4, 0x9D, 0x9B, 0x21, 0x0E, 0xA2, 0xD6, 0x04, 0x3D, 0xAB, 0x66, 0xF8, - 0x3A, 0xA5, 0xCA, 0x7D, 0xE4, 0xA2, 0x43, 0xE9, 0xE9, 0x20, 0x2A, 0x35, 0xE0, 0xBB, 0x6E, 0xB2, 0xCA, 0x88, 0x70, 0x4D, 0xD0, 0x31, 0x22, 0x8E, - 0x46, 0x93, 0xD8, 0xF1, 0x56, 0x5A, 0xEB, 0xF6, 0x47, 0x79, 0x9F, 0x4E, 0x5E, 0xC0, 0x97, 0x44, 0x70, 0x3E, 0xBF, 0x6F, 0x1D, 0x7C, 0x08, 0xCA, - 0x0A, 0xAA, 0x5F, 0x3F, 0x88, 0xC4, 0x9C, 0xF0, 0xE5, 0xB3, 0xBD, 0x6E, 0x65, 0x8C, 0x0F, 0xBD, 0x9C, 0xAC, 0xEE, 0x8A, 0xCA, 0x2E, 0xF1, 0xE4, - 0x2E, 0x72, 0x52, 0x4A, 0xBC, 0x8A, 0x2B, 0x72, 0x53, 0x0C, 0x82, 0xC3, 0xA8, 0x62, 0xD2, 0xA2, 0x6B, 0x9D, 0x8C, 0x26, 0x85, 0xC2, 0xB4, 0x19, - 0x92, 0x38, 0x08, 0x47, 0x0A, 0x39, 0x10, 0xE5, 0x1C, 0x2F, 0x07, 0x82, 0x5A, 0x1E, 0x64, 0x18, 0x0A, 0x1A, 0x54, 0x23, 0x49, 0x92, 0x0E, 0xD4, - 0x06, 0xA4, 0xC4, 0xA6, 0x36, 0x50, 0xEB, 0xF3, 0x6C, 0xAD, 0x8E, 0xF7, 0xF9, 0xAA, 0x84, 0x2A, 0x98, 0x17, 0xAB, 0x56, 0x0A, 0x6B, 0xE2, 0xE0, - 0xC4, 0x1E, 0xB4, 0xAB, 0xDA, 0xF6, 0x49, 0x34, 0xC1, 0xA8, 0xAA, 0xB1, 0x90, 0x8C, 0x88, 0xE6, 0xD8, 0x32, 0xE4, 0x15, 0x32, 0x79, 0x2F, 0xC3, - 0xAE, 0xEE, 0xC4, 0x0D, 0x99, 0x3C, 0xAF, 0x53, 0xB8, 0xB0, 0x9C, 0x43, 0x23, 0x8C, 0x9D, 0x8F, 0xDC, 0xED, 0xF1, 0x93, 0x45, 0x44, 0x44, 0xA7, - 0xED, 0xCB, 0x9C, 0x5A, 0x57, 0x82, 0x61, 0x42, 0x01, 0x26, 0xE5, 0x81, 0x0C, 0x22, 0x89, 0x11, 0x53, 0xFC, 0xB4, 0x70, 0x55, 0xB2, 0x7F, 0x42, - 0x06, 0xDD, 0x0F, 0x70, 0x68, 0x93, 0x23, 0xB1, 0x9E, 0xF5, 0x6A, 0xEB, 0xD1, 0x68, 0x2C, 0xAD, 0x49, 0x44, 0x1E, 0xD4, 0xD4, 0xB8, 0xEF, 0xBA, - 0xDB, 0xA1, 0x88, 0x4C, 0x0B, 0xD1, 0xE7, 0x8E, 0x6D, 0xBA, 0xCA, 0x58, 0x6F, 0x68, 0x45, 0x4D, 0xF6, 0xC4, 0xA5, 0x9E, 0x70, 0x8C, 0x83, 0x9F, - 0x60, 0x83, 0xD0, 0xB2, 0x92, 0x09, 0x2A, 0x49, 0x14, 0x32, 0x34, 0x36, 0x46, 0xC2, 0x57, 0x9B, 0x80, 0x92, 0x1D, 0x41, 0x8E, 0x01, 0x31, 0xF6, - 0x36, 0xEB, 0x15, 0xF1, 0xBE, 0x96, 0xB6, 0xBF, 0xE7, 0xAF, 0xEA, 0xCB, 0x20, 0xF9, 0x07, 0xD7, 0x40, 0x42, 0x38, 0xB2, 0x26, 0x6F, 0xF5, 0x9E, - 0xE1, 0x3A, 0x77, 0x9A, 0xCE, 0x86, 0x7B, 0xFC, 0x36, 0x6B, 0x4F, 0xBC, 0x4B, 0xA9, 0x41, 0x25, 0x98, 0x04, 0x31, 0xD7, 0xA9, 0x7B, 0xF5, 0x3E, - 0xD8, 0xD1, 0xDE, 0x41, 0xA7, 0xE5, 0x80, 0x6E, 0x1E, 0x5C, 0xC1, 0x04, 0xBB, 0xD6, 0x83, 0xB7, 0x09, 0x1B, 0x96, 0xFF, 0xA7, 0xA1, 0xA7, 0x9E, - 0x54, 0xE4, 0x2E, 0xEB, 0x35, 0xE8, 0x52, 0xFF, 0xC5, 0xD6, 0x38, 0x07, 0x46, 0x49, 0x4F, 0x5D, 0x43, 0x88, 0x29, 0x41, 0xEC, 0x31, 0x35, 0x02, - 0x0D, 0x37, 0x61, 0x56, 0xF8, 0x4F, 0xF0, 0x45, 0xB6, 0xDC, 0xC8, 0xD2, 0x40, 0xAD, 0x41, 0x35, 0xBC, 0xD7, 0x83, 0x27, 0x79, 0x44, 0x2B, 0x80, - 0xF5, 0xD5, 0x31, 0x84, 0xED, 0x36, 0x18, 0x07, 0x6E, 0x4E, 0x3F, 0xD9, 0xBA, 0x6D, 0xCC, 0x7F, 0x07, 0x60, 0xD4, 0xA4, 0xA0, 0x28, 0x12, 0x55, - 0x9F, 0x24, 0x0D, 0x41, 0x2C, 0x97, 0x1E, 0xDC, 0x23, 0x81, 0x98, 0xD7, 0xCF, 0x33, 0xE2, 0x9E, 0x51, 0x42, 0xA2, 0xE8, 0x82, 0x01, 0x10, 0x93, - 0x57, 0x02, 0x07, 0x06, 0x83, 0xAE, 0x3C, 0x49, 0xC7, 0x2D, 0x9E, 0x26, 0x26, 0x05, 0x55, 0x48, 0x7E, 0x21, 0x9C, 0xEC, 0x91, 0x41, 0xDE, 0xEF, - 0x22, 0x53, 0xC6, 0x15, 0x39, 0xA2, 0xE0, 0x78, 0x18, 0x3A, 0x4E, 0x3E, 0x35, 0x23, 0x8C, 0x22, 0x5B, 0x1A, 0x42, 0x84, 0x19, 0x99, 0xCC, 0xD3, - 0x3E, 0x86, 0x53, 0xDD, 0x8B, 0x02, 0xD1, 0x1F, 0x5B, 0x05, 0x0C, 0xB0, 0x37, 0xC1, 0x6E, 0xB5, 0xD5, 0x13, 0xC9, 0xE7, 0x7D, 0x74, 0x97, 0x2C, - 0x73, 0xF6, 0x2E, 0xB0, 0x41, 0x34, 0x87, 0x06, 0x9B, 0xEF, 0x88, 0xE4, 0xDC, 0x73, 0xB6, 0x4C, 0x41, 0x5D, 0x4C, 0x31, 0xF3, 0x39, 0x89, 0x4F, - 0xB8, 0xA5, 0xB1, 0x54, 0xC4, 0xE7, 0xD4, 0x4E, 0x14, 0x8A, 0x78, 0x99, 0xD1, 0xBB, 0x18, 0xBA, 0x78, 0x74, 0xBC, 0x81, 0x86, 0x14, 0xA8, 0x61, - 0x79, 0xC5, 0x4A, 0x8D, 0xE4, 0x37, 0xBC, 0x18, 0x44, 0xEB, 0x96, 0x40, 0xEC, 0xDF, 0x72, 0x89, 0xC5, 0xE6, 0xC3, 0x4F, 0xE1, 0x1E, 0x46, 0x58, - 0xB7, 0xE8, 0x2C, 0xBA, 0x62, 0xBA, 0x49, 0xF0, 0x07, 0xD1, 0x18, 0x6A, 0xCB, 0xB8, 0x6D, 0x7C, 0xA2, 0x4D, 0x4C, 0x0D, 0x77, 0x5C, 0x26, 0x00, - 0x1F, 0x36, 0xB1, 0x5B, 0x28, 0x9A, 0x34, 0x43, 0xDB, 0xF0, 0x4B, 0x91, 0x32, 0x2E, 0x0E, 0xEF, 0xB8, 0x4E, 0x47, 0xBE, 0xAB, 0x4E, 0xAB, 0x85, - 0x0B, 0x7C, 0x37, 0xAC, 0xCF, 0x02, 0xF9, 0x09, 0x25, 0x57, 0xE9, 0x6E, 0x97, 0xE5, 0x5C, 0x2C, 0x34, 0x52, 0x46, 0x99, 0x6B, 0xB6, 0x76, 0x00, - 0xDC, 0xCB, 0x41, 0x70, 0x7E, 0xB3, 0x94, 0x15, 0xF2, 0xDE, 0x6B, 0x6D, 0xCB, 0x7C, 0xF0, 0x9E, 0xB9, 0xEA, 0x2F, 0x2A, 0x9F, 0x8F, 0x22, 0x73, - 0x51, 0xD3, 0x8D, 0xA2, 0x84, 0xDF, 0x34, 0x97, 0x03, 0x29, 0xD8, 0x17, 0x5C, 0xD3, 0x90, 0x1D, 0x42, 0x0E, 0x36, 0x7E, 0xD6, 0xA0, 0x42, 0x6B, - 0x7F, 0x1B, 0x4D, 0x3F, 0xF3, 0xC9, 0xE6, 0x14, 0x50, 0xD0, 0x6C, 0x06, 0x90, 0x90, 0xE8, 0x09, 0xB2, 0x28, 0xB1, 0xC5, 0x89, 0x56, 0xE9, 0x1C, - 0x97, 0x3A, 0x21, 0x6C, 0x4B, 0xDA, 0xB5, 0xFF, 0x9E, 0x90, 0x65, 0x89, 0x75, 0xB2, 0xB3, 0xD2, 0xC9, 0x24, 0xC6, 0x63, 0x44, 0xA1, 0xEF, 0x9D, - 0x34, 0xD6, 0x9F, 0xC1, 0x34, 0x72, 0xB0, 0x8F, 0xB2, 0x4A, 0xD5, 0x8C, 0x9C, 0x8A, 0x91, 0x10, 0x15, 0x84, 0xA0, 0x18, 0x38, 0xBA, 0x0E, 0x2D, - 0x5B, 0x23, 0x50, 0x54, 0x0B, 0xCD, 0x12, 0x91, 0x41, 0xAA, 0xB8, 0x00, 0x7D, 0x56, 0x10, 0x0C, 0x4E, 0x7F, 0x06, 0x32, 0xE8, 0xFC, 0x3F, 0xE7, - 0xAB, 0xF8, 0x58, 0x58, 0x14, 0x82, 0xB3, 0xEB, 0x99, 0x8C, 0x19, 0x50, 0xAA, 0xF0, 0x5B, 0x43, 0x40, 0x9F, 0xF6, 0xC1, 0x6D, 0x8B, 0x22, 0xA3, - 0xC1, 0xDD, 0x19, 0x7A, 0xFF, 0xDF, 0x42, 0x97, 0x7D, 0xE9, 0x79, 0x85, 0xB0, 0xB2, 0xAD, 0x73, 0x3A, 0x2B, 0x0A, 0x77, 0x48, 0xC4, 0x34, 0x7E, - 0x2B, 0xE0, 0xD5, 0x7B, 0x4C, 0xDC, 0x54, 0x0D, 0xFF, 0xD2, 0xF5, 0x0C, 0xD2, 0x16, 0xCB, 0xB6, 0xFC, 0x7B, 0x2E, 0x3E, 0xFB, 0x05, 0xC4, 0xBA, - 0xBC, 0x00, 0x0C, 0x50, 0x97, 0x45, 0x3E, 0x1D, 0x89, 0xFC, 0x0D, 0x95, 0x23, 0x2A, 0xD9, 0x2C, 0x52, 0x59, 0x9C, 0xF8, 0x88, 0x41, 0x5A, 0x69, - 0xBD, 0x1C, 0x1D, 0x77, 0xB7, 0xCC, 0xC8, 0x17, 0x24, 0x06, 0xF4, 0x86, 0x00, 0x93, 0x90, 0x8B, 0xC3, 0x27, 0xC9, 0xCC, 0xF8, 0x82, 0x6F, 0xD5, - 0x8E, 0x19, 0x44, 0x33, 0x2B, 0xE6, 0x02, 0x05, 0xDD, 0x1D, 0xB4, 0xC9, 0x44, 0x0F, 0x5F, 0x02, 0x01, 0x11, 0xEB, 0x06, 0x16, 0x56, 0x22, 0xBD, - 0x3E, 0x2B, 0xF4, 0x01, 0x31, 0x96, 0xAF, 0x3C, 0xFF, 0x07, 0xFF, 0x00, 0x95, 0x28, 0x0B, 0xFD, 0x4F, 0x22, 0x02, 0x48, 0xE8, 0x91, 0x27, 0x70, - 0x99, 0x2E, 0x54, 0x17, 0x6D, 0x7B, 0xE1, 0x2C, 0xD2, 0x44, 0x48, 0xBF, 0x66, 0x55, 0x24, 0x4F, 0xC2, 0x04, 0xD3, 0xA9, 0x34, 0xEE, 0x1C, 0x2E, - 0xFB, 0x1D, 0xEE, 0x21, 0xCC, 0x7D, 0xF5, 0xA6, 0xC8, 0xC8, 0x1A, 0xD1, 0x76, 0x01, 0xAD, 0xC0, 0xA5, 0x63, 0x23, 0x1B, 0x15, 0xFE, 0x5F, 0x95, - 0xDF, 0x42, 0x6E, 0x54, 0xAA, 0x2D, 0x96, 0x4C, 0xF3, 0x82, 0x51, 0xB2, 0x94, 0x16, 0x0B, 0xD5, 0x25, 0x62, 0x13, 0x45, 0x35, 0xC9, 0x18, 0x86, - 0xA6, 0x28, 0xA6, 0x91, 0x24, 0x15, 0x58, 0x38, 0x9A, 0x32, 0x07, 0x36, 0xB7, 0x9F, 0xDB, 0x14, 0x9D, 0x37, 0x1D, 0x52, 0xC8, 0x0E, 0x05, 0x1D, - 0x56, 0xB8, 0x71, 0x44, 0x37, 0x8A, 0xF8, 0x8A, 0x13, 0x00, 0x2D, 0x3B, 0xC6, 0x45, 0x1F, 0x57, 0xEF, 0xEE, 0x14, 0x65, 0x84, 0x50, 0x47, 0xE1, - 0x97, 0x21, 0x99, 0x9D, 0xB9, 0xA3, 0x0F, 0xD9, 0x00, 0x1C, 0x40, 0x2C, 0xA6, 0x36, 0x8E, 0x12, 0x73, 0x38, 0x9A, 0xD5, 0x4C, 0x23, 0x86, 0x90, - 0x45, 0x7F, 0xE9, 0x9B, 0xEC, 0xC2, 0xB2, 0x51, 0x0D, 0x31, 0x99, 0x63, 0x88, 0xC9, 0x81, 0x74, 0x67, 0xF4, 0x4B, 0xA1, 0xB4, 0x1F, 0xC7, 0x56, - 0xD4, 0xDB, 0x85, 0x02, 0x05, 0x89, 0x8B, 0x6F, 0x92, 0xCF, 0xF5, 0x97, 0x94, 0x25, 0x86, 0x3E, 0x0E, 0x28, 0x32, 0xB1, 0xF2, 0x40, 0x5A, 0x5D, - 0xD0, 0x64, 0x49, 0x14, 0xB5, 0x23, 0x45, 0x74, 0x1B, 0x1D, 0xB4, 0xE6, 0x9F, 0xB1, 0x50, 0xBB, 0x14, 0x51, 0x24, 0xF0, 0x22, 0x07, 0xC7, 0x85, - 0x7F, 0x48, 0x47, 0x8D, 0x86, 0x8A, 0xC4, 0x7A, 0x40, 0x24, 0x2D, 0x47, 0x5B, 0xB2, 0xC1, 0xED, 0xD8, 0x5E, 0x77, 0x81, 0x69, 0x34, 0x1D, 0x4F, - 0x42, 0x2D, 0xA2, 0xE8, 0xA3, 0x71, 0x36, 0x9A, 0xFB, 0x06, 0x56, 0x5D, 0x75, 0xD3, 0x10, 0x49, 0xF6, 0x7C, 0x86, 0x1F, 0x9B, 0x7D, 0x0A, 0x88, - 0x77, 0xC2, 0x23, 0x75, 0x69, 0x73, 0xD4, 0x22, 0xE6, 0x9C, 0x64, 0x72, 0x99, 0x8F, 0x66, 0x49, 0x44, 0x48, 0x83, 0xF7, 0x93, 0x18, 0x74, 0x67, - 0xE2, 0xA4, 0x8A, 0x67, 0xD1, 0x0C, 0x95, 0x27, 0x19, 0xEA, 0xAF, 0x92, 0xA5, 0xAB, 0x9A, 0xBD, 0x2B, 0xCB, 0x92, 0x1B, 0x50, 0x05, 0x18, 0xB3, - 0xD1, 0x7C, 0x61, 0x62, 0xF3, 0xC9, 0x91, 0xB4, 0x30, 0x27, 0xEC, 0x4E, 0x4A, 0xA3, 0x5F, 0x91, 0x0F, 0x80, 0x15, 0xC8, 0x95, 0xCC, 0x6C, 0x0B, - 0xDB, 0x2C, 0xE1, 0x06, 0x7C, 0x79, 0x1E, 0xC3, 0x3C, 0xD7, 0xE6, 0x66, 0x92, 0x44, 0x5E, 0x58, 0x3E, 0x47, 0x00, 0x36, 0x01, 0x46, 0x83, 0x37, - 0x46, 0xC0, 0xCD, 0xE7, 0xF4, 0xCE, 0xD8, 0xA0, 0x30, 0x03, 0x77, 0x68, 0x70, 0x00, 0x11, 0x5C, 0xCD, 0xB3, 0x50, 0x54, 0x1F, 0x24, 0x47, 0x12, - 0xCD, 0x79, 0x23, 0x65, 0xA8, 0xA9, 0xA4, 0x46, 0xBE, 0x91, 0x8B, 0x64, 0xC8, 0x2D, 0x18, 0xF6, 0xDF, 0xC5, 0xD9, 0xB3, 0x0F, 0xA4, 0x78, 0xFD, - 0x79, 0xFE, 0x70, 0x1F, 0xDE, 0xF8, 0x62, 0x4E, 0x03, 0x3D, 0xCE, 0x66, 0x01, 0x58, 0xC8, 0x06, 0x17, 0x3E, 0x26, 0xAD, 0x18, 0xFC, 0xCE, 0x4D, - 0x0D, 0x56, 0x89, 0x9C, 0x4B, 0x76, 0xC0, 0x1A, 0xD6, 0x66, 0x67, 0x60, 0x51, 0x83, 0xD9, 0x87, 0x9F, 0xB4, 0xE5, 0xAF, 0xD5, 0x89, 0x9E, 0xC9, - 0x75, 0x89, 0xD1, 0xAE, 0x27, 0xA5, 0x73, 0x17, 0xC4, 0xF4, 0x6D, 0xCE, 0xF3, 0x5C, 0x9D, 0xE6, 0xE6, 0xAC, 0xCD, 0x38, 0x94, 0x77, 0xD7, 0x35, - 0x5C, 0xC7, 0x93, 0xE2, 0x85, 0x05, 0xE0, 0x47, 0xEE, 0xD0, 0x6D, 0xF9, 0x8F, 0x4D, 0x45, 0x96, 0x6D, 0xBA, 0xFA, 0xA0, 0xD8, 0xF4, 0x69, 0x1D, - 0x30, 0x7B, 0xDF, 0x97, 0x74, 0xAC, 0xBF, 0xBE, 0x4F, 0x29, 0x34, 0x23, 0x00, 0xB7, 0x65, 0x6D, 0x7C, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x11, 0x53, - 0x19, 0xE9, 0xA6, 0xFD, 0x2D, 0xD6, 0xBF, 0x57, 0x81, 0xA7, 0x97, 0x90, 0x27, 0x99, 0xCC, 0x84, 0x84, 0x83, 0x36, 0xC4, 0x33, 0x08, 0x11, 0x02, - 0x08, 0x1F, 0x78, 0xD0, 0x01, 0x07, 0x1B, 0x68, 0x90, 0x01, 0x45, 0x88, 0x10, 0x40, 0xF8, 0xC0, 0x83, 0x0E, 0x38, 0xD8, 0x40, 0x83, 0x0C, 0x2A, - 0xC8, 0x54, 0x2E, 0x30, 0x80, 0xA2, 0xC5, 0x52, 0x5F, 0x8B, 0x15, 0x37, 0x95, 0x64, 0x61, 0xE1, 0x81, 0xC5, 0xC4, 0xC2, 0x02, 0x62, 0xF0, 0x46, - 0x7D, 0xEB, 0x7A, 0xFA, 0x12, 0x79, 0x89, 0x4C, 0x6C, 0x57, 0x5C, 0x39, 0x43, 0xD4, 0x41, 0x30, 0x31, 0x14, 0x18, 0x64, 0xA6, 0x62, 0xF2, 0x01, - 0xC6, 0x62, 0x31, 0x59, 0x99, 0xC8, 0x24, 0xC2, 0xB4, 0xD0, 0x0A, 0x0C, 0xDE, 0x76, 0xD2, 0x31, 0x12, 0xC9, 0x4A, 0x6E, 0x08, 0xEA, 0x07, 0xB4, - 0x1E, 0x01, 0xED, 0x3A, 0x24, 0x38, 0xFD, 0x48, 0x40, 0x5F, 0x7E, 0x99, 0x79, 0xED, 0xE2, 0x9A, 0x68, 0xCD, 0xA6, 0xA7, 0xD7, 0x6A, 0x82, 0x5A, - 0x4C, 0xE4, 0xA8, 0x26, 0xBD, 0x79, 0x3D, 0xA9, 0x96, 0xB6, 0xA3, 0xA5, 0xA8, 0xE1, 0xE5, 0xC1, 0x11, 0xB0, 0xA1, 0xE4, 0x9E, 0x20, 0x2F, 0x53, - 0xE9, 0x74, 0x3D, 0x5A, 0x97, 0x28, 0x3D, 0xF1, 0x11, 0xB7, 0x46, 0xC3, 0xBD, 0xB1, 0x55, 0x3B, 0x47, 0x97, 0x57, 0xA3, 0xA2, 0x3B, 0x3D, 0xA5, - 0x54, 0x0A, 0xDF, 0x33, 0x3E, 0x7B, 0x91, 0x8E, 0x3F, 0x15, 0x6E, 0x32, 0xFA, 0xA7, 0x10, 0x7E, 0x72, 0x36, 0xA6, 0xBB, 0x4B, 0x26, 0x9E, 0x4A, - 0x5C, 0x9C, 0x3A, 0xBB, 0x89, 0x60, 0x50, 0x71, 0x09, 0x01, 0x33, 0xE9, 0xB0, 0x5C, 0x56, 0x66, 0xE3, 0xB2, 0x5A, 0x93, 0x9C, 0x8C, 0x50, 0x26, - 0x14, 0x2A, 0x60, 0x2C, 0x16, 0x65, 0xBD, 0xF0, 0xC7, 0xA8, 0x04, 0x83, 0x8A, 0x0A, 0x08, 0x18, 0x8C, 0x4A, 0x65, 0x65, 0xB5, 0x78, 0x35, 0x19, - 0xA2, 0xB1, 0xC9, 0x48, 0x87, 0xE9, 0xAD, 0x1D, 0x31, 0x76, 0x15, 0xD7, 0x1F, 0x46, 0x4F, 0x5D, 0x56, 0x58, 0x9F, 0xAB, 0x86, 0x37, 0xD5, 0x50, - 0xC3, 0x75, 0x37, 0x71, 0x4E, 0x85, 0x30, 0x30, 0x30, 0xD6, 0x44, 0xA1, 0x90, 0x5C, 0xAC, 0x2A, 0x45, 0xF8, 0x6B, 0xE5, 0x3A, 0x9B, 0x47, 0x5D, - 0x4A, 0x15, 0xF2, 0xA3, 0xA5, 0xD7, 0xF0, 0x97, 0x5F, 0x32, 0xB4, 0xB1, 0xF3, 0x51, 0x7A, 0x04, 0x93, 0x4C, 0x45, 0xA4, 0x03, 0x0C, 0x3C, 0xA8, - 0x0A, 0x83, 0x59, 0x1A, 0x93, 0x11, 0x1D, 0xD2, 0xF7, 0x65, 0x6D, 0xB2, 0xC4, 0xBE, 0xA7, 0x53, 0x7E, 0xF8, 0x6F, 0x32, 0x66, 0x48, 0xBA, 0xCB, - 0x4C, 0x53, 0x9B, 0x18, 0xA9, 0x3D, 0x3B, 0x2A, 0x9B, 0xE4, 0xD5, 0x0D, 0x43, 0x71, 0x3F, 0x5F, 0xBB, 0x28, 0xF5, 0x26, 0xBC, 0xF5, 0xF7, 0x2D, - 0x19, 0x12, 0x2D, 0x4C, 0xE8, 0x9A, 0xAC, 0x34, 0xB5, 0x9C, 0x1A, 0xDC, 0xF1, 0xF5, 0xCB, 0x97, 0xFB, 0x50, 0x9B, 0x7C, 0x79, 0x11, 0xDF, 0x44, - 0xF2, 0xAF, 0x66, 0xDB, 0x44, 0xE7, 0x94, 0x22, 0x2A, 0xFF, 0x47, 0x63, 0x9A, 0x98, 0xFE, 0xA3, 0xF4, 0x2D, 0x99, 0xA0, 0xCF, 0x81, 0x62, 0xA8, - 0x51, 0xC1, 0xB4, 0x4C, 0x52, 0x6A, 0x33, 0x71, 0x10, 0x83, 0x4C, 0xA3, 0x4C, 0x99, 0x99, 0x07, 0x51, 0x1D, 0x43, 0x04, 0x59, 0x4B, 0x6D, 0x07, - 0xA4, 0x2C, 0xD4, 0x6F, 0x00, 0x52, 0x49, 0x6B, 0xAB, 0x37, 0xE1, 0x80, 0xA7, 0xB0, 0x46, 0x69, 0xA2, 0xD7, 0x4C, 0xFB, 0x80, 0x23, 0xB4, 0x6F, - 0x74, 0x2C, 0x57, 0xC2, 0x44, 0x29, 0x28, 0x39, 0x73, 0x02, 0xA5, 0x87, 0x6C, 0x2E, 0x3B, 0x19, 0x8E, 0xDB, 0x68, 0xAC, 0xC1, 0x09, 0x00, 0x44, - 0x17, 0x69, 0x2F, 0x74, 0x96, 0x2D, 0x4C, 0x34, 0x8C, 0x13, 0xD1, 0x28, 0x56, 0xD2, 0xB2, 0x86, 0xF3, 0x49, 0x5E, 0x90, 0x81, 0xF0, 0xE3, 0x82, - 0x6D, 0x15, 0x43, 0x5A, 0xC4, 0x5C, 0x5B, 0xE0, 0x01, 0x73, 0x6F, 0x04, 0x20, 0x87, 0x18, 0x8C, 0xA2, 0x84, 0xD0, 0x82, 0x45, 0xB3, 0x41, 0x89, - 0x1E, 0x11, 0xD7, 0x22, 0xEC, 0xB9, 0xE8, 0x1B, 0xBF, 0x4C, 0xB3, 0xE8, 0x83, 0x7D, 0x39, 0x8B, 0xA9, 0x50, 0x7A, 0x97, 0x44, 0xDC, 0xE4, 0x8C, - 0x32, 0xF3, 0x14, 0x0B, 0xE5, 0xF9, 0xBD, 0x2B, 0x55, 0x1E, 0x35, 0x66, 0x69, 0xFD, 0x65, 0xF2, 0xEB, 0x93, 0x0C, 0x74, 0x72, 0x8A, 0x8A, 0x2A, - 0x73, 0xFC, 0xA2, 0x20, 0x03, 0xE0, 0xBE, 0x8D, 0x07, 0x13, 0x02, 0xAE, 0xD3, 0x35, 0x11, 0xAC, 0x2A, 0x3C, 0x09, 0x7D, 0x84, 0xF6, 0x94, 0xDB, - 0x3A, 0x68, 0x88, 0x75, 0x24, 0x0D, 0x21, 0x59, 0x1C, 0x93, 0x29, 0xD5, 0xF9, 0x1D, 0xF8, 0x9C, 0x41, 0x43, 0xDD, 0xF0, 0xA4, 0x7B, 0xF2, 0xFF, - 0x05, 0xE9, 0x6D, 0xFD, 0x54, 0x22, 0xEC, 0x5F, 0xC0, 0x1F, 0x73, 0xDD, 0xD6, 0xBF, 0xA9, 0xBD, 0xD4, 0xE7, 0xC3, 0x72, 0x0D, 0x61, 0xC9, 0x8B, - 0xC2, 0xD2, 0x43, 0x29, 0x68, 0x1D, 0x06, 0x4B, 0xA6, 0x95, 0xB9, 0xA6, 0x12, 0x2B, 0xC2, 0x98, 0xC9, 0xB2, 0xBD, 0xDB, 0x27, 0x92, 0x0B, 0x04, - 0x77, 0x06, 0x41, 0x4A, 0x21, 0x21, 0x8A, 0x11, 0x95, 0xAA, 0x5E, 0xBB, 0x08, 0xC8, 0x20, 0xC9, 0x39, 0xEB, 0xEA, 0xA1, 0x58, 0x0A, 0xCF, 0xCD, - 0x8C, 0xC7, 0xD9, 0xB6, 0x3D, 0x3D, 0x39, 0xBC, 0x58, 0x4C, 0x0D, 0x81, 0xE4, 0x10, 0xCA, 0x99, 0x00, 0x37, 0x15, 0x13, 0x55, 0x74, 0x55, 0xC5, - 0xE4, 0xFC, 0xB0, 0xEE, 0x35, 0xC0, 0x94, 0x5A, 0xE3, 0x85, 0x0B, 0x75, 0x85, 0xD3, 0xB7, 0x21, 0x26, 0x8C, 0xDF, 0x0B, 0x3A, 0x4A, 0x08, 0x07, - 0x96, 0x19, 0x04, 0x82, 0xBB, 0xE9, 0xDC, 0xEB, 0x1D, 0xE5, 0x58, 0x4C, 0xC8, 0x0B, 0xDC, 0xE9, 0xF8, 0xFB, 0x72, 0x5A, 0x0E, 0x58, 0x9D, 0xAC, - 0xD3, 0xE7, 0x3C, 0x72, 0x68, 0x5B, 0xF1, 0xBF, 0xBE, 0xD8, 0x8F, 0x83, 0x30, 0x8F, 0x41, 0x5E, 0x3E, 0x1F, 0x5D, 0xC5, 0xB3, 0x40, 0xB2, 0x37, - 0xFA, 0x23, 0x9A, 0x6C, 0x02, 0x72, 0xD6, 0x69, 0x11, 0x7B, 0x51, 0x6B, 0x87, 0x03, 0xC6, 0x72, 0xB6, 0x83, 0xE1, 0x83, 0x8B, 0x83, 0xC3, 0x6B, - 0x06, 0x12, 0xD0, 0xCE, 0x27, 0x2D, 0xC0, 0xFF, 0x5C, 0x0B, 0x8F, 0x9C, 0x27, 0x05, 0x87, 0x96, 0x0E, 0x56, 0x10, 0x49, 0x4D, 0xEE, 0x93, 0x2E, - 0x2A, 0x6C, 0xB1, 0x6D, 0x06, 0x0A, 0xF3, 0x9D, 0x30, 0xAA, 0x14, 0xB2, 0x89, 0x7F, 0x13, 0xC2, 0xC8, 0x32, 0x48, 0x86, 0xF7, 0x35, 0xC2, 0x0A, - 0x86, 0x60, 0xF9, 0xB4, 0xEC, 0xED, 0x67, 0x54, 0x30, 0xA3, 0x1E, 0x39, 0x26, 0xBF, 0x9D, 0x85, 0x34, 0x48, 0xF6, 0xCE, 0x9C, 0x65, 0x07, 0xA5, - 0x13, 0xD4, 0xB0, 0x53, 0xDF, 0xB2, 0x3D, 0x3C, 0xFC, 0x59, 0x50, 0x4E, 0x2D, 0x91, 0x1B, 0x43, 0xEE, 0x49, 0xC1, 0xA5, 0xE0, 0x86, 0x39, 0x5C, - 0xF0, 0xDE, 0x81, 0xCD, 0xCF, 0x59, 0x36, 0x9C, 0xB6, 0x24, 0xCC, 0x64, 0x03, 0xDA, 0x89, 0x34, 0x58, 0xBC, 0x89, 0x98, 0x10, 0x9C, 0x10, 0x8C, - 0xB1, 0xB9, 0xC8, 0x11, 0xEA, 0x4F, 0x64, 0x99, 0xF4, 0x66, 0x5B, 0x7B, 0x1D, 0x72, 0x81, 0xCC, 0x22, 0x08, 0xDF, 0x11, 0x84, 0xAC, 0x9B, 0x57, - 0x83, 0xFB, 0x97, 0x2E, 0x1F, 0xEB, 0x31, 0xF8, 0xDA, 0x0A, 0x07, 0xA4, 0xB5, 0xC5, 0xEB, 0x6F, 0x03, 0xC6, 0x62, 0x09, 0xC6, 0x62, 0x11, 0x12, - 0xF6, 0x24, 0x89, 0xA5, 0x86, 0xF6, 0x75, 0x68, 0x54, 0x01, 0x26, 0xB8, 0xDD, 0xC7, 0x91, 0x5B, 0xA0, 0x11, 0x31, 0xB1, 0xA8, 0x6E, 0x47, 0x3D, - 0xBD, 0x49, 0x22, 0x9E, 0x4E, 0xE8, 0x03, 0x6E, 0xCF, 0xA2, 0x5A, 0x63, 0x8E, 0x03, 0xE8, 0xD8, 0x90, 0xD9, 0x4C, 0xA7, 0x92, 0x7E, 0xCF, 0x8D, - 0xA9, 0x8B, 0x4B, 0xF9, 0xCD, 0x7E, 0x42, 0x7C, 0xDD, 0x22, 0x89, 0x61, 0x69, 0xF6, 0xA8, 0x11, 0xF0, 0xF0, 0xBA, 0xE0, 0x23, 0x31, 0x60, 0x57, - 0x96, 0xCD, 0x5F, 0x26, 0xBB, 0x55, 0xD9, 0x74, 0x01, 0x1A, 0xC6, 0x50, 0x44, 0xB6, 0xD3, 0xE0, 0xA2, 0x75, 0xB3, 0x9F, 0x2E, 0xD4, 0x97, 0x81, - 0xC6, 0x88, 0x3C, 0x65, 0xA3, 0x4C, 0xAD, 0x97, 0xF1, 0xE8, 0x01, 0x4D, 0x2A, 0x00, 0x76, 0x30, 0x84, 0x24, 0x20, 0x6D, 0xB6, 0x1A, 0x17, 0x95, - 0xE5, 0xD6, 0x53, 0xD3, 0xDF, 0x5F, 0x06, 0x84, 0x87, 0xD1, 0xA4, 0xA2, 0x16, 0xD9, 0xDA, 0xC8, 0xD7, 0xE6, 0xA4, 0x91, 0xAB, 0xB2, 0x30, 0x09, - 0x14, 0x60, 0x00, 0x40, 0x97, 0x02, 0x80, 0x00, 0x75, 0x00, 0x79, 0x00, 0x33, 0x75, 0xEA, 0xCC, 0x69, 0x49, 0xA7, 0x9E, 0xDF, 0x22, 0xED, 0x96, - 0xBA, 0x7A, 0xCE, 0x98, 0x67, 0xA7, 0x71, 0x51, 0x2E, 0x41, 0x37, 0x9E, 0xC4, 0xB2, 0x43, 0xF5, 0x1A, 0xA7, 0x98, 0x33, 0x65, 0x60, 0xFA, 0x24, - 0x9A, 0x63, 0x62, 0x29, 0xC7, 0x31, 0xED, 0xB1, 0x2D, 0x37, 0x0D, 0x92, 0xC7, 0xD4, 0x77, 0x3A, 0x73, 0xE0, 0xB4, 0x8A, 0x81, 0x6B, 0xC6, 0x1F, - 0x33, 0x9D, 0xBA, 0x01, 0x39, 0xB2, 0x6C, 0x2A, 0x0D, 0x21, 0xF6, 0xB3, 0xBF, 0xC6, 0x97, 0xFC, 0x59, 0x12, 0xB4, 0x08, 0xF5, 0xD8, 0x56, 0x3B, - 0x0C, 0x0B, 0x8F, 0xB1, 0x58, 0xBC, 0x29, 0x0A, 0x11, 0x4D, 0x41, 0x51, 0x6C, 0xF5, 0xBE, 0xF0, 0xBA, 0xB2, 0x02, 0x97, 0x35, 0xC1, 0xC0, 0x51, - 0x01, 0x8E, 0x7B, 0x3A, 0x48, 0x05, 0x17, 0x99, 0x6C, 0xB6, 0x9A, 0x62, 0xC3, 0x0E, 0xAE, 0xC5, 0x26, 0xBA, 0x60, 0xC0, 0xF4, 0x07, 0x3A, 0x1C, - 0x90, 0x08, 0xF6, 0xB2, 0x3D, 0x14, 0x66, 0xEC, 0x90, 0xBA, 0xDA, 0x1E, 0x13, 0x90, 0x07, 0x22, 0xB7, 0x0E, 0x9B, 0x94, 0x26, 0x97, 0xA2, 0xE8, - 0x39, 0x55, 0x9D, 0x69, 0xF9, 0xE5, 0x24, 0x38, 0xED, 0x39, 0xDE, 0xAB, 0x7D, 0xDA, 0x1F, 0xFA, 0x9C, 0x5B, 0xE8, 0x2B, 0x5E, 0xE3, 0x4F, 0x1C, - 0x62, 0x76, 0x60, 0x9C, 0x8A, 0x63, 0x0B, 0x8C, 0xA6, 0xD0, 0x10, 0x3A, 0xC6, 0x25, 0x67, 0x1B, 0xFD, 0xAA, 0xAD, 0x39, 0xE4, 0x0C, 0x97, 0x11, - 0xE0, 0x84, 0x47, 0xBB, 0x3C, 0x1B, 0x0A, 0xBF, 0xD8, 0xB1, 0x3A, 0xBB, 0x4B, 0xF6, 0x48, 0xA7, 0x72, 0x28, 0xBF, 0xF0, 0xA8, 0x59, 0x80, 0x1D, - 0xF7, 0x68, 0xAE, 0x24, 0xBA, 0x7C, 0x1A, 0x24, 0xCA, 0x01, 0x00, 0x0A, 0x02, 0xBD, 0xB2, 0x5E, 0x6C, 0xCC, 0xD7, 0x32, 0xB4, 0x1C, 0xED, 0x0F, - 0xBD, 0x7E, 0x15, 0x88, 0xBD, 0xA6, 0x91, 0xC8, 0xC6, 0x4D, 0x8A, 0x44, 0xC7, 0x69, 0xF6, 0x6A, 0xFD, 0xDC, 0xDF, 0xFC, 0x91, 0x37, 0xD0, 0x08, - 0x0C, 0xB4, 0x8E, 0xD6, 0x8C, 0x35, 0xC2, 0xB4, 0xA5, 0x37, 0xE8, 0x0E, 0x9D, 0x99, 0x57, 0x39, 0x16, 0x9F, 0xE2, 0x4D, 0x5C, 0x6A, 0xA6, 0xCD, - 0xD7, 0xB2, 0xB4, 0x3C, 0x97, 0x97, 0xE5, 0xEB, 0x1D, 0xB7, 0x0B, 0x4E, 0xBF, 0x69, 0x4C, 0x4E, 0xA3, 0x25, 0x86, 0xB2, 0x3B, 0x71, 0xE9, 0x3C, - 0x5B, 0xC0, 0x58, 0x30, 0x20, 0x99, 0x64, 0xE2, 0x2C, 0x9B, 0xE1, 0xE2, 0xE8, 0xB4, 0xB3, 0x31, 0x0A, 0x39, 0x3D, 0x68, 0xD3, 0x7B, 0xDA, 0xD1, - 0xB6, 0x4C, 0x99, 0x98, 0x6A, 0xC4, 0x56, 0x7F, 0x09, 0x1C, 0x84, 0x56, 0x95, 0x01, 0x3A, 0x43, 0x41, 0x5F, 0x4F, 0xD1, 0x76, 0xDA, 0x92, 0x55, - 0xD3, 0x7B, 0x74, 0x77, 0xEE, 0x16, 0x95, 0x90, 0x6C, 0x10, 0xFA, 0xAC, 0xA9, 0x3D, 0xB2, 0x4B, 0xF5, 0x49, 0x67, 0xE6, 0x10, 0x34, 0xD4, 0xFC, - 0xDC, 0x5C, 0xEF, 0xCD, 0x3D, 0xD6, 0x61, 0xBD, 0x11, 0xCF, 0x98, 0x23, 0x73, 0xF4, 0x86, 0xD6, 0x63, 0x9B, 0x54, 0x58, 0xD7, 0xAB, 0xBA, 0x7D, - 0x33, 0x18, 0x07, 0x75, 0xDA, 0xC0, 0xC6, 0x09, 0x16, 0x02, 0xF2, 0x5E, 0xE0, 0x5C, 0xD3, 0x6A, 0xAB, 0xFE, 0xD0, 0x19, 0x8D, 0x79, 0x8A, 0x84, - 0x7A, 0x68, 0x0F, 0x92, 0xBB, 0xC4, 0xD0, 0x3D, 0xA7, 0x27, 0x89, 0xE6, 0x80, 0x5C, 0x77, 0xCC, 0x46, 0x28, 0xEC, 0xCD, 0xF2, 0xA1, 0xD3, 0xA6, - 0xC2, 0x45, 0xEC, 0x56, 0x61, 0x62, 0xBF, 0xB2, 0x5F, 0x76, 0x66, 0xBF, 0xE8, 0x17, 0xDD, 0xCE, 0x9B, 0x81, 0x62, 0xA8, 0x61, 0xA9, 0x95, 0x66, - 0x44, 0x92, 0xA4, 0xA0, 0x50, 0xEA, 0x41, 0x0C, 0x01, 0xA1, 0x98, 0x42, 0x8C, 0x39, 0xEF, 0x06, 0xD1, 0x98, 0x63, 0x84, 0x20, 0x43, 0x08, 0x11, - 0x4A, 0x34, 0x92, 0xD4, 0x30, 0x06, 0x03, 0x66, 0xFA, 0xAA, 0xB0, 0x5B, 0x9B, 0xF3, 0x94, 0x2A, 0x70, 0x0D, 0x33, 0xEA, 0x88, 0x6B, 0x0F, 0x23, - 0x57, 0x8B, 0x04, 0xFF, 0xA2, 0x48, 0x79, 0xD5, 0x9F, 0xFB, 0x96, 0xBF, 0x43, 0x8F, 0x66, 0x34, 0x2B, 0xD0, 0x49, 0x9D, 0x65, 0x89, 0x1D, 0x30, - 0xA9, 0xAB, 0x89, 0x22, 0x07, 0x1E, 0x3E, 0x32, 0x65, 0xC1, 0x1A, 0x4A, 0x0F, 0x9A, 0x52, 0x83, 0x65, 0x23, 0x8E, 0x0B, 0xAE, 0x38, 0x67, 0x77, - 0x30, 0x89, 0x31, 0xCB, 0x45, 0x4A, 0x63, 0x94, 0x4B, 0x67, 0x88, 0x82, 0xBA, 0x3F, 0x51, 0xC8, 0x5D, 0x1D, 0x0C, 0xA8, 0xD5, 0xA8, 0xCE, 0x72, - 0xF9, 0x0E, 0x80, 0xF7, 0xB0, 0xE7, 0x74, 0xCA, 0xFB, 0x85, 0x58, 0x60, 0x96, 0xAF, 0x5E, 0xBE, 0xEC, 0xD0, 0x48, 0x43, 0xC9, 0x99, 0x4E, 0x0D, - 0x93, 0x1F, 0x4D, 0xD7, 0x85, 0x12, 0xEA, 0xF6, 0x1C, 0xA3, 0xE2, 0x42, 0x17, 0x3D, 0x23, 0x28, 0x39, 0xC4, 0x64, 0x9C, 0x29, 0x53, 0xA5, 0x10, - 0xBD, 0x14, 0x13, 0x3B, 0x32, 0x73, 0x54, 0xDD, 0x84, 0x4F, 0x4E, 0x0D, 0xDD, 0xE0, 0xCA, 0xF8, 0xDB, 0x51, 0x3D, 0x9F, 0xDA, 0x51, 0x80, 0x12, - 0x0E, 0x66, 0x1D, 0x7E, 0x0A, 0x39, 0x76, 0x7A, 0x13, 0xAC, 0x85, 0x75, 0x3B, 0xD0, 0x79, 0x0E, 0x8B, 0x3E, 0x71, 0xE1, 0x95, 0x7E, 0x99, 0x25, - 0x46, 0x2A, 0x49, 0xA3, 0xA1, 0x03, 0xB5, 0x2A, 0xBD, 0x39, 0xEB, 0xDE, 0xCA, 0xBA, 0xF1, 0x65, 0xEE, 0xDF, 0x54, 0x13, 0xE3, 0x15, 0x80, 0xF0, - 0xAC, 0x9F, 0xAE, 0x71, 0x23, 0x93, 0x9E, 0xAC, 0xF6, 0xC7, 0x3C, 0x82, 0x58, 0xAA, 0xD3, 0x4D, 0xB8, 0x4C, 0x69, 0x2C, 0x29, 0x8A, 0xA1, 0x15, - 0x29, 0x21, 0xE0, 0x57, 0x53, 0xE3, 0x49, 0x5D, 0xD6, 0x14, 0x77, 0x18, 0x7C, 0x27, 0x08, 0x22, 0x16, 0xF3, 0x27, 0x23, 0xBD, 0x09, 0x35, 0x33, - 0x64, 0x47, 0xA9, 0x52, 0xB1, 0x0F, 0xAF, 0xCA, 0x7A, 0xB7, 0x50, 0x09, 0x14, 0x2E, 0xD1, 0xFA, 0x65, 0xF7, 0xA0, 0x57, 0x3D, 0x35, 0x5C, 0x68, - 0x16, 0x42, 0xE1, 0x94, 0x35, 0x0F, 0xB0, 0x5C, 0x7A, 0x54, 0x33, 0x6E, 0xBE, 0x38, 0x73, 0xBA, 0xB7, 0xA4, 0x7D, 0xB2, 0x1C, 0xE5, 0xA2, 0x99, - 0xB1, 0x51, 0x8D, 0xC2, 0x47, 0x26, 0x26, 0xCE, 0x50, 0x86, 0x89, 0x19, 0xB3, 0x7B, 0x9D, 0xEF, 0x2E, 0x75, 0x51, 0x2E, 0xA9, 0xB2, 0x01, 0xC8, - 0x36, 0x78, 0x15, 0x21, 0xB2, 0x89, 0x0F, 0x74, 0x8D, 0x3B, 0x6A, 0x83, 0x5A, 0xDF, 0xDC, 0x77, 0x0B, 0xAE, 0xA7, 0xE2, 0x10, 0x9D, 0x32, 0xE8, - 0x6E, 0x4C, 0x29, 0xA4, 0x3D, 0x4D, 0xB3, 0xC9, 0x5E, 0x0F, 0xB3, 0xDB, 0x8A, 0x34, 0xE8, 0x99, 0xC9, 0x28, 0x14, 0x87, 0xE2, 0x91, 0x3D, 0xBC, - 0xD0, 0x37, 0x86, 0x28, 0x81, 0xC0, 0x71, 0x28, 0x97, 0xC0, 0x6C, 0x38, 0x9F, 0x8C, 0x2D, 0x29, 0x1C, 0x5A, 0x46, 0xA7, 0x1B, 0x5A, 0x5C, 0x99, - 0xB1, 0xAD, 0xE6, 0xB3, 0xB2, 0x91, 0xEA, 0x7C, 0xE1, 0x84, 0x91, 0xD2, 0xAE, 0x1A, 0x85, 0x6D, 0xA7, 0x8A, 0xE7, 0x8E, 0x1C, 0xDC, 0xD1, 0xC2, - 0x43, 0x11, 0x74, 0x84, 0x74, 0x46, 0x1D, 0x3F, 0x9A, 0xE9, 0x73, 0x6B, 0x63, 0x57, 0xF6, 0x6E, 0x03, 0x3C, 0xC1, 0x34, 0xE9, 0x3D, 0x5B, 0x21, - 0x11, 0x7C, 0x39, 0xA6, 0xAD, 0x5D, 0x18, 0x01, 0xC0, 0x82, 0xEC, 0x41, 0x1A, 0xDC, 0x16, 0x4A, 0xF0, 0x56, 0xE9, 0x49, 0x4C, 0xE5, 0x4F, 0x84, - 0xCD, 0xF6, 0xE5, 0x20, 0x80, 0x4C, 0xEB, 0x80, 0xDF, 0x2D, 0x74, 0x79, 0xBE, 0xE7, 0xD2, 0xA3, 0x7E, 0x06, 0xF2, 0x34, 0x1F, 0xC1, 0x36, 0xA9, - 0xE2, 0xFD, 0x5D, 0x21, 0xD2, 0xFF, 0x49, 0x50, 0x22, 0x58, 0x17, 0x93, 0x6B, 0x2B, 0x80, 0x85, 0x5F, 0x9E, 0x08, 0xBD, 0x97, 0x60, 0x85, 0x76, - 0x30, 0xFA, 0x77, 0x5B, 0xBC, 0x2F, 0x2C, 0x41, 0x86, 0x18, 0x38, 0xD3, 0xD4, 0x4D, 0x2A, 0xF4, 0x79, 0x29, 0xCA, 0xDF, 0x3B, 0x73, 0x82, 0x88, - 0x34, 0x7B, 0x9E, 0x98, 0x67, 0x13, 0xEA, 0x92, 0xE1, 0x28, 0x89, 0xDA, 0x74, 0x36, 0x9F, 0x0E, 0xCA, 0x04, 0x6C, 0x8C, 0x1A, 0x0C, 0xEB, 0x69, - 0x27, 0xDB, 0x6F, 0x84, 0xD5, 0x6F, 0x71, 0x0F, 0xE3, 0xA3, 0x49, 0x3C, 0xC3, 0xCF, 0x04, 0x0B, 0x0E, 0x49, 0x1E, 0x90, 0x4B, 0x6D, 0x25, 0xDC, - 0xA7, 0x2C, 0x62, 0xC6, 0x26, 0x59, 0x87, 0xBC, 0xEA, 0x49, 0x64, 0xA6, 0x75, 0xA7, 0xFC, 0x63, 0x39, 0x5B, 0x12, 0x7A, 0xEC, 0x06, 0x6F, 0x90, - 0x69, 0x59, 0x3D, 0x56, 0xB8, 0x01, 0xC9, 0x39, 0xAD, 0xEB, 0xBC, 0x19, 0x4F, 0xD0, 0xB4, 0x4E, 0x05, 0x47, 0x08, 0x2C, 0x94, 0xDC, 0xF8, 0x99, - 0x83, 0x02, 0x0E, 0xED, 0x21, 0x60, 0x5E, 0x92, 0x26, 0xCA, 0x2E, 0x49, 0x43, 0xC0, 0xE0, 0xD1, 0x5C, 0x75, 0xC0, 0x56, 0x58, 0x42, 0x84, 0xD5, - 0x60, 0x11, 0xDB, 0xB2, 0xD6, 0xBE, 0x5C, 0x1E, 0x7E, 0x00, 0xFF, 0xE4, 0x84, 0xF4, 0x15, 0xE0, 0x0D, 0x77, 0x88, 0x2D, 0xE3, 0x53, 0xF4, 0x10, - 0x04, 0x51, 0x87, 0x2E, 0xA1, 0xD7, 0xEB, 0x30, 0xB7, 0xB3, 0x60, 0xFE, 0x2D, 0x81, 0x25, 0x97, 0x2E, 0x7E, 0xF1, 0xED, 0xC4, 0x1F, 0x52, 0xEE, - 0x80, 0x5C, 0x9D, 0x60, 0xB8, 0xD5, 0xBC, 0x76, 0xC4, 0x4D, 0x96, 0xB1, 0xD1, 0x7B, 0xCA, 0x30, 0xDB, 0x18, 0x6E, 0x87, 0x0E, 0x33, 0x09, 0x26, - 0x6F, 0x90, 0xF6, 0xC2, 0xBE, 0xFD, 0x5A, 0x31, 0xF9, 0x8F, 0x4B, 0x9F, 0x4F, 0xCF, 0x79, 0x1D, 0x46, 0x35, 0x6B, 0x41, 0xB1, 0x2F, 0xBD, 0xB3, - 0x0B, 0x97, 0x97, 0x1B, 0x00, 0x86, 0xF0, 0x41, 0x97, 0x9A, 0x2E, 0x21, 0xA0, 0x37, 0xEF, 0x29, 0xA1, 0xFA, 0x8A, 0x10, 0x1E, 0xB2, 0x73, 0xCC, - 0xF6, 0xE8, 0x06, + 0x28, 0xB5, 0x2F, 0xFD, 0xA4, 0xF5, 0x32, 0x03, 0x00, 0x04, 0x04, 0x03, 0x7E, 0x0F, 0xC4, 0xC7, 0x0F, 0x31, 0x10, 0x60, 0x1C, 0x01, 0x12, 0xC0, + 0x35, 0x5E, 0x7C, 0x8A, 0x00, 0x24, 0x22, 0x64, 0x27, 0x03, 0x26, 0xA0, 0x15, 0xDF, 0x2C, 0x7C, 0x9A, 0xFF, 0xDE, 0xBC, 0xAC, 0x64, 0x51, 0xF0, + 0xB9, 0x19, 0x8C, 0x44, 0x23, 0x80, 0x86, 0x52, 0xB3, 0x15, 0xD1, 0x3B, 0x92, 0xF5, 0x09, 0x0A, 0xFA, 0xBF, 0x0F, 0xC1, 0x0F, 0xBC, 0x0F, 0xAB, + 0x0F, 0x8B, 0xA3, 0xB1, 0xFF, 0x4B, 0x2C, 0x49, 0x95, 0x30, 0xD6, 0x09, 0x3F, 0xAC, 0x65, 0xE7, 0x8B, 0x37, 0x85, 0x51, 0x02, 0x2F, 0x9C, 0xBA, + 0x1C, 0x0D, 0xDF, 0xCB, 0x37, 0xE0, 0x3A, 0x72, 0x17, 0xB8, 0x39, 0xC7, 0x7B, 0xD9, 0xFC, 0x45, 0xDF, 0xF8, 0x9B, 0x4F, 0x6A, 0xF6, 0x54, 0x5A, + 0x72, 0x26, 0x53, 0x30, 0x41, 0x52, 0x4F, 0xA2, 0xF8, 0xE6, 0xBF, 0x97, 0x7A, 0x27, 0x78, 0xDB, 0x8F, 0xC1, 0x41, 0xFC, 0x9E, 0x10, 0xA2, 0x67, + 0x16, 0x4B, 0xBE, 0xEB, 0xBD, 0xF4, 0x45, 0x45, 0x29, 0x90, 0xD6, 0xB2, 0x1A, 0x56, 0xDC, 0xD6, 0xB4, 0xB1, 0x3C, 0x66, 0xDA, 0xC4, 0x56, 0x40, + 0x93, 0xEA, 0x22, 0x1F, 0xB5, 0x26, 0x5E, 0x5B, 0x53, 0x5F, 0x69, 0x97, 0xB0, 0x46, 0x7F, 0xED, 0x29, 0x3C, 0x84, 0xE9, 0x37, 0xD8, 0xEE, 0x14, + 0xFB, 0x90, 0xBB, 0xDC, 0x70, 0xA7, 0xA1, 0xA2, 0x0E, 0x72, 0x58, 0x03, 0x10, 0x24, 0xF3, 0x56, 0xCA, 0xE6, 0xCC, 0x55, 0x90, 0xD4, 0x02, 0xAE, + 0xD4, 0x7D, 0x2F, 0x07, 0xF8, 0x3D, 0xB0, 0xC6, 0x0C, 0x78, 0xDF, 0x2C, 0x24, 0x6F, 0x08, 0xDA, 0x41, 0x6D, 0xCC, 0x30, 0x16, 0x94, 0x82, 0xA0, + 0x9B, 0xA8, 0x22, 0x24, 0x78, 0xC1, 0x13, 0x7A, 0x50, 0xE7, 0x7D, 0x4E, 0x5E, 0x91, 0x07, 0xF4, 0x1D, 0x4D, 0x75, 0xA7, 0x4C, 0xE6, 0x22, 0xE4, + 0x0B, 0xE4, 0xDD, 0x09, 0x90, 0x59, 0xD0, 0x46, 0x7D, 0xA9, 0x3B, 0x40, 0x88, 0x9C, 0xAA, 0x7A, 0x44, 0xF5, 0x8B, 0x68, 0xF5, 0x8A, 0xDA, 0x86, + 0x46, 0x40, 0x3D, 0x60, 0x71, 0x69, 0x69, 0x69, 0x62, 0x1B, 0xE9, 0x97, 0x00, 0xFF, 0x99, 0x9F, 0x59, 0x8E, 0xA1, 0x63, 0x0D, 0xA3, 0xB4, 0x90, + 0xFD, 0x9B, 0x31, 0x8C, 0xE0, 0xE0, 0x18, 0x38, 0x4D, 0x79, 0x5F, 0x0A, 0xE7, 0xF8, 0xB5, 0xB2, 0x33, 0xB7, 0x8E, 0x01, 0xE5, 0xA4, 0x67, 0x77, + 0x00, 0x32, 0xAD, 0x89, 0x50, 0x05, 0x13, 0x2A, 0x9E, 0x2F, 0x61, 0x1E, 0x1A, 0x70, 0xA2, 0xA6, 0x24, 0x4B, 0x44, 0x69, 0xA6, 0x03, 0x12, 0x30, + 0x12, 0x24, 0x03, 0xE5, 0x02, 0x3B, 0x45, 0x8E, 0xC4, 0x80, 0x18, 0x45, 0xCD, 0x16, 0x59, 0x10, 0xFD, 0x04, 0xD6, 0x82, 0x07, 0x9C, 0x68, 0xA4, + 0x41, 0x08, 0x8D, 0x89, 0x0C, 0x47, 0x87, 0x1C, 0x9D, 0x08, 0x11, 0x79, 0x12, 0x74, 0x1A, 0x03, 0x44, 0x2B, 0xF3, 0x13, 0xA5, 0x40, 0xF9, 0x47, + 0x8C, 0xE4, 0x23, 0x33, 0xD3, 0x43, 0xA0, 0x2B, 0x17, 0xA0, 0x94, 0xC4, 0xA3, 0x40, 0x69, 0x27, 0xCC, 0x2B, 0x80, 0xDB, 0x97, 0x4E, 0x05, 0xBA, + 0x1C, 0x11, 0x13, 0x00, 0xF8, 0xA1, 0x18, 0xDB, 0x48, 0x81, 0xC3, 0x43, 0xBF, 0x11, 0x62, 0x68, 0x63, 0x4E, 0x51, 0xD3, 0x9A, 0xF9, 0x69, 0x78, + 0xC3, 0x99, 0x5E, 0x87, 0x0C, 0xA3, 0xA3, 0xCC, 0xD8, 0x0C, 0xF3, 0x48, 0x04, 0x53, 0xCB, 0xBE, 0x90, 0xAD, 0xB8, 0xD8, 0xBF, 0x2D, 0x73, 0x3E, + 0x65, 0x16, 0x0E, 0x3F, 0xB9, 0x38, 0x44, 0xB8, 0x0F, 0xF8, 0xD7, 0x00, 0x0A, 0x5E, 0x0C, 0x02, 0xA6, 0x54, 0x70, 0xE5, 0x73, 0xC1, 0x2F, 0xAB, + 0x03, 0x2B, 0x4C, 0xDA, 0x7C, 0x79, 0x3F, 0x8C, 0x4F, 0xE7, 0x7A, 0x7E, 0xA8, 0x5F, 0xCD, 0xA2, 0x17, 0x19, 0x6D, 0x78, 0xA3, 0x8A, 0xCE, 0x67, + 0xA0, 0xB0, 0x72, 0xA7, 0x4C, 0x98, 0x08, 0xA6, 0xB7, 0xFC, 0x98, 0x73, 0x94, 0xFF, 0x9B, 0x90, 0x3D, 0x38, 0x78, 0xC8, 0x9D, 0x1F, 0xD9, 0x80, + 0x78, 0xC0, 0xB9, 0x46, 0xDB, 0x1A, 0xEE, 0x49, 0x37, 0x11, 0x42, 0xA2, 0x4B, 0xA6, 0x8C, 0x51, 0xAF, 0xEE, 0x21, 0xC8, 0xB1, 0xFB, 0x96, 0x59, + 0xC9, 0x3E, 0x48, 0x6E, 0xB1, 0x07, 0x20, 0xF3, 0x78, 0xD9, 0x79, 0xF3, 0x02, 0x32, 0x93, 0xB9, 0x43, 0x0D, 0x1B, 0x73, 0x56, 0xBD, 0x48, 0xBE, + 0xAD, 0x8B, 0xC8, 0x76, 0x35, 0x14, 0xF3, 0x6D, 0x0C, 0xD9, 0x92, 0x8C, 0xA2, 0xD6, 0x22, 0x9B, 0xED, 0x0E, 0x7A, 0x13, 0xFC, 0x00, 0x98, 0x18, + 0x46, 0x1F, 0x53, 0x3D, 0x73, 0x1C, 0x6E, 0x21, 0x67, 0x98, 0x08, 0xAF, 0xBF, 0x1D, 0x74, 0x58, 0x11, 0xFD, 0xBB, 0x7D, 0x03, 0xCD, 0xBD, 0x49, + 0x36, 0xF0, 0x3E, 0xCA, 0xF2, 0xB2, 0xB8, 0xD8, 0x3D, 0xFD, 0x6D, 0xDB, 0x2D, 0xF2, 0x31, 0xA5, 0x09, 0x92, 0x29, 0x5B, 0xA0, 0x52, 0x64, 0xAE, + 0x44, 0x91, 0x60, 0xAB, 0x6B, 0x00, 0xA8, 0xDF, 0x73, 0xE1, 0xA1, 0x88, 0x29, 0xC8, 0xBD, 0xD1, 0x04, 0xD7, 0x82, 0x83, 0xDB, 0x4A, 0x98, 0x27, + 0x2C, 0x3E, 0xB0, 0x3A, 0x79, 0x3D, 0x6A, 0x62, 0xE8, 0xC6, 0xC4, 0xCA, 0x96, 0x2D, 0x91, 0x10, 0xB6, 0x40, 0x73, 0xF7, 0xAC, 0x0C, 0x25, 0x4F, + 0x5C, 0xB5, 0x61, 0xC8, 0x24, 0x69, 0x11, 0x68, 0x07, 0x98, 0xCE, 0x76, 0xC8, 0xF9, 0x2C, 0x28, 0x6D, 0x48, 0x02, 0x09, 0x65, 0xDB, 0x4E, 0x63, + 0x1F, 0x2D, 0x61, 0xF1, 0xFA, 0xD7, 0x53, 0x57, 0xDE, 0xD5, 0xC6, 0x4D, 0x6B, 0x13, 0x4C, 0x56, 0x21, 0x4C, 0xAC, 0x32, 0xB0, 0xAB, 0xAD, 0x62, + 0xAB, 0x4A, 0xBE, 0xF7, 0x69, 0x27, 0x07, 0x5B, 0x94, 0x4C, 0x20, 0xB4, 0xBC, 0xD9, 0x66, 0x64, 0xD6, 0x03, 0xA8, 0xA2, 0x22, 0x68, 0x18, 0xD0, + 0xD3, 0x93, 0xD3, 0x73, 0x40, 0x23, 0x6F, 0x9B, 0x84, 0x93, 0x2A, 0xC6, 0x66, 0x4C, 0xB8, 0x48, 0x9B, 0xA3, 0x66, 0x12, 0x11, 0x2B, 0x11, 0xBC, + 0x52, 0x33, 0x16, 0x4B, 0x66, 0x0D, 0x5C, 0x4C, 0xB9, 0x01, 0x97, 0x42, 0x68, 0x2C, 0x4D, 0x60, 0x55, 0xB6, 0x2A, 0xDD, 0x43, 0x0A, 0x21, 0x08, + 0xE9, 0x6B, 0x28, 0x33, 0x49, 0x50, 0x8E, 0x0D, 0x4C, 0x22, 0xB1, 0xD1, 0x36, 0x59, 0x83, 0x45, 0xEE, 0xE6, 0x45, 0xDA, 0x75, 0x90, 0x3C, 0x37, + 0x1E, 0xF1, 0x3D, 0xDE, 0x51, 0x8A, 0x72, 0xEF, 0xA3, 0xD0, 0xC8, 0xB6, 0x0A, 0x44, 0x91, 0x8F, 0x11, 0xC4, 0x94, 0x1F, 0x9E, 0x88, 0x3E, 0x68, + 0xE4, 0xB0, 0xF6, 0xB0, 0xD5, 0xE4, 0xC1, 0xB8, 0xCB, 0x03, 0x86, 0x57, 0x24, 0x0D, 0xA0, 0x88, 0x04, 0x7C, 0xE7, 0x3E, 0xED, 0xB8, 0x9A, 0x45, + 0xBC, 0xB1, 0x0F, 0xE3, 0x82, 0x75, 0x1C, 0xD0, 0xD0, 0x66, 0x68, 0x42, 0x48, 0xEF, 0x42, 0x63, 0x2D, 0x2C, 0x64, 0xC0, 0xC7, 0x88, 0x6B, 0xDD, + 0xE7, 0xFE, 0xFF, 0x7F, 0x77, 0x77, 0x42, 0xFF, 0x66, 0x0C, 0x13, 0xF3, 0x09, 0x14, 0xA0, 0xBE, 0xB2, 0x75, 0xE4, 0x82, 0xEF, 0x1C, 0x35, 0x1B, + 0x00, 0x08, 0xDA, 0xF1, 0x9D, 0x06, 0x1C, 0x1A, 0x9F, 0x17, 0xFC, 0xBB, 0xF9, 0x51, 0xCE, 0x13, 0x2C, 0xDC, 0xA5, 0x38, 0x12, 0x49, 0xF3, 0xCA, + 0x46, 0x57, 0x09, 0x88, 0x0A, 0xA2, 0xE0, 0x93, 0x84, 0x9E, 0x03, 0xC9, 0x3C, 0x26, 0xE0, 0xFC, 0xFF, 0x59, 0x5A, 0x58, 0x7C, 0xE8, 0xC1, 0xC1, + 0x39, 0x24, 0xC4, 0xD1, 0x11, 0xC2, 0xD4, 0xE6, 0xF4, 0xAE, 0xFA, 0xBB, 0x42, 0x07, 0x39, 0x19, 0x04, 0xE2, 0xFC, 0xCA, 0x10, 0x24, 0xC9, 0x70, + 0x3C, 0x80, 0xFC, 0x07, 0xAA, 0x71, 0x6F, 0x66, 0x40, 0x37, 0x2A, 0x79, 0x6D, 0x7C, 0x63, 0xDB, 0xCE, 0x0D, 0xDC, 0xC1, 0x1F, 0x65, 0x63, 0x2A, + 0xB1, 0x46, 0x3F, 0x51, 0x23, 0xC3, 0xD1, 0x20, 0x4B, 0x26, 0x0D, 0x83, 0x10, 0xB4, 0x39, 0x24, 0xFB, 0xEF, 0xC8, 0x19, 0x1E, 0x8D, 0xD9, 0x0F, + 0x2A, 0x65, 0x35, 0x94, 0x3E, 0xD9, 0xA8, 0x4E, 0x3F, 0x46, 0xB9, 0x26, 0x56, 0xEB, 0x12, 0xC6, 0xEA, 0x21, 0xAC, 0xA5, 0x28, 0xE7, 0x05, 0x1C, + 0x1B, 0x65, 0x79, 0xE1, 0xDC, 0x95, 0xE5, 0xF2, 0xC0, 0x72, 0x89, 0x7C, 0x2F, 0xE3, 0xC8, 0x49, 0xCB, 0x26, 0x3E, 0x8C, 0xDB, 0x72, 0x8E, 0x3F, + 0x53, 0x9E, 0x34, 0xFD, 0x54, 0x72, 0xCA, 0xAD, 0xBF, 0x66, 0x0C, 0x54, 0xE5, 0x1B, 0x18, 0xBB, 0xE6, 0x60, 0xE3, 0x34, 0xDE, 0xBC, 0x6E, 0xCC, + 0x93, 0xEF, 0x8D, 0x43, 0x9D, 0x70, 0xA0, 0xBC, 0xC2, 0xD7, 0x53, 0xD6, 0x48, 0x05, 0xC9, 0xB2, 0x9A, 0xAC, 0x63, 0xBB, 0x1B, 0x75, 0x97, 0x76, + 0x4C, 0x32, 0xC1, 0x5D, 0xFE, 0x7F, 0xAD, 0x3D, 0x0B, 0x0B, 0xA4, 0xCD, 0x6E, 0x80, 0xED, 0x4E, 0x5E, 0x89, 0x07, 0x74, 0x1A, 0x4D, 0x75, 0x9B, + 0x4C, 0x66, 0xC9, 0xFA, 0xC2, 0x93, 0xEE, 0xC4, 0x66, 0x17, 0x6C, 0x71, 0x99, 0x04, 0x69, 0x00, 0xCE, 0x85, 0x20, 0xAB, 0xCF, 0x02, 0xDA, 0xDB, + 0xB8, 0xEE, 0x76, 0x62, 0x1A, 0x72, 0x30, 0x8D, 0x09, 0x3D, 0xB9, 0x74, 0x44, 0x92, 0x12, 0x14, 0xD4, 0x44, 0xC0, 0x42, 0x9D, 0x73, 0x33, 0x70, + 0xCB, 0xE9, 0xD6, 0x72, 0x22, 0xC0, 0xE2, 0x40, 0xA4, 0x42, 0xB0, 0x51, 0x07, 0x37, 0x23, 0xCA, 0x03, 0x21, 0x7B, 0xE6, 0xB7, 0x20, 0x63, 0xB7, + 0x33, 0x85, 0xB6, 0x23, 0x07, 0x1C, 0xF4, 0x7C, 0x6C, 0x55, 0xC8, 0x76, 0xB6, 0x31, 0xE2, 0xDA, 0x4D, 0xAD, 0x56, 0x6B, 0x36, 0x38, 0x60, 0x96, + 0x66, 0xF3, 0x68, 0x66, 0x18, 0x6C, 0x1C, 0x98, 0x7C, 0x96, 0x07, 0x9B, 0xF5, 0xCD, 0x96, 0x19, 0x0A, 0x91, 0x29, 0x06, 0x8F, 0x11, 0x61, 0x50, + 0xBB, 0x06, 0x9A, 0x7D, 0x77, 0xF3, 0x0F, 0xA9, 0x19, 0xDF, 0x11, 0x3C, 0x92, 0x75, 0x5E, 0xEC, 0x68, 0x67, 0x72, 0x82, 0x1F, 0x3B, 0x87, 0x5C, + 0xF2, 0x88, 0xFA, 0x8D, 0x91, 0x0C, 0xA3, 0x46, 0xCD, 0xA1, 0x6C, 0x6E, 0x9C, 0xAC, 0x33, 0x51, 0xC9, 0x86, 0x8D, 0xFB, 0xEA, 0xA4, 0x99, 0xC8, + 0x68, 0xAA, 0x79, 0xF6, 0xC9, 0xCE, 0x13, 0xC5, 0xCC, 0x5B, 0x5A, 0x4F, 0x8D, 0xCD, 0x38, 0x8F, 0xBB, 0x83, 0x03, 0x66, 0x29, 0x0A, 0xB5, 0x86, + 0x1C, 0xF2, 0x59, 0xEE, 0xFE, 0x27, 0x6B, 0xC6, 0x6B, 0x98, 0x7C, 0xF8, 0x20, 0x0B, 0x91, 0xA9, 0x4B, 0x2F, 0xCD, 0x60, 0xFB, 0xAE, 0x8E, 0xFA, + 0x25, 0x93, 0xAC, 0x61, 0xCF, 0x62, 0x40, 0x86, 0x62, 0x50, 0x73, 0xB5, 0x91, 0x1B, 0xC0, 0xEC, 0x73, 0xA8, 0x64, 0x1C, 0xC5, 0x9E, 0x17, 0x9A, + 0x61, 0x9C, 0xD5, 0x85, 0x18, 0xC8, 0x40, 0x80, 0x80, 0x30, 0xA9, 0x7E, 0x44, 0x99, 0xF8, 0xBC, 0x42, 0xF6, 0x38, 0x91, 0x20, 0xC0, 0x37, 0x27, + 0xCB, 0x38, 0xED, 0x3E, 0x64, 0x66, 0xA4, 0xDF, 0x9A, 0xCA, 0xC7, 0xEB, 0x73, 0x5F, 0x13, 0xA4, 0xDB, 0xDA, 0xEF, 0x6E, 0xF3, 0x1B, 0x82, 0x3D, + 0x46, 0x13, 0x1E, 0x70, 0x5D, 0xE3, 0xD8, 0x4B, 0x43, 0xC3, 0xC8, 0x4C, 0xE9, 0xDD, 0x6F, 0xBF, 0xBB, 0x92, 0x29, 0x05, 0x50, 0x29, 0xDB, 0xF1, + 0x0A, 0x31, 0x5B, 0x61, 0xF5, 0x61, 0xC9, 0xA1, 0xA6, 0x88, 0x58, 0xFF, 0x8B, 0x8A, 0x99, 0xB6, 0x85, 0x41, 0x42, 0x81, 0xE9, 0x82, 0x20, 0xA3, + 0xA6, 0xED, 0x31, 0x7E, 0xF5, 0x2F, 0xA0, 0x2B, 0x3B, 0x22, 0x33, 0x37, 0xB3, 0x58, 0x6A, 0xF2, 0x90, 0xC2, 0xDD, 0xD1, 0x52, 0xF3, 0x47, 0x6A, + 0x66, 0x10, 0x9D, 0xD5, 0xFF, 0x87, 0xDF, 0x2B, 0x33, 0x2F, 0x8B, 0xED, 0x8A, 0xBC, 0xEB, 0xCC, 0x92, 0xC1, 0x76, 0x5F, 0x3B, 0xF3, 0x68, 0x11, + 0x1F, 0x28, 0xF2, 0x63, 0x8B, 0x55, 0x0B, 0x11, 0xDD, 0xBA, 0x40, 0x81, 0x51, 0xC9, 0x9D, 0xE9, 0xEA, 0xB7, 0xB8, 0xB0, 0x88, 0x01, 0x3F, 0xD7, + 0x67, 0x52, 0x2E, 0x0B, 0xDA, 0xD7, 0xDD, 0x80, 0x03, 0x33, 0x10, 0xCA, 0x2F, 0xFE, 0x0C, 0xBE, 0x38, 0xFC, 0xF9, 0xEB, 0xFE, 0x56, 0x30, 0xB9, + 0x4A, 0x2C, 0xF7, 0xB5, 0x7E, 0x96, 0xA6, 0x1A, 0x59, 0x95, 0xC6, 0x4C, 0xAE, 0xD4, 0xAD, 0x83, 0xC9, 0x8D, 0x4C, 0x6F, 0x9F, 0xFC, 0xC5, 0xFF, + 0x66, 0xEC, 0x69, 0x2E, 0x12, 0x0C, 0x0A, 0x15, 0x66, 0x39, 0x07, 0xE6, 0xAB, 0xE3, 0x0B, 0xD6, 0xC4, 0x57, 0x08, 0x99, 0x3D, 0xD3, 0xC0, 0xBE, + 0x64, 0xC3, 0x81, 0x01, 0x1E, 0x55, 0x69, 0x5F, 0xA0, 0xF4, 0x75, 0x91, 0xCE, 0x6D, 0x48, 0x03, 0x70, 0x7A, 0x10, 0x47, 0x53, 0x51, 0x8C, 0xC6, + 0x82, 0x45, 0x82, 0x92, 0x7B, 0x8C, 0xD1, 0x06, 0x98, 0x43, 0xB1, 0xE0, 0x3F, 0x28, 0xE0, 0xC1, 0xA8, 0x2B, 0x3F, 0xD2, 0x7E, 0x2D, 0x1B, 0x58, + 0x29, 0x12, 0xE5, 0x8B, 0x2A, 0xD5, 0x3B, 0x25, 0x04, 0xAF, 0x27, 0x2A, 0xAF, 0xCE, 0xD6, 0xEE, 0x92, 0xC2, 0xDD, 0xA7, 0x31, 0x99, 0x8A, 0x0B, + 0x86, 0x29, 0x85, 0xBB, 0x53, 0xAC, 0x62, 0x52, 0xB7, 0xE8, 0x50, 0x95, 0xA2, 0x48, 0x77, 0x5C, 0xD2, 0xB2, 0x60, 0x23, 0xB9, 0x7C, 0x46, 0xCA, + 0x0C, 0x3F, 0x4F, 0x08, 0x7A, 0xF2, 0x8D, 0x9E, 0x59, 0x53, 0x9C, 0xC0, 0x06, 0x38, 0xEF, 0x96, 0xCD, 0xA0, 0xDD, 0x22, 0x7A, 0x67, 0x4D, 0x06, + 0x45, 0x88, 0xE4, 0xC8, 0x61, 0x7E, 0x83, 0x91, 0x59, 0xD9, 0x83, 0x89, 0x37, 0x73, 0xD9, 0x5A, 0x6B, 0x39, 0xE3, 0x48, 0x25, 0xAF, 0xE3, 0x10, + 0xC9, 0xC3, 0x21, 0x02, 0x4A, 0x98, 0x53, 0x71, 0xCA, 0x21, 0x18, 0x41, 0x7E, 0x40, 0xF7, 0x49, 0x12, 0x69, 0x64, 0x1D, 0x81, 0x30, 0xEA, 0x0E, + 0x83, 0x8B, 0x64, 0xDF, 0x4A, 0x35, 0x38, 0xF8, 0xF4, 0x8C, 0x46, 0x2B, 0xB0, 0x53, 0x4C, 0xB8, 0x32, 0xEB, 0xAD, 0x36, 0x01, 0xAB, 0x36, 0xDC, + 0x30, 0xF8, 0x0C, 0x5E, 0x00, 0x83, 0x49, 0x01, 0x92, 0x24, 0x54, 0x94, 0x2A, 0xA6, 0x76, 0x3B, 0xA5, 0x4A, 0x29, 0x48, 0xD1, 0x44, 0x50, 0xBC, + 0x44, 0xA0, 0xA8, 0xD4, 0x12, 0xBC, 0x58, 0x78, 0x72, 0x40, 0x23, 0x10, 0x2A, 0xAB, 0x73, 0x11, 0x47, 0xA9, 0x9E, 0xF4, 0x54, 0x94, 0x22, 0x5A, + 0x9C, 0x6E, 0x1B, 0xEF, 0x34, 0x9F, 0x7A, 0x85, 0x3A, 0x41, 0xA9, 0xAE, 0x47, 0xE1, 0x1D, 0x11, 0x02, 0x7D, 0xF1, 0xDB, 0xA2, 0x84, 0x95, 0xDB, + 0x17, 0xDF, 0x45, 0x5E, 0xDF, 0x99, 0x53, 0x5E, 0x4F, 0x4E, 0x5E, 0x34, 0xA6, 0x2A, 0x37, 0x43, 0xFA, 0xAE, 0x99, 0x8E, 0x8A, 0x4B, 0xDC, 0x14, + 0x1A, 0xC1, 0xBC, 0x5A, 0x02, 0x24, 0xB1, 0x37, 0x64, 0x71, 0xC8, 0xC6, 0x5A, 0xD9, 0x81, 0x15, 0xE2, 0x06, 0xEB, 0x33, 0xBB, 0x3F, 0xF1, 0x75, + 0xA5, 0x02, 0x32, 0x44, 0xF6, 0x8E, 0x16, 0xA2, 0x0B, 0x45, 0x79, 0x85, 0xAC, 0x53, 0xE2, 0x84, 0xF1, 0x77, 0x75, 0x02, 0x31, 0x86, 0x5F, 0xF5, + 0x5B, 0xF0, 0x36, 0x79, 0x6B, 0xD0, 0xB3, 0x35, 0x09, 0x1D, 0xC8, 0x6E, 0x43, 0x17, 0xDD, 0x83, 0xE8, 0x4E, 0x76, 0x1C, 0x74, 0xCC, 0x2E, 0x84, + 0x2E, 0xB0, 0x1F, 0xA1, 0x67, 0x6A, 0x2B, 0x54, 0xE8, 0xA4, 0x5A, 0x87, 0xF6, 0xAA, 0x9F, 0x76, 0x75, 0xBA, 0x3A, 0x80, 0x8E, 0xA2, 0x7E, 0xD2, + 0xF5, 0xDA, 0xD6, 0x53, 0xEC, 0x1A, 0x74, 0xC3, 0xFD, 0x4F, 0xCF, 0xDC, 0x71, 0x3A, 0x76, 0x7F, 0x41, 0x03, 0x5B, 0xD9, 0x7F, 0xD0, 0x48, 0x6A, + 0x23, 0x74, 0xD4, 0x95, 0x9A, 0x09, 0x0D, 0x54, 0x87, 0xEA, 0x0A, 0xEA, 0x26, 0xFA, 0xCB, 0x0E, 0xA3, 0x1B, 0xED, 0x47, 0xBA, 0xD6, 0x8E, 0xB6, + 0x43, 0x91, 0x37, 0xEC, 0x69, 0xE4, 0x06, 0x35, 0x4E, 0xE6, 0x50, 0xBF, 0x20, 0xC3, 0xA8, 0x7F, 0x90, 0x19, 0x6B, 0x23, 0xE4, 0xD0, 0x9A, 0xD1, + 0xDC, 0xA4, 0x0E, 0x35, 0x8B, 0xEE, 0xAF, 0x64, 0x27, 0xBB, 0x9C, 0x39, 0x66, 0x3F, 0x32, 0x17, 0xD8, 0xB7, 0xCC, 0x5C, 0x3B, 0x12, 0x39, 0xA9, + 0xEE, 0x22, 0x7B, 0xD5, 0x12, 0xC8, 0x5A, 0xB5, 0x94, 0x39, 0x0B, 0xEC, 0x04, 0xEE, 0xD2, 0x22, 0xCE, 0xF0, 0xED, 0xCF, 0xE0, 0xFF, 0x9B, 0xF1, + 0x04, 0x3B, 0x38, 0xF8, 0x2D, 0xC2, 0xFF, 0x3E, 0x68, 0xBF, 0x9A, 0x12, 0x6B, 0x29, 0x50, 0xBA, 0xB8, 0xFC, 0x76, 0x4B, 0x33, 0x9D, 0xE2, 0xEE, + 0x6A, 0xDE, 0xE3, 0xDC, 0xB1, 0x70, 0xF0, 0xB8, 0x2A, 0x5B, 0x55, 0x7A, 0x56, 0xED, 0x0C, 0x5E, 0x95, 0x2C, 0xF0, 0xEC, 0x51, 0xF0, 0xCB, 0x42, + 0x65, 0x2B, 0x45, 0x15, 0x34, 0xA0, 0x9E, 0x82, 0x51, 0x93, 0x4A, 0xD0, 0x12, 0x85, 0xD4, 0x01, 0xAA, 0x01, 0x81, 0x04, 0x5F, 0xE5, 0x27, 0x0F, + 0x64, 0x04, 0x76, 0x40, 0xC8, 0x75, 0x8C, 0x38, 0x27, 0x3D, 0xC0, 0x03, 0x90, 0x79, 0x4D, 0x84, 0x8D, 0x4C, 0xA8, 0x37, 0xF6, 0x25, 0xCC, 0x51, + 0x03, 0x4E, 0xF4, 0x94, 0x64, 0x9F, 0x28, 0xCD, 0x0C, 0x22, 0x05, 0xCB, 0x98, 0x91, 0x0C, 0x0C, 0x29, 0xB0, 0x83, 0x7B, 0x24, 0xC6, 0x6A, 0x24, + 0x45, 0x53, 0x64, 0x61, 0xB7, 0x26, 0xC0, 0xA6, 0x34, 0xE0, 0x44, 0x2B, 0x0D, 0xE2, 0x8B, 0x4C, 0x64, 0x58, 0x65, 0xE8, 0x31, 0x80, 0x90, 0xE6, + 0x4E, 0xD0, 0xE9, 0x13, 0x10, 0x6D, 0x82, 0x3D, 0x8A, 0xFD, 0x23, 0x96, 0xF3, 0x91, 0xD1, 0xEA, 0x21, 0x50, 0xC2, 0x2C, 0x40, 0xE9, 0xE6, 0x51, + 0x30, 0xB7, 0x13, 0xA6, 0x17, 0xC0, 0xCD, 0x84, 0x4E, 0x85, 0xC1, 0x1C, 0x11, 0x39, 0x00, 0xF8, 0x0D, 0x19, 0xDB, 0xA8, 0xC4, 0xE1, 0xC1, 0x7A, + 0x23, 0xA4, 0xD5, 0xC6, 0x5C, 0x59, 0xD3, 0x5A, 0x85, 0x4C, 0xC3, 0xB7, 0xCE, 0xF4, 0xAE, 0x32, 0x8C, 0x4D, 0x6C, 0x99, 0x31, 0x2F, 0x8C, 0x8B, + 0x0C, 0x30, 0x08, 0x78, 0x5E, 0xB2, 0x12, 0xB8, 0xA0, 0x41, 0xB5, 0xCC, 0xA1, 0x9D, 0x59, 0x38, 0xA4, 0x99, 0xC5, 0x78, 0x51, 0x76, 0x78, 0x85, + 0x14, 0xF9, 0xD7, 0x68, 0x10, 0xBC, 0xA8, 0x06, 0x4C, 0xC9, 0xC1, 0x3E, 0x9D, 0xB3, 0xC9, 0xA1, 0x7E, 0x5C, 0xE7, 0x12, 0x7D, 0x6F, 0x71, 0x58, + 0x9A, 0x57, 0x4C, 0xFF, 0xFF, 0x3A, 0xFF, 0xC3, 0xB0, 0x06, 0x07, 0x7B, 0xB8, 0xD3, 0xFE, 0xFF, 0x2D, 0x2C, 0xE3, 0x98, 0xDC, 0xBA, 0xDB, 0xCD, + 0x87, 0x5A, 0x1B, 0x8F, 0xE5, 0x20, 0xB4, 0x83, 0x83, 0x0F, 0x61, 0xED, 0x49, 0x79, 0x8D, 0x26, 0xA1, 0x46, 0x03, 0xA4, 0x01, 0xD9, 0x46, 0x1A, + 0x01, 0x40, 0xB4, 0x3B, 0xC8, 0xD7, 0x6A, 0x3C, 0x0B, 0x71, 0xC3, 0x2C, 0xC2, 0x28, 0x8A, 0x27, 0x63, 0x41, 0x56, 0xC5, 0x0E, 0x65, 0xC2, 0x8E, + 0x4C, 0x01, 0x2B, 0x82, 0xF4, 0xC5, 0x9B, 0xE3, 0xE5, 0x4A, 0xE3, 0x75, 0xB3, 0xCB, 0xE5, 0xAB, 0xC4, 0xE5, 0x14, 0x60, 0xEB, 0xE7, 0x4B, 0xEB, + 0xEC, 0x29, 0x6B, 0xC1, 0x04, 0xF2, 0x06, 0x1B, 0xEB, 0x2D, 0xC5, 0x15, 0x0C, 0x0D, 0x56, 0x1C, 0xEA, 0xAA, 0x1A, 0xFF, 0x18, 0x00, 0xEE, 0x2D, + 0xE8, 0xDC, 0xA0, 0xC0, 0x51, 0x07, 0xD5, 0x12, 0x17, 0x53, 0xA6, 0xE6, 0xA9, 0x17, 0x1F, 0x51, 0xBA, 0xB2, 0x51, 0x05, 0x40, 0xA1, 0x00, 0xFC, + 0x96, 0x13, 0x78, 0xD9, 0x3E, 0x25, 0xF1, 0x08, 0xEC, 0x5C, 0xF2, 0x1A, 0x0C, 0xD8, 0xA9, 0x44, 0x93, 0x03, 0x15, 0x82, 0x9A, 0xFE, 0x3C, 0xEF, + 0x31, 0xBC, 0xA8, 0xBF, 0x30, 0x16, 0x22, 0xB9, 0x4B, 0xBE, 0x8A, 0x90, 0x78, 0xB9, 0x80, 0x76, 0x6D, 0x5D, 0x19, 0x73, 0x1D, 0xD9, 0x96, 0xB7, + 0xAA, 0x72, 0xAD, 0x3B, 0x1B, 0x17, 0xA4, 0xA0, 0x19, 0x23, 0x26, 0x9F, 0xA2, 0xAA, 0x24, 0x02, 0x87, 0x0B, 0xFA, 0x1F, 0xDC, 0xEF, 0x87, 0x8D, + 0x27, 0x92, 0x9E, 0xE7, 0xF6, 0x4B, 0xC2, 0x93, 0xEA, 0x88, 0xFA, 0xA2, 0xFB, 0x58, 0x93, 0xE7, 0x8F, 0xF2, 0x10, 0x50, 0x16, 0xB5, 0x1D, 0x1B, + 0x87, 0x32, 0x56, 0xE8, 0x8D, 0xD0, 0xAA, 0xF1, 0xAF, 0x04, 0x78, 0xC2, 0xBB, 0x73, 0xC1, 0x0A, 0xDE, 0xCD, 0x4B, 0x67, 0x0A, 0x81, 0xDF, 0xF3, + 0xA9, 0x7E, 0x10, 0x5D, 0x03, 0x97, 0x56, 0x21, 0x12, 0x4D, 0xAD, 0x35, 0xB7, 0x73, 0xD8, 0xF8, 0x50, 0x51, 0x16, 0x12, 0x70, 0x49, 0x3D, 0x99, + 0xE9, 0x16, 0x20, 0x07, 0x39, 0x3A, 0x9A, 0x32, 0x82, 0x12, 0xF7, 0x45, 0x5C, 0x2C, 0x04, 0xD2, 0x4A, 0x07, 0xCC, 0xE9, 0xD1, 0xB8, 0x18, 0x11, + 0x8A, 0xDC, 0xA1, 0x18, 0x34, 0xB8, 0x50, 0xA1, 0x80, 0xA0, 0xC8, 0x11, 0x40, 0x97, 0xB5, 0x3F, 0xA2, 0x8D, 0xFD, 0xAB, 0x9C, 0x13, 0x03, 0x02, + 0x7C, 0x97, 0x78, 0x01, 0x31, 0x55, 0x78, 0x02, 0xB5, 0x9E, 0x98, 0x75, 0xF6, 0x3C, 0x44, 0x95, 0x38, 0x61, 0xE1, 0xB6, 0x13, 0xF8, 0x59, 0xBC, + 0x53, 0xD9, 0x22, 0xC6, 0x3B, 0xB6, 0xD2, 0x71, 0x6C, 0x18, 0x85, 0x88, 0xD1, 0x17, 0xFF, 0x5F, 0x45, 0xC2, 0x8B, 0x0D, 0x67, 0xA0, 0xB2, 0xFC, + 0x16, 0xB6, 0xE2, 0x45, 0x1E, 0x57, 0x75, 0xA5, 0xFE, 0xFF, 0x07, 0x15, 0x90, 0xAD, 0xE3, 0x80, 0xE7, 0x74, 0x63, 0x31, 0x8D, 0x18, 0x26, 0x0C, + 0xE3, 0xD7, 0xFF, 0xC0, 0xA7, 0x7F, 0xCF, 0x17, 0xF0, 0x67, 0xBE, 0xCC, 0x87, 0x29, 0xF0, 0xA3, 0x28, 0x89, 0xC0, 0x9E, 0x28, 0x86, 0x11, 0xA9, + 0x44, 0x70, 0x19, 0x23, 0x3D, 0x79, 0xC7, 0x5C, 0xD7, 0xB6, 0x16, 0xC1, 0xA6, 0xBF, 0xBF, 0xE6, 0xBB, 0x50, 0x09, 0xA4, 0x52, 0x6B, 0xE4, 0x4D, + 0x83, 0x2E, 0x87, 0x61, 0x30, 0x1C, 0x2E, 0xF1, 0x97, 0xBE, 0xD2, 0x4F, 0xFA, 0x04, 0xBE, 0xD0, 0x0F, 0xFA, 0x00, 0x3E, 0x0E, 0x0B, 0x17, 0x5A, + 0xAA, 0x20, 0x64, 0xFE, 0xFF, 0x62, 0xF1, 0x0F, 0xFF, 0xA8, 0xFF, 0xF4, 0x0D, 0x7C, 0xA4, 0x7F, 0xF4, 0x8D, 0x7E, 0xD1, 0xF7, 0xF9, 0xE3, 0x5F, + 0x03, 0x13, 0xF3, 0x59, 0x9C, 0x0C, 0xD1, 0xA0, 0xE0, 0xCD, 0xE8, 0xB1, 0xF5, 0x67, 0xDF, 0x9F, 0xCF, 0x07, 0x6E, 0xFA, 0x67, 0x7E, 0xCB, 0x50, + 0xEE, 0xC6, 0x5B, 0xFB, 0x7F, 0x98, 0xE5, 0x1B, 0xA8, 0x11, 0x6F, 0x01, 0x3F, 0x54, 0x79, 0x17, 0xED, 0xA8, 0x77, 0x36, 0xCD, 0xC0, 0x9F, 0xC7, + 0x6C, 0xDD, 0x16, 0x3C, 0x6E, 0xA7, 0x1D, 0xB3, 0xA1, 0xA7, 0x26, 0xF0, 0x9C, 0xE4, 0xE2, 0x5E, 0x92, 0xB3, 0xC1, 0x7C, 0xDC, 0x62, 0x4B, 0x67, + 0x0A, 0xA0, 0xB7, 0x5D, 0xE2, 0x79, 0x85, 0xED, 0xCD, 0xE7, 0xC1, 0xE6, 0xE1, 0x72, 0xEF, 0xC0, 0x3A, 0x67, 0x32, 0xE1, 0x37, 0x6B, 0x6C, 0xFE, + 0x85, 0x9F, 0xED, 0xB3, 0xBF, 0x91, 0xCC, 0x6F, 0x70, 0xA6, 0xB5, 0x78, 0x22, 0xDA, 0x26, 0xDF, 0xBC, 0xD5, 0xED, 0xF9, 0xD6, 0xE3, 0x1F, 0xD8, + 0x9D, 0x4B, 0xF4, 0xF2, 0x23, 0xA0, 0x6C, 0x42, 0x01, 0xFC, 0xE4, 0xA1, 0x2B, 0xEC, 0xFF, 0xBF, 0x28, 0xFC, 0x9B, 0x3E, 0xD3, 0x1F, 0xDA, 0xE9, + 0xF9, 0x3F, 0xA5, 0x7A, 0x68, 0xA4, 0x2F, 0x34, 0x8C, 0x12, 0x6E, 0x10, 0x05, 0x82, 0xC2, 0xAB, 0x94, 0x42, 0x06, 0x12, 0x59, 0xE4, 0xCF, 0xF2, + 0x5F, 0x58, 0x74, 0x7B, 0x29, 0x3A, 0x8D, 0xE9, 0x5E, 0xB6, 0x04, 0xEB, 0xF9, 0xAA, 0xFF, 0xFF, 0x6F, 0x61, 0xB1, 0xD2, 0xF2, 0xD4, 0x21, 0x12, + 0x01, 0x84, 0xD6, 0x71, 0x4D, 0xC7, 0xE8, 0x74, 0x67, 0x79, 0x60, 0x2F, 0xB4, 0x49, 0xD0, 0x95, 0x09, 0xB1, 0xC9, 0x52, 0x24, 0xAF, 0x01, 0x4E, + 0x18, 0x25, 0x55, 0x9E, 0x24, 0x25, 0x47, 0x54, 0xA0, 0x90, 0x5C, 0xB6, 0xCA, 0x05, 0x6A, 0x7A, 0x8E, 0x82, 0x90, 0x8C, 0x98, 0xA4, 0x8A, 0xAA, + 0xBA, 0x08, 0x90, 0xA9, 0x0D, 0x80, 0x93, 0x40, 0x63, 0xE8, 0x40, 0x94, 0x77, 0x62, 0xFC, 0x57, 0x1E, 0x3F, 0xE2, 0x75, 0xEE, 0xE8, 0x37, 0x62, + 0xEF, 0x0B, 0xD0, 0xA8, 0x9F, 0x01, 0x51, 0x7C, 0xFA, 0x8C, 0xA0, 0x8A, 0xBD, 0xAD, 0x86, 0xDF, 0xC3, 0xA5, 0xD0, 0x50, 0xC3, 0x4D, 0xB8, 0xFA, + 0x01, 0xD7, 0x93, 0x92, 0xFC, 0x1F, 0x66, 0x94, 0x89, 0xE5, 0xFF, 0xFF, 0x79, 0x6B, 0x2D, 0x34, 0xD6, 0xE3, 0xCC, 0xB2, 0x0A, 0x80, 0x49, 0x76, + 0xBA, 0xBC, 0xDD, 0x90, 0x13, 0x75, 0x40, 0x54, 0x13, 0x70, 0x03, 0x70, 0x01, 0xD2, 0xF1, 0x60, 0xD0, 0x43, 0x18, 0x45, 0xC0, 0xE2, 0xBC, 0xF5, + 0xBC, 0xBB, 0x43, 0x1B, 0x8D, 0x8A, 0x13, 0x87, 0x63, 0x87, 0xFB, 0xA5, 0x00, 0x47, 0xBA, 0xF5, 0xE0, 0xC8, 0xFC, 0x56, 0x65, 0xF0, 0x2D, 0x88, + 0x58, 0xA7, 0x22, 0x45, 0xB7, 0x35, 0x70, 0xA3, 0xA7, 0x1C, 0xA1, 0xDB, 0x76, 0xD4, 0x4E, 0xFC, 0x70, 0xD0, 0xA0, 0xAF, 0xCA, 0x15, 0x1B, 0xD3, + 0x94, 0x8F, 0x96, 0xB5, 0x3C, 0x7E, 0x4D, 0x00, 0x9C, 0x1A, 0xD9, 0x1A, 0xAC, 0x17, 0x0D, 0x64, 0x40, 0x83, 0xA9, 0x11, 0x18, 0x3C, 0xA4, 0xB9, + 0x10, 0xB2, 0x86, 0xA2, 0xE5, 0xA1, 0xB0, 0x53, 0xD4, 0x11, 0xDC, 0xA0, 0xED, 0x98, 0xCA, 0xB3, 0x41, 0x5D, 0x37, 0x83, 0xFA, 0x2B, 0xA3, 0x41, + 0x49, 0x06, 0x41, 0xC9, 0x18, 0xAC, 0x59, 0xCC, 0x85, 0xAA, 0x58, 0xD1, 0x0F, 0x58, 0x25, 0x01, 0xB0, 0xAC, 0x25, 0x43, 0x3D, 0x1B, 0x7E, 0x82, + 0x24, 0x7E, 0xA4, 0x43, 0x1C, 0x15, 0x2D, 0x7F, 0x11, 0xC5, 0x06, 0x14, 0xAB, 0x66, 0xEE, 0x8B, 0x86, 0x49, 0xCD, 0xEF, 0xCC, 0x70, 0x61, 0x2F, + 0xD7, 0x84, 0x62, 0xE5, 0xA8, 0x03, 0x39, 0x17, 0xE9, 0x35, 0x0A, 0xF1, 0xD4, 0xF0, 0xA9, 0x6F, 0xC5, 0xCD, 0x7F, 0xE0, 0x01, 0xF2, 0x19, 0x68, + 0xBD, 0x83, 0xFF, 0xB7, 0xFC, 0xFF, 0xBF, 0xBE, 0x38, 0x1C, 0x16, 0xFC, 0x39, 0x3F, 0xE8, 0x0B, 0xAD, 0x4D, 0x58, 0x80, 0x4A, 0x27, 0xD6, 0xAB, + 0x19, 0xA1, 0x68, 0xE0, 0x6A, 0xD9, 0xE0, 0x87, 0x5F, 0x83, 0x0F, 0x54, 0x88, 0xBE, 0x64, 0x7E, 0x6D, 0x64, 0x73, 0x97, 0x63, 0xAE, 0xE1, 0x5B, + 0xCC, 0x85, 0x90, 0xB0, 0xBE, 0x16, 0x2C, 0x08, 0x00, 0x30, 0x00, 0x62, 0x2F, 0xDE, 0x82, 0x17, 0x02, 0xBA, 0x2B, 0xE7, 0x0A, 0x17, 0xDA, 0xD5, + 0x56, 0xD5, 0x9A, 0x16, 0x07, 0xB8, 0x2C, 0x38, 0x0F, 0x67, 0x32, 0xE0, 0x08, 0x1E, 0xCB, 0x83, 0x83, 0xAB, 0xC5, 0x38, 0xAB, 0x18, 0x1F, 0xAA, + 0x3E, 0xF3, 0x18, 0x40, 0xC6, 0x58, 0xC0, 0xE4, 0x86, 0x02, 0x54, 0x47, 0x05, 0x31, 0x94, 0x53, 0x52, 0x4A, 0xED, 0xA9, 0xA4, 0xD5, 0x5B, 0x6A, + 0xE6, 0x31, 0x0A, 0x4B, 0x08, 0x94, 0x81, 0x73, 0x02, 0xB9, 0x23, 0x4F, 0x62, 0xB9, 0x08, 0x4C, 0x50, 0xA9, 0x2F, 0xD4, 0x9C, 0x9D, 0x74, 0x1D, + 0xFE, 0xC0, 0x5F, 0x62, 0x93, 0x26, 0x2D, 0x93, 0x0F, 0x0F, 0x4B, 0xB3, 0x8D, 0x06, 0x8C, 0x60, 0x19, 0xF8, 0x24, 0x92, 0x6C, 0x87, 0x24, 0x09, + 0xED, 0xF6, 0x48, 0x2F, 0x7D, 0x05, 0xAA, 0x8A, 0x1C, 0x1D, 0x35, 0x30, 0x8A, 0x80, 0x54, 0xC4, 0x45, 0x13, 0x18, 0x01, 0x37, 0x60, 0x0E, 0x07, + 0x4D, 0xE3, 0x08, 0x51, 0x6E, 0x37, 0x34, 0x29, 0x54, 0x08, 0x15, 0xA4, 0x8A, 0xE3, 0x40, 0x98, 0xFF, 0x67, 0x4A, 0x95, 0xCF, 0x2B, 0x09, 0x1F, + 0xFC, 0x50, 0x80, 0x2F, 0x98, 0x00, 0x3D, 0x06, 0x8F, 0x4F, 0x80, 0x9D, 0x33, 0x3B, 0xC0, 0xD4, 0xAC, 0x8E, 0x06, 0xA1, 0x9C, 0x35, 0x38, 0x00, + 0x2D, 0x6A, 0x8C, 0xEF, 0x92, 0x70, 0x7A, 0xD0, 0xDD, 0x04, 0x52, 0xB1, 0x01, 0x45, 0x50, 0xE3, 0x48, 0x84, 0xC6, 0xF6, 0xCA, 0x4C, 0x69, 0xD5, + 0x8C, 0x0B, 0x5E, 0x9F, 0x2B, 0x13, 0x63, 0xD4, 0x05, 0x53, 0xA3, 0xFA, 0xB2, 0x05, 0xA0, 0x77, 0x59, 0xD2, 0xD1, 0x32, 0x27, 0xCB, 0x12, 0x26, + 0x24, 0x36, 0xE1, 0x86, 0x87, 0xED, 0x9A, 0x7C, 0xC2, 0xA3, 0x60, 0xBE, 0x49, 0x60, 0xCB, 0x28, 0x57, 0x76, 0xA0, 0xF8, 0x7D, 0xC5, 0xB4, 0xA2, + 0x79, 0xC5, 0xB7, 0x57, 0x41, 0xEF, 0xAC, 0x91, 0xDE, 0x9E, 0x97, 0x2A, 0xC1, 0x5D, 0x55, 0xA4, 0x4E, 0x77, 0x87, 0x00, 0x50, 0x59, 0x2B, 0x9C, + 0xC2, 0xE0, 0x4F, 0x8A, 0x87, 0x9F, 0x28, 0x6F, 0x6E, 0xBA, 0x80, 0x4B, 0x7A, 0x11, 0x47, 0x39, 0x94, 0x1D, 0x12, 0xCF, 0xA5, 0x11, 0x05, 0xB7, + 0xD4, 0xC4, 0xAD, 0x64, 0x86, 0xFF, 0xE4, 0xD6, 0xB7, 0x57, 0xBD, 0x9D, 0xD0, 0x08, 0x61, 0x32, 0xA5, 0x89, 0x89, 0x94, 0x84, 0xFB, 0x94, 0x05, + 0xA3, 0x6D, 0xBA, 0x77, 0x9F, 0x79, 0x88, 0x28, 0x85, 0x51, 0x47, 0xDA, 0x39, 0x65, 0xF4, 0xC7, 0xAC, 0x28, 0x12, 0xA5, 0xD0, 0x50, 0x20, 0xB7, + 0x11, 0x4F, 0x26, 0xA0, 0xBF, 0x50, 0x78, 0x5F, 0x12, 0xE7, 0xCF, 0x18, 0x91, 0x7C, 0x89, 0xCB, 0x32, 0x1F, 0xFF, 0xFF, 0x99, 0xFF, 0x61, 0x98, + 0xFC, 0x1F, 0x44, 0xFF, 0x13, 0xE2, 0xB0, 0x6E, 0x1F, 0x1C, 0xAC, 0x4B, 0x27, 0x63, 0x37, 0x37, 0x1E, 0x2C, 0xB3, 0xFC, 0xFF, 0xC5, 0x2D, 0x61, + 0x71, 0x4B, 0xD8, 0xA5, 0x45, 0x1C, 0x66, 0x41, 0xF0, 0xFF, 0xCD, 0xF8, 0x8B, 0x61, 0x77, 0xCE, 0xE4, 0x7E, 0x00, 0xB9, 0x3C, 0x9D, 0x3A, 0xAD, + 0x3C, 0x07, 0xC4, 0x16, 0x3E, 0xA6, 0xE0, 0x36, 0xC6, 0xC1, 0x7B, 0xBB, 0x61, 0xE4, 0xB2, 0x29, 0x3D, 0xC2, 0xB2, 0xF5, 0xD9, 0x7F, 0x3C, 0x22, + 0x52, 0xE0, 0xF6, 0x5E, 0x09, 0x29, 0xEA, 0x4F, 0x1D, 0x83, 0x39, 0x35, 0x33, 0x32, 0xED, 0xC0, 0x73, 0x66, 0xF1, 0x7F, 0x69, 0x20, 0xD4, 0xD8, + 0x55, 0x46, 0xF7, 0x6C, 0x1B, 0x92, 0x77, 0x06, 0x53, 0xBE, 0x20, 0x46, 0x9D, 0x38, 0x1C, 0x41, 0xA6, 0xE3, 0x40, 0x8E, 0x61, 0x5E, 0xA3, 0xDF, + 0x3B, 0x0A, 0x75, 0x03, 0x84, 0x7E, 0xC0, 0x39, 0xCC, 0x60, 0x83, 0x96, 0x80, 0x47, 0x98, 0x70, 0x1C, 0x5F, 0x78, 0x3D, 0xE0, 0x6D, 0x9F, 0x5D, + 0x00, 0xC5, 0x61, 0xF4, 0x5F, 0xFE, 0xFF, 0x04, 0xFE, 0xFF, 0x61, 0xF1, 0x4B, 0x38, 0x2E, 0x0C, 0xF7, 0xA7, 0x3E, 0xD4, 0x4F, 0xF0, 0x0B, 0x7C, + 0xA0, 0xCF, 0xF3, 0x77, 0xBE, 0xCD, 0xA7, 0xF9, 0x2F, 0xDF, 0xE5, 0x07, 0x07, 0xFF, 0x52, 0x05, 0x2E, 0x09, 0x04, 0x0B, 0x0E, 0x0E, 0x0E, 0xFE, + 0x37, 0xFF, 0x7F, 0xF1, 0xFF, 0x3F, 0xFC, 0xC5, 0xA4, 0x94, 0xFF, 0x43, 0x39, 0xF8, 0x3F, 0x3C, 0xE5, 0xFF, 0xCF, 0xF3, 0x7F, 0xD8, 0xEE, 0x7F, + 0xB1, 0x89, 0xBB, 0x58, 0x0F, 0xA8, 0x9C, 0xE1, 0x4D, 0x91, 0x13, 0x21, 0x65, 0xC3, 0x8C, 0x28, 0x26, 0x68, 0xE8, 0xC2, 0x88, 0xEC, 0x0B, 0x25, + 0xEF, 0x53, 0x3C, 0x13, 0xF5, 0x96, 0xEC, 0x57, 0x27, 0x79, 0x54, 0x27, 0xBB, 0xA9, 0x4E, 0x5E, 0x74, 0x9B, 0xF0, 0x44, 0xE1, 0x71, 0xFA, 0x31, + 0xDF, 0xD4, 0x1F, 0xE0, 0x6B, 0xE3, 0x00, 0x24, 0x09, 0x4B, 0xA2, 0xD5, 0x73, 0x9A, 0xAD, 0xE0, 0xC5, 0x13, 0x96, 0x21, 0xF1, 0xA3, 0x93, 0xC5, + 0xBC, 0x10, 0xCB, 0x03, 0xC5, 0x0D, 0x2B, 0xEB, 0xF7, 0x6A, 0x9E, 0xE0, 0x0A, 0xAC, 0x88, 0xF3, 0x29, 0x5A, 0x77, 0x58, 0xAC, 0xD2, 0x99, 0xAA, + 0x6B, 0x9D, 0xA9, 0xE4, 0x58, 0xD4, 0x0F, 0xB5, 0x46, 0x58, 0x15, 0x18, 0xB1, 0x21, 0x7F, 0xF2, 0x3A, 0xB8, 0x3F, 0x9B, 0xE2, 0x3C, 0x71, 0x16, + 0x74, 0x96, 0xE5, 0xE1, 0x9C, 0x4E, 0x6D, 0x9B, 0x74, 0x72, 0x45, 0xCE, 0x40, 0x68, 0xEE, 0x95, 0x20, 0xB2, 0x41, 0xD0, 0x8C, 0x88, 0x23, 0x13, + 0xC6, 0x23, 0xE6, 0xA6, 0x90, 0xCB, 0xC6, 0x33, 0x6F, 0x59, 0x7B, 0xA4, 0xB2, 0x33, 0xB2, 0xB2, 0x85, 0x4A, 0xBF, 0xE4, 0x54, 0x08, 0x50, 0x2B, + 0xE5, 0x4B, 0x1F, 0x84, 0x41, 0x95, 0x49, 0xA4, 0x1B, 0x49, 0xB8, 0x9D, 0xC8, 0x2F, 0x26, 0xC8, 0xBC, 0x04, 0x8F, 0x76, 0x62, 0xB7, 0xE3, 0x20, + 0x1A, 0xBE, 0xEB, 0x35, 0x35, 0x42, 0x42, 0x01, 0x21, 0x1C, 0x63, 0x44, 0x4D, 0xFB, 0x81, 0x59, 0xC1, 0x87, 0x14, 0xBC, 0x87, 0x6C, 0x5F, 0x0F, + 0x9D, 0x17, 0xFD, 0x0E, 0xFE, 0x47, 0xEF, 0x77, 0x1B, 0xE9, 0x15, 0x6F, 0x0B, 0x40, 0xF0, 0x14, 0x4D, 0x78, 0x5A, 0x7C, 0xF4, 0xFE, 0xF8, 0x93, + 0x7B, 0x08, 0xEF, 0xCB, 0x8D, 0xFF, 0x7F, 0x98, 0x97, 0x96, 0x97, 0xF0, 0xFF, 0x3B, 0x3D, 0x30, 0x31, 0xA4, 0x9F, 0x65, 0x56, 0x40, 0xD5, 0xEA, + 0x73, 0x03, 0x20, 0xDB, 0x34, 0x3B, 0x17, 0xAD, 0xCA, 0xC5, 0xE6, 0x50, 0x63, 0x9F, 0xC1, 0x15, 0x99, 0xF5, 0x41, 0x29, 0xCB, 0xCB, 0x22, 0x5B, + 0xB4, 0x19, 0x1B, 0x94, 0x23, 0x8B, 0x7D, 0x2E, 0x85, 0x41, 0x6A, 0x81, 0x31, 0x09, 0x3B, 0xA3, 0xDE, 0xE6, 0x18, 0x3B, 0x3D, 0xB3, 0xCE, 0xE0, + 0x16, 0xD8, 0xE1, 0x59, 0xCB, 0xCB, 0xC9, 0x9C, 0x45, 0xB6, 0xBB, 0x54, 0x0E, 0x9A, 0x59, 0xB2, 0xAD, 0xA2, 0xC5, 0xEC, 0x0C, 0x6B, 0x09, 0x20, + 0xE1, 0x0D, 0x28, 0xA2, 0xA6, 0x83, 0x40, 0x07, 0x00, 0xBF, 0xA6, 0x1D, 0x00, 0xD6, 0xD8, 0xB8, 0x75, 0x15, 0x47, 0xAE, 0xC2, 0x8D, 0xAD, 0x09, + 0x1B, 0x13, 0x7D, 0x36, 0x3F, 0x39, 0xF5, 0xAB, 0x87, 0x37, 0xB3, 0x3A, 0xA6, 0xB3, 0x30, 0x09, 0x89, 0x6B, 0x0D, 0x98, 0x19, 0x40, 0x2D, 0x9D, + 0x0B, 0xF4, 0x16, 0x07, 0x2C, 0xF6, 0xCF, 0x9A, 0xF6, 0x67, 0x7C, 0xB8, 0xB2, 0x0B, 0xE0, 0x17, 0xF8, 0xAA, 0xF6, 0x05, 0xAA, 0xA2, 0xD3, 0x01, + 0x57, 0xC5, 0x2A, 0x8D, 0xDB, 0x25, 0x76, 0x1B, 0x25, 0xEA, 0x96, 0x46, 0xC5, 0xA8, 0x8C, 0xBA, 0xA4, 0xD1, 0xA1, 0xCE, 0x16, 0x85, 0xA0, 0x4C, + 0x86, 0x40, 0x85, 0x86, 0x3E, 0x63, 0xAA, 0x7A, 0xD2, 0x3A, 0x89, 0x88, 0x76, 0x08, 0x6F, 0x54, 0x58, 0x56, 0x25, 0xF0, 0xA8, 0xE4, 0x1E, 0x10, + 0x11, 0x7C, 0x60, 0x36, 0x21, 0x22, 0x52, 0x04, 0x65, 0x21, 0x8D, 0x54, 0x1A, 0x96, 0x99, 0x92, 0x95, 0x53, 0x24, 0x19, 0x21, 0x41, 0xFC, 0x28, + 0xF2, 0x47, 0xE9, 0xE3, 0x74, 0xAB, 0x07, 0xAD, 0x1B, 0x15, 0x0F, 0x5D, 0x9C, 0xC5, 0xE0, 0xD7, 0x3E, 0xC8, 0xB5, 0xFF, 0x0D, 0xA7, 0x5C, 0xFD, + 0xCF, 0x22, 0x64, 0x2F, 0xC2, 0xD5, 0x17, 0x2A, 0x42, 0x69, 0xDC, 0xCC, 0xCD, 0xE4, 0xC4, 0xED, 0x54, 0xE4, 0x57, 0x17, 0x77, 0x3B, 0x9B, 0xF0, + 0xD4, 0x2A, 0x49, 0xFA, 0xAD, 0x8D, 0x63, 0x79, 0x35, 0x3B, 0x70, 0x05, 0xB6, 0xB1, 0x1A, 0x6A, 0x36, 0xC2, 0x13, 0xF7, 0x89, 0xDB, 0xF8, 0x09, + 0xA6, 0xEB, 0x69, 0x33, 0xDA, 0x89, 0xF3, 0x72, 0xEA, 0xB8, 0xD8, 0xC4, 0xA1, 0xA3, 0x48, 0x02, 0x1F, 0x9A, 0x95, 0x66, 0x22, 0x0D, 0x40, 0xCD, + 0xAF, 0xB2, 0x99, 0x1E, 0x6C, 0x31, 0xAD, 0xFC, 0x5E, 0x0A, 0xBA, 0x5C, 0x06, 0xFD, 0x54, 0x46, 0x56, 0x56, 0x51, 0x9D, 0x27, 0xED, 0xB8, 0x21, + 0x5C, 0x4B, 0x4F, 0xF3, 0x0F, 0xF4, 0x34, 0x87, 0xB6, 0x6D, 0x99, 0xFF, 0xCE, 0x48, 0xE4, 0x37, 0xBB, 0x8B, 0x8C, 0x67, 0x4B, 0x20, 0xCF, 0xB0, + 0xBD, 0xCC, 0x74, 0x66, 0x14, 0x66, 0xDD, 0x8C, 0x69, 0x7E, 0xD1, 0x3D, 0xEB, 0x20, 0xE3, 0x9C, 0xCD, 0x1D, 0x64, 0xC9, 0x81, 0xBF, 0x3F, 0x54, + 0x0A, 0xCC, 0x06, 0x1C, 0x7C, 0x30, 0x2F, 0x9E, 0x63, 0x98, 0xD7, 0x3A, 0xCF, 0x1F, 0x6A, 0x75, 0x34, 0x48, 0x30, 0x03, 0x22, 0x11, 0x90, 0xA4, + 0x7A, 0xFE, 0x4C, 0x44, 0x5F, 0xF1, 0xB0, 0xE6, 0x15, 0x44, 0x8A, 0xB3, 0xAB, 0x8D, 0x9D, 0xD2, 0x20, 0x6C, 0xA5, 0xE2, 0x4E, 0x50, 0x27, 0xB1, + 0xA0, 0x6B, 0xBA, 0x3C, 0x0D, 0x28, 0x9B, 0x53, 0x76, 0xFF, 0x5C, 0x7B, 0x89, 0xAB, 0x05, 0x20, 0x60, 0xEE, 0x80, 0xDB, 0xCB, 0x85, 0xE3, 0xF2, + 0x7A, 0x20, 0x5B, 0xE4, 0x6D, 0x47, 0x8D, 0x1B, 0xF0, 0x6C, 0x1B, 0xA7, 0x81, 0xB6, 0xA1, 0x60, 0x5A, 0x09, 0x58, 0xCD, 0x16, 0x45, 0x5B, 0xFF, + 0xD4, 0x7E, 0x26, 0x1F, 0xBF, 0x48, 0x0B, 0xBE, 0x91, 0x2B, 0x1E, 0x8B, 0x64, 0x2E, 0x44, 0x98, 0x84, 0x9F, 0x36, 0xF4, 0x75, 0x54, 0xFD, 0x6F, + 0x85, 0x58, 0x63, 0xD5, 0x0C, 0x1F, 0xA4, 0x91, 0x2F, 0xF8, 0x30, 0xAF, 0x9D, 0x96, 0x50, 0x3C, 0xBB, 0x06, 0x32, 0xE0, 0xE1, 0x27, 0x57, 0xDE, + 0x27, 0xB4, 0xE4, 0x22, 0x6A, 0x09, 0xB3, 0x34, 0xF6, 0x04, 0x11, 0xB9, 0xA9, 0x69, 0xC8, 0xB7, 0xB0, 0xB4, 0xDA, 0xCC, 0x4F, 0xA0, 0x50, 0x96, + 0xB6, 0x62, 0x79, 0x03, 0x44, 0x94, 0xE6, 0x00, 0xE9, 0x48, 0x34, 0x1D, 0x04, 0xAE, 0x7C, 0xEA, 0x06, 0x00, 0x68, 0xBC, 0xBC, 0x0C, 0xE2, 0xD5, + 0xD6, 0xDD, 0x7D, 0xD9, 0x04, 0xC6, 0x84, 0x89, 0x6E, 0x2C, 0xBB, 0x47, 0x21, 0x4F, 0x29, 0x84, 0x18, 0x84, 0x58, 0xE9, 0xDB, 0x00, 0x2C, 0x20, + 0xF8, 0x78, 0x2E, 0xE7, 0x96, 0x4F, 0xFB, 0x1B, 0x26, 0xB1, 0x71, 0xD3, 0x58, 0x85, 0xBE, 0x1D, 0xE5, 0x22, 0xAC, 0x39, 0x23, 0xF0, 0x1D, 0x1C, + 0x8C, 0xFC, 0x97, 0xF8, 0x11, 0x1C, 0x1C, 0x64, 0x13, 0xC7, 0xE0, 0x11, 0x8B, 0x7D, 0x44, 0x98, 0x84, 0xC6, 0xCB, 0x44, 0xBD, 0x55, 0xCE, 0x43, + 0x30, 0x86, 0x7A, 0x36, 0x3D, 0x83, 0x71, 0x3C, 0x71, 0x98, 0x3A, 0x89, 0x9A, 0xCC, 0x9C, 0x4E, 0x62, 0x9B, 0x5D, 0x49, 0x8A, 0x94, 0x8A, 0xFD, + 0xDD, 0x91, 0x53, 0x8B, 0x2C, 0xC7, 0xE6, 0xA0, 0x5C, 0xD5, 0x54, 0xCA, 0xA1, 0x19, 0xB5, 0xFB, 0x9D, 0x83, 0xFF, 0x6F, 0xFC, 0x18, 0x37, 0x86, + 0xCA, 0x4A, 0x3D, 0xA8, 0x88, 0x32, 0xF8, 0x91, 0xA0, 0x1D, 0x3C, 0x00, 0x10, 0x3D, 0x54, 0xF9, 0x20, 0x3E, 0x61, 0x05, 0x95, 0x84, 0x04, 0x3A, + 0x93, 0xDE, 0x7A, 0xE2, 0xDC, 0xF9, 0xE5, 0x9D, 0xFF, 0xC5, 0xE1, 0x2F, 0xE4, 0xC3, 0x88, 0xA1, 0x89, 0x88, 0x62, 0x30, 0x14, 0xFB, 0x9F, 0xA5, + 0x85, 0x7F, 0x31, 0xCB, 0x35, 0x00, 0x29, 0x95, 0xE3, 0x6B, 0x46, 0x5D, 0x0E, 0x84, 0x06, 0x76, 0x72, 0x80, 0xFF, 0x7F, 0x98, 0x85, 0xC7, 0xD1, + 0x07, 0xD2, 0x64, 0x67, 0xC5, 0xB7, 0x7E, 0x05, 0x36, 0x82, 0xFD, 0x23, 0xEE, 0x62, 0xF3, 0x2D, 0xF7, 0x15, 0x9C, 0x05, 0x07, 0x16, 0x87, 0xE0, + 0x7B, 0x40, 0x4B, 0x47, 0xD7, 0x1C, 0x28, 0xA5, 0x1E, 0x03, 0x9F, 0xC5, 0xC3, 0x97, 0xF1, 0xF9, 0x80, 0x18, 0x01, 0x81, 0x4F, 0x91, 0xEB, 0x33, + 0xE5, 0x85, 0x80, 0x8B, 0x5B, 0xBE, 0x8B, 0xAF, 0xE2, 0x87, 0x68, 0x5C, 0x10, 0xEE, 0xFB, 0x34, 0x80, 0x83, 0x15, 0x83, 0x79, 0x60, 0xCA, 0x20, + 0x27, 0x30, 0x9F, 0x49, 0x14, 0x1E, 0x07, 0x04, 0xF2, 0x02, 0xFF, 0x7F, 0x1A, 0xBC, 0x36, 0xE1, 0x17, 0x3F, 0xC5, 0xE3, 0x67, 0xDA, 0x07, 0x56, + 0xB4, 0x2A, 0x9D, 0x10, 0xBC, 0x59, 0xA6, 0x59, 0x99, 0x0A, 0x3F, 0x32, 0xD1, 0x2A, 0x63, 0x83, 0x19, 0xC4, 0x92, 0xBC, 0x86, 0x41, 0xDA, 0x61, + 0x2C, 0xBD, 0xB0, 0x3C, 0x6C, 0x23, 0x01, 0xF6, 0xDF, 0x99, 0x5C, 0xF7, 0x20, 0xD2, 0x3E, 0xB5, 0x25, 0x51, 0xAF, 0x04, 0xB1, 0x85, 0x54, 0x6D, + 0xB2, 0x5B, 0xD3, 0xD9, 0x41, 0xD5, 0x79, 0xEC, 0x1C, 0xE2, 0xED, 0xEC, 0x64, 0xE3, 0x9D, 0xCB, 0xEC, 0x19, 0xE7, 0x0B, 0x76, 0xA4, 0x9F, 0x7D, + 0xAA, 0x68, 0x0B, 0xA0, 0x35, 0xB3, 0x38, 0x1F, 0xE8, 0x24, 0x17, 0x25, 0x38, 0x6F, 0xA1, 0xBD, 0x45, 0xD9, 0x23, 0x1A, 0xD8, 0xAF, 0x33, 0x56, + 0x92, 0x80, 0xBC, 0x18, 0xD0, 0x9D, 0x1F, 0xA0, 0x38, 0x23, 0x10, 0x3D, 0xA3, 0x39, 0x39, 0x97, 0xC0, 0x41, 0x7E, 0xD4, 0x61, 0x53, 0x30, 0x8F, + 0xD9, 0x7C, 0xE6, 0x0A, 0xB6, 0x05, 0xF2, 0xEF, 0x8B, 0xA9, 0x42, 0xC2, 0xBC, 0xC3, 0xE6, 0x01, 0x23, 0x17, 0xB1, 0xA3, 0x02, 0x98, 0x67, 0x6D, + 0x5A, 0xF2, 0xE6, 0x99, 0xF3, 0xD3, 0xA0, 0x0D, 0x56, 0x4C, 0x02, 0x4C, 0xF9, 0xAF, 0xF4, 0x51, 0xE0, 0xE4, 0x71, 0x56, 0xE4, 0x7B, 0x65, 0xD0, + 0x9B, 0xA4, 0x41, 0xAF, 0x15, 0x57, 0x95, 0xE0, 0x12, 0xB7, 0xCB, 0x39, 0x15, 0xE8, 0x08, 0xEA, 0x21, 0xEE, 0x29, 0x03, 0x2A, 0xA6, 0x9B, 0x34, + 0xA2, 0x83, 0x67, 0xA2, 0x46, 0x13, 0x6C, 0xCB, 0x0C, 0x46, 0x18, 0x80, 0x7D, 0xA2, 0xA2, 0x7D, 0x3E, 0xA8, 0x3B, 0x3F, 0xDE, 0x39, 0xA2, 0x0A, + 0xDA, 0x3F, 0x22, 0x68, 0xE6, 0x49, 0x94, 0x30, 0xB2, 0x5F, 0x1C, 0x0E, 0xA7, 0xF8, 0x28, 0x36, 0xEF, 0xA4, 0x0A, 0x67, 0xB2, 0xBB, 0xCC, 0xB6, + 0x70, 0x5F, 0xB0, 0x57, 0xDC, 0x7E, 0x76, 0xE2, 0x8D, 0x79, 0xE6, 0xE4, 0x61, 0xCF, 0x49, 0x78, 0xDF, 0x33, 0x52, 0x98, 0xEF, 0x39, 0x13, 0xF1, + 0x49, 0xC2, 0x7E, 0x68, 0xF5, 0x06, 0xF8, 0xB1, 0x61, 0x42, 0xB8, 0x79, 0xDC, 0xC9, 0x92, 0xB8, 0xF9, 0x0F, 0xAD, 0x3B, 0x79, 0xAC, 0x3F, 0xC6, + 0x91, 0xA8, 0x1B, 0xAB, 0xB4, 0xA3, 0xEA, 0x1A, 0x65, 0x2A, 0xB9, 0x14, 0xF5, 0xC3, 0x2A, 0x6F, 0xA4, 0xF5, 0x36, 0xC2, 0x63, 0x7F, 0xF6, 0xEA, + 0xED, 0x9F, 0x52, 0x1A, 0xCC, 0x9E, 0xE3, 0x08, 0x9D, 0x1D, 0xAA, 0x38, 0xEB, 0x7C, 0x33, 0x03, 0x5E, 0xC4, 0x26, 0xD4, 0x74, 0xF1, 0x82, 0x48, + 0xDC, 0x97, 0x19, 0xD5, 0x36, 0xB3, 0xEC, 0x8A, 0x19, 0x25, 0xF2, 0x32, 0x07, 0x82, 0xCB, 0x0F, 0xA1, 0xD2, 0x94, 0xA7, 0xAE, 0x6C, 0x09, 0x15, + 0x62, 0xDB, 0x2B, 0xC4, 0xA8, 0x89, 0x99, 0x32, 0x46, 0x40, 0x10, 0x33, 0xAD, 0xC9, 0x1D, 0xB3, 0xA4, 0x86, 0x3B, 0xD2, 0xA8, 0x12, 0xF2, 0x85, + 0xDE, 0xE3, 0x18, 0xBE, 0x3B, 0x02, 0x19, 0xDC, 0x4D, 0xAC, 0x35, 0x5A, 0x76, 0x01, 0xF9, 0x03, 0x66, 0xCC, 0x65, 0xFD, 0x91, 0x09, 0xE8, 0xE3, + 0xF7, 0x44, 0x8F, 0xD1, 0x05, 0x3D, 0x82, 0x0A, 0x17, 0x61, 0xA1, 0x16, 0xAB, 0x2E, 0x13, 0x7F, 0xD3, 0x76, 0xDD, 0xBC, 0x03, 0x88, 0x08, 0x71, + 0x04, 0xDB, 0x61, 0xED, 0xA8, 0x23, 0x87, 0x00, 0x32, 0x8E, 0x55, 0x9D, 0xFA, 0x46, 0xAD, 0xAC, 0xD9, 0x87, 0xFE, 0xC5, 0x5F, 0x31, 0xCC, 0x12, + 0xCA, 0x05, 0x2A, 0x42, 0x49, 0x95, 0x6C, 0xC2, 0xC5, 0xB2, 0x46, 0x2B, 0x09, 0x72, 0xA0, 0x10, 0x4E, 0xDF, 0x24, 0x85, 0xE0, 0x60, 0xC9, 0x24, + 0x0E, 0x4B, 0x78, 0x7E, 0xCF, 0x93, 0x88, 0x5A, 0x3D, 0x05, 0xDA, 0x7C, 0x18, 0x11, 0x8D, 0xA9, 0xA5, 0x7D, 0xC2, 0x39, 0xC9, 0xE5, 0x4B, 0x6B, + 0x7D, 0x98, 0xF0, 0x60, 0x46, 0x67, 0x9C, 0x61, 0xD6, 0x7E, 0x14, 0xE6, 0x70, 0x02, 0xDB, 0x42, 0x29, 0x0E, 0x0E, 0xBE, 0xB4, 0x59, 0xF0, 0xA8, + 0xBD, 0x15, 0x29, 0xF8, 0xE7, 0x0D, 0x00, 0xFF, 0xB0, 0xE4, 0x76, 0xC2, 0x67, 0xB4, 0xDC, 0x65, 0x38, 0x81, 0xFB, 0x02, 0x4E, 0xBE, 0xFD, 0x70, + 0xDC, 0x22, 0x1E, 0x43, 0x9F, 0xC5, 0xA3, 0xB2, 0xE8, 0x63, 0xE0, 0x44, 0x02, 0x0F, 0xD5, 0x30, 0x49, 0xCA, 0x60, 0x5E, 0x5A, 0x5C, 0x48, 0x3F, + 0x49, 0x05, 0xE1, 0x64, 0xEC, 0xDA, 0x58, 0x77, 0x00, 0x3B, 0x2B, 0x8F, 0x07, 0x83, 0x5C, 0xDE, 0x6D, 0xEF, 0xAD, 0xD6, 0xAF, 0x80, 0xDB, 0xFB, + 0x37, 0xEA, 0x56, 0xEF, 0xD7, 0x07, 0x03, 0x59, 0xDE, 0x14, 0x06, 0x0B, 0x79, 0x6B, 0x18, 0x56, 0xE4, 0x51, 0xDD, 0x26, 0xE6, 0xB1, 0xDD, 0x6E, + 0xE6, 0xDD, 0xDD, 0x2A, 0xFE, 0x06, 0xDE, 0x72, 0xFE, 0xEE, 0xB7, 0x49, 0x7E, 0x05, 0x31, 0x84, 0x7F, 0x1F, 0x31, 0x10, 0xFD, 0x9E, 0x31, 0x58, + 0xFD, 0x06, 0xE6, 0x62, 0x18, 0x90, 0x57, 0x83, 0x01, 0x2B, 0xCF, 0x00, 0x06, 0x08, 0x79, 0x36, 0xB7, 0x24, 0xF2, 0x96, 0x6E, 0x6D, 0xE4, 0x85, + 0xDD, 0xE6, 0xFD, 0xDA, 0xDD, 0xDE, 0xF8, 0x0D, 0x01, 0x4B, 0xF9, 0x5B, 0x01, 0xC3, 0xEA, 0x2F, 0xDE, 0xDB, 0x21, 0x4E, 0xF9, 0x3E, 0xD4, 0x33, + 0x8D, 0x9E, 0x57, 0xD4, 0x3D, 0xDF, 0xF0, 0x38, 0x67, 0x26, 0xBE, 0xE9, 0x2C, 0xC6, 0x8F, 0x9D, 0x0F, 0xDC, 0xAE, 0x17, 0xDA, 0x22, 0x7C, 0x76, + 0xB5, 0x79, 0x81, 0x49, 0x68, 0xA3, 0x03, 0x3B, 0xD1, 0x86, 0x02, 0xAC, 0xB0, 0xAD, 0x50, 0x07, 0xBB, 0x6C, 0xCB, 0xC4, 0x04, 0xD3, 0x59, 0x83, + 0x7C, 0x62, 0xB2, 0x9A, 0xAA, 0x7E, 0xBE, 0xC0, 0xB2, 0xEA, 0x4A, 0xF9, 0x2C, 0xE0, 0xCB, 0x0A, 0xE0, 0xB3, 0x87, 0x35, 0x89, 0x0A, 0x18, 0x84, + 0xDF, 0x36, 0x1D, 0xB0, 0x9F, 0x36, 0x04, 0x60, 0x54, 0x6D, 0x67, 0x60, 0x6C, 0x6D, 0x19, 0xC0, 0xEE, 0xDA, 0x4E, 0x39, 0x3F, 0x5F, 0x01, 0xC0, + 0xB4, 0x35, 0xE1, 0xD1, 0x8A, 0xE4, 0x84, 0x55, 0xC6, 0xF3, 0x08, 0x58, 0xCC, 0xAA, 0x92, 0x12, 0x55, 0x0A, 0x05, 0x52, 0xC5, 0x58, 0xC0, 0x50, + 0x57, 0x70, 0xB1, 0x2E, 0xF9, 0x14, 0xFD, 0x41, 0x8C, 0xD0, 0x0A, 0x1A, 0xA1, 0x55, 0x41, 0xC5, 0x8F, 0x49, 0x9F, 0x64, 0x29, 0x9E, 0x3E, 0x32, + 0x3A, 0xE7, 0x00, 0x50, 0x72, 0x2A, 0x80, 0xDC, 0x0C, 0x41, 0xA5, 0x88, 0x9B, 0xA9, 0x66, 0x0D, 0x1D, 0x22, 0x22, 0xA0, 0x2C, 0x9A, 0x29, 0x11, + 0x50, 0x61, 0x12, 0x96, 0x61, 0x58, 0xCD, 0xD3, 0xFB, 0xAB, 0x32, 0x82, 0x89, 0x8D, 0xF9, 0xFF, 0xDB, 0xF8, 0x9E, 0xA2, 0x68, 0xE7, 0x1B, 0x7C, + 0x94, 0x4D, 0xE1, 0x6E, 0x60, 0xF3, 0xDD, 0x1C, 0x6C, 0xC4, 0x5B, 0xED, 0xAC, 0x79, 0x77, 0x38, 0x37, 0xF2, 0x77, 0xF9, 0x5C, 0x84, 0x07, 0x72, + 0x5E, 0x32, 0xCB, 0x03, 0x89, 0x9E, 0x5F, 0x82, 0xF9, 0xDD, 0x16, 0xE2, 0xD3, 0x48, 0x6D, 0x06, 0x7C, 0x87, 0x5B, 0xDB, 0x8D, 0x9F, 0xB1, 0xAD, + 0xEE, 0x24, 0xFB, 0xC1, 0xED, 0x65, 0x8B, 0xB8, 0x95, 0xCE, 0x34, 0xEE, 0xAF, 0xB3, 0xE4, 0x3D, 0xE1, 0x5C, 0x83, 0x4F, 0x71, 0x9E, 0xE4, 0x0D, + 0xCF, 0x6B, 0xA9, 0xFC, 0x87, 0x28, 0xFC, 0xA1, 0x6D, 0xBE, 0x89, 0xD8, 0xA6, 0xB2, 0xF7, 0x2C, 0x12, 0x5B, 0xF7, 0xF7, 0x0E, 0x5F, 0xB1, 0x63, + 0x90, 0xF0, 0x40, 0x76, 0xD2, 0x15, 0x9E, 0x0A, 0xF8, 0x0C, 0xC4, 0x07, 0x9D, 0x15, 0xF0, 0x0B, 0xCE, 0x0E, 0xF8, 0xB6, 0xF3, 0x07, 0x1E, 0xC4, + 0xD9, 0xED, 0x16, 0x3C, 0x87, 0xB8, 0xF1, 0x6C, 0x19, 0xF7, 0x0C, 0x1B, 0xF2, 0x8E, 0xB4, 0xC9, 0xA6, 0xB7, 0x7D, 0x6A, 0xDF, 0x50, 0x6E, 0x2A, + 0x1B, 0xEB, 0xBE, 0x71, 0xCF, 0x2B, 0x68, 0xEF, 0x21, 0xB1, 0xB6, 0x8A, 0x3B, 0xD7, 0x5D, 0x84, 0xF2, 0x34, 0xFF, 0xB3, 0xCF, 0x44, 0x41, 0x14, + 0xB1, 0x1E, 0xA4, 0xFF, 0x59, 0xFE, 0x7A, 0x73, 0x44, 0x78, 0x00, 0xE0, 0x57, 0x1E, 0xDB, 0x00, 0xC4, 0xE1, 0x01, 0x84, 0xBE, 0x11, 0xD2, 0x68, + 0x63, 0x06, 0xAC, 0x69, 0x1D, 0x1D, 0xD3, 0xF0, 0x8D, 0x33, 0xFD, 0x3E, 0xF3, 0x47, 0x65, 0xC6, 0xA4, 0x31, 0xF4, 0x93, 0x97, 0x2C, 0x19, 0x1D, + 0x6D, 0x06, 0x44, 0xDF, 0x8B, 0xFF, 0xFF, 0x8F, 0xBB, 0xA8, 0xAA, 0xC4, 0x08, 0x4C, 0x1E, 0x0E, 0xC7, 0x0D, 0x58, 0xFE, 0x50, 0x35, 0x91, 0x76, + 0xF9, 0x1F, 0x33, 0x4F, 0x33, 0xD1, 0x02, 0x76, 0x9B, 0x68, 0xAA, 0xE3, 0x99, 0xCC, 0xA6, 0x36, 0x4D, 0xAD, 0x59, 0x85, 0x9D, 0xE0, 0x89, 0x59, + 0x47, 0x3B, 0xE3, 0x49, 0x01, 0xED, 0xF1, 0xDB, 0xC2, 0x86, 0x95, 0x5B, 0x02, 0xBE, 0x3C, 0x02, 0xF8, 0xCE, 0xE4, 0xF2, 0x7A, 0xCE, 0xF2, 0xA2, + 0x81, 0x55, 0xB9, 0x59, 0x60, 0xDE, 0x35, 0xE7, 0x54, 0x5C, 0x46, 0x4C, 0xA9, 0x01, 0xC4, 0xAB, 0x9D, 0x21, 0x89, 0x9D, 0x94, 0xC5, 0xE1, 0x1B, + 0x6B, 0x05, 0x81, 0x2D, 0xE4, 0x03, 0xAC, 0xCF, 0x95, 0xF3, 0x2D, 0x8F, 0x2B, 0x95, 0x91, 0x21, 0xB2, 0x65, 0x5D, 0x9B, 0x12, 0xA7, 0xEE, 0xF7, + 0x58, 0x91, 0x0F, 0xE8, 0xFB, 0x49, 0x7E, 0x07, 0x19, 0x15, 0x18, 0x17, 0x54, 0x80, 0xC1, 0xB1, 0x1B, 0x6D, 0xB3, 0x43, 0x6A, 0x9A, 0x80, 0x0B, + 0xC2, 0xAE, 0x02, 0x2E, 0x39, 0x0C, 0xD2, 0x2F, 0xCC, 0x26, 0x0E, 0x55, 0x71, 0xBC, 0x4A, 0xC3, 0xAA, 0xAE, 0x25, 0x01, 0xD6, 0x5C, 0x4C, 0x00, + 0xAA, 0xB8, 0xB3, 0x23, 0xB1, 0x81, 0xED, 0x78, 0x47, 0x54, 0x11, 0x0B, 0x46, 0x58, 0x30, 0xD1, 0x4F, 0x55, 0xD4, 0x00, 0xA4, 0x6C, 0x2C, 0xAF, + 0x90, 0x25, 0xD5, 0xFB, 0xFF, 0xFF, 0xBB, 0x8F, 0xFB, 0xD0, 0x1F, 0x8F, 0xE4, 0xF8, 0xC5, 0x7E, 0xEB, 0x06, 0x81, 0xCF, 0xDD, 0x82, 0xB8, 0x89, + 0xFB, 0x11, 0x8F, 0xC0, 0x9F, 0xF1, 0x9C, 0x6F, 0xCC, 0xC5, 0x09, 0x79, 0xD3, 0xDE, 0x92, 0x5F, 0x02, 0xA5, 0x77, 0xE5, 0x6D, 0x7A, 0x4C, 0xD8, + 0x3B, 0xAC, 0xEF, 0xE0, 0x7B, 0xDB, 0xF5, 0x3D, 0xF7, 0x18, 0x2E, 0x75, 0x57, 0xC0, 0xD5, 0xEE, 0x3E, 0x7C, 0x8A, 0xDF, 0x81, 0xAF, 0xF1, 0x45, + 0x70, 0x0F, 0x7C, 0x62, 0x0F, 0xC8, 0x6F, 0xF6, 0x8D, 0x3C, 0x6D, 0x1F, 0xE3, 0xFE, 0xBD, 0xE3, 0x4D, 0x8A, 0x97, 0xDE, 0xA6, 0xB7, 0xF8, 0x2C, + 0xEE, 0x3D, 0x83, 0xD3, 0xDC, 0x31, 0xB8, 0xBD, 0xD2, 0x4D, 0xD3, 0xD7, 0xF0, 0x4A, 0xBD, 0x01, 0xFE, 0xAB, 0x07, 0xE3, 0x27, 0xF4, 0x75, 0x7C, + 0x8A, 0x7E, 0x8F, 0xB7, 0xC3, 0x75, 0xF0, 0x2A, 0x66, 0x7A, 0xBE, 0x99, 0x7F, 0x88, 0x23, 0xDE, 0x3E, 0xB8, 0xE6, 0x8D, 0x04, 0xBF, 0xF2, 0x29, + 0xF4, 0xCB, 0xBA, 0xA0, 0x67, 0xC2, 0xC0, 0x4A, 0x82, 0xF2, 0x9F, 0x21, 0xBF, 0xD8, 0xDF, 0x56, 0xF8, 0x1E, 0xB7, 0x51, 0x25, 0xF8, 0x55, 0x5C, + 0x12, 0x78, 0x20, 0x64, 0xFF, 0xF6, 0x07, 0xB1, 0xD1, 0x2F, 0xFD, 0x1B, 0x04, 0x5D, 0x44, 0xF9, 0xC6, 0x80, 0xE8, 0xC5, 0x69, 0x13, 0xFC, 0x88, + 0x30, 0x12, 0x11, 0xBB, 0xF8, 0xC3, 0xBD, 0x97, 0xAF, 0x63, 0x02, 0x70, 0xB9, 0x49, 0x1E, 0x54, 0x86, 0xC9, 0x17, 0x94, 0x6B, 0xF6, 0xDF, 0xF2, + 0xE7, 0x60, 0x80, 0x85, 0xFB, 0x32, 0x92, 0x9E, 0x54, 0xC6, 0xA3, 0xA1, 0x57, 0xD4, 0x8C, 0x95, 0x68, 0x03, 0xE3, 0x2F, 0x1A, 0x6C, 0x3C, 0x81, + 0xAE, 0x1B, 0xA7, 0xA0, 0xF7, 0xC6, 0x75, 0xFA, 0x9E, 0xAE, 0xA2, 0xAC, 0x43, 0x3F, 0x2C, 0x9B, 0x75, 0x22, 0x65, 0x59, 0x9D, 0x43, 0xE5, 0xF8, + 0x57, 0x07, 0x6C, 0x1D, 0xF3, 0xB4, 0x63, 0x95, 0x31, 0x30, 0x7D, 0xBA, 0x33, 0x0E, 0xA2, 0xA3, 0x24, 0x18, 0x2F, 0xA0, 0x73, 0xD0, 0xC6, 0x6D, + 0xB4, 0x12, 0x25, 0xA8, 0xDA, 0x38, 0x06, 0x49, 0xC2, 0xD2, 0x68, 0xF5, 0xDC, 0x66, 0x2B, 0xE0, 0x3C, 0x61, 0x08, 0x24, 0x7E, 0x2F, 0x64, 0x31, + 0x65, 0xC4, 0xF2, 0xF8, 0x51, 0x86, 0x95, 0x85, 0x7C, 0x35, 0x97, 0x5D, 0x81, 0x4D, 0x75, 0x1E, 0x48, 0xEB, 0x8E, 0x24, 0xEB, 0x17, 0x3D, 0x12, + 0x85, 0xBA, 0x4A, 0x3B, 0x51, 0x5D, 0xD3, 0x3D, 0x9D, 0x2B, 0x30, 0x0E, 0xF5, 0xFB, 0xA1, 0xBC, 0xB4, 0xBA, 0xA3, 0x52, 0xF0, 0x83, 0xE8, 0x12, + 0x64, 0x70, 0xBD, 0x26, 0x4D, 0x63, 0xCC, 0xD3, 0x92, 0x63, 0x60, 0x1A, 0x75, 0x1C, 0x44, 0xEB, 0x8E, 0x17, 0xD0, 0xA9, 0x65, 0x21, 0x3D, 0x4A, + 0x99, 0x81, 0x3E, 0x2E, 0xBB, 0xE9, 0x07, 0xCA, 0x21, 0x74, 0xB2, 0xB2, 0x0C, 0xDD, 0x42, 0x19, 0x52, 0x17, 0xC3, 0x35, 0x0B, 0x83, 0xA4, 0x11, + 0x66, 0x83, 0x61, 0x8C, 0x2A, 0x49, 0xB3, 0x18, 0xA3, 0xD2, 0x19, 0xCA, 0xBA, 0xDE, 0xA2, 0x8C, 0xA9, 0xF3, 0x28, 0xC3, 0xEA, 0x2E, 0xC6, 0xBE, + 0xBA, 0x8F, 0xF1, 0xDE, 0xA3, 0x83, 0x8E, 0x7F, 0x69, 0xDB, 0xB1, 0xED, 0x0F, 0x1D, 0xE4, 0x32, 0xA6, 0xA2, 0x99, 0x94, 0xD9, 0xE8, 0x06, 0x2F, + 0xE5, 0x3B, 0xDA, 0xA8, 0x1C, 0x48, 0x6B, 0x95, 0x3F, 0xE8, 0x77, 0x39, 0x86, 0xAE, 0x57, 0x76, 0xD4, 0x23, 0x8C, 0x4B, 0xF5, 0x7C, 0x8C, 0xAB, + 0x13, 0x8E, 0x4D, 0x69, 0xCB, 0x31, 0x14, 0xDA, 0x75, 0xCC, 0x66, 0x43, 0x5F, 0x96, 0x97, 0xE8, 0xD7, 0x32, 0xAF, 0xDE, 0x5B, 0x3E, 0xE5, 0xD1, + 0x69, 0x05, 0x94, 0xA1, 0xF4, 0xA8, 0xB2, 0x4C, 0x0F, 0xDD, 0xA0, 0x2C, 0x45, 0x4F, 0x8D, 0xD5, 0xE8, 0xB5, 0x71, 0x07, 0xFA, 0xB6, 0xAC, 0x63, + 0x0C, 0x02, 0x8A, 0x0E, 0xF5, 0x4B, 0x32, 0xD4, 0x71, 0x9A, 0xEB, 0xE7, 0x9A, 0xC2, 0xCA, 0x22, 0x9D, 0x90, 0x32, 0x74, 0x2F, 0xC1, 0xF7, 0x22, + 0x12, 0xEE, 0xFF, 0xDC, 0xDC, 0xD6, 0xAC, 0x1E, 0x2C, 0x1C, 0xFF, 0x00, 0x32, 0xB6, 0xBC, 0x1C, 0x17, 0x01, 0xF4, 0x41, 0xD5, 0x7B, 0x92, 0x34, + 0x4A, 0xC1, 0x14, 0xC0, 0x32, 0x5A, 0xD6, 0x81, 0x2D, 0x17, 0x3D, 0x29, 0x27, 0xF0, 0x2D, 0x97, 0xE5, 0xD9, 0x38, 0xF2, 0xB0, 0xFF, 0xFF, 0x0F, + 0xC5, 0x40, 0x42, 0xD0, 0xED, 0xFF, 0xFF, 0xD2, 0x12, 0x6E, 0x09, 0xB3, 0x5C, 0x56, 0x5A, 0xE2, 0x76, 0xAF, 0x7B, 0x75, 0xB1, 0x03, 0xEA, 0x72, + 0x6A, 0xEA, 0x29, 0x2D, 0x74, 0x49, 0x50, 0x24, 0x08, 0x41, 0x20, 0x47, 0x08, 0xB3, 0x99, 0x83, 0x22, 0xDF, 0x15, 0x96, 0xE4, 0xD4, 0xE2, 0xE2, + 0xFC, 0xC2, 0x20, 0xDC, 0xE5, 0xC1, 0xF1, 0xB0, 0xDB, 0x1F, 0x6C, 0xD8, 0xDF, 0xAA, 0xF4, 0x70, 0x83, 0x41, 0xEA, 0x26, 0xE0, 0xD3, 0x41, 0xE3, + 0xAD, 0x83, 0xA9, 0x25, 0x34, 0xAC, 0x70, 0x1D, 0x96, 0x76, 0xF3, 0xCA, 0x5A, 0xAD, 0xE6, 0x41, 0x20, 0xBB, 0x8D, 0xA2, 0x13, 0xC6, 0x71, 0x01, + 0x99, 0xF2, 0x9B, 0xB6, 0x91, 0x2D, 0x37, 0xE8, 0x82, 0x20, 0x43, 0x11, 0x83, 0xC9, 0x30, 0x53, 0x80, 0x34, 0x37, 0x30, 0xB2, 0xC2, 0xF4, 0xCA, + 0x02, 0x10, 0xAB, 0x45, 0x28, 0x55, 0x2C, 0x44, 0x7B, 0x38, 0xA9, 0x9B, 0x2C, 0x99, 0x43, 0x63, 0xA8, 0x3E, 0xCE, 0xD4, 0x8B, 0xAA, 0x0D, 0x65, + 0x14, 0x0D, 0x17, 0x79, 0xCC, 0xEA, 0xC8, 0x5D, 0x81, 0xAC, 0xD5, 0x15, 0x41, 0xC2, 0x4E, 0xC3, 0x82, 0x19, 0xC5, 0x89, 0x3D, 0xC8, 0x0A, 0xB3, + 0x8E, 0xC2, 0x23, 0x89, 0x9A, 0x97, 0x49, 0x55, 0x36, 0x91, 0x5A, 0x0D, 0x0B, 0x44, 0x39, 0xEA, 0xCD, 0x34, 0xF2, 0x89, 0x2D, 0x32, 0xAB, 0x25, + 0xA0, 0xC0, 0xCA, 0x80, 0x0D, 0x62, 0xDA, 0x04, 0x11, 0x0C, 0x7A, 0x08, 0xFF, 0x15, 0x6A, 0xBC, 0x0A, 0x02, 0xFC, 0x01, 0x2A, 0x85, 0xFB, 0x71, + 0xA4, 0xF2, 0x59, 0x82, 0xD5, 0x63, 0xCA, 0xD6, 0xF3, 0x02, 0x81, 0x47, 0xF7, 0x8E, 0xA7, 0x00, 0x89, 0x1D, 0x00, 0x81, 0x01, 0xBC, 0x6C, 0xE8, + 0x24, 0x81, 0xC8, 0xA9, 0x0B, 0x02, 0x00, 0x4C, 0x70, 0xCC, 0xA7, 0x8A, 0x43, 0xE1, 0xF1, 0x26, 0x87, 0x10, 0x9B, 0x15, 0xE7, 0x9A, 0xCB, 0x23, + 0x34, 0x8B, 0xB9, 0x33, 0xB1, 0x54, 0x64, 0x32, 0x4D, 0x63, 0x80, 0x75, 0x60, 0x90, 0x40, 0x81, 0xA1, 0xB1, 0x7C, 0xF9, 0x36, 0x2E, 0x57, 0x67, + 0xE4, 0x16, 0x7A, 0x89, 0x65, 0x4D, 0x4C, 0x0E, 0x0B, 0x7F, 0xF8, 0x1D, 0x60, 0x83, 0x45, 0x47, 0x81, 0x03, 0x7D, 0xF7, 0x2B, 0xA6, 0x55, 0x20, + 0x02, 0x55, 0x13, 0xAC, 0xA5, 0x0A, 0x48, 0x42, 0xC6, 0x60, 0x2B, 0x1A, 0xA4, 0x41, 0x88, 0x27, 0x6D, 0xC3, 0x99, 0x6B, 0x71, 0x04, 0xF9, 0x39, + 0x32, 0xC9, 0x62, 0x27, 0xA9, 0x00, 0x31, 0x92, 0x00, 0x14, 0x64, 0x1B, 0xFE, 0x98, 0xF5, 0x43, 0x58, 0x91, 0xF7, 0xB8, 0x10, 0x86, 0x9F, 0x83, + 0x39, 0x8F, 0x96, 0x13, 0x07, 0x9C, 0x12, 0x1B, 0x46, 0xFD, 0xD5, 0x45, 0x34, 0x86, 0xF9, 0xFF, 0xAB, 0x70, 0x4C, 0xA0, 0xD9, 0xAA, 0x14, 0x42, + 0x33, 0x49, 0xC3, 0xE4, 0x19, 0x89, 0x06, 0x7F, 0xD3, 0xB0, 0x6D, 0x9E, 0xA5, 0x3B, 0x88, 0x06, 0x5B, 0x2A, 0xEC, 0x3E, 0x64, 0xC2, 0x53, 0x26, + 0xC3, 0x34, 0xE1, 0x6B, 0x8C, 0xCE, 0x46, 0x50, 0x02, 0x9F, 0xEE, 0xDD, 0xF4, 0x58, 0xEE, 0xEB, 0xCB, 0xCB, 0xCE, 0x65, 0xC2, 0x0B, 0x9F, 0x2E, + 0x12, 0xD3, 0xEF, 0xEF, 0x9D, 0xCA, 0x2A, 0x7E, 0xC4, 0x7E, 0x0C, 0xCC, 0xF9, 0x39, 0x6F, 0xC6, 0x43, 0x49, 0xBE, 0x12, 0xA6, 0x71, 0x54, 0xA7, + 0xDA, 0x2F, 0xCE, 0x5A, 0x6D, 0x1E, 0xD4, 0x04, 0x42, 0x9C, 0x57, 0x8D, 0xCD, 0xA0, 0x73, 0xB8, 0x0A, 0xE5, 0xC9, 0xAB, 0x3C, 0xAF, 0xAF, 0xBC, + 0x0A, 0x97, 0x0F, 0xEE, 0x28, 0xC3, 0x30, 0xC1, 0xFA, 0xFF, 0x0F, 0xFE, 0x39, 0xFD, 0x08, 0xE3, 0xB2, 0x9F, 0x8F, 0xEB, 0x9C, 0x70, 0x6C, 0x96, + 0x5B, 0x8E, 0x31, 0xE4, 0x85, 0x65, 0x16, 0xF9, 0x65, 0x79, 0x31, 0x7F, 0x2D, 0xFF, 0xFD, 0xDE, 0x72, 0x8F, 0x2F, 0xA0, 0xFC, 0xF9, 0xA3, 0xCA, + 0x4B, 0xBE, 0xCF, 0x98, 0xB1, 0xE5, 0x4F, 0x8D, 0x09, 0xFC, 0xAA, 0xFF, 0xBF, 0x38, 0xBC, 0x21, 0x9C, 0xFF, 0x08, 0x8C, 0xA9, 0x19, 0xFA, 0x20, + 0xFF, 0xF5, 0x47, 0x27, 0xFF, 0x91, 0x16, 0xFC, 0x02, 0xB8, 0x5B, 0xCE, 0x85, 0x87, 0xC8, 0x23, 0xE0, 0x32, 0xF2, 0xAE, 0x1E, 0x32, 0x97, 0xD0, + 0x9B, 0xE6, 0x27, 0x7A, 0xC9, 0x5F, 0xD8, 0xA3, 0xFE, 0xCB, 0x5E, 0xF7, 0x1B, 0xE2, 0x38, 0x3F, 0x13, 0x87, 0xCD, 0x63, 0x71, 0x53, 0xDF, 0x7C, + 0xD6, 0x0A, 0xDE, 0x93, 0xFB, 0xE0, 0x31, 0xBF, 0x7F, 0x0A, 0xFF, 0xF9, 0x3F, 0x3D, 0xD5, 0xA7, 0xEA, 0xD9, 0x3E, 0x5B, 0x7F, 0xF7, 0xEF, 0xFA, + 0xC0, 0x1F, 0xD8, 0x7F, 0xC8, 0xF7, 0x3E, 0x46, 0x2E, 0x88, 0x1F, 0xFE, 0x47, 0xDC, 0x05, 0x52, 0x7E, 0xC6, 0x71, 0xF3, 0x5C, 0x9C, 0x26, 0xAF, + 0xC1, 0xC5, 0x21, 0xA0, 0x84, 0xFB, 0xA0, 0xFC, 0x1A, 0x36, 0xC1, 0x8B, 0x36, 0xC0, 0x94, 0x4B, 0x57, 0x3E, 0x97, 0x7E, 0x59, 0xC3, 0xAC, 0x30, + 0x09, 0xF3, 0xE5, 0xB5, 0xFB, 0xBE, 0x31, 0x76, 0xBD, 0x38, 0x62, 0xBC, 0x49, 0x2A, 0xFA, 0x2A, 0x4E, 0x14, 0x76, 0xB1, 0x2A, 0x50, 0x39, 0xE3, + 0x9B, 0x22, 0xD7, 0x27, 0x65, 0x03, 0x62, 0x14, 0x13, 0x3B, 0x74, 0x61, 0x68, 0xF6, 0x85, 0x45, 0xA0, 0x34, 0xF2, 0x79, 0x6E, 0xE6, 0x2C, 0x8E, + 0xDB, 0xB9, 0x79, 0xBB, 0xBA, 0xF9, 0x64, 0x09, 0xAD, 0x93, 0x5D, 0x5A, 0x27, 0x30, 0xBF, 0x89, 0x80, 0xCF, 0x04, 0x69, 0xC8, 0x76, 0x6A, 0x68, + 0xC9, 0x57, 0xD5, 0x57, 0xE2, 0xA0, 0xAA, 0xAF, 0x8D, 0x23, 0x4A, 0x12, 0x26, 0x45, 0xAB, 0x67, 0x35, 0x5B, 0x41, 0x8D, 0x27, 0x7C, 0x80, 0xC4, + 0xAF, 0x83, 0x2C, 0xA6, 0x88, 0x58, 0x1E, 0x65, 0x1C, 0x56, 0x96, 0xC6, 0xAB, 0xD9, 0x83, 0x2B, 0xB0, 0x64, 0xCE, 0x03, 0xB6, 0xEE, 0xD8, 0x60, + 0xFD, 0x8D, 0x47, 0xA2, 0x82, 0xAC, 0xD2, 0xA3, 0xAA, 0x6B, 0xAA, 0xA9, 0xE4, 0x27, 0xA8, 0x1F, 0xA4, 0xFC, 0xD3, 0x39, 0x99, 0xD0, 0x15, 0xC0, + 0x46, 0x58, 0xBF, 0x8C, 0xD8, 0x98, 0x7D, 0xF2, 0x4E, 0xC0, 0x3F, 0x9B, 0x30, 0x78, 0xE2, 0x80, 0x75, 0x96, 0xB1, 0xC8, 0x39, 0x9D, 0xEA, 0x36, + 0xE9, 0x34, 0x14, 0x39, 0xDB, 0xD3, 0xDC, 0xFB, 0x23, 0xB2, 0x57, 0x99, 0xDF, 0x56, 0x33, 0x61, 0x98, 0x31, 0x37, 0x3F, 0x2F, 0x1B, 0x65, 0x7B, + 0xCB, 0xDA, 0x25, 0x95, 0x9D, 0x9A, 0x95, 0x2E, 0x2D, 0x7F, 0x09, 0x2F, 0x04, 0xC8, 0x96, 0xF2, 0x05, 0x38, 0x48, 0x95, 0xCB, 0x24, 0x52, 0x90, + 0x24, 0x5C, 0x51, 0xE4, 0xD7, 0x02, 0xC8, 0xBC, 0xAC, 0x47, 0xBB, 0x36, 0xDC, 0x71, 0x50, 0x2E, 0xDF, 0xF5, 0x18, 0x34, 0x42, 0x6E, 0x01, 0x21, + 0x74, 0x63, 0x44, 0x9D, 0xFB, 0x81, 0x19, 0xC2, 0x87, 0x14, 0x13, 0x3D, 0x64, 0x65, 0xF4, 0xD0, 0xC9, 0xB1, 0x08, 0x0C, 0xB9, 0x98, 0xA0, 0x52, + 0x71, 0x87, 0x46, 0x62, 0x83, 0xD6, 0x1D, 0x12, 0x5A, 0xFB, 0xAE, 0xD3, 0x3C, 0x45, 0x95, 0xD0, 0xBB, 0xFF, 0x8A, 0xDF, 0xE4, 0x43, 0x7A, 0x71, + 0xA6, 0x9C, 0x05, 0x58, 0xF7, 0xA1, 0xFF, 0x46, 0x4F, 0x33, 0x69, 0x0F, 0x38, 0x65, 0x89, 0x08, 0x97, 0x99, 0x32, 0x38, 0x98, 0x08, 0xD9, 0xC1, + 0x11, 0x8F, 0x07, 0x81, 0xF8, 0x6E, 0x8D, 0x5B, 0xB0, 0x18, 0x43, 0x4C, 0x55, 0x15, 0xFA, 0x7E, 0xC8, 0x2E, 0xC1, 0xC1, 0x40, 0x02, 0x21, 0xF2, + 0x30, 0x79, 0x5F, 0x68, 0xF8, 0xF1, 0xED, 0x0C, 0x30, 0xE0, 0x81, 0x4E, 0x93, 0xDF, 0x3B, 0x31, 0xC1, 0xAE, 0x93, 0xFB, 0xCA, 0x9E, 0xD7, 0x82, + 0x47, 0xD2, 0xB2, 0x53, 0x31, 0xD0, 0x7C, 0xD4, 0x09, 0xFB, 0x29, 0x8B, 0x23, 0xB9, 0xE9, 0x5E, 0x89, 0x33, 0x0F, 0xC4, 0x52, 0x18, 0x29, 0x48, + 0x3D, 0x89, 0x46, 0x7F, 0x00, 0x7F, 0x51, 0x25, 0x4F, 0x40, 0xA9, 0x06, 0x73, 0xDF, 0xC4, 0xFF, 0xE9, 0x56, 0xFA, 0x4B, 0x5C, 0xE2, 0xFC, 0x1F, + 0x93, 0x14, 0x8E, 0x22, 0x4A, 0x75, 0x70, 0x36, 0x71, 0xBE, 0x2D, 0x62, 0xE7, 0x83, 0x4A, 0xA1, 0x27, 0x89, 0x0A, 0x01, 0x52, 0xF8, 0x04, 0xB0, + 0x1C, 0xF2, 0xE8, 0x20, 0xEE, 0x14, 0x11, 0x09, 0x90, 0x40, 0x53, 0xA7, 0xCC, 0x87, 0xCF, 0x69, 0xC0, 0x03, 0x90, 0x70, 0xE5, 0xFE, 0x70, 0xB6, + 0x07, 0xC4, 0x62, 0x16, 0x51, 0x01, 0x66, 0xA7, 0x2F, 0x5D, 0x6C, 0xA0, 0x0C, 0x6E, 0x63, 0x88, 0x21, 0x26, 0x98, 0x24, 0x6D, 0xE4, 0xDC, 0x08, + 0x8C, 0x14, 0x79, 0x0F, 0x00, 0xB1, 0x4D, 0x78, 0x29, 0x5C, 0x93, 0x80, 0xD5, 0xC6, 0xA2, 0x5E, 0xF9, 0x83, 0xA5, 0x82, 0x22, 0x34, 0x4E, 0x6A, + 0xDC, 0xC6, 0x53, 0x1F, 0xB8, 0x64, 0xE8, 0xBB, 0x84, 0x5D, 0xC4, 0x61, 0xBC, 0xF0, 0x4C, 0x38, 0x26, 0xED, 0xAC, 0xB6, 0x59, 0xC5, 0xCA, 0x60, + 0x73, 0x1C, 0xBB, 0x78, 0x5A, 0xE3, 0x69, 0xD1, 0x85, 0x43, 0x8F, 0x91, 0x57, 0xBE, 0x9D, 0xCF, 0x12, 0xE6, 0x7F, 0x16, 0x02, 0x31, 0xBA, 0x76, + 0x55, 0x13, 0x1C, 0xBB, 0xC9, 0x1E, 0x9F, 0x0F, 0x9C, 0xC9, 0xCE, 0x16, 0xCE, 0x53, 0x24, 0xDC, 0x2B, 0xCE, 0x89, 0xE7, 0x0A, 0x2C, 0xD0, 0xD4, + 0x2E, 0x6D, 0xBF, 0xC5, 0xCE, 0x5A, 0x5B, 0x52, 0x80, 0x13, 0x31, 0x0D, 0x87, 0x2F, 0x2A, 0x0D, 0xEE, 0x2F, 0x03, 0x57, 0x35, 0x52, 0x4D, 0x89, + 0xB7, 0x95, 0xA2, 0x4D, 0xD6, 0xE1, 0x02, 0xD6, 0x2D, 0x22, 0xF7, 0xBC, 0x62, 0x48, 0x8D, 0x4A, 0x45, 0x3E, 0x3E, 0x48, 0xDF, 0xB9, 0xB3, 0xC1, + 0xAA, 0x1A, 0x95, 0x12, 0x22, 0x5E, 0x7C, 0x23, 0x07, 0xC7, 0xD0, 0xE6, 0x8D, 0x80, 0x82, 0x7D, 0x45, 0x7B, 0xDB, 0x50, 0x72, 0xFF, 0xCE, 0xD0, + 0x78, 0xCE, 0x23, 0xF1, 0x97, 0x87, 0xE3, 0x8B, 0x3F, 0x94, 0x3B, 0x3F, 0x2E, 0x19, 0xD9, 0x51, 0xA2, 0x05, 0xBA, 0x6D, 0xCB, 0xF6, 0xEE, 0x03, + 0x17, 0x24, 0x7C, 0x87, 0x2B, 0x3F, 0xEA, 0xC2, 0x9F, 0x32, 0xF3, 0x0B, 0x84, 0x7D, 0xD5, 0xDD, 0xF7, 0xF3, 0xE9, 0xBF, 0x46, 0x10, 0xB1, 0x50, + 0x32, 0xB4, 0x56, 0x04, 0x74, 0x19, 0xE5, 0xE7, 0x88, 0xD8, 0xF7, 0x79, 0xCD, 0xD0, 0x33, 0x85, 0xF3, 0x93, 0xB9, 0xC0, 0x7B, 0x78, 0xF8, 0x17, + 0xDC, 0x7A, 0xAC, 0x1C, 0x12, 0x67, 0x52, 0xF3, 0x0F, 0xE0, 0x7D, 0x1F, 0xD7, 0x37, 0xC6, 0xA8, 0x01, 0x4C, 0x8D, 0xE7, 0xCB, 0x16, 0xFD, 0x5D, + 0x96, 0x62, 0xB4, 0xCC, 0xDD, 0xB2, 0x84, 0xDD, 0xF4, 0x62, 0x13, 0x76, 0x61, 0xBB, 0xD7, 0x4F, 0x08, 0x15, 0xCC, 0xA7, 0x08, 0x7C, 0xF9, 0xF3, + 0x2B, 0x3B, 0xEE, 0x7E, 0x5F, 0x47, 0xBF, 0x6F, 0x2F, 0x58, 0xEF, 0x6C, 0x65, 0xEF, 0x8D, 0x6B, 0x95, 0x60, 0xA9, 0x2A, 0x52, 0xB3, 0x7F, 0x97, + 0x48, 0x6D, 0x8D, 0x91, 0x81, 0x9E, 0x87, 0xA4, 0x07, 0x83, 0x69, 0x9F, 0x07, 0x4C, 0x89, 0x7E, 0x91, 0x56, 0x55, 0x1C, 0x9A, 0x4B, 0x24, 0x4B, + 0xD9, 0x71, 0x65, 0x02, 0x71, 0x0E, 0xC0, 0x61, 0x5C, 0x0E, 0x5C, 0xC7, 0x09, 0xA7, 0xDC, 0x30, 0x8A, 0x0F, 0x3C, 0x11, 0x62, 0x97, 0x97, 0x07, + 0x14, 0x60, 0x62, 0xCA, 0xDA, 0xBE, 0x58, 0xED, 0x17, 0x83, 0xE5, 0xC2, 0x72, 0x92, 0xF4, 0xC4, 0xE1, 0x23, 0x48, 0xFC, 0xA7, 0x1F, 0x72, 0x2A, + 0x51, 0x19, 0x4B, 0x7A, 0xE8, 0x5B, 0x50, 0x52, 0x5F, 0x8E, 0xCB, 0x20, 0x1B, 0x46, 0x31, 0x9F, 0x25, 0xEC, 0xA2, 0x57, 0xC6, 0x73, 0x3C, 0xB7, + 0x70, 0x93, 0xD2, 0xA0, 0x1F, 0xA8, 0x7C, 0x36, 0x56, 0x50, 0x7E, 0x2A, 0x3B, 0x28, 0xDB, 0xE5, 0x0F, 0x65, 0x0D, 0x65, 0xB7, 0xF1, 0x5F, 0x39, + 0xC4, 0xB8, 0x47, 0x59, 0xC6, 0x38, 0xC5, 0x67, 0x19, 0x72, 0xFC, 0x63, 0xAC, 0xC2, 0x74, 0x3C, 0xC4, 0xC8, 0x78, 0x07, 0x94, 0xB1, 0x2B, 0x93, + 0xB1, 0xE5, 0x69, 0xF9, 0x65, 0x0C, 0xDB, 0x97, 0x8D, 0xC6, 0x37, 0x65, 0x98, 0x32, 0x53, 0xF9, 0xA8, 0x2C, 0x56, 0xDE, 0x2A, 0x53, 0x28, 0x5B, + 0x8D, 0xC9, 0xC6, 0x0F, 0xC6, 0x16, 0xC6, 0x22, 0xC6, 0x2B, 0xC6, 0x34, 0xC6, 0x89, 0xE3, 0x0B, 0x74, 0x11, 0x7A, 0x73, 0xDC, 0x82, 0x3A, 0x56, + 0x2C, 0x4F, 0x96, 0x39, 0xCB, 0x0C, 0x52, 0xCB, 0x63, 0x49, 0xCA, 0x0F, 0xA2, 0x94, 0xC3, 0xE5, 0x71, 0x99, 0xA8, 0x7C, 0xA0, 0x6C, 0x55, 0x26, + 0x2B, 0x63, 0xE1, 0xAE, 0xB9, 0xFC, 0x10, 0xBC, 0xF2, 0xE6, 0xEB, 0xFB, 0xF8, 0x06, 0x38, 0xE7, 0x37, 0x7C, 0xED, 0xD5, 0xB5, 0xBA, 0x7A, 0x2B, + 0x81, 0x74, 0x6A, 0x5B, 0xEF, 0x67, 0x94, 0xB6, 0x58, 0x30, 0xBE, 0x71, 0x9B, 0x15, 0xB0, 0x03, 0x6D, 0x3E, 0x60, 0x97, 0x00, 0x58, 0xA7, 0xC0, + 0x62, 0x9D, 0x58, 0xD7, 0xC0, 0xCA, 0x58, 0x9F, 0xA8, 0x3E, 0x2F, 0xB0, 0xA6, 0xB1, 0x7D, 0xFA, 0xB1, 0xDA, 0xE9, 0x75, 0x99, 0xF5, 0x18, 0xCD, + 0x17, 0x5B, 0x81, 0x4E, 0xB4, 0xD2, 0x47, 0x7B, 0xFA, 0xEC, 0xA0, 0x6D, 0xA7, 0x6A, 0xCC, 0x43, 0xB3, 0xDE, 0x18, 0x0C, 0xCE, 0xB6, 0x59, 0x6B, + 0xF2, 0x50, 0x06, 0xB8, 0xDF, 0xA4, 0x37, 0xA9, 0x8C, 0x81, 0x9F, 0x21, 0xC9, 0x0A, 0x5B, 0x92, 0x10, 0x13, 0xFE, 0xA7, 0xC8, 0xAE, 0x92, 0xBC, + 0xA9, 0xA0, 0x9C, 0xED, 0xE4, 0x6E, 0x07, 0x96, 0xF6, 0x21, 0xD3, 0x87, 0x29, 0x9B, 0x06, 0xE8, 0x2D, 0xC8, 0x72, 0x9E, 0x94, 0xF1, 0x78, 0x0E, + 0x52, 0x8D, 0x00, 0x64, 0x3A, 0xF8, 0x33, 0xE5, 0x00, 0xEF, 0x48, 0xC3, 0xDE, 0x62, 0xE1, 0x97, 0x42, 0x9C, 0x13, 0xE3, 0xEE, 0x11, 0x64, 0xE7, + 0x1D, 0xB7, 0x0D, 0x1F, 0x9B, 0x03, 0x3F, 0x29, 0x68, 0xDF, 0xF2, 0x06, 0x54, 0x2C, 0x3E, 0x79, 0x5C, 0x54, 0x92, 0xA2, 0x88, 0x64, 0x22, 0xF1, + 0x08, 0x89, 0xDF, 0x81, 0x15, 0x79, 0x46, 0x34, 0xCB, 0x0F, 0x49, 0xA0, 0xD0, 0x31, 0x41, 0x02, 0xDB, 0xA4, 0x26, 0xE5, 0xFF, 0x30, 0xE7, 0xFF, + 0x7F, 0x16, 0xCB, 0x1E, 0xDB, 0xAE, 0x13, 0xB5, 0x31, 0x29, 0x75, 0x97, 0x59, 0x04, 0x59, 0x82, 0x19, 0xCE, 0x7C, 0xC2, 0x3C, 0xC2, 0x5C, 0x68, + 0xAE, 0x61, 0x4E, 0x41, 0x9E, 0x34, 0x1B, 0x92, 0x25, 0xA4, 0x9A, 0x33, 0xC9, 0x9E, 0x64, 0x14, 0x0E, 0xEC, 0x85, 0x68, 0x12, 0xE4, 0x63, 0x42, + 0x94, 0xB1, 0x14, 0x89, 0x68, 0x60, 0x13, 0x32, 0x55, 0xB3, 0xEE, 0x23, 0x23, 0x21, 0xF3, 0x75, 0x0B, 0x94, 0xBA, 0xC8, 0x7E, 0xBA, 0xCF, 0xEE, + 0x0C, 0xFA, 0x7D, 0x2C, 0x78, 0xBF, 0xD9, 0x85, 0x76, 0xA6, 0x9D, 0xC1, 0x2E, 0xB6, 0xBB, 0xED, 0x14, 0xF6, 0x07, 0x65, 0x9A, 0xBF, 0x5A, 0x3E, + 0x92, 0x1F, 0x8E, 0x11, 0xFF, 0x86, 0xF2, 0x2C, 0x6D, 0xA8, 0x5A, 0xE2, 0x77, 0x4B, 0x26, 0x6C, 0x16, 0x46, 0xF0, 0xAF, 0xC4, 0x55, 0x59, 0xB0, + 0xDB, 0xFC, 0x2D, 0x07, 0x2C, 0x81, 0x72, 0x72, 0xAE, 0x20, 0x8F, 0xCB, 0x1D, 0xE4, 0x79, 0xF9, 0x87, 0xFC, 0xFE, 0x31, 0x72, 0xBD, 0xEF, 0x98, + 0x0F, 0x7E, 0x19, 0x7F, 0xC8, 0x87, 0xFC, 0xAC, 0xDF, 0xF4, 0x57, 0xE6, 0x50, 0x7E, 0x6B, 0x6E, 0xF3, 0xBB, 0xAA, 0xE4, 0x4B, 0x7F, 0x27, 0xBF, + 0xC9, 0x13, 0xE4, 0x4C, 0x39, 0x5A, 0x2E, 0x96, 0x47, 0x7D, 0x0A, 0x79, 0x83, 0xCF, 0x97, 0x73, 0xF8, 0x16, 0x3E, 0x8C, 0xBF, 0xE2, 0x33, 0x6E, + 0x3E, 0xC1, 0x53, 0x7D, 0x71, 0x25, 0x32, 0x38, 0x95, 0x99, 0x3B, 0x05, 0x1F, 0xC8, 0xAB, 0x30, 0x47, 0xD5, 0x4E, 0xBE, 0xD9, 0x37, 0xBC, 0x8F, + 0x92, 0x49, 0x6B, 0x1B, 0x66, 0x5B, 0xD7, 0x1A, 0x5B, 0x06, 0x97, 0xD6, 0xFF, 0x4A, 0xFA, 0x57, 0x42, 0xFC, 0x44, 0x2E, 0xBF, 0x49, 0x33, 0xB7, + 0x32, 0xCD, 0xC9, 0x60, 0x7C, 0x38, 0xC2, 0x7F, 0xE1, 0x41, 0x3E, 0xC2, 0xE7, 0xAF, 0x58, 0xCD, 0x6B, 0x28, 0xC9, 0x15, 0x0F, 0x7F, 0x90, 0xC7, + 0xDF, 0xB9, 0x21, 0x5F, 0xD5, 0xCB, 0x95, 0x54, 0x7D, 0x29, 0xBD, 0xB9, 0x38, 0xC8, 0xCF, 0xE9, 0xFC, 0x03, 0x52, 0xE4, 0x08, 0x02, 0x73, 0xB2, + 0xFD, 0x6D, 0xB9, 0x2F, 0x17, 0x94, 0x67, 0x70, 0xFD, 0x79, 0x34, 0xF2, 0xBF, 0xC4, 0x1C, 0x07, 0xDD, 0xF7, 0xE1, 0xE1, 0x0F, 0xA9, 0x73, 0x23, + 0x58, 0x39, 0xEB, 0x95, 0x6F, 0x3B, 0x24, 0xDF, 0xC5, 0xFB, 0x3E, 0x37, 0x2E, 0x0F, 0xF4, 0xCB, 0x33, 0xD1, 0xF0, 0x55, 0x3F, 0x3C, 0x99, 0x12, + 0x0F, 0x47, 0xC5, 0x63, 0xF8, 0xE2, 0xF3, 0xD8, 0xF8, 0xBF, 0x09, 0xFC, 0xE0, 0x1D, 0xCF, 0xE8, 0x69, 0x5F, 0x7E, 0x8A, 0xC6, 0x38, 0xBF, 0x6E, + 0x91, 0x99, 0x5C, 0xDA, 0xBE, 0x38, 0x35, 0x05, 0xC2, 0xBA, 0xAA, 0x02, 0x9F, 0xE3, 0x97, 0xFA, 0x2D, 0x2F, 0x2D, 0x06, 0xEA, 0x7A, 0x27, 0x45, + 0xA4, 0xB1, 0x63, 0xD8, 0x4F, 0xFF, 0x7F, 0xDE, 0x5F, 0x45, 0xCA, 0x32, 0xB3, 0x76, 0xC1, 0xE5, 0xD3, 0x12, 0x55, 0xE3, 0x9F, 0x9F, 0xD8, 0xB5, + 0xB6, 0x8C, 0x2D, 0xB6, 0x91, 0x6C, 0xDA, 0x9E, 0x39, 0x5F, 0xD9, 0x49, 0x67, 0x38, 0xDB, 0xEB, 0x8C, 0x75, 0xA6, 0x3B, 0x43, 0x38, 0xA3, 0x38, + 0x93, 0x38, 0xEB, 0xE7, 0x0F, 0xF6, 0x9E, 0x6D, 0xDE, 0x41, 0x6A, 0xB1, 0xBF, 0xA0, 0x90, 0xE0, 0xDF, 0x8C, 0xA9, 0xC8, 0xFF, 0x3B, 0xED, 0xDD, + 0xF0, 0xBD, 0x77, 0x94, 0x9B, 0x4F, 0xC0, 0x5D, 0x39, 0xBE, 0xA3, 0xEE, 0x03, 0xF7, 0x65, 0x0F, 0x4F, 0x76, 0x4B, 0xF1, 0x16, 0x6E, 0x35, 0xBE, + 0x8C, 0xEF, 0xC0, 0x5F, 0xE0, 0xCB, 0xBC, 0x1F, 0x0F, 0x84, 0x4F, 0x71, 0x07, 0xDE, 0x31, 0x6C, 0x1D, 0xB6, 0xA3, 0x6D, 0xB6, 0x4B, 0x6D, 0x59, + 0xFB, 0xF0, 0xB6, 0x72, 0xFF, 0xDA, 0x2E, 0x34, 0x36, 0x92, 0x73, 0xEE, 0xF9, 0xCA, 0xB9, 0xAA, 0xE6, 0x0C, 0x74, 0x36, 0x70, 0x56, 0x70, 0x06, + 0x3B, 0x3B, 0x38, 0xD7, 0x9D, 0x3F, 0x9C, 0xF7, 0xCE, 0x21, 0x76, 0x24, 0x3A, 0xCA, 0x2E, 0x45, 0x27, 0xBD, 0x42, 0x16, 0xE4, 0x9F, 0x5C, 0x5E, + 0x5C, 0x26, 0xCF, 0xFF, 0x2F, 0x26, 0xD2, 0xB9, 0x08, 0xC6, 0x9F, 0xFA, 0x31, 0x20, 0x38, 0x62, 0x49, 0x62, 0x48, 0x9B, 0xC2, 0x58, 0x0A, 0xE4, + 0x8E, 0x7D, 0x15, 0x71, 0x79, 0xB9, 0x1C, 0xE8, 0xDA, 0x8A, 0xC0, 0x75, 0x44, 0xB6, 0x25, 0x21, 0xD7, 0xEA, 0xB2, 0xC0, 0x13, 0xDE, 0x2B, 0xF8, + 0x13, 0x7C, 0x22, 0x5F, 0xC8, 0xEF, 0xD8, 0xE4, 0x5D, 0x2F, 0xF9, 0xC5, 0xDB, 0xF7, 0x95, 0x87, 0xED, 0xBC, 0x7F, 0x7A, 0xF9, 0xDF, 0xDA, 0x1B, + 0x41, 0xEC, 0x2D, 0xBE, 0xD1, 0xAC, 0xDC, 0xF4, 0xED, 0x73, 0x5F, 0xDD, 0x39, 0xFC, 0x00, 0xBE, 0xE6, 0xB1, 0x78, 0x33, 0x1E, 0x02, 0x8F, 0x81, + 0x27, 0xC1, 0x93, 0xFB, 0x1A, 0xBD, 0x0D, 0x7E, 0x91, 0x2F, 0x71, 0xFF, 0xDD, 0x38, 0xEE, 0x1E, 0xF7, 0x98, 0x90, 0xFB, 0xC1, 0xE7, 0x7D, 0xE1, + 0xC8, 0x4D, 0x62, 0xC9, 0xDD, 0x82, 0xCA, 0xDD, 0x72, 0xEB, 0xDC, 0x04, 0xEE, 0x1D, 0xB2, 0xEB, 0xE6, 0x5E, 0x26, 0x03, 0xD6, 0x66, 0xFB, 0x99, + 0x0E, 0x54, 0xFF, 0x99, 0x85, 0x06, 0xC9, 0x9F, 0x65, 0xC0, 0x2E, 0xE1, 0x2D, 0x38, 0x82, 0x80, 0xE0, 0x39, 0x12, 0x42, 0x37, 0x04, 0x18, 0x39, + 0x1C, 0x0C, 0xEE, 0xB8, 0x12, 0x47, 0x10, 0x1E, 0x87, 0xE0, 0x72, 0x6C, 0x3D, 0x38, 0xAD, 0x7A, 0xBB, 0x9C, 0x73, 0x8B, 0xF2, 0x16, 0x6B, 0xA2, + 0x2D, 0x07, 0xEF, 0x4A, 0x0E, 0xB6, 0xA7, 0x19, 0x6B, 0x01, 0x4E, 0xA8, 0xA5, 0x45, 0xF2, 0x0D, 0xA0, 0x0A, 0xD3, 0x20, 0xE3, 0xEC, 0xB3, 0xBB, + 0x57, 0x33, 0x1E, 0xA9, 0x65, 0x82, 0xBD, 0x64, 0xA0, 0x51, 0xC6, 0x7C, 0xC0, 0x8A, 0x39, 0x19, 0x87, 0x85, 0xFA, 0x86, 0xCD, 0x1C, 0xB8, 0x6F, + 0x7B, 0xBE, 0x06, 0x00, 0xF0, 0xAA, 0x91, 0xEA, 0xCA, 0x72, 0xE2, 0x62, 0x52, 0xDB, 0xAA, 0x92, 0xA0, 0x45, 0xD6, 0x21, 0x4B, 0xC3, 0x2E, 0x4F, + 0x96, 0xC0, 0x86, 0x55, 0xE3, 0xEC, 0x2A, 0xC5, 0x9D, 0x55, 0x8C, 0x14, 0x55, 0x87, 0x81, 0x0C, 0x20, 0x0D, 0x17, 0x68, 0xC6, 0x29, 0x40, 0xCD, + 0xA4, 0xA2, 0xCD, 0x73, 0xA2, 0x83, 0x9F, 0x1A, 0x11, 0x43, 0x2A, 0xCC, 0x17, 0x65, 0x34, 0x03, 0x35, 0x00, 0x31, 0x01, 0x57, 0xD2, 0x53, 0x96, + 0x26, 0x02, 0x3A, 0xAF, 0xBA, 0x6D, 0x87, 0xD3, 0x1E, 0xDD, 0x01, 0x3C, 0xC9, 0x26, 0x1C, 0x5F, 0x4C, 0x3E, 0xC0, 0x96, 0x2A, 0x27, 0x18, 0x08, + 0xAD, 0x53, 0xAA, 0xC5, 0x4C, 0xA2, 0x15, 0x4B, 0xA2, 0xF1, 0xC0, 0x23, 0xD1, 0xE8, 0x28, 0x30, 0xB5, 0xE1, 0x68, 0xC9, 0x6C, 0x44, 0x67, 0x59, + 0x34, 0x46, 0x20, 0x8D, 0xC8, 0x80, 0x08, 0x9A, 0x74, 0x07, 0x24, 0x44, 0x28, 0x78, 0x7B, 0x28, 0xF0, 0x8A, 0x50, 0xA1, 0x4C, 0x10, 0xA3, 0xEA, + 0xDD, 0xE1, 0x87, 0xCB, 0x1D, 0x68, 0x04, 0xB4, 0xC8, 0xF5, 0x83, 0x2D, 0x14, 0xBE, 0x34, 0x67, 0xC6, 0xC5, 0x42, 0x0C, 0xAE, 0xEC, 0x79, 0xEA, + 0x86, 0xA9, 0xF1, 0xD1, 0x41, 0x04, 0x3F, 0x79, 0xB8, 0x7A, 0x76, 0x70, 0x33, 0x06, 0x33, 0x9A, 0xC0, 0x77, 0xD2, 0x29, 0xA2, 0x98, 0x27, 0xC4, + 0x92, 0x6F, 0xAC, 0x92, 0x55, 0x58, 0x21, 0x0F, 0x99, 0x42, 0x4E, 0xF4, 0x21, 0xE7, 0x90, 0x2D, 0x7F, 0xCD, 0x35, 0x39, 0xF6, 0xC7, 0x6C, 0x46, + 0xBE, 0x42, 0x65, 0x46, 0x60, 0x16, 0x62, 0x33, 0x9F, 0x99, 0xAD, 0xEE, 0xCC, 0x1D, 0x32, 0x98, 0x21, 0x04, 0x9A, 0xE9, 0xEC, 0x9C, 0xEE, 0x40, + 0x1C, 0xD8, 0x53, 0xD1, 0x24, 0x28, 0xC8, 0x84, 0x78, 0xB8, 0x14, 0xF9, 0x68, 0x60, 0x93, 0x48, 0xEA, 0x99, 0x8C, 0x27, 0x23, 0xA9, 0x16, 0xD7, + 0x9E, 0x92, 0x8B, 0xD4, 0x72, 0x85, 0x5C, 0x80, 0xC6, 0xF4, 0x08, 0x88, 0x37, 0x5A, 0x82, 0x52, 0xC4, 0x00, 0x98, 0x00, 0x99, 0xCD, 0x00, 0xB8, + 0x20, 0x1A, 0xC3, 0x12, 0x51, 0xDE, 0x82, 0xA1, 0xBF, 0x30, 0xA1, 0xC1, 0xB6, 0xA0, 0x1F, 0x76, 0x40, 0x96, 0x20, 0xCC, 0xA2, 0x15, 0xF4, 0x57, + 0x9F, 0x26, 0xB5, 0x7A, 0xAF, 0x92, 0x18, 0xC7, 0x9E, 0x47, 0xA4, 0x12, 0xBA, 0xE4, 0x21, 0x94, 0x1E, 0x3E, 0x24, 0x60, 0xA1, 0x26, 0x98, 0xE8, + 0xAA, 0x0F, 0x48, 0xAA, 0x68, 0x2A, 0x53, 0x30, 0x6E, 0xD2, 0xE6, 0xF7, 0x5B, 0x82, 0xE8, 0x9C, 0x6F, 0x23, 0x6E, 0x12, 0xFF, 0xFF, 0x05, 0xD4, + 0x96, 0x35, 0x18, 0x7F, 0xDA, 0x0F, 0x7F, 0xB5, 0x5B, 0xCC, 0x95, 0x00, 0x99, 0x96, 0x7E, 0xCA, 0xCD, 0xFA, 0x14, 0x94, 0xD1, 0x00, 0xFB, 0x9E, + 0x1F, 0xFE, 0x91, 0x3D, 0x30, 0xDB, 0x18, 0xBF, 0x8A, 0x0E, 0x18, 0xC7, 0xEF, 0xE5, 0x32, 0x36, 0x24, 0x98, 0x1F, 0x38, 0xBC, 0x7A, 0xEC, 0xEA, + 0xFB, 0x71, 0xE7, 0x09, 0x3B, 0xBE, 0x5E, 0x6E, 0xAE, 0xA2, 0xC8, 0x2F, 0xAC, 0xC9, 0x77, 0xCC, 0xFE, 0x4C, 0x03, 0xB9, 0xE5, 0x66, 0xAE, 0xD8, + 0x24, 0x87, 0xA5, 0x9D, 0xDA, 0x65, 0x92, 0xBA, 0xF2, 0x73, 0x1E, 0xB1, 0x2F, 0x34, 0x94, 0x4B, 0x51, 0xF8, 0x56, 0x55, 0x39, 0x1B, 0xDF, 0x87, + 0xF0, 0x96, 0x67, 0xA8, 0x70, 0x77, 0xF5, 0x5D, 0xF4, 0x09, 0xC2, 0x7D, 0xC6, 0x1D, 0x91, 0x23, 0xFE, 0x68, 0x91, 0x3C, 0xB5, 0xC6, 0x4F, 0xC2, + 0x9D, 0xCD, 0xA7, 0x4C, 0x7E, 0xA5, 0x46, 0xB1, 0x5C, 0xD8, 0x28, 0x81, 0x12, 0x15, 0x9E, 0x68, 0xE1, 0xC8, 0x49, 0xDD, 0xF4, 0x72, 0x1A, 0x8F, + 0xDF, 0x0E, 0x69, 0xA6, 0xAF, 0x6B, 0x36, 0x49, 0x51, 0x42, 0xD8, 0xAB, 0x27, 0x09, 0x82, 0x01, 0x74, 0x2C, 0x67, 0x54, 0x60, 0x01, 0x80, 0x53, + 0xB4, 0xD5, 0x0D, 0x19, 0xE2, 0xED, 0x5D, 0x65, 0xD6, 0xD0, 0x11, 0x17, 0x4B, 0x69, 0xD6, 0xB3, 0xF2, 0x59, 0x65, 0x0B, 0x7D, 0x3E, 0x14, 0x22, + 0x32, 0x92, 0xBF, 0x38, 0x48, 0x60, 0x56, 0x7F, 0x9C, 0xAE, 0xB8, 0xEC, 0x52, 0xD9, 0x40, 0x36, 0x41, 0x1F, 0x57, 0x80, 0xD5, 0x28, 0xC4, 0x16, + 0x98, 0x2B, 0x32, 0x00, 0xB1, 0xE7, 0x9E, 0x6E, 0x8F, 0x7A, 0x9A, 0x2E, 0xFE, 0x91, 0x1A, 0x0A, 0x5F, 0xEF, 0x1B, 0xB4, 0x74, 0xFA, 0x38, 0x54, + 0xF1, 0xF2, 0x5C, 0x78, 0x60, 0x60, 0xFF, 0xFF, 0x70, 0x5F, 0xFC, 0x23, 0xE0, 0xDF, 0xCB, 0xAE, 0x62, 0xEE, 0x51, 0xD3, 0xF0, 0x25, 0x07, 0xA8, + 0x81, 0xBF, 0x7C, 0xEC, 0x7E, 0x21, 0x19, 0x24, 0xFF, 0x7F, 0x63, 0xFF, 0x78, 0x8C, 0xE3, 0xA7, 0xA2, 0xAB, 0x62, 0xB2, 0x61, 0xB7, 0xA1, 0xA8, + 0x22, 0x0B, 0x62, 0xE3, 0xE9, 0x55, 0x12, 0x38, 0x67, 0x74, 0xD5, 0x70, 0x2E, 0x84, 0x1E, 0x08, 0x09, 0x40, 0x7E, 0x43, 0xC4, 0x99, 0x61, 0x70, + 0xB6, 0x35, 0x5A, 0x36, 0xE3, 0x31, 0x38, 0x06, 0x43, 0xCC, 0xF5, 0x65, 0x48, 0x34, 0x07, 0x10, 0x2E, 0x11, 0x26, 0x3D, 0xB1, 0xA5, 0x17, 0xA8, + 0xE7, 0x74, 0xAE, 0xBA, 0x8B, 0x8E, 0x50, 0x4B, 0xA0, 0xF3, 0xFA, 0x04, 0x4D, 0x58, 0x17, 0xD2, 0x96, 0xF5, 0x25, 0x5D, 0xB8, 0xBF, 0xD2, 0x97, + 0x3B, 0xA6, 0x3E, 0x5A, 0xC3, 0xEA, 0x68, 0xBB, 0xAF, 0x2E, 0x60, 0xEF, 0xD1, 0xA3, 0x76, 0x29, 0xDD, 0xA7, 0xFE, 0xA1, 0xA7, 0x6A, 0x2A, 0x7A, + 0xAD, 0x9E, 0xCA, 0xDC, 0xFB, 0xB3, 0x5F, 0xF3, 0x96, 0x91, 0xA9, 0x02, 0x4C, 0xD8, 0x6E, 0x54, 0xCC, 0x0E, 0x89, 0x68, 0x02, 0x2E, 0x05, 0xBB, + 0x07, 0x11, 0x9A, 0x10, 0xE7, 0xFA, 0x44, 0xDC, 0x65, 0x9A, 0x79, 0x22, 0x04, 0xCC, 0x11, 0x70, 0x7B, 0x9B, 0x0F, 0x64, 0x6B, 0xBC, 0x21, 0xD2, + 0xBA, 0x01, 0x4F, 0xB6, 0x71, 0xFE, 0xB6, 0xA1, 0xD4, 0x5E, 0xC9, 0x10, 0x5B, 0x14, 0xA5, 0xDA, 0x6F, 0x58, 0x6D, 0x6D, 0xB5, 0x41, 0x10, 0x51, + 0xDA, 0x87, 0x29, 0x68, 0x55, 0x56, 0xF6, 0x8C, 0x9C, 0x33, 0xB8, 0x07, 0xB8, 0x08, 0xC2, 0x20, 0x49, 0x84, 0xD9, 0x9C, 0x7D, 0xA1, 0xDA, 0x28, + 0x91, 0xE3, 0xC8, 0x81, 0x7F, 0xD0, 0x5F, 0xF0, 0xDB, 0x3E, 0x88, 0x3F, 0x05, 0x41, 0x04, 0x29, 0x1B, 0x10, 0x57, 0x37, 0x3F, 0xE1, 0x1A, 0xDC, + 0x2B, 0x28, 0xE8, 0x0C, 0x28, 0x0D, 0xF3, 0x22, 0x43, 0x6C, 0x1C, 0x61, 0x2B, 0x74, 0xB5, 0xBB, 0xA0, 0x8E, 0x70, 0x8B, 0xCF, 0x9F, 0xA0, 0xE3, + 0xDC, 0x63, 0x31, 0xE8, 0xF7, 0x65, 0x05, 0x19, 0xC1, 0x2A, 0xC0, 0x08, 0x95, 0x47, 0xEB, 0x74, 0x24, 0x06, 0x13, 0x70, 0xF7, 0xF4, 0x2F, 0x37, + 0x1B, 0x1D, 0xBA, 0x5D, 0x88, 0x26, 0xB3, 0xDE, 0x27, 0xA3, 0x4C, 0x56, 0x17, 0x31, 0x4E, 0xCD, 0x8A, 0x41, 0x05, 0x81, 0x73, 0x9B, 0xF8, 0xC3, + 0x7C, 0x1F, 0xF6, 0xBB, 0xFE, 0xFD, 0x03, 0xF9, 0x3F, 0x24, 0x05, 0x73, 0x0A, 0x28, 0xCD, 0xC0, 0xEA, 0xDC, 0xDD, 0xAE, 0x09, 0x02, 0x6F, 0xCB, + 0x0D, 0xB8, 0x0C, 0x00, 0x37, 0xAC, 0x0B, 0x5C, 0xD2, 0xE9, 0xFE, 0xF5, 0x81, 0xAB, 0x96, 0xDD, 0x41, 0xA0, 0xF8, 0x6D, 0xED, 0x35, 0xA0, 0xC6, + 0x8C, 0xAF, 0xC3, 0x4A, 0xC2, 0xE3, 0x83, 0xD0, 0xAF, 0xF1, 0x20, 0x0B, 0xBE, 0xF4, 0x8E, 0xE4, 0xBE, 0x03, 0x9F, 0x94, 0x31, 0xF0, 0x0E, 0xB9, + 0x78, 0xB7, 0xFF, 0xE5, 0xFB, 0xF8, 0xFF, 0x7F, 0x16, 0xB1, 0xD0, 0x7F, 0xFD, 0xA0, 0xFF, 0x43, 0x20, 0x59, 0xAD, 0x4E, 0x99, 0xF5, 0x44, 0x0B, + 0x82, 0x32, 0x6F, 0xA1, 0xD1, 0x99, 0x17, 0x0E, 0x0A, 0xDD, 0xEC, 0xE1, 0xF5, 0xC3, 0xB0, 0x02, 0x46, 0x1E, 0x01, 0x72, 0x3B, 0xCD, 0x01, 0x24, + 0xD4, 0x89, 0x8B, 0x7C, 0x26, 0x4A, 0x10, 0xA0, 0xF1, 0x64, 0x1D, 0x5D, 0xA3, 0xAE, 0x02, 0x1E, 0x22, 0xCE, 0x5E, 0xF7, 0x32, 0xF7, 0xB0, 0xCE, + 0x70, 0x3C, 0x84, 0xF3, 0x08, 0x3E, 0xC4, 0xEF, 0xFF, 0x1F, 0x40, 0xD1, 0x07, 0x52, 0xB5, 0xFA, 0x79, 0xE6, 0x82, 0xF0, 0xC7, 0x23, 0x92, 0x8D, + 0x7D, 0x98, 0x1B, 0x4C, 0x18, 0x7F, 0xE8, 0x6F, 0x22, 0x00, 0x07, 0x44, 0x78, 0x46, 0x0F, 0xCF, 0xFF, 0x2D, 0x2C, 0xE2, 0x1F, 0x0E, 0xAF, 0x08, + 0x9B, 0xF8, 0xA7, 0xFF, 0x07, 0xD1, 0x32, 0xA0, 0x80, 0x51, 0xB5, 0xD9, 0x80, 0x2D, 0xB8, 0xC3, 0xA1, 0x6B, 0xBB, 0x13, 0xBB, 0x8D, 0x12, 0x06, + 0xC9, 0x20, 0xCC, 0x66, 0xFC, 0x85, 0xEA, 0xF6, 0xD5, 0x74, 0xA0, 0x56, 0x4A, 0x88, 0x29, 0x5D, 0x5C, 0x39, 0x5C, 0x4E, 0x5D, 0x5B, 0x74, 0xB5, + 0xD6, 0x99, 0x84, 0xAC, 0x3D, 0x33, 0x3A, 0xF2, 0x86, 0x13, 0x58, 0x38, 0x10, 0x5C, 0xC1, 0x28, 0xB4, 0xAA, 0x3C, 0xAB, 0x6A, 0xBC, 0xBC, 0x63, + 0xF0, 0x9A, 0x61, 0x41, 0xA7, 0xAE, 0xC0, 0x4A, 0x0B, 0xAA, 0x25, 0x98, 0x54, 0x40, 0x3C, 0x6A, 0xA7, 0x5E, 0x60, 0xA5, 0x9E, 0x5C, 0x44, 0x15, + 0xF0, 0x85, 0x4A, 0xF3, 0x91, 0xE0, 0xAB, 0xE7, 0xA9, 0x03, 0x28, 0x02, 0x3B, 0xA9, 0x29, 0x75, 0xA0, 0xAD, 0x93, 0xDE, 0xCF, 0x01, 0x48, 0x97, + 0x26, 0x42, 0x2A, 0x26, 0xD4, 0x22, 0xBA, 0x25, 0x4C, 0x36, 0x03, 0x4E, 0x5E, 0x94, 0x64, 0xEF, 0x94, 0x66, 0x8C, 0x90, 0x80, 0x03, 0xEF, 0x90, + 0x0C, 0x68, 0x15, 0xD8, 0xF9, 0x70, 0x24, 0x76, 0xD7, 0x1A, 0x45, 0xC5, 0x28, 0xB2, 0x80, 0xA7, 0x12, 0x58, 0x73, 0x1C, 0x70, 0x22, 0x02, 0x1D, + 0xFD, 0xA1, 0x74, 0x0A, 0x91, 0xE1, 0xE0, 0x90, 0xA3, 0x10, 0x21, 0x22, 0x43, 0x82, 0x4C, 0x8F, 0x00, 0xCD, 0xB2, 0xFE, 0xE8, 0x52, 0x69, 0xFB, + 0x09, 0xEF, 0xFA, 0xC4, 0xB0, 0xF6, 0x10, 0x08, 0x00, 0x26, 0x20, 0x69, 0x97, 0x87, 0xAA, 0xCA, 0x4E, 0x58, 0x80, 0x00, 0x6E, 0x3B, 0x3A, 0x15, + 0x9E, 0x72, 0x44, 0x24, 0x00, 0xE0, 0x67, 0x8F, 0x6D, 0xA0, 0xE1, 0xF0, 0xD0, 0xC0, 0x7A, 0x23, 0x64, 0x83, 0x8D, 0x39, 0xAD, 0xA6, 0xD5, 0xC5, + 0x27, 0x0D, 0x8F, 0xCF, 0xF4, 0x1C, 0x66, 0xFE, 0x0B, 0x99, 0x31, 0x8C, 0x18, 0xFA, 0x07, 0x4C, 0xCD, 0xF8, 0x42, 0x36, 0xE2, 0x62, 0x87, 0xB6, + 0xDC, 0x4C, 0xDA, 0x58, 0xF0, 0x26, 0x60, 0xE2, 0x10, 0xA1, 0xAC, 0xE1, 0x3E, 0xD1, 0x4F, 0xA3, 0x49, 0xB0, 0xA2, 0x13, 0x60, 0x4A, 0x98, 0x2B, + 0x9F, 0x31, 0xBF, 0xAC, 0x47, 0x56, 0x98, 0x14, 0xF0, 0xE5, 0xDD, 0x62, 0xF5, 0x8D, 0xE1, 0xEA, 0xC5, 0x41, 0xCA, 0x94, 0x6C, 0xEC, 0x29, 0x7D, + 0x59, 0xBC, 0x54, 0x1D, 0xB5, 0x84, 0x62, 0x54, 0x49, 0xE5, 0x49, 0x25, 0x8E, 0x16, 0xEC, 0x10, 0x1E, 0x19, 0x32, 0x21, 0x05, 0x0A, 0x50, 0x32, + 0xDD, 0x06, 0xF1, 0xE2, 0x9A, 0x2C, 0x9B, 0x91, 0xAC, 0x8B, 0x10, 0x99, 0x21, 0x09, 0x92, 0x2F, 0x7F, 0x74, 0xE1, 0xE5, 0x98, 0x48, 0x08, 0xB7, + 0x33, 0xD2, 0x35, 0x5E, 0x9E, 0x00, 0x32, 0x8A, 0x42, 0x94, 0x31, 0xB6, 0xF0, 0x47, 0x6D, 0x9D, 0x8F, 0x2B, 0x97, 0x3E, 0x5A, 0xF6, 0x7A, 0x08, + 0xBD, 0xF2, 0x30, 0xD2, 0xB1, 0x68, 0xD5, 0xAB, 0xC8, 0x65, 0x4E, 0x8C, 0x13, 0xB0, 0x83, 0x4E, 0x16, 0xF1, 0xCE, 0xCA, 0xE1, 0xDE, 0xAF, 0x0E, + 0x19, 0x3E, 0x70, 0x86, 0x54, 0xC1, 0x64, 0xFA, 0xE4, 0x0A, 0xD8, 0x1C, 0x88, 0x4B, 0x7E, 0x07, 0x4B, 0x23, 0xD4, 0xB0, 0xFC, 0xCA, 0x13, 0x48, + 0xA7, 0x8A, 0x09, 0x6C, 0xE8, 0xF6, 0x4F, 0x28, 0x4C, 0xB0, 0x02, 0xD2, 0xAB, 0x43, 0x19, 0x99, 0x35, 0x90, 0xC9, 0x7A, 0x1E, 0x84, 0x49, 0x84, + 0x43, 0xFC, 0xD1, 0x54, 0x32, 0x38, 0x0A, 0xE6, 0x07, 0xB2, 0xF7, 0xDB, 0x0A, 0x59, 0x37, 0xDA, 0xD2, 0xB6, 0xC5, 0xDF, 0x36, 0x31, 0x2E, 0x1B, + 0xE8, 0x0F, 0x5B, 0x11, 0x4D, 0x13, 0x2A, 0x7A, 0x4B, 0xA9, 0xFE, 0x40, 0xBA, 0x84, 0xAF, 0xFA, 0xFE, 0xA0, 0x0F, 0xA9, 0xDB, 0xF3, 0x18, 0xBA, + 0xEB, 0xDE, 0x4E, 0x23, 0x44, 0x10, 0x19, 0x34, 0x5F, 0xCD, 0x77, 0x95, 0x35, 0xA8, 0x62, 0xBA, 0x85, 0xA6, 0xA9, 0x25, 0x00, 0x0A, 0x9D, 0xC5, + 0xC7, 0x4B, 0x3E, 0x70, 0x8A, 0x05, 0x08, 0x5C, 0xBF, 0xC9, 0xD4, 0x1D, 0x22, 0x1F, 0x7C, 0x97, 0xB0, 0x4B, 0x0B, 0xE6, 0x0D, 0x13, 0x83, 0xA7, + 0x34, 0x7B, 0xEB, 0x96, 0x9F, 0x2E, 0x99, 0x7E, 0x60, 0x2C, 0xB1, 0x2D, 0x60, 0x61, 0x5F, 0x13, 0x60, 0x5B, 0x24, 0x3E, 0x83, 0x15, 0x75, 0x15, + 0x8B, 0x90, 0x87, 0x20, 0xD1, 0xBA, 0x63, 0x00, 0xFC, 0xFF, 0x59, 0x7E, 0x38, 0xFC, 0xF7, 0x43, 0x2B, 0x55, 0x6B, 0x54, 0x58, 0x18, 0xE6, 0x0F, + 0xC3, 0x04, 0xC5, 0xFF, 0xA2, 0x94, 0x60, 0x37, 0x87, 0x11, 0x60, 0x43, 0x61, 0x11, 0xDF, 0xF3, 0x63, 0x9A, 0x71, 0xBE, 0x32, 0x11, 0x8B, 0xAA, + 0x95, 0x13, 0xF9, 0x0F, 0xB3, 0x08, 0xAB, 0xF8, 0x9C, 0x39, 0x6A, 0x57, 0x51, 0xFF, 0xD1, 0xDF, 0xFA, 0x05, 0xF2, 0xA6, 0x32, 0x57, 0x3E, 0x56, + 0x8E, 0x90, 0x57, 0x28, 0xE7, 0x79, 0x5F, 0x99, 0x30, 0xDF, 0x51, 0x46, 0xF1, 0x8B, 0x94, 0xF5, 0xAF, 0x38, 0xC6, 0xFC, 0x9C, 0x0D, 0x36, 0xA0, + 0x4A, 0xF8, 0x73, 0x02, 0x04, 0xD4, 0x37, 0x14, 0x74, 0x46, 0xF4, 0xA7, 0x09, 0x02, 0xD1, 0x1E, 0xCD, 0xC9, 0xDC, 0x93, 0x0E, 0x31, 0x7D, 0x6E, + 0x4E, 0x65, 0x69, 0x6E, 0xE5, 0x4D, 0x8D, 0x0B, 0x39, 0xD2, 0xE9, 0x83, 0x74, 0x21, 0xE8, 0x25, 0x27, 0x9C, 0xF5, 0x7C, 0x57, 0x62, 0x04, 0x41, + 0x21, 0x01, 0x6B, 0x82, 0xA3, 0xDB, 0x51, 0x22, 0xCD, 0x7C, 0xC7, 0x3C, 0x86, 0x2E, 0xB5, 0xFD, 0xD3, 0x10, 0xF6, 0xFF, 0x7F, 0xB9, 0xDC, 0x6B, + 0xDB, 0xE2, 0xDB, 0x85, 0x7E, 0x3F, 0x24, 0x7D, 0x24, 0x3F, 0xF6, 0xAF, 0x7E, 0xD5, 0x9F, 0xFA, 0x51, 0xBF, 0xE9, 0x37, 0xFF, 0xCB, 0xCF, 0xF8, + 0x0B, 0x3F, 0x8E, 0x3F, 0xE3, 0xEB, 0xFD, 0x12, 0x9F, 0xC3, 0xBF, 0xF0, 0xD9, 0x7E, 0xD9, 0xBF, 0xFA, 0x56, 0x9A, 0x30, 0xBB, 0x4F, 0xE6, 0x11, + 0x4B, 0x64, 0x08, 0xF9, 0x3C, 0x9F, 0xA5, 0x07, 0x05, 0x93, 0x6F, 0xC9, 0x65, 0x59, 0x2F, 0xF2, 0x6D, 0x9B, 0xBA, 0x7D, 0x8F, 0x3B, 0x04, 0xA0, + 0xA0, 0xA1, 0x0C, 0xC7, 0x47, 0x22, 0x90, 0xA6, 0x67, 0x7B, 0xD6, 0x54, 0xF7, 0x27, 0x0C, 0x85, 0x3C, 0xA8, 0x81, 0xC3, 0x2F, 0x91, 0x0D, 0x43, + 0xFB, 0x0F, 0x68, 0x10, 0x17, 0x10, 0x03, 0xFD, 0xCA, 0x5F, 0x61, 0x46, 0xEE, 0xF9, 0xC4, 0x4F, 0x10, 0x0C, 0x25, 0x3F, 0x4A, 0x05, 0x20, 0x09, + 0xA2, 0x69, 0x21, 0xFD, 0x14, 0x25, 0x13, 0xAD, 0x12, 0x05, 0x03, 0xC2, 0x44, 0x70, 0x1A, 0x81, 0x29, 0x12, 0xD3, 0xC0, 0xD8, 0x26, 0xA0, 0xCB, + 0x0B, 0x4A, 0x97, 0x70, 0x38, 0xF0, 0x53, 0xFE, 0x09, 0x7F, 0xEE, 0x9F, 0x49, 0xF6, 0x04, 0x07, 0x4B, 0xE2, 0x2D, 0xA6, 0xCC, 0x15, 0x18, 0x7F, + 0x97, 0x2F, 0x26, 0x0C, 0xB2, 0xC2, 0xE9, 0x6C, 0xF8, 0xF2, 0xAB, 0xC9, 0x3F, 0xA2, 0x34, 0xB7, 0x13, 0x2A, 0xD7, 0xC5, 0x48, 0xD2, 0x11, 0xD9, + 0x90, 0x00, 0x41, 0x34, 0x02, 0x56, 0x90, 0xB9, 0x12, 0xB7, 0x9C, 0xAA, 0x9C, 0x20, 0x69, 0x9C, 0x09, 0x29, 0x10, 0x22, 0x6F, 0xE1, 0x74, 0x00, + 0x0F, 0x54, 0xDF, 0x26, 0xA7, 0xB8, 0x51, 0x69, 0x3B, 0x92, 0xD3, 0xB6, 0xC3, 0x56, 0xA5, 0x66, 0x63, 0x52, 0x5A, 0x13, 0x60, 0xA6, 0x46, 0xF6, + 0xD5, 0x60, 0x0A, 0x43, 0x1A, 0x86, 0x09, 0x68, 0x0E, 0x58, 0xD8, 0x7F, 0x67, 0x1A, 0x34, 0x98, 0xFD, 0x28, 0x7B, 0xF1, 0x47, 0x26, 0x9A, 0x39, + 0x06, 0xA4, 0x87, 0x58, 0x92, 0x43, 0xD2, 0x4F, 0xA0, 0xB1, 0x97, 0x4B, 0x59, 0xC1, 0xB8, 0xAE, 0xA8, 0x1C, 0x54, 0xE6, 0xCB, 0x5A, 0x50, 0x4E, + 0x94, 0x6B, 0x2B, 0x5F, 0x6E, 0x81, 0x28, 0xC7, 0xCE, 0x09, 0x96, 0xAF, 0x98, 0x90, 0x7A, 0xC2, 0x8B, 0x12, 0xCA, 0x01, 0x95, 0x36, 0x23, 0x81, + 0x55, 0xE5, 0x98, 0xC2, 0x87, 0x69, 0xF2, 0xC5, 0xFF, 0x0D, 0xD7, 0x30, 0xE6, 0xBB, 0xE9, 0x32, 0x6A, 0x5C, 0x44, 0x0E, 0xFB, 0xE7, 0xC2, 0x23, + 0xFE, 0x63, 0x12, 0x7E, 0x76, 0x70, 0xC3, 0x2F, 0x7F, 0xEA, 0xEA, 0x4D, 0xC2, 0x65, 0x24, 0xAA, 0x94, 0x21, 0xA6, 0x76, 0x29, 0x8F, 0x52, 0x85, + 0x1C, 0x4D, 0xD5, 0x16, 0xD1, 0x16, 0xB5, 0x89, 0xF5, 0x21, 0xE5, 0x07, 0x34, 0x91, 0x32, 0x92, 0x0E, 0x5A, 0xCE, 0xE9, 0x0D, 0xE5, 0x27, 0xBC, + 0xF4, 0xA2, 0x93, 0xF2, 0xA6, 0x1E, 0x64, 0x3C, 0x44, 0x8F, 0xCB, 0x6C, 0xB4, 0x3D, 0xA6, 0xA1, 0xAB, 0x1F, 0x8D, 0xFF, 0xE8, 0xF3, 0x18, 0x86, + 0x1E, 0x1B, 0xA3, 0xEA, 0x4F, 0x6E, 0x69, 0x9D, 0xAE, 0x1B, 0x17, 0xA0, 0x9F, 0xCA, 0x0C, 0xCE, 0x13, 0xE8, 0x90, 0xE8, 0x72, 0x27, 0x35, 0x9C, + 0x9D, 0x86, 0xC5, 0x1C, 0x7E, 0x23, 0x21, 0xE2, 0xCD, 0xDB, 0xD6, 0xFB, 0xC6, 0xB8, 0xF5, 0xE2, 0x54, 0xF1, 0x26, 0x85, 0xE0, 0xAB, 0x38, 0xBD, + 0x05, 0xFF, 0xFF, 0x0F, 0x34, 0xEB, 0xA4, 0x68, 0xE1, 0x27, 0x5C, 0xE6, 0xB9, 0xA2, 0x67, 0xC0, 0x05, 0xDB, 0xC4, 0x15, 0x3C, 0x54, 0x67, 0xC0, + 0x2C, 0x02, 0xB4, 0x65, 0xD0, 0x19, 0x5A, 0xCC, 0xC1, 0x6B, 0x01, 0xE0, 0xAA, 0x73, 0x5C, 0x83, 0x07, 0x4E, 0x5C, 0xED, 0x0D, 0xA5, 0xA7, 0x8D, + 0x09, 0x71, 0x0D, 0xAB, 0x12, 0x9A, 0x42, 0x3A, 0x66, 0x06, 0xCA, 0xE7, 0x24, 0x8B, 0x0C, 0xCE, 0x92, 0xD5, 0x01, 0x30, 0x75, 0xCB, 0x2E, 0x96, + 0x8B, 0x4D, 0xA0, 0xC5, 0xA6, 0x88, 0x05, 0x27, 0x97, 0xF6, 0xD5, 0xA4, 0xFA, 0x88, 0x51, 0x45, 0x33, 0xD1, 0x20, 0x10, 0x3C, 0x31, 0x84, 0xAA, + 0x0D, 0xB1, 0x94, 0x8A, 0x19, 0x34, 0x03, 0x5D, 0x41, 0x22, 0x87, 0x05, 0xF7, 0xC0, 0xD3, 0x22, 0x00, 0x4C, 0xEB, 0x1A, 0xE8, 0x66, 0x0A, 0x04, + 0xBC, 0x11, 0xC0, 0xC3, 0x33, 0xDE, 0x49, 0x23, 0x58, 0x91, 0x0C, 0x3C, 0xD8, 0xCC, 0xA8, 0x97, 0x2B, 0xDA, 0x3D, 0xC0, 0x91, 0xCB, 0x77, 0xC3, + 0x9D, 0x55, 0xFA, 0xE3, 0xE2, 0x9E, 0xC3, 0x5D, 0xE0, 0xE3, 0x46, 0x41, 0xF3, 0x06, 0x7A, 0xEB, 0x50, 0xE2, 0xF0, 0x83, 0xD7, 0xFC, 0x02, 0x34, + 0xC2, 0xB7, 0x1A, 0xA8, 0x84, 0x21, 0xF7, 0x32, 0xED, 0xAF, 0x47, 0x64, 0x1F, 0xB1, 0xDD, 0xCA, 0x16, 0x59, 0x01, 0x85, 0x1D, 0x68, 0x8E, 0xEC, + 0xC6, 0xB7, 0x33, 0x30, 0x41, 0x16, 0x81, 0xB8, 0xA7, 0x45, 0x30, 0x97, 0x57, 0xD4, 0x1D, 0x72, 0xF3, 0x9E, 0x58, 0x07, 0x12, 0x9A, 0xCF, 0x9B, + 0xF5, 0xA3, 0xA5, 0xB9, 0xA7, 0xAD, 0x4F, 0x5D, 0xCD, 0x00, 0x7E, 0xDD, 0x04, 0x93, 0x8C, 0x60, 0xA8, 0x96, 0x81, 0x25, 0x83, 0x85, 0x77, 0x25, + 0x5F, 0x32, 0x05, 0xA2, 0x5D, 0xAC, 0x87, 0xBC, 0xC2, 0x6A, 0xB7, 0x20, 0x45, 0x5E, 0x7C, 0xB0, 0xB3, 0xF0, 0xAA, 0x13, 0xE9, 0x1E, 0x74, 0xF5, + 0x26, 0x8D, 0x6A, 0x09, 0x00, 0x78, 0x54, 0xEA, 0x44, 0x2D, 0x1A, 0x24, 0x38, 0x14, 0x93, 0xD3, 0x08, 0xAB, 0x10, 0xA8, 0x6C, 0x92, 0x4F, 0x5E, + 0x06, 0x9E, 0xC1, 0x61, 0xCF, 0x1E, 0x37, 0x72, 0xE7, 0x8E, 0x12, 0x4E, 0xA7, 0xAA, 0x4D, 0x29, 0x96, 0x22, 0x67, 0x6F, 0x31, 0xB8, 0xE4, 0xAA, + 0xBA, 0x62, 0x2B, 0xF0, 0x5D, 0x31, 0xFF, 0x20, 0xBA, 0x4F, 0x42, 0xF1, 0x01, 0xBF, 0xFE, 0x1B, 0x7F, 0x8A, 0x3C, 0x30, 0x37, 0xCC, 0xF7, 0xBC, + 0xC6, 0xF7, 0x77, 0xED, 0x6A, 0xBB, 0x84, 0xBD, 0xC3, 0x7E, 0x62, 0x2F, 0xEF, 0x85, 0x3B, 0x90, 0xFD, 0x72, 0x0F, 0xAC, 0x0D, 0xEB, 0xBD, 0xCE, + 0xAC, 0x49, 0xEB, 0xD8, 0xFA, 0xB6, 0x76, 0xB5, 0x52, 0xCF, 0xD4, 0x3E, 0x75, 0x52, 0x3D, 0x55, 0xD3, 0xEC, 0x3F, 0xBB, 0xD2, 0x4E, 0xB5, 0x7F, + 0xED, 0x6C, 0xFB, 0x84, 0xFD, 0x6E, 0x4F, 0xB1, 0x07, 0xEE, 0x76, 0x35, 0x88, 0x5A, 0x45, 0x2D, 0x58, 0x1F, 0xD6, 0x8F, 0x35, 0x91, 0xFA, 0x5C, + 0x23, 0xA9, 0x37, 0x3F, 0xEA, 0x07, 0xCC, 0xEF, 0x7A, 0x07, 0x3D, 0xCC, 0x6F, 0xCC, 0x03, 0xFB, 0x0F, 0xFD, 0xD1, 0x1F, 0xCD, 0xF7, 0xDE, 0x0D, + 0xDF, 0xFA, 0x8E, 0xDF, 0xAD, 0x77, 0xEC, 0xE7, 0x3E, 0x87, 0x7F, 0x20, 0xE7, 0x73, 0xDC, 0x7C, 0x48, 0xBE, 0xE3, 0x63, 0x7D, 0x9D, 0x1C, 0xC2, + 0xC7, 0xF0, 0x49, 0xFC, 0x1B, 0xC1, 0xDF, 0xC6, 0xCF, 0x90, 0x07, 0xF9, 0x9D, 0x9F, 0x32, 0xF7, 0xCC, 0x57, 0xF3, 0xB7, 0x3C, 0x27, 0x9F, 0x91, + 0x43, 0xC8, 0x9F, 0xFE, 0xE4, 0xD7, 0xF0, 0x5D, 0xAE, 0xE4, 0x8E, 0x5F, 0x69, 0x5C, 0x22, 0xCF, 0xA2, 0x52, 0x6E, 0xC1, 0x9B, 0xE3, 0x65, 0xCE, + 0x6F, 0x35, 0x06, 0xCC, 0x65, 0xCA, 0xC0, 0xAA, 0x38, 0xC6, 0x48, 0xE4, 0x83, 0x3F, 0x45, 0xD8, 0x2D, 0xDC, 0xD6, 0xBB, 0xD8, 0x22, 0x03, 0x62, + 0xDB, 0x17, 0x37, 0x99, 0x22, 0xA0, 0x0A, 0xF9, 0xF4, 0x2E, 0x4F, 0xC6, 0x20, 0xAD, 0x5B, 0x96, 0xFA, 0xA9, 0x59, 0x55, 0xE8, 0xC4, 0x16, 0xA3, + 0x7F, 0x33, 0x5E, 0x60, 0x3B, 0x25, 0x5E, 0x82, 0x41, 0x86, 0x47, 0x92, 0x21, 0xF1, 0x2C, 0x9B, 0xE4, 0x2B, 0x76, 0x8F, 0x45, 0x2C, 0xCB, 0x84, + 0x57, 0xC4, 0xC5, 0xD7, 0xA2, 0x14, 0xF7, 0x66, 0x67, 0xEB, 0x82, 0x3A, 0xBE, 0xB6, 0xD6, 0xDE, 0xE3, 0x57, 0x6F, 0xAA, 0x54, 0x39, 0x50, 0x38, + 0x85, 0x91, 0x63, 0xBA, 0xF6, 0xE0, 0xDC, 0x58, 0xDB, 0x22, 0x6E, 0xD2, 0x4E, 0x9D, 0x51, 0x42, 0x53, 0x51, 0x54, 0xCB, 0xD0, 0x08, 0x89, 0x7C, + 0x64, 0x11, 0xA1, 0x25, 0x7B, 0x94, 0x52, 0x19, 0x3A, 0x73, 0x04, 0x19, 0x93, 0x1A, 0x14, 0x05, 0xE2, 0xF5, 0xFF, 0xD7, 0x91, 0xFB, 0x3F, 0x06, + 0x46, 0x89, 0x6D, 0xCF, 0x44, 0xA4, 0x39, 0x8E, 0xE4, 0xB5, 0xBA, 0xA1, 0x7B, 0x93, 0x34, 0x90, 0x12, 0x81, 0x97, 0x85, 0x43, 0x15, 0x9D, 0x7C, + 0xB7, 0x08, 0x06, 0x95, 0x04, 0x84, 0x53, 0xB6, 0x18, 0xA5, 0x34, 0xB0, 0x8C, 0x22, 0x21, 0xD4, 0xAC, 0x6B, 0x41, 0xEF, 0x47, 0x34, 0x42, 0x61, + 0x81, 0x99, 0x7B, 0x63, 0x14, 0xB7, 0x07, 0xEC, 0xED, 0x90, 0x27, 0x4F, 0x8C, 0xF8, 0x3A, 0x79, 0x95, 0x69, 0xF2, 0xA4, 0xC7, 0x0A, 0x12, 0x13, + 0x1A, 0x29, 0x5B, 0xA0, 0x2E, 0xF2, 0x12, 0x02, 0x3E, 0x4A, 0x9E, 0x92, 0x6A, 0xB5, 0xA6, 0x92, 0xA4, 0x79, 0xD1, 0x46, 0x58, 0x9B, 0xED, 0x40, + 0x67, 0x46, 0xE1, 0xC1, 0x1C, 0x88, 0xC2, 0xAC, 0x03, 0xD0, 0xFC, 0xA8, 0x9B, 0x5F, 0x41, 0xF7, 0x5C, 0x5B, 0x63, 0xB4, 0x36, 0xD1, 0xD1, 0xEB, + 0xC8, 0x2A, 0x63, 0x3E, 0xE2, 0x53, 0xBA, 0xAA, 0x24, 0xAB, 0xAA, 0x00, 0x97, 0x9C, 0x1A, 0x76, 0x8A, 0xEA, 0x36, 0x7B, 0xAA, 0xA2, 0xF0, 0xF4, + 0x29, 0xC5, 0x5F, 0x9D, 0xE9, 0xDA, 0xCE, 0x79, 0xC7, 0xE5, 0x39, 0xCD, 0xF0, 0xC7, 0x5E, 0xE9, 0x87, 0x04, 0x63, 0xCE, 0xD7, 0x32, 0x69, 0x87, + 0xCC, 0x5F, 0xC4, 0x2E, 0x1F, 0x95, 0x81, 0x06, 0x9B, 0x29, 0x9A, 0x53, 0xB8, 0x35, 0xFF, 0x50, 0xC9, 0x0D, 0x46, 0x68, 0x4D, 0x8C, 0x75, 0xAB, + 0x40, 0x59, 0x72, 0x5B, 0xAD, 0x78, 0xB6, 0x7D, 0x76, 0x2E, 0x80, 0xE4, 0xA4, 0x2A, 0xDB, 0xC4, 0x1A, 0x50, 0xA4, 0xED, 0x82, 0x26, 0x85, 0x11, + 0x44, 0xF0, 0xFC, 0xCC, 0x38, 0x6C, 0x32, 0x6B, 0x28, 0x62, 0x16, 0x92, 0xB8, 0xA4, 0xE4, 0xB4, 0xFC, 0x4C, 0x1D, 0x0B, 0x81, 0xD2, 0x19, 0xAC, + 0x4A, 0x89, 0x53, 0x1B, 0xE4, 0x89, 0x42, 0xA4, 0x4D, 0x43, 0x76, 0x40, 0x7C, 0xC4, 0xAF, 0x1C, 0xFD, 0x56, 0xEC, 0x7D, 0x04, 0x1A, 0xF5, 0x0B, + 0x40, 0x14, 0xC9, 0x8C, 0xA0, 0x9B, 0xB6, 0x79, 0x3D, 0x5E, 0x68, 0x76, 0x9C, 0x65, 0x22, 0x16, 0x4D, 0x1D, 0x6A, 0x68, 0xA1, 0x43, 0x8B, 0xC5, + 0xF0, 0xEF, 0x45, 0x97, 0xD3, 0x2B, 0xEC, 0x81, 0x3A, 0x26, 0xD4, 0x1B, 0xA3, 0x73, 0x80, 0x16, 0x0E, 0xE6, 0x10, 0x00, 0x9C, 0x04, 0xC7, 0x98, + 0x5F, 0xE0, 0x92, 0x1E, 0xC1, 0xC1, 0xBA, 0xFD, 0xE7, 0xA3, 0xC3, 0x0D, 0x28, 0x43, 0xDC, 0x73, 0xBC, 0x8C, 0xDB, 0x04, 0x8F, 0xC7, 0xE7, 0xE0, + 0x67, 0xF0, 0x95, 0x7C, 0x24, 0x5F, 0x78, 0x9F, 0xF2, 0x97, 0x77, 0xCF, 0xBF, 0xDE, 0x80, 0xB8, 0x8E, 0x37, 0xE2, 0x98, 0xF8, 0x28, 0x0E, 0x8B, + 0xDF, 0xF8, 0x57, 0x2D, 0x2F, 0x62, 0x28, 0x22, 0x9F, 0xAA, 0xB5, 0xEC, 0x45, 0x03, 0x32, 0xD1, 0xBE, 0x31, 0x20, 0x1C, 0xC4, 0x92, 0x58, 0x20, + 0x85, 0x11, 0xC6, 0xB2, 0x22, 0x97, 0xF1, 0xAB, 0x28, 0xD1, 0xCB, 0x05, 0xB0, 0x6B, 0x6B, 0x93, 0xEB, 0xA8, 0x71, 0x4B, 0x02, 0xAD, 0x56, 0xD7, + 0xA8, 0x6D, 0x98, 0xE3, 0x18, 0x8C, 0x43, 0x4E, 0x06, 0x49, 0x6B, 0x26, 0x8C, 0x16, 0x9E, 0x7A, 0x40, 0xE1, 0xCD, 0xC9, 0xF4, 0x86, 0x5D, 0x68, + 0x94, 0xDF, 0x1E, 0x0F, 0x68, 0x2C, 0x9F, 0x45, 0x84, 0x0E, 0x27, 0x71, 0x59, 0x24, 0x0E, 0xA3, 0x72, 0xA7, 0x7F, 0xF5, 0xCB, 0x3E, 0xDC, 0xBF, + 0xF0, 0x47, 0x7C, 0xBF, 0x5F, 0x03, 0x0F, 0x82, 0xE4, 0x38, 0x4F, 0x9C, 0xD3, 0xF9, 0x55, 0x8E, 0xE0, 0xE6, 0xCF, 0x3F, 0x6E, 0x46, 0x1B, 0x8F, + 0x17, 0x71, 0xEE, 0xBA, 0x27, 0x7B, 0x45, 0xB7, 0x2F, 0x08, 0x2C, 0x2E, 0x30, 0x2E, 0x2F, 0x62, 0x3E, 0x2F, 0xB3, 0x0C, 0x53, 0xC8, 0xB1, 0x43, + 0xB4, 0xC8, 0xC2, 0x42, 0x69, 0x41, 0x3B, 0xF0, 0x10, 0xBA, 0xB1, 0xB0, 0x58, 0xB8, 0x05, 0xCA, 0x81, 0x92, 0x2D, 0x36, 0x2A, 0x6C, 0x2E, 0x73, + 0x6B, 0x3B, 0x37, 0x74, 0x21, 0x34, 0xA3, 0x04, 0x3A, 0x14, 0x54, 0x99, 0x0B, 0x4D, 0x57, 0x4B, 0x2B, 0xD3, 0x6D, 0xD0, 0xEF, 0xF0, 0x3D, 0x17, + 0xBA, 0xE0, 0x0B, 0x44, 0x1F, 0x7A, 0xE4, 0x8F, 0x26, 0x90, 0xBA, 0x4D, 0x78, 0x28, 0x6F, 0x22, 0xAA, 0xCA, 0xCB, 0xAC, 0xDE, 0xF2, 0xAB, 0x0F, + 0xF7, 0x47, 0xFC, 0x1A, 0x35, 0xFA, 0xC9, 0x6F, 0x23, 0x0F, 0x92, 0xAB, 0xE6, 0x52, 0x72, 0x00, 0x79, 0x0E, 0xEE, 0x94, 0xD7, 0x38, 0x0E, 0x0D, + 0x2C, 0x6A, 0x2E, 0xD9, 0xD3, 0x99, 0xA2, 0x83, 0x30, 0x5E, 0x33, 0x69, 0x93, 0x5D, 0x89, 0x59, 0x2B, 0x8F, 0x38, 0x40, 0xF8, 0xEB, 0xA7, 0x60, + 0x09, 0x16, 0x7A, 0x85, 0xF9, 0x0C, 0x7A, 0x5C, 0xB7, 0xFE, 0xC8, 0x95, 0x78, 0x82, 0x26, 0x22, 0x2E, 0xE3, 0x15, 0xEE, 0x6A, 0x53, 0xAB, 0x19, + 0x30, 0x57, 0xB6, 0xA8, 0xBA, 0x64, 0xE7, 0x2E, 0x27, 0xBC, 0xF5, 0x3F, 0x82, 0xFF, 0x6B, 0x09, 0x4B, 0x75, 0x11, 0xA8, 0xF8, 0x39, 0xB1, 0xA7, + 0xDB, 0xC1, 0x0F, 0x39, 0x3B, 0xC6, 0xB8, 0x0F, 0x48, 0x10, 0xF8, 0x46, 0xC0, 0xC5, 0x98, 0xF2, 0x60, 0x87, 0xD7, 0xDB, 0xA3, 0xD9, 0x6D, 0x53, + 0xA7, 0xCD, 0xD4, 0x85, 0x83, 0xD9, 0x17, 0x6C, 0x50, 0x62, 0xD8, 0xFE, 0xDB, 0x9A, 0x4D, 0xAA, 0x1A, 0x11, 0x52, 0x03, 0x26, 0xC3, 0x34, 0x2B, + 0x29, 0x68, 0x63, 0x19, 0xEC, 0x07, 0x1A, 0xCE, 0x28, 0x30, 0x31, 0x1B, 0xC1, 0xA0, 0xAC, 0x4F, 0x92, 0xCC, 0x06, 0xCC, 0x58, 0xE2, 0x09, 0xB1, + 0x20, 0xA9, 0x61, 0x9C, 0x66, 0x60, 0xAA, 0x67, 0x5F, 0xB4, 0xA0, 0x5B, 0x08, 0x22, 0xB5, 0xB0, 0x0E, 0x64, 0x9D, 0x39, 0x91, 0xE1, 0x78, 0x60, + 0x65, 0xF8, 0xBD, 0x22, 0x01, 0xC2, 0xEA, 0xEF, 0xB3, 0xEA, 0x86, 0x16, 0x03, 0x1F, 0x32, 0x16, 0x08, 0xC1, 0x51, 0x00, 0x3A, 0xA6, 0x5A, 0x65, + 0x31, 0x65, 0x0B, 0x2B, 0x45, 0x85, 0x4D, 0xEA, 0x85, 0x47, 0x54, 0x80, 0x22, 0xA8, 0xA3, 0x0F, 0x09, 0x9E, 0xA0, 0x3C, 0x69, 0xF5, 0x08, 0x6C, + 0x0B, 0xF5, 0xDC, 0xA5, 0xD3, 0x86, 0xA9, 0x03, 0xB9, 0x5F, 0xD3, 0x0B, 0x16, 0xA6, 0x42, 0x36, 0xAA, 0xA5, 0x1F, 0x67, 0x30, 0x03, 0x98, 0x04, + 0x94, 0x42, 0x07, 0x93, 0x5E, 0x73, 0x24, 0xD1, 0x20, 0x02, 0x34, 0xED, 0xE8, 0x4C, 0xDD, 0xE6, 0x74, 0x89, 0x01, 0xF8, 0x61, 0xE1, 0x93, 0xC7, + 0x95, 0x71, 0x92, 0x11, 0x33, 0x8E, 0x08, 0x0F, 0xE4, 0x1B, 0x46, 0x1D, 0x1B, 0x41, 0xAE, 0x2F, 0x0A, 0xE6, 0xA5, 0x14, 0xDC, 0x04, 0x0F, 0xEA, + 0x63, 0x3D, 0xC9, 0x75, 0xD1, 0x9E, 0xB5, 0xBB, 0xD5, 0x3A, 0x6C, 0xB3, 0x2D, 0x6B, 0xDF, 0x08, 0x3F, 0x55, 0x49, 0x21, 0xE3, 0x16, 0x26, 0x73, + 0x60, 0x92, 0xA3, 0x16, 0x0B, 0xE0, 0xD5, 0x70, 0x86, 0xF4, 0x32, 0xD6, 0x2F, 0x8F, 0xC1, 0x65, 0xF1, 0xCB, 0xF6, 0x45, 0xF0, 0xE2, 0xAD, 0x4E, + 0x79, 0x8D, 0xCD, 0xBE, 0xCA, 0xEF, 0x9F, 0xC4, 0xEA, 0x39, 0x69, 0x72, 0xD5, 0x1A, 0x3C, 0x09, 0xEB, 0x97, 0xC2, 0xDA, 0x33, 0x11, 0x23, 0xE6, + 0x39, 0x3B, 0xF0, 0x01, 0x1C, 0x7E, 0x4D, 0x5F, 0xDD, 0xFE, 0x10, 0xA1, 0x38, 0x04, 0xCA, 0x3C, 0x4F, 0xC5, 0x47, 0x22, 0x49, 0x27, 0xDB, 0xC1, + 0x43, 0x6C, 0x8F, 0x0F, 0xCB, 0xBB, 0xBD, 0x7E, 0x8E, 0xE2, 0xB6, 0x36, 0xD4, 0xDC, 0x8A, 0xAC, 0x85, 0xDD, 0x91, 0xF6, 0x3D, 0x4A, 0xE5, 0xA0, + 0x04, 0xB5, 0x2E, 0x2D, 0x2F, 0x70, 0xE1, 0xAB, 0x70, 0x11, 0x04, 0x1E, 0xC0, 0xDC, 0x2F, 0x17, 0x63, 0x04, 0x52, 0x89, 0x79, 0xA6, 0xF6, 0x36, + 0x2B, 0xF3, 0xA5, 0x50, 0x71, 0xF8, 0x61, 0x90, 0xC2, 0x28, 0xC2, 0x56, 0xE1, 0x71, 0xF8, 0x25, 0xEC, 0xC2, 0xE1, 0xC8, 0x29, 0x19, 0xE6, 0x8B, + 0x8E, 0x12, 0x87, 0x34, 0x81, 0xCE, 0xC9, 0x1B, 0x7F, 0xA5, 0xA0, 0x95, 0xE4, 0xEC, 0x0C, 0xE9, 0xB0, 0x12, 0x7A, 0x7B, 0x7A, 0x05, 0x1C, 0x43, + 0xD2, 0xE8, 0xAC, 0x3C, 0xA1, 0x28, 0xF9, 0x4B, 0x1D, 0x5E, 0x95, 0xF9, 0xC9, 0x32, 0xBB, 0x08, 0xFC, 0x16, 0x24, 0xF2, 0xD0, 0x3C, 0x72, 0xA8, + 0x0B, 0x8B, 0x1E, 0xF5, 0xF2, 0x83, 0xCA, 0xF4, 0xE2, 0x9F, 0x6E, 0x71, 0x86, 0x22, 0x5A, 0x7A, 0x9D, 0x86, 0x8E, 0x5C, 0x4F, 0x6A, 0x4B, 0x49, + 0xFC, 0x16, 0xAE, 0x0A, 0x11, 0x63, 0x16, 0x96, 0x42, 0x4B, 0x83, 0xDD, 0xE1, 0x5D, 0xEE, 0xA3, 0x38, 0xE4, 0xF6, 0xDD, 0xC0, 0x2D, 0xE5, 0xB9, + 0x55, 0xA4, 0x7C, 0x02, 0x05, 0xCA, 0x89, 0xE7, 0xAA, 0x13, 0x9C, 0x34, 0x26, 0xB6, 0x53, 0x98, 0x38, 0xE1, 0xB6, 0x00, 0xC8, 0x98, 0x2C, 0x89, + 0xC0, 0x82, 0xA7, 0x44, 0xA7, 0x87, 0x42, 0xED, 0x85, 0x20, 0xBC, 0x7F, 0xBA, 0xFC, 0x12, 0xDA, 0x9E, 0x11, 0x87, 0x3F, 0xC2, 0xA2, 0x6F, 0x1D, + 0x6D, 0xE2, 0x8F, 0xE4, 0xE6, 0xB8, 0x8F, 0x4C, 0x80, 0xD1, 0xE3, 0x05, 0xB1, 0x88, 0xAF, 0x40, 0x71, 0x55, 0x4C, 0xB5, 0xE8, 0x95, 0x55, 0x7C, + 0xB2, 0xED, 0x3E, 0x0E, 0xE9, 0x09, 0x7F, 0x91, 0x6F, 0xF9, 0x67, 0xBF, 0xEB, 0xDF, 0xCC, 0x79, 0x3F, 0x6D, 0xCE, 0xF3, 0x7F, 0xDE, 0x9B, 0x0F, + 0xE5, 0x02, 0xF2, 0xD3, 0xFA, 0xB1, 0xE7, 0xEB, 0x47, 0x29, 0xC3, 0x54, 0xE1, 0xA3, 0xAF, 0xFB, 0x71, 0x97, 0x44, 0x55, 0x81, 0xA4, 0x22, 0x5D, + 0x58, 0xAC, 0x6E, 0x38, 0x20, 0x56, 0x9D, 0x4F, 0x67, 0x80, 0xB8, 0x02, 0x0E, 0x0A, 0xEA, 0x5B, 0xB1, 0x5B, 0x58, 0x04, 0x9A, 0x73, 0xD4, 0x21, + 0x56, 0x7C, 0xA8, 0x7D, 0xBB, 0xFD, 0x80, 0xAF, 0x91, 0x8A, 0x71, 0x1E, 0xDE, 0x16, 0x47, 0x74, 0x07, 0x5B, 0x7E, 0x38, 0x02, 0x5E, 0x97, 0x0F, + 0x70, 0xBD, 0xCB, 0x45, 0x20, 0xC6, 0x0C, 0xE1, 0x41, 0xF2, 0x61, 0x5A, 0x5E, 0x5C, 0x8C, 0x0C, 0xEF, 0xCC, 0x3B, 0xF6, 0x86, 0x4D, 0xC0, 0x65, + 0xC4, 0x10, 0x92, 0x3C, 0x59, 0x66, 0xE9, 0x20, 0x86, 0xA8, 0x19, 0xD8, 0xEC, 0xD5, 0xC7, 0x08, 0xCD, 0xDC, 0xA3, 0x87, 0x36, 0xC7, 0x86, 0x99, + 0xC0, 0x64, 0x12, 0xFC, 0x2F, 0x6E, 0xE1, 0x40, 0x21, 0x38, 0x98, 0x88, 0x89, 0xE3, 0x22, 0x5B, 0xE6, 0xBF, 0xE1, 0xB3, 0xC1, 0xF1, 0xFB, 0x21, + 0x1F, 0x60, 0x50, 0xF0, 0x4F, 0x1D, 0x95, 0x98, 0x8E, 0x95, 0xFD, 0xB1, 0x60, 0xE1, 0xF8, 0x9A, 0xE1, 0x1E, 0x7C, 0x70, 0x4A, 0x4E, 0xA2, 0x72, + 0x04, 0x39, 0xC0, 0x2C, 0x6C, 0xCF, 0x4D, 0x38, 0x2C, 0xAC, 0xB4, 0xA1, 0x5F, 0x21, 0xC4, 0xA3, 0x5A, 0xFE, 0x2C, 0x2E, 0x1C, 0x00, 0x0D, 0xFE, + 0x1F, 0xBC, 0x10, 0xCE, 0x83, 0x33, 0xC0, 0xA1, 0x70, 0x37, 0x3C, 0x0D, 0xAF, 0xEA, 0x71, 0xFC, 0xAD, 0x7F, 0x81, 0xE3, 0xF5, 0x3F, 0xF0, 0x19, + 0x3D, 0x8C, 0x3E, 0xB2, 0x67, 0xEC, 0x4F, 0xFB, 0xD0, 0x7E, 0x12, 0xF7, 0x6D, 0xD2, 0xA7, 0xE2, 0x30, 0xFD, 0x6F, 0x14, 0x1C, 0xC1, 0x13, 0x7C, + 0x8C, 0xCB, 0xE0, 0x07, 0x70, 0x24, 0x9C, 0x0C, 0xA7, 0x7B, 0xA7, 0xFE, 0xAA, 0x2F, 0xEB, 0xE1, 0xFA, 0x0B, 0xFD, 0x88, 0xDE, 0xAF, 0x3F, 0xFB, + 0x8A, 0x5F, 0xB1, 0x9F, 0xEC, 0x5B, 0xE0, 0x37, 0x70, 0x1E, 0x38, 0x25, 0xEE, 0x89, 0xAF, 0xE2, 0x17, 0x94, 0xE0, 0x24, 0xA6, 0xE0, 0x2C, 0x78, + 0x0E, 0x3E, 0x00, 0xAF, 0xF1, 0x22, 0xFB, 0xD4, 0x4C, 0x68, 0x17, 0x92, 0x2D, 0x9C, 0x2F, 0x98, 0xA7, 0xEC, 0x45, 0xBE, 0x87, 0x8F, 0x54, 0x18, + 0x29, 0x5C, 0xA0, 0x43, 0x54, 0x4F, 0x83, 0x9E, 0x1B, 0x5C, 0x25, 0x72, 0x33, 0xF0, 0x5A, 0x27, 0x9A, 0x24, 0x20, 0x44, 0xD0, 0x9E, 0x29, 0xCD, + 0xCE, 0xA0, 0xC1, 0x9F, 0xB0, 0x44, 0x32, 0x29, 0x29, 0x6B, 0xE8, 0x1A, 0x4B, 0xEA, 0x04, 0x96, 0x12, 0xF3, 0x15, 0x37, 0x08, 0x2B, 0xAC, 0xAB, + 0x8D, 0x2A, 0xAF, 0x2F, 0xAB, 0x13, 0xD7, 0x11, 0xAC, 0x06, 0xAB, 0x77, 0x64, 0xAA, 0x63, 0x3E, 0x52, 0xB5, 0x18, 0x51, 0xF1, 0x78, 0xE7, 0x46, + 0xE0, 0xA4, 0x39, 0x76, 0x9C, 0x47, 0x51, 0x81, 0x6F, 0x51, 0x2B, 0x43, 0x42, 0xA3, 0x18, 0xE1, 0x43, 0xBF, 0x3E, 0xF4, 0x0F, 0x5E, 0x7C, 0xFD, + 0x1F, 0x0E, 0x1B, 0x85, 0x7F, 0xCB, 0x8E, 0x9F, 0x82, 0x65, 0xE8, 0x01, 0xDB, 0x42, 0xAB, 0xA4, 0x18, 0xD9, 0x0C, 0x7E, 0x90, 0xF9, 0x99, 0x44, + 0xF4, 0x8B, 0x58, 0xE0, 0x19, 0xFE, 0xE2, 0xAC, 0x01, 0xC3, 0x49, 0xEB, 0xE4, 0x93, 0xA8, 0xE7, 0x22, 0x57, 0xFC, 0x57, 0xD5, 0x89, 0x40, 0xF8, + 0x91, 0x4B, 0x01, 0xB2, 0x33, 0x49, 0x20, 0xE5, 0x76, 0x5F, 0x2B, 0x18, 0x59, 0xBC, 0xFC, 0x1C, 0x3C, 0x77, 0x70, 0xA3, 0x84, 0x9E, 0xDA, 0x2F, + 0xD4, 0x6B, 0xBB, 0xD9, 0xEE, 0x61, 0xC7, 0xB0, 0x03, 0xEE, 0x2C, 0xF6, 0x0F, 0xF5, 0xE2, 0x1E, 0xA3, 0xEE, 0xDC, 0x1D, 0xEB, 0xDA, 0xBD, 0xB4, + 0xFE, 0xAC, 0x71, 0xEB, 0x25, 0x75, 0x22, 0x4D, 0xFD, 0xDA, 0x52, 0xE7, 0xEE, 0x04, 0xEA, 0xD8, 0x9A, 0x9D, 0x5C, 0x1B, 0xD8, 0x8B, 0x76, 0xB0, + 0x3D, 0x6B, 0xAF, 0xDB, 0xE5, 0xF6, 0xBD, 0xDD, 0xAD, 0xEE, 0xB5, 0x89, 0xD2, 0x22, 0x76, 0x09, 0x0F, 0x08, 0x8B, 0xFF, 0x98, 0xD7, 0x4F, 0x63, + 0xFD, 0xB3, 0x1C, 0xB2, 0x65, 0xFB, 0xA4, 0x2E, 0x30, 0xA4, 0x27, 0x89, 0xC5, 0x05, 0xE6, 0xC5, 0xE5, 0xC6, 0x6E, 0x62, 0x47, 0x83, 0xD8, 0x7E, + 0x61, 0x23, 0x9A, 0x35, 0x06, 0x64, 0x49, 0x2C, 0x89, 0x11, 0x69, 0x58, 0xCE, 0xA2, 0x95, 0xE7, 0xDA, 0xE5, 0x45, 0x77, 0xBE, 0xA5, 0x77, 0x54, + 0xE1, 0x4B, 0x18, 0x4C, 0x31, 0x24, 0x4A, 0x2D, 0x5E, 0xFB, 0x64, 0x8B, 0x28, 0x3C, 0x64, 0xC3, 0x9E, 0xFD, 0x0E, 0xF5, 0xD4, 0x8B, 0x25, 0xF6, + 0xC4, 0xFF, 0x8B, 0x8F, 0xC2, 0x41, 0x57, 0xB0, 0x24, 0xE8, 0xDA, 0xD8, 0x30, 0x9A, 0x2F, 0xB0, 0x2C, 0xBD, 0x2E, 0x4C, 0x6A, 0xEC, 0xDA, 0x63, + 0x17, 0xF9, 0xD3, 0xBB, 0x1C, 0x3F, 0xBD, 0x5F, 0xC4, 0x19, 0x41, 0x4B, 0x38, 0x33, 0x84, 0x5D, 0x89, 0x7C, 0xE3, 0xAC, 0x98, 0x36, 0x63, 0x93, + 0x2C, 0x0E, 0x63, 0x81, 0x91, 0xCF, 0xBD, 0x47, 0x2A, 0xF0, 0xDD, 0xD0, 0x5B, 0xC4, 0x3A, 0x61, 0xAE, 0xB0, 0x0F, 0xCD, 0x1B, 0x44, 0xBF, 0x60, + 0x08, 0xC3, 0x5B, 0xF3, 0x38, 0xAD, 0xBE, 0x4A, 0xDE, 0x87, 0x03, 0x97, 0xB1, 0x93, 0xFF, 0xC9, 0xF4, 0x3E, 0x7A, 0x3F, 0xE6, 0x60, 0x2F, 0x2A, + 0xE0, 0xDB, 0x52, 0xC1, 0xA3, 0xFE, 0xAD, 0x84, 0xBE, 0xA6, 0xC1, 0xD0, 0x6E, 0xCF, 0xC4, 0xE1, 0x83, 0x9D, 0xE1, 0x36, 0xE2, 0xED, 0x41, 0x0A, + 0xCF, 0x2B, 0xE4, 0x53, 0xA0, 0xC0, 0xF5, 0xC7, 0xFB, 0x50, 0xC7, 0x81, 0x3C, 0xB8, 0xCF, 0xB8, 0x38, 0x6C, 0x2F, 0xBF, 0x2B, 0x97, 0x97, 0x86, + 0xF1, 0x96, 0x98, 0x5C, 0x37, 0xD1, 0x06, 0x20, 0xE3, 0x07, 0xF8, 0x8C, 0x00, 0xB2, 0x4A, 0x83, 0x0D, 0x36, 0x83, 0x67, 0xC2, 0x64, 0x04, 0x93, + 0x02, 0x8C, 0xB6, 0x00, 0x4E, 0x97, 0xDC, 0xF5, 0x4A, 0x78, 0xB2, 0x7E, 0x2D, 0x21, 0x58, 0x12, 0x29, 0xB9, 0x4F, 0x8B, 0x59, 0x37, 0xCB, 0x56, + 0x82, 0x6F, 0x01, 0x02, 0x09, 0x62, 0xA6, 0x6C, 0x24, 0x69, 0x2C, 0xEF, 0x02, 0x56, 0x88, 0xD5, 0x6B, 0x24, 0xD4, 0x93, 0xB3, 0xEB, 0xB8, 0x4A, + 0xEB, 0x90, 0x84, 0xCF, 0xEA, 0x54, 0x24, 0x47, 0xD2, 0xF6, 0x56, 0xE7, 0x84, 0x75, 0xD7, 0x0A, 0x5F, 0x18, 0xD9, 0x09, 0x2A, 0x6A, 0xA4, 0x60, + 0xA2, 0x83, 0xC2, 0xB9, 0x4D, 0x48, 0x4F, 0xA4, 0x86, 0x0B, 0x8B, 0xB8, 0x02, 0x9E, 0x1D, 0x4D, 0x47, 0x4F, 0x91, 0xC4, 0x74, 0x0D, 0x2E, 0xF0, + 0x8F, 0x54, 0x75, 0xC6, 0x9C, 0xB2, 0xE5, 0xE2, 0x99, 0x29, 0x56, 0xC8, 0xDE, 0x3C, 0xF0, 0x54, 0xF0, 0x73, 0x44, 0x8E, 0x8C, 0x95, 0x74, 0x08, + 0xF4, 0x97, 0x4E, 0xD6, 0x27, 0xE8, 0x71, 0x7A, 0x0A, 0x3D, 0x4F, 0xB7, 0xA3, 0x6F, 0xE8, 0x2A, 0x68, 0x4A, 0xFD, 0x90, 0x5E, 0xD5, 0x89, 0xD0, + 0x43, 0x68, 0x24, 0x34, 0x2B, 0x7D, 0x85, 0xDE, 0xA5, 0x79, 0x3D, 0x00, 0x0D, 0xAC, 0x3F, 0xD1, 0x41, 0xFA, 0x8E, 0xBE, 0x40, 0x4F, 0xA0, 0xB7, + 0xE9, 0x68, 0x3A, 0x08, 0x7D, 0x8C, 0x7E, 0xA3, 0x2B, 0xD0, 0x78, 0x74, 0x1F, 0x3D, 0x83, 0xDE, 0x41, 0x47, 0xD2, 0x22, 0x6A, 0xD3, 0xFA, 0x94, + 0x9E, 0xA5, 0x25, 0xF5, 0x4D, 0x1D, 0x55, 0xA7, 0xD5, 0x75, 0x75, 0xB8, 0xAF, 0xE3, 0xE8, 0x43, 0x7A, 0x93, 0x5E, 0xA5, 0x8F, 0xE9, 0x6F, 0x7A, + 0x59, 0xFE, 0xDB, 0x7F, 0xE6, 0xA0, 0xB8, 0x8C, 0x2F, 0xA3, 0x9F, 0xCB, 0x51, 0xC7, 0x3A, 0x72, 0x70, 0x32, 0xB3, 0xD6, 0x6E, 0x1D, 0x28, 0x41, + 0x63, 0xA3, 0xC2, 0x82, 0xCD, 0x65, 0x43, 0xBE, 0xB6, 0xB3, 0xD8, 0x25, 0x24, 0x47, 0x73, 0x19, 0x65, 0x6D, 0x41, 0x69, 0x69, 0x71, 0xC9, 0x65, + 0xA9, 0xDD, 0x64, 0xBA, 0x12, 0x58, 0x77, 0x7A, 0x57, 0x7B, 0x38, 0xAC, 0x04, 0x07, 0xAB, 0x10, 0x85, 0x30, 0x88, 0x1C, 0xA2, 0x2F, 0x68, 0x42, + 0x64, 0xB7, 0xC1, 0xC9, 0x9B, 0x9A, 0xF2, 0xB2, 0xB1, 0xDC, 0xE9, 0x97, 0xFD, 0x0B, 0xDF, 0xEF, 0xA7, 0x26, 0x8E, 0x15, 0x3F, 0xE7, 0x5F, 0xCC, + 0x3B, 0xF3, 0xDA, 0x3C, 0x5C, 0x16, 0xE7, 0x44, 0x65, 0x3A, 0xB7, 0x2A, 0x5F, 0xE5, 0x38, 0x34, 0x4E, 0x74, 0x3E, 0xD4, 0x5F, 0xC1, 0xA7, 0xFF, + 0x59, 0xC2, 0x2D, 0xE2, 0xB0, 0xCE, 0x87, 0x89, 0xF9, 0x17, 0x66, 0x72, 0x36, 0x60, 0x58, 0x04, 0x80, 0x43, 0x41, 0x4D, 0xE7, 0x1F, 0xCD, 0x2A, + 0x8F, 0x99, 0x25, 0x3D, 0x64, 0x76, 0x3D, 0x65, 0x5E, 0x3E, 0x61, 0x52, 0x58, 0xC1, 0x14, 0x58, 0xF2, 0x32, 0xC5, 0x42, 0xE5, 0xF2, 0xD5, 0x92, + 0xD4, 0x52, 0xB6, 0x84, 0x05, 0x82, 0xAD, 0x58, 0x43, 0x0B, 0x65, 0x58, 0xCF, 0xE5, 0x05, 0x81, 0x60, 0xC2, 0x22, 0x60, 0x20, 0xE4, 0x2B, 0x98, + 0x59, 0xBF, 0xA4, 0x71, 0x94, 0x56, 0x64, 0xE5, 0x16, 0x7D, 0x95, 0x68, 0xF5, 0x52, 0xD9, 0xEA, 0x75, 0xB9, 0x79, 0x77, 0xE6, 0xAA, 0x1C, 0xE9, + 0xED, 0x7E, 0x4D, 0xA1, 0x42, 0x3F, 0x4E, 0xA1, 0x90, 0x23, 0x85, 0x7C, 0xA6, 0x8A, 0xB2, 0xA2, 0xD6, 0x75, 0x00, 0xD9, 0x2F, 0x9A, 0x41, 0x09, + 0x61, 0x9A, 0xFB, 0x89, 0x21, 0x09, 0x97, 0x06, 0x94, 0x5B, 0x24, 0x67, 0x4F, 0x28, 0x6D, 0x9C, 0x04, 0x67, 0x68, 0xC2, 0xBA, 0xD4, 0x84, 0xA8, + 0x85, 0x2D, 0x6F, 0x18, 0xA5, 0x2D, 0x16, 0x8F, 0x25, 0x38, 0x4D, 0x4A, 0xE4, 0x3C, 0x17, 0x6B, 0x9D, 0xC6, 0x92, 0x80, 0xF0, 0x41, 0x7B, 0x56, + 0x61, 0x76, 0x06, 0x68, 0x12, 0x7F, 0xE2, 0x17, 0xC9, 0x24, 0x15, 0x59, 0x1D, 0x3F, 0x54, 0xB1, 0xA3, 0xAC, 0xB0, 0x94, 0x57, 0x5E, 0x71, 0x77, + 0x5B, 0x81, 0x5C, 0x6F, 0x02, 0xF4, 0x2A, 0x60, 0x3D, 0xF0, 0x74, 0xE4, 0x2A, 0x6B, 0xD5, 0xC3, 0x02, 0x55, 0xAD, 0x9D, 0x2A, 0x35, 0x46, 0x1B, + 0x25, 0xEA, 0x85, 0x23, 0xF9, 0xA9, 0x2E, 0x1B, 0x3A, 0xC6, 0x01, 0xEF, 0x35, 0x86, 0x91, 0x32, 0x04, 0xB5, 0x8A, 0x0A, 0x12, 0xCA, 0x5D, 0x24, + 0xC2, 0x0A, 0xC2, 0x39, 0xFF, 0x4B, 0x98, 0xB1, 0xF2, 0xC4, 0x75, 0xAA, 0x51, 0x3B, 0x19, 0xA5, 0xAC, 0xFA, 0x54, 0xC9, 0xE0, 0xAA, 0x37, 0x75, + 0x23, 0xC5, 0x47, 0xA3, 0x96, 0xE4, 0x80, 0x87, 0x4B, 0x33, 0x31, 0x3F, 0x2C, 0xB6, 0xDD, 0x83, 0xB5, 0xE1, 0x68, 0x78, 0x1F, 0xB0, 0x7C, 0xE5, + 0x8B, 0xED, 0x33, 0xF5, 0xFF, 0xFF, 0xB4, 0x1D, 0x7B, 0x83, 0x0B, 0x56, 0x45, 0x88, 0xD7, 0x21, 0x11, 0xA0, 0x54, 0x70, 0x0D, 0xF2, 0xEB, 0xBA, + 0x43, 0x46, 0xAB, 0x6B, 0xDE, 0x3A, 0xE0, 0x37, 0x9F, 0x55, 0xEB, 0x59, 0x2D, 0x45, 0xF8, 0x12, 0x1E, 0x19, 0x5B, 0x3B, 0x32, 0xC3, 0xB8, 0x0A, + 0x4B, 0xE3, 0xAF, 0xFE, 0x58, 0xAA, 0xDE, 0x31, 0xAA, 0x5A, 0x91, 0xF1, 0x5F, 0xC9, 0x9F, 0x8A, 0xA0, 0x50, 0x91, 0x8C, 0x5A, 0x57, 0x03, 0x35, + 0x08, 0xF5, 0x14, 0xED, 0xF2, 0x6C, 0xC3, 0xD4, 0xC6, 0x92, 0x69, 0x95, 0xEE, 0x99, 0xC3, 0x6B, 0xDF, 0xAB, 0x9B, 0x6B, 0x96, 0x42, 0xC0, 0xF9, + 0x80, 0xBD, 0xA4, 0x4C, 0x7B, 0xA3, 0x4F, 0x60, 0x47, 0x3D, 0x44, 0xC9, 0x1F, 0x05, 0x88, 0xAE, 0xC5, 0x7E, 0xD1, 0xA1, 0x0D, 0xA1, 0x82, 0xE3, + 0xFC, 0x34, 0xAA, 0x5F, 0x81, 0x7F, 0x23, 0x5F, 0x9F, 0xA0, 0x91, 0x07, 0x9A, 0xF8, 0xE0, 0x8E, 0x9F, 0x9F, 0x1C, 0xE8, 0x3E, 0x95, 0x2C, 0xF7, + 0x94, 0x99, 0xEA, 0x7C, 0x1D, 0xD0, 0x79, 0x08, 0xD9, 0x4F, 0xB1, 0xC0, 0xA9, 0xB4, 0xC6, 0x69, 0x13, 0xB9, 0x39, 0x5A, 0xB3, 0x49, 0x63, 0xF6, + 0xC3, 0x3C, 0x14, 0x99, 0xAA, 0xC9, 0x83, 0x0B, 0x35, 0x97, 0x30, 0x54, 0x00, 0x24, 0x72, 0x4F, 0x20, 0x92, 0x54, 0xC5, 0x3C, 0xC6, 0x22, 0x33, + 0xCD, 0xC6, 0xBC, 0xB0, 0x78, 0xB9, 0x97, 0xC4, 0xB2, 0x45, 0x25, 0x10, 0x06, 0x43, 0x7C, 0xD4, 0x0A, 0x39, 0x22, 0x48, 0xA9, 0xE9, 0x49, 0xF9, + 0x6F, 0x27, 0x69, 0x95, 0x4C, 0x0E, 0x98, 0x91, 0x64, 0x61, 0x89, 0x9C, 0xCA, 0x85, 0x2C, 0x1A, 0xF0, 0xD8, 0x56, 0xE3, 0xB8, 0x85, 0xB5, 0xE3, + 0x19, 0x68, 0x7C, 0x2B, 0x02, 0x52, 0x03, 0x8C, 0x71, 0x44, 0xD6, 0x0F, 0xCA, 0x3A, 0x1F, 0x37, 0xE4, 0x7A, 0xB0, 0xEE, 0xF1, 0x18, 0x52, 0x62, + 0x11, 0x8A, 0x9D, 0xA2, 0x2B, 0x0E, 0x45, 0x1C, 0x15, 0x89, 0x2C, 0x52, 0xF2, 0x1D, 0x4E, 0x87, 0x88, 0x4D, 0x64, 0x87, 0x67, 0x44, 0x74, 0xC8, + 0x21, 0x31, 0xDC, 0x80, 0x44, 0xF7, 0x9B, 0x2A, 0xE4, 0x2B, 0x82, 0x57, 0x1A, 0x21, 0x24, 0x41, 0xC8, 0x61, 0x37, 0x47, 0xE2, 0xAC, 0x92, 0xA2, + 0x8F, 0x48, 0x62, 0x30, 0x30, 0x00, 0x8E, 0xCE, 0x60, 0x1C, 0x37, 0x24, 0x6E, 0x1C, 0xB9, 0xB9, 0xF1, 0xF8, 0x64, 0x2B, 0x64, 0x23, 0xEB, 0x26, + 0xB7, 0xF1, 0xEB, 0x6A, 0x80, 0xF2, 0xD4, 0x80, 0x61, 0x40, 0xC3, 0x82, 0x1D, 0x8D, 0xA7, 0xDE, 0xBE, 0x09, 0xD4, 0x0C, 0x24, 0xB7, 0x18, 0x99, + 0x32, 0xE0, 0xD2, 0x64, 0xC8, 0xA4, 0x09, 0x5E, 0x55, 0x75, 0xA6, 0xE5, 0x2A, 0x7A, 0xC4, 0x50, 0xC2, 0x03, 0xE3, 0xDE, 0xA0, 0xF8, 0x00, 0xA4, + 0xB4, 0x02, 0x0C, 0x7B, 0xF1, 0xF5, 0xF0, 0xCA, 0x04, 0xAE, 0xFE, 0xEC, 0x0C, 0x18, 0x37, 0x07, 0xC2, 0x19, 0x2F, 0xC6, 0x66, 0xBC, 0x08, 0xA0, + 0xE4, 0x22, 0x87, 0x91, 0x63, 0x46, 0xFE, 0xDD, 0xFD, 0x59, 0x9D, 0xE8, 0x5B, 0xE8, 0x8C, 0xB9, 0x6A, 0x0D, 0x16, 0x8A, 0x28, 0x3E, 0x8B, 0x3A, + 0x57, 0x3F, 0x73, 0x28, 0x95, 0xC9, 0x15, 0x67, 0xD7, 0x15, 0x36, 0x32, 0xE0, 0x7C, 0x93, 0xBC, 0x6F, 0x27, 0x15, 0xF7, 0x4D, 0xF0, 0x35, 0x39, + 0x7B, 0xBE, 0x30, 0x4C, 0x1E, 0x15, 0x45, 0x05, 0x53, 0xE7, 0xDE, 0xED, 0xD0, 0x9E, 0x80, 0x27, 0x29, 0x86, 0x1C, 0x51, 0x50, 0xC9, 0x9C, 0x00, + 0x93, 0xC9, 0xDB, 0x90, 0x4C, 0x8C, 0x18, 0xD4, 0xC3, 0xEB, 0x2A, 0x71, 0x48, 0x05, 0xCF, 0xAB, 0x21, 0xE1, 0x93, 0x18, 0x2D, 0x1B, 0xD1, 0xE8, + 0x25, 0x02, 0xF7, 0x02, 0x5E, 0x29, 0x1D, 0x9E, 0x8D, 0x1F, 0x08, 0x28, 0x28, 0x40, 0x34, 0x29, 0xF6, 0x1F, 0x70, 0xF4, 0x3B, 0xAE, 0x16, 0x1E, + 0x9C, 0x30, 0x3B, 0xD0, 0xF1, 0xE0, 0x70, 0x06, 0x9B, 0xE3, 0x7B, 0x9E, 0x1B, 0xEA, 0xCC, 0x1A, 0x10, 0x95, 0x64, 0xE0, 0x93, 0xC5, 0xC0, 0xC9, + 0x82, 0x5F, 0x48, 0xFC, 0xB5, 0xF0, 0xBA, 0xA4, 0x82, 0xE7, 0x0F, 0x85, 0xDF, 0x16, 0xBB, 0x5E, 0xC6, 0x6F, 0x17, 0x44, 0xA0, 0xAE, 0xC8, 0x25, + 0x9F, 0x50, 0x45, 0xA6, 0x23, 0x8B, 0xFB, 0x70, 0x71, 0x3D, 0xD6, 0x5F, 0x9C, 0xDB, 0x1D, 0x23, 0xED, 0xD6, 0x0B, 0x71, 0x43, 0x46, 0xB0, 0x51, + 0x5F, 0xDC, 0x92, 0x7C, 0xBD, 0x94, 0x1F, 0xB7, 0x2A, 0x62, 0xAF, 0x63, 0xE4, 0xA6, 0xA2, 0xD9, 0x2B, 0xC1, 0xDD, 0x3B, 0xA7, 0x7C, 0x18, 0x06, + 0x9B, 0x4B, 0x7F, 0x00, 0xC8, 0x47, 0x69, 0x0E, 0x32, 0x1E, 0x18, 0xD4, 0xAB, 0x98, 0x6C, 0xC0, 0x6F, 0x5A, 0xB3, 0x82, 0xF4, 0x71, 0xB6, 0x70, + 0x27, 0x4C, 0xB8, 0xF0, 0x30, 0x5E, 0xCE, 0x85, 0x45, 0x7C, 0xA3, 0xD1, 0x39, 0x33, 0x8B, 0x2F, 0xD5, 0x3A, 0x23, 0x91, 0x83, 0x72, 0x9F, 0x83, + 0x4B, 0x50, 0xBD, 0x70, 0xD9, 0x3C, 0x73, 0x60, 0x9C, 0x67, 0x12, 0x4A, 0xBC, 0x1C, 0x0F, 0xBF, 0x30, 0x0F, 0xA2, 0xB3, 0x99, 0xE5, 0x49, 0x29, + 0xBF, 0xBA, 0xE4, 0xFC, 0xB1, 0xEC, 0xB7, 0xFA, 0xA6, 0x21, 0xD8, 0x71, 0x84, 0x61, 0x92, 0x28, 0x1C, 0xEF, 0xDB, 0x86, 0x2D, 0x1A, 0x7C, 0x07, + 0x9B, 0x33, 0xA8, 0x04, 0x44, 0x22, 0xD5, 0x71, 0x3D, 0x00, 0x00, 0x20, 0x42, 0x50, 0x00, 0x73, 0xCA, 0x0D, 0x86, 0x41, 0x00, 0x20, 0xE6, 0x1C, + 0x84, 0x6A, 0x04, 0xC0, 0x05, 0x39, 0x4A, 0x22, 0xA0, 0x30, 0x00, 0x22, 0x69, 0x04, 0xA0, 0x00, 0x80, 0x61, 0x28, 0x14, 0x00, 0x00, 0x10, 0x82, + 0x00, 0x20, 0x00, 0x18, 0x02, 0x86, 0x10, 0x0D, 0x51, 0x59, 0x65, 0x62, 0xC4, 0x18, 0xD7, 0xB6, 0x0B, 0x0E, 0x24, 0x8F, 0x84, 0x06, 0xED, 0xCD, + 0xE3, 0xD2, 0xA0, 0x91, 0xE8, 0x92, 0x5A, 0x70, 0x93, 0x51, 0xBD, 0x9E, 0xFD, 0x78, 0xF0, 0x22, 0xDF, 0x59, 0x72, 0xD6, 0x2E, 0x73, 0x5B, 0xDF, + 0x3D, 0x1A, 0x65, 0xCB, 0xA6, 0x8D, 0x02, 0xF7, 0xC7, 0x61, 0x15, 0x9B, 0xA1, 0x49, 0x8D, 0xD9, 0xC6, 0x46, 0xA4, 0xB4, 0x1B, 0x6B, 0x5B, 0xDD, + 0x67, 0xBB, 0x10, 0x92, 0xCF, 0x25, 0x76, 0x70, 0x64, 0x5B, 0x39, 0xCF, 0x4B, 0xC3, 0xA4, 0x4C, 0x99, 0xE8, 0xFF, 0x3F, 0xF2, 0x26, 0x05, 0x6C, + 0xBC, 0x6D, 0xC9, 0x6C, 0x37, 0xD2, 0xDC, 0x19, 0x2E, 0xA0, 0x4E, 0xCD, 0xA2, 0x37, 0x2C, 0xFD, 0xA5, 0xC5, 0xD1, 0xBD, 0x4E, 0xAB, 0x61, 0xFD, + 0xC0, 0xE2, 0xD3, 0xEE, 0xD8, 0x8F, 0xC1, 0x3A, 0xBB, 0x41, 0x66, 0x35, 0xC7, 0xB6, 0x1A, 0xEC, 0x71, 0x79, 0x5A, 0xEF, 0xAA, 0x3D, 0x71, 0x4A, + 0x16, 0x34, 0x06, 0x01, 0xD6, 0x75, 0x82, 0xE8, 0x1A, 0xAC, 0xCA, 0x96, 0x73, 0x0C, 0xF3, 0xDC, 0xD1, 0x5A, 0x96, 0xA7, 0xB8, 0xDB, 0x3C, 0xC0, + 0x3B, 0x11, 0x68, 0x15, 0x82, 0x1F, 0xC3, 0x1D, 0x51, 0xE6, 0x03, 0xF4, 0xB4, 0xD9, 0x1E, 0x3F, 0x5C, 0x3C, 0xA4, 0x9C, 0x93, 0x6C, 0xC7, 0xF7, + 0x77, 0xE8, 0x64, 0x18, 0xE6, 0xB4, 0x3E, 0x93, 0xBD, 0x3F, 0x75, 0xD0, 0x5D, 0x26, 0xED, 0xFC, 0x30, 0x0C, 0x6F, 0x7F, 0x99, 0x74, 0x03, 0x8E, + 0xDB, 0xA2, 0x78, 0x04, 0xA5, 0x97, 0x3D, 0xB4, 0x5A, 0x6C, 0x10, 0x7A, 0x9F, 0xE1, 0x69, 0xDB, 0x85, 0x3B, 0x7A, 0x44, 0x5A, 0x33, 0x3C, 0xF0, + 0xD5, 0x7F, 0x8D, 0x7B, 0xE7, 0xFD, 0xF1, 0xC2, 0x82, 0x9D, 0xEE, 0xCF, 0xF6, 0x91, 0xC2, 0xC1, 0x2A, 0xD8, 0x85, 0xCF, 0xD9, 0xFC, 0xD8, 0xAE, + 0x23, 0xD6, 0x5A, 0xD7, 0x17, 0x44, 0xAB, 0x44, 0x7B, 0x22, 0xD8, 0x55, 0x72, 0xCB, 0x6A, 0xC4, 0x6F, 0xAB, 0x30, 0x31, 0x2C, 0xCE, 0xC0, 0x0E, + 0xDB, 0x7A, 0x88, 0x53, 0x89, 0xF5, 0x65, 0x38, 0x94, 0x82, 0xE9, 0x0C, 0x35, 0x5D, 0xDD, 0xD4, 0x68, 0xA8, 0xA1, 0x89, 0x9B, 0x2F, 0xB4, 0x21, + 0x3F, 0x11, 0x75, 0x1A, 0xCB, 0xF2, 0x38, 0x08, 0xB9, 0x5B, 0x47, 0x5B, 0xB2, 0x75, 0x0C, 0x86, 0x02, 0x7B, 0x4A, 0xBA, 0xD8, 0x70, 0x63, 0xE2, + 0x4A, 0xC8, 0x79, 0x4D, 0xA9, 0x9F, 0x3E, 0x73, 0x70, 0x86, 0x31, 0x35, 0xB5, 0x36, 0x98, 0xBB, 0x47, 0x37, 0x28, 0x08, 0x71, 0x6A, 0xAD, 0x60, + 0x3D, 0x45, 0x13, 0x2B, 0xAB, 0x8C, 0x61, 0x4D, 0x5B, 0x81, 0xFE, 0x56, 0x1B, 0x00, 0x62, 0x6C, 0x43, 0x1D, 0xA0, 0x3E, 0x84, 0x0F, 0x88, 0xBC, + 0x03, 0x03, 0xD2, 0xDF, 0xD2, 0x46, 0xBD, 0x67, 0xD2, 0x96, 0x3B, 0xF1, 0xA3, 0x98, 0x5A, 0x0E, 0xB6, 0x66, 0xD7, 0x8E, 0x34, 0xCC, 0xD8, 0x8E, + 0x33, 0x4B, 0xA3, 0xF3, 0x85, 0xB3, 0x00, 0x1B, 0x9C, 0xDD, 0x63, 0x83, 0x1F, 0xE7, 0x9B, 0x30, 0xD8, 0xBA, 0xB3, 0x61, 0x9C, 0x1D, 0x0B, 0x9F, + 0xE1, 0x5F, 0xC1, 0xAF, 0x36, 0x31, 0x9C, 0xBE, 0xA3, 0xE5, 0x44, 0xA5, 0x66, 0xAB, 0x72, 0x31, 0xAD, 0xCC, 0x36, 0x59, 0x62, 0x1C, 0x76, 0xC5, + 0xA2, 0xEC, 0x54, 0x3F, 0x93, 0xD4, 0x81, 0xE2, 0xA2, 0x4D, 0x59, 0x32, 0xCB, 0xFD, 0x7E, 0x95, 0x2C, 0x0D, 0x76, 0x48, 0x8D, 0x64, 0xEA, 0x03, + 0xBB, 0xE3, 0x2E, 0xD9, 0x0D, 0xE7, 0x31, 0xC2, 0x69, 0xD3, 0x79, 0xC2, 0xE6, 0x3A, 0x67, 0x07, 0xE2, 0x2A, 0xC1, 0xC7, 0x0D, 0x8E, 0xE9, 0xDC, + 0xDA, 0xB0, 0xE8, 0xD8, 0x05, 0xC0, 0x94, 0x91, 0x46, 0xCD, 0x6E, 0xBB, 0x34, 0xD4, 0xD4, 0x2E, 0x3D, 0xA0, 0xAE, 0x94, 0x9B, 0x1A, 0x36, 0x11, + 0x43, 0xC5, 0x81, 0x67, 0x2D, 0xB9, 0xE5, 0x03, 0x03, 0xA6, 0xDC, 0xBD, 0x15, 0x9C, 0x1C, 0xBB, 0xF5, 0xEB, 0xAE, 0x6B, 0xA4, 0xB7, 0x92, 0x0F, + 0xD9, 0x70, 0xA2, 0xD9, 0xFF, 0xFF, 0x63, 0x29, 0x38, 0xC5, 0xDF, 0xE9, 0x3A, 0x66, 0xEA, 0x73, 0x8F, 0x14, 0xAB, 0xE7, 0x2B, 0x61, 0xA1, 0xD8, + 0x06, 0x9F, 0x53, 0xAB, 0x02, 0x79, 0x60, 0xD0, 0xB8, 0xF1, 0xD7, 0xA9, 0x96, 0x1C, 0x32, 0xF8, 0xF9, 0xFA, 0xD9, 0xDB, 0x11, 0xD9, 0x58, 0x98, + 0x35, 0xA9, 0xC8, 0xFA, 0xF4, 0x7A, 0x63, 0xD7, 0xF3, 0xAD, 0xAA, 0x7E, 0x35, 0x1C, 0x6E, 0xD2, 0x69, 0x53, 0x04, 0x1F, 0x7B, 0xD6, 0x2A, 0xC2, + 0x1D, 0x28, 0xE8, 0xE9, 0x71, 0x9A, 0xDA, 0xC0, 0xC8, 0x36, 0x47, 0x9F, 0x56, 0xDB, 0x3D, 0xDF, 0x22, 0x77, 0xC0, 0xED, 0x9C, 0x37, 0xC4, 0xFD, + 0xA5, 0xCA, 0x63, 0xE4, 0x8F, 0x31, 0x06, 0x3B, 0xEC, 0x5C, 0x03, 0x75, 0xE4, 0x9A, 0x1B, 0x46, 0x4C, 0x9A, 0xA2, 0x1C, 0x9A, 0x08, 0xD8, 0xEF, + 0x93, 0x33, 0x36, 0x81, 0xBA, 0x35, 0x14, 0x01, 0xDC, 0x06, 0x9B, 0xEE, 0x8B, 0x1A, 0x22, 0x7A, 0xB5, 0xA1, 0x66, 0xE9, 0x79, 0x6D, 0x2D, 0xCA, + 0xC3, 0x60, 0xFD, 0x60, 0x5D, 0xE7, 0xB0, 0xB8, 0x27, 0x63, 0x6E, 0x44, 0xE3, 0x8D, 0xE3, 0xC5, 0x52, 0x24, 0xF7, 0x16, 0x74, 0x25, 0xD2, 0xD9, + 0x7F, 0x4A, 0xAB, 0xD9, 0x5F, 0x4B, 0x07, 0x31, 0xAA, 0x59, 0x2E, 0x4C, 0xB9, 0x50, 0x31, 0x67, 0x29, 0x7D, 0xDB, 0xAD, 0x7A, 0x46, 0xAE, 0x24, + 0x42, 0x9B, 0x80, 0xD1, 0x04, 0x13, 0x98, 0x57, 0xCF, 0xD7, 0x67, 0xB1, 0xB6, 0x4F, 0xC1, 0xC6, 0x52, 0x4F, 0x90, 0xF9, 0x4F, 0x57, 0xC5, 0x53, + 0x43, 0x05, 0xFA, 0xF3, 0xA3, 0x23, 0x46, 0xC7, 0x57, 0x8D, 0xE7, 0x16, 0x8C, 0x5A, 0x46, 0x67, 0x5B, 0x23, 0x27, 0xF7, 0x56, 0x7A, 0xDA, 0x16, + 0x15, 0x43, 0x0A, 0xDD, 0xB5, 0x02, 0xC8, 0x95, 0x49, 0xBA, 0x15, 0x34, 0xCA, 0x54, 0xEB, 0x8B, 0x4E, 0x6C, 0xA4, 0xD3, 0xAF, 0x93, 0x9B, 0x9D, + 0xAC, 0x37, 0xF7, 0x08, 0xB5, 0x63, 0x7B, 0xD6, 0x78, 0xC0, 0x4C, 0xDD, 0x17, 0x2D, 0x30, 0xDF, 0xAD, 0xA2, 0xCA, 0xF4, 0x76, 0x6D, 0x52, 0xE0, + 0x28, 0xB5, 0x0E, 0x77, 0x0B, 0x79, 0x54, 0xEA, 0x83, 0x18, 0xBA, 0xFF, 0x83, 0x80, 0x5E, 0xFE, 0x1C, 0x28, 0x99, 0xBE, 0x71, 0xD9, 0xC9, 0x8E, + 0x99, 0x29, 0x43, 0x65, 0x80, 0x0D, 0x77, 0x6A, 0x0C, 0xF7, 0x7A, 0x28, 0xE4, 0x38, 0xD1, 0xC8, 0x82, 0xF3, 0x4B, 0x27, 0x96, 0x8D, 0x2D, 0xE1, + 0xB4, 0xC7, 0xD9, 0xC7, 0xAF, 0x06, 0x74, 0x51, 0xDE, 0x01, 0xEF, 0xEF, 0x9E, 0xD8, 0x80, 0x01, 0x96, 0xBD, 0xCC, 0x44, 0xB8, 0x55, 0x1B, 0x44, + 0x75, 0x74, 0xAB, 0x7D, 0xC2, 0xB9, 0x0D, 0x57, 0x62, 0x81, 0xBB, 0x5E, 0x75, 0x1B, 0x23, 0x1B, 0x2F, 0x01, 0xE3, 0xF3, 0x89, 0x5F, 0xD2, 0xB2, + 0xEC, 0x6A, 0x91, 0xC9, 0xCD, 0xED, 0xD4, 0x8C, 0x43, 0x51, 0xEE, 0xCC, 0x59, 0xE6, 0x4F, 0x0F, 0xA8, 0x7B, 0x67, 0x92, 0x5F, 0x8F, 0xAE, 0x7D, + 0xDE, 0x59, 0x0B, 0x54, 0x49, 0xC8, 0x8D, 0x37, 0x7B, 0x86, 0x2F, 0x3C, 0x52, 0xF9, 0x1F, 0xC4, 0x0E, 0x15, 0xE9, 0xAC, 0x6A, 0xED, 0x2D, 0x62, + 0xE9, 0x9E, 0xD2, 0x70, 0x96, 0x78, 0x9B, 0x59, 0xF9, 0x99, 0x5D, 0xC1, 0xB5, 0xEE, 0xF4, 0x9C, 0x65, 0x1C, 0x3F, 0x9A, 0x50, 0x0E, 0x89, 0xE9, + 0xD0, 0xCE, 0xEE, 0x80, 0xF9, 0xC3, 0xD8, 0xE6, 0xCA, 0x91, 0xB4, 0x8D, 0xF1, 0xB8, 0xD5, 0x95, 0x73, 0xB2, 0x38, 0xDD, 0x16, 0xF0, 0x6C, 0x10, + 0x2E, 0xC5, 0x62, 0x5F, 0x03, 0x5D, 0xB1, 0x33, 0xC2, 0xA0, 0x5D, 0x6F, 0x69, 0xB4, 0x46, 0x49, 0x42, 0x34, 0x1F, 0x05, 0xFA, 0xF4, 0x33, 0x55, + 0xFC, 0xCC, 0x00, 0x33, 0xDA, 0x55, 0x17, 0x43, 0x9F, 0xA0, 0x3C, 0xDE, 0xAE, 0x9C, 0x9B, 0x46, 0xB0, 0x8B, 0xAD, 0x5D, 0x29, 0x0E, 0x46, 0xA1, + 0x1B, 0x24, 0x78, 0x22, 0x64, 0x30, 0x91, 0xFF, 0x2E, 0xA4, 0xB0, 0xE8, 0x52, 0xA1, 0xD8, 0xFF, 0xE0, 0x5B, 0xB3, 0xB2, 0x23, 0x44, 0x31, 0xEB, + 0xF2, 0xF4, 0xDC, 0x67, 0x74, 0xD3, 0xB5, 0x54, 0x14, 0x98, 0x5C, 0xFB, 0x50, 0xF7, 0x6E, 0xB7, 0xD8, 0x67, 0x22, 0xE2, 0x73, 0x86, 0xEC, 0xBB, + 0x3E, 0xA3, 0x5B, 0x6F, 0x9F, 0x18, 0x08, 0x0F, 0x20, 0xB7, 0x0E, 0x14, 0xC8, 0x96, 0x20, 0x76, 0xA7, 0xC4, 0x25, 0x5D, 0xA5, 0x9A, 0xAD, 0x05, + 0xE9, 0x81, 0x0F, 0xB1, 0x84, 0x77, 0xA2, 0xD6, 0x48, 0x7A, 0xAE, 0xBF, 0x31, 0x11, 0xF2, 0x3D, 0x8F, 0xA1, 0x3E, 0x46, 0x43, 0x5E, 0x29, 0xDD, + 0xAD, 0x85, 0x09, 0xDF, 0x6B, 0xCC, 0x52, 0xAE, 0xFB, 0xC1, 0x0A, 0xC7, 0xCB, 0x5D, 0xC7, 0xD6, 0xAD, 0x06, 0x90, 0x48, 0x40, 0xB9, 0x11, 0x82, + 0x52, 0x99, 0x03, 0xD0, 0xB4, 0xB4, 0x4D, 0xFF, 0xE3, 0xC8, 0xC9, 0x31, 0xD2, 0x3F, 0x2D, 0x81, 0xFA, 0x8A, 0x3F, 0x7D, 0x1F, 0x6F, 0xFC, 0x0A, + 0x7A, 0x6C, 0xFB, 0xE0, 0x50, 0xB7, 0x75, 0x95, 0xCA, 0x28, 0x53, 0xF4, 0xE5, 0x51, 0x91, 0x07, 0xCD, 0xCF, 0x34, 0xF1, 0x0D, 0xC5, 0xF5, 0x27, + 0x7D, 0x7F, 0x89, 0xDE, 0x47, 0x05, 0xE7, 0x4B, 0xBC, 0x8A, 0x7A, 0xB1, 0x5C, 0xED, 0x2F, 0x94, 0xF3, 0x96, 0x60, 0x12, 0x69, 0xB0, 0x70, 0xF1, + 0x27, 0xBC, 0xA8, 0x4B, 0x7D, 0x1F, 0xCB, 0x5F, 0x76, 0x7E, 0x39, 0xC2, 0xCD, 0xBD, 0x76, 0x1B, 0xBC, 0xAE, 0xDB, 0x50, 0x66, 0xFB, 0x8E, 0xE9, + 0x90, 0xD0, 0x91, 0x45, 0xAD, 0x2F, 0x4F, 0xE5, 0xF1, 0x83, 0x62, 0xF7, 0x32, 0xE6, 0x14, 0xC9, 0xD6, 0x41, 0x0A, 0xB9, 0x24, 0x1F, 0xD1, 0x25, + 0xD6, 0x0C, 0xDE, 0x7C, 0xA2, 0xA3, 0xF8, 0x46, 0xBF, 0xF7, 0x68, 0x5E, 0xE6, 0x29, 0xF4, 0x38, 0x1A, 0xEA, 0xA5, 0xD4, 0xFB, 0xDF, 0xEE, 0x54, + 0x88, 0x88, 0xE6, 0xCF, 0x51, 0xA6, 0x68, 0x59, 0xAE, 0xA0, 0xEE, 0xB7, 0xC8, 0x23, 0xF3, 0x5D, 0x42, 0xBB, 0x52, 0xC4, 0x22, 0xB7, 0xCB, 0x4E, + 0x87, 0xAE, 0xFD, 0x9D, 0x0E, 0x8B, 0x59, 0x7C, 0xCF, 0x36, 0x35, 0x96, 0xB3, 0xF5, 0x44, 0x53, 0x32, 0x68, 0x0D, 0x8D, 0x82, 0x8D, 0x6C, 0x06, + 0x25, 0x08, 0x6F, 0x12, 0x99, 0xC3, 0x26, 0xC8, 0x6E, 0xC6, 0x84, 0x01, 0xD0, 0x3F, 0x7A, 0x8E, 0xCE, 0xD9, 0xF3, 0xC9, 0xDA, 0xE7, 0x0B, 0xFA, + 0x77, 0xD5, 0xA9, 0x77, 0xF3, 0x6C, 0x83, 0xAE, 0x37, 0x98, 0x42, 0xCE, 0x26, 0xAB, 0x9D, 0x5C, 0xA4, 0x28, 0x31, 0x92, 0x8D, 0x88, 0x60, 0x48, + 0x36, 0x89, 0x63, 0xA5, 0x7D, 0xB1, 0xC8, 0x67, 0x95, 0xC6, 0x6F, 0x78, 0xDE, 0xE4, 0xF8, 0xB3, 0x6A, 0x06, 0x20, 0xD6, 0x69, 0x06, 0x08, 0xF7, + 0xA0, 0x3E, 0xAD, 0x80, 0x1C, 0x00, 0xA8, 0x7E, 0xC5, 0x2B, 0xA5, 0xB0, 0x63, 0x36, 0x61, 0x35, 0x27, 0xA5, 0x0F, 0x3B, 0x28, 0x60, 0x70, 0xE5, + 0xC2, 0xA8, 0x62, 0x78, 0xAE, 0xD8, 0xB0, 0x53, 0x9C, 0xA7, 0xA7, 0xEC, 0x6A, 0xE1, 0xCA, 0x58, 0x61, 0x37, 0x5F, 0x2D, 0x83, 0x03, 0x37, 0xC6, + 0x7E, 0x02, 0x80, 0xBD, 0xDD, 0x09, 0xBD, 0x3B, 0x3C, 0x79, 0x19, 0x4E, 0x7A, 0xD5, 0xF8, 0x05, 0x92, 0x31, 0x6B, 0x09, 0x00, 0x5C, 0xB0, 0x38, + 0x63, 0x70, 0x76, 0x2A, 0xF1, 0x5C, 0x1D, 0xD2, 0xED, 0x23, 0x0D, 0x8A, 0x5D, 0x5C, 0x54, 0x92, 0xCB, 0xCA, 0x52, 0x16, 0xD5, 0xB2, 0x54, 0x01, + 0x14, 0x1E, 0xA1, 0x21, 0x38, 0xA7, 0xD8, 0x4B, 0xB9, 0xF6, 0x72, 0xF4, 0xDD, 0x5A, 0x6F, 0x9C, 0x58, 0xBD, 0x1B, 0x1F, 0x6D, 0x60, 0x41, 0x16, + 0x5F, 0xF6, 0x50, 0xAA, 0xA9, 0x4C, 0x06, 0x3F, 0xE0, 0x30, 0xDB, 0x9E, 0x33, 0x54, 0xF8, 0xC4, 0xD3, 0x38, 0x5E, 0x25, 0x6E, 0x6B, 0xC7, 0x32, + 0xED, 0xA9, 0xE1, 0xBC, 0xFC, 0x08, 0x4A, 0x48, 0xAC, 0x45, 0x21, 0x30, 0x47, 0xD4, 0xEE, 0x92, 0xE1, 0x51, 0x5B, 0xE3, 0xD1, 0xAD, 0xA4, 0x20, + 0x4F, 0xAC, 0x80, 0xDF, 0x82, 0x75, 0x2E, 0x8F, 0xAC, 0x5C, 0x84, 0x11, 0x10, 0xBE, 0xBE, 0x19, 0x9D, 0x5F, 0x7C, 0xDC, 0x71, 0xE0, 0xEA, 0xA5, + 0x47, 0x75, 0x11, 0x40, 0xC7, 0xC3, 0x8E, 0x6C, 0x55, 0x63, 0x7F, 0xAF, 0x56, 0xAC, 0x50, 0xC3, 0xBA, 0x3F, 0x5C, 0x3B, 0x0C, 0x2D, 0x7F, 0xA4, + 0xEF, 0xFE, 0x47, 0x7B, 0xEA, 0x0C, 0xBE, 0x10, 0x56, 0x9C, 0xF9, 0x8A, 0xFA, 0xF2, 0xBF, 0xEB, 0x47, 0x90, 0x07, 0xFE, 0x46, 0x8F, 0x4D, 0xB5, + 0x9D, 0x0A, 0x94, 0xC7, 0xF8, 0xCB, 0xB6, 0xFE, 0x92, 0x0A, 0xD2, 0xA1, 0x73, 0x7A, 0xBA, 0xD7, 0xEC, 0x5A, 0x92, 0x58, 0xBB, 0x36, 0x47, 0x2B, + 0x8F, 0x67, 0xD1, 0x3C, 0x1B, 0x46, 0x39, 0xC2, 0x39, 0x41, 0xD3, 0xC6, 0x03, 0xDD, 0x92, 0x64, 0x6B, 0x75, 0x9E, 0xE2, 0x69, 0xF7, 0x08, 0x7F, + 0x44, 0xB1, 0x44, 0x18, 0x4F, 0x88, 0xF2, 0x19, 0x40, 0x3F, 0x12, 0xAC, 0x5C, 0x2A, 0xD3, 0x62, 0xB2, 0x3B, 0x6A, 0x08, 0x86, 0x5D, 0x94, 0x7F, + 0xCD, 0x99, 0x06, 0xF5, 0x75, 0xAC, 0xD9, 0xCA, 0x12, 0xD0, 0x95, 0x9D, 0xA3, 0xBA, 0x11, 0xB6, 0x33, 0x1B, 0xC1, 0x54, 0x64, 0x65, 0xB8, 0xAD, + 0x18, 0x65, 0xD2, 0xA0, 0x16, 0x50, 0xA8, 0x59, 0xBC, 0x69, 0x7C, 0x48, 0x5B, 0x71, 0x79, 0x99, 0x6E, 0x49, 0x2B, 0x81, 0xC1, 0xB1, 0xCE, 0x4A, + 0x32, 0x73, 0x01, 0x08, 0xE4, 0x34, 0x42, 0xEC, 0xE6, 0x06, 0x99, 0x1C, 0x05, 0xE1, 0x0B, 0xB4, 0x89, 0x76, 0x45, 0xB7, 0x5C, 0x18, 0xC0, 0xD2, + 0x70, 0xD6, 0xC9, 0x5A, 0x24, 0x20, 0x76, 0x23, 0x54, 0x5D, 0xCA, 0xC9, 0xB7, 0x24, 0x0B, 0x8B, 0x3F, 0xB5, 0x19, 0x93, 0x14, 0xFF, 0x98, 0x62, + 0xDF, 0xEA, 0xDB, 0x91, 0x11, 0x16, 0x99, 0x7F, 0x35, 0x37, 0x1B, 0xB0, 0x2E, 0xE1, 0x7C, 0x6B, 0x38, 0xD7, 0xAD, 0x7D, 0x76, 0x79, 0x5E, 0xC9, + 0x9D, 0x32, 0x21, 0xD9, 0x44, 0xE5, 0x7D, 0x61, 0xD8, 0xDE, 0x7E, 0x40, 0xA1, 0x92, 0x78, 0xEC, 0xA2, 0x8E, 0x4C, 0xAB, 0xE5, 0xF0, 0xBA, 0xD3, + 0xC6, 0x0C, 0xEC, 0x69, 0x62, 0xC3, 0x82, 0x30, 0x99, 0xF8, 0x60, 0x4A, 0x82, 0x99, 0x5B, 0x61, 0xC1, 0x1D, 0xA7, 0xF2, 0x90, 0x68, 0xFB, 0x1F, + 0x4D, 0xE2, 0x78, 0xAB, 0x33, 0x3D, 0x4C, 0xFE, 0x0C, 0x8F, 0xDA, 0xC5, 0xE1, 0xE6, 0x11, 0xA7, 0x13, 0x91, 0x83, 0xC6, 0x04, 0x04, 0x2E, 0xD7, + 0x43, 0x78, 0xFC, 0xEE, 0x38, 0x2D, 0x55, 0x10, 0xDE, 0x99, 0xE6, 0x83, 0x61, 0xE4, 0x21, 0xC8, 0x99, 0x81, 0xA8, 0x42, 0xA0, 0x9F, 0x54, 0xBD, + 0xB9, 0xC5, 0xED, 0x55, 0x47, 0x5E, 0x19, 0x83, 0x2A, 0x1C, 0xF6, 0x44, 0x73, 0xCB, 0x64, 0x7C, 0x66, 0x49, 0xBE, 0x6D, 0xE7, 0x58, 0xD2, 0x3F, + 0x81, 0x54, 0xD7, 0xAD, 0x54, 0x79, 0x2A, 0xF7, 0x78, 0xE3, 0xBE, 0xB4, 0xF1, 0x95, 0xA0, 0xD0, 0xFC, 0xE2, 0x02, 0x3B, 0x8B, 0x0D, 0x8E, 0x7D, + 0xE6, 0xCA, 0x96, 0x85, 0xFE, 0x66, 0x21, 0xA0, 0x75, 0x26, 0x0C, 0x27, 0x3D, 0x00, 0xA1, 0x50, 0x2E, 0x4F, 0xE2, 0xE2, 0x4F, 0x4D, 0xDA, 0x60, + 0x37, 0xEE, 0xD8, 0x44, 0xC9, 0xD8, 0xB9, 0xB2, 0x7B, 0x0A, 0xCA, 0x08, 0xCF, 0x64, 0xB7, 0xC7, 0xAC, 0x03, 0xE2, 0x8A, 0x03, 0x18, 0x71, 0x90, + 0x69, 0xE2, 0xF6, 0xF3, 0x7E, 0xFD, 0x79, 0xC7, 0xC1, 0x72, 0x40, 0x4C, 0x66, 0xB9, 0x73, 0xF4, 0x19, 0xAD, 0x1B, 0xFE, 0x98, 0x70, 0xF5, 0xD5, + 0x63, 0x6D, 0x27, 0x92, 0x48, 0x4E, 0x45, 0x1D, 0x74, 0x31, 0xC0, 0x6A, 0xF3, 0x28, 0x60, 0x0E, 0x8E, 0x1A, 0x92, 0xA1, 0x16, 0x90, 0x1A, 0xFE, + 0x5B, 0x6A, 0x80, 0xF2, 0x11, 0x51, 0xD6, 0xE6, 0x7A, 0x79, 0x32, 0x2B, 0x54, 0x5F, 0xE3, 0x41, 0xE2, 0xEB, 0x49, 0x89, 0x78, 0xBB, 0x51, 0x6E, + 0x9A, 0xE7, 0x0A, 0x8A, 0x47, 0x86, 0x02, 0xFD, 0x26, 0x3F, 0x62, 0xD0, 0xD6, 0x26, 0x3E, 0x15, 0xE5, 0x76, 0xB9, 0x96, 0xCA, 0x09, 0x32, 0xEE, + 0x10, 0x50, 0x67, 0x04, 0xA2, 0x31, 0x0A, 0xE6, 0x51, 0x6C, 0x0C, 0xCC, 0x1E, 0x1F, 0x16, 0xAB, 0x2E, 0x73, 0x6F, 0x4D, 0x8C, 0xFD, 0x74, 0x87, + 0x17, 0x08, 0x9A, 0x33, 0xA8, 0xB7, 0x34, 0x3D, 0xF2, 0x37, 0x81, 0xBB, 0xB2, 0x89, 0x6C, 0x43, 0x22, 0x98, 0x68, 0x6B, 0x44, 0x64, 0x11, 0xA8, + 0xA9, 0xD7, 0x6E, 0x55, 0x9D, 0xB5, 0xE2, 0x95, 0x6D, 0x40, 0x88, 0x72, 0x6C, 0x12, 0xB7, 0xAC, 0x3A, 0xDF, 0x8F, 0x2D, 0xA3, 0x61, 0x4C, 0xB5, + 0xAF, 0x90, 0x99, 0x70, 0xE3, 0xB3, 0xA0, 0x43, 0x1B, 0xA9, 0x0E, 0x5B, 0x28, 0xCD, 0x07, 0xDC, 0x35, 0xD8, 0xE8, 0x6E, 0x22, 0xDF, 0xA1, 0xF5, + 0x79, 0x2E, 0xC5, 0x4A, 0x56, 0x02, 0x51, 0xCC, 0x72, 0x43, 0xA5, 0xA8, 0xF4, 0x0D, 0x26, 0x01, 0xCD, 0xB8, 0x0C, 0x69, 0x77, 0x7B, 0x3A, 0x50, + 0x51, 0x2E, 0x05, 0xD4, 0x9C, 0x1C, 0xB8, 0xDA, 0x89, 0x61, 0xB7, 0x69, 0x80, 0x3B, 0xF8, 0xC1, 0xA2, 0xE9, 0xCE, 0xD3, 0xBD, 0x1A, 0xDA, 0x33, + 0x8E, 0x53, 0xC3, 0x7A, 0x0A, 0xBE, 0xA8, 0x1B, 0x31, 0x31, 0x67, 0x7E, 0x47, 0x74, 0x67, 0xFA, 0xED, 0x47, 0x68, 0x04, 0x9A, 0x46, 0xB2, 0x35, + 0x86, 0xDA, 0x95, 0x15, 0xB9, 0x66, 0xD3, 0x71, 0x55, 0x7E, 0xCF, 0x6D, 0xAA, 0x4B, 0x24, 0x9E, 0x61, 0x73, 0x69, 0x47, 0x39, 0xE5, 0x0E, 0x86, + 0x9F, 0xC1, 0x7C, 0xA2, 0xF9, 0xD6, 0xCE, 0xA2, 0x6F, 0x1E, 0x2D, 0xDE, 0xCE, 0x5A, 0x08, 0xD0, 0xAD, 0xE2, 0x6C, 0x8B, 0xB0, 0xE5, 0x37, 0x1C, + 0x4B, 0xF3, 0xB2, 0x3F, 0xAD, 0x83, 0x06, 0xDA, 0x06, 0x69, 0x9C, 0xE7, 0xCE, 0xAB, 0x2D, 0x8A, 0xBB, 0x73, 0x63, 0x74, 0xDA, 0x6A, 0xE9, 0xB7, + 0x09, 0xFF, 0x75, 0x35, 0xCC, 0x1C, 0xFB, 0x88, 0xAD, 0xB0, 0xBD, 0x90, 0xA3, 0x2D, 0x77, 0x5D, 0xC8, 0x61, 0xC6, 0x59, 0x92, 0x6C, 0xB7, 0x4C, + 0x90, 0x5F, 0x6E, 0xF0, 0x2F, 0x98, 0x7B, 0x81, 0xAB, 0x1C, 0xA3, 0x16, 0x83, 0x2C, 0x43, 0x17, 0xCB, 0xB1, 0x9A, 0xCB, 0x2C, 0xC6, 0x2D, 0x77, + 0x8C, 0xCA, 0x26, 0x72, 0xB2, 0x9F, 0xA4, 0x29, 0xB7, 0xBD, 0x0B, 0x38, 0xCD, 0xDB, 0x8E, 0xEC, 0x46, 0xE3, 0xD8, 0x19, 0x5A, 0xE9, 0xB9, 0x94, + 0x94, 0x0A, 0xA4, 0x9D, 0x1B, 0xBA, 0x42, 0x53, 0x35, 0xB8, 0x22, 0xD3, 0x53, 0x5F, 0x7C, 0x68, 0xBD, 0xFC, 0x44, 0xB9, 0x27, 0xDA, 0xBE, 0x80, + 0x20, 0x36, 0x09, 0x1F, 0xFB, 0x90, 0xC9, 0xB4, 0x1A, 0xDA, 0x54, 0x7B, 0x55, 0xD7, 0x5A, 0xD2, 0x71, 0x72, 0x3B, 0xC5, 0x06, 0xCD, 0xE1, 0xB6, + 0x7E, 0x3E, 0x73, 0x85, 0xCE, 0x3D, 0x10, 0x1F, 0xF5, 0x1D, 0xBD, 0x79, 0x64, 0x30, 0x52, 0xC8, 0x07, 0xF2, 0x9C, 0x7E, 0x33, 0xBF, 0x13, 0x70, + 0x92, 0xD2, 0xC7, 0x32, 0x85, 0xB1, 0xF4, 0xC7, 0xF0, 0xA1, 0xEF, 0x52, 0xE5, 0xA7, 0x6C, 0xEA, 0x5C, 0xEE, 0x8B, 0x1D, 0x88, 0x13, 0xE6, 0x46, + 0x36, 0x66, 0x20, 0x9A, 0x2D, 0x92, 0xE8, 0xB2, 0xB1, 0x83, 0x69, 0x4D, 0x86, 0x69, 0xFD, 0x28, 0xB1, 0x71, 0xE7, 0x7A, 0x02, 0x8C, 0x44, 0x4B, + 0x5C, 0x76, 0x00, 0x3F, 0xDD, 0xDB, 0x56, 0x2E, 0x5B, 0xCF, 0x71, 0x7D, 0x5D, 0x22, 0xC2, 0x70, 0xA5, 0x7F, 0x28, 0xC6, 0x69, 0xA3, 0xF1, 0xF6, + 0xAC, 0x2E, 0xEF, 0xF5, 0x90, 0x5D, 0x87, 0x71, 0xFC, 0xEC, 0xE9, 0x0B, 0x31, 0xA7, 0x0A, 0x75, 0xC2, 0xE8, 0x46, 0x95, 0xFC, 0xC9, 0x1C, 0x13, + 0xBB, 0xF5, 0x20, 0x16, 0x15, 0xD2, 0x47, 0x0E, 0xD3, 0x5B, 0x43, 0x08, 0xE0, 0x72, 0x97, 0xCD, 0x68, 0x1C, 0x19, 0x54, 0x53, 0xE5, 0xDD, 0x64, + 0x2F, 0x48, 0x0E, 0xBF, 0xA8, 0xB2, 0x4E, 0x16, 0x0C, 0x29, 0xCB, 0x84, 0x51, 0xCC, 0xE0, 0x96, 0x9E, 0x97, 0x7A, 0xE1, 0x47, 0xA4, 0x76, 0x9F, + 0x7B, 0x7F, 0xB9, 0x54, 0x8C, 0xB5, 0x5A, 0x54, 0x01, 0x9D, 0x13, 0xF6, 0x15, 0xA1, 0xD3, 0x37, 0x02, 0x66, 0x2F, 0x79, 0x5F, 0x36, 0x8E, 0xD8, + 0xDD, 0x74, 0x46, 0xAB, 0xEF, 0xFB, 0x28, 0xA3, 0x56, 0x82, 0x95, 0xE6, 0x3F, 0x5C, 0x68, 0x58, 0x31, 0x54, 0x07, 0x61, 0xD7, 0x54, 0x74, 0x62, + 0x46, 0x76, 0x56, 0xE5, 0xC9, 0xC6, 0x8F, 0x0E, 0x3A, 0x9A, 0x19, 0xFF, 0xA5, 0xF4, 0x23, 0x8B, 0x91, 0x77, 0x74, 0x99, 0x81, 0x5F, 0x94, 0xD6, + 0x0C, 0xEB, 0xDC, 0x56, 0xD0, 0x08, 0x3D, 0xDA, 0x81, 0xBD, 0x14, 0x88, 0x80, 0xD1, 0x37, 0xDB, 0x6E, 0x38, 0xB6, 0x6E, 0xC0, 0x09, 0x22, 0xAA, + 0xA7, 0x05, 0xDE, 0xAF, 0x86, 0x1C, 0x75, 0xC7, 0xF3, 0x53, 0xD8, 0x31, 0x9D, 0xF5, 0xCC, 0x58, 0xF2, 0x6A, 0x63, 0x8D, 0x18, 0xB9, 0xD4, 0x12, + 0xBF, 0x44, 0xAB, 0x3C, 0x73, 0xFD, 0x5B, 0x96, 0x58, 0x61, 0x46, 0xD9, 0x65, 0xD7, 0x5F, 0x3F, 0x63, 0x52, 0x8E, 0xDC, 0xDC, 0xB0, 0x28, 0x53, + 0x2A, 0xC1, 0xD3, 0x21, 0x67, 0x12, 0x41, 0x46, 0x4F, 0x78, 0x8A, 0x27, 0xEF, 0xEA, 0x40, 0xD6, 0x71, 0x0D, 0x7F, 0x44, 0x17, 0x39, 0x45, 0x91, + 0x80, 0xE6, 0x73, 0x08, 0xBB, 0xF5, 0xD4, 0x0B, 0xD4, 0x4C, 0xDF, 0xE9, 0xDC, 0xA0, 0xE5, 0x1C, 0xCF, 0xB8, 0x72, 0xAD, 0x13, 0x0D, 0x77, 0xAC, + 0x79, 0xCF, 0x31, 0x42, 0xCA, 0x36, 0x16, 0x7D, 0x9F, 0xB0, 0x1C, 0x7C, 0xFC, 0xDE, 0x37, 0x9A, 0xE2, 0x49, 0x02, 0x31, 0x6B, 0xF4, 0x27, 0x76, + 0x02, 0x46, 0x0F, 0x2A, 0x56, 0x59, 0x05, 0xD3, 0x17, 0x5A, 0x2B, 0xBD, 0x1D, 0x5A, 0x11, 0xB4, 0x49, 0x64, 0xB1, 0xC5, 0xA1, 0x14, 0x06, 0xB5, + 0x11, 0x5B, 0x22, 0x13, 0x72, 0x1D, 0x4B, 0x1F, 0xA5, 0xFC, 0x04, 0x37, 0x0F, 0xC4, 0xDA, 0x8A, 0x9A, 0x9F, 0x57, 0xDE, 0x60, 0xDB, 0x00, 0xEF, + 0x1E, 0x47, 0xC9, 0xB1, 0x1C, 0x3F, 0x3B, 0x03, 0x11, 0xE0, 0xCE, 0xEF, 0xAE, 0x86, 0x1B, 0xF8, 0x64, 0x5B, 0xEF, 0xF6, 0x2A, 0xD4, 0xBD, 0xC1, + 0x1C, 0x01, 0xAF, 0xDB, 0xF1, 0x55, 0x0A, 0xDB, 0x6C, 0xC3, 0xCE, 0x89, 0xB2, 0xFB, 0x24, 0x9C, 0xAD, 0x5E, 0xD3, 0x30, 0x9C, 0x56, 0x44, 0x93, + 0xB6, 0x61, 0xFB, 0x7B, 0x53, 0x08, 0x9E, 0xA0, 0xED, 0x8E, 0xD3, 0x70, 0x79, 0xEC, 0x06, 0xA8, 0x06, 0x9D, 0xF1, 0xFD, 0x31, 0x19, 0x0F, 0x66, + 0x57, 0xAD, 0x3C, 0xA5, 0xD7, 0x3F, 0xF7, 0x87, 0x3F, 0x97, 0x30, 0x3E, 0x4E, 0xFB, 0xD4, 0x76, 0x03, 0x8D, 0x0F, 0xEE, 0xE6, 0x5D, 0x45, 0xB1, + 0x2B, 0xB8, 0xFD, 0x1D, 0x5C, 0x15, 0x41, 0x57, 0x8A, 0x10, 0xAE, 0x7D, 0x47, 0x2C, 0xAC, 0x1A, 0xDA, 0x54, 0xDD, 0x72, 0x11, 0xF5, 0x11, 0x8D, + 0xFE, 0xD8, 0x8C, 0x3A, 0xBA, 0xB8, 0x44, 0x66, 0x18, 0x70, 0x5F, 0xF4, 0x1C, 0x83, 0x18, 0x7D, 0xF6, 0x96, 0x22, 0x67, 0xE0, 0xB8, 0xEF, 0xEE, + 0xAB, 0x7B, 0xA5, 0x79, 0xA1, 0x9E, 0x42, 0x7B, 0xF0, 0x1C, 0xF4, 0x96, 0xC4, 0x3C, 0x76, 0x12, 0xCF, 0x7B, 0x5F, 0xC0, 0xBC, 0x9D, 0x6C, 0x14, + 0x83, 0xE7, 0xF9, 0x71, 0x1A, 0xEE, 0x66, 0x63, 0x8C, 0x28, 0x0C, 0xF6, 0x3F, 0xBF, 0x54, 0xAE, 0x49, 0x9A, 0x80, 0xED, 0x34, 0x13, 0xB2, 0x3C, + 0x14, 0x47, 0x50, 0x9A, 0x3B, 0x27, 0x14, 0x00, 0xB6, 0x56, 0x4E, 0xCD, 0xB3, 0x50, 0x1E, 0xDB, 0x89, 0xAC, 0x08, 0x61, 0x0C, 0xA2, 0x47, 0xDA, + 0xD2, 0xB4, 0xD3, 0x79, 0xB5, 0xF3, 0x4A, 0xC8, 0xE7, 0xBF, 0xE0, 0x65, 0xF5, 0x6D, 0x00, 0x7F, 0xA9, 0x8F, 0x59, 0x18, 0x3C, 0xE5, 0x67, 0x8C, + 0x0C, 0x82, 0x8C, 0x57, 0xA8, 0x57, 0xC7, 0x2C, 0x07, 0xA2, 0x40, 0x6C, 0x85, 0xB3, 0xB0, 0x83, 0xD7, 0xCA, 0x1A, 0x51, 0x4E, 0x67, 0x32, 0xB5, + 0xB1, 0xCF, 0x0F, 0xFC, 0xFC, 0x9F, 0x7D, 0xCC, 0x3F, 0x34, 0xDF, 0x9A, 0xD0, 0xCF, 0x06, 0x6D, 0x19, 0x65, 0x5C, 0x7F, 0xD2, 0x85, 0xE0, 0xFF, + 0xAA, 0x4C, 0xEA, 0x45, 0xAE, 0x2D, 0x81, 0x2B, 0x36, 0x16, 0xBE, 0x27, 0x33, 0x7A, 0xEC, 0xD9, 0xDE, 0x8B, 0x0E, 0x0F, 0x54, 0x18, 0x50, 0xF6, + 0xE1, 0x1E, 0x7D, 0x1D, 0x85, 0xF7, 0xA1, 0x81, 0xC4, 0x06, 0x89, 0xC0, 0x5B, 0x8B, 0x64, 0x13, 0x82, 0xBA, 0x9A, 0x38, 0xD5, 0x53, 0x7F, 0xC4, + 0x3D, 0x1B, 0x95, 0xA3, 0xF1, 0x32, 0x6F, 0x9C, 0x18, 0x40, 0x93, 0xCC, 0xDD, 0x34, 0x7E, 0x5B, 0xE9, 0x18, 0x84, 0x93, 0x91, 0x11, 0xA6, 0xCE, + 0x18, 0x22, 0x4E, 0x1D, 0x60, 0x8A, 0x40, 0x23, 0x23, 0xD4, 0xFF, 0x0F, 0xEE, 0xB9, 0xF9, 0xEE, 0x8C, 0xEB, 0x5E, 0xBA, 0x73, 0xE6, 0xFE, 0xD7, + 0x7B, 0x1F, 0xFC, 0x3B, 0xF3, 0xDC, 0x9B, 0x7B, 0xAF, 0xFA, 0x67, 0xCD, 0xD7, 0xB7, 0xDE, 0xD3, 0x79, 0xFD, 0x01, 0xC7, 0x79, 0x18, 0xC8, 0xBC, + 0x12, 0xA7, 0x3E, 0x9A, 0x66, 0xDE, 0xD6, 0x52, 0x1D, 0x5C, 0x71, 0x24, 0x64, 0x0D, 0xEE, 0xC0, 0x40, 0xC9, 0xB0, 0x32, 0x86, 0xA9, 0x2E, 0x4C, + 0x95, 0xE9, 0x35, 0xE5, 0x0C, 0xD8, 0xA2, 0x73, 0x3A, 0xDD, 0xF8, 0x28, 0x52, 0x1C, 0xCA, 0xE3, 0xA9, 0x09, 0xE6, 0xE3, 0x8D, 0xDE, 0x96, 0x07, + 0x48, 0x65, 0x30, 0xC9, 0xC3, 0xD3, 0x9B, 0x45, 0x9A, 0x30, 0x5E, 0xC1, 0xDE, 0xAE, 0xD7, 0xCD, 0x60, 0x8D, 0x5B, 0x71, 0xC6, 0xB7, 0xD3, 0xBD, + 0x0F, 0x77, 0xF6, 0xEA, 0xF3, 0x6A, 0x04, 0x56, 0xCB, 0xBA, 0xAB, 0x57, 0x73, 0x46, 0x99, 0xBA, 0x07, 0xDE, 0xD5, 0x51, 0xB6, 0xF9, 0x45, 0x2D, + 0xC3, 0xD9, 0xE8, 0x2A, 0x75, 0x7A, 0x9F, 0x21, 0xE7, 0x5A, 0x01, 0xDD, 0x09, 0x5E, 0x3C, 0xD2, 0x16, 0xA5, 0x4A, 0x45, 0x3F, 0xF2, 0x08, 0x04, + 0xFA, 0x1F, 0x9F, 0xBF, 0x6B, 0x62, 0xCD, 0x89, 0x91, 0xDC, 0xED, 0xE6, 0xC6, 0x2E, 0xDB, 0x1E, 0x7A, 0xCC, 0xD2, 0x6F, 0x4C, 0xA0, 0x18, 0x00, + 0x29, 0xFE, 0x03, 0x44, 0xAD, 0x97, 0x18, 0x43, 0xAF, 0x2E, 0xAC, 0xA3, 0x54, 0x18, 0x39, 0x76, 0xED, 0x6C, 0x5B, 0xF4, 0x2C, 0xBE, 0xEE, 0x42, + 0x88, 0xCA, 0x14, 0xB2, 0x95, 0xF5, 0xDC, 0x61, 0x33, 0xF5, 0xA4, 0x7D, 0xAF, 0xB6, 0xE2, 0x36, 0x2E, 0x57, 0xFC, 0x99, 0x8F, 0xE5, 0x3B, 0x83, + 0x7B, 0xC8, 0x12, 0xC3, 0x39, 0x4B, 0x1E, 0x7F, 0xDC, 0x37, 0xC8, 0xEF, 0xAD, 0x13, 0xDD, 0xCF, 0x90, 0x28, 0x7E, 0x50, 0x51, 0x90, 0x13, 0x2E, + 0x3D, 0x1B, 0x1B, 0xD1, 0xA8, 0xD8, 0xBD, 0xAA, 0xFF, 0x54, 0x13, 0x5D, 0x0F, 0xCD, 0x1A, 0xFD, 0x6B, 0xEA, 0xDD, 0x62, 0x2A, 0x33, 0x2D, 0x62, + 0x84, 0xEB, 0x68, 0x3F, 0xD8, 0x86, 0xBC, 0xAA, 0x7F, 0x00, 0x95, 0xDC, 0xA6, 0xE2, 0x08, 0x00, 0x7F, 0xE8, 0x91, 0x49, 0x8B, 0x32, 0x06, 0x3A, + 0xBA, 0x12, 0xA1, 0x70, 0x83, 0x79, 0xED, 0xCC, 0xF4, 0x27, 0xE5, 0x17, 0xB0, 0x90, 0xB6, 0x30, 0x0E, 0x8F, 0x1A, 0x75, 0xD2, 0x38, 0x14, 0x41, + 0x4F, 0x1B, 0xD7, 0x37, 0x19, 0xFA, 0xD8, 0x88, 0xCF, 0x59, 0x88, 0xF3, 0x01, 0x7E, 0xAA, 0x39, 0xF7, 0xCB, 0xAF, 0x1B, 0x25, 0x24, 0xD7, 0x7A, + 0x97, 0xAB, 0xA8, 0xA0, 0xE2, 0xE0, 0xEC, 0xFF, 0x97, 0x5D, 0xE9, 0xE8, 0x2C, 0xCD, 0xDD, 0xE8, 0x04, 0xA7, 0x80, 0xD0, 0x99, 0x17, 0xFE, 0x1A, + 0x7F, 0x42, 0xD4, 0xF2, 0x99, 0xDD, 0x14, 0xBD, 0x1D, 0xB9, 0x6F, 0xBE, 0xBB, 0x0D, 0x4E, 0x47, 0xE4, 0xD5, 0x09, 0xCE, 0x05, 0xFF, 0x08, 0x93, + 0x1A, 0x38, 0x66, 0xB8, 0x7D, 0x86, 0xDE, 0x3D, 0xEE, 0x02, 0xB4, 0x6C, 0x32, 0x55, 0x43, 0x8A, 0x7D, 0x2F, 0xC6, 0xC0, 0xB6, 0x83, 0xD2, 0x8B, + 0x85, 0x6B, 0x59, 0xB7, 0x47, 0x18, 0xC7, 0xBB, 0x67, 0x73, 0x7B, 0x40, 0x9E, 0xEA, 0x4D, 0x91, 0x90, 0x49, 0xDD, 0x09, 0x9F, 0xBE, 0x44, 0x72, + 0x16, 0xBC, 0xF6, 0x43, 0x90, 0xF2, 0xAB, 0x28, 0x3D, 0x79, 0x0F, 0x2A, 0x99, 0x0C, 0x3B, 0xA8, 0x4D, 0xE0, 0x32, 0x5F, 0xF8, 0x27, 0xE3, 0x57, + 0x01, 0xFA, 0x3D, 0x1B, 0x5D, 0xDD, 0x11, 0x5D, 0xDF, 0x46, 0xCB, 0x1D, 0xC3, 0x5C, 0x09, 0xD6, 0x42, 0x56, 0xBE, 0x4C, 0x35, 0xFB, 0xC3, 0x66, + 0xD7, 0x54, 0xDB, 0xE4, 0xEA, 0xB7, 0xC7, 0xB6, 0x59, 0xAE, 0xAF, 0x88, 0x2E, 0x08, 0x83, 0x52, 0x0E, 0x24, 0x06, 0x3C, 0x87, 0xFB, 0xAD, 0x09, + 0xDF, 0xDE, 0xE0, 0x4C, 0xF1, 0x9B, 0x88, 0x89, 0xFD, 0x49, 0x31, 0x1B, 0x79, 0x28, 0xA6, 0xF9, 0x8E, 0x62, 0xD5, 0x53, 0x9F, 0x0A, 0xB7, 0xEC, + 0xAD, 0x62, 0xCC, 0x14, 0x61, 0xDA, 0x0F, 0x77, 0x05, 0x9C, 0x32, 0x9F, 0x77, 0xE2, 0x26, 0x19, 0x47, 0x47, 0x8D, 0xBD, 0xC7, 0xC2, 0xC2, 0x30, + 0x6C, 0x6A, 0xB6, 0x5D, 0x59, 0xCF, 0xFC, 0xC2, 0x2D, 0x50, 0xED, 0x24, 0x0A, 0x53, 0x40, 0x90, 0xEC, 0xA0, 0xFC, 0xEA, 0xBE, 0xB1, 0x9B, 0x1F, + 0x9A, 0xEC, 0x6E, 0x43, 0xD4, 0x48, 0x4A, 0xF2, 0xEC, 0xE2, 0xCA, 0xBE, 0x32, 0x70, 0x45, 0x06, 0x5C, 0x54, 0xD2, 0x8F, 0x74, 0x39, 0x51, 0xB9, + 0x2A, 0x88, 0x47, 0x2A, 0xB7, 0x8C, 0xEB, 0x2C, 0xAE, 0x1C, 0x3A, 0xD5, 0xF6, 0x22, 0x7B, 0x52, 0xF7, 0x52, 0xF6, 0xB4, 0xB8, 0xBE, 0xA7, 0x4F, + 0x73, 0x20, 0xDB, 0x69, 0xEA, 0x65, 0xEB, 0x4B, 0x8D, 0x77, 0xD0, 0x72, 0x8F, 0x66, 0xBD, 0x98, 0xBA, 0x10, 0xA7, 0x58, 0x6A, 0xA8, 0xC2, 0x69, + 0xD6, 0xE0, 0xA8, 0x4B, 0x41, 0x5A, 0xB8, 0x5C, 0xB8, 0xE9, 0xE3, 0x41, 0x5E, 0x43, 0x8D, 0x13, 0x5C, 0x06, 0xAA, 0x9B, 0xCB, 0x29, 0x34, 0x7F, + 0xBC, 0x9A, 0x71, 0x9C, 0x72, 0xE1, 0x91, 0xB9, 0x81, 0x51, 0xF6, 0x00, 0x91, 0xBC, 0xB1, 0x7A, 0x78, 0x1C, 0xD4, 0x9C, 0x8F, 0x03, 0xCB, 0xA9, + 0x65, 0x69, 0x47, 0xB7, 0x18, 0x23, 0xDB, 0x60, 0xE1, 0x4E, 0xA6, 0xDB, 0x70, 0x94, 0x1B, 0x4C, 0xA3, 0xC0, 0xC1, 0x81, 0xCA, 0x56, 0xB7, 0xB2, + 0xF7, 0x34, 0x1E, 0x82, 0xE0, 0x6C, 0xB9, 0x79, 0x4C, 0x74, 0xF5, 0x6D, 0x13, 0x73, 0x51, 0xCA, 0xFA, 0x7F, 0x06, 0xEB, 0x3F, 0x9F, 0x53, 0x8A, + 0xA8, 0xE9, 0xEF, 0xB9, 0x12, 0xA8, 0xB6, 0xE3, 0x2B, 0x8F, 0x1E, 0xCF, 0x6B, 0x45, 0xEC, 0x9B, 0xFA, 0xD1, 0xFC, 0xAE, 0x03, 0x8F, 0x87, 0xDD, + 0xB0, 0xBA, 0xCF, 0xF0, 0xEE, 0x12, 0x57, 0x6C, 0xC6, 0x8E, 0x58, 0x47, 0xD3, 0xC7, 0x4D, 0xF1, 0xE2, 0x8B, 0x4C, 0x53, 0x81, 0xC3, 0xAA, 0x59, + 0x3B, 0xEB, 0xEE, 0x0C, 0x66, 0x21, 0x2E, 0xE4, 0x36, 0xD7, 0x46, 0x81, 0xAF, 0xA0, 0x51, 0x43, 0x77, 0xD3, 0xDD, 0xAA, 0x8D, 0x61, 0x93, 0x1C, + 0xD1, 0x2D, 0xB8, 0xDD, 0xFC, 0xF5, 0x92, 0x9A, 0xE2, 0x61, 0x8A, 0x58, 0xD5, 0x1B, 0xC4, 0x83, 0x49, 0x0B, 0xC7, 0x95, 0x78, 0x6C, 0xE9, 0x5C, + 0x70, 0x0D, 0x48, 0xAB, 0x6E, 0x3B, 0x06, 0xA2, 0xDA, 0x6E, 0x7B, 0x61, 0x96, 0xC4, 0x5B, 0xBF, 0xEB, 0xB9, 0x97, 0xF6, 0xCC, 0x67, 0x5B, 0xC1, + 0xBA, 0x62, 0x17, 0xD4, 0xC2, 0x9B, 0xC4, 0xC9, 0x3C, 0x11, 0xE6, 0x94, 0xB3, 0x7C, 0xC1, 0xA3, 0x75, 0xE0, 0x61, 0xDF, 0x24, 0x9D, 0x4F, 0x78, + 0xBA, 0x7B, 0x87, 0x04, 0x06, 0xDE, 0xAC, 0x51, 0x4F, 0x02, 0xA1, 0x1F, 0x2E, 0xC3, 0x62, 0x80, 0xBC, 0x9B, 0x2B, 0x81, 0x87, 0x1E, 0x2B, 0x8D, + 0x83, 0x06, 0x92, 0x45, 0x60, 0xC7, 0x41, 0x1F, 0x4E, 0xD7, 0x18, 0xFC, 0x02, 0x7C, 0x7C, 0x67, 0xE2, 0x44, 0xA9, 0x77, 0x36, 0x7D, 0xFA, 0x52, + 0x88, 0x07, 0xB2, 0x37, 0x27, 0x65, 0xDE, 0x1A, 0x00, 0xB4, 0xD7, 0xD2, 0x80, 0x1B, 0x9E, 0x5F, 0xFD, 0x32, 0xB0, 0xF4, 0xB8, 0x98, 0x48, 0x22, + 0xC7, 0xE2, 0xA3, 0xEE, 0xF5, 0x23, 0xDD, 0xF6, 0xC3, 0x1F, 0xC4, 0xAD, 0x36, 0x5E, 0x2B, 0xB9, 0x5F, 0xCB, 0xBB, 0x01, 0x65, 0x55, 0x8A, 0x9E, + 0x5B, 0x42, 0xDB, 0x1E, 0x2F, 0x16, 0x5D, 0xEE, 0xD9, 0x25, 0x3D, 0xA6, 0x18, 0xF7, 0xD8, 0xA2, 0x9D, 0xC1, 0xEF, 0x30, 0x4C, 0xE8, 0x17, 0xA4, + 0x81, 0x11, 0xC9, 0xD6, 0x85, 0x55, 0x07, 0x91, 0x96, 0xFA, 0xA8, 0xD7, 0xA4, 0xED, 0x44, 0xD3, 0x8B, 0x3D, 0x97, 0x8F, 0x22, 0x8B, 0x06, 0x71, + 0x3B, 0x0E, 0x86, 0xE7, 0x70, 0x05, 0xBB, 0x5E, 0x6D, 0x58, 0x85, 0xCD, 0xF8, 0x36, 0xD5, 0x84, 0x3D, 0x1E, 0x04, 0x8A, 0xF9, 0xA3, 0x43, 0x8A, + 0xAE, 0x70, 0xC4, 0xFA, 0xDB, 0x4E, 0x1F, 0x00, 0xD1, 0x61, 0x77, 0xB9, 0x84, 0xA4, 0xC6, 0x5F, 0x60, 0xD5, 0x8D, 0x6E, 0xE4, 0x6F, 0x82, 0xA8, + 0xF4, 0xF1, 0x31, 0x06, 0x16, 0xAC, 0x97, 0x7E, 0x56, 0x6B, 0xCB, 0xF4, 0xBA, 0xCA, 0x28, 0xFB, 0xEE, 0x47, 0x91, 0x13, 0x78, 0xEF, 0x68, 0xBD, + 0xB5, 0x36, 0x17, 0x93, 0x3F, 0xE4, 0x81, 0x26, 0x49, 0x36, 0xDE, 0x3C, 0x28, 0x60, 0x26, 0x7E, 0x6B, 0xC4, 0xA4, 0xFE, 0x0D, 0x6E, 0x67, 0xD6, + 0xCE, 0x5C, 0xF7, 0xC8, 0x8B, 0x9B, 0x82, 0x15, 0x07, 0x0B, 0x6D, 0x06, 0x36, 0x05, 0x70, 0xA8, 0x14, 0x58, 0xE0, 0xAB, 0x34, 0x3B, 0x99, 0xE6, + 0xC7, 0x7F, 0xC8, 0xCA, 0x51, 0x5F, 0xD0, 0x1F, 0xD4, 0xB7, 0xF8, 0xAB, 0xAB, 0xAF, 0x7F, 0x4C, 0xBE, 0xCB, 0xFC, 0xEF, 0xAD, 0x0D, 0xA6, 0xEA, + 0xA6, 0xE2, 0x10, 0x60, 0x23, 0x5A, 0x6F, 0xA5, 0x48, 0x6B, 0xDB, 0x35, 0xF8, 0x24, 0x59, 0x6D, 0x7E, 0xA7, 0x90, 0x27, 0x90, 0xE6, 0xC5, 0xE4, + 0x25, 0x60, 0xE3, 0xDA, 0xEF, 0xA6, 0x75, 0x73, 0xAD, 0x62, 0x7D, 0x96, 0xF9, 0x8E, 0xC3, 0x85, 0xB9, 0x9C, 0xF4, 0x50, 0x64, 0xFE, 0x34, 0xE7, + 0x18, 0x1C, 0xA2, 0x1B, 0x54, 0xF7, 0x4B, 0x29, 0x86, 0x12, 0x7A, 0x24, 0xF7, 0xAA, 0x39, 0xCD, 0xDB, 0xBA, 0x05, 0x4F, 0x5B, 0xD8, 0x84, 0xC3, + 0xF6, 0xED, 0xAE, 0x05, 0x3D, 0x95, 0x86, 0x54, 0x97, 0x41, 0x02, 0x8D, 0x51, 0xB9, 0xA2, 0xD0, 0xB0, 0x2F, 0xD3, 0x5D, 0xC4, 0x05, 0x31, 0x7A, + 0xEF, 0x83, 0x4D, 0xBA, 0x81, 0x54, 0x53, 0xD2, 0x77, 0x04, 0x8B, 0x12, 0xC8, 0x46, 0xDC, 0xAE, 0xFC, 0xEE, 0xED, 0xD8, 0x09, 0x27, 0xBA, 0xFB, + 0x84, 0x67, 0xE3, 0xE9, 0xB0, 0xAC, 0x35, 0xA7, 0x00, 0x55, 0x86, 0xCD, 0x68, 0x5C, 0x69, 0x88, 0x84, 0x33, 0xCD, 0x09, 0xD8, 0x35, 0x68, 0xA1, + 0x1F, 0x33, 0x56, 0x45, 0xF4, 0xAD, 0xDD, 0x62, 0xB0, 0xF6, 0x8E, 0xCA, 0x20, 0xDA, 0x2C, 0x8E, 0x93, 0xF6, 0x2E, 0x16, 0x42, 0x8E, 0xC1, 0xEC, + 0x15, 0x46, 0xCF, 0x9F, 0x8C, 0x66, 0xF7, 0xF6, 0x66, 0x3F, 0xF3, 0x50, 0x41, 0x4C, 0x4D, 0xFD, 0x42, 0xFB, 0x6E, 0xD9, 0xE2, 0x33, 0x83, 0x20, + 0x5A, 0x3B, 0x1E, 0xE0, 0x57, 0x58, 0xA3, 0x25, 0x7A, 0x77, 0xC7, 0x88, 0x44, 0x6A, 0x3C, 0xB9, 0x7D, 0xD8, 0x2B, 0x12, 0x24, 0x3E, 0x12, 0xAD, + 0x49, 0x79, 0xC5, 0xB7, 0xDB, 0xD2, 0xBB, 0x36, 0x9A, 0x57, 0x96, 0xB3, 0x1C, 0x30, 0x75, 0x16, 0x79, 0x55, 0x8B, 0x1E, 0xD6, 0xCB, 0xED, 0xE8, + 0x19, 0x63, 0x83, 0x86, 0x8D, 0x09, 0xFF, 0x71, 0x22, 0x31, 0xD0, 0xBF, 0x8B, 0x75, 0x49, 0xAB, 0x75, 0x8E, 0xEE, 0xE5, 0xEA, 0xFF, 0xC1, 0x3C, + 0x49, 0x7C, 0x3B, 0xF5, 0xD1, 0xE8, 0xCC, 0x15, 0x45, 0x3B, 0x62, 0x8A, 0xED, 0x69, 0xD2, 0x32, 0xE4, 0xFD, 0x2A, 0x43, 0xFB, 0xD7, 0x1E, 0x5D, + 0x7A, 0x13, 0x3C, 0x58, 0x22, 0x6C, 0x2F, 0xC2, 0x52, 0x45, 0xC6, 0x7A, 0xDA, 0x6E, 0xB2, 0x56, 0xC7, 0xBA, 0x89, 0xB6, 0x64, 0xA0, 0xED, 0xF3, + 0x8F, 0xFE, 0xA5, 0x30, 0xC8, 0xD7, 0xE3, 0xA5, 0x58, 0xE4, 0xB9, 0x80, 0xE7, 0xC1, 0xE5, 0xD4, 0x05, 0x30, 0xA6, 0xE4, 0xA8, 0x51, 0x91, 0xDF, + 0xF6, 0x6A, 0x0B, 0x74, 0xF9, 0xE2, 0xEE, 0x2E, 0x88, 0xD0, 0x32, 0x0F, 0x0F, 0xC3, 0x18, 0xED, 0xE2, 0x2F, 0x23, 0x64, 0x36, 0xB4, 0x6A, 0xDE, + 0xA5, 0x81, 0x69, 0x71, 0x3C, 0x9E, 0x93, 0x32, 0xF0, 0xB6, 0xA3, 0xB8, 0xE2, 0x16, 0x68, 0xB3, 0x2C, 0x2A, 0x3A, 0x57, 0xA4, 0xD2, 0xE6, 0x83, + 0x1C, 0x62, 0xBD, 0x6F, 0xEC, 0xBA, 0xB2, 0x6D, 0xD6, 0x20, 0x7E, 0x32, 0xB6, 0x3A, 0xD3, 0xD8, 0xD1, 0xDA, 0xB0, 0x7D, 0xD7, 0xB6, 0xFF, 0x48, + 0x80, 0x8E, 0xD6, 0xF1, 0x5C, 0xE1, 0xBB, 0x5E, 0xD7, 0x48, 0xD1, 0xB9, 0x5F, 0x5C, 0x37, 0x10, 0xCF, 0x9A, 0xCD, 0x6D, 0x1B, 0xC5, 0x1C, 0x58, + 0xA4, 0x7E, 0x3C, 0xF8, 0xAF, 0x15, 0x2C, 0x10, 0xBE, 0xCC, 0x92, 0x88, 0x6E, 0x71, 0x11, 0xB0, 0x9F, 0xD7, 0x17, 0xA3, 0x83, 0x68, 0x4C, 0x8C, + 0xB6, 0x95, 0x03, 0x40, 0xCF, 0xB5, 0x9E, 0xA6, 0xF9, 0xCB, 0x8C, 0x62, 0x87, 0xB3, 0x3C, 0x9F, 0x18, 0x71, 0x61, 0x47, 0x71, 0xB1, 0x21, 0x39, + 0x65, 0xB1, 0x2F, 0xC6, 0x02, 0x11, 0xEF, 0xC6, 0x81, 0x5A, 0x18, 0x98, 0x0A, 0x44, 0x7A, 0xCC, 0x46, 0xF9, 0x52, 0xFC, 0x2A, 0x5A, 0xB8, 0xEE, + 0xD4, 0x2C, 0x26, 0xD0, 0x3D, 0xA7, 0xBA, 0xE8, 0xC9, 0x55, 0x4E, 0x9E, 0x7F, 0xA6, 0x54, 0x63, 0x35, 0x2F, 0x10, 0xF6, 0x9B, 0x6D, 0x02, 0x61, + 0x4F, 0x2C, 0xE0, 0x94, 0x13, 0x08, 0x19, 0x63, 0xE0, 0x58, 0x4C, 0x5C, 0xC6, 0xB3, 0xF1, 0x0B, 0x58, 0x81, 0x70, 0xF3, 0xE7, 0x76, 0xE0, 0x1C, + 0xF3, 0x09, 0xC4, 0xF5, 0x7D, 0x82, 0x29, 0x84, 0x77, 0xED, 0xE8, 0x7D, 0xE2, 0x71, 0x05, 0xC8, 0x64, 0x6F, 0xD0, 0xA1, 0xBB, 0xD6, 0x57, 0x75, + 0x02, 0x43, 0x17, 0x9C, 0xA3, 0xA7, 0x8D, 0xD0, 0xA3, 0xCE, 0x68, 0xDB, 0x44, 0xC9, 0xDA, 0x78, 0xE9, 0x34, 0x36, 0x0C, 0x8D, 0xAD, 0xA1, 0x6A, + 0xDE, 0xCD, 0x18, 0xE8, 0xCE, 0x2B, 0x76, 0x73, 0x35, 0xCD, 0x7F, 0xE9, 0xF1, 0x3A, 0x71, 0x95, 0x4E, 0xC7, 0x69, 0x7B, 0x73, 0xE6, 0x44, 0x98, + 0x1A, 0x57, 0xEF, 0xC4, 0x1D, 0x32, 0xE7, 0xB6, 0xCC, 0x04, 0xB6, 0x3E, 0xD4, 0x61, 0x01, 0x47, 0xD7, 0x26, 0xB6, 0xE1, 0x93, 0xAC, 0x9E, 0x60, + 0x79, 0x10, 0x3D, 0x93, 0xF1, 0x7A, 0xB2, 0x2E, 0x0E, 0x22, 0x15, 0xEE, 0x0E, 0x78, 0xD2, 0xDA, 0xD6, 0x42, 0xFD, 0x1A, 0xF9, 0x1A, 0xE8, 0x36, + 0x7B, 0xCE, 0x58, 0x0D, 0xDD, 0xFE, 0x17, 0x46, 0xD9, 0x90, 0x34, 0x01, 0x8D, 0x93, 0xD3, 0x03, 0x8A, 0x07, 0x04, 0x6E, 0xDB, 0x44, 0xFA, 0xB6, + 0xC1, 0xF4, 0x4C, 0xDD, 0x9D, 0xD9, 0x9E, 0xCF, 0x48, 0xF1, 0xB2, 0x48, 0xA7, 0xE5, 0xF5, 0x7A, 0x34, 0x08, 0xC6, 0x67, 0x3A, 0xB4, 0xEB, 0x39, + 0x93, 0xAF, 0x6F, 0x76, 0xA4, 0xB0, 0x60, 0xDF, 0x0E, 0x04, 0xA5, 0x23, 0xFE, 0xA4, 0x71, 0xAE, 0x58, 0x8E, 0xC3, 0xEC, 0xDA, 0x09, 0x90, 0x9C, + 0xED, 0x3D, 0xB2, 0xA5, 0x65, 0x1F, 0x3F, 0xEE, 0x6D, 0x31, 0x97, 0xC7, 0x38, 0xEC, 0x5D, 0x22, 0x2B, 0x73, 0x6D, 0x9F, 0x37, 0x32, 0xD8, 0x31, + 0xB6, 0xB8, 0x55, 0xDF, 0x1B, 0x23, 0xC2, 0xCD, 0x6D, 0x3F, 0x42, 0x1C, 0x89, 0x66, 0x0B, 0x96, 0xA9, 0x59, 0x1B, 0x5D, 0x84, 0x51, 0x59, 0xB2, + 0x3E, 0xA1, 0xD8, 0x96, 0x87, 0x44, 0x47, 0xD7, 0x5B, 0x62, 0x93, 0xF6, 0x5F, 0x36, 0xA0, 0xC2, 0x8B, 0x54, 0x29, 0x1F, 0x3F, 0x88, 0xB7, 0x66, + 0xFF, 0xFC, 0xC4, 0xC5, 0x36, 0xB1, 0xFB, 0xE4, 0xFA, 0xCC, 0xA5, 0x8F, 0xC3, 0x13, 0x5A, 0x4C, 0x44, 0x10, 0x70, 0xD9, 0xF1, 0xB0, 0x6C, 0x41, + 0xED, 0xB7, 0x0B, 0x50, 0x49, 0x8F, 0xED, 0x91, 0x53, 0xDC, 0x95, 0x27, 0xBE, 0x1C, 0xB0, 0x7B, 0x75, 0x2C, 0xA1, 0xF1, 0x34, 0x48, 0x89, 0xD6, + 0xAD, 0xB4, 0x0E, 0x99, 0x5D, 0xC7, 0x19, 0xE7, 0x1B, 0xAD, 0x58, 0x0C, 0x21, 0x17, 0x6F, 0xA6, 0x3B, 0x58, 0x58, 0xA2, 0x71, 0xD3, 0xD5, 0x89, + 0x18, 0xEF, 0xC2, 0x79, 0x33, 0x38, 0x51, 0xB2, 0xE2, 0x5D, 0x4C, 0xE1, 0x69, 0x72, 0x39, 0x58, 0xF2, 0xD1, 0xA4, 0x24, 0xD7, 0x03, 0xEE, 0x0B, + 0x47, 0xA0, 0x1A, 0x7B, 0x47, 0xE9, 0xA3, 0xE9, 0xAA, 0xE4, 0x6F, 0x10, 0x35, 0xB7, 0x0D, 0x30, 0x42, 0xEC, 0x9F, 0x60, 0xD0, 0x87, 0x3C, 0x3A, + 0x31, 0x69, 0x3A, 0x0C, 0xEB, 0x6A, 0xBE, 0x12, 0x03, 0xA0, 0x6B, 0x31, 0x03, 0xD8, 0xE5, 0x41, 0xAC, 0x76, 0xA9, 0x4B, 0x3F, 0xAF, 0x57, 0xF4, + 0xEB, 0x2D, 0x4E, 0x4E, 0x7C, 0x33, 0x03, 0xED, 0xE3, 0x35, 0x0A, 0x59, 0xB9, 0x07, 0x79, 0x42, 0xE8, 0x59, 0xF9, 0x39, 0x58, 0x9A, 0x71, 0xF4, + 0xC5, 0x82, 0x48, 0x3B, 0xA4, 0x1C, 0xC0, 0x38, 0x2B, 0x0D, 0xDA, 0xD1, 0x65, 0xC5, 0x88, 0x34, 0x22, 0x17, 0x0E, 0xBB, 0x4F, 0x4B, 0x8C, 0x51, + 0x4B, 0x5E, 0x69, 0xE9, 0x40, 0x97, 0x32, 0x39, 0x45, 0xB0, 0x5D, 0xA4, 0x04, 0x98, 0x19, 0x2D, 0xF6, 0xE9, 0xE1, 0x80, 0xBB, 0xB8, 0x96, 0xA0, + 0x26, 0x38, 0xE6, 0xF1, 0x91, 0xAF, 0x81, 0x0E, 0x1B, 0xB3, 0xE5, 0x82, 0xA3, 0x77, 0x1B, 0xC7, 0x78, 0xF9, 0xC6, 0x4F, 0x9C, 0xB9, 0x1A, 0xD7, + 0xF8, 0x17, 0x13, 0x5C, 0x75, 0x73, 0xD7, 0xB8, 0x48, 0x5E, 0x60, 0x31, 0x63, 0xCD, 0xD2, 0xD3, 0xCC, 0xE9, 0x36, 0x94, 0x5F, 0x84, 0x1D, 0xD2, + 0xC1, 0xC4, 0x6D, 0xD4, 0x7C, 0x1F, 0x44, 0x0B, 0x89, 0x34, 0x64, 0xC2, 0xEB, 0xF3, 0xF8, 0x99, 0x3C, 0x26, 0x83, 0xF0, 0x81, 0x3A, 0x80, 0x21, + 0xDE, 0x72, 0x2B, 0xFC, 0xBC, 0xC2, 0xC3, 0x85, 0x02, 0xC7, 0x5B, 0x3C, 0x6C, 0x1D, 0x82, 0x08, 0x9D, 0x02, 0x76, 0xCB, 0xB5, 0x06, 0x16, 0x14, + 0x15, 0x14, 0xD3, 0x40, 0x11, 0xC3, 0x29, 0x92, 0x50, 0xA2, 0x65, 0x9B, 0x56, 0xBD, 0x8A, 0x3D, 0x09, 0xAF, 0x2F, 0x1B, 0x04, 0xC0, 0x50, 0x03, + 0x50, 0x6E, 0xBA, 0xD6, 0xBC, 0x1D, 0xC8, 0x40, 0x95, 0x55, 0xFC, 0x62, 0x7C, 0x5E, 0x9B, 0xD1, 0x85, 0x29, 0x15, 0x87, 0xC7, 0x99, 0x56, 0x62, + 0x14, 0xB6, 0xAD, 0x83, 0x71, 0x66, 0x0F, 0x61, 0x6B, 0xB4, 0xF7, 0xD8, 0x12, 0x02, 0xAF, 0xA2, 0x96, 0x66, 0x33, 0x36, 0xED, 0xDE, 0xBC, 0x49, + 0x0D, 0x4B, 0xB7, 0x77, 0x97, 0xF0, 0xCB, 0xB2, 0x73, 0x1D, 0x10, 0x8C, 0xEB, 0xFA, 0x88, 0xB8, 0xD1, 0xB2, 0xF9, 0x78, 0x36, 0x72, 0x1B, 0x97, + 0x50, 0x43, 0x21, 0xDC, 0xCC, 0x9A, 0xF7, 0x1A, 0xC8, 0x31, 0xA5, 0x1D, 0x50, 0x98, 0x15, 0x1D, 0xA2, 0x6D, 0x8A, 0x77, 0x14, 0xA1, 0x65, 0x57, + 0x4A, 0x01, 0xA5, 0x3D, 0x05, 0x60, 0x76, 0x2A, 0x2C, 0xF4, 0x8C, 0x02, 0xEC, 0x9A, 0xA0, 0x8E, 0xE6, 0xEE, 0x0B, 0x0D, 0x26, 0x0D, 0xEC, 0xD3, + 0x1C, 0x4B, 0x96, 0x01, 0x70, 0x89, 0x9F, 0x3D, 0xFB, 0xCC, 0x81, 0x15, 0x1D, 0x18, 0x48, 0x46, 0x55, 0xE3, 0xDF, 0x67, 0x26, 0x71, 0x77, 0xF0, + 0x1A, 0xAB, 0x18, 0x4D, 0x61, 0x3E, 0x34, 0x1A, 0x2D, 0x0B, 0x78, 0x1D, 0x67, 0x3C, 0x19, 0x53, 0x21, 0x98, 0x76, 0x02, 0x6F, 0x24, 0x86, 0x1F, + 0xA4, 0x64, 0xC7, 0xCA, 0xD3, 0x7B, 0x47, 0xD2, 0x60, 0x56, 0x40, 0xCD, 0x6D, 0x9C, 0x1A, 0xC4, 0xF5, 0xD6, 0x9A, 0xE5, 0xB5, 0xAE, 0x2A, 0x6D, + 0xA5, 0xC0, 0xA4, 0xF7, 0xCD, 0x63, 0xF8, 0x86, 0x39, 0xC4, 0xAC, 0x0E, 0x24, 0x99, 0x13, 0x30, 0x1B, 0x38, 0xBF, 0x26, 0x57, 0xD7, 0x87, 0xE2, + 0x93, 0x2B, 0xC4, 0x92, 0x81, 0x32, 0xB4, 0x34, 0xFF, 0x75, 0x40, 0x33, 0x1E, 0xF5, 0xAA, 0xAB, 0xC3, 0xC8, 0x52, 0xBB, 0xCA, 0xC6, 0x1C, 0xB0, + 0x43, 0x7A, 0xC1, 0x18, 0xCF, 0x5C, 0xA1, 0xCD, 0x19, 0xDE, 0xB8, 0xD2, 0x3B, 0x9B, 0x47, 0xA4, 0xB5, 0x1A, 0x64, 0xAA, 0x4A, 0xA3, 0x9D, 0x93, + 0xED, 0x7D, 0x82, 0x5B, 0x2D, 0x45, 0x24, 0x41, 0x1E, 0x52, 0x94, 0xBA, 0x85, 0xBA, 0x5C, 0x5B, 0x38, 0x31, 0xA0, 0xC5, 0xA0, 0x7F, 0x43, 0x96, + 0x6D, 0x63, 0x47, 0x25, 0x14, 0x58, 0x41, 0x66, 0x95, 0x00, 0xA8, 0xB1, 0x15, 0xEC, 0xFE, 0x22, 0x6A, 0x2E, 0xEB, 0xF1, 0x31, 0x25, 0xA0, 0x32, + 0xC3, 0x81, 0x43, 0x2B, 0xF5, 0xBE, 0xD2, 0x7F, 0xB1, 0xE3, 0x63, 0x34, 0xC9, 0xEA, 0x52, 0xCD, 0x5D, 0xFF, 0x7A, 0x12, 0xD4, 0xE8, 0xE0, 0x95, + 0xE7, 0x31, 0x50, 0x06, 0x72, 0xBB, 0x59, 0xC8, 0x75, 0xF8, 0xF1, 0x1A, 0x20, 0x30, 0xBC, 0xF8, 0x7A, 0xAD, 0x81, 0xF5, 0xC4, 0x28, 0x8F, 0x1A, + 0x96, 0xDA, 0xE1, 0x35, 0xA3, 0x98, 0xB6, 0x6C, 0x4F, 0x2F, 0x58, 0xC1, 0x6D, 0xD0, 0x24, 0x24, 0xE8, 0xAD, 0x15, 0x22, 0xD6, 0xFC, 0x7C, 0x5A, + 0x2C, 0x84, 0x37, 0x11, 0x6C, 0x18, 0x28, 0xBD, 0xCD, 0xC6, 0xAF, 0xD6, 0xB8, 0x3D, 0x21, 0x41, 0xE6, 0x39, 0xC6, 0xE9, 0x6E, 0x90, 0x73, 0x23, + 0x0C, 0x68, 0x99, 0x30, 0x0D, 0x12, 0xA7, 0xDC, 0x17, 0xEF, 0xDA, 0x59, 0xDD, 0xCB, 0x50, 0x20, 0xCB, 0xFC, 0xAC, 0xD4, 0x6A, 0x04, 0x28, 0xD4, + 0xB1, 0xC3, 0x88, 0x9F, 0x13, 0xA3, 0xE9, 0x2E, 0x51, 0xAD, 0x65, 0xC7, 0x97, 0x25, 0x86, 0xE1, 0xB0, 0x6D, 0x07, 0xBD, 0x85, 0x4B, 0x8C, 0xFD, + 0xBD, 0xC2, 0xE9, 0xDC, 0xA9, 0xE3, 0x35, 0x0F, 0xC8, 0xA4, 0xE3, 0xEB, 0xC2, 0x4D, 0x5D, 0xF9, 0x8F, 0x49, 0x4B, 0x70, 0x9A, 0x6F, 0xE8, 0x8E, + 0xAB, 0x47, 0xC7, 0xE7, 0x21, 0xD9, 0x4E, 0x8D, 0xEC, 0xA1, 0x34, 0xE3, 0xCA, 0x8E, 0xC8, 0x64, 0x0A, 0xE0, 0xF6, 0x3D, 0xE9, 0x77, 0xF0, 0xD8, + 0x22, 0xE9, 0xBA, 0xE9, 0xAC, 0x23, 0x71, 0xC4, 0xB3, 0x85, 0xEB, 0xC2, 0xAE, 0xF8, 0xD0, 0x65, 0x9F, 0xCE, 0xEB, 0xAD, 0x83, 0xEE, 0x57, 0x1B, + 0x75, 0x9C, 0x0B, 0xF9, 0x68, 0xAC, 0x38, 0xAF, 0x82, 0xBE, 0x64, 0x7A, 0x71, 0x6B, 0xAE, 0x8E, 0x1C, 0x5A, 0x56, 0xC6, 0x05, 0x72, 0x45, 0x3B, + 0xA1, 0x32, 0xDB, 0x19, 0x35, 0xD2, 0x0C, 0x56, 0xB1, 0x6A, 0xD8, 0x5E, 0xE7, 0xFC, 0x25, 0x5A, 0xEF, 0x8A, 0xC6, 0x21, 0x45, 0x0B, 0x72, 0x64, + 0x96, 0x88, 0xF8, 0x52, 0xCC, 0x03, 0x3B, 0xDD, 0x87, 0x26, 0xBB, 0x2B, 0x73, 0x79, 0xAE, 0x2E, 0x09, 0xD6, 0x4B, 0x83, 0xF6, 0x84, 0x27, 0x7C, + 0x62, 0xF8, 0x7E, 0x50, 0x46, 0x9C, 0x06, 0xDB, 0x23, 0xC7, 0x4F, 0x76, 0xC3, 0x31, 0x7D, 0x3E, 0x7A, 0xBF, 0x47, 0x5A, 0xC6, 0x51, 0x23, 0xC2, + 0x46, 0x2B, 0x9A, 0x63, 0xF5, 0x34, 0xDE, 0x54, 0xE2, 0x33, 0xBA, 0xF0, 0xDA, 0x69, 0xF7, 0xC7, 0x08, 0x75, 0x02, 0x05, 0xAA, 0x81, 0x1D, 0x3D, + 0x40, 0xF1, 0x28, 0xF7, 0x3C, 0xA7, 0x5A, 0x29, 0x34, 0x8E, 0xE4, 0x63, 0x6A, 0xA3, 0xC7, 0x0C, 0xCD, 0xE6, 0x56, 0x0A, 0x86, 0xCB, 0xB1, 0xC2, + 0xEA, 0x91, 0x59, 0xC0, 0x6F, 0xAD, 0x34, 0x96, 0x81, 0x10, 0x07, 0x46, 0x0B, 0x6C, 0x82, 0x77, 0xD9, 0xB5, 0x0F, 0x08, 0x31, 0x21, 0xCF, 0x83, + 0x14, 0x20, 0xED, 0xB1, 0x96, 0x98, 0x72, 0xA1, 0x43, 0x08, 0x76, 0x4C, 0xEB, 0x7A, 0xC1, 0x75, 0x98, 0xA0, 0xF7, 0x15, 0xCF, 0x97, 0x1E, 0x7C, + 0x3B, 0x8F, 0x0A, 0x29, 0xA7, 0x4E, 0xB0, 0x07, 0x00, 0xF7, 0x12, 0xC0, 0x3A, 0x02, 0x9E, 0x33, 0x61, 0x7D, 0x39, 0x63, 0x59, 0x79, 0xD7, 0x15, + 0x21, 0xBB, 0x28, 0xBA, 0xA5, 0x03, 0x43, 0xCE, 0x2E, 0x3A, 0xEB, 0x64, 0x72, 0x47, 0xDA, 0x91, 0x55, 0x59, 0x0E, 0x66, 0xC7, 0x3A, 0xEA, 0x09, + 0x16, 0x23, 0xE7, 0x8A, 0x08, 0xE8, 0xEC, 0x91, 0x84, 0xAD, 0x44, 0x44, 0x60, 0xFF, 0x80, 0x0F, 0x2C, 0x1E, 0xDA, 0x1D, 0xCD, 0x28, 0x99, 0x94, + 0x09, 0x7B, 0xD3, 0x2A, 0x1A, 0xEA, 0x55, 0x23, 0xF4, 0xDD, 0x62, 0x37, 0x7E, 0x55, 0xA3, 0xCF, 0xA8, 0x8B, 0xF7, 0x09, 0x41, 0x34, 0x90, 0xA0, + 0xAE, 0x39, 0xEE, 0x32, 0x62, 0x34, 0xBD, 0x37, 0xFE, 0xA2, 0x23, 0x99, 0x1D, 0x65, 0x93, 0x29, 0x70, 0xB2, 0x0A, 0x78, 0xC4, 0x35, 0x42, 0x2C, + 0xFE, 0x87, 0xD9, 0x86, 0xF9, 0x7D, 0x34, 0x66, 0xB0, 0x14, 0x74, 0x1D, 0x82, 0x67, 0xE8, 0xB6, 0x9D, 0xD3, 0xB4, 0xD5, 0xC7, 0xA6, 0x19, 0xAD, + 0xE0, 0xAB, 0xC2, 0x8D, 0xC3, 0x12, 0x73, 0xBC, 0x10, 0x6A, 0xD1, 0x20, 0xB2, 0xF6, 0xA3, 0xB5, 0x46, 0x8E, 0x11, 0x8A, 0x21, 0xB7, 0x4C, 0xD9, + 0x5A, 0x23, 0xCE, 0xE7, 0xE1, 0xD6, 0x23, 0x43, 0x96, 0x25, 0x7B, 0xAC, 0xCD, 0xC3, 0x4B, 0x33, 0x8C, 0x25, 0x04, 0xD5, 0x3E, 0x30, 0x54, 0x06, + 0x28, 0x08, 0xE5, 0x21, 0x9B, 0xDE, 0x38, 0x57, 0xD3, 0x1E, 0x41, 0x31, 0xB0, 0xFB, 0x3B, 0x86, 0x61, 0xFF, 0x09, 0x89, 0x43, 0x6A, 0xA4, 0x2D, + 0x32, 0xC8, 0x6A, 0x8E, 0x7E, 0x9C, 0xEA, 0x5E, 0x1C, 0x4B, 0x75, 0x83, 0x04, 0x23, 0xD8, 0x93, 0x19, 0xAC, 0x02, 0x59, 0x8D, 0x36, 0x8F, 0x39, + 0xE1, 0x38, 0x06, 0x51, 0x34, 0x3A, 0xF9, 0xED, 0x67, 0xE6, 0x86, 0x31, 0xED, 0x32, 0xFE, 0x44, 0xD5, 0xC7, 0x02, 0x24, 0x34, 0x98, 0x8B, 0x85, + 0x63, 0xA5, 0xE6, 0xE6, 0x4F, 0x15, 0x7A, 0x95, 0xE1, 0x97, 0x2A, 0xFD, 0xAD, 0x39, 0xB2, 0x8F, 0x63, 0x4C, 0xAC, 0x74, 0xDC, 0x76, 0x11, 0xAD, + 0xF1, 0xB3, 0x91, 0xD0, 0xB8, 0xB2, 0xC8, 0x09, 0xB6, 0x2C, 0x37, 0x0D, 0x5A, 0x55, 0x88, 0x20, 0x23, 0x4E, 0x4C, 0x65, 0x76, 0xE3, 0xC7, 0x0B, + 0x6A, 0xEF, 0x34, 0x51, 0x24, 0xFB, 0xF1, 0x37, 0xD4, 0x46, 0x7B, 0x43, 0xC2, 0x45, 0x4A, 0xE7, 0xD8, 0xBD, 0x2E, 0x93, 0x6D, 0x22, 0xA3, 0x9A, + 0xB7, 0x71, 0xE7, 0x14, 0xD9, 0x85, 0xF1, 0x11, 0x55, 0x27, 0xAE, 0x4C, 0xAE, 0xC9, 0xC6, 0x44, 0x0D, 0xF0, 0x87, 0x8F, 0xC8, 0x36, 0xDB, 0xC6, + 0xF0, 0xCF, 0x2C, 0x06, 0xF1, 0x23, 0x04, 0x03, 0xF2, 0xB2, 0x1F, 0xAE, 0x73, 0xFB, 0x85, 0xFF, 0x01, 0x4C, 0x1B, 0x57, 0xCF, 0x47, 0xA7, 0x8D, + 0xD0, 0xCD, 0x1D, 0xC4, 0xBD, 0xFA, 0x94, 0xEA, 0x97, 0x76, 0x33, 0xBC, 0x54, 0xB7, 0x8D, 0x4B, 0xE4, 0xF7, 0xCE, 0xB4, 0x26, 0x58, 0x87, 0x03, + 0x0B, 0xF2, 0x3E, 0xD3, 0x44, 0x51, 0x37, 0xB0, 0x05, 0x70, 0xCB, 0x84, 0x49, 0xBC, 0x6C, 0xF3, 0x59, 0x3B, 0xE0, 0x82, 0xD5, 0xA5, 0x81, 0x9F, + 0x07, 0x43, 0x51, 0xC4, 0x32, 0x9E, 0xE7, 0xA3, 0x35, 0x16, 0x64, 0x3A, 0x29, 0x4B, 0x45, 0x68, 0x56, 0x87, 0xDB, 0xCE, 0xCD, 0x0B, 0xE0, 0x10, + 0x02, 0x2E, 0x5A, 0x31, 0xC8, 0xD5, 0xD2, 0xDF, 0x82, 0x8F, 0x75, 0x9C, 0x0F, 0x09, 0xED, 0xCB, 0x4D, 0x5A, 0x31, 0x0E, 0x9D, 0xD0, 0x6E, 0x2E, + 0x92, 0xB5, 0x38, 0x31, 0x96, 0xD5, 0x41, 0xBE, 0x91, 0xEB, 0xA9, 0x2A, 0x43, 0xBF, 0xB9, 0x20, 0xFF, 0x3F, 0xC1, 0xE0, 0x70, 0x30, 0xD7, 0x36, + 0x72, 0xDB, 0x77, 0x6C, 0x5B, 0x7E, 0xED, 0x48, 0xFA, 0x32, 0xB6, 0xA9, 0x74, 0x0E, 0x2F, 0x96, 0x3C, 0x65, 0xA1, 0xCC, 0xCB, 0x7B, 0x02, 0x30, + 0xDA, 0xAF, 0xCE, 0x61, 0xC5, 0xB7, 0xD3, 0x0F, 0xBB, 0xFC, 0xA8, 0x1E, 0x26, 0x6F, 0xC0, 0x44, 0xC9, 0xAE, 0x2B, 0x6A, 0x5F, 0x11, 0x7F, 0x70, + 0x26, 0xF8, 0xD1, 0x15, 0x29, 0x3E, 0x8E, 0x56, 0x7B, 0x18, 0x38, 0x6D, 0x03, 0xC7, 0x9D, 0x59, 0xA2, 0x5B, 0xF3, 0x28, 0xF6, 0x09, 0xE3, 0x2D, + 0x15, 0xCE, 0x7D, 0x9B, 0xD7, 0xDC, 0x2C, 0x2E, 0x97, 0x16, 0xEA, 0x8B, 0x82, 0x0D, 0x32, 0x52, 0x13, 0x9D, 0x55, 0x36, 0x54, 0x98, 0xCC, 0x6C, + 0xBB, 0x14, 0x01, 0x12, 0x62, 0x39, 0xB9, 0xE6, 0x92, 0x33, 0x3E, 0x60, 0xF8, 0x1A, 0x82, 0xC9, 0xF6, 0x84, 0xF0, 0x9D, 0xA0, 0x4A, 0x3F, 0xC1, + 0x04, 0x0D, 0xA0, 0x79, 0x41, 0xC8, 0xF2, 0xD1, 0x48, 0xFE, 0x73, 0x75, 0xA9, 0x32, 0x3F, 0x8D, 0x15, 0x25, 0xDC, 0x31, 0xD8, 0x61, 0x66, 0xA0, + 0xF1, 0x01, 0x5A, 0xFE, 0xCA, 0x9C, 0x0C, 0xFB, 0xBB, 0x51, 0x2B, 0x74, 0x0A, 0xE3, 0x85, 0xF7, 0x05, 0x9A, 0x00, 0xF1, 0xE4, 0x7D, 0x9D, 0x38, + 0x3C, 0x38, 0x6A, 0x9B, 0xCC, 0x48, 0xBB, 0x30, 0x00, 0xC9, 0x62, 0x25, 0x16, 0x17, 0x19, 0xA4, 0x41, 0xD6, 0x4E, 0xC9, 0x71, 0xFF, 0x08, 0xC4, + 0x50, 0x8A, 0x9C, 0xD0, 0xEA, 0x8D, 0xDD, 0xDA, 0x38, 0x88, 0xCD, 0xA8, 0xDD, 0xAC, 0x00, 0x3A, 0xA9, 0xE4, 0x7D, 0x1A, 0x5F, 0x0B, 0xEB, 0xAF, + 0xBB, 0x16, 0x22, 0x90, 0x68, 0xDB, 0x1E, 0xF2, 0x1D, 0xA8, 0xE9, 0x18, 0x30, 0x03, 0x90, 0x2B, 0x48, 0xD6, 0xF3, 0xF0, 0xB3, 0x7F, 0x9A, 0x75, + 0x2A, 0xA8, 0x40, 0x95, 0x0C, 0x4D, 0x3E, 0x43, 0xB6, 0xEA, 0x2F, 0x52, 0xBD, 0x87, 0x95, 0xE3, 0x8A, 0xD5, 0xC0, 0xF9, 0x44, 0xE9, 0x97, 0xA2, + 0x3A, 0x0B, 0x69, 0x4D, 0xDC, 0xAC, 0xA1, 0x5D, 0x1F, 0x8F, 0xED, 0x8B, 0x9B, 0x71, 0x1F, 0x0A, 0x90, 0x0C, 0xA0, 0x3E, 0x63, 0x57, 0x32, 0xD8, + 0x2D, 0x36, 0x1E, 0xEA, 0x7E, 0x36, 0xE2, 0x4A, 0xDB, 0x31, 0x7F, 0xCA, 0xD2, 0xC3, 0x90, 0xDA, 0xFB, 0x85, 0x42, 0x6D, 0x94, 0x7A, 0x19, 0x12, + 0x43, 0x26, 0xB3, 0xE2, 0x26, 0x12, 0x88, 0xAB, 0xFE, 0xC3, 0xCE, 0x3A, 0x40, 0x4F, 0xC6, 0xC1, 0xDA, 0x16, 0x41, 0xDC, 0x24, 0xEA, 0x23, 0x11, + 0xB9, 0x6C, 0x58, 0x3E, 0xCA, 0x6D, 0x64, 0xE0, 0x3C, 0x17, 0x3B, 0xF2, 0x23, 0xD1, 0x5F, 0x1F, 0xAE, 0xAD, 0x6A, 0x60, 0x70, 0x4F, 0x49, 0x65, + 0xD1, 0x92, 0x8E, 0x01, 0x27, 0x63, 0xE4, 0xA0, 0xE5, 0xB9, 0xE6, 0xD9, 0x80, 0x59, 0xB9, 0x5F, 0xEC, 0x96, 0x0F, 0x67, 0x7F, 0xC5, 0xD9, 0x5D, + 0xE1, 0x81, 0xC1, 0x71, 0x18, 0x69, 0x6B, 0x44, 0x35, 0x96, 0x2D, 0x5D, 0xCE, 0x7F, 0x09, 0xFC, 0x65, 0x66, 0x28, 0x7E, 0xE9, 0x8D, 0x14, 0xCE, + 0x2C, 0xE8, 0xEE, 0x90, 0xE1, 0x59, 0x39, 0xBB, 0x6B, 0x1F, 0x45, 0x8A, 0xA8, 0x85, 0x73, 0x10, 0xF1, 0x4D, 0xE8, 0x66, 0x60, 0x86, 0x43, 0x77, + 0x0F, 0x9E, 0x9D, 0x26, 0xB9, 0xD0, 0x56, 0x17, 0x89, 0x53, 0x54, 0xC5, 0xF2, 0x26, 0x8E, 0x6E, 0x86, 0x35, 0xBB, 0x02, 0x0C, 0x56, 0x9F, 0x9D, + 0xEB, 0xC0, 0xD9, 0xFD, 0x19, 0x07, 0x98, 0xE3, 0xE8, 0x03, 0xEC, 0x87, 0xB2, 0x54, 0xC8, 0x63, 0x0A, 0x70, 0x46, 0x40, 0x3C, 0xA9, 0x6B, 0xE9, + 0x61, 0x53, 0xA9, 0x23, 0x31, 0xDC, 0xF4, 0x6B, 0x75, 0x88, 0x7C, 0xD5, 0x78, 0x5D, 0x5F, 0xCE, 0x4E, 0x86, 0x65, 0xEE, 0x95, 0xD9, 0xC9, 0xD9, + 0xC6, 0x6E, 0xC2, 0xE1, 0x8C, 0x8E, 0x23, 0x9E, 0xFA, 0x76, 0xED, 0xC9, 0xCC, 0x82, 0xB9, 0x23, 0x64, 0x94, 0xA3, 0x9A, 0x77, 0xC8, 0x29, 0xDD, + 0xC1, 0xB0, 0xCA, 0x47, 0xBF, 0xA5, 0xDC, 0xFF, 0xCF, 0x64, 0xDF, 0xDE, 0x6C, 0x37, 0xBE, 0x27, 0x5F, 0xE5, 0x23, 0xE2, 0xBF, 0xBC, 0xBB, 0xF9, + 0x70, 0x58, 0xAF, 0x06, 0x7A, 0x48, 0x9A, 0x2F, 0xBD, 0xAC, 0xD2, 0x33, 0xDC, 0x08, 0x84, 0xF7, 0xA0, 0x75, 0x83, 0x2F, 0x82, 0xD0, 0xDF, 0x4A, + 0xBF, 0x8E, 0xD3, 0x3A, 0x9C, 0x01, 0xC8, 0x08, 0xF7, 0x08, 0xF7, 0x79, 0x6E, 0x07, 0xB9, 0x61, 0x62, 0xE6, 0x0B, 0xEF, 0xAC, 0xE3, 0x2C, 0x73, + 0x0E, 0x5F, 0x9B, 0x85, 0xC4, 0x21, 0x94, 0x41, 0x2C, 0x2E, 0xDB, 0xAC, 0x1C, 0x6F, 0x34, 0xA2, 0x78, 0x8A, 0xC6, 0xB5, 0x9E, 0xEF, 0xF0, 0xD2, + 0xC4, 0x22, 0x85, 0x4D, 0x13, 0xB3, 0x0F, 0x8E, 0x11, 0x74, 0x0C, 0x11, 0xBE, 0x9D, 0x6A, 0xA3, 0x89, 0x75, 0x04, 0xD3, 0x38, 0xF5, 0x99, 0x93, + 0xDF, 0x37, 0xF8, 0xE6, 0x32, 0x4E, 0xD8, 0x56, 0x67, 0x00, 0xFE, 0x52, 0x26, 0x53, 0x16, 0xA2, 0xBE, 0x4D, 0xD4, 0x9A, 0x32, 0x9C, 0x51, 0x2E, + 0x72, 0x9D, 0xEE, 0xDA, 0xFB, 0x9B, 0x71, 0xE8, 0xE5, 0xA8, 0x61, 0x71, 0x1B, 0x7C, 0xFF, 0xA2, 0xDE, 0xAF, 0x2E, 0x33, 0xF9, 0xBA, 0xE9, 0x9E, + 0xBC, 0xA4, 0xBF, 0x57, 0xC2, 0x41, 0x2D, 0x47, 0x49, 0x2B, 0xE2, 0xED, 0x1A, 0x2C, 0xF7, 0xBE, 0xD7, 0x4B, 0x92, 0x17, 0x53, 0xAD, 0x8B, 0xF3, + 0x39, 0xCA, 0x1C, 0x24, 0xD9, 0x83, 0xE2, 0x48, 0x06, 0x34, 0x3C, 0x8D, 0xDB, 0xC0, 0x23, 0x04, 0x50, 0xD3, 0x42, 0xD6, 0xE7, 0x60, 0x8C, 0x58, + 0x4A, 0x46, 0x7A, 0xD1, 0x7B, 0xB6, 0xE4, 0x54, 0xF7, 0x09, 0x3B, 0x8E, 0xD9, 0x75, 0x24, 0x8A, 0x27, 0xB8, 0xD3, 0xBC, 0x54, 0xCF, 0x14, 0x8F, + 0x77, 0x17, 0xD0, 0xEA, 0x0F, 0x95, 0xE8, 0xDB, 0xB7, 0xC1, 0x90, 0x76, 0xA0, 0x62, 0x1D, 0xFD, 0x92, 0x5E, 0xD4, 0xC8, 0x18, 0x08, 0x89, 0x77, + 0xCF, 0x98, 0xE7, 0x5B, 0xB5, 0x47, 0x05, 0xF5, 0x09, 0x87, 0x96, 0x7D, 0x13, 0xF4, 0x32, 0xB5, 0x65, 0xD0, 0xE8, 0xCE, 0xA5, 0xF5, 0x09, 0x7C, + 0xAF, 0x8C, 0xB2, 0x0B, 0x4C, 0x1C, 0xDC, 0x99, 0xB9, 0x8E, 0x45, 0x80, 0x92, 0xFF, 0x0D, 0xC0, 0x24, 0xFA, 0x55, 0x8A, 0xF1, 0xD0, 0xDC, 0xF8, + 0xFD, 0x04, 0xF6, 0x38, 0x80, 0xF5, 0x15, 0xEC, 0x67, 0xA4, 0xDE, 0xC0, 0x50, 0xFA, 0x2D, 0xDA, 0xB7, 0x62, 0xE7, 0xD1, 0xF9, 0x22, 0x49, 0xA8, + 0xA3, 0xCF, 0x6C, 0x43, 0x1C, 0x84, 0xF8, 0xAA, 0x81, 0xD4, 0x05, 0x4C, 0x75, 0x90, 0xB9, 0x04, 0x33, 0x36, 0x49, 0xCB, 0x8F, 0x99, 0x7C, 0x45, + 0xD2, 0x58, 0x27, 0x4D, 0x4C, 0xEB, 0xCF, 0xC2, 0xF7, 0x2F, 0xF1, 0x27, 0x6D, 0x59, 0xF4, 0xA9, 0xCD, 0xDD, 0xAA, 0xB9, 0xFB, 0x85, 0xE5, 0x23, + 0x2D, 0xB5, 0x6D, 0xF8, 0xED, 0xCB, 0x40, 0xFD, 0x55, 0x15, 0xAC, 0x1C, 0xF6, 0xF7, 0x14, 0x0D, 0xF8, 0x07, 0xC9, 0xE5, 0x6C, 0xFB, 0xEC, 0x35, + 0x99, 0x91, 0x46, 0xB8, 0x69, 0x7B, 0x97, 0xF4, 0x61, 0xD4, 0x4B, 0x63, 0xD9, 0x0D, 0xA6, 0x0E, 0x79, 0xED, 0x51, 0xB5, 0x0E, 0xC6, 0x64, 0x98, + 0xC1, 0xD7, 0x28, 0x80, 0x15, 0xE5, 0xE0, 0xDE, 0xF8, 0x89, 0xB2, 0x6A, 0x23, 0xF3, 0xD1, 0xF2, 0x19, 0x20, 0x7C, 0x3D, 0xF0, 0xA4, 0xA1, 0xA3, + 0x07, 0x62, 0xCD, 0x87, 0x60, 0x70, 0x2A, 0xEB, 0x32, 0xEB, 0x12, 0x1D, 0x55, 0xAF, 0x57, 0x67, 0xAD, 0x3D, 0xF7, 0x38, 0xFE, 0x25, 0x73, 0x3C, + 0xC7, 0x2A, 0x36, 0xFF, 0xA5, 0xAF, 0x61, 0x74, 0x72, 0xBA, 0xD8, 0x86, 0x97, 0x41, 0xE8, 0x34, 0x53, 0x24, 0x21, 0x11, 0x14, 0x5F, 0xDB, 0x98, + 0x1B, 0x83, 0x18, 0x6B, 0x4A, 0xE2, 0x79, 0x1C, 0x02, 0x6B, 0xAD, 0x59, 0xFB, 0x5C, 0x88, 0x3B, 0x16, 0x90, 0xC5, 0x0E, 0x35, 0xCC, 0xE6, 0xFB, + 0xDE, 0xA2, 0x76, 0x8C, 0x97, 0xD3, 0xB2, 0x7D, 0x8C, 0x42, 0xCA, 0xE1, 0x59, 0x84, 0x84, 0xB2, 0x95, 0x17, 0xD9, 0xB3, 0x91, 0x0C, 0x36, 0xC7, + 0x34, 0x33, 0xEE, 0x64, 0xB0, 0x43, 0xCF, 0x4C, 0x5A, 0x69, 0xBE, 0x04, 0x38, 0xD8, 0x62, 0xB2, 0x8E, 0x1A, 0xCE, 0x3C, 0x2F, 0xF7, 0xF4, 0x71, + 0x99, 0x9D, 0x44, 0xD5, 0xEB, 0x25, 0x1A, 0x34, 0x2F, 0x50, 0x86, 0xF1, 0x3F, 0x9B, 0xF3, 0x4C, 0x97, 0x69, 0x97, 0x2B, 0x18, 0x6E, 0x0C, 0x04, + 0x5B, 0x89, 0xC4, 0xFE, 0x6E, 0x6D, 0xDA, 0xB6, 0xCC, 0xEE, 0xD4, 0x38, 0x26, 0xDA, 0x63, 0xCA, 0x99, 0x86, 0x0A, 0xC7, 0xA5, 0xFF, 0x07, 0xA1, + 0x36, 0x77, 0x28, 0x1A, 0xD7, 0x16, 0xF1, 0xF1, 0x93, 0x0B, 0x77, 0x92, 0xB5, 0xA6, 0x80, 0x19, 0xB0, 0xEA, 0xCB, 0x4B, 0x20, 0x0D, 0x05, 0xB3, + 0x2E, 0x40, 0xC5, 0xF5, 0xBB, 0xF5, 0x2D, 0x88, 0xC7, 0x67, 0xFE, 0xBF, 0xD7, 0xFC, 0x17, 0x6E, 0x7F, 0xA8, 0x87, 0x42, 0x07, 0x32, 0x08, 0xC1, + 0xFE, 0xD3, 0xF4, 0x0B, 0xD0, 0x60, 0xD3, 0xB3, 0x87, 0x12, 0x34, 0xC1, 0xC0, 0x71, 0x25, 0x03, 0x99, 0xC6, 0x2A, 0x89, 0x64, 0x61, 0x8C, 0x6A, + 0xD8, 0x02, 0xCF, 0x0B, 0x77, 0x8F, 0x9B, 0x6C, 0xE0, 0xB9, 0x2E, 0xB3, 0x73, 0x2F, 0x5C, 0xDA, 0x55, 0xFD, 0xFE, 0xCB, 0x9D, 0x7E, 0x15, 0x0C, + 0x4F, 0xC5, 0xAC, 0x04, 0x35, 0xAE, 0x61, 0x33, 0xCC, 0x8F, 0x93, 0x22, 0xEE, 0x82, 0x8D, 0x70, 0x4B, 0xDD, 0x81, 0x43, 0x67, 0x58, 0xDD, 0x4C, + 0x6B, 0x6A, 0xCD, 0x6E, 0x2E, 0x12, 0x17, 0x28, 0xC3, 0xE9, 0xA3, 0x27, 0x63, 0x55, 0x3A, 0x39, 0x8E, 0xEB, 0x6B, 0x8D, 0xED, 0x77, 0x59, 0xE9, + 0xD8, 0x77, 0x0C, 0xFE, 0x16, 0xC8, 0x6D, 0xD6, 0x35, 0x44, 0x1F, 0xA7, 0xC1, 0x3E, 0x79, 0x4C, 0xCA, 0x2C, 0x4F, 0x0F, 0x99, 0x2C, 0xF9, 0x9E, + 0x50, 0x9E, 0x85, 0x1D, 0x9A, 0xED, 0xE2, 0x7D, 0x2F, 0xF4, 0x55, 0xF8, 0x9C, 0x72, 0xAE, 0x9C, 0x54, 0x48, 0xA8, 0x85, 0x75, 0xA1, 0xA7, 0x02, + 0xA8, 0x8C, 0x89, 0x5F, 0x01, 0x64, 0x0F, 0x7D, 0x05, 0xCF, 0x1C, 0xF4, 0xF5, 0x8B, 0x13, 0xC9, 0x62, 0x8A, 0x54, 0x49, 0x7C, 0x04, 0x97, 0xB0, + 0xA7, 0xBA, 0x9F, 0x49, 0x35, 0x96, 0x0A, 0x9F, 0x33, 0x1D, 0x1D, 0xA9, 0x58, 0x96, 0x17, 0x1F, 0xE1, 0xF5, 0x1A, 0xC3, 0x88, 0x47, 0xEA, 0x11, + 0x6D, 0x3C, 0xD4, 0x56, 0xCB, 0x57, 0x7F, 0xDE, 0xD8, 0x57, 0x59, 0x22, 0x8C, 0x29, 0xB2, 0xC9, 0x4A, 0x6E, 0x33, 0x50, 0x75, 0x78, 0xE5, 0xC9, + 0xE9, 0xBD, 0xC8, 0xAD, 0x0E, 0x3E, 0x41, 0x0C, 0xFC, 0x45, 0xF3, 0x1A, 0x89, 0x4E, 0xA8, 0x30, 0x31, 0xED, 0xCF, 0x56, 0xAD, 0x63, 0xA8, 0x9D, + 0xFB, 0x1B, 0x5D, 0x35, 0x61, 0x48, 0x18, 0xF4, 0x70, 0x8F, 0x10, 0x8D, 0x67, 0x36, 0xAE, 0xAE, 0x8C, 0x0F, 0xF5, 0x74, 0x82, 0x97, 0x62, 0x80, + 0x54, 0x4D, 0xA8, 0xF2, 0x9C, 0x86, 0xE9, 0x7B, 0x71, 0x38, 0x6C, 0xA9, 0xF1, 0x78, 0x47, 0x1D, 0xB3, 0xF4, 0x20, 0x26, 0x39, 0x1B, 0xFA, 0x81, + 0x21, 0xB1, 0xEC, 0xD1, 0x91, 0x80, 0x07, 0x79, 0x4E, 0xB4, 0x60, 0x09, 0x1F, 0x6F, 0xF9, 0x7B, 0x6F, 0x76, 0x1C, 0x65, 0x19, 0xE8, 0x34, 0x1C, + 0x43, 0x7D, 0xD0, 0xB6, 0x53, 0x39, 0xC9, 0x6E, 0x34, 0xA4, 0x7F, 0x33, 0x0B, 0x27, 0xDF, 0x6B, 0x19, 0xC8, 0x54, 0x20, 0x3F, 0x78, 0x2B, 0xFC, + 0x9D, 0xEF, 0x1A, 0xC1, 0xEA, 0x53, 0xC5, 0xA2, 0x55, 0xE8, 0xF5, 0x7D, 0xB6, 0x12, 0x69, 0x79, 0xD8, 0xCB, 0xCE, 0xB1, 0x27, 0x41, 0x9F, 0x06, + 0x5A, 0x19, 0xA4, 0x56, 0xC6, 0x46, 0x7A, 0x2A, 0xBC, 0x85, 0xB6, 0xFE, 0xBA, 0xDE, 0x5B, 0x00, 0x92, 0xCB, 0x53, 0x3F, 0xBD, 0x76, 0x02, 0x7A, + 0xFF, 0x4E, 0xB1, 0x3E, 0x10, 0x18, 0x51, 0x27, 0x01, 0x47, 0x75, 0x38, 0x03, 0xB4, 0x74, 0x10, 0xB4, 0x7C, 0xED, 0xD7, 0x05, 0x5E, 0x31, 0xF6, + 0xB8, 0x5D, 0xAB, 0xDB, 0xB5, 0xBA, 0x5D, 0x13, 0xF1, 0xDC, 0x2A, 0x7B, 0xCA, 0x99, 0xF0, 0x88, 0x92, 0x8A, 0x70, 0xC0, 0x2B, 0x12, 0x6B, 0x17, + 0x18, 0xD6, 0x62, 0x0A, 0x28, 0xF0, 0x0E, 0x94, 0xB5, 0x3D, 0x49, 0xBE, 0xA9, 0xA0, 0xA3, 0xE5, 0xF7, 0x01, 0xD7, 0x0B, 0x2A, 0x0B, 0xE1, 0x0A, + 0x7C, 0x4A, 0x0C, 0x4A, 0x09, 0x7B, 0x2A, 0xD8, 0x8E, 0xA5, 0x9F, 0x50, 0xEA, 0x5E, 0xDC, 0xA6, 0x02, 0x11, 0x48, 0x0A, 0x1F, 0x7F, 0x01, 0x28, + 0x62, 0x51, 0x0C, 0x50, 0x43, 0xCF, 0x8C, 0x90, 0xBC, 0x35, 0x50, 0xBC, 0x02, 0xEE, 0x06, 0xD9, 0xB0, 0xAB, 0x04, 0x69, 0x67, 0x00, 0x4A, 0x1D, + 0x05, 0xBA, 0xAD, 0x33, 0x4E, 0xB2, 0x39, 0xB5, 0xEC, 0x89, 0x67, 0x73, 0x05, 0x25, 0x39, 0x42, 0x05, 0xD9, 0x49, 0x3A, 0xAE, 0x30, 0x00, 0x15, + 0xC1, 0xC0, 0x84, 0x6B, 0x0F, 0x1F, 0xD3, 0xD8, 0x8F, 0x26, 0x49, 0xD2, 0x11, 0x6D, 0x2E, 0xC1, 0x07, 0xED, 0x83, 0x56, 0x62, 0x95, 0x24, 0x92, + 0xE5, 0x33, 0x37, 0x25, 0x40, 0x86, 0x43, 0x80, 0xA6, 0x37, 0x45, 0x29, 0x13, 0xA0, 0x49, 0xA2, 0x49, 0x3C, 0x63, 0x0E, 0xF2, 0x83, 0xC5, 0x71, + 0xF8, 0xE8, 0x3F, 0xA4, 0x9D, 0x68, 0xAB, 0x31, 0xB0, 0xC0, 0x9F, 0xFE, 0x78, 0xC6, 0xF9, 0x47, 0x6F, 0x6E, 0x20, 0x00, 0x9E, 0x85, 0x1D, 0x03, + 0x36, 0x2C, 0xD4, 0x86, 0xE8, 0xAD, 0xBB, 0xED, 0x08, 0x7B, 0x88, 0xF6, 0xF0, 0x03, 0x74, 0x04, 0xBA, 0x8B, 0x96, 0x70, 0x75, 0x18, 0x00, 0xD0, + 0x00, 0x84, 0x58, 0x24, 0x64, 0x42, 0x17, 0x11, 0x75, 0x0B, 0x42, 0x44, 0x76, 0xF4, 0x14, 0x38, 0x32, 0x24, 0x81, 0x2A, 0x50, 0xC2, 0x70, 0x39, + 0x34, 0x45, 0xE4, 0x86, 0xE6, 0x10, 0x29, 0x64, 0x40, 0x14, 0xBA, 0x0B, 0x4D, 0xE7, 0x2A, 0xB0, 0xF0, 0x55, 0x92, 0xC5, 0x3C, 0x87, 0x00, 0x91, + 0x69, 0x84, 0xF9, 0xE0, 0x1F, 0x29, 0x30, 0x5E, 0x3C, 0x9B, 0xBC, 0x64, 0xAE, 0x47, 0xBE, 0xDC, 0x88, 0xA2, 0x17, 0xE4, 0xF7, 0x05, 0x28, 0x7B, + 0x84, 0xB6, 0x3C, 0x13, 0xA8, 0x1B, 0xA9, 0x39, 0xED, 0xA6, 0xFD, 0x00, 0xDA, 0x00, 0x3B, 0x46, 0xC9, 0x92, 0x39, 0x22, 0xCF, 0xEB, 0x8A, 0x1C, + 0x79, 0x45, 0xE1, 0x33, 0x11, 0xB3, 0x86, 0x89, 0xC4, 0x24, 0x72, 0x14, 0x9A, 0x8F, 0x68, 0x4E, 0x76, 0xA0, 0x16, 0x9C, 0x80, 0x7C, 0xCE, 0x11, + 0x20, 0x0D, 0x0D, 0x85, 0x75, 0x2F, 0x0C, 0x44, 0xD3, 0xD3, 0x89, 0x41, 0xE2, 0x85, 0xC8, 0x92, 0xE2, 0x64, 0xC5, 0xEE, 0x34, 0xD8, 0xF2, 0x64, + 0x63, 0x94, 0x43, 0x30, 0x3E, 0xE5, 0x46, 0xA7, 0x02, 0x4F, 0xEB, 0x9F, 0x04, 0x4F, 0xAC, 0x24, 0x59, 0x88, 0xD7, 0x93, 0x4E, 0x60, 0xDD, 0x85, + 0xED, 0x59, 0x48, 0x4E, 0x86, 0x64, 0x91, 0x45, 0xF4, 0xD4, 0x05, 0xA2, 0x5E, 0x2C, 0x64, 0x15, 0xBC, 0x41, 0xA4, 0x0A, 0x07, 0x68, 0xD3, 0xD2, + 0x21, 0x70, 0x80, 0xA4, 0xBE, 0xC9, 0xAE, 0x29, 0x82, 0xF5, 0x92, 0x8D, 0xDF, 0x30, 0xC8, 0x4B, 0xF2, 0xBE, 0x1E, 0x9D, 0xEA, 0x70, 0x60, 0x5A, + 0x72, 0xD0, 0x27, 0x81, 0x1C, 0xC8, 0x46, 0x39, 0x43, 0x02, 0x90, 0x1D, 0xB0, 0xB2, 0x08, 0x20, 0xEB, 0x4A, 0x64, 0x11, 0x35, 0x2D, 0xB4, 0x33, + 0x9A, 0x40, 0x54, 0x71, 0x2C, 0x80, 0x3D, 0x31, 0x2D, 0x35, 0x51, 0xC8, 0x52, 0x98, 0xBC, 0x7A, 0xAA, 0xA0, 0xAF, 0x90, 0x67, 0xD6, 0x40, 0x6E, + 0xF1, 0x00, 0xAB, 0xE5, 0x30, 0x19, 0xCB, 0xBE, 0x61, 0xE2, 0x01, 0xA5, 0xB5, 0xFB, 0x92, 0x06, 0xCA, 0x6D, 0x69, 0x2B, 0x31, 0x68, 0xC0, 0x3B, + 0x8B, 0x93, 0xC9, 0x98, 0x94, 0x4B, 0x60, 0x8C, 0x0E, 0x3D, 0xCF, 0xE6, 0x06, 0xB6, 0xA2, 0x2D, 0x68, 0x07, 0x42, 0xD0, 0xC5, 0x29, 0x49, 0xC6, + 0x52, 0x80, 0x2C, 0x41, 0x86, 0x33, 0x49, 0x3E, 0x20, 0x5B, 0x27, 0x88, 0x02, 0x71, 0xE0, 0x0D, 0x88, 0x03, 0x60, 0x77, 0x32, 0x02, 0x77, 0x75, + 0x1A, 0x00, 0x35, 0x82, 0x6A, 0x2D, 0x0D, 0x32, 0x26, 0x86, 0xFC, 0x7F, 0x7F, 0xE9, 0x4B, 0x0F, 0x9C, 0x38, 0x81, 0x1B, 0x63, 0xC5, 0x21, 0xF3, + 0xBD, 0x03, 0xF8, 0x3D, 0xB1, 0x10, 0x4A, 0xF4, 0xE7, 0x8D, 0x8C, 0x2B, 0xA2, 0xDA, 0x61, 0x0F, 0x4A, 0x37, 0x28, 0x39, 0xBD, 0xC3, 0x36, 0x60, + 0x13, 0xA0, 0x00, 0x71, 0xCB, 0xCB, 0xB7, 0x31, 0xE5, 0x1C, 0xA4, 0x4B, 0x4B, 0x5B, 0xF1, 0x39, 0x2D, 0xA3, 0x72, 0x0C, 0x15, 0xB0, 0xF9, 0x6C, + 0xAB, 0x52, 0x0C, 0xC4, 0x71, 0x24, 0x82, 0x94, 0x33, 0x20, 0xCA, 0x95, 0x5D, 0x53, 0x3F, 0xC3, 0x06, 0xC9, 0x61, 0x35, 0xDE, 0x96, 0xFC, 0x2A, + 0xB7, 0x01, 0xA7, 0xA7, 0x26, 0xA6, 0x17, 0xBB, 0xCF, 0x18, 0x99, 0x55, 0x5B, 0x81, 0xB0, 0x02, 0x88, 0x05, 0xE4, 0x42, 0x33, 0x53, 0x26, 0xA4, + 0x35, 0x47, 0x2C, 0x45, 0x71, 0x00, 0x1D, 0xA1, 0x01, 0x0B, 0x65, 0x29, 0xD8, 0x44, 0x84, 0x14, 0xB2, 0x6B, 0x64, 0xB4, 0xD0, 0x1D, 0xA4, 0x47, + 0x20, 0xE0, 0x25, 0x5E, 0x0A, 0x40, 0x52, 0x42, 0x42, 0x91, 0xE1, 0x04, 0x36, 0x07, 0x98, 0x9A, 0x9C, 0x0C, 0x18, 0x60, 0x5A, 0x72, 0x8C, 0x10, + 0x49, 0x25, 0x1F, 0x50, 0xAA, 0x90, 0xAB, 0x8C, 0xB9, 0xE9, 0x42, 0xBB, 0x82, 0x24, 0x5E, 0x5B, 0x9E, 0x86, 0xA2, 0x4C, 0x6A, 0x30, 0x89, 0xD5, + 0xE5, 0x42, 0x13, 0x09, 0x63, 0x8D, 0x31, 0x87, 0xFE, 0x12, 0x69, 0x6D, 0x82, 0x88, 0xC6, 0xE3, 0xC8, 0x7A, 0x06, 0xEA, 0xD1, 0x94, 0x75, 0x0F, + 0x07, 0x19, 0xC2, 0x03, 0xF9, 0x80, 0x05, 0x09, 0x1B, 0xE4, 0x1A, 0x83, 0xF5, 0x92, 0x19, 0xA1, 0x32, 0xC9, 0xE6, 0xBB, 0x9A, 0x2D, 0x95, 0x64, + 0x70, 0xA1, 0x24, 0x08, 0x72, 0x92, 0x11, 0x49, 0x92, 0xBA, 0xC8, 0x8C, 0x5D, 0x22, 0x59, 0xD1, 0x91, 0xF5, 0xA4, 0xC9, 0x42, 0xDA, 0xE0, 0xA5, + 0xC6, 0x83, 0x00, 0x6D, 0x2D, 0x52, 0x5F, 0x3D, 0x92, 0x49, 0x88, 0xBD, 0xD1, 0xA9, 0x1D, 0xA3, 0x1D, 0xDE, 0x24, 0x3C, 0x03, 0xF8, 0x20, 0x1F, + 0x62, 0x79, 0x5A, 0x65, 0x71, 0x50, 0x92, 0xC1, 0xFC, 0xA9, 0xE6, 0x4D, 0xFD, 0x98, 0x1D, 0x9B, 0x94, 0x65, 0x32, 0x75, 0xFC, 0xEE, 0x96, 0x24, + 0xED, 0xC8, 0x31, 0xA9, 0xB6, 0x52, 0x55, 0x8B, 0xB1, 0xB2, 0x43, 0x65, 0x04, 0xE4, 0x49, 0x01, 0x1C, 0x3D, 0x85, 0x50, 0xD4, 0x7A, 0x26, 0xA8, + 0xD9, 0x21, 0x73, 0x51, 0x87, 0x2F, 0x11, 0x9D, 0xA7, 0x91, 0x39, 0x33, 0x16, 0x40, 0xD2, 0x60, 0xE4, 0x3C, 0xF9, 0x7A, 0x5E, 0xEF, 0xC8, 0x8F, + 0x43, 0x58, 0x34, 0xFA, 0xF3, 0x6C, 0xD4, 0xA3, 0xC6, 0x42, 0xA1, 0x06, 0x04, 0xF5, 0x29, 0x58, 0x95, 0x65, 0x2B, 0x51, 0xB7, 0x39, 0x54, 0x23, + 0xCA, 0x4D, 0x62, 0x44, 0xE9, 0xAB, 0xB5, 0xAA, 0xBE, 0x82, 0x78, 0x18, 0x35, 0xB5, 0x81, 0x2D, 0x69, 0x47, 0xDA, 0x77, 0x76, 0x9C, 0xFD, 0xB8, + 0xDD, 0xEC, 0x32, 0x95, 0xBC, 0x48, 0x48, 0x01, 0xF5, 0xE6, 0x4E, 0x5B, 0xA9, 0x84, 0xA1, 0xC4, 0xDB, 0x75, 0x1E, 0xF5, 0x25, 0x50, 0x80, 0xA4, + 0xCA, 0x8A, 0xD7, 0x03, 0x7D, 0x58, 0xA4, 0xB6, 0x54, 0x77, 0x6D, 0xE0, 0x57, 0x61, 0x7A, 0xDD, 0x20, 0xE3, 0xEC, 0xC7, 0xD5, 0x79, 0xBD, 0xF6, + 0x0D, 0x70, 0x05, 0xA4, 0xD1, 0xB6, 0x08, 0x92, 0xC3, 0x49, 0x95, 0x46, 0x37, 0x7F, 0x0C, 0x49, 0x42, 0x20, 0x01, 0x6B, 0x52, 0x15, 0x1F, 0x88, + 0xBB, 0xEB, 0x8A, 0x6B, 0xF5, 0x4A, 0xE3, 0x9A, 0xD7, 0x42, 0x5B, 0x42, 0x3D, 0x85, 0x6C, 0x40, 0x69, 0xB9, 0xB4, 0x67, 0xDA, 0x39, 0xAD, 0x97, + 0x3A, 0x4D, 0x3D, 0x43, 0x5E, 0x91, 0x6D, 0xCB, 0xD2, 0x4D, 0xD6, 0x4D, 0x64, 0x1A, 0xD9, 0xBF, 0xEC, 0x83, 0xDA, 0x8C, 0xEC, 0x24, 0xEB, 0xF6, + 0xE4, 0x50, 0x3B, 0xA9, 0xDD, 0xB2, 0x4F, 0xB2, 0x73, 0x35, 0x5F, 0xDD, 0xCA, 0x74, 0xE4, 0x02, 0xE4, 0x2B, 0x32, 0x91, 0x9A, 0x59, 0x37, 0xC9, + 0x71, 0x75, 0x8A, 0x3A, 0x5A, 0xD7, 0x75, 0x8E, 0x7A, 0x4F, 0x56, 0xAC, 0xC1, 0xD4, 0x55, 0x64, 0x46, 0xD9, 0x51, 0x7E, 0x93, 0x99, 0x64, 0x44, + 0x39, 0x4B, 0xDE, 0xAB, 0x11, 0xC9, 0x4E, 0xEA, 0xAA, 0xC7, 0x32, 0x21, 0xDA, 0x90, 0xCC, 0x47, 0x7F, 0xA9, 0xC3, 0x3E, 0x43, 0x4E, 0x12, 0x89, + 0x66, 0x43, 0x0B, 0xCA, 0x40, 0x06, 0xF5, 0xE8, 0xD1, 0xB4, 0xB4, 0x27, 0x77, 0xBD, 0xE9, 0x6C, 0x48, 0xAB, 0x24, 0x3C, 0xDA, 0x38, 0x70, 0x14, + 0x65, 0x38, 0x60, 0x5E, 0xA0, 0x44, 0xA8, 0x12, 0x61, 0x8A, 0xDF, 0x99, 0xDE, 0x75, 0xB0, 0xC4, 0x26, 0x07, 0x51, 0x62, 0xA9, 0x69, 0x09, 0x83, + 0x2C, 0xE5, 0x6F, 0xCE, 0x84, 0xCE, 0x42, 0x93, 0xA1, 0x1F, 0x69, 0x3E, 0xB4, 0x21, 0x1A, 0x12, 0x7D, 0xE7, 0x29, 0xEF, 0x4B, 0x76, 0x08, 0x8D, + 0xE7, 0x0B, 0xC2, 0x2C, 0x0A, 0xB6, 0xE6, 0x99, 0x9A, 0xB6, 0x12, 0x70, 0xC3, 0xD9, 0x0F, 0xAA, 0xA0, 0xAF, 0xEA, 0x92, 0x03, 0xD6, 0x24, 0xB1, + 0x64, 0xAC, 0xCC, 0xD2, 0x87, 0x1F, 0xAB, 0x8E, 0x19, 0x35, 0x12, 0x31, 0xAB, 0x35, 0x88, 0xAA, 0x06, 0x41, 0xD6, 0x7D, 0xB0, 0xD9, 0x08, 0x38, + 0xB0, 0xF2, 0xA4, 0x54, 0x53, 0x0A, 0x16, 0x3D, 0x64, 0x08, 0xB4, 0x50, 0xA2, 0x0F, 0x40, 0x52, 0x01, 0x05, 0xF9, 0x06, 0x2A, 0xC2, 0x0B, 0xA7, + 0x48, 0xD5, 0x3B, 0x81, 0x30, 0x20, 0x0D, 0x22, 0x8F, 0x41, 0x36, 0x82, 0x1A, 0x60, 0xC4, 0x87, 0x28, 0x11, 0x23, 0xC6, 0xE7, 0x16, 0x12, 0x4D, + 0x3B, 0x90, 0x4F, 0x3A, 0x01, 0x34, 0xB9, 0xE8, 0x29, 0x54, 0x42, 0x8F, 0xB2, 0x48, 0x24, 0x54, 0x8A, 0x3C, 0x18, 0x10, 0xE1, 0xB9, 0xE0, 0xE2, + 0xCE, 0x9B, 0x02, 0x3B, 0x67, 0xCA, 0x3A, 0x4E, 0x40, 0xD0, 0x09, 0x84, 0x8A, 0x43, 0xB7, 0x31, 0xD1, 0xA8, 0xCD, 0x6E, 0xE0, 0x18, 0xA3, 0xC4, + 0x15, 0x34, 0x62, 0x56, 0x20, 0xE1, 0xA8, 0x62, 0xC0, 0xA4, 0x2A, 0x5D, 0x5D, 0xCF, 0x32, 0x90, 0x56, 0x1D, 0x8D, 0x12, 0x1D, 0x23, 0xA7, 0xD7, + 0xB3, 0xDF, 0x40, 0x78, 0x1F, 0x1B, 0x05, 0xBD, 0x89, 0x73, 0xE2, 0x09, 0x65, 0x1A, 0x57, 0x59, 0xB5, 0x92, 0x28, 0x2D, 0x5D, 0x0B, 0xC8, 0x2E, + 0xCD, 0x26, 0x59, 0xEB, 0xF4, 0x5D, 0xB8, 0xD2, 0x81, 0x29, 0xB5, 0x91, 0x34, 0x58, 0x01, 0x59, 0x9F, 0x78, 0x5A, 0xFE, 0x48, 0x18, 0xE2, 0xCD, + 0x9C, 0x09, 0x45, 0xF2, 0xDC, 0x3C, 0xD8, 0xB2, 0xB2, 0x89, 0x36, 0xB5, 0x5B, 0x58, 0xAE, 0x77, 0x73, 0x0E, 0x30, 0x51, 0x99, 0x1C, 0x77, 0xA5, + 0x4A, 0x0B, 0x2C, 0xC0, 0x51, 0x84, 0x8A, 0x97, 0x36, 0x75, 0x0A, 0x25, 0x5A, 0xE0, 0xF5, 0x65, 0xC2, 0xCD, 0xEA, 0x86, 0xC5, 0x80, 0x6B, 0x12, + 0x48, 0x00, 0x0E, 0x03, 0xA6, 0x4C, 0xE6, 0xA0, 0xA2, 0x52, 0x1C, 0x5B, 0x83, 0xA7, 0xE8, 0x2A, 0xE6, 0xE4, 0xBA, 0xBC, 0x9E, 0x68, 0xBC, 0x82, + 0x5D, 0x59, 0xFC, 0xB9, 0x62, 0x19, 0xB4, 0x3F, 0x24, 0x5D, 0x21, 0x99, 0xED, 0x16, 0x09, 0xD7, 0x38, 0x47, 0x4A, 0x50, 0xCB, 0x68, 0x21, 0xAB, + 0x36, 0x47, 0x0B, 0x05, 0x40, 0xAB, 0xD8, 0x5E, 0xC0, 0x6B, 0xD7, 0xB4, 0x62, 0x64, 0x49, 0x2E, 0xAC, 0x44, 0x92, 0x5C, 0x5A, 0xA0, 0x91, 0x0B, + 0x24, 0x4A, 0x84, 0x26, 0xB3, 0x73, 0x26, 0x29, 0x34, 0x3D, 0x3A, 0x22, 0x47, 0x25, 0x2A, 0x34, 0xC2, 0x52, 0xC0, 0x68, 0xAF, 0x6C, 0x91, 0x89, + 0x15, 0x8A, 0x04, 0xD8, 0x4B, 0xE4, 0xB6, 0xD5, 0x4C, 0x60, 0x20, 0x8A, 0xF1, 0xA7, 0xC0, 0x11, 0x2D, 0x09, 0x70, 0x59, 0xE0, 0x50, 0x18, 0x39, + 0x08, 0xF8, 0x71, 0x1A, 0xDA, 0x93, 0x40, 0xA1, 0x49, 0x2C, 0x22, 0x21, 0x90, 0x9C, 0xA2, 0x23, 0x54, 0x75, 0x61, 0xD0, 0x08, 0x2C, 0x82, 0x34, + 0x24, 0x38, 0x80, 0xCE, 0x1A, 0x68, 0xCE, 0x0B, 0x50, 0x20, 0xD5, 0x80, 0x99, 0x4D, 0xC8, 0xE3, 0x35, 0xCB, 0x83, 0x2E, 0x86, 0xAE, 0x69, 0x20, + 0xF5, 0x79, 0x13, 0x87, 0x8F, 0x20, 0x08, 0x51, 0x72, 0x24, 0xA4, 0x21, 0x91, 0x60, 0x3D, 0xF2, 0x64, 0xE8, 0xC2, 0x13, 0x77, 0xE5, 0xCE, 0x04, + 0x2C, 0x3B, 0x6D, 0xD9, 0x13, 0x50, 0x5A, 0x4C, 0xD2, 0x42, 0x89, 0xCF, 0x5D, 0xA1, 0x33, 0x32, 0xC0, 0x03, 0x02, 0x89, 0xBD, 0xC1, 0x1C, 0xE0, + 0x6A, 0x98, 0x65, 0x4A, 0x1C, 0x96, 0x36, 0x74, 0xAE, 0x74, 0x79, 0x64, 0xA5, 0x50, 0x7D, 0xC7, 0x41, 0x95, 0xD7, 0x41, 0x60, 0x52, 0xD1, 0x2B, + 0xC3, 0x14, 0xAE, 0x35, 0x52, 0xB4, 0x9E, 0x51, 0x4A, 0x44, 0x82, 0x12, 0xB7, 0xF8, 0xC4, 0x85, 0xA3, 0x93, 0x37, 0x49, 0x93, 0x41, 0x49, 0xD7, + 0x1A, 0x61, 0x5A, 0x26, 0x45, 0x24, 0x2C, 0x49, 0xA2, 0x44, 0x09, 0x14, 0x34, 0x49, 0xE4, 0x30, 0xAF, 0x78, 0x10, 0x48, 0x21, 0xD9, 0xE2, 0xF2, + 0xE3, 0xC9, 0x69, 0x2B, 0xB0, 0xD7, 0x48, 0x60, 0x54, 0xF5, 0xD4, 0x87, 0x7E, 0xEE, 0xB1, 0xD9, 0x13, 0xD2, 0xA8, 0x42, 0x00, 0x41, 0x29, 0xBA, + 0xFC, 0x09, 0xE0, 0xBA, 0x84, 0xC2, 0x8E, 0x24, 0xBD, 0x2F, 0x83, 0xF2, 0x1A, 0x49, 0xC0, 0xCB, 0xC3, 0x84, 0xE7, 0x60, 0xEC, 0x8F, 0xB2, 0x68, + 0x41, 0x10, 0xE8, 0x2D, 0xFA, 0xEC, 0x2D, 0x1B, 0xA1, 0xEE, 0xAA, 0x3F, 0xD4, 0x6A, 0x75, 0x5D, 0xED, 0x57, 0x4F, 0x6B, 0x19, 0xF5, 0x8F, 0xFA, + 0x5A, 0x47, 0x41, 0x2E, 0x68, 0xE4, 0xD3, 0xEC, 0x43, 0x36, 0x05, 0x32, 0xCE, 0xFA, 0x24, 0x84, 0xD1, 0x74, 0x1C, 0x13, 0x64, 0x49, 0x20, 0x93, + 0x04, 0x76, 0x5F, 0x1C, 0x27, 0x9D, 0x08, 0xC5, 0x52, 0x07, 0xD0, 0x90, 0x83, 0xC8, 0x4E, 0x64, 0x2D, 0x72, 0xA3, 0x2C, 0x47, 0xFE, 0x23, 0x73, + 0x49, 0x26, 0x3A, 0x63, 0xA5, 0xCB, 0x2E, 0xA5, 0xB2, 0x56, 0x2A, 0xA5, 0xA5, 0x18, 0x25, 0xAB, 0x8C, 0x29, 0xCB, 0x13, 0x8A, 0x9C, 0x28, 0xD6, + 0x11, 0xA2, 0xA0, 0x59, 0x3A, 0xA0, 0x9B, 0x52, 0x72, 0xB4, 0x91, 0x8D, 0x14, 0x66, 0xDB, 0x2C, 0xF2, 0xF2, 0x7C, 0x02, 0x47, 0xC4, 0x89, 0x2C, + 0xA1, 0x89, 0x9F, 0xE1, 0xF5, 0x48, 0x9A, 0x65, 0x20, 0x84, 0x1F, 0x49, 0xD2, 0x4E, 0x55, 0xE8, 0xA2, 0x03, 0x7D, 0xE0, 0x11, 0xDE, 0xF5, 0x0F, + 0x57, 0x7B, 0xDD, 0xFD, 0x3E, 0xBD, 0x19, 0x3A, 0x0E, 0xED, 0x87, 0x86, 0x7C, 0x22, 0x80, 0xC4, 0x32, 0xE7, 0xC9, 0x86, 0x8A, 0x69, 0x4D, 0x57, + 0x03, 0x35, 0x63, 0x59, 0x69, 0xD8, 0xC4, 0xD0, 0xE0, 0x91, 0x9D, 0x81, 0xCA, 0x11, 0x50, 0xA8, 0xA2, 0x11, 0x51, 0xD0, 0x4C, 0xE2, 0x8B, 0x32, + 0x8A, 0x3A, 0xC8, 0x2C, 0x1E, 0x19, 0xE3, 0x48, 0x45, 0xCE, 0x61, 0x98, 0x02, 0x78, 0x20, 0x4A, 0xC2, 0x48, 0x62, 0x11, 0x96, 0xCE, 0x93, 0x53, + 0x9C, 0x7F, 0x88, 0xB0, 0xF1, 0xEC, 0x33, 0x3B, 0xCC, 0x3E, 0x3C, 0x93, 0x04, 0x3A, 0x2A, 0x2B, 0x97, 0x95, 0xEA, 0xC1, 0x9A, 0x66, 0x8D, 0xD2, + 0x21, 0x41, 0x01, 0x94, 0x3E, 0xEB, 0x0F, 0xA9, 0x0A, 0x13, 0x11, 0xF0, 0xAC, 0x01, 0x80, 0x00, 0x35, 0x63, 0x28, 0xD2, 0x94, 0x08, 0x43, 0x83, + 0x67, 0x78, 0x06, 0x8A, 0x48, 0x40, 0x61, 0x91, 0x46, 0x24, 0x55, 0x66, 0x12, 0xBD, 0x94, 0x61, 0x82, 0x83, 0xCC, 0x62, 0x9D, 0x31, 0x8E, 0x80, + 0xC4, 0x44, 0x26, 0x0A, 0x83, 0x47, 0x07, 0x23, 0x22, 0x3B, 0x95, 0x02, 0x2D, 0x2E, 0x50, 0x04, 0x46, 0xC3, 0xE0, 0x17, 0xE6, 0xB2, 0x4D, 0xAF, + 0xED, 0xD2, 0x04, 0xE3, 0x32, 0x6F, 0xB9, 0xBE, 0x5D, 0x03, 0xEB, 0xA6, 0xBA, 0x02, 0x96, 0x36, 0x53, 0xAE, 0xB0, 0x29, 0xB3, 0x92, 0xA7, 0x59, + 0x85, 0xD0, 0x5E, 0xAD, 0x00, 0x07, 0xD9, 0x6B, 0x4F, 0xE1, 0x42, 0x22, 0x45, 0xAB, 0x43, 0x14, 0x15, 0x15, 0x0E, 0xC5, 0xC8, 0x71, 0x26, 0x99, + 0x5A, 0x72, 0x22, 0x5A, 0x1B, 0x2D, 0x28, 0xE9, 0xF8, 0x37, 0x7D, 0xC5, 0xD2, 0x46, 0x60, 0xFF, 0xD9, 0x6C, 0x76, 0xE3, 0xD6, 0x7B, 0x9D, 0x00, + 0x11, 0xAD, 0x50, 0xA1, 0x1E, 0x25, 0x01, 0x91, 0x50, 0x44, 0xF2, 0x64, 0x90, 0x80, 0xE7, 0x42, 0x97, 0xA5, 0xE2, 0xF4, 0x7F, 0x84, 0xAC, 0x73, + 0x5A, 0x3B, 0x6E, 0x15, 0xEA, 0xF4, 0x55, 0x59, 0x9B, 0xC8, 0xE1, 0x95, 0xA1, 0xBF, 0x10, 0xF9, 0xFC, 0xC0, 0x11, 0x8A, 0x05, 0x13, 0x1A, 0x45, + 0x96, 0x30, 0x5A, 0x95, 0x40, 0xF1, 0x92, 0x44, 0x4F, 0x16, 0x24, 0x77, 0xF6, 0xD4, 0x48, 0x12, 0x40, 0x78, 0xC6, 0x4E, 0xDC, 0xC9, 0x47, 0xBB, + 0xD0, 0x1E, 0xB4, 0xB3, 0xDB, 0xCE, 0x36, 0xD3, 0x60, 0x8C, 0xCC, 0xDA, 0x64, 0x87, 0x0C, 0xAD, 0xE5, 0xD9, 0xD2, 0x8D, 0xB2, 0x43, 0x56, 0x85, + 0x88, 0x9A, 0x4D, 0x8B, 0xCC, 0xFE, 0xF1, 0x40, 0xC4, 0x99, 0xA6, 0x9A, 0x8C, 0xE7, 0x5C, 0x95, 0xDA, 0xCF, 0x05, 0xA0, 0xA4, 0x25, 0x8D, 0x0F, + 0xF4, 0x8E, 0x14, 0x34, 0x94, 0x3A, 0x9A, 0x0C, 0x13, 0x38, 0x59, 0x8E, 0x80, 0x8A, 0xD1, 0x21, 0x15, 0x4F, 0x30, 0x07, 0x0A, 0x68, 0xB0, 0xE4, + 0x29, 0x27, 0x08, 0xEC, 0x84, 0x2A, 0x5B, 0xE5, 0xAD, 0x76, 0xB3, 0x54, 0x6C, 0xB6, 0x51, 0xD4, 0xC7, 0xB8, 0x66, 0x00, 0xC6, 0x17, 0x85, 0x90, + 0xD0, 0x58, 0xB4, 0x55, 0xFB, 0x61, 0x69, 0x2F, 0x7D, 0x25, 0xB0, 0x7B, 0x34, 0x45, 0xF7, 0x76, 0x15, 0xD7, 0x4F, 0x64, 0x1E, 0xB5, 0x8D, 0xDA, + 0x4A, 0x0D, 0xA5, 0x56, 0xA0, 0x4D, 0x7D, 0xA6, 0xC6, 0x22, 0x67, 0x6B, 0xAD, 0x37, 0x32, 0xA5, 0x7A, 0x85, 0x93, 0x8C, 0xE4, 0xB2, 0x0D, 0x2D, + 0x59, 0xCC, 0x04, 0x59, 0xCA, 0x99, 0xCC, 0x47, 0x84, 0x4C, 0x66, 0xAB, 0x8E, 0xA4, 0x27, 0xFB, 0x41, 0xAB, 0x0B, 0x65, 0xBE, 0x5A, 0x03, 0x8A, + 0x9A, 0xAE, 0x0D, 0xEB, 0x25, 0x75, 0x91, 0x7A, 0x4C, 0x7D, 0xE8, 0x49, 0x25, 0x73, 0xC9, 0x91, 0x6A, 0xAB, 0x03, 0x35, 0x55, 0x1A, 0xF5, 0xDF, + 0x51, 0x3E, 0xE7, 0x22, 0xE4, 0x12, 0x94, 0x87, 0xCA, 0x16, 0x4A, 0x43, 0x65, 0xE0, 0x66, 0xDC, 0x60, 0xF6, 0x97, 0xED, 0xE5, 0x4F, 0x87, 0x56, + 0x99, 0xE6, 0x93, 0xE9, 0x9E, 0x6A, 0xCD, 0x31, 0x32, 0x2F, 0xA6, 0x55, 0x9B, 0x26, 0xFB, 0x05, 0x52, 0xB1, 0xF1, 0x08, 0x76, 0xC7, 0x35, 0x86, + 0xEC, 0x83, 0x56, 0x62, 0xB5, 0x72, 0x65, 0xC4, 0xB2, 0x69, 0xBC, 0x03, 0x19, 0x28, 0x7B, 0x47, 0xF6, 0x36, 0xA7, 0xCF, 0x4A, 0x95, 0xBE, 0xBF, + 0x2B, 0x6D, 0x4A, 0x7B, 0xD2, 0xEE, 0xB2, 0xB9, 0x6C, 0x28, 0xBB, 0xC8, 0x26, 0xF2, 0x03, 0xFF, 0x19, 0xB1, 0x8C, 0xD8, 0x29, 0xBC, 0x7F, 0xCF, + 0x5B, 0x70, 0xFB, 0xED, 0xE7, 0x37, 0x8F, 0x34, 0x78, 0x3D, 0x68, 0x08, 0x09, 0x70, 0x08, 0x20, 0xA2, 0xA9, 0xBA, 0xE3, 0x68, 0x12, 0x33, 0xAD, + 0x9B, 0x36, 0x80, 0xAB, 0x00, 0x57, 0xBE, 0x2B, 0xD5, 0x35, 0xD2, 0xB5, 0xEF, 0x3A, 0x5F, 0x63, 0x5C, 0xE5, 0xB4, 0x6E, 0xED, 0x9F, 0xEB, 0x83, + 0x03, 0x1A, 0x50, 0x77, 0xF4, 0x86, 0x24, 0x10, 0xE7, 0x16, 0x16, 0x84, 0x42, 0x6E, 0x98, 0x84, 0x4D, 0x0C, 0xFA, 0x01, 0x21, 0x26, 0xF4, 0xF4, + 0x41, 0x0D, 0xAA, 0xD3, 0xED, 0xC0, 0xED, 0xF4, 0x14, 0x44, 0x71, 0x88, 0x05, 0x1D, 0x93, 0x41, 0xEA, 0xBC, 0x42, 0xC2, 0xC2, 0x21, 0xF2, 0x90, + 0x42, 0x2E, 0x85, 0x44, 0x26, 0xBB, 0x4C, 0xD2, 0x99, 0x9C, 0x02, 0xA8, 0x30, 0x6A, 0x52, 0x51, 0xE1, 0xB2, 0x10, 0xAC, 0xF0, 0x4D, 0x06, 0xF2, + 0x56, 0x88, 0x47, 0xCB, 0x5B, 0x00, 0x0B, 0x97, 0xC8, 0x88, 0x85, 0x63, 0xE4, 0xC0, 0x49, 0x26, 0x35, 0x8E, 0x49, 0x39, 0x64, 0xEA, 0x29, 0x93, + 0x88, 0xF8, 0xD4, 0x6A, 0x26, 0xA5, 0x75, 0xA0, 0xC9, 0x1D, 0x35, 0x9B, 0x42, 0x59, 0xAE, 0x32, 0x69, 0xD8, 0x43, 0x5E, 0x2A, 0x64, 0x42, 0xB1, + 0x0A, 0xE3, 0xA0, 0x25, 0x0A, 0xD9, 0x44, 0xA1, 0x29, 0x0A, 0x09, 0xE5, 0x80, 0x92, 0x4D, 0xAE, 0x48, 0x10, 0x44, 0xF1, 0x20, 0x74, 0x5C, 0xB9, + 0x42, 0xFB, 0xA0, 0xDD, 0xD0, 0xD3, 0x3B, 0xB1, 0xE3, 0xE0, 0x9F, 0x6E, 0xE8, 0x7E, 0x73, 0x58, 0x5D, 0x9F, 0x67, 0x4C, 0xC2, 0x6C, 0x2D, 0xA1, + 0x5D, 0x25, 0x7B, 0xD0, 0x68, 0x2A, 0xD8, 0x0F, 0xD8, 0x71, 0xFD, 0xD3, 0x45, 0x82, 0x17, 0x42, 0xC6, 0x41, 0xD1, 0xB6, 0x44, 0xA7, 0x80, 0xE1, + 0x5A, 0x85, 0x59, 0xAD, 0x29, 0x80, 0x34, 0x16, 0x5D, 0x06, 0x30, 0x23, 0xE4, 0x82, 0xD6, 0xDC, 0x54, 0x75, 0x0A, 0x6F, 0x2A, 0x6A, 0x2A, 0xD1, + 0x54, 0xDE, 0x14, 0x74, 0x8A, 0x70, 0x2A, 0x8D, 0x10, 0x32, 0x21, 0x76, 0xA6, 0xA8, 0x52, 0x0E, 0x60, 0x1B, 0x48, 0xF7, 0x8A, 0xBF, 0x85, 0x22, + 0x10, 0xBC, 0x03, 0x4C, 0x7B, 0x70, 0xF7, 0xD1, 0x48, 0x82, 0x37, 0xA4, 0x90, 0xCB, 0x00, 0x5E, 0xB4, 0x10, 0x25, 0x82, 0x90, 0xC0, 0x3A, 0xC8, + 0x14, 0x25, 0x8B, 0xE8, 0x77, 0xBB, 0x2D, 0x6F, 0x3E, 0x1F, 0x85, 0x03, 0x64, 0xB9, 0x00, 0xF5, 0xE3, 0x72, 0x1E, 0xB5, 0x10, 0x19, 0xBA, 0x40, + 0x4F, 0xBF, 0x46, 0xDB, 0xD1, 0x2F, 0xCD, 0xE5, 0x61, 0x3F, 0x43, 0xCF, 0x09, 0x30, 0x79, 0xBA, 0xF2, 0x2C, 0xB6, 0x1B, 0xE3, 0x10, 0x1B, 0xA1, + 0x23, 0x8F, 0xA8, 0xAC, 0x55, 0xF8, 0x53, 0x6B, 0x9A, 0x4B, 0x3B, 0xAB, 0x81, 0x06, 0x56, 0xE5, 0xAC, 0xEF, 0x89, 0x59, 0x5A, 0x8F, 0xB2, 0x19, + 0x04, 0x90, 0xBD, 0x57, 0x4A, 0x1C, 0x89, 0x42, 0xA2, 0x86, 0x9B, 0x11, 0x61, 0xD0, 0x88, 0x88, 0x22, 0x19, 0x62, 0x8F, 0x01, 0x00, 0x8C, 0x79, + 0x03, 0x62, 0x81, 0x4A, 0x63, 0x7D, 0x0A, 0x8D, 0x55, 0x5D, 0x3D, 0x4D, 0x72, 0xF2, 0xA0, 0xE1, 0x1D, 0xAE, 0xB4, 0x38, 0xCC, 0x4D, 0xD8, 0x30, + 0xE2, 0x4C, 0xC3, 0x2C, 0x42, 0x2C, 0x85, 0xCB, 0x04, 0xA2, 0x17, 0x46, 0x4D, 0x03, 0xDB, 0x52, 0xF8, 0xA5, 0xA3, 0x2B, 0x03, 0x1D, 0x22, 0x18, + 0x12, 0xE7, 0x78, 0x15, 0x6A, 0xF1, 0xF4, 0xC6, 0x0F, 0xF1, 0x02, 0x54, 0xA2, 0x9C, 0x3A, 0x5C, 0x17, 0xBE, 0x6C, 0x29, 0x57, 0x68, 0xB5, 0xD9, + 0xB1, 0x90, 0x45, 0xA3, 0x02, 0xAC, 0x28, 0xCB, 0xCC, 0x09, 0x16, 0x7D, 0xA7, 0xCD, 0xAB, 0x41, 0xA1, 0x08, 0xE3, 0xD3, 0x04, 0xC3, 0x27, 0x57, + 0x04, 0x48, 0x59, 0xDD, 0x78, 0x43, 0x0C, 0x6F, 0x24, 0xBF, 0xDD, 0x28, 0x7E, 0xB7, 0xB1, 0xD6, 0xDF, 0x6B, 0xB0, 0xDF, 0x69, 0xAC, 0xF2, 0xF7, + 0x99, 0x2E, 0xC8, 0x6F, 0x02, 0x9A, 0xDF, 0x66, 0x98, 0xDF, 0x63, 0x12, 0xBF, 0x19, 0x27, 0xBF, 0xC1, 0x78, 0x29, 0x5C, 0xFC, 0xEE, 0x02, 0xFD, + 0xD6, 0xE2, 0xF8, 0x3D, 0xC0, 0xBF, 0xB3, 0x50, 0x7D, 0x5B, 0xA1, 0xBF, 0xAB, 0x28, 0x92, 0x4D, 0x7E, 0x4F, 0xC1, 0x83, 0x62, 0x5D, 0xFC, 0xBE, + 0x42, 0xBF, 0x99, 0x64, 0xBF, 0x95, 0x20, 0x61, 0xB3, 0x7E, 0x27, 0x3E, 0x7D, 0xEF, 0x90, 0xFC, 0x8E, 0x71, 0x88, 0x28, 0x7F, 0xC3, 0x60, 0x46, + 0x7E, 0xBF, 0x60, 0x73, 0xFC, 0x26, 0x64, 0x7E, 0x53, 0xAD, 0xDF, 0x73, 0xE1, 0x77, 0x3C, 0x89, 0xF7, 0x2D, 0x48, 0xF5, 0x0D, 0x48, 0x7F, 0x43, + 0x9F, 0xDF, 0x2B, 0xD8, 0xBE, 0xDF, 0xF6, 0xBB, 0xEF, 0xE9, 0xFB, 0x39, 0xF9, 0xAD, 0x02, 0xFA, 0x8D, 0xA2, 0xFD, 0xCE, 0x7B, 0xC2, 0xCA, 0xDF, + 0x78, 0x63, 0xDF, 0x75, 0xCB, 0x6F, 0x3A, 0xC7, 0xEF, 0x13, 0x78, 0x92, 0xDF, 0x4D, 0xAA, 0xEF, 0xB7, 0xC9, 0xEF, 0xB5, 0xE5, 0xF7, 0x19, 0xFD, + 0x3D, 0x42, 0xFE, 0xFE, 0xC0, 0xFC, 0xD6, 0xC0, 0x16, 0xF9, 0xCD, 0x0C, 0x83, 0x7E, 0x77, 0x21, 0x7E, 0x6B, 0xE1, 0x7D, 0x63, 0x65, 0xBF, 0x27, + 0xC8, 0xDF, 0x57, 0x88, 0xDF, 0x54, 0xD8, 0x6F, 0x08, 0xD0, 0xEF, 0x07, 0xCB, 0xEF, 0x06, 0x64, 0xDF, 0x52, 0x5D, 0xDF, 0x0B, 0x12, 0xBF, 0x13, + 0x38, 0x7E, 0x3F, 0xE1, 0x7D, 0x3B, 0x65, 0xBF, 0x0D, 0x28, 0x42, 0x7E, 0x33, 0x31, 0x1D, 0xBF, 0x97, 0x0A, 0xBF, 0xF7, 0xD3, 0x3F, 0x29, 0xF1, + 0x5F, 0x80, 0xF9, 0x3B, 0x72, 0xFC, 0x17, 0x51, 0xFD, 0x89, 0xE8, 0x5F, 0x81, 0xE6, 0x4F, 0xA8, 0xFE, 0x0F, 0x48, 0xFC, 0x01, 0x75, 0xFD, 0x7D, + 0x1C, 0x7F, 0x3E, 0x91, 0x78, 0xBF, 0x1D, 0xB2, 0x3F, 0xCE, 0x22, 0xDE, 0x0F, 0xE7, 0xE9, 0x7F, 0xB3, 0xF8, 0xBB, 0x91, 0xFC, 0xD3, 0x5C, 0x23, + 0x7F, 0x34, 0xD6, 0xBF, 0x91, 0xEC, 0x17, 0x13, 0xF9, 0x2F, 0x2A, 0xFE, 0x60, 0xB0, 0x7F, 0x17, 0xC8, 0x1F, 0x8B, 0xFD, 0xB3, 0x34, 0xFE, 0x58, + 0xFE, 0x5F, 0x79, 0xFA, 0xAB, 0x14, 0xFE, 0x8A, 0xCC, 0x5F, 0x8A, 0xFC, 0x3B, 0x91, 0xFC, 0x9B, 0xB0, 0xFD, 0xD7, 0xC4, 0x3F, 0x89, 0x95, 0x08, + 0xD5, 0xE2, 0x3F, 0x84, 0xF9, 0x03, 0x19, 0xFB, 0x77, 0x14, 0xFE, 0x37, 0xEA, 0xC5, 0x5F, 0x06, 0xDE, 0x4F, 0xD7, 0xFF, 0x0B, 0xB6, 0xDF, 0x70, + 0xEC, 0xAF, 0x5A, 0xFF, 0x69, 0xF3, 0x8F, 0x1B, 0x7F, 0x69, 0xF3, 0x07, 0x9C, 0xFC, 0x5F, 0xF9, 0xE7, 0x9B, 0xFC, 0x9F, 0x89, 0xBF, 0x1E, 0xF3, + 0xCF, 0x93, 0xFC, 0xF3, 0xE8, 0xFF, 0x6E, 0xF1, 0xA7, 0x6B, 0xFE, 0x27, 0xF0, 0x7E, 0x38, 0xC9, 0x1F, 0x2E, 0xF1, 0x7F, 0x1B, 0xFB, 0xD9, 0xAC, + 0xBF, 0x5A, 0xE2, 0x5F, 0x16, 0x19, 0xC2, 0xFA, 0x83, 0x58, 0xFE, 0x1E, 0x12, 0x7F, 0x0E, 0xED, 0x2F, 0xD6, 0xF8, 0x67, 0x88, 0xFC, 0xBB, 0xB4, + 0x12, 0xFF, 0x0A, 0xED, 0xDF, 0x2A, 0xFE, 0x13, 0x14, 0xFF, 0xAA, 0xC2, 0x9F, 0x8A, 0xED, 0x7F, 0xD0, 0xFE, 0x53, 0xD8, 0xBF, 0x41, 0xF3, 0x67, + 0x50, 0xFF, 0x0C, 0xAE, 0x3F, 0x83, 0xF6, 0x97, 0x8A, 0xFC, 0xA5, 0xAC, 0xBF, 0x54, 0xFB, 0x2F, 0x50, 0xFC, 0xA3, 0x14, 0x2C, 0xFE, 0x50, 0xD6, + 0xDF, 0x40, 0xE4, 0xAF, 0xA4, 0xFA, 0x37, 0x64, 0xB3, 0xC3, 0x1A, 0xBB, 0x76, 0xCA, 0xA4, 0xB6, 0x3F, 0x98, 0x40, 0x5A, 0x2A, 0x16, 0xDB, 0x02, + 0xC1, 0x06, 0xB9, 0x62, 0xB1, 0x5A, 0xAE, 0x71, 0xB0, 0x72, 0xAE, 0x87, 0xB0, 0x09, 0x5C, 0xB1, 0x07, 0xB0, 0x8D, 0x08, 0xAE, 0x0A, 0x30, 0x11, + 0xB5, 0x84, 0x12, 0x30, 0x1F, 0x29, 0x68, 0xBD, 0x98, 0x18, 0x5A, 0x36, 0x66, 0x5B, 0xBB, 0x82, 0xF9, 0xD7, 0xB6, 0x61, 0x6B, 0xB4, 0x7F, 0x58, + 0xE8, 0xB5, 0x06, 0xD6, 0xC6, 0xF5, 0x09, 0x16, 0xCA, 0x95, 0x00, 0xEC, 0x99, 0x6B, 0x01, 0xD8, 0xEC, 0x15, 0x0D, 0x93, 0xD2, 0xD5, 0x00, 0x26, + 0xA0, 0xB6, 0x12, 0x73, 0xC5, 0xDB, 0x2B, 0xE6, 0x84, 0x36, 0x04, 0x53, 0x44, 0x9B, 0xC7, 0xD4, 0x6B, 0x45, 0x60, 0x47, 0x5C, 0xF5, 0xB0, 0x28, + 0xAE, 0x85, 0x58, 0xC3, 0x2B, 0x11, 0x6C, 0x91, 0x6B, 0x17, 0xAC, 0x97, 0x6B, 0x22, 0xB3, 0xCE, 0xF5, 0x0B, 0x13, 0x4C, 0x6B, 0x87, 0x79, 0xA7, + 0x55, 0xC4, 0x5C, 0xD4, 0x2E, 0x99, 0x52, 0x2D, 0x18, 0x33, 0xAC, 0x95, 0xC2, 0x32, 0xB8, 0x86, 0x61, 0x99, 0x57, 0x38, 0x6C, 0xDC, 0x35, 0x10, + 0x1B, 0xBD, 0xE2, 0xC0, 0xE6, 0xB8, 0x0A, 0x32, 0x15, 0xAF, 0x3A, 0x98, 0x3B, 0x5A, 0x2A, 0xCC, 0x2A, 0x2D, 0x1B, 0xA6, 0x63, 0x2B, 0x88, 0x39, + 0xA8, 0xA5, 0x5B, 0x62, 0xFA, 0x31, 0xB5, 0x83, 0xB0, 0x59, 0xAD, 0x0E, 0x26, 0x2C, 0x92, 0xA6, 0x6B, 0x16, 0xB6, 0xC2, 0x95, 0x0C, 0x5B, 0x76, + 0x55, 0x81, 0xDD, 0xBB, 0xBE, 0xC0, 0xC2, 0xB8, 0x3E, 0x99, 0x6E, 0x7B, 0xC8, 0x14, 0xA0, 0x4D, 0xC2, 0x54, 0xD2, 0x86, 0x61, 0x8A, 0x69, 0x23, + 0x99, 0x7A, 0x5A, 0x31, 0xD8, 0x47, 0xAD, 0x1E, 0xEC, 0x9E, 0xEB, 0x23, 0xEC, 0xE4, 0xD5, 0x01, 0xF6, 0xC1, 0x75, 0x03, 0x96, 0xC3, 0xF5, 0x04, + 0x76, 0xEE, 0xDA, 0x81, 0xE9, 0xA1, 0x75, 0x32, 0xE9, 0x5A, 0x16, 0xCC, 0x18, 0x72, 0x0C, 0x79, 0xD1, 0x7A, 0x56, 0x80, 0x55, 0xD2, 0x75, 0x92, + 0x35, 0xDB, 0x1A, 0xB0, 0xA4, 0xD4, 0x56, 0x5A, 0x5A, 0xB5, 0x57, 0x4B, 0x00, 0xDA, 0x10, 0x4B, 0xBC, 0x36, 0x6F, 0x19, 0xE2, 0x4A, 0xB8, 0xCC, + 0xBB, 0xEA, 0x59, 0x09, 0xAF, 0x85, 0xD6, 0x21, 0x57, 0x22, 0x56, 0x2E, 0xD7, 0x2E, 0xD6, 0x39, 0x57, 0x3A, 0x56, 0x2F, 0x6D, 0x6D, 0xAD, 0xD3, + 0xDA, 0x59, 0x26, 0x6A, 0x15, 0x2D, 0x17, 0xB4, 0xCB, 0xE5, 0x57, 0x0B, 0xB6, 0x74, 0x6B, 0xDF, 0x96, 0x60, 0xD7, 0x30, 0xEB, 0xDB, 0x15, 0xCE, + 0x0A, 0x78, 0x0D, 0xB4, 0x22, 0x5E, 0x71, 0x58, 0xA5, 0x5C, 0xA7, 0x58, 0x73, 0xB4, 0x6A, 0xAC, 0x8A, 0x2D, 0x95, 0xE5, 0x9A, 0x96, 0xCD, 0xF2, + 0x80, 0x56, 0xD0, 0x52, 0x90, 0xB2, 0x6D, 0xB4, 0xB4, 0x6C, 0x5B, 0xAC, 0xA5, 0xD5, 0xD2, 0x35, 0x03, 0x93, 0x15, 0xEB, 0x9A, 0x65, 0x2D, 0x71, + 0x25, 0xB3, 0xA6, 0xB8, 0xAA, 0xB0, 0xBA, 0xB8, 0xBE, 0xB0, 0xEE, 0xB5, 0x47, 0xAC, 0x30, 0xDA, 0xC3, 0x25, 0x92, 0x36, 0xC9, 0x92, 0xB1, 0x0D, + 0xB3, 0xCC, 0xD3, 0x46, 0x2E, 0x1B, 0xB5, 0x8E, 0x96, 0x91, 0xD7, 0xA9, 0xA5, 0x23, 0x2B, 0x53, 0x1A, 0xAE, 0x1B, 0xAC, 0xCD, 0xEB, 0x09, 0xAB, + 0xF4, 0x1A, 0x5B, 0xE7, 0x5A, 0xE7, 0x52, 0x6E, 0x59, 0x2C, 0x7B, 0xB4, 0x3E, 0x96, 0x57, 0x5A, 0x2C, 0x4B, 0x37, 0xED, 0x9B, 0x25, 0x96, 0x6B, + 0xA1, 0xE5, 0x9B, 0xEB, 0xA3, 0xB5, 0xD0, 0xD5, 0x90, 0x55, 0x82, 0xD3, 0xD5, 0xEB, 0xC9, 0xAA, 0x75, 0xDD, 0xB2, 0x9E, 0x5D, 0xD1, 0xAC, 0x5C, + 0x6D, 0xD7, 0x32, 0xAD, 0x55, 0x5B, 0xF6, 0xB5, 0x3E, 0x68, 0x1F, 0x74, 0x87, 0x2A, 0x84, 0xB4, 0x10, 0xEB, 0x10, 0x7A, 0x8B, 0x95, 0x0B, 0x0D, + 0xC9, 0x64, 0x0E, 0x6D, 0x06, 0x11, 0x9D, 0xC8, 0x90, 0x53, 0xD4, 0xD7, 0x4D, 0x2C, 0xDD, 0xFE, 0x65, 0x85, 0xEF, 0x66, 0x7D, 0xA3, 0x61, 0x2B, + 0x20, 0x7D, 0xC3, 0x8A, 0x48, 0x47, 0xB1, 0x4A, 0xA1, 0xD1, 0x58, 0xD3, 0xD0, 0x60, 0x80, 0xE8, 0xB4, 0x35, 0xEF, 0x01, 0x36, 0x94, 0xA7, 0xB0, + 0x6C, 0x3F, 0x62, 0x49, 0xE2, 0xAD, 0x15, 0x8B, 0x26, 0x61, 0x2D, 0x41, 0x07, 0x60, 0x4D, 0x41, 0xB7, 0xB0, 0xBA, 0xA0, 0x13, 0xAD, 0x46, 0xE8, + 0x17, 0x4B, 0x30, 0xF4, 0x91, 0x25, 0x92, 0x2F, 0x2E, 0x19, 0x8F, 0x67, 0x99, 0xE7, 0x8C, 0x96, 0x8D, 0xDE, 0xC4, 0x60, 0xE9, 0xA0, 0xC1, 0x1B, + 0x59, 0x1D, 0xD1, 0x22, 0x1A, 0x58, 0xDF, 0xA6, 0x68, 0x31, 0xAB, 0x06, 0x5A, 0xCE, 0xDA, 0xA4, 0x05, 0xAD, 0x52, 0xFA, 0xC4, 0x72, 0x07, 0x1D, + 0x2F, 0xE5, 0xF3, 0x58, 0xF6, 0xB8, 0x95, 0xE5, 0x95, 0x33, 0x37, 0x92, 0xB7, 0x62, 0xC0, 0x82, 0x55, 0x8B, 0x36, 0xB3, 0x9E, 0xD1, 0x4F, 0xAB, + 0x4B, 0xA3, 0x2D, 0xD3, 0xCE, 0xB7, 0xEC, 0x3B, 0xBD, 0x8C, 0xF1, 0x25, 0x4B, 0x26, 0x1F, 0xB3, 0x24, 0x73, 0x26, 0x56, 0x3E, 0x27, 0x63, 0x2D, + 0x43, 0xF3, 0xB1, 0xF6, 0xA1, 0x21, 0x59, 0x17, 0xA0, 0x01, 0x80, 0x60, 0x35, 0x41, 0x45, 0x77, 0xB0, 0x7A, 0xA0, 0x59, 0x44, 0x58, 0xC6, 0x90, + 0xF0, 0x0F, 0xCB, 0xB1, 0xD7, 0x2D, 0x89, 0x4C, 0x9F, 0x1D, 0x7A, 0x10, 0xBE, 0x65, 0xC3, 0x64, 0xE6, 0x13, 0x4E, 0x8E, 0x7B, 0x06, 0xCA, 0xD1, + 0x27, 0x0E, 0x39, 0xC7, 0x73, 0x8A, 0xAC, 0xF8, 0xCC, 0x51, 0xAF, 0x79, 0x2A, 0xD6, 0x54, 0xB2, 0x6B, 0x6A, 0x36, 0xD9, 0x03, 0x6A, 0x41, 0x59, + 0x65, 0xBD, 0x94, 0x6D, 0x6B, 0xAC, 0x2C, 0x93, 0xDC, 0xF4, 0xCC, 0x9A, 0x54, 0xA1, 0x6C, 0xC4, 0xDE, 0x33, 0x05, 0x8C, 0x27, 0x0C, 0x24, 0x4F, + 0x21, 0x87, 0x5D, 0x92, 0xD8, 0x09, 0xC3, 0x28, 0x32, 0x3B, 0xE5, 0x28, 0x8B, 0x61, 0x8F, 0xDB, 0x64, 0xF4, 0xC1, 0x0E, 0x0E, 0x57, 0xE6, 0x9E, + 0x8F, 0xF2, 0x33, 0x90, 0x93, 0x85, 0x8F, 0x33, 0x2C, 0xD9, 0xBE, 0x37, 0xD9, 0x47, 0x19, 0xCE, 0xD3, 0x90, 0x8C, 0xC0, 0xF3, 0x49, 0x86, 0x7A, + 0x6E, 0xC9, 0x5C, 0x4F, 0x34, 0x39, 0xED, 0xC9, 0x55, 0x77, 0x65, 0xD3, 0x6A, 0xB5, 0x6C, 0x5F, 0xED, 0x97, 0x8D, 0x51, 0xCB, 0xC8, 0x32, 0xA9, + 0xAF, 0xD9, 0x19, 0x72, 0x99, 0x6C, 0x13, 0xD9, 0xC9, 0xD3, 0x8C, 0xDC, 0xF8, 0xF4, 0x23, 0xFF, 0x79, 0x82, 0x88, 0x24, 0xB7, 0x49, 0x7A, 0x46, + 0x90, 0x25, 0x3C, 0x65, 0x49, 0x35, 0x3D, 0x29, 0x2B, 0xA1, 0xAE, 0xCA, 0x8E, 0xD5, 0x8C, 0x58, 0x46, 0x6C, 0x87, 0x24, 0x84, 0x7A, 0xF5, 0x08, + 0x9A, 0x84, 0x8C, 0x82, 0x0E, 0x40, 0x36, 0xA4, 0x5B, 0xC8, 0x45, 0xE8, 0x44, 0xD9, 0x0B, 0xFD, 0x45, 0x36, 0x72, 0x3B, 0x32, 0x98, 0x83, 0xA9, + 0xEF, 0xFC, 0x4E, 0xBD, 0xE8, 0x8B, 0x6A, 0x22, 0x52, 0x97, 0x22, 0x55, 0x87, 0x3D, 0xAC, 0x86, 0xC3, 0x80, 0xF6, 0xD3, 0x40, 0x66, 0x4E, 0x12, + 0x93, 0x1B, 0x21, 0x28, 0xA7, 0xD0, 0x21, 0xC3, 0xA0, 0x22, 0xEF, 0xA8, 0xAB, 0x74, 0xA9, 0x62, 0xC7, 0x91, 0xD1, 0xA0, 0xDA, 0x01, 0x53, 0x8D, + 0x41, 0xF2, 0xCD, 0x40, 0xD4, 0xC2, 0x0E, 0xB3, 0x2B, 0x4F, 0xF9, 0xF1, 0x50, 0x0E, 0xE4, 0x0A, 0xA0, 0xE4, 0x4C, 0x4C, 0x3D, 0x86, 0x7E, 0x54, + 0xEF, 0xA9, 0x32, 0xF9, 0xE6, 0x01, 0xCD, 0x07, 0x82, 0xCC, 0x81, 0xEE, 0x20, 0xCF, 0xD1, 0x4E, 0xD9, 0xC3, 0x3D, 0xD4, 0x74, 0xA7, 0xAB, 0xE7, + 0xCF, 0xB5, 0xF5, 0xD6, 0x3A, 0xCB, 0xB3, 0xD4, 0x3F, 0xE8, 0xC7, 0x7A, 0x00, 0x1A, 0x2B, 0xC3, 0xA1, 0xE3, 0xC8, 0x08, 0xD0, 0x87, 0x64, 0x00, + 0xA0, 0x68, 0x13, 0x07, 0x64, 0x2E, 0xBA, 0x4B, 0x66, 0x81, 0xE0, 0x31, 0x10, 0xD4, 0x17, 0x7E, 0xA1, 0x5E, 0xBB, 0x95, 0x2B, 0x14, 0x16, 0x1B, + 0xD9, 0x0E, 0xB4, 0x13, 0x0F, 0x04, 0x1F, 0x94, 0x77, 0x2D, 0x21, 0x89, 0x1E, 0x59, 0x59, 0xC8, 0xE2, 0xA8, 0x50, 0xE0, 0xC5, 0x9E, 0xA8, 0x23, + 0x60, 0x8B, 0x69, 0x48, 0x5E, 0xC6, 0x1A, 0x79, 0xE6, 0x0C, 0x61, 0x04, 0x19, 0xA5, 0x91, 0x9C, 0x2F, 0x0C, 0x1E, 0x1D, 0x3D, 0xF6, 0x79, 0xF4, + 0x26, 0x81, 0x29, 0x37, 0x93, 0x1C, 0xB8, 0x51, 0xA0, 0xA1, 0xCD, 0xD5, 0x06, 0xC7, 0xAF, 0x26, 0x9B, 0x11, 0x27, 0xD6, 0xC0, 0x49, 0xD5, 0xA4, + 0x88, 0xD3, 0xF8, 0x9D, 0x40, 0x03, 0x83, 0xC7, 0x19, 0x1A, 0x31, 0x01, 0x42, 0xE4, 0x46, 0x28, 0x3C, 0xCC, 0x74, 0xE9, 0x51, 0x26, 0x8D, 0x15, + 0x32, 0x76, 0xAE, 0x8C, 0x39, 0xA0, 0x8D, 0x18, 0x46, 0x6E, 0xC4, 0x30, 0x11, 0x0A, 0x33, 0x25, 0xC9, 0x58, 0xF5, 0x66, 0x11, 0xC3, 0x23, 0x98, + 0x10, 0x85, 0xBE, 0xB8, 0x19, 0xF2, 0x12, 0x80, 0x53, 0x97, 0xE8, 0x13, 0x97, 0x17, 0x5A, 0x5B, 0x76, 0x6C, 0x69, 0x49, 0x72, 0x86, 0xBD, 0x82, + 0x36, 0x00, 0x97, 0xAD, 0x2C, 0x8F, 0x7C, 0x58, 0xEA, 0xA0, 0x5D, 0x09, 0x94, 0x66, 0x65, 0x11, 0x9F, 0x95, 0xA5, 0xBE, 0x2A, 0x0D, 0x68, 0x2A, + 0x15, 0x62, 0x28, 0x6E, 0x58, 0x32, 0xE5, 0x8C, 0x89, 0x14, 0xE7, 0x98, 0x28, 0x7D, 0x64, 0xA0, 0x00, 0x5E, 0x9F, 0xCC, 0xE0, 0xE3, 0x24, 0x47, + 0x99, 0x26, 0x47, 0xCC, 0x5C, 0xAB, 0xF4, 0x71, 0xB3, 0x80, 0x92, 0x3D, 0x91, 0x92, 0x20, 0x40, 0x85, 0x24, 0x52, 0x04, 0x24, 0x08, 0x3C, 0x1C, + 0xA9, 0xFA, 0x60, 0x24, 0x2B, 0x42, 0xA2, 0x06, 0x09, 0x45, 0xCC, 0x3E, 0x10, 0x99, 0x1B, 0x1B, 0xC2, 0x57, 0x27, 0x64, 0xB6, 0x0B, 0x22, 0x63, + 0x0A, 0x04, 0x08, 0xF5, 0x87, 0x93, 0x1F, 0x59, 0xEB, 0x16, 0x20, 0x3E, 0x18, 0xAB, 0x3D, 0xE8, 0x60, 0x21, 0x79, 0xE8, 0x09, 0xB2, 0xA3, 0x90, + 0x10, 0x1D, 0x90, 0xB4, 0xE8, 0x80, 0xDA, 0x92, 0x23, 0x82, 0x1C, 0x1C, 0x17, 0x20, 0x11, 0x41, 0x24, 0x70, 0xA3, 0x4D, 0x01, 0x1B, 0x7A, 0x35, + 0x00, 0x13, 0x34, 0x69, 0xC8, 0x33, 0x68, 0x44, 0x32, 0x7E, 0xC4, 0x68, 0xA2, 0x80, 0xD6, 0xE2, 0x95, 0x75, 0x62, 0x24, 0xB2, 0xE5, 0x63, 0x21, + 0x6B, 0xD1, 0xE2, 0x5D, 0xE5, 0x11, 0x31, 0x77, 0x24, 0x44, 0x20, 0x43, 0xCD, 0x79, 0x91, 0xE9, 0xA9, 0x59, 0x07, 0x46, 0xA4, 0x35, 0x48, 0x44, + 0x52, 0xD9, 0xB4, 0x24, 0x64, 0x07, 0xB6, 0xB2, 0x4E, 0x49, 0xA7, 0x3C, 0xA1, 0x24, 0x53, 0x76, 0x70, 0xC2, 0x81, 0xC1, 0xE3, 0x9B, 0xA6, 0xCF, + 0xA9, 0x63, 0xDD, 0x22, 0xE0, 0x98, 0x98, 0x45, 0xC8, 0x33, 0x54, 0x5A, 0x75, 0x96, 0x6A, 0x49, 0x89, 0x4A, 0xCC, 0x8A, 0x6A, 0xAD, 0x52, 0x22, + 0xF4, 0x2A, 0x11, 0x31, 0x44, 0xA2, 0x62, 0x5E, 0x22, 0x95, 0x23, 0x61, 0xE2, 0x03, 0xC9, 0x21, 0x89, 0x53, 0x95, 0x0C, 0xA9, 0x28, 0x14, 0xA1, + 0x2A, 0xE1, 0x85, 0xEA, 0x4A, 0x55, 0xD7, 0xF3, 0xC5, 0x5B, 0x1F, 0x47, 0x2B, 0x3B, 0xA5, 0x53, 0xF9, 0xA6, 0x0C, 0xFB, 0x20, 0x1C, 0x93, 0x7A, + 0x4F, 0x6F, 0x42, 0x2C, 0xB4, 0x91, 0xA0, 0x03, 0x9A, 0xD9, 0xD3, 0x07, 0x30, 0x46, 0x87, 0x53, 0x19, 0x84, 0x78, 0x38, 0x24, 0x27, 0xAA, 0x54, + 0x16, 0x2A, 0xE5, 0x94, 0x6B, 0x4A, 0xB3, 0x49, 0x2D, 0x85, 0x91, 0x9E, 0x1C, 0x9F, 0xCE, 0x94, 0x24, 0x01, 0x15, 0xDA, 0xD5, 0xAD, 0x96, 0xFA, + 0x80, 0x68, 0xB9, 0x66, 0x81, 0x9D, 0x98, 0xC6, 0xA8, 0x41, 0x61, 0x9A, 0x69, 0xA8, 0x4A, 0xE8, 0xC2, 0xBD, 0xC2, 0x1F, 0x2A, 0x10, 0xAF, 0x29, + 0x12, 0x07, 0xA8, 0xED, 0x16, 0x0C, 0x82, 0x12, 0x82, 0xBA, 0x81, 0xF6, 0x29, 0x9B, 0x3C, 0xF4, 0x00, 0x01, 0xD1, 0x49, 0xD0, 0x93, 0x72, 0x3F, + 0x2B, 0x3D, 0x1B, 0x49, 0x59, 0x6C, 0x02, 0xF8, 0xD6, 0x64, 0x31, 0xC9, 0x0B, 0x29, 0x2C, 0x45, 0xC9, 0x33, 0x2E, 0x6B, 0x21, 0xC7, 0x27, 0x84, + 0x30, 0x30, 0xB5, 0x80, 0xC2, 0x18, 0x7B, 0x63, 0x6B, 0xDA, 0xCA, 0x12, 0x4A, 0x08, 0x49, 0xB2, 0x19, 0x06, 0xA3, 0xE4, 0xA8, 0xC5, 0x38, 0xF4, + 0x40, 0xD5, 0x1A, 0xF8, 0xD2, 0x11, 0x6C, 0xE2, 0xE3, 0x8E, 0xEB, 0xDA, 0x9B, 0x8E, 0x3F, 0xB2, 0x4A, 0x36, 0xAA, 0x9A, 0x74, 0xA4, 0xA2, 0x0C, + 0x1D, 0x78, 0x12, 0x90, 0x40, 0x87, 0x2B, 0x08, 0x48, 0x89, 0x52, 0x43, 0x20, 0xAD, 0x38, 0x74, 0x39, 0xD4, 0x92, 0x72, 0xA7, 0xC9, 0x4E, 0x9F, + 0x80, 0x8F, 0xD2, 0x18, 0xAE, 0x8A, 0xCE, 0xF6, 0x25, 0x11, 0x4A, 0x0D, 0x77, 0x64, 0x61, 0x4D, 0x20, 0xEC, 0x98, 0x1A, 0xD1, 0x64, 0xE0, 0x43, + 0x2F, 0xCD, 0xCA, 0x10, 0x4A, 0x00, 0x94, 0x4A, 0xBC, 0xA8, 0xF2, 0x0E, 0x8F, 0x29, 0x5B, 0xDB, 0xD8, 0x08, 0x47, 0x2C, 0x16, 0x4D, 0x67, 0x6C, + 0xD4, 0xAC, 0x5C, 0xC6, 0x80, 0x32, 0x4E, 0x09, 0xA2, 0x4B, 0xA5, 0x4A, 0x1E, 0x38, 0x44, 0xBD, 0x0A, 0xCC, 0x08, 0xA1, 0x5F, 0xDC, 0xC8, 0x90, + 0xE0, 0xE9, 0xC7, 0x1F, 0xE7, 0xD4, 0x4E, 0x72, 0x91, 0xA5, 0xAD, 0x3B, 0x25, 0x61, 0xD4, 0x56, 0xAB, 0xCA, 0x2F, 0x92, 0xA1, 0x52, 0x0A, 0xBC, + 0x4A, 0x7B, 0x02, 0xA5, 0x12, 0x5C, 0x3B, 0x22, 0x21, 0xB9, 0x87, 0xC9, 0x43, 0x1F, 0xCA, 0x40, 0x82, 0xE1, 0xF8, 0x30, 0x24, 0xFB, 0xF5, 0xA6, + 0xE6, 0x71, 0x4A, 0x89, 0x42, 0xE9, 0xA6, 0x34, 0xA1, 0x1C, 0x1B, 0xC2, 0x48, 0x6E, 0xC3, 0x8E, 0x18, 0x72, 0x8C, 0x17, 0x42, 0x9A, 0x6C, 0x4C, + 0x57, 0xCE, 0x82, 0x11, 0x84, 0x03, 0xD9, 0x2F, 0x50, 0xDE, 0xE8, 0x29, 0x8D, 0xCA, 0x44, 0xCF, 0x3F, 0x4E, 0x11, 0xEC, 0xAC, 0x59, 0x03, 0x42, + 0x11, 0x0A, 0x3F, 0x54, 0xF4, 0xF4, 0x96, 0x79, 0x93, 0xD8, 0xB4, 0x00, 0xC3, 0xBB, 0x12, 0x74, 0xE4, 0xCD, 0x8E, 0x43, 0x9B, 0x30, 0x80, 0x74, + 0x74, 0x84, 0xF6, 0xB4, 0x80, 0x9C, 0x0B, 0x74, 0xB3, 0x80, 0x94, 0xA6, 0x8C, 0x2C, 0x72, 0x12, 0xD2, 0x64, 0x9D, 0x3C, 0xCE, 0x65, 0x67, 0x0C, + 0x65, 0x59, 0xE0, 0x26, 0x95, 0x61, 0x0C, 0x4C, 0x1F, 0x0B, 0x1C, 0xF2, 0x2C, 0x10, 0x02, 0x42, 0xF6, 0x48, 0x03, 0x2D, 0x8D, 0x5F, 0x20, 0x0B, + 0x8D, 0x1C, 0x7E, 0xA8, 0x43, 0x54, 0xF2, 0x88, 0xC1, 0xDD, 0xE0, 0x55, 0x88, 0x0D, 0x32, 0xC6, 0x09, 0xB2, 0x03, 0x5E, 0x40, 0x22, 0x70, 0x46, + 0x16, 0xE9, 0xD9, 0x74, 0xC9, 0xAA, 0x0E, 0x9B, 0x18, 0x04, 0x20, 0x0B, 0x91, 0xA4, 0xE9, 0xA6, 0x84, 0x0C, 0x45, 0x98, 0x66, 0xA0, 0x18, 0x32, + 0x17, 0x91, 0x4D, 0x19, 0x4B, 0x1A, 0x91, 0x38, 0x6A, 0x3A, 0x19, 0xD3, 0xB8, 0x45, 0x0F, 0xE4, 0x99, 0x3D, 0x74, 0x1E, 0x41, 0x2E, 0xF0, 0x68, + 0x68, 0x04, 0x48, 0xA3, 0x33, 0x5D, 0x20, 0x30, 0x37, 0x0F, 0x23, 0x85, 0x8C, 0x6C, 0xC8, 0x10, 0xAD, 0x5C, 0x2F, 0xDD, 0x4A, 0x30, 0x65, 0x05, + 0x2E, 0x6F, 0x57, 0xD0, 0xAC, 0x48, 0x22, 0x32, 0x06, 0x8D, 0x06, 0x97, 0xB7, 0xAF, 0xED, 0x25, 0xE5, 0x8E, 0x2B, 0x8E, 0xFA, 0x47, 0xE4, 0x92, + 0x3C, 0x34, 0x1D, 0x0C, 0xD1, 0xC7, 0x33, 0x0F, 0x4F, 0x28, 0xD6, 0xB9, 0xAE, 0x0C, 0xCA, 0x2C, 0x25, 0xD6, 0x0F, 0xBD, 0x90, 0xD3, 0x53, 0x93, + 0xC5, 0x3F, 0x89, 0x64, 0xFB, 0x23, 0xE2, 0xF9, 0xA8, 0x86, 0x1A, 0x25, 0xBD, 0x48, 0x8C, 0x26, 0x14, 0x3E, 0xAD, 0x18, 0x90, 0x20, 0xF2, 0x14, + 0xC1, 0x54, 0xD1, 0xE5, 0x19, 0x40, 0xDC, 0x21, 0x9D, 0xA9, 0x58, 0x76, 0x49, 0x1D, 0x44, 0x2E, 0x6B, 0x26, 0x8A, 0x8B, 0xE0, 0x9D, 0x44, 0x63, + 0x75, 0x74, 0x7C, 0x70, 0xB4, 0x60, 0x11, 0x95, 0x07, 0x5F, 0x07, 0x78, 0x3C, 0x3D, 0xB8, 0x79, 0x59, 0x79, 0xDA, 0xC1, 0x26, 0xDA, 0x30, 0x05, + 0xE1, 0xC2, 0x00, 0x09, 0x81, 0xF1, 0x08, 0xFC, 0xD2, 0xD1, 0x94, 0x01, 0x0B, 0x0E, 0x0C, 0x56, 0x2A, 0x78, 0xC1, 0x99, 0x72, 0xE1, 0x8C, 0x81, + 0xAE, 0x04, 0xD4, 0x00, 0xB2, 0x90, 0x55, 0x10, 0xE8, 0xA9, 0x34, 0xCB, 0x82, 0xED, 0xE7, 0x02, 0x04, 0xB2, 0x70, 0x9C, 0x2C, 0x13, 0x8A, 0xDE, + 0xD2, 0xAA, 0x25, 0x40, 0xE1, 0x0A, 0x39, 0xE1, 0x0F, 0xC1, 0x55, 0xA3, 0x02, 0x56, 0x37, 0x2E, 0x54, 0x19, 0x90, 0x60, 0xCA, 0x9A, 0x84, 0xED, + 0xC5, 0xE6, 0xA5, 0xC2, 0x62, 0x23, 0xF4, 0x60, 0x0C, 0x03, 0xA5, 0x15, 0x53, 0x78, 0x6D, 0x0D, 0x26, 0xD0, 0x60, 0x50, 0xF8, 0x27, 0x45, 0x62, + 0x99, 0x58, 0x63, 0x81, 0x8A, 0xC6, 0xA8, 0xC5, 0x27, 0x0A, 0x0E, 0xFF, 0x24, 0x88, 0x24, 0x00, 0xC1, 0x12, 0x49, 0x88, 0xA2, 0x40, 0x5D, 0x81, + 0xA2, 0x9A, 0xB4, 0xE7, 0x08, 0xD9, 0x23, 0x18, 0x50, 0xC7, 0x0C, 0x31, 0x9B, 0xA8, 0xE7, 0x9A, 0x37, 0x1E, 0x4F, 0x35, 0x60, 0x87, 0x69, 0xFA, + 0x64, 0x34, 0x71, 0x49, 0xCE, 0xCC, 0xE8, 0x41, 0x80, 0x34, 0x4C, 0xE3, 0x93, 0x2B, 0x66, 0x7A, 0x44, 0x96, 0x41, 0xE3, 0x86, 0x4C, 0x16, 0x47, + 0x92, 0x63, 0x02, 0x49, 0x8A, 0x89, 0x94, 0x0B, 0xF3, 0x38, 0x85, 0x71, 0x52, 0x70, 0xF1, 0x90, 0x9A, 0xEC, 0x22, 0x95, 0x0E, 0x30, 0x4B, 0x54, + 0xBE, 0x8C, 0x51, 0xF1, 0x52, 0x81, 0x4D, 0x17, 0x3C, 0x36, 0x5C, 0xCE, 0x04, 0x3D, 0xB7, 0x14, 0x0A, 0x82, 0x6A, 0x39, 0x71, 0x03, 0xDB, 0x65, + 0x4B, 0x19, 0x98, 0x37, 0x45, 0x27, 0x3D, 0x6F, 0x7A, 0xCF, 0x71, 0xB5, 0x3F, 0x3E, 0xD1, 0x99, 0x97, 0x81, 0xE0, 0x01, 0x00, 0xDE, 0xC5, 0xA1, + 0x48, 0x1E, 0xD1, 0x20, 0x44, 0xF5, 0x15, 0x38, 0xF4, 0x04, 0x0C, 0xBD, 0xD0, 0x11, 0x78, 0x7D, 0xC9, 0x13, 0xFA, 0xA0, 0xD7, 0x17, 0xF4, 0x07, + 0xDA, 0x53, 0x80, 0x6E, 0xC0, 0xB3, 0x2F, 0xE0, 0x7F, 0xEE, 0xE7, 0x7D, 0xF6, 0x5C, 0x4F, 0xE4, 0xF3, 0xE0, 0xF9, 0x98, 0xD7, 0xB9, 0x96, 0xD3, + 0xF9, 0x9C, 0x43, 0x5E, 0xCE, 0xE3, 0x1C, 0xCE, 0x1F, 0xFF, 0xE6, 0x6E, 0xDE, 0xE6, 0x56, 0xEE, 0x78, 0x3E, 0x67, 0xF3, 0x35, 0x57, 0xF3, 0x34, + 0x47, 0x73, 0x25, 0x3F, 0x73, 0x02, 0xDE, 0x78, 0x33, 0x2F, 0x73, 0x29, 0x27, 0xF3, 0x31, 0x17, 0x13, 0xE6, 0x8E, 0x67, 0x3C, 0x9C, 0x2F, 0x1E, + 0xCC, 0xBF, 0x78, 0x79, 0x9E, 0x77, 0x39, 0x97, 0x6F, 0xB9, 0x96, 0x63, 0x3F, 0xC0, 0xB6, 0x7A, 0x96, 0x63, 0xF9, 0x95, 0x5B, 0x79, 0x15, 0x2A, + 0x57, 0xFC, 0x94, 0x4B, 0x79, 0x94, 0x27, 0x87, 0x72, 0x27, 0x8F, 0xC7, 0xF3, 0x26, 0x07, 0x73, 0xC6, 0x5F, 0xCF, 0xE4, 0x4B, 0xAE, 0xE4, 0x49, + 0xAE, 0xE6, 0x48, 0x7E, 0xC4, 0xC8, 0x13, 0x5F, 0xE4, 0x44, 0x3E, 0xE4, 0x42, 0xA6, 0x1E, 0xE4, 0x40, 0xEE, 0xF8, 0xE3, 0xD6, 0x3D, 0xE2, 0x3E, + 0xDE, 0x63, 0xD7, 0x9D, 0xC7, 0x77, 0x3C, 0xC4, 0x75, 0x3C, 0xC7, 0x71, 0x20, 0xDA, 0xF8, 0x8D, 0xD7, 0x38, 0x8D, 0xCF, 0x78, 0xF5, 0x32, 0x62, + 0xD0, 0x17, 0xE0, 0xC5, 0x6D, 0x9C, 0xAA, 0xC5, 0x0D, 0x5F, 0x78, 0xC2, 0xE7, 0xF8, 0x20, 0xF5, 0x2E, 0x5E, 0xFD, 0xF4, 0xF3, 0x59, 0x7C, 0xEE, + 0xF2, 0xE3, 0x4B, 0x2F, 0xF8, 0xE8, 0x03, 0x0F, 0x78, 0x18, 0xFA, 0x1B, 0x37, 0xFE, 0xF7, 0x15, 0x7F, 0xEF, 0xF7, 0xBE, 0xF3, 0xDD, 0xFD, 0xF3, + 0x55, 0xDF, 0xBB, 0x8A, 0xA7, 0x08, 0xE0, 0x28, 0xAE, 0xF7, 0x6A, 0xDE, 0xF1, 0x7E, 0x67, 0xE2, 0x76, 0xAF, 0x3B, 0xDD, 0x23, 0xDC, 0xF9, 0xB9, + 0x9F, 0x68, 0x5E, 0xEE, 0x71, 0x87, 0xFB, 0xDB, 0xDD, 0x6E, 0xE2, 0x6D, 0x67, 0xFB, 0xDA, 0xD5, 0x9E, 0x76, 0xB4, 0x9F, 0x99, 0xBD, 0xEC, 0xD2, + 0x93, 0xBD, 0xC4, 0xDB, 0x4E, 0xE2, 0x36, 0x3E, 0xE2, 0x22, 0x1E, 0x02, 0x80, 0x83, 0xF8, 0xD8, 0x3F, 0xDC, 0x43, 0x87, 0xCF, 0x54, 0x97, 0xAF, + 0xE1, 0x62, 0x67, 0x5E, 0x7E, 0x18, 0x18, 0x06, 0xAF, 0x5F, 0x78, 0x17, 0xD7, 0xB7, 0xDE, 0xE0, 0xF2, 0xB3, 0x5E, 0x3D, 0xD6, 0xDB, 0x53, 0xF8, + 0x84, 0x5F, 0xDD, 0xEA, 0x55, 0x97, 0xF0, 0x08, 0xA7, 0x82, 0xF0, 0x07, 0x77, 0x50, 0xFD, 0xD4, 0x1B, 0x9C, 0xC1, 0xA5, 0xBE, 0x5C, 0xF0, 0xA8, + 0x04, 0x57, 0xE0, 0x74, 0xA8, 0xA7, 0x3B, 0x21, 0x30, 0xF0, 0x03, 0x4C, 0x5F, 0x3A, 0x70, 0x04, 0x0A, 0xF6, 0xA7, 0xBE, 0xF4, 0x07, 0xAF, 0x5F, + 0x68, 0x8B, 0x6B, 0x81, 0x8D, 0x9F, 0x69, 0x1D, 0x3A, 0x58, 0xE4, 0xF0, 0x45, 0xB6, 0x25, 0x99, 0xE5, 0xC5, 0x8B, 0x03, 0x57, 0x57, 0x73, 0x41, + 0x9D, 0xD5, 0x83, 0x43, 0x02, 0x0E, 0x50, 0x0C, 0xEE, 0x9E, 0x68, 0x88, 0x02, 0x52, 0x08, 0x10, 0x60, 0x60, 0x41, 0xF2, 0x2E, 0x04, 0x6D, 0x00, + 0x04, 0xC6, 0xB8, 0xA4, 0x30, 0x35, 0xE8, 0x6E, 0x34, 0x30, 0x32, 0x98, 0x71, 0xEC, 0x06, 0x2C, 0x57, 0x48, 0xAA, 0x2C, 0xB9, 0x4C, 0x39, 0x24, + 0x28, 0x57, 0x23, 0x9A, 0x54, 0x49, 0x2D, 0xB1, 0x22, 0x84, 0xE4, 0x8A, 0x41, 0xA2, 0x15, 0x9B, 0x21, 0x13, 0xF0, 0x80, 0x5C, 0xC5, 0xF0, 0x61, + 0xF5, 0x1D, 0x55, 0x31, 0x8E, 0x43, 0xDA, 0x98, 0x40, 0x9D, 0xF1, 0x2C, 0xBA, 0x2A, 0x86, 0xFE, 0x12, 0xB4, 0xCC, 0x2C, 0x79, 0x78, 0x95, 0x5B, + 0xF8, 0x76, 0xBD, 0xF7, 0x33, 0x01, 0x28, 0xDB, 0xF2, 0xF6, 0x24, 0xBB, 0x9F, 0x39, 0xF7, 0x5A, 0xF3, 0x5F, 0xE0, 0xFE, 0xC2, 0xC4, 0xCE, 0x36, + 0x5A, 0x53, 0x56, 0xA1, 0xED, 0xFA, 0x16, 0xB4, 0x0E, 0x21, 0xCE, 0x06, 0xA8, 0x09, 0x3F, 0x9E, 0x48, 0xEB, 0xD0, 0xC1, 0x94, 0xE7, 0xD1, 0x60, + 0xC7, 0x40, 0x86, 0x8F, 0xD1, 0xC0, 0xE8, 0x20, 0xC5, 0x99, 0x68, 0x99, 0xC1, 0x6A, 0xF4, 0x77, 0x06, 0x49, 0xB2, 0x34, 0x18, 0x89, 0xB0, 0xE6, + 0x40, 0xB2, 0x48, 0x64, 0x5D, 0x47, 0x96, 0x0A, 0x44, 0x09, 0x54, 0x8F, 0xF1, 0x60, 0xC7, 0xE1, 0x16, 0x90, 0x08, 0x7D, 0x4E, 0xE8, 0x52, 0xA2, + 0xB6, 0x87, 0xDC, 0x0B, 0xDA, 0x59, 0x01, 0x56, 0x58, 0xF0, 0x87, 0x55, 0xE5, 0xE7, 0x6A, 0x50, 0x91, 0x65, 0x80, 0x9D, 0x86, 0xBB, 0x41, 0x16, + 0xCE, 0x68, 0x99, 0xD1, 0x6A, 0x76, 0xCD, 0x06, 0x6A, 0xAE, 0x04, 0xD9, 0x8F, 0xD0, 0x6A, 0x42, 0x2D, 0xB9, 0x05, 0x0D, 0x58, 0x4F, 0x22, 0x34, + 0x28, 0xC5, 0x2A, 0x59, 0xA1, 0xDD, 0x90, 0xD6, 0xA1, 0x54, 0x94, 0x43, 0x8A, 0xC1, 0xC9, 0xAA, 0xCF, 0xA8, 0x75, 0xE8, 0xA0, 0x84, 0x58, 0x14, + 0x32, 0x0C, 0x16, 0x38, 0xC9, 0x6A, 0x32, 0x11, 0x4B, 0x12, 0x56, 0x3B, 0xE0, 0x70, 0xC4, 0x2D, 0x20, 0x4B, 0x41, 0x2E, 0x6B, 0x1D, 0x82, 0xDD, + 0xF3, 0xC4, 0xEC, 0xA6, 0xF6, 0xBC, 0x4C, 0x8C, 0x84, 0x94, 0xA3, 0x7B, 0x89, 0x71, 0xED, 0x33, 0x62, 0x0D, 0xC4, 0x96, 0x52, 0x7F, 0x3E, 0xC9, + 0x6C, 0x34, 0xE5, 0x7C, 0x08, 0x77, 0x8D, 0x48, 0x65, 0x88, 0x3B, 0x3B, 0x08, 0x1D, 0xE5, 0x54, 0xCE, 0xA6, 0xF3, 0xA8, 0xBC, 0x6B, 0x73, 0x29, + 0x6B, 0xFE, 0xA2, 0x51, 0x5E, 0x68, 0xDC, 0x55, 0x32, 0x6F, 0x13, 0x66, 0x07, 0x61, 0x32, 0xE1, 0x92, 0xE4, 0x56, 0x8C, 0x30, 0x89, 0x84, 0xD5, + 0xCC, 0x20, 0x53, 0x8B, 0x77, 0xA1, 0x75, 0x78, 0x45, 0xE6, 0x2F, 0x93, 0x6F, 0xC9, 0xE9, 0x6E, 0x4C, 0xB6, 0xF6, 0xF0, 0x1C, 0xB4, 0x0E, 0xA7, + 0x64, 0x30, 0xAF, 0x98, 0x12, 0x10, 0x08, 0xF0, 0x4A, 0x5A, 0x66, 0xA6, 0x06, 0x25, 0x49, 0xE1, 0x41, 0x72, 0x90, 0xE8, 0x68, 0xCA, 0x69, 0x11, + 0x03, 0xC5, 0x9A, 0x82, 0x5C, 0x02, 0x5C, 0x54, 0xA4, 0xF0, 0x9E, 0xA7, 0x8E, 0x4A, 0x63, 0x67, 0x41, 0xDD, 0x9C, 0x28, 0x3C, 0x71, 0x14, 0x38, + 0xBD, 0xB9, 0x12, 0x47, 0x05, 0x68, 0x6A, 0x10, 0xE0, 0x39, 0xF3, 0x44, 0x66, 0x0E, 0x18, 0x30, 0xE6, 0x5C, 0x18, 0xA3, 0x9C, 0x7C, 0x39, 0x11, + 0x2E, 0xDF, 0xC3, 0x3E, 0xC9, 0x61, 0x89, 0xA2, 0x50, 0xE5, 0x84, 0x53, 0x9C, 0x16, 0x40, 0x69, 0x32, 0x42, 0x67, 0xAD, 0xC9, 0x01, 0xBD, 0x25, + 0x51, 0x2E, 0x90, 0x3C, 0x30, 0xB1, 0x09, 0x6A, 0xC8, 0x67, 0x20, 0x4B, 0x33, 0x7C, 0x1C, 0xD0, 0xFB, 0xE0, 0x02, 0x07, 0xE0, 0x69, 0x98, 0x6C, + 0x40, 0x85, 0x60, 0x18, 0xE0, 0x13, 0x0D, 0x01, 0xC2, 0xFE, 0x0C, 0x6C, 0x01, 0x69, 0x6A, 0x7C, 0x9C, 0x96, 0x99, 0x04, 0x70, 0xDC, 0x0C, 0xC4, + 0xB1, 0x85, 0xA5, 0x6D, 0x01, 0x59, 0xDA, 0x19, 0x88, 0xCB, 0xF0, 0xD8, 0xFE, 0xC1, 0x15, 0x8F, 0xB0, 0x25, 0x06, 0x7E, 0x8E, 0x48, 0xEB, 0x70, + 0x8A, 0xCD, 0xF9, 0x80, 0x45, 0x5A, 0xE9, 0xC9, 0xDE, 0xA9, 0xF2, 0x41, 0xE7, 0xAA, 0xED, 0x63, 0xC0, 0x18, 0xA7, 0xE2, 0xDE, 0x04, 0x6C, 0x49, + 0x13, 0x84, 0xDF, 0x00, 0xA3, 0xC1, 0xE0, 0xD0, 0x05, 0xBB, 0xE6, 0x05, 0x1D, 0x1E, 0xA7, 0xD5, 0xDC, 0xE2, 0x3A, 0x30, 0xF8, 0xB1, 0x0B, 0xD7, + 0xA8, 0xA8, 0x53, 0xD0, 0x3A, 0x6C, 0xB0, 0x08, 0x2C, 0x01, 0x18, 0xD4, 0xD4, 0xD6, 0x11, 0x35, 0x20, 0x71, 0x3C, 0x5A, 0x87, 0x0C, 0x78, 0x7C, + 0xF1, 0xCB, 0x4B, 0x16, 0x85, 0x33, 0xD9, 0xE2, 0x8A, 0xFA, 0x71, 0x1A, 0x6D, 0x93, 0x8B, 0x4A, 0xAB, 0xE3, 0x80, 0x5F, 0x7F, 0x0D, 0x1C, 0xDC, + 0xF9, 0xD5, 0x5C, 0x3A, 0x3B, 0x00, 0x5A, 0x87, 0x0D, 0x00, 0xDD, 0xEA, 0x2B, 0xC2, 0xD7, 0x83, 0xA9, 0x14, 0x5F, 0x47, 0xF0, 0xE0, 0x59, 0xAD, + 0xA6, 0x0F, 0x44, 0x27, 0xBC, 0xB7, 0xC9, 0xB0, 0x66, 0xD7, 0xA1, 0x39, 0xA1, 0x63, 0xC9, 0x50, 0x05, 0xC1, 0x96, 0xFF, 0xC8, 0xD0, 0x43, 0x6A, + 0x3E, 0x75, 0x07, 0x8B, 0x3B, 0x4A, 0x33, 0x04, 0x52, 0x50, 0xBC, 0x13, 0x03, 0x18, 0x56, 0xA0, 0x87, 0xB8, 0x70, 0x55, 0x80, 0xF7, 0x2C, 0xAD, + 0xC3, 0x06, 0xD6, 0x0C, 0x09, 0xB4, 0x0E, 0x01, 0xF1, 0x8C, 0xB4, 0xCC, 0x60, 0x48, 0x84, 0x75, 0x3D, 0x1E, 0x0C, 0x75, 0x96, 0xB8, 0x6E, 0x06, + 0xC3, 0x98, 0x29, 0x26, 0x57, 0xD4, 0x3A, 0xB4, 0xE2, 0x7B, 0x0F, 0x0C, 0x3A, 0x96, 0x7E, 0x3E, 0x88, 0x61, 0xCA, 0x00, 0xCD, 0x55, 0x60, 0x40, + 0xE1, 0x60, 0x9F, 0x0D, 0x43, 0x9A, 0x54, 0x91, 0x8B, 0x69, 0x99, 0xD1, 0x6A, 0x9E, 0x0E, 0x58, 0xD6, 0x13, 0x0C, 0x58, 0x31, 0x86, 0x29, 0x0C, + 0x52, 0x09, 0xEA, 0x4F, 0x5A, 0xB0, 0xC0, 0x76, 0xF0, 0x68, 0xBF, 0x8C, 0xFE, 0x24, 0x91, 0x12, 0x52, 0x81, 0x4B, 0x11, 0x78, 0x10, 0x49, 0x2F, + 0x41, 0x4F, 0x04, 0xB4, 0x0D, 0x14, 0xF0, 0x1C, 0x7D, 0x9C, 0x28, 0x54, 0xD0, 0xF3, 0x29, 0x3C, 0x4A, 0x40, 0x75, 0x1E, 0x18, 0x5E, 0xCC, 0x96, + 0xD7, 0x93, 0x84, 0x3F, 0xF1, 0xBA, 0x4E, 0x31, 0x3A, 0x0E, 0x26, 0x36, 0xAA, 0xCC, 0x66, 0x78, 0xD1, 0x55, 0x4C, 0xA4, 0x47, 0xE8, 0x02, 0x2F, + 0x06, 0xAD, 0x52, 0xF4, 0x5E, 0x8A, 0x6F, 0xC2, 0xA0, 0x7B, 0xD8, 0x7B, 0xC0, 0x20, 0x00, 0x2B, 0xC6, 0x9B, 0xF0, 0x5A, 0xC3, 0x8A, 0x3A, 0x07, + 0x2F, 0xB1, 0xAB, 0x2A, 0xD8, 0x65, 0x60, 0xB0, 0xC2, 0xEA, 0x9A, 0x70, 0x40, 0xEB, 0xEA, 0x50, 0x96, 0x17, 0x05, 0xAB, 0xAA, 0x97, 0x02, 0xAD, + 0x43, 0x07, 0x73, 0xAE, 0xE8, 0x02, 0xA2, 0xEF, 0x39, 0x9F, 0xAE, 0xD9, 0xC0, 0xD5, 0xC7, 0x68, 0x35, 0xD3, 0x0A, 0x31, 0x22, 0x98, 0xFC, 0x93, + 0x0B, 0xD7, 0x56, 0xCE, 0x69, 0x6C, 0x01, 0x89, 0xEA, 0xF0, 0xF7, 0x02, 0x5F, 0x15, 0xE4, 0x4D, 0x68, 0xFD, 0x81, 0xE4, 0x85, 0x30, 0x2D, 0x33, + 0x14, 0x18, 0x33, 0x9C, 0xCB, 0x85, 0x9D, 0xB5, 0xA5, 0x94, 0x51, 0xE1, 0x5F, 0x2D, 0x05, 0xC1, 0x2B, 0xE5, 0x13, 0x09, 0x67, 0x42, 0xF8, 0x73, + 0xEA, 0x82, 0x14, 0x17, 0x15, 0xE5, 0x93, 0x43, 0xD2, 0x32, 0x13, 0x61, 0xCE, 0x01, 0x75, 0x15, 0xF0, 0xE4, 0xE5, 0x8F, 0x5D, 0x6D, 0xBA, 0xD6, + 0x70, 0x5D, 0x99, 0xAA, 0xF2, 0x2D, 0x6D, 0xB3, 0xD5, 0x72, 0x21, 0x08, 0x02, 0x5F, 0xD5, 0x6A, 0x76, 0xCD, 0x5B, 0x5C, 0x06, 0x04, 0xBB, 0x02, + 0x70, 0x6B, 0xFF, 0xE4, 0x61, 0xC5, 0xD9, 0x3D, 0xCC, 0xD9, 0xD5, 0xE4, 0xA2, 0x82, 0xA5, 0xA1, 0x2B, 0xEC, 0xEA, 0x4F, 0x86, 0x09, 0xD8, 0x0B, + 0x5D, 0x5B, 0x13, 0x9C, 0x74, 0x39, 0x75, 0x31, 0x75, 0xED, 0xEF, 0x51, 0xBA, 0x5D, 0xA4, 0x2A, 0xEC, 0xE4, 0xD5, 0x5F, 0x23, 0x2E, 0x45, 0x55, + 0xD8, 0xDF, 0xD9, 0x02, 0xE2, 0xF4, 0x7B, 0x61, 0xB2, 0x85, 0x42, 0xD9, 0x00, 0x0E, 0x0E, 0xAF, 0x2C, 0x9B, 0x50, 0x91, 0x3A, 0x7C, 0x0F, 0x2D, + 0x33, 0x4E, 0x55, 0x74, 0x1C, 0x38, 0x83, 0x18, 0x55, 0xA9, 0xC6, 0xD3, 0x2C, 0x03, 0x2B, 0x83, 0x00, 0x06, 0xDE, 0xBC, 0xE0, 0x32, 0x7C, 0x9A, + 0x8F, 0x42, 0xEB, 0x0F, 0xEE, 0x9C, 0x04, 0x57, 0x08, 0x07, 0x07, 0xDC, 0x82, 0x96, 0x19, 0x2E, 0xAC, 0x07, 0x59, 0x5C, 0x4F, 0x5B, 0x5C, 0x0A, + 0xA2, 0x1C, 0x81, 0x2D, 0xC9, 0x36, 0xC9, 0xE1, 0x6C, 0xB9, 0xB9, 0x6A, 0x7A, 0x17, 0x2E, 0x2D, 0x5B, 0x59, 0x18, 0x30, 0x7D, 0xC8, 0x16, 0x90, + 0xA6, 0xC3, 0x2D, 0x17, 0x51, 0x6B, 0x5E, 0x0E, 0x12, 0x7D, 0xC5, 0x56, 0x9F, 0x54, 0xDC, 0x4D, 0x68, 0x1D, 0x82, 0xDA, 0xBF, 0xB6, 0xBC, 0xB6, + 0xB8, 0xB6, 0x2C, 0x3C, 0x45, 0xD9, 0x52, 0xA0, 0x75, 0x58, 0x25, 0xF6, 0x46, 0x5A, 0x8A, 0x9A, 0xFC, 0x7C, 0x8F, 0x56, 0x1E, 0x29, 0x0E, 0x3F, + 0xA3, 0x65, 0xA6, 0x01, 0x98, 0x31, 0x5A, 0x8C, 0x0C, 0x22, 0xFC, 0xC9, 0xBF, 0x64, 0x73, 0x3D, 0xA0, 0x92, 0xA9, 0x0E, 0x47, 0x1A, 0x48, 0x8B, + 0xBC, 0x4C, 0xC8, 0x14, 0xE0, 0x63, 0x68, 0x1D, 0x4A, 0xBD, 0x07, 0x74, 0xDE, 0xA9, 0xD5, 0x5C, 0x7A, 0x1E, 0x00, 0xAD, 0x0F, 0x0C, 0xE8, 0xFC, + 0x8A, 0x6A, 0x49, 0x6B, 0x49, 0xEB, 0x10, 0x3A, 0x48, 0xFA, 0x2D, 0x00, 0x5A, 0x00, 0x82, 0x55, 0x6B, 0xC0, 0x12, 0x14, 0xA4, 0x3B, 0x3D, 0x7D, + 0x9F, 0xA0, 0x23, 0x7D, 0x01, 0x40, 0x4B, 0x50, 0xBF, 0x01, 0xFF, 0xEF, 0xC5, 0x1C, 0x23, 0xBB, 0x72, 0xEB, 0x0A, 0xA0, 0xA7, 0x03, 0xFF, 0x93, + 0x32, 0x6A, 0xB9, 0x74, 0x28, 0x3F, 0x7A, 0xEC, 0x40, 0xAE, 0x39, 0xC3, 0x58, 0x85, 0x49, 0x90, 0x1B, 0xD5, 0xC0, 0x67, 0x33, 0x0D, 0x00, 0x30, + 0x0B, 0x6D, 0x84, 0xDF, 0x02, 0x94, 0xA4, 0x43, 0x05, 0xC8, 0x39, 0xD3, 0x05, 0x4A, 0x91, 0x1B, 0x2D, 0x00, 0x53, 0x38, 0xE5, 0xDC, 0xD6, 0x48, + 0x7C, 0xC0, 0x60, 0xB5, 0xE0, 0xAF, 0xA0, 0xAC, 0x0D, 0xA0, 0x93, 0xC6, 0xCB, 0x93, 0x1E, 0x02, 0xC4, 0x2A, 0x9A, 0x24, 0xC2, 0x28, 0x30, 0xF8, + 0xB3, 0x98, 0x1A, 0x05, 0xB2, 0x73, 0x86, 0xCB, 0xF5, 0x07, 0x1D, 0xF3, 0xC5, 0x91, 0xC8, 0x50, 0xF5, 0x63, 0x45, 0x51, 0x3A, 0xF4, 0x47, 0x0E, + 0x01, 0x5A, 0x92, 0xE8, 0x38, 0x1C, 0x9C, 0xA5, 0x80, 0x2B, 0xF8, 0x50, 0x38, 0x9B, 0x6F, 0x6C, 0x20, 0xBC, 0x22, 0x48, 0x29, 0x80, 0xFA, 0xDB, + 0xA6, 0x4A, 0x8D, 0x10, 0x11, 0x0A, 0xD4, 0x27, 0x8F, 0x23, 0xA3, 0x62, 0xE2, 0x0E, 0x17, 0x82, 0x4F, 0xB9, 0xB2, 0x2F, 0x2D, 0xAC, 0xAB, 0xA9, + 0xA5, 0x81, 0x9F, 0xC2, 0x8E, 0xD4, 0x28, 0x51, 0xA1, 0x41, 0x7F, 0x20, 0xCB, 0x60, 0x9D, 0xFC, 0x90, 0x41, 0x7D, 0xF1, 0xD8, 0x3E, 0x70, 0x45, + 0xF8, 0x27, 0x1C, 0x80, 0x74, 0xE8, 0xCF, 0x23, 0x19, 0x2C, 0x48, 0x78, 0xC4, 0xA8, 0xFE, 0xD9, 0xB5, 0x8D, 0x00, 0x6B, 0xAF, 0x9A, 0x18, 0x11, + 0xD2, 0xC3, 0x86, 0x51, 0xF1, 0x48, 0x8F, 0x18, 0x55, 0x3E, 0x3A, 0x34, 0x31, 0x2C, 0xA9, 0xDF, 0xEA, 0x8A, 0xB2, 0x90, 0x8D, 0x58, 0x26, 0x3E, + 0x60, 0xC4, 0x7B, 0x4D, 0x33, 0xB1, 0x2C, 0xA9, 0xBF, 0xEA, 0x49, 0x92, 0x02, 0x83, 0xFE, 0x40, 0xB2, 0x21, 0xC3, 0x85, 0x0A, 0x93, 0x21, 0x3A, + 0x62, 0x10, 0x46, 0x5D, 0x3B, 0x37, 0x32, 0x0F, 0x19, 0xB2, 0xA8, 0xA2, 0x7E, 0x09, 0x4C, 0x0B, 0xD4, 0x83, 0xFA, 0xCC, 0x59, 0x23, 0x46, 0xCB, + 0x14, 0x26, 0x47, 0x84, 0xEC, 0x40, 0xA4, 0x41, 0xBF, 0x20, 0xF4, 0x9B, 0x4B, 0x13, 0xCB, 0xFA, 0x23, 0x44, 0x51, 0x42, 0x84, 0x87, 0x0D, 0x18, + 0x2E, 0x56, 0xA6, 0x3C, 0x61, 0x82, 0xC4, 0xC7, 0x8C, 0x79, 0x05, 0x9F, 0x8A, 0x36, 0x00, 0xB8, 0xA6, 0x7E, 0xAA, 0xA5, 0x45, 0x7F, 0xE2, 0x94, + 0xC1, 0x26, 0x41, 0xAC, 0xFE, 0xD9, 0xA9, 0x75, 0x80, 0x20, 0x15, 0x49, 0xF2, 0x0E, 0x9B, 0x2F, 0x4F, 0x76, 0x18, 0x42, 0xEB, 0xD4, 0x3C, 0x64, + 0x31, 0xF8, 0x1F, 0xEC, 0x44, 0x07, 0xCC, 0x51, 0xB3, 0x38, 0x25, 0x31, 0xC7, 0xA1, 0x34, 0x85, 0x89, 0x12, 0x1C, 0xB8, 0xAA, 0x14, 0xFC, 0x0E, + 0x36, 0xA3, 0x41, 0x7B, 0xD8, 0x2C, 0x56, 0x61, 0x62, 0x95, 0x31, 0x08, 0x98, 0x22, 0x8E, 0xAC, 0xC3, 0x16, 0x84, 0x7F, 0xCA, 0x69, 0xD2, 0x22, + 0x40, 0x90, 0x68, 0xC0, 0x58, 0x51, 0x62, 0xAD, 0xD1, 0x42, 0xEA, 0xCA, 0x91, 0x10, 0xAB, 0xE0, 0xE0, 0x6F, 0x60, 0x00, 0xD2, 0x21, 0x3F, 0x74, + 0xD0, 0x78, 0x51, 0x3C, 0xC2, 0x63, 0x06, 0xB5, 0xEF, 0xCE, 0xAD, 0x4C, 0x83, 0x85, 0x08, 0x0B, 0x7E, 0x06, 0x4C, 0x8E, 0x10, 0xF8, 0x23, 0xA7, + 0x11, 0x1B, 0xA5, 0x88, 0x8E, 0xC3, 0xF9, 0xC5, 0x8B, 0x2B, 0xE3, 0xD0, 0x15, 0xE1, 0x97, 0x42, 0x50, 0x89, 0x91, 0xA4, 0x9F, 0x39, 0x6C, 0x96, + 0x4D, 0xAC, 0x02, 0xC0, 0x76, 0x68, 0x62, 0xCB, 0x03, 0x75, 0x01, 0x71, 0xC2, 0x54, 0x31, 0x62, 0x83, 0xF0, 0xAD, 0x4B, 0xEB, 0x80, 0xF5, 0x2F, + 0x68, 0x50, 0xA9, 0x10, 0x9E, 0x34, 0x5C, 0x9C, 0xFC, 0x70, 0x21, 0x18, 0xC0, 0x5A, 0x87, 0xA8, 0xA8, 0x49, 0x92, 0x91, 0x6A, 0xBA, 0x44, 0x21, + 0x12, 0x43, 0x10, 0x0F, 0x8D, 0x79, 0xF5, 0x2B, 0x70, 0xAA, 0xB3, 0x70, 0x18, 0xAB, 0x1C, 0xE1, 0x21, 0x9D, 0xF3, 0xB0, 0x15, 0xE1, 0x4F, 0x00, + 0x35, 0x59, 0x08, 0x8F, 0x9A, 0x2D, 0x57, 0xAB, 0x00, 0x81, 0x78, 0x6D, 0x1F, 0xB8, 0x1E, 0xFC, 0x08, 0x98, 0x12, 0x19, 0x40, 0xE7, 0x0C, 0x17, + 0x25, 0x39, 0x06, 0x61, 0xBD, 0xB5, 0x0F, 0xFF, 0x81, 0xAA, 0x4A, 0x35, 0x20, 0x3A, 0x67, 0xB8, 0x2C, 0xB1, 0x11, 0xB5, 0x13, 0x81, 0xF5, 0x43, + 0x41, 0x31, 0xCA, 0x93, 0xC6, 0x4B, 0x14, 0x22, 0x34, 0xE6, 0x3D, 0x39, 0x11, 0x5E, 0x12, 0xFE, 0xA7, 0x27, 0x47, 0x81, 0xE4, 0x94, 0xA9, 0x62, + 0x04, 0x91, 0xF0, 0x0F, 0xEF, 0x8C, 0x03, 0x85, 0x05, 0xBF, 0xD3, 0xEB, 0x3F, 0x6F, 0xC4, 0x44, 0xF1, 0x01, 0x43, 0x50, 0x45, 0x9C, 0x88, 0x0C, + 0x57, 0xBF, 0x81, 0x27, 0x45, 0x7E, 0xE4, 0x34, 0x66, 0x31, 0x92, 0xE3, 0x45, 0x20, 0x9D, 0x1A, 0x88, 0x2E, 0x08, 0x7F, 0x13, 0x14, 0xA2, 0x3B, + 0x61, 0x98, 0xDC, 0x80, 0xC6, 0x6D, 0xA8, 0xFA, 0x99, 0x76, 0x36, 0x4D, 0x94, 0x20, 0x33, 0xE6, 0xA7, 0xF3, 0xCC, 0x03, 0x17, 0x84, 0x7F, 0xC9, + 0xA9, 0x11, 0x20, 0xC8, 0x33, 0x5B, 0x9E, 0x04, 0x91, 0x31, 0x7D, 0xEF, 0xDA, 0x42, 0x5C, 0xA8, 0xFA, 0x95, 0x0B, 0x26, 0xD5, 0x59, 0x3A, 0x67, + 0xB8, 0x4C, 0x09, 0x52, 0xA3, 0xFA, 0xD6, 0xAD, 0x7D, 0xC8, 0x72, 0xF0, 0xFF, 0x90, 0x08, 0xED, 0x69, 0xF3, 0xE5, 0x4F, 0xCC, 0xF1, 0xE2, 0xCF, + 0x9F, 0x31, 0x3F, 0xF5, 0x04, 0x4F, 0x3F, 0xDB, 0x95, 0x6D, 0xD0, 0x2C, 0x5E, 0x89, 0xA6, 0x4C, 0xD7, 0x95, 0x65, 0xC8, 0x48, 0x16, 0x5E, 0xB9, + 0x88, 0xB7, 0xC5, 0x11, 0xCB, 0xD8, 0xF5, 0x79, 0xE5, 0x95, 0x2E, 0xC9, 0xC4, 0x2A, 0x4F, 0x54, 0xD8, 0x16, 0xE5, 0x2B, 0x15, 0x97, 0x53, 0x9A, + 0x58, 0xBC, 0x2B, 0xA5, 0xFC, 0xCA, 0x28, 0x88, 0x56, 0x28, 0x4F, 0x16, 0xE9, 0x2B, 0x9D, 0x34, 0x19, 0xBB, 0x52, 0xC9, 0xF2, 0xCA, 0x23, 0xCD, + 0x2B, 0x8D, 0x64, 0xAF, 0x24, 0x42, 0x75, 0xA5, 0x90, 0xC8, 0x2B, 0x7F, 0xF4, 0xC0, 0x9B, 0xE4, 0x51, 0x78, 0x25, 0x0F, 0xBC, 0x2B, 0x75, 0x34, + 0x5E, 0x89, 0x68, 0xE3, 0xE9, 0xCA, 0x19, 0x31, 0x12, 0xAF, 0x3C, 0x7C, 0x51, 0x5F, 0xD9, 0xC2, 0xF1, 0xCA, 0xC2, 0x6A, 0xE4, 0x95, 0x53, 0xBC, + 0x2B, 0x65, 0xC8, 0x2B, 0x05, 0x11, 0xAF, 0x0C, 0x6C, 0x5E, 0x09, 0x95, 0xBC, 0xF2, 0x7D, 0xBA, 0xD2, 0xCD, 0x5E, 0xB9, 0xD7, 0x5E, 0x89, 0x22, + 0x7B, 0x25, 0xDE, 0x5C, 0xE4, 0x95, 0x70, 0x88, 0x57, 0xBA, 0x39, 0x5E, 0xD9, 0xD6, 0xBC, 0x52, 0x8D, 0x79, 0x65, 0x19, 0xE4, 0x95, 0x64, 0xCF, + 0x2B, 0x45, 0xFC, 0x4A, 0x0F, 0xD7, 0x2B, 0x35, 0x34, 0xAF, 0x0C, 0xFB, 0xB2, 0x5E, 0x79, 0x01, 0x7B, 0x25, 0x97, 0x16, 0xF6, 0xCA, 0x0A, 0x85, + 0x57, 0xB6, 0x93, 0x90, 0x58, 0xFA, 0x49, 0x46, 0xE1, 0xD9, 0x09, 0x57, 0xFA, 0x49, 0x36, 0xE1, 0xD9, 0xC6, 0x2B, 0xFD, 0x24, 0x6B, 0xAF, 0xBC, + 0x7A, 0xB6, 0xF1, 0x4A, 0x3F, 0xC9, 0xAE, 0xB4, 0xA2, 0x72, 0xBC, 0xD2, 0x01, 0xE2, 0x95, 0x0C, 0xF0, 0xAE, 0x94, 0x2A, 0xBC, 0x32, 0xAA, 0x79, + 0x65, 0x82, 0xA7, 0x2B, 0x11, 0x50, 0x5D, 0xE9, 0x44, 0x5F, 0xAF, 0x34, 0xF0, 0x74, 0xA5, 0x72, 0xF1, 0xCA, 0x2D, 0xF9, 0x4D, 0x49, 0x71, 0xF2, + 0x3B, 0x92, 0xA3, 0xC8, 0xC2, 0xEF, 0x46, 0x54, 0xDF, 0x8B, 0x12, 0xBF, 0xEB, 0xA7, 0x6F, 0x43, 0xF5, 0xB7, 0x64, 0x21, 0xE4, 0xB7, 0xA0, 0xC2, + 0x6F, 0x40, 0xD9, 0xEF, 0x2C, 0x5B, 0xE2, 0xB7, 0x1F, 0x3E, 0xCF, 0xC6, 0xEF, 0xC8, 0xB1, 0xEF, 0x3C, 0x77, 0x98, 0xDF, 0x75, 0xBA, 0xBE, 0xE9, + 0x2C, 0xBF, 0x21, 0x9F, 0x8E, 0xDF, 0x70, 0x98, 0xDF, 0x01, 0x7F, 0x73, 0xCD, 0x29, 0x75, 0xCD, 0xF4, 0xEA, 0x0E, 0x47, 0x77, 0x13, 0x74, 0x57, + 0xA3, 0xBB, 0x1D, 0x5C, 0x0C, 0x57, 0x66, 0x77, 0x38, 0xE7, 0x4D, 0x38, 0x2F, 0x17, 0xF7, 0xB7, 0xB8, 0xD3, 0xC1, 0xBD, 0xEE, 0xED, 0x6D, 0x68, + 0x67, 0x3B, 0xDA, 0xE3, 0xE8, 0x8E, 0x46, 0xF7, 0xB5, 0xB8, 0xA7, 0xC5, 0xFD, 0xC4, 0xDA, 0xF5, 0xCE, 0xBE, 0x76, 0x76, 0x13, 0x65, 0x77, 0x7F, + 0x9A, 0x5D, 0xF4, 0xAD, 0xB6, 0xB0, 0x7B, 0xDC, 0xEB, 0xE2, 0xEE, 0x2C, 0x03, 0x11, 0xD7, 0xFC, 0x98, 0x73, 0xED, 0x1A, 0x98, 0x5F, 0x6E, 0x74, + 0xDF, 0xA0, 0x76, 0x77, 0xB8, 0xBA, 0xDB, 0xC5, 0x9D, 0x0D, 0xEE, 0x67, 0xEE, 0x59, 0xDB, 0x4E, 0xE7, 0x02, 0xA7, 0x74, 0x97, 0xFB, 0x93, 0xFE, + 0xAD, 0xF9, 0x66, 0x6C, 0x18, 0x38, 0x95, 0xF6, 0xC5, 0x77, 0x74, 0x7F, 0x9E, 0x73, 0x36, 0x74, 0x1E, 0xED, 0x6E, 0x81, 0x52, 0xF7, 0xAD, 0x85, + 0x67, 0xE7, 0x9D, 0x7E, 0x55, 0xE7, 0xEF, 0xF4, 0x5E, 0xA6, 0x77, 0x3D, 0xE7, 0xEF, 0xE0, 0x7E, 0x07, 0x77, 0xE7, 0xDB, 0xD1, 0xCE, 0x7E, 0x67, + 0xF6, 0x36, 0xB3, 0xAF, 0x39, 0xCF, 0x06, 0xF7, 0xB8, 0xD3, 0xB5, 0xB1, 0xF3, 0xEE, 0x87, 0x9B, 0xE7, 0x17, 0x6E, 0xB6, 0x54, 0x29, 0xEC, 0x3A, + 0x1F, 0x67, 0x77, 0x36, 0xBA, 0x97, 0xBD, 0x9D, 0xCD, 0xED, 0x68, 0x6D, 0xC7, 0x3B, 0x4B, 0xB3, 0xFB, 0x9A, 0xDD, 0xCF, 0xEC, 0x8E, 0xD7, 0xBC, + 0x13, 0xEE, 0x27, 0xE0, 0xAE, 0x06, 0xF7, 0xB3, 0xB7, 0xC7, 0xB9, 0x3D, 0x6F, 0xED, 0xCD, 0xB5, 0xA3, 0xAD, 0x5D, 0x0E, 0xED, 0x70, 0x68, 0xBF, + 0x33, 0xBB, 0x9C, 0xD9, 0xDF, 0xCC, 0x6E, 0x66, 0xF6, 0xBB, 0xB2, 0xAF, 0xBD, 0x77, 0x69, 0xCD, 0x97, 0x35, 0x9F, 0xE7, 0x76, 0x38, 0x13, 0x97, + 0x3B, 0x9B, 0x73, 0x5E, 0x6D, 0xED, 0x6E, 0x6A, 0x65, 0xCE, 0x0D, 0x62, 0x4C, 0xB1, 0x58, 0x6F, 0x5A, 0x55, 0xBB, 0x1B, 0xDA, 0xD3, 0xCC, 0xFA, + 0xA4, 0xEE, 0xF5, 0x9E, 0x67, 0xE8, 0xD6, 0x0C, 0x74, 0x9B, 0x65, 0x7F, 0xF3, 0x6A, 0xEE, 0xAB, 0xD7, 0x12, 0x36, 0xE3, 0xE4, 0x37, 0xEE, 0x65, + 0x7C, 0xB0, 0xA0, 0x1B, 0xD7, 0x82, 0xC5, 0x1D, 0xCE, 0x10, 0xAE, 0x0C, 0xEE, 0x6C, 0x69, 0x7D, 0x7D, 0xF1, 0x9C, 0x77, 0xB3, 0x37, 0xEA, 0xF7, + 0x33, 0xE7, 0xD3, 0xE6, 0x5E, 0xF7, 0x46, 0x09, 0x63, 0xB6, 0xD8, 0xED, 0x95, 0xE6, 0xBD, 0xCD, 0xB9, 0x6E, 0xBF, 0x9C, 0x09, 0x13, 0x7F, 0xFB, + 0x5C, 0x9C, 0x5A, 0xF3, 0x6E, 0xA7, 0x63, 0xA3, 0xC2, 0x2E, 0xDB, 0xD7, 0xE8, 0x1E, 0xE7, 0xFC, 0x9B, 0xF3, 0x6C, 0x70, 0x7F, 0x7B, 0xBB, 0xB3, + 0xED, 0x77, 0x6B, 0x8F, 0x5B, 0x3B, 0xDB, 0xDA, 0xDB, 0xCE, 0xAE, 0x96, 0xB6, 0xF6, 0xB9, 0xB5, 0xE7, 0x99, 0xA5, 0x99, 0x38, 0x5C, 0xDB, 0xD3, + 0xDA, 0xBE, 0xB6, 0x76, 0x39, 0xB5, 0x3B, 0x4D, 0xBC, 0xAC, 0xEE, 0x76, 0x6F, 0x6F, 0x53, 0x7B, 0x9A, 0xD9, 0xF1, 0xD2, 0xDE, 0xD2, 0xBE, 0xE6, + 0x34, 0xFB, 0x5B, 0x8B, 0x06, 0x97, 0x76, 0xBA, 0xBC, 0xAF, 0xD5, 0x5D, 0x8F, 0xEE, 0x72, 0xCE, 0xAF, 0xC1, 0x1D, 0xCE, 0xED, 0x69, 0x6E, 0xA7, + 0x4B, 0xBB, 0x09, 0xB4, 0x15, 0x69, 0x52, 0x38, 0x8A, 0xF6, 0x3A, 0x39, 0xB8, 0xBB, 0xB1, 0x41, 0xA7, 0xED, 0x2F, 0x74, 0xB5, 0x78, 0x70, 0xAF, + 0xB3, 0xBB, 0x99, 0xF3, 0x67, 0x71, 0x87, 0x53, 0x7B, 0x6B, 0xDE, 0x0D, 0xEE, 0x73, 0x6C, 0x2F, 0x33, 0xBB, 0x9B, 0xF3, 0xCE, 0xB9, 0xD7, 0xB1, + 0x5D, 0xEF, 0xEC, 0x6F, 0x52, 0x17, 0x5E, 0x73, 0xBB, 0x5C, 0xDB, 0x4F, 0xC0, 0x53, 0x3B, 0xE7, 0xDB, 0xE2, 0x1E, 0xB7, 0xF6, 0xB9, 0xB3, 0xEB, + 0xBD, 0x05, 0x3B, 0x5C, 0xD9, 0xD4, 0xED, 0xAB, 0xDA, 0xB5, 0x5F, 0x7B, 0x9E, 0x1B, 0x7D, 0xCD, 0x8E, 0x06, 0x9D, 0xBE, 0x7F, 0xD2, 0xB9, 0xD3, + 0x35, 0x7F, 0x3D, 0x40, 0xD8, 0x6D, 0x01, 0x76, 0xF7, 0xB6, 0x76, 0xB8, 0xCF, 0xFD, 0x44, 0xDB, 0xFB, 0xF4, 0xF4, 0x0E, 0x27, 0xF7, 0xB4, 0xB4, + 0xBC, 0x97, 0x39, 0x5F, 0x76, 0xE2, 0x6C, 0x66, 0xE7, 0x73, 0xE7, 0xB9, 0x47, 0x9D, 0x75, 0x57, 0x93, 0xAE, 0x28, 0xBB, 0x54, 0x8C, 0xC9, 0xFC, + 0xDB, 0xFA, 0xCD, 0xAE, 0x5F, 0x1B, 0x6D, 0x9D, 0x75, 0x65, 0xA7, 0x4B, 0xBB, 0x7B, 0x9D, 0xF3, 0xEB, 0xCE, 0x8E, 0xF0, 0x0F, 0x86, 0xFB, 0x72, + 0x61, 0x98, 0x4D, 0x1A, 0xB7, 0x80, 0xE7, 0xEA, 0xEB, 0xAC, 0xBB, 0xE1, 0x9B, 0x07, 0x82, 0xCE, 0x99, 0xD7, 0x84, 0x63, 0x8A, 0xA5, 0x9D, 0xD1, + 0xA1, 0x95, 0x71, 0x08, 0xB3, 0x13, 0x34, 0x74, 0x3A, 0xEF, 0x76, 0x26, 0x37, 0xF7, 0x68, 0xDC, 0xBC, 0xEC, 0xE6, 0xC1, 0xD1, 0xE8, 0x1B, 0xE8, + 0xDE, 0xCD, 0x55, 0xD9, 0xA5, 0xBA, 0x84, 0xEE, 0x0C, 0x75, 0xE9, 0x62, 0x41, 0xD8, 0x75, 0xFE, 0xC5, 0xB3, 0x3B, 0x07, 0x27, 0xBB, 0x52, 0xF8, + 0x95, 0xA5, 0x7D, 0x54, 0xB8, 0xEA, 0x46, 0xF7, 0x9A, 0xAF, 0x60, 0x75, 0xC5, 0x1C, 0xBF, 0x55, 0xB8, 0x1A, 0x67, 0xE8, 0x12, 0xFA, 0xC1, 0x53, + 0x77, 0x3A, 0x03, 0xBA, 0x70, 0xB1, 0x20, 0x2C, 0x75, 0xDD, 0xA4, 0x31, 0x35, 0x1A, 0x47, 0xDD, 0xA8, 0x0B, 0xF3, 0x09, 0xCA, 0x6E, 0xD4, 0x5D, + 0xA3, 0x56, 0x57, 0xB8, 0xD2, 0x2A, 0x0C, 0x08, 0xC7, 0x2B, 0x62, 0x35, 0xC3, 0xB9, 0xCA, 0xF7, 0x52, 0xDD, 0x15, 0x81, 0xD5, 0x2A, 0x55, 0x0A, + 0xBB, 0x66, 0x81, 0x7D, 0x55, 0x17, 0x96, 0xCB, 0xC2, 0xD0, 0x9A, 0x63, 0xCE, 0xB3, 0xB9, 0xB8, 0xE6, 0xE9, 0xDE, 0x9A, 0x8F, 0x01, 0xDD, 0x41, + 0xB7, 0x8C, 0xED, 0xEC, 0x4B, 0xCF, 0x19, 0x17, 0xF7, 0xA8, 0xF4, 0x5D, 0x9B, 0xAB, 0xCE, 0xD7, 0x2C, 0x0C, 0xEE, 0x83, 0xB3, 0x0D, 0x0E, 0xEE, + 0xAE, 0xE1, 0xA5, 0x30, 0xA0, 0x4B, 0xE8, 0x26, 0xBB, 0x26, 0x4C, 0xB8, 0xF3, 0xD4, 0x9D, 0xE3, 0x77, 0x8E, 0x4A, 0x8D, 0x0D, 0xAB, 0x30, 0x75, + 0xFA, 0x46, 0x5F, 0xA8, 0x0B, 0x27, 0x7D, 0xA3, 0xF3, 0xEB, 0x36, 0xC7, 0x82, 0x71, 0x5F, 0x3C, 0x85, 0x67, 0xE8, 0xD9, 0x2C, 0x75, 0xDF, 0xBE, + 0xB6, 0xB6, 0xB6, 0x07, 0xB2, 0x9D, 0xF9, 0xF5, 0x65, 0xB6, 0x76, 0x43, 0x57, 0x6D, 0xAE, 0xBE, 0x86, 0xD0, 0x16, 0x32, 0xDF, 0x3B, 0xFF, 0xB9, + 0x7A, 0xEE, 0x5D, 0xFC, 0x17, 0x8D, 0xA7, 0x55, 0x58, 0x1A, 0x75, 0xA5, 0x2D, 0xAA, 0x7E, 0xAE, 0x5D, 0x0C, 0x6D, 0x51, 0x37, 0x18, 0x47, 0xEB, + 0xA6, 0x33, 0x2C, 0x8D, 0xBA, 0x74, 0xD1, 0x18, 0x4A, 0x85, 0x57, 0xC4, 0x80, 0x72, 0x1C, 0x18, 0x8D, 0xDF, 0x15, 0x82, 0xB0, 0x93, 0x3A, 0xF8, + 0x02, 0xBA, 0x73, 0x2C, 0x85, 0x67, 0x57, 0x30, 0x9E, 0x8B, 0xFF, 0xFC, 0xE2, 0x69, 0x33, 0x3A, 0x95, 0x9B, 0xC6, 0x7D, 0xAE, 0x34, 0x0E, 0x9C, + 0x4A, 0x61, 0xA9, 0xA0, 0x0B, 0x7D, 0xE1, 0xD9, 0x0F, 0x9E, 0xFF, 0x27, 0xF8, 0x16, 0x56, 0xFD, 0xA4, 0x31, 0x20, 0xA0, 0x34, 0x7E, 0x83, 0x0D, + 0xE3, 0x79, 0xF0, 0x85, 0xA5, 0xB0, 0x1B, 0xDC, 0x0B, 0xB3, 0x19, 0xBE, 0xD3, 0xEA, 0xEC, 0x52, 0xF9, 0xE0, 0x35, 0x17, 0x2A, 0xAD, 0xBA, 0xD2, + 0x77, 0x2A, 0xBB, 0x7E, 0xD5, 0x77, 0xAE, 0x46, 0xDF, 0xE9, 0xB4, 0xEA, 0x0E, 0xC2, 0x54, 0x77, 0x9E, 0xE7, 0xBF, 0x15, 0xD1, 0xD7, 0x0C, 0x4A, + 0x0D, 0x74, 0x67, 0xA9, 0xA0, 0xEB, 0xF7, 0xCA, 0xF1, 0x5C, 0x4D, 0x7B, 0x35, 0x5A, 0xD5, 0x93, 0x06, 0x3E, 0xEA, 0x4E, 0xA5, 0x67, 0x74, 0x74, + 0x67, 0x75, 0xD0, 0x3E, 0x98, 0x70, 0x66, 0x82, 0x18, 0xFB, 0xE0, 0xA1, 0x03, 0x87, 0x0D, 0x1A, 0xC4, 0x98, 0x5F, 0x20, 0xC6, 0x3E, 0x78, 0xE8, + 0xC0, 0x61, 0x83, 0x06, 0x31, 0x26, 0x06, 0xB8, 0xB5, 0xB0, 0x3E, 0x6A, 0xDA, 0xDF, 0x19, 0x4E, 0x3B, 0x55, 0x36, 0xFC, 0x4B, 0x4B, 0xEB, 0x90, + 0x76, 0x96, 0x96, 0x36, 0x6D, 0x93, 0xC2, 0xD4, 0x37, 0x0A, 0x43, 0xE3, 0xC0, 0x6A, 0x9E, 0xDB, 0xF4, 0x5B, 0x01, 0xC7, 0x70, 0xF3, 0x2B, 0x75, + 0x09, 0x5F, 0x68, 0xDF, 0xD4, 0x75, 0x7E, 0x3D, 0xCC, 0xA5, 0x43, 0x3B, 0xF3, 0xE0, 0x8C, 0x4B, 0x3B, 0x3B, 0x5D, 0xD9, 0x19, 0x75, 0xEA, 0xA6, + 0xB9, 0x2F, 0x5A, 0x58, 0x14, 0x6E, 0xE1, 0xCE, 0x4E, 0x33, 0xB3, 0xBB, 0xD2, 0xB8, 0x6F, 0xFA, 0xDE, 0xCD, 0xB0, 0x1D, 0x2C, 0xF5, 0x33, 0xF4, + 0x9B, 0x4A, 0xDF, 0xB7, 0x7A, 0xE2, 0x6D, 0xDE, 0x39, 0xE3, 0xA2, 0x2F, 0x1C, 0x61, 0xCC, 0xB6, 0x07, 0xB7, 0xB1, 0xBD, 0xF1, 0xC5, 0x6C, 0xD1, + 0xBE, 0x6A, 0x1A, 0xE1, 0xEC, 0xA2, 0xC1, 0xF3, 0x5C, 0x75, 0xE7, 0xF9, 0x8C, 0x6D, 0x2F, 0x9E, 0xC2, 0xB3, 0x99, 0x1B, 0x7D, 0xFD, 0xE8, 0xCE, + 0xA2, 0xEE, 0x1C, 0x95, 0xBA, 0x52, 0xB6, 0x3D, 0xAA, 0x1B, 0xBF, 0x6B, 0x81, 0x80, 0x81, 0x86, 0xEF, 0xDC, 0x0C, 0xC3, 0xC1, 0x99, 0x88, 0xE7, + 0xD7, 0x55, 0x7B, 0xFD, 0xE6, 0x57, 0x36, 0x9C, 0xCF, 0x5C, 0xBF, 0xAA, 0x59, 0x8B, 0xB2, 0xC1, 0x77, 0x7E, 0xDF, 0x3C, 0xE9, 0x74, 0x30, 0xFA, + 0xF7, 0x85, 0xED, 0xDE, 0xB8, 0x2F, 0xEA, 0xF4, 0x30, 0x0B, 0xC1, 0xEA, 0x1C, 0x53, 0xA5, 0xAE, 0x1B, 0xAD, 0x6B, 0xEE, 0x01, 0xD6, 0xC5, 0x86, + 0x81, 0x31, 0x54, 0x86, 0x7E, 0x10, 0x1E, 0x7C, 0xA7, 0x7C, 0xA7, 0x4A, 0x61, 0x97, 0xCE, 0x30, 0x1A, 0x07, 0xBE, 0x52, 0xBF, 0x77, 0xBE, 0xEC, + 0x3A, 0x61, 0x17, 0x0A, 0x75, 0xC6, 0xEF, 0xE1, 0xA8, 0xEB, 0x84, 0xFA, 0xB9, 0x86, 0xF0, 0xA5, 0x71, 0x3C, 0xE7, 0x46, 0x0D, 0x5B, 0x18, 0x9E, + 0x30, 0xBC, 0x7B, 0xC2, 0x35, 0x6C, 0x06, 0xBA, 0xD2, 0xB8, 0xF9, 0x36, 0xF7, 0xA0, 0xCD, 0xA8, 0x3B, 0x0B, 0xCA, 0xC1, 0x7C, 0xAF, 0xB4, 0x45, + 0x35, 0x30, 0x1E, 0x8C, 0x1F, 0x47, 0x75, 0xFB, 0xA6, 0x55, 0x58, 0x3A, 0x07, 0x4A, 0xFF, 0xAA, 0xB1, 0x0B, 0xBB, 0x84, 0xD0, 0x3E, 0x41, 0xB7, + 0xD9, 0x35, 0x8C, 0xA5, 0x53, 0x77, 0x8E, 0xC6, 0x77, 0xFF, 0x0C, 0xA1, 0x55, 0x3F, 0x28, 0x75, 0x86, 0xA1, 0x7D, 0xEE, 0xEC, 0x42, 0xFB, 0x04, + 0x5D, 0xB7, 0x69, 0x75, 0x7E, 0x65, 0xD7, 0xD0, 0xA5, 0x9A, 0xA0, 0x4E, 0x1F, 0x38, 0x9D, 0xDB, 0xFC, 0x06, 0xDD, 0xF6, 0xFE, 0xA4, 0x50, 0x29, + 0x0C, 0xB7, 0xB7, 0x17, 0xC7, 0x33, 0xDF, 0x7B, 0x29, 0xBC, 0x36, 0xAD, 0xFE, 0xA9, 0x4D, 0x5D, 0xBE, 0xEA, 0xE1, 0x28, 0x1C, 0xE8, 0x42, 0x3F, + 0xAD, 0xF6, 0xF5, 0xB9, 0x68, 0x31, 0x9F, 0x60, 0x1C, 0x8D, 0xAB, 0x2C, 0xFE, 0xA4, 0xD3, 0xA8, 0xCB, 0x07, 0xC7, 0x30, 0xF4, 0x2E, 0x95, 0x5A, + 0xED, 0x8B, 0xBA, 0x67, 0xF0, 0x0B, 0x9F, 0xFA, 0x5E, 0x3A, 0xFF, 0x49, 0x03, 0x67, 0xA8, 0x1F, 0xF4, 0xCF, 0x35, 0x9B, 0x0D, 0xA9, 0xF7, 0x83, + 0x06, 0x52, 0xFF, 0xA0, 0xD3, 0xBF, 0x7C, 0x71, 0xD5, 0xA5, 0xB2, 0xF8, 0x6C, 0xE8, 0xE7, 0x4A, 0x5D, 0x41, 0x41, 0x57, 0x30, 0x0A, 0x4F, 0x5F, + 0xC3, 0xD7, 0xF0, 0x4F, 0x1A, 0xDB, 0x45, 0xA3, 0xF1, 0x4C, 0x47, 0x18, 0x1B, 0x3A, 0xE3, 0x66, 0x35, 0x76, 0xBA, 0x73, 0x3C, 0x17, 0x75, 0x9B, + 0x55, 0xB8, 0xBE, 0x2A, 0x8B, 0xB9, 0x0E, 0xAD, 0x3A, 0x45, 0x33, 0x7C, 0xA7, 0xD4, 0x3E, 0x69, 0x20, 0x21, 0x35, 0xD0, 0xCD, 0xFB, 0x7B, 0x65, + 0x58, 0x2A, 0x85, 0xDF, 0xE9, 0x4B, 0x85, 0xA1, 0x77, 0x33, 0x6C, 0x57, 0xB5, 0x8B, 0x63, 0x2A, 0x5D, 0x15, 0x10, 0xA6, 0x7B, 0xA3, 0x55, 0xA8, + 0x2B, 0x47, 0xDD, 0xBF, 0xBF, 0x97, 0x6E, 0xDE, 0xA3, 0xAE, 0x80, 0x09, 0xA9, 0xAE, 0x60, 0x0C, 0xA5, 0x83, 0xFB, 0xAA, 0x6F, 0x54, 0x2A, 0xEC, + 0x36, 0x95, 0x1A, 0x52, 0x9D, 0x70, 0x60, 0x5F, 0x3C, 0xCB, 0x30, 0xD4, 0xCF, 0x90, 0x2F, 0xEE, 0x8B, 0x56, 0xE3, 0xBD, 0x18, 0x16, 0x7C, 0xE7, + 0xE8, 0xDB, 0xE6, 0x3A, 0xA7, 0x71, 0x3C, 0x9B, 0x51, 0xAB, 0xD1, 0x78, 0xFA, 0xF7, 0xAE, 0x80, 0xFB, 0x5E, 0xEA, 0x1B, 0x95, 0xB6, 0xED, 0x49, + 0xE1, 0x40, 0xA9, 0xE1, 0x5C, 0x99, 0xF4, 0xAD, 0x42, 0xA3, 0xAF, 0xDD, 0xDF, 0xB9, 0xAF, 0x6C, 0x2F, 0x96, 0xC2, 0xAE, 0xEC, 0xDC, 0x5B, 0x11, + 0xEF, 0x7D, 0x55, 0xDD, 0xAF, 0x42, 0x58, 0xFA, 0xC6, 0xD0, 0x79, 0xFE, 0x9F, 0xB4, 0x4A, 0x9D, 0x61, 0xE9, 0x9F, 0xDB, 0x74, 0xFF, 0x00, 0xE5, + 0xAA, 0xD5, 0x38, 0xD7, 0xF9, 0x9E, 0x41, 0xDD, 0x68, 0x95, 0x1A, 0xEF, 0x41, 0xA9, 0x2E, 0x54, 0x1A, 0xCF, 0x52, 0x19, 0x8E, 0x8B, 0xF3, 0xF6, + 0x08, 0x5F, 0x37, 0x8E, 0xEA, 0x4E, 0xA3, 0xEE, 0x3B, 0xC7, 0xAF, 0x1F, 0x3C, 0x68, 0xF8, 0xCE, 0xD4, 0x3E, 0xB8, 0xB8, 0x0F, 0x2E, 0x0C, 0xF4, + 0x7B, 0xDF, 0x28, 0xD5, 0x6D, 0x11, 0xCF, 0x31, 0x1C, 0x3C, 0x57, 0xA5, 0x31, 0x0C, 0x37, 0x37, 0xF7, 0xE6, 0x97, 0x70, 0xFA, 0x27, 0x75, 0x05, + 0xE1, 0x16, 0x6E, 0x13, 0xF0, 0x8D, 0x6B, 0xDB, 0x7B, 0xFB, 0x60, 0xAA, 0x61, 0x60, 0x5F, 0xD5, 0xAD, 0xEA, 0xBE, 0x52, 0x13, 0xD5, 0xA8, 0xCB, + 0x22, 0x10, 0x1E, 0xAC, 0xF6, 0x4D, 0xFB, 0xA4, 0xB0, 0x3B, 0xF8, 0xDA, 0x19, 0x52, 0xE1, 0xB3, 0xBD, 0x68, 0xF4, 0xA5, 0x9B, 0x5F, 0xEA, 0xDB, + 0xFC, 0x73, 0xE1, 0x0A, 0x63, 0xA9, 0xDB, 0x14, 0xF6, 0x93, 0xB6, 0xC5, 0xD3, 0x58, 0x70, 0x7E, 0x07, 0x0B, 0xDD, 0x15, 0x6C, 0xAE, 0x21, 0x0B, + 0xD7, 0x50, 0xF0, 0xA5, 0xBE, 0x72, 0x35, 0x8E, 0xBE, 0x7E, 0x6E, 0x75, 0x2D, 0x70, 0xAE, 0xD2, 0xED, 0x4D, 0x63, 0x3E, 0x17, 0x4A, 0xF5, 0x8B, + 0xD7, 0xFA, 0xDC, 0xE8, 0xDA, 0x1B, 0x75, 0x5F, 0xA9, 0xA1, 0x7B, 0x17, 0x7D, 0xE3, 0xF6, 0xD4, 0xFC, 0xA6, 0xD2, 0x39, 0x8E, 0xBE, 0xF2, 0x2B, + 0xAD, 0xCE, 0x70, 0x9A, 0x34, 0x16, 0xF4, 0x4B, 0x93, 0xC2, 0x82, 0x31, 0x15, 0x8E, 0x67, 0xEA, 0xFC, 0x0E, 0x4A, 0xDF, 0xC0, 0x77, 0x2F, 0x9E, + 0x12, 0x4A, 0x07, 0x03, 0xA5, 0x2E, 0x5A, 0x5C, 0x75, 0xE1, 0x6A, 0x4C, 0x7D, 0xAB, 0x2C, 0x60, 0x17, 0x6A, 0xD8, 0x47, 0x9D, 0x7E, 0xDE, 0x8B, + 0xB6, 0x78, 0x67, 0x3F, 0xD7, 0xAF, 0x0A, 0xC7, 0x7F, 0xD0, 0xB3, 0xD7, 0xB0, 0xCF, 0x9D, 0xFB, 0xA4, 0x9F, 0x65, 0xC3, 0xF7, 0xCF, 0x0F, 0x9E, + 0xF9, 0x04, 0xDD, 0xB3, 0x29, 0x1C, 0xFF, 0xDD, 0xA5, 0x4E, 0x61, 0xBB, 0x3D, 0x37, 0x96, 0xC6, 0xCE, 0xAF, 0xB7, 0x05, 0x2C, 0xF8, 0x46, 0x5F, + 0xBE, 0xF9, 0x53, 0xB3, 0x3D, 0x18, 0xEA, 0x9A, 0x05, 0xBA, 0x81, 0x71, 0x20, 0x4C, 0x8D, 0xE1, 0x3E, 0x69, 0x75, 0x5A, 0x75, 0xEB, 0x04, 0xAB, + 0x7D, 0x55, 0xBA, 0x29, 0x5C, 0x7D, 0x33, 0xB1, 0x27, 0x75, 0xA9, 0xF1, 0x9F, 0xBB, 0xB6, 0xD7, 0x22, 0xF6, 0x93, 0xBE, 0x70, 0x80, 0x31, 0x3C, + 0xB7, 0xE7, 0xBE, 0x19, 0x9A, 0xE0, 0x8B, 0xFD, 0x66, 0x3F, 0x77, 0x10, 0x9E, 0xF3, 0x8B, 0x05, 0x63, 0xB7, 0x6F, 0xA6, 0xBA, 0x67, 0xD3, 0x77, + 0x86, 0x63, 0xE9, 0x4B, 0x95, 0xBE, 0x7F, 0x52, 0xD8, 0x9D, 0xAB, 0x7F, 0x30, 0xD5, 0x8D, 0x46, 0xDF, 0x3F, 0x77, 0x96, 0xBA, 0x65, 0x0F, 0x7C, + 0x63, 0xA8, 0x9F, 0x14, 0xBE, 0x83, 0xA5, 0x55, 0xBA, 0x58, 0x86, 0xE7, 0x2A, 0x35, 0x10, 0xA6, 0xA3, 0xDB, 0x8B, 0x5D, 0xE7, 0x60, 0x1C, 0xF7, + 0x15, 0x37, 0xE3, 0xBF, 0x6A, 0xEC, 0x56, 0xE5, 0xA8, 0xD4, 0x18, 0xDA, 0x17, 0x68, 0x08, 0x4B, 0x5F, 0x41, 0x98, 0x2F, 0x0E, 0x1E, 0x8C, 0x67, + 0x69, 0x15, 0xEA, 0xFE, 0xC5, 0x70, 0xD5, 0xF9, 0x9D, 0x52, 0xAB, 0x72, 0xCC, 0x07, 0x37, 0x85, 0xA1, 0x53, 0x97, 0xAF, 0xEA, 0x27, 0x75, 0xAB, + 0x82, 0xF1, 0xDB, 0x3B, 0xF3, 0xC1, 0xD3, 0xF8, 0xCF, 0x7D, 0x83, 0x4E, 0xA5, 0x70, 0xD4, 0xFD, 0x83, 0x05, 0xAB, 0x31, 0x75, 0x76, 0xA1, 0x6E, + 0xB2, 0xA0, 0xEB, 0x56, 0xFD, 0x9B, 0xBE, 0x52, 0xA9, 0x0B, 0x8D, 0x4D, 0xB0, 0xBD, 0xD2, 0x78, 0x8E, 0xF9, 0xDC, 0x35, 0xC2, 0x98, 0xFA, 0xBE, + 0xB3, 0x5A, 0x15, 0x76, 0xCB, 0xF6, 0xA2, 0x69, 0xD5, 0x18, 0xA6, 0x56, 0x63, 0x3E, 0x18, 0x50, 0xD0, 0x75, 0xA3, 0xAE, 0x73, 0x6F, 0xFA, 0xEE, + 0xCD, 0xB3, 0x1B, 0xE8, 0xA2, 0x19, 0xCE, 0x81, 0xAE, 0x34, 0xA6, 0x56, 0xDF, 0x3E, 0x3E, 0x77, 0x1A, 0xCF, 0xB0, 0x7B, 0x56, 0xE5, 0x13, 0xAC, + 0x02, 0x56, 0xA3, 0x51, 0x78, 0xD0, 0x6D, 0xD1, 0xC2, 0x9D, 0xDF, 0xD9, 0x90, 0x2A, 0x75, 0xA3, 0x55, 0x77, 0x3A, 0x85, 0xA3, 0x55, 0x40, 0xB8, + 0xFA, 0x27, 0x8D, 0x03, 0x5D, 0x41, 0xD7, 0xEC, 0x75, 0x05, 0xA7, 0xD3, 0xF9, 0x85, 0xFF, 0xA2, 0x7C, 0x2E, 0x5F, 0xD9, 0xDE, 0x14, 0xF6, 0xF3, + 0x73, 0xE7, 0xBE, 0x3E, 0xA9, 0x0B, 0xE8, 0x07, 0xF3, 0x95, 0x85, 0xB9, 0x7D, 0xF0, 0xBC, 0xC2, 0x39, 0xB7, 0x96, 0x56, 0x76, 0xBA, 0xB3, 0xB2, + 0x76, 0x6B, 0xDD, 0x4A, 0xD4, 0x9E, 0xCB, 0x3B, 0x88, 0x03, 0x70, 0x13, 0x9B, 0xED, 0xD0, 0xB7, 0x9D, 0xF9, 0xF9, 0x0D, 0x3E, 0xB5, 0xA3, 0xB6, + 0xD5, 0x5E, 0xB1, 0xDF, 0xED, 0xB7, 0xFB, 0x36, 0xDF, 0x76, 0xF7, 0x73, 0xEF, 0x6D, 0x15, 0x3B, 0xC5, 0x0E, 0x60, 0xA3, 0xD8, 0x7A, 0x87, 0xB3, + 0xD5, 0xE4, 0xEC, 0xB6, 0x0E, 0x2F, 0xEF, 0x6E, 0xCF, 0xD9, 0xA9, 0xD1, 0x6D, 0xE7, 0x89, 0xDD, 0xDC, 0x72, 0x3B, 0x6E, 0xCF, 0x6D, 0xB8, 0xB7, + 0x36, 0xB6, 0xBD, 0xB6, 0xD5, 0x76, 0xDA, 0x46, 0xDB, 0x78, 0xFB, 0x6C, 0x9B, 0xED, 0xB6, 0x5D, 0xB6, 0xC9, 0x76, 0x1D, 0x89, 0x3D, 0x22, 0xC4, + 0xF3, 0x5E, 0xFD, 0xE3, 0xE7, 0x77, 0x39, 0x88, 0x5D, 0x62, 0x8F, 0xD8, 0x23, 0xEE, 0xE7, 0x7D, 0xDF, 0xBB, 0x8D, 0x0D, 0xC0, 0xE7, 0x2D, 0x62, + 0xB9, 0xDF, 0x9C, 0xB7, 0x23, 0xE5, 0x0A, 0x36, 0x08, 0x29, 0x7B, 0xEC, 0x0A, 0x94, 0xEC, 0x0F, 0x60, 0xAE, 0x6C, 0xB6, 0x2F, 0xC2, 0xDA, 0x1E, + 0x1A, 0x6C, 0x0E, 0x7B, 0xC3, 0xD6, 0xB0, 0xC5, 0x3A, 0x30, 0xC3, 0x36, 0xD8, 0x57, 0x86, 0x8D, 0x61, 0x37, 0xF0, 0xDA, 0x17, 0x76, 0x57, 0x83, + 0xA9, 0x97, 0xD9, 0x5A, 0x7B, 0x6B, 0x5B, 0xA8, 0xB0, 0xB3, 0xB6, 0x83, 0x06, 0xBB, 0x9D, 0xB0, 0x29, 0xEC, 0x2B, 0xAB, 0x2A, 0x09, 0x3B, 0x02, + 0xD5, 0xEE, 0xB0, 0x21, 0xEC, 0x07, 0x5C, 0x7B, 0x6A, 0x37, 0xD8, 0x58, 0x52, 0x7B, 0xB9, 0x1D, 0x2C, 0xD8, 0x51, 0x0A, 0x76, 0x82, 0x8D, 0x60, + 0x1F, 0xD8, 0x50, 0xFB, 0x69, 0x3B, 0xED, 0xBD, 0x5F, 0xE9, 0x94, 0x3E, 0xE9, 0x92, 0x1E, 0xE9, 0x0B, 0x1C, 0x92, 0xA3, 0x37, 0x3A, 0xA3, 0x2F, + 0xFA, 0xA2, 0x1D, 0xE1, 0x4B, 0x57, 0x74, 0x79, 0x33, 0xD8, 0x5B, 0x57, 0xF0, 0xB5, 0x4D, 0x76, 0x18, 0x86, 0x32, 0x1E, 0xC2, 0x30, 0x96, 0x2D, + 0xA8, 0x44, 0x25, 0xCF, 0x66, 0x38, 0x63, 0x34, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xD8, 0x21, 0x8C, 0x09, 0x29, 0x8C, 0x23, 0xAD, + 0x95, 0x8A, 0xAA, 0x03, 0x04, 0xC8, 0xC2, 0x4B, 0x51, 0x70, 0x68, 0x4C, 0x8E, 0x43, 0x79, 0x80, 0x02, 0x11, 0x62, 0x8C, 0x62, 0x00, 0x00, 0x00, + 0x80, 0x12, 0x00, 0x00, 0x0C, 0x00, 0x81, 0x60, 0x84, 0x0E, 0xF7, 0x42, 0x5F, 0xDB, 0xF5, 0xDD, 0x6D, 0xCE, 0x95, 0x56, 0x79, 0x7B, 0xC3, 0x2C, + 0xF2, 0x7F, 0xD8, 0xDE, 0x3D, 0x86, 0x12, 0x92, 0xC2, 0x4F, 0x22, 0x15, 0x1F, 0x17, 0x53, 0xCF, 0xFB, 0x77, 0xAB, 0xB9, 0xE8, 0x7A, 0xA1, 0x13, + 0x17, 0x4F, 0x96, 0xF4, 0x7F, 0x88, 0x13, 0x80, 0x1D, 0x1A, 0x50, 0xD7, 0x0D, 0x7B, 0xE4, 0x86, 0x41, 0x93, 0x4C, 0x81, 0xB2, 0xB6, 0x53, 0x0F, + 0x1A, 0x52, 0xE7, 0x26, 0x8D, 0xB2, 0xFC, 0x82, 0xB4, 0x40, 0xDD, 0x79, 0x7C, 0x13, 0x6B, 0x3F, 0x5C, 0x54, 0xD0, 0x37, 0xBA, 0xF4, 0x69, 0x49, + 0x85, 0xB0, 0xFE, 0x64, 0xDF, 0xE6, 0x8E, 0xFE, 0x7E, 0x80, 0xBA, 0x69, 0x66, 0x65, 0x72, 0x57, 0xFD, 0x22, 0xAB, 0x93, 0x43, 0x5A, 0x5F, 0x79, + 0x73, 0x85, 0xB8, 0x1B, 0xB3, 0x35, 0xA4, 0x5E, 0x4E, 0xBC, 0x87, 0x39, 0xD2, 0xA1, 0x6F, 0x4E, 0xF2, 0xD4, 0x2A, 0xC4, 0x8D, 0x6A, 0x0F, 0x3A, + 0xFC, 0xA7, 0x2E, 0xE8, 0x33, 0x24, 0x82, 0xE2, 0x78, 0x2C, 0x5A, 0x42, 0x80, 0x96, 0xA4, 0x26, 0x63, 0x7F, 0x18, 0x1D, 0x29, 0x85, 0xCF, 0x5B, + 0x46, 0x9A, 0x8D, 0xCF, 0xED, 0x77, 0x3E, 0x39, 0x0A, 0x6B, 0x9F, 0x2E, 0xE3, 0xC6, 0x60, 0x54, 0xDE, 0xC1, 0xBA, 0xCC, 0x62, 0x40, 0x21, 0x45, + 0xCC, 0x2C, 0xF7, 0xA3, 0x2E, 0x1C, 0x33, 0xF4, 0x32, 0x81, 0xF4, 0xC1, 0xFA, 0x17, 0xA3, 0xB3, 0x50, 0x96, 0x1A, 0x3E, 0xB1, 0x94, 0x56, 0xFA, + 0x5F, 0xBC, 0xB1, 0x46, 0x2A, 0xA1, 0x7A, 0x14, 0x73, 0x40, 0x0D, 0x4A, 0x59, 0xFC, 0x98, 0xBE, 0x37, 0x26, 0x7C, 0x55, 0x55, 0xE6, 0x92, 0x6E, + 0x54, 0x35, 0xA9, 0x45, 0x0B, 0x30, 0x79, 0x58, 0xEF, 0xA8, 0x71, 0x27, 0x9E, 0xD3, 0x6B, 0xE4, 0xEF, 0xFF, 0xE1, 0x08, 0x06, 0x2E, 0xEB, 0xA4, + 0xCD, 0xC5, 0x2A, 0x49, 0x69, 0x2C, 0x60, 0x58, 0xCA, 0xF0, 0x15, 0x0D, 0x3E, 0x57, 0xD9, 0xB6, 0x03, 0xBF, 0xDE, 0x29, 0x51, 0x6C, 0x94, 0xC1, + 0x50, 0x02, 0x49, 0x0C, 0x0B, 0xE7, 0x3C, 0x59, 0xDB, 0x9B, 0x38, 0x5F, 0xCD, 0xCC, 0x3C, 0x4A, 0xD3, 0x8E, 0x97, 0xC1, 0x2B, 0x68, 0x73, 0x9D, + 0x42, 0x65, 0x74, 0x83, 0x2B, 0x0E, 0xB5, 0x9F, 0x27, 0x03, 0xBD, 0x68, 0xC8, 0xEA, 0xBD, 0xD0, 0x3D, 0x3F, 0x94, 0x34, 0x31, 0x7B, 0x3B, 0x38, + 0xEA, 0x2C, 0xED, 0xB0, 0xC6, 0x78, 0xBE, 0xA2, 0xFB, 0x6C, 0x65, 0x6F, 0x9C, 0xE3, 0x4F, 0x50, 0x39, 0x6A, 0x53, 0x22, 0x33, 0x35, 0x7D, 0xAB, + 0xC6, 0xE7, 0xA2, 0xFE, 0x24, 0xD7, 0x27, 0xCA, 0x84, 0x89, 0x4A, 0x80, 0xB9, 0x7B, 0xD8, 0x5D, 0x72, 0x17, 0x94, 0x46, 0xAA, 0x44, 0x0E, 0x96, + 0xB3, 0x81, 0xF0, 0xBD, 0xE6, 0xB9, 0xE7, 0xFF, 0xC5, 0xAE, 0xF9, 0x49, 0x1B, 0x40, 0xDA, 0xFA, 0x0C, 0xDF, 0x2D, 0x22, 0xAD, 0xA7, 0xD7, 0x15, + 0x9E, 0x12, 0x02, 0xB7, 0x21, 0x9C, 0x12, 0xE1, 0x22, 0x34, 0xB0, 0x27, 0x79, 0x5C, 0x0F, 0x89, 0xD6, 0x56, 0xE9, 0xE0, 0x9D, 0xD9, 0xEB, 0x15, + 0x7A, 0x56, 0xF7, 0x24, 0xF0, 0xF0, 0xA6, 0xCB, 0x5D, 0x11, 0x55, 0x56, 0x41, 0x3C, 0x84, 0xF2, 0x47, 0x61, 0xC6, 0x79, 0x3E, 0x7D, 0x7C, 0x7F, + 0x0A, 0x07, 0xFD, 0x65, 0x03, 0xBA, 0x71, 0xB7, 0xB8, 0x4A, 0x93, 0x6D, 0x29, 0x5F, 0x59, 0xCF, 0x4B, 0x55, 0x30, 0xF0, 0x69, 0xA6, 0x47, 0xDA, + 0xD9, 0x45, 0x09, 0x9B, 0xF5, 0x57, 0x18, 0x13, 0x3B, 0xC2, 0x0F, 0x77, 0xF5, 0x25, 0x3D, 0xF0, 0x82, 0xAE, 0x1A, 0xEE, 0x33, 0x19, 0x3F, 0xBD, + 0xF4, 0xB9, 0xF4, 0x39, 0x36, 0x8F, 0xF1, 0xEE, 0xB8, 0x01, 0xEE, 0x1F, 0xA6, 0x8D, 0x2D, 0x7A, 0x99, 0xF8, 0x90, 0x0C, 0xD7, 0xE5, 0xD4, 0xB5, + 0x08, 0x33, 0x0C, 0xC5, 0x07, 0xD8, 0xF3, 0xF2, 0xA0, 0xF0, 0x98, 0xDB, 0xA3, 0x12, 0x84, 0xF9, 0x95, 0x24, 0x32, 0x86, 0xFF, 0xC9, 0x65, 0x8B, + 0xB4, 0xDC, 0x04, 0x20, 0xC2, 0x37, 0x07, 0x59, 0xB8, 0xC5, 0x16, 0x42, 0x9B, 0x73, 0x2A, 0x8E, 0xD3, 0xC1, 0x80, 0xFC, 0x52, 0x0C, 0x80, 0x4D, + 0x4F, 0x78, 0xA5, 0x25, 0x05, 0x5C, 0xFD, 0xA7, 0xB5, 0x21, 0x86, 0xC1, 0xBA, 0xC9, 0xC1, 0x35, 0xF2, 0x0A, 0xD8, 0xCC, 0xCB, 0x47, 0x5C, 0xBC, + 0xA7, 0xB2, 0xC2, 0x47, 0xF2, 0x26, 0x23, 0xDE, 0x26, 0x65, 0xBE, 0x98, 0x21, 0xE4, 0xA9, 0xA4, 0x38, 0xF4, 0x2F, 0x05, 0x04, 0xB5, 0x80, 0xF0, + 0x1D, 0xE8, 0xFC, 0x05, 0x34, 0x99, 0x5B, 0x10, 0xA9, 0xF2, 0xEC, 0x81, 0xC5, 0xBC, 0x5A, 0x3B, 0x85, 0xC8, 0x70, 0xF3, 0x5D, 0x37, 0x2C, 0x10, + 0x5B, 0x4C, 0x87, 0x91, 0x17, 0x70, 0x8D, 0x53, 0x33, 0xC6, 0x7A, 0x67, 0x19, 0xAB, 0x7B, 0x8D, 0x80, 0xF5, 0xFB, 0x91, 0x8F, 0x9E, 0x0E, 0xAD, + 0xF9, 0x43, 0x2C, 0xC3, 0xDE, 0x07, 0x07, 0xC5, 0xC8, 0x83, 0xE3, 0xA9, 0x1D, 0x2C, 0xC3, 0x97, 0x8F, 0x45, 0xA2, 0x66, 0x9B, 0x64, 0x7D, 0xC9, + 0x89, 0xFD, 0xB8, 0xDF, 0xC1, 0x4B, 0xE7, 0xD8, 0x71, 0x97, 0x5A, 0x12, 0x32, 0xB7, 0x84, 0xDF, 0x20, 0xC4, 0x71, 0xD6, 0x17, 0x2B, 0x81, 0x19, + 0x70, 0xFF, 0x38, 0x55, 0xF1, 0xBC, 0xBA, 0x1F, 0x5B, 0xE7, 0x9A, 0xB0, 0xCC, 0xEE, 0x25, 0xA0, 0xD7, 0x31, 0xB0, 0xAA, 0x73, 0x8E, 0xE6, 0xCD, + 0x1C, 0xD8, 0x55, 0x40, 0x08, 0x0A, 0x97, 0xC4, 0xD2, 0xD5, 0x56, 0x29, 0xA4, 0x43, 0x91, 0xC5, 0xE9, 0x21, 0x30, 0xE3, 0x93, 0x5D, 0xF5, 0x39, + 0x59, 0x5A, 0x2C, 0xF1, 0x44, 0xE5, 0x30, 0x2D, 0xA2, 0xB2, 0x4F, 0x7C, 0xED, 0x09, 0x7A, 0x83, 0xA5, 0x09, 0xEE, 0xF4, 0x9A, 0xD0, 0xEA, 0x20, + 0x6E, 0xB9, 0x03, 0xAC, 0xCA, 0x58, 0xA6, 0x42, 0x39, 0x28, 0x75, 0xCC, 0x25, 0x0C, 0x25, 0xD7, 0x29, 0xEC, 0xED, 0x3C, 0x39, 0x1F, 0xBB, 0x5C, + 0x2B, 0xB9, 0x39, 0xEC, 0x01, 0xE0, 0xDB, 0xC0, 0x6F, 0xB7, 0x94, 0x33, 0x85, 0xD3, 0x22, 0xD8, 0x23, 0x15, 0x5B, 0xEB, 0x55, 0xE2, 0xD6, 0x3F, + 0x10, 0x2A, 0x58, 0x86, 0x37, 0x52, 0x85, 0x5B, 0xAB, 0xCF, 0x7B, 0x92, 0x28, 0x79, 0x63, 0xD3, 0x20, 0x7F, 0xDF, 0x92, 0x41, 0x2D, 0x75, 0xEB, + 0x5F, 0xDA, 0x84, 0x8E, 0xDD, 0xBC, 0x06, 0x53, 0xCD, 0x96, 0x07, 0xA5, 0xF2, 0x4B, 0x6F, 0xCD, 0xB6, 0x09, 0xAE, 0xC1, 0x2A, 0xC3, 0x56, 0xBA, + 0x13, 0x86, 0x43, 0xE5, 0xF0, 0xB6, 0xBC, 0x07, 0x0F, 0xAC, 0x5F, 0x79, 0x70, 0x3C, 0x88, 0x63, 0x16, 0x02, 0x95, 0x40, 0x0E, 0xF1, 0x18, 0x6D, + 0x41, 0x7D, 0xFA, 0x0F, 0xFE, 0xC3, 0x1A, 0x76, 0x13, 0xCD, 0x5E, 0xA6, 0xDF, 0x83, 0x6B, 0x9A, 0x4C, 0xF5, 0x53, 0xE6, 0x56, 0x89, 0xAD, 0x94, + 0x6A, 0xC9, 0x71, 0x7F, 0x02, 0x9B, 0x1C, 0x02, 0x27, 0x54, 0x05, 0x64, 0xD6, 0x62, 0x42, 0x00, 0xAD, 0x91, 0x0C, 0xC0, 0x54, 0xF2, 0x4C, 0xDA, + 0xE4, 0xD2, 0xC1, 0xC7, 0xA2, 0x1E, 0x90, 0x9C, 0x13, 0x05, 0xAC, 0x58, 0xB8, 0xB5, 0xDC, 0x3B, 0xC3, 0x72, 0x79, 0x32, 0xE9, 0xB5, 0xF0, 0x37, + 0x3A, 0xAE, 0xB1, 0xC2, 0x23, 0x06, 0x8C, 0x90, 0xE9, 0xB6, 0xB8, 0x33, 0xCE, 0xEE, 0xDD, 0x6C, 0x98, 0x64, 0x10, 0x03, 0xF3, 0x9C, 0x8C, 0x5F, + 0x91, 0x40, 0xB3, 0x80, 0x2C, 0x4D, 0x23, 0x82, 0xF3, 0xAA, 0xC2, 0xD8, 0xF5, 0x13, 0x82, 0x3F, 0xAE, 0x3D, 0xC3, 0x56, 0x94, 0x0C, 0xF7, 0x03, + 0x41, 0x7E, 0x97, 0xB4, 0x77, 0xD9, 0x27, 0x72, 0x96, 0x93, 0xBF, 0x19, 0xB2, 0x29, 0x66, 0xAF, 0xCE, 0xF2, 0x5D, 0x9E, 0xF2, 0xB8, 0x79, 0xE7, + 0x93, 0x2B, 0xBF, 0x23, 0xEF, 0xE8, 0x43, 0x1C, 0x07, 0xBF, 0x0C, 0xF0, 0x59, 0x9F, 0xD4, 0xDA, 0x79, 0xE6, 0x08, 0xEA, 0x6E, 0x2E, 0x1C, 0xC0, + 0x2D, 0x9E, 0xB8, 0xF6, 0xE7, 0x3D, 0xF0, 0x39, 0x74, 0xB8, 0x84, 0xFE, 0xA9, 0x02, 0xE1, 0xDC, 0xDE, 0x28, 0xE4, 0x5F, 0xCE, 0xD5, 0x00, 0x4C, + 0x62, 0xBD, 0xB1, 0x2C, 0x50, 0x4B, 0x53, 0x7D, 0x0A, 0xB7, 0x09, 0x6A, 0x60, 0x8A, 0x8D, 0x0B, 0xB2, 0x2A, 0x5C, 0xC1, 0x13, 0x9A, 0x65, 0x5F, + 0xD0, 0x6E, 0x85, 0xA4, 0x52, 0x38, 0x44, 0xEB, 0xC9, 0x6F, 0xDD, 0xDA, 0x9C, 0x7F, 0x87, 0xFA, 0x4B, 0x55, 0xD9, 0x7F, 0x0E, 0x61, 0x04, 0x15, + 0xD3, 0xA6, 0x07, 0xB1, 0x30, 0xC9, 0x01, 0x60, 0xC9, 0x1B, 0xF6, 0x2C, 0x27, 0x64, 0x4D, 0x6E, 0xBE, 0xE7, 0xD1, 0x3E, 0xA0, 0xC1, 0xBB, 0x23, + 0x82, 0x7C, 0x61, 0x5E, 0x3B, 0x3A, 0x0D, 0x9F, 0x52, 0x80, 0x42, 0x4C, 0x53, 0x58, 0xAC, 0xEA, 0x2D, 0xE5, 0x9C, 0x20, 0xD3, 0xDF, 0xE8, 0xCC, + 0xBA, 0x7B, 0x2A, 0xD6, 0xDE, 0xB9, 0x01, 0x6D, 0xCE, 0x6E, 0x39, 0x8D, 0x7A, 0xB2, 0x78, 0x31, 0x27, 0xD4, 0xC0, 0xFD, 0x2B, 0x36, 0x9B, 0x2A, + 0x74, 0x54, 0x3F, 0x57, 0x25, 0x2C, 0xBD, 0x14, 0xAC, 0x3F, 0x1B, 0x40, 0x88, 0xAC, 0x33, 0x71, 0x40, 0xCB, 0x38, 0x78, 0x85, 0x1E, 0x6F, 0x9F, + 0x5B, 0xC0, 0x38, 0xC1, 0xC4, 0x1A, 0xE1, 0x58, 0x38, 0x3A, 0x6C, 0x06, 0x81, 0xAE, 0xFD, 0xAB, 0x80, 0xE9, 0xE9, 0xF6, 0xA9, 0x1B, 0xFA, 0x1C, + 0xC9, 0x09, 0x3D, 0x10, 0x9C, 0x5A, 0x88, 0x69, 0x2F, 0x87, 0x47, 0xAA, 0xB2, 0x56, 0xC3, 0xEB, 0xAC, 0x70, 0x5E, 0xCB, 0xC3, 0x8B, 0x5E, 0xF1, + 0x4C, 0xE5, 0x65, 0x0B, 0x66, 0x41, 0x41, 0x35, 0x73, 0xF7, 0xEB, 0xC1, 0xA3, 0x0F, 0x1E, 0x04, 0x04, 0x32, 0x25, 0x7B, 0x04, 0x0E, 0x8E, 0xCE, + 0x49, 0x9B, 0xE0, 0xF0, 0x5A, 0x67, 0x6C, 0x50, 0x2B, 0x8A, 0x5A, 0xE6, 0xC0, 0x13, 0x18, 0x40, 0xC3, 0x0A, 0x05, 0xD3, 0x31, 0x5C, 0x4A, 0x25, + 0x5E, 0xC3, 0x3F, 0x88, 0x5D, 0xAC, 0x16, 0x2E, 0x0D, 0x24, 0x10, 0x76, 0xFE, 0xCC, 0x78, 0x4A, 0x4E, 0xD6, 0x9A, 0xEB, 0x5A, 0x8A, 0x15, 0x04, + 0x1D, 0x10, 0x31, 0x72, 0x8F, 0xD2, 0x7D, 0x12, 0x4F, 0xB1, 0xF1, 0xD1, 0x14, 0xE1, 0x5D, 0x9E, 0xA9, 0xB6, 0xB5, 0x14, 0x84, 0x28, 0xBE, 0xE3, + 0x41, 0x68, 0x48, 0xCB, 0x64, 0x1E, 0xFB, 0xBC, 0xA6, 0xB2, 0x17, 0xE0, 0x84, 0xE5, 0x15, 0x2C, 0x9B, 0xD0, 0x16, 0x4A, 0xDD, 0x5F, 0xC4, 0xA6, + 0xC7, 0xE2, 0xC8, 0xFC, 0x42, 0xDE, 0x77, 0xAA, 0x40, 0x86, 0x71, 0xDE, 0x49, 0x3F, 0x97, 0xE5, 0xB0, 0xD7, 0x18, 0x44, 0xC4, 0xCE, 0xEF, 0xC4, + 0x7F, 0x38, 0x26, 0x9D, 0xEA, 0x0E, 0x75, 0xCF, 0x18, 0xD5, 0x1B, 0x25, 0x20, 0x75, 0x8A, 0x1B, 0xB4, 0x2A, 0xDE, 0xE1, 0xBD, 0xA0, 0x21, 0xC8, + 0x8F, 0x18, 0xCA, 0x61, 0xCF, 0x3B, 0xA3, 0x60, 0x45, 0x16, 0x80, 0xA8, 0x5A, 0xA4, 0x34, 0x1A, 0x6F, 0x65, 0xC1, 0x46, 0x37, 0x6E, 0x8C, 0x66, + 0x2D, 0x56, 0xAA, 0x99, 0xA3, 0x67, 0xFD, 0x00, 0xCB, 0x4C, 0xD4, 0x67, 0x7B, 0xE5, 0x28, 0xCD, 0x32, 0xF1, 0xEA, 0xDD, 0x5C, 0x76, 0x2F, 0xD4, + 0xEE, 0x12, 0x9C, 0xED, 0x0D, 0xE3, 0x88, 0x0F, 0x3B, 0x08, 0x12, 0x8C, 0x00, 0x88, 0x99, 0x23, 0xC2, 0x4D, 0x46, 0xEC, 0x87, 0x9F, 0xC8, 0x97, + 0x9C, 0xBC, 0xC2, 0x72, 0x0E, 0xA4, 0x68, 0xD6, 0x13, 0x6B, 0x70, 0x50, 0xE5, 0xA6, 0x91, 0x64, 0x64, 0xCD, 0x29, 0x24, 0x27, 0x1C, 0x1A, 0x11, + 0x8C, 0x5D, 0x73, 0xDF, 0x2E, 0x4B, 0x35, 0x4A, 0x52, 0x42, 0x27, 0xA4, 0x12, 0x9C, 0x52, 0x66, 0x40, 0x6F, 0x7B, 0xD9, 0x71, 0x35, 0xC0, 0xE1, + 0x5D, 0x26, 0xDC, 0x8E, 0x57, 0xD2, 0x10, 0x23, 0x7F, 0xA9, 0xD3, 0x5A, 0xFC, 0x4F, 0x67, 0x98, 0x43, 0x9C, 0x2A, 0x0B, 0x40, 0xCE, 0x21, 0x86, + 0x53, 0x63, 0xD2, 0xB1, 0x96, 0x0B, 0xF2, 0x59, 0x0C, 0x26, 0xFC, 0xBF, 0x78, 0xD6, 0xDF, 0xB6, 0x52, 0xAD, 0x9D, 0x33, 0xFA, 0x85, 0x22, 0x98, + 0xF8, 0x77, 0x4F, 0x8A, 0xD6, 0xD7, 0x29, 0xD2, 0x85, 0x1B, 0x32, 0x2A, 0x30, 0xB2, 0xB9, 0xCB, 0x9F, 0x6E, 0xFF, 0x4D, 0xF6, 0xE7, 0xA9, 0x06, + 0x80, 0x1E, 0xB7, 0x09, 0xC9, 0x50, 0x1B, 0x61, 0xE6, 0x5D, 0x13, 0x16, 0xF1, 0xB2, 0x73, 0x22, 0x31, 0xE1, 0xC0, 0xAF, 0xDA, 0x9F, 0xB1, 0x51, + 0xC0, 0xBD, 0x9B, 0x3D, 0x28, 0xA4, 0x57, 0xFB, 0xFB, 0xBC, 0x6B, 0xB3, 0x34, 0x2A, 0x2D, 0x16, 0x5E, 0x9B, 0x28, 0x42, 0xA5, 0x2D, 0xC2, 0x31, + 0xB5, 0x44, 0xC8, 0xE6, 0x90, 0xC5, 0xBE, 0x28, 0x6D, 0xB3, 0xF2, 0x88, 0x30, 0x79, 0x5F, 0xB8, 0x7C, 0x0E, 0x96, 0x7C, 0xCB, 0x47, 0x77, 0x6D, + 0x52, 0x7B, 0x32, 0x75, 0x0C, 0x17, 0x3F, 0x8D, 0xDB, 0x23, 0x7F, 0xB9, 0xAE, 0x08, 0xD8, 0x05, 0x0A, 0xAC, 0x4D, 0x65, 0x00, 0x39, 0x06, 0x89, + 0x8B, 0xB7, 0x47, 0xF9, 0xF4, 0xBE, 0x53, 0x7B, 0x32, 0x78, 0x0A, 0x76, 0x8C, 0x0F, 0x40, 0x90, 0x76, 0x42, 0x6F, 0xB5, 0x06, 0x79, 0x96, 0xEB, + 0x51, 0x32, 0x83, 0xAB, 0x75, 0x78, 0xE8, 0x26, 0xA8, 0xA6, 0x1B, 0xC9, 0xBA, 0x1E, 0x65, 0x87, 0xDA, 0xF6, 0xB4, 0x4C, 0xC8, 0xD9, 0xB6, 0xD9, + 0x5F, 0x65, 0x9D, 0x37, 0x9E, 0x88, 0x6A, 0xCE, 0x52, 0x61, 0x07, 0x37, 0x0E, 0x16, 0x26, 0xE9, 0x10, 0x8A, 0x5F, 0x32, 0x5A, 0xB1, 0x47, 0x61, + 0x95, 0x25, 0x36, 0xFB, 0xCC, 0x77, 0xEB, 0x00, 0x2A, 0xE9, 0x47, 0x5B, 0x3D, 0x46, 0x7E, 0x63, 0xF3, 0x69, 0x05, 0x5B, 0x5F, 0x72, 0xBF, 0xAA, + 0x62, 0x06, 0xE0, 0x65, 0x23, 0x85, 0x87, 0xFD, 0xB4, 0x19, 0xC8, 0x15, 0x42, 0xBE, 0xEE, 0xE0, 0xFA, 0xB5, 0x2A, 0x87, 0x83, 0x13, 0xAF, 0x9B, + 0x2E, 0x27, 0xBC, 0xC8, 0x5A, 0xC5, 0x73, 0x80, 0x9F, 0xBE, 0xAF, 0x1A, 0xFC, 0x2A, 0x5F, 0x75, 0xF2, 0x70, 0x55, 0x71, 0x44, 0x60, 0x1A, 0x74, + 0xB1, 0x10, 0x84, 0x4E, 0x6E, 0xB8, 0x3E, 0x5C, 0x6C, 0xBB, 0xF6, 0x88, 0x77, 0xD8, 0xD0, 0xF8, 0x13, 0x82, 0x17, 0x15, 0x60, 0x52, 0x5E, 0xB6, + 0x2B, 0x59, 0x1A, 0x30, 0x03, 0xE4, 0x4C, 0xFE, 0x29, 0x8D, 0xA7, 0xEE, 0x43, 0xFC, 0x13, 0x8C, 0xE1, 0x4C, 0x77, 0x42, 0x23, 0xFD, 0xF5, 0x1A, + 0xC4, 0x69, 0x6C, 0x11, 0x5C, 0xB8, 0x7F, 0x96, 0xA2, 0xC3, 0xB1, 0x81, 0xC6, 0xB3, 0xAB, 0x45, 0x6F, 0xAA, 0x82, 0x1F, 0x62, 0x7A, 0x6D, 0x8F, + 0x56, 0xA8, 0x0A, 0xB4, 0x19, 0xA9, 0xD0, 0x69, 0xB5, 0x6D, 0xE0, 0x7F, 0x00, 0x1C, 0x35, 0xE9, 0x88, 0xC5, 0x92, 0x28, 0xC7, 0x27, 0xA5, 0x8E, + 0x20, 0x3E, 0x4F, 0x0D, 0x67, 0xA4, 0x83, 0x85, 0xEB, 0x39, 0xD9, 0x75, 0x18, 0xD7, 0xE8, 0x12, 0xE6, 0x82, 0x94, 0xB8, 0x23, 0x79, 0xAC, 0x5A, + 0x70, 0x17, 0x88, 0xD3, 0xE1, 0xA0, 0x07, 0xC0, 0x4B, 0xB2, 0xB7, 0x90, 0x14, 0x89, 0xC3, 0xB1, 0x07, 0x85, 0xC3, 0x39, 0x53, 0x06, 0xA9, 0xCE, + 0xA9, 0x15, 0xEF, 0x06, 0x66, 0xE3, 0xE0, 0xAC, 0x7D, 0xC9, 0x83, 0x21, 0x3F, 0x95, 0x36, 0x22, 0xBE, 0xCA, 0xE4, 0x37, 0x2E, 0xE8, 0x0C, 0xF1, + 0x59, 0xF9, 0xCE, 0x48, 0x79, 0xF6, 0xE9, 0x4C, 0x2A, 0xB2, 0x26, 0xA1, 0x01, 0x18, 0x14, 0x3B, 0xB5, 0xD8, 0xAE, 0xF0, 0xD1, 0xA5, 0x62, 0x83, + 0xDE, 0x0B, 0x64, 0x23, 0x40, 0xE7, 0xE0, 0x7E, 0xC6, 0xE2, 0x8F, 0x67, 0x15, 0xF3, 0x04, 0x6C, 0x99, 0xBD, 0xE7, 0xCC, 0xB0, 0xEC, 0xF6, 0x8D, + 0x8F, 0x79, 0x3E, 0x45, 0x90, 0x02, 0x7B, 0x6B, 0xED, 0xD8, 0x73, 0x7D, 0x79, 0x76, 0x6E, 0x0C, 0x81, 0xF0, 0xA0, 0x9D, 0x69, 0xC0, 0x0F, 0x46, + 0xC0, 0x5E, 0x0E, 0x36, 0x56, 0xF9, 0x55, 0x40, 0x62, 0xD0, 0xC3, 0x58, 0xB9, 0xFE, 0x5A, 0x80, 0xEA, 0x86, 0xDF, 0x14, 0x16, 0x7F, 0xD2, 0x61, + 0xA1, 0xEB, 0x90, 0x4C, 0x9A, 0x1F, 0x64, 0x16, 0xC3, 0x7F, 0xC2, 0x3C, 0x09, 0x04, 0xEE, 0x30, 0xB4, 0xA1, 0xC7, 0x5F, 0xA1, 0x2E, 0x38, 0xC8, + 0x00, 0x48, 0x69, 0x78, 0x75, 0x47, 0x75, 0x9D, 0xA8, 0x99, 0x6E, 0x26, 0xAD, 0x5F, 0xD4, 0xBB, 0xFC, 0x8B, 0xA3, 0x0B, 0x38, 0x57, 0x87, 0x7D, + 0xBA, 0x8A, 0x5B, 0xD7, 0x05, 0x17, 0x21, 0xDC, 0xF0, 0x15, 0x18, 0xE5, 0x07, 0xF3, 0xD4, 0xC2, 0x2E, 0x6C, 0x78, 0xD9, 0x2B, 0x8B, 0x53, 0x40, + 0x99, 0xD4, 0xE6, 0xD0, 0xD6, 0x66, 0x8E, 0x1A, 0x62, 0xC1, 0xB7, 0xC2, 0xE4, 0x34, 0xC6, 0x3A, 0xE8, 0xE5, 0xD7, 0xE5, 0x9E, 0x52, 0x52, 0xFF, + 0x93, 0xD7, 0x04, 0x42, 0x24, 0x51, 0x09, 0xA1, 0x9F, 0x9F, 0x5F, 0xE1, 0xB0, 0x9C, 0x6E, 0x10, 0x58, 0xC6, 0x9B, 0xDD, 0x69, 0xFB, 0xC9, 0xD0, + 0xC7, 0xDF, 0xDD, 0x3E, 0xD0, 0xBA, 0xEA, 0x6C, 0xC4, 0xB6, 0x20, 0x44, 0x5E, 0x23, 0x05, 0x0B, 0x2A, 0x24, 0xB3, 0xF2, 0x09, 0xA9, 0x6E, 0xC2, + 0x50, 0x94, 0x0E, 0x27, 0x4E, 0x84, 0xAB, 0xA8, 0xEA, 0xE8, 0xD3, 0x49, 0x1F, 0xAE, 0x1F, 0x54, 0xB1, 0xFB, 0x0E, 0xCF, 0xB2, 0x32, 0x56, 0x24, + 0x5A, 0xA5, 0x9F, 0x2D, 0x11, 0x1D, 0x2D, 0x28, 0xB4, 0x8E, 0x13, 0x86, 0xC9, 0xC6, 0x80, 0x27, 0xF3, 0x84, 0x9D, 0x33, 0xAB, 0x24, 0x3D, 0x8D, + 0x82, 0x86, 0x7D, 0x88, 0xDF, 0x92, 0x9B, 0xE8, 0xF1, 0xC4, 0x17, 0x60, 0x87, 0x3F, 0xD7, 0xF2, 0xA0, 0xAE, 0xD2, 0x42, 0x2E, 0x12, 0xFD, 0x82, + 0x54, 0xD4, 0xEF, 0x90, 0xFB, 0xAC, 0x42, 0x59, 0xF0, 0xB3, 0xAD, 0x65, 0xE3, 0x7A, 0x93, 0xEF, 0x56, 0x21, 0x88, 0x65, 0xC2, 0x02, 0xFB, 0x4D, + 0xC6, 0x59, 0xD8, 0xCF, 0x25, 0x37, 0xAB, 0xB2, 0xB7, 0x6C, 0x85, 0xED, 0x23, 0x5E, 0x3D, 0x81, 0x68, 0x67, 0x94, 0x47, 0xC5, 0xE0, 0xE5, 0x86, + 0x6F, 0xDB, 0x3A, 0xA1, 0x61, 0xF2, 0x6D, 0x92, 0x57, 0xE2, 0x95, 0x7D, 0x88, 0xE5, 0x17, 0x04, 0x23, 0x91, 0x7C, 0x8F, 0x0D, 0x2B, 0x15, 0xCA, + 0xBD, 0xCD, 0x9A, 0x10, 0x57, 0xAD, 0x53, 0x5E, 0xED, 0x0A, 0xD2, 0xBB, 0xC6, 0x00, 0x54, 0xF8, 0xF6, 0xD0, 0xC7, 0xC8, 0xF9, 0x4D, 0x28, 0xCA, + 0x73, 0x72, 0x0C, 0x63, 0x09, 0x8B, 0x23, 0x35, 0x8B, 0x90, 0x0B, 0x0C, 0x17, 0x04, 0x29, 0x88, 0x7A, 0x15, 0xAC, 0x2D, 0xBE, 0x3F, 0x78, 0x2C, + 0xED, 0x4F, 0xB5, 0x17, 0x91, 0x8A, 0xCB, 0x7F, 0x19, 0xC1, 0xF8, 0x02, 0x9A, 0x01, 0xFD, 0x24, 0x26, 0x93, 0x2E, 0xC9, 0x28, 0x9C, 0x91, 0x8E, + 0x99, 0x63, 0x5B, 0x6A, 0xB6, 0xCA, 0x05, 0xAC, 0x30, 0xE7, 0x19, 0x31, 0xC3, 0x29, 0x16, 0x38, 0xFA, 0x1B, 0x68, 0x85, 0x33, 0xA9, 0x3A, 0x08, + 0x41, 0x48, 0x80, 0x28, 0x54, 0x36, 0xE9, 0x61, 0xE3, 0x91, 0x26, 0xC4, 0xC8, 0xD5, 0x47, 0x5A, 0x18, 0xC3, 0xA3, 0x72, 0x8A, 0x7F, 0xDC, 0x0F, + 0x48, 0x95, 0x28, 0x71, 0xF4, 0x4F, 0x18, 0x91, 0xE3, 0x20, 0x8B, 0xC1, 0xCA, 0x02, 0xB7, 0xC0, 0x5D, 0x54, 0x56, 0x80, 0xEF, 0xBC, 0x64, 0x23, + 0xF1, 0x66, 0x08, 0x47, 0xBE, 0x32, 0x56, 0xB5, 0xF8, 0x64, 0x00, 0x3F, 0x61, 0x95, 0xD6, 0x26, 0x92, 0x34, 0x8D, 0xB6, 0x48, 0xEE, 0x40, 0x98, + 0xF3, 0x69, 0xD0, 0xE8, 0x37, 0x1A, 0x53, 0xA1, 0x75, 0x04, 0xA5, 0x06, 0x4E, 0xC8, 0x73, 0x26, 0x30, 0x00, 0x46, 0xE9, 0xAA, 0x15, 0x23, 0x5C, + 0xC7, 0xBC, 0x57, 0xCB, 0x1D, 0x06, 0xAA, 0xD4, 0x2A, 0x46, 0xE2, 0x5E, 0x77, 0x8B, 0x2D, 0xED, 0x31, 0x97, 0x3D, 0xE1, 0x10, 0x63, 0x7B, 0x63, + 0x9C, 0x71, 0xD9, 0x07, 0xFA, 0x6C, 0x75, 0x15, 0x8E, 0xFA, 0xE8, 0x6B, 0x0C, 0xB3, 0xCB, 0xF3, 0x02, 0x65, 0xB8, 0x37, 0x1F, 0x5B, 0xE9, 0xE2, + 0x90, 0xF8, 0xEF, 0xA0, 0x70, 0xFD, 0x21, 0x5A, 0x06, 0x98, 0x64, 0xF6, 0xCF, 0x79, 0x45, 0x6E, 0xE5, 0xC7, 0xE7, 0x60, 0xD9, 0x46, 0xC9, 0xBB, + 0xF3, 0x46, 0x3C, 0x07, 0x61, 0x8F, 0x72, 0x46, 0xD9, 0xC2, 0xFF, 0x9B, 0xD3, 0xAA, 0x03, 0x40, 0x61, 0x49, 0xCB, 0xC1, 0xDF, 0x59, 0x31, 0xB1, + 0xFD, 0x8C, 0x92, 0xCA, 0x79, 0x0F, 0xB3, 0xC6, 0xC4, 0x1C, 0xAF, 0x9F, 0x2A, 0x89, 0x28, 0x9A, 0xC5, 0xF4, 0x54, 0xB3, 0xFB, 0x54, 0xB1, 0xED, + 0xCD, 0x10, 0x03, 0x79, 0xDD, 0x98, 0x2B, 0x81, 0xCE, 0x75, 0x58, 0x7E, 0xD9, 0x56, 0x2F, 0xFB, 0x38, 0xD3, 0x45, 0x65, 0x08, 0xD9, 0x52, 0x46, + 0x72, 0x78, 0xC8, 0xB8, 0xD1, 0x37, 0x83, 0x54, 0x1E, 0xC6, 0x4A, 0x63, 0xA3, 0x31, 0x48, 0x73, 0x28, 0xA5, 0xCC, 0x85, 0x2E, 0xC2, 0xA1, 0xAD, + 0x2E, 0x04, 0x92, 0xC5, 0x4F, 0xB2, 0xFE, 0x1A, 0xCC, 0x45, 0x9F, 0x1A, 0x29, 0x53, 0x3F, 0xFE, 0xB9, 0x24, 0xD4, 0x50, 0x8D, 0x20, 0x7F, 0xB9, + 0x7E, 0x14, 0x6D, 0x6E, 0x85, 0x2F, 0x10, 0xFF, 0x06, 0x7F, 0x41, 0x9A, 0x9D, 0x69, 0xEA, 0x61, 0xE3, 0x1E, 0x34, 0xF4, 0xC7, 0x96, 0x36, 0xCC, + 0x62, 0x63, 0x4B, 0x96, 0x64, 0x34, 0x85, 0xE7, 0xB0, 0xBA, 0x5C, 0xDF, 0xDE, 0x5D, 0xB1, 0x76, 0xBB, 0xB0, 0x5B, 0xC0, 0x9E, 0x05, 0x6C, 0x0B, + 0x92, 0x94, 0x86, 0x7A, 0x5E, 0x99, 0xDE, 0xB2, 0xD9, 0xCC, 0x19, 0x8A, 0xE2, 0x84, 0x8F, 0x50, 0xF7, 0x70, 0x04, 0x55, 0x10, 0xDF, 0xC0, 0x00, + 0x12, 0xE1, 0x69, 0x3E, 0xC2, 0x58, 0xF2, 0x46, 0xFE, 0x11, 0xFC, 0x73, 0x52, 0x9D, 0xCB, 0x9D, 0x2A, 0x3E, 0xC9, 0x43, 0x94, 0x25, 0x99, 0xC1, + 0x7E, 0x42, 0xC6, 0xFA, 0xBF, 0x31, 0xAB, 0xF4, 0xAE, 0x66, 0xD7, 0x51, 0xEA, 0x9C, 0xD4, 0x1B, 0xBF, 0x66, 0xC6, 0xA8, 0x6B, 0xA0, 0x1F, 0x46, + 0xE1, 0xE0, 0x27, 0xF8, 0x4A, 0x2D, 0x30, 0xD7, 0x7C, 0x74, 0xDD, 0x22, 0x46, 0x5D, 0x81, 0xF7, 0x00, 0x3B, 0x0A, 0xC4, 0xA9, 0xF2, 0xF1, 0x18, + 0x1C, 0x58, 0xFE, 0x69, 0xF6, 0x2B, 0x9A, 0x54, 0xB8, 0x8D, 0x05, 0x1F, 0xBC, 0x00, 0x7F, 0x8F, 0x38, 0xC9, 0x97, 0x0C, 0xB4, 0x29, 0x6B, 0x00, + 0x02, 0x21, 0x70, 0x64, 0x9E, 0x7C, 0x95, 0xC1, 0x1D, 0x7F, 0x4E, 0x98, 0xC3, 0x71, 0x02, 0x35, 0xEE, 0xC1, 0x6F, 0x68, 0x1E, 0xC0, 0xE3, 0xE9, + 0x7F, 0x64, 0x44, 0x5F, 0xA4, 0xC2, 0x81, 0xF3, 0xC8, 0xFD, 0x1D, 0x74, 0x6E, 0xE0, 0xB4, 0x57, 0x54, 0xAE, 0x0C, 0x67, 0xBD, 0x89, 0x88, 0x10, + 0x20, 0x30, 0x9C, 0xBE, 0xF1, 0x02, 0x8E, 0xB5, 0x8B, 0x7A, 0xD3, 0xA1, 0xAB, 0x23, 0xBF, 0x81, 0x32, 0x02, 0x1F, 0x4A, 0x9C, 0x81, 0x98, 0xEB, + 0x62, 0x0D, 0x40, 0x03, 0xF2, 0x3E, 0xA4, 0x5B, 0xBB, 0x54, 0x7B, 0xAA, 0x75, 0xF2, 0xEB, 0xE2, 0xA5, 0xC3, 0x8F, 0xA3, 0x8B, 0xCF, 0x19, 0xC1, + 0x4A, 0xA1, 0xD5, 0xC4, 0xEA, 0xB1, 0x0F, 0xAA, 0x64, 0xA1, 0x76, 0x6A, 0x1B, 0x84, 0xF6, 0xC8, 0x8E, 0xE6, 0xEC, 0x53, 0x8A, 0x98, 0x72, 0x3A, + 0x16, 0x1C, 0xEA, 0xA1, 0x4D, 0x43, 0x5D, 0xCD, 0xF1, 0x16, 0xE3, 0x89, 0x6A, 0x4D, 0xB4, 0xE6, 0xF1, 0x6C, 0x68, 0x1F, 0x3D, 0x13, 0x70, 0xCB, + 0x45, 0x8D, 0x09, 0xBA, 0x06, 0x43, 0x07, 0xC2, 0x1A, 0xFD, 0x41, 0xC3, 0x25, 0x19, 0x57, 0x5E, 0x86, 0x37, 0x1E, 0xFA, 0x81, 0xB8, 0x1B, 0x80, + 0xDF, 0xA1, 0xE8, 0xB5, 0xF5, 0xC2, 0xD9, 0xF7, 0xCB, 0x25, 0x10, 0x5B, 0x52, 0x5A, 0x57, 0xDD, 0x5F, 0xA6, 0x65, 0x26, 0x69, 0x56, 0x7C, 0x76, + 0xBD, 0xEF, 0x50, 0x16, 0xEB, 0xE0, 0x0A, 0x03, 0xC8, 0x3D, 0xF7, 0xFC, 0x7A, 0x0D, 0x02, 0xD3, 0xF7, 0xAD, 0x74, 0xFB, 0xE9, 0x7C, 0xB7, 0x60, + 0x5F, 0x88, 0x81, 0x09, 0x47, 0xCF, 0x13, 0x7E, 0xD9, 0xFF, 0x87, 0xFE, 0x5A, 0xF4, 0xD3, 0x28, 0x38, 0xF4, 0xA3, 0xE8, 0x9F, 0x81, 0xDE, 0x80, + 0xE9, 0x14, 0x1E, 0xC6, 0x0B, 0x37, 0x61, 0x1C, 0x1A, 0x7A, 0xB6, 0xCB, 0xDF, 0x52, 0x85, 0xDF, 0xBF, 0xEC, 0x89, 0x3F, 0xF5, 0xC4, 0xF4, 0xEB, + 0xE9, 0xC0, 0x1F, 0xEC, 0xF9, 0x1F, 0x38, 0xFD, 0x03, 0x66, 0xFB, 0x8F, 0xD4, 0x96, 0x4E, 0xC9, 0xD0, 0xA7, 0x5B, 0x17, 0xAF, 0x10, 0x76, 0xDA, + 0x73, 0x65, 0x51, 0x32, 0x7C, 0xFC, 0x8A, 0x4E, 0xF4, 0x0E, 0x6E, 0xA1, 0x0D, 0xC5, 0xB8, 0x1F, 0x5A, 0x19, 0x91, 0x37, 0x7A, 0x44, 0xC3, 0x34, + 0x20, 0xAD, 0xCC, 0xC1, 0x82, 0x9E, 0xB5, 0x4B, 0xCD, 0xBB, 0x31, 0xF8, 0x7B, 0x71, 0x6B, 0x0D, 0x3B, 0x50, 0xCC, 0x22, 0xB3, 0xC0, 0x9D, 0xA1, + 0xB7, 0xE4, 0x05, 0xC7, 0x3F, 0x53, 0x76, 0x69, 0x7F, 0x5D, 0x66, 0x87, 0x9B, 0xE1, 0xCD, 0x83, 0x47, 0xD0, 0xB2, 0xAA, 0x54, 0x27, 0x99, 0x92, + 0xF2, 0xF2, 0x58, 0xD9, 0x82, 0x08, 0x04, 0x7B, 0x84, 0x09, 0xED, 0x8F, 0x4F, 0x58, 0xF8, 0xBF, 0x03, 0xB3, 0x2B, 0xFE, 0xA7, 0xF1, 0x4C, 0x59, + 0x62, 0x48, 0x96, 0x57, 0xE4, 0x32, 0x60, 0xC2, 0x00, 0x45, 0x04, 0x65, 0x79, 0xC2, 0x00, 0x15, 0x1A, 0x19, 0x44, 0x6A, 0x0E, 0x26, 0xE8, 0x7D, + 0x5E, 0x00, 0x39, 0x45, 0x1C, 0x11, 0x2C, 0xE4, 0x28, 0x05, 0x1C, 0x85, 0x55, 0x2C, 0xCC, 0x0B, 0x28, 0x50, 0xA4, 0xF4, 0x39, 0x73, 0x46, 0x82, + 0x57, 0xBA, 0x37, 0x17, 0xD1, 0x08, 0x45, 0xE8, 0x41, 0x2E, 0xB4, 0x04, 0xFB, 0x97, 0x9B, 0x57, 0x48, 0x06, 0x51, 0xA9, 0xE9, 0x1B, 0x07, 0xA5, + 0x67, 0xCD, 0x47, 0x52, 0x26, 0xF0, 0xD2, 0x05, 0xD4, 0xD2, 0xD7, 0x64, 0x3B, 0xB9, 0x07, 0xBD, 0xF5, 0x70, 0xEB, 0x0E, 0xFD, 0xAC, 0xD5, 0x04, + 0xD8, 0x49, 0x0D, 0x72, 0xF9, 0x5A, 0x71, 0xA4, 0x15, 0x81, 0xE3, 0x00, 0x5C, 0x0A, 0xD5, 0xF6, 0xCF, 0xC7, 0x78, 0x2F, 0xD6, 0x78, 0xBC, 0x6E, + 0x9B, 0xCF, 0x87, 0xFF, 0xB5, 0x3B, 0x7A, 0x92, 0xCF, 0x45, 0xFF, 0xD2, 0x1B, 0xD3, 0x29, 0x21, 0xE7, 0x95, 0x47, 0x34, 0xE3, 0x19, 0x1F, 0x3C, + 0x17, 0x83, 0xE3, 0x78, 0xD1, 0x25, 0x9D, 0x44, 0x48, 0x9B, 0x07, 0xA8, 0x80, 0xA0, 0xF7, 0xC5, 0x19, 0xDB, 0x1B, 0xAB, 0xE7, 0xC1, 0x60, 0x09, + 0x00, 0xB9, 0x0C, 0xBA, 0x76, 0x47, 0xA2, 0x28, 0x08, 0x20, 0x89, 0xB3, 0x9F, 0xC1, 0x56, 0x4C, 0xF5, 0x8F, 0xED, 0x59, 0xAC, 0xB3, 0xD8, 0x21, + 0xC2, 0x30, 0x23, 0x0A, 0xA5, 0x78, 0xAB, 0xAB, 0xC7, 0xAD, 0xC7, 0x79, 0x32, 0xE7, 0x07, 0xC5, 0x7E, 0xB8, 0xB7, 0x39, 0x05, 0x98, 0x4C, 0x5A, + 0xFF, 0x44, 0xF9, 0x6D, 0xD5, 0x85, 0x53, 0xCC, 0x8A, 0xC1, 0x09, 0x80, 0x9A, 0xCA, 0xDB, 0x72, 0xD3, 0x83, 0xAF, 0xDE, 0x6D, 0x43, 0x54, 0xF9, + 0x12, 0x96, 0xA0, 0xAE, 0x60, 0xD8, 0x78, 0x34, 0x9B, 0xF5, 0x66, 0x92, 0x39, 0x55, 0xAD, 0x92, 0x65, 0x39, 0xE6, 0xC7, 0x8D, 0x9E, 0x10, 0x64, + 0x35, 0x86, 0x84, 0x11, 0x05, 0x14, 0x4B, 0x33, 0x9A, 0x10, 0x55, 0xB4, 0xC8, 0x7C, 0x00, 0x3C, 0x2F, 0xC2, 0xC1, 0x92, 0x46, 0x18, 0xD8, 0xC0, + 0xA7, 0x19, 0x9B, 0x37, 0x36, 0xE3, 0x1D, 0x11, 0x54, 0xF6, 0x73, 0x24, 0x51, 0x48, 0x7F, 0x4B, 0x9E, 0xC9, 0x5C, 0x9F, 0x0D, 0x78, 0xE7, 0x46, + 0xBE, 0x1F, 0xC3, 0xD0, 0x5E, 0x5E, 0x7A, 0xE7, 0x09, 0x50, 0xF5, 0xA3, 0xA2, 0xC7, 0x45, 0xE9, 0x5E, 0xA5, 0x8F, 0xF1, 0xF7, 0x9D, 0x8C, 0xA8, + 0x2B, 0x3B, 0x57, 0xAE, 0x88, 0xE2, 0x96, 0x0F, 0xCC, 0x78, 0x34, 0x96, 0x0D, 0x6C, 0xD1, 0x3A, 0xE8, 0xBF, 0xF8, 0x8C, 0x6C, 0x7F, 0x0D, 0x0D, + 0xEA, 0x2D, 0x7C, 0x72, 0x73, 0x70, 0x13, 0x1A, 0xA1, 0x31, 0x24, 0x80, 0x5F, 0xFF, 0xFD, 0xC9, 0xBE, 0xD6, 0x1C, 0x1A, 0xB1, 0x85, 0x59, 0x74, + 0xC4, 0x3C, 0x1C, 0x86, 0x0A, 0xE3, 0xB7, 0x3F, 0xBC, 0x2C, 0x1B, 0x0A, 0x4D, 0xC2, 0x8F, 0x82, 0x9F, 0x3C, 0xF9, 0xA8, 0xD2, 0xC7, 0x62, 0xDB, + 0x99, 0x9D, 0x7B, 0xD6, 0x8E, 0xDB, 0xF1, 0x04, 0x38, 0x9B, 0x6C, 0x63, 0x9F, 0x50, 0x19, 0x5C, 0x62, 0x17, 0x21, 0xE7, 0xCC, 0xC5, 0xCF, 0x1F, + 0xA0, 0xF6, 0xC5, 0x57, 0xA5, 0xEF, 0xF8, 0x47, 0xE2, 0x74, 0x43, 0x44, 0x23, 0x54, 0x4B, 0x90, 0xFB, 0x81, 0xD3, 0xE9, 0x12, 0x41, 0x5F, 0x10, + 0x54, 0xC4, 0x0C, 0x40, 0x8B, 0x61, 0x4B, 0x37, 0xFE, 0x7A, 0xC8, 0xD2, 0xC7, 0xE9, 0x36, 0xF0, 0x12, 0xCC, 0x70, 0x07, 0x52, 0x4A, 0x05, 0x21, + 0x8A, 0xFE, 0x3C, 0x0B, 0x16, 0x42, 0x85, 0x90, 0x7A, 0x0F, 0x17, 0x2B, 0xF3, 0xFE, 0xDF, 0x71, 0x0A, 0x14, 0xFC, 0xA9, 0x31, 0xD2, 0x8F, 0x4B, + 0xB8, 0xEC, 0xEE, 0x41, 0x1A, 0xFA, 0x4A, 0x58, 0x4A, 0xE1, 0xF7, 0x92, 0x0F, 0xCA, 0x49, 0xF4, 0xD8, 0x5B, 0x88, 0x30, 0xED, 0x81, 0xF9, 0x83, + 0x83, 0x38, 0x44, 0xAE, 0x17, 0xB1, 0x75, 0x3E, 0x57, 0xB4, 0x1C, 0x78, 0x4E, 0x23, 0xBE, 0x04, 0xB9, 0x0A, 0x05, 0x85, 0x2A, 0x12, 0x8C, 0xD7, + 0xC4, 0x63, 0x29, 0xD3, 0x6F, 0x20, 0x7E, 0x3A, 0xCE, 0x14, 0xF2, 0xF4, 0x55, 0x6F, 0x3C, 0xF1, 0x44, 0x8E, 0xFA, 0x2F, 0x0A, 0x1C, 0xF1, 0xED, + 0x29, 0x80, 0x36, 0x00, 0x4F, 0x14, 0xE3, 0xE7, 0xF9, 0x48, 0x04, 0x03, 0xF0, 0x2A, 0xEB, 0xE0, 0xE6, 0xA2, 0x13, 0x4F, 0x09, 0x5A, 0x06, 0x90, + 0x48, 0x2A, 0x14, 0x6A, 0x98, 0x10, 0x3D, 0x24, 0xC8, 0x45, 0xDE, 0xAC, 0xDA, 0xEE, 0xC3, 0xE8, 0x9A, 0xC0, 0xA6, 0x1E, 0xC2, 0x80, 0xC1, 0xCE, + 0x32, 0x41, 0x14, 0x6B, 0xC8, 0xF7, 0xAB, 0xE4, 0x90, 0x9E, 0xDE, 0xB6, 0x22, 0x88, 0x30, 0x96, 0xF6, 0x63, 0x39, 0x97, 0x1F, 0xD6, 0xA0, 0x19, + 0xB3, 0x4A, 0x8C, 0xDC, 0x1F, 0x80, 0x5F, 0xB5, 0x0A, 0x98, 0x6B, 0x99, 0x71, 0x07, 0x84, 0xB1, 0x7C, 0xD0, 0xDB, 0xAF, 0xF5, 0x72, 0x98, 0x7F, + 0x01, 0x15, 0x28, 0x39, 0xA0, 0xB0, 0x9F, 0xE1, 0x5F, 0xFA, 0xF2, 0xF0, 0xFA, 0x7F, 0xC8, 0xA1, 0x89, 0x8A, 0xD3, 0x8C, 0x90, 0x0D, 0x69, 0xBA, + 0xA9, 0x11, 0xDC, 0xA5, 0x12, 0x7D, 0x3F, 0x82, 0x53, 0xF0, 0x57, 0x7D, 0x12, 0xE7, 0xF4, 0x33, 0x2A, 0xA9, 0xF5, 0xFE, 0x7C, 0xC1, 0x77, 0xB9, + 0x7A, 0xBE, 0xD9, 0x13, 0x58, 0x4E, 0xFE, 0x6D, 0xF4, 0x05, 0xD9, 0xD2, 0x17, 0x97, 0x05, 0x81, 0x01, 0x05, 0x72, 0x36, 0x88, 0x99, 0x23, 0xF8, + 0x9B, 0x76, 0xB4, 0x5B, 0x8E, 0xA4, 0x8F, 0xAB, 0xEF, 0x60, 0xA9, 0xC9, 0xD6, 0x7E, 0xE4, 0xDA, 0xB4, 0xAB, 0xE0, 0x9E, 0xE8, 0x71, 0x0A, 0x51, + 0xA0, 0x33, 0xDC, 0x9D, 0x44, 0x17, 0x17, 0x24, 0x94, 0x21, 0x40, 0x44, 0xE2, 0x6D, 0xD3, 0xF0, 0x51, 0x73, 0xC5, 0xF7, 0xC4, 0xBC, 0x92, 0xB8, + 0x8E, 0x96, 0xAE, 0x4A, 0x3E, 0x19, 0x73, 0xA4, 0x1B, 0xA7, 0x09, 0x4F, 0xF7, 0x17, 0x90, 0x6B, 0xD4, 0xC3, 0x3B, 0x16, 0x48, 0xA4, 0x59, 0x18, + 0xD0, 0xAB, 0x80, 0x70, 0x60, 0x3B, 0x36, 0x0A, 0x8F, 0xC1, 0x58, 0x12, 0xFF, 0xA2, 0x37, 0x1E, 0x61, 0x53, 0x41, 0xD6, 0x3A, 0xB9, 0x39, 0x5C, + 0x33, 0xAD, 0xEF, 0xE2, 0x70, 0x7D, 0x30, 0x13, 0xDB, 0xFD, 0xF0, 0xC7, 0x9A, 0x1B, 0x16, 0x94, 0x71, 0x15, 0x1C, 0x7E, 0xB8, 0x5C, 0x33, 0x67, + 0x7E, 0xCE, 0xB2, 0xCF, 0xD7, 0x8A, 0x94, 0x2A, 0x84, 0x56, 0x3D, 0x9B, 0x07, 0xF8, 0x1C, 0xFF, 0x88, 0xF7, 0xE6, 0xA6, 0xB1, 0x5D, 0x61, 0x3F, + 0x68, 0x88, 0x07, 0xC3, 0x7E, 0x25, 0xFF, 0x0C, 0xC6, 0xC7, 0xA5, 0xD6, 0xB3, 0x67, 0x00, 0x4A, 0xC0, 0x87, 0xC4, 0x37, 0x68, 0x4D, 0x7B, 0x07, + 0x27, 0x4B, 0x86, 0x30, 0x4C, 0x6C, 0x4B, 0xF6, 0x8C, 0x9E, 0xAE, 0x4F, 0xFA, 0x08, 0x0E, 0x34, 0x78, 0xE2, 0xA6, 0x93, 0xF5, 0xBB, 0x78, 0x38, + 0xB2, 0x95, 0x3A, 0xEC, 0x35, 0x11, 0x40, 0x8C, 0x10, 0xC4, 0x18, 0x47, 0x00, 0x61, 0x04, 0xC1, 0x62, 0x80, 0x30, 0x82, 0x00, 0x00, 0x00, 0x20, + 0x80, 0x30, 0x82, 0x00, 0x21, 0xE4, 0x08, 0x62, 0x80, 0x10, 0x42, 0x20, 0x80, 0x00, 0x62, 0xA4, 0x18, 0x20, 0x8C, 0x20, 0x00, 0x00, 0x00, 0x08, + 0xA0, 0x42, 0x84, 0x40, 0x00, 0x01, 0xC4, 0x08, 0x41, 0x8C, 0x71, 0x04, 0x10, 0x46, 0x10, 0x20, 0x84, 0x90, 0x22, 0x04, 0x31, 0xC0, 0x18, 0x00, + 0x08, 0x23, 0x8C, 0x10, 0xC4, 0x18, 0xE3, 0x00, 0x61, 0x14, 0x00, 0x61, 0x84, 0x11, 0x04, 0x08, 0x21, 0x84, 0x00, 0x08, 0x20, 0x46, 0x83, 0x01, + 0xC2, 0x08, 0x02, 0x00, 0x00, 0x80, 0x00, 0xC2, 0x08, 0x36, 0xBC, 0x90, 0x22, 0x84, 0x32, 0xC2, 0x08, 0x02, 0x84, 0x10, 0x42, 0x00, 0x31, 0x06, + 0x18, 0x40, 0x00, 0x61, 0x04, 0x01, 0x42, 0xC8, 0x11, 0xC4, 0x00, 0x21, 0x84, 0x40, 0x00, 0x01, 0xC4, 0x08, 0x41, 0x8C, 0x71, 0x04, 0x10, 0x36, + 0x02, 0x10, 0x40, 0x18, 0x41, 0x80, 0x10, 0x42, 0x08, 0x80, 0x00, 0x62, 0x04, 0x31, 0x20, 0x04, 0x31, 0x40, 0x08, 0x21, 0x10, 0x40, 0x00, 0x31, + 0x42, 0x10, 0x63, 0x1C, 0x09, 0x63, 0x80, 0x01, 0x04, 0x10, 0x46, 0x10, 0x20, 0x06, 0x63, 0x19, 0x40, 0x38, 0x61, 0x84, 0x20, 0xC6, 0x18, 0x07, + 0xC5, 0x18, 0x60, 0x00, 0x01, 0x84, 0x11, 0x04, 0x08, 0x21, 0x84, 0x0C, 0x00, 0x00, 0x00, 0x80, 0x00, 0xC2, 0x08, 0x02, 0x84, 0x10, 0x52, 0x84, + 0x20, 0x06, 0x18, 0x03, 0x00, 0x61, 0x84, 0x11, 0x82, 0x18, 0x07, 0x42, 0x10, 0x03, 0x8C, 0x01, 0x80, 0x30, 0xC2, 0x08, 0x41, 0x8C, 0x71, 0x23, + 0x04, 0x31, 0xC0, 0x18, 0x00, 0x08, 0x23, 0x8C, 0x10, 0xC4, 0x38, 0x10, 0x82, 0x18, 0x60, 0x8C, 0x04, 0x64, 0x14, 0x01, 0x42, 0x28, 0x9C, 0x21, + 0xC2, 0x28, 0x23, 0x04, 0x31, 0xC6, 0x38, 0x28, 0xC6, 0x00, 0x03, 0x08, 0x20, 0x8C, 0x20, 0x40, 0x54, 0x0C, 0x66, 0x00, 0xE1, 0x84, 0x11, 0x82, + 0x18, 0x63, 0x9C, 0x00, 0x00, 0x00, 0x00, 0x04, 0x10, 0x46, 0x30, 0x30, 0x40, 0x18, 0x41, 0x00, 0x00, 0x00, 0x10, 0x40, 0x85, 0x08, 0x81, 0x00, + 0x02, 0x88, 0x11, 0x82, 0x80, 0x20, 0x06, 0x08, 0x21, 0x04, 0x02, 0x08, 0x8A, 0x00, 0x04, 0x10, 0x46, 0x10, 0x20, 0x84, 0x90, 0x22, 0x04, 0x31, + 0xC0, 0x18, 0x00, 0x08, 0x23, 0x8C, 0x08, 0x03, 0x84, 0x11, 0x04, 0x00, 0x00, 0x00, 0x81, 0x41, 0x84, 0x10, 0x00, 0x01, 0xC4, 0x08, 0x62, 0x80, + 0x01, 0x21, 0x88, 0x01, 0xC6, 0x00, 0x28, 0xC6, 0x00, 0x03, 0x08, 0x20, 0x8C, 0x20, 0x58, 0x0C, 0x10, 0x46, 0x10, 0x00, 0x00, 0x00, 0x04, 0x10, + 0x36, 0x02, 0x10, 0x40, 0x18, 0x41, 0x80, 0x10, 0x42, 0x08, 0x80, 0xB1, 0xC6, 0x8B, 0x80, 0x12, 0x46, 0x31, 0x8E, 0x5C, 0x7A, 0xB3, 0xCE, 0xDC, + 0x9D, 0x75, 0x7C, 0x39, 0xB4, 0x3E, 0x50, 0x50, 0x2F, 0x6C, 0xFE, 0x67, 0x89, 0x8D, 0x55, 0xA2, 0xDF, 0xF8, 0xE6, 0x29, 0xCE, 0x9E, 0x55, 0x7A, + 0x94, 0xB6, 0xB5, 0xB3, 0x4E, 0xA3, 0x1D, 0x7C, 0x9D, 0xAF, 0xBA, 0xF5, 0xA8, 0xEC, 0x61, 0xB0, 0xE0, 0xA3, 0x06, 0xEB, 0x5A, 0x27, 0x95, 0xAF, + 0xEA, 0xD4, 0x93, 0xDD, 0x53, 0x8E, 0xFC, 0x18, 0x7B, 0x45, 0x6C, 0xFC, 0xBC, 0xF1, 0x56, 0xAF, 0xBA, 0xF1, 0xC6, 0x59, 0x1B, 0x61, 0x62, 0x38, + 0x6F, 0x1D, 0xD1, 0xA0, 0xBD, 0xF7, 0x84, 0x71, 0x30, 0x82, 0x11, 0x46, 0xAE, 0x2A, 0xD3, 0x9D, 0xDF, 0x62, 0x5C, 0x3E, 0x49, 0xA4, 0x7D, 0x6E, + 0x72, 0x49, 0x90, 0xFF, 0x57, 0x7F, 0xE2, 0x57, 0x6F, 0x23, 0x94, 0xFA, 0x84, 0x27, 0xAB, 0x4F, 0xB3, 0x2F, 0xA2, 0x23, 0x90, 0xA8, 0x97, 0x21, + 0xD1, 0x56, 0xE8, 0x8B, 0x3F, 0x0C, 0x3D, 0x24, 0xFE, 0x38, 0x49, 0xC9, 0x09, 0x5B, 0x6E, 0x94, 0x30, 0x28, 0xB4, 0x66, 0x76, 0xA5, 0xD6, 0xB9, + 0x61, 0x66, 0xCF, 0xA6, 0xD1, 0x1D, 0xB4, 0xD8, 0x4B, 0xAF, 0xDB, 0x11, 0xB4, 0xB6, 0x78, 0x0F, 0x4A, 0x46, 0x95, 0x7F, 0x53, 0x06, 0x45, 0x6F, + 0x45, 0x60, 0x17, 0x2F, 0x82, 0x04, 0x66, 0x33, 0x7A, 0xEC, 0xE0, 0xFC, 0x85, 0xD8, 0xBB, 0xE6, 0x15, 0xAC, 0xB7, 0x24, 0x4A, 0xE3, 0x2C, 0x49, + 0xDA, 0x86, 0x7A, 0x3F, 0xE6, 0x97, 0x0C, 0x52, 0x2B, 0xDC, 0x18, 0xE0, 0xF1, 0xA2, 0x17, 0x12, 0x88, 0xC0, 0x9B, 0x1B, 0xCB, 0xD0, 0x60, 0x7D, + 0x43, 0x94, 0x29, 0xCC, 0xDE, 0x98, 0x10, 0x2A, 0x48, 0xB3, 0x64, 0xE5, 0x8E, 0x0A, 0x03, 0x5C, 0x59, 0x40, 0x4E, 0x88, 0x0A, 0x90, 0x44, 0x43, + 0x79, 0xA7, 0x82, 0x93, 0x62, 0x87, 0x80, 0x1B, 0xC2, 0xAE, 0xC6, 0x1B, 0xF3, 0x6E, 0x41, 0x93, 0xC2, 0x6C, 0x0D, 0x0E, 0x89, 0x03, 0x38, 0x62, + 0x84, 0xB0, 0xBB, 0x31, 0x43, 0xAC, 0x39, 0x56, 0xAA, 0x11, 0x3B, 0x6B, 0xC6, 0x88, 0x40, 0x41, 0x12, 0xB4, 0x59, 0x01, 0x37, 0xAE, 0xC9, 0x90, + 0x31, 0x27, 0xA8, 0x55, 0xF9, 0x80, 0xB5, 0xA9, 0xD2, 0x71, 0xDF, 0x38, 0x22, 0xBF, 0x5A, 0x7D, 0x73, 0xB9, 0x73, 0xB6, 0xDA, 0xCE, 0xD9, 0x57, + 0xD7, 0xBD, 0xB6, 0x4A, 0xCD, 0xB8, 0x18, 0xFA, 0xA5, 0xFC, 0x7B, 0xFE, 0xB5, 0xFF, 0x37, 0x76, 0xAD, 0xDC, 0xF7, 0xFC, 0x75, 0x42, 0x4B, 0x7D, + 0xFD, 0x3F, 0xFD, 0xB5, 0x5E, 0x69, 0xF7, 0x8E, 0x39, 0xA7, 0x4E, 0xEB, 0x9B, 0x7D, 0x69, 0xA9, 0xC7, 0xC8, 0xEC, 0xE2, 0xE8, 0x18, 0x4B, 0xCE, + 0x3B, 0x6A, 0xA6, 0x88, 0x9A, 0x73, 0x42, 0x24, 0x24, 0x19, 0x22, 0x68, 0xE6, 0x8C, 0x8D, 0xA4, 0x72, 0xB2, 0x5C, 0xF9, 0xF2, 0xC6, 0x4E, 0x89, + 0x15, 0x73, 0x59, 0x75, 0x99, 0x61, 0x62, 0x71, 0x76, 0x5D, 0x23, 0x86, 0xDB, 0x77, 0xB5, 0x1D, 0x79, 0x73, 0xD6, 0x41, 0x96, 0x23, 0xAD, 0x97, + 0x9D, 0x97, 0xB3, 0x9E, 0x1C, 0xEF, 0x7B, 0x47, 0x8D, 0x37, 0x96, 0xD4, 0xB6, 0x22, 0x23, 0x76, 0x97, 0x0E, 0x85, 0x29, 0x40, 0xD6, 0x98, 0x12, + 0x2E, 0x48, 0xB3, 0x04, 0x99, 0x60, 0x85, 0x90, 0x9C, 0xAD, 0x13, 0x56, 0x0A, 0x25, 0x33, 0x0E, 0x65, 0x3F, 0x65, 0x67, 0x29, 0x0C, 0xB4, 0x6D, + 0x21, 0xFC, 0x9D, 0x60, 0x1A, 0x5E, 0x98, 0x62, 0xA0, 0x78, 0x73, 0xDC, 0xAD, 0x61, 0x61, 0x1A, 0xA9, 0xF1, 0x66, 0x3C, 0x4C, 0x91, 0xC7, 0xBC, + 0x41, 0xB2, 0x98, 0x2D, 0xA6, 0x9A, 0x7E, 0xF3, 0x9D, 0x57, 0x41, 0x9D, 0xF0, 0xB2, 0xD2, 0x8D, 0x86, 0x62, 0x4D, 0x9F, 0x78, 0x67, 0x36, 0x9A, + 0x5F, 0x39, 0x31, 0xDE, 0xFF, 0xFD, 0xED, 0xDF, 0xCD, 0x6A, 0x23, 0xEA, 0xB9, 0xDB, 0x68, 0x15, 0x8D, 0x80, 0x66, 0x44, 0xD9, 0xD0, 0xC5, 0x4B, + 0x1B, 0x69, 0xB2, 0x86, 0x19, 0x6C, 0x26, 0xC8, 0x08, 0xA7, 0x6B, 0x85, 0x05, 0xC5, 0x01, 0x27, 0xAF, 0xF9, 0xCE, 0x60, 0x34, 0x5C, 0xB9, 0xB3, + 0xEC, 0x68, 0xFF, 0xEF, 0xF4, 0x5A, 0x79, 0xEB, 0xE8, 0x79, 0xBF, 0x67, 0xE9, 0x73, 0xE9, 0x72, 0xB6, 0xB7, 0xCA, 0xF3, 0x8E, 0x7A, 0xDD, 0xCB, + 0x8B, 0xDB, 0x1D, 0x15, 0x70, 0x59, 0x69, 0x67, 0x3C, 0x8B, 0xDE, 0xF8, 0xDB, 0xDB, 0xA6, 0x59, 0xAB, 0xC6, 0xDD, 0x32, 0x55, 0x5E, 0xC7, 0x4F, + 0xD3, 0x88, 0x46, 0x37, 0x3A, 0xAB, 0xBB, 0x04, 0x40, 0x71, 0x10, 0xB8, 0x2A, 0x76, 0x68, 0xF6, 0xE9, 0xA6, 0x43, 0x3C, 0x69, 0x31, 0x74, 0x03, + 0x22, 0xB4, 0x06, 0xE2, 0x44, 0x6D, 0x36, 0x0B, 0xB1, 0x73, 0x3E, 0xD8, 0xEB, 0x98, 0x47, 0xB7, 0x93, 0xF1, 0x7C, 0xB5, 0x9D, 0xB3, 0xEC, 0xAC, + 0xFF, 0x75, 0xF4, 0x5A, 0x39, 0xAE, 0x5D, 0x5B, 0x7F, 0x6A, 0x4F, 0x22, 0x30, 0xD9, 0x57, 0x7C, 0xB1, 0xB2, 0x38, 0x3C, 0x12, 0x75, 0xBB, 0xEF, + 0x5B, 0x84, 0xD0, 0x58, 0xC6, 0x6A, 0x09, 0xC3, 0x5F, 0x07, 0xA2, 0xD3, 0x0F, 0x4C, 0x0F, 0xFB, 0x8F, 0xE1, 0x09, 0xFD, 0x9A, 0x3D, 0xA7, 0xB8, + 0x1F, 0x24, 0x2C, 0x48, 0x26, 0xA5, 0xE1, 0x3F, 0x51, 0xEB, 0xFE, 0xEA, 0x2F, 0xB3, 0x57, 0x93, 0x25, 0x5A, 0x04, 0x0B, 0x40, 0x88, 0xAC, 0x0C, + 0xD1, 0x61, 0xDE, 0xD8, 0x60, 0xCA, 0x1D, 0x68, 0x53, 0x06, 0x5C, 0x32, 0x10, 0x07, 0x34, 0xB3, 0x7C, 0x85, 0x69, 0x67, 0xA3, 0xB4, 0x8D, 0x71, + 0x2D, 0x18, 0x89, 0x3A, 0x4D, 0x62, 0xC8, 0x1E, 0xE2, 0x78, 0xCF, 0xC8, 0x27, 0x65, 0x4A, 0x80, 0x23, 0x6B, 0xAA, 0x6A, 0x31, 0x98, 0xC1, 0x65, + 0x5C, 0x30, 0x33, 0x68, 0x26, 0xC1, 0x0C, 0xE1, 0x06, 0x38, 0x2F, 0x65, 0xC6, 0xFC, 0xA1, 0xF8, 0xB7, 0x0F, 0x8D, 0x6F, 0x6F, 0x4C, 0xDD, 0x81, + 0x2C, 0xA5, 0xF0, 0x0F, 0x08, 0xCC, 0x1C, 0xC4, 0xFE, 0x40, 0xDD, 0x02, 0x5B, 0xEB, 0xC3, 0x7A, 0xC9, 0x9E, 0x76, 0x6B, 0x8A, 0x87, 0x74, 0x30, + 0x29, 0x6E, 0x04, 0xD0, 0xCC, 0x49, 0x8E, 0x06, 0xD0, 0x6B, 0x05, 0x24, 0x53, 0xC6, 0x7C, 0xE2, 0x04, 0x18, 0xEB, 0x5B, 0x4F, 0xE1, 0xD4, 0xCF, + 0x57, 0x9F, 0xEB, 0xCE, 0x4A, 0x83, 0x7B, 0x2C, 0xE2, 0x6F, 0xE8, 0xA6, 0x2A, 0x6D, 0xF1, 0xAB, 0x51, 0x18, 0x76, 0x27, 0xDE, 0xD0, 0x7C, 0x0B, + 0x27, 0x65, 0x02, 0xC3, 0x4B, 0x5D, 0xF4, 0x6F, 0xEE, 0x1A, 0x44, 0xAE, 0x36, 0xA6, 0x16, 0x22, 0x0C, 0x3C, 0x58, 0x7E, 0x55, 0x37, 0x41, 0x49, + 0x77, 0xE1, 0x73, 0x45, 0x64, 0x6F, 0xC2, 0x22, 0x6C, 0xE4, 0x6C, 0xB8, 0x77, 0x01, 0xF8, 0xF3, 0x75, 0x86, 0xAA, 0xB5, 0x75, 0x7C, 0x84, 0x25, + 0xBD, 0xFD, 0x8B, 0xEF, 0xFF, 0x1F, 0x1C, 0xA7, 0xBB, 0xC0, 0x0C, 0x5E, 0xBC, 0x37, 0x79, 0x67, 0xD1, 0x1A, 0x27, 0xE5, 0xB6, 0x7A, 0x78, 0x88, + 0x93, 0x13, 0xE8, 0x9F, 0xE5, 0x18, 0xD6, 0xC5, 0xC7, 0x4C, 0xA4, 0x7E, 0x5F, 0x7F, 0x21, 0x5E, 0xD8, 0xEE, 0x02, 0xFC, 0x17, 0x4D, 0xE7, 0xDE, + 0xAF, 0x4C, 0x24, 0x94, 0xA6, 0xF6, 0xB0, 0x09, 0x0B, 0x88, 0x8B, 0xD7, 0xB4, 0x1B, 0x05, 0x61, 0xA6, 0x25, 0x58, 0x25, 0xB8, 0x0C, 0xDF, 0x57, + 0x32, 0x31, 0x64, 0xF8, 0xD1, 0x24, 0x58, 0x58, 0x71, 0xC5, 0x57, 0x28, 0x8C, 0xDC, 0x82, 0xCF, 0xD2, 0x0C, 0x87, 0x7B, 0x8F, 0x3E, 0x45, 0xB0, + 0xF7, 0x28, 0xCC, 0xA1, 0x31, 0x4C, 0x98, 0x64, 0x6D, 0x22, 0x0F, 0x75, 0x45, 0x8B, 0xF1, 0xD8, 0x3D, 0x3D, 0x3A, 0x87, 0xB2, 0x2A, 0xD7, 0x8C, + 0xE7, 0xD9, 0x81, 0x00, 0xFB, 0x72, 0xC8, 0x63, 0x40, 0x94, 0x5E, 0xE0, 0x02, 0x66, 0x31, 0x78, 0xEE, 0x3D, 0xE4, 0x08, 0xF8, 0x94, 0x0D, 0x38, + 0x55, 0x1E, 0x27, 0x90, 0x6B, 0xDE, 0x74, 0xEF, 0x34, 0xA8, 0x23, 0xA0, 0xBF, 0x51, 0x07, 0x9E, 0xA1, 0x72, 0x83, 0x6E, 0x9F, 0xE9, 0x1E, 0x4C, + 0x30, 0x7B, 0xA6, 0x60, 0x91, 0xA4, 0xD1, 0x6A, 0x41, 0xDA, 0xB8, 0x4E, 0xC5, 0xE2, 0x6D, 0x45, 0x05, 0x17, 0x70, 0x51, 0x21, 0x0B, 0x5D, 0x1F, + 0x65, 0xAC, 0x87, 0x0B, 0x5C, 0x4E, 0xC4, 0x1C, 0x3B, 0x35, 0xDF, 0x48, 0x41, 0x99, 0xBE, 0xBE, 0x6C, 0xC0, 0x3D, 0x45, 0x29, 0xFE, 0x5A, 0xD9, + 0x5C, 0xF9, 0x86, 0xFA, 0x82, 0xD6, 0xC5, 0x12, 0xA4, 0xC3, 0x0C, 0xEB, 0x07, 0x78, 0x6E, 0xF5, 0xDD, 0x13, 0x7B, 0x34, 0x96, 0xBE, 0xB2, 0xFC, + 0x6B, 0x7A, 0xE1, 0x64, 0xFB, 0x5D, 0xF4, 0xDA, 0xE0, 0x95, 0xB8, 0x73, 0x6D, 0xAF, 0x81, 0x71, 0xD3, 0xF6, 0x1A, 0xED, 0xF6, 0x13, 0xF1, 0x1B, + 0x95, 0xDB, 0x42, 0x4D, 0xDF, 0x28, 0x61, 0xBA, 0xF9, 0xCA, 0x25, 0xBF, 0x9C, 0xA0, 0x1D, 0xAE, 0x7E, 0x5F, 0xD9, 0x3E, 0xEA, 0x72, 0xFC, 0xEA, + 0x1F, 0x81, 0xBA, 0xA2, 0x0A, 0x2B, 0x99, 0x61, 0xC3, 0x25, 0x9E, 0x8C, 0x3E, 0x13, 0x7D, 0xAA, 0x56, 0x0C, 0xE0, 0x6F, 0x8B, 0x26, 0x9E, 0x96, + 0xDB, 0xA6, 0x8E, 0x95, 0xB8, 0x07, 0xB2, 0x41, 0x08, 0x42, 0xFA, 0x7C, 0xDB, 0x9B, 0xF7, 0xDF, 0x16, 0x4F, 0xB2, 0xDF, 0x9D, 0x9E, 0xFD, 0x40, + 0x1C, 0x26, 0xA5, 0xEF, 0x96, 0xB4, 0x6F, 0x63, 0x8F, 0x79, 0xF9, 0x49, 0xC6, 0x82, 0x52, 0xD4, 0x77, 0x82, 0x99, 0xB3, 0x02, 0xBA, 0xC1, 0x39, + 0x19, 0x56, 0x44, 0xAE, 0x75, 0x9F, 0xCE, 0x14, 0x30, 0x86, 0x86, 0x58, 0xD6, 0x6D, 0x88, 0x33, 0xF6, 0xB8, 0x12, 0x93, 0x3A, 0xE7, 0x7F, 0xB1, + 0xF3, 0x32, 0x51, 0xD2, 0xEE, 0xD9, 0xE8, 0x9E, 0x63, 0x84, 0xD1, 0xBF, 0x47, 0x27, 0xBE, 0x46, 0x86, 0x8B, 0x96, 0x52, 0xD7, 0x34, 0x63, 0x77, + 0xC5, 0x58, 0xCC, 0xAB, 0x0D, 0xDE, 0xEC, 0xB0, 0xDA, 0x6B, 0xD1, 0x42, 0x27, 0x94, 0x38, 0xB2, 0x34, 0x5F, 0x06, 0x35, 0x3B, 0x59, 0xB4, 0x8C, + 0xCF, 0x14, 0x54, 0x9C, 0x76, 0x14, 0x9F, 0xB0, 0xCD, 0x74, 0x7B, 0x36, 0x3B, 0x15, 0xE6, 0x85, 0x46, 0x5B, 0x2A, 0x42, 0x4F, 0x9B, 0x79, 0x44, + 0xF3, 0xB5, 0x25, 0x37, 0x39, 0x76, 0x2C, 0x08, 0x82, 0x61, 0x27, 0x30, 0x9B, 0x25, 0x62, 0x0C, 0x2B, 0x4A, 0x52, 0x20, 0x6D, 0x76, 0x0C, 0x19, + 0x1F, 0x82, 0x71, 0xD0, 0xB8, 0xF6, 0xF3, 0x53, 0x8C, 0x38, 0xF2, 0x9E, 0x36, 0x87, 0x9B, 0xB7, 0x62, 0xE5, 0xA9, 0xC0, 0xD9, 0x46, 0x1D, 0x56, + 0x45, 0x85, 0xAD, 0x67, 0xF5, 0x3D, 0x35, 0x15, 0x36, 0xFF, 0x36, 0x7D, 0xF7, 0x70, 0xC6, 0xD7, 0xD3, 0xEA, 0xB7, 0xAD, 0x43, 0xEF, 0x25, 0xE8, + 0x9F, 0x2B, 0x48, 0x87, 0x47, 0x94, 0x19, 0x28, 0x26, 0xA7, 0x2A, 0x2D, 0x8C, 0x5A, 0x37, 0x96, 0x38, 0x3D, 0x1E, 0xD8, 0x7E, 0x2A, 0x98, 0x4B, + 0xC8, 0x08, 0xA4, 0x30, 0xB7, 0x50, 0x81, 0x13, 0x4B, 0xDD, 0x5E, 0x6C, 0x25, 0x3B, 0xBD, 0x61, 0xE3, 0xF7, 0xFD, 0x32, 0x93, 0xFC, 0xA5, 0x86, + 0x6A, 0xAE, 0xA8, 0xA7, 0x49, 0xE9, 0x38, 0xBE, 0xC0, 0x16, 0xE8, 0xC2, 0xF6, 0xB5, 0x8A, 0xB3, 0xFB, 0xB1, 0x64, 0x04, 0x07, 0xB2, 0xC3, 0x22, + 0x95, 0x80, 0x79, 0x76, 0x4F, 0x5F, 0xF6, 0xD7, 0x77, 0xFD, 0x8F, 0xAA, 0x86, 0x86, 0x15, 0xF0, 0x1B, 0xD4, 0xBF, 0x15, 0x75, 0x62, 0xA5, 0x8B, + 0x3A, 0x58, 0x91, 0xA6, 0x97, 0xE9, 0xF7, 0x35, 0xCF, 0xFC, 0x95, 0xDA, 0x0E, 0x0D, 0xBC, 0x7D, 0x30, 0xBA, 0x05, 0x7A, 0xED, 0x6C, 0x7E, 0xA4, + 0x9E, 0xD3, 0x38, 0x37, 0x9A, 0x32, 0x06, 0x83, 0x29, 0xD1, 0xB6, 0x66, 0x42, 0xF1, 0x86, 0x1B, 0x77, 0x22, 0x4F, 0x32, 0xD9, 0x76, 0x7A, 0x91, + 0x19, 0x38, 0xE8, 0xB5, 0x58, 0x47, 0x11, 0x34, 0x06, 0xE3, 0x3F, 0x7D, 0x3B, 0x8B, 0x13, 0x37, 0x55, 0x80, 0x3D, 0xBA, 0xA7, 0x83, 0xAE, 0x8D, + 0xF8, 0x9F, 0x9F, 0x16, 0x47, 0x7C, 0xE2, 0x74, 0x4D, 0x40, 0xC9, 0xBC, 0x80, 0x39, 0xCB, 0xEA, 0xB3, 0x9D, 0x9D, 0x00, 0x9D, 0xC5, 0x7B, 0xBE, + 0xE3, 0x04, 0xB7, 0xC5, 0x3C, 0x16, 0xD2, 0xAB, 0x02, 0x7D, 0x26, 0x70, 0x7E, 0xB7, 0x27, 0xA4, 0x6B, 0x42, 0x8F, 0x5D, 0x9B, 0xDF, 0xA0, 0x74, + 0x14, 0x4F, 0xFB, 0x9F, 0xC9, 0x33, 0xEE, 0x1A, 0x47, 0xF8, 0x17, 0x3D, 0x87, 0x99, 0x95, 0x03, 0xD2, 0xDE, 0x77, 0x8D, 0xCD, 0xD4, 0xD0, 0xC1, + 0x6F, 0x67, 0x31, 0xD1, 0x8A, 0x53, 0x21, 0x79, 0x08, 0x3A, 0x27, 0xA8, 0xF7, 0x3A, 0xDD, 0xC2, 0x94, 0x35, 0xA9, 0xE5, 0xAC, 0xDD, 0x68, 0xF1, + 0xB3, 0x7F, 0x46, 0x5A, 0x9C, 0xB9, 0x40, 0x1B, 0xBB, 0x12, 0x86, 0x28, 0x83, 0x92, 0x98, 0x26, 0x3F, 0x2B, 0xB8, 0x5E, 0x06, 0x5A, 0x2E, 0xBF, + 0x32, 0x72, 0x07, 0xC5, 0xAC, 0x5E, 0x44, 0x0C, 0xBD, 0x46, 0xE6, 0xC3, 0xA5, 0xF5, 0x0E, 0x09, 0x08, 0x29, 0xEF, 0xCD, 0xB7, 0xC0, 0xE7, 0x23, + 0x41, 0x7A, 0x2F, 0xA4, 0x3F, 0x83, 0x55, 0x5F, 0xA5, 0xD8, 0x74, 0xEC, 0x28, 0xE7, 0x95, 0x66, 0xC7, 0x31, 0xB7, 0xA6, 0x3F, 0x7E, 0x6C, 0x45, + 0x44, 0x27, 0xD3, 0x18, 0x18, 0x99, 0xB3, 0x9E, 0x8B, 0x11, 0xE7, 0x98, 0xC5, 0xE5, 0x04, 0x66, 0x6E, 0x69, 0xBC, 0x0B, 0x3C, 0x8C, 0x15, 0x14, + 0xBD, 0x1D, 0xFD, 0x04, 0x64, 0x6D, 0x43, 0xF1, 0x56, 0x01, 0xBF, 0x23, 0x46, 0x38, 0x45, 0xA6, 0x43, 0x78, 0xA6, 0xED, 0x8E, 0x3D, 0xD9, 0xA9, + 0xB5, 0xE0, 0x09, 0x4C, 0xE7, 0x66, 0x19, 0xF7, 0x14, 0x8D, 0x58, 0x73, 0x93, 0xAC, 0x34, 0xC7, 0xD1, 0x34, 0xC7, 0x25, 0xF7, 0x69, 0x07, 0xD4, + 0x80, 0xDD, 0x7B, 0x43, 0x59, 0xBB, 0xF1, 0xE3, 0x55, 0x4D, 0xAF, 0xA8, 0xDD, 0xFD, 0x2C, 0xE6, 0xD1, 0xFF, 0xA1, 0xEB, 0x42, 0xD6, 0x0B, 0x36, + 0x7E, 0x3D, 0x8F, 0x9B, 0x08, 0xD0, 0x36, 0xDC, 0x43, 0x21, 0x44, 0xED, 0x84, 0xDB, 0x05, 0xA4, 0x56, 0xDE, 0x9D, 0x59, 0x6C, 0x9F, 0x88, 0xD8, + 0x46, 0xEB, 0x3E, 0x64, 0xAC, 0x51, 0xBD, 0x59, 0xD9, 0x3B, 0xF8, 0x8B, 0x3A, 0x67, 0xF1, 0x68, 0xAB, 0xC3, 0xA1, 0xE0, 0xBA, 0x94, 0x71, 0x00, + 0xFC, 0x7E, 0xE6, 0xF8, 0x98, 0x87, 0x76, 0xA3, 0x56, 0xF7, 0x29, 0xF4, 0x1B, 0x31, 0x6D, 0x4A, 0x3D, 0x10, 0x2D, 0xC5, 0xC2, 0xA2, 0x9E, 0x69, + 0x37, 0x63, 0x63, 0xC3, 0xC2, 0x0B, 0x2B, 0x24, 0x6A, 0xAD, 0x2F, 0x0B, 0x9B, 0x66, 0x4C, 0x2D, 0x9C, 0x13, 0xA8, 0x4F, 0xC0, 0x13, 0xAF, 0x31, + 0x1C, 0xE0, 0x8B, 0xA8, 0xB5, 0xF7, 0x4D, 0xF5, 0x1B, 0x78, 0x40, 0x7B, 0x65, 0x5D, 0x4B, 0x71, 0x6E, 0x62, 0x0C, 0xEF, 0x3D, 0x37, 0xE9, 0x66, + 0x29, 0x5F, 0xFB, 0xF3, 0x23, 0x6E, 0x34, 0xD5, 0x6F, 0xD7, 0x7E, 0x21, 0x89, 0xA7, 0xB0, 0x22, 0xCE, 0x9D, 0x3E, 0xDC, 0x84, 0x22, 0x12, 0x52, + 0xC6, 0x66, 0xDF, 0xEC, 0x48, 0xC2, 0x31, 0x0B, 0x2A, 0xD3, 0x5F, 0x9D, 0xB4, 0xA5, 0x40, 0xB5, 0x42, 0xC0, 0x5B, 0x57, 0xF3, 0x7B, 0x76, 0x43, + 0x3E, 0x93, 0x1E, 0x8A, 0xC7, 0xCE, 0x21, 0x8A, 0x1B, 0x6E, 0x34, 0x36, 0x94, 0xFA, 0x4A, 0x7C, 0x25, 0xE1, 0xE0, 0x81, 0xEE, 0xF6, 0xC3, 0x1F, + 0x6C, 0x55, 0x6C, 0xEC, 0x95, 0x4B, 0xCB, 0x28, 0x51, 0x51, 0x75, 0x88, 0xED, 0xD3, 0xD6, 0x99, 0xC0, 0x5B, 0xC6, 0xFC, 0x72, 0x50, 0xFD, 0xB0, + 0xA3, 0x32, 0x26, 0xD3, 0x42, 0xF0, 0xC0, 0x3E, 0x35, 0xAB, 0xB1, 0x13, 0xA0, 0x77, 0x35, 0xFF, 0xF9, 0xEE, 0x55, 0xB9, 0xFF, 0xE7, 0x09, 0xCB, + 0x07, 0x47, 0xDC, 0x5D, 0x76, 0xD1, 0x05, 0x17, 0xDC, 0x22, 0x39, 0xF6, 0x7D, 0x02, 0xE9, 0x06, 0xA1, 0xCB, 0x85, 0x58, 0x3B, 0xE9, 0x03, 0x17, + 0xD6, 0x22, 0x4F, 0x72, 0x08, 0x60, 0xAA, 0x6D, 0x98, 0xA7, 0xCA, 0xB5, 0xA5, 0x69, 0xC6, 0xD7, 0x77, 0x2E, 0xF9, 0x62, 0x15, 0xEB, 0xCA, 0x8C, + 0x22, 0xA0, 0xD7, 0x31, 0x8A, 0x6E, 0x2B, 0x9E, 0x9B, 0x01, 0xAF, 0x51, 0xDA, 0x68, 0xC0, 0xBE, 0xD6, 0xF9, 0x96, 0xA9, 0x08, 0x73, 0x35, 0xE0, + 0xEE, 0xCE, 0x68, 0x46, 0x11, 0x5D, 0xEE, 0xEE, 0x5E, 0xE5, 0x20, 0xB6, 0xA0, 0x58, 0xB0, 0xD6, 0xD6, 0xA3, 0xDD, 0x75, 0x3E, 0x09, 0x4C, 0x6D, + 0x6B, 0x78, 0xEA, 0xF7, 0x67, 0x8B, 0x71, 0xFA, 0xAE, 0x1E, 0x0D, 0x41, 0x86, 0xEF, 0x55, 0x91, 0x00, 0xCF, 0xDE, 0x46, 0x18, 0x2A, 0x61, 0x69, + 0xCF, 0x1B, 0x79, 0xB3, 0x64, 0xFA, 0xFA, 0x4A, 0xEA, 0xC3, 0xE0, 0xB5, 0xE2, 0x80, 0xB4, 0xB9, 0xCD, 0x45, 0x55, 0xFD, 0xDA, 0x3B, 0x98, 0xA6, + 0xEA, 0x10, 0x46, 0xEA, 0x02, 0xE6, 0x2B, 0xE5, 0x36, 0x17, 0x16, 0x59, 0x0A, 0x3F, 0xF7, 0xE7, 0x9B, 0x37, 0x65, 0xDE, 0x19, 0xC8, 0x5A, 0x08, + 0xF7, 0x29, 0x69, 0xE7, 0x91, 0x34, 0x0C, 0xAC, 0xCB, 0xEE, 0xDC, 0x18, 0x22, 0x64, 0xFD, 0x71, 0x74, 0xA1, 0x12, 0x3D, 0x8C, 0xB8, 0xA3, 0x8B, + 0xFF, 0x8D, 0x81, 0xCF, 0xEC, 0x09, 0x5E, 0x64, 0xDE, 0x35, 0x88, 0xD3, 0x80, 0x64, 0xAC, 0x92, 0xC9, 0xDC, 0xA9, 0xE4, 0x2A, 0x29, 0x70, 0x6B, + 0x6B, 0xAE, 0xCA, 0xAC, 0x86, 0x36, 0x71, 0x2F, 0x09, 0xE4, 0xD6, 0x25, 0x36, 0xAD, 0x3C, 0x36, 0xEA, 0x53, 0x1D, 0xAE, 0x5E, 0x0C, 0x2C, 0xF6, + 0xC9, 0x51, 0xA5, 0xC3, 0x6C, 0x23, 0x04, 0x64, 0xEC, 0x54, 0x78, 0xB0, 0x62, 0x14, 0xF0, 0x4B, 0x5E, 0x43, 0x89, 0xE3, 0x1F, 0xD8, 0xB1, 0xDF, + 0xFC, 0xB1, 0x7F, 0x8B, 0x1C, 0xC8, 0xE0, 0x7A, 0xAF, 0x09, 0x81, 0x53, 0xCF, 0x0B, 0x57, 0x14, 0xC2, 0x3E, 0xE2, 0x4D, 0xAB, 0xEE, 0x9F, 0x26, + 0x47, 0xC3, 0x2B, 0xA8, 0xE1, 0x6C, 0xF5, 0xD5, 0xE0, 0x79, 0x3C, 0x3C, 0xF2, 0x67, 0x9B, 0xB1, 0x87, 0x58, 0xDC, 0x42, 0xF7, 0xFF, 0x7F, 0x03, + 0x77, 0x4C, 0x33, 0x2C, 0xB7, 0x6F, 0x11, 0x3D, 0xDD, 0xF8, 0x74, 0x75, 0xDB, 0x78, 0xC8, 0xAC, 0x39, 0x7A, 0x53, 0xC3, 0x86, 0xF4, 0x58, 0x23, + 0x15, 0x37, 0xC7, 0x12, 0x99, 0x1A, 0x82, 0xA8, 0x27, 0x4F, 0xF2, 0x2C, 0x05, 0xE2, 0xA0, 0xD8, 0x9E, 0x1E, 0xFC, 0x31, 0x2A, 0x6F, 0xAE, 0xF1, + 0x9C, 0xC7, 0xC2, 0xBF, 0x6E, 0x36, 0xE4, 0x26, 0x40, 0x2C, 0x3D, 0x89, 0xB6, 0xD0, 0x91, 0xDE, 0xDE, 0x79, 0xD2, 0xD7, 0xA6, 0x6C, 0xA8, 0xB1, + 0x2B, 0xCF, 0xD7, 0xFB, 0x1F, 0xEE, 0x39, 0x08, 0x43, 0xBC, 0xC7, 0x8B, 0x13, 0xD3, 0x64, 0x96, 0xD5, 0xFB, 0x4F, 0xB5, 0x6D, 0xE1, 0x75, 0xAA, + 0xD8, 0x02, 0x8B, 0x61, 0xAD, 0xE8, 0x51, 0x02, 0x57, 0x34, 0x74, 0xF4, 0x0D, 0x22, 0xE2, 0xE2, 0xF0, 0xBD, 0x25, 0x3F, 0x9F, 0x41, 0x37, 0xF7, + 0xB0, 0x98, 0x4B, 0xD5, 0x46, 0x29, 0xA0, 0xD5, 0x78, 0x06, 0x4E, 0xCA, 0x74, 0x31, 0x30, 0x18, 0xC7, 0x29, 0x8D, 0x9D, 0x60, 0x93, 0x46, 0x73, + 0x68, 0xE8, 0x1E, 0x4A, 0xC8, 0xD5, 0xAF, 0x07, 0xA4, 0xE6, 0x7B, 0x4B, 0x58, 0x2A, 0xA0, 0xE7, 0x49, 0xC1, 0x6B, 0xE1, 0x7C, 0xF0, 0x2A, 0xE4, + 0x11, 0xC4, 0xE0, 0x30, 0x52, 0x8F, 0xD0, 0xA9, 0x39, 0x47, 0xCC, 0x1A, 0x48, 0xA2, 0x81, 0x79, 0xB7, 0xC2, 0x49, 0x91, 0x69, 0x15, 0x15, 0x88, + 0x6D, 0x0E, 0x53, 0xA6, 0xF8, 0xF7, 0xE7, 0xB6, 0xFB, 0x73, 0xEA, 0xB3, 0x16, 0x86, 0xC8, 0x9A, 0x50, 0x93, 0x75, 0x77, 0x6C, 0x32, 0xC9, 0x18, + 0xB7, 0xDD, 0xE4, 0x49, 0x2B, 0x66, 0xB7, 0x45, 0x2C, 0xE6, 0x92, 0x53, 0x54, 0xFA, 0x76, 0x82, 0xBE, 0x0B, 0x6B, 0xC6, 0x51, 0xB9, 0xB9, 0x8E, + 0xC9, 0x23, 0x32, 0xFB, 0x8E, 0x9F, 0x6E, 0xF6, 0x45, 0x4F, 0x4C, 0xA1, 0x1C, 0xDE, 0xF8, 0x2D, 0x94, 0xEB, 0xFD, 0xF8, 0x72, 0xF9, 0x5F, 0xF0, + 0x86, 0x0F, 0x9A, 0x0B, 0x6D, 0x07, 0xD7, 0x53, 0x6D, 0x7B, 0xDB, 0x21, 0xEF, 0xB5, 0x20, 0xAD, 0xCF, 0x5D, 0x1A, 0xEA, 0x80, 0xCC, 0x57, 0x0D, + 0x65, 0x73, 0x5A, 0xC0, 0x45, 0x24, 0x95, 0x08, 0xA3, 0xB3, 0x45, 0x40, 0xB4, 0x43, 0x84, 0xDF, 0xAC, 0xAF, 0x79, 0xC7, 0xC1, 0x1B, 0x03, 0xB9, + 0xCD, 0xF5, 0xD5, 0x62, 0x4A, 0xF1, 0xB3, 0xF8, 0xD0, 0x90, 0xE7, 0xE2, 0xAB, 0x0D, 0xCD, 0x7B, 0xC0, 0xAC, 0x75, 0x32, 0xE3, 0x3B, 0xC4, 0x25, + 0x1E, 0x72, 0x45, 0xE4, 0x62, 0xFD, 0xA8, 0xC6, 0x7D, 0x46, 0xC4, 0x64, 0xC2, 0x16, 0x40, 0xE5, 0x48, 0x11, 0x4E, 0xD3, 0xE3, 0x5A, 0x47, 0x20, + 0xA9, 0xA1, 0xC7, 0x50, 0x3E, 0xC9, 0x12, 0x45, 0x83, 0xEC, 0x32, 0xC9, 0xDD, 0xAC, 0x32, 0x0D, 0xEB, 0xB4, 0xD5, 0xB0, 0xBF, 0x3D, 0x98, 0x9F, + 0x93, 0x2C, 0x46, 0x67, 0xFF, 0xC9, 0x50, 0xBF, 0xFE, 0x47, 0x80, 0xA8, 0x67, 0xC3, 0x5C, 0x60, 0xF4, 0x6E, 0x38, 0x19, 0x68, 0xB9, 0x9C, 0x8F, + 0x52, 0xAF, 0xEB, 0x9C, 0xC1, 0x57, 0x18, 0x8B, 0x45, 0x47, 0x2C, 0x37, 0x95, 0xD4, 0x9F, 0x5C, 0x4D, 0xF7, 0xDD, 0xC6, 0xF4, 0x0F, 0xC9, 0x16, + 0x3A, 0x9C, 0xBC, 0x7F, 0x9E, 0xEB, 0xDC, 0xCA, 0xC5, 0x07, 0x3A, 0x44, 0x91, 0x12, 0xBF, 0x3E, 0xAB, 0xA6, 0x93, 0xFA, 0x11, 0xE3, 0x0D, 0xBB, + 0xD3, 0xB7, 0xC7, 0x22, 0xAA, 0x3D, 0x2C, 0x62, 0x36, 0x94, 0x53, 0x88, 0xDB, 0xED, 0xCC, 0x22, 0xBE, 0x1C, 0xD0, 0xB6, 0x14, 0x01, 0x35, 0x25, + 0x46, 0x39, 0xAB, 0x58, 0x35, 0x6C, 0x66, 0x21, 0xAA, 0xF9, 0x0D, 0xC6, 0x5A, 0x37, 0x01, 0x6E, 0x3F, 0xC5, 0x07, 0xBF, 0x74, 0x96, 0x61, 0xEE, + 0x6B, 0xB4, 0xD8, 0x76, 0x68, 0xF3, 0x06, 0x31, 0xFD, 0x31, 0xF6, 0x1F, 0x5F, 0xA4, 0x14, 0x60, 0x23, 0xA2, 0x4C, 0x38, 0x85, 0x3A, 0xF5, 0x19, + 0xD3, 0xC8, 0x24, 0x02, 0x0F, 0x2A, 0xB8, 0xA0, 0x6E, 0xC9, 0xE8, 0x4B, 0x14, 0xC1, 0x26, 0xC1, 0x3A, 0xF4, 0x6B, 0x6E, 0xF4, 0x2E, 0x55, 0x4E, + 0x01, 0xE8, 0x27, 0x23, 0x03, 0x35, 0xEC, 0xAB, 0xCC, 0x27, 0xF0, 0x17, 0x1A, 0xF1, 0xDC, 0x92, 0xE2, 0x78, 0xC6, 0x67, 0x9A, 0x01, 0xD9, 0xEF, + 0x71, 0x8B, 0x02, 0x06, 0xC2, 0x48, 0x9E, 0x8D, 0x22, 0xEF, 0xE6, 0xEF, 0x04, 0x0B, 0xB0, 0x74, 0xD6, 0xA8, 0xAD, 0xDB, 0xB1, 0xD4, 0xEC, 0x00, + 0x72, 0xDB, 0xE6, 0x0D, 0x0A, 0x0C, 0x36, 0x9E, 0xDB, 0x26, 0xA7, 0x19, 0x27, 0x9C, 0x0A, 0xAB, 0x34, 0x58, 0x74, 0x32, 0x5A, 0xF1, 0x75, 0x67, + 0xEC, 0xAE, 0x85, 0x1B, 0x7F, 0x63, 0xFE, 0xE4, 0xD3, 0x64, 0x5E, 0x38, 0x36, 0xB2, 0x95, 0x40, 0xE8, 0x3B, 0xF5, 0x51, 0x40, 0xBC, 0x48, 0xA5, + 0x01, 0xAD, 0x55, 0xFA, 0xF1, 0x3F, 0x74, 0xDA, 0x0C, 0x8B, 0x77, 0x0F, 0xCC, 0xA6, 0x74, 0x54, 0x32, 0x36, 0x75, 0xD4, 0xCE, 0x6F, 0x82, 0x9C, + 0x89, 0x98, 0x8A, 0x53, 0x70, 0x85, 0xC0, 0xAA, 0x70, 0xA4, 0xC9, 0x67, 0xC3, 0x6C, 0x67, 0x96, 0x91, 0x75, 0x86, 0x98, 0xB7, 0xD4, 0xEB, 0x38, + 0x37, 0x0F, 0x9A, 0xAA, 0xAD, 0x21, 0x62, 0x1A, 0x46, 0x7A, 0x1B, 0x56, 0xC2, 0xAB, 0xE3, 0x69, 0x43, 0x2C, 0x5D, 0xDB, 0x4E, 0x7B, 0xFA, 0xB4, + 0x33, 0x4C, 0xF3, 0xA8, 0x60, 0xD8, 0xCE, 0xB6, 0x2D, 0xDF, 0xD8, 0xF1, 0xA3, 0xA3, 0x8A, 0x1B, 0xA8, 0xD4, 0x2C, 0x12, 0xCB, 0x74, 0x1F, 0xFA, + 0x59, 0x9A, 0x86, 0x34, 0x8A, 0x57, 0xF2, 0xEA, 0x8D, 0x32, 0x2C, 0xC7, 0xEE, 0x84, 0xFD, 0x7C, 0x9D, 0x8E, 0x83, 0x83, 0x58, 0x95, 0x38, 0x9B, + 0x84, 0xE7, 0xA2, 0x1C, 0x7E, 0xB3, 0x2F, 0x78, 0xE6, 0x30, 0x44, 0x8E, 0xBF, 0x62, 0xC4, 0x14, 0xA4, 0xAE, 0x62, 0x5C, 0xB4, 0x8B, 0x30, 0xD2, + 0x5A, 0xFD, 0x3D, 0xEF, 0x7E, 0x68, 0x34, 0xDB, 0x77, 0xA1, 0xED, 0x9F, 0x63, 0xC7, 0x18, 0x20, 0x5E, 0xA2, 0x93, 0x04, 0xFC, 0x48, 0xA1, 0x2D, + 0x9C, 0xEB, 0xCE, 0x81, 0x11, 0xDA, 0x08, 0x42, 0xC0, 0x76, 0xB6, 0xDE, 0x81, 0xDA, 0x7A, 0x5C, 0xAC, 0xBB, 0x8F, 0x57, 0x6D, 0x57, 0x41, 0x6E, + 0x25, 0xD3, 0xAC, 0x2E, 0xC1, 0xBB, 0xD9, 0x9D, 0x0B, 0x49, 0x76, 0x5A, 0xFB, 0x21, 0x64, 0x0A, 0x15, 0xB8, 0x61, 0x77, 0xBD, 0x14, 0xC4, 0xD9, + 0xA5, 0x49, 0x5C, 0x88, 0x96, 0xAB, 0x6E, 0x03, 0x10, 0xC4, 0x66, 0x09, 0x7A, 0x52, 0xD1, 0xB6, 0xBB, 0xAD, 0xDA, 0x9E, 0x0A, 0x15, 0xAB, 0xDE, + 0xDD, 0x52, 0x0D, 0x77, 0x2F, 0xA6, 0x6D, 0x17, 0xE1, 0x38, 0x76, 0x3E, 0xAF, 0x57, 0x22, 0x73, 0x12, 0x33, 0xB6, 0x08, 0xEC, 0x50, 0xAD, 0xCD, + 0x82, 0x20, 0xF5, 0xFF, 0x78, 0xC0, 0x96, 0xD9, 0x91, 0x09, 0x69, 0xDC, 0xF4, 0x58, 0xD6, 0x09, 0xB5, 0x96, 0xA2, 0xDF, 0xF9, 0x9E, 0xDF, 0x85, + 0x54, 0xCC, 0x9C, 0xF6, 0xC4, 0x12, 0x3B, 0xB7, 0xC2, 0x63, 0x0F, 0x08, 0x59, 0xF9, 0x3F, 0xBB, 0x98, 0x92, 0x39, 0xB0, 0x30, 0x47, 0xD7, 0xFF, + 0xA6, 0xCD, 0x05, 0xAA, 0x3A, 0x36, 0xBC, 0x6E, 0x3C, 0x3D, 0xA3, 0x21, 0x0D, 0x9D, 0xD7, 0x7E, 0x69, 0x3D, 0xE6, 0x30, 0x0E, 0x78, 0x88, 0x5B, + 0xC4, 0x94, 0xD9, 0xE3, 0x93, 0x93, 0x1E, 0x87, 0x2F, 0x62, 0x5D, 0x3A, 0x96, 0x47, 0x53, 0x82, 0xC5, 0xEE, 0x2A, 0x77, 0x17, 0xC2, 0x2E, 0x43, + 0xD0, 0x85, 0x94, 0x6D, 0x08, 0x40, 0xEF, 0x4D, 0xE3, 0xC5, 0xE3, 0xE5, 0xEC, 0xBF, 0x83, 0xD4, 0x95, 0x5D, 0xE2, 0x1E, 0x52, 0x91, 0xCA, 0x8C, + 0xCB, 0x8B, 0x5A, 0x84, 0x32, 0x61, 0x60, 0x0F, 0xEA, 0x43, 0xB2, 0xB9, 0xCF, 0xA0, 0x31, 0x28, 0xB0, 0xDD, 0x52, 0x9D, 0x4B, 0xF8, 0x79, 0x8E, + 0xE0, 0x45, 0xDA, 0x8C, 0x5A, 0x6E, 0xD7, 0x01, 0x76, 0xE5, 0x10, 0xBA, 0x94, 0x9C, 0xD6, 0xB9, 0x1A, 0xD6, 0x60, 0xEE, 0x14, 0x8A, 0xD0, 0xF1, + 0x73, 0x99, 0xEF, 0x90, 0x53, 0x40, 0xB8, 0x29, 0x3F, 0x57, 0xB9, 0x45, 0x9C, 0x1D, 0xED, 0x82, 0xB7, 0x51, 0x57, 0x8A, 0x97, 0x9F, 0xD3, 0x0D, + 0x49, 0x37, 0x7F, 0xC5, 0x64, 0x87, 0xC5, 0x06, 0x6E, 0x76, 0xB9, 0x4D, 0x6D, 0x0C, 0x7B, 0xF3, 0xF6, 0xF0, 0x62, 0xAD, 0xFA, 0x2E, 0x16, 0x06, + 0xF1, 0x7C, 0xB5, 0x1C, 0xFE, 0x8D, 0x8C, 0xF0, 0x5C, 0xE8, 0x6B, 0x5C, 0x41, 0x2F, 0xF2, 0xA0, 0x37, 0xA2, 0xE6, 0xDE, 0x61, 0x87, 0x16, 0x31, + 0x64, 0xA1, 0xE7, 0xE5, 0xAC, 0x34, 0xB5, 0xCA, 0x65, 0xFE, 0x91, 0x64, 0x25, 0x93, 0x83, 0x0E, 0x38, 0x70, 0xD0, 0x01, 0x87, 0x1D, 0x38, 0xE4, + 0xB0, 0x43, 0x07, 0x1D, 0x80, 0x15, 0x2F, 0xE0, 0x62, 0x5E, 0xD0, 0x41, 0x85, 0x02, 0x0A, 0x28, 0x50, 0x40, 0x01, 0x15, 0x94, 0x87, 0xDE, 0x28, + 0xFC, 0xB6, 0x38, 0x8A, 0xD1, 0xD0, 0x78, 0x9A, 0x2B, 0xED, 0xF0, 0x44, 0x32, 0x29, 0x39, 0x62, 0xAE, 0x35, 0x87, 0x5A, 0x85, 0xC1, 0x8C, 0x34, + 0xE2, 0x42, 0xBF, 0x9D, 0x7B, 0x89, 0xB6, 0x9A, 0x86, 0x79, 0xAC, 0x4E, 0x43, 0x27, 0xA0, 0x4E, 0xDA, 0xCA, 0xD1, 0xF3, 0x22, 0xEA, 0x23, 0x8D, + 0xE2, 0x5E, 0xEF, 0x32, 0x46, 0xE4, 0xAB, 0x05, 0x97, 0x04, 0xE2, 0x7D, 0x00, 0x7E, 0xAA, 0x46, 0x2B, 0xCB, 0xA8, 0x35, 0x7D, 0xC9, 0xD6, 0x29, + 0x24, 0x4A, 0xDF, 0xD3, 0x8F, 0x69, 0x4A, 0xB2, 0xA9, 0x4A, 0x16, 0x04, 0x5C, 0xE0, 0xE3, 0x7D, 0x44, 0x02, 0xB9, 0xD2, 0xAC, 0xB4, 0xB1, 0x2E, + 0x9B, 0x91, 0xF2, 0xEC, 0xBB, 0xDF, 0xFE, 0xDC, 0xA1, 0x6D, 0xE9, 0x2A, 0x93, 0xAF, 0x7D, 0x4B, 0x9C, 0x56, 0x96, 0xC9, 0x38, 0xF4, 0x85, 0x87, + 0x91, 0x7F, 0x09, 0xA7, 0xDD, 0x42, 0x92, 0x2E, 0x64, 0x4B, 0x8C, 0x9F, 0x1F, 0x31, 0x35, 0x4E, 0x7F, 0xF9, 0x96, 0x39, 0x19, 0x1B, 0xE1, 0x8E, + 0x81, 0x31, 0xAD, 0x8B, 0xD6, 0x28, 0xB5, 0x10, 0x4E, 0x60, 0x95, 0x34, 0x01, 0x2B, 0xC6, 0x01, 0x5B, 0x09, 0x27, 0x19, 0x96, 0x84, 0x01, 0xAA, + 0x37, 0x9F, 0x4D, 0x68, 0x9D, 0x61, 0x4B, 0xCD, 0x90, 0x0A, 0x0E, 0x9C, 0x8C, 0x39, 0x19, 0x06, 0x75, 0x12, 0x75, 0x31, 0xDC, 0x15, 0xCC, 0x0D, + 0xCA, 0xC2, 0x58, 0x9A, 0x04, 0xC2, 0xDC, 0x1D, 0x37, 0x0C, 0x1E, 0xDE, 0xE2, 0x3B, 0x55, 0x00, 0x9E, 0x6D, 0x8A, 0xAF, 0x4A, 0x30, 0x15, 0x49, + 0xFD, 0x33, 0xA5, 0xF8, 0x19, 0xD2, 0x89, 0xA4, 0x87, 0xBE, 0x7A, 0x83, 0x99, 0xE8, 0x1B, 0x7B, 0x50, 0x81, 0xA3, 0x8F, 0xF9, 0x7C, 0xE5, 0x04, + 0x4C, 0x99, 0xCC, 0xB4, 0x66, 0x43, 0xEF, 0x6B, 0xB7, 0x52, 0x93, 0x42, 0xEB, 0x03, 0xF5, 0x8A, 0xCD, 0xFB, 0x9A, 0x42, 0x97, 0xF9, 0x84, 0x05, + 0x91, 0xE9, 0x10, 0xAE, 0xCE, 0x30, 0x1E, 0x23, 0x83, 0x54, 0xF5, 0x65, 0xFD, 0x43, 0xB0, 0xBC, 0x71, 0x71, 0xCA, 0x46, 0x17, 0x68, 0x33, 0x4F, + 0x46, 0x1B, 0x6B, 0x62, 0x24, 0x71, 0xDE, 0x34, 0xC1, 0xFD, 0xAD, 0x21, 0x0A, 0x69, 0xCE, 0x94, 0xEC, 0xD8, 0x08, 0xFA, 0x55, 0x95, 0xF7, 0x15, + 0x48, 0x42, 0x5C, 0x9C, 0x28, 0x98, 0xEB, 0xD1, 0x12, 0x45, 0xE1, 0x5C, 0xD7, 0x30, 0x62, 0xD1, 0x1D, 0xBE, 0xF2, 0x0D, 0xC6, 0x39, 0x99, 0x60, + 0xE1, 0xB0, 0x25, 0x4D, 0xC6, 0x97, 0x74, 0xD9, 0x15, 0x8B, 0xA7, 0xE2, 0x28, 0x75, 0x8E, 0xBD, 0x96, 0x35, 0x73, 0xB0, 0x34, 0x63, 0x46, 0xD8, + 0xB6, 0x9D, 0x47, 0x46, 0xE8, 0xC9, 0xE9, 0x58, 0x01, 0x77, 0x33, 0x16, 0x16, 0xD7, 0x94, 0xD6, 0x6F, 0xFF, 0x34, 0xB8, 0x27, 0xFC, 0x76, 0x66, + 0x4F, 0x49, 0x37, 0x23, 0x3E, 0xE3, 0x3B, 0xDE, 0xDA, 0x79, 0x6F, 0x61, 0x67, 0xB7, 0xFB, 0x82, 0x74, 0xD3, 0x30, 0x1A, 0x1A, 0xE6, 0x10, 0x96, + 0x8C, 0x69, 0x42, 0xF3, 0xA0, 0xE3, 0xDD, 0x9B, 0x50, 0x60, 0xAE, 0x28, 0xD8, 0x5A, 0xF5, 0xC7, 0x95, 0x59, 0x8F, 0x94, 0x76, 0x09, 0x74, 0x09, + 0x41, 0xC0, }; -const idx_t TZ_COMPRESSED_SIZE = 44667; -const idx_t TZ_UNCOMPRESSED_SIZE = 205625; -const char *const TZ_VERSION = "2026c"; +const idx_t TZ_COMPRESSED_SIZE = 44906; +const idx_t TZ_UNCOMPRESSED_SIZE = 209653; +const char *const TZ_VERSION = "2026e"; } // namespace datetime } // namespace duckdb diff --git a/src/duckdb/extension/icu/datetime/islamic.cpp b/src/duckdb/extension/icu/datetime/islamic.cpp index 0e9bef659..afbe6d85c 100644 --- a/src/duckdb/extension/icu/datetime/islamic.cpp +++ b/src/duckdb/extension/icu/datetime/islamic.cpp @@ -215,7 +215,7 @@ void IslamicCalendar::HandleComputeFields(int32_t julian_day) { // guess how many whole months have passed, then correct the guess auto month = int32_t(std::floor(double(days) / Astronomer::SYNODIC_MONTH)); const auto start_date = int32_t(std::floor(month * Astronomer::SYNODIC_MONTH)); - if (days - start_date >= 25 && MoonAge(GetTimeInternal()) > 0) { + if (days - start_date >= 25 && MoonAge(double(GetTimeInternal())) > 0) { // the day is near the end of the month, so assume the next one and search backwards month++; } @@ -291,7 +291,7 @@ int64_t IslamicUmalquraCalendar::YearStart(int32_t year) const { year -= TABLE_START; // a rounded least squares fit of the dates that follow from the observed month lengths, // with a correction for the years it does not land on - const auto estimate = int64_t((354.36720 * double(year)) + 460322.05 + 0.5); + const auto estimate = int64_t(std::llround((354.36720 * double(year)) + 460322.05)); return estimate + UMALQURA_YEAR_START_FIX[year]; } diff --git a/src/duckdb/extension/icu/datetime/tz_loader.cpp b/src/duckdb/extension/icu/datetime/tz_loader.cpp index ebef31a9b..1adb4fce5 100644 --- a/src/duckdb/extension/icu/datetime/tz_loader.cpp +++ b/src/duckdb/extension/icu/datetime/tz_loader.cpp @@ -114,7 +114,7 @@ static unique_ptr Decompress() { const auto data_offsets = reader.Read(header->zone_data_count); const auto name_offsets = reader.Read(header->zone_count); const auto rule_values = reader.Read(header->rule_count * RULE_FIELD_COUNT); - const auto windows_entries = reader.Read(header->windows_zone_count * 3); + const auto windows_entries = reader.Read(static_cast(header->windows_zone_count) * 3); reader.Align(sizeof(uint16_t)); const auto data_indexes = reader.Read(header->zone_count); const auto pool = reinterpret_cast(reader.Read(header->pool_size)); diff --git a/src/duckdb/extension/icu/icu-current.cpp b/src/duckdb/extension/icu/icu-current.cpp index 7c8dd2ad3..c9f295bee 100644 --- a/src/duckdb/extension/icu/icu-current.cpp +++ b/src/duckdb/extension/icu/icu-current.cpp @@ -35,6 +35,7 @@ static void CurrentDateFunction(DataChunk &input, ExpressionState &state, Vector ScalarFunction GetCurrentTimeFun() { ScalarFunction current_time({}, LogicalType::TIME_TZ, CurrentTimeFunction); current_time.SetStability(FunctionStability::CONSISTENT_WITHIN_QUERY); + current_time.SetFallible(); return current_time; } diff --git a/src/duckdb/extension/icu/icu-dateadd.cpp b/src/duckdb/extension/icu/icu-dateadd.cpp index 370ca6125..5f291e146 100644 --- a/src/duckdb/extension/icu/icu-dateadd.cpp +++ b/src/duckdb/extension/icu/icu-dateadd.cpp @@ -133,7 +133,7 @@ timestamp_tz_t ICUCalendarAdd::Operation(interval_t interval, timestamp_tz_t tim template <> timestamp_tz_t ICUCalendarSub::Operation(timestamp_tz_t timestamp, interval_t interval, TZCalendar &calendar) { - const interval_t negated {-interval.months, -interval.days, -interval.micros}; + const auto negated = Interval::Invert(interval); return ICUCalendarAdd::template Operation(timestamp, negated, calendar); } @@ -263,10 +263,16 @@ struct ICUDateAdd : public ICUDateFunc { static void AddDateAddOperators(const Identifier &name, ExtensionLoader &loader) { // temporal + interval ScalarFunctionSet set {name}; - set.AddFunction(GetDateAddFunction(LogicalType::TIMESTAMP_TZ, - LogicalType::INTERVAL)); - set.AddFunction(GetDateAddFunction(LogicalType::INTERVAL, - LogicalType::TIMESTAMP_TZ)); + auto tstz_interval_fun = GetDateAddFunction( + LogicalType::TIMESTAMP_TZ, LogicalType::INTERVAL); + tstz_interval_fun.GetSignature().GetParameter(0).SetName("left"); + tstz_interval_fun.GetSignature().GetParameter(1).SetName("right"); + set.AddFunction(tstz_interval_fun); + auto interval_tstz_fun = GetDateAddFunction( + LogicalType::INTERVAL, LogicalType::TIMESTAMP_TZ); + interval_tstz_fun.GetSignature().GetParameter(0).SetName("left"); + interval_tstz_fun.GetSignature().GetParameter(1).SetName("right"); + set.AddFunction(interval_tstz_fun); // throws for dates that overflow the timestamp range set.SetFallible(); loader.RegisterFunction(set); @@ -285,12 +291,18 @@ struct ICUDateAdd : public ICUDateFunc { static void AddDateSubOperators(const Identifier &name, ExtensionLoader &loader) { // temporal - interval ScalarFunctionSet set {name}; - set.AddFunction(GetDateAddFunction(LogicalType::TIMESTAMP_TZ, - LogicalType::INTERVAL)); + auto tstz_interval_fun = GetDateAddFunction( + LogicalType::TIMESTAMP_TZ, LogicalType::INTERVAL); + tstz_interval_fun.GetSignature().GetParameter(0).SetName("left"); + tstz_interval_fun.GetSignature().GetParameter(1).SetName("right"); + set.AddFunction(tstz_interval_fun); // temporal - temporal - set.AddFunction(GetBinaryAgeFunction( - LogicalType::TIMESTAMP_TZ, LogicalType::TIMESTAMP_TZ)); + auto tstz_tstz_fun = GetBinaryAgeFunction( + LogicalType::TIMESTAMP_TZ, LogicalType::TIMESTAMP_TZ); + tstz_tstz_fun.GetSignature().GetParameter(0).SetName("left"); + tstz_tstz_fun.GetSignature().GetParameter(1).SetName("right"); + set.AddFunction(tstz_tstz_fun); // throws for dates that overflow the timestamp range set.SetFallible(); loader.RegisterFunction(set); diff --git a/src/duckdb/extension/icu/icu-datefunc.cpp b/src/duckdb/extension/icu/icu-datefunc.cpp index 633ef530d..ab10a86c7 100644 --- a/src/duckdb/extension/icu/icu-datefunc.cpp +++ b/src/duckdb/extension/icu/icu-datefunc.cpp @@ -18,18 +18,9 @@ ICUDateFunc::BindData::BindData(const string &tz_setting_p, const string &cal_se InitCalendar(); } -ICUDateFunc::BindData::BindData(ClientContext &context) { - Value tz_value; - if (context.TryGetCurrentSetting("TimeZone", tz_value)) { - tz_setting = tz_value.ToString(); - } - - Value cal_value; - if (context.TryGetCurrentSetting("Calendar", cal_value)) { - cal_setting = cal_value.ToString(); - } else { - cal_setting = "gregorian"; - } +ICUDateFunc::BindData::BindData(ClientContext &context) : cal_setting("gregorian") { + context.TryGetCurrentSetting("TimeZone", tz_setting); + context.TryGetCurrentSetting("Calendar", cal_setting); InitCalendar(); } @@ -87,6 +78,17 @@ Calendar *ICUDateFunc::CalendarCacheState::GetCalendar(const string_t &tz_id) { return entry.calendar.get(); } +Calendar *ICUDateFunc::CalendarCacheState::TryGetCalendar(const string_t &tz_id, string *error_message) { + auto &entry = GetEntry(tz_id); + if (!entry.calendar) { + if (error_message) { + *error_message = entry.error; + } + return nullptr; + } + return entry.calendar.get(); +} + void ICUDateFunc::CalendarCacheState::SetTimeZone(Calendar *calendar, const string_t &tz_id, string *error_message) { auto &entry = GetEntry(tz_id); if (!entry.tz) { @@ -126,7 +128,13 @@ timestamp_tz_t ICUDateFunc::GetTimeUnsafe(Calendar *calendar, uint64_t micros) { if (calendar->HasFailed()) { throw InternalException("Unable to get calendar time."); } - return timestamp_tz_t(millis * Interval::MICROS_PER_MSEC + int64_t(micros)); + // The time is a whole number of milliseconds, so converting it back to microseconds can overflow. + int64_t result; + if (!TryMultiplyOperator::Operation(millis, Interval::MICROS_PER_MSEC, result) || + !TryAddOperator::Operation(result, int64_t(micros), result)) { + throw OutOfRangeException("Unable to get calendar time: timestamp out of range"); + } + return timestamp_tz_t(result); } bool ICUDateFunc::TryGetTime(Calendar *calendar, uint64_t micros, timestamp_tz_t &result) { @@ -169,7 +177,7 @@ bool ICUDateFunc::TryGetTimeNS(Calendar *calendar, uint64_t nanos, timestamp_tz_ nanos %= Interval::NANOS_PER_MICRO; timestamp_t us(tstz_micros); timestamp_ns_t ns; - if (!Timestamp::TryFromTimestampNanos(us, nanos, ns)) { + if (!Timestamp::TryFromTimestampNanos(us, int32_t(nanos), ns)) { return false; } result.value = ns.value; @@ -217,6 +225,27 @@ int32_t ICUDateFunc::ExtractField(Calendar *calendar, CalendarField field) { return result; } +bool ICUDateFunc::TryGetTimeTZOffset(Calendar *calendar, int32_t &offset) { + offset = ExtractField(calendar, CAL_ZONE_OFFSET); + offset += ExtractField(calendar, CAL_DST_OFFSET); + offset /= Interval::MSECS_PER_SEC; + return offset >= dtime_tz_t::MIN_OFFSET && offset <= dtime_tz_t::MAX_OFFSET; +} + +int32_t ICUDateFunc::GetTimeTZOffset(Calendar *calendar) { + int32_t offset; + if (!TryGetTimeTZOffset(calendar, offset)) { + throw OutOfRangeException(TimeTZOffsetError(offset)); + } + return offset; +} + +string ICUDateFunc::TimeTZOffsetError(int32_t offset) { + interval_t interval {0, 0, offset * Interval::MICROS_PER_SEC}; + return StringUtil::Format("Time zone offset %s is out of range, expected a value between -15:59:59 and +15:59:59", + Interval::ToString(interval)); +} + int32_t ICUDateFunc::SubtractField(Calendar *calendar, CalendarField field, timestamp_tz_t end_date) { const int64_t millis = end_date.value / Interval::MICROS_PER_MSEC; const auto sub = calendar->FieldDifference(millis, field); diff --git a/src/duckdb/extension/icu/icu-datepart.cpp b/src/duckdb/extension/icu/icu-datepart.cpp index 47fbefad3..97fc88e4e 100644 --- a/src/duckdb/extension/icu/icu-datepart.cpp +++ b/src/duckdb/extension/icu/icu-datepart.cpp @@ -3,6 +3,8 @@ #include "include/icu-datepart.hpp" #include "include/icu-datefunc.hpp" +#include "duckdb/catalog/catalog.hpp" +#include "duckdb/catalog/catalog_entry/scalar_function_catalog_entry.hpp" #include "duckdb/main/extension/extension_loader.hpp" #include "duckdb/common/enums/date_part_specifier.hpp" #include "duckdb/common/types/date.hpp" @@ -10,6 +12,7 @@ #include "duckdb/common/vector_operations/unary_executor.hpp" #include "duckdb/common/vector_operations/binary_executor.hpp" #include "duckdb/execution/expression_executor.hpp" +#include "duckdb/function/function_binder.hpp" #include "duckdb/main/client_context.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" @@ -124,13 +127,13 @@ struct ICUDatePart : public ICUDateFunc { } static int64_t ExtractMicrosecond(Calendar *calendar, const uint64_t micros) { - return ExtractMillisecond(calendar, micros) * Interval::MICROS_PER_MSEC + micros; + return ExtractMillisecond(calendar, micros) * Interval::MICROS_PER_MSEC + NumericCast(micros); } static double ExtractEpoch(Calendar *calendar, const uint64_t micros) { // the milliseconds carry the fraction of a second, so the division has to keep it auto result = double(calendar->GetTime()) / double(Interval::MSECS_PER_SEC); - result += micros / double(Interval::MICROS_PER_SEC); + result += static_cast(micros) / double(Interval::MICROS_PER_SEC); return result; } @@ -162,7 +165,7 @@ struct ICUDatePart : public ICUDateFunc { frac *= Interval::MICROS_PER_MINUTE; frac += ExtractMicrosecond(calendar, micros); - double result = frac; + double result = static_cast(frac); result /= Interval::MICROS_PER_DAY; result += days; @@ -347,12 +350,12 @@ struct ICUDatePart : public ICUDateFunc { using bigints_t = vector; using doubles_t = vector; - BindStructData(ClientContext &context, part_codes_t &&part_codes_p) - : BindData(context), part_codes(part_codes_p) { + BindStructData(ClientContext &context, part_codes_t part_codes_p) + : BindData(context), part_codes(std::move(part_codes_p)) { InitFactories(); } - BindStructData(const string &tz_setting_p, const string &cal_setting_p, part_codes_t &&part_codes_p) - : BindData(tz_setting_p, cal_setting_p), part_codes(part_codes_p) { + BindStructData(const string &tz_setting_p, const string &cal_setting_p, part_codes_t part_codes_p) + : BindData(tz_setting_p, cal_setting_p), part_codes(std::move(part_codes_p)) { InitFactories(); } BindStructData(const BindStructData &other) @@ -472,7 +475,7 @@ struct ICUDatePart : public ICUDateFunc { auto &context = input.GetClientContext(); auto &arguments = input.GetArguments(); - // If we have a constant first argument, then patch in the unary function. + // Bind the equivalent unary function for a constant first argument. do { auto part_constant = input.TryGetConstant(0); if (!part_constant || part_constant->IsNull()) { @@ -482,18 +485,16 @@ struct ICUDatePart : public ICUDateFunc { const auto part_name = part_constant->ToString(); const auto part_code = GetDatePartSpecifier(part_name); arguments.erase(arguments.begin()); - bound_function.GetArguments().erase(bound_function.GetArguments().begin()); - bound_function.SetName(Identifier(DatePartUnaryFunctionName(part_code))); - - if (IsBigintDatepart(part_code)) { - bound_function.SetReturnType(LogicalType::BIGINT); - bound_function.SetFunctionCallback(UnaryTimestampFunction); - } else { - bound_function.SetReturnType(LogicalType::DOUBLE); - bound_function.SetFunctionCallback(UnaryTimestampFunction); - } - - return BindUnaryDatePart(input); + auto unary_name = bound_function.GetDefinition()->GetQualifiedName().WithName( + Identifier(DatePartUnaryFunctionName(part_code))); + auto &entry = Catalog::GetEntry(context, unary_name); + auto unary_function = entry.functions.GetFunctionByArguments(context, {arguments[0]->GetReturnType()}); + FunctionBinder binder(context); + auto unary_expression = binder.BindScalarFunction(std::move(unary_function), std::move(arguments)); + auto &unary = unary_expression->Cast(); + bound_function = std::move(unary.FunctionMutable()); + arguments = std::move(unary.GetChildrenMutable()); + return std::move(unary.BindInfoMutable()); } while (false); using data_t = BindAdapterData; @@ -575,6 +576,7 @@ struct ICUDatePart : public ICUDateFunc { ArgProperties unary_arg0_props = {}) { ScalarFunctionSet set {name}; set.AddFunction(GetUnaryPartCodeFunction(LogicalType::TIMESTAMP_TZ, result_type)); + set.SetFallible(); set.SetUnaryArgProperties(unary_arg0_props); loader.RegisterFunction(set); } @@ -624,6 +626,7 @@ struct ICUDatePart : public ICUDateFunc { static void AddLastDayFunctions(const Identifier &name, ExtensionLoader &loader) { ScalarFunctionSet set {name}; set.AddFunction(GetLastDayFunction(LogicalType::TIMESTAMP_TZ)); + set.SetFallible(); loader.RegisterFunction(set); } @@ -644,6 +647,7 @@ struct ICUDatePart : public ICUDateFunc { static void AddMonthNameFunctions(const Identifier &name, ExtensionLoader &loader) { ScalarFunctionSet set {name}; set.AddFunction(GetMonthNameFunction(LogicalType::TIMESTAMP_TZ)); + set.SetFallible(); loader.RegisterFunction(set); } @@ -664,6 +668,7 @@ struct ICUDatePart : public ICUDateFunc { static void AddDayNameFunctions(const Identifier &name, ExtensionLoader &loader) { ScalarFunctionSet set {name}; set.AddFunction(GetDayNameFunction(LogicalType::TIMESTAMP_TZ)); + set.SetFallible(); loader.RegisterFunction(set); } }; @@ -672,7 +677,8 @@ void RegisterICUDatePartFunctions(ExtensionLoader &loader) { // register the individual operators // year/decade use CAL_YEAR (year-of-era, positive in both BC and AD), which is non-monotonic - // across the BC/AD flip; leave them unannotated. era/century/millennium/isoyear are signed. + // across the BC/AD flip; leave them unannotated. century/millennium are negated in era 0, which is the only era + // of some calendars, and yearweek decreases within negative years, so these are unannotated as well. // BIGINTs ICUDatePart::AddUnaryPartCodeFunctions("era", loader, LogicalType::BIGINT, ArgProperties().NonDecreasing()); @@ -680,8 +686,8 @@ void RegisterICUDatePartFunctions(ExtensionLoader &loader) { ICUDatePart::AddUnaryPartCodeFunctions("month", loader); ICUDatePart::AddUnaryPartCodeFunctions("day", loader); ICUDatePart::AddUnaryPartCodeFunctions("decade", loader); - ICUDatePart::AddUnaryPartCodeFunctions("century", loader, LogicalType::BIGINT, ArgProperties().NonDecreasing()); - ICUDatePart::AddUnaryPartCodeFunctions("millennium", loader, LogicalType::BIGINT, ArgProperties().NonDecreasing()); + ICUDatePart::AddUnaryPartCodeFunctions("century", loader); + ICUDatePart::AddUnaryPartCodeFunctions("millennium", loader); ICUDatePart::AddUnaryPartCodeFunctions("microsecond", loader); ICUDatePart::AddUnaryPartCodeFunctions("millisecond", loader); ICUDatePart::AddUnaryPartCodeFunctions("second", loader); diff --git a/src/duckdb/extension/icu/icu-datesub.cpp b/src/duckdb/extension/icu/icu-datesub.cpp index d80be2ce8..1c8d48541 100644 --- a/src/duckdb/extension/icu/icu-datesub.cpp +++ b/src/duckdb/extension/icu/icu-datesub.cpp @@ -209,6 +209,20 @@ ICUDateFunc::part_sub_t ICUDateFunc::SubtractFactory(DatePartSpecifier type) { // MS-SQL differences can be computed using ICU by truncating both arguments // to the desired part precision and then applying ICU subtraction/difference struct ICUCalendarDiff : public ICUDateFunc { + static unique_ptr Bind(BindScalarFunctionInput &input) { + auto part_value = input.TryGetConstant(0); + if (part_value && !part_value->IsNull()) { + DatePartSpecifier part; + if (TryGetDatePartSpecifier(part_value->GetValue(), part) && + (part == DatePartSpecifier::DECADE || part == DatePartSpecifier::CENTURY || + part == DatePartSpecifier::MILLENNIUM)) { + // date_diff is not monotone for these parts because ICU years skip year 0 at the BC/AD boundary + input.GetBoundFunction().SetArgProperties({}); + } + } + return ICUDateFunc::Bind(input); + } + static int64_t DifferenceEra(Calendar *calendar, timestamp_tz_t start_date, timestamp_tz_t end_date) { SetTime(calendar, start_date); const auto start_era = ExtractField(calendar, CAL_ERA); diff --git a/src/duckdb/extension/icu/icu-datetrunc.cpp b/src/duckdb/extension/icu/icu-datetrunc.cpp index 13562cc85..2e980012f 100644 --- a/src/duckdb/extension/icu/icu-datetrunc.cpp +++ b/src/duckdb/extension/icu/icu-datetrunc.cpp @@ -1,6 +1,7 @@ #include "include/icu-datetrunc.hpp" #include "include/icu-datefunc.hpp" +#include "duckdb/common/enums/date_part_specifier.hpp" #include "duckdb/common/vector_operations/binary_executor.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" #include "duckdb/main/extension/extension_loader.hpp" @@ -9,6 +10,20 @@ namespace duckdb { struct ICUDateTrunc : public ICUDateFunc { + static unique_ptr Bind(BindScalarFunctionInput &input) { + auto part_value = input.TryGetConstant(0); + if (part_value && !part_value->IsNull()) { + DatePartSpecifier part; + if (TryGetDatePartSpecifier(part_value->GetValue(), part) && + (part == DatePartSpecifier::DECADE || part == DatePartSpecifier::CENTURY || + part == DatePartSpecifier::MILLENNIUM)) { + // date_trunc is not monotone for these parts because ICU years skip year 0 at the BC/AD boundary + input.GetBoundFunction().SetArgProperties({}); + } + } + return ICUDateFunc::Bind(input); + } + static void PreserveOffsets(Calendar *calendar) { // We have to extract _everything_ before setting anything // Otherwise ICU will clear the fStamp fields diff --git a/src/duckdb/extension/icu/icu-makedate.cpp b/src/duckdb/extension/icu/icu-makedate.cpp index b0dd06df5..b41f54893 100644 --- a/src/duckdb/extension/icu/icu-makedate.cpp +++ b/src/duckdb/extension/icu/icu-makedate.cpp @@ -51,7 +51,27 @@ bool ICUMakeDate::CastToDate(Vector &source, Vector &result, idx_t count, CastPa return true; } -BoundCastInfo ICUMakeDate::BindCastToDate(BindCastInput &input, const LogicalType &source, const LogicalType &target) { +bool ICUMakeDate::CastNsToDate(Vector &source, Vector &result, idx_t count, CastParameters ¶meters) { + auto &cast_data = parameters.cast_data->Cast(); + auto &info = cast_data.info->Cast(); + CalendarPtr calendar(info.calendar->Copy()); + + UnaryExecutor::Execute(source, result, count, [&](timestamp_tz_ns_t input) { + if (!input.IsFinite()) { + return Timestamp::GetDate(timestamp_t(input.value)); + } + // the date only needs the microsecond instant: floor the nanoseconds so no instant moves past midnight + const auto nanos = input.value; + auto micros = nanos / Interval::NANOS_PER_MICRO; + if (nanos % Interval::NANOS_PER_MICRO < 0) { + micros--; + } + return Operation(calendar.get(), timestamp_tz_t(micros)); + }); + return true; +} + +static unique_ptr BindCastToDateData(BindCastInput &input) { if (!input.context) { throw InternalException("Missing context for TIMESTAMPTZ to DATE cast."); } @@ -59,14 +79,21 @@ BoundCastInfo ICUMakeDate::BindCastToDate(BindCastInput &input, const LogicalTyp throw BinderException("Casting from TIMESTAMP WITH TIME ZONE to DATE without an explicit time zone " "has been disabled - use \"AT TIME ZONE ...\""); } + return make_uniq(make_uniq(*input.context)); +} - auto cast_data = make_uniq(make_uniq(*input.context)); +BoundCastInfo ICUMakeDate::BindCastToDate(BindCastInput &input, const LogicalType &source, const LogicalType &target) { + return BoundCastInfo(CastToDate, BindCastToDateData(input)); +} - return BoundCastInfo(CastToDate, std::move(cast_data)); +BoundCastInfo ICUMakeDate::BindCastNsToDate(BindCastInput &input, const LogicalType &source, + const LogicalType &target) { + return BoundCastInfo(CastNsToDate, BindCastToDateData(input)); } void ICUMakeDate::AddCasts(ExtensionLoader &loader) { loader.RegisterCastFunction(LogicalType::TIMESTAMP_TZ, LogicalType::DATE, BindCastToDate); + loader.RegisterCastFunction(LogicalType::TIMESTAMP_TZ_NS, LogicalType::DATE, BindCastNsToDate); } struct ICUMakeTimestampTZFunc : public ICUDateFunc { diff --git a/src/duckdb/extension/icu/icu-strptime.cpp b/src/duckdb/extension/icu/icu-strptime.cpp index 464c9f501..e53514780 100644 --- a/src/duckdb/extension/icu/icu-strptime.cpp +++ b/src/duckdb/extension/icu/icu-strptime.cpp @@ -38,8 +38,12 @@ TimestampComponents ICUHelpers::GetComponents(timestamp_tz_t ts, Calendar *calen TimestampComponents ICUHelpers::GetComponents(timestamp_tz_ns_t tsns, Calendar *calendar) { // Get the parts in the given time zone - auto ts_data = GetComponents(timestamp_tz_t(tsns.value / Interval::NANOS_PER_MICRO), calendar); - ts_data.nanosecond = tsns.value % Interval::NANOS_PER_MICRO; + int64_t micros; + int64_t nanos; + Timestamp::SplitTime(tsns.value, Interval::NANOS_PER_MICRO, micros, nanos); + + auto ts_data = GetComponents(timestamp_tz_t(micros), calendar); + ts_data.nanosecond = UnsafeNumericCast(nanos); return ts_data; } @@ -139,12 +143,9 @@ struct ICUStrptime : public ICUDateFunc { result = timestamp_tz_t(parsed.ToTimestamp()); return; } else { - // Set TZ first, if any. - if (!parsed.tz.empty()) { - cache.SetTimeZone(calendar, parsed.tz); - } - - result = GetTime(calendar, ToMicros(calendar, parsed, format)); + // Use the calendar of the parsed time zone, if any - the shared calendar keeps the session zone + auto row_calendar = parsed.tz.empty() ? calendar : cache.GetCalendar(parsed.tz); + result = GetTime(row_calendar, ToMicros(row_calendar, parsed, format)); return; } } @@ -162,11 +163,9 @@ struct ICUStrptime : public ICUDateFunc { result.value = parsed.ToTimestamp().value; return; } else { - // Set TZ first, if any. - if (!parsed.tz.empty()) { - cache.SetTimeZone(calendar, parsed.tz); - } - result = GetTimeNS(calendar, ToNanos(calendar, parsed, format)); + // Use the calendar of the parsed time zone, if any - the shared calendar keeps the session zone + auto row_calendar = parsed.tz.empty() ? calendar : cache.GetCalendar(parsed.tz); + result = GetTimeNS(row_calendar, ToNanos(row_calendar, parsed, format)); return; } } @@ -203,8 +202,9 @@ struct ICUStrptime : public ICUDateFunc { if (parsed.is_special) { result.value = parsed.ToTimestamp().value; return true; - } else if (parsed.tz.empty() || cache.TrySetTimeZone(calendar, parsed.tz)) { - if (TryGetTime(calendar, ToMicros(calendar, parsed, format), result)) { + } else { + auto row_calendar = parsed.tz.empty() ? calendar : cache.TryGetCalendar(parsed.tz); + if (row_calendar && TryGetTime(row_calendar, ToMicros(row_calendar, parsed, format), result)) { return true; } } @@ -222,8 +222,9 @@ struct ICUStrptime : public ICUDateFunc { if (parsed.is_special) { result.value = parsed.ToTimestamp().value; return true; - } else if (parsed.tz.empty() || cache.TrySetTimeZone(calendar, parsed.tz)) { - if (TryGetTimeNS(calendar, ToNanos(calendar, parsed, format), result)) { + } else { + auto row_calendar = parsed.tz.empty() ? calendar : cache.TryGetCalendar(parsed.tz); + if (row_calendar && TryGetTimeNS(row_calendar, ToNanos(row_calendar, parsed, format), result)) { return true; } } @@ -399,18 +400,24 @@ struct ICUStrptime : public ICUDateFunc { // Convert parts to a TZ (default or parsed) if no offset was provided auto calendar = cal.get(); - // Change TZ if one was provided. + // Use the calendar of the time zone if one was provided - the shared calendar keeps the session zone if (tz.GetSize()) { string error_msg; - cache.SetTimeZone(calendar, tz, &error_msg); - if (!error_msg.empty()) { + calendar = cache.TryGetCalendar(tz, &error_msg); + if (!calendar) { HandleCastError::AssignError(error_msg, parameters); return nullopt; } } // Now get the parts in the given time zone - return FromNaive(calendar, us); + timestamp_tz_t result; + string error_msg; + if (!TryFromNaive(calendar, us, result, error_msg)) { + HandleCastError::AssignError(error_msg, parameters); + return nullopt; + } + return result; } return timestamp_tz_t(us); @@ -422,10 +429,13 @@ struct ICUStrptime : public ICUDateFunc { auto &cache = parameters.local_state->Cast(); CalendarPtr cal(info.calendar->Copy()); + bool all_converted = true; UnaryExecutor::Execute(source, result, count, [&](string_t input) { - return VarcharToTimestampTZUS(cal, cache, input, parameters); + auto converted = VarcharToTimestampTZUS(cal, cache, input, parameters); + all_converted = all_converted && converted; + return converted; }); - return true; + return all_converted; } static bool VarcharToTimestampTZNS(Vector &source, Vector &result, idx_t count, CastParameters ¶meters) { @@ -434,11 +444,13 @@ struct ICUStrptime : public ICUDateFunc { auto &cache = parameters.local_state->Cast(); CalendarPtr cal(info.calendar->Copy()); + bool all_converted = true; UnaryExecutor::Execute( source, result, count, [&](string_t input) -> optional { int32_t nanos = 0; auto ts_us = VarcharToTimestampTZUS(cal, cache, input, parameters, &nanos); if (!ts_us) { + all_converted = false; return nullopt; } @@ -446,12 +458,13 @@ struct ICUStrptime : public ICUDateFunc { timestamp_ns_t result; if (!Timestamp::TryFromTimestampNanos(us, nanos, result)) { HandleCastError::AssignError(Timestamp::RangeError(input), parameters); + all_converted = false; return nullopt; } return timestamp_tz_ns_t(result); }); - return true; + return all_converted; } static bool VarcharToTimeTZ(Vector &source, Vector &result, idx_t count, CastParameters ¶meters) { @@ -459,6 +472,7 @@ struct ICUStrptime : public ICUDateFunc { auto &info = cast_data.info->Cast(); CalendarPtr cal(info.calendar->Copy()); + bool all_converted = true; UnaryExecutor::Execute( source, result, count, [&](string_t input) -> optional { dtime_tz_t result; @@ -469,15 +483,19 @@ struct ICUStrptime : public ICUDateFunc { if (!Time::TryConvertTimeTZ(str, len, pos, result, has_offset, false)) { auto msg = Time::ConversionError(string(str, len)); HandleCastError::AssignError(msg, parameters); + all_converted = false; return nullopt; } else if (!has_offset) { // Convert parts to a TZ (default or parsed) if no offset was provided auto calendar = cal.get(); // Extract the offset from the calendar - auto offset = ExtractField(calendar, CAL_ZONE_OFFSET); - offset += ExtractField(calendar, CAL_DST_OFFSET); - offset /= Interval::MSECS_PER_SEC; + int32_t offset; + if (!TryGetTimeTZOffset(calendar, offset)) { + HandleCastError::AssignError(TimeTZOffsetError(offset), parameters); + all_converted = false; + return nullopt; + } // Apply it to the offset +00 time we parsed. result = dtime_tz_t(result.time(), offset); @@ -485,7 +503,7 @@ struct ICUStrptime : public ICUDateFunc { return result; }); - return true; + return all_converted; } static BoundCastInfo BindCastFromVarchar(BindCastInput &input, const LogicalType &source, diff --git a/src/duckdb/extension/icu/icu-table-range.cpp b/src/duckdb/extension/icu/icu-table-range.cpp index 5f69878ef..0fd549a10 100644 --- a/src/duckdb/extension/icu/icu-table-range.cpp +++ b/src/duckdb/extension/icu/icu-table-range.cpp @@ -1,3 +1,4 @@ +#include "duckdb/common/atomic.hpp" #include "duckdb/common/exception.hpp" #include "duckdb/common/operator/subtract.hpp" #include "duckdb/common/types/interval.hpp" @@ -19,20 +20,10 @@ struct ICUTableRange { calendar(other.calendar->Copy()), cardinality(other.cardinality) { } - explicit ICURangeBindData(ClientContext &context, const vector &inputs) { - Value tz_value; - if (context.TryGetCurrentSetting("TimeZone", tz_value)) { - tz_setting = tz_value.ToString(); - } + explicit ICURangeBindData(ClientContext &context, const vector &inputs) : cal_setting("gregorian") { + context.TryGetCurrentSetting("TimeZone", tz_setting); + context.TryGetCurrentSetting("Calendar", cal_setting); auto tz = TimeZone::TryCreate(tz_setting); - - Value cal_value; - if (context.TryGetCurrentSetting("Calendar", cal_value)) { - cal_setting = cal_value.ToString(); - } else { - cal_setting = "gregorian"; - } - calendar = Calendar::TryCreate(cal_setting, tz ? std::move(tz) : TimeZone::TryCreate("UTC")); if (!calendar) { throw InternalException("Unable to create calendar."); @@ -69,10 +60,27 @@ struct ICUTableRange { idx_t cardinality; }; + //! Progress of a range used as a source (i.e. with a single input row) + struct ICURangeGlobalState : public GlobalTableFunctionState { + //! The bounds of the range, and the timestamp that is generated next + atomic start {0}; + atomic end {0}; + atomic current {0}; + + void Initialize(timestamp_tz_t start_p, timestamp_tz_t end_p) { + start.store(start_p.value, std::memory_order_relaxed); + end.store(end_p.value, std::memory_order_relaxed); + current.store(start_p.value, std::memory_order_relaxed); + } + }; + struct ICURangeLocalState : public LocalTableFunctionState { - ICURangeLocalState() { + explicit ICURangeLocalState(optional_ptr global_state) + : global_state(global_state ? &global_state->Cast() : nullptr) { } + optional_ptr global_state; + bool initialized_row = false; idx_t current_input_row = 0; timestamp_tz_t current_state; @@ -163,7 +171,24 @@ struct ICUTableRange { static unique_ptr RangeDateTimeLocalInit(ExecutionContext &context, TableFunctionInitInput &input, GlobalTableFunctionState *global_state) { - return make_uniq(); + return make_uniq(global_state); + } + + static unique_ptr RangeDateTimeGlobalInit(ClientContext &context, + TableFunctionInitInput &input) { + return make_uniq(); + } + + static double Progress(ClientContext &context, const FunctionData *bind_data, + const GlobalTableFunctionState *global_state) { + auto &state = global_state->Cast(); + auto start = static_cast(state.start.load(std::memory_order_relaxed)); + auto range = static_cast(state.end.load(std::memory_order_relaxed)) - start; + if (range == 0) { + return 0; + } + auto fraction = (static_cast(state.current.load(std::memory_order_relaxed)) - start) / range; + return 100.0 * MaxValue(MinValue(fraction, 1.0), 0.0); } static unique_ptr Cardinality(ClientContext &context, const FunctionData *bind_data_p) { @@ -192,6 +217,9 @@ struct ICUTableRange { GenerateRangeDateTimeParameters(input, state.current_input_row, state); state.initialized_row = true; state.current_state = state.start; + if (state.global_state) { + state.global_state->Initialize(state.start, state.end); + } } if (state.empty_range) { // empty range @@ -219,15 +247,22 @@ struct ICUTableRange { continue; } output.SetChildCardinality(size); + if (state.global_state) { + state.global_state->current.store(state.current_state.value, std::memory_order_relaxed); + } return OperatorResultType::HAVE_MORE_OUTPUT; } } static void AddICUTableRangeFunction(ExtensionLoader &loader) { + const auto timestamps = FunctionSignature() + .AddPositionalOnly("start", LogicalType::TIMESTAMP_TZ) + .AddPositionalOnly("stop", LogicalType::TIMESTAMP_TZ) + .AddPositionalOnly("step", LogicalType::INTERVAL); TableFunctionSet range("range"); - TableFunction range_function({LogicalType::TIMESTAMP_TZ, LogicalType::TIMESTAMP_TZ, LogicalType::INTERVAL}, - nullptr, Bind, nullptr, RangeDateTimeLocalInit); + TableFunction range_function(timestamps, nullptr, Bind, RangeDateTimeGlobalInit, RangeDateTimeLocalInit); range_function.in_out_function = ICUTableRangeFunction; + range_function.table_scan_progress = Progress; range_function.cardinality = Cardinality; range_function.return_type = TableFunctionReturnType::SET_RETURNING_FUNCTION; range.AddFunction(range_function); @@ -236,10 +271,10 @@ struct ICUTableRange { // generate_series: similar to range, but inclusive instead of exclusive bounds on the RHS TableFunctionSet generate_series("generate_series"); - TableFunction generate_series_function( - {LogicalType::TIMESTAMP_TZ, LogicalType::TIMESTAMP_TZ, LogicalType::INTERVAL}, nullptr, Bind, nullptr, - RangeDateTimeLocalInit); + TableFunction generate_series_function(timestamps, nullptr, Bind, RangeDateTimeGlobalInit, + RangeDateTimeLocalInit); generate_series_function.in_out_function = ICUTableRangeFunction; + generate_series_function.table_scan_progress = Progress; generate_series_function.cardinality = Cardinality; generate_series_function.return_type = TableFunctionReturnType::SET_RETURNING_FUNCTION; generate_series.AddFunction(generate_series_function); diff --git a/src/duckdb/extension/icu/icu-timebucket.cpp b/src/duckdb/extension/icu/icu-timebucket.cpp index 0a9586ab1..495ead083 100644 --- a/src/duckdb/extension/icu/icu-timebucket.cpp +++ b/src/duckdb/extension/icu/icu-timebucket.cpp @@ -73,6 +73,25 @@ struct ICUTimeBucket : public ICUDateFunc { return Add(calendar, origin, interval_t {0, 0, result_micros}); } + //! Add resolves a local time that occurs twice (e.g. when the clocks are turned back) to its latter occurrence. + //! If that is after ts, ts lies between the two occurrences, and its bucket starts at the former occurrence. + static timestamp_tz_t FormerOccurrence(Calendar *calendar, const timestamp_tz_t bucket, const timestamp_tz_t ts) { + auto get_offset = [&](const timestamp_tz_t instant) { + SetTime(calendar, instant); + return int64_t(ExtractField(calendar, CAL_ZONE_OFFSET)) + ExtractField(calendar, CAL_DST_OFFSET); + }; + const auto ts_offset = get_offset(ts); + const auto bucket_offset = get_offset(bucket); + if (ts_offset <= bucket_offset) { + return bucket; + } + const timestamp_tz_t former(bucket.value - (ts_offset - bucket_offset) * Interval::MICROS_PER_MSEC); + if (ts < former || get_offset(former) != ts_offset) { + return bucket; + } + return former; + } + static inline timestamp_tz_t WidthConvertibleToDaysCommon(int32_t bucket_width_days, const timestamp_tz_t ts, const timestamp_tz_t origin, TZCalendar &calendar) { if (!bucket_width_days) { @@ -87,9 +106,10 @@ struct ICUTimeBucket : public ICUDateFunc { } auto bucket = Add(calendar, origin, interval_t {0, static_cast(result_days), 0}); if (ts < bucket) { - D_ASSERT(ts < origin); + bucket = FormerOccurrence(calendar.GetCalendar(), bucket, ts); + } + while (ts < bucket) { bucket = Add(calendar, bucket, interval_t {0, -bucket_width_days, 0}); - D_ASSERT(ts > bucket); } return bucket; @@ -123,7 +143,14 @@ struct ICUTimeBucket : public ICUDateFunc { SubtractOperatorOverflowCheck::Operation(result_months, bucket_width_months); } - return Add(calendar_p, truncated_origin, interval_t {static_cast(result_months), 0, 0}); + auto bucket = Add(calendar_p, truncated_origin, interval_t {static_cast(result_months), 0, 0}); + if (ts < bucket) { + bucket = FormerOccurrence(calendar, bucket, ts); + } + while (ts < bucket) { + bucket = Add(calendar_p, bucket, interval_t {-bucket_width_months, 0, 0}); + } + return bucket; } template diff --git a/src/duckdb/extension/icu/icu-timezone.cpp b/src/duckdb/extension/icu/icu-timezone.cpp index b662c180c..b452d419b 100644 --- a/src/duckdb/extension/icu/icu-timezone.cpp +++ b/src/duckdb/extension/icu/icu-timezone.cpp @@ -19,7 +19,7 @@ struct ICUTimeZoneData : public GlobalTableFunctionState { } idx_t offset = 0; - double now; + int64_t now; }; static duckdb::unique_ptr ICUTimeZoneBind(ClientContext &context, TableFunctionBindInput &input, @@ -108,7 +108,7 @@ struct ICUFromNaiveTimestamp : public ICUDateFunc { return true; } - auto nanos = naive.value % Interval::NANOS_PER_MICRO; + auto nanos = UnsafeNumericCast(naive.value % Interval::NANOS_PER_MICRO); timestamp_t micros(naive.value / Interval::NANOS_PER_MICRO); timestamp_tz_t cast; if (!TryOperation(calendar, micros, cast, error)) { @@ -195,7 +195,7 @@ struct ICUFromNaiveTimestamp : public ICUDateFunc { return timestamp_tz_ns_t(naive); } - auto nanos = naive.value % Interval::NANOS_PER_MICRO; + auto nanos = UnsafeNumericCast(naive.value % Interval::NANOS_PER_MICRO); timestamp_t micros(naive.value / Interval::NANOS_PER_MICRO); timestamp_t cast(Operation(calendar, micros)); @@ -343,7 +343,7 @@ struct ICUToNaiveTimestamp : public ICUDateFunc { return true; } - auto nanos = instant.value % Interval::NANOS_PER_MICRO; + auto nanos = UnsafeNumericCast(instant.value % Interval::NANOS_PER_MICRO); timestamp_tz_t micros(instant.value / Interval::NANOS_PER_MICRO); timestamp_t cast; if (!TryOperation(calendar, micros, cast, error)) { @@ -523,17 +523,19 @@ dtime_tz_t ICUToTimeTZ::Operation(Calendar *calendar, dtime_tz_t timetz) { // Normalise to +00:00, add TZ offset, then set offset to TZ auto time = Time::NormalizeTimeTZ(timetz); - auto offset = ExtractField(calendar, CAL_ZONE_OFFSET); - offset += ExtractField(calendar, CAL_DST_OFFSET); - offset /= Interval::MSECS_PER_SEC; + auto offset = GetTimeTZOffset(calendar); date_t date(0); time = Interval::Add(time, {0, 0, offset * Interval::MICROS_PER_SEC}, date); return dtime_tz_t(time, offset); } -bool ICUToTimeTZ::ToTimeTZ(Calendar *calendar, timestamp_tz_t instant, dtime_tz_t &result) { +bool ICUToTimeTZ::ToTimeTZ(Calendar *calendar, timestamp_tz_t instant, dtime_tz_t &result, + optional_ptr error_message) { if (!instant.IsFinite()) { + if (error_message) { + *error_message = "Can't get TIME of infinite TIMESTAMP"; + } return false; } @@ -550,9 +552,13 @@ bool ICUToTimeTZ::ToTimeTZ(Calendar *calendar, timestamp_tz_t instant, dtime_tz_ const auto time = Time::FromTime(hour, minute, second, micros); // Offset in current TZ - auto offset = ExtractField(calendar, CAL_ZONE_OFFSET); - offset += ExtractField(calendar, CAL_DST_OFFSET); - offset /= Interval::MSECS_PER_SEC; + int32_t offset; + if (!error_message) { + offset = GetTimeTZOffset(calendar); + } else if (!TryGetTimeTZOffset(calendar, offset)) { + *error_message = TimeTZOffsetError(offset); + return false; + } result = dtime_tz_t(time, offset); return true; @@ -566,11 +572,14 @@ bool ICUToTimeTZ::CastToTimeTZ(Vector &source, Vector &result, idx_t count, Cast UnaryExecutor::Execute(source, result, count, [&](timestamp_tz_t input) -> optional { dtime_tz_t output; - if (ToTimeTZ(calendar.get(), input, output)) { + string error_message; + if (ToTimeTZ(calendar.get(), input, output, error_message)) { return output; - } else { - return nullopt; } + if (!error_message.empty()) { + HandleCastError::AssignError(error_message, parameters); + } + return nullopt; }); return true; } @@ -595,9 +604,15 @@ bool ICUToTimeTZ::CastFromTime(Vector &source, Vector &result, idx_t count, Cast // Read the session UTC offset (with DST) from the calendar. // This mirrors the no-offset branch in ICUStrptime::VarcharToTimeTZ so that // '00:00:00'::TIME::TIMETZ matches '00:00:00'::TIMETZ. - auto offset = ExtractField(calendar, CAL_ZONE_OFFSET); - offset += ExtractField(calendar, CAL_DST_OFFSET); - offset /= Interval::MSECS_PER_SEC; + int32_t offset; + if (!TryGetTimeTZOffset(calendar, offset)) { + auto error_message = TimeTZOffsetError(offset); + UnaryExecutor::Execute(source, result, count, [&](dtime_t input) -> optional { + HandleCastError::AssignError(error_message, parameters); + return nullopt; + }); + return false; + } UnaryExecutor::Execute(source, result, count, [&](dtime_t input) { return dtime_tz_t(input, offset); }); @@ -696,6 +711,10 @@ timestamp_tz_t ICUDateFunc::FromNaive(Calendar *calendar, timestamp_t naive) { return ICUFromNaiveTimestamp::Operation(calendar, naive); } +bool ICUDateFunc::TryFromNaive(Calendar *calendar, timestamp_t naive, timestamp_tz_t &result, string &error) { + return ICUFromNaiveTimestamp::TryOperation(calendar, naive, result, error); +} + void RegisterICUTimeZoneFunctions(ExtensionLoader &loader) { // Table functions TableFunction tz_names("pg_timezone_names", {}, ICUTimeZoneFunction, ICUTimeZoneBind, ICUTimeZoneInit); diff --git a/src/duckdb/extension/icu/icu_extension.cpp b/src/duckdb/extension/icu/icu_extension.cpp index a479ed703..985929511 100644 --- a/src/duckdb/extension/icu/icu_extension.cpp +++ b/src/duckdb/extension/icu/icu_extension.cpp @@ -55,7 +55,8 @@ struct IcuBindData : public FunctionData { explicit IcuBindData(string tag_p) : collator(SettingsFromTag(tag_p)), tag(std::move(tag_p)) { } - static duckdb::unique_ptr CreateInstance(string language, string country, string tag) { + static duckdb::unique_ptr CreateInstance(const string &language, const string &country, + const string &tag) { //! give priority to tagged collation if (!tag.empty()) { return make_uniq(tag); @@ -308,7 +309,8 @@ unique_ptr GetNormalizedTimeZone(string &tz_str) { mapped += hours_str; } // Final sanity check - if (tz = GetKnownTimeZone(mapped)) { + tz = GetKnownTimeZone(mapped); + if (tz) { tz_str = mapped; return tz; } @@ -355,6 +357,9 @@ unique_ptr ICUHelpers::GetTimeZone(string &tz_str, string *error_messa } static void SetICUTimeZone(ClientContext &context, SetScope scope, Value ¶meter) { + if (parameter.IsNull()) { + throw InvalidInputException("TimeZone setting cannot be NULL"); + } auto tz_str = StringValue::Get(parameter); ICUHelpers::GetTimeZone(tz_str); parameter = Value(tz_str); @@ -392,6 +397,9 @@ static void ICUCalendarFunction(ClientContext &context, TableFunctionInput &data } static void SetICUCalendar(ClientContext &context, SetScope scope, Value ¶meter) { + if (parameter.IsNull()) { + throw InvalidInputException("Calendar setting cannot be NULL"); + } const auto name = parameter.Value::GetValueUnsafe(); // Try to be friendlier: look for a case insensitive match, and if we don't find one, // make a suggestion @@ -463,6 +471,7 @@ static void LoadInternal(ExtensionLoader &loader) { loader.RegisterFunction(cal_names); } +// LCOV_EXCL_START void IcuExtension::Load(ExtensionLoader &loader) { LoadInternal(loader); } @@ -478,6 +487,7 @@ std::string IcuExtension::Version() const { return ""; #endif } +// LCOV_EXCL_STOP } // namespace duckdb diff --git a/src/duckdb/extension/icu/include/icu-casts.hpp b/src/duckdb/extension/icu/include/icu-casts.hpp index 131119de4..97f00f4cd 100644 --- a/src/duckdb/extension/icu/include/icu-casts.hpp +++ b/src/duckdb/extension/icu/include/icu-casts.hpp @@ -18,8 +18,10 @@ struct ICUMakeDate : public ICUDateFunc { static date_t Operation(Calendar *calendar, timestamp_tz_t instant); static bool CastToDate(Vector &source, Vector &result, idx_t count, CastParameters ¶meters); + static bool CastNsToDate(Vector &source, Vector &result, idx_t count, CastParameters ¶meters); static BoundCastInfo BindCastToDate(BindCastInput &input, const LogicalType &source, const LogicalType &target); + static BoundCastInfo BindCastNsToDate(BindCastInput &input, const LogicalType &source, const LogicalType &target); static void AddCasts(ExtensionLoader &loader); @@ -29,7 +31,10 @@ struct ICUMakeDate : public ICUDateFunc { struct ICUToTimeTZ : public ICUDateFunc { static dtime_tz_t Operation(Calendar *calendar, dtime_tz_t timetz); - static bool ToTimeTZ(Calendar *calendar, timestamp_tz_t instant, dtime_tz_t &result); + //! Returns false if the instant cannot be converted. If no error_message is provided, an out-of-range time zone + //! offset throws, otherwise the error is written to error_message. + static bool ToTimeTZ(Calendar *calendar, timestamp_tz_t instant, dtime_tz_t &result, + optional_ptr error_message = nullptr); static bool CastToTimeTZ(Vector &source, Vector &result, idx_t count, CastParameters ¶meters); static bool CastFromTime(Vector &source, Vector &result, idx_t count, CastParameters ¶meters); diff --git a/src/duckdb/extension/icu/include/icu-datefunc.hpp b/src/duckdb/extension/icu/include/icu-datefunc.hpp index 7f80bd1e9..c47777f8e 100644 --- a/src/duckdb/extension/icu/include/icu-datefunc.hpp +++ b/src/duckdb/extension/icu/include/icu-datefunc.hpp @@ -62,6 +62,8 @@ struct ICUDateFunc { //! Returns a calendar for the time zone. Throws for unknown zones. Calendar *GetCalendar(const string_t &tz_id); + //! Returns a calendar for the time zone, or nullptr for unknown zones (filling error_message if provided). + Calendar *TryGetCalendar(const string_t &tz_id, string *error_message = nullptr); //! Sets the time zone for the calendar. //! For unknown zones, fills error_message if provided and throws otherwise. void SetTimeZone(Calendar *calendar, const string_t &tz_id, string *error_message = nullptr); @@ -105,6 +107,11 @@ struct ICUDateFunc { static uint64_t SetTimeNS(Calendar *calendar, timestamp_tz_ns_t date); //! Extracts the field from the calendar static int32_t ExtractField(Calendar *calendar, CalendarField field); + //! Extracts the UTC offset (in seconds) of the calendar, returns false if a TIMETZ cannot represent it + static bool TryGetTimeTZOffset(Calendar *calendar, int32_t &offset); + //! Extracts the UTC offset (in seconds) of the calendar, throws if a TIMETZ cannot represent it + static int32_t GetTimeTZOffset(Calendar *calendar); + static string TimeTZOffsetError(int32_t offset); //! Subtracts the field of the given date from the calendar static int32_t SubtractField(Calendar *calendar, CalendarField field, timestamp_tz_t end_date); //! Adds the timestamp and the interval using the calendar @@ -117,6 +124,8 @@ struct ICUDateFunc { //! constructs an ICU timestamp, and then converts that back to a DuckDB instant //! Adding offset doesn't really work around DST because the bin values are ambiguous static timestamp_tz_t FromNaive(Calendar *calendar, timestamp_t naive); + //! Non-throwing form of FromNaive - sets the error and returns false if the result is out of range + static bool TryFromNaive(Calendar *calendar, timestamp_t naive, timestamp_tz_t &result, string &error); //! Truncates the calendar time to the given part precision typedef void (*part_trunc_t)(Calendar *calendar, uint64_t µs); diff --git a/src/duckdb/extension/json/include/json_common.hpp b/src/duckdb/extension/json/include/json_common.hpp index 13b55ff95..c3f41aaaa 100644 --- a/src/duckdb/extension/json/include/json_common.hpp +++ b/src/duckdb/extension/json/include/json_common.hpp @@ -92,6 +92,10 @@ class JSONPathIterator { //! Common JSON functionality for most JSON functions struct JSONCommon { public: + //! Deep copy of a mutable value - equivalent to yyjson_mut_val_mut_copy, but iterative instead of recursive, so + //! that deeply nested values do not overflow the stack + static yyjson_mut_val *MutValMutCopy(yyjson_mut_doc *doc, yyjson_mut_val *val); + //! Maximum recursion depth for recursive functions static constexpr idx_t MAX_RECURSION_DEPTH = 128; //! Read/Write flags diff --git a/src/duckdb/extension/json/include/json_functions.hpp b/src/duckdb/extension/json/include/json_functions.hpp index 88156d98d..a77283c6b 100644 --- a/src/duckdb/extension/json/include/json_functions.hpp +++ b/src/duckdb/extension/json/include/json_functions.hpp @@ -32,6 +32,7 @@ struct JSONReadFunctionData : public FunctionData { unique_ptr Copy() const override; bool Equals(const FunctionData &other_p) const override; static JSONCommon::JSONPathType CheckPath(const Value &path_val, string &path, idx_t &len); + static void ResolveTypes(ResolveScalarFunctionTypesInput &input); static unique_ptr Bind(BindScalarFunctionInput &input); public: diff --git a/src/duckdb/extension/json/include/json_reader.hpp b/src/duckdb/extension/json/include/json_reader.hpp index ec41fd1ea..1b7fd47e7 100644 --- a/src/duckdb/extension/json/include/json_reader.hpp +++ b/src/duckdb/extension/json/include/json_reader.hpp @@ -26,7 +26,7 @@ class JSONReader; struct JSONBufferHandle { public: - JSONBufferHandle(JSONReader &reader, idx_t buffer_index, idx_t readers, AllocatedData &&buffer, idx_t buffer_size, + JSONBufferHandle(JSONReader &reader, idx_t buffer_index, idx_t readers, AllocatedData buffer, idx_t buffer_size, idx_t buffer_start); public: @@ -58,6 +58,8 @@ struct JSONFileHandle { idx_t FileSize() const; idx_t Remaining() const; + //! The fraction of the file that has been read, in [0, 1] + double GetProgress() const; bool CanSeek() const; bool IsPipe() const; @@ -84,6 +86,8 @@ struct JSONFileHandle { //! File properties const bool can_seek; const idx_t file_size; + //! Whether the file is compressed - the read position then refers to the decompressed data + const bool compressed; //! Read properties atomic read_position; @@ -247,7 +251,7 @@ class JSONReader { void FinalizeBuffer(JSONReaderScanState &scan_state); //! Insert/get/remove buffer (grabs the lock) - void InsertBuffer(idx_t buffer_idx, unique_ptr &&buffer); + void InsertBuffer(idx_t buffer_idx, unique_ptr buffer); optional_ptr GetBuffer(idx_t buffer_idx); AllocatedData RemoveBuffer(JSONBufferHandle &handle); diff --git a/src/duckdb/extension/json/include/json_scan.hpp b/src/duckdb/extension/json/include/json_scan.hpp index f6b19ea49..7f3aafe80 100644 --- a/src/duckdb/extension/json/include/json_scan.hpp +++ b/src/duckdb/extension/json/include/json_scan.hpp @@ -186,8 +186,8 @@ struct JSONScan { vector &names); static void Serialize(Serializer &serializer, const optional_ptr bind_data, - const TableFunction &function); - static unique_ptr Deserialize(Deserializer &deserializer, TableFunction &function); + const BoundTableFunction &function); + static unique_ptr Deserialize(Deserializer &deserializer, BoundTableFunction &function); static void TableFunctionDefaults(TableFunction &table_function); //! The named parameters shared by all read_json variants diff --git a/src/duckdb/extension/json/include/json_stack.hpp b/src/duckdb/extension/json/include/json_stack.hpp new file mode 100644 index 000000000..a362cc0fe --- /dev/null +++ b/src/duckdb/extension/json/include/json_stack.hpp @@ -0,0 +1,24 @@ +#pragma once +#include "json_common.hpp" + +namespace duckdb { +template // preferably a struct +class Stack { +public: + void Push(STACK_ITEM item) { + stack.push_back(std::move(item)); + } + STACK_ITEM Pop() { + STACK_ITEM item = stack.back(); + stack.pop_back(); + return item; + } + bool Empty() const { + return stack.empty(); + } + +private: + vector stack; +}; + +} // namespace duckdb diff --git a/src/duckdb/extension/json/include/json_structure.hpp b/src/duckdb/extension/json/include/json_structure.hpp index 2ec777db9..f8a38f193 100644 --- a/src/duckdb/extension/json/include/json_structure.hpp +++ b/src/duckdb/extension/json/include/json_structure.hpp @@ -75,7 +75,8 @@ struct JSONStructureDescription { JSONStructureNode &GetOrCreateChild(); JSONStructureNode &GetOrCreateChild(const char *key_ptr, size_t key_size); - JSONStructureNode &GetOrCreateChild(yyjson_val *key, yyjson_val *val, bool ignore_errors, bool detect_geojson); + JSONStructureNode &GetOrCreateChild(yyjson_val *key, yyjson_val *val, bool ignore_errors, bool detect_geojson, + const idx_t depth); public: //! Type of this description @@ -97,7 +98,8 @@ struct JSONStructureDescription { struct JSONStructure { public: - static void ExtractStructure(yyjson_val *val, JSONStructureNode &node, bool ignore_errors, bool detect_geojson); + static void ExtractStructure(yyjson_val *val, JSONStructureNode &node, bool ignore_errors, bool detect_geojson, + const idx_t depth = 0); static void MergeNodes(JSONStructureNode &merged, const JSONStructureNode &node); static LogicalType StructureToType(ClientContext &context, const JSONStructureNode &node, idx_t max_depth, double field_appearance_threshold, idx_t map_inference_threshold, diff --git a/src/duckdb/extension/json/json_common.cpp b/src/duckdb/extension/json/json_common.cpp index 5aa54a8fc..b0db86a97 100644 --- a/src/duckdb/extension/json/json_common.cpp +++ b/src/duckdb/extension/json/json_common.cpp @@ -127,6 +127,10 @@ static inline idx_t ReadInteger(const char *ptr, const char *const end, idx_t &i } ptr++; } + if (ptr == end || *ptr != ']') { + // No closing ']', or too many digits + return 0; + } // Invalid if overflow return idx >= (idx_t)IDX_T_MAX ? 0 : ptr - before; } @@ -422,4 +426,77 @@ void JSONCommon::GetWildcardPath(yyjson_val *val, const char *ptr, const idx_t & GetWildcardPathInternal(val, ptr, end, vals); } +//! Shallow copy of a mutable value - the children of containers are copied by MutValMutCopy +static yyjson_mut_val *MutValShallowCopy(yyjson_mut_doc *doc, yyjson_mut_val *val) { + auto result = unsafe_yyjson_mut_val(doc, 1); + if (!result) { + return nullptr; + } + result->tag = val->tag; + switch (unsafe_yyjson_get_type(val)) { + case YYJSON_TYPE_OBJ: + case YYJSON_TYPE_ARR: + result->uni.ptr = nullptr; + break; + case YYJSON_TYPE_RAW: + case YYJSON_TYPE_STR: + result->uni.str = unsafe_yyjson_mut_strncpy(doc, val->uni.str, unsafe_yyjson_get_len(val)); + if (!result->uni.str) { + return nullptr; + } + break; + default: + result->uni = val->uni; + break; + } + return result; +} + +yyjson_mut_val *JSONCommon::MutValMutCopy(yyjson_mut_doc *doc, yyjson_mut_val *val) { + if (!doc || !val) { + return nullptr; + } + struct CopyEntry { + yyjson_mut_val *source; + yyjson_mut_val *target; + }; + vector containers; + auto result = MutValShallowCopy(doc, val); + if (!result) { + return nullptr; + } + if (unsafe_yyjson_is_ctn(val) && unsafe_yyjson_get_len(val) > 0) { + containers.push_back({val, result}); + } + while (!containers.empty()) { + auto entry = containers.back(); + containers.pop_back(); + // the children of a container form a circular list - the container points to the last child + auto last = reinterpret_cast(entry.source->uni.ptr); + auto source_child = last; + yyjson_mut_val *first_copy = nullptr; + yyjson_mut_val *prev_copy = nullptr; + do { + auto child_copy = MutValShallowCopy(doc, source_child); + if (!child_copy) { + return nullptr; + } + if (unsafe_yyjson_is_ctn(source_child) && unsafe_yyjson_get_len(source_child) > 0) { + containers.push_back({source_child, child_copy}); + } + if (prev_copy) { + prev_copy->next = child_copy; + } else { + first_copy = child_copy; + } + prev_copy = child_copy; + source_child = source_child->next; + } while (source_child != last); + // the copies were made starting from the last child - close the circle, and point the copy at the last child + prev_copy->next = first_copy; + entry.target->uni.ptr = first_copy; + } + return result; +} + } // namespace duckdb diff --git a/src/duckdb/extension/json/json_deserializer.cpp b/src/duckdb/extension/json/json_deserializer.cpp index 7ea58ce1c..647c82c32 100644 --- a/src/duckdb/extension/json/json_deserializer.cpp +++ b/src/duckdb/extension/json/json_deserializer.cpp @@ -164,7 +164,7 @@ int8_t JsonDeserializer::ReadSignedInt8() { if (!yyjson_is_int(val)) { ThrowTypeError(val, "int8_t"); } - return yyjson_get_sint(val); + return NumericCast(yyjson_get_sint(val)); } uint8_t JsonDeserializer::ReadUnsignedInt8() { @@ -180,7 +180,7 @@ int16_t JsonDeserializer::ReadSignedInt16() { if (!yyjson_is_int(val)) { ThrowTypeError(val, "int16_t"); } - return yyjson_get_sint(val); + return NumericCast(yyjson_get_sint(val)); } uint16_t JsonDeserializer::ReadUnsignedInt16() { @@ -196,7 +196,7 @@ int32_t JsonDeserializer::ReadSignedInt32() { if (!yyjson_is_int(val)) { ThrowTypeError(val, "int32_t"); } - return yyjson_get_sint(val); + return NumericCast(yyjson_get_sint(val)); } uint32_t JsonDeserializer::ReadUnsignedInt32() { diff --git a/src/duckdb/extension/json/json_extension.cpp b/src/duckdb/extension/json/json_extension.cpp index 6e6a32d39..5db13aab6 100644 --- a/src/duckdb/extension/json/json_extension.cpp +++ b/src/duckdb/extension/json/json_extension.cpp @@ -9,7 +9,6 @@ #include "duckdb/main/database.hpp" #include "duckdb/main/extension/extension_loader.hpp" #include "duckdb/parser/expression/function_expression.hpp" -#include "duckdb/parser/peg/compiled_grammar.hpp" namespace duckdb { @@ -84,14 +83,13 @@ static void LoadInternal(ExtensionLoader &loader) { geojson_copy_fun.SetName("geojsonl"); loader.RegisterFunction(geojson_copy_fun); - ParserOptions parser_options; - parser_options.compiled_grammar = loader.GetDatabaseInstance().GetParserCache().GetMatcher(); for (idx_t index = 0; JSON_MACROS[index].name != nullptr; index++) { - auto info = DefaultFunctionGenerator::CreateInternalMacroInfo(JSON_MACROS[index], parser_options); + auto info = DefaultFunctionGenerator::CreateInternalMacroInfo(JSON_MACROS[index]); loader.RegisterFunction(*info); } } +// LCOV_EXCL_START void JsonExtension::Load(ExtensionLoader &loader) { LoadInternal(loader); } @@ -107,6 +105,7 @@ std::string JsonExtension::Version() const { return ""; #endif } +// LCOV_EXCL_STOP } // namespace duckdb diff --git a/src/duckdb/extension/json/json_functions.cpp b/src/duckdb/extension/json/json_functions.cpp index daabd5c1b..f82779f4f 100644 --- a/src/duckdb/extension/json/json_functions.cpp +++ b/src/duckdb/extension/json/json_functions.cpp @@ -63,9 +63,19 @@ bool JSONReadFunctionData::Equals(const FunctionData &other_p) const { return constant == other.constant && path == other.path && len == other.len && path_type == other.path_type; } +//! An integral path is an array index, other paths are cast to VARCHAR +void JSONReadFunctionData::ResolveTypes(ResolveScalarFunctionTypesInput &input) { + auto &bound_function = input.GetBoundFunction(); + D_ASSERT(bound_function.GetArguments().size() == 2); + if (input.GetArgumentType(1).IsIntegral()) { + bound_function.GetArguments()[1] = LogicalType::BIGINT; + } else { + bound_function.GetArguments()[1] = LogicalType::VARCHAR; + } +} + unique_ptr JSONReadFunctionData::Bind(BindScalarFunctionInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); D_ASSERT(bound_function.GetArguments().size() == 2); bool constant = false; string path; @@ -76,11 +86,6 @@ unique_ptr JSONReadFunctionData::Bind(BindScalarFunctionInput &inp constant = true; path_type = CheckPath(*path_val, path, len); } - if (arguments[1]->GetReturnType().IsIntegral()) { - bound_function.GetArguments()[1] = LogicalType::BIGINT; - } else { - bound_function.GetArguments()[1] = LogicalType::VARCHAR; - } if (path_type == JSONCommon::JSONPathType::WILDCARD) { bound_function.SetReturnType(LogicalType::LIST(bound_function.GetReturnType())); } diff --git a/src/duckdb/extension/json/json_functions/json_array_length.cpp b/src/duckdb/extension/json/json_functions/json_array_length.cpp index f505bc30b..a6637ef03 100644 --- a/src/duckdb/extension/json/json_functions/json_array_length.cpp +++ b/src/duckdb/extension/json/json_functions/json_array_length.cpp @@ -26,6 +26,7 @@ static void GetArrayLengthFunctionsInternal(ScalarFunctionSet &set, const Logica ScalarFunction path_fun({}, LogicalType::UBIGINT, BinaryArrayLengthFunction, JSONReadFunctionData::Bind, nullptr, JSONFunctionLocalState::Init); path_fun.GetSignature().AddParameter("json", input_type).AddParameter("path", LogicalType::VARCHAR); + path_fun.SetResolveTypesCallback(JSONReadFunctionData::ResolveTypes); set.AddFunction(path_fun); ScalarFunction many_fun({}, LogicalType::LIST(LogicalType::UBIGINT), ManyArrayLengthFunction, JSONReadManyFunctionData::Bind, nullptr, JSONFunctionLocalState::Init); diff --git a/src/duckdb/extension/json/json_functions/json_create.cpp b/src/duckdb/extension/json/json_functions/json_create.cpp index fc9b4cc10..6117f0b89 100644 --- a/src/duckdb/extension/json/json_functions/json_create.cpp +++ b/src/duckdb/extension/json/json_functions/json_create.cpp @@ -31,12 +31,6 @@ struct StructNames { values[name] = make_uniq(Value(name), count_t(0ULL)); } - Vector &Get(const string &name, idx_t count) const { - auto &result = *values.at(name); - FlatVector::SetSize(result, count); - return result; - } - StructNames Copy() const { StructNames result; // Have to do this because we can't implicitly copy Vector @@ -86,9 +80,9 @@ enum class JSONGeometryFormat : uint8_t { WKT, GEOJSON }; struct JSONCopyFormatOptions { JSONGeometryFormat geometry_format = JSONGeometryFormat::WKT; - optional_ptr date_format; - optional_ptr timestamp_format; - optional_ptr context; + mutable optional_ptr date_format; + mutable optional_ptr timestamp_format; + mutable optional_ptr context; optional_ptr timestamptz_format_expression; optional_ptr timestamptz_ns_format_expression; }; @@ -305,8 +299,10 @@ static LogicalType GetJSONType(StructNames &const_struct_names, const LogicalTyp } } +//! Resolves the argument types and the struct names - this is called from both resolve_types and bind, which only +//! works because the JSON type of a JSON type is the same type static unique_ptr JSONCreateBindParams(BoundScalarFunction &bound_function, - vector> &arguments, bool object) { + const vector> &arguments, bool object) { StructNames const_struct_names; auto &bound_arguments = bound_function.GetArguments(); bound_arguments.clear(); @@ -329,7 +325,8 @@ static unique_ptr JSONCreateBindParams(BoundScalarFunction &bound_ return make_uniq(std::move(const_struct_names)); } -static unique_ptr JSONObjectBind(BindScalarFunctionInput &input) { +template +static unique_ptr JSONObjectBind(INPUT &input) { auto &bound_function = input.GetBoundFunction(); auto &arguments = input.GetArguments(); if (arguments.size() % 2 != 0) { @@ -338,13 +335,15 @@ static unique_ptr JSONObjectBind(BindScalarFunctionInput &input) { return JSONCreateBindParams(bound_function, arguments, true); } -static unique_ptr JSONArrayBind(BindScalarFunctionInput &input) { +template +static unique_ptr JSONArrayBind(INPUT &input) { auto &bound_function = input.GetBoundFunction(); auto &arguments = input.GetArguments(); return JSONCreateBindParams(bound_function, arguments, false); } -static unique_ptr ToJSONBind(BindScalarFunctionInput &input) { +template +static unique_ptr ToJSONBind(INPUT &input) { auto &bound_function = input.GetBoundFunction(); auto &arguments = input.GetArguments(); if (arguments.size() != 1) { @@ -353,7 +352,8 @@ static unique_ptr ToJSONBind(BindScalarFunctionInput &input) { return JSONCreateBindParams(bound_function, arguments, false); } -static unique_ptr ArrayToJSONBind(BindScalarFunctionInput &input) { +template +static unique_ptr ArrayToJSONBind(INPUT &input) { auto &bound_function = input.GetBoundFunction(); auto &arguments = input.GetArguments(); if (arguments.size() != 1) { @@ -369,7 +369,8 @@ static unique_ptr ArrayToJSONBind(BindScalarFunctionInput &input) return JSONCreateBindParams(bound_function, arguments, false); } -static unique_ptr RowToJSONBind(BindScalarFunctionInput &input) { +template +static unique_ptr RowToJSONBind(INPUT &input) { auto &bound_function = input.GetBoundFunction(); auto &arguments = input.GetArguments(); if (arguments.size() != 1) { @@ -385,6 +386,11 @@ static unique_ptr RowToJSONBind(BindScalarFunctionInput &input) { return JSONCreateBindParams(bound_function, arguments, false); } +template (*BIND)(ResolveScalarFunctionTypesInput &)> +static void JSONCreateResolveTypes(ResolveScalarFunctionTypesInput &input) { + BIND(input); +} + static bool BindJSONCopyFormat(ClientContext &context, Expression &argument, const string &name, string &format_string, StrfTimeFormat &format) { if (argument.HasParameter()) { @@ -422,13 +428,7 @@ static unique_ptr BindJSONCopyTimestampTZFormatter(ClientContext &co children.push_back(make_uniq(Value(format_string))); FunctionBinder function_binder(context); - ErrorData error; - auto result = - function_binder.BindScalarFunction(Identifier::DefaultSchema(), "strftime", std::move(children), error); - if (!result) { - error.Throw(); - } - return result; + return function_binder.BindScalarFunction(Identifier::DefaultSchema(), "strftime", std::move(children)); } static unique_ptr @@ -626,7 +626,8 @@ static void CreateValuesStruct(const StructNames &names, yyjson_mut_doc *doc, yy // Add the key/value pairs to the values auto &entries = StructVector::GetEntries(value_v); for (idx_t entry_i = 0; entry_i < entries.size(); entry_i++) { - auto &struct_key_v = names.Get(StructType::GetChildName(value_v.GetType(), entry_i).GetIdentifierName(), count); + Vector struct_key_v(Value(StructType::GetChildName(value_v.GetType(), entry_i).GetIdentifierName()), + count_t(count)); auto &struct_val_v = entries[entry_i]; CreateKeyValuePairs(names, doc, vals, nested_vals, struct_key_v, struct_val_v, count, options); } @@ -733,8 +734,8 @@ static void CreateValuesUnion(const StructNames &names, yyjson_mut_doc *doc, yyj // Add the key/value pairs to the values for (idx_t member_idx = 0; member_idx < UnionType::GetMemberCount(value_v.GetType()); member_idx++) { auto &member_val_v = UnionVector::GetMember(value_v, member_idx); - auto &member_key_v = - names.Get(UnionType::GetMemberName(value_v.GetType(), member_idx).GetIdentifierName(), count); + Vector member_key_v(Value(UnionType::GetMemberName(value_v.GetType(), member_idx).GetIdentifierName()), + count_t(count)); // This implementation is not optimal since we convert the entire member vector, // and then skip the rows not matching the tag afterwards. @@ -861,7 +862,7 @@ static void CreateValuesFormatted(yyjson_mut_doc *doc, yyjson_mut_val *vals[], V static void CreateValuesDate(yyjson_mut_doc *doc, yyjson_mut_val *vals[], Vector &value_v, idx_t count, const JSONCopyFormatOptions &options) { CreateValuesFormatted(doc, vals, value_v, count, bool(options.date_format), [&](Vector &string_vector) { - options.date_format.get_mutable()->ConvertDateVector(value_v, string_vector); + options.date_format->ConvertDateVector(value_v, string_vector); }); } @@ -874,14 +875,14 @@ static void CreateValuesTimestamp(yyjson_mut_doc *doc, yyjson_mut_val *vals[], V return; } CreateValuesFormatted(doc, vals, value_v, count, bool(options.timestamp_format), [&](Vector &string_vector) { - options.timestamp_format.get_mutable()->ConvertTimestampVector(value_v, string_vector); + options.timestamp_format->ConvertTimestampVector(value_v, string_vector); }); } static void CreateValuesTimestampNS(yyjson_mut_doc *doc, yyjson_mut_val *vals[], Vector &value_v, idx_t count, const JSONCopyFormatOptions &options) { CreateValuesFormatted(doc, vals, value_v, count, bool(options.timestamp_format), [&](Vector &string_vector) { - options.timestamp_format.get_mutable()->ConvertTimestampNSVector(value_v, string_vector); + options.timestamp_format->ConvertTimestampNSVector(value_v, string_vector); }); } @@ -891,7 +892,7 @@ static void CreateValuesTimestampTZ(yyjson_mut_doc *doc, yyjson_mut_val *vals[], // no COPY format was given - use the full cast set so that the session time zone is applied Vector string_vector(LogicalType::VARCHAR, count); if (options.context) { - VectorOperations::Cast(*options.context.get_mutable(), value_v, string_vector, count, true); + VectorOperations::Cast(*options.context, value_v, string_vector, count, true); } else { VectorOperations::DefaultCast(value_v, string_vector, count); } @@ -909,7 +910,7 @@ static void CreateValuesTimestampTZ(yyjson_mut_doc *doc, yyjson_mut_val *vals[], input.InitializeEmpty({value_v.GetType()}); input.data[0].Reference(value_v); input.CheckCardinality(count); - ExpressionExecutor executor(*options.context.get_mutable(), *format_expression); + ExpressionExecutor executor(*options.context, *format_expression); executor.ExecuteExpression(input, string_vector); }); } @@ -1277,7 +1278,7 @@ static void JSONCopyToGeoJSONFunction(DataChunk &args, ExpressionState &state, V } for (const auto property_index : info.property_indices) { - auto &key_v = names.Get(StructType::GetChildName(payload_type, property_index).GetIdentifierName(), count); + Vector key_v(Value(StructType::GetChildName(payload_type, property_index).GetIdentifierName()), count_t(count)); CreateKeyValuePairs(names, doc, properties, nested_vals, key_v, entries[property_index], count, options); } for (idx_t i = 0; i < count; i++) { @@ -1480,9 +1481,10 @@ unique_ptr JSONFunctions::CreateJSONCopyToJSONExpression(ClientConte } ScalarFunctionSet JSONFunctions::GetObjectFunction() { - ScalarFunction fun("json_object", {}, LogicalType::JSON(), ObjectFunction, JSONObjectBind, nullptr, - JSONFunctionLocalState::Init); - fun.SetVarArgs(LogicalType::ANY); + ScalarFunction fun("json_object", {}, LogicalType::JSON(), ObjectFunction, JSONObjectBind, + nullptr, JSONFunctionLocalState::Init); + fun.GetSignature().AddArgs("args", LogicalType::ANY); + fun.SetResolveTypesCallback(JSONCreateResolveTypes>); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); // throws if a key is NULL fun.SetFallible(); @@ -1490,17 +1492,19 @@ ScalarFunctionSet JSONFunctions::GetObjectFunction() { } ScalarFunctionSet JSONFunctions::GetArrayFunction() { - ScalarFunction fun("json_array", {}, LogicalType::JSON(), ArrayFunction, JSONArrayBind, nullptr, - JSONFunctionLocalState::Init); - fun.SetVarArgs(LogicalType::ANY); + ScalarFunction fun("json_array", {}, LogicalType::JSON(), ArrayFunction, JSONArrayBind, + nullptr, JSONFunctionLocalState::Init); + fun.GetSignature().AddArgs("args", LogicalType::ANY); + fun.SetResolveTypesCallback(JSONCreateResolveTypes>); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); return ScalarFunctionSet(fun); } ScalarFunctionSet JSONFunctions::GetToJSONFunction() { - ScalarFunction fun("to_json", {}, LogicalType::JSON(), ToJSONFunction, ToJSONBind, nullptr, + ScalarFunction fun("to_json", {}, LogicalType::JSON(), ToJSONFunction, ToJSONBind, nullptr, JSONFunctionLocalState::Init); - fun.SetVarArgs(LogicalType::ANY); + fun.GetSignature().AddArgs("args", LogicalType::ANY); + fun.SetResolveTypesCallback(JSONCreateResolveTypes>); return ScalarFunctionSet(fun); } @@ -1508,7 +1512,12 @@ ScalarFunction JSONFunctions::GetJSONCopyToJSONFunction() { ScalarFunction fun(JSON_COPY_TO_JSON_INTERNAL_NAME, {LogicalType::ANY, LogicalType::VARCHAR, LogicalType::VARCHAR}, LogicalType::JSON(), JSONCopyToJSONFunction, JSONCopyToJSONBind, nullptr, JSONFunctionLocalState::Init); + fun.GetSignature().GetParameter(0).SetName("value"); + fun.GetSignature().GetParameter(1).SetName("date_format"); + fun.GetSignature().GetParameter(2).SetName("timestamp_format"); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); + // formatting dates and timestamps can fail + fun.SetFallible(); fun.SetSerializeCallback(JSONCopyToJSONSerialize); fun.SetDeserializeCallback(JSONCopyToJSONDeserialize); return fun; @@ -1520,21 +1529,30 @@ ScalarFunction JSONFunctions::GetJSONCopyToGeoJSONFunction() { LogicalType::VARCHAR, LogicalType::BOOLEAN}, LogicalType::JSON(), JSONCopyToGeoJSONFunction, JSONCopyToGeoJSONBind, nullptr, JSONFunctionLocalState::Init); + fun.GetSignature().GetParameter(0).SetName("value"); + fun.GetSignature().GetParameter(1).SetName("date_format"); + fun.GetSignature().GetParameter(2).SetName("timestamp_format"); + fun.GetSignature().GetParameter(3).SetName("geometry_column"); + fun.GetSignature().GetParameter(4).SetName("id_column"); + fun.GetSignature().GetParameter(5).SetName("write_bbox"); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); + fun.SetFallible(); return fun; } ScalarFunctionSet JSONFunctions::GetArrayToJSONFunction() { - ScalarFunction fun("array_to_json", {}, LogicalType::JSON(), ToJSONFunction, ArrayToJSONBind, nullptr, - JSONFunctionLocalState::Init); - fun.SetVarArgs(LogicalType::ANY); + ScalarFunction fun("array_to_json", {}, LogicalType::JSON(), ToJSONFunction, + ArrayToJSONBind, nullptr, JSONFunctionLocalState::Init); + fun.GetSignature().AddArgs("args", LogicalType::ANY); + fun.SetResolveTypesCallback(JSONCreateResolveTypes>); return ScalarFunctionSet(fun); } ScalarFunctionSet JSONFunctions::GetRowToJSONFunction() { - ScalarFunction fun("row_to_json", {}, LogicalType::JSON(), ToJSONFunction, RowToJSONBind, nullptr, - JSONFunctionLocalState::Init); - fun.SetVarArgs(LogicalType::ANY); + ScalarFunction fun("row_to_json", {}, LogicalType::JSON(), ToJSONFunction, RowToJSONBind, + nullptr, JSONFunctionLocalState::Init); + fun.GetSignature().AddArgs("args", LogicalType::ANY); + fun.SetResolveTypesCallback(JSONCreateResolveTypes>); return ScalarFunctionSet(fun); } diff --git a/src/duckdb/extension/json/json_functions/json_deep_merge.cpp b/src/duckdb/extension/json/json_functions/json_deep_merge.cpp index a500109b7..6afdd555c 100644 --- a/src/duckdb/extension/json/json_functions/json_deep_merge.cpp +++ b/src/duckdb/extension/json/json_functions/json_deep_merge.cpp @@ -1,16 +1,17 @@ #include "json_common.hpp" #include "json_functions.hpp" +#include "json_stack.hpp" namespace duckdb { //! Coalescing deep merge: null in patch means "absent/unknown", keeps the original value. -//! Non-null patch values overwrite. Nested objects are merged recursively. +//! Non-null patch values overwrite. static yyjson_mut_val *DeepMerge(yyjson_mut_doc *doc, yyjson_mut_val *orig_root, yyjson_mut_val *patch_root) { if (!yyjson_mut_is_obj(orig_root) || !yyjson_mut_is_obj(patch_root)) { if (unsafe_yyjson_is_null(patch_root)) { - return yyjson_mut_val_mut_copy(doc, orig_root); + return JSONCommon::MutValMutCopy(doc, orig_root); } - return yyjson_mut_val_mut_copy(doc, patch_root); + return JSONCommon::MutValMutCopy(doc, patch_root); } auto root_builder = yyjson_mut_obj(doc); @@ -22,13 +23,12 @@ static yyjson_mut_val *DeepMerge(yyjson_mut_doc *doc, yyjson_mut_val *orig_root, yyjson_mut_val *patch_node; yyjson_mut_val *builder; }; - auto stack = std::vector(); - stack.emplace_back(stack_item {nullptr, orig_root, patch_root, root_builder}); + Stack stack; + stack.Push(stack_item {nullptr, orig_root, patch_root, root_builder}); // loop over each level of nesting - while (!stack.empty()) { - auto nodes = stack.back(); - stack.pop_back(); + while (!stack.Empty()) { + auto nodes = stack.Pop(); auto builder = nodes.builder; @@ -36,12 +36,13 @@ static yyjson_mut_val *DeepMerge(yyjson_mut_doc *doc, yyjson_mut_val *orig_root, { idx_t idx, max; yyjson_mut_val *key, *orig_val; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast): yyjson iteration macro yyjson_mut_obj_foreach(nodes.orig_node, idx, max, key, orig_val) { auto patch_val = yyjson_mut_obj_getn(nodes.patch_node, unsafe_yyjson_get_str(key), unsafe_yyjson_get_len(key)); if (!patch_val || unsafe_yyjson_is_null(patch_val)) { - auto mut_key = yyjson_mut_val_mut_copy(doc, key); - auto mut_val = yyjson_mut_val_mut_copy(doc, orig_val); + auto mut_key = JSONCommon::MutValMutCopy(doc, key); + auto mut_val = JSONCommon::MutValMutCopy(doc, orig_val); yyjson_mut_obj_add(builder, mut_key, mut_val); } } @@ -51,6 +52,7 @@ static yyjson_mut_val *DeepMerge(yyjson_mut_doc *doc, yyjson_mut_val *orig_root, { idx_t idx, max; yyjson_mut_val *key, *patch_val; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast): yyjson iteration macro yyjson_mut_obj_foreach(nodes.patch_node, idx, max, key, patch_val) { if (unsafe_yyjson_is_null(patch_val)) { continue; // null entries handled in the first pass @@ -58,7 +60,7 @@ static yyjson_mut_val *DeepMerge(yyjson_mut_doc *doc, yyjson_mut_val *orig_root, auto orig_val = yyjson_mut_obj_getn(nodes.orig_node, unsafe_yyjson_get_str(key), unsafe_yyjson_get_len(key)); - auto mut_key = yyjson_mut_val_mut_copy(doc, key); + auto mut_key = JSONCommon::MutValMutCopy(doc, key); // at least one of (patch_val, orig_val) is not an object, then we copy the patch if it's not null, and // the original otherwise @@ -69,15 +71,15 @@ static yyjson_mut_val *DeepMerge(yyjson_mut_doc *doc, yyjson_mut_val *orig_root, yyjson_mut_val *mut_val; if (unsafe_yyjson_is_null(patch_val) && orig_val) { - mut_val = yyjson_mut_val_mut_copy(doc, orig_val); + mut_val = JSONCommon::MutValMutCopy(doc, orig_val); } else { - mut_val = yyjson_mut_val_mut_copy(doc, patch_val); + mut_val = JSONCommon::MutValMutCopy(doc, patch_val); } yyjson_mut_obj_add(builder, mut_key, mut_val); } else { auto child_builder = yyjson_mut_obj(doc); // now we know that both are objects and we need to check them, so we add them to the stack - stack.emplace_back(stack_item {mut_key, orig_val, patch_val, child_builder}); + stack.Push(stack_item {mut_key, orig_val, patch_val, child_builder}); yyjson_mut_obj_add(builder, mut_key, child_builder); } } @@ -144,7 +146,7 @@ ScalarFunctionSet JSONFunctions::GetDeepMergeFunction() { ScalarFunction fun("json_deep_merge", {}, LogicalType::JSON(), DeepMergeFunction, nullptr, nullptr, JSONFunctionLocalState::Init); fun.GetSignature().AddParameter("json1", LogicalType::JSON()).AddParameter("json2", LogicalType::JSON()); - fun.SetVarArgs(LogicalType::JSON()); + fun.GetSignature().AddArgs("args", LogicalType::JSON()); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); fun.SetFallible(); diff --git a/src/duckdb/extension/json/json_functions/json_exists.cpp b/src/duckdb/extension/json/json_functions/json_exists.cpp index 754b1c642..a1d8624c5 100644 --- a/src/duckdb/extension/json/json_functions/json_exists.cpp +++ b/src/duckdb/extension/json/json_functions/json_exists.cpp @@ -18,6 +18,7 @@ static void GetExistsFunctionsInternal(ScalarFunctionSet &set, const LogicalType ScalarFunction single_fun({}, LogicalType::BOOLEAN, BinaryExistsFunction, JSONReadFunctionData::Bind, nullptr, JSONFunctionLocalState::Init); single_fun.GetSignature().AddParameter("json", input_type).AddParameter("path", LogicalType::VARCHAR); + single_fun.SetResolveTypesCallback(JSONReadFunctionData::ResolveTypes); set.AddFunction(single_fun); ScalarFunction many_fun({}, LogicalType::LIST(LogicalType::BOOLEAN), ManyExistsFunction, JSONReadManyFunctionData::Bind, nullptr, JSONFunctionLocalState::Init); diff --git a/src/duckdb/extension/json/json_functions/json_extract.cpp b/src/duckdb/extension/json/json_functions/json_extract.cpp index 226382a77..2e2a1ec38 100644 --- a/src/duckdb/extension/json/json_functions/json_extract.cpp +++ b/src/duckdb/extension/json/json_functions/json_extract.cpp @@ -38,10 +38,14 @@ static void GetExtractFunctionsInternal(ScalarFunctionSet &set, const LogicalTyp ScalarFunction index_fun({}, LogicalType::JSON(), ExtractFunction, JSONReadFunctionData::Bind, nullptr, JSONFunctionLocalState::Init); index_fun.GetSignature().AddParameter("json", input_type).AddParameter("index", LogicalType::BIGINT); + index_fun.SetResolveTypesCallback(JSONReadFunctionData::ResolveTypes); + index_fun.SetResolveTypesCallback(JSONReadFunctionData::ResolveTypes); set.AddFunction(index_fun); ScalarFunction path_fun({}, LogicalType::JSON(), ExtractFunction, JSONReadFunctionData::Bind, nullptr, JSONFunctionLocalState::Init); path_fun.GetSignature().AddParameter("json", input_type).AddParameter("path", LogicalType::VARCHAR); + path_fun.SetResolveTypesCallback(JSONReadFunctionData::ResolveTypes); + path_fun.SetResolveTypesCallback(JSONReadFunctionData::ResolveTypes); set.AddFunction(path_fun); ScalarFunction many_fun({}, LogicalType::LIST(LogicalType::JSON()), ExtractManyFunction, JSONReadManyFunctionData::Bind, nullptr, JSONFunctionLocalState::Init); diff --git a/src/duckdb/extension/json/json_functions/json_keys.cpp b/src/duckdb/extension/json/json_functions/json_keys.cpp index 33da7eafd..f84d9ef36 100644 --- a/src/duckdb/extension/json/json_functions/json_keys.cpp +++ b/src/duckdb/extension/json/json_functions/json_keys.cpp @@ -46,6 +46,7 @@ static void GetJSONKeysFunctionsInternal(ScalarFunctionSet &set, const LogicalTy ScalarFunction path_fun({}, LogicalType::LIST(LogicalType::VARCHAR), BinaryJSONKeysFunction, JSONReadFunctionData::Bind, nullptr, JSONFunctionLocalState::Init); path_fun.GetSignature().AddParameter("json", input_type).AddParameter("path", LogicalType::VARCHAR); + path_fun.SetResolveTypesCallback(JSONReadFunctionData::ResolveTypes); set.AddFunction(path_fun); ScalarFunction many_fun({}, LogicalType::LIST(LogicalType::LIST(LogicalType::VARCHAR)), ManyJSONKeysFunction, JSONReadManyFunctionData::Bind, nullptr, JSONFunctionLocalState::Init); diff --git a/src/duckdb/extension/json/json_functions/json_merge_patch.cpp b/src/duckdb/extension/json/json_functions/json_merge_patch.cpp index f58791112..e6f925fbf 100644 --- a/src/duckdb/extension/json/json_functions/json_merge_patch.cpp +++ b/src/duckdb/extension/json/json_functions/json_merge_patch.cpp @@ -1,8 +1,10 @@ #include "json_common.hpp" #include "json_functions.hpp" +#include "json_stack.hpp" namespace duckdb { +//! RFC 7396 merge patch; a missing or non-object orig is treated as an empty object static inline yyjson_mut_val *MergePatch(yyjson_mut_doc *doc, yyjson_mut_val *orig, yyjson_mut_val *patch) { if ((yyjson_mut_get_tag(orig) != (YYJSON_TYPE_OBJ | YYJSON_SUBTYPE_NONE)) || (yyjson_mut_get_tag(patch) != (YYJSON_TYPE_OBJ | YYJSON_SUBTYPE_NONE))) { @@ -11,7 +13,61 @@ static inline yyjson_mut_val *MergePatch(yyjson_mut_doc *doc, yyjson_mut_val *or } // Both are object, do the merge - return yyjson_mut_merge_patch(doc, orig, patch); + auto root_builder = yyjson_mut_obj(doc); + + struct stack_item { + yyjson_mut_val *key; + yyjson_mut_val *orig; + yyjson_mut_val *patch; + yyjson_mut_val *builder; + }; + Stack stack; + stack.Push(stack_item {nullptr, orig, patch, root_builder}); + + while (!stack.Empty()) { + auto nodes = stack.Pop(); + + if (!yyjson_mut_is_obj(nodes.orig)) { + // yyjson_mut_obj_getn on a non-object returns nullptr, so lookups below need no special case + nodes.orig = nullptr; + } + + // Copy orig keys that the patch does not touch + if (nodes.orig) { + idx_t idx, max; + yyjson_mut_val *key, *orig_val; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast): yyjson iteration macro + yyjson_mut_obj_foreach(nodes.orig, idx, max, key, orig_val) { + auto patch_val = + yyjson_mut_obj_getn(nodes.patch, unsafe_yyjson_get_str(key), unsafe_yyjson_get_len(key)); + if (!patch_val) { + yyjson_mut_obj_add(nodes.builder, JSONCommon::MutValMutCopy(doc, key), + JSONCommon::MutValMutCopy(doc, orig_val)); + } + } + } + + // Merge patch keys; null removes the key + idx_t idx, max; + yyjson_mut_val *key, *patch_val; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast): yyjson iteration macro + yyjson_mut_obj_foreach(nodes.patch, idx, max, key, patch_val) { + if (unsafe_yyjson_is_null(patch_val)) { + continue; + } + auto mut_key = JSONCommon::MutValMutCopy(doc, key); + if (!yyjson_mut_is_obj(patch_val)) { + yyjson_mut_obj_add(nodes.builder, mut_key, JSONCommon::MutValMutCopy(doc, patch_val)); + continue; + } + auto orig_val = yyjson_mut_obj_getn(nodes.orig, unsafe_yyjson_get_str(key), unsafe_yyjson_get_len(key)); + auto child_builder = yyjson_mut_obj(doc); + yyjson_mut_obj_add(nodes.builder, mut_key, child_builder); + stack.Push(stack_item {mut_key, orig_val, patch_val, child_builder}); + } + } + + return root_builder; } static inline void ReadObjects(yyjson_mut_doc *doc, const Vector &input, yyjson_mut_val *objs[]) { @@ -75,7 +131,7 @@ ScalarFunctionSet JSONFunctions::GetMergePatchFunction() { ScalarFunction fun("json_merge_patch", {}, LogicalType::JSON(), MergePatchFunction, nullptr, nullptr, JSONFunctionLocalState::Init); fun.GetSignature().AddParameter("json1", LogicalType::JSON()).AddParameter("json2", LogicalType::JSON()); - fun.SetVarArgs(LogicalType::JSON()); + fun.GetSignature().AddArgs("args", LogicalType::JSON()); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); return ScalarFunctionSet(fun); diff --git a/src/duckdb/extension/json/json_functions/json_merge_patch_diff.cpp b/src/duckdb/extension/json/json_functions/json_merge_patch_diff.cpp index 86dc51c95..42dd83349 100644 --- a/src/duckdb/extension/json/json_functions/json_merge_patch_diff.cpp +++ b/src/duckdb/extension/json/json_functions/json_merge_patch_diff.cpp @@ -1,5 +1,6 @@ #include "json_common.hpp" #include "json_functions.hpp" +#include "json_stack.hpp" namespace duckdb { @@ -17,12 +18,11 @@ static yyjson_mut_val *ComputeDiff(yyjson_mut_doc *doc, yyjson_val *old_val, yyj }; yyjson_mut_val *result = nullptr; - auto stack = std::vector(); - stack.push_back(stack_item {nullptr, old_val, new_val, nullptr, nullptr, false}); + Stack stack; + stack.Push(stack_item {nullptr, old_val, new_val, nullptr, nullptr, false}); - while (!stack.empty()) { - auto item = stack.back(); - stack.pop_back(); + while (!stack.Empty()) { + auto item = stack.Pop(); // finalize phase: update result if (item.finalize) { @@ -37,7 +37,7 @@ static yyjson_mut_val *ComputeDiff(yyjson_mut_doc *doc, yyjson_val *old_val, yyj } // Both objects: compute structural diff - if (item.old_node && item.new_node && yyjson_is_obj(item.old_node) && yyjson_is_obj(item.new_node)) { + if (yyjson_is_obj(item.old_node) && yyjson_is_obj(item.new_node)) { auto builder = yyjson_mut_obj(doc); // Keys in old but not in new: removed (emit null) @@ -51,13 +51,13 @@ static yyjson_mut_val *ComputeDiff(yyjson_mut_doc *doc, yyjson_val *old_val, yyj } } - stack.push_back({item.key, nullptr, nullptr, item.parent_builder, builder, true}); + stack.Push({item.key, nullptr, nullptr, item.parent_builder, builder, true}); // Keys in new: collect in order { idx_t idx, max; yyjson_val *key, *new_child; - std::vector children; + vector children; yyjson_obj_foreach(item.new_node, idx, max, key, new_child) { auto old_child = yyjson_obj_getn(item.old_node, unsafe_yyjson_get_str(key), unsafe_yyjson_get_len(key)); @@ -67,7 +67,7 @@ static yyjson_mut_val *ComputeDiff(yyjson_mut_doc *doc, yyjson_val *old_val, yyj } // push to stack in reverse to preserve order in output for (auto it = children.rbegin(); it != children.rend(); ++it) { - stack.push_back(*it); + stack.Push(*it); } } } else if (!item.old_node || !yyjson_equals(item.old_node, item.new_node)) { diff --git a/src/duckdb/extension/json/json_functions/json_normalize.cpp b/src/duckdb/extension/json/json_functions/json_normalize.cpp index 263a89b47..3fb1e28b7 100644 --- a/src/duckdb/extension/json/json_functions/json_normalize.cpp +++ b/src/duckdb/extension/json/json_functions/json_normalize.cpp @@ -1,5 +1,6 @@ #include "json_common.hpp" #include "json_functions.hpp" +#include "json_stack.hpp" namespace duckdb { @@ -9,12 +10,11 @@ static void SortKeys(yyjson_mut_val *v) { yyjson_mut_val *val; }; - auto stack = std::vector(); - stack.push_back(stack_item {v}); + Stack stack; + stack.Push(stack_item {v}); - while (!stack.empty()) { - auto item = stack.back(); - stack.pop_back(); + while (!stack.Empty()) { + auto item = stack.Pop(); // if null, do nothing if (!item.val) { @@ -29,7 +29,7 @@ static void SortKeys(yyjson_mut_val *v) { yyjson_mut_obj_iter iter; yyjson_mut_obj_iter_init(item.val, &iter); yyjson_mut_val *key = yyjson_mut_obj_iter_next(&iter); - stack.push_back(stack_item {yyjson_mut_obj_iter_get_val(key)}); + stack.Push(stack_item {yyjson_mut_obj_iter_get_val(key)}); } continue; } @@ -39,6 +39,7 @@ static void SortKeys(yyjson_mut_val *v) { pairs.reserve(size); idx_t idx, max; yyjson_mut_val *key, *child_val; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast): yyjson iteration macro yyjson_mut_obj_foreach(item.val, idx, max, key, child_val) { pairs.emplace_back(key, child_val); } @@ -54,7 +55,7 @@ static void SortKeys(yyjson_mut_val *v) { // Recursively sort nested values for (auto &pair : pairs) { - stack.push_back(stack_item {pair.second}); + stack.Push(stack_item {pair.second}); } // Clear and rebuild in sorted order @@ -66,7 +67,7 @@ static void SortKeys(yyjson_mut_val *v) { idx_t idx, max; yyjson_mut_val *elem; yyjson_mut_arr_foreach(item.val, idx, max, elem) { - stack.push_back(stack_item {elem}); + stack.Push(stack_item {elem}); } } } diff --git a/src/duckdb/extension/json/json_functions/json_serialize_plan.cpp b/src/duckdb/extension/json/json_functions/json_serialize_plan.cpp index 1fad6841a..48766e751 100644 --- a/src/duckdb/extension/json/json_functions/json_serialize_plan.cpp +++ b/src/duckdb/extension/json/json_functions/json_serialize_plan.cpp @@ -123,7 +123,7 @@ static void JsonSerializePlanFunction(DataChunk &args, ExpressionState &state, V yyjson_mut_doc_set_root(doc, result_obj); try { - Parser parser; + Parser parser(state.GetContext()); parser.ParseQuery(input.GetString()); auto plans_arr = yyjson_mut_arr(doc); diff --git a/src/duckdb/extension/json/json_functions/json_serialize_sql.cpp b/src/duckdb/extension/json/json_functions/json_serialize_sql.cpp index 59790fbff..6eb717337 100644 --- a/src/duckdb/extension/json/json_functions/json_serialize_sql.cpp +++ b/src/duckdb/extension/json/json_functions/json_serialize_sql.cpp @@ -92,7 +92,7 @@ static void JsonSerializeFunction(DataChunk &args, ExpressionState &state, Vecto yyjson_mut_doc_set_root(doc, result_obj); try { - auto parser = Parser(); + Parser parser(state.GetContext()); parser.ParseQuery(input.GetString()); auto statements_arr = yyjson_mut_arr(doc); @@ -269,8 +269,9 @@ static string ExecuteJsonSerializedSqlPragmaFunction(ClientContext &context, con } PragmaFunctionSet JSONFunctions::GetExecuteJsonSerializedSqlPragmaFunction() { - return PragmaFunctionSet(PragmaFunction::PragmaCall( - "json_execute_serialized_sql", ExecuteJsonSerializedSqlPragmaFunction, {LogicalType::VARCHAR})); + return PragmaFunctionSet( + PragmaFunction::PragmaCall("json_execute_serialized_sql", ExecuteJsonSerializedSqlPragmaFunction, + FunctionSignature().AddPositionalOnly("serialized_sql", LogicalType::VARCHAR))); } //---------------------------------------------------------------------- @@ -309,7 +310,7 @@ struct ExecuteSqlTableFunction { } static void Function(ClientContext &context, TableFunctionInput &data_p, DataChunk &output) { - auto &data = (BindData &)*data_p.bind_data; + auto &data = data_p.bind_data->CastNoConst(); if (!data.result) { data.result = data.plan->Execute(); } @@ -322,8 +323,9 @@ struct ExecuteSqlTableFunction { }; TableFunctionSet JSONFunctions::GetExecuteJsonSerializedSqlFunction() { - TableFunction func("json_execute_serialized_sql", {LogicalType::VARCHAR}, ExecuteSqlTableFunction::Function, - ExecuteSqlTableFunction::Bind); + TableFunction func("json_execute_serialized_sql", + FunctionSignature().AddPositionalOnly("serialized_sql", LogicalType::VARCHAR), + ExecuteSqlTableFunction::Function, ExecuteSqlTableFunction::Bind); return TableFunctionSet(func); } diff --git a/src/duckdb/extension/json/json_functions/json_strip_nulls.cpp b/src/duckdb/extension/json/json_functions/json_strip_nulls.cpp index 449427d8f..07bfa2344 100644 --- a/src/duckdb/extension/json/json_functions/json_strip_nulls.cpp +++ b/src/duckdb/extension/json/json_functions/json_strip_nulls.cpp @@ -1,30 +1,44 @@ #include "json_common.hpp" #include "json_functions.hpp" +#include "json_stack.hpp" namespace duckdb { -//! Recursively remove all object keys with null values +//! Remove all object keys with null values static void StripNulls(yyjson_mut_val *val) { - if (!val) { - return; - } - if (yyjson_mut_is_obj(val)) { - yyjson_mut_obj_iter iter; - yyjson_mut_obj_iter_init(val, &iter); - yyjson_mut_val *key; - while ((key = yyjson_mut_obj_iter_next(&iter)) != nullptr) { - auto child = yyjson_mut_obj_iter_get_val(key); - if (unsafe_yyjson_is_null(child)) { - yyjson_mut_obj_iter_remove(&iter); - } else { - StripNulls(child); - } + struct stack_item { + yyjson_mut_val *val; + }; + + Stack stack; + stack.Push(stack_item {val}); + + while (!stack.Empty()) { + auto curr_val = stack.Pop().val; + + if (!curr_val) { // TODO: at any point would we push something onto the stack that is a nullptr val? + return; // TODO: maybe put this as continue } - } else if (yyjson_mut_is_arr(val)) { - idx_t idx, max; - yyjson_mut_val *elem; - yyjson_mut_arr_foreach(val, idx, max, elem) { - StripNulls(elem); + if (yyjson_mut_is_obj(curr_val)) { + yyjson_mut_obj_iter iter; + yyjson_mut_obj_iter_init(curr_val, &iter); + yyjson_mut_val *key; + while ((key = yyjson_mut_obj_iter_next(&iter)) != nullptr) { + auto child = yyjson_mut_obj_iter_get_val(key); + if (unsafe_yyjson_is_null(child)) { + yyjson_mut_obj_iter_remove(&iter); + } else { + stack.Push(stack_item {child}); + } + } + } else if (yyjson_mut_is_arr(curr_val)) { // TODO: this is exceptionally stupid bc you never check if array + // items are null ????????? + // TODO: plus this logic could probably be combined with obj logic, its just the iterator ... + idx_t idx, max; + yyjson_mut_val *elem; + yyjson_mut_arr_foreach(curr_val, idx, max, elem) { + stack.Push(stack_item {elem}); + } } } } diff --git a/src/duckdb/extension/json/json_functions/json_structure.cpp b/src/duckdb/extension/json/json_functions/json_structure.cpp index 90fa15eff..83e2f09c1 100644 --- a/src/duckdb/extension/json/json_functions/json_structure.cpp +++ b/src/duckdb/extension/json/json_functions/json_structure.cpp @@ -1,7 +1,7 @@ #include "json_structure.hpp" #include "duckdb/common/enum_util.hpp" -#include "duckdb/common/extra_type_info.hpp" +#include "duckdb/common/logical_type_info.hpp" #include "json_executors.hpp" #include "json_geojson.hpp" #include "json_scan.hpp" @@ -519,15 +519,16 @@ JSONStructureNode &JSONStructureDescription::GetOrCreateChild(const char *key_pt } JSONStructureNode &JSONStructureDescription::GetOrCreateChild(yyjson_val *key, yyjson_val *val, - const bool ignore_errors, const bool detect_geojson) { + const bool ignore_errors, const bool detect_geojson, + const idx_t depth) { D_ASSERT(yyjson_is_str(key)); auto &child = GetOrCreateChild(unsafe_yyjson_get_str(key), unsafe_yyjson_get_len(key)); - JSONStructure::ExtractStructure(val, child, ignore_errors, detect_geojson); + JSONStructure::ExtractStructure(val, child, ignore_errors, detect_geojson, depth); return child; } static void ExtractStructureArray(yyjson_val *arr, JSONStructureNode &node, const bool ignore_errors, - const bool detect_geojson) { + const bool detect_geojson, const idx_t depth) { D_ASSERT(yyjson_is_arr(arr)); auto &description = node.GetOrCreateDescription(LogicalTypeId::LIST); auto &child = description.GetOrCreateChild(); @@ -535,12 +536,12 @@ static void ExtractStructureArray(yyjson_val *arr, JSONStructureNode &node, cons size_t idx, max; yyjson_val *val; yyjson_arr_foreach(arr, idx, max, val) { - JSONStructure::ExtractStructure(val, child, ignore_errors, detect_geojson); + JSONStructure::ExtractStructure(val, child, ignore_errors, detect_geojson, depth + 1); } } static void ExtractStructureObject(yyjson_val *obj, JSONStructureNode &node, const bool ignore_errors, - const bool detect_geojson) { + const bool detect_geojson, const idx_t depth) { D_ASSERT(yyjson_is_obj(obj)); auto &description = node.GetOrCreateDescription(LogicalTypeId::STRUCT); @@ -562,7 +563,7 @@ static void ExtractStructureObject(yyjson_val *obj, JSONStructureNode &node, con *insert_result.first + "\" in object %s", obj); } - description.GetOrCreateChild(key, val, ignore_errors, detect_geojson); + description.GetOrCreateChild(key, val, ignore_errors, detect_geojson, depth + 1); } } @@ -577,7 +578,10 @@ static void ExtractStructureVal(yyjson_val *val, JSONStructureNode &node) { } void JSONStructure::ExtractStructure(yyjson_val *val, JSONStructureNode &node, const bool ignore_errors, - const bool detect_geojson) { + const bool detect_geojson, const idx_t depth) { + if (depth >= JSONCommon::MAX_RECURSION_DEPTH) { + throw InvalidInputException("JSON exceeds maximum recursion depth of %d", JSONCommon::MAX_RECURSION_DEPTH); + } node.count++; const auto tag = yyjson_get_tag(val); if (tag == (YYJSON_TYPE_NULL | YYJSON_SUBTYPE_NONE)) { @@ -586,7 +590,7 @@ void JSONStructure::ExtractStructure(yyjson_val *val, JSONStructureNode &node, c switch (tag) { case YYJSON_TYPE_ARR | YYJSON_SUBTYPE_NONE: - return ExtractStructureArray(val, node, ignore_errors, detect_geojson); + return ExtractStructureArray(val, node, ignore_errors, detect_geojson, depth); case YYJSON_TYPE_OBJ | YYJSON_SUBTYPE_NONE: // A GeoJSON geometry is a leaf: it becomes GEOMETRY rather than a struct of type/coordinates. Detecting it // here (rather than on the finished tree) means geometries of different kinds still merge into one type. @@ -594,7 +598,7 @@ void JSONStructure::ExtractStructure(yyjson_val *val, JSONStructureNode &node, c node.GetOrCreateDescription(LogicalTypeId::GEOMETRY); return; } - return ExtractStructureObject(val, node, ignore_errors, detect_geojson); + return ExtractStructureObject(val, node, ignore_errors, detect_geojson, depth); default: return ExtractStructureVal(val, node); } @@ -890,9 +894,19 @@ static LogicalType StructureToTypeObject(ClientContext &context, const JSONStruc } } + // An empty key can't be a STRUCT member name, so such objects can only be a MAP or JSON + bool has_empty_key = false; + for (auto &child : desc.children) { + D_ASSERT(child.key); + has_empty_key = has_empty_key || child.key->empty(); + } + if (has_empty_key && map_inference_threshold == DConstants::INVALID_INDEX) { + return LogicalType::JSON(); + } + // If it's an inconsistent object we also just do MAP with the best-possible, recursively-merged value type - if (map_inference_threshold != DConstants::INVALID_INDEX && - IsStructureInconsistent(desc, node.count, node.null_count, field_appearance_threshold)) { + if (has_empty_key || (map_inference_threshold != DConstants::INVALID_INDEX && + IsStructureInconsistent(desc, node.count, node.null_count, field_appearance_threshold))) { return LogicalType::MAP(LogicalType::VARCHAR, GetMergedType(context, node, max_depth, field_appearance_threshold, map_inference_threshold, depth + 1, null_type)); diff --git a/src/duckdb/extension/json/json_functions/json_table_in_out.cpp b/src/duckdb/extension/json/json_functions/json_table_in_out.cpp index f67c50e0e..65232dea4 100644 --- a/src/duckdb/extension/json/json_functions/json_table_in_out.cpp +++ b/src/duckdb/extension/json/json_functions/json_table_in_out.cpp @@ -1,5 +1,6 @@ #include "json_common.hpp" #include "json_functions.hpp" +#include "duckdb/common/atomic.hpp" #include "duckdb/common/string_util.hpp" #include "duckdb/function/table_function.hpp" @@ -48,6 +49,11 @@ struct JSONTableInOutGlobalState : GlobalTableFunctionState { static constexpr idx_t JSON_COLUMN_OFFSET = 0; static constexpr idx_t ROOT_COLUMN_OFFSET = 1; + + //! Progress when used as a source (i.e. with a single input row) - the rows that will be produced, and the rows + //! that were produced + atomic total_rows {0}; + atomic produced_rows {0}; }; static unique_ptr JSONTableInOutInitGlobal(ClientContext &, TableFunctionInitInput &input) { @@ -146,9 +152,9 @@ struct JSONTableInOutRecursionNode { }; struct JSONTableInOutLocalState : LocalTableFunctionState { - explicit JSONTableInOutLocalState(ClientContext &context) - : json_allocator(BufferAllocator::Get(context)), alc(json_allocator.GetYYAlc()), len(DConstants::INVALID_INDEX), - doc(nullptr), initialized(false), total_count(0) { + JSONTableInOutLocalState(ClientContext &context, JSONTableInOutGlobalState &gstate) + : gstate(gstate), json_allocator(BufferAllocator::Get(context)), alc(json_allocator.GetYYAlc()), + len(DConstants::INVALID_INDEX), doc(nullptr), initialized(false), total_count(0) { } string GetPath() const { @@ -169,8 +175,12 @@ struct JSONTableInOutLocalState : LocalTableFunctionState { recursion_nodes.emplace_back(str, val); } + JSONTableInOutGlobalState &gstate; JSONAllocator json_allocator; yyjson_alc *alc; + //! The rows the current input row is estimated to produce, and the rows it produced so far + idx_t estimated_rows = 0; + idx_t produced_rows = 0; string path; idx_t len; @@ -182,8 +192,19 @@ struct JSONTableInOutLocalState : LocalTableFunctionState { }; static unique_ptr JSONTableInOutInitLocal(ExecutionContext &context, TableFunctionInitInput &, - GlobalTableFunctionState *) { - return make_uniq(context.client); + GlobalTableFunctionState *global_state) { + return make_uniq(context.client, global_state->Cast()); +} + +static double JSONTableInOutProgress(ClientContext &, const FunctionData *, + const GlobalTableFunctionState *global_state) { + auto &gstate = global_state->Cast(); + auto total_rows = gstate.total_rows.load(std::memory_order_relaxed); + if (total_rows == 0) { + return 0; + } + auto produced_rows = MinValue(gstate.produced_rows.load(std::memory_order_relaxed), total_rows); + return 100.0 * static_cast(produced_rows) / static_cast(total_rows); } template @@ -315,6 +336,14 @@ static void InitializeLocalState(JSONTableInOutLocalState &lstate, DataChunk &in } const auto is_container = unsafe_yyjson_is_arr(root) || unsafe_yyjson_is_obj(root); + // json_each produces a row per element of the root, json_tree a row per value (the value count is an upper bound) + if (TYPE == JSONTableInOutType::TREE) { + lstate.estimated_rows = yyjson_doc_get_val_count(lstate.doc); + } else { + lstate.estimated_rows = is_container ? unsafe_yyjson_get_len(root) : 1; + } + lstate.produced_rows = 0; + lstate.gstate.total_rows.fetch_add(lstate.estimated_rows, std::memory_order_relaxed); if (!is_container || TYPE == JSONTableInOutType::TREE) { result.AddRow(lstate, nullptr, root); } @@ -384,6 +413,8 @@ static OperatorResultType JSONTableInOutFunction(ExecutionContext &, TableFuncti } } output.SetChildCardinality(result.count); + lstate.produced_rows += result.count; + gstate.produced_rows.fetch_add(result.count, std::memory_order_relaxed); // Set constant virtual columns ("json", "root", and "empty") if (gstate.json_column_index.IsValid()) { @@ -403,6 +434,14 @@ static OperatorResultType JSONTableInOutFunction(ExecutionContext &, TableFuncti if (output.size() == 0) { D_ASSERT(recursion_nodes.empty()); + // the input row is done - correct the estimate to the rows that were actually produced + if (lstate.estimated_rows > lstate.produced_rows) { + gstate.total_rows.fetch_sub(lstate.estimated_rows - lstate.produced_rows, std::memory_order_relaxed); + } else { + gstate.total_rows.fetch_add(lstate.produced_rows - lstate.estimated_rows, std::memory_order_relaxed); + } + lstate.estimated_rows = 0; + lstate.produced_rows = 0; lstate.json_allocator.Reset(); lstate.initialized = false; return OperatorResultType::NEED_MORE_INPUT; @@ -423,12 +462,15 @@ virtual_column_map_t GetJSONTableInOutVirtualColumns(ClientContext &, optional_p template TableFunction GetJSONTableInOutFunction(const LogicalType &input_type, const bool &has_path_param) { - vector arguments = {input_type}; + FunctionSignature signature; + signature.AddPositionalOnly("json", input_type); if (has_path_param) { - arguments.push_back(LogicalType::VARCHAR); + signature.AddPositionalOnly("path", LogicalType::VARCHAR); } - TableFunction function(arguments, nullptr, JSONTableInOutBind, JSONTableInOutInitGlobal, JSONTableInOutInitLocal); + TableFunction function(std::move(signature), nullptr, JSONTableInOutBind, JSONTableInOutInitGlobal, + JSONTableInOutInitLocal); function.in_out_function = JSONTableInOutFunction; + function.table_scan_progress = JSONTableInOutProgress; function.get_virtual_columns = GetJSONTableInOutVirtualColumns; function.projection_pushdown = true; return function; diff --git a/src/duckdb/extension/json/json_functions/json_transform.cpp b/src/duckdb/extension/json/json_functions/json_transform.cpp index 757ccbac5..ffa43977e 100644 --- a/src/duckdb/extension/json/json_functions/json_transform.cpp +++ b/src/duckdb/extension/json/json_functions/json_transform.cpp @@ -11,6 +11,7 @@ #include "duckdb/common/error_data.hpp" #include "duckdb/common/serializer/deserializer.hpp" #include "duckdb/common/types/geometry.hpp" +#include "duckdb/common/types/value_map.hpp" #include "json_geojson.hpp" #include "duckdb/common/serializer/serializer.hpp" #include "duckdb/common/types.hpp" @@ -44,17 +45,20 @@ static LogicalType StructureStringToTypeArray(yyjson_val *arr, ClientContext &co } static LogicalType StructureToTypeObject(yyjson_val *obj, ClientContext &context) { - unordered_set names; + // STRUCT field names are case-insensitive + identifier_set_t names; child_list_t child_types; size_t idx, max; yyjson_val *key, *val; yyjson_obj_foreach(obj, idx, max, key, val) { val = yyjson_obj_iter_get_val(key); string key_str(unsafe_yyjson_get_str(key), unsafe_yyjson_get_len(key)); - if (names.find(key_str) != names.end()) { + if (key_str.empty()) { + throw BinderException("Empty key in object in JSON structure"); + } + if (!names.insert(Identifier(key_str)).second) { JSONCommon::ThrowValFormatError("Duplicate keys in object in JSON structure: %s", val); } - names.insert(key_str); child_types.emplace_back(key_str, StructureStringToType(val, context)); } D_ASSERT(yyjson_obj_size(obj) == names.size()); @@ -919,6 +923,38 @@ static bool TransformObjectToMap(yyjson_val *objects[], yyjson_alc *alc, Vector success = false; } + // objects can have duplicate keys, and keys that fail to transform in non-strict mode are NULL + auto &map_keys = MapVector::GetKeys(result); + auto key_validity = map_keys.Validity(); + value_set_t unique_keys; + for (idx_t i = 0; i < count; i++) { + if (!list_validity.RowIsValid(i)) { + continue; + } + const auto &list_entry = list_entries[i]; + const char *invalid_reason = nullptr; + unique_keys.clear(); + for (idx_t key_idx = list_entry.offset; key_idx < list_entry.offset + list_entry.length; key_idx++) { + if (!key_validity.IsValid(key_idx)) { + invalid_reason = "Map keys can not be NULL"; + break; + } + if (!unique_keys.insert(map_keys.GetValue(key_idx)).second) { + invalid_reason = "Map keys must be unique"; + break; + } + } + if (!invalid_reason) { + continue; + } + list_validity.SetInvalid(i); + if (success && options.strict_cast) { + options.error_message = invalid_reason; + options.object_index = i; + success = false; + } + } + if (!options.delay_error && !success) { throw InvalidInputException(options.error_message); } diff --git a/src/duckdb/extension/json/json_functions/json_type.cpp b/src/duckdb/extension/json/json_functions/json_type.cpp index b476e33a2..002bf7482 100644 --- a/src/duckdb/extension/json/json_functions/json_type.cpp +++ b/src/duckdb/extension/json/json_functions/json_type.cpp @@ -26,6 +26,7 @@ static void GetTypeFunctionsInternal(ScalarFunctionSet &set, const LogicalType & ScalarFunction path_fun({}, LogicalType::VARCHAR, BinaryTypeFunction, JSONReadFunctionData::Bind, nullptr, JSONFunctionLocalState::Init); path_fun.GetSignature().AddParameter("json", input_type).AddParameter("path", LogicalType::VARCHAR); + path_fun.SetResolveTypesCallback(JSONReadFunctionData::ResolveTypes); set.AddFunction(path_fun); ScalarFunction many_fun({}, LogicalType::LIST(LogicalType::VARCHAR), ManyTypeFunction, JSONReadManyFunctionData::Bind, nullptr, JSONFunctionLocalState::Init); diff --git a/src/duckdb/extension/json/json_functions/json_value.cpp b/src/duckdb/extension/json/json_functions/json_value.cpp index 82115403f..176b047ee 100644 --- a/src/duckdb/extension/json/json_functions/json_value.cpp +++ b/src/duckdb/extension/json/json_functions/json_value.cpp @@ -14,10 +14,12 @@ static void GetValueFunctionsInternal(ScalarFunctionSet &set, const LogicalType ScalarFunction index_fun({}, LogicalType::VARCHAR, ValueFunction, JSONReadFunctionData::Bind, nullptr, JSONFunctionLocalState::Init); index_fun.GetSignature().AddParameter("json", input_type).AddParameter("index", LogicalType::BIGINT); + index_fun.SetResolveTypesCallback(JSONReadFunctionData::ResolveTypes); set.AddFunction(index_fun); ScalarFunction path_fun({}, LogicalType::VARCHAR, ValueFunction, JSONReadFunctionData::Bind, nullptr, JSONFunctionLocalState::Init); path_fun.GetSignature().AddParameter("json", input_type).AddParameter("path", LogicalType::VARCHAR); + path_fun.SetResolveTypesCallback(JSONReadFunctionData::ResolveTypes); set.AddFunction(path_fun); ScalarFunction many_fun({}, LogicalType::LIST(LogicalType::VARCHAR), ValueManyFunction, JSONReadManyFunctionData::Bind, nullptr, JSONFunctionLocalState::Init); diff --git a/src/duckdb/extension/json/json_functions/read_json.cpp b/src/duckdb/extension/json/json_functions/read_json.cpp index fa54c21b3..c7fbb3384 100644 --- a/src/duckdb/extension/json/json_functions/read_json.cpp +++ b/src/duckdb/extension/json/json_functions/read_json.cpp @@ -77,7 +77,8 @@ class JSONSchemaTask : public BaseExecutorTask { auto &reader = *json_reader; auto_detect_state.sampled_readers[file_idx] = std::move(json_reader); - auto &global_allocator = Allocator::Get(context); + // the sampled readers keep their first buffer for the scan, so it has to count against the memory limit + auto &global_allocator = BufferAllocator::Get(context); idx_t buffer_capacity = json_data.options.maximum_object_size * 2; JSONReaderScanState scan_state(context, global_allocator, buffer_capacity); auto &options = json_data.options; diff --git a/src/duckdb/extension/json/json_functions/read_single_json_file.cpp b/src/duckdb/extension/json/json_functions/read_single_json_file.cpp index dcedcbf5a..8e46f2d1c 100644 --- a/src/duckdb/extension/json/json_functions/read_single_json_file.cpp +++ b/src/duckdb/extension/json/json_functions/read_single_json_file.cpp @@ -73,6 +73,7 @@ struct ReadSingleJSONFileLocalState : public LocalTableFunctionState { static unique_ptr ReadSingleJSONFileBind(ClientContext &context, TableFunctionBindInput &input, vector &return_types, vector &names) { + auto &file_input = TableFunctionFileBindInput::Get(input); auto result = make_uniq(); auto &options = result->options; if (input.info) { @@ -100,16 +101,16 @@ static unique_ptr ReadSingleJSONFileBind(ClientContext &context, T if (input.inputs[0].IsNull()) { throw BinderException("read_single_json_file requires a non-NULL file name"); } - if (input.expected_bind_data && input.HasExpectedSchema()) { + if (file_input.expected_bind_data && file_input.HasExpectedSchema()) { // the schema of the scan was already determined - read this file exactly the way it was determined, so that // every file of the scan produces the same columns from the same JSON keys - auto &source = input.expected_bind_data->Cast(); - result->file = OpenFileInfo(StringValue::Get(input.inputs[0])); + auto &source = file_input.expected_bind_data->Cast(); + result->file = TableFunctionFileBindInput::GetFile(input); result->options.record_type = source.options.record_type; result->options.geojson = source.options.geojson; result->options.auto_detect = false; - result->options.name_list = *input.expected_names; - result->options.sql_type_list = *input.expected_types; + result->options.name_list = *file_input.expected_names; + result->options.sql_type_list = *file_input.expected_types; result->key_names = source.key_names; result->feature_columns = source.feature_columns; // the date/timestamp formats that auto-detection settled on are part of how the scan is read @@ -119,13 +120,13 @@ static unique_ptr ReadSingleJSONFileBind(ClientContext &context, T return_types = result->options.sql_type_list; return std::move(result); } - if (input.HasExpectedSchema()) { + if (file_input.HasExpectedSchema()) { // the schema is known but not how it was determined (COPY takes its columns from the target table) - read // this file using those columns - options.name_list = *input.expected_names; - options.sql_type_list = *input.expected_types; + options.name_list = *file_input.expected_names; + options.sql_type_list = *file_input.expected_types; } - result->file = OpenFileInfo(StringValue::Get(input.inputs[0])); + result->file = TableFunctionFileBindInput::GetFile(input); // keep the detected structure around - it is used to combine the schema of this file with that of other files // when this function is wrapped into a multi-file function. This is only needed when the columns are not known @@ -272,6 +273,9 @@ static unique_ptr ReadSingleJSONFileCombineSchema(ClientContext &c } // the date/timestamp formats that were settled on while detecting the structure are carried over result->date_format_map = make_uniq(*first_file->date_format_map); + // the merged structure describes the schema of the scan by itself + return_types.clear(); + names.clear(); JSONScan::StructureToColumns(context, result->options, merged, result->feature_columns, return_types, names); // the JSON reader looks columns up by their exact key, so the keys are kept before the column names that are @@ -304,17 +308,15 @@ static unique_ptr ReadSingleJSONFileCardinality(ClientContext &c TableFunction JSONFunctions::GetReadSingleJSONFileTableFunction(shared_ptr function_info) { const auto scan_type = function_info->type; - TableFunction table_function("read_single_json_file", {LogicalType::VARCHAR}, ReadSingleJSONFileFunction, - ReadSingleJSONFileBind, ReadSingleJSONFileInitGlobal, ReadSingleJSONFileInitLocal); + TableFunction table_function( + "read_single_json_file", FunctionSignature().AddPositionalOnly("file", LogicalType::ANY), + ReadSingleJSONFileFunction, ReadSingleJSONFileBind, ReadSingleJSONFileInitGlobal, ReadSingleJSONFileInitLocal); JSONScan::TableFunctionDefaults(table_function); if (scan_type != JSONScanType::READ_JSON_OBJECTS) { // read_json_objects always emits a single JSON column - it has no schema options JSONScan::AddReadJSONParameters(table_function); JSONScan::AddAutoDetectParameters(table_function); } - table_function.combine_schema = ReadSingleJSONFileCombineSchema; - table_function.claim_batch = ReadSingleJSONFileClaimBatch; - table_function.finish_batch = ReadSingleJSONFileFinishBatch; table_function.table_scan_progress = ReadSingleJSONFileProgress; table_function.cardinality = ReadSingleJSONFileCardinality; table_function.function_info = std::move(function_info); @@ -327,8 +329,12 @@ TableFunction JSONFunctions::GetJSONTableFunction(Identifier name, shared_ptr file_handle_p, Allocator &allocator_p) : context(context_p), file_handle(std::move(file_handle_p)), allocator(allocator_p), - can_seek(file_handle->CanSeek()), file_size(file_handle->GetFileSize()), read_position(0), requested_reads(0), - actual_reads(0), last_read_requested(false), cached_size(0) { + can_seek(file_handle->CanSeek()), file_size(file_handle->GetFileSize()), + compressed(file_handle->GetFileCompressionType() != FileCompressionType::UNCOMPRESSED), read_position(0), + requested_reads(0), actual_reads(0), last_read_requested(false), cached_size(0) { } bool JSONFileHandle::IsOpen() const { @@ -62,6 +63,23 @@ idx_t JSONFileHandle::Remaining() const { return file_size - read_position; } +double JSONFileHandle::GetProgress() const { + if (file_size == 0) { + return 0; + } + idx_t position; + if (compressed) { + if (!IsOpen()) { + return last_read_requested ? 1 : 0; + } + // the progress of a compressed file is the position in the compressed stream + position = file_handle->GetProgress(); + } else { + position = read_position; + } + return MinValue(static_cast(position) / static_cast(file_size), 1.0); +} + bool JSONFileHandle::CanSeek() const { return can_seek; } @@ -179,8 +197,8 @@ idx_t JSONFileHandle::ReadFromCache(char *&pointer, idx_t &size, atomic & } JSONReader::JSONReader(ClientContext &context, JSONReaderOptions options_p, OpenFileInfo file_p) - : file(std::move(file_p)), context(context), options(std::move(options_p)), initialized(0), next_buffer_index(0), - thrown(false) { + : file(std::move(file_p)), context(context), options(std::move(options_p)), initialized(false), + next_buffer_index(0), thrown(false) { } void JSONReader::OpenJSONFile() { @@ -256,7 +274,7 @@ JSONFileHandle &JSONReader::GetFileHandle() const { return *file_handle; } -void JSONReader::InsertBuffer(idx_t buffer_idx, unique_ptr &&buffer) { +void JSONReader::InsertBuffer(idx_t buffer_idx, unique_ptr buffer) { lock_guard guard(lock); D_ASSERT(buffer_map.find(buffer_idx) == buffer_map.end()); buffer_map.insert(make_pair(buffer_idx, std::move(buffer))); @@ -288,7 +306,7 @@ void JSONReader::SetBufferLineOrObjectCount(JSONBufferHandle &handle, idx_t coun D_ASSERT(buffer_map.find(handle.buffer_index) != buffer_map.end()); D_ASSERT(RefersToSameObject(handle, *buffer_map.find(handle.buffer_index)->second)); D_ASSERT(buffer_line_or_object_counts[handle.buffer_index] == -1); - buffer_line_or_object_counts[handle.buffer_index] = count; + buffer_line_or_object_counts[handle.buffer_index] = NumericCast(count); // if we have any errors - try to report them after finishing a buffer ThrowErrorsIfPossible(); } @@ -377,11 +395,7 @@ double JSONReader::GetProgress() const { if (!HasFileHandle()) { return 0; } - const auto file_size = file_handle->FileSize(); - if (file_size == 0) { - return 0; - } - return 100.0 - 100.0 * double(file_handle->Remaining()) / double(file_size); + return 100.0 * file_handle->GetProgress(); } static inline void TrimWhitespace(JSONString &line) { @@ -738,7 +752,7 @@ static pair DetectFormatAndRecordType(char *const bu } // We weren't able to parse an array, could be broken or an array larger than our buffer size, let's skip over '[' - SkipWhitespace(buffer_ptr, ++buffer_offset, --remaining); + SkipWhitespace(buffer_ptr, ++buffer_offset, buffer_size); remaining = buffer_size - buffer_offset; // If it's '{' we know there's RECORDS in the ARRAY, else it's VALUES @@ -754,7 +768,18 @@ bool JSONReader::ParseJSON(JSONReaderScanState &scan_state, char *const json_sta const idx_t remaining) { yyjson_doc *doc; yyjson_read_err err; - if (options.type == JSONScanType::READ_JSON_OBJECTS) { // If we return strings, we cannot parse INSITU + // If we return strings, we cannot parse INSITU. If we ignore errors, we cannot parse INSITU either: a malformed + // document could extend into (and modify) the next one + const bool ignorable_errors = options.ignore_errors && GetFormat() == JSONFormat::NEWLINE_DELIMITED; + // A number that does not fit is read as a raw string, which INSITU parsing null-terminates in place - at the root + // that overwrites the delimiter that follows the document + idx_t first_char = 0; + while (first_char < json_size && StringUtil::CharacterIsSpace(json_start[first_char])) { + first_char++; + } + const bool number_root = first_char < json_size && + (StringUtil::CharacterIsDigit(json_start[first_char]) || json_start[first_char] == '-'); + if (options.type == JSONScanType::READ_JSON_OBJECTS || ignorable_errors || number_root) { doc = JSONCommon::ReadDocumentUnsafe(json_start, json_size, JSONCommon::READ_STOP_FLAG, scan_state.allocator.GetYYAlc(), &err); } else { @@ -851,13 +876,23 @@ void JSONReader::AutoDetect(Allocator &allocator, idx_t buffer_capacity) { } if (!options.ignore_errors && options.record_type == JSONRecordType::RECORDS && GetRecordType() != JSONRecordType::RECORDS) { + // LCOV_EXCL_START string unit = options.format == JSONFormat::NEWLINE_DELIMITED ? "line" : "record/value"; throw InvalidInputException( "JSON auto-detection error in file \"%s\": Expected records, detected non-record JSON instead", GetFileName()); + // LCOV_EXCL_STOP } // store the buffer in the file so it can be re-used by the first reader of the file if (!file_handle->IsPipe()) { + // a file that is much smaller than the buffer only keeps what was read - with many files (e.g. union_by_name) + // one full-sized buffer per file adds up to far more than the data + const idx_t required = read_size + YYJSON_PADDING_SIZE; + if (required * 2 <= buffer_capacity) { + auto shrunk = allocator.Allocate(required); + memcpy(shrunk.get(), read_buffer.get(), required); + read_buffer = std::move(shrunk); + } auto_detect_data = std::move(read_buffer); auto_detect_data_size = read_size; } else { @@ -866,10 +901,12 @@ void JSONReader::AutoDetect(Allocator &allocator, idx_t buffer_capacity) { } void JSONReader::ThrowObjectSizeError(const idx_t object_size) { + // LCOV_EXCL_START throw InvalidInputException( "\"maximum_object_size\" of %llu bytes exceeded while reading file \"%s\" (>%llu bytes)." "\n Try increasing \"maximum_object_size\".", options.maximum_object_size, GetFileName(), object_size); + // LCOV_EXCL_STOP } bool JSONReader::CopyRemainderFromPreviousBuffer(JSONReaderScanState &scan_state) { diff --git a/src/duckdb/extension/json/json_scan.cpp b/src/duckdb/extension/json/json_scan.cpp index 7effec241..b34970e77 100644 --- a/src/duckdb/extension/json/json_scan.cpp +++ b/src/duckdb/extension/json/json_scan.cpp @@ -111,19 +111,22 @@ void JSONScanLocalState::AddTransformError(idx_t object_index, const string &err scan_state.current_reader->AddTransformError(scan_state, object_index, error_message); } -void JSONScan::Serialize(Serializer &serializer, const optional_ptr bind_data_p, const TableFunction &) { +void JSONScan::Serialize(Serializer &serializer, const optional_ptr bind_data_p, + const BoundTableFunction &) { throw NotImplementedException("JSONScan Serialize not implemented"); } -unique_ptr JSONScan::Deserialize(Deserializer &deserializer, TableFunction &) { +unique_ptr JSONScan::Deserialize(Deserializer &deserializer, BoundTableFunction &) { throw NotImplementedException("JSONScan Deserialize not implemented"); } void JSONScan::TableFunctionDefaults(TableFunction &table_function) { - table_function.named_parameters["maximum_object_size"] = LogicalType::UINTEGER; - table_function.named_parameters["ignore_errors"] = LogicalType::BOOLEAN; - table_function.named_parameters["format"] = LogicalType::VARCHAR; - table_function.named_parameters["compression"] = LogicalType::VARCHAR; + table_function.GetSignature().WithTypedKwargs("options", [](TypedKwargs &options) { + options.Add("maximum_object_size", LogicalType::UINTEGER) + .Add("ignore_errors", LogicalType::BOOLEAN) + .Add("format", LogicalType::VARCHAR) + .Add("compression", LogicalType::VARCHAR); + }); table_function.serialize = Serialize; table_function.deserialize = Deserialize; @@ -134,24 +137,29 @@ void JSONScan::TableFunctionDefaults(TableFunction &table_function) { } void JSONScan::AddReadJSONParameters(TableFunction &table_function) { - table_function.named_parameters["columns"] = LogicalType::ANY; - table_function.named_parameters["auto_detect"] = LogicalType::BOOLEAN; - table_function.named_parameters["geojson"] = LogicalType::BOOLEAN; - table_function.named_parameters["sample_size"] = LogicalType::BIGINT; - table_function.named_parameters["dateformat"] = LogicalType::VARCHAR; - table_function.named_parameters["date_format"] = LogicalType::VARCHAR; - table_function.named_parameters["timestampformat"] = LogicalType::VARCHAR; - table_function.named_parameters["timestamp_format"] = LogicalType::VARCHAR; - table_function.named_parameters["records"] = LogicalType::VARCHAR; - table_function.named_parameters["array"] = LogicalType::BOOLEAN; - table_function.named_parameters["maximum_sample_files"] = LogicalType::BIGINT; + // extends the options TableFunctionDefaults declares + table_function.GetSignature().ExtendTypedKwargs([](TypedKwargs &options) { + options.Add("columns", LogicalType::ANY) + .Add("auto_detect", LogicalType::BOOLEAN) + .Add("geojson", LogicalType::BOOLEAN) + .Add("sample_size", LogicalType::BIGINT) + .Add("dateformat", LogicalType::VARCHAR) + .Add("date_format", LogicalType::VARCHAR) + .Add("timestampformat", LogicalType::VARCHAR) + .Add("timestamp_format", LogicalType::VARCHAR) + .Add("records", LogicalType::ANY) + .Add("array", LogicalType::BOOLEAN) + .Add("maximum_sample_files", LogicalType::BIGINT); + }); } void JSONScan::AddAutoDetectParameters(TableFunction &table_function) { - table_function.named_parameters["maximum_depth"] = LogicalType::BIGINT; - table_function.named_parameters["field_appearance_threshold"] = LogicalType::DOUBLE; - table_function.named_parameters["convert_strings_to_integers"] = LogicalType::BOOLEAN; - table_function.named_parameters["map_inference_threshold"] = LogicalType::BIGINT; + table_function.GetSignature().ExtendTypedKwargs([](TypedKwargs &options) { + options.Add("maximum_depth", LogicalType::BIGINT) + .Add("field_appearance_threshold", LogicalType::DOUBLE) + .Add("convert_strings_to_integers", LogicalType::BOOLEAN) + .Add("map_inference_threshold", LogicalType::BIGINT); + }); } bool JSONScan::ParseOption(ClientContext &context, const Identifier &key, const Value &value, @@ -238,8 +246,10 @@ bool JSONScan::ParseOption(ClientContext &context, const Identifier &key, const } else if (arg > 0) { options.sample_size = arg; } else { + // LCOV_EXCL_START throw BinderException("read_json \"sample_size\" parameter must be positive, or -1 to sample all input " "files entirely, up to \"maximum_sample_files\" files."); + // LCOV_EXCL_STOP } return true; } @@ -267,8 +277,10 @@ bool JSONScan::ParseOption(ClientContext &context, const Identifier &key, const } else if (arg >= 0) { options.map_inference_threshold = arg; } else { + // LCOV_EXCL_START throw BinderException("read_json_auto \"map_inference_threshold\" parameter must be 0 or positive, " "or -1 to disable map inference for consistent objects."); + // LCOV_EXCL_STOP } return true; } @@ -301,7 +313,18 @@ bool JSONScan::ParseOption(ClientContext &context, const Identifier &key, const return true; } if (key == "records") { - auto arg = StringValue::Get(value); + // records is tri-state: 'auto', 'true' or 'false'. Accept a boolean for the latter two, since + // records = false reads more naturally than records = 'false'. + string arg; + if (value.type().id() == LogicalTypeId::BOOLEAN) { + arg = BooleanValue::Get(value) ? "true" : "false"; + } else if (value.type().id() == LogicalTypeId::VARCHAR) { + arg = StringValue::Get(value); + } else { + throw BinderException("read_json \"records\" parameter must be a string ('auto', 'true' or 'false') or " + "a boolean, not %s", + value.type().ToString()); + } if (arg == "auto") { options.record_type = JSONRecordType::AUTO_DETECT; } else if (arg == "true") { @@ -320,8 +343,10 @@ bool JSONScan::ParseOption(ClientContext &context, const Identifier &key, const } else if (arg > 0) { options.maximum_sample_files = arg; } else { + // LCOV_EXCL_START throw BinderException("read_json \"maximum_sample_files\" parameter must be positive, or -1 to remove " "the limit on the number of files used to sample \"sample_size\" rows."); + // LCOV_EXCL_STOP } return true; } @@ -351,13 +376,17 @@ void JSONScan::BindSchema(ClientContext &context, JSONScanData &json_data, Multi if (!options.auto_detect) { // Need to specify columns if RECORDS and not auto-detecting if (return_types.empty()) { + // LCOV_EXCL_START throw BinderException("When auto_detect=false, read_json requires columns to be specified through the " "\"columns\" parameter."); + // LCOV_EXCL_STOP } // If we are reading VALUES, we can only have one column if (options.record_type == JSONRecordType::VALUES && return_types.size() != 1) { + // LCOV_EXCL_START throw BinderException("read_json requires a single column to be specified through the \"columns\" " "parameter when \"records\" is set to 'false'."); + // LCOV_EXCL_STOP } } diff --git a/src/duckdb/extension/json/json_serializer.cpp b/src/duckdb/extension/json/json_serializer.cpp index 0b38354c3..19dc0e597 100644 --- a/src/duckdb/extension/json/json_serializer.cpp +++ b/src/duckdb/extension/json/json_serializer.cpp @@ -105,6 +105,7 @@ void JsonSerializer::OnObjectEnd() { yyjson_mut_val *item; yyjson_mut_val *key; const char *found; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast): yyjson iteration macro yyjson_mut_obj_foreach(parent, idx, max, key, item) { if (item == obj) { found = yyjson_mut_get_str(key); @@ -185,7 +186,7 @@ void JsonSerializer::WriteValue(uhugeint_t value) { } yyjson_mut_val *DoubleToJSONValue(yyjson_mut_doc *doc, double value) { - if (Value::FloatIsFinite(value)) { + if (Value::DoubleIsFinite(value)) { // simple - finite json return yyjson_mut_real(doc, value); } diff --git a/src/duckdb/extension/loader/dummy_static_extension_loader.cpp b/src/duckdb/extension/loader/dummy_static_extension_loader.cpp deleted file mode 100644 index 406a8d9da..000000000 --- a/src/duckdb/extension/loader/dummy_static_extension_loader.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "duckdb/main/config.hpp" -#include "duckdb/main/extension_helper.hpp" - -// This is a dummy loader to produce a workable duckdb library without linking any extensions. -// Link this to libduckdb_static.a to get a working system. -// -// Note that it no longer stubs out LoadExtension: that lives in core and reads the registry on the -// config, so a binary (or an extension) that links this can still load whatever it was handed. - -namespace duckdb { - -void ExtensionHelper::RegisterLinkedExtensions(DBConfig &config) { - // nothing is linked into this binary -} - -vector ExtensionHelper::LoadedExtensionTestPaths() { - return {}; -} - -} // namespace duckdb diff --git a/src/duckdb/extension/loader/static_extension_autoregister.cpp b/src/duckdb/extension/loader/static_extension_autoregister.cpp new file mode 100644 index 000000000..463bf2db7 --- /dev/null +++ b/src/duckdb/extension/loader/static_extension_autoregister.cpp @@ -0,0 +1,16 @@ +// Calls the generated static extension loader before main. Compile it into a program that wants its statically linked +// extensions registered without calling duckdb_register_static_extensions itself. Compile it into the program, not into +// an archive, or the linker drops it. + +#include + +extern "C" int32_t duckdb_register_static_extensions(void); + +namespace { +struct DuckDBStaticExtensionLoader { + DuckDBStaticExtensionLoader() { + duckdb_register_static_extensions(); + } +}; +const DuckDBStaticExtensionLoader duckdb_static_extension_loader; +} // namespace diff --git a/src/duckdb/extension/parquet/column_reader.cpp b/src/duckdb/extension/parquet/column_reader.cpp index 7d0d116e6..54519dfc2 100644 --- a/src/duckdb/extension/parquet/column_reader.cpp +++ b/src/duckdb/extension/parquet/column_reader.cpp @@ -166,12 +166,11 @@ void ColumnReader::ValidateColumnMetadata(idx_t row_group_num_rows, const Column Reader().GetFileName().c_str()); } -uint64_t ColumnReader::TotalCompressedSize() { +void ColumnReader::GatherColumnChunks(unordered_set &column_chunks) { if (IsSkipped()) { - return 0; + return; } - - return chunk->meta_data.total_compressed_size; + column_chunks.insert(ColumnIndex()); } // Note: It's not trivial to determine where all Column data is stored. Chunk->file_offset @@ -251,6 +250,9 @@ void ColumnReader::InitializeRead(idx_t row_group_idx_p, idx_t row_group_num_row chunk = &columns[ColumnIndex()]; protocol = &protocol_p; D_ASSERT(chunk); + if (chunk->__isset.crypto_metadata && !reader.metadata->crypto_metadata) { + throw InvalidInputException("File is encrypted but no file crypto metadata is set"); + } D_ASSERT(chunk->__isset.meta_data); if (chunk->__isset.file_path) { @@ -434,10 +436,12 @@ void ColumnReader::PreparePageV2(PageHeader &page_hdr) { } if (chunk->meta_data.codec == CompressionCodec::UNCOMPRESSED) { if (page_hdr.compressed_page_size != page_hdr.uncompressed_page_size) { + // LCOV_EXCL_START const auto &file_name = Reader().GetFileName(); throw InvalidInputException( "Parquet file (%s) corrupted: uncompressed page size mismatch (expected %d, actual: %d)", file_name, page_hdr.uncompressed_page_size, page_hdr.compressed_page_size); + // LCOV_EXCL_STOP } uncompressed = true; } @@ -452,24 +456,30 @@ void ColumnReader::PreparePageV2(PageHeader &page_hdr) { // to uint64_t, and the uint64_t casts in the comparisons below are safe. if (page_hdr.data_page_header_v2.repetition_levels_byte_length < 0 || page_hdr.data_page_header_v2.definition_levels_byte_length < 0) { + // LCOV_EXCL_START throw InvalidInputException( "Failed to read file \"%s\": header inconsistency, repetition_levels_byte_length and " "definition_levels_byte_length must be >= 0", Reader().GetFileName()); + // LCOV_EXCL_STOP } uint64_t uncompressed_bytes = static_cast(page_hdr.data_page_header_v2.repetition_levels_byte_length) + page_hdr.data_page_header_v2.definition_levels_byte_length; if (uncompressed_bytes > static_cast(page_hdr.uncompressed_page_size)) { + // LCOV_EXCL_START throw InvalidInputException( "Failed to read file \"%s\": header inconsistency, uncompressed_page_size needs to be larger than " "repetition_levels_byte_length + definition_levels_byte_length", Reader().GetFileName()); + // LCOV_EXCL_STOP } if (static_cast(page_hdr.compressed_page_size) < uncompressed_bytes) { + // LCOV_EXCL_START throw InvalidInputException( "Failed to read file \"%s\": header inconsistency, compressed_page_size is smaller than " "repetition_levels_byte_length + definition_levels_byte_length", Reader().GetFileName()); + // LCOV_EXCL_STOP } ReadData(block->ptr, uncompressed_bytes, page_hdr.type); @@ -477,10 +487,12 @@ void ColumnReader::PreparePageV2(PageHeader &page_hdr) { auto compressed_bytes = page_hdr.compressed_page_size - uncompressed_bytes; if (compressed_bytes == 0 && static_cast(page_hdr.uncompressed_page_size) > uncompressed_bytes) { + // LCOV_EXCL_START throw InvalidInputException( "Failed to read file \"%s\": header inconsistency, compressed_page_size is too small for the " "declared value region", Reader().GetFileName()); + // LCOV_EXCL_STOP } if (compressed_bytes > 0) { @@ -507,9 +519,6 @@ void ColumnReader::PreparePage(PageHeader &page_hdr) { uint32_t compressed_page_size = page_hdr.compressed_page_size; if (chunk->__isset.crypto_metadata) { - if (!reader.metadata->crypto_metadata) { - throw InvalidInputException("File is encrypted but no file crypto metadata is set"); - } auto const file_aad = reader.GetUniqueFileIdentifier(reader.metadata->crypto_metadata->encryption_algorithm); if (!file_aad.empty()) { // If there is a file aad (identifier), this means that the Encrypted file is written by Arrow @@ -522,10 +531,12 @@ void ColumnReader::PreparePage(PageHeader &page_hdr) { if (chunk->meta_data.codec == CompressionCodec::UNCOMPRESSED) { if (compressed_page_size != NumericCast(page_hdr.uncompressed_page_size)) { + // LCOV_EXCL_START const auto &file_name = Reader().GetFileName(); throw InvalidInputException( "Parquet file (%s) corrupted: uncompressed page size mismatch (expected %d, actual: %d)", file_name, page_hdr.uncompressed_page_size, compressed_page_size); + // LCOV_EXCL_STOP } ReadData(block->ptr, compressed_page_size, page_hdr.type); return; @@ -539,6 +550,50 @@ void ColumnReader::PreparePage(PageHeader &page_hdr) { page_hdr.uncompressed_page_size); } +static bool TryDecompressLZ4Hadoop(const_data_ptr_t src, idx_t src_size, data_ptr_t dst, idx_t dst_size) { + // Hadoop framing stores a big-endian uncompressed block size, followed by one or more + // chunks, each prefixed by its big-endian compressed size. + while (src_size > 0) { + if (src_size < sizeof(uint32_t)) { + return false; + } + auto block_size = BSwapIfLE(Load(src)); + src += sizeof(uint32_t); + src_size -= sizeof(uint32_t); + if (block_size > dst_size) { + return false; + } + if (block_size == 0) { + // Hadoop uses a zero-sized block to terminate an empty stream. + return src_size == 0 && dst_size == 0; + } + idx_t remaining = block_size; + while (remaining > 0) { + if (src_size < sizeof(uint32_t)) { + return false; + } + auto compressed_size = BSwapIfLE(Load(src)); + src += sizeof(uint32_t); + src_size -= sizeof(uint32_t); + if (compressed_size == 0 || compressed_size > src_size) { + return false; + } + auto decompressed_size = + duckdb_lz4::LZ4_decompress_safe(const_char_ptr_cast(src), char_ptr_cast(dst), + NumericCast(compressed_size), NumericCast(remaining)); + if (decompressed_size <= 0) { + return false; + } + src += compressed_size; + src_size -= compressed_size; + dst += decompressed_size; + dst_size -= decompressed_size; + remaining -= decompressed_size; + } + } + return dst_size == 0; +} + void ColumnReader::DecompressInternal(CompressionCodec::type codec, const_data_ptr_t src, idx_t src_size, data_ptr_t dst, idx_t dst_size) { switch (codec) { @@ -549,6 +604,12 @@ void ColumnReader::DecompressInternal(CompressionCodec::type codec, const_data_p s.Decompress(const_char_ptr_cast(src), src_size, char_ptr_cast(dst), dst_size); break; } + case CompressionCodec::LZ4: + if (TryDecompressLZ4Hadoop(src, src_size, dst, dst_size)) { + break; + } + // Early parquet-cpp writers used raw LZ4 under the legacy codec identifier. + DUCKDB_EXPLICIT_FALLTHROUGH; case CompressionCodec::LZ4_RAW: { auto res = duckdb_lz4::LZ4_decompress_safe(const_char_ptr_cast(src), char_ptr_cast(dst), @@ -604,13 +665,13 @@ void ColumnReader::DecompressInternal(CompressionCodec::type codec, const_data_p break; } - default: { + default: { // LCOV_EXCL_START duckdb::stringstream codec_name; codec_name << codec; throw InvalidInputException("Failed to read file \"%s\": Unsupported compression codec \"%s\". Supported " - "options are uncompressed, brotli, gzip, lz4_raw, snappy or zstd", + "options are uncompressed, brotli, gzip, lz4, lz4_raw, snappy or zstd", Reader().GetFileName(), codec_name.str()); - } + } // LCOV_EXCL_STOP } } @@ -1012,6 +1073,20 @@ static unique_ptr CreateDecimalReader(const ParquetReader &reader, } } +static_assert(ParquetTimestampLogicalType(ParquetExtraTypeInfo::UNIT_NS) != LogicalTypeId::TIMESTAMP); +static_assert(ParquetTimestampTzLogicalType(ParquetExtraTypeInfo::UNIT_NS) != LogicalTypeId::TIMESTAMP_TZ); + +static_assert(ParquetTimestampLogicalType(ParquetExtraTypeInfo::IMPALA_TIMESTAMP) != LogicalTypeId::TIMESTAMP_NS); +static_assert(ParquetTimestampTzLogicalType(ParquetExtraTypeInfo::IMPALA_TIMESTAMP) != LogicalTypeId::TIMESTAMP_TZ_NS); +static_assert(ParquetTimestampLogicalType(ParquetExtraTypeInfo::UNIT_MS) != LogicalTypeId::TIMESTAMP_NS); +static_assert(ParquetTimestampTzLogicalType(ParquetExtraTypeInfo::UNIT_MS) != LogicalTypeId::TIMESTAMP_TZ_NS); +static_assert(ParquetTimestampLogicalType(ParquetExtraTypeInfo::UNIT_MICROS) != LogicalTypeId::TIMESTAMP_NS); +static_assert(ParquetTimestampTzLogicalType(ParquetExtraTypeInfo::UNIT_MICROS) != LogicalTypeId::TIMESTAMP_TZ_NS); + +static_assert(ParquetTimeLogicalType(ParquetExtraTypeInfo::UNIT_NS) != LogicalTypeId::TIME); +static_assert(ParquetTimeLogicalType(ParquetExtraTypeInfo::UNIT_MS) != LogicalTypeId::TIME_NS); +static_assert(ParquetTimeLogicalType(ParquetExtraTypeInfo::UNIT_MICROS) != LogicalTypeId::TIME_NS); + unique_ptr ColumnReader::CreateReader(const ParquetReader &reader, const ParquetColumnSchema &schema) { switch (schema.type.id()) { case LogicalTypeId::BOOLEAN: @@ -1052,8 +1127,6 @@ unique_ptr ColumnReader::CreateReader(const ParquetReader &reader, case ParquetExtraTypeInfo::UNIT_MICROS: return make_uniq>(reader, schema); - case ParquetExtraTypeInfo::UNIT_NS: - return make_uniq>(reader, schema); default: throw InternalException("TIMESTAMP requires type info"); } @@ -1082,17 +1155,11 @@ unique_ptr ColumnReader::CreateReader(const ParquetReader &reader, return make_uniq>(reader, schema); case ParquetExtraTypeInfo::UNIT_MICROS: return make_uniq>(reader, schema); - case ParquetExtraTypeInfo::UNIT_NS: - return make_uniq>(reader, schema); default: throw InternalException("TIME requires type info"); } case LogicalTypeId::TIME_NS: switch (schema.type_info) { - case ParquetExtraTypeInfo::UNIT_MS: - return make_uniq>(reader, schema); - case ParquetExtraTypeInfo::UNIT_MICROS: - return make_uniq>(reader, schema); case ParquetExtraTypeInfo::UNIT_NS: return make_uniq>(reader, schema); default: diff --git a/src/duckdb/extension/parquet/column_writer.cpp b/src/duckdb/extension/parquet/column_writer.cpp index afabf4e66..fbe5e7108 100644 --- a/src/duckdb/extension/parquet/column_writer.cpp +++ b/src/duckdb/extension/parquet/column_writer.cpp @@ -94,6 +94,10 @@ bool ColumnWriterStatistics::HasNaN() { return false; } +idx_t ColumnWriterStatistics::GetNaNCount() { + return 0; +} + bool ColumnWriterStatistics::MinIsExact() { return true; } @@ -125,6 +129,25 @@ ColumnWriter::ColumnWriter(ParquetWriter &writer, ParquetColumnSchema &&column_s ColumnWriter::~ColumnWriter() { } +void ColumnWriter::MarkRepetitionRequired() { + if (column_schema.repetition_type == duckdb_parquet::FieldRepetitionType::REQUIRED) { + return; + } + D_ASSERT(column_schema.repetition_type == duckdb_parquet::FieldRepetitionType::OPTIONAL); + D_ASSERT(can_have_nulls); + column_schema.repetition_type = duckdb_parquet::FieldRepetitionType::REQUIRED; + can_have_nulls = false; + DecrementMaxDefineRecursive(); +} + +void ColumnWriter::DecrementMaxDefineRecursive() { + D_ASSERT(column_schema.max_define > 0); + column_schema.max_define--; + for (auto &child : child_writers) { + child->DecrementMaxDefineRecursive(); + } +} + bool ColumnWriter::TryExportPreparedShreddingType(ShreddingType &result) const { bool has_shredding = false; auto writer_shredding_type = ShreddingType(Type()); @@ -300,7 +323,8 @@ unique_ptr ColumnWriter::CreateWriterRecursive(ClientContext &cont } if (type.id() == LogicalTypeId::VARIANT) { - const bool is_shredded = shredding_type != nullptr; + const bool is_shredded = shredding_type && shredding_type->type.id() != LogicalTypeId::SQLNULL && + shredding_type->type.id() != LogicalTypeId::ANY; //! Build the child types for the Parquet VARIANT child_list_t child_types; @@ -308,10 +332,8 @@ unique_ptr ColumnWriter::CreateWriterRecursive(ClientContext &cont child_types.emplace_back("value", LogicalType::BLOB); if (is_shredded) { auto &typed_value_type = shredding_type->type; - if (typed_value_type.id() != LogicalTypeId::SQLNULL) { - child_types.emplace_back("typed_value", - VariantColumnWriter::TransformTypedValueRecursive(typed_value_type)); - } + child_types.emplace_back("typed_value", + VariantColumnWriter::TransformTypedValueRecursive(typed_value_type)); } //! Construct the column schema @@ -351,7 +373,7 @@ unique_ptr ColumnWriter::CreateWriterRecursive(ClientContext &cont max_define + 1, is_optional)); } return make_uniq(writer, std::move(variant_column), path_in_schema, - std::move(child_writers)); + std::move(child_writers), !shredding_type); } if (StructType::IsStruct(type.id()) || type.id() == LogicalTypeId::UNION) { @@ -407,10 +429,7 @@ unique_ptr ColumnWriter::CreateWriterRecursive(ClientContext &cont path_in_schema.push_back("key_value"); // construct the child types recursively - child_list_t key_value; - key_value.reserve(2); - key_value.emplace_back("key", MapType::KeyType(type)); - key_value.emplace_back("value", MapType::ValueType(type)); + auto key_value = LogicalType::GetNamedChildTypes(type); auto key_value_type = LogicalType::STRUCT(key_value); auto map_column = diff --git a/src/duckdb/extension/parquet/decoder/delta_length_byte_array_decoder.cpp b/src/duckdb/extension/parquet/decoder/delta_length_byte_array_decoder.cpp index 6ded4d5f4..74da3df82 100644 --- a/src/duckdb/extension/parquet/decoder/delta_length_byte_array_decoder.cpp +++ b/src/duckdb/extension/parquet/decoder/delta_length_byte_array_decoder.cpp @@ -41,27 +41,20 @@ void DeltaLengthByteArrayDecoder::InitializePage() { length_idx = 0; } +bool IsContinuationByte(uint8_t b) { + return (b & 0xC0) == 0x80; +} + void DeltaLengthByteArrayDecoder::Read(shared_ptr &block_ref, uint8_t *defines, idx_t read_count, Vector &result, idx_t result_offset) { - bool validate_individually = - reader.Type().IsJSONType() || reader.Cast().reader.parquet_options.utf8_validation_option != - StringColumnReader::Utf8ValidationOption::STRICT_UTF8; if (defines) { - if (validate_individually) { - ReadInternal(block_ref, defines, read_count, result, result_offset); - } else { - ReadInternal(block_ref, defines, read_count, result, result_offset); - } + ReadInternal(block_ref, defines, read_count, result, result_offset); } else { - if (validate_individually) { - ReadInternal(block_ref, defines, read_count, result, result_offset); - } else { - ReadInternal(block_ref, defines, read_count, result, result_offset); - } + ReadInternal(block_ref, defines, read_count, result, result_offset); } } -template +template void DeltaLengthByteArrayDecoder::ReadInternal(shared_ptr &block_ref, uint8_t *const defines, const idx_t read_count, Vector &result, const idx_t result_offset) { auto &block = *block_ref; @@ -80,8 +73,16 @@ void DeltaLengthByteArrayDecoder::ReadInternal(shared_ptr &blo const auto &string_column_reader = reader.Cast(); string_column_reader.SetCurrentResult(result); + // JSON and non-strict modes require individual validation: bytes are rewritten and the fast path discards its + // return value so it can't sanitize + const bool needs_full_individual_validation = + reader.Type().IsJSONType() || reader.Cast().reader.parquet_options.utf8_validation_option != + StringColumnReader::Utf8ValidationOption::STRICT_UTF8; + const auto start_ptr = block.ptr; + auto current_ptr = start_ptr; auto result_data = FlatVector::Writer(result, read_count, result_offset); + for (idx_t row_idx = 0; row_idx < read_count; row_idx++) { const auto result_idx = result_offset + row_idx; if (HAS_DEFINES) { @@ -96,20 +97,26 @@ void DeltaLengthByteArrayDecoder::ReadInternal(shared_ptr &blo length_idx, byte_array_count); } } - const auto &str_len = length_data[length_idx++]; - if (VALIDATE_INDIVIDUAL_STRINGS) { - auto verified = string_column_reader.VerifyString(char_ptr_cast(block.ptr), str_len); + const auto str_len = length_data[length_idx++]; + + if (needs_full_individual_validation) { + auto verified = string_column_reader.VerifyString(char_ptr_cast(current_ptr), str_len); result_data.WriteValue(verified); } else { - result_data.WriteValue(string_t(char_ptr_cast(block.ptr), str_len)); + // Batch validation below catches incomplete trailing characters. Within a valid batch, a character + // can only cross a string boundary if the next nonempty string starts with a continuation byte. + if (str_len > 0 && IsContinuationByte(current_ptr[0])) { + string_column_reader.VerifyString(char_ptr_cast(current_ptr), str_len); + } + result_data.WriteStringRef(string_t(char_ptr_cast(current_ptr), str_len)); } - block.unsafe_inc(str_len); + current_ptr += str_len; } - if (!VALIDATE_INDIVIDUAL_STRINGS) { + block.unsafe_inc(current_ptr - start_ptr); + if (!needs_full_individual_validation) { string_column_reader.VerifyString(char_ptr_cast(start_ptr), NumericCast(block.ptr - start_ptr)); } - StringColumnReader::ReferenceBlock(result, block_ref); } diff --git a/src/duckdb/extension/parquet/include/column_reader.hpp b/src/duckdb/extension/parquet/include/column_reader.hpp index f06aae93b..edf0a320b 100644 --- a/src/duckdb/extension/parquet/include/column_reader.hpp +++ b/src/duckdb/extension/parquet/include/column_reader.hpp @@ -29,6 +29,7 @@ #include "duckdb/common/operator/cast_operators.hpp" #include "duckdb/common/types/string_type.hpp" #include "duckdb/common/types/vector.hpp" +#include "duckdb/common/unordered_set.hpp" #include "duckdb/common/types/vector_cache.hpp" #include "duckdb/common/encryption_functions.hpp" #include "duckdb/common/assert.hpp" @@ -167,7 +168,8 @@ class ColumnReader { } virtual idx_t FileOffset() const; - virtual uint64_t TotalCompressedSize(); + //! Adds the indexes of the row group's column chunks this reader reads + virtual void GatherColumnChunks(unordered_set &column_chunks); virtual idx_t GroupRowsAvailable(); // register the range this reader will touch for prefetching diff --git a/src/duckdb/extension/parquet/include/column_writer.hpp b/src/duckdb/extension/parquet/include/column_writer.hpp index 00309aaea..612567f53 100644 --- a/src/duckdb/extension/parquet/include/column_writer.hpp +++ b/src/duckdb/extension/parquet/include/column_writer.hpp @@ -163,6 +163,9 @@ class ColumnWriter { return child_writers; } + //! Mark this writer (and descendants) as REQUIRED in the Parquet schema + void MarkRepetitionRequired(); + virtual void AnalyzeSchema(ParquetAnalyzeSchemaState &state, Vector &input, idx_t count) { throw NotImplementedException("Writer doesn't require an AnalyzeSchema pass"); } @@ -228,6 +231,8 @@ class ColumnWriter { void CompressPage(MemoryStream &temp_writer, size_t &compressed_size, data_ptr_t &compressed_data, AllocatedData &compressed_buf); + void DecrementMaxDefineRecursive(); + public: ParquetWriter &writer; //! The parent writer (if this is a nested field) diff --git a/src/duckdb/extension/parquet/include/decoder/delta_length_byte_array_decoder.hpp b/src/duckdb/extension/parquet/include/decoder/delta_length_byte_array_decoder.hpp index fdc379053..d3d64def2 100644 --- a/src/duckdb/extension/parquet/include/decoder/delta_length_byte_array_decoder.hpp +++ b/src/duckdb/extension/parquet/include/decoder/delta_length_byte_array_decoder.hpp @@ -33,7 +33,7 @@ class DeltaLengthByteArrayDecoder { void Skip(uint8_t *defines, idx_t skip_count); private: - template + template void ReadInternal(shared_ptr &block, uint8_t *defines, idx_t read_count, Vector &result, idx_t result_offset); template diff --git a/src/duckdb/extension/parquet/include/parquet_column_schema.hpp b/src/duckdb/extension/parquet/include/parquet_column_schema.hpp index 2850ab3ab..9a9fdeae0 100644 --- a/src/duckdb/extension/parquet/include/parquet_column_schema.hpp +++ b/src/duckdb/extension/parquet/include/parquet_column_schema.hpp @@ -45,6 +45,54 @@ enum class ParquetExtraTypeInfo { FLOAT16 }; +constexpr LogicalTypeId ParquetTimestampLogicalType(ParquetExtraTypeInfo type_info) { + switch (type_info) { + case ParquetExtraTypeInfo::IMPALA_TIMESTAMP: + case ParquetExtraTypeInfo::UNIT_MS: + case ParquetExtraTypeInfo::UNIT_MICROS: + return LogicalTypeId::TIMESTAMP; + case ParquetExtraTypeInfo::UNIT_NS: + return LogicalTypeId::TIMESTAMP_NS; + default: + return LogicalTypeId::INVALID; + } +} + +constexpr LogicalTypeId ParquetTimestampTzLogicalType(ParquetExtraTypeInfo type_info) { + switch (type_info) { + case ParquetExtraTypeInfo::UNIT_NS: + return LogicalTypeId::TIMESTAMP_TZ_NS; + case ParquetExtraTypeInfo::UNIT_MS: + case ParquetExtraTypeInfo::UNIT_MICROS: + return LogicalTypeId::TIMESTAMP_TZ; + default: + return LogicalTypeId::INVALID; + } +} + +constexpr LogicalTypeId ParquetTimeLogicalType(ParquetExtraTypeInfo type_info) { + switch (type_info) { + case ParquetExtraTypeInfo::UNIT_NS: + return LogicalTypeId::TIME_NS; + case ParquetExtraTypeInfo::UNIT_MS: + case ParquetExtraTypeInfo::UNIT_MICROS: + return LogicalTypeId::TIME; + default: + return LogicalTypeId::INVALID; + } +} + +constexpr LogicalTypeId ParquetTimeTzLogicalType(ParquetExtraTypeInfo type_info) { + switch (type_info) { + case ParquetExtraTypeInfo::UNIT_MS: + case ParquetExtraTypeInfo::UNIT_MICROS: + case ParquetExtraTypeInfo::UNIT_NS: + return LogicalTypeId::TIME_TZ; + default: + return LogicalTypeId::INVALID; + } +} + struct ParquetColumnSchema { public: ParquetColumnSchema() = default; diff --git a/src/duckdb/extension/parquet/include/parquet_dbp_decoder.hpp b/src/duckdb/extension/parquet/include/parquet_dbp_decoder.hpp index 775160215..6f71072e3 100644 --- a/src/duckdb/extension/parquet/include/parquet_dbp_decoder.hpp +++ b/src/duckdb/extension/parquet/include/parquet_dbp_decoder.hpp @@ -27,7 +27,7 @@ class DbpDecoder { miniblock_index(number_of_miniblocks_per_block - 1), list_of_bitwidths_of_miniblocks(nullptr), miniblock_offset(number_of_values_in_a_miniblock), unpacked_data_offset(BitpackingPrimitives::BITPACKING_ALGORITHM_GROUP_SIZE) { - if (!(block_size_in_values % number_of_miniblocks_per_block == 0 && + if (!(block_size_in_values % number_of_miniblocks_per_block == 0 && number_of_values_in_a_miniblock > 0 && number_of_values_in_a_miniblock % BitpackingPrimitives::BITPACKING_ALGORITHM_GROUP_SIZE == 0)) { throw InvalidInputException("Parquet file has invalid block sizes for DELTA_BINARY_PACKED"); } @@ -63,6 +63,9 @@ class DbpDecoder { if (miniblock_offset == number_of_values_in_a_miniblock) { return; } + if (miniblock_offset > number_of_values_in_a_miniblock) { + throw InvalidInputException("Parquet file has invalid miniblocks for DELTA_BINARY_PACKED"); + } auto data = make_unsafe_uniq_array(number_of_values_in_a_miniblock); GetBatchInternal(data_ptr_cast(data.get()), number_of_values_in_a_miniblock - miniblock_offset); } diff --git a/src/duckdb/extension/parquet/include/parquet_dlba_encoder.hpp b/src/duckdb/extension/parquet/include/parquet_dlba_encoder.hpp index 3774ac37e..1061a3f0f 100644 --- a/src/duckdb/extension/parquet/include/parquet_dlba_encoder.hpp +++ b/src/duckdb/extension/parquet/include/parquet_dlba_encoder.hpp @@ -15,8 +15,8 @@ namespace duckdb { class DlbaEncoder { public: - DlbaEncoder(const idx_t total_value_count_p, const idx_t total_string_size_p) - : dbp_encoder(total_value_count_p), total_string_size(total_string_size_p) { + DlbaEncoder(const idx_t total_value_count_p, const idx_t string_buffer_size_p) + : dbp_encoder(total_value_count_p), string_buffer_size(string_buffer_size_p) { } public: @@ -35,21 +35,16 @@ class DlbaEncoder { writer.WriteData(buffer.get(), stream->GetPosition()); } - template - static idx_t GetStringSize(const SRC &) { - return 0; - } - private: DbpEncoder dbp_encoder; - const idx_t total_string_size; + const idx_t string_buffer_size; AllocatedData buffer; unsafe_unique_ptr stream; }; template <> inline void DlbaEncoder::BeginWrite(Allocator &allocator, WriteStream &writer, const string_t &first_value) { - buffer = allocator.Allocate(total_string_size + 1); + buffer = allocator.Allocate(string_buffer_size + 1); stream = make_unsafe_uniq(buffer.get(), buffer.GetSize()); dbp_encoder.BeginWrite(writer, UnsafeNumericCast(first_value.GetSize())); stream->WriteData(const_data_ptr_cast(first_value.GetData()), first_value.GetSize()); @@ -61,9 +56,4 @@ inline void DlbaEncoder::WriteValue(WriteStream &writer, const string_t &value) stream->WriteData(const_data_ptr_cast(value.GetData()), value.GetSize()); } -template <> -inline idx_t DlbaEncoder::GetStringSize(const string_t &src_value) { - return src_value.GetSize(); -} - } // namespace duckdb diff --git a/src/duckdb/extension/parquet/include/parquet_int96.hpp b/src/duckdb/extension/parquet/include/parquet_int96.hpp new file mode 100644 index 000000000..0db7c0b4f --- /dev/null +++ b/src/duckdb/extension/parquet/include/parquet_int96.hpp @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// parquet_int96.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb.hpp" + +namespace duckdb { + +class ClientContext; +class Expression; + +//! Builds an expression that converts a raw 12-byte INT96 value (read as BLOB) into +//! STRUCT(date DATE, time TIME_NS). Used for the int96_as='struct' read option. +unique_ptr CreateInt96AsStructExpression(ClientContext &context); + +//! Builds an expression that returns the child at child_index of the struct conversion +//! (0 = date, 1 = time). Used for pushdown struct extracts on the int96_as='struct' column. +unique_ptr CreateInt96AsStructChildExpression(ClientContext &context, idx_t child_index); + +} // namespace duckdb diff --git a/src/duckdb/extension/parquet/include/parquet_multi_file_info.hpp b/src/duckdb/extension/parquet/include/parquet_multi_file_info.hpp index 2adbdcfd1..aebbb08bc 100644 --- a/src/duckdb/extension/parquet/include/parquet_multi_file_info.hpp +++ b/src/duckdb/extension/parquet/include/parquet_multi_file_info.hpp @@ -32,6 +32,7 @@ #include "duckdb/storage/statistics/node_statistics.hpp" namespace duckdb { +struct TableFunctionMultiFileSettings; class ClientContext; class ExecutionContext; enum class FileExpandResult : uint8_t; @@ -50,6 +51,24 @@ class ParquetFileReaderOptions : public BaseFileReaderOptions { ParquetOptions options; }; +struct ParquetReadGlobalState : public GlobalTableFunctionState { + explicit ParquetReadGlobalState(optional_ptr op_p) : row_group_index(0), op(op_p) { + } + //! Index of row group within file currently up for scanning + idx_t row_group_index; + //! (Optional) pointer to physical operator performing the scan + optional_ptr op; + //! Row groups read but not yet reported to the profiler + atomic row_groups_scanned_unreported {0}; + //! Total considered, across all scan states + atomic total_row_groups_to_scan {0}; +}; + +struct ParquetReadLocalState : public LocalTableFunctionState { + ParquetReaderScanState scan_state; + idx_t group_index; +}; + struct ParquetMultiFileInfo : MultiFileReaderInterface { static unique_ptr CreateInterface(ClientContext &context); @@ -92,7 +111,19 @@ struct ParquetMultiFileInfo : MultiFileReaderInterface { class ParquetScanFunction { public: - static TableFunctionSet GetFunctionSet(); + //! The single-file parquet reader that the multi-file reader is built on + static TableFunction GetSingleFileFunction(); + //! How the single-file parquet reader is wrapped into the multi-file reader + static TableFunctionMultiFileSettings GetMultiFileSettings(); + //! The named parameters that both of the above accept + static void AddNamedParameters(TableFunction &table_function); + //! The multi-file parquet reader, built by wrapping the single-file reader above + static TableFunction GetMultiFileFunction(Identifier name); + //! Push strlen/octet_length on a column into the multi-file parquet reader of a single file + static bool ProjectionExpressionPushdown(ClientContext &context, + const TableFunctionProjectionExpressionInput &input); + //! The options a file is read with, given the bind data of the single-file reader for it + static const ParquetOptions &GetFileOptions(const FunctionData &file_bind_data); }; } // namespace duckdb diff --git a/src/duckdb/extension/parquet/include/parquet_reader.hpp b/src/duckdb/extension/parquet/include/parquet_reader.hpp index 1cb449813..b7af194e4 100644 --- a/src/duckdb/extension/parquet/include/parquet_reader.hpp +++ b/src/duckdb/extension/parquet/include/parquet_reader.hpp @@ -18,6 +18,7 @@ #include "duckdb.hpp" #include "duckdb/common/enum_util.hpp" #include "duckdb/common/helper.hpp" +#include "duckdb/common/once_flag.hpp" #include "duckdb/storage/external_file_cache/caching_file_system.hpp" #include "duckdb/common/common.hpp" #include "duckdb/common/encryption_functions.hpp" @@ -99,6 +100,8 @@ struct ParquetReaderPrefetchConfig { static constexpr double WHOLE_GROUP_PREFETCH_MINIMUM_SCAN = 0.95; //! How many row groups need to produce at least one surviving row (from filtering) static constexpr double PREFETCH_FILTER_MINIMUM_MATCH_RATIO = 0.9; + //! Before observing matches, only speculate on filter columns that are cheap to read + static constexpr double FIRST_GROUP_PREFETCH_MAXIMUM_FILTER_SCAN = 0.05; }; enum class ParquetPrefetchStrategy : uint8_t { @@ -123,6 +126,21 @@ const char *EnumUtil::ToChars(ParquetPrefetchStra template <> ParquetPrefetchStrategyOption EnumUtil::FromString(const char *value); +//! How INT96 (deprecated Impala timestamp) columns are exposed by the Parquet reader +enum class ParquetInt96AsOption : uint8_t { + TIMESTAMP, //! Read as TIMESTAMP (microsecond precision) - the default + TIMESTAMP_NS, //! Read as TIMESTAMP_NS, preserving the nanosecond part (out-of-range values saturate) + STRUCT //! Read as STRUCT(date DATE, time TIME_NS), preserving the full range and precision +}; + +ParquetInt96AsOption ParquetInt96AsOptionFromString(const string &value); + +template <> +const char *EnumUtil::ToChars(ParquetInt96AsOption value); + +template <> +ParquetInt96AsOption EnumUtil::FromString(const char *value); + template <> const char *EnumUtil::ToChars(StringColumnReader::Utf8ValidationOption value); @@ -247,9 +265,10 @@ struct ParquetReaderScanState { PrefetchCostModelState cost_model_state; }; +//! A column of the "schema" option, as plans serialized before that became an option of every multi-file reader +//! store it - see ParquetOptionsSerialization::legacy_schema struct ParquetColumnDefinition { public: - static vector FromSchemaMap(ClientContext &context, const Value &schema_value); MultiFileColumnDefinition ToMultiFileColumnDefinition() const; bool operator==(const ParquetColumnDefinition &other) const { return field_id == other.field_id && name == other.name && type == other.type && @@ -277,15 +296,14 @@ struct ParquetOptions { bool binary_as_string = false; bool variant_legacy_encoding = false; - bool file_row_number = false; shared_ptr encryption_config; - vector schema; idx_t explicit_cardinality = 0; - bool can_have_nan = false; // if floats or doubles can contain NaN values + bool can_have_nan = false; // if floats or doubles can contain NaN values (ignored if nan_count is present) ParquetPrefetchStrategyOption prefetch_strategy = ParquetPrefetchStrategyOption::AUTO; StringColumnReader::Utf8ValidationOption utf8_validation_option = StringColumnReader::Utf8ValidationOption::STRICT_UTF8; + ParquetInt96AsOption int96_as = ParquetInt96AsOption::TIMESTAMP; }; struct ParquetOptionsSerialization { @@ -296,6 +314,10 @@ struct ParquetOptionsSerialization { ParquetOptions parquet_options; MultiFileOptions file_options; + //! Only read from plans serialized before "file_row_number" and "schema" became options of every multi-file + //! reader - they are then moved into the file options + bool legacy_file_row_number = false; + vector legacy_schema; public: void Serialize(Serializer &serializer) const; @@ -352,7 +374,7 @@ class ParquetReader : public BaseFileReader { shared_ptr encryption_util; bool can_use_metadata_statistics = false; //! How many rows have been read from this file - atomic rows_read; + atomic rows_read {0}; ParquetIntervalBloomFilterVersion interval_bloom_filter_version {}; //! Storage indices of columns where expressions like strlen/octet_length are pushed down unordered_map projection_expressions; @@ -411,6 +433,12 @@ class ParquetReader : public BaseFileReader { const Identifier &name); static unique_ptr ReadStatistics(ClientContext &context, const ParquetUnionData &union_data, const Identifier &name); + //! The statistics of a virtual column of a file, read from its metadata + static unique_ptr ReadVirtualColumnStatistics(ClientContext &context, + const ParquetOptions &parquet_options, + const shared_ptr &metadata, + column_t virtual_column_id); + unique_ptr GetVirtualColumnStatistics(ClientContext &context, column_t virtual_column_id) override; LogicalType DeriveLogicalType(const SchemaElement &s_ele, ParquetColumnSchema &schema) const; static LogicalType DeriveLogicalType(const SchemaElement &s_ele, const ParquetOptions &options, @@ -419,6 +447,10 @@ class ParquetReader : public BaseFileReader { void AddVirtualColumn(column_t virtual_column_id) override; void GetPartitionStats(vector &result); + //! Construct a reader over the metadata of a file without opening the file - it can describe the schema and the + //! statistics of the file, but not read it + static shared_ptr CreateMetadataReader(ClientContext &context, ParquetOptions parquet_options, + shared_ptr metadata); static void GetPartitionStats(const duckdb_parquet::FileMetaData &metadata, vector &result, optional_ptr root_schema = nullptr, optional_ptr parquet_options = nullptr); @@ -443,6 +475,9 @@ class ParquetReader : public BaseFileReader { idx_t GetGroupOffset(ParquetReaderScanState &state); //! Group span is the distance between the min page offset and the max page offset plus the max page compressed size uint64_t GetGroupSpan(ParquetReaderScanState &state); + FilterPropagateResult CheckRowGroupFilterStatistics(ClientContext &context, ParquetReaderScanState &state, + ColumnReader &column_reader, const TableFilter &filter); + bool DynamicFiltersExcludeRowGroup(ClientContext &context, ParquetReaderScanState &state); void PrepareRowGroupBuffer(ClientContext &context, ParquetReaderScanState &state, idx_t out_col_idx); //! Whole-group prefetch strategy. ParquetPrefetchStrategy WholeGroupPrefetch(ParquetReaderScanState &state, ThriftFileTransport &trans, @@ -483,12 +518,12 @@ class ParquetReader : public BaseFileReader { optional_ptr GetVariantStats(const ParquetColumnSchema &schema) const; //! Open a file handle for scanning, resolving the open flags from the prefetch mode unique_ptr OpenScanHandle(ClientContext &context) const; + //! Initialize the shared scan handle, including when pre-opening it for read-ahead + void InitializeScanHandle(ClientContext &context) const; private: unique_ptr file_handle; - //! Scan handle pre-opened by PrepareReadAhead while the file was opened, adopted by the first InitializeScan - mutable mutex prewarm_lock; - mutable unique_ptr prewarmed_scan_handle; + mutable OnceFlag scan_handle_initialized; //! Scan handle shared by all scan states of this reader mutable shared_ptr shared_scan_handle; mutable annotated_mutex variant_stats_lock; diff --git a/src/duckdb/extension/parquet/include/parquet_statistics.hpp b/src/duckdb/extension/parquet/include/parquet_statistics.hpp index 993a0447f..d5ab0f5c8 100644 --- a/src/duckdb/extension/parquet/include/parquet_statistics.hpp +++ b/src/duckdb/extension/parquet/include/parquet_statistics.hpp @@ -64,6 +64,9 @@ struct ParquetStatisticsUtils { const duckdb_parquet::Statistics &parquet_stats, bool can_have_nan, optional_ptr column_chunk = nullptr); + //! Whether the column can contain NaN values - uses nan_count if present, otherwise the can_have_nan option + static bool CanHaveNaN(const duckdb_parquet::Statistics &parquet_stats, bool can_have_nan); + static Value ConvertValue(const LogicalType &type, const ParquetColumnSchema &schema_ele, const std::string &stats); static ParquetIntervalBloomFilterVersion diff --git a/src/duckdb/extension/parquet/include/parquet_timestamp.hpp b/src/duckdb/extension/parquet/include/parquet_timestamp.hpp index bc6067807..d99cec505 100644 --- a/src/duckdb/extension/parquet/include/parquet_timestamp.hpp +++ b/src/duckdb/extension/parquet/include/parquet_timestamp.hpp @@ -23,23 +23,23 @@ struct Int96 { timestamp_t ImpalaTimestampToTimestamp(const Int96 &raw_ts); timestamp_ns_t ImpalaTimestampToTimestampNS(const Int96 &raw_ts); +date_t ImpalaTimestampToDate(const Int96 &raw_ts); +dtime_ns_t ImpalaTimestampToTimeNs(const Int96 &raw_ts); Int96 TimestampToImpalaTimestamp(timestamp_t &ts); timestamp_t ParquetTimestampMicrosToTimestamp(const int64_t &raw_ts); timestamp_t ParquetTimestampMsToTimestamp(const int64_t &raw_ts); -timestamp_t ParquetTimestampNsToTimestamp(const int64_t &raw_ts); timestamp_ns_t ParquetTimestampMsToTimestampNs(const int64_t &raw_ms); timestamp_ns_t ParquetTimestampUsToTimestampNs(const int64_t &raw_us); timestamp_ns_t ParquetTimestampNsToTimestampNs(const int64_t &raw_ns); date_t ParquetIntToDate(const int32_t &raw_date); +//! Whether a raw TIME value is within a day, given the number of units in a day +bool ParquetTimeIsValid(const int64_t &raw, const int64_t day); dtime_t ParquetMsIntToTime(const int32_t &raw_millis); dtime_t ParquetIntToTime(const int64_t &raw_micros); -dtime_t ParquetNsIntToTime(const int64_t &raw_nanos); -dtime_ns_t ParquetMsIntToTimeNs(const int32_t &raw_millis); -dtime_ns_t ParquetUsIntToTimeNs(const int64_t &raw_micros); dtime_ns_t ParquetIntToTimeNs(const int64_t &raw_nanos); dtime_tz_t ParquetIntToTimeMsTZ(const int32_t &raw_millis); diff --git a/src/duckdb/extension/parquet/include/parquet_writer.hpp b/src/duckdb/extension/parquet/include/parquet_writer.hpp index 2b81d8303..a151f7bf0 100644 --- a/src/duckdb/extension/parquet/include/parquet_writer.hpp +++ b/src/duckdb/extension/parquet/include/parquet_writer.hpp @@ -283,6 +283,7 @@ class ParquetWriter { void SetWrittenStatistics(CopyFunctionFileStatistics &written_stats); void FlushColumnStats(idx_t col_idx, duckdb_parquet::ColumnChunk &chunk, optional_ptr writer_stats); + void FlushNestedColumnStats(idx_t schema_idx, idx_t null_count, idx_t num_values); void InitializePreprocessing(unique_ptr &transform_data); void InitializeSchemaElements(); diff --git a/src/duckdb/extension/parquet/include/reader/expression_column_reader.hpp b/src/duckdb/extension/parquet/include/reader/expression_column_reader.hpp index 48d2ae02b..f53fb1647 100644 --- a/src/duckdb/extension/parquet/include/reader/expression_column_reader.hpp +++ b/src/duckdb/extension/parquet/include/reader/expression_column_reader.hpp @@ -69,12 +69,10 @@ class ExpressionColumnReader : public ColumnReader { void Skip(idx_t num_values) override; idx_t GroupRowsAvailable() override; - uint64_t TotalCompressedSize() override { - idx_t total_compressed_size = 0; + void GatherColumnChunks(unordered_set &column_chunks) override { for (auto &child_reader : child_readers) { - total_compressed_size += child_reader->TotalCompressedSize(); + child_reader->GatherColumnChunks(column_chunks); } - return total_compressed_size; } idx_t FileOffset() const override { diff --git a/src/duckdb/extension/parquet/include/reader/list_column_reader.hpp b/src/duckdb/extension/parquet/include/reader/list_column_reader.hpp index 0c7a5216d..66ffee086 100644 --- a/src/duckdb/extension/parquet/include/reader/list_column_reader.hpp +++ b/src/duckdb/extension/parquet/include/reader/list_column_reader.hpp @@ -35,8 +35,8 @@ class ListColumnReader : public ColumnReader { return child_column_reader->GroupRowsAvailable() + overflow_child_count; } - uint64_t TotalCompressedSize() override { - return child_column_reader->TotalCompressedSize(); + void GatherColumnChunks(unordered_set &column_chunks) override { + child_column_reader->GatherColumnChunks(column_chunks); } void RegisterPrefetch(ThriftFileTransport &transport, bool allow_merge) override { diff --git a/src/duckdb/extension/parquet/include/reader/row_number_column_reader.hpp b/src/duckdb/extension/parquet/include/reader/row_number_column_reader.hpp index 5fd5e9ed0..616a7fd48 100644 --- a/src/duckdb/extension/parquet/include/reader/row_number_column_reader.hpp +++ b/src/duckdb/extension/parquet/include/reader/row_number_column_reader.hpp @@ -59,8 +59,7 @@ class RowNumberColumnReader : public ColumnReader { idx_t GroupRowsAvailable() override { return NumericLimits::Maximum(); }; - uint64_t TotalCompressedSize() override { - return 0; + void GatherColumnChunks(unordered_set &column_chunks) override { } idx_t FileOffset() const override { return 0; @@ -91,8 +90,7 @@ class RowGroupColumnReader : public ColumnReader { idx_t GroupRowsAvailable() override { return NumericLimits::Maximum(); }; - uint64_t TotalCompressedSize() override { - return 0; + void GatherColumnChunks(unordered_set &column_chunks) override { } idx_t FileOffset() const override { return 0; diff --git a/src/duckdb/extension/parquet/include/reader/struct_column_reader.hpp b/src/duckdb/extension/parquet/include/reader/struct_column_reader.hpp index 0602aca67..cac63ecec 100644 --- a/src/duckdb/extension/parquet/include/reader/struct_column_reader.hpp +++ b/src/duckdb/extension/parquet/include/reader/struct_column_reader.hpp @@ -33,7 +33,7 @@ class StructColumnReader : public ColumnReader { void Skip(idx_t num_values) override; idx_t GroupRowsAvailable() override; - uint64_t TotalCompressedSize() override; + void GatherColumnChunks(unordered_set &column_chunks) override; void RegisterPrefetch(ThriftFileTransport &transport, bool allow_merge) override; }; diff --git a/src/duckdb/extension/parquet/include/reader/variant_column_reader.hpp b/src/duckdb/extension/parquet/include/reader/variant_column_reader.hpp index 19f0d94b4..b4037b9cb 100644 --- a/src/duckdb/extension/parquet/include/reader/variant_column_reader.hpp +++ b/src/duckdb/extension/parquet/include/reader/variant_column_reader.hpp @@ -45,7 +45,7 @@ class VariantColumnReader : public ColumnReader { idx_t GroupRowsAvailable() override; void Convert(Vector &metadata, Vector &group, Vector &result, idx_t count); void PrepareChunk(DataChunk &chunk, idx_t &capacity, const vector &types, idx_t count); - uint64_t TotalCompressedSize() override; + void GatherColumnChunks(unordered_set &column_chunks) override; void RegisterPrefetch(ThriftFileTransport &transport, bool allow_merge) override; static bool TypedValueLayoutToType(const LogicalType &typed_value, LogicalType &logical_type); diff --git a/src/duckdb/extension/parquet/include/thrift_tools.hpp b/src/duckdb/extension/parquet/include/thrift_tools.hpp index 942aee22f..9985ee65b 100644 --- a/src/duckdb/extension/parquet/include/thrift_tools.hpp +++ b/src/duckdb/extension/parquet/include/thrift_tools.hpp @@ -136,19 +136,28 @@ struct ReadAheadBuffer { } // Returns the relevant read head - ReadHead *GetReadHead(idx_t pos) { + ReadHead *GetReadHead(idx_t pos, idx_t len = 1) { + optional_ptr unfetched_head; for (auto &read_head : read_heads) { - if (pos >= read_head->location && pos < read_head->GetEnd()) { - return read_head.get(); + if (pos >= read_head->location && pos < read_head->GetEnd() && len <= read_head->GetEnd() - pos) { + // Filter and payload ranges can overlap: reuse fetched bytes before fetching another range. + if (read_head->data_isset) { + return read_head.get(); + } + if (!unfetched_head) { + unfetched_head = read_head.get(); + } } } - return nullptr; + return unfetched_head.get(); } // Prefetch all read heads void Prefetch() { for (auto &read_head : read_heads) { - read_head->Fetch(file_handle); + if (!read_head->data_isset) { + read_head->Fetch(file_handle); + } } } }; @@ -173,7 +182,7 @@ class ThriftFileTransport : public duckdb_apache::thrift::transport::TVirtualTra } uint32_t read(uint8_t *buf, uint32_t len) { - auto prefetch_buffer = ra_buffer.GetReadHead(location); + auto prefetch_buffer = ra_buffer.GetReadHead(location, len); if (prefetch_buffer != nullptr && location - prefetch_buffer->location + len <= prefetch_buffer->size) { D_ASSERT(location - prefetch_buffer->location + len <= prefetch_buffer->size); @@ -189,7 +198,7 @@ class ThriftFileTransport : public duckdb_apache::thrift::transport::TVirtualTra return len; } Prefetch(location, MinValue(PREFETCH_FALLBACK_BUFFERSIZE, file_size - location)); - auto prefetch_buffer_fallback = ra_buffer.GetReadHead(location); + auto prefetch_buffer_fallback = ra_buffer.GetReadHead(location, len); if (!prefetch_buffer_fallback || location - prefetch_buffer_fallback->location + len > prefetch_buffer_fallback->size) { file_handle.GetFileHandle()->Read(context, buf, len, location); diff --git a/src/duckdb/extension/parquet/include/writer/list_column_writer.hpp b/src/duckdb/extension/parquet/include/writer/list_column_writer.hpp index c4db2c097..27ed70e15 100644 --- a/src/duckdb/extension/parquet/include/writer/list_column_writer.hpp +++ b/src/duckdb/extension/parquet/include/writer/list_column_writer.hpp @@ -22,6 +22,10 @@ class ListColumnWriterState : public ColumnWriterState { idx_t col_idx; unique_ptr child_state; idx_t parent_index = 0; + //! Null containers, excluding absent parents and empty containers + idx_t own_null_count = 0; + //! Containers with a present parent, including null and empty containers + idx_t num_values = 0; }; class ListColumnWriter : public ColumnWriter { diff --git a/src/duckdb/extension/parquet/include/writer/parquet_write_operators.hpp b/src/duckdb/extension/parquet/include/writer/parquet_write_operators.hpp index fe6e196dd..73fee68ba 100644 --- a/src/duckdb/extension/parquet/include/writer/parquet_write_operators.hpp +++ b/src/duckdb/extension/parquet/include/writer/parquet_write_operators.hpp @@ -96,14 +96,14 @@ struct FloatingPointOperator : public BaseParquetOperator { static void HandleStats(ColumnWriterStatistics *stats, TGT target_value) { auto &numeric_stats = stats->Cast>(); if (Value::IsNan(target_value)) { - numeric_stats.has_nan = true; - } else { - if (LessThan::Operation(target_value, numeric_stats.min)) { - numeric_stats.min = target_value; - } - if (GreaterThan::Operation(target_value, numeric_stats.max)) { - numeric_stats.max = target_value; - } + numeric_stats.nan_count++; + return; + } + if (LessThan::Operation(target_value, numeric_stats.min)) { + numeric_stats.min = target_value; + } + if (GreaterThan::Operation(target_value, numeric_stats.max)) { + numeric_stats.max = target_value; } } }; diff --git a/src/duckdb/extension/parquet/include/writer/parquet_write_stats.hpp b/src/duckdb/extension/parquet/include/writer/parquet_write_stats.hpp index dc42ce211..5df142dbf 100644 --- a/src/duckdb/extension/parquet/include/writer/parquet_write_stats.hpp +++ b/src/duckdb/extension/parquet/include/writer/parquet_write_stats.hpp @@ -24,6 +24,7 @@ class ColumnWriterStatistics { virtual string GetMaxValue(); virtual bool CanHaveNaN(); virtual bool HasNaN(); + virtual idx_t GetNaNCount(); virtual bool MinIsExact(); virtual bool MaxIsExact(); @@ -75,14 +76,17 @@ class NumericStatisticsState : public ColumnWriterStatistics { template class FloatingPointStatisticsState : public NumericStatisticsState { public: - bool has_nan = false; + idx_t nan_count = 0; public: bool CanHaveNaN() override { return true; } bool HasNaN() override { - return has_nan; + return nan_count != 0; + } + idx_t GetNaNCount() override { + return nan_count; } }; diff --git a/src/duckdb/extension/parquet/include/writer/templated_column_writer.hpp b/src/duckdb/extension/parquet/include/writer/templated_column_writer.hpp index 45094d880..e75c53e7b 100644 --- a/src/duckdb/extension/parquet/include/writer/templated_column_writer.hpp +++ b/src/duckdb/extension/parquet/include/writer/templated_column_writer.hpp @@ -78,9 +78,6 @@ class StandardColumnWriterState : public PrimitiveColumnWriterState { } ~StandardColumnWriterState() override = default; - // analysis state for integer values for DELTA_BINARY_PACKED/DELTA_LENGTH_BYTE_ARRAY - idx_t total_value_count = 0; - idx_t total_string_size = 0; uint32_t key_bit_width = 0; PrimitiveDictionary dictionary; @@ -90,11 +87,11 @@ class StandardColumnWriterState : public PrimitiveColumnWriterState { template class StandardWriterPageState : public ColumnWriterPageState { public: - explicit StandardWriterPageState(const idx_t total_value_count, const idx_t total_string_size, + explicit StandardWriterPageState(const idx_t total_value_count, const idx_t estimated_page_size, duckdb_parquet::Encoding::type encoding_p, const PrimitiveDictionary &dictionary_p) : encoding(encoding_p), dbp_initialized(false), dbp_encoder(total_value_count), dlba_initialized(false), - dlba_encoder(total_value_count, total_string_size), bss_initialized(false), + dlba_encoder(total_value_count, estimated_page_size), bss_initialized(false), bss_encoder(total_value_count, sizeof(TGT)), dictionary(dictionary_p), dict_written_value(false), dict_bit_width(RleBpDecoder::ComputeBitWidthFromValueCount(dictionary.GetSize())), dict_encoder(dict_bit_width) { @@ -124,6 +121,10 @@ class StandardColumnWriter : public PrimitiveColumnWriter { } ~StandardColumnWriter() override = default; + //! Dictionary encoding normally updates the statistics once per distinct dictionary value. + //! Floating point statistics include nan_count, which counts every NaN value written, so they are updated per row. + static constexpr bool STATS_PER_OCCURRENCE = std::is_same::value; + public: unique_ptr InitializeWriteState(duckdb_parquet::RowGroup &row_group) override { auto result = make_uniq>(writer, row_group, row_group.columns.size()); @@ -137,8 +138,9 @@ class StandardColumnWriter : public PrimitiveColumnWriter { idx_t page_idx) override { auto &state = state_p.Cast>(); const auto &page_info = state_p.page_info[page_idx]; + // The prepared page size bounds the string payload for DELTA_LENGTH_BYTE_ARRAY. auto result = make_uniq>( - page_info.row_count - (page_info.empty_count + page_info.null_count), state.total_string_size, + page_info.row_count - (page_info.empty_count + page_info.null_count), page_info.estimated_page_size, state.encoding, state.dictionary); return std::move(result); } @@ -213,8 +215,6 @@ class StandardColumnWriter : public PrimitiveColumnWriter { for (; vector_index < vcount; vector_index++) { const auto &src_value = data_ptr[vector_index]; state.dictionary.template Insert(src_value); - state.total_value_count++; - state.total_string_size += DlbaEncoder::GetStringSize(src_value); } } else { for (idx_t i = 0; i < vcount; i++) { @@ -224,8 +224,6 @@ class StandardColumnWriter : public PrimitiveColumnWriter { if (validity.RowIsValid(vector_index)) { const auto &src_value = data_ptr[vector_index]; state.dictionary.template Insert(src_value); - state.total_value_count++; - state.total_string_size += DlbaEncoder::GetStringSize(src_value); } vector_index++; } @@ -302,8 +300,10 @@ class StandardColumnWriter : public PrimitiveColumnWriter { } state.dictionary.IterateValues([&](const SRC &src_value, const TGT &tgt_value) { - // update the statistics - OP::template HandleStats(stats, tgt_value); + if (!STATS_PER_OCCURRENCE) { + // update the statistics + OP::template HandleStats(stats, tgt_value); + } if (state.bloom_filter) { // update the bloom filter auto hash = OP::template XXHash64(tgt_value); @@ -366,6 +366,9 @@ class StandardColumnWriter : public PrimitiveColumnWriter { continue; } const auto &src_value = data_ptr[r]; + if (STATS_PER_OCCURRENCE) { + OP::template HandleStats(stats, OP::template Operation(src_value)); + } const auto value_index = page_state.dictionary.GetIndex(src_value); page_state.dict_encoder.WriteValue(temp_writer, value_index); } diff --git a/src/duckdb/extension/parquet/include/writer/variant_column_writer.hpp b/src/duckdb/extension/parquet/include/writer/variant_column_writer.hpp index 16d66bc7e..ac57cf145 100644 --- a/src/duckdb/extension/parquet/include/writer/variant_column_writer.hpp +++ b/src/duckdb/extension/parquet/include/writer/variant_column_writer.hpp @@ -72,8 +72,9 @@ struct VariantAnalyzeSchemaState : public ParquetAnalyzeSchemaState { class VariantColumnWriter : public StructColumnWriter { public: VariantColumnWriter(ParquetWriter &writer, ParquetColumnSchema &&column_schema, vector schema_path_p, - vector> child_writers_p) - : StructColumnWriter(writer, std::move(column_schema), std::move(schema_path_p), std::move(child_writers_p)) { + vector> child_writers_p, bool auto_shred) + : StructColumnWriter(writer, std::move(column_schema), std::move(schema_path_p), std::move(child_writers_p)), + is_analyzed(!auto_shred) { } ~VariantColumnWriter() override = default; @@ -100,18 +101,21 @@ class VariantColumnWriter : public StructColumnWriter { vector> arguments; arguments.push_back(unique_ptr_cast(std::move(expr))); - BoundScalarFunction bound_func(GetTransformFunction()); + BoundScalarFunction bound_func(GetTransformFunction(true)); bound_func.SetReturnType(TransformedType()); return make_uniq(std::move(bound_func), std::move(arguments), nullptr); } public: - static ScalarFunction GetTransformFunction(); + static ScalarFunction GetTransformFunction(bool preserve_nulls = false); + //! 'variant_bytes_to_variant': decode a binary Variant value (metadata followed by value) + //! into a VARIANT. The inverse of 'variant_to_parquet_variant'. + static ScalarFunction GetBytesToVariantFunction(); static LogicalType TransformTypedValueRecursive(const LogicalType &type); private: - //! Whether the schema of the variant has been analyzed already + //! Whether the schema has been analyzed or explicitly specified bool is_analyzed = false; ShreddingType analyzed_shredding_type; }; diff --git a/src/duckdb/extension/parquet/parquet_crypto.cpp b/src/duckdb/extension/parquet/parquet_crypto.cpp index db6aa3a93..aaff30172 100644 --- a/src/duckdb/extension/parquet/parquet_crypto.cpp +++ b/src/duckdb/extension/parquet/parquet_crypto.cpp @@ -553,6 +553,9 @@ static string Base64Decode(const string &key) { } void ParquetCrypto::AddKey(ClientContext &context, const FunctionParameters ¶meters) { + if (parameters.values[0].IsNull() || parameters.values[1].IsNull()) { + throw InvalidInputException("add_parquet_key: key name and key cannot be NULL"); + } const auto &key_name = StringValue::Get(parameters.values[0]); const auto &key = StringValue::Get(parameters.values[1]); diff --git a/src/duckdb/extension/parquet/parquet_extension.cpp b/src/duckdb/extension/parquet/parquet_extension.cpp index 81f240f6d..fc01e0def 100644 --- a/src/duckdb/extension/parquet/parquet_extension.cpp +++ b/src/duckdb/extension/parquet/parquet_extension.cpp @@ -42,6 +42,7 @@ #include "duckdb/parser/tableref/table_function_ref.hpp" #include "duckdb/planner/expression/bound_cast_expression.hpp" #include "duckdb/common/multi_file/multi_file_function.hpp" +#include "duckdb/common/multi_file/table_function_multi_file.hpp" #include "parquet_multi_file_info.hpp" #include "column_reader.hpp" #include "duckdb/common/assert.hpp" @@ -78,7 +79,7 @@ #include "duckdb/storage/storage_info.hpp" #include "parquet_field_id.hpp" #include "parquet_types.h" -#include "reader/variant/parquet_variant_iterator.hpp" +#include "duckdb/common/types/variant/parquet_variant_iterator.hpp" namespace duckdb { class ClientContext; @@ -159,6 +160,7 @@ static void ParquetListCopyOptions(ClientContext &context, CopyOptionsInput &inp copy_options["binary_as_string"] = CopyOption(LogicalType::BOOLEAN, CopyOptionMode::READ_ONLY); copy_options["file_row_number"] = CopyOption(LogicalType::BOOLEAN, CopyOptionMode::READ_ONLY); copy_options["can_have_nan"] = CopyOption(LogicalType::BOOLEAN, CopyOptionMode::READ_ONLY); + copy_options["int96_as"] = CopyOption(LogicalType::VARCHAR, CopyOptionMode::READ_ONLY); copy_options["geoparquet_version"] = CopyOption(LogicalType::VARCHAR, CopyOptionMode::WRITE_ONLY); copy_options["shredding"] = CopyOption(LogicalType::ANY, CopyOptionMode::WRITE_ONLY); copy_options["write_timestamp_as_int96"] = CopyOption(LogicalType::BOOLEAN, CopyOptionMode::WRITE_ONLY); @@ -281,6 +283,9 @@ static unique_ptr ParquetWriteBind(ClientContext &context, CopyFun for (idx_t i = 0; i < values.size(); i++) { auto &value = values[i]; auto key = StructType::GetChildName(kv_struct_type, i); + if (value.IsNull()) { + throw BinderException("kv_metadata value for key %s cannot be NULL", key); + } // If the value is a blob, write the raw blob bytes // otherwise, cast to string if (value.type().id() == LogicalTypeId::BLOB) { @@ -320,8 +325,8 @@ static unique_ptr ParquetWriteBind(ClientContext &context, CopyFun bind_data->enable_bloom_filters = BooleanValue::Get(option_values[0].DefaultCastAs(LogicalType::BOOLEAN)); } else if (option_name == "bloom_filter_false_positive_ratio") { auto val = option_values[0].GetValue(); - if (val <= 0) { - throw BinderException("bloom_filter_false_positive_ratio must be greater than 0"); + if (!(val > 0 && val <= 1)) { + throw BinderException("bloom_filter_false_positive_ratio must be greater than 0 and at most 1"); } bind_data->bloom_filter_false_positive_ratio = val; } else if (option_name == "compression_level") { @@ -607,6 +612,34 @@ ParquetPrefetchStrategyOption EnumUtil::FromString +const char *EnumUtil::ToChars(ParquetInt96AsOption value) { + switch (value) { + case ParquetInt96AsOption::TIMESTAMP: + return "TIMESTAMP"; + case ParquetInt96AsOption::TIMESTAMP_NS: + return "TIMESTAMP_NS"; + case ParquetInt96AsOption::STRUCT: + return "STRUCT"; + default: + throw NotImplementedException(StringUtil::Format("Enum value: '%s' not implemented", value)); + } +} + +template <> +ParquetInt96AsOption EnumUtil::FromString(const char *value) { + if (StringUtil::Equals(value, "TIMESTAMP")) { + return ParquetInt96AsOption::TIMESTAMP; + } + if (StringUtil::Equals(value, "TIMESTAMP_NS")) { + return ParquetInt96AsOption::TIMESTAMP_NS; + } + if (StringUtil::Equals(value, "STRUCT")) { + return ParquetInt96AsOption::STRUCT; + } + throw NotImplementedException(StringUtil::Format("Enum value: '%s' not implemented", value)); +} + template <> const char * EnumUtil::ToChars(StringColumnReader::Utf8ValidationOption value) { @@ -1001,17 +1034,36 @@ static vector> ParquetWriteSelect(CopyToSelectInput &inpu return {}; } +//! Bind a COPY ... FROM a parquet file - the reader is the same one read_parquet is built on +static unique_ptr ParquetCopyFromBind(ClientContext &context, CopyFromFunctionBindInput &input, + vector &expected_names, + vector &expected_types) { + // write options (e.g. written by EXPORT DATABASE) have no effect on reading - the codec is read from the file + auto info = input.info.Copy(); + info->options.erase("codec"); + info->options.erase("row_group_size"); + CopyFromFunctionBindInput read_input(*info, input.tf); + return TableFunctionMultiFileWrapper::MultiFileBindCopyWith(context, read_input, expected_names, expected_types, + ParquetScanFunction::GetSingleFileFunction(), + ParquetScanFunction::GetMultiFileSettings()); +} + static void LoadInternal(ExtensionLoader &loader) { auto &db_instance = loader.GetDatabaseInstance(); auto &fs = db_instance.GetFileSystem(); fs.RegisterCompressionFilesystem(make_uniq()); - auto scan_fun = ParquetScanFunction::GetFunctionSet(); + auto scan_fun = MultiFileReader::CreateFunctionSet(ParquetScanFunction::GetMultiFileFunction("read_parquet")); scan_fun.SetName("read_parquet"); loader.RegisterFunction(scan_fun); scan_fun.SetName("parquet_scan"); loader.RegisterFunction(scan_fun); + // the single-file parquet reader that the multi-file reader above is built on + TableFunctionSet single_file_set("read_single_parquet_file"); + single_file_set.AddFunction(ParquetScanFunction::GetSingleFileFunction()); + loader.RegisterFunction(std::move(single_file_set)); + // parquet_metadata ParquetMetaDataFunction meta_fun; loader.RegisterFunction(MultiFileReader::CreateFunctionSet(meta_fun)); @@ -1040,7 +1092,7 @@ static void LoadInternal(ExtensionLoader &loader) { loader.RegisterFunction(VariantColumnWriter::GetTransformFunction()); // bytes_to_variant - loader.RegisterFunction(ParquetVariantConversion::GetBytesToVariantFunction()); + loader.RegisterFunction(VariantColumnWriter::GetBytesToVariantFunction()); CopyFunction function("parquet"); function.copy_to_select = ParquetWriteSelect; @@ -1055,7 +1107,7 @@ static void LoadInternal(ExtensionLoader &loader) { function.copy_to_finalize = ParquetWriteFinalize; function.execution_mode = ParquetWriteExecutionMode; function.initialize_operator = ParquetWriteInitializeOperator; - function.copy_from_bind = MultiFileFunction::MultiFileBindCopy; + function.copy_from_bind = ParquetCopyFromBind; function.copy_from_function = *scan_fun.functions[0]; function.prepare_batch = ParquetWritePrepareBatch; @@ -1072,7 +1124,9 @@ static void LoadInternal(ExtensionLoader &loader) { // parquet_key auto parquet_key_fun = PragmaFunction::PragmaCall("add_parquet_key", ParquetCrypto::AddKey, - {LogicalType::VARCHAR, LogicalType::VARCHAR}); + FunctionSignature() + .AddPositionalOnly("key_name", LogicalType::VARCHAR) + .AddPositionalOnly("key", LogicalType::VARCHAR)); loader.RegisterFunction(parquet_key_fun); auto &config = DBConfig::GetConfig(db_instance); @@ -1097,6 +1151,7 @@ static void LoadInternal(ExtensionLoader &loader) { LogicalType::BOOLEAN, Value::BOOLEAN(true)); } +// LCOV_EXCL_START void ParquetExtension::Load(ExtensionLoader &loader) { LoadInternal(loader); } @@ -1112,14 +1167,13 @@ std::string ParquetExtension::Version() const { return ""; #endif } +// LCOV_EXCL_STOP } // namespace duckdb -#ifdef DUCKDB_BUILD_LOADABLE_EXTENSION extern "C" { DUCKDB_CPP_EXTENSION_ENTRY(parquet, loader) { // NOLINT duckdb::LoadInternal(loader); } } -#endif diff --git a/src/duckdb/extension/parquet/parquet_field_id.cpp b/src/duckdb/extension/parquet/parquet_field_id.cpp index 927fc32d1..db4435a5b 100644 --- a/src/duckdb/extension/parquet/parquet_field_id.cpp +++ b/src/duckdb/extension/parquet/parquet_field_id.cpp @@ -43,70 +43,15 @@ FieldID FieldID::Copy() const { static case_insensitive_map_t GetChildNameToTypeMap(const LogicalType &type) { case_insensitive_map_t name_to_type_map; - switch (type.id()) { - case LogicalTypeId::LIST: - name_to_type_map.emplace("element", ListType::GetChildType(type)); - break; - case LogicalTypeId::MAP: - name_to_type_map.emplace("key", MapType::KeyType(type)); - name_to_type_map.emplace("value", MapType::ValueType(type)); - break; - case LogicalTypeId::STRUCT: { - for (auto &[name, type] : StructType::GetChildTypes(type)) { - if (name == FieldID::DUCKDB_FIELD_ID) { - throw BinderException("Cannot have column named \"%s\" with FIELD_IDS", FieldID::DUCKDB_FIELD_ID); - } - name_to_type_map.emplace(name.GetIdentifierName(), type); - } - break; - } - case LogicalTypeId::TUPLE: { - for (auto &[name, type] : TupleType::NamedChildren(type)) { - if (name == FieldID::DUCKDB_FIELD_ID) { - throw BinderException("Cannot have column named \"%s\" with FIELD_IDS", FieldID::DUCKDB_FIELD_ID); - } - name_to_type_map.emplace(name.GetIdentifierName(), type); + for (auto &[name, child_type] : LogicalType::GetNamedChildTypes(type)) { + if (name == FieldID::DUCKDB_FIELD_ID) { + throw BinderException("Cannot have column named \"%s\" with FIELD_IDS", FieldID::DUCKDB_FIELD_ID); } - break; + name_to_type_map.emplace(name.GetIdentifierName(), child_type); } - default: // LCOV_EXCL_START - throw InternalException("Unexpected type in GetChildNameToTypeMap"); - } // LCOV_EXCL_STOP return name_to_type_map; } -static void GetChildNamesAndTypes(const LogicalType &type, vector &child_names, - vector &child_types) { - switch (type.id()) { - case LogicalTypeId::LIST: - child_names.emplace_back("element"); - child_types.emplace_back(ListType::GetChildType(type)); - break; - case LogicalTypeId::MAP: - child_names.emplace_back("key"); - child_names.emplace_back("value"); - child_types.emplace_back(MapType::KeyType(type)); - child_types.emplace_back(MapType::ValueType(type)); - break; - case LogicalTypeId::STRUCT: { - for (const auto &[name, type] : StructType::GetChildTypes(type)) { - child_names.emplace_back(name); - child_types.emplace_back(type); - } - break; - } - case LogicalTypeId::TUPLE: { - for (auto &[name, type] : TupleType::NamedChildren(type)) { - child_names.emplace_back(name); - child_types.emplace_back(type); - } - break; - } - default: // LCOV_EXCL_START - throw InternalException("Unexpected type in GetChildNamesAndTypes"); - } // LCOV_EXCL_STOP -} - void FieldID::GenerateFieldIDs(ChildFieldIDs &field_ids, idx_t &field_id, const vector &names, const vector &sql_types) { D_ASSERT(names.size() == sql_types.size()); @@ -121,10 +66,12 @@ void FieldID::GenerateFieldIDs(ChildFieldIDs &field_ids, idx_t &field_id, const continue; } - // Cannot use GetChildNameToTypeMap here because we lose order, and we want to generate depth-first vector child_names; vector child_types; - GetChildNamesAndTypes(col_type, child_names, child_types); + for (auto &[name, child_type] : LogicalType::GetNamedChildTypes(col_type)) { + child_names.push_back(name); + child_types.push_back(child_type); + } GenerateFieldIDs(inserted.first->second.child_field_ids, field_id, child_names, child_types); } } diff --git a/src/duckdb/extension/parquet/parquet_file_metadata_cache.cpp b/src/duckdb/extension/parquet/parquet_file_metadata_cache.cpp index ce6548e58..83b44764b 100644 --- a/src/duckdb/extension/parquet/parquet_file_metadata_cache.cpp +++ b/src/duckdb/extension/parquet/parquet_file_metadata_cache.cpp @@ -104,17 +104,13 @@ ParquetCacheValidity ParquetFileMetadataCache::IsValid(const OpenFileInfo &info, return ParquetCacheValidity::UNKNOWN; } - const auto &open_options = info.extended_info->options; - const auto lm_entry = open_options.find("last_modified"); - if (lm_entry == open_options.end()) { + const auto &extended_info = *info.extended_info; + timestamp_t new_last_modified; + if (!extended_info.TryGetOption("last_modified", new_last_modified)) { return ParquetCacheValidity::UNKNOWN; } - auto new_last_modified = lm_entry->second.GetValue(); string new_etag; - const auto etag_entry = open_options.find("etag"); - if (etag_entry != open_options.end()) { - new_etag = StringValue::Get(etag_entry->second); - } + extended_info.TryGetOption("etag", new_etag); if (ExternalFileCache::IsValid(/*validate=*/true, version_tag, last_modified, new_etag, new_last_modified)) { return ParquetCacheValidity::VALID; diff --git a/src/duckdb/extension/parquet/parquet_geometry.cpp b/src/duckdb/extension/parquet/parquet_geometry.cpp index 63ae814b3..e407aab88 100644 --- a/src/duckdb/extension/parquet/parquet_geometry.cpp +++ b/src/duckdb/extension/parquet/parquet_geometry.cpp @@ -355,15 +355,9 @@ bool GeoParquetFileMetadata::IsGeometryColumn(const string &column_name) const { } bool GeoParquetFileMetadata::IsGeoParquetConversionEnabled(const ClientContext &context) { - Value geoparquet_enabled; - if (!context.TryGetCurrentSetting("enable_geoparquet_conversion", geoparquet_enabled)) { - return false; - } - if (!geoparquet_enabled.GetValue()) { - // Disabled by setting - return false; - } - return true; + bool geoparquet_enabled = false; + context.TryGetCurrentSetting("enable_geoparquet_conversion", geoparquet_enabled); + return geoparquet_enabled; } const unordered_map &GeoParquetFileMetadata::GetColumnMeta() const { diff --git a/src/duckdb/extension/parquet/parquet_int96.cpp b/src/duckdb/extension/parquet/parquet_int96.cpp new file mode 100644 index 000000000..2178ad62e --- /dev/null +++ b/src/duckdb/extension/parquet/parquet_int96.cpp @@ -0,0 +1,66 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// parquet_int96.cpp +// +// +//===----------------------------------------------------------------------===// + +#include "parquet_int96.hpp" + +#include + +#include "duckdb/common/types/date.hpp" +#include "duckdb/common/types/time.hpp" +#include "duckdb/common/vector/vector_iterator.hpp" +#include "duckdb/common/vector/vector_writer.hpp" +#include "duckdb/function/scalar/struct_functions.hpp" +#include "duckdb/function/scalar_function.hpp" +#include "duckdb/planner/expression/bound_constant_expression.hpp" +#include "duckdb/planner/expression/bound_function_expression.hpp" +#include "duckdb/planner/expression/bound_reference_expression.hpp" +#include "parquet_timestamp.hpp" + +namespace duckdb { + +static LogicalType Int96StructType() { + return LogicalType::STRUCT({{"date", LogicalType::DATE}, {"time", LogicalType::TIME_NS}}); +} + +static void Int96AsStructFunction(DataChunk &args, ExpressionState &state, Vector &result) { + auto count = args.size(); + auto blob_values = args.data[0].Values(); + auto writer = FlatVector::Writer>(result, count); + for (auto entry : blob_values) { + if (!entry.IsValid()) { + writer.WriteNull(); + continue; + } + auto blob = entry.GetValue(); + Int96 raw_ts; + memcpy(raw_ts.value, blob.GetData(), sizeof(Int96)); + writer.WriteValue([&](auto &date_writer, auto &time_writer) { + date_writer.WriteValue(ImpalaTimestampToDate(raw_ts)); + time_writer.WriteValue(ImpalaTimestampToTimeNs(raw_ts)); + }); + } +} + +unique_ptr CreateInt96AsStructExpression(ClientContext &context) { + auto args = vector>(); + auto ref = make_uniq_base(LogicalTypeId::BLOB, 0); + args.push_back(std::move(ref)); + ScalarFunction func("__internal_int96_as_struct", vector {LogicalType::BLOB}, Int96StructType(), + Int96AsStructFunction); + return func.Bind(context, std::move(args)); +} + +unique_ptr CreateInt96AsStructChildExpression(ClientContext &context, idx_t child_index) { + auto args = vector>(); + args.push_back(CreateInt96AsStructExpression(context)); + args.push_back( + make_uniq_base(Value::BIGINT(NumericCast(child_index + 1)))); + return StructExtractAtFun::GetFunction().Bind(context, std::move(args)); +} + +} // namespace duckdb diff --git a/src/duckdb/extension/parquet/parquet_metadata.cpp b/src/duckdb/extension/parquet/parquet_metadata.cpp index 7c0dcb4c9..3d8b00473 100644 --- a/src/duckdb/extension/parquet/parquet_metadata.cpp +++ b/src/duckdb/extension/parquet/parquet_metadata.cpp @@ -53,9 +53,6 @@ #include "thrift/protocol/TCompactProtocol.h" #include "thrift_tools.hpp" #include "duckdb/planner/filter/expression_filter.hpp" -#include "duckdb/planner/expression/bound_comparison_expression.hpp" -#include "duckdb/planner/expression/bound_constant_expression.hpp" -#include "duckdb/planner/expression/bound_reference_expression.hpp" namespace duckdb { class ClientContext; @@ -361,6 +358,9 @@ void ParquetMetaDataOperator::BindSchema {"encoding", LogicalType::VARCHAR}, {"count", LogicalType::INTEGER}, }))); + + names.emplace_back("stats_nan_count"); + return_types.emplace_back(LogicalType::BIGINT); } static Value ConvertParquetStats(const LogicalType &type, const ParquetColumnSchema &schema_ele, bool stats_is_set, @@ -556,6 +556,8 @@ void ParquetRowGroupMetadataProcessor::ReadRow(vector> &output // encoding_stats, LogicalType::LIST(LogicalType::STRUCT(...)) output[31].get().Append(ConvertParquetEncodingStats(col_meta)); + // stats_nan_count + output[32].get().Append(ParquetElementBigint(stats.nan_count, stats.__isset.nan_count)); } //===--------------------------------------------------------------------===// @@ -564,8 +566,12 @@ void ParquetRowGroupMetadataProcessor::ReadRow(vector> &output class ParquetSchemaProcessor : public ParquetMetadataFileProcessor { public: + void InitializeInternal(ClientContext &context, ParquetReader &reader) override; idx_t TotalRowCount(ParquetReader &reader) override; void ReadRow(vector> &output, idx_t row_idx, ParquetReader &reader) override; + +private: + vector element_types; }; template <> @@ -666,6 +672,25 @@ static Value ParquetLogicalTypeToString(const duckdb_parquet::LogicalType &type, return Value(); } +static void CollectElementTypes(const ParquetColumnSchema &schema, vector &element_types) { + // a group collapsed into its only child has the type of that child + for (auto &index : {schema.parent_schema_index, schema.schema_index}) { + if (index.IsValid() && index.GetIndex() < element_types.size() && element_types[index.GetIndex()].IsNull()) { + element_types[index.GetIndex()] = Value(schema.type.ToString()); + } + } + for (auto &child : schema.children) { + CollectElementTypes(child, element_types); + } +} + +void ParquetSchemaProcessor::InitializeInternal(ClientContext &context, ParquetReader &reader) { + element_types.assign(reader.GetFileMetadata()->schema.size(), Value()); + for (auto &column : reader.root_schema->children) { + CollectElementTypes(column, element_types); + } +} + idx_t ParquetSchemaProcessor::TotalRowCount(ParquetReader &reader) { return reader.GetFileMetadata()->schema.size(); } @@ -697,12 +722,7 @@ void ParquetSchemaProcessor::ReadRow(vector> &output, idx_t ro // logical_type output[10].get().Append(ParquetLogicalTypeToString(column.logicalType, column.__isset.logicalType)); // duckdb_type - ParquetColumnSchema column_schema; - Value duckdb_type; - if (column.__isset.type) { - duckdb_type = reader.DeriveLogicalType(column, column_schema).ToString(); - } - output[11].get().Append(duckdb_type); + output[11].get().Append(element_types[row_idx]); // column_id output[12].get().Append(Value::BIGINT(UnsafeNumericCast(row_idx))); } @@ -886,11 +906,8 @@ void ParquetBloomProbeProcessor::InitializeInternal(ClientContext &context, Parq protocol = make_uniq>(std::move(transport)); allocator = &BufferAllocator::Get(context); auto column_type = reader.GetColumns()[probe_column_idx.GetIndex()].type; - auto comparison = BoundComparisonExpression::Create( - ExpressionType::COMPARE_EQUAL, - make_uniq(Identifier(probe_column_name), column_type, 0), - make_uniq(probe_constant.CastAs(context, column_type))); - filter = make_uniq(std::move(comparison)); + filter = ExpressionFilter::CreateComparisonFilter(ExpressionType::COMPARE_EQUAL, + probe_constant.CastAs(context, column_type)); auto &column_schema = reader.root_schema->children[probe_column_idx.GetIndex()]; hash_strategy = ParquetStatisticsUtils::GetBloomFilterHashStrategy(column_schema, reader.GetIntervalBloomFilterVersion()); @@ -1174,7 +1191,8 @@ double ParquetMetaDataOperator::Progress(ClientContext &context, const FunctionD } ParquetMetaDataFunction::ParquetMetaDataFunction() - : TableFunction("parquet_metadata", {LogicalType::VARCHAR}, ParquetMetaDataOperator::Function, + : TableFunction("parquet_metadata", FunctionSignature().AddPositionalOnly("path", LogicalType::VARCHAR), + ParquetMetaDataOperator::Function, ParquetMetaDataOperator::Bind, ParquetMetaDataOperator::InitGlobal, ParquetMetaDataOperator::InitLocal) { @@ -1183,7 +1201,8 @@ ParquetMetaDataFunction::ParquetMetaDataFunction() } ParquetSchemaFunction::ParquetSchemaFunction() - : TableFunction("parquet_schema", {LogicalType::VARCHAR}, ParquetMetaDataOperator::Function, + : TableFunction("parquet_schema", FunctionSignature().AddPositionalOnly("path", LogicalType::VARCHAR), + ParquetMetaDataOperator::Function, ParquetMetaDataOperator::Bind, ParquetMetaDataOperator::InitGlobal, ParquetMetaDataOperator::InitLocal) { @@ -1192,7 +1211,8 @@ ParquetSchemaFunction::ParquetSchemaFunction() } ParquetKeyValueMetadataFunction::ParquetKeyValueMetadataFunction() - : TableFunction("parquet_kv_metadata", {LogicalType::VARCHAR}, ParquetMetaDataOperator::Function, + : TableFunction("parquet_kv_metadata", FunctionSignature().AddPositionalOnly("path", LogicalType::VARCHAR), + ParquetMetaDataOperator::Function, ParquetMetaDataOperator::Bind, ParquetMetaDataOperator::InitGlobal, ParquetMetaDataOperator::InitLocal) { @@ -1201,7 +1221,8 @@ ParquetKeyValueMetadataFunction::ParquetKeyValueMetadataFunction() } ParquetFileMetadataFunction::ParquetFileMetadataFunction() - : TableFunction("parquet_file_metadata", {LogicalType::VARCHAR}, ParquetMetaDataOperator::Function, + : TableFunction("parquet_file_metadata", FunctionSignature().AddPositionalOnly("path", LogicalType::VARCHAR), + ParquetMetaDataOperator::Function, ParquetMetaDataOperator::Bind, ParquetMetaDataOperator::InitGlobal, ParquetMetaDataOperator::InitLocal) { @@ -1210,7 +1231,11 @@ ParquetFileMetadataFunction::ParquetFileMetadataFunction() } ParquetBloomProbeFunction::ParquetBloomProbeFunction() - : TableFunction("parquet_bloom_probe", {LogicalType::VARCHAR, LogicalType::VARCHAR, LogicalType::ANY}, + : TableFunction("parquet_bloom_probe", + FunctionSignature() + .AddPositionalOnly("path", LogicalType::VARCHAR) + .AddPositionalOnly("column_name", LogicalType::VARCHAR) + .AddPositionalOnly("value", LogicalType::ANY), ParquetMetaDataOperator::Function, ParquetMetaDataOperator::Bind, ParquetMetaDataOperator::InitGlobal, @@ -1220,7 +1245,8 @@ ParquetBloomProbeFunction::ParquetBloomProbeFunction() } ParquetFullMetadataFunction::ParquetFullMetadataFunction() - : TableFunction("parquet_full_metadata", {LogicalType::VARCHAR}, ParquetMetaDataOperator::Function, + : TableFunction("parquet_full_metadata", FunctionSignature().AddPositionalOnly("path", LogicalType::VARCHAR), + ParquetMetaDataOperator::Function, ParquetMetaDataOperator::Bind, ParquetMetaDataOperator::InitGlobal, ParquetMetaDataOperator::InitLocal) { diff --git a/src/duckdb/extension/parquet/parquet_multi_file_info.cpp b/src/duckdb/extension/parquet/parquet_multi_file_info.cpp index 447d1eb70..3cb60934a 100644 --- a/src/duckdb/extension/parquet/parquet_multi_file_info.cpp +++ b/src/duckdb/extension/parquet/parquet_multi_file_info.cpp @@ -1,4 +1,5 @@ #include "parquet_multi_file_info.hpp" +#include "duckdb/common/multi_file/table_function_multi_file.hpp" #include "duckdb/main/client_context.hpp" #include @@ -88,105 +89,6 @@ struct ParquetReadBindData : public TableFunctionData { bool attempted_to_load_caches = false; }; -struct ParquetReadGlobalState : public GlobalTableFunctionState { - explicit ParquetReadGlobalState(optional_ptr op_p) : row_group_index(0), op(op_p) { - } - //! Index of row group within file currently up for scanning - idx_t row_group_index; - //! (Optional) pointer to physical operator performing the scan - optional_ptr op; - //! Row groups read but not yet reported to the profiler - atomic row_groups_scanned_unreported {0}; - //! Total considered, across all scan states - atomic total_row_groups_to_scan {0}; -}; - -struct ParquetReadLocalState : public LocalTableFunctionState { - ParquetReaderScanState scan_state; - idx_t group_index; -}; - -static void ParseFileRowNumberOption(MultiFileReaderBindData &bind_data, ParquetOptions &options, - vector &return_types, vector &names) { - if (options.file_row_number) { - if (StringUtil::CIFind(names, "file_row_number") != DConstants::INVALID_INDEX) { - throw BinderException( - "Using file_row_number option on file with column named file_row_number is not supported"); - } - - return_types.emplace_back(LogicalType::BIGINT); - names.emplace_back("file_row_number"); - } -} - -static void BindSchema(ClientContext &context, vector &return_types, vector &names, - MultiFileBindData &bind_data) { - auto &parquet_bind = bind_data.bind_data->Cast(); - auto &options = parquet_bind.GetParquetOptions(); - D_ASSERT(!options.schema.empty()); - - auto &file_options = bind_data.file_options; - if (file_options.union_by_name || file_options.hive_partitioning) { - throw BinderException("Parquet schema cannot be combined with union_by_name=true or hive_partitioning=true"); - } - auto &reader_bind = bind_data.reader_bind; - - vector schema_col_names; - vector schema_col_types; - schema_col_names.reserve(options.schema.size()); - schema_col_types.reserve(options.schema.size()); - bool match_by_field_id; - if (!options.schema.empty()) { - auto &column = options.schema[0]; - if (column.identifier.type().id() == LogicalTypeId::INTEGER) { - match_by_field_id = true; - } else { - match_by_field_id = false; - } - } else { - match_by_field_id = false; - } - - for (idx_t i = 0; i < options.schema.size(); i++) { - const auto &column = options.schema[i]; - schema_col_names.push_back(Identifier(column.name)); - schema_col_types.push_back(column.type); - - auto res = column.ToMultiFileColumnDefinition(); -#ifdef DEBUG - if (match_by_field_id) { - D_ASSERT(res.identifier.type().id() == LogicalTypeId::INTEGER); - } else { - D_ASSERT(res.identifier.type().id() == LogicalTypeId::VARCHAR); - } -#endif - - reader_bind.schema.emplace_back(res); - } - ParseFileRowNumberOption(reader_bind, options, return_types, names); - if (options.file_row_number) { - MultiFileColumnDefinition res("file_row_number", LogicalType::BIGINT); - res.identifier = Value::INTEGER(MultiFileReader::ORDINAL_FIELD_ID); - schema_col_names.push_back(Identifier(res.name)); - schema_col_types.push_back(res.type); - reader_bind.schema.emplace_back(res); - } - - if (match_by_field_id) { - reader_bind.mapping = MultiFileColumnMappingMode::BY_FIELD_ID; - } else { - reader_bind.mapping = MultiFileColumnMappingMode::BY_NAME; - } - - // perform the binding on the obtained set of names + types - bind_data.multi_file_reader->BindOptions(file_options, *bind_data.file_list, schema_col_types, schema_col_names, - reader_bind); - - names = schema_col_names; - return_types = schema_col_types; - D_ASSERT(names.size() == return_types.size()); -} - unique_ptr ParquetMultiFileInfo::CreateInterface(ClientContext &context) { return make_uniq(); } @@ -194,14 +96,8 @@ unique_ptr ParquetMultiFileInfo::CreateInterface(Clien void ParquetMultiFileInfo::BindReader(ClientContext &context, vector &return_types, vector &names, MultiFileBindData &bind_data) { auto &parquet_bind = bind_data.bind_data->Cast(); - auto &options = parquet_bind.GetParquetOptions(); - if (!options.schema.empty()) { - BindSchema(context, return_types, names, bind_data); - } else { - bind_data.reader_bind = - bind_data.multi_file_reader->BindReader(context, return_types, names, *bind_data.file_list, bind_data, - *parquet_bind.options, bind_data.file_options); - } + bind_data.reader_bind = bind_data.multi_file_reader->BindReader( + context, return_types, names, *bind_data.file_list, bind_data, *parquet_bind.options, bind_data.file_options); } static bool GetBooleanArgument(const Identifier &key, const vector &option_values) { @@ -226,89 +122,7 @@ static bool ParquetScanSupportPushdownExtract(const FunctionData &bind_data_p, c auto &column = bind_data.columns[col_idx.index]; auto &column_type = column.type; - return column_type.id() == LogicalTypeId::STRUCT || column_type.id() == LogicalTypeId::VARIANT; -} - -static void ParquetScanSerialize(Serializer &serializer, const optional_ptr bind_data_p, - const TableFunction &function) { - auto &bind_data = bind_data_p->Cast(); - auto &parquet_data = bind_data.bind_data->Cast(); - - vector files; - for (auto &file : bind_data.file_list->GetAllFiles()) { - files.emplace_back(file.path); - } - serializer.WriteProperty(100, "files", files); - serializer.WriteProperty(101, "types", bind_data.types); - serializer.WriteProperty(102, "names", bind_data.names); - ParquetOptionsSerialization serialization(parquet_data.GetParquetOptions(), bind_data.file_options); - serializer.WriteProperty(103, "parquet_options", serialization); - // previously serialization version 3 - if (serializer.ShouldSerialize(StorageVersion::V1_2_0)) { - serializer.WriteProperty(104, "table_columns", bind_data.table_columns); - } - // Old clients won't be able to read the plan even if we - // don't push this field. If projection expression worked, - // it modified "types" which we serialize ultimately - serializer.WriteProperty(105, "projection_expressions", parquet_data.projection_expressions); -} - -static unique_ptr ParquetScanDeserialize(Deserializer &deserializer, TableFunction &function) { - auto &context = deserializer.Get(); - auto files = deserializer.ReadProperty>(100, "files"); - auto types = deserializer.ReadProperty>(101, "types"); - auto names = StringsToIdentifiers(deserializer.ReadProperty>(102, "names")); - auto serialization = deserializer.ReadProperty(103, "parquet_options"); - auto table_columns = - deserializer.ReadPropertyWithExplicitDefault>(104, "table_columns", vector {}); - auto projection_expressions = - deserializer.ReadPropertyWithExplicitDefault>( - 105, "projection_expressions", unordered_map {}); - - vector file_path; - for (auto &path : files) { - file_path.emplace_back(path); - } - FileGlobInput input(FileGlobOptions::FALLBACK_GLOB, "parquet"); - // we are restoring an already bound file list rather than globbing user input - it is legitimately empty - // when filter pushdown pruned every file away, and rejecting that makes the plan impossible to deserialize - input.allow_empty = true; - - auto multi_file_reader = MultiFileReader::Create(function); - auto file_list = multi_file_reader->CreateFileList(context, Value::LIST(LogicalType::VARCHAR, file_path), input); - auto parquet_options = make_uniq(std::move(serialization.parquet_options)); - auto interface = make_uniq(); - auto bind_data = MultiFileFunction::MultiFileBindInternal( - context, std::move(multi_file_reader), std::move(file_list), types, names, - std::move(serialization.file_options), std::move(parquet_options), std::move(interface)); - auto &inner_bind_data = bind_data->Cast(); - inner_bind_data.table_columns = std::move(table_columns); - auto &parquet_bind_data = inner_bind_data.bind_data->Cast(); - parquet_bind_data.projection_expressions = std::move(projection_expressions); - - for (const auto &[idx, expr] : parquet_bind_data.projection_expressions) { - if (idx < inner_bind_data.columns.size()) { - inner_bind_data.columns[idx].type = expr.return_type; - } - if (idx < inner_bind_data.types.size()) { - inner_bind_data.types[idx] = expr.return_type; - } - if (auto &schema = inner_bind_data.reader_bind.schema; !schema.empty() && idx < schema.size()) { - schema[idx].type = expr.return_type; - } - if (!inner_bind_data.initial_reader) { - continue; - } - auto &reader = inner_bind_data.initial_reader->Cast(); - reader.projection_expressions[idx] = expr; - if (idx < reader.columns.size()) { - reader.columns[idx].type = expr.return_type; - } - if (idx < reader.root_schema->children.size()) { - reader.root_schema->children[idx].type = expr.return_type; - } - } - return bind_data; + return StructType::IsStruct(column_type) || column_type.id() == LogicalTypeId::VARIANT; } static vector ParquetGetRowIdColumns(ClientContext &context, optional_ptr bind_data) { @@ -318,136 +132,25 @@ static vector ParquetGetRowIdColumns(ClientContext &context, optional_ return result; } -static void ParquetScanGetMetrics(TableFunctionGetMetricsInput &input) { - // emit the shared multi-file metrics (files read, filenames, rows scanned) - MultiFileFunction::MultiFileGetMetrics(input); - if (!input.global_state) { - return; - } - auto &gstate = input.global_state->Cast(); - if (!gstate.global_state) { - return; - } - auto &parquet_gstate = gstate.global_state->Cast(); - // each local state drains the not-yet-reported count - input.operator_metrics.row_groups_scanned = parquet_gstate.row_groups_scanned_unreported.exchange(0); - input.operator_metrics.total_row_groups_to_scan = parquet_gstate.total_row_groups_to_scan.load(); -} - -static bool ParquetProjectionExpressionPushdown(ClientContext &context, - const TableFunctionProjectionExpressionInput &input) { - if (input.expr.GetExpressionClass() != ExpressionClass::BOUND_FUNCTION) { - return false; - } - const auto &fn = input.expr.Cast(); - if (const Identifier &name = fn.Function().GetName(); name != "strlen" && name != "octet_length") { - return false; - } - - auto &bind_data = input.get.bind_data->Cast(); - // Don't do pushdown on custom schema users like Ducklake. - // We may support it and UNION readers in the future - if (!bind_data.reader_bind.schema.empty() || !bind_data.union_readers.empty()) { - return false; - } - - const auto &column_id = input.get.GetColumnIds()[input.column_index]; - // Pushdown extract columns i.e. SELECT x.y.z have a complex nested type update - if (column_id.IsPushdownExtract()) { - return false; - } - const idx_t idx = column_id.GetPrimaryIndex(); - - // Don't do pushdown of strlen to hive and filename columns. - // See src/function/table/read_csv.cpp : PushdownProjectionExpression - for (const auto &partition : bind_data.reader_bind.hive_partitioning_indexes) { - if (partition.index == idx) { - return false; - } - } - if (bind_data.reader_bind.filename_idx.IsValid() && bind_data.reader_bind.filename_idx.GetIndex() == idx) { - return false; - } - - // We run scalar function pushdown after filter pushdown. If a filter was - // pushed into get.table_filters, we don't see it here and can't - // proceed with pushdown - if (input.get.table_filters.HasFilter(input.column_index)) { - return false; - } - - auto &parquet_bind_data = bind_data.bind_data->Cast(); - // Don't do pushdown with multiple files. As metadata for all but first file is not - // available upfront, we need to TryLoadCaches and check cache validity which complicates - // things. Another complication is that different files may have separate schemas: - // if we're reading file 1 (int, str) and file 2 (str, int) and trying to push down - // strlen for column 2, query will fail on file 2 as it compares column indices and - // not names - if (bind_data.file_list->GetExpandResult() == FileExpandResult::MULTIPLE_FILES) { - return false; - } - if (!bind_data.initial_reader) { - return false; - } - - auto &reader = bind_data.initial_reader->Cast(); - auto &children = reader.root_schema->children; - if (idx >= children.size()) { - return false; - } - - const idx_t column_flat_idx = children[idx].column_index; - for (const auto &group : reader.GetFileMetadata()->row_groups) { - D_ASSERT(column_flat_idx < group.columns.size()); - for (const Encoding::type type : group.columns[column_flat_idx].meta_data.encodings) { - if (type == duckdb_parquet::Encoding::DELTA_LENGTH_BYTE_ARRAY) { - return false; - } - } - } - - const LogicalType type = LogicalType::BIGINT; - bind_data.types[idx] = type; - bind_data.columns[idx].type = type; - - const ParquetReaderProjectionExpression expression {ParquetReaderProjectionExpressionType::BYTE_LENGTH, type}; - - parquet_bind_data.projection_expressions[idx] = expression; - - // initial reader was created before scalar function pushdown optimizer pass, update its types. - reader.projection_expressions[idx] = expression; - if (idx < reader.columns.size()) { - reader.columns[idx].type = type; - } - children[idx].type = type; - return true; -} - ParquetMetadataCacheEntry::ParquetMetadataCacheEntry(shared_ptr metadata_p, ParquetCacheValidity validity_p, bool has_deletes_p) : metadata(std::move(metadata_p)), validity(validity_p), has_deletes(has_deletes_p) { } -const vector &ParquetReadBindData::TryLoadCaches(const MultiFileBindData &bind_data, - ClientContext &context) { - if (attempted_to_load_caches) { - return caches; - } - // only attempt to load the caches once - attempted_to_load_caches = true; +//! The cached metadata of every file of the scan - empty unless the metadata of all of them is cached +static vector LoadMetadataCaches(ClientContext &context, MultiFileList &file_list) { + vector result; // if we are reading multiple files - we check if we have caching enabled if (!ParquetReader::MetadataCacheEnabled(context)) { // no caching - bail - return caches; + return result; } // caching is enabled - check if we have ALL of the metadata cached - vector result; - for (auto &file : bind_data.file_list->Files()) { + for (auto &file : file_list.Files()) { auto metadata_entry = ParquetReader::GetMetadataCacheEntry(context, file); if (!metadata_entry) { // no cache entry found for this file - attempted_to_load_caches = true; - return caches; + return vector(); } // check if the file has any deletes // if it has, skip emitting partition stats @@ -461,37 +164,39 @@ const vector &ParquetReadBindData::TryLoadCaches(cons const auto is_valid = metadata_entry->IsValid(file, context); result.emplace_back(std::move(metadata_entry), is_valid, has_deletes); } - caches = std::move(result); + return result; +} + +const vector &ParquetReadBindData::TryLoadCaches(const MultiFileBindData &bind_data, + ClientContext &context) { + if (attempted_to_load_caches) { + return caches; + } + // only attempt to load the caches once + attempted_to_load_caches = true; + caches = LoadMetadataCaches(context, *bind_data.file_list); return caches; } -static vector ParquetGetPartitionStats(ClientContext &context, GetPartitionStatsInput &input) { - auto &bind_data = input.bind_data->Cast(); +//! The row groups of every file of the scan, taken from their cached metadata - empty unless all of it can be used +static vector +GetCachedPartitionStats(ClientContext &context, const vector &cached_metadata, + const shared_ptr &encryption_config) { vector result; - if (bind_data.file_list->GetExpandResult() == FileExpandResult::SINGLE_FILE && bind_data.initial_reader) { - // we have read the metadata - get the partitions for this reader - auto &reader = bind_data.initial_reader->Cast(); - reader.GetPartitionStats(result); - return result; - } - auto &parquet_data = bind_data.bind_data->Cast(); - auto &cached_metadata = parquet_data.TryLoadCaches(bind_data, context); if (cached_metadata.empty()) { // no cached metadata - bail return result; } - const auto &parquet_options = parquet_data.GetParquetOptions(); string encryption_key_hash; optional_ptr encryption_key_hash_ptr; // first check if all caches are valid and there are no deletes for (auto &cache : cached_metadata) { - if (cache.metadata->IsEncrypted() && parquet_options.encryption_config && !encryption_key_hash_ptr) { + if (cache.metadata->IsEncrypted() && encryption_config && !encryption_key_hash_ptr) { auto hash_util = context.db->GetMbedTLSUtil(false); - encryption_key_hash = - ParquetFileMetadataCache::CreateEncryptionKeyHash(*parquet_options.encryption_config, *hash_util); + encryption_key_hash = ParquetFileMetadataCache::CreateEncryptionKeyHash(*encryption_config, *hash_util); encryption_key_hash_ptr = encryption_key_hash; } - if (!cache.metadata->CanUseMetadataStatistics(parquet_options.encryption_config, encryption_key_hash_ptr)) { + if (!cache.metadata->CanUseMetadataStatistics(encryption_config, encryption_key_hash_ptr)) { return result; } if (cache.has_deletes) { @@ -512,33 +217,80 @@ static vector ParquetGetPartitionStats(ClientContext &conte return result; } -TableFunctionSet ParquetScanFunction::GetFunctionSet() { - MultiFileFunction table_function("parquet_scan"); - table_function.named_parameters["binary_as_string"] = LogicalType::BOOLEAN; - table_function.named_parameters["file_row_number"] = LogicalType::BOOLEAN; - table_function.named_parameters["debug_use_openssl"] = LogicalType::BOOLEAN; - table_function.named_parameters["compression"] = LogicalType::VARCHAR; - table_function.named_parameters["explicit_cardinality"] = LogicalType::UBIGINT; - table_function.named_parameters["schema"] = LogicalTypeId::ANY; - table_function.named_parameters["encryption_config"] = LogicalTypeId::ANY; - table_function.named_parameters["parquet_version"] = LogicalType::VARCHAR; - table_function.named_parameters["can_have_nan"] = LogicalType::BOOLEAN; - table_function.named_parameters["prefetch_strategy"] = LogicalType::VARCHAR; - table_function.named_parameters["utf8_validation"] = LogicalType::VARCHAR; - table_function.statistics_extended = MultiFileFunction::MultiFileScanStatsExtended; - table_function.get_metrics = ParquetScanGetMetrics; - table_function.projection_expression_pushdown = ParquetProjectionExpressionPushdown; - table_function.supports_pushdown_extract = ParquetScanSupportPushdownExtract; - table_function.serialize = ParquetScanSerialize; - table_function.deserialize = ParquetScanDeserialize; - table_function.get_row_id_columns = ParquetGetRowIdColumns; - table_function.pushdown_expression = ParquetScanPushdownExpression; - table_function.get_partition_stats = ParquetGetPartitionStats; - table_function.filter_pushdown = true; - table_function.filter_prune = true; - table_function.late_materialization = true; - - return MultiFileReader::CreateFunctionSet(static_cast(table_function)); +//! The row groups of a multi-file parquet scan - those of a single file are read from the file, those of several files +//! only when the metadata of every one of them is cached +static vector ParquetMultiFileGetPartitionStats(ClientContext &context, + GetPartitionStatsInput &input) { + auto &bind_data = input.bind_data->Cast(); + if (bind_data.file_list->GetExpandResult() == FileExpandResult::SINGLE_FILE) { + auto result = TableFunctionMultiFileWrapper::GetPartitionStats(context, input); + if (!bind_data.file_options.bound_schema.empty()) { + // Column accessors address physical ordinals and types, not the mapped output or generated roles. + const bool identity = + bind_data.initial_reader && bind_data.HasIdentityMapping(bind_data.initial_reader->GetColumns()); + if (!identity) { + for (auto &partition : result) { + partition.partition_row_group.reset(); + } + } + } + return result; + } + // without the bind of a file we do not know the key the files are encrypted with - encrypted files are then not + // answered from their metadata + shared_ptr encryption_config; + auto &file_bind_data = bind_data.bind_data->Cast().options.schema_bind_data; + if (file_bind_data) { + encryption_config = ParquetScanFunction::GetFileOptions(*file_bind_data).encryption_config; + } + return GetCachedPartitionStats(context, LoadMetadataCaches(context, *bind_data.file_list), encryption_config); +} + +void ParquetScanFunction::AddNamedParameters(TableFunction &table_function) { + auto add_options = [](TypedKwargs &options) { + options.Add("binary_as_string", LogicalType::BOOLEAN) + .Add("debug_use_openssl", LogicalType::BOOLEAN) + .Add("compression", LogicalType::VARCHAR) + .Add("explicit_cardinality", LogicalType::UBIGINT) + .Add("encryption_config", LogicalTypeId::ANY) + .Add("parquet_version", LogicalType::VARCHAR) + .Add("can_have_nan", LogicalType::BOOLEAN) + .Add("prefetch_strategy", LogicalType::VARCHAR) + .Add("utf8_validation", LogicalType::VARCHAR) + .Add("int96_as", LogicalType::VARCHAR); + }; + // the multi-file function already declares options of its own - the single-file function declares none + auto &signature = table_function.GetSignature(); + if (signature.GetTypedKwargs()) { + signature.ExtendTypedKwargs(add_options); + } else { + signature.WithTypedKwargs("options", add_options); + } +} + +//! Bind a parquet scan. The wrapped single-file function is resolved here rather than taken from the function that +//! is being bound - callers that manage parquet files themselves (e.g. DuckLake) bind this through a TableFunction +//! they construct, which carries none of our info +static unique_ptr ParquetMultiFileBind(ClientContext &context, TableFunctionBindInput &input, + vector &return_types, vector &names) { + return TableFunctionMultiFileWrapper::MultiFileBindWith(context, input, return_types, names, + ParquetScanFunction::GetSingleFileFunction(), + ParquetScanFunction::GetMultiFileSettings()); +} + +TableFunction ParquetScanFunction::GetMultiFileFunction(Identifier name) { + // the multi-file parquet reader is the single-file parquet reader wrapped into a multi-file function + auto result = TableFunctionMultiFileWrapper::CreateFunction(GetSingleFileFunction(), std::move(name), + GetMultiFileSettings(), ParquetMultiFileBind); + result.GetSignature().ExtendTypedKwargs( + [](TypedKwargs &options) { options.Add("bound_schema", LogicalType::ANY); }); + // the callbacks below describe the scan rather than one of its files, so they are set on the wrapper + result.get_row_id_columns = ParquetGetRowIdColumns; + result.supports_pushdown_extract = ParquetScanSupportPushdownExtract; + result.pushdown_expression = ParquetScanPushdownExpression; + result.get_partition_stats = ParquetMultiFileGetPartitionStats; + result.projection_expression_pushdown = ParquetScanFunction::ProjectionExpressionPushdown; + return result; } unique_ptr ParquetMultiFileInfo::InitializeOptions(ClientContext &context, @@ -560,10 +312,6 @@ bool ParquetMultiFileInfo::ParseCopyOption(ClientContext &context, const Identif options.binary_as_string = GetBooleanArgument(key, values); return true; } - if (key == "file_row_number") { - options.file_row_number = GetBooleanArgument(key, values); - return true; - } if (key == "debug_use_openssl") { return true; // deprecated } @@ -595,6 +343,13 @@ bool ParquetMultiFileInfo::ParseCopyOption(ClientContext &context, const Identif options.utf8_validation_option = StringColumnReader::GetUtf8ValidationOption(StringValue::Get(values[0])); return true; } + if (key == "int96_as") { + if (values.size() != 1) { + throw BinderException("Parquet int96_as cannot be empty!"); + } + options.int96_as = ParquetInt96AsOptionFromString(StringValue::Get(values[0])); + return true; + } return false; } @@ -614,10 +369,6 @@ bool ParquetMultiFileInfo::ParseOption(ClientContext &context, const Identifier options.binary_as_string = BooleanValue::Get(val); return true; } - if (key == "file_row_number") { - options.file_row_number = BooleanValue::Get(val); - return true; - } if (key == "debug_use_openssl") { return true; // deprecated } @@ -625,16 +376,6 @@ bool ParquetMultiFileInfo::ParseOption(ClientContext &context, const Identifier options.can_have_nan = BooleanValue::Get(val); return true; } - if (key == "schema") { - // Argument is a map that defines the schema - auto schema = ParquetColumnDefinition::FromSchemaMap(context, val); - if (schema.empty()) { - throw BinderException("Parquet schema cannot be empty"); - } - options.schema = std::move(schema); - file_options.auto_detect_hive_partitioning = false; - return true; - } if (key == "explicit_cardinality") { options.explicit_cardinality = UBigIntValue::Get(val); return true; @@ -651,6 +392,10 @@ bool ParquetMultiFileInfo::ParseOption(ClientContext &context, const Identifier options.utf8_validation_option = StringColumnReader::GetUtf8ValidationOption(StringValue::Get(val)); return true; } + if (key == "int96_as") { + options.int96_as = ParquetInt96AsOptionFromString(StringValue::Get(val)); + return true; + } return false; } @@ -673,7 +418,6 @@ void ParquetMultiFileInfo::GetBindInfo(const TableFunctionData &bind_data_p, Bin auto &parquet_options = bind_data.GetParquetOptions(); info.type = ScanType::PARQUET; info.InsertOption("binary_as_string", Value::BOOLEAN(parquet_options.binary_as_string)); - info.InsertOption("file_row_number", Value::BOOLEAN(parquet_options.file_row_number)); } optional_idx ParquetMultiFileInfo::MaxThreads(const MultiFileBindData &bind_data_p, diff --git a/src/duckdb/extension/parquet/parquet_reader.cpp b/src/duckdb/extension/parquet/parquet_reader.cpp index 745c2c023..38408519e 100644 --- a/src/duckdb/extension/parquet/parquet_reader.cpp +++ b/src/duckdb/extension/parquet/parquet_reader.cpp @@ -13,6 +13,7 @@ #include "reader/byte_array_length_column_reader.hpp" #include "reader/expression_column_reader.hpp" #include "parquet_geometry.hpp" +#include "parquet_int96.hpp" #include "reader/list_column_reader.hpp" #include "parquet_crypto.hpp" #include "parquet_file_metadata_cache.hpp" @@ -20,6 +21,7 @@ #include "reader/row_number_column_reader.hpp" #include "reader/variant_column_reader.hpp" #include "reader/struct_column_reader.hpp" +#include "reader/string_column_reader.hpp" #include "thrift_tools.hpp" #include "parquet_prefetch_cost_model.hpp" #include "duckdb/common/encryption_state.hpp" @@ -29,8 +31,10 @@ #include "duckdb/storage/object_cache.hpp" #include "duckdb/planner/table_filter_state.hpp" #include "duckdb/common/multi_file/multi_file_adaptive_filter_cache.hpp" +#include "duckdb/common/multi_file/multi_file_data.hpp" #include "duckdb/common/multi_file/multi_file_reader.hpp" #include "duckdb/common/types/geometry_crs.hpp" +#include "duckdb/common/types.hpp" #include "duckdb/common/allocator.hpp" #include "duckdb/common/assert.hpp" #include "duckdb/common/case_insensitive_map.hpp" @@ -49,6 +53,7 @@ #include "duckdb/common/types/vector.hpp" #include "duckdb/common/vector_size.hpp" #include "duckdb/function/scalar/struct_utils.hpp" +#include "duckdb/function/scalar/variant_utils.hpp" #include "duckdb/logging/log_type.hpp" #include "duckdb/logging/logger.hpp" #include "duckdb/main/client_context.hpp" @@ -73,6 +78,14 @@ namespace duckdb { +static_assert(ParquetTimestampTzLogicalType(ParquetExtraTypeInfo::UNIT_MS) == LogicalTypeId::TIMESTAMP_TZ); +static_assert(ParquetTimestampTzLogicalType(ParquetExtraTypeInfo::UNIT_MICROS) == LogicalTypeId::TIMESTAMP_TZ); +static_assert(ParquetTimestampTzLogicalType(ParquetExtraTypeInfo::UNIT_NS) == LogicalTypeId::TIMESTAMP_TZ_NS); + +static_assert(ParquetTimeTzLogicalType(ParquetExtraTypeInfo::UNIT_MS) == LogicalTypeId::TIME_TZ); +static_assert(ParquetTimeTzLogicalType(ParquetExtraTypeInfo::UNIT_MICROS) == LogicalTypeId::TIME_TZ); +static_assert(ParquetTimeTzLogicalType(ParquetExtraTypeInfo::UNIT_NS) == LogicalTypeId::TIME_TZ); + const char *ParquetPrefetchStrategyToString(ParquetPrefetchStrategy strategy) { switch (strategy) { case ParquetPrefetchStrategy::WHOLE_GROUP: @@ -99,6 +112,20 @@ ParquetPrefetchStrategyOption ParquetPrefetchStrategyOptionFromString(const stri throw BinderException("Unrecognized prefetch_strategy '%s' (supported: 'auto', 'whole_group')", value); } +ParquetInt96AsOption ParquetInt96AsOptionFromString(const string &value) { + auto lower = StringUtil::Lower(value); + if (lower == "timestamp") { + return ParquetInt96AsOption::TIMESTAMP; + } + if (lower == "timestamp_ns") { + return ParquetInt96AsOption::TIMESTAMP_NS; + } + if (lower == "struct") { + return ParquetInt96AsOption::STRUCT; + } + throw BinderException("Unrecognized int96_as '%s' (supported: 'timestamp', 'timestamp_ns', 'struct')", value); +} + static idx_t ParquetColumnChunkFileOffset(const duckdb_parquet::ColumnChunk &chunk) { idx_t offset = NumericCast(chunk.meta_data.data_page_offset); if (chunk.meta_data.__isset.dictionary_page_offset) { @@ -211,10 +238,10 @@ CreateThriftFileProtocol(QueryContext context, CachingFileHandle &file_handle, b } static bool ShouldAndCanPrefetch(ClientContext &context, CachingFileHandle &file_handle) { - Value disable_prefetch = false; + bool disable_prefetch = false; context.TryGetCurrentSetting("disable_parquet_prefetching", disable_prefetch); // local files also prefetch by default, the async I/O overlaps with decoding - return file_handle.CanSeek() && !disable_prefetch.GetValue(); + return file_handle.CanSeek() && !disable_prefetch; } //! Coalescing gap for the scan's prefetch I/O, either pinned through a setting or chosen by the cost model @@ -422,14 +449,9 @@ LogicalType ParquetReader::DeriveLogicalType(const SchemaElement &s_ele, const P throw NotImplementedException("Unimplemented TIMESTAMP encoding - missing UNIT"); } if (s_ele.logicalType.TIMESTAMP.isAdjustedToUTC) { - if (s_ele.logicalType.TIMESTAMP.unit.__isset.NANOS) { - return LogicalType::TIMESTAMP_TZ_NS; - } - return LogicalType::TIMESTAMP_TZ; - } else if (s_ele.logicalType.TIMESTAMP.unit.__isset.NANOS) { - return LogicalType::TIMESTAMP_NS; + return LogicalType(ParquetTimestampTzLogicalType(schema.type_info)); } - return LogicalType::TIMESTAMP; + return LogicalType(ParquetTimestampLogicalType(schema.type_info)); } else if (s_ele.logicalType.__isset.TIME) { if (s_ele.logicalType.TIME.unit.__isset.MILLIS) { schema.type_info = ParquetExtraTypeInfo::UNIT_MS; @@ -441,11 +463,9 @@ LogicalType ParquetReader::DeriveLogicalType(const SchemaElement &s_ele, const P throw NotImplementedException("Unimplemented TIME encoding - missing UNIT"); } if (s_ele.logicalType.TIME.isAdjustedToUTC) { - return LogicalType::TIME_TZ; - } else if (s_ele.logicalType.TIME.unit.__isset.NANOS) { - return LogicalType::TIME_NS; + return LogicalType(ParquetTimeTzLogicalType(schema.type_info)); } - return LogicalType::TIME; + return LogicalType(ParquetTimeLogicalType(schema.type_info)); } } if (s_ele.__isset.converted_type) { @@ -511,14 +531,14 @@ LogicalType ParquetReader::DeriveLogicalType(const SchemaElement &s_ele, const P case ConvertedType::TIMESTAMP_MICROS: schema.type_info = ParquetExtraTypeInfo::UNIT_MICROS; if (s_ele.type == Type::INT64) { - return LogicalType::TIMESTAMP; + return LogicalType(ParquetTimestampLogicalType(schema.type_info)); } else { throw IOException("TIMESTAMP converted type can only be set for value of Type::INT64"); } case ConvertedType::TIMESTAMP_MILLIS: schema.type_info = ParquetExtraTypeInfo::UNIT_MS; if (s_ele.type == Type::INT64) { - return LogicalType::TIMESTAMP; + return LogicalType(ParquetTimestampLogicalType(schema.type_info)); } else { throw IOException("TIMESTAMP converted type can only be set for value of Type::INT64"); } @@ -526,6 +546,9 @@ LogicalType ParquetReader::DeriveLogicalType(const SchemaElement &s_ele, const P if (!s_ele.__isset.precision || !s_ele.__isset.scale) { throw IOException("DECIMAL requires a length and scale specifier!"); } + if (s_ele.precision < 1 || s_ele.scale < 0 || s_ele.scale > s_ele.precision) { + throw IOException("Invalid DECIMAL precision %d and scale %d", s_ele.precision, s_ele.scale); + } schema.type_scale = NumericCast(s_ele.scale); if (s_ele.precision > DecimalType::MaxWidth()) { schema.type_info = ParquetExtraTypeInfo::DECIMAL_BYTE_ARRAY; @@ -559,18 +582,22 @@ LogicalType ParquetReader::DeriveLogicalType(const SchemaElement &s_ele, const P case ConvertedType::TIME_MILLIS: schema.type_info = ParquetExtraTypeInfo::UNIT_MS; if (s_ele.type == Type::INT32) { - return LogicalType::TIME; + return LogicalType(ParquetTimeLogicalType(schema.type_info)); } else { throw IOException("TIME_MILLIS converted type can only be set for value of Type::INT32"); } case ConvertedType::TIME_MICROS: schema.type_info = ParquetExtraTypeInfo::UNIT_MICROS; if (s_ele.type == Type::INT64) { - return LogicalType::TIME; + return LogicalType(ParquetTimeLogicalType(schema.type_info)); } else { throw IOException("TIME_MICROS converted type can only be set for value of Type::INT64"); } case ConvertedType::INTERVAL: + if (s_ele.type != Type::FIXED_LEN_BYTE_ARRAY || s_ele.type_length != 12) { + throw DataCorruptionException( + "INTERVAL converted type can only be set for value of Type::FIXED_LEN_BYTE_ARRAY of length 12"); + } return LogicalType::INTERVAL; case ConvertedType::JSON: return LogicalType::JSON(); @@ -593,7 +620,16 @@ LogicalType ParquetReader::DeriveLogicalType(const SchemaElement &s_ele, const P return LogicalType::BIGINT; case Type::INT96: // always a timestamp it would seem schema.type_info = ParquetExtraTypeInfo::IMPALA_TIMESTAMP; - return LogicalType::TIMESTAMP; + switch (parquet_options.int96_as) { + case ParquetInt96AsOption::TIMESTAMP: + return LogicalType::TIMESTAMP; + case ParquetInt96AsOption::TIMESTAMP_NS: + return LogicalType::TIMESTAMP_NS; + case ParquetInt96AsOption::STRUCT: + return LogicalType::STRUCT({{"date", LogicalType::DATE}, {"time", LogicalType::TIME_NS}}); + default: + throw InternalException("Unrecognized int96_as option"); + } case Type::FLOAT: return LogicalType::FLOAT; case Type::DOUBLE: @@ -671,6 +707,23 @@ unique_ptr ParquetReader::ReadStatistics(ClientContext &context, return reader.ReadStatistics(name); } +unique_ptr +ParquetReader::ReadVirtualColumnStatistics(ClientContext &context, const ParquetOptions &parquet_options, + const shared_ptr &metadata, + column_t virtual_column_id) { + if (virtual_column_id != MultiFileReader::COLUMN_IDENTIFIER_FILE_ROW_NUMBER || !metadata || + !CanUseParquetMetadataStatistics(context, metadata, parquet_options)) { + return nullptr; + } + // the row numbers of every row group follow on from those of the row groups before it + return ReadColumnStatistics(*metadata->metadata, ParquetColumnSchema::FileRowNumber(), parquet_options); +} + +unique_ptr ParquetReader::GetVirtualColumnStatistics(ClientContext &context, + column_t virtual_column_id) { + return ReadVirtualColumnStatistics(context, parquet_options, metadata, virtual_column_id); +} + unique_ptr ParquetReader::ReadStatistics(ClientContext &context, const ParquetUnionData &union_data, const Identifier &name) { if (!CanUseParquetMetadataStatistics(context, union_data.metadata, union_data.options)) { @@ -721,6 +774,28 @@ optional_ptr ParquetReader::GetVariantStats(const ParquetC return *res.first->second; } +//! Whether the cast of the extracted field can be applied to its 'typed_value' instead of to the VARIANT +static bool TypedValueCastMatchesVariantCast(const ParquetColumnSchema &schema, const ColumnIndex &column_id) { + D_ASSERT(schema.name == "typed_value"); + reference typed_value(schema); + reference path_iter(column_id); + while (path_iter.get().HasChildren()) { + auto &child = path_iter.get().GetChildIndexes()[0]; + auto child_column_index = typed_value.get().GetChildIndexByName(child.GetFieldName()); + if (!child_column_index.IsValid()) { + return false; + } + auto &child_column = typed_value.get().GetChildByIndex(child_column_index.GetIndex()); + auto typed_value_index = child_column.GetChildIndexByName("typed_value"); + if (!typed_value_index.IsValid()) { + return false; + } + typed_value = child_column.GetChildByIndex(typed_value_index.GetIndex()); + path_iter = child; + } + return VariantUtils::ShreddedCastMatchesVariantCast(typed_value.get().type, path_iter.get().GetType()); +} + static ColumnIndex CreateVariantTypedValuePushdown(const ParquetColumnSchema &schema, const ColumnIndex &column_id) { D_ASSERT(schema.name == "typed_value"); reference typed_value(schema); @@ -737,7 +812,7 @@ static ColumnIndex CreateVariantTypedValuePushdown(const ParquetColumnSchema &sc throw InternalException("Can't locate the child by name '%s' in the VARIANT column", field_name); } auto &child_column = typed_value.get().GetChildByIndex(child_column_index.GetIndex()); - if (child_column.type.id() != LogicalTypeId::STRUCT) { + if (!StructType::IsStruct(child_column.type)) { throw InternalException("Extracted field for '%s' from 'typed_value', is not a struct (received: %s)", field_name, child_column.type.ToString()); } @@ -768,6 +843,43 @@ static ColumnIndex CreateVariantTypedValuePushdown(const ParquetColumnSchema &sc return result_index; } +static unique_ptr CreateInt96StructReader(ClientContext &context, const ParquetReader &reader, + const ParquetColumnSchema &schema, + optional_ptr pushdown_child) { + // Read the raw 12-byte INT96 value as a BLOB - this is lossless over the full range INT96 can express + // (0001-01-01 through 9999-12-31), unlike TIMESTAMP/TIMESTAMP_NS which clamp or truncate + // The blob schema is a copy of the leaf schema with a different type; it is kept alive on the + // expression reader's heap (the ColumnReader base class only holds a reference to it) + if (pushdown_child) { + // The scan only exposes a single child of the INT96-as-struct column (e.g. ts['date']). + // Follow the same pattern as a pushed-down extract on a regular STRUCT column: wrap the leaf in an + // EXPRESSION schema carrying the child's type so the reader reports the child type and produces no + // (struct) row group statistics, which a filter on the child would otherwise be checked against. + auto expr = CreateInt96AsStructChildExpression(context, pushdown_child->GetPrimaryIndex()); + if (expr->GetReturnType() != pushdown_child->GetType()) { + expr = BoundCastExpression::AddCastToType(context, std::move(expr), pushdown_child->GetType()); + } + auto expr_schema = make_uniq( + ParquetColumnSchema::FromParentSchema(schema, expr->GetReturnType(), ParquetColumnSchemaType::EXPRESSION)); + // FromParentSchema embeds a copy of the leaf schema as its first child - retype that copy to BLOB so the + // embedded child is the raw INT96 leaf and stays alive through the expression schema's ownership + expr_schema->children[0].type = LogicalType::BLOB; + vector> children; + children.push_back(make_uniq(reader, expr_schema->children[0])); + return make_uniq(context, std::move(children), std::move(expr), std::move(expr_schema)); + } + auto child_schema = make_uniq(schema); + child_schema->type = LogicalType::BLOB; + auto blob_reader = make_uniq(reader, *child_schema); + + vector> children; + children.push_back(std::move(blob_reader)); + auto expr = CreateInt96AsStructExpression(context); + auto result = make_uniq(context, std::move(children), std::move(expr), schema); + result->owned_schema = std::move(child_schema); + return std::move(result); +} + unique_ptr ParquetReader::CreateReaderRecursive(ClientContext &context, const ColumnIndex &column_id, const ParquetColumnSchema &schema) const { auto &indexes = column_id.GetChildIndexes(); @@ -782,6 +894,11 @@ unique_ptr ParquetReader::CreateReaderRecursive(ClientContext &con } case ParquetColumnSchemaType::COLUMN: { if (schema.children.empty()) { + if (schema.parquet_type == Type::INT96 && schema.type.id() == LogicalTypeId::STRUCT) { + // int96_as='struct' exposes the raw INT96 value as STRUCT(date DATE, time TIME_NS) + return CreateInt96StructReader(context, *this, schema, + column_id.IsPushdownExtract() ? &indexes[0] : nullptr); + } // leaf reader return ColumnReader::CreateReader(*this, schema); } @@ -804,7 +921,8 @@ unique_ptr ParquetReader::CreateReaderRecursive(ClientContext &con case LogicalTypeId::MAP: D_ASSERT(children.size() == 1); return make_uniq(*this, schema, std::move(children[0])); - case LogicalTypeId::STRUCT: { + case LogicalTypeId::STRUCT: + case LogicalTypeId::TUPLE: { if (column_id.IsPushdownExtract()) { auto &child = indexes[0]; auto child_index = child.GetPrimaryIndex(); @@ -838,36 +956,31 @@ unique_ptr ParquetReader::CreateReaderRecursive(ClientContext &con } vector> children; children.resize(schema.children.size()); - if (schema.children.size() != 3 || !column_id.IsPushdownExtract()) { - for (idx_t child_index = 0; child_index < schema.children.size(); child_index++) { - children[child_index] = - CreateReaderRecursive(context, ColumnIndex(child_index), schema.children[child_index]); + if (schema.children.size() == 3 && column_id.IsPushdownExtract()) { + //! VARIANT is shredded - it has a 'typed_value' column + auto &typed_value_schema = schema.children[2]; + D_ASSERT(typed_value_schema.name == "typed_value"); + auto variant_stats = GetVariantStats(schema); + + if (variant_stats && IsFullyShredded(*variant_stats, column_id) && + TypedValueCastMatchesVariantCast(typed_value_schema, column_id)) { + //! This field is present in 'typed_value' across all rowgroups + //! So we can directly push a struct extract into 'typed_value' and ignore 'value'+'metadata' + auto typed_value_index = CreateVariantTypedValuePushdown(typed_value_schema, column_id); + return CreateReaderRecursive(context, typed_value_index, typed_value_schema); } - return make_uniq(context, *this, schema, std::move(children)); - } - //! VARIANT is shredded - it has a 'typed_value' column - //! And the extract is pushed down into the scan - auto &typed_value_schema = schema.children[2]; - D_ASSERT(typed_value_schema.name == "typed_value"); - auto variant_stats = GetVariantStats(schema); - - if (variant_stats && IsFullyShredded(*variant_stats, column_id)) { - //! This field is present in 'typed_value' across all rowgroups - //! So we can directly push a struct extract into 'typed_value' and ignore 'value'+'metadata' - auto typed_value_index = CreateVariantTypedValuePushdown(typed_value_schema, column_id); - return CreateReaderRecursive(context, typed_value_index, typed_value_schema); } - for (idx_t child_index = 0; child_index < 3; child_index++) { + for (idx_t child_index = 0; child_index < schema.children.size(); child_index++) { children[child_index] = CreateReaderRecursive(context, ColumnIndex(child_index), schema.children[child_index]); } // Create the VariantColumnReader with the column index, so we can perform the extract at Read auto column_reader = make_uniq(context, *this, schema, std::move(children), column_id); - auto scan_type = column_id.GetScanType(); - if (scan_type.id() == LogicalTypeId::VARIANT) { + if (!column_id.IsPushdownExtract() || column_id.GetScanType().id() == LogicalTypeId::VARIANT) { return std::move(column_reader); } + const auto &scan_type = column_id.GetScanType(); auto input = make_uniq(LogicalType::VARIANT(), 0ULL); auto cast_expression = BoundCastExpression::AddCastToType(context, std::move(input), scan_type); auto expr_schema = make_uniq(ParquetColumnSchema::FromParentSchema( @@ -956,11 +1069,20 @@ static bool IsVariantType(const SchemaElement &root, const vector MAX_PARQUET_SCHEMA_DEPTH) { + throw InvalidInputException( + "Malformed Parquet schema in file \"%s\": the schema is nested more than %llu levels " + "deep", + file.path, MAX_PARQUET_SCHEMA_DEPTH); + } if (next_schema_idx >= file_meta_data->schema.size()) { throw InvalidInputException("Malformed Parquet schema in file \"%s\": invalid schema index %d", file.path, next_schema_idx); @@ -1060,6 +1182,24 @@ ParquetColumnSchema ParquetReader::ParseSchemaRecursive(idx_t depth, idx_t max_d if (!is_variant && parquet_options.variant_legacy_encoding && IsVariantType(s_ele, child_schemas)) { is_variant = true; } + if (is_variant) { + // the VARIANT reader reads the 'metadata' and 'value' columns as binary + for (auto &child_schema : child_schemas) { + if (child_schema.name != "metadata" && child_schema.name != "value") { + continue; + } + // a logical type annotation (e.g. GEOMETRY) would make the column reader produce another type + const auto is_binary = + child_schema.type.id() == LogicalTypeId::BLOB || child_schema.type.id() == LogicalTypeId::VARCHAR; + if (!child_schema.children.empty() || child_schema.parquet_type != duckdb_parquet::Type::BYTE_ARRAY || + !is_binary || child_schema.schema_type != ParquetColumnSchemaType::COLUMN) { + throw DataCorruptionException( + "Failed to read Parquet file \"%s\": the '%s' column of VARIANT column \"%s\" " + "must be a binary BYTE_ARRAY column", + file.path, child_schema.name, s_ele.name); + } + } + } if (!is_map_kv && this_idx > 0) { // check if the parent node of this is a map @@ -1155,7 +1295,7 @@ unique_ptr ParquetReader::ParseSchema(ClientContext &contex throw IOException("Failed to read Parquet file \"%s\": root schema element has no children", file.path); } auto root = ParseSchemaRecursive(0, 0, 0, next_schema_idx, next_file_idx, context); - if (root.type.id() != LogicalTypeId::STRUCT) { + if (!StructType::IsStruct(root.type)) { throw InvalidInputException("Failed to read Parquet file \"%s\": Root element of Parquet file must be a struct", file.path); } @@ -1164,17 +1304,6 @@ unique_ptr ParquetReader::ParseSchema(ClientContext &contex throw InvalidInputException("Failed to read Parquet file \"%s\": row group does not have enough columns", file.path); } - if (parquet_options.file_row_number) { - for (auto &column : root.children) { - auto &name = column.name; - if (StringUtil::CIEquals(name, "file_row_number")) { - throw BinderException("Failed to read Parquet file \"%s\": Using file_row_number option on file with " - "column named file_row_number is not supported", - file.path); - } - } - root.children.push_back(FileRowNumberSchema()); - } return make_uniq(root); } @@ -1205,6 +1334,18 @@ MultiFileColumnDefinition ParquetReader::ParseColumnDefinition(const FileMetaDat result.children.push_back(ParseColumnDefinition(file_meta_data, child)); } } + // An INT96 column exposed as a STRUCT (int96_as='struct') is a logical struct over a physical leaf - synthesize + // the struct children here, mirroring MultiFileColumnDefinition::CreateFromNameAndType, so that the column + // definition matches the global one (and the multi-file mapper can map the column trivially). + if (element.schema_type == ParquetColumnSchemaType::COLUMN && element.parquet_type == Type::INT96 && + element.type.id() == LogicalTypeId::STRUCT && parquet_options.int96_as == ParquetInt96AsOption::STRUCT) { + result.children.clear(); + result.children.reserve(StructType::GetChildTypes(element.type).size()); + for (auto &child_entry : StructType::GetChildTypes(element.type)) { + result.children.push_back( + MultiFileColumnDefinition::CreateFromNameAndType(child_entry.first, child_entry.second)); + } + } return result; } @@ -1240,194 +1381,8 @@ void ParquetReader::AddVirtualColumn(column_t virtual_column_id) { } ParquetOptions::ParquetOptions(ClientContext &context) { - Value lookup_value; - if (context.TryGetCurrentSetting("binary_as_string", lookup_value)) { - binary_as_string = lookup_value.GetValue(); - } - if (context.TryGetCurrentSetting("__delta_only_variant_encoding_enabled", lookup_value)) { - variant_legacy_encoding = lookup_value.GetValue(); - } -} - -static void VerifyParquetSchemaDefinitionType(const LogicalType &definition_type, bool is_root) { - if (definition_type.id() != LogicalTypeId::STRUCT) { - if (is_root) { - throw InvalidInputException("'schema' expects a STRUCT as the value type of the map"); - } - throw BinderException("Parquet schema 'children' expects a STRUCT as the value type of the map, not %s", - definition_type.ToString()); - } - auto &fields = StructType::GetChildTypes(definition_type); - if (fields.size() != 3 && fields.size() != 4) { - throw InvalidInputException( - "'schema' expects the STRUCT to have 3 or 4 fields, 'name', 'type', 'default_value' and optionally " - "'children', not %d", - fields.size()); - } - if (fields[0].first != "name") { - throw InvalidInputException("'schema' expects the first field of the struct to be called 'name'"); - } - if (fields[0].second.id() != LogicalTypeId::VARCHAR) { - throw InvalidInputException("'schema' expects the 'name' field to be of type VARCHAR, not %s", - LogicalTypeIdToString(fields[0].second.id())); - } - if (fields[1].first != "type") { - throw InvalidInputException("'schema' expects the second field of the struct to be called 'type'"); - } - if (fields[1].second.id() != LogicalTypeId::VARCHAR) { - throw InvalidInputException("'schema' expects the 'type' field to be of type VARCHAR, not %s", - LogicalTypeIdToString(fields[1].second.id())); - } - if (fields[2].first != "default_value") { - throw InvalidInputException("'schema' expects the third field of the struct to be called 'default_value'"); - } - if (fields.size() == 4 && fields[3].first != "children") { - throw InvalidInputException("'schema' expects the fourth field of the struct to be called 'children'"); - } -} - -static void VerifyParquetSchemaChildType(const ParquetColumnDefinition &column, const ParquetColumnDefinition &child, - const string &expected_name, const LogicalType &expected_type) { - auto &column_name = column.name; - - const bool name_equivalent = child.name == expected_name; - const bool type_equivalent = child.type == expected_type; - if (name_equivalent && type_equivalent) { - return; - } - string error; - if (!name_equivalent) { - error = StringUtil::Format("name \"%s\" (got \"%s\")", expected_name, child.name); - } - if (!type_equivalent) { - const bool name_mentioned = !error.empty(); - if (name_mentioned) { - error += " and "; - } else { - error += StringUtil::Format("name \"%s\" to have ", expected_name); - } - error += StringUtil::Format("type \"%s\" (got \"%s\")", expected_type.ToString(), child.type.ToString()); - } - - throw BinderException("Parquet schema column \"%s\" expects a child with %s", column_name, error); -} - -static void VerifyParquetSchemaChildren(const ParquetColumnDefinition &column) { - idx_t expected_count; - switch (column.type.id()) { - case LogicalTypeId::STRUCT: - expected_count = StructType::GetChildCount(column.type); - break; - case LogicalTypeId::LIST: - expected_count = 1; - break; - case LogicalTypeId::MAP: - expected_count = 2; - break; - default: - throw BinderException("Parquet schema column \"%s\" of type %s cannot define nested children", column.name, - column.type.ToString()); - } - if (column.children.size() != expected_count) { - throw BinderException("Parquet schema column \"%s\" of type %s expects %d child definitions, not %d", - column.name, column.type.ToString(), expected_count, column.children.size()); - } - - switch (column.type.id()) { - case LogicalTypeId::STRUCT: { - auto &expected_children = StructType::GetChildTypes(column.type); - for (idx_t i = 0; i < expected_children.size(); i++) { - VerifyParquetSchemaChildType(column, column.children[i], expected_children[i].first.GetIdentifierName(), - expected_children[i].second); - } - break; - } - case LogicalTypeId::LIST: - VerifyParquetSchemaChildType(column, column.children[0], "element", ListType::GetChildType(column.type)); - break; - case LogicalTypeId::MAP: - VerifyParquetSchemaChildType(column, column.children[0], "key", MapType::KeyType(column.type)); - VerifyParquetSchemaChildType(column, column.children[1], "value", MapType::ValueType(column.type)); - break; - default: - throw InternalException("Unexpected Parquet schema type with children"); - } -} - -static vector ParseParquetSchemaMap(ClientContext &context, const Value &schema_value, - const LogicalType &root_key_type, bool is_root); - -static ParquetColumnDefinition ParseParquetSchemaDefinition(ClientContext &context, const Value &column_value, - const LogicalType &root_key_type) { - ParquetColumnDefinition result; - auto &map_entry = StructValue::GetChildren(column_value); - result.identifier = map_entry[0]; - - const auto &column_def = map_entry[1]; - if (column_def.IsNull()) { - throw BinderException("Parquet schema definition cannot be NULL"); - } - VerifyParquetSchemaDefinitionType(column_def.type(), false); - - const auto children = StructValue::GetChildren(column_def); - result.name = StringValue::Get(children[0]); - result.type = TransformStringToLogicalType(StringValue::Get(children[1]), context); - string error_message; - auto default_value = children[2].TryCastAs(context, result.type, &error_message); - if (!default_value) { - throw BinderException("Unable to cast Parquet schema default_value \"%s\" to %s", children[2].ToString(), - result.type.ToString()); - } - result.default_value = std::move(*default_value); - if (children.size() > 3 && !children[3].IsNull()) { - result.children = ParseParquetSchemaMap(context, children[3], root_key_type, false); - VerifyParquetSchemaChildren(result); - } - - return result; -} - -static vector ParseParquetSchemaMap(ClientContext &context, const Value &schema_value, - const LogicalType &root_key_type, bool is_root) { - if (schema_value.type().id() != LogicalTypeId::MAP) { - if (is_root) { - throw InvalidInputException("'schema' expects a value of type MAP, not %s", - LogicalTypeIdToString(schema_value.type().id())); - } - throw BinderException("Parquet schema 'children' expects a value of type MAP, not %s", - LogicalTypeIdToString(schema_value.type().id())); - } - auto &map_type = schema_value.type(); - auto &key_type = MapType::KeyType(map_type); - auto &value_type = MapType::ValueType(map_type); - VerifyParquetSchemaDefinitionType(value_type, is_root); - if (is_root) { - if (key_type.id() != LogicalTypeId::INTEGER && key_type.id() != LogicalTypeId::VARCHAR) { - throw InvalidInputException( - "'schema' expects the value type of the map to be either INTEGER or VARCHAR, not %s", - LogicalTypeIdToString(key_type.id())); - } - } else if (key_type != root_key_type) { - throw BinderException("Parquet schema 'children' key type must match the root schema key type %s, not %s", - root_key_type.ToString(), key_type.ToString()); - } - - auto &entries = ListValue::GetChildren(schema_value); - vector result; - result.reserve(entries.size()); - for (auto &entry : entries) { - result.emplace_back(ParseParquetSchemaDefinition(context, entry, root_key_type)); - } - return result; -} - -vector ParquetColumnDefinition::FromSchemaMap(ClientContext &context, - const Value &schema_value) { - if (schema_value.type().id() != LogicalTypeId::MAP) { - throw InvalidInputException("'schema' expects a value of type MAP, not %s", - LogicalTypeIdToString(schema_value.type().id())); - } - return ParseParquetSchemaMap(context, schema_value, MapType::KeyType(schema_value.type()), true); + context.TryGetCurrentSetting("binary_as_string", binary_as_string); + context.TryGetCurrentSetting("debug_delta_only_variant_encoding_enabled", variant_legacy_encoding); } MultiFileColumnDefinition ParquetColumnDefinition::ToMultiFileColumnDefinition() const { @@ -1447,7 +1402,9 @@ ParquetReader::ParquetReader(ClientContext &context_p, OpenFileInfo file_p, Parq : BaseFileReader(std::move(file_p)), fs(CachingFileSystem::Get(context_p)), allocator(BufferAllocator::Get(context_p)), parquet_options(std::move(parquet_options_p)), projection_expressions(std::move(projection_expressions_p)) { - file_handle = fs.OpenFile(context_p, file, FileFlags::FILE_FLAGS_READ); + auto flags = FileFlags::FILE_FLAGS_READ; + flags.SetRequestSizing(RequestSizing::BY_READER); + file_handle = fs.OpenFile(context_p, file, flags); if (!file_handle->CanSeek()) { throw NotImplementedException( "Reading parquet files from a FIFO stream is not supported and cannot be efficiently supported since " @@ -1515,9 +1472,9 @@ ParquetReader::ParquetReader(ClientContext &context_p, OpenFileInfo file_p, Parq } bool ParquetReader::MetadataCacheEnabled(ClientContext &context) { - Value metadata_cache = false; + bool metadata_cache = false; context.TryGetCurrentSetting("parquet_metadata_cache", metadata_cache); - return metadata_cache.GetValue(); + return metadata_cache; } shared_ptr ParquetReader::GetMetadataCacheEntry(ClientContext &context, @@ -1736,7 +1693,7 @@ static bool TryGetNestedBloomFilterLeaf(ColumnReader &column_reader, const Expre // Handle MAP value extraction. if (leaf_reader->Type().id() == LogicalTypeId::MAP && function.Function().GetName() == "map_extract_value") { auto &entry_reader = leaf_reader->Cast().GetChildReader(); - if (entry_reader.Type().id() != LogicalTypeId::STRUCT) { + if (!StructType::IsStruct(entry_reader.Type())) { return false; } auto &struct_reader = entry_reader.Cast(); @@ -1748,7 +1705,7 @@ static bool TryGetNestedBloomFilterLeaf(ColumnReader &column_reader, const Expre } // Handle STRUCT type. - if (leaf_reader->Type().id() == LogicalTypeId::STRUCT) { + if (StructType::IsStruct(leaf_reader->Type())) { idx_t child_idx; if (!TryGetStructExtractChildIndex(function, child_idx)) { return false; @@ -1815,10 +1772,7 @@ static bool TryGetComparisonBloomFilterLeaf(ColumnReader &column_reader, const E return false; } - auto leaf_comparison = BoundComparisonExpression::Create( - comparison_type, make_uniq(leaf_reader->Type(), 0ULL), - make_uniq(constant->GetValue())); - leaf_filter = make_uniq(std::move(leaf_comparison)); + leaf_filter = ExpressionFilter::CreateComparisonFilter(comparison_type, constant->GetValue()); return true; } @@ -1871,6 +1825,55 @@ ColumnReader &ParquetReaderScanState::GetColumnReader(idx_t i) { return *column_readers[i]; } +FilterPropagateResult ParquetReader::CheckRowGroupFilterStatistics(ClientContext &context, + ParquetReaderScanState &state, + ColumnReader &column_reader, + const TableFilter &filter) { + auto &group = GetGroup(state); + auto schema_column_index = column_reader.ColumnIndex(); + auto prune_result = FilterPropagateResult::NO_PRUNING_POSSIBLE; + bool is_generated_column = schema_column_index >= group.columns.size(); + bool is_expression = column_reader.Schema().schema_type == ParquetColumnSchemaType::EXPRESSION; + auto stats = column_reader.Stats(state.group_index, group.columns); + if (stats) { + bool has_min_max = false; + if (!is_generated_column) { + has_min_max = group.columns[schema_column_index].meta_data.statistics.__isset.min_value && + group.columns[schema_column_index].meta_data.statistics.__isset.max_value; + } + if (!is_expression && !is_generated_column && has_min_max && + (column_reader.Type().id() == LogicalTypeId::FLOAT || column_reader.Type().id() == LogicalTypeId::DOUBLE) && + ParquetStatisticsUtils::CanHaveNaN(group.columns[schema_column_index].meta_data.statistics, + parquet_options.can_have_nan)) { + // floating point columns can have NaN values in addition to the min/max bounds defined in the file + // in order to do optimal pruning - we prune based on the [min, max] of the file followed by pruning + // based on nan + prune_result = CheckParquetFloatFilter(context, column_reader, + group.columns[schema_column_index].meta_data.statistics, filter); + } else { + auto &expr_filter = + ExpressionFilter::GetExpressionFilter(filter, "ParquetReader::CheckRowGroupFilterStatistics"); + prune_result = expr_filter.CheckStatistics(context, *stats); + } + } + return prune_result; +} + +bool ParquetReader::DynamicFiltersExcludeRowGroup(ClientContext &context, ParquetReaderScanState &state) { + for (auto &scan_filter : state.scan_filters) { + if (!ExpressionFilter::GetOptionalDynamicFilterData(scan_filter.filter)) { + continue; + } + auto &column_reader = state.GetColumnReader(MultiFileLocalIndex(scan_filter.filter_idx)); + auto prune_result = CheckRowGroupFilterStatistics(context, state, column_reader, scan_filter.filter); + if (prune_result == FilterPropagateResult::FILTER_ALWAYS_FALSE || + prune_result == FilterPropagateResult::FILTER_FALSE_OR_NULL) { + return true; + } + } + return false; +} + void ParquetReader::PrepareRowGroupBuffer(ClientContext &context, ParquetReaderScanState &state, idx_t i) { auto &group = GetGroup(state); auto col_idx = MultiFileLocalIndex(i); @@ -1895,32 +1898,7 @@ void ParquetReader::PrepareRowGroupBuffer(ClientContext &context, ParquetReaderS if (filter_entry) { auto &filter = *filter_entry; - auto schema_column_index = column_reader.ColumnIndex(); - auto prune_result = FilterPropagateResult::NO_PRUNING_POSSIBLE; - bool is_generated_column = schema_column_index >= group.columns.size(); - bool is_expression = column_reader.Schema().schema_type == ParquetColumnSchemaType::EXPRESSION; - auto stats = column_reader.Stats(state.group_index, group.columns); - if (stats) { - bool has_min_max = false; - if (!is_generated_column) { - has_min_max = group.columns[schema_column_index].meta_data.statistics.__isset.min_value && - group.columns[schema_column_index].meta_data.statistics.__isset.max_value; - } - if (!is_expression && !is_generated_column && has_min_max && - (column_reader.Type().id() == LogicalTypeId::FLOAT || - column_reader.Type().id() == LogicalTypeId::DOUBLE) && - parquet_options.can_have_nan) { - // floating point columns can have NaN values in addition to the min/max bounds defined in the file - // in order to do optimal pruning - we prune based on the [min, max] of the file followed by pruning - // based on nan - prune_result = CheckParquetFloatFilter( - context, column_reader, group.columns[schema_column_index].meta_data.statistics, filter); - } else { - auto &expr_filter = - ExpressionFilter::GetExpressionFilter(filter, "ParquetReader::PrepareRowGroupBuffer"); - prune_result = expr_filter.CheckStatistics(context, *stats); - } - } + auto prune_result = CheckRowGroupFilterStatistics(context, state, column_reader, filter); // check the bloom filter if present auto bloom_source = TryGetBloomFilterSource(column_reader); if (bloom_source) { @@ -1951,7 +1929,8 @@ void ParquetReader::PrepareRowGroupBuffer(ClientContext &context, ParquetReaderS if (prune_result == FilterPropagateResult::FILTER_ALWAYS_FALSE || prune_result == FilterPropagateResult::FILTER_FALSE_OR_NULL) { - // this effectively will skip this chunk + // this effectively will skip this chunk - count the skipped rows towards the progress + rows_read += row_group_num_rows - state.offset_in_group; state.offset_in_group = group.num_rows; return; } @@ -1991,6 +1970,7 @@ ParquetScanFilter::~ParquetScanFilter() { unique_ptr ParquetReader::OpenScanHandle(ClientContext &context) const { auto flags = FileFlags::FILE_FLAGS_READ; + flags.SetRequestSizing(RequestSizing::BY_READER); if (ShouldAndCanPrefetch(context, *file_handle)) { flags |= FileFlags::FILE_FLAGS_PARALLEL_ACCESS; if (file_handle->IsRemoteFile()) { @@ -2002,9 +1982,11 @@ unique_ptr ParquetReader::OpenScanHandle(ClientContext &conte void ParquetReader::PrepareReadAhead(ClientContext &context, GlobalTableFunctionState &) { // pre-open the scan handle while the file is opened, so the first InitializeScan skips its file-open - auto handle = OpenScanHandle(context); - lock_guard guard(prewarm_lock); - prewarmed_scan_handle = std::move(handle); + InitializeScanHandle(context); +} + +void ParquetReader::InitializeScanHandle(ClientContext &context) const { + CallOnce(scan_handle_initialized, [&] { shared_scan_handle = OpenScanHandle(context); }); } void ParquetReader::InitializeScan(ClientContext &context, ParquetReaderScanState &state, idx_t group_to_read) const { @@ -2017,14 +1999,7 @@ void ParquetReader::InitializeScan(ClientContext &context, ParquetReaderScanStat state.prefetch_mode = ShouldAndCanPrefetch(context, *file_handle); // all scan states share one handle (opened with parallel access), so open handles and // connections scale with the number of readers instead of the number of row-group jobs - lock_guard guard(prewarm_lock); - if (!shared_scan_handle) { - if (prewarmed_scan_handle) { - shared_scan_handle = std::move(prewarmed_scan_handle); - } else { - shared_scan_handle = OpenScanHandle(context); - } - } + InitializeScanHandle(context); state.file_handle = shared_scan_handle; } state.scan_filters.clear(); @@ -2083,6 +2058,11 @@ void ParquetReader::InitializeScan(ClientContext &context, ParquetReaderScanStat state.repeat_buf.resize(allocator, STANDARD_VECTOR_SIZE); } +shared_ptr ParquetReader::CreateMetadataReader(ClientContext &context, ParquetOptions parquet_options, + shared_ptr metadata) { + return shared_ptr(new ParquetReader(context, std::move(parquet_options), std::move(metadata))); +} + void ParquetReader::GetPartitionStats(vector &result) { if (!can_use_metadata_statistics) { return; @@ -2105,8 +2085,10 @@ struct ParquetPartitionRowGroup : public PartitionRowGroup { unique_ptr GetColumnStatistics(const StorageIndex &storage_index) override { const idx_t primary_index = storage_index.GetPrimaryIndex(); + if (primary_index >= root_schema->children.size()) { + return nullptr; + } D_ASSERT(metadata.row_groups.size() > row_group_idx); - D_ASSERT(root_schema->children.size() > primary_index); const auto &row_group = metadata.row_groups[row_group_idx]; const auto &column_schema = root_schema->children[primary_index]; @@ -2119,8 +2101,10 @@ struct ParquetPartitionRowGroup : public PartitionRowGroup { bool MinMaxIsExact(const StorageIndex &storage_index) override { const idx_t primary_index = storage_index.GetPrimaryIndex(); + if (primary_index >= root_schema->children.size()) { + return false; + } D_ASSERT(metadata.row_groups.size() > row_group_idx); - D_ASSERT(root_schema->children.size() > primary_index); // Special handle generated columns. const auto &column_schema = root_schema->children[primary_index]; @@ -2290,14 +2274,18 @@ ParquetPrefetchStrategy ParquetReader::RegisterRowGroupReads(ClientContext &cont // TODO: only need this if we have a deletion vector? state.group_offset = GetRowGroupOffset(*this, state.group_index); - uint64_t to_scan_compressed_bytes = 0; + auto &group = GetGroup(state); + // several readers can read the same column chunk (e.g. two fields of one variant column), count each chunk once + unordered_set scanned_column_chunks; for (idx_t i = 0; i < column_ids.size(); i++) { auto col_idx = MultiFileLocalIndex(i); PrepareRowGroupBuffer(context, state, col_idx); - to_scan_compressed_bytes += state.GetColumnReader(i).TotalCompressedSize(); + state.GetColumnReader(i).GatherColumnChunks(scanned_column_chunks); + } + uint64_t to_scan_compressed_bytes = 0; + for (auto column_chunk : scanned_column_chunks) { + to_scan_compressed_bytes += NumericCast(group.columns[column_chunk].meta_data.total_compressed_size); } - - auto &group = GetGroup(state); const bool row_group_skipped = state.offset_in_group == (idx_t)group.num_rows; if (row_group_skipped) { ++state.row_groups_skipped; @@ -2335,10 +2323,27 @@ ParquetPrefetchStrategy ParquetReader::RegisterRowGroupReads(ClientContext &cont if (filters) { if (state.prefetch_metrics.row_groups_executed == 0) { filters_look_unselective = true; - } else if (static_cast(state.prefetch_metrics.row_groups_with_matches) / - static_cast(state.prefetch_metrics.row_groups_executed) > - ParquetReaderPrefetchConfig::PREFETCH_FILTER_MINIMUM_MATCH_RATIO) { - filters_look_unselective = true; + if (scan_percentage > ParquetReaderPrefetchConfig::WHOLE_GROUP_PREFETCH_MINIMUM_SCAN) { + unordered_set filter_column_chunks; + for (auto &scan_filter : state.scan_filters) { + state.GetColumnReader(scan_filter.filter_idx.GetIndex()) + .GatherColumnChunks(filter_column_chunks); + } + uint64_t filter_compressed_bytes = 0; + for (auto column_chunk : filter_column_chunks) { + filter_compressed_bytes += + NumericCast(group.columns[column_chunk].meta_data.total_compressed_size); + } + filters_look_unselective = + filter_column_chunks.empty() || + static_cast(filter_compressed_bytes) / + static_cast(total_row_group_span) > + ParquetReaderPrefetchConfig::FIRST_GROUP_PREFETCH_MAXIMUM_FILTER_SCAN; + } + } else { + filters_look_unselective = static_cast(state.prefetch_metrics.row_groups_with_matches) / + static_cast(state.prefetch_metrics.row_groups_executed) > + ParquetReaderPrefetchConfig::PREFETCH_FILTER_MINIMUM_MATCH_RATIO; } } @@ -2518,6 +2523,11 @@ AsyncResult ParquetReader::Process(ClientContext &context, ParquetReaderScanStat const bool log_prefetch = Logger::Get(context).ShouldLog(ParquetPrefetchLogType::NAME, ParquetPrefetchLogType::LEVEL); const idx_t group_num_rows = GetGroup(state).num_rows; + // Queued row groups may predate the current dynamic filter. Do not revisit a partially decoded chunk. + if (state.offset_in_group == 0 && !state.resuming_payload && DynamicFiltersExcludeRowGroup(context, state)) { + rows_read += group_num_rows; + state.offset_in_group = group_num_rows; + } auto scan_count = MinValue(STANDARD_VECTOR_SIZE, group_num_rows - state.offset_in_group); if (!state.resuming_payload) { result.SetChildCardinality(scan_count); diff --git a/src/duckdb/extension/parquet/parquet_shredding.cpp b/src/duckdb/extension/parquet/parquet_shredding.cpp index 3c046cba2..903a03020 100644 --- a/src/duckdb/extension/parquet/parquet_shredding.cpp +++ b/src/duckdb/extension/parquet/parquet_shredding.cpp @@ -72,24 +72,23 @@ static ShreddingType ConvertShreddingTypeRecursive(const LogicalType &type) { return ShreddingType(LogicalType(LogicalTypeId::ANY)); } if (!type.IsNested()) { + // the values are written without further conversion or validation, so types with an alias (e.g., JSON) and + // types without a Parquet typed_value representation cannot be shredded on + if (type.HasAlias() || type.id() == LogicalTypeId::BIT || type.id() == LogicalTypeId::BIGNUM) { + throw BinderException("VARIANT cannot be shredded on type %s in Parquet", type.ToString()); + } return ShreddingType(type); } switch (type.id()) { - case LogicalTypeId::STRUCT: { + case LogicalTypeId::STRUCT: + case LogicalTypeId::LIST: { ShreddingType res(type); - auto &children = StructType::GetChildTypes(type); - for (auto &entry : children) { + for (auto &entry : LogicalType::GetNamedChildTypes(type)) { res.AddChild(entry.first, ConvertShreddingTypeRecursive(entry.second)); } return res; } - case LogicalTypeId::LIST: { - ShreddingType res(type); - const auto &child = ListType::GetChildType(type); - res.AddChild("element", ConvertShreddingTypeRecursive(child)); - return res; - } default: break; } diff --git a/src/duckdb/extension/parquet/parquet_statistics.cpp b/src/duckdb/extension/parquet/parquet_statistics.cpp index b6913db14..62ff20a12 100644 --- a/src/duckdb/extension/parquet/parquet_statistics.cpp +++ b/src/duckdb/extension/parquet/parquet_statistics.cpp @@ -22,14 +22,12 @@ #include "duckdb/storage/statistics/list_stats.hpp" #include "duckdb/planner/expression_iterator.hpp" #include "duckdb/planner/filter/expression_filter.hpp" -#include "duckdb/planner/filter/table_filter_functions.hpp" #include "duckdb/planner/expression/bound_comparison_expression.hpp" #include "duckdb/planner/expression/bound_conjunction_expression.hpp" #include "duckdb/planner/expression/bound_constant_expression.hpp" #include "duckdb/planner/expression/bound_operator_expression.hpp" #include "duckdb/planner/expression/bound_reference_expression.hpp" #include "reader/uuid_column_reader.hpp" -#include "duckdb/common/type_visitor.hpp" #include "column_reader.hpp" #include "duckdb/common/allocator.hpp" #include "duckdb/common/constants.hpp" @@ -111,6 +109,13 @@ static unique_ptr CreateFloatingPointStats(const LogicalType &ty return stats.ToUnique(); } +bool ParquetStatisticsUtils::CanHaveNaN(const duckdb_parquet::Statistics &parquet_stats, bool can_have_nan) { + if (parquet_stats.__isset.nan_count) { + return parquet_stats.nan_count != 0; + } + return can_have_nan; +} + Value ParquetStatisticsUtils::ConvertValue(const LogicalType &type, const ParquetColumnSchema &schema_ele, const std::string &stats) { string error; @@ -121,6 +126,17 @@ Value ParquetStatisticsUtils::ConvertValue(const LogicalType &type, const Parque } return std::move(*result); } +//! Statistics values that do not fit in the width of the decimal are ignored +template +static Value ParquetDecimalStatsValue(T value, uint8_t width, uint8_t scale) { + const auto max_value = Hugeint::POWERS_OF_TEN[width]; + const auto hugeint_value = hugeint_t(value); + if (hugeint_value >= max_value || hugeint_value <= -max_value) { + return Value(); + } + return Value::DECIMAL(value, width, scale); +} + Value ParquetStatisticsUtils::ConvertValueInternal(const LogicalType &type, const ParquetColumnSchema &schema_ele, const std::string &stats) { auto stats_data = const_data_ptr_cast(stats.c_str()); @@ -195,25 +211,25 @@ Value ParquetStatisticsUtils::ConvertValueInternal(const LogicalType &type, cons if (stats.size() != sizeof(int32_t)) { throw InvalidInputException("Incorrect stats size for type %s", type.ToString()); } - return Value::DECIMAL(Load(stats_data), width, scale); + return ParquetDecimalStatsValue(Load(stats_data), width, scale); case ParquetExtraTypeInfo::DECIMAL_INT64: if (stats.size() != sizeof(int64_t)) { throw InvalidInputException("Incorrect stats size for type %s", type.ToString()); } - return Value::DECIMAL(Load(stats_data), width, scale); + return ParquetDecimalStatsValue(Load(stats_data), width, scale); case ParquetExtraTypeInfo::DECIMAL_BYTE_ARRAY: switch (type.InternalType()) { case PhysicalType::INT16: - return Value::DECIMAL( + return ParquetDecimalStatsValue( ParquetDecimalUtils::ReadDecimalValue(stats_data, stats.size(), schema_ele), width, scale); case PhysicalType::INT32: - return Value::DECIMAL( + return ParquetDecimalStatsValue( ParquetDecimalUtils::ReadDecimalValue(stats_data, stats.size(), schema_ele), width, scale); case PhysicalType::INT64: - return Value::DECIMAL( + return ParquetDecimalStatsValue( ParquetDecimalUtils::ReadDecimalValue(stats_data, stats.size(), schema_ele), width, scale); case PhysicalType::INT128: - return Value::DECIMAL( + return ParquetDecimalStatsValue( ParquetDecimalUtils::ReadDecimalValue(stats_data, stats.size(), schema_ele), width, scale); default: @@ -245,32 +261,30 @@ Value ParquetStatisticsUtils::ConvertValueInternal(const LogicalType &type, cons } switch (schema_ele.type_info) { case ParquetExtraTypeInfo::UNIT_MS: + if (!ParquetTimeIsValid(val, Interval::MSECS_PER_SEC * Interval::SECS_PER_DAY)) { + return Value(); + } return Value::TIME(Time::FromTimeMs(val)); - case ParquetExtraTypeInfo::UNIT_NS: - return Value::TIME(Time::FromTimeNs(val)); case ParquetExtraTypeInfo::UNIT_MICROS: default: + if (!ParquetTimeIsValid(val, Interval::MICROS_PER_DAY)) { + return Value(); + } return Value::TIME(dtime_t(val)); } } case LogicalTypeId::TIME_NS: { - int64_t val; - if (stats.size() == sizeof(int32_t)) { - val = Load(stats_data); - } else if (stats.size() == sizeof(int64_t)) { - val = Load(stats_data); - } else { + if (stats.size() != sizeof(int64_t)) { throw InvalidInputException("Incorrect stats size for type TIME_NS"); } - switch (schema_ele.type_info) { - case ParquetExtraTypeInfo::UNIT_MS: - return Value::TIME_NS(ParquetMsIntToTimeNs(NumericCast(val))); - case ParquetExtraTypeInfo::UNIT_NS: - return Value::TIME_NS(ParquetIntToTimeNs(val)); - case ParquetExtraTypeInfo::UNIT_MICROS: - default: - return Value::TIME_NS(dtime_ns_t(val)); + if (schema_ele.type_info != ParquetExtraTypeInfo::UNIT_NS) { + throw InternalException("TIME_NS requires nanosecond type info"); + } + const auto nanos = Load(stats_data); + if (!ParquetTimeIsValid(nanos, Interval::NANOS_PER_DAY)) { + return Value(); } + return Value::TIME_NS(ParquetIntToTimeNs(nanos)); } case LogicalTypeId::TIME_TZ: { int64_t val; @@ -308,9 +322,6 @@ Value ParquetStatisticsUtils::ConvertValueInternal(const LogicalType &type, cons case ParquetExtraTypeInfo::UNIT_MS: timestamp_value = ParquetTimestampMsToTimestamp(val); break; - case ParquetExtraTypeInfo::UNIT_NS: - timestamp_value = ParquetTimestampNsToTimestamp(val); - break; case ParquetExtraTypeInfo::UNIT_MICROS: default: timestamp_value = timestamp_t(val); @@ -349,7 +360,7 @@ Value ParquetStatisticsUtils::ConvertValueInternal(const LogicalType &type, cons } } if (type.id() == LogicalTypeId::TIMESTAMP_TZ_NS) { - return Value::TIMESTAMPTZNS(timestamp_tz_ns_t(timestamp_value)); + return Value::TIMESTAMPTZNS(timestamp_tz_ns_t(timestamp_value.value)); } return Value::TIMESTAMPNS(timestamp_value); } @@ -438,7 +449,7 @@ static void ConvertShreddedStats(BaseStatistics &result, optional_ptr ParquetStatisticsUtils::TransformColumnStatistics(con return row_group_stats; } // Structs are handled differently (they dont have stats) - if (type.id() == LogicalTypeId::STRUCT) { + if (StructType::IsStruct(type)) { auto struct_stats = StructStats::CreateUnknown(type); // Recurse into child readers for (idx_t i = 0; i < schema.children.size(); i++) { @@ -650,10 +659,7 @@ unique_ptr ParquetStatisticsUtils::TransformColumnStatistics(con //! field is missing) return nullptr; } - auto shredding_type = TypeVisitor::VisitReplace(logical_type, [](const LogicalType &type) { - return LogicalType::STRUCT({{"typed_value", type}, {"untyped_value_index", LogicalType::UINTEGER}}); - }); - auto variant_stats = VariantStats::CreateShredded(shredding_type); + auto variant_stats = VariantStats::CreateShredded(VariantStats::GetShreddingType(logical_type)); //! Take the root stats auto &shredded_stats = VariantStats::GetShreddedStats(variant_stats); @@ -662,7 +668,9 @@ unique_ptr ParquetStatisticsUtils::TransformColumnStatistics(con //! Convert the root 'value' -> 'untyped_value_index' auto &value = schema.children[1]; - D_ASSERT(value.name == "value"); + if (schema.children[0].name != "metadata" || value.name != "value") { + return nullptr; + } auto value_stats = ParquetStatisticsUtils::TransformColumnStatistics(value, columns, can_have_nan); //! Best-effort: nodes whose stats can't be converted are left UNKNOWN (not fully shredded) rather //! than discarding the statistics for the entire variant column @@ -699,24 +707,6 @@ unique_ptr ParquetStatisticsUtils::TransformColumnStatistics(con return row_group_stats; } -// Optional filters store the expression used for pruning in their bind data. -static optional_ptr GetOptionalFilterChild(const Expression &expr) { - if (expr.GetExpressionClass() != ExpressionClass::BOUND_FUNCTION) { - return nullptr; - } - auto &func = expr.Cast(); - if (!func.BindInfo()) { - return nullptr; - } - if (func.Function().GetName() == OptionalFilterScalarFun::NAME) { - return func.BindInfo()->Cast().child_filter_expr.get(); - } - if (func.Function().GetName() == SelectivityOptionalFilterScalarFun::NAME) { - return func.BindInfo()->Cast().child_filter_expr.get(); - } - return nullptr; -} - static bool UsesNormalizedIntervalHash(ParquetBloomFilterHashStrategy hash_strategy) { return hash_strategy == ParquetBloomFilterHashStrategy::NORMALIZED_INTERVAL_V1; } @@ -799,7 +789,7 @@ static bool HasFilterConstants(const Expression &expr, ParquetBloomFilterHashStr if (GetBloomFilterInExpression(expr, hash_strategy)) { return true; } - auto optional_filter_child = GetOptionalFilterChild(expr); + auto optional_filter_child = ExpressionFilter::GetOptionalFilterChild(expr); if (optional_filter_child) { return HasFilterConstants(*optional_filter_child, hash_strategy); } @@ -1035,7 +1025,7 @@ static bool ApplyBloomFilter(const Expression &expr, ParquetBloomFilter &bloom_f } return true; } - auto optional_filter_child = GetOptionalFilterChild(expr); + auto optional_filter_child = ExpressionFilter::GetOptionalFilterChild(expr); if (optional_filter_child) { return ApplyBloomFilter(*optional_filter_child, bloom_filter, schema, hash_strategy); } diff --git a/src/duckdb/extension/parquet/parquet_timestamp.cpp b/src/duckdb/extension/parquet/parquet_timestamp.cpp index c95b34a11..1cddc882d 100644 --- a/src/duckdb/extension/parquet/parquet_timestamp.cpp +++ b/src/duckdb/extension/parquet/parquet_timestamp.cpp @@ -1,5 +1,7 @@ #include "parquet_timestamp.hpp" +#include "duckdb/common/operator/add.hpp" +#include "duckdb/common/operator/multiply.hpp" #include "duckdb/common/types/date.hpp" #include "duckdb/common/types/time.hpp" #include "duckdb/common/types/timestamp.hpp" @@ -28,9 +30,25 @@ static int64_t ImpalaTimestampToMicroseconds(const Int96 &impala_timestamp) { } static int64_t ImpalaTimestampToNanoseconds(const Int96 &impala_timestamp) { + int64_t result; int64_t days_since_epoch = ImpalaTimestampToDays(impala_timestamp); auto nanoseconds = Load(const_data_ptr_cast(impala_timestamp.value)); - return days_since_epoch * NANOSECONDS_PER_DAY + nanoseconds; + int64_t day_nanoseconds; + if (days_since_epoch < 0) { + // Don't saturate the minimum value by going past it + if (!TryMultiplyOperator::Operation(days_since_epoch + 1, NANOSECONDS_PER_DAY, day_nanoseconds) || + !TryAddOperator::Operation(day_nanoseconds, nanoseconds - NANOSECONDS_PER_DAY, result)) { + // out of range for TIMESTAMP_NS - saturate to -infinity + return timestamp_ns_t::ninfinity().value; + } + return result; + } + if (!TryMultiplyOperator::Operation(days_since_epoch, NANOSECONDS_PER_DAY, day_nanoseconds) || + !TryAddOperator::Operation(day_nanoseconds, nanoseconds, result)) { + // out of range for TIMESTAMP_NS - saturate to +infinity + return timestamp_ns_t::infinity().value; + } + return result; } timestamp_ns_t ImpalaTimestampToTimestampNS(const Int96 &raw_ts) { @@ -44,6 +62,14 @@ timestamp_t ImpalaTimestampToTimestamp(const Int96 &raw_ts) { return Timestamp::FromEpochMicroSeconds(impala_us); } +date_t ImpalaTimestampToDate(const Int96 &raw_ts) { + return date_t(NumericCast(ImpalaTimestampToDays(raw_ts))); +} + +dtime_ns_t ImpalaTimestampToTimeNs(const Int96 &raw_ts) { + return dtime_ns_t(Load(const_data_ptr_cast(raw_ts.value))); +} + Int96 TimestampToImpalaTimestamp(timestamp_t &ts) { int32_t hour, min, sec, micros; Time::Convert(Timestamp::GetTime(ts), hour, min, sec, micros); @@ -94,14 +120,6 @@ timestamp_ns_t ParquetTimestampNsToTimestampNs(const int64_t &raw_ns) { return result; } -timestamp_t ParquetTimestampNsToTimestamp(const int64_t &raw_ts) { - timestamp_t input(raw_ts); - if (!input.IsFinite()) { - return input; - } - return Timestamp::FromEpochNanoSeconds(raw_ts); -} - date_t ParquetIntToDate(const int32_t &raw_date) { return date_t(raw_date); } @@ -116,27 +134,29 @@ static T ParquetWrapTime(const T &raw, const T day) { return modulus + (modulus < 0) * day; } -dtime_t ParquetMsIntToTime(const int32_t &raw_millis) { - return Time::FromTimeMs(raw_millis); -} - -dtime_t ParquetIntToTime(const int64_t &raw_micros) { - return dtime_t(raw_micros); +bool ParquetTimeIsValid(const int64_t &raw, const int64_t day) { + return raw >= 0 && raw <= day; } -dtime_t ParquetNsIntToTime(const int64_t &raw_nanos) { - return Time::FromTimeNs(raw_nanos); +template +static void CheckParquetTime(const T &raw, const int64_t day) { + if (!ParquetTimeIsValid(raw, day)) { + throw InvalidInputException("Invalid TIME value %d in Parquet file - TIME values must be within a day", raw); + } } -dtime_ns_t ParquetMsIntToTimeNs(const int32_t &raw_millis) { - return dtime_ns_t(Interval::NANOS_PER_MSEC * raw_millis); +dtime_t ParquetMsIntToTime(const int32_t &raw_millis) { + CheckParquetTime(raw_millis, Interval::MSECS_PER_SEC * Interval::SECS_PER_DAY); + return Time::FromTimeMs(raw_millis); } -dtime_ns_t ParquetUsIntToTimeNs(const int64_t &raw_micros) { - return dtime_ns_t(raw_micros * Interval::NANOS_PER_MICRO); +dtime_t ParquetIntToTime(const int64_t &raw_micros) { + CheckParquetTime(raw_micros, Interval::MICROS_PER_DAY); + return dtime_t(raw_micros); } dtime_ns_t ParquetIntToTimeNs(const int64_t &raw_nanos) { + CheckParquetTime(raw_nanos, Interval::NANOS_PER_DAY); return dtime_ns_t(raw_nanos); } diff --git a/src/duckdb/extension/parquet/parquet_writer.cpp b/src/duckdb/extension/parquet/parquet_writer.cpp index 1eef8853e..3d0700ec9 100644 --- a/src/duckdb/extension/parquet/parquet_writer.cpp +++ b/src/duckdb/extension/parquet/parquet_writer.cpp @@ -7,6 +7,7 @@ #include "parquet_crypto.hpp" #include "parquet_decimal_utils.hpp" #include "parquet_shredding.hpp" +#include "parquet_timestamp.hpp" #include "resizable_buffer.hpp" #include "duckdb/parser/keyword_helper.hpp" #include "duckdb/common/serializer/async_file_writer.hpp" @@ -465,6 +466,7 @@ struct ColumnStatsUnifier { idx_t column_size_bytes = 0; bool can_have_nan = false; bool has_nan = false; + idx_t nan_count = 0; unique_ptr geo_stats; @@ -477,12 +479,19 @@ struct ColumnStatsUnifier { class ParquetStatsAccumulator { public: + struct NestedColumnStats { + string column_name; + idx_t null_count = 0; + idx_t num_values = 0; + }; + vector> stats_unifiers; + map nested_stats; }; ParquetWriteTransformData::ParquetWriteTransformData(ClientContext &context, const vector &types, vector> expressions_p) - : buffer(context, types, ColumnDataAllocatorType::BUFFER_MANAGER_ALLOCATOR), types(std::move(types)), + : buffer(context, types, ColumnDataAllocatorType::BUFFER_MANAGER_ALLOCATOR), types(types), expressions(std::move(expressions_p)), executor(context, expressions) { chunk.Initialize(buffer.GetAllocator(), this->types); } @@ -797,9 +806,11 @@ void ParquetWriter::PrepareRowGroup(ColumnDataCollection &raw_buffer, PreparedRo static void ValidateOffsetInFile(const string &filename, idx_t col_idx, idx_t file_length, idx_t offset, const string &offset_name) { if (offset >= file_length) { + // LCOV_EXCL_START throw IOException("File '%s': metadata is corrupt. Column %d has invalid " "%s (offset=%llu file_size=%llu).", filename, col_idx, offset_name, offset, file_length); + // LCOV_EXCL_STOP } } @@ -813,18 +824,22 @@ static void ValidateColumnOffsets(const string &filename, idx_t file_length, con ValidateOffsetInFile(filename, i, file_length, col_chunk.meta_data.dictionary_page_offset, "dictionary page offset"); if (NumericCast(col_chunk.meta_data.dictionary_page_offset) >= col_start) { + // LCOV_EXCL_START throw IOException("Parquet file '%s': metadata is corrupt. Dictionary " "page (offset=%llu) must come before any data pages (offset=%llu).", filename, col_chunk.meta_data.dictionary_page_offset, col_start); + // LCOV_EXCL_STOP } col_start = col_chunk.meta_data.dictionary_page_offset; } auto col_len = NumericCast(col_chunk.meta_data.total_compressed_size); auto col_end = col_start + col_len; if (col_end <= 0 || col_end > file_length) { + // LCOV_EXCL_START throw IOException("Parquet file '%s': metadata is corrupt. Column %llu has " "invalid column offsets (offset=%llu, size=%llu, file_size=%llu).", filename, i, col_start, col_len, file_length); + // LCOV_EXCL_STOP } } } @@ -919,6 +934,15 @@ struct NumericStatsUnifier : public BaseNumericStatsUnifier { } }; +struct TimeTZStatsUnifier : public BaseNumericStatsUnifier { + string StatsToString(const string &stats) override { + if (stats.empty()) { + return string(); + } + return Value::TIMETZ(ParquetIntToTimeTZ(Load(const_data_ptr_cast(stats.data())))).ToString(); + } +}; + template struct DecimalStatsUnifier : public NumericStatsUnifier { DecimalStatsUnifier(uint8_t width, uint8_t scale) : width(width), scale(scale) { @@ -1117,7 +1141,7 @@ static unique_ptr GetBaseStatsUnifier(const LogicalType &typ case LogicalTypeId::TIMESTAMP_NS: return make_uniq>(); case LogicalTypeId::TIME_TZ: - return make_uniq>(); + return make_uniq(); case LogicalTypeId::UINTEGER: return make_uniq>(); case LogicalTypeId::UBIGINT: @@ -1172,7 +1196,7 @@ static bool IsVariantMetadataField(const ColumnWriter &writer) { return name == "metadata"; } -static void GetStatsUnifier(const ColumnWriter &column_writer, vector> &unifiers, +static void GetStatsUnifier(const ColumnWriter &column_writer, ParquetStatsAccumulator &accumulator, string base_name = string()) { auto &schema = column_writer.Schema(); if (schema.repetition_type != duckdb_parquet::FieldRepetitionType::REPEATED) { @@ -1192,11 +1216,25 @@ static void GetStatsUnifier(const ColumnWriter &column_writer, vectorCast(); unifier->variant_type = variant_writer.TransformedType().ToString(); } - unifiers.push_back(std::move(unifier)); + accumulator.stats_unifiers.push_back(std::move(unifier)); return; } + if (schema.repetition_type != duckdb_parquet::FieldRepetitionType::REPEATED) { + accumulator.nested_stats[column_writer.SchemaIndex()].column_name = base_name; + } for (auto &child_writer : children) { - GetStatsUnifier(*child_writer, unifiers, base_name); + GetStatsUnifier(*child_writer, accumulator, base_name); + } +} + +void ParquetWriter::FlushNestedColumnStats(idx_t schema_idx, idx_t null_count, idx_t num_values) { + if (!written_stats) { + return; + } + auto entry = stats_accumulator->nested_stats.find(schema_idx); + if (entry != stats_accumulator->nested_stats.end()) { + entry->second.null_count += null_count; + entry->second.num_values += num_values; } } @@ -1212,7 +1250,9 @@ void ParquetWriter::FlushColumnStats(idx_t col_idx, duckdb_parquet::ColumnChunk if (writer_stats) { stats_unifier->can_have_nan = writer_stats->CanHaveNaN(); has_nan = writer_stats->HasNaN(); - stats_unifier->has_nan = has_nan; + // this is called once per row group: accumulate, so NaNs in earlier row groups are not lost + stats_unifier->has_nan = stats_unifier->has_nan || has_nan; + stats_unifier->nan_count += writer_stats->GetNaNCount(); } if (column.meta_data.__isset.statistics) { if (has_nan && writer_stats->HasStats()) { @@ -1248,6 +1288,14 @@ void ParquetWriter::FlushColumnStats(idx_t col_idx, duckdb_parquet::ColumnChunk void ParquetWriter::GatherWrittenStatistics() { written_stats->row_count = file_meta_data.num_rows; + for (auto &entry : stats_accumulator->nested_stats) { + auto &stats = entry.second; + case_insensitive_map_t column_stats; + column_stats["null_count"] = Value::UBIGINT(stats.null_count); + column_stats["num_values"] = Value::UBIGINT(stats.num_values); + written_stats->column_statistics.emplace(stats.column_name, std::move(column_stats)); + } + // finalize the min/max values and write to column stats for (idx_t c = 0; c < stats_accumulator->stats_unifiers.size(); c++) { auto &stats_unifier = stats_accumulator->stats_unifiers[c]; @@ -1274,6 +1322,7 @@ void ParquetWriter::GatherWrittenStatistics() { } if (stats_unifier->can_have_nan) { column_stats["has_nan"] = Value::BOOLEAN(stats_unifier->has_nan); + column_stats["nan_count"] = Value::UBIGINT(stats_unifier->nan_count); } if (stats_unifier->geo_stats) { const auto &bbox = stats_unifier->geo_stats->extent; @@ -1350,7 +1399,7 @@ void ParquetWriter::InitializeStatsUnifiers() { return; } for (auto &column_writer : column_writers) { - GetStatsUnifier(*column_writer, stats_accumulator->stats_unifiers); + GetStatsUnifier(*column_writer, *stats_accumulator); } } diff --git a/src/duckdb/extension/parquet/read_single_parquet_file.cpp b/src/duckdb/extension/parquet/read_single_parquet_file.cpp new file mode 100644 index 000000000..d88450a5c --- /dev/null +++ b/src/duckdb/extension/parquet/read_single_parquet_file.cpp @@ -0,0 +1,576 @@ +#include "duckdb/common/multi_file/multi_file_list.hpp" +#include "duckdb/common/multi_file/table_function_multi_file.hpp" +#include "parquet_multi_file_info.hpp" +#include "duckdb/common/enum_util.hpp" +#include "parquet_reader.hpp" +#include "duckdb/common/mutex.hpp" +#include "duckdb/common/once_flag.hpp" +#include "duckdb/planner/expression/bound_function_expression.hpp" +#include "duckdb/planner/operator/logical_get.hpp" + +namespace duckdb { + +//! Bind data of read_single_parquet_file - the single file that is read, and how it is read +struct ReadSingleParquetFileData : public TableFunctionData { + OpenFileInfo file; + ParquetOptions options; + //! The names/types this file is read with + vector parquet_names; + vector parquet_types; + //! The columns of the file, with their nested structure and field ids + vector columns; + //! The metadata of the file, read during binding and reused when the file is scanned + shared_ptr metadata; + //! The number of rows of the file, if it was read during binding + optional_idx cardinality; + //! The expressions the reader evaluates on columns instead of reading them (e.g. their length for strlen) + unordered_map projection_expressions; + + //! Read the given column as the result of an expression on it - the column then has the type of that result + void AddProjectionExpression(idx_t column_idx, const ParquetReaderProjectionExpression &expression) { + projection_expressions[column_idx] = expression; + if (column_idx < parquet_types.size()) { + parquet_types[column_idx] = expression.return_type; + } + if (column_idx < columns.size()) { + columns[column_idx].type = expression.return_type; + } + } + + unique_ptr Copy() const override { + // the copy does not take over the reader the bind opened - its scans open the file themselves + auto result = make_uniq(); + result->file = file; + result->options = options; + result->parquet_names = parquet_names; + result->parquet_types = parquet_types; + result->columns = columns; + result->metadata = metadata; + result->cardinality = cardinality; + result->projection_expressions = projection_expressions; + return std::move(result); + } + + //! The reader the bind opened for this file, handed to the first scan of this bind data so that the file does + //! not have to be opened again. Only set when the bind data is used to read the file + shared_ptr TakeBindReader() const { + lock_guard guard(bind_reader_lock); + return std::move(bind_reader); + } + void SetBindReader(shared_ptr reader) { + lock_guard guard(bind_reader_lock); + bind_reader = std::move(reader); + } + //! A reader over the metadata of the file, created on first use. The row groups it describes refer to the schema + //! it holds, so it is kept for as long as this bind data is + ParquetReader &GetMetadataReader(ClientContext &context) const { + CallOnce(metadata_reader_initialized, + [&] { metadata_reader = ParquetReader::CreateMetadataReader(context, options, metadata); }); + return *metadata_reader; + } + +private: + //! Guards the transfer of the bind reader + mutable mutex bind_reader_lock; + mutable shared_ptr bind_reader; + mutable OnceFlag metadata_reader_initialized; + mutable shared_ptr metadata_reader; +}; + +struct ReadSingleParquetFileGlobalState : public GlobalTableFunctionState { + explicit ReadSingleParquetFileGlobalState(optional_ptr op) : state(op) { + } + +public: + idx_t MaxThreads() const override { + return max_threads; + } + +public: + //! The file that is read - declared before the state below, which refers to it + shared_ptr reader; + ParquetReadGlobalState state; + //! Handing out the next row group is done single-threadedly + mutex lock; + idx_t max_threads = 1; +}; + +struct ReadSingleParquetFileLocalState : public LocalTableFunctionState { + ParquetReadLocalState state; + //! Whether our caller claims the row groups we read - see table_function_claim_batch_t + bool claimed_externally = false; + //! Whether this thread holds a row group that it has not finished scanning + bool has_row_group = false; +}; + +static unique_ptr ReadSingleParquetFileBind(ClientContext &context, TableFunctionBindInput &input, + vector &return_types, + vector &names) { + auto &file_input = TableFunctionFileBindInput::Get(input); + auto result = make_uniq(); + if (input.inputs[0].IsNull()) { + throw BinderException("read_single_parquet_file requires a non-NULL file name"); + } + result->file = TableFunctionFileBindInput::GetFile(input); + + // the named parameters of this function are those of the multi-file parquet reader + ParquetMultiFileInfo interface; + MultiFileOptions file_options; + if (file_input.multi_file_options) { + file_options = *file_input.multi_file_options; + } + ParquetFileReaderOptions options(context); + for (auto &kv : input.named_parameters) { + interface.ParseOption(context, kv.first, kv.second, file_options, options); + } + result->options = std::move(options.options); + + // read the metadata of the file to determine its schema - when this file was bound before, to determine the + // schema of the scan it is part of, its metadata is taken from that bind instead of being read again + shared_ptr known_metadata; + if (file_input.file_bind_data) { + auto &previous = file_input.file_bind_data->Cast(); + if (previous.file.path == result->file.path) { + known_metadata = previous.metadata; + } + } + auto reader = make_shared_ptr(context, result->file, result->options, std::move(known_metadata)); + for (auto &column : reader->GetColumns()) { + names.push_back(column.name); + return_types.push_back(column.type); + } + result->columns = reader->GetColumns(); + result->metadata = reader->metadata; + result->cardinality = reader->NumRows(); + result->parquet_names = names; + result->parquet_types = return_types; + if (file_input.expected_bind_data) { + // expressions pushed into the scan after the schema was bound are read from this file in the same way + auto &expected = file_input.expected_bind_data->Cast(); + if (expected.file.path == result->file.path) { + for (auto &entry : expected.projection_expressions) { + result->AddProjectionExpression(entry.first, entry.second); + if (entry.first < return_types.size()) { + return_types[entry.first] = entry.second.return_type; + } + } + } + } + if (!file_input.schema_only) { + // this file is read with this bind data - keep the reader so the scan does not open the file a second time + result->SetBindReader(std::move(reader)); + } + return std::move(result); +} + +//! The virtual columns the parquet reader can produce for a file +static virtual_column_map_t ReadSingleParquetFileVirtualColumns(ClientContext &context, + optional_ptr bind_data); + +//! The columns of this file, as the parquet reader describes them +static vector ReadSingleParquetFileColumns(ClientContext &context, + const FunctionData &bind_data) { + return bind_data.Cast().columns; +} + +static unique_ptr ReadSingleParquetFileInitGlobal(ClientContext &context, + TableFunctionInitInput &input) { + auto &file_input = TableFunctionFileInitInput::Get(input); + auto &parquet_data = input.bind_data->Cast(); + auto result = make_uniq(input.op); + + result->reader = parquet_data.TakeBindReader(); + if (!result->reader) { + result->reader = + make_shared_ptr(context, parquet_data.file, parquet_data.options, parquet_data.metadata); + } + auto &reader = *result->reader; + for (auto &entry : parquet_data.projection_expressions) { + // the reader may have been opened before these expressions were pushed into the scan + auto column_idx = entry.first; + reader.projection_expressions[column_idx] = entry.second; + if (column_idx < reader.columns.size()) { + reader.columns[column_idx].type = entry.second.return_type; + } + if (column_idx < reader.root_schema->children.size()) { + reader.root_schema->children[column_idx].type = entry.second.return_type; + } + } + auto virtual_column_types = ReadSingleParquetFileVirtualColumns(context, nullptr); + // perform projection pushdown - the reader emits the columns in the order they are requested + vector column_indexes; + for (auto &column_index : input.column_indexes) { + const auto col_id = column_index.GetPrimaryIndex(); + // a virtual column is either projected under its own id, or - when our caller reads several files - under + // an index of its own past the columns of this file + auto virtual_id = col_id; + if (file_input.virtual_columns) { + auto entry = file_input.virtual_columns->find(col_id); + if (entry != file_input.virtual_columns->end()) { + virtual_id = entry->second; + } + } + if (!IsVirtualColumn(virtual_id)) { + reader.column_ids.push_back(MultiFileLocalColumnId(col_id)); + column_indexes.push_back(column_index); + continue; + } + // the reader produces a virtual column as a column of its own, appended after the ones the file has + auto virtual_entry = virtual_column_types.find(virtual_id); + if (virtual_entry == virtual_column_types.end()) { + throw InternalException("Unsupported virtual column id %d for read_single_parquet_file", virtual_id); + } + const auto local_id = reader.columns.size(); + reader.column_ids.push_back(MultiFileLocalColumnId(local_id)); + column_indexes.push_back(column_index.RemapRootIndex(local_id)); + reader.columns.emplace_back(virtual_entry->second.name.GetIdentifierName(), virtual_entry->second.type); + reader.AddVirtualColumn(virtual_id); + } + reader.column_indexes = std::move(column_indexes); + if (input.filters) { + // the filters are evaluated by the reader itself, so that row groups can be pruned on them + reader.filters = input.filters->Copy(); + } + if (file_input.filter_global_indices) { + reader.filter_global_indices = *file_input.filter_global_indices; + } + if (file_input.expression_map) { + // our caller could not express some of its filters in the types this file stores its columns as - the + // reader evaluates these expressions on them first, and applies the filters to the result + for (auto &entry : *file_input.expression_map) { + vector expression_column_indexes = entry.second.column_indexes; + reader.expression_map.emplace(entry.first, BaseFileReaderExpression(entry.second.expression->Copy(), + std::move(expression_column_indexes))); + } + } + // NOTE: the parquet reader does not read columns as a type other than the one they have in the file, so + // "supports_cast_map" is not set and our caller casts the columns it needs converted itself + if (file_input.deletion_filter) { + // our caller has deleted rows from this file - the reader skips them while scanning. The filter stays owned + // by the caller, which outlives this scan + reader.deletion_filter = make_uniq(*file_input.deletion_filter); + } + result->max_threads = MaxValue(reader.NumRowGroups(), 1); + return std::move(result); +} + +static unique_ptr ReadSingleParquetFileInitLocal(ExecutionContext &context, + TableFunctionInitInput &input, + GlobalTableFunctionState *global_state) { + return make_uniq(); +} + +//! Assign the next row group of the file to this thread +static bool ClaimNextRowGroup(ClientContext &context, ReadSingleParquetFileGlobalState &gstate, + ReadSingleParquetFileLocalState &lstate) { + { + lock_guard guard(gstate.lock); + if (!gstate.reader->TryInitializeScan(context, gstate.state, lstate.state)) { + return false; + } + } + // preparing the scan of the claimed row group is done without the lock + gstate.reader->PrepareScan(context, gstate.state, lstate.state); + lstate.has_row_group = true; + return true; +} + +//! Register and run the reads of the row group this thread claimed. Our caller schedules this I/O itself when it +//! drives the scan through "schedule_io" +static void LoadClaimedRowGroup(ClientContext &context, ReadSingleParquetFileGlobalState &gstate, + ReadSingleParquetFileLocalState &lstate) { + auto io_result = gstate.reader->ScheduleIO(context, gstate.state, lstate.state); + if (io_result.GetResultType() == AsyncResultType::BLOCKED) { + io_result.ExecuteTasksSynchronously(); + } +} + +static bool ReadSingleParquetFileClaimBatch(ClientContext &context, TableFunctionInput &input) { + auto &gstate = input.global_state->Cast(); + auto &lstate = input.local_state->Cast(); + // our caller hands out the row groups, so we must not claim the next one ourselves + lstate.claimed_externally = true; + // our caller holds the lock of its scan here - the claimed row group is prepared in the prepare_batch callback + lock_guard guard(gstate.lock); + return gstate.reader->TryInitializeScan(context, gstate.state, lstate.state); +} + +static void ReadSingleParquetFilePrepareBatch(ClientContext &context, TableFunctionInput &input) { + auto &gstate = input.global_state->Cast(); + auto &lstate = input.local_state->Cast(); + gstate.reader->PrepareScan(context, gstate.state, lstate.state); + lstate.has_row_group = true; +} + +//! Pre-open the handle the scan reads the file through, so that claiming a row group does not have to open the file +static void ReadSingleParquetFilePrepareReadAhead(ClientContext &context, TableFunctionInput &input) { + auto &gstate = input.global_state->Cast(); + gstate.reader->PrepareReadAhead(context, gstate.state); +} + +//! The parquet reader can always be read ahead - the row group a thread claimed is loaded before it is scanned +static bool ReadSingleParquetFileSupportsReadAhead(optional_ptr bind_data) { + return true; +} + +//! Load the data of the claimed row group +static AsyncResult ReadSingleParquetFileScheduleIO(ClientContext &context, TableFunctionInput &input) { + auto &gstate = input.global_state->Cast(); + auto &lstate = input.local_state->Cast(); + if (!lstate.has_row_group) { + return SourceResultType::HAVE_MORE_OUTPUT; + } + return gstate.reader->ScheduleIO(context, gstate.state, lstate.state); +} + +//! Release the row group this thread was reading +static void ReadSingleParquetFileFinishBatch(ClientContext &context, TableFunctionInput &input) { + auto &lstate = input.local_state->Cast(); + lstate.has_row_group = false; +} + +static void ReadSingleParquetFileFunction(ClientContext &context, TableFunctionInput &input, DataChunk &output) { + auto &gstate = input.global_state->Cast(); + auto &lstate = input.local_state->Cast(); + + bool resuming_blocked_scan = false; + while (true) { + if (!lstate.has_row_group) { + if (lstate.claimed_externally) { + // the next row group is claimed by our caller + return; + } + if (!ClaimNextRowGroup(context, gstate, lstate)) { + // there is nothing left for us to read in this file + return; + } + LoadClaimedRowGroup(context, gstate, lstate); + } + if (!resuming_blocked_scan) { + // a blocked scan left partial data in the chunk that resuming it completes - everything else starts + // from an empty chunk + output.Reset(); + } + auto scan_result = gstate.reader->Scan(context, gstate.state, lstate.state, output); + resuming_blocked_scan = scan_result.GetResultType() == AsyncResultType::BLOCKED; + switch (scan_result.GetResultType()) { + case AsyncResultType::BLOCKED: + // the reader needs data that is not in memory yet - run the I/O it scheduled and resume. It left + // partial data in the chunk that the resumed scan completes, so the chunk is not reset + scan_result.ExecuteTasksSynchronously(); + continue; + case AsyncResultType::HAVE_MORE_OUTPUT: + if (output.size() == 0) { + // every row of this chunk was filtered out - there is more to read in this row group + continue; + } + return; + case AsyncResultType::FINISHED: + // this row group is done - the next one is claimed above + lstate.has_row_group = false; + if (output.size() != 0) { + return; + } + continue; + default: + throw InternalException("Unexpected result type %s scanning a parquet file", + EnumUtil::ToChars(scan_result.GetResultType())); + } + } +} + +static double ReadSingleParquetFileProgress(ClientContext &context, const FunctionData *bind_data, + const GlobalTableFunctionState *global_state) { + if (!global_state) { + return 0; + } + auto &gstate = global_state->Cast(); + if (!gstate.reader) { + return 0; + } + return gstate.reader->GetProgressInFile(context); +} + +static unique_ptr ReadSingleParquetFileCardinality(ClientContext &context, + const FunctionData *bind_data) { + auto &parquet_data = bind_data->Cast(); + if (!parquet_data.cardinality.IsValid()) { + return nullptr; + } + return make_uniq(parquet_data.cardinality.GetIndex()); +} + +//! The virtual columns the parquet reader can produce for a file +static virtual_column_map_t ReadSingleParquetFileVirtualColumns(ClientContext &context, + optional_ptr bind_data) { + virtual_column_map_t result; + result.insert(make_pair(MultiFileReader::COLUMN_IDENTIFIER_FILE_ROW_NUMBER, + TableColumn("file_row_number", LogicalType::BIGINT))); + result.insert(make_pair(ParquetReader::COLUMN_IDENTIFIER_FILE_ROW_GROUP_NUMBER, + TableColumn("file_row_group_number", LogicalType::UBIGINT))); + return result; +} + +//! The row number within the file identifies a row of a single file +static vector ReadSingleParquetFileRowIdColumns(ClientContext &context, + optional_ptr bind_data) { + return {MultiFileReader::COLUMN_IDENTIFIER_FILE_ROW_NUMBER}; +} + +//! The statistics of a column of this file, read from its metadata +static unique_ptr +ReadSingleParquetFileStatistics(ClientContext &context, const FunctionData *bind_data_p, column_t column_index) { + auto &parquet_data = bind_data_p->Cast(); + if (IsVirtualColumn(column_index)) { + return ParquetReader::ReadVirtualColumnStatistics(context, parquet_data.options, parquet_data.metadata, + column_index); + } + if (column_index >= parquet_data.parquet_names.size()) { + return nullptr; + } + return parquet_data.GetMetadataReader(context).ReadStatistics(parquet_data.parquet_names[column_index]); +} + +//! The row groups read from this file. The counts accumulate, so that a scan over several files reports their sum +static void ReadSingleParquetFileGetMetrics(TableFunctionGetMetricsInput &input) { + if (!input.global_state) { + return; + } + auto &gstate = input.global_state->Cast(); + // the row groups scanned are drained, so each is reported once; the number to scan is the size of this file, + // which our caller reports as-is + input.operator_metrics.row_groups_scanned += gstate.state.row_groups_scanned_unreported.exchange(0); + input.operator_metrics.total_row_groups_to_scan += gstate.state.total_row_groups_to_scan.load(); +} + +//! The row groups of this file, read from its metadata +static vector ReadSingleParquetFilePartitionStats(ClientContext &context, + GetPartitionStatsInput &input) { + vector result; + auto &parquet_data = input.bind_data->Cast(); + if (!parquet_data.metadata) { + return result; + } + // the row groups carry the statistics of their columns, which lets e.g. min/max be answered from the metadata + parquet_data.GetMetadataReader(context).GetPartitionStats(result); + return result; +} + +bool ParquetScanFunction::ProjectionExpressionPushdown(ClientContext &context, + const TableFunctionProjectionExpressionInput &input) { + if (input.expr.GetExpressionClass() != ExpressionClass::BOUND_FUNCTION) { + return false; + } + const auto &fn = input.expr.Cast(); + if (const Identifier &name = fn.Function().GetName(); name != "strlen" && name != "octet_length") { + return false; + } + auto &bind_data = input.get.bind_data->CastNoConst(); + // Don't do pushdown on custom schema users like Ducklake, or when the schemas of several files are unified + if (!bind_data.reader_bind.schema.empty() || !bind_data.union_readers.empty()) { + return false; + } + // Don't do pushdown with multiple files - the metadata of all but the first file is not available upfront, and + // the files may have their columns in a different order + if (bind_data.file_list->GetExpandResult() == FileExpandResult::MULTIPLE_FILES || !bind_data.initial_reader) { + return false; + } + const auto &column_id = input.get.GetColumnIds()[input.column_index]; + // Pushdown extract columns i.e. SELECT x.y.z have a complex nested type update + if (column_id.IsPushdownExtract()) { + return false; + } + const idx_t idx = column_id.GetPrimaryIndex(); + // Generated values do not come from the physical column, even when its name and type match. + if (bind_data.IsGeneratedColumn(idx)) { + return false; + } + // We run scalar function pushdown after filter pushdown - a filter pushed into the scan needs the column itself + if (input.get.table_filters.HasFilter(input.column_index)) { + return false; + } + auto &reader = bind_data.initial_reader->Cast(); + if (!reader.bind_data) { + return false; + } + auto &parquet_data = reader.bind_data->Cast(); + auto &metadata_reader = parquet_data.GetMetadataReader(context); + auto &children = metadata_reader.root_schema->children; + if (idx >= children.size() || idx >= bind_data.types.size() || idx >= reader.types.size()) { + return false; + } + if (!bind_data.file_options.bound_schema.empty() && + (idx >= reader.columns.size() || + !bind_data.MatchesPhysicalColumn(idx, reader.columns[idx].name, reader.types[idx]))) { + // The pushed expression addresses a physical column, before name mapping and conversion. + return false; + } + const idx_t column_flat_idx = children[idx].column_index; + for (const auto &group : metadata_reader.GetFileMetadata()->row_groups) { + D_ASSERT(column_flat_idx < group.columns.size()); + for (const Encoding::type type : group.columns[column_flat_idx].meta_data.encodings) { + if (type == duckdb_parquet::Encoding::DELTA_LENGTH_BYTE_ARRAY) { + return false; + } + } + } + + const LogicalType type = LogicalType::BIGINT; + const ParquetReaderProjectionExpression expression {ParquetReaderProjectionExpressionType::BYTE_LENGTH, type}; + // the file is read with this bind data, and every other bind of it takes the expression from here + parquet_data.AddProjectionExpression(idx, expression); + bind_data.types[idx] = type; + bind_data.columns[idx].type = type; + // the reader of the file was bound before the expression was pushed down - update its types + reader.types[idx] = type; + if (idx < reader.columns.size()) { + reader.columns[idx].type = type; + } + auto &data = bind_data.bind_data->Cast(); + if (idx < data.options.expected_types.size()) { + data.options.expected_types[idx] = type; + } + return true; +} + +const ParquetOptions &ParquetScanFunction::GetFileOptions(const FunctionData &file_bind_data) { + return file_bind_data.Cast().options; +} + +TableFunction ParquetScanFunction::GetSingleFileFunction() { + TableFunction read_parquet("read_single_parquet_file", + FunctionSignature().AddPositionalOnly("file", LogicalType::ANY), + ReadSingleParquetFileFunction, ReadSingleParquetFileBind, + ReadSingleParquetFileInitGlobal, ReadSingleParquetFileInitLocal); + read_parquet.table_scan_progress = ReadSingleParquetFileProgress; + read_parquet.cardinality = ReadSingleParquetFileCardinality; + read_parquet.statistics = ReadSingleParquetFileStatistics; + read_parquet.get_virtual_columns = ReadSingleParquetFileVirtualColumns; + read_parquet.get_partition_stats = ReadSingleParquetFilePartitionStats; + read_parquet.get_metrics = ReadSingleParquetFileGetMetrics; + read_parquet.projection_pushdown = true; + read_parquet.late_materialization = true; + read_parquet.get_row_id_columns = ReadSingleParquetFileRowIdColumns; + read_parquet.filter_pushdown = true; + read_parquet.filter_prune = true; + ParquetScanFunction::AddNamedParameters(read_parquet); + return read_parquet; +} + +TableFunctionMultiFileSettings ParquetScanFunction::GetMultiFileSettings() { + TableFunctionMultiFileSettings settings; + settings.glob_input = FileGlobInput(FileGlobOptions::FALLBACK_GLOB, "parquet"); + settings.reader_type = "Parquet"; + settings.claim_batch = ReadSingleParquetFileClaimBatch; + settings.prepare_batch = ReadSingleParquetFilePrepareBatch; + settings.finish_batch = ReadSingleParquetFileFinishBatch; + settings.supports_read_ahead = ReadSingleParquetFileSupportsReadAhead; + settings.schedule_io = ReadSingleParquetFileScheduleIO; + settings.prepare_read_ahead = ReadSingleParquetFilePrepareReadAhead; + settings.get_file_columns = ReadSingleParquetFileColumns; + // the order the filters are best applied in is learned while reading, and carries over to the next file + settings.reuses_local_state = true; + return settings; +} + +} // namespace duckdb diff --git a/src/duckdb/extension/parquet/reader/decimal_column_reader.cpp b/src/duckdb/extension/parquet/reader/decimal_column_reader.cpp index ef58e9c5e..3df8d70b5 100644 --- a/src/duckdb/extension/parquet/reader/decimal_column_reader.cpp +++ b/src/duckdb/extension/parquet/reader/decimal_column_reader.cpp @@ -5,7 +5,6 @@ #include "duckdb/common/exception.hpp" #include "duckdb/common/helper.hpp" -#include "duckdb/common/limits.hpp" #include "duckdb/common/types.hpp" #include "duckdb/common/unique_ptr.hpp" #include "parquet_types.h" @@ -42,15 +41,15 @@ double ParquetDecimalUtils::ReadDecimalValue(const_data_ptr_t pointer, idx_t siz return res; } bool positive = (*pointer & 0x80) == 0; - for (idx_t i = 0; i < size; i += 8) { + for (idx_t i = 0; i < size; i += sizeof(uint64_t)) { + // the trailing chunk can hold fewer than 8 bytes - scale by the bytes actually consumed auto byte_size = MinValue(sizeof(uint64_t), size - i); uint64_t input = 0; - auto res_ptr = reinterpret_cast(&input); for (idx_t k = 0; k < byte_size; k++) { auto byte = pointer[i + k]; - res_ptr[sizeof(uint64_t) - k - 1] = positive ? byte : byte ^ 0xFF; + input = (input << 8) | static_cast(positive ? byte : byte ^ 0xFF); } - res *= double(NumericLimits::Maximum()) + 1; + res = std::ldexp(res, static_cast(8 * byte_size)); res += static_cast(input); } if (!positive) { diff --git a/src/duckdb/extension/parquet/reader/list_column_reader.cpp b/src/duckdb/extension/parquet/reader/list_column_reader.cpp index d50479ff7..be3b792ad 100644 --- a/src/duckdb/extension/parquet/reader/list_column_reader.cpp +++ b/src/duckdb/extension/parquet/reader/list_column_reader.cpp @@ -137,7 +137,11 @@ idx_t ListColumnReader::ReadInternal(ColumnReaderInput &input, optional_ptr 0); + if (result_offset == 0) { + throw DataCorruptionException( + "Corrupt Parquet file: a value in list column \"%s\" repeats a list before any list started", + Schema().name); + } OP::HandleRepeat(data, result_offset - 1); continue; } diff --git a/src/duckdb/extension/parquet/reader/row_number_column_reader.cpp b/src/duckdb/extension/parquet/reader/row_number_column_reader.cpp index 3679ee03b..e7579b4ff 100644 --- a/src/duckdb/extension/parquet/reader/row_number_column_reader.cpp +++ b/src/duckdb/extension/parquet/reader/row_number_column_reader.cpp @@ -31,6 +31,15 @@ RowNumberColumnReader::RowNumberColumnReader(const ParquetReader &reader, const void RowNumberColumnReader::InitializeRead(idx_t row_group_idx_p, idx_t row_group_num_rows, const vector &columns, TProtocol &protocol_p) { + // the row numbers are produced from the row count without reading a column chunk - every column chunk has a value + // (or NULL) per row, so it cannot have fewer values than the row group has rows + for (auto &column : columns) { + if (column.__isset.meta_data && column.meta_data.num_values < NumericCast(row_group_num_rows)) { + throw DataCorruptionException( + "Failed to read file \"%s\": metadata is corrupt. Column has %lld values but row group has %llu rows", + reader.GetFileName(), column.meta_data.num_values, row_group_num_rows); + } + } row_group_offset = 0; auto &row_groups = reader.GetFileMetadata()->row_groups; for (idx_t i = 0; i < row_group_idx_p; i++) { diff --git a/src/duckdb/extension/parquet/reader/string_column_reader.cpp b/src/duckdb/extension/parquet/reader/string_column_reader.cpp index ba290f5ff..33f3d727b 100644 --- a/src/duckdb/extension/parquet/reader/string_column_reader.cpp +++ b/src/duckdb/extension/parquet/reader/string_column_reader.cpp @@ -13,6 +13,7 @@ #include "duckdb/common/vector/string_vector.hpp" #include "parquet_column_schema.hpp" #include "parquet_types.h" +#include "parquet_timestamp.hpp" namespace duckdb { class Vector; @@ -27,6 +28,10 @@ StringColumnReader::StringColumnReader(const ParquetReader &reader, const Parque if (schema.parquet_type == Type::FIXED_LEN_BYTE_ARRAY) { fixed_width_string_length = schema.type_length; } + if (schema.parquet_type == Type::INT96) { + // INT96 is read as a raw 12-byte blob for int96_as='struct' (which converts it to STRUCT(date, time)) + fixed_width_string_length = sizeof(Int96); + } } bool StringColumnReader::IsValid(const char *str_data, uint32_t str_len, const bool is_varchar) { @@ -78,7 +83,7 @@ string_t StringColumnReader::VerifyString(const char *str_data, uint32_t str_len auto new_str = Utf8Proc::RemoveInvalid(str_data, str_len); auto target = StringVector::EmptyString(*current_plain_result, new_str.size()); auto output = target.GetDataWriteable(); - memcpy(output, new_str.data(), new_str.size()); + memcpy(output, new_str.data(), new_str.size()); // NOLINT(bugprone-not-null-terminated-result) target.Finalize(); return target; } diff --git a/src/duckdb/extension/parquet/reader/struct_column_reader.cpp b/src/duckdb/extension/parquet/reader/struct_column_reader.cpp index c895bcf82..6262dd841 100644 --- a/src/duckdb/extension/parquet/reader/struct_column_reader.cpp +++ b/src/duckdb/extension/parquet/reader/struct_column_reader.cpp @@ -132,15 +132,13 @@ void StructColumnReader::RegisterPrefetch(ThriftFileTransport &transport, bool a } } -uint64_t StructColumnReader::TotalCompressedSize() { - uint64_t size = 0; +void StructColumnReader::GatherColumnChunks(unordered_set &column_chunks) { for (auto &child : child_readers) { if (!child) { continue; } - size += child->TotalCompressedSize(); + child->GatherColumnChunks(column_chunks); } - return size; } static bool TypeHasExactRowCount(const LogicalType &type) { @@ -149,6 +147,7 @@ static bool TypeHasExactRowCount(const LogicalType &type) { case LogicalTypeId::MAP: return false; case LogicalTypeId::STRUCT: + case LogicalTypeId::TUPLE: for (auto &kv : StructType::GetChildTypes(type)) { if (TypeHasExactRowCount(kv.second)) { return true; diff --git a/src/duckdb/extension/parquet/reader/variant/parquet_variant_shredding.cpp b/src/duckdb/extension/parquet/reader/variant/parquet_variant_shredding.cpp index 7ce888427..cc30f65eb 100644 --- a/src/duckdb/extension/parquet/reader/variant/parquet_variant_shredding.cpp +++ b/src/duckdb/extension/parquet/reader/variant/parquet_variant_shredding.cpp @@ -1,4 +1,4 @@ -#include "reader/variant/parquet_variant_iterator.hpp" +#include "duckdb/common/types/variant/parquet_variant_iterator.hpp" #include "reader/variant_column_reader.hpp" #include "duckdb/common/types/variant/variant_builder.hpp" diff --git a/src/duckdb/extension/parquet/reader/variant_column_reader.cpp b/src/duckdb/extension/parquet/reader/variant_column_reader.cpp index d471f5df1..5a2ddb98e 100644 --- a/src/duckdb/extension/parquet/reader/variant_column_reader.cpp +++ b/src/duckdb/extension/parquet/reader/variant_column_reader.cpp @@ -6,8 +6,9 @@ #include "duckdb/common/vector/struct_vector.hpp" #include "reader/variant_column_reader.hpp" -#include "reader/variant/parquet_variant_iterator.hpp" +#include "duckdb/common/types/variant/parquet_variant_iterator.hpp" #include "column_reader.hpp" +#include "parquet_reader.hpp" #include "duckdb/common/assert.hpp" #include "duckdb/common/constants.hpp" #include "duckdb/common/exception.hpp" @@ -73,6 +74,11 @@ VariantColumnReader::VariantColumnReader(ClientContext &context, const ParquetRe } } + if (child_readers.size() < 2 || !child_readers[0] || !child_readers[1]) { + throw DataCorruptionException("Failed to read file \"%s\": the Variant column must have 'metadata' and 'value' " + "as the first two columns", + reader.GetFileName()); + } if (child_readers[0]->Schema().name == "metadata" && child_readers[1]->Schema().name == "value") { metadata_reader_idx = 0; value_reader_idx = 1; @@ -80,7 +86,9 @@ VariantColumnReader::VariantColumnReader(ClientContext &context, const ParquetRe metadata_reader_idx = 1; value_reader_idx = 0; } else { - throw InternalException("The Variant column must have 'metadata' and 'value' as the first two columns"); + throw DataCorruptionException("Failed to read file \"%s\": the Variant column must have 'metadata' and 'value' " + "as the first two columns", + reader.GetFileName()); } } @@ -214,15 +222,13 @@ void VariantColumnReader::RegisterPrefetch(ThriftFileTransport &transport, bool } } -uint64_t VariantColumnReader::TotalCompressedSize() { - uint64_t size = 0; +void VariantColumnReader::GatherColumnChunks(unordered_set &column_chunks) { for (auto &child : child_readers) { if (!child) { continue; } - size += child->TotalCompressedSize(); + child->GatherColumnChunks(column_chunks); } - return size; } idx_t VariantColumnReader::GroupRowsAvailable() { @@ -241,13 +247,17 @@ bool VariantColumnReader::TypedValueLayoutToType(const LogicalType &typed_value, return true; } auto type_id = typed_value.id(); - if (type_id == LogicalTypeId::STRUCT) { + if (StructType::IsStruct(type_id)) { //! OBJECT (...) auto &object_fields = StructType::GetChildTypes(typed_value); child_list_t children; for (auto &object_field : object_fields) { auto &name = object_field.first; auto &field = object_field.second; + if (name.empty()) { + //! An empty key can't be a STRUCT member name + return false; + } //! : { //! value: BLOB, //! typed_value: diff --git a/src/duckdb/extension/parquet/serialize_parquet.cpp b/src/duckdb/extension/parquet/serialize_parquet.cpp index 56aeb76a9..4dfe51da1 100644 --- a/src/duckdb/extension/parquet/serialize_parquet.cpp +++ b/src/duckdb/extension/parquet/serialize_parquet.cpp @@ -80,29 +80,31 @@ shared_ptr ParquetEncryptionConfig::Deserialize(Deseria void ParquetOptionsSerialization::Serialize(Serializer &serializer) const { serializer.WritePropertyWithDefault(100, "binary_as_string", parquet_options.binary_as_string); - serializer.WritePropertyWithDefault(101, "file_row_number", parquet_options.file_row_number); + serializer.WritePropertyWithDefault(101, "file_row_number", legacy_file_row_number); serializer.WriteProperty(102, "file_options", file_options); - serializer.WritePropertyWithDefault>(103, "schema", parquet_options.schema); + serializer.WritePropertyWithDefault>(103, "schema", legacy_schema); serializer.WritePropertyWithDefault>(104, "encryption_config", parquet_options.encryption_config, nullptr); /* [Deleted] (bool) "parquet_options.debug_use_openssl" */ serializer.WritePropertyWithDefault(106, "explicit_cardinality", parquet_options.explicit_cardinality, 0); serializer.WritePropertyWithDefault(107, "can_have_nan", parquet_options.can_have_nan, false); serializer.WritePropertyWithDefault(108, "prefetch_strategy", parquet_options.prefetch_strategy, ParquetPrefetchStrategyOption::AUTO); serializer.WritePropertyWithDefault(109, "utf8_validation", parquet_options.utf8_validation_option, StringColumnReader::Utf8ValidationOption::STRICT_UTF8); + serializer.WritePropertyWithDefault(110, "int96_as", parquet_options.int96_as, ParquetInt96AsOption::TIMESTAMP); } ParquetOptionsSerialization ParquetOptionsSerialization::Deserialize(Deserializer &deserializer) { ParquetOptionsSerialization result; deserializer.ReadPropertyWithDefault(100, "binary_as_string", result.parquet_options.binary_as_string); - deserializer.ReadPropertyWithDefault(101, "file_row_number", result.parquet_options.file_row_number); + deserializer.ReadPropertyWithDefault(101, "file_row_number", result.legacy_file_row_number); deserializer.ReadProperty(102, "file_options", result.file_options); - deserializer.ReadPropertyWithDefault>(103, "schema", result.parquet_options.schema); + deserializer.ReadPropertyWithDefault>(103, "schema", result.legacy_schema); deserializer.ReadPropertyWithExplicitDefault>(104, "encryption_config", result.parquet_options.encryption_config, nullptr); deserializer.ReadDeletedProperty(105, "debug_use_openssl"); deserializer.ReadPropertyWithExplicitDefault(106, "explicit_cardinality", result.parquet_options.explicit_cardinality, 0); deserializer.ReadPropertyWithExplicitDefault(107, "can_have_nan", result.parquet_options.can_have_nan, false); deserializer.ReadPropertyWithExplicitDefault(108, "prefetch_strategy", result.parquet_options.prefetch_strategy, ParquetPrefetchStrategyOption::AUTO); deserializer.ReadPropertyWithExplicitDefault(109, "utf8_validation", result.parquet_options.utf8_validation_option, StringColumnReader::Utf8ValidationOption::STRICT_UTF8); + deserializer.ReadPropertyWithExplicitDefault(110, "int96_as", result.parquet_options.int96_as, ParquetInt96AsOption::TIMESTAMP); return result; } diff --git a/src/duckdb/extension/parquet/writer/array_column_writer.cpp b/src/duckdb/extension/parquet/writer/array_column_writer.cpp index b4bd32ab9..14afec541 100644 --- a/src/duckdb/extension/parquet/writer/array_column_writer.cpp +++ b/src/duckdb/extension/parquet/writer/array_column_writer.cpp @@ -61,10 +61,13 @@ void ArrayColumnWriter::Prepare(ColumnWriterState &state_p, ColumnWriterState *p if (parent && parent->definition_levels[parent_index] != PARQUET_DEFINE_VALID) { WriteArrayState(state, array_size, first_repeat_level, parent->definition_levels[parent_index]); } else if (validity.RowIsValid(vector_index)) { + state.num_values++; // push the repetition levels WriteArrayState(state, array_size, first_repeat_level, PARQUET_DEFINE_VALID); } else { //! Produce a null + state.own_null_count++; + state.num_values++; WriteArrayState(state, array_size, first_repeat_level, MaxDefine() - 1); } vector_index++; diff --git a/src/duckdb/extension/parquet/writer/list_column_writer.cpp b/src/duckdb/extension/parquet/writer/list_column_writer.cpp index 88c5dbf92..567279f77 100644 --- a/src/duckdb/extension/parquet/writer/list_column_writer.cpp +++ b/src/duckdb/extension/parquet/writer/list_column_writer.cpp @@ -19,6 +19,7 @@ #include "duckdb/common/vector.hpp" #include "duckdb/common/vector/flat_vector.hpp" #include "parquet_column_schema.hpp" +#include "parquet_writer.hpp" #include "parquet_types.h" namespace duckdb { @@ -109,6 +110,7 @@ void ListColumnWriter::Prepare(ColumnWriterState &state_p, ColumnWriterState *pa state.repetition_levels.push_back(first_repeat_level); state.is_empty.push_back(true); } else if (validity.RowIsValid(vector_index)) { + state.num_values++; // push the repetition levels if (list_data[vector_index].length == 0) { state.definition_levels.push_back(MaxDefine()); @@ -127,6 +129,8 @@ void ListColumnWriter::Prepare(ColumnWriterState &state_p, ColumnWriterState *pa if (!can_have_nulls) { throw IOException("Parquet writer: map key column is not allowed to contain NULL values"); } + state.own_null_count++; + state.num_values++; state.definition_levels.push_back(MaxDefine() - 1); state.repetition_levels.push_back(first_repeat_level); state.is_empty.push_back(true); @@ -164,6 +168,7 @@ void ListColumnWriter::PrepareWrite(ColumnWriterState &state_p) { void ListColumnWriter::FinalizeWrite(ColumnWriterState &state_p) { auto &state = state_p.Cast(); + writer.FlushNestedColumnStats(SchemaIndex(), state.own_null_count, state.num_values); GetChildWriter().FinalizeWrite(*state.child_state); } diff --git a/src/duckdb/extension/parquet/writer/primitive_column_writer.cpp b/src/duckdb/extension/parquet/writer/primitive_column_writer.cpp index 652199ac6..7d95a70d2 100644 --- a/src/duckdb/extension/parquet/writer/primitive_column_writer.cpp +++ b/src/duckdb/extension/parquet/writer/primitive_column_writer.cpp @@ -133,11 +133,8 @@ void PrimitiveColumnWriter::Prepare(ColumnWriterState &state_p, ColumnWriterStat } if (validity.RowIsValid(vector_index)) { page_info.estimated_page_size += GetRowSize(vector, vector_index, state); - if (page_info.estimated_page_size >= page_size_limit) { - if (!vector_can_span_multiple_pages && i != 0) { - // Vector is not allowed to span multiple pages, and we already started writing it - continue; - } + // a vector that cannot span multiple pages stays on the page it started on + if (page_info.estimated_page_size >= page_size_limit && (vector_can_span_multiple_pages || i == 0)) { PageInformation new_info; new_info.offset = page_info.offset + page_info.row_count; state.page_info.push_back(new_info); @@ -378,10 +375,15 @@ void PrimitiveColumnWriter::SetParquetStatistics(PrimitiveColumnWriterState &sta if (!state.stats_state) { return; } - auto null_count = MaxRepeat() == 0 ? state.null_count : state.null_count + state.parent_null_count; - column_chunk.meta_data.statistics.null_count = NumericCast(null_count); + // a NULL ancestor counts as a NULL of this column, the children count it once as a parent NULL + column_chunk.meta_data.statistics.null_count = NumericCast(state.null_count + state.parent_null_count); column_chunk.meta_data.statistics.__isset.null_count = true; column_chunk.meta_data.__isset.statistics = true; + if (column_chunk.meta_data.type == duckdb_parquet::Type::FLOAT || + column_chunk.meta_data.type == duckdb_parquet::Type::DOUBLE) { + column_chunk.meta_data.statistics.nan_count = NumericCast(state.stats_state->GetNaNCount()); + column_chunk.meta_data.statistics.__isset.nan_count = true; + } // if we have NaN values - don't write the min/max here if (!state.stats_state->HasNaN()) { diff --git a/src/duckdb/extension/parquet/writer/struct_column_writer.cpp b/src/duckdb/extension/parquet/writer/struct_column_writer.cpp index d16413a66..9a8210b3d 100644 --- a/src/duckdb/extension/parquet/writer/struct_column_writer.cpp +++ b/src/duckdb/extension/parquet/writer/struct_column_writer.cpp @@ -15,6 +15,7 @@ #include "duckdb/common/vector.hpp" #include "duckdb/common/vector/flat_vector.hpp" #include "parquet_column_schema.hpp" +#include "parquet_writer.hpp" #include "parquet_types.h" namespace duckdb { @@ -34,6 +35,8 @@ class StructColumnWriterState : public ColumnWriterState { duckdb_parquet::RowGroup &row_group; idx_t col_idx; vector> child_states; + //! Null structs before PrepareWrite adds ancestor nulls to null_count + idx_t own_null_count = 0; }; unique_ptr StructColumnWriter::InitializeWriteState(duckdb_parquet::RowGroup &row_group) { @@ -93,6 +96,7 @@ void StructColumnWriter::Prepare(ColumnWriterState &state_p, ColumnWriterState * } HandleRepeatLevels(state_p, parent, count); HandleDefineLevels(state_p, parent, validity, count, PARQUET_DEFINE_VALID, MaxDefine() - 1); + state.own_null_count = state.null_count; auto &child_vectors = StructVector::GetEntries(vector); for (idx_t child_idx = 0; child_idx < child_writers.size(); child_idx++) { child_writers[child_idx]->Prepare(*state.child_states[child_idx], &state_p, child_vectors[child_idx], count, @@ -118,14 +122,14 @@ void StructColumnWriter::Write(ColumnWriterState &state_p, Vector &vector, idx_t void StructColumnWriter::PrepareWrite(ColumnWriterState &state_p) { auto &state = state_p.Cast(); for (idx_t child_idx = 0; child_idx < child_writers.size(); child_idx++) { - // we add the null count of the struct to the null count of the children - state.child_states[child_idx]->null_count += state_p.null_count; child_writers[child_idx]->PrepareWrite(*state.child_states[child_idx]); } } void StructColumnWriter::FinalizeWrite(ColumnWriterState &state_p) { auto &state = state_p.Cast(); + auto num_values = state.definition_levels.size() - state.parent_null_count; + writer.FlushNestedColumnStats(SchemaIndex(), state.own_null_count, num_values); for (idx_t child_idx = 0; child_idx < child_writers.size(); child_idx++) { child_writers[child_idx]->FinalizeWrite(*state.child_states[child_idx]); } diff --git a/src/duckdb/extension/parquet/writer/variant/convert_variant.cpp b/src/duckdb/extension/parquet/writer/variant/convert_variant.cpp index e65e97257..55b48c86b 100644 --- a/src/duckdb/extension/parquet/writer/variant/convert_variant.cpp +++ b/src/duckdb/extension/parquet/writer/variant/convert_variant.cpp @@ -1,927 +1,58 @@ -#include -#include #include -#include #include #include -#include "duckdb/common/vector/struct_vector.hpp" #include "writer/variant_column_writer.hpp" -#include "duckdb/common/types/variant.hpp" -#include "duckdb/function/scalar/variant_utils.hpp" -#include "reader/variant/variant_binary_decoder.hpp" -#include "duckdb/function/variant/variant_shredding.hpp" -#include "duckdb/planner/expression_binder.hpp" -#include "duckdb/execution/expression_executor.hpp" -#include "duckdb/common/operator/cast_operators.hpp" #include "column_writer.hpp" -#include "duckdb/common/assert.hpp" -#include "duckdb/common/enum_util.hpp" -#include "duckdb/common/exception.hpp" +#include "parquet_column_schema.hpp" +#include "parquet_types.h" +#include "duckdb/common/types/variant/parquet_variant_iterator.hpp" #include "duckdb/common/exception/binder_exception.hpp" -#include "duckdb/common/helper.hpp" -#include "duckdb/common/hugeint.hpp" -#include "duckdb/common/limits.hpp" #include "duckdb/common/numeric_utils.hpp" -#include "duckdb/common/optional_idx.hpp" -#include "duckdb/common/optional_ptr.hpp" -#include "duckdb/common/string.hpp" -#include "duckdb/common/string_map_set.hpp" -#include "duckdb/common/typedefs.hpp" #include "duckdb/common/types.hpp" #include "duckdb/common/types/data_chunk.hpp" -#include "duckdb/common/types/selection_vector.hpp" -#include "duckdb/common/types/string_type.hpp" -#include "duckdb/common/types/uuid.hpp" -#include "duckdb/common/types/validity_mask.hpp" -#include "duckdb/common/types/value.hpp" #include "duckdb/common/types/vector.hpp" -#include "duckdb/common/uhugeint.hpp" -#include "duckdb/common/unique_ptr.hpp" -#include "duckdb/common/vector.hpp" #include "duckdb/common/vector/flat_vector.hpp" -#include "duckdb/common/vector/string_vector.hpp" -#include "duckdb/common/vector/unified_vector_format.hpp" -#include "duckdb/function/function.hpp" +#include "duckdb/common/vector/vector_iterator.hpp" #include "duckdb/function/scalar_function.hpp" -#include "duckdb/planner/expression.hpp" -#include "parquet_column_schema.hpp" -#include "parquet_types.h" +#include "duckdb/planner/expression_binder.hpp" namespace duckdb { -class ClientContext; -struct ExpressionState; - -static idx_t CalculateByteLength(idx_t value) { - if (value == 0) { - return 1; - } - auto value_data = reinterpret_cast(&value); - idx_t irrelevant_bytes = 0; - //! Check how many of the most significant bytes are 0 - for (idx_t i = sizeof(idx_t); i > 0 && value_data[i - 1] == 0; i--) { - irrelevant_bytes++; - } - return sizeof(idx_t) - irrelevant_bytes; -} - -static uint8_t EncodeMetadataHeader(idx_t byte_length) { - D_ASSERT(byte_length <= 4); - - uint8_t header_byte = 0; - //! Set 'version' to 1 - header_byte |= static_cast(1); - //! NOTE: keep 'sorted_strings' at 0, we don't always sort the key strings - //! Set 'offset_size_minus_one' to byte_length-1 - header_byte |= (static_cast(byte_length) - 1) << 6; -#ifdef DEBUG - auto decoded_header = VariantMetadataHeader::FromHeaderByte(header_byte); - D_ASSERT(decoded_header.offset_size == byte_length); -#endif - - return header_byte; -} - -static void CreateMetadata(UnifiedVariantVectorData &variant, Vector &metadata, idx_t count) { - //! NOTE: the parquet variant is limited to a max dictionary size of NumericLimits::Maximum() - //! Whereas we can have NumericLimits::Maximum() *per* string in DuckDB - auto metadata_data = FlatVector::GetDataMutable(metadata); - for (idx_t row = 0; row < count; row++) { - uint64_t dictionary_count = 0; - if (variant.RowIsValid(row)) { - dictionary_count = variant.GetKeysCount(row); - } - idx_t dictionary_size = 0; - for (idx_t i = 0; i < dictionary_count; i++) { - auto &key = variant.GetKey(row, i); - dictionary_size += key.GetSize(); - } - if (dictionary_size >= NumericLimits::Maximum()) { - throw InvalidInputException("The total length of the dictionary exceeds a 4 byte value (uint32_t), failed " - "to export VARIANT to Parquet"); - } - - auto byte_length = CalculateByteLength(dictionary_size); - auto total_length = 1 + (byte_length * (dictionary_count + 2)) + dictionary_size; - - metadata_data[row] = StringVector::EmptyString(metadata, total_length); - auto &metadata_blob = metadata_data[row]; - auto metadata_blob_data = metadata_blob.GetDataWriteable(); - - metadata_blob_data[0] = static_cast(EncodeMetadataHeader(byte_length)); - memcpy(metadata_blob_data + 1, const_data_ptr_cast(&dictionary_count), byte_length); - - auto offset_ptr = metadata_blob_data + 1 + byte_length; - auto string_ptr = - metadata_blob_data + 1 + byte_length + (NumericCast(dictionary_count + 1) * byte_length); - idx_t total_offset = 0; - for (idx_t i = 0; i < dictionary_count; i++) { - memcpy(offset_ptr + (i * byte_length), const_data_ptr_cast(&total_offset), byte_length); - auto &key = variant.GetKey(row, i); - - memcpy(string_ptr + total_offset, key.GetData(), key.GetSize()); - total_offset += key.GetSize(); +static optional_ptr FindChildWriterByName(ColumnWriter &parent, const char *name) { + for (auto &child : parent.ChildWriters()) { + if (child->Schema().name == name) { + return *child; } - memcpy(offset_ptr + (NumericCast(dictionary_count) * byte_length), const_data_ptr_cast(&total_offset), - byte_length); - D_ASSERT(offset_ptr + (NumericCast(dictionary_count + 1) * byte_length) == string_ptr); - D_ASSERT(string_ptr + total_offset == metadata_blob_data + total_length); - metadata_blob.SetSizeAndFinalize(total_length, total_length); - -#ifdef DEBUG - auto decoded_metadata = VariantMetadata(metadata_blob); - D_ASSERT(decoded_metadata.strings.size() == dictionary_count); - for (idx_t i = 0; i < dictionary_count; i++) { - D_ASSERT(decoded_metadata.strings[i] == variant.GetKey(row, i).GetString()); - } -#endif - } -} - -namespace { - -static unordered_set GetVariantType(const LogicalType &type) { - if (type.id() == LogicalTypeId::ANY) { - return {}; - } - switch (type.id()) { - case LogicalTypeId::STRUCT: - return {VariantLogicalType::OBJECT}; - case LogicalTypeId::LIST: - return {VariantLogicalType::ARRAY}; - case LogicalTypeId::BOOLEAN: - return {VariantLogicalType::BOOL_TRUE, VariantLogicalType::BOOL_FALSE}; - case LogicalTypeId::TINYINT: - return {VariantLogicalType::INT8}; - case LogicalTypeId::SMALLINT: - return {VariantLogicalType::INT16}; - case LogicalTypeId::INTEGER: - return {VariantLogicalType::INT32}; - case LogicalTypeId::BIGINT: - return {VariantLogicalType::INT64}; - case LogicalTypeId::FLOAT: - return {VariantLogicalType::FLOAT}; - case LogicalTypeId::DOUBLE: - return {VariantLogicalType::DOUBLE}; - case LogicalTypeId::DECIMAL: - return {VariantLogicalType::DECIMAL}; - case LogicalTypeId::DATE: - return {VariantLogicalType::DATE}; - case LogicalTypeId::TIME: - return {VariantLogicalType::TIME_MICROS}; - case LogicalTypeId::TIMESTAMP_TZ: - return {VariantLogicalType::TIMESTAMP_MICROS_TZ}; - case LogicalTypeId::TIMESTAMP_TZ_NS: - return {VariantLogicalType::TIMESTAMP_NANOS_TZ}; - case LogicalTypeId::TIMESTAMP: - return {VariantLogicalType::TIMESTAMP_MICROS}; - case LogicalTypeId::TIMESTAMP_NS: - return {VariantLogicalType::TIMESTAMP_NANOS}; - case LogicalTypeId::BLOB: - return {VariantLogicalType::BLOB}; - case LogicalTypeId::VARCHAR: - return {VariantLogicalType::VARCHAR}; - case LogicalTypeId::UUID: - return {VariantLogicalType::UUID}; - default: - throw BinderException("Type '%s' can't be translated to a VARIANT type", type.ToString()); - } -} - -struct ParquetVariantShreddingState : public VariantShreddingState { -public: - ParquetVariantShreddingState(const LogicalType &type, idx_t total_count) - : VariantShreddingState(type, total_count), variant_types(GetVariantType(type)) { - } - -public: - const unordered_set &GetVariantTypes() override { - return variant_types; - } - -private: - unordered_set variant_types; -}; - -struct ParquetVariantShredding : public VariantShredding { - ParquetVariantShredding() { - // for parquet untyped ("value") comes before typed ("typed_value") - untyped_value_index = 0; - typed_value_index = 1; - } - - void WriteVariantValues(UnifiedVariantVectorData &variant, Vector &result, optional_ptr sel, - optional_ptr value_index_sel, - optional_ptr result_sel, idx_t count) override; - -protected: - void WriteMissingField(Vector &vector, idx_t index) override; -}; - -} // namespace - -vector GetChildIndices(const UnifiedVariantVectorData &variant, idx_t row, const VariantNestedData &nested_data, - optional_ptr shredding_state) { - vector child_indices; - if (!shredding_state || shredding_state->type.id() != LogicalTypeId::STRUCT) { - for (idx_t i = 0; i < nested_data.child_count; i++) { - child_indices.push_back(i); - } - return child_indices; - } - auto shredded_fields = shredding_state->ObjectFields(); - - for (idx_t i = 0; i < nested_data.child_count; i++) { - auto keys_index = variant.GetKeysIndex(row, i + nested_data.children_idx); - auto &key = variant.GetKey(row, keys_index); - - if (shredded_fields.count(key.GetString())) { - //! This field is shredded on, omit it from the value - continue; - } - child_indices.push_back(i); - } - return child_indices; -} - -static idx_t AnalyzeValueData(const UnifiedVariantVectorData &variant, idx_t row, uint32_t values_index, - vector &offsets, optional_ptr shredding_state) { - idx_t total_size = 0; - //! Every value has at least a value header - total_size++; - - idx_t offset_size = offsets.size(); - VariantLogicalType type_id = VariantLogicalType::VARIANT_NULL; - if (variant.RowIsValid(row)) { - type_id = variant.GetTypeId(row, values_index); - } - switch (type_id) { - case VariantLogicalType::OBJECT: { - auto nested_data = VariantUtils::DecodeNestedData(variant, row, values_index); - - //! Calculate value and key offsets for all children - idx_t total_offset = 0; - uint32_t highest_keys_index = 0; - - auto child_indices = GetChildIndices(variant, row, nested_data, shredding_state); - if (nested_data.child_count && child_indices.empty()) { - //! All fields of the object are shredded, omit the object entirely - return 0; - } - - auto num_elements = child_indices.size(); - offsets.resize(offset_size + num_elements + 1); - - for (idx_t entry = 0; entry < child_indices.size(); entry++) { - auto i = child_indices[entry]; - auto keys_index = variant.GetKeysIndex(row, i + nested_data.children_idx); - auto values_index = variant.GetValuesIndex(row, i + nested_data.children_idx); - offsets[offset_size + entry] = total_offset; - - total_offset += AnalyzeValueData(variant, row, values_index, offsets, nullptr); - highest_keys_index = MaxValue(highest_keys_index, keys_index); - } - offsets[offset_size + num_elements] = total_offset; - - //! Calculate the sizes for the objects value data - auto field_id_size = CalculateByteLength(highest_keys_index); - auto field_offset_size = CalculateByteLength(total_offset); - const bool is_large = num_elements > NumericLimits::Maximum(); - - //! Now add the sizes for the objects value data - if (is_large) { - total_size += sizeof(uint32_t); - } else { - total_size += sizeof(uint8_t); - } - total_size += num_elements * field_id_size; - total_size += (num_elements + 1) * field_offset_size; - total_size += total_offset; - break; - } - case VariantLogicalType::ARRAY: { - auto nested_data = VariantUtils::DecodeNestedData(variant, row, values_index); - - idx_t total_offset = 0; - offsets.resize(offset_size + nested_data.child_count + 1); - for (idx_t i = 0; i < nested_data.child_count; i++) { - auto values_index = variant.GetValuesIndex(row, i + nested_data.children_idx); - offsets[offset_size + i] = total_offset; - - total_offset += AnalyzeValueData(variant, row, values_index, offsets, nullptr); - } - offsets[offset_size + nested_data.child_count] = total_offset; - - auto field_offset_size = CalculateByteLength(total_offset); - auto num_elements = nested_data.child_count; - const bool is_large = num_elements > NumericLimits::Maximum(); - - if (is_large) { - total_size += sizeof(uint32_t); - } else { - total_size += sizeof(uint8_t); - } - total_size += (num_elements + 1) * field_offset_size; - total_size += total_offset; - break; - } - case VariantLogicalType::BLOB: - case VariantLogicalType::VARCHAR: { - auto string_value = VariantUtils::DecodeStringData(variant, row, values_index); - total_size += string_value.GetSize(); - if (type_id == VariantLogicalType::BLOB || string_value.GetSize() > 64) { - //! Save as regular string value - total_size += sizeof(uint32_t); - } - break; - } - case VariantLogicalType::VARIANT_NULL: - case VariantLogicalType::BOOL_TRUE: - case VariantLogicalType::BOOL_FALSE: - break; - case VariantLogicalType::INT8: - total_size += sizeof(uint8_t); - break; - case VariantLogicalType::INT16: - total_size += sizeof(uint16_t); - break; - case VariantLogicalType::INT32: - total_size += sizeof(uint32_t); - break; - case VariantLogicalType::INT64: - total_size += sizeof(uint64_t); - break; - case VariantLogicalType::FLOAT: - total_size += sizeof(float); - break; - case VariantLogicalType::DOUBLE: - total_size += sizeof(double); - break; - case VariantLogicalType::DECIMAL: { - auto decimal_data = VariantUtils::DecodeDecimalData(variant, row, values_index); - total_size += 1; - if (decimal_data.width <= 9) { - total_size += sizeof(int32_t); - } else if (decimal_data.width <= 18) { - total_size += sizeof(int64_t); - } else if (decimal_data.width <= 38) { - total_size += sizeof(uhugeint_t); - } else { - throw InvalidInputException("Can't convert VARIANT DECIMAL(%d, %d) to Parquet VARIANT", decimal_data.width, - decimal_data.scale); - } - break; - } - case VariantLogicalType::UUID: - total_size += sizeof(uhugeint_t); - break; - case VariantLogicalType::DATE: - total_size += sizeof(uint32_t); - break; - case VariantLogicalType::TIME_MICROS: - case VariantLogicalType::TIMESTAMP_MICROS: - case VariantLogicalType::TIMESTAMP_NANOS: - case VariantLogicalType::TIMESTAMP_MICROS_TZ: - total_size += sizeof(uint64_t); - break; - case VariantLogicalType::UINT8: - // store as int16_t - total_size += sizeof(int16_t); - break; - case VariantLogicalType::UINT16: - // store as int32_t - total_size += sizeof(int32_t); - break; - case VariantLogicalType::UINT32: - case VariantLogicalType::UINT64: - case VariantLogicalType::UINT128: - case VariantLogicalType::INT128: - // try to store as int64_t - fail if it doesn't fit - total_size += sizeof(int64_t); - break; - case VariantLogicalType::INTERVAL: - case VariantLogicalType::BIGNUM: - case VariantLogicalType::BITSTRING: - case VariantLogicalType::TIMESTAMP_MILIS: - case VariantLogicalType::TIMESTAMP_SEC: - case VariantLogicalType::TIME_MICROS_TZ: - case VariantLogicalType::TIME_NANOS: - default: - throw InvalidInputException("Can't convert VARIANT of type '%s' to Parquet VARIANT", - EnumUtil::ToString(type_id)); - } - - return total_size; -} - -template -void WritePrimitiveTypeHeader(data_ptr_t &value_data) { - uint8_t value_header = 0; - value_header |= static_cast(VariantBasicType::PRIMITIVE); - value_header |= static_cast(TYPE_ID) << 2; - - *value_data = value_header; - value_data++; -} - -struct VariantSimpleCopy { - template - static void CopyValue(const_data_ptr_t source, data_ptr_t target) { - memcpy(target, source, sizeof(T)); - } -}; - -template -struct VariantSimpleConversion { - template - static void CopyValue(const_data_ptr_t source, data_ptr_t target) { - auto src = Load(source); - Store(static_cast(src), target); - } -}; - -template -struct VariantTryConvert { - template - static void CopyValue(const_data_ptr_t source, data_ptr_t target) { - auto src = Load(source); - Store(Cast::Operation(src), target); - } -}; - -template -void CopySimplePrimitiveData(const UnifiedVariantVectorData &variant, data_ptr_t &value_data, idx_t row, - uint32_t values_index) { - auto byte_offset = variant.GetByteOffset(row, values_index); - auto data = const_data_ptr_cast(variant.GetData(row).GetData()); - auto ptr = data + byte_offset; - OP::template CopyValue(ptr, value_data); - value_data += sizeof(T); -} - -void CopyUUIDData(const UnifiedVariantVectorData &variant, data_ptr_t &value_data, idx_t row, uint32_t values_index) { - auto byte_offset = variant.GetByteOffset(row, values_index); - auto data = const_data_ptr_cast(variant.GetData(row).GetData()); - auto ptr = data + byte_offset; - - auto uuid = Load(ptr); - BaseUUID::ToBlob(uuid, value_data); - value_data += sizeof(uhugeint_t); -} - -static void WritePrimitiveValueData(const UnifiedVariantVectorData &variant, idx_t row, uint32_t values_index, - data_ptr_t &value_data, const vector &offsets, idx_t &offset_index) { - VariantLogicalType type_id = VariantLogicalType::VARIANT_NULL; - if (variant.RowIsValid(row)) { - type_id = variant.GetTypeId(row, values_index); - } - - D_ASSERT(type_id != VariantLogicalType::OBJECT && type_id != VariantLogicalType::ARRAY); - switch (type_id) { - case VariantLogicalType::BLOB: - case VariantLogicalType::VARCHAR: { - auto string_value = VariantUtils::DecodeStringData(variant, row, values_index); - auto string_size = string_value.GetSize(); - if (type_id == VariantLogicalType::BLOB || string_size > 64) { - if (type_id == VariantLogicalType::BLOB) { - WritePrimitiveTypeHeader(value_data); - } else { - WritePrimitiveTypeHeader(value_data); - } - Store(string_size, value_data); - value_data += sizeof(uint32_t); - } else { - uint8_t value_header = 0; - value_header |= static_cast(VariantBasicType::SHORT_STRING); - value_header |= static_cast(string_size) << 2; - - *value_data = value_header; - value_data++; - } - memcpy(value_data, reinterpret_cast(string_value.GetData()), string_size); - value_data += string_size; - break; - } - case VariantLogicalType::VARIANT_NULL: - WritePrimitiveTypeHeader(value_data); - break; - case VariantLogicalType::BOOL_TRUE: - WritePrimitiveTypeHeader(value_data); - break; - case VariantLogicalType::BOOL_FALSE: - WritePrimitiveTypeHeader(value_data); - break; - case VariantLogicalType::INT8: - WritePrimitiveTypeHeader(value_data); - CopySimplePrimitiveData(variant, value_data, row, values_index); - break; - case VariantLogicalType::INT16: - WritePrimitiveTypeHeader(value_data); - CopySimplePrimitiveData(variant, value_data, row, values_index); - break; - case VariantLogicalType::INT32: - WritePrimitiveTypeHeader(value_data); - CopySimplePrimitiveData(variant, value_data, row, values_index); - break; - case VariantLogicalType::INT64: - WritePrimitiveTypeHeader(value_data); - CopySimplePrimitiveData(variant, value_data, row, values_index); - break; - case VariantLogicalType::FLOAT: - WritePrimitiveTypeHeader(value_data); - CopySimplePrimitiveData(variant, value_data, row, values_index); - break; - case VariantLogicalType::DOUBLE: - WritePrimitiveTypeHeader(value_data); - CopySimplePrimitiveData(variant, value_data, row, values_index); - break; - case VariantLogicalType::UUID: - WritePrimitiveTypeHeader(value_data); - CopyUUIDData(variant, value_data, row, values_index); - break; - case VariantLogicalType::DATE: - WritePrimitiveTypeHeader(value_data); - CopySimplePrimitiveData(variant, value_data, row, values_index); - break; - case VariantLogicalType::TIME_MICROS: - WritePrimitiveTypeHeader(value_data); - CopySimplePrimitiveData(variant, value_data, row, values_index); - break; - case VariantLogicalType::TIMESTAMP_MICROS: - WritePrimitiveTypeHeader(value_data); - CopySimplePrimitiveData(variant, value_data, row, values_index); - break; - case VariantLogicalType::TIMESTAMP_NANOS: - WritePrimitiveTypeHeader(value_data); - CopySimplePrimitiveData(variant, value_data, row, values_index); - break; - case VariantLogicalType::TIMESTAMP_MICROS_TZ: - WritePrimitiveTypeHeader(value_data); - CopySimplePrimitiveData(variant, value_data, row, values_index); - break; - case VariantLogicalType::DECIMAL: { - auto decimal_data = VariantUtils::DecodeDecimalData(variant, row, values_index); - - if (decimal_data.width > 38) { - throw InvalidInputException("Can't convert VARIANT DECIMAL(%d, %d) to Parquet VARIANT", decimal_data.width, - decimal_data.scale); - } else if (decimal_data.width <= 4) { - // DuckDB uses INT16 to store small decimals, but parquet only supports DECIMAL4 at minimum, so here we - // promote to INT32. - WritePrimitiveTypeHeader(value_data); - Store(NumericCast(decimal_data.scale), value_data); - value_data++; - const int32_t promoted = Load(decimal_data.value_ptr); - Store(promoted, value_data); - value_data += sizeof(int32_t); - } else if (decimal_data.width <= 9) { - WritePrimitiveTypeHeader(value_data); - Store(NumericCast(decimal_data.scale), value_data); - value_data++; - memcpy(value_data, decimal_data.value_ptr, sizeof(int32_t)); - value_data += sizeof(int32_t); - } else if (decimal_data.width <= 18) { - WritePrimitiveTypeHeader(value_data); - Store(NumericCast(decimal_data.scale), value_data); - value_data++; - memcpy(value_data, decimal_data.value_ptr, sizeof(int64_t)); - value_data += sizeof(int64_t); - } else if (decimal_data.width <= 38) { - WritePrimitiveTypeHeader(value_data); - Store(NumericCast(decimal_data.scale), value_data); - value_data++; - memcpy(value_data, decimal_data.value_ptr, sizeof(hugeint_t)); - value_data += sizeof(hugeint_t); - } else { - throw InternalException( - "Uncovered VARIANT(DECIMAL) -> Parquet VARIANT conversion for type 'DECIMAL(%d, %d)'", - decimal_data.width, decimal_data.scale); - } - break; - } - case VariantLogicalType::UINT8: - WritePrimitiveTypeHeader(value_data); - CopySimplePrimitiveData>(variant, value_data, row, values_index); - break; - case VariantLogicalType::UINT16: - WritePrimitiveTypeHeader(value_data); - CopySimplePrimitiveData>(variant, value_data, row, values_index); - break; - case VariantLogicalType::UINT32: - WritePrimitiveTypeHeader(value_data); - CopySimplePrimitiveData>(variant, value_data, row, values_index); - break; - case VariantLogicalType::UINT64: - WritePrimitiveTypeHeader(value_data); - CopySimplePrimitiveData>(variant, value_data, row, values_index); - break; - case VariantLogicalType::UINT128: - WritePrimitiveTypeHeader(value_data); - CopySimplePrimitiveData>(variant, value_data, row, values_index); - break; - case VariantLogicalType::INT128: - WritePrimitiveTypeHeader(value_data); - CopySimplePrimitiveData>(variant, value_data, row, values_index); - break; - case VariantLogicalType::INTERVAL: - case VariantLogicalType::BIGNUM: - case VariantLogicalType::BITSTRING: - case VariantLogicalType::TIMESTAMP_MILIS: - case VariantLogicalType::TIMESTAMP_SEC: - case VariantLogicalType::TIME_MICROS_TZ: - case VariantLogicalType::TIME_NANOS: - default: - throw InvalidInputException("Can't convert VARIANT of type '%s' to Parquet VARIANT", - EnumUtil::ToString(type_id)); - } -} - -static void WriteValueData(const UnifiedVariantVectorData &variant, idx_t row, uint32_t values_index, - data_ptr_t &value_data, const vector &offsets, idx_t &offset_index, - optional_ptr shredding_state) { - VariantLogicalType type_id = VariantLogicalType::VARIANT_NULL; - if (variant.RowIsValid(row)) { - type_id = variant.GetTypeId(row, values_index); - } - if (type_id == VariantLogicalType::OBJECT) { - auto nested_data = VariantUtils::DecodeNestedData(variant, row, values_index); - - //! -- Object value header -- - - auto child_indices = GetChildIndices(variant, row, nested_data, shredding_state); - if (nested_data.child_count && child_indices.empty()) { - throw InternalException( - "The entire should be omitted, should have been handled by the Analyze step already"); - } - auto num_elements = child_indices.size(); - - //! Determine the 'field_id_size' - uint32_t highest_keys_index = 0; - for (auto &i : child_indices) { - auto keys_index = variant.GetKeysIndex(row, i + nested_data.children_idx); - highest_keys_index = MaxValue(highest_keys_index, keys_index); - } - auto field_id_size = CalculateByteLength(highest_keys_index); - - uint32_t last_offset = 0; - if (num_elements) { - last_offset = offsets[offset_index + num_elements]; - } - offset_index += num_elements + 1; - auto field_offset_size = CalculateByteLength(last_offset); - - const bool is_large = num_elements > NumericLimits::Maximum(); - - uint8_t value_header = 0; - value_header |= static_cast(VariantBasicType::OBJECT); - value_header |= static_cast(is_large) << 6; - value_header |= (static_cast(field_id_size) - 1) << 4; - value_header |= (static_cast(field_offset_size) - 1) << 2; - -#ifdef DEBUG - auto object_value_header = VariantValueMetadata::FromHeaderByte(value_header); - D_ASSERT(object_value_header.basic_type == VariantBasicType::OBJECT); - D_ASSERT(object_value_header.is_large == is_large); - D_ASSERT(object_value_header.field_offset_size == field_offset_size); - D_ASSERT(object_value_header.field_id_size == field_id_size); -#endif - - *value_data = value_header; - value_data++; - - //! Write the 'num_elements' - if (is_large) { - Store(static_cast(num_elements), value_data); - value_data += sizeof(uint32_t); - } else { - Store(static_cast(num_elements), value_data); - value_data += sizeof(uint8_t); - } - - //! Write the 'field_id' entries - for (auto &i : child_indices) { - auto keys_index = variant.GetKeysIndex(row, i + nested_data.children_idx); - memcpy(value_data, reinterpret_cast(&keys_index), field_id_size); - value_data += field_id_size; - } - - //! Write the 'field_offset' entries and the child 'value's - auto children_ptr = value_data + ((num_elements + 1) * field_offset_size); - idx_t total_offset = 0; - for (auto &i : child_indices) { - auto values_index = variant.GetValuesIndex(row, i + nested_data.children_idx); - - memcpy(value_data, reinterpret_cast(&total_offset), field_offset_size); - value_data += field_offset_size; - auto start_ptr = children_ptr; - WriteValueData(variant, row, values_index, children_ptr, offsets, offset_index, nullptr); - total_offset += (children_ptr - start_ptr); - } - memcpy(value_data, reinterpret_cast(&total_offset), field_offset_size); - value_data += field_offset_size; - D_ASSERT(children_ptr - total_offset == value_data); - value_data = children_ptr; - } else if (type_id == VariantLogicalType::ARRAY) { - auto nested_data = VariantUtils::DecodeNestedData(variant, row, values_index); - - //! -- Array value header -- - - uint32_t last_offset = 0; - if (nested_data.child_count) { - last_offset = offsets[offset_index + nested_data.child_count]; - } - offset_index += nested_data.child_count + 1; - auto field_offset_size = CalculateByteLength(last_offset); - - auto num_elements = nested_data.child_count; - const bool is_large = num_elements > NumericLimits::Maximum(); - - uint8_t value_header = 0; - value_header |= static_cast(VariantBasicType::ARRAY); - value_header |= static_cast(is_large) << 4; - value_header |= (static_cast(field_offset_size) - 1) << 2; - -#ifdef DEBUG - auto array_value_header = VariantValueMetadata::FromHeaderByte(value_header); - D_ASSERT(array_value_header.basic_type == VariantBasicType::ARRAY); - D_ASSERT(array_value_header.is_large == is_large); - D_ASSERT(array_value_header.field_offset_size == field_offset_size); -#endif - - *value_data = value_header; - value_data++; - - //! Write the 'num_elements' - if (is_large) { - Store(static_cast(num_elements), value_data); - value_data += sizeof(uint32_t); - } else { - Store(static_cast(num_elements), value_data); - value_data += sizeof(uint8_t); - } - - //! Write the 'field_offset' entries and the child 'value's - auto children_ptr = value_data + ((num_elements + 1) * field_offset_size); - idx_t total_offset = 0; - for (idx_t i = 0; i < nested_data.child_count; i++) { - auto values_index = variant.GetValuesIndex(row, i + nested_data.children_idx); - - memcpy(value_data, reinterpret_cast(&total_offset), field_offset_size); - value_data += field_offset_size; - auto start_ptr = children_ptr; - WriteValueData(variant, row, values_index, children_ptr, offsets, offset_index, nullptr); - total_offset += (children_ptr - start_ptr); - } - memcpy(value_data, reinterpret_cast(&total_offset), field_offset_size); - value_data += field_offset_size; - D_ASSERT(children_ptr - total_offset == value_data); - value_data = children_ptr; - } else { - WritePrimitiveValueData(variant, row, values_index, value_data, offsets, offset_index); } + return nullptr; } -static void CreateValues(UnifiedVariantVectorData &variant, Vector &value, optional_ptr sel, - optional_ptr value_index_sel, - optional_ptr result_sel, - optional_ptr shredding_state, idx_t count) { - auto &validity = FlatVector::ValidityMutable(value); - auto value_data = FlatVector::GetDataMutable(value); - - for (idx_t i = 0; i < count; i++) { - idx_t value_index = 0; - if (value_index_sel) { - value_index = value_index_sel->get_index(i); - } - - idx_t row = i; - if (sel) { - row = sel->get_index(i); - } - - idx_t result_index = i; - if (result_sel) { - result_index = result_sel->get_index(i); - } - - bool is_shredded = false; - if (variant.RowIsValid(row) && shredding_state && shredding_state->ValueIsShredded(variant, row, value_index)) { - shredding_state->SetShredded(row, value_index, result_index); - is_shredded = true; - if (shredding_state->type.id() != LogicalTypeId::STRUCT) { - //! Value is shredded, directly write a NULL to the 'value' if the type is not an OBJECT - //! When the type is OBJECT, all excess fields would still need to be written to the 'value' - validity.SetInvalid(result_index); - continue; - } - } +static void MarkTypedValueShreddingGroupsRequired(ColumnWriter &typed_value); - //! The (relative) offsets for each value, in the case of nesting - vector offsets; - //! Determine the size of this 'value' blob - idx_t blob_length = AnalyzeValueData(variant, row, value_index, offsets, shredding_state); - if (!blob_length) { - //! This is only allowed to happen for a shredded OBJECT, where there are no excess fields to write for the - //! OBJECT - (void)is_shredded; - D_ASSERT(is_shredded); - validity.SetInvalid(result_index); - continue; - } - value_data[result_index] = StringVector::EmptyString(value, blob_length); - auto &value_blob = value_data[result_index]; - auto value_blob_data = reinterpret_cast(value_blob.GetDataWriteable()); - - idx_t offset_index = 0; - WriteValueData(variant, row, value_index, value_blob_data, offsets, offset_index, shredding_state); - D_ASSERT(data_ptr_cast(value_blob.GetDataWriteable() + blob_length) == value_blob_data); - value_blob.SetSizeAndFinalize(blob_length, blob_length); +// Field/element group: mark REQUIRED, then recurse into nested typed_value if present +static void MarkShreddedGroupRequired(ColumnWriter &group) { + D_ASSERT(StructType::IsStruct(group.Type().id())); + group.MarkRepetitionRequired(); + auto nested_typed_value = FindChildWriterByName(group, "typed_value"); + if (nested_typed_value) { + MarkTypedValueShreddingGroupsRequired(*nested_typed_value); } } -void ParquetVariantShredding::WriteMissingField(Vector &vector, idx_t index) { - //! The field is missing, set it to null - FlatVector::SetNull(vector, index, true); -} - -void ParquetVariantShredding::WriteVariantValues(UnifiedVariantVectorData &variant, Vector &result, - optional_ptr sel, - optional_ptr value_index_sel, - optional_ptr result_sel, idx_t count) { - optional_ptr value; - optional_ptr typed_value; - - auto &result_type = result.GetType(); - D_ASSERT(result_type.id() == LogicalTypeId::STRUCT); - auto &child_types = StructType::GetChildTypes(result_type); - auto &child_vectors = StructVector::GetEntries(result); - D_ASSERT(child_types.size() == child_vectors.size()); - for (idx_t i = 0; i < child_types.size(); i++) { - auto &name = child_types[i].first; - if (name == "value") { - value = &child_vectors[i]; - } else if (name == "typed_value") { - typed_value = &child_vectors[i]; - } - } - - if (typed_value) { - ParquetVariantShreddingState shredding_state(typed_value->GetType(), count); - CreateValues(variant, *value, sel, value_index_sel, result_sel, &shredding_state, count); - - SelectionVector null_values; - if (shredding_state.count) { - WriteTypedValues(variant, *typed_value, shredding_state.shredded_sel, shredding_state.values_index_sel, - shredding_state.result_sel, shredding_state.count); - //! 'shredding_state.result_sel' will always be a subset of 'result_sel', set the rows not in the subset to - //! NULL - idx_t sel_idx = 0; - for (idx_t i = 0; i < count; i++) { - auto original_index = result_sel ? result_sel->get_index(i) : i; - if (sel_idx < shredding_state.count && shredding_state.result_sel[sel_idx] == original_index) { - sel_idx++; - continue; - } - FlatVector::SetNull(*typed_value, original_index, true); - } - } else { - //! Set all rows of the typed_value to NULL, nothing is shredded on - for (idx_t i = 0; i < count; i++) { - FlatVector::SetNull(*typed_value, result_sel ? result_sel->get_index(i) : i, true); - } +// Field/element groups under typed_value must be REQUIRED (VariantShredding.md) +static void MarkTypedValueShreddingGroupsRequired(ColumnWriter &typed_value) { + auto type_id = typed_value.Type().id(); + if (StructType::IsStruct(type_id)) { + for (auto &field_group : typed_value.ChildWriters()) { + MarkShreddedGroupRequired(*field_group); } - } else { - CreateValues(variant, *value, sel, value_index_sel, result_sel, nullptr, count); + } else if (type_id == LogicalTypeId::LIST || type_id == LogicalTypeId::ARRAY) { + D_ASSERT(typed_value.ChildWriters().size() == 1); + MarkShreddedGroupRequired(*typed_value.ChildWriters()[0]); } } -static void ToParquetVariant(DataChunk &input, ExpressionState &state, Vector &result) { - // DuckDB Variant: - // - keys = VARCHAR[] - // - children = STRUCT(keys_index UINTEGER, values_index UINTEGER)[] - // - values = STRUCT(type_id UTINYINT, byte_offset UINTEGER)[] - // - data = BLOB - - // Parquet VARIANT: - // - metadata = BLOB - // - value = BLOB - - const auto &variant_vec = input.data[0]; - auto count = input.size(); - - RecursiveUnifiedVectorFormat recursive_format; - Vector::RecursiveToUnifiedFormat(variant_vec, recursive_format); - UnifiedVariantVectorData variant(recursive_format); - - auto &result_vectors = StructVector::GetEntries(result); - auto &metadata = result_vectors[0]; - CreateMetadata(variant, metadata, count); - - ParquetVariantShredding shredding; - shredding.WriteVariantValues(variant, result, nullptr, nullptr, nullptr, count); -} - idx_t VariantColumnWriter::FinalizeSchema(vector &schemas) { idx_t schema_idx = schemas.size(); @@ -932,6 +63,12 @@ idx_t VariantColumnWriter::FinalizeSchema(vector auto &name = schema.name; auto &field_id = schema.field_id; + // Mark shredded field/element groups REQUIRED (reserved Parquet names only) + auto typed_value = FindChildWriterByName(*this, "typed_value"); + if (typed_value) { + MarkTypedValueShreddingGroupsRequired(*typed_value); + } + // variant group duckdb_parquet::SchemaElement top_element; top_element.repetition_type = repetition_type; @@ -965,7 +102,7 @@ LogicalType VariantColumnWriter::TransformTypedValueRecursive(const LogicalType for (auto &entry : child_types) { child_list_t child_children; child_children.emplace_back("value", LogicalType::BLOB); - if (entry.second.id() != LogicalTypeId::VARIANT) { + if (entry.second.id() != LogicalTypeId::VARIANT && entry.second.id() != LogicalTypeId::SQLNULL) { child_children.emplace_back("typed_value", TransformTypedValueRecursive(entry.second)); } replaced_types.emplace_back(entry.first, LogicalType::STRUCT(child_children)); @@ -976,7 +113,7 @@ LogicalType VariantColumnWriter::TransformTypedValueRecursive(const LogicalType auto &child_type = ListType::GetChildType(type); child_list_t replaced_types; replaced_types.emplace_back("value", LogicalType::BLOB); - if (child_type.id() != LogicalTypeId::VARIANT) { + if (child_type.id() != LogicalTypeId::VARIANT && child_type.id() != LogicalTypeId::SQLNULL) { replaced_types.emplace_back("typed_value", TransformTypedValueRecursive(child_type)); } return LogicalType::LIST(LogicalType::STRUCT(replaced_types)); @@ -1029,8 +166,29 @@ static unique_ptr BindTransform(BindScalarFunctionInput &input) { return nullptr; } -ScalarFunction VariantColumnWriter::GetTransformFunction() { - ScalarFunction transform("variant_to_parquet_variant", {}, LogicalType::ANY, ToParquetVariant, BindTransform); +//! The conversion itself lives in core (ParquetVariantConversion::ToParquetVariant), so the +//! arrow.parquet.variant extension type can call it directly without going through the binder. +static void ToParquetVariantFunction(DataChunk &input, ExpressionState &state, Vector &result) { + ParquetVariantConversion::ToParquetVariant(input.data[0], input.size(), result); +} + +static void ToParquetVariantWriteFunction(DataChunk &input, ExpressionState &state, Vector &result) { + ToParquetVariantFunction(input, state, result); + // Preserve SQL NULL at the VARIANT group. + auto validity = input.data[0].Validity(); + if (validity.CannotHaveNull()) { + return; + } + for (idx_t i = 0; i < input.size(); i++) { + if (!validity.IsValid(i)) { + FlatVector::SetNull(result, i, true); + } + } +} + +ScalarFunction VariantColumnWriter::GetTransformFunction(bool preserve_nulls) { + auto function = preserve_nulls ? ToParquetVariantWriteFunction : ToParquetVariantFunction; + ScalarFunction transform("variant_to_parquet_variant", {}, LogicalType::ANY, function, BindTransform); transform.GetSignature().AddParameter("variant", LogicalType::VARIANT()); transform.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); // throws for values that are out of range for the parquet variant encoding @@ -1038,4 +196,16 @@ ScalarFunction VariantColumnWriter::GetTransformFunction() { return transform; } +static void VariantBytesToVariantFunction(DataChunk &input, ExpressionState &state, Vector &result) { + ParquetVariantConversion::ConvertBinary(input.data[0], result, input.size()); +} + +ScalarFunction VariantColumnWriter::GetBytesToVariantFunction() { + ScalarFunction function("variant_bytes_to_variant", {}, LogicalType::VARIANT(), VariantBytesToVariantFunction); + function.GetSignature().AddParameter("blob", LogicalType::BLOB); + function.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); + function.SetFallible(); + return function; +} + } // namespace duckdb diff --git a/src/duckdb/generated_extension_loader_package_build.cpp b/src/duckdb/generated_extension_loader_package_build.cpp index 2ff26a8ce..c64d9ec90 100644 --- a/src/duckdb/generated_extension_loader_package_build.cpp +++ b/src/duckdb/generated_extension_loader_package_build.cpp @@ -1,3 +1,4 @@ +// Generated by package_build.py. Do not edit. #ifndef DUCKDB_EXTENSION_CORE_FUNCTIONS_LINKED #define DUCKDB_EXTENSION_CORE_FUNCTIONS_LINKED 1 #endif @@ -14,74 +15,180 @@ #define DUCKDB_EXTENSION_JSON_LINKED 1 #endif +#include "duckdb/main/extension/extension_loader.hpp" +#include "duckdb_static_extension.h" + +extern "C" int32_t duckdb_extension_httplib_describe(duckdb_extension_descriptor *descriptor); + +extern "C" int32_t duckdb_extension_loadable_extensions_describe(duckdb_extension_descriptor *descriptor); + #if DUCKDB_EXTENSION_CORE_FUNCTIONS_LINKED -#include "core_functions_extension.hpp" +#ifndef EXT_VERSION_CORE_FUNCTIONS +#define EXT_VERSION_CORE_FUNCTIONS "" #endif -#if DUCKDB_EXTENSION_PARQUET_LINKED -#include "parquet_extension.hpp" +// Generated from extension/loader/extension_describe.c.in. Do not edit. +// Describes the core_functions extension to duckdb_register_static_extension. + +#include "duckdb_static_extension.h" + +extern "C" void core_functions_duckdb_cpp_init(duckdb::ExtensionLoader &loader); + +#ifdef __cplusplus +extern "C" { #endif -#if DUCKDB_EXTENSION_ICU_LINKED -#include "icu_extension.hpp" + +int32_t duckdb_extension_core_functions_describe(duckdb_extension_descriptor *descriptor) { + if (descriptor->version < 1) { + descriptor->set_error(descriptor, "extension core_functions needs descriptor layout 1"); + return 1; + } + descriptor->version = 1; + descriptor->name = "core_functions"; + descriptor->extension_version = EXT_VERSION_CORE_FUNCTIONS; + descriptor->api_version = "v2.1.0-alpha48443"; + descriptor->entry_cpp = (void (*)(void))core_functions_duckdb_cpp_init; + return 0; +} + +#ifdef __cplusplus +} #endif -#if DUCKDB_EXTENSION_JSON_LINKED -#include "json_extension.hpp" #endif -#include "duckdb/main/extension/generated_extension_loader.hpp" -#include "duckdb/main/extension_helper.hpp" -#include "duckdb/main/config.hpp" -#include "duckdb/main/database.hpp" +#if DUCKDB_EXTENSION_PARQUET_LINKED +#ifndef EXT_VERSION_PARQUET +#define EXT_VERSION_PARQUET "" +#endif +// Generated from extension/loader/extension_describe.c.in. Do not edit. +// Describes the parquet extension to duckdb_register_static_extension. + +#include "duckdb_static_extension.h" -namespace duckdb { +extern "C" void parquet_duckdb_cpp_init(duckdb::ExtensionLoader &loader); -//! Publishes the package_build.py-generated list of extensions linked into this binary onto the config, so that -//! ExtensionHelper::LoadExtension can find them - including when it is called from code carrying its -//! own copy of DuckDB, which links no generated loader of its own. -void ExtensionHelper::RegisterLinkedExtensions(DBConfig &config) { -#if DUCKDB_EXTENSION_CORE_FUNCTIONS_LINKED - config.linked_extensions.push_back({"core_functions", [](DuckDB &db) { - db.LoadStaticExtension(); - }}); +#ifdef __cplusplus +extern "C" { +#endif + +int32_t duckdb_extension_parquet_describe(duckdb_extension_descriptor *descriptor) { + if (descriptor->version < 1) { + descriptor->set_error(descriptor, "extension parquet needs descriptor layout 1"); + return 1; + } + descriptor->version = 1; + descriptor->name = "parquet"; + descriptor->extension_version = EXT_VERSION_PARQUET; + descriptor->api_version = "v2.1.0-alpha48443"; + descriptor->entry_cpp = (void (*)(void))parquet_duckdb_cpp_init; + return 0; +} + +#ifdef __cplusplus +} #endif -#if DUCKDB_EXTENSION_PARQUET_LINKED - config.linked_extensions.push_back({"parquet", [](DuckDB &db) { - db.LoadStaticExtension(); - }}); #endif + #if DUCKDB_EXTENSION_ICU_LINKED - config.linked_extensions.push_back({"icu", [](DuckDB &db) { - db.LoadStaticExtension(); - }}); +#ifndef EXT_VERSION_ICU +#define EXT_VERSION_ICU "" #endif +// Generated from extension/loader/extension_describe.c.in. Do not edit. +// Describes the icu extension to duckdb_register_static_extension. + +#include "duckdb_static_extension.h" + +extern "C" void icu_duckdb_cpp_init(duckdb::ExtensionLoader &loader); + +#ifdef __cplusplus +extern "C" { +#endif + +int32_t duckdb_extension_icu_describe(duckdb_extension_descriptor *descriptor) { + if (descriptor->version < 1) { + descriptor->set_error(descriptor, "extension icu needs descriptor layout 1"); + return 1; + } + descriptor->version = 1; + descriptor->name = "icu"; + descriptor->extension_version = EXT_VERSION_ICU; + descriptor->api_version = "v2.1.0-alpha48443"; + descriptor->entry_cpp = (void (*)(void))icu_duckdb_cpp_init; + return 0; +} + +#ifdef __cplusplus +} +#endif +#endif + #if DUCKDB_EXTENSION_JSON_LINKED - config.linked_extensions.push_back({"json", [](DuckDB &db) { - db.LoadStaticExtension(); - }}); +#ifndef EXT_VERSION_JSON +#define EXT_VERSION_JSON "" +#endif +// Generated from extension/loader/extension_describe.c.in. Do not edit. +// Describes the json extension to duckdb_register_static_extension. + +#include "duckdb_static_extension.h" + +extern "C" void json_duckdb_cpp_init(duckdb::ExtensionLoader &loader); + +#ifdef __cplusplus +extern "C" { #endif +int32_t duckdb_extension_json_describe(duckdb_extension_descriptor *descriptor) { + if (descriptor->version < 1) { + descriptor->set_error(descriptor, "extension json needs descriptor layout 1"); + return 1; + } + descriptor->version = 1; + descriptor->name = "json"; + descriptor->extension_version = EXT_VERSION_JSON; + descriptor->api_version = "v2.1.0-alpha48443"; + descriptor->entry_cpp = (void (*)(void))json_duckdb_cpp_init; + return 0; } -vector LinkedExtensions(){ - vector VEC = { +#ifdef __cplusplus +} +#endif +#endif + +#ifndef DUCKDB_STATIC_EXTENSION_LOADER_API +#if defined(__GNUC__) || defined(__clang__) +#define DUCKDB_STATIC_EXTENSION_LOADER_API __attribute__((visibility("hidden"))) +#else +#define DUCKDB_STATIC_EXTENSION_LOADER_API +#endif +#endif + +extern "C" DUCKDB_STATIC_EXTENSION_LOADER_API int32_t duckdb_register_static_extensions(void) { + int32_t result = 0; + if (duckdb_register_static_extension(duckdb_extension_httplib_describe) != 0) { + result = 1; + } + if (duckdb_register_static_extension(duckdb_extension_loadable_extensions_describe) != 0) { + result = 1; + } #if DUCKDB_EXTENSION_CORE_FUNCTIONS_LINKED - "core_functions", + if (duckdb_register_static_extension(duckdb_extension_core_functions_describe) != 0) { + result = 1; + } #endif #if DUCKDB_EXTENSION_PARQUET_LINKED - "parquet", + if (duckdb_register_static_extension(duckdb_extension_parquet_describe) != 0) { + result = 1; + } #endif #if DUCKDB_EXTENSION_ICU_LINKED - "icu", + if (duckdb_register_static_extension(duckdb_extension_icu_describe) != 0) { + result = 1; + } #endif #if DUCKDB_EXTENSION_JSON_LINKED - "json", + if (duckdb_register_static_extension(duckdb_extension_json_describe) != 0) { + result = 1; + } #endif - }; - return VEC; -} - -vector ExtensionHelper::LoadedExtensionTestPaths(){ - vector VEC = { - }; - return VEC; + return result; } -} \ No newline at end of file diff --git a/src/duckdb/src/catalog/catalog.cpp b/src/duckdb/src/catalog/catalog.cpp index dd8fe6f53..e286ec9e8 100644 --- a/src/duckdb/src/catalog/catalog.cpp +++ b/src/duckdb/src/catalog/catalog.cpp @@ -1,3 +1,4 @@ +#include "duckdb/common/exception/parser_exception.hpp" #include "duckdb/catalog/catalog.hpp" #include "duckdb/catalog/catalog_search_path.hpp" @@ -13,6 +14,7 @@ #include "duckdb/main/database.hpp" #include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/main/extension_helper.hpp" +#include "duckdb/parser/parsed_data/alter_schema_info.hpp" #include "duckdb/parser/parsed_data/alter_table_info.hpp" #include "duckdb/parser/parsed_data/create_aggregate_function_info.hpp" #include "duckdb/parser/parsed_data/create_collation_info.hpp" @@ -35,7 +37,6 @@ #include "duckdb/planner/expression_binder/index_binder.hpp" #include "duckdb/catalog/default/default_types.hpp" #include "duckdb/main/extension_entries.hpp" -#include "duckdb/main/extension/generated_extension_loader.hpp" #include "duckdb/main/connection.hpp" #include "duckdb/main/attached_database.hpp" #include "duckdb/main/database_manager.hpp" @@ -77,8 +78,9 @@ Catalog &Catalog::GetSystemCatalog(ClientContext &context) { return Catalog::GetSystemCatalog(*context.db); } +//! The default catalog for lookups; empty when no database is attached, in which case the lookup finds nothing there. Identifier GetDefaultCatalog(CatalogEntryRetriever &retriever) { - return DatabaseManager::GetDefaultDatabase(retriever.GetContext()); + return DatabaseManager::TryGetDefaultDatabase(retriever.GetContext()); } optional_ptr Catalog::GetCatalogEntry(CatalogEntryRetriever &retriever, const Identifier &catalog_name) { @@ -120,6 +122,10 @@ Catalog &Catalog::GetCatalog(ClientContext &context, const Identifier &catalog_n // Schema //===--------------------------------------------------------------------===// optional_ptr Catalog::CreateSchema(ClientContext &context, CreateSchemaInfo &info) { + auto supports_create_schema = SupportsCreateSchema(info); + if (supports_create_schema.HasError()) { + supports_create_schema.Throw(); + } return CreateSchema(GetCatalogTransaction(context), info); } @@ -480,42 +486,143 @@ optional_ptr Catalog::GetSchema(ClientContext &context, cons return GetSchema(retriever, schema_lookup, if_not_found); } -static optional_ptr NavigateNestedSchema(CatalogTransaction transaction, SchemaCatalogEntry &parent, - const Identifier &name, OnEntryNotFound if_not_found) { - // go through the schema's own lookup so that nested schemas work for any catalog, not only DuckCatalog - EntryLookupInfo nested_lookup(CatalogType::SCHEMA_ENTRY, QualifiedName(name)); - auto entry = parent.LookupEntry(transaction, nested_lookup); - if (!entry) { - if (if_not_found == OnEntryNotFound::THROW_EXCEPTION) { - throw CatalogException("Schema with name %s does not exist!", name); +static bool TryParseSchemaPath(const string &schema_arg, vector &result) { + try { + result = QualifiedName::ParseComponents(schema_arg); + } catch (ParserException &) { + return false; + } + if (schema_arg.empty() || schema_arg.back() == '.') { + result.emplace_back(); + } + return true; +} + +static string EmptyComponentError(const string &schema_arg) { + return StringUtil::Format( + "Failed to parse schema path \"%s\" - schema paths cannot contain empty components. Schema names that " + "contain a '.' or a '\"' must be double-quoted (e.g. '\"my.schema\"')", + schema_arg); +} + +struct SchemaPathCandidateList { + vector> candidates; + string parse_error; +}; + +static SchemaPathCandidateList SchemaPathCandidates(Catalog &catalog, const string &schema_arg) { + SchemaPathCandidateList result; + vector literal_path; + literal_path.emplace_back(schema_arg); + vector nested_path; + if (!catalog.SupportsNestedSchemas() || !TryParseSchemaPath(schema_arg, nested_path)) { + result.candidates.push_back(std::move(literal_path)); + return result; + } + if (std::any_of(nested_path.begin(), nested_path.end(), [](const Identifier &part) { return part.empty(); })) { + result.parse_error = EmptyComponentError(schema_arg); + result.candidates.push_back(std::move(literal_path)); + return result; + } + bool is_literal = nested_path.size() == 1 && nested_path[0].GetIdentifierName() == schema_arg; + result.candidates.push_back(std::move(nested_path)); + if (!is_literal) { + result.candidates.push_back(std::move(literal_path)); + } + return result; +} + +QualifiedName Catalog::ResolveEntryName(ClientContext &context, const string &schema_arg, const string &entry_name, + CatalogType entry_type, optional_ptr at_clause) { + Identifier name(entry_name); + if (schema_arg.empty()) { + return QualifiedName(GetName(), Identifier(), std::move(name)); + } + auto candidates = SchemaPathCandidates(*this, schema_arg); + if (candidates.candidates.size() > 1 || !candidates.parse_error.empty()) { + for (auto &candidate : candidates.candidates) { + auto qualified_name = QualifiedName::FromCatalogSchema(GetName(), candidate, name); + EntryLookupInfo lookup(entry_type, qualified_name, at_clause, QueryErrorContext()); + CatalogEntryRetriever retriever(context); + if (LookupEntry(retriever, lookup, OnEntryNotFound::RETURN_NULL).entry) { + return qualified_name; + } + } + if (!candidates.parse_error.empty()) { + throw InvalidInputException("%s", candidates.parse_error); + } + } + return QualifiedName::FromCatalogSchema(GetName(), candidates.candidates[0], name); +} + +SchemaCatalogEntry &Catalog::ResolveSchema(ClientContext &context, const string &schema_arg) { + auto candidates = SchemaPathCandidates(*this, schema_arg); + if (candidates.candidates.size() > 1 || !candidates.parse_error.empty()) { + for (auto &candidate : candidates.candidates) { + auto entry = Catalog::GetSchema(context, GetName(), candidate, OnEntryNotFound::RETURN_NULL); + if (entry) { + return *entry; + } + } + if (!candidates.parse_error.empty()) { + throw InvalidInputException("%s", candidates.parse_error); } - return nullptr; } - return entry->Cast(); + return *Catalog::GetSchema(context, GetName(), candidates.candidates[0], OnEntryNotFound::THROW_EXCEPTION); +} + +optional_ptr +Catalog::LookupSchemaPath(CatalogTransaction transaction, const EntryLookupInfo &schema_lookup, + OnEntryNotFound if_not_found, + const std::function(const EntryLookupInfo &)> &lookup_root) { + auto &path = schema_lookup.GetQualifiedName().Path(); + optional_ptr schema; + // Bound schema lookups lead with the catalog; bare lookups contain just the schema name. + for (idx_t i = path.size() > 1 ? 1 : 0; i < path.size(); i++) { + if (path[i].empty()) { + continue; + } + EntryLookupInfo lookup(schema_lookup, QualifiedName(path[i])); + auto entry = schema ? schema->LookupEntry(transaction, lookup) : lookup_root(lookup); + if (!entry) { + if (if_not_found == OnEntryNotFound::THROW_EXCEPTION) { + throw CatalogException::MissingEntry(lookup, string()); + } + return nullptr; + } + schema = entry->Cast(); + } + return schema; } optional_ptr Catalog::GetSchema(ClientContext &context, const Identifier &catalog_name, const vector &schema_path, OnEntryNotFound if_not_found) { D_ASSERT(!schema_path.empty()); - // resolve the outermost schema (this also resolves the catalog), then navigate the nested-schema chain auto schema = GetSchema(context, catalog_name, schema_path[0], if_not_found); - for (idx_t i = 1; schema && i < schema_path.size(); i++) { - schema = - NavigateNestedSchema(schema->catalog.GetCatalogTransaction(context), *schema, schema_path[i], if_not_found); + if (!schema || schema_path.size() == 1) { + return schema; } - return schema; + auto &catalog = schema->catalog; + auto path = schema_path; + path.insert(path.begin(), catalog.GetName()); + EntryLookupInfo lookup(CatalogType::SCHEMA_ENTRY, QualifiedName::FromPath(std::move(path))); + return catalog.LookupSchemaPath( + catalog.GetCatalogTransaction(context), lookup, if_not_found, + [&](const EntryLookupInfo &) -> optional_ptr { return schema.get(); }); } optional_ptr Catalog::GetSchema(CatalogTransaction transaction, const vector &schema_path, OnEntryNotFound if_not_found) { D_ASSERT(!schema_path.empty()); - auto schema = GetSchema(transaction, schema_path[0], if_not_found); - for (idx_t i = 1; schema && i < schema_path.size(); i++) { - schema = NavigateNestedSchema(transaction, *schema, schema_path[i], if_not_found); - } - return schema; + auto path = schema_path; + path.insert(path.begin(), GetName()); + EntryLookupInfo lookup(CatalogType::SCHEMA_ENTRY, QualifiedName::FromPath(std::move(path))); + return LookupSchemaPath(transaction, lookup, if_not_found, + [&](const EntryLookupInfo &root) -> optional_ptr { + return LookupSchema(transaction, root, if_not_found).get(); + }); } SchemaCatalogEntry &Catalog::GetSchema(ClientContext &context, const Identifier &catalog_name, @@ -590,8 +697,9 @@ vector GetCatalogEntries(CatalogEntryRetriever &retriever, c auto &default_entry = search_path.GetDefault(); if (!IsInvalidCatalog(default_entry.GetCatalog())) { entries.emplace_back(default_entry.GetCatalog(), schema); - } else { - entries.emplace_back(DatabaseManager::GetDefaultDatabase(context), schema); + } else if (auto default_database = DatabaseManager::TryGetDefaultDatabase(context); + !IsInvalidCatalog(default_database)) { + entries.emplace_back(std::move(default_database), schema); } } } else if (IsInvalidSchema(schema)) { @@ -658,31 +766,27 @@ bool Catalog::TryAutoLoad(ClientContext &context, const string &original_name) n if (context.db->ExtensionIsLoaded(extension_name)) { return true; } -#ifndef DUCKDB_DISABLE_EXTENSION_LOAD if (!Settings::Get(context)) { return false; } try { - if (ExtensionHelper::CanAutoloadExtension(extension_name)) { + if (ExtensionHelper::CanAutoloadExtension(*context.db, extension_name)) { return ExtensionHelper::TryAutoLoadExtension(context, extension_name); } } catch (...) { return false; } -#endif return false; } String Catalog::AutoloadExtensionByConfigName(ClientContext &context, const Identifier &configuration_name) { -#ifndef DUCKDB_DISABLE_EXTENSION_LOAD if (Settings::Get(context)) { auto extension_name = ExtensionHelper::FindExtensionInEntries(configuration_name, EXTENSION_SETTINGS); - if (ExtensionHelper::CanAutoloadExtension(extension_name)) { + if (ExtensionHelper::CanAutoloadExtension(*context.db, extension_name)) { ExtensionHelper::AutoLoadExtension(context, extension_name); return extension_name; } } -#endif throw Catalog::UnrecognizedConfigurationError(context, configuration_name); } @@ -729,7 +833,6 @@ static bool CompareCatalogTypes(CatalogType type_a, CatalogType type_b) { } bool Catalog::AutoLoadExtensionByCatalogEntry(DatabaseInstance &db, CatalogType type, const Identifier &entry_name) { -#ifndef DUCKDB_DISABLE_EXTENSION_LOAD if (Settings::Get(db)) { string extension_name; if (IsAutoloadableFunction(type)) { @@ -753,12 +856,11 @@ bool Catalog::AutoLoadExtensionByCatalogEntry(DatabaseInstance &db, CatalogType extension_name = ExtensionHelper::FindExtensionInEntries(entry_name, EXTENSION_COLLATIONS); } - if (!extension_name.empty() && ExtensionHelper::CanAutoloadExtension(extension_name)) { + if (!extension_name.empty() && ExtensionHelper::CanAutoloadExtension(db, extension_name)) { ExtensionHelper::AutoLoadExtension(db, extension_name); return true; } } -#endif return false; } @@ -998,12 +1100,16 @@ CatalogEntryLookup Catalog::TryLookupEntryAcrossCatalogs(CatalogEntryRetriever & lookups.reserve(entries.size()); for (auto &entry : entries) { optional_ptr catalog_entry; - if (if_not_found == OnEntryNotFound::RETURN_NULL) { + if (if_not_found == OnEntryNotFound::RETURN_NULL || IsInvalidCatalog(entry.GetCatalog())) { catalog_entry = Catalog::GetCatalogEntry(retriever, entry.GetCatalog()); } else { catalog_entry = &Catalog::GetCatalog(retriever, entry.GetCatalog()); } if (!catalog_entry) { + if (IsInvalidCatalog(entry.GetCatalog())) { + // the search path's default-database entry, with no database attached: nothing to search there + continue; + } return {nullptr, nullptr, ErrorData()}; } D_ASSERT(catalog_entry); @@ -1396,12 +1502,19 @@ vector> Catalog::GetSchemas(CatalogEntryRetriever auto &search_path = retriever.GetSearchPath(); for (auto &entry : search_path.Get()) { - auto &catalog = Catalog::GetCatalog(retriever, entry.GetCatalog()); - if (inserted_catalogs.find(catalog) != inserted_catalogs.end()) { + auto catalog = Catalog::GetCatalogEntry(retriever, entry.GetCatalog()); + if (!catalog) { + if (IsInvalidCatalog(entry.GetCatalog())) { + // the search path's default-database entry, with no database attached + continue; + } + throw BinderException("Catalog %s does not exist!", entry.GetCatalog()); + } + if (inserted_catalogs.find(*catalog) != inserted_catalogs.end()) { continue; } - inserted_catalogs.insert(catalog); - catalogs.push_back(catalog); + inserted_catalogs.insert(*catalog); + catalogs.push_back(*catalog); } } else { catalogs.push_back(Catalog::GetCatalog(retriever, Identifier(catalog_name))); @@ -1459,6 +1572,14 @@ vector> Catalog::GetAllEntries(ClientContext &context, C } void Catalog::Alter(CatalogTransaction transaction, AlterInfo &info) { + if (info.type == AlterType::ALTER_SCHEMA) { + auto &schema_info = info.Cast(); + auto schema = GetSchema(transaction, schema_info.SchemaPath(), info.if_not_found); + if (!schema) { + return; + } + return AlterSchema(transaction, *schema, schema_info); + } if (transaction.HasContext()) { CatalogEntryRetriever retriever(transaction.GetContext()); EntryLookupInfo lookup_info(info.GetCatalogType(), info.GetQualifiedName()); @@ -1504,6 +1625,27 @@ ErrorData Catalog::SupportsCreateTable(BoundCreateTableInfo &info) { return ErrorData(); } +ErrorData Catalog::SupportsCreateSchema(CreateSchemaInfo &info) { + if (!info.options.empty()) { + return ErrorData( + ExceptionType::CATALOG, + StringUtil::Format("WITH clause is not supported for schemas in a %s catalog", GetCatalogType())); + } + return ErrorData(); +} + +void Catalog::AlterSchema(CatalogTransaction transaction, SchemaCatalogEntry &schema, AlterSchemaInfo &info) { + switch (info.alter_schema_type) { + case AlterSchemaType::SET_SCHEMA_OPTIONS: + throw NotImplementedException("SET () is not supported for schemas in a %s catalog", GetCatalogType()); + case AlterSchemaType::RESET_SCHEMA_OPTIONS: + throw NotImplementedException("RESET () is not supported for schemas in a %s catalog", + GetCatalogType()); + default: + throw InternalException("Unrecognized alter schema type!"); + } +} + optional Catalog::GetDefaultSchema() const { return Identifier(DEFAULT_SCHEMA); } diff --git a/src/duckdb/src/catalog/catalog_entry/aggregate_function_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/aggregate_function_catalog_entry.cpp index fd4ecc286..9debae64b 100644 --- a/src/duckdb/src/catalog/catalog_entry/aggregate_function_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/aggregate_function_catalog_entry.cpp @@ -9,10 +9,8 @@ namespace duckdb { AggregateFunctionCatalogEntry::AggregateFunctionCatalogEntry(Catalog &catalog, SchemaCatalogEntry &schema, CreateAggregateFunctionInfo &info) : FunctionEntry(CatalogType::AGGREGATE_FUNCTION_ENTRY, catalog, schema, info), functions(info.functions) { - functions.ApplyToFunctions([&](AggregateFunction &function) { - function.SetCatalogName(catalog.GetAttached().GetName()); - function.SetSchemaName(schema.name); - }); + functions.ApplyToFunctions( + [&](AggregateFunction &function) { function.SetQualifiedName(schema.GetQualifiedName(name)); }); } } // namespace duckdb diff --git a/src/duckdb/src/catalog/catalog_entry/column_dependency_manager.cpp b/src/duckdb/src/catalog/catalog_entry/column_dependency_manager.cpp index c1741e9f7..c7cefbceb 100644 --- a/src/duckdb/src/catalog/catalog_entry/column_dependency_manager.cpp +++ b/src/duckdb/src/catalog/catalog_entry/column_dependency_manager.cpp @@ -65,6 +65,15 @@ void ColumnDependencyManager::AddGeneratedColumn(LogicalIndex index, const vecto } } +ColumnDependencyManager ColumnDependencyManager::Copy() const { + ColumnDependencyManager result; + result.dependencies_map = dependencies_map; + result.dependents_map = dependents_map; + result.direct_dependencies = direct_dependencies; + result.deleted_columns = deleted_columns; + return result; +} + vector ColumnDependencyManager::RemoveColumn(LogicalIndex index, idx_t column_amount) { // Always add the initial column deleted_columns.insert(index); diff --git a/src/duckdb/src/catalog/catalog_entry/duck_index_entry.cpp b/src/duckdb/src/catalog/catalog_entry/duck_index_entry.cpp index 5f81bab17..5855aca6d 100644 --- a/src/duckdb/src/catalog/catalog_entry/duck_index_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/duck_index_entry.cpp @@ -7,8 +7,7 @@ namespace duckdb { -IndexDataTableInfo::IndexDataTableInfo(shared_ptr info_p, const Identifier &index_name_p) - : info(std::move(info_p)), index_name(index_name_p) { +IndexDataTableInfo::IndexDataTableInfo(shared_ptr info_p) : info(std::move(info_p)) { } void DuckIndexEntry::Rollback(CatalogEntry &) { @@ -18,7 +17,7 @@ void DuckIndexEntry::Rollback(CatalogEntry &) { if (!info->info) { return; } - info->info->GetIndexes().RemoveIndex(name); + info->info->GetIndexes().RemoveIndex(oid); } DuckIndexEntry::DuckIndexEntry(Catalog &catalog, SchemaCatalogEntry &schema, CreateIndexInfo &create_info, @@ -26,7 +25,7 @@ DuckIndexEntry::DuckIndexEntry(Catalog &catalog, SchemaCatalogEntry &schema, Cre : IndexCatalogEntry(catalog, schema, create_info), initial_index_size(0) { auto &table = table_p.Cast(); auto &storage = table.GetStorage(); - info = make_shared_ptr(storage.GetDataTableInfo(), name); + info = make_shared_ptr(storage.GetDataTableInfo()); } DuckIndexEntry::DuckIndexEntry(Catalog &catalog, SchemaCatalogEntry &schema, CreateIndexInfo &create_info, @@ -58,7 +57,7 @@ DataTableInfo &DuckIndexEntry::GetDataTableInfo() const { void DuckIndexEntry::CommitDrop(CommitDropState &drop_state) { D_ASSERT(info); - drop_state.RemoveIndex(GetDataTableInfo().GetIndexes(), name); + drop_state.RemoveIndex(GetDataTableInfo().GetIndexes(), oid); } } // namespace duckdb diff --git a/src/duckdb/src/catalog/catalog_entry/duck_schema_entry.cpp b/src/duckdb/src/catalog/catalog_entry/duck_schema_entry.cpp index 7e2b00a5c..e541d1441 100644 --- a/src/duckdb/src/catalog/catalog_entry/duck_schema_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/duck_schema_entry.cpp @@ -7,13 +7,12 @@ #include "duckdb/catalog/catalog_entry/copy_function_catalog_entry.hpp" #include "duckdb/catalog/catalog_entry/duck_index_entry.hpp" #include "duckdb/catalog/catalog_entry/duck_table_entry.hpp" +#include "duckdb/catalog/catalog_entry/macro_catalog_entry.hpp" #include "duckdb/catalog/catalog_entry/pragma_function_catalog_entry.hpp" #include "duckdb/catalog/catalog_entry/scalar_function_catalog_entry.hpp" -#include "duckdb/catalog/catalog_entry/scalar_macro_catalog_entry.hpp" #include "duckdb/catalog/catalog_entry/sequence_catalog_entry.hpp" #include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp" #include "duckdb/catalog/catalog_entry/table_function_catalog_entry.hpp" -#include "duckdb/catalog/catalog_entry/table_macro_catalog_entry.hpp" #include "duckdb/catalog/catalog_entry/type_catalog_entry.hpp" #include "duckdb/catalog/catalog_entry/view_catalog_entry.hpp" #include "duckdb/catalog/catalog_entry/window_function_catalog_entry.hpp" @@ -73,7 +72,7 @@ static void FindForeignKeyInformation(TableCatalogEntry &table, AlterForeignKeyT DuckSchemaEntry::DuckSchemaEntry(Catalog &catalog, CreateSchemaInfo &info, optional_ptr parent_schema_p) - : SchemaCatalogEntry(catalog, info), parent_schema(parent_schema_p), schemas(catalog), + : SchemaCatalogEntry(catalog, info, parent_schema_p), schemas(catalog), tables(catalog, catalog.IsSystemCatalog() ? make_uniq(catalog, *this) : nullptr), indexes(catalog), table_functions(catalog, @@ -94,31 +93,6 @@ unique_ptr DuckSchemaEntry::Copy(ClientContext &context) const { return std::move(result); } -unique_ptr DuckSchemaEntry::GetInfo() const { - auto result = make_uniq(); - // collect the parent chain (innermost first) - vector parents; - auto current = GetParentSchema(); - while (current) { - parents.push_back(current->name); - current = current->GetParentSchema(); - } - // build the schema path: top-level schemas serialize as [name] (unchanged), nested schemas root the path at the - // catalog so the full path can be navigated on load: [catalog, parent schemas (outermost first)..., name] - vector path; - if (!parents.empty()) { - path.push_back(catalog.GetName()); - for (auto it = parents.rbegin(); it != parents.rend(); ++it) { - path.push_back(*it); - } - } - path.push_back(name); - result->SetQualifiedName(QualifiedName(std::move(path), Identifier())); - result->comment = comment; - result->tags = tags; - return std::move(result); -} - optional_ptr DuckSchemaEntry::CreateSchema(CatalogTransaction transaction, CreateSchemaInfo &info) { LogicalDependencyList dependencies; dependencies.AddDependency(*this); @@ -170,8 +144,7 @@ optional_ptr DuckSchemaEntry::AddEntryInternal(CatalogTransaction throw CatalogException("Existing object %s is of type %s, trying to replace with type %s", entry_name, CatalogTypeToString(old_entry->type), CatalogTypeToString(entry_type)); } - OnDropEntry(transaction, *old_entry); - (void)set.DropEntry(transaction, entry_name, false, entry->internal); + (void)DropEntryInternal(transaction, *old_entry, entry_name, false, entry->internal); } } // now try to add the entry @@ -188,6 +161,27 @@ optional_ptr DuckSchemaEntry::AddEntryInternal(CatalogTransaction return result; } +bool DuckSchemaEntry::DropEntryInternal(CatalogTransaction transaction, CatalogEntry &entry, const Identifier &name, + bool cascade, bool allow_drop_internal) { + vector> fk_arrays; + if (entry.type == CatalogType::TABLE_ENTRY) { + // if there is a foreign key constraint, get that information + FindForeignKeyInformation(entry.Cast(), AlterForeignKeyType::AFT_DELETE, fk_arrays); + } + + OnDropEntry(transaction, entry); + auto &set = GetCatalogSet(entry.type); + if (!set.DropEntry(transaction, name, cascade, allow_drop_internal)) { + return false; + } + + // remove the foreign key constraint in main key table if main key table's name is valid + for (auto &fk_info : fk_arrays) { + Alter(transaction, *fk_info); + } + return true; +} + optional_ptr DuckSchemaEntry::CreateTable(CatalogTransaction transaction, BoundCreateTableInfo &info) { auto table = make_uniq(catalog, *this, info); auto &dependencies = info.Base().dependencies; @@ -239,13 +233,8 @@ optional_ptr DuckSchemaEntry::CreateFunction(CatalogTransaction tr info.Cast()); break; case CatalogType::MACRO_ENTRY: - // create a macro function - function = make_uniq_base(catalog, *this, info.Cast()); - break; - case CatalogType::TABLE_MACRO_ENTRY: - // create a macro table function - function = make_uniq_base(catalog, *this, info.Cast()); + function = MacroCatalogEntry::Create(catalog, *this, info.Cast()); break; case CatalogType::AGGREGATE_FUNCTION_ENTRY: D_ASSERT(info.type == CatalogType::AGGREGATE_FUNCTION_ENTRY); @@ -292,13 +281,6 @@ optional_ptr DuckSchemaEntry::CreateIndex(CatalogTransaction trans // indexes do not require CASCADE to be dropped, they are simply always dropped along with the table info.dependencies.AddDependency(table, DependencyDependentFlags()); - // currently, we can not alter PK/FK/UNIQUE constraints - // concurrency-safe name checks against other INDEX catalog entries happens in the catalog - if (info.on_conflict != OnCreateConflict::IGNORE_ON_CONFLICT && - !table.GetStorage().IndexNameIsUnique(info.GetIndexName().GetIdentifierName())) { - throw CatalogException("An index with the name " + info.GetIndexName() + " already exists!"); - } - auto index = make_uniq(catalog, *this, info, table); auto dependencies = index->dependencies; return AddEntryInternal(transaction, std::move(index), info.on_conflict, dependencies); @@ -356,8 +338,12 @@ void DuckSchemaEntry::Alter(CatalogTransaction transaction, AlterInfo &info) { void DuckSchemaEntry::Scan(ClientContext &context, CatalogType type, const std::function &callback) { - auto &set = GetCatalogSet(type); - set.Scan(GetCatalogTransaction(context), callback); + Scan(GetCatalogTransaction(context), type, callback); +} + +void DuckSchemaEntry::Scan(CatalogTransaction transaction, CatalogType type, + const std::function &callback) { + GetCatalogSet(type).Scan(transaction, callback); } void DuckSchemaEntry::Scan(CatalogType type, const std::function &callback) { @@ -389,23 +375,10 @@ void DuckSchemaEntry::DropEntry(ClientContext &context, DropInfo &info) { CatalogTypeToString(info.type)); } - vector> fk_arrays; - if (existing_entry->type == CatalogType::TABLE_ENTRY) { - // if there is a foreign key constraint, get that information - auto &table_entry = existing_entry->Cast(); - FindForeignKeyInformation(table_entry, AlterForeignKeyType::AFT_DELETE, fk_arrays); - } - - OnDropEntry(transaction, *existing_entry); - if (!set.DropEntry(transaction, info.GetQualifiedName().Name(), info.cascade, info.allow_drop_internal)) { + if (!DropEntryInternal(transaction, *existing_entry, info.GetQualifiedName().Name(), info.cascade, + info.allow_drop_internal)) { throw InternalException("Could not drop element because of an internal error"); } - - // remove the foreign key constraint in main key table if main key table's name is valid - for (idx_t i = 0; i < fk_arrays.size(); i++) { - // alter primary key table - Alter(transaction, *fk_arrays[i]); - } } void DuckSchemaEntry::OnDropEntry(CatalogTransaction transaction, CatalogEntry &entry) { diff --git a/src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp b/src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp index e3bd405bb..54a5d11d5 100644 --- a/src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp @@ -6,9 +6,11 @@ #include "duckdb/common/exception/transaction_exception.hpp" #include "duckdb/common/index_map.hpp" #include "duckdb/common/type_visitor.hpp" +#include "duckdb/common/unordered_set.hpp" #include "duckdb/execution/index/art/art.hpp" #include "duckdb/function/table/table_scan.hpp" #include "duckdb/main/database.hpp" +#include "duckdb/main/database_manager.hpp" #include "duckdb/parser/constraints/list.hpp" #include "duckdb/parser/expression/constant_expression.hpp" #include "duckdb/parser/parsed_data/comment_on_column_info.hpp" @@ -77,15 +79,6 @@ static void CheckTypeIsSupported(const LogicalType &logical_type, AttachedDataba "(database %s is using storage version %s)", required, db.GetName(), current); } - // an unnamed STRUCT is serialized identically to a TUPLE, so it must pass the same gate - if (storage_version < StorageVersion::V2_0_0 && StructType::IsUnnamed(type)) { - auto required = GetStorageVersionName(StorageVersion::V2_0_0, false); - auto current = GetStorageVersionName(storage_version, false); - - throw InvalidInputException("TUPLE columns are not supported in storage versions prior to %s " - "(database %s is using storage version %s)", - required, db.GetName(), current); - } } break; case LogicalTypeId::TUPLE: { // TUPLEs are stored as unnamed STRUCTs on disk, which older engines reject - gate them to v2.0.0+ @@ -178,6 +171,8 @@ DuckTableEntry::DuckTableEntry(Catalog &catalog, SchemaCatalogEntry &schema, Bou if (constraint->type == ConstraintType::UNIQUE) { // UNIQUE constraint: Create a unique index. auto &unique = constraint->Cast(); + auto index_oid = DatabaseManager::Get(catalog.GetDatabase()).NextOid(); + constraint->SetBackingIndexOid(index_oid); IndexConstraintType constraint_type = IndexConstraintType::UNIQUE; if (unique.is_primary_key) { constraint_type = IndexConstraintType::PRIMARY; @@ -186,7 +181,8 @@ DuckTableEntry::DuckTableEntry(Catalog &catalog, SchemaCatalogEntry &schema, Bou auto column_indexes = unique.GetLogicalIndexes(columns); if (info.indexes.empty()) { auto index_info = GetIndexInfo(constraint_type, false, info.base, i); - storage->AddIndex(columns, column_indexes, constraint_type, std::move(index_info), unique.check_mode); + storage->AddIndex(columns, column_indexes, constraint_type, std::move(index_info), index_oid, + unique.check_mode); continue; } @@ -197,7 +193,7 @@ DuckTableEntry::DuckTableEntry(Catalog &catalog, SchemaCatalogEntry &schema, Bou index_storage_info.name = name_info.name; } - storage->AddIndex(columns, column_indexes, constraint_type, std::move(index_storage_info), + storage->AddIndex(columns, column_indexes, constraint_type, std::move(index_storage_info), index_oid, unique.check_mode); continue; } @@ -207,6 +203,8 @@ DuckTableEntry::DuckTableEntry(Catalog &catalog, SchemaCatalogEntry &schema, Bou auto &bfk = constraint->Cast(); if (bfk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE || bfk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE) { + auto index_oid = DatabaseManager::Get(catalog.GetDatabase()).NextOid(); + constraint->SetBackingIndexOid(index_oid); vector column_indexes; for (const auto &physical_index : bfk.info.fk_keys) { auto &col = columns.GetColumn(physical_index); @@ -216,7 +214,7 @@ DuckTableEntry::DuckTableEntry(Catalog &catalog, SchemaCatalogEntry &schema, Bou if (info.indexes.empty()) { auto constraint_type = IndexConstraintType::FOREIGN; auto index_info = GetIndexInfo(constraint_type, false, info.base, i); - storage->AddIndex(columns, column_indexes, constraint_type, std::move(index_info), + storage->AddIndex(columns, column_indexes, constraint_type, std::move(index_info), index_oid, ConstraintCheckMode::DEFAULT); continue; } @@ -229,7 +227,7 @@ DuckTableEntry::DuckTableEntry(Catalog &catalog, SchemaCatalogEntry &schema, Bou } storage->AddIndex(columns, column_indexes, IndexConstraintType::FOREIGN, std::move(index_storage_info), - ConstraintCheckMode::DEFAULT); + index_oid, ConstraintCheckMode::DEFAULT); } } } @@ -286,6 +284,13 @@ unique_ptr DuckTableEntry::GetSample() { return storage->GetSample(); } +//! The index of the column name in a column reference of a table expression - the names before it qualify the table, +//! the names after it are struct fields +static idx_t ColumnNameIndex(const ColumnRefExpression &colref, const Identifier &table_name) { + auto &names = colref.ColumnNames(); + return names.size() > 1 && names[0] == table_name ? 1 : 0; +} + unique_ptr DuckTableEntry::AlterEntry(CatalogTransaction transaction, AlterInfo &info) { if (transaction.HasContext()) { return AlterEntry(transaction.GetContext(), info); @@ -334,6 +339,19 @@ unique_ptr DuckTableEntry::AlterEntry(ClientContext &context, Alte auto &rename_info = table_info.Cast(); auto copied_table = Copy(context); copied_table->name = rename_info.new_table_name; + // CHECK constraints can qualify columns with the table name + for (auto &constraint : copied_table->Cast().constraints) { + if (constraint->type != ConstraintType::CHECK) { + continue; + } + auto &check = constraint->Cast(); + ParsedExpressionIterator::VisitExpressionMutable( + *check.expression, [&](ColumnRefExpression &colref) { + if (ColumnNameIndex(colref, name) == 1) { + colref.ColumnNamesMutable()[0] = rename_info.new_table_name; + } + }); + } return copied_table; } case AlterTableType::ADD_COLUMN: { @@ -408,10 +426,11 @@ void DuckTableEntry::UndoAlter(ClientContext &context, AlterInfo &info) { } } -static void RenameExpression(ParsedExpression &root_expr, RenameColumnInfo &info) { +static void RenameExpression(ParsedExpression &root_expr, RenameColumnInfo &info, const Identifier &table_name) { ParsedExpressionIterator::VisitExpressionMutable(root_expr, [&](ColumnRefExpression &colref) { - if (colref.ColumnNames().back() == info.old_name) { - colref.ColumnNamesMutable().back() = info.new_name; + auto column_idx = ColumnNameIndex(colref, table_name); + if (colref.ColumnNames()[column_idx] == info.old_name) { + colref.ColumnNamesMutable()[column_idx] = info.new_name; } }); } @@ -454,7 +473,7 @@ unique_ptr DuckTableEntry::RenameColumn(ClientContext &context, Re copy.SetName(info.new_name); } if (col.Generated() && column_dependency_manager.IsDependencyOf(col.Logical(), rename_idx)) { - RenameExpression(copy.GeneratedExpressionMutable(), info); + RenameExpression(copy.GeneratedExpressionMutable(), info, name); } create_info->columns.AddColumn(std::move(copy)); } @@ -467,7 +486,7 @@ unique_ptr DuckTableEntry::RenameColumn(ClientContext &context, Re case ConstraintType::CHECK: { // CHECK constraint: need to rename column references that refer to the renamed column auto &check = copy->Cast(); - RenameExpression(*check.expression, info); + RenameExpression(*check.expression, info, name); break; } case ConstraintType::UNIQUE: { @@ -631,24 +650,6 @@ static child_list_t GetChildList(const LogicalType &type) { return child_types; } -static LogicalType ConstructNewType(const LogicalType &original_type, child_list_t new_child_types) { - switch (original_type.id()) { - case LogicalTypeId::STRUCT: { - return LogicalType::STRUCT(std::move(new_child_types)); - } - case LogicalTypeId::LIST: { - D_ASSERT(new_child_types.size() == 1); - return LogicalType::LIST(new_child_types[0].second); - } - case LogicalTypeId::MAP: { - D_ASSERT(new_child_types.size() == 2); - return LogicalType::MAP(new_child_types[0].second, new_child_types[1].second); - } - default: - throw BinderException("Type '%s' not supported for ADD COLUMN", original_type.ToString()); - } -} - Value ConstructMapping(const Identifier &name, const LogicalType &type) { if (!type.IsNested()) { return Value(name); @@ -732,7 +733,7 @@ StructMappingInfo AddFieldToStruct(const LogicalType &type, const vector DuckTableEntry::AddField(ClientContext &context, AddFie auto function = make_uniq("remap_struct", std::move(children)); - ChangeColumnTypeInfo change_column_type(info.GetAlterEntryData(), info.column_path[0], std::move(res.new_type), - std::move(function)); + ChangeColumnTypeInfo change_column_type(info.GetAlterEntryData(), vector {info.column_path[0]}, + std::move(res.new_type), std::move(function)); return ChangeColumnType(context, change_column_type, AlterTableType::ADD_FIELD); } @@ -883,12 +884,9 @@ unique_ptr DuckTableEntry::RemoveColumn(ClientContext &context, Re if (!removed_columns.empty() && !info.cascade) { throw CatalogException("Cannot drop column: column is a dependency of 1 or more generated column(s)"); } - bool dropped_column_is_generated = false; + bool dropped_column_is_generated = columns.GetColumn(LogicalIndex(removed_index)).Generated(); for (auto &col : columns.Logical()) { if (col.Logical() == removed_index || removed_columns.count(col.Logical())) { - if (col.Generated()) { - dropped_column_is_generated = true; - } continue; } create_info->columns.AddColumn(col.Copy()); @@ -896,7 +894,10 @@ unique_ptr DuckTableEntry::RemoveColumn(ClientContext &context, Re if (create_info->columns.empty()) { throw CatalogException("Cannot drop column: table only has one column remaining!"); } - auto adjusted_indices = column_dependency_manager.RemoveColumn(removed_index, columns.LogicalColumnCount()); + // operate on a copy: this is the live catalog entry, and mutating it here races with concurrent ALTERs and leaves + // the dependency map corrupted if a later step throws and the ALTER is rolled back + auto column_dependencies = column_dependency_manager.Copy(); + auto adjusted_indices = column_dependencies.RemoveColumn(removed_index, columns.LogicalColumnCount()); auto binder = Binder::CreateBinder(context); auto bound_constraints = binder->BindConstraints(constraints, name, columns); @@ -980,7 +981,7 @@ DroppedFieldMapping DropFieldFromStruct(const LogicalType &type, const vector DuckTableEntry::RemoveField(ClientContext &context, Rem auto function = make_uniq("remap_struct", std::move(children)); - ChangeColumnTypeInfo change_column_type(info.GetAlterEntryData(), info.column_path[0], std::move(res.new_type), - std::move(function)); + ChangeColumnTypeInfo change_column_type(info.GetAlterEntryData(), vector {info.column_path[0]}, + std::move(res.new_type), std::move(function)); return ChangeColumnType(context, change_column_type, AlterTableType::REMOVE_FIELD); } @@ -1080,7 +1081,7 @@ DroppedFieldMapping RenameFieldFromStruct(const LogicalType &type, const vector< result.error = ErrorData(CatalogException("Cannot rename field \"%s\" - it does not exist", rename_entry)); } else { result.mapping = Value::STRUCT(std::move(child_mapping)); - result.new_type = ConstructNewType(type, std::move(new_type_children)); + result.new_type = LogicalType::ConstructNestedType(type, std::move(new_type_children)); } return result; } @@ -1105,13 +1106,16 @@ unique_ptr DuckTableEntry::RenameField(ClientContext &context, Ren children.push_back(ConstantExpression::Null()); auto function = make_uniq("remap_struct", std::move(children)); - ChangeColumnTypeInfo change_column_type(info.GetAlterEntryData(), info.column_path[0], std::move(res.new_type), - std::move(function)); + ChangeColumnTypeInfo change_column_type(info.GetAlterEntryData(), vector {info.column_path[0]}, + std::move(res.new_type), std::move(function)); return ChangeColumnType(context, change_column_type, AlterTableType::RENAME_FIELD); } unique_ptr DuckTableEntry::SetDefault(ClientContext &context, SetDefaultInfo &info) { - auto default_idx = GetColumnIndex(info.column_name); + if (info.column_path.size() > 1) { + throw NotImplementedException("Setting a default value on a nested field is not yet supported"); + } + auto default_idx = GetColumnIndex(info.column_path[0]); if (default_idx.index == COLUMN_IDENTIFIER_ROW_ID) { throw CatalogException("Cannot SET DEFAULT for rowid column"); } @@ -1119,7 +1123,7 @@ unique_ptr DuckTableEntry::SetDefault(ClientContext &context, SetD auto create_info = GetInfo(); auto &table_info = create_info->Cast(); - // Modify the column that was specified by 'column_name' + // Modify the column that was specified by 'column_path' auto &col = table_info.columns.GetColumnMutable(default_idx); if (col.Generated()) { throw BinderException("Cannot SET DEFAULT for generated column %s", col.Name()); @@ -1133,7 +1137,10 @@ unique_ptr DuckTableEntry::SetDefault(ClientContext &context, SetD } unique_ptr DuckTableEntry::SetNotNull(ClientContext &context, SetNotNullInfo &info) { - auto not_null_idx = GetColumnIndex(info.column_name); + if (info.column_path.size() > 1) { + throw NotImplementedException("Setting a NOT NULL constraint on a nested field is not yet supported"); + } + auto not_null_idx = GetColumnIndex(info.column_path[0]); if (columns.GetColumn(LogicalIndex(not_null_idx)).Generated()) { throw BinderException("Unsupported constraint for generated column!"); } @@ -1141,16 +1148,7 @@ unique_ptr DuckTableEntry::SetNotNull(ClientContext &context, SetN auto create_info = GetInfo(); auto &table_info = create_info->Cast(); - bool has_not_null = false; - for (auto &constraint : table_info.constraints) { - if (constraint->type == ConstraintType::NOT_NULL) { - auto ¬_null = constraint->Cast(); - if (not_null.index == not_null_idx) { - has_not_null = true; - break; - } - } - } + bool has_not_null = table_info.FindNotNullConstraint(not_null_idx).IsValid(); if (!has_not_null) { table_info.constraints.push_back(make_uniq(not_null_idx)); } @@ -1172,12 +1170,15 @@ unique_ptr DuckTableEntry::SetNotNull(ClientContext &context, SetN } unique_ptr DuckTableEntry::DropNotNull(ClientContext &context, DropNotNullInfo &info) { - auto not_null_idx = GetColumnIndex(info.column_name); + if (info.column_path.size() > 1) { + throw NotImplementedException("Dropping a NOT NULL constraint on a nested field is not yet supported"); + } + auto not_null_idx = GetColumnIndex(info.column_path[0]); if (const auto pk = GetPrimaryKey()) { auto &unique = pk->Cast(); for (const auto &pk_index : unique.GetLogicalIndexes(columns)) { if (pk_index == not_null_idx) { - throw CatalogException("column %s is in a primary key", info.column_name); + throw CatalogException("column %s is in a primary key", info.column_path[0]); } } } @@ -1186,15 +1187,9 @@ unique_ptr DuckTableEntry::DropNotNull(ClientContext &context, Dro auto &table_info = create_info->Cast(); // Remove the NOT NULL constraint for the specified column - for (idx_t i = 0; i < table_info.constraints.size(); i++) { - auto &constraint = table_info.constraints[i]; - if (constraint->type == ConstraintType::NOT_NULL) { - auto ¬_null = constraint->Cast(); - if (not_null.index == not_null_idx) { - table_info.constraints.erase(table_info.constraints.begin() + static_cast(i)); - break; - } - } + auto not_null_constraint = table_info.FindNotNullConstraint(not_null_idx); + if (not_null_constraint.IsValid()) { + table_info.constraints.erase_at(not_null_constraint.GetIndex()); } auto binder = Binder::CreateBinder(context); @@ -1205,12 +1200,17 @@ unique_ptr DuckTableEntry::DropNotNull(ClientContext &context, Dro unique_ptr DuckTableEntry::ChangeColumnType(ClientContext &context, ChangeColumnTypeInfo &info, AlterTableType alter_table_type) { + // Only reject a user issued ALTER TYPE ADD/REMOVE/RENAME_FIELD's internal calls use a single element + // column_path (the parent struct column), which never exceeds this size check. + if (alter_table_type == AlterTableType::ALTER_COLUMN_TYPE && info.column_path.size() > 1) { + throw NotImplementedException("Changing the type of a nested field is not yet supported"); + } // Bind type auto type_binder = Binder::CreateBinder(context); type_binder->SetSearchPath(catalog, schema.name); type_binder->BindLogicalType(info.target_type); - auto change_idx = GetColumnIndex(info.column_name); + auto change_idx = GetColumnIndex(info.column_path[0]); auto create_info = make_uniq(schema, name); create_info->temporary = temporary; create_info->comment = comment; @@ -1395,35 +1395,32 @@ void DuckTableEntry::Rollback(CatalogEntry &prev_entry) { } // Rolls back any physical index creation for index-based constraints. - - auto &table = Cast(); auto &prev_table = prev_entry.Cast(); - auto &prev_info = prev_table.GetStorage().GetDataTableInfo(); - auto &prev_indexes = prev_info->GetIndexes(); - - // Find all index-based constraints that exist in rollback_table, but not in table. - // Then, remove them. + auto &prev_indexes = prev_table.GetStorage().GetDataTableInfo()->GetIndexes(); + for (auto oid : GetAddedUniqueIndexOids(prev_table)) { + prev_indexes.RemoveIndex(oid); + } +} - identifier_set_t names; +vector DuckTableEntry::GetAddedUniqueIndexOids(const DuckTableEntry &prev_table) const { + unordered_set prev_oids; for (const auto &constraint : prev_table.GetConstraints()) { - if (constraint->type != ConstraintType::UNIQUE) { - continue; + if (constraint->type == ConstraintType::UNIQUE) { + prev_oids.insert(constraint->GetBackingIndexOid()); } - const auto &unique = constraint->Cast(); - auto index_name = unique.GetName(prev_table.name); - names.insert(index_name); } + vector result; for (const auto &constraint : GetConstraints()) { if (constraint->type != ConstraintType::UNIQUE) { continue; } - const auto &unique = constraint->Cast(); - auto index_name = unique.GetName(table.name); - if (names.find(index_name) == names.end()) { - prev_indexes.RemoveIndex(index_name); + auto oid = constraint->GetBackingIndexOid(); + if (prev_oids.find(oid) == prev_oids.end()) { + result.push_back(oid); } } + return result; } void DuckTableEntry::OnDrop() { @@ -1435,13 +1432,25 @@ unique_ptr DuckTableEntry::AddConstraint(ClientContext &context, A auto &table_info = create_info->Cast(); if (info.constraint->type == ConstraintType::UNIQUE) { - const auto &unique = info.constraint->Cast(); + auto &unique = info.constraint->Cast(); const auto existing_pk = GetPrimaryKey(); if (unique.is_primary_key && existing_pk) { auto existing_name = existing_pk->ToString(); throw CatalogException("table %s can have only one primary key: %s", name, existing_name); } + + // Constraint index names are not unique, detect a duplicate constraint by its kind and columns. + for (const auto &constraint : GetConstraints()) { + if (constraint->type != ConstraintType::UNIQUE) { + continue; + } + auto &existing = constraint->Cast(); + if (existing.is_primary_key == unique.is_primary_key && + existing.GetLogicalIndexes(columns) == unique.GetLogicalIndexes(columns)) { + throw CatalogException("table %s already has the constraint %s", name, existing.ToString()); + } + } table_info.constraints.push_back(info.constraint->Copy()); } else { @@ -1478,12 +1487,9 @@ void DuckTableEntry::SetAsRoot() { void DuckTableEntry::CommitAlter(string &column_name, CommitDropState &drop_state) { D_ASSERT(!column_name.empty()); optional_idx logical_column_idx; - auto column_path = StringUtil::Split(column_name, '.'); - D_ASSERT(!column_path.empty()); - auto &root_column_name = column_path[0]; idx_t column_position = 0; for (auto &col : columns.Logical()) { - if (col.Name() == root_column_name) { + if (col.Name() == column_name) { // No need to alter storage, removed column is generated column if (col.Generated()) { return; diff --git a/src/duckdb/src/catalog/catalog_entry/index_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/index_catalog_entry.cpp index dc5489fc6..907702f8e 100644 --- a/src/duckdb/src/catalog/catalog_entry/index_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/index_catalog_entry.cpp @@ -11,6 +11,7 @@ IndexCatalogEntry::IndexCatalogEntry(Catalog &catalog, SchemaCatalogEntry &schem this->temporary = info.temporary; this->dependencies = info.dependencies; this->comment = info.comment; + this->tags = info.tags; for (auto &expr : expressions) { D_ASSERT(expr); expressions.push_back(expr->Copy()); diff --git a/src/duckdb/src/catalog/catalog_entry/macro_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/macro_catalog_entry.cpp index 16434d251..6deaabcd1 100644 --- a/src/duckdb/src/catalog/catalog_entry/macro_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/macro_catalog_entry.cpp @@ -18,6 +18,14 @@ MacroCatalogEntry::MacroCatalogEntry(Catalog &catalog, SchemaCatalogEntry &schem this->tags = info.tags; } +unique_ptr MacroCatalogEntry::Create(Catalog &catalog, SchemaCatalogEntry &schema, + CreateMacroInfo &info) { + if (info.type == CatalogType::TABLE_MACRO_ENTRY) { + return make_uniq(catalog, schema, info); + } + return make_uniq(catalog, schema, info); +} + ScalarMacroCatalogEntry::ScalarMacroCatalogEntry(Catalog &catalog, SchemaCatalogEntry &schema, CreateMacroInfo &info) : MacroCatalogEntry(catalog, schema, info) { } diff --git a/src/duckdb/src/catalog/catalog_entry/scalar_function_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/scalar_function_catalog_entry.cpp index dd89850cb..e51e22270 100644 --- a/src/duckdb/src/catalog/catalog_entry/scalar_function_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/scalar_function_catalog_entry.cpp @@ -20,9 +20,7 @@ ScalarFunctionCatalogEntry::ScalarFunctionCatalogEntry(Catalog &catalog, SchemaC shared_ptr ScalarFunctionCatalogEntry::FinalizeFunction(ScalarFunction function) const { auto result = make_shared_ptr(std::move(function)); - result->SetName(name); - result->SetCatalogName(catalog.GetAttached().GetName()); - result->SetSchemaName(schema.name); + result->SetQualifiedName(schema.GetQualifiedName(name)); return result; } diff --git a/src/duckdb/src/catalog/catalog_entry/schema_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/schema_catalog_entry.cpp index 606ef6642..77581c8f2 100644 --- a/src/duckdb/src/catalog/catalog_entry/schema_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/schema_catalog_entry.cpp @@ -10,8 +10,9 @@ namespace duckdb { -SchemaCatalogEntry::SchemaCatalogEntry(Catalog &catalog, CreateSchemaInfo &info) - : InCatalogEntry(CatalogType::SCHEMA_ENTRY, catalog, info.SchemaName()) { +SchemaCatalogEntry::SchemaCatalogEntry(Catalog &catalog, CreateSchemaInfo &info, + optional_ptr parent_schema_p) + : InCatalogEntry(CatalogType::SCHEMA_ENTRY, catalog, info.SchemaName()), parent_schema(parent_schema_p) { this->internal = info.internal; this->comment = info.comment; this->tags = info.tags; @@ -29,7 +30,7 @@ optional_ptr SchemaCatalogEntry::CreateIndex(ClientContext &contex SimilarCatalogEntry SchemaCatalogEntry::GetSimilarEntry(CatalogTransaction transaction, const EntryLookupInfo &lookup_info) { SimilarCatalogEntry result; - Scan(transaction.GetContext(), lookup_info.GetCatalogType(), [&](CatalogEntry &entry) { + Scan(transaction, lookup_info.GetCatalogType(), [&](CatalogEntry &entry) { auto entry_score = StringUtil::SimilarityRating(entry.name.GetIdentifierName(), lookup_info.GetEntryName()); if (entry_score > result.score) { result.score = entry_score; @@ -71,14 +72,55 @@ vector SchemaCatalogEntry::GetSchemaPath() const { } QualifiedName SchemaCatalogEntry::GetQualifiedName(const Identifier &entry_name) const { - auto path = GetSchemaPath(); - path.insert(path.begin(), catalog.GetName()); - return QualifiedName(std::move(path), entry_name); + return QualifiedName::FromCatalogSchema(catalog.GetName(), GetSchemaPath(), entry_name); +} + +string SchemaCatalogEntry::GetSchemaName() const { + return QualifiedName::FromPath(GetSchemaPath()).ToString(); +} + +void SchemaCatalogEntry::Scan(CatalogTransaction transaction, CatalogType type, + const std::function &callback) { + Scan(transaction.GetContext(), type, callback); +} + +template +static void ScanSchemaTreeInternal(SchemaCatalogEntry &root, SCAN scan, + const std::function &callback) { + vector> pending; + pending.emplace_back(root); + while (!pending.empty()) { + auto &schema = pending.back().get(); + pending.pop_back(); + callback(schema); + auto child_start = pending.size(); + scan(schema, [&](CatalogEntry &entry) { pending.emplace_back(entry.Cast()); }); + std::reverse(pending.begin() + NumericCast(child_start), pending.end()); + } +} + +void SchemaCatalogEntry::ScanSchemaTree(CatalogTransaction transaction, + const std::function &callback) { + ScanSchemaTreeInternal( + *this, + [&](SchemaCatalogEntry &schema, const std::function &visit) { + schema.Scan(transaction, CatalogType::SCHEMA_ENTRY, visit); + }, + callback); +} + +void SchemaCatalogEntry::ScanSchemaTree(const std::function &callback) { + ScanSchemaTreeInternal( + *this, + [](SchemaCatalogEntry &schema, const std::function &visit) { + schema.Scan(CatalogType::SCHEMA_ENTRY, visit); + }, + callback); } unique_ptr SchemaCatalogEntry::GetInfo() const { auto result = make_uniq(); - result->SetQualifiedName(QualifiedName({name}, Identifier())); + result->SetQualifiedName(GetParentSchema() ? GetQualifiedName(Identifier()) : QualifiedName({name}, Identifier())); result->comment = comment; result->tags = tags; return std::move(result); diff --git a/src/duckdb/src/catalog/catalog_entry/sequence_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/sequence_catalog_entry.cpp index acbd079b0..95c870b57 100644 --- a/src/duckdb/src/catalog/catalog_entry/sequence_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/sequence_catalog_entry.cpp @@ -6,6 +6,7 @@ #include "duckdb/parser/parsed_data/create_sequence_info.hpp" #include "duckdb/catalog/dependency_manager.hpp" #include "duckdb/common/operator/add.hpp" +#include "duckdb/common/operator/subtract.hpp" #include "duckdb/transaction/duck_transaction.hpp" #include @@ -56,14 +57,15 @@ int64_t SequenceCatalogEntry::NextValue(DuckTransaction &transaction) { lock_guard seqlock(lock); int64_t result; result = data.counter; - bool overflow = !TryAddOperator::Operation(data.counter, data.increment, data.counter); + int64_t next_counter; + bool overflow = !TryAddOperator::Operation(data.counter, data.increment, next_counter); if (data.cycle) { if (overflow) { - data.counter = data.increment < 0 ? data.max_value : data.min_value; - } else if (data.counter < data.min_value) { - data.counter = data.max_value; - } else if (data.counter > data.max_value) { - data.counter = data.min_value; + next_counter = data.increment < 0 ? data.max_value : data.min_value; + } else if (next_counter < data.min_value) { + next_counter = data.max_value; + } else if (next_counter > data.max_value) { + next_counter = data.min_value; } } else { if (result < data.min_value || (overflow && data.increment < 0)) { @@ -73,6 +75,7 @@ int64_t SequenceCatalogEntry::NextValue(DuckTransaction &transaction) { throw SequenceException("nextval: reached maximum value of sequence \"%s\" (%lld)", name, data.max_value); } } + data.counter = next_counter; data.last_value = result; data.usage_count++; if (!temporary) { @@ -111,6 +114,22 @@ void SequenceCatalogEntry::ReplayValue(uint64_t v_usage_count, int64_t v_counter } } +void SequenceCatalogEntry::DeriveLastValue() { + lock_guard seqlock(lock); + if (data.last_value || data.usage_count == 0) { + return; + } + int64_t last_value; + if (!TrySubtractOperator::Operation(data.counter, data.increment, last_value)) { + return; + } + // a counter that wrapped around in a cycle does not tell us the last value + if (last_value < data.min_value || last_value > data.max_value) { + return; + } + data.last_value = last_value; +} + unique_ptr SequenceCatalogEntry::GetInfo() const { auto seq_data = GetData(); diff --git a/src/duckdb/src/catalog/catalog_entry/table_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/table_catalog_entry.cpp index ee58c9f46..de51aa530 100644 --- a/src/duckdb/src/catalog/catalog_entry/table_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/table_catalog_entry.cpp @@ -5,7 +5,7 @@ #include "duckdb/common/algorithm.hpp" #include "duckdb/common/exception.hpp" #include "duckdb/common/enum_util.hpp" -#include "duckdb/common/extra_type_info.hpp" +#include "duckdb/common/logical_type_info.hpp" #include "duckdb/main/database.hpp" #include "duckdb/main/settings.hpp" #include "duckdb/parser/constraints/list.hpp" @@ -243,7 +243,8 @@ DataTable &TableCatalogEntry::GetStorage() { // LCOV_EXCL_STOP void LogicalUpdate::BindExtraColumns(TableCatalogEntry &table, LogicalGet &get, LogicalProjection &proj, - LogicalUpdate &update, physical_index_set_t &bound_columns) { + LogicalUpdate &update, physical_index_set_t &bound_columns, + bool reuse_projected_columns) { if (bound_columns.size() <= 1) { return; } @@ -266,17 +267,30 @@ void LogicalUpdate::BindExtraColumns(TableCatalogEntry &table, LogicalGet &get, } // column is not projected yet: project it by adding the clause "i=i" to the set of updated columns auto &column = table.GetColumns().GetColumn(physical_id); - auto proj_ref = make_uniq( - column.Type(), ColumnBinding(get.table_index, ProjectionIndex(get.GetColumnIds().size()))); + auto column_id = column.Logical().index; + auto get_index = reuse_projected_columns ? get.TryGetProjectionIndex(column_id) : ProjectionIndex(); + if (!get_index.IsValid()) { + get_index = get.AddColumnId(column_id); + } + auto proj_ref = + make_uniq(column.Type(), ColumnBinding(get.table_index, get_index)); auto proj_index = ColumnBinding::PushExpression(proj.expressions, std::move(proj_ref)); update.expressions.push_back( make_uniq(column.Type(), ColumnBinding(proj.table_index, proj_index))); - get.AddColumnId(column.Logical().index); update.columns.push_back(physical_id); } } } +void LogicalUpdate::BindAllColumns(TableCatalogEntry &table, LogicalGet &get, LogicalProjection &proj, + LogicalUpdate &update, bool reuse_projected_columns) { + physical_index_set_t all_columns; + for (auto &column : table.GetColumns().Physical()) { + all_columns.insert(column.Physical()); + } + BindExtraColumns(table, get, proj, update, all_columns, reuse_projected_columns); +} + vector TableCatalogEntry::GetColumnSegmentInfo(const QueryContext &context, const ColumnSegmentInfoScanOptions &options) { return {}; @@ -306,11 +320,7 @@ void TableCatalogEntry::BindUpdateConstraints(Binder &binder, LogicalGet &get, L } } if (update.return_chunk) { - physical_index_set_t all_columns; - for (auto &column : GetColumns().Physical()) { - all_columns.insert(column.Physical()); - } - LogicalUpdate::BindExtraColumns(*this, get, proj, update, all_columns); + LogicalUpdate::BindAllColumns(*this, get, proj, update); } // for index updates we always turn any update into an insert and a delete // we thus need all the columns to be available, hence we check if the update touches any index columns @@ -339,11 +349,7 @@ void TableCatalogEntry::BindUpdateConstraints(Binder &binder, LogicalGet &get, L if (update.update_is_del_and_insert) { // the update updates a column required by an index or requires returning the updated rows, // push projections for all columns - physical_index_set_t all_columns; - for (auto &column : GetColumns().Physical()) { - all_columns.insert(column.Physical()); - } - LogicalUpdate::BindExtraColumns(*this, get, proj, update, all_columns); + LogicalUpdate::BindAllColumns(*this, get, proj, update); } } @@ -405,7 +411,9 @@ optional_ptr TableCatalogEntry::GetTrigger(CatalogTransaction tran } bool TableCatalogEntry::DropTrigger(CatalogTransaction transaction, const Identifier &name, bool cascade) { - throw NotImplementedException("Triggers are not supported for this table type"); + // Default: no triggers, so there is nothing to drop. Returning false instead of throwing lets DROP TRIGGER IF + // EXISTS be a no-op. + return false; } vector> TableCatalogEntry::GetTriggersForEvent(CatalogTransaction transaction, diff --git a/src/duckdb/src/catalog/catalog_entry/table_function_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/table_function_catalog_entry.cpp index cceb88859..bce39a750 100644 --- a/src/duckdb/src/catalog/catalog_entry/table_function_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/table_function_catalog_entry.cpp @@ -13,10 +13,12 @@ TableFunctionCatalogEntry::TableFunctionCatalogEntry(Catalog &catalog, SchemaCat CreateTableFunctionInfo &info) : FunctionEntry(CatalogType::TABLE_FUNCTION_ENTRY, catalog, schema, info), functions(std::move(info.functions)) { D_ASSERT(this->functions.Size() > 0); - functions.ApplyToFunctions([&](TableFunction &function) { - function.SetCatalogName(catalog.GetAttached().GetName()); - function.SetSchemaName(schema.name); - }); + functions.name = name; + functions.ApplyToFunctions([&](TableFunction &function) { FinalizeFunction(function); }); +} + +void TableFunctionCatalogEntry::FinalizeFunction(TableFunction &function) const { + function.SetQualifiedName(schema.GetQualifiedName(name)); } unique_ptr TableFunctionCatalogEntry::AlterEntry(CatalogTransaction transaction, AlterInfo &info) { @@ -35,6 +37,10 @@ unique_ptr TableFunctionCatalogEntry::AlterEntry(CatalogTransactio throw BinderException("Failed to add new function overloads to function \"%s\": function already exists", name); } CreateTableFunctionInfo new_info(std::move(new_set)); + new_info.internal = internal; + new_info.descriptions = descriptions; + new_info.descriptions.insert(new_info.descriptions.end(), add_overloads.new_descriptions.begin(), + add_overloads.new_descriptions.end()); return make_uniq(catalog, schema, new_info); } diff --git a/src/duckdb/src/catalog/catalog_entry/view_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/view_catalog_entry.cpp index 22bd465bd..63b4209b8 100644 --- a/src/duckdb/src/catalog/catalog_entry/view_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/view_catalog_entry.cpp @@ -10,8 +10,6 @@ #include "duckdb/planner/binder.hpp" #include "duckdb/catalog/catalog.hpp" -#include - namespace duckdb { void ViewCatalogEntry::Initialize(CreateViewInfo &info) { @@ -73,28 +71,8 @@ unique_ptr ViewCatalogEntry::AlterEntry(ClientContext &context, Al // Column comments have a special alter type if (info.type == AlterType::SET_COLUMN_COMMENT) { auto &comment_on_column_info = info.Cast(); + auto resolved_column_name = ResolveColumnName(comment_on_column_info.column_name); auto copied_view = Copy(context); - - Identifier resolved_column_name = comment_on_column_info.column_name; - auto view_columns = GetColumnInfo(); - if (view_columns) { - // if the view is bound - verify the name we are commenting on exists - auto &names = view_columns->names; - auto match_name = [&](const auto &n) { - return resolved_column_name == n; - }; - auto entry = std::find_if(names.begin(), names.end(), match_name); - if (entry == names.end()) { - // the column name might be a view alias - check those as well - auto alias_entry = std::find_if(aliases.begin(), aliases.end(), match_name); - if (alias_entry == aliases.end()) { - throw BinderException("View %s does not have a column with name %s", name, resolved_column_name); - } - auto alias_index = NumericCast(std::distance(aliases.begin(), alias_entry)); - D_ASSERT(alias_index < names.size()); - resolved_column_name = names[alias_index]; - } - } // apply the comment to the view auto &copied_view_entry = copied_view->Cast(); copied_view_entry.column_comments[resolved_column_name] = comment_on_column_info.comment_value; @@ -150,6 +128,21 @@ Value ViewCatalogEntry::GetColumnComment(idx_t column_index) { return Value(); } +Identifier ViewCatalogEntry::ResolveColumnName(const Identifier &column_name) const { + auto view_columns = GetColumnInfo(); + if (!view_columns) { + return column_name; + } + auto &names = view_columns->names; + for (idx_t i = 0; i < names.size(); i++) { + auto &visible_name = i < aliases.size() ? aliases[i] : names[i]; + if (visible_name == column_name) { + return names[i]; + } + } + throw BinderException("View %s does not have a column with name %s", name, column_name); +} + void ViewCatalogEntry::BindView(ClientContext &context, BindViewAction action) { if (bind_state == ViewBindState::BINDING && bind_thread == ThreadUtil::GetThreadId()) { throw InvalidInputException("View \"%s\" was requested to be bound but this thread is already binding that " diff --git a/src/duckdb/src/catalog/catalog_entry/window_function_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/window_function_catalog_entry.cpp index 55ef448da..3a1ea819e 100644 --- a/src/duckdb/src/catalog/catalog_entry/window_function_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/window_function_catalog_entry.cpp @@ -10,10 +10,8 @@ namespace duckdb { WindowFunctionCatalogEntry::WindowFunctionCatalogEntry(Catalog &catalog, SchemaCatalogEntry &schema, CreateWindowFunctionInfo &info) : FunctionEntry(Type, catalog, schema, info), functions(info.functions) { - functions.ApplyToFunctions([&](WindowFunction &function) { - function.SetCatalogName(catalog.GetAttached().GetName()); - function.SetSchemaName(schema.name); - }); + functions.ApplyToFunctions( + [&](WindowFunction &function) { function.SetQualifiedName(schema.GetQualifiedName(name)); }); } } // namespace duckdb diff --git a/src/duckdb/src/catalog/catalog_entry_retriever.cpp b/src/duckdb/src/catalog/catalog_entry_retriever.cpp index f1ca9f1b4..ec5298ea3 100644 --- a/src/duckdb/src/catalog/catalog_entry_retriever.cpp +++ b/src/duckdb/src/catalog/catalog_entry_retriever.cpp @@ -116,7 +116,10 @@ void CatalogEntryRetriever::SetSearchPath(vector entries) { auto &set_paths = client_search_path.GetSetPaths(); for (auto path : set_paths) { if (IsInvalidCatalog(path.GetCatalog())) { - path.SetCatalog(DatabaseManager::GetDefaultDatabase(context)); + path.SetCatalog(DatabaseManager::TryGetDefaultDatabase(context)); + if (IsInvalidCatalog(path.GetCatalog())) { + continue; + } } new_path.push_back(std::move(path)); } diff --git a/src/duckdb/src/catalog/catalog_search_path.cpp b/src/duckdb/src/catalog/catalog_search_path.cpp index 33473bb15..3dfced92d 100644 --- a/src/duckdb/src/catalog/catalog_search_path.cpp +++ b/src/duckdb/src/catalog/catalog_search_path.cpp @@ -54,36 +54,24 @@ CatalogSearchEntry CatalogSearchEntry::ParseInternal(const string &input, idx_t string entry; bool finished = false; normal: - for (; idx < input.size(); idx++) { + for (; idx < input.size();) { if (input[idx] == '"') { - idx++; - goto quoted; + string quoted; + if (!StringUtil::TryParseQuotedString(input, idx, quoted)) { + throw ParserException("Unterminated quote in qualified name!"); + } + entry += quoted; } else if (input[idx] == '.') { goto separator; } else if (input[idx] == ',') { finished = true; goto separator; + } else { + entry += input[idx++]; } - entry += input[idx]; } finished = true; goto separator; -quoted: - //! look for another quote - for (; idx < input.size(); idx++) { - if (input[idx] == '"') { - //! unquote - idx++; - if (idx < input.size() && input[idx] == '"') { - // escaped quote - entry += input[idx]; - continue; - } - goto normal; - } - entry += input[idx]; - } - throw ParserException("Unterminated quote in qualified name!"); separator: if (entry.empty()) { throw ParserException("Unexpected dot - empty CatalogSearchEntry"); @@ -272,6 +260,17 @@ Identifier CatalogSearchPath::GetDefaultCatalog(const Identifier &schema) const return Identifier::InvalidCatalog(); } +Identifier CatalogSearchPath::ResolveCatalog(const Identifier &schema) const { + auto catalog = schema.empty() ? Identifier::InvalidCatalog() : GetDefaultCatalog(schema); + if (IsInvalidCatalog(catalog)) { + catalog = GetDefault().GetCatalog(); + } + if (IsInvalidCatalog(catalog)) { + catalog = DatabaseManager::GetDefaultDatabase(context); + } + return catalog; +} + vector CatalogSearchPath::GetCatalogsForSchema(const Identifier &schema) const { vector catalogs; if (DefaultSchemaGenerator::IsDefaultSchema(schema)) { @@ -364,7 +363,7 @@ bool CatalogSearchPath::SchemaInSearchPath(ClientContext &context, const Identif if (path.GetCatalog() == catalog_name) { return true; } - if (IsInvalidCatalog(path.GetCatalog()) && catalog_name == DatabaseManager::GetDefaultDatabase(context)) { + if (IsInvalidCatalog(path.GetCatalog()) && catalog_name == DatabaseManager::TryGetDefaultDatabase(context)) { return true; } } diff --git a/src/duckdb/src/catalog/catalog_set.cpp b/src/duckdb/src/catalog/catalog_set.cpp index a07c7285c..361df9dda 100644 --- a/src/duckdb/src/catalog/catalog_set.cpp +++ b/src/duckdb/src/catalog/catalog_set.cpp @@ -339,13 +339,12 @@ bool CatalogSet::AlterEntry(CatalogTransaction transaction, const Identifier &na // If this ALTER produced a new DuckTableEntry, refresh the LocalTableStorage's table_entry // pointer so that commit-time Flush pushes an AppendInfo referencing the current DuckTableEntry. if (transaction.context && value->type == CatalogType::TABLE_ENTRY) { - auto &tce = value->Cast(); - if (tce.IsDuckTable()) { - auto &new_entry = tce.Cast(); - auto &new_storage = new_entry.GetStorage(); + auto new_entry = value->Cast().TryGetDuckTableEntry(); + if (new_entry) { + auto &new_storage = new_entry->GetStorage(); auto lstorage = LocalStorage::Get(*transaction.context, new_storage.db).GetStorage(new_storage); if (lstorage) { - lstorage->table_entry = &new_entry; + lstorage->table_entry = new_entry; } } } diff --git a/src/duckdb/src/catalog/default/default_functions.cpp b/src/duckdb/src/catalog/default/default_functions.cpp index 7ff0c39b4..3b23a8632 100644 --- a/src/duckdb/src/catalog/default/default_functions.cpp +++ b/src/duckdb/src/catalog/default/default_functions.cpp @@ -1,6 +1,5 @@ #include "duckdb/catalog/default/default_functions.hpp" #include "duckdb/parser/parser.hpp" -#include "duckdb/parser/peg/compiled_grammar.hpp" #include "duckdb/parser/parsed_data/create_macro_info.hpp" #include "duckdb/parser/statement/create_statement.hpp" #include "duckdb/catalog/catalog_entry/scalar_macro_catalog_entry.hpp" @@ -228,16 +227,11 @@ static const DefaultMacro internal_macros[] = { {nullptr, nullptr, nullptr}}; unique_ptr DefaultFunctionGenerator::CreateInternalMacroInfo(const DefaultMacro &default_macro) { - return CreateInternalMacroInfo(default_macro, ParserOptions()); -} - -unique_ptr DefaultFunctionGenerator::CreateInternalMacroInfo(const DefaultMacro &default_macro, - const ParserOptions &options) { + auto parser = Parser::GetBuiltinParser(); auto bind_info = make_uniq(CatalogType::MACRO_ENTRY); // Build a full CREATE MACRO statement and let the parser handle parameters, types, and defaults. // macro_definition may contain multiple comma-separated overloads, e.g. "(x) AS x, (x, y) AS x+y". auto sql = StringUtil::Format("CREATE MACRO __dummy__%s", default_macro.macro_definition); - Parser parser(options); parser.ParseQuery(sql); D_ASSERT(parser.statements.size() == 1); D_ASSERT(parser.statements[0]->type == StatementType::CREATE_STATEMENT); @@ -265,13 +259,12 @@ static bool DefaultFunctionMatches(const DefaultMacro ¯o, const Identifier & return macro.schema == schema && macro.name == name; } -static unique_ptr GetDefaultFunction(const Identifier &input_schema, const Identifier &input_name, - const ParserOptions &options) { +static unique_ptr GetDefaultFunction(const Identifier &input_schema, const Identifier &input_name) { auto &schema = input_schema; auto &name = input_name; for (idx_t index = 0; internal_macros[index].name != nullptr; index++) { if (DefaultFunctionMatches(internal_macros[index], schema, name)) { - return DefaultFunctionGenerator::CreateInternalMacroInfo(internal_macros[index], options); + return DefaultFunctionGenerator::CreateInternalMacroInfo(internal_macros[index]); } } return nullptr; @@ -283,9 +276,7 @@ DefaultFunctionGenerator::DefaultFunctionGenerator(Catalog &catalog, SchemaCatal unique_ptr DefaultFunctionGenerator::CreateDefaultEntry(ClientContext &context, const Identifier &entry_name) { - ParserOptions options; - options.compiled_grammar = CompiledGrammar::Get(context); - auto info = GetDefaultFunction(schema.name, entry_name, options); + auto info = GetDefaultFunction(schema.name, entry_name); if (info) { return make_uniq_base(catalog, schema, info->Cast()); } diff --git a/src/duckdb/src/catalog/default/default_table_functions.cpp b/src/duckdb/src/catalog/default/default_table_functions.cpp index 3162d9081..3fbd68a41 100644 --- a/src/duckdb/src/catalog/default/default_table_functions.cpp +++ b/src/duckdb/src/catalog/default/default_table_functions.cpp @@ -2,9 +2,7 @@ #include "duckdb/parser/expression/columnref_expression.hpp" #include "duckdb/catalog/catalog_entry/table_macro_catalog_entry.hpp" #include "duckdb/parser/parser.hpp" -#include "duckdb/parser/peg/compiled_grammar.hpp" #include "duckdb/parser/parsed_data/create_macro_info.hpp" -#include "duckdb/parser/statement/select_statement.hpp" #include "duckdb/function/table_macro_function.hpp" #include "duckdb/main/client_context.hpp" #include "duckdb/main/database.hpp" @@ -77,7 +75,7 @@ WHERE type ILIKE log_type )"}, {DEFAULT_SCHEMA, "duckdb_profiling_settings", {}, {}, R"( SELECT * EXCLUDE(input_type, scope, aliases, typed_value) - FROM duckdb_settings(deprecated := true) + FROM duckdb_settings(deprecated := false) WHERE name IN ( 'enable_profiling', 'profiling_coverage', @@ -102,12 +100,9 @@ DefaultTableFunctionGenerator::CreateInternalTableMacroInfo(const DefaultTableMa } for (idx_t named_idx = 0; default_macro.named_parameters[named_idx].name != nullptr; named_idx++) { const auto &named_param = default_macro.named_parameters[named_idx]; - auto expr_list = Parser::ParseExpressionList(named_param.default_value); - if (expr_list.size() != 1) { - throw InternalException("Expected a single expression"); - } + auto default_value = Parser::GetBuiltinParser().ParseSingleExpression(named_param.default_value); function->parameters.push_back(make_uniq(named_param.name)); - function->default_parameters.insert(Identifier(named_param.name), std::move(expr_list[0])); + function->default_parameters.insert(Identifier(named_param.name), std::move(default_value)); } auto type = CatalogType::TABLE_MACRO_ENTRY; @@ -121,39 +116,34 @@ DefaultTableFunctionGenerator::CreateInternalTableMacroInfo(const DefaultTableMa unique_ptr DefaultTableFunctionGenerator::CreateTableMacroInfo(const DefaultTableMacro &default_macro) { - return CreateTableMacroInfo(default_macro, ParserOptions()); -} - -unique_ptr DefaultTableFunctionGenerator::CreateTableMacroInfo(const DefaultTableMacro &default_macro, - const ParserOptions &options) { - Parser parser(options); - parser.ParseQuery(default_macro.macro); - if (parser.statements.size() != 1 || parser.statements[0]->type != StatementType::SELECT_STATEMENT) { - throw InternalException("Expected a single select statement in CreateTableMacroInfo internal"); - } - auto node = std::move(parser.statements[0]->Cast().node); - - auto result = make_uniq(std::move(node)); + auto parser = Parser::GetBuiltinParser(); + auto result = make_uniq(parser.ParseSelectNode(default_macro.macro)); return CreateInternalTableMacroInfo(default_macro, std::move(result)); } -static unique_ptr -GetDefaultTableFunction(const Identifier &input_schema, const Identifier &input_name, const ParserOptions &options) { - for (idx_t index = 0; internal_table_macros[index].name != nullptr; index++) { - if (internal_table_macros[index].schema == input_schema && internal_table_macros[index].name == input_name) { - return DefaultTableFunctionGenerator::CreateTableMacroInfo(internal_table_macros[index], options); +optional_ptr +DefaultTableFunctionGenerator::FindTableMacro(const DefaultTableMacro macros[], const Identifier &name, + optional_ptr schema_name) { + for (idx_t index = 0; macros[index].name != nullptr; index++) { + if ((!schema_name || macros[index].schema == *schema_name) && macros[index].name == name) { + return macros[index]; } } return nullptr; } -unique_ptr DefaultTableFunctionGenerator::CreateDefaultEntry(ClientContext &context, +unique_ptr DefaultTableFunctionGenerator::CreateTableMacroEntry(Catalog &catalog, + SchemaCatalogEntry &schema, + const DefaultTableMacro &default_macro) { + auto info = CreateTableMacroInfo(default_macro); + return make_uniq_base(catalog, schema, *info); +} + +unique_ptr DefaultTableFunctionGenerator::CreateDefaultEntry(ClientContext &, const Identifier &entry_name) { - ParserOptions options; - options.compiled_grammar = CompiledGrammar::Get(context); - auto info = GetDefaultTableFunction(schema.name, entry_name, options); - if (info) { - return make_uniq_base(catalog, schema, info->Cast()); + auto macro = FindTableMacro(internal_table_macros, entry_name, schema.name); + if (macro) { + return CreateTableMacroEntry(catalog, schema, *macro); } return nullptr; } diff --git a/src/duckdb/src/catalog/default/default_types.cpp b/src/duckdb/src/catalog/default/default_types.cpp index 4cbfc0611..370633eb8 100644 --- a/src/duckdb/src/catalog/default/default_types.cpp +++ b/src/duckdb/src/catalog/default/default_types.cpp @@ -181,13 +181,7 @@ LogicalType BindEnumType(BindLogicalTypeInput &input) { Vector enum_vector(LogicalType::VARCHAR, NumericCast(arguments.size())); auto string_data = FlatVector::Writer(enum_vector, arguments.size()); - for (idx_t arg_idx = 0; arg_idx < arguments.size(); arg_idx++) { - auto &arg = arguments[arg_idx]; - if (arg.HasName()) { - throw BinderException(input.GetLocation(arg_idx), - "ENUM type arguments cannot have names (argument %d has name \"%s\")", arg_idx + 1, - arg.GetName()); - } + for (auto &arg : arguments) { string_data.WriteValue(string_t(StringValue::Get(arg.GetValue()))); } @@ -196,7 +190,7 @@ LogicalType BindEnumType(BindLogicalTypeInput &input) { void RegisterEnumConstructors(TypeConstructorSet &set) { auto signature = TypeConstructor::Signature(); - signature.SetVarArgs(LogicalType::VARCHAR); + signature.AddArgs("args", LogicalType::VARCHAR); set.AddFunction(TypeConstructor(std::move(signature), BindEnumType)); } @@ -243,24 +237,11 @@ void RegisterArrayConstructors(TypeConstructorSet &set) { LogicalType BindStructType(BindLogicalTypeInput &input) { auto &arguments = input.modifiers; - identifier_set_t name_collision_set; child_list_t children; children.reserve(arguments.size()); - for (idx_t arg_idx = 0; arg_idx < arguments.size(); arg_idx++) { - auto &arg = arguments[arg_idx]; - if (!arg.HasName()) { - throw BinderException(input.GetLocation(arg_idx), "STRUCT type arguments must have names"); - } - - auto name = Identifier(arg.GetName()); - if (name_collision_set.find(name) != name_collision_set.end()) { - throw BinderException(input.GetLocation(arg_idx), "Duplicate STRUCT type argument name \"%s\"", - arg.GetName()); - } - name_collision_set.insert(name); - - children.emplace_back(std::move(name), TypeValue::GetType(arg.GetValue())); + for (auto &arg : arguments) { + children.emplace_back(Identifier(arg.GetName()), TypeValue::GetType(arg.GetValue())); } return LogicalType::STRUCT(std::move(children)); @@ -268,7 +249,7 @@ LogicalType BindStructType(BindLogicalTypeInput &input) { void RegisterStructConstructors(TypeConstructorSet &set) { auto signature = TypeConstructor::Signature(); - signature.SetVarArgs(LogicalType::TYPE()); + signature.AddKwargs("kwargs", LogicalType::TYPE()); set.AddFunction(TypeConstructor(std::move(signature), BindStructType)); } @@ -278,12 +259,7 @@ void RegisterStructConstructors(TypeConstructorSet &set) { LogicalType BindTupleType(BindLogicalTypeInput &input) { vector children; children.reserve(input.modifiers.size()); - for (idx_t arg_idx = 0; arg_idx < input.modifiers.size(); arg_idx++) { - auto &arg = input.modifiers[arg_idx]; - if (arg.HasName()) { - throw BinderException(input.GetLocation(arg_idx), - "TUPLE type arguments cannot have names - use STRUCT for named fields"); - } + for (auto &arg : input.modifiers) { children.push_back(TypeValue::GetType(arg.GetValue())); } return LogicalType::TUPLE(std::move(children)); @@ -291,7 +267,7 @@ LogicalType BindTupleType(BindLogicalTypeInput &input) { void RegisterTupleConstructors(TypeConstructorSet &set) { auto signature = TypeConstructor::Signature(); - signature.SetVarArgs(LogicalType::TYPE()); + signature.AddArgs("args", LogicalType::TYPE()); set.AddFunction(TypeConstructor(std::move(signature), BindTupleType)); } @@ -326,21 +302,8 @@ LogicalType BindUnionType(BindLogicalTypeInput &input) { } child_list_t children; - identifier_set_t name_collision_set; - - for (idx_t arg_idx = 0; arg_idx < arguments.size(); arg_idx++) { - auto &arg = arguments[arg_idx]; - if (!arg.HasName()) { - throw BinderException(input.GetLocation(arg_idx), "UNION type modifiers must have names"); - } - - auto &entry_name = arg.GetName(); - if (name_collision_set.find(Identifier(entry_name)) != name_collision_set.end()) { - throw BinderException(input.GetLocation(arg_idx), "Duplicate UNION type member name \"%s\"", entry_name); - } - name_collision_set.insert(Identifier(entry_name)); - - children.emplace_back(entry_name, TypeValue::GetType(arg.GetValue())); + for (auto &arg : arguments) { + children.emplace_back(arg.GetName(), TypeValue::GetType(arg.GetValue())); } return LogicalType::UNION(std::move(children)); @@ -348,7 +311,7 @@ LogicalType BindUnionType(BindLogicalTypeInput &input) { void RegisterUnionConstructors(TypeConstructorSet &set) { auto signature = TypeConstructor::Signature(); - signature.SetVarArgs(LogicalType::TYPE()); + signature.AddKwargs("kwargs", LogicalType::TYPE()); set.AddFunction(TypeConstructor(std::move(signature), BindUnionType)); } @@ -356,7 +319,7 @@ void RegisterUnionConstructors(TypeConstructorSet &set) { // VARIANT Type //---------------------------------------------------------------------------------------------------------------------- LogicalType BindVariantType(BindLogicalTypeInput &input) { - // We need this function to make sure we always create a VARIANT type with ExtraTypeInfo + // We need this function to make sure we always create a VARIANT type with LogicalTypeInfo return LogicalType::VARIANT(); } diff --git a/src/duckdb/src/catalog/default/default_views.cpp b/src/duckdb/src/catalog/default/default_views.cpp index 252813621..68b9e033c 100644 --- a/src/duckdb/src/catalog/default/default_views.cpp +++ b/src/duckdb/src/catalog/default/default_views.cpp @@ -1,3 +1,4 @@ +#include "duckdb/parser/parser.hpp" #include "duckdb/catalog/default/default_views.hpp" #include "duckdb/planner/binder.hpp" #include "duckdb/catalog/catalog_entry/schema_catalog_entry.hpp" @@ -236,7 +237,8 @@ static unique_ptr GetDefaultView(ClientContext &context, const I result->temporary = true; result->internal = true; - return CreateViewInfo::FromSelect(context, std::move(result)); + auto parser = Parser::GetBuiltinParser(); + return CreateViewInfo::FromSelect(parser, std::move(result)); } } return nullptr; diff --git a/src/duckdb/src/catalog/dependency_manager.cpp b/src/duckdb/src/catalog/dependency_manager.cpp index f7eccea6b..858a8bfc5 100644 --- a/src/duckdb/src/catalog/dependency_manager.cpp +++ b/src/duckdb/src/catalog/dependency_manager.cpp @@ -83,13 +83,7 @@ vector DependencyManager::GetSchemaPath(const CatalogEntry &entry) { } else { schema = entry.ParentSchema(); } - vector path; - while (schema) { - path.push_back(schema->name); - schema = schema->GetParentSchema().get(); - } - std::reverse(path.begin(), path.end()); - return path; + return schema ? schema->GetSchemaPath() : vector(); } MangledEntryName DependencyManager::MangleName(const CatalogEntryInfo &info) { @@ -369,17 +363,10 @@ CatalogEntryInfo DependencyManager::GetLookupProperties(const CatalogEntry &entr optional_ptr DependencyManager::NavigateSchemaPath(CatalogTransaction transaction, const vector &schema_path) { - optional_ptr schema; - reference current = catalog.GetSchemaCatalogSet(); - for (auto &component : schema_path) { - auto entry = current.get().GetEntry(transaction, component); - if (!entry) { - return nullptr; - } - schema = entry->Cast(); - current = schema->Cast().GetCatalogSet(CatalogType::SCHEMA_ENTRY); + if (schema_path.empty()) { + return nullptr; } - return schema; + return catalog.GetSchema(transaction, schema_path, OnEntryNotFound::RETURN_NULL); } optional_ptr DependencyManager::LookupEntry(CatalogTransaction transaction, @@ -440,7 +427,7 @@ void DependencyManager::CleanupDependencies(CatalogTransaction transaction, Cata } } -static string EntryToString(CatalogEntryInfo &info) { +static string EntryToString(const CatalogEntryInfo &info) { auto type = info.type; switch (type) { case CatalogType::TABLE_ENTRY: { @@ -506,13 +493,33 @@ static string EntryToString(CatalogEntryInfo &info) { }; } +static string DependencyToString(const CatalogEntryInfo &subject, const CatalogEntryInfo &dependent) { + return StringUtil::Format("%s depends on %s.\n", EntryToString(dependent), EntryToString(subject)); +} + +static string DropErrorToString(const Identifier &name, const string &dependents) { + return StringUtil::Format("Cannot drop entry %s because there are entries that depend on it.\n%s" + "Use DROP...CASCADE to drop all dependents.", + name, dependents); +} + +string DependencyManager::FormatDropError(const CatalogEntry &object, + const vector> &dependents) { + auto subject = GetLookupProperties(object); + string result; + for (auto &dependent : dependents) { + result += DependencyToString(subject, GetLookupProperties(dependent)); + } + return DropErrorToString(object.name, result); +} + string DependencyManager::CollectDependents(CatalogTransaction transaction, catalog_entry_set_t &entries, CatalogEntryInfo &info) { string result; for (auto &entry : entries) { D_ASSERT(!IsSystemEntry(entry.get())); auto other_info = GetLookupProperties(entry); - result += StringUtil::Format("%s depends on %s.\n", EntryToString(other_info), EntryToString(info)); + result += DependencyToString(info, other_info); catalog_entry_set_t entry_dependents; ScanDependents(transaction, other_info, [&](DependencyEntry &dep) { auto child = LookupEntry(transaction, dep); @@ -633,11 +640,8 @@ catalog_entry_set_t DependencyManager::CheckDropDependencies(CatalogTransaction } }); if (!blocking_dependents.empty()) { - string error_string = - StringUtil::Format("Cannot drop entry %s because there are entries that depend on it.\n", object.name); - error_string += CollectDependents(transaction, blocking_dependents, info); - error_string += "Use DROP...CASCADE to drop all dependents."; - throw DependencyException(error_string); + throw DependencyException( + DropErrorToString(object.name, CollectDependents(transaction, blocking_dependents, info))); } // Look through all the entries that 'object' depends on diff --git a/src/duckdb/src/catalog/duck_catalog.cpp b/src/duckdb/src/catalog/duck_catalog.cpp index 05698d1cf..962c96e33 100644 --- a/src/duckdb/src/catalog/duck_catalog.cpp +++ b/src/duckdb/src/catalog/duck_catalog.cpp @@ -3,6 +3,7 @@ #include "duckdb/catalog/catalog_entry/duck_schema_entry.hpp" #include "duckdb/storage/storage_manager.hpp" #include "duckdb/parser/parsed_data/drop_info.hpp" +#include "duckdb/parser/parsed_data/alter_schema_info.hpp" #include "duckdb/parser/parsed_data/create_schema_info.hpp" #include "duckdb/catalog/default/default_schemas.hpp" #include "duckdb/function/built_in_functions.hpp" @@ -76,51 +77,42 @@ optional_ptr DuckCatalog::CreateSchemaInternal(CatalogTransaction } return result; } - // nested schema: navigate to the deepest parent schema, then create the schema inside it - optional_ptr parent_entry = schemas->GetEntry(transaction, parents[0]); - if (!parent_entry) { - // the root component was not a catalog (otherwise it would have been resolved as one) nor an existing schema - throw CatalogException("%s is not a catalog or schema", parents[0]); + EntryLookupInfo lookup(CatalogType::SCHEMA_ENTRY, info.GetQualifiedName().Parent().Parent()); + auto parent = + LookupSchemaPath(transaction, lookup, OnEntryNotFound::THROW_EXCEPTION, [&](const EntryLookupInfo &root) { + auto entry = schemas->GetEntry(transaction, root.GetEntryIdentifier()); + if (!entry) { + throw CatalogException("%s is not a catalog or schema", root.GetEntryIdentifier()); + } + return entry; + }); + return parent->Cast().CreateSchema(transaction, info); +} + +void DuckCatalog::AlterSchema(CatalogTransaction transaction, SchemaCatalogEntry &schema, AlterSchemaInfo &info) { + switch (info.alter_schema_type) { + case AlterSchemaType::SET_SCHEMA_OPTIONS: + throw NotImplementedException("SET () is not supported for DuckDB schemas"); + case AlterSchemaType::RESET_SCHEMA_OPTIONS: + throw NotImplementedException("RESET () is not supported for DuckDB schemas"); + default: + throw InternalException("Unrecognized alter schema type!"); } - for (idx_t i = 1; i < parents.size(); i++) { - auto &duck_parent = parent_entry->Cast(); - parent_entry = duck_parent.GetCatalogSet(CatalogType::SCHEMA_ENTRY).GetEntry(transaction, parents[i]); - if (!parent_entry) { - throw CatalogException("Cannot create nested schema %s: parent schema %s does not exist", info.SchemaName(), - parents[i]); - } - } - return parent_entry->Cast().CreateSchema(transaction, info); } optional_ptr DuckCatalog::CreateSchema(CatalogTransaction transaction, CreateSchemaInfo &info) { D_ASSERT(!info.SchemaName().empty()); auto result = CreateSchemaInternal(transaction, info); if (!result) { - switch (info.on_conflict) { - case OnCreateConflict::ERROR_ON_CONFLICT: - throw CatalogException::EntryAlreadyExists(CatalogType::SCHEMA_ENTRY, info.SchemaName()); - case OnCreateConflict::REPLACE_ON_CONFLICT: { + if (info.ShouldReplaceOnConflict()) { DropInfo drop_info; drop_info.type = CatalogType::SCHEMA_ENTRY; - // build the path [catalog, parent schemas..., schema] so a nested schema can be navigated on drop - vector drop_path; - drop_path.push_back(info.SchemaCatalog()); - for (auto &parent : info.ParentSchemas()) { - drop_path.push_back(parent); - } - drop_info.SetQualifiedName(QualifiedName(std::move(drop_path), info.SchemaName())); + drop_info.SetQualifiedName(info.GetQualifiedName().Parent()); DropSchema(transaction, drop_info); result = CreateSchemaInternal(transaction, info); if (!result) { throw InternalException("Failed to create schema entry in CREATE_OR_REPLACE"); } - break; - } - case OnCreateConflict::IGNORE_ON_CONFLICT: - break; - default: - throw InternalException("Unsupported OnCreateConflict for CreateSchema"); } return nullptr; } @@ -128,28 +120,15 @@ optional_ptr DuckCatalog::CreateSchema(CatalogTransaction transact } void DuckCatalog::DropSchema(CatalogTransaction transaction, DropInfo &info) { - auto &path = info.GetQualifiedName().Path(); - auto &schema_name = info.GetQualifiedName().Name(); - D_ASSERT(!schema_name.empty()); - // navigate to the catalog set that holds the schema to drop: the root set for a top-level schema, or the nested - // schemas set of the deepest parent. The path is [catalog, parent schemas..., schema] after binding; for internal - // callers it can be just [schema]. - reference target_set = *schemas; - for (idx_t i = 1; i + 1 < path.size(); i++) { - auto parent_entry = target_set.get().GetEntry(transaction, path[i]); - if (!parent_entry) { - if (info.if_not_found == OnEntryNotFound::THROW_EXCEPTION) { - throw CatalogException("Cannot drop schema %s: parent schema %s does not exist", schema_name, path[i]); - } - return; - } - target_set = parent_entry->Cast().GetCatalogSet(CatalogType::SCHEMA_ENTRY); + EntryLookupInfo lookup(CatalogType::SCHEMA_ENTRY, info.GetQualifiedName()); + auto schema = LookupSchema(transaction, lookup, info.if_not_found); + if (!schema) { + return; } - // drop exactly this schema - the dependency manager blocks the drop (RESTRICT) or cascades to the schema's - // contents, including any nested schemas (which depend on it), when CASCADE is given - if (!target_set.get().DropEntry(transaction, schema_name, info.cascade, info.allow_drop_internal)) { + D_ASSERT(schema->set); + if (!schema->set->DropEntry(transaction, schema->name, info.cascade, info.allow_drop_internal)) { if (info.if_not_found == OnEntryNotFound::THROW_EXCEPTION) { - throw CatalogException::MissingEntry(CatalogType::SCHEMA_ENTRY, schema_name, string()); + throw CatalogException::MissingEntry(lookup, string()); } } } @@ -158,17 +137,6 @@ void DuckCatalog::DropSchema(ClientContext &context, DropInfo &info) { DropSchema(GetCatalogTransaction(context), info); } -static void ScanNestedSchemas(CatalogTransaction transaction, SchemaCatalogEntry &schema, - const std::function &callback) { - // scan the nested schemas using the already-obtained transaction - re-deriving it from the context here would - // acquire the meta-transaction lock while a catalog set lock is held, inverting the lock order - schema.Cast().GetCatalogSet(CatalogType::SCHEMA_ENTRY).Scan(transaction, [&](CatalogEntry &entry) { - auto &nested = entry.Cast(); - callback(nested); - ScanNestedSchemas(transaction, nested, callback); - }); -} - static void ScanNestedSchemas(VisibilityBound bound, DuckSchemaEntry &schema, const std::function &callback) { schema.Scan(CatalogType::SCHEMA_ENTRY, bound, [&](CatalogEntry &entry) { @@ -184,8 +152,7 @@ void DuckCatalog::ScanSchemas(ClientContext &context, std::functionScan(transaction, [&](CatalogEntry &entry) { auto &schema = entry.Cast(); - callback(schema); - ScanNestedSchemas(transaction, schema, callback); + schema.ScanSchemaTree(transaction, callback); }); } @@ -204,38 +171,9 @@ CatalogSet &DuckCatalog::GetSchemaCatalogSet() { optional_ptr DuckCatalog::LookupSchema(CatalogTransaction transaction, const EntryLookupInfo &schema_lookup, OnEntryNotFound if_not_found) { - // build the (possibly nested) schema path from the qualification. A qualified schema lookup always leads with the - // catalog component (e.g. [catalog, schema], [catalog, "", schema] or a nested [catalog, s1, s2]); a bare lookup is - // just [schema]. Drop that leading catalog component and any empty placeholders. - auto &qualified_path = schema_lookup.GetQualifiedName().Path(); - vector schema_path; - for (idx_t i = 0; i < qualified_path.size(); i++) { - if (i == 0 && qualified_path.size() > 1) { - continue; - } - if (qualified_path[i].empty()) { - continue; - } - schema_path.push_back(qualified_path[i]); - } - D_ASSERT(!schema_path.empty()); - // navigate the schema chain: the outermost schema lives in the catalog, each subsequent one in its parent's schemas - reference current_set = *schemas; - optional_ptr entry; - for (idx_t i = 0; i < schema_path.size(); i++) { - entry = current_set.get().GetEntry(transaction, schema_path[i]); - if (!entry) { - if (if_not_found == OnEntryNotFound::THROW_EXCEPTION) { - throw CatalogException(schema_lookup.GetErrorContext(), "Schema with name %s does not exist!", - schema_path[i].GetIdentifierName()); - } - return nullptr; - } - if (i + 1 < schema_path.size()) { - current_set = entry->Cast().GetCatalogSet(CatalogType::SCHEMA_ENTRY); - } - } - return &entry->Cast(); + return LookupSchemaPath(transaction, schema_lookup, if_not_found, [&](const EntryLookupInfo &lookup) { + return schemas->GetEntry(transaction, lookup.GetEntryIdentifier()); + }); } DatabaseSize DuckCatalog::GetDatabaseSize(ClientContext &context) { diff --git a/src/duckdb/src/catalog/entry_lookup_info.cpp b/src/duckdb/src/catalog/entry_lookup_info.cpp index 8129310e6..b76eb902a 100644 --- a/src/duckdb/src/catalog/entry_lookup_info.cpp +++ b/src/duckdb/src/catalog/entry_lookup_info.cpp @@ -31,13 +31,8 @@ EntryLookupInfo::EntryLookupInfo(const EntryLookupInfo &parent, optional_ptr schema_path) { - Identifier schema_name; - if (!schema_path.empty()) { - schema_name = std::move(schema_path.back()); - schema_path.pop_back(); - } - return EntryLookupInfo(CatalogType::SCHEMA_ENTRY, QualifiedName(std::move(schema_path), std::move(schema_name)), - parent.at_clause, parent.error_context); + return EntryLookupInfo(CatalogType::SCHEMA_ENTRY, QualifiedName::FromPath(std::move(schema_path)), parent.at_clause, + parent.error_context); } CatalogType EntryLookupInfo::GetCatalogType() const { diff --git a/src/duckdb/src/common/adbc/adbc.cpp b/src/duckdb/src/common/adbc/adbc.cpp index 68a90ef50..fd05fc8f3 100644 --- a/src/duckdb/src/common/adbc/adbc.cpp +++ b/src/duckdb/src/common/adbc/adbc.cpp @@ -5,11 +5,16 @@ #include "duckdb/common/string_util.hpp" #include "duckdb.h" +#include "duckdb/common/arrow/arrow_format.hpp" #include "duckdb/common/arrow/arrow_wrapper.hpp" #include "duckdb/common/arrow/nanoarrow/nanoarrow.hpp" +#include "duckdb/main/query_parameters.hpp" +#include "duckdb/main/query_result_stream.hpp" #include "duckdb/common/exception.hpp" +#include "duckdb/common/mutex.hpp" #include "duckdb/main/connection.hpp" +#include "duckdb/main/valid_checker.hpp" #include "duckdb/common/adbc/options.h" #include "duckdb/common/adbc/single_batch_array_stream.hpp" #include "duckdb/function/table/arrow.hpp" @@ -30,8 +35,6 @@ struct DuckDBErrorDetails { #include -#include "duckdb/main/materialized_query_result.hpp" - #include "duckdb/parser/keyword_helper.hpp" // We must leak the symbols of the init function @@ -128,87 +131,167 @@ struct DuckDBAdbcStatementWrapper { ArrowArrayStream ingestion_stream; IngestionMode ingestion_mode = IngestionMode::CREATE; bool temporary_table = false; + //! Metadata calls complete their result at submit, so they snapshot the catalog at the call + bool eager_result = false; uint64_t plan_length; }; -struct MaterializedData { - ArrowArray *batches; - idx_t count; - idx_t current; -}; - struct DuckDBAdbcStreamWrapper { - duckdb_result result; - char *last_error; - AdbcStatusCode status_code; - AdbcError adbc_error; - MaterializedData *materialized; - duckdb::DuckDBAdbcConnectionWrapper *conn_wrapper; + ~DuckDBAdbcStreamWrapper() { + free(last_error); + InitializeADBCError(&adbc_error); + } + + //! Drained into `materialized` and reset when another statement runs on the connection + duckdb::unique_ptr> stream; + //! A result the driver completed before handing it out, which no later statement on the connection affects + duckdb::unique_ptr result; + duckdb::ArrowSchemaWrapper schema; + //! What draining collected, handed out before the error it ran into, if any + duckdb::vector> materialized; + //! Serializes a reader with a drain or a close; per stream, so neither blocks another stream's reader + duckdb::mutex fetch_mutex; + idx_t materialized_index = 0; + char *last_error = nullptr; + AdbcStatusCode status_code = ADBC_STATUS_OK; + AdbcError adbc_error = {}; + duckdb::DuckDBAdbcConnectionWrapper *conn_wrapper = nullptr; }; -class DuckDBAdbcStreamWrapperGuard { -public: - explicit DuckDBAdbcStreamWrapperGuard(DuckDBAdbcStreamWrapper *ptr_p) : ptr(ptr_p) { +static bool IsCancellation(duckdb::optional_ptr conn_wrapper, + duckdb::ExceptionType type) { + return type == duckdb::ExceptionType::INTERRUPT && conn_wrapper && conn_wrapper->cancel_requested; +} + +static void SetStreamError(DuckDBAdbcStreamWrapper &wrapper, const duckdb::ErrorData &error) { + free(wrapper.last_error); + wrapper.last_error = strdup(error.Message().c_str()); + wrapper.status_code = + IsCancellation(wrapper.conn_wrapper, error.Type()) ? ADBC_STATUS_CANCELLED : ADBC_STATUS_INTERNAL; + wrapper.adbc_error.message = wrapper.last_error; + wrapper.adbc_error.vendor_code = ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA; + delete static_cast(wrapper.adbc_error.private_data); + wrapper.adbc_error.private_data = nullptr; + wrapper.adbc_error.release = nullptr; + auto *details = new (std::nothrow) DuckDBErrorDetails(); + if (details) { + details->entries.emplace_back("duckdb:error_type", DuckDBErrorTypeToString(duckdb::ErrorTypeToC(error.Type()))); + wrapper.adbc_error.private_data = details; + wrapper.adbc_error.release = ::ReleaseStreamErrorDetails; } - DuckDBAdbcStreamWrapperGuard(const DuckDBAdbcStreamWrapperGuard &) = delete; - DuckDBAdbcStreamWrapperGuard &operator=(const DuckDBAdbcStreamWrapperGuard &) = delete; +} - ~DuckDBAdbcStreamWrapperGuard() { - if (ptr) { - duckdb_destroy_result(&ptr->result); - free(ptr); +//! The next array, null at the end of the result and on an error, which goes to `error` +static duckdb::unique_ptr FetchArray(DuckDBAdbcStreamWrapper &wrapper, + duckdb::ErrorData &error) { + try { + if (wrapper.stream) { + auto array = wrapper.stream->Fetch(); + if (!array && wrapper.stream->HasError()) { + error = wrapper.stream->GetErrorObject(); + } + return array; } + if (wrapper.result) { + return wrapper.result->Fetch(); + } + } catch (std::exception &ex) { + error = duckdb::ErrorData(ex); + } catch (...) { + error = duckdb::ErrorData("Unknown error in Fetch"); } + return nullptr; +} - DuckDBAdbcStreamWrapper *release() { - auto tmp = ptr; - ptr = nullptr; - return tmp; +//! Drains the stream into `materialized`; an error ends the drain and is recorded on the wrapper +static void MaterializeStream(DuckDBAdbcStreamWrapper &wrapper) { + const duckdb::lock_guard guard(wrapper.fetch_mutex); + if (!wrapper.stream) { + return; } - - DuckDBAdbcStreamWrapper *get() const { - return ptr; + try { + while (!wrapper.last_error) { + duckdb::ErrorData error; + auto array = FetchArray(wrapper, error); + if (error.HasError()) { + SetStreamError(wrapper, error); + break; + } + if (!array) { + break; + } + wrapper.materialized.push_back(std::move(array)); + } + } catch (std::exception &ex) { + SetStreamError(wrapper, duckdb::ErrorData(ex)); } + wrapper.stream.reset(); +} - DuckDBAdbcStreamWrapper *operator->() const { - return ptr; +static void CopySchema(const ArrowSchema &source, duckdb::ArrowSchemaWrapper &target) { + if (target.arrow_schema.release) { + target.arrow_schema.release(&target.arrow_schema); } + if (duckdb_nanoarrow::ArrowSchemaDeepCopy(&source, &target.arrow_schema) != NANOARROW_OK) { + throw duckdb::OutOfMemoryException("Failed to copy the Arrow schema of the result"); + } +} -private: - DuckDBAdbcStreamWrapper *ptr; -}; - -static bool IsInterruptError(const char *message) { - if (!message) { - return false; +//! eager completes the result here, for a driver-built query that must reflect the catalog at the call. rows_affected +//! stays -1, which ADBC defines as unknown, unless the statement reports a changed-row count +static AdbcStatusCode ExecuteArrow(duckdb::PreparedStatementWrapper &prepared, DuckDBAdbcStreamWrapper &out, + bool reads_to_the_end, int64_t &rows_affected, struct AdbcError *error, bool eager) { + out.stream.reset(); + out.result.reset(); + rows_affected = -1; + duckdb::ErrorData error_data; + try { + duckdb::QueryParameters parameters(duckdb::make_shared_ptr(STANDARD_VECTOR_SIZE)); + auto result = prepared.statement->Submit(prepared.values, parameters); + if (!result->HasError() && !eager && + duckdb::StreamsByDefault(prepared.statement->GetStatementType(), *result, reads_to_the_end)) { + out.stream = duckdb::make_uniq>(std::move(result)); + CopySchema(out.stream->FormatState().Schema(), out.schema); + return ADBC_STATUS_OK; + } + result->Complete(); + if (!result->HasError()) { + CopySchema(result->FormatState().Schema(), out.schema); + if (result->GetStatementProperties().return_type == duckdb::StatementReturnType::CHANGED_ROWS) { + auto &arrays = result->Collection(); + auto rows_changed = arrays.empty() ? 0 : duckdb::ArrowFormat::ChangedRows(arrays.front()->arrow_array); + rows_affected = static_cast(rows_changed); + } + out.result = std::move(result); + return ADBC_STATUS_OK; + } + error_data = result->GetErrorObject(); + } catch (std::exception &ex) { + error_data = duckdb::ErrorData(ex); + } catch (...) { + error_data = duckdb::ErrorData("Unknown error in Execute"); } - return std::strcmp(message, duckdb::InterruptException::INTERRUPT_MESSAGE) == 0; + SetError(error, error_data.Message()); + AppendDuckDBErrorDetails(error, duckdb::ErrorTypeToC(error_data.Type())); + return IsCancellation(out.conn_wrapper, error_data.Type()) ? ADBC_STATUS_CANCELLED : ADBC_STATUS_INVALID_ARGUMENT; } static AdbcStatusCode QueryInternal(struct AdbcConnection *connection, struct ArrowArrayStream *out, const char *query, struct AdbcError *error) { - AdbcStatement statement; + AdbcStatement statement = {}; auto status = StatementNew(connection, &statement, error); if (status != ADBC_STATUS_OK) { - StatementRelease(&statement, error); - SetError(error, "unable to initialize statement"); return status; } + // A metadata call promises a point-in-time snapshot, so its result must complete before the call returns + static_cast(statement.private_data)->eager_result = true; status = StatementSetSqlQuery(&statement, query, error); - if (status != ADBC_STATUS_OK) { - StatementRelease(&statement, error); - SetError(error, "unable to initialize statement"); - return status; - } - status = StatementExecuteQuery(&statement, out, nullptr, error); - if (status != ADBC_STATUS_OK) { - StatementRelease(&statement, error); - SetError(error, "unable to initialize statement"); - return status; + if (status == ADBC_STATUS_OK) { + status = StatementExecuteQuery(&statement, out, nullptr, error); } StatementRelease(&statement, error); - return ADBC_STATUS_OK; + return status; } struct DuckDBAdbcDatabaseWrapper { @@ -518,7 +601,11 @@ AdbcStatusCode ConnectionNew(struct AdbcConnection *connection, struct AdbcError return ADBC_STATUS_OK; } -static AdbcStatusCode ExecuteQuery(duckdb::Connection *conn, const char *query, struct AdbcError *error) { +static AdbcStatusCode ExecuteQuery(duckdb::DuckDBAdbcConnectionWrapper &conn_wrapper, const char *query, + struct AdbcError *error) { + // The connection refuses a statement while one of its streams is open + conn_wrapper.MaterializeStreams(); + auto conn = reinterpret_cast(conn_wrapper.connection); auto res = conn->Query(query); if (res->HasError()) { auto error_message = "Failed to execute query \"" + std::string(query) + "\": " + res->GetError(); @@ -528,14 +615,34 @@ static AdbcStatusCode ExecuteQuery(duckdb::Connection *conn, const char *query, return ADBC_STATUS_OK; } -static AdbcStatusCode InternalSetOption(duckdb::Connection &conn, std::unordered_map &options, - struct AdbcError *error) { +//! The engine turns a COMMIT of a transaction that an earlier error invalidated into a rollback that reports success, +//! so that case is reported here. Either way the transaction has ended when this returns. +static AdbcStatusCode CommitTransaction(duckdb::DuckDBAdbcConnectionWrapper &conn_wrapper, struct AdbcError *error) { + // Reading an open stream into memory can fail, which invalidates the transaction + conn_wrapper.MaterializeStreams(); + auto &context = *reinterpret_cast(conn_wrapper.connection)->context; + std::string invalidated; + if (context.transaction.HasActiveTransaction() && + duckdb::ValidChecker::IsInvalidated(context.ActiveTransaction())) { + invalidated = duckdb::ValidChecker::InvalidatedMessage(context.ActiveTransaction()); + } + auto status = ExecuteQuery(conn_wrapper, "COMMIT", error); + if (status == ADBC_STATUS_OK && !invalidated.empty()) { + SetError(error, "Failed to commit, the transaction was rolled back after an earlier error: " + invalidated); + return ADBC_STATUS_INVALID_STATE; + } + return status; +} + +static AdbcStatusCode InternalSetOption(duckdb::DuckDBAdbcConnectionWrapper &conn_wrapper, struct AdbcError *error) { + auto &conn = *reinterpret_cast(conn_wrapper.connection); + auto &options = conn_wrapper.options; // If we got here, the options have already been validated and are acceptable for (auto &option : options) { if (strcmp(option.first.c_str(), ADBC_CONNECTION_OPTION_AUTOCOMMIT) == 0) { if (strcmp(option.second.c_str(), ADBC_OPTION_VALUE_ENABLED) == 0) { if (conn.HasActiveTransaction()) { - AdbcStatusCode status = ExecuteQuery(&conn, "COMMIT", error); + AdbcStatusCode status = CommitTransaction(conn_wrapper, error); if (status != ADBC_STATUS_OK) { options.clear(); return status; @@ -543,7 +650,7 @@ static AdbcStatusCode InternalSetOption(duckdb::Connection &conn, std::unordered } } else if (strcmp(option.second.c_str(), ADBC_OPTION_VALUE_DISABLED) == 0) { if (!conn.HasActiveTransaction()) { - AdbcStatusCode status = ExecuteQuery(&conn, "START TRANSACTION", error); + AdbcStatusCode status = ExecuteQuery(conn_wrapper, "START TRANSACTION", error); if (status != ADBC_STATUS_OK) { options.clear(); return status; @@ -563,9 +670,8 @@ static AdbcStatusCode ConnectionSetOptionCurrentValue(duckdb::DuckDBAdbcConnecti SetError(error, "Connection is not initialized"); return ADBC_STATUS_INVALID_STATE; } - auto conn = reinterpret_cast(conn_wrapper->connection); std::string query = sql_prefix + duckdb::SQLIdentifier(value); - return ExecuteQuery(conn, query.c_str(), error); + return ExecuteQuery(*conn_wrapper, query.c_str(), error); } AdbcStatusCode ConnectionSetOption(struct AdbcConnection *connection, const char *key, const char *value, @@ -595,8 +701,7 @@ AdbcStatusCode ConnectionSetOption(struct AdbcConnection *connection, const char // If the connection has not yet been initialized, we just return here. return ADBC_STATUS_OK; } - auto conn = reinterpret_cast(conn_wrapper->connection); - return InternalSetOption(*conn, conn_wrapper->options, error); + return InternalSetOption(*conn_wrapper, error); } if (strcmp(key, ADBC_CONNECTION_OPTION_CURRENT_CATALOG) == 0) { return ConnectionSetOptionCurrentValue(conn_wrapper, "USE ", value, error); @@ -801,11 +906,13 @@ AdbcStatusCode ConnectionCommit(struct AdbcConnection *connection, struct AdbcEr return ADBC_STATUS_INVALID_STATE; } - AdbcStatusCode status = ExecuteQuery(conn, "COMMIT", error); - if (status != ADBC_STATUS_OK) { + AdbcStatusCode status = CommitTransaction(*conn_wrapper, error); + if (status != ADBC_STATUS_OK && status != ADBC_STATUS_INVALID_STATE) { return status; } - return ExecuteQuery(conn, "START TRANSACTION", error); + // The transaction ended, committed or rolled back, so manual commit mode starts the next one + auto restarted = ExecuteQuery(*conn_wrapper, "START TRANSACTION", error); + return status == ADBC_STATUS_OK ? restarted : status; } AdbcStatusCode ConnectionRollback(struct AdbcConnection *connection, struct AdbcError *error) { @@ -820,11 +927,13 @@ AdbcStatusCode ConnectionRollback(struct AdbcConnection *connection, struct Adbc return ADBC_STATUS_INVALID_STATE; } - AdbcStatusCode status = ExecuteQuery(conn, "ROLLBACK", error); + // Reading the open streams into memory first would only keep rows of a transaction that is rolled back + conn_wrapper->CloseStreams("the stream was closed because its transaction was rolled back"); + AdbcStatusCode status = ExecuteQuery(*conn_wrapper, "ROLLBACK", error); if (status != ADBC_STATUS_OK) { return status; } - return ExecuteQuery(conn, "START TRANSACTION", error); + return ExecuteQuery(*conn_wrapper, "START TRANSACTION", error); } AdbcStatusCode ConnectionCancel(struct AdbcConnection *connection, struct AdbcError *error) { @@ -841,6 +950,7 @@ AdbcStatusCode ConnectionCancel(struct AdbcConnection *connection, struct AdbcEr SetError(error, "Connection is not initialized"); return ADBC_STATUS_INVALID_STATE; } + conn_wrapper->cancel_requested = true; duckdb_interrupt(conn_wrapper->connection); return ADBC_STATUS_OK; } @@ -1383,8 +1493,7 @@ AdbcStatusCode ConnectionInit(struct AdbcConnection *connection, struct AdbcData return adbc_status; } // We might have options to set - auto conn = reinterpret_cast(conn_wrapper->connection); - return InternalSetOption(*conn, conn_wrapper->options, error); + return InternalSetOption(*conn_wrapper, error); } AdbcStatusCode ConnectionRelease(struct AdbcConnection *connection, struct AdbcError *error) { @@ -1408,29 +1517,8 @@ static int get_schema(struct ArrowArrayStream *stream, struct ArrowSchema *out) if (!stream || !stream->private_data || !out) { return DuckDBError; } - auto result_wrapper = static_cast(stream->private_data); - auto count = duckdb_column_count(&result_wrapper->result); - std::vector types(count); - - std::vector owned_names; - owned_names.reserve(count); - duckdb::vector names(count); - for (idx_t i = 0; i < count; i++) { - types[i] = duckdb_column_logical_type(&result_wrapper->result, i); - auto column_name = duckdb_column_name(&result_wrapper->result, i); - owned_names.emplace_back(column_name); - names[i] = owned_names.back().c_str(); - } - - auto arrow_options = duckdb_result_get_arrow_options(&result_wrapper->result); - - auto res = duckdb_to_arrow_schema(arrow_options, types.data(), names.data(), count, out); - duckdb_destroy_arrow_options(&arrow_options); - for (auto &type : types) { - duckdb_destroy_logical_type(&type); - } - if (res) { - duckdb_destroy_error_data(&res); + auto &result_wrapper = *static_cast(stream->private_data); + if (duckdb_nanoarrow::ArrowSchemaDeepCopy(&result_wrapper.schema.arrow_schema, out) != NANOARROW_OK) { return DuckDBError; } return DuckDBSuccess; @@ -1441,88 +1529,26 @@ static int get_next(struct ArrowArrayStream *stream, struct ArrowArray *out) { return DuckDBError; } out->release = nullptr; - auto result_wrapper = static_cast(stream->private_data); - - // If the stream has been materialized, return from stored batches - if (result_wrapper->materialized) { - auto mat = result_wrapper->materialized; - if (mat->current >= mat->count) { - // Surface any error that was encountered during materialization - if (result_wrapper->last_error) { - return DuckDBError; - } - return DuckDBSuccess; // end of stream - } - // Transfer ownership of the batch to the caller - *out = mat->batches[mat->current]; - mat->batches[mat->current].release = nullptr; - mat->current++; + auto &result_wrapper = *static_cast(stream->private_data); + // A statement on the same connection drains or closes this stream from its own thread; serialize with it + const duckdb::lock_guard guard(result_wrapper.fetch_mutex); + if (result_wrapper.materialized_index < result_wrapper.materialized.size()) { + auto array = std::move(result_wrapper.materialized[result_wrapper.materialized_index++]); + array->MoveTo(*out); return DuckDBSuccess; } - - auto duckdb_chunk = duckdb_fetch_chunk(result_wrapper->result); - if (!duckdb_chunk) { - // End of stream or error; distinguish by checking the result error message. - auto err = duckdb_result_error(&result_wrapper->result); - if (err && err[0] != '\0') { - if (result_wrapper->last_error) { - free(result_wrapper->last_error); - } - result_wrapper->last_error = strdup(err); - result_wrapper->status_code = IsInterruptError(err) ? ADBC_STATUS_CANCELLED : ADBC_STATUS_INTERNAL; - // Populate adbc_error for AdbcErrorFromArrayStream with rich metadata - result_wrapper->adbc_error.message = result_wrapper->last_error; - result_wrapper->adbc_error.vendor_code = ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA; - if (result_wrapper->adbc_error.private_data) { - delete static_cast(result_wrapper->adbc_error.private_data); - result_wrapper->adbc_error.private_data = nullptr; - } - auto *details = new (std::nothrow) DuckDBErrorDetails(); - if (details) { - details->entries.emplace_back( - "duckdb:error_type", DuckDBErrorTypeToString(duckdb_result_error_type(&result_wrapper->result))); - result_wrapper->adbc_error.private_data = details; - result_wrapper->adbc_error.release = ::ReleaseStreamErrorDetails; - } else { - result_wrapper->adbc_error.release = nullptr; - } - return DuckDBError; - } - return DuckDBSuccess; + if (result_wrapper.last_error) { + return DuckDBError; } - auto arrow_options = duckdb_result_get_arrow_options(&result_wrapper->result); - - auto conversion_success = duckdb_data_chunk_to_arrow(arrow_options, duckdb_chunk, out); - duckdb_destroy_arrow_options(&arrow_options); - duckdb_destroy_data_chunk(&duckdb_chunk); - - if (conversion_success) { - auto conv_err_msg = duckdb_error_data_message(conversion_success); - if (conv_err_msg && conv_err_msg[0] != '\0') { - if (result_wrapper->last_error) { - free(result_wrapper->last_error); - } - result_wrapper->last_error = strdup(conv_err_msg); - result_wrapper->status_code = ADBC_STATUS_INTERNAL; - result_wrapper->adbc_error.message = result_wrapper->last_error; - result_wrapper->adbc_error.vendor_code = ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA; - if (result_wrapper->adbc_error.private_data) { - delete static_cast(result_wrapper->adbc_error.private_data); - result_wrapper->adbc_error.private_data = nullptr; - } - auto *details = new (std::nothrow) DuckDBErrorDetails(); - if (details) { - details->entries.emplace_back( - "duckdb:error_type", DuckDBErrorTypeToString(duckdb_error_data_error_type(conversion_success))); - result_wrapper->adbc_error.private_data = details; - result_wrapper->adbc_error.release = ::ReleaseStreamErrorDetails; - } else { - result_wrapper->adbc_error.release = nullptr; - } - } - duckdb_destroy_error_data(&conversion_success); + duckdb::ErrorData error; + auto array = FetchArray(result_wrapper, error); + if (error.HasError()) { + SetStreamError(result_wrapper, error); return DuckDBError; } + if (array) { + array->MoveTo(*out); + } return DuckDBSuccess; } @@ -1530,33 +1556,13 @@ void release(struct ArrowArrayStream *stream) { if (!stream || !stream->release) { return; } - auto result_wrapper = reinterpret_cast(stream->private_data); + auto result_wrapper = static_cast(stream->private_data); if (result_wrapper) { - // Unregister from connection's active streams if (result_wrapper->conn_wrapper) { result_wrapper->conn_wrapper->UnregisterStream(result_wrapper); } - // Clean up materialized data if present - if (result_wrapper->materialized) { - auto mat = result_wrapper->materialized; - for (idx_t i = mat->current; i < mat->count; i++) { - if (mat->batches[i].release) { - mat->batches[i].release(&mat->batches[i]); - } - } - free(mat->batches); - free(mat); - result_wrapper->materialized = nullptr; - } - duckdb_destroy_result(&result_wrapper->result); - if (result_wrapper->last_error) { - free(result_wrapper->last_error); - result_wrapper->last_error = nullptr; - } - // Release any error that was set on the stream wrapper - InitializeADBCError(&result_wrapper->adbc_error); + delete result_wrapper; } - free(stream->private_data); stream->private_data = nullptr; stream->release = nullptr; } @@ -1566,7 +1572,8 @@ const char *get_last_error(struct ArrowArrayStream *stream) { return nullptr; } auto result_wrapper = reinterpret_cast(stream->private_data); - return result_wrapper ? result_wrapper->last_error : nullptr; + const duckdb::lock_guard guard(result_wrapper->fetch_mutex); + return result_wrapper->last_error; } const AdbcError *ErrorFromArrayStream(struct ArrowArrayStream *stream, AdbcStatusCode *status) { @@ -1578,6 +1585,7 @@ const AdbcError *ErrorFromArrayStream(struct ArrowArrayStream *stream, AdbcStatu return nullptr; } auto result_wrapper = reinterpret_cast(stream->private_data); + const duckdb::lock_guard guard(result_wrapper->fetch_mutex); if (!result_wrapper->last_error) { return nullptr; } @@ -1646,9 +1654,10 @@ static std::string BuildCreateTableSQL(const char *catalog, const char *schema, return create_table.str(); } -AdbcStatusCode Ingest(duckdb_connection connection, const char *catalog, const char *table_name, const char *schema, - struct ArrowArrayStream *input, struct AdbcError *error, IngestionMode ingestion_mode, - bool temporary, int64_t *rows_affected) { +AdbcStatusCode Ingest(duckdb_connection connection, + duckdb::optional_ptr conn_wrapper, const char *catalog, + const char *table_name, const char *schema, struct ArrowArrayStream *input, + struct AdbcError *error, IngestionMode ingestion_mode, bool temporary, int64_t *rows_affected) { if (!connection) { SetError(error, "Missing connection object"); return ADBC_STATUS_INVALID_ARGUMENT; @@ -1743,7 +1752,7 @@ AdbcStatusCode Ingest(duckdb_connection connection, const char *catalog, const c if (duckdb_query(connection, sql.c_str(), &result) == DuckDBError) { const char *error_msg = duckdb_result_error(&result); bool already_exists = error_msg && std::string(error_msg).find("already exists") != std::string::npos; - bool interrupted = IsInterruptError(error_msg); + bool interrupted = IsCancellation(conn_wrapper, duckdb::ErrorTypeFromC(duckdb_result_error_type(&result))); SetError(error, error_msg); AppendDuckDBErrorDetails(error, duckdb_result_error_type(&result)); duckdb_destroy_result(&result); @@ -1771,7 +1780,7 @@ AdbcStatusCode Ingest(duckdb_connection connection, const char *catalog, const c auto err = duckdb_result_error(&result); SetError(error, err); AppendDuckDBErrorDetails(error, duckdb_result_error_type(&result)); - bool interrupted = IsInterruptError(err); + bool interrupted = IsCancellation(conn_wrapper, duckdb::ErrorTypeFromC(duckdb_result_error_type(&result))); duckdb_destroy_result(&result); return interrupted ? ADBC_STATUS_CANCELLED : ADBC_STATUS_INTERNAL; } @@ -1786,7 +1795,7 @@ AdbcStatusCode Ingest(duckdb_connection connection, const char *catalog, const c auto err = duckdb_result_error(&result); SetError(error, err); AppendDuckDBErrorDetails(error, duckdb_result_error_type(&result)); - bool interrupted = IsInterruptError(err); + bool interrupted = IsCancellation(conn_wrapper, duckdb::ErrorTypeFromC(duckdb_result_error_type(&result))); duckdb_destroy_result(&result); return interrupted ? ADBC_STATUS_CANCELLED : ADBC_STATUS_INTERNAL; } @@ -1804,6 +1813,20 @@ AdbcStatusCode Ingest(duckdb_connection connection, const char *catalog, const c } return ADBC_STATUS_INTERNAL; } + auto appender_error = [&]() { + auto error_data = duckdb_appender_error_data(appender.Get()); + auto err = duckdb_error_data_message(error_data); + if (err && err[0] != '\0') { + set_ingest_error(err); + } else { + SetError(error, missing_table_error); + } + bool interrupted = + IsCancellation(conn_wrapper, duckdb::ErrorTypeFromC(duckdb_error_data_error_type(error_data))); + AppendDuckDBErrorDetails(error, error_data); + duckdb_destroy_error_data(&error_data); + return interrupted ? ADBC_STATUS_CANCELLED : ADBC_STATUS_INTERNAL; + }; duckdb::ArrowArrayWrapper arrow_array_wrapper; // Initialize rows_affected counter if requested @@ -1825,21 +1848,15 @@ AdbcStatusCode Ingest(duckdb_connection connection, const char *catalog, const c affected += static_cast(chunk->size()); } if (duckdb_append_data_chunk(appender.Get(), out_chunk.chunk) != DuckDBSuccess) { - auto error_data = duckdb_appender_error_data(appender.Get()); - auto err = duckdb_error_data_message(error_data); - if (err && err[0] != '\0') { - set_ingest_error(err); - } else { - SetError(error, missing_table_error); - } - bool interrupted = IsInterruptError(err); - AppendDuckDBErrorDetails(error, error_data); - duckdb_destroy_error_data(&error_data); - return interrupted ? ADBC_STATUS_CANCELLED : ADBC_STATUS_INTERNAL; + return appender_error(); } arrow_array_wrapper = duckdb::ArrowArrayWrapper(); input->get_next(input, &arrow_array_wrapper.arrow_array); } + // The appender writes the rows it still holds when it closes, so a failure there must fail the ingestion + if (duckdb_appender_close(appender.Get()) != DuckDBSuccess) { + return appender_error(); + } if (rows_affected) { *rows_affected = affected; } @@ -1880,6 +1897,7 @@ AdbcStatusCode StatementNew(struct AdbcConnection *connection, struct AdbcStatem statement_wrapper->target_catalog = nullptr; statement_wrapper->db_schema = nullptr; statement_wrapper->temporary_table = false; + statement_wrapper->eager_result = false; statement_wrapper->ingestion_mode = IngestionMode::CREATE; return ADBC_STATUS_OK; @@ -1931,6 +1949,9 @@ AdbcStatusCode StatementCancel(struct AdbcStatement *statement, struct AdbcError SetError(error, "Invalid statement object"); return ADBC_STATUS_INVALID_ARGUMENT; } + if (wrapper->conn_wrapper) { + wrapper->conn_wrapper->cancel_requested = true; + } duckdb_interrupt(wrapper->connection); return ADBC_STATUS_OK; } @@ -1959,6 +1980,13 @@ AdbcStatusCode StatementExecuteSchema(struct AdbcStatement *statement, struct Ar wrapper->conn_wrapper->MaterializeStreams(); } + auto &prepared = *reinterpret_cast(wrapper->statement); + if (prepared.statement && !prepared.statement->HasError() && !prepared.statement->MetadataAvailable()) { + SetError(error, "The result schema of this statement is known only once it is executed: it expands into " + "several statements, or the types of its parameters are not known yet"); + return ADBC_STATUS_NOT_IMPLEMENTED; + } + auto count = duckdb_prepared_statement_column_count(wrapper->statement); std::vector types(count); std::vector owned_names; @@ -2060,9 +2088,9 @@ static AdbcStatusCode IngestToTableFromBoundStream(DuckDBAdbcStatementWrapper *s auto stream = statement->ingestion_stream; // Ingest into a table from the bound stream - return Ingest(statement->connection, statement->target_catalog, statement->ingestion_table_name, - statement->db_schema, &stream, error, statement->ingestion_mode, statement->temporary_table, - rows_affected); + return Ingest(statement->connection, statement->conn_wrapper, statement->target_catalog, + statement->ingestion_table_name, statement->db_schema, &stream, error, statement->ingestion_mode, + statement->temporary_table, rows_affected); } AdbcStatusCode StatementExecuteQuery(struct AdbcStatement *statement, struct ArrowArrayStream *out, @@ -2082,10 +2110,10 @@ AdbcStatusCode StatementExecuteQuery(struct AdbcStatement *statement, struct Arr } // Materialize any active streams on this connection before executing a new query. - // Without materialization, executing a new query would silently invalidate any existing streaming results on the - // same connection. + // Without materialization, the connection would refuse the new query while a streaming result is open. if (wrapper->conn_wrapper) { wrapper->conn_wrapper->MaterializeStreams(); + wrapper->conn_wrapper->cancel_requested = false; } // TODO: Set affected rows, careful with early return @@ -2115,21 +2143,16 @@ AdbcStatusCode StatementExecuteQuery(struct AdbcStatement *statement, struct Arr return ADBC_STATUS_OK; } - auto *raw_stream_wrapper = static_cast(malloc(sizeof(DuckDBAdbcStreamWrapper))); - if (!raw_stream_wrapper) { + duckdb::unique_ptr stream_wrapper(new (std::nothrow) DuckDBAdbcStreamWrapper()); + if (!stream_wrapper) { SetError(error, "Allocation error"); return ADBC_STATUS_INVALID_ARGUMENT; } - raw_stream_wrapper->last_error = nullptr; - raw_stream_wrapper->status_code = ADBC_STATUS_OK; - raw_stream_wrapper->materialized = nullptr; - raw_stream_wrapper->conn_wrapper = wrapper->conn_wrapper; - std::memset(&raw_stream_wrapper->adbc_error, 0, sizeof(raw_stream_wrapper->adbc_error)); - std::memset(&raw_stream_wrapper->result, 0, sizeof(raw_stream_wrapper->result)); - DuckDBAdbcStreamWrapperGuard stream_wrapper(raw_stream_wrapper); + stream_wrapper->conn_wrapper = wrapper->conn_wrapper; + auto &prepared = *reinterpret_cast(wrapper->statement); + int64_t affected = -1; // Only process the stream if there are parameters to bind - auto prepared_statement_params = - reinterpret_cast(wrapper->statement)->statement->GetParameterCount(); + auto prepared_statement_params = prepared.statement->GetParameterCount(); if (has_stream && prepared_statement_params > 0) { // A stream was bound to the statement, use that to bind parameters ArrowArrayStream stream = wrapper->ingestion_stream; @@ -2192,44 +2215,29 @@ AdbcStatusCode StatementExecuteQuery(struct AdbcStatement *statement, struct Arr return ADBC_STATUS_INVALID_ARGUMENT; } } - // Destroy any previous result before overwriting to avoid leaks - duckdb_destroy_result(&stream_wrapper->result); - auto res = duckdb_execute_prepared_streaming(wrapper->statement, &stream_wrapper->result); - if (res != DuckDBSuccess) { - auto err = duckdb_result_error(&stream_wrapper->result); - SetError(error, err); - AppendDuckDBErrorDetails(error, duckdb_result_error_type(&stream_wrapper->result)); - bool interrupted = IsInterruptError(err); - return interrupted ? ADBC_STATUS_CANCELLED : ADBC_STATUS_INVALID_ARGUMENT; + // Only the last row's result is handed out, and which row is last is not known without consuming the + // next one, so no run counts on being read to its end + auto status = ExecuteArrow(prepared, *stream_wrapper, false, affected, error, wrapper->eager_result); + if (status != ADBC_STATUS_OK) { + return status; } - // Recreate wrappers for next iteration arrow_array_wrapper = duckdb::ArrowArrayWrapper(); stream.get_next(&stream, &arrow_array_wrapper.arrow_array); } + if (!stream_wrapper->stream && !stream_wrapper->result) { + SetError(error, "Please provide a non-empty chunk to be bound"); + return ADBC_STATUS_INVALID_ARGUMENT; + } } else { - auto res = duckdb_execute_prepared_streaming(wrapper->statement, &stream_wrapper->result); - if (res != DuckDBSuccess) { - auto err = duckdb_result_error(&stream_wrapper->result); - SetError(error, err); - AppendDuckDBErrorDetails(error, duckdb_result_error_type(&stream_wrapper->result)); - bool interrupted = IsInterruptError(err); - return interrupted ? ADBC_STATUS_CANCELLED : ADBC_STATUS_INVALID_ARGUMENT; + auto status = ExecuteArrow(prepared, *stream_wrapper, out != nullptr, affected, error, wrapper->eager_result); + if (status != ADBC_STATUS_OK) { + return status; } } // Set rows_affected for queries (if not already set by ingestion path) if (rows_affected && !(has_stream && to_table)) { - // For DML queries (INSERT/UPDATE/DELETE), duckdb_rows_changed() returns the count - // For SELECT queries, duckdb_rows_changed() returns 0 - auto rows_changed = duckdb_rows_changed(&stream_wrapper->result); - if (rows_changed > 0) { - // This was a DML query - *rows_affected = static_cast(rows_changed); - } else { - // This is a SELECT or other query that returns a result set - // Return -1 to indicate unknown, as results are streamed - *rows_affected = -1; - } + *rows_affected = affected; } if (out) { @@ -3337,7 +3345,7 @@ AdbcStatusCode ConnectionGetStatistics(struct AdbcConnection *connection, const SetError(error, "Connection is not initialized"); return ADBC_STATUS_INVALID_STATE; } - // Running a query on this connection invalidates any open streaming results. + // The connection refuses a query while a streaming result is open. conn_wrapper->MaterializeStreams(); auto conn = reinterpret_cast(conn_wrapper->connection); @@ -3363,8 +3371,9 @@ AdbcStatusCode ConnectionGetStatistics(struct AdbcConnection *connection, const duckdb::vector catalogs; idx_t schema_count = 0; idx_t stat_count = 0; + auto rows = result->Collection().GetRows(); for (idx_t row_idx = 0; row_idx < result->RowCount(); row_idx++) { - auto size_value = result->GetValue(3, row_idx); + auto size_value = rows.GetValue(3, row_idx); if (size_value.IsNull()) { continue; } @@ -3372,9 +3381,9 @@ AdbcStatusCode ConnectionGetStatistics(struct AdbcConnection *connection, const if (estimated_size < 0) { continue; } - auto catalog_name = result->GetValue(0, row_idx).GetValue(); - auto schema_name = result->GetValue(1, row_idx).GetValue(); - auto current_table = result->GetValue(2, row_idx).GetValue(); + auto catalog_name = rows.GetValue(0, row_idx).GetValue(); + auto schema_name = rows.GetValue(1, row_idx).GetValue(); + auto current_table = rows.GetValue(2, row_idx).GetValue(); if (catalogs.empty() || catalogs.back().name != catalog_name) { catalogs.push_back({catalog_name, {}}); } @@ -3489,119 +3498,28 @@ void duckdb::DuckDBAdbcConnectionWrapper::UnregisterStream(duckdb_adbc::DuckDBAd void duckdb::DuckDBAdbcConnectionWrapper::MaterializeStreams() { const duckdb::lock_guard guard(stream_mutex); for (auto *result_wrapper : active_streams) { - if (!result_wrapper || result_wrapper->materialized) { + if (!result_wrapper) { continue; } + duckdb_adbc::MaterializeStream(*result_wrapper); + } +} - // Collect remaining batches from the streaming result. Errors encountered mid-stream - // are stored on result_wrapper so that get_next can return buffered batches first - // and then surface the error once they are exhausted. - duckdb::vector batches; - auto arrow_options = duckdb_result_get_arrow_options(&result_wrapper->result); - while (true) { - ArrowArray array; - std::memset(&array, 0, sizeof(ArrowArray)); - - auto duckdb_chunk = duckdb_fetch_chunk(result_wrapper->result); - if (!duckdb_chunk) { - // End of stream or error; distinguish by checking the result error message. - auto err = duckdb_result_error(&result_wrapper->result); - if (err && err[0] != '\0') { - if (result_wrapper->last_error) { - free(result_wrapper->last_error); - } - result_wrapper->last_error = strdup(err); - result_wrapper->status_code = - duckdb_adbc::IsInterruptError(err) ? ADBC_STATUS_CANCELLED : ADBC_STATUS_INTERNAL; - result_wrapper->adbc_error.message = result_wrapper->last_error; - result_wrapper->adbc_error.vendor_code = ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA; - if (result_wrapper->adbc_error.private_data) { - delete static_cast(result_wrapper->adbc_error.private_data); - result_wrapper->adbc_error.private_data = nullptr; - } - auto *details = new (std::nothrow) DuckDBErrorDetails(); - if (details) { - details->entries.emplace_back( - "duckdb:error_type", - DuckDBErrorTypeToString(duckdb_result_error_type(&result_wrapper->result))); - result_wrapper->adbc_error.private_data = details; - result_wrapper->adbc_error.release = ::ReleaseStreamErrorDetails; - } else { - result_wrapper->adbc_error.release = nullptr; - } - } - break; - } - auto conversion_err = duckdb_data_chunk_to_arrow(arrow_options, duckdb_chunk, &array); - duckdb_destroy_data_chunk(&duckdb_chunk); - - if (conversion_err) { - // Store error before freeing so get_next can surface it after buffered batches - auto conv_err_msg = duckdb_error_data_message(conversion_err); - if (conv_err_msg && conv_err_msg[0] != '\0') { - if (result_wrapper->last_error) { - free(result_wrapper->last_error); - } - result_wrapper->last_error = strdup(conv_err_msg); - result_wrapper->status_code = ADBC_STATUS_INTERNAL; - result_wrapper->adbc_error.message = result_wrapper->last_error; - result_wrapper->adbc_error.vendor_code = ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA; - if (result_wrapper->adbc_error.private_data) { - delete static_cast(result_wrapper->adbc_error.private_data); - result_wrapper->adbc_error.private_data = nullptr; - } - auto *details = new (std::nothrow) DuckDBErrorDetails(); - if (details) { - details->entries.emplace_back( - "duckdb:error_type", DuckDBErrorTypeToString(duckdb_error_data_error_type(conversion_err))); - result_wrapper->adbc_error.private_data = details; - result_wrapper->adbc_error.release = ::ReleaseStreamErrorDetails; - } else { - result_wrapper->adbc_error.release = nullptr; - } - } - duckdb_destroy_error_data(&conversion_err); - if (array.release) { - array.release(&array); - } - break; - } - batches.push_back(array); +void duckdb::DuckDBAdbcConnectionWrapper::CloseStreams(const char *reason) { + const duckdb::lock_guard guard(stream_mutex); + for (auto *result_wrapper : active_streams) { + if (!result_wrapper) { + continue; } - duckdb_destroy_arrow_options(&arrow_options); - - // Store materialized data - auto mat = static_cast(malloc(sizeof(duckdb_adbc::MaterializedData))); - if (!mat) { - // Allocation failed — release fetched batches and skip materialization - for (auto &batch : batches) { - if (batch.release) { - batch.release(&batch); - } - } + const duckdb::lock_guard fetch_guard(result_wrapper->fetch_mutex); + if (!result_wrapper->stream) { continue; } - mat->current = 0; - mat->count = static_cast(batches.size()); - if (!batches.empty()) { - mat->batches = static_cast(malloc(sizeof(ArrowArray) * batches.size())); - if (!mat->batches) { - // Allocation failed — release fetched batches and skip materialization - for (auto &batch : batches) { - if (batch.release) { - batch.release(&batch); - } - } - free(mat); - continue; - } - for (idx_t i = 0; i < batches.size(); i++) { - mat->batches[i] = batches[i]; - } - } else { - mat->batches = nullptr; + // A reader may still hold the pointer get_last_error returned, so an error is never replaced + if (!result_wrapper->last_error) { + duckdb_adbc::SetStreamError(*result_wrapper, duckdb::ErrorData(duckdb::ExceptionType::TRANSACTION, reason)); } - result_wrapper->materialized = mat; + result_wrapper->stream.reset(); } } @@ -3763,6 +3681,8 @@ static const char *DuckDBErrorTypeToString(duckdb_error_type type) { return "DataCorruption"; case DUCKDB_INVALID_CONFIGURATION: return "InvalidConfiguration"; + case DUCKDB_ERROR_RESOURCE_IN_USE: + return "ResourceInUse"; default: return "Unknown"; } diff --git a/src/duckdb/src/common/adbc/nanoarrow/schema.cpp b/src/duckdb/src/common/adbc/nanoarrow/schema.cpp index 29e164dc0..dd992990b 100644 --- a/src/duckdb/src/common/adbc/nanoarrow/schema.cpp +++ b/src/duckdb/src/common/adbc/nanoarrow/schema.cpp @@ -416,7 +416,7 @@ ArrowErrorCode ArrowSchemaAllocateDictionary(struct ArrowSchema *schema) { return NANOARROW_OK; } -int ArrowSchemaDeepCopy(struct ArrowSchema *schema, struct ArrowSchema *schema_out) { +int ArrowSchemaDeepCopy(const struct ArrowSchema *schema, struct ArrowSchema *schema_out) { int result; result = ArrowSchemaInit(schema_out, NANOARROW_TYPE_NA); if (result != NANOARROW_OK) { diff --git a/src/duckdb/src/common/allocator/allocator.cpp b/src/duckdb/src/common/allocator/allocator.cpp index 20074f990..948b87cd9 100644 --- a/src/duckdb/src/common/allocator/allocator.cpp +++ b/src/duckdb/src/common/allocator/allocator.cpp @@ -9,6 +9,13 @@ #include +#if defined(__linux__) && INTPTR_MAX == INT64_MAX +#include +#include +#include +#include +#endif + #ifdef __GLIBC__ #include #endif @@ -116,6 +123,43 @@ Allocator::Allocator(allocate_function_ptr_t allocate_function_p, free_function_ Allocator::~Allocator() { } +#if defined(__linux__) && INTPTR_MAX == INT64_MAX && defined(MADV_HUGEPAGE) +static idx_t GetHugePageSize() { + auto file = fopen("/sys/kernel/mm/transparent_hugepage/hpage_pmd_size", "r"); + if (!file) { + return 0; + } + int64_t huge_page_size = 0; + char trailing; + auto fields = fscanf(file, "%" SCNd64 " %c", &huge_page_size, &trailing); + fclose(file); + auto page_size = sysconf(_SC_PAGESIZE); + if (fields != 1 || page_size <= 0 || huge_page_size < page_size || (huge_page_size & (huge_page_size - 1)) != 0 || + huge_page_size % page_size != 0) { + return 0; + } + return NumericCast(huge_page_size); +} +#endif + +AllocatedData Allocator::TryAllocateHuge(idx_t size) { + auto result = Allocate(size); +#if defined(__linux__) && INTPTR_MAX == INT64_MAX && defined(MADV_HUGEPAGE) + static constexpr idx_t MIN_ADVISE_SIZE = 8ULL * 1024 * 1024; + if (size >= MIN_ADVISE_SIZE) { + static const idx_t huge_page_size = GetHugePageSize(); + if (huge_page_size != 0 && size / huge_page_size >= 4) { + const auto base = reinterpret_cast(result.get()); + const auto offset = (huge_page_size - base % huge_page_size) % huge_page_size; + const auto length = ((size - offset) / huge_page_size) * huge_page_size; + // Only advise complete huge pages contained in this allocation. + madvise(result.get() + offset, length, MADV_HUGEPAGE); + } + } +#endif + return result; +} + data_ptr_t Allocator::AllocateData(idx_t size) { D_ASSERT(size > 0); if (size >= MAXIMUM_ALLOC_SIZE) { diff --git a/src/duckdb/src/common/allocator/allocator_jemalloc.cpp b/src/duckdb/src/common/allocator/allocator_jemalloc.cpp index 74e25516b..c146cfb41 100644 --- a/src/duckdb/src/common/allocator/allocator_jemalloc.cpp +++ b/src/duckdb/src/common/allocator/allocator_jemalloc.cpp @@ -75,6 +75,23 @@ optional_idx Allocator::DecayDelay() { return NumericCast(DUCKDB_JEMALLOC_DECAY); } +optional_idx Allocator::GetMappedMemory() const { + if (allocate_function != DefaultAllocate || free_function != DefaultFree || + reallocate_function != DefaultReallocate) { + return optional_idx(); + } + uint64_t epoch = 1; + if (duckdb_je_mallctl("epoch", nullptr, nullptr, &epoch, sizeof(epoch)) != 0) { + return optional_idx(); + } + size_t mapped = 0; + size_t mapped_size = sizeof(mapped); + if (duckdb_je_mallctl("stats.mapped", &mapped, &mapped_size, nullptr, 0) != 0) { + return optional_idx(); + } + return optional_idx(mapped); +} + bool Allocator::SupportsFlush() { return true; } diff --git a/src/duckdb/src/common/allocator/allocator_standard.cpp b/src/duckdb/src/common/allocator/allocator_standard.cpp index da6a959c8..d74816ed1 100644 --- a/src/duckdb/src/common/allocator/allocator_standard.cpp +++ b/src/duckdb/src/common/allocator/allocator_standard.cpp @@ -37,6 +37,10 @@ optional_idx Allocator::DecayDelay() { return optional_idx(); } +optional_idx Allocator::GetMappedMemory() const { + return optional_idx(); +} + bool Allocator::SupportsFlush() { #if defined(__GLIBC__) return true; diff --git a/src/duckdb/src/common/arrow/arrow_appender.cpp b/src/duckdb/src/common/arrow/arrow_appender.cpp index 296178990..33ca0a92b 100644 --- a/src/duckdb/src/common/arrow/arrow_appender.cpp +++ b/src/duckdb/src/common/arrow/arrow_appender.cpp @@ -48,6 +48,14 @@ idx_t ArrowAppender::RowCount() const { return row_count; } +idx_t ArrowAppender::ByteSize() const { + idx_t bytes = 0; + for (auto &data : root_data) { + bytes += data->ByteSize(); + } + return bytes; +} + void ArrowAppender::ReleaseArray(ArrowArray *array) { if (!array || !array->release) { return; @@ -88,7 +96,16 @@ ArrowArray *ArrowAppender::FinalizeChild(const LogicalType &type, unique_ptrbuffers[0] = append_data.GetValidityBuffer().data(); if (append_data.finalize) { - append_data.finalize(append_data, type, result.get()); + // An extension type's appender tree was initialized (and appended) with the extension's INTERNAL + // (storage) type — see InitializeChild — so it must also be finalized with that type: a nested + // finalizer (struct/list/map) sizes its child walk from the type it is given, and the LOGICAL + // type's shape need not match the storage-built child_data (e.g. a canonical Arrow extension over + // struct storage whose logical type has a different child count). + if (append_data.extension_data) { + append_data.finalize(append_data, append_data.extension_data->GetInternalType(), result.get()); + } else { + append_data.finalize(append_data, type, result.get()); + } } append_data.array = std::move(result); diff --git a/src/duckdb/src/common/arrow/arrow_format.cpp b/src/duckdb/src/common/arrow/arrow_format.cpp new file mode 100644 index 000000000..e7afb634b --- /dev/null +++ b/src/duckdb/src/common/arrow/arrow_format.cpp @@ -0,0 +1,225 @@ +#include "duckdb/common/arrow/arrow_format.hpp" + +#include "duckdb/common/arrow/arrow_appender.hpp" +#include "duckdb/common/arrow/arrow_converter.hpp" +#include "duckdb/common/deque.hpp" +#include "duckdb/common/exception.hpp" +#include "duckdb/common/helper.hpp" +#include "duckdb/common/numeric_utils.hpp" +#include "duckdb/common/types/data_chunk.hpp" +#include "duckdb/function/table/arrow/arrow_duck_schema.hpp" + +namespace duckdb { + +//===--------------------------------------------------------------------===// +// Shared exports +//===--------------------------------------------------------------------===// +namespace { + +//! Also releases a partly built export, whose n_children counts only the children attached so far +void ReleaseSharedArray(ArrowArray *array) { + if (!array || !array->release) { + return; + } + array->release = nullptr; + for (int64_t i = 0; i < array->n_children; i++) { + auto child = array->children[i]; + // A child without a release was moved out and is released by whoever took it + if (child->release) { + child->release(child); + } + delete child; + } + delete[] array->children; + if (array->dictionary) { + if (array->dictionary->release) { + array->dictionary->release(array->dictionary); + } + delete array->dictionary; + } + delete static_cast(array->private_data); + array->children = nullptr; + array->dictionary = nullptr; + array->private_data = nullptr; +} + +ArrowArray *NewSharedNode() { + auto node = make_uniq(); + node->release = ReleaseSharedArray; + return node.release(); +} + +//! The target's release is installed before the call and each allocation is attached as soon as it is made, +//! so a throw leaves nothing that release cannot free +void FillSharedArray(ArrowArray &target, const ArrowArray &source, const ArrowArrayOwner &owner) { + target.private_data = make_uniq(owner).release(); + target.length = source.length; + target.null_count = source.null_count; + target.offset = source.offset; + target.n_buffers = source.n_buffers; + target.buffers = source.buffers; + if (source.n_children > 0) { + target.children = make_unsafe_uniq_array(NumericCast(source.n_children)).release(); + for (int64_t i = 0; i < source.n_children; i++) { + target.children[i] = NewSharedNode(); + target.n_children = i + 1; + FillSharedArray(*target.children[i], *source.children[i], owner); + } + } + if (source.dictionary) { + target.dictionary = NewSharedNode(); + FillSharedArray(*target.dictionary, *source.dictionary, owner); + } +} + +} // namespace + +ArrowFormatGlobalState::ArrowFormatGlobalState(const ResultFormatContext &context) + : types(context.types), properties(context.client_properties) { + if (!properties.client_context) { + throw InternalException("The Arrow format needs the client context of the query that submitted it"); + } + extension_types = ArrowTypeExtensionData::GetExtensionTypes(*properties.client_context, types); + ArrowConverter::ToArrowSchema(&schema.arrow_schema, types, IdentifiersToStrings(context.names), properties); +} + +ArrowFormatGlobalState::~ArrowFormatGlobalState() { +} + +//===--------------------------------------------------------------------===// +// ArrowFormat +//===--------------------------------------------------------------------===// +namespace { + +class ArrowUnit : public ResultUnit { +public: + ArrowUnit(idx_t row_count, idx_t byte_size, unique_ptr array_p) + : ResultUnit(row_count, byte_size), array(std::move(array_p)) { + } + +public: + unique_ptr array; +}; + +class ArrowFormatLocalState : public ResultFormatLocalState { +public: + //! The array being built. Created at the first row that goes into it + unique_ptr appender; + //! Arrays that reached the batch size and have not been handed over yet + deque> sealed; +}; + +unique_ptr SealAppender(ArrowFormatLocalState &lstate) { + // Finalize hands the buffers to the array, so the size has to be read before it + auto rows = lstate.appender->RowCount(); + auto bytes = lstate.appender->ByteSize(); + // Allocated before Finalize, so nothing can throw between the array's creation and its adoption + auto array = make_uniq(); + array->arrow_array = lstate.appender->Finalize(); + lstate.appender.reset(); + return make_uniq(rows, bytes, std::move(array)); +} + +} // namespace + +ArrowFormat::ArrowFormat(idx_t batch_size_p) : batch_size(batch_size_p) { + if (batch_size == 0) { + throw InvalidInputException("The Arrow format needs a batch size of at least one row"); + } +} + +const char *ArrowFormat::Name() const { + return NAME; +} + +unique_ptr ArrowFormat::InitGlobal(const ResultFormatContext &context) { + return make_uniq(context); +} + +unique_ptr ArrowFormat::InitLocal(ResultFormatGlobalState &gstate) { + return make_uniq(); +} + +void ArrowFormat::AppendToUnit(ResultFormatGlobalState &gstate_p, ResultFormatLocalState &lstate_p, DataChunk &chunk) { + auto &gstate = gstate_p.Cast(); + auto &lstate = lstate_p.Cast(); + auto count = chunk.size(); + idx_t processed = 0; + while (processed < count) { + if (!lstate.appender) { + auto capacity = MinValue(batch_size, count - processed); + lstate.appender = + make_uniq(gstate.Types(), capacity, gstate.Properties(), gstate.ExtensionTypes()); + } + auto to_append = MinValue(batch_size - lstate.appender->RowCount(), count - processed); + lstate.appender->Append(chunk, processed, processed + to_append, count); + processed += to_append; + if (lstate.appender->RowCount() >= batch_size) { + lstate.sealed.push_back(SealAppender(lstate)); + } + } +} + +bool ArrowFormat::IsUnitFinished(ResultFormatLocalState &lstate) { + return !lstate.Cast().sealed.empty(); +} + +unique_ptr ArrowFormat::FinishUnit(ResultFormatGlobalState &gstate, ResultFormatLocalState &lstate_p) { + auto &lstate = lstate_p.Cast(); + if (!lstate.sealed.empty()) { + auto unit = std::move(lstate.sealed.front()); + lstate.sealed.pop_front(); + return std::move(unit); + } + if (!lstate.appender || lstate.appender->RowCount() == 0) { + return nullptr; + } + return SealAppender(lstate); +} + +unique_ptr ArrowFormat::CreateCollection(ClientContext &context, + ResultFormatGlobalState &gstate, + const ResultFormatContext &format_context) { + return make_uniq(*this, gstate); +} + +unique_ptr ArrowFormat::UnpackUnit(unique_ptr unit) { + if (!unit) { + return nullptr; + } + return std::move(unit->Cast().array); +} + +unique_ptr ArrowFormat::ShareArray(const ArrowArrayOwner &owner) { + D_ASSERT(owner && owner->arrow_array.release); + auto result = make_uniq(); + result->arrow_array = ArrowArray {}; + result->arrow_array.release = ReleaseSharedArray; + FillSharedArray(result->arrow_array, owner->arrow_array, owner); + return result; +} + +idx_t ArrowFormat::ChangedRows(const ArrowArray &array) { + if (array.length == 0) { + return 0; + } + D_ASSERT(array.length == 1 && array.n_children == 1); + auto &count = *array.children[0]; + auto values = static_cast(count.buffers[1]); + return NumericCast(values[array.offset + count.offset]); +} + +//===--------------------------------------------------------------------===// +// ArrowRetainedCollection +//===--------------------------------------------------------------------===// +unique_ptr ArrowRetainedCollection::Fetch() { + auto &arrays = Get(); + if (fetch_index >= arrays.size()) { + return nullptr; + } + auto array = ArrowFormat::ShareArray(arrays[fetch_index]); + fetch_index++; + return array; +} + +} // namespace duckdb diff --git a/src/duckdb/src/common/arrow/arrow_merge_event.cpp b/src/duckdb/src/common/arrow/arrow_merge_event.cpp deleted file mode 100644 index 900a5cc8e..000000000 --- a/src/duckdb/src/common/arrow/arrow_merge_event.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#include "duckdb/common/arrow/arrow_merge_event.hpp" -#include "duckdb/common/arrow/arrow_util.hpp" -#include "duckdb/storage/storage_info.hpp" -#include "duckdb/function/table/arrow/arrow_duck_schema.hpp" -#include "duckdb/main/client_context.hpp" - -namespace duckdb { - -//===--------------------------------------------------------------------===// -// Arrow Batch Task -//===--------------------------------------------------------------------===// - -ArrowBatchTask::ArrowBatchTask(ArrowQueryResult &result, vector record_batch_indices, Executor &executor, - shared_ptr event_p, BatchCollectionChunkScanState scan_state, idx_t batch_size) - : ExecutorTask(executor, std::move(event_p)), result(result), record_batch_indices(std::move(record_batch_indices)), - batch_size(batch_size), scan_state(std::move(scan_state)) { -} - -void ArrowBatchTask::ProduceRecordBatches() { - auto &arrays = result.Arrays(); - auto arrow_options = executor.context.GetClientProperties(); - for (auto &index : record_batch_indices) { - auto &array = arrays[index]; - D_ASSERT(array); - const idx_t count = ArrowUtil::FetchChunk( - scan_state, arrow_options, batch_size, &array->arrow_array, - ArrowTypeExtensionData::GetExtensionTypes(event->GetClientContext(), scan_state.Types())); - (void)count; - D_ASSERT(count != 0); - } -} - -TaskExecutionResult ArrowBatchTask::ExecuteTask(TaskExecutionMode mode) { - ProduceRecordBatches(); - event->FinishTask(); - return TaskExecutionResult::TASK_FINISHED; -} - -//===--------------------------------------------------------------------===// -// Arrow Merge Event -//===--------------------------------------------------------------------===// - -ArrowMergeEvent::ArrowMergeEvent(ArrowQueryResult &result, BatchedDataCollection &batches, Pipeline &pipeline_p) - : BasePipelineEvent(pipeline_p), result(result), batches(batches) { - record_batch_size = result.BatchSize(); -} - -namespace { - -struct BatchesForTask { - idx_t tuple_count; - BatchedChunkIteratorRange batches; -}; - -struct BatchesToTaskTransformer { -public: - explicit BatchesToTaskTransformer(BatchedDataCollection &batches) : batches(batches), batch_index(0) { - batch_count = batches.BatchCount(); - } - idx_t GetIndex() const { - return batch_index; - } - bool TryGetNextBatchSize(idx_t &tuple_count) { - if (batch_index >= batch_count) { - return false; - } - auto internal_index = batches.IndexToBatchIndex(batch_index++); - auto tuples_in_batch = batches.BatchSize(internal_index); - tuple_count = tuples_in_batch; - return true; - } - -public: - BatchedDataCollection &batches; - idx_t batch_index; - idx_t batch_count; -}; - -} // namespace - -void ArrowMergeEvent::Schedule() { - vector> tasks; - - BatchesToTaskTransformer transformer(batches); - vector task_data; - bool finished = false; - // First we convert our list of batches into units of Storage::ROW_GROUP_SIZE tuples each - while (!finished) { - idx_t tuples_for_task = 0; - idx_t start_index = transformer.GetIndex(); - idx_t end_index = start_index; - while (tuples_for_task < DEFAULT_ROW_GROUP_SIZE) { - idx_t batch_size; - if (!transformer.TryGetNextBatchSize(batch_size)) { - finished = true; - break; - } - end_index++; - tuples_for_task += batch_size; - } - if (start_index == end_index) { - break; - } - BatchesForTask batches_for_task; - batches_for_task.tuple_count = tuples_for_task; - batches_for_task.batches = batches.BatchRange(start_index, end_index); - task_data.push_back(batches_for_task); - } - - // Now we produce tasks from these units - // Every task is given a scan_state created from the range of batches - // and a vector of indices indicating the arrays (record batches) they should populate - idx_t record_batch_index = 0; - for (auto &data : task_data) { - const auto tuples = data.tuple_count; - - auto full_batches = tuples / record_batch_size; - auto remainder = tuples % record_batch_size; - auto total_batches = full_batches + !!remainder; - - vector record_batch_indices(total_batches); - for (idx_t i = 0; i < total_batches; i++) { - record_batch_indices[i] = record_batch_index++; - } - - BatchCollectionChunkScanState scan_state(batches, data.batches, pipeline->executor.context); - tasks.push_back(make_uniq(result, std::move(record_batch_indices), pipeline->executor, - shared_from_this(), std::move(scan_state), record_batch_size)); - } - - // Allocate the list of record batches inside the query result - { - vector> arrays; - arrays.resize(record_batch_index); - for (idx_t i = 0; i < record_batch_index; i++) { - arrays[i] = make_uniq(); - } - result.SetArrowData(std::move(arrays)); - } - D_ASSERT(!tasks.empty()); - SetTasks(std::move(tasks)); -} - -} // namespace duckdb diff --git a/src/duckdb/src/common/arrow/arrow_query_result.cpp b/src/duckdb/src/common/arrow/arrow_query_result.cpp deleted file mode 100644 index 40d9b09d5..000000000 --- a/src/duckdb/src/common/arrow/arrow_query_result.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "duckdb/common/arrow/arrow_query_result.hpp" -#include "duckdb/common/to_string.hpp" -#include "duckdb/main/client_context.hpp" -#include "duckdb/common/box_renderer.hpp" -#include "duckdb/common/arrow/arrow_converter.hpp" - -namespace duckdb { - -ArrowQueryResult::ArrowQueryResult(StatementType statement_type, StatementProperties properties, - vector names_p, vector types_p, - ClientProperties client_properties, idx_t batch_size) - : QueryResult(QueryResultType::ARROW_RESULT, statement_type, std::move(properties), std::move(types_p), - std::move(names_p), std::move(client_properties)), - batch_size(batch_size) { -} - -ArrowQueryResult::ArrowQueryResult(ErrorData error) - : QueryResult(QueryResultType::ARROW_RESULT, std::move(error)), batch_size(0) { -} - -unique_ptr ArrowQueryResult::FetchInternal() { - throw NotImplementedException("Can't 'FetchRaw' from ArrowQueryResult"); -} - -string ArrowQueryResult::ToString() { - // FIXME: can't throw an exception here as it's used for verification - return ""; -} - -vector> ArrowQueryResult::ConsumeArrays() { - if (HasError()) { - throw InvalidInputException("Attempting to fetch ArrowArrays from an unsuccessful query result: Error %s", - GetError()); - } - return std::move(arrays); -} - -vector> &ArrowQueryResult::Arrays() { - if (HasError()) { - throw InvalidInputException("Attempting to fetch ArrowArrays from an unsuccessful query result: Error %s", - GetError()); - } - return arrays; -} - -void ArrowQueryResult::SetArrowData(vector> arrays) { - D_ASSERT(this->arrays.empty()); - this->arrays = std::move(arrays); -} - -idx_t ArrowQueryResult::BatchSize() const { - return batch_size; -} - -} // namespace duckdb diff --git a/src/duckdb/src/common/arrow/arrow_type_extension.cpp b/src/duckdb/src/common/arrow/arrow_type_extension.cpp index e7baa48ec..a41f8dfec 100644 --- a/src/duckdb/src/common/arrow/arrow_type_extension.cpp +++ b/src/duckdb/src/common/arrow/arrow_type_extension.cpp @@ -2,12 +2,15 @@ #include "duckdb/common/types/hash.hpp" #include "duckdb/main/config.hpp" #include "duckdb/function/table/arrow/arrow_duck_schema.hpp" +#include "duckdb/function/table/arrow/arrow_type_info.hpp" #include "duckdb/main/client_context.hpp" #include "duckdb/common/arrow/arrow_converter.hpp" #include "duckdb/common/arrow/schema_metadata.hpp" #include "duckdb/common/types/vector.hpp" #include "duckdb/common/types/geometry_crs.hpp" #include "duckdb/common/json_document.hpp" +#include "duckdb/common/vector/struct_vector.hpp" +#include "duckdb/common/types/variant/parquet_variant_iterator.hpp" namespace duckdb { @@ -523,6 +526,220 @@ struct ArrowGeometry { } }; +struct ArrowVariant { + //! VARIANT travels as the canonical `arrow.parquet.variant` extension: the Variant spec's binary + //! encoding in a struct storage type. The values convert through + //! ParquetVariantConversion's encode/decode directly — never through the binder, since a conversion + //! can run without a valid transaction (e.g. duckdb_result_arrow_array over a materialized result). + + static LogicalType StorageType() { + child_list_t children; + children.emplace_back("metadata", LogicalType::BLOB); + children.emplace_back("value", LogicalType::BLOB); + return LogicalType::STRUCT(std::move(children)); + } + + //! Declares the storage schema: struct, tagged with the canonical + //! extension name. + static void PopulateSchema(DuckDBArrowSchemaHolder &root_holder, ArrowSchema &schema, const LogicalType &type, + ClientContext &context, const ArrowTypeExtension &extension) { + const auto schema_metadata = ArrowSchemaMetadata::ArrowCanonicalType(extension.GetInfo().GetExtensionName()); + root_holder.metadata_info.emplace_back(schema_metadata.SerializeMetadata()); + schema.metadata = root_holder.metadata_info.back().get(); + + auto release_child = [](ArrowSchema *child) { + child->release = nullptr; + }; + + schema.format = "+s"; + schema.n_children = 2; + root_holder.nested_children.emplace_back(); + root_holder.nested_children.back().resize(2); + root_holder.nested_children_ptr.emplace_back(); + root_holder.nested_children_ptr.back().push_back(&root_holder.nested_children.back()[0]); + root_holder.nested_children_ptr.back().push_back(&root_holder.nested_children.back()[1]); + schema.children = root_holder.nested_children_ptr.back().data(); + + // The appender picks the binary layout from the session settings — declare the same one, or the + // schema and the produced buffers disagree (same rule as SetArrowFormat's BLOB case). + const auto options = context.GetClientProperties(); + const char *binary_format; + if (options.arrow_output_version >= ArrowFormatVersion::V1_4) { + binary_format = "vz"; + } else if (options.arrow_offset_size == ArrowOffsetSize::LARGE) { + binary_format = "Z"; + } else { + binary_format = "z"; + } + + const char *child_names[] = {"metadata", "value"}; + for (idx_t i = 0; i < 2; i++) { + auto &child = *schema.children[i]; + child.format = binary_format; + child.name = child_names[i]; + // the spec requires `metadata` non-nullable; `value` stays nullable (it may be absent per row + // once shredding exists) + child.flags = i == 0 ? 0 : ARROW_FLAG_NULLABLE; + child.release = release_child; + } + } + + //! Maps a tagged schema back: the VARIANT logical type, with type info describing the storage struct + //! for the reader's buffer walk. The spec allows the fields in ANY order, in any binary layout + //! (binary / large binary / view), and dictionary- or run-end-encoded — so the fields are resolved by + //! NAME and the children are parsed from the actual schema. The resolved order is recorded on a + //! per-column ArrowTypeExtensionData whose internal type is the storage struct in the schema's OWN + //! field order (the buffer walk is positional), so the conversion can normalize by name. + static unique_ptr GetType(ClientContext &context, const ArrowSchema &schema, + const ArrowSchemaMetadata &schema_metadata) { + if (!schema.format || string(schema.format) != "+s") { + throw InvalidInputException("arrow.parquet.variant column must have a struct storage type, got format '%s'", + schema.format ? schema.format : "(none)"); + } + idx_t metadata_idx = DConstants::INVALID_INDEX; + idx_t value_idx = DConstants::INVALID_INDEX; + for (idx_t i = 0; i < NumericCast(schema.n_children); i++) { + const string name = schema.children[i]->name ? schema.children[i]->name : ""; + if (name == "metadata" && metadata_idx == DConstants::INVALID_INDEX) { + metadata_idx = i; + } else if (name == "value" && value_idx == DConstants::INVALID_INDEX) { + value_idx = i; + } else if (name == "typed_value") { + // TODO: support shredded variants — the value has to be re-assembled from typed_value + // (and, when both are present, merged with the residual value field). + throw NotImplementedException( + "arrow.parquet.variant column with a 'typed_value' field (a shredded variant) is not supported " + "yet"); + } else { + throw InvalidInputException( + "arrow.parquet.variant column has an unexpected or duplicate field '%s' (expected 'metadata' and " + "'value')", + name); + } + } + if (metadata_idx == DConstants::INVALID_INDEX || value_idx == DConstants::INVALID_INDEX) { + throw InvalidInputException( + "arrow.parquet.variant column must have a 'metadata' and a 'value' field, got %lld children", + schema.n_children); + } + vector> children; + child_list_t storage_children; + for (idx_t i = 0; i < NumericCast(schema.n_children); i++) { + // GetArrowLogicalType, not GetTypeFromSchema: a dictionary-encoded child carries its value + // type in schema.dictionary, which only this entry point resolves. + children.push_back(ArrowType::GetArrowLogicalType(context, *schema.children[i])); + // Both fields are binary by the spec. Without this check a non-binary field would be + // declared BLOB storage here and then reinterpreted as bytes by the decode. + const auto child_type = children.back()->GetDuckType(true); + if (child_type.id() != LogicalTypeId::BLOB) { + throw InvalidInputException("arrow.parquet.variant field '%s' must be a binary type, got %s", + schema.children[i]->name, child_type.ToString()); + } + storage_children.emplace_back(schema.children[i]->name, LogicalType::BLOB); + } + auto result = make_uniq(LogicalType::VARIANT(), make_uniq(std::move(children))); + result->extension_data = make_shared_ptr( + LogicalType::VARIANT(), LogicalType::STRUCT(std::move(storage_children)), ArrowToDuck, DuckToArrow); + return result; + } + + //! DuckDB -> Arrow: convert VARIANT values into the storage struct via the Parquet Variant encode + //! (the result vector declares no typed_value child, so the result is the unshredded + //! struct). + static void DuckToArrow(ClientContext &, const Vector &source, Vector &result, idx_t count) { + Vector transformed(StorageType(), count); + ParquetVariantConversion::ToParquetVariant(source, count, transformed); + + // The transform encodes SQL NULL as a Variant-null VALUE; Arrow needs the source's top-level validity. + UnifiedVectorFormat source_format; + source.ToUnifiedFormat(source_format); + bool has_nulls = false; + for (idx_t i = 0; i < count; i++) { + if (!source_format.validity.RowIsValid(source_format.sel->get_index(i)) || + FlatVector::IsNull(transformed, i)) { + FlatVector::SetNull(result, i, true); + has_nulls = true; + } + } + + // Move the encoded data over child-wise so `result` (the extension's declared storage type) + // shares the encode's buffers where possible. + auto &result_entries = StructVector::GetEntries(result); + auto &transformed_entries = StructVector::GetEntries(transformed); + if (!has_nulls) { + result_entries[0].Reference(transformed_entries[0]); + result_entries[1].Reference(transformed_entries[1]); + return; + } + // `metadata` is declared non-nullable, as the spec requires — so a NULL row's child slots carry + // the minimal valid encoding (v1 empty-dictionary metadata + a Variant null value) instead of the + // child-level NULLs a strict consumer would reject. Copy into the result's OWN children rather + // than patching buffers shared with the transform's output. + static constexpr const char MINIMAL_METADATA[] = "\x01\x00\x00"; + static constexpr const char VARIANT_NULL_VALUE[] = "\x00"; + transformed_entries[0].Flatten(); + transformed_entries[1].Flatten(); + auto src_metadata = FlatVector::GetData(transformed_entries[0]); + auto src_value = FlatVector::GetData(transformed_entries[1]); + auto &metadata_entry = result_entries[0]; + auto &value_entry = result_entries[1]; + auto dst_metadata = FlatVector::GetDataMutable(metadata_entry); + auto dst_value = FlatVector::GetDataMutable(value_entry); + for (idx_t i = 0; i < count; i++) { + if (FlatVector::IsNull(result, i)) { + dst_metadata[i] = string_t(MINIMAL_METADATA, 3); + dst_value[i] = string_t(VARIANT_NULL_VALUE, 1); + } else { + dst_metadata[i] = StringVector::AddStringOrBlob(metadata_entry, src_metadata[i]); + dst_value[i] = StringVector::AddStringOrBlob(value_entry, src_value[i]); + } + } + FlatVector::ValidityMutable(metadata_entry).SetAllValid(count); + FlatVector::ValidityMutable(value_entry).SetAllValid(count); + } + + //! Arrow -> DuckDB: concatenate each row's metadata and value bytes (the self-delimiting Variant + //! binary form) and decode through the Parquet Variant binary decode. + static void ArrowToDuck(ClientContext &, Vector &source, Vector &result, idx_t count) { + source.Flatten(); + // The storage vector's type records the incoming schema's own field order (see GetType, + // which already established that these are exactly the two fields) - so only their order + // is open here, the spec allowing them either way round. + auto &source_children = StructType::GetChildTypes(source.GetType()); + const idx_t metadata_idx = source_children[0].first == "metadata" ? 0 : 1; + const idx_t value_idx = 1 - metadata_idx; + auto &entries = StructVector::GetEntries(source); + Vector &metadata = entries[metadata_idx]; + Vector &value = entries[value_idx]; + metadata.Flatten(); + value.Flatten(); + + Vector blob(LogicalType::BLOB, count); + auto blob_data = FlatVector::GetDataMutable(blob); + auto metadata_data = FlatVector::GetData(metadata); + auto value_data = FlatVector::GetData(value); + for (idx_t i = 0; i < count; i++) { + if (FlatVector::IsNull(source, i) || FlatVector::IsNull(metadata, i) || FlatVector::IsNull(value, i)) { + // The decode reads the blob's validity and answers a Variant null for an invalid + // row, so the slot itself is never read. + FlatVector::SetNull(blob, i, true); + continue; + } + auto &metadata_bytes = metadata_data[i]; + auto &value_bytes = value_data[i]; + auto total_size = metadata_bytes.GetSize() + value_bytes.GetSize(); + auto target = StringVector::EmptyString(blob, total_size); + auto target_ptr = target.GetDataWriteable(); + memcpy(target_ptr, metadata_bytes.GetData(), metadata_bytes.GetSize()); + memcpy(target_ptr + metadata_bytes.GetSize(), value_bytes.GetData(), value_bytes.GetSize()); + target.Finalize(); + blob_data[i] = target; + } + + ParquetVariantConversion::ConvertBinary(blob, result, count); + } +}; + void ArrowTypeExtensionSet::Initialize(const DBConfig &config) { // Types that are 1:1 config.RegisterArrowExtension({"arrow.uuid", "w:16", make_shared_ptr(LogicalType::UUID)}); @@ -552,5 +769,10 @@ void ArrowTypeExtensionSet::Initialize(const DBConfig &config) { config.RegisterArrowExtension({"DuckDB", "bignum", &ArrowBignum::PopulateSchema, &ArrowBignum::GetType, make_shared_ptr(LogicalType::BIGNUM), nullptr, nullptr}); + + config.RegisterArrowExtension( + {"arrow.parquet.variant", &ArrowVariant::PopulateSchema, &ArrowVariant::GetType, + make_shared_ptr(LogicalType::VARIANT(), ArrowVariant::StorageType(), + ArrowVariant::ArrowToDuck, ArrowVariant::DuckToArrow)}); } } // namespace duckdb diff --git a/src/duckdb/src/common/arrow/arrow_util.cpp b/src/duckdb/src/common/arrow/arrow_util.cpp deleted file mode 100644 index 267479d9e..000000000 --- a/src/duckdb/src/common/arrow/arrow_util.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include - -#include "duckdb/common/arrow/arrow_util.hpp" -#include "duckdb/common/arrow/arrow_appender.hpp" -#include "duckdb/common/types/data_chunk.hpp" -#include "duckdb/function/table/arrow/arrow_duck_schema.hpp" -namespace duckdb { - -bool ArrowUtil::TryFetchChunk(ChunkScanState &scan_state, ClientProperties options, idx_t batch_size, ArrowArray *out, - idx_t &count, ErrorData &error, - unordered_map> extension_type_cast) { - count = 0; - ArrowAppender appender(scan_state.Types(), batch_size, std::move(options), std::move(extension_type_cast)); - const auto remaining_tuples_in_chunk = scan_state.RemainingInChunk(); - if (remaining_tuples_in_chunk) { - // We start by scanning the non-finished current chunk - idx_t cur_consumption = MinValue(remaining_tuples_in_chunk, batch_size); - count += cur_consumption; - auto ¤t_chunk = scan_state.CurrentChunk(); - appender.Append(current_chunk, scan_state.CurrentOffset(), scan_state.CurrentOffset() + cur_consumption, - current_chunk.size()); - scan_state.IncreaseOffset(cur_consumption); - } - while (count < batch_size) { - if (!scan_state.LoadNextChunk(error)) { - if (scan_state.HasError()) { - error = scan_state.GetError(); - } - return false; - } - if (scan_state.ChunkIsEmpty()) { - // The scan was successful, but an empty chunk was returned - break; - } - auto ¤t_chunk = scan_state.CurrentChunk(); - if (scan_state.Finished() || current_chunk.size() == 0) { - break; - } - // The amount we still need to append into this chunk - auto remaining = batch_size - count; - - // The amount remaining, capped by the amount left in the current chunk - auto to_append_to_batch = MinValue(remaining, scan_state.RemainingInChunk()); - appender.Append(current_chunk, 0, to_append_to_batch, current_chunk.size()); - count += to_append_to_batch; - scan_state.IncreaseOffset(to_append_to_batch); - } - if (count > 0) { - *out = appender.Finalize(); - } - return true; -} - -idx_t ArrowUtil::FetchChunk(ChunkScanState &scan_state, ClientProperties options, idx_t chunk_size, ArrowArray *out, - const unordered_map> &extension_type_cast) { - ErrorData error; - idx_t result_count; - if (!TryFetchChunk(scan_state, std::move(options), chunk_size, out, result_count, error, extension_type_cast)) { - error.Throw(); - } - return result_count; -} - -} // namespace duckdb diff --git a/src/duckdb/src/common/arrow/arrow_wrapper.cpp b/src/duckdb/src/common/arrow/arrow_wrapper.cpp index f739e09a3..04a372132 100644 --- a/src/duckdb/src/common/arrow/arrow_wrapper.cpp +++ b/src/duckdb/src/common/arrow/arrow_wrapper.cpp @@ -1,18 +1,8 @@ #include "duckdb/common/arrow/arrow_wrapper.hpp" -#include "duckdb/common/arrow/arrow_util.hpp" -#include "duckdb/common/arrow/arrow_converter.hpp" #include "duckdb/common/assert.hpp" #include "duckdb/common/exception.hpp" -#include "duckdb/main/stream_query_result.hpp" - -#include "duckdb/common/arrow/result_arrow_wrapper.hpp" -#include "duckdb/common/arrow/arrow_appender.hpp" -#include "duckdb/main/query_result.hpp" -#include "duckdb/main/chunk_scan_state/query_result.hpp" -#include "duckdb/function/table/arrow/arrow_duck_schema.hpp" - namespace duckdb { ArrowSchemaWrapper::~ArrowSchemaWrapper() { @@ -64,128 +54,4 @@ const char *ArrowArrayStreamWrapper::GetError() { // LCOV_EXCL_START return arrow_array_stream.get_last_error(&arrow_array_stream); } // LCOV_EXCL_STOP -int ResultArrowArrayStreamWrapper::MyStreamGetSchema(struct ArrowArrayStream *stream, struct ArrowSchema *out) { - if (!stream->release) { - return -1; - } - out->release = nullptr; - auto my_stream = reinterpret_cast(stream->private_data); - if (!my_stream->column_types.empty()) { - try { - ArrowConverter::ToArrowSchema(out, my_stream->column_types, my_stream->column_names, - my_stream->result->client_properties); - } catch (std::runtime_error &e) { - my_stream->last_error = ErrorData(e); - return -1; - } - return 0; - } - - auto &result = *my_stream->result; - if (result.HasError()) { - my_stream->last_error = result.GetErrorObject(); - return -1; - } - if (result.GetResultType() == QueryResultType::STREAM_RESULT) { - auto &stream_result = result.Cast(); - if (!stream_result.IsOpen()) { - my_stream->last_error = ErrorData("Query Stream is closed"); - return -1; - } - } - if (my_stream->column_types.empty()) { - my_stream->column_types = result.GetTypes(); - my_stream->column_names = IdentifiersToStrings(result.GetNames()); - } - try { - ArrowConverter::ToArrowSchema(out, my_stream->column_types, my_stream->column_names, - my_stream->result->client_properties); - } catch (std::runtime_error &e) { - my_stream->last_error = ErrorData(e); - return -1; - } - return 0; -} - -int ResultArrowArrayStreamWrapper::MyStreamGetNext(struct ArrowArrayStream *stream, struct ArrowArray *out) { - if (!stream->release) { - return -1; - } - auto my_stream = reinterpret_cast(stream->private_data); - auto &result = *my_stream->result; - auto &scan_state = *my_stream->scan_state; - if (result.HasError()) { - my_stream->last_error = result.GetErrorObject(); - return -1; - } - if (result.GetResultType() == QueryResultType::STREAM_RESULT) { - auto &stream_result = result.Cast(); - if (!stream_result.IsOpen()) { - // Nothing to output - out->release = nullptr; - return 0; - } - } - if (my_stream->column_types.empty()) { - my_stream->column_types = result.GetTypes(); - my_stream->column_names = IdentifiersToStrings(result.GetNames()); - } - - try { - idx_t result_count; - ErrorData error; - if (!ArrowUtil::TryFetchChunk(scan_state, result.client_properties, my_stream->batch_size, out, result_count, - error, my_stream->extension_types)) { - D_ASSERT(error.HasError()); - my_stream->last_error = error; - return -1; - } - if (result_count == 0) { - // Nothing to output - out->release = nullptr; - } - } catch (std::exception &e) { - my_stream->last_error = ErrorData(e); - return -1; - } - - return 0; -} - -void ResultArrowArrayStreamWrapper::MyStreamRelease(struct ArrowArrayStream *stream) { - if (!stream || !stream->release) { - return; - } - stream->release = nullptr; - delete reinterpret_cast(stream->private_data); -} - -const char *ResultArrowArrayStreamWrapper::MyStreamGetLastError(struct ArrowArrayStream *stream) { - if (!stream->release) { - return "stream was released"; - } - D_ASSERT(stream->private_data); - auto my_stream = reinterpret_cast(stream->private_data); - return my_stream->last_error.Message().c_str(); -} - -ResultArrowArrayStreamWrapper::ResultArrowArrayStreamWrapper(unique_ptr result_p, idx_t batch_size_p) - : result(std::move(result_p)), scan_state(make_uniq(*result)) { - //! We first initialize the private data of the stream - stream.private_data = this; - //! Ceil Approx_Batch_Size/STANDARD_VECTOR_SIZE - if (batch_size_p == 0) { - throw std::runtime_error("Approximate Batch Size of Record Batch MUST be higher than 0"); - } - batch_size = batch_size_p; - //! We initialize the stream functions - stream.get_schema = ResultArrowArrayStreamWrapper::MyStreamGetSchema; - stream.get_next = ResultArrowArrayStreamWrapper::MyStreamGetNext; - stream.release = ResultArrowArrayStreamWrapper::MyStreamRelease; - stream.get_last_error = ResultArrowArrayStreamWrapper::MyStreamGetLastError; - - extension_types = - ArrowTypeExtensionData::GetExtensionTypes(*result->client_properties.client_context, result->GetTypes()); -} - } // namespace duckdb diff --git a/src/duckdb/src/common/arrow/physical_arrow_batch_collector.cpp b/src/duckdb/src/common/arrow/physical_arrow_batch_collector.cpp deleted file mode 100644 index 11406c540..000000000 --- a/src/duckdb/src/common/arrow/physical_arrow_batch_collector.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "duckdb/common/arrow/physical_arrow_batch_collector.hpp" -#include "duckdb/common/types/batched_data_collection.hpp" -#include "duckdb/common/arrow/arrow_query_result.hpp" -#include "duckdb/common/arrow/arrow_merge_event.hpp" -#include "duckdb/main/client_context.hpp" -#include "duckdb/common/arrow/physical_arrow_collector.hpp" - -namespace duckdb { - -unique_ptr PhysicalArrowBatchCollector::GetGlobalSinkState(ClientContext &context) const { - return make_uniq(context, *this); -} - -SinkFinalizeType PhysicalArrowBatchCollector::Finalize(Pipeline &pipeline, Event &event, ClientContext &context, - OperatorSinkFinalizeInput &input) const { - auto &gstate = input.global_state.Cast(); - - auto total_tuple_count = gstate.data.Count(); - if (total_tuple_count == 0) { - // Create the result containing a single empty result conversion - gstate.result = make_uniq(statement_type, properties, names, types, - context.GetClientProperties(), record_batch_size); - return SinkFinalizeType::READY; - } - - // Already create the final query result - gstate.result = make_uniq(statement_type, properties, names, types, context.GetClientProperties(), - record_batch_size); - // Spawn an event that will populate the conversion result - auto &arrow_result = gstate.result->Cast(); - auto new_event = make_shared_ptr(arrow_result, gstate.data, pipeline); - event.InsertEvent(std::move(new_event)); - - return SinkFinalizeType::READY; -} - -} // namespace duckdb diff --git a/src/duckdb/src/common/arrow/physical_arrow_collector.cpp b/src/duckdb/src/common/arrow/physical_arrow_collector.cpp deleted file mode 100644 index 215eff1d0..000000000 --- a/src/duckdb/src/common/arrow/physical_arrow_collector.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include "duckdb/common/types/column/column_data_collection.hpp" -#include "duckdb/common/arrow/physical_arrow_collector.hpp" -#include "duckdb/common/arrow/physical_arrow_batch_collector.hpp" -#include "duckdb/common/arrow/arrow_query_result.hpp" -#include "duckdb/common/assert.hpp" -#include "duckdb/main/prepared_statement_data.hpp" -#include "duckdb/execution/physical_plan_generator.hpp" -#include "duckdb/main/client_context.hpp" -#include "duckdb/common/arrow/arrow_wrapper.hpp" -#include "duckdb/function/table/arrow/arrow_duck_schema.hpp" - -namespace duckdb { - -unique_ptr PhysicalArrowCollector::Create(ClientContext &context, PreparedStatementData &data, - idx_t batch_size) { - auto &physical_plan = *data.physical_plan; - auto &root = physical_plan.Root(); - - if (!PhysicalPlanGenerator::PreserveInsertionOrder(context, root)) { - // Not an order-preserving plan: use the parallel materialized collector. - return make_uniq(physical_plan, data, true, batch_size); - } - - if (!PhysicalPlanGenerator::UseBatchIndex(context, root)) { - // Order-preserving plan, and we cannot use the batch index: use single-threaded result collector. - return make_uniq(physical_plan, data, false, batch_size); - } - - // Order-preserving plan, and we can use the batch index: use a batch collector. - return make_uniq(physical_plan, data, batch_size); -} - -SinkResultType PhysicalArrowCollector::Sink(ExecutionContext &context, DataChunk &chunk, - OperatorSinkInput &input) const { - auto &lstate = input.local_state.Cast(); - // Append to the appender, up to chunk size - - auto count = chunk.size(); - auto &appender = lstate.appender; - D_ASSERT(count != 0); - - idx_t processed = 0; - do { - if (!appender) { - // Create the appender if we haven't started this chunk yet - auto properties = context.client.GetClientProperties(); - D_ASSERT(processed < count); - auto initial_capacity = MinValue(record_batch_size, count - processed); - appender = make_uniq(types, initial_capacity, properties, - ArrowTypeExtensionData::GetExtensionTypes(context.client, types)); - } - - // Figure out how much we can still append to this chunk - auto row_count = appender->RowCount(); - D_ASSERT(record_batch_size > row_count); - auto to_append = MinValue(record_batch_size - row_count, count - processed); - - // Append and check if the chunk is finished - appender->Append(chunk, processed, processed + to_append, count); - processed += to_append; - row_count = appender->RowCount(); - if (row_count >= record_batch_size) { - lstate.FinishArray(); - } - } while (processed < count); - return SinkResultType::NEED_MORE_INPUT; -} - -SinkCombineResultType PhysicalArrowCollector::Combine(ExecutionContext &context, - OperatorSinkCombineInput &input) const { - auto &gstate = input.global_state.Cast(); - auto &lstate = input.local_state.Cast(); - auto &last_appender = lstate.appender; - auto &arrays = lstate.finished_arrays; - if (arrays.empty() && !last_appender) { - // Nothing to do - return SinkCombineResultType::FINISHED; - } - if (last_appender) { - // FIXME: we could set these aside and merge them in a finalize event in an effort to create more balanced - // chunks out of these remnants - lstate.FinishArray(); - } - // Collect all the finished arrays - lock_guard l(gstate.glock); - // Move the arrays from our local state into the global state - gstate.chunks.insert(gstate.chunks.end(), std::make_move_iterator(arrays.begin()), - std::make_move_iterator(arrays.end())); - arrays.clear(); - gstate.tuple_count += lstate.tuple_count; - return SinkCombineResultType::FINISHED; -} - -unique_ptr PhysicalArrowCollector::GetResult(GlobalSinkState &state_p) const { - auto &gstate = state_p.Cast(); - return std::move(gstate.result); -} - -unique_ptr PhysicalArrowCollector::GetGlobalSinkState(ClientContext &context) const { - return make_uniq(); -} - -unique_ptr PhysicalArrowCollector::GetLocalSinkState(ExecutionContext &context) const { - return make_uniq(); -} - -SinkFinalizeType PhysicalArrowCollector::Finalize(Pipeline &pipeline, Event &event, ClientContext &context, - OperatorSinkFinalizeInput &input) const { - auto &gstate = input.global_state.Cast(); - - if (gstate.chunks.empty()) { - if (gstate.tuple_count != 0) { - throw InternalException( - "PhysicalArrowCollector Finalize contains no chunks, but tuple_count is non-zero (%d)", - gstate.tuple_count); - } - gstate.result = make_uniq(statement_type, properties, names, types, - context.GetClientProperties(), record_batch_size); - return SinkFinalizeType::READY; - } - - gstate.result = make_uniq(statement_type, properties, names, types, context.GetClientProperties(), - record_batch_size); - auto &arrow_result = gstate.result->Cast(); - arrow_result.SetArrowData(std::move(gstate.chunks)); - - return SinkFinalizeType::READY; -} - -bool PhysicalArrowCollector::ParallelSink() const { - return parallel; -} - -bool PhysicalArrowCollector::SinkOrderDependent() const { - return true; -} - -} // namespace duckdb diff --git a/src/duckdb/src/common/bind_helpers.cpp b/src/duckdb/src/common/bind_helpers.cpp index c40a1c4af..74bfde8da 100644 --- a/src/duckdb/src/common/bind_helpers.cpp +++ b/src/duckdb/src/common/bind_helpers.cpp @@ -9,6 +9,7 @@ #include "duckdb/planner/binder.hpp" #include "duckdb/planner/bound_result_modifier.hpp" #include "duckdb/planner/expression_iterator.hpp" +#include "duckdb/planner/expression_binder/relation_binder.hpp" #include "duckdb/planner/expression/bound_reference_expression.hpp" #include "duckdb/main/config.hpp" @@ -134,6 +135,21 @@ vector ParseColumnsOrdered(const Value &value, const vector & return ParseColumnsOrdered(children, names, option_name); } +vector BindOrderByNodes(Binder &binder, TableIndex table_index, const Identifier &alias, + const vector &names, const vector &types, + vector &orders) { + auto child_binder = Binder::CreateBinder(binder.context, &binder); + child_binder->bind_context.AddGenericBinding(table_index, alias, names, types); + RelationBinder expr_binder(*child_binder, binder.context, "ORDER_BY"); + vector bound_orders; + for (auto &order : orders) { + auto expression = expr_binder.Bind(order.expression); + ExpressionBinder::PushCollation(binder.context, expression, expression->GetReturnType()); + bound_orders.emplace_back(order.type, order.null_order, std::move(expression)); + } + return bound_orders; +} + vector ParseOrderByColumns(Binder &binder, const vector &set, const BoundStatement &bound_statement, const Identifier &option_name) { // Parse @@ -142,21 +158,16 @@ vector ParseOrderByColumns(Binder &binder, const vector order_by_strings.push_back(value.ToString()); } const auto order_by_clause = StringUtil::Join(order_by_strings, ", "); - auto parsed_orders = Parser::ParseOrderList(order_by_clause); + auto parsed_orders = Parser(binder.context).ParseOrderList(order_by_clause); // Bind auto &config = DBConfig::GetConfig(binder.context); - auto child_binder = Binder::CreateBinder(binder.context, &binder); - auto table_index = binder.GenerateTableIndex(); - child_binder->bind_context.AddGenericBinding(table_index, "__copy_input", bound_statement.names, - bound_statement.types); - ExpressionBinder expr_binder(*child_binder, binder.context); - vector bound_orders; for (auto &parsed_order : parsed_orders) { - const auto order_type = config.ResolveOrder(binder.context, parsed_order.type); - const auto null_order = config.ResolveNullOrder(binder.context, order_type, parsed_order.null_order); - bound_orders.emplace_back(order_type, null_order, expr_binder.Bind(parsed_order.expression)); + parsed_order.type = config.ResolveOrder(binder.context, parsed_order.type); + parsed_order.null_order = config.ResolveNullOrder(binder.context, parsed_order.type, parsed_order.null_order); } + auto bound_orders = BindOrderByNodes(binder, binder.GenerateTableIndex(), "__copy_input", bound_statement.names, + bound_statement.types, parsed_orders); // Convert BoundColumnRefExpression to BoundReferenceExpression vector name_set; diff --git a/src/duckdb/src/common/clustered_aggregate.cpp b/src/duckdb/src/common/clustered_aggregate.cpp index ba8ac0ddb..8225d4acc 100644 --- a/src/duckdb/src/common/clustered_aggregate.cpp +++ b/src/duckdb/src/common/clustered_aggregate.cpp @@ -89,7 +89,10 @@ static inline uint64_t slot_hash(uint64_t gid) { return ((gid * 3217161767) ^ (gid >> ClusteredAggr::HASHTAB_LOG2)) & (ClusteredAggr::HASHTAB_SZ - 1); } -bool ClusteredAggr::TryClustered(const uint64_t *group_ids, sel_t count, sel_t *arena, uint64_t *slots) { +bool ClusteredAggr::TryClustered(const uint64_t *group_ids, sel_t count, sel_t *arena, uint64_t *slots, + GroupRun *runs) { + group_runs = runs; + constexpr sel_t HALF_VEC = STANDARD_VECTOR_SIZE / 2; constexpr idx_t MAX_HOT_PER_CURSOR = MAX_HOTKEYS / 2; idx_t tuples_in_large = 0; @@ -240,9 +243,10 @@ bool ClusteredAggr::TryClustered(const uint64_t *group_ids, sel_t count, sel_t * return (5 * tuples_in_large >= 4 * static_cast(count)); // require >=80% of tuples in long-enough runs } -void ClusteredAggr::SetSingleRun(data_ptr_t state, idx_t count) { +void ClusteredAggr::SetSingleRun(data_ptr_t state_ptr, idx_t count) { + D_ASSERT(group_runs == &single_run); n_group_runs = 1; - group_runs[0].state = state; + group_runs[0].state = state_ptr; group_runs[0].sel = nullptr; group_runs[0].gid = 0; group_runs[0].count = count; @@ -255,7 +259,7 @@ void ClusteredAggr::AdvanceStates(idx_t payload_size) { } } -const sel_t *ClusteredAggr::ClusterIter(const Vector &input, idx_t count) const { +const sel_t *ClusteredAggr::ClusterIter(const Vector &input) const { if (input.GetVectorType() != VectorType::DICTIONARY_VECTOR) { return nullptr; } @@ -263,23 +267,29 @@ const sel_t *ClusteredAggr::ClusterIter(const Vector &input, idx_t count) const if (child.GetVectorType() != VectorType::FLAT_VECTOR) { return nullptr; } - auto &dict_sel = DictionaryVector::SelVector(input); - const sel_t *dict_data = dict_sel.data(); + const sel_t *dict_data = DictionaryVector::SelVector(input).data(); if (dict_data == nullptr) { return nullptr; } + auto run_range = runs(); + if (run_range.size() == 1 && run_range[0].sel == nullptr) { + return dict_data; + } + D_ASSERT(state); if (cached_dict_sel == dict_data) { - return composed_sel_data; + return state->composed_sel_data.get(); } + if (!state->composed_sel_data) { + state->composed_sel_data = make_unsafe_uniq_array_uninitialized(STANDARD_VECTOR_SIZE); + } + auto composed_sel_data = state->composed_sel_data.get(); idx_t pos = 0; - for (idx_t r = 0; r < n_group_runs; r++) { - const auto *run_sel = group_runs[r].sel; - const auto run_count = group_runs[r].count; - for (idx_t k = 0; k < run_count; k++) { - auto idx = run_sel ? run_sel[k] : k; + for (auto &run : run_range) { + for (idx_t k = 0; k < run.count; k++) { + auto idx = run.sel ? run.sel[k] : k; composed_sel_data[pos + k] = dict_data[idx]; } - pos += run_count; + pos += run.count; } cached_dict_sel = dict_data; return composed_sel_data; @@ -290,13 +300,14 @@ void ClusteredAggrState::Initialize() { // Total = 2 * (MAX_HOTKEYS/2 + 1) * HALF_VEC = (MAX_HOTKEYS/2 + 1) * STANDARD_VECTOR_SIZE. arena = make_unsafe_uniq_array_uninitialized((ClusteredAggr::MAX_HOTKEYS / 2 + 1) * STANDARD_VECTOR_SIZE); slots = make_unsafe_uniq_array_uninitialized(2 * ClusteredAggr::HASHTAB_SZ); + group_runs = make_unsafe_uniq_array_uninitialized(ClusteredAggr::MAX_RUNS); std::fill_n(slots.get(), 2 * ClusteredAggr::HASHTAB_SZ, ClusteredAggr::FREE_SLOT); skipped_opportunities = 0; retry_backoff = 1; } bool ClusteredAggrState::TryBuild(ClusteredAggr &clustered, const uint64_t *group_ids, idx_t count) { - if (!arena) { + if (!arena || !group_runs) { return false; } if (skipped_opportunities > 0) { @@ -304,7 +315,7 @@ bool ClusteredAggrState::TryBuild(ClusteredAggr &clustered, const uint64_t *grou return false; } if (count >= ClusteredAggr::SAMPLE_SIZE && - clustered.TryClustered(group_ids, static_cast(count), arena.get(), slots.get())) { + clustered.TryClustered(group_ids, static_cast(count), arena.get(), slots.get(), group_runs.get())) { clustered.state = this; skipped_opportunities = 0; retry_backoff = 1; diff --git a/src/duckdb/src/common/compressed_file_system.cpp b/src/duckdb/src/common/compressed_file_system.cpp index bc25e788c..d45aa3867 100644 --- a/src/duckdb/src/common/compressed_file_system.cpp +++ b/src/duckdb/src/common/compressed_file_system.cpp @@ -5,9 +5,15 @@ namespace duckdb { +//! Compressed data of remote files is read in chunks of this size, so they take few requests +static constexpr idx_t REMOTE_READ_IN_BUF_SIZE = 16777216; + StreamWrapper::~StreamWrapper() { } +void StreamWrapper::FinalizeRead(StreamData &) { +} + void StreamWrapper::AbortWrite() { Close(); } @@ -63,15 +69,22 @@ void CompressedFile::Initialize(QueryContext context, bool write) { this->context = context; this->write = write; stream_data.in_buf_size = compressed_fs.InBufferSize(); + if (!write && FileSystem::IsRemoteFile(child_handle->path)) { + stream_data.in_buf_size = MaxValue( + stream_data.in_buf_size, MinValue(REMOTE_READ_IN_BUF_SIZE, child_handle->GetFileSize())); + } stream_data.out_buf_size = compressed_fs.OutBufferSize(); - stream_data.in_buff = make_unsafe_uniq_array(stream_data.in_buf_size); + auto &allocator = + context.Valid() ? BufferAllocator::Get(*context.GetClientContext()) : Allocator::DefaultAllocator(); + stream_data.in_buff = allocator.Allocate(stream_data.in_buf_size); stream_data.in_buff_start = stream_data.in_buff.get(); stream_data.in_buff_end = stream_data.in_buff.get(); - stream_data.out_buff = make_unsafe_uniq_array(stream_data.out_buf_size); + stream_data.out_buff = allocator.Allocate(stream_data.out_buf_size); stream_data.out_buff_start = stream_data.out_buff.get(); stream_data.out_buff_end = stream_data.out_buff.get(); - current_position = 0; + compressed_bytes_read = 0; + compressed_bytes_consumed.store(0, std::memory_order_relaxed); stream_wrapper = compressed_fs.CreateStream(); stream_wrapper->Initialize(context, *this, write); @@ -79,7 +92,7 @@ void CompressedFile::Initialize(QueryContext context, bool write) { } idx_t CompressedFile::GetProgress() { - return current_position; + return compressed_bytes_consumed.load(std::memory_order_relaxed); } int64_t CompressedFile::ReadData(void *buffer, int64_t remaining) { @@ -105,29 +118,12 @@ int64_t CompressedFile::ReadData(void *buffer, int64_t remaining) { if (!stream_wrapper) { return UnsafeNumericCast(total_read); } - current_position += static_cast(stream_data.in_buff_end - stream_data.in_buff_start); // ran out of buffer: read more data from the child stream stream_data.out_buff_start = stream_data.out_buff.get(); stream_data.out_buff_end = stream_data.out_buff.get(); D_ASSERT(stream_data.in_buff_start <= stream_data.in_buff_end); D_ASSERT(stream_data.in_buff_end <= stream_data.in_buff_start + stream_data.in_buf_size); - // read more input when requested and still data in the input stream - if (stream_data.refresh && (stream_data.in_buff_end == stream_data.in_buff.get() + stream_data.in_buf_size)) { - auto bufrem = stream_data.in_buff_end - stream_data.in_buff_start; - // buffer not empty, move remaining bytes to the beginning - memmove(stream_data.in_buff.get(), stream_data.in_buff_start, UnsafeNumericCast(bufrem)); - stream_data.in_buff_start = stream_data.in_buff.get(); - // refill the rest of input buffer - auto sz = child_handle->Read(context, stream_data.in_buff_start + bufrem, - stream_data.in_buf_size - UnsafeNumericCast(bufrem)); - stream_data.in_buff_end = stream_data.in_buff_start + bufrem + sz; - if (sz <= 0) { - stream_wrapper.reset(); - break; - } - } - // read more input if none available if (stream_data.in_buff_start == stream_data.in_buff_end) { // empty input buffer: refill from the start @@ -135,13 +131,21 @@ int64_t CompressedFile::ReadData(void *buffer, int64_t remaining) { stream_data.in_buff_end = stream_data.in_buff_start; auto sz = child_handle->Read(context, stream_data.in_buff.get(), stream_data.in_buf_size); if (sz <= 0) { + stream_wrapper->FinalizeRead(stream_data); stream_wrapper.reset(); + compressed_bytes_consumed.store(compressed_bytes_read, std::memory_order_relaxed); break; + } else { + stream_data.in_buff_end = stream_data.in_buff_start + sz; + compressed_bytes_read += UnsafeNumericCast(sz); } - stream_data.in_buff_end = stream_data.in_buff_start + sz; } auto finished = stream_wrapper->Read(stream_data); + // the input that remains in the buffer has not been consumed by the decompressor yet + auto unconsumed = static_cast(stream_data.in_buff_end - stream_data.in_buff_start); + compressed_bytes_consumed.store(compressed_bytes_read - MinValue(unconsumed, compressed_bytes_read), + std::memory_order_relaxed); if (finished) { stream_wrapper.reset(); } @@ -166,8 +170,8 @@ void CompressedFile::Clear() { } void CompressedFile::ResetStreamData() { - stream_data.in_buff.reset(); - stream_data.out_buff.reset(); + stream_data.in_buff.Reset(); + stream_data.out_buff.Reset(); stream_data.out_buff_start = nullptr; stream_data.out_buff_end = nullptr; stream_data.in_buff_start = nullptr; diff --git a/src/duckdb/src/common/csv_writer.cpp b/src/duckdb/src/common/csv_writer.cpp index 3d1986da6..56bef0a57 100644 --- a/src/duckdb/src/common/csv_writer.cpp +++ b/src/duckdb/src/common/csv_writer.cpp @@ -18,7 +18,7 @@ CSVWriterState::CSVWriterState() } CSVWriterState::CSVWriterState(ClientContext &context, idx_t flush_size_p) - : flush_size(flush_size_p), stream(make_uniq(Allocator::Get(context), flush_size)) { + : flush_size(flush_size_p), stream(make_uniq(BufferAllocator::Get(context), flush_size)) { } CSVWriterState::CSVWriterState(DatabaseInstance &db, idx_t flush_size_p) @@ -39,10 +39,11 @@ CSVWriterOptions::CSVWriterOptions(const string &delim, const char "e, const requires_quotes['\r'] = true; // quote values containing the comment char so they are not read back as comments (see #17744) if (comment_char != '\0') { - requires_quotes[NumericCast(comment_char)] = true; + requires_quotes[static_cast(comment_char)] = true; } - requires_quotes[NumericCast(delim[0])] = true; - requires_quotes[NumericCast(quote)] = true; + // values containing the (first byte of the) delimiter are quoted + requires_quotes[static_cast(delim[0])] = true; + requires_quotes[static_cast(quote)] = true; if (!write_newline.empty()) { newline = TransformNewLine(write_newline); @@ -371,8 +372,9 @@ void CSVWriter::WriteChunk(DataChunk &input, MemoryStream &writer, CSVReaderOpti D_ASSERT(options.null_str.size() == 1); for (idx_t col_idx = 0; col_idx < input.ColumnCount(); col_idx++) { if (col_idx != 0) { - CSVWriter::WriteQuoteOrEscape(writer, - options.dialect_options.state_machine_options.delimiter.GetValue()[0]); + // the delimiter can consist of multiple bytes + auto &delimiter = options.dialect_options.state_machine_options.delimiter.GetValue(); + writer.WriteData(const_data_ptr_cast(delimiter.c_str()), delimiter.size()); } auto input_val = input_iterators[col_idx][row_idx]; if (!input_val.IsValid()) { @@ -398,7 +400,8 @@ void CSVWriter::WriteChunk(DataChunk &input, MemoryStream &writer, CSVReaderOpti void CSVWriter::WriteHeader(MemoryStream &stream, CSVReaderOptions &options, CSVWriterOptions &writer_options) { for (idx_t i = 0; i < options.name_list.size(); i++) { if (i != 0) { - WriteQuoteOrEscape(stream, options.dialect_options.state_machine_options.delimiter.GetValue()[0]); + auto &delimiter = options.dialect_options.state_machine_options.delimiter.GetValue(); + stream.WriteData(const_data_ptr_cast(delimiter.c_str()), delimiter.size()); } WriteQuotedString(stream, options.name_list[i].c_str(), options.name_list[i].size(), i, options, diff --git a/src/duckdb/src/common/encryption_functions.cpp b/src/duckdb/src/common/encryption_functions.cpp index ffabe81bc..3e1679f7b 100644 --- a/src/duckdb/src/common/encryption_functions.cpp +++ b/src/duckdb/src/common/encryption_functions.cpp @@ -66,15 +66,18 @@ string EncryptionEngine::AddKeyToCache(DatabaseInstance &db, data_ptr_t key) { return key_id; } +void EncryptionEngine::GenerateRandomKey(DatabaseInstance &db, data_ptr_t key, idx_t key_length) { + // key generation needs the read-write encryption util + auto metadata = make_uniq(EncryptionTypes::GCM, key_length, EncryptionTypes::V0_1); + auto encryption_state = db.GetEncryptionUtil(false)->CreateEncryptionState(std::move(metadata)); + encryption_state->GenerateRandomData(key, key_length); +} + void EncryptionEngine::AddTempKeyToCache(DatabaseInstance &db) { //! Add a temporary key to the cache const auto length = MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH; data_t temp_key[length]; - - // we cannot generate temporary keys with read-only enabled - auto metadata = make_uniq(EncryptionTypes::GCM, length, EncryptionTypes::V0_1); - auto encryption_state = db.GetEncryptionUtil(false)->CreateEncryptionState(std::move(metadata)); - encryption_state->GenerateRandomData(temp_key, length); + GenerateRandomKey(db, temp_key, length); string key_id = "temp_key"; AddKeyToCache(db, temp_key, key_id); diff --git a/src/duckdb/src/common/enum_util.cpp b/src/duckdb/src/common/enum_util.cpp index 4ac68bf19..81770861c 100644 --- a/src/duckdb/src/common/enum_util.cpp +++ b/src/duckdb/src/common/enum_util.cpp @@ -26,12 +26,14 @@ #include "duckdb/common/enums/checkpoint_abort.hpp" #include "duckdb/common/enums/checkpoint_on_detach.hpp" #include "duckdb/common/enums/compression_type.hpp" +#include "duckdb/common/enums/connection_type.hpp" #include "duckdb/common/enums/constraint_check_mode.hpp" #include "duckdb/common/enums/copy_overwrite_mode.hpp" #include "duckdb/common/enums/cte_materialize.hpp" #include "duckdb/common/enums/date_part_specifier.hpp" #include "duckdb/common/enums/debug_initialize.hpp" #include "duckdb/common/enums/debug_order_verification.hpp" +#include "duckdb/common/enums/debug_progress_verification.hpp" #include "duckdb/common/enums/debug_statement_verification.hpp" #include "duckdb/common/enums/debug_vector_verification.hpp" #include "duckdb/common/enums/debug_verification_mode.hpp" @@ -39,6 +41,7 @@ #include "duckdb/common/enums/dialect_compatibility_mode.hpp" #include "duckdb/common/enums/expression_type.hpp" #include "duckdb/common/enums/file_glob_options.hpp" +#include "duckdb/common/enums/file_sync_mode.hpp" #include "duckdb/common/enums/file_write_mode.hpp" #include "duckdb/common/enums/filter_propagate_result.hpp" #include "duckdb/common/enums/function_errors.hpp" @@ -59,15 +62,17 @@ #include "duckdb/common/enums/order_type.hpp" #include "duckdb/common/enums/ordinality_request_type.hpp" #include "duckdb/common/enums/output_type.hpp" -#include "duckdb/common/enums/pending_execution_result.hpp" #include "duckdb/common/enums/physical_operator_type.hpp" #include "duckdb/common/enums/prepared_statement_mode.hpp" #include "duckdb/common/enums/preserve_order.hpp" #include "duckdb/common/enums/profiling_coverage.hpp" #include "duckdb/common/enums/quantile_enum.hpp" +#include "duckdb/common/enums/query_result_memory_type.hpp" +#include "duckdb/common/enums/query_result_state.hpp" #include "duckdb/common/enums/regex_match_operator_semantics.hpp" #include "duckdb/common/enums/relation_type.hpp" #include "duckdb/common/enums/result_lifetime.hpp" +#include "duckdb/common/enums/result_ordering.hpp" #include "duckdb/common/enums/row_group_append_mode.hpp" #include "duckdb/common/enums/row_id_handling.hpp" #include "duckdb/common/enums/set_operation_type.hpp" @@ -76,7 +81,6 @@ #include "duckdb/common/enums/show_behavior.hpp" #include "duckdb/common/enums/statement_type.hpp" #include "duckdb/common/enums/storage_block_prefetch.hpp" -#include "duckdb/common/enums/stream_execution_result.hpp" #include "duckdb/common/enums/subquery_type.hpp" #include "duckdb/common/enums/table_function_identifier_conversion.hpp" #include "duckdb/common/enums/tableref_type.hpp" @@ -91,7 +95,6 @@ #include "duckdb/common/enums/window_aggregation_mode.hpp" #include "duckdb/common/exception.hpp" #include "duckdb/common/exception_format_value.hpp" -#include "duckdb/common/extra_type_info.hpp" #include "duckdb/common/file_buffer.hpp" #include "duckdb/common/file_open_flags.hpp" #include "duckdb/common/filename_pattern.hpp" @@ -101,7 +104,6 @@ #include "duckdb/common/multi_file/multi_file_states.hpp" #include "duckdb/common/operator/decimal_cast_operators.hpp" #include "duckdb/common/printer.hpp" -#include "duckdb/common/query_parameters.hpp" #include "duckdb/common/sorting/sort_key.hpp" #include "duckdb/common/types.hpp" #include "duckdb/common/types/column/column_data_scan_states.hpp" @@ -129,7 +131,6 @@ #include "duckdb/execution/index/unbound_index.hpp" #include "duckdb/execution/operator/csv_scanner/csv_option.hpp" #include "duckdb/execution/operator/csv_scanner/csv_state.hpp" -#include "duckdb/execution/operator/helper/physical_result_sink.hpp" #include "duckdb/execution/operator/join/join_filter_pushdown.hpp" #include "duckdb/execution/operator/set/physical_cte.hpp" #include "duckdb/execution/operator/set/physical_recursive_cte_state.hpp" @@ -165,9 +166,9 @@ #include "duckdb/main/extension.hpp" #include "duckdb/main/extension_helper.hpp" #include "duckdb/main/extension_install_info.hpp" +#include "duckdb/main/http/http_retry_budget.hpp" #include "duckdb/main/http/http_util.hpp" #include "duckdb/main/profiler/gathered_metrics.hpp" -#include "duckdb/main/query_result.hpp" #include "duckdb/main/secret/secret.hpp" #include "duckdb/main/setting_info.hpp" #include "duckdb/optimizer/aggregate_rewrite.hpp" @@ -184,6 +185,7 @@ #include "duckdb/parallel/pipeline.hpp" #include "duckdb/parallel/pipeline_broadcast_exchange.hpp" #include "duckdb/parallel/pipeline_schedule.hpp" +#include "duckdb/parallel/progress_verifier.hpp" #include "duckdb/parallel/scan_read_ahead.hpp" #include "duckdb/parallel/task.hpp" #include "duckdb/parser/constraint.hpp" @@ -196,6 +198,7 @@ #include "duckdb/parser/parsed_data/alter_database_info.hpp" #include "duckdb/parser/parsed_data/alter_info.hpp" #include "duckdb/parser/parsed_data/alter_scalar_function_info.hpp" +#include "duckdb/parser/parsed_data/alter_schema_info.hpp" #include "duckdb/parser/parsed_data/alter_table_function_info.hpp" #include "duckdb/parser/parsed_data/alter_table_info.hpp" #include "duckdb/parser/parsed_data/create_sequence_info.hpp" @@ -206,8 +209,10 @@ #include "duckdb/parser/parsed_data/sample_options.hpp" #include "duckdb/parser/parsed_data/transaction_info.hpp" #include "duckdb/parser/parser_extension.hpp" -#include "duckdb/parser/peg/keyword_helper.hpp" +#include "duckdb/parser/peg/ast/expression_chain.hpp" #include "duckdb/parser/peg/matcher.hpp" +#include "duckdb/parser/peg/matcher/operator_matcher.hpp" +#include "duckdb/parser/peg/parsed_grammar.hpp" #include "duckdb/parser/peg/sql_formatter.hpp" #include "duckdb/parser/peg/transformer/parse_result.hpp" #include "duckdb/parser/peg/transformer/peg_transformer.hpp" @@ -225,7 +230,9 @@ #include "duckdb/planner/bound_result_modifier.hpp" #include "duckdb/planner/filter/table_filter_functions.hpp" #include "duckdb/planner/logical_operator_repeatability.hpp" +#include "duckdb/planner/logical_plan_verification_result.hpp" #include "duckdb/planner/table_filter.hpp" +#include "duckdb/storage/buffer/block_handle.hpp" #include "duckdb/storage/buffer/buffer_pool_reservation.hpp" #include "duckdb/storage/caching_mode.hpp" #include "duckdb/storage/compression/bitpacking.hpp" @@ -595,6 +602,25 @@ AlterScalarFunctionType EnumUtil::FromString(const char return static_cast(StringUtil::StringToEnum(GetAlterScalarFunctionTypeValues(), 2, "AlterScalarFunctionType", value)); } +const StringUtil::EnumStringLiteral *GetAlterSchemaTypeValues() { + static constexpr StringUtil::EnumStringLiteral values[] { + { static_cast(AlterSchemaType::INVALID), "INVALID" }, + { static_cast(AlterSchemaType::SET_SCHEMA_OPTIONS), "SET_SCHEMA_OPTIONS" }, + { static_cast(AlterSchemaType::RESET_SCHEMA_OPTIONS), "RESET_SCHEMA_OPTIONS" } + }; + return values; +} + +template<> +const char* EnumUtil::ToChars(AlterSchemaType value) { + return StringUtil::EnumToString(GetAlterSchemaTypeValues(), 3, "AlterSchemaType", static_cast(value)); +} + +template<> +AlterSchemaType EnumUtil::FromString(const char *value) { + return static_cast(StringUtil::StringToEnum(GetAlterSchemaTypeValues(), 3, "AlterSchemaType", value)); +} + const StringUtil::EnumStringLiteral *GetAlterTableFunctionTypeValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(AlterTableFunctionType::INVALID), "INVALID" }, @@ -659,19 +685,20 @@ const StringUtil::EnumStringLiteral *GetAlterTypeValues() { { static_cast(AlterType::ALTER_TABLE_FUNCTION), "ALTER_TABLE_FUNCTION" }, { static_cast(AlterType::SET_COMMENT), "SET_COMMENT" }, { static_cast(AlterType::SET_COLUMN_COMMENT), "SET_COLUMN_COMMENT" }, - { static_cast(AlterType::ALTER_DATABASE), "ALTER_DATABASE" } + { static_cast(AlterType::ALTER_DATABASE), "ALTER_DATABASE" }, + { static_cast(AlterType::ALTER_SCHEMA), "ALTER_SCHEMA" } }; return values; } template<> const char* EnumUtil::ToChars(AlterType value) { - return StringUtil::EnumToString(GetAlterTypeValues(), 10, "AlterType", static_cast(value)); + return StringUtil::EnumToString(GetAlterTypeValues(), 11, "AlterType", static_cast(value)); } template<> AlterType EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetAlterTypeValues(), 10, "AlterType", value)); + return static_cast(StringUtil::StringToEnum(GetAlterTypeValues(), 11, "AlterType", value)); } const StringUtil::EnumStringLiteral *GetAlterViewTypeValues() { @@ -1157,6 +1184,26 @@ CachingMode EnumUtil::FromString(const char *value) { return static_cast(StringUtil::StringToEnum(GetCachingModeValues(), 3, "CachingMode", value)); } +const StringUtil::EnumStringLiteral *GetCanUnloadResultValues() { + static constexpr StringUtil::EnumStringLiteral values[] { + { static_cast(CanUnloadResult::CAN_UNLOAD), "CAN_UNLOAD" }, + { static_cast(CanUnloadResult::ALREADY_UNLOADED), "ALREADY_UNLOADED" }, + { static_cast(CanUnloadResult::PINNED), "PINNED" }, + { static_cast(CanUnloadResult::NO_TEMP_DIRECTORY), "NO_TEMP_DIRECTORY" } + }; + return values; +} + +template<> +const char* EnumUtil::ToChars(CanUnloadResult value) { + return StringUtil::EnumToString(GetCanUnloadResultValues(), 4, "CanUnloadResult", static_cast(value)); +} + +template<> +CanUnloadResult EnumUtil::FromString(const char *value) { + return static_cast(StringUtil::StringToEnum(GetCanUnloadResultValues(), 4, "CanUnloadResult", value)); +} + const StringUtil::EnumStringLiteral *GetCatalogLookupBehaviorValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(CatalogLookupBehavior::STANDARD), "STANDARD" }, @@ -1228,19 +1275,20 @@ const StringUtil::EnumStringLiteral *GetCheckpointAbortValues() { { static_cast(CheckpointAbort::DEBUG_ABORT_BEFORE_MOVING_RECOVERY), "BEFORE_MOVING_RECOVERY" }, { static_cast(CheckpointAbort::DEBUG_ABORT_BEFORE_DELETING_CHECKPOINT_WAL), "BEFORE_DELETING_CHECKPOINT_WAL" }, { static_cast(CheckpointAbort::DEBUG_ABORT_BEFORE_HEADER_NON_FATAL), "BEFORE_HEADER_NON_FATAL" }, - { static_cast(CheckpointAbort::DEBUG_ABORT_IN_MEMORY_CHECKPOINT), "IN_MEMORY_CHECKPOINT" } + { static_cast(CheckpointAbort::DEBUG_ABORT_IN_MEMORY_CHECKPOINT), "IN_MEMORY_CHECKPOINT" }, + { static_cast(CheckpointAbort::DEBUG_ABORT_BEFORE_WAL_FLUSH), "BEFORE_WAL_FLUSH" } }; return values; } template<> const char* EnumUtil::ToChars(CheckpointAbort value) { - return StringUtil::EnumToString(GetCheckpointAbortValues(), 9, "CheckpointAbort", static_cast(value)); + return StringUtil::EnumToString(GetCheckpointAbortValues(), 10, "CheckpointAbort", static_cast(value)); } template<> CheckpointAbort EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetCheckpointAbortValues(), 9, "CheckpointAbort", value)); + return static_cast(StringUtil::StringToEnum(GetCheckpointAbortValues(), 10, "CheckpointAbort", value)); } const StringUtil::EnumStringLiteral *GetCheckpointOnDetachValues() { @@ -1431,19 +1479,19 @@ const StringUtil::EnumStringLiteral *GetCompressionTypeValues() { { static_cast(CompressionType::COMPRESSION_ROARING), "ROARING" }, { static_cast(CompressionType::COMPRESSION_EMPTY), "EMPTY" }, { static_cast(CompressionType::COMPRESSION_DICT_FSST), "DICT_FSST" }, - { static_cast(CompressionType::COMPRESSION_COUNT), "COMPRESSION_COUNT" } + { static_cast(CompressionType::ENUM_SIZE), "ENUM_SIZE" } }; return values; } template<> const char* EnumUtil::ToChars(CompressionType value) { - return StringUtil::EnumToString(GetCompressionTypeValues(), 17, "CompressionType", static_cast(value)); + return StringUtil::EnumToString(GetCompressionTypeValues(), static_cast(CompressionType::ENUM_SIZE), "CompressionType", static_cast(value)); } template<> CompressionType EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetCompressionTypeValues(), 17, "CompressionType", value)); + return static_cast(StringUtil::StringToEnum(GetCompressionTypeValues(), static_cast(CompressionType::ENUM_SIZE), "CompressionType", value)); } const StringUtil::EnumStringLiteral *GetCompressionValidityValues() { @@ -1482,6 +1530,24 @@ ConflictManagerMode EnumUtil::FromString(const char *value) return static_cast(StringUtil::StringToEnum(GetConflictManagerModeValues(), 2, "ConflictManagerMode", value)); } +const StringUtil::EnumStringLiteral *GetConnectionTypeValues() { + static constexpr StringUtil::EnumStringLiteral values[] { + { static_cast(ConnectionType::USER), "USER" }, + { static_cast(ConnectionType::INTERNAL), "INTERNAL" } + }; + return values; +} + +template<> +const char* EnumUtil::ToChars(ConnectionType value) { + return StringUtil::EnumToString(GetConnectionTypeValues(), 2, "ConnectionType", static_cast(value)); +} + +template<> +ConnectionType EnumUtil::FromString(const char *value) { + return static_cast(StringUtil::StringToEnum(GetConnectionTypeValues(), 2, "ConnectionType", value)); +} + const StringUtil::EnumStringLiteral *GetConstraintCheckModeValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(ConstraintCheckMode::DEFAULT), "DEFAULT" }, @@ -1757,6 +1823,25 @@ DebugOrderVerification EnumUtil::FromString(const char * return static_cast(StringUtil::StringToEnum(GetDebugOrderVerificationValues(), 3, "DebugOrderVerification", value)); } +const StringUtil::EnumStringLiteral *GetDebugProgressVerificationValues() { + static constexpr StringUtil::EnumStringLiteral values[] { + { static_cast(DebugProgressVerification::NONE), "NONE" }, + { static_cast(DebugProgressVerification::LOG), "LOG" }, + { static_cast(DebugProgressVerification::ERROR), "ERROR" } + }; + return values; +} + +template<> +const char* EnumUtil::ToChars(DebugProgressVerification value) { + return StringUtil::EnumToString(GetDebugProgressVerificationValues(), 3, "DebugProgressVerification", static_cast(value)); +} + +template<> +DebugProgressVerification EnumUtil::FromString(const char *value) { + return static_cast(StringUtil::StringToEnum(GetDebugProgressVerificationValues(), 3, "DebugProgressVerification", value)); +} + const StringUtil::EnumStringLiteral *GetDebugStatementVerificationValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(DebugStatementVerification::NONE), "NONE" }, @@ -1764,19 +1849,21 @@ const StringUtil::EnumStringLiteral *GetDebugStatementVerificationValues() { { static_cast(DebugStatementVerification::REPARSE_STATEMENT), "REPARSE_STATEMENT" }, { static_cast(DebugStatementVerification::SERIALIZE_STATEMENT), "SERIALIZE_STATEMENT" }, { static_cast(DebugStatementVerification::PREPARED_STATEMENT), "PREPARED_STATEMENT" }, - { static_cast(DebugStatementVerification::EXPLAIN_STATEMENT), "EXPLAIN_STATEMENT" } + { static_cast(DebugStatementVerification::EXPLAIN_STATEMENT), "EXPLAIN_STATEMENT" }, + { static_cast(DebugStatementVerification::EXPLAIN_SQL), "EXPLAIN_SQL" }, + { static_cast(DebugStatementVerification::EXPLAIN_SQL_STRICT), "EXPLAIN_SQL_STRICT" } }; return values; } template<> const char* EnumUtil::ToChars(DebugStatementVerification value) { - return StringUtil::EnumToString(GetDebugStatementVerificationValues(), 6, "DebugStatementVerification", static_cast(value)); + return StringUtil::EnumToString(GetDebugStatementVerificationValues(), 8, "DebugStatementVerification", static_cast(value)); } template<> DebugStatementVerification EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetDebugStatementVerificationValues(), 6, "DebugStatementVerification", value)); + return static_cast(StringUtil::StringToEnum(GetDebugStatementVerificationValues(), 8, "DebugStatementVerification", value)); } const StringUtil::EnumStringLiteral *GetDebugVectorVerificationValues() { @@ -2138,19 +2225,20 @@ ExplainOutputType EnumUtil::FromString(const char *value) { const StringUtil::EnumStringLiteral *GetExplainTypeValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(ExplainType::EXPLAIN_STANDARD), "EXPLAIN_STANDARD" }, - { static_cast(ExplainType::EXPLAIN_ANALYZE), "EXPLAIN_ANALYZE" } + { static_cast(ExplainType::EXPLAIN_ANALYZE), "EXPLAIN_ANALYZE" }, + { static_cast(ExplainType::EXPLAIN_SQL), "EXPLAIN_SQL" } }; return values; } template<> const char* EnumUtil::ToChars(ExplainType value) { - return StringUtil::EnumToString(GetExplainTypeValues(), 2, "ExplainType", static_cast(value)); + return StringUtil::EnumToString(GetExplainTypeValues(), 3, "ExplainType", static_cast(value)); } template<> ExplainType EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetExplainTypeValues(), 2, "ExplainType", value)); + return static_cast(StringUtil::StringToEnum(GetExplainTypeValues(), 3, "ExplainType", value)); } const StringUtil::EnumStringLiteral *GetExponentTypeValues() { @@ -2229,6 +2317,45 @@ ExpressionClass EnumUtil::FromString(const char *value) { return static_cast(StringUtil::StringToEnum(GetExpressionClassValues(), 41, "ExpressionClass", value)); } +const StringUtil::EnumStringLiteral *GetExpressionDepthKindValues() { + static constexpr StringUtil::EnumStringLiteral values[] { + { static_cast(ExpressionDepthKind::NONE), "NONE" }, + { static_cast(ExpressionDepthKind::NESTING), "NESTING" }, + { static_cast(ExpressionDepthKind::CHAIN), "CHAIN" } + }; + return values; +} + +template<> +const char* EnumUtil::ToChars(ExpressionDepthKind value) { + return StringUtil::EnumToString(GetExpressionDepthKindValues(), 3, "ExpressionDepthKind", static_cast(value)); +} + +template<> +ExpressionDepthKind EnumUtil::FromString(const char *value) { + return static_cast(StringUtil::StringToEnum(GetExpressionDepthKindValues(), 3, "ExpressionDepthKind", value)); +} + +const StringUtil::EnumStringLiteral *GetExpressionTailTypeValues() { + static constexpr StringUtil::EnumStringLiteral values[] { + { static_cast(ExpressionTailType::IS_TEST), "IS_TEST" }, + { static_cast(ExpressionTailType::DISTINCT), "DISTINCT" }, + { static_cast(ExpressionTailType::COMPARISON), "COMPARISON" }, + { static_cast(ExpressionTailType::OTHER_OPERATOR), "OTHER_OPERATOR" } + }; + return values; +} + +template<> +const char* EnumUtil::ToChars(ExpressionTailType value) { + return StringUtil::EnumToString(GetExpressionTailTypeValues(), 4, "ExpressionTailType", static_cast(value)); +} + +template<> +ExpressionTailType EnumUtil::FromString(const char *value) { + return static_cast(StringUtil::StringToEnum(GetExpressionTailTypeValues(), 4, "ExpressionTailType", value)); +} + const StringUtil::EnumStringLiteral *GetExpressionTypeValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(ExpressionType::INVALID), "INVALID" }, @@ -2445,6 +2572,25 @@ ExtensionUpdateResultTag EnumUtil::FromString(const ch return static_cast(StringUtil::StringToEnum(GetExtensionUpdateResultTagValues(), 8, "ExtensionUpdateResultTag", value)); } +const StringUtil::EnumStringLiteral *GetExternalFileCacheRequestSizingValues() { + static constexpr StringUtil::EnumStringLiteral values[] { + { static_cast(ExternalFileCacheRequestSizing::AUTO), "AUTO" }, + { static_cast(ExternalFileCacheRequestSizing::GRID), "GRID" }, + { static_cast(ExternalFileCacheRequestSizing::EXACT), "EXACT" } + }; + return values; +} + +template<> +const char* EnumUtil::ToChars(ExternalFileCacheRequestSizing value) { + return StringUtil::EnumToString(GetExternalFileCacheRequestSizingValues(), 3, "ExternalFileCacheRequestSizing", static_cast(value)); +} + +template<> +ExternalFileCacheRequestSizing EnumUtil::FromString(const char *value) { + return static_cast(StringUtil::StringToEnum(GetExternalFileCacheRequestSizingValues(), 3, "ExternalFileCacheRequestSizing", value)); +} + const StringUtil::EnumStringLiteral *GetExternalResourceOperationValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(ExternalResourceOperation::CREATE), "CREATE" }, @@ -2503,36 +2649,6 @@ ExtraPersistentColumnDataType EnumUtil::FromString(StringUtil::StringToEnum(GetExtraPersistentColumnDataTypeValues(), 3, "ExtraPersistentColumnDataType", value)); } -const StringUtil::EnumStringLiteral *GetExtraTypeInfoTypeValues() { - static constexpr StringUtil::EnumStringLiteral values[] { - { static_cast(ExtraTypeInfoType::INVALID_TYPE_INFO), "INVALID_TYPE_INFO" }, - { static_cast(ExtraTypeInfoType::GENERIC_TYPE_INFO), "GENERIC_TYPE_INFO" }, - { static_cast(ExtraTypeInfoType::DECIMAL_TYPE_INFO), "DECIMAL_TYPE_INFO" }, - { static_cast(ExtraTypeInfoType::STRING_TYPE_INFO), "STRING_TYPE_INFO" }, - { static_cast(ExtraTypeInfoType::LIST_TYPE_INFO), "LIST_TYPE_INFO" }, - { static_cast(ExtraTypeInfoType::STRUCT_TYPE_INFO), "STRUCT_TYPE_INFO" }, - { static_cast(ExtraTypeInfoType::ENUM_TYPE_INFO), "ENUM_TYPE_INFO" }, - { static_cast(ExtraTypeInfoType::UNBOUND_TYPE_INFO), "UNBOUND_TYPE_INFO" }, - { static_cast(ExtraTypeInfoType::LEGACY_AGGREGATE_STATE_TYPE_INFO), "LEGACY_AGGREGATE_STATE_TYPE_INFO" }, - { static_cast(ExtraTypeInfoType::ARRAY_TYPE_INFO), "ARRAY_TYPE_INFO" }, - { static_cast(ExtraTypeInfoType::ANY_TYPE_INFO), "ANY_TYPE_INFO" }, - { static_cast(ExtraTypeInfoType::INTEGER_LITERAL_TYPE_INFO), "INTEGER_LITERAL_TYPE_INFO" }, - { static_cast(ExtraTypeInfoType::TEMPLATE_TYPE_INFO), "TEMPLATE_TYPE_INFO" }, - { static_cast(ExtraTypeInfoType::GEO_TYPE_INFO), "GEO_TYPE_INFO" } - }; - return values; -} - -template<> -const char* EnumUtil::ToChars(ExtraTypeInfoType value) { - return StringUtil::EnumToString(GetExtraTypeInfoTypeValues(), 14, "ExtraTypeInfoType", static_cast(value)); -} - -template<> -ExtraTypeInfoType EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetExtraTypeInfoTypeValues(), 14, "ExtraTypeInfoType", value)); -} - const StringUtil::EnumStringLiteral *GetFileBufferTypeValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(FileBufferType::BLOCK), "BLOCK" }, @@ -2649,6 +2765,25 @@ FileNameSegmentType EnumUtil::FromString(const char *value) return static_cast(StringUtil::StringToEnum(GetFileNameSegmentTypeValues(), 4, "FileNameSegmentType", value)); } +const StringUtil::EnumStringLiteral *GetFileSyncModeValues() { + static constexpr StringUtil::EnumStringLiteral values[] { + { static_cast(FileSyncMode::STANDARD), "STANDARD" }, + { static_cast(FileSyncMode::NONE), "NONE" }, + { static_cast(FileSyncMode::FULL), "FULL" } + }; + return values; +} + +template<> +const char* EnumUtil::ToChars(FileSyncMode value) { + return StringUtil::EnumToString(GetFileSyncModeValues(), 3, "FileSyncMode", static_cast(value)); +} + +template<> +FileSyncMode EnumUtil::FromString(const char *value) { + return static_cast(StringUtil::StringToEnum(GetFileSyncModeValues(), 3, "FileSyncMode", value)); +} + const StringUtil::EnumStringLiteral *GetFileWriteModeValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(FileWriteMode::SEQUENTIAL), "SEQUENTIAL" }, @@ -2712,19 +2847,20 @@ const StringUtil::EnumStringLiteral *GetFunctionCollationHandlingValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(FunctionCollationHandling::PROPAGATE_COLLATIONS), "PROPAGATE_COLLATIONS" }, { static_cast(FunctionCollationHandling::PUSH_COMBINABLE_COLLATIONS), "PUSH_COMBINABLE_COLLATIONS" }, - { static_cast(FunctionCollationHandling::IGNORE_COLLATIONS), "IGNORE_COLLATIONS" } + { static_cast(FunctionCollationHandling::IGNORE_COLLATIONS), "IGNORE_COLLATIONS" }, + { static_cast(FunctionCollationHandling::PUSH_DISTINCT_COLLATIONS), "PUSH_DISTINCT_COLLATIONS" } }; return values; } template<> const char* EnumUtil::ToChars(FunctionCollationHandling value) { - return StringUtil::EnumToString(GetFunctionCollationHandlingValues(), 3, "FunctionCollationHandling", static_cast(value)); + return StringUtil::EnumToString(GetFunctionCollationHandlingValues(), 4, "FunctionCollationHandling", static_cast(value)); } template<> FunctionCollationHandling EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetFunctionCollationHandlingValues(), 3, "FunctionCollationHandling", value)); + return static_cast(StringUtil::StringToEnum(GetFunctionCollationHandlingValues(), 4, "FunctionCollationHandling", value)); } const StringUtil::EnumStringLiteral *GetFunctionErrorsValues() { @@ -2763,6 +2899,27 @@ FunctionNullHandling EnumUtil::FromString(const char *valu return static_cast(StringUtil::StringToEnum(GetFunctionNullHandlingValues(), 2, "FunctionNullHandling", value)); } +const StringUtil::EnumStringLiteral *GetFunctionParameterKindValues() { + static constexpr StringUtil::EnumStringLiteral values[] { + { static_cast(FunctionParameterKind::POSITIONAL_ONLY), "POSITIONAL_ONLY" }, + { static_cast(FunctionParameterKind::STANDARD), "STANDARD" }, + { static_cast(FunctionParameterKind::VAR_POSITIONAL), "VAR_POSITIONAL" }, + { static_cast(FunctionParameterKind::KEYWORD_ONLY), "KEYWORD_ONLY" }, + { static_cast(FunctionParameterKind::VAR_KEYWORD), "VAR_KEYWORD" } + }; + return values; +} + +template<> +const char* EnumUtil::ToChars(FunctionParameterKind value) { + return StringUtil::EnumToString(GetFunctionParameterKindValues(), 5, "FunctionParameterKind", static_cast(value)); +} + +template<> +FunctionParameterKind EnumUtil::FromString(const char *value) { + return static_cast(StringUtil::StringToEnum(GetFunctionParameterKindValues(), 5, "FunctionParameterKind", value)); +} + const StringUtil::EnumStringLiteral *GetFunctionStabilityValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(FunctionStability::CONSISTENT), "CONSISTENT" }, @@ -2947,6 +3104,24 @@ HTTPClientCachePolicy EnumUtil::FromString(const char *va return static_cast(StringUtil::StringToEnum(GetHTTPClientCachePolicyValues(), 2, "HTTPClientCachePolicy", value)); } +const StringUtil::EnumStringLiteral *GetHTTPRetryOutcomeOwnerValues() { + static constexpr StringUtil::EnumStringLiteral values[] { + { static_cast(HTTPRetryOutcomeOwner::STATUS), "STATUS" }, + { static_cast(HTTPRetryOutcomeOwner::COORDINATOR), "COORDINATOR" } + }; + return values; +} + +template<> +const char* EnumUtil::ToChars(HTTPRetryOutcomeOwner value) { + return StringUtil::EnumToString(GetHTTPRetryOutcomeOwnerValues(), 2, "HTTPRetryOutcomeOwner", static_cast(value)); +} + +template<> +HTTPRetryOutcomeOwner EnumUtil::FromString(const char *value) { + return static_cast(StringUtil::StringToEnum(GetHTTPRetryOutcomeOwnerValues(), 2, "HTTPRetryOutcomeOwner", value)); +} + const StringUtil::EnumStringLiteral *GetHTTPStatusCodeValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(HTTPStatusCode::INVALID), "INVALID" }, @@ -3313,19 +3488,20 @@ const StringUtil::EnumStringLiteral *GetKeywordCategoryValues() { { static_cast(KeywordCategory::KEYWORD_UNRESERVED), "KEYWORD_UNRESERVED" }, { static_cast(KeywordCategory::KEYWORD_TYPE_FUNC), "KEYWORD_TYPE_FUNC" }, { static_cast(KeywordCategory::KEYWORD_COL_NAME), "KEYWORD_COL_NAME" }, - { static_cast(KeywordCategory::KEYWORD_NONE), "KEYWORD_NONE" } + { static_cast(KeywordCategory::KEYWORD_NONE), "KEYWORD_NONE" }, + { static_cast(KeywordCategory::KEYWORD_TYPE_NAME), "KEYWORD_TYPE_NAME" } }; return values; } template<> const char* EnumUtil::ToChars(KeywordCategory value) { - return StringUtil::EnumToString(GetKeywordCategoryValues(), 5, "KeywordCategory", static_cast(value)); + return StringUtil::EnumToString(GetKeywordCategoryValues(), 6, "KeywordCategory", static_cast(value)); } template<> KeywordCategory EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetKeywordCategoryValues(), 5, "KeywordCategory", value)); + return static_cast(StringUtil::StringToEnum(GetKeywordCategoryValues(), 6, "KeywordCategory", value)); } const StringUtil::EnumStringLiteral *GetLambdaSyntaxValues() { @@ -3634,6 +3810,70 @@ LogicalOperatorType EnumUtil::FromString(const char *value) return static_cast(StringUtil::StringToEnum(GetLogicalOperatorTypeValues(), 68, "LogicalOperatorType", value)); } +const StringUtil::EnumStringLiteral *GetLogicalPlanVerificationIssueCodeValues() { + static constexpr StringUtil::EnumStringLiteral values[] { + { static_cast(LogicalPlanVerificationIssueCode::INVALID_BINDING), "INVALID_BINDING" }, + { static_cast(LogicalPlanVerificationIssueCode::TYPE_MISMATCH), "TYPE_MISMATCH" }, + { static_cast(LogicalPlanVerificationIssueCode::UNSUPPORTED_OPERATOR), "UNSUPPORTED_OPERATOR" }, + { static_cast(LogicalPlanVerificationIssueCode::UNSUPPORTED_EXPRESSION), "UNSUPPORTED_EXPRESSION" }, + { static_cast(LogicalPlanVerificationIssueCode::UNSUPPORTED_FUNCTION), "UNSUPPORTED_FUNCTION" }, + { static_cast(LogicalPlanVerificationIssueCode::UNSUPPORTED_SOURCE), "UNSUPPORTED_SOURCE" }, + { static_cast(LogicalPlanVerificationIssueCode::UNSUPPORTED_EXTENSION), "UNSUPPORTED_EXTENSION" }, + { static_cast(LogicalPlanVerificationIssueCode::MALFORMED_EXTENSION_RESULT), "MALFORMED_EXTENSION_RESULT" }, + { static_cast(LogicalPlanVerificationIssueCode::UNSUPPORTED_EXPORT_FEATURE), "UNSUPPORTED_EXPORT_FEATURE" }, + { static_cast(LogicalPlanVerificationIssueCode::INTERNAL_INVARIANT), "INTERNAL_INVARIANT" } + }; + return values; +} + +template<> +const char* EnumUtil::ToChars(LogicalPlanVerificationIssueCode value) { + return StringUtil::EnumToString(GetLogicalPlanVerificationIssueCodeValues(), 10, "LogicalPlanVerificationIssueCode", static_cast(value)); +} + +template<> +LogicalPlanVerificationIssueCode EnumUtil::FromString(const char *value) { + return static_cast(StringUtil::StringToEnum(GetLogicalPlanVerificationIssueCodeValues(), 10, "LogicalPlanVerificationIssueCode", value)); +} + +const StringUtil::EnumStringLiteral *GetLogicalPlanVerificationPathComponentTypeValues() { + static constexpr StringUtil::EnumStringLiteral values[] { + { static_cast(LogicalPlanVerificationPathComponentType::OPERATOR_CHILD), "OPERATOR_CHILD" }, + { static_cast(LogicalPlanVerificationPathComponentType::OPERATOR_EXPRESSION), "OPERATOR_EXPRESSION" }, + { static_cast(LogicalPlanVerificationPathComponentType::EXPRESSION_CHILD), "EXPRESSION_CHILD" } + }; + return values; +} + +template<> +const char* EnumUtil::ToChars(LogicalPlanVerificationPathComponentType value) { + return StringUtil::EnumToString(GetLogicalPlanVerificationPathComponentTypeValues(), 3, "LogicalPlanVerificationPathComponentType", static_cast(value)); +} + +template<> +LogicalPlanVerificationPathComponentType EnumUtil::FromString(const char *value) { + return static_cast(StringUtil::StringToEnum(GetLogicalPlanVerificationPathComponentTypeValues(), 3, "LogicalPlanVerificationPathComponentType", value)); +} + +const StringUtil::EnumStringLiteral *GetLogicalPlanVerificationPhaseValues() { + static constexpr StringUtil::EnumStringLiteral values[] { + { static_cast(LogicalPlanVerificationPhase::VERIFY), "VERIFY" }, + { static_cast(LogicalPlanVerificationPhase::EXPRESSION_EXPORT), "EXPRESSION_EXPORT" }, + { static_cast(LogicalPlanVerificationPhase::PLAN_EXPORT), "PLAN_EXPORT" } + }; + return values; +} + +template<> +const char* EnumUtil::ToChars(LogicalPlanVerificationPhase value) { + return StringUtil::EnumToString(GetLogicalPlanVerificationPhaseValues(), 3, "LogicalPlanVerificationPhase", static_cast(value)); +} + +template<> +LogicalPlanVerificationPhase EnumUtil::FromString(const char *value) { + return static_cast(StringUtil::StringToEnum(GetLogicalPlanVerificationPhaseValues(), 3, "LogicalPlanVerificationPhase", value)); +} + const StringUtil::EnumStringLiteral *GetLogicalTypeIdValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(LogicalTypeId::INVALID), "INVALID" }, @@ -3704,6 +3944,36 @@ LogicalTypeId EnumUtil::FromString(const char *value) { return static_cast(StringUtil::StringToEnum(GetLogicalTypeIdValues(), 54, "LogicalTypeId", value)); } +const StringUtil::EnumStringLiteral *GetLogicalTypeInfoTypeValues() { + static constexpr StringUtil::EnumStringLiteral values[] { + { static_cast(LogicalTypeInfoType::INVALID_TYPE_INFO), "INVALID_TYPE_INFO" }, + { static_cast(LogicalTypeInfoType::GENERIC_TYPE_INFO), "GENERIC_TYPE_INFO" }, + { static_cast(LogicalTypeInfoType::DECIMAL_TYPE_INFO), "DECIMAL_TYPE_INFO" }, + { static_cast(LogicalTypeInfoType::STRING_TYPE_INFO), "STRING_TYPE_INFO" }, + { static_cast(LogicalTypeInfoType::LIST_TYPE_INFO), "LIST_TYPE_INFO" }, + { static_cast(LogicalTypeInfoType::STRUCT_TYPE_INFO), "STRUCT_TYPE_INFO" }, + { static_cast(LogicalTypeInfoType::ENUM_TYPE_INFO), "ENUM_TYPE_INFO" }, + { static_cast(LogicalTypeInfoType::UNBOUND_TYPE_INFO), "UNBOUND_TYPE_INFO" }, + { static_cast(LogicalTypeInfoType::LEGACY_AGGREGATE_STATE_TYPE_INFO), "LEGACY_AGGREGATE_STATE_TYPE_INFO" }, + { static_cast(LogicalTypeInfoType::ARRAY_TYPE_INFO), "ARRAY_TYPE_INFO" }, + { static_cast(LogicalTypeInfoType::ANY_TYPE_INFO), "ANY_TYPE_INFO" }, + { static_cast(LogicalTypeInfoType::INTEGER_LITERAL_TYPE_INFO), "INTEGER_LITERAL_TYPE_INFO" }, + { static_cast(LogicalTypeInfoType::TEMPLATE_TYPE_INFO), "TEMPLATE_TYPE_INFO" }, + { static_cast(LogicalTypeInfoType::GEO_TYPE_INFO), "GEO_TYPE_INFO" } + }; + return values; +} + +template<> +const char* EnumUtil::ToChars(LogicalTypeInfoType value) { + return StringUtil::EnumToString(GetLogicalTypeInfoTypeValues(), 14, "LogicalTypeInfoType", static_cast(value)); +} + +template<> +LogicalTypeInfoType EnumUtil::FromString(const char *value) { + return static_cast(StringUtil::StringToEnum(GetLogicalTypeInfoTypeValues(), 14, "LogicalTypeInfoType", value)); +} + const StringUtil::EnumStringLiteral *GetLookupResultTypeValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(LookupResultType::LOOKUP_MISS), "LOOKUP_MISS" }, @@ -4238,6 +4508,24 @@ OperatorFinalizeResultType EnumUtil::FromString(cons return static_cast(StringUtil::StringToEnum(GetOperatorFinalizeResultTypeValues(), 2, "OperatorFinalizeResultType", value)); } +const StringUtil::EnumStringLiteral *GetOperatorMatcherModeValues() { + static constexpr StringUtil::EnumStringLiteral values[] { + { static_cast(OperatorMatcherMode::GENERIC_PRECEDENCE), "GENERIC_PRECEDENCE" }, + { static_cast(OperatorMatcherMode::ALL_OPERATORS), "ALL_OPERATORS" } + }; + return values; +} + +template<> +const char* EnumUtil::ToChars(OperatorMatcherMode value) { + return StringUtil::EnumToString(GetOperatorMatcherModeValues(), 2, "OperatorMatcherMode", static_cast(value)); +} + +template<> +OperatorMatcherMode EnumUtil::FromString(const char *value) { + return static_cast(StringUtil::StringToEnum(GetOperatorMatcherModeValues(), 2, "OperatorMatcherMode", value)); +} + const StringUtil::EnumStringLiteral *GetOperatorResultTypeValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(OperatorResultType::NEED_MORE_INPUT), "NEED_MORE_INPUT" }, @@ -4458,28 +4746,6 @@ OutputStream EnumUtil::FromString(const char *value) { return static_cast(StringUtil::StringToEnum(GetOutputStreamValues(), 2, "OutputStream", value)); } -const StringUtil::EnumStringLiteral *GetPEGKeywordCategoryValues() { - static constexpr StringUtil::EnumStringLiteral values[] { - { static_cast(PEGKeywordCategory::KEYWORD_NONE), "KEYWORD_NONE" }, - { static_cast(PEGKeywordCategory::KEYWORD_UNRESERVED), "KEYWORD_UNRESERVED" }, - { static_cast(PEGKeywordCategory::KEYWORD_RESERVED), "KEYWORD_RESERVED" }, - { static_cast(PEGKeywordCategory::KEYWORD_TYPE_FUNC), "KEYWORD_TYPE_FUNC" }, - { static_cast(PEGKeywordCategory::KEYWORD_COL_NAME), "KEYWORD_COL_NAME" }, - { static_cast(PEGKeywordCategory::KEYWORD_TYPE_NAME), "KEYWORD_TYPE_NAME" } - }; - return values; -} - -template<> -const char* EnumUtil::ToChars(PEGKeywordCategory value) { - return StringUtil::EnumToString(GetPEGKeywordCategoryValues(), 6, "PEGKeywordCategory", static_cast(value)); -} - -template<> -PEGKeywordCategory EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetPEGKeywordCategoryValues(), 6, "PEGKeywordCategory", value)); -} - const StringUtil::EnumStringLiteral *GetParseInfoTypeValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(ParseInfoType::ALTER_INFO), "ALTER_INFO" }, @@ -4530,6 +4796,7 @@ const StringUtil::EnumStringLiteral *GetParseResultTypeValues() { { static_cast(ParseResultType::NUMBER), "NUMBER" }, { static_cast(ParseResultType::STRING), "STRING" }, { static_cast(ParseResultType::END_OF_INPUT), "END_OF_INPUT" }, + { static_cast(ParseResultType::TOKEN), "TOKEN" }, { static_cast(ParseResultType::INVALID), "INVALID" } }; return values; @@ -4537,12 +4804,12 @@ const StringUtil::EnumStringLiteral *GetParseResultTypeValues() { template<> const char* EnumUtil::ToChars(ParseResultType value) { - return StringUtil::EnumToString(GetParseResultTypeValues(), 14, "ParseResultType", static_cast(value)); + return StringUtil::EnumToString(GetParseResultTypeValues(), 15, "ParseResultType", static_cast(value)); } template<> ParseResultType EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetParseResultTypeValues(), 14, "ParseResultType", value)); + return static_cast(StringUtil::StringToEnum(GetParseResultTypeValues(), 15, "ParseResultType", value)); } const StringUtil::EnumStringLiteral *GetParserExtensionResultTypeValues() { @@ -4659,28 +4926,6 @@ PatternOp EnumUtil::FromString(const char *value) { return static_cast(StringUtil::StringToEnum(GetPatternOpValues(), 5, "PatternOp", value)); } -const StringUtil::EnumStringLiteral *GetPendingExecutionResultValues() { - static constexpr StringUtil::EnumStringLiteral values[] { - { static_cast(PendingExecutionResult::RESULT_READY), "RESULT_READY" }, - { static_cast(PendingExecutionResult::RESULT_NOT_READY), "RESULT_NOT_READY" }, - { static_cast(PendingExecutionResult::EXECUTION_ERROR), "EXECUTION_ERROR" }, - { static_cast(PendingExecutionResult::BLOCKED), "BLOCKED" }, - { static_cast(PendingExecutionResult::NO_TASKS_AVAILABLE), "NO_TASKS_AVAILABLE" }, - { static_cast(PendingExecutionResult::EXECUTION_FINISHED), "EXECUTION_FINISHED" } - }; - return values; -} - -template<> -const char* EnumUtil::ToChars(PendingExecutionResult value) { - return StringUtil::EnumToString(GetPendingExecutionResultValues(), 6, "PendingExecutionResult", static_cast(value)); -} - -template<> -PendingExecutionResult EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetPendingExecutionResultValues(), 6, "PendingExecutionResult", value)); -} - const StringUtil::EnumStringLiteral *GetPhysicalOperatorTypeValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(PhysicalOperatorType::INVALID), "INVALID" }, @@ -5072,6 +5317,30 @@ ProfilingParameterNames EnumUtil::FromString(const char return static_cast(StringUtil::StringToEnum(GetProfilingParameterNamesValues(), 5, "ProfilingParameterNames", value)); } +const StringUtil::EnumStringLiteral *GetProgressInvariantValues() { + static constexpr StringUtil::EnumStringLiteral values[] { + { static_cast(ProgressInvariant::UNSUPPORTED_SOURCE), "UNSUPPORTED_SOURCE" }, + { static_cast(ProgressInvariant::UNSUPPORTED_SINK), "UNSUPPORTED_SINK" }, + { static_cast(ProgressInvariant::MALFORMED_SOURCE), "MALFORMED_SOURCE" }, + { static_cast(ProgressInvariant::MALFORMED_SINK), "MALFORMED_SINK" }, + { static_cast(ProgressInvariant::NON_MONOTONIC), "NON_MONOTONIC" }, + { static_cast(ProgressInvariant::INCOMPLETE), "INCOMPLETE" }, + { static_cast(ProgressInvariant::STALLED), "STALLED" }, + { static_cast(ProgressInvariant::INACCURATE), "INACCURATE" } + }; + return values; +} + +template<> +const char* EnumUtil::ToChars(ProgressInvariant value) { + return StringUtil::EnumToString(GetProgressInvariantValues(), 8, "ProgressInvariant", static_cast(value)); +} + +template<> +ProgressInvariant EnumUtil::FromString(const char *value) { + return static_cast(StringUtil::StringToEnum(GetProgressInvariantValues(), 8, "ProgressInvariant", value)); +} + const StringUtil::EnumStringLiteral *GetPushdownExtractSupportValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(PushdownExtractSupport::UNCHECKED), "UNCHECKED" }, @@ -5175,42 +5444,26 @@ QueryResultMemoryType EnumUtil::FromString(const char *va return static_cast(StringUtil::StringToEnum(GetQueryResultMemoryTypeValues(), 2, "QueryResultMemoryType", value)); } -const StringUtil::EnumStringLiteral *GetQueryResultOutputTypeValues() { - static constexpr StringUtil::EnumStringLiteral values[] { - { static_cast(QueryResultOutputType::FORCE_MATERIALIZED), "FORCE_MATERIALIZED" }, - { static_cast(QueryResultOutputType::ALLOW_STREAMING), "ALLOW_STREAMING" } - }; - return values; -} - -template<> -const char* EnumUtil::ToChars(QueryResultOutputType value) { - return StringUtil::EnumToString(GetQueryResultOutputTypeValues(), 2, "QueryResultOutputType", static_cast(value)); -} - -template<> -QueryResultOutputType EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetQueryResultOutputTypeValues(), 2, "QueryResultOutputType", value)); -} - -const StringUtil::EnumStringLiteral *GetQueryResultTypeValues() { +const StringUtil::EnumStringLiteral *GetQueryResultStateValues() { static constexpr StringUtil::EnumStringLiteral values[] { - { static_cast(QueryResultType::MATERIALIZED_RESULT), "MATERIALIZED_RESULT" }, - { static_cast(QueryResultType::STREAM_RESULT), "STREAM_RESULT" }, - { static_cast(QueryResultType::PENDING_RESULT), "PENDING_RESULT" }, - { static_cast(QueryResultType::ARROW_RESULT), "ARROW_RESULT" } + { static_cast(QueryResultState::READY), "READY" }, + { static_cast(QueryResultState::NOT_READY), "NOT_READY" }, + { static_cast(QueryResultState::BLOCKED), "BLOCKED" }, + { static_cast(QueryResultState::NO_TASKS_AVAILABLE), "NO_TASKS_AVAILABLE" }, + { static_cast(QueryResultState::FINISHED), "FINISHED" }, + { static_cast(QueryResultState::EXECUTION_ERROR), "EXECUTION_ERROR" } }; return values; } template<> -const char* EnumUtil::ToChars(QueryResultType value) { - return StringUtil::EnumToString(GetQueryResultTypeValues(), 4, "QueryResultType", static_cast(value)); +const char* EnumUtil::ToChars(QueryResultState value) { + return StringUtil::EnumToString(GetQueryResultStateValues(), 6, "QueryResultState", static_cast(value)); } template<> -QueryResultType EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetQueryResultTypeValues(), 4, "QueryResultType", value)); +QueryResultState EnumUtil::FromString(const char *value) { + return static_cast(StringUtil::StringToEnum(GetQueryResultStateValues(), 6, "QueryResultState", value)); } const StringUtil::EnumStringLiteral *GetRecoveryModeValues() { @@ -5409,6 +5662,24 @@ RenderMode EnumUtil::FromString(const char *value) { return static_cast(StringUtil::StringToEnum(GetRenderModeValues(), 2, "RenderMode", value)); } +const StringUtil::EnumStringLiteral *GetRequestSizingValues() { + static constexpr StringUtil::EnumStringLiteral values[] { + { static_cast(RequestSizing::BY_CACHE), "BY_CACHE" }, + { static_cast(RequestSizing::BY_READER), "BY_READER" } + }; + return values; +} + +template<> +const char* EnumUtil::ToChars(RequestSizing value) { + return StringUtil::EnumToString(GetRequestSizingValues(), 2, "RequestSizing", static_cast(value)); +} + +template<> +RequestSizing EnumUtil::FromString(const char *value) { + return static_cast(StringUtil::StringToEnum(GetRequestSizingValues(), 2, "RequestSizing", value)); +} + const StringUtil::EnumStringLiteral *GetRequestTypeValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(RequestType::GET_REQUEST), "GET" }, @@ -6158,19 +6429,21 @@ const StringUtil::EnumStringLiteral *GetStatementTypeValues() { { static_cast(StatementType::MERGE_INTO_STATEMENT), "MERGE_INTO_STATEMENT" }, { static_cast(StatementType::CONNECT_STATEMENT), "CONNECT_STATEMENT" }, { static_cast(StatementType::DISCONNECT_STATEMENT), "DISCONNECT_STATEMENT" }, - { static_cast(StatementType::EXTERNAL_RESOURCE_STATEMENT), "EXTERNAL_RESOURCE_STATEMENT" } + { static_cast(StatementType::EXTERNAL_RESOURCE_STATEMENT), "EXTERNAL_RESOURCE_STATEMENT" }, + { static_cast(StatementType::PASSTHROUGH_STATEMENT), "PASSTHROUGH_STATEMENT" }, + { static_cast(StatementType::ENUM_SIZE), "ENUM_SIZE" } }; return values; } template<> const char* EnumUtil::ToChars(StatementType value) { - return StringUtil::EnumToString(GetStatementTypeValues(), 34, "StatementType", static_cast(value)); + return StringUtil::EnumToString(GetStatementTypeValues(), static_cast(StatementType::ENUM_SIZE), "StatementType", static_cast(value)); } template<> StatementType EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetStatementTypeValues(), 34, "StatementType", value)); + return static_cast(StringUtil::StringToEnum(GetStatementTypeValues(), static_cast(StatementType::ENUM_SIZE), "StatementType", value)); } const StringUtil::EnumStringLiteral *GetStatisticsPropagationModeValues() { @@ -6413,29 +6686,6 @@ StrTimeSpecifier EnumUtil::FromString(const char *value) { return static_cast(StringUtil::StringToEnum(GetStrTimeSpecifierValues(), 36, "StrTimeSpecifier", value)); } -const StringUtil::EnumStringLiteral *GetStreamExecutionResultValues() { - static constexpr StringUtil::EnumStringLiteral values[] { - { static_cast(StreamExecutionResult::CHUNK_READY), "CHUNK_READY" }, - { static_cast(StreamExecutionResult::CHUNK_NOT_READY), "CHUNK_NOT_READY" }, - { static_cast(StreamExecutionResult::EXECUTION_ERROR), "EXECUTION_ERROR" }, - { static_cast(StreamExecutionResult::EXECUTION_CANCELLED), "EXECUTION_CANCELLED" }, - { static_cast(StreamExecutionResult::BLOCKED), "BLOCKED" }, - { static_cast(StreamExecutionResult::NO_TASKS_AVAILABLE), "NO_TASKS_AVAILABLE" }, - { static_cast(StreamExecutionResult::EXECUTION_FINISHED), "EXECUTION_FINISHED" } - }; - return values; -} - -template<> -const char* EnumUtil::ToChars(StreamExecutionResult value) { - return StringUtil::EnumToString(GetStreamExecutionResultValues(), 7, "StreamExecutionResult", static_cast(value)); -} - -template<> -StreamExecutionResult EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetStreamExecutionResultValues(), 7, "StreamExecutionResult", value)); -} - const StringUtil::EnumStringLiteral *GetSubqueryTypeValues() { static constexpr StringUtil::EnumStringLiteral values[] { { static_cast(SubqueryType::INVALID), "INVALID" }, @@ -6472,19 +6722,20 @@ const StringUtil::EnumStringLiteral *GetSuggestionStateValues() { { static_cast(SuggestionState::SUGGEST_TABLE_FUNCTION_NAME), "SUGGEST_TABLE_FUNCTION_NAME" }, { static_cast(SuggestionState::SUGGEST_PRAGMA_NAME), "SUGGEST_PRAGMA_NAME" }, { static_cast(SuggestionState::SUGGEST_SETTING_NAME), "SUGGEST_SETTING_NAME" }, - { static_cast(SuggestionState::SUGGEST_RESERVED_VARIABLE), "SUGGEST_RESERVED_VARIABLE" } + { static_cast(SuggestionState::SUGGEST_RESERVED_VARIABLE), "SUGGEST_RESERVED_VARIABLE" }, + { static_cast(SuggestionState::SUGGEST_BARE_LABEL), "SUGGEST_BARE_LABEL" } }; return values; } template<> const char* EnumUtil::ToChars(SuggestionState value) { - return StringUtil::EnumToString(GetSuggestionStateValues(), 14, "SuggestionState", static_cast(value)); + return StringUtil::EnumToString(GetSuggestionStateValues(), 15, "SuggestionState", static_cast(value)); } template<> SuggestionState EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetSuggestionStateValues(), 14, "SuggestionState", value)); + return static_cast(StringUtil::StringToEnum(GetSuggestionStateValues(), 15, "SuggestionState", value)); } const StringUtil::EnumStringLiteral *GetTableColumnTypeValues() { @@ -6670,12 +6921,12 @@ const StringUtil::EnumStringLiteral *GetTaskSchedulerTypeValues() { template<> const char* EnumUtil::ToChars(TaskSchedulerType value) { - return StringUtil::EnumToString(GetTaskSchedulerTypeValues(), 3, "TaskSchedulerType", static_cast(value)); + return StringUtil::EnumToString(GetTaskSchedulerTypeValues(), static_cast(TaskSchedulerType::ENUM_SIZE), "TaskSchedulerType", static_cast(value)); } template<> TaskSchedulerType EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetTaskSchedulerTypeValues(), 3, "TaskSchedulerType", value)); + return static_cast(StringUtil::StringToEnum(GetTaskSchedulerTypeValues(), static_cast(TaskSchedulerType::ENUM_SIZE), "TaskSchedulerType", value)); } const StringUtil::EnumStringLiteral *GetTemporaryBufferSizeValues() { @@ -7097,12 +7348,12 @@ const StringUtil::EnumStringLiteral *GetVariantLogicalTypeValues() { template<> const char* EnumUtil::ToChars(VariantLogicalType value) { - return StringUtil::EnumToString(GetVariantLogicalTypeValues(), 36, "VariantLogicalType", static_cast(value)); + return StringUtil::EnumToString(GetVariantLogicalTypeValues(), static_cast(VariantLogicalType::ENUM_SIZE), "VariantLogicalType", static_cast(value)); } template<> VariantLogicalType EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetVariantLogicalTypeValues(), 36, "VariantLogicalType", value)); + return static_cast(StringUtil::StringToEnum(GetVariantLogicalTypeValues(), static_cast(VariantLogicalType::ENUM_SIZE), "VariantLogicalType", value)); } const StringUtil::EnumStringLiteral *GetVariantStatsShreddingStateValues() { diff --git a/src/duckdb/src/common/enums/compression_type.cpp b/src/duckdb/src/common/enums/compression_type.cpp index 3d9dd02a4..ec816fd8c 100644 --- a/src/duckdb/src/common/enums/compression_type.cpp +++ b/src/duckdb/src/common/enums/compression_type.cpp @@ -10,7 +10,7 @@ namespace duckdb { vector ListCompressionTypes(void) { vector compression_types; - uint8_t amount_of_compression_options = (uint8_t)CompressionType::COMPRESSION_COUNT; + uint8_t amount_of_compression_options = (uint8_t)CompressionType::ENUM_SIZE; compression_types.reserve(amount_of_compression_options); for (uint8_t i = 0; i < amount_of_compression_options; i++) { compression_types.push_back(CompressionTypeToString((CompressionType)i)); diff --git a/src/duckdb/src/common/enums/statement_type.cpp b/src/duckdb/src/common/enums/statement_type.cpp index eaf414b88..54e7ba7d2 100644 --- a/src/duckdb/src/common/enums/statement_type.cpp +++ b/src/duckdb/src/common/enums/statement_type.cpp @@ -73,7 +73,10 @@ string StatementTypeToString(StatementType type) { return "DISCONNECT"; case StatementType::EXTERNAL_RESOURCE_STATEMENT: return "EXTERNAL_RESOURCE"; + case StatementType::PASSTHROUGH_STATEMENT: + return "PASSTHROUGH"; case StatementType::INVALID_STATEMENT: + case StatementType::ENUM_SIZE: break; } return "INVALID"; diff --git a/src/duckdb/src/common/exception.cpp b/src/duckdb/src/common/exception.cpp index 185bfe4f3..3ca99bdfb 100644 --- a/src/duckdb/src/common/exception.cpp +++ b/src/duckdb/src/common/exception.cpp @@ -74,7 +74,6 @@ bool Exception::InvalidatesTransaction(ExceptionType exception_type) { bool Exception::InvalidatesDatabase(ExceptionType exception_type) { switch (exception_type) { - case ExceptionType::DATA_CORRUPTION: case ExceptionType::FATAL: return true; default: diff --git a/src/duckdb/src/common/file_buffer.cpp b/src/duckdb/src/common/file_buffer.cpp index f86e3fb40..642adcb5a 100644 --- a/src/duckdb/src/common/file_buffer.cpp +++ b/src/duckdb/src/common/file_buffer.cpp @@ -113,6 +113,12 @@ void FileBuffer::Resize(BlockManager &block_manager) { void FileBuffer::Read(QueryContext context, FileHandle &handle, uint64_t location) { D_ASSERT(type != FileBufferType::TINY_BUFFER); + if (handle.file_system.IsLocalFileSystem()) { + // Pre-fault local read buffers before the kernel fills them. + for (idx_t offset = 0; offset < internal_size; offset += 4096) { + internal_buffer[offset] = 0; + } + } handle.Read(context, internal_buffer, internal_size, location); } diff --git a/src/duckdb/src/common/file_system.cpp b/src/duckdb/src/common/file_system.cpp index 81a265cbd..802417eaa 100644 --- a/src/duckdb/src/common/file_system.cpp +++ b/src/duckdb/src/common/file_system.cpp @@ -514,6 +514,14 @@ FileMetadata FileSystem::Stats(FileHandle &handle) { return metadata; } +optional FileSystem::GetStatsIfExists(const OpenFileInfo &file, optional_ptr opener) { + auto handle = OpenFile(file, FileFlags::FILE_FLAGS_READ | FileFlags::FILE_FLAGS_NULL_IF_NOT_EXISTS, opener); + if (!handle) { + return nullopt; + } + return Stats(*handle); +} + void FileSystem::Truncate(FileHandle &handle, int64_t new_size) { throw NotImplementedException("%s: Truncate is not implemented!", GetName()); } diff --git a/src/duckdb/src/common/filename_pattern.cpp b/src/duckdb/src/common/filename_pattern.cpp index 35f33f6b4..1d33dcb35 100644 --- a/src/duckdb/src/common/filename_pattern.cpp +++ b/src/duckdb/src/common/filename_pattern.cpp @@ -88,6 +88,11 @@ void FilenamePattern::SetFilenamePattern(const string &pattern) { // if we have ONLY a literal we add an offset at the end segments.emplace_back(FileNameSegmentType::OFFSET); } + if (segments.empty()) { + // an explicitly empty pattern is stored as one empty literal: an empty segment list is the + // legacy marker in deserialization, which would reconstruct the offset segment it implies + segments.emplace_back(string()); + } } string FilenamePattern::CreateFilename(FileSystem &fs, const string &path, const string &extension, diff --git a/src/duckdb/src/common/fsst.cpp b/src/duckdb/src/common/fsst.cpp index d4a0aca7b..d0126eeec 100644 --- a/src/duckdb/src/common/fsst.cpp +++ b/src/duckdb/src/common/fsst.cpp @@ -1,16 +1,31 @@ #include "duckdb/common/fsst.hpp" +#include "duckdb/common/exception.hpp" namespace duckdb { +void FSSTPrimitives::ThrowInvalidCompressedString() { + throw DataCorruptionException("Failed to decompress FSST string - escape code has no following byte"); +} + +[[noreturn]] static void ThrowFSSTDecodedStringTooLarge() { + throw DataCorruptionException("Failed to decompress FSST string - decoded size exceeds the output buffer"); +} + string FSSTPrimitives::DecompressValue(void *duckdb_fsst_decoder, const char *compressed_string, const idx_t compressed_string_len, vector &decompress_buffer) { + D_ASSERT(!decompress_buffer.empty()); auto compressed_string_ptr = reinterpret_cast(compressed_string); auto fsst_decoder = static_cast(duckdb_fsst_decoder); auto decompressed_string_size = duckdb_fsst_decompress(fsst_decoder, compressed_string_len, compressed_string_ptr, decompress_buffer.size(), decompress_buffer.data()); + if (decompressed_string_size == DUCKDB_FSST_DECOMPRESS_INVALID_INPUT) { + ThrowInvalidCompressedString(); + } - D_ASSERT(!decompress_buffer.empty()); - D_ASSERT(decompressed_string_size <= decompress_buffer.size() - 1); + // The decoder reports the full string length even when the output buffer is too small. + if (decompressed_string_size > decompress_buffer.size()) { + ThrowFSSTDecodedStringTooLarge(); + } return string(char_ptr_cast(decompress_buffer.data()), decompressed_string_size); } diff --git a/src/duckdb/src/common/gzip_file_system.cpp b/src/duckdb/src/common/gzip_file_system.cpp index ea0e70c1e..a154be2a1 100644 --- a/src/duckdb/src/common/gzip_file_system.cpp +++ b/src/duckdb/src/common/gzip_file_system.cpp @@ -69,6 +69,20 @@ idx_t GZipConsumeString(QueryContext context, FileHandle &input) { return size; } +idx_t GZipReadExact(QueryContext context, FileHandle &input, data_ptr_t buffer, idx_t size) { + idx_t total_read = 0; + while (total_read < size) { + auto read_count = input.Read(context, buffer + total_read, size - total_read); + if (read_count <= 0) { + break; + } + total_read += NumericCast(read_count); + } + return total_read; +} + +enum class GZipReadState : uint8_t { DEFLATE, FOOTER, HEADER, EXTRA_LENGTH, EXTRA_DATA, FILE_NAME }; + struct MiniZStreamWrapper : public StreamWrapper { ~MiniZStreamWrapper() override; @@ -77,17 +91,27 @@ struct MiniZStreamWrapper : public StreamWrapper { bool writing = false; duckdb_miniz::mz_ulong crc; idx_t total_size; + GZipReadState read_state = GZipReadState::DEFLATE; + idx_t state_bytes_remaining = 0; + idx_t gzip_header_size = 0; + idx_t gzip_header_bytes = 0; + uint8_t gzip_header[GZIP_HEADER_MINSIZE]; + idx_t gzip_xlen_bytes = 0; + uint8_t gzip_xlen[2]; public: void Initialize(QueryContext context, CompressedFile &file, bool write) override; bool Read(StreamData &stream_data) override; + void FinalizeRead(StreamData &stream_data) override; void Write(CompressedFile &file, StreamData &stream_data, data_ptr_t buffer, int64_t nr_bytes) override; void Close() override; void AbortWrite() override; void FlushStream() const; + bool ReadNextMemberHeader(StreamData &stream_data); + void InitializeInflator(); }; MiniZStreamWrapper::~MiniZStreamWrapper() { @@ -136,13 +160,15 @@ void MiniZStreamWrapper::Initialize(QueryContext context, CompressedFile &file, } } else { idx_t data_start = GZIP_HEADER_MINSIZE; - auto read_count = file.child_handle->Read(context, gzip_hdr, GZIP_HEADER_MINSIZE); - GZipFileSystem::VerifyGZIPHeader(gzip_hdr, NumericCast(read_count), &file); + auto read_count = GZipReadExact(context, *file.child_handle, gzip_hdr, GZIP_HEADER_MINSIZE); + GZipFileSystem::VerifyGZIPHeader(gzip_hdr, read_count, &file); // Skip over the extra field if necessary if (gzip_hdr[3] & GZIP_FLAG_EXTRA) { uint8_t gzip_xlen[2]; file.child_handle->Seek(data_start); - file.child_handle->Read(context, gzip_xlen, 2); + if (GZipReadExact(context, *file.child_handle, gzip_xlen, 2) != 2) { + throw IOException("Input is not a GZIP stream: %s", file.path); + } auto xlen = NumericCast((uint8_t)gzip_xlen[0] | (uint8_t)gzip_xlen[1] << 8); data_start += xlen + 2; } @@ -153,58 +179,124 @@ void MiniZStreamWrapper::Initialize(QueryContext context, CompressedFile &file, } file.child_handle->Seek(data_start); // stream is now set to beginning of payload data - auto ret = duckdb_miniz::mz_inflateInit2(mz_stream_ptr.get(), -MZ_DEFAULT_WINDOW_BITS); - if (ret != duckdb_miniz::MZ_OK) { - throw InternalException("Failed to initialize miniz"); - } + InitializeInflator(); } } -bool MiniZStreamWrapper::Read(StreamData &sd) { - // Handling for the concatenated files - if (sd.refresh) { - auto available = static_cast(sd.in_buff_end - sd.in_buff_start); - if (available <= GZIP_FOOTER_SIZE) { - // Only footer is available so we just close and return finished - Close(); - return true; - } +void MiniZStreamWrapper::InitializeInflator() { + auto ret = duckdb_miniz::mz_inflateInit2(mz_stream_ptr.get(), -MZ_DEFAULT_WINDOW_BITS); + if (ret != duckdb_miniz::MZ_OK) { + throw InternalException("Failed to initialize miniz"); + } +} - sd.refresh = false; - auto body_ptr = sd.in_buff_start + GZIP_FOOTER_SIZE; - uint8_t gzip_hdr[GZIP_HEADER_MINSIZE]; - memcpy(gzip_hdr, body_ptr, GZIP_HEADER_MINSIZE); - GZipFileSystem::VerifyGZIPHeader(gzip_hdr, GZIP_HEADER_MINSIZE, nullptr); - body_ptr += GZIP_HEADER_MINSIZE; - if (gzip_hdr[3] & GZIP_FLAG_EXTRA) { - auto xlen = NumericCast((uint8_t)*body_ptr | (uint8_t) * (body_ptr + 1) << 8); - body_ptr += xlen + 2; - if (GZIP_FOOTER_SIZE + GZIP_HEADER_MINSIZE + 2 + xlen >= GZIP_HEADER_MAXSIZE) { - throw InternalException("Extra field resulting in GZIP header larger than defined maximum (%d)", - GZIP_HEADER_MAXSIZE); +bool MiniZStreamWrapper::ReadNextMemberHeader(StreamData &sd) { + while (sd.in_buff_start < sd.in_buff_end) { + auto available = NumericCast(sd.in_buff_end - sd.in_buff_start); + switch (read_state) { + case GZipReadState::FOOTER: { + auto consume_count = MinValue(available, state_bytes_remaining); + sd.in_buff_start += consume_count; + state_bytes_remaining -= consume_count; + if (state_bytes_remaining > 0) { + return false; } + read_state = GZipReadState::HEADER; + gzip_header_size = GZIP_FOOTER_SIZE; + gzip_header_bytes = 0; + break; } - if (gzip_hdr[3] & GZIP_FLAG_NAME) { - char c; - do { - c = UnsafeNumericCast(*body_ptr); - body_ptr++; - } while (c != '\0' && body_ptr < sd.in_buff_end); - if (static_cast(body_ptr - sd.in_buff_start) >= GZIP_HEADER_MAXSIZE) { - throw InternalException("Filename resulting in GZIP header larger than defined maximum (%d)", - GZIP_HEADER_MAXSIZE); + case GZipReadState::HEADER: { + auto consume_count = MinValue(available, GZIP_HEADER_MINSIZE - gzip_header_bytes); + memcpy(gzip_header + gzip_header_bytes, sd.in_buff_start, consume_count); + sd.in_buff_start += consume_count; + gzip_header_bytes += consume_count; + gzip_header_size += consume_count; + if (gzip_header_bytes < GZIP_HEADER_MINSIZE) { + return false; } + GZipFileSystem::VerifyGZIPHeader(gzip_header, GZIP_HEADER_MINSIZE, nullptr); + if (gzip_header[3] & GZIP_FLAG_EXTRA) { + read_state = GZipReadState::EXTRA_LENGTH; + gzip_xlen_bytes = 0; + } else if (gzip_header[3] & GZIP_FLAG_NAME) { + read_state = GZipReadState::FILE_NAME; + } else { + read_state = GZipReadState::DEFLATE; + return true; + } + break; + } + case GZipReadState::EXTRA_LENGTH: { + auto consume_count = MinValue(available, 2 - gzip_xlen_bytes); + memcpy(gzip_xlen + gzip_xlen_bytes, sd.in_buff_start, consume_count); + sd.in_buff_start += consume_count; + gzip_xlen_bytes += consume_count; + gzip_header_size += consume_count; + if (gzip_xlen_bytes < 2) { + return false; + } + state_bytes_remaining = NumericCast((uint8_t)gzip_xlen[0] | (uint8_t)gzip_xlen[1] << 8); + if (gzip_header_size + state_bytes_remaining >= GZIP_HEADER_MAXSIZE) { + throw DataCorruptionException("Extra field resulting in GZIP header larger than defined maximum (%d)", + GZIP_HEADER_MAXSIZE); + } + read_state = GZipReadState::EXTRA_DATA; + break; } - sd.in_buff_start = body_ptr; - if (sd.in_buff_end - sd.in_buff_start < 1) { - Close(); + case GZipReadState::EXTRA_DATA: { + auto consume_count = MinValue(available, state_bytes_remaining); + sd.in_buff_start += consume_count; + state_bytes_remaining -= consume_count; + gzip_header_size += consume_count; + if (state_bytes_remaining > 0) { + return false; + } + if (gzip_header[3] & GZIP_FLAG_NAME) { + read_state = GZipReadState::FILE_NAME; + } else { + read_state = GZipReadState::DEFLATE; + return true; + } + break; + } + case GZipReadState::FILE_NAME: + while (sd.in_buff_start < sd.in_buff_end) { + auto c = *sd.in_buff_start++; + gzip_header_size++; + if (gzip_header_size >= GZIP_HEADER_MAXSIZE) { + throw DataCorruptionException("Filename resulting in GZIP header larger than defined maximum (%d)", + GZIP_HEADER_MAXSIZE); + } + if (c == '\0') { + read_state = GZipReadState::DEFLATE; + return true; + } + } + return false; + case GZipReadState::DEFLATE: return true; } - duckdb_miniz::mz_inflateEnd(mz_stream_ptr.get()); - auto sta = duckdb_miniz::mz_inflateInit2(mz_stream_ptr.get(), -MZ_DEFAULT_WINDOW_BITS); - if (sta != duckdb_miniz::MZ_OK) { - throw InternalException("Failed to initialize miniz"); + } + return false; +} + +bool MiniZStreamWrapper::Read(StreamData &sd) { + // Handling for the concatenated files + if (sd.refresh && read_state == GZipReadState::DEFLATE) { + read_state = GZipReadState::FOOTER; + state_bytes_remaining = GZIP_FOOTER_SIZE; + } + if (read_state != GZipReadState::DEFLATE) { + if (!ReadNextMemberHeader(sd)) { + return false; } + sd.refresh = false; + duckdb_miniz::mz_inflateEnd(mz_stream_ptr.get()); + InitializeInflator(); + } + if (sd.in_buff_start == sd.in_buff_end) { + return false; } // actually decompress @@ -231,6 +323,13 @@ bool MiniZStreamWrapper::Read(StreamData &sd) { return false; } +void MiniZStreamWrapper::FinalizeRead(StreamData &) { + if (read_state == GZipReadState::HEADER && gzip_header_bytes == 0) { + return; + } + throw IOException("Unexpected end of GZIP stream: %s", file->path); +} + void MiniZStreamWrapper::Write(CompressedFile &file, StreamData &sd, data_ptr_t uncompressed_data, int64_t uncompressed_size) { // update the src and the total size @@ -403,11 +502,12 @@ string GZipFileSystem::UncompressGZIPString(const char *data, idx_t size) { } if (gzip_hdr[3] & GZIP_FLAG_NAME) { - char c; - do { - c = *body_ptr; - body_ptr++; - } while (c != '\0' && static_cast(body_ptr - data) < size); + auto end_ptr = data + size; + while (body_ptr < end_ptr) { + if (*body_ptr++ == '\0') { + break; + } + } } // stream is now set to beginning of payload data @@ -428,6 +528,7 @@ string GZipFileSystem::UncompressGZIPString(const char *data, idx_t size) { mz_stream_ptr->avail_out = sizeof(decompress_buffer); status = mz_inflate(mz_stream_ptr.get(), duckdb_miniz::MZ_NO_FLUSH); if (status != duckdb_miniz::MZ_STREAM_END && status != duckdb_miniz::MZ_OK) { + duckdb_miniz::mz_inflateEnd(mz_stream_ptr.get()); throw IOException("Failed to uncompress"); } decompressed.append(char_ptr_cast(decompress_buffer), mz_stream_ptr->total_out - decompressed.size()); diff --git a/src/duckdb/src/common/hive_partitioning.cpp b/src/duckdb/src/common/hive_partitioning.cpp index bff0d0c5b..c103ab2e9 100644 --- a/src/duckdb/src/common/hive_partitioning.cpp +++ b/src/duckdb/src/common/hive_partitioning.cpp @@ -2,6 +2,7 @@ #include "duckdb/common/uhugeint.hpp" #include "duckdb/execution/expression_executor.hpp" +#include "duckdb/function/table_function.hpp" #include "duckdb/planner/expression/bound_columnref_expression.hpp" #include "duckdb/planner/expression/bound_constant_expression.hpp" #include "duckdb/planner/expression/bound_reference_expression.hpp" @@ -28,7 +29,7 @@ GetKnownColumnValues(const string &filename, const HivePartitioningFilterInfo &f auto &column_map = filter_info.column_map; if (filter_info.filename_enabled) { - auto lookup_column_id = column_map.find("filename"); + auto lookup_column_id = column_map.find(filter_info.filename_column); if (lookup_column_id != column_map.end()) { result.insert(make_pair(lookup_column_id->second, PartitioningColumnValue(filename))); } @@ -37,7 +38,7 @@ GetKnownColumnValues(const string &filename, const HivePartitioningFilterInfo &f if (filter_info.hive_enabled) { auto partitions = HivePartitioning::Parse(filename); for (auto &partition : partitions) { - auto lookup_column_id = column_map.find(partition.first); + auto lookup_column_id = column_map.find(Identifier(partition.first)); if (lookup_column_id != column_map.end()) { result.insert( make_pair(lookup_column_id->second, PartitioningColumnValue(partition.first, partition.second))); @@ -192,6 +193,10 @@ void HivePartitioning::ApplyFiltersToFileList(ClientContext &context, vector>(); + } + for (idx_t i = 0; i < files.size(); i++) { auto &file = files[i]; bool should_prune_file = false; @@ -229,6 +234,35 @@ void HivePartitioning::ApplyFiltersToFileList(ClientContext &context, vector= pruned_filters.size()); + for (idx_t i = 0; i < filters.size() && info.extra_info.file_filter_expressions; i++) { + if (have_preserved_filter[i]) { + continue; + } + auto retained = filters[i]->Copy(); + bool representable = true; + ExpressionIterator::VisitExpressionMutable( + retained, [&](auto &column, unique_ptr &) { + auto binding = column.Binding(); + if (column.Depth() != 0 || binding.table_index != table_index || + binding.column_index.GetIndex() >= info.column_indexes.size()) { + representable = false; + return; + } + auto &index = info.column_indexes[binding.column_index.GetIndex()]; + if (index.IsVirtualColumn() || index.IsPushdownExtract() || index.HasType()) { + representable = false; + return; + } + column.BindingMutable() = ColumnBinding(TableIndex(ExtraOperatorInfo::FILE_FILTER_TABLE_INDEX), + ProjectionIndex(index.GetPrimaryIndex())); + }); + if (!representable) { + info.extra_info.file_filter_expressions.reset(); + break; + } + info.extra_info.file_filter_expressions->push_back(std::move(retained)); + } + info.extra_info.total_files = files.size(); info.extra_info.filtered_files = pruned_files.size(); diff --git a/src/duckdb/src/common/identifier.cpp b/src/duckdb/src/common/identifier.cpp index 2907f0fbc..69bda856b 100644 --- a/src/duckdb/src/common/identifier.cpp +++ b/src/duckdb/src/common/identifier.cpp @@ -1,11 +1,16 @@ #include "duckdb/common/identifier.hpp" #include "duckdb/common/string_util.hpp" +#include "duckdb/common/types/uuid.hpp" #include namespace duckdb { +Identifier GenerateInternalName(const char *prefix) { + return Identifier(prefix + UUID::ToString(UUID::GenerateRandomUUID())); +} + bool Identifier::StartsWith(const string &prefix) const { return StringUtil::CIStartsWith(value, prefix); } diff --git a/src/duckdb/src/common/json_document.cpp b/src/duckdb/src/common/json_document.cpp index 495b78590..dc6bdd275 100644 --- a/src/duckdb/src/common/json_document.cpp +++ b/src/duckdb/src/common/json_document.cpp @@ -107,6 +107,10 @@ bool JSONValue::IsInteger() const { return val && yyjson_is_int(val); } +bool JSONValue::IsNumber() const { + return val && yyjson_is_num(val); +} + string JSONValue::GetString() const { const auto str = yyjson_get_str(val); const auto len = yyjson_get_len(val); @@ -129,6 +133,10 @@ double JSONValue::GetDouble() const { return unsafe_yyjson_get_real(val); } +double JSONValue::GetNumber() const { + return unsafe_yyjson_get_num(val); +} + void JSONValue::IterateArray(const std::function &callback) const { size_t idx, max; yyjson_val *child; diff --git a/src/duckdb/src/common/local_file_system.cpp b/src/duckdb/src/common/local_file_system.cpp index d40cf0966..664659e76 100644 --- a/src/duckdb/src/common/local_file_system.cpp +++ b/src/duckdb/src/common/local_file_system.cpp @@ -1,6 +1,7 @@ #include "duckdb/common/local_file_system.hpp" #include "duckdb/common/checksum.hpp" +#include "duckdb/common/enums/file_sync_mode.hpp" #include "duckdb/common/exception.hpp" #include "duckdb/common/file_opener.hpp" #include "duckdb/common/helper.hpp" @@ -17,7 +18,9 @@ #include "duckdb/logging/log_manager.hpp" #include "duckdb/common/multi_file/multi_file_list.hpp" +#include #include +#include #include #include #include @@ -62,12 +65,9 @@ namespace duckdb { bool LocalFileSystem::FileExists(const string &filename, optional_ptr opener) { if (!filename.empty()) { auto normalized_file = ExpandPath(filename, opener); - if (access(normalized_file.c_str(), 0) == 0) { - struct stat status; - stat(normalized_file.c_str(), &status); - if (S_ISREG(status.st_mode)) { - return true; - } + struct stat status; + if (stat(normalized_file.c_str(), &status) == 0 && S_ISREG(status.st_mode)) { + return true; } } // if any condition fails @@ -77,12 +77,9 @@ bool LocalFileSystem::FileExists(const string &filename, optional_ptr opener) { if (!filename.empty()) { auto normalized_file = ExpandPath(filename, opener); - if (access(normalized_file.c_str(), 0) == 0) { - struct stat status; - stat(normalized_file.c_str(), &status); - if (S_ISFIFO(status.st_mode) || S_ISCHR(status.st_mode)) { - return true; - } + struct stat status; + if (stat(normalized_file.c_str(), &status) == 0 && (S_ISFIFO(status.st_mode) || S_ISCHR(status.st_mode))) { + return true; } } // if any condition fails @@ -150,7 +147,9 @@ static std::wstring NormalizePathAndConvertToUnicode(FileSystem &fs, const strin } if (abs_path.find(L"\\\\") == 0) { - return WINDOWS_UNC_LONG_PATH_PREFIX + abs_path; + // Extended UNC paths use "\\?\UNC\server\share", so remove the original leading "\\". + // See https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#namespaces + return WINDOWS_UNC_LONG_PATH_PREFIX + abs_path.substr(2); } return WINDOWS_LOCAL_LONG_PATH_PREFIX + abs_path; @@ -159,24 +158,18 @@ static std::wstring NormalizePathAndConvertToUnicode(FileSystem &fs, const strin bool LocalFileSystem::FileExists(const string &filename, optional_ptr opener) { auto unicode_path = NormalizePathAndConvertToUnicode(*this, filename, opener); const wchar_t *wpath = unicode_path.c_str(); - if (_waccess(wpath, 0) == 0) { - struct _stati64 status; // typos:ignore - _wstati64(wpath, &status); - if (status.st_mode & S_IFREG) { - return true; - } + struct _stati64 status; // typos:ignore + if (_wstati64(wpath, &status) == 0 && (status.st_mode & S_IFREG)) { + return true; } return false; } bool LocalFileSystem::IsPipe(const string &filename, optional_ptr opener) { auto unicode_path = NormalizePathAndConvertToUnicode(*this, filename, opener); const wchar_t *wpath = unicode_path.c_str(); - if (_waccess(wpath, 0) == 0) { - struct _stati64 status; // typos:ignore - _wstati64(wpath, &status); - if (status.st_mode & _S_IFCHR) { - return true; - } + struct _stati64 status; // typos:ignore + if (_wstati64(wpath, &status) == 0 && (status.st_mode & _S_IFCHR)) { + return true; } return false; } @@ -627,6 +620,26 @@ FileMetadata LocalFileSystem::Stats(FileHandle &handle) { return file_metadata; } +optional LocalFileSystem::GetStatsIfExists(const OpenFileInfo &file, optional_ptr opener) { + const auto &path_p = file.path; + if (path_p.empty()) { + return nullopt; + } + auto path = ExpandPath(path_p, opener); + struct stat status; + if (stat(path.c_str(), &status) != 0) { + auto retained_errno = errno; + if (retained_errno == ENOENT || retained_errno == ENOTDIR) { + return nullopt; + } + throw IOException({{"errno", std::to_string(retained_errno)}}, "Failed to get stats for path \"%s\": %s", + path_p, strerror(retained_errno)); + } + auto file_metadata = StatsFromStruct(status); + file_metadata.version_tag = VersionTagFromMetadata(file_metadata); + return file_metadata; +} + void LocalFileSystem::Truncate(FileHandle &handle, int64_t new_size) { int fd = handle.Cast().fd; if (ftruncate(fd, new_size) != 0) { @@ -640,12 +653,9 @@ bool LocalFileSystem::DirectoryExists(const string &directory, optional_ptr().fd; + auto &unix_handle = handle.Cast(); + auto fsync_mode = unix_handle.db ? Settings::Get(*unix_handle.db) : FileSyncMode::STANDARD; + if (fsync_mode == FileSyncMode::NONE) { + return; + } + int fd = unix_handle.fd; -#if HAVE_FULLFSYNC +#ifdef F_FULLFSYNC // On macOS and iOS, fsync() doesn't guarantee durability past power failures. fcntl(F_FULLFSYNC) is required for // that purpose. Some filesystems don't support fcntl(F_FULLFSYNC), and require a fallback to fsync(). - if (::fcntl(fd, F_FULLFSYNC) == 0) { + if (fsync_mode == FileSyncMode::FULL && ::fcntl(fd, F_FULLFSYNC) == 0) { return; } -#endif // HAVE_FULLFSYNC +#endif // F_FULLFSYNC -#if HAVE_FDATASYNC +#ifdef DUCKDB_HAVE_FDATASYNC bool sync_success = ::fdatasync(fd) == 0; #else bool sync_success = ::fsync(fd) == 0; -#endif // HAVE_FDATASYNC +#endif // DUCKDB_HAVE_FDATASYNC if (sync_success) { return; @@ -1121,8 +1136,9 @@ static FileMetadata StatsFromDirInfo(const FILE_ID_BOTH_DIR_INFO &entry) { struct WindowsFileHandle : public FileHandle { public: - WindowsFileHandle(FileSystem &file_system, string path, HANDLE fd, FileOpenFlags flags) - : FileHandle(file_system, path, flags), position(0), fd(fd) { + WindowsFileHandle(FileSystem &file_system, string path, HANDLE fd, FileOpenFlags flags, + optional_ptr db) + : FileHandle(file_system, path, flags), position(0), fd(fd), db(db) { } ~WindowsFileHandle() override { Close(); @@ -1130,6 +1146,7 @@ struct WindowsFileHandle : public FileHandle { idx_t position; HANDLE fd; + optional_ptr db; public: void Close() override { @@ -1268,7 +1285,12 @@ unique_ptr LocalFileSystem::OpenFile(const string &path_p, FileOpenF HANDLE hFile = CreateFileW(unicode_path.c_str(), desired_access, share_mode, NULL, creation_disposition, flags_and_attributes, NULL); if (hFile == INVALID_HANDLE_VALUE) { - if (flags.ReturnNullIfNotExists() && GetLastError() == ERROR_FILE_NOT_FOUND) { + auto last_error = GetLastError(); + if (flags.ReturnNullIfNotExists() && last_error == ERROR_FILE_NOT_FOUND) { + return nullptr; + } + // Match POSIX EEXIST handling for exclusive creation. + if (flags.ReturnNullIfExists() && (last_error == ERROR_FILE_EXISTS || last_error == ERROR_ALREADY_EXISTS)) { return nullptr; } auto error = LocalFileSystem::GetLastErrorAsString(); @@ -1280,7 +1302,7 @@ unique_ptr LocalFileSystem::OpenFile(const string &path_p, FileOpenF auto abs_path = WindowsUtil::UnicodeToUTF8(unicode_path.c_str()); throw IOException("Cannot open file \"%s\": %s%s", abs_path, error, extended_error); } - auto handle = make_uniq(*this, path.c_str(), hFile, flags); + auto handle = make_uniq(*this, path.c_str(), hFile, flags, FileOpener::TryGetDatabase(opener)); if (flags.OpenForAppending()) { auto file_size = GetFileSize(*handle); SetFilePointer(*handle, file_size); @@ -1596,19 +1618,64 @@ bool LocalFileSystem::ListFilesExtended(const string &directory, } void LocalFileSystem::FileSync(FileHandle &handle) { - HANDLE hFile = handle.Cast().fd; + auto &windows_handle = handle.Cast(); + auto fsync_mode = windows_handle.db ? Settings::Get(*windows_handle.db) : FileSyncMode::STANDARD; + if (fsync_mode == FileSyncMode::NONE) { + return; + } + HANDLE hFile = windows_handle.fd; if (FlushFileBuffers(hFile) == 0) { throw IOException("Could not flush file handle to disk!"); } } +static bool TryMoveFileWithPosixSemantics(HANDLE source_handle, const std::wstring &target) { + constexpr auto file_rename_info_ex = static_cast(22); // FileRenameInfoEx + const auto file_name_length = target.size() * sizeof(WCHAR); + const auto rename_info_size = offsetof(FILE_RENAME_INFO, FileName) + file_name_length + sizeof(WCHAR); + const auto rename_info_size_dw = NumericCast(rename_info_size); + auto rename_info_buffer = make_uniq_array(rename_info_size); + auto rename_info = reinterpret_cast(rename_info_buffer.get()); + rename_info->Flags = FILE_RENAME_FLAG_REPLACE_IF_EXISTS | FILE_RENAME_FLAG_POSIX_SEMANTICS; + rename_info->RootDirectory = nullptr; + rename_info->FileNameLength = NumericCast(file_name_length); + std::copy(target.c_str(), target.c_str() + target.size() + 1, rename_info->FileName); + + return SetFileInformationByHandle(source_handle, file_rename_info_ex, rename_info, rename_info_size_dw); +} + void LocalFileSystem::MoveFile(const string &source, const string &target, optional_ptr opener) { auto source_unicode = NormalizePathAndConvertToUnicode(*this, source, opener); auto target_unicode = NormalizePathAndConvertToUnicode(*this, target, opener); - DWORD flags = MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH; + // FileRenameInfoEx renames the file identified by a handle opened with DELETE access. + // See https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_rename_info + constexpr DWORD delete_access = 0x00010000L; // DELETE + auto raw_source_handle = + CreateFileW(source_unicode.c_str(), delete_access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr); + if (raw_source_handle == INVALID_HANDLE_VALUE) { + auto error = GetLastErrorAsString(); + throw IOException("Could not move file \"%s\" to \"%s\": failed to open source file: %s", source, target, + error); + } + unique_ptr source_handle(raw_source_handle, CloseHandle); + + if (TryMoveFileWithPosixSemantics(source_handle.get(), target_unicode)) { + return; + } + auto error_code = GetLastError(); + source_handle.reset(); + + if (error_code != ERROR_INVALID_PARAMETER && error_code != ERROR_NOT_SUPPORTED && + error_code != ERROR_INVALID_FUNCTION) { + SetLastError(error_code); + throw IOException("Could not move file \"%s\" to \"%s\": %s", source, target, GetLastErrorAsString()); + } + + DWORD flags = MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH; if (!MoveFileExW(source_unicode.c_str(), target_unicode.c_str(), flags)) { - throw IOException("Could not move file: %s", GetLastErrorAsString()); + throw IOException("Could not move file \"%s\" to \"%s\": %s", source, target, GetLastErrorAsString()); } } @@ -1624,6 +1691,29 @@ FileMetadata LocalFileSystem::Stats(FileHandle &handle) { return file_metadata; } +optional LocalFileSystem::GetStatsIfExists(const OpenFileInfo &file, optional_ptr opener) { + const auto &path_p = file.path; + if (path_p.empty()) { + return nullopt; + } + auto unicode_path = NormalizePathAndConvertToUnicode(*this, path_p, opener); + auto raw_handle = CreateFileW(unicode_path.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (raw_handle == INVALID_HANDLE_VALUE) { + auto error_code = GetLastError(); + if (error_code == ERROR_FILE_NOT_FOUND || error_code == ERROR_PATH_NOT_FOUND) { + return nullopt; + } + SetLastError(error_code); + throw IOException("Failed to get stats for path \"%s\": %s", path_p, GetLastErrorAsString()); + } + unique_ptr handle(raw_handle, CloseHandle); + + auto file_metadata = StatsInternal(handle.get(), path_p); + file_metadata.version_tag = VersionTagFromMetadata(file_metadata); + return file_metadata; +} + bool LocalFileSystem::TryCanonicalizeExistingPath(string &input) { auto unicode_path = ConvertPathToUnicode(input); HANDLE handle = CreateFileW(unicode_path.c_str(), diff --git a/src/duckdb/src/common/extra_type_info.cpp b/src/duckdb/src/common/logical_type_info.cpp similarity index 63% rename from src/duckdb/src/common/extra_type_info.cpp rename to src/duckdb/src/common/logical_type_info.cpp index a943c719b..0599cfcaa 100644 --- a/src/duckdb/src/common/extra_type_info.cpp +++ b/src/duckdb/src/common/logical_type_info.cpp @@ -1,5 +1,5 @@ -#include "duckdb/common/extra_type_info.hpp" -#include "duckdb/common/extra_type_info/enum_type_info.hpp" +#include "duckdb/common/logical_type_info.hpp" +#include "duckdb/common/logical_type_info/enum_type_info.hpp" #include "duckdb/common/serializer/deserializer.hpp" #include "duckdb/common/enum_util.hpp" #include "duckdb/common/numeric_utils.hpp" @@ -79,80 +79,53 @@ bool ExtensionTypeInfo::Equals(optional_ptr lhs, optional_ptr //===--------------------------------------------------------------------===// // Extra Type Info //===--------------------------------------------------------------------===// -ExtraTypeInfo::ExtraTypeInfo(ExtraTypeInfoType type) : type(type) { +LogicalTypeInfo::LogicalTypeInfo(LogicalTypeInfoType type) noexcept : type(type) { } -ExtraTypeInfo::ExtraTypeInfo(ExtraTypeInfoType type, string alias) : type(type), alias(std::move(alias)) { +LogicalTypeInfo::LogicalTypeInfo(LogicalTypeInfoType type, string alias) : type(type), alias(std::move(alias)) { } -ExtraTypeInfo::~ExtraTypeInfo() { +LogicalTypeInfo::~LogicalTypeInfo() { } -ExtraTypeInfo::ExtraTypeInfo(const ExtraTypeInfo &other) : type(other.type), alias(other.alias) { +LogicalTypeInfo::LogicalTypeInfo(const LogicalTypeInfo &other) : type(other.type), alias(other.alias) { if (other.extension_info) { extension_info = make_uniq(*other.extension_info); } } -ExtraTypeInfo &ExtraTypeInfo::operator=(const ExtraTypeInfo &other) { - type = other.type; - alias = other.alias; - if (other.extension_info) { - extension_info = make_uniq(*other.extension_info); - } - return *this; -} - -shared_ptr ExtraTypeInfo::Copy() const { - return shared_ptr(new ExtraTypeInfo(*this)); +unique_ptr LogicalTypeInfo::Copy() const { + return unique_ptr(new LogicalTypeInfo(*this)); } -shared_ptr ExtraTypeInfo::DeepCopy() const { - return Copy(); -} - -void ExtraTypeInfo::CopyBaseInfo(ExtraTypeInfo &target) const { +void LogicalTypeInfo::CopyBaseInfo(LogicalTypeInfo &target) const { target.alias = alias; if (extension_info) { target.extension_info = make_uniq(*extension_info); } } -bool ExtraTypeInfo::Equals(const ExtraTypeInfo *other_p) const { - if (type == ExtraTypeInfoType::INVALID_TYPE_INFO || type == ExtraTypeInfoType::STRING_TYPE_INFO || - type == ExtraTypeInfoType::GENERIC_TYPE_INFO) { - if (!other_p) { - if (!alias.empty()) { - return false; - } - if (extension_info) { - return false; - } - //! We only need to compare aliases when both types have them in this case - return true; - } - if (alias != other_p->alias) { - return false; - } - if (!ExtensionTypeInfo::Equals(extension_info, other_p->extension_info)) { - return false; - } - return true; - } - if (!other_p) { +//! Infos that carry no parameters that affect equality - collations do not affect equality +static bool HasNoEqualityParameters(LogicalTypeInfoType type) { + return type == LogicalTypeInfoType::INVALID_TYPE_INFO || type == LogicalTypeInfoType::STRING_TYPE_INFO || + type == LogicalTypeInfoType::GENERIC_TYPE_INFO; +} + +bool LogicalTypeInfo::Equals(const LogicalTypeInfo &other) const { + if (alias != other.alias) { return false; } - if (type != other_p->type) { + if (!ExtensionTypeInfo::Equals(extension_info, other.extension_info)) { return false; } - if (alias != other_p->alias) { - return false; + if (HasNoEqualityParameters(type) && HasNoEqualityParameters(other.type)) { + return true; } - if (!ExtensionTypeInfo::Equals(extension_info, other_p->extension_info)) { + if (type != other.type) { return false; } - return EqualsInternal(other_p); + return EqualsInternal(&other); } -bool ExtraTypeInfo::EqualsInternal(const ExtraTypeInfo *other_p) const { +bool LogicalTypeInfo::EqualsInternal(const LogicalTypeInfo *other_p) const { // Do nothing return true; } @@ -160,105 +133,85 @@ bool ExtraTypeInfo::EqualsInternal(const ExtraTypeInfo *other_p) const { //===--------------------------------------------------------------------===// // Decimal Type Info //===--------------------------------------------------------------------===// -DecimalTypeInfo::DecimalTypeInfo() : ExtraTypeInfo(ExtraTypeInfoType::DECIMAL_TYPE_INFO) { +DecimalTypeInfo::DecimalTypeInfo() : LogicalTypeInfo(LogicalTypeInfoType::DECIMAL_TYPE_INFO) { } DecimalTypeInfo::DecimalTypeInfo(uint8_t width_p, uint8_t scale_p) - : ExtraTypeInfo(ExtraTypeInfoType::DECIMAL_TYPE_INFO), width(width_p), scale(scale_p) { + : LogicalTypeInfo(LogicalTypeInfoType::DECIMAL_TYPE_INFO), width(width_p), scale(scale_p) { D_ASSERT(width_p >= scale_p); } -bool DecimalTypeInfo::EqualsInternal(const ExtraTypeInfo *other_p) const { +bool DecimalTypeInfo::EqualsInternal(const LogicalTypeInfo *other_p) const { auto &other = other_p->Cast(); return width == other.width && scale == other.scale; } -shared_ptr DecimalTypeInfo::Copy() const { - return make_shared_ptr(*this); +unique_ptr DecimalTypeInfo::Copy() const { + return make_uniq(*this); } //===--------------------------------------------------------------------===// // String Type Info //===--------------------------------------------------------------------===// -StringTypeInfo::StringTypeInfo() : ExtraTypeInfo(ExtraTypeInfoType::STRING_TYPE_INFO) { +StringTypeInfo::StringTypeInfo() : LogicalTypeInfo(LogicalTypeInfoType::STRING_TYPE_INFO) { } StringTypeInfo::StringTypeInfo(string collation_p) - : ExtraTypeInfo(ExtraTypeInfoType::STRING_TYPE_INFO), collation(std::move(collation_p)) { + : LogicalTypeInfo(LogicalTypeInfoType::STRING_TYPE_INFO), collation(std::move(collation_p)) { } -bool StringTypeInfo::EqualsInternal(const ExtraTypeInfo *other_p) const { +bool StringTypeInfo::EqualsInternal(const LogicalTypeInfo *other_p) const { // collation info has no impact on equality return true; } -shared_ptr StringTypeInfo::Copy() const { - return make_shared_ptr(*this); +unique_ptr StringTypeInfo::Copy() const { + return make_uniq(*this); } //===--------------------------------------------------------------------===// // List Type Info //===--------------------------------------------------------------------===// -ListTypeInfo::ListTypeInfo() : ExtraTypeInfo(ExtraTypeInfoType::LIST_TYPE_INFO) { +ListTypeInfo::ListTypeInfo() : LogicalTypeInfo(LogicalTypeInfoType::LIST_TYPE_INFO) { } ListTypeInfo::ListTypeInfo(LogicalType child_type_p) - : ExtraTypeInfo(ExtraTypeInfoType::LIST_TYPE_INFO), child_type(std::move(child_type_p)) { + : LogicalTypeInfo(LogicalTypeInfoType::LIST_TYPE_INFO), child_type(std::move(child_type_p)) { } -bool ListTypeInfo::EqualsInternal(const ExtraTypeInfo *other_p) const { +bool ListTypeInfo::EqualsInternal(const LogicalTypeInfo *other_p) const { auto &other = other_p->Cast(); return child_type == other.child_type; } -shared_ptr ListTypeInfo::Copy() const { - return make_shared_ptr(*this); -} - -shared_ptr ListTypeInfo::DeepCopy() const { - auto result = make_shared_ptr(child_type.DeepCopy()); - CopyBaseInfo(*result); - return result; +unique_ptr ListTypeInfo::Copy() const { + return make_uniq(*this); } //===--------------------------------------------------------------------===// // Struct Type Info //===--------------------------------------------------------------------===// -StructTypeInfo::StructTypeInfo() : ExtraTypeInfo(ExtraTypeInfoType::STRUCT_TYPE_INFO) { -} - -StructTypeInfo::StructTypeInfo(ExtraTypeInfoType type, child_list_t child_types_p) - : ExtraTypeInfo(type), child_types(std::move(child_types_p)) { +StructTypeInfo::StructTypeInfo() : LogicalTypeInfo(LogicalTypeInfoType::STRUCT_TYPE_INFO) { } StructTypeInfo::StructTypeInfo(child_list_t child_types_p) - : ExtraTypeInfo(ExtraTypeInfoType::STRUCT_TYPE_INFO), child_types(std::move(child_types_p)) { + : LogicalTypeInfo(LogicalTypeInfoType::STRUCT_TYPE_INFO), child_types(std::move(child_types_p)) { } -bool StructTypeInfo::EqualsInternal(const ExtraTypeInfo *other_p) const { +bool StructTypeInfo::EqualsInternal(const LogicalTypeInfo *other_p) const { auto &other = other_p->Cast(); return child_types == other.child_types; } -shared_ptr StructTypeInfo::Copy() const { - return make_shared_ptr(*this); -} - -shared_ptr StructTypeInfo::DeepCopy() const { - child_list_t copied_child_types; - for (const auto &child_type : child_types) { - copied_child_types.emplace_back(child_type.first, child_type.second.DeepCopy()); - } - auto result = make_shared_ptr(type, std::move(copied_child_types)); - CopyBaseInfo(*result); - return result; +unique_ptr StructTypeInfo::Copy() const { + return make_uniq(*this); } //===--------------------------------------------------------------------===// // User Type Info //===--------------------------------------------------------------------===// void UnboundTypeInfo::Serialize(Serializer &serializer) const { - ExtraTypeInfo::Serialize(serializer); + LogicalTypeInfo::Serialize(serializer); if (serializer.ShouldSerialize(StorageVersion::V1_5_0)) { serializer.WritePropertyWithDefault>(204, "expr", expr); @@ -293,8 +246,8 @@ void UnboundTypeInfo::Serialize(Serializer &serializer) const { serializer.WritePropertyWithDefault>(203, "user_type_modifiers", user_type_mods); } -shared_ptr UnboundTypeInfo::Deserialize(Deserializer &deserializer) { - auto result = duckdb::shared_ptr(new UnboundTypeInfo()); +unique_ptr UnboundTypeInfo::Deserialize(Deserializer &deserializer) { + auto result = duckdb::unique_ptr(new UnboundTypeInfo()); deserializer.ReadPropertyWithDefault>(204, "expr", result->expr); @@ -324,16 +277,16 @@ shared_ptr UnboundTypeInfo::Deserialize(Deserializer &deserialize // Legacy Aggregate State Type Info //===--------------------------------------------------------------------===// LegacyAggregateStateTypeInfo::LegacyAggregateStateTypeInfo() - : ExtraTypeInfo(ExtraTypeInfoType::LEGACY_AGGREGATE_STATE_TYPE_INFO) { + : LogicalTypeInfo(LogicalTypeInfoType::LEGACY_AGGREGATE_STATE_TYPE_INFO) { throw InternalException("LegacyAggregateStateTypeInfo should no longer be getting constructed"); } -bool LegacyAggregateStateTypeInfo::EqualsInternal(const ExtraTypeInfo *other_p) const { +bool LegacyAggregateStateTypeInfo::EqualsInternal(const LogicalTypeInfo *other_p) const { throw InternalException("LegacyAggregateStateTypeInfo should no longer be getting constructed"); } -shared_ptr LegacyAggregateStateTypeInfo::LegacyDeserialize() { - return make_shared_ptr(ExtraTypeInfoType::GENERIC_TYPE_INFO); +unique_ptr LegacyAggregateStateTypeInfo::LegacyDeserialize() { + return make_uniq(LogicalTypeInfoType::GENERIC_TYPE_INFO); } //===--------------------------------------------------------------------===// @@ -352,7 +305,7 @@ PhysicalType EnumTypeInfo::DictType(idx_t size) { } EnumTypeInfo::EnumTypeInfo(const Vector &values_insert_order_p, idx_t dict_size_p) - : ExtraTypeInfo(ExtraTypeInfoType::ENUM_TYPE_INFO), values_insert_order(Vector::Ref(values_insert_order_p)), + : LogicalTypeInfo(LogicalTypeInfoType::ENUM_TYPE_INFO), values_insert_order(Vector::Ref(values_insert_order_p)), dict_type(EnumDictType::VECTOR_DICT), dict_size(dict_size_p) { } @@ -368,15 +321,15 @@ const idx_t &EnumTypeInfo::GetDictSize() const { return dict_size; } -shared_ptr EnumTypeInfo::CreateTypeInfo(const Vector &ordered_data, idx_t size) { +unique_ptr EnumTypeInfo::CreateTypeInfo(const Vector &ordered_data, idx_t size) { auto enum_internal_type = EnumTypeInfo::DictType(size); switch (enum_internal_type) { case PhysicalType::UINT8: - return make_shared_ptr>(ordered_data, size); + return make_uniq>(ordered_data, size); case PhysicalType::UINT16: - return make_shared_ptr>(ordered_data, size); + return make_uniq>(ordered_data, size); case PhysicalType::UINT32: - return make_shared_ptr>(ordered_data, size); + return make_uniq>(ordered_data, size); default: throw InternalException("Invalid Physical Type for ENUMs"); } @@ -396,14 +349,14 @@ int64_t TemplatedGetPos(const string_map_t &map, const string_t &key) { } int64_t EnumType::GetPos(const LogicalType &type, const string_t &key) { - auto info = type.AuxInfo(); + auto &info = type.GetTypeInfo(); switch (type.InternalType()) { case PhysicalType::UINT8: - return TemplatedGetPos(info->Cast>().GetValues(), key); + return TemplatedGetPos(info.Cast>().GetValues(), key); case PhysicalType::UINT16: - return TemplatedGetPos(info->Cast>().GetValues(), key); + return TemplatedGetPos(info.Cast>().GetValues(), key); case PhysicalType::UINT32: - return TemplatedGetPos(info->Cast>().GetValues(), key); + return TemplatedGetPos(info.Cast>().GetValues(), key); default: throw InternalException("ENUM can only have unsigned integers (except UINT64) as physical types"); } @@ -414,8 +367,12 @@ string_t EnumType::GetString(const LogicalType &type, idx_t pos) { return FlatVector::GetData(EnumType::GetValuesInsertOrder(type))[pos]; } -shared_ptr EnumTypeInfo::Deserialize(Deserializer &deserializer) { +unique_ptr EnumTypeInfo::Deserialize(Deserializer &deserializer) { auto values_count = deserializer.ReadProperty(200, "values_count"); + if (values_count > NumericLimits::Maximum()) { + throw SerializationException("Corrupted enum: enum size %llu exceeds maximum %llu", values_count, + static_cast(NumericLimits::Maximum())); + } auto enum_internal_type = EnumTypeInfo::DictType(values_count); switch (enum_internal_type) { case PhysicalType::UINT8: @@ -430,7 +387,7 @@ shared_ptr EnumTypeInfo::Deserialize(Deserializer &deserializer) } // Equalities are only used in enums with different catalog entries -bool EnumTypeInfo::EqualsInternal(const ExtraTypeInfo *other_p) const { +bool EnumTypeInfo::EqualsInternal(const LogicalTypeInfo *other_p) const { auto &other = other_p->Cast(); if (dict_type != other.dict_type) { return false; @@ -453,7 +410,7 @@ bool EnumTypeInfo::EqualsInternal(const ExtraTypeInfo *other_p) const { } void EnumTypeInfo::Serialize(Serializer &serializer) const { - ExtraTypeInfo::Serialize(serializer); + LogicalTypeInfo::Serialize(serializer); // Enums are special in that we serialize their values as a list instead of dumping the whole vector auto strings = FlatVector::GetData(values_insert_order); @@ -462,7 +419,7 @@ void EnumTypeInfo::Serialize(Serializer &serializer) const { [&](Serializer::List &list, idx_t i) { list.WriteElement(strings[i]); }); } -shared_ptr EnumTypeInfo::Copy() const { +unique_ptr EnumTypeInfo::Copy() const { // create a templated copy so that the value lookup map is rebuilt - the dictionary itself is shared auto result = CreateTypeInfo(values_insert_order, dict_size); CopyBaseInfo(*result); @@ -474,117 +431,106 @@ shared_ptr EnumTypeInfo::Copy() const { //===--------------------------------------------------------------------===// ArrayTypeInfo::ArrayTypeInfo(LogicalType child_type_p, uint32_t size_p) - : ExtraTypeInfo(ExtraTypeInfoType::ARRAY_TYPE_INFO), child_type(std::move(child_type_p)), size(size_p) { + : LogicalTypeInfo(LogicalTypeInfoType::ARRAY_TYPE_INFO), child_type(std::move(child_type_p)), size(size_p) { } -bool ArrayTypeInfo::EqualsInternal(const ExtraTypeInfo *other_p) const { +bool ArrayTypeInfo::EqualsInternal(const LogicalTypeInfo *other_p) const { auto &other = other_p->Cast(); return child_type == other.child_type && size == other.size; } -shared_ptr ArrayTypeInfo::Copy() const { - return make_shared_ptr(*this); -} - -shared_ptr ArrayTypeInfo::DeepCopy() const { - auto result = make_shared_ptr(child_type.DeepCopy(), size); - CopyBaseInfo(*result); - return result; +unique_ptr ArrayTypeInfo::Copy() const { + return make_uniq(*this); } //===--------------------------------------------------------------------===// // Any Type Info //===--------------------------------------------------------------------===// -AnyTypeInfo::AnyTypeInfo() : ExtraTypeInfo(ExtraTypeInfoType::ANY_TYPE_INFO) { +AnyTypeInfo::AnyTypeInfo() : LogicalTypeInfo(LogicalTypeInfoType::ANY_TYPE_INFO) { } AnyTypeInfo::AnyTypeInfo(LogicalType target_type_p, idx_t cast_score_p) - : ExtraTypeInfo(ExtraTypeInfoType::ANY_TYPE_INFO), target_type(std::move(target_type_p)), cast_score(cast_score_p) { + : LogicalTypeInfo(LogicalTypeInfoType::ANY_TYPE_INFO), target_type(std::move(target_type_p)), + cast_score(cast_score_p) { } -bool AnyTypeInfo::EqualsInternal(const ExtraTypeInfo *other_p) const { +bool AnyTypeInfo::EqualsInternal(const LogicalTypeInfo *other_p) const { auto &other = other_p->Cast(); return target_type == other.target_type && cast_score == other.cast_score; } -shared_ptr AnyTypeInfo::Copy() const { - return make_shared_ptr(*this); -} - -shared_ptr AnyTypeInfo::DeepCopy() const { - auto result = make_shared_ptr(target_type.DeepCopy(), cast_score); - CopyBaseInfo(*result); - return result; +unique_ptr AnyTypeInfo::Copy() const { + return make_uniq(*this); } //===--------------------------------------------------------------------===// // Integer Literal Type Info //===--------------------------------------------------------------------===// -IntegerLiteralTypeInfo::IntegerLiteralTypeInfo() : ExtraTypeInfo(ExtraTypeInfoType::INTEGER_LITERAL_TYPE_INFO) { +IntegerLiteralTypeInfo::IntegerLiteralTypeInfo() : LogicalTypeInfo(LogicalTypeInfoType::INTEGER_LITERAL_TYPE_INFO) { } IntegerLiteralTypeInfo::IntegerLiteralTypeInfo(Value constant_value_p) - : ExtraTypeInfo(ExtraTypeInfoType::INTEGER_LITERAL_TYPE_INFO), constant_value(std::move(constant_value_p)) { + : LogicalTypeInfo(LogicalTypeInfoType::INTEGER_LITERAL_TYPE_INFO), constant_value(std::move(constant_value_p)) { if (constant_value.IsNull()) { throw InternalException("Integer literal cannot be NULL"); } } -bool IntegerLiteralTypeInfo::EqualsInternal(const ExtraTypeInfo *other_p) const { +bool IntegerLiteralTypeInfo::EqualsInternal(const LogicalTypeInfo *other_p) const { auto &other = other_p->Cast(); return constant_value == other.constant_value; } -shared_ptr IntegerLiteralTypeInfo::Copy() const { - return make_shared_ptr(*this); +unique_ptr IntegerLiteralTypeInfo::Copy() const { + return make_uniq(*this); } //===--------------------------------------------------------------------===// // Template Type Info //===--------------------------------------------------------------------===// -TemplateTypeInfo::TemplateTypeInfo() : ExtraTypeInfo(ExtraTypeInfoType::TEMPLATE_TYPE_INFO) { +TemplateTypeInfo::TemplateTypeInfo() : LogicalTypeInfo(LogicalTypeInfoType::TEMPLATE_TYPE_INFO) { } TemplateTypeInfo::TemplateTypeInfo(string name_p) - : ExtraTypeInfo(ExtraTypeInfoType::TEMPLATE_TYPE_INFO), name(std::move(name_p)) { + : LogicalTypeInfo(LogicalTypeInfoType::TEMPLATE_TYPE_INFO), name(std::move(name_p)) { } -bool TemplateTypeInfo::EqualsInternal(const ExtraTypeInfo *other_p) const { +bool TemplateTypeInfo::EqualsInternal(const LogicalTypeInfo *other_p) const { auto &other = other_p->Cast(); return name == other.name; } -shared_ptr TemplateTypeInfo::Copy() const { - return make_shared_ptr(*this); +unique_ptr TemplateTypeInfo::Copy() const { + return make_uniq(*this); } //===--------------------------------------------------------------------===// // Geo Type Info //===--------------------------------------------------------------------===// -GeoTypeInfo::GeoTypeInfo() : ExtraTypeInfo(ExtraTypeInfoType::GEO_TYPE_INFO) { +GeoTypeInfo::GeoTypeInfo() : LogicalTypeInfo(LogicalTypeInfoType::GEO_TYPE_INFO) { } -bool GeoTypeInfo::EqualsInternal(const ExtraTypeInfo *other_p) const { +bool GeoTypeInfo::EqualsInternal(const LogicalTypeInfo *other_p) const { // No additional info to compare const auto &other = other_p->Cast(); return other.crs.Equals(crs); } -shared_ptr GeoTypeInfo::Copy() const { - return make_shared_ptr(*this); +unique_ptr GeoTypeInfo::Copy() const { + return make_uniq(*this); } //===--------------------------------------------------------------------===// // Unbound Type Info //===--------------------------------------------------------------------===// -UnboundTypeInfo::UnboundTypeInfo() : ExtraTypeInfo(ExtraTypeInfoType::UNBOUND_TYPE_INFO) { +UnboundTypeInfo::UnboundTypeInfo() : LogicalTypeInfo(LogicalTypeInfoType::UNBOUND_TYPE_INFO) { } UnboundTypeInfo::UnboundTypeInfo(unique_ptr expr_p) - : ExtraTypeInfo(ExtraTypeInfoType::UNBOUND_TYPE_INFO), expr(std::move(expr_p)) { + : LogicalTypeInfo(LogicalTypeInfoType::UNBOUND_TYPE_INFO), expr(std::move(expr_p)) { } -bool UnboundTypeInfo::EqualsInternal(const ExtraTypeInfo *other_p) const { +bool UnboundTypeInfo::EqualsInternal(const LogicalTypeInfo *other_p) const { auto &other = other_p->Cast(); if (!expr->Equals(*other.expr)) { return false; @@ -592,10 +538,10 @@ bool UnboundTypeInfo::EqualsInternal(const ExtraTypeInfo *other_p) const { return true; } -shared_ptr UnboundTypeInfo::Copy() const { - auto result = make_shared_ptr(expr->Copy()); +unique_ptr UnboundTypeInfo::Copy() const { + auto result = make_uniq(expr->Copy()); CopyBaseInfo(*result); - return result; + return std::move(result); } } // namespace duckdb diff --git a/src/duckdb/src/common/multi_file/base_file_reader.cpp b/src/duckdb/src/common/multi_file/base_file_reader.cpp index 82c7c3e59..d42cbf29d 100644 --- a/src/duckdb/src/common/multi_file/base_file_reader.cpp +++ b/src/duckdb/src/common/multi_file/base_file_reader.cpp @@ -8,6 +8,11 @@ unique_ptr BaseFileReader::GetStatistics(ClientContext &context, return nullptr; } +unique_ptr BaseFileReader::GetVirtualColumnStatistics(ClientContext &context, + column_t virtual_column_id) { + return nullptr; +} + shared_ptr BaseFileReader::GetUnionData(idx_t file_idx) { throw NotImplementedException("Union by name not supported for reader of type %s", GetReaderType()); } diff --git a/src/duckdb/src/common/multi_file/multi_file_column_mapper.cpp b/src/duckdb/src/common/multi_file/multi_file_column_mapper.cpp index e730c06f3..af5b3c272 100644 --- a/src/duckdb/src/common/multi_file/multi_file_column_mapper.cpp +++ b/src/duckdb/src/common/multi_file/multi_file_column_mapper.cpp @@ -1,4 +1,5 @@ #include "duckdb/common/multi_file/multi_file_column_mapper.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/planner/expression/bound_cast_expression.hpp" #include "duckdb/planner/expression/bound_comparison_expression.hpp" #include "duckdb/planner/expression/bound_columnref_expression.hpp" @@ -118,7 +119,9 @@ struct FieldIdMapper : public ColumnMapper { static unique_ptr GetDefault(ClientContext &context, const MultiFileColumnDefinition &column) { auto &default_val = column.default_expression; if (!default_val) { - throw InternalException("No default expression in FieldId Map"); + throw InvalidInputException("Field \"%s\" (field id %d) is missing from the file schema and has no default " + "expression", + column.name, column.GetIdentifierFieldId()); } auto binder = Binder::CreateBinder(context); binder->SetCanContainNulls(true); @@ -318,9 +321,12 @@ static ColumnMapResult MapColumnList(ClientContext &context, const MultiFileColu default_expressions.push_back(std::move(child_map.default_value)); // auto default_type = LogicalType::STRUCT(std::move(default_type_list)); - result.default_value = StructPackFun::GetFunction().Bind(context, std::move(default_expressions)); + result.default_value = BindBuiltinScalarFunction(context, StructPackFun::Name, std::move(default_expressions)); } result.column_index = make_uniq(local_id.GetIndex(), std::move(child_indexes)); + if (global_index.HasType()) { + result.column_index->SetType(local_column.type); + } result.mapping = std::move(mapping); return result; } @@ -439,12 +445,15 @@ static ColumnMapResult MapColumnMap(ClientContext &context, const MultiFileColum } if (!default_expressions.empty()) { // we have default values at a previous level wrap it in a "list" - result.default_value = StructPackFun::GetFunction().Bind(context, std::move(default_expressions)); + result.default_value = BindBuiltinScalarFunction(context, StructPackFun::Name, std::move(default_expressions)); } vector map_indexes; map_indexes.emplace_back(0, std::move(child_indexes)); result.column_index = make_uniq(local_id.GetIndex(), std::move(map_indexes)); + if (global_index.HasType()) { + result.column_index->SetType(local_column.type); + } result.mapping = std::move(mapping); return result; } @@ -467,10 +476,22 @@ static ColumnMapResult MapColumnStruct(ClientContext &context, const MultiFileCo auto child_mapping = MapColumn(context, child_column, child_index, local_column.children, *nested_mapper); if (child_mapping.column_index) { + auto &child_column_index = *child_mapping.column_index; + if (child_mapping.local_column && child_mapping.local_column->type.id() == LogicalTypeId::STRUCT && + !child_column_index.IsPushdownExtract()) { + // the extracted field is read with the type it has in this file, and then remapped to the global type + // (the fields of a nested struct can differ between files, e.g. with union_by_name) + child_column_index.SetType(child_mapping.local_column->type); + if (child_mapping.column_map.type().id() == LogicalTypeId::TUPLE) { + // the mapping of a nested struct is (local name, mapping) - we extract the field directly + auto map_entry = StructValue::GetChildren(child_mapping.column_map)[1]; + child_mapping.column_map = std::move(map_entry); + } + } vector single_child; single_child.push_back(std::move(*child_mapping.column_index)); child_mapping.column_index = make_uniq(local_id.GetIndex(), single_child); - child_mapping.column_index->SetType(global_column.type); + child_mapping.column_index->SetType(local_column.type); child_mapping.column_index->SetPushdownExtract(); mapping->child_mapping.emplace(MultiFileGlobalIndex(0), std::move(child_mapping.mapping)); @@ -544,11 +565,11 @@ static ColumnMapResult MapColumnStruct(ClientContext &context, const MultiFileCo } if (!default_expressions.empty()) { - result.default_value = StructPackFun::GetFunction().Bind(context, std::move(default_expressions)); + result.default_value = BindBuiltinScalarFunction(context, StructPackFun::Name, std::move(default_expressions)); } result.column_index = make_uniq(local_id.GetIndex(), std::move(child_indexes)); if (global_index.HasType()) { - result.column_index->SetType(global_column.type); + result.column_index->SetType(local_column.type); } result.mapping = std::move(mapping); return result; @@ -568,10 +589,19 @@ static ColumnMapResult MapColumn(ClientContext &context, const MultiFileColumnDe } // the field exists! get the local column auto &local_column = local_columns[local_idx]; + if (local_column.type.id() == LogicalTypeId::SQLNULL) { + // An explicitly NULL field can represent any result type, including a nested field. + // Do not feed it into remap_struct, which requires matching nested source types. + auto &type = (is_root || global_index.IsPushdownExtract()) && global_index.HasType() + ? global_index.GetScanType() + : global_column.type; + result.default_value = make_uniq(Value(type)); + return result; + } auto mapping_idx = is_root ? top_level_index : local_idx; auto mapping = make_uniq(mapping_idx); - if (global_column.children.empty()) { - // not a struct - map the column directly + if (global_column.children.empty() || !local_column.type.IsNested()) { + // Map directly when no child remapping is needed or the source requires a scalar cast. result.column_map = Value(local_column.name); result.column_index = make_uniq(global_index.RemapRootIndex(local_idx.GetIndex())); result.mapping = std::move(mapping); @@ -581,6 +611,11 @@ static ColumnMapResult MapColumn(ClientContext &context, const MultiFileColumnDe // nested type - check if the field identifiers match and if we need to remap D_ASSERT(global_column.type.IsNested()); + if (global_column.type.id() != local_column.type.id()) { + throw BinderException("Failed to map file-column of type '%s' to result-column of type '%s'", local_column.type, + global_column.type); + } + switch (global_column.type.id()) { case LogicalTypeId::STRUCT: return MapColumnStruct(context, global_column, global_index, local_column, local_idx, mapper, @@ -604,6 +639,11 @@ static unique_ptr ConstructMapExpression(ClientContext &context, Mul ColumnMapResult &mapping, const LogicalType &global_column_type, const LogicalType &local_column_type, bool is_trivially_mappable) { unique_ptr expr = make_uniq(local_column_type, local_idx.GetIndex()); + if (mapping.column_map.type().id() == LogicalTypeId::TUPLE) { + // A pushed-down extract already selects the source column; retain only its child mapping. + auto child_mapping = StructValue::GetChildren(mapping.column_map)[1]; + mapping.column_map = std::move(child_mapping); + } const bool can_use_remap_struct = global_column_type.IsNested() && (mapping.column_map.IsNull() || mapping.column_map.type().id() == LogicalTypeId::STRUCT) && @@ -625,7 +665,7 @@ static unique_ptr ConstructMapExpression(ClientContext &context, Mul } else { children.push_back(std::move(mapping.default_value)); } - return RemapStructFun::GetFunction().Bind(context, std::move(children)); + return BindBuiltinScalarFunction(context, RemapStructFun::Name, std::move(children)); } ResultColumnMapping MultiFileColumnMapper::CreateColumnMappingByMapper(const ColumnMapper &mapper) { @@ -739,7 +779,9 @@ ResultColumnMapping MultiFileColumnMapper::CreateColumnMappingByMapper(const Col // reader is responsible for converting types - perform a top-level match only auto entry = mapper.Find(global_column); if (!entry.IsValid()) { - ThrowColumnNotFoundError(global_column.name.GetIdentifierName()); + // the file lacks the column - it takes its default value, and is an error when it has none + reader_data.expressions.push_back(mapper.GetDefaultExpression(context, global_column, true)); + continue; } MultiFileLocalColumnId local_id(entry.GetIndex()); auto local_index = global_id.RemapRootIndex(local_id.GetId()); @@ -892,16 +934,51 @@ static unique_ptr CreateStructExtractExpression(unique_ptrsecond->index.GetIndex() != i || + !(source_children[i].first == target_children[i].first) || + !MappedCastIsInvertible(*entry->second, source_children[i].second, target_children[i].second)) { + return false; + } + } + return true; +} + +static bool TryCastConstant(Value &constant, const MultiFileIndexMapping &mapping, const LogicalType &target_type) { + // this filter replaces the global one, so both casts must preserve which rows match + if (!BoundCastExpression::CastIsInvertible(constant.type(), target_type) || + !MappedCastIsInvertible(mapping, target_type, constant.type())) { return false; } auto cast = constant.DefaultTryCastAs(target_type); if (!cast) { return false; } + auto roundtrip = cast->DefaultTryCastAs(constant.type()); + if (!roundtrip || *roundtrip != constant) { + return false; + } constant = std::move(*cast); return true; } @@ -950,13 +1027,14 @@ static RewrittenMappedExpression RewriteMappedValueExpression(const Expression & } static unique_ptr RewriteDynamicFilterExpression(const shared_ptr &filter_data, + const MultiFileIndexMapping &mapping, const LogicalType &target_type) { if (!filter_data || !filter_data->initialized) { return nullptr; } - lock_guard lock(filter_data->lock); + annotated_shared_lock lock(filter_data->lock); auto new_constant = filter_data->constant; - if (!TryCastConstant(new_constant, target_type)) { + if (!TryCastConstant(new_constant, mapping, target_type)) { return nullptr; } return BoundComparisonExpression::Create(filter_data->comparison_type, CreateReferenceExpression(target_type), @@ -968,30 +1046,19 @@ static unique_ptr TryCastFilterExpression(const Expression &expr, co switch (expr.GetExpressionClass()) { case ExpressionClass::BOUND_FUNCTION: { auto &func = expr.Cast(); - if (func.Function().GetName() == OptionalFilterScalarFun::NAME) { - if (!func.BindInfo()) { - return CreateOptionalFilterExpression(nullptr, target_type); - } - auto &data = func.BindInfo()->Cast(); - auto child_expr = data.child_filter_expr - ? TryCastFilterExpression(*data.child_filter_expr, mapping, target_type) - : nullptr; - if (data.child_filter_expr && !child_expr) { + if (ExpressionFilter::IsRootOptionalExpression(expr)) { + auto optional_child = ExpressionFilter::GetOptionalFilterChild(expr); + auto child_expr = optional_child ? TryCastFilterExpression(*optional_child, mapping, target_type) : nullptr; + if (optional_child && !child_expr) { return nullptr; } - return CreateOptionalFilterExpression(std::move(child_expr), target_type); - } - if (func.Function().GetName() == SelectivityOptionalFilterScalarFun::NAME) { + if (func.Function().GetName() == OptionalFilterScalarFun::NAME) { + return CreateOptionalFilterExpression(std::move(child_expr), target_type); + } if (!func.BindInfo()) { - return CreateSelectivityOptionalFilterExpression(nullptr, target_type, 0.5f, idx_t(6)); + return CreateSelectivityOptionalFilterExpression(std::move(child_expr), target_type, 0.5f, idx_t(6)); } auto &data = func.BindInfo()->Cast(); - auto child_expr = data.child_filter_expr - ? TryCastFilterExpression(*data.child_filter_expr, mapping, target_type) - : nullptr; - if (data.child_filter_expr && !child_expr) { - return nullptr; - } return CreateSelectivityOptionalFilterExpression(std::move(child_expr), target_type, data.selectivity_threshold, data.n_vectors_to_check); } @@ -1000,7 +1067,7 @@ static unique_ptr TryCastFilterExpression(const Expression &expr, co return nullptr; } auto &data = func.BindInfo()->Cast(); - return RewriteDynamicFilterExpression(data.filter_data, target_type); + return RewriteDynamicFilterExpression(data.filter_data, mapping, target_type); } if (!BoundComparisonExpression::IsComparison(expr)) { return nullptr; @@ -1016,7 +1083,7 @@ static unique_ptr TryCastFilterExpression(const Expression &expr, co return nullptr; } auto constant = right.Cast().GetValue(); - if (!TryCastConstant(constant, *lhs.type)) { + if (!TryCastConstant(constant, *lhs.mapping, *lhs.type)) { return nullptr; } return BoundComparisonExpression::Create(comparison.GetExpressionType(), std::move(lhs.expr), @@ -1065,7 +1132,7 @@ static unique_ptr TryCastFilterExpression(const Expression &expr, co return nullptr; } auto constant = op.GetChildren()[i]->Cast().GetValue(); - if (!TryCastConstant(constant, *lhs.type)) { + if (!TryCastConstant(constant, *lhs.mapping, *lhs.type)) { return nullptr; } result->GetChildrenMutable().push_back(make_uniq(std::move(constant))); diff --git a/src/duckdb/src/common/multi_file/multi_file_function.cpp b/src/duckdb/src/common/multi_file/multi_file_function.cpp index 8608bd6c0..b3f906a69 100644 --- a/src/duckdb/src/common/multi_file/multi_file_function.cpp +++ b/src/duckdb/src/common/multi_file/multi_file_function.cpp @@ -12,7 +12,12 @@ MultiFileGlobalState::MultiFileGlobalState(unique_ptr owned_file_ : file_list(*owned_file_list_p), owned_file_list(std::move(owned_file_list_p)) { } -MultiFileGlobalState::~MultiFileGlobalState() = default; +MultiFileGlobalState::~MultiFileGlobalState() { + if (read_ahead) { + // the file opens scheduled on the async pool reference this state - wait for them before it goes away + read_ahead->CancelAndDrain(); + } +} MultiFileReaderInterface::~MultiFileReaderInterface() { } @@ -42,7 +47,7 @@ optional_idx MultiFileReaderInterface::MaxThreads(ClientContext &context, const } void MultiFileReaderInterface::CombineSchemas(ClientContext &context, - const vector> &union_data, + const vector> &union_data, bool union_by_name, vector &return_types, vector &names) { identifier_map_t union_names_map; for (auto &data : union_data) { diff --git a/src/duckdb/src/common/multi_file/multi_file_list.cpp b/src/duckdb/src/common/multi_file/multi_file_list.cpp index 8b48a40d8..38ecf87cb 100644 --- a/src/duckdb/src/common/multi_file/multi_file_list.cpp +++ b/src/duckdb/src/common/multi_file/multi_file_list.cpp @@ -14,14 +14,6 @@ namespace duckdb { -MultiFilePushdownInfo::MultiFilePushdownInfo(LogicalGet &get) - : table_index(get.table_index), column_names(get.names), column_indexes(get.GetColumnIds()), - extra_info(get.extra_info) { - for (auto &col_id : column_indexes) { - column_ids.push_back(col_id.GetPrimaryIndex()); - } -} - MultiFilePushdownInfo::MultiFilePushdownInfo(TableIndex table_index, const vector &column_names, const vector &column_indexes, ExtraOperatorInfo &extra_info) : table_index(table_index), column_names(column_names), column_indexes(column_indexes), extra_info(extra_info) { @@ -38,10 +30,11 @@ bool PushdownInternal(ClientContext &context, const MultiFileOptions &options, M if (IsVirtualColumn(info.column_ids[i])) { continue; } - filter_info.column_map.insert({info.column_names[info.column_ids[i]].GetIdentifierName(), i}); + filter_info.column_map.insert({info.column_names[info.column_ids[i]], i}); } filter_info.hive_enabled = options.hive_partitioning; filter_info.filename_enabled = options.filename; + filter_info.filename_column = Identifier(options.filename_column); auto start_files = expanded_files.size(); HivePartitioning::ApplyFiltersToFileList(context, expanded_files, filters, filter_info, info); @@ -168,6 +161,10 @@ vector MultiFileList::GetDisplayFileList(optional_idx max_files) c return files; } +vector MultiFileList::GetAllFiles() const { + return MultiFileList::GetDisplayFileList(); +} + MultiFileCount MultiFileList::GetFileCount(idx_t min_exact_count) const { return MultiFileCount(GetTotalFileCount()); } diff --git a/src/duckdb/src/common/multi_file/multi_file_reader.cpp b/src/duckdb/src/common/multi_file/multi_file_reader.cpp index be6c42ee7..58641a287 100644 --- a/src/duckdb/src/common/multi_file/multi_file_reader.cpp +++ b/src/duckdb/src/common/multi_file/multi_file_reader.cpp @@ -1,5 +1,8 @@ #include "duckdb/common/multi_file/multi_file_reader.hpp" #include "duckdb/function/partition_stats.hpp" +#include "duckdb/storage/statistics/numeric_stats.hpp" +#include "duckdb/storage/statistics/struct_stats.hpp" +#include "duckdb/storage/statistics/list_stats.hpp" #include "duckdb/common/exception.hpp" #include "duckdb/common/hive_partitioning.hpp" @@ -34,14 +37,14 @@ MultiFileReaderGlobalState::~MultiFileReaderGlobalState() { MultiFileReader::~MultiFileReader() { } -unique_ptr MultiFileReader::Create(const TableFunction &table_function) { +unique_ptr MultiFileReader::Create(const BoundTableFunction &table_function) { unique_ptr res; if (table_function.get_multi_file_reader) { res = table_function.get_multi_file_reader(table_function); - res->function_name = table_function.name; + res->function_name = table_function.GetName(); } else { res = make_uniq(); - res->function_name = table_function.name; + res->function_name = table_function.GetName(); } return res; } @@ -71,6 +74,37 @@ unique_ptr MultiFileBindData::Copy() const { return std::move(result); } +bool MultiFileBindData::IsGeneratedColumn(idx_t column_idx) const { + if ((reader_bind.filename_idx.IsValid() && reader_bind.filename_idx.GetIndex() == column_idx) || + (reader_bind.file_row_number_idx.IsValid() && reader_bind.file_row_number_idx.GetIndex() == column_idx)) { + return true; + } + for (const auto &partition : reader_bind.hive_partitioning_indexes) { + if (partition.index == column_idx) { + return true; + } + } + return false; +} + +bool MultiFileBindData::MatchesPhysicalColumn(idx_t column_idx, const Identifier &physical_name, + const LogicalType &physical_type) const { + return column_idx < names.size() && column_idx < types.size() && !IsGeneratedColumn(column_idx) && + names[column_idx] == physical_name && types[column_idx].EqualsIncludingCollation(physical_type); +} + +bool MultiFileBindData::HasIdentityMapping(const vector &physical_columns) const { + if (physical_columns.size() != names.size() || physical_columns.size() != types.size()) { + return false; + } + for (idx_t i = 0; i < physical_columns.size(); i++) { + if (!MatchesPhysicalColumn(i, physical_columns[i].name, physical_columns[i].type)) { + return false; + } + } + return true; +} + unique_ptr MultiFileReader::CreateDefault(const string &function_name) { auto res = make_uniq(); res->function_name = Identifier(function_name); @@ -85,62 +119,88 @@ Value MultiFileReader::CreateValueFromFileList(const vector &file_list) return Value::LIST(LogicalType::VARCHAR, std::move(files)); } -void MultiFileReader::AddParameters(TableFunction &table_function) { - table_function.named_parameters["filename"] = LogicalType::ANY; - table_function.named_parameters["hive_partitioning"] = LogicalType::BOOLEAN; - table_function.named_parameters["union_by_name"] = LogicalType::BOOLEAN; - table_function.named_parameters["hive_types"] = LogicalType::ANY; - table_function.named_parameters["hive_types_autocast"] = LogicalType::BOOLEAN; - table_function.named_parameters["allow_empty"] = LogicalType::BOOLEAN; +void MultiFileReader::AddParameters(TableFunction &table_function, MultiFileParameters which) { + auto add_options = [&](TypedKwargs &options) { + if (which == MultiFileParameters::ALL) { + // "filename" is a boolean, or the name of the column to hold the file name + options.Add("filename", LogicalType::ANY) + .Add("hive_partitioning", LogicalType::BOOLEAN) + .Add("union_by_name", LogicalType::BOOLEAN) + .Add("hive_types", LogicalType::ANY) + .Add("hive_types_autocast", LogicalType::BOOLEAN) + .Add("schema", LogicalTypeId::ANY) + .Add("file_row_number", LogicalType::BOOLEAN) + .Add("column_statistics", LogicalTypeId::ANY); + } + options.Add("allow_empty", LogicalType::BOOLEAN); + }; + // a reader can declare its own options before or after these + auto &signature = table_function.GetSignature(); + if (signature.GetTypedKwargs()) { + signature.ExtendTypedKwargs(add_options); + } else { + signature.WithTypedKwargs("options", add_options); + } } -OpenFileInfo MultiFileReader::ParseFileEntry(const Value &input) { - if (input.IsNull()) { - throw ParserException("%s reader cannot take NULL input as parameter", function_name); - } - if (input.type().id() == LogicalTypeId::VARCHAR) { - return OpenFileInfo(StringValue::Get(input)); - } - if (input.type().id() == LogicalTypeId::VARIANT) { - // a VARIANT lets every file carry its own set of open options - unpack it to its logical value - // a variant never unpacks to another variant, so this recurses at most once - return ParseFileEntry(VariantValue::GetValue(input)); - } - if (input.type().id() != LogicalTypeId::STRUCT) { - throw ParserException("%s reader can only take a list of strings, structs or variants as a parameter", - function_name); - } - // a file specified as a struct holds the path in the "filename" field - every other field is an open option - auto &child_types = StructType::GetChildTypes(input.type()); - auto &children = StructValue::GetChildren(input); - OpenFileInfo result; - auto extended_info = make_shared_ptr(); - bool found_path = false; - for (idx_t child_idx = 0; child_idx < children.size(); child_idx++) { - auto &name = child_types[child_idx].first; - auto &child = children[child_idx]; - if (name == MultiFileReader::FILE_PATH_FIELD) { - if (child.IsNull() || child.type().id() != LogicalTypeId::VARCHAR) { - throw ParserException("%s reader requires the \"%s\" field of a file struct to be a non-NULL VARCHAR", - function_name, MultiFileReader::FILE_PATH_FIELD); +MultiFileColumnDefinition MultiFileColumnDefinition::CreateNested(const Identifier &name, const LogicalType &type) { + MultiFileColumnDefinition result(name, type); + switch (type.id()) { + case LogicalTypeId::STRUCT: + for (auto &child : StructType::GetChildTypes(type)) { + result.children.push_back(CreateNested(child.first, child.second)); + } + break; + case LogicalTypeId::LIST: + result.children.push_back(CreateNested(Identifier("list"), ListType::GetChildType(type))); + break; + case LogicalTypeId::ARRAY: + result.children.push_back(CreateNested(Identifier("list"), ArrayType::GetChildType(type))); + break; + case LogicalTypeId::MAP: { + // the multi-file reader expects the entries of a MAP as a "key_value" STRUCT of the keys and the values + MultiFileColumnDefinition key_value(Identifier("key_value"), ListType::GetChildType(type)); + key_value.children.push_back(CreateNested(Identifier("key"), MapType::KeyType(type))); + key_value.children.push_back(CreateNested(Identifier("value"), MapType::ValueType(type))); + result.children.push_back(std::move(key_value)); + break; + } + case LogicalTypeId::UNION: + for (idx_t i = 0; i < UnionType::GetMemberCount(type); i++) { + result.children.push_back( + CreateNested(UnionType::GetMemberName(type, i), UnionType::GetMemberType(type, i))); + } + break; + default: + break; + } + return result; +} + +MultiFileColumnDefinition &MultiFileColumnDefinition::ResolveChildPath(const vector &child_path) { + reference current(*this); + for (auto child_index : child_path) { + auto &definition = current.get(); + if (definition.type.id() == LogicalTypeId::MAP) { + // the keys and the values are the children of the "key_value" entry + if (child_index > 1) { + throw InvalidInputException("A MAP has two children: its keys (0) and its values (1), not %llu", + child_index); } - result.path = StringValue::Get(child); - found_path = true; + current = definition.children[0].children[child_index]; continue; } - if (child.IsNull()) { - // a NULL option is an option that was not specified - a list of structs is typed by unifying the - // structs of its entries, which fills the options an entry did not specify with NULL - continue; + if (child_index >= definition.children.size()) { + throw InvalidInputException("Child index %llu is out of range for a field of type %s", child_index, + definition.type.ToString()); } - extended_info->SetUserOption(name.GetIdentifierName(), child); - } - if (!found_path) { - throw ParserException("%s reader requires a file struct to have a \"%s\" field holding the path of the file", - function_name, MultiFileReader::FILE_PATH_FIELD); + current = definition.children[child_index]; } - result.extended_info = std::move(extended_info); - return result; + return current.get(); +} + +OpenFileInfo MultiFileReader::ParseFileEntry(const Value &input) { + return OpenFileInfo::FromValue(input, function_name); } vector MultiFileReader::ParseFileList(const Value &input) { @@ -196,6 +256,252 @@ shared_ptr MultiFileReader::CreateFileList(ClientContext &context return CreateFileList(context, ParseFileList(input), glob_input); } +static void VerifySchemaDefinitionType(const LogicalType &definition_type, bool is_root) { + if (definition_type.id() != LogicalTypeId::STRUCT) { + if (is_root) { + throw InvalidInputException("'schema' expects a STRUCT as the value type of the map"); + } + throw BinderException("'schema' 'children' expects a STRUCT as the value type of the map, not %s", + definition_type.ToString()); + } + auto &fields = StructType::GetChildTypes(definition_type); + if (fields.size() != 3 && fields.size() != 4) { + throw InvalidInputException( + "'schema' expects the STRUCT to have 3 or 4 fields, 'name', 'type', 'default_value' and optionally " + "'children', not %d", + fields.size()); + } + if (fields[0].first != "name") { + throw InvalidInputException("'schema' expects the first field of the struct to be called 'name'"); + } + if (fields[0].second.id() != LogicalTypeId::VARCHAR) { + throw InvalidInputException("'schema' expects the 'name' field to be of type VARCHAR, not %s", + LogicalTypeIdToString(fields[0].second.id())); + } + if (fields[1].first != "type") { + throw InvalidInputException("'schema' expects the second field of the struct to be called 'type'"); + } + if (fields[1].second.id() != LogicalTypeId::VARCHAR) { + throw InvalidInputException("'schema' expects the 'type' field to be of type VARCHAR, not %s", + LogicalTypeIdToString(fields[1].second.id())); + } + if (fields[2].first != "default_value") { + throw InvalidInputException("'schema' expects the third field of the struct to be called 'default_value'"); + } + if (fields.size() == 4 && fields[3].first != "children") { + throw InvalidInputException("'schema' expects the fourth field of the struct to be called 'children'"); + } +} + +static void VerifySchemaChildType(const MultiFileColumnDefinition &column, const MultiFileColumnDefinition &child, + const string &expected_name, const LogicalType &expected_type) { + auto child_name = child.name.GetIdentifierName(); + const bool name_equivalent = child_name == expected_name; + const bool type_equivalent = child.type == expected_type; + if (name_equivalent && type_equivalent) { + return; + } + string error; + if (!name_equivalent) { + error = StringUtil::Format("name \"%s\" (got \"%s\")", expected_name, child_name); + } + if (!type_equivalent) { + const bool name_mentioned = !error.empty(); + if (name_mentioned) { + error += " and "; + } else { + error += StringUtil::Format("name \"%s\" to have ", expected_name); + } + error += StringUtil::Format("type \"%s\" (got \"%s\")", expected_type.ToString(), child.type.ToString()); + } + throw BinderException("'schema' column \"%s\" expects a child with %s", column.name.GetIdentifierName(), error); +} + +static void VerifySchemaChildren(const MultiFileColumnDefinition &column) { + auto column_name = column.name.GetIdentifierName(); + switch (column.type.id()) { + case LogicalTypeId::STRUCT: + case LogicalTypeId::LIST: + case LogicalTypeId::MAP: + break; + default: + throw BinderException("'schema' column \"%s\" of type %s cannot define nested children", column_name, + column.type.ToString()); + } + auto expected_children = LogicalType::GetNamedChildTypes(column.type); + if (column.children.size() != expected_children.size()) { + throw BinderException("'schema' column \"%s\" of type %s expects %d child definitions, not %d", column_name, + column.type.ToString(), expected_children.size(), column.children.size()); + } + for (idx_t i = 0; i < expected_children.size(); i++) { + VerifySchemaChildType(column, column.children[i], expected_children[i].first.GetIdentifierName(), + expected_children[i].second); + } +} + +static vector ParseSchemaMap(ClientContext &context, const Value &schema_value, + const LogicalType &root_key_type, bool is_root); + +static MultiFileColumnDefinition ParseSchemaDefinition(ClientContext &context, const Value &column_value, + const LogicalType &root_key_type) { + auto &map_entry = StructValue::GetChildren(column_value); + const auto &column_def = map_entry[1]; + if (column_def.IsNull()) { + throw BinderException("'schema' definition cannot be NULL"); + } + VerifySchemaDefinitionType(column_def.type(), false); + + const auto &children = StructValue::GetChildren(column_def); + auto type = TransformStringToLogicalType(StringValue::Get(children[1]), context); + MultiFileColumnDefinition result(StringValue::Get(children[0]), type); + result.identifier = map_entry[0]; + string error_message; + auto default_value = children[2].TryCastAs(context, result.type, &error_message); + if (!default_value) { + throw BinderException("Unable to cast 'schema' default_value \"%s\" to %s", children[2].ToString(), + result.type.ToString()); + } + result.default_expression = ConstantExpression::FromValue(*default_value); + if (children.size() > 3 && !children[3].IsNull()) { + result.children = ParseSchemaMap(context, children[3], root_key_type, false); + VerifySchemaChildren(result); + } + return result; +} + +static vector ParseSchemaMap(ClientContext &context, const Value &schema_value, + const LogicalType &root_key_type, bool is_root) { + if (schema_value.type().id() != LogicalTypeId::MAP) { + if (is_root) { + throw InvalidInputException("'schema' expects a value of type MAP, not %s", + LogicalTypeIdToString(schema_value.type().id())); + } + throw BinderException("'schema' 'children' expects a value of type MAP, not %s", + LogicalTypeIdToString(schema_value.type().id())); + } + auto &map_type = schema_value.type(); + auto &key_type = MapType::KeyType(map_type); + auto &value_type = MapType::ValueType(map_type); + VerifySchemaDefinitionType(value_type, is_root); + if (is_root) { + if (key_type.id() != LogicalTypeId::INTEGER && key_type.id() != LogicalTypeId::VARCHAR) { + throw InvalidInputException( + "'schema' expects the value type of the map to be either INTEGER or VARCHAR, not %s", + LogicalTypeIdToString(key_type.id())); + } + } else if (key_type != root_key_type) { + throw BinderException("'schema' 'children' key type must match the root schema key type %s, not %s", + root_key_type.ToString(), key_type.ToString()); + } + + auto &entries = ListValue::GetChildren(schema_value); + vector result; + result.reserve(entries.size()); + for (auto &entry : entries) { + result.push_back(ParseSchemaDefinition(context, entry, root_key_type)); + } + return result; +} + +vector MultiFileReader::ParseSchemaOption(ClientContext &context, + const Value &schema_value) { + if (schema_value.type().id() != LogicalTypeId::MAP) { + throw InvalidInputException("'schema' expects a value of type MAP, not %s", + LogicalTypeIdToString(schema_value.type().id())); + } + return ParseSchemaMap(context, schema_value, MapType::KeyType(schema_value.type()), true); +} + +void MultiFileOptions::SetBoundSchema(const Value &value, ClientContext &context) { + if (value.IsNull() || value.type().id() != LogicalTypeId::STRUCT) { + throw BinderException("'bound_schema' expects a non-NULL STRUCT of column names to type strings"); + } + auto &values = StructValue::GetChildren(value); + if (values.empty()) { + throw BinderException("'bound_schema' cannot be empty"); + } + for (idx_t i = 0; i < values.size(); i++) { + if (values[i].IsNull() || values[i].type().id() != LogicalTypeId::VARCHAR) { + throw BinderException("'bound_schema' types must be non-NULL VARCHAR values"); + } + auto type = TransformStringToLogicalType(StringValue::Get(values[i]), context); + bound_schema.push_back( + MultiFileColumnDefinition::CreateNested(StructType::GetChildName(value.type(), i), type)); + } +} + +void MultiFileOptions::ValidateBoundSchema() const { + if (bound_schema.empty()) { + return; + } + if (!schema.empty()) { + throw BinderException("'bound_schema' cannot be combined with 'schema'"); + } + if (union_by_name || maximum_sample_files != 1) { + throw BinderException("'bound_schema' does not support union_by_name or multi-file schema sampling"); + } + if (auto_detect_hive_partitioning) { + throw BinderException("'bound_schema' requires explicit hive_partitioning=true or false"); + } + if (hive_partitioning && hive_types_autocast) { + throw BinderException("'bound_schema' requires hive_types_autocast=false and complete hive_types"); + } + if (!hive_partitioning && !hive_types_schema.empty()) { + throw BinderException("'bound_schema' cannot combine hive_types with hive_partitioning=false"); + } + case_insensitive_map_t columns; + for (auto &column : bound_schema) { + if (!column.type.IsComplete() || !columns.emplace(column.name.GetIdentifierName(), column.type).second) { + throw BinderException("'bound_schema' requires complete types and distinct column names"); + } + } + auto require_type = [&](const string &name, const LogicalType &type) { + auto entry = columns.find(name); + if (entry == columns.end() || !entry->second.EqualsIncludingCollation(type)) { + throw BinderException("'bound_schema' must include column \"%s\" with type %s", name, type.ToString()); + } + }; + if (filename) { + require_type(filename_column, LogicalType::VARCHAR); + } + if (file_row_number) { + require_type("file_row_number", LogicalType::BIGINT); + if (filename && StringUtil::CIEquals(filename_column, "file_row_number")) { + throw BinderException("'bound_schema' cannot assign filename and file_row_number to the same column"); + } + } + for (auto &partition : hive_types_schema) { + require_type(partition.first, partition.second); + if ((filename && StringUtil::CIEquals(filename_column, partition.first)) || + (file_row_number && StringUtil::CIEquals("file_row_number", partition.first))) { + throw BinderException("'bound_schema' cannot assign generated and Hive roles to column \"%s\"", + partition.first); + } + } + if (allow_empty) { + throw BinderException("'bound_schema' does not support allow_empty"); + } +} + +bool MultiFileReader::ParseCopyOption(const Identifier &key, const vector &values, MultiFileOptions &options) { + if (key != "file_row_number") { + return false; + } + // a boolean COPY option that is given without a value is set + if (values.empty()) { + options.file_row_number = true; + return true; + } + string error_message; + auto value = values[0].DefaultTryCastAs(LogicalType::BOOLEAN, &error_message); + if (!value) { + throw InvalidInputException("Unable to cast \"%s\" to BOOLEAN for boolean option \"%s\"", values[0].ToString(), + key); + } + options.file_row_number = BooleanValue::Get(*value); + return true; +} + bool MultiFileReader::ParseOption(const Identifier &key, const Value &val, MultiFileOptions &options, ClientContext &context) { if (key == "filename") { @@ -230,19 +536,10 @@ bool MultiFileReader::ParseOption(const Identifier &key, const Value &val, Multi throw InvalidInputException("Cannot use NULL as argument for %s", key); } options.allow_empty = BooleanValue::Get(val); + } else if (key == "column_statistics") { + options.SetColumnStatistics(key, val); } else if (key == "maximum_sample_files") { - if (val.IsNull()) { - throw BinderException("Cannot use NULL as argument to key %s", key); - } - auto sample_files = val.DefaultCastAs(LogicalType::BIGINT).GetValue(); - if (sample_files == -1) { - options.maximum_sample_files = NumericLimits::Maximum(); - } else if (sample_files > 0) { - options.maximum_sample_files = NumericCast(sample_files); - } else { - throw BinderException("\"maximum_sample_files\" parameter must be positive, or -1 to remove the limit " - "on the number of files used to determine the schema."); - } + options.SetMaximumSampleFiles(key, val); } else if (key == "hive_types_autocast" || key == "hive_type_autocast") { if (val.IsNull()) { throw InvalidInputException("Cannot use NULL as argument for %s", key); @@ -259,6 +556,9 @@ bool MultiFileReader::ParseOption(const Identifier &key, const Value &val, Multi } // verify that all the children of the struct value are VARCHAR auto &children = StructValue::GetChildren(val); + if (children.empty()) { + throw InvalidInputException("'hive_types' must specify the type of at least one column"); + } for (idx_t i = 0; i < children.size(); i++) { const Value &child = children[i]; if (child.type().id() != LogicalType::VARCHAR) { @@ -271,6 +571,19 @@ bool MultiFileReader::ParseOption(const Identifier &key, const Value &val, Multi options.hive_types_schema[name] = transformed_type; } D_ASSERT(!options.hive_types_schema.empty()); + } else if (key == "bound_schema") { + options.SetBoundSchema(val, context); + } else if (key == "schema") { + options.schema = ParseSchemaOption(context, val); + if (options.schema.empty()) { + throw BinderException("'schema' cannot be empty"); + } + options.auto_detect_hive_partitioning = false; + } else if (key == "file_row_number") { + if (val.IsNull()) { + throw InvalidInputException("Cannot use NULL as argument for %s", key); + } + options.file_row_number = BooleanValue::Get(val); } else { return false; } @@ -303,22 +616,66 @@ unique_ptr MultiFileReader::DynamicFilterPushdown(const MultiFile bool MultiFileReader::Bind(MultiFileOptions &options, MultiFileList &files, vector &return_types, vector &names, MultiFileReaderBindData &bind_data) { - // The Default MultiFileReader can not perform any binding as it uses MultiFileLists with no schema information. - return false; + if (options.schema.empty()) { + // the default MultiFileReader has no schema information of its own - the files have to be bound + return false; + } + if (options.union_by_name || options.hive_partitioning) { + throw BinderException("'schema' cannot be combined with union_by_name=true or hive_partitioning=true"); + } + // the files are mapped onto the schema by field id when its columns are identified by INTEGER, by name otherwise + const bool by_field_id = options.schema[0].identifier.type().id() == LogicalTypeId::INTEGER; + for (auto &column : options.schema) { + names.push_back(column.name); + return_types.push_back(column.type); + bind_data.schema.push_back(column); + } + bind_data.mapping = by_field_id ? MultiFileColumnMappingMode::BY_FIELD_ID : MultiFileColumnMappingMode::BY_NAME; + return true; } void MultiFileReader::BindOptions(MultiFileOptions &options, MultiFileList &files, vector &return_types, vector &names, MultiFileReaderBindData &bind_data) { + const bool has_bound_schema = !options.bound_schema.empty(); + if (has_bound_schema) { + // Roles bind directly to their final declared positions; the physical reader's schema is unchanged. + names.clear(); + return_types.clear(); + for (auto &column : options.bound_schema) { + names.push_back(column.name); + return_types.push_back(column.type); + } + } + auto bind_generated_column = [&](const Identifier &name, const LogicalType &type) -> idx_t { + if (has_bound_schema) { + auto index = StringUtil::CIFind(names, name); + if (index == DConstants::INVALID_INDEX || !return_types[index].EqualsIncludingCollation(type)) { + throw BinderException("'bound_schema' column \"%s\" does not match the resolved output", name); + } + return index; + } + auto index = names.size(); + names.push_back(name); + return_types.push_back(type); + return index; + }; + // Add the file_row_number column - it is read from the row number virtual column of the reader + if (options.file_row_number) { + if (!has_bound_schema && StringUtil::CIFind(names, "file_row_number") != DConstants::INVALID_INDEX) { + throw BinderException("Option file_row_number adds column \"file_row_number\", but a column with this " + "name is also in the file"); + } + bind_data.file_row_number_idx = bind_generated_column("file_row_number", LogicalType::BIGINT); + } + // Add generated constant column for filename if (options.filename) { - if (std::find(names.begin(), names.end(), options.filename_column) != names.end()) { + if (!has_bound_schema && std::find(names.begin(), names.end(), options.filename_column) != names.end()) { throw BinderException("Option filename adds column \"%s\", but a column with this name is also in the " "file. Try setting a different name: filename=''", options.filename_column); } - bind_data.filename_idx = names.size(); - return_types.emplace_back(LogicalType::VARCHAR); - names.emplace_back(options.filename_column); + bind_data.filename_idx = bind_generated_column(Identifier(options.filename_column), LogicalType::VARCHAR); } // Add generated constant columns from hive partitioning scheme @@ -348,12 +705,20 @@ void MultiFileReader::BindOptions(MultiFileOptions &options, MultiFileList &file } } + if (has_bound_schema && partitions.size() != options.hive_types_schema.size()) { + throw BinderException("'bound_schema' requires hive_types for every Hive partition column"); + } if (!options.hive_types_schema.empty()) { // verify that all hive_types are existing partitions options.VerifyHiveTypesArePartitions(partitions); } for (auto &part : partitions) { + if (has_bound_schema) { + auto index = bind_generated_column(Identifier(part.first), options.GetHiveLogicalType(part.first)); + bind_data.hive_partitioning_indexes.emplace_back(part.first, index); + continue; + } idx_t hive_partitioning_index; auto lookup = std::find_if(names.begin(), names.end(), [&](const Identifier &col_name) { return col_name == part.first; }); @@ -431,7 +796,10 @@ void MultiFileReader::FinalizeBind(MultiFileReaderData &reader_data, const Multi auto &local_columns = reader_data.reader->GetColumns(); auto &filename = reader_data.reader->GetFileName(); case_insensitive_map_t name_map; - if (file_options.SchemaIsUnion()) { + // a column a file lacks is read as NULL when the schema is a union of the files - a schema given up front instead + // says what such a column is read as, which the column mapper takes care of + const bool missing_columns_are_null = file_options.SchemaIsUnion() && file_options.schema.empty(); + if (missing_columns_are_null) { for (idx_t col_idx = 0; col_idx < local_columns.size(); col_idx++) { auto &column = local_columns[col_idx]; name_map[column.name.GetIdentifierName()] = col_idx; @@ -453,7 +821,7 @@ void MultiFileReader::FinalizeBind(MultiFileReaderData &reader_data, const Multi if (IsVirtualColumn(column_id)) { continue; } - if (file_options.SchemaIsUnion()) { + if (missing_columns_are_null) { auto &column = global_columns[column_id]; auto &name = column.name; auto &type = column.type; @@ -618,15 +986,20 @@ TablePartitionInfo MultiFileReader::GetPartitionInfo(ClientContext &context, con TableFunctionSet MultiFileReader::CreateFunctionSet(TableFunction table_function) { TableFunctionSet function_set {table_function.name}; function_set.AddFunction(table_function); - D_ASSERT(!table_function.GetArguments().empty() && table_function.GetArguments()[0] == LogicalType::VARCHAR); + D_ASSERT(table_function.GetSignature().GetPositionalParameterCount() > 0 && + table_function.GetSignature().GetParameter(0).GetType() == LogicalType::VARCHAR); // the list variant takes ANY as its child type: a file is either a path (VARCHAR) or a STRUCT/VARIANT // holding the path together with the options to open the file with auto list_function = table_function; - list_function.GetArguments()[0] = LogicalType::LIST(LogicalType::ANY); + auto &list_parameter = list_function.GetSignature().GetParameter(0); + list_parameter.SetType(LogicalType::LIST(LogicalType::ANY)); + if (list_parameter.GetName() == "path") { + list_parameter.SetName("paths"); + } function_set.AddFunction(std::move(list_function)); // a single file can also be passed as a VARIANT - without this overload it would implicitly cast to VARCHAR // and the stringified variant would be read as a path - table_function.GetArguments()[0] = LogicalType::VARIANT(); + table_function.GetSignature().GetParameter(0).SetType(LogicalType::VARIANT()); function_set.AddFunction(std::move(table_function)); return function_set; } @@ -751,9 +1124,19 @@ ReaderInitializeType MultiFileReader::InitializeReader(MultiFileReaderData &read const vector &global_column_ids, optional_ptr table_filters, ClientContext &context, MultiFileGlobalState &gstate) { - FinalizeBind(reader_data, bind_data.file_options, bind_data.reader_bind, global_columns, global_column_ids, context, + // the file_row_number column is not a column of the file - it is read from the row number virtual column + auto column_ids = global_column_ids; + auto &file_row_number_idx = bind_data.reader_bind.file_row_number_idx; + if (file_row_number_idx.IsValid()) { + for (auto &column_id : column_ids) { + if (column_id.GetPrimaryIndex() == file_row_number_idx.GetIndex()) { + column_id = ColumnIndex(COLUMN_IDENTIFIER_FILE_ROW_NUMBER); + } + } + } + FinalizeBind(reader_data, bind_data.file_options, bind_data.reader_bind, global_columns, column_ids, context, gstate.multi_file_reader_state.get()); - return CreateMapping(context, reader_data, global_columns, global_column_ids, table_filters, gstate.file_list, + return CreateMapping(context, reader_data, global_columns, column_ids, table_filters, gstate.file_list, bind_data.reader_bind, bind_data.virtual_columns); } @@ -853,6 +1236,7 @@ void MultiFileOptions::AddBatchInfo(BindInfo &bind_info) const { bind_info.InsertOption("union_by_name", Value::BOOLEAN(union_by_name)); bind_info.InsertOption("hive_types_autocast", Value::BOOLEAN(hive_types_autocast)); bind_info.InsertOption("allow_empty", Value::BOOLEAN(allow_empty)); + bind_info.InsertOption("file_row_number", Value::BOOLEAN(file_row_number)); } void UnionByName::CombineUnionTypes(const vector &col_names, const vector &sql_types, @@ -984,8 +1368,256 @@ LogicalType MultiFileOptions::GetHiveLogicalType(const string &hive_partition_co return LogicalType::VARCHAR; } +void MultiFileOptions::SetMaximumSampleFiles(const Identifier &key, const Value &val) { + if (val.IsNull()) { + throw BinderException("Cannot use NULL as argument to key %s", key); + } + if (!TrySetMaximumSampleFiles(val)) { + throw BinderException("\"%s\" parameter must be positive, or -1 to remove the limit on the number of files " + "used to determine the schema.", + key); + } +} + +bool MultiFileOptions::TrySetMaximumSampleFiles(const Value &val) { + if (val.IsNull()) { + return false; + } + auto sample_files = val.DefaultCastAs(LogicalType::BIGINT).GetValue(); + if (sample_files == -1) { + maximum_sample_files = NumericLimits::Maximum(); + return true; + } + if (sample_files <= 0) { + return false; + } + maximum_sample_files = NumericCast(sample_files); + return true; +} + +void MultiFileOptions::SetColumnStatistics(const Identifier &key, const Value &val) { + if (val.IsNull()) { + column_statistics = Value(); + return; + } + // The shape COPY ... (RETURN_STATS) reports column statistics in, so what one COPY returns can be handed to the + // next scan as it is. Anything that casts to it (a MAP literal of other value types, say) is taken. + auto target = LogicalType::MAP(LogicalType::VARCHAR, LogicalType::MAP(LogicalType::VARCHAR, LogicalType::VARCHAR)); + string error; + auto cast = val.DefaultTryCastAs(target, &error); + if (!cast) { + throw InvalidInputException( + "'%s' must be a MAP of column name to a MAP of statistic name ('min', 'max', 'count', 'null_count', " + "'distinct_count') to its value, but '%s' was provided", + key, val.type().ToString()); + } + column_statistics = std::move(*cast); +} + +// A key of the column_statistics option names a column of the scan - the way RETURN_STATS names what it wrote: every +// component of the path quoted, "s"."x" for field x of struct column s, "l"."element" for the elements of list column +// l, "m"."key" / "m"."value" for a map. A key without quotes is a plain top-level column name. +static vector ParseColumnStatisticsKey(const string &key) { + vector path; + if (key.find('"') == string::npos) { + path.push_back(key); + return path; + } + idx_t pos = 0; + while (pos < key.size()) { + if (key[pos] != '"') { + throw InvalidInputException( + "column_statistics: key '%s' is not a quoted column path (\"column\".\"field\")", key); + } + string component; + pos++; + while (true) { + if (pos >= key.size()) { + throw InvalidInputException("column_statistics: key '%s' has an unterminated quote", key); + } + if (key[pos] == '"') { + if (pos + 1 < key.size() && key[pos + 1] == '"') { + component += '"'; + pos += 2; + continue; + } + pos++; + break; + } + component += key[pos++]; + } + path.push_back(std::move(component)); + if (pos < key.size()) { + if (key[pos] != '.') { + throw InvalidInputException("column_statistics: key '%s' has '%c' where a '.' or the end was expected", + key, key[pos]); + } + pos++; + } + } + return path; +} + +// Apply the statistics given for one (possibly nested) column to the statistics object of that column. +static void ApplyColumnStatistics(BaseStatistics &result, const Value &given, const string &key) { + Value min, max; + optional count, null_count, distinct_count; + for (auto &stat : MapValue::GetChildren(given)) { + auto &stat_key_value = StructValue::GetChildren(stat); + if (stat_key_value[0].IsNull() || stat_key_value[1].IsNull()) { + continue; + } + auto stat_name = StringUtil::Lower(StringValue::Get(stat_key_value[0])); + auto &stat_value = stat_key_value[1]; + if (stat_name == "min") { + min = stat_value; + } else if (stat_name == "max") { + max = stat_value; + } else if (stat_name == "count" || stat_name == "null_count" || stat_name == "distinct_count") { + string error; + auto parsed = stat_value.DefaultTryCastAs(LogicalType::UBIGINT, &error); + if (!parsed) { + throw InvalidInputException("column_statistics: '%s' of '%s' must be a count, got '%s'", stat_name, key, + stat_value.ToString()); + } + auto parsed_count = UBigIntValue::Get(*parsed); + if (stat_name == "count") { + count = parsed_count; + } else if (stat_name == "null_count") { + null_count = parsed_count; + } else { + distinct_count = parsed_count; + } + } + // other statistics a writer reports (column_size_bytes, has_nan, ...) say nothing the optimizer uses + } + // Only what is given is claimed: the rest is left unknown + if (null_count.has_value()) { + if (*null_count == 0) { + result.Set(StatsInfo::CANNOT_HAVE_NULL_VALUES); + } + if (count.has_value() && *null_count >= *count) { + result.Set(StatsInfo::CANNOT_HAVE_VALID_VALUES); + } + } + bool has_values = !count.has_value() || !null_count.has_value() || *null_count < *count; + auto &type = result.GetType(); + if (has_values && !min.IsNull() && !max.IsNull() && + BaseStatistics::GetStatsType(type) == StatisticsType::NUMERIC_STATS) { + NumericStats::SetMin(result, min.DefaultCastAs(type, true)); + NumericStats::SetMax(result, max.DefaultCastAs(type, true)); + } + if (distinct_count.has_value() && *distinct_count > 0) { + result.SetDistinctCount(*distinct_count); + } +} + +// The statistics object a path below a column's leads to, following the RETURN_STATS naming of nested columns; +// nullptr when the path names nothing of the type. +static optional_ptr DescendColumnStatistics(BaseStatistics &stats, const vector &path, + idx_t depth) { + if (depth >= path.size()) { + return &stats; + } + auto &type = stats.GetType(); + auto &component = path[depth]; + switch (type.id()) { + case LogicalTypeId::STRUCT: { + for (idx_t i = 0; i < StructType::GetChildCount(type); i++) { + if (StructType::GetChildName(type, i) == component) { + return DescendColumnStatistics(StructStats::GetChildStats(stats, i), path, depth + 1); + } + } + return nullptr; + } + case LogicalTypeId::LIST: + case LogicalTypeId::ARRAY: + if (component == "element") { + return DescendColumnStatistics(ListStats::GetChildStats(stats), path, depth + 1); + } + return nullptr; + case LogicalTypeId::MAP: { + // a map's statistics are those of a list of key/value structs + auto &entry_stats = ListStats::GetChildStats(stats); + if (component == "key") { + return DescendColumnStatistics(StructStats::GetChildStats(entry_stats, 0), path, depth + 1); + } + if (component == "value") { + return DescendColumnStatistics(StructStats::GetChildStats(entry_stats, 1), path, depth + 1); + } + return nullptr; + } + default: + return nullptr; + } +} + +void MultiFileOptions::VerifyColumnStatistics(const vector &names, const vector &types) const { + if (column_statistics.IsNull()) { + return; + } + for (auto &entry : MapValue::GetChildren(column_statistics)) { + auto &key_value = StructValue::GetChildren(entry); + if (key_value[0].IsNull() || key_value[1].IsNull()) { + continue; + } + auto &key = StringValue::Get(key_value[0]); + auto path = ParseColumnStatisticsKey(key); + optional_idx column; + for (idx_t i = 0; i < names.size(); i++) { + if (names[i] == Identifier(path[0])) { + column = i; + break; + } + } + if (!column.IsValid()) { + throw InvalidInputException("column_statistics: '%s' names no column of the scan", key); + } + // Build the statistics once here, so a key that names nothing of the column, a count that is not one or a + // bound that does not cast to the column's type fails at bind rather than when the optimizer first asks + auto stats = BaseStatistics::CreateUnknown(types[column.GetIndex()]); + auto target = DescendColumnStatistics(stats, path, 1); + if (!target) { + throw InvalidInputException("column_statistics: '%s' names no part of column \"%s\" of type %s", key, + path[0], types[column.GetIndex()].ToString()); + } + ApplyColumnStatistics(*target, key_value[1], key); + } +} + +unique_ptr MultiFileOptions::GetColumnStatistics(const Identifier &column_name, + const LogicalType &type) const { + if (column_statistics.IsNull()) { + return nullptr; + } + unique_ptr result; + for (auto &entry : MapValue::GetChildren(column_statistics)) { + auto &key_value = StructValue::GetChildren(entry); + if (key_value[0].IsNull() || key_value[1].IsNull()) { + continue; + } + auto &key = StringValue::Get(key_value[0]); + auto path = ParseColumnStatisticsKey(key); + if (Identifier(path[0]) != column_name) { + continue; + } + if (!result) { + // Nothing is claimed about the column, or any part of it, that the option does not say + result = BaseStatistics::CreateUnknown(type).ToUnique(); + } + auto target = DescendColumnStatistics(*result, path, 1); + if (!target) { + // checked at bind, see VerifyColumnStatistics + throw InternalException("column_statistics: '%s' names no part of column \"%s\"", key, + column_name.GetIdentifierName()); + } + ApplyColumnStatistics(*target, key_value[1], key); + } + return result; +} + bool MultiFileOptions::AnySet() const { - return filename || hive_partitioning || union_by_name; + return filename || hive_partitioning || union_by_name || !schema.empty() || !bound_schema.empty(); } Value MultiFileOptions::GetHivePartitionValue(const string &value, const string &key, ClientContext &context) const { diff --git a/src/duckdb/src/common/multi_file/table_function_multi_file.cpp b/src/duckdb/src/common/multi_file/table_function_multi_file.cpp index 6fe7dda13..4e224291a 100644 --- a/src/duckdb/src/common/multi_file/table_function_multi_file.cpp +++ b/src/duckdb/src/common/multi_file/table_function_multi_file.cpp @@ -1,5 +1,9 @@ #include "duckdb/common/multi_file/table_function_multi_file.hpp" +#include + +#include "duckdb/common/bind_helpers.hpp" + #include "duckdb/execution/execution_context.hpp" #include "duckdb/function/function_set.hpp" #include "duckdb/parallel/async_result.hpp" @@ -12,7 +16,20 @@ namespace duckdb { //===--------------------------------------------------------------------===// // States //===--------------------------------------------------------------------===// -class TableFunctionMultiFileGlobalState : public GlobalTableFunctionState {}; +class TableFunctionMultiFileGlobalState : public GlobalTableFunctionState { +public: + //! The operator the files are scanned for, and how many files that scan reads + optional_ptr op; + idx_t file_count = 1; + //! What the wrapped function has reported for the files of this scan - collected whenever a file may have + //! counted more, so that a reader can be released without losing what it counted. + //! "scanned" is handed over and cleared on every report - the profiler sums what each thread reports, so a + //! scanned row group must be handed over exactly once. "total" is the size of the scan, which every thread + //! reports identically and the profiler does not sum, so it is never cleared + mutex metrics_lock; + idx_t collected_scanned = 0; + idx_t collected_total = 0; +}; class TableFunctionMultiFileLocalState : public LocalTableFunctionState { public: @@ -32,41 +49,102 @@ class TableFunctionMultiFileLocalState : public LocalTableFunctionState { // Reader //===--------------------------------------------------------------------===// TableFunctionFileReader::TableFunctionFileReader(TableFunction function_p, OpenFileInfo file_p, - named_parameter_map_t named_parameters_p, string reader_type_p) + named_argument_map_t named_parameters_p, + TableFunctionMultiFileSettings settings_p) : BaseFileReader(std::move(file_p)), function(std::move(function_p)), - named_parameters(std::move(named_parameters_p)), reader_type(std::move(reader_type_p)), file_is_assigned(false), + named_parameters(std::move(named_parameters_p)), settings(std::move(settings_p)), file_is_assigned(false), exhausted(false) { } TableFunctionFileReader::~TableFunctionFileReader() = default; string TableFunctionFileReader::GetReaderType() const { - return reader_type; + return settings.reader_type; } -void TableFunctionFileReader::BindFunction(ClientContext &context, const TableFunctionFileReaderOptions &options) { +bool TableFunctionFileReader::UseCastMap() const { + // when the function can produce columns as a different type than it bound them, the conversion is pushed into + // the function instead of being applied to its output + return settings.supports_cast_map; +} + +void TableFunctionFileReader::BindFunction(ClientContext &context, const TableFunctionFileReaderOptions &options, + const MultiFileOptions &file_options, bool schema_only, + optional_ptr file_bind_data) { if (!function.bind) { throw InternalException("Table function %s cannot be wrapped in a multi file function - it has no bind", function.name); } + // a function that takes the file as ANY gets it together with the options to open it with - one that takes a + // VARCHAR only gets its path vector inputs; - inputs.emplace_back(file.path); + if (function.GetSignature().GetParameter(0).GetType().id() == LogicalTypeId::ANY) { + inputs.emplace_back(file.ToValue()); + } else { + inputs.emplace_back(file.path); + } auto parameters = named_parameters; + function.GetSignature().FillNamedDefaults(context, parameters); vector input_table_types; vector input_table_names; TableFunctionRef empty_ref; + BoundTableFunction bound_function(function); TableFunctionBindInput bind_input(inputs, parameters, input_table_types, input_table_names, - function.function_info.get(), nullptr, function, empty_ref); + function.function_info.get(), nullptr, bound_function, empty_ref); + TableFunctionFileBindInput file_input; + file_input.multi_file_options = file_options; + file_input.multi_file_scan = options.multi_file_scan; + file_input.schema_only = schema_only; + file_input.file_bind_data = file_bind_data; if (!options.expected_names.empty()) { // the schema of the scan is known upfront - bind this file against that schema - bind_input.expected_names = options.expected_names; - bind_input.expected_types = options.expected_types; - bind_input.expected_bind_data = options.schema_bind_data.get(); + file_input.expected_names = options.expected_names; + file_input.expected_types = options.expected_types; + file_input.expected_bind_data = options.schema_bind_data.get(); } + bind_input.multi_file_input = file_input; names.clear(); types.clear(); bind_data = function.bind(context, bind_input, types, names); - columns = MultiFileColumnDefinition::ColumnsFromNamesAndTypes(names, types); + // a function can describe what it bound in more detail than its names and types - e.g. with the field ids of its + // columns, and options that describe the file, which are exposed as the metadata of the reader of the file + optional bind_info; + if (function.get_bind_info && bind_data) { + TableFunctionGetBindInfoInput bind_info_input(context, bind_data.get()); + bind_info = function.get_bind_info(bind_info_input); + } + if (settings.get_file_columns && bind_data) { + // the function describes the columns of its file itself - names and types alone would lose their nested + // structure and the identifiers that map the files of a scan onto one another + columns = settings.get_file_columns(context, *bind_data); + } else if (bind_info && !bind_info->column_identifiers.empty()) { + columns.clear(); + for (idx_t i = 0; i < names.size(); i++) { + columns.push_back(MultiFileColumnDefinition::CreateNested(names[i], types[i])); + } + for (auto &entry : bind_info->column_identifiers) { + if (entry.column_index >= columns.size()) { + throw InternalException("Table function %s attached an identifier to column %llu of file \"%s\", but " + "bound %llu", + function.name, entry.column_index, file.path, columns.size()); + } + columns[entry.column_index].ResolveChildPath(entry.child_path).identifier = entry.identifier; + } + } else { + columns = MultiFileColumnDefinition::ColumnsFromNamesAndTypes(names, types); + } + metadata.clear(); + if (bind_info) { + // the options are kept unordered - sort them, so the same file always has the same metadata + vector keys; + for (auto &option : bind_info->options) { + keys.push_back(option.first); + } + std::sort(keys.begin(), keys.end()); + for (auto &key : keys) { + metadata.insert(key, bind_info->options[key]); + } + } cardinality = optional_idx(); if (function.cardinality) { @@ -77,12 +155,25 @@ void TableFunctionFileReader::BindFunction(ClientContext &context, const TableFu } } +unique_ptr TableFunctionUnionData::GetStatistics(ClientContext &context, const Identifier &name) { + if (!statistics || !bind_data) { + return nullptr; + } + for (idx_t col_idx = 0; col_idx < names.size(); col_idx++) { + if (Identifier(names[col_idx]) == name) { + return statistics(context, bind_data.get(), col_idx); + } + } + return nullptr; +} + shared_ptr TableFunctionFileReader::GetUnionData(idx_t file_idx) { auto result = make_shared_ptr(file); result->names = IdentifiersToStrings(names); result->types = types; result->cardinality = cardinality; result->bind_data = bind_data; + result->statistics = function.statistics; if (file_idx == 0) { // keep the first reader around so we don't need to bind it again result->reader = shared_from_this(); @@ -102,18 +193,75 @@ unique_ptr TableFunctionFileReader::GetStatistics(ClientContext return nullptr; } +unique_ptr TableFunctionFileReader::GetVirtualColumnStatistics(ClientContext &context, + column_t virtual_column_id) { + if (!function.statistics || !bind_data) { + return nullptr; + } + return function.statistics(context, bind_data.get(), virtual_column_id); +} + void TableFunctionFileReader::AddVirtualColumn(column_t virtual_column_id) { - throw NotImplementedException("Table function %s does not support reading virtual columns", function.name); + if (!function.get_virtual_columns) { + throw NotImplementedException("Table function %s does not support reading virtual columns", function.name); + } + // our caller has given this virtual column an index of its own, past the columns the function bound - the + // function is told which virtual column that index stands for when it is initialized + D_ASSERT(!columns.empty()); + virtual_columns[columns.size() - 1] = virtual_column_id; +} + +void TableFunctionFileReader::CollectMetrics(ClientContext &context, GlobalTableFunctionState &gstate_p) { + if (!function.get_metrics || !global_state) { + return; + } + auto &gstate = gstate_p.Cast(); + // several threads can collect the metrics of a file at once - they are asked for under the lock, so that the + // growth of the file is added only once + lock_guard guard(gstate.metrics_lock); + OperatorMetrics file_metrics; + TableFunctionGetMetricsInput input(context, bind_data.get(), nullptr, global_state.get(), file_metrics); + function.get_metrics(input); + gstate.collected_scanned += file_metrics.row_groups_scanned; + // the function reports the size of its file as a whole - only add what it has grown by since we last asked + gstate.collected_total += file_metrics.total_row_groups_to_scan - collected_file_total; + collected_file_total = file_metrics.total_row_groups_to_scan; +} + +void TableFunctionFileReader::FinishFile(ClientContext &context, GlobalTableFunctionState &gstate) { + // this reader is about to be released - collect what the wrapped function counted for its file + CollectMetrics(context, gstate); } -TableFunctionInitInput TableFunctionFileReader::GetInitInput() const { +TableFunctionInitInput TableFunctionFileReader::GetInitInput(TableFunctionFileInitInput &file_input) const { // the multi file reader does the projection/filter pruning itself - the wrapped function only needs to emit the // (local) columns it is asked for, in order vector projection_ids; - return TableFunctionInitInput(bind_data.get(), column_indexes, projection_ids, filters.get()); + TableFunctionInitInput input(bind_data.get(), column_indexes, projection_ids, filters.get()); + input.op = scan_op; + if (!virtual_columns.empty()) { + file_input.virtual_columns = virtual_columns; + } + if (deletion_filter) { + file_input.deletion_filter = *deletion_filter; + } + if (!expression_map.empty()) { + file_input.expression_map = expression_map; + } + if (!filter_global_indices.empty()) { + file_input.filter_global_indices = filter_global_indices; + } + file_input.file_index = file_list_idx; + file_input.file_count = scan_file_count; + if (!cast_map.empty()) { + file_input.cast_map = cast_map; + } + input.multi_file_input = file_input; + return input; } -optional_idx TableFunctionFileReader::MaxThreads(ClientContext &context) { +optional_idx TableFunctionFileReader::MaxThreads(ClientContext &context, GlobalTableFunctionState &gstate) { + SetScanState(gstate); InitializeFunctionState(context); if (!global_state) { // no global state - the wrapped function is scanned by a single thread @@ -122,25 +270,47 @@ optional_idx TableFunctionFileReader::MaxThreads(ClientContext &context) { return global_state->MaxThreads(); } +void TableFunctionFileReader::SetScanState(GlobalTableFunctionState &gstate) { + auto &scan_state = gstate.Cast(); + scan_op = scan_state.op; + scan_file_count = scan_state.file_count; +} + void TableFunctionFileReader::InitializeFunctionState(ClientContext &context) { - lock_guard guard(lock); - if (global_state || !function.init_global) { - return; - } - auto init_input = GetInitInput(); - global_state = function.init_global(context, init_input); + CallOnce(initialize_once, [&] { + if (!function.init_global) { + return; + } + TableFunctionFileInitInput file_input; + auto init_input = GetInitInput(file_input); + global_state = function.init_global(context, init_input); + }); } -void TableFunctionFileReader::PrepareReader(ClientContext &context, GlobalTableFunctionState &) { +void TableFunctionFileReader::PrepareReader(ClientContext &context, GlobalTableFunctionState &gstate) { + SetScanState(gstate); InitializeFunctionState(context); } -bool TableFunctionFileReader::TryInitializeScan(ClientContext &context, GlobalTableFunctionState &, +void TableFunctionFileReader::PrepareReadAhead(ClientContext &context, GlobalTableFunctionState &gstate) { + if (!settings.prepare_read_ahead) { + return; + } + // the file has been opened on the read-ahead pool - let the function pre-open what its scan needs, so that + // claiming a batch does no I/O while the global lock of the scan is held + SetScanState(gstate); + InitializeFunctionState(context); + TableFunctionInput input(bind_data.get(), nullptr, global_state.get()); + settings.prepare_read_ahead(context, input); +} + +bool TableFunctionFileReader::TryInitializeScan(ClientContext &context, GlobalTableFunctionState &gstate, LocalTableFunctionState &lstate_p) { if (exhausted) { return false; } // the initial reader obtained during binding is never prepared - initialize it here instead + SetScanState(gstate); InitializeFunctionState(context); auto &lstate = lstate_p.Cast(); if (!function.init_local) { @@ -150,22 +320,47 @@ bool TableFunctionFileReader::TryInitializeScan(ClientContext &context, GlobalTa return false; } lstate.local_state = nullptr; - } else if (lstate.reader.get() != this) { - // we are moving to a new file - initialize a local state for it - auto init_input = GetInitInput(); + } else if (lstate.reader.get() != this && !(settings.reuses_local_state && lstate.local_state)) { + // we are moving to a new file - initialize a local state for it, unless the function carries what it has + // learned so far over to the next file in the one it already has + TableFunctionFileInitInput file_input; + auto init_input = GetInitInput(file_input); ExecutionContext execution_context(context, lstate.thread_context, nullptr); lstate.local_state = function.init_local(execution_context, init_input, global_state.get()); } lstate.reader = shared_from_this(); - if (function.claim_batch) { + if (settings.claim_batch) { // the function scans the file in batches - claim one, so that every batch becomes a scan of its own that // the multi file reader can put back in order TableFunctionInput input(bind_data.get(), lstate.local_state.get(), global_state.get()); - return function.claim_batch(context, input); + return settings.claim_batch(context, input); } return true; } +void TableFunctionFileReader::PrepareScan(ClientContext &context, GlobalTableFunctionState &, + LocalTableFunctionState &lstate_p) { + if (!settings.prepare_batch) { + return; + } + auto &lstate = lstate_p.Cast(); + TableFunctionInput input(bind_data.get(), lstate.local_state.get(), global_state.get()); + settings.prepare_batch(context, input); +} + +AsyncResult TableFunctionFileReader::ScheduleIO(ClientContext &context, GlobalTableFunctionState &gstate, + LocalTableFunctionState &lstate_p) { + if (!settings.schedule_io) { + return SourceResultType::HAVE_MORE_OUTPUT; + } + auto &lstate = lstate_p.Cast(); + TableFunctionInput input(bind_data.get(), lstate.local_state.get(), global_state.get()); + auto result = settings.schedule_io(context, input); + // the file may already have been finished while this batch was queued - collect what scheduling it counted + CollectMetrics(context, gstate); + return result; +} + AsyncResult TableFunctionFileReader::Scan(ClientContext &context, GlobalTableFunctionState &, LocalTableFunctionState &lstate_p, DataChunk &chunk) { auto &lstate = lstate_p.Cast(); @@ -174,7 +369,7 @@ AsyncResult TableFunctionFileReader::Scan(ClientContext &context, GlobalTableFun input.async_result = AsyncResultType::IMPLICIT; input.results_execution_mode = AsyncResultsExecutionMode::SYNCHRONOUS; function.function(context, input, chunk); - if (chunk.size() == 0 && !function.claim_batch) { + if (chunk.size() == 0 && !settings.claim_batch) { // an empty chunk signals the end of the scan for this thread - when the function scans in batches it only // signals the end of the current batch, and the next batch is claimed by TryInitializeScan exhausted = true; @@ -183,11 +378,11 @@ AsyncResult TableFunctionFileReader::Scan(ClientContext &context, GlobalTableFun } void TableFunctionFileReader::FinishBatch(ClientContext &context, LocalTableFunctionState &local_state) { - if (!function.finish_batch) { + if (!settings.finish_batch) { return; } TableFunctionInput input(bind_data.get(), &local_state, global_state.get()); - function.finish_batch(context, input); + settings.finish_batch(context, input); } double TableFunctionFileReader::GetProgressInFile(ClientContext &context) { @@ -198,7 +393,7 @@ double TableFunctionFileReader::GetProgressInFile(ClientContext &context) { } InsertionOrderPreservingMap TableFunctionFileReader::GetMetadata() const { - return {}; + return metadata; } //===--------------------------------------------------------------------===// @@ -223,6 +418,7 @@ FileGlobInput TableFunctionMultiFileWrapper::GetGlobInput() { void TableFunctionMultiFileWrapper::InitializeFileOptions(MultiFileOptions &file_options) { file_options.maximum_sample_files = settings.maximum_sample_files; + file_options.sampled_schema_is_union = settings.sampled_schema_is_union; } unique_ptr TableFunctionMultiFileWrapper::InitializeOptions(ClientContext &context, @@ -230,18 +426,46 @@ unique_ptr TableFunctionMultiFileWrapper::InitializeOptio return make_uniq(); } +optional> TableFunctionMultiFileWrapper::GetDeclaredOption(const Identifier &key) const { + auto &signature = function.GetSignature(); + auto index = signature.GetParameterIndexByName(key); + if (index.IsValid()) { + auto ¶m = signature.GetParameter(index.GetIndex()); + if (param.GetKind() != FunctionParameterKind::KEYWORD_ONLY) { + return {}; + } + return make_pair(param.GetName(), param.GetType()); + } + auto option_schema = signature.GetTypedKwargs(); + auto option = option_schema ? option_schema->Find(key) : nullptr; + if (!option) { + return {}; + } + return make_pair(option->name, option->type); +} + bool TableFunctionMultiFileWrapper::ParseNamedParameter(const Identifier &key, const Value &val, TableFunctionFileReaderOptions &options) const { - if (function.named_parameters.find(key) == function.named_parameters.end()) { + auto declared_option = GetDeclaredOption(key); + if (!declared_option) { return false; } - options.named_parameters[key] = val; + // under the name of the option, whichever alias was used + options.named_parameters[declared_option->first] = val; return true; } bool TableFunctionMultiFileWrapper::ParseOption(ClientContext &context, const Identifier &key, const Value &val, - MultiFileOptions &, BaseFileReaderOptions &options_p) { - return ParseNamedParameter(key, val, options_p.Cast()); + MultiFileOptions &file_options, BaseFileReaderOptions &options_p) { + if (!ParseNamedParameter(key, val, options_p.Cast())) { + return false; + } + if (!settings.sample_files_parameter.empty() && key == settings.sample_files_parameter) { + // this parameter of the wrapped function also sets how many files are sampled to determine the schema - a + // value we cannot use is left to the function to report, in its own words, when it binds + file_options.TrySetMaximumSampleFiles(val); + } + return true; } bool TableFunctionMultiFileWrapper::ParseCopyOption(ClientContext &context, const Identifier &key, @@ -249,16 +473,19 @@ bool TableFunctionMultiFileWrapper::ParseCopyOption(ClientContext &context, cons vector &, vector &) { // COPY supports exactly the named parameters of the wrapped function - the only difference is that COPY passes // the values as a list, and that a bare option (e.g. "auto_detect") means "true" - auto entry = function.named_parameters.find(key); - if (entry == function.named_parameters.end()) { + auto declared_option = GetDeclaredOption(key); + if (!declared_option) { return false; } - auto &type = entry->second; - if (values.size() > 1) { - throw BinderException("COPY parameter %s expects a single argument", key); - } + auto &type = declared_option->second; Value val; - if (values.empty()) { + if (type.id() == LogicalTypeId::LIST || (type.id() == LogicalTypeId::ANY && values.size() != 1)) { + // COPY passes the elements of a list-valued option as separate values - an option that takes any value is + // given the list as-is, so that it can report what it expected itself + val = ConvertVectorToValue(values); + } else if (values.size() > 1) { + throw BinderException("COPY parameter %s expects a single argument", key); + } else if (values.empty()) { // a bare option is a shorthand for setting a flag - only boolean parameters can be given like that if (type.id() != LogicalTypeId::BOOLEAN) { throw BinderException("COPY parameter %s expects a single argument", key); @@ -302,14 +529,18 @@ optional_idx TableFunctionMultiFileWrapper::MaxThreads(ClientContext &context, c // the file has not been opened yet - we cannot know how many threads it wants return optional_idx(); } - return global_state.readers[0]->reader->Cast().MaxThreads(context); + return global_state.readers[0]->reader->Cast().MaxThreads(context, + *global_state.global_state); } void TableFunctionMultiFileWrapper::CombineSchemas(ClientContext &context, const vector> &union_data, - vector &return_types, vector &names) { + bool union_by_name, vector &return_types, + vector &names) { schema_combined = true; - if (function.combine_schema) { + // combine the types of the files by name - the function can replace or adjust the result + MultiFileReaderInterface::CombineSchemas(context, union_data, union_by_name, return_types, names); + if (settings.combine_schema) { vector> bind_data; bool have_all_bind_data = true; for (auto &data : union_data) { @@ -321,28 +552,43 @@ void TableFunctionMultiFileWrapper::CombineSchemas(ClientContext &context, bind_data.emplace_back(*function_data); } if (have_all_bind_data) { - TableFunctionCombineSchemaInput input(bind_data); - combined_bind_data = function.combine_schema(context, input, return_types, names); - if (combined_bind_data) { - // the function combined the schemas itself - every file is read using the resulting bind data - combined_names = names; - combined_types = return_types; - ReleaseBindData(union_data); - return; - } - return_types.clear(); - names.clear(); + // when the function returns bind data, every file is read using it - otherwise the files are bound + // individually and reconciled with the schema below + TableFunctionCombineSchemaInput input(bind_data, union_by_name); + combined_bind_data = settings.combine_schema(context, input, return_types, names); } } - // fall back to combining the return types of the files - MultiFileReaderInterface::CombineSchemas(context, union_data, return_types, names); combined_names = names; combined_types = return_types; - ReleaseBindData(union_data); + if (combined_bind_data) { + // every file is re-bound against the schema the function combined - what they bound to on their own is stale + ReleaseBindData(union_data); + } +} + +void TableFunctionMultiFileWrapper::GetVirtualColumns(ClientContext &context, MultiFileBindData &bind_data, + virtual_column_map_t &result) { + if (!function.get_virtual_columns) { + return; + } + // the virtual columns of the scan are those the wrapped function can produce for a file of it + auto &data = bind_data.bind_data->Cast(); + for (auto &entry : function.get_virtual_columns(context, data.options.schema_bind_data.get())) { + result.insert(entry); + } } void TableFunctionMultiFileWrapper::FinalizeBindData(MultiFileBindData &multi_file_data) { - if (!schema_combined) { + if (!multi_file_data.file_options.schema.empty()) { + // the schema was given up front, so BindReader never ran to find out whether the scan reads several files + auto &data = multi_file_data.bind_data->Cast(); + data.options.multi_file_scan = multi_file_data.file_list->GetExpandResult() == FileExpandResult::MULTIPLE_FILES; + return; + } + if (!schema_combined || !combined_bind_data) { + // either the schema comes from a single file, or the schemas were combined by type. In the latter case every + // file is bound on its own and the column mapper reconciles it with the combined schema - only a schema the + // function combined itself can be used to read every file return; } auto &data = multi_file_data.bind_data->Cast(); @@ -358,8 +604,8 @@ void TableFunctionMultiFileWrapper::FinalizeBindData(MultiFileBindData &multi_fi } } -//! The per-file bind data is only kept around to combine the schemas - release it afterwards so we don't hold on to -//! the bind data of every file for the duration of the query +//! Drop the per-file bind data of a scan whose schema the function combined itself - every file is re-bound against +//! that combined schema, so what the files bound to on their own is of no use anymore void TableFunctionMultiFileWrapper::ReleaseBindData(const vector> &union_data) { for (auto &data : union_data) { data->Cast().bind_data.reset(); @@ -369,6 +615,9 @@ void TableFunctionMultiFileWrapper::ReleaseBindData(const vector &return_types, vector &names, MultiFileBindData &bind_data) { auto &data = bind_data.bind_data->Cast(); + // whether the scan reads several files. The file list is only asked here, where it has been created and is + // about to be read anyway - a list that is built lazily is not ready to be expanded any earlier + data.options.multi_file_scan = bind_data.file_list->GetExpandResult() == FileExpandResult::MULTIPLE_FILES; if (data.HasExpectedSchema()) { // the schema is already known (COPY) - there is no need to sample any files to determine it bind_data.file_options.maximum_sample_files = 1; @@ -385,8 +634,12 @@ void TableFunctionMultiFileWrapper::BindReader(ClientContext &context, vector -TableFunctionMultiFileWrapper::InitializeGlobalState(ClientContext &, MultiFileBindData &, MultiFileGlobalState &) { - return make_uniq(); +TableFunctionMultiFileWrapper::InitializeGlobalState(ClientContext &, MultiFileBindData &, + MultiFileGlobalState &global_state) { + auto result = make_uniq(); + result->op = global_state.op; + result->file_count = global_state.file_list.GetTotalFileCount(); + return std::move(result); } unique_ptr TableFunctionMultiFileWrapper::InitializeLocalState(ClientContext &context, @@ -394,6 +647,19 @@ unique_ptr TableFunctionMultiFileWrapper::InitializeLoc return make_uniq(context); } +bool TableFunctionMultiFileWrapper::SupportsReadAhead(const MultiFileBindData &bind_data) const { + if (!settings.supports_read_ahead || !settings.schedule_io) { + return false; + } + auto &data = bind_data.bind_data->Cast(); + auto file_bind_data = data.options.schema_bind_data; + if (!file_bind_data && !bind_data.union_readers.empty()) { + // the schema was combined from the files rather than taken from one of them - ask the first of them + file_bind_data = bind_data.union_readers[0]->Cast().bind_data; + } + return settings.supports_read_ahead(file_bind_data.get()); +} + void TableFunctionMultiFileWrapper::FinishReading(ClientContext &context, GlobalTableFunctionState &, LocalTableFunctionState &lstate_p) { auto &lstate = lstate_p.Cast(); @@ -405,11 +671,11 @@ void TableFunctionMultiFileWrapper::FinishReading(ClientContext &context, Global shared_ptr TableFunctionMultiFileWrapper::CreateReader(ClientContext &context, const OpenFileInfo &file, BaseFileReaderOptions &options_p, - const MultiFileOptions &) { + const MultiFileOptions &file_options) { auto &options = options_p.Cast(); - auto result = - make_shared_ptr(function, file, options.named_parameters, settings.reader_type); - result->BindFunction(context, options); + auto result = make_shared_ptr(function, file, options.named_parameters, settings); + // this overload binds a file only to determine the schema of the scan - the file is read by a reader of its own + result->BindFunction(context, options, file_options, true); return std::move(result); } @@ -418,17 +684,22 @@ shared_ptr TableFunctionMultiFileWrapper::CreateReader(ClientCon const OpenFileInfo &file, idx_t, const MultiFileBindData &bind_data) { auto &data = bind_data.bind_data->Cast(); - auto result = - make_shared_ptr(function, file, data.options.named_parameters, settings.reader_type); - result->BindFunction(context, data.options); + auto result = make_shared_ptr(function, file, data.options.named_parameters, settings); + result->BindFunction(context, data.options, bind_data.file_options); return std::move(result); } shared_ptr TableFunctionMultiFileWrapper::CreateReader(ClientContext &context, - GlobalTableFunctionState &gstate, + GlobalTableFunctionState &, BaseUnionData &union_data, const MultiFileBindData &bind_data) { - return CreateReader(context, gstate, union_data.file, 0, bind_data); + auto &data = bind_data.bind_data->Cast(); + auto &union_entry = union_data.Cast(); + auto result = + make_shared_ptr(function, union_data.file, data.options.named_parameters, settings); + // this file was already bound to combine the schemas - the bind can reuse what that read from the file + result->BindFunction(context, data.options, bind_data.file_options, false, union_entry.bind_data.get()); + return std::move(result); } unique_ptr TableFunctionMultiFileWrapper::GetCardinality(ClientContext &context, @@ -450,12 +721,26 @@ unique_ptr TableFunctionMultiFileWrapper::GetCardinality(ClientC //===--------------------------------------------------------------------===// using TableFunctionMultiFileFunction = MultiFileFunction; +unique_ptr TableFunctionMultiFileWrapper::MultiFileBindWith( + ClientContext &context, TableFunctionBindInput &input, vector &return_types, vector &names, + TableFunction single_file_function, TableFunctionMultiFileSettings settings) { + return TableFunctionMultiFileFunction::MultiFileBindInterface( + context, input, return_types, names, + make_uniq(std::move(single_file_function), std::move(settings))); +} + static unique_ptr TableFunctionMultiFileBind(ClientContext &context, TableFunctionBindInput &input, vector &return_types, vector &names) { - auto &info = input.info->Cast(); - return TableFunctionMultiFileFunction::MultiFileBindInterface( - context, input, return_types, names, make_uniq(info.function, info.settings)); + if (!input.table_function.function_info) { + // the wrapped function is reached through the function info, which a caller binding the function itself has + // to pass along + throw InvalidInputException("Multi-file table function %s was bound without its function info", + input.table_function.GetName()); + } + auto &info = input.table_function.function_info->Cast(); + return TableFunctionMultiFileWrapper::MultiFileBindWith(context, input, return_types, names, info.function, + info.settings); } unique_ptr TableFunctionMultiFileWrapper::MultiFileBindCopy(ClientContext &context, @@ -463,41 +748,121 @@ unique_ptr TableFunctionMultiFileWrapper::MultiFileBindCopy(Client vector &expected_names, vector &expected_types) { auto &info = input.tf.function_info->Cast(); + return MultiFileBindCopyWith(context, input, expected_names, expected_types, info.function, info.settings); +} + +unique_ptr TableFunctionMultiFileWrapper::MultiFileBindCopyWith( + ClientContext &context, CopyFromFunctionBindInput &input, vector &expected_names, + vector &expected_types, TableFunction single_file_function, TableFunctionMultiFileSettings settings) { return TableFunctionMultiFileFunction::MultiFileBindCopyInterface( context, input, expected_names, expected_types, - make_uniq(info.function, info.settings)); + make_uniq(std::move(single_file_function), std::move(settings))); +} + +//! Report the metrics of the scan: the shared multi-file ones, plus those the wrapped function keeps for each of +//! the files that have been opened +static void TableFunctionMultiFileGetMetrics(TableFunctionGetMetricsInput &input) { + TableFunctionMultiFileFunction::MultiFileGetMetrics(input); + if (!input.global_state) { + return; + } + auto &gstate = input.global_state->Cast(); + if (!gstate.global_state) { + return; + } + auto &scan_state = gstate.global_state->Cast(); + { + // collect what the files that are still open have counted so far + lock_guard guard(gstate.lock); + for (auto &reader_data : gstate.readers) { + // a file that is still being opened has its reader assigned under its own lock - skip it until it is open + if (!reader_data || reader_data->file_state != MultiFileFileState::OPEN || !reader_data->reader) { + continue; + } + reader_data->reader->Cast().CollectMetrics(input.context, scan_state); + } + } + lock_guard metrics_guard(scan_state.metrics_lock); + // the row groups scanned are handed over once - the profiler sums what every thread reports + input.operator_metrics.row_groups_scanned += scan_state.collected_scanned; + scan_state.collected_scanned = 0; + // the size of the scan is reported as-is - it is not summed across the threads that report it + input.operator_metrics.total_row_groups_to_scan = scan_state.collected_total; +} + +vector TableFunctionMultiFileWrapper::GetPartitionStats(ClientContext &context, + GetPartitionStatsInput &input) { + vector result; + auto &bind_data = input.bind_data->Cast(); + if (bind_data.file_list->GetExpandResult() != FileExpandResult::SINGLE_FILE || !bind_data.initial_reader) { + return result; + } + auto &reader = bind_data.initial_reader->Cast(); + auto &function = reader.GetFunction(); + if (!function.get_partition_stats || !reader.bind_data) { + return result; + } + BoundTableFunction bound_function(function); + GetPartitionStatsInput file_input(bound_function, reader.bind_data.get()); + return function.get_partition_stats(context, file_input); } TableFunction TableFunctionMultiFileWrapper::CreateFunction(TableFunction single_file_function, Identifier name, - TableFunctionMultiFileSettings settings) { - if (single_file_function.GetArguments().size() != 1 || - single_file_function.GetArguments()[0] != LogicalType::VARCHAR) { - throw InternalException("Only table functions taking a single VARCHAR file path can be wrapped in a multi " - "file function, %s does not", + TableFunctionMultiFileSettings settings, + table_function_bind_t bind) { + auto &wrapped_signature = single_file_function.GetSignature(); + if (wrapped_signature.GetPositionalParameterCount() != 1 || + (wrapped_signature.GetParameter(0).GetType() != LogicalType::VARCHAR && + wrapped_signature.GetParameter(0).GetType().id() != LogicalTypeId::ANY)) { + throw InternalException("Only table functions taking a single file - an ANY file or a VARCHAR file path - can " + "be wrapped in a multi file function, %s does not", single_file_function.name); } if (settings.reader_type.empty()) { settings.reader_type = name.GetIdentifierName(); } TableFunctionMultiFileFunction result(std::move(name)); - result.bind = TableFunctionMultiFileBind; - // forward the named parameters and the pushdown capabilities of the wrapped function - for (auto &named_parameter : single_file_function.named_parameters) { - result.named_parameters[named_parameter.first] = named_parameter.second; + result.bind = bind ? bind : TableFunctionMultiFileBind; + // forward the options and the pushdown capabilities of the wrapped function + auto &signature = result.GetSignature(); + auto wrapped_options = single_file_function.GetSignature().GetTypedKwargs(); + if (wrapped_options) { + signature.ExtendTypedKwargs([&](TypedKwargs &options) { options = options.Merge(*wrapped_options); }); } result.projection_pushdown = single_file_function.projection_pushdown; result.filter_pushdown = single_file_function.filter_pushdown; result.filter_prune = single_file_function.filter_prune; result.supports_pushdown_type = single_file_function.supports_pushdown_type; - result.function_info = - make_shared_ptr(std::move(single_file_function), std::move(settings)); + result.late_materialization = single_file_function.late_materialization; + if (single_file_function.get_partition_stats) { + // the row groups of the scan are those of its files + result.get_partition_stats = GetPartitionStats; + } + if (single_file_function.get_metrics) { + // the metrics of the scan include those the wrapped function keeps per file + result.get_metrics = TableFunctionMultiFileGetMetrics; + } + if (single_file_function.statistics) { + // the statistics of the scan are those of its files, combined + result.statistics_extended = TableFunctionMultiFileFunction::MultiFileScanStatsExtended; + } + // NOTE: callbacks that take bind data are NOT forwarded - through the wrapper they are handed the bind data of + // the multi-file scan, not the bind data the wrapped function produced for a file. A format that wants one sets + // it on the function this returns + if (!bind) { + // the bind below reads the wrapped function from here - a function that brought its own bind does not need + // this, and leaves the slot to whoever binds it + result.function_info = + make_shared_ptr(std::move(single_file_function), std::move(settings)); + } return std::move(result); } TableFunctionSet TableFunctionMultiFileWrapper::CreateFunctionSet(TableFunction single_file_function, Identifier name, - TableFunctionMultiFileSettings settings) { + TableFunctionMultiFileSettings settings, + table_function_bind_t bind) { return MultiFileReader::CreateFunctionSet( - CreateFunction(std::move(single_file_function), std::move(name), std::move(settings))); + CreateFunction(std::move(single_file_function), std::move(name), std::move(settings), bind)); } } // namespace duckdb diff --git a/src/duckdb/src/common/multi_file/union_by_name.cpp b/src/duckdb/src/common/multi_file/union_by_name.cpp index 82ecaa1cc..5226970fb 100644 --- a/src/duckdb/src/common/multi_file/union_by_name.cpp +++ b/src/duckdb/src/common/multi_file/union_by_name.cpp @@ -54,7 +54,7 @@ vector> UnionByName::UnionCols(ClientContext &context, executor.WorkOnTasks(); // now combine the result schemas - interface.CombineSchemas(context, union_readers, union_col_types, union_col_names); + interface.CombineSchemas(context, union_readers, file_options.union_by_name, union_col_types, union_col_names); return union_readers; } diff --git a/src/duckdb/src/common/open_file_info.cpp b/src/duckdb/src/common/open_file_info.cpp index 90814fc3e..d17aa1c64 100644 --- a/src/duckdb/src/common/open_file_info.cpp +++ b/src/duckdb/src/common/open_file_info.cpp @@ -1,55 +1,77 @@ #include "duckdb/common/open_file_info.hpp" #include "duckdb/common/exception.hpp" -#include "duckdb/common/string_util.hpp" +#include "duckdb/common/exception/parser_exception.hpp" -namespace duckdb { - -namespace { +#include -struct OpenFileOption { - const char *name; - LogicalTypeId type; -}; - -//! The open options the core knows about, together with the type they are read back as. Options that are not -//! listed here are stored as-is - extensions are free to define options of their own. -const OpenFileOption OPEN_FILE_OPTIONS[] = {{"file_size", LogicalTypeId::UBIGINT}, - {"last_modified", LogicalTypeId::TIMESTAMP}, - {"etag", LogicalTypeId::VARCHAR}, - {"type", LogicalTypeId::VARCHAR}, - {"force_full_download", LogicalTypeId::BOOLEAN}, - {"validate_external_file_cache", LogicalTypeId::BOOLEAN}, - {"footer_size", LogicalTypeId::UBIGINT}}; +namespace duckdb { -optional_ptr FindOpenFileOption(const string &name) { - for (auto &option : OPEN_FILE_OPTIONS) { - if (StringUtil::CIEquals(name, option.name)) { - return &option; +OpenFileInfo OpenFileInfo::FromValue(const Value &input, const Identifier &function_name) { + if (input.IsNull()) { + throw ParserException("%s reader cannot take NULL input as parameter", function_name); + } + if (input.type().id() == LogicalTypeId::VARCHAR) { + return OpenFileInfo(StringValue::Get(input)); + } + if (input.type().id() == LogicalTypeId::VARIANT) { + // a VARIANT lets every file carry its own set of open options - unpack it to its logical value + // a variant never unpacks to another variant, so this recurses at most once + return FromValue(VariantValue::GetValue(input), function_name); + } + if (input.type().id() != LogicalTypeId::STRUCT) { + throw ParserException("%s reader can only take a list of strings, structs or variants as a parameter", + function_name); + } + // a file specified as a struct holds the path in the "filename" field - every other field is an open option + auto &child_types = StructType::GetChildTypes(input.type()); + auto &children = StructValue::GetChildren(input); + OpenFileInfo result; + auto extended_info = make_shared_ptr(); + bool found_path = false; + for (idx_t child_idx = 0; child_idx < children.size(); child_idx++) { + auto &name = child_types[child_idx].first; + auto &child = children[child_idx]; + if (name == PATH_FIELD) { + if (child.IsNull() || child.type().id() != LogicalTypeId::VARCHAR) { + throw ParserException("%s reader requires the \"%s\" field of a file struct to be a non-NULL VARCHAR", + function_name, PATH_FIELD); + } + result.path = StringValue::Get(child); + found_path = true; + continue; } + if (child.IsNull()) { + // a NULL option is an option that was not specified - a list of structs is typed by unifying the + // structs of its entries, which fills the options an entry did not specify with NULL + continue; + } + extended_info->options[name.GetIdentifierName()] = child; + } + if (!found_path) { + throw ParserException("%s reader requires a file struct to have a \"%s\" field holding the path of the file", + function_name, PATH_FIELD); } - return nullptr; + result.extended_info = std::move(extended_info); + return result; } -} // namespace - -void ExtendedOpenFileInfo::SetUserOption(const string &name, const Value &value) { - auto known_option = FindOpenFileOption(name); - if (!known_option) { - // not an option the core knows about - store it as-is for extensions to interpret - options[name] = value; - return; - } - LogicalType target_type(known_option->type); - string error_message; - auto casted_value = value.DefaultTryCastAs(target_type, &error_message); - if (!casted_value) { - throw InvalidInputException("Invalid value for file option \"%s\" - expected a value of type %s, but \"%s\" " - "was provided", - known_option->name, target_type.ToString(), value.ToString()); - } - // store the option under its canonical name, so a lookup of the option always finds it - options[known_option->name] = std::move(*casted_value); +Value OpenFileInfo::ToValue() const { + if (!extended_info || extended_info->options.empty()) { + return Value(path); + } + // the options are kept unordered - sort them, so the same file always becomes the same value + vector names; + for (auto &option : extended_info->options) { + names.push_back(option.first); + } + std::sort(names.begin(), names.end()); + child_list_t children; + children.emplace_back(PATH_FIELD, Value(path)); + for (auto &name : names) { + children.emplace_back(name, extended_info->options.at(name)); + } + return Value::STRUCT(std::move(children)); } template <> @@ -77,14 +99,17 @@ bool ExtendedOpenFileInfo::TryGetOption(const string &name, string &result) cons return false; } auto &value = entry->second; - // a string option can be stored as a VARCHAR or as a BLOB - both are read back with StringValue::Get, - // and casting a BLOB to VARCHAR here would escape the bytes it holds - if (value.IsNull() || value.type().InternalType() != PhysicalType::VARCHAR) { + if (value.IsNull()) { throw InvalidInputException( "Invalid value for file option \"%s\" - expected a VARCHAR, but \"%s\" was provided", name, value.ToString()); } - result = StringValue::Get(value); + // VARCHAR and BLOB are read back as-is - casting a BLOB to VARCHAR would escape the bytes it holds + if (value.type().InternalType() == PhysicalType::VARCHAR) { + result = StringValue::Get(value); + return true; + } + result = value.ToString(); return true; } @@ -106,4 +131,22 @@ bool ExtendedOpenFileInfo::TryGetOption(const string &name, idx_t &result) const return true; } +template <> +bool ExtendedOpenFileInfo::TryGetOption(const string &name, timestamp_t &result) const { + auto entry = options.find(name); + if (entry == options.end()) { + return false; + } + auto &value = entry->second; + string error_message; + auto timestamp_value = value.DefaultTryCastAs(LogicalType::TIMESTAMP, &error_message); + if (!timestamp_value || timestamp_value->IsNull()) { + throw InvalidInputException( + "Invalid value for file option \"%s\" - expected a TIMESTAMP, but \"%s\" was provided", name, + value.ToString()); + } + result = TimestampValue::Get(*timestamp_value); + return true; +} + } // namespace duckdb diff --git a/src/duckdb/src/common/opener_file_system.cpp b/src/duckdb/src/common/opener_file_system.cpp index 2b8e0e51c..617947828 100644 --- a/src/duckdb/src/common/opener_file_system.cpp +++ b/src/duckdb/src/common/opener_file_system.cpp @@ -23,6 +23,13 @@ unique_ptr OpenerFileSystem::MemoryMapFile(const OpenFileInfo return GetFileSystem().MemoryMapFile(path, flags, options, GetOpener()); } +unique_ptr OpenerFileSystem::GlobFilesExtended(const string &path, const FileGlobInput &input, + optional_ptr opener) { + VerifyNoOpener(opener); + VerifyCanAccessFile(path); + return GetFileSystem().Glob(path, input, GetOpener()); +} + void OpenerFileSystem::VerifyNoOpener(optional_ptr opener) { if (opener) { throw InternalException("OpenerFileSystem cannot take an opener - the opener is pushed automatically"); @@ -38,7 +45,7 @@ void OpenerFileSystem::VerifyCanAccessFileInternal(const string &path, FileType return; } auto &config = db->config; - if (!config.CanAccessFile(path, type)) { + if (!config.CanAccessFile(path, type, opener)) { throw PermissionException("Cannot access %s \"%s\" - file system operations are disabled by configuration", type == FileType::FILE_TYPE_DIR ? "directory" : "file", path); } diff --git a/src/duckdb/src/common/operator/cast_operators.cpp b/src/duckdb/src/common/operator/cast_operators.cpp index 464b7d83f..95d7828e9 100644 --- a/src/duckdb/src/common/operator/cast_operators.cpp +++ b/src/duckdb/src/common/operator/cast_operators.cpp @@ -1134,6 +1134,14 @@ static bool TryCastTimestampBase(const SRC &input, DST &result, bool strict = tr ++result.value; } result.value /= 2; + // rounding can go past the largest (or smallest) timestamp - the result must still be a valid timestamp + if (DST::PRECISION < Interval::MICROS_PER_SEC) { + int64_t micros; + if (!TryMultiplyOperator::Operation(result.value, Interval::MICROS_PER_SEC / DST::PRECISION, micros) || + !timestamp_t(micros).IsFinite()) { + return false; + } + } return true; } } @@ -1375,6 +1383,16 @@ bool TryCast::Operation(timestamp_tz_ns_t input, timestamp_ns_t &result, bool st return TryCastTimestampBase(input, result, strict); } +template <> +bool TryCast::Operation(timestamp_tz_ns_t input, timestamp_ms_t &result, bool strict) { + return TryCastTimestampBase(input, result, strict); +} + +template <> +bool TryCast::Operation(timestamp_tz_ns_t input, timestamp_sec_t &result, bool strict) { + return TryCastTimestampBase(input, result, strict); +} + template <> bool TryCast::Operation(timestamp_t input, timestamp_tz_t &result, bool strict) { return TryCastTimestampBase(input, result, strict); @@ -1608,12 +1626,15 @@ string_t CastFromBlob::Operation(string_t input, StringHeap &heap) { } template <> -string_t CastFromBlobToBit::Operation(string_t input, StringHeap &heap) { - idx_t result_size = input.GetSize() + 1; - if (result_size <= 1) { - throw ConversionException("Cannot cast empty BLOB to BIT"); +bool TryCastBlobToBit::Operation(string_t input, string_t &result, Vector &result_vector, CastParameters ¶meters) { + if (input.GetSize() == 0) { + HandleCastError::AssignError("Cannot cast empty BLOB to BIT", parameters); + return false; } - return heap.AddBlob(Bit::BlobToBit(input)); + result = StringVector::EmptyString(result_vector, input.GetSize() + 1); + Bit::BlobToBit(input, result); + result.Finalize(); + return true; } //===--------------------------------------------------------------------===// @@ -1835,9 +1856,20 @@ hugeint_t CastFromUHugeintToUUID::Operation(uhugeint_t input) { //===--------------------------------------------------------------------===// template <> bool TryCastToGeometry::Operation(string_t input, string_t &result, Vector &result_vector, CastParameters ¶meters) { - // Pass the query location of the cast source if available. - return Geometry::FromString(input, result, StringVector::GetStringHeap(result_vector), parameters.strict, - parameters.cast_source ? parameters.cast_source->GetQueryLocation() : QueryLocation()); + auto &heap = StringVector::GetStringHeap(result_vector); + if (!parameters.error_message) { + // Pass the query location of the cast source if available. + return Geometry::FromString(input, result, heap, parameters.strict, + parameters.cast_source ? parameters.cast_source->GetQueryLocation() + : QueryLocation()); + } + // the caller collects errors (e.g. TRY_CAST) - report malformed WKT as a cast error + string error_message; + if (Geometry::TryFromString(input, result, heap, error_message)) { + return true; + } + HandleCastError::AssignError(error_message, parameters); + return false; } //===--------------------------------------------------------------------===// @@ -1960,7 +1992,11 @@ bool TryCast::Operation(string_t input, dtime_ns_t &result, bool strict) { if (!TryCast::Operation(micros, result)) { return false; } - return TryAddOperator::Operation(result.value, nanos, result.value); + if (!TryAddOperator::Operation(result.value, nanos, result.value)) { + return false; + } + // the nanoseconds can push e.g. 24:00:00 past the end of the day + return result.value <= Interval::NANOS_PER_DAY; } template <> @@ -2148,12 +2184,12 @@ struct HugeIntCastData { using Operation = OP; ResultType result; IntermediateType intermediate; - uint8_t digits; + idx_t digits; ResultType decimal; - uint16_t decimal_total_digits; + idx_t decimal_total_digits; ResultType decimal_intermediate; - uint16_t decimal_intermediate_digits; + idx_t decimal_intermediate_digits; bool Flush() { if (digits == 0 && intermediate == 0) { @@ -2283,7 +2319,7 @@ struct HugeIntegerCastOperation { remainder = negate_result; } state.decimal = remainder; - state.decimal_total_digits = static_cast(-e); + state.decimal_total_digits = static_cast(-e); state.decimal_intermediate = 0; state.decimal_intermediate_digits = 0; return Finalize(state); @@ -2302,16 +2338,19 @@ struct HugeIntegerCastOperation { return Finalize(state); } - e = exponent - state.decimal_total_digits; - if (e < 0) { - if (e < -38) { - return false; + const auto shift = static_cast(exponent) - static_cast(state.decimal_total_digits); + if (shift < 0) { + if (shift < -38) { + // all of decimal is fractional + remainder = state.decimal; + state.decimal = 0; + } else { + state.decimal = T::Operation::DivMod(state.decimal, T::Operation::POWERS_OF_TEN[-shift], remainder); } - state.decimal = T::Operation::DivMod(state.decimal, T::Operation::POWERS_OF_TEN[-e], remainder); - state.decimal_total_digits -= (exponent); + state.decimal_total_digits -= static_cast(exponent); } else { - if (e > 38 || - !TryMultiplyOperator::Operation(state.decimal, T::Operation::POWERS_OF_TEN[e], state.decimal)) { + if (shift > 38 || + !TryMultiplyOperator::Operation(state.decimal, T::Operation::POWERS_OF_TEN[shift], state.decimal)) { return false; } } @@ -2332,6 +2371,11 @@ struct HugeIntegerCastOperation { if (!state.Flush()) { return false; } + // digits past this position cannot affect the result for any int16_t exponent. Ignore them! + static constexpr uint16_t MAX_DECIMAL_DIGITS = -NumericLimits::Minimum(); + if (DUCKDB_UNLIKELY(state.decimal_total_digits + state.decimal_intermediate_digits == MAX_DECIMAL_DIGITS)) { + return true; + } if (DUCKDB_UNLIKELY(state.decimal_intermediate > (NumericLimits::Maximum() - digit) / 10)) { if (!state.FlushDecimal()) { return false; @@ -2359,7 +2403,7 @@ struct HugeIntegerCastOperation { state.decimal /= T::Operation::POWERS_OF_TEN[MAX_DIGITS]; state.decimal_total_digits -= MAX_DIGITS; } - D_ASSERT((state.decimal_total_digits - 1) >= 0 && (state.decimal_total_digits - 1) <= MAX_DIGITS); + D_ASSERT(state.decimal_total_digits >= 1 && state.decimal_total_digits - 1 <= MAX_DIGITS); state.decimal /= T::Operation::POWERS_OF_TEN[state.decimal_total_digits - 1]; if (state.decimal >= 5) { @@ -3279,6 +3323,7 @@ static void FillDecimalDigits(SRC input, duckdb_fast_float::decimal &decimal, bo } static void FillDecimalDigits(hugeint_t input, duckdb_fast_float::decimal &decimal, bool &negative) { + negative = false; if (input == 0) { return; } @@ -3286,11 +3331,10 @@ static void FillDecimalDigits(hugeint_t input, duckdb_fast_float::decimal &decim if (input < 0) { negative = true; Hugeint::NegateInPlace(input); - } else { - negative = false; } - char buffer[DecimalWidth::max]; + // a hugeint can have one more digit than the maximum decimal width + char buffer[DecimalWidth::max + 1]; auto end = buffer + sizeof(buffer); auto begin = NumericHelper::FormatUnsigned(input, end); @@ -3309,7 +3353,7 @@ bool TryCastDecimalToFloatingPoint(SRC input, DST &result, uint8_t width, uint8_ } duckdb_fast_float::decimal decimal; - bool negative; + bool negative = false; FillDecimalDigits(input, decimal, negative); decimal.decimal_point = UnsafeNumericCast(decimal.num_digits) - UnsafeNumericCast(scale); while (decimal.num_digits > 0 && decimal.digits[decimal.num_digits - 1] == 0) { diff --git a/src/duckdb/src/common/optional_idx.cpp b/src/duckdb/src/common/optional_idx.cpp index e8efd4120..ea701d7de 100644 --- a/src/duckdb/src/common/optional_idx.cpp +++ b/src/duckdb/src/common/optional_idx.cpp @@ -2,10 +2,6 @@ namespace duckdb { -void optional_idx::ThrowInvalidInitialization() { - throw InternalException("optional_idx cannot be initialized with an invalid index"); -} - void optional_idx::ThrowNotSet() { throw InternalException("Attempting to get the index of an optional_idx that is not set"); } diff --git a/src/duckdb/src/common/progress_bar/unscented_kalman_filter.cpp b/src/duckdb/src/common/progress_bar/unscented_kalman_filter.cpp deleted file mode 100644 index 76e52c194..000000000 --- a/src/duckdb/src/common/progress_bar/unscented_kalman_filter.cpp +++ /dev/null @@ -1,287 +0,0 @@ -#include "duckdb/common/progress_bar/unscented_kalman_filter.hpp" -#include "duckdb.hpp" -namespace duckdb { - -UnscentedKalmanFilter::UnscentedKalmanFilter() - : x(STATE_DIM, 0.0), P(STATE_DIM, vector(STATE_DIM, 0.0)), Q(STATE_DIM, vector(STATE_DIM, 0.0)), - R(OBS_DIM, vector(OBS_DIM, 0.0)), last_time(0.0), initialized(false), last_progress(-1.0), - scale_factor(1.0) { - // Calculate UKF parameters - lambda = ALPHA * ALPHA * (STATE_DIM + KAPPA) - STATE_DIM; - - // Initialize weights - wm.resize(SIGMA_POINTS); - wc.resize(SIGMA_POINTS); - - wm[0] = lambda / (STATE_DIM + lambda); - wc[0] = lambda / (STATE_DIM + lambda) + (1 - ALPHA * ALPHA + BETA); - - for (size_t i = 1; i < SIGMA_POINTS; i++) { - wm[i] = wc[i] = 0.5 / (STATE_DIM + lambda); - } - - // Initialize covariance matrices - P[0][0] = 0.165; // progress variance - P[1][1] = 0.0098; // velocity variance - - Q[0][0] = 0.01; // process noise for progress - Q[1][1] = 0.0; // process noise for velocity - - R[0][0] = 0.000077; // measurement noise for progress -} - -void UnscentedKalmanFilter::Update(double progress, double time) { - progress *= scale_factor; - if (!initialized) { - Initialize(progress, time); - return; - } - Predict(time); - if (last_progress != progress) { - UpdateInternal(progress); - last_progress = progress; - } -} - -void UnscentedKalmanFilter::Initialize(double initial_progress, double current_time) { - // If the initial progress is zero we can't yet initialize, since the filter - // wont' have a reasonable guess about query velocity, just wait until some - // progress has been made. - if (initial_progress == 0.0 || current_time == 0.0) { - return; - } - // If the initial progress value is very small, it needs to be scaled up so - // its the same relative magnitude of updates that was used to determine - // the P, Q, and R parameters of the Kalman filter. - // - // The settings for the Kalman filter make assumptions around the magnitude of - // measurement noise. If the progress updates are very small, the updates - // could be considered noise by the Kalman filter. - scale_factor = std::max(1.0, 0.1 / initial_progress); - initial_progress *= scale_factor; - x[0] = initial_progress; - x[1] = initial_progress / current_time; // initial velocity guess - last_time = current_time; - last_progress = initial_progress; - initialized = true; -} - -// Matrix operations -vector> UnscentedKalmanFilter::MatrixSqrt(const vector> &mat) { - size_t n = mat.size(); - vector> L(n, vector(n, 0.0)); - - // Cholesky decomposition - for (size_t i = 0; i < n; i++) { - for (size_t j = 0; j <= i; j++) { - if (i == j) { - double sum = 0; - for (size_t k = 0; k < j; k++) { - sum += L[j][k] * L[j][k]; - } - L[j][j] = std::sqrt(mat[j][j] - sum); - } else { - double sum = 0; - for (size_t k = 0; k < j; k++) { - sum += L[i][k] * L[j][k]; - } - L[i][j] = (mat[i][j] - sum) / L[j][j]; - } - } - } - return L; -} - -// Generate sigma points -vector> UnscentedKalmanFilter::GenerateSigmaPoints() { - vector> sigma_points(SIGMA_POINTS, vector(STATE_DIM)); - - // Calculate sqrt((n + lambda) * P) - auto scaled_P = P; - double scale = STATE_DIM + lambda; - for (size_t i = 0; i < STATE_DIM; i++) { - for (size_t j = 0; j < STATE_DIM; j++) { - scaled_P[i][j] *= scale; - } - } - - auto sqrt_P = MatrixSqrt(scaled_P); - - // First sigma point is the mean - sigma_points[0] = x; - - // Remaining sigma points - for (size_t i = 0; i < STATE_DIM; i++) { - for (size_t j = 0; j < STATE_DIM; j++) { - sigma_points[i + 1][j] = x[j] + sqrt_P[j][i]; - sigma_points[i + 1 + STATE_DIM][j] = x[j] - sqrt_P[j][i]; - } - } - - return sigma_points; -} - -// State transition function -vector UnscentedKalmanFilter::StateTransition(const vector &state, double dt) { - vector new_state(STATE_DIM); - new_state[0] = state[0] + state[1] * dt; // progress += velocity * dt - new_state[1] = state[1]; // velocity remains constant - - // Clamp progress to [0, 1] - new_state[0] = std::max(0.0, std::min(1.0, new_state[0])); - - return new_state; -} - -// Measurement function (identity for progress) -vector UnscentedKalmanFilter::MeasurementFunction(const vector &state) { - return {state[0]}; -} - -void UnscentedKalmanFilter::Predict(double current_time) { - D_ASSERT(initialized); - - double dt = current_time - last_time; - last_time = current_time; - - if (dt <= 0) { - return; - } - - // Generate sigma points - auto sigma_points = GenerateSigmaPoints(); - - // Propagate sigma points through state transition - vector> sigma_points_pred(SIGMA_POINTS, vector(STATE_DIM)); - for (size_t i = 0; i < SIGMA_POINTS; i++) { - sigma_points_pred[i] = StateTransition(sigma_points[i], dt); - } - - // Predict state mean - std::fill(x.begin(), x.end(), 0.0); - for (size_t i = 0; i < SIGMA_POINTS; i++) { - for (size_t j = 0; j < STATE_DIM; j++) { - x[j] += wm[i] * sigma_points_pred[i][j]; - } - } - - // Predict covariance - for (size_t i = 0; i < STATE_DIM; i++) { - std::fill(P[i].begin(), P[i].end(), 0.0); - } - - for (size_t i = 0; i < SIGMA_POINTS; i++) { - for (size_t j = 0; j < STATE_DIM; j++) { - for (size_t k = 0; k < STATE_DIM; k++) { - P[j][k] += wc[i] * (sigma_points_pred[i][j] - x[j]) * (sigma_points_pred[i][k] - x[k]); - } - } - } - - // Add process noise - for (size_t i = 0; i < STATE_DIM; i++) { - for (size_t j = 0; j < STATE_DIM; j++) { - P[i][j] += Q[i][j]; - } - } -} - -void UnscentedKalmanFilter::UpdateInternal(double measured_progress) { - D_ASSERT(initialized); - // Generate sigma points - auto sigma_points = GenerateSigmaPoints(); - - // Transform sigma points through measurement function - vector> Z_sigma(SIGMA_POINTS, vector(OBS_DIM)); - for (size_t i = 0; i < SIGMA_POINTS; i++) { - Z_sigma[i] = MeasurementFunction(sigma_points[i]); - } - - // Predict measurement mean - vector z_pred(OBS_DIM, 0.0); - for (size_t i = 0; i < SIGMA_POINTS; i++) { - for (size_t j = 0; j < OBS_DIM; j++) { - z_pred[j] += wm[i] * Z_sigma[i][j]; - } - } - - // Innovation covariance - vector> S(OBS_DIM, vector(OBS_DIM, 0.0)); - for (size_t i = 0; i < SIGMA_POINTS; i++) { - for (size_t j = 0; j < OBS_DIM; j++) { - for (size_t k = 0; k < OBS_DIM; k++) { - S[j][k] += wc[i] * (Z_sigma[i][j] - z_pred[j]) * (Z_sigma[i][k] - z_pred[k]); - } - } - } - - // Add measurement noise - for (size_t i = 0; i < OBS_DIM; i++) { - for (size_t j = 0; j < OBS_DIM; j++) { - S[i][j] += R[i][j]; - } - } - - // Cross correlation - vector> T(STATE_DIM, vector(OBS_DIM, 0.0)); - for (size_t i = 0; i < SIGMA_POINTS; i++) { - for (size_t j = 0; j < STATE_DIM; j++) { - for (size_t k = 0; k < OBS_DIM; k++) { - T[j][k] += wc[i] * (sigma_points[i][j] - x[j]) * (Z_sigma[i][k] - z_pred[k]); - } - } - } - - // Kalman gain (simplified for 1D measurement) - vector> K(STATE_DIM, vector(OBS_DIM)); - double S_inv = 1.0 / S[0][0]; - for (size_t i = 0; i < STATE_DIM; i++) { - K[i][0] = T[i][0] * S_inv; - } - - // Update state - vector innovation = {measured_progress - z_pred[0]}; - for (size_t i = 0; i < STATE_DIM; i++) { - x[i] += K[i][0] * innovation[0]; - } - - // Update covariance - for (size_t i = 0; i < STATE_DIM; i++) { - for (size_t j = 0; j < STATE_DIM; j++) { - P[i][j] -= K[i][0] * S[0][0] * K[j][0]; - } - } - - // Ensure progress stays in bounds - x[0] = std::max(0.0, std::min(scale_factor, x[0])); -} - -double UnscentedKalmanFilter::GetProgress() const { - return x[0] / scale_factor; -} - -double UnscentedKalmanFilter::GetVelocity() const { - return x[1]; -} - -double UnscentedKalmanFilter::GetEstimatedRemainingSeconds() const { - if (!initialized) { - return 2147483647.0; - } - if (x[1] <= 0) { - // velocity is negative or zero - we estimate this will never finish - return NumericLimits::Maximum(); - } - double remaining_progress = (1.0 * scale_factor) - x[0]; - return std::max(remaining_progress / x[1], 0.0); -} - -double UnscentedKalmanFilter::GetProgressVariance() const { - return P[0][0]; -} - -double UnscentedKalmanFilter::GetVelocityVariance() const { - return P[1][1]; -} - -} // namespace duckdb diff --git a/src/duckdb/src/common/serializer/async_memory_governor.cpp b/src/duckdb/src/common/serializer/async_memory_governor.cpp index 601cb4faf..dc3f320d1 100644 --- a/src/duckdb/src/common/serializer/async_memory_governor.cpp +++ b/src/duckdb/src/common/serializer/async_memory_governor.cpp @@ -3,6 +3,7 @@ #include "duckdb/common/helper.hpp" #include "duckdb/main/client_context.hpp" #include "duckdb/parallel/task_scheduler.hpp" +#include "duckdb/storage/buffer_manager.hpp" #include "duckdb/storage/temporary_memory_manager.hpp" namespace duckdb { @@ -12,9 +13,12 @@ ManagedAsyncMemoryGovernor::ManagedAsyncMemoryGovernor(ClientContext &client_con auto &scheduler = TaskScheduler::GetScheduler(client_context); auto regular_threads = MaxValue(NumericCast(scheduler.NumberOfThreads()), 1); auto async_threads = NumericCast(scheduler.NumberOfAsyncThreads()); - max_pending_bytes = ManagedAsyncMemoryConfig::MAX_PENDING_BYTES_PER_THREAD * regular_threads; + auto max_memory = BufferManager::GetBufferManager(client_context).GetMaxMemory(); + max_pending_bytes = MinValue(ManagedAsyncMemoryConfig::MAX_PENDING_BYTES_PER_THREAD * regular_threads, + max_memory / ManagedAsyncMemoryConfig::MAX_PENDING_MEMORY_LIMIT_DIVISOR); min_pending_bytes = - MinValue(max_pending_bytes, ManagedAsyncMemoryConfig::MIN_PENDING_BYTES_PER_THREAD * regular_threads); + MinValue(max_pending_bytes, MinValue(ManagedAsyncMemoryConfig::MIN_PENDING_BYTES_PER_THREAD * regular_threads, + max_memory / ManagedAsyncMemoryConfig::MIN_PENDING_MEMORY_LIMIT_DIVISOR)); // A reservation is only useful when drain tasks run asynchronously; synchronous draining bounds itself. if (async_threads > 0 && max_pending_bytes > 0) { memory_state = TemporaryMemoryManager::Get(client_context).Register(client_context); diff --git a/src/duckdb/src/common/serializer/async_task_queue.cpp b/src/duckdb/src/common/serializer/async_task_queue.cpp index a41563fd5..80d7d1e7e 100644 --- a/src/duckdb/src/common/serializer/async_task_queue.cpp +++ b/src/duckdb/src/common/serializer/async_task_queue.cpp @@ -114,14 +114,7 @@ void AsyncTaskQueue::Submit(AsyncTaskRequest request) { } auto request_size = request.Size(); if (executor && executor->HasError()) { - ErrorData error; - try { - executor->ThrowError(); - } catch (const std::exception &ex) { - error = ErrorData(ex); - } catch (...) { // LCOV_EXCL_START - error = ErrorData("Unknown exception during async task"); - } // LCOV_EXCL_STOP + auto error = executor->GetError(); request.task.reset(); CompleteRequest(request, request_size, error); error.Throw(); @@ -299,15 +292,10 @@ void AsyncTaskQueue::Flush() { return; } - try { + { + // join before leaving this scope, whether the scheduling succeeds or throws + TaskExecutor::JoinGuard join(*executor); ScheduleTasksInternal(); - executor->WorkOnTasks(); - } catch (...) { - try { - executor->WorkOnTasks(); - } catch (...) { - } - throw; } RethrowTaskError(); } diff --git a/src/duckdb/src/common/serializer/async_write_queue.cpp b/src/duckdb/src/common/serializer/async_write_queue.cpp index f0fa69098..363b372f4 100644 --- a/src/duckdb/src/common/serializer/async_write_queue.cpp +++ b/src/duckdb/src/common/serializer/async_write_queue.cpp @@ -73,20 +73,16 @@ class AsyncWriteQueueTask : public BaseExecutorTask { AsyncWriteQueueTask(AsyncWriteQueue &queue_p, TaskExecutor &executor) : BaseExecutorTask(executor), queue(queue_p) { } - ~AsyncWriteQueueTask() override { - if (!started) { - queue.CancelScheduledTask(); - } - } - void ExecuteTask() override { - started = true; queue.DrainRequests(); } + void Cancel() override { + queue.CancelScheduledTask(); + } + private: AsyncWriteQueue &queue; - bool started = false; }; AsyncWriteQueue::AsyncWriteQueue(ClientContext &client_context_p, AsyncWriteTarget &target_p) @@ -124,14 +120,7 @@ void AsyncWriteQueue::Submit(AsyncWriteRequest request) { } auto request_size = request.Size(); if (executor && executor->HasError()) { - ErrorData error; - try { - executor->ThrowError(); - } catch (const std::exception &ex) { - error = ErrorData(ex); - } catch (...) { // LCOV_EXCL_START - error = ErrorData("Unknown exception during async write"); - } // LCOV_EXCL_STOP + auto error = executor->GetError(); request.payload.reset(); CompleteRequest(request, request_size, error); error.Throw(); @@ -211,7 +200,9 @@ void AsyncWriteQueue::ScheduleTasksInternal(bool force) { auto selected_bytes = SelectPendingRequestBytes(scheduled_pending_bytes + scheduled_bytes); auto task_budget = TaskByteBudget(); auto has_active_task = active_tasks + schedule_count > 0; - if (!force && has_active_task && selected_bytes < task_budget) { + auto unscheduled_bytes = pending_bytes - scheduled_pending_bytes - scheduled_bytes; + // A small leading request must not block a larger request behind it. + if (!force && has_active_task && unscheduled_bytes < task_budget) { break; } schedule_count++; @@ -316,14 +307,7 @@ void AsyncWriteQueue::DrainRequests() { } } catch (...) { auto error_ptr = std::current_exception(); - ErrorData error; - try { - std::rethrow_exception(error_ptr); - } catch (const std::exception &ex) { - error = ErrorData(ex); - } catch (...) { // LCOV_EXCL_START - error = ErrorData("Unknown exception during async write"); - } // LCOV_EXCL_STOP + auto error = ErrorDataFromExceptionPtr(error_ptr); request_idx++; for (; request_idx < requests.size(); request_idx++) { auto &request = requests[request_idx].request; @@ -419,15 +403,10 @@ void AsyncWriteQueue::Flush() { return; } - try { + { + // join before leaving this scope, whether the scheduling succeeds or throws + TaskExecutor::JoinGuard join(*executor); ScheduleTasksInternal(true); - executor->WorkOnTasks(); - } catch (...) { - try { - executor->WorkOnTasks(); - } catch (...) { - } - throw; } RethrowTaskError(); } @@ -774,7 +753,7 @@ void ManagedAsyncWriteQueue::ApplyBackpressure() { idx_t current_pending_bytes; { lock_guard guard(lock); - D_ASSERT(external_pending_bytes == 0); + // external bytes a stream wrapper has not adopted yet are still pending current_pending_bytes = TotalPendingBytes(); } if (current_pending_bytes <= BackpressureBudget()) { @@ -1341,10 +1320,8 @@ bool ManagedAsyncWriteStreamQueue::TakePendingWriteRequest(AsyncWriteRequest &re } idx_t selected_bytes = 0; + // The queued-byte threshold was checked above; a small prefix may precede a large zero-copy payload. auto end = SelectPhysicalWriteEnd(0, selected_bytes); - if (policy == SchedulePolicy::THRESHOLD && selected_bytes < first_task_schedule_threshold) { - return false; - } auto write_offset = pending_writes.front().offset; unique_ptr payload; diff --git a/src/duckdb/src/common/sort/full_sort.cpp b/src/duckdb/src/common/sort/full_sort.cpp index 7f450a1ca..40339704e 100644 --- a/src/duckdb/src/common/sort/full_sort.cpp +++ b/src/duckdb/src/common/sort/full_sort.cpp @@ -38,7 +38,7 @@ class FullSortGlobalSinkState : public GlobalSinkState { FullSortGlobalSinkState(ClientContext &client, const FullSort &full_sort); // OVER(PARTITION BY...) (hash grouping) - ProgressData GetSinkProgress(ClientContext &context, const ProgressData source_progress) const; + ProgressData GetSinkProgress(ClientContext &context, const ProgressData source_progress); //! System and query state ClientContext &client; @@ -50,6 +50,8 @@ class FullSortGlobalSinkState : public GlobalSinkState { // Threading atomic count; + //! Keeps the sink progress monotonic + MonotonicProgress sink_progress; }; FullSortGlobalSinkState::FullSortGlobalSinkState(ClientContext &client, const FullSort &full_sort) @@ -58,23 +60,10 @@ FullSortGlobalSinkState::FullSortGlobalSinkState(ClientContext &client, const Fu hash_group = make_uniq(client, *full_sort.sort); } -ProgressData FullSortGlobalSinkState::GetSinkProgress(ClientContext &client, const ProgressData source) const { - ProgressData result; - result.done = source.done / 2; - result.total = source.total; - result.invalid = source.invalid; - - // Sort::GetSinkProgress assumes that there is only 1 sort. - // So we just use it to figure out how many rows have been sorted. - const ProgressData zero_progress; +ProgressData FullSortGlobalSinkState::GetSinkProgress(ClientContext &client, const ProgressData source) { lock_guard guard(lock); - const auto &sort = full_sort.sort; - - const auto group_progress = sort->GetSinkProgress(client, *hash_group->sort_global, zero_progress); - result.done += group_progress.done; - result.invalid = result.invalid || group_progress.invalid; - - return result; + const auto sorted_count = hash_group ? full_sort.sort->GetSortedCount(*hash_group->sort_global) : 0; + return Sort::GetSinkProgress(source, sorted_count, count, sink_progress); } SinkFinalizeType FullSort::Finalize(ClientContext &client, OperatorSinkFinalizeInput &finalize) const { diff --git a/src/duckdb/src/common/sort/hashed_sort.cpp b/src/duckdb/src/common/sort/hashed_sort.cpp index e23b01056..8515e1841 100644 --- a/src/duckdb/src/common/sort/hashed_sort.cpp +++ b/src/duckdb/src/common/sort/hashed_sort.cpp @@ -272,27 +272,8 @@ bool HashedSortGlobalSinkState::CanBypassSort(idx_t hash_bin) const { } ProgressData HashedSortGlobalSinkState::GetSinkProgress(ClientContext &client, const ProgressData source) const { - ProgressData result; - result.done = source.done / 2; - result.total = source.total; - result.invalid = source.invalid; - - // Sort::GetSinkProgress assumes that there is only 1 sort. - // So we just use it to figure out how many rows have been sorted. - const ProgressData zero_progress; - lock_guard guard(lock); - const auto &sort = hashed_sort.sort; - for (auto &hash_group : hash_groups) { - if (!hash_group || !hash_group->sort_global) { - continue; - } - - const auto group_progress = sort->GetSinkProgress(client, *hash_group->sort_global, zero_progress); - result.done += group_progress.done; - result.invalid = result.invalid || group_progress.invalid; - } - - return result; + // the hash groups are sorted after the sink has finished + return source; } SinkFinalizeType HashedSort::Finalize(ClientContext &client, OperatorSinkFinalizeInput &finalize) const { @@ -529,19 +510,10 @@ static void PackDirectColumnData(HashedSortGroup &hash_group) { } auto result = std::move(fragments[0]); - ColumnDataAppendState append_state; - result->InitializeAppend(append_state); - DataChunk chunk; - result->InitializeScanChunk(chunk); // Downstream source tasks address hash groups by packed chunk index. for (idx_t fragment_idx = 1; fragment_idx < fragments.size(); fragment_idx++) { - auto &fragment = *fragments[fragment_idx]; - ColumnDataScanState scan_state; - fragment.InitializeScan(scan_state); - while (fragment.Scan(scan_state, chunk)) { - result->Append(append_state, chunk); - } + result->Append(*fragments[fragment_idx]); } D_ASSERT(result->Count() == hash_group.count); diff --git a/src/duckdb/src/common/sort/natural_sort.cpp b/src/duckdb/src/common/sort/natural_sort.cpp index 3dd27c1ff..6f94b0682 100644 --- a/src/duckdb/src/common/sort/natural_sort.cpp +++ b/src/duckdb/src/common/sort/natural_sort.cpp @@ -47,12 +47,8 @@ NaturalSortGlobalSinkState::NaturalSortGlobalSinkState(ClientContext &client, co } ProgressData NaturalSortGlobalSinkState::GetSinkProgress(ClientContext &client, const ProgressData source) const { - ProgressData result; - result.done = source.done / 2; - result.total = source.total; - result.invalid = source.invalid; - - return result; + // there is nothing to sort + return source; } SinkFinalizeType NaturalSort::Finalize(ClientContext &client, OperatorSinkFinalizeInput &finalize) const { diff --git a/src/duckdb/src/common/sort/sort.cpp b/src/duckdb/src/common/sort/sort.cpp index be5b9c7e2..5bb566837 100644 --- a/src/duckdb/src/common/sort/sort.cpp +++ b/src/duckdb/src/common/sort/sort.cpp @@ -2,10 +2,10 @@ #include "duckdb/main/client_context.hpp" #include "duckdb/main/settings.hpp" -#include "duckdb/common/type_visitor.hpp" #include "duckdb/common/sorting/sort_key.hpp" #include "duckdb/common/sorting/sorted_run.hpp" #include "duckdb/common/sorting/sorted_run_merger.hpp" +#include "duckdb/common/type_visitor.hpp" #include "duckdb/common/types/batched_data_collection.hpp" #include "duckdb/common/types/row/tuple_data_collection.hpp" #include "duckdb/function/create_sort_key.hpp" @@ -23,33 +23,19 @@ Sort::Sort(ClientContext &client_context_p, const vector &orde // Convert orders to a single "create_sort_key" expression (and corresponding "decode_sort_key") FunctionBinder binder(client_context); vector> create_children; - vector> decode_children; - child_list_t decode_child_list; + child_list_t decode_columns; + vector decode_modifiers; for (idx_t col_idx = 0; col_idx < orders.size(); col_idx++) { const auto &order = orders[col_idx]; + const auto order_modifier = order.GetOrderModifier(); // Create: for each column we have two arguments: 1. the column, 2. sort specifier create_children.emplace_back(order.expression->Copy()); - create_children.emplace_back(make_uniq(Value(order.GetOrderModifier()))); - - // Avoid having unnamed structs fields (otherwise we get a parser exception while binding) - const auto col_name = StringUtil::Format("c%llu", col_idx); - auto col_type = order.expression->GetReturnType(); - decode_child_list.emplace_back(col_name, col_type); - col_type = TypeVisitor::VisitReplace(col_type, [](const LogicalType &type) { - if (!StructType::IsStruct(type)) { - return type; - } - child_list_t internal_child_list; - for (const auto &child : StructType::GetChildTypes(type)) { - internal_child_list.emplace_back(StringUtil::Format("c%llu", internal_child_list.size()), child.second); - } - return LogicalType::STRUCT(std::move(internal_child_list)); - }); + create_children.emplace_back(make_uniq(Value(order_modifier))); - // Decode: for each column we have two arguments: 1. col name + type, 2. sort specifier - decode_children.emplace_back(make_uniq(Value(col_name + " " + col_type.ToString()))); - decode_children.emplace_back(make_uniq(order.GetOrderModifier())); + // Decode: bind field types directly and use the same modifiers as the encoder + decode_columns.emplace_back(StringUtil::Format("c%llu", col_idx), order.expression->GetReturnType()); + decode_modifiers.push_back(OrderModifiers::Parse(order_modifier)); } ErrorData error; @@ -59,24 +45,10 @@ Sort::Sort(ClientContext &client_context_p, const vector &orde throw InternalException("Unable to bind create_sort_key in Sort::Sort"); } - switch (create_sort_key->GetReturnType().id()) { - case LogicalTypeId::BIGINT: - decode_children.insert(decode_children.begin(), - make_uniq(LogicalType::BIGINT, static_cast(0))); - break; - default: - D_ASSERT(create_sort_key->GetReturnType().id() == LogicalTypeId::BLOB); - decode_children.insert(decode_children.begin(), - make_uniq(LogicalType::BLOB, static_cast(0))); - } - - decode_sort_key = binder.BindScalarFunction(DecodeSortKeyFun::GetFunction(), std::move(decode_children)); - if (!decode_sort_key) { - throw InternalException("Unable to bind decode_sort_key in Sort::Sort"); - } - - // A bit hacky, but this way we make sure that the output does contain the unnamed structs again - decode_sort_key->SetReturnType(LogicalType::STRUCT(std::move(decode_child_list))); + auto sort_key_reference = + make_uniq(create_sort_key->GetReturnType(), static_cast(0)); + decode_sort_key = + DecodeSortKeyFun::Bind(std::move(sort_key_reference), std::move(decode_columns), std::move(decode_modifiers)); // For convenience, we fill the projection map if it is empty if (projection_map.empty()) { @@ -102,14 +74,20 @@ Sort::Sort(ClientContext &client_context_p, const vector &orde vector payload_types; for (idx_t output_col_idx = 0; output_col_idx < projection_map.size(); output_col_idx++) { const auto &input_col_idx = projection_map[output_col_idx]; + const auto &input_col_type = input_types[input_col_idx]; const auto it = input_column_to_key.find(input_col_idx); - if (it != input_column_to_key.end()) { + //! The sort key encoding for FLOAT/DOUBLE is not injective around zero (-0.0 and 0.0 encode + //! identically, since they must compare equal), so re-decoding the key would turn -0.0 into 0.0. + //! Route such columns through the payload instead, where the original bits are preserved. + const bool key_encoding_is_lossy = TypeVisitor::Contains(input_col_type, LogicalTypeId::FLOAT) || + TypeVisitor::Contains(input_col_type, LogicalTypeId::DOUBLE); + if (it != input_column_to_key.end() && !key_encoding_is_lossy) { // Projected column also appears as a key, just reference it output_projection_columns.push_back({false, it->second, output_col_idx}); } else { // Projected column does not appear as a key, add to payload layout output_projection_columns.push_back({true, payload_types.size(), output_col_idx}); - payload_types.push_back(input_types[input_col_idx]); + payload_types.push_back(input_col_type); input_projection_map.push_back(input_col_idx); } } @@ -163,8 +141,8 @@ class SortGlobalSinkState : public GlobalSinkState { explicit SortGlobalSinkState(ClientContext &context) : num_threads(TaskScheduler::GetScheduler(context).NumberOfThreads()), temporary_memory_state(TemporaryMemoryManager::Get(context).Register(context)), sorted_tuples(0), - external(Settings::Get(context)), any_combined(false), total_count(0), - partition_size(0) { + sunk_tuples(0), external(Settings::Get(context)), any_combined(false), + total_count(0), partition_size(0) { } public: @@ -186,7 +164,7 @@ class SortGlobalSinkState : public GlobalSinkState { } // Double until it fits - auto required = num_threads * lstate.sorted_run->SizeInBytes(); + auto required = num_threads * lstate.sorted_run->SizeInBytesForSorting(); if (is_index_sort) { required *= 4; // Index creation is pretty intense, so we are very conservative here } @@ -221,6 +199,10 @@ class SortGlobalSinkState : public GlobalSinkState { vector> sorted_runs; //! Sorted tuple count (for progress) atomic sorted_tuples; + //! Sunk tuple count (for progress) + atomic sunk_tuples; + //! Keeps the sink progress monotonic + MonotonicProgress sink_progress; //! Whether this is an external sort bool external; @@ -243,8 +225,8 @@ unique_ptr Sort::GetGlobalSinkState(ClientContext &context) con //! Returns true if the Sink call is done (either because run size is small or because run was finalized) static bool TryFinishSink(SortGlobalSinkState &gstate, SortLocalSinkState &lstate, unique_lock &guard) { - // Check if we exceed the limit - const auto sorted_run_size = lstate.sorted_run->SizeInBytes(); + // Check if we exceed the limit (sorting the run needs a merge buffer on top of the run itself) + const auto sorted_run_size = lstate.sorted_run->SizeInBytesForSorting(); if (sorted_run_size < lstate.maximum_run_size) { return true; // Sink is done } @@ -280,6 +262,7 @@ SinkResultType Sort::Sink(ExecutionContext &context, DataChunk &chunk, OperatorS lstate.key_executor.Execute(chunk, lstate.key); lstate.payload.ReferenceColumns(chunk, input_projection_map); lstate.sorted_run->Sink(lstate.key, lstate.payload); + gstate.sunk_tuples.fetch_add(chunk.size(), std::memory_order_relaxed); // Try to finish this call to Sink unique_lock guard; @@ -351,13 +334,33 @@ SinkFinalizeType Sort::Finalize(ClientContext &context, OperatorSinkFinalizeInpu ProgressData Sort::GetSinkProgress(ClientContext &context, GlobalSinkState &gstate_p, const ProgressData source_progress) const { auto &gstate = gstate_p.Cast(); - // Estimate that half of the Sink effort is sorting + return GetSinkProgress(source_progress, gstate.sorted_tuples, gstate.sunk_tuples, gstate.sink_progress); +} + +idx_t Sort::GetSortedCount(GlobalSinkState &gstate) const { + return gstate.Cast().sorted_tuples; +} + +ProgressData Sort::GetSinkProgress(const ProgressData &source_progress, idx_t sorted_count, idx_t sunk_count, + MonotonicProgress &monotonic_progress) { ProgressData res; - const auto sorted_tuples = static_cast(gstate.sorted_tuples); - res.done = source_progress.done / 2 + sorted_tuples / 2; res.total = source_progress.total; res.invalid = source_progress.invalid; - return res; + if (source_progress.total <= 0) { + return res; + } + const auto source_fraction = MinValue(source_progress.done / source_progress.total, 1.0); + // the total number of tuples is unknown - relate the sorted tuples to the tuples sunk so far + double sorted_fraction; + if (sunk_count == 0) { + // there is nothing to sort if the source finished without producing any tuples + sorted_fraction = source_fraction >= 1.0 ? 1.0 : 0.0; + } else { + sorted_fraction = MinValue(static_cast(sorted_count) / static_cast(sunk_count), 1.0); + } + res.done = source_progress.total * source_fraction * (1.0 + sorted_fraction) / 2; + // the sorted fraction drops when tuples are sunk faster than they are sorted + return monotonic_progress.Update(res); } //===--------------------------------------------------------------------===// @@ -506,8 +509,7 @@ SourceResultType Sort::MaterializeColumnData(ExecutionContext &context, Operator input.local_state.Cast().merger_local_state.reset(); // Return type indicates whether materialization is done - const auto progress_data = GetProgress(context.client, input.global_state); - if (progress_data.done == progress_data.total) { + if (gstate.merger.total_count == 0 || gstate.merger.IsFinished(*gstate.merger_global_state)) { // Destroy global state before returning gstate.Destroy(); return SourceResultType::FINISHED; diff --git a/src/duckdb/src/common/sort/sorted_run.cpp b/src/duckdb/src/common/sort/sorted_run.cpp index d72cc9328..f7eab31cf 100644 --- a/src/duckdb/src/common/sort/sorted_run.cpp +++ b/src/duckdb/src/common/sort/sorted_run.cpp @@ -8,6 +8,7 @@ #include "duckdb/common/types/row/tuple_data_collection.hpp" #include "duckdb/common/sorting/sort.hpp" #include "duckdb/common/sorting/sort_key.hpp" +#include "duckdb/common/sorting/buffered_merge.hpp" #include "duckdb/common/types/row/block_iterator.hpp" #include "vergesort.h" @@ -247,10 +248,16 @@ static void TemplatedSort(ClientContext &context, TupleDataCollection &key_data, auto ska_extract_key = SkaExtractKey(requires_next_sort, ska_sort_width, sort_skippable_bytes, context.interrupt_state); - const auto fallback = [ska_extract_key](const BLOCK_ITERATOR &fb_begin, const BLOCK_ITERATOR &fb_end) { - duckdb_ska_sort::ska_sort(fb_begin, fb_end, ska_extract_key); + // vergesort merges the sorted runs it finds with a temporary buffer of up to half the keys - allocate that + // buffer through the buffer allocator instead of letting std::inplace_merge take it from operator new. The same + // buffer serves the merges of the nested vergesort that ska_sort runs over the buckets its radix passes cannot + // order, so that no merge anywhere in this sort allocates on its own (BufferedMerge never does) + SortMergeBuffer merge_buffer(BufferAllocator::Get(context), key_data.Count(), sizeof(SORT_KEY)); + const BufferedMergePolicy merge(merge_buffer); + const auto fallback = [&ska_extract_key, &merge](const BLOCK_ITERATOR &fb_begin, const BLOCK_ITERATOR &fb_end) { + duckdb_ska_sort::ska_sort(fb_begin, fb_end, ska_extract_key, merge); }; - duckdb_vergesort::vergesort(begin, end, std::less(), fallback); + duckdb_vergesort::vergesort(begin, end, std::less(), fallback, merge); context.InterruptCheck(); } @@ -447,4 +454,9 @@ idx_t SortedRun::SizeInBytes() const { return size; } +idx_t SortedRun::SizeInBytesForSorting() const { + // the fixed-size sort keys are the rows of the key layout, so the row width is the key size the merge buffer uses + return SizeInBytes() + SortMergeBuffer::MaximumSize(key_data->Count(), key_data->GetLayout().GetRowWidth()); +} + } // namespace duckdb diff --git a/src/duckdb/src/common/sort/sorted_run_merger.cpp b/src/duckdb/src/common/sort/sorted_run_merger.cpp index 04edb800f..1ddd7c6f0 100644 --- a/src/duckdb/src/common/sort/sorted_run_merger.cpp +++ b/src/duckdb/src/common/sort/sorted_run_merger.cpp @@ -6,6 +6,7 @@ #include "duckdb/common/thread_annotation.hpp" #include "duckdb/common/sorting/sorted_run.hpp" #include "duckdb/common/sorting/sort_key.hpp" +#include "duckdb/common/sorting/buffered_merge.hpp" #include "duckdb/common/types/row/block_iterator.hpp" #include "duckdb/common/types/row/tuple_data_collection.hpp" #include "duckdb/parallel/parallel_destroy_task.hpp" @@ -151,6 +152,8 @@ class SortedRunMergerLocalState : public LocalSourceState { //! Variables for scanning idx_t merged_partition_count; idx_t merged_partition_index; + //! Rows of the current partition that were added to the global progress + idx_t progress_scanned; //! For scanning Vector sort_key_pointers; @@ -168,7 +171,7 @@ class SortedRunMergerGlobalState : public GlobalSourceState { num_partitions((merger.total_count + (merger.partition_size - 1)) / merger.partition_size), iterator_state_type(GetBlockIteratorStateType(merger.external)), sort_key_type(merger.sort.key_layout->GetSortKeyType()), next_partition_idx(0), total_scanned(0), - destroy_partition_idx(0) { + progress_scanned(0), destroy_partition_idx(0) { // Initialize partitions partitions.resize(num_partitions); for (idx_t partition_idx = 0; partition_idx < num_partitions; partition_idx++) { @@ -277,6 +280,8 @@ class SortedRunMergerGlobalState : public GlobalSourceState { idx_t next_partition_idx; vector> partitions; atomic total_scanned; + //! Rows scanned so far, updated per chunk (for progress) + atomic progress_scanned; mutex destroy_lock; idx_t destroy_partition_idx; @@ -292,7 +297,8 @@ SortedRunMergerLocalState::SortedRunMergerLocalState(SortedRunMergerGlobalState : iterator_state_type(gstate.iterator_state_type), sort_key_type(gstate.sort_key_type), task(SortedRunMergerTask::FINISHED), run_boundaries(gstate.num_runs), merged_partition_count(DConstants::INVALID_INDEX), merged_partition_index(DConstants::INVALID_INDEX), - sort_key_pointers(LogicalType::POINTER), sorted_run_scan_state(gstate.context, gstate.merger.sort) { + progress_scanned(0), sort_key_pointers(LogicalType::POINTER), + sorted_run_scan_state(gstate.context, gstate.merger.sort) { for (const auto &run : gstate.merger.sorted_runs) { auto &key_data = *run->key_data; switch (iterator_state_type) { @@ -351,10 +357,14 @@ SourceResultType SortedRunMergerLocalState::ExecuteTask(SortedRunMergerGlobalSta case SortedRunMergerTask::SCAN_PARTITION: if (chunk) { ScanPartition(gstate, *chunk); + gstate.progress_scanned.fetch_add(chunk->size(), std::memory_order_relaxed); + progress_scanned += chunk->size(); } else { MaterializePartition(gstate); } if (!chunk || chunk->size() == 0) { + gstate.progress_scanned.fetch_add(merged_partition_count - progress_scanned, std::memory_order_relaxed); + progress_scanned = 0; gstate.DestroyScannedData(); gstate.partitions[partition_idx.GetIndex()]->scanned = true; // fetch_add returns the _previous_ value! @@ -646,8 +656,12 @@ void SortedRunMergerLocalState::TemplatedMergePartition(SortedRunMergerGlobalSta static const auto fallback = [](SORT_KEY *begin, SORT_KEY *end) { duckdb_pdqsort::pdqsort_branchless(begin, end); }; + // the run merges need a scratch buffer of up to half the keys - take it from the buffer allocator so that it is + // counted against the memory limit instead of coming from operator new inside std::inplace_merge + SortMergeBuffer merge_buffer(BufferAllocator::Get(gstate.context), merged_partition_count, sizeof(SORT_KEY)); + const BufferedMergePolicy merge(merge_buffer); duckdb_vergesort::vergesort(merged_partition_keys, merged_partition_keys + merged_partition_count, - std::less(), fallback); + std::less(), fallback, merge); } void SortedRunMergerLocalState::ScanPartition(SortedRunMergerGlobalState &gstate, DataChunk &chunk) { @@ -824,12 +838,17 @@ OperatorPartitionData SortedRunMerger::GetPartitionData(ExecutionContext &, Data ProgressData SortedRunMerger::GetProgress(ClientContext &, GlobalSourceState &gstate_p) const { auto &gstate = gstate_p.Cast(); ProgressData res; - res.done = static_cast(gstate.total_scanned); + res.done = static_cast(MinValue(gstate.progress_scanned, total_count)); res.total = static_cast(total_count); res.invalid = false; return res; } +bool SortedRunMerger::IsFinished(GlobalSourceState &gstate_p) const { + auto &gstate = gstate_p.Cast(); + return gstate.total_scanned == total_count; +} + //===--------------------------------------------------------------------===// // Non-Standard Interface //===--------------------------------------------------------------------===// diff --git a/src/duckdb/src/common/string_util.cpp b/src/duckdb/src/common/string_util.cpp index 9e919264f..1b1ab0fef 100644 --- a/src/duckdb/src/common/string_util.cpp +++ b/src/duckdb/src/common/string_util.cpp @@ -223,6 +223,26 @@ inline string TakePossiblyQuotedItem(const string &str, idx_t &index, char delim } // namespace string_util_internal +bool StringUtil::TryParseQuotedString(const string &str, idx_t &pos, string &result, char quote) { + if (pos >= str.size() || str[pos] != quote) { + return false; + } + string value; + for (idx_t i = pos + 1; i < str.size(); i++) { + if (str[i] != quote) { + value += str[i]; + } else if (i + 1 < str.size() && str[i + 1] == quote) { + value += quote; + i++; + } else { + pos = i + 1; + result = std::move(value); + return true; + } + } + return false; +} + vector StringUtil::SplitWithQuote(const string &str, char delimiter, char quote) { vector entries; idx_t i = 0; @@ -506,18 +526,17 @@ bool StringUtil::CIEndsWith(const string &str, const string &suffix) { bool StringUtil::CILessThan(const string &s1, const string &s2) { const auto charmap = ASCII_TO_UPPER_MAP; - unsigned char u1 {}, u2 {}; - - idx_t length = MinValue(s1.length(), s2.length()); - length += s1.length() != s2.length(); + // compare the common prefix byte-by-byte (treating an embedded NUL as a regular byte) + const idx_t length = MinValue(s1.length(), s2.length()); for (idx_t i = 0; i < length; i++) { - u1 = (unsigned char)s1[i]; - u2 = (unsigned char)s2[i]; - if (charmap[u1] != charmap[u2]) { - break; + const auto u1 = charmap[(unsigned char)s1[i]]; + const auto u2 = charmap[(unsigned char)s2[i]]; + if (u1 != u2) { + return u1 < u2; } } - return (charmap[u1] - charmap[u2]) < 0; + // the prefixes are equal - the shorter string sorts first + return s1.length() < s2.length(); } idx_t StringUtil::CIFind(const vector &vector, const Identifier &search_string) { @@ -922,6 +941,14 @@ string StringUtil::GetFilePath(const string &file_path) { return file_path.substr(0, pos + 1); } +string StringUtil::ReplaceFileName(const string &file_path, const string &file_name) { + auto pos = file_path.find_last_of("/\\"); + if (pos == string::npos) { + return file_name; + } + return file_path.substr(0, pos + 1) + file_name; +} + struct URLEncodeLength { using RESULT_TYPE = idx_t; diff --git a/src/duckdb/src/common/tree_renderer.cpp b/src/duckdb/src/common/tree_renderer.cpp index 665b746ea..2634c6714 100644 --- a/src/duckdb/src/common/tree_renderer.cpp +++ b/src/duckdb/src/common/tree_renderer.cpp @@ -5,6 +5,7 @@ #include "duckdb/common/tree_renderer/graphviz_tree_renderer.hpp" #include "duckdb/common/tree_renderer/yaml_tree_renderer.hpp" #include "duckdb/common/tree_renderer/mermaid_tree_renderer.hpp" +#include "duckdb/common/tree_renderer/compact_tree_renderer.hpp" #include "duckdb/main/profiler/profiler_print_format.hpp" #include "duckdb/common/exception.hpp" #include "duckdb/common/string_util.hpp" @@ -61,6 +62,7 @@ static const ProfilerPrintFormatEntry PRINT_FORMATS[] = { {"graphviz", MakeRenderer}, {"yaml", MakeRenderer}, {"mermaid", MakeRenderer}, + {"compact", MakeRenderer}, }; //! Look up the registry entry for a format name, throwing InvalidInputException (listing valid names) when unknown. diff --git a/src/duckdb/src/common/tree_renderer/compact_tree_renderer.cpp b/src/duckdb/src/common/tree_renderer/compact_tree_renderer.cpp new file mode 100644 index 000000000..a93d566c7 --- /dev/null +++ b/src/duckdb/src/common/tree_renderer/compact_tree_renderer.cpp @@ -0,0 +1,134 @@ +#include "duckdb/common/tree_renderer/compact_tree_renderer.hpp" + +#include "duckdb/common/string_util.hpp" +#include "duckdb/execution/physical_operator.hpp" +#include "duckdb/main/profiler/profiling_node.hpp" +#include "duckdb/main/query_profiler.hpp" +#include "duckdb/parallel/pipeline.hpp" +#include "duckdb/planner/logical_operator.hpp" + +namespace duckdb { + +string CompactTreeRenderer::ToString(const LogicalOperator &op) { + StringTreeRenderer ss; + Render(op, ss); + return ss.str(); +} + +string CompactTreeRenderer::ToString(const PhysicalOperator &op) { + StringTreeRenderer ss; + Render(op, ss); + return ss.str(); +} + +string CompactTreeRenderer::ToString(const ProfilingNode &op) { + StringTreeRenderer ss; + Render(op, ss); + return ss.str(); +} + +string CompactTreeRenderer::ToString(const Pipeline &op) { + StringTreeRenderer ss; + Render(op, ss); + return ss.str(); +} + +void CompactTreeRenderer::Render(const LogicalOperator &op, BaseTreeRenderer &ss) { + auto tree = RenderTree::CreateRenderTree(op); + ToStream(*tree, ss); +} + +void CompactTreeRenderer::Render(const PhysicalOperator &op, BaseTreeRenderer &ss) { + auto tree = RenderTree::CreateRenderTree(op); + ToStream(*tree, ss); +} + +void CompactTreeRenderer::Render(const ProfilingNode &op, BaseTreeRenderer &ss) { + auto tree = RenderTree::CreateRenderTree(op); + ToStream(*tree, ss); +} + +void CompactTreeRenderer::Render(const Pipeline &op, BaseTreeRenderer &ss) { + auto tree = RenderTree::CreateRenderTree(op); + ToStream(*tree, ss); +} + +void CompactTreeRenderer::RenderProfiler(const QueryProfiler &profiler, BaseTreeRenderer &ss) { + auto &metrics = profiler.GetQueryMetrics(); + vector stats; + stats.push_back(StringUtil::Format("time=%.4fs", metrics.GetStringMetricInSeconds("query.total_time"))); + auto bytes_read = metrics.GetBytesRead(); + if (bytes_read > 0) { + stats.push_back("read=" + StringUtil::BytesToHumanReadableString(bytes_read, 1000)); + } + auto bytes_written = metrics.GetBytesWritten(); + if (bytes_written > 0) { + stats.push_back("written=" + StringUtil::BytesToHumanReadableString(bytes_written, 1000)); + } + ss << "QUERY (" << StringUtil::Join(stats, ", ") << ")\n"; + profiler.RenderProfilingNodeTree(*this, ss); +} + +void CompactTreeRenderer::ToStreamInternal(RenderTree &root, BaseTreeRenderer &ss) { + RenderRecursive(root, ss, 0, 0, 0); +} + +//! Collapse a (possibly multi-line) property value onto one line +static string CompactValue(const string &value) { + auto lines = StringUtil::Split(value, "\n"); + vector parts; + for (auto &line : lines) { + auto trimmed = line; + StringUtil::Trim(trimmed); + if (!trimmed.empty()) { + parts.push_back(std::move(trimmed)); + } + } + return StringUtil::Join(parts, ", "); +} + +void CompactTreeRenderer::RenderRecursive(RenderTree &tree, BaseTreeRenderer &ss, idx_t depth, idx_t x, idx_t y) { + auto node_p = tree.GetNode(x, y); + D_ASSERT(node_p); + auto &node = *node_p; + + // the row counts and timing go in parentheses right after the name, the remaining properties follow + vector stats; + vector properties; + for (auto &entry : node.extra_text) { + auto &key = entry.first; + auto &value = entry.second; + if (value.empty()) { + continue; + } + if (key == RenderTreeNode::CARDINALITY) { + stats.push_back("rows=" + value); + } else if (key == RenderTreeNode::ESTIMATED_CARDINALITY) { + stats.push_back("est=" + value); + } else if (key == RenderTreeNode::TIMING) { + stats.push_back("time=" + value); + } else { + auto display_key = key; + if (StringUtil::StartsWith(display_key, "__")) { + display_key = + StringUtil::Title(StringUtil::Replace(StringUtil::Replace(display_key, "__", ""), "_", " ")); + } + properties.push_back(display_key + ": " + CompactValue(value)); + } + } + + ss << string(depth * 2, ' ') << node.name; + if (!stats.empty()) { + ss << " (" << StringUtil::Join(stats, ", ") << ")"; + } + if (!properties.empty()) { + ss << " " << StringUtil::Join(properties, "; "); + } + ss << '\n'; + + for (auto &child_pos : node.child_positions) { + RenderRecursive(tree, ss, depth + 1, child_pos.x, child_pos.y); + } +} + +} // namespace duckdb diff --git a/src/duckdb/src/common/tree_renderer/html_tree_renderer.cpp b/src/duckdb/src/common/tree_renderer/html_tree_renderer.cpp index f60ab52bd..b8909899a 100644 --- a/src/duckdb/src/common/tree_renderer/html_tree_renderer.cpp +++ b/src/duckdb/src/common/tree_renderer/html_tree_renderer.cpp @@ -300,7 +300,7 @@ void HTMLTreeRenderer::RenderProfiler(const QueryProfiler &profiler, BaseTreeRen query_sql = HighlightSQL(FormatSQL(profiler.GetQuerySQL())); query_timings.clear(); for (auto &entry : qm.GetMetricTimings()) { - query_timings.emplace_back(entry.first, static_cast(entry.second) / 1e9); + query_timings.emplace_back(entry.first, static_cast(entry.second.duration) / 1e9); } has_query_metrics = true; profiler.RenderProfilingNodeTree(*this, ss); diff --git a/src/duckdb/src/common/types.cpp b/src/duckdb/src/common/types.cpp index bd465a2d2..4aab4a9a4 100644 --- a/src/duckdb/src/common/types.cpp +++ b/src/duckdb/src/common/types.cpp @@ -6,7 +6,7 @@ #include "duckdb/catalog/default/default_types.hpp" #include "duckdb/common/enum_util.hpp" #include "duckdb/common/exception.hpp" -#include "duckdb/common/extra_type_info.hpp" +#include "duckdb/common/logical_type_info.hpp" #include "duckdb/common/numeric_utils.hpp" #include "duckdb/common/string_util.hpp" #include "duckdb/common/type_visitor.hpp" @@ -37,47 +37,32 @@ namespace duckdb { constexpr idx_t ArrayType::MAX_ARRAY_SIZE; const idx_t UnionType::MAX_UNION_MEMBERS; -LogicalType::LogicalType() : LogicalType(LogicalTypeId::INVALID) { -} - -LogicalType::LogicalType(LogicalTypeId id) : id_(id) { - physical_type_ = GetInternalType(); -} -LogicalType::LogicalType(LogicalTypeId id, shared_ptr type_info_p) - : id_(id), type_info_(std::move(type_info_p)) { - physical_type_ = GetInternalType(); -} - -LogicalType::LogicalType(const LogicalType &other) - : id_(other.id_), physical_type_(other.physical_type_), type_info_(other.type_info_) { -} - -LogicalType::LogicalType(LogicalType &&other) noexcept - : id_(other.id_), physical_type_(other.physical_type_), type_info_(std::move(other.type_info_)) { -} - -hash_t LogicalType::Hash() const { - return duckdb::Hash((uint8_t)id_); -} - -PhysicalType LogicalType::GetInternalType() { - switch (id_) { +//! Computes the physical type of a type with the given id and no parameters - returns false for an unknown id +static bool TryGetBuiltinPhysicalType(LogicalTypeId id, PhysicalType &result) noexcept { + switch (id) { case LogicalTypeId::BOOLEAN: - return PhysicalType::BOOL; + result = PhysicalType::BOOL; + return true; case LogicalTypeId::TINYINT: - return PhysicalType::INT8; + result = PhysicalType::INT8; + return true; case LogicalTypeId::UTINYINT: - return PhysicalType::UINT8; + result = PhysicalType::UINT8; + return true; case LogicalTypeId::SMALLINT: - return PhysicalType::INT16; + result = PhysicalType::INT16; + return true; case LogicalTypeId::USMALLINT: - return PhysicalType::UINT16; + result = PhysicalType::UINT16; + return true; case LogicalTypeId::SQLNULL: case LogicalTypeId::DATE: case LogicalTypeId::INTEGER: - return PhysicalType::INT32; + result = PhysicalType::INT32; + return true; case LogicalTypeId::UINTEGER: - return PhysicalType::UINT32; + result = PhysicalType::UINT32; + return true; case LogicalTypeId::BIGINT: case LogicalTypeId::TIME: case LogicalTypeId::TIME_NS: @@ -88,76 +73,63 @@ PhysicalType LogicalType::GetInternalType() { case LogicalTypeId::TIME_TZ: case LogicalTypeId::TIMESTAMP_TZ: case LogicalTypeId::TIMESTAMP_TZ_NS: - return PhysicalType::INT64; + result = PhysicalType::INT64; + return true; case LogicalTypeId::UBIGINT: - return PhysicalType::UINT64; + result = PhysicalType::UINT64; + return true; case LogicalTypeId::UHUGEINT: - return PhysicalType::UINT128; + result = PhysicalType::UINT128; + return true; case LogicalTypeId::HUGEINT: case LogicalTypeId::UUID: - return PhysicalType::INT128; + result = PhysicalType::INT128; + return true; case LogicalTypeId::FLOAT: - return PhysicalType::FLOAT; + result = PhysicalType::FLOAT; + return true; case LogicalTypeId::DOUBLE: - return PhysicalType::DOUBLE; - case LogicalTypeId::DECIMAL: { - if (!type_info_) { - return PhysicalType::INVALID; - } - auto width = DecimalType::GetWidth(*this); - if (width <= Decimal::MAX_WIDTH_INT16) { - return PhysicalType::INT16; - } else if (width <= Decimal::MAX_WIDTH_INT32) { - return PhysicalType::INT32; - } else if (width <= Decimal::MAX_WIDTH_INT64) { - return PhysicalType::INT64; - } else if (width <= Decimal::MAX_WIDTH_INT128) { - return PhysicalType::INT128; - } else { - throw InternalException("Decimal has a width of %d which is bigger than the maximum supported width of %d", - width, DecimalType::MaxWidth()); - } - } + result = PhysicalType::DOUBLE; + return true; case LogicalTypeId::BIGNUM: case LogicalTypeId::VARCHAR: case LogicalTypeId::CHAR: case LogicalTypeId::BLOB: case LogicalTypeId::BIT: case LogicalTypeId::TYPE: - return PhysicalType::VARCHAR; + case LogicalTypeId::GEOMETRY: + result = PhysicalType::VARCHAR; + return true; case LogicalTypeId::INTERVAL: - return PhysicalType::INTERVAL; + result = PhysicalType::INTERVAL; + return true; case LogicalTypeId::UNION: case LogicalTypeId::VARIANT: case LogicalTypeId::STRUCT: case LogicalTypeId::TUPLE: - return PhysicalType::STRUCT; + result = PhysicalType::STRUCT; + return true; case LogicalTypeId::LIST: case LogicalTypeId::MAP: - return PhysicalType::LIST; + result = PhysicalType::LIST; + return true; case LogicalTypeId::ARRAY: - return PhysicalType::ARRAY; + result = PhysicalType::ARRAY; + return true; case LogicalTypeId::POINTER: - // LCOV_EXCL_START - if (sizeof(uintptr_t) == sizeof(uint32_t)) { - return PhysicalType::UINT32; - } else if (sizeof(uintptr_t) == sizeof(uint64_t)) { - return PhysicalType::UINT64; - } else { - throw InternalException("Unsupported pointer size"); - } - // LCOV_EXCL_STOP + static_assert(sizeof(uintptr_t) == sizeof(uint32_t) || sizeof(uintptr_t) == sizeof(uint64_t), + "Unsupported pointer size"); + result = sizeof(uintptr_t) == sizeof(uint32_t) ? PhysicalType::UINT32 : PhysicalType::UINT64; + return true; case LogicalTypeId::VALIDITY: - return PhysicalType::BIT; - case LogicalTypeId::ENUM: { - if (!type_info_) { - return PhysicalType::INVALID; - } - return EnumType::GetPhysicalType(*this); - } + result = PhysicalType::BIT; + return true; case LogicalTypeId::LAMBDA: // a lambda has no value of its own - it occupies an argument slot that holds a constant placeholder - return PhysicalType::UINT8; + result = PhysicalType::UINT8; + return true; + case LogicalTypeId::DECIMAL: + case LogicalTypeId::ENUM: case LogicalTypeId::TABLE: case LogicalTypeId::ANY: case LogicalTypeId::INVALID: @@ -165,13 +137,170 @@ PhysicalType LogicalType::GetInternalType() { case LogicalTypeId::STRING_LITERAL: case LogicalTypeId::INTEGER_LITERAL: case LogicalTypeId::TEMPLATE: - return PhysicalType::INVALID; + result = PhysicalType::INVALID; + return true; case LogicalTypeId::UNBOUND: - return PhysicalType::UNKNOWN; + result = PhysicalType::UNKNOWN; + return true; + default: + return false; + } +} + +//! Computes the physical type of a type with the given id and info - returns false for an unknown id +static bool TryGetPhysicalType(LogicalTypeId id, const LogicalTypeInfo &info, PhysicalType &result) { + switch (id) { + case LogicalTypeId::DECIMAL: { + if (info.type != LogicalTypeInfoType::DECIMAL_TYPE_INFO) { + break; + } + auto width = info.Cast().width; + if (width <= Decimal::MAX_WIDTH_INT16) { + result = PhysicalType::INT16; + } else if (width <= Decimal::MAX_WIDTH_INT32) { + result = PhysicalType::INT32; + } else if (width <= Decimal::MAX_WIDTH_INT64) { + result = PhysicalType::INT64; + } else if (width <= Decimal::MAX_WIDTH_INT128) { + result = PhysicalType::INT128; + } else { + throw InternalException("Decimal has a width of %d which is bigger than the maximum supported width of %d", + width, DecimalType::MaxWidth()); + } + return true; + } + case LogicalTypeId::ENUM: + if (info.type != LogicalTypeInfoType::ENUM_TYPE_INFO) { + break; + } + result = EnumTypeInfo::DictType(info.Cast().GetDictSize()); + return true; + default: + break; + } + return TryGetBuiltinPhysicalType(id, result); +} + +namespace { + +constexpr idx_t BUILTIN_TYPE_INFO_COUNT = NumericLimits::Maximum() + 1; + +struct BuiltinTypeInfos { + //! The info for each id - nullptr for an invalid id + const LogicalTypeInfo *infos[BUILTIN_TYPE_INFO_COUNT] = {}; + //! The infos are created in place, so creating them never allocates + alignas(LogicalTypeInfo) char storage[BUILTIN_TYPE_INFO_COUNT][sizeof(LogicalTypeInfo)]; +}; + +} // namespace + +const LogicalTypeInfo *LogicalType::TryGetBuiltinTypeInfo(LogicalTypeId id) noexcept { + static const auto *builtins = []() noexcept { + // zero-initialized and never destroyed - types can still be destroyed during static destruction + static BuiltinTypeInfos result; + for (idx_t i = 0; i < BUILTIN_TYPE_INFO_COUNT; i++) { + auto builtin_id = static_cast(i); + auto &info = *new (result.storage[i]) LogicalTypeInfo(LogicalTypeInfoType::INVALID_TYPE_INFO); + if (TryGetBuiltinPhysicalType(builtin_id, info.physical_type)) { + info.id = builtin_id; + info.immortal = true; + result.infos[i] = &info; + } + } + return &result; + }(); + return builtins->infos[static_cast(id)]; +} + +const LogicalTypeInfo &LogicalType::GetBuiltinTypeInfo(LogicalTypeId id) { + auto info = TryGetBuiltinTypeInfo(id); + if (!info) { + throw InternalException("Invalid LogicalType %d", static_cast(id)); + } + return *info; +} + +LogicalType::LogicalType() : LogicalType(LogicalTypeId::INVALID) { +} + +LogicalType::LogicalType(LogicalTypeId id) : type_info_(GetBuiltinTypeInfo(id)) { +} + +const LogicalTypeInfo &LogicalType::AdoptNewTypeInfo(LogicalTypeId id, unique_ptr type_info) { + // validates the id - and ensures the builtin infos exist before any type does + auto &builtin_type_info = GetBuiltinTypeInfo(id); + if (!type_info) { + return builtin_type_info; + } + if (!TryGetPhysicalType(id, *type_info, type_info->physical_type)) { + throw InternalException("Invalid LogicalType %d", static_cast(id)); + } + type_info->id = id; + type_info->ref_count = 1; + return *type_info.release(); +} + +LogicalType::LogicalType(LogicalTypeId id, unique_ptr type_info) + : type_info_(AdoptNewTypeInfo(id, std::move(type_info))) { +} + +LogicalType::LogicalType(LogicalType &&other) noexcept : type_info_(other.type_info_) { + // the moved-from type keeps its id, but loses its parameters + // it may come from another copy of DuckDB (e.g. the host of a loadable extension that statically links its own + // copy), so this can be the first use of the builtins of this copy + other.type_info_ = *TryGetBuiltinTypeInfo(type_info_.get().id); +} + +const LogicalTypeInfo &LogicalType::ReleaseTypeInfo() && { + auto &result = type_info_.get(); + type_info_ = *TryGetBuiltinTypeInfo(result.id); + return result; +} + +LogicalType LogicalType::AdoptTypeInfo(const LogicalTypeInfo &type_info) { + return LogicalType(type_info); +} + +LogicalType LogicalType::CreateImmortal(LogicalTypeId id, unique_ptr type_info) { + type_info->immortal = true; + return LogicalType(id, std::move(type_info)); +} + +hash_t LogicalType::Hash() const { + return duckdb::Hash((uint8_t)id()); +} + +bool LogicalType::HasParameters() const { + auto info_type = type_info_.get().type; + switch (id()) { + case LogicalTypeId::LIST: + case LogicalTypeId::MAP: + return info_type == LogicalTypeInfoType::LIST_TYPE_INFO; + case LogicalTypeId::STRUCT: + case LogicalTypeId::TUPLE: + case LogicalTypeId::UNION: + case LogicalTypeId::VARIANT: + return info_type == LogicalTypeInfoType::STRUCT_TYPE_INFO; + case LogicalTypeId::ARRAY: + return info_type == LogicalTypeInfoType::ARRAY_TYPE_INFO; + case LogicalTypeId::DECIMAL: + return info_type == LogicalTypeInfoType::DECIMAL_TYPE_INFO; + case LogicalTypeId::ENUM: + return info_type == LogicalTypeInfoType::ENUM_TYPE_INFO; + case LogicalTypeId::VARCHAR: + return info_type == LogicalTypeInfoType::STRING_TYPE_INFO; + case LogicalTypeId::ANY: + return info_type == LogicalTypeInfoType::ANY_TYPE_INFO; + case LogicalTypeId::INTEGER_LITERAL: + return info_type == LogicalTypeInfoType::INTEGER_LITERAL_TYPE_INFO; + case LogicalTypeId::TEMPLATE: + return info_type == LogicalTypeInfoType::TEMPLATE_TYPE_INFO; case LogicalTypeId::GEOMETRY: - return PhysicalType::VARCHAR; + return info_type == LogicalTypeInfoType::GEO_TYPE_INFO; + case LogicalTypeId::UNBOUND: + return info_type == LogicalTypeInfoType::UNBOUND_TYPE_INFO; default: - throw InternalException("Invalid LogicalType %s", ToString()); + return false; } } @@ -401,155 +530,177 @@ static string TypeModifierListToString(const vector &mod_li } string LogicalType::ToString() const { - if (id_ != LogicalTypeId::UNBOUND) { - auto alias = GetAlias(); - if (!alias.empty()) { - if (HasExtensionInfo()) { - auto &ext_info = *GetExtensionInfo(); - alias += TypeModifierListToString(ext_info.modifiers); - } - return alias; - } - } - switch (id_) { - case LogicalTypeId::TUPLE: { - if (!type_info_) { - return "TUPLE"; - } - auto &child_types = StructType::GetChildTypes(*this); - if (child_types.empty()) { - return "TUPLE"; + // nested types are rendered with an explicit stack to avoid unbounded recursion + struct StackEntry { + optional_ptr type; + string text; + }; + string result; + vector stack; + auto push_text = [&](string text) { + stack.push_back(StackEntry {nullptr, std::move(text)}); + }; + auto push_type = [&](const LogicalType &type) { + stack.push_back(StackEntry {&type, string()}); + }; + push_type(*this); + + while (!stack.empty()) { + auto entry = std::move(stack.back()); + stack.pop_back(); + if (!entry.type) { + result += entry.text; + continue; } - string ret = "TUPLE("; - for (size_t i = 0; i < child_types.size(); i++) { - ret += child_types[i].second.ToString(); - if (i < child_types.size() - 1) { - ret += ", "; + auto &type = *entry.type; + if (type.id() != LogicalTypeId::UNBOUND) { + auto alias = type.GetAlias(); + if (!alias.empty()) { + if (type.HasExtensionInfo()) { + auto &ext_info = *type.GetExtensionInfo(); + alias += TypeModifierListToString(ext_info.modifiers); + } + result += alias; + continue; } } - ret += ")"; - return ret; - } - case LogicalTypeId::STRUCT: { - if (!type_info_) { - return "STRUCT"; - } - auto &child_types = StructType::GetChildTypes(*this); - if (child_types.empty()) { - return "STRUCT"; - } - - auto is_unnamed = StructType::IsUnnamed(*this); - string ret = "STRUCT("; - for (size_t i = 0; i < child_types.size(); i++) { - if (is_unnamed) { - ret += child_types[i].second.ToString(); - } else { - ret += StringUtil::Format("%s %s", SQLIdentifier(child_types[i].first), child_types[i].second); + switch (type.id()) { + case LogicalTypeId::TUPLE: + case LogicalTypeId::STRUCT: { + string name = type.id() == LogicalTypeId::TUPLE ? "TUPLE" : "STRUCT"; + if (!type.HasParameters() || StructType::GetChildTypes(type).empty()) { + result += name; + break; } - if (i < child_types.size() - 1) { - ret += ", "; + auto &child_types = StructType::GetChildTypes(type); + auto print_names = type.id() == LogicalTypeId::STRUCT; + push_text(")"); + for (idx_t i = child_types.size(); i > 0; i--) { + auto &child = child_types[i - 1]; + push_type(child.second); + if (print_names) { + push_text(SQLIdentifier(child.first) + " "); + } + if (i > 1) { + push_text(", "); + } } + push_text(name + "("); + break; } - ret += ")"; - return ret; - } - case LogicalTypeId::LIST: { - if (!type_info_) { - return "LIST"; - } - return ListType::GetChildType(*this).ToString() + "[]"; - } - case LogicalTypeId::MAP: { - if (!type_info_) { - return "MAP"; - } - auto &key_type = MapType::KeyType(*this); - auto &value_type = MapType::ValueType(*this); - return "MAP(" + key_type.ToString() + ", " + value_type.ToString() + ")"; - } - case LogicalTypeId::UNION: { - if (!type_info_) { - return "UNION"; + case LogicalTypeId::LIST: { + if (!type.HasParameters()) { + result += "LIST"; + break; + } + push_text("[]"); + push_type(ListType::GetChildType(type)); + break; } - string ret = "UNION("; - size_t count = UnionType::GetMemberCount(*this); - for (size_t i = 0; i < count; i++) { - auto member_name = UnionType::GetMemberName(*this, i); - auto member_type = UnionType::GetMemberType(*this, i).ToString(); - ret += StringUtil::Format("%s %s", SQLIdentifier(member_name), member_type); - if (i < count - 1) { - ret += ", "; + case LogicalTypeId::ARRAY: { + if (!type.HasParameters()) { + result += "ARRAY"; + break; } + auto size = ArrayType::GetSize(type); + push_text(size == 0 ? "[ANY]" : "[" + to_string(size) + "]"); + push_type(ArrayType::GetChildType(type)); + break; } - ret += ")"; - return ret; - } - case LogicalTypeId::ARRAY: { - if (!type_info_) { - return "ARRAY"; + case LogicalTypeId::MAP: { + if (!type.HasParameters()) { + result += "MAP"; + break; + } + push_text(")"); + push_type(MapType::ValueType(type)); + push_text(", "); + push_type(MapType::KeyType(type)); + push_text("MAP("); + break; } - auto size = ArrayType::GetSize(*this); - if (size == 0) { - return ArrayType::GetChildType(*this).ToString() + "[ANY]"; - } else { - return ArrayType::GetChildType(*this).ToString() + "[" + to_string(size) + "]"; + case LogicalTypeId::UNION: { + if (!type.HasParameters()) { + result += "UNION"; + break; + } + auto count = UnionType::GetMemberCount(type); + push_text(")"); + for (idx_t i = count; i > 0; i--) { + push_type(UnionType::GetMemberType(type, i - 1)); + push_text(SQLIdentifier(UnionType::GetMemberName(type, i - 1)) + " "); + if (i > 1) { + push_text(", "); + } + } + push_text("UNION("); + break; } - } - case LogicalTypeId::DECIMAL: { - if (!type_info_) { - return "DECIMAL"; + case LogicalTypeId::DECIMAL: { + if (!type.HasParameters()) { + result += "DECIMAL"; + break; + } + auto width = DecimalType::GetWidth(type); + auto scale = DecimalType::GetScale(type); + if (width == 0) { + result += "DECIMAL"; + break; + } + result += StringUtil::Format("DECIMAL(%d,%d)", width, scale); + break; } - auto width = DecimalType::GetWidth(*this); - auto scale = DecimalType::GetScale(*this); - if (width == 0) { - return "DECIMAL"; + case LogicalTypeId::ENUM: { + result += "ENUM("; + for (idx_t i = 0; i < EnumType::GetSize(type); i++) { + if (i > 0) { + result += ", "; + } + result += SQLString(EnumType::GetString(type, i).GetString()); + } + result += ")"; + break; } - return StringUtil::Format("DECIMAL(%d,%d)", width, scale); - } - case LogicalTypeId::ENUM: { - string ret = "ENUM("; - for (idx_t i = 0; i < EnumType::GetSize(*this); i++) { - if (i > 0) { - ret += ", "; + case LogicalTypeId::UNBOUND: { + if (!type.HasParameters()) { + result += "UNBOUND"; + break; } - ret += SQLString(EnumType::GetString(*this, i).GetString()); + auto &expr = UnboundType::GetTypeExpression(type); + if (expr->GetExpressionType() != ExpressionType::TYPE) { + result += "(" + expr->ToString() + ")"; + } else { + result += expr->ToString(); + } + break; } - ret += ")"; - return ret; - } - case LogicalTypeId::UNBOUND: { - if (!type_info_) { - return "UNBOUND"; + case LogicalTypeId::SQLNULL: { + result += "\"NULL\""; + break; } - - auto &expr = UnboundType::GetTypeExpression(*this); - if (expr->GetExpressionType() != ExpressionType::TYPE) { - return "(" + expr->ToString() + ")"; - } else { - return expr->ToString(); + case LogicalTypeId::TEMPLATE: { + if (!type.HasParameters()) { + result += "T"; + break; + } + result += TemplateType::GetName(type); + break; } - } - case LogicalTypeId::SQLNULL: { - return "\"NULL\""; - } - case LogicalTypeId::TEMPLATE: { - if (!type_info_) { - return "T"; + case LogicalTypeId::GEOMETRY: { + if (!GeoType::HasCRS(type)) { + result += "GEOMETRY"; + break; + } + auto &crs = GeoType::GetCRS(type); + result += StringUtil::Format("GEOMETRY(%s)", SQLString(crs.GetDefinition())); + break; } - return TemplateType::GetName(*this); - } - case LogicalTypeId::GEOMETRY: { - if (!type_info_ || !GeoType::HasCRS(*this)) { - return "GEOMETRY"; + default: + result += EnumUtil::ToString(type.id()); + break; } - auto &crs = GeoType::GetCRS(*this); - auto crs_text = SQLString(crs.GetDefinition()); - return StringUtil::Format("GEOMETRY(%s)", crs_text); - } - default: - return EnumUtil::ToString(id_); } + return result; } LogicalTypeId TransformStringToLogicalTypeId(const string &str) { @@ -567,7 +718,7 @@ LogicalType TransformStringToLogicalType(const string &str, ClientContext &conte } bool LogicalType::IsIntegral() const { - switch (id_) { + switch (id()) { case LogicalTypeId::TINYINT: case LogicalTypeId::SMALLINT: case LogicalTypeId::INTEGER: @@ -585,7 +736,7 @@ bool LogicalType::IsIntegral() const { } bool LogicalType::IsSigned() const { - switch (id_) { + switch (id()) { case LogicalTypeId::TINYINT: case LogicalTypeId::SMALLINT: case LogicalTypeId::INTEGER: @@ -599,7 +750,7 @@ bool LogicalType::IsSigned() const { } bool LogicalType::IsUnsigned() const { - switch (id_) { + switch (id()) { case LogicalTypeId::UTINYINT: case LogicalTypeId::USMALLINT: case LogicalTypeId::UINTEGER: @@ -613,7 +764,7 @@ bool LogicalType::IsUnsigned() const { } bool LogicalType::IsFloating() const { - switch (id_) { + switch (id()) { case LogicalTypeId::FLOAT: case LogicalTypeId::DOUBLE: return true; @@ -623,7 +774,7 @@ bool LogicalType::IsFloating() const { } bool LogicalType::IsNumeric() const { - return IsNumeric(id_); + return IsNumeric(id()); } bool LogicalType::IsNumeric(const LogicalTypeId type) { @@ -648,7 +799,7 @@ bool LogicalType::IsNumeric(const LogicalTypeId type) { } bool LogicalType::IsTemporal() const { - switch (id_) { + switch (id()) { case LogicalTypeId::DATE: case LogicalTypeId::TIME: case LogicalTypeId::TIME_NS: @@ -680,30 +831,14 @@ bool LogicalType::IsComplete() const { return true; // These are incomplete by default case LogicalTypeId::LIST: case LogicalTypeId::MAP: - if (!type.AuxInfo() || type.AuxInfo()->type != ExtraTypeInfoType::LIST_TYPE_INFO) { - return true; // Missing or incorrect type info - } - break; case LogicalTypeId::STRUCT: case LogicalTypeId::TUPLE: case LogicalTypeId::UNION: case LogicalTypeId::VARIANT: - if (!type.AuxInfo() || type.AuxInfo()->type != ExtraTypeInfoType::STRUCT_TYPE_INFO) { - return true; // Missing or incorrect type info - } - break; case LogicalTypeId::ARRAY: - if (!type.AuxInfo() || type.AuxInfo()->type != ExtraTypeInfoType::ARRAY_TYPE_INFO) { - return true; // Missing or incorrect type info - } - break; case LogicalTypeId::DECIMAL: - if (!type.AuxInfo() || type.AuxInfo()->type != ExtraTypeInfoType::DECIMAL_TYPE_INFO) { - return true; // Missing or incorrect type info - } - break; case LogicalTypeId::ENUM: - if (!type.AuxInfo() || type.AuxInfo()->type != ExtraTypeInfoType::ENUM_TYPE_INFO) { + if (!type.HasParameters()) { return true; // Missing or incorrect type info } break; @@ -712,16 +847,15 @@ bool LogicalType::IsComplete() const { } // Type has type info, check if it is complete - D_ASSERT(type.AuxInfo()); - switch (type.AuxInfo()->type) { - case ExtraTypeInfoType::STRUCT_TYPE_INFO: + switch (type.GetTypeInfo().type) { + case LogicalTypeInfoType::STRUCT_TYPE_INFO: // empty STRUCTs/TUPLEs are complete (children, if any, are checked by recursion) // UNION/VARIANT (which also use STRUCT_TYPE_INFO) cannot be empty if (type.id() == LogicalTypeId::STRUCT || type.id() == LogicalTypeId::TUPLE) { return false; } - return type.AuxInfo()->Cast().child_types.empty(); - case ExtraTypeInfoType::DECIMAL_TYPE_INFO: { + return type.GetTypeInfo().Cast().child_types.empty(); + case LogicalTypeInfoType::DECIMAL_TYPE_INFO: { // A well-formed decimal is not incomplete auto is_well_formed = DecimalType::GetWidth(type) >= 1 && DecimalType::GetWidth(type) <= Decimal::MAX_WIDTH_DECIMAL && @@ -763,7 +897,7 @@ bool LogicalType::SupportsRegularUpdate() const { } bool LogicalType::GetDecimalProperties(uint8_t &width, uint8_t &scale) const { - switch (id_) { + switch (id()) { case LogicalTypeId::SQLNULL: width = 0; scale = 0; @@ -1191,7 +1325,7 @@ LogicalType LogicalType::MaxLogicalType(ClientContext &context, const LogicalTyp void LogicalType::Verify() const { #ifdef DEBUG - switch (id_) { + switch (id()) { case LogicalTypeId::DECIMAL: D_ASSERT(DecimalType::GetWidth(*this) >= 1 && DecimalType::GetWidth(*this) <= Decimal::MAX_WIDTH_DECIMAL); D_ASSERT(DecimalType::GetScale(*this) <= DecimalType::GetWidth(*this)); @@ -1260,7 +1394,7 @@ bool ApproxEqual(double ldecimal, double rdecimal) { void LogicalType::Serialize(Serializer &serializer) const { // Serialize geometry as old extension geometry type if required - if (id_ == LogicalTypeId::GEOMETRY && !serializer.ShouldSerialize(StorageVersion::V1_5_0)) { + if (id() == LogicalTypeId::GEOMETRY && !serializer.ShouldSerialize(StorageVersion::V1_5_0)) { // This will drop the CRS information, but that's better than throwing an error. auto legacy_geom = Geometry::GetSpatialGeometryType(); legacy_geom.Serialize(serializer); @@ -1271,7 +1405,7 @@ void LogicalType::Serialize(Serializer &serializer) const { // 1. try to default-bind into a concrete logical type, and serialize that // 2. if that fails, serialize normally, in which case the UNBOUND_TYPE_INFO will try to // write itself as an old-style USER type. - if (id_ == LogicalTypeId::UNBOUND && !serializer.ShouldSerialize(StorageVersion::V1_5_0)) { + if (id() == LogicalTypeId::UNBOUND && !serializer.ShouldSerialize(StorageVersion::V1_5_0)) { try { auto bound_type = UnboundType::TryDefaultBind(*this); if (bound_type.id() != LogicalTypeId::INVALID && bound_type.id() != LogicalTypeId::UNBOUND) { @@ -1293,34 +1427,51 @@ void LogicalType::Serialize(Serializer &serializer) const { // TUPLE is a recent type. We store its own id when targeting a recent storage version (V2_0_0+), but for older // formats we write it as an unnamed STRUCT for backwards compatibility (older engines read it as an unnamed // struct - its children carry empty names - and we convert unnamed structs back to TUPLE on deserialize). - auto serialized_id = id_; - if (id_ == LogicalTypeId::TUPLE && !serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + auto serialized_id = id(); + if (id() == LogicalTypeId::TUPLE && !serializer.ShouldSerialize(StorageVersion::V2_0_0)) { serialized_id = LogicalTypeId::STRUCT; } serializer.WriteProperty(100, "id", serialized_id); - serializer.WritePropertyWithDefault>(101, "type_info", type_info_); + // types without parameters are written without type info + optional_ptr type_info; + if (type_info_.get().type != LogicalTypeInfoType::INVALID_TYPE_INFO) { + type_info = type_info_.get(); + } + serializer.WritePropertyWithDefault>(101, "type_info", type_info); } LogicalType LogicalType::Deserialize(Deserializer &deserializer) { auto id = deserializer.ReadProperty(100, "id"); - auto type_info = deserializer.ReadPropertyWithDefault>(101, "type_info"); + auto type_info = deserializer.ReadPropertyWithDefault>(101, "type_info"); if (id == LogicalTypeId::LEGACY_AGGREGATE_STATE) { // convert legacy aggregate state to blob on deserialize return LogicalType::BLOB; } - LogicalType result(id, type_info); + // Convert unnamed (non-empty) STRUCTs back to TUPLE + if (id == LogicalTypeId::STRUCT && type_info && type_info->type == LogicalTypeInfoType::STRUCT_TYPE_INFO && + StructType::AllUnnamed(type_info->Cast().child_types)) { + id = LogicalTypeId::TUPLE; + } + auto info_type = type_info ? type_info->type : LogicalTypeInfoType::INVALID_TYPE_INFO; + if (info_type == LogicalTypeInfoType::DECIMAL_TYPE_INFO) { + auto &decimal_info = type_info->Cast(); + if (!Decimal::IsValidWidthScale(decimal_info.width, decimal_info.scale)) { + throw SerializationException("Failed to deserialize type: invalid DECIMAL(%d, %d)", decimal_info.width, + decimal_info.scale); + } + } + LogicalType result(id, std::move(type_info)); + if (info_type != LogicalTypeInfoType::INVALID_TYPE_INFO && info_type != LogicalTypeInfoType::GENERIC_TYPE_INFO && + !result.HasParameters()) { + throw SerializationException("Failed to deserialize type %s: type info %s does not match the type", + EnumUtil::ToString(id), EnumUtil::ToString(info_type)); + } if (Geometry::IsSpatialGeometryType(result)) { // This is a legacy geometry type, deserialize as geometry return LogicalType::GEOMETRY(); } - - // Convert unnamed (non-empty) STRUCTs back to TUPLE - if (id == LogicalTypeId::STRUCT && type_info && StructType::IsUnnamed(result)) { - return LogicalType(LogicalTypeId::TUPLE, std::move(type_info)); - } - return result; } @@ -1328,79 +1479,52 @@ LogicalType LogicalType::Deserialize(Deserializer &deserializer) { // Extra Type Info //===--------------------------------------------------------------------===// LogicalType LogicalType::Copy() const { - LogicalType copy = *this; - if (type_info_ && type_info_->type != ExtraTypeInfoType::ENUM_TYPE_INFO) { - // We copy (i.e., create new) type info, unless the type is an ENUM - enum type info is kept shared to avoid - // rebuilding the dictionary lookup map; use DeepCopy to force a copy - copy.type_info_ = type_info_->Copy(); + if (type_info_.get().type == LogicalTypeInfoType::INVALID_TYPE_INFO || + type_info_.get().type == LogicalTypeInfoType::ENUM_TYPE_INFO) { + // enum type info is kept shared to avoid rebuilding the dictionary lookup map + return *this; } - return copy; + return LogicalType(id(), type_info_.get().Copy()); } -LogicalType LogicalType::DeepCopy() const { - LogicalType copy = *this; - if (type_info_) { - copy.type_info_ = type_info_->DeepCopy(); +//! Copies the type info so it can be modified before being adopted by a new type +static unique_ptr CopyTypeInfo(const LogicalTypeInfo &info) { + if (info.type == LogicalTypeInfoType::INVALID_TYPE_INFO) { + return make_uniq(LogicalTypeInfoType::GENERIC_TYPE_INFO); } - return copy; + return info.Copy(); } LogicalType LogicalType::WithAlias(string alias) const { - if (!type_info_) { - if (alias.empty()) { - // a type without type info is equivalent to a generic type info with an empty alias - return *this; - } - LogicalType result = *this; - result.type_info_ = make_shared_ptr(ExtraTypeInfoType::GENERIC_TYPE_INFO, std::move(alias)); - return result; - } - if (type_info_->alias == alias) { + if (type_info_.get().alias == alias) { // avoid copying the (potentially expensive) type info if the alias does not change return *this; } - auto new_info = type_info_->Copy(); + auto new_info = CopyTypeInfo(type_info_.get()); new_info->alias = std::move(alias); - LogicalType result = *this; - result.type_info_ = std::move(new_info); - return result; + return LogicalType(id(), std::move(new_info)); } string LogicalType::GetAlias() const { - if (type_info_) { - return type_info_->alias; - } - return string(); + return type_info_.get().alias; } bool LogicalType::HasAlias() const { - if (type_info_ && !type_info_->alias.empty()) { - return true; - } - return false; + return !type_info_.get().alias.empty(); } bool LogicalType::HasExtensionInfo() const { - if (type_info_ && type_info_->extension_info) { - return true; - } - return false; + return type_info_.get().extension_info != nullptr; } optional_ptr LogicalType::GetExtensionInfo() const { - if (type_info_ && type_info_->extension_info) { - return type_info_->extension_info.get(); - } - return nullptr; + return type_info_.get().extension_info.get(); } LogicalType LogicalType::WithExtensionInfo(unique_ptr info) const { - auto new_info = - type_info_ ? type_info_->Copy() : make_shared_ptr(ExtraTypeInfoType::GENERIC_TYPE_INFO); + auto new_info = CopyTypeInfo(type_info_.get()); new_info->extension_info = std::move(info); - LogicalType result = *this; - result.type_info_ = std::move(new_info); - return result; + return LogicalType(id(), std::move(new_info)); } //===--------------------------------------------------------------------===// @@ -1408,16 +1532,14 @@ LogicalType LogicalType::WithExtensionInfo(unique_ptr info) c //===--------------------------------------------------------------------===// uint8_t DecimalType::GetWidth(const LogicalType &type) { D_ASSERT(type.id() == LogicalTypeId::DECIMAL); - auto info = type.AuxInfo(); - D_ASSERT(info); - return info->Cast().width; + auto &info = type.GetTypeInfo(); + return info.Cast().width; } uint8_t DecimalType::GetScale(const LogicalType &type) { D_ASSERT(type.id() == LogicalTypeId::DECIMAL); - auto info = type.AuxInfo(); - D_ASSERT(info); - return info->Cast().scale; + auto &info = type.GetTypeInfo(); + return info.Cast().scale; } uint8_t DecimalType::MaxWidth() { @@ -1426,7 +1548,7 @@ uint8_t DecimalType::MaxWidth() { LogicalType LogicalType::DECIMAL(uint8_t width, uint8_t scale) { D_ASSERT(width >= scale); - auto type_info = make_shared_ptr(width, scale); + auto type_info = make_uniq(width, scale); return LogicalType(LogicalTypeId::DECIMAL, std::move(type_info)); } @@ -1437,18 +1559,15 @@ string StringType::GetCollation(const LogicalType &type) { if (type.id() != LogicalTypeId::VARCHAR) { return string(); } - auto info = type.AuxInfo(); - if (!info) { + auto &info = type.GetTypeInfo(); + if (info.type != LogicalTypeInfoType::STRING_TYPE_INFO) { return string(); } - if (info->type == ExtraTypeInfoType::GENERIC_TYPE_INFO) { - return string(); - } - return info->Cast().collation; + return info.Cast().collation; } LogicalType LogicalType::VARCHAR_COLLATION(string collation) { // NOLINT - auto string_info = make_shared_ptr(std::move(collation)); + auto string_info = make_uniq(std::move(collation)); return LogicalType(LogicalTypeId::VARCHAR, std::move(string_info)); } @@ -1456,14 +1575,15 @@ LogicalType LogicalType::VARCHAR_COLLATION(string collation) { // NOLINT // List Type //===--------------------------------------------------------------------===// const LogicalType &ListType::GetChildType(const LogicalType &type) { - D_ASSERT(type.id() == LogicalTypeId::LIST || type.id() == LogicalTypeId::MAP); - auto info = type.AuxInfo(); - D_ASSERT(info); - return info->Cast().child_type; + if (type.id() != LogicalTypeId::LIST && type.id() != LogicalTypeId::MAP) { + throw InternalException("ListType::GetChildType called on a non-LIST type: %s", type.ToString()); + } + auto &info = type.GetTypeInfo(); + return info.Cast().child_type; } LogicalType LogicalType::LIST(const LogicalType &child) { - auto info = make_shared_ptr(child); + auto info = make_uniq(child); return LogicalType(LogicalTypeId::LIST, std::move(info)); } @@ -1471,12 +1591,13 @@ LogicalType LogicalType::LIST(const LogicalType &child) { // Struct Type //===--------------------------------------------------------------------===// const child_list_t &StructType::GetChildTypes(const LogicalType &type) { - D_ASSERT(type.id() == LogicalTypeId::STRUCT || type.id() == LogicalTypeId::TUPLE || - type.id() == LogicalTypeId::UNION || type.id() == LogicalTypeId::VARIANT); + if (type.id() != LogicalTypeId::STRUCT && type.id() != LogicalTypeId::TUPLE && type.id() != LogicalTypeId::UNION && + type.id() != LogicalTypeId::VARIANT) { + throw InternalException("StructType::GetChildTypes called on a non-STRUCT type: %s", type.ToString()); + } - auto info = type.AuxInfo(); - D_ASSERT(info); - return info->Cast().child_types; + auto &info = type.GetTypeInfo(); + return info.Cast().child_types; } const LogicalType &StructType::GetChildType(const LogicalType &type, idx_t index) { @@ -1506,18 +1627,27 @@ idx_t StructType::GetChildCount(const LogicalType &type) { } bool StructType::IsUnnamed(const LogicalType &type) { - if (type.id() == LogicalTypeId::TUPLE) { - return true; - } - auto &child_types = StructType::GetChildTypes(type); - if (child_types.empty()) { + return type.id() == LogicalTypeId::TUPLE; +} + +bool StructType::AllUnnamed(const child_list_t &children) { + if (children.empty()) { return false; } - return child_types[0].first.empty(); // NOLINT + for (auto &child : children) { + if (!child.first.empty()) { + return false; + } + } + return true; } LogicalType LogicalType::STRUCT(child_list_t children) { - auto info = make_shared_ptr(std::move(children)); + if (StructType::AllUnnamed(children)) { + // a struct without any member names is a TUPLE + return LogicalType::TUPLE(std::move(children)); + } + auto info = make_uniq(std::move(children)); return LogicalType(LogicalTypeId::STRUCT, std::move(info)); } @@ -1545,12 +1675,44 @@ child_list_t TupleType::NamedChildren(const LogicalType &type) { return result; } +child_list_t LogicalType::GetNamedChildTypes(const LogicalType &type) { + switch (type.id()) { + case LogicalTypeId::LIST: + return {{"element", ListType::GetChildType(type)}}; + case LogicalTypeId::ARRAY: + return {{"element", ArrayType::GetChildType(type)}}; + case LogicalTypeId::MAP: + return {{"key", MapType::KeyType(type)}, {"value", MapType::ValueType(type)}}; + case LogicalTypeId::STRUCT: + return StructType::GetChildTypes(type); + case LogicalTypeId::TUPLE: + return TupleType::NamedChildren(type); + default: + throw InternalException("Unsupported type %s in GetNamedChildTypes", type.ToString()); + } +} + +LogicalType LogicalType::ConstructNestedType(const LogicalType &type, child_list_t children) { + switch (type.id()) { + case LogicalTypeId::LIST: + D_ASSERT(children.size() == 1); + return LogicalType::LIST(children[0].second); + case LogicalTypeId::MAP: + D_ASSERT(children.size() == 2); + return LogicalType::MAP(children[0].second, children[1].second); + case LogicalTypeId::STRUCT: + return LogicalType::STRUCT(std::move(children)); + default: + throw InternalException("Unsupported type %s in ConstructNestedType", type.ToString()); + } +} + LogicalType LogicalType::TUPLE(child_list_t children) { // a TUPLE is an unnamed struct - all member names are empty for (auto &child : children) { child.first.clear(); } - auto info = make_shared_ptr(std::move(children)); + auto info = make_uniq(std::move(children)); return LogicalType(LogicalTypeId::TUPLE, std::move(info)); } @@ -1584,7 +1746,7 @@ LogicalType LogicalType::MAP(const LogicalType &child_p) { new_children[1].first = "value"; auto child = LogicalType::STRUCT(std::move(new_children)); - auto info = make_shared_ptr(child); + auto info = make_uniq(child); return LogicalType(LogicalTypeId::MAP, std::move(info)); } @@ -1613,7 +1775,7 @@ LogicalType LogicalType::UNION(child_list_t members) { D_ASSERT(members.size() <= UnionType::MAX_UNION_MEMBERS); // union types always have a hidden "tag" field in front members.insert(members.begin(), {"", LogicalType::UTINYINT}); - auto info = make_shared_ptr(std::move(members)); + auto info = make_uniq(std::move(members)); return LogicalType(LogicalTypeId::UNION, std::move(info)); } @@ -1645,7 +1807,7 @@ const child_list_t UnionType::CopyMemberTypes(const LogicalType &ty // Unbound Type //===--------------------------------------------------------------------===// LogicalType LogicalType::UNBOUND(unique_ptr expr) { - auto info = make_shared_ptr(std::move(expr)); + auto info = make_uniq(std::move(expr)); return LogicalType(LogicalTypeId::UNBOUND, std::move(info)); } @@ -1668,27 +1830,26 @@ LogicalType LogicalType::ENUM(const string &enum_name, const Vector &ordered_dat } const string EnumType::GetValue(const Value &val) { - auto info = val.type().AuxInfo(); - auto &values_insert_order = info->Cast().GetValuesInsertOrder(); + auto &info = val.type().GetTypeInfo(); + auto &values_insert_order = info.Cast().GetValuesInsertOrder(); return StringValue::Get(values_insert_order.GetValue(val.GetValue())); } const Vector &EnumType::GetValuesInsertOrder(const LogicalType &type) { D_ASSERT(type.id() == LogicalTypeId::ENUM); - auto info = type.AuxInfo(); - return info->Cast().GetValuesInsertOrder(); + auto &info = type.GetTypeInfo(); + return info.Cast().GetValuesInsertOrder(); } idx_t EnumType::GetSize(const LogicalType &type) { D_ASSERT(type.id() == LogicalTypeId::ENUM); - auto info = type.AuxInfo(); - return info->Cast().GetDictSize(); + auto &info = type.GetTypeInfo(); + return info.Cast().GetDictSize(); } PhysicalType EnumType::GetPhysicalType(const LogicalType &type) { D_ASSERT(type.id() == LogicalTypeId::ENUM); - auto aux_info = type.AuxInfo(); - auto &info = aux_info->Cast(); + auto &info = type.GetTypeInfo().Cast(); D_ASSERT(info.GetEnumDictType() == EnumDictType::VECTOR_DICT); return EnumTypeInfo::DictType(info.GetDictSize()); } @@ -1697,7 +1858,9 @@ PhysicalType EnumType::GetPhysicalType(const LogicalType &type) { // JSON Type //===--------------------------------------------------------------------===// LogicalType LogicalType::JSON() { - return LogicalType(LogicalTypeId::VARCHAR).WithAlias(JSON_TYPE_NAME); + static const auto json_type = CreateImmortal( + LogicalTypeId::VARCHAR, make_uniq(LogicalTypeInfoType::GENERIC_TYPE_INFO, JSON_TYPE_NAME)); + return json_type; } bool LogicalType::IsJSONType() const { @@ -1713,20 +1876,20 @@ bool LogicalType::IsAggregateState() const { const LogicalType &ArrayType::GetChildType(const LogicalType &type) { D_ASSERT(type.id() == LogicalTypeId::ARRAY); - auto info = type.AuxInfo(); - return info->Cast().child_type; + auto &info = type.GetTypeInfo(); + return info.Cast().child_type; } idx_t ArrayType::GetSize(const LogicalType &type) { D_ASSERT(type.id() == LogicalTypeId::ARRAY); - auto info = type.AuxInfo(); - return info->Cast().size; + auto &info = type.GetTypeInfo(); + return info.Cast().size; } bool ArrayType::IsAnySize(const LogicalType &type) { D_ASSERT(type.id() == LogicalTypeId::ARRAY); - auto info = type.AuxInfo(); - return info->Cast().size == 0; + auto &info = type.GetTypeInfo(); + return info.Cast().size == 0; } LogicalType ArrayType::ConvertToList(const LogicalType &type) { @@ -1763,13 +1926,13 @@ LogicalType ArrayType::ConvertToList(const LogicalType &type) { LogicalType LogicalType::ARRAY(const LogicalType &child, optional_idx size) { if (!size.IsValid()) { // Create an incomplete ARRAY type, used for binding - auto info = make_shared_ptr(child, 0); + auto info = make_uniq(child, uint32_t(0)); return LogicalType(LogicalTypeId::ARRAY, std::move(info)); } else { auto array_size = size.GetIndex(); D_ASSERT(array_size > 0); D_ASSERT(array_size <= ArrayType::MAX_ARRAY_SIZE); - auto info = make_shared_ptr(child, array_size); + auto info = make_uniq(child, NumericCast(array_size)); return LogicalType(LogicalTypeId::ARRAY, std::move(info)); } } @@ -1778,26 +1941,26 @@ LogicalType LogicalType::ARRAY(const LogicalType &child, optional_idx size) { // Any Type //===--------------------------------------------------------------------===// LogicalType LogicalType::ANY_PARAMS(LogicalType target, idx_t cast_score) { // NOLINT - auto type_info = make_shared_ptr(std::move(target), cast_score); + auto type_info = make_uniq(std::move(target), cast_score); return LogicalType(LogicalTypeId::ANY, std::move(type_info)); } LogicalType AnyType::GetTargetType(const LogicalType &type) { D_ASSERT(type.id() == LogicalTypeId::ANY); - auto info = type.AuxInfo(); - if (!info) { + auto &info = type.GetTypeInfo(); + if (info.type != LogicalTypeInfoType::ANY_TYPE_INFO) { return LogicalType::ANY; } - return info->Cast().target_type; + return info.Cast().target_type; } idx_t AnyType::GetCastScore(const LogicalType &type) { D_ASSERT(type.id() == LogicalTypeId::ANY); - auto info = type.AuxInfo(); - if (!info) { + auto &info = type.GetTypeInfo(); + if (info.type != LogicalTypeInfoType::ANY_TYPE_INFO) { return 5; } - return info->Cast().cast_score; + return info.Cast().cast_score; } //===--------------------------------------------------------------------===// @@ -1805,9 +1968,9 @@ idx_t AnyType::GetCastScore(const LogicalType &type) { //===--------------------------------------------------------------------===// LogicalType IntegerLiteral::GetType(const LogicalType &type) { D_ASSERT(type.id() == LogicalTypeId::INTEGER_LITERAL); - auto info = type.AuxInfo(); - D_ASSERT(info->type == ExtraTypeInfoType::INTEGER_LITERAL_TYPE_INFO); - return info->Cast().constant_value.type(); + auto &info = type.GetTypeInfo(); + D_ASSERT(info.type == LogicalTypeInfoType::INTEGER_LITERAL_TYPE_INFO); + return info.Cast().constant_value.type(); } bool IntegerLiteral::FitsInType(const LogicalType &type, const LogicalType &target) { @@ -1820,9 +1983,9 @@ bool IntegerLiteral::FitsInType(const LogicalType &type, const LogicalType &targ return false; } // we can cast to integral types if the constant value fits within that type - auto info = type.AuxInfo(); - D_ASSERT(info->type == ExtraTypeInfoType::INTEGER_LITERAL_TYPE_INFO); - auto &literal_info = info->Cast(); + auto &info = type.GetTypeInfo(); + D_ASSERT(info.type == LogicalTypeInfoType::INTEGER_LITERAL_TYPE_INFO); + auto &literal_info = info.Cast(); return literal_info.constant_value.DefaultTryCastAs(target).has_value(); } @@ -1830,7 +1993,7 @@ LogicalType LogicalType::INTEGER_LITERAL(const Value &constant) { // NOLINT if (!constant.type().IsIntegral()) { throw InternalException("INTEGER_LITERAL can only be made from literals of integer types"); } - auto type_info = make_shared_ptr(constant); + auto type_info = make_uniq(constant); return LogicalType(LogicalTypeId::INTEGER_LITERAL, std::move(type_info)); } @@ -1839,22 +2002,22 @@ LogicalType LogicalType::INTEGER_LITERAL(const Value &constant) { // NOLINT //===--------------------------------------------------------------------===// LogicalType LogicalType::TEMPLATE(const string &name) { D_ASSERT(!name.empty()); - auto type_info = make_shared_ptr(name); + auto type_info = make_uniq(name); return LogicalType(LogicalTypeId::TEMPLATE, std::move(type_info)); } const string &TemplateType::GetName(const LogicalType &type) { D_ASSERT(type.id() == LogicalTypeId::TEMPLATE); - auto info = type.AuxInfo(); - D_ASSERT(info->type == ExtraTypeInfoType::TEMPLATE_TYPE_INFO); - return info->Cast().name; + auto &info = type.GetTypeInfo(); + D_ASSERT(info.type == LogicalTypeInfoType::TEMPLATE_TYPE_INFO); + return info.Cast().name; } //===--------------------------------------------------------------------===// // Variant Type //===--------------------------------------------------------------------===// -LogicalType LogicalType::VARIANT() { +static unique_ptr CreateVariantTypeInfo() { child_list_t children; //! keys children.emplace_back("keys", LogicalType::LIST(LogicalTypeId::VARCHAR)); @@ -1868,8 +2031,12 @@ LogicalType LogicalType::VARIANT() { //! data children.emplace_back("data", LogicalType::BLOB); - auto info = make_shared_ptr(std::move(children)); - return LogicalType(LogicalTypeId::VARIANT, std::move(info)); + return make_uniq(std::move(children)); +} + +LogicalType LogicalType::VARIANT() { + static const auto variant_type = CreateImmortal(LogicalTypeId::VARIANT, CreateVariantTypeInfo()); + return variant_type; } //===--------------------------------------------------------------------===// @@ -1884,37 +2051,37 @@ LogicalType LogicalType::GEOMETRY(const string &crs) { if (crs.empty()) { return LogicalType::GEOMETRY(); } - auto info = make_shared_ptr(); + auto info = make_uniq(); info->crs = CoordinateReferenceSystem(crs); return LogicalType(LogicalTypeId::GEOMETRY, std::move(info)); } LogicalType LogicalType::GEOMETRY(const CoordinateReferenceSystem &crs) { - auto info = make_shared_ptr(); + auto info = make_uniq(); info->crs = crs; return LogicalType(LogicalTypeId::GEOMETRY, std::move(info)); } bool GeoType::HasCRS(const LogicalType &type) { D_ASSERT(type.id() == LogicalTypeId::GEOMETRY); - auto info = type.AuxInfo(); - if (!info || info->type != ExtraTypeInfoType::GEO_TYPE_INFO) { + auto &info = type.GetTypeInfo(); + if (info.type != LogicalTypeInfoType::GEO_TYPE_INFO) { // a GEOMETRY type without geo type info has no CRS - this can happen when an alias is set on a geometry // type that was created without a CRS (WithAlias attaches a generic type info) return false; } - const auto &geo_info = info->Cast(); + const auto &geo_info = info.Cast(); return geo_info.crs.GetType() != CoordinateReferenceSystemType::INVALID; } const CoordinateReferenceSystem &GeoType::GetCRS(const LogicalType &type) { D_ASSERT(type.id() == LogicalTypeId::GEOMETRY); - auto info = type.AuxInfo(); - if (!info || info->type != ExtraTypeInfoType::GEO_TYPE_INFO) { + auto &info = type.GetTypeInfo(); + if (info.type != LogicalTypeInfoType::GEO_TYPE_INFO) { throw InternalException("Geometry type has no CRS information"); } - auto &geo_info = info->Cast(); + auto &geo_info = info.Cast(); return geo_info.crs; } @@ -1925,9 +2092,9 @@ const CoordinateReferenceSystem &GeoType::GetCRS(const LogicalType &type) { const unique_ptr &UnboundType::GetTypeExpression(const LogicalType &type) { D_ASSERT(type.id() == LogicalTypeId::UNBOUND); - auto info = type.AuxInfo(); - D_ASSERT(info->type == ExtraTypeInfoType::UNBOUND_TYPE_INFO); - return info->Cast().expr; + auto &info = type.GetTypeInfo(); + D_ASSERT(info.type == LogicalTypeInfoType::UNBOUND_TYPE_INFO); + return info.Cast().expr; } LogicalType UnboundType::TryParseAndDefaultBind(const string &type_str) { @@ -1935,7 +2102,7 @@ LogicalType UnboundType::TryParseAndDefaultBind(const string &type_str) { return LogicalType::INVALID; } try { - ColumnList list = Parser::ParseColumnList("dummy " + type_str); + ColumnList list = Parser::GetBuiltinParser().ParseColumnList("dummy " + type_str); auto unbound = list.GetColumn(LogicalIndex(0)).Type(); return TryDefaultBind(unbound); } catch (const std::runtime_error &e) { @@ -1998,24 +2165,44 @@ LogicalType UnboundType::TryDefaultBind(const ParsedExpression &type_expr) { // Logical Type //===--------------------------------------------------------------------===// -// the destructor needs to know about the extra type info -LogicalType::~LogicalType() { -} - bool LogicalType::EqualTypeInfo(const LogicalType &rhs) const { - if (type_info_.get() == rhs.type_info_.get()) { + if (RefersToSameObject(type_info_, rhs.type_info_)) { return true; } - if (type_info_) { - return type_info_->Equals(rhs.type_info_.get()); - } else { - D_ASSERT(rhs.type_info_); - return rhs.type_info_->Equals(type_info_.get()); + return type_info_.get().Equals(rhs.type_info_.get()); +} + +bool LogicalType::EqualsIncludingCollation(const LogicalType &rhs) const { + if (*this != rhs) { + return false; } + vector collations; + TypeVisitor::Contains(*this, [&](const LogicalType &child) { + if (child.id() == LogicalTypeId::VARCHAR) { + collations.push_back(StringType::GetCollation(child)); + } + return false; + }); + idx_t index = 0; + auto mismatch = TypeVisitor::Contains(rhs, [&](const LogicalType &child) { + if (child.id() != LogicalTypeId::VARCHAR) { + return false; + } + if (index >= collations.size()) { + return true; + } + auto collation_mismatch = collations[index] != StringType::GetCollation(child); + index++; + return collation_mismatch; + }); + return !mismatch && index == collations.size(); } bool LogicalType::operator==(const LogicalType &rhs) const { - if (id_ != rhs.id_) { + if (RefersToSameObject(type_info_, rhs.type_info_)) { + return true; + } + if (id() != rhs.id()) { return false; } return EqualTypeInfo(rhs); diff --git a/src/duckdb/src/common/types/batched_data_collection.cpp b/src/duckdb/src/common/types/batched_data_collection.cpp index 6f38c098c..c31c8f1fa 100644 --- a/src/duckdb/src/common/types/batched_data_collection.cpp +++ b/src/duckdb/src/common/types/batched_data_collection.cpp @@ -13,17 +13,6 @@ BatchedDataCollection::BatchedDataCollection(ClientContext &context_p, vector types, - QueryResultMemoryType memory_type) - : BatchedDataCollection(context, std::move(types), - memory_type == QueryResultMemoryType::BUFFER_MANAGED - ? ColumnDataAllocatorType::BUFFER_MANAGER_ALLOCATOR - : ColumnDataAllocatorType::IN_MEMORY_ALLOCATOR, - memory_type == QueryResultMemoryType::BUFFER_MANAGED - ? ColumnDataCollectionLifetime::THROW_ERROR_AFTER_DATABASE_CLOSES - : ColumnDataCollectionLifetime::REGULAR) { -} - BatchedDataCollection::BatchedDataCollection(ClientContext &context_p, vector types_p, batch_map_t batches, ColumnDataAllocatorType allocator_type_p, ColumnDataCollectionLifetime lifetime_p) diff --git a/src/duckdb/src/common/types/bignum.cpp b/src/duckdb/src/common/types/bignum.cpp index 3bb960851..862e959ae 100644 --- a/src/duckdb/src/common/types/bignum.cpp +++ b/src/duckdb/src/common/types/bignum.cpp @@ -101,29 +101,8 @@ char Bignum::DigitToChar(int digit) { } static bool ShouldRoundDecimalDigits(const char *data, idx_t decimal_start, idx_t decimal_end) { - if (decimal_start >= decimal_end) { - return false; - } - uint64_t decimal = 0; - uint16_t decimal_digits = 0; - for (idx_t pos = decimal_start; pos < decimal_end; pos++) { - auto digit = UnsafeNumericCast(data[pos] - '0'); - if (decimal > (NumericLimits::Maximum() - digit) / 10) { - for (; pos < decimal_end; pos++) { - if (data[pos] != '0') { - return true; - } - } - break; - } - decimal_digits++; - decimal = decimal * 10 + digit; - } - while (decimal > 10) { - decimal /= 10; - decimal_digits--; - } - return decimal_digits == 1 && decimal >= 5; + // the fraction is at least 0.5 exactly when its first digit is at least 5 + return decimal_start < decimal_end && data[decimal_start] >= '5'; } static void IncrementDecimalString(string &digits) { @@ -242,49 +221,48 @@ string Bignum::EncodeBignum(const string_t &value, idx_t start_pos, idx_t end_po auto int_value_char = value.GetData(); idx_t actual_size = end_pos - start_pos; + // convert the decimal digits to base 2**32 digits (least significant first), DECIMAL_SHIFT digits at a time + vector digits; + digits.reserve(actual_size / DECIMAL_SHIFT + 1); + idx_t chunk_size = actual_size % DECIMAL_SHIFT; + if (chunk_size == 0) { + chunk_size = DECIMAL_SHIFT; + } + digit_t chunk_base = 1; + for (idx_t i = 0; i < chunk_size; i++) { + chunk_base *= 10; + } + for (idx_t pos = start_pos; pos < end_pos; pos += chunk_size, chunk_size = DECIMAL_SHIFT) { + digit_t carry = 0; + for (idx_t i = pos; i < pos + chunk_size; i++) { + carry = carry * 10 + static_cast(int_value_char[i] - '0'); + } + // digits = digits * 10**chunk_size + chunk + for (auto &digit : digits) { + twodigit_t tmp = static_cast(digit) * chunk_base + carry; + digit = static_cast(tmp); + carry = static_cast(tmp >> DIGIT_BITS); + } + if (carry) { + digits.push_back(carry); + } + chunk_base = DECIMAL_BASE; + } + // we initialize result with space for our header string result(BIGNUM_HEADER_SIZE, '0'); - unsafe_vector digits; - - // The max number a uint64_t can represent is 18.446.744.073.709.551.615 - // That has 20 digits - // In the worst case a remainder of a division will be 255, which is 3 digits - // Since the max value is 184, we need to take one more digit out - // Hence we end up with a max of 16 digits supported. - constexpr uint8_t max_digits = 16; - const idx_t number_of_digits = static_cast(std::ceil(static_cast(actual_size) / max_digits)); - - // lets convert the string to a uint64_t vector - idx_t cur_end = end_pos; - for (idx_t i = 0; i < number_of_digits; i++) { - idx_t cur_start = static_cast(start_pos) > static_cast(cur_end - max_digits) - ? start_pos - : cur_end - max_digits; - std::string current_number(int_value_char + cur_start, cur_end - cur_start); - digits.push_back(std::stoull(current_number)); - // move cur_end to more digits down the road - cur_end = cur_end - max_digits; - } - - // Now that we have our uint64_t vector, lets start our division process to figure out the new number and remainder - while (!digits.empty()) { - idx_t digit_idx = digits.size() - 1; - uint8_t remainder = 0; - idx_t digits_size = digits.size(); - for (idx_t i = 0; i < digits_size; i++) { - digits[digit_idx] += static_cast(remainder * pow(10, max_digits)); - remainder = static_cast(digits[digit_idx] % 256); - digits[digit_idx] /= 256; - if (digits[digit_idx] == 0 && digit_idx == digits.size() - 1) { - // we can cap this - digits.pop_back(); + result.reserve(BIGNUM_HEADER_SIZE + digits.size() * DIGIT_BYTES); + // write the bytes least significant first, these are reversed afterwards + for (idx_t digit_idx = 0; digit_idx < digits.size(); digit_idx++) { + auto digit = digits[digit_idx]; + for (idx_t byte_idx = 0; byte_idx < DIGIT_BYTES; byte_idx++) { + if (digit_idx + 1 == digits.size() && digit == 0) { + // skip leading zero bytes of the most significant digit + break; } - digit_idx--; - } - if (is_negative) { - result.push_back(static_cast(~remainder)); - } else { - result.push_back(static_cast(remainder)); + auto byte = static_cast(digit & 0xFF); + result.push_back(static_cast(is_negative ? ~byte : byte)); + digit >>= 8; } } std::reverse(result.begin() + BIGNUM_HEADER_SIZE, result.end()); @@ -329,35 +307,177 @@ string Bignum::FromByteArray(uint8_t *data, idx_t size, bool is_negative) { return result; } -// typos:ignore-next-line -// Following CPython and Knuth (TAOCP, Volume 2 (3rd edn), section 4.4, Method 1b). +//! Below this number of limbs, numbers are multiplied with the schoolbook algorithm +static constexpr idx_t KARATSUBA_THRESHOLD = 48; +//! Below this number of binary limbs, numbers are converted to decimal with the schoolbook algorithm +static constexpr idx_t CONVERSION_THRESHOLD = 64; + +static void TrimLimbs(vector &limbs) { + while (!limbs.empty() && limbs.back() == 0) { + limbs.pop_back(); + } +} + +// Adds the decimal limbs of b, shifted by "shift" limbs, to a +static void AddDecimalLimbs(vector &a, const digit_t *b, idx_t b_size, idx_t shift) { + while (b_size > 0 && b[b_size - 1] == 0) { + b_size--; + } + if (a.size() < shift + b_size) { + a.resize(shift + b_size, 0); + } + digit_t carry = 0; + idx_t i = 0; + for (; i < b_size; i++) { + digit_t sum = a[shift + i] + b[i] + carry; + carry = sum >= Bignum::DECIMAL_BASE; + a[shift + i] = carry ? sum - Bignum::DECIMAL_BASE : sum; + } + for (idx_t idx = shift + i; carry; idx++) { + if (idx == a.size()) { + a.push_back(0); + } + digit_t sum = a[idx] + carry; + carry = sum >= Bignum::DECIMAL_BASE; + a[idx] = carry ? sum - Bignum::DECIMAL_BASE : sum; + } +} + +// Subtracts the decimal limbs of b from a, where a >= b +static void SubtractDecimalLimbs(vector &a, const vector &b) { + digit_t borrow = 0; + idx_t i = 0; + for (; i < b.size(); i++) { + if (i >= a.size()) { + D_ASSERT(b[i] == 0); + continue; + } + digit_t subtrahend = b[i] + borrow; + borrow = a[i] < subtrahend; + a[i] = borrow ? a[i] + Bignum::DECIMAL_BASE - subtrahend : a[i] - subtrahend; + } + for (; borrow; i++) { + D_ASSERT(i < a.size()); + borrow = a[i] == 0; + a[i] = borrow ? Bignum::DECIMAL_BASE - 1 : a[i] - 1; + } +} + +// Multiplies two numbers in decimal limbs using Karatsuba multiplication +static vector MultiplyDecimalLimbs(const digit_t *a, idx_t a_size, const digit_t *b, idx_t b_size) { + if (a_size < b_size) { + std::swap(a, b); + std::swap(a_size, b_size); + } + vector result; + if (b_size == 0) { + return result; + } + result.resize(a_size + b_size, 0); + if (b_size < KARATSUBA_THRESHOLD) { + for (idx_t i = 0; i < b_size; i++) { + twodigit_t carry = 0; + for (idx_t j = 0; j < a_size; j++) { + twodigit_t current = UnsafeNumericCast(b[i]) * a[j] + result[i + j] + carry; + carry = current / Bignum::DECIMAL_BASE; + result[i + j] = static_cast(current - carry * Bignum::DECIMAL_BASE); + } + result[i + a_size] = static_cast(carry); + } + TrimLimbs(result); + return result; + } + if (2 * b_size <= a_size) { + // unbalanced: multiply b with chunks of a + for (idx_t offset = 0; offset < a_size; offset += b_size) { + auto part = MultiplyDecimalLimbs(a + offset, MinValue(b_size, a_size - offset), b, b_size); + AddDecimalLimbs(result, part.data(), part.size(), offset); + } + TrimLimbs(result); + return result; + } + // a = a1 * BASE^m + a0, b = b1 * BASE^m + b0 + idx_t m = a_size / 2; + auto z0 = MultiplyDecimalLimbs(a, m, b, m); + auto z2 = MultiplyDecimalLimbs(a + m, a_size - m, b + m, b_size - m); + vector a_sum(a, a + m); + AddDecimalLimbs(a_sum, a + m, a_size - m, 0); + vector b_sum(b, b + m); + AddDecimalLimbs(b_sum, b + m, b_size - m, 0); + // z1 = (a0 + a1) * (b0 + b1) - z0 - z2 + auto z1 = MultiplyDecimalLimbs(a_sum.data(), a_sum.size(), b_sum.data(), b_sum.size()); + SubtractDecimalLimbs(z1, z0); + SubtractDecimalLimbs(z1, z2); + AddDecimalLimbs(result, z0.data(), z0.size(), 0); + AddDecimalLimbs(result, z1.data(), z1.size(), m); + AddDecimalLimbs(result, z2.data(), z2.size(), 2 * m); + TrimLimbs(result); + return result; +} + +// Converts binary limbs (base 2^32) to decimal limbs (base 10^9), both little-endian +// powers[k] holds 2^(32 * 2^k) in decimal limbs +static vector BinaryToDecimalLimbs(const digit_t *binary, idx_t size, vector> &powers) { + vector digits; + if (size <= CONVERSION_THRESHOLD) { + // typos:ignore-next-line + // Following CPython and Knuth (TAOCP, Volume 2 (3rd edn), section 4.4, Method 1b). + for (idx_t i = size; i > 0; i--) { + digit_t hi = binary[i - 1]; + for (idx_t j = 0; j < digits.size(); j++) { + twodigit_t tmp = UnsafeNumericCast(digits[j]) << Bignum::DIGIT_BITS | hi; + hi = static_cast(tmp / UnsafeNumericCast(Bignum::DECIMAL_BASE)); + digits[j] = static_cast(tmp - UnsafeNumericCast(Bignum::DECIMAL_BASE * hi)); + } + while (hi) { + digits.push_back(hi % Bignum::DECIMAL_BASE); + hi /= Bignum::DECIMAL_BASE; + } + } + return digits; + } + // split the number into high * 2^(32 * m) + low, where m is a power of two + idx_t k = 0; + idx_t m = 1; + while (2 * m < size) { + m *= 2; + k++; + } + auto low = BinaryToDecimalLimbs(binary, m, powers); + auto high = BinaryToDecimalLimbs(binary + m, size - m, powers); + while (powers.size() <= k) { + auto &last = powers.back(); + powers.push_back(MultiplyDecimalLimbs(last.data(), last.size(), last.data(), last.size())); + } + auto &power = powers[k]; + digits = MultiplyDecimalLimbs(high.data(), high.size(), power.data(), power.size()); + AddDecimalLimbs(digits, low.data(), low.size(), 0); + TrimLimbs(digits); + return digits; +} + string Bignum::BignumToVarchar(const bignum_t &blob) { string decimal_string; vector byte_array; bool is_negative; GetByteArray(byte_array, is_negative, blob.data); - vector digits; // Rounding byte_array to digit_bytes multiple size, so that we can process every digit_bytes bytes // at a time without if check in the for loop idx_t padding_size = (-byte_array.size()) & (DIGIT_BYTES - 1); byte_array.insert(byte_array.begin(), padding_size, 0); - for (idx_t i = 0; i < byte_array.size(); i += DIGIT_BYTES) { - digit_t hi = 0; + idx_t limb_count = byte_array.size() / DIGIT_BYTES; + vector binary(limb_count); + for (idx_t i = 0; i < limb_count; i++) { + digit_t limb = 0; for (idx_t j = 0; j < DIGIT_BYTES; j++) { - hi |= UnsafeNumericCast(byte_array[i + j]) << (8 * (DIGIT_BYTES - j - 1)); - } - - for (idx_t j = 0; j < digits.size(); j++) { - twodigit_t tmp = UnsafeNumericCast(digits[j]) << DIGIT_BITS | hi; - hi = static_cast(tmp / UnsafeNumericCast(DECIMAL_BASE)); - digits[j] = static_cast(tmp - UnsafeNumericCast(DECIMAL_BASE * hi)); - } - - while (hi) { - digits.push_back(hi % DECIMAL_BASE); - hi /= DECIMAL_BASE; + limb |= UnsafeNumericCast(byte_array[i * DIGIT_BYTES + j]) << (8 * (DIGIT_BYTES - j - 1)); } + binary[limb_count - i - 1] = limb; } + TrimLimbs(binary); + // 2^32 in decimal limbs + vector> powers {{294967296, 4}}; + auto digits = BinaryToDecimalLimbs(binary.data(), binary.size(), powers); if (digits.empty()) { digits.push_back(0); @@ -417,11 +537,8 @@ bool Bignum::BignumToDouble(const bignum_t &blob, double &result, bool &strict) if (is_negative) { result *= -1; } - if (!std::isfinite(result)) { - // We throw an error - throw ConversionException("Could not convert bignum '%s' to Double", BignumToVarchar(blob)); - } - return true; + // the value does not fit in a double + return std::isfinite(result); } } // namespace duckdb diff --git a/src/duckdb/src/common/types/column/column_data_allocator.cpp b/src/duckdb/src/common/types/column/column_data_allocator.cpp index 3560e9240..377a66c3f 100644 --- a/src/duckdb/src/common/types/column/column_data_allocator.cpp +++ b/src/duckdb/src/common/types/column/column_data_allocator.cpp @@ -80,14 +80,15 @@ ColumnDataAllocator::~ColumnDataAllocator() { BufferHandle ColumnDataAllocator::Pin(uint32_t block_id) { D_ASSERT(type == ColumnDataAllocatorType::BUFFER_MANAGER_ALLOCATOR || type == ColumnDataAllocatorType::HYBRID); + if (!shared) { + return blocks[block_id].Pin(*alloc.buffer_manager); + } shared_ptr handle; - if (shared) { + { // we only need to grab the lock when accessing the vector, because vector access is not thread-safe: // the vector can be resized by another thread while we try to access it lock_guard guard(lock); handle = blocks[block_id].GetHandle(); - } else { - handle = blocks[block_id].GetHandle(); } return alloc.buffer_manager->Pin(handle); } @@ -152,8 +153,7 @@ void ColumnDataAllocator::AllocateBuffer(idx_t size, uint32_t &block_id, uint32_ block_id = NumericCast(blocks.size() - 1); if (chunk_state && chunk_state->handles.find(block_id) == chunk_state->handles.end()) { // not guaranteed to be pinned already by this thread (if shared allocator) - auto handle = blocks[block_id].GetHandle(); - chunk_state->handles[block_id] = alloc.buffer_manager->Pin(handle); + chunk_state->handles[block_id] = block.Pin(*alloc.buffer_manager); } offset = block.size; block.size += size; @@ -255,10 +255,13 @@ void ColumnDataAllocator::UnswizzlePointers(ChunkManagementState &state, Vector SwizzleMetaData &swizzle_segment, const VectorMetaData &string_heap_segment, const idx_t &v_offset, const bool &copied) { D_ASSERT(result.GetType().InternalType() == PhysicalType::VARCHAR); - lock_guard guard(lock); - const auto old_base_ptr = char_ptr_cast(swizzle_segment.ptr); const auto new_base_ptr = char_ptr_cast(GetDataPointer(state, string_heap_segment.block_id, string_heap_segment.offset)); + if (!copied && swizzle_segment.ptr.load(std::memory_order_acquire) == data_ptr_cast(new_base_ptr)) { + return; + } + lock_guard guard(lock); + const auto old_base_ptr = char_ptr_cast(swizzle_segment.ptr.load(std::memory_order_relaxed)); if (old_base_ptr == new_base_ptr) { return; // pointers are still valid } @@ -286,7 +289,7 @@ void ColumnDataAllocator::UnswizzlePointers(ChunkManagementState &state, Vector if (!copied) { // if the data was not copied, we modified data on the blocks. store the new base ptr - swizzle_segment.ptr = data_ptr_cast(new_base_ptr); + swizzle_segment.ptr.store(data_ptr_cast(new_base_ptr), std::memory_order_release); } } @@ -360,6 +363,14 @@ shared_ptr BlockMetaData::GetHandle() const { return res; } +BufferHandle BlockMetaData::Pin(BufferManager &buffer_manager) { + if (handle) { + return buffer_manager.Pin(handle); + } + auto owned_handle = GetHandle(); + return buffer_manager.Pin(owned_handle); +} + void BlockMetaData::SetHandle(ManagedResultSet &managed_result_set, shared_ptr handle_p) { if (managed_result_set.IsValid()) { managed_result_set.GetHandles().emplace_back(handle_p); diff --git a/src/duckdb/src/common/types/column/column_data_collection.cpp b/src/duckdb/src/common/types/column/column_data_collection.cpp index 7c1afdaa6..e17de0f66 100644 --- a/src/duckdb/src/common/types/column/column_data_collection.cpp +++ b/src/duckdb/src/common/types/column/column_data_collection.cpp @@ -81,13 +81,11 @@ ColumnDataCollection::ColumnDataCollection(shared_ptr alloc ColumnDataCollection::ColumnDataCollection(ClientContext &context, vector types_p, ColumnDataAllocatorType type, ColumnDataCollectionLifetime lifetime) : ColumnDataCollection(make_shared_ptr(context, type, lifetime), std::move(types_p)) { - D_ASSERT(!types.empty()); } ColumnDataCollection::ColumnDataCollection(ColumnDataCollection &other) : ColumnDataCollection(other.allocator, other.types) { other.finished_append = true; - D_ASSERT(!types.empty()); } ColumnDataCollection::~ColumnDataCollection() { @@ -97,7 +95,6 @@ void ColumnDataCollection::Initialize(vector types_p) { this->types = std::move(types_p); this->count = 0; this->finished_append = false; - D_ASSERT(!types.empty()); copy_functions.reserve(types.size()); for (auto &type : types) { copy_functions.push_back(GetCopyFunction(type)); @@ -1092,6 +1089,14 @@ void ColumnDataCollection::Append(DataChunk &input) { Append(state, input); } +void ColumnDataCollection::Append(const ColumnDataCollection &other) { + ColumnDataAppendState state; + InitializeAppend(state); + for (auto &chunk : other.Chunks()) { + Append(state, chunk); + } +} + //===--------------------------------------------------------------------===// // Scan //===--------------------------------------------------------------------===// @@ -1153,7 +1158,6 @@ void ColumnDataCollection::InitializeScanChunk(Allocator &allocator, DataChunk & } void ColumnDataCollection::InitializeScanChunk(ColumnDataScanState &state, DataChunk &chunk) const { - D_ASSERT(!state.column_ids.empty()); vector chunk_types; chunk_types.reserve(state.column_ids.size()); for (idx_t i = 0; i < state.column_ids.size(); i++) { @@ -1283,6 +1287,10 @@ ColumnDataRowCollection ColumnDataCollection::GetRows() const { return ColumnDataRowCollection(*this); } +Value ColumnDataCollection::GetValue(idx_t column, idx_t index) const { + return GetRows().GetValue(column, index); +} + //===--------------------------------------------------------------------===// // Combine //===--------------------------------------------------------------------===// diff --git a/src/duckdb/src/common/types/column/column_data_consumer.cpp b/src/duckdb/src/common/types/column/column_data_consumer.cpp index fa20f1d5b..7cdab1ecb 100644 --- a/src/duckdb/src/common/types/column/column_data_consumer.cpp +++ b/src/duckdb/src/common/types/column/column_data_consumer.cpp @@ -38,7 +38,6 @@ bool ColumnDataConsumer::AssignChunk(ColumnDataConsumerScanState &state) { lock_guard guard(lock); if (current_chunk_index == chunk_count) { // All chunks have been assigned - state.current_chunk_state.handles.clear(); state.chunk_index = DConstants::INVALID_INDEX; return false; } diff --git a/src/duckdb/src/common/types/conflict_manager.cpp b/src/duckdb/src/common/types/conflict_manager.cpp index c758d23dc..8c9c88bc1 100644 --- a/src/duckdb/src/common/types/conflict_manager.cpp +++ b/src/duckdb/src/common/types/conflict_manager.cpp @@ -43,7 +43,23 @@ bool ConflictManager::IsConflict(LookupResultType type) { } } -bool ConflictManager::AddHit(const idx_t index_in_chunk, const std::function &callback) { +void ConflictManager::AddRowId(const idx_t index_in_chunk, const row_t row_id) { + // Only ON CONFLICT DO NOTHING can have multiple conflict targets, + // which can cause multiple row IDs per index_in_chunk. + // We ignore later row IDs, as we don't need them. + auto &data = GetConflictData(FIRST); + if (!data.validity.RowIsValid(index_in_chunk)) { + data.Insert(index_in_chunk, row_id); + } +} + +void ConflictManager::AddSecondRowId(const idx_t index_in_chunk, const row_t row_id) { + D_ASSERT(HasConflicts()); + D_ASSERT(conflict_data[FIRST].validity.RowIsValid(index_in_chunk)); + GetConflictData(SECOND).Insert(index_in_chunk, row_id); +} + +bool ConflictManager::AddHitInternal(const idx_t index_in_chunk, const row_t row_id, const bool second) { D_ASSERT(index_in_chunk < chunk_size); if (ShouldThrow(index_in_chunk)) { return true; @@ -63,16 +79,20 @@ bool ConflictManager::AddHit(const idx_t index_in_chunk, const std::function &type D_ASSERT(data.empty()); for (idx_t i = 0; i < types.size(); i++) { - // We copy the type here so we don't create another reference to the same shared_ptr + // We copy the type here so we don't create another reference to the same LogicalTypeInfo // Otherwise, threads will constantly increment/decrement the atomic ref count to the same shared_ptr // This is necessary to avoid heavy contention on the atomic on many-core machines // Note that for nested types, there will still be contention on the atomic(s) one level down, - // because this is a shallow copy (only copies ExtraTypeInfo to depth=1) + // because this is a shallow copy (only copies LogicalTypeInfo to depth=1) auto copied_type = types[i].Copy(); if (!initialize[i]) { data.emplace_back(copied_type, nullptr); @@ -88,6 +88,17 @@ idx_t DataChunk::DeriveSize() const { "DataChunk::size() called but neither count was set, nor any vectors with valid counts were set"); } +idx_t DataChunk::GetCapacity() const { + for (auto &cache : vector_caches) { + auto capacity = cache.GetCapacity(); + if (capacity.IsValid()) { + return capacity.GetIndex(); + } + } + // a chunk that was not initialized with its own vectors holds at most a standard vector's worth of rows + return STANDARD_VECTOR_SIZE; +} + idx_t DataChunk::GetDataSize() const { idx_t total_size = 0; for (auto &vec : data) { diff --git a/src/duckdb/src/common/types/date.cpp b/src/duckdb/src/common/types/date.cpp index 4520ad4af..61e3965e4 100644 --- a/src/duckdb/src/common/types/date.cpp +++ b/src/duckdb/src/common/types/date.cpp @@ -615,7 +615,16 @@ int32_t Date::ExtractWeekNumberRegular(date_t date, bool monday_first) { // Returns the date of the monday of the current week. date_t Date::GetMondayOfCurrentWeek(date_t date) { int32_t dotw = Date::ExtractISODayOfTheWeek(date); - return date - (dotw - 1); + return Date::SubtractDays(date, dotw - 1); +} + +date_t Date::SubtractDays(date_t date, int64_t days) { + static const auto min_days = Date::FromDate(DATE_MIN_YEAR, DATE_MIN_MONTH, DATE_MIN_DAY).days; + const auto result = int64_t(date.days) - days; + if (result < min_days || result > NumericLimits::Maximum() - 1) { + throw OutOfRangeException("Date out of range: subtracting %d days from %s", days, Date::ToString(date)); + } + return date_t(int32_t(result)); } } // namespace duckdb diff --git a/src/duckdb/src/common/types/geometry.cpp b/src/duckdb/src/common/types/geometry.cpp index 7ecbf2718..2e7539f77 100644 --- a/src/duckdb/src/common/types/geometry.cpp +++ b/src/duckdb/src/common/types/geometry.cpp @@ -267,38 +267,38 @@ class TextReader { return false; // not matched } - void Match(const char *str) { - if (!TryMatch(str)) { - // Check if this would go EOF - if (pos + strlen(str) >= end) { - throw MakeError("Expected '%s' but got end of input", str); - } - - throw MakeError("Expected '%s' but got '%c'", str, *pos); + bool Match(const char *str) { + if (TryMatch(str)) { + return true; } + // Check if this would go EOF + if (pos + strlen(str) >= end) { + return SetError("Expected '%s' but got end of input", str); + } + return SetError("Expected '%s' but got '%c'", str, *pos); } - void Match(char c) { - if (!TryMatch(c)) { - if (pos >= end) { - throw MakeError("Expected '%c' but got end of input", c); - } - throw MakeError("Expected '%c' but got '%c'", c, *pos); + bool Match(char c) { + if (TryMatch(c)) { + return true; } + if (pos >= end) { + return SetError("Expected '%c' but got end of input", c); + } + return SetError("Expected '%c' but got '%c'", c, *pos); } - double MatchNumber() { + bool MatchNumber(double &num) { // Now use fast_float to parse the number - double num; const auto res = duckdb_fast_float::from_chars(pos, end, num); if (res.ec != std::errc()) { - throw MakeError("Expected number"); + return SetError("Expected number"); } pos = res.ptr; // update position to the end of the parsed number SkipWhitespace(); // remove trailing whitespace - return num; // return the parsed number + return true; } idx_t GetPosition() const { @@ -314,18 +314,25 @@ class TextReader { pos = beg; } + //! Records a parse error at the current position, always returns false template - InvalidInputException MakeError(const char *raw_msg, ARGS... args) const { - const auto byte_offset = UnsafeNumericCast(pos - beg); - auto msg = StringUtil::Format("Failed to parse geometry: %s at offset %lu", - StringUtil::Format(raw_msg, args...), byte_offset); + bool SetError(const char *raw_msg, ARGS... args) { + error_message = StringUtil::Format(raw_msg, args...); + error_offset = UnsafeNumericCast(pos - beg); + return false; + } + + string GetErrorMessage() const { + return StringUtil::Format("Failed to parse geometry: %s at offset %lu", error_message, error_offset); + } + + InvalidInputException MakeError() const { if (query_location.IsValid()) { // point at the specific byte within the WKT literal where parsing failed - const QueryLocation expr_location(query_location.Start() + byte_offset, 0); - return InvalidInputException(Exception::InitializeExtraInfo(expr_location), msg); - } else { - return InvalidInputException(msg); + const QueryLocation expr_location(query_location.Start() + error_offset, 0); + return InvalidInputException(Exception::InitializeExtraInfo(expr_location), GetErrorMessage()); } + return InvalidInputException(GetErrorMessage()); } void SetQueryLocation(QueryLocation location) { @@ -343,11 +350,14 @@ class TextReader { const char *pos; const char *end; QueryLocation query_location; + string error_message; + idx_t error_offset = 0; }; -void FromStringRecursive(TextReader &reader, BlobWriter &writer, uint32_t depth, bool parent_has_z, bool parent_has_m) { +//! Returns false if the text is not valid WKT, the error is recorded in the reader +bool FromStringRecursive(TextReader &reader, BlobWriter &writer, uint32_t depth, bool parent_has_z, bool parent_has_m) { if (depth == Geometry::MAX_RECURSION_DEPTH) { - throw reader.MakeError("Geometry string exceeds maximum recursion depth of %d", Geometry::MAX_RECURSION_DEPTH); + return reader.SetError("Geometry string exceeds maximum recursion depth of %d", Geometry::MAX_RECURSION_DEPTH); } // Skip leading whitespace @@ -355,9 +365,16 @@ void FromStringRecursive(TextReader &reader, BlobWriter &writer, uint32_t depth, // EWKT dialect (ignore SRID if present) if (reader.TryMatch("SRID")) { - reader.Match('='); - reader.MatchNumber(); - reader.Match(';'); + if (!reader.Match('=')) { + return false; + } + double srid; + if (!reader.MatchNumber(srid)) { + return false; + } + if (!reader.Match(';')) { + return false; + } } GeometryType type = GeometryType::INVALID; @@ -377,7 +394,7 @@ void FromStringRecursive(TextReader &reader, BlobWriter &writer, uint32_t depth, } else if (reader.TryMatch("geometrycollection")) { type = GeometryType::GEOMETRYCOLLECTION; } else { - throw reader.MakeError("Unknown geometry type"); + return reader.SetError("Unknown geometry type"); } const auto has_z = reader.TryMatch("z"); @@ -386,7 +403,7 @@ void FromStringRecursive(TextReader &reader, BlobWriter &writer, uint32_t depth, const auto is_empty = reader.TryMatch("empty"); if ((depth != 0) && ((parent_has_z != has_z) || (parent_has_m != has_m))) { - throw reader.MakeError("Geometry has inconsistent Z/M dimensions"); + return reader.SetError("Geometry has inconsistent Z/M dimensions"); } // How many dimensions does this geometry have? @@ -406,12 +423,19 @@ void FromStringRecursive(TextReader &reader, BlobWriter &writer, uint32_t depth, writer.Write(std::numeric_limits::quiet_NaN()); } } else { - reader.Match('('); + if (!reader.Match('(')) { + return false; + } for (uint32_t d_idx = 0; d_idx < dims; d_idx++) { - auto value = reader.MatchNumber(); + double value; + if (!reader.MatchNumber(value)) { + return false; + } writer.Write(value); } - reader.Match(')'); + if (!reader.Match(')')) { + return false; + } } } break; case GeometryType::LINESTRING: { @@ -420,15 +444,22 @@ void FromStringRecursive(TextReader &reader, BlobWriter &writer, uint32_t depth, break; } auto vert_count = writer.Reserve(); - reader.Match('('); + if (!reader.Match('(')) { + return false; + } do { for (uint32_t d_idx = 0; d_idx < dims; d_idx++) { - auto value = reader.MatchNumber(); + double value; + if (!reader.MatchNumber(value)) { + return false; + } writer.Write(value); } vert_count.value++; } while (reader.TryMatch(',')); - reader.Match(')'); + if (!reader.Match(')')) { + return false; + } writer.Write(vert_count); } break; case GeometryType::POLYGON: { @@ -437,22 +468,33 @@ void FromStringRecursive(TextReader &reader, BlobWriter &writer, uint32_t depth, break; // No rings in empty polygon } auto ring_count = writer.Reserve(); - reader.Match('('); + if (!reader.Match('(')) { + return false; + } do { auto vert_count = writer.Reserve(); - reader.Match('('); + if (!reader.Match('(')) { + return false; + } do { for (uint32_t d_idx = 0; d_idx < dims; d_idx++) { - auto value = reader.MatchNumber(); + double value; + if (!reader.MatchNumber(value)) { + return false; + } writer.Write(value); } vert_count.value++; } while (reader.TryMatch(',')); - reader.Match(')'); + if (!reader.Match(')')) { + return false; + } writer.Write(vert_count); ring_count.value++; } while (reader.TryMatch(',')); - reader.Match(')'); + if (!reader.Match(')')) { + return false; + } writer.Write(ring_count); } break; case GeometryType::MULTIPOINT: { @@ -461,7 +503,9 @@ void FromStringRecursive(TextReader &reader, BlobWriter &writer, uint32_t depth, break; } auto part_count = writer.Reserve(); - reader.Match('('); + if (!reader.Match('(')) { + return false; + } do { bool has_paren = reader.TryMatch('('); @@ -476,25 +520,34 @@ void FromStringRecursive(TextReader &reader, BlobWriter &writer, uint32_t depth, } } else { for (uint32_t d_idx = 0; d_idx < dims; d_idx++) { - auto value = reader.MatchNumber(); + double value; + if (!reader.MatchNumber(value)) { + return false; + } writer.Write(value); } } if (has_paren) { - reader.Match(')'); // Match the closing parenthesis if it was opened + if (!reader.Match(')')) { // Match the closing parenthesis if it was opened + return false; + } } part_count.value++; } while (reader.TryMatch(',')); - reader.Match(')'); + if (!reader.Match(')')) { + return false; + } writer.Write(part_count); } break; case GeometryType::MULTILINESTRING: { if (is_empty) { writer.Write(0); - return; // No linestrings in empty multilinestring + return true; // No linestrings in empty multilinestring } auto part_count = writer.Reserve(); - reader.Match('('); + if (!reader.Match('(')) { + return false; + } do { const auto part_meta = static_cast(GeometryType::LINESTRING) + (has_z ? 1000 : 0) + (has_m ? 2000 : 0); @@ -506,20 +559,29 @@ void FromStringRecursive(TextReader &reader, BlobWriter &writer, uint32_t depth, part_count.value++; } else { auto vert_count = writer.Reserve(); - reader.Match('('); + if (!reader.Match('(')) { + return false; + } do { for (uint32_t d_idx = 0; d_idx < dims; d_idx++) { - auto value = reader.MatchNumber(); + double value; + if (!reader.MatchNumber(value)) { + return false; + } writer.Write(value); } vert_count.value++; } while (reader.TryMatch(',')); - reader.Match(')'); + if (!reader.Match(')')) { + return false; + } writer.Write(vert_count); part_count.value++; } } while (reader.TryMatch(',')); - reader.Match(')'); + if (!reader.Match(')')) { + return false; + } writer.Write(part_count); } break; case GeometryType::MULTIPOLYGON: { @@ -528,7 +590,9 @@ void FromStringRecursive(TextReader &reader, BlobWriter &writer, uint32_t depth, break; } auto part_count = writer.Reserve(); - reader.Match('('); + if (!reader.Match('(')) { + return false; + } do { const auto part_meta = static_cast(GeometryType::POLYGON) + (has_z ? 1000 : 0) + (has_m ? 2000 : 0); @@ -540,27 +604,40 @@ void FromStringRecursive(TextReader &reader, BlobWriter &writer, uint32_t depth, part_count.value++; } else { auto ring_count = writer.Reserve(); - reader.Match('('); + if (!reader.Match('(')) { + return false; + } do { auto vert_count = writer.Reserve(); - reader.Match('('); + if (!reader.Match('(')) { + return false; + } do { for (uint32_t d_idx = 0; d_idx < dims; d_idx++) { - auto value = reader.MatchNumber(); + double value; + if (!reader.MatchNumber(value)) { + return false; + } writer.Write(value); } vert_count.value++; } while (reader.TryMatch(',')); - reader.Match(')'); + if (!reader.Match(')')) { + return false; + } writer.Write(vert_count); ring_count.value++; } while (reader.TryMatch(',')); - reader.Match(')'); + if (!reader.Match(')')) { + return false; + } writer.Write(ring_count); part_count.value++; } } while (reader.TryMatch(',')); - reader.Match(')'); + if (!reader.Match(')')) { + return false; + } writer.Write(part_count); } break; case GeometryType::GEOMETRYCOLLECTION: { @@ -569,18 +646,25 @@ void FromStringRecursive(TextReader &reader, BlobWriter &writer, uint32_t depth, break; } auto part_count = writer.Reserve(); - reader.Match('('); + if (!reader.Match('(')) { + return false; + } do { // Recursively parse the geometry inside the collection - FromStringRecursive(reader, writer, depth + 1, has_z, has_m); + if (!FromStringRecursive(reader, writer, depth + 1, has_z, has_m)) { + return false; + } part_count.value++; } while (reader.TryMatch(',')); - reader.Match(')'); + if (!reader.Match(')')) { + return false; + } writer.Write(part_count); } break; default: - throw reader.MakeError("Unknown geometry type %d", static_cast(type)); + return reader.SetError("Unknown geometry type %d", static_cast(type)); } + return true; } void ToStringRecursive(BlobReader &reader, TextWriter &writer, idx_t depth, bool parent_has_z, bool parent_has_m) { @@ -895,17 +979,48 @@ struct WKBAnalysis { bool any_m = false; bool any_unknown = false; bool any_ewkb = false; + bool has_trailing_data = false; +}; + +//! A collection whose parts are being analyzed +struct WKBAnalysisFrame { + //! The number of parts that remain to be analyzed + uint32_t parts_remaining; + //! The type the parts must have, or 0 if they can have any type + uint32_t part_type; }; WKBAnalysis AnalyzeWKB(BlobReader &reader) { WKBAnalysis result; - - while (!reader.IsAtEnd()) { + // Collection children are complete WKB objects embedded in the root object. + vector collections; + bool is_root = true; + bool root_has_z = false; + bool root_has_m = false; + + while (is_root || !collections.empty()) { + uint32_t required_type = 0; + if (!is_root) { + auto &collection = collections.back(); + if (collection.parts_remaining == 0) { + collections.pop_back(); + continue; + } + collection.parts_remaining--; + required_type = collection.part_type; + if (collections.size() >= Geometry::MAX_RECURSION_DEPTH) { + throw InvalidInputException("Geometry exceeds maximum recursion depth of %d", + Geometry::MAX_RECURSION_DEPTH); + } + } const auto le = reader.Read() == 1; const auto meta = reader.Read(le); const auto type_id = (meta & 0x0000FFFF) % 1000; const auto flag_id = (meta & 0x0000FFFF) / 1000; + if (flag_id > 3) { + throw InvalidInputException("Unsupported geometry flag %d in WKB", flag_id); + } // Extended WKB detection const auto has_extz = (meta & 0x80000000) != 0; @@ -915,6 +1030,21 @@ WKBAnalysis AnalyzeWKB(BlobReader &reader) { const auto has_z = ((flag_id & 0x01) != 0) || has_extz; const auto has_m = ((flag_id & 0x02) != 0) || has_extm; + if (is_root) { + root_has_z = has_z; + root_has_m = has_m; + is_root = false; + } else { + if (has_z != root_has_z || has_m != root_has_m) { + throw InvalidInputException("Geometry has inconsistent Z/M dimensions, starting at position %zu", + reader.GetPosition()); + } + if (required_type != 0 && type_id != required_type) { + throw InvalidInputException("Unexpected geometry type %d in a collection of type %d at position %zu", + type_id, required_type + 3, reader.GetPosition()); + } + } + if (has_srid) { result.any_ewkb = true; reader.Skip(sizeof(uint32_t)); // Skip SRID @@ -957,7 +1087,9 @@ WKBAnalysis AnalyzeWKB(BlobReader &reader) { case 5: // MULTILINESTRING case 6: // MULTIPOLYGON case 7: { // GEOMETRYCOLLECTION - reader.Skip(sizeof(uint32_t)); + const auto part_count = reader.Read(le); + // MULTIPOINT, MULTILINESTRING and MULTIPOLYGON contain POINT, LINESTRING and POLYGON parts + collections.push_back({part_count, type_id == 7 ? 0 : type_id - 3}); result.size += sizeof(uint32_t); // part count } break; default: { @@ -966,6 +1098,7 @@ WKBAnalysis AnalyzeWKB(BlobReader &reader) { } } } + result.has_trailing_data = !reader.IsAtEnd(); return result; } @@ -1051,7 +1184,23 @@ constexpr const idx_t Geometry::MAX_RECURSION_DEPTH; bool Geometry::FromBinary(const string_t &wkb, string_t &result, StringHeap &heap, bool strict) { BlobReader reader(wkb.GetData(), static_cast(wkb.GetSize())); - const auto analysis = AnalyzeWKB(reader); + WKBAnalysis analysis; + try { + analysis = AnalyzeWKB(reader); + } catch (InvalidInputException &) { + // Truncated input (e.g. a collection declaring more children than are present) must + // uphold the non-strict contract: report failure instead of throwing. + if (strict) { + throw; + } + return false; + } + if (analysis.has_trailing_data) { + if (strict) { + throw InvalidInputException("Unexpected trailing data at position %zu", reader.GetPosition()); + } + return false; + } if (analysis.any_unknown) { if (strict) { throw InvalidInputException("Unsupported geometry type in WKB"); @@ -1103,27 +1252,47 @@ void Geometry::ToBinary(const Vector &source, Vector &result) { result.Reinterpret(source); } -bool Geometry::FromString(const string_t &wkt_text, string_t &result, StringHeap &heap, bool strict, - QueryLocation query_location) { - TextReader reader(wkt_text.GetData(), static_cast(wkt_text.GetSize())); - reader.SetQueryLocation(query_location); +//! Returns false if the text is not valid WKT, the error is recorded in the reader +static bool TryParseWKT(TextReader &reader, string_t &result, StringHeap &heap, bool &trailing_text) { BlobWriter writer; - - FromStringRecursive(reader, writer, 0, false, false); - + trailing_text = false; + if (!FromStringRecursive(reader, writer, 0, false, false)) { + return false; + } // Check whether reader has consumed over all meaningful characters. if (!reader.IsAtEnd()) { - if (strict) { - throw reader.MakeError("Unexpected trailing text"); - } - return false; + trailing_text = true; + return reader.SetError("Unexpected trailing text"); } - const auto &buffer = writer.GetBuffer(); result = heap.AddBlob(buffer.data(), buffer.size()); return true; } +bool Geometry::TryFromString(const string_t &wkt_text, string_t &result, StringHeap &heap, string &error_message) { + TextReader reader(wkt_text.GetData(), static_cast(wkt_text.GetSize())); + bool trailing_text; + if (!TryParseWKT(reader, result, heap, trailing_text)) { + error_message = reader.GetErrorMessage(); + return false; + } + return true; +} + +bool Geometry::FromString(const string_t &wkt_text, string_t &result, StringHeap &heap, bool strict, + QueryLocation query_location) { + TextReader reader(wkt_text.GetData(), static_cast(wkt_text.GetSize())); + reader.SetQueryLocation(query_location); + bool trailing_text; + if (TryParseWKT(reader, result, heap, trailing_text)) { + return true; + } + if (trailing_text && !strict) { + return false; + } + throw reader.MakeError(); +} + bool Geometry::FromString(const string_t &wkt_text, string_t &result, Vector &result_vector, bool strict) { return FromString(wkt_text, result, StringVector::GetStringHeap(result_vector), strict, optional_idx::Invalid()); } diff --git a/src/duckdb/src/common/types/hugeint.cpp b/src/duckdb/src/common/types/hugeint.cpp index 79a353a6e..b98611ddb 100644 --- a/src/duckdb/src/common/types/hugeint.cpp +++ b/src/duckdb/src/common/types/hugeint.cpp @@ -89,42 +89,6 @@ hugeint_t Hugeint::Abs(hugeint_t n) { // Divide //===--------------------------------------------------------------------===// -static uint8_t PositiveHugeintHighestBit(hugeint_t bits) { - uint8_t out = 0; - if (bits.upper) { - out = 64; - uint64_t up = static_cast(bits.upper); - while (up) { - up >>= 1; - out++; - } - } else { - uint64_t low = bits.lower; - while (low) { - low >>= 1; - out++; - } - } - return out; -} - -static bool PositiveHugeintIsBitSet(hugeint_t lhs, uint8_t bit_position) { - if (bit_position < 64) { - return lhs.lower & (uint64_t(1) << uint64_t(bit_position)); - } else { - return static_cast(lhs.upper) & (uint64_t(1) << uint64_t(bit_position - 64)); - } -} - -static hugeint_t PositiveHugeintLeftShift(hugeint_t lhs, uint32_t amount) { - D_ASSERT(amount > 0 && amount < 64); - hugeint_t result; - result.lower = lhs.lower << amount; - result.upper = - UnsafeNumericCast((UnsafeNumericCast(lhs.upper) << amount) + (lhs.lower >> (64 - amount))); - return result; -} - hugeint_t Hugeint::DivModPositive(hugeint_t lhs, uint64_t rhs, uint64_t &remainder) { D_ASSERT(lhs.upper >= 0); // DivMod code adapted from: @@ -136,14 +100,14 @@ hugeint_t Hugeint::DivModPositive(hugeint_t lhs, uint64_t rhs, uint64_t &remaind div_result.upper = 0; remainder = 0; - uint8_t highest_bit_set = PositiveHugeintHighestBit(lhs); + uint8_t highest_bit_set = HugeintHighestBit(lhs); // now iterate over the amount of bits that are set in the LHS for (uint8_t x = highest_bit_set; x > 0; x--) { // left-shift the current result and remainder by 1 - div_result = PositiveHugeintLeftShift(div_result, 1); + div_result = HugeintLeftShift(div_result, 1); remainder <<= 1; // we get the value of the bit at position X, where position 0 is the least-significant bit - if (PositiveHugeintIsBitSet(lhs, x - 1)) { + if (HugeintIsBitSet(lhs, x - 1)) { // increment the remainder remainder++; } @@ -303,172 +267,18 @@ bool Hugeint::TryMultiply(hugeint_t lhs, hugeint_t rhs, hugeint_t &result) { // Multiply without overflow check template <> hugeint_t Hugeint::Multiply(hugeint_t lhs, hugeint_t rhs) { - hugeint_t result; - bool lhs_negative = lhs.upper < 0; - bool rhs_negative = rhs.upper < 0; - if (lhs_negative) { - NegateInPlace(lhs); - } - if (rhs_negative) { - NegateInPlace(rhs); - } - -#if ((__GNUC__ >= 5) || defined(__clang__)) && defined(__SIZEOF_INT128__) - __uint128_t left = __uint128_t(lhs.lower) + (__uint128_t(lhs.upper) << 64); - __uint128_t right = __uint128_t(rhs.lower) + (__uint128_t(rhs.upper) << 64); - __uint128_t result_i128; - result_i128 = left * right; - uint64_t upper = uint64_t(result_i128 >> 64); - result.upper = int64_t(upper); - result.lower = uint64_t(result_i128 & 0xffffffffffffffff); -#else - // Multiply code adapted from: - // https://github.com/calccrypto/uint128_t/blob/master/uint128_t.cpp - - // split values into 4 32-bit parts - uint64_t top[4] = {uint64_t(lhs.upper) >> 32, uint64_t(lhs.upper) & 0xffffffff, lhs.lower >> 32, - lhs.lower & 0xffffffff}; - uint64_t bottom[4] = {uint64_t(rhs.upper) >> 32, uint64_t(rhs.upper) & 0xffffffff, rhs.lower >> 32, - rhs.lower & 0xffffffff}; - uint64_t products[4][4]; - - // multiply each component of the values - for (auto x = 0; x < 4; x++) { - for (auto y = 0; y < 4; y++) { - products[x][y] = top[x] * bottom[y]; - } - } - - // first row - uint64_t fourth32 = (products[3][3] & 0xffffffff); - uint64_t third32 = (products[3][2] & 0xffffffff) + (products[3][3] >> 32); - uint64_t second32 = (products[3][1] & 0xffffffff) + (products[3][2] >> 32); - uint64_t first32 = (products[3][0] & 0xffffffff) + (products[3][1] >> 32); - - // second row - third32 += (products[2][3] & 0xffffffff); - second32 += (products[2][2] & 0xffffffff) + (products[2][3] >> 32); - first32 += (products[2][1] & 0xffffffff) + (products[2][2] >> 32); - - // third row - second32 += (products[1][3] & 0xffffffff); - first32 += (products[1][2] & 0xffffffff) + (products[1][3] >> 32); - - // fourth row - first32 += (products[0][3] & 0xffffffff); - - // move carry to next digit - third32 += fourth32 >> 32; - second32 += third32 >> 32; - first32 += second32 >> 32; - - // remove carry from current digit - fourth32 &= 0xffffffff; - third32 &= 0xffffffff; - second32 &= 0xffffffff; - first32 &= 0xffffffff; - - // combine components - result.lower = (third32 << 32) | fourth32; - result.upper = (first32 << 32) | second32; -#endif - if (lhs_negative ^ rhs_negative) { - NegateInPlace(result); - } - return result; + return HugeintMultiplyUnchecked(lhs, rhs); } //===--------------------------------------------------------------------===// // Divide //===--------------------------------------------------------------------===// -int Sign(hugeint_t n) { - return ((n > 0) - (n < 0)); -} - -hugeint_t Abs(hugeint_t n) { - D_ASSERT(n != NumericLimits::Minimum()); - return (n * Sign(n)); -} - -static hugeint_t DivModMinimum(hugeint_t lhs, hugeint_t rhs, hugeint_t &remainder) { - D_ASSERT(lhs == NumericLimits::Minimum() || rhs == NumericLimits::Minimum()); - if (rhs == NumericLimits::Minimum()) { - if (lhs == NumericLimits::Minimum()) { - remainder = 0; - return 1; - } - remainder = lhs; - return 0; - } - - // Add 1 to minimum and run through DivMod again - hugeint_t result = Hugeint::DivMod(NumericLimits::Minimum() + 1, rhs, remainder); - - // If the 1 mattered we need to adjust the result, otherwise the remainder - if (Abs(remainder) + 1 == Abs(rhs)) { - result -= Sign(rhs); - remainder = 0; - } else { - remainder -= 1; - } - return result; -} - // No overflow checks hugeint_t Hugeint::DivMod(hugeint_t lhs, hugeint_t rhs, hugeint_t &remainder) { - if (rhs == 0) { - remainder = lhs; - return hugeint_t(0); - } - - // Check if one of the sides is hugeint_t minimum, as that can't be negated. - if (lhs == NumericLimits::Minimum() || rhs == NumericLimits::Minimum()) { - return DivModMinimum(lhs, rhs, remainder); - } - - bool lhs_negative = lhs.upper < 0; - bool rhs_negative = rhs.upper < 0; - if (lhs_negative) { - Hugeint::NegateInPlace(lhs); - } - if (rhs_negative) { - Hugeint::NegateInPlace(rhs); - } - // DivMod code adapted from: - // https://github.com/calccrypto/uint128_t/blob/master/uint128_t.cpp - - // initialize the result and remainder to 0 - hugeint_t div_result; - div_result.lower = 0; - div_result.upper = 0; - remainder.lower = 0; - remainder.upper = 0; - - uint8_t highest_bit_set = PositiveHugeintHighestBit(lhs); - // now iterate over the amount of bits that are set in the LHS - for (uint8_t x = highest_bit_set; x > 0; x--) { - // left-shift the current result and remainder by 1 - div_result = PositiveHugeintLeftShift(div_result, 1); - remainder = PositiveHugeintLeftShift(remainder, 1); - - // we get the value of the bit at position X, where position 0 is the least-significant bit - if (PositiveHugeintIsBitSet(lhs, x - 1)) { - remainder += 1; - } - if (Hugeint::GreaterThanEquals(remainder, rhs)) { - // the remainder has passed the division multiplier: add one to the divide result - remainder -= rhs; - div_result += 1; - } - } - if (lhs_negative ^ rhs_negative) { - Hugeint::NegateInPlace(div_result); - } - if (lhs_negative) { - Hugeint::NegateInPlace(remainder); - } - return div_result; + auto result = HugeintDivMod(lhs, rhs); + remainder = result.remainder; + return result.quotient; } bool Hugeint::TryDivMod(hugeint_t lhs, hugeint_t rhs, hugeint_t &result, hugeint_t &remainder) { @@ -494,9 +304,7 @@ hugeint_t Hugeint::Divide(hugeint_t lhs, hugeint_t rhs) { template <> hugeint_t Hugeint::Modulo(hugeint_t lhs, hugeint_t rhs) { - hugeint_t remainder; - (void)Hugeint::DivMod(lhs, rhs, remainder); - return remainder; + return HugeintDivMod(lhs, rhs).remainder; } //===--------------------------------------------------------------------===// @@ -773,62 +581,11 @@ bool Hugeint::TryConvert(long double value, hugeint_t &result) { //===--------------------------------------------------------------------===// // hugeint_t operators //===--------------------------------------------------------------------===// -hugeint_t::hugeint_t(int64_t value) { - auto result = Hugeint::Convert(value); - this->lower = result.lower; - this->upper = result.upper; -} - -bool hugeint_t::operator==(const hugeint_t &rhs) const { - return Hugeint::Equals(*this, rhs); -} - -bool hugeint_t::operator!=(const hugeint_t &rhs) const { - return Hugeint::NotEquals(*this, rhs); -} - -bool hugeint_t::operator<(const hugeint_t &rhs) const { - return Hugeint::LessThan(*this, rhs); -} - -bool hugeint_t::operator<=(const hugeint_t &rhs) const { - return Hugeint::LessThanEquals(*this, rhs); -} - -bool hugeint_t::operator>(const hugeint_t &rhs) const { - return Hugeint::GreaterThan(*this, rhs); -} - -bool hugeint_t::operator>=(const hugeint_t &rhs) const { - return Hugeint::GreaterThanEquals(*this, rhs); -} - -hugeint_t hugeint_t::operator+(const hugeint_t &rhs) const { - return hugeint_t(upper + rhs.upper + ((lower + rhs.lower) < lower), lower + rhs.lower); -} - -hugeint_t hugeint_t::operator-(const hugeint_t &rhs) const { - return hugeint_t(upper - rhs.upper - ((lower - rhs.lower) > lower), lower - rhs.lower); -} - -hugeint_t hugeint_t::operator*(const hugeint_t &rhs) const { - hugeint_t result = *this; - result *= rhs; - return result; -} - -hugeint_t hugeint_t::operator/(const hugeint_t &rhs) const { - return Hugeint::Divide(*this, rhs); -} hugeint_t hugeint_t::operator%(const hugeint_t &rhs) const { return Hugeint::Modulo(*this, rhs); } -hugeint_t hugeint_t::operator-() const { - return Hugeint::Negate(*this); -} - hugeint_t hugeint_t::operator>>(const hugeint_t &rhs) const { hugeint_t result; uint64_t shift = rhs.lower; diff --git a/src/duckdb/src/common/types/hyperloglog.cpp b/src/duckdb/src/common/types/hyperloglog.cpp index 5978cccf7..e88b3b39f 100644 --- a/src/duckdb/src/common/types/hyperloglog.cpp +++ b/src/duckdb/src/common/types/hyperloglog.cpp @@ -199,6 +199,12 @@ unique_ptr HyperLogLog::Deserialize(Deserializer &deserializer) { default: throw SerializationException("Unknown HyperLogLog storage type!"); } + for (idx_t i = 0; i < M; i++) { + // registers hold the number of leading zeros (plus one) of the hashes + if (result->k[i] > Q + 1) { + throw SerializationException("Invalid HyperLogLog register value %d", result->k[i]); + } + } return result; } diff --git a/src/duckdb/src/common/types/interval.cpp b/src/duckdb/src/common/types/interval.cpp index be943a4de..f208bf137 100644 --- a/src/duckdb/src/common/types/interval.cpp +++ b/src/duckdb/src/common/types/interval.cpp @@ -167,10 +167,10 @@ interval_parse_time : { // parse the remainder of the time as a Time type dtime_t time; idx_t pos; - if (!Time::TryConvertInterval(str + start_pos, len - start_pos, pos, time)) { + if (!Time::TryConvertInterval(str + start_pos, len - start_pos, pos, time, false, nullptr, negative)) { return false; } - if (!IntervalTryAddition(result.micros, negative ? -time.value : time.value, 1, error_message)) { + if (!IntervalTryAddition(result.micros, time.value, 1, error_message)) { return false; } found_any = true; diff --git a/src/duckdb/src/common/types/row/partitioned_tuple_data.cpp b/src/duckdb/src/common/types/row/partitioned_tuple_data.cpp index 7f19b7609..85c33683b 100644 --- a/src/duckdb/src/common/types/row/partitioned_tuple_data.cpp +++ b/src/duckdb/src/common/types/row/partitioned_tuple_data.cpp @@ -405,6 +405,14 @@ idx_t PartitionedTupleData::SizeInBytes() const { return data_size + stl_allocator->AllocationSize(); } +idx_t PartitionedTupleData::GetAllocatedSizeInBytes() const { + idx_t result = stl_allocator->AllocationSize(); + for (const auto &partition : partitions) { + result += partition->GetBlockAllocationSize(); + } + return result; +} + idx_t PartitionedTupleData::PartitionCount() const { return partitions.size(); } diff --git a/src/duckdb/src/common/types/row/tuple_data_allocator.cpp b/src/duckdb/src/common/types/row/tuple_data_allocator.cpp index 4a0e98537..fb1ead696 100644 --- a/src/duckdb/src/common/types/row/tuple_data_allocator.cpp +++ b/src/duckdb/src/common/types/row/tuple_data_allocator.cpp @@ -60,6 +60,7 @@ void TupleDataAllocator::Reset() { SetDestroyBufferUponUnpin(); row_blocks.clear(); heap_blocks.clear(); + block_allocation_size = 0; } void TupleDataAllocator::DestroyRowBlocks(const idx_t row_block_begin, const idx_t row_block_end) { @@ -119,6 +120,10 @@ idx_t TupleDataAllocator::HeapBlockCount() const { return heap_blocks.size(); } +idx_t TupleDataAllocator::GetBlockAllocationSize() const { + return block_allocation_size; +} + void TupleDataAllocator::SetPartitionIndex(const idx_t index) { D_ASSERT(!partition_index.IsValid()); D_ASSERT(row_blocks.empty() && heap_blocks.empty()); @@ -769,6 +774,7 @@ void TupleDataAllocator::ReleaseOrStoreHandlesInternal(TupleDataSegment &segment // Prevent it from being added to the eviction queue blocks[block_id].handle->GetMemory().SetDestroyBufferUpon(DestroyBufferUpon::UNPIN); // Destroy + block_allocation_size -= blocks[block_id].handle->GetMemory().GetMemoryUsage(); blocks[block_id].handle.reset(); break; default: @@ -791,6 +797,7 @@ void TupleDataAllocator::CreateRowBlock(TupleDataSegment &segment, TupleDataPinS } auto block_index = NumericCast(row_blocks.size()); row_blocks.emplace_back(std::move(block_handle), block_size); + block_allocation_size += row_blocks.back().handle->GetMemory().GetMemoryUsage(); segment.pinned_row_handles.resize(row_blocks.size()); pin_state.row_handles.emplace(block_index, std::move(buffer_handle)); } @@ -803,6 +810,7 @@ void TupleDataAllocator::CreateHeapBlock(TupleDataSegment &segment, TupleDataPin } auto block_index = NumericCast(heap_blocks.size()); heap_blocks.emplace_back(std::move(block_handle), size); + block_allocation_size += heap_blocks.back().handle->GetMemory().GetMemoryUsage(); segment.pinned_heap_handles.resize(heap_blocks.size()); pin_state.heap_handles.emplace(block_index, std::move(buffer_handle)); } diff --git a/src/duckdb/src/common/types/row/tuple_data_collection.cpp b/src/duckdb/src/common/types/row/tuple_data_collection.cpp index 4df088042..19a682334 100644 --- a/src/duckdb/src/common/types/row/tuple_data_collection.cpp +++ b/src/duckdb/src/common/types/row/tuple_data_collection.cpp @@ -8,6 +8,7 @@ #include "duckdb/common/printer.hpp" #include "duckdb/common/row_operations/row_operations.hpp" +#include "duckdb/common/reference_map.hpp" #include "duckdb/common/type_visitor.hpp" #include "duckdb/common/types/row/tuple_data_allocator.hpp" #include "duckdb/main/database.hpp" @@ -59,8 +60,7 @@ void TupleDataCollection::Initialize() { } else { scatter_functions.reserve(layout.ColumnCount()); gather_functions.reserve(layout.ColumnCount()); - for (idx_t col_idx = 0; col_idx < layout.ColumnCount(); col_idx++) { - auto &type = layout.GetTypes()[col_idx]; + for (auto &type : layout.GetTypes()) { scatter_functions.emplace_back(GetScatterFunction(type)); gather_functions.emplace_back(GetGatherFunction(type)); } @@ -111,6 +111,24 @@ idx_t TupleDataCollection::SizeInBytes() const { return data_size + stl_allocator->AllocationSize(); } +idx_t TupleDataCollection::GetBlockAllocationSize() const { + if (segments.empty() || (segments.size() == 1 && segments[0] && segments[0]->allocator == allocator)) { + return allocator->GetBlockAllocationSize(); + } + reference_set_t allocators; + allocators.insert(*allocator); + for (const auto &segment : segments) { + if (segment) { + allocators.insert(*segment->allocator); + } + } + idx_t result = 0; + for (auto &entry : allocators) { + result += entry.get().GetBlockAllocationSize(); + } + return result; +} + void TupleDataCollection::Unpin() { for (auto &segment : segments) { segment->Unpin(); @@ -739,7 +757,14 @@ void TupleDataCollection::FinalizePinState(TupleDataPinState &pin_state, TupleDa } void TupleDataCollection::FinalizePinState(TupleDataPinState &pin_state) { - D_ASSERT(!segments.empty()); + if (segments.empty()) { + // The collection was Reset() while an append state still referred to it, which happens when an exception + // (e.g. out-of-memory) unwinds a Repartition after it moved this partition's rows and before the owner + // re-initialised its append states. There is no segment left to store the handles in, so release them. + pin_state.row_handles.clear(); + pin_state.heap_handles.clear(); + return; + } FinalizePinState(pin_state, *segments.back()); } diff --git a/src/duckdb/src/common/types/row/tuple_data_scatter_gather.cpp b/src/duckdb/src/common/types/row/tuple_data_scatter_gather.cpp index ce4463b41..dabe91869 100644 --- a/src/duckdb/src/common/types/row/tuple_data_scatter_gather.cpp +++ b/src/duckdb/src/common/types/row/tuple_data_scatter_gather.cpp @@ -11,6 +11,7 @@ #include "duckdb/common/vector/list_vector.hpp" #include "duckdb/common/vector/map_vector.hpp" #include "duckdb/common/vector/struct_vector.hpp" +#include "duckdb/common/vector/vector_writer.hpp" namespace duckdb { @@ -110,10 +111,12 @@ void TupleDataCollection::ComputeHeapSizes(TupleDataChunkState &chunk_state, con const SelectionVector &append_sel, const idx_t append_count) { ResetCombinedListData(chunk_state.vector_data); - auto heap_sizes = FlatVector::GetDataMutable(chunk_state.heap_sizes); - std::fill_n(heap_sizes, append_count, 0); + auto writer = FlatVector::Writer(chunk_state.heap_sizes, append_count); + for (idx_t i = 0; i < append_count; i++) { + writer.WriteValue(0); + } - for (idx_t col_idx = 0; col_idx < new_chunk.ColumnCount(); col_idx++) { + for (auto col_idx : chunk_state.column_ids) { const auto &source_v = new_chunk.data[col_idx]; auto &source_format = chunk_state.vector_data[col_idx]; ComputeHeapSizes(chunk_state.heap_sizes, source_v, source_format, append_sel, append_count); diff --git a/src/duckdb/src/common/types/time.cpp b/src/duckdb/src/common/types/time.cpp index d19041fce..04396b7a1 100644 --- a/src/duckdb/src/common/types/time.cpp +++ b/src/duckdb/src/common/types/time.cpp @@ -5,7 +5,9 @@ #include "duckdb/common/types/date.hpp" #include "duckdb/common/types/interval.hpp" #include "duckdb/common/types/timestamp.hpp" +#include "duckdb/common/operator/add.hpp" #include "duckdb/common/operator/multiply.hpp" +#include "duckdb/common/operator/subtract.hpp" #include "duckdb/common/exception/conversion_exception.hpp" #include @@ -19,8 +21,9 @@ static_assert(sizeof(dtime_t) == sizeof(int64_t), "dtime_t was padded"); // ISO 8601 bool Time::TryConvertInternal(const char *buf, idx_t len, idx_t &pos, dtime_t &result, bool strict, - optional_ptr nanos) { - int32_t hour = -1, min = -1, sec = -1, micros = -1; + optional_ptr nanos, bool negative) { + int64_t hour = -1; + int32_t min = -1, sec = -1, micros = -1; pos = 0; if (len == 0) { @@ -42,9 +45,9 @@ bool Time::TryConvertInternal(const char *buf, idx_t len, idx_t &pos, dtime_t &r return false; } - // Allow up to 9 digit hours to support intervals + // Allow up to 10 digit interval hours hour = 0; - for (int32_t digits = 9; pos < len && StringUtil::CharacterIsDigit(buf[pos]); ++pos) { + for (int32_t digits = 10; pos < len && StringUtil::CharacterIsDigit(buf[pos]); ++pos) { if (digits-- > 0) { hour = hour * 10 + (buf[pos] - '0'); } else { @@ -133,13 +136,25 @@ bool Time::TryConvertInternal(const char *buf, idx_t len, idx_t &pos, dtime_t &r } } - result = Time::FromTime(hour, min, sec, micros); + // a negative interval accumulates below zero, so its minimum fits + int64_t time_micros; + auto sub_hour_micros = Time::FromTime(0, min, sec, micros).value; + if (negative) { + if (!TryMultiplyOperator::Operation(hour, -Interval::MICROS_PER_HOUR, time_micros) || + !TrySubtractOperator::Operation(time_micros, sub_hour_micros, time_micros)) { + return false; + } + } else if (!TryMultiplyOperator::Operation(hour, Interval::MICROS_PER_HOUR, time_micros) || + !TryAddOperator::Operation(time_micros, sub_hour_micros, time_micros)) { + return false; + } + result = dtime_t(time_micros); return true; } bool Time::TryConvertInterval(const char *buf, idx_t len, idx_t &pos, dtime_t &result, bool strict, - optional_ptr nanos) { - return Time::TryConvertInternal(buf, len, pos, result, strict, nanos); + optional_ptr nanos, bool negative) { + return Time::TryConvertInternal(buf, len, pos, result, strict, nanos, negative); } bool Time::TryConvertTime(const char *buf, idx_t len, idx_t &pos, dtime_t &result, bool strict, @@ -316,6 +331,12 @@ bool Time::IsValidTime(int32_t hour, int32_t minute, int32_t second, int32_t mic if (microseconds < 0 || microseconds > 1000000) { return false; } + // a leap second (or a full second of microseconds) is allowed, as long as the time does not exceed 24:00:00 + const int64_t total_micros = + ((int64_t(hour) * 60 + minute) * 60 + second) * Interval::MICROS_PER_SEC + microseconds; + if (total_micros > Interval::MICROS_PER_DAY) { + return false; + } return true; } diff --git a/src/duckdb/src/common/types/timestamp.cpp b/src/duckdb/src/common/types/timestamp.cpp index da0255712..9af372bf1 100644 --- a/src/duckdb/src/common/types/timestamp.cpp +++ b/src/duckdb/src/common/types/timestamp.cpp @@ -2,6 +2,7 @@ #include "duckdb/common/exception.hpp" #include "duckdb/common/types/date.hpp" +#include "duckdb/common/types/hugeint.hpp" #include "duckdb/common/types/interval.hpp" #include "duckdb/common/types/time.hpp" #include "duckdb/common/string_util.hpp" @@ -17,15 +18,6 @@ namespace duckdb { static_assert(sizeof(timestamp_t) == sizeof(int64_t), "timestamp_t was padded"); -// Temporal values need to round down when changing precision, -// but C/C++ rounds towards 0 when you simply divide. -// This piece of bit banging solves that problem. -template -static inline T TemporalRound(T value, T scale) { - const auto negative = int(value < 0); - return UnsafeNumericCast((value + negative) / scale - negative); -} - static bool CanStartTimestampSuffix(char c) { return c == 'Z' || c == '+' || c == '-' || StringUtil::CharacterIsSpace(c); } @@ -173,16 +165,8 @@ bool Timestamp::TryFromTimestampNanos(timestamp_t input, int32_t nanos, timestam result.value = input.value; return true; } - // Scale to ns - if (!TryMultiplyOperator::Operation(input.value, Interval::NANOS_PER_MICRO, result.value)) { - return false; - } - - if (!TryAddOperator::Operation(result.value, int64_t(nanos), result.value)) { - return false; - } - - return result.IsFinite(); + const auto value = hugeint_t(input.value) * hugeint_t(Interval::NANOS_PER_MICRO) + hugeint_t(nanos); + return Hugeint::TryCast(value, result.value) && result.IsFinite(); } TimestampCastResult Timestamp::TryConvertTimestamp(const char *str, idx_t len, timestamp_ns_t &result, bool use_offset, @@ -372,20 +356,22 @@ dtime_t Timestamp::GetTime(timestamp_t timestamp) { if (!timestamp.IsFinite()) { throw ConversionException("Can't get TIME of infinite TIMESTAMP"); } - date_t date = Timestamp::GetDate(timestamp); - return dtime_t(timestamp.value - (int64_t(date.days) * int64_t(Interval::MICROS_PER_DAY))); + int64_t micros = timestamp.value % Interval::MICROS_PER_DAY; + if (micros < 0) { + micros += Interval::MICROS_PER_DAY; + } + return dtime_t(micros); } dtime_ns_t Timestamp::GetTimeNs(timestamp_ns_t input) { if (!input.IsFinite()) { throw ConversionException("Can't get TIME_NS of infinite TIMESTAMP"); } - date_t date = Timestamp::GetDateNS(input); - int64_t nanos; - if (!TryMultiplyOperator::Operation(date.days, Interval::NANOS_PER_DAY, nanos)) { - throw ConversionException("Overflow extracting TIME_NS of TIMESTAMP"); + auto nanos = input.value % Interval::NANOS_PER_DAY; + if (nanos < 0) { + nanos += Interval::NANOS_PER_DAY; } - return dtime_ns_t(input.value - nanos); + return dtime_ns_t(nanos); } bool Timestamp::TryFromDatetime(date_t date, dtime_t time, timestamp_t &result) { @@ -430,16 +416,10 @@ void Timestamp::Convert(timestamp_t timestamp, date_t &out_date, dtime_t &out_ti } void Timestamp::Convert(timestamp_ns_t input, date_t &out_date, dtime_t &out_time, int32_t &out_nanos) { - timestamp_t ms(TemporalRound(input.value, Interval::NANOS_PER_MICRO)); - out_date = Timestamp::GetDate(ms); - int64_t days_nanos; - if (!TryMultiplyOperator::Operation(out_date.days, Interval::NANOS_PER_DAY, - days_nanos)) { - throw ConversionException("Date out of range in timestamp_ns conversion"); - } - - out_time = dtime_t((input.value - days_nanos) / Interval::NANOS_PER_MICRO); - out_nanos = UnsafeNumericCast((input.value - days_nanos) % Interval::NANOS_PER_MICRO); + out_date = Timestamp::GetDateNS(input); + auto time_nanos = Timestamp::GetTimeNs(input).value; + out_time = dtime_t(time_nanos / Interval::NANOS_PER_MICRO); + out_nanos = UnsafeNumericCast(time_nanos % Interval::NANOS_PER_MICRO); } timestamp_t Timestamp::GetCurrentTimestamp() { diff --git a/src/duckdb/src/common/types/type_manager.cpp b/src/duckdb/src/common/types/type_manager.cpp index 255e41c85..145e4fdce 100644 --- a/src/duckdb/src/common/types/type_manager.cpp +++ b/src/duckdb/src/common/types/type_manager.cpp @@ -1,4 +1,6 @@ #include "duckdb/common/types/type_manager.hpp" + +#include "duckdb/common/type_visitor.hpp" #include "duckdb/function/cast/cast_function_set.hpp" #include "duckdb/parser/parser.hpp" #include "duckdb/planner/binder.hpp" @@ -12,13 +14,17 @@ CastFunctionSet &TypeManager::GetCastFunctions() { return *cast_functions; } -static LogicalType TransformStringToUnboundType(const string &str) { +const CastFunctionSet &TypeManager::GetCastFunctions() const { + return *cast_functions; +} + +static LogicalType TransformStringToUnboundType(const string &str, ClientContext &context) { if (StringUtil::Lower(str) == "null") { return LogicalType::SQLNULL; } ColumnList column_list; try { - column_list = Parser::ParseColumnList("dummy " + str); + column_list = Parser(context).ParseColumnList("dummy " + str); } catch (const std::runtime_error &e) { const vector suggested_types {"BIGINT", "INT8", @@ -82,7 +88,7 @@ static LogicalType TransformStringToUnboundType(const string &str) { // This has to be called with a level of indirection (through "parse_function") in order to avoid being included in // extensions that statically link the core DuckDB library. static LogicalType ParseLogicalTypeInternal(const string &type_str, ClientContext &context) { - auto type = TransformStringToUnboundType(type_str); + auto type = TransformStringToUnboundType(type_str, context); if (type.IsUnbound()) { if (!context.transaction.HasActiveTransaction()) { throw InternalException( @@ -98,6 +104,14 @@ LogicalType TypeManager::ParseLogicalType(const string &type_str, ClientContext return parse_function(type_str, context); } +LogicalType TypeManager::ParseLogicalType(const string &type_str) const { + auto type = UnboundType::TryParseAndDefaultBind(type_str); + if (TypeVisitor::Contains(type, LogicalTypeId::UNBOUND)) { + throw InvalidInputException("Type \"%s\" is not a built-in type", type_str); + } + return type; +} + TypeManager &TypeManager::Get(DatabaseInstance &db) { return DBConfig::GetConfig(db).GetTypeManager(); } diff --git a/src/duckdb/src/common/types/value.cpp b/src/duckdb/src/common/types/value.cpp index d817e80dc..22611ee68 100644 --- a/src/duckdb/src/common/types/value.cpp +++ b/src/duckdb/src/common/types/value.cpp @@ -159,6 +159,12 @@ Value::Value(const Identifier &val) : Value(val.GetIdentifierName()) { Value::Value(const char *val) : Value(val ? string(val) : string()) { } +static void VerifyStringValue(const Value &value) { + if (!value.IsValid()) { + throw ErrorManager::InvalidUnicodeError(StringValue::Get(value), "value construction"); + } +} + Value::Value(std::nullptr_t val) : Value(LogicalType::VARCHAR) { } @@ -166,24 +172,18 @@ Value::Value(string_t val) : Value(val.GetString()) { } Value::Value(string val) : type_(LogicalType::VARCHAR), is_null(false) { - if (!Value::StringIsValid(val.c_str(), val.size())) { - throw ErrorManager::InvalidUnicodeError(val, "value construction"); - } value_info_ = make_shared_ptr(std::move(val)); + VerifyStringValue(*this); } Value::Value(String val) : type_(LogicalType::VARCHAR), is_null(false) { - if (!Value::StringIsValid(val.c_str(), val.size())) { - throw ErrorManager::InvalidUnicodeError(val, "value construction"); - } value_info_ = make_shared_ptr(val.ToStdString()); + VerifyStringValue(*this); } Value::Value(std::string_view val) : type_(LogicalType::VARCHAR), is_null(false) { - if (!Value::StringIsValid(val.data(), val.size())) { - throw ErrorManager::InvalidUnicodeError(string(val), "value construction"); - } value_info_ = make_shared_ptr(string(val)); + VerifyStringValue(*this); } Value::~Value() { @@ -975,7 +975,7 @@ Value Value::BIGNUM(const string &data) { Value Value::GEOMETRY(const_data_ptr_t data, idx_t len, const CoordinateReferenceSystem &crs) { Value result; - result.type_ = LogicalType::GEOMETRY(crs); // construct type explicitly so that we get the ExtraTypeInfo + result.type_ = LogicalType::GEOMETRY(crs); // construct type explicitly so that we get the LogicalTypeInfo result.is_null = false; result.value_info_ = make_shared_ptr(string(const_char_ptr_cast(data), len)); return result; @@ -983,7 +983,7 @@ Value Value::GEOMETRY(const_data_ptr_t data, idx_t len, const CoordinateReferenc Value Value::GEOMETRY(const_data_ptr_t data, idx_t len) { Value result; - result.type_ = LogicalType::GEOMETRY(); // construct type explicitly so that we get the ExtraTypeInfo + result.type_ = LogicalType::GEOMETRY(); // construct type explicitly so that we get the LogicalTypeInfo result.is_null = false; result.value_info_ = make_shared_ptr(string(const_char_ptr_cast(data), len)); return result; @@ -1788,31 +1788,12 @@ string Value::ToSQLString() const { return "CAST(CAST(" + val.ToSQLString() + " AS " + val.type().ToString() + ") AS VARIANT)"; } case LogicalTypeId::TUPLE: - case LogicalTypeId::STRUCT: { - // a TUPLE is always unnamed (even when empty, where IsUnnamed cannot tell) - bool is_unnamed = type_.id() == LogicalTypeId::TUPLE || StructType::IsUnnamed(type_); - string ret = is_unnamed ? "(" : "{"; - auto &child_types = StructType::GetChildTypes(type_); - auto &struct_values = StructValue::GetChildren(*this); - for (idx_t i = 0; i < struct_values.size(); i++) { - auto &name = child_types[i].first; - auto &child = struct_values[i]; - if (is_unnamed) { - ret += child.ToSQLString(); - } else { - ret += "'" + StringUtil::Replace(name.GetIdentifierName(), "'", "''") + "': " + child.ToSQLString(); - } - if (i < struct_values.size() - 1) { - ret += ", "; - } - } - // a single-element tuple needs a trailing comma to round-trip: (1,) - otherwise (1) is just grouping - if (is_unnamed && struct_values.size() == 1) { - ret += ","; - } - ret += is_unnamed ? ")" : "}"; - return ret; - } + case LogicalTypeId::STRUCT: + case LogicalTypeId::LIST: + case LogicalTypeId::ARRAY: + case LogicalTypeId::MAP: + case LogicalTypeId::UNION: + return NestedToSQLString(*this, [](const Value &child) { return child.ToSQLString(); }); case LogicalTypeId::FLOAT: if (!FloatIsFinite(FloatValue::Get(*this)) || (FloatValue::Get(*this) == 0 && std::signbit(FloatValue::Get(*this)))) { @@ -1834,61 +1815,74 @@ string Value::ToSQLString() const { } return ToString(); } - case LogicalTypeId::LIST: { - string ret = "["; - auto &list_values = ListValue::GetChildren(*this); - for (idx_t i = 0; i < list_values.size(); i++) { - auto &child = list_values[i]; - ret += child.ToSQLString(); - if (i < list_values.size() - 1) { + default: + return ToString(); + } +} + +string Value::NestedToSQLString(const Value &value, const std::function &child_to_sql) { + auto &type = value.type(); + switch (type.id()) { + case LogicalTypeId::TUPLE: + case LogicalTypeId::STRUCT: { + bool is_unnamed = type.id() == LogicalTypeId::TUPLE; + string ret = is_unnamed ? "(" : "{"; + auto &child_types = StructType::GetChildTypes(type); + auto &struct_values = StructValue::GetChildren(value); + for (idx_t i = 0; i < struct_values.size(); i++) { + auto &name = child_types[i].first; + auto &child = struct_values[i]; + if (is_unnamed) { + ret += child_to_sql(child); + } else { + ret += "'" + StringUtil::Replace(name.GetIdentifierName(), "'", "''") + "': " + child_to_sql(child); + } + if (i < struct_values.size() - 1) { ret += ", "; } } - ret += "]"; + // a single-element tuple needs a trailing comma to round-trip: (1,) - otherwise (1) is just grouping + if (is_unnamed && struct_values.size() == 1) { + ret += ","; + } + ret += is_unnamed ? ")" : "}"; return ret; } + case LogicalTypeId::LIST: case LogicalTypeId::ARRAY: { - string ret = "["; - auto &array_values = ArrayValue::GetChildren(*this); - for (idx_t i = 0; i < array_values.size(); i++) { - auto &child = array_values[i]; - ret += child.ToSQLString(); - if (i < array_values.size() - 1) { - ret += ", "; - } - } - ret += "]"; - return ret; + auto &children = + type.id() == LogicalTypeId::LIST ? ListValue::GetChildren(value) : ArrayValue::GetChildren(value); + return "[" + StringUtil::Join(children, children.size(), ", ", child_to_sql) + "]"; } case LogicalTypeId::MAP: { // A bare `MAP {...}` literal infers its element types from the entries // (and `MAP {}` infers MAP(INTEGER, INTEGER)), so it does not faithfully // round-trip on its own. Append an explicit cast to the real type - auto &entries = MapValue::GetChildren(*this); + auto &entries = MapValue::GetChildren(value); string ret = "MAP {"; for (idx_t i = 0; i < entries.size(); i++) { auto &kv = StructValue::GetChildren(entries[i]); if (i > 0) { ret += ", "; } - ret += kv[0].ToSQLString(); + ret += child_to_sql(kv[0]); ret += ": "; - ret += kv[1].ToSQLString(); + ret += child_to_sql(kv[1]); } - ret += "}::" + type_.ToString(); + ret += "}::" + type.ToString(); return ret; } case LogicalTypeId::UNION: { string ret = "union_value("; - auto union_tag = UnionValue::GetTag(*this); - auto &tag_name = UnionType::GetMemberName(type(), union_tag); + auto union_tag = UnionValue::GetTag(value); + auto &tag_name = UnionType::GetMemberName(type, union_tag); ret += SQLIdentifier(tag_name) + " := "; - ret += UnionValue::GetValue(*this).ToSQLString(); - ret += ")::" + type_.ToString(); + ret += child_to_sql(UnionValue::GetValue(value)); + ret += ")::" + type.ToString(); return ret; } default: - return ToString(); + throw InternalException("Value::NestedToSQLString called on non-nested type %s", type.ToString()); } } @@ -2353,6 +2347,167 @@ void Value::Serialize(Serializer &serializer) const { SerializeInternal(serializer, true); } +//! Whether the value (excluding its children) is within the domain of its type +//! Whether a child value has the type its parent expects - ANY accepts values of any type (e.g. in secrets) +static bool ChildTypeMatches(const Value &child, const LogicalType &expected_type) { + return expected_type.id() == LogicalTypeId::ANY || child.type() == expected_type; +} + +static bool ValueIsValidShallow(const Value &value) { + if (value.IsNull()) { + return true; + } + auto &type = value.type(); + switch (type.id()) { + case LogicalTypeId::VARCHAR: + return Value::StringIsValid(StringValue::Get(value)); + case LogicalTypeId::ENUM: { + uint64_t index; + switch (type.InternalType()) { + case PhysicalType::UINT8: + index = value.GetValueUnsafe(); + break; + case PhysicalType::UINT16: + index = value.GetValueUnsafe(); + break; + case PhysicalType::UINT32: + index = value.GetValueUnsafe(); + break; + default: + throw InternalException("Invalid physical type for ENUM"); + } + return index < EnumType::GetSize(type); + } + case LogicalTypeId::TIME: { + const auto micros = value.GetValueUnsafe().value; + return micros >= 0 && micros <= Interval::MICROS_PER_DAY; + } + case LogicalTypeId::TIME_NS: { + const auto nanos = value.GetValueUnsafe().value; + return nanos >= 0 && nanos <= Interval::NANOS_PER_DAY; + } + case LogicalTypeId::TIME_TZ: { + const auto time_tz = value.GetValueUnsafe(); + const auto micros = time_tz.time().value; + const auto offset = time_tz.offset(); + return micros >= 0 && micros <= Interval::MICROS_PER_DAY && offset >= dtime_tz_t::MIN_OFFSET && + offset <= dtime_tz_t::MAX_OFFSET; + } + case LogicalTypeId::DECIMAL: { + const auto width = DecimalType::GetWidth(type); + switch (type.InternalType()) { + case PhysicalType::INT16: + case PhysicalType::INT32: + case PhysicalType::INT64: { + int64_t decimal_value; + if (type.InternalType() == PhysicalType::INT16) { + decimal_value = value.GetValueUnsafe(); + } else if (type.InternalType() == PhysicalType::INT32) { + decimal_value = value.GetValueUnsafe(); + } else { + decimal_value = value.GetValueUnsafe(); + } + return decimal_value > -NumericHelper::POWERS_OF_TEN[width] && + decimal_value < NumericHelper::POWERS_OF_TEN[width]; + } + case PhysicalType::INT128: { + const auto decimal_value = value.GetValueUnsafe(); + return decimal_value > -Hugeint::POWERS_OF_TEN[width] && decimal_value < Hugeint::POWERS_OF_TEN[width]; + } + default: + throw InternalException("Invalid physical type for DECIMAL"); + } + } + case LogicalTypeId::BIT: { + // a padding byte followed by at least one data byte, with the padding bits set + auto &bits = StringValue::Get(value); + if (bits.size() < 2) { + return false; + } + const auto padding = static_cast(bits[0]); + if (padding >= 8) { + return false; + } + const auto padding_mask = static_cast(0xFF << (8 - padding)); + return padding == 0 || (static_cast(bits[1]) & padding_mask) == padding_mask; + } + case LogicalTypeId::UNION: { + auto &children = StructValue::GetChildren(value); + if (children.size() != UnionType::GetMemberCount(type) + 1 || + children[0].type().id() != LogicalTypeId::UTINYINT || children[0].IsNull() || + children[0].GetValueUnsafe() >= UnionType::GetMemberCount(type)) { + return false; + } + for (idx_t member_idx = 0; member_idx < UnionType::GetMemberCount(type); member_idx++) { + if (!ChildTypeMatches(children[member_idx + 1], UnionType::GetMemberType(type, member_idx))) { + return false; + } + } + return true; + } + case LogicalTypeId::STRUCT: { + auto &children = StructValue::GetChildren(value); + if (children.size() != StructType::GetChildCount(type)) { + return false; + } + for (idx_t child_idx = 0; child_idx < children.size(); child_idx++) { + if (!ChildTypeMatches(children[child_idx], StructType::GetChildType(type, child_idx))) { + return false; + } + } + return true; + } + case LogicalTypeId::LIST: + case LogicalTypeId::MAP: { + auto &child_type = ListType::GetChildType(type); + for (auto &child : ListValue::GetChildren(value)) { + if (!ChildTypeMatches(child, child_type)) { + return false; + } + } + return true; + } + case LogicalTypeId::ARRAY: { + auto &children = ArrayValue::GetChildren(value); + if (children.size() != ArrayType::GetSize(type)) { + return false; + } + auto &child_type = ArrayType::GetChildType(type); + for (auto &child : children) { + if (!ChildTypeMatches(child, child_type)) { + return false; + } + } + return true; + } + default: + return true; + } +} + +bool Value::IsValid() const { + if (!ValueIsValidShallow(*this)) { + return false; + } + if (is_null) { + return true; + } + switch (type_.InternalType()) { + case PhysicalType::STRUCT: + case PhysicalType::LIST: + case PhysicalType::ARRAY: + for (auto &child : value_info_->Get().GetValues()) { + if (!child.IsValid()) { + return false; + } + } + break; + default: + break; + } + return true; +} + Value Value::Deserialize(Deserializer &deserializer) { auto type = deserializer.ReadPropertyWithExplicitDefault(100, "type", LogicalTypeId::INVALID); if (type.InternalType() == PhysicalType::INVALID) { @@ -2364,6 +2519,9 @@ Value Value::Deserialize(Deserializer &deserializer) { return new_value; } new_value.is_null = false; + if (type.IsNested() && !type.HasParameters()) { + throw SerializationException("Failed to deserialize value: type %s is missing its type info", type.ToString()); + } if (type.id() == LogicalTypeId::TYPE) { // special case for TYPE values: deserialize the type as a nested object @@ -2452,6 +2610,10 @@ Value Value::Deserialize(Deserializer &deserializer) { deserializer.ReadObject(102, "value", [&](Deserializer &obj) { vector children; obj.ReadList(100, "children", [&](Deserializer::List &list, idx_t i) { + if (i >= StructType::GetChildCount(type)) { + throw SerializationException("Failed to deserialize value: too many children for type %s", + type.ToString()); + } deserializer.Set(StructType::GetChildType(type, i)); auto child = list.ReadElement(); deserializer.Unset(); @@ -2471,6 +2633,10 @@ Value Value::Deserialize(Deserializer &deserializer) { default: throw NotImplementedException("Unimplemented type for Deserialize"); } + // the children are deserialized (and checked) through Value::Deserialize as well + if (!ValueIsValidShallow(new_value)) { + throw SerializationException("Failed to deserialize value: value is not valid for type %s", type.ToString()); + } return new_value; } diff --git a/src/duckdb/src/common/types/variant/parquet_variant_encoding.cpp b/src/duckdb/src/common/types/variant/parquet_variant_encoding.cpp new file mode 100644 index 000000000..6bde981f2 --- /dev/null +++ b/src/duckdb/src/common/types/variant/parquet_variant_encoding.cpp @@ -0,0 +1,910 @@ +#include +#include +#include +#include +#include +#include + +#include "duckdb/common/types/variant/parquet_variant_iterator.hpp" +#include "duckdb/common/types/variant/variant_binary_decoder.hpp" +#include "duckdb/common/vector/struct_vector.hpp" +#include "duckdb/common/types/variant.hpp" +#include "duckdb/function/scalar/variant_utils.hpp" +#include "duckdb/function/variant/variant_shredding.hpp" +#include "duckdb/common/operator/cast_operators.hpp" +#include "duckdb/common/assert.hpp" +#include "duckdb/common/enum_util.hpp" +#include "duckdb/common/exception.hpp" +#include "duckdb/common/helper.hpp" +#include "duckdb/common/hugeint.hpp" +#include "duckdb/common/limits.hpp" +#include "duckdb/common/numeric_utils.hpp" +#include "duckdb/common/optional_idx.hpp" +#include "duckdb/common/optional_ptr.hpp" +#include "duckdb/common/string.hpp" +#include "duckdb/common/string_map_set.hpp" +#include "duckdb/common/typedefs.hpp" +#include "duckdb/common/types.hpp" +#include "duckdb/common/types/selection_vector.hpp" +#include "duckdb/common/types/string_type.hpp" +#include "duckdb/common/types/uuid.hpp" +#include "duckdb/common/types/validity_mask.hpp" +#include "duckdb/common/types/value.hpp" +#include "duckdb/common/types/vector.hpp" +#include "duckdb/common/uhugeint.hpp" +#include "duckdb/common/unique_ptr.hpp" +#include "duckdb/common/vector.hpp" +#include "duckdb/common/vector/flat_vector.hpp" +#include "duckdb/common/vector/string_vector.hpp" +#include "duckdb/common/vector/unified_vector_format.hpp" + +namespace duckdb { + +static idx_t CalculateByteLength(idx_t value) { + if (value == 0) { + return 1; + } + auto value_data = reinterpret_cast(&value); + idx_t irrelevant_bytes = 0; + //! Check how many of the most significant bytes are 0 + for (idx_t i = sizeof(idx_t); i > 0 && value_data[i - 1] == 0; i--) { + irrelevant_bytes++; + } + return sizeof(idx_t) - irrelevant_bytes; +} + +static uint8_t EncodeMetadataHeader(idx_t byte_length) { + D_ASSERT(byte_length <= 4); + + uint8_t header_byte = 0; + //! Set 'version' to 1 + header_byte |= static_cast(1); + //! NOTE: keep 'sorted_strings' at 0, we don't always sort the key strings + //! Set 'offset_size_minus_one' to byte_length-1 + header_byte |= (static_cast(byte_length) - 1) << 6; + +#ifdef DEBUG + auto decoded_header = VariantMetadataHeader::FromHeaderByte(header_byte); + D_ASSERT(decoded_header.offset_size == byte_length); +#endif + + return header_byte; +} + +static void CreateMetadata(UnifiedVariantVectorData &variant, Vector &metadata, idx_t count) { + //! NOTE: the parquet variant is limited to a max dictionary size of NumericLimits::Maximum() + //! Whereas we can have NumericLimits::Maximum() *per* string in DuckDB + auto metadata_data = FlatVector::GetDataMutable(metadata); + for (idx_t row = 0; row < count; row++) { + uint64_t dictionary_count = 0; + if (variant.RowIsValid(row)) { + dictionary_count = variant.GetKeysCount(row); + } + idx_t dictionary_size = 0; + for (idx_t i = 0; i < dictionary_count; i++) { + auto &key = variant.GetKey(row, i); + dictionary_size += key.GetSize(); + } + if (dictionary_size >= NumericLimits::Maximum()) { + throw InvalidInputException("The total length of the dictionary exceeds a 4 byte value (uint32_t), failed " + "to export VARIANT to Parquet"); + } + + auto byte_length = CalculateByteLength(dictionary_size); + auto total_length = 1 + (byte_length * (dictionary_count + 2)) + dictionary_size; + + metadata_data[row] = StringVector::EmptyString(metadata, total_length); + auto &metadata_blob = metadata_data[row]; + auto metadata_blob_data = metadata_blob.GetDataWriteable(); + + metadata_blob_data[0] = static_cast(EncodeMetadataHeader(byte_length)); + memcpy(metadata_blob_data + 1, const_data_ptr_cast(&dictionary_count), byte_length); + + auto offset_ptr = metadata_blob_data + 1 + byte_length; + auto string_ptr = + metadata_blob_data + 1 + byte_length + (NumericCast(dictionary_count + 1) * byte_length); + idx_t total_offset = 0; + for (idx_t i = 0; i < dictionary_count; i++) { + memcpy(offset_ptr + (i * byte_length), const_data_ptr_cast(&total_offset), byte_length); + auto &key = variant.GetKey(row, i); + + memcpy(string_ptr + total_offset, key.GetData(), key.GetSize()); + total_offset += key.GetSize(); + } + memcpy(offset_ptr + (NumericCast(dictionary_count) * byte_length), const_data_ptr_cast(&total_offset), + byte_length); + D_ASSERT(offset_ptr + (NumericCast(dictionary_count + 1) * byte_length) == string_ptr); + D_ASSERT(string_ptr + total_offset == metadata_blob_data + total_length); + metadata_blob.SetSizeAndFinalize(NumericCast(total_length), total_length); + +#ifdef DEBUG + auto decoded_metadata = VariantMetadata(metadata_blob); + D_ASSERT(decoded_metadata.strings.size() == dictionary_count); + for (idx_t i = 0; i < dictionary_count; i++) { + D_ASSERT(decoded_metadata.strings[i] == variant.GetKey(row, i).GetString()); + } +#endif + } +} + +namespace { + +static unordered_set GetVariantType(const LogicalType &type) { + if (type.id() == LogicalTypeId::ANY) { + return {}; + } + switch (type.id()) { + case LogicalTypeId::STRUCT: + return {VariantLogicalType::OBJECT}; + case LogicalTypeId::LIST: + return {VariantLogicalType::ARRAY}; + case LogicalTypeId::BOOLEAN: + return {VariantLogicalType::BOOL_TRUE, VariantLogicalType::BOOL_FALSE}; + case LogicalTypeId::TINYINT: + return {VariantLogicalType::INT8}; + case LogicalTypeId::SMALLINT: + return {VariantLogicalType::INT16}; + case LogicalTypeId::INTEGER: + return {VariantLogicalType::INT32}; + case LogicalTypeId::BIGINT: + return {VariantLogicalType::INT64}; + case LogicalTypeId::FLOAT: + return {VariantLogicalType::FLOAT}; + case LogicalTypeId::DOUBLE: + return {VariantLogicalType::DOUBLE}; + case LogicalTypeId::DECIMAL: + return {VariantLogicalType::DECIMAL}; + case LogicalTypeId::DATE: + return {VariantLogicalType::DATE}; + case LogicalTypeId::TIME: + return {VariantLogicalType::TIME_MICROS}; + case LogicalTypeId::TIMESTAMP_TZ: + return {VariantLogicalType::TIMESTAMP_MICROS_TZ}; + case LogicalTypeId::TIMESTAMP_TZ_NS: + return {VariantLogicalType::TIMESTAMP_NANOS_TZ}; + case LogicalTypeId::TIMESTAMP: + return {VariantLogicalType::TIMESTAMP_MICROS}; + case LogicalTypeId::TIMESTAMP_NS: + return {VariantLogicalType::TIMESTAMP_NANOS}; + case LogicalTypeId::BLOB: + return {VariantLogicalType::BLOB}; + case LogicalTypeId::VARCHAR: + return {VariantLogicalType::VARCHAR}; + case LogicalTypeId::UUID: + return {VariantLogicalType::UUID}; + default: + throw BinderException("Type '%s' can't be translated to a VARIANT type", type.ToString()); + } +} + +struct ParquetVariantShreddingState : public VariantShreddingState { +public: + ParquetVariantShreddingState(const LogicalType &type, idx_t total_count) + : VariantShreddingState(type, total_count), variant_types(GetVariantType(type)) { + } + +public: + const unordered_set &GetVariantTypes() override { + return variant_types; + } + +private: + unordered_set variant_types; +}; + +struct ParquetVariantShredding : public VariantShredding { + ParquetVariantShredding() { + // for parquet untyped ("value") comes before typed ("typed_value") + untyped_value_index = 0; + typed_value_index = 1; + } + + void WriteVariantValues(UnifiedVariantVectorData &variant, Vector &result, optional_ptr sel, + optional_ptr value_index_sel, + optional_ptr result_sel, idx_t count) override; + +protected: + void WriteMissingField(Vector &vector, idx_t index) override; +}; + +} // namespace + +vector GetChildIndices(const UnifiedVariantVectorData &variant, idx_t row, const VariantNestedData &nested_data, + optional_ptr shredding_state) { + vector child_indices; + if (!shredding_state || shredding_state->type.id() != LogicalTypeId::STRUCT) { + for (idx_t i = 0; i < nested_data.child_count; i++) { + child_indices.push_back(i); + } + return child_indices; + } + auto shredded_fields = shredding_state->ObjectFields(); + + for (idx_t i = 0; i < nested_data.child_count; i++) { + auto keys_index = variant.GetKeysIndex(row, i + nested_data.children_idx); + auto &key = variant.GetKey(row, keys_index); + + if (shredded_fields.count(key.GetString())) { + //! This field is shredded on, omit it from the value + continue; + } + child_indices.push_back(i); + } + return child_indices; +} + +static idx_t AnalyzeValueData(const UnifiedVariantVectorData &variant, idx_t row, uint32_t values_index, + vector &offsets, optional_ptr shredding_state) { + idx_t total_size = 0; + //! Every value has at least a value header + total_size++; + + idx_t offset_size = offsets.size(); + VariantLogicalType type_id = VariantLogicalType::VARIANT_NULL; + if (variant.RowIsValid(row)) { + type_id = variant.GetTypeId(row, values_index); + } + switch (type_id) { + case VariantLogicalType::OBJECT: { + auto nested_data = VariantUtils::DecodeNestedData(variant, row, values_index); + + //! Calculate value and key offsets for all children + idx_t total_offset = 0; + uint32_t highest_keys_index = 0; + + auto child_indices = GetChildIndices(variant, row, nested_data, shredding_state); + if (nested_data.child_count && child_indices.empty()) { + //! All fields of the object are shredded, omit the object entirely + return 0; + } + + auto num_elements = child_indices.size(); + offsets.resize(offset_size + num_elements + 1); + + for (idx_t entry = 0; entry < child_indices.size(); entry++) { + auto i = child_indices[entry]; + auto keys_index = variant.GetKeysIndex(row, i + nested_data.children_idx); + auto values_index = variant.GetValuesIndex(row, i + nested_data.children_idx); + offsets[offset_size + entry] = NumericCast(total_offset); + + total_offset += AnalyzeValueData(variant, row, values_index, offsets, nullptr); + highest_keys_index = MaxValue(highest_keys_index, keys_index); + } + offsets[offset_size + num_elements] = NumericCast(total_offset); + + //! Calculate the sizes for the objects value data + auto field_id_size = CalculateByteLength(highest_keys_index); + auto field_offset_size = CalculateByteLength(total_offset); + const bool is_large = num_elements > NumericLimits::Maximum(); + + //! Now add the sizes for the objects value data + if (is_large) { + total_size += sizeof(uint32_t); + } else { + total_size += sizeof(uint8_t); + } + total_size += num_elements * field_id_size; + total_size += (num_elements + 1) * field_offset_size; + total_size += total_offset; + break; + } + case VariantLogicalType::ARRAY: { + auto nested_data = VariantUtils::DecodeNestedData(variant, row, values_index); + + idx_t total_offset = 0; + offsets.resize(offset_size + nested_data.child_count + 1); + for (idx_t i = 0; i < nested_data.child_count; i++) { + auto values_index = variant.GetValuesIndex(row, i + nested_data.children_idx); + offsets[offset_size + i] = NumericCast(total_offset); + + total_offset += AnalyzeValueData(variant, row, values_index, offsets, nullptr); + } + offsets[offset_size + nested_data.child_count] = NumericCast(total_offset); + + auto field_offset_size = CalculateByteLength(total_offset); + auto num_elements = nested_data.child_count; + const bool is_large = num_elements > NumericLimits::Maximum(); + + if (is_large) { + total_size += sizeof(uint32_t); + } else { + total_size += sizeof(uint8_t); + } + total_size += (num_elements + 1) * field_offset_size; + total_size += total_offset; + break; + } + case VariantLogicalType::BLOB: + case VariantLogicalType::VARCHAR: { + auto string_value = VariantUtils::DecodeStringData(variant, row, values_index); + total_size += string_value.GetSize(); + if (type_id == VariantLogicalType::BLOB || string_value.GetSize() >= 64) { + //! Save as regular string value + total_size += sizeof(uint32_t); + } + break; + } + case VariantLogicalType::VARIANT_NULL: + case VariantLogicalType::BOOL_TRUE: + case VariantLogicalType::BOOL_FALSE: + break; + case VariantLogicalType::INT8: + total_size += sizeof(uint8_t); + break; + case VariantLogicalType::INT16: + total_size += sizeof(uint16_t); + break; + case VariantLogicalType::INT32: + total_size += sizeof(uint32_t); + break; + case VariantLogicalType::INT64: + total_size += sizeof(uint64_t); + break; + case VariantLogicalType::FLOAT: + total_size += sizeof(float); + break; + case VariantLogicalType::DOUBLE: + total_size += sizeof(double); + break; + case VariantLogicalType::DECIMAL: { + auto decimal_data = VariantUtils::DecodeDecimalData(variant, row, values_index); + total_size += 1; + if (decimal_data.width <= 9) { + total_size += sizeof(int32_t); + } else if (decimal_data.width <= 18) { + total_size += sizeof(int64_t); + } else if (decimal_data.width <= 38) { + total_size += sizeof(uhugeint_t); + } else { + throw InvalidInputException("Can't convert VARIANT DECIMAL(%d, %d) to Parquet VARIANT", decimal_data.width, + decimal_data.scale); + } + break; + } + case VariantLogicalType::UUID: + total_size += sizeof(uhugeint_t); + break; + case VariantLogicalType::DATE: + total_size += sizeof(uint32_t); + break; + case VariantLogicalType::TIME_MICROS: + case VariantLogicalType::TIMESTAMP_MICROS: + case VariantLogicalType::TIMESTAMP_NANOS: + case VariantLogicalType::TIMESTAMP_MICROS_TZ: + total_size += sizeof(uint64_t); + break; + case VariantLogicalType::UINT8: + // store as int16_t + total_size += sizeof(int16_t); + break; + case VariantLogicalType::UINT16: + // store as int32_t + total_size += sizeof(int32_t); + break; + case VariantLogicalType::UINT32: + case VariantLogicalType::UINT64: + case VariantLogicalType::UINT128: + case VariantLogicalType::INT128: + // try to store as int64_t - fail if it doesn't fit + total_size += sizeof(int64_t); + break; + case VariantLogicalType::INTERVAL: + case VariantLogicalType::BIGNUM: + case VariantLogicalType::BITSTRING: + case VariantLogicalType::TIMESTAMP_MILIS: + case VariantLogicalType::TIMESTAMP_SEC: + case VariantLogicalType::TIME_MICROS_TZ: + case VariantLogicalType::TIME_NANOS: + default: + throw InvalidInputException("Can't convert VARIANT of type '%s' to Parquet VARIANT", + EnumUtil::ToString(type_id)); + } + + return total_size; +} + +template +void WritePrimitiveTypeHeader(data_ptr_t &value_data) { + uint8_t value_header = 0; + value_header |= static_cast(VariantBasicType::PRIMITIVE); + value_header |= static_cast(TYPE_ID) << 2; + + *value_data = value_header; + value_data++; +} + +struct VariantSimpleCopy { + template + static void CopyValue(const_data_ptr_t source, data_ptr_t target) { + memcpy(target, source, sizeof(T)); + } +}; + +template +struct VariantSimpleConversion { + template + static void CopyValue(const_data_ptr_t source, data_ptr_t target) { + auto src = Load(source); + Store(static_cast(src), target); + } +}; + +template +struct VariantTryConvert { + template + static void CopyValue(const_data_ptr_t source, data_ptr_t target) { + auto src = Load(source); + Store(Cast::Operation(src), target); + } +}; + +template +void CopySimplePrimitiveData(const UnifiedVariantVectorData &variant, data_ptr_t &value_data, idx_t row, + uint32_t values_index) { + auto byte_offset = variant.GetByteOffset(row, values_index); + auto data = const_data_ptr_cast(variant.GetData(row).GetData()); + auto ptr = data + byte_offset; + OP::template CopyValue(ptr, value_data); + value_data += sizeof(T); +} + +void CopyUUIDData(const UnifiedVariantVectorData &variant, data_ptr_t &value_data, idx_t row, uint32_t values_index) { + auto byte_offset = variant.GetByteOffset(row, values_index); + auto data = const_data_ptr_cast(variant.GetData(row).GetData()); + auto ptr = data + byte_offset; + + auto uuid = Load(ptr); + BaseUUID::ToBlob(uuid, value_data); + value_data += sizeof(uhugeint_t); +} + +static void WritePrimitiveValueData(const UnifiedVariantVectorData &variant, idx_t row, uint32_t values_index, + data_ptr_t &value_data, const vector &offsets, idx_t &offset_index) { + VariantLogicalType type_id = VariantLogicalType::VARIANT_NULL; + if (variant.RowIsValid(row)) { + type_id = variant.GetTypeId(row, values_index); + } + + D_ASSERT(type_id != VariantLogicalType::OBJECT && type_id != VariantLogicalType::ARRAY); + switch (type_id) { + case VariantLogicalType::BLOB: + case VariantLogicalType::VARCHAR: { + auto string_value = VariantUtils::DecodeStringData(variant, row, values_index); + auto string_size = string_value.GetSize(); + if (type_id == VariantLogicalType::BLOB || string_size >= 64) { + if (type_id == VariantLogicalType::BLOB) { + WritePrimitiveTypeHeader(value_data); + } else { + WritePrimitiveTypeHeader(value_data); + } + Store(NumericCast(string_size), value_data); + value_data += sizeof(uint32_t); + } else { + uint8_t value_header = 0; + value_header |= static_cast(VariantBasicType::SHORT_STRING); + value_header |= static_cast(string_size) << 2; + + *value_data = value_header; + value_data++; + } + memcpy(value_data, reinterpret_cast(string_value.GetData()), string_size); + value_data += string_size; + break; + } + case VariantLogicalType::VARIANT_NULL: + WritePrimitiveTypeHeader(value_data); + break; + case VariantLogicalType::BOOL_TRUE: + WritePrimitiveTypeHeader(value_data); + break; + case VariantLogicalType::BOOL_FALSE: + WritePrimitiveTypeHeader(value_data); + break; + case VariantLogicalType::INT8: + WritePrimitiveTypeHeader(value_data); + CopySimplePrimitiveData(variant, value_data, row, values_index); + break; + case VariantLogicalType::INT16: + WritePrimitiveTypeHeader(value_data); + CopySimplePrimitiveData(variant, value_data, row, values_index); + break; + case VariantLogicalType::INT32: + WritePrimitiveTypeHeader(value_data); + CopySimplePrimitiveData(variant, value_data, row, values_index); + break; + case VariantLogicalType::INT64: + WritePrimitiveTypeHeader(value_data); + CopySimplePrimitiveData(variant, value_data, row, values_index); + break; + case VariantLogicalType::FLOAT: + WritePrimitiveTypeHeader(value_data); + CopySimplePrimitiveData(variant, value_data, row, values_index); + break; + case VariantLogicalType::DOUBLE: + WritePrimitiveTypeHeader(value_data); + CopySimplePrimitiveData(variant, value_data, row, values_index); + break; + case VariantLogicalType::UUID: + WritePrimitiveTypeHeader(value_data); + CopyUUIDData(variant, value_data, row, values_index); + break; + case VariantLogicalType::DATE: + WritePrimitiveTypeHeader(value_data); + CopySimplePrimitiveData(variant, value_data, row, values_index); + break; + case VariantLogicalType::TIME_MICROS: + WritePrimitiveTypeHeader(value_data); + CopySimplePrimitiveData(variant, value_data, row, values_index); + break; + case VariantLogicalType::TIMESTAMP_MICROS: + WritePrimitiveTypeHeader(value_data); + CopySimplePrimitiveData(variant, value_data, row, values_index); + break; + case VariantLogicalType::TIMESTAMP_NANOS: + WritePrimitiveTypeHeader(value_data); + CopySimplePrimitiveData(variant, value_data, row, values_index); + break; + case VariantLogicalType::TIMESTAMP_MICROS_TZ: + WritePrimitiveTypeHeader(value_data); + CopySimplePrimitiveData(variant, value_data, row, values_index); + break; + case VariantLogicalType::DECIMAL: { + auto decimal_data = VariantUtils::DecodeDecimalData(variant, row, values_index); + + if (decimal_data.width > 38) { + throw InvalidInputException("Can't convert VARIANT DECIMAL(%d, %d) to Parquet VARIANT", decimal_data.width, + decimal_data.scale); + } else if (decimal_data.width <= 4) { + // DuckDB uses INT16 to store small decimals, but parquet only supports DECIMAL4 at minimum, so here we + // promote to INT32. + WritePrimitiveTypeHeader(value_data); + Store(NumericCast(decimal_data.scale), value_data); + value_data++; + const int32_t promoted = Load(decimal_data.value_ptr); + Store(promoted, value_data); + value_data += sizeof(int32_t); + } else if (decimal_data.width <= 9) { + WritePrimitiveTypeHeader(value_data); + Store(NumericCast(decimal_data.scale), value_data); + value_data++; + memcpy(value_data, decimal_data.value_ptr, sizeof(int32_t)); + value_data += sizeof(int32_t); + } else if (decimal_data.width <= 18) { + WritePrimitiveTypeHeader(value_data); + Store(NumericCast(decimal_data.scale), value_data); + value_data++; + memcpy(value_data, decimal_data.value_ptr, sizeof(int64_t)); + value_data += sizeof(int64_t); + } else if (decimal_data.width <= 38) { + WritePrimitiveTypeHeader(value_data); + Store(NumericCast(decimal_data.scale), value_data); + value_data++; + memcpy(value_data, decimal_data.value_ptr, sizeof(hugeint_t)); + value_data += sizeof(hugeint_t); + } else { + throw InternalException( + "Uncovered VARIANT(DECIMAL) -> Parquet VARIANT conversion for type 'DECIMAL(%d, %d)'", + decimal_data.width, decimal_data.scale); + } + break; + } + case VariantLogicalType::UINT8: + WritePrimitiveTypeHeader(value_data); + CopySimplePrimitiveData>(variant, value_data, row, values_index); + break; + case VariantLogicalType::UINT16: + WritePrimitiveTypeHeader(value_data); + CopySimplePrimitiveData>(variant, value_data, row, values_index); + break; + case VariantLogicalType::UINT32: + WritePrimitiveTypeHeader(value_data); + CopySimplePrimitiveData>(variant, value_data, row, values_index); + break; + case VariantLogicalType::UINT64: + WritePrimitiveTypeHeader(value_data); + CopySimplePrimitiveData>(variant, value_data, row, values_index); + break; + case VariantLogicalType::UINT128: + WritePrimitiveTypeHeader(value_data); + CopySimplePrimitiveData>(variant, value_data, row, values_index); + break; + case VariantLogicalType::INT128: + WritePrimitiveTypeHeader(value_data); + CopySimplePrimitiveData>(variant, value_data, row, values_index); + break; + case VariantLogicalType::INTERVAL: + case VariantLogicalType::BIGNUM: + case VariantLogicalType::BITSTRING: + case VariantLogicalType::TIMESTAMP_MILIS: + case VariantLogicalType::TIMESTAMP_SEC: + case VariantLogicalType::TIME_MICROS_TZ: + case VariantLogicalType::TIME_NANOS: + default: + throw InvalidInputException("Can't convert VARIANT of type '%s' to Parquet VARIANT", + EnumUtil::ToString(type_id)); + } +} + +static void WriteValueData(const UnifiedVariantVectorData &variant, idx_t row, uint32_t values_index, + data_ptr_t &value_data, const vector &offsets, idx_t &offset_index, + optional_ptr shredding_state) { + VariantLogicalType type_id = VariantLogicalType::VARIANT_NULL; + if (variant.RowIsValid(row)) { + type_id = variant.GetTypeId(row, values_index); + } + if (type_id == VariantLogicalType::OBJECT) { + auto nested_data = VariantUtils::DecodeNestedData(variant, row, values_index); + + //! -- Object value header -- + + auto child_indices = GetChildIndices(variant, row, nested_data, shredding_state); + if (nested_data.child_count && child_indices.empty()) { + throw InternalException( + "The entire should be omitted, should have been handled by the Analyze step already"); + } + auto num_elements = child_indices.size(); + + //! Determine the 'field_id_size' + uint32_t highest_keys_index = 0; + for (auto &i : child_indices) { + auto keys_index = variant.GetKeysIndex(row, i + nested_data.children_idx); + highest_keys_index = MaxValue(highest_keys_index, keys_index); + } + auto field_id_size = CalculateByteLength(highest_keys_index); + + uint32_t last_offset = 0; + if (num_elements) { + last_offset = offsets[offset_index + num_elements]; + } + offset_index += num_elements + 1; + auto field_offset_size = CalculateByteLength(last_offset); + + const bool is_large = num_elements > NumericLimits::Maximum(); + + uint8_t value_header = 0; + value_header |= static_cast(VariantBasicType::OBJECT); + value_header |= static_cast(is_large) << 6; + value_header |= (static_cast(field_id_size) - 1) << 4; + value_header |= (static_cast(field_offset_size) - 1) << 2; + +#ifdef DEBUG + auto object_value_header = VariantValueMetadata::FromHeaderByte(value_header); + D_ASSERT(object_value_header.basic_type == VariantBasicType::OBJECT); + D_ASSERT(object_value_header.is_large == is_large); + D_ASSERT(object_value_header.field_offset_size == field_offset_size); + D_ASSERT(object_value_header.field_id_size == field_id_size); +#endif + + *value_data = value_header; + value_data++; + + //! Write the 'num_elements' + if (is_large) { + Store(static_cast(num_elements), value_data); + value_data += sizeof(uint32_t); + } else { + Store(static_cast(num_elements), value_data); + value_data += sizeof(uint8_t); + } + + //! Write the 'field_id' entries + for (auto &i : child_indices) { + auto keys_index = variant.GetKeysIndex(row, i + nested_data.children_idx); + memcpy(value_data, reinterpret_cast(&keys_index), field_id_size); + value_data += field_id_size; + } + + //! Write the 'field_offset' entries and the child 'value's + auto children_ptr = value_data + ((num_elements + 1) * field_offset_size); + idx_t total_offset = 0; + for (auto &i : child_indices) { + auto values_index = variant.GetValuesIndex(row, i + nested_data.children_idx); + + memcpy(value_data, reinterpret_cast(&total_offset), field_offset_size); + value_data += field_offset_size; + auto start_ptr = children_ptr; + WriteValueData(variant, row, values_index, children_ptr, offsets, offset_index, nullptr); + total_offset += NumericCast(children_ptr - start_ptr); + } + memcpy(value_data, reinterpret_cast(&total_offset), field_offset_size); + value_data += field_offset_size; + D_ASSERT(children_ptr - total_offset == value_data); + value_data = children_ptr; + } else if (type_id == VariantLogicalType::ARRAY) { + auto nested_data = VariantUtils::DecodeNestedData(variant, row, values_index); + + //! -- Array value header -- + + uint32_t last_offset = 0; + if (nested_data.child_count) { + last_offset = offsets[offset_index + nested_data.child_count]; + } + offset_index += nested_data.child_count + 1; + auto field_offset_size = CalculateByteLength(last_offset); + + auto num_elements = nested_data.child_count; + const bool is_large = num_elements > NumericLimits::Maximum(); + + uint8_t value_header = 0; + value_header |= static_cast(VariantBasicType::ARRAY); + value_header |= static_cast(is_large) << 4; + value_header |= (static_cast(field_offset_size) - 1) << 2; + +#ifdef DEBUG + auto array_value_header = VariantValueMetadata::FromHeaderByte(value_header); + D_ASSERT(array_value_header.basic_type == VariantBasicType::ARRAY); + D_ASSERT(array_value_header.is_large == is_large); + D_ASSERT(array_value_header.field_offset_size == field_offset_size); +#endif + + *value_data = value_header; + value_data++; + + //! Write the 'num_elements' + if (is_large) { + Store(static_cast(num_elements), value_data); + value_data += sizeof(uint32_t); + } else { + Store(static_cast(num_elements), value_data); + value_data += sizeof(uint8_t); + } + + //! Write the 'field_offset' entries and the child 'value's + auto children_ptr = value_data + ((num_elements + 1) * field_offset_size); + idx_t total_offset = 0; + for (idx_t i = 0; i < nested_data.child_count; i++) { + auto values_index = variant.GetValuesIndex(row, i + nested_data.children_idx); + + memcpy(value_data, reinterpret_cast(&total_offset), field_offset_size); + value_data += field_offset_size; + auto start_ptr = children_ptr; + WriteValueData(variant, row, values_index, children_ptr, offsets, offset_index, nullptr); + total_offset += NumericCast(children_ptr - start_ptr); + } + memcpy(value_data, reinterpret_cast(&total_offset), field_offset_size); + value_data += field_offset_size; + D_ASSERT(children_ptr - total_offset == value_data); + value_data = children_ptr; + } else { + WritePrimitiveValueData(variant, row, values_index, value_data, offsets, offset_index); + } +} + +static void CreateValues(UnifiedVariantVectorData &variant, Vector &value, optional_ptr sel, + optional_ptr value_index_sel, + optional_ptr result_sel, + optional_ptr shredding_state, idx_t count) { + auto &validity = FlatVector::ValidityMutable(value); + auto value_data = FlatVector::GetDataMutable(value); + + for (idx_t i = 0; i < count; i++) { + uint32_t value_index = 0; + if (value_index_sel) { + value_index = NumericCast(value_index_sel->get_index(i)); + } + + auto row = NumericCast(i); + if (sel) { + row = NumericCast(sel->get_index(i)); + } + + auto result_index = NumericCast(i); + if (result_sel) { + result_index = NumericCast(result_sel->get_index(i)); + } + + bool is_shredded = false; + if (variant.RowIsValid(row) && shredding_state && shredding_state->ValueIsShredded(variant, row, value_index)) { + shredding_state->SetShredded(row, value_index, result_index); + is_shredded = true; + if (shredding_state->type.id() != LogicalTypeId::STRUCT) { + //! Value is shredded, directly write a NULL to the 'value' if the type is not an OBJECT + //! When the type is OBJECT, all excess fields would still need to be written to the 'value' + validity.SetInvalid(result_index); + continue; + } + } + + //! The (relative) offsets for each value, in the case of nesting + vector offsets; + //! Determine the size of this 'value' blob + idx_t blob_length = AnalyzeValueData(variant, row, value_index, offsets, shredding_state); + if (!blob_length) { + //! This is only allowed to happen for a shredded OBJECT, where there are no excess fields to write for the + //! OBJECT + (void)is_shredded; + D_ASSERT(is_shredded); + validity.SetInvalid(result_index); + continue; + } + value_data[result_index] = StringVector::EmptyString(value, blob_length); + auto &value_blob = value_data[result_index]; + auto value_blob_data = reinterpret_cast(value_blob.GetDataWriteable()); + + idx_t offset_index = 0; + WriteValueData(variant, row, value_index, value_blob_data, offsets, offset_index, shredding_state); + D_ASSERT(data_ptr_cast(value_blob.GetDataWriteable() + blob_length) == value_blob_data); + value_blob.SetSizeAndFinalize(NumericCast(blob_length), blob_length); + } +} + +void ParquetVariantShredding::WriteMissingField(Vector &vector, idx_t index) { + //! The field is missing, set it to null + FlatVector::SetNull(vector, index, true); +} + +void ParquetVariantShredding::WriteVariantValues(UnifiedVariantVectorData &variant, Vector &result, + optional_ptr sel, + optional_ptr value_index_sel, + optional_ptr result_sel, idx_t count) { + optional_ptr value; + optional_ptr typed_value; + + auto &result_type = result.GetType(); + D_ASSERT(result_type.id() == LogicalTypeId::STRUCT); + auto &child_types = StructType::GetChildTypes(result_type); + auto &child_vectors = StructVector::GetEntries(result); + D_ASSERT(child_types.size() == child_vectors.size()); + for (idx_t i = 0; i < child_types.size(); i++) { + auto &name = child_types[i].first; + if (name == "value") { + value = &child_vectors[i]; + } else if (name == "typed_value") { + typed_value = &child_vectors[i]; + } + } + + if (typed_value) { + ParquetVariantShreddingState shredding_state(typed_value->GetType(), count); + CreateValues(variant, *value, sel, value_index_sel, result_sel, &shredding_state, count); + + SelectionVector null_values; + if (shredding_state.count) { + WriteTypedValues(variant, *typed_value, shredding_state.shredded_sel, shredding_state.values_index_sel, + shredding_state.result_sel, shredding_state.count); + //! 'shredding_state.result_sel' will always be a subset of 'result_sel', set the rows not in the subset to + //! NULL + idx_t sel_idx = 0; + for (idx_t i = 0; i < count; i++) { + auto original_index = result_sel ? result_sel->get_index(i) : i; + if (sel_idx < shredding_state.count && shredding_state.result_sel[sel_idx] == original_index) { + sel_idx++; + continue; + } + FlatVector::SetNull(*typed_value, original_index, true); + } + } else { + //! Set all rows of the typed_value to NULL, nothing is shredded on + for (idx_t i = 0; i < count; i++) { + FlatVector::SetNull(*typed_value, result_sel ? result_sel->get_index(i) : i, true); + } + } + } else { + CreateValues(variant, *value, sel, value_index_sel, result_sel, nullptr, count); + } +} + +void ParquetVariantConversion::ToParquetVariant(const Vector &variant_vec, idx_t count, Vector &result) { + // DuckDB Variant: + // - keys = VARCHAR[] + // - children = STRUCT(keys_index UINTEGER, values_index UINTEGER)[] + // - values = STRUCT(type_id UTINYINT, byte_offset UINTEGER)[] + // - data = BLOB + + // Parquet VARIANT: + // - metadata = BLOB + // - value = BLOB (+ typed_value when 'result' declares a shredded layout) + + RecursiveUnifiedVectorFormat recursive_format; + Vector::RecursiveToUnifiedFormat(variant_vec, recursive_format); + UnifiedVariantVectorData variant(recursive_format); + + auto &result_vectors = StructVector::GetEntries(result); + auto &metadata = result_vectors[0]; + CreateMetadata(variant, metadata, count); + + ParquetVariantShredding shredding; + shredding.WriteVariantValues(variant, result, nullptr, nullptr, nullptr, count); +} + +} // namespace duckdb diff --git a/src/duckdb/extension/parquet/reader/variant/parquet_variant_iterator.cpp b/src/duckdb/src/common/types/variant/parquet_variant_iterator.cpp similarity index 93% rename from src/duckdb/extension/parquet/reader/variant/parquet_variant_iterator.cpp rename to src/duckdb/src/common/types/variant/parquet_variant_iterator.cpp index 518086c7b..d85fc87dd 100644 --- a/src/duckdb/extension/parquet/reader/variant/parquet_variant_iterator.cpp +++ b/src/duckdb/src/common/types/variant/parquet_variant_iterator.cpp @@ -1,4 +1,4 @@ -#include "reader/variant/parquet_variant_iterator.hpp" +#include "duckdb/common/types/variant/parquet_variant_iterator.hpp" #include "duckdb/common/types/variant/variant_builder.hpp" #include "duckdb/common/vector/struct_vector.hpp" @@ -9,7 +9,8 @@ #include "duckdb/common/helper.hpp" #include "duckdb/common/exception.hpp" #include "duckdb/common/numeric_utils.hpp" -#include "reader/uuid_column_reader.hpp" +#include "duckdb/common/types/uuid.hpp" +#include "duckdb/common/types/interval.hpp" #include "utf8proc_wrapper.hpp" #include @@ -273,7 +274,7 @@ T CastDecimalValue(SRC value) { template T ReadBinaryUUID(const_data_ptr_t payload) { if constexpr (std::is_same::value) { - return UUIDValueConversion::ReadParquetUUID(payload); + return BaseUUID::FromBlob(payload); } else { throw InternalException("Variant UUID must be read as hugeint_t"); } @@ -507,6 +508,13 @@ T ParquetVariantNode::GetData() const { case VariantPrimitiveType::DECIMAL16: CheckBinaryRead(payload, sizeof(uint8_t) + sizeof(hugeint_t), binary_end); return ReadBinaryDecimalValue(value_metadata.primitive_type, payload); + case VariantPrimitiveType::TIME_NTZ_MICROS: { + auto micros = LoadChecked(payload, binary_end); + if (micros < 0 || micros > Interval::MICROS_PER_DAY) { + throw IOException("Corrupted VARIANT 'value' buffer, TIME value (%d) is out of range", micros); + } + return LoadChecked(payload, binary_end); + } default: //! Fixed-width primitives are stored in the canonical little-endian layout return LoadChecked(payload, binary_end); @@ -560,18 +568,36 @@ VariantDecimalProperties ParquetVariantNode::GetDecimalProperties() const { auto value_metadata = VariantValueMetadata::FromHeaderByte(binary[0]); auto payload = binary + 1; uint8_t scale = LoadChecked(payload, binary_end); + if (scale > Decimal::MAX_WIDTH_DECIMAL) { + throw IOException("Corrupted VARIANT 'value' buffer, decimal scale %d exceeds the maximum of %d", scale, + Decimal::MAX_WIDTH_DECIMAL); + } auto value_data = payload + sizeof(uint8_t); + uint32_t width; switch (value_metadata.primitive_type) { case VariantPrimitiveType::DECIMAL4: - return VariantDecimalProperties(ComputeDecimalWidth(LoadChecked(value_data, binary_end)), - scale); + width = ComputeDecimalWidth(LoadChecked(value_data, binary_end)); + break; case VariantPrimitiveType::DECIMAL8: - return VariantDecimalProperties(ComputeDecimalWidth(LoadChecked(value_data, binary_end)), - scale); - default: + width = ComputeDecimalWidth(LoadChecked(value_data, binary_end)); + break; + default: { D_ASSERT(value_metadata.primitive_type == VariantPrimitiveType::DECIMAL16); - return VariantDecimalProperties(DecimalWidth::max, scale); + CheckBinaryRead(value_data, sizeof(hugeint_t), binary_end); + hugeint_t value; + value.lower = Load(value_data); + value.upper = Load(value_data + sizeof(uint64_t)); + auto &limit = Hugeint::POWERS_OF_TEN[Decimal::MAX_WIDTH_DECIMAL]; + if (value >= limit || value <= -limit) { + throw IOException("Corrupted VARIANT 'value' buffer, DECIMAL16 value exceeds the maximum width of %d", + Decimal::MAX_WIDTH_DECIMAL); + } + width = Decimal::MAX_WIDTH_DECIMAL; + break; + } } + //! The scale can exceed the digits of the unscaled value (e.g. 0.005), the width must cover it + return VariantDecimalProperties(MaxValue(width, scale), scale); } ParquetObjectIterator ParquetVariantNode::GetObjectChildren(VariantIterationOrder order) const { @@ -597,6 +623,13 @@ ParquetArrayIterator ParquetVariantNode::GetArrayChildren() const { void ParquetObjectIterator::Finalize() { std::sort(ordered_entries.begin(), ordered_entries.end(), [](const ParquetObjectEntry &a, const ParquetObjectEntry &b) { return a.key < b.key; }); + // the keys of a Variant object have to be unique + for (idx_t i = 1; i < ordered_entries.size(); i++) { + if (ordered_entries[i].key == ordered_entries[i - 1].key) { + throw InvalidInputException("Invalid VARIANT: object contains the key \"%s\" more than once", + ordered_entries[i].key.GetString()); + } + } } ParquetObjectIterator::ParquetObjectIterator(const ParquetVariantIterator &state, const ShreddedGroupView &view, @@ -705,15 +738,4 @@ void ParquetVariantConversion::ConvertBinary(Vector &metadata_and_value, Vector BuildVariant(source, count, result); } -static void VariantBytesToVariantFunction(DataChunk &input, ExpressionState &state, Vector &result) { - ParquetVariantConversion::ConvertBinary(input.data[0], result, input.size()); -} - -ScalarFunction ParquetVariantConversion::GetBytesToVariantFunction() { - ScalarFunction function("variant_bytes_to_variant", {}, LogicalType::VARIANT(), VariantBytesToVariantFunction); - function.GetSignature().AddParameter("blob", LogicalType::BLOB); - function.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); - return function; -} - } // namespace duckdb diff --git a/src/duckdb/extension/parquet/reader/variant/variant_binary_decoder.cpp b/src/duckdb/src/common/types/variant/variant_binary_decoder.cpp similarity index 92% rename from src/duckdb/extension/parquet/reader/variant/variant_binary_decoder.cpp rename to src/duckdb/src/common/types/variant/variant_binary_decoder.cpp index 868f278aa..5114fe5ab 100644 --- a/src/duckdb/extension/parquet/reader/variant/variant_binary_decoder.cpp +++ b/src/duckdb/src/common/types/variant/variant_binary_decoder.cpp @@ -1,4 +1,5 @@ -#include "reader/variant/variant_binary_decoder.hpp" +#include "duckdb/common/types/variant/variant_binary_decoder.hpp" +#include "utf8proc_wrapper.hpp" #include @@ -88,7 +89,11 @@ VariantMetadata::VariantMetadata(const string_t &metadata) : metadata(metadata) if (data_start + last_offset + string_size > metadata_buffer_capacity) { throw IOException("Corrupted VARIANT 'metadata' buffer"); } - strings.emplace_back(reinterpret_cast(metadata_data + data_start + last_offset), string_size); + auto key_data = reinterpret_cast(metadata_data + data_start + last_offset); + if (!Utf8Proc::IsValid(key_data, string_size)) { + throw IOException("Corrupted VARIANT 'metadata' buffer, a dictionary key isn't valid UTF8"); + } + strings.emplace_back(key_data, string_size); last_offset = next_offset; } //! header byte + offsets region + string bytes diff --git a/src/duckdb/src/common/types/vector.cpp b/src/duckdb/src/common/types/vector.cpp index 7f4ba5fb6..ff8f572e7 100644 --- a/src/duckdb/src/common/types/vector.cpp +++ b/src/duckdb/src/common/types/vector.cpp @@ -521,55 +521,16 @@ void Vector::Serialize(Serializer &serializer, bool compressed_serialization) { if (!serializer.ShouldSerialize(StorageVersion::V1_3_0)) { compressed_serialization = false; } - if (compressed_serialization) { - auto vtype = GetVectorType(); - if (vtype == VectorType::DICTIONARY_VECTOR && DictionaryVector::DictionarySize(*this).IsValid()) { - auto dict = Vector::Ref(DictionaryVector::Child(*this)); - if (dict.GetVectorType() == VectorType::FLAT_VECTOR) { - idx_t dict_count = DictionaryVector::DictionarySize(*this).GetIndex(); - auto old_sel = DictionaryVector::SelVector(*this); - SelectionVector new_sel(count), used_sel(count), map_sel(dict_count); - - // dictionaries may be large (row-group level). A vector may use only a small part. - // So, restrict dict to the used_sel subset & remap old_sel into new_sel to the new dict positions - sel_t CODE_UNSEEN = static_cast(dict_count); - for (sel_t i = 0; i < dict_count; ++i) { - map_sel[i] = CODE_UNSEEN; // initialize with unused marker - } - idx_t used_count = 0; - for (idx_t i = 0; i < count; ++i) { - auto pos = old_sel[i]; - if (map_sel[pos] == CODE_UNSEEN) { - map_sel[pos] = static_cast(used_count); - used_sel[used_count++] = pos; - } - new_sel[i] = map_sel[pos]; - } - if (used_count * 2 < count) { // only serialize as a dict vector if that makes things smaller - auto sel_data = reinterpret_cast(new_sel.data()); - dict.Slice(used_sel, used_count); - serializer.WriteProperty(90, "vector_type", VectorType::DICTIONARY_VECTOR); - serializer.WriteProperty(91, "sel_vector", sel_data, sizeof(sel_t) * count); - serializer.WriteProperty(92, "dict_count", used_count); - return dict.Serialize(serializer, false); - } - } - } else if (vtype == VectorType::CONSTANT_VECTOR && count >= 1) { - serializer.WriteProperty(90, "vector_type", VectorType::CONSTANT_VECTOR); - // Resize to 1 so that size() == count == 1 during the recursive call, then restore - FlatVector::SetSize(*this, 1); - Vector::Serialize(serializer, false); // just serialize one value - FlatVector::SetSize(*this, count); - return; - } else if (vtype == VectorType::SEQUENCE_VECTOR) { - serializer.WriteProperty(90, "vector_type", VectorType::SEQUENCE_VECTOR); - auto &sequence = buffer->Cast(); - serializer.WriteProperty(91, "seq_start", sequence.start); - serializer.WriteProperty(92, "seq_increment", sequence.increment); - return; // for sequence vectors we do not serialize anything else - } else { - // TODO: other compressed vector types (SHREDDED, FSST) - } + if (compressed_serialization && GetVectorType() == VectorType::CONSTANT_VECTOR && count >= 1) { + serializer.WriteProperty(90, "vector_type", VectorType::CONSTANT_VECTOR); + // Resize to 1 so that size() == count == 1 during the recursive call, then restore + FlatVector::SetSize(*this, 1); + Vector::Serialize(serializer, false); // just serialize one value + FlatVector::SetSize(*this, count); + return; + } + if (buffer && buffer->TrySerialize(serializer, logical_type, compressed_serialization)) { + return; } ToUnifiedFormat(vdata); @@ -729,6 +690,19 @@ class StringDeserializeHolder : public AuxiliaryDataHolder { unique_ptr data; }; +static void CheckDeserializedListIndex(idx_t i, idx_t count) { + if (i >= count) { + throw SerializationException("Failed to deserialize vector: more entries than the vector size %llu", count); + } +} + +static void CheckDeserializedListCount(idx_t list_count, idx_t count) { + if (list_count != count) { + throw SerializationException("Failed to deserialize vector: expected %llu entries but got %llu", count, + list_count); + } +} + void Vector::Deserialize(Deserializer &deserializer, idx_t count) { auto &logical_type = GetType(); @@ -740,17 +714,8 @@ void Vector::Deserialize(Deserializer &deserializer, idx_t count) { Vector::Deserialize(deserializer, 1); // read a vector of size 1 Vector::SetVectorType(VectorType::CONSTANT_VECTOR); return; - } else if (vtype == VectorType::DICTIONARY_VECTOR) { - SelectionVector sel(count); - deserializer.ReadProperty(91, "sel_vector", reinterpret_cast(sel.data()), sizeof(sel_t) * count); - const auto dict_count = deserializer.ReadProperty(92, "dict_count"); - Vector::Deserialize(deserializer, dict_count); // deserialize the dictionary in this vector - Vector::Slice(sel, count); // will create a dictionary vector - return; - } else if (vtype == VectorType::SEQUENCE_VECTOR) { - const int64_t seq_start = deserializer.ReadProperty(91, "seq_start"); - const int64_t seq_increment = deserializer.ReadProperty(92, "seq_increment"); - Vector::Sequence(seq_start, seq_increment, count); + } else if (vtype != VectorType::FLAT_VECTOR) { + buffer = VectorBuffer::Deserialize(deserializer, vtype, logical_type, count); return; } @@ -782,20 +747,28 @@ void Vector::Deserialize(Deserializer &deserializer, idx_t count) { auto blobs = FlatVector::GetDataMutable(*this); if (geometry_format == GeometryStorageType::WKB) { + idx_t entry_count = 0; deserializer.ReadList(102, "data", [&](Deserializer::List &list, idx_t i) { + CheckDeserializedListIndex(i, count); + entry_count++; auto geom = list.ReadElement(); if (validity.RowIsValid(i)) { blobs[i] = StringVector::AddStringOrBlob(*this, geom); } }); + CheckDeserializedListCount(entry_count, count); } else if (geometry_format == GeometryStorageType::SPATIAL) { // Try to read old SPATIAL format and convert to new GEOMETRY format + idx_t entry_count = 0; deserializer.ReadList(102, "data", [&](Deserializer::List &list, idx_t i) { + CheckDeserializedListIndex(i, count); + entry_count++; auto blob = list.ReadElement(); if (validity.RowIsValid(i)) { Geometry::FromSpatialGeometry(blob, blobs[i], *this); } }); + CheckDeserializedListCount(entry_count, count); } else { throw InternalException("Unsupported geometry format in vector serialization"); } @@ -818,29 +791,43 @@ void Vector::Deserialize(Deserializer &deserializer, idx_t count) { auto byte_read_ptr = reinterpret_cast(byte_data_buffer->data.get()); StringVector::AddAuxiliaryData(*this, std::move(byte_data_buffer)); + idx_t remaining_bytes = byte_data_length.GetIndex(); for (idx_t i = 0; i < count; ++i) { if (!validity.RowIsValid(i)) { continue; } + if (lengths_read_ptr[i] > remaining_bytes) { + throw SerializationException( + "Failed to deserialize vector: string lengths exceed the serialized string data"); + } + remaining_bytes -= lengths_read_ptr[i]; strings[i] = string_t(byte_read_ptr, lengths_read_ptr[i]); byte_read_ptr += lengths_read_ptr[i]; } } else { // this is ye olde way of string serialization + idx_t entry_count = 0; deserializer.ReadList(102, "data", [&](Deserializer::List &list, idx_t i) { + CheckDeserializedListIndex(i, count); + entry_count++; auto str = list.ReadElement(); if (validity.RowIsValid(i)) { strings[i] = StringVector::AddStringOrBlob(*this, str); } }); + CheckDeserializedListCount(entry_count, count); } break; } case PhysicalType::STRUCT: { auto &entries = StructVector::GetEntries(*this); // Deserialize entries as a list + idx_t child_count = 0; deserializer.ReadList(103, "children", [&](Deserializer::List &list, idx_t i) { + CheckDeserializedListIndex(i, entries.size()); + child_count++; list.ReadObject([&](Deserializer &obj) { entries[i].Deserialize(obj, count); }); }); + CheckDeserializedListCount(child_count, entries.size()); break; } case PhysicalType::LIST: { @@ -851,12 +838,24 @@ void Vector::Deserialize(Deserializer &deserializer, idx_t count) { // Read the entries auto list_entries = FlatVector::GetDataMutable(*this); + idx_t entry_count = 0; deserializer.ReadList(105, "entries", [&](Deserializer::List &list, idx_t i) { + CheckDeserializedListIndex(i, count); + entry_count++; list.ReadObject([&](Deserializer &obj) { list_entries[i].offset = obj.ReadProperty(100, "offset"); list_entries[i].length = obj.ReadProperty(101, "length"); }); + if (!validity.RowIsValid(i)) { + list_entries[i] = list_entry_t(0, 0); + } else if (list_entries[i].offset > list_size || + list_entries[i].length > list_size - list_entries[i].offset) { + throw SerializationException("Failed to deserialize vector: list entry is out of range of the " + "child vector of size %llu", + list_size); + } }); + CheckDeserializedListCount(entry_count, count); // Read the child vector deserializer.ReadObject(106, "child", [&](Deserializer &obj) { @@ -867,6 +866,10 @@ void Vector::Deserialize(Deserializer &deserializer, idx_t count) { } case PhysicalType::ARRAY: { auto array_size = deserializer.ReadProperty(103, "array_size"); + if (array_size != ArrayType::GetSize(logical_type)) { + throw SerializationException("Failed to deserialize vector: array size %llu does not match type %s", + array_size, logical_type.ToString()); + } deserializer.ReadObject(104, "child", [&](Deserializer &obj) { auto &child = ArrayVector::GetChildMutable(*this); child.Deserialize(obj, array_size * count); diff --git a/src/duckdb/src/common/types/vector_buffer.cpp b/src/duckdb/src/common/types/vector_buffer.cpp index b4a8ecaa0..751027350 100644 --- a/src/duckdb/src/common/types/vector_buffer.cpp +++ b/src/duckdb/src/common/types/vector_buffer.cpp @@ -1,15 +1,18 @@ #include "duckdb/common/vector/array_vector.hpp" #include "duckdb/common/vector/constant_vector.hpp" +#include "duckdb/common/vector/dictionary_vector.hpp" #include "duckdb/common/vector/flat_vector.hpp" #include "duckdb/common/vector/fsst_vector.hpp" #include "duckdb/common/vector/list_vector.hpp" #include "duckdb/common/vector/map_vector.hpp" +#include "duckdb/common/vector/sequence_vector.hpp" #include "duckdb/common/vector/shredded_vector.hpp" #include "duckdb/common/vector/string_vector.hpp" #include "duckdb/common/vector/struct_vector.hpp" #include "duckdb/common/types/vector_buffer.hpp" #include "duckdb/common/assert.hpp" +#include "duckdb/common/enum_util.hpp" #include "duckdb/common/types/vector.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" #include "duckdb/storage/buffer/buffer_handle.hpp" @@ -145,6 +148,24 @@ void VectorBuffer::ToUnifiedFormat(UnifiedVectorFormat &format) const { throw InternalException("ToUnifiedFormat not supported for this buffer type - flatten first"); } +bool VectorBuffer::TrySerialize(Serializer &serializer, const LogicalType &type, bool compressed_serialization) const { + return false; +} + +buffer_ptr VectorBuffer::Deserialize(Deserializer &deserializer, VectorType vector_type, + const LogicalType &type, idx_t count) { + switch (vector_type) { + case VectorType::DICTIONARY_VECTOR: + return DictionaryBuffer::Deserialize(deserializer, type, count); + case VectorType::SEQUENCE_VECTOR: + return SequenceBuffer::Deserialize(deserializer, type, count); + case VectorType::SHREDDED_VECTOR: + return ShreddedVectorBuffer::Deserialize(deserializer, type, count); + default: + throw SerializationException("Unsupported vector type %s for deserialization", EnumUtil::ToString(vector_type)); + } +} + buffer_ptr VectorBuffer::Flatten(const LogicalType &type) const { auto result = FlattenSliceInternal(type, *FlatVector::IncrementalSelectionVector(), Size()); if (result && (result->Size() != Size())) { @@ -300,7 +321,7 @@ void VectorBuffer::Copy(const Vector &source_p, const SelectionVector &source_se auto &dict_sel = DictionaryVector::SelVector(source); // merge the selection vectors and verify the child if (sel.IsSet()) { - auto new_buffer = dict_sel.Slice(sel, copy_count); + auto new_buffer = dict_sel.Slice(sel, source_offset + copy_count); owned_sel.Initialize(new_buffer); sel_ref = owned_sel; } else { @@ -310,18 +331,24 @@ void VectorBuffer::Copy(const Vector &source_p, const SelectionVector &source_se break; } case VectorType::CONSTANT_VECTOR: - sel_ref = *ConstantVector::ZeroSelectionVector(copy_count, owned_sel); + sel_ref = *ConstantVector::ZeroSelectionVector(source_offset + copy_count, owned_sel); finished = true; break; case VectorType::FLAT_VECTOR: finished = true; break; default: { - // for exotic types we flatten followed by copying + // for exotic types we flatten followed by copying - only the copied rows are flattened, as e.g. list + // functions copy the children of every list at a different offset, and the selection vector might only + // cover the copied rows + D_ASSERT(source_count >= source_offset + copy_count); + SelectionVector range_sel(copy_count); + for (idx_t i = 0; i < copy_count; i++) { + range_sel.set_index(i, sel.get_index(source_offset + i)); + } Vector flattened_vector(Vector::Ref(source)); - flattened_vector.Flatten(sel, source_offset + source_count); - Copy(flattened_vector, *FlatVector::IncrementalSelectionVector(), source_count, source_offset, - target_offset, copy_count); + flattened_vector.Flatten(range_sel, copy_count); + Copy(flattened_vector, *FlatVector::IncrementalSelectionVector(), copy_count, 0, target_offset, copy_count); return; } } diff --git a/src/duckdb/src/common/types/vector_cache.cpp b/src/duckdb/src/common/types/vector_cache.cpp index 8ea04f54e..dff602471 100644 --- a/src/duckdb/src/common/types/vector_cache.cpp +++ b/src/duckdb/src/common/types/vector_cache.cpp @@ -94,6 +94,9 @@ class VectorCacheEntry { const LogicalType &GetType() { return type; } + idx_t GetCapacity() const { + return capacity; + } private: //! The type of the vector cache @@ -135,4 +138,11 @@ const LogicalType &VectorCache::GetType() const { return cache_entry->GetType(); } +optional_idx VectorCache::GetCapacity() const { + if (!cache_entry) { + return optional_idx(); + } + return optional_idx(cache_entry->GetCapacity()); +} + } // namespace duckdb diff --git a/src/duckdb/src/common/value_operations/comparison_operations.cpp b/src/duckdb/src/common/value_operations/comparison_operations.cpp index a89f890ad..aa4f313c4 100644 --- a/src/duckdb/src/common/value_operations/comparison_operations.cpp +++ b/src/duckdb/src/common/value_operations/comparison_operations.cpp @@ -187,7 +187,7 @@ static bool TemplatedBooleanOperation(const Value &left, const Value &right) { return false; } } - return true; + return ValuePositionComparator::TieBreak(left_children.size(), right_children.size()); } default: throw InternalException("Unimplemented type for value comparison"); diff --git a/src/duckdb/src/common/vector/constant_vector.cpp b/src/duckdb/src/common/vector/constant_vector.cpp index a55430e52..706d3830e 100644 --- a/src/duckdb/src/common/vector/constant_vector.cpp +++ b/src/duckdb/src/common/vector/constant_vector.cpp @@ -34,22 +34,60 @@ void ConstantVector::Reference(Vector &vector, const Value &value, count_t count vector.SetBuffer(CreateConstantBuffer(value, count)); } -void ConstantVector::SetNull(Vector &vector, count_t count) { - // try to re-use the buffer if possible +static bool IsReusableBufferType(VectorBufferType buffer_type) { + switch (buffer_type) { + case VectorBufferType::STANDARD_BUFFER: + case VectorBufferType::STRUCT_BUFFER: + case VectorBufferType::ARRAY_BUFFER: + case VectorBufferType::LIST_BUFFER: + case VectorBufferType::STRING_BUFFER: + return true; + default: + return false; + } +} + +//! Whether the buffer of the vector can be turned into a NULL constant in place: it must not be shared with other +//! vectors (e.g. a referenced payload), and the children that are nulled as well must be flat or constant +static bool CanSetNullInPlace(const Vector &vector) { auto &buffer_ref = vector.GetBufferRef(); - bool needs_new_buffer = !buffer_ref; - if (!needs_new_buffer) { - auto buffer_type = buffer_ref->GetBufferType(); - needs_new_buffer = - (buffer_type != VectorBufferType::STANDARD_BUFFER && buffer_type != VectorBufferType::STRUCT_BUFFER && - buffer_type != VectorBufferType::ARRAY_BUFFER && buffer_type != VectorBufferType::LIST_BUFFER && - buffer_type != VectorBufferType::STRING_BUFFER); + if (!buffer_ref || buffer_ref.use_count() > 1) { + return false; } - if (needs_new_buffer) { + switch (buffer_ref->GetBufferType()) { + case VectorBufferType::STANDARD_BUFFER: + case VectorBufferType::LIST_BUFFER: + case VectorBufferType::STRING_BUFFER: + return true; + case VectorBufferType::STRUCT_BUFFER: + for (auto &entry : StructVector::GetEntries(vector)) { + if (!CanSetNullInPlace(entry)) { + return false; + } + } + return true; + case VectorBufferType::ARRAY_BUFFER: { + auto child_type = ArrayVector::GetChild(vector).GetVectorType(); + return child_type == VectorType::FLAT_VECTOR || child_type == VectorType::CONSTANT_VECTOR; + } + default: + return false; + } +} + +void ConstantVector::SetNull(Vector &vector, count_t count) { + auto &buffer_ref = vector.GetBufferRef(); + if (!buffer_ref || !IsReusableBufferType(buffer_ref->GetBufferType())) { // we cannot re-use the buffer - refer a null-value which has code necessary to create a new buffer Reference(vector, Value(vector.GetType()), count); return; } + if (!CanSetNullInPlace(vector)) { + // the buffer is shared with other vectors (or has children that cannot be nulled) - use a fresh buffer + auto capacity = MaxValue(buffer_ref->Capacity(), 1); + vector.Initialize(VectorDataInitialization::UNINITIALIZED, capacity); + } + // re-use the buffer, so that e.g. the reserved capacity of a list child is kept vector.SetVectorType(VectorType::CONSTANT_VECTOR); FlatVector::SetSize(vector, count); SetNull(vector, true); @@ -125,6 +163,8 @@ void ConstantVector::Reference(Vector &vector, count_t count, const Vector &sour ListVector::SetListSize(vector, ListVector::GetListSize(source)); vector.SetVectorType(VectorType::CONSTANT_VECTOR); + auto &validity = vector.BufferMutable().GetValidityMask(); + validity.Set(0, true); FlatVector::SetSize(vector, count); break; } diff --git a/src/duckdb/src/common/vector/dictionary_vector.cpp b/src/duckdb/src/common/vector/dictionary_vector.cpp index b0cc8a492..b50cd4461 100644 --- a/src/duckdb/src/common/vector/dictionary_vector.cpp +++ b/src/duckdb/src/common/vector/dictionary_vector.cpp @@ -3,6 +3,8 @@ #include "duckdb/common/types/uuid.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" #include "duckdb/common/types/sel_cache.hpp" +#include "duckdb/common/serializer/deserializer.hpp" +#include "duckdb/common/serializer/serializer.hpp" namespace duckdb { @@ -181,15 +183,74 @@ const Vector &DictionaryVector::GetCachedHashes(const Vector &input) { D_ASSERT(CanCacheHashes(input)); const auto &entry = input.Buffer().Cast().GetEntry(); - lock_guard guard(entry.cached_hashes_lock); - - if (!entry.cached_hashes) { - // Uninitialized: hash the dictionary + CallOnce(entry.cached_hashes_once, [&] { const auto dictionary_size = DictionarySize(input).GetIndex(); - entry.cached_hashes = make_uniq(LogicalType::HASH, dictionary_size); - VectorOperations::Hash(entry.data, *entry.cached_hashes); - } + auto hashes = make_uniq(LogicalType::HASH, dictionary_size); + VectorOperations::Hash(entry.data, *hashes); + entry.cached_hashes = std::move(hashes); + }); return *entry.cached_hashes; } +bool DictionaryBuffer::TrySerialize(Serializer &serializer, const LogicalType &type, + bool compressed_serialization) const { + auto dictionary_size = GetDictionarySize(); + if (!compressed_serialization || !dictionary_size.IsValid()) { + return false; + } + auto dict = Vector::Ref(entry->data); + if (dict.GetVectorType() != VectorType::FLAT_VECTOR) { + return false; + } + auto count = Size(); + auto dict_count = dictionary_size.GetIndex(); + SelectionVector new_sel(count), used_sel(count), map_sel(dict_count); + + // dictionaries may be large (row-group level). A vector may use only a small part. + // So, restrict dict to the used_sel subset & remap old_sel into new_sel to the new dict positions + sel_t CODE_UNSEEN = static_cast(dict_count); + for (sel_t i = 0; i < dict_count; ++i) { + map_sel[i] = CODE_UNSEEN; // initialize with unused marker + } + idx_t used_count = 0; + for (idx_t i = 0; i < count; ++i) { + auto pos = sel_vector[i]; + if (map_sel[pos] == CODE_UNSEEN) { + map_sel[pos] = static_cast(used_count); + used_sel[used_count++] = pos; + } + new_sel[i] = map_sel[pos]; + } + if (used_count * 2 >= count) { + // only serialize as a dict vector if that makes things smaller + return false; + } + auto sel_data = reinterpret_cast(new_sel.data()); + dict.Slice(used_sel, used_count); + serializer.WriteProperty(90, "vector_type", VectorType::DICTIONARY_VECTOR); + serializer.WriteProperty(91, "sel_vector", sel_data, sizeof(sel_t) * count); + serializer.WriteProperty(92, "dict_count", used_count); + dict.Serialize(serializer, false); + return true; +} + +buffer_ptr DictionaryBuffer::Deserialize(Deserializer &deserializer, const LogicalType &type, + idx_t count) { + SelectionVector sel(count); + deserializer.ReadProperty(91, "sel_vector", reinterpret_cast(sel.data()), sizeof(sel_t) * count); + const auto dict_count = deserializer.ReadProperty(92, "dict_count"); + for (idx_t i = 0; i < count; i++) { + if (sel.get_index(i) >= dict_count) { + throw SerializationException("Failed to deserialize dictionary vector: selection index %llu is out of " + "range of the dictionary of size %llu", + sel.get_index(i), dict_count); + } + } + Vector dict(type, MaxValue(dict_count, STANDARD_VECTOR_SIZE)); + dict.Deserialize(deserializer, dict_count); + FlatVector::SetSize(dict, dict_count); + dict.Slice(sel, count); + return dict.GetBufferRef(); +} + } // namespace duckdb diff --git a/src/duckdb/src/common/vector/list_vector.cpp b/src/duckdb/src/common/vector/list_vector.cpp index 127125e54..124911264 100644 --- a/src/duckdb/src/common/vector/list_vector.cpp +++ b/src/duckdb/src/common/vector/list_vector.cpp @@ -329,7 +329,7 @@ void ListVector::SetListSize(Vector &vec, idx_t size) { if (child_buffer) { auto vtype = child_buffer->GetVectorType(); if ((vtype == VectorType::FLAT_VECTOR || vtype == VectorType::CONSTANT_VECTOR) && - size > child_buffer->Capacity()) { + size > child_buffer->Capacity() && size > child_buffer->Size()) { list_buffer.Reserve(size); } } diff --git a/src/duckdb/src/common/vector/sequence_vector.cpp b/src/duckdb/src/common/vector/sequence_vector.cpp index 049eac486..f2894c1cd 100644 --- a/src/duckdb/src/common/vector/sequence_vector.cpp +++ b/src/duckdb/src/common/vector/sequence_vector.cpp @@ -1,5 +1,7 @@ #include "duckdb/common/vector/sequence_vector.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" +#include "duckdb/common/serializer/deserializer.hpp" +#include "duckdb/common/serializer/serializer.hpp" namespace duckdb { @@ -49,4 +51,21 @@ void SequenceVector::GetSequence(const Vector &vector, int64_t &start, int64_t & increment = data.increment; } +bool SequenceBuffer::TrySerialize(Serializer &serializer, const LogicalType &type, + bool compressed_serialization) const { + if (!compressed_serialization) { + return false; + } + serializer.WriteProperty(90, "vector_type", VectorType::SEQUENCE_VECTOR); + serializer.WriteProperty(91, "seq_start", start); + serializer.WriteProperty(92, "seq_increment", increment); + return true; +} + +buffer_ptr SequenceBuffer::Deserialize(Deserializer &deserializer, const LogicalType &type, idx_t count) { + const auto seq_start = deserializer.ReadProperty(91, "seq_start"); + const auto seq_increment = deserializer.ReadProperty(92, "seq_increment"); + return make_buffer(seq_start, seq_increment, count_t(count)); +} + } // namespace duckdb diff --git a/src/duckdb/src/common/vector/shredded_vector.cpp b/src/duckdb/src/common/vector/shredded_vector.cpp index 5849b308b..72d20beb4 100644 --- a/src/duckdb/src/common/vector/shredded_vector.cpp +++ b/src/duckdb/src/common/vector/shredded_vector.cpp @@ -1,5 +1,8 @@ #include "duckdb/common/vector/shredded_vector.hpp" #include "duckdb/common/vector/struct_vector.hpp" +#include "duckdb/common/vector/flat_vector.hpp" +#include "duckdb/common/serializer/deserializer.hpp" +#include "duckdb/common/serializer/serializer.hpp" #include "duckdb/function/scalar/variant_utils.hpp" namespace duckdb { @@ -39,6 +42,36 @@ void ShreddedVectorBuffer::SetVectorType(VectorType new_vector_type) { throw InternalException("ShreddedVectorBuffer::SetVectorType is not implemented and shouldn't be reached"); } +bool ShreddedVectorBuffer::TrySerialize(Serializer &serializer, const LogicalType &type, + bool compressed_serialization) const { + // older versions cannot read shredded vectors - these are unshredded prior to serialization instead + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + return false; + } + serializer.WriteProperty(90, "vector_type", VectorType::SHREDDED_VECTOR); + serializer.WriteProperty(91, "shredded_type", shredded_data->GetType()); + serializer.WriteObject(92, "shredded_data", [&](Serializer &object) { + auto serialized_vector = Vector::Ref(*shredded_data); + serialized_vector.Serialize(object, compressed_serialization); + }); + return true; +} + +buffer_ptr ShreddedVectorBuffer::Deserialize(Deserializer &deserializer, const LogicalType &type, + idx_t count) { + if (type.id() != LogicalTypeId::VARIANT) { + throw SerializationException("Shredded vectors can only be deserialized as VARIANT vectors"); + } + auto shredded_type = deserializer.ReadProperty(91, "shredded_type"); + if (shredded_type.id() != LogicalTypeId::STRUCT || StructType::GetChildCount(shredded_type) != 2) { + throw SerializationException("Shredded vector data must be a struct with two children"); + } + Vector shredded_data(shredded_type, MaxValue(count, STANDARD_VECTOR_SIZE)); + deserializer.ReadObject(92, "shredded_data", [&](Deserializer &obj) { shredded_data.Deserialize(obj, count); }); + FlatVector::SetSize(shredded_data, count); + return make_buffer(shredded_data, count_t(count)); +} + Value ShreddedVectorBuffer::GetValue(const LogicalType &type, idx_t index) const { Vector input(*shredded_data, index, index + 1); Vector result(LogicalType::VARIANT(), 1); diff --git a/src/duckdb/src/common/vector/struct_vector.cpp b/src/duckdb/src/common/vector/struct_vector.cpp index 6d0619958..e258884dd 100644 --- a/src/duckdb/src/common/vector/struct_vector.cpp +++ b/src/duckdb/src/common/vector/struct_vector.cpp @@ -163,7 +163,7 @@ buffer_ptr VectorStructBuffer::SliceInternal(const LogicalType &ty buffer_ptr VectorStructBuffer::ConstantSliceInternal(const LogicalType &type, count_t count) { vector result_children; for (idx_t i = 0; i < children.size(); i++) { - result_children.emplace_back(Vector::Ref(children[i])); + result_children.emplace_back(children[i], *ConstantVector::ZeroSelectionVector(), count); } auto result = make_buffer(std::move(result_children), capacity_t(1ULL)); result->GetValidityMask().Set(0, validity.RowIsValid(0)); diff --git a/src/duckdb/src/common/vector_operations/comparison_operators.cpp b/src/duckdb/src/common/vector_operations/comparison_operators.cpp index 30a5963f0..ba00b6ecb 100644 --- a/src/duckdb/src/common/vector_operations/comparison_operators.cpp +++ b/src/duckdb/src/common/vector_operations/comparison_operators.cpp @@ -191,6 +191,13 @@ static void ComparatorToBoolean(const Vector &left, const Vector &right, Vector VectorOperations::Comparator(left, right, comparator_result, comp); const auto count = comparator_result.size(); auto cmp_data = comparator_result.Values(); + if (left.GetVectorType() == VectorType::CONSTANT_VECTOR && right.GetVectorType() == VectorType::CONSTANT_VECTOR) { + // the comparison of two constants is a constant + auto entry = cmp_data[0]; + auto value = entry.IsValid() ? Value::BOOLEAN(predicate(entry.GetValue())) : Value(LogicalType::BOOLEAN); + result.Reference(value, count_t(count)); + return; + } result.SetVectorType(VectorType::FLAT_VECTOR); auto result_data = FlatVector::Writer(result, count); for (idx_t i = 0; i < count; i++) { diff --git a/src/duckdb/src/common/virtual_file_system.cpp b/src/duckdb/src/common/virtual_file_system.cpp index 49b1c2230..f9539d997 100644 --- a/src/duckdb/src/common/virtual_file_system.cpp +++ b/src/duckdb/src/common/virtual_file_system.cpp @@ -11,7 +11,6 @@ #include "duckdb/common/multi_file/multi_file_list.hpp" #include "duckdb/common/mutex.hpp" #include "duckdb/main/database.hpp" -#include "duckdb/main/extension_entries.hpp" #include "duckdb/main/extension_helper.hpp" namespace duckdb { @@ -259,6 +258,10 @@ FileMetadata VirtualFileSystem::Stats(FileHandle &handle) { return handle.file_system.Stats(handle); } +optional VirtualFileSystem::GetStatsIfExists(const OpenFileInfo &file, optional_ptr opener) { + return FindFileSystem(file.path, opener).GetStatsIfExists(file, opener); +} + void VirtualFileSystem::Truncate(FileHandle &handle, int64_t new_size) { handle.file_system.Truncate(handle, new_size); } @@ -422,24 +425,7 @@ FileSystem &VirtualFileSystem::FindFileSystem(shared_ptr &re auto fs = FindFileSystemInternal(*registry, path); if (!fs && db_instance) { - string required_extension; - - for (const auto &entry : EXTENSION_FILE_PREFIXES) { - if (StringUtil::StartsWith(path, entry.name)) { - required_extension = entry.extension; - } - } - if (!required_extension.empty() && db_instance && !db_instance->ExtensionIsLoaded(required_extension)) { - if (!ExtensionHelper::CanAutoloadExtension(required_extension) || - !Settings::Get(*db_instance)) { - auto error_message = "File " + path + " requires the extension " + required_extension + " to be loaded"; - error_message = - ExtensionHelper::AddExtensionInstallHintToErrorMsg(*db_instance, error_message, required_extension); - throw MissingExtensionException(error_message); - } - // an extension is required to read this file, but it is not loaded - try to load it - ExtensionHelper::AutoLoadExtension(*db_instance, required_extension); - } + ExtensionHelper::AutoLoadExtensionForPath(*db_instance, path); // refresh the registry after loading extensions registry = file_system_registry.atomic_load(); diff --git a/src/duckdb/src/execution/aggregate_hashtable.cpp b/src/duckdb/src/execution/aggregate_hashtable.cpp index 30086378e..2b2b1ba5b 100644 --- a/src/duckdb/src/execution/aggregate_hashtable.cpp +++ b/src/duckdb/src/execution/aggregate_hashtable.cpp @@ -1,4 +1,6 @@ #include "duckdb/execution/aggregate_hashtable.hpp" + +#include "duckdb/common/error_data.hpp" #include "duckdb/main/client_context.hpp" #include "duckdb/storage/buffer_manager.hpp" @@ -55,8 +57,8 @@ GroupedAggregateHashTable::GroupedAggregateHashTable(ClientContext &context_p, A idx_t initial_capacity, idx_t radix_bits, TupleDataValidityType group_validity) : BaseAggregateHashTable(context_p, allocator, aggregate_objects_p, std::move(payload_types_p)), context(context_p), - radix_bits(radix_bits), count(0), capacity(0), sink_count(0), skip_lookups(false), enable_hll(false), - aggregate_allocator(make_shared_ptr(allocator)), state(*aggregate_allocator) { + radix_bits(radix_bits), count(0), capacity(0), aggregate_allocator(make_shared_ptr(allocator)), + state(*aggregate_allocator) { state.owner = this; state.initialized = true; clustered_state.all_clustered = AllAggregatesClustered(aggregate_objects_p); @@ -93,6 +95,7 @@ GroupedAggregateHashTable::GroupedAggregateHashTable(ClientContext &context_p, A } void GroupedAggregateHashTable::InitializePartitionedData() { + tuple_size_cache.count.SetInvalid(); if (!partitioned_data || RadixPartitioning::RadixBitsOfPowerOfTwo(partitioned_data->PartitionCount()) != radix_bits) { D_ASSERT(!partitioned_data || partitioned_data->Count() == 0); @@ -111,6 +114,7 @@ void GroupedAggregateHashTable::InitializePartitionedData() { } void GroupedAggregateHashTable::InitializeUnpartitionedData() { + tuple_size_cache.count.SetInvalid(); D_ASSERT(radix_bits >= UNPARTITIONED_RADIX_BITS_THRESHOLD); if (!unpartitioned_data) { unpartitioned_data = make_uniq(buffer_manager, layout_ptr, MemoryTag::HASH_TABLE, @@ -126,6 +130,16 @@ const PartitionedTupleData &GroupedAggregateHashTable::GetPartitionedData() cons return *partitioned_data; } +idx_t GroupedAggregateHashTable::GetSizeInBytes() const { + const auto materialized_count = GetMaterializedCount(); + if (!tuple_size_cache.count.IsValid() || tuple_size_cache.count.GetIndex() != materialized_count) { + tuple_size_cache.size = partitioned_data->GetAllocatedSizeInBytes() + + (unpartitioned_data ? unpartitioned_data->GetAllocatedSizeInBytes() : 0); + tuple_size_cache.count = materialized_count; + } + return tuple_size_cache.size + aggregate_allocator->AllocationSize() + hash_map.GetSize(); +} + unique_ptr GroupedAggregateHashTable::AcquirePartitionedData() { if (radix_bits >= UNPARTITIONED_RADIX_BITS_THRESHOLD) { // Flush/unpin unpartitioned data and append to partitioned data @@ -133,6 +147,8 @@ unique_ptr GroupedAggregateHashTable::AcquirePartitionedDa unpartitioned_data->FlushAppendState(state.unpartitioned_append_state); unpartitioned_data->Unpin(); unpartitioned_data->Repartition(context, *partitioned_data); + // Release scratch metadata along with the rows transferred to partitioned_data. + unpartitioned_data.reset(); } InitializeUnpartitionedData(); } @@ -161,6 +177,7 @@ void GroupedAggregateHashTable::Abandon() { // Start over ClearPointerTable(); count = 0; + adaptivity.BeginCycle(); // Resetting the id ensures the dict state is reset properly when needed state.dict_state.dictionary_id = string(); @@ -177,7 +194,12 @@ shared_ptr GroupedAggregateHashTable::GetAggregateAllocator() { } GroupedAggregateHashTable::~GroupedAggregateHashTable() { - Destroy(); + // destroying the states pins their blocks, which can fail when the table is torn down after an out-of-memory + // error - a destructor cannot throw, so the states leak in that case + try { + Destroy(); + } catch (...) { // NOLINT + } } void GroupedAggregateHashTable::Destroy() { @@ -198,17 +220,28 @@ void GroupedAggregateHashTable::DestroyAggregateData(PartitionedTupleData &data, PartitionedTupleDataAppendState &append_state) { // Call the destructor for each of the aggregates data.FlushAppendState(append_state); + ErrorData first_error; for (auto &data_collection : data.GetPartitions()) { if (data_collection->Count() == 0) { continue; } - TupleDataChunkIterator iterator(*data_collection, TupleDataPinProperties::DESTROY_AFTER_DONE, false); - auto &row_locations = iterator.GetChunkState().row_locations; - do { - RowOperations::DestroyStates(state.row_state, *layout_ptr, row_locations); - } while (iterator.Next()); + try { + TupleDataChunkIterator iterator(*data_collection, TupleDataPinProperties::DESTROY_AFTER_DONE, false); + auto &row_locations = iterator.GetChunkState().row_locations; + do { + RowOperations::DestroyStates(state.row_state, *layout_ptr, row_locations); + } while (iterator.Next()); + } catch (std::exception &ex) { + // keep releasing the other partitions, freeing their memory may let their states be destroyed + if (!first_error.HasError()) { + first_error = ErrorData(ex); + } + } data_collection->Reset(); } + if (first_error.HasError()) { + first_error.Throw(); + } } shared_ptr GroupedAggregateHashTable::GetLayoutPtr() { @@ -250,7 +283,7 @@ idx_t GroupedAggregateHashTable::ApplyBitMask(hash_t hash) const { void GroupedAggregateHashTable::Verify() { #ifdef DEBUG - if (skip_lookups) { + if (adaptivity.LookupsSkipped()) { return; } idx_t total_count = 0; @@ -279,10 +312,6 @@ idx_t GroupedAggregateHashTable::GetRadixBits() const { return radix_bits; } -idx_t GroupedAggregateHashTable::GetSinkCount() const { - return sink_count; -} - idx_t GroupedAggregateHashTable::GetMaterializedCount() const { auto result = partitioned_data->Count(); if (unpartitioned_data) { @@ -292,20 +321,17 @@ idx_t GroupedAggregateHashTable::GetMaterializedCount() const { } void GroupedAggregateHashTable::SkipLookups() { - skip_lookups = true; + adaptivity.SkipLookups(); } -void GroupedAggregateHashTable::EnableHLL(bool enable) { - enable_hll = enable; -} - -bool GroupedAggregateHashTable::HLLEnabled() const { - return enable_hll; +void GroupedAggregateHashTable::ResumeLookups() { + D_ASSERT(Count() == 0); + adaptivity.ResumeLookups(); } -idx_t GroupedAggregateHashTable::GetHLLUpperBound() const { - D_ASSERT(enable_hll); - return LossyNumericCast((1 + HyperLogLog::GetErrorRate()) * static_cast(hll.Count())); +void GroupedAggregateHashTable::EnableHLL(bool enable) { + D_ASSERT(!enable || adaptivity.HLLEnabled() || GetMaterializedCount() == 0); + adaptivity.EnableHLL(enable); } void GroupedAggregateHashTable::Resize(idx_t size) { @@ -314,9 +340,13 @@ void GroupedAggregateHashTable::Resize(idx_t size) { throw InternalException("Cannot downsize a non-empty hash table!"); } D_ASSERT(Count() == 0 || Count() == GetMaterializedCount()); + if (Count() == 0 && size == capacity && hash_map.GetSize() == size * sizeof(ht_entry_t)) { + return; + } + auto new_hash_map = buffer_manager.GetBufferAllocator().TryAllocateHuge(size * sizeof(ht_entry_t)); capacity = size; - hash_map = buffer_manager.GetBufferAllocator().Allocate(capacity * sizeof(ht_entry_t)); + hash_map = std::move(new_hash_map); entries = reinterpret_cast(hash_map.get()); ClearPointerTable(); bitmask = capacity - 1; @@ -394,7 +424,7 @@ idx_t GroupedAggregateHashTable::AddChunk(DataChunk &groups, DataChunk &payload, return 0; } - sink_count += groups.size(); + adaptivity.AddInput(groups.size()); groups.Hash(state.hashes); const auto new_group_count = FindOrCreateGroups(groups, state.hashes, state.addresses, state.new_groups); before_update(state.addresses, state.new_groups, new_group_count); @@ -416,7 +446,7 @@ idx_t GroupedAggregateHashTable::AddChunkAndGetNewGroups(DataChunk &groups, Data return 0; } - sink_count += groups.size(); + adaptivity.AddInput(groups.size()); groups.Hash(state.hashes); const auto new_group_count = FindOrCreateGroups(groups, state.hashes, state.addresses, state.new_groups); auto source_addresses = FlatVector::GetData(state.addresses); @@ -628,7 +658,7 @@ optional_idx GroupedAggregateHashTable::TryAddCompressedGroups(DataChunk &groups } idx_t GroupedAggregateHashTable::AddChunk(DataChunk &groups, DataChunk &payload, const unsafe_vector &filter) { - sink_count += groups.size(); + adaptivity.AddInput(groups.size()); // check if we can use an optimized path that utilizes compressed vectors auto result = TryAddCompressedGroups(groups, payload, filter); @@ -643,7 +673,7 @@ idx_t GroupedAggregateHashTable::AddChunk(DataChunk &groups, DataChunk &payload, bool GroupedAggregateHashTable::UpdateAggregatesClustered(DataChunk &payload, const unsafe_vector &filter, idx_t count, bool ht_offsets_valid) { - if (skip_lookups) { + if (adaptivity.LookupsSkipped()) { return false; } if (sizeof(uintptr_t) < sizeof(uint64_t)) { @@ -834,16 +864,15 @@ idx_t GroupedAggregateHashTable::FindOrCreateGroupsInternal(DataChunk &groups, V // convert all vectors to unified format TupleDataCollection::ToUnifiedFormat(state.partitioned_append_state.chunk_state, state.group_chunk); - if (enable_hll) { - hll.Update(group_hashes_v); - } - const auto hashes = group_hashes_v.Values(); addresses_v.Flatten(); const auto addresses = FlatVector::GetDataMutable(addresses_v); - if (skip_lookups) { + if (adaptivity.LookupsSkipped()) { + if (adaptivity.HLLEnabled()) { + adaptivity.ObserveHashes(group_hashes_v); + } // Just appending now partitioned_data->AppendUnified(state.partitioned_append_state, state.group_chunk, *FlatVector::IncrementalSelectionVector(), chunk_size); @@ -971,6 +1000,14 @@ idx_t GroupedAggregateHashTable::FindOrCreateGroupsInternal(DataChunk &groups, V throw InternalException("Maximum outer iteration count reached in GroupedAggregateHashTable"); } + if (adaptivity.HLLEnabled() && new_group_count > 0) { + if (new_group_count == chunk_size) { + adaptivity.ObserveHashes(group_hashes_v); + } else { + adaptivity.ObserveHashes(hashes, new_groups_out, new_group_count); + } + } + FlatVector::SetSize(addresses_v, chunk_size); count += new_group_count; return new_group_count; @@ -1165,7 +1202,7 @@ void GroupedAggregateHashTable::Combine(GroupedAggregateHashTable &other) { } } -void GroupedAggregateHashTable::Combine(TupleDataCollection &other_data, optional_ptr> progress) { +void GroupedAggregateHashTable::Combine(TupleDataCollection &other_data, optional_ptr> combined_chunks) { D_ASSERT(other_data.GetLayout().GetAggrWidth() == layout_ptr->GetAggrWidth()); D_ASSERT(other_data.GetLayout().GetDataWidth() == layout_ptr->GetDataWidth()); D_ASSERT(other_data.GetLayout().GetRowWidth() == layout_ptr->GetRowWidth()); @@ -1176,8 +1213,6 @@ void GroupedAggregateHashTable::Combine(TupleDataCollection &other_data, optiona FlushMoveState fm_state(other_data); - idx_t chunk_idx = 0; - const auto chunk_count = other_data.ChunkCount(); while (fm_state.Scan()) { // Check for interrupts with each chunk context.InterruptCheck(); @@ -1188,8 +1223,8 @@ void GroupedAggregateHashTable::Combine(TupleDataCollection &other_data, optiona RowOperations::DestroyStates(state.row_state, *layout_ptr, fm_state.scan_state.chunk_state.row_locations); } - if (progress) { - *progress = static_cast(++chunk_idx) / static_cast(chunk_count); + if (combined_chunks) { + combined_chunks->fetch_add(1, std::memory_order_relaxed); } } @@ -1241,6 +1276,7 @@ bool GroupedAggregateHashTable::Scan(AggregateHTScanState &scan_state, DataChunk } void GroupedAggregateHashTable::ResetForNewIteration(idx_t radix_bits_p) { + tuple_size_cache.count.SetInvalid(); // Save the previous iteration's group count before destroying aggregate states. // This lets us size the pointer table based on actual prior data rather than the // global sink capacity, which is typically much larger than recursive iteration sizes. @@ -1280,10 +1316,7 @@ void GroupedAggregateHashTable::ResetForNewIteration(idx_t radix_bits_p) { } count = 0; - sink_count = 0; - skip_lookups = false; - enable_hll = false; - hll = HyperLogLog(); + adaptivity.Reset(); state.dict_state.dictionary_id = string(); // Compute effective capacity based on the previous iteration's actual group count. diff --git a/src/duckdb/src/execution/column_binding_resolver.cpp b/src/duckdb/src/execution/column_binding_resolver.cpp index 975df565e..c078d0ff6 100644 --- a/src/duckdb/src/execution/column_binding_resolver.cpp +++ b/src/duckdb/src/execution/column_binding_resolver.cpp @@ -14,6 +14,25 @@ namespace duckdb { ColumnBindingResolver::ColumnBindingResolver(bool verify_only) : verify_only(verify_only) { } +ColumnBindingResolver::ColumnBindingResolver(vector bindings_p, vector types_p) + : bindings(std::move(bindings_p)), types(std::move(types_p)), verify_only(false) { +} + +//! Combine the types of both join sides for resolving expressions against the combined bindings. +//! An empty type vector for a non-empty binding set means type verification was skipped for that side (e.g., legacy +//! extension operators), in which case we skip it for the combined set too. +static vector CombineJoinTypes(const vector &left_bindings, + const vector &left_types, + const vector &right_bindings, + const vector &right_types) { + if ((left_types.empty() && !left_bindings.empty()) || (right_types.empty() && !right_bindings.empty())) { + return vector(); + } + auto result = left_types; + result.insert(result.end(), right_types.begin(), right_types.end()); + return result; +} + void ColumnBindingResolver::VisitOperator(LogicalOperator &op) { switch (op.type) { case LogicalOperatorType::LOGICAL_ASOF_JOIN: @@ -45,8 +64,7 @@ void ColumnBindingResolver::VisitOperator(LogicalOperator &op) { // combine bindings to resolve predicate auto combined_bindings = left_bindings; combined_bindings.insert(combined_bindings.end(), right_bindings.begin(), right_bindings.end()); - auto combined_types = left_types; - combined_types.insert(combined_types.end(), right_types.begin(), right_types.end()); + auto combined_types = CombineJoinTypes(left_bindings, left_types, right_bindings, right_types); bindings = combined_bindings; types = combined_types; @@ -92,12 +110,13 @@ void ColumnBindingResolver::VisitOperator(LogicalOperator &op) { } // arbitrary expressions are resolved against both join sides in logical left/right order - auto combined_bindings = comp_join.delim_flipped ? other_bindings : delim_bindings; + auto &left_bindings = comp_join.delim_flipped ? other_bindings : delim_bindings; auto &right_bindings = comp_join.delim_flipped ? delim_bindings : other_bindings; + auto combined_bindings = left_bindings; combined_bindings.insert(combined_bindings.end(), right_bindings.begin(), right_bindings.end()); - auto combined_types = comp_join.delim_flipped ? other_types : delim_types; + auto &left_types = comp_join.delim_flipped ? other_types : delim_types; auto &right_types = comp_join.delim_flipped ? delim_types : other_types; - combined_types.insert(combined_types.end(), right_types.begin(), right_types.end()); + auto combined_types = CombineJoinTypes(left_bindings, left_types, right_bindings, right_types); bindings = std::move(combined_bindings); types = std::move(combined_types); for (auto &cond : comp_join.conditions) { @@ -228,6 +247,7 @@ unique_ptr ColumnBindingResolver::VisitReplace(BoundColumnRefExpress for (idx_t i = 0; i < bindings.size(); i++) { if (expr.Binding() == bindings[i]) { if (!types.empty()) { + // LCOV_EXCL_START if (bindings.size() != types.size()) { throw InternalException( "Failed to bind column reference %s [%d.%d]: inequal num bindings/types (%llu != %llu)", @@ -240,6 +260,7 @@ unique_ptr ColumnBindingResolver::VisitReplace(BoundColumnRefExpress expr.Binding().column_index, expr.GetReturnType().ToString(), types[i].ToString()); } + // LCOV_EXCL_STOP } if (verify_only) { // in verification mode diff --git a/src/duckdb/src/execution/expression_executor.cpp b/src/duckdb/src/execution/expression_executor.cpp index a0b433c46..db45aaa8d 100644 --- a/src/duckdb/src/execution/expression_executor.cpp +++ b/src/duckdb/src/execution/expression_executor.cpp @@ -151,7 +151,7 @@ Value ExpressionExecutor::EvaluateScalar(ClientContext &context, const Expressio D_ASSERT(allow_unfoldable || result.GetVectorType() == VectorType::CONSTANT_VECTOR); auto result_value = result.GetValue(0); D_ASSERT(result_value.type().InternalType() == expr.GetReturnType().InternalType()); - return result_value; + return result_value.WithType(expr.GetReturnType()); } bool ExpressionExecutor::TryEvaluateScalar(ClientContext &context, const Expression &expr, Value &result) { @@ -325,6 +325,8 @@ idx_t ExpressionExecutor::Select(const Expression &expr, ExpressionState *state, case ExpressionClass::BOUND_FUNCTION: return Select(expr.Cast(), state, sel, count, true_sel, false_sel); // NOLINT: c-style cast + case ExpressionClass::BOUND_OPERATOR: + return Select(expr.Cast(), *state, sel, count, true_sel, false_sel); default: return DefaultSelect(expr, state, sel, count, true_sel, false_sel); } diff --git a/src/duckdb/src/execution/expression_executor/execute_case.cpp b/src/duckdb/src/execution/expression_executor/execute_case.cpp index 05667af50..f1ff36c4c 100644 --- a/src/duckdb/src/execution/expression_executor/execute_case.cpp +++ b/src/duckdb/src/execution/expression_executor/execute_case.cpp @@ -1,3 +1,4 @@ +#include "duckdb/common/vector/array_vector.hpp" #include "duckdb/common/vector/list_vector.hpp" #include "duckdb/common/vector/map_vector.hpp" #include "duckdb/common/vector/string_vector.hpp" @@ -92,6 +93,27 @@ void ExpressionExecutor::Execute(const BoundCaseExpression &expr, ExpressionStat } } +static void FillNulls(ValidityMask &validity, const SelectionVector &sel, idx_t count) { + if (count == 0) { + return; + } + validity.EnsureWritable(); + idx_t current_entry = sel.get_index(0) / ValidityMask::BITS_PER_VALUE; + validity_t invalid_bits = 0; + for (idx_t i = 0; i < count; i++) { + auto row_idx = sel.get_index(i); + D_ASSERT(row_idx < validity.Capacity()); + auto entry_idx = row_idx / ValidityMask::BITS_PER_VALUE; + if (entry_idx != current_entry) { + validity.GetValidityEntryUnsafe(current_entry) &= ~invalid_bits; + current_entry = entry_idx; + invalid_bits = 0; + } + invalid_bits |= validity_t(1) << (row_idx % ValidityMask::BITS_PER_VALUE); + } + validity.GetValidityEntryUnsafe(current_entry) &= ~invalid_bits; +} + template void TemplatedFillLoop(const Vector &vector, Vector &result, const SelectionVector &sel, sel_t count) { result.SetVectorType(VectorType::FLAT_VECTOR); @@ -100,9 +122,7 @@ void TemplatedFillLoop(const Vector &vector, Vector &result, const SelectionVect if (vector.GetVectorType() == VectorType::CONSTANT_VECTOR) { auto data = ConstantVector::GetData(vector); if (ConstantVector::IsNull(vector)) { - for (idx_t i = 0; i < count; i++) { - result_mask.SetInvalid(sel.get_index(i)); - } + FillNulls(result_mask, sel, count); } else { for (idx_t i = 0; i < count; i++) { res[sel.get_index(i)] = *data; @@ -127,9 +147,7 @@ void ValidityFillLoop(const Vector &vector, Vector &result, const SelectionVecto auto &result_mask = FlatVector::ValidityMutable(result); if (vector.GetVectorType() == VectorType::CONSTANT_VECTOR) { if (ConstantVector::IsNull(vector)) { - for (idx_t i = 0; i < count; i++) { - result_mask.SetInvalid(sel.get_index(i)); - } + FillNulls(result_mask, sel, count); } } else { auto entries = vector.Validity(); @@ -224,6 +242,30 @@ void ExpressionExecutor::FillSwitch(const Vector &vector, Vector &result, const result.Verify(); break; } + case PhysicalType::ARRAY: { + const bool is_constant = vector.GetVectorType() == VectorType::CONSTANT_VECTOR; + if (!is_constant) { + // below code needs constant or flat arrays + vector.Flatten(); + } + ValidityFillLoop(vector, result, sel, count); + auto array_size = ArrayType::GetSize(result.GetType()); + auto child_count = count * array_size; + SelectionVector source_sel(child_count); + SelectionVector result_sel(child_count); + for (idx_t i = 0; i < count; i++) { + auto source_idx = is_constant ? 0 : i; + auto result_idx = sel.get_index(i); + for (idx_t k = 0; k < array_size; k++) { + source_sel.set_index(i * array_size + k, source_idx * array_size + k); + result_sel.set_index(i * array_size + k, result_idx * array_size + k); + } + } + Vector source_child(ArrayVector::GetChild(vector), source_sel, child_count); + source_child.Flatten(); + FillSwitch(source_child, ArrayVector::GetChildMutable(result), result_sel, NumericCast(child_count)); + break; + } default: throw NotImplementedException("Unimplemented type for case expression: %s", result.GetType().ToString()); } diff --git a/src/duckdb/src/execution/expression_executor/execute_function.cpp b/src/duckdb/src/execution/expression_executor/execute_function.cpp index 7481fe125..ef1fe0bc2 100644 --- a/src/duckdb/src/execution/expression_executor/execute_function.cpp +++ b/src/duckdb/src/execution/expression_executor/execute_function.cpp @@ -186,6 +186,29 @@ static idx_t SelectBooleanResult(Vector &result, const SelectionVector *sel, idx result, sel, count, [](bool value) { return value; }, true_sel, false_sel); } +static void SetConstantArgumentCardinality(DataChunk &arguments, vector> &buffers) { + buffers.clear(); + for (auto &arg : arguments.data) { + buffers.push_back(arg.GetBufferRef()); + } + arguments.SetChildCardinality(1ULL); +} + +static void RestoreConstantArgumentCardinality(DataChunk &arguments, vector> &buffers, + idx_t count) { + for (auto &arg : arguments.data) { + arg.SetVectorType(VectorType::CONSTANT_VECTOR); + } + arguments.SetChildCardinality(count); + // the function can have replaced an argument's buffer (e.g. by flattening it) + for (auto &buffer : buffers) { + if (buffer) { + buffer->SetVectorSize(count); + } + } + buffers.clear(); +} + static void ExecuteConstantSelectFunction(const BoundFunctionExpression &expr, DataChunk &args, ExpressionState &state, Vector &result) { D_ASSERT(args.size() == 1); @@ -222,15 +245,15 @@ void ExpressionExecutor::Execute(const BoundFunctionExpression &expr, Expression } } } + auto &execute_function_state = state->Cast(); if (all_constant) { // if all arguments are constant temporarily set the child cardinality to 1 - arguments.SetChildCardinality(1ULL); + SetConstantArgumentCardinality(arguments, execute_function_state.constant_argument_buffers); } else { arguments.SetChildCardinality(count); } arguments.Verify(context); - auto &execute_function_state = state->Cast(); auto dictionary_executed = expr.Function().HasFunctionCallback() && !all_constant && execute_function_state.TryExecuteDictionaryExpression(expr, arguments, *state, result); if (!dictionary_executed) { @@ -243,11 +266,7 @@ void ExpressionExecutor::Execute(const BoundFunctionExpression &expr, Expression } } if (all_constant) { - // restore the input cardinality - for (auto &arg : arguments.data) { - arg.SetVectorType(VectorType::CONSTANT_VECTOR); - } - arguments.SetChildCardinality(count); + RestoreConstantArgumentCardinality(arguments, execute_function_state.constant_argument_buffers, count); // ensure the result type is constant result.FlattenAndSetConstant(); } @@ -281,9 +300,10 @@ idx_t ExpressionExecutor::Select(const BoundFunctionExpression &expr, Expression return SelectBooleanResult(result, sel, count, true_sel, false_sel); } } + auto &execute_function_state = state->Cast(); if (all_constant) { // if all arguments are constant we only need to run the function on one value - arguments.SetChildCardinality(1ULL); + SetConstantArgumentCardinality(arguments, execute_function_state.constant_argument_buffers); } else { arguments.SetChildCardinality(count); } @@ -296,14 +316,9 @@ idx_t ExpressionExecutor::Select(const BoundFunctionExpression &expr, Expression } else { ExecuteConstantSelectFunction(expr, arguments, *state, result); } - // restore the input cardinality - for (auto &arg : arguments.data) { - arg.SetVectorType(VectorType::CONSTANT_VECTOR); - } - arguments.SetChildCardinality(count); + RestoreConstantArgumentCardinality(arguments, execute_function_state.constant_argument_buffers, count); return SelectBooleanResult(result, sel, count, true_sel, false_sel); } - auto &execute_function_state = state->Cast(); if (expr.Function().HasFunctionCallback()) { Vector result(LogicalType::BOOLEAN); auto dictionary_executed = diff --git a/src/duckdb/src/execution/expression_executor/execute_operator.cpp b/src/duckdb/src/execution/expression_executor/execute_operator.cpp index 7837d405e..5e3c2683e 100644 --- a/src/duckdb/src/execution/expression_executor/execute_operator.cpp +++ b/src/duckdb/src/execution/expression_executor/execute_operator.cpp @@ -17,6 +17,67 @@ unique_ptr ExpressionExecutor::InitializeState(const BoundOpera return result; } +template +static idx_t SelectNullLoop(const VectorValidityIterator &entries, const SelectionVector &sel, idx_t count, + optional_ptr valid_sel, optional_ptr null_sel) { + idx_t valid_count = 0, null_count = 0; + for (idx_t i = 0; i < count; i++) { + const auto row_idx = sel.get_index(i); + const auto valid = entries.IsValid(i); + if (HAS_VALID_SEL) { + valid_sel->set_index(valid_count, row_idx); + valid_count += valid; + } + if (HAS_NULL_SEL) { + null_sel->set_index(null_count, row_idx); + null_count += !valid; + } + } + return HAS_VALID_SEL ? valid_count : count - null_count; +} + +static idx_t SelectNulls(const Vector &input, const SelectionVector &sel, idx_t count, + optional_ptr valid_sel, optional_ptr null_sel) { + auto entries = input.Validity(); + if (entries.CannotHaveNull() || input.GetVectorType() == VectorType::CONSTANT_VECTOR) { + const auto valid = entries.CannotHaveNull() || entries.IsValid(0); + auto result_sel = valid ? valid_sel : null_sel; + if (result_sel) { + for (idx_t i = 0; i < count; i++) { + result_sel->set_index(i, sel.get_index(i)); + } + } + return valid ? count : 0; + } + if (valid_sel && null_sel) { + return SelectNullLoop(entries, sel, count, valid_sel, null_sel); + } + if (valid_sel) { + return SelectNullLoop(entries, sel, count, valid_sel, null_sel); + } + return SelectNullLoop(entries, sel, count, valid_sel, null_sel); +} + +idx_t ExpressionExecutor::Select(const BoundOperatorExpression &expr, ExpressionState &state, + optional_ptr sel, idx_t count, + optional_ptr true_sel, optional_ptr false_sel) { + const auto type = expr.GetExpressionType(); + if (type != ExpressionType::OPERATOR_IS_NULL && type != ExpressionType::OPERATOR_IS_NOT_NULL) { + return DefaultSelect(expr, &state, sel.get(), count, true_sel.get(), false_sel.get()); + } + D_ASSERT(expr.GetChildren().size() == 1); + state.intermediate_chunk.Reset(); + auto &child = state.intermediate_chunk.data[0]; + Execute(*expr.GetChildren()[0], state.child_states[0].get(), sel.get(), count, child); + + const SelectionVector identity; + const auto &input_sel = sel ? *sel : identity; + if (type == ExpressionType::OPERATOR_IS_NOT_NULL) { + return SelectNulls(child, input_sel, count, true_sel, false_sel); + } + return count - SelectNulls(child, input_sel, count, false_sel, true_sel); +} + void ExpressionExecutor::Execute(const BoundOperatorExpression &expr, ExpressionState *state, const SelectionVector *sel, idx_t count, Vector &result) { // special handling for special snowflake 'IN' diff --git a/src/duckdb/src/execution/ie_join_union.cpp b/src/duckdb/src/execution/ie_join_union.cpp new file mode 100644 index 000000000..c19619d65 --- /dev/null +++ b/src/duckdb/src/execution/ie_join_union.cpp @@ -0,0 +1,363 @@ +#include "duckdb/execution/ie_join_union.hpp" +#include "duckdb/common/bit_utils.hpp" +#include "duckdb/common/sorting/sort_key.hpp" +#include "duckdb/main/client_context.hpp" +#include "duckdb/planner/expression/bound_constant_expression.hpp" +#include "duckdb/planner/expression/bound_reference_expression.hpp" + +namespace duckdb { + +idx_t IEJoinUnion::AppendKey(ExecutionContext &context, InterruptState &interrupt, SortedTable &table, + ExpressionExecutor &executor, SortedTable &marked, int64_t increment, int64_t rid, + const ChunkRange &chunk_range) { + const auto chunk_begin = chunk_range.first; + const auto chunk_end = chunk_range.second; + + if (chunk_begin == chunk_end) { + return 0; + } + + // Reading + const auto valid = table.count - table.has_null; + auto &source = *table.sorted->payload_data; + TupleDataScanState scanner; + source.InitializeScan(scanner); + + DataChunk scanned; + source.InitializeScanChunk(scanner, scanned); + idx_t table_idx = source.Seek(scanner, chunk_begin); + + // Writing + auto &sort = *marked.sort; + auto local_sort_state = sort.GetLocalSinkState(context); + vector types; + for (const auto &expr : executor.expressions) { + types.emplace_back(expr->GetReturnType()); + } + const idx_t rid_idx = types.size(); + types.emplace_back(LogicalType::BIGINT); + + DataChunk keys; + DataChunk payload; + keys.Initialize(Allocator::DefaultAllocator(), types); + + OperatorSinkInput sink {*marked.global_sink, *local_sort_state, interrupt}; + idx_t inserted = 0; + for (auto chunk_idx = chunk_begin; chunk_idx < chunk_end; ++chunk_idx) { + source.Scan(scanner, scanned); + + // NULLs are at the end, so stop when we reach them + auto scan_count = scanned.size(); + if (table_idx + scan_count > valid) { + if (table_idx >= valid) { + scan_count = 0; + ; + } else { + scan_count = valid - table_idx; + scanned.SetChildCardinality(scan_count); + } + } + if (scan_count == 0) { + break; + } + table_idx += scan_count; + + // Compute the input columns from the payload + keys.Reset(); + keys.Split(payload, rid_idx); + executor.Execute(scanned, keys); + + // Mark the rid column + payload.data[0].Sequence(rid, increment, scan_count); + keys.Fuse(payload); + rid += increment * UnsafeNumericCast(scan_count); + + // Sort on the sort columns (which will no longer be needed) + sort.Sink(context, keys, sink); + inserted += scan_count; + } + OperatorSinkCombineInput combine {*marked.global_sink, *local_sort_state, interrupt}; + sort.Combine(context, combine); + marked.count += inserted; + + return inserted; +} + +IEJoinUnion::IEJoinUnion(SortedTable &l2, ColumnDataCollection &li, ColumnDataCollection &p, + const vector &conditions, const ChunkRange &chunks) + : n(0), i(0), li(li), p(p) { + // 7. initialize bit-array B (|B| = n), and set all bits to 0 + n_j = l2.count.load(); + bit_array.resize(ValidityMask::EntryCount(n_j), 0); + bit_mask.Initialize(bit_array.data(), n_j); + + // Bloom filter + bloom_count = (n_j + (BLOOM_CHUNK_BITS - 1)) / BLOOM_CHUNK_BITS; + bloom_array.resize(ValidityMask::EntryCount(bloom_count), 0); + bloom_filter.Initialize(bloom_array.data(), bloom_count); + + // 11. for(i←1 to n) do + const auto strict2 = IsStrictComparison(conditions[1].GetComparisonType()); + op2 = make_uniq(l2, strict2); + off2 = make_uniq(l2, strict2); + n = l2.BlockStart(chunks.second); + i = l2.BlockStart(chunks.first); + j = i; + anti_i = i; + + const auto sort_key_type = l2.GetSortKeyType(); + switch (sort_key_type) { +#define DUCKDB_SORT_KEY_CASE(SORT_KEY_TYPE) \ + case SortKeyType::SORT_KEY_TYPE: \ + next_row_func = &IEJoinUnion::NextRow; \ + break; + DUCKDB_FOR_EACH_SORT_KEY_TYPE(DUCKDB_SORT_KEY_CASE) +#undef DUCKDB_SORT_KEY_CASE + default: + throw NotImplementedException("IEJoinUnion for %s", EnumUtil::ToString(sort_key_type)); + } + + (this->*next_row_func)(); +} + +template +bool IEJoinUnion::NextRow() { + using SORT_KEY = SortKey; + using BLOCKS_ITERATOR = block_iterator_t; + + BLOCKS_ITERATOR off2_itr(*off2->state); + BLOCKS_ITERATOR op2_itr(*op2->state); + const auto strict = off2->strict; + + auto pinned_idx = off2->GetChunkIndex(); + for (; i < n; ++i) { + // 12. pos ← P[i] + auto pos = p[i]; + lrid = li[pos]; + if (lrid < 0) { + continue; + } + + // 16. B[pos] ← 1 + op2->SetIndex(i); + for (; off2->GetIndex() < n_j; ++(*off2)) { + // Prevent buildup of pinned blocks + if (off2->GetChunkIndex() != pinned_idx) { + off2->Repin(); + pinned_idx = off2->GetChunkIndex(); + } + if (!Compare(off2_itr[off2->GetIndex()], op2_itr[op2->GetIndex()], strict)) { + break; + } + const auto p2 = p[off2->GetIndex()]; + if (li[p2] < 0) { + // Only mark rhs matches. + bit_mask.SetValidUnsafe(p2); + bloom_filter.SetValidUnsafe(p2 / BLOOM_CHUNK_BITS); + } + } + + // 9. if (op1 ∈ {≤,≥} and op2 ∈ {≤,≥}) eqOff = 0 + // 10. else eqOff = 1 + // No, because there could be more than one equal value. + // Find the leftmost off1 where L1[pos] op1 L1[off1..n] + // These are the rows that satisfy the op1 condition + // and that is where we should start scanning B from + j = pos; + + return true; + } + return false; +} + +static idx_t NextValid(const ValidityMask &bits, idx_t j, const idx_t n) { + if (j >= n) { + return n; + } + + // We can do a first approximation by checking entries one at a time + // which gives 64:1. + idx_t entry_idx, idx_in_entry; + bits.GetEntryIndex(j, entry_idx, idx_in_entry); + + // Copy first entry to local and trim the bits before the start position + auto first_entry = bits.GetValidityEntryUnsafe(entry_idx++); + first_entry &= (ValidityMask::ValidityBuffer::MAX_ENTRY << idx_in_entry); + + // If the first entry has a valid bit, we can return immediately + if (first_entry) { + return j + CountZeros::Trailing(first_entry) - idx_in_entry; + } + + // The first entry did not have a valid bit + j += ValidityMask::BITS_PER_VALUE - idx_in_entry; + + // Loop over non-ragged entries + const auto entry_count_minus_one = bits.EntryCount(n) - 1; + const auto entry_idx_before = entry_idx; + + // The compiler has a hard time optimizing this loop for some reason + // Creating a static inner loop like this improves performance by almost 2x + static constexpr idx_t NEXT_VALID_UNROLL = 8; + for (; entry_idx + NEXT_VALID_UNROLL < entry_count_minus_one; entry_idx += NEXT_VALID_UNROLL) { + for (idx_t unroll_idx = 0; unroll_idx < NEXT_VALID_UNROLL; unroll_idx++) { + const auto unroll_entry_idx = entry_idx + unroll_idx; + const auto &entry = bits.GetValidityEntryUnsafe(unroll_entry_idx); + if (entry) { + return j + (unroll_entry_idx - entry_idx_before) * ValidityMask::BITS_PER_VALUE + + CountZeros::Trailing(entry); + } + } + } + + for (; entry_idx < entry_count_minus_one; ++entry_idx) { + const auto &entry = bits.GetValidityEntryUnsafe(entry_idx); + if (entry) { + return j + (entry_idx - entry_idx_before) * ValidityMask::BITS_PER_VALUE + + CountZeros::Trailing(entry); + } + } + + // Update j once after the loop so we don't have to update it in each iteration + j += (entry_idx - entry_idx_before) * ValidityMask::BITS_PER_VALUE; + + // Check the final entry + return j >= n ? n : j + CountZeros::Trailing(bits.GetValidityEntryUnsafe(entry_idx)); +} + +idx_t IEJoinUnion::JoinBlocks(unsafe_vector &lsel, unsafe_vector &rsel) { + // Release pinned blocks + op2->Repin(); + off2->Repin(); + + // 8. initialize join result as an empty list for tuple pairs + idx_t result_count = 0; + + lsel.resize(STANDARD_VECTOR_SIZE); + rsel.resize(STANDARD_VECTOR_SIZE); + + // 11. for(i←1 to n) do + while (i < n) { + // 13. for (j ← pos+eqOff to n) do + for (;;) { + // 14. if B[j] = 1 then + + // Use the Bloom filter to find candidate blocks + while (j < n_j) { + auto bloom_begin = NextValid(bloom_filter, j / BLOOM_CHUNK_BITS, bloom_count) * BLOOM_CHUNK_BITS; + auto bloom_end = MinValue(n_j, bloom_begin + BLOOM_CHUNK_BITS); + + j = MaxValue(j, bloom_begin); + j = NextValid(bit_mask, j, bloom_end); + if (j < bloom_end) { + break; + } + } + + if (j >= n_j) { + break; + } + + // Filter out tuples with the same sign (they come from the same table) + const auto rrid = li[j]; + ++j; + + D_ASSERT(lrid > 0 && rrid < 0); + // 15. add tuples w.r.t. (L1[j], L1[i]) to join result + lsel[result_count] = static_cast(+lrid - 1); + rsel[result_count] = static_cast(-rrid - 1); + ++result_count; + if (result_count == STANDARD_VECTOR_SIZE) { + // out of space! + return result_count; + } + } + + if (!FinishRow()) { + break; + } + } + + lsel.resize(result_count); + rsel.resize(result_count); + + return result_count; +} + +void IEJoinUnion::InitializeTables(ClientContext &client, const PhysicalComparisonJoin &op, + const vector &conditions, unique_ptr &l1, + unique_ptr &l2) { + // input : query Q with 2 join predicates t1.X op1 t2.X' and t1.Y op2 t2.Y', tables T, T' of sizes m and n resp. + // output: a list of tuple pairs (ti , tj) + // Note that T/T' are already sorted on X/X' and contain the payload data + // We only join the two block numbers and use the sizes of the blocks as the counts + + // 1. let L1 (resp. L2) be the array of column X (resp. Y ) + const auto first = conditions[0].GetComparisonType(); + const auto second = conditions[1].GetComparisonType(); + const auto first_order = + first == ExpressionType::COMPARE_LESSTHAN || first == ExpressionType::COMPARE_LESSTHANOREQUALTO + ? OrderType::ASCENDING + : OrderType::DESCENDING; + const auto second_order = + second == ExpressionType::COMPARE_LESSTHAN || second == ExpressionType::COMPARE_LESSTHANOREQUALTO + ? OrderType::DESCENDING + : OrderType::ASCENDING; + BoundOrderByNode order1(first_order, OrderByNullType::NULLS_LAST, + make_uniq(conditions[0].GetLHS().GetReturnType(), storage_t(0))); + BoundOrderByNode order2(second_order, OrderByNullType::NULLS_LAST, + make_uniq(conditions[1].GetLHS().GetReturnType(), storage_t(0))); + + // 2. if (op1 ∈ {>, ≥}) sort L1 in descending order + // 3. else if (op1 ∈ {<, ≤}) sort L1 in ascending order + + // For the union algorithm, we make a unified table with the keys and the rids as the payload: + // X/X', Y/Y', R/R'/Li + // The first position is the sort key. + vector types; + types.emplace_back(order2.expression->GetReturnType()); + types.emplace_back(LogicalType::BIGINT); + + // Sort on the first expression + auto ref = make_uniq(order1.expression->GetReturnType(), 0U); + vector orders; + orders.emplace_back(order1.type, order1.null_order, std::move(ref)); + // The goal is to make i (from the left table) < j (from the right table), + // if value[i] and value[j] match the condition 1. + // Add a column from_left to solve the problem when there exist multiple equal values in l1. + // If the operator is loose inequality, make t1.from_left (== true) sort BEFORE t2.from_left (== false). + // Otherwise, make t1.from_left sort (== true) sort AFTER t2.from_left (== false). + // For example, if t1.time <= t2.time + // | value | 1 | 1 | 1 | 1 | + // | --------- | ----- | ----- | ----- | ----- | + // | from_left | T(l2) | T(l2) | F(r1) | F(r2) | + // if t1.time < t2.time + // | value | 1 | 1 | 1 | 1 | + // | --------- | ----- | ----- | ----- | ----- | + // | from_left | F(r2) | F(r1) | T(l2) | T(l1) | + // Using this OrderType, if i < j then value[i] (from left table) and value[j] (from right table) match + // the condition (t1.time <= t2.time or t1.time < t2.time), then from_left will force them into the correct order. + auto from_left = make_uniq(Value::BOOLEAN(true)); + const auto strict1 = IEJoinUnion::IsStrictComparison(conditions[0].GetComparisonType()); + orders.emplace_back(!strict1 ? OrderType::DESCENDING : OrderType::ASCENDING, OrderByNullType::ORDER_DEFAULT, + std::move(from_left)); + + l1 = make_uniq(client, orders, types, op); + + // 4. if (op2 ∈ {>, ≥}) sort L2 in ascending order + // 5. else if (op2 ∈ {<, ≤}) sort L2 in descending order + + // We sort on Y/Y' to obtain the sort keys and the permutation array. + // For this we just need a two-column table of Y, P + types.clear(); + types.emplace_back(LogicalType::BIGINT); + + // Sort on the first expression + orders.clear(); + ref = make_uniq(order2.expression->GetReturnType(), 0U); + orders.emplace_back(order2.type, order2.null_order, std::move(ref)); + + l2 = make_uniq(client, orders, types, op); +} + +} // namespace duckdb diff --git a/src/duckdb/src/execution/index/fixed_size_allocator.cpp b/src/duckdb/src/execution/index/fixed_size_allocator.cpp index d69e5b855..fb31792a0 100644 --- a/src/duckdb/src/execution/index/fixed_size_allocator.cpp +++ b/src/duckdb/src/execution/index/fixed_size_allocator.cpp @@ -257,6 +257,7 @@ IndexPointer FixedSizeAllocator::VacuumPointer(const IndexPointer old_ptr) { auto old_handle = GetHandle(old_ptr); auto new_handle = GetHandle(new_ptr); + new_handle.MarkModified(); memcpy(new_handle.GetPtr(), old_handle.GetPtr(), segment_size); return new_ptr; diff --git a/src/duckdb/src/execution/index/fixed_size_buffer.cpp b/src/duckdb/src/execution/index/fixed_size_buffer.cpp index eff38246c..d6cb49962 100644 --- a/src/duckdb/src/execution/index/fixed_size_buffer.cpp +++ b/src/duckdb/src/execution/index/fixed_size_buffer.cpp @@ -78,7 +78,11 @@ FixedSizeBuffer::~FixedSizeBuffer() { } if (OnDisk()) { // marking a block as modified decreases the reference count of multi-use blocks - block_manager.MarkBlockAsModified(block_pointer.block_id); + // this must not throw out of a destructor, e.g., if the block ids of a corrupt file overlap + try { + block_manager.MarkBlockAsModified(block_pointer.block_id); + } catch (...) { // NOLINT + } } } diff --git a/src/duckdb/src/execution/index/unbound_index.cpp b/src/duckdb/src/execution/index/unbound_index.cpp index f230c2075..66b9c8a84 100644 --- a/src/duckdb/src/execution/index/unbound_index.cpp +++ b/src/duckdb/src/execution/index/unbound_index.cpp @@ -5,6 +5,7 @@ #include "duckdb/parser/parsed_data/create_index_info.hpp" #include "duckdb/planner/expression_binder/index_binder.hpp" #include "duckdb/storage/block_manager.hpp" +#include "duckdb/storage/buffer_manager.hpp" #include "duckdb/storage/index_storage_info.hpp" #include "duckdb/storage/table_io_manager.hpp" #include "duckdb/storage/checkpoint/table_index_writer.hpp" @@ -92,10 +93,9 @@ void UnboundIndex::BufferChunk(DataChunk &table_chunk, Vector &row_ids, const Bu } combined_chunk.data.back().Reference(row_ids); - auto &allocator = Allocator::Get(db); auto &buffer = buffered_replays.GetBuffer(replay_type); if (buffer == nullptr) { - buffer = make_uniq(allocator, types); + buffer = make_uniq(BufferManager::GetBufferManager(db), types); } // The starting index of the buffer range is the size of the buffer. const idx_t start = buffer->Count(); diff --git a/src/duckdb/src/execution/join_hashtable.cpp b/src/duckdb/src/execution/join_hashtable.cpp index aef0040c9..493e0f084 100644 --- a/src/duckdb/src/execution/join_hashtable.cpp +++ b/src/duckdb/src/execution/join_hashtable.cpp @@ -101,15 +101,11 @@ JoinHashTable::JoinHashTable(ClientContext &context_p, const PhysicalOperator &o } void JoinHashTable::FinishInitWithLayout(shared_ptr published_layout, - vector dict_index_width_p) { + shared_ptr dict_info_p) { D_ASSERT(!layout_ptr); layout_ptr = std::move(published_layout); - if (dict_index_width_p.empty()) { - dict_index_width.assign(build_types.size(), 0); - } else { - D_ASSERT(dict_index_width_p.size() == build_types.size()); - dict_index_width = std::move(dict_index_width_p); - } + D_ASSERT(!dict_info_p || dict_info_p->columns.size() == build_types.size()); + dict_info = std::move(dict_info_p); // Initialize the row matcher that are used for filtering during the probing only if there are non-equality preds if (!non_equality_predicates.empty()) { @@ -126,6 +122,8 @@ void JoinHashTable::FinishInitWithLayout(shared_ptr published_l } chains_longer_than_one.store(false, std::memory_order_relaxed); + // a fresh matcher: Initialize appends, and a recursive CTE iteration publishes the layout again + row_matcher_build = RowMatcher(); row_matcher_build.Initialize(true, *layout_ptr, equality_predicates); const auto &offsets = layout_ptr->GetOffsets(); @@ -140,9 +138,6 @@ void JoinHashTable::FinishInitWithLayout(shared_ptr published_l dead_end = make_unsafe_uniq_array_uninitialized(layout_ptr->GetRowWidth()); memset(dead_end.get(), 0, layout_ptr->GetRowWidth()); - - // indexed parallel to build_types / payload columns (not to output_columns) - dict_registry.assign(build_types.size(), nullptr); } JoinHashTable::~JoinHashTable() { @@ -179,28 +174,13 @@ void JoinHashTable::Merge(JoinHashTable &other) { } } - if (bloom_filter.IsInitialized() && other.bloom_filter.IsInitialized()) { - bloom_filter.Merge(other.bloom_filter); - } + // every thread-local table shares this operator's filter - a different one here would mean its rows + // never reached the filter that gets published + D_ASSERT(!other.bloom_filter || bloom_filter.get() == other.bloom_filter.get()); sink_collection->Combine(*other.sink_collection); - // Reconcile per-column pinned dictionary entries. For global dictionary producers every thread pins the - // same buffer_ptr, so this is normally a no-op or a "take theirs" adoption. - if (dict_registry.size() < other.dict_registry.size()) { - dict_registry.resize(other.dict_registry.size(), nullptr); - } - for (idx_t col = 0; col < other.dict_registry.size(); col++) { - if (!other.dict_registry[col]) { - continue; - } - if (!dict_registry[col]) { - dict_registry[col] = std::move(other.dict_registry[col]); - } else { - // Both threads pinned the same upstream entry, so ids match by construction; a mismatch is a producer bug. - D_ASSERT(dict_registry[col]->id == other.dict_registry[col]->id); - } - } + // dict_info is the descriptor shared by every per-thread JHT of this join; there is nothing to reconcile } static void ApplyBitmaskAndGetSaltBuild(Vector &hashes_v, Vector &salt_v, const idx_t &bitmask) { @@ -250,12 +230,13 @@ static idx_t ProbeForPointersInternal(JoinHashTable::ProbeState &state, JoinHash unsafe_optional_ptr entries, Vector &pointers_result_v, optional_ptr row_sel, idx_t &count) { auto hashes_dense = FlatVector::GetDataMutable(state.hashes_dense_v); + const auto bitmask = ht.bitmask; idx_t keys_to_compare_count = 0; for (idx_t i = 0; i < count; i++) { auto row_hash = hashes_dense[i]; // hashes have been flattened before -> always access dense - auto row_ht_offset = row_hash & ht.bitmask; + auto row_ht_offset = row_hash & bitmask; if (USE_SALTS) { // increment the ht_offset of the entry as long as the next entry is occupied and salt does not match @@ -279,7 +260,7 @@ static idx_t ProbeForPointersInternal(JoinHashTable::ProbeState &state, JoinHash } // full and salt do not match -> continue probing - IncrementAndWrap(row_ht_offset, ht.bitmask); + IncrementAndWrap(row_ht_offset, bitmask); } } else { const ht_entry_t entry = entries.get()[row_ht_offset]; @@ -539,8 +520,9 @@ uint8_t JoinHashTable::GetDictSurvivingIndexWidth(idx_t build_col_idx, const Vec if (!dict_size.IsValid()) { return 0; } - // only narrow when strictly smaller than the native slot (never regress; e.g. INTEGER over a D<=256 dict) - const uint8_t index_width = DictIndexWidth(dict_size.GetIndex()); + // only narrow when strictly smaller than the native slot (never regress; e.g. INTEGER over a D<=255 dict). + // One extra slot is reserved for the NULL index that encodes NULL-only chunks (see CreateDictSurvivingColumn). + const uint8_t index_width = DictIndexWidth(dict_size.GetIndex() + 1); const auto native_bytes = GetTypeIdSize(type.InternalType()); if (index_width >= native_bytes) { return 0; @@ -548,34 +530,70 @@ uint8_t JoinHashTable::GetDictSurvivingIndexWidth(idx_t build_col_idx, const Vec return index_width; } -void JoinHashTable::PinDictSurvivingColumn(idx_t build_col_idx, const Vector &incoming, uint8_t index_width) { - // Slot was narrowed on the first chunk; there is no fallback for a non-dict later chunk. Throw, not D_ASSERT: - // the Cast below is UB in release on a non-dict vector, scattering foreign bytes as indices. - if (incoming.GetVectorType() != VectorType::DICTIONARY_VECTOR || DictionaryVector::DictionaryId(incoming).empty() || - !DictionaryVector::IsGlobalDictionary(incoming)) { - throw InternalException("dict-surviving join: narrowed column %llu received a " - "non-global-dictionary chunk; build pipeline is not single-source", - static_cast(build_col_idx)); +LogicalType DictSurvivingInfo::IndexType(uint8_t index_width) { + switch (index_width) { + case sizeof(uint8_t): + return LogicalType::UTINYINT; + case sizeof(uint16_t): + return LogicalType::USMALLINT; + default: + D_ASSERT(index_width == sizeof(uint32_t)); + return LogicalType::UINTEGER; } - const auto &entry = incoming.Buffer().Cast().GetEntry(); - if (dict_registry[build_col_idx]) { - // Subsequent chunks wrap the same entry, so ids match by construction; a mismatch is a producer bug. - D_ASSERT(dict_registry[build_col_idx]->id == entry.id); +} + +DictSurvivingColumn JoinHashTable::CreateDictSurvivingColumn(const Vector &incoming, uint8_t index_width) { + D_ASSERT(index_width != 0); + D_ASSERT(incoming.GetVectorType() == VectorType::DICTIONARY_VECTOR); + D_ASSERT(DictionaryVector::IsGlobalDictionary(incoming)); + // The upstream child is a zero-copy gather: its long strings point into the producer's row-store heap, recycled + // before we gather on probe. Deep-copy into a self-owned entry so it outlives them, and append one NULL slot at + // index upstream_count: NULL-only chunks (the unmatched-build scan of an upstream FULL/RIGHT join fills the probe + // columns with NULL) are stored as that index, so the regular dictionary gather reconstructs them. + const auto &upstream_child = DictionaryVector::Child(incoming); + const auto upstream_count = upstream_child.size(); + // GetDictSurvivingIndexWidth sized the width to upstream_count + 1 + D_ASSERT(upstream_count < (idx_t(1) << (8 * index_width))); + DictSurvivingColumn column; + column.index_width = index_width; + // CreateReusableGlobalDictionary assigns a fresh id: this is a different dictionary than the upstream one (one + // more slot), so a downstream consumer must not treat the two as interchangeable. + column.entry = DictionaryVector::CreateReusableGlobalDictionary(upstream_child.GetType(), upstream_count + 1); + if (upstream_count > 0) { + VectorOperations::Copy(upstream_child, column.entry->data, upstream_count, 0, 0); + } + FlatVector::SetNull(column.entry->data, upstream_count, true); + column.upstream_id = DictionaryVector::DictionaryId(incoming); + return column; +} + +void JoinHashTable::EncodeDictSurvivingColumn(const DictSurvivingColumn &column, idx_t build_col_idx, + const Vector &incoming, idx_t count, vector &buffers, + vector &vectors) { + if (incoming.GetVectorType() == VectorType::DICTIONARY_VECTOR) { + // Slot was narrowed against one specific upstream dictionary; a different one would scatter foreign indices. + if (DictionaryVector::DictionaryId(incoming) != column.upstream_id) { + throw InternalException("dict-surviving join: narrowed column %llu received a chunk with an unexpected " + "dictionary; build pipeline is not single-source", + static_cast(build_col_idx)); + } + // Indices are < upstream size by construction, and the publisher sized the width to upstream size + 1 + ScatterDictIndices(column.index_width, DictionaryVector::SelVector(incoming), count, + buffer_manager.GetBufferAllocator(), buffers, vectors); return; } - // The upstream child is a zero-copy gather: its long strings point into the producer's row-store heap, recycled - // before we gather on probe. Deep-copy into a self-owned entry so it outlives them. - const auto &upstream_child = entry.data; - const auto child_count = upstream_child.size(); - // child_count fits index_width by construction (publisher sized the width to this dict). Assert so a producer - // that grows the child past it fails loudly instead of truncating in the UnsafeNumericCast. - D_ASSERT(child_count <= (idx_t(1) << (8 * index_width))); - auto owned_entry = DictionaryVector::CreateReusableGlobalDictionary(upstream_child.GetType(), child_count); - if (child_count > 0) { - VectorOperations::Copy(upstream_child, owned_entry->data, child_count, 0, 0); + if (!VectorOperations::HasNotNull(incoming)) { + // e.g. the unmatched-build scan of an upstream FULL/RIGHT join: every row stores the reserved NULL index + const auto null_index = + Value::Numeric(DictSurvivingInfo::IndexType(column.index_width), NumericCast(column.NullIndex())); + vectors.emplace_back(null_index, count_t(count)); + return; } - owned_entry->id = entry.id; - dict_registry[build_col_idx] = std::move(owned_entry); + // There is no fallback for values that are not in the pinned dictionary. Throw, not D_ASSERT: storing the + // native bytes into a narrowed slot would corrupt the row store. + throw InternalException("dict-surviving join: narrowed column %llu received a non-dictionary chunk with " + "values; build pipeline is not single-source", + static_cast(build_col_idx)); } static bool MarkJoinNullRows(const UnifiedVectorFormat &format, idx_t count, @@ -661,15 +679,13 @@ void JoinHashTable::Build(PartitionedTupleDataAppendState &append_state, DataChu vector dict_idx_vectors; for (idx_t i = 0; i < payload.ColumnCount(); i++) { auto &incoming = payload.data[i]; - const uint8_t index_width = i < dict_index_width.size() ? dict_index_width[i] : 0; - if (index_width == 0) { + auto dict_column = GetDictSurvivingColumn(i); + if (!dict_column) { source_chunk.data[col_offset + i].Reference(incoming); continue; } - // Narrowed column: pin the dictionary, then scatter its sel indices at the chosen width in place of the value. - PinDictSurvivingColumn(i, incoming, index_width); - ScatterDictIndices(index_width, DictionaryVector::SelVector(incoming), payload.size(), - buffer_manager.GetBufferAllocator(), dict_idx_buffers, dict_idx_vectors); + // Narrowed column: store dictionary indices (or the reserved NULL index) at the chosen width, not the value + EncodeDictSurvivingColumn(*dict_column, i, incoming, payload.size(), dict_idx_buffers, dict_idx_vectors); source_chunk.data[col_offset + i].Reference(dict_idx_vectors.back()); } col_offset += payload.ColumnCount(); @@ -699,8 +715,8 @@ void JoinHashTable::Build(PartitionedTupleDataAppendState &append_state, DataChu // hash the keys and obtain an entry in the list // note that we only hash the keys used in the equality comparison Hash(keys, *current_sel, added_count, hash_values); - if (bloom_filter.IsInitialized()) { - bloom_filter.InsertHashes(hash_values); + if (bloom_filter) { + bloom_filter->InsertHashes(hash_values, *current_sel, added_count); } // Re-reference and ToUnifiedFormat the hash column after computing it @@ -830,6 +846,7 @@ static inline void InsertMatchesAndIncrementMisses(unsafe_optional_ptr(entry, row_ptr_to_insert, salt, ht.pointer_offset); + InsertRowToEntry(entry, row_ptr_to_insert, salt, pointer_offset); } } @@ -912,6 +929,7 @@ static void InsertHashesLoop(unsafe_optional_ptr> entries, Ve // use the ht bitmask to make the modulo operation faster but keep the salt bits intact idx_t capacity_mask = ht.bitmask | ht_entry_t::SALT_MASK; + const auto pointer_offset = ht.pointer_offset; while (remaining_count > 0) { idx_t salt_match_count = 0; @@ -944,7 +962,7 @@ static void InsertHashesLoop(unsafe_optional_ptr> entries, Ve auto &atomic_entry = entries.get()[ht_offset]; const auto row_ptr_to_insert = lhs_row_locations[row_index]; const auto potential_collided_ptr = - InsertRowToEntry(atomic_entry, row_ptr_to_insert, salt, ht.pointer_offset); + InsertRowToEntry(atomic_entry, row_ptr_to_insert, salt, pointer_offset); if (PARALLEL) { // if the insertion was not successful, the entry was occupied in the meantime, so we have to @@ -985,9 +1003,9 @@ static void InsertHashesLoop(unsafe_optional_ptr> entries, Ve void JoinHashTable::InsertHashes(Vector &hashes_v, TupleDataChunkState &chunk_state, InsertState &insert_state, bool parallel) { - // Insert Hashes into the BF - if (bloom_filter.IsInitialized()) { - bloom_filter.InsertHashes(hashes_v); + // a sealed filter was accumulated during Sink and is already complete; only a finalize-built one fills here + if (bloom_filter && !bloom_filter->IsSealed()) { + bloom_filter->InsertHashes(hashes_v); } auto atomic_entries = GetAtomicEntries(); auto &row_locations = chunk_state.row_locations; @@ -1054,9 +1072,10 @@ void JoinHashTable::AllocatePointerTable() { throw InternalException("Hashtable capacity exceeds 48-bit limit (2^48 - 1)"); } - if (should_build_bloom_filter && !bloom_filter.IsInitialized()) { - bloom_filter_init_count = MaxValue(Count(), 1); - bloom_filter.Initialize(context, bloom_filter_init_count); + if (should_build_bloom_filter && (!bloom_filter || bloom_filter->IsSealed())) { + // (re)create the finalize-built filter now that the actual build count is known + bloom_filter = make_shared_ptr(); + bloom_filter->Initialize(context, MaxValue(Count(), 1)); } if (hash_map.get()) { @@ -1064,14 +1083,14 @@ void JoinHashTable::AllocatePointerTable() { auto current_capacity = hash_map.GetSize() / sizeof(ht_entry_t); if (capacity > current_capacity) { // Need more space - hash_map = buffer_manager.GetBufferAllocator().Allocate(capacity * sizeof(ht_entry_t)); + hash_map = buffer_manager.GetBufferAllocator().TryAllocateHuge(capacity * sizeof(ht_entry_t)); } else { // Just use the current hash map capacity = current_capacity; } } else { // Allocate a hash map - hash_map = buffer_manager.GetBufferAllocator().Allocate(capacity * sizeof(ht_entry_t)); + hash_map = buffer_manager.GetBufferAllocator().TryAllocateHuge(capacity * sizeof(ht_entry_t)); } D_ASSERT(hash_map.GetSize() == capacity * sizeof(ht_entry_t)); @@ -1082,28 +1101,6 @@ void JoinHashTable::AllocatePointerTable() { {"size", to_string(data_collection->SizeInBytes() + hash_map.GetSize())}}); } -void JoinHashTable::PrepareBloomFilterForFinalize() { - if (!should_build_bloom_filter) { - return; - } - - // Finalize scans every build tuple and inserts its hash into the bloom filter. - // Make sure any existing filter has enough sectors for the actual build count. - const auto build_count = Count(); - const auto actual_init_count = MaxValue(build_count, 1); - if (bloom_filter.IsInitialized()) { - const auto current_sectors = BloomFilter::GetNumberOfSectors(bloom_filter_init_count); - const auto required_sectors = BloomFilter::GetNumberOfSectors(actual_init_count); - if (current_sectors >= required_sectors) { - return; - } - } - - bloom_filter.Reset(); - bloom_filter_init_count = actual_init_count; - bloom_filter.Initialize(context, bloom_filter_init_count); -} - void JoinHashTable::InitializePointerTable(idx_t entry_idx_from, idx_t entry_idx_to) { // initialize HT with all-zero entries auto entries = GetEntries(); @@ -1706,14 +1703,13 @@ void JoinHashTable::GatherRHS(Vector &row_ptrs, const SelectionVector &ptr_sel, // Pinned column: re-emit the upstream dictionary. Layout is [conditions, build, (found), hash], so payload // columns sit at layout index >= cond_count; guard the subtraction so it cannot underflow into a spurious idx. if (output_col_idx >= cond_count) { - const idx_t payload_idx = output_col_idx - cond_count; - if (payload_idx < dict_registry.size() && dict_registry[payload_idx]) { + auto dict_column = GetDictSurvivingColumn(output_col_idx - cond_count); + if (dict_column) { SelectionVector build_sel_vec(count); const auto col_offset = offsets[output_col_idx]; // Read the dict index at the width chosen at layout-publication time - const uint8_t index_width = payload_idx < dict_index_width.size() ? dict_index_width[payload_idx] : 0; - LoadDictIndices(index_width, ptrs, ptr_sel, count, col_offset, build_sel_vec); - vector.Dictionary(dict_registry[payload_idx], build_sel_vec, count); + LoadDictIndices(dict_column->index_width, ptrs, ptr_sel, count, col_offset, build_sel_vec); + vector.Dictionary(dict_column->entry, build_sel_vec, count); continue; } } @@ -2334,6 +2330,7 @@ void JoinHashTable::ScanFullOuter(JoinHTScanState &state, Vector &addresses, Dat break; } state.offset_in_chunk = 0; + state.chunks_done++; } while (iterator.Next()); // now gather from the found rows @@ -2562,16 +2559,17 @@ void JoinHashTable::ResetForNewIterationSinglePartition() { // layout was never published (no Sink chunk last iteration); the next first chunk will publish return; } - data_collection->Reset(); + // Return to the unpublished state (see FinishInitWithLayout): the next iteration's first chunk publishes the + // layout afresh. The rerun build is the same operator, but it may deliver a different dictionary (e.g. a + // nextval() in its build side), a dictionary of another size, or none at all, so a narrowed slot and the + // dictionary it indexes into cannot be carried over. + layout_ptr.reset(); + dict_info.reset(); + data_collection.reset(); // Always use a single partition (radix_bits=0) to avoid per-iteration overhead of resetting // and re-creating many radix partitions when only one thread builds the hash table. - if (radix_bits != 0) { - radix_bits = 0; - sink_collection = make_uniq(buffer_manager, layout_ptr, MemoryTag::HASH_TABLE, - idx_t(0), layout_ptr->ColumnCount() - 1, context); - } else { - sink_collection->Reset(); - } + radix_bits = 0; + sink_collection.reset(); InitializePartitionMasks(); capacity = DConstants::INVALID_INDEX; bitmask = DConstants::INVALID_INDEX; @@ -2580,9 +2578,8 @@ void JoinHashTable::ResetForNewIterationSinglePartition() { chains_longer_than_one.store(false, std::memory_order_relaxed); total_probe_matches = 0; load_factor = DEFAULT_LOAD_FACTOR; + bloom_filter.reset(); should_build_bloom_filter = false; - bloom_filter.Reset(); - bloom_filter_init_count = 0; prefix_range_filter.reset(); should_build_prefix_range_filter = false; ResetMarkJoinInfo(*this); @@ -2592,10 +2589,6 @@ void JoinHashTable::ResetForNewIterationSinglePartition() { aux_next_ptrs.Reset(); aux_next_ptrs_data = nullptr; use_dict_emission = false; - // Drop pinned upstream dictionary entries; the next iteration's first chunk re-pins - for (auto &entry : dict_registry) { - entry.reset(); - } } bool JoinHashTable::PrepareExternalFinalize(const idx_t max_ht_size) { @@ -2791,10 +2784,8 @@ bool JoinHashTable::CanUseDictionaryEmission(const PhysicalHashJoin &op, bool ex } // mutually exclusive with the dict-surviving path: that path narrows the payload slot to a 1/2/4-byte // index, while NEXT_PTR embedding here overwrites a different field and assumes payload bytes are intact. - for (const auto &entry : dict_registry) { - if (entry) { - return false; - } + if (HasDictSurvivingColumns()) { + return false; } // SINGLE joins need FlatVector::SetNull for unmatched rows; dictionary vectors cannot supply it if (join_type == JoinType::SINGLE) { diff --git a/src/duckdb/src/execution/mark_join_row_comparison.cpp b/src/duckdb/src/execution/mark_join_row_comparison.cpp index 8d9de1b41..e7de97107 100644 --- a/src/duckdb/src/execution/mark_join_row_comparison.cpp +++ b/src/duckdb/src/execution/mark_join_row_comparison.cpp @@ -2,6 +2,7 @@ #include "duckdb/common/vector/constant_vector.hpp" #include "duckdb/common/vector/struct_vector.hpp" +#include "duckdb/common/vector/flat_vector.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" namespace duckdb { @@ -76,4 +77,30 @@ void MarkJoinRowComparison::CompareEquality(const Vector &left, idx_t left_row, } } +void MarkJoinRowComparison::Compare(const Vector &left, const Vector &right, ExpressionType comparison_type, + Vector &result) { + result.SetVectorType(VectorType::FLAT_VECTOR); + FlatVector::ValidityMutable(result).Reset(right.size()); + switch (comparison_type) { + case ExpressionType::COMPARE_EQUAL: + return VectorOperations::Equals(left, right, result); + case ExpressionType::COMPARE_NOTEQUAL: + return VectorOperations::NotEquals(left, right, result); + case ExpressionType::COMPARE_LESSTHAN: + return VectorOperations::LessThan(left, right, result); + case ExpressionType::COMPARE_GREATERTHAN: + return VectorOperations::GreaterThan(left, right, result); + case ExpressionType::COMPARE_LESSTHANOREQUALTO: + return VectorOperations::LessThanEquals(left, right, result); + case ExpressionType::COMPARE_GREATERTHANOREQUALTO: + return VectorOperations::GreaterThanEquals(left, right, result); + case ExpressionType::COMPARE_DISTINCT_FROM: + return VectorOperations::DistinctFrom(left, right, result); + case ExpressionType::COMPARE_NOT_DISTINCT_FROM: + return VectorOperations::NotDistinctFrom(left, right, result); + default: + throw InternalException("Unsupported comparison type for MARK join"); + } +} + } // namespace duckdb diff --git a/src/duckdb/src/execution/nested_loop_join/nested_loop_join_mark.cpp b/src/duckdb/src/execution/nested_loop_join/nested_loop_join_mark.cpp index 3c614b2a4..e5cb1499c 100644 --- a/src/duckdb/src/execution/nested_loop_join/nested_loop_join_mark.cpp +++ b/src/duckdb/src/execution/nested_loop_join/nested_loop_join_mark.cpp @@ -1,5 +1,6 @@ #include "duckdb/common/operator/comparison_operators.hpp" #include "duckdb/common/vector/constant_vector.hpp" +#include "duckdb/common/vector/flat_vector.hpp" #include "duckdb/common/types/vector.hpp" #include "duckdb/common/uhugeint.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" @@ -83,16 +84,18 @@ static void MarkJoinNested(const Vector &left, const Vector &right, idx_t lcount count = VectorOperations::NotEquals(left_reference, right, nullptr, rcount, nullptr, nullptr, &null_mask); break; case ExpressionType::COMPARE_LESSTHAN: - count = VectorOperations::LessThan(left_reference, right, nullptr, rcount, nullptr, nullptr); + count = VectorOperations::LessThan(left_reference, right, nullptr, rcount, nullptr, nullptr, &null_mask); break; case ExpressionType::COMPARE_GREATERTHAN: - count = VectorOperations::GreaterThan(left_reference, right, nullptr, rcount, nullptr, nullptr); + count = VectorOperations::GreaterThan(left_reference, right, nullptr, rcount, nullptr, nullptr, &null_mask); break; case ExpressionType::COMPARE_LESSTHANOREQUALTO: - count = VectorOperations::LessThanEquals(left_reference, right, nullptr, rcount, nullptr, nullptr); + count = + VectorOperations::LessThanEquals(left_reference, right, nullptr, rcount, nullptr, nullptr, &null_mask); break; case ExpressionType::COMPARE_GREATERTHANOREQUALTO: - count = VectorOperations::GreaterThanEquals(left_reference, right, nullptr, rcount, nullptr, nullptr); + count = VectorOperations::GreaterThanEquals(left_reference, right, nullptr, rcount, nullptr, nullptr, + &null_mask); break; case ExpressionType::COMPARE_DISTINCT_FROM: count = VectorOperations::DistinctFrom(left_reference, right, nullptr, rcount, nullptr, nullptr); @@ -180,6 +183,7 @@ static void MarkJoinComparisonSwitch(const Vector &left, const Vector &right, id void NestedLoopJoinMark::Perform(DataChunk &left, ColumnDataCollection &right, bool found_match[], const vector &conditions, optional_ptr found_unknown) { + D_ASSERT(conditions.size() == 1); // initialize a new temporary selection vector for the left chunk // loop over all chunks in the RHS ColumnDataScanState scan_state; @@ -189,10 +193,8 @@ void NestedLoopJoinMark::Perform(DataChunk &left, ColumnDataCollection &right, b right.InitializeScanChunk(scan_chunk); while (right.Scan(scan_state, scan_chunk)) { - for (idx_t i = 0; i < conditions.size(); i++) { - MarkJoinComparisonSwitch(left.data[i], scan_chunk.data[i], left.size(), scan_chunk.size(), found_match, - conditions[i].GetComparisonType(), found_unknown); - } + MarkJoinComparisonSwitch(left.data[0], scan_chunk.data[0], left.size(), scan_chunk.size(), found_match, + conditions[0].GetComparisonType(), found_unknown); } } diff --git a/src/duckdb/src/execution/operator/aggregate/physical_limited_distinct.cpp b/src/duckdb/src/execution/operator/aggregate/physical_limited_distinct.cpp index b5726cfdf..72bc29b25 100644 --- a/src/duckdb/src/execution/operator/aggregate/physical_limited_distinct.cpp +++ b/src/duckdb/src/execution/operator/aggregate/physical_limited_distinct.cpp @@ -44,7 +44,7 @@ struct LimitedDistinctGlobalSinkState : public GlobalSinkState { LimitedDistinctGlobalSinkState(const PhysicalLimitedDistinct &op, ClientContext &context) : limit_reached(false) { auto aggregate_objects = GetAggregateObjects(op.aggregates); auto payload_types = GetPayloadTypes(op.aggregates); - ht = make_uniq(context, Allocator::Get(context), op.group_types, payload_types, + ht = make_uniq(context, BufferAllocator::Get(context), op.group_types, payload_types, aggregate_objects); } @@ -56,7 +56,7 @@ struct LimitedDistinctGlobalSinkState : public GlobalSinkState { struct LimitedDistinctLocalSinkState : public LocalSinkState { LimitedDistinctLocalSinkState(const PhysicalLimitedDistinct &op, ExecutionContext &context) : group_executor(context.client, op.groups), payload_executor(context.client) { - auto &allocator = Allocator::Get(context.client); + auto &allocator = BufferAllocator::Get(context.client); auto aggregate_objects = GetAggregateObjects(op.aggregates); auto payload_types = GetPayloadTypes(op.aggregates); @@ -154,7 +154,9 @@ SinkFinalizeType PhysicalLimitedDistinct::Finalize(Pipeline &pipeline, Event &ev // Source //===--------------------------------------------------------------------===// struct LimitedDistinctGlobalSourceState : public GlobalSourceState { - LimitedDistinctGlobalSourceState(const PhysicalLimitedDistinct &op, ClientContext &context) : initialized(false) { + LimitedDistinctGlobalSourceState(const PhysicalLimitedDistinct &op, ClientContext &context) + : initialized(false), total_rows(op.sink_state->Cast().ht->Count()), + rows_scanned(0) { group_chunk.Initialize(Allocator::Get(context), op.group_types); vector payload_types; @@ -169,6 +171,8 @@ struct LimitedDistinctGlobalSourceState : public GlobalSourceState { AggregateHTScanState scan_state; bool initialized; + const idx_t total_rows; + atomic rows_scanned; DataChunk group_chunk; DataChunk payload_chunk; }; @@ -177,6 +181,19 @@ unique_ptr PhysicalLimitedDistinct::GetGlobalSourceState(Clie return make_uniq(*this, context); } +ProgressData PhysicalLimitedDistinct::GetProgress(ClientContext &context, GlobalSourceState &gstate) const { + auto &state = gstate.Cast(); + ProgressData progress; + progress.total = double(MaxValue(state.total_rows, 1)); + progress.done = state.total_rows == 0 ? 1.0 : double(state.rows_scanned.load(std::memory_order_relaxed)); + return progress; +} + +void PhysicalLimitedDistinct::SourceFinished(ClientContext &context, GlobalSourceState &gstate) const { + auto &state = gstate.Cast(); + state.rows_scanned.store(state.total_rows, std::memory_order_relaxed); +} + unique_ptr PhysicalLimitedDistinct::GetLocalSourceState(ExecutionContext &context, GlobalSourceState &gstate) const { return make_uniq(); @@ -217,6 +234,7 @@ SourceResultType PhysicalLimitedDistinct::GetDataInternal(ExecutionContext &cont col++; } + gstate_source.rows_scanned.fetch_add(chunk.size(), std::memory_order_relaxed); return chunk.size() == 0 ? SourceResultType::FINISHED : SourceResultType::HAVE_MORE_OUTPUT; } diff --git a/src/duckdb/src/execution/operator/aggregate/physical_perfecthash_aggregate.cpp b/src/duckdb/src/execution/operator/aggregate/physical_perfecthash_aggregate.cpp index c0f339e65..8f8d3f944 100644 --- a/src/duckdb/src/execution/operator/aggregate/physical_perfecthash_aggregate.cpp +++ b/src/duckdb/src/execution/operator/aggregate/physical_perfecthash_aggregate.cpp @@ -1,6 +1,7 @@ #include "duckdb/execution/operator/aggregate/physical_perfecthash_aggregate.hpp" #include "duckdb/execution/perfect_aggregate_hashtable.hpp" +#include "duckdb/common/atomic.hpp" #include "duckdb/planner/expression/bound_aggregate_expression.hpp" #include "duckdb/planner/expression/bound_reference_expression.hpp" #include "duckdb/storage/buffer_manager.hpp" @@ -86,7 +87,7 @@ unique_ptr PhysicalPerfectHashAggregate::CreateHT(All class PerfectHashAggregateGlobalState : public GlobalSinkState { public: PerfectHashAggregateGlobalState(const PhysicalPerfectHashAggregate &op, ClientContext &context) - : ht(op.CreateHT(Allocator::Get(context), context)) { + : ht(op.CreateHT(BufferAllocator::Get(context), context)) { } //! The lock for updating the global aggregate state @@ -98,7 +99,7 @@ class PerfectHashAggregateGlobalState : public GlobalSinkState { class PerfectHashAggregateLocalState : public LocalSinkState { public: PerfectHashAggregateLocalState(const PhysicalPerfectHashAggregate &op, ExecutionContext &context) - : ht(op.CreateHT(Allocator::Get(context.client), context.client)) { + : ht(op.CreateHT(BufferAllocator::Get(context.client), context.client)) { group_chunk.InitializeEmpty(op.group_types); if (!op.payload_types.empty()) { aggregate_input_chunk.InitializeEmpty(op.payload_types); @@ -181,18 +182,32 @@ class PerfectHashAggregateState : public GlobalSourceState { //! The current position to scan the HT for output tuples idx_t ht_scan_position; + atomic scanned_slots {0}; }; unique_ptr PhysicalPerfectHashAggregate::GetGlobalSourceState(ClientContext &context) const { return make_uniq(); } +ProgressData PhysicalPerfectHashAggregate::GetProgress(ClientContext &context, GlobalSourceState &gstate_p) const { + auto &state = gstate_p.Cast(); + auto &gstate = sink_state->Cast(); + return ProgressData {double(state.scanned_slots.load()), double(gstate.ht->Capacity()), false}; +} + +void PhysicalPerfectHashAggregate::SourceFinished(ClientContext &context, GlobalSourceState &gstate_p) const { + auto &state = gstate_p.Cast(); + auto &gstate = sink_state->Cast(); + state.scanned_slots = gstate.ht->Capacity(); +} + SourceResultType PhysicalPerfectHashAggregate::GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const { auto &state = input.global_state.Cast(); auto &gstate = sink_state->Cast(); gstate.ht->Scan(state.ht_scan_position, chunk); + state.scanned_slots.store(state.ht_scan_position, std::memory_order_relaxed); if (chunk.size() > 0) { return SourceResultType::HAVE_MORE_OUTPUT; diff --git a/src/duckdb/src/execution/operator/aggregate/physical_ungrouped_aggregate.cpp b/src/duckdb/src/execution/operator/aggregate/physical_ungrouped_aggregate.cpp index 3ef9347db..9a6619476 100644 --- a/src/duckdb/src/execution/operator/aggregate/physical_ungrouped_aggregate.cpp +++ b/src/duckdb/src/execution/operator/aggregate/physical_ungrouped_aggregate.cpp @@ -21,6 +21,8 @@ #include "duckdb/planner/expression/bound_reference_expression.hpp" #include "duckdb/execution/operator/aggregate/ungrouped_aggregate_state.hpp" +#include "duckdb/common/atomic.hpp" + #include namespace duckdb { @@ -658,6 +660,20 @@ SinkFinalizeType PhysicalUngroupedAggregate::Finalize(Pipeline &pipeline, Event //===--------------------------------------------------------------------===// // Source //===--------------------------------------------------------------------===// +class UngroupedAggregateGlobalSourceState : public GlobalSourceState { +public: + atomic finished {false}; +}; + +unique_ptr PhysicalUngroupedAggregate::GetGlobalSourceState(ClientContext &context) const { + return make_uniq(); +} + +ProgressData PhysicalUngroupedAggregate::GetProgress(ClientContext &context, GlobalSourceState &gstate) const { + auto &state = gstate.Cast(); + return ProgressData {state.finished ? 1.0 : 0.0, 1.0, false}; +} + void VerifyNullHandling(DataChunk &chunk, UngroupedAggregateState &state, const vector> &aggregates) { if (DBConfigOptions::global_verification_mode != DebugVerificationMode::VERIFY_FUNCTIONS) { @@ -700,6 +716,7 @@ SourceResultType PhysicalUngroupedAggregate::GetDataInternal(ExecutionContext &c // initialize the result chunk with the aggregate values gstate.state.Finalize(chunk); VerifyNullHandling(chunk, gstate.state.state, aggregates); + input.global_state.Cast().finished = true; return SourceResultType::FINISHED; } diff --git a/src/duckdb/src/execution/operator/aggregate/physical_window.cpp b/src/duckdb/src/execution/operator/aggregate/physical_window.cpp index 9deb42f93..c96f8f9c9 100644 --- a/src/duckdb/src/execution/operator/aggregate/physical_window.cpp +++ b/src/duckdb/src/execution/operator/aggregate/physical_window.cpp @@ -1,5 +1,6 @@ #include "duckdb/execution/operator/aggregate/physical_window.hpp" +#include "duckdb/common/once_flag.hpp" #include "duckdb/common/sorting/sort_strategy.hpp" #include "duckdb/common/types/row/tuple_data_collection.hpp" #include "duckdb/common/types/row/tuple_data_iterator.hpp" @@ -156,16 +157,21 @@ class WindowHashGroup { idx_t count = 0; //! The number of blocks in the group idx_t blocks = 0; - //! The partition boundary mask + //! The partition boundary mask. The masks are O(rows): their storage comes from the buffer allocator and the + //! masks point into it ValidityMask partition_mask; + AllocatedData partition_mask_data; //! The order boundary mask (maps sort column END to mask) OrderMasks order_masks; + unordered_map order_mask_data; + OnceFlag masks_initialized; //! The fully materialised data collection unique_ptr collection; //! The processing stage for this group atomic stage; //! The function global states for this hash group ExecutorGlobalStates gestates; + OnceFlag gestates_initialized; //! Executor local states, one per thread ThreadLocalStates thread_states; @@ -216,6 +222,8 @@ class WindowGlobalSinkState : public GlobalSinkState { GlobalStatePtr strategy_sink; //! The number of sunk rows (for progress) atomic count; + //! Keeps the sink progress monotonic + MonotonicProgress sink_progress; //! The execution functions Executors executors; //! The shared expressions library @@ -230,6 +238,7 @@ class WindowGlobalSinkState : public GlobalSinkState { // sort sink holds per-iteration materialized data, so replace that while preserving the setup. strategy_sink = sort_strategy->GetGlobalSinkState(context); count = 0; + sink_progress.Reset(); GlobalSinkState::Reset(context); } }; @@ -384,8 +393,7 @@ ProgressData PhysicalWindow::GetSinkProgress(ClientContext &context, GlobalSinkS auto progress = source_progress; lock_guard sinks_guard(gsink.lock); progress.Add(gsink.sort_strategy->GetSinkProgress(context, *gsink.strategy_sink, progress)); - - return progress; + return gsink.sink_progress.Update(progress); } //===--------------------------------------------------------------------===// @@ -410,7 +418,9 @@ class WindowGlobalSourceState : public GlobalSourceState { return !stopped && finished < total_tasks; } //! Get the next task given the current state - bool TryNextTask(TaskPtr &task, Task &task_local); + bool TryNextTask(TaskPtr &task, Task &task_local) DUCKDB_EXCLUDES(lock); + //! Select a task with the lock held, after the previous task has been retired. + bool TryNextTaskLocked(TaskPtr &task, Task &task_local) DUCKDB_REQUIRES(lock); //! Context for executing computations ClientContext &client; @@ -449,8 +459,8 @@ class WindowGlobalSourceState : public GlobalSourceState { protected: //! Build task list void CreateTaskList(); - //! Finish a task - void FinishTask(TaskPtr task); + //! Finish a task and return any completed group for destruction outside the source lock. + WindowHashGroupPtr FinishTask(TaskPtr task); }; WindowGlobalSourceState::WindowGlobalSourceState(ClientContext &client, WindowGlobalSinkState &gsink_p) @@ -553,27 +563,37 @@ void WindowHashGroup::UpdateScanner(ScannerPtr &scanner, idx_t begin_idx) const } } -void WindowHashGroup::AllocateMasks() { - // Single-threaded building as this is mostly memory allocation - lock_guard gestate_guard(lock); - if (partition_mask.IsMaskSet()) { - return; - } - - // Allocate masks inside the lock - partition_mask.Initialize(count); - - const auto &executors = gsink.executors; - for (auto &wexec : executors) { - auto &wexpr = wexec->wexpr; +//! Backs a mask of `count` rows (all valid) with a block from the allocator, so that it counts against the memory limit +static void AllocateMask(Allocator &allocator, idx_t count, ValidityMask &mask, AllocatedData &storage) { + storage = allocator.Allocate(ValidityMask::EntryCount(count) * sizeof(validity_t)); + memset(storage.get(), 0xFF, storage.GetSize()); + mask.Initialize(reinterpret_cast(storage.get()), count); +} - const auto order_begin = gsink.op.partition_info.RequiresPartitionColumns() ? 0 : wexpr.Partitions().size(); - auto &order_mask = order_masks[order_begin + wexpr.OrderBy().size()]; - if (order_mask.IsMaskSet()) { - continue; +void WindowHashGroup::AllocateMasks() { + CallOnce(masks_initialized, [&] { + auto &allocator = BufferAllocator::Get(gsink.client); + ValidityMask new_partition_mask; + AllocatedData new_partition_mask_data; + AllocateMask(allocator, count, new_partition_mask, new_partition_mask_data); + OrderMasks new_order_masks; + unordered_map new_order_mask_data; + + // All masks refer to the shared sort's partition columns, including duplicate expressions. + const auto &sort_expr = gsink.op.select_list[gsink.op.order_idx]->Cast(); + const auto order_begin = gsink.op.partition_info.RequiresPartitionColumns() ? 0 : sort_expr.Partitions().size(); + for (auto &wexec : gsink.executors) { + const auto order_idx = order_begin + wexec->wexpr.OrderBy().size(); + auto &order_mask = new_order_masks[order_idx]; + if (!order_mask.IsMaskSet()) { + AllocateMask(allocator, count, order_mask, new_order_mask_data[order_idx]); + } } - order_mask.Initialize(count); - } + partition_mask = std::move(new_partition_mask); + partition_mask_data = std::move(new_partition_mask_data); + order_masks = std::move(new_order_masks); + order_mask_data = std::move(new_order_mask_data); + }); } void WindowHashGroup::ComputeMasks(const idx_t block_begin, const idx_t block_end) { @@ -794,21 +814,17 @@ void WindowLocalSourceState::Mask(ExecutionContext &context, InterruptState &int } WindowHashGroup::ExecutorGlobalStates &WindowHashGroup::GetGlobalStates(ClientContext &client) { - // Single-threaded building as this is mostly memory allocation - lock_guard gestate_guard(lock); - const auto &executors = gsink.executors; - if (gestates.size() == executors.size()) { - return gestates; - } - - // These can be large so we defer building them until we are ready. - for (auto &wexec : executors) { - auto &wexpr = wexec->wexpr; - const auto order_begin = gsink.op.partition_info.RequiresPartitionColumns() ? 0 : wexpr.Partitions().size(); - auto &order_mask = order_masks[order_begin + wexpr.OrderBy().size()]; - gestates.emplace_back(wexec->GetGlobalState(client, count, partition_mask, order_mask)); - } - + CallOnce(gestates_initialized, [&] { + // These can be large so we defer building them until we are ready. + ExecutorGlobalStates new_gestates; + const auto &sort_expr = gsink.op.select_list[gsink.op.order_idx]->Cast(); + const auto order_begin = gsink.op.partition_info.RequiresPartitionColumns() ? 0 : sort_expr.Partitions().size(); + for (auto &wexec : gsink.executors) { + auto &order_mask = order_masks.at(order_begin + wexec->wexpr.OrderBy().size()); + new_gestates.emplace_back(wexec->GetGlobalState(client, count, partition_mask, order_mask)); + } + gestates = std::move(new_gestates); + }); return gestates; } @@ -926,9 +942,14 @@ WindowLocalSourceState::WindowLocalSourceState(WindowGlobalSourceState &gsource) } bool WindowGlobalSourceState::TryNextTask(TaskPtr &task, Task &task_local) { + // Destroy completed groups after releasing the source lock. + WindowHashGroupPtr finished_group; annotated_lock_guard guard(lock); - FinishTask(task); + finished_group = FinishTask(task); + return TryNextTaskLocked(task, task_local); +} +bool WindowGlobalSourceState::TryNextTaskLocked(TaskPtr &task, Task &task_local) { if (!HasMoreTasks()) { task = nullptr; return false; @@ -971,17 +992,18 @@ bool WindowGlobalSourceState::TryNextTask(TaskPtr &task, Task &task_local) { return false; } -void WindowGlobalSourceState::FinishTask(TaskPtr task) { +WindowGlobalSourceState::WindowHashGroupPtr WindowGlobalSourceState::FinishTask(TaskPtr task) { if (!task) { - return; + return nullptr; } const auto group_idx = task->group_idx; auto &finished_hash_group = window_hash_groups[group_idx]; D_ASSERT(finished_hash_group); + WindowHashGroupPtr finished_group; if (++finished_hash_group->completed >= finished_hash_group->GetTaskCount()) { - finished_hash_group.reset(); + finished_group = std::move(finished_hash_group); // Remove it from the active groups auto &v = active_groups; v.erase(std::remove(v.begin(), v.end(), group_idx), v.end()); @@ -989,6 +1011,7 @@ void WindowGlobalSourceState::FinishTask(TaskPtr task) { // Count the global tasks completed. ++completed; + return finished_group; } bool WindowLocalSourceState::TryAssignTask() { @@ -1127,11 +1150,11 @@ ProgressData PhysicalWindow::GetProgress(ClientContext &client, GlobalSourceStat auto &gsource = gsource_p.Cast(); auto &gsink = gsource.gsink; const auto count = gsink.count.load(); - const auto completed = gsource.completed.load(); + const auto finished = gsource.finished.load(); ProgressData res; if (count) { - res.done = double(completed); + res.done = double(finished); res.total = double(gsource.total_tasks); // Convert to tuples. res.Normalize(double(count)); @@ -1171,11 +1194,15 @@ SourceResultType PhysicalWindow::GetDataInternal(ExecutionContext &context, Data // no more tasks - exit gsource.UnblockTasks(); break; - } else { - // there are more tasks available, but we can't execute them yet - // block the source - return gsource.BlockSource(source.interrupt_state); } + // TryAssignTask released the lock: another thread may have prepared the next stage and unblocked the + // blocked tasks in between, so look again before blocking, or no one would ever unblock this task. + if (gsource.TryNextTaskLocked(lsource.task, lsource.task_local)) { + continue; + } + // there are more tasks available, but we can't execute them yet + // block the source + return gsource.BlockSource(source.interrupt_state); } } diff --git a/src/duckdb/src/execution/operator/csv_scanner/buffer_manager/csv_buffer.cpp b/src/duckdb/src/execution/operator/csv_scanner/buffer_manager/csv_buffer.cpp index 1b5e27843..8972d94c8 100644 --- a/src/duckdb/src/execution/operator/csv_scanner/buffer_manager/csv_buffer.cpp +++ b/src/duckdb/src/execution/operator/csv_scanner/buffer_manager/csv_buffer.cpp @@ -80,17 +80,24 @@ void CSVBuffer::LoadRandomAccess(CSVFileHandle &file_handle) { shared_ptr CSVBuffer::Pin(CSVFileHandle &file_handle, bool &has_seeked) { lock_guard guard(load_lock); - if (!IsInMemory()) { - // We have to reload it from disk - block = nullptr; - if (random_access) { - LoadRandomAccess(file_handle); - } else { - Reload(file_handle); - has_seeked = true; + while (true) { + if (!IsInMemory()) { + // We have to reload it from disk + block = nullptr; + if (random_access) { + LoadRandomAccess(file_handle); + } else { + Reload(file_handle); + has_seeked = true; + } + } + auto pinned = CreatePin(); + if (pinned->handle.IsValid()) { + return pinned; } + // the block is destroyable and was evicted between the in-memory check and the pin - reload it + block = nullptr; } - return CreatePin(); } bool CSVBuffer::TryPin(shared_ptr &pinned) { diff --git a/src/duckdb/src/execution/operator/csv_scanner/buffer_manager/csv_file_handle.cpp b/src/duckdb/src/execution/operator/csv_scanner/buffer_manager/csv_file_handle.cpp index 0b7ae7c30..8e922902b 100644 --- a/src/duckdb/src/execution/operator/csv_scanner/buffer_manager/csv_file_handle.cpp +++ b/src/duckdb/src/execution/operator/csv_scanner/buffer_manager/csv_file_handle.cpp @@ -103,7 +103,7 @@ idx_t CSVFileHandle::Read(void *buffer, idx_t nr_bytes) { if (!finished) { finished = bytes_read == 0; } - uncompressed_bytes_read += static_cast(bytes_read); + uncompressed_bytes_read.fetch_add(static_cast(bytes_read), std::memory_order_relaxed); return UnsafeNumericCast(bytes_read); } diff --git a/src/duckdb/src/execution/operator/csv_scanner/encode/csv_encoder.cpp b/src/duckdb/src/execution/operator/csv_scanner/encode/csv_encoder.cpp index 7184daeb7..7e5207277 100644 --- a/src/duckdb/src/execution/operator/csv_scanner/encode/csv_encoder.cpp +++ b/src/duckdb/src/execution/operator/csv_scanner/encode/csv_encoder.cpp @@ -68,11 +68,15 @@ CSVEncoder::CSVEncoder(ClientContext &context_p, const string &encoding_name_to_ throw InvalidInputException(error.str()); } + encoding_function = function; + if (encoding_name == "utf-8") { + // UTF-8 is read straight into the CSV buffers (see CSVFileHandle::Read), the encoder buffers are never used + return; + } // The encoded buffer is even for the two byte lookup of utf-16, and fits a full lookup plus the probe byte const idx_t lookup_bytes = function->GetLookupBytes(); encoded_buffer.Initialize(MaxValue(buffer_size - buffer_size % 2, lookup_bytes + lookup_bytes % 2)); remaining_bytes_buffer.Initialize(function->GetBytesPerIteration()); - encoding_function = function; } void CSVEncoder::Reset() { diff --git a/src/duckdb/src/execution/operator/csv_scanner/scanner/string_value_scanner.cpp b/src/duckdb/src/execution/operator/csv_scanner/scanner/string_value_scanner.cpp index b5d33d1f7..96773a018 100644 --- a/src/duckdb/src/execution/operator/csv_scanner/scanner/string_value_scanner.cpp +++ b/src/duckdb/src/execution/operator/csv_scanner/scanner/string_value_scanner.cpp @@ -167,7 +167,8 @@ bool StringValueResult::HandleTooManyColumnsError(const char *value_ptr, const i // we make an exception if the first over-value is null bool is_value_null = false; for (idx_t i = 0; i < null_str_count; i++) { - is_value_null = is_value_null || IsValueNull(null_str_ptr[i], value_ptr, size); + is_value_null = + is_value_null || (size == null_str_size[i] && IsValueNull(null_str_ptr[i], value_ptr, size)); } error = !is_value_null; } @@ -248,7 +249,8 @@ void StringValueResult::AddValueToVector(const char *value_ptr, idx_t size, bool // we make an exception if the first over-value is null bool is_value_null = false; for (idx_t i = 0; i < null_str_count; i++) { - is_value_null = is_value_null || IsValueNull(null_str_ptr[i], value_ptr, size); + is_value_null = + is_value_null || (size == null_str_size[i] && IsValueNull(null_str_ptr[i], value_ptr, size)); } error = !is_value_null; } @@ -596,7 +598,7 @@ void StringValueResult::AddQuotedValue(StringValueResult &result, const idx_t bu void StringValueResult::AddPossiblyEscapedValue(StringValueResult &result, const idx_t buffer_pos, const char *value_ptr, const idx_t length, const bool empty) { if (result.escaped) { - if (result.projecting_columns) { + if (result.projecting_columns && result.cur_col_id < result.number_of_columns) { if (!result.projected_columns[result.cur_col_id]) { result.cur_col_id++; result.escaped = false; @@ -671,7 +673,7 @@ void StringValueResult::AddValue(StringValueResult &result, const idx_t buffer_p AddQuotedValue(result, buffer_pos - result.extra_delimiter_bytes); } else if (result.escaped) { AddPossiblyEscapedValue(result, buffer_pos, result.buffer_ptr + result.last_position.buffer_pos, - buffer_pos - result.last_position.buffer_pos, false); + result.HandleMultiDelimiter(buffer_pos), false); } else { result.AddValueToVector(result.buffer_ptr + result.last_position.buffer_pos, result.HandleMultiDelimiter(buffer_pos)); @@ -747,7 +749,10 @@ bool LineError::HandleErrors(StringValueResult &result) { result.current_line_position.begin.GetGlobalPosition(result.requested_size, first_nl), line_pos.GetGlobalPosition(result.requested_size), result.path); } - if (!StringValueScanner::CanDirectlyCast(result.csv_file_scan->file_types[col_idx], result.icu_loaded)) { + // the file types are those of the projected columns + auto &file_types = result.csv_file_scan->file_types; + if (cur_error.chunk_idx < file_types.size() && + !StringValueScanner::CanDirectlyCast(file_types[cur_error.chunk_idx], result.icu_loaded)) { result.number_of_rows--; } break; @@ -1531,12 +1536,22 @@ void StringValueScanner::ProcessOverBufferValue() { } if (!skip_value) { string_t value; + const auto &delimiter = result.state_machine.dialect_options.state_machine_options.delimiter.GetValue(); + const idx_t extra_delimiter_bytes = states.IsDelimiter() && !delimiter.empty() ? delimiter.size() - 1 : 0; + bool removed_extra_delimiter_bytes = false; if (result.quoted && !result.comment) { idx_t length = over_buffer_string.size() - 1 - result.quoted_position; while (length > 0 && result.ignore_empty_values && over_buffer_string.c_str()[result.quoted_position + length] == ' ') { length--; } + // remove the extra delimiter bytes before removing escapes, which can change the length of the value + if (extra_delimiter_bytes > length) { + throw InternalException( + "Value size is lower than the number of extra delimiter bytes in the ProcessOverBufferValue()"); + } + length -= extra_delimiter_bytes; + removed_extra_delimiter_bytes = true; value = string_t(over_buffer_string.c_str() + result.quoted_position, UnsafeNumericCast(length)); if (result.escaped) { if (result.cur_col_id >= result.number_of_columns && @@ -1561,12 +1576,11 @@ void StringValueScanner::ProcessOverBufferValue() { } return; } - value = - RemoveEscape(str_ptr, over_buffer_string.size() - 2, - state_machine->dialect_options.state_machine_options.escape.GetValue(), - state_machine->dialect_options.state_machine_options.quote.GetValue(), - result.state_machine.dialect_options.state_machine_options.strict_mode.GetValue(), - result.parse_chunk.data[result.chunk_col_id]); + value = RemoveEscape( + str_ptr, length, state_machine->dialect_options.state_machine_options.escape.GetValue(), + state_machine->dialect_options.state_machine_options.quote.GetValue(), + result.state_machine.dialect_options.state_machine_options.strict_mode.GetValue(), + result.parse_chunk.data[result.chunk_col_id]); } } } else { @@ -1606,10 +1620,7 @@ void StringValueScanner::ProcessOverBufferValue() { result.EmptyLine(result, iterator.pos.buffer_pos); } else if (!states.IsNotSet() && (!result.comment || !value.Empty())) { idx_t value_size = value.GetSize(); - if (states.IsDelimiter() && - !result.state_machine.dialect_options.state_machine_options.delimiter.GetValue().empty()) { - idx_t extra_delimiter_bytes = - result.state_machine.dialect_options.state_machine_options.delimiter.GetValue().size() - 1; + if (!removed_extra_delimiter_bytes) { if (extra_delimiter_bytes > value_size) { throw InternalException( "Value size is lower than the number of extra delimiter bytes in the ProcessOverBufferValue()"); @@ -1753,12 +1764,16 @@ bool StringValueScanner::FirstValueEndsOnQuote(CSVIterator iterator) const { CSVStates current_state; current_state.Initialize(CSVState::STANDARD); const idx_t to_pos = iterator.GetEndPos(); + const auto quote = state_machine->dialect_options.state_machine_options.quote.GetValue(); + const auto start_pos = iterator.pos.buffer_pos; while (iterator.pos.buffer_pos < to_pos) { state_machine->Transition(current_state, buffer_handle_ptr[iterator.pos.buffer_pos++]); if (current_state.IsState(CSVState::DELIMITER) || current_state.IsState(CSVState::CARRIAGE_RETURN) || current_state.IsState(CSVState::RECORD_SEPARATOR)) { - return buffer_handle_ptr[iterator.pos.buffer_pos - 2] == - state_machine->dialect_options.state_machine_options.quote.GetValue(); + // a value that also starts with a quote is a regular quoted value + const auto value_end = iterator.pos.buffer_pos - 2; + return value_end > start_pos && buffer_handle_ptr[value_end] == quote && + buffer_handle_ptr[start_pos] != quote; } } return false; diff --git a/src/duckdb/src/execution/operator/csv_scanner/sniffer/csv_sniffer.cpp b/src/duckdb/src/execution/operator/csv_scanner/sniffer/csv_sniffer.cpp index d523f2b53..aa0f8d6eb 100644 --- a/src/duckdb/src/execution/operator/csv_scanner/sniffer/csv_sniffer.cpp +++ b/src/duckdb/src/execution/operator/csv_scanner/sniffer/csv_sniffer.cpp @@ -106,8 +106,10 @@ AdaptiveSnifferResult CSVSniffer::MinimalSniff() { // Parse chunk and read csv with info candidate auto &data_chunk = scanner->ParseChunk().ToChunk(); idx_t start_row = 0; - if (sniffed_column_counts.result_position == 2) { - // If equal to two, we will only use the second row for type checking + const bool should_skip_header = + !options.dialect_options.header.IsSetByUser() || options.dialect_options.header.GetValue(); + if (sniffed_column_counts.result_position > 1 && should_skip_header) { + // Skip the potential header when detecting types start_row = 1; } @@ -149,7 +151,7 @@ SnifferResult CSVSniffer::AdaptiveSniff(const CSVSchema &file_schema) { // Check if we are happy with the result or if we need to do more sniffing if (!error_handler->AnyErrors() && !detection_error_handler->AnyErrors()) { // If we got no errors, we also run full if schemas do not match. - if (!set_columns.IsSet() && !file_options.AnySet()) { + if (!set_columns.IsSet()) { string error; run_full = !file_schema.SchemasMatch(error, min_sniff_res, options.file_path, true); } diff --git a/src/duckdb/src/execution/operator/csv_scanner/table_function/csv_file_scanner.cpp b/src/duckdb/src/execution/operator/csv_scanner/table_function/csv_file_scanner.cpp index 099b91364..794f43b8d 100644 --- a/src/duckdb/src/execution/operator/csv_scanner/table_function/csv_file_scanner.cpp +++ b/src/duckdb/src/execution/operator/csv_scanner/table_function/csv_file_scanner.cpp @@ -10,8 +10,9 @@ CSVFileScan::CSVFileScan(ClientContext &context, const OpenFileInfo &file_p, CSV const MultiFileOptions &file_options, const vector &names, const vector &types, CSVSchema &file_schema, bool per_file_single_threaded, shared_ptr buffer_manager_p, bool fixed_schema) - : BaseFileReader(file_p), buffer_manager(std::move(buffer_manager_p)), - error_handler(make_shared_ptr(options_p.ignore_errors.GetValue())), + : file(file_p), buffer_manager(std::move(buffer_manager_p)), + error_handler( + make_shared_ptr(options_p.ignore_errors.GetValue(), options_p.store_rejects.GetValue())), options(std::move(options_p)) { // Initialize Buffer Manager if (!buffer_manager) { @@ -51,7 +52,8 @@ CSVFileScan::CSVFileScan(ClientContext &context, const OpenFileInfo &file_p, CSV CSVFileScan::CSVFileScan(ClientContext &context, const OpenFileInfo &file_p, const CSVReaderOptions &options_p, const MultiFileOptions &file_options) - : BaseFileReader(file_p), error_handler(make_shared_ptr(options_p.ignore_errors.GetValue())), + : file(file_p), error_handler(make_shared_ptr(options_p.ignore_errors.GetValue(), + options_p.store_rejects.GetValue())), options(options_p) { buffer_manager = CSVBufferManager::Open(context, options, file); // Initialize On Disk and Size of file @@ -78,9 +80,6 @@ CSVFileScan::CSVFileScan(ClientContext &context, const OpenFileInfo &file_p, con SetStart(); } -CSVUnionData::~CSVUnionData() { -} - void CSVFileScan::SetStart() { idx_t rows_to_skip = options.GetSkipRows() + state_machine->dialect_options.header.GetValue(); @@ -159,4 +158,34 @@ void CSVFileScan::Finish() { buffer_manager.reset(); } +double CSVFileScan::GetProgressInFile(ClientContext &context) { + auto manager = buffer_manager; + if (!manager) { + // We are done with this file, so it's 100% + return 100.0; + } + if (file_size == 0) { + // empty file + return 100.0; + } + auto &file_handle = *manager->file_handle; + if (file_handle.compression_type == FileCompressionType::GZIP || + file_handle.compression_type == FileCompressionType::ZSTD) { + // compressed file: the file is decompressed ahead of parsing - the progress is the fraction of the file that + // was decompressed, times the fraction of the decompressed bytes that was parsed + auto decompressed_fraction = file_handle.GetProgress() / static_cast(file_size); + auto decompressed_bytes = file_handle.UncompressedBytesRead(); + double parsed_fraction = 0; + if (decompressed_bytes > 0) { + parsed_fraction = MinValue(static_cast(bytes_read.load(std::memory_order_relaxed)) / + static_cast(decompressed_bytes), + 1.0); + } + auto progress = compressed_progress.Update( + ProgressData {MinValue(decompressed_fraction, 1.0) * parsed_fraction, 1.0, false}); + return progress.done * 100.0; + } + return static_cast(bytes_read.load(std::memory_order_relaxed)) / static_cast(file_size) * 100.0; +} + } // namespace duckdb diff --git a/src/duckdb/src/execution/operator/csv_scanner/table_function/csv_multi_file_info.cpp b/src/duckdb/src/execution/operator/csv_scanner/table_function/csv_multi_file_info.cpp deleted file mode 100644 index 4684e328e..000000000 --- a/src/duckdb/src/execution/operator/csv_scanner/table_function/csv_multi_file_info.cpp +++ /dev/null @@ -1,490 +0,0 @@ -#include "duckdb/execution/operator/csv_scanner/csv_multi_file_info.hpp" -#include "duckdb/execution/operator/csv_scanner/csv_file_scanner.hpp" -#include "duckdb/execution/operator/csv_scanner/global_csv_state.hpp" -#include "duckdb/execution/operator/csv_scanner/sniffer/csv_sniffer.hpp" -#include "duckdb/execution/operator/csv_scanner/csv_buffer.hpp" -#include "duckdb/execution/operator/persistent/csv_rejects_table.hpp" -#include "duckdb/common/bind_helpers.hpp" -#include "duckdb/common/exception.hpp" -#include "duckdb/parallel/async_result.hpp" -#include "duckdb/parallel/callback_async_task.hpp" - -namespace duckdb { - -unique_ptr CSVMultiFileInfo::CreateInterface(ClientContext &context) { - return make_uniq(); -} - -unique_ptr CSVMultiFileInfo::InitializeOptions(ClientContext &context, - optional_ptr info) { - return make_uniq(); -} - -bool CSVMultiFileInfo::ParseCopyOption(ClientContext &context, const Identifier &key, const vector &values, - BaseFileReaderOptions &options_p, vector &expected_names, - vector &expected_types) { - auto &options = options_p.Cast(); - options.options.SetReadOption(key, ConvertVectorToValue(values), expected_names); - return true; -} - -bool CSVMultiFileInfo::ParseOption(ClientContext &context, const Identifier &key, const Value &val, - MultiFileOptions &file_options, BaseFileReaderOptions &options_p) { - auto &options = options_p.Cast(); - options.options.ParseOption(context, key, val); - return true; -} - -void CSVMultiFileInfo::FinalizeCopyBind(ClientContext &context, BaseFileReaderOptions &options_p, - const vector &expected_names, - const vector &expected_types) { - auto &options = options_p.Cast(); - auto &csv_options = options.options; - csv_options.name_list = expected_names; - csv_options.sql_type_list = expected_types; - csv_options.columns_set = true; - for (idx_t i = 0; i < expected_types.size(); i++) { - csv_options.sql_types_per_column[expected_names[i]] = i; - } -} - -unique_ptr CSVMultiFileInfo::InitializeBindData(MultiFileBindData &multi_file_data, - unique_ptr options_p) { - auto &options = options_p->Cast(); - auto csv_data = make_uniq(); - csv_data->options = std::move(options.options); - if (multi_file_data.file_list->GetExpandResult() == FileExpandResult::MULTIPLE_FILES) { - csv_data->options.multi_file_reader = true; - } - csv_data->options.Verify(multi_file_data.file_options); - return std::move(csv_data); -} - -//! Function to do schema discovery over one CSV file or a list/glob of CSV files -CSVSchema CSVSchemaDiscovery::SchemaDiscovery(ClientContext &context, shared_ptr &buffer_manager, - CSVReaderOptions &options, const MultiFileOptions &file_options, - vector &return_types, vector &names, - MultiFileList &multi_file_list) { - vector schemas; - const auto option_og = options; - - const auto file_paths = multi_file_list.GetAllFiles(); - - // Here what we want to do is to sniff a given number of lines, if we have many files, we might go through them - // to reach the number of lines. - const idx_t required_number_of_lines = options.sniff_size * options.sample_size_chunks; - - idx_t total_number_of_rows = 0; - idx_t current_file = 0; - options.file_path = file_paths[current_file].path; - - buffer_manager = CSVBufferManager::Open(context, options, options.file_path, false); - idx_t only_header_or_empty_files = 0; - - { - CSVSniffer sniffer(options, file_options, buffer_manager, CSVStateMachineCache::Get(context)); - auto sniffer_result = sniffer.SniffCSV(); - idx_t rows_read = sniffer.LinesSniffed() - - (options.dialect_options.skip_rows.GetValue() + options.dialect_options.header.GetValue()); - - schemas.emplace_back(sniffer_result.names, sniffer_result.return_types, file_paths[0].path, rows_read, - buffer_manager->GetBuffer(0)->actual_size == 0); - total_number_of_rows += sniffer.LinesSniffed(); - current_file++; - if (sniffer.EmptyOrOnlyHeader()) { - only_header_or_empty_files++; - } - } - - // We do a copy of the options to not pollute the options of the first file. - idx_t max_files_to_sniff = static_cast(options.files_to_sniff == -1) - ? NumericLimits::Maximum() - : static_cast(options.files_to_sniff); - idx_t files_to_sniff = file_paths.size() > max_files_to_sniff ? max_files_to_sniff : file_paths.size(); - while (total_number_of_rows < required_number_of_lines && current_file < files_to_sniff) { - auto option_copy = option_og; - option_copy.file_path = file_paths[current_file].path; - auto file_buffer_manager = CSVBufferManager::Open(context, option_copy, option_copy.file_path, false); - // TODO: We could cache the sniffer to be reused during scanning. Currently that's an exercise left to the - // reader - CSVSniffer sniffer(option_copy, file_options, file_buffer_manager, CSVStateMachineCache::Get(context)); - auto sniffer_result = sniffer.SniffCSV(); - idx_t rows_read = sniffer.LinesSniffed() - (option_copy.dialect_options.skip_rows.GetValue() + - option_copy.dialect_options.header.GetValue()); - if (file_buffer_manager->GetBuffer(0)->actual_size == 0) { - schemas.emplace_back(true); - } else { - schemas.emplace_back(sniffer_result.names, sniffer_result.return_types, option_copy.file_path, rows_read); - } - total_number_of_rows += sniffer.LinesSniffed(); - if (sniffer.EmptyOrOnlyHeader()) { - only_header_or_empty_files++; - } - current_file++; - } - - // We might now have multiple schemas, we need to go through them to define the one true schema - CSVSchema best_schema; - for (auto &schema : schemas) { - if (best_schema.Empty()) { - // A schema is bettah than no schema - best_schema = schema; - } else if (best_schema.GetRowsRead() == 0) { - // If the best-schema has no data-rows, that's easy; we just take the new schema - best_schema = schema; - } else if (schema.GetRowsRead() != 0) { - // We might have conflicting-schemas, we must merge them - best_schema.MergeSchemas(schema, options.null_padding); - } - } - - // At this point, replace a sqlnull with varchar for the type - best_schema.ReplaceNullWithVarchar(); - - if (names.empty()) { - names = StringsToIdentifiers(best_schema.GetNames()); - return_types = best_schema.GetTypes(); - } - if (names.empty() && return_types.empty()) { - throw InvalidInputException("No columns found in CSV files. Provide the columns option or ensure at least one " - "file contains a header or data row."); - } - if (only_header_or_empty_files == current_file && !options.columns_set) { - for (idx_t i = 0; i < return_types.size(); i++) { - if (!options.sql_types_per_column.empty()) { - if (options.sql_types_per_column.find(names[i]) != options.sql_types_per_column.end()) { - continue; - } - } else if (i < options.sql_type_list.size()) { - continue; - } - // we default to varchar if all files are empty or only have a header after all the sniffing - return_types[i] = LogicalType::VARCHAR; - } - } - return best_schema; -} - -void CSVMultiFileInfo::BindReader(ClientContext &context, vector &return_types, vector &names, - MultiFileBindData &bind_data) { - auto &csv_data = bind_data.bind_data->Cast(); - auto &multi_file_list = *bind_data.file_list; - auto &options = csv_data.options; - if (!bind_data.file_options.union_by_name) { - if (options.auto_detect) { - csv_data.csv_schema = - CSVSchemaDiscovery::SchemaDiscovery(context, csv_data.buffer_manager, options, bind_data.file_options, - return_types, names, multi_file_list); - } else { - // If we are not running the sniffer, the columns must be set! - if (!options.columns_set) { - throw BinderException("read_csv requires columns to be specified through the 'columns' option. Use " - "read_csv_auto or set read_csv(..., " - "AUTO_DETECT=TRUE) to automatically guess columns."); - } - names = options.name_list; - return_types = options.sql_type_list; - } - if (return_types.size() != names.size()) { - throw BinderException("read_csv: mismatch between the number of column names (%d) and column types (%d)", - names.size(), return_types.size()); - } - csv_data.options.dialect_options.num_cols = names.size(); - - bind_data.multi_file_reader->BindOptions(bind_data.file_options, multi_file_list, return_types, names, - bind_data.reader_bind); - } else { - CSVFileReaderOptions csv_options(options); - bind_data.reader_bind = bind_data.multi_file_reader->BindUnionReader( - context, return_types, names, multi_file_list, bind_data, csv_options, bind_data.file_options); - if (bind_data.union_readers.size() > 1) { - for (idx_t i = 0; i < bind_data.union_readers.size(); i++) { - auto &csv_union_data = bind_data.union_readers[i]->Cast(); - csv_data.column_info.emplace_back(csv_union_data.names, csv_union_data.types); - } - } - if (!options.sql_types_per_column.empty()) { - const auto exception = CSVError::ColumnTypesError(options.sql_types_per_column, names); - if (!exception.error_message.empty()) { - throw BinderException(exception.error_message); - } - for (idx_t i = 0; i < names.size(); i++) { - auto it = options.sql_types_per_column.find(names[i]); - if (it != options.sql_types_per_column.end()) { - return_types[i] = options.sql_type_list[it->second]; - } - } - } - bind_data.initial_reader.reset(); - } -} - -void CSVMultiFileInfo::FinalizeBindData(MultiFileBindData &multi_file_data) { - auto &csv_data = multi_file_data.bind_data->Cast(); - auto &names = multi_file_data.names; - auto &options = csv_data.options; - if (!options.force_not_null_names.empty()) { - // Let's first check all column names match - identifier_set_t column_names; - for (auto &name : names) { - column_names.insert(name); - } - for (auto &force_name : options.force_not_null_names) { - if (column_names.find(Identifier(force_name)) == column_names.end()) { - throw BinderException("\"force_not_null\" expected to find %s, but it was not found in the table", - force_name); - } - } - D_ASSERT(options.force_not_null.empty()); - for (idx_t i = 0; i < names.size(); i++) { - if (options.force_not_null_names.find(names[i].GetIdentifierName()) != options.force_not_null_names.end()) { - options.force_not_null.push_back(true); - } else { - options.force_not_null.push_back(false); - } - } - } - for (auto &type : multi_file_data.types) { - if (type.id() == LogicalTypeId::SQLNULL) { - // If after performing all the type detection of all files, - // we can't tell the type of column, we default to the highest type, a VARCHAR. - type = LogicalType::VARCHAR; - } - } - csv_data.Finalize(); -} - -optional_idx CSVMultiFileInfo::MaxThreads(const MultiFileBindData &bind_data, const MultiFileGlobalState &global_state, - FileExpandResult expand_result) { - auto &csv_data = bind_data.bind_data->Cast(); - if (!csv_data.buffer_manager) { - return optional_idx(); - } - if (expand_result == FileExpandResult::MULTIPLE_FILES) { - // always launch max threads if we are reading multiple files - return optional_idx(); - } - const idx_t bytes_per_thread = CSVIterator::BytesPerThread(csv_data.options); - const idx_t file_size = csv_data.buffer_manager->file_handle->FileSize(); - return file_size / bytes_per_thread + 1; -} - -bool CSVMultiFileInfo::SupportsReadAhead(const MultiFileBindData &bind_data) const { - auto &csv_data = bind_data.bind_data->Cast(); - return csv_data.buffer_manager && csv_data.buffer_manager->file_handle && - csv_data.buffer_manager->file_handle->HasKnownBufferRanges(); -} - -unique_ptr CSVMultiFileInfo::InitializeGlobalState(ClientContext &context, - MultiFileBindData &bind_data, - MultiFileGlobalState &global_state) { - auto &csv_data = bind_data.bind_data->Cast(); - - // Create the temporary rejects table - if (csv_data.options.store_rejects.GetValue()) { - CSVRejectsTable::GetOrCreate(context, csv_data.options.rejects_scan_name.GetValue(), - csv_data.options.rejects_table_name.GetValue()) - ->InitializeTable(context, csv_data); - } - return make_uniq(context, csv_data.options, bind_data.file_list->GetTotalFileCount(), bind_data); -} - -unique_ptr CSVMultiFileInfo::InitializeLocalState(ClientContext &, - GlobalTableFunctionState &) { - return make_uniq(); -} - -shared_ptr CSVMultiFileInfo::CreateReader(ClientContext &context, GlobalTableFunctionState &gstate_p, - BaseUnionData &union_data_p, - const MultiFileBindData &bind_data) { - auto &union_data = union_data_p.Cast(); - auto &gstate = gstate_p.Cast(); - auto &csv_data = bind_data.bind_data->Cast(); - // union readers - use cached options - auto &csv_names = union_data.names; - auto &csv_types = union_data.types; - auto options = union_data.options; - options.auto_detect = false; - D_ASSERT(csv_data.csv_schema.Empty()); - return make_shared_ptr(context, union_data.GetFileName(), std::move(options), bind_data.file_options, - StringsToIdentifiers(csv_names), csv_types, csv_data.csv_schema, - gstate.SingleThreadedRead(), nullptr, false); -} - -shared_ptr CSVMultiFileInfo::CreateReader(ClientContext &context, GlobalTableFunctionState &gstate_p, - const OpenFileInfo &file, idx_t file_idx, - const MultiFileBindData &bind_data) { - auto &gstate = gstate_p.Cast(); - auto &csv_data = bind_data.bind_data->Cast(); - - auto options = csv_data.options; - if (bind_data.file_list->GetExpandResult() == FileExpandResult::SINGLE_FILE) { - options.auto_detect = false; - } - - shared_ptr buffer_manager; - if (file_idx == 0) { - buffer_manager = csv_data.buffer_manager; - if (buffer_manager && buffer_manager->GetFilePath() != file.path) { - buffer_manager.reset(); - } - } - return make_shared_ptr(context, file, std::move(options), bind_data.file_options, bind_data.names, - bind_data.types, csv_data.csv_schema, gstate.SingleThreadedRead(), - std::move(buffer_manager), false); -} - -shared_ptr CSVMultiFileInfo::CreateReader(ClientContext &context, const OpenFileInfo &file, - BaseFileReaderOptions &options_p, - const MultiFileOptions &file_options) { - auto &csv_options = options_p.Cast(); - return make_shared_ptr(context, file, csv_options.options, file_options); -} - -shared_ptr CSVFileScan::GetUnionData(idx_t file_idx) { - auto data = make_shared_ptr(file); - data->names = GetNames(); - data->types = GetTypes(); - if (file_idx == 0) { - data->options = options; - data->reader = shared_from_this(); - } else { - data->options = std::move(options); - } - data->options.auto_detect = false; - return data; -} - -void CSVFileScan::PrepareReader(ClientContext &context, GlobalTableFunctionState &) { - InitializeFileNamesTypes(); - SetStart(); -} - -bool CSVFileScan::TryInitializeScan(ClientContext &context, GlobalTableFunctionState &gstate_p, - LocalTableFunctionState &lstate_p) { - auto &gstate = gstate_p.Cast(); - auto &lstate = lstate_p.Cast(); - auto csv_reader_ptr = shared_ptr_cast(shared_from_this()); - gstate.FinishScan(std::move(lstate.csv_reader)); - lstate.claim_state = CSVLocalState::ClaimState::IDLE; - return gstate.Next(csv_reader_ptr, lstate); -} - -// A task that loads the buffer on the async pool, sized for the read-ahead I/O budget -static unique_ptr BufferLoadTask(const shared_ptr &manager, const idx_t buffer_idx) { - const idx_t io_size = - manager->HasKnownBufferRanges() ? manager->KnownBufferSize(buffer_idx) : manager->GetBufferSize(); - return make_uniq([manager, buffer_idx] { manager->GetBuffer(buffer_idx); }, io_size); -} - -// Adds a load task when the buffer is not in memory -static void TryPushBufferLoadTask(const shared_ptr &manager, const idx_t buffer_idx, - vector> &io_tasks) { - shared_ptr buffer_handle; - if (manager->GetBufferResidency(buffer_idx, buffer_handle) == CSVBufferResidency::NEEDS_LOAD) { - io_tasks.push_back(BufferLoadTask(manager, buffer_idx)); - } -} - -// I/O tasks for the buffers of the claim's decode start that are not in memory -static vector> CollectClaimIOTasks(CSVLocalState &lstate) { - auto &manager = lstate.file_scan->buffer_manager; - const idx_t start_buffer_idx = lstate.iterator.GetBufferIdx(); - vector> io_tasks; - if (manager->HasKnownBufferRanges() && start_buffer_idx >= manager->KnownBufferCount()) { - // the claim starts past the last buffer (e.g. skipping the header consumed the whole file) - return io_tasks; - } - TryPushBufferLoadTask(manager, start_buffer_idx, io_tasks); - if (lstate.iterator.IsBoundarySet() && - (!manager->HasKnownBufferRanges() || - lstate.iterator.GetEndPos() >= manager->KnownBufferSize(start_buffer_idx))) { - // a boundary reaching the end of its buffer also touches the next one, for straddling values - // and first-line detection - TryPushBufferLoadTask(manager, start_buffer_idx + 1, io_tasks); - } - return io_tasks; -} - -AsyncResult CSVFileScan::ScheduleIO(ClientContext &context, GlobalTableFunctionState &gstate, - LocalTableFunctionState &lstate_p) { - auto &lstate = lstate_p.Cast(); - D_ASSERT(lstate.claim_state == CSVLocalState::ClaimState::PENDING); - return AsyncResult::FromTasks(CollectClaimIOTasks(lstate), TaskSchedulerType::ASYNC); -} - -AsyncResult CSVFileScan::Scan(ClientContext &context, GlobalTableFunctionState &global_state, - LocalTableFunctionState &local_state, DataChunk &chunk) { -#ifdef DUCKDB_DEBUG_ASYNC_SINK_SOURCE - { - AsyncResult test_result; - if (AsyncResult::TryGenerateTestResult(test_result)) { - return test_result; - } - } -#endif - auto &lstate = local_state.Cast(); - if (lstate.claim_state == CSVLocalState::ClaimState::PENDING) { - lstate.Materialize(); - } - auto &csv_reader = *lstate.csv_reader; - if (!csv_reader.IsSuspended() && csv_reader.FinishedIterator()) { - return AsyncResult(SourceResultType::FINISHED); - } - csv_reader.Flush(chunk); - if (csv_reader.IsSuspended()) { - // buffer is not in memory, we need to load it to the async pool - vector> io_tasks; - io_tasks.push_back(BufferLoadTask(csv_reader.buffer_manager, csv_reader.PendingBufferIdx())); - return AsyncResult(std::move(io_tasks), TaskSchedulerType::ASYNC); - } - return AsyncResult::FromChunk(chunk); -} - -void CSVFileScan::FinishFile(ClientContext &context, GlobalTableFunctionState &global_state) { - auto &gstate = global_state.Cast(); - gstate.FinishLaunchingTasks(*this); -} - -void CSVMultiFileInfo::FinishReading(ClientContext &context, GlobalTableFunctionState &global_state, - LocalTableFunctionState &local_state) { - auto &gstate = global_state.Cast(); - auto &lstate = local_state.Cast(); - gstate.FinishScan(std::move(lstate.csv_reader)); -} - -unique_ptr CSVMultiFileInfo::GetCardinality(const MultiFileBindData &bind_data, idx_t file_count) { - auto &csv_data = bind_data.bind_data->Cast(); - // determined through the scientific method as the average amount of rows in a CSV file - idx_t per_file_cardinality = 42; - if (csv_data.buffer_manager && csv_data.buffer_manager->file_handle) { - auto estimated_row_width = (bind_data.types.size() * 5); - per_file_cardinality = csv_data.buffer_manager->file_handle->FileSize() / estimated_row_width; - } - return make_uniq(file_count * per_file_cardinality); -} - -double CSVFileScan::GetProgressInFile(ClientContext &context) { - auto manager = buffer_manager; - if (!manager) { - // We are done with this file, so it's 100% - return 100.0; - } - double total_bytes_read; - if (manager->file_handle->compression_type == FileCompressionType::GZIP || - manager->file_handle->compression_type == FileCompressionType::ZSTD) { - // compressed file: we care about the progress made in the *underlying* file handle - // the bytes read from the uncompressed file are skewed - total_bytes_read = manager->file_handle->GetProgress(); - } else { - total_bytes_read = static_cast(bytes_read); - } - double file_progress = total_bytes_read / static_cast(file_size); - return file_progress * 100.0; -} - -FileGlobInput CSVMultiFileInfo::GetGlobInput() { - return FileGlobInput(FileGlobOptions::FALLBACK_GLOB, "csv"); -} - -} // namespace duckdb diff --git a/src/duckdb/src/execution/operator/csv_scanner/table_function/csv_schema_discovery.cpp b/src/duckdb/src/execution/operator/csv_scanner/table_function/csv_schema_discovery.cpp new file mode 100644 index 000000000..b370d62a9 --- /dev/null +++ b/src/duckdb/src/execution/operator/csv_scanner/table_function/csv_schema_discovery.cpp @@ -0,0 +1,115 @@ +#include "duckdb/execution/operator/csv_scanner/csv_schema_discovery.hpp" + +#include "duckdb/execution/operator/csv_scanner/csv_buffer.hpp" +#include "duckdb/execution/operator/csv_scanner/sniffer/csv_sniffer.hpp" + +namespace duckdb { + +//! Function to do schema discovery over one CSV file or a list/glob of CSV files +CSVSchema CSVSchemaDiscovery::SchemaDiscovery(ClientContext &context, shared_ptr &buffer_manager, + CSVReaderOptions &options, const MultiFileOptions &file_options, + vector &return_types, vector &names, + MultiFileList &multi_file_list, bool replace_null_with_varchar) { + vector schemas; + const auto option_og = options; + + const auto file_paths = multi_file_list.GetAllFiles(); + + // Here what we want to do is to sniff a given number of lines, if we have many files, we might go through them + // to reach the number of lines. + const idx_t required_number_of_lines = options.sniff_size * options.sample_size_chunks; + + idx_t total_number_of_rows = 0; + idx_t current_file = 0; + options.file_path = file_paths[current_file].path; + + buffer_manager = CSVBufferManager::Open(context, options, options.file_path, false); + idx_t only_header_or_empty_files = 0; + + { + CSVSniffer sniffer(options, file_options, buffer_manager, CSVStateMachineCache::Get(context)); + auto sniffer_result = sniffer.SniffCSV(); + idx_t rows_read = sniffer.LinesSniffed() - + (options.dialect_options.skip_rows.GetValue() + options.dialect_options.header.GetValue()); + + schemas.emplace_back(sniffer_result.names, sniffer_result.return_types, file_paths[0].path, rows_read, + buffer_manager->GetBuffer(0)->actual_size == 0); + total_number_of_rows += sniffer.LinesSniffed(); + current_file++; + if (sniffer.EmptyOrOnlyHeader()) { + only_header_or_empty_files++; + } + } + + // We do a copy of the options to not pollute the options of the first file. + idx_t max_files_to_sniff = static_cast(options.files_to_sniff == -1) + ? NumericLimits::Maximum() + : static_cast(options.files_to_sniff); + idx_t files_to_sniff = file_paths.size() > max_files_to_sniff ? max_files_to_sniff : file_paths.size(); + while (total_number_of_rows < required_number_of_lines && current_file < files_to_sniff) { + auto option_copy = option_og; + option_copy.file_path = file_paths[current_file].path; + auto file_buffer_manager = CSVBufferManager::Open(context, option_copy, option_copy.file_path, false); + // TODO: We could cache the sniffer to be reused during scanning. Currently that's an exercise left to the + // reader + CSVSniffer sniffer(option_copy, file_options, file_buffer_manager, CSVStateMachineCache::Get(context)); + auto sniffer_result = sniffer.SniffCSV(); + idx_t rows_read = sniffer.LinesSniffed() - (option_copy.dialect_options.skip_rows.GetValue() + + option_copy.dialect_options.header.GetValue()); + if (file_buffer_manager->GetBuffer(0)->actual_size == 0) { + schemas.emplace_back(true); + } else { + schemas.emplace_back(sniffer_result.names, sniffer_result.return_types, option_copy.file_path, rows_read); + } + total_number_of_rows += sniffer.LinesSniffed(); + if (sniffer.EmptyOrOnlyHeader()) { + only_header_or_empty_files++; + } + current_file++; + } + + // We might now have multiple schemas, we need to go through them to define the one true schema + CSVSchema best_schema; + for (auto &schema : schemas) { + if (best_schema.Empty()) { + // A schema is bettah than no schema + best_schema = schema; + } else if (best_schema.GetRowsRead() == 0) { + // If the best-schema has no data-rows, that's easy; we just take the new schema + best_schema = schema; + } else if (schema.GetRowsRead() != 0) { + // We might have conflicting-schemas, we must merge them + best_schema.MergeSchemas(schema, options.null_padding); + } + } + + // At this point, replace a sqlnull with varchar for the type + if (replace_null_with_varchar) { + best_schema.ReplaceNullWithVarchar(); + } + + if (names.empty()) { + names = StringsToIdentifiers(best_schema.GetNames()); + return_types = best_schema.GetTypes(); + } + if (names.empty() && return_types.empty()) { + throw InvalidInputException("No columns found in CSV files. Provide the columns option or ensure at least one " + "file contains a header or data row."); + } + if (replace_null_with_varchar && only_header_or_empty_files == current_file && !options.columns_set) { + for (idx_t i = 0; i < return_types.size(); i++) { + if (!options.sql_types_per_column.empty()) { + if (options.sql_types_per_column.find(names[i]) != options.sql_types_per_column.end()) { + continue; + } + } else if (i < options.sql_type_list.size()) { + continue; + } + // we default to varchar if all files are empty or only have a header after all the sniffing + return_types[i] = LogicalType::VARCHAR; + } + } + return best_schema; +} + +} // namespace duckdb diff --git a/src/duckdb/src/execution/operator/csv_scanner/table_function/global_csv_state.cpp b/src/duckdb/src/execution/operator/csv_scanner/table_function/global_csv_state.cpp index a1525659f..e037aa74a 100644 --- a/src/duckdb/src/execution/operator/csv_scanner/table_function/global_csv_state.cpp +++ b/src/duckdb/src/execution/operator/csv_scanner/table_function/global_csv_state.cpp @@ -7,13 +7,17 @@ #include "duckdb/execution/operator/persistent/csv_rejects_table.hpp" #include "duckdb/main/appender.hpp" #include "duckdb/main/client_data.hpp" -#include "duckdb/execution/operator/csv_scanner/csv_multi_file_info.hpp" +#include "duckdb/execution/operator/csv_scanner/csv_schema_discovery.hpp" +#include "duckdb/parallel/callback_async_task.hpp" namespace duckdb { -CSVGlobalState::CSVGlobalState(ClientContext &context_p, const CSVReaderOptions &options, idx_t total_file_count, - const MultiFileBindData &bind_data) - : context(context_p), bind_data(bind_data), sniffer_mismatch_error(options.sniffer_user_mismatch_error) { +CSVGlobalState::CSVGlobalState(ClientContext &context_p, ReadCSVData &csv_data_p, + const vector &column_names_p, idx_t total_file_count_p, + optional_ptr scan_op_p) + : context(context_p), csv_data(csv_data_p), scan_op(scan_op_p), total_file_count(total_file_count_p), + column_names(column_names_p), sniffer_mismatch_error(csv_data_p.options.sniffer_user_mismatch_error) { + auto &options = csv_data.options; // There are situations where we only support single threaded scanning auto system_threads = context.db->NumberOfThreads(); bool many_csv_files = total_file_count > 1 && total_file_count > system_threads * 2; @@ -22,6 +26,42 @@ CSVGlobalState::CSVGlobalState(ClientContext &context_p, const CSVReaderOptions initialized = false; } +// A task that loads the buffer on the async pool, sized for the read-ahead I/O budget +static unique_ptr BufferLoadTask(const shared_ptr &manager, const idx_t buffer_idx) { + const idx_t io_size = + manager->HasKnownBufferRanges() ? manager->KnownBufferSize(buffer_idx) : manager->GetBufferSize(); + return make_uniq([manager, buffer_idx] { manager->GetBuffer(buffer_idx); }, io_size); +} + +// Adds a load task when the buffer is not in memory +static void TryPushBufferLoadTask(const shared_ptr &manager, const idx_t buffer_idx, + vector> &io_tasks) { + shared_ptr buffer_handle; + if (manager->GetBufferResidency(buffer_idx, buffer_handle) == CSVBufferResidency::NEEDS_LOAD) { + io_tasks.push_back(BufferLoadTask(manager, buffer_idx)); + } +} + +//! I/O tasks for the buffers of the claim's decode start that are not in memory +vector> CSVCollectClaimIOTasks(CSVLocalState &lstate) { + auto &manager = lstate.file_scan->buffer_manager; + const idx_t start_buffer_idx = lstate.iterator.GetBufferIdx(); + vector> io_tasks; + if (manager->HasKnownBufferRanges() && start_buffer_idx >= manager->KnownBufferCount()) { + // the claim starts past the last buffer (e.g. skipping the header consumed the whole file) + return io_tasks; + } + TryPushBufferLoadTask(manager, start_buffer_idx, io_tasks); + if (lstate.iterator.IsBoundarySet() && + (!manager->HasKnownBufferRanges() || + lstate.iterator.GetEndPos() >= manager->KnownBufferSize(start_buffer_idx))) { + // a boundary reaching the end of its buffer also touches the next one, for straddling values + // and first-line detection + TryPushBufferLoadTask(manager, start_buffer_idx + 1, io_tasks); + } + return io_tasks; +} + void CSVGlobalState::FinishTask(CSVFileScan &scan) { auto started_tasks = scan.started_tasks.load(); auto finished_tasks = ++scan.finished_tasks; @@ -106,7 +146,6 @@ void CSVGlobalState::FinishFile(CSVFileScan &scan) { current_buffer_in_use.reset(); } scan.Finish(); - auto &csv_data = bind_data.bind_data->Cast(); const bool ignore_or_store_errors = csv_data.options.ignore_errors.GetValue() || csv_data.options.store_rejects.GetValue(); if (!single_threaded && !ignore_or_store_errors) { @@ -182,7 +221,6 @@ void FillScanErrorTable(InternalAppender &scan_appender, idx_t scan_idx, idx_t f } void CSVGlobalState::FillRejectsTable(CSVFileScan &scan) { - auto &csv_data = bind_data.bind_data->Cast(); auto &options = csv_data.options; if (!options.store_rejects.GetValue()) { @@ -198,15 +236,13 @@ void CSVGlobalState::FillRejectsTable(CSVFileScan &scan) { InternalAppender scans_appender(context, scans_table); idx_t scan_idx = context.transaction.GetActiveQuery(); - // get the file indexes for the rejects table - // we store these so that they are deterministic (i.e. file index 0 always gets the lowest rejects index) - // otherwise parallelism can result in out-of-order file indexes - auto file_idx = scan.GetFileIndex(); - for (idx_t i = rejects_file_indexes.size(); i <= file_idx; i++) { - rejects_file_indexes.push_back(rejects->GetCurrentFileIndex(scan_idx)); - } - const idx_t rejects_file_idx = rejects_file_indexes[file_idx]; - scan.error_handler->FillRejectsTable(errors_appender, rejects_file_idx, scan_idx, scan, *rejects, bind_data, limit); + // the files of a scan report under a block of indexes, so that the index of a file within its scan identifies + // it - that keeps the indexes deterministic when files are read in parallel, and keeps the files of one scan + // apart from those of another scan in the same query + const idx_t rejects_file_idx = + rejects->GetFileIndexBase(scan_idx, scan_op.get(), total_file_count) + scan.GetFileIndex(); + scan.error_handler->FillRejectsTable(errors_appender, rejects_file_idx, scan_idx, scan, *rejects, column_names, + limit); if (rejects->count != 0) { rejects->count = 0; FillScanErrorTable(scans_appender, scan_idx, rejects_file_idx, scan); diff --git a/src/duckdb/src/execution/operator/csv_scanner/table_function/read_single_csv_file.cpp b/src/duckdb/src/execution/operator/csv_scanner/table_function/read_single_csv_file.cpp new file mode 100644 index 000000000..907cb14a6 --- /dev/null +++ b/src/duckdb/src/execution/operator/csv_scanner/table_function/read_single_csv_file.cpp @@ -0,0 +1,509 @@ +#include "duckdb/common/multi_file/multi_file_list.hpp" +#include "duckdb/common/multi_file/table_function_multi_file.hpp" +#include "duckdb/execution/operator/csv_scanner/csv_error.hpp" +#include "duckdb/execution/operator/csv_scanner/csv_schema_discovery.hpp" +#include "duckdb/execution/operator/csv_scanner/global_csv_state.hpp" +#include "duckdb/execution/operator/csv_scanner/sniffer/csv_sniffer.hpp" +#include "duckdb/execution/operator/persistent/csv_rejects_table.hpp" +#include "duckdb/function/table/read_csv.hpp" + +namespace duckdb { + +//! Bind data of read_single_csv_file - the regular CSV read data plus the single file that is read +struct ReadSingleCSVFileData : public ReadCSVData { + OpenFileInfo file; + //! The names/types this file is read with + vector csv_names; + vector csv_types; +}; + +struct ReadSingleCSVFileGlobalState : public GlobalTableFunctionState { +public: + ReadSingleCSVFileGlobalState(ClientContext &context, ReadSingleCSVFileData &csv_data, + const TableFunctionInitInput &input) + : state(context, csv_data, csv_data.csv_names, TableFunctionFileInitInput::Get(input).file_count, input.op) { + } + +public: + idx_t MaxThreads() const override { + return max_threads; + } + +public: + //! The file that is read - declared before the state below, which holds buffers of this file and must therefore + //! be destroyed first + shared_ptr file_scan; + CSVGlobalState state; + //! Handing out the next part of the file is done single-threadedly + mutable mutex lock; + //! Whether we are done handing out parts of the file + bool finished_launching = false; + idx_t max_threads = 1; +}; + +struct ReadSingleCSVFileLocalState : public LocalTableFunctionState { + CSVLocalState state; + //! Whether our caller claims the batches we read - see table_function_claim_batch_t + bool claimed_externally = false; +}; + +//! The columns "force_not_null" was given, looked up the way column names are compared +static identifier_set_t GetForceNotNullNames(const CSVReaderOptions &options) { + identifier_set_t result; + for (auto &force_name : options.force_not_null_names) { + result.insert(Identifier(force_name)); + } + return result; +} + +//! "force_not_null" names columns of the scan, so it is verified against the schema of the scan rather than +//! against the columns of any single file of it +static void VerifyForceNotNull(const CSVReaderOptions &options, const vector &schema) { + if (options.force_not_null_names.empty()) { + return; + } + identifier_set_t column_names; + for (auto &name : schema) { + column_names.insert(name); + } + for (auto &force_name : GetForceNotNullNames(options)) { + if (column_names.find(force_name) == column_names.end()) { + throw BinderException("\"force_not_null\" expected to find %s, but it was not found in the table", + force_name.GetIdentifierName()); + } + } +} + +//! The equivalent of MultiFileReaderInterface::FinalizeBindData - mark the columns of this file that +//! "force_not_null" names. A file of a multi-file scan only has some of the columns of the scan +static void ApplyForceNotNull(CSVReaderOptions &options, const vector &names) { + if (options.force_not_null_names.empty()) { + return; + } + const auto force_not_null = GetForceNotNullNames(options); + options.force_not_null.clear(); + for (auto &name : names) { + options.force_not_null.push_back(force_not_null.find(name) != force_not_null.end()); + } +} + +//! Sniff this file - the dialect that is detected is stored in the options of the bind data. When a schema is given +//! the sniffed schema is reconciled with it, and files whose schema does not match it are reported +static void SniffCSVFile(ClientContext &context, ReadSingleCSVFileData &result, const CSVSchema &file_schema, + const MultiFileOptions &file_options, vector &return_types, + vector &names) { + auto &options = result.options; + result.buffer_manager = CSVBufferManager::Open(context, options, options.file_path, false); + auto &state_machine_cache = CSVStateMachineCache::Get(context); + if (file_schema.Empty()) { + // the columns of this file are fixed - only its dialect is sniffed + CSVSniffer sniffer(options, file_options, result.buffer_manager, state_machine_cache); + sniffer.SniffCSV(); + return; + } + if (result.buffer_manager->file_handle->FileSize() == 0) { + // an empty file has no dialect for us to reconcile with the schema of the scan + return; + } + CSVSniffer sniffer(options, file_options, result.buffer_manager, state_machine_cache, false); + auto sniff_result = sniffer.AdaptiveSniff(file_schema); + names = std::move(sniff_result.names); + return_types = std::move(sniff_result.return_types); +} + +static unique_ptr ReadSingleCSVFileBind(ClientContext &context, TableFunctionBindInput &input, + vector &return_types, vector &names) { + auto &file_input = TableFunctionFileBindInput::Get(input); + auto result = make_uniq(); + auto &options = result->options; + for (auto &kv : input.named_parameters) { + options.ParseOption(context, kv.first, kv.second); + } + if (input.inputs[0].IsNull()) { + throw BinderException("read_single_csv_file requires a non-NULL file name"); + } + result->file = TableFunctionFileBindInput::GetFile(input); + if (result->file.path.empty()) { + throw BinderException("read_single_csv_file requires a non-empty file name"); + } + + // the options of the scan this file is part of steer the sniffer - the file list is this single file + MultiFileOptions file_options; + if (file_input.multi_file_options) { + file_options = *file_input.multi_file_options; + } + SimpleMultiFileList file_list(vector {result->file}); + + //! Whether the options that name columns are verified against the columns of this file, which is the schema of + //! the scan only when this file is the whole scan + bool verify_against_file = false; + optional_ptr schema_source; + if (file_input.HasExpectedSchema()) { + if (file_input.expected_bind_data) { + // the schema of the scan was determined on (other) files of this scan - start from the options it was + // determined with, so that this file is read with the same dialect + schema_source = file_input.expected_bind_data->Cast(); + options = schema_source->options; + options.force_not_null.clear(); + names = *file_input.expected_names; + return_types = *file_input.expected_types; + } else { + // the columns are known but the dialect of this file is not - this is the case for COPY, which takes + // its columns from the target table + options.name_list = *file_input.expected_names; + options.sql_type_list = *file_input.expected_types; + options.columns_set = true; + options.sql_types_per_column.clear(); + for (idx_t i = 0; i < options.name_list.size(); i++) { + options.sql_types_per_column[options.name_list[i]] = i; + } + names = options.name_list; + return_types = options.sql_type_list; + } + } + options.file_path = result->file.path; + // when several files are read, options like "names" describe the scan and not this file - the sniffer is then + // lenient about a file whose columns do not line up with them exactly + options.multi_file_reader = file_input.multi_file_scan; + options.Verify(file_options); + + // when the schema of the scan is known upfront the options are verified against it before this file is + // sniffed, so that an option that does not match it is reported before any error in the file itself + if (file_input.HasExpectedSchema()) { + VerifyForceNotNull(options, *file_input.expected_names); + } else if (!file_input.multi_file_scan) { + // this file is the whole scan, so its own columns are the schema the options are verified against + verify_against_file = true; + } + if (schema_source) { + // the schema of this file must be reconcilable with the schema of the scan + result->csv_schema = schema_source->csv_schema; + if (options.auto_detect) { + SniffCSVFile(context, *result, result->csv_schema, file_options, return_types, names); + } + } else if (file_input.HasExpectedSchema()) { + if (options.auto_detect) { + SniffCSVFile(context, *result, CSVSchema(), file_options, return_types, names); + } + } else if (options.auto_detect || file_options.union_by_name) { + // a column without any value is kept as SQLNULL here, so the other files can still determine its type + result->csv_schema = CSVSchemaDiscovery::SchemaDiscovery(context, result->buffer_manager, options, file_options, + return_types, names, file_list, false); + } else { + if (!options.columns_set) { + throw BinderException("read_csv requires columns to be specified through the 'columns' option. Use " + "read_csv_auto or set read_csv(..., AUTO_DETECT=TRUE) to automatically guess " + "columns."); + } + names = options.name_list; + return_types = options.sql_type_list; + } + if (return_types.size() != names.size()) { + throw BinderException("read_csv: mismatch between the number of column names (%d) and column types (%d)", + names.size(), return_types.size()); + } + options.dialect_options.num_cols = names.size(); + + if (verify_against_file) { + VerifyForceNotNull(options, names); + } + ApplyForceNotNull(options, names); + if (!file_options.union_by_name) { + for (auto &type : return_types) { + if (type.id() == LogicalTypeId::SQLNULL) { + // if we cannot tell the type of a column we default to the highest type, a VARCHAR + type = LogicalType::VARCHAR; + } + } + } + result->Finalize(); + result->csv_names = names; + result->csv_types = return_types; + return std::move(result); +} + +//! Combine the schemas of several CSV files the way the multi-file sniffer does - the resulting CSV schema is +//! handed to the bind of every file, whose dialect is then sniffed and reconciled with it +static unique_ptr ReadSingleCSVFileCombineSchema(ClientContext &context, + TableFunctionCombineSchemaInput &input, + vector &return_types, + vector &names) { + if (input.union_by_name) { + // the columns of the files were unified by name - the options that name columns apply to the result of that + auto &options = input.bind_data[0].get().Cast().options; + VerifyForceNotNull(options, names); + if (!options.sql_types_per_column.empty()) { + const auto exception = CSVError::ColumnTypesError(options.sql_types_per_column, names); + if (!exception.error_message.empty()) { + throw BinderException(exception.error_message); + } + for (idx_t i = 0; i < names.size(); i++) { + auto entry = options.sql_types_per_column.find(names[i]); + if (entry != options.sql_types_per_column.end()) { + return_types[i] = options.sql_type_list[entry->second]; + } + } + } + for (auto &type : return_types) { + if (type.id() == LogicalTypeId::SQLNULL) { + // if we cannot tell the type of a column we default to the highest type, a VARCHAR + type = LogicalType::VARCHAR; + } + } + // the files have different columns - every file is read the way it was bound + return nullptr; + } + optional_ptr first_file; + CSVSchema best_schema; + for (auto &bind_data : input.bind_data) { + auto &csv_data = bind_data.get().Cast(); + if (csv_data.csv_schema.Empty()) { + // the schema of this file was not sniffed - fall back to combining the types + return nullptr; + } + auto schema = csv_data.csv_schema; + if (first_file && schema.IsEmptyFile()) { + // a file without any data contributes no columns to the schema of the scan + schema = CSVSchema(true); + } + if (!first_file) { + first_file = csv_data; + } + if (best_schema.Empty() || best_schema.GetRowsRead() == 0) { + // a schema is better than no schema, and any schema beats one without data rows + best_schema = schema; + } else if (schema.GetRowsRead() != 0) { + best_schema.MergeSchemas(schema, first_file->options.null_padding); + } + } + if (!first_file) { + return nullptr; + } + if (best_schema.Empty()) { + throw InvalidInputException("No columns found in CSV files. Provide the columns option or ensure at least one " + "file contains a header or data row."); + } + best_schema.ReplaceNullWithVarchar(); + names = StringsToIdentifiers(best_schema.GetNames()); + return_types = best_schema.GetTypes(); + VerifyForceNotNull(first_file->options, names); + + auto result = make_uniq(); + // the options that were sniffed on the first file are the starting point for every file of the scan. The + // columns are not set on them - they are carried by the CSV schema, which each file is reconciled with + result->options = first_file->options; + result->options.dialect_options.num_cols = names.size(); + // the buffer manager of the first file is kept, like the multi-file sniffer does - it tells the scan whether + // the files can be read ahead, and the first file does not need to be opened again + result->buffer_manager = first_file->buffer_manager; + result->csv_schema = best_schema; + result->csv_names = names; + result->csv_types = return_types; + result->Finalize(); + return std::move(result); +} + +static unique_ptr ReadSingleCSVFileInitGlobal(ClientContext &context, + TableFunctionInitInput &input) { + auto &file_input = TableFunctionFileInitInput::Get(input); + auto &csv_data = input.bind_data->CastNoConst(); + + // create the temporary rejects table + if (csv_data.options.store_rejects.GetValue()) { + CSVRejectsTable::GetOrCreate(context, csv_data.options.rejects_scan_name.GetValue(), + csv_data.options.rejects_table_name.GetValue()) + ->InitializeTable(context, csv_data); + } + + auto result = make_uniq(context, csv_data, input); + + // this file was sniffed during binding, so its dialect and columns are known + auto options = csv_data.options; + options.auto_detect = false; + MultiFileOptions file_options; + CSVSchema no_schema; + result->file_scan = make_shared_ptr( + context, csv_data.file, std::move(options), file_options, csv_data.csv_names, csv_data.csv_types, no_schema, + result->state.SingleThreadedRead(), csv_data.buffer_manager, false); + + // perform projection pushdown - the scanner emits the columns in the order they are requested + auto &file_scan = *result->file_scan; + for (auto &column_index : input.column_indexes) { + const auto col_id = column_index.GetPrimaryIndex(); + if (IsVirtualColumn(col_id)) { + continue; + } + file_scan.column_ids.push_back(MultiFileLocalColumnId(col_id)); + } + // the index of this file in the scan it is part of - it identifies the file in the rejects tables + file_scan.file_list_idx = file_input.file_index.IsValid() ? file_input.file_index.GetIndex() : 0; + if (file_input.cast_map) { + // our caller needs some columns as a different type than this file has them - the scanner converts to those + // types while parsing, so that "ignore_errors" applies to the conversions that fail + for (auto &entry : *file_input.cast_map) { + file_scan.cast_map[entry.first] = entry.second; + } + } + file_scan.InitializeFileNamesTypes(); + file_scan.SetStart(); + + if (!result->state.SingleThreadedRead()) { + const idx_t bytes_per_thread = CSVIterator::BytesPerThread(csv_data.options); + result->max_threads = file_scan.file_size / bytes_per_thread + 1; + } + return std::move(result); +} + +static unique_ptr ReadSingleCSVFileInitLocal(ExecutionContext &context, + TableFunctionInitInput &input, + GlobalTableFunctionState *global_state) { + return make_uniq(); +} + +//! Assign the next part of the file to this thread +static bool ClaimNextPart(ReadSingleCSVFileGlobalState &gstate, ReadSingleCSVFileLocalState &lstate) { + lock_guard guard(gstate.lock); + gstate.state.FinishScan(std::move(lstate.state.csv_reader)); + lstate.state.claim_state = CSVLocalState::ClaimState::IDLE; + if (gstate.finished_launching) { + return false; + } + if (gstate.state.Next(gstate.file_scan, lstate.state)) { + return true; + } + // we have handed out the entire file - this is also where the errors of the file are reported + gstate.finished_launching = true; + gstate.state.FinishLaunchingTasks(*gstate.file_scan); + return false; +} + +static bool ReadSingleCSVFileClaimBatch(ClientContext &context, TableFunctionInput &input) { + auto &gstate = input.global_state->Cast(); + auto &lstate = input.local_state->Cast(); + // our caller hands out the parts of the file, so we must not claim the next one ourselves + lstate.claimed_externally = true; + return ClaimNextPart(gstate, lstate); +} + +//! The CSV scanner can be read ahead when the buffers of the file can be addressed individually +static bool ReadSingleCSVFileSupportsReadAhead(optional_ptr bind_data) { + if (!bind_data) { + return false; + } + auto &csv_data = bind_data->Cast(); + return csv_data.buffer_manager && csv_data.buffer_manager->file_handle && + csv_data.buffer_manager->file_handle->HasKnownBufferRanges(); +} + +//! Load the buffers of the claimed part of the file that are not in memory yet +static AsyncResult ReadSingleCSVFileScheduleIO(ClientContext &context, TableFunctionInput &input) { + auto &lstate = input.local_state->Cast(); + if (lstate.state.claim_state != CSVLocalState::ClaimState::PENDING) { + return SourceResultType::HAVE_MORE_OUTPUT; + } + return AsyncResult::FromTasks(CSVCollectClaimIOTasks(lstate.state), TaskSchedulerType::ASYNC); +} + +//! Release the part of the file this thread was reading +static void ReadSingleCSVFileFinishBatch(ClientContext &context, TableFunctionInput &input) { + auto &gstate = input.global_state->Cast(); + auto &lstate = input.local_state->Cast(); + lock_guard guard(gstate.lock); + gstate.state.FinishScan(std::move(lstate.state.csv_reader)); +} + +static void ReadSingleCSVFileFunction(ClientContext &context, TableFunctionInput &input, DataChunk &output) { + auto &gstate = input.global_state->Cast(); + auto &lstate = input.local_state->Cast(); + + while (true) { + if (lstate.state.claim_state == CSVLocalState::ClaimState::IDLE) { + if (lstate.claimed_externally) { + // the next part of the file is claimed by our caller + return; + } + if (!ClaimNextPart(gstate, lstate)) { + // there is nothing left for us to read in this file + return; + } + } + if (lstate.state.claim_state == CSVLocalState::ClaimState::PENDING) { + lstate.state.Materialize(); + } + auto &csv_reader = *lstate.state.csv_reader; + if (csv_reader.IsSuspended() || !csv_reader.FinishedIterator()) { + csv_reader.Flush(output); + if (csv_reader.IsSuspended()) { + // the scanner needs a buffer that is not in memory - load it and resume + csv_reader.buffer_manager->GetBuffer(csv_reader.PendingBufferIdx()); + continue; + } + if (output.size() != 0) { + return; + } + } + // this part of the file is done - grab the next one + lock_guard guard(gstate.lock); + gstate.state.FinishScan(std::move(lstate.state.csv_reader)); + lstate.state.claim_state = CSVLocalState::ClaimState::IDLE; + } +} + +static double ReadSingleCSVFileProgress(ClientContext &context, const FunctionData *bind_data, + const GlobalTableFunctionState *global_state) { + if (!global_state) { + return 0; + } + auto &gstate = global_state->Cast(); + // the buffers of the file are released when the last part of it is handed out - hold the lock so we do not read + // them while that happens + lock_guard guard(gstate.lock); + if (!gstate.file_scan) { + return 0; + } + return gstate.file_scan->GetProgressInFile(context); +} + +static unique_ptr ReadSingleCSVFileCardinality(ClientContext &context, const FunctionData *bind_data) { + auto &csv_data = bind_data->Cast(); + // determined through the scientific method as the average amount of rows in a CSV file + idx_t per_file_cardinality = 42; + if (csv_data.buffer_manager && csv_data.buffer_manager->file_handle) { + auto estimated_row_width = csv_data.csv_types.size() * 5; + per_file_cardinality = csv_data.buffer_manager->file_handle->FileSize() / estimated_row_width; + } + return make_uniq(per_file_cardinality); +} + +TableFunction ReadCSVTableFunction::GetSingleFileFunction() { + TableFunction read_csv("read_single_csv_file", FunctionSignature().AddPositionalOnly("file", LogicalType::ANY), + ReadSingleCSVFileFunction, ReadSingleCSVFileBind, ReadSingleCSVFileInitGlobal, + ReadSingleCSVFileInitLocal); + read_csv.table_scan_progress = ReadSingleCSVFileProgress; + read_csv.cardinality = ReadSingleCSVFileCardinality; + read_csv.projection_pushdown = true; + ReadCSVAddNamedParameters(read_csv); + return read_csv; +} + +TableFunction ReadCSVTableFunction::GetMultiFileFunction(Identifier name) { + // the multi-file CSV reader is the single-file CSV reader wrapped into a multi-file function + TableFunctionMultiFileSettings settings; + settings.glob_input = FileGlobInput(FileGlobOptions::FALLBACK_GLOB, "csv"); + settings.reader_type = "CSV"; + // like read_csv, the schema is determined by combining the schemas of up to "files_to_sniff" files + settings.maximum_sample_files = 10; + settings.sample_files_parameter = "files_to_sniff"; + // the schemas of the sampled files are reconciled with one another - every file must have every column + settings.sampled_schema_is_union = false; + settings.combine_schema = ReadSingleCSVFileCombineSchema; + settings.claim_batch = ReadSingleCSVFileClaimBatch; + settings.finish_batch = ReadSingleCSVFileFinishBatch; + settings.supports_read_ahead = ReadSingleCSVFileSupportsReadAhead; + settings.schedule_io = ReadSingleCSVFileScheduleIO; + // the scanner converts to the types the scan asks for while parsing, rather than casting its output + settings.supports_cast_map = true; + return TableFunctionMultiFileWrapper::CreateFunction(GetSingleFileFunction(), std::move(name), std::move(settings)); +} + +} // namespace duckdb diff --git a/src/duckdb/src/execution/operator/csv_scanner/util/csv_error.cpp b/src/duckdb/src/execution/operator/csv_scanner/util/csv_error.cpp index 429621a83..aebc04f31 100644 --- a/src/duckdb/src/execution/operator/csv_scanner/util/csv_error.cpp +++ b/src/duckdb/src/execution/operator/csv_scanner/util/csv_error.cpp @@ -18,7 +18,8 @@ LinesPerBoundary::LinesPerBoundary(idx_t boundary_idx_p, idx_t lines_in_batch_p) : boundary_idx(boundary_idx_p), lines_in_batch(lines_in_batch_p) { } -CSVErrorHandler::CSVErrorHandler(bool ignore_errors_p) : ignore_errors(ignore_errors_p) { +CSVErrorHandler::CSVErrorHandler(bool ignore_errors_p, bool store_ignored_errors_p) + : ignore_errors(ignore_errors_p), store_ignored_errors(store_ignored_errors_p) { } void CSVErrorHandler::ThrowError(const CSVError &csv_error) { @@ -62,8 +63,15 @@ void CSVErrorHandler::ThrowError(const CSVError &csv_error) { void CSVErrorHandler::Error(const CSVError &csv_error, bool force_error) { lock_guard parallel_lock(main_mutex); - if (!force_error && (ignore_errors || (PrintLineNumber(csv_error) && !CanGetLine(csv_error.GetBoundaryIndex())))) { - // We store this error, we can't throw it now, or we are ignoring it + if (!force_error && ignore_errors) { + // We are ignoring this error - it is only kept if something (e.g. the rejects table) will read it + if (store_ignored_errors) { + errors.push_back(csv_error); + } + return; + } + if (!force_error && PrintLineNumber(csv_error) && !CanGetLine(csv_error.GetBoundaryIndex())) { + // We store this error, we can't throw it now errors.push_back(csv_error); return; } @@ -186,7 +194,7 @@ string CSVErrorTypeToEnum(CSVErrorType type) { void CSVErrorHandler::FillRejectsTable(InternalAppender &errors_appender, const idx_t file_idx, const idx_t scan_idx, const CSVFileScan &file, CSVRejectsTable &rejects, - const MultiFileBindData &bind_data, const idx_t limit) { + const vector &column_names, const idx_t limit) { lock_guard parallel_lock(main_mutex); // We first insert the file into the file scans table for (auto &error : file.error_handler->errors) { @@ -232,15 +240,15 @@ void CSVErrorHandler::FillRejectsTable(InternalAppender &errors_appender, const errors_appender.Append(Value()); break; case CSVErrorType::TOO_FEW_COLUMNS: - if (col_idx + 1 < bind_data.names.size()) { - errors_appender.Append(string_t(bind_data.names[col_idx + 1].GetIdentifierName())); + if (col_idx + 1 < column_names.size()) { + errors_appender.Append(string_t(column_names[col_idx + 1].GetIdentifierName())); } else { errors_appender.Append(Value()); } break; default: - if (col_idx < bind_data.names.size()) { - errors_appender.Append(string_t(bind_data.names[col_idx].GetIdentifierName())); + if (col_idx < column_names.size()) { + errors_appender.Append(string_t(column_names[col_idx].GetIdentifierName())); } else { errors_appender.Append(Value()); } diff --git a/src/duckdb/src/execution/operator/csv_scanner/util/csv_reader_options.cpp b/src/duckdb/src/execution/operator/csv_scanner/util/csv_reader_options.cpp index 8a0e8a1c4..160f15832 100644 --- a/src/duckdb/src/execution/operator/csv_scanner/util/csv_reader_options.cpp +++ b/src/duckdb/src/execution/operator/csv_scanner/util/csv_reader_options.cpp @@ -3,6 +3,7 @@ #include "duckdb/common/vector_size.hpp" #include "duckdb/common/string_util.hpp" #include "duckdb/common/enum_util.hpp" +#include "duckdb/common/operator/multiply.hpp" #include "duckdb/common/multi_file/multi_file_reader.hpp" #include "duckdb/common/set.hpp" #include "duckdb/parser/keyword_helper.hpp" @@ -294,7 +295,11 @@ void CSVReaderOptions::SetReadOption(const Identifier &loption, const Value &val } else if (loption == "ignore_errors") { ignore_errors.Set(ParseBoolean(value, loption)); } else if (loption == "buffer_size") { - buffer_size_option.Set(NumericCast(ParseInteger(value, loption))); + auto buffer_size = ParseInteger(value, loption); + if (buffer_size < 0) { + throw InvalidInputException("Buffer Size option must be higher than 0"); + } + buffer_size_option.Set(NumericCast(buffer_size)); if (buffer_size_option == 0) { throw InvalidInputException("Buffer Size option must be higher than 0"); } @@ -621,7 +626,13 @@ void CSVReaderOptions::Verify(MultiFileOptions &file_options) { } else if (maximum_line_size.IsSetByUser() && maximum_line_size.GetValue() > max_line_size_default) { // If the max line size is set by the user and bigger than we have by default, we make it part of our buffer // size decision. - buffer_size_option.Set(CSVBuffer::ROWS_PER_BUFFER * maximum_line_size.GetValue(), false); + idx_t buffer_size; + if (!TryMultiplyOperator::Operation(CSVBuffer::ROWS_PER_BUFFER, + maximum_line_size.GetValue(), buffer_size)) { + throw BinderException("MAX_LINE_SIZE option was set to %d, which is too large", + maximum_line_size.GetValue()); + } + buffer_size_option.Set(buffer_size, false); } } @@ -643,7 +654,7 @@ string CSVReaderOptions::GetUserDefinedParameters() const { return result; } -void CSVReaderOptions::FromNamedParameters(const named_parameter_map_t &in, ClientContext &context, +void CSVReaderOptions::FromNamedParameters(const named_argument_map_t &in, ClientContext &context, MultiFileOptions &file_options) { for (auto &kv : in) { if (MultiFileReader().ParseOption(kv.first, kv.second, file_options, context)) { @@ -669,6 +680,9 @@ void CSVReaderOptions::ParseOption(ClientContext &context, const Identifier &key if (child_type.id() != LogicalTypeId::STRUCT) { throw BinderException("read_csv columns requires a struct as input"); } + if (val.IsNull()) { + throw BinderException("read_csv columns cannot be NULL"); + } auto &struct_children = StructValue::GetChildren(val); D_ASSERT(StructType::GetChildCount(child_type) == struct_children.size()); @@ -683,6 +697,9 @@ void CSVReaderOptions::ParseOption(ClientContext &context, const Identifier &key if (val.type().id() != LogicalTypeId::VARCHAR) { throw BinderException("read_csv requires a type specification as string"); } + if (val.IsNull()) { + throw BinderException("read_csv columns cannot have a NULL type"); + } parsed_types_per_column[name] = i; parsed_types.emplace_back(TransformStringToLogicalType(StringValue::Get(val), context)); } @@ -708,6 +725,9 @@ void CSVReaderOptions::ParseOption(ClientContext &context, const Identifier &key if (child_type.id() != LogicalTypeId::LIST) { throw BinderException("read_csv auto_types requires a list as input"); } + if (val.IsNull()) { + throw BinderException("auto_type_candidates cannot be NULL"); + } auto &list_children = ListValue::GetChildren(val); if (list_children.empty()) { throw BinderException("auto_type_candidates requires at least one type"); @@ -716,6 +736,9 @@ void CSVReaderOptions::ParseOption(ClientContext &context, const Identifier &key if (child.type().id() != LogicalTypeId::VARCHAR) { throw BinderException("auto_type_candidates requires a type specification as string"); } + if (child.IsNull()) { + throw BinderException("auto_type_candidates cannot contain NULL types"); + } auto candidate_type = TransformStringToLogicalType(StringValue::Get(child), context); candidate_types[GetCandidateSpecificity(candidate_type)] = candidate_type; } @@ -761,6 +784,9 @@ void CSVReaderOptions::ParseOption(ClientContext &context, const Identifier &key if (!columns_set && !sql_type_list.empty()) { throw BinderException("read_csv column_types/types/dtypes can only be supplied once"); } + if (val.IsNull()) { + throw BinderException("read_csv %s cannot be NULL", key); + } // Parse into temporary lists first vector sql_type_names; @@ -774,6 +800,9 @@ void CSVReaderOptions::ParseOption(ClientContext &context, const Identifier &key if (val.type().id() != LogicalTypeId::VARCHAR) { throw BinderException("read_csv %s requires a type specification as string", key); } + if (val.IsNull()) { + throw BinderException("read_csv %s cannot have a NULL type", key); + } sql_type_names.push_back(StringValue::Get(val)); parsed_types_per_column[name] = i; } @@ -784,6 +813,9 @@ void CSVReaderOptions::ParseOption(ClientContext &context, const Identifier &key } auto &children = ListValue::GetChildren(val); for (auto &child : children) { + if (child.IsNull()) { + throw BinderException("read_csv %s cannot have a NULL type", key); + } sql_type_names.push_back(StringValue::Get(child)); } } @@ -851,7 +883,7 @@ void CSVReaderOptions::ToNamedParameters(named_parameter_map_t &named_params) co } named_params["max_line_size"] = Value::BIGINT(NumericCast(maximum_line_size.GetValue())); if (dialect_options.skip_rows.IsSetByUser()) { - named_params["skip"] = Value::UBIGINT(GetSkipRows()); + named_params["skip"] = Value::BIGINT(NumericCast(GetSkipRows())); } named_params["null_padding"] = Value::BOOLEAN(null_padding); named_params["parallel"] = Value::BOOLEAN(parallel); diff --git a/src/duckdb/src/execution/operator/csv_scanner/util/csv_validator.cpp b/src/duckdb/src/execution/operator/csv_scanner/util/csv_validator.cpp index 09c2ea4c2..f2f27d572 100644 --- a/src/duckdb/src/execution/operator/csv_scanner/util/csv_validator.cpp +++ b/src/duckdb/src/execution/operator/csv_scanner/util/csv_validator.cpp @@ -30,7 +30,7 @@ void ThreadLines::Verify() const { continue; } if (last_end_pos + error_margin < line_info.second.start_pos || - line_info.second.start_pos < last_end_pos - error_margin) { + line_info.second.start_pos + error_margin < last_end_pos) { std::ostringstream error; error << "The Parallel CSV Reader currently does not support a full read on this file." << '\n'; error << "To correctly parse this file, please run with the single threaded error (i.e., parallel = " diff --git a/src/duckdb/src/execution/operator/filter/physical_filter.cpp b/src/duckdb/src/execution/operator/filter/physical_filter.cpp index 0a9ee17ac..b93c44fbc 100644 --- a/src/duckdb/src/execution/operator/filter/physical_filter.cpp +++ b/src/duckdb/src/execution/operator/filter/physical_filter.cpp @@ -6,19 +6,8 @@ namespace duckdb { PhysicalFilter::PhysicalFilter(PhysicalPlan &physical_plan, vector types, vector> select_list, idx_t estimated_cardinality) - : CachingPhysicalOperator(physical_plan, PhysicalOperatorType::FILTER, std::move(types), estimated_cardinality) { - D_ASSERT(!select_list.empty()); - if (select_list.size() == 1) { - expression = std::move(select_list[0]); - return; - } - - // Create a conjunction from the select list. - auto conjunction = make_uniq(ExpressionType::CONJUNCTION_AND); - for (auto &expr : select_list) { - conjunction->GetChildrenMutable().push_back(std::move(expr)); - } - expression = std::move(conjunction); + : CachingPhysicalOperator(physical_plan, PhysicalOperatorType::FILTER, std::move(types), estimated_cardinality), + expression(BoundConjunctionExpression::Create(ExpressionType::CONJUNCTION_AND, std::move(select_list))) { } class FilterState : public CachingOperatorState { diff --git a/src/duckdb/src/execution/operator/helper/launch_external_resource.cpp b/src/duckdb/src/execution/operator/helper/launch_external_resource.cpp index b483d26f0..efd478b1d 100644 --- a/src/duckdb/src/execution/operator/helper/launch_external_resource.cpp +++ b/src/duckdb/src/execution/operator/helper/launch_external_resource.cpp @@ -1,10 +1,12 @@ #include "duckdb/execution/operator/helper/launch_external_resource.hpp" #include "duckdb/common/exception.hpp" +#include "duckdb/common/string_util.hpp" #include "duckdb/main/client_context.hpp" #include "duckdb/main/connection.hpp" #include "duckdb/main/database.hpp" #include "duckdb/main/external_resources_manager.hpp" +#include "duckdb/main/attached_database.hpp" #include "duckdb/parser/parsed_data/attach_info.hpp" namespace duckdb { @@ -30,7 +32,7 @@ LaunchedResource ProvisionExternalResource(ClientContext &client, const string & Connection con(DatabaseInstance::GetDatabase(client)); // resource_name is forwarded only for observability (it labels the recipe-call log entries). auto name_arg = resource_name.empty() ? string() : ", resource_name := " + Value(resource_name).ToSQLString(); - // A handle triggers the adopt path (skip create, resolve the given handle). + // A handle skips create and resolves the given one instead (REGISTER). auto handle_arg = adopt_handle.IsNull() ? string() : ", handle := " + adopt_handle.ToSQLString(); auto sql = "SELECT uri, attached_db_type, result, deleter_function, deleter_payload " "FROM create_external_resource(" + @@ -45,15 +47,16 @@ LaunchedResource ProvisionExternalResource(ClientContext &client, const string & LaunchedResource out; // Read the deleter binding first: the resource now exists but nothing owns it yet, so if it turns out // to be unusable below, this is the only chance to tear it down rather than strand it. - auto del_fn = res->GetValue(3, 0); + auto rows = res->Collection().GetRows(); + auto del_fn = rows.GetValue(3, 0); out.deleter_function = del_fn.IsNull() ? string() : StringValue::Get(del_fn); - out.deleter_payload = res->GetValue(4, 0); + out.deleter_payload = rows.GetValue(4, 0); - auto uri_val = res->GetValue(0, 0); + auto uri_val = rows.GetValue(0, 0); if (uri_val.IsNull()) { // A resource without an endpoint is legal (it just cannot be attached), but one provisioned // solely in order to attach to it is useless now, so reap it rather than strand it. Only ever - // reap what we created: an adopted handle names a resource the caller merely borrows, and + // reap what we created: a registered handle names a resource the caller merely borrows, and // destroying it would tear down something someone else owns. Best-effort, so a teardown failure // is logged rather than masking the error below. if (adopt_handle.IsNull()) { @@ -64,16 +67,31 @@ LaunchedResource ProvisionExternalResource(ClientContext &client, const string & throw IOException("EXTERNAL RESOURCE: provisioning '%s' returned a NULL uri", provider); } out.uri = uri_val.ToString(); - auto type_val = res->GetValue(1, 0); + auto type_val = rows.GetValue(1, 0); out.attached_db_type = type_val.IsNull() ? string() : type_val.ToString(); - out.result = res->GetValue(2, 0); + out.result = rows.GetValue(2, 0); return out; } +//! The statement's options are already in place, so a key the resource also supplies would silently +//! overrule what the user wrote. The resource dictates how it is reached, so refuse instead. Compared +//! by setting, not by key: READWRITE and READ_ONLY are one option under two names. +static void RejectOptionCollision(const AttachInfo &info, const string &key) { + auto setting = AttachOptions::OptionSetting(key); + for (auto &entry : info.options) { + if (AttachOptions::OptionSetting(entry.first) == setting) { + throw InvalidInputException("EXTERNAL RESOURCE: the resource supplies \"%s\", so it cannot also be given " + "as an option on the statement", + key); + } + } +} + void ApplyLaunchedResource(const LaunchedResource &launched, AttachInfo &info) { info.path = launched.uri; // A NULL/empty db type falls back to the extension prefix extracted from the uri. if (!launched.attached_db_type.empty()) { + RejectOptionCollision(info, "type"); info.options["type"] = Value(launched.attached_db_type); } // The remaining connect options (e.g. token) flow through as attach options. @@ -84,6 +102,14 @@ void ApplyLaunchedResource(const LaunchedResource &launched, AttachInfo &info) { if (key == "uri" || key == "attached_db_type") { continue; } + RejectOptionCollision(info, key); + // No option takes NULL: consumers cast and unwrap without checking, so it reads as a value + // (read_only would come out true). A recipe meaning "no value" omits the key. + if (kv[1].IsNull()) { + throw InvalidInputException("EXTERNAL RESOURCE: NULL is not supported as a valid option for \"%s\"; " + "omit the key rather than returning NULL", + key); + } info.options[key] = kv[1]; } } diff --git a/src/duckdb/src/execution/operator/helper/physical_batch_collector.cpp b/src/duckdb/src/execution/operator/helper/physical_batch_collector.cpp deleted file mode 100644 index e79a4d044..000000000 --- a/src/duckdb/src/execution/operator/helper/physical_batch_collector.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "duckdb/execution/operator/helper/physical_batch_collector.hpp" - -#include "duckdb/common/types/batched_data_collection.hpp" -#include "duckdb/main/client_context.hpp" -#include "duckdb/main/materialized_query_result.hpp" - -namespace duckdb { - -PhysicalBatchCollector::PhysicalBatchCollector(PhysicalPlan &physical_plan, PreparedStatementData &data) - : PhysicalResultCollector(physical_plan, data) { -} - -SinkResultType PhysicalBatchCollector::Sink(ExecutionContext &context, DataChunk &chunk, - OperatorSinkInput &input) const { - auto &state = input.local_state.Cast(); - state.data.Append(chunk, state.partition_info.batch_index.GetIndex()); - return SinkResultType::NEED_MORE_INPUT; -} - -SinkCombineResultType PhysicalBatchCollector::Combine(ExecutionContext &context, - OperatorSinkCombineInput &input) const { - auto &gstate = input.global_state.Cast(); - auto &state = input.local_state.Cast(); - - lock_guard lock(gstate.glock); - gstate.data.Merge(state.data); - - return SinkCombineResultType::FINISHED; -} - -SinkFinalizeType PhysicalBatchCollector::Finalize(Pipeline &pipeline, Event &event, ClientContext &context, - OperatorSinkFinalizeInput &input) const { - auto &gstate = input.global_state.Cast(); - auto collection = gstate.data.FetchCollection(); - D_ASSERT(collection); - auto result = make_uniq(statement_type, properties, names, std::move(collection), - context.GetClientProperties()); - gstate.result = std::move(result); - return SinkFinalizeType::READY; -} - -unique_ptr PhysicalBatchCollector::GetLocalSinkState(ExecutionContext &context) const { - return make_uniq(context.client, *this); -} - -unique_ptr PhysicalBatchCollector::GetGlobalSinkState(ClientContext &context) const { - return make_uniq(context, *this); -} - -unique_ptr PhysicalBatchCollector::GetResult(GlobalSinkState &state) const { - auto &gstate = state.Cast(); - D_ASSERT(gstate.result); - return std::move(gstate.result); -} - -} // namespace duckdb diff --git a/src/duckdb/src/execution/operator/helper/physical_connect.cpp b/src/duckdb/src/execution/operator/helper/physical_connect.cpp index 7a55de083..3744495d3 100644 --- a/src/duckdb/src/execution/operator/helper/physical_connect.cpp +++ b/src/duckdb/src/execution/operator/helper/physical_connect.cpp @@ -1,11 +1,14 @@ #include "duckdb/execution/operator/helper/physical_connect.hpp" #include "duckdb/catalog/catalog.hpp" #include "duckdb/common/exception.hpp" -#include "duckdb/common/types/uuid.hpp" +#include "duckdb/common/identifier.hpp" +#include "duckdb/execution/operator/helper/launch_external_resource.hpp" #include "duckdb/main/attached_database.hpp" #include "duckdb/main/client_context.hpp" #include "duckdb/main/config.hpp" +#include "duckdb/main/database.hpp" #include "duckdb/main/database_manager.hpp" +#include "duckdb/main/external_resources_manager.hpp" #include "duckdb/main/database_path_and_type.hpp" #include "duckdb/parser/parsed_data/attach_info.hpp" #include "duckdb/storage/storage_extension.hpp" @@ -19,28 +22,103 @@ SourceResultType PhysicalConnect::GetDataInternal(ExecutionContext &context, Dat if (info->target_is_local) { throw NotImplementedException("CONNECT LOCAL is not yet implemented"); } - if (info->name.empty()) { - throw NotImplementedException("CONNECT with no target is not yet implemented"); - } auto &client = context.client; + auto &db_manager = DatabaseManager::Get(client); // At most one active connection; only DISCONNECT clears it (even if the target was detached // elsewhere, so the user explicitly acknowledges the broken connection). - if (client.IsConnected()) { - auto current = client.TryGetConnectedCatalog(); - throw InvalidInputException("Already connected to \"%s\"; DISCONNECT first before issuing another CONNECT", - current ? current->GetName().GetIdentifierName() : ""); + auto ensure_not_connected = [&]() { + if (client.IsConnected()) { + auto current = client.TryGetConnectedCatalog(); + throw InvalidInputException("Already connected to \"%s\"; DISCONNECT first before issuing another CONNECT", + current ? current->GetName().GetIdentifierName() : ""); + } + }; + + // `CONNECT TO [NEW TEMPORARY] EXTERNAL RESOURCE`: get the endpoint, then ephemeral-attach it + // under a hidden alias and connect to it. The deleter is bound so the DISCONNECT reap tears it down. + if (info->external_resource) { + ensure_not_connected(); + auto &external_resource = *info->external_resource; + // A string type PROVISIONS a resource this connection OWNS (deleter bound, DISCONNECT reaps it); + // a bare identifier REFERENCES a registered resource it only BORROWS (no deleter). + LaunchedResource launched; + string resource_type, borrowed_resource_name; + bool owns_resource = false; + if (!external_resource.reference_name.empty()) { + auto instance = ExternalResourcesManager::Get(client).Lookup(external_resource.reference_name); + if (!instance) { + throw InvalidInputException("no external resource named \"%s\" is registered", + external_resource.reference_name); + } + launched = ProvisionExternalResource(client, instance->type, {}, instance->name, instance->handle); + // Record the borrow so DESTROY refuses while this connection is still using the resource. + borrowed_resource_name = instance->name; + } else { + resource_type = external_resource.provider; + // CONNECT has no alias to borrow a label from, so the resource stays unnamed in the logs. + launched = ProvisionExternalResource(client, external_resource.provider, external_resource.params); + owns_resource = true; + } + AttachInfo attach_info; + attach_info.name = GenerateInternalName("__connect_"); + // Applying the resource, parsing its options and the attach can all throw, and by now the + // resource exists with nothing owning it. One guard covers the lot; a narrower one strands it. + auto reap_if_owned = [&]() { + if (owns_resource) { + ResourceDeleter(DatabaseInstance::GetDatabase(client), launched.deleter_function, + launched.deleter_payload, resource_type, string()) + .TryDelete(); + } + }; + shared_ptr target; + try { + ApplyLaunchedResource(launched, attach_info); + + auto &config = DBConfig::GetConfig(client); + AttachOptions options(attach_info.options, config.options.access_mode); + options.visibility = AttachVisibility::HIDDEN; + options.ephemeral = true; + if (owns_resource) { + options.deleter_function = launched.deleter_function; + options.deleter_payload = launched.deleter_payload; + options.deleter_resource_type = resource_type; + } + options.borrowed_resource_name = borrowed_resource_name; + options.original_path = attach_info.path; + if (options.db_type.empty()) { + DBPathAndType::ExtractExtensionPrefix(attach_info.path, options.db_type); + } + target = db_manager.AttachDatabase(client, attach_info, options); + } catch (...) { + // Compensating teardown (best-effort): only for a resource we just PROVISIONED; a borrowed + // (referenced) resource is never torn down here. + reap_if_owned(); + throw; + } + if (!target->GetCatalog().Supports(RemoteCapability::CONNECT)) { + // The detach also runs the extracted deleter, destroying the provisioned resource. + db_manager.DetachDatabase(client, attach_info.name, OnEntryNotFound::RETURN_NULL); + throw InvalidInputException("CONNECT TO EXTERNAL RESOURCE: the provisioned resource does not " + "support CONNECT"); + } + client.ConnectToCatalog(target); + return SourceResultType::FINISHED; } - auto &db_manager = DatabaseManager::Get(client); + if (info->name.empty()) { + throw NotImplementedException("CONNECT with no target is not yet implemented"); + } + + ensure_not_connected(); if (info->name_is_string_literal) { // `CONNECT ''`: attach the connection string under an internal, hidden, ephemeral alias and // bind to it in one shot. The name is a random UUID (like __pivot_enum_): unique, ASCII // (backend-safe), and unguessable, so it is not referenceable in SQL. It is owned by this // connection and detached again by DISCONNECT (see PhysicalDisconnect). AttachInfo attach_info; - attach_info.name = Identifier("__connect_" + UUID::ToString(UUID::GenerateRandomUUID())); + attach_info.name = GenerateInternalName("__connect_"); attach_info.path = info->name.GetIdentifierName(); attach_info.options = info->options; @@ -48,6 +126,7 @@ SourceResultType PhysicalConnect::GetDataInternal(ExecutionContext &context, Dat AttachOptions options(attach_info.options, config.options.access_mode); options.visibility = AttachVisibility::HIDDEN; options.ephemeral = true; + options.original_path = attach_info.path; if (options.db_type.empty()) { DBPathAndType::ExtractExtensionPrefix(attach_info.path, options.db_type); } diff --git a/src/duckdb/src/execution/operator/helper/physical_external_resource.cpp b/src/duckdb/src/execution/operator/helper/physical_external_resource.cpp index 17c1ce16b..49f87548f 100644 --- a/src/duckdb/src/execution/operator/helper/physical_external_resource.cpp +++ b/src/duckdb/src/execution/operator/helper/physical_external_resource.cpp @@ -3,11 +3,12 @@ #include "duckdb/execution/operator/helper/launch_external_resource.hpp" #include "duckdb/main/client_context.hpp" #include "duckdb/main/database.hpp" +#include "duckdb/main/database_manager.hpp" #include "duckdb/main/external_resources_manager.hpp" namespace duckdb { -//! Provision (CREATE) or adopt (REGISTER) a resource and register it in the manager under its name. +//! Provision it (CREATE) or take over an existing handle (REGISTER), then record it under its name. static void RegisterExternalResource(ClientContext &client, const BoundExternalResource &data) { auto &manager = ExternalResourcesManager::Get(client); // Reject a duplicate name before provisioning, so a name clash does not spin up (and tear down) a @@ -46,10 +47,14 @@ static void DestroyExternalResource(ClientContext &client, const BoundExternalRe if (!instance) { throw InvalidInputException("external resource \"%s\" is not registered", data.name); } - ResourceDeleter(DatabaseInstance::GetDatabase(client), instance->deleter_function, instance->deleter_payload, - instance->type, instance->name) - .Delete(); + // Never blocked by a borrower: DESTROY is what stops the meter, so it cannot depend on a DETACH + // that may itself be refused. A stranded resource costs money; a dangling attachment costs nothing. + auto &db = DatabaseInstance::GetDatabase(client); + ResourceDeleter(db, instance->deleter_function, instance->deleter_payload, instance->type, instance->name).Delete(); manager.Remove(data.name); + // Then clean up after it: the borrowing attachments now point at nothing, so detach them best-effort. + // Runs only after a successful teardown, so the scan never sits in front of the remote call. + DatabaseManager::Get(client).DetachResourceBorrowers(client, instance->name); } SourceResultType PhysicalExternalResource::GetDataInternal(ExecutionContext &context, DataChunk &chunk, diff --git a/src/duckdb/src/execution/operator/helper/physical_limit.cpp b/src/duckdb/src/execution/operator/helper/physical_limit.cpp index 10697f344..b3bbc6a9a 100644 --- a/src/duckdb/src/execution/operator/helper/physical_limit.cpp +++ b/src/duckdb/src/execution/operator/helper/physical_limit.cpp @@ -1,6 +1,7 @@ #include "duckdb/execution/operator/helper/physical_limit.hpp" #include "duckdb/common/algorithm.hpp" +#include "duckdb/common/atomic.hpp" #include "duckdb/common/types/batched_data_collection.hpp" #include "duckdb/common/vector/constant_vector.hpp" #include "duckdb/execution/expression_executor.hpp" @@ -80,30 +81,27 @@ void PhysicalLimit::SetInitialLimits(const BoundLimitNode &limit_val, const Boun } } +idx_t PhysicalLimit::GetLimitValue(const Value &val, idx_t null_value) { + if (val.IsNull()) { + return null_value; + } + auto value = val.GetValue(); + if (value > MAX_LIMIT_VALUE) { + throw BinderException("Max value %llu for LIMIT/OFFSET is %llu", value, MAX_LIMIT_VALUE); + } + return value; +} + bool PhysicalLimit::ComputeOffset(ExecutionContext &context, DataChunk &input, optional_idx &limit, optional_idx &offset, idx_t current_offset, idx_t &max_element, const BoundLimitNode &limit_val, const BoundLimitNode &offset_val) { if (!limit.IsValid()) { Value val = GetDelimiter(context, input, limit_val.GetValueExpression()); - if (!val.IsNull()) { - limit = val.GetValue(); - } else { - limit = MAX_LIMIT_VALUE; - } - if (limit.GetIndex() > MAX_LIMIT_VALUE) { - throw BinderException("Max value %lld for LIMIT/OFFSET is %lld", limit.GetIndex(), MAX_LIMIT_VALUE); - } + limit = GetLimitValue(val, MAX_LIMIT_VALUE); } if (!offset.IsValid()) { Value val = GetDelimiter(context, input, offset_val.GetValueExpression()); - if (!val.IsNull()) { - offset = val.GetValue(); - } else { - offset = 0; - } - if (offset.GetIndex() > MAX_LIMIT_VALUE) { - throw BinderException("Max value %lld for LIMIT/OFFSET is %lld", offset.GetIndex(), MAX_LIMIT_VALUE); - } + offset = GetLimitValue(val, 0); } max_element = limit.GetIndex() + offset.GetIndex(); if (limit == 0 || current_offset >= max_element) { @@ -159,11 +157,17 @@ class LimitSourceState : public GlobalSourceState { LimitSourceState() { initialized = false; current_offset = 0; + scanned_rows = 0; + total_rows = 0; } bool initialized; idx_t current_offset; BatchedChunkScanState scan_state; + //! The number of rows scanned from the collection (including rows skipped by the offset) + atomic scanned_rows; + //! The number of rows that will be scanned - set when the scan is initialized + atomic total_rows; }; unique_ptr PhysicalLimit::GetGlobalSourceState(ClientContext &context) const { @@ -177,9 +181,15 @@ SourceResultType PhysicalLimit::GetDataInternal(ExecutionContext &context, DataC while (state.current_offset < gstate.limit + gstate.offset) { if (!state.initialized) { gstate.data.InitializeScan(state.scan_state); + idx_t total_rows = gstate.data.Count(); + if (gstate.limit != DConstants::INVALID_INDEX) { + total_rows = MinValue(total_rows, gstate.limit + gstate.offset); + } + state.total_rows = total_rows; state.initialized = true; } gstate.data.Scan(state.scan_state, chunk); + state.scanned_rows.fetch_add(chunk.size(), std::memory_order_relaxed); if (chunk.size() == 0) { return SourceResultType::FINISHED; } @@ -191,6 +201,15 @@ SourceResultType PhysicalLimit::GetDataInternal(ExecutionContext &context, DataC return chunk.size() > 0 ? SourceResultType::HAVE_MORE_OUTPUT : SourceResultType::FINISHED; } +ProgressData PhysicalLimit::GetProgress(ClientContext &context, GlobalSourceState &gstate_p) const { + auto &state = gstate_p.Cast(); + idx_t total = state.total_rows; + ProgressData progress; + progress.total = static_cast(MaxValue(total, 1)); + progress.done = static_cast(MinValue(state.scanned_rows, total)); + return progress; +} + bool PhysicalLimit::HandleOffset(DataChunk &input, idx_t ¤t_offset, idx_t offset, idx_t limit) { idx_t max_element = limit + offset; if (limit == DConstants::INVALID_INDEX) { @@ -235,9 +254,7 @@ Value PhysicalLimit::GetDelimiter(ExecutionContext &context, DataChunk &input, c // only evaluate the expression on the first row of the input DataChunk single_row_input; single_row_input.InitializeEmpty(input.GetTypes()); - for (idx_t c = 0; c < input.ColumnCount(); c++) { - ConstantVector::Reference(single_row_input.data[c], count_t(1), input.data[c], 0, input.size()); - } + single_row_input.Slice(input, 0, MinValue(input.size(), 1)); limit_executor.Execute(single_row_input, limit_chunk); auto limit_value = limit_chunk.GetValue(0, 0); return limit_value; diff --git a/src/duckdb/src/execution/operator/helper/physical_limit_percent.cpp b/src/duckdb/src/execution/operator/helper/physical_limit_percent.cpp index 270287907..e18ad936f 100644 --- a/src/duckdb/src/execution/operator/helper/physical_limit_percent.cpp +++ b/src/duckdb/src/execution/operator/helper/physical_limit_percent.cpp @@ -81,14 +81,7 @@ SinkResultType PhysicalLimitPercent::Sink(ExecutionContext &context, DataChunk & } if (!state.offset.IsValid()) { Value val = PhysicalLimit::GetDelimiter(context, chunk, offset_val.GetValueExpression()); - if (!val.IsNull()) { - offset = val.GetValue(); - } else { - offset = 0; - } - if (offset.GetIndex() > 1ULL << 62ULL) { - throw BinderException("Max value %lld for LIMIT/OFFSET is %lld", offset.GetIndex(), 1ULL << 62ULL); - } + offset = PhysicalLimit::GetLimitValue(val, 0); } if (!PhysicalLimit::HandleOffset(chunk, state.current_offset, offset.GetIndex(), NumericLimits::Maximum())) { @@ -104,7 +97,7 @@ SinkResultType PhysicalLimitPercent::Sink(ExecutionContext &context, DataChunk & //===--------------------------------------------------------------------===// class LimitPercentOperatorState : public GlobalSourceState { public: - explicit LimitPercentOperatorState(const PhysicalLimitPercent &op) : current_offset(0) { + explicit LimitPercentOperatorState(const PhysicalLimitPercent &op) : current_offset(0), limit_validated(false) { D_ASSERT(op.sink_state); auto &gstate = op.sink_state->Cast(); gstate.data.InitializeScan(scan_state); @@ -113,12 +106,37 @@ class LimitPercentOperatorState : public GlobalSourceState { ColumnDataScanState scan_state; optional_idx limit; idx_t current_offset; + atomic limit_validated; + //! The number of rows that will be emitted, the rows scanned so far and whether the source finished + atomic total_rows {0}; + atomic scanned_rows {0}; + atomic finished {false}; }; unique_ptr PhysicalLimitPercent::GetGlobalSourceState(ClientContext &context) const { return make_uniq(*this); } +ProgressData PhysicalLimitPercent::GetProgress(ClientContext &context, GlobalSourceState &gstate) const { + auto &state = gstate.Cast(); + if (state.finished.load(std::memory_order_relaxed)) { + return ProgressData {1.0, 1.0, false}; + } + auto total_rows = state.total_rows.load(std::memory_order_relaxed); + if (total_rows == 0) { + return ProgressData {0.0, 1.0, false}; + } + auto scanned_rows = MinValue(state.scanned_rows.load(std::memory_order_relaxed), total_rows); + return ProgressData {double(scanned_rows), double(total_rows), false}; +} + +void PhysicalLimitPercent::SourceFinished(ClientContext &context, GlobalSourceState &gstate) const { + auto &state = gstate.Cast(); + if (state.limit_validated.load(std::memory_order_relaxed)) { + state.finished.store(true, std::memory_order_relaxed); + } +} + SourceResultType PhysicalLimitPercent::GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const { auto &gstate = sink_state->Cast(); @@ -133,6 +151,7 @@ SourceResultType PhysicalLimitPercent::GetDataInternal(ExecutionContext &context // no limit value and we have not set limit_percent // we are running LIMIT % with a subquery over an empty table D_ASSERT(gstate.data.Count() == 0); + state.limit_validated.store(true, std::memory_order_relaxed); return SourceResultType::FINISHED; } idx_t count = gstate.data.Count(); @@ -148,6 +167,8 @@ SourceResultType PhysicalLimitPercent::GetDataInternal(ExecutionContext &context } else { limit = idx_t(limit_percentage); } + state.total_rows.store(MinValue(limit.GetIndex(), gstate.data.Count()), std::memory_order_relaxed); + state.limit_validated.store(true, std::memory_order_relaxed); if (limit == 0) { return SourceResultType::FINISHED; } @@ -161,6 +182,7 @@ SourceResultType PhysicalLimitPercent::GetDataInternal(ExecutionContext &context } PhysicalLimit::HandleOffset(chunk, current_offset, 0, limit.GetIndex()); + state.scanned_rows.store(current_offset, std::memory_order_relaxed); return SourceResultType::HAVE_MORE_OUTPUT; } diff --git a/src/duckdb/src/execution/operator/helper/physical_pragma.cpp b/src/duckdb/src/execution/operator/helper/physical_pragma.cpp index df48d7f8d..88d66e4f8 100644 --- a/src/duckdb/src/execution/operator/helper/physical_pragma.cpp +++ b/src/duckdb/src/execution/operator/helper/physical_pragma.cpp @@ -5,7 +5,7 @@ namespace duckdb { SourceResultType PhysicalPragma::GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const { auto &client = context.client; - FunctionParameters parameters {info->parameters, info->named_parameters}; + FunctionParameters parameters {info->parameters, info->named_parameters, &info->function}; info->function.function(client, parameters); return SourceResultType::FINISHED; diff --git a/src/duckdb/src/execution/operator/helper/physical_reservoir_sample.cpp b/src/duckdb/src/execution/operator/helper/physical_reservoir_sample.cpp index 2b2edad5c..f25139601 100644 --- a/src/duckdb/src/execution/operator/helper/physical_reservoir_sample.cpp +++ b/src/duckdb/src/execution/operator/helper/physical_reservoir_sample.cpp @@ -1,5 +1,6 @@ #include "duckdb/execution/operator/helper/physical_reservoir_sample.hpp" #include "duckdb/execution/reservoir_sample.hpp" +#include "duckdb/common/atomic.hpp" namespace duckdb { @@ -34,7 +35,7 @@ class SampleGlobalSinkState : public GlobalSinkState { }; unique_ptr PhysicalReservoirSample::GetGlobalSinkState(ClientContext &context) const { - return make_uniq(Allocator::Get(context), *options); + return make_uniq(BufferAllocator::Get(context), *options); } SinkResultType PhysicalReservoirSample::Sink(ExecutionContext &context, DataChunk &chunk, @@ -44,7 +45,7 @@ SinkResultType PhysicalReservoirSample::Sink(ExecutionContext &context, DataChun lock_guard glock(global_state.lock); if (!global_state.sample) { // always gather full thread percentage - auto &allocator = Allocator::Get(context.client); + auto &allocator = BufferAllocator::Get(context.client); if (options->is_percentage) { double percentage = options->sample_size.GetValue(); if (percentage == 0) { @@ -78,19 +79,56 @@ SinkFinalizeType PhysicalReservoirSample::Finalize(Pipeline &pipeline, Event &ev //===--------------------------------------------------------------------===// // Source //===--------------------------------------------------------------------===// +class SampleGlobalSourceState : public GlobalSourceState { +public: + atomic total_rows {0}; + atomic rows_scanned {0}; + atomic finished {false}; +}; + +unique_ptr PhysicalReservoirSample::GetGlobalSourceState(ClientContext &context) const { + return make_uniq(); +} + +ProgressData PhysicalReservoirSample::GetProgress(ClientContext &context, GlobalSourceState &gstate) const { + auto &state = gstate.Cast(); + if (state.finished.load(std::memory_order_relaxed)) { + return ProgressData {1.0, 1.0, false}; + } + auto total_rows = state.total_rows.load(std::memory_order_relaxed); + if (total_rows == 0) { + return ProgressData {0.0, 1.0, false}; + } + auto rows_scanned = MinValue(state.rows_scanned.load(std::memory_order_relaxed), total_rows); + return ProgressData {double(rows_scanned), double(total_rows), false}; +} + +void PhysicalReservoirSample::SourceFinished(ClientContext &context, GlobalSourceState &gstate) const { + gstate.Cast().finished = true; +} + SourceResultType PhysicalReservoirSample::GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const { auto &sink = this->sink_state->Cast(); + auto &state = input.global_state.Cast(); lock_guard glock(sink.lock); if (!sink.sample) { + state.finished = true; return SourceResultType::FINISHED; } auto sample_chunk = sink.sample->GetChunk(); if (!sample_chunk) { + state.finished = true; return SourceResultType::FINISHED; } + if (state.total_rows == 0) { + auto remaining = options->is_percentage ? sink.sample->Cast().GetActiveSampleCount() + : sink.sample->Cast().GetActiveSampleCount(); + state.total_rows = sample_chunk->size() + remaining; + } chunk.Move(*sample_chunk); + state.rows_scanned.fetch_add(chunk.size(), std::memory_order_relaxed); return SourceResultType::HAVE_MORE_OUTPUT; } diff --git a/src/duckdb/src/execution/operator/helper/physical_reset.cpp b/src/duckdb/src/execution/operator/helper/physical_reset.cpp index e94dc2243..b8a9866a3 100644 --- a/src/duckdb/src/execution/operator/helper/physical_reset.cpp +++ b/src/duckdb/src/execution/operator/helper/physical_reset.cpp @@ -10,10 +10,13 @@ namespace duckdb { void PhysicalReset::ResetExtensionVariable(ExecutionContext &context, DBConfig &config, ExtensionOption &extension_option) const { - auto effective_scope = scope == SetScope::AUTOMATIC ? extension_option.default_scope : scope; + auto effective_scope = extension_option.ResolveScope(Identifier(name.ToStdString()), scope); if (extension_option.set_function) { extension_option.set_function(context.client, effective_scope, extension_option.default_value); } + if (extension_option.info) { + extension_option.info->OnSet(context.client, effective_scope, extension_option.default_value, true); + } if (effective_scope == SetScope::GLOBAL) { config.ResetOption(extension_option); } else { diff --git a/src/duckdb/src/execution/operator/helper/physical_result_collector.cpp b/src/duckdb/src/execution/operator/helper/physical_result_collector.cpp index f742c8e00..c1d9f940a 100644 --- a/src/duckdb/src/execution/operator/helper/physical_result_collector.cpp +++ b/src/duckdb/src/execution/operator/helper/physical_result_collector.cpp @@ -1,6 +1,5 @@ #include "duckdb/execution/operator/helper/physical_result_collector.hpp" -#include "duckdb/common/types/column/column_data_collection.hpp" #include "duckdb/execution/operator/helper/physical_result_sink.hpp" #include "duckdb/execution/physical_plan_generator.hpp" #include "duckdb/main/config.hpp" @@ -8,15 +7,14 @@ #include "duckdb/parallel/meta_pipeline.hpp" #include "duckdb/main/query_result.hpp" #include "duckdb/parallel/pipeline.hpp" -#include "duckdb/storage/buffer_manager.hpp" #include "duckdb/main/client_context.hpp" namespace duckdb { PhysicalResultCollector::PhysicalResultCollector(PhysicalPlan &physical_plan, PreparedStatementData &data) : PhysicalOperator(physical_plan, PhysicalOperatorType::RESULT_COLLECTOR, {LogicalType::BOOLEAN}, 0), - statement_type(data.statement_type), properties(data.properties), memory_type(data.memory_type), - plan(data.physical_plan->Root()), names(data.names) { + statement_type(data.statement_type), properties(data.properties), plan(data.physical_plan->Root()), + names(data.names) { types = data.types; } @@ -25,15 +23,13 @@ unique_ptr PhysicalResultCollector::GetResultCollector(ClientC auto &physical_plan = *data.physical_plan; auto &root = physical_plan.Root(); - const auto lifetime = data.output_type == QueryResultOutputType::ALLOW_STREAMING ? ResultLifetime::UNDECIDED - : ResultLifetime::RETAINED; if (!PhysicalPlanGenerator::PreserveInsertionOrder(context, root)) { - return make_uniq(physical_plan, data, lifetime, ResultOrdering::UNORDERED); + return make_uniq(physical_plan, data, ResultOrdering::UNORDERED); } if (!PhysicalPlanGenerator::UseBatchIndex(context, root)) { - return make_uniq(physical_plan, data, lifetime, ResultOrdering::SOURCE_ORDERED); + return make_uniq(physical_plan, data, ResultOrdering::SOURCE_ORDERED); } - return make_uniq(physical_plan, data, lifetime, ResultOrdering::BATCH_INDEX_ORDERED); + return make_uniq(physical_plan, data, ResultOrdering::BATCH_INDEX_ORDERED); } vector> PhysicalResultCollector::GetChildren() const { @@ -55,18 +51,4 @@ void PhysicalResultCollector::BuildPipelines(Pipeline ¤t, MetaPipeline &me child_meta_pipeline.Build(plan); } -unique_ptr PhysicalResultCollector::CreateCollection(ClientContext &context) const { - switch (memory_type) { - case QueryResultMemoryType::IN_MEMORY: - return make_uniq(Allocator::DefaultAllocator(), types); - case QueryResultMemoryType::BUFFER_MANAGED: - // Use the DatabaseInstance BufferManager because the query result can outlive the ClientContext - return make_uniq(BufferManager::GetBufferManager(*context.db), types, - ColumnDataCollectionLifetime::THROW_ERROR_AFTER_DATABASE_CLOSES); - default: - throw NotImplementedException("PhysicalResultCollector::CreateCollection for %s", - EnumUtil::ToString(memory_type)); - } -} - } // namespace duckdb diff --git a/src/duckdb/src/execution/operator/helper/physical_result_sink.cpp b/src/duckdb/src/execution/operator/helper/physical_result_sink.cpp index fcbdad23e..e079cbf03 100644 --- a/src/duckdb/src/execution/operator/helper/physical_result_sink.cpp +++ b/src/duckdb/src/execution/operator/helper/physical_result_sink.cpp @@ -3,19 +3,19 @@ #include "duckdb/common/exception.hpp" #include "duckdb/common/mutex.hpp" #include "duckdb/common/thread_annotation.hpp" -#include "duckdb/common/types/batched_data_collection.hpp" -#include "duckdb/common/types/column/column_data_collection.hpp" #include "duckdb/main/buffered_data/batched_buffered_data.hpp" #include "duckdb/main/buffered_data/simple_buffered_data.hpp" #include "duckdb/main/client_context.hpp" -#include "duckdb/main/materialized_query_result.hpp" -#include "duckdb/main/stream_query_result.hpp" +#include "duckdb/main/query_result.hpp" +#include "duckdb/main/result_format.hpp" +#include "duckdb/main/result_unit.hpp" +#include "duckdb/main/retained_result_collection.hpp" namespace duckdb { PhysicalResultSink::PhysicalResultSink(PhysicalPlan &physical_plan, PreparedStatementData &data, - ResultLifetime lifetime, ResultOrdering ordering) - : PhysicalResultCollector(physical_plan, data), lifetime(lifetime), ordering(ordering) { + ResultOrdering ordering) + : PhysicalResultCollector(physical_plan, data), ordering(ordering) { } //===--------------------------------------------------------------------===// @@ -25,40 +25,36 @@ class ResultSinkGlobalState : public GlobalSinkState { public: //! This is weak to avoid creating a cyclical reference weak_ptr context; - //! The buffer behind a stream result. It also holds the retention decision, so it exists - //! whenever the plan left retention open. Null for a sink retained by the plan + //! The buffer behind the result. It holds the retention decision and the format shared_ptr buffered_data; annotated_mutex glock; - //! CDC to materialize a result in arrival order - unique_ptr collection DUCKDB_GUARDED_BY(glock); - //! CDC to materialize a result in batch order - unique_ptr batch_data DUCKDB_GUARDED_BY(glock); + //! The merged retained deposit, once at least one producer has combined into it + unique_ptr collection DUCKDB_GUARDED_BY(glock); }; class ResultSinkLocalState : public LocalSinkState { public: - //! Set when a park deposited the chunk, so the re-delivery is not appended again. Parks deposit - //! so that a parked producer always implies a poppable chunk - bool chunk_deposited = false; + //! Set once the chunk is appended, so a re-invocation after BLOCKED resumes the drain, not the append + bool chunk_appended = false; //! The batch this producer is currently sinking idx_t current_batch = 0; - //! Local CDC (arrival order) that will be merged later, in Combine - unique_ptr collection; - ColumnDataAppendState append_state; - //! Local CDC (batch order) that will be merged later, in Combine - unique_ptr batch_data; + //! Created at the first Append, because the lifetime is not settled yet when the local sink state is + unique_ptr format_state; + //! The producer's own retained deposit, created lazily at the first retained append and merged + //! into the global instance in Combine + unique_ptr collection; }; +void PhysicalResultSink::SetResultBuffer(shared_ptr buffer) { + D_ASSERT(!result_buffer); + result_buffer = std::move(buffer); +} + unique_ptr PhysicalResultSink::GetGlobalSinkState(ClientContext &context) const { auto state = make_uniq(); state->context = context.shared_from_this(); - if (lifetime != ResultLifetime::RETAINED) { - if (BatchOrdered()) { - state->buffered_data = make_shared_ptr(context, lifetime); - } else { - state->buffered_data = make_shared_ptr(context, lifetime); - } - } + D_ASSERT(result_buffer); + state->buffered_data = result_buffer; return std::move(state); } @@ -67,9 +63,6 @@ unique_ptr PhysicalResultSink::GetLocalSinkState(ExecutionContex } ResultLifetime PhysicalResultSink::CurrentLifetime(ResultSinkGlobalState &gstate) const { - if (!gstate.buffered_data) { - return ResultLifetime::RETAINED; - } return gstate.buffered_data->Lifetime(); } @@ -77,6 +70,15 @@ bool PhysicalResultSink::DrainsByBatchIndex(ResultSinkGlobalState &gstate) const return BatchOrdered() && CurrentLifetime(gstate) != ResultLifetime::RETAINED; } +ResultFormatLocalState &PhysicalResultSink::LocalFormatState(ResultSinkGlobalState &gstate, + ResultSinkLocalState &lstate) const { + if (!lstate.format_state) { + auto &format = gstate.buffered_data->Format(); + lstate.format_state = format.InitLocal(gstate.buffered_data->FormatState()); + } + return *lstate.format_state; +} + SinkResultType PhysicalResultSink::Sink(ExecutionContext &context, DataChunk &chunk, OperatorSinkInput &input) const { auto &gstate = input.global_state.Cast(); auto &lstate = input.local_state.Cast(); @@ -89,49 +91,85 @@ SinkResultType PhysicalResultSink::Sink(ExecutionContext &context, DataChunk &ch current = gstate.buffered_data->Lifetime(); } if (current == ResultLifetime::RETAINED) { - return SinkRetained(context, lstate, chunk); + return SinkRetained(context, gstate, lstate, chunk); } return SinkDraining(gstate, lstate, chunk, input); } -SinkResultType PhysicalResultSink::SinkRetained(ExecutionContext &context, ResultSinkLocalState &lstate, - DataChunk &chunk) const { - if (BatchOrdered()) { - if (!lstate.batch_data) { - lstate.batch_data = make_uniq(context.client, types, memory_type); +SinkResultType PhysicalResultSink::SinkRetained(ExecutionContext &context, ResultSinkGlobalState &gstate, + ResultSinkLocalState &lstate, DataChunk &chunk) const { + auto &buffered_data = *gstate.buffered_data; + if (!lstate.collection) { + lstate.collection = buffered_data.Format().CreateCollection(context.client, buffered_data.FormatState(), + buffered_data.FormatContext()); + } + // batch_index is unset (throws on GetIndex) for a plan that is not batch ordered + auto batch = BatchOrdered() ? lstate.partition_info.batch_index.GetIndex() : 0; + lstate.collection->Append(chunk, batch); + return SinkResultType::NEED_MORE_INPUT; +} + +void PhysicalResultSink::AppendChunk(ResultSinkGlobalState &gstate, ResultSinkLocalState &lstate, + DataChunk &chunk) const { + auto &format = gstate.buffered_data->Format(); + format.AppendToUnit(gstate.buffered_data->FormatState(), LocalFormatState(gstate, lstate), chunk); +} + +bool PhysicalResultSink::DrainFinishedUnits(ResultSinkGlobalState &gstate, ResultSinkLocalState &lstate, + const InterruptState &interrupt) const { + if (!lstate.format_state) { + return false; + } + auto &format = gstate.buffered_data->Format(); + // A re-invocation after BLOCKED continues here: the delivered unit is already out of the format's state + while (format.IsUnitFinished(*lstate.format_state)) { + auto unit = format.FinishUnit(gstate.buffered_data->FormatState(), *lstate.format_state); + if (HandOver(gstate, lstate, std::move(unit), interrupt)) { + return true; } - lstate.batch_data->Append(chunk, lstate.partition_info.batch_index.GetIndex()); - } else { - if (!lstate.collection) { - lstate.collection = CreateCollection(context.client); - lstate.collection->InitializeAppend(lstate.append_state); + } + return false; +} + +bool PhysicalResultSink::FlushUnits(ResultSinkGlobalState &gstate, ResultSinkLocalState &lstate, + const InterruptState &interrupt) const { + if (!lstate.format_state) { + return false; + } + auto &format = gstate.buffered_data->Format(); + while (auto unit = format.FinishUnit(gstate.buffered_data->FormatState(), *lstate.format_state)) { + if (HandOver(gstate, lstate, std::move(unit), interrupt)) { + return true; } - lstate.collection->Append(lstate.append_state, chunk); } - return SinkResultType::NEED_MORE_INPUT; + return false; +} + +bool PhysicalResultSink::HandOver(ResultSinkGlobalState &gstate, ResultSinkLocalState &lstate, + unique_ptr unit, const InterruptState &interrupt) const { + D_ASSERT(unit->row_count > 0); + if (BatchOrdered()) { + return gstate.buffered_data->Cast().AppendOrBlock(std::move(unit), lstate.current_batch, + interrupt); + } + return gstate.buffered_data->Cast().AppendOrBlock(std::move(unit), interrupt); } SinkResultType PhysicalResultSink::SinkDraining(ResultSinkGlobalState &gstate, ResultSinkLocalState &lstate, DataChunk &chunk, OperatorSinkInput &input) const { - if (lstate.chunk_deposited) { - lstate.chunk_deposited = false; - return SinkResultType::NEED_MORE_INPUT; - } - if (BatchOrdered()) { - auto batch = lstate.partition_info.batch_index.GetIndex(); - auto min_batch_index = lstate.partition_info.min_batch_index.GetIndex(); - lstate.current_batch = batch; - auto &buffered_data = gstate.buffered_data->Cast(); - buffered_data.UpdateMinBatchIndex(min_batch_index); - if (buffered_data.AppendOrBlock(chunk, batch, input.interrupt_state)) { - lstate.chunk_deposited = true; - return SinkResultType::BLOCKED; + if (lstate.chunk_appended) { + // The chunk was appended before the park; the units it still owes come out of the drain below + lstate.chunk_appended = false; + } else { + if (BatchOrdered()) { + lstate.current_batch = lstate.partition_info.batch_index.GetIndex(); + gstate.buffered_data->Cast().UpdateMinBatchIndex( + lstate.partition_info.min_batch_index.GetIndex()); } - return SinkResultType::NEED_MORE_INPUT; + AppendChunk(gstate, lstate, chunk); } - auto &buffered_data = gstate.buffered_data->Cast(); - if (buffered_data.AppendOrBlock(chunk, input.interrupt_state)) { - lstate.chunk_deposited = true; + if (DrainFinishedUnits(gstate, lstate, input.interrupt_state)) { + lstate.chunk_appended = true; return SinkResultType::BLOCKED; } return SinkResultType::NEED_MORE_INPUT; @@ -140,8 +178,11 @@ SinkResultType PhysicalResultSink::SinkDraining(ResultSinkGlobalState &gstate, R SinkCombineResultType PhysicalResultSink::Combine(ExecutionContext &context, OperatorSinkCombineInput &input) const { auto &gstate = input.global_state.Cast(); auto &lstate = input.local_state.Cast(); + if (FlushUnits(gstate, lstate, input.interrupt_state)) { + return SinkCombineResultType::BLOCKED; + } if (CurrentLifetime(gstate) == ResultLifetime::RETAINED) { - return CombineRetained(gstate, lstate); + return CombineRetained(context.client, gstate, lstate); } return CombineDraining(gstate, lstate); } @@ -155,39 +196,51 @@ SinkCombineResultType PhysicalResultSink::CombineDraining(ResultSinkGlobalState return SinkCombineResultType::FINISHED; } -SinkCombineResultType PhysicalResultSink::CombineRetained(ResultSinkGlobalState &gstate, +SinkCombineResultType PhysicalResultSink::CombineRetained(ClientContext &context, ResultSinkGlobalState &gstate, ResultSinkLocalState &lstate) const { + auto &buffered_data = *gstate.buffered_data; + annotated_lock_guard l(gstate.glock); + if (!gstate.collection) { + // Never a producer's own: Combine flushes the producer's partial unit here, inside its task, so a + // throwing format surfaces as the query's error + gstate.collection = buffered_data.Format().CreateCollection(context, buffered_data.FormatState(), + buffered_data.FormatContext()); + } // A producer whose partition held no rows never created its local collection - if (BatchOrdered()) { - if (!lstate.batch_data) { - return SinkCombineResultType::FINISHED; - } - annotated_lock_guard l(gstate.glock); - if (!gstate.batch_data) { - gstate.batch_data = std::move(lstate.batch_data); - } else { - gstate.batch_data->Merge(*lstate.batch_data); - } - return SinkCombineResultType::FINISHED; + if (lstate.collection) { + gstate.collection->Combine(*lstate.collection); } - if (!lstate.collection || lstate.collection->Count() == 0) { - return SinkCombineResultType::FINISHED; + return SinkCombineResultType::FINISHED; +} + +SinkFinalizeType PhysicalResultSink::Finalize(Pipeline &pipeline, Event &event, ClientContext &context, + OperatorSinkFinalizeInput &input) const { + auto &gstate = input.global_state.Cast(); + if (CurrentLifetime(gstate) == ResultLifetime::DRAINING) { + return SinkFinalizeType::READY; } + auto &buffered_data = *gstate.buffered_data; annotated_lock_guard l(gstate.glock); if (!gstate.collection) { - gstate.collection = std::move(lstate.collection); - } else { - gstate.collection->Combine(*lstate.collection); + // A query that sinks no rows can reach here still UNDECIDED: no producer ever parked, so an empty + // deposit is correct whether the consumer goes on to retain or to drain instead + gstate.collection = buffered_data.Format().CreateCollection(context, buffered_data.FormatState(), + buffered_data.FormatContext()); } - return SinkCombineResultType::FINISHED; + gstate.collection->Finalize(); + return SinkFinalizeType::READY; } SinkNextBatchType PhysicalResultSink::NextBatch(ExecutionContext &context, OperatorSinkNextBatchInput &input) const { auto &gstate = input.global_state.Cast(); + auto &lstate = input.local_state.Cast(); + // Flushed at the batch boundary, so no unit spans two batch indexes + if (FlushUnits(gstate, lstate, input.interrupt_state)) { + return SinkNextBatchType::BLOCKED; + } if (!DrainsByBatchIndex(gstate)) { return SinkNextBatchType::READY; } - auto &lstate = input.local_state.Cast(); auto batch = lstate.current_batch; auto min_batch_index = lstate.partition_info.min_batch_index.GetIndex(); @@ -213,50 +266,23 @@ SinkNextBatchType PhysicalResultSink::UpdateMinBatchIndex(ExecutionContext &cont unique_ptr PhysicalResultSink::GetResult(GlobalSinkState &state) const { auto &gstate = state.Cast(); - if (CurrentLifetime(gstate) == ResultLifetime::RETAINED) { - return GetMaterializedResult(gstate); - } - return GetStreamResult(gstate); -} - -unique_ptr PhysicalResultSink::GetStreamResult(ResultSinkGlobalState &gstate) const { - auto cc = gstate.context.lock(); - if (!cc) { - throw ConnectionException("Connection has already been closed"); - } - return make_uniq(statement_type, properties, types, names, cc->GetClientProperties(), - gstate.buffered_data); -} - -unique_ptr PhysicalResultSink::GetMaterializedResult(ResultSinkGlobalState &gstate) const { + // A draining sink hands its units to the consumer through the buffer, never through a result + D_ASSERT(CurrentLifetime(gstate) != ResultLifetime::DRAINING); auto cc = gstate.context.lock(); if (!cc) { throw ConnectionException("Connection has already been closed"); } - unique_ptr collection; + auto &buffered_data = *gstate.buffered_data; + unique_ptr collection; { annotated_lock_guard l(gstate.glock); - if (BatchOrdered()) { - if (gstate.batch_data) { - collection = gstate.batch_data->FetchCollection(); - } - } else { - collection = std::move(gstate.collection); - } - } - if (!collection) { - collection = CreateCollection(*cc); - } - return make_uniq(statement_type, properties, names, std::move(collection), - cc->GetClientProperties()); -} - -bool PhysicalResultSink::HasBlockedResultProducer(GlobalSinkState &state) const { - auto &gstate = state.Cast(); - if (!gstate.buffered_data) { - return false; + collection = std::move(gstate.collection); } - return gstate.buffered_data->HasParkedProducer(); + // Finalize already built and finalized it, inside the pipeline's finish task + D_ASSERT(collection); + return make_uniq(statement_type, properties, types, names, std::move(collection), + buffered_data.SharedFormat(), buffered_data.SharedFormatState(), + cc->GetClientProperties()); } OperatorPartitionInfo PhysicalResultSink::RequiredPartitionInfo() const { @@ -276,7 +302,8 @@ bool PhysicalResultSink::SinkOrderDependent() const { } bool PhysicalResultSink::IsStreaming() const { - return lifetime != ResultLifetime::RETAINED; + // Producers may park: the buffer's lifetime decides whether they drain or retain + return true; } PipelineExternalInputSupport PhysicalResultSink::GetExternalInputSupport() const { diff --git a/src/duckdb/src/execution/operator/helper/physical_set.cpp b/src/duckdb/src/execution/operator/helper/physical_set.cpp index 6e0f939c8..6833b124e 100644 --- a/src/duckdb/src/execution/operator/helper/physical_set.cpp +++ b/src/duckdb/src/execution/operator/helper/physical_set.cpp @@ -14,14 +14,21 @@ void PhysicalSet::SetGenericVariable(ClientContext &context, idx_t setting_index void PhysicalSet::SetExtensionVariable(ClientContext &context, ExtensionOption &extension_option, SetScope scope, const Value &value) { + SetExtensionVariable(context, Identifier(), extension_option, scope, value); +} + +void PhysicalSet::SetExtensionVariable(ClientContext &context, const Identifier &name, + ExtensionOption &extension_option, SetScope scope, const Value &value) { auto &target_type = extension_option.type; Value target_value = value.CastAs(context, target_type); + auto resolved_scope = extension_option.ResolveScope(name, scope); if (extension_option.set_function) { extension_option.set_function(context, scope, target_value); } - if (scope == SetScope::AUTOMATIC) { - scope = extension_option.default_scope; + if (extension_option.info) { + extension_option.info->OnSet(context, resolved_scope, target_value, false); } + scope = resolved_scope; auto setting_index = extension_option.setting_index.GetIndex(); SetGenericVariable(context, setting_index, scope, std::move(target_value)); } @@ -71,7 +78,7 @@ void PhysicalSet::SetVariable(ClientContext &context, const Identifier &name, Se throw InvalidInputException("Extension parameter %s was not found after autoloading", name); } } - SetExtensionVariable(context, extension_option, scope, value); + SetExtensionVariable(context, name, extension_option, scope, value); return; } SetScope variable_scope = GetSettingScope(*option, scope); diff --git a/src/duckdb/src/execution/operator/helper/physical_transaction.cpp b/src/duckdb/src/execution/operator/helper/physical_transaction.cpp index 602a1d8d0..c94ea0b22 100644 --- a/src/duckdb/src/execution/operator/helper/physical_transaction.cpp +++ b/src/duckdb/src/execution/operator/helper/physical_transaction.cpp @@ -1,89 +1,12 @@ #include "duckdb/execution/operator/helper/physical_transaction.hpp" -#include "duckdb/common/exception/transaction_exception.hpp" #include "duckdb/main/client_context.hpp" -#include "duckdb/main/config.hpp" -#include "duckdb/main/database_manager.hpp" -#include "duckdb/main/valid_checker.hpp" -#include "duckdb/transaction/meta_transaction.hpp" -#include "duckdb/transaction/transaction_manager.hpp" -#include "duckdb/main/settings.hpp" namespace duckdb { SourceResultType PhysicalTransaction::GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const { - auto &client = context.client; - - auto type = info->type; - if (type == TransactionType::COMMIT && ValidChecker::IsInvalidated(client.ActiveTransaction())) { - // transaction is invalidated - turn COMMIT into ROLLBACK - type = TransactionType::ROLLBACK; - } - switch (type) { - case TransactionType::BEGIN_TRANSACTION: { - if (client.transaction.IsAutoCommit()) { - // start the active transaction - // if autocommit is active, we have already called - // BeginTransaction by setting autocommit to false we - // prevent it from being closed after this query, hence - // preserving the transaction context for the next query - client.transaction.SetAutoCommit(false); - if (info->modifier == TransactionModifierType::TRANSACTION_READ_ONLY) { - client.transaction.SetReadOnly(); - } - client.transaction.SetInvalidationPolicy(info->invalidation_policy); - client.transaction.SetAutoRollback(info->auto_rollback); - if (Settings::Get(context.client)) { - // if immediate transaction mode is enabled then start all transactions immediately - auto databases = DatabaseManager::Get(client).GetDatabases(client); - for (auto &db : databases) { - if (ValidChecker::IsInvalidated(*db)) { - continue; - } - context.client.transaction.ActiveTransaction().GetTransaction(*db); - } - } - } else { - throw TransactionException("cannot start a transaction within a transaction"); - } - break; - } - case TransactionType::COMMIT: { - if (client.transaction.IsAutoCommit()) { - throw TransactionException("cannot commit - no transaction is active"); - } else { - // explicitly commit the current transaction - client.transaction.Commit(); - // Suppress further interrupts for the remainder of this query. - // A committed transaction is irreversible. If a concurrent Interrupt() - // arrives after the physical commit, it must be silently discarded — - // otherwise the caller sees a failed COMMIT even though the data - // is already durably committed. - client.SuppressInterrupts(); - } - break; - } - case TransactionType::ROLLBACK: { - if (client.transaction.IsAutoCommit()) { - throw TransactionException("cannot rollback - no transaction is active"); - } else { - // Explicitly rollback the current transaction - // If it is because of an invalidated transaction, we need to rollback with an error - auto &valid_checker = ValidChecker::Get(client.transaction.ActiveTransaction()); - if (valid_checker.IsInvalidated()) { - ErrorData error(ExceptionType::TRANSACTION, valid_checker.InvalidatedMessage()); - client.transaction.Rollback(error); - } else { - client.transaction.Rollback(nullptr); - } - } - break; - } - default: - throw NotImplementedException("Unrecognized transaction type!"); - } - + context.client.RunTransactionStatementInternal(*info); return SourceResultType::FINISHED; } diff --git a/src/duckdb/src/execution/operator/helper/physical_verify_vector.cpp b/src/duckdb/src/execution/operator/helper/physical_verify_vector.cpp index 7bf7bacb1..5caaa5b6c 100644 --- a/src/duckdb/src/execution/operator/helper/physical_verify_vector.cpp +++ b/src/duckdb/src/execution/operator/helper/physical_verify_vector.cpp @@ -56,6 +56,48 @@ struct ConstantOrSequenceInfo { bool is_constant = true; }; +static const vector &GetNestedValueChildren(const Value &value) { + switch (value.type().InternalType()) { + case PhysicalType::STRUCT: + return StructValue::GetChildren(value); + case PhysicalType::LIST: + return ListValue::GetChildren(value); + case PhysicalType::ARRAY: + return ArrayValue::GetChildren(value); + default: + throw InternalException("Expected a nested value"); + } +} + +static bool CanShareConstantValue(const Value &left, const Value &right) { + if (left.type() != right.type()) { + return false; + } + if (left.IsNull() || right.IsNull()) { + return left.IsNull() == right.IsNull(); + } + switch (left.type().InternalType()) { + case PhysicalType::STRUCT: + case PhysicalType::LIST: + case PhysicalType::ARRAY: { + // Compare VARIANT storage children directly to preserve payload types and object fields. + auto &left_children = GetNestedValueChildren(left); + auto &right_children = GetNestedValueChildren(right); + if (left_children.size() != right_children.size()) { + return false; + } + for (idx_t i = 0; i < left_children.size(); i++) { + if (!CanShareConstantValue(left_children[i], right_children[i])) { + return false; + } + } + return true; + } + default: + return ValueOperations::NotDistinctFrom(left, right); + } +} + OperatorResultType VerifyEmitSequenceVector(const DataChunk &input_p, DataChunk &chunk, OperatorState &state_p) { auto &state = state_p.Cast(); D_ASSERT(state.const_idx < input_p.size()); @@ -97,7 +139,7 @@ OperatorResultType VerifyEmitSequenceVector(const DataChunk &input_p, DataChunk if (info.values.empty()) { info.values.push_back(std::move(val)); } else if (info.is_constant) { - if (!ValueOperations::DistinctFrom(val, info.values[0]) && can_be_constant) { + if (can_be_constant && CanShareConstantValue(val, info.values[0])) { // found the same value! continue info.values.push_back(std::move(val)); continue; diff --git a/src/duckdb/src/execution/operator/join/outer_join_marker.cpp b/src/duckdb/src/execution/operator/join/outer_join_marker.cpp index 87997a861..f79f6262b 100644 --- a/src/duckdb/src/execution/operator/join/outer_join_marker.cpp +++ b/src/duckdb/src/execution/operator/join/outer_join_marker.cpp @@ -4,6 +4,14 @@ namespace duckdb { +ProgressData OuterJoinGlobalScanState::GetProgress() const { + const auto total = data->Count(); + if (total == 0) { + return ProgressData {1.0, 1.0, false}; + } + return ProgressData {double(rows_scanned.load()), double(total), false}; +} + void OuterJoinLocalScanState::Reset() { scan_chunk.Reset(); local_scan = ColumnDataLocalScanState(); @@ -77,6 +85,7 @@ idx_t OuterJoinMarker::MaxThreads() const { void OuterJoinMarker::InitializeScan(ColumnDataCollection &data, OuterJoinGlobalScanState &gstate) { gstate.data = &data; + gstate.rows_scanned = 0; data.InitializeScan(gstate.global_scan); } @@ -103,10 +112,10 @@ void OuterJoinMarker::Scan(OuterJoinGlobalScanState &gstate, OuterJoinLocalScanS for (idx_t i = 0; i < left_column_count; i++) { ConstantVector::SetNull(result.data[i], count_t(result_count)); } - for (idx_t col_idx = left_column_count; col_idx < result.ColumnCount(); col_idx++) { - result.data[col_idx].Slice(lstate.scan_chunk.data[col_idx - left_column_count], lstate.match_sel, - result_count); - } + result.Slice(lstate.scan_chunk, lstate.match_sel, result_count, left_column_count); + } + gstate.rows_scanned.fetch_add(lstate.scan_chunk.size(), std::memory_order_relaxed); + if (result_count > 0) { return; } } diff --git a/src/duckdb/src/execution/operator/join/physical_asof_join.cpp b/src/duckdb/src/execution/operator/join/physical_asof_join.cpp index 75426de91..7a8913391 100644 --- a/src/duckdb/src/execution/operator/join/physical_asof_join.cpp +++ b/src/duckdb/src/execution/operator/join/physical_asof_join.cpp @@ -712,6 +712,8 @@ class AsOfGlobalSourceState : public GlobalSourceState { atomic finished; //! Stop producing tasks atomic stopped; + //! The progress of the tasks + TaskProgress task_progress; public: idx_t MaxThreads() override { @@ -1020,14 +1022,16 @@ AsOfProbeBuffer::AsOfProbeBuffer(ClientContext &client, const PhysicalAsOfJoin & prefix_types.emplace_back(type); SortKeyPrefixComparisonColumn col; col.size = DConstants::INVALID_INDEX; - switch (type.id()) { - case LogicalTypeId::VARCHAR: - case LogicalTypeId::BLOB: - col.type = SortKeyPrefixComparisonType::VARCHAR; + switch (type.InternalType()) { + case PhysicalType::VARCHAR: + // only the sort keys of VARCHAR are terminated by the first null byte - the sort keys of other string + // types (e.g., BLOB or GEOMETRY) can contain null bytes, so we have to decode them to measure their width + col.type = type.id() == LogicalTypeId::VARCHAR ? SortKeyPrefixComparisonType::VARCHAR + : SortKeyPrefixComparisonType::NESTED; break; - case LogicalTypeId::STRUCT: - case LogicalTypeId::LIST: - case LogicalTypeId::ARRAY: + case PhysicalType::STRUCT: + case PhysicalType::LIST: + case PhysicalType::ARRAY: col.type = SortKeyPrefixComparisonType::NESTED; break; default: @@ -1391,6 +1395,9 @@ class AsOfLocalSourceState : public LocalSourceState { TaskPtr task; //! The task storage Task task_local; + //! The start of the range of the current task, and the progress reported for it + idx_t task_begin = 0; + TaskProgressTracker task_progress; //! The rhs group HashGroupPtr hash_group; //! The read cursor @@ -1479,6 +1486,7 @@ bool AsOfLocalSourceState::TryAssignTask() { // Because downstream operators may be using our internal buffers, // we can't "finish" a task until we are about to get the next one. if (task) { + task_progress.Finish(gsource.task_progress); switch (task->stage) { case AsOfJoinSourceStage::SORT: gsource.asof_groups[task_local.group_idx]->sorted++; @@ -1504,6 +1512,8 @@ bool AsOfLocalSourceState::TryAssignTask() { if (!gsource.TryNextTask(task, task_local)) { return false; } + task_begin = task->begin_idx; + task_progress.Start(); switch (task->stage) { case AsOfJoinSourceStage::SORT: @@ -1652,17 +1662,12 @@ void AsOfLocalSourceState::ExecuteLeftTask(ExecutionContext &context, DataChunk ProgressData PhysicalAsOfJoin::GetProgress(ClientContext &context, GlobalSourceState &gsource_p) const { auto &gsource = gsource_p.Cast(); const auto count = gsource.total_tasks; - - const auto returned = gsource.finished.load(); - - ProgressData res; - if (count) { - res.done = double(returned); - res.total = double(count); - } else { + if (!count) { + ProgressData res; res.SetInvalid(); + return res; } - return res; + return gsource.task_progress.GetProgress(count); } SourceResultType PhysicalAsOfJoin::GetDataInternal(ExecutionContext &context, DataChunk &chunk, @@ -1680,6 +1685,12 @@ SourceResultType PhysicalAsOfJoin::GetDataInternal(ExecutionContext &context, Da gsource.stopped = true; throw; } + if (lsource.task) { + auto &task = *lsource.task; + auto done = task.begin_idx > lsource.task_begin ? task.begin_idx - lsource.task_begin : 0; + auto total = task.end_idx > lsource.task_begin ? task.end_idx - lsource.task_begin : 0; + lsource.task_progress.Update(gsource.task_progress, done, total); + } } else { annotated_lock_guard guard(gsource.lock); if (!gsource.HasMoreTasks()) { diff --git a/src/duckdb/src/execution/operator/join/physical_blockwise_nl_join.cpp b/src/duckdb/src/execution/operator/join/physical_blockwise_nl_join.cpp index 9c4993c74..6089d6a62 100644 --- a/src/duckdb/src/execution/operator/join/physical_blockwise_nl_join.cpp +++ b/src/duckdb/src/execution/operator/join/physical_blockwise_nl_join.cpp @@ -253,6 +253,10 @@ class BlockwiseNLJoinLocalScanState : public LocalSourceState { OuterJoinLocalScanState scan_state; }; +ProgressData PhysicalBlockwiseNLJoin::GetProgress(ClientContext &context, GlobalSourceState &gstate) const { + return gstate.Cast().scan_state.GetProgress(); +} + unique_ptr PhysicalBlockwiseNLJoin::GetGlobalSourceState(ClientContext &context) const { return make_uniq(*this); } diff --git a/src/duckdb/src/execution/operator/join/physical_hash_join.cpp b/src/duckdb/src/execution/operator/join/physical_hash_join.cpp index e678b69b1..81ff1e45d 100644 --- a/src/duckdb/src/execution/operator/join/physical_hash_join.cpp +++ b/src/duckdb/src/execution/operator/join/physical_hash_join.cpp @@ -1,7 +1,9 @@ #include "duckdb/execution/operator/join/physical_hash_join.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/logging/log_manager.hpp" #include "duckdb/common/assert.hpp" +#include "duckdb/common/once_flag.hpp" #include "duckdb/common/projection_index.hpp" #include "duckdb/common/radix_partitioning.hpp" #include "duckdb/common/types.hpp" @@ -13,6 +15,7 @@ #include "duckdb/execution/join_hashtable.hpp" #include "duckdb/execution/operator/aggregate/ungrouped_aggregate_state.hpp" #include "duckdb/execution/operator/join/perfect_hash_join_executor.hpp" +#include "duckdb/execution/operator/join/physical_delim_join.hpp" #include "duckdb/function/aggregate/distributive_function_utils.hpp" #include "duckdb/function/aggregate/distributive_functions.hpp" #include "duckdb/function/function_binder.hpp" @@ -50,6 +53,7 @@ PhysicalHashJoin::PhysicalHashJoin(PhysicalPlan &physical_plan, LogicalOperator : PhysicalComparisonJoin(physical_plan, op, PhysicalOperatorType::HASH_JOIN, std::move(conds), join_type, estimated_cardinality), delim_types(std::move(delim_types)) { + D_ASSERT(join_type != JoinType::MARK || !predicate); filter_pushdown = std::move(pushdown_info_p); children.push_back(left); @@ -296,12 +300,28 @@ unique_ptr JoinFilterPushdownInfo::GetGlobalState(ClientC return result; } -//! True iff the build subtree funnels multiple producer pipelines into one sink (UNION ALL, recursive CTE), -//! breaking the "decide layout once on the first chunk" contract. Conservative: may over-exclude, never misses one. +//! Check for producers that can change a build column's dictionary between chunks. static bool BuildSideHasMultipleSources(const PhysicalOperator &op) { - if (op.type == PhysicalOperatorType::UNION || op.type == PhysicalOperatorType::RECURSIVE_CTE || - op.type == PhysicalOperatorType::RECURSIVE_KEY_CTE) { + switch (op.type) { + case PhysicalOperatorType::UNION: + case PhysicalOperatorType::RECURSIVE_CTE: + case PhysicalOperatorType::RECURSIVE_KEY_CTE: return true; + case PhysicalOperatorType::CTE_SCAN: + case PhysicalOperatorType::RECURSIVE_CTE_SCAN: + case PhysicalOperatorType::RECURSIVE_RECURRING_CTE_SCAN: + // CTE producers are not children of their scans and may forward different dictionaries. + return true; + case PhysicalOperatorType::LEFT_DELIM_JOIN: + case PhysicalOperatorType::RIGHT_DELIM_JOIN: + // The output flows out of the wrapped join, which is not among the children. + return BuildSideHasMultipleSources(op.Cast().join); + default: + break; + } + if (op.IsSink() && op.children.size() == 1) { + // Single-input sinks materialize their input and become the source of a new pipeline. + return false; } for (const auto &child : op.children) { if (BuildSideHasMultipleSources(child.get())) { @@ -314,18 +334,76 @@ static bool BuildSideHasMultipleSources(const PhysicalOperator &op) { //! Synchronisation state for the first-chunk publication of the TupleDataLayout: the canonical layout //! shared by all per-thread JHTs, plus the per-column decision on whether to narrow the row-store slot. struct LayoutGate { - mutex publish_mutex; - atomic published {false}; + //! Recreated by Reset(): the flag must be re-armed for the next recursive CTE iteration + unique_ptr publish_once = make_uniq(); shared_ptr layout_ptr; - vector dict_index_width; + //! Per narrowed build column: slot width, owned dictionary (+ NULL slot) and upstream dictionary id; shared by all + //! threads, nullptr when no column is narrowed + shared_ptr dict_info; void Reset() { - published.store(false, std::memory_order_release); + publish_once = make_uniq(); layout_ptr.reset(); - dict_index_width.clear(); + dict_info.reset(); } }; +static idx_t GetBuildSpaceRequirement(ClientContext &context, const JoinHashTable &ht, idx_t num_threads); + +//! Building the bloom filter is costly on the build side to make probing faster, +//! so only use it if there are less build tuples than probing tuples +static constexpr double BUILD_TO_PROBE_RATIO_THRESHOLD = 1.0; +//! If we have a build side without a filter, only build the bloom filter if the ratio/size is small enough +static constexpr double NON_FILTERING_RATIO_THRESHOLD = 0.1; +static constexpr idx_t NON_FILTERING_BUILD_SIDE_THRESHOLD = 4194304; +//! A join is expected to stay in memory while its estimated build side is below this share of the memory budget +static constexpr idx_t LIKELY_IN_MEMORY_BUILD_MEMORY_FRACTION = 4; + +//! Whether the build side is expected to exceed the memory budget and spill +static bool JoinIsLikelyExternal(ClientContext &context, const PhysicalHashJoin &op) { + if (Settings::Get(context)) { + return true; + } + idx_t row_width = sizeof(data_ptr_t) + sizeof(hash_t); // per-row pointer table and hash overhead + for (auto &type : op.children[1].get().GetTypes()) { + row_width += GetTypeIdSize(type.InternalType()); + } + const auto build_size = op.children[1].get().estimated_cardinality * row_width; + const auto memory_budget = BufferManager::GetBufferManager(context).GetMaxMemory(); + return build_size > memory_budget / LIKELY_IN_MEMORY_BUILD_MEMORY_FRACTION; +} + +//! Whether to accumulate a bloom filter during Sink. Only an external join needs that: its finalize only ever +//! materializes some partitions, so a filter built there would be incomplete, while every row passes through +//! Sink. An in-memory join builds its filter during finalize instead, where the actual build count and the +//! competing filter choices are known. The estimate-based checks mirror CanUseBloomFilter, which decides +//! publication once the actual build count is known. +static bool ShouldBuildBloomFilterInSink(ClientContext &context, const PhysicalHashJoin &op) { + if (!op.filter_pushdown || op.filter_pushdown->probe_info.empty()) { + return false; + } + idx_t equality_count = 0; + for (auto &cond : op.conditions) { + const auto cmp = cond.GetComparisonType(); + if (cmp == ExpressionType::COMPARE_EQUAL || cmp == ExpressionType::COMPARE_NOT_DISTINCT_FROM) { + equality_count++; + } + } + if (equality_count != 1) { + return false; + } + const auto build_estimate = static_cast(op.children[1].get().estimated_cardinality); + const auto probe_estimate = static_cast(op.children[0].get().estimated_cardinality); + if (build_estimate > BUILD_TO_PROBE_RATIO_THRESHOLD * probe_estimate) { + return false; + } + if (!op.filter_pushdown->build_side_has_filter && build_estimate > NON_FILTERING_RATIO_THRESHOLD * probe_estimate && + build_estimate > NON_FILTERING_BUILD_SIDE_THRESHOLD) { + return false; + } + return JoinIsLikelyExternal(context, op); +} + class HashJoinGlobalSinkState : public GlobalSinkState { public: HashJoinGlobalSinkState(const PhysicalHashJoin &op_p, ClientContext &context_p) @@ -334,13 +412,18 @@ class HashJoinGlobalSinkState : public GlobalSinkState { initial_radix_bits(num_threads < 100 ? 4 : 5), finalized(false), active_local_states(0), total_size(0), max_partition_size(0), max_partition_count(0), probe_side_requirement(0), scanned_data(false) { hash_table = op.InitializeHashTable(context, initial_radix_bits); + // Reserve the initial partition buffers before upstream hash tables are finalized. + auto minimum_reservation = MinValue(temporary_memory_state->GetMinimumReservation(), + GetBuildSpaceRequirement(context, *hash_table, num_threads)); + temporary_memory_state->SetMinimumReservation(minimum_reservation); + temporary_memory_state->SetRemainingSize(minimum_reservation); + temporary_memory_state->UpdateReservation(context); // For perfect hash join perfect_join_executor = make_uniq(op, *hash_table); auto use_perfect_hash = CanUsePerfectHashJoin(op, *perfect_join_executor); can_use_perfect_hash = use_perfect_hash; - // A multi-source build side (UNION ALL / recursive CTE) feeds the sink from several producers, - // disqualifying dict-surviving. Computed once from the static plan; cannot change at runtime. + // Multiple build producers can change dictionaries after the layout has been published. build_side_multi_source = BuildSideHasMultipleSources(op.children[1].get()); // For external hash join external = Settings::Get(context); @@ -355,6 +438,19 @@ class HashJoinGlobalSinkState : public GlobalSinkState { } global_filter_state = op.filter_pushdown->GetGlobalState(context, op); } + InitializeBloomFilter(); + } + + //! Creates the bloom filter that every thread-local hash table inserts into during Sink + void InitializeBloomFilter() { + // a perfect hash join publishes its own exact filter, so a bloom filter would never leave the sink + if (skip_filter_pushdown || can_use_perfect_hash || !ShouldBuildBloomFilterInSink(context, op)) { + hash_table->SetBloomFilter(nullptr); + return; + } + auto filter = make_shared_ptr(); + filter->Initialize(context, MaxValue(op.children[1].get().estimated_cardinality, 1)); + hash_table->SetBloomFilter(std::move(filter)); } ~HashJoinGlobalSinkState() override { @@ -402,8 +498,11 @@ class HashJoinGlobalSinkState : public GlobalSinkState { } global_filter_state = op.filter_pushdown->GetGlobalState(context, op); } - // Keep the published layout across CTE iterations (same upstream operator, same arrival types). - // ResetForNewIterationSinglePartition already cleared the row data and dict_registry. + InitializeBloomFilter(); + // The next iteration's first chunk publishes the layout afresh: the rerun build may deliver a different + // dictionary (or none), so neither a narrowed slot nor the dictionary it indexes into can be carried over. + // ResetForNewIterationSinglePartition returned the global JHT to the unpublished state accordingly. + layout_gate.Reset(); GlobalSinkState::Reset(context); } @@ -426,6 +525,12 @@ class HashJoinGlobalSinkState : public GlobalSinkState { //! The number of active local states atomic active_local_states; + //! The memory available to the hash table: the reservation minus what partitioning the probe side requires + idx_t GetMaxExternalHTSize() const { + const auto reservation = temporary_memory_state->GetReservation(); + return reservation > probe_side_requirement ? reservation - probe_side_requirement : 0; + } + //! Whether we are doing an external + some sizes bool external; idx_t total_size; @@ -483,6 +588,8 @@ class HashJoinLocalSinkState : public LocalSinkState { } hash_table = op.InitializeHashTable(context, gstate.hash_table->GetRadixBits()); + // every thread inserts into this one filter - InsertOne is an atomic fetch_or, so it needs no lock + hash_table->SetBloomFilter(gstate.hash_table->GetBloomFilter()); // sink_collection exists only after the layout is published on the first build chunk, so // InitializeAppendState runs lazily inside Sink. keep_hash_table = gstate.keep_local_hash_tables; @@ -520,15 +627,19 @@ class HashJoinLocalSinkState : public LocalSinkState { join_keys.Reset(); payload_chunk.Reset(); if (hash_table && append_state_initialised) { - // the layout survives the iteration; only the row data is dropped + // Drop the row data and the layout: the next iteration's first chunk republishes it, and Sink then + // reruns the lazy per-thread setup (FinishInitWithLayout + InitializeAppendState) against it. hash_table->ResetForNewIterationSinglePartition(); - hash_table->GetSinkCollection().ResetAppendState(append_state); + append_state_initialised = false; } else { // HT was moved into gstate during Combine, or never had a layout published. Rebuild against the global // radix_bits so partition counts stay consistent in PrepareFinalize. hash_table = op.InitializeHashTable(context.client, gstate.hash_table->GetRadixBits()); append_state_initialised = false; } + // the global state resets before any local one, so the filter this picks up is always a fresh one + D_ASSERT(!gstate.hash_table->GetBloomFilter() || !gstate.hash_table->GetBloomFilter()->IsSealed()); + hash_table->SetBloomFilter(gstate.hash_table->GetBloomFilter()); keep_hash_table = gstate.keep_local_hash_tables; gstate.active_local_states++; if (op.filter_pushdown) { @@ -539,27 +650,16 @@ class HashJoinLocalSinkState : public LocalSinkState { } }; -//! Map a dict-index byte width to its row-store slot type (the width is decided by GetDictSurvivingIndexWidth) -static LogicalType DictIndexType(uint8_t index_width) { - switch (index_width) { - case sizeof(uint8_t): - return LogicalType::UTINYINT; - case sizeof(uint16_t): - return LogicalType::USMALLINT; - default: - return LogicalType::UINTEGER; - } -} - //! Build the row layout [conditions, build payload, (found flag), hash], narrowing a payload column to its -//! dict-index slot when dict_index_width[col] != 0. Shared by publisher and empty-input fallback to avoid drift. +//! dict-index slot when dict_info narrows it. Shared by publisher and empty-input fallback to avoid drift. static shared_ptr BuildJoinLayout(const vector &cond_types, const vector &build_types, JoinType join_type, - const vector &dict_index_width) { + optional_ptr dict_info) { vector layout_types(cond_types); for (idx_t col = 0; col < build_types.size(); col++) { - if (col < dict_index_width.size() && dict_index_width[col] != 0) { - layout_types.emplace_back(DictIndexType(dict_index_width[col])); + auto dict_column = dict_info ? dict_info->Get(col) : nullptr; + if (dict_column) { + layout_types.emplace_back(DictSurvivingInfo::IndexType(dict_column->index_width)); } else { layout_types.emplace_back(build_types[col]); } @@ -579,8 +679,10 @@ static bool CanUseDictSurvivingJoin(const PhysicalHashJoin &op, const JoinHashTa bool build_side_multi_source) { // external is safe here: the dictionary is an in-memory self-owned copy and the index is a plain row-store // column, so a spill/repartition preserves both (unlike the pointer-embedding dict-emission/compressed-probe paths) - // a multi-source build can deliver a later chunk flat or as a different dictionary under the - // already-narrowed slot, so disqualify the whole join (see BuildSideHasMultipleSources) + // a multi-source build (UNION ALL, CTE) can deliver a later chunk flat or as a different dictionary under the + // already-narrowed slot, so disqualify the whole join (see BuildSideHasMultipleSources). A FULL/RIGHT join in + // the build subtree is fine: its second phase only ever delivers NULL-only probe columns, which the reserved + // NULL index of the owned dictionary encodes. if (build_side_multi_source) { return false; } @@ -609,50 +711,41 @@ static bool CanUseDictSurvivingJoin(const PhysicalHashJoin &op, const JoinHashTa } bool HashJoinGlobalSinkState::DictSurvivingActive() const { - if (!hash_table) { - return false; - } - for (const auto &entry : hash_table->dict_registry) { - if (entry) { - return true; - } - } - return false; + return hash_table && hash_table->HasDictSurvivingColumns(); } void HashJoinGlobalSinkState::PublishLayoutIfFirst(HashJoinLocalSinkState &lstate, DataChunk &payload_chunk) { - if (layout_gate.published.load(std::memory_order_acquire)) { - return; - } - unique_lock guard(layout_gate.publish_mutex); - if (layout_gate.published.load(std::memory_order_relaxed)) { - return; - } - - const auto &cond_types = lstate.hash_table->condition_types; - const auto &build_types = lstate.hash_table->build_types; - layout_gate.dict_index_width.assign(build_types.size(), 0); - - if (CanUseDictSurvivingJoin(op, *lstate.hash_table, can_use_perfect_hash, build_side_multi_source)) { - // Per-column width decision lives on the JHT (GetDictSurvivingIndexWidth); feed it each arriving vector. - for (idx_t col = 0; col < build_types.size(); col++) { - if (col >= payload_chunk.ColumnCount()) { - continue; + CallOnce(*layout_gate.publish_once, [&] { + const auto &cond_types = lstate.hash_table->condition_types; + const auto &build_types = lstate.hash_table->build_types; + + shared_ptr dict_info; + if (CanUseDictSurvivingJoin(op, *lstate.hash_table, can_use_perfect_hash, build_side_multi_source)) { + // Per-column width decision lives on the JHT (GetDictSurvivingIndexWidth); feed it each arriving vector. + // The dictionary a narrowed slot indexes into is created here, once, and handed to every thread with the + // layout: a later chunk may carry no dictionary at all (NULL-only), so no thread can pin it lazily. + DictSurvivingInfo info; + info.columns.resize(build_types.size()); + for (idx_t col = 0; col < MinValue(build_types.size(), payload_chunk.ColumnCount()); col++) { + auto &incoming = payload_chunk.data[col]; + const auto index_width = lstate.hash_table->GetDictSurvivingIndexWidth(col, incoming); + if (index_width != 0) { + info.columns[col] = JoinHashTable::CreateDictSurvivingColumn(incoming, index_width); + } + } + if (info.Any()) { + dict_info = make_shared_ptr(std::move(info)); } - layout_gate.dict_index_width[col] = - lstate.hash_table->GetDictSurvivingIndexWidth(col, payload_chunk.data[col]); } - } - - auto layout = BuildJoinLayout(cond_types, build_types, lstate.hash_table->join_type, layout_gate.dict_index_width); - layout_gate.layout_ptr = layout; - // global HT receives the same layout so Merge/Combine and finalize-time scans operate against it - if (hash_table && !hash_table->IsLayoutFinalized()) { - hash_table->FinishInitWithLayout(layout, layout_gate.dict_index_width); - } - - layout_gate.published.store(true, std::memory_order_release); + auto layout = BuildJoinLayout(cond_types, build_types, lstate.hash_table->join_type, dict_info.get()); + // global HT receives the same layout so Merge/Combine and finalize-time scans operate against it + if (hash_table && !hash_table->IsLayoutFinalized()) { + hash_table->FinishInitWithLayout(layout, dict_info); + } + layout_gate.layout_ptr = std::move(layout); + layout_gate.dict_info = std::move(dict_info); + }); } unique_ptr PhysicalHashJoin::InitializeHashTable(ClientContext &context, @@ -683,17 +776,17 @@ unique_ptr PhysicalHashJoin::InitializeHashTable(ClientContext &c // we need a count_star and a count to get counts with and without NULLs FunctionBinder function_binder(context); - aggr = function_binder.BindAggregateFunction(CountStarFun::GetFunction(), {}, nullptr, - AggregateType::NON_DISTINCT); + aggr = function_binder.BindAggregateFunction(GetBuiltinAggregateFunction(context, CountStarFun::Name, {}), + {}, nullptr, AggregateType::NON_DISTINCT); correlated_aggregates.emplace_back(*aggr); delim_payload_types.push_back(aggr->GetReturnType()); info.correlated_aggregates.push_back(std::move(aggr)); - auto count_fun = CountFunctionBase::GetFunction(); + auto count_fun = GetBuiltinAggregateFunction(context, CountFun::Name, {LogicalType::BIGINT}); vector> children; // this is a dummy but we need it to make the hash table understand whats going on - children.push_back(make_uniq_base(count_fun.GetReturnType(), 0U)); - aggr = function_binder.BindAggregateFunction(count_fun, std::move(children), nullptr, + children.push_back(make_uniq_base(count_fun->GetReturnType(), 0U)); + aggr = function_binder.BindAggregateFunction(std::move(count_fun), std::move(children), nullptr, AggregateType::NON_DISTINCT); correlated_aggregates.emplace_back(*aggr); delim_payload_types.push_back(aggr->GetReturnType()); @@ -784,7 +877,7 @@ SinkResultType PhysicalHashJoin::Sink(ExecutionContext &context, DataChunk &chun // lazy per-thread setup against the published layout if (!lstate.append_state_initialised) { - lstate.hash_table->FinishInitWithLayout(gstate.layout_gate.layout_ptr, gstate.layout_gate.dict_index_width); + lstate.hash_table->FinishInitWithLayout(gstate.layout_gate.layout_ptr, gstate.layout_gate.dict_info); lstate.hash_table->GetSinkCollection().InitializeAppendState(lstate.append_state); lstate.append_state_initialised = true; } @@ -902,26 +995,26 @@ static idx_t GetPartitioningSpaceRequirement(ClientContext &context, const vecto return num_threads * num_partitions * size_per_partition; } +static idx_t GetBuildSpaceRequirement(ClientContext &context, const JoinHashTable &ht, idx_t num_threads) { + auto layout = BuildJoinLayout(ht.condition_types, ht.build_types, ht.join_type, {}); + return GetPartitioningSpaceRequirement(context, layout->GetTypes(), ht.GetRadixBits(), num_threads); +} + void PhysicalHashJoin::PrepareFinalize(ClientContext &context, GlobalSinkState &global_state) const { auto &gstate = global_state.Cast(); // If no Sink chunk ever arrived, the layout was never published. Fall back to a default layout // (all columns at their native width) so finalize-time scans can dereference data_collection. - if (!gstate.layout_gate.published.load(std::memory_order_acquire)) { - unique_lock guard(gstate.layout_gate.publish_mutex); - if (!gstate.layout_gate.published.load(std::memory_order_relaxed)) { - const auto &cond_types = gstate.hash_table->condition_types; - const auto &build_types = gstate.hash_table->build_types; - gstate.layout_gate.dict_index_width.assign(build_types.size(), 0); - // all-zero dict_index_width => BuildJoinLayout keeps every build column at its native width - auto layout = BuildJoinLayout(cond_types, build_types, gstate.hash_table->join_type, - gstate.layout_gate.dict_index_width); - gstate.layout_gate.layout_ptr = layout; - if (!gstate.hash_table->IsLayoutFinalized()) { - gstate.hash_table->FinishInitWithLayout(layout); - } - gstate.layout_gate.published.store(true, std::memory_order_release); - } - } + CallOnce(*gstate.layout_gate.publish_once, [&] { + const auto &cond_types = gstate.hash_table->condition_types; + const auto &build_types = gstate.hash_table->build_types; + // no dict_info => BuildJoinLayout keeps every build column at its native width + gstate.layout_gate.dict_info.reset(); + auto layout = BuildJoinLayout(cond_types, build_types, gstate.hash_table->join_type, nullptr); + if (!gstate.hash_table->IsLayoutFinalized()) { + gstate.hash_table->FinishInitWithLayout(layout); + } + gstate.layout_gate.layout_ptr = std::move(layout); + }); const auto &ht = *gstate.hash_table; gstate.total_size = @@ -949,26 +1042,17 @@ struct PrefixRangeBuildLane { idx_t task_count = 0; }; -static void ExecuteHashJoinFinalizeTask(HashJoinGlobalSinkState &sink, optional_idx partition_idx, - optional_ptr prefix_range_lane) { - const auto &data_collection = sink.hash_table->GetDataCollection(); +static void ExecuteHashJoinFinalizeTask(HashJoinGlobalSinkState &sink, const vector> &chunk_ranges, + bool parallel, optional_ptr prefix_range_lane) { optional_ptr prefix_range_state; bool prefix_range_parallel = false; if (prefix_range_lane) { prefix_range_state = *prefix_range_lane->state; prefix_range_parallel = prefix_range_lane->task_count > 1; } - if (!partition_idx.IsValid() || sink.hash_table->GetRadixBits() == 0) { - // Unpartitioned builds still finalize over the full chunk range even if the scheduler created a - // single "partition 0" task, because tuple-data segments are not tagged with partition ids there. - sink.hash_table->Finalize(0U, data_collection.ChunkCount(), false, prefix_range_state, prefix_range_parallel); - } else { - // Parallel finalize - each thread processes one partition - const auto chunk_ranges = data_collection.GetChunkRangesForPartition(partition_idx.GetIndex()); - for (auto &chunk_range : chunk_ranges) { - sink.hash_table->Finalize(chunk_range.first, chunk_range.second, true, prefix_range_state, - prefix_range_parallel); - } + for (auto &chunk_range : chunk_ranges) { + sink.hash_table->Finalize(chunk_range.first, chunk_range.second, parallel, prefix_range_state, + prefix_range_parallel); } } @@ -1042,14 +1126,15 @@ class HashJoinTableInitEvent : public BasePipelineEvent { class HashJoinFinalizeTask : public ExecutorTask { public: - HashJoinFinalizeTask(HashJoinGlobalSinkState &sink_p, shared_ptr event, optional_idx partition_idx_p, + HashJoinFinalizeTask(HashJoinGlobalSinkState &sink_p, shared_ptr event, + vector> chunk_ranges_p, bool parallel_p, optional_ptr prefix_range_lane_p = nullptr) - : ExecutorTask(sink_p.context, std::move(event), sink_p.op), sink(sink_p), partition_idx(partition_idx_p), - prefix_range_lane(prefix_range_lane_p) { + : ExecutorTask(sink_p.context, std::move(event), sink_p.op), sink(sink_p), + chunk_ranges(std::move(chunk_ranges_p)), parallel(parallel_p), prefix_range_lane(prefix_range_lane_p) { } TaskExecutionResult ExecuteTask(TaskExecutionMode mode) override { - ExecuteHashJoinFinalizeTask(sink, partition_idx, prefix_range_lane); + ExecuteHashJoinFinalizeTask(sink, chunk_ranges, parallel, prefix_range_lane); event->FinishTask(); return TaskExecutionResult::TASK_FINISHED; } @@ -1059,7 +1144,8 @@ class HashJoinFinalizeTask : public ExecutorTask { private: HashJoinGlobalSinkState &sink; - optional_idx partition_idx; + vector> chunk_ranges; + bool parallel; optional_ptr prefix_range_lane; }; @@ -1074,31 +1160,35 @@ class HashJoinFinalizeEvent : public BasePipelineEvent { public: vector> GetTasks() { auto &ht = *sink.hash_table; - vector finalize_partitions; - if (FinalizeSingleThreaded(sink, false)) { - finalize_partitions.emplace_back(); + auto &data_collection = ht.GetDataCollection(); + const bool parallel = !FinalizeSingleThreaded(sink, false) && ht.GetRadixBits() != 0; + vector>> task_ranges; + if (!parallel) { + task_ranges.push_back({{0, data_collection.ChunkCount()}}); } else { const auto num_partitions = RadixPartitioning::NumberOfPartitions(ht.GetRadixBits()); const auto ¤t_partitions = ht.GetCurrentPartitions(); + const auto max_tasks_per_partition = MaxValue(sink.num_threads / num_partitions, 1); for (idx_t partition_idx = 0; partition_idx < num_partitions; partition_idx++) { if (sink.external && !current_partitions.RowIsValidUnsafe(partition_idx)) { - continue; // Partition is not being built on + continue; } - finalize_partitions.emplace_back(partition_idx); + SplitPartitionRanges(data_collection.GetChunkRangesForPartition(partition_idx), max_tasks_per_partition, + task_ranges); } } - InitializePrefixRangeLanes(ht, finalize_partitions.size()); + InitializePrefixRangeLanes(ht, task_ranges.size()); vector> finalize_tasks; - finalize_tasks.reserve(finalize_partitions.size()); - for (idx_t task_idx = 0; task_idx < finalize_partitions.size(); task_idx++) { + finalize_tasks.reserve(task_ranges.size()); + for (idx_t task_idx = 0; task_idx < task_ranges.size(); task_idx++) { optional_ptr lane; if (!prefix_range_lanes.empty()) { lane = *prefix_range_lanes[task_idx % prefix_range_lanes.size()]; lane->task_count++; } - finalize_tasks.push_back( - make_uniq(sink, shared_from_this(), finalize_partitions[task_idx], lane)); + finalize_tasks.push_back(make_uniq(sink, shared_from_this(), + std::move(task_ranges[task_idx]), parallel, lane)); } return finalize_tasks; } @@ -1111,7 +1201,7 @@ class HashJoinFinalizeEvent : public BasePipelineEvent { lane = *prefix_range_lanes[0]; lane->task_count = 1; } - ExecuteHashJoinFinalizeTask(sink, optional_idx(), lane); + ExecuteHashJoinFinalizeTask(sink, {{0, ht.GetDataCollection().ChunkCount()}}, false, lane); FinishTasks(); } @@ -1126,6 +1216,33 @@ class HashJoinFinalizeEvent : public BasePipelineEvent { static constexpr idx_t CHUNKS_PER_TASK = 64; private: + static void SplitPartitionRanges(vector> ranges, idx_t max_tasks, + vector>> &task_ranges) { + if (max_tasks == 1) { + task_ranges.push_back(std::move(ranges)); + return; + } + idx_t partition_chunks = 0; + for (const auto &range : ranges) { + partition_chunks += range.second - range.first; + } + const auto chunks_per_task = MaxValue(CHUNKS_PER_TASK, (partition_chunks + max_tasks - 1) / max_tasks); + // A task can span disjoint ranges within the partition. + idx_t remaining = 0; + for (auto range : ranges) { + while (range.first < range.second) { + if (remaining == 0) { + task_ranges.emplace_back(); + remaining = chunks_per_task; + } + const auto count = MinValue(range.second - range.first, remaining); + task_ranges.back().emplace_back(range.first, range.first + count); + range.first += count; + remaining -= count; + } + } + } + void FinishTasks() { for (auto &lane : prefix_range_lanes) { sink.hash_table->MergePrefixRangeBuildState(*lane->state); @@ -1140,6 +1257,9 @@ class HashJoinFinalizeEvent : public BasePipelineEvent { } if (sink.global_filter_state) { + // a finalize-built bloom filter is complete once the tasks are done; a sink-built one is + // already sealed and this is a no-op + sink.hash_table->SealBloomFilter(); PublishDeferredRuntimeFilters(sink.context, *sink.hash_table, *sink.global_filter_state); } sink.hash_table->finalized = true; @@ -1191,6 +1311,24 @@ void HashJoinGlobalSinkState::InitializeProbeSpill() { } } +//! Computes and registers the runtime filters of an external join. Both external paths call this exactly once, +//! after the whole build side has been merged into the global hash table and before the finalize event is +//! scheduled. Every input is accumulated during Sink, so it does not matter which partitions are materialized. +static void FinalizeExternalRuntimeFilters(ClientContext &context, const PhysicalHashJoin &op, + HashJoinGlobalSinkState &sink) { + if (!op.filter_pushdown || sink.skip_filter_pushdown) { + return; + } + auto &ht = *sink.hash_table; + if (ht.GetSinkCollection().Count() == 0) { + return; + } + auto filter_min_max = op.filter_pushdown->FinalizeMinMax(*sink.global_filter_state); + // prefix-range filters are built from the materialized hash table, which only ever holds one round + op.filter_pushdown->FinalizeFilters(context, op, std::move(filter_min_max), &ht, true, false, + sink.global_filter_state.get()); +} + class HashJoinRepartitionTask : public ExecutorTask { public: HashJoinRepartitionTask(shared_ptr event_p, ClientContext &context, JoinHashTable &global_ht, @@ -1285,9 +1423,9 @@ class HashJoinRepartitionEvent : public BasePipelineEvent { sink.probe_side_requirement); sink.temporary_memory_state->UpdateReservation(executor.context); - D_ASSERT(sink.temporary_memory_state->GetReservation() >= sink.probe_side_requirement); - sink.hash_table->PrepareExternalFinalize(sink.temporary_memory_state->GetReservation() - - sink.probe_side_requirement); + FinalizeExternalRuntimeFilters(sink.context, op, sink); + + sink.hash_table->PrepareExternalFinalize(sink.GetMaxExternalHTSize()); sink.ScheduleFinalize(*pipeline, *this); } }; @@ -1298,8 +1436,9 @@ bool JoinFilterPushdownInfo::CanUseInFilter(const ClientContext &context, option return ht && ht->Count() > 1 && ht->Count() <= dynamic_or_filter_threshold && cmp == ExpressionType::COMPARE_EQUAL; } -bool JoinFilterPushdownInfo::CanUseBloomFilter(const ClientContext &context, const PhysicalComparisonJoin &op, - const ExpressionType &cmp, optional_ptr ht) const { +bool JoinFilterPushdownInfo::CanUseBloomFilter(const JoinFilterPushdownFilter &info, const ClientContext &context, + const PhysicalComparisonJoin &op, const ExpressionType &cmp, + optional_ptr ht) const { if (!ht) { return false; } @@ -1322,22 +1461,20 @@ bool JoinFilterPushdownInfo::CanUseBloomFilter(const ClientContext &context, con return false; } - // building the bloom filter is costly on the build to make probing faster, - // so only use it if there are less build tuples than probing tuples - static constexpr double BUILD_TO_PROBE_RATIO_THRESHOLD = 1.0; auto build_count = ht->Count(); if (build_count == 0) { build_count = ht->GetSinkCollection().Count(); } - const double build_to_probe_ratio = - static_cast(build_count) / static_cast(op.children[0].get().estimated_cardinality); + auto probe_cardinality = op.children[0].get().estimated_cardinality; + if (build_side_has_filter) { + probe_cardinality = MaxValue(info.estimated_scan_cardinality, probe_cardinality); + } + const double build_to_probe_ratio = static_cast(build_count) / static_cast(probe_cardinality); if (build_to_probe_ratio > BUILD_TO_PROBE_RATIO_THRESHOLD) { return false; } // if we have a build side without a filter, only build the bloom filter if the ratio/size is small enough - static constexpr double NON_FILTERING_RATIO_THRESHOLD = 0.1; - static constexpr idx_t NON_FILTERING_BUILD_SIDE_THRESHOLD = 4194304; if (!build_side_has_filter && build_to_probe_ratio > NON_FILTERING_RATIO_THRESHOLD && build_count > NON_FILTERING_BUILD_SIDE_THRESHOLD) { return false; @@ -1346,9 +1483,10 @@ bool JoinFilterPushdownInfo::CanUseBloomFilter(const ClientContext &context, con return true; } -bool JoinFilterPushdownInfo::CanUsePrefixRangeFilter(const ClientContext &context, const PhysicalComparisonJoin &op, - optional_ptr ht, const ExpressionType &cmp) const { - if (!CanUseBloomFilter(context, op, cmp, ht)) { +bool JoinFilterPushdownInfo::CanUsePrefixRangeFilter(const JoinFilterPushdownFilter &info, const ClientContext &context, + const PhysicalComparisonJoin &op, optional_ptr ht, + const ExpressionType &cmp) const { + if (!CanUseBloomFilter(info, context, op, cmp, ht)) { return false; } if (cmp != ExpressionType::COMPARE_EQUAL) { @@ -1488,13 +1626,15 @@ static unique_ptr CreateRuntimeFilterExpression(ClientContext &conte unique_ptr filter_expr; switch (deferred.type) { case DeferredRuntimeFilterType::BLOOM_FILTER: { - D_ASSERT(ht.GetBloomFilter().IsInitialized()); - if (!ht.GetBloomFilter().IsInitialized()) { + // a filter that is not sealed does not describe the complete build side - publishing it would drop + // rows - and a filter that saturated is not worth the hashing it costs the scan + if (!ht.HasPublishableBloomFilter()) { return nullptr; } + auto bloom_filter = ht.GetBloomFilter(); filter_expr = make_uniq( BoundScalarFunction(BloomFilterScalarFun::GetFunction(filter_input_type)), std::move(children), - make_uniq(ht.GetBloomFilter(), filters_null_values, key_name, key_type, + make_uniq(std::move(bloom_filter), filters_null_values, key_name, key_type, selectivity_threshold, n_vectors_to_check)); break; } @@ -1772,7 +1912,7 @@ unique_ptr JoinFilterPushdownInfo::FinalizeFilters(ClientContext &con const auto can_compute_span = PrefixRangeFilter::TryComputeSpan(min_val_before_cast, max_val_before_cast, span); const auto can_emit_prf = allow_prefix_range_filters && can_emit_runtime_filters && gstate && - CanUsePrefixRangeFilter(context, op, ht, cmp) && can_compute_span; + CanUsePrefixRangeFilter(info, context, op, ht, cmp) && can_compute_span; bool pushed_in_filter = false; if (CanUseInFilter(context, ht, cmp)) { @@ -1807,10 +1947,18 @@ unique_ptr JoinFilterPushdownInfo::FinalizeFilters(ClientContext &con max_val, condition_type, reconstruct_filter_expression, false); } if (allow_bloom_filters && can_emit_runtime_filters && ht && gstate && - CanUseBloomFilter(context, op, cmp, ht)) { - ht->SetBuildBloomFilter(true); - DeferRuntimeFilter(DeferredRuntimeFilterType::BLOOM_FILTER, op, info, pushdown_column, - filter_col_idx, *gstate); + CanUseBloomFilter(info, context, op, cmp, ht)) { + if (ht->HasPublishableBloomFilter()) { + // a complete filter was accumulated during Sink (external joins build it there) + DeferRuntimeFilter(DeferredRuntimeFilterType::BLOOM_FILTER, op, info, pushdown_column, + filter_col_idx, *gstate); + } else if (allow_prefix_range_filters) { + // like a prefix-range filter, a bloom filter may be built during finalize when the + // finalize materializes the whole build side - an external finalize does not + ht->SetBuildBloomFilter(true); + DeferRuntimeFilter(DeferredRuntimeFilterType::BLOOM_FILTER, op, info, pushdown_column, + filter_col_idx, *gstate); + } } } } @@ -1830,6 +1978,9 @@ SinkFinalizeType PhysicalHashJoin::Finalize(Pipeline &pipeline, Event &event, Cl auto &sink = input.global_state.Cast(); auto &ht = *sink.hash_table; + // the filter covers the whole build side once Sink is done + ht.SealBloomFilter(); + sink.temporary_memory_state->UpdateReservation(context); sink.external = sink.temporary_memory_state->GetReservation() < sink.total_size; if (sink.external) { @@ -1870,8 +2021,7 @@ SinkFinalizeType PhysicalHashJoin::Finalize(Pipeline &pipeline, Event &event, Cl (max_partition_ht_size + sink.probe_side_requirement) > sink.temporary_memory_state->GetReservation()) { // We have to repartition const auto radix_bits_before = ht.GetRadixBits(); - ht.SetRepartitionRadixBits(sink.temporary_memory_state->GetReservation(), sink.max_partition_size, - sink.max_partition_count); + ht.SetRepartitionRadixBits(sink.GetMaxExternalHTSize(), sink.max_partition_size, sink.max_partition_count); DUCKDB_LOG(context, PhysicalOperatorLogType, *this, "PhysicalHashJoin", "Repartition", {{"partitions_before", to_string(RadixPartitioning::NumberOfPartitions(radix_bits_before))}, {"partitions_after", to_string(RadixPartitioning::NumberOfPartitions(ht.GetRadixBits()))}}); @@ -1884,15 +2034,8 @@ SinkFinalizeType PhysicalHashJoin::Finalize(Pipeline &pipeline, Event &event, Cl } sink.local_hash_tables.clear(); sink.owned_local_hash_tables.clear(); - if (filter_pushdown && !sink.skip_filter_pushdown && ht.GetSinkCollection().Count() > 0) { - auto filter_min_max = filter_pushdown->FinalizeMinMax(*sink.global_filter_state); - filter_pushdown->FinalizeFilters(context, *this, std::move(filter_min_max), &ht, true, false, - sink.global_filter_state.get()); - } - ht.PrepareBloomFilterForFinalize(); - D_ASSERT(sink.temporary_memory_state->GetReservation() >= sink.probe_side_requirement); - sink.hash_table->PrepareExternalFinalize(sink.temporary_memory_state->GetReservation() - - sink.probe_side_requirement); + FinalizeExternalRuntimeFilters(context, *this, sink); + sink.hash_table->PrepareExternalFinalize(sink.GetMaxExternalHTSize()); sink.ScheduleFinalize(pipeline, event); } sink.finalized = true; @@ -1946,7 +2089,6 @@ SinkFinalizeType PhysicalHashJoin::Finalize(Pipeline &pipeline, Event &event, Cl // In case of a large build side or duplicates, use regular hash join if (!use_perfect_hash) { - ht.PrepareBloomFilterForFinalize(); sink.ScheduleFinalize(pipeline, event); } sink.finalized = true; @@ -2199,6 +2341,8 @@ class HashJoinGlobalSourceState : public GlobalSourceState { //! For probe synchronization atomic probe_chunk_count; atomic probe_chunk_done; + //! Last reported progress, also used while partition masks are being updated + MonotonicProgress external_progress; //! To determine the number of threads idx_t probe_count; @@ -2208,6 +2352,8 @@ class HashJoinGlobalSourceState : public GlobalSourceState { idx_t full_outer_chunk_idx = DConstants::INVALID_INDEX; atomic full_outer_chunk_count; atomic full_outer_chunk_done; + //! Chunks of the full/outer scan that have been scanned, updated while scanning (for progress) + atomic full_outer_chunk_progress; idx_t full_outer_chunks_per_thread = DConstants::INVALID_INDEX; vector blocked_tasks; @@ -2221,11 +2367,13 @@ class HashJoinGlobalSourceState : public GlobalSourceState { build_chunks_per_thread = DConstants::INVALID_INDEX; probe_chunk_count = 0; probe_chunk_done = 0; + external_progress.Reset(); probe_count = op.children[0].get().estimated_cardinality; parallel_scan_chunk_count = context.config.verify_parallelism ? 1 : 120; full_outer_chunk_idx = DConstants::INVALID_INDEX; full_outer_chunk_count = 0; full_outer_chunk_done = 0; + full_outer_chunk_progress = 0; full_outer_chunks_per_thread = DConstants::INVALID_INDEX; blocked_tasks.clear(); GlobalSourceState::Reset(context); @@ -2281,6 +2429,8 @@ class HashJoinLocalSourceState : public LocalSourceState { idx_t full_outer_chunk_idx_from = DConstants::INVALID_INDEX; idx_t full_outer_chunk_idx_to = DConstants::INVALID_INDEX; unique_ptr full_outer_scan_state; + //! Chunks of the current full/outer scan that were added to the global progress + idx_t full_outer_chunks_reported = 0; private: void ResetState() { @@ -2301,6 +2451,7 @@ class HashJoinLocalSourceState : public LocalSourceState { full_outer_chunk_idx_from = DConstants::INVALID_INDEX; full_outer_chunk_idx_to = DConstants::INVALID_INDEX; full_outer_scan_state.reset(); + full_outer_chunks_reported = 0; } public: @@ -2384,9 +2535,7 @@ void HashJoinGlobalSourceState::PrepareBuild(HashJoinGlobalSinkState &sink) { sink.probe_side_requirement); // Try to put the next partitions in the block collection of the HT - D_ASSERT(!sink.external || sink.temporary_memory_state->GetReservation() >= sink.probe_side_requirement); - if (!sink.external || - !ht.PrepareExternalFinalize(sink.temporary_memory_state->GetReservation() - sink.probe_side_requirement)) { + if (!sink.external || !ht.PrepareExternalFinalize(sink.GetMaxExternalHTSize())) { global_stage = HashJoinSourceStage::DONE; sink.temporary_memory_state->SetZero(); return; @@ -2441,6 +2590,7 @@ void HashJoinGlobalSourceState::PrepareScanHT(HashJoinGlobalSinkState &sink) { full_outer_chunk_idx = 0; full_outer_chunk_count = data_collection.ChunkCount(); full_outer_chunk_done = 0; + full_outer_chunk_progress = 0; full_outer_chunks_per_thread = MaxValue((full_outer_chunk_count + sink.num_threads - 1) / sink.num_threads, 1); @@ -2451,7 +2601,7 @@ void HashJoinGlobalSourceState::PrepareScanHT(HashJoinGlobalSinkState &sink) { bool HashJoinGlobalSourceState::AssignTask(HashJoinGlobalSinkState &sink, HashJoinLocalSourceState &lstate) { D_ASSERT(lstate.TaskFinished()); - annotated_lock_guard guard(lock); + annotated_unique_lock guard(lock); switch (global_stage.load()) { case HashJoinSourceStage::BUILD: if (build_chunk_idx != build_chunk_count) { @@ -2468,7 +2618,10 @@ bool HashJoinGlobalSourceState::AssignTask(HashJoinGlobalSinkState &sink, HashJo lstate.empty_ht_probe_in_progress = false; return true; } - break; + guard.unlock(); + // Only touch local handles after unlocking: another thread may replace the consumer. + lstate.probe_local_scan.current_chunk_state.handles.clear(); + return false; case HashJoinSourceStage::SCAN_HT: if (full_outer_chunk_idx != full_outer_chunk_count) { lstate.local_stage = global_stage; @@ -2606,9 +2759,17 @@ void HashJoinLocalSourceState::ExternalScanHT(HashJoinGlobalSinkState &sink, Has if (!full_outer_scan_state) { full_outer_scan_state = make_uniq(sink.hash_table->GetDataCollection(), full_outer_chunk_idx_from, full_outer_chunk_idx_to); + full_outer_chunks_reported = 0; } sink.hash_table->ScanFullOuter(*full_outer_scan_state, addresses, chunk); + auto chunks_scanned = + chunk.size() == 0 ? full_outer_chunk_idx_to - full_outer_chunk_idx_from : full_outer_scan_state->chunks_done; + if (chunks_scanned > full_outer_chunks_reported) { + gstate.full_outer_chunk_progress.fetch_add(chunks_scanned - full_outer_chunks_reported, + std::memory_order_relaxed); + full_outer_chunks_reported = chunks_scanned; + } if (chunk.size() == 0) { full_outer_scan_state = nullptr; annotated_lock_guard guard(gstate.lock); @@ -2667,20 +2828,25 @@ ProgressData PhysicalHashJoin::GetProgress(ClientContext &context, GlobalSourceS if (!sink.external) { if (PropagatesBuildSide(join_type)) { - res.done = static_cast(gstate.full_outer_chunk_done); + res.done = static_cast(gstate.full_outer_chunk_progress.load(std::memory_order_relaxed)); res.total = static_cast(gstate.full_outer_chunk_count); return res; } - res.done = 0.0; + res.done = gstate.global_stage == HashJoinSourceStage::DONE ? 1.0 : 0.0; res.total = 1.0; return res; } const auto &ht = *sink.hash_table; - const auto num_partitions = static_cast(RadixPartitioning::NumberOfPartitions(ht.GetRadixBits())); + res.total = static_cast(RadixPartitioning::NumberOfPartitions(ht.GetRadixBits())); + + // Progress holds the executor lock, which workers may acquire while holding this lock. + if (!gstate.lock.try_lock()) { + return gstate.external_progress.Update(res); + } + annotated_lock_guard guard(gstate.lock, std::adopt_lock); res.done = static_cast(ht.FinishedPartitionCount()); - res.total = num_partitions; const auto probe_chunk_done = static_cast(gstate.probe_chunk_done); const auto probe_chunk_count = static_cast(gstate.probe_chunk_count); @@ -2693,7 +2859,7 @@ ProgressData PhysicalHashJoin::GetProgress(ClientContext &context, GlobalSourceS res.done += probe_progress; } - return res; + return gstate.external_progress.Update(res); } InsertionOrderPreservingMap PhysicalHashJoin::ParamsToString() const { diff --git a/src/duckdb/src/execution/operator/join/physical_iejoin.cpp b/src/duckdb/src/execution/operator/join/physical_iejoin.cpp index 4aedf9e2a..0a525e3c2 100644 --- a/src/duckdb/src/execution/operator/join/physical_iejoin.cpp +++ b/src/duckdb/src/execution/operator/join/physical_iejoin.cpp @@ -1,3 +1,4 @@ +#include "duckdb/execution/ie_join_union.hpp" #include "duckdb/execution/operator/join/physical_iejoin.hpp" #include "duckdb/common/atomic.hpp" @@ -335,6 +336,8 @@ class IEJoinGlobalSourceState : public GlobalSourceState { atomic stopped; //! The number of completed tasks for each stage array, static_cast(IEJoinSourceStage::DONE) + 1> completed; + //! The progress of the tasks + TaskProgress task_progress; //! L1 unique_ptr l1; @@ -361,479 +364,6 @@ class IEJoinGlobalSourceState : public GlobalSourceState { bool TryNextTask(Task &task); }; -template -class IEJoinCursor { -public: - explicit IEJoinCursor(ColumnDataCollection &collection) : collection(collection) { - collection.InitializeScan(state); - collection.InitializeScanChunk(state, chunk); - } - - //! The row count of the paged collection - idx_t size() const { // NOLINT - return collection.Count(); - } - - //! Read a typed cell - const T &operator[](idx_t row_idx) { - auto index = Seek(row_idx); - const auto &source = chunk.data[0]; - const auto data_ptr = reinterpret_cast(FlatVector::GetData(source)); - return data_ptr[index]; - } - -private: - //! Is the scan in range? - inline bool RowIsVisible(idx_t row_idx) const { - return (row_idx < state.next_row_index && state.current_row_index <= row_idx); - } - //! The offset of the row in the given state - inline sel_t RowOffset(idx_t row_idx) const { - D_ASSERT(RowIsVisible(row_idx)); - return UnsafeNumericCast(row_idx - state.current_row_index); - } - //! Scan the next chunk - inline bool Scan() { - return collection.Scan(state, chunk); - } - //! Seek to the given row - inline idx_t Seek(idx_t row_idx) { - if (!RowIsVisible(row_idx)) { - collection.Seek(row_idx, state, chunk); - } - return RowOffset(row_idx); - } - - //! The pageable data - const ColumnDataCollection &collection; - //! The state used for reading the collection - ColumnDataScanState state; - //! The data chunk read into - DataChunk chunk; -}; - -struct IEJoinUnion { - using SortedTable = PhysicalRangeJoin::GlobalSortedTable; - using ChunkRange = std::pair; - - // Comparison utilities - static bool IsStrictComparison(ExpressionType comparison) { - switch (comparison) { - case ExpressionType::COMPARE_LESSTHAN: - case ExpressionType::COMPARE_GREATERTHAN: - return true; - case ExpressionType::COMPARE_LESSTHANOREQUALTO: - case ExpressionType::COMPARE_GREATERTHANOREQUALTO: - return false; - default: - throw InternalException("Unimplemented comparison type for IEJoin!"); - } - } - - template - static inline bool Compare(const T &lhs, const T &rhs, const bool strict) { - const bool less_than = lhs < rhs; - if (!less_than && !strict) { - return !(rhs < lhs); - } - return less_than; - } - - static idx_t AppendKey(ExecutionContext &context, InterruptState &interrupt, SortedTable &table, - ExpressionExecutor &executor, SortedTable &marked, int64_t increment, int64_t rid, - const ChunkRange &range); - - static unique_ptr ExtractColumn(SortedTable &table, idx_t col_idx, - BufferManager &buffer_manager) { - auto &collection = *table.sorted->payload_data; - vector scan_ids(1, col_idx); - TupleDataScanState scan_state; - collection.InitializeScan(scan_state, scan_ids); - - DataChunk payload; - collection.InitializeScanChunk(scan_state, payload); - - auto result = make_uniq(buffer_manager, payload.GetTypes()); - ColumnDataAppendState append_state; - result->InitializeAppend(append_state); - while (collection.Scan(scan_state, payload)) { - result->Append(append_state, payload); - } - - return result; - } - - class UnionIterator { - public: - UnionIterator(SortedTable &table, bool strict) : state(table.CreateIteratorState()), strict(strict) { - } - - inline idx_t GetIndex() const { - return index; - } - - inline void SetIndex(idx_t i) { - index = i; - } - - inline idx_t GetChunkIndex() const { - idx_t chunk_idx; - idx_t tuple_idx; - state->RandomAccess(chunk_idx, tuple_idx, index); - return chunk_idx; - } - - inline void SetChunkIndex(idx_t chunk_idx) { - index = state->GetDivisor() * chunk_idx; - } - - UnionIterator &operator++() { - ++index; - return *this; - } - - void Repin() { - state->SetKeepPinned(true); - state->SetPinPayload(true); - } - - unique_ptr state; - idx_t index = 0; - const bool strict; - }; - - IEJoinUnion(IEJoinGlobalSourceState &gsource, const ChunkRange &chunks); - - idx_t SearchL1(idx_t pos); - - //! Start the current row. - //! Returns false if there are no more rows to process - template - bool NextRow(); - - //! NextRow pointer to member for the sort key type. - using next_row_t = bool (duckdb::IEJoinUnion::*)(); - next_row_t next_row_func; - - //! Finish this row and move to the next one. - //! Returns false if there are no more rows to process - bool FinishRow() { - ++i; - return (this->*next_row_func)(); - } - - //! Constructor arguments - IEJoinGlobalSourceState &gsource; - - //! Inverted loop - idx_t JoinBlocks(unsafe_vector &lsel, unsafe_vector &rsel); - - //! B - vector bit_array; - ValidityMask bit_mask; - //! Bloom Filter - static constexpr idx_t BLOOM_CHUNK_BITS = 1024; - idx_t bloom_count; - vector bloom_array; - ValidityMask bloom_filter; - - //! Iteration state - idx_t n; - idx_t i; - idx_t n_j; - idx_t j; - unique_ptr op2; - unique_ptr off2; - int64_t lrid = std::numeric_limits::max(); - - //! ANTI JOIN bookmark - idx_t anti_i; - - //! Li - IEJoinCursor li; - //! P - IEJoinCursor p; -}; - -idx_t IEJoinUnion::AppendKey(ExecutionContext &context, InterruptState &interrupt, SortedTable &table, - ExpressionExecutor &executor, SortedTable &marked, int64_t increment, int64_t rid, - const ChunkRange &chunk_range) { - const auto chunk_begin = chunk_range.first; - const auto chunk_end = chunk_range.second; - - if (chunk_begin == chunk_end) { - return 0; - } - - // Reading - const auto valid = table.count - table.has_null; - auto &source = *table.sorted->payload_data; - TupleDataScanState scanner; - source.InitializeScan(scanner); - - DataChunk scanned; - source.InitializeScanChunk(scanner, scanned); - idx_t table_idx = source.Seek(scanner, chunk_begin); - - // Writing - auto &sort = *marked.sort; - auto local_sort_state = sort.GetLocalSinkState(context); - vector types; - for (const auto &expr : executor.expressions) { - types.emplace_back(expr->GetReturnType()); - } - const idx_t rid_idx = types.size(); - types.emplace_back(LogicalType::BIGINT); - - DataChunk keys; - DataChunk payload; - keys.Initialize(Allocator::DefaultAllocator(), types); - - OperatorSinkInput sink {*marked.global_sink, *local_sort_state, interrupt}; - idx_t inserted = 0; - for (auto chunk_idx = chunk_begin; chunk_idx < chunk_end; ++chunk_idx) { - source.Scan(scanner, scanned); - - // NULLs are at the end, so stop when we reach them - auto scan_count = scanned.size(); - if (table_idx + scan_count > valid) { - if (table_idx >= valid) { - scan_count = 0; - ; - } else { - scan_count = valid - table_idx; - scanned.SetChildCardinality(scan_count); - } - } - if (scan_count == 0) { - break; - } - table_idx += scan_count; - - // Compute the input columns from the payload - keys.Reset(); - keys.Split(payload, rid_idx); - executor.Execute(scanned, keys); - - // Mark the rid column - payload.data[0].Sequence(rid, increment, scan_count); - keys.Fuse(payload); - rid += increment * UnsafeNumericCast(scan_count); - - // Sort on the sort columns (which will no longer be needed) - sort.Sink(context, keys, sink); - inserted += scan_count; - } - OperatorSinkCombineInput combine {*marked.global_sink, *local_sort_state, interrupt}; - sort.Combine(context, combine); - marked.count += inserted; - - return inserted; -} - -IEJoinUnion::IEJoinUnion(IEJoinGlobalSourceState &gsource, const ChunkRange &chunks) - : gsource(gsource), n(0), i(0), li(*gsource.li), p(*gsource.p) { - auto &op = gsource.op; - - // 7. initialize bit-array B (|B| = n), and set all bits to 0 - auto &l2 = *gsource.l2; - n_j = l2.count.load(); - bit_array.resize(ValidityMask::EntryCount(n_j), 0); - bit_mask.Initialize(bit_array.data(), n_j); - - // Bloom filter - bloom_count = (n_j + (BLOOM_CHUNK_BITS - 1)) / BLOOM_CHUNK_BITS; - bloom_array.resize(ValidityMask::EntryCount(bloom_count), 0); - bloom_filter.Initialize(bloom_array.data(), bloom_count); - - // 11. for(i←1 to n) do - const auto strict2 = IsStrictComparison(op.conditions[1].GetComparisonType()); - op2 = make_uniq(l2, strict2); - off2 = make_uniq(l2, strict2); - n = l2.BlockStart(chunks.second); - i = l2.BlockStart(chunks.first); - j = i; - anti_i = i; - - const auto sort_key_type = l2.GetSortKeyType(); - switch (sort_key_type) { -#define DUCKDB_SORT_KEY_CASE(SORT_KEY_TYPE) \ - case SortKeyType::SORT_KEY_TYPE: \ - next_row_func = &IEJoinUnion::NextRow; \ - break; - DUCKDB_FOR_EACH_SORT_KEY_TYPE(DUCKDB_SORT_KEY_CASE) -#undef DUCKDB_SORT_KEY_CASE - default: - throw NotImplementedException("IEJoinUnion for %s", EnumUtil::ToString(sort_key_type)); - } - - (this->*next_row_func)(); -} - -template -bool IEJoinUnion::NextRow() { - using SORT_KEY = SortKey; - using BLOCKS_ITERATOR = block_iterator_t; - - BLOCKS_ITERATOR off2_itr(*off2->state); - BLOCKS_ITERATOR op2_itr(*op2->state); - const auto strict = off2->strict; - - auto pinned_idx = off2->GetChunkIndex(); - for (; i < n; ++i) { - // 12. pos ← P[i] - auto pos = p[i]; - lrid = li[pos]; - if (lrid < 0) { - continue; - } - - // 16. B[pos] ← 1 - op2->SetIndex(i); - for (; off2->GetIndex() < n_j; ++(*off2)) { - // Prevent buildup of pinned blocks - if (off2->GetChunkIndex() != pinned_idx) { - off2->Repin(); - pinned_idx = off2->GetChunkIndex(); - } - if (!Compare(off2_itr[off2->GetIndex()], op2_itr[op2->GetIndex()], strict)) { - break; - } - const auto p2 = p[off2->GetIndex()]; - if (li[p2] < 0) { - // Only mark rhs matches. - bit_mask.SetValidUnsafe(p2); - bloom_filter.SetValidUnsafe(p2 / BLOOM_CHUNK_BITS); - } - } - - // 9. if (op1 ∈ {≤,≥} and op2 ∈ {≤,≥}) eqOff = 0 - // 10. else eqOff = 1 - // No, because there could be more than one equal value. - // Find the leftmost off1 where L1[pos] op1 L1[off1..n] - // These are the rows that satisfy the op1 condition - // and that is where we should start scanning B from - j = pos; - - return true; - } - return false; -} - -static idx_t NextValid(const ValidityMask &bits, idx_t j, const idx_t n) { - if (j >= n) { - return n; - } - - // We can do a first approximation by checking entries one at a time - // which gives 64:1. - idx_t entry_idx, idx_in_entry; - bits.GetEntryIndex(j, entry_idx, idx_in_entry); - - // Copy first entry to local and trim the bits before the start position - auto first_entry = bits.GetValidityEntryUnsafe(entry_idx++); - first_entry &= (ValidityMask::ValidityBuffer::MAX_ENTRY << idx_in_entry); - - // If the first entry has a valid bit, we can return immediately - if (first_entry) { - return j + CountZeros::Trailing(first_entry) - idx_in_entry; - } - - // The first entry did not have a valid bit - j += ValidityMask::BITS_PER_VALUE - idx_in_entry; - - // Loop over non-ragged entries - const auto entry_count_minus_one = bits.EntryCount(n) - 1; - const auto entry_idx_before = entry_idx; - - // The compiler has a hard time optimizing this loop for some reason - // Creating a static inner loop like this improves performance by almost 2x - static constexpr idx_t NEXT_VALID_UNROLL = 8; - for (; entry_idx + NEXT_VALID_UNROLL < entry_count_minus_one; entry_idx += NEXT_VALID_UNROLL) { - for (idx_t unroll_idx = 0; unroll_idx < NEXT_VALID_UNROLL; unroll_idx++) { - const auto unroll_entry_idx = entry_idx + unroll_idx; - const auto &entry = bits.GetValidityEntryUnsafe(unroll_entry_idx); - if (entry) { - return j + (unroll_entry_idx - entry_idx_before) * ValidityMask::BITS_PER_VALUE + - CountZeros::Trailing(entry); - } - } - } - - for (; entry_idx < entry_count_minus_one; ++entry_idx) { - const auto &entry = bits.GetValidityEntryUnsafe(entry_idx); - if (entry) { - return j + (entry_idx - entry_idx_before) * ValidityMask::BITS_PER_VALUE + - CountZeros::Trailing(entry); - } - } - - // Update j once after the loop so we don't have to update it in each iteration - j += (entry_idx - entry_idx_before) * ValidityMask::BITS_PER_VALUE; - - // Check the final entry - return j >= n ? n : j + CountZeros::Trailing(bits.GetValidityEntryUnsafe(entry_idx)); -} - -idx_t IEJoinUnion::JoinBlocks(unsafe_vector &lsel, unsafe_vector &rsel) { - // Release pinned blocks - op2->Repin(); - off2->Repin(); - - // 8. initialize join result as an empty list for tuple pairs - idx_t result_count = 0; - - lsel.resize(STANDARD_VECTOR_SIZE); - rsel.resize(STANDARD_VECTOR_SIZE); - - // 11. for(i←1 to n) do - while (i < n) { - // 13. for (j ← pos+eqOff to n) do - for (;;) { - // 14. if B[j] = 1 then - - // Use the Bloom filter to find candidate blocks - while (j < n_j) { - auto bloom_begin = NextValid(bloom_filter, j / BLOOM_CHUNK_BITS, bloom_count) * BLOOM_CHUNK_BITS; - auto bloom_end = MinValue(n_j, bloom_begin + BLOOM_CHUNK_BITS); - - j = MaxValue(j, bloom_begin); - j = NextValid(bit_mask, j, bloom_end); - if (j < bloom_end) { - break; - } - } - - if (j >= n_j) { - break; - } - - // Filter out tuples with the same sign (they come from the same table) - const auto rrid = li[j]; - ++j; - - D_ASSERT(lrid > 0 && rrid < 0); - // 15. add tuples w.r.t. (L1[j], L1[i]) to join result - lsel[result_count] = static_cast(+lrid - 1); - rsel[result_count] = static_cast(-rrid - 1); - ++result_count; - if (result_count == STANDARD_VECTOR_SIZE) { - // out of space! - return result_count; - } - } - - if (!FinishRow()) { - break; - } - } - - lsel.resize(result_count); - rsel.resize(result_count); - - return result_count; -} - IEJoinGlobalSourceState::IEJoinGlobalSourceState(const PhysicalIEJoin &op, ClientContext &client, IEJoinGlobalState &gsink) : op(op), gsink(gsink), stage(IEJoinSourceStage::INIT), started(0), finished(0), stopped(false) { @@ -850,65 +380,7 @@ IEJoinGlobalSourceState::IEJoinGlobalSourceState(const PhysicalIEJoin &op, Clien right_outers = right_blocks; } - // input : query Q with 2 join predicates t1.X op1 t2.X' and t1.Y op2 t2.Y', tables T, T' of sizes m and n resp. - // output: a list of tuple pairs (ti , tj) - // Note that T/T' are already sorted on X/X' and contain the payload data - // We only join the two block numbers and use the sizes of the blocks as the counts - - // 1. let L1 (resp. L2) be the array of column X (resp. Y ) - const auto &order1 = op.lhs_orders[0]; - const auto &order2 = op.lhs_orders[1]; - - // 2. if (op1 ∈ {>, ≥}) sort L1 in descending order - // 3. else if (op1 ∈ {<, ≤}) sort L1 in ascending order - - // For the union algorithm, we make a unified table with the keys and the rids as the payload: - // X/X', Y/Y', R/R'/Li - // The first position is the sort key. - vector types; - types.emplace_back(order2.expression->GetReturnType()); - types.emplace_back(LogicalType::BIGINT); - - // Sort on the first expression - auto ref = make_uniq(order1.expression->GetReturnType(), 0U); - vector orders; - orders.emplace_back(order1.type, order1.null_order, std::move(ref)); - // The goal is to make i (from the left table) < j (from the right table), - // if value[i] and value[j] match the condition 1. - // Add a column from_left to solve the problem when there exist multiple equal values in l1. - // If the operator is loose inequality, make t1.from_left (== true) sort BEFORE t2.from_left (== false). - // Otherwise, make t1.from_left sort (== true) sort AFTER t2.from_left (== false). - // For example, if t1.time <= t2.time - // | value | 1 | 1 | 1 | 1 | - // | --------- | ----- | ----- | ----- | ----- | - // | from_left | T(l2) | T(l2) | F(r1) | F(r2) | - // if t1.time < t2.time - // | value | 1 | 1 | 1 | 1 | - // | --------- | ----- | ----- | ----- | ----- | - // | from_left | F(r2) | F(r1) | T(l2) | T(l1) | - // Using this OrderType, if i < j then value[i] (from left table) and value[j] (from right table) match - // the condition (t1.time <= t2.time or t1.time < t2.time), then from_left will force them into the correct order. - auto from_left = make_uniq(Value::BOOLEAN(true)); - const auto strict1 = IEJoinUnion::IsStrictComparison(op.conditions[0].GetComparisonType()); - orders.emplace_back(!strict1 ? OrderType::DESCENDING : OrderType::ASCENDING, OrderByNullType::ORDER_DEFAULT, - std::move(from_left)); - - l1 = make_uniq(client, orders, types, op); - - // 4. if (op2 ∈ {>, ≥}) sort L2 in ascending order - // 5. else if (op2 ∈ {<, ≤}) sort L2 in descending order - - // We sort on Y/Y' to obtain the sort keys and the permutation array. - // For this we just need a two-column table of Y, P - types.clear(); - types.emplace_back(LogicalType::BIGINT); - - // Sort on the first expression - orders.clear(); - ref = make_uniq(order2.expression->GetReturnType(), 0U); - orders.emplace_back(order2.type, order2.null_order, std::move(ref)); - - l2 = make_uniq(client, orders, types, op); + IEJoinUnion::InitializeTables(client, op, op.conditions, l1, l2); // The number of blocks in L2 is not quite the sum of the blocks in the two tables... const auto join_count = left_table.count.load() + right_table.count.load(); @@ -1012,6 +484,20 @@ class IEJoinLocalSourceState : public LocalSourceState { } bool TryAssignTask(); + //! Reports the progress of the current task + void UpdateProgress() { + if (!task) { + return; + } + if (TaskFinished()) { + task_progress.Finish(gsource.task_progress); + } else if (task->stage == IEJoinSourceStage::INNER && joiner) { + // the inner join advances over the rows of its range + auto done = joiner->i > inner_begin ? joiner->i - inner_begin : 0; + auto total = joiner->n > inner_begin ? joiner->n - inner_begin : 0; + task_progress.Update(gsource.task_progress, done, total); + } + } // Sort L1 void ExecuteSinkL1Task(ExecutionContext &context, InterruptState &interrupt); // Finalize L1 sort @@ -1065,6 +551,10 @@ class IEJoinLocalSourceState : public LocalSourceState { TaskPtr task; //! The task storage Task task_local; + //! The progress reported for the current task + TaskProgressTracker task_progress; + //! The first row of the current inner join task + idx_t inner_begin = 0; // Joining unique_ptr joiner; @@ -1118,6 +608,7 @@ bool IEJoinLocalSourceState::TryAssignTask() { // Because downstream operators may be using our internal buffers, // we can't "finish" a task until we are about to get the next one. if (task) { + task_progress.Finish(gsource.task_progress); ++gsource.GetStageNext(task->stage); left_matches = nullptr; right_matches = nullptr; @@ -1126,6 +617,7 @@ bool IEJoinLocalSourceState::TryAssignTask() { if (!gsource.TryNextTask(task, task_local)) { return false; } + task_progress.Start(); auto &gsink = gsource.gsink; auto &left_table = *gsink.tables[0]; @@ -1149,7 +641,8 @@ bool IEJoinLocalSourceState::TryAssignTask() { right_block_index = 0; right_base = 0; - joiner = make_uniq(gsource, task->range); + joiner = make_uniq(*gsource.l2, *gsource.li, *gsource.p, gsource.op.conditions, task->range); + inner_begin = joiner->i; break; case IEJoinSourceStage::OUTER: if (task->thread_idx < gsource.left_outers) { @@ -1410,7 +903,7 @@ void IEJoinLocalSourceState::SplitPayloads(DataChunk &chunk) { if (col_idx < left_cols) { lpayload.data[col_idx].Reference(chunk.data[col_idx]); } else { - rpayload.data[col_idx - left_cols].Reference(chunk.data[col_idx - left_cols]); + rpayload.data[col_idx - left_cols].Reference(chunk.data[col_idx]); } } } @@ -1938,17 +1431,12 @@ bool IEJoinGlobalSourceState::TryNextTask(Task &task) { ProgressData IEJoinGlobalSourceState::GetProgress() const { const auto count = GetTaskCount(); - - const auto returned = finished.load(); - - ProgressData res; - if (count) { - res.done = double(returned); - res.total = double(count); - } else { + if (!count) { + ProgressData res; res.SetInvalid(); + return res; } - return res; + return task_progress.GetProgress(count); } unique_ptr PhysicalIEJoin::GetGlobalSourceState(ClientContext &client) const { auto &gsink = sink_state->Cast(); @@ -1976,6 +1464,7 @@ SourceResultType PhysicalIEJoin::GetDataInternal(ExecutionContext &context, Data while (gsource.stage != IEJoinSourceStage::DONE && result.size() == 0) { if (!lsource.TaskFinished() || lsource.TryAssignTask()) { lsource.ExecuteTask(context, result, input.interrupt_state); + lsource.UpdateProgress(); } else { annotated_lock_guard guard(gsource.lock); if (gsource.TryPrepareNextStage() || gsource.stage == IEJoinSourceStage::DONE) { diff --git a/src/duckdb/src/execution/operator/join/physical_nested_loop_join.cpp b/src/duckdb/src/execution/operator/join/physical_nested_loop_join.cpp index 5b6faefe7..9eea64017 100644 --- a/src/duckdb/src/execution/operator/join/physical_nested_loop_join.cpp +++ b/src/duckdb/src/execution/operator/join/physical_nested_loop_join.cpp @@ -2,9 +2,12 @@ #include "duckdb/parallel/thread_context.hpp" #include "duckdb/execution/expression_executor.hpp" #include "duckdb/execution/nested_loop_join.hpp" +#include "duckdb/execution/mark_join_row_comparison.hpp" #include "duckdb/main/client_context.hpp" #include "duckdb/execution/operator/join/outer_join_marker.hpp" +#include "duckdb/common/types/vector_cache.hpp" #include "duckdb/common/vector/flat_vector.hpp" +#include "duckdb/common/vector/constant_vector.hpp" namespace duckdb { @@ -16,8 +19,15 @@ PhysicalNestedLoopJoin::PhysicalNestedLoopJoin(PhysicalPlan &physical_plan, Logi : PhysicalComparisonJoin(physical_plan, op, PhysicalOperatorType::NESTED_LOOP_JOIN, std::move(conds), join_type, estimated_cardinality) { filter_pushdown = std::move(pushdown_info_p); + D_ASSERT(join_type != JoinType::MARK || (conditions.size() == 1 && !predicate)); + track_unknown = + join_type == JoinType::MARK && (conditions[0].GetLHS().GetReturnType().id() == LogicalTypeId::TUPLE || + conditions[0].GetComparisonType() == ExpressionType::COMPARE_DISTINCT_FROM); children.push_back(left); children.push_back(right); + if (join_type == JoinType::MARK) { + mark_projection_map = FillProjectionMap(left, op.left_projection_map); + } } PhysicalNestedLoopJoin::PhysicalNestedLoopJoin(PhysicalPlan &physical_plan, LogicalComparisonJoin &op, @@ -124,6 +134,7 @@ bool PhysicalNestedLoopJoin::IsSupported(const vector &conditions if (join_type == JoinType::MARK) { return true; } + idx_t comparison_count = 0; for (auto &cond : conditions) { if (!cond.IsComparison()) { continue; @@ -133,18 +144,17 @@ bool PhysicalNestedLoopJoin::IsSupported(const vector &conditions cond.GetLHS().GetReturnType().InternalType() == PhysicalType::ARRAY) { return false; } + comparison_count++; + } + if (comparison_count == 0) { + // the other conditions are only evaluated on the pairs that match a comparison + return false; } // To avoid situations like https://github.com/duckdb/duckdb/issues/10046 // If there is an equality in the conditions, a hash join is planned // with one condition, we can use mark join logic, otherwise we should use physical blockwise nl join if (join_type == JoinType::SEMI || join_type == JoinType::ANTI) { - idx_t comparison_count = 0; - for (auto &cond : conditions) { - if (cond.IsComparison()) { - comparison_count++; - } - } return comparison_count == 1; } return true; @@ -337,10 +347,20 @@ class PhysicalNestedLoopJoinState : public CachingOperatorState { right_condition.Initialize(allocator, condition_types); right_payload.Initialize(allocator, op.children[1].get().GetTypes()); left_outer.Initialize(STANDARD_VECTOR_SIZE); + if (op.join_type == JoinType::MARK) { + auto payload_types = op.GetTypes(); + payload_types.pop_back(); + mark_payload.InitializeEmpty(payload_types); + } if (op.predicate) { pred_executor.AddExpression(*op.predicate); pred_matches.Initialize(); + auto predicate_types = op.children[0].get().GetTypes(); + for (auto &type : op.children[1].get().GetTypes()) { + predicate_types.push_back(type); + } + pred_input.Initialize(allocator, predicate_types); } ResetState(); } @@ -364,6 +384,8 @@ class PhysicalNestedLoopJoinState : public CachingOperatorState { //! Predicate ExpressionExecutor pred_executor; SelectionVector pred_matches; + DataChunk pred_input; + DataChunk mark_payload; private: void ResetState() { @@ -375,6 +397,8 @@ class PhysicalNestedLoopJoinState : public CachingOperatorState { payload_scan_state = ColumnDataScanState(); right_condition.Reset(); right_payload.Reset(); + pred_input.Reset(); + mark_payload.Reset(); left_tuple = 0; right_tuple = 0; left_outer.Reset(); @@ -402,11 +426,16 @@ OperatorResultType PhysicalNestedLoopJoin::ExecuteInternal(ExecutionContext &con DataChunk &chunk, GlobalOperatorState &gstate_p, OperatorState &state_p) const { auto &gstate = sink_state->Cast(); + auto &state = state_p.Cast(); + if (join_type == JoinType::MARK) { + state.mark_payload.ReferenceColumns(input, mark_projection_map); + } if (gstate.right_payload_data.Count() == 0) { // empty RHS if (!EmptyResultIfRHSIsEmpty()) { - ConstructEmptyJoinResult(join_type, gstate.has_null, input, chunk); + auto &payload = join_type == JoinType::MARK ? state.mark_payload : input; + ConstructEmptyJoinResult(join_type, gstate.has_null, payload, chunk); return OperatorResultType::NEED_MORE_INPUT; } else { return OperatorResultType::FINISHED; @@ -430,6 +459,61 @@ OperatorResultType PhysicalNestedLoopJoin::ExecuteInternal(ExecutionContext &con } } +static void ResolveSimpleJoinPredicate(const vector &conditions, DataChunk &input, + PhysicalNestedLoopJoinState &state, NestedLoopJoinGlobalState &gstate, + bool found_match[]) { + D_ASSERT(conditions.size() == 1); + gstate.right_condition_data.InitializeScan(state.condition_scan_state); + gstate.right_payload_data.InitializeScan(state.payload_scan_state); + Vector comparison(LogicalType::BOOLEAN); + Vector left_reference(state.left_condition.data[0].GetType()); + VectorCache predicate_cache(state.pred_executor.GetAllocator(), LogicalType::BOOLEAN); + Vector predicate_result(predicate_cache); + while (gstate.right_condition_data.Scan(state.condition_scan_state, state.right_condition)) { + if (!gstate.right_payload_data.Scan(state.payload_scan_state, state.right_payload) || + state.right_condition.size() != state.right_payload.size()) { + throw InternalException("Nested loop join: payload and conditions are unaligned!?"); + } + for (idx_t left_row = 0; left_row < input.size(); left_row++) { + if (found_match[left_row]) { + continue; + } + ConstantVector::Reference(left_reference, count_t(state.right_condition.size()), + state.left_condition.data[0], left_row, state.left_condition.size()); + MarkJoinRowComparison::Compare(left_reference, state.right_condition.data[0], + conditions[0].GetComparisonType(), comparison); + auto comparisons = comparison.Values(); + idx_t candidate_count = 0; + for (idx_t right_row = 0; right_row < state.right_condition.size(); right_row++) { + auto entry = comparisons[right_row]; + if (entry.IsValid() && entry.GetValue()) { + state.pred_matches.set_index(candidate_count++, right_row); + } + } + if (candidate_count == 0) { + continue; + } + state.pred_input.Reset(); + state.pred_input.SetChildCardinality(candidate_count); + state.pred_input.Slice(state.right_payload, state.pred_matches, candidate_count, input.ColumnCount()); + for (idx_t column_idx = 0; column_idx < input.ColumnCount(); column_idx++) { + ConstantVector::Reference(state.pred_input.data[column_idx], count_t(candidate_count), + input.data[column_idx], left_row, input.size()); + } + predicate_result.ResetFromCache(predicate_cache); + state.pred_executor.ExecuteExpression(state.pred_input, predicate_result); + auto predicates = predicate_result.Values(); + for (idx_t candidate = 0; candidate < candidate_count; candidate++) { + auto predicate = predicates[candidate]; + if (predicate.IsValid() && predicate.GetValue()) { + found_match[left_row] = true; + break; + } + } + } + } +} + void PhysicalNestedLoopJoin::ResolveSimpleJoin(ExecutionContext &context, DataChunk &input, DataChunk &chunk, OperatorState &state_p) const { auto &state = state_p.Cast(); @@ -441,14 +525,19 @@ void PhysicalNestedLoopJoin::ResolveSimpleJoin(ExecutionContext &context, DataCh bool found_match[STANDARD_VECTOR_SIZE] = {false}; bool found_unknown[STANDARD_VECTOR_SIZE] = {false}; - const bool track_unknown = join_type == JoinType::MARK && conditions.size() == 1 && - conditions[0].GetLHS().GetReturnType().id() == LogicalTypeId::TUPLE; - NestedLoopJoinMark::Perform(state.left_condition, gstate.right_condition_data, found_match, conditions, - track_unknown ? optional_ptr(found_unknown) : nullptr); + + if (predicate) { + D_ASSERT(join_type == JoinType::SEMI || join_type == JoinType::ANTI); + ResolveSimpleJoinPredicate(conditions, input, state, gstate, found_match); + } else { + NestedLoopJoinMark::Perform(state.left_condition, gstate.right_condition_data, found_match, conditions, + track_unknown ? optional_ptr(found_unknown) : nullptr); + } switch (join_type) { case JoinType::MARK: // now construct the mark join result from the found matches - PhysicalJoin::ConstructMarkJoinResult(state.left_condition, input, chunk, found_match, gstate.has_null, + PhysicalJoin::ConstructMarkJoinResult(state.left_condition, state.mark_payload, chunk, found_match, + gstate.has_null, track_unknown ? optional_ptr(found_unknown) : nullptr); break; case JoinType::SEMI: @@ -611,6 +700,10 @@ class NestedLoopJoinLocalScanState : public LocalSourceState { } }; +ProgressData PhysicalNestedLoopJoin::GetProgress(ClientContext &context, GlobalSourceState &gstate) const { + return gstate.Cast().scan_state.GetProgress(); +} + unique_ptr PhysicalNestedLoopJoin::GetGlobalSourceState(ClientContext &context) const { return make_uniq(*this, context); } diff --git a/src/duckdb/src/execution/operator/join/physical_piecewise_merge_join.cpp b/src/duckdb/src/execution/operator/join/physical_piecewise_merge_join.cpp index 26c6ca177..334b2d9f4 100644 --- a/src/duckdb/src/execution/operator/join/physical_piecewise_merge_join.cpp +++ b/src/duckdb/src/execution/operator/join/physical_piecewise_merge_join.cpp @@ -2,6 +2,7 @@ #include +#include "duckdb/common/atomic.hpp" #include "duckdb/common/row_operations/row_operations.hpp" #include "duckdb/common/sorting/sort_key.hpp" #include "duckdb/common/types/row/block_iterator.hpp" @@ -798,6 +799,7 @@ class PiecewiseJoinGlobalScanState : public GlobalSourceState { } TupleDataCollection &payload; + atomic rows_scanned {0}; public: idx_t MaxThreads() override { @@ -827,6 +829,15 @@ unique_ptr PhysicalPiecewiseMergeJoin::GetGlobalSourceState(C return make_uniq(*gsink.table->sorted->payload_data); } +ProgressData PhysicalPiecewiseMergeJoin::GetProgress(ClientContext &context, GlobalSourceState &gstate) const { + auto &state = gstate.Cast(); + const auto total = state.payload.Count(); + if (total == 0) { + return ProgressData {1.0, 1.0, false}; + } + return ProgressData {double(state.rows_scanned.load()), double(total), false}; +} + unique_ptr PhysicalPiecewiseMergeJoin::GetLocalSourceState(ExecutionContext &context, GlobalSourceState &gstate) const { return make_uniq(gstate.Cast()); @@ -879,6 +890,9 @@ SourceResultType PhysicalPiecewiseMergeJoin::GetDataInternal(ExecutionContext &c for (idx_t col_idx = 0; col_idx < right_column_count; ++col_idx) { result.data[left_column_count + col_idx].Slice(rhs_chunk.data[col_idx], rsel, result_count); } + } + gsource.rows_scanned.fetch_add(count, std::memory_order_relaxed); + if (result_count > 0) { break; } } diff --git a/src/duckdb/src/execution/operator/join/physical_positional_join.cpp b/src/duckdb/src/execution/operator/join/physical_positional_join.cpp index 035858e18..c839b52cc 100644 --- a/src/duckdb/src/execution/operator/join/physical_positional_join.cpp +++ b/src/duckdb/src/execution/operator/join/physical_positional_join.cpp @@ -21,7 +21,7 @@ class PositionalJoinGlobalState : public GlobalSinkState { public: explicit PositionalJoinGlobalState(ClientContext &context, const PhysicalPositionalJoin &op) : rhs(context, op.children[1].get().GetTypes()), initialized(false), source_count(0), source_offset(0), - exhausted(false) { + exhausted(false), rows_scanned(0) { rhs.InitializeAppend(append_state); } @@ -35,6 +35,7 @@ class PositionalJoinGlobalState : public GlobalSinkState { idx_t source_count; idx_t source_offset; bool exhausted; + atomic rows_scanned; void InitializeScan(); idx_t Refill(); @@ -137,6 +138,8 @@ void PositionalJoinGlobalState::Execute(DataChunk &input, DataChunk &output) { CopyData(output, count, col_offset); output.SetChildCardinality(count); + const auto scanned = rows_scanned.load(std::memory_order_relaxed); + rows_scanned.store(scanned + MinValue(count, rhs.Count() - scanned), std::memory_order_relaxed); } OperatorResultType PhysicalPositionalJoin::Execute(ExecutionContext &context, DataChunk &input, DataChunk &chunk, @@ -172,6 +175,8 @@ void PositionalJoinGlobalState::GetData(DataChunk &output) { // RHS still has data, so copy it CopyData(output, count, col_offset); output.SetChildCardinality(count); + const auto scanned = rows_scanned.load(std::memory_order_relaxed); + rows_scanned.store(scanned + MinValue(count, rhs.Count() - scanned), std::memory_order_relaxed); } SourceResultType PhysicalPositionalJoin::GetDataInternal(ExecutionContext &context, DataChunk &result, @@ -182,6 +187,19 @@ SourceResultType PhysicalPositionalJoin::GetDataInternal(ExecutionContext &conte return result.size() == 0 ? SourceResultType::FINISHED : SourceResultType::HAVE_MORE_OUTPUT; } +ProgressData PhysicalPositionalJoin::GetProgress(ClientContext &context, GlobalSourceState &gstate) const { + auto &sink = sink_state->Cast(); + ProgressData progress; + progress.total = double(MaxValue(sink.rhs.Count(), 1)); + progress.done = sink.rhs.Count() == 0 ? 1.0 : double(sink.rows_scanned.load(std::memory_order_relaxed)); + return progress; +} + +void PhysicalPositionalJoin::SourceFinished(ClientContext &context, GlobalSourceState &gstate) const { + auto &sink = sink_state->Cast(); + sink.rows_scanned.store(sink.rhs.Count(), std::memory_order_relaxed); +} + //===--------------------------------------------------------------------===// // Pipeline Construction //===--------------------------------------------------------------------===// diff --git a/src/duckdb/src/execution/operator/join/physical_range_join.cpp b/src/duckdb/src/execution/operator/join/physical_range_join.cpp index 48fc406ec..26426f4ed 100644 --- a/src/duckdb/src/execution/operator/join/physical_range_join.cpp +++ b/src/duckdb/src/execution/operator/join/physical_range_join.cpp @@ -80,7 +80,7 @@ void PhysicalRangeJoin::LocalSortedTable::Sink(ExecutionContext &context, DataCh PhysicalRangeJoin::GlobalSortedTable::GlobalSortedTable(ClientContext &client, const vector &order_bys, const vector &payload_types, - const PhysicalRangeJoin &op) + const PhysicalComparisonJoin &op) : op(op), has_null(0), count(0), tasks_completed(0) { // Set up the sort. We will materialize keys ourselves, so just set up references. vector orders; diff --git a/src/duckdb/src/execution/operator/order/physical_top_n.cpp b/src/duckdb/src/execution/operator/order/physical_top_n.cpp index 617c49423..f44778518 100644 --- a/src/duckdb/src/execution/operator/order/physical_top_n.cpp +++ b/src/duckdb/src/execution/operator/order/physical_top_n.cpp @@ -1,4 +1,5 @@ #include "duckdb/execution/operator/order/physical_top_n.hpp" +#include "duckdb/common/atomic.hpp" #include "duckdb/common/assert.hpp" #include "duckdb/common/mutex.hpp" @@ -57,9 +58,13 @@ struct TopNBoundaryValue { Vector boundary_vector; OrderModifiers boundary_modifiers; - string GetBoundaryValue() { - lock_guard l(lock); - return boundary_value; + bool TryGetBoundaryValue(string ¤t_boundary) { + unique_lock l(lock, std::try_to_lock); + if (!l.owns_lock() || boundary_value == current_boundary) { + return false; + } + current_boundary = boundary_value; + return true; } void UpdateValue(string_t boundary_val) { @@ -273,15 +278,14 @@ void TopNHeap::AddLargeHeap(DataChunk &input, const Vector &sort_keys_vec) { } bool TopNHeap::CheckBoundaryValues(DataChunk &sort_chunk, DataChunk &payload, TopNBoundaryValue &global_boundary) { - // get the global boundary value - auto current_boundary_val = global_boundary.GetBoundaryValue(); - if (current_boundary_val.empty()) { + // An older local boundary remains valid if refreshing it would block. + const auto boundary_updated = global_boundary.TryGetBoundaryValue(boundary_val); + if (boundary_val.empty()) { // no boundary value (yet) - don't do anything return true; } - if (current_boundary_val != boundary_val) { + if (boundary_updated) { // new boundary value - decode - boundary_val = std::move(current_boundary_val); boundary_values.Reset(); CreateSortKeyHelpers::DecodeSortKey(string_t(boundary_val), boundary_values, 0, modifiers); for (auto &col : boundary_values.data) { @@ -431,7 +435,7 @@ void TopNHeap::Reduce() { return; } // we have too many values in the heap - reduce them - StringHeap new_sort_heap; + StringHeap new_sort_heap(allocator); DataChunk new_heap_data; new_heap_data.Initialize(allocator, payload_types, heap.size()); @@ -563,6 +567,7 @@ class TopNGlobalSourceState : public GlobalSourceState { public: explicit TopNGlobalSourceState(TopNGlobalSinkState &sink_p) : sink(sink_p), batch_index(0) { sink.heap.InitializeScan(state, true); + total_rows = state.scan_order.size() - MinValue(sink.heap.offset, state.scan_order.size()); } idx_t MaxThreads() override { @@ -576,12 +581,22 @@ class TopNGlobalSourceState : public GlobalSourceState { TopNGlobalSinkState &sink; TopNScanState state; idx_t batch_index; + idx_t total_rows; + atomic rows_scanned {0}; }; unique_ptr PhysicalTopN::GetGlobalSourceState(ClientContext &context) const { return make_uniq(this->sink_state->Cast()); } +ProgressData PhysicalTopN::GetProgress(ClientContext &context, GlobalSourceState &gstate) const { + auto &state = gstate.Cast(); + if (state.total_rows == 0) { + return ProgressData {1.0, 1.0, false}; + } + return ProgressData {double(state.rows_scanned.load()), double(state.total_rows), false}; +} + unique_ptr PhysicalTopN::GetLocalSourceState(ExecutionContext &context, GlobalSourceState &gstate) const { return make_uniq(); @@ -606,6 +621,7 @@ SourceResultType PhysicalTopN::GetDataInternal(ExecutionContext &context, DataCh } sink.heap.Scan(gstate.state, chunk, lstate.pos); + gstate.rows_scanned.fetch_add(chunk.size(), std::memory_order_relaxed); return chunk.size() == 0 ? SourceResultType::FINISHED : SourceResultType::HAVE_MORE_OUTPUT; } diff --git a/src/duckdb/src/execution/operator/persistent/copy_batch_slicer.cpp b/src/duckdb/src/execution/operator/persistent/copy_batch_slicer.cpp new file mode 100644 index 000000000..aad699c9e --- /dev/null +++ b/src/duckdb/src/execution/operator/persistent/copy_batch_slicer.cpp @@ -0,0 +1,110 @@ +#include "duckdb/execution/operator/persistent/copy_batch_slicer.hpp" + +#include "duckdb/common/types/column/column_data_collection.hpp" +#include "duckdb/common/types/row/tuple_data_collection.hpp" +#include "duckdb/common/vector/vector_iterator.hpp" +#include "duckdb/common/vector/vector_writer.hpp" + +namespace duckdb { + +static constexpr idx_t BATCH_SIZE_BYTES_SLACK_DIVISOR = 8; + +//! Column collections store fixed-width struct children without tuple headers. +static idx_t FixedRowBytes(const LogicalType &type) { + if (type.InternalType() != PhysicalType::STRUCT) { + return GetTypeIdSize(type.InternalType()); + } + idx_t result = 0; + for (const auto &child_type : StructType::GetChildTypes(type)) { + result += FixedRowBytes(child_type.second); + } + return result; +} + +CopyBatchSlicer::CopyBatchSlicer(const vector &types, const optional_idx &batch_size_bytes_p) + : batch_size_bytes(batch_size_bytes_p) { + if (!batch_size_bytes.IsValid()) { + return; + } + layout.Initialize(types, TupleDataValidityType::CAN_HAVE_NULL_VALUES); + for (const auto &type : types) { + fixed_row_bytes += FixedRowBytes(type); + } + partial.InitializeEmpty(types); + row_bytes_state = make_uniq(); + TupleDataCollection::InitializeChunkState(*row_bytes_state, types, layout.GetVariableColumns()); +} + +CopyBatchSlicer::~CopyBatchSlicer() { +} + +const Vector &CopyBatchSlicer::ComputeRowSizes(DataChunk &chunk) { + D_ASSERT(row_bytes_state); + auto &row_sizes = row_bytes_state->heap_sizes; + if (layout.AllConstant()) { + auto writer = FlatVector::Writer(row_sizes, chunk.size()); + for (idx_t i = 0; i < chunk.size(); i++) { + writer.WriteValue(fixed_row_bytes); + } + } else { + TupleDataCollection::ToUnifiedFormat(*row_bytes_state, chunk); + TupleDataCollection::ComputeHeapSizes(*row_bytes_state, chunk, *FlatVector::IncrementalSelectionVector(), + chunk.size()); + auto heap_sizes = row_sizes.Values(); + auto writer = FlatVector::Writer(row_sizes, chunk.size()); + for (auto entry : heap_sizes) { + writer.WriteValue(fixed_row_bytes + entry.GetValue()); + } + } + return row_sizes; +} + +idx_t CopyBatchSlicer::RowsThatFit(idx_t count, idx_t offset, idx_t budget) const { + const auto remaining = count - offset; + if (layout.AllConstant()) { + if (fixed_row_bytes == 0) { + return remaining; + } + const auto fits = budget / fixed_row_bytes + (budget % fixed_row_bytes != 0); + return MaxValue(MinValue(remaining, fits), 1); + } + auto row_sizes = row_bytes_state->heap_sizes.Values(); + idx_t total = 0; + for (idx_t i = 0; i < remaining; i++) { + total += row_sizes[offset + i].GetValue(); + if (total >= budget) { + return i + 1; + } + } + return remaining; +} + +DataChunk &CopyBatchSlicer::Slice(DataChunk &chunk, idx_t &offset, const ColumnDataCollection &batch) { + D_ASSERT(offset < chunk.size()); + if (!batch_size_bytes.IsValid()) { + offset = chunk.size(); + return chunk; + } + if (!layout.AllConstant() && offset == 0) { + ComputeRowSizes(chunk); + } + // InitializeAppend allocates a chunk, so an empty batch reports its capacity + const auto current_batch_bytes = batch.Count() == 0 ? 0 : batch.SizeInBytes(); + const auto limit = batch_size_bytes.GetIndex(); + // Avoid slicing chunks that only marginally cross the byte limit. + const auto slack_limit = limit + limit / BATCH_SIZE_BYTES_SLACK_DIVISOR; + auto append_count = + RowsThatFit(chunk.size(), offset, slack_limit > current_batch_bytes ? slack_limit - current_batch_bytes : 0); + if (append_count < chunk.size() - offset) { + append_count = RowsThatFit(chunk.size(), offset, limit > current_batch_bytes ? limit - current_batch_bytes : 0); + } + if (offset == 0 && append_count == chunk.size()) { + offset = chunk.size(); + return chunk; + } + partial.Slice(chunk, offset, offset + append_count); + offset += append_count; + return partial; +} + +} // namespace duckdb diff --git a/src/duckdb/src/execution/operator/persistent/copy_output_lifecycle.cpp b/src/duckdb/src/execution/operator/persistent/copy_output_lifecycle.cpp index ab9b50def..adc19add2 100644 --- a/src/duckdb/src/execution/operator/persistent/copy_output_lifecycle.cpp +++ b/src/duckdb/src/execution/operator/persistent/copy_output_lifecycle.cpp @@ -34,8 +34,7 @@ void CopyOutputLifecycle::Cleanup() { return; } for (auto &file : files) { - if (file.ownership == CopyOutputOwnership::REMOVE_ON_FAILURE && - file.publication == CopyOutputPublicationState::FINALIZED) { + if (file.ownership == CopyOutputOwnership::REMOVE_ON_FAILURE) { files_to_remove.push_back(std::move(file.path)); } } @@ -72,19 +71,10 @@ void CopyOutputLifecycle::Cleanup() { } } -idx_t CopyOutputLifecycle::RegisterFile(string path) { +void CopyOutputLifecycle::RegisterFile(string path) { auto ownership = GetOwnership(path); annotated_lock_guard guard(lock); - auto result = files.size(); - files.push_back({std::move(path), ownership, CopyOutputPublicationState::UNFINALIZED}); - return result; -} - -void CopyOutputLifecycle::MarkFileFinalized(idx_t file_index) { - annotated_lock_guard guard(lock); - D_ASSERT(file_index < files.size()); - D_ASSERT(files[file_index].publication == CopyOutputPublicationState::UNFINALIZED); - files[file_index].publication = CopyOutputPublicationState::FINALIZED; + files.push_back({std::move(path), ownership}); } void CopyOutputLifecycle::RegisterCreatedDirectory(string path) { diff --git a/src/duckdb/src/execution/operator/persistent/csv_rejects_table.cpp b/src/duckdb/src/execution/operator/persistent/csv_rejects_table.cpp index bf80823fb..5a89081f9 100644 --- a/src/duckdb/src/execution/operator/persistent/csv_rejects_table.cpp +++ b/src/duckdb/src/execution/operator/persistent/csv_rejects_table.cpp @@ -23,14 +23,6 @@ TableCatalogEntry &CSVRejectsTable::GetScansTable(ClientContext &context) { return table_entry; } -idx_t CSVRejectsTable::GetCurrentFileIndex(idx_t query_id) { - if (current_query_id != query_id) { - current_query_id = query_id; - current_file_idx = 0; - } - return current_file_idx++; -} - shared_ptr CSVRejectsTable::GetOrCreate(ClientContext &context, const string &rejects_scan, const string &rejects_error) { // Check that these names can't be the same @@ -38,7 +30,9 @@ shared_ptr CSVRejectsTable::GetOrCreate(ClientContext &context, throw BinderException("The names of the rejects scan and rejects error tables can't be the same. Use different " "names for these tables."); } - auto key = StringUtil::Format("CSV_REJECTS_TABLE_CACHE_ENTRY_%s_%s", StringUtil::Upper(rejects_scan), + // length-prefix the scan name so that different name pairs can't produce the same key + auto upper_scan = StringUtil::Upper(rejects_scan); + auto key = StringUtil::Format("CSV_REJECTS_TABLE_CACHE_ENTRY_%llu_%s_%s", upper_scan.size(), upper_scan, StringUtil::Upper(rejects_error)); auto &cache = ObjectCache::GetObjectCache(context); auto &catalog = Catalog::GetCatalog(context, Identifier::TempCatalog()); @@ -65,7 +59,31 @@ shared_ptr CSVRejectsTable::GetOrCreate(ClientContext &context, return cache.GetOrCreate(key, rejects_scan, rejects_error); } +idx_t CSVRejectsTable::GetFileIndexBase(idx_t query_id, const void *scan_key, idx_t file_count) { + const lock_guard lock(file_index_lock); + if (file_index_query_id != query_id) { + // the file indexes are unique within a query + file_index_query_id = query_id; + next_file_index = 0; + scan_file_index_base.clear(); + } + if (!scan_key) { + // we cannot tell which scan this file belongs to - give it an index of its own + return next_file_index++; + } + auto entry = scan_file_index_base.find(scan_key); + if (entry != scan_file_index_base.end()) { + return entry->second; + } + const idx_t base = next_file_index; + next_file_index += MaxValue(file_count, 1); + scan_file_index_base.emplace(scan_key, base); + return base; +} + void CSVRejectsTable::InitializeTable(ClientContext &context, const ReadCSVData &data) { + // every file of a scan initializes the table it reports its rejects to, and files are opened in parallel + const lock_guard lock(write_lock); // (Re)Create the temporary rejects table auto &catalog = Catalog::GetCatalog(context, Identifier::TempCatalog()); diff --git a/src/duckdb/src/execution/operator/persistent/physical_batch_copy_to_file.cpp b/src/duckdb/src/execution/operator/persistent/physical_batch_copy_to_file.cpp index ebbf12c4e..3ca9f6a95 100644 --- a/src/duckdb/src/execution/operator/persistent/physical_batch_copy_to_file.cpp +++ b/src/duckdb/src/execution/operator/persistent/physical_batch_copy_to_file.cpp @@ -7,6 +7,7 @@ #include "duckdb/common/vector_operations/vector_operations.hpp" #include "duckdb/execution/operator/persistent/batch_memory_manager.hpp" #include "duckdb/execution/operator/persistent/batch_task_manager.hpp" +#include "duckdb/execution/operator/persistent/copy_batch_slicer.hpp" #include "duckdb/execution/operator/persistent/copy_output_lifecycle.hpp" #include "duckdb/execution/operator/persistent/physical_copy_to_file.hpp" #include "duckdb/parallel/base_pipeline_event.hpp" @@ -86,23 +87,22 @@ class FixedBatchCopyGlobalState : public GlobalSinkState { public: explicit FixedBatchCopyGlobalState(ClientContext &context_p, idx_t minimum_memory_per_thread) - : output_lifecycle(context_p), memory_manager(context_p, minimum_memory_per_thread), initialized(false), - rows_copied(0), scheduled_batch_index(0), flushed_batch_index(0), any_flushing(false), any_finished(false), + : output_lifecycle(context_p), memory_manager(context_p, minimum_memory_per_thread), rows_copied(0), + scheduled_batch_index(0), flushed_batch_index(0), any_flushing(false), any_finished(false), minimum_memory_per_thread(minimum_memory_per_thread) { } + //! Destroy after global_state so its handle closes before cleanup. CopyOutputLifecycle output_lifecycle; BatchMemoryManager memory_manager; BatchTaskManager task_manager; mutex lock; mutex flush_lock; - //! Whether or not the copy has been initialized - atomic initialized; + atomic initialized {false}; //! The total number of rows copied to the file atomic rows_copied; //! Global copy state unique_ptr global_state; - optional_idx lifecycle_file_index; //! Unpartitioned batches map> raw_batches; //! The prepared batch data by batch index - ready to flush @@ -121,26 +121,28 @@ class FixedBatchCopyGlobalState : public GlobalSinkState { unique_ptr written_file_info; void Initialize(ClientContext &context, const PhysicalBatchCopyToFile &op) { - if (initialized) { + if (initialized.load(std::memory_order_acquire)) { return; } lock_guard guard(lock); - if (initialized) { + if (initialized.load(std::memory_order_relaxed)) { return; } - // initialize writing to the file - lifecycle_file_index = output_lifecycle.RegisterFile(op.file_path); - global_state = op.function.copy_to_initialize_global(context, *op.bind_data, op.file_path); + output_lifecycle.RegisterFile(op.file_path); + auto new_global_state = op.function.copy_to_initialize_global(context, *op.bind_data, op.file_path); if (op.function.initialize_operator) { - op.function.initialize_operator(*global_state, op); + op.function.initialize_operator(*new_global_state, op); } + unique_ptr new_written_file_info; if (op.return_type == CopyFunctionReturnType::WRITTEN_FILE_STATISTICS) { - written_file_info = make_uniq(op.file_path); - written_file_info->file_stats = make_uniq(); - op.function.copy_to_get_written_statistics(context, *op.bind_data, *global_state, - *written_file_info->file_stats); + new_written_file_info = make_uniq(op.file_path); + new_written_file_info->file_stats = make_uniq(); + op.function.copy_to_get_written_statistics(context, *op.bind_data, *new_global_state, + *new_written_file_info->file_stats); } - initialized = true; + global_state = std::move(new_global_state); + written_file_info = std::move(new_written_file_info); + initialized.store(true, std::memory_order_release); } void AddBatchData(idx_t batch_index, unique_ptr new_batch, idx_t memory_usage, @@ -341,9 +343,6 @@ SinkFinalizeType PhysicalBatchCopyToFile::FinalFlush(ClientContext &context, Glo gstate.memory_manager.FinalCheck(); if (function.copy_to_finalize && gstate.global_state) { function.copy_to_finalize(context, *bind_data, *gstate.global_state); - D_ASSERT(gstate.lifecycle_file_index.IsValid()); - gstate.output_lifecycle.MarkFileFinalized(gstate.lifecycle_file_index.GetIndex()); - if (use_tmp_file) { PhysicalCopyToFile::MoveTmpFile(context, file_path); } @@ -500,6 +499,7 @@ void PhysicalBatchCopyToFile::RepartitionBatches(ClientContext &context, GlobalS } unique_ptr append_batch; ColumnDataAppendState append_state; + CopyBatchSlicer batch_slicer(children[0].get().GetTypes(), batch_size_bytes); // now perform the actual repartitioning for (auto ¤t_batch : raw_batches) { if (!append_batch) { @@ -533,20 +533,23 @@ void PhysicalBatchCopyToFile::RepartitionBatches(ClientContext &context, GlobalS append_batch->memory_usage += current_batch->memory_usage; // iterate the collection while appending for (auto &chunk : current_collection.Chunks()) { - // append the chunk to the collection - append_batch->collection->Append(append_state, chunk); - const CopyFunctionBatchAnalyzer batch_analyzer(*append_batch->collection, batch_size, batch_size_bytes); - if (!batch_analyzer.MeetsFlushCriteria()) { - // the collection is still under the desired batch size - continue - continue; - } - // the collection is full - move it to the result and create a new one - task_manager.AddTask(make_uniq(gstate.scheduled_batch_index++, std::move(append_batch))); + idx_t offset = 0; + while (offset < chunk.size()) { + auto &slice = batch_slicer.Slice(chunk, offset, *append_batch->collection); + append_batch->collection->Append(append_state, slice); + const CopyFunctionBatchAnalyzer batch_analyzer(*append_batch->collection, batch_size, batch_size_bytes); + if (!batch_analyzer.MeetsFlushCriteria()) { + continue; // the collection is still under the desired batch size + } + // the collection is full - move it to the result and create a new one + task_manager.AddTask( + make_uniq(gstate.scheduled_batch_index++, std::move(append_batch))); - auto new_collection = make_uniq(context, children[0].get().GetTypes()); - new_collection->SetPartitionIndex(0); // Makes the buffer manager less likely to spill this data - append_batch = make_uniq(0U, std::move(new_collection)); - append_batch->collection->InitializeAppend(append_state); + auto new_collection = make_uniq(context, children[0].get().GetTypes()); + new_collection->SetPartitionIndex(0); // Makes the buffer manager less likely to spill this data + append_batch = make_uniq(0U, std::move(new_collection)); + append_batch->collection->InitializeAppend(append_state); + } } } if (append_batch && append_batch->collection->Count() > 0) { diff --git a/src/duckdb/src/execution/operator/persistent/physical_batch_insert.cpp b/src/duckdb/src/execution/operator/persistent/physical_batch_insert.cpp index ef35aa085..af65686ff 100644 --- a/src/duckdb/src/execution/operator/persistent/physical_batch_insert.cpp +++ b/src/duckdb/src/execution/operator/persistent/physical_batch_insert.cpp @@ -335,12 +335,12 @@ unique_ptr PhysicalBatchInsert::GetGlobalSinkState(ClientContex // CREATE TABLE AS D_ASSERT(!insert_table); auto &catalog = schema->catalog; - auto created_table = catalog.CreateTable(catalog.GetCatalogTransaction(context), *schema.get_mutable(), *info); + auto created_table = catalog.CreateTable(catalog.GetCatalogTransaction(context), *schema, *info); table = &created_table->Cast(); } else { D_ASSERT(insert_table); D_ASSERT(insert_table->IsDuckTable()); - table = insert_table.get_mutable(); + table = insert_table; } // heuristic - we start off by allocating 4MB of cache space per column static constexpr const idx_t MINIMUM_MEMORY_PER_COLUMN = 4ULL * 1024ULL * 1024ULL; diff --git a/src/duckdb/src/execution/operator/persistent/physical_copy_database.cpp b/src/duckdb/src/execution/operator/persistent/physical_copy_database.cpp index ae291b6e9..9e1d3b223 100644 --- a/src/duckdb/src/execution/operator/persistent/physical_copy_database.cpp +++ b/src/duckdb/src/execution/operator/persistent/physical_copy_database.cpp @@ -72,7 +72,8 @@ SourceResultType PhysicalCopyDatabase::GetDataInternal(ExecutionContext &context if (!create_info || create_info->type != CatalogType::INDEX_ENTRY) { continue; } - catalog.CreateIndex(context.client, create_info->Cast()); + auto index_entry = catalog.CreateIndex(context.client, create_info->Cast()); + D_ASSERT(index_entry); auto &create_index_info = create_info->Cast(); auto &table_entry = catalog.GetEntry( @@ -84,7 +85,7 @@ SourceResultType PhysicalCopyDatabase::GetDataInternal(ExecutionContext &context storage_info.options.emplace("v1_0_0_storage", false); auto unbound_index = make_uniq(create_index_info.Copy(), std::move(storage_info), data_table.GetTableIOManager(), catalog.GetAttached()); - data_table.AddIndex(std::move(unbound_index)); + data_table.AddIndex(std::move(unbound_index), index_entry->oid); // We add unbound indexes, so we immediately bind them. // Otherwise, WAL serialization fails due to unbound indexes. diff --git a/src/duckdb/src/execution/operator/persistent/physical_copy_to_file.cpp b/src/duckdb/src/execution/operator/persistent/physical_copy_to_file.cpp index 51d0beba2..bf6949364 100644 --- a/src/duckdb/src/execution/operator/persistent/physical_copy_to_file.cpp +++ b/src/duckdb/src/execution/operator/persistent/physical_copy_to_file.cpp @@ -2,18 +2,24 @@ #include "duckdb/common/file_opener.hpp" #include "duckdb/common/file_system.hpp" -#include "duckdb/common/hive_partitioning.hpp" +#include "duckdb/common/path.hpp" #include "duckdb/common/optional.hpp" #include "duckdb/common/string_util.hpp" #include "duckdb/common/sorting/sort_strategy.hpp" #include "duckdb/common/types/column/column_data_collection_segment.hpp" #include "duckdb/common/value_operations/value_operations.hpp" +#include "duckdb/common/vector/vector_iterator.hpp" +#include "duckdb/execution/expression_executor.hpp" +#include "duckdb/function/function_binder.hpp" +#include "duckdb/function/scalar/string_functions.hpp" #include "duckdb/function/window/window_collection.hpp" +#include "duckdb/planner/expression/bound_constant_expression.hpp" #include "duckdb/planner/expression/bound_reference_expression.hpp" #include "duckdb/planner/operator/logical_copy_to_file.hpp" #include "duckdb/parallel/base_pipeline_event.hpp" #include "duckdb/parallel/task_executor.hpp" #include "duckdb/main/settings.hpp" +#include "duckdb/execution/operator/persistent/copy_batch_slicer.hpp" #include "duckdb/execution/operator/persistent/copy_output_lifecycle.hpp" #include "fmt/format.h" @@ -86,15 +92,14 @@ using vector_of_value_map_t = unordered_map, T, VectorOfValuesHash //===--------------------------------------------------------------------===// struct GlobalFileState { public: - explicit GlobalFileState(unique_ptr data_p, const string &path_p, idx_t lifecycle_file_index_p) - : data(std::move(data_p)), path(path_p), lifecycle_file_index(lifecycle_file_index_p), num_batches(0) { + explicit GlobalFileState(unique_ptr data_p, const string &path_p) + : data(std::move(data_p)), path(path_p), num_batches(0) { } public: annotated_mutex lock; unique_ptr data; const string path; - const idx_t lifecycle_file_index; idx_t num_batches DUCKDB_GUARDED_BY(lock); }; @@ -109,6 +114,8 @@ struct PendingFileState { struct PartitionDirectory { string path; vector directories; + //! The directory relative to the COPY target, with "/" separators + string relative_path; }; enum class CopyDirectoryState : uint8_t { PENDING, COMPLETE, FAILED }; @@ -315,6 +322,13 @@ class CopyFileLifecycleExecutor { max_pending_tasks = MaxValue(MIN_PENDING_TASKS, (async_threads + regular_threads) * 4); } + ~CopyFileLifecycleExecutor() { + // A queued task's Cancel reaches back into this object (GetError, FinishTask). Join here, while every + // member is still alive, rather than leaving it to ~TaskExecutor, which runs after error_lock and error + // have already been destroyed. CancelAndDrain does not throw. + executor.CancelAndDrain(); + } + public: template void Schedule(shared_ptr job, CopyFileLifecycleWaitMode mode, FUNC &&task); @@ -323,6 +337,8 @@ class CopyFileLifecycleExecutor { void WorkOnTaskOrYield(); void FinishTask(); void PushError(const std::exception_ptr &error); + //! The first error pushed by a task, if any + std::exception_ptr GetError(); private: bool WorkOnTask(bool throw_error = true); @@ -341,8 +357,7 @@ class CopyFileLifecycleExecutor { class CopyFileLifecycleTaskFinishGuard { public: - CopyFileLifecycleTaskFinishGuard(TaskExecutor &executor_p, CopyFileLifecycleExecutor &lifecycle_p) - : executor(executor_p), lifecycle(lifecycle_p) { + explicit CopyFileLifecycleTaskFinishGuard(CopyFileLifecycleExecutor &lifecycle_p) : lifecycle(lifecycle_p) { } ~CopyFileLifecycleTaskFinishGuard() { @@ -352,28 +367,39 @@ class CopyFileLifecycleTaskFinishGuard { void Finish() { if (!finished) { lifecycle.FinishTask(); - executor.FinishTask(); finished = true; } } private: - TaskExecutor &executor; CopyFileLifecycleExecutor &lifecycle; bool finished = false; }; template -class CopyFileLifecycleTask : public Task { +class CopyFileLifecycleTask : public BaseExecutorTask { public: CopyFileLifecycleTask(TaskExecutor &executor_p, CopyFileLifecycleExecutor &lifecycle_p, shared_ptr job_p, FUNC task_p) - : executor(executor_p), lifecycle(lifecycle_p), job(std::move(job_p)), task(std::move(task_p)) { + : BaseExecutorTask(executor_p), lifecycle(lifecycle_p), job(std::move(job_p)), task(std::move(task_p)) { + } + + ~CopyFileLifecycleTask() override { + // Neither ExecuteTask nor Cancel ran, because the executor could not take the task at all. The job + // waiter and pending_tasks still have to be settled, or a waiter spins with nothing left to run. + if (settled) { + return; + } + try { + Cancel(); + } catch (...) { // NOLINT + } } public: - TaskExecutionResult Execute(TaskExecutionMode mode) override { - CopyFileLifecycleTaskFinishGuard finish_guard(executor, lifecycle); + void ExecuteTask() override { + settled = true; + CopyFileLifecycleTaskFinishGuard finish_guard(lifecycle); try { task(); if (!job->IsFinished()) { @@ -384,7 +410,20 @@ class CopyFileLifecycleTask : public Task { job->CompleteException(error); lifecycle.PushError(error); } - return TaskExecutionResult::TASK_FINISHED; + } + + void Cancel() override { + // the task is retired without running - settle the job, WaitForJob spins until it is finished + settled = true; + CopyFileLifecycleTaskFinishGuard finish_guard(lifecycle); + if (job->IsFinished()) { + return; + } + auto error = lifecycle.GetError(); + if (!error) { + error = std::make_exception_ptr(InternalException("COPY file task was cancelled before it could run")); + } + job->CompleteException(error); } string TaskType() const override { @@ -392,10 +431,11 @@ class CopyFileLifecycleTask : public Task { } private: - TaskExecutor &executor; CopyFileLifecycleExecutor &lifecycle; shared_ptr job; FUNC task; + //! Whether ExecuteTask or Cancel ran, so the destructor knows the job and the count are settled + bool settled = false; }; template @@ -403,14 +443,11 @@ void CopyFileLifecycleExecutor::Schedule(shared_ptr job, C FUNC &&task) { WaitForTaskSlot(mode); auto job_ref = job; + using TaskType = CopyFileLifecycleTask::type>; + auto lifecycle_task = make_uniq(executor, *this, std::move(job), std::forward(task)); + // past this point the task settles pending_tasks itself, on the execute path and on the cancel path ++pending_tasks; - try { - using TaskType = CopyFileLifecycleTask::type>; - executor.ScheduleTask(make_uniq(executor, *this, std::move(job), std::forward(task))); - } catch (...) { - --pending_tasks; - throw; - } + executor.ScheduleTask(std::move(lifecycle_task)); if (async_threads == 0) { WaitForJob(*job_ref, mode); } @@ -468,6 +505,7 @@ class CopyToFileGlobalState : public GlobalSinkState { public: const PhysicalCopyToFile &op; ClientContext &context; + //! Destroy after file states so their handles close before cleanup. CopyOutputLifecycle output_lifecycle; //! Lock guarding the global state @@ -526,6 +564,8 @@ class CopyToFileLocalState : public LocalSinkState { //! Current append batch (unpartitioned write) unique_ptr batch; ColumnDataAppendState batch_append_state; + //! Cuts chunks so BATCH_SIZE_BYTES is reachable + unique_ptr batch_slicer; //! Local state (partitioned write) unique_ptr partitioned_copy_local_state; @@ -594,7 +634,7 @@ class PartitionWriteLease { PartitionWriteInfo &Get() const { D_ASSERT(write_info); - return *write_info.get_mutable(); + return *write_info; } PartitionWriteInfo &operator*() const { @@ -609,7 +649,7 @@ class PartitionWriteLease { private: optional_ptr manager; - optional_ptr write_info; + mutable optional_ptr write_info; }; class PartitionWriteManager { @@ -642,6 +682,9 @@ class PartitionWriteManager { ReservationLock LockForReservation() DUCKDB_EXCLUDES(lock); PartitionFileStateReservation ReserveFileState(ReservationLock &reservation_lock, const vector &values, FileCreationReason reason) DUCKDB_NO_THREAD_SAFETY_ANALYSIS; + //! Claims the directory of a partition, throws if another partition already owns it + void ClaimDirectory(ReservationLock &reservation_lock, const vector &values, + const string &directory) DUCKDB_NO_THREAD_SAFETY_ANALYSIS; FileStateHandle TryTakeInactiveFileState(const vector &values) DUCKDB_EXCLUDES(lock); vector TakeOpenFileStates() DUCKDB_EXCLUDES(lock); @@ -656,6 +699,9 @@ class PartitionWriteManager { ActiveWrites active_writes DUCKDB_GUARDED_BY(lock); vector_of_value_map_t previous_partitions DUCKDB_GUARDED_BY(lock); idx_t global_offset DUCKDB_GUARDED_BY(lock) = 0; + //! The claimed directory of each partition, and the partition owning each directory + vector_of_value_map_t partition_directories DUCKDB_GUARDED_BY(lock); + unordered_map> directory_partitions DUCKDB_GUARDED_BY(lock); friend class PartitionWriteLease; }; @@ -720,8 +766,21 @@ struct PartitionedCopyFlushAction { struct PartitionedCopyBatchState { private: + //! Partition values can be NULL, so they are compared with IS NOT DISTINCT FROM semantics + static bool PartitionValuesEqual(const vector &left, const vector &right) { + if (left.size() != right.size()) { + return false; + } + for (idx_t i = 0; i < left.size(); i++) { + if (!Value::NotDistinctFrom(left[i], right[i])) { + return false; + } + } + return true; + } + void SetValues(vector values_p) { - D_ASSERT(!values || *values == values_p); + D_ASSERT(!values || PartitionValuesEqual(*values, values_p)); if (!values) { values = std::move(values_p); } @@ -793,7 +852,9 @@ struct PartitionedCopyBatchState { } const vector &Values() const { - D_ASSERT(values); + if (!values) { + throw InternalException("Partitioned copy batch has no partition values"); + } return *values; } @@ -1172,6 +1233,8 @@ class PartitionedCopyHashGroup { unique_ptr collection; //! The partition boundary mask (marks where partition key changes within the bin) ValidityMask partition_mask; + //! Row sizes computed during MASK for byte-limited batches. + vector row_sizes; //! Lock for stage transitions and task assignment mutable annotated_mutex lock; @@ -1215,6 +1278,8 @@ class PartitionedCopyHashGroup { atomic flushed; }; +enum class PartitionedCopyInputState : uint8_t { OPEN, CLOSED }; + //! Manages a partitioned COPY flush class PartitionedCopyState { public: @@ -1224,6 +1289,14 @@ class PartitionedCopyState { PartitionedCopyState(PartitionedCopy &partitioned_copy, unique_ptr global_sink_state); public: + bool CanSink() const { + return input_state.load(std::memory_order_relaxed) == PartitionedCopyInputState::OPEN; + } + + void CloseInput() { + input_state = PartitionedCopyInputState::CLOSED; + } + bool ShouldInitiateFlush(const idx_t &local_append_count); bool IsCombineComplete() const DUCKDB_REQUIRES(lock); void CreateTaskList() DUCKDB_REQUIRES(lock); @@ -1243,6 +1316,8 @@ class PartitionedCopyState { ParallelHyperLogLogGlobalState hll; //! Check if we should flush once a thread's append count exceeds this value atomic next_flush_check; + //! Closed states accept no more input, including while waiting for the previous flush + atomic input_state {PartitionedCopyInputState::OPEN}; //! Sink management unique_ptr global_sink_state; @@ -1267,18 +1342,21 @@ class PartitionedCopyState { enum class PartitionedCopyCombineType : uint8_t { DURING_SINK, DURING_PIPELINE_COMBINE }; //! Manages partitioned COPY -class PartitionedCopy { +class PartitionedCopy : public StateWithBlockableTasks { public: PartitionedCopy(const PhysicalCopyToFile &op, ClientContext &context, CopyToFileGlobalState ©_gstate); public: //! PhysicalOperator-like interface - void Sink(ExecutionContext &execution_context, DataChunk &chunk, PartitionedCopyLocalState &lstate, - InterruptState &interrupt_state); + SinkResultType Sink(ExecutionContext &execution_context, DataChunk &chunk, PartitionedCopyLocalState &lstate, + InterruptState &interrupt_state); void Combine(ExecutionContext &execution_context, PartitionedCopyLocalState &lstate, InterruptState &interrupt_state, PartitionedCopyCombineType combine_type); + SinkCombineResultType FinishSink(ExecutionContext &execution_context, PartitionedCopyLocalState &lstate, + InterruptState &interrupt_state); void Finalize(Pipeline &pipeline, Event &event, InterruptState &interrupt_state); void Flush(ExecutionContext &execution_context, InterruptState &interrupt_state); + void NotifyFlushProgress(); public: //! Partitioning-specific functions @@ -1316,8 +1394,11 @@ class PartitionedCopy { private: unique_ptr ConstructSortStrategy() const; - void CreateNextState(); - bool ShouldStopFlushing() const; + template + bool TryBlockOnFlush(ExecutionContext &execution_context, InterruptState &interrupt_state, idx_t observed_progress, + FUNC &&can_continue) DUCKDB_EXCLUDES(lock); + bool TryPrepareSink(ExecutionContext &execution_context, PartitionedCopyLocalState &lstate, + InterruptState &interrupt_state); bool RequiresSerializedPartitionWrites() const; void EnsureFreshPartitionFileForSortedRun(PartitionWriteInfo &write_info, const vector &values) DUCKDB_EXCLUDES(copy_gstate.lock); @@ -1349,17 +1430,20 @@ class PartitionedCopy { vector write_types; vector raw_columns; + //! Expression describing the directories of a partition's files relative to the COPY target: + //! PARTITION_PATH, or the hive layout by default. useful if partitions are registered in + //! locations that do not follow hive partition naming structures + unique_ptr partition_path; + //! Partition/sort strategy with PhysicalOperator-like interface const unique_ptr sort_strategy; - //! Lock for managing states (sinking_state, flushing_state, flushing flag) - mutable annotated_mutex lock; //! Whether a flushing state currently exists atomic flushing; + //! Progress used to register blocked producers without missing a wakeup + idx_t flush_progress DUCKDB_GUARDED_BY(lock) = 0; //! How many threads are active atomic locals; - //! How many threads did a combine - atomic combined; //! Whether Finalize has been called atomic finalized; @@ -1431,6 +1515,65 @@ class DelayedPartitionFlushGuard { //===--------------------------------------------------------------------===// // Utility Helpers //===--------------------------------------------------------------------===// +static bool UsePerPartitionFileOffsets(const PhysicalCopyToFile &op) { + // every partition has a directory of its own, unless all files are written to the COPY target + return op.hive_file_pattern || op.partition_path_expression; +} + +//! The hive layout, e.g. year=2024/month=1, as an expression over the partition values +static unique_ptr CreateHivePartitionPath(ClientContext &context, const PhysicalCopyToFile &op) { + if (op.partition_columns.empty()) { + return nullptr; + } + auto component_function = HivePartitionComponentFun::GetFunction(); + + FunctionBinder function_binder(context); + vector> components; + for (idx_t i = 0; i < op.partition_columns.size(); i++) { + auto col_idx = op.partition_columns[i]; + vector> children; + children.push_back(make_uniq(Value(op.names[col_idx].GetIdentifierName()))); + children.push_back(make_uniq(op.expected_types[col_idx], i)); + components.push_back(function_binder.BindScalarFunction(component_function, std::move(children))); + } + return function_binder.BindScalarFunction(PathJoinFun::GetFunction(), std::move(components)); +} + +//! e.g. "year=2024, month=NULL" +static string PartitionValuesToString(const PhysicalCopyToFile &op, const vector &values) { + string result; + for (idx_t i = 0; i < values.size(); i++) { + result += i > 0 ? ", " : ""; + result += op.names[op.partition_columns[i]].GetIdentifierName() + "=" + values[i].ToString(); + } + return result; +} + +static string EvaluatePartitionPath(ClientContext &context, const PhysicalCopyToFile &op, + const Expression &partition_path, const vector &values) { + D_ASSERT(values.size() == op.partition_columns.size()); + vector types; + for (auto &col_idx : op.partition_columns) { + types.push_back(op.expected_types[col_idx]); + } + DataChunk partition_values; + partition_values.Initialize(Allocator::Get(context), types, 1); + for (idx_t i = 0; i < values.size(); i++) { + partition_values.data[i].Append(values[i]); + } + partition_values.CheckCardinality(1); + + ExpressionExecutor executor(context, partition_path); + Vector result(LogicalType::VARCHAR, 1); + executor.ExecuteExpression(partition_values, result); + auto path = result.GetValue(0); + if (path.IsNull()) { + throw InvalidInputException("PARTITION_PATH evaluated to NULL for partition %s", + PartitionValuesToString(op, values)); + } + return StringValue::Get(path); +} + void CheckDirectory(FileSystem &fs, const string &file_path, CopyOverwriteMode overwrite_mode) { if (overwrite_mode == CopyOverwriteMode::COPY_OVERWRITE_OR_IGNORE || overwrite_mode == CopyOverwriteMode::COPY_APPEND) { @@ -1498,7 +1641,7 @@ CreateColumnStatistics(const case_insensitive_map_t state) { + unique_ptr state) { if (!finalize) { throw InternalException("COPY file lifecycle finalize requires a finalize callback"); } @@ -1506,7 +1649,6 @@ static void FinalizeLifecycleFileState(ClientContext &context, copy_to_finalize_ throw InternalException("COPY file lifecycle finalize reached an empty file state"); } finalize(context, bind_data, *state->data); - output_lifecycle.MarkFileFinalized(state->lifecycle_file_index); } void CopyFileLifecycleExecutor::WaitForJob(CopyFileLifecycleJob &job, CopyFileLifecycleWaitMode mode) { while (!job.IsFinished()) { @@ -1542,6 +1684,11 @@ void CopyFileLifecycleExecutor::PushError(const std::exception_ptr &error_p) { } } +std::exception_ptr CopyFileLifecycleExecutor::GetError() { + lock_guard guard(error_lock); + return error; +} + bool CopyFileLifecycleExecutor::WorkOnTask(bool throw_error) { shared_ptr task; if (!executor.GetTask(task)) { @@ -1763,6 +1910,24 @@ PartitionWriteManager::ReservationLock PartitionWriteManager::LockForReservation return ReservationLock(lock); } +void PartitionWriteManager::ClaimDirectory(ReservationLock &reservation_lock, const vector &values, + const string &directory) DUCKDB_NO_THREAD_SAFETY_ANALYSIS { + D_ASSERT(reservation_lock.guard.owns_lock()); + D_ASSERT(!directory.empty()); + auto claimed = partition_directories.find(values); + if (claimed != partition_directories.end() && claimed->second == directory) { + return; + } + auto owner = directory_partitions.find(directory); + if (owner != directory_partitions.end()) { + throw InvalidInputException("PARTITION_PATH puts partitions (%s) and (%s) in the same directory \"%s\"", + PartitionValuesToString(op, owner->second), PartitionValuesToString(op, values), + directory); + } + directory_partitions.emplace(directory, values); + partition_directories[values] = directory; +} + PartitionFileStateReservation PartitionWriteManager::ReserveFileState(ReservationLock &reservation_lock, const vector &values, FileCreationReason reason) DUCKDB_NO_THREAD_SAFETY_ANALYSIS { @@ -1777,7 +1942,7 @@ PartitionWriteManager::ReserveFileState(ReservationLock &reservation_lock, const } } - if (op.hive_file_pattern) { + if (UsePerPartitionFileOffsets(op)) { if (reason == FileCreationReason::SORTED_RUN_BOUNDARY || reason == FileCreationReason::ROTATION) { ++previous_partitions[values]; } @@ -1810,7 +1975,7 @@ FileStateHandle PartitionWriteManager::TakeInactiveFileStateLocked(ActiveWriteIt D_ASSERT(entry->second->active_writes == 0); auto file_state = std::move(entry->second->file_state); - if (op.hive_file_pattern) { + if (UsePerPartitionFileOffsets(op)) { ++previous_partitions[entry->first]; } active_writes.erase(entry); @@ -1866,6 +2031,9 @@ bool PartitionedCopyHashGroup::TryPrepareNextStage() { case PartitionedCopyStage::MATERIALIZE: if (materialized == blocks && collection.get()) { partition_mask.Initialize(count); + if (partitioned_copy.op.batch_size_bytes.IsValid()) { + row_sizes.resize(count); + } stage = PartitionedCopyStage::MASK; return true; } @@ -1957,6 +2125,7 @@ optional PartitionedCopyHashGroup::TryNextBatchTask() { // Uses entry-level validity mask iteration to skip 64 rows at a time (see ExecuteFlat in unary_executor.hpp) D_ASSERT(batch_row_idx <= batch_scan_state.next_row_index); const idx_t chunk_end = batch_scan_state.next_row_index; + const idx_t row_before = batch_row_idx; // Skip batch_row_idx itself if it's the start of the current partition const idx_t search_start = batch_row_idx + (batch_row_idx == task.begin_idx ? 1 : 0); if (search_start < chunk_end) { @@ -1988,13 +2157,23 @@ optional PartitionedCopyHashGroup::TryNextBatchTask() { batch_row_idx = chunk_end; } - // Did not find the next partition boundary, add chunk - auto &segment = segments[batch_scan_state.segment_index]; + const auto &batch_size_bytes = partitioned_copy.op.batch_size_bytes; + if (batch_size_bytes.IsValid()) { + const auto partition_end = batch_row_idx; + batch_row_idx = row_before; + // the next partition can start right here, then this batch has no rows to count + while (batch_row_idx < partition_end && current_batch_size_bytes < batch_size_bytes.GetIndex()) { + current_batch_size_bytes += row_sizes[batch_row_idx++]; + } + found_next_partition = found_next_partition && batch_row_idx == partition_end; + } else { + auto &segment = segments[batch_scan_state.segment_index]; + current_batch_size_bytes += segment->GetChunkAllocationSize(batch_scan_state.chunk_index - 1); + } + const auto current_batch_size = batch_row_idx - task.begin_idx; - current_batch_size_bytes += segment->GetChunkAllocationSize(batch_scan_state.chunk_index - 1); const CopyFunctionBatchAnalyzer batch_analyzer(current_batch_size, current_batch_size_bytes, - partitioned_copy.op.batch_size, - partitioned_copy.op.batch_size_bytes); + partitioned_copy.op.batch_size, batch_size_bytes); // Move to the next chunk if we exhausted this one if (batch_row_idx == batch_scan_state.next_row_index) { @@ -2127,6 +2306,18 @@ void PartitionedCopyHashGroup::Mask(const PartitionedCopyTask &task) { partition_mask.SetValidUnsafe(0); } + if (!row_sizes.empty()) { + CopyBatchSlicer batch_slicer(partitioned_copy.write_types, partitioned_copy.op.batch_size_bytes); + WindowCollectionChunkScanner scanner(*collection, partitioned_copy.write_columns, task.begin_idx); + for (idx_t block_idx = task.begin_idx; block_idx < task.end_idx && scanner.Scan(); block_idx++) { + auto sizes = batch_slicer.ComputeRowSizes(scanner.chunk).Values(); + auto row_idx = scanner.state.current_row_index; + for (auto entry : sizes) { + row_sizes[row_idx++] = entry.GetValue(); + } + } + } + // Only compare partition columns (not order columns) const auto key_count = partitioned_copy.op.partition_columns.size(); if (key_count == 0) { @@ -2478,6 +2669,7 @@ void PartitionedCopyState::ExecuteTask(ExecutionContext &execution_context, cons throw InternalException("Invalid PartitionedCopyStage in PartitionedCopyState::ExecuteTask"); } auto partitions_to_finalize = FinishTask(task); + partitioned_copy.NotifyFlushProgress(); for (const auto &values : partitions_to_finalize) { partitioned_copy.TryFinalizePartitionWrite(values); } @@ -2516,7 +2708,7 @@ vector> PartitionedCopyState::FinishTask(const PartitionedCopyTask PartitionedCopy::PartitionedCopy(const PhysicalCopyToFile &op_p, ClientContext &context_p, CopyToFileGlobalState ©_gstate_p) : op(op_p), context(context_p), copy_gstate(copy_gstate_p), partition_writes(op_p, context_p), - sort_strategy(ConstructSortStrategy()), flushing(false), locals(0), combined(0), finalized(false) { + sort_strategy(ConstructSortStrategy()), flushing(false), locals(0), finalized(false) { unordered_set part_col_set(op.partition_columns.begin(), op.partition_columns.end()); for (idx_t col_idx = 0; col_idx < op.expected_types.size(); col_idx++) { raw_columns.push_back(col_idx); @@ -2525,6 +2717,11 @@ PartitionedCopy::PartitionedCopy(const PhysicalCopyToFile &op_p, ClientContext & write_types.push_back(op.expected_types[col_idx]); } } + if (op.partition_path_expression) { + partition_path = op.partition_path_expression->Copy(); + } else if (op.hive_file_pattern) { + partition_path = CreateHivePartitionPath(context, op); + } } unique_ptr PartitionedCopy::ConstructSortStrategy() const { @@ -2540,18 +2737,6 @@ unique_ptr PartitionedCopy::ConstructSortStrategy() const { op.children.empty() ? 0 : op.children[0].get().estimated_cardinality); } -void PartitionedCopy::CreateNextState() { - auto global_sink_state = sort_strategy->GetGlobalSinkState(context); - annotated_lock_guard guard(lock); - D_ASSERT(!sinking_state); - sinking_state = make_shared_ptr(*this, std::move(global_sink_state)); -} - -bool PartitionedCopy::ShouldStopFlushing() const { - return !finalized.load(std::memory_order_relaxed) && - locals.load(std::memory_order_relaxed) == combined.load(std::memory_order_relaxed); -} - bool PartitionedCopy::RequiresSerializedPartitionWrites() const { // A full partition writer run must remain serialized when the run boundary has file-state semantics: // ORDER BY starts a fresh file per sorted run, and rotation can start a fresh file between batches. @@ -2569,38 +2754,100 @@ void PartitionedCopy::InitializeFlush() { return; // Nothing to do } + flushing_state->CloseInput(); flushing = true; } void PartitionedCopy::FinalizeState(PartitionedCopyState &state, InterruptState &interrupt_state) { D_ASSERT(state.combined == state.locals); + // The flush finalizes a state once all producers have combined. + D_ASSERT(!state.global_source_state); OperatorSinkFinalizeInput sort_strategy_finalize_input {*state.global_sink_state, interrupt_state}; - sort_strategy->Finalize(context, sort_strategy_finalize_input); + auto finalize_result = sort_strategy->Finalize(context, sort_strategy_finalize_input); + if (finalize_result == SinkFinalizeType::BLOCKED) { + // the flush runs the strategy's tasks itself, so there is nothing that could resume it + throw InternalException("PartitionedCopy cannot resume a blocked sort strategy finalize"); + } state.CreateTaskList(); } -void PartitionedCopy::Sink(ExecutionContext &execution_context, DataChunk &chunk, PartitionedCopyLocalState &lstate, - InterruptState &interrupt_state) { - // Create new sinking/local state if necessary - if (!lstate.current_state) { +void PartitionedCopy::NotifyFlushProgress() { + vector to_wake; + { + annotated_lock_guard guard(lock); + ++flush_progress; + to_wake = TakeBlockedTasks(); + } + // Waking outside the lock is safe: parking re-checks flush_progress under the lock + for (auto &blocked_sink : to_wake) { + blocked_sink.Callback(); + } +} + +template +bool PartitionedCopy::TryBlockOnFlush(ExecutionContext &execution_context, InterruptState &interrupt_state, + idx_t observed_progress, FUNC &&can_continue) { + context.InterruptCheck(); + Flush(execution_context, interrupt_state); + if (!interrupt_state.CanCallback()) { + copy_gstate.lifecycle_executor.WorkOnTaskOrYield(); + return false; + } + annotated_lock_guard guard(lock); + if (observed_progress != flush_progress || can_continue()) { + return false; + } + return BlockTask(interrupt_state); +} + +bool PartitionedCopy::TryPrepareSink(ExecutionContext &execution_context, PartitionedCopyLocalState &lstate, + InterruptState &interrupt_state) { + while (true) { + if (lstate.current_state && lstate.current_state->CanSink()) { + return true; + } + // A producer must combine into the active flush before it can wait for more input space. + Combine(execution_context, lstate, interrupt_state, PartitionedCopyCombineType::DURING_SINK); + + idx_t observed_progress; + bool joined_state = false; { - annotated_lock_guard global_guard(lock); - if (!sinking_state) { - auto global_sink_state = sort_strategy->GetGlobalSinkState(context); - sinking_state = make_shared_ptr(*this, std::move(global_sink_state)); + annotated_lock_guard guard(lock); + if (!lstate.current_state && (!sinking_state || sinking_state->CanSink())) { + if (!sinking_state) { + auto global_sink_state = sort_strategy->GetGlobalSinkState(context); + sinking_state = make_shared_ptr(*this, std::move(global_sink_state)); + } + lstate.current_state = sinking_state; + annotated_lock_guard state_guard(lstate.current_state->lock); + lstate.current_state->locals++; + joined_state = true; } - lstate.current_state = sinking_state; + observed_progress = flush_progress; + } + if (joined_state) { + lstate.sort_strategy_local_state = sort_strategy->GetLocalSinkState(execution_context); + sort_strategy->RegisterHyperLogLog(*lstate.sort_strategy_local_state, + lstate.current_state->hll.GetLocalState()); + lstate.append_count = 0; + return true; } - { - annotated_lock_guard state_guard(lstate.current_state->lock); - lstate.current_state->locals++; + auto can_continue = [&]() DUCKDB_REQUIRES(lock) { + return !sinking_state || sinking_state->CanSink() || + (lstate.current_state && flushing_state && + RefersToSameObject(*lstate.current_state, *flushing_state)); + }; + if (TryBlockOnFlush(execution_context, interrupt_state, observed_progress, can_continue)) { + return false; } + } +} - lstate.sort_strategy_local_state = sort_strategy->GetLocalSinkState(execution_context); - sort_strategy->RegisterHyperLogLog(*lstate.sort_strategy_local_state, - lstate.current_state->hll.GetLocalState()); - lstate.append_count = 0; +SinkResultType PartitionedCopy::Sink(ExecutionContext &execution_context, DataChunk &chunk, + PartitionedCopyLocalState &lstate, InterruptState &interrupt_state) { + if (!TryPrepareSink(execution_context, lstate, interrupt_state)) { + return SinkResultType::BLOCKED; } // Sink into sort strategy @@ -2609,17 +2856,19 @@ void PartitionedCopy::Sink(ExecutionContext &execution_context, DataChunk &chunk sort_strategy->Sink(execution_context, chunk, sort_strategy_sink_input); lstate.append_count += chunk.size(); - if (!flushing.load(std::memory_order_relaxed) && lstate.current_state->ShouldInitiateFlush(lstate.append_count)) { + if (lstate.current_state->CanSink() && lstate.current_state->ShouldInitiateFlush(lstate.append_count)) { annotated_lock_guard guard(lock); + lstate.current_state->CloseInput(); InitializeFlush(); } if (!flushing.load(std::memory_order_relaxed)) { - return; + return SinkResultType::NEED_MORE_INPUT; } Combine(execution_context, lstate, interrupt_state, PartitionedCopyCombineType::DURING_SINK); Flush(execution_context, interrupt_state); + return SinkResultType::NEED_MORE_INPUT; } void PartitionedCopy::Combine(ExecutionContext &execution_context, PartitionedCopyLocalState &lstate, @@ -2628,19 +2877,7 @@ void PartitionedCopy::Combine(ExecutionContext &execution_context, PartitionedCo return; } - if (combine_type == PartitionedCopyCombineType::DURING_PIPELINE_COMBINE) { - OperatorSinkCombineInput sort_strategy_combine_input {*lstate.current_state->global_sink_state, - *lstate.sort_strategy_local_state, interrupt_state}; - sort_strategy->Combine(execution_context, sort_strategy_combine_input); - - annotated_lock_guard guard(lstate.current_state->lock); - ++lstate.current_state->combined; - - return; - } - D_ASSERT(combine_type == PartitionedCopyCombineType::DURING_SINK); - - { + if (combine_type == PartitionedCopyCombineType::DURING_SINK) { annotated_lock_guard guard(lock); if (!flushing_state || !RefersToSameObject(*lstate.current_state, *flushing_state)) { return; // Not flushing anymore, or can't combine this state into the flushing state @@ -2652,16 +2889,36 @@ void PartitionedCopy::Combine(ExecutionContext &execution_context, PartitionedCo sort_strategy->Combine(execution_context, sort_strategy_combine_input); { - // Finalize if this is the last combine annotated_lock_guard guard(lstate.current_state->lock); - if (++lstate.current_state->combined == lstate.current_state->locals) { - FinalizeState(*lstate.current_state, interrupt_state); - } + ++lstate.current_state->combined; } // Reset local state lstate.sort_strategy_local_state.reset(); lstate.current_state.reset(); + NotifyFlushProgress(); +} + +SinkCombineResultType PartitionedCopy::FinishSink(ExecutionContext &execution_context, + PartitionedCopyLocalState &lstate, InterruptState &interrupt_state) { + Combine(execution_context, lstate, interrupt_state, PartitionedCopyCombineType::DURING_PIPELINE_COMBINE); + // Exhausted producers keep helping until the active flush finishes. + while (true) { + idx_t observed_progress; + { + annotated_lock_guard guard(lock); + if (!flushing) { + return SinkCombineResultType::FINISHED; + } + observed_progress = flush_progress; + } + auto can_finish = [&]() DUCKDB_REQUIRES(lock) { + return !flushing; + }; + if (TryBlockOnFlush(execution_context, interrupt_state, observed_progress, can_finish)) { + return SinkCombineResultType::BLOCKED; + } + } } class PartitionedCopyFinalizeTask : public ExecutorTask { @@ -2760,8 +3017,7 @@ void PartitionedCopy::Finalize(Pipeline &pipeline, Event &event, InterruptState } else { should_finalize_writes = false; if (sinking_state) { - annotated_lock_guard guard(sinking_state->lock); - FinalizeState(*sinking_state, interrupt_state); + sinking_state->CloseInput(); } auto partitioned_copy_finalize_event = make_shared_ptr(pipeline, *this); @@ -2804,15 +3060,8 @@ void PartitionedCopy::Flush(ExecutionContext &execution_context, InterruptState D_ASSERT(flushing_state_copy->global_source_state); } - if (ShouldStopFlushing()) { - return; // Avoid straggling threads during Combine - } - while (auto task = flushing_state_copy->TryAssignTask()) { flushing_state_copy->ExecuteTask(execution_context, *task, interrupt_state); - if (ShouldStopFlushing()) { - break; // Avoid straggling threads during Combine - } } if (!flushing_state_copy->HasCompleted()) { @@ -2827,8 +3076,12 @@ void PartitionedCopy::Flush(ExecutionContext &execution_context, InterruptState } flushing = false; flushing_state.reset(); - should_finalize_writes = finalized && !sinking_state; + if (!finalized && sinking_state && !sinking_state->CanSink()) { + InitializeFlush(); + } + should_finalize_writes = finalized && !sinking_state && !flushing_state; } + NotifyFlushProgress(); if (should_finalize_writes) { DrainDelayedPartitions(execution_context, interrupt_state); } @@ -2967,18 +3220,24 @@ unique_ptr PartitionedCopy::ProjectToWriteColumns(unique_p // Partition File Request Builder //===--------------------------------------------------------------------===// optional PartitionFileRequestBuilder::Build() { + auto &op = partitioned_copy.op; + auto &context = partitioned_copy.context; + // built before locking and reserving, as evaluating the partition path can throw + auto directory = BuildDirectory(op.GetTrimmedPath(context, op.file_path)); + auto reservation_lock = partitioned_copy.partition_writes.LockForReservation(); annotated_lock_guard global_guard(partitioned_copy.copy_gstate.lock); if (file_state) { return nullopt; } + if (op.partition_path_expression) { + // claimed before reserving, which can take evicted file states that must not get lost when this throws + partitioned_copy.partition_writes.ClaimDirectory(reservation_lock, values, directory.relative_path); + } reservation = partitioned_copy.partition_writes.ReserveFileState(reservation_lock, values, reason); - auto &op = partitioned_copy.op; - auto &context = partitioned_copy.context; auto &fs = FileSystem::GetFileSystem(context); - auto directory = BuildDirectory(op.GetTrimmedPath(context, op.file_path)); auto full_path = op.filename_pattern.CreateFilename(fs, directory.path, op.file_extension, reservation.offset); auto pending_file_state_open = partitioned_copy.copy_gstate.CreatePartitionFileStateOpenLocked(file_state, std::move(full_path), values); @@ -2993,25 +3252,43 @@ vector PartitionFileRequestBuilder::TakeFilesToFinalize() { } PartitionDirectory PartitionFileRequestBuilder::BuildDirectory(string path) const { - auto &fs = FileSystem::GetFileSystem(partitioned_copy.context); PartitionDirectory result; result.path = std::move(path); - if (partitioned_copy.op.hive_file_pattern) { - for (idx_t i = 0; i < partitioned_copy.op.partition_columns.size(); i++) { - const auto &partition_col_name = partitioned_copy.op.names[partitioned_copy.op.partition_columns[i]]; - const auto &partition_value = values[i]; - string p_dir; - p_dir += HivePartitioning::Escape(partition_col_name.GetIdentifierName()); - p_dir += "="; - if (partition_value.IsNull()) { - p_dir += HivePartitioning::DEFAULT_PARTITION_NAME; - } else { - p_dir += HivePartitioning::EscapeValue(partition_value.ToString()); - } - result.path = fs.JoinPath(result.path, p_dir); - result.directories.push_back(result.path); + if (!partitioned_copy.partition_path) { + return result; + } + auto &context = partitioned_copy.context; + auto &fs = FileSystem::GetFileSystem(context); + auto partition_path = EvaluatePartitionPath(context, partitioned_copy.op, *partitioned_copy.partition_path, values); + // This is a relative path fragment, not a file to resolve through the VFS. Resolving it + // would require the local filesystem even when the COPY target is remote. + auto parsed_path = Path::FromString(partition_path); + if (parsed_path.IsAbsolute() || FileSystem::IsRemoteFile(partition_path)) { + throw InvalidInputException("PARTITION_PATH must be relative to the COPY target, but got \"%s\"", + partition_path); + } + auto separator = fs.PathSeparator(result.path); + if (!FileSystem::IsRemoteFile(result.path)) { + partition_path = fs.ConvertSeparators(partition_path); + } + vector components; + for (auto &component : StringUtil::Split(partition_path, separator)) { + if (component.empty() || component == ".") { + continue; + } + if (component == "..") { + throw InvalidInputException("PARTITION_PATH cannot contain \"..\", but got \"%s\"", partition_path); } + components.push_back(component); + result.path = fs.JoinPath(result.path, component); + result.directories.push_back(result.path); } + if (components.empty()) { + // every partition needs a directory of its own + throw InvalidInputException("PARTITION_PATH evaluated to an empty path for partition %s", + PartitionValuesToString(partitioned_copy.op, values)); + } + result.relative_path = StringUtil::Join(components, "/"); return result; } @@ -3068,6 +3345,7 @@ void PartitionedCopy::FlushDelayedPartitionRun(const vector &values, Part auto batch = make_uniq(context, write_types); ColumnDataAppendState append_state; batch->InitializeAppend(append_state); + CopyBatchSlicer batch_slicer(write_types, op.batch_size_bytes); const auto flush_batch = [&]() { if (batch->Count() == 0) { @@ -3084,10 +3362,14 @@ void PartitionedCopy::FlushDelayedPartitionRun(const vector &values, Part }; while (collection.Scan(scan_state, scan_chunk)) { - batch->Append(append_state, scan_chunk); - const CopyFunctionBatchAnalyzer batch_analyzer(*batch, op.batch_size, op.batch_size_bytes); - if (batch_analyzer.MeetsFlushCriteria()) { - flush_batch(); + idx_t offset = 0; + while (offset < scan_chunk.size()) { + auto &slice = batch_slicer.Slice(scan_chunk, offset, *batch); + batch->Append(append_state, slice); + const CopyFunctionBatchAnalyzer batch_analyzer(*batch, op.batch_size, op.batch_size_bytes); + if (batch_analyzer.MeetsFlushCriteria()) { + flush_batch(); + } } } flush_batch(); @@ -3341,7 +3623,7 @@ void CopyToFileGlobalState::RegisterPendingFileStatePathLocked(PendingFileState } unique_ptr CopyToFileGlobalState::InitializeFileState(PendingFileState pending_file_state) { - auto lifecycle_file_index = output_lifecycle.RegisterFile(pending_file_state.output_path); + output_lifecycle.RegisterFile(pending_file_state.output_path); auto data = op.function.copy_to_initialize_global(context, *op.bind_data, pending_file_state.output_path); if (pending_file_state.written_file_info && pending_file_state.written_file_info->file_stats) { op.function.copy_to_get_written_statistics(context, *op.bind_data, *data, @@ -3351,7 +3633,7 @@ unique_ptr CopyToFileGlobalState::InitializeFileState(PendingFi op.function.initialize_operator(*data, op); } - return make_uniq(std::move(data), pending_file_state.output_path, lifecycle_file_index); + return make_uniq(std::move(data), pending_file_state.output_path); } void CopyToFileGlobalState::RegisterPrepareGlobalStateLocked(GlobalFileState &file_state) { @@ -3506,19 +3788,16 @@ void CopyToFileGlobalState::FinalizeFileState(FileStateHandle file_state) { auto finalize = op.function.copy_to_finalize; auto &context_ref = context; auto &bind_data = *op.bind_data; - auto &output_lifecycle_ref = output_lifecycle; try { - lifecycle_executor.Schedule( - finalize_job, CopyFileLifecycleWaitMode::DRAIN, - [finalize, &context_ref, &bind_data, &output_lifecycle_ref, state_holder]() mutable { - FinalizeLifecycleFileState(context_ref, finalize, bind_data, output_lifecycle_ref, - std::move(*state_holder)); - }); + lifecycle_executor.Schedule(finalize_job, CopyFileLifecycleWaitMode::DRAIN, + [finalize, &context_ref, &bind_data, state_holder]() mutable { + FinalizeLifecycleFileState(context_ref, finalize, bind_data, + std::move(*state_holder)); + }); } catch (...) { if (!finalize_job->IsFinished() && state_holder && *state_holder) { try { - FinalizeLifecycleFileState(context_ref, finalize, bind_data, output_lifecycle_ref, - std::move(*state_holder)); + FinalizeLifecycleFileState(context_ref, finalize, bind_data, std::move(*state_holder)); } catch (...) { } } @@ -3563,7 +3842,9 @@ CopyToFileLocalState::CopyToFileLocalState(const PhysicalCopyToFile &op_p, Execu if (op.partition_output) { ++gstate.partitioned_copy->locals; partitioned_copy_local_state = make_uniq(); + return; } + batch_slicer = make_uniq(op.expected_types, op.batch_size_bytes); } //===--------------------------------------------------------------------===// @@ -3599,9 +3880,6 @@ void PhysicalCopyToFile::MoveTmpFile(ClientContext &context, const string &tmp_f } string PhysicalCopyToFile::GetNonTmpFile(ClientContext &context, const string &tmp_file_path) { - auto &fs = FileSystem::GetFileSystem(context); - - auto path = StringUtil::GetFilePath(tmp_file_path); auto base = StringUtil::GetFileName(tmp_file_path); auto prefix = base.find("tmp_"); @@ -3609,7 +3887,7 @@ string PhysicalCopyToFile::GetNonTmpFile(ClientContext &context, const string &t base = base.substr(4); } - return fs.JoinPath(path, base); + return StringUtil::ReplaceFileName(tmp_file_path, base); } void PhysicalCopyToFile::ReturnStatistics(DataChunk &chunk, CopyToFileInfo &info) { @@ -3649,6 +3927,11 @@ bool PhysicalCopyToFile::Rotate() const { static bool PhysicalCopyRotateNow(const PhysicalCopyToFile &op, GlobalFileState &global_state) DUCKDB_REQUIRES(global_state.lock) { + if (global_state.num_batches == 0) { + // never rotate a file that holds no data yet - e.g. a CSV header or a Parquet header can exceed the + // file size limit by itself, and rotating would then create new empty files forever + return false; + } if (op.file_size_bytes.IsValid()) { return op.function.file_size_bytes(*global_state.data) >= op.file_size_bytes.GetIndex(); } @@ -3697,12 +3980,15 @@ SinkResultType PhysicalCopyToFile::Sink(ExecutionContext &context, DataChunk &ch // if we are only writing the file when there are rows to write we need to initialize here gstate.Initialize(); } - lstate.total_rows_copied += chunk.size(); - if (partition_output) { - gstate.partitioned_copy->Sink(context, chunk, *lstate.partitioned_copy_local_state, input.interrupt_state); - return SinkResultType::NEED_MORE_INPUT; + auto result = + gstate.partitioned_copy->Sink(context, chunk, *lstate.partitioned_copy_local_state, input.interrupt_state); + if (result != SinkResultType::BLOCKED) { + lstate.total_rows_copied += chunk.size(); + } + return result; } + lstate.total_rows_copied += chunk.size(); if (per_thread_output) { if (!lstate.global_file_state) { @@ -3713,16 +3999,20 @@ SinkResultType PhysicalCopyToFile::Sink(ExecutionContext &context, DataChunk &ch } auto &file_state = per_thread_output ? lstate.global_file_state : gstate.global_state; - if (!lstate.batch) { - lstate.batch = make_uniq(context.client, expected_types); - lstate.batch->InitializeAppend(lstate.batch_append_state); - } - lstate.batch->Append(lstate.batch_append_state, chunk); - - const CopyFunctionBatchAnalyzer batch_analyzer(*lstate.batch, batch_size, batch_size_bytes); - if (batch_analyzer.MeetsFlushCriteria()) { - lstate.batch_append_state.current_chunk_state.handles.clear(); - PrepareAndFlushBatch(context.client, gstate, file_state, gstate.create_file_state_fun, std::move(lstate.batch)); + idx_t offset = 0; + while (offset < chunk.size()) { + if (!lstate.batch) { + lstate.batch = make_uniq(context.client, expected_types); + lstate.batch->InitializeAppend(lstate.batch_append_state); + } + auto &slice = lstate.batch_slicer->Slice(chunk, offset, *lstate.batch); + lstate.batch->Append(lstate.batch_append_state, slice); + const CopyFunctionBatchAnalyzer batch_analyzer(*lstate.batch, batch_size, batch_size_bytes); + if (batch_analyzer.MeetsFlushCriteria()) { + lstate.batch_append_state.current_chunk_state.handles.clear(); + PrepareAndFlushBatch(context.client, gstate, file_state, gstate.create_file_state_fun, + std::move(lstate.batch)); + } } return SinkResultType::NEED_MORE_INPUT; @@ -3731,18 +4021,20 @@ SinkResultType PhysicalCopyToFile::Sink(ExecutionContext &context, DataChunk &ch SinkCombineResultType PhysicalCopyToFile::Combine(ExecutionContext &context, OperatorSinkCombineInput &input) const { auto &gstate = input.global_state.Cast(); auto &lstate = input.local_state.Cast(); + if (partition_output) { + if (lstate.total_rows_copied) { + gstate.rows_copied += lstate.total_rows_copied; + lstate.total_rows_copied = 0; + } + return gstate.partitioned_copy->FinishSink(context, *lstate.partitioned_copy_local_state, + input.interrupt_state); + } if (lstate.total_rows_copied == 0) { // no rows copied return SinkCombineResultType::FINISHED; } gstate.rows_copied += lstate.total_rows_copied; - if (partition_output) { - gstate.partitioned_copy->Combine(context, *lstate.partitioned_copy_local_state, input.interrupt_state, - PartitionedCopyCombineType::DURING_PIPELINE_COMBINE); - return SinkCombineResultType::FINISHED; - } - if (per_thread_output) { if (lstate.batch) { PrepareAndFlushBatch(context.client, gstate, lstate.global_file_state, gstate.create_file_state_fun, diff --git a/src/duckdb/src/execution/operator/persistent/physical_delete.cpp b/src/duckdb/src/execution/operator/persistent/physical_delete.cpp index 6bc679ea6..366e10da9 100644 --- a/src/duckdb/src/execution/operator/persistent/physical_delete.cpp +++ b/src/duckdb/src/execution/operator/persistent/physical_delete.cpp @@ -2,11 +2,13 @@ #include "duckdb/catalog/catalog_entry/duck_table_entry.hpp" #include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp" +#include "duckdb/common/algorithm.hpp" #include "duckdb/common/mutex.hpp" #include "duckdb/common/types/column/column_data_collection.hpp" #include "duckdb/common/vector_size.hpp" #include "duckdb/execution/expression_executor.hpp" #include "duckdb/execution/index/bound_index.hpp" +#include "duckdb/execution/row_id_deduplicator.hpp" #include "duckdb/storage/data_table.hpp" #include "duckdb/storage/table/delete_state.hpp" #include "duckdb/storage/table/scan_state.hpp" @@ -29,7 +31,7 @@ PhysicalDelete::PhysicalDelete(PhysicalPlan &physical_plan, vector class DeleteGlobalState : public GlobalSinkState { public: explicit DeleteGlobalState(ClientContext &context, const vector &return_types, DuckTableEntry &table, - const vector> &bound_constraints) + const vector> &bound_constraints, bool return_chunk) : deleted_count(0), return_collection(context, return_types), has_unique_indexes(false) { // We need to append deletes to the local delete-ART. auto &storage = table.GetStorage(); @@ -37,17 +39,20 @@ class DeleteGlobalState : public GlobalSinkState { storage.InitializeLocalStorage(delete_index_append_state, table, context, bound_constraints); has_unique_indexes = true; } + if (return_chunk) { + deleted_rows = make_uniq(context, vector {LogicalType::ROW_TYPE}); + } } - //! Protects deleted_row_ids and return_collection Combine() + //! Protects deleted_rows and return_collection Combine() annotated_mutex return_lock; //! Conservatively protect delete-index maintenance (unique indexes) annotated_mutex index_lock; atomic deleted_count; ColumnDataCollection return_collection; - //! Global set of deleted row_ids (for cross-thread dedup in Sink; only accessed under return_lock) - unordered_set deleted_row_ids DUCKDB_GUARDED_BY(return_lock); + //! Global deleted row_ids (for cross-thread dedup in Sink; only accessed under return_lock) + unique_ptr deleted_rows DUCKDB_GUARDED_BY(return_lock); LocalAppendState delete_index_append_state; bool has_unique_indexes; }; @@ -69,9 +74,10 @@ class DeleteLocalState : public LocalSinkState { if (return_chunk) { // Collect RETURNING rows per-thread, merge into global in Combine() return_collection = make_uniq(context, return_types); - // Per-thread set of deleted row_ids (filters intra-thread duplicates lock-free) - deleted_row_ids = make_uniq>(); - // Reusable buffer for global-pass selection to avoid per-chunk allocation + // Per-thread deleted row_ids (filters intra-thread duplicates lock-free) + deleted_rows = make_uniq(context, vector {LogicalType::ROW_TYPE}); + // Reusable buffers for the global pass to avoid per-chunk allocation + global_sel.Initialize(STANDARD_VECTOR_SIZE); final_sel.Initialize(STANDARD_VECTOR_SIZE); } } @@ -80,8 +86,9 @@ class DeleteLocalState : public LocalSinkState { DataChunk delete_chunk; unique_ptr delete_state; unique_ptr return_collection; - unique_ptr> deleted_row_ids; - //! Reusable selection for global row_id pass (avoids allocation in Sink hot path) + unique_ptr deleted_rows; + //! Reusable selections for the global row_id pass (avoids allocation in Sink hot path) + SelectionVector global_sel; SelectionVector final_sel; }; @@ -106,72 +113,107 @@ SinkResultType PhysicalDelete::Sink(ExecutionContext &context, DataChunk &chunk, idx_t delete_count = chunk.size(); Vector delete_row_ids(Vector::Ref(row_ids)); - // If RETURNING is enabled, deduplicate row_ids: per-thread set (lock-free) then global set (batched lock). + // If RETURNING is enabled, deduplicate row_ids: per-thread pass (lock-free) then global pass (batched lock). + // Both passes use buffer-managed hash tables so the set of deleted row ids counts against the memory limit. if (return_chunk) { - D_ASSERT(l_state.deleted_row_ids); - auto flat_ids = FlatVector::GetData(row_ids); - idx_t count = 0; - for (idx_t i = 0; i < chunk.size(); i++) { - auto row_id = flat_ids[i]; - if (l_state.deleted_row_ids->insert(row_id).second) { - delete_sel.set_index(count++, i); - } - } + D_ASSERT(l_state.deleted_rows); + idx_t count = l_state.deleted_rows->Register(row_ids, chunk.size(), delete_sel); if (count == 0) { return SinkResultType::NEED_MORE_INPUT; } + // the global pass only sees the rows that are new to this thread + Vector local_new_row_ids(Vector::Ref(row_ids)); + local_new_row_ids.Slice(row_ids, delete_sel, count); { annotated_lock_guard guard(g_state.return_lock); - idx_t final_count = 0; - unique_ptr large_sel; - SelectionVector *write_sel = (count <= STANDARD_VECTOR_SIZE) ? &l_state.final_sel : nullptr; - if (!write_sel) { - large_sel = make_uniq(chunk.size()); - write_sel = large_sel.get(); - } - for (idx_t i = 0; i < count; i++) { - auto orig_idx = delete_sel[i]; - auto row_id = flat_ids[orig_idx]; - if (g_state.deleted_row_ids.insert(row_id).second) { - write_sel->set_index(final_count++, orig_idx); - } + D_ASSERT(g_state.deleted_rows); + if (l_state.global_sel.Capacity() < count) { + l_state.global_sel.Initialize(count); + l_state.final_sel.Initialize(count); } + auto final_count = g_state.deleted_rows->Register(local_new_row_ids, count, l_state.global_sel); if (final_count == 0) { return SinkResultType::NEED_MORE_INPUT; } - delete_count = final_count; - if (write_sel == &l_state.final_sel) { - delete_sel.Initialize(l_state.final_sel.data(), l_state.final_sel.Capacity()); - } else { - delete_sel = std::move(*large_sel); + // map the selection over the locally new rows back to the input chunk + for (idx_t i = 0; i < final_count; i++) { + l_state.final_sel.set_index(i, delete_sel.get_index(l_state.global_sel.get_index(i))); } + delete_count = final_count; + delete_sel.Initialize(l_state.final_sel.data(), l_state.final_sel.Capacity()); } if (delete_count != chunk.size()) { delete_row_ids.Slice(row_ids, delete_sel, delete_count); } } - // Use columns from the input chunk - they were passed through from the scan - // return_columns maps storage_idx -> chunk_idx - // For RETURNING: all columns have valid indices - // For index-only: only indexed columns have valid indices (sparse mapping) - D_ASSERT(!return_columns.empty() && "return_columns should always be populated for RETURNING or unique indexes"); - for (idx_t i = 0; i < table.ColumnCount(); i++) { - if (return_columns[i] != DConstants::INVALID_INDEX) { - // Column was passed through from the scan - l_state.delete_chunk.data[i].Reference(chunk.data[return_columns[i]]); + // Plans serialized by pre-v2.0 clients carry no `return_columns` and do not pass the needed columns + // through the scan, so fetch them by row id instead. + const bool fetch_by_row_id = return_columns.empty(); + DataChunk fetch_chunk; + if (fetch_by_row_id) { + vector column_ids; + if (return_chunk) { + for (idx_t i = 0; i < table.ColumnCount(); i++) { + column_ids.emplace_back(i); + } } else { - // Column not in scan (sparse mapping for index-only case) - use NULL placeholder - l_state.delete_chunk.data[i].Reference(Value(types[i]), count_t(chunk.size())); + auto indexed_columns = table.GetDataTableInfo()->GetIndexes().GetUniqueIndexColumns(); + vector sorted_columns(indexed_columns.begin(), indexed_columns.end()); + std::sort(sorted_columns.begin(), sorted_columns.end()); + for (auto col : sorted_columns) { + column_ids.emplace_back(col); + } + } + vector column_types; + for (auto &col : column_ids) { + column_types.push_back(types[col.GetPrimaryIndex()]); + } + // Also fetch the row ids, since Fetch skips rows that are no longer visible. + column_ids.emplace_back(COLUMN_IDENTIFIER_ROW_ID); + column_types.push_back(LogicalType::ROW_TYPE); + + delete_row_ids.Flatten(); + fetch_chunk.Initialize(Allocator::Get(context.client), column_types, delete_count); + ColumnFetchState fetch_state; + table.Fetch(DuckTransaction::Get(context.client, table.db), fetch_chunk, column_ids, delete_row_ids, + delete_count, fetch_state); + delete_count = fetch_chunk.size(); + if (delete_count == 0) { + return SinkResultType::NEED_MORE_INPUT; + } + delete_row_ids.Reference(fetch_chunk.data.back()); + + idx_t fetch_idx = 0; + for (idx_t i = 0; i < table.ColumnCount(); i++) { + if (fetch_idx + 1 < column_ids.size() && column_ids[fetch_idx].GetPrimaryIndex() == i) { + l_state.delete_chunk.data[i].Reference(fetch_chunk.data[fetch_idx++]); + } else { + l_state.delete_chunk.data[i].Reference(Value(types[i]), count_t(delete_count)); + } + } + } else { + // Use columns from the input chunk - they were passed through from the scan + // return_columns maps storage_idx -> chunk_idx + // For RETURNING: all columns have valid indices + // For index-only: only indexed columns have valid indices (sparse mapping) + for (idx_t i = 0; i < table.ColumnCount(); i++) { + if (return_columns[i] != DConstants::INVALID_INDEX) { + // Column was passed through from the scan + l_state.delete_chunk.data[i].Reference(chunk.data[return_columns[i]]); + } else { + // Column not in scan (sparse mapping for index-only case) - use NULL placeholder + l_state.delete_chunk.data[i].Reference(Value(types[i]), count_t(chunk.size())); + } } } // Add virtual columns (e.g., rowid) after table columns for (idx_t i = table.ColumnCount(); i < l_state.delete_chunk.ColumnCount(); i++) { - l_state.delete_chunk.data[i].Reference(row_ids); + l_state.delete_chunk.data[i].Reference(fetch_by_row_id ? delete_row_ids : row_ids); } - // Slice down to the claimed rows (RETURNING only) - if (return_chunk && delete_count != chunk.size()) { + // Slice down to the claimed rows (RETURNING only); fetched rows are already limited to them + if (return_chunk && !fetch_by_row_id && delete_count != chunk.size()) { l_state.delete_chunk.Slice(delete_sel, delete_count); } @@ -205,14 +247,14 @@ SinkCombineResultType PhysicalDelete::Combine(ExecutionContext &, OperatorSinkCo if (!l_state.return_collection || l_state.return_collection->Count() == 0) { return SinkCombineResultType::FINISHED; } - // Merge per-thread return_collection into global (deleted_row_ids is only needed during Sink) + // Merge per-thread return_collection into global (deleted_rows is only needed during Sink) annotated_lock_guard guard(g_state.return_lock); g_state.return_collection.Combine(*l_state.return_collection); return SinkCombineResultType::FINISHED; } unique_ptr PhysicalDelete::GetGlobalSinkState(ClientContext &context) const { - return make_uniq(context, GetTypes(), tableref, bound_constraints); + return make_uniq(context, GetTypes(), tableref, bound_constraints, return_chunk); } unique_ptr PhysicalDelete::GetLocalSinkState(ExecutionContext &context) const { @@ -224,12 +266,13 @@ unique_ptr PhysicalDelete::GetLocalSinkState(ExecutionContext &c //===--------------------------------------------------------------------===// class DeleteSourceState : public GlobalSourceState { public: - explicit DeleteSourceState(const PhysicalDelete &op) { + explicit DeleteSourceState(const PhysicalDelete &op) : total_rows(1), rows_scanned(0) { if (op.return_chunk) { D_ASSERT(op.sink_state); auto &g = op.sink_state->Cast(); g.return_collection.InitializeScan(global_scan_state); max_threads = MaxValue(g.return_collection.ChunkCount(), 1); + total_rows = g.return_collection.Count(); } else { max_threads = 1; } @@ -241,6 +284,8 @@ class DeleteSourceState : public GlobalSourceState { ColumnDataParallelScanState global_scan_state; idx_t max_threads; + idx_t total_rows; + atomic rows_scanned; }; class DeleteLocalSourceState : public LocalSourceState { @@ -256,17 +301,27 @@ unique_ptr PhysicalDelete::GetLocalSourceState(ExecutionContex return make_uniq(); } +ProgressData PhysicalDelete::GetProgress(ClientContext &context, GlobalSourceState &gstate) const { + auto &state = gstate.Cast(); + ProgressData progress; + progress.total = double(MaxValue(state.total_rows, 1)); + progress.done = state.total_rows == 0 ? 1.0 : double(state.rows_scanned.load(std::memory_order_relaxed)); + return progress; +} + SourceResultType PhysicalDelete::GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const { auto &state = input.global_state.Cast(); auto &g = sink_state->Cast(); if (!return_chunk) { chunk.data[0].Append(Value::BIGINT(NumericCast(g.deleted_count.load()))); + state.rows_scanned.store(1, std::memory_order_relaxed); return SourceResultType::FINISHED; } auto &lstate = input.local_state.Cast(); g.return_collection.Scan(state.global_scan_state, lstate.local_scan_state, chunk); + state.rows_scanned.fetch_add(chunk.size(), std::memory_order_relaxed); return chunk.size() == 0 ? SourceResultType::FINISHED : SourceResultType::HAVE_MORE_OUTPUT; } diff --git a/src/duckdb/src/execution/operator/persistent/physical_export.cpp b/src/duckdb/src/execution/operator/persistent/physical_export.cpp index 28cca0224..c211b317b 100644 --- a/src/duckdb/src/execution/operator/persistent/physical_export.cpp +++ b/src/duckdb/src/execution/operator/persistent/physical_export.cpp @@ -71,6 +71,10 @@ static void WriteCopyStatement(FileSystem &fs, stringstream &ss, CopyInfo &info, for (auto ¬_null_column : exported_table.not_null_columns) { info.options["force_not_null"].push_back(not_null_column); } + // empty strings are written as quoted empty values, which must not be read back as NULL + if (info.options.find("allow_quoted_nulls") == info.options.end()) { + ss << ", allow_quoted_nulls false"; + } } for (auto ©_option : info.options) { if (copy_option.second.empty()) { diff --git a/src/duckdb/src/execution/operator/persistent/physical_insert.cpp b/src/duckdb/src/execution/operator/persistent/physical_insert.cpp index 7518e87bc..361468f39 100644 --- a/src/duckdb/src/execution/operator/persistent/physical_insert.cpp +++ b/src/duckdb/src/execution/operator/persistent/physical_insert.cpp @@ -118,12 +118,11 @@ unique_ptr PhysicalInsert::GetGlobalSinkState(ClientContext &co // CREATE TABLE AS D_ASSERT(!insert_table); auto &catalog = schema->catalog; - table = &catalog.CreateTable(catalog.GetCatalogTransaction(context), *schema.get_mutable(), *info) - ->Cast(); + table = &catalog.CreateTable(catalog.GetCatalogTransaction(context), *schema, *info)->Cast(); } else { D_ASSERT(insert_table); D_ASSERT(insert_table->IsDuckTable()); - table = insert_table.get_mutable(); + table = insert_table; } auto result = make_uniq(context, GetTypes(), *table); return std::move(result); @@ -803,6 +802,7 @@ SinkFinalizeType PhysicalInsert::Finalize(Pipeline &pipeline, Event &event, Clie current_rows += data_table.GetOptimisticCollection(context, collection_index).collection->GetTotalRows(); if (current_rows >= row_group_size) { mergers.push_back(std::move(current_merger)); + current_merger.reset(); current_rows = 0; } } @@ -822,31 +822,44 @@ SinkFinalizeType PhysicalInsert::Finalize(Pipeline &pipeline, Event &event, Clie //===--------------------------------------------------------------------===// class InsertSourceState : public GlobalSourceState { public: - explicit InsertSourceState(const PhysicalInsert &op) { + explicit InsertSourceState(const PhysicalInsert &op) : total_rows(1), rows_scanned(0) { if (op.return_chunk) { D_ASSERT(op.sink_state); auto &g = op.sink_state->Cast(); g.return_collection.InitializeScan(scan_state); + total_rows = g.return_collection.Count(); } } ColumnDataScanState scan_state; + idx_t total_rows; + atomic rows_scanned; }; unique_ptr PhysicalInsert::GetGlobalSourceState(ClientContext &context) const { return make_uniq(*this); } +ProgressData PhysicalInsert::GetProgress(ClientContext &context, GlobalSourceState &gstate) const { + auto &state = gstate.Cast(); + ProgressData progress; + progress.total = double(MaxValue(state.total_rows, 1)); + progress.done = state.total_rows == 0 ? 1.0 : double(state.rows_scanned.load(std::memory_order_relaxed)); + return progress; +} + SourceResultType PhysicalInsert::GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const { auto &state = input.global_state.Cast(); auto &insert_gstate = sink_state->Cast(); if (!return_chunk) { chunk.data[0].Append(Value::BIGINT(NumericCast(insert_gstate.insert_count))); + state.rows_scanned.store(1, std::memory_order_relaxed); return SourceResultType::FINISHED; } insert_gstate.return_collection.Scan(state.scan_state, chunk); + state.rows_scanned.fetch_add(chunk.size(), std::memory_order_relaxed); return chunk.size() == 0 ? SourceResultType::FINISHED : SourceResultType::HAVE_MORE_OUTPUT; } diff --git a/src/duckdb/src/execution/operator/persistent/physical_merge_into.cpp b/src/duckdb/src/execution/operator/persistent/physical_merge_into.cpp index 57eea4527..32bc4ef54 100644 --- a/src/duckdb/src/execution/operator/persistent/physical_merge_into.cpp +++ b/src/duckdb/src/execution/operator/persistent/physical_merge_into.cpp @@ -77,6 +77,8 @@ class MergeActionGlobalSourceState : public GlobalSourceState { idx_t consumer_idx; idx_t max_threads; atomic unregistered {false}; + //! The exchange total grows while the producer outruns its estimate - keeps the scan progress monotonic + MonotonicProgress scan_progress; }; class MergeActionLocalSourceState : public LocalSourceState { @@ -110,7 +112,7 @@ ProgressData PhysicalMergeActionSource::GetProgress(ClientContext &context, Glob auto &gstate = gstate_p.Cast(); // we do not know how many rows the merge into will push into this action - the exchange reports the rows we have // consumed out of the rows that have been pushed so far - return gstate.exchange->ScanProgress(gstate.consumer_idx, estimated_cardinality); + return gstate.scan_progress.Update(gstate.exchange->ScanProgress(gstate.consumer_idx, estimated_cardinality)); } void PhysicalMergeActionSource::SourceFinished(ClientContext &context, GlobalSourceState &gstate_p) const { diff --git a/src/duckdb/src/execution/operator/persistent/physical_update.cpp b/src/duckdb/src/execution/operator/persistent/physical_update.cpp index 5880a12b5..a5fb85fe4 100644 --- a/src/duckdb/src/execution/operator/persistent/physical_update.cpp +++ b/src/duckdb/src/execution/operator/persistent/physical_update.cpp @@ -301,31 +301,44 @@ SinkCombineResultType PhysicalUpdate::Combine(ExecutionContext &context, Operato //===--------------------------------------------------------------------===// class UpdateSourceState : public GlobalSourceState { public: - explicit UpdateSourceState(const PhysicalUpdate &op) { + explicit UpdateSourceState(const PhysicalUpdate &op) : total_rows(1), rows_scanned(0) { if (op.return_chunk) { D_ASSERT(op.sink_state); auto &g = op.sink_state->Cast(); g.return_collection.InitializeScan(scan_state); + total_rows = g.return_collection.Count(); } } ColumnDataScanState scan_state; + idx_t total_rows; + atomic rows_scanned; }; unique_ptr PhysicalUpdate::GetGlobalSourceState(ClientContext &context) const { return make_uniq(*this); } +ProgressData PhysicalUpdate::GetProgress(ClientContext &context, GlobalSourceState &gstate) const { + auto &state = gstate.Cast(); + ProgressData progress; + progress.total = double(MaxValue(state.total_rows, 1)); + progress.done = state.total_rows == 0 ? 1.0 : double(state.rows_scanned.load(std::memory_order_relaxed)); + return progress; +} + SourceResultType PhysicalUpdate::GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const { auto &state = input.global_state.Cast(); auto &g = sink_state->Cast(); if (!return_chunk) { chunk.data[0].Append(Value::BIGINT(NumericCast(g.updated_count.load()))); + state.rows_scanned.store(1, std::memory_order_relaxed); return SourceResultType::FINISHED; } g.return_collection.Scan(state.scan_state, chunk); + state.rows_scanned.fetch_add(chunk.size(), std::memory_order_relaxed); return chunk.size() == 0 ? SourceResultType::FINISHED : SourceResultType::HAVE_MORE_OUTPUT; } diff --git a/src/duckdb/src/execution/operator/projection/physical_projection.cpp b/src/duckdb/src/execution/operator/projection/physical_projection.cpp index fcc5b7ba6..5dd02daa8 100644 --- a/src/duckdb/src/execution/operator/projection/physical_projection.cpp +++ b/src/duckdb/src/execution/operator/projection/physical_projection.cpp @@ -31,6 +31,16 @@ PhysicalProjection::PhysicalProjection(PhysicalPlan &physical_plan, vectorGetExpressionClass() != ExpressionClass::BOUND_REF) { + PhysicalOperator::InitializeOutputChunk(allocator, chunk); + return; + } + } + chunk.InitializeEmpty(types); +} + OperatorResultType PhysicalProjection::Execute(ExecutionContext &context, DataChunk &input, DataChunk &chunk, GlobalOperatorState &gstate, OperatorState &state_p) const { auto &state = state_p.Cast(); diff --git a/src/duckdb/src/execution/operator/projection/physical_tableinout_function.cpp b/src/duckdb/src/execution/operator/projection/physical_tableinout_function.cpp index 1e2564de5..cb82d41ac 100644 --- a/src/duckdb/src/execution/operator/projection/physical_tableinout_function.cpp +++ b/src/duckdb/src/execution/operator/projection/physical_tableinout_function.cpp @@ -33,11 +33,14 @@ class TableInOutGlobalState : public GlobalOperatorState { }; PhysicalTableInOutFunction::PhysicalTableInOutFunction(PhysicalPlan &physical_plan, vector types, - TableFunction function_p, unique_ptr bind_data_p, - vector column_ids_p, idx_t estimated_cardinality, - vector project_input_p) + BoundTableFunction function_p, + unique_ptr bind_data_p, + vector column_ids_p, vector projection_ids_p, + unique_ptr table_filters_p, + idx_t estimated_cardinality, vector project_input_p) : PhysicalOperator(physical_plan, PhysicalOperatorType::INOUT_FUNCTION, std::move(types), estimated_cardinality), function(std::move(function_p)), bind_data(std::move(bind_data_p)), column_ids(std::move(column_ids_p)), + projection_ids(std::move(projection_ids_p)), table_filters(std::move(table_filters_p)), projected_input(std::move(project_input_p)) { } @@ -45,7 +48,7 @@ unique_ptr PhysicalTableInOutFunction::GetOperatorState(Execution auto &gstate = op_state->Cast(); auto result = make_uniq(); if (function.init_local) { - TableFunctionInitInput input(bind_data.get(), column_ids, vector(), nullptr); + TableFunctionInitInput input(bind_data.get(), column_ids, projection_ids, table_filters.get()); result->local_state = function.init_local(context, input, gstate.global_state.get()); } if (!projected_input.empty()) { @@ -66,7 +69,7 @@ unique_ptr PhysicalTableInOutFunction::GetOperatorState(Execution unique_ptr PhysicalTableInOutFunction::GetGlobalOperatorState(ClientContext &context) const { auto result = make_uniq(); if (function.init_global) { - TableFunctionInitInput input(bind_data.get(), column_ids, vector(), nullptr); + TableFunctionInitInput input(bind_data.get(), column_ids, projection_ids, table_filters.get()); result->global_state = function.init_global(context, input); } return std::move(result); @@ -152,7 +155,7 @@ InsertionOrderPreservingMap PhysicalTableInOutFunction::ParamsToString() result[it.first] = it.second; } } else { - result["Name"] = function.name.GetIdentifierName(); + result["Name"] = function.GetName().GetIdentifierName(); } SetEstimatedCardinality(result, estimated_cardinality); return result; diff --git a/src/duckdb/src/execution/operator/scan/physical_column_data_scan.cpp b/src/duckdb/src/execution/operator/scan/physical_column_data_scan.cpp index 5608ca4b6..eb6d228f5 100644 --- a/src/duckdb/src/execution/operator/scan/physical_column_data_scan.cpp +++ b/src/duckdb/src/execution/operator/scan/physical_column_data_scan.cpp @@ -3,6 +3,7 @@ #include "duckdb/logging/log_type.hpp" #include "duckdb/logging/logger.hpp" +#include "duckdb/common/atomic.hpp" #include "duckdb/common/types/column/column_data_collection.hpp" #include "duckdb/common/types/column/column_data_collection_segment.hpp" #include "duckdb/execution/operator/aggregate/physical_hash_aggregate.hpp" @@ -70,27 +71,6 @@ PhysicalColumnDataScan::PhysicalColumnDataScan(PhysicalPlan &physical_plan, vect column_ids(GenerateColumnDataColumnIds(this->types)), cte_index(cte_index) { } -class PhysicalColumnDataGlobalScanState : public GlobalSourceState { -public: - PhysicalColumnDataGlobalScanState(ClientContext &context, const ColumnDataCollection &collection, - const vector &column_ids, const OperatorPartitionInfo &partition_info) - : batch_size(GetColumnDataScanBatchSize(context, collection.Count(), partition_info)), - max_threads(ColumnDataScanBatchCount(collection.Count(), batch_size)) { - collection.InitializeScan(global_scan_state, column_ids); - } - - idx_t MaxThreads() override { - return max_threads; - } - -public: - ColumnDataParallelScanState global_scan_state; - - const idx_t batch_size; - const idx_t max_threads; - idx_t next_batch_index = 0; -}; - struct ColumnDataScanEntry { ColumnDataScanEntry(idx_t chunk_index_p, idx_t segment_index_p, idx_t row_index_p) : chunk_index(chunk_index_p), segment_index(segment_index_p), row_index(row_index_p) { @@ -101,36 +81,63 @@ struct ColumnDataScanEntry { idx_t row_index; }; -class PhysicalColumnDataLocalScanState : public LocalSourceState { +class PhysicalColumnDataGlobalScanState : public GlobalSourceState { public: - bool AssignTask(const ColumnDataCollection &collection, PhysicalColumnDataGlobalScanState &gstate) { - entries.clear(); - entry_index = 0; - batch_index = DConstants::INVALID_INDEX; + PhysicalColumnDataGlobalScanState(ClientContext &context, const ColumnDataCollection &collection, + const vector &column_ids, const OperatorPartitionInfo &partition_info) { + collection.InitializeScan(global_scan_state, column_ids); + auto batch_size = GetColumnDataScanBatchSize(context, collection.Count(), partition_info); + auto chunk_count = collection.ChunkCount(); + scan_entries.reserve(chunk_count); + batch_offsets.reserve(MinValue(chunk_count, ColumnDataScanBatchCount(collection.Count(), batch_size)) + 1); + batch_offsets.push_back(0); - idx_t task_count = 0; - lock_guard l(gstate.global_scan_state.lock); - while (task_count < gstate.batch_size) { - idx_t chunk_index; - idx_t segment_index; - idx_t row_index; - if (!collection.NextScanIndex(gstate.global_scan_state.scan_state, chunk_index, segment_index, row_index)) { - break; + idx_t batch_rows = 0; + idx_t chunk_index; + idx_t segment_index; + idx_t row_index; + while (collection.NextScanIndex(global_scan_state.scan_state, chunk_index, segment_index, row_index)) { + scan_entries.emplace_back(chunk_index, segment_index, row_index); + batch_rows += collection.GetSegments()[segment_index]->chunk_data[chunk_index].count; + if (batch_rows >= batch_size) { + batch_offsets.push_back(scan_entries.size()); + batch_rows = 0; } - entries.emplace_back(chunk_index, segment_index, row_index); - task_count += collection.GetSegments()[segment_index]->chunk_data[chunk_index].count; } + if (batch_offsets.back() != scan_entries.size()) { + batch_offsets.push_back(scan_entries.size()); + } + } + + idx_t MaxThreads() override { + return MaxValue(batch_offsets.size() - 1, 1); + } - if (entries.empty()) { +public: + ColumnDataParallelScanState global_scan_state; + vector scan_entries; + //! Batch boundaries in scan_entries, including the end sentinel. + vector batch_offsets; + atomic next_batch_index {0}; +}; + +class PhysicalColumnDataLocalScanState : public LocalSourceState { +public: + bool AssignTask(PhysicalColumnDataGlobalScanState &gstate) { + // Metadata is immutable; the atomic ticket assigns exclusive ownership of an ordered batch. + batch_index = gstate.next_batch_index.fetch_add(1, std::memory_order_relaxed); + if (batch_index >= gstate.batch_offsets.size() - 1) { + batch_index = DConstants::INVALID_INDEX; return false; } - batch_index = gstate.next_batch_index++; + entry_index = gstate.batch_offsets[batch_index]; + entry_end = gstate.batch_offsets[batch_index + 1]; return true; } ColumnDataLocalScanState local_scan_state; - vector entries; idx_t entry_index = 0; + idx_t entry_end = 0; idx_t batch_index = DConstants::INVALID_INDEX; }; @@ -159,12 +166,12 @@ SourceResultType PhysicalColumnDataScan::GetDataInternal(ExecutionContext &conte OperatorSourceInput &input) const { auto &gstate = input.global_state.Cast(); auto &lstate = input.local_state.Cast(); - if (lstate.entry_index >= lstate.entries.size() && !lstate.AssignTask(*collection, gstate)) { + if (lstate.entry_index >= lstate.entry_end && !lstate.AssignTask(gstate)) { chunk.Reset(); return SourceResultType::FINISHED; } - auto &entry = lstate.entries[lstate.entry_index++]; + auto &entry = gstate.scan_entries[lstate.entry_index++]; if (column_ids.empty()) { chunk.Reset(); auto &chunk_data = collection->GetSegments()[entry.segment_index]->chunk_data[entry.chunk_index]; @@ -204,11 +211,13 @@ ProgressData PhysicalColumnDataScan::GetProgress(ClientContext &context, GlobalS return progress; } auto &state = gstate.Cast(); - lock_guard guard(state.global_scan_state.lock); - auto total = MaxValue(collection->Count(), 1); + auto next_batch = state.next_batch_index.load(std::memory_order_relaxed); + auto next_entry = + next_batch < state.batch_offsets.size() - 1 ? state.batch_offsets[next_batch] : state.scan_entries.size(); ProgressData progress; - progress.done = double(MinValue(state.global_scan_state.scan_state.next_row_index, total)); - progress.total = double(total); + progress.done = + double(next_entry < state.scan_entries.size() ? state.scan_entries[next_entry].row_index : collection->Count()); + progress.total = double(MaxValue(collection->Count(), 1)); return progress; } diff --git a/src/duckdb/src/execution/operator/scan/physical_table_scan.cpp b/src/duckdb/src/execution/operator/scan/physical_table_scan.cpp index fd2c1f669..75d3fd8fd 100644 --- a/src/duckdb/src/execution/operator/scan/physical_table_scan.cpp +++ b/src/duckdb/src/execution/operator/scan/physical_table_scan.cpp @@ -16,12 +16,13 @@ namespace duckdb { -PhysicalTableScan::PhysicalTableScan(PhysicalPlan &physical_plan, vector types, TableFunction function_p, - unique_ptr bind_data_p, vector returned_types_p, - vector column_ids_p, vector projection_ids_p, - vector names_p, unique_ptr table_filters_p, - idx_t estimated_cardinality, ExtraOperatorInfo extra_info, - vector parameters_p, virtual_column_map_t virtual_columns_p) +PhysicalTableScan::PhysicalTableScan(PhysicalPlan &physical_plan, vector types, + BoundTableFunction function_p, unique_ptr bind_data_p, + vector returned_types_p, vector column_ids_p, + vector projection_ids_p, vector names_p, + unique_ptr table_filters_p, idx_t estimated_cardinality, + ExtraOperatorInfo extra_info, vector parameters_p, + virtual_column_map_t virtual_columns_p) : PhysicalOperator(physical_plan, PhysicalOperatorType::TABLE_SCAN, std::move(types), estimated_cardinality), function(std::move(function_p)), bind_data(std::move(bind_data_p)), returned_types(std::move(returned_types_p)), @@ -66,11 +67,11 @@ class TableScanGlobalSourceState : public GlobalSourceState { idx_t max_threads = 0; PhysicalTableScanExecutionStrategy physical_table_scan_execution_strategy; + //! Combined dynamic table filters - before global_state, which references them during teardown + unique_ptr table_filters; unique_ptr global_state; bool in_out_final = false; DataChunk input_chunk; - //! Combined table filters, if we have dynamic filters - unique_ptr table_filters; optional_ptr GetTableFilters(const PhysicalTableScan &op) const { return table_filters ? table_filters.get() : op.table_filters.get(); @@ -268,7 +269,8 @@ OperatorPartitionData PhysicalTableScan::GetPartitionData(ExecutionContext &cont } string PhysicalTableScan::GetName() const { - return StringUtil::Upper(function.name + (function.extra_info.empty() ? "" : " " + function.extra_info)); + auto &extra_info = function.GetExtraInfo(); + return StringUtil::Upper(function.GetName().GetIdentifierName() + (extra_info.empty() ? "" : " " + extra_info)); } void AddProjectionNames(const ColumnIndex &index, const string &name, const LogicalType &type, string &result) { @@ -309,7 +311,7 @@ string PhysicalTableScan::GetFilterInfo(const TableFilterSet &filter_set) const for (auto &f : filter_set) { auto filter_idx = f.GetIndex(); auto &filter = f.Filter().Cast(); - if (filter_idx < names.size()) { + if (filter_idx < column_ids.size()) { if (!first_item) { filters_info += "\n"; } @@ -341,7 +343,7 @@ InsertionOrderPreservingMap PhysicalTableScan::ParamsToString() const { result[it.first] = it.second; } } else { - result["Function"] = StringUtil::Upper(function.name.GetIdentifierName()); + result["Function"] = StringUtil::Upper(function.GetName().GetIdentifierName()); } if (function.projection_pushdown) { string projections; diff --git a/src/duckdb/src/execution/operator/schema/physical_attach.cpp b/src/duckdb/src/execution/operator/schema/physical_attach.cpp index 3925a74ab..2bfccae25 100644 --- a/src/duckdb/src/execution/operator/schema/physical_attach.cpp +++ b/src/duckdb/src/execution/operator/schema/physical_attach.cpp @@ -1,8 +1,12 @@ #include "duckdb/execution/operator/schema/physical_attach.hpp" #include "duckdb/catalog/catalog.hpp" +#include "duckdb/common/types/value.hpp" +#include "duckdb/execution/operator/helper/launch_external_resource.hpp" #include "duckdb/main/attached_database.hpp" +#include "duckdb/main/database.hpp" #include "duckdb/main/database_manager.hpp" +#include "duckdb/main/external_resources_manager.hpp" #include "duckdb/main/extension_helper.hpp" #include "duckdb/parser/parsed_data/attach_info.hpp" #include "duckdb/storage/storage_extension.hpp" @@ -15,27 +19,85 @@ namespace duckdb { //===--------------------------------------------------------------------===// SourceResultType PhysicalAttach::GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const { - // parse the options auto &config = DBConfig::GetConfig(context.client); - // construct the options - AttachOptions options(info->options, config.options.access_mode); - - // get the name and path of the database - auto &name = info->name; - auto &path = info->path; - // preserve the verbatim path before extension-prefix stripping - options.original_path = path; - if (options.db_type.empty()) { - DBPathAndType::ExtractExtensionPrefix(path, options.db_type); - } - if (name.empty()) { - auto &fs = FileSystem::GetFileSystem(context.client); - name = Identifier(AttachedDatabase::ExtractDatabaseName(path, fs)); + + // Attach mutates the info (name resolution, path prefix stripping) and the external-resource path + // writes the provisioned endpoint into it, so work on a copy and leave the plan-owned info pristine. + auto attach_info = info->Copy(); + + // `NEW TEMPORARY` provisions a resource this attachment OWNS: a deleter is bound and DETACH reaps it. + // A bare identifier BORROWS a registered one -- no deleter, and DETACH leaves it for DESTROY. + LaunchedResource launched; + string resource_type, resource_name, borrowed_resource_name; + bool owns_resource = false; + if (attach_info->external_resource) { + auto &external_resource = *attach_info->external_resource; + if (!external_resource.reference_name.empty()) { + auto instance = ExternalResourcesManager::Get(context.client).Lookup(external_resource.reference_name); + if (!instance) { + throw InvalidInputException("no external resource named \"%s\" is registered", + external_resource.reference_name); + } + // Resolve the current endpoint from the registered handle (status); bind no deleter. Record the + // borrow so DESTROY refuses while this attachment is still pointing at the resource. + launched = ProvisionExternalResource(context.client, instance->type, {}, instance->name, instance->handle); + borrowed_resource_name = instance->name; + } else { + resource_type = external_resource.provider; + // The scoped resource is anonymous; label its log entries with the database it backs. + resource_name = attach_info->name.GetIdentifierName(); + launched = ProvisionExternalResource(context.client, external_resource.provider, external_resource.params, + resource_name); + owns_resource = true; + } } - // check ATTACH IF NOT EXISTS - auto &db_manager = DatabaseManager::Get(context.client); - db_manager.AttachDatabase(context.client, *info, options); + // Applying the resource, parsing its options, deriving the name and the attach can all throw, and by + // now the resource exists with nothing owning it. One guard covers the lot; a narrower one strands it. + auto reap_if_owned = [&]() { + if (owns_resource) { + ResourceDeleter(DatabaseInstance::GetDatabase(context.client), launched.deleter_function, + launched.deleter_payload, resource_type, resource_name) + .TryDelete(); + } + }; + try { + if (attach_info->external_resource) { + ApplyLaunchedResource(launched, *attach_info); + } + + // construct the options + AttachOptions options(attach_info->options, config.options.access_mode); + if (owns_resource) { + options.deleter_function = launched.deleter_function; + options.deleter_payload = launched.deleter_payload; + options.deleter_resource_type = resource_type; + options.deleter_resource_name = resource_name; + } + options.borrowed_resource_name = borrowed_resource_name; + + // get the name and path of the database + auto &name = attach_info->name; + auto &path = attach_info->path; + // preserve the verbatim path before extension-prefix stripping + options.original_path = path; + if (options.db_type.empty()) { + DBPathAndType::ExtractExtensionPrefix(path, options.db_type); + } + if (name.empty()) { + auto &fs = FileSystem::GetFileSystem(context.client); + name = Identifier(AttachedDatabase::ExtractDatabaseName(path, fs)); + } + + // check ATTACH IF NOT EXISTS + auto &db_manager = DatabaseManager::Get(context.client); + db_manager.AttachDatabase(context.client, *attach_info, options); + } catch (...) { + // Compensating teardown (best-effort): a resource we just PROVISIONED has nothing owning it yet. + // A borrowed (referenced) resource is never torn down here. + reap_if_owned(); + throw; + } return SourceResultType::FINISHED; } diff --git a/src/duckdb/src/execution/operator/schema/physical_create_index.cpp b/src/duckdb/src/execution/operator/schema/physical_create_index.cpp index ad27a4bfa..ba00a76ae 100644 --- a/src/duckdb/src/execution/operator/schema/physical_create_index.cpp +++ b/src/duckdb/src/execution/operator/schema/physical_create_index.cpp @@ -152,6 +152,7 @@ SinkFinalizeType PhysicalCreateIndex::Finalize(Pipeline &pipeline, Event &event, auto local_table_storage = local_storage.GetStorage(storage); auto check_mode = ConstraintCheckMode::DEFAULT; + idx_t index_oid = DConstants::INVALID_INDEX; if (!alter_table_info) { // Ensure that the index does not yet exist in the catalog. auto entry = @@ -168,15 +169,8 @@ SinkFinalizeType PhysicalCreateIndex::Finalize(Pipeline &pipeline, Event &event, D_ASSERT(index_entry); auto &index = index_entry->Cast(); index.initial_index_size = bound_index->GetInMemorySize(); - + index_oid = index.oid; } else { - // Ensure that there are no other indexes with that name on this table. - const auto &indexes = storage.GetDataTableInfo()->GetIndexes(); - if (indexes.Contains(info->GetIndexName())) { - throw CatalogException("an index with that name already exists for this table: %s", - SQLIdentifier(info->GetIndexName())); - } - // PRIMARY KEY columns cannot be NULL. if (info->constraint_type == IndexConstraintType::PRIMARY) { for (const auto &column_id : storage_ids) { @@ -186,13 +180,15 @@ SinkFinalizeType PhysicalCreateIndex::Finalize(Pipeline &pipeline, Event &event, } auto &constraint_info = alter_table_info->Cast(); check_mode = constraint_info.constraint->Cast().check_mode; + index_oid = DatabaseManager::Get(context).NextOid(); + constraint_info.constraint->SetBackingIndexOid(index_oid); auto &catalog = Catalog::GetCatalog(context, info->GetQualifiedName().Catalog()); catalog.Alter(context, *alter_table_info); } // Add the index to the storage. - auto index_entry = storage.GetDataTableInfo()->GetIndexes().AddIndex(std::move(bound_index), check_mode); + auto index_entry = storage.GetDataTableInfo()->GetIndexes().AddIndex(std::move(bound_index), index_oid, check_mode); if (local_table_storage) { // Existing transaction-local rows are verified when committing, so we only add a delete index. index_entry->InitializeLocalDeleteIndex(local_table_storage->delete_indexes); diff --git a/src/duckdb/src/execution/operator/set/physical_cte.cpp b/src/duckdb/src/execution/operator/set/physical_cte.cpp index 8173b1362..4e3222f33 100644 --- a/src/duckdb/src/execution/operator/set/physical_cte.cpp +++ b/src/duckdb/src/execution/operator/set/physical_cte.cpp @@ -44,6 +44,8 @@ class CTEConsumerGlobalSourceState : public GlobalSourceState { idx_t consumer_idx; idx_t max_threads; atomic unregistered {false}; + //! The exchange total grows while the producer outruns its estimate - keeps the scan progress monotonic + MonotonicProgress scan_progress; }; class CTEConsumerLocalSourceState : public LocalSourceState { @@ -110,7 +112,7 @@ bool PhysicalCTEConsumerSource::ParallelSource() const { ProgressData PhysicalCTEConsumerSource::GetProgress(ClientContext &context, GlobalSourceState &gstate) const { auto &state = gstate.Cast(); - return state.exchange->ScanProgress(state.consumer_idx, estimated_cardinality); + return state.scan_progress.Update(state.exchange->ScanProgress(state.consumer_idx, estimated_cardinality)); } void PhysicalCTEConsumerSource::SourceFinished(ClientContext &context, GlobalSourceState &gstate) const { @@ -606,11 +608,8 @@ ProgressData PhysicalCTE::GetSinkProgress(ClientContext &context, GlobalSinkStat if (!state.working_table_ref) { return ProgressData {0, 1, true}; } - auto count = double(state.ordered_data ? state.ordered_data->Count() : state.working_table_ref->Count()); - ProgressData progress; - progress.done = count; - progress.total = count + source_progress.total; - return progress; + // materializing the CTE is done once the source is exhausted + return source_progress; } } // namespace duckdb diff --git a/src/duckdb/src/execution/operator/set/physical_recursive_cte.cpp b/src/duckdb/src/execution/operator/set/physical_recursive_cte.cpp index 6e0e22fe0..b4ceec3ae 100644 --- a/src/duckdb/src/execution/operator/set/physical_recursive_cte.cpp +++ b/src/duckdb/src/execution/operator/set/physical_recursive_cte.cpp @@ -10,6 +10,8 @@ #include "duckdb/main/settings.hpp" +#include + namespace duckdb { RecursiveCTEPartialKeySpec::RecursiveCTEPartialKeySpec(vector indices_p, idx_t full_key_count) @@ -156,14 +158,10 @@ RecursiveCTEState::RecursiveCTEState(ClientContext &context, const PhysicalRecur source_distinct_rows.Initialize(Allocator::DefaultAllocator(), op.hash_key_types); source_aggregate_rows.Initialize(Allocator::DefaultAllocator(), op.aggregate_types); } - source_result.Initialize(Allocator::DefaultAllocator(), op.GetTypes()); if (op.using_key) { InitializeIntermediateAppend(); op.working_table->InitializeAppend(working_append_state); } - if (op.recurring_table) { - op.recurring_table->InitializeAppend(recurring_append_state); - } } unique_ptr RecursiveCTEState::CreateUsingKeyHashTable() const { @@ -1164,6 +1162,9 @@ class RecursiveCTEStateScanGlobalState : public GlobalSourceState { mutex lock; AggregateHTScanState scan_state; bool initialized = false; + //! The number of groups in the hash table and the number of groups scanned (for progress) + atomic total_groups {0}; + atomic scanned_groups {0}; }; class RecursiveCTEStateScanLocalState : public LocalSourceState { @@ -1233,11 +1234,13 @@ SourceResultType PhysicalRecursiveCTEStateScan::GetDataFromState(DataChunk &chun lock_guard guard(gstate.lock); if (!gstate.initialized) { recursive_state.GetHashTable().InitializeScan(gstate.scan_state); + gstate.total_groups = recursive_state.GetHashTable().Count(); gstate.initialized = true; } if (!recursive_state.GetHashTable().ScanGroups(gstate.scan_state, lstate.distinct_rows)) { return SourceResultType::FINISHED; } + gstate.scanned_groups.fetch_add(lstate.distinct_rows.size(), std::memory_order_relaxed); } if (lstate.distinct_rows.size() == 0) { continue; @@ -1258,6 +1261,15 @@ SourceResultType PhysicalRecursiveCTEStateScan::GetDataFromState(DataChunk &chun } } +ProgressData PhysicalRecursiveCTEStateScan::GetProgress(ClientContext &context, GlobalSourceState &gstate_p) const { + auto &gstate = gstate_p.Cast(); + idx_t total_groups = gstate.total_groups; + ProgressData progress; + progress.total = static_cast(MaxValue(total_groups, 1)); + progress.done = static_cast(MinValue(gstate.scanned_groups, total_groups)); + return progress; +} + InsertionOrderPreservingMap PhysicalRecursiveCTEStateScan::ParamsToString() const { InsertionOrderPreservingMap result; result["CTE Index"] = StringUtil::Format("%llu", cte_index.index); @@ -1350,30 +1362,38 @@ SinkCombineResultType PhysicalRecursiveCTE::Combine(ExecutionContext &context, O SourceResultType PhysicalRecursiveCTE::GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const { auto &gstate = sink_state->Cast(); - return gstate.GetData(context, chunk); + return gstate.GetData(context, chunk, input.global_state.Cast()); +} + +unique_ptr PhysicalRecursiveCTE::GetGlobalSourceState(ClientContext &context) const { + return make_uniq(); } -SourceResultType RecursiveCTEState::GetData(ExecutionContext &context, DataChunk &chunk) { +SourceResultType RecursiveCTEState::GetData(ExecutionContext &context, DataChunk &chunk, + RecursiveCTEGlobalSourceState &source) { if (source_phase == RecursiveCTESourcePhase::INITIAL) { if (op.using_key) { source_phase = RecursiveCTESourcePhase::RECURSING_KEY; } else { CurrentOutputTable().InitializeScan(scan_state); + source.StartIteration(CurrentOutputTable().Count()); source_phase = RecursiveCTESourcePhase::SCANNING_UNION; } } - return op.using_key ? GetUsingKeyData(context, chunk) : GetUnionData(context, chunk); + return op.using_key ? GetUsingKeyData(context, chunk, source) : GetUnionData(context, chunk, source); } -SourceResultType RecursiveCTEState::GetUsingKeyData(ExecutionContext &context, DataChunk &chunk) { +SourceResultType RecursiveCTEState::GetUsingKeyData(ExecutionContext &context, DataChunk &chunk, + RecursiveCTEGlobalSourceState &source) { if (metrics.Enabled()) { - return GetUsingKeyDataInternal(context, chunk); + return GetUsingKeyDataInternal(context, chunk, source); } - return GetUsingKeyDataInternal(context, chunk); + return GetUsingKeyDataInternal(context, chunk, source); } template -SourceResultType RecursiveCTEState::GetUsingKeyDataInternal(ExecutionContext &context, DataChunk &chunk) { +SourceResultType RecursiveCTEState::GetUsingKeyDataInternal(ExecutionContext &context, DataChunk &chunk, + RecursiveCTEGlobalSourceState &source) { D_ASSERT(op.using_key); while (true) { switch (source_phase) { @@ -1388,6 +1408,7 @@ SourceResultType RecursiveCTEState::GetUsingKeyDataInternal(ExecutionContext &co expected_new = op.working_table->Count(); if (expected_new == 0) { ht->InitializeScan(ht_scan_state); + source.StartFinalDrain(ht->Count()); source_phase = RecursiveCTESourcePhase::DRAINING_FINAL_KEY_STATE; break; } @@ -1405,6 +1426,7 @@ SourceResultType RecursiveCTEState::GetUsingKeyDataInternal(ExecutionContext &co const auto next_count = op.union_all ? intermediate_table.Count() : op.working_table->Count(); if (next_count == 0) { ht->InitializeScan(ht_scan_state); + source.StartFinalDrain(ht->Count()); source_phase = RecursiveCTESourcePhase::DRAINING_FINAL_KEY_STATE; } break; @@ -1417,6 +1439,7 @@ SourceResultType RecursiveCTEState::GetUsingKeyDataInternal(ExecutionContext &co continue; } AssembleStateRows(source_distinct_rows, source_aggregate_rows, chunk); + source.AddEmittedRows(chunk.size()); if constexpr (COLLECT_METRICS) { metrics.RecordFinalStateRows(chunk.size()); const auto drain_end = std::chrono::steady_clock::now(); @@ -1431,6 +1454,7 @@ SourceResultType RecursiveCTEState::GetUsingKeyDataInternal(ExecutionContext &co std::chrono::duration_cast(drain_end - drain_start).count())); } source_phase = RecursiveCTESourcePhase::FINISHED; + source.Finish(); break; } case RecursiveCTESourcePhase::FINISHED: @@ -1441,13 +1465,15 @@ SourceResultType RecursiveCTEState::GetUsingKeyDataInternal(ExecutionContext &co } } -SourceResultType RecursiveCTEState::GetUnionData(ExecutionContext &context, DataChunk &chunk) { +SourceResultType RecursiveCTEState::GetUnionData(ExecutionContext &context, DataChunk &chunk, + RecursiveCTEGlobalSourceState &source) { D_ASSERT(!op.using_key); while (chunk.size() == 0) { if (source_phase == RecursiveCTESourcePhase::SCANNING_UNION) { // scan any chunks we have collected so far CurrentOutputTable().Scan(scan_state, chunk); if (chunk.size() != 0) { + source.AddEmittedRows(chunk.size()); break; } } else if (source_phase == RecursiveCTESourcePhase::FINISHED) { @@ -1465,16 +1491,8 @@ SourceResultType RecursiveCTEState::GetUnionData(ExecutionContext &context, Data // After an iteration, we reset the recurring table // and fill it up with the new hash table rows for the next iteration. if (op.ref_recurring && current_output.Count() != 0) { - // we need to populate the recurring table from the intermediate table - // careful: we can not just use Combine here, because this destroys the intermediate table - // instead we need to scan and append to create a copy - // Note: as we are in the "normal" recursion case here, not the USING KEY case, - // we can just scan the intermediate table directly, instead of going through the HT - ColumnDataScanState recurring_scan_state; - current_output.InitializeScan(recurring_scan_state); - while (current_output.Scan(recurring_scan_state, source_result)) { - op.recurring_table->Append(recurring_append_state, source_result); - } + // copy since Combine would destroy the intermediate table + op.recurring_table->Append(current_output); } AdvanceIterationBuffers(); @@ -1514,16 +1532,75 @@ SourceResultType RecursiveCTEState::GetUnionData(ExecutionContext &context, Data // if not, we are done if (CurrentOutputTable().Count() == 0) { source_phase = RecursiveCTESourcePhase::FINISHED; + source.Finish(); break; } // set up the scan again CurrentOutputTable().InitializeScan(scan_state); + source.StartIteration(CurrentOutputTable().Count()); } } return chunk.size() == 0 ? SourceResultType::FINISHED : SourceResultType::HAVE_MORE_OUTPUT; } +void RecursiveCTEGlobalSourceState::StartIteration(idx_t row_count) { + lock_guard guard(iteration_lock); + if (iterations == 0) { + anchor_rows = row_count; + } + iterations++; + iteration_start_rows = emitted_rows.load(std::memory_order_relaxed); + previous_iteration_rows = iteration_rows; + iteration_rows = row_count; +} + +void RecursiveCTEGlobalSourceState::StartFinalDrain(idx_t row_count) { + lock_guard guard(iteration_lock); + draining = true; + iteration_start_rows = emitted_rows.load(std::memory_order_relaxed); + iteration_rows = row_count; +} + +ProgressData RecursiveCTEGlobalSourceState::GetProgress() { + if (finished.load(std::memory_order_relaxed)) { + return ProgressData {1.0, 1.0, false}; + } + auto emitted = static_cast(emitted_rows.load(std::memory_order_relaxed)); + double fraction; + { + lock_guard guard(iteration_lock); + if (draining) { + // the final state of a USING KEY recursion is emitted in one go - its size is known + auto total = static_cast(iteration_start_rows + iteration_rows); + fraction = total == 0 ? 1.0 : emitted / total; + } else { + // the total number of rows is unknown - converge towards 100% as rows are emitted, logarithmically in the + // number of rows relative to the first iteration (50% after one anchor's worth of rows, 67% after 3, 91% + // after ~1000) + auto anchor = static_cast(MaxValue(anchor_rows, 1)); + fraction = 1 - 1 / (1 + std::log2(1 + emitted / anchor)); + // if the iterations are shrinking, extrapolate the remaining rows as a geometric series + if (iteration_rows < previous_iteration_rows) { + auto current = static_cast(iteration_rows); + auto ratio = current / static_cast(previous_iteration_rows); + auto total = static_cast(iteration_start_rows) + current + current * ratio / (1 - ratio); + if (total > 0) { + fraction = MaxValue(fraction, emitted / total); + } + } + // we are not done until the recursion finishes + fraction = MinValue(fraction, 0.999); + } + } + // the estimate can drop when an iteration is larger than expected + return progress.Update(ProgressData {MinValue(fraction, 1.0), 1.0, false}); +} + +ProgressData PhysicalRecursiveCTE::GetProgress(ClientContext &context, GlobalSourceState &gstate) const { + return gstate.Cast().GetProgress(); +} + vector> PhysicalRecursiveCTE::GetSources() const { return {*this}; } diff --git a/src/duckdb/src/execution/operator/set/physical_recursive_cte_runtime.cpp b/src/duckdb/src/execution/operator/set/physical_recursive_cte_runtime.cpp index 247f9f009..703fe00f5 100644 --- a/src/duckdb/src/execution/operator/set/physical_recursive_cte_runtime.cpp +++ b/src/duckdb/src/execution/operator/set/physical_recursive_cte_runtime.cpp @@ -261,9 +261,17 @@ class RecursiveCTEPipelineEvent : public BasePipelineEvent { pipeline->ResetForReschedule(false); } - auto max_threads = GetRecursivePipelineMaxThreads(*pipeline, worker_limit); + SchedulePrepared(GetRecursivePipelineMaxThreads(*pipeline, worker_limit)); + } + + // The caller has prepared the pipeline and determined its worker count. + void SchedulePrepared(idx_t max_threads) { + D_ASSERT(max_threads > 0); + D_ASSERT(max_threads <= worker_limit); + prepared_for_schedule = true; if (state.GetMetrics().Enabled()) { state.GetMetrics().RecordTasks(max_threads); + state.GetEpochMetrics().RecordPipelineWorkers(max_threads); } state.GetScheduler().PrepareExecutors(*pipeline, max_threads); auto &executors = state.GetScheduler().GetExecutors(*pipeline); @@ -360,11 +368,14 @@ BuildRecursivePipelineSchedulePlan(const vector> &meta_ // 2. a per-iteration Event graph for the multi-threaded path auto plan = make_uniq(); reference_map_t pipeline_metric_types; + reference_set_t invariant_build_pipelines; for (auto &meta_pipeline : meta_pipelines) { auto metric_type = RecursiveCTEPipelineMetricType::RECURSIVE; + bool is_invariant_build = false; if (recursive_cte.invariant_meta_pipelines.find(*meta_pipeline) != recursive_cte.invariant_meta_pipelines.end()) { auto sink = meta_pipeline->GetSink(); + is_invariant_build = !sink || sink->type != PhysicalOperatorType::CTE; metric_type = sink && sink->type == PhysicalOperatorType::CTE ? RecursiveCTEPipelineMetricType::INVARIANT_CTE_MATERIALIZATION : RecursiveCTEPipelineMetricType::INVARIANT_BUILD; @@ -373,6 +384,9 @@ BuildRecursivePipelineSchedulePlan(const vector> &meta_ meta_pipeline->GetPipelines(pipelines, false); for (auto &pipeline : pipelines) { pipeline_metric_types.emplace(*pipeline, metric_type); + if (is_invariant_build) { + invariant_build_pipelines.insert(*pipeline); + } } } // Retained invariant meta-pipelines are absent after the first epoch, so build the induced schedule for @@ -403,7 +417,8 @@ BuildRecursivePipelineSchedulePlan(const vector> &meta_ recursive_stage_indices[stage_idx] = plan->stages.size(); auto metric_type = pipeline_metric_types.find(pipeline); D_ASSERT(metric_type != pipeline_metric_types.end()); - plan->stages.emplace_back(stage.type, pipeline, has_source_tasks, metric_type->second); + plan->stages.emplace_back(stage.type, pipeline, has_source_tasks, metric_type->second, + invariant_build_pipelines.find(pipeline) != invariant_build_pipelines.end()); } for (idx_t stage_idx = 0; stage_idx < schedule->stages.size(); stage_idx++) { const auto recursive_stage_idx = recursive_stage_indices[stage_idx]; @@ -741,13 +756,18 @@ static void ScheduleRecursivePlan(const RecursiveCTEPipelineSchedulePlan &plan, pipeline.get().ResetSource(true); } + const auto configured_threads = + TaskScheduler::GetScheduler(state.GetOperator().recursive_meta_pipeline->GetExecutor().context) + .NumberOfThreads(); events.reserve(plan.stages.size()); for (auto &stage : plan.stages) { auto pipeline = stage.pipeline.get().shared_from_this(); switch (stage.type) { case PipelineScheduleStageType::EXECUTE: events.push_back(make_shared_ptr( - std::move(pipeline), state, parallelism.WorkerCount(stage.has_source_tasks), stage.metric_type)); + std::move(pipeline), state, + stage.is_invariant_build ? configured_threads : parallelism.WorkerCount(stage.has_source_tasks), + stage.metric_type)); break; case PipelineScheduleStageType::PREPARE_FINISH: events.push_back(make_shared_ptr(std::move(pipeline))); @@ -807,7 +827,17 @@ static void ExecuteRecursiveInlinePlan(RecursiveCTEState &state, Executor &execu switch (stage.type) { case PipelineScheduleStageType::EXECUTE: { pipeline.ResetForReschedule(false); - auto max_threads = GetRecursivePipelineMaxThreads(pipeline, 1); + // Invariant builds have independent source work even when the recursive frontier is tiny. + const auto worker_limit = + stage.is_invariant_build ? TaskScheduler::GetScheduler(executor.context).NumberOfThreads() : idx_t(1); + const auto max_threads = GetRecursivePipelineMaxThreads(pipeline, worker_limit); + if (max_threads > 1) { + auto event = make_shared_ptr(pipeline.shared_from_this(), state, + worker_limit, stage.metric_type); + event->SchedulePrepared(max_threads); + WaitForRecursiveEvent(executor, *event); + break; + } D_ASSERT(max_threads == 1); state.GetScheduler().PrepareExecutors(pipeline, max_threads); auto &executors = state.GetScheduler().GetExecutors(pipeline); diff --git a/src/duckdb/src/execution/operator/set/physical_recursive_cte_state.cpp b/src/duckdb/src/execution/operator/set/physical_recursive_cte_state.cpp index a1d2e0d89..3fe0427d9 100644 --- a/src/duckdb/src/execution/operator/set/physical_recursive_cte_state.cpp +++ b/src/duckdb/src/execution/operator/set/physical_recursive_cte_state.cpp @@ -196,6 +196,12 @@ void RecursiveCTEEpochMetrics::RecordDistinctGrouping(idx_t candidate_rows, idx_ distinct_grouping_work_ns.fetch_add(elapsed_ns); } +void RecursiveCTEEpochMetrics::RecordPipelineWorkers(idx_t workers) { + auto current = max_pipeline_workers.load(); + while (current < workers && !max_pipeline_workers.compare_exchange_weak(current, workers)) { + } +} + void RecursiveCTEEpochMetrics::RecordPipelineExecution(RecursiveCTEPipelineMetricType metric_type, idx_t elapsed_ns) { switch (metric_type) { case RecursiveCTEPipelineMetricType::RECURSIVE: @@ -355,6 +361,7 @@ void RecursiveCTEMetrics::LogEpochSummary(const RecursiveCTEEpochMetrics &epoch_ {"frontier_rows_max", to_string(epoch_metrics.frontier_rows.maximum)}, {"workers_p50_upper_bound", to_string(epoch_metrics.workers.MedianUpperBound())}, {"workers_max", to_string(epoch_metrics.workers.maximum)}, + {"max_pipeline_workers", to_string(epoch_metrics.max_pipeline_workers.load())}, {"tasks_p50_upper_bound", to_string(epoch_metrics.tasks.MedianUpperBound())}, {"tasks_max", to_string(epoch_metrics.tasks.maximum)}, {"elapsed_us_p50_upper_bound", to_string(epoch_metrics.elapsed_us.MedianUpperBound())}, diff --git a/src/duckdb/src/execution/operator/set/physical_union.cpp b/src/duckdb/src/execution/operator/set/physical_union.cpp index 3be025eff..78551361b 100644 --- a/src/duckdb/src/execution/operator/set/physical_union.cpp +++ b/src/duckdb/src/execution/operator/set/physical_union.cpp @@ -83,10 +83,15 @@ void PhysicalUnion::BuildPipelines(Pipeline ¤t, MetaPipeline &meta_pipelin auto dependency_type = order_matters ? PipelineDependencyType::REQUIRED : PipelineDependencyType::OPTIONAL_DEPENDENCY; dependencies = meta_pipeline.AddDependenciesFrom(union_pipeline, union_pipeline, false, dependency_type); + if (!allow_out_of_order) { + // CreateUnionPipeline already orders this pipeline after current. Include current when + // propagating that ordering to the child sink pipelines as well. + dependencies.push_back(current.shared_from_this()); + } // we also add dependencies if the LHS child can saturate all available threads // in that case, we recursively make all RHS children depend on the LHS. // This prevents breadth-first plan evaluation - if (can_saturate_threads) { + if (!allow_out_of_order || can_saturate_threads) { last_child_ptr = meta_pipeline.GetLastChild(); } } @@ -96,8 +101,10 @@ void PhysicalUnion::BuildPipelines(Pipeline ¤t, MetaPipeline &meta_pipelin children[i].get().BuildPipelines(union_pipeline, meta_pipeline); if (last_child_ptr) { - // the pointer was set, set up the dependencies - meta_pipeline.AddRecursiveDependencies(dependencies, *last_child_ptr); + // Explicitly ordered unions also order their children's sink pipelines. + auto dependency_mode = + !allow_out_of_order ? RecursiveDependencyMode::FORCE : RecursiveDependencyMode::RESPECT_PARALLELISM; + meta_pipeline.AddRecursiveDependencies(dependencies, *last_child_ptr, dependency_mode); } } } diff --git a/src/duckdb/src/execution/perfect_aggregate_hashtable.cpp b/src/duckdb/src/execution/perfect_aggregate_hashtable.cpp index 7005c4e41..b3d85584f 100644 --- a/src/duckdb/src/execution/perfect_aggregate_hashtable.cpp +++ b/src/duckdb/src/execution/perfect_aggregate_hashtable.cpp @@ -56,7 +56,7 @@ PerfectAggregateHashTable::PerfectAggregateHashTable(ClientContext &context, All RowOperations::InitializeStates(*layout_ptr, addresses, *FlatVector::IncrementalSelectionVector(), init_count); } -PerfectAggregateHashTable::~PerfectAggregateHashTable() { +PerfectAggregateHashTable::~PerfectAggregateHashTable() { // NOLINT(bugprone-exception-escape) Destroy(); } @@ -266,9 +266,10 @@ static void ReconstructGroupVectorTemplated(uint32_t group_values[], Value &min, // if it is 0, the value is NULL data.WriteNull(); } else { - // otherwise we add the value (minus 1) to the min value + // otherwise we add the value (minus 1) to the min value - subtracting first, as the min value can be close + // to the maximum (e.g. the infinite timestamp) data.WriteValue(UnsafeNumericCast(UnsafeNumericCast(min_data) + - UnsafeNumericCast(group_index) - 1)); + (UnsafeNumericCast(group_index) - 1))); } } } diff --git a/src/duckdb/src/execution/physical_plan/plan_asof_join.cpp b/src/duckdb/src/execution/physical_plan/plan_asof_join.cpp index 4c3b301d3..f2b2dbbcc 100644 --- a/src/duckdb/src/execution/physical_plan/plan_asof_join.cpp +++ b/src/duckdb/src/execution/physical_plan/plan_asof_join.cpp @@ -7,6 +7,8 @@ #include "duckdb/execution/operator/join/physical_nested_loop_join.hpp" #include "duckdb/execution/operator/projection/physical_projection.hpp" #include "duckdb/function/aggregate/distributive_function_utils.hpp" +#include "duckdb/function/aggregate/distributive_functions.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/function/window/rows_functions.hpp" #include "duckdb/function/window/value_functions.hpp" #include "duckdb/execution/physical_plan_generator.hpp" @@ -38,8 +40,9 @@ static unique_ptr CreatePredicateFromConditions(const vector -PhysicalPlanGenerator::PlanAsOfLoopJoin(LogicalComparisonJoin &op, PhysicalOperator &probe, PhysicalOperator &build) { +optional_ptr PhysicalPlanGenerator::PlanAsOfLoopJoin(LogicalComparisonJoin &op, + PhysicalOperator &probe, PhysicalOperator &build, + const idx_t join_cardinality) { // Plan a inverse nested loop join, then aggregate the values to choose the optimal match for each probe row. // Use a row number primary key to handle duplicate probe values. // aggregate the fields to produce at most one match per probe row, @@ -141,6 +144,7 @@ PhysicalPlanGenerator::PlanAsOfLoopJoin(LogicalComparisonJoin &op, PhysicalOpera case ExpressionType::COMPARE_EQUAL: case ExpressionType::COMPARE_NOTEQUAL: case ExpressionType::COMPARE_DISTINCT_FROM: + case ExpressionType::COMPARE_NOT_DISTINCT_FROM: break; default: // Unsupported NLJ comparison @@ -203,7 +207,8 @@ PhysicalPlanGenerator::PlanAsOfLoopJoin(LogicalComparisonJoin &op, PhysicalOpera auto col_ref = make_uniq(col_type, col_idx); aggr_children.push_back(std::move(col_ref)); - auto aggr_expr = FirstFunctionGetter::GetFunction(col_type).Bind(context, std::move(aggr_children)); + auto aggr_expr = + GetBuiltinAggregateFunction(context, FirstFun::Name, {col_type})->Bind(context, std::move(aggr_children)); D_ASSERT(col_type == aggr_expr->GetReturnType()); aggregates.emplace_back(std::move(aggr_expr)); @@ -240,10 +245,9 @@ PhysicalPlanGenerator::PlanAsOfLoopJoin(LogicalComparisonJoin &op, PhysicalOpera } // Add a synthetic primary integer key to the probe relation using streaming windowing. - auto row_number = make_uniq(RowNumberFun::GetFunction()); vector> window_select; - auto pk = RowNumberFun::GetFunction().Bind(context); + auto pk = GetBuiltinWindowFunction(context, RowNumberFun::Name, {})->Bind(context); D_ASSERT(pk->GetReturnType() == pk_type); pk->WindowStartMutable() = WindowBoundary::UNBOUNDED_PRECEDING; @@ -254,15 +258,14 @@ PhysicalPlanGenerator::PlanAsOfLoopJoin(LogicalComparisonJoin &op, PhysicalOpera auto window_types = probe.GetTypes(); window_types.emplace_back(pk_type); - const auto probe_cardinality = op.EstimateCardinality(context); - auto &window = Make(window_types, std::move(window_select), probe_cardinality); + auto &window = Make(window_types, std::move(window_select), join_cardinality); window.children.emplace_back(probe); auto &join = Make(join_op, build, window, std::move(join_op.conditions), join_op.join_type, - probe_cardinality); + join_cardinality); // Plan a projection of the compare column - auto &comp_proj = Make(std::move(comp_types), std::move(comp_list), probe_cardinality); + auto &comp_proj = Make(std::move(comp_types), std::move(comp_list), join_cardinality); comp_proj.children.emplace_back(join); // Plan an aggregation on the output of the join, grouping by key; @@ -273,7 +276,7 @@ PhysicalPlanGenerator::PlanAsOfLoopJoin(LogicalComparisonJoin &op, PhysicalOpera groups.emplace_back(std::move(pk_ref)); auto &aggr = - Make(context, aggr_types, std::move(aggregates), std::move(groups), probe_cardinality); + Make(context, aggr_types, std::move(aggregates), std::move(groups), join_cardinality); aggr.children.emplace_back(comp_proj); // Project away primary/grouping key @@ -285,7 +288,7 @@ PhysicalPlanGenerator::PlanAsOfLoopJoin(LogicalComparisonJoin &op, PhysicalOpera project_list.emplace_back(std::move(col_ref)); } - auto &proj = Make(op.types, std::move(project_list), probe_cardinality); + auto &proj = Make(op.types, std::move(project_list), join_cardinality); proj.children.emplace_back(aggr); return proj; } @@ -339,6 +342,8 @@ PhysicalOperator &PhysicalPlanGenerator::PlanAsOfJoin(LogicalComparisonJoin &op) D_ASSERT(op.children.size() == 2); idx_t lhs_cardinality = op.children[0]->EstimateCardinality(context); idx_t rhs_cardinality = op.children[1]->EstimateCardinality(context); + // Estimate the join cardinality before planning the children - planning consumes the bind data of their scans + const idx_t join_cardinality = op.EstimateCardinality(context); auto &left = CreatePlan(*op.children[0]); auto &right = CreatePlan(*op.children[1]); @@ -352,10 +357,15 @@ PhysicalOperator &PhysicalPlanGenerator::PlanAsOfJoin(LogicalComparisonJoin &op) if (!force_asof_join || has_predicate) { const idx_t asof_join_threshold = Settings::Get(context); if (has_predicate || (op.children[0]->has_estimated_cardinality && lhs_cardinality < asof_join_threshold)) { - auto result = PlanAsOfLoopJoin(op, left, right); + auto result = PlanAsOfLoopJoin(op, left, right, join_cardinality); if (result) { return *result; } + if (has_predicate) { + // PhysicalAsOfJoin only evaluates the comparisons + throw NotImplementedException("Unsupported ASOF JOIN: the join predicate cannot be evaluated together " + "with these join conditions"); + } } // Check to see if we can leverage partitioning diff --git a/src/duckdb/src/execution/physical_plan/plan_comparison_join.cpp b/src/duckdb/src/execution/physical_plan/plan_comparison_join.cpp index 4431083a1..3a3267568 100644 --- a/src/duckdb/src/execution/physical_plan/plan_comparison_join.cpp +++ b/src/duckdb/src/execution/physical_plan/plan_comparison_join.cpp @@ -247,7 +247,7 @@ PhysicalOperator &PhysicalPlanGenerator::PlanComparisonJoin(LogicalComparisonJoi return join; } - D_ASSERT(op.left_projection_map.empty()); + // range joins apply both projection maps, the nested loop join only applies the left one of a MARK join idx_t nested_loop_join_threshold = Settings::Get(context); if (left.estimated_cardinality < nested_loop_join_threshold || right.estimated_cardinality < nested_loop_join_threshold) { @@ -273,6 +273,7 @@ PhysicalOperator &PhysicalPlanGenerator::PlanComparisonJoin(LogicalComparisonJoi } if (PhysicalNestedLoopJoin::IsSupported(op.conditions, op.join_type)) { // inequality join: use nested loop + D_ASSERT(op.left_projection_map.empty() || op.join_type == JoinType::MARK); return Make(op, left, right, std::move(op.conditions), op.join_type, op.estimated_cardinality, std::move(op.filter_pushdown)); } @@ -282,6 +283,7 @@ PhysicalOperator &PhysicalPlanGenerator::PlanComparisonJoin(LogicalComparisonJoi RewriteJoinCondition(cond.RightReference(), left.types.size()); } } + D_ASSERT(op.left_projection_map.empty()); auto condition = JoinCondition::CreateExpression(std::move(op.conditions)); return Make(op, left, right, std::move(condition), op.join_type, op.estimated_cardinality); } diff --git a/src/duckdb/src/execution/physical_plan/plan_copy_to_file.cpp b/src/duckdb/src/execution/physical_plan/plan_copy_to_file.cpp index 15201e54e..9f8475374 100644 --- a/src/duckdb/src/execution/physical_plan/plan_copy_to_file.cpp +++ b/src/duckdb/src/execution/physical_plan/plan_copy_to_file.cpp @@ -7,6 +7,16 @@ namespace duckdb { PhysicalOperator &PhysicalPlanGenerator::CreatePlan(LogicalCopyToFile &op) { auto &plan = CreatePlan(*op.children[0]); + auto &fs = FileSystem::GetFileSystem(context); + op.file_path = fs.ExpandPath(op.file_path); + return CreatePlan(op, plan); +} + +PhysicalOperator &PhysicalPlanGenerator::CreatePlan(LogicalCopyToFile &op, PhysicalOperator &plan) { + if (op.use_tmp_file) { + auto base = StringUtil::GetFileName(op.file_path); + op.file_path = StringUtil::ReplaceFileName(op.file_path, "tmp_" + base); + } bool preserve_insertion_order = PhysicalPlanGenerator::PreserveInsertionOrder(context, plan); bool supports_batch_index = PhysicalPlanGenerator::UseBatchIndex(context, plan); @@ -15,15 +25,6 @@ PhysicalOperator &PhysicalPlanGenerator::CreatePlan(LogicalCopyToFile &op) { } else if (op.preserve_order == PreserveOrderType::DONT_PRESERVE_ORDER) { preserve_insertion_order = false; } - - auto &fs = FileSystem::GetFileSystem(context); - op.file_path = fs.ExpandPath(op.file_path); - - if (op.use_tmp_file) { - auto path = StringUtil::GetFilePath(op.file_path); - auto base = StringUtil::GetFileName(op.file_path); - op.file_path = fs.JoinPath(path, "tmp_" + base); - } if (op.per_thread_output || op.file_size_bytes.IsValid() || op.rotate || op.partition_output || !op.partition_columns.empty() || !op.order_columns.empty()) { if (op.preserve_order == PreserveOrderType::PRESERVE_ORDER) { @@ -84,6 +85,7 @@ PhysicalOperator &PhysicalPlanGenerator::CreatePlan(LogicalCopyToFile &op) { cast_copy.write_empty_file = op.write_empty_file; cast_copy.hive_file_pattern = op.hive_file_pattern; cast_copy.order_columns = std::move(op.order_columns); + cast_copy.partition_path_expression = std::move(op.partition_path_expression); cast_copy.children.push_back(plan); return copy; diff --git a/src/duckdb/src/execution/physical_plan/plan_create_index.cpp b/src/duckdb/src/execution/physical_plan/plan_create_index.cpp index 7263d83e2..90e9a3e62 100644 --- a/src/duckdb/src/execution/physical_plan/plan_create_index.cpp +++ b/src/duckdb/src/execution/physical_plan/plan_create_index.cpp @@ -95,14 +95,17 @@ static PhysicalOperator &AddSort(PhysicalPlanGenerator &plan, LogicalCreateIndex PhysicalOperator &PhysicalPlanGenerator::CreatePlan(LogicalCreateIndex &op) { // Early-out, if the index already exists. - auto &schema = op.table.schema; - auto entry = - schema.GetEntry(schema.GetCatalogTransaction(context), CatalogType::INDEX_ENTRY, op.info->GetIndexName()); - if (entry) { - if (op.info->on_conflict != OnCreateConflict::IGNORE_ON_CONFLICT) { - throw CatalogException("Index with name %s already exists!", op.info->GetIndexName()); + // ALTER TABLE ADD PRIMARY KEY / UNIQUE creates a constraint index, which has no catalog entry. + if (!op.alter_table_info) { + auto &schema = op.table.schema; + auto entry = + schema.GetEntry(schema.GetCatalogTransaction(context), CatalogType::INDEX_ENTRY, op.info->GetIndexName()); + if (entry) { + if (op.info->on_conflict != OnCreateConflict::IGNORE_ON_CONFLICT) { + throw CatalogException("Index with name %s already exists!", op.info->GetIndexName()); + } + return Make(op.types, op.estimated_cardinality); } - return Make(op.types, op.estimated_cardinality); } if (!op.table.IsDuckTable()) { diff --git a/src/duckdb/src/execution/physical_plan/plan_distinct.cpp b/src/duckdb/src/execution/physical_plan/plan_distinct.cpp index e0761133a..319c98d71 100644 --- a/src/duckdb/src/execution/physical_plan/plan_distinct.cpp +++ b/src/duckdb/src/execution/physical_plan/plan_distinct.cpp @@ -1,7 +1,8 @@ #include "duckdb/execution/operator/aggregate/physical_hash_aggregate.hpp" #include "duckdb/execution/operator/projection/physical_projection.hpp" #include "duckdb/execution/physical_plan_generator.hpp" -#include "duckdb/function/aggregate/distributive_function_utils.hpp" +#include "duckdb/function/aggregate/distributive_functions.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/planner/expression/bound_aggregate_expression.hpp" #include "duckdb/planner/expression/bound_reference_expression.hpp" #include "duckdb/planner/operator/logical_distinct.hpp" @@ -59,9 +60,9 @@ PhysicalOperator &PhysicalPlanGenerator::CreatePlan(LogicalDistinct &op) { first_children.push_back(std::move(bound)); FunctionBinder function_binder(context); - auto first_aggregate = - function_binder.BindAggregateFunction(FirstFunctionGetter::GetFunction(logical_type), - std::move(first_children), nullptr, AggregateType::NON_DISTINCT); + auto first_aggregate = function_binder.BindAggregateFunction( + GetBuiltinAggregateFunction(context, FirstFun::Name, {logical_type}), std::move(first_children), + nullptr, AggregateType::NON_DISTINCT); first_aggregate->GetOrderBysMutable() = op.order_by ? op.order_by->Copy() : nullptr; if (Settings::Get(context)) { diff --git a/src/duckdb/src/execution/physical_plan/plan_get.cpp b/src/duckdb/src/execution/physical_plan/plan_get.cpp index 3b6e3403f..17d89bba0 100644 --- a/src/duckdb/src/execution/physical_plan/plan_get.cpp +++ b/src/duckdb/src/execution/physical_plan/plan_get.cpp @@ -67,12 +67,31 @@ PhysicalOperator &PhysicalPlanGenerator::CreatePlan(LogicalGet &op) { child = proj; } - auto &table_in_out = - Make(op.types, op.function, std::move(op.bind_data), column_ids, - op.estimated_cardinality, std::move(op.projected_input)); + unique_ptr table_filters; + if (op.table_filters.HasFilters() || op.table_filters.HasMultiColumnFilters()) { + table_filters = MoveTableFilters(op.table_filters); + } + vector projection_indices; + for (auto &projection_id : op.projection_ids) { + projection_indices.push_back(projection_id); + } + optional_idx ordinality_idx; + if (op.ordinality_idx.IsValid()) { + auto output_count = projection_indices.empty() ? column_ids.size() : projection_indices.size(); + for (idx_t output_idx = 0; output_idx < output_count; output_idx++) { + auto column_idx = projection_indices.empty() ? output_idx : projection_indices[output_idx]; + if (column_ids[column_idx].GetPrimaryIndex() == op.ordinality_idx.GetIndex()) { + ordinality_idx = output_idx; + break; + } + } + } + auto &table_in_out = Make( + op.types, op.function, std::move(op.bind_data), column_ids, std::move(projection_indices), + std::move(table_filters), op.estimated_cardinality, std::move(op.projected_input)); table_in_out.children.push_back(child); auto &cast_table_in_out = table_in_out.Cast(); - cast_table_in_out.ordinality_idx = op.ordinality_idx; + cast_table_in_out.ordinality_idx = ordinality_idx; return table_in_out; } diff --git a/src/duckdb/src/execution/physical_plan/plan_merge_into.cpp b/src/duckdb/src/execution/physical_plan/plan_merge_into.cpp index 3bed508ee..c64ac988d 100644 --- a/src/duckdb/src/execution/physical_plan/plan_merge_into.cpp +++ b/src/duckdb/src/execution/physical_plan/plan_merge_into.cpp @@ -58,7 +58,7 @@ unique_ptr PlanMergeIntoAction(ClientContext &context, Logica return_types.pop_back(); } - auto cardinality = op.EstimateCardinality(context); + auto cardinality = op.estimated_cardinality; switch (action.action_type) { case MergeActionType::MERGE_UPDATE: { vector> defaults; @@ -212,7 +212,7 @@ static unique_ptr PlanGenericMergeIntoAction(ClientContext &c auto &catalog = op.table.catalog; // the operators of an action emit the same rows as those of a regular INSERT/UPDATE/DELETE - resolve the types // and cardinality of the logical operators in the same manner - auto cardinality = op.EstimateCardinality(context); + auto cardinality = op.estimated_cardinality; switch (action.action_type) { case MergeActionType::MERGE_UPDATE: { // a regular UPDATE reads the new values of a row followed by its row id - project the rows of the action into @@ -326,6 +326,8 @@ PhysicalOperator &Catalog::PlanMergeInto(ClientContext &context, PhysicalPlanGen } PhysicalOperator &PhysicalPlanGenerator::CreatePlan(LogicalMergeInto &op) { + // planning the child consumes its logical operators - estimate the cardinality beforehand + op.estimated_cardinality = op.EstimateCardinality(context); auto &plan = CreatePlan(*op.children[0]); D_ASSERT(op.children.size() == 1); dependencies.AddDependency(op.table); diff --git a/src/duckdb/src/execution/physical_plan/plan_projection.cpp b/src/duckdb/src/execution/physical_plan/plan_projection.cpp index 72d81e869..03b198b1d 100644 --- a/src/duckdb/src/execution/physical_plan/plan_projection.cpp +++ b/src/duckdb/src/execution/physical_plan/plan_projection.cpp @@ -23,7 +23,7 @@ PhysicalOperator &PhysicalPlanGenerator::CreatePlan(LogicalProjection &op) { for (idx_t i = 0; i < op.types.size(); i++) { if (op.expressions[i]->GetExpressionType() == ExpressionType::BOUND_REF) { auto &bound_ref = op.expressions[i]->Cast(); - if (bound_ref.Index() == i) { + if (bound_ref.Index() == i && op.types[i].EqualsIncludingCollation(plan.GetTypes()[i])) { continue; } } diff --git a/src/duckdb/src/execution/physical_plan/plan_recursive_cte.cpp b/src/duckdb/src/execution/physical_plan/plan_recursive_cte.cpp index b42be9ef3..bf1432b13 100644 --- a/src/duckdb/src/execution/physical_plan/plan_recursive_cte.cpp +++ b/src/duckdb/src/execution/physical_plan/plan_recursive_cte.cpp @@ -11,7 +11,8 @@ #include "duckdb/planner/operator/logical_cteref.hpp" #include "duckdb/planner/operator/logical_recursive_cte.hpp" #include "duckdb/planner/expression_binder.hpp" -#include "duckdb/function/aggregate/distributive_function_utils.hpp" +#include "duckdb/function/aggregate/distributive_functions.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/function/function_binder.hpp" #include "duckdb/execution/aggregate_hashtable.hpp" #include "duckdb/execution/perfect_aggregate_hashtable.hpp" @@ -105,7 +106,7 @@ PhysicalOperator &PhysicalPlanGenerator::CreatePlan(LogicalRecursiveCTE &op) { vector> children; children.push_back(make_uniq(distinct_types[key_idx], distinct_idx[key_idx])); auto representative = function_binder.BindAggregateFunction( - FirstFunctionGetter::GetFunction(distinct_types[key_idx]), std::move(children)); + GetBuiltinAggregateFunction(context, FirstFun::Name, {distinct_types[key_idx]}), std::move(children)); key_representative_indices[key_idx] = payload_aggregates.size(); aggregate_types.push_back(representative->GetReturnType()); payload_aggregates.push_back(std::move(representative)); diff --git a/src/duckdb/src/execution/physical_plan/plan_set_operation.cpp b/src/duckdb/src/execution/physical_plan/plan_set_operation.cpp index 8fece1f43..078f2b426 100644 --- a/src/duckdb/src/execution/physical_plan/plan_set_operation.cpp +++ b/src/duckdb/src/execution/physical_plan/plan_set_operation.cpp @@ -5,6 +5,7 @@ #include "duckdb/execution/operator/join/physical_hash_join.hpp" #include "duckdb/execution/operator/projection/physical_projection.hpp" #include "duckdb/execution/operator/set/physical_union.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/planner/expression/bound_reference_expression.hpp" #include "duckdb/planner/expression/bound_window_expression.hpp" #include "duckdb/planner/expression_binder.hpp" @@ -17,7 +18,7 @@ namespace duckdb { static vector> CreatePartitionedRowNumExpression(ClientContext &client, const vector &types) { vector> res; - auto expr = RowNumberFun::GetFunction().Bind(client); + auto expr = GetBuiltinWindowFunction(client, RowNumberFun::Name, {})->Bind(client); expr->WindowStartMutable() = WindowBoundary::UNBOUNDED_PRECEDING; expr->WindowEndMutable() = WindowBoundary::UNBOUNDED_FOLLOWING; for (idx_t i = 0; i < types.size(); i++) { @@ -38,6 +39,24 @@ static JoinCondition CreateNotDistinctComparison(ClientContext &context, const L return cond; } +//! The types to compare the columns of EXCEPT / INTERSECT with - a collation of either side applies (as for UNION) +static vector GetComparisonTypes(ClientContext &context, const LogicalSetOperation &op, + const vector &left_types, + const vector &right_types) { + // the types only differ in their collations here, but a collation can be missing from a child (e.g. when the + // projection applying a COLLATE was removed) or from the operator (it takes the types of the left child) + vector types; + for (idx_t i = 0; i < left_types.size(); i++) { + auto type = LogicalType::ForceMaxLogicalType(context, left_types[i], right_types[i]); + // the operator types can be outdated (e.g. compressed children), only take a collation from them + if (i < op.types.size() && op.types[i].id() == type.id()) { + type = LogicalType::ForceMaxLogicalType(context, op.types[i], type); + } + types.push_back(std::move(type)); + } + return types; +} + PhysicalOperator &PhysicalPlanGenerator::CreatePlan(LogicalSetOperation &op) { ArenaLinkedList> children(physical_plan->ArenaRef()); for (auto &child : op.children) { @@ -62,7 +81,7 @@ PhysicalOperator &PhysicalPlanGenerator::CreatePlan(LogicalSetOperation &op) { } auto &left = children[0]; auto &right = children[1]; - auto &types = left.get().GetTypes(); + auto types = GetComparisonTypes(context, op, left.get().GetTypes(), right.get().GetTypes()); vector conditions; // create equality condition for all columns for (idx_t i = 0; i < types.size(); i++) { diff --git a/src/duckdb/src/execution/physical_plan/plan_window.cpp b/src/duckdb/src/execution/physical_plan/plan_window.cpp index 1f70fcce7..e958caf74 100644 --- a/src/duckdb/src/execution/physical_plan/plan_window.cpp +++ b/src/duckdb/src/execution/physical_plan/plan_window.cpp @@ -145,6 +145,7 @@ PhysicalOperator &PhysicalPlanGenerator::CreatePlan(LogicalWindow &op) { // Identify streaming windows, partitioned windows and degenerate frames (0,1) using Columns = vector; const bool enable_optimizer = Settings::Get(context); + const bool can_stream = enable_optimizer && (plan.get().GetSources().size() == 1); vector blocking_windows; vector streaming_windows; vector partitioned_windows; @@ -155,7 +156,7 @@ PhysicalOperator &PhysicalPlanGenerator::CreatePlan(LogicalWindow &op) { Columns partition_columns; if (HasDegenerateFrame(context, wexpr)) { degenerate_frames.emplace_back(expr_idx); - } else if (enable_optimizer && PhysicalStreamingWindow::IsStreamingFunction(context, wexpr)) { + } else if (can_stream && PhysicalStreamingWindow::IsStreamingFunction(context, wexpr)) { streaming_windows.push_back(expr_idx); } else if (!wexpr.Partitions().empty() && HasSingleValuePartitions(context, wexpr.Partitions(), plan, partition_columns)) { @@ -310,9 +311,10 @@ PhysicalOperator &PhysicalPlanGenerator::CreatePlan(LogicalWindow &op) { } // Evaluate the degenerate functions as scalar window functions FunctionBinder binder(context); + const vector input_types(op.types.begin(), op.types.begin() + NumericCast(input_width)); for (const auto &expr_idx : degenerate_frames) { auto &wexpr = op.expressions[expr_idx]->Cast(); - select_list[input_width + expr_idx] = binder.BindScalarWindowFunction(wexpr); + select_list[input_width + expr_idx] = binder.BindScalarWindowFunction(wexpr, input_types); } auto &proj = Make(op.types, std::move(select_list), op.estimated_cardinality); diff --git a/src/duckdb/src/execution/radix_ht_adaptivity.cpp b/src/duckdb/src/execution/radix_ht_adaptivity.cpp new file mode 100644 index 000000000..4200c0285 --- /dev/null +++ b/src/duckdb/src/execution/radix_ht_adaptivity.cpp @@ -0,0 +1,107 @@ +#include "duckdb/execution/radix_ht_adaptivity.hpp" + +#include "duckdb/common/exception.hpp" +#include "duckdb/common/limits.hpp" +#include "duckdb/execution/radix_ht_sink_state.hpp" + +namespace duckdb { + +bool RadixHTAdaptivity::TryGrow(RadixHTGlobalSinkState &gstate, RadixHTLocalSinkState &lstate) { + if (gstate.number_of_threads <= RadixHTConfig::GROW_STRATEGY_THREAD_THRESHOLD || gstate.external) { + return false; + } + auto &ht = *lstate.ht; + const auto &adaptivity = ht.GetAdaptivityState(); + const auto materialized_count = ht.GetMaterializedCount(); + if (!adaptivity.HLLEnabled() || ht.Count() == materialized_count || + (gstate.spill_plan && gstate.StatePressureExceeded(ht))) { + return false; + } + + const auto hll_count = MinValue(adaptivity.GetHLLUpperBound(), materialized_count); + if (hll_count == 0) { + return false; + } + + if (ht.Capacity() > NumericLimits::Maximum() / (3 * sizeof(ht_entry_t))) { + return false; + } + const auto minimum_capacity = ht.Capacity() * 2; + auto next_capacity = MaxValue(GroupedAggregateHashTable::GetCapacityForCount(hll_count), minimum_capacity); + if (next_capacity > NumericLimits::Maximum() / sizeof(ht_entry_t)) { + return false; + } + const auto current_size = ht.GetSizeInBytes(); + const auto table_size = next_capacity * sizeof(ht_entry_t); + // Grow only when repeated tuple materialization outweighs the larger pointer table. + if (static_cast(materialized_count - hll_count) * static_cast(ht.GetLayout().GetRowWidth()) < + GROWTH_DUPLICATE_BYTES_FACTOR * static_cast(table_size)) { + return false; + } + + if (table_size > NumericLimits::Maximum() - current_size) { + return false; + } + // The old pointer table remains allocated until the new allocation succeeds. + const auto desired_size = current_size + table_size; + if (desired_size > gstate.GetThreadLimit() && desired_size <= gstate.memory_limit / gstate.number_of_threads) { + const annotated_lock_guard guard {gstate.lock}; + auto &memory_state = *gstate.temporary_memory_state; + const auto request = desired_size * gstate.number_of_threads; + const auto doubled_request = request <= NumericLimits::Maximum() / 2 ? request * 2 : request; + memory_state.SetRemainingSizeAndUpdateReservation(gstate.context, + MaxValue(memory_state.GetRemainingSize(), doubled_request)); + } + const auto thread_limit = gstate.GetThreadLimit(); + while (next_capacity >= minimum_capacity) { + if (current_size <= thread_limit && next_capacity <= (thread_limit - current_size) / sizeof(ht_entry_t)) { + break; + } + next_capacity /= 2; + } + if (next_capacity < minimum_capacity) { + return false; + } + + gstate.any_abandoned = true; + ht.Abandon(); + try { + ht.Resize(next_capacity); + } catch (const OutOfMemoryException &) { + // Other operators may consume the reservation before the allocation succeeds. + ht.EnableHLL(false); + return false; + } + ht.ResumeLookups(); + lstate.local_sink_capacity = next_capacity; + return true; +} + +void RadixHTAdaptivity::MaybeSkipLookups(RadixHTGlobalSinkState &gstate, RadixHTLocalSinkState &lstate) { + auto &ht = *lstate.ht; + const auto &adaptivity = ht.GetAdaptivityState(); + if (gstate.external || !adaptivity.HLLEnabled() || adaptivity.LookupsSkipped() || + gstate.number_of_threads <= RadixHTConfig::GROW_STRATEGY_THREAD_THRESHOLD || + adaptivity.GetSinkCount() < LOOKUP_SAMPLE_SIZE || ht.Capacity() != gstate.config.sink_capacity) { + return; + } + + const auto cycle_input = adaptivity.GetCycleInputCount(); + D_ASSERT(cycle_input >= ht.Count()); + const auto reduction = + cycle_input ? static_cast(cycle_input - ht.Count()) / static_cast(cycle_input) : 0; + if (reduction < MINIMUM_SMALL_TABLE_REDUCTION) { + ht.SkipLookups(); + } +} + +void RadixHTAdaptivity::MaybeResumeLookups(RadixHTGlobalSinkState &gstate, RadixHTLocalSinkState &lstate) { + auto &ht = *lstate.ht; + const auto &adaptivity = ht.GetAdaptivityState(); + if (!gstate.external && !lstate.spilling && adaptivity.HLLEnabled() && adaptivity.LookupsSkipped() && + adaptivity.GetSkippedInputCount() >= LOOKUP_SAMPLE_SIZE) { + ht.ResumeLookups(); + } +} + +} // namespace duckdb diff --git a/src/duckdb/src/execution/radix_partitioned_hashtable.cpp b/src/duckdb/src/execution/radix_partitioned_hashtable.cpp index 6e24d1321..500287f33 100644 --- a/src/duckdb/src/execution/radix_partitioned_hashtable.cpp +++ b/src/duckdb/src/execution/radix_partitioned_hashtable.cpp @@ -1,4 +1,6 @@ #include "duckdb/execution/radix_partitioned_hashtable.hpp" +#include "duckdb/execution/radix_ht_sink_state.hpp" +#include "duckdb/execution/radix_ht_adaptivity.hpp" #include "duckdb/common/radix_partitioning.hpp" #include "duckdb/common/enums/debug_verification_mode.hpp" @@ -91,7 +93,7 @@ enum class AggregatePartitionState : uint8_t { struct AggregatePartition : StateWithBlockableTasks { explicit AggregatePartition(unique_ptr data_p) - : state(AggregatePartitionState::READY_TO_FINALIZE), data(std::move(data_p)), progress(0) { + : state(AggregatePartitionState::READY_TO_FINALIZE), data(std::move(data_p)) { } AggregatePartitionState state; @@ -103,146 +105,22 @@ struct AggregatePartition : StateWithBlockableTasks { shared_ptr allocator; //! Arena holding the imported states (combining may steal from them, so it must live as long) shared_ptr import_allocator; - atomic progress; -}; - -class RadixHTGlobalSinkState; - -//! How aggregate state spilling and native row combining are allowed to interleave -enum class SpillPhase { - //! Rows may still be combined natively, and the exported width may still grow beyond it - NATIVE_ALLOWED, - //! The exported width grew beyond the native width: every combine must export its rows - EXPORTED_ONLY, - //! Rows were combined natively: the exported width can no longer grow beyond the native width - NATIVE_COMBINE_STARTED -}; - -struct RadixHTConfig { -public: - explicit RadixHTConfig(RadixHTGlobalSinkState &sink); - - void Reset(); - void SetRadixBits(const idx_t &radix_bits_p); - bool SetRadixBitsToExternal(); - idx_t GetRadixBits() const; - idx_t GetMaximumSinkRadixBits() const; - -private: - void SetRadixBitsInternal(idx_t radix_bits_p, bool external); - idx_t InitialSinkRadixBits() const; - idx_t ExternalRadixBits(bool dynamic) const; - idx_t MaximumSinkRadixBits() const; - idx_t SinkCapacity() const; - -private: - //! The global sink state - RadixHTGlobalSinkState &sink; - -public: - //! Width of tuples - const idx_t row_width; - //! Capacity of HTs during the Sink - const idx_t sink_capacity; - -private: - //! Sink radix bits to initialize with - static constexpr idx_t MAXIMUM_INITIAL_SINK_RADIX_BITS = 4; - -public: - //! Maximum Sink radix bits (independent of threads) - static constexpr idx_t MAXIMUM_FINAL_SINK_RADIX_BITS = 8; - -private: - //! Current thread-global sink radix bits - atomic sink_radix_bits; - //! Maximum Sink radix bits (set based on number of threads, if not external) - const idx_t maximum_sink_radix_bits; - - //! Thresholds at which we reduce the sink radix bits - //! This needed to reduce cache misses when we have very wide rows - static constexpr idx_t ROW_WIDTH_THRESHOLD_ONE = 32; - static constexpr idx_t ROW_WIDTH_THRESHOLD_TWO = 64; - -public: - //! If we have this many or less threads, we grow the HT, otherwise we abandon - static constexpr idx_t GROW_STRATEGY_THREAD_THRESHOLD = 2; - //! If we fill this many blocks per partition, we trigger a repartition - static constexpr double BLOCK_FILL_FACTOR = 0.5; - //! By how many bits to repartition if a repartition is triggered - static constexpr idx_t REPARTITION_RADIX_BITS = 2; - //! Thread-limit divisor for state export and exported partition sizing - static constexpr idx_t AGGREGATE_STATE_SPILL_DIVISOR = 8; - //! Arena-only pressure that forces external aggregation - static constexpr idx_t AGGREGATE_STATE_PRESSURE_DIVISOR = 2; - //! Estimated memory amplification when importing exported states - static constexpr idx_t EXPORTED_STATE_MEMORY_MULTIPLIER = 2; -}; - -class RadixHTGlobalSinkState : public GlobalSinkState { -public: - RadixHTGlobalSinkState(ClientContext &context, const RadixPartitionedHashTable &radix_ht); - - //! Destroys aggregate states (if multi-scan) - ~RadixHTGlobalSinkState() override; - void Destroy(); - -public: - idx_t GetThreadLimit() const { - return temporary_memory_state->GetReservation() / number_of_threads / 10 * 8; + //! Combine progress of this partition + atomic combine_chunk_count {0}; + atomic combined_chunks {0}; + atomic finalized {false}; + + double GetCombineProgress() const { + if (finalized.load(std::memory_order_relaxed)) { + return 1; + } + auto chunk_count = combine_chunk_count.load(std::memory_order_relaxed); + if (chunk_count == 0) { + return 0; + } + auto combined = MinValue(combined_chunks.load(std::memory_order_relaxed), chunk_count); + return static_cast(combined) / static_cast(chunk_count); } - -public: - ClientContext &context; - //! Temporary memory state for managing this hash table's memory usage - unique_ptr temporary_memory_state; - atomic minimum_reservation; - - //! Whether we've called Finalize - bool finalized; - //! Whether we are doing an external aggregation - atomic external; - //! Threads that have called Sink - atomic active_threads; - //! Number of threads (from TaskScheduler) - const idx_t number_of_threads; - //! Memory limit (from BufferManager) - const idx_t memory_limit; - //! Block size (from BufferManager) - const idx_t block_alloc_size; - //! If any thread has called combine - atomic any_combined; - //! If any thread has called ht.Abandon() during Sink (meaning uncombined_data may have duplicates) - atomic any_abandoned; - - //! The radix HT - const RadixPartitionedHashTable &radix_ht; - //! Config for partitioning - RadixHTConfig config; - - //! Uncombined partitioned data that will be put into the AggregatePartitions - unique_ptr uncombined_data; - //! The spill metadata of the aggregate layout, set if the states can spill - unique_ptr spill_plan; - //! Synchronizes the transition to exported-only aggregation with concurrent combines - SpillPhase spill_phase DUCKDB_GUARDED_BY(lock); - //! Uncombined exported data, aligned one-to-one with the partitions of uncombined_data - vector> uncombined_exported_data; - //! Allocators used during the Sink/Finalize - vector> stored_allocators; - idx_t stored_allocators_size; - - //! Partitions that are finalized during GetData - vector> partitions; - //! For keeping track of progress - atomic finalize_done; - - //! Pin properties when scanning - TupleDataPinProperties scan_pin_properties; - //! Total count before combining - idx_t count_before_combining; - //! Maximum partition size if all unique - idx_t max_partition_size; }; RadixHTGlobalSinkState::RadixHTGlobalSinkState(ClientContext &context_p, const RadixPartitionedHashTable &radix_ht_p) @@ -340,23 +218,24 @@ idx_t RadixHTConfig::GetMaximumSinkRadixBits() const { } void RadixHTConfig::SetRadixBitsInternal(const idx_t radix_bits_p, bool external) { - if (sink_radix_bits > radix_bits_p || sink.any_combined) { + if ((!external && sink_radix_bits > radix_bits_p) || sink.any_combined) { return; } const annotated_lock_guard guard {sink.lock}; - if (sink_radix_bits > radix_bits_p || sink.any_combined) { + if ((!external && sink_radix_bits > radix_bits_p) || sink.any_combined) { return; } + const auto radix_bits = MaxValue(radix_bits_p, sink_radix_bits.load()); if (external) { - const auto partition_multiplier = RadixPartitioning::NumberOfPartitions(radix_bits_p) / - RadixPartitioning::NumberOfPartitions(sink_radix_bits); + const auto partition_multiplier = + RadixPartitioning::NumberOfPartitions(radix_bits) / RadixPartitioning::NumberOfPartitions(sink_radix_bits); sink.minimum_reservation = sink.minimum_reservation * partition_multiplier; sink.external = true; } - sink_radix_bits = radix_bits_p; + sink_radix_bits = radix_bits; } idx_t RadixHTConfig::InitialSinkRadixBits() const { @@ -407,39 +286,12 @@ idx_t RadixHTConfig::SinkCapacity() const { // Grow strategy, start off a bit bigger return 262144; } - // Start off tiny, we can adapt with DecideAdaptation later + // Start with a small local table and grow when repeated groups justify it return 32768; } -class RadixHTLocalSinkState : public LocalSinkState { -public: - RadixHTLocalSinkState(ClientContext &context, const RadixPartitionedHashTable &radix_ht); - void ResetForReuse(const RadixPartitionedHashTable &radix_ht, RadixHTGlobalSinkState &gstate); - -public: - //! Thread-local HT that is re-used after abandoning - unique_ptr ht; - //! Chunk with group columns - DataChunk group_chunk; - - //! After seeing this many tuples, we decide whether to adapt our strategy - //! This also serves as the maximum HT sink capacity - static constexpr idx_t ADAPTIVITY_THRESHOLD = 1048576; - //! Whether we have decided to adapt our strategy - bool adapted; - //! Whether this local state has already registered itself as active for the current iteration - bool registered; - //! Sink capacity for this thread - idx_t local_sink_capacity; - - //! Data that is abandoned ends up here (only if we're doing external aggregation) - unique_ptr abandoned_data; - //! Exported abandoned aggregate states, aligned one-to-one with the partitions of abandoned_data - vector> abandoned_exported_data; -}; - RadixHTLocalSinkState::RadixHTLocalSinkState(ClientContext &, const RadixPartitionedHashTable &radix_ht) - : adapted(false), registered(false), local_sink_capacity(DConstants::INVALID_INDEX) { + : registered(false), local_sink_capacity(DConstants::INVALID_INDEX) { // If there are no groups we create a fake group so everything has the same group group_chunk.InitializeEmpty(radix_ht.group_types); if (radix_ht.grouping_set.empty()) { @@ -447,27 +299,40 @@ RadixHTLocalSinkState::RadixHTLocalSinkState(ClientContext &, const RadixPartiti } } +void RadixHTLocalSinkState::PrepareForSpill(RadixHTGlobalSinkState &gstate) { + ht->EnableHLL(false); + gstate.any_abandoned = true; + ht->Abandon(); + if (!spilling) { + // Iteration reuse can retain a larger allocation than the logical capacity. + try { + ht->Resize(gstate.config.sink_capacity); + } catch (const OutOfMemoryException &) { + // Keep the existing table if the temporary replacement allocation cannot fit. + } + spilling = true; + } + local_sink_capacity = ht->Capacity(); +} + void RadixHTLocalSinkState::ResetForReuse(const RadixPartitionedHashTable &radix_ht, RadixHTGlobalSinkState &gstate) { group_chunk.Reset(); if (radix_ht.grouping_set.empty()) { group_chunk.data[0].Reference(Value::TINYINT(42), count_t(STANDARD_VECTOR_SIZE)); } registered = false; + spilling = false; abandoned_data.reset(); abandoned_exported_data.clear(); if (!ht) { - adapted = false; local_sink_capacity = DConstants::INVALID_INDEX; return; } - local_sink_capacity = MaxValue(gstate.config.sink_capacity, ht->Capacity()); ht->ResetForNewIteration(gstate.config.GetRadixBits()); + local_sink_capacity = MaxValue(gstate.config.sink_capacity, ht->Capacity()); if (gstate.number_of_threads > RadixHTConfig::GROW_STRATEGY_THREAD_THRESHOLD) { ht->EnableHLL(true); - adapted = false; - } else { - adapted = true; } } @@ -531,47 +396,6 @@ void RadixPartitionedHashTable::PopulateGroupChunk(DataChunk &group_chunk, DataC group_chunk.Verify(); } -void DecideAdaptation(RadixHTGlobalSinkState &gstate, RadixHTLocalSinkState &lstate) { - //! If the number of unique values is greater than this percentage, we skip lookups altogether - static constexpr double SKIP_LOOKUP_UNIQUE_PERCENTAGE_THRESHOLD = 0.95; - //! If the deduplication rate could be increased by this number, we increase our sink capacity - static constexpr double CAPACITY_INCREASE_DEDUPLICATION_RATE_THRESHOLD = 2.0; - - if (gstate.external) { - return; // Shouldn't adapt after this flag has been set - } - - auto &ht = *lstate.ht; - const auto sink_count = ht.GetSinkCount(); - D_ASSERT(sink_count >= RadixHTLocalSinkState::ADAPTIVITY_THRESHOLD); - - // Deduplicated count (affected by HT size) - const auto deduplicated_count = ht.GetMaterializedCount(); - // Estimated unique count (unaffected by HT size) - const auto hll_count = MinValue(ht.GetHLLUpperBound(), deduplicated_count); - - // Compute actual deduplicated percentage and potential deduplicated count (estimated by hll) - const auto deduplicated_percentage = static_cast(deduplicated_count) / static_cast(sink_count); - const auto hll_percentage = static_cast(hll_count) / static_cast(sink_count); - if (hll_percentage > SKIP_LOOKUP_UNIQUE_PERCENTAGE_THRESHOLD) { - // Almost everything is unique, skip lookups, just append, defer deduplication to GetData phase - ht.SkipLookups(); - return; - } - - const auto potential_increase_rate = deduplicated_percentage / hll_percentage; - if (potential_increase_rate > CAPACITY_INCREASE_DEDUPLICATION_RATE_THRESHOLD) { - // We could be deduplicating a lot better, increase HT capacity - D_ASSERT(IsPowerOfTwo(RadixHTLocalSinkState::ADAPTIVITY_THRESHOLD)); - const auto new_capacity = MinValue(GroupedAggregateHashTable::GetCapacityForCount(hll_count), - RadixHTLocalSinkState::ADAPTIVITY_THRESHOLD); - lstate.local_sink_capacity = MaxValue(gstate.config.sink_capacity, new_capacity); - gstate.any_abandoned = true; - ht.Abandon(); - ht.Resize(lstate.local_sink_capacity); - } -} - // Grow abandoned_data (frozen at the radix bits of when we first went external) up to the current radix bits, so it // never has fewer partitions than the data merged into it - Repartition/Combine only handle new >= old. void GrowAbandonedDataToRadixBits(ClientContext &context, RadixHTGlobalSinkState &gstate, RadixHTLocalSinkState &lstate, @@ -698,10 +522,9 @@ void ExportAbandonedData(ClientContext &context, RadixHTGlobalSinkState &gstate, MemoryTag::HASH_TABLE, radix_bits, hash_col_idx, context); } -// Whether the aggregate arena alone approaches the thread memory limit -bool StatePressureExceeded(RadixHTGlobalSinkState &gstate, GroupedAggregateHashTable &ht) { +bool RadixHTGlobalSinkState::StatePressureExceeded(GroupedAggregateHashTable &ht) const { return ht.GetAggregateAllocator()->AllocationSize() > - gstate.GetThreadLimit() / RadixHTConfig::AGGREGATE_STATE_PRESSURE_DIVISOR; + GetThreadLimit() / RadixHTConfig::AGGREGATE_STATE_PRESSURE_DIVISOR; } // Whether this thread's aggregate states should be exported to spillable storage @@ -711,7 +534,7 @@ bool ShouldExportStates(RadixHTGlobalSinkState &gstate, RadixHTLocalSinkState &l // every current state is inline, and exporting would only cost return false; } - if (StatePressureExceeded(gstate, ht) || !lstate.abandoned_exported_data.empty()) { + if (gstate.StatePressureExceeded(ht) || !lstate.abandoned_exported_data.empty()) { return true; } if (!gstate.external) { @@ -720,34 +543,36 @@ bool ShouldExportStates(RadixHTGlobalSinkState &gstate, RadixHTLocalSinkState &l return arena_size >= gstate.GetThreadLimit() / RadixHTConfig::AGGREGATE_STATE_SPILL_DIVISOR; } +void UpdateSinkReservation(ClientContext &context, RadixHTGlobalSinkState &gstate, GroupedAggregateHashTable &ht, + idx_t total_size) { + if (total_size <= gstate.GetThreadLimit() || gstate.external) { + return; + } + const annotated_lock_guard guard {gstate.lock}; + if (total_size <= gstate.GetThreadLimit()) { + return; + } + auto &temporary_memory_state = *gstate.temporary_memory_state; + const auto aggregate_allocator_size = ht.GetAggregateAllocator()->AllocationSize(); + temporary_memory_state.SetMinimumReservation(aggregate_allocator_size * gstate.number_of_threads + + gstate.minimum_reservation); + const auto remaining_size = + MaxValue(gstate.number_of_threads * total_size, temporary_memory_state.GetRemainingSize()); + // Repeated pressure checks must not keep doubling an unsatisfied reservation. + const auto requested_size = remaining_size > gstate.memory_limit / 2 ? gstate.memory_limit : 2 * remaining_size; + temporary_memory_state.SetRemainingSizeAndUpdateReservation(context, requested_size); +} + void MaybeRepartition(ClientContext &context, RadixHTGlobalSinkState &gstate, RadixHTLocalSinkState &lstate, const bool combine) { auto &config = gstate.config; auto &ht = *lstate.ht; - - // Check if we're approaching the memory limit - auto &temporary_memory_state = *gstate.temporary_memory_state; - const auto aggregate_allocator_size = ht.GetAggregateAllocator()->AllocationSize(); - const auto total_size = - aggregate_allocator_size + ht.GetPartitionedData().SizeInBytes() + ht.Capacity() * sizeof(ht_entry_t); - if (total_size > gstate.GetThreadLimit()) { - // We're over the thread memory limit - if (!gstate.external) { - // We haven't yet triggered out-of-core behavior, but maybe we don't have to, grab the lock and check again - const annotated_lock_guard guard {gstate.lock}; - if (total_size > gstate.GetThreadLimit()) { - // Out-of-core would be triggered below, update minimum reservation and try to increase the reservation - temporary_memory_state.SetMinimumReservation(aggregate_allocator_size * gstate.number_of_threads + - gstate.minimum_reservation); - auto remaining_size = - MaxValue(gstate.number_of_threads * total_size, temporary_memory_state.GetRemainingSize()); - temporary_memory_state.SetRemainingSizeAndUpdateReservation(context, 2 * remaining_size); - } - } - } + const auto total_size = ht.GetSizeInBytes(); + UpdateSinkReservation(context, gstate, ht, total_size); if (total_size > gstate.GetThreadLimit()) { if (gstate.config.SetRadixBitsToExternal()) { + lstate.PrepareForSpill(gstate); // We're approaching the memory limit, unpin the data const auto external_radix_bits = config.GetRadixBits(); if (!lstate.abandoned_data) { @@ -797,11 +622,7 @@ void RadixPartitionedHashTable::Sink(ExecutionContext &context, DataChunk &chunk lstate.local_sink_capacity = gstate.config.sink_capacity; lstate.ht = CreateHT(context.client, lstate.local_sink_capacity, gstate.config.GetRadixBits()); if (gstate.number_of_threads > RadixHTConfig::GROW_STRATEGY_THREAD_THRESHOLD) { - // Not using grow strategy, so we enable the HLL to potentially adapt later lstate.ht->EnableHLL(true); - } else { - // Using grow strategy, so won't ever adapt - lstate.adapted = true; } } if (!lstate.registered) { @@ -813,29 +634,36 @@ void RadixPartitionedHashTable::Sink(ExecutionContext &context, DataChunk &chunk PopulateGroupChunk(group_chunk, chunk); auto &ht = *lstate.ht; + if (gstate.external && !lstate.spilling) { + lstate.PrepareForSpill(gstate); + } ht.AddChunk(group_chunk, payload_input, filter); - // Decide whether we should adapt our strategy to the data - if (!lstate.adapted && lstate.ht->GetSinkCount() >= RadixHTLocalSinkState::ADAPTIVITY_THRESHOLD) { - DecideAdaptation(gstate, lstate); - ht.EnableHLL(false); // Can be disabled now (costs 5-10% performance in worst case, single column distinct) - lstate.adapted = true; + // Row width and aggregate state size can increase without filling the pointer table. + const auto total_size = ht.GetSizeInBytes(); + UpdateSinkReservation(context.client, gstate, ht, total_size); + const auto memory_pressure = + total_size > gstate.GetThreadLimit() || (gstate.spill_plan && gstate.StatePressureExceeded(ht)); + if (!memory_pressure && + ht.Count() + STANDARD_VECTOR_SIZE < GroupedAggregateHashTable::ResizeThreshold(lstate.local_sink_capacity)) { + return; } - if (ht.Count() + STANDARD_VECTOR_SIZE < GroupedAggregateHashTable::ResizeThreshold(lstate.local_sink_capacity) && - !(gstate.spill_plan && StatePressureExceeded(gstate, ht))) { - // We can fit another chunk, and the aggregate states are not under memory pressure either. - // The state check matters for few groups with large states: those never fill the hash - // table, but their arena must still be flushed and exported before it exhausts the limit. - return; + if (!memory_pressure) { + if (RadixHTAdaptivity::TryGrow(gstate, lstate)) { + return; + } + RadixHTAdaptivity::MaybeSkipLookups(gstate, lstate); } if (gstate.number_of_threads > RadixHTConfig::GROW_STRATEGY_THREAD_THRESHOLD || gstate.external) { - // 'Reset' the HT without taking its data, we can just keep appending to the same collection - // This only works because we never resize the HT + // Keep the materialized rows and clear only the local pointer table // We don't do this when running with 1 or 2 threads, it only makes sense when there's many threads gstate.any_abandoned = true; ht.Abandon(); + if (!memory_pressure) { + RadixHTAdaptivity::MaybeResumeLookups(gstate, lstate); + } } // Check if we need to repartition @@ -870,6 +698,7 @@ void RadixPartitionedHashTable::Sink(ExecutionContext &context, DataChunk &chunk } else { GrowAbandonedDataToRadixBits(context.client, gstate, lstate, ht.GetRadixBits()); } + lstate.PrepareForSpill(gstate); ht.AcquirePartitionedData()->Repartition(context.client, *lstate.abandoned_data); ExportAbandonedData(context.client, gstate, lstate); ht.GetAggregateAllocator()->FreeAll(); @@ -1024,7 +853,7 @@ void RadixPartitionedHashTable::Finalize(ClientContext &context, GlobalSinkState gstate.max_partition_size = MaxValue(gstate.max_partition_size, partition_size); if (single_ht) { gstate.finalize_done++; - gstate.partitions.back()->progress = 1; + gstate.partitions.back()->finalized = true; gstate.partitions.back()->state = AggregatePartitionState::READY_TO_SCAN; } } @@ -1093,6 +922,12 @@ class RadixHTGlobalSourceState : public GlobalSourceState { //! For synchronizing tasks atomic task_idx; atomic task_done; + //! Scan progress: the partitions whose scan started, the rows in these partitions, and the rows scanned + atomic started_partitions; + atomic started_rows; + atomic scanned_rows; + //! The scan progress estimate drops when a larger partition starts scanning + MonotonicProgress scan_progress; }; enum class RadixHTScanStatus : uint8_t { INIT, IN_PROGRESS, DONE }; @@ -1147,10 +982,15 @@ void RadixPartitionedHashTable::ResetGlobalSourceState(ClientContext &context, G gstate.finished = false; gstate.task_idx = 0; gstate.task_done = 0; + gstate.started_partitions = 0; + gstate.started_rows = 0; + gstate.scanned_rows = 0; + gstate.scan_progress.Reset(); } RadixHTGlobalSourceState::RadixHTGlobalSourceState(ClientContext &context_p, const RadixPartitionedHashTable &radix_ht) - : context(context_p), finished(false), task_idx(0), task_done(0) { + : context(context_p), finished(false), task_idx(0), task_done(0), started_partitions(0), started_rows(0), + scanned_rows(0) { for (column_t column_id = 0; column_id < radix_ht.group_types.size(); column_id++) { column_ids.push_back(column_id); } @@ -1260,7 +1100,8 @@ void RadixHTLocalSourceState::Finalize(RadixHTGlobalSinkState &sink, RadixHTGlob } // Now combine the uncombined data using this thread's HT - ht->Combine(*partition.data, &partition.progress); + partition.combine_chunk_count = partition.data->ChunkCount(); + ht->Combine(*partition.data, &partition.combined_chunks); if (partition.exported_data) { // Rebuild the exported states on an arena of their own, one chunk at a time, and combine // them like the rest. Combining may steal from the imported states, so the arena lives @@ -1271,7 +1112,7 @@ void RadixHTLocalSourceState::Finalize(RadixHTGlobalSinkState &sink, RadixHTGlob [&](TupleDataCollection &imported) { ht->Combine(imported); }); partition.exported_data.reset(); } - partition.progress = 1; + partition.finalized = true; // Move the combined data back to the partition partition.data = @@ -1318,6 +1159,8 @@ void RadixHTLocalSourceState::Scan(RadixHTGlobalSinkState &sink, RadixHTGlobalSo if (scan_status == RadixHTScanStatus::INIT) { data_collection.InitializeScan(scan_state, gstate.column_ids, sink.scan_pin_properties); scan_status = RadixHTScanStatus::IN_PROGRESS; + gstate.started_rows.fetch_add(data_collection.Count(), std::memory_order_relaxed); + gstate.started_partitions.fetch_add(1, std::memory_order_relaxed); } if (!data_collection.Scan(scan_state, scan_chunk)) { @@ -1334,6 +1177,8 @@ void RadixHTLocalSourceState::Scan(RadixHTGlobalSinkState &sink, RadixHTGlobalSo return; } + gstate.scanned_rows.fetch_add(scan_chunk.size(), std::memory_order_relaxed); + const auto group_cols = layout.ColumnCount() - 1; RowOperations::FinalizeStates(row_state, layout, scan_state.chunk_state.row_locations, scan_chunk, group_cols); @@ -1385,6 +1230,7 @@ SourceResultType RadixPartitionedHashTable::GetData(ExecutionContext &context, D sink.scan_pin_properties == TupleDataPinProperties::DESTROY_AFTER_DONE); if (gstate.finished) { + lstate.ht.reset(); return SourceResultType::FINISHED; } @@ -1440,6 +1286,7 @@ SourceResultType RadixPartitionedHashTable::GetData(ExecutionContext &context, D if (chunk.size() != 0) { return SourceResultType::HAVE_MORE_OUTPUT; } else { + lstate.ht.reset(); return SourceResultType::FINISHED; } } @@ -1452,14 +1299,25 @@ ProgressData RadixPartitionedHashTable::GetProgress(ClientContext &, GlobalSinkS // Get partition combine progress, weigh it 2x ProgressData progress; for (auto &partition : sink.partitions) { - progress.done += 2.0 * partition->progress; + progress.done += 2.0 * partition->GetCombineProgress(); } - // Get scan progress, weigh it 1x - progress.done += 1.0 * double(gstate.task_done); + // Get scan progress in partitions, weigh it 1x - weigh the partitions whose scan started by the scanned rows + const auto partition_count = static_cast(sink.partitions.size()); + const auto started_partitions = static_cast(gstate.started_partitions.load(std::memory_order_relaxed)); + const auto started_rows = gstate.started_rows.load(std::memory_order_relaxed); + const auto scanned_rows = gstate.scanned_rows.load(std::memory_order_relaxed); + double scanned_partitions = started_partitions; + if (started_rows > 0) { + scanned_partitions *= + static_cast(MinValue(scanned_rows, started_rows)) / static_cast(started_rows); + } + auto scan_progress = + gstate.scan_progress.Update(ProgressData {scanned_partitions, MaxValue(partition_count, 1.0), false}); + progress.done += 1.0 * scan_progress.done; // Divide by 3x for the weights, and the number of partitions to get a value between 0 and 1 again - progress.total += 3.0 * double(sink.partitions.size()); + progress.total += 3.0 * partition_count; return progress; } diff --git a/src/duckdb/src/execution/sample/reservoir_sample.cpp b/src/duckdb/src/execution/sample/reservoir_sample.cpp index a3fd1f013..da5bf2da7 100644 --- a/src/duckdb/src/execution/sample/reservoir_sample.cpp +++ b/src/duckdb/src/execution/sample/reservoir_sample.cpp @@ -153,7 +153,7 @@ unique_ptr ReservoirSample::GetChunk() { unique_ptr ReservoirSample::CreateNewSampleChunk(vector &types, idx_t size) const { auto new_sample_chunk = make_uniq(); - new_sample_chunk->chunk.Initialize(Allocator::DefaultAllocator(), types, size); + new_sample_chunk->chunk.Initialize(allocator, types, size); // set the NULL columns correctly for (idx_t col_idx = 0; col_idx < types.size(); col_idx++) { @@ -190,7 +190,7 @@ void ReservoirSample::Vacuum() { } unique_ptr ReservoirSample::Copy() const { - auto ret = make_uniq(sample_count); + auto ret = make_uniq(allocator, sample_count); ret->stats_sample = stats_sample; ret->base_reservoir_sample = base_reservoir_sample->Copy(); @@ -739,7 +739,7 @@ void ReservoirSample::AddToReservoir(DataChunk &chunk) { for (idx_t i = 0; i < samples_remaining; i++) { input_sel.set_index(i, tuples_consumed + i); } - slice->Initialize(Allocator::DefaultAllocator(), types, samples_remaining); + slice->Initialize(allocator, types, samples_remaining); slice->Slice(chunk, input_sel, samples_remaining); AddToReservoir(*slice); return; @@ -899,6 +899,15 @@ unique_ptr ReservoirSamplePercentage::GetChunk() { return nullptr; } +idx_t ReservoirSamplePercentage::GetActiveSampleCount() const { + D_ASSERT(is_finalized); + idx_t count = 0; + for (auto &sample : finished_samples) { + count += sample->GetActiveSampleCount(); + } + return count; +} + unique_ptr ReservoirSamplePercentage::Copy() const { throw InternalException("Cannot call Copy on ReservoirSample Percentage"); } diff --git a/src/duckdb/src/function/aggregate/distributive/count.cpp b/src/duckdb/src/function/aggregate/distributive/count.cpp index 2e52552ae..f62027b09 100644 --- a/src/duckdb/src/function/aggregate/distributive/count.cpp +++ b/src/duckdb/src/function/aggregate/distributive/count.cpp @@ -149,10 +149,10 @@ struct CountFunction : public BaseCountFunction { static void CountClusteredRuns(const ClusteredAggr &cs, const ValidityMask &validity, INDEXER indexer) { const bool all_valid = !validity.CanHaveNull(); idx_t pos = 0; - for (idx_t r = 0; r < cs.n_group_runs; r++) { - auto &state = *reinterpret_cast(cs.group_runs[r].state); - const auto *run_sel = cs.group_runs[r].sel; - const auto run_count = cs.group_runs[r].count; + for (auto &run : cs.runs()) { + auto &state = *reinterpret_cast(run.state); + const auto *run_sel = run.sel; + const auto run_count = run.count; if (all_valid) { state += UnsafeNumericCast(run_count); } else { @@ -200,13 +200,13 @@ struct CountFunction : public BaseCountFunction { if (ConstantVector::IsNull(inputs[0])) { return; } - for (idx_t r = 0; r < clustered.n_group_runs; r++) { - auto &state = *reinterpret_cast(clustered.group_runs[r].state); - state += UnsafeNumericCast(clustered.group_runs[r].count); + for (auto &run : clustered.runs()) { + auto &state = *reinterpret_cast(run.state); + state += UnsafeNumericCast(run.count); } return; } - auto *cluster_iter = clustered.ClusterIter(inputs[0], count); + auto *cluster_iter = clustered.ClusterIter(inputs[0]); if (cluster_iter) { CountClusteredDict(inputs[0], clustered, count, cluster_iter); return; @@ -221,7 +221,7 @@ struct CountFunction : public BaseCountFunction { // pre-composes the dict sel once for the whole chunk. if (aggr_input_data.clustered) { auto &cs = *aggr_input_data.clustered; - auto *cluster_iter = cs.ClusterIter(inputs[0], count); + auto *cluster_iter = cs.ClusterIter(inputs[0]); if (cluster_iter) { CountClusteredDict(inputs[0], cs, count, cluster_iter); return; @@ -296,6 +296,7 @@ AggregateFunction CountFunctionBase::GetFunction() { fun.GetSignature().AddParameter("arg", LogicalTypeId::ANY); fun.SetName("count"); fun.SetOrderDependent(AggregateOrderDependent::NOT_ORDER_DEPENDENT); + fun.SetCollationHandling(FunctionCollationHandling::PUSH_DISTINCT_COLLATIONS); fun.SetStructStateExport(GetCountStateType); fun.SetStatisticsCallback(CountPropagateStats); return fun; diff --git a/src/duckdb/src/function/aggregate/distributive/first_last_any.cpp b/src/duckdb/src/function/aggregate/distributive/first_last_any.cpp index 46d7d11e2..c6e16c6bc 100644 --- a/src/duckdb/src/function/aggregate/distributive/first_last_any.cpp +++ b/src/duckdb/src/function/aggregate/distributive/first_last_any.cpp @@ -30,12 +30,7 @@ unique_ptr RewriteOrderedFirst(AggregateRewriteInput &input) { } aggregate->GetOrderBysMutable().reset(); - ErrorData error; - auto sort_key = - binder.BindScalarFunction(Identifier::DefaultSchema(), "create_sort_key", std::move(sort_children), error); - if (!sort_key) { - error.Throw(); - } + auto sort_key = binder.BindScalarFunction(Identifier::DefaultSchema(), "create_sort_key", std::move(sort_children)); auto children = std::move(aggregate->GetChildrenMutable()); children.emplace_back(std::move(sort_key)); @@ -86,24 +81,25 @@ struct FirstStringStateBase { bool value_is_valid; //! Whether the state has been set (i.e. we have seen a row) bool is_set; - //! The size of the arena allocation for a non-inlined string value - not part of the exported state - uint32_t alloc_size; + //! The size of the arena allocation for a non-inlined string value - not part of the exported state. + //! Must be wide enough for NextPowerOfTwo(MAX_STRING_SIZE), which does not fit in a uint32_t. + idx_t alloc_size; void Assign(string_t input, AggregateInputData &input_data) { if (input.IsInlined()) { value = input; alloc_size = 0; } else { - auto len = UnsafeNumericCast(input.GetSize()); + auto len = input.GetSize(); char *ptr; if (alloc_size >= len) { ptr = value.GetDataWriteable(); } else { - alloc_size = UnsafeNumericCast(NextPowerOfTwo(len)); + alloc_size = NextPowerOfTwo(len); ptr = char_ptr_cast(input_data.allocator.Allocate(alloc_size)); } memcpy(ptr, input.GetData(), len); - value = string_t(ptr, len); + value = string_t(ptr, UnsafeNumericCast(len)); } } }; @@ -384,10 +380,10 @@ void FirstFunctionClusterUpdate(Vector inputs[], AggregateInputData &aggregate_i inputs[0].ToUnifiedFormat(idata); auto input_data = UnifiedVectorFormat::GetData(idata); AggregateUnaryInput unary_input(aggregate_input_data, idata.validity); - for (idx_t r = 0; r < clustered.n_group_runs; r++) { - auto &state = *reinterpret_cast *>(clustered.group_runs[r].state); - const auto *run_sel = clustered.group_runs[r].sel; - const auto run_count = clustered.group_runs[r].count; + for (auto &run : clustered.runs()) { + auto &state = *reinterpret_cast *>(run.state); + const auto *run_sel = run.sel; + const auto run_count = run.count; FirstFunction::template ClusteredOp, FirstFunction>( state, input_data, unary_input, run_sel, *idata.sel, idata.validity, 0, run_count); } diff --git a/src/duckdb/src/function/aggregate/distributive/minmax.cpp b/src/duckdb/src/function/aggregate/distributive/minmax.cpp index 8d24996a9..eaca13d6e 100644 --- a/src/duckdb/src/function/aggregate/distributive/minmax.cpp +++ b/src/duckdb/src/function/aggregate/distributive/minmax.cpp @@ -1,3 +1,4 @@ +#include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/catalog/catalog_entry/aggregate_function_catalog_entry.hpp" #include "duckdb/catalog/catalog.hpp" #include "duckdb/function/aggregate_state_layout.hpp" @@ -156,23 +157,24 @@ using MaxOperation = NumericMinMaxBase; struct BaseMinMaxStringState { string_t value; bool is_set; - uint32_t alloc_size; + //! Must be wide enough for NextPowerOfTwo(MAX_STRING_SIZE), which does not fit in a uint32_t + idx_t alloc_size; void Assign(string_t input, AggregateInputData &input_data) { if (input.IsInlined()) { value = input; alloc_size = 0; } else { - auto len = UnsafeNumericCast(input.GetSize()); + auto len = input.GetSize(); char *ptr; if (alloc_size >= len) { ptr = value.GetDataWriteable(); } else { - alloc_size = UnsafeNumericCast(NextPowerOfTwo(len)); + alloc_size = NextPowerOfTwo(len); ptr = char_ptr_cast(input_data.allocator.Allocate(alloc_size)); } memcpy(ptr, input.GetData(), len); - value = string_t(ptr, len); + value = string_t(ptr, UnsafeNumericCast(len)); } } }; @@ -354,6 +356,19 @@ shared_ptr GetCollatedMinMaxFunction(ClientContext &con return func->functions.GetFunctionByOffset(best_function.GetIndex()); } +//! Whether min/max over the type are computed through arg_min/arg_max over the collated value +static bool MinMaxUsesCollation(ClientContext &context, const LogicalType &input_type) { + // The generic non-VARCHAR collation path is not ready yet (see internal #8704). BIT, TIMETZ and VARIANT use + // explicit binary-comparable keys so min/max follows the same logical order as comparisons and ORDER BY. + const auto varchar_collation = + input_type.id() == LogicalTypeId::VARCHAR && + (!StringType::GetCollation(input_type).empty() || !Settings::Get(context).empty()); + const auto nested_collation = StructType::IsStruct(input_type) || input_type.id() == LogicalTypeId::LIST || + input_type.id() == LogicalTypeId::ARRAY; + return input_type.id() == LogicalTypeId::BIT || input_type.id() == LogicalTypeId::TIME_TZ || + input_type.id() == LogicalTypeId::VARIANT || varchar_collation || nested_collation; +} + template unique_ptr BindMinMax(BindAggregateFunctionInput &input) { auto &context = input.GetClientContext(); @@ -361,13 +376,7 @@ unique_ptr BindMinMax(BindAggregateFunctionInput &input) { auto &arguments = input.GetArguments(); auto input_type = arguments[0]->GetReturnType(); - // The generic non-VARCHAR collation path is not ready yet (see internal #8704). BIT and VARIANT use explicit - // binary-comparable keys so min/max follows the same logical order as comparisons and ORDER BY. - const auto varchar_collation = - input_type.id() == LogicalTypeId::VARCHAR && - (!StringType::GetCollation(input_type).empty() || !Settings::Get(context).empty()); - const auto collation = - input_type.id() == LogicalTypeId::BIT || input_type.id() == LogicalTypeId::VARIANT || varchar_collation; + const auto collation = MinMaxUsesCollation(context, input_type); auto collated_arg = collation ? arguments[0]->Copy() : nullptr; if (collation && ExpressionBinder::PushCollation(context, collated_arg, collated_arg->GetReturnType())) { // If aggr function is min/max and uses collations, replace bound_function with arg_min/arg_max @@ -379,6 +388,7 @@ unique_ptr BindMinMax(BindAggregateFunctionInput &input) { // Bind function like arg_min/arg_max. arguments.push_back(std::move(collated_arg)); function.GetArguments()[0] = arguments[0]->GetReturnType(); + function.GetArguments()[1] = arguments[1]->GetReturnType(); function.SetReturnType(arguments[0]->GetReturnType()); return make_uniq(); } @@ -407,11 +417,25 @@ unique_ptr BindMinMax(BindAggregateFunctionInput &input) { return std::move(expr->BindInfoMutable()); } +//! BindMinMax replaces a collated min/max with arg_min/arg_max over the collation key, so the bound call has one +//! more argument than the definition and must be rendered under the replacement's own name +static unique_ptr MinMaxUnbind(AggregateFunctionUnbindInput &input) { + auto &function = input.expression.Function(); + auto name = function.GetDefinition()->GetQualifiedName(); + if (input.children.size() == function.GetLogicalArguments().size() + 1) { + name = function.GetQualifiedName(); + } else if (input.children.size() != function.GetLogicalArguments().size()) { + return nullptr; + } + return make_uniq(name, std::move(input.children)); +} + template AggregateFunction GetMinMaxOperator(const string &name) { AggregateFunction fun(Identifier(name), {}, LogicalType::ANY, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, BindMinMax); fun.GetSignature().AddParameter("arg", LogicalTypeId::ANY); + fun.SetUnbindCallback(MinMaxUnbind); return fun; } @@ -547,7 +571,7 @@ unique_ptr MinMaxNBind(BindAggregateFunctionInput &input) { throw ParameterNotResolvedException(); } } - if (arguments[0]->GetReturnType().id() == LogicalTypeId::VARIANT) { + if (MinMaxUsesCollation(context, arguments[0]->GetReturnType())) { auto collated_arg = arguments[0]->Copy(); if (ExpressionBinder::PushCollation(context, collated_arg, collated_arg->GetReturnType())) { vector types {arguments[0]->GetReturnType(), collated_arg->GetReturnType(), @@ -582,6 +606,7 @@ AggregateFunction GetMinMaxNFunction() { AggregateFunction fun({}, LogicalType::LIST(LogicalType::ANY), nullptr, nullptr, nullptr, nullptr, nullptr, FunctionNullHandling::DEFAULT_NULL_HANDLING, nullptr, MinMaxNBind, nullptr); fun.GetSignature().AddParameter("arg", LogicalTypeId::ANY).AddParameter("n", LogicalType::BIGINT); + fun.SetUnbindCallback(MinMaxUnbind); return fun; } diff --git a/src/duckdb/src/function/aggregate/sorted_aggregate_function.cpp b/src/duckdb/src/function/aggregate/sorted_aggregate_function.cpp index dfe4e6d09..318702a0b 100644 --- a/src/duckdb/src/function/aggregate/sorted_aggregate_function.cpp +++ b/src/duckdb/src/function/aggregate/sorted_aggregate_function.cpp @@ -63,12 +63,22 @@ struct SortedAggregateBindData : public FunctionData { using BindInfoPtr = unique_ptr; using OrderBys = vector; - SortedAggregateBindData(ClientContext &context, Expressions &children, BoundAggregateFunction &aggregate, - BindInfoPtr &bind_info, OrderBys &order_bys) + //! fixed_layout: the buffer struct and per-key columns to use instead of mapping the keys onto the arguments + SortedAggregateBindData( + ClientContext &context, Expressions &children, BoundAggregateFunction &aggregate, BindInfoPtr &bind_info, + OrderBys &order_bys, + optional_ptr>> fixed_layout = nullptr) : context(context), function(aggregate), bind_info(std::move(bind_info)), threshold(Settings::Get(context)) { vector order_spec; - buffered_types = MapSortedColumns(children, order_bys, order_spec); + if (fixed_layout) { + for (auto &child : StructType::GetChildTypes(fixed_layout->first)) { + buffered_types.emplace_back(child.second); + } + order_spec = fixed_layout->second; + } else { + buffered_types = MapSortedColumns(children, order_bys, order_spec); + } const idx_t argument_count = children.size(); // the arguments are the leading buffered columns (referencing their input slot); appended sort keys follow buffered_cols.resize(buffered_types.size()); @@ -86,9 +96,11 @@ struct SortedAggregateBindData : public FunctionData { BuildSort(order_spec); } - SortedAggregateBindData(ClientContext &context, BoundAggregateExpression &expr) + SortedAggregateBindData( + ClientContext &context, BoundAggregateExpression &expr, + optional_ptr>> fixed_layout = nullptr) : SortedAggregateBindData(context, expr.GetChildrenMutable(), expr.FunctionMutable(), expr.BindInfoMutable(), - expr.GetOrderBysMutable()->orders) { + expr.GetOrderBysMutable()->orders, fixed_layout) { } SortedAggregateBindData(ClientContext &context, BoundWindowExpression &expr) @@ -388,6 +400,35 @@ struct SortedAggregateFunction { AggregateStateInput state_input(aggr, bind_info); data_ptr_t agg_state_ptr = agg_state.data(); + // The inner aggregate (or the sort) can throw while its state is initialized - destroy it in that case + struct InnerStateGuard { + ~InnerStateGuard() { + if (live && destructor) { + try { + destructor(state_vec, input, 1); + } catch (...) { // NOLINT + } + } + } + aggregate_destructor_t destructor; + Vector &state_vec; + AggregateInputData &input; + bool live; + }; + InnerStateGuard inner_state_guard {destructor, agg_state_vec, aggr_bind_info, false}; + auto initialize_inner_state = [&]() { + initialize(state_input, &agg_state_ptr, 1); + inner_state_guard.live = true; + }; + auto finalize_inner_state = [&](idx_t result_idx) { + agg_state_vec.SetVectorType(states.GetVectorType()); + finalize(agg_state_vec, aggr_bind_info, result, 1, result_idx); + if (destructor) { + destructor(agg_state_vec, aggr_bind_info, 1); + } + inner_state_guard.live = false; + }; + auto sdata = states.Values(); vector state_unprocessed(count, 0); @@ -438,7 +479,7 @@ struct SortedAggregateFunction { auto global_source = sort->GetGlobalSourceState(client, *global_sink); auto local_source = sort->GetLocalSourceState(context, *global_source); - initialize(state_input, &agg_state_ptr, 1); + initialize_inner_state(); for (;;) { OperatorSourceInput source {*global_source, *local_source, interrupt}; scanned.Reset(); @@ -452,13 +493,8 @@ struct SortedAggregateFunction { // Find the next aggregate that needs data for (; !state_unprocessed[sorted]; ++sorted) { // Finalize a single value at the next offset - agg_state_vec.SetVectorType(states.GetVectorType()); - finalize(agg_state_vec, aggr_bind_info, result, 1, sorted + offset); - if (destructor) { - destructor(agg_state_vec, aggr_bind_info, 1); - } - - initialize(state_input, &agg_state_ptr, 1); + finalize_inner_state(sorted + offset); + initialize_inner_state(); } const auto input_count = MinValue(state_unprocessed[sorted], scanned.size() - consumed); for (column_t col_idx = 0; col_idx < scanned.ColumnCount(); ++col_idx) { @@ -484,11 +520,7 @@ struct SortedAggregateFunction { } // Finalize the last state for this sort - agg_state_vec.SetVectorType(states.GetVectorType()); - finalize(agg_state_vec, aggr_bind_info, result, 1, sorted + offset); - if (destructor) { - destructor(agg_state_vec, aggr_bind_info, 1); - } + finalize_inner_state(sorted + offset); ++sorted; // Stop if we are done @@ -503,15 +535,9 @@ struct SortedAggregateFunction { } for (; sorted < count; ++sorted) { - initialize(state_input, &agg_state_ptr, 1); - + initialize_inner_state(); // Finalize a single value at the next offset - agg_state_vec.SetVectorType(states.GetVectorType()); - finalize(agg_state_vec, aggr_bind_info, result, 1, sorted + offset); - - if (destructor) { - destructor(agg_state_vec, aggr_bind_info, 1); - } + finalize_inner_state(sorted + offset); } result.Verify(); @@ -555,6 +581,21 @@ pair> FunctionBinder::BindSortedAggregateState(ClientContext &context, const BoundAggregateFunction &inner_function, unique_ptr inner_bind_info, const LogicalType &buffer_struct, const vector &orders, idx_t argument_count) { + // the leading buffered columns are passed to the inner aggregate as-is - they must have its argument types + auto &buffer_columns = StructType::GetChildTypes(buffer_struct); + auto &inner_arguments = inner_function.GetArguments(); + if (argument_count != inner_arguments.size() || argument_count > buffer_columns.size()) { + throw BinderException("Aggregate state for \"%s\" has %llu state columns, expected at least %llu", + inner_function.GetName(), (uint64_t)buffer_columns.size(), + (uint64_t)inner_arguments.size()); + } + for (idx_t i = 0; i < argument_count; i++) { + if (inner_arguments[i].IsComplete() && buffer_columns[i].second != inner_arguments[i]) { + throw BinderException("Aggregate state for \"%s\" has state column %llu of type %s, expected %s", + inner_function.GetName(), (uint64_t)i, buffer_columns[i].second.ToString(), + inner_arguments[i].ToString()); + } + } const auto null_handling = inner_function.GetProperties().GetNullHandling(); auto bind_data = make_uniq(context, inner_function, std::move(inner_bind_info), buffer_struct, orders, argument_count); @@ -587,29 +628,34 @@ void FunctionBinder::BindSortedAggregate(ClientContext &context, BoundAggregateE auto &children = expr.GetChildrenMutable(); auto &order_bys = *expr.GetOrderBysMutable(); + // the layout of an exported state was fixed at bind time and is part of its type - keep it, as the optimizer may + // have changed the expressions since (e.g. so that an ORDER BY key no longer matches the argument it matched) + pair> export_layout; if (state_export) { + export_layout.first = ListType::GetChildType(expr.GetReturnType()); + auto &logical_fields = StructType::GetChildTypes(export_layout.first); + ExportAggregateFunction::GetSortedStateOrders(expr.GetReturnType(), export_layout.second); + if (export_layout.second.size() != order_bys.orders.size() || children.size() > logical_fields.size()) { + throw InternalException("Exported state layout of ordered aggregate \"%s\" does not match its ORDER BY", + bound_function.GetName()); + } // the statistics optimizer may have narrowed the buffered column types since the exported type was fixed at // bind time (e.g. a small-range group column INTEGER->TINYINT) - cast them back so the buffer matches its - // declared type. The widening preserves sort order; casting reused columns to the same type keeps matching. - LogicalType plan_struct; - vector order_columns; - idx_t argument_count; - GetSortedAggregateStateLayout(expr, plan_struct, order_columns, argument_count); - auto &logical_fields = StructType::GetChildTypes(ListType::GetChildType(expr.GetReturnType())); + // declared type. The widening preserves sort order. auto cast_to = [&](unique_ptr &e, const LogicalType &type) { if (e->GetReturnType() != type) { e = BoundCastExpression::AddCastToType(context, std::move(e), type); } }; - for (idx_t i = 0; i < children.size() && i < logical_fields.size(); i++) { + for (idx_t i = 0; i < children.size(); i++) { cast_to(children[i], logical_fields[i].second); } - for (idx_t o = 0; o < order_bys.orders.size() && o < order_columns.size(); o++) { - cast_to(order_bys.orders[o].expression, logical_fields[order_columns[o].column].second); + for (idx_t o = 0; o < order_bys.orders.size(); o++) { + cast_to(order_bys.orders[o].expression, logical_fields[export_layout.second[o].column].second); } } - auto sorted_bind = make_uniq(context, expr); + auto sorted_bind = make_uniq(context, expr, state_export ? &export_layout : nullptr); if (!sorted_bind->sorted_on_args) { // The arguments are the children plus the sort columns. diff --git a/src/duckdb/src/function/aggregate_function.cpp b/src/duckdb/src/function/aggregate_function.cpp index b65e83723..68d8686f8 100644 --- a/src/duckdb/src/function/aggregate_function.cpp +++ b/src/duckdb/src/function/aggregate_function.cpp @@ -1,3 +1,4 @@ +#include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/function/aggregate_function.hpp" #include "duckdb/execution/operator/aggregate/aggregate_object.hpp" @@ -8,6 +9,14 @@ namespace duckdb { +AggregateFunctionUnbindInput::AggregateFunctionUnbindInput(const BoundAggregateExpression &expression_p, + vector> children_p) + : expression(expression_p), children(std::move(children_p)) { +} + +AggregateFunctionUnbindInput::~AggregateFunctionUnbindInput() { +} + unique_ptr AggregateFunction::PropagateInputValueStats(ClientContext &context, BoundAggregateExpression &expr, AggregateStatisticsInput &input) { @@ -74,7 +83,8 @@ void AggregateFinalizeInputData::InitializeLocalState() { bool AggregateFunctionProperties::operator==(const AggregateFunctionProperties &rhs) const { return FunctionProperties::operator==(rhs) && order_dependent == rhs.order_dependent && - distinct_dependent == rhs.distinct_dependent && single_value_identity == rhs.single_value_identity; + distinct_dependent == rhs.distinct_dependent && single_value_identity == rhs.single_value_identity && + is_holistic == rhs.is_holistic; } bool AggregateFunctionProperties::operator!=(const AggregateFunctionProperties &rhs) const { return !(*this == rhs); @@ -85,7 +95,7 @@ bool AggregateFunctionCallbacks::operator==(const AggregateFunctionCallbacks &rh combine == rhs.combine && finalize == rhs.finalize && init_local_state_finalize == rhs.init_local_state_finalize && cluster_update == rhs.cluster_update && window == rhs.window && window_init == rhs.window_init && window_batch == rhs.window_batch && - bind == rhs.bind && destructor == rhs.destructor && statistics == rhs.statistics && + bind == rhs.bind && unbind == rhs.unbind && destructor == rhs.destructor && statistics == rhs.statistics && serialize == rhs.serialize && deserialize == rhs.deserialize && direct_rewrite == rhs.direct_rewrite && rewrite == rhs.rewrite && rewrite_policy == rhs.rewrite_policy && rewrite_optimizer_type == rhs.rewrite_optimizer_type && rewrite_cost == rhs.rewrite_cost && @@ -114,9 +124,7 @@ BoundAggregateFunction::BoundAggregateFunction(const AggregateFunction &function BoundAggregateFunction::BoundAggregateFunction(shared_ptr function_p) : definition(std::move(function_p)) { auto &function = *definition; - name = function.name; - schema_name = function.GetSchemaName(); - catalog_name = function.GetCatalogName(); + qualified_name = function.GetQualifiedName(); extra_info = function.extra_info; return_type = function.GetReturnType(); properties = function.GetProperties(); @@ -126,14 +134,18 @@ BoundAggregateFunction::BoundAggregateFunction(shared_ptrname = function.name; - this->schema_name = function.GetSchemaName(); - this->catalog_name = function.GetCatalogName(); + SetName(function.GetName()); + // The replacement is a specialized implementation of the function we were bound from, and is usually built by + // a factory rather than handed out by a catalog entry. Only take its qualification when it has one, so that + // specializing an implementation does not drop the catalog and schema name of the definition. + if (!function.GetCatalogName().empty() || !function.GetSchemaName().empty()) { + qualified_name = function.GetQualifiedName(); + } this->return_type = function.GetReturnType(); this->properties = function.GetProperties(); this->callbacks = function.GetCallbacks(); @@ -159,7 +175,9 @@ void BoundAggregateFunction::ReplaceImplementation(const AggregateFunction &func // "bind" callback) arguments.clear(); for (auto ¶m : function.GetSignature().GetParameters()) { - arguments.push_back(param.GetType()); + if (!param.IsVariadic()) { + arguments.push_back(param.GetType()); + } } } diff --git a/src/duckdb/src/function/aggregate_state_serialization.cpp b/src/duckdb/src/function/aggregate_state_serialization.cpp index 0dfd35964..0b6c6f4fa 100644 --- a/src/duckdb/src/function/aggregate_state_serialization.cpp +++ b/src/duckdb/src/function/aggregate_state_serialization.cpp @@ -270,10 +270,20 @@ static void DeserializeField(const LogicalType &type, const AggregateStateField RecursiveUnifiedVectorFormat child_data; Vector::RecursiveToUnifiedFormat(*physical_child, child_data); + const auto child_validity = logical_child.Validity(); for (idx_t i = 0; i < count; i++) { LinkedList linked_list; const auto entry = values[i]; if (entry.IsValid()) { + if (field.reject_null_elements) { + const auto &list_entry = entry.GetValue(); + for (idx_t child_idx = 0; child_idx < list_entry.length; child_idx++) { + if (!child_validity.IsValid(list_entry.offset + child_idx)) { + throw InvalidInputException( + "Invalid aggregate state - the list cannot contain NULL values"); + } + } + } // NULL inputs keep an empty linked list field.list_functions.AppendListEntry(allocator, linked_list, child_data, entry.GetValue()); } @@ -298,6 +308,9 @@ void AggregateStateSerialization::DeserializeStates(const BoundAggregateFunction } DeserializeField(layout.type, layout.field, input_vec, count, dest_buffer, layout.total_state_size, 0, allocator, ownership); + if (layout.validate_state) { + layout.validate_state(dest_buffer, count, layout.total_state_size); + } } void AggregateStateSerialization::SerializeStates(const AggregateStateLayout &layout, Vector &result, idx_t count, diff --git a/src/duckdb/src/function/built_in_functions.cpp b/src/duckdb/src/function/built_in_functions.cpp index a73626d2e..a9b490c4e 100644 --- a/src/duckdb/src/function/built_in_functions.cpp +++ b/src/duckdb/src/function/built_in_functions.cpp @@ -112,7 +112,7 @@ static unique_ptr BindExtensionFunction(FunctionBindExpressionInput auto &extension_name = function_info.extension; auto &db = *context.db; - if (!ExtensionHelper::CanAutoloadExtension(extension_name)) { + if (!ExtensionHelper::CanAutoloadExtension(db, extension_name)) { throw BinderException("Trying to call function %s which is present in extension \"%s\" - but the extension " "is not loaded and could not be auto-loaded", bound_function.GetName(), extension_name); diff --git a/src/duckdb/src/optimizer/builtin_function_lookup.cpp b/src/duckdb/src/function/builtin_function_lookup.cpp similarity index 59% rename from src/duckdb/src/optimizer/builtin_function_lookup.cpp rename to src/duckdb/src/function/builtin_function_lookup.cpp index ab42e5da8..6b38e38e7 100644 --- a/src/duckdb/src/optimizer/builtin_function_lookup.cpp +++ b/src/duckdb/src/function/builtin_function_lookup.cpp @@ -1,8 +1,9 @@ -#include "duckdb/optimizer/builtin_function_lookup.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/catalog/catalog.hpp" #include "duckdb/catalog/catalog_entry/aggregate_function_catalog_entry.hpp" #include "duckdb/catalog/catalog_entry/scalar_function_catalog_entry.hpp" +#include "duckdb/catalog/catalog_entry/window_function_catalog_entry.hpp" #include "duckdb/function/function_binder.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" @@ -46,14 +47,42 @@ shared_ptr TryGetBuiltinAggregateFunction(ClientContext return entry.functions.GetFunctionByOffset(index.GetIndex()); } -unique_ptr BindBuiltinScalarFunction(ClientContext &context, const Identifier &name, - vector> children) { - vector arguments; +shared_ptr GetBuiltinWindowFunction(ClientContext &context, const Identifier &name, + const vector &arguments) { + auto &catalog = Catalog::GetSystemCatalog(context); + auto &entry = catalog.GetEntry(context, BuiltinName(catalog, name)); + return entry.functions.GetFunctionByArguments(context, arguments); +} + +unique_ptr BindBuiltinScalarExpression(ClientContext &context, const Identifier &name, + vector> children) { + auto &catalog = Catalog::GetSystemCatalog(context); + auto &entry = catalog.GetEntry(context, BuiltinName(catalog, name)); + + // Bind the children as the arguments of a call, so that functions that capture the aliases of their arguments + // (e.g. struct_pack) receive them as named arguments + vector>> arguments; arguments.reserve(children.size()); for (auto &child : children) { - arguments.push_back(child->GetReturnType()); + arguments.emplace_back(Identifier(), std::move(child)); + } + + ErrorData error; + FunctionBinder function_binder(context); + auto expr = function_binder.BindScalarFunction(entry, std::move(arguments), error); + if (!expr) { + error.Throw(); + } + return expr; +} + +unique_ptr BindBuiltinScalarFunction(ClientContext &context, const Identifier &name, + vector> children) { + auto expr = BindBuiltinScalarExpression(context, name, std::move(children)); + if (expr->GetExpressionClass() != ExpressionClass::BOUND_FUNCTION) { + throw InvalidInputException("BindBuiltinScalarFunction did not return a BoundFunctionExpression"); } - return GetBuiltinScalarFunction(context, name, arguments)->Bind(context, std::move(children)); + return unique_ptr_cast(std::move(expr)); } } // namespace duckdb diff --git a/src/duckdb/src/function/cast/array_casts.cpp b/src/duckdb/src/function/cast/array_casts.cpp index a115cc3e5..118dd5690 100644 --- a/src/duckdb/src/function/cast/array_casts.cpp +++ b/src/duckdb/src/function/cast/array_casts.cpp @@ -7,6 +7,7 @@ #include "duckdb/function/cast/default_casts.hpp" #include "duckdb/function/cast/bound_cast_data.hpp" #include "duckdb/common/operator/cast_operators.hpp" +#include "duckdb/function/cast/vector_cast_helpers.hpp" namespace duckdb { @@ -51,11 +52,9 @@ static bool ArrayToArrayCast(Vector &source, Vector &result, idx_t count, CastPa auto msg = StringUtil::Format("Cannot cast array of size %u to array of size %u", source_array_size, target_array_size); HandleCastError::AssignError(msg, parameters); - if (!parameters.strict) { - // if this was a TRY_CAST, we know every row will fail, so just return null - ConstantVector::SetNull(result, count_t(count)); - return false; - } + // every row fails, so just return null + ConstantVector::SetNull(result, count_t(count)); + return false; } auto &cast_data = parameters.cast_data->Cast(); @@ -106,6 +105,14 @@ static bool ArrayToVarcharCast(Vector &source, Vector &result, idx_t count, Cast auto in_data = FlatVector::GetData(child); + // string elements are quoted and escaped where required, as for lists + auto child_is_nested = ArrayType::GetChildType(source.GetType()).IsNested(); + auto string_length_func = child_is_nested ? VectorCastHelpers::CalculateStringLength + : VectorCastHelpers::CalculateEscapedStringLength; + auto write_string_func = + child_is_nested ? VectorCastHelpers::WriteString : VectorCastHelpers::WriteEscapedString; + auto needs_quotes = make_unsafe_uniq_array_uninitialized(MaxValue(size, 1)); + static constexpr idx_t SEP_LENGTH = 2; static constexpr idx_t NULL_LENGTH = 4; @@ -124,7 +131,8 @@ static bool ArrayToVarcharCast(Vector &source, Vector &result, idx_t count, Cast if (j > 0) { array_varchar_length += SEP_LENGTH; } - array_varchar_length += child_validity.RowIsValid(elem_idx) ? elem.GetSize() : NULL_LENGTH; + array_varchar_length += + child_validity.RowIsValid(elem_idx) ? string_length_func(elem, needs_quotes[j]) : NULL_LENGTH; } auto &out_str = result_data.WriteEmptyString(array_varchar_length); @@ -141,9 +149,7 @@ static bool ArrayToVarcharCast(Vector &source, Vector &result, idx_t count, Cast offset += SEP_LENGTH; } if (child_validity.RowIsValid(elem_idx)) { - auto len = elem.GetSize(); - memcpy(dataptr + offset, elem.GetData(), len); - offset += len; + offset += write_string_func(dataptr + offset, elem, needs_quotes[j]); } else { memcpy(dataptr + offset, "NULL", NULL_LENGTH); offset += NULL_LENGTH; diff --git a/src/duckdb/src/function/cast/bignum_casts.cpp b/src/duckdb/src/function/cast/bignum_casts.cpp index 45e9336bd..11b6e0ec3 100644 --- a/src/duckdb/src/function/cast/bignum_casts.cpp +++ b/src/duckdb/src/function/cast/bignum_casts.cpp @@ -2,8 +2,8 @@ #include "duckdb/common/operator/cast_operators.hpp" #include "duckdb/function/cast/vector_cast_helpers.hpp" #include "duckdb/common/types/bignum.hpp" -#include #include "duckdb/common/bignum.hpp" +#include "duckdb/common/bit_utils.hpp" namespace duckdb { @@ -23,10 +23,11 @@ static bignum_t IntToBignum(StringHeap &heap, T int_value) { abs_value = static_cast(int_value); } uint32_t data_byte_size; - if (abs_value != NumericLimits::Maximum()) { - data_byte_size = (abs_value == 0) ? 1 : static_cast(std::ceil(std::log2(abs_value + 1) / 8.0)); + if (abs_value == 0) { + data_byte_size = 1; } else { - data_byte_size = static_cast(std::ceil(std::log2(abs_value) / 8.0)); + const uint32_t bits_needed = 64 - static_cast(CountZeros::Leading(abs_value)); + data_byte_size = (bits_needed + 7) / 8; } uint32_t blob_size = data_byte_size + Bignum::BIGNUM_HEADER_SIZE; @@ -51,11 +52,11 @@ static bignum_t IntToBignum(StringHeap &heap, T int_value) { template <> bignum_t HugeintCastToBignum::Operation(uhugeint_t int_value, StringHeap &heap) { uint32_t data_byte_size; - if (int_value.upper != NumericLimits::Maximum()) { - data_byte_size = - (int_value.upper == 0) ? 0 : static_cast(std::ceil(std::log2(int_value.upper + 1) / 8.0)); + if (int_value.upper == 0) { + data_byte_size = 0; } else { - data_byte_size = static_cast(std::ceil(std::log2(int_value.upper) / 8.0)); + const uint32_t bits_needed = 64 - static_cast(CountZeros::Leading(int_value.upper)); + data_byte_size = (bits_needed + 7) / 8; } uint32_t upper_byte_size = data_byte_size; @@ -63,10 +64,9 @@ bignum_t HugeintCastToBignum::Operation(uhugeint_t int_value, StringHeap &heap) // If we have at least one byte on the upper side, the bottom side is complete data_byte_size += 8; } else { - if (int_value.lower != NumericLimits::Maximum()) { - data_byte_size += static_cast(std::ceil(std::log2(int_value.lower + 1) / 8.0)); - } else { - data_byte_size += static_cast(std::ceil(std::log2(int_value.lower) / 8.0)); + if (int_value.lower != 0) { + const uint32_t bits_needed = 64 - static_cast(CountZeros::Leading(int_value.lower)); + data_byte_size += (bits_needed + 7) / 8; } } if (data_byte_size == 0) { @@ -115,11 +115,11 @@ bignum_t HugeintCastToBignum::Operation(hugeint_t int_value, StringHeap &heap) { uint64_t abs_value_upper = static_cast(int_value.upper); uint32_t data_byte_size; - if (abs_value_upper != NumericLimits::Maximum()) { - data_byte_size = - (abs_value_upper == 0) ? 0 : static_cast(std::ceil(std::log2(abs_value_upper + 1) / 8.0)); + if (abs_value_upper == 0) { + data_byte_size = 0; } else { - data_byte_size = static_cast(std::ceil(std::log2(abs_value_upper) / 8.0)); + const uint32_t bits_needed = 64 - static_cast(CountZeros::Leading(abs_value_upper)); + data_byte_size = (bits_needed + 7) / 8; } uint32_t upper_byte_size = data_byte_size; @@ -127,10 +127,9 @@ bignum_t HugeintCastToBignum::Operation(hugeint_t int_value, StringHeap &heap) { // If we have at least one byte on the upper side, the bottom side is complete data_byte_size += 8; } else { - if (int_value.lower != NumericLimits::Maximum()) { - data_byte_size += static_cast(std::ceil(std::log2(int_value.lower + 1) / 8.0)); - } else { - data_byte_size += static_cast(std::ceil(std::log2(int_value.lower) / 8.0)); + if (int_value.lower != 0) { + const uint32_t bits_needed = 64 - static_cast(CountZeros::Leading(int_value.lower)); + data_byte_size += (bits_needed + 7) / 8; } } @@ -277,6 +276,18 @@ BoundCastInfo Bignum::NumericToBignumCastSwitch(const LogicalType &source) { } } +struct BignumToDoubleErrorCast { + template + static inline bool Operation(SRC input, DST &result, CastParameters ¶meters) { + if (Bignum::BignumToDouble(input, result, parameters.strict)) { + return true; + } + HandleCastError::AssignError( + StringUtil::Format("Could not convert bignum '%s' to Double", Bignum::BignumToVarchar(input)), parameters); + return false; + } +}; + BoundCastInfo DefaultCasts::BignumCastSwitch(BindCastInput &input, const LogicalType &source, const LogicalType &target) { D_ASSERT(source.id() == LogicalTypeId::BIGNUM); @@ -314,7 +325,7 @@ BoundCastInfo DefaultCasts::BignumCastSwitch(BindCastInput &input, const Logical case LogicalTypeId::VARCHAR: return BoundCastInfo(&VectorCastHelpers::StringCast); case LogicalTypeId::DOUBLE: - return BoundCastInfo(&VectorCastHelpers::TryCastLoop); + return BoundCastInfo(&VectorCastHelpers::TryCastErrorLoop); default: return TryVectorNullCast; } diff --git a/src/duckdb/src/function/cast/blob_cast.cpp b/src/duckdb/src/function/cast/blob_cast.cpp index 556996f48..65f90e17f 100644 --- a/src/duckdb/src/function/cast/blob_cast.cpp +++ b/src/duckdb/src/function/cast/blob_cast.cpp @@ -15,8 +15,9 @@ BoundCastInfo DefaultCasts::BlobCastSwitch(BindCastInput &input, const LogicalTy case LogicalTypeId::LEGACY_AGGREGATE_STATE: return DefaultCasts::ReinterpretCast; case LogicalTypeId::BIT: - return BoundCastInfo(&VectorCastHelpers::StringCast); - + return BoundCastInfo(&VectorCastHelpers::TryCastStringLoop); + case LogicalTypeId::GEOMETRY: + return BlobToGeoCast(input, source, target); default: return DefaultCasts::TryVectorNullCast; } diff --git a/src/duckdb/src/function/cast/cast_function_set.cpp b/src/duckdb/src/function/cast/cast_function_set.cpp index 4fa392db3..665a066db 100644 --- a/src/duckdb/src/function/cast/cast_function_set.cpp +++ b/src/duckdb/src/function/cast/cast_function_set.cpp @@ -39,6 +39,10 @@ CastFunctionSet &CastFunctionSet::Get(ClientContext &context) { return DBConfig::GetConfig(context).GetCastFunctions(); } +const CastFunctionSet &CastFunctionSet::Get(const ClientContext &context) { + return DBConfig::GetConfig(context).GetCastFunctions(); +} + CollationBinding &CollationBinding::Get(ClientContext &context) { return DBConfig::GetConfig(context).GetCollationBinding(); } @@ -58,6 +62,11 @@ BoundCastInfo CastFunctionSet::GetCastFunction(const LogicalType &source, const result.SetStatisticsCallback(CastStatistics::Propagate); return result; } + if (registration_probe) { + // VARIANT selects additional casts from runtime values. + registered_cast_found |= + source.id() == LogicalTypeId::VARIANT || registration_probe->HasRegisteredCast(source, target); + } auto bind_cast = [&](BindCastFunction &bind_function) { BindCastInput input(*this, bind_function.info.get(), get_input.context); input.query_location = get_input.query_location; @@ -98,8 +107,8 @@ struct MapCastNode { int64_t implicit_cast_cost; }; -template -static auto RelaxedTypeMatch(type_map_t &map, const LogicalType &type) -> decltype(map.find(type)) { +template +static auto RelaxedTypeMatch(MAP &map, const LogicalType &type) -> decltype(map.find(type)) { D_ASSERT(map.find(type) == map.end()); // we shouldn't be here switch (type.id()) { case LogicalTypeId::LIST: @@ -137,7 +146,7 @@ static auto RelaxedTypeMatch(type_map_t &map, const LogicalType struct MapCastInfo : public BindCastInfo { public: - const optional_ptr GetEntry(const LogicalType &source, const LogicalType &target) { + optional_ptr GetEntry(const LogicalType &source, const LogicalType &target) const { auto source_type_id_entry = casts.find(source.id()); if (source_type_id_entry == casts.end()) { source_type_id_entry = casts.find(LogicalTypeId::ANY); @@ -184,8 +193,29 @@ struct MapCastInfo : public BindCastInfo { type_id_map_t>>> casts; }; +bool CastFunctionSet::HasRegisteredCast(const LogicalType &source, const LogicalType &target) const { + return map_info && map_info->GetEntry(source, target); +} + +bool CastFunctionSet::CanOverrideDefaultCast(const LogicalType &source, const LogicalType &target) const { + if (!map_info || source == target) { + return false; + } + if (HasRegisteredCast(source, target)) { + return true; + } + CastFunctionSet probe; + probe.registration_probe = this; + GetCastFunctionInput input; + probe.GetCastFunction(source, target, input); + return probe.registered_cast_found; +} + int64_t CastFunctionSet::ImplicitCastCost(optional_ptr context, const LogicalType &source, const LogicalType &target) { + if (registration_probe && registration_probe->HasRegisteredCast(source, target)) { + registered_cast_found = true; + } // check if a cast has been registered if (map_info) { auto entry = map_info->GetEntry(source, target); diff --git a/src/duckdb/src/function/cast/cast_statistics.cpp b/src/duckdb/src/function/cast/cast_statistics.cpp index ed8ad55e9..baebb777a 100644 --- a/src/duckdb/src/function/cast/cast_statistics.cpp +++ b/src/duckdb/src/function/cast/cast_statistics.cpp @@ -153,6 +153,10 @@ static unique_ptr StatisticsPropagateVariant(const BaseStatistic // only try this for non-nested return nullptr; } + if (input.GetStatsType() != StatisticsType::VARIANT_STATS) { + // e.g. the unknown statistics of a prepared statement parameter + return nullptr; + } if (!VariantStats::IsShredded(input)) { // not shredded return nullptr; @@ -165,12 +169,18 @@ static unique_ptr StatisticsPropagateVariant(const BaseStatistic } // extract the typed stats auto &typed_stats = VariantStats::GetTypedStats(shredded_stats); + unique_ptr result; if (structured_type == target) { // type matches - return stats directly - return typed_stats.ToUnique(); + result = typed_stats.ToUnique(); + } else { + // typed stats don't match - try to cast + result = CastStatistics::TryPropagate(typed_stats, structured_type, target); + } + if (result && input.CanHaveNull()) { + result->SetHasNull(); } - // typed stats don't match - try to cast - return CastStatistics::TryPropagate(typed_stats, structured_type, target); + return result; } static unique_ptr StatisticsPropagateArrayToList(const BaseStatistics &input, const LogicalType &source, diff --git a/src/duckdb/src/function/cast/default_casts.cpp b/src/duckdb/src/function/cast/default_casts.cpp index e9c739007..1867fd882 100644 --- a/src/duckdb/src/function/cast/default_casts.cpp +++ b/src/duckdb/src/function/cast/default_casts.cpp @@ -179,9 +179,7 @@ BoundCastInfo DefaultCasts::GetDefaultCastFunction(BindCastInput &input, const L case LogicalTypeId::MAP: return MapCastSwitch(input, source, target); case LogicalTypeId::STRUCT: - // an unnamed STRUCT (e.g. from variant shredding with empty keys) is cast positionally, like a TUPLE - return StructType::IsUnnamed(source) ? TupleCastSwitch(input, source, target) - : StructCastSwitch(input, source, target); + return StructCastSwitch(input, source, target); case LogicalTypeId::TUPLE: return TupleCastSwitch(input, source, target); case LogicalTypeId::LIST: diff --git a/src/duckdb/src/function/cast/enum_casts.cpp b/src/duckdb/src/function/cast/enum_casts.cpp index 9fdea2cba..18bdfc523 100644 --- a/src/duckdb/src/function/cast/enum_casts.cpp +++ b/src/duckdb/src/function/cast/enum_casts.cpp @@ -16,7 +16,9 @@ static bool EnumEnumCast(Vector &source, Vector &result, idx_t count, CastParame auto key = EnumType::GetPos(res_enum_type, dictionary_data[value]); if (key == -1) { if (!parameters.error_message) { - HandleCastError::AssignError(CastExceptionText(value), vector_cast_data.parameters); + HandleCastError::AssignError( + CastExceptionText(dictionary_data[value], source.GetType(), res_enum_type), + vector_cast_data.parameters); vector_cast_data.all_converted = false; } return nullopt; @@ -101,13 +103,14 @@ static bool EnumToAnyCast(Vector &source, Vector &result, idx_t count, CastParam // cast to varchar CastParameters to_varchar_params(parameters, cast_data.to_varchar_cast.GetCastData(), lstate.to_varchar_local); - cast_data.to_varchar_cast.Cast(source, varchar_cast, count, to_varchar_params); + if (!cast_data.to_varchar_cast.Cast(source, varchar_cast, count, to_varchar_params)) { + return false; + } // cast from varchar to the target CastParameters from_varchar_params(parameters, cast_data.from_varchar_cast.GetCastData(), lstate.from_varchar_local); - cast_data.from_varchar_cast.Cast(varchar_cast, result, count, from_varchar_params); - return true; + return cast_data.from_varchar_cast.Cast(varchar_cast, result, count, from_varchar_params); } BoundCastInfo DefaultCasts::EnumCastSwitch(BindCastInput &input, const LogicalType &source, const LogicalType &target) { diff --git a/src/duckdb/src/function/cast/geo_casts.cpp b/src/duckdb/src/function/cast/geo_casts.cpp index 8fea38bd3..a1de3d7d4 100644 --- a/src/duckdb/src/function/cast/geo_casts.cpp +++ b/src/duckdb/src/function/cast/geo_casts.cpp @@ -1,7 +1,9 @@ #include "duckdb/common/types/geometry.hpp" #include "duckdb/common/types/geometry_crs.hpp" #include "duckdb/function/cast/default_casts.hpp" +#include "duckdb/function/cast/vector_cast_helpers.hpp" #include "duckdb/common/exception/binder_exception.hpp" +#include "duckdb/common/error_data.hpp" namespace duckdb { @@ -12,11 +14,35 @@ static bool GeometryToVarcharCast(Vector &source, Vector &result, idx_t count, C return true; } +static bool GeometryToBlobCast(Vector &source, Vector &result, idx_t count, CastParameters ¶meters) { + Geometry::ToBinary(source, result); + return true; +} + +struct TryCastBlobToGeometry { + template + static bool Operation(SRC input, DST &result, Vector &result_vector, CastParameters ¶meters) { + try { + return Geometry::FromBinary(input, result, StringVector::GetStringHeap(result_vector), true); + } catch (InvalidInputException &ex) { + ErrorData error(ex); + HandleCastError::AssignError("Could not convert BLOB to GEOMETRY: " + error.RawMessage(), parameters); + return false; + } + } +}; + +BoundCastInfo DefaultCasts::BlobToGeoCast(BindCastInput &input, const LogicalType &source, const LogicalType &target) { + return BoundCastInfo(&VectorCastHelpers::TryCastStringLoop); +} + BoundCastInfo DefaultCasts::GeoCastSwitch(BindCastInput &input, const LogicalType &source, const LogicalType &target) { // now switch on the result type switch (target.id()) { case LogicalTypeId::VARCHAR: return GeometryToVarcharCast; + case LogicalTypeId::BLOB: + return GeometryToBlobCast; case LogicalTypeId::GEOMETRY: { // If the coordinate reference systems differ, we may not be able to cast if (GeoType::HasCRS(source) && GeoType::HasCRS(target)) { diff --git a/src/duckdb/src/function/cast/nested_to_varchar_cast.cpp b/src/duckdb/src/function/cast/nested_to_varchar_cast.cpp index 2af4cb569..af473fe05 100644 --- a/src/duckdb/src/function/cast/nested_to_varchar_cast.cpp +++ b/src/duckdb/src/function/cast/nested_to_varchar_cast.cpp @@ -20,7 +20,7 @@ const bool NestedToVarcharCast::LOOKUP_TABLE[256] = { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, // [ - false, + true, // backslash true, // ] false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, diff --git a/src/duckdb/src/function/cast/string_cast.cpp b/src/duckdb/src/function/cast/string_cast.cpp index f02ef75ae..ce5893030 100644 --- a/src/duckdb/src/function/cast/string_cast.cpp +++ b/src/duckdb/src/function/cast/string_cast.cpp @@ -4,6 +4,7 @@ #include "duckdb/common/vector/list_vector.hpp" #include "duckdb/common/vector/map_vector.hpp" #include "duckdb/common/vector/struct_vector.hpp" +#include "duckdb/common/vector/vector_iterator.hpp" #include "duckdb/function/cast/default_casts.hpp" #include "duckdb/function/cast/vector_cast_helpers.hpp" #include "duckdb/common/exception/conversion_exception.hpp" @@ -27,7 +28,7 @@ static bool StringEnumCastLoop(const VectorIterator &source_data, Vect auto &source_val = source_entry.GetValue(); auto pos = EnumType::GetPos(result_type, source_val); if (pos == -1) { - result_data[i] = HandleVectorCastError::Operation(CastExceptionText(source_val), + result_data[i] = HandleVectorCastError::Operation(CastExceptionText(source_val, result_type), result_data, i, vector_cast_data); } else { result_data[i] = UnsafeNumericCast(pos); @@ -305,7 +306,7 @@ bool VectorStringToMap::StringToNestedTypeCastLoop(const string_t *source_data, } list_data[i].length = total - list_data[i].offset; } - D_ASSERT(total_elements == total); + D_ASSERT(total <= total_elements); auto &result_key_child = MapVector::GetKeys(result); auto &result_val_child = MapVector::GetValues(result); @@ -313,18 +314,20 @@ bool VectorStringToMap::StringToNestedTypeCastLoop(const string_t *source_data, auto &lstate = parameters.local_state->Cast(); CastParameters key_params(parameters, cast_data.key_cast.GetCastData(), lstate.key_state); - if (!cast_data.key_cast.Cast(varchar_key_vector, result_key_child, total_elements, key_params)) { + if (!cast_data.key_cast.Cast(varchar_key_vector, result_key_child, total, key_params)) { vector_cast_data.all_converted = false; } CastParameters val_params(parameters, cast_data.value_cast.GetCastData(), lstate.value_state); - if (!cast_data.value_cast.Cast(varchar_val_vector, result_val_child, total_elements, val_params)) { + if (!cast_data.value_cast.Cast(varchar_val_vector, result_val_child, total, val_params)) { vector_cast_data.all_converted = false; } // set the list size after the child casts, since the casts may have replaced the child buffers - ListVector::SetListSize(result, total_elements); + ListVector::SetListSize(result, total); - if (!vector_cast_data.all_converted) { - auto &key_validity = FlatVector::ValidityMutable(result_key_child); + if (!vector_cast_data.all_converted || parameters.error_message) { + // maps with a NULL key - from a failed key cast, or keys of type NULL - fail to convert + // the key cast does not necessarily produce a flat vector (e.g. when casting to NULL keys) + auto key_validity = result_key_child.Validity(); for (idx_t row_idx = 0; row_idx < count; row_idx++) { if (!result_mask.RowIsValid(row_idx)) { continue; @@ -332,13 +335,30 @@ bool VectorStringToMap::StringToNestedTypeCastLoop(const string_t *source_data, auto list = list_data[row_idx]; for (idx_t list_idx = 0; list_idx < list.length; list_idx++) { auto idx = list.offset + list_idx; - if (!key_validity.RowIsValid(idx)) { - result_mask.SetInvalid(row_idx); + if (!key_validity.IsValid(idx)) { + HandleVectorCastError::Operation("Map keys can not be NULL.", result_mask, row_idx, + vector_cast_data); + break; } } } } - MapVector::MapConversionVerify(result, count); + if (!parameters.error_message) { + MapVector::MapConversionVerify(result, count); + return vector_cast_data.all_converted; + } + // TRY_CAST: nullify maps with duplicate keys instead of throwing + SelectionVector row_sel(1); + for (idx_t row_idx = 0; row_idx < count; row_idx++) { + if (!result_mask.RowIsValid(row_idx)) { + continue; + } + row_sel.set_index(0, row_idx); + if (MapVector::CheckMapValidity(result, 1, row_sel) != MapInvalidReason::VALID) { + HandleVectorCastError::Operation("Map keys must be unique.", result_mask, row_idx, + vector_cast_data); + } + } return vector_cast_data.all_converted; } diff --git a/src/duckdb/src/function/cast/time_casts.cpp b/src/duckdb/src/function/cast/time_casts.cpp index d8a44ff06..ffbf31cab 100644 --- a/src/duckdb/src/function/cast/time_casts.cpp +++ b/src/duckdb/src/function/cast/time_casts.cpp @@ -153,7 +153,8 @@ BoundCastInfo DefaultCasts::TimestampTzCastSwitch(BindCastInput &input, const Lo // timestamp with time zone to timestamp (us) return ReinterpretCast; case LogicalTypeId::TIMESTAMP_NS: - // timestamptz (us) to timestamp (ns) + case LogicalTypeId::TIMESTAMP_TZ_NS: + // timestamptz (us) to timestamp [with time zone] (ns) return BoundCastInfo( &VectorCastHelpers::TryCastErrorLoop); case LogicalTypeId::TIMESTAMP_MS: @@ -184,6 +185,12 @@ BoundCastInfo DefaultCasts::TimestampTzNsCastSwitch(BindCastInput &input, const case LogicalTypeId::TIMESTAMP_TZ: // timestamp with time zone (ns) to timestamp with time zone (us) return BoundCastInfo(&VectorCastHelpers::TryCastLoop); + case LogicalTypeId::TIMESTAMP_MS: + // timestamp with time zone (ns) to timestamp (ms) + return BoundCastInfo(&VectorCastHelpers::TryCastLoop); + case LogicalTypeId::TIMESTAMP_SEC: + // timestamp with time zone (ns) to timestamp (s) + return BoundCastInfo(&VectorCastHelpers::TryCastLoop); default: return TryVectorNullCast; } diff --git a/src/duckdb/src/function/cast/union/from_struct.cpp b/src/duckdb/src/function/cast/union/from_struct.cpp index ed8fbdf65..fa748e326 100644 --- a/src/duckdb/src/function/cast/union/from_struct.cpp +++ b/src/duckdb/src/function/cast/union/from_struct.cpp @@ -5,6 +5,7 @@ #include "duckdb/common/vector/struct_vector.hpp" #include "duckdb/function/cast/bound_cast_data.hpp" #include "duckdb/common/exception/conversion_exception.hpp" +#include "duckdb/common/operator/cast_operators.hpp" namespace duckdb { @@ -44,6 +45,21 @@ bool StructToUnionCast::AllowImplicitCastFromStruct(const LogicalType &source, c // Physical Cast execution +static string GetUnionInvalidReasonMessage(UnionInvalidReason reason) { + switch (reason) { + case UnionInvalidReason::TAG_OUT_OF_RANGE: + return "One or more of the tags do not point to a valid union member"; + case UnionInvalidReason::VALIDITY_OVERLAP: + return "One or more rows in the produced UNION have validity set for more than 1 member"; + case UnionInvalidReason::TAG_MISMATCH: + return "One or more rows in the produced UNION have tags that don't point to the valid member"; + case UnionInvalidReason::NULL_TAG: + return "One or more rows in the produced UNION have a NULL tag"; + default: + throw InternalException("Struct to union cast failed for unknown reason"); + } +} + bool StructToUnionCast::Cast(Vector &source, Vector &result, idx_t count, CastParameters ¶meters) { auto &cast_data = parameters.cast_data->Cast(); auto &lstate = parameters.local_state->Cast(); @@ -54,15 +70,16 @@ bool StructToUnionCast::Cast(Vector &source, Vector &result, idx_t count, CastPa auto &source_children = StructVector::GetEntries(source); auto &target_children = StructVector::GetEntries(result); + bool all_converted = true; for (idx_t i = 0; i < source_children.size(); i++) { auto &result_child_vector = target_children[i]; auto &source_child_vector = source_children[i]; CastParameters child_parameters(parameters, cast_data.child_cast_info[i].GetCastData(), lstate.local_states[i]); - auto converted = - cast_data.child_cast_info[i].Cast(source_child_vector, result_child_vector, count, child_parameters); - (void)converted; - D_ASSERT(converted); + if (!cast_data.child_cast_info[i].Cast(source_child_vector, result_child_vector, count, child_parameters)) { + // the failing values have been set to NULL by the (TRY_CAST) member cast + all_converted = false; + } // we flatten the child because we use FlatVector::SetNull below and we may get non-flat from source/cast result_child_vector.Flatten(); } @@ -87,26 +104,30 @@ bool StructToUnionCast::Cast(Vector &source, Vector &result, idx_t count, CastPa } } - auto check_tags = UnionVector::CheckUnionValidity(result, count); - switch (check_tags) { - case UnionInvalidReason::TAG_OUT_OF_RANGE: - throw ConversionException("One or more of the tags do not point to a valid union member"); - case UnionInvalidReason::VALIDITY_OVERLAP: - throw ConversionException("One or more rows in the produced UNION have validity set for more than 1 member"); - case UnionInvalidReason::TAG_MISMATCH: - throw ConversionException( - "One or more rows in the produced UNION have tags that don't point to the valid member"); - case UnionInvalidReason::NULL_TAG: - throw ConversionException("One or more rows in the produced UNION have a NULL tag"); - case UnionInvalidReason::VALID: - break; - default: - throw InternalException("Struct to union cast failed for unknown reason"); + const bool is_constant = result.GetVectorType() == VectorType::CONSTANT_VECTOR; + const idx_t row_count = is_constant ? 1 : count; + if (UnionVector::CheckUnionValidity(result, row_count) != UnionInvalidReason::VALID) { + // find the invalid rows - these raise an error, or become NULL for TRY_CAST + SelectionVector row_sel(1); + for (idx_t row = 0; row < row_count; row++) { + row_sel.set_index(0, row); + auto reason = UnionVector::CheckUnionValidity(result, 1, row_sel); + if (reason == UnionInvalidReason::VALID) { + continue; + } + HandleCastError::AssignError(GetUnionInvalidReasonMessage(reason), parameters); + if (is_constant) { + ConstantVector::SetNull(result, true); + } else { + FlatVector::SetNull(result, row, true); + } + all_converted = false; + } } FlatVector::SetSize(result, count_t(count)); result.Verify(); - return true; + return all_converted; } // Bind cast diff --git a/src/duckdb/src/function/cast/union_casts.cpp b/src/duckdb/src/function/cast/union_casts.cpp index f1254ddff..b1ad5230f 100644 --- a/src/duckdb/src/function/cast/union_casts.cpp +++ b/src/duckdb/src/function/cast/union_casts.cpp @@ -210,16 +210,28 @@ static bool ToUnionMemberCast(Vector &source, Vector &result, idx_t count, CastP auto member_cast_info = cast_data.member_casts[0].Copy(); CastParameters child_parameters(parameters, member_cast_info.GetCastData(), parameters.local_state); - if (!member_cast_info.Cast(source, selected_member_vector, count, child_parameters)) { - return false; - } - - // cast succeeded, create union vector + bool all_converted = member_cast_info.Cast(source, selected_member_vector, count, child_parameters); UnionVector::SetToMember(result, cast_data.tag_map[0], selected_member_vector, count, true); + if (!all_converted) { + // rows that failed to cast (under TRY_CAST) are NULL in the member - make the union NULL as well + auto source_validity = source.Validity(); + auto member_validity = selected_member_vector.Validity(); + if (result.GetVectorType() == VectorType::CONSTANT_VECTOR) { + if (source_validity.IsValid(0) && !member_validity.IsValid(0)) { + ConstantVector::SetNull(result, count_t(count)); + } + } else { + for (idx_t row_idx = 0; row_idx < count; row_idx++) { + if (source_validity.IsValid(row_idx) && !member_validity.IsValid(row_idx)) { + FlatVector::SetNull(result, row_idx, true); + } + } + } + } result.Verify(); - return true; + return all_converted; } static bool UnionMemberToMemberCast(Vector &source, Vector &result, idx_t count, CastParameters ¶meters) { @@ -232,6 +244,7 @@ static bool UnionMemberToMemberCast(Vector &source, Vector &result, idx_t count, auto target_member_is_mapped = vector(target_member_count); // Perform the casts from source to target members + bool all_converted = true; for (idx_t member_idx = 0; member_idx < source_member_count; member_idx++) { auto target_member_idx = cast_data.tag_map[member_idx]; @@ -241,14 +254,12 @@ static bool UnionMemberToMemberCast(Vector &source, Vector &result, idx_t count, CastParameters child_parameters(parameters, member_cast.GetCastData(), lstate.local_states[member_idx]); if (!member_cast.Cast(source_member_vector, target_member_vector, count, child_parameters)) { - return false; + all_converted = false; } target_member_is_mapped[target_member_idx] = true; } - // All member casts succeeded! - // Set the unmapped target members to constant NULL. // If we cast UNION(A, B) -> UNION(A, B, C) we need to invalidate C so that // the invariants of the result union hold. (only member columns "selected" @@ -275,6 +286,11 @@ static bool UnionMemberToMemberCast(Vector &source, Vector &result, idx_t count, auto source_tag = ConstantVector::GetData(source_tag_vector)[0]; auto mapped_tag = cast_data.tag_map[source_tag]; ConstantVector::GetData(result_tag_vector)[0] = UnsafeNumericCast(mapped_tag); + // the selected member failed to cast (under TRY_CAST) - the union becomes NULL + if (!all_converted && UnionVector::GetMember(source, source_tag).Validity().IsValid(0) && + !UnionVector::GetMember(result, mapped_tag).Validity().IsValid(0)) { + ConstantVector::SetNull(result, count_t(count)); + } } } else { // Otherwise, use the unified vector format to access the source vector. @@ -288,6 +304,13 @@ static bool UnionMemberToMemberCast(Vector &source, Vector &result, idx_t count, // We assume that a union tag vector validity matches the union vector validity. auto source_tag_entries = source_tag_vector.Values(); + vector> source_member_validity; + if (!all_converted) { + for (idx_t member_idx = 0; member_idx < source_member_count; member_idx++) { + source_member_validity.push_back( + make_uniq(UnionVector::GetMember(source, member_idx))); + } + } for (idx_t row_idx = 0; row_idx < count; row_idx++) { auto entry = source_tag_entries[row_idx]; @@ -296,6 +319,11 @@ static bool UnionMemberToMemberCast(Vector &source, Vector &result, idx_t count, auto target_tag = cast_data.tag_map[entry.GetValue()]; FlatVector::GetDataMutable(result_tag_vector)[row_idx] = UnsafeNumericCast(target_tag); + // the selected member failed to cast (under TRY_CAST) - the union becomes NULL + if (!all_converted && source_member_validity[entry.GetValue()]->IsValid(row_idx) && + !FlatVector::Validity(UnionVector::GetMember(result, target_tag)).RowIsValid(row_idx)) { + FlatVector::SetNull(result, row_idx, true); + } } else { // Issue: The members of the result is not always flatvectors // In the case of TryNullCast, the result member is constant. @@ -307,7 +335,7 @@ static bool UnionMemberToMemberCast(Vector &source, Vector &result, idx_t count, FlatVector::SetSize(result, count_t(count)); result.Verify(); - return true; + return all_converted; } static bool ToUnionCast(Vector &source, Vector &result, idx_t count, CastParameters ¶meters) { @@ -315,11 +343,9 @@ static bool ToUnionCast(Vector &source, Vector &result, idx_t count, CastParamet auto &cast_data = parameters.cast_data->Cast(); if (cast_data.is_member_to_member) { - UnionMemberToMemberCast(source, result, count, parameters); - } else { - ToUnionMemberCast(source, result, count, parameters); + return UnionMemberToMemberCast(source, result, count, parameters); } - return true; + return ToUnionMemberCast(source, result, count, parameters); } BoundCastInfo DefaultCasts::ImplicitToUnionCast(BindCastInput &input, const LogicalType &source, @@ -333,6 +359,11 @@ BoundCastInfo DefaultCasts::ImplicitToUnionCast(BindCastInput &input, const Logi } static bool UnionToVarcharCast(Vector &source, Vector &result, idx_t count, CastParameters ¶meters) { + if (count == 0) { + // the intermediate union vector would not have any storage + FlatVector::SetSize(result, count_t(0)); + return true; + } // first cast all union members to varchar auto &cast_data = parameters.cast_data->Cast(); Vector varchar_union(cast_data.type, count); diff --git a/src/duckdb/src/function/cast/variant/from_variant.cpp b/src/duckdb/src/function/cast/variant/from_variant.cpp index 5358b92ba..331f3dd43 100644 --- a/src/duckdb/src/function/cast/variant/from_variant.cpp +++ b/src/duckdb/src/function/cast/variant/from_variant.cpp @@ -2,6 +2,8 @@ #include "duckdb/common/vector/constant_vector.hpp" #include "duckdb/common/vector/flat_vector.hpp" #include "duckdb/common/vector/list_vector.hpp" +#include "duckdb/common/vector/map_vector.hpp" +#include "duckdb/common/error_data.hpp" #include "duckdb/common/vector/shredded_vector.hpp" #include "duckdb/common/vector/string_vector.hpp" #include "duckdb/common/vector/struct_vector.hpp" @@ -12,6 +14,7 @@ #include "duckdb/common/serializer/varint.hpp" #include "yyjson.hpp" +#include "duckdb/common/algorithm.hpp" #include "duckdb/common/enum_util.hpp" #include "duckdb/common/exception/conversion_exception.hpp" @@ -27,12 +30,11 @@ struct FromVariantConversionData { : variant(variant_format), client(client) { } - optional TryCastAs(const Value &value, const LogicalType &target_type, string *error_message = nullptr, - bool strict = false) { + optional TryCastAs(const Value &value, const LogicalType &target_type, string *error_message = nullptr) { if (client) { - return value.TryCastAs(*client, target_type, error_message, strict); + return value.TryCastAs(*client, target_type, error_message); } - return value.DefaultTryCastAs(target_type, error_message, strict); + return value.DefaultTryCastAs(target_type, error_message); } public: @@ -206,7 +208,7 @@ static bool CastVariantToPrimitive(FromVariantConversionData &conversion_data, V } if (!converted) { auto value = VariantUtils::ConvertVariantToValue(conversion_data.variant, row_index, sel[i]); - auto cast_value = conversion_data.TryCastAs(value, target_type, nullptr, true); + auto cast_value = conversion_data.TryCastAs(value, target_type); if (!cast_value) { conversion_data.error = StringUtil::Format("Can't convert VARIANT(%s) value '%s'", EnumUtil::ToString(type_id), value.ToString()); @@ -434,6 +436,7 @@ static bool ConvertVariantToStruct(FromVariantConversionData &conversion_data, V auto row_index = row.IsValid() ? row.GetIndex() : nested_index.GetIndex(); auto object_keys = VariantUtils::GetObjectKeys(conversion_data.variant, row_index, child_data[nested_index.GetIndex()]); + std::sort(object_keys.begin(), object_keys.end()); conversion_data.error = StringUtil::Format("VARIANT(OBJECT(%s)) is missing key '%s'", StringUtil::Join(object_keys, ","), component.key); return false; @@ -517,9 +520,16 @@ static bool CastVariantToJSON(FromVariantConversionData &conversion_data, Vector ConvertedJSONHolder holder(Allocator::DefaultAllocator()); + auto &variant = conversion_data.variant; auto result_data = FlatVector::Writer(result, count, offset); for (idx_t i = 0; i < count; i++) { const auto row_index = row.IsValid() ? row.GetIndex() : i; + if (!variant.RowIsValid(row_index)) { + // a SQL NULL row stays a SQL NULL - rendering it as the JSON token `null` would make it + // indistinguishable from a JSON null actually stored in the variant + result_data.WriteNull(); + continue; + } const auto json_val = VariantCasts::ConvertVariantToJSON(holder.GetDocument(), conversion_data.variant, row_index, sel[i]); if (!json_val) { @@ -549,6 +559,61 @@ static bool CastVariantToJSON(FromVariantConversionData &conversion_data, Vector //! * @param offset The offset into the result where to write the converted values //! * @param count The amount of values we're converting //! * @param row The row of the Variant to pull data from, if 'IsValid()' is true +//! Maps cannot have NULL or duplicate keys - such rows are set to NULL, reporting the error +static bool VerifyConvertedMaps(FromVariantConversionData &conversion_data, Vector &result, idx_t offset, idx_t count) { + bool all_valid = true; + SelectionVector row_sel(1); + for (idx_t i = 0; i < count; i++) { + if (FlatVector::IsNull(result, offset + i)) { + continue; + } + row_sel.set_index(0, offset + i); + auto reason = MapVector::CheckMapValidity(result, 1, row_sel); + if (reason == MapInvalidReason::VALID) { + continue; + } + if (conversion_data.error.empty()) { + conversion_data.error = + reason == MapInvalidReason::DUPLICATE_KEY ? "Map keys must be unique." : "Map keys can not be NULL."; + } + FlatVector::SetNull(result, offset + i, true); + all_valid = false; + } + return all_valid; +} + +//! A VARIANT holds a MAP as a list of {key, value} structs - cast it to the entries of the target MAP +static optional TryCastVariantValueToMap(FromVariantConversionData &conversion_data, const Value &value, + const LogicalType &target_type) { + auto entry_type = ListType::GetChildType(target_type); + auto entries = conversion_data.TryCastAs(value, LogicalType::LIST(entry_type)); + if (!entries) { + return entries; + } + if (entries->IsNull()) { + return Value(target_type); + } + auto &children = ListValue::GetChildren(*entries); + for (auto &child : children) { + if (child.IsNull()) { + // a NULL entry has no key + if (conversion_data.error.empty()) { + conversion_data.error = "Map keys can not be NULL."; + } + return optional(); + } + } + try { + return Value::MAP(entry_type, children); + } catch (const InvalidInputException &ex) { + // the entries do not form a valid map (e.g. duplicate keys) + if (conversion_data.error.empty()) { + conversion_data.error = ErrorData(ex).RawMessage(); + } + return optional(); + } +} + static bool CastVariant(FromVariantConversionData &conversion_data, Vector &result, const SelectionVector &sel, idx_t offset, idx_t count, optional_idx row) { auto &target_type = result.GetType(); @@ -574,11 +639,15 @@ static bool CastVariant(FromVariantConversionData &conversion_data, Vector &resu } break; case LogicalTypeId::LIST: - case LogicalTypeId::MAP: { if (ConvertVariantToList(conversion_data, result, sel, offset, count, row)) { return true; } break; + case LogicalTypeId::MAP: { + if (ConvertVariantToList(conversion_data, result, sel, offset, count, row)) { + return VerifyConvertedMaps(conversion_data, result, offset, count); + } + break; } case LogicalTypeId::UNION: { error = "Can't convert VARIANT"; @@ -607,7 +676,9 @@ static bool CastVariant(FromVariantConversionData &conversion_data, Vector &resu uint32_t value_index = sel[i]; auto value = VariantUtils::ConvertVariantToValue(conversion_data.variant, row_index, value_index); try { - auto cast_value = conversion_data.TryCastAs(value, target_type, nullptr, true); + auto cast_value = target_type.id() == LogicalTypeId::MAP + ? TryCastVariantValueToMap(conversion_data, value, target_type) + : conversion_data.TryCastAs(value, target_type); if (!cast_value) { cast_value = Value(target_type); all_valid = false; @@ -618,8 +689,18 @@ static bool CastVariant(FromVariantConversionData &conversion_data, Vector &resu // Set the value to NULL and mark as failed FlatVector::SetNull(result, offset + i, true); all_valid = false; + } catch (const ConversionException &ex) { + // strict casts of the converted value (e.g. VARCHAR to an ARRAY of a different size) can throw + if (error.empty()) { + error = ErrorData(ex).RawMessage(); + } + FlatVector::SetNull(result, offset + i, true); + all_valid = false; } } + if (target_type.id() == LogicalTypeId::MAP) { + all_valid = VerifyConvertedMaps(conversion_data, result, offset, count) && all_valid; + } return all_valid; } else { EmptyConversionPayloadFromVariant empty_payload; @@ -784,9 +865,10 @@ static bool TryFromShreddedCast(Vector &variant_vec, Vector &result) { if (ShreddedVector::IsFullyShredded(variant_vec) && shredded_vec.GetType().id() == LogicalTypeId::STRUCT) { // it is! check if the type of the typed_value entry matches auto &shredded_entries = StructVector::GetEntries(shredded_vec); - if (shredded_entries[1].GetType() == result.GetType()) { + auto &typed_value = shredded_entries[VariantStats::TYPED_VALUE_INDEX]; + if (typed_value.GetType() == result.GetType()) { // the typed_value matches - directly reference it - result.Reference(shredded_entries[1]); + result.Reference(typed_value); return true; } } diff --git a/src/duckdb/src/function/cast/variant/to_variant.cpp b/src/duckdb/src/function/cast/variant/to_variant.cpp index 9da98fa7e..d275e2d04 100644 --- a/src/duckdb/src/function/cast/variant/to_variant.cpp +++ b/src/duckdb/src/function/cast/variant/to_variant.cpp @@ -116,10 +116,14 @@ struct VariantCastData : BoundCastData { }; struct VariantLocalData : FunctionLocalState { - explicit VariantLocalData(const LogicalType &shredded_type) { + VariantLocalData(const LogicalType &shredded_type, Allocator &allocator) : allocator(allocator) { Initialize(shredded_type, STANDARD_VECTOR_SIZE); } + //! Allocator for temporary conversion memory (e.g. JSON parsing), backed by the buffer allocator when possible + //! so the memory is tracked + Allocator &allocator; + void Initialize(const LogicalType &shredded_type, idx_t new_capacity) { capacity = new_capacity; shredded_vector = make_uniq(shredded_type, capacity); @@ -147,7 +151,8 @@ struct VariantLocalData : FunctionLocalState { unique_ptr CastToVariantLocalState(CastLocalStateParameters ¶meters) { auto &cast_data = parameters.cast_data->Cast(); - return make_uniq(cast_data.shredded_type); + auto &allocator = parameters.context ? BufferAllocator::Get(*parameters.context) : Allocator::DefaultAllocator(); + return make_uniq(cast_data.shredded_type, allocator); } static bool SupportsShreddedCast(const LogicalType &type) { @@ -214,6 +219,7 @@ static bool CastToVARIANT(Vector &source, Vector &result, idx_t count, CastParam if (TryToShreddedCast(source, result, count, parameters)) { return true; } + auto &local_data = parameters.local_state->Cast(); DataChunk offsets; offsets.Initialize(Allocator::DefaultAllocator(), {LogicalType::UINTEGER, LogicalType::UINTEGER, LogicalType::UINTEGER, LogicalType::UINTEGER}, @@ -229,7 +235,7 @@ static bool CastToVARIANT(Vector &source, Vector &result, idx_t count, CastParam { VariantVectorData variant_data(result); - ToVariantGlobalResultData result_data(variant_data, offsets, dictionary, keys_selvec); + ToVariantGlobalResultData result_data(variant_data, offsets, dictionary, keys_selvec, local_data.allocator); if (!GatherOffsetsAndSizes(source_data, result_data, count)) { return false; } @@ -241,7 +247,7 @@ static bool CastToVARIANT(Vector &source, Vector &result, idx_t count, CastParam { VariantVectorData variant_data(result); - ToVariantGlobalResultData result_data(variant_data, offsets, dictionary, keys_selvec); + ToVariantGlobalResultData result_data(variant_data, offsets, dictionary, keys_selvec, local_data.allocator); if (!WriteVariantResultData(source_data, result_data, count)) { return false; } diff --git a/src/duckdb/src/function/cast_rules.cpp b/src/duckdb/src/function/cast_rules.cpp index 9b93dd2d4..9eaea5de2 100644 --- a/src/duckdb/src/function/cast_rules.cpp +++ b/src/duckdb/src/function/cast_rules.cpp @@ -258,6 +258,8 @@ static int64_t ImplicitCastTimestampSec(const LogicalType &to) { case LogicalTypeId::TIMESTAMP: case LogicalTypeId::TIMESTAMP_MS: case LogicalTypeId::TIMESTAMP_NS: + case LogicalTypeId::TIMESTAMP_TZ: + case LogicalTypeId::TIMESTAMP_TZ_NS: return TargetTypeCost(to); default: return -1; @@ -268,6 +270,8 @@ static int64_t ImplicitCastTimestampMS(const LogicalType &to) { switch (to.id()) { case LogicalTypeId::TIMESTAMP: case LogicalTypeId::TIMESTAMP_NS: + case LogicalTypeId::TIMESTAMP_TZ: + case LogicalTypeId::TIMESTAMP_TZ_NS: return TargetTypeCost(to); default: return -1; @@ -321,7 +325,7 @@ bool LogicalTypeIsValid(const LogicalType &type) { case LogicalTypeId::ARRAY: case LogicalTypeId::DECIMAL: // these types are only valid with auxiliary info - if (!type.AuxInfo()) { + if (!type.HasParameters()) { return false; } break; @@ -364,7 +368,7 @@ bool LogicalTypeIsValid(const LogicalType &type) { int64_t ImplicitCastToUnionMember(const LogicalType &from, const LogicalType &to) { // check that the union type is fully resolved. - if (to.AuxInfo() == nullptr) { + if (!to.HasParameters()) { return -1; } // check if the union contains something castable from the source type @@ -481,7 +485,7 @@ int64_t CastRules::ImplicitCast(const LogicalType &from, const LogicalType &to) } if (from.id() == LogicalTypeId::UNION && to.id() == LogicalTypeId::UNION) { // Check that the target union type is fully resolved. - if (to.AuxInfo() == nullptr) { + if (!to.HasParameters()) { // If not, try anyway and let the actual cast logic handle it. // This is to allow passing unions into functions that take a generic union type (without specifying member // types) as an argument. @@ -517,7 +521,7 @@ int64_t CastRules::ImplicitCast(const LogicalType &from, const LogicalType &to) } } if (StructType::IsStruct(from) && StructType::IsStruct(to)) { - if (to.AuxInfo() == nullptr) { + if (!to.HasParameters()) { // If this struct is not fully resolved, we'll leave it to the actual cast logic to handle it. return 0; } diff --git a/src/duckdb/src/function/copy_blob.cpp b/src/duckdb/src/function/copy_blob.cpp index ba522e16e..0b4360c1c 100644 --- a/src/duckdb/src/function/copy_blob.cpp +++ b/src/duckdb/src/function/copy_blob.cpp @@ -43,6 +43,9 @@ unique_ptr WriteBlobBind(ClientContext &context, CopyFunctionBindI for (auto &[option_name, option_values] : input.info.options) { if (option_name == "compression") { + if (option_values.empty()) { + throw BinderException("COPY (FORMAT BLOB) option %s requires a value", SQLIdentifier(option_name)); + } auto compression_str = ParseStringOption(option_values[0], option_name); result->compression_type = FileCompressionType(compression_str); } else { diff --git a/src/duckdb/src/function/encoding_function.cpp b/src/duckdb/src/function/encoding_function.cpp index b32a612ce..392f86da3 100644 --- a/src/duckdb/src/function/encoding_function.cpp +++ b/src/duckdb/src/function/encoding_function.cpp @@ -21,6 +21,13 @@ void DecodeUTF16ToUTF8(CSVEncoderBuffer &encoded_buffer, char *target_buffer, id // We are done return; } + if (encoded_buffer.cur_pos + 1 >= encoded_buffer.actual_encoded_buffer_size) { + if (encoded_buffer.last_buffer) { + throw InvalidInputException("File is not utf-16 encoded"); + } + // the second byte is passed on to the next buffer + return; + } const uint16_t ch = static_cast(static_cast(encoded_ptr[encoded_buffer.cur_pos]) | (static_cast(encoded_ptr[encoded_buffer.cur_pos + 1]) << 8)); diff --git a/src/duckdb/src/function/function.cpp b/src/duckdb/src/function/function.cpp index 6e84d4228..9d059ea8e 100644 --- a/src/duckdb/src/function/function.cpp +++ b/src/duckdb/src/function/function.cpp @@ -4,11 +4,108 @@ #include "duckdb/common/types/hash.hpp" #include "duckdb/execution/expression_executor.hpp" #include "duckdb/function/built_in_functions.hpp" +#include "duckdb/function/function_binder.hpp" #include "duckdb/function/scalar/string_functions.hpp" #include "duckdb/function/scalar_function.hpp" +#include "duckdb/planner/expression/bound_cast_expression.hpp" namespace duckdb { +TypedKwarg::TypedKwarg(Identifier name_p, LogicalType type_p) : name(std::move(name_p)), type(std::move(type_p)) { +} + +bool TypedKwarg::operator==(const TypedKwarg &other) const { + return name == other.name && aliases == other.aliases && type == other.type; +} + +bool TypedKwarg::operator!=(const TypedKwarg &other) const { + return !(*this == other); +} + +string TypedKwarg::ToString() const { + return StringUtil::Format("%s %s", SQLIdentifier(name), type.ToString()); +} + +TypedKwargs &TypedKwargs::Add(Identifier name, LogicalType type) { + options.emplace_back(std::move(name), std::move(type)); + return *this; +} + +TypedKwargs &TypedKwargs::Alias(Identifier alias) { + if (options.empty()) { + throw InternalException("TypedKwargs::Alias called before any option was added"); + } + options.back().aliases.push_back(std::move(alias)); + return *this; +} + +TypedKwargs TypedKwargs::Merge(const TypedKwargs &other) const { + auto result = *this; + result.options.insert(result.options.end(), other.options.begin(), other.options.end()); + return result; +} + +optional_ptr TypedKwargs::Find(const Identifier &name) const { + // names are matched case-insensitively, like parameter names + for (auto &option : options) { + if (option.name == name) { + return option; + } + for (auto &alias : option.aliases) { + if (alias == name) { + return option; + } + } + } + return nullptr; +} + +const vector &TypedKwargs::GetOptions() const { + return options; +} + +void TypedKwargs::Verify() const { + identifier_set_t names; + for (auto &option : options) { + if (!names.insert(option.name).second) { + throw InvalidInputException("Duplicate option name: %s", option.name); + } + for (auto &alias : option.aliases) { + if (!names.insert(alias).second) { + throw InvalidInputException("Duplicate option name: %s", alias); + } + } + } +} + +vector TypedKwargs::GetNames() const { + vector result; + for (auto &option : options) { + result.push_back(option.name); + for (auto &alias : option.aliases) { + result.push_back(alias); + } + } + return result; +} + +bool TypedKwargs::operator==(const TypedKwargs &other) const { + return options == other.options; +} + +bool TypedKwargs::operator!=(const TypedKwargs &other) const { + return !(*this == other); +} + +hash_t TypedKwargs::Hash() const { + hash_t hash = duckdb::Hash(options.size()); + for (auto &option : options) { + hash = CombineHash(hash, IdentifierHashFunction()(option.name)); + hash = CombineHash(hash, option.type.Hash()); + } + return hash; +} + bool FunctionProperties::operator==(const FunctionProperties &rhs) const { return stability == rhs.stability && null_handling == rhs.null_handling && errors == rhs.errors && collation_handling == rhs.collation_handling && capture_argument_aliases == rhs.capture_argument_aliases && @@ -59,7 +156,10 @@ SimpleFunction::SimpleFunction(Identifier name_p, FunctionSignature signature_p) SimpleFunction::SimpleFunction(Identifier name_p, vector arguments_p, LogicalType return_type, LogicalType varargs_p) - : Function(std::move(name_p)), signature(std::move(arguments_p), std::move(varargs_p), std::move(return_type)) { + : Function(std::move(name_p)), signature(std::move(arguments_p), std::move(return_type)) { + if (varargs_p.id() != LogicalTypeId::INVALID) { + signature.AddArgs("args", varargs_p).AddKwargs("kwargs", std::move(varargs_p)); + } } SimpleFunction::~SimpleFunction() { @@ -71,20 +171,66 @@ static bool RequiresCatalogAndSchemaNamePrefix(const Identifier &catalog_name, c } string FunctionParameter::ToString() const { + if (kind == FunctionParameterKind::VAR_POSITIONAL) { + return StringUtil::Format("*%s %s", SQLIdentifier(name), type.ToString()); + } + if (kind == FunctionParameterKind::VAR_KEYWORD) { + return StringUtil::Format("**%s %s", SQLIdentifier(name), type.ToString()); + } if (default_value) { return StringUtil::Format("%s %s := %s", SQLIdentifier(name), type.ToString(), default_value->ToString()); } return StringUtil::Format("%s %s", SQLIdentifier(name), type.ToString()); } +// signatures are copied and moved freely - keep the move cheap +static_assert(std::is_nothrow_move_constructible::value, "FunctionSignature must stay movable"); +static_assert(std::is_nothrow_move_assignable::value, "FunctionSignature must stay movable"); + +auto FunctionSignature::AddTypedKwargs(Identifier name, TypedKwargs schema) -> FunctionSignature & { + typed_kwargs = make_shared_ptr(std::move(schema)); + return AddKwargs(std::move(name), LogicalType::ANY); +} + +auto FunctionSignature::WithTypedKwargs(Identifier name, const std::function &configure) + -> FunctionSignature & { + TypedKwargs schema; + configure(schema); + return AddTypedKwargs(std::move(name), std::move(schema)); +} + +auto FunctionSignature::ExtendTypedKwargs(const std::function &configure) -> FunctionSignature & { + if (!typed_kwargs) { + throw InternalException("ExtendTypedKwargs called on a signature without typed \"**kwargs\""); + } + if (typed_kwargs.use_count() > 1) { + // other signatures copied from this one share its schema - extend a copy of it + typed_kwargs = make_shared_ptr(*typed_kwargs); + } + configure(*typed_kwargs); + return *this; +} + string FunctionSignature::ToString() const { vector params; params.reserve(parameters.size()); - for (auto ¶m : parameters) { + // A keyword-only parameter that no "*args" precedes closes the positional parameters by itself, which Python + // spells as a bare "*" in that position; a "/" likewise closes the positional-only parameters + const auto positional_only_count = GetPositionalOnlyParameterCount(); + auto needs_separator = !GetArgs(); + for (idx_t i = 0; i < parameters.size(); i++) { + auto ¶m = parameters[i]; + if (i == positional_only_count && positional_only_count > 0) { + params.push_back("/"); + } + if (needs_separator && param.GetKind() == FunctionParameterKind::KEYWORD_ONLY) { + params.push_back("*"); + needs_separator = false; + } params.push_back(param.ToString()); } - if (varargs.IsValid()) { - params.push_back("[" + varargs.ToString() + "...]"); + if (positional_only_count > 0 && positional_only_count == parameters.size()) { + params.push_back("/"); } auto head = StringUtil::Format("(%s)", StringUtil::Join(params, ", ")); if (return_type.IsValid()) { @@ -101,22 +247,6 @@ string SimpleFunction::ToString() const { return SQLIdentifier::ToString(name.GetIdentifierName()) + signature.ToString(); } -SimpleNamedParameterFunction::SimpleNamedParameterFunction(Identifier name_p, vector arguments_p, - LogicalType varargs_p) - : Function(std::move(name_p)), arguments(std::move(arguments_p)), varargs(std::move(varargs_p)) { -} - -SimpleNamedParameterFunction::~SimpleNamedParameterFunction() { -} - -string SimpleNamedParameterFunction::ToString() const { - return Function::CallToString(GetCatalogName(), GetSchemaName(), name, arguments, named_parameters); -} - -bool SimpleNamedParameterFunction::HasNamedParameters() const { - return !named_parameters.empty(); -} - // add your initializer for new functions here void BuiltinFunctions::Initialize() { RegisterTableScanFunctions(); @@ -141,10 +271,128 @@ hash_t FunctionSignature::Hash() const { hash_t hash = return_type.Hash(); for (auto ¶m : parameters) { hash = duckdb::CombineHash(hash, param.GetType().Hash()); + hash = duckdb::CombineHash(hash, duckdb::Hash(static_cast(param.GetKind()))); + } + if (typed_kwargs) { + hash = duckdb::CombineHash(hash, typed_kwargs->Hash()); } return hash; } +void FunctionSignature::FillNamedDefaults(ClientContext &context, named_argument_map_t &named_parameters) const { + // keyword-only parameters are slots, bound in the order they are declared - the arguments "**kwargs" receives + // follow them in the order they were passed + named_argument_map_t result; + for (auto ¶m : parameters) { + if (param.GetKind() != FunctionParameterKind::KEYWORD_ONLY) { + continue; + } + auto entry = named_parameters.find(param.GetName()); + if (entry != named_parameters.end()) { + result.insert(param.GetName(), std::move(entry->second)); + } else if (param.HasDefaultValue()) { + result.insert(param.GetName(), + FunctionBinder::CastToParameterType(context, *param.GetDefaultValue(), param.GetType())); + } + } + if (result.empty()) { + return; + } + for (auto &entry : named_parameters) { + if (!result.contains(entry.first)) { + result.insert(entry.first, std::move(entry.second)); + } + } + named_parameters = std::move(result); +} + +void FunctionSignature::Verify() const { + // Check for duplicate parameter names + identifier_set_t seen_names; + for (const auto ¶m : parameters) { + if (seen_names.find(param.GetName()) != seen_names.end()) { + throw InvalidInputException("Duplicate parameter name: %s", param.GetName()); + } + seen_names.insert(param.GetName()); + } + + // Also check for default values that are not at the end of the positional parameters + bool found_default_value = false; + for (const auto ¶m : parameters) { + if (param.GetKind() != FunctionParameterKind::STANDARD) { + continue; + } + if (param.HasDefaultValue()) { + found_default_value = true; + } else if (found_default_value) { + throw InvalidInputException( + "Parameters with default values must be at the end of the parameter list. Parameter '%s' does not " + "have a default value but follows a parameter with a default value.", + param.GetName()); + } + } + + // And that the parameter kinds are in order: + // positional-only, "/", standard parameters, "*args", keyword-only parameters, "**kwargs" + bool found_args = false; + bool found_kwargs = false; + bool found_keyword_only = false; + bool found_standard = false; + for (const auto ¶m : parameters) { + if (found_kwargs) { + throw InvalidInputException("Parameter '%s' follows '**kwargs', which must be the last parameter", + param.ToString()); + } + if (param.IsVariadic() && param.HasDefaultValue()) { + throw InvalidInputException("Variadic parameter '%s' cannot have a default value", param.ToString()); + } + switch (param.GetKind()) { + case FunctionParameterKind::POSITIONAL_ONLY: + if (found_standard || found_args || found_keyword_only) { + throw InvalidInputException( + "Positional-only parameter '%s' must be declared before every parameter that can be passed by name", + param.ToString()); + } + break; + case FunctionParameterKind::STANDARD: + if (found_args || found_keyword_only) { + throw InvalidInputException("Parameter '%s' follows '*args' and must therefore be keyword-only", + param.ToString()); + } + found_standard = true; + break; + case FunctionParameterKind::VAR_POSITIONAL: + if (found_args) { + throw InvalidInputException("A function signature can only have one '*args' parameter"); + } + if (found_keyword_only) { + throw InvalidInputException("Parameter '%s' cannot follow a keyword-only parameter", param.ToString()); + } + found_args = true; + break; + case FunctionParameterKind::VAR_KEYWORD: + found_kwargs = true; + break; + case FunctionParameterKind::KEYWORD_ONLY: + found_keyword_only = true; + break; + } + } + + if (typed_kwargs) { + if (!found_kwargs) { + throw InvalidInputException("A function signature with options must have a '**kwargs' parameter"); + } + typed_kwargs->Verify(); + // a named argument binds to a parameter of its name first, so an option of the same name is never reached + for (auto ¶m : parameters) { + if (param.AcceptsName() && typed_kwargs->Find(param.GetName())) { + throw InvalidInputException("Option '%s' has the same name as a parameter", param.GetName()); + } + } + } +} + hash_t SimpleFunction::Hash() const { return signature.Hash(); } @@ -183,80 +431,207 @@ string Function::CallToString(const Identifier &catalog_name, const Identifier & return result; } -string Function::CallToString(const Identifier &catalog_name, const Identifier &schema_name, const Identifier &name, - const vector &arguments, - const named_parameter_type_map_t &named_parameters) { - vector input_arguments; - input_arguments.reserve(arguments.size() + named_parameters.size()); +hash_t BoundSimpleFunction::Hash() const { + hash_t hash = return_type.Hash(); for (auto &arg : arguments) { - input_arguments.push_back(arg.ToString()); + hash = duckdb::CombineHash(hash, arg.Hash()); } - for (auto &kv : named_parameters) { - input_arguments.push_back(StringUtil::Format("%s : %s", SQLIdentifier(kv.first), kv.second.ToString())); + return hash; +} + +idx_t BoundSimpleFunction::GetStandardArgumentCount(const FunctionSignature &signature) const { + return positional_arguments - GetVarArgsCount(signature); +} + +idx_t BoundSimpleFunction::GetVarArgsCount(const FunctionSignature &signature) const { + const auto standard_count = signature.GetPositionalParameterCount(); + return positional_arguments > standard_count ? positional_arguments - standard_count : 0; +} + +idx_t BoundSimpleFunction::GetKeywordOnlyArgumentCount(const FunctionSignature &signature) const { + return named_arguments.size() - GetKwargsCount(signature); +} + +idx_t BoundSimpleFunction::GetKwargsCount(const FunctionSignature &signature) const { + idx_t result = 0; + for (auto &name : named_arguments) { + if (!signature.GetParameterIndexByName(name).IsValid()) { + result++; + } } - string prefix = ""; - if (RequiresCatalogAndSchemaNamePrefix(catalog_name, schema_name)) { - prefix = StringUtil::Format("%s.%s.", SQLIdentifier(catalog_name), SQLIdentifier(schema_name)); + return result; +} + +void BoundSimpleFunction::SetCallArguments(const FunctionSignature &signature, const vector ¶meters, + const named_argument_map_t &named_parameters) { + const auto positional_count = signature.GetPositionalParameterCount(); + arguments.clear(); + for (idx_t i = 0; i < positional_count; i++) { + arguments.push_back(signature.GetParameter(i).GetType()); + } + auto args = signature.GetArgs(); + for (idx_t i = positional_count; i < parameters.size(); i++) { + auto is_typed = args && args->GetType().id() != LogicalTypeId::ANY; + arguments.push_back(is_typed ? args->GetType() : parameters[i].type()); + } + const auto positional_argument_count = arguments.size(); + + // only the options the signature receives by name, in the order they were passed + vector names; + for (auto &entry : named_parameters) { + auto param_idx = signature.GetParameterIndexByName(entry.first); + if (!param_idx.IsValid()) { + if (signature.GetKwargs()) { + names.push_back(entry.first); + arguments.push_back(entry.second.type()); + } + continue; + } + auto ¶m = signature.GetParameter(param_idx.GetIndex()); + if (!param.AcceptsPosition()) { + names.push_back(entry.first); + arguments.push_back(param.GetType()); + } } - return StringUtil::Format("%s%s(%s)", prefix, name, StringUtil::Join(input_arguments, ", ")); + SetNamedArguments(positional_argument_count, std::move(names)); } -hash_t BoundSimpleFunction::Hash() const { - hash_t hash = return_type.Hash(); - for (auto &arg : arguments) { - hash = duckdb::CombineHash(hash, arg.Hash()); +FunctionParameterKind BoundSimpleFunction::GetArgumentParameterKind(const FunctionSignature &signature, + idx_t argument_index) const { + if (argument_index >= positional_arguments + named_arguments.size()) { + throw InternalException("%s: Argument index %llu is out of range", GetName(), argument_index); } - return hash; + if (argument_index < signature.GetPositionalParameterCount()) { + return signature.GetParameter(argument_index).GetKind(); + } + if (argument_index < positional_arguments) { + return FunctionParameterKind::VAR_POSITIONAL; + } + auto &name = named_arguments[argument_index - positional_arguments]; + return signature.GetParameterIndexByName(name).IsValid() ? FunctionParameterKind::KEYWORD_ONLY + : FunctionParameterKind::VAR_KEYWORD; } string BoundSimpleFunction::ToString() const { - return Function::CallToString(catalog_name, schema_name, name, arguments, LogicalTypeId::INVALID, return_type); + return Function::CallToString(GetCatalogName(), GetSchemaName(), GetName(), arguments, LogicalTypeId::INVALID, + return_type); } bool FunctionParameter::operator==(const FunctionParameter &other) const { - return type == other.type && name == other.name; + return type == other.type && name == other.name && kind == other.kind; } bool FunctionParameter::operator!=(const FunctionParameter &other) const { return !(*this == other); } +//! Whether two signatures declare the same options - compared by content, as each registration builds its own +static bool OptionSchemasEqual(optional_ptr lhs, optional_ptr rhs) { + if (!lhs || !rhs) { + return !lhs && !rhs; + } + return *lhs == *rhs; +} + bool FunctionSignature::operator==(const FunctionSignature &other) const { - return parameters == other.parameters && varargs == other.varargs && return_type == other.return_type; + return parameters == other.parameters && return_type == other.return_type && + OptionSchemasEqual(GetTypedKwargs(), other.GetTypedKwargs()); } bool FunctionSignature::operator!=(const FunctionSignature &other) const { return !(*this == other); } +bool FunctionSignature::IsSameOverload(const FunctionSignature &other) const { + // the parameters a caller can pass by position, "*args" and "**kwargs", in declaration order + auto ordered = [](const FunctionSignature &signature) { + vector> result; + for (auto ¶m : signature.GetParameters()) { + if (param.GetKind() != FunctionParameterKind::KEYWORD_ONLY) { + result.push_back(param); + } + } + return result; + }; + auto lhs_ordered = ordered(*this); + auto rhs_ordered = ordered(other); + if (lhs_ordered.size() != rhs_ordered.size()) { + return false; + } + for (idx_t i = 0; i < lhs_ordered.size(); i++) { + auto &lhs = lhs_ordered[i].get(); + auto &rhs = rhs_ordered[i].get(); + // a positional-only and a standard parameter both take a position, which is all a call can tell apart + auto takes_position = lhs.AcceptsPosition(); + if (takes_position != rhs.AcceptsPosition() || (!takes_position && lhs.GetKind() != rhs.GetKind())) { + return false; + } + if (lhs.GetType() != rhs.GetType()) { + return false; + } + } + // the keyword-only parameters, by name + auto keywords = [](const FunctionSignature &signature) { + identifier_map_t result; + for (auto ¶m : signature.GetParameters()) { + if (param.GetKind() == FunctionParameterKind::KEYWORD_ONLY) { + result.emplace(param.GetName(), param.GetType()); + } + } + return result; + }; + auto lhs_keywords = keywords(*this); + auto rhs_keywords = keywords(other); + if (lhs_keywords.size() != rhs_keywords.size()) { + return false; + } + for (auto &entry : lhs_keywords) { + auto match = rhs_keywords.find(entry.first); + if (match == rhs_keywords.end() || match->second != entry.second) { + return false; + } + } + return true; +} + bool FunctionSignature::Equal(const FunctionSignature &other) const { if (parameters.size() != other.parameters.size()) { return false; } for (idx_t i = 0; i < parameters.size(); i++) { - if (parameters[i].GetType() != other.parameters[i].GetType()) { + if (parameters[i].GetType() != other.parameters[i].GetType() || + parameters[i].GetKind() != other.parameters[i].GetKind()) { return false; } } - if (varargs != other.varargs) { - return false; - } if (return_type != other.return_type) { return false; } - return true; + return OptionSchemasEqual(GetTypedKwargs(), other.GetTypedKwargs()); } //---------------------------------------------------------------------------------------------------------------------- // Bind Function Input //---------------------------------------------------------------------------------------------------------------------- +//! An unresolved parameter or an as-yet-unknown type (e.g. a macro/prepared argument), possibly cast to the argument +//! type of the function +static bool IsUnresolvedArgument(const Expression &expr) { + if (expr.HasParameter() || expr.GetReturnType().id() == LogicalTypeId::UNKNOWN) { + return true; + } + if (BoundCastExpression::IsCast(expr)) { + return IsUnresolvedArgument(BoundCastExpression::Child(expr.Cast())); + } + return false; +} + Value BindFunctionInput::GetConstant(idx_t arg_idx, bool accept_null) const { if (arg_idx >= arguments.size()) { throw InternalException("%s: Argument index %llu is out of range", function.GetName(), arg_idx); } const auto &expr = *arguments[arg_idx]; - // an unresolved parameter or an as-yet-unknown type (e.g. a macro/prepared argument) - defer binding - if (expr.HasParameter() || expr.GetReturnType().id() == LogicalTypeId::UNKNOWN) { + // defer binding until the argument is resolved + if (IsUnresolvedArgument(expr)) { throw ParameterNotResolvedException(); } // Use the argument name if available, otherwise use the argument index @@ -290,13 +665,17 @@ optional BindFunctionInput::TryGetConstant(idx_t arg_idx) const { return {}; } const auto &expr = *arguments[arg_idx]; - if (expr.HasParameter() || expr.GetReturnType().id() == LogicalTypeId::UNKNOWN) { + if (IsUnresolvedArgument(expr)) { return {}; } if (!expr.IsFoldable()) { return {}; } - return ExpressionExecutor::EvaluateScalar(context, expr); + Value result; + if (!ExpressionExecutor::TryEvaluateScalar(context, expr, result)) { + return {}; + } + return result; } optional BindFunctionInput::TryGetConstant(const Identifier &name) const { @@ -312,8 +691,8 @@ optional_idx BindFunctionInput::GetArgumentIndex(const Identifier &name) const { throw InternalException("Function '%s' was bound without argument names, cannot look up argument '%s' by name", function.GetName(), name); } - // The binder resolves every argument to a slot and reports its name: the signature parameter name for positional - // slots, or the name the caller used for named varargs. + // The binder resolves every argument to a slot and reports its name: the signature parameter name for standard + // and keyword-only parameters, or the name the caller used for "**kwargs". for (idx_t arg_idx = 0; arg_idx < argument_names->size(); arg_idx++) { if ((*argument_names)[arg_idx] == name) { return optional_idx(arg_idx); diff --git a/src/duckdb/src/function/function_binder.cpp b/src/duckdb/src/function/function_binder.cpp index aa18d03d2..2d10779e9 100644 --- a/src/duckdb/src/function/function_binder.cpp +++ b/src/duckdb/src/function/function_binder.cpp @@ -88,14 +88,40 @@ optional_idx FunctionOverloads::Cost(optional_ptr context, const // Compute total number of arguments passed const auto received_arg_count = static_cast(arguments.size() + named_arguments.size()); - // And the minimum and maximum number of arguments the function can accept - const auto minimum_arg_count = sig.GetRequiredParameterCount(); + // And the maximum number of arguments the function can accept + const auto positional_count = sig.GetPositionalParameterCount(); + const auto args_param = sig.GetArgs(); + const auto kwargs_param = sig.GetKwargs(); - const auto maximum_arg_count = sig.HasVarArgs() ? NumericLimits::Maximum() : sig.GetParameterCount(); + idx_t maximum_arg_count = NumericLimits::Maximum(); + if (!args_param && !kwargs_param) { + maximum_arg_count = sig.GetParameterCount(); + } - if (received_arg_count < minimum_arg_count) { - // We have fewer arguments than the function requires, so this function cannot be a match. - return optional_idx(); + // Every parameter without a default has to be filled by the call, either from its position or by its name. + // Counting the arguments instead would let a named argument stand in for a required positional parameter, so + // that f(a, *, opt := NULL) matched a call f(opt := 1) that cannot fill "a" just as well as f(*, opt := NULL). + for (idx_t i = 0; i < sig.GetParameterCount(); i++) { + auto ¶m = sig.GetParameter(i); + if (param.IsVariadic() || param.HasDefaultValue()) { + continue; + } + if (param.AcceptsPosition() && i < arguments.size()) { + continue; + } + bool filled = false; + if (param.AcceptsName()) { + for (auto &named_arg : named_arguments) { + if (named_arg.first == param.GetName()) { + filled = true; + break; + } + } + } + if (!filled) { + // This parameter cannot be filled by the call, so this function cannot be a match. + return optional_idx(); + } } if (received_arg_count > maximum_arg_count) { @@ -103,6 +129,11 @@ optional_idx FunctionOverloads::Cost(optional_ptr context, const return optional_idx(); } + if (arguments.size() > positional_count && !args_param) { + // There is no "*args" to receive the extra positional arguments + return optional_idx(); + } + idx_t cost = 0; bool has_parameter = false; @@ -112,7 +143,7 @@ optional_idx FunctionOverloads::Cost(optional_ptr context, const continue; } - auto arg_type = i < sig.GetParameterCount() ? sig.GetParameter(i).GetType() : sig.GetVarArgs(); + auto &arg_type = i < positional_count ? sig.GetParameter(i).GetType() : args_param->GetType(); int64_t cast_cost = ImplicitCastCost(context, arguments[i], arg_type); if (cast_cost >= 0) { @@ -130,15 +161,12 @@ optional_idx FunctionOverloads::Cost(optional_ptr context, const auto opt_param_idx = sig.GetParameterIndexByName(named_arg.first); if (!opt_param_idx.IsValid()) { - if (!sig.HasVarArgs()) { - // no parameter with this name: continue + if (!kwargs_param) { + // no parameter with this name, and no "**kwargs" to receive it return optional_idx(); } - - // This is a named vararg argument, we can skip the parameter index check as varargs are always at the end - // of the argument list - auto &vararg_type = sig.GetVarArgs(); - int64_t cast_cost = ImplicitCastCost(context, named_arg.second, vararg_type); + // the options of "**kwargs" do not select the overload - they are checked once it is chosen + int64_t cast_cost = ImplicitCastCost(context, named_arg.second, kwargs_param->GetType()); if (cast_cost >= 0) { // we can implicitly cast, add the cost to the total cost cost += static_cast(cast_cost); @@ -147,22 +175,23 @@ optional_idx FunctionOverloads::Cost(optional_ptr context, const return optional_idx(); } } else { - // This parameter index might technically be invalid, in that it may point to a parameter that has already + // This parameter might technically be invalid, in that it may point to a parameter that has already // been filled by a positional argument. However, we will catch that later when we actually bind the // argument expressions to construct the bound function expression. At that point we will also have more // context so we can give a better error message. const auto param_idx = opt_param_idx.GetIndex(); + auto ¶m = sig.GetParameter(param_idx); - if (param_idx < arguments.size()) { + if (param.GetKind() == FunctionParameterKind::STANDARD && param_idx < arguments.size()) { // If already covered by a positional argument, skip the cost check here. continue; } - auto ¶m = sig.GetParameter(param_idx); int64_t cast_cost = ImplicitCastCost(context, named_arg.second, param.GetType()); if (cast_cost >= 0) { cost += idx_t(cast_cost); } else { + // A named argument reaches its parameter only by an implicit cast, exactly like a positional one return optional_idx(); } } @@ -175,66 +204,6 @@ optional_idx FunctionOverloads::Cost(optional_ptr context, const return cost; } -static optional_idx BindVarArgsFunctionCost(optional_ptr context, - const SimpleNamedParameterFunction &func, - const vector &arguments) { - if (arguments.size() < func.GetArguments().size()) { - // not enough arguments to fulfill the non-vararg part of the function - return optional_idx(); - } - idx_t cost = 0; - for (idx_t i = 0; i < arguments.size(); i++) { - LogicalType arg_type = i < func.GetArguments().size() ? func.GetArguments()[i] : func.GetVarArgs(); - if (arguments[i] == arg_type) { - // arguments match: do nothing - continue; - } - int64_t cast_cost = ImplicitCastCost(context, arguments[i], arg_type); - if (cast_cost >= 0) { - // we can implicitly cast, add the cost to the total cost - cost += idx_t(cast_cost); - } else { - // we can't implicitly cast: throw an error - return optional_idx(); - } - } - return cost; -} - -optional_idx FunctionOverloads::Cost(optional_ptr context, const SimpleNamedParameterFunction &func, - const vector &arguments, - const vector> &) { - if (func.HasVarArgs()) { - // special case varargs function - return BindVarArgsFunctionCost(context, func, arguments); - } - if (func.GetArguments().size() != arguments.size()) { - // invalid argument count: check the next function - return optional_idx(); - } - idx_t cost = 0; - bool has_parameter = false; - for (idx_t i = 0; i < arguments.size(); i++) { - if (arguments[i].id() == LogicalTypeId::UNKNOWN) { - has_parameter = true; - continue; - } - int64_t cast_cost = ImplicitCastCost(context, arguments[i], func.GetArguments()[i]); - if (cast_cost >= 0) { - // we can implicitly cast, add the cost to the total cost - cost += idx_t(cast_cost); - } else { - // we can't implicitly cast: throw an error - return optional_idx(); - } - } - if (has_parameter) { - // all arguments are implicitly castable and there is a parameter - return 0 as cost - return 0; - } - return cost; -} - template vector FunctionOverloads::Candidates(optional_ptr context, const Identifier &name, const FunctionSet &functions, const vector &arguments, @@ -384,11 +353,20 @@ optional_idx FunctionBinder::BindFunctionFromArguments(const Identifier &name, c vector positional; vector> named; - if (TrySplitArgumentTypes(arguments, positional, named)) { - return BindFunctionFromArguments(name, functions, positional, named, error); + // Does this call have a valid positional -> named argument shape? + const auto is_valid_call = TrySplitArgumentTypes(arguments, positional, named); + if (is_valid_call) { + // Yes. Let's try to bind + const auto result = BindFunctionFromArguments(name, functions, positional, named, error); + + // We failed binding - does any other overload support implicit argument names? + // If not, this is a hard failure. + if (result.IsValid() || !AnyOverloadSupportsImplicitArgumentNames(functions)) { + return result; + } } - // The split failed because a positional argument follows a named one. + // Either a positional argument follows a named one, or no overload takes the positional arguments. // Check if there is any overload that supports implicit argument names. if (AnyOverloadSupportsImplicitArgumentNames(functions)) { // If so, we can attempt to salvage the call by implicitly naming the positional arguments and retrying again @@ -402,9 +380,18 @@ optional_idx FunctionBinder::BindFunctionFromArguments(const Identifier &name, c named.clear(); if (TrySplitArgumentTypes(arguments, positional, named)) { - return BindFunctionFromArguments(name, functions, positional, named, error); + ErrorData retry_error; + auto result = BindFunctionFromArguments(name, functions, positional, named, retry_error); + if (result.IsValid() || !is_valid_call) { + error = std::move(retry_error); + return result; + } } } + if (is_valid_call) { + // the call only failed to match an overload, which is reported in the error + return optional_idx(); + } // No overload could rescue the positional-after-named call, give a clear error. return PositionalAfterNamedArgumentError(arguments, error); @@ -434,23 +421,256 @@ optional_idx FunctionBinder::BindFunction(const Identifier &name, const TableFun return BindFunctionFromArguments(name, functions, regular_args, keyword_args, error); } -optional_idx FunctionBinder::BindFunction(const Identifier &name, const PragmaFunctionSet &functions, - vector ¶meters, ErrorData &error) { - vector types; - for (auto &value : parameters) { - types.push_back(value.type()); +//! Reject a named argument that no overload of the set accepts, naming the options that were available. Overload +//! selection would otherwise report only that nothing matched the call, which hides which name was wrong +template +static void VerifyNamedArgumentsAccepted(const Identifier &name, const FunctionSet &functions, + const vector>> &named_arguments) { + for (auto &named_argument : named_arguments) { + bool accepted = false; + for (idx_t i = 0; i < functions.functions.size() && !accepted; i++) { + auto &signature = functions.functions[i]->GetSignature(); + // the options of a "**kwargs" are checked once the overload is chosen + accepted = + signature.GetParameterIndexByName(named_argument.first).IsValid() || signature.GetKwargs() != nullptr; + } + if (accepted) { + continue; + } + // list every option any overload accepts, in name order + map candidates; + for (idx_t i = 0; i < functions.functions.size(); i++) { + for (auto ¶m : functions.functions[i]->GetSignature().GetParameters()) { + if (!param.IsVariadic() && param.HasDefaultValue()) { + candidates[param.GetName().GetIdentifierName()] = param.GetType().ToString(); + } + } + } + string candidate_list; + for (auto &candidate : candidates) { + candidate_list += " " + candidate.first + " " + candidate.second + "\n"; + } + throw BinderException("Invalid named parameter %s for function %s\n%s", named_argument.first, + name.GetIdentifierName(), + candidate_list.empty() ? "Function does not accept any named parameters." + : "Candidates:\n" + candidate_list); + } +} + +enum class LogicalTypeComparisonResult : uint8_t { IDENTICAL_TYPE, TARGET_IS_ANY, DIFFERENT_TYPES }; + +static LogicalTypeComparisonResult RequiresCast(const LogicalType &source_type, const LogicalType &target_type) { + if (target_type.id() == LogicalTypeId::ANY) { + return LogicalTypeComparisonResult::TARGET_IS_ANY; + } + if (source_type == target_type) { + return LogicalTypeComparisonResult::IDENTICAL_TYPE; + } + if (source_type.id() == LogicalTypeId::LIST && target_type.id() == LogicalTypeId::LIST) { + return RequiresCast(ListType::GetChildType(source_type), ListType::GetChildType(target_type)); + } + if (source_type.id() == LogicalTypeId::ARRAY && target_type.id() == LogicalTypeId::ARRAY) { + return RequiresCast(ArrayType::GetChildType(source_type), ArrayType::GetChildType(target_type)); + } + return LogicalTypeComparisonResult::DIFFERENT_TYPES; +} + +//! Resolve any ANY in the target type by substituting the matching part of the source, so we never cast a value to a +//! type that still contains ANY - ANY has no physical representation, so building such a vector throws. This happens +//! e.g. when an ARRAY argument fills a LIST(ANY) parameter (the file list of the multi-file readers). +static LogicalType ResolveAnyInParameterType(const LogicalType &target, const LogicalType &source) { + if (target.id() == LogicalTypeId::ANY) { + return source; + } + if (target.id() == LogicalTypeId::LIST && + (source.id() == LogicalTypeId::LIST || source.id() == LogicalTypeId::ARRAY)) { + auto &source_child = + source.id() == LogicalTypeId::LIST ? ListType::GetChildType(source) : ArrayType::GetChildType(source); + return LogicalType::LIST(ResolveAnyInParameterType(ListType::GetChildType(target), source_child)); + } + return target; +} + +Value FunctionBinder::CastToParameterType(ClientContext &context, Value value, const LogicalType ¶meter_type) { + if (RequiresCast(value.type(), parameter_type) == LogicalTypeComparisonResult::DIFFERENT_TYPES) { + return value.CastAs(context, ResolveAnyInParameterType(parameter_type, value.type())); + } + return value; +} + +//! Fold an argument to the constant it was bound to. A constant expression carries its value directly - evaluating +//! one is both wasteful and impossible for the types that have no vector representation, such as TABLE +static Value FoldArgument(ClientContext &context, Expression &expr) { + if (expr.GetExpressionClass() == ExpressionClass::BOUND_CONSTANT) { + return expr.Cast().GetValue(); + } + return ExpressionExecutor::EvaluateScalar(context, expr, true); +} + +//! Fold an argument to a constant and cast it to the type of the parameter it fills +static Value PlaceArgument(ClientContext &context, Expression &expr, const LogicalType &target_type) { + if (target_type.id() == LogicalTypeId::TABLE) { + // A TABLE parameter is filled by the subquery itself rather than by a value, so its slot holds a plain + // NULL. A NULL of type TABLE would not do: TABLE has no physical type, so such a value cannot be read + // back when the plan is deserialized. + return Value(); + } + return FunctionBinder::CastToParameterType(context, FoldArgument(context, expr), target_type); +} + +//! Fold an option to a constant and cast it to its type like an explicit cast - overload selection never checked it. +//! A failed cast names the option, as the cast alone cannot tell which one it was +static Value PlaceOption(ClientContext &context, Expression &expr, const TypedKwarg &option) { + auto value = FoldArgument(context, expr); + if (RequiresCast(value.type(), option.type) != LogicalTypeComparisonResult::DIFFERENT_TYPES) { + return value; + } + string error_message; + auto result = value.TryCastAs(context, option.type, &error_message); + if (!result) { + throw InvalidInputException(expr, "Could not cast value %s to named parameter %s of type %s: %s", + value.ToSQLString(), option.name, option.type.ToString(), error_message); } - auto entry = BindFunctionFromArguments(name, functions, types, {}, error); + return std::move(*result); +} + +//! Place the arguments of a call onto the parameters of the chosen overload: fold each to a constant and cast it to +//! the type that parameter declares. Every parameter that accepts a position gets its slot in "parameters", whether +//! the caller passed it by position, by name or not at all, so "range(1, col1 := 5)" reaches the callback exactly as +//! "range(1, 5)" does. Only keyword-only and "**kwargs" arguments are handed back in "named_parameters", keyed by the +//! name the caller wrote - or, for an option "**kwargs" declares, by the name of the option, cast to its type. A +//! parameter the call leaves out receives its default; an option the call leaves out is not passed. Overload selection +//! has already established that every parameter's cast is one the implicit rules allow. An option takes no part in +//! selection, so it is cast to its type like an explicit cast. +template +static void PlaceArguments(ClientContext &context, const T &function, + vector> &positional_arguments, + vector>> &named_arguments, vector ¶meters, + named_argument_map_t &named_parameters) { + auto &signature = function.GetSignature(); + const auto positional_count = signature.GetPositionalParameterCount(); + const auto passed_count = positional_arguments.size(); + + // the positional parameters the caller filled by name, by parameter index + vector> named_slots(positional_count); + identifier_set_t seen_names; + for (auto &named_argument : named_arguments) { + auto &argument_name = named_argument.first; + if (!seen_names.insert(argument_name).second) { + throw BinderException("Duplicate named argument %s for function %s", argument_name, + function.GetName().GetIdentifierName()); + } + auto param_idx = signature.GetParameterIndexByName(argument_name); + if (!param_idx.IsValid()) { + auto option_schema = signature.GetTypedKwargs(); + if (!option_schema) { + // received by "**kwargs" - cast to its type, as overload selection checked, unless that is ANY + auto &kwargs_type = signature.GetKwargs()->GetType(); + named_parameters.insert( + make_pair(argument_name, PlaceArgument(context, *named_argument.second, kwargs_type))); + continue; + } + auto &expr = *named_argument.second; + auto option_ptr = option_schema->Find(argument_name); + if (!option_ptr) { + // only the options that resemble the name - listing every option buries the one that was meant + const double similarity_threshold = 0.8; + auto lower_name = StringUtil::Lower(argument_name.GetIdentifierName()); + vector> scores; + for (auto &option_name : option_schema->GetNames()) { + auto score = StringUtil::SimilarityRating(option_name.GetIdentifierName(), lower_name); + if (score >= similarity_threshold) { + scores.emplace_back(option_name.GetIdentifierName(), score); + } + } + auto similar = StringUtil::TopNStrings(scores, 3, similarity_threshold); + throw BinderException(expr.GetQueryLocation(), "Invalid named parameter %s for function %s%s", + argument_name, function.GetName().GetIdentifierName(), + StringUtil::CandidatesMessage(similar, "Did you mean")); + } + auto &option = *option_ptr; + if (named_parameters.find(option.name) != named_parameters.end()) { + throw BinderException(named_argument.second->GetQueryLocation(), + "Named parameter %s was passed more than once in function call to %s", + option.name, function.GetName().GetIdentifierName()); + } + named_parameters.insert(make_pair(option.name, PlaceOption(context, *named_argument.second, option))); + continue; + } + auto ¶m = signature.GetParameter(param_idx.GetIndex()); + if (!param.AcceptsPosition()) { + named_parameters.insert( + make_pair(argument_name, PlaceArgument(context, *named_argument.second, param.GetType()))); + continue; + } + if (param_idx.GetIndex() < passed_count) { + throw BinderException(named_argument.second->GetQueryLocation(), + "Named argument '%s' cannot be used for parameter '%s' because it has already " + "been provided as a positional argument in function call to '%s'", + named_argument.second->ToString(), argument_name, + function.GetName().GetIdentifierName()); + } + named_slots[param_idx.GetIndex()] = *named_argument.second; + } + + for (idx_t i = 0; i < positional_count; i++) { + auto ¶m = signature.GetParameter(i); + if (i < passed_count) { + parameters.push_back(PlaceArgument(context, *positional_arguments[i], param.GetType())); + } else if (named_slots[i]) { + parameters.push_back(PlaceArgument(context, *named_slots[i], param.GetType())); + } else if (param.HasDefaultValue()) { + parameters.push_back( + FunctionBinder::CastToParameterType(context, *param.GetDefaultValue(), param.GetType())); + } else { + // overload selection only picks an overload whose required parameters the call fills + throw InternalException("Missing value for parameter %s in function call to %s", param.GetName(), + function.GetName().GetIdentifierName()); + } + } + if (passed_count > positional_count) { + // overload selection only picks an overload with "*args" for surplus positional arguments + auto &args_type = signature.GetArgs()->GetType(); + for (idx_t i = positional_count; i < passed_count; i++) { + parameters.push_back(PlaceArgument(context, *positional_arguments[i], args_type)); + } + } + signature.FillNamedDefaults(context, named_parameters); +} + +optional_idx FunctionBinder::BindFunction(const Identifier &name, const TableFunctionSet &functions, + vector> &positional_arguments, + vector>> &named_arguments, + vector ¶meters, named_argument_map_t &named_parameters, + ErrorData &error) { + VerifyNamedArgumentsAccepted(name, functions, named_arguments); + auto entry = BindFunction(name, functions, positional_arguments, named_arguments, error); if (!entry.IsValid()) { - error.Throw(); + return entry; } - const auto &candidate_function = *functions.GetFunctionByOffset(entry.GetIndex()); - // cast the input parameters - for (idx_t i = 0; i < parameters.size(); i++) { - auto target_type = i < candidate_function.GetArguments().size() ? candidate_function.GetArguments()[i] - : candidate_function.GetVarArgs(); - parameters[i] = parameters[i].CastAs(context, target_type); + PlaceArguments(context, *functions.GetFunctionByOffset(entry.GetIndex()), positional_arguments, named_arguments, + parameters, named_parameters); + return entry; +} + +optional_idx FunctionBinder::BindTableInOutFunction(const Identifier &name, const TableFunctionSet &functions, + const vector &input_types, ErrorData &error) { + return BindFunctionFromArguments(name, functions, input_types, {}, error); +} + +optional_idx FunctionBinder::BindFunction(const Identifier &name, const PragmaFunctionSet &functions, + vector> &positional_arguments, + vector>> &named_arguments, + vector ¶meters, named_argument_map_t &named_parameters, + ErrorData &error) { + VerifyNamedArgumentsAccepted(name, functions, named_arguments); + auto [args, kwargs] = GetArgumentsFromExpressions(positional_arguments, named_arguments); + auto entry = BindFunctionFromArguments(name, functions, args, kwargs, error); + if (!entry.IsValid()) { + error.Throw(); } + PlaceArguments(context, *functions.GetFunctionByOffset(entry.GetIndex()), positional_arguments, named_arguments, + parameters, named_parameters); return entry; } @@ -493,24 +713,6 @@ optional_idx FunctionBinder::BindFunction(const Identifier &name, const TableFun return BindFunctionFromArguments(name, functions, args, kwargs, error); } -enum class LogicalTypeComparisonResult : uint8_t { IDENTICAL_TYPE, TARGET_IS_ANY, DIFFERENT_TYPES }; - -static LogicalTypeComparisonResult RequiresCast(const LogicalType &source_type, const LogicalType &target_type) { - if (target_type.id() == LogicalTypeId::ANY) { - return LogicalTypeComparisonResult::TARGET_IS_ANY; - } - if (source_type == target_type) { - return LogicalTypeComparisonResult::IDENTICAL_TYPE; - } - if (source_type.id() == LogicalTypeId::LIST && target_type.id() == LogicalTypeId::LIST) { - return RequiresCast(ListType::GetChildType(source_type), ListType::GetChildType(target_type)); - } - if (source_type.id() == LogicalTypeId::ARRAY && target_type.id() == LogicalTypeId::ARRAY) { - return RequiresCast(ArrayType::GetChildType(source_type), ArrayType::GetChildType(target_type)); - } - return LogicalTypeComparisonResult::DIFFERENT_TYPES; -} - static bool TypeRequiresPrepare(const LogicalType &type) { if (type.id() == LogicalTypeId::ANY) { return true; @@ -538,6 +740,18 @@ static void PrepareTypeForCast(LogicalType &type) { type = PrepareTypeForCastRecursive(type); } +//! Whether the type has no concrete type information (e.g. DECIMAL without width / scale) - ANY, UNKNOWN and INVALID +//! are excluded, as casts to these have special meaning (e.g. for prepared statement parameters) +static bool IsIncompleteCastTarget(const LogicalType &type) { + if (type.IsComplete()) { + return false; + } + return !TypeVisitor::Contains(type, [](const LogicalType &child) { + return child.id() == LogicalTypeId::ANY || child.id() == LogicalTypeId::UNKNOWN || + child.id() == LogicalTypeId::INVALID; + }); +} + void FunctionBinder::CastToFunctionArguments(BoundSimpleFunction &function, vector> &children) { for (auto &arg : function.GetArguments()) { PrepareTypeForCast(arg); @@ -561,12 +775,28 @@ void FunctionBinder::CastToFunctionArguments(BoundSimpleFunction &function, vect if (children[i]->GetReturnType().id() == LogicalTypeId::LAMBDA) { continue; } + const bool target_has_any = TypeVisitor::Contains(target_type, LogicalTypeId::ANY); + if (target_has_any && children[i]->GetReturnType().id() == LogicalTypeId::ARRAY && + target_type.id() == LogicalTypeId::LIST) { + // an array passed to a LIST(ANY) parameter is cast to a list of its child type + children[i] = BoundCastExpression::AddArrayCastToList(context, std::move(children[i])); + } // check if the type of child matches the type of function argument // if not we need to add a cast auto cast_result = RequiresCast(children[i]->GetReturnType(), target_type); // except for one special case: if the function accepts ANY argument // in that case we don't add a cast if (cast_result == LogicalTypeComparisonResult::DIFFERENT_TYPES) { + if (target_has_any && children[i]->GetReturnType().id() != LogicalTypeId::UNKNOWN) { + // a type that is not concrete cannot be cast to (e.g. a NULL or a VARIANT passed to LIST(ANY)) - the + // function has to handle the argument as-is. Unresolved parameters are still assigned the type. + continue; + } + if (IsIncompleteCastTarget(target_type)) { + throw InternalException("Function '%s' has incomplete argument type %s - concrete argument types must " + "be resolved in the resolve_types callback", + function.GetName(), target_type); + } children[i] = BoundCastExpression::AddCastToType(context, std::move(children[i]), target_type); } } @@ -582,6 +812,17 @@ unique_ptr FunctionBinder::BindScalarFunction(const Identifier &sche return BindScalarFunction(function, std::move(children), error, is_operator, binder); } +unique_ptr FunctionBinder::BindScalarFunction(const Identifier &schema, const Identifier &name, + vector> children, bool is_operator, + optional_ptr binder) { + ErrorData error; + auto result = BindScalarFunction(schema, name, std::move(children), error, is_operator, binder); + if (!result) { + error.Throw(); + } + return result; +} + unique_ptr FunctionBinder::BindScalarFunction(const ScalarFunctionCatalogEntry &func, vector> children, ErrorData &error, bool is_operator, optional_ptr binder) { @@ -593,6 +834,73 @@ unique_ptr FunctionBinder::BindScalarFunction(const ScalarFunctionCa return BindScalarFunction(func, std::move(arguments), error, is_operator, binder); } +//! Returns true if any argument is an untyped NULL, i.e. has type SQLNULL +static bool HasUntypedNullArgument(const vector> ®ular_args, + const vector>> &keyword_args) { + for (auto &child : regular_args) { + if (child->GetReturnType() == LogicalTypeId::SQLNULL) { + return true; + } + } + for (auto &child : keyword_args) { + if (child.second->GetReturnType() == LogicalTypeId::SQLNULL) { + return true; + } + } + return false; +} + +//! Evaluates a constant argument and returns true if it is NULL. A non-NULL result replaces the argument with a +//! constant: the argument has been evaluated anyway, and keeping the result means that it is not evaluated again by +//! every enclosing function call - which made binding nested constant expressions quadratic in their depth. +static bool FoldConstantArgument(ClientContext &context, unique_ptr &child) { + if (child->GetExpressionClass() == ExpressionClass::BOUND_CONSTANT) { + return child->Cast().GetValue().IsNull(); + } + // a lambda has no value of its own, the function it is passed to executes its body + if (child->GetExpressionClass() == ExpressionClass::BOUND_LAMBDA || !child->IsFoldable()) { + return false; + } + Value result; + if (!ExpressionExecutor::TryEvaluateScalar(context, *child, result)) { + // the argument cannot be evaluated: leave it in place so that the error surfaces at execution + return false; + } + if (result.IsNull()) { + return true; + } + auto constant = make_uniq(std::move(result)); + constant->SetAlias(child->GetAlias()); + constant->SetQueryLocation(child->GetQueryLocation()); + child = std::move(constant); + return false; +} + +//! Returns true if any argument is a constant expression that evaluates to NULL. Arguments that evaluate to a non-NULL +//! value are folded to constants along the way. +static bool HasNullConstantArgument(ClientContext &context, vector> ®ular_args, + vector>> &keyword_args) { + for (auto &child : regular_args) { + if (FoldConstantArgument(context, child)) { + return true; + } + } + for (auto &child : keyword_args) { + if (FoldConstantArgument(context, child.second)) { + return true; + } + } + return false; +} + +static void PropagateReturnCollation(BoundSimpleFunction &bound_function, const FunctionProperties &props, + const vector> &children, const string &pushed_collation); + +//! The type of the NULL constant a function call is folded to - SQLNULL if the return type could not be determined +static LogicalType NullReturnType(const LogicalType &return_type) { + return return_type.IsComplete() ? return_type : LogicalType::SQLNULL; +} + unique_ptr FunctionBinder::BindScalarFunction(const ScalarFunctionCatalogEntry &func, vector>> arguments, ErrorData &error, bool is_operator, @@ -612,42 +920,26 @@ unique_ptr FunctionBinder::BindScalarFunction(const ScalarFunctionCa auto [regular_args, keyword_args] = SplitArguments(std::move(arguments)); // If any of the parameters are NULL, the function will just be replaced with a NULL constant. - // We try to give the NULL constant the correct type, but we have to do this without binding the function, - // because functions with DEFAULT_NULL_HANDLING should not have to deal with NULL inputs in their bind code. - // Some functions may have an invalid default return type, as they must be bound to infer the return type. - // In those cases, we default to SQLNULL. - const auto return_type_if_null = - bound_function.GetReturnType().IsComplete() ? bound_function.GetReturnType() : LogicalType::SQLNULL; + // Untyped NULLs never go through type resolution. For typed NULL constants, resolve the function's types only + // when needed to determine the return type. Preserve result collation without invoking bind callbacks. if (bound_function.GetNullHandling() == FunctionNullHandling::DEFAULT_NULL_HANDLING) { - for (auto &child : regular_args) { - if (child->GetReturnType() == LogicalTypeId::SQLNULL) { - return make_uniq(Value(return_type_if_null)); - } - if (!child->IsFoldable()) { - continue; - } - Value result; - if (!ExpressionExecutor::TryEvaluateScalar(context, *child, result)) { - continue; - } - if (result.IsNull()) { - return make_uniq(Value(return_type_if_null)); - } + const auto &return_type = bound_function.GetReturnType(); + if (HasUntypedNullArgument(regular_args, keyword_args)) { + return make_uniq(Value(NullReturnType(return_type))); } - for (auto &child : keyword_args) { - if (child.second->GetReturnType() == LogicalTypeId::SQLNULL) { - return make_uniq(Value(return_type_if_null)); - } - if (!child.second->IsFoldable()) { - continue; - } - Value result; - if (!ExpressionExecutor::TryEvaluateScalar(context, *child.second, result)) { - continue; - } - if (result.IsNull()) { - return make_uniq(Value(return_type_if_null)); + if (HasNullConstantArgument(context, regular_args, keyword_args)) { + if (return_type.IsComplete()) { + BoundScalarFunction null_function(std::move(selected_function)); + for (auto &child : keyword_args) { + regular_args.push_back(std::move(child.second)); + } + PropagateReturnCollation(null_function, null_function.GetProperties(), regular_args, string()); + return make_uniq(Value(null_function.GetReturnType())); } + auto resolved = ResolveScalarFunctionTypes(std::move(selected_function), regular_args, keyword_args); + PropagateReturnCollation(resolved.bound_function, resolved.bound_function.GetProperties(), regular_args, + resolved.pushed_collation); + return make_uniq(Value(NullReturnType(resolved.bound_function.GetReturnType()))); } } return BindScalarFunction(std::move(selected_function), std::move(regular_args), std::move(keyword_args), @@ -658,30 +950,115 @@ static bool RequiresCollationPropagation(const LogicalType &type) { return type.id() == LogicalTypeId::VARCHAR && !type.HasAlias(); } +struct FunctionCollations { + string collation; + identifier_map_t> fields; + + FunctionCollations &GetField(const Identifier &name) { + auto &field = fields[name]; + if (!field) { + field = make_uniq(); + } + return *field; + } +}; + +static Identifier CollationFieldName(const LogicalType &type, idx_t index) { + return StructType::IsUnnamed(type) ? Identifier(to_string(index)) : StructType::GetChildName(type, index); +} + //! Recursively extracts the collation of a (possibly nested) type, e.g. the element collation of a LIST(VARCHAR). -static string ExtractCollationFromType(const LogicalType &type) { - switch (type.id()) { - case LogicalTypeId::VARCHAR: - return RequiresCollationPropagation(type) ? StringType::GetCollation(type) : string(); +//! target is the resolved argument type and may include fields from other arguments. +static void ExtractCollationFromType(const LogicalType &source_type, const LogicalType &target, + FunctionCollations &collations) { + // Leave collation of aliased types to their registered callbacks. + if (source_type.HasAlias() || target.HasAlias()) { + return; + } + switch (source_type.id()) { + case LogicalTypeId::VARCHAR: { + auto collation = StringType::GetCollation(source_type); + if (collations.collation.empty()) { + collations.collation = std::move(collation); + } else if (!collation.empty() && collations.collation != collation) { + throw BinderException("Cannot combine types with different collation"); + } + break; + } case LogicalTypeId::LIST: - return ExtractCollationFromType(ListType::GetChildType(type)); - case LogicalTypeId::ARRAY: - return ExtractCollationFromType(ArrayType::GetChildType(type)); + case LogicalTypeId::ARRAY: { + auto &source_child = source_type.id() == LogicalTypeId::LIST ? ListType::GetChildType(source_type) + : ArrayType::GetChildType(source_type); + auto &target_child = target.id() == LogicalTypeId::LIST ? ListType::GetChildType(target) + : target.id() == LogicalTypeId::ARRAY ? ArrayType::GetChildType(target) + : source_child; + ExtractCollationFromType(source_child, target_child, collations); + break; + } + case LogicalTypeId::STRUCT: + case LogicalTypeId::TUPLE: { + auto &target_type = StructType::IsStruct(target) && target.HasParameters() ? target : source_type; + auto &source_children = StructType::GetChildTypes(source_type); + auto &target_children = StructType::GetChildTypes(target_type); + + // If BOTH source and target_type are named, we can use named indexing, else we use positional. + if (!StructType::IsUnnamed(source_type) && !StructType::IsUnnamed(target_type)) { + // Named indexing, so build a map to map source fields to their position + identifier_map_t src_child_indices; + for (idx_t i = 0; i < source_children.size(); i++) { + src_child_indices.emplace(source_children[i].first, i); + } + + for (idx_t i = 0; i < target_children.size(); i++) { + // Find index in the source fields, or skip if non existent. + auto entry = src_child_indices.find(target_children[i].first); + if (entry == src_child_indices.end()) { + continue; + } + const auto child_index = entry->second; + auto &field = collations.GetField(CollationFieldName(target_type, i)); + ExtractCollationFromType(source_children[child_index].second, target_children[i].second, field); + } + } else { + // Use positional indexing, as not both source_type and target_type are named. + for (idx_t i = 0; i < MinValue(target_children.size(), source_children.size()); i++) { + auto &field = collations.GetField(CollationFieldName(target_type, i)); + ExtractCollationFromType(source_children[i].second, target_children[i].second, field); + } + } + break; + } default: - return string(); + break; } } -//! Returns a copy of the type with the collation applied to every (nested) VARCHAR leaf. -static LogicalType ApplyCollationToType(const LogicalType &type, const LogicalType &collation_type) { +//! Returns a copy of the type with the collected collations applied to its VARCHAR fields. +static LogicalType ApplyCollationToType(const LogicalType &type, const FunctionCollations &collations) { + // Rebuilding an aliased type would discard its alias and bypass its collation callbacks + if (type.HasAlias()) { + return type; + } switch (type.id()) { case LogicalTypeId::VARCHAR: - return RequiresCollationPropagation(type) ? collation_type : type; + return collations.collation.empty() ? type : LogicalType::VARCHAR_COLLATION(collations.collation); case LogicalTypeId::LIST: - return LogicalType::LIST(ApplyCollationToType(ListType::GetChildType(type), collation_type)); + return LogicalType::LIST(ApplyCollationToType(ListType::GetChildType(type), collations)); case LogicalTypeId::ARRAY: - return LogicalType::ARRAY(ApplyCollationToType(ArrayType::GetChildType(type), collation_type), + return LogicalType::ARRAY(ApplyCollationToType(ArrayType::GetChildType(type), collations), ArrayType::GetSize(type)); + case LogicalTypeId::STRUCT: + case LogicalTypeId::TUPLE: { + auto children = StructType::GetChildTypes(type); + for (idx_t i = 0; i < children.size(); i++) { + auto entry = collations.fields.find(CollationFieldName(type, i)); + if (entry != collations.fields.end()) { + children[i].second = ApplyCollationToType(children[i].second, *entry->second); + } + } + return StructType::IsUnnamed(type) ? LogicalType::TUPLE(std::move(children)) + : LogicalType::STRUCT(std::move(children)); + } default: return type; } @@ -704,79 +1081,72 @@ static string ExtractCollation(const vector> &children) { return collation; } -//! Like ExtractCollation, but also considers the collation of nested (LIST/ARRAY) VARCHAR elements. -static string ExtractNestedCollation(const vector> &children) { - string collation; - for (auto &arg : children) { - auto child_collation = ExtractCollationFromType(arg->GetReturnType()); - if (collation.empty()) { - collation = child_collation; - } else if (!child_collation.empty() && collation != child_collation) { - throw BinderException("Cannot combine types with different collation!"); - } - } - return collation; -} - -static void PropagateCollations(ClientContext &, BoundSimpleFunction &bound_function, - vector> &children) { - if (!RequiresCollationPropagation(bound_function.GetReturnType())) { - // we only need to propagate if the function returns a varchar - return; - } - auto collation = ExtractCollation(children); - if (collation.empty()) { - // no collation to propagate - return; +//! Pushes the collations of the arguments into the children, returns the collation to propagate to the return type +static string PushCollations(ClientContext &context, BoundSimpleFunction &bound_function, + vector> &children, CollationType type) { + FunctionCollations collations; + auto &argument_types = bound_function.GetArguments(); + for (idx_t i = 0; i < children.size(); i++) { + auto &source_type = children[i]->GetReturnType(); + auto &target_type = argument_types[i].IsComplete() ? argument_types[i] : source_type; + ExtractCollationFromType(source_type, target_type, collations); } - // propagate the collation to the return type - auto collation_type = LogicalType::VARCHAR_COLLATION(std::move(collation)); - bound_function.SetReturnType(std::move(collation_type)); -} - -static void PushCollations(ClientContext &context, BoundSimpleFunction &bound_function, - vector> &children, CollationType type) { - auto collation = ExtractNestedCollation(children); - if (collation.empty()) { + if (collations.collation.empty() && collations.fields.empty()) { // no collation to push - return; - } - // push collation into the return type if required - auto collation_type = LogicalType::VARCHAR_COLLATION(std::move(collation)); - if (RequiresCollationPropagation(bound_function.GetReturnType())) { - bound_function.SetReturnType(collation_type); + return string(); } // push collations to the children - for (auto &arg : children) { - // apply the collation to the (possibly nested) varchar leaves of the argument type - auto collated_type = ApplyCollationToType(arg->GetReturnType(), collation_type); + for (idx_t i = 0; i < children.size(); i++) { + auto &arg = children[i]; + auto &arg_type = argument_types[i]; + auto &target_type = arg_type.IsComplete() ? arg_type : arg->GetReturnType(); + // apply the collations to the (possibly nested) varchar leaves of the argument type + auto collated_type = ApplyCollationToType(target_type, collations); + arg = BoundCastExpression::AddCastToType(context, std::move(arg), collated_type); if (RequiresCollationPropagation(arg->GetReturnType())) { // if this is a varchar type - propagate the collation - arg->SetReturnType(collation_type); + arg->SetReturnType(collated_type); } // now push the actual collation handling - ExpressionBinder::PushCollation(context, arg, collated_type, type); + if (ExpressionBinder::PushCollation(context, arg, collated_type, type)) { + // Normalization can change field types, so do not cast back to the original argument type. + arg_type = arg->GetReturnType(); + } } + return collations.collation; } -static void HandleCollations(ClientContext &context, BoundSimpleFunction &bound_function, - const FunctionProperties &props, vector> &children) { +//! Pushes collations into the children (if required), returns the collation to propagate to the return type +static string PushArgumentCollations(ClientContext &context, BoundSimpleFunction &bound_function, + const FunctionProperties &props, vector> &children) { switch (props.GetCollationHandling()) { case FunctionCollationHandling::IGNORE_COLLATIONS: - // explicitly ignoring collation handling - break; case FunctionCollationHandling::PROPAGATE_COLLATIONS: - PropagateCollations(context, bound_function, children); - break; + return string(); case FunctionCollationHandling::PUSH_COMBINABLE_COLLATIONS: - // first propagate, then push collations to the children - PushCollations(context, bound_function, children, CollationType::COMBINABLE_COLLATIONS); - break; + return PushCollations(context, bound_function, children, CollationType::COMBINABLE_COLLATIONS); + case FunctionCollationHandling::PUSH_DISTINCT_COLLATIONS: + throw InternalException("PUSH_DISTINCT_COLLATIONS is only supported for aggregate functions"); default: throw InternalException("Unrecognized collation handling"); } } +//! Propagates the collation of the arguments to the return type of the function (if it returns a varchar) +static void PropagateReturnCollation(BoundSimpleFunction &bound_function, const FunctionProperties &props, + const vector> &children, const string &pushed_collation) { + if (!RequiresCollationPropagation(bound_function.GetReturnType())) { + return; + } + auto collation = props.GetCollationHandling() == FunctionCollationHandling::PROPAGATE_COLLATIONS + ? ExtractCollation(children) + : pushed_collation; + if (collation.empty()) { + return; + } + bound_function.SetReturnType(LogicalType::VARCHAR_COLLATION(std::move(collation))); +} + static void InferTemplateType(ClientContext &context, const LogicalType &source, const LogicalType &target, case_insensitive_map_t> &bindings, const Expression ¤t_expr, const BoundSimpleFunction &function) { @@ -851,7 +1221,7 @@ static void InferTemplateType(ClientContext &context, const LogicalType &source, // Otherwise, recurse downwards into nested types, and try to infer nested type members // This only works if the source and target types are completely defined (excluding templates), // i.e. they have aux info. - if (!(source.IsNested() && target.IsNested() && source.AuxInfo() && target.AuxInfo())) { + if (!(source.IsNested() && target.IsNested() && source.HasParameters() && target.HasParameters())) { return; } @@ -975,28 +1345,29 @@ void FunctionBinder::CheckTemplateTypesResolved(const BoundSimpleFunction &bound VerifyTemplateType(bound_function.GetReturnType(), bound_function.GetName()); } -// Drain all named argument and insert them in the correct position according to the function signature. -// Also insert default arguments where needed. -// Returns the resolved name of every argument slot: the signature parameter name for positional slots, the -// caller-provided name for named varargs, and an empty identifier for unnamed varargs. -static vector ResolveArguments(const SimpleFunction &function, vector> &arguments, +// Drain all named arguments and lay out the arguments in the order of the function signature: +// [standard parameters | *args | keyword-only parameters | **kwargs], inserting default values where needed. +// The types of the variadic arguments are added to the arguments of the bound function. +// Returns the resolved name of every argument: the parameter name for standard and keyword-only parameters, the +// caller-provided name for "**kwargs" and an empty identifier for "*args". +static vector ResolveArguments(const SimpleFunction &function, BoundSimpleFunction &bound_function, + vector> &arguments, vector>> &named_arguments) { const auto &sig = function.GetSignature(); + const auto positional_count = sig.GetPositionalParameterCount(); + const auto args_param = sig.GetArgs(); + const auto kwargs_param = sig.GetKwargs(); - const auto kwargs_offset = arguments.size(); + // The arguments that were passed by position + auto positional_arguments = std::move(arguments); + const auto passed_count = positional_arguments.size(); - // Reserve space for the named arguments - if (arguments.size() < sig.GetParameterCount()) { - arguments.resize(sig.GetParameterCount()); - } + // The arguments that were passed by name, by the index of their parameter + vector> keyword_arguments(sig.GetParameterCount()); + vector>> kwargs; identifier_set_t seen_names; - - vector>> trailing_kwargs; - - // We now need to reorder them to match the function signature, before appending them to the argument list. - for (idx_t kwarg_idx = 0; kwarg_idx < named_arguments.size(); kwarg_idx++) { - auto &[name, arg] = named_arguments[kwarg_idx]; + for (auto &[name, arg] : named_arguments) { const auto location = arg->GetQueryLocation(); if (name.empty()) { @@ -1011,78 +1382,140 @@ static vector ResolveArguments(const SimpleFunction &function, vecto throw BinderException(location, "Duplicate named argument %s in function call to '%s'", name, function.GetName()); } - seen_names.insert(name); const auto opt_param_idx = sig.GetParameterIndexByName(name); if (!opt_param_idx.IsValid()) { - if (!sig.HasVarArgs()) { + if (!kwargs_param) { throw BinderException(location, "Function '%s' does not have a parameter named '%s'", function.GetName(), name); } - - // This is a named vararg argument, come back for it later - trailing_kwargs.emplace_back(name, std::move(arg)); + kwargs.emplace_back(name, std::move(arg)); continue; } const auto param_idx = opt_param_idx.GetIndex(); - - if (param_idx < kwargs_offset) { + if (sig.GetParameter(param_idx).AcceptsPosition() && param_idx < passed_count) { throw BinderException(location, "Named argument '%s' cannot be used for parameter '%s' because it has already " "been provided as a positional argument in function call to '%s'", arg->ToString(), name, function.GetName()); } - - // Move it into the correct reserved position - arguments[param_idx] = std::move(arg); - } - - // Fill out missing arguments with default values if they exist, otherwise throw an error. - for (idx_t i = 0; i < sig.GetParameterCount(); i++) { - if (arguments[i]) { + keyword_arguments[param_idx] = std::move(arg); + } + + // the arguments in the order of the signature, moved back into "arguments" once they are all resolved + vector> resolved_arguments; + vector resolved_names; + vector named_arguments_names; + auto &bound_arguments = bound_function.GetArguments(); + + auto add_argument = [&](unique_ptr arg, Identifier resolved_name) { + resolved_arguments.push_back(std::move(arg)); + resolved_names.push_back(std::move(resolved_name)); + }; + + for (idx_t param_idx = 0; param_idx < sig.GetParameterCount(); param_idx++) { + const auto ¶m = sig.GetParameter(param_idx); + switch (param.GetKind()) { + case FunctionParameterKind::VAR_POSITIONAL: + for (idx_t i = positional_count; i < passed_count; i++) { + bound_arguments.insert(bound_arguments.begin() + NumericCast(resolved_arguments.size()), + param.GetType()); + add_argument(std::move(positional_arguments[i]), Identifier()); + } + continue; + case FunctionParameterKind::VAR_KEYWORD: + for (auto &[name, arg] : kwargs) { + bound_arguments.push_back(param.GetType()); + named_arguments_names.push_back(name); + add_argument(std::move(arg), name); + } continue; + default: + break; } - const auto ¶m = sig.GetParameter(i); - - if (param.HasDefaultValue()) { - arguments[i] = make_uniq(*param.GetDefaultValue()); - arguments[i]->SetAlias(param.GetName()); - + if (param.AcceptsPosition() && param_idx < passed_count) { + add_argument(std::move(positional_arguments[param_idx]), param.GetName()); + } else if (keyword_arguments[param_idx]) { + add_argument(std::move(keyword_arguments[param_idx]), param.GetName()); + } else if (param.HasDefaultValue()) { + auto default_arg = make_uniq(*param.GetDefaultValue()); + default_arg->SetAlias(param.GetName()); + add_argument(std::move(default_arg), param.GetName()); } else { throw BinderException("Missing value for parameter %s in function call to %s", param.GetName(), function.GetName()); } + if (param.GetKind() == FunctionParameterKind::KEYWORD_ONLY) { + named_arguments_names.push_back(param.GetName()); + } } - // Every slot covered by the signature is now filled, and sits at the position of its parameter. - vector argument_names(arguments.size()); - for (idx_t i = 0; i < MinValue(arguments.size(), sig.GetParameterCount()); i++) { - argument_names[i] = sig.GetParameter(i).GetName(); + if (!args_param) { + // Without "*args", positional arguments beyond the signature stay behind the resolved arguments + for (idx_t i = positional_count; i < passed_count; i++) { + add_argument(std::move(positional_arguments[i]), Identifier()); + } } - // Now spread out any trailing named vararg arguments into the remaining argument slots, wherever they may be - idx_t kwarg_idx = 0; - for (idx_t slot_idx = kwargs_offset; slot_idx < arguments.size(); slot_idx++) { - if (!arguments[slot_idx]) { - argument_names[slot_idx] = trailing_kwargs[kwarg_idx].first; - arguments[slot_idx] = std::move(trailing_kwargs[kwarg_idx].second); - kwarg_idx++; - } + const auto positional_argument_count = resolved_arguments.size() - named_arguments_names.size(); + bound_function.SetNamedArguments(positional_argument_count, std::move(named_arguments_names)); + arguments = std::move(resolved_arguments); + + return resolved_names; +} + +// The names of the named arguments have to stay parallel to the last arguments +static void VerifyArgumentCount(const BoundSimpleFunction &bound_function, idx_t resolved_count, idx_t argument_count) { + if (resolved_count != argument_count && !bound_function.GetNamedArguments().empty()) { + throw InternalException("Function '%s' cannot add or remove arguments in its bind callback when it is " + "called with keyword-only or '**kwargs' arguments", + bound_function.GetName()); } +} - // And if there are still some left, just append them to the end - idx_t kwargs_remaining = trailing_kwargs.size() - kwarg_idx; - while (kwargs_remaining) { - argument_names.push_back(trailing_kwargs[kwarg_idx].first); - arguments.push_back(std::move(trailing_kwargs[kwarg_idx].second)); - kwarg_idx++; - kwargs_remaining--; +//! The arguments are cast before the bind callback - verify that it did not change the argument types +static void VerifyBindArgumentTypes(const BoundScalarFunction &bound_function, + const vector> &arguments) { + auto &argument_types = bound_function.GetArguments(); + if (arguments.size() > argument_types.size()) { + throw InternalException("Function '%s' added arguments in its bind callback - argument types must be " + "resolved in the resolve_types callback", + bound_function.GetName()); } + for (idx_t i = 0; i < arguments.size(); i++) { + auto &source_type = arguments[i]->GetReturnType(); + if (source_type.id() == LogicalTypeId::LAMBDA) { + continue; + } + auto target_type = argument_types[i]; + PrepareTypeForCast(target_type); + if (target_type.id() == LogicalTypeId::UNKNOWN || target_type.id() == LogicalTypeId::INVALID) { + continue; + } + if (RequiresCast(source_type, target_type) == LogicalTypeComparisonResult::DIFFERENT_TYPES) { + throw InternalException("Function '%s' changed the type of argument %llu from %s to %s in its bind " + "callback - argument types must be resolved in the resolve_types callback", + bound_function.GetName(), i + 1, source_type, target_type); + } + } +} - return argument_names; +//! Incomplete argument types (e.g. a DECIMAL without width and scale) accept any type of that kind - resolve them to +//! the type of the argument +static void ResolveIncompleteArgumentTypes(BoundSimpleFunction &bound_function, + const vector> &arguments) { + auto &argument_types = bound_function.GetArguments(); + for (idx_t i = 0; i < arguments.size() && i < argument_types.size(); i++) { + auto &source_type = arguments[i]->GetReturnType(); + if (!IsIncompleteCastTarget(argument_types[i]) || source_type.id() != argument_types[i].id() || + !source_type.IsComplete()) { + continue; + } + argument_types[i] = source_type; + } } static vector CaptureLogicalArguments(const BoundSimpleFunction &function, @@ -1098,37 +1531,55 @@ static vector CaptureLogicalArguments(const BoundSimpleFunction &fu return result; } -pair> -FunctionBinder::ResolveFunction(shared_ptr function_p, vector> &arguments, - vector>> &named_arguments) { +ResolvedScalarFunctionTypes +FunctionBinder::ResolveScalarFunctionTypes(shared_ptr function_p, + vector> &arguments, + vector>> &named_arguments) { auto &function = *function_p; - // Reorder named args - auto argument_names = ResolveArguments(function, arguments, named_arguments); - // Make a BoundScalarFunction out of the ScalarFunction, so we can store bind info and other properties in it. - BoundScalarFunction bound_function(std::move(function_p)); + ResolvedScalarFunctionTypes result {BoundScalarFunction(std::move(function_p)), {}, {}}; + auto &bound_function = result.bound_function; - // Expand varargs if necessary - if (function.HasVarArgs()) { - const auto &varargs_type = function.GetVarArgs(); - for (idx_t i = function.GetSignature().GetParameterCount(); i < arguments.size(); i++) { - bound_function.GetArguments().push_back(varargs_type); - } - } + // Reorder named args and expand the variadic arguments + result.argument_names = ResolveArguments(function, bound_function, arguments, named_arguments); - // Attempt to resolve template types, before we call the "Bind" callback. + // Attempt to resolve template types, before we call the "resolve_types" callback. ResolveTemplateTypes(bound_function, arguments); bound_function.SetLogicalArguments(CaptureLogicalArguments(bound_function, arguments)); + ResolveIncompleteArgumentTypes(bound_function, arguments); - unique_ptr bind_info; + if (bound_function.HasResolveTypesCallback()) { + ResolveScalarFunctionTypesInput input(context, bound_function, arguments, result.argument_names); + bound_function.GetResolveTypesCallback()(input); + VerifyArgumentCount(bound_function, result.argument_names.size(), arguments.size()); + } + + // The argument types are final - all templates must be resolved before we add casts + for (auto &argument_type : bound_function.GetArguments()) { + VerifyTemplateType(argument_type, bound_function.GetName()); + } + result.pushed_collation = + PushArgumentCollations(context, bound_function, bound_function.GetProperties(), arguments); + CastToFunctionArguments(bound_function, arguments); + return result; +} + +pair> +FunctionBinder::ResolveFunction(shared_ptr function_p, vector> &arguments, + vector>> &named_arguments) { + auto resolved = ResolveScalarFunctionTypes(std::move(function_p), arguments, named_arguments); + auto &bound_function = resolved.bound_function; + auto &argument_names = resolved.argument_names; + // The bind callback is called with arguments that are cast to the argument types of the bound function + unique_ptr bind_info; if (bound_function.HasBindCallback()) { BindScalarFunctionInput input(context, bound_function, arguments, argument_names, binder); bind_info = bound_function.GetBindCallback()(input); + VerifyArgumentCount(bound_function, argument_names.size(), arguments.size()); + VerifyBindArgumentTypes(bound_function, arguments); } - - // After the "bind" callback, we verify that all template types are bound to concrete types. - CheckTemplateTypesResolved(bound_function); + VerifyTemplateType(bound_function.GetReturnType(), bound_function.GetName()); if (bound_function.HasModifiedDatabasesCallback() && binder) { auto &properties = binder->GetStatementProperties(); @@ -1136,12 +1587,9 @@ FunctionBinder::ResolveFunction(shared_ptr function_p, vec bound_function.GetModifiedDatabasesCallback()(context, input); } - HandleCollations(context, bound_function, bound_function.GetProperties(), arguments); + PropagateReturnCollation(bound_function, bound_function.GetProperties(), arguments, resolved.pushed_collation); bound_function.SetLogicalReturnType(bound_function.GetReturnType()); - // check if we need to add casts to the children - CastToFunctionArguments(bound_function, arguments); - return {std::move(bound_function), std::move(bind_info)}; } @@ -1176,6 +1624,10 @@ unique_ptr FunctionBinder::BindScalarFunction(shared_ptr(std::move(bound_function), std::move(children), std::move(bind_info), is_operator); + if (binder && result_func->Function().GetStability() == FunctionStability::CONSISTENT_WITHIN_QUERY) { + // the result depends on the query it is bound in, also when the bind_expression callback folds it + binder->SetAlwaysRequireRebind(); + } if (result_func->Function().HasBindExpressionCallback()) { // if a bind_expression callback is registered - call it and emit the resulting expression FunctionBindExpressionInput input(context, result_func->FunctionMutable(), result_func->BindInfoMutable().get(), @@ -1195,19 +1647,11 @@ FunctionBinder::ResolveFunction(shared_ptr function_p, vector> &children, vector>> &named_arguments) { auto &function = *function_p; - // Reorder named args - auto argument_names = ResolveArguments(function, children, named_arguments); - // Make a BoundFunction out of the func BoundAggregateFunction bound_function(std::move(function_p)); - // Expand varargs if necessary - if (function.HasVarArgs()) { - const auto &varargs_type = function.GetVarArgs(); - for (idx_t i = function.GetSignature().GetParameterCount(); i < children.size(); i++) { - bound_function.GetArguments().push_back(varargs_type); - } - } + // Reorder named args and expand the variadic arguments + auto argument_names = ResolveArguments(function, bound_function, children, named_arguments); ResolveTemplateTypes(bound_function, children); bound_function.SetLogicalArguments(CaptureLogicalArguments(bound_function, children)); @@ -1220,6 +1664,7 @@ FunctionBinder::ResolveFunction(shared_ptr function_p, // we may have lost some arguments in the bind children.resize(MinValue(bound_function.GetArguments().size(), children.size())); + VerifyArgumentCount(bound_function, argument_names.size(), children.size()); } CheckTemplateTypesResolved(bound_function); @@ -1259,6 +1704,15 @@ FunctionBinder::BindAggregateFunction(shared_ptr functi vector> children, vector>> keyword_args, unique_ptr filter, AggregateType aggr_type) { + if (aggr_type == AggregateType::DISTINCT && + function->GetCollationHandling() == FunctionCollationHandling::PUSH_DISTINCT_COLLATIONS) { + for (auto &child : children) { + ExpressionBinder::PushCollation(context, child, child->GetReturnType()); + } + for (auto &argument : keyword_args) { + ExpressionBinder::PushCollation(context, argument.second, argument.second->GetReturnType()); + } + } auto [bound_function, bind_info] = ResolveFunction(std::move(function), children, keyword_args); return make_uniq(std::move(bound_function), std::move(children), std::move(filter), @@ -1292,20 +1746,14 @@ FunctionBinder::ResolveFunction(shared_ptr function_p, vec optional_ptr> order_types, optional_ptr> arg_order_types) { auto &function = *function_p; - // Reorder named args - auto argument_names = ResolveArguments(function, children, named_arguments); - BoundWindowFunction bound_function(std::move(function_p)); - // Expand varargs if necessary - if (function.HasVarArgs()) { - const auto &varargs_type = function.GetVarArgs(); - for (idx_t i = function.GetSignature().GetParameterCount(); i < children.size(); i++) { - bound_function.GetArguments().push_back(varargs_type); - } - } + // Reorder named args and expand the variadic arguments + auto argument_names = ResolveArguments(function, bound_function, children, named_arguments); ResolveTemplateTypes(bound_function, children); + auto logical_arguments = CaptureLogicalArguments(bound_function, children); + auto argument_types = bound_function.GetArguments(); unique_ptr bind_info; @@ -1314,9 +1762,17 @@ FunctionBinder::ResolveFunction(shared_ptr function_p, vec bind_info = bound_function.GetBindCallback()(input); // we may have lost some arguments in the bind children.resize(MinValue(bound_function.GetArguments().size(), children.size())); + VerifyArgumentCount(bound_function, argument_names.size(), children.size()); } CheckTemplateTypesResolved(bound_function); + for (idx_t i = 0; i < argument_types.size() && i < bound_function.GetArguments().size(); i++) { + if (!argument_types[i].IsComplete() && bound_function.GetArguments()[i].IsComplete()) { + logical_arguments[i] = bound_function.GetArguments()[i]; + } + } + bound_function.SetLogicalArguments(std::move(logical_arguments)); + bound_function.SetLogicalReturnType(bound_function.GetReturnType()); // check if we need to add casts to the children CastToFunctionArguments(bound_function, children); diff --git a/src/duckdb/src/function/function_list.cpp b/src/duckdb/src/function/function_list.cpp index 6e0bf405d..a4af4fdec 100644 --- a/src/duckdb/src/function/function_list.cpp +++ b/src/duckdb/src/function/function_list.cpp @@ -115,6 +115,7 @@ static const StaticFunctionDefinition function[] = { DUCKDB_SCALAR_FUNCTION(InternalCompressStringUintegerFun), DUCKDB_SCALAR_FUNCTION(InternalCompressStringUsmallintFun), DUCKDB_SCALAR_FUNCTION(InternalCompressStringUtinyintFun), + DUCKDB_SCALAR_FUNCTION(InternalCompressedMaterializationCastFun), DUCKDB_SCALAR_FUNCTION(InternalDecompressGeometryPointFun), DUCKDB_SCALAR_FUNCTION_SET(InternalDecompressIntegralBigintFun), DUCKDB_SCALAR_FUNCTION_SET(InternalDecompressIntegralHugeintFun), @@ -174,6 +175,7 @@ static const StaticFunctionDefinition function[] = { DUCKDB_AGGREGATE_FUNCTION_SET(FirstFun), DUCKDB_WINDOW_FUNCTION(FirstValueFun), DUCKDB_SCALAR_FUNCTION(GetVariableFun), + DUCKDB_SCALAR_FUNCTION(HivePartitionComponentFun), DUCKDB_SCALAR_FUNCTION(IlikeEscapeFun), DUCKDB_SCALAR_FUNCTION(InvokeFun), DUCKDB_WINDOW_FUNCTION(LagFun), diff --git a/src/duckdb/src/function/function_set.cpp b/src/duckdb/src/function/function_set.cpp index ae8a5a806..4b6a6c262 100644 --- a/src/duckdb/src/function/function_set.cpp +++ b/src/duckdb/src/function/function_set.cpp @@ -41,25 +41,6 @@ shared_ptr AggregateFunctionSet::GetFunctionByArguments FunctionBinder binder(context); auto index = binder.BindFunction(name, *this, arguments, error); if (!index.IsValid()) { - // check if the arguments are a prefix of any of the arguments - // this is used for functions such as quantile or string_agg that delete part of their arguments during bind - // FIXME: we should come up with a better solution here - for (auto &func : functions) { - auto &sig = func->GetSignature(); - if (arguments.size() >= sig.GetParameters().size()) { - continue; - } - bool is_prefix = true; - for (idx_t k = 0; k < arguments.size(); k++) { - if (arguments[k].id() != sig.GetParameter(k).GetType().id()) { - is_prefix = false; - break; - } - } - if (is_prefix) { - return func; - } - } throw BinderException("Failed to find function %s(%s)\n%s", name, StringUtil::ToString(arguments, ","), error.RawMessage()); } diff --git a/src/duckdb/src/function/pragma/pragma_functions.cpp b/src/duckdb/src/function/pragma/pragma_functions.cpp index c86c04875..4ddce15a7 100644 --- a/src/duckdb/src/function/pragma/pragma_functions.cpp +++ b/src/duckdb/src/function/pragma/pragma_functions.cpp @@ -19,9 +19,9 @@ namespace duckdb { -static void PragmaEnableProfilingStatement(ClientContext &context, const FunctionParameters ¶meters) { +static string PragmaEnableProfilingStatement(ClientContext &context, const FunctionParameters ¶meters) { auto &config = ClientConfig::GetConfig(context); - config.enable_profiler = true; + return StringUtil::Format("SET enable_profiling=%s", SQLString(config.profiler_print_format)); } void RegisterEnableProfiling(BuiltinFunctions &set) { @@ -33,24 +33,25 @@ void RegisterEnableProfiling(BuiltinFunctions &set) { } static void PragmaDisableProfiling(ClientContext &context, const FunctionParameters ¶meters) { + DBConfig::GetConfig(context).CheckLock(EnableProfilingSetting::Name); auto &config = ClientConfig::GetConfig(context); config.enable_profiler = false; } -static void PragmaEnableProgressBar(ClientContext &context, const FunctionParameters ¶meters) { - ClientConfig::GetConfig(context).enable_progress_bar = true; +static string PragmaEnableProgressBar(ClientContext &context, const FunctionParameters ¶meters) { + return "SET enable_progress_bar=true"; } -static void PragmaDisableProgressBar(ClientContext &context, const FunctionParameters ¶meters) { - ClientConfig::GetConfig(context).enable_progress_bar = false; +static string PragmaDisableProgressBar(ClientContext &context, const FunctionParameters ¶meters) { + return "SET enable_progress_bar=false"; } -static void PragmaEnablePrintProgressBar(ClientContext &context, const FunctionParameters ¶meters) { - ClientConfig::GetConfig(context).print_progress_bar = true; +static string PragmaEnablePrintProgressBar(ClientContext &context, const FunctionParameters ¶meters) { + return "SET enable_progress_bar_print=true"; } -static void PragmaDisablePrintProgressBar(ClientContext &context, const FunctionParameters ¶meters) { - ClientConfig::GetConfig(context).print_progress_bar = false; +static string PragmaDisablePrintProgressBar(ClientContext &context, const FunctionParameters ¶meters) { + return "SET enable_progress_bar_print=false"; } //! Warn that a pragma that no longer does anything is on its way out @@ -71,14 +72,17 @@ static void PragmaDisableVerification(ClientContext &context, const FunctionPara } static void PragmaEnableForceParallelism(ClientContext &context, const FunctionParameters ¶meters) { + DBConfig::GetConfig(context).CheckLock("verify_parallelism"); ClientConfig::GetConfig(context).verify_parallelism = true; } static void PragmaForceCheckpoint(ClientContext &context, const FunctionParameters ¶meters) { + DBConfig::GetConfig(context).CheckLock("force_checkpoint"); DBConfig::GetConfig(context).options.force_checkpoint = true; } static void PragmaDisableForceParallelism(ClientContext &context, const FunctionParameters ¶meters) { + DBConfig::GetConfig(context).CheckLock("verify_parallelism"); ClientConfig::GetConfig(context).verify_parallelism = false; } @@ -91,19 +95,21 @@ static void PragmaDisableObjectCache(ClientContext &context, const FunctionParam } static void PragmaEnableCheckpointOnShutdown(ClientContext &context, const FunctionParameters ¶meters) { + DBConfig::GetConfig(context).CheckLock("checkpoint_on_shutdown"); DBConfig::GetConfig(context).options.checkpoint_on_shutdown = true; } static void PragmaDisableCheckpointOnShutdown(ClientContext &context, const FunctionParameters ¶meters) { + DBConfig::GetConfig(context).CheckLock("checkpoint_on_shutdown"); DBConfig::GetConfig(context).options.checkpoint_on_shutdown = false; } -static void PragmaEnableOptimizer(ClientContext &context, const FunctionParameters ¶meters) { - Settings::Set(context, SetScope::SESSION, Value::BOOLEAN(true)); +static string PragmaEnableOptimizer(ClientContext &context, const FunctionParameters ¶meters) { + return "SET SESSION enable_optimizer=true"; } -static void PragmaDisableOptimizer(ClientContext &context, const FunctionParameters ¶meters) { - Settings::Set(context, SetScope::SESSION, Value::BOOLEAN(false)); +static string PragmaDisableOptimizer(ClientContext &context, const FunctionParameters ¶meters) { + return "SET SESSION enable_optimizer=false"; } void PragmaFunctions::RegisterFunction(BuiltinFunctions &set) { diff --git a/src/duckdb/src/function/pragma/pragma_queries.cpp b/src/duckdb/src/function/pragma/pragma_queries.cpp index 688199e4b..8c69b06cb 100644 --- a/src/duckdb/src/function/pragma/pragma_queries.cpp +++ b/src/duckdb/src/function/pragma/pragma_queries.cpp @@ -170,7 +170,7 @@ static string PragmaImportDatabase(ClientContext &context, const FunctionParamet auto query = string(buffer.get(), UnsafeNumericCast(fsize)); // Replace the placeholder with the path provided to IMPORT if (file == "load.sql") { - Parser parser; + auto parser = Parser::GetBuiltinParser(); parser.ParseQuery(query); auto copy_statements = std::move(parser.statements); query.clear(); @@ -216,23 +216,29 @@ static string PragmaUserAgent(ClientContext &context, const FunctionParameters & } void PragmaQueries::RegisterFunction(BuiltinFunctions &set) { - set.AddFunction(PragmaFunction::PragmaCall("table_info", PragmaTableInfo, {LogicalType::VARCHAR})); - set.AddFunction(PragmaFunction::PragmaCall("storage_info", PragmaStorageInfo, {LogicalType::VARCHAR})); + set.AddFunction(PragmaFunction::PragmaCall( + "table_info", PragmaTableInfo, FunctionSignature().AddPositionalOnly("table_name", LogicalType::VARCHAR))); + set.AddFunction(PragmaFunction::PragmaCall( + "storage_info", PragmaStorageInfo, FunctionSignature().AddPositionalOnly("table_name", LogicalType::VARCHAR))); set.AddFunction(PragmaFunction::PragmaCall("metadata_info", PragmaMetadataInfo, {})); set.AddFunction(PragmaFunction::PragmaStatement("show_tables", PragmaShowTables)); set.AddFunction(PragmaFunction::PragmaStatement("show_tables_expanded", PragmaShowTablesExpanded)); set.AddFunction(PragmaFunction::PragmaStatement("show_databases", PragmaShowDatabases)); set.AddFunction(PragmaFunction::PragmaStatement("database_list", PragmaDatabaseList)); set.AddFunction(PragmaFunction::PragmaStatement("collations", PragmaCollations)); - set.AddFunction(PragmaFunction::PragmaCall("show", PragmaShow, {LogicalType::VARCHAR})); + set.AddFunction(PragmaFunction::PragmaCall( + "show", PragmaShow, FunctionSignature().AddPositionalOnly("table_name", LogicalType::VARCHAR))); set.AddFunction(PragmaFunction::PragmaStatement("version", PragmaVersion)); set.AddFunction(PragmaFunction::PragmaStatement("extension_versions", PragmaExtensionVersions)); set.AddFunction(PragmaFunction::PragmaStatement("platform", PragmaPlatform)); set.AddFunction(PragmaFunction::PragmaStatement("database_size", PragmaDatabaseSize)); set.AddFunction(PragmaFunction::PragmaStatement("functions", PragmaFunctionsQuery)); - set.AddFunction(PragmaFunction::PragmaCall("import_database", PragmaImportDatabase, {LogicalType::VARCHAR})); - set.AddFunction( - PragmaFunction::PragmaCall("copy_database", PragmaCopyDatabase, {LogicalType::VARCHAR, LogicalType::VARCHAR})); + set.AddFunction(PragmaFunction::PragmaCall("import_database", PragmaImportDatabase, + FunctionSignature().AddPositionalOnly("path", LogicalType::VARCHAR))); + set.AddFunction(PragmaFunction::PragmaCall("copy_database", PragmaCopyDatabase, + FunctionSignature() + .AddPositionalOnly("from_database", LogicalType::VARCHAR) + .AddPositionalOnly("to_database", LogicalType::VARCHAR))); set.AddFunction(PragmaFunction::PragmaStatement("user_agent", PragmaUserAgent)); } diff --git a/src/duckdb/src/function/pragma_function.cpp b/src/duckdb/src/function/pragma_function.cpp index 9208a906b..b63450d96 100644 --- a/src/duckdb/src/function/pragma_function.cpp +++ b/src/duckdb/src/function/pragma_function.cpp @@ -3,9 +3,25 @@ namespace duckdb { +PragmaFunctionInfo::~PragmaFunctionInfo() { +} + +//! A pragma's varargs are a positional list, so it declares "*args" and never a "**kwargs" - an argument named after +//! no parameter stays an error rather than being absorbed +static FunctionSignature PragmaSignature(vector arguments, LogicalType varargs) { + FunctionSignature signature; + for (auto &argument : arguments) { + signature.AddParameter(std::move(argument)); + } + if (varargs.id() != LogicalTypeId::INVALID) { + signature.AddArgs("args", std::move(varargs)); + } + return signature; +} + PragmaFunction::PragmaFunction(Identifier name, PragmaType pragma_type, pragma_query_t query, pragma_function_t function, vector arguments, LogicalType varargs) - : SimpleNamedParameterFunction(std::move(name), std::move(arguments), std::move(varargs)), type(pragma_type), + : SimpleFunction(std::move(name), PragmaSignature(std::move(arguments), std::move(varargs))), type(pragma_type), query(query), function(function) { } @@ -19,6 +35,20 @@ PragmaFunction PragmaFunction::PragmaCall(const Identifier &name, pragma_functio return PragmaFunction(name, PragmaType::PRAGMA_CALL, nullptr, function, std::move(arguments), std::move(varargs)); } +PragmaFunction::PragmaFunction(Identifier name, PragmaType pragma_type, pragma_query_t query, + pragma_function_t function, FunctionSignature signature) + : SimpleFunction(std::move(name), std::move(signature)), type(pragma_type), query(query), function(function) { +} + +PragmaFunction PragmaFunction::PragmaCall(const Identifier &name, pragma_query_t query, FunctionSignature signature) { + return PragmaFunction(name, PragmaType::PRAGMA_CALL, query, nullptr, std::move(signature)); +} + +PragmaFunction PragmaFunction::PragmaCall(const Identifier &name, pragma_function_t function, + FunctionSignature signature) { + return PragmaFunction(name, PragmaType::PRAGMA_CALL, nullptr, function, std::move(signature)); +} + PragmaFunction PragmaFunction::PragmaStatement(const Identifier &name, pragma_query_t query) { vector types; return PragmaFunction(name, PragmaType::PRAGMA_STATEMENT, query, nullptr, std::move(types), LogicalType::INVALID); @@ -35,7 +65,7 @@ string PragmaFunction::ToString() const { case PragmaType::PRAGMA_STATEMENT: return StringUtil::Format("PRAGMA %s", name); case PragmaType::PRAGMA_CALL: { - return StringUtil::Format("PRAGMA %s", SimpleNamedParameterFunction::ToString()); + return StringUtil::Format("PRAGMA %s", SimpleFunction::ToString()); } default: return "UNKNOWN"; diff --git a/src/duckdb/src/function/scalar/comparison/between.cpp b/src/duckdb/src/function/scalar/comparison/between.cpp index e8aa8b0cf..f1fc5b657 100644 --- a/src/duckdb/src/function/scalar/comparison/between.cpp +++ b/src/duckdb/src/function/scalar/comparison/between.cpp @@ -12,8 +12,7 @@ namespace duckdb { struct BetweenFunctionData : public FunctionData { BetweenFunctionData(bool lower_inclusive, bool upper_inclusive) - : FunctionData(InternalKind::BOUND_BETWEEN), lower_inclusive(lower_inclusive), - upper_inclusive(upper_inclusive) { + : lower_inclusive(lower_inclusive), upper_inclusive(upper_inclusive) { } bool lower_inclusive; @@ -244,8 +243,7 @@ bool BoundBetweenExpression::UpperInclusive(const BoundFunctionExpression &betwe } bool BoundBetweenExpression::HasValidBindData(const BoundFunctionExpression &between_expr) { - return between_expr.BindInfo() && - between_expr.BindInfo()->GetInternalKind() == FunctionData::InternalKind::BOUND_BETWEEN; + return between_expr.BindInfo() != nullptr; } const Expression &BoundBetweenExpression::Input(const BoundFunctionExpression &between_expr) { diff --git a/src/duckdb/src/function/scalar/compressed_materialization/compress_geometry.cpp b/src/duckdb/src/function/scalar/compressed_materialization/compress_geometry.cpp index accdfa082..ca258058e 100644 --- a/src/duckdb/src/function/scalar/compressed_materialization/compress_geometry.cpp +++ b/src/duckdb/src/function/scalar/compressed_materialization/compress_geometry.cpp @@ -133,4 +133,14 @@ ScalarFunction InternalDecompressGeometryPointFun::GetFunction() { return CMGeometryPointDecompressFun::GetFunction(); } +CMExpressionType CMUtils::GetGeometryType(const BoundScalarFunction &function) { + if (function.GetDeserializeCallback() == CMGeometryPointCompressDeserialize) { + return CMExpressionType::COMPRESS; + } + if (function.GetDeserializeCallback() == CMGeometryPointDecompressDeserialize) { + return CMExpressionType::DECOMPRESS; + } + return CMExpressionType::NONE; +} + } // namespace duckdb diff --git a/src/duckdb/src/function/scalar/compressed_materialization/compress_integral.cpp b/src/duckdb/src/function/scalar/compressed_materialization/compress_integral.cpp index 8083edeb8..05806a431 100644 --- a/src/duckdb/src/function/scalar/compressed_materialization/compress_integral.cpp +++ b/src/duckdb/src/function/scalar/compressed_materialization/compress_integral.cpp @@ -297,4 +297,14 @@ ScalarFunctionSet InternalDecompressIntegralUhugeintFun::GetFunctions() { return GetIntegralDecompressFunctionSet(LogicalType(LogicalTypeId::UHUGEINT)); } +CMExpressionType CMUtils::GetIntegralType(const BoundScalarFunction &function) { + if (function.GetDeserializeCallback() == CMIntegralDeserialize) { + return CMExpressionType::COMPRESS; + } + if (function.GetDeserializeCallback() == CMIntegralDeserialize) { + return CMExpressionType::DECOMPRESS; + } + return CMExpressionType::NONE; +} + } // namespace duckdb diff --git a/src/duckdb/src/function/scalar/compressed_materialization/compress_string.cpp b/src/duckdb/src/function/scalar/compressed_materialization/compress_string.cpp index b2e66cf77..e428d5f7b 100644 --- a/src/duckdb/src/function/scalar/compressed_materialization/compress_string.cpp +++ b/src/duckdb/src/function/scalar/compressed_materialization/compress_string.cpp @@ -298,4 +298,14 @@ ScalarFunctionSet InternalDecompressStringFun::GetFunctions() { return GetStringDecompressFunctionSet(); } +CMExpressionType CMUtils::GetStringType(const BoundScalarFunction &function) { + if (function.GetDeserializeCallback() == CMStringCompressDeserialize) { + return CMExpressionType::COMPRESS; + } + if (function.GetDeserializeCallback() == CMStringDecompressDeserialize) { + return CMExpressionType::DECOMPRESS; + } + return CMExpressionType::NONE; +} + } // namespace duckdb diff --git a/src/duckdb/src/function/scalar/compressed_materialization_utils.cpp b/src/duckdb/src/function/scalar/compressed_materialization_utils.cpp index c8aa67eb1..f3d2949d9 100644 --- a/src/duckdb/src/function/scalar/compressed_materialization_utils.cpp +++ b/src/duckdb/src/function/scalar/compressed_materialization_utils.cpp @@ -1,7 +1,58 @@ #include "duckdb/function/scalar/compressed_materialization_utils.hpp" +#include "duckdb/function/scalar/compressed_materialization_functions.hpp" +#include "duckdb/function/scalar/operator_functions.hpp" +#include "duckdb/planner/expression/bound_cast_expression.hpp" +#include "duckdb/planner/expression/bound_function_expression.hpp" namespace duckdb { +ScalarFunction InternalCompressedMaterializationCastFun::GetFunction() { + auto function = CastFun::GetFunction(); + function.SetQualifiedName(QualifiedName("system", "main", Name)); + function.SetBindCallback(CMUtils::Bind); + return function; +} + +void CMUtils::MarkCast(BoundFunctionExpression &expression) { + D_ASSERT(BoundCastExpression::IsCast(expression)); + D_ASSERT(BoundCastExpression::IsDefaultCast(expression)); + auto definition = make_shared_ptr(InternalCompressedMaterializationCastFun::GetFunction()); + auto &function = expression.FunctionMutable(); + function.SetName(definition->GetName()); + function.SetBindCallback(CMUtils::Bind); + function.SetDefinition(std::move(definition)); +} + +CMExpressionType CMUtils::GetExpressionType(const BoundFunctionExpression &expression) { + auto &function = expression.Function(); + if (function.GetBindCallback() != Bind) { + return CMExpressionType::NONE; + } + if (function.GetName() == InternalCompressedMaterializationCastFun::Name && + function.GetDeserializeCallback() == CastFun::GetFunction().GetDeserializeCallback()) { + return CMExpressionType::CAST; + } + auto type = GetIntegralType(function); + if (type == CMExpressionType::NONE) { + type = GetStringType(function); + } + if (type == CMExpressionType::NONE) { + type = GetGeometryType(function); + } + return type; +} + +optional_ptr CMUtils::GetWrappedInput(const Expression &expression) { + if (expression.GetExpressionClass() != ExpressionClass::BOUND_FUNCTION) { + return nullptr; + } + auto &function = expression.Cast(); + if (function.GetChildren().empty() || GetExpressionType(function) == CMExpressionType::NONE) { + return nullptr; + } + return function.GetChildren()[0].get(); +} + const vector CMUtils::IntegralTypes() { return {LogicalType::UTINYINT, LogicalType::USMALLINT, LogicalType::UINTEGER, LogicalType::UBIGINT}; } diff --git a/src/duckdb/src/function/scalar/create_sort_key.cpp b/src/duckdb/src/function/scalar/create_sort_key.cpp index d97a81ba2..eb2fff76e 100644 --- a/src/duckdb/src/function/scalar/create_sort_key.cpp +++ b/src/duckdb/src/function/scalar/create_sort_key.cpp @@ -17,6 +17,7 @@ #include "duckdb/planner/expression/bound_function_expression.hpp" #include "duckdb/planner/expression_binder.hpp" #include "duckdb/parser/parser.hpp" +#include "duckdb/main/client_context.hpp" namespace duckdb { @@ -37,6 +38,30 @@ struct SortKeyBindData : public FunctionData { } }; +static void AccumulateSortKeyWidth(const LogicalType &type, bool &all_constant, idx_t &constant_size) { + const auto physical_type = type.InternalType(); + if (!TypeIsConstantSize(physical_type)) { + all_constant = false; + return; + } + // we always add one byte for the validity + constant_size += GetTypeIdSize(physical_type) + 1; +} + +static void VerifyDecodeSortKeyType(const LogicalType &sort_key_type, bool all_constant, idx_t constant_size) { + if (sort_key_type == LogicalType::BIGINT) { + if (!all_constant || constant_size > sizeof(int64_t)) { + throw BinderException("sort_key has type BIGINT but arguments require BLOB"); + } + } else if (sort_key_type == LogicalType::BLOB) { + if (all_constant && constant_size <= sizeof(int64_t)) { + throw BinderException("sort_key has type BLOB but arguments require BIGINT"); + } + } else { + throw BinderException("sort_key must be either BIGINT or BLOB, got %s instead", sort_key_type.ToString()); + } +} + unique_ptr CreateSortKeyBind(BindScalarFunctionInput &input) { auto &arguments = input.GetArguments(); auto &function = input.GetBoundFunction(); @@ -55,13 +80,7 @@ unique_ptr CreateSortKeyBind(BindScalarFunctionInput &input) { bool all_constant = true; idx_t constant_size = 0; for (idx_t i = 0; i < arguments.size(); i += 2) { - auto physical_type = arguments[i]->GetReturnType().InternalType(); - if (!TypeIsConstantSize(physical_type)) { - all_constant = false; - } else { - // we always add one byte for the validity - constant_size += GetTypeIdSize(physical_type) + 1; - } + AccumulateSortKeyWidth(arguments[i]->GetReturnType(), all_constant, constant_size); } if (all_constant) { if (constant_size <= sizeof(int64_t)) { @@ -1028,22 +1047,20 @@ unique_ptr DecodeSortKeyBind(BindScalarFunctionInput &input) { for (idx_t i = 1; i < arguments.size(); i += 2) { // Parse column definition Value col = input.GetConstant(i); - const auto col_list = Parser::ParseColumnList(col.ToString()); + const auto col_list = Parser(context).ParseColumnList(col.ToString()); if (col_list.LogicalColumnCount() != 1) { throw BinderException("decode_sort_key col must contain exactly one column"); } const auto &col_def = col_list.GetColumn(PhysicalIndex(0)); const auto &col_name = col_def.GetName(); - const auto &col_type = TransformStringToLogicalType(col_def.GetType().ToString(), context); + auto col_type = col_def.GetType(); + if (col_type.IsUnbound()) { + // Resolve the parsed type against the catalog. + col_type = TransformStringToLogicalType(col_type.ToString(), context); + } // Keep track of this to validate the arguments - const auto physical_type = col_type.InternalType(); - if (!TypeIsConstantSize(physical_type)) { - all_constant = false; - } else { - // we always add one byte for the validity - constant_size += GetTypeIdSize(physical_type) + 1; - } + AccumulateSortKeyWidth(col_type, all_constant, constant_size); // Add name/type to create a struct children.emplace_back(col_name, col_type); @@ -1054,19 +1071,7 @@ unique_ptr DecodeSortKeyBind(BindScalarFunctionInput &input) { result->modifiers.push_back(OrderModifiers::Parse(sort_specifier_str)); } - const auto &sort_key_arg = *arguments[0]; - if (sort_key_arg.GetReturnType() == LogicalType::BIGINT) { - if (!all_constant || constant_size > sizeof(int64_t)) { - throw BinderException("sort_key has type BIGINT but arguments require BLOB"); - } - } else if (sort_key_arg.GetReturnType() == LogicalType::BLOB) { - if (all_constant && constant_size <= sizeof(int64_t)) { - throw BinderException("sort_key has type BLOB but arguments require BIGINT"); - } - } else { - throw BinderException("sort_key must be either BIGINT or BLOB, got %s instead", - sort_key_arg.GetReturnType().ToString()); - } + VerifyDecodeSortKeyType(arguments[0]->GetReturnType(), all_constant, constant_size); function.SetReturnType(LogicalType::STRUCT(std::move(children))); return std::move(result); @@ -1484,17 +1489,39 @@ static void DecodeSortKeyFunction(DataChunk &args, ExpressionState &state, Vecto ScalarFunction CreateSortKeyFun::GetFunction() { ScalarFunction sort_key_function("create_sort_key", {LogicalType::ANY}, LogicalType::BLOB, CreateSortKeyFunction, CreateSortKeyBind); - sort_key_function.SetVarArgs(LogicalType::ANY); + sort_key_function.GetSignature().GetParameter(0).SetName("key1"); + sort_key_function.GetSignature().AddArgs("args", LogicalType::ANY); sort_key_function.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); return sort_key_function; } +unique_ptr DecodeSortKeyFun::Bind(unique_ptr sort_key, child_list_t columns, + vector modifiers) { + D_ASSERT(columns.size() == modifiers.size()); + bool all_constant = true; + idx_t constant_size = 0; + for (auto &column : columns) { + AccumulateSortKeyWidth(column.second, all_constant, constant_size); + } + VerifyDecodeSortKeyType(sort_key->GetReturnType(), all_constant, constant_size); + + auto bind_data = make_uniq(); + bind_data->modifiers = std::move(modifiers); + bind_data->all_constant = all_constant; + ScalarFunction definition("decode_sort_key", {sort_key->GetReturnType()}, LogicalType::STRUCT(std::move(columns)), + DecodeSortKeyFunction); + BoundScalarFunction function(definition); + vector> children; + children.push_back(std::move(sort_key)); + return make_uniq(std::move(function), std::move(children), std::move(bind_data)); +} + ScalarFunction DecodeSortKeyFun::GetFunction() { ScalarFunction sort_key_function( "decode_sort_key", {{"sort_key", LogicalType::ANY}, {"col", LogicalType::VARCHAR}, {"sort_specifier", LogicalType::VARCHAR}}, LogicalType::STRUCT({{"any", LogicalType::ANY}}), DecodeSortKeyFunction, DecodeSortKeyBind); - sort_key_function.SetVarArgs(LogicalType::VARCHAR); + sort_key_function.GetSignature().AddArgs("args", LogicalType::VARCHAR); return sort_key_function; } diff --git a/src/duckdb/src/function/scalar/generic/constant_or_null.cpp b/src/duckdb/src/function/scalar/generic/constant_or_null.cpp index 0c64a79a7..ffd587661 100644 --- a/src/duckdb/src/function/scalar/generic/constant_or_null.cpp +++ b/src/duckdb/src/function/scalar/generic/constant_or_null.cpp @@ -96,7 +96,7 @@ ScalarFunction ConstantOrNullFun::GetFunction() { auto fun = ScalarFunction("constant_or_null", {}, LogicalType::ANY, ConstantOrNullFunction); fun.GetSignature().AddParameter("arg1", LogicalType::ANY).AddParameter("arg2", LogicalType::ANY); fun.SetBindCallback(ConstantOrNullBind); - fun.SetVarArgs(LogicalType::ANY); + fun.GetSignature().AddArgs("args", LogicalType::ANY); return fun; } diff --git a/src/duckdb/src/function/scalar/generic/getvariable.cpp b/src/duckdb/src/function/scalar/generic/getvariable.cpp index fe1fbb2a9..4eca4fa03 100644 --- a/src/duckdb/src/function/scalar/generic/getvariable.cpp +++ b/src/duckdb/src/function/scalar/generic/getvariable.cpp @@ -57,6 +57,8 @@ ScalarFunction GetVariableFun::GetFunction() { ScalarFunction getvar("getvariable", {}, LogicalType::ANY, nullptr, GetVariableBind, nullptr); getvar.GetSignature().AddParameter("variable_name", LogicalType::VARCHAR); getvar.SetBindExpressionCallback(BindGetVariableExpression); + // the value of the variable can change between queries + getvar.SetStability(FunctionStability::CONSISTENT_WITHIN_QUERY); return getvar; } diff --git a/src/duckdb/src/function/scalar/generic/invoke.cpp b/src/duckdb/src/function/scalar/generic/invoke.cpp index 86b873036..baa8f1d78 100644 --- a/src/duckdb/src/function/scalar/generic/invoke.cpp +++ b/src/duckdb/src/function/scalar/generic/invoke.cpp @@ -9,6 +9,15 @@ namespace duckdb { namespace { +static void PropagateLambdaProperties(BoundScalarFunction &function, const Expression &lambda_expr) { + if (lambda_expr.IsVolatile()) { + function.SetVolatile(); + } + if (lambda_expr.CanThrow()) { + function.SetFallible(); + } +} + struct LambdaInvokeData final : public LambdaFunctionData { unique_ptr lambda_expr; @@ -33,9 +42,13 @@ struct LambdaInvokeData final : public LambdaFunctionData { } //! Deserializes a lambda function's bind data - static unique_ptr Deserialize(Deserializer &deserializer, BoundScalarFunction &) { + static unique_ptr Deserialize(Deserializer &deserializer, BoundScalarFunction &function) { auto lambda_expr = deserializer.ReadPropertyWithExplicitDefault>( 101, "lambda_expr", unique_ptr()); + if (lambda_expr) { + auto &bound_lambda_expr = lambda_expr->Cast(); + PropagateLambdaProperties(function, *bound_lambda_expr.LambdaExpr()); + } return make_uniq(std::move(lambda_expr)); } @@ -117,6 +130,7 @@ unique_ptr LambdaInvokeBind(BindScalarFunctionInput &input) { } bound_function.SetReturnType(bound_lambda_expr.LambdaExpr()->GetReturnType()); + PropagateLambdaProperties(bound_function, *bound_lambda_expr.LambdaExpr()); return make_uniq(bound_lambda_expr.Copy()); } @@ -139,7 +153,7 @@ ScalarFunction InvokeFun::GetFunction() { ScalarFunction fun("invoke", {}, LogicalType::ANY, LambdaInvokeFunction); fun.GetSignature().AddParameter("lambda", LogicalType::LAMBDA).AddParameter("arg1", LogicalType::ANY); fun.SetBindCallback(LambdaInvokeBind); - fun.SetVarArgs(LogicalType::ANY); + fun.GetSignature().AddArgs("args", LogicalType::ANY); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); fun.SetBindLambdaCallback(LambdaInvokeBindParameters); fun.SetInitStateCallback(LambdaInvokeState::Init); diff --git a/src/duckdb/src/function/scalar/geometry/geometry_functions.cpp b/src/duckdb/src/function/scalar/geometry/geometry_functions.cpp index 3ffe6af7f..42b1cd1e0 100644 --- a/src/duckdb/src/function/scalar/geometry/geometry_functions.cpp +++ b/src/duckdb/src/function/scalar/geometry/geometry_functions.cpp @@ -233,21 +233,19 @@ static unique_ptr BindCRSFunctionExpression(FunctionBindExpressionIn return make_uniq(GetCRSValue(return_type)); } -static unique_ptr BindCRSFunction(BindScalarFunctionInput &input) { - auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - - if (arguments[0]->GetReturnType().id() != LogicalTypeId::GEOMETRY) { - return nullptr; +static void CRSFunctionResolveTypes(ResolveScalarFunctionTypesInput &input) { + auto &argument_type = input.GetArgumentType(0); + if (argument_type.id() != LogicalTypeId::GEOMETRY) { + return; } // Propagate the CRS from the input argument to the parameter type - bound_function.GetArguments()[0] = arguments[0]->GetReturnType(); - return nullptr; + input.GetBoundFunction().GetArguments()[0] = argument_type; } ScalarFunction StCrsFun::GetFunction() { - ScalarFunction geom_func({}, LogicalType::VARCHAR, CRSFunction, BindCRSFunction); + ScalarFunction geom_func({}, LogicalType::VARCHAR, CRSFunction); + geom_func.SetResolveTypesCallback(CRSFunctionResolveTypes); geom_func.GetSignature().AddParameter("geom", LogicalType::GEOMETRY()); geom_func.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); geom_func.SetBindExpressionCallback(BindCRSFunctionExpression); diff --git a/src/duckdb/src/function/scalar/list/list_extract.cpp b/src/duckdb/src/function/scalar/list/list_extract.cpp index 42be1683b..5c7910077 100644 --- a/src/duckdb/src/function/scalar/list/list_extract.cpp +++ b/src/duckdb/src/function/scalar/list/list_extract.cpp @@ -176,6 +176,9 @@ ScalarFunctionSet ListExtractFun::GetFunctions() { ScalarFunction sfun({LogicalType::VARCHAR, LogicalType::BIGINT}, LogicalType::VARCHAR, ListExtractFunction, StringExtractBind); + sfun.GetSignature().GetParameter(0).SetName("string"); + sfun.GetSignature().GetParameter(1).SetName("index"); + lfun.SetFallible(); list_extract_set.AddFunction(lfun); list_extract_set.AddFunction(sfun); diff --git a/src/duckdb/src/function/scalar/list/list_resize.cpp b/src/duckdb/src/function/scalar/list/list_resize.cpp index a8cb15d64..33d53a291 100644 --- a/src/duckdb/src/function/scalar/list/list_resize.cpp +++ b/src/duckdb/src/function/scalar/list/list_resize.cpp @@ -74,7 +74,8 @@ static void ListResizeFunction(DataChunk &args, ExpressionState &, Vector &resul sel.set_index(j, row_idx); } const auto &default_vector = args.data[2]; - list.Append(default_vector, sel, args.size(), 0, remaining_count.GetValue()); + // the selection only has an entry for every padded row + list.Append(default_vector, sel, remaining_count.GetValue(), 0, remaining_count.GetValue()); continue; } @@ -83,54 +84,58 @@ static void ListResizeFunction(DataChunk &args, ExpressionState &, Vector &resul } } -static unique_ptr ListResizeBind(BindScalarFunctionInput &input) { - auto &context = input.GetClientContext(); +static void ListResizeResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - D_ASSERT(bound_function.GetArguments().size() == 2 || arguments.size() == 3); + D_ASSERT(bound_function.GetArguments().size() == 2 || input.GetArgumentCount() == 3); bound_function.GetArguments()[1] = LogicalType::UBIGINT; // If the first argument is an array, cast it to a list. - arguments[0] = BoundCastExpression::AddArrayCastToList(context, std::move(arguments[0])); + auto list_type = input.GetArgumentType(0); + if (list_type.id() == LogicalTypeId::ARRAY) { + list_type = LogicalType::LIST(ArrayType::GetChildType(list_type)); + bound_function.GetArguments()[0] = list_type; + } // Early-out, if the first argument is a constant NULL. - if (arguments[0]->GetReturnType() == LogicalType::SQLNULL) { + if (list_type == LogicalType::SQLNULL) { bound_function.GetArguments()[0] = LogicalType::SQLNULL; bound_function.SetReturnType(LogicalType::SQLNULL); - return make_uniq(bound_function.GetReturnType()); + return; } // Early-out, if the first argument is a prepared statement. - if (arguments[0]->GetReturnType() == LogicalType::UNKNOWN) { - bound_function.SetReturnType(arguments[0]->GetReturnType()); - return make_uniq(bound_function.GetReturnType()); + if (list_type == LogicalType::UNKNOWN) { + bound_function.SetReturnType(list_type); + return; } // Attempt implicit casting, if the default type does not match list the list child type. - if (bound_function.GetArguments().size() == 3 && - ListType::GetChildType(arguments[0]->GetReturnType()) != arguments[2]->GetReturnType() && - arguments[2]->GetReturnType() != LogicalTypeId::SQLNULL) { - bound_function.GetArguments()[2] = ListType::GetChildType(arguments[0]->GetReturnType()); + if (bound_function.GetArguments().size() == 3 && ListType::GetChildType(list_type) != input.GetArgumentType(2) && + input.GetArgumentType(2) != LogicalTypeId::SQLNULL) { + bound_function.GetArguments()[2] = ListType::GetChildType(list_type); } - bound_function.SetReturnType(arguments[0]->GetReturnType()); - return make_uniq(bound_function.GetReturnType()); + bound_function.SetReturnType(list_type); } ScalarFunctionSet ListResizeFun::GetFunctions() { - ScalarFunction simple_fun({}, LogicalType::LIST(LogicalTypeId::ANY), ListResizeFunction, ListResizeBind); + ScalarFunction simple_fun({}, LogicalType::LIST(LogicalTypeId::ANY), ListResizeFunction, + VariableReturnBindData::Bind); simple_fun.GetSignature() .AddParameter("list", LogicalType::LIST(LogicalTypeId::ANY)) .AddParameter("size", LogicalTypeId::ANY); simple_fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); simple_fun.SetFallible(); - ScalarFunction default_value_fun({}, LogicalType::LIST(LogicalTypeId::ANY), ListResizeFunction, ListResizeBind); + simple_fun.SetResolveTypesCallback(ListResizeResolveTypes); + ScalarFunction default_value_fun({}, LogicalType::LIST(LogicalTypeId::ANY), ListResizeFunction, + VariableReturnBindData::Bind); default_value_fun.GetSignature() .AddParameter("list", LogicalType::LIST(LogicalTypeId::ANY)) .AddParameter("size", LogicalTypeId::ANY) .AddParameter("value", LogicalTypeId::ANY); default_value_fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); default_value_fun.SetFallible(); + default_value_fun.SetResolveTypesCallback(ListResizeResolveTypes); ScalarFunctionSet list_resize_set("list_resize"); list_resize_set.AddFunction(simple_fun); list_resize_set.AddFunction(default_value_fun); diff --git a/src/duckdb/src/function/scalar/list/list_select.cpp b/src/duckdb/src/function/scalar/list/list_select.cpp index a52d7c4d6..a3f67377b 100644 --- a/src/duckdb/src/function/scalar/list/list_select.cpp +++ b/src/duckdb/src/function/scalar/list/list_select.cpp @@ -14,9 +14,9 @@ struct SetSelectionVectorSelect { using CHILD_TYPE = int64_t; static void SetSelectionVector(SelectionVector &selection_vector, ValidityMask &validity_mask, - const ValidityMask &input_validity, const VectorIterator &child_data, - idx_t child_idx, idx_t &target_offset, idx_t selection_offset, idx_t input_offset, - idx_t target_length) { + const VectorValidityIterator &input_validity, + const VectorIterator &child_data, idx_t child_idx, idx_t &target_offset, + idx_t selection_offset, idx_t input_offset, idx_t target_length) { auto child_entry = child_data[selection_offset + child_idx]; int64_t sel_idx = -1; if (child_entry.IsValid()) { @@ -29,7 +29,7 @@ struct SetSelectionVectorSelect { if (sel_idx >= 0 && sel_idx < UnsafeNumericCast(target_length)) { auto sel_idx_unsigned = UnsafeNumericCast(sel_idx); selection_vector.set_index(target_offset, input_offset + sel_idx_unsigned); - if (!input_validity.RowIsValid(input_offset + sel_idx_unsigned)) { + if (!input_validity.IsValid(input_offset + sel_idx_unsigned)) { validity_mask.SetInvalid(target_offset); } } else { @@ -49,7 +49,7 @@ struct SetSelectionVectorWhere { using CHILD_TYPE = bool; static void SetSelectionVector(SelectionVector &selection_vector, ValidityMask &validity_mask, - const ValidityMask &input_validity, const VectorIterator &child_data, + const VectorValidityIterator &input_validity, const VectorIterator &child_data, idx_t child_idx, idx_t &target_offset, idx_t selection_offset, idx_t input_offset, idx_t target_length) { auto child_val = child_data[selection_offset + child_idx]; @@ -65,7 +65,7 @@ struct SetSelectionVectorWhere { } selection_vector.set_index(target_offset, input_offset + child_idx); - if (!input_validity.RowIsValid(input_offset + child_idx)) { + if (!input_validity.IsValid(input_offset + child_idx)) { validity_mask.SetInvalid(target_offset); } @@ -97,7 +97,7 @@ void ListSelectFunction(const DataChunk &args, ExpressionState &state, Vector &r auto &selection_entry = ListVector::GetChild(selection_list); auto input_lists_data = list.Values(); auto &input_entry = ListVector::GetChild(list); - auto &input_validity = FlatVector::Validity(input_entry); + auto input_validity = input_entry.Validity(); auto selection_entry_data = selection_entry.Values(); idx_t result_length = 0; diff --git a/src/duckdb/src/function/scalar/list/list_zip.cpp b/src/duckdb/src/function/scalar/list/list_zip.cpp index 285a5473c..e7318989f 100644 --- a/src/duckdb/src/function/scalar/list/list_zip.cpp +++ b/src/duckdb/src/function/scalar/list/list_zip.cpp @@ -175,7 +175,7 @@ static unique_ptr ListZipBind(BindScalarFunctionInput &input) { ScalarFunction ListZipFun::GetFunction() { auto fun = ScalarFunction({}, LogicalType::LIST(LogicalTypeId::STRUCT), ListZipFunction, ListZipBind); - fun.SetVarArgs(LogicalType::ANY); + fun.GetSignature().AddArgs("args", LogicalType::ANY); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); return fun; } diff --git a/src/duckdb/src/function/scalar/map/map_contains.cpp b/src/duckdb/src/function/scalar/map/map_contains.cpp index 95624c3a5..5565d0ebd 100644 --- a/src/duckdb/src/function/scalar/map/map_contains.cpp +++ b/src/duckdb/src/function/scalar/map/map_contains.cpp @@ -1,4 +1,5 @@ #include "duckdb/common/vector/map_vector.hpp" +#include "duckdb/function/function_binder.hpp" #include "duckdb/function/scalar/list/contains_or_position.hpp" #include "duckdb/planner/expression/bound_cast_expression.hpp" #include "duckdb/function/scalar/map_functions.hpp" @@ -15,12 +16,52 @@ static void MapContainsFunction(DataChunk &input, ExpressionState &state, Vector ListSearchOp(map_vec, key_vec, arg_vec, result, count); } +static bool TypeHasCollation(const LogicalType &type) { + switch (type.id()) { + case LogicalTypeId::VARCHAR: + return !type.HasAlias() && !StringType::GetCollation(type).empty(); + case LogicalTypeId::LIST: + return TypeHasCollation(ListType::GetChildType(type)); + case LogicalTypeId::ARRAY: + return TypeHasCollation(ArrayType::GetChildType(type)); + default: + return false; + } +} + +static unique_ptr BindMapContainsExpression(FunctionBindExpressionInput &input) { + const auto &key_type = input.children[1]->GetReturnType(); + const auto &map_key_type = MapType::KeyType(input.children[0]->GetReturnType()); + if (!TypeHasCollation(key_type) && !TypeHasCollation(map_key_type)) { + return nullptr; + } + + FunctionBinder function_binder(input.context); + ErrorData error; + vector> children; + children.push_back(std::move(input.children[0])); + auto keys = function_binder.BindScalarFunction(Identifier::DefaultSchema(), "map_keys", std::move(children), error); + if (!keys) { + error.Throw(); + } + children.clear(); + children.push_back(std::move(keys)); + children.push_back(std::move(input.children[1])); + auto result = + function_binder.BindScalarFunction(Identifier::DefaultSchema(), "list_contains", std::move(children), error); + if (!result) { + error.Throw(); + } + return result; +} + ScalarFunction MapContainsFun::GetFunction() { auto key_type = LogicalType::TEMPLATE("K"); auto val_type = LogicalType::TEMPLATE("V"); ScalarFunction fun("map_contains", {}, LogicalType::BOOLEAN, MapContainsFunction); fun.GetSignature().AddParameter("map", LogicalType::MAP(key_type, val_type)).AddParameter("key", key_type); + fun.SetBindExpressionCallback(BindMapContainsExpression); return fun; } diff --git a/src/duckdb/src/function/scalar/operator/arithmetic.cpp b/src/duckdb/src/function/scalar/operator/arithmetic.cpp index 86be9a6d2..bdc3e8e01 100644 --- a/src/duckdb/src/function/scalar/operator/arithmetic.cpp +++ b/src/duckdb/src/function/scalar/operator/arithmetic.cpp @@ -323,23 +323,22 @@ unique_ptr PropagateFloatingStats(ClientContext &context, Functi return result.ToUnique(); } -template -unique_ptr BindDecimalArithmetic(BindScalarFunctionInput &input) { - auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - auto bind_data = make_uniq(); - +//! Computes the result type of a decimal addition, subtraction or modulo over the given input types +template +LogicalType DecimalArithmeticResultType(const vector &input_types, bool &check_overflow) { + check_overflow = false; // get the max width and scale of the input arguments uint8_t max_width = 0, max_scale = 0, max_width_over_scale = 0; - for (idx_t i = 0; i < arguments.size(); i++) { - if (arguments[i]->GetReturnType().id() == LogicalTypeId::UNKNOWN) { + for (auto &input_type : input_types) { + if (input_type.id() == LogicalTypeId::UNKNOWN) { continue; } uint8_t width, scale; - auto can_convert = arguments[i]->GetReturnType().GetDecimalProperties(width, scale); + auto can_convert = input_type.GetDecimalProperties(width, scale); if (!can_convert) { - throw InternalException("Could not convert type %s to a decimal.", - arguments[i]->GetReturnType().ToString()); + throw BinderException("Could not convert type %s to a decimal for the arithmetic operator - add an " + "explicit cast", + input_type.ToString()); } if (width > max_width) { max_width = width; @@ -355,16 +354,24 @@ unique_ptr BindDecimalArithmetic(BindScalarFunctionIn } if (required_width > Decimal::MAX_WIDTH_DECIMAL) { // target width does not fit in decimal at all: truncate the scale and perform overflow detection - bind_data->check_overflow = true; + check_overflow = true; required_width = Decimal::MAX_WIDTH_DECIMAL; } - // arithmetic between two decimal arguments: check the types of the input arguments - LogicalType result_type = LogicalType::DECIMAL(required_width, max_scale); + return LogicalType::DECIMAL(required_width, max_scale); +} + +//! Resolves the types of a decimal addition, subtraction or modulo, returns whether overflow checks are required +template +bool ResolveDecimalArithmeticTypes(ResolveScalarFunctionTypesInput &input) { + auto &bound_function = input.GetBoundFunction(); + bool check_overflow; + auto argument_types = input.GetArgumentTypes(); + auto result_type = DecimalArithmeticResultType(argument_types, check_overflow); // we cast all input types to the specified type - for (idx_t i = 0; i < arguments.size(); i++) { + for (idx_t i = 0; i < argument_types.size(); i++) { // first check if the cast is necessary // if the argument has a matching scale and internal type as the output type, no casting is necessary - auto &argument_type = arguments[i]->GetReturnType(); + auto &argument_type = argument_types[i]; uint8_t width, scale; argument_type.GetDecimalProperties(width, scale); if (scale == DecimalType::GetScale(result_type) && argument_type.InternalType() == result_type.InternalType()) { @@ -374,18 +381,25 @@ unique_ptr BindDecimalArithmetic(BindScalarFunctionIn } } bound_function.SetReturnType(result_type); - return bind_data; + return check_overflow; +} + +//! The overflow check is derived from the types the function was called with, before they were cast +template +unique_ptr BindDecimalArithmetic(BindScalarFunctionInput &input) { + auto bind_data = make_uniq(); + DecimalArithmeticResultType(input.GetBoundFunction().GetLogicalArguments(), bind_data->check_overflow); + return std::move(bind_data); } template -unique_ptr BindDecimalAddSubtract(BindScalarFunctionInput &input) { +void DecimalAddSubtractResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); - - auto bind_data = BindDecimalArithmetic(input); + auto check_overflow = ResolveDecimalArithmeticTypes(input); // now select the physical function to execute auto &result_type = bound_function.GetReturnType(); - if (bind_data->check_overflow) { + if (check_overflow) { bound_function.SetFunctionCallback(GetScalarBinaryFunction(result_type.InternalType())); } else { bound_function.SetFunctionCallback(GetScalarBinaryFunction(result_type.InternalType())); @@ -396,7 +410,6 @@ unique_ptr BindDecimalAddSubtract(BindScalarFunctionInput &input) } else { bound_function.SetStatisticsCallback(PropagateNumericStats); } - return std::move(bind_data); } void SerializeDecimalArithmetic(Serializer &serializer, const optional_ptr bind_data_p, @@ -428,22 +441,21 @@ unique_ptr DeserializeDecimalArithmetic(Deserializer &deserializer return std::move(bind_data); } -unique_ptr NopDecimalBind(BindScalarFunctionInput &input) { +void NopDecimalResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - - bound_function.SetReturnType(arguments[0]->GetReturnType()); - bound_function.GetArguments()[0] = arguments[0]->GetReturnType(); - return nullptr; + auto &decimal_type = input.GetArgumentType(0); + bound_function.SetReturnType(decimal_type); + bound_function.GetArguments()[0] = decimal_type; } } // namespace ScalarFunction AddFunction::GetFunction(const LogicalType &type) { D_ASSERT(type.IsNumeric()); - auto fn = type.id() == LogicalTypeId::DECIMAL - ? ScalarFunction("+", {type}, type, ScalarFunction::NopFunction, NopDecimalBind) - : ScalarFunction("+", {type}, type, ScalarFunction::NopFunction); + ScalarFunction fn("+", {type}, type, ScalarFunction::NopFunction); + if (type.id() == LogicalTypeId::DECIMAL) { + fn.SetResolveTypesCallback(NopDecimalResolveTypes); + } fn.SetUnaryArgProperties(ArgProperties().StrictlyIncreasing()); return fn; } @@ -460,14 +472,16 @@ static void BignumAdd(DataChunk &args, ExpressionState &state, Vector &result) { static void BignumSubtract(DataChunk &args, ExpressionState &state, Vector &result) { auto &heap = StringVector::GetStringHeap(result); + // negate a copy of the right operand - the input must not be modified, it can e.g. also be the left operand + vector negated_rhs; BinaryExecutor::Execute( args.data[0], args.data[1], result, [&](bignum_t a, bignum_t b) { const BignumIntermediate lhs(a); - BignumIntermediate rhs(b); - rhs.NegateInPlace(); - auto result_value = BignumIntermediate::Add(heap, lhs, rhs); + negated_rhs.resize(b.data.GetSize()); + memcpy(negated_rhs.data(), b.data.GetData(), b.data.GetSize()); + BignumIntermediate rhs(negated_rhs.data(), negated_rhs.size()); rhs.NegateInPlace(); - return result_value; + return BignumIntermediate::Add(heap, lhs, rhs); }); } @@ -484,8 +498,8 @@ ScalarFunction AddFunction::GetFunction(const LogicalType &left_type, const Logi const auto unset = ArgProperties(); if (left_type.IsNumeric() && left_type.id() == right_type.id()) { if (left_type.id() == LogicalTypeId::DECIMAL) { - auto function = ScalarFunction("+", {left_type, right_type}, left_type, nullptr, - BindDecimalAddSubtract); + auto function = ScalarFunction("+", {left_type, right_type}, left_type, nullptr, BindDecimalArithmetic); + function.SetResolveTypesCallback(DecimalAddSubtractResolveTypes); function.SetFallible(); function.SetSerializeCallback(SerializeDecimalArithmetic); function.SetDeserializeCallback(DeserializeDecimalArithmetic); @@ -538,7 +552,9 @@ ScalarFunction AddFunction::GetFunction(const LogicalType &left_type, const Logi function.SetFallible(); // INTERVAL ordering uses 30-day months but DATE arithmetic uses calendar months, // e.g. '2025-02-01' + INTERVAL '29 days' = '2025-03-02' > + INTERVAL '1 month' = '2025-03-01'. - function.SetArgProperties({inc, unset}); + // Adding months clamps the day, e.g. both '2000-01-30' and '2000-01-31' + INTERVAL '1 month' are + // '2000-02-29', so this is not strictly increasing in the DATE. + function.SetArgProperties({ArgProperties().NonDecreasing(), unset}); return function; } else if (right_type.id() == LogicalTypeId::TIME) { ScalarFunction function("+", {left_type, right_type}, LogicalType::TIMESTAMP, @@ -574,7 +590,7 @@ ScalarFunction AddFunction::GetFunction(const LogicalType &left_type, const Logi ScalarFunction function("+", {left_type, right_type}, LogicalType::TIMESTAMP, ScalarFunction::BinaryFunction); function.SetFallible(); - function.SetArgProperties({unset, inc}); + function.SetArgProperties({unset, ArgProperties().NonDecreasing()}); return function; } else if (right_type.id() == LogicalTypeId::TIME) { // TIME +/- INTERVAL wraps modulo 24h. @@ -592,7 +608,8 @@ ScalarFunction AddFunction::GetFunction(const LogicalType &left_type, const Logi ScalarFunction function("+", {left_type, right_type}, LogicalType::TIMESTAMP, ScalarFunction::BinaryFunction); function.SetFallible(); - function.SetArgProperties({unset, inc}); + // not monotonic: adding months clamps the day but keeps the time, e.g. + // '2000-01-30 23:00' + INTERVAL '1 month' > '2000-01-31 01:00' + INTERVAL '1 month' return function; } break; @@ -628,7 +645,7 @@ ScalarFunction AddFunction::GetFunction(const LogicalType &left_type, const Logi ScalarFunction function("+", {left_type, right_type}, LogicalType::TIMESTAMP, ScalarFunction::BinaryFunction); function.SetFallible(); - function.SetArgProperties({inc, unset}); + // not monotonic: see INTERVAL + TIMESTAMP return function; } break; @@ -716,31 +733,9 @@ interval_t NegateOperator::Operation(interval_t input) { return result; } -struct DecimalNegateBindData : public FunctionData { - DecimalNegateBindData() : bound_type(LogicalTypeId::INVALID) { - } - - unique_ptr Copy() const override { - auto res = make_uniq(); - res->bound_type = bound_type; - return std::move(res); - } - - bool Equals(const FunctionData &other_p) const override { - const auto &other = other_p.Cast(); - return other.bound_type == bound_type; - } - - LogicalTypeId bound_type; -}; - -static unique_ptr DecimalNegateBind(BindScalarFunctionInput &input) { +static void DecimalNegateResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - - auto bind_data = make_uniq(); - - auto &decimal_type = arguments[0]->GetReturnType(); + auto &decimal_type = input.GetArgumentType(0); auto width = DecimalType::GetWidth(decimal_type); if (width <= Decimal::MAX_WIDTH_INT16) { bound_function.SetFunctionCallback( @@ -759,21 +754,20 @@ static unique_ptr DecimalNegateBind(BindScalarFunctionInput &input decimal_type.Verify(); bound_function.GetArguments()[0] = decimal_type; bound_function.SetReturnType(decimal_type); - return nullptr; } -static unique_ptr IntegerNegateBind(BindScalarFunctionInput &input) { +static void IntegerNegateResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); - D_ASSERT(input.GetArguments().size() == 1); + D_ASSERT(input.GetArgumentCount() == 1); // only need to promote if the argument is a constant that exactly equals the type's minimum value auto constant = input.TryGetConstant(0); if (!constant || constant->IsNull()) { - return nullptr; + return; } auto &type = bound_function.GetArguments()[0]; if (*constant != Value::MinimumValue(type)) { - return nullptr; + return; } LogicalType promoted_type; switch (type.id()) { @@ -790,12 +784,11 @@ static unique_ptr IntegerNegateBind(BindScalarFunctionInput &input promoted_type = LogicalType::HUGEINT; break; default: - return nullptr; + return; } bound_function.GetArguments()[0] = promoted_type; bound_function.SetReturnType(promoted_type); bound_function.SetFunctionCallback(ScalarFunction::GetScalarUnaryFunction(promoted_type)); - return nullptr; } ScalarFunction SubtractFunction::GetFunction(const LogicalType &type) { @@ -805,7 +798,8 @@ ScalarFunction SubtractFunction::GetFunction(const LogicalType &type) { func.SetUnaryArgProperties(ArgProperties().StrictlyDecreasing()); return func; } else if (type.id() == LogicalTypeId::DECIMAL) { - ScalarFunction func("-", {type}, type, nullptr, DecimalNegateBind); + ScalarFunction func("-", {type}, type, nullptr); + func.SetResolveTypesCallback(DecimalNegateResolveTypes); func.SetUnaryArgProperties(ArgProperties().StrictlyDecreasing()); return func; } else if (type.id() == LogicalTypeId::BIGNUM) { @@ -814,8 +808,8 @@ ScalarFunction SubtractFunction::GetFunction(const LogicalType &type) { return func; } else { D_ASSERT(type.IsNumeric()); - ScalarFunction func("-", {type}, type, ScalarFunction::GetScalarUnaryFunction(type), - IntegerNegateBind); + ScalarFunction func("-", {type}, type, ScalarFunction::GetScalarUnaryFunction(type)); + func.SetResolveTypesCallback(IntegerNegateResolveTypes); func.SetFallible(); func.SetUnaryArgProperties(ArgProperties().StrictlyDecreasing()); return func; @@ -825,8 +819,9 @@ ScalarFunction SubtractFunction::GetFunction(const LogicalType &type) { ScalarFunction SubtractFunction::GetFunction(const LogicalType &left_type, const LogicalType &right_type) { if (left_type.IsNumeric() && left_type.id() == right_type.id()) { if (left_type.id() == LogicalTypeId::DECIMAL) { - ScalarFunction function("-", {left_type, right_type}, left_type, nullptr, - BindDecimalAddSubtract); + ScalarFunction function("-", {left_type, right_type}, left_type, nullptr, BindDecimalArithmetic); + function.SetResolveTypesCallback( + DecimalAddSubtractResolveTypes); function.SetFallible(); function.SetSerializeCallback(SerializeDecimalArithmetic); function.SetDeserializeCallback( @@ -884,7 +879,8 @@ ScalarFunction SubtractFunction::GetFunction(const LogicalType &left_type, const function.SetFallible(); // Monotonic in DATE only; INTERVAL ordering uses 30-day months but DATE arithmetic uses // calendar months so monotonicity in the INTERVAL arg is broken (see OperatorAddFun). - function.SetArgProperties({ArgProperties().StrictlyIncreasing(), ArgProperties()}); + // Subtracting months clamps the day, so this is not strictly increasing in the DATE. + function.SetArgProperties({ArgProperties().NonDecreasing(), ArgProperties()}); return function; } break; @@ -901,8 +897,9 @@ ScalarFunction SubtractFunction::GetFunction(const LogicalType &left_type, const "-", {left_type, right_type}, LogicalType::TIMESTAMP, ScalarFunction::BinaryFunction); function.SetFallible(); - // Monotonic in TIMESTAMP only; see DATE - INTERVAL above. - function.SetArgProperties({ArgProperties().StrictlyIncreasing(), ArgProperties()}); + // not monotonic: subtracting months clamps the day but keeps the time, e.g. + // '2000-03-30 23:00' - INTERVAL '1 month' > '2000-03-31 01:00' - INTERVAL '1 month' + return function; } break; @@ -1017,23 +1014,21 @@ struct MultiplyPropagateStatistics { } }; -unique_ptr BindDecimalMultiply(BindScalarFunctionInput &input) { - auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - - auto bind_data = make_uniq(); - +//! Computes the result type of a decimal multiplication over the given input types +LogicalType DecimalMultiplyResultType(const vector &input_types, bool &check_overflow) { + check_overflow = false; uint8_t result_width = 0, result_scale = 0; uint8_t max_width = 0; - for (idx_t i = 0; i < arguments.size(); i++) { - if (arguments[i]->GetReturnType().id() == LogicalTypeId::UNKNOWN) { + for (auto &input_type : input_types) { + if (input_type.id() == LogicalTypeId::UNKNOWN) { continue; } uint8_t width, scale; - auto can_convert = arguments[i]->GetReturnType().GetDecimalProperties(width, scale); + auto can_convert = input_type.GetDecimalProperties(width, scale); if (!can_convert) { - throw InternalException("Could not convert type %s to a decimal?", - arguments[i]->GetReturnType().ToString()); + throw BinderException("Could not convert type %s to a decimal for the arithmetic operator - add an " + "explicit cast", + input_type.ToString()); } max_width = MaxValue(width, max_width); result_width += width; @@ -1049,18 +1044,26 @@ unique_ptr BindDecimalMultiply(BindScalarFunctionInput &input) { } if (result_width > Decimal::MAX_WIDTH_INT64 && max_width <= Decimal::MAX_WIDTH_INT64 && result_scale < Decimal::MAX_WIDTH_INT64) { - bind_data->check_overflow = true; + check_overflow = true; result_width = Decimal::MAX_WIDTH_INT64; } if (result_width > Decimal::MAX_WIDTH_DECIMAL) { - bind_data->check_overflow = true; + check_overflow = true; result_width = Decimal::MAX_WIDTH_DECIMAL; } - LogicalType result_type = LogicalType::DECIMAL(result_width, result_scale); + return LogicalType::DECIMAL(result_width, result_scale); +} + +void DecimalMultiplyResolveTypes(ResolveScalarFunctionTypesInput &input) { + auto &bound_function = input.GetBoundFunction(); + bool check_overflow; + auto argument_types = input.GetArgumentTypes(); + auto result_type = DecimalMultiplyResultType(argument_types, check_overflow); + auto result_width = DecimalType::GetWidth(result_type); // since our scale is the summation of our input scales, we do not need to cast to the result scale // however, we might need to cast to the correct internal type - for (idx_t i = 0; i < arguments.size(); i++) { - auto &argument_type = arguments[i]->GetReturnType(); + for (idx_t i = 0; i < argument_types.size(); i++) { + auto &argument_type = argument_types[i]; if (argument_type.InternalType() == result_type.InternalType()) { bound_function.GetArguments()[i] = argument_type; } else { @@ -1075,7 +1078,7 @@ unique_ptr BindDecimalMultiply(BindScalarFunctionInput &input) { result_type.Verify(); bound_function.SetReturnType(result_type); // now select the physical function to execute - if (bind_data->check_overflow) { + if (check_overflow) { bound_function.SetFunctionCallback( GetScalarBinaryFunction(result_type.InternalType())); } else { @@ -1083,6 +1086,12 @@ unique_ptr BindDecimalMultiply(BindScalarFunctionInput &input) { } bound_function.SetStatisticsCallback( PropagateNumericStats); +} + +//! The overflow check is derived from the types the function was called with, before they were cast +unique_ptr BindDecimalMultiply(BindScalarFunctionInput &input) { + auto bind_data = make_uniq(); + DecimalMultiplyResultType(input.GetBoundFunction().GetLogicalArguments(), bind_data->check_overflow); return std::move(bind_data); } @@ -1093,6 +1102,7 @@ ScalarFunctionSet OperatorMultiplyFun::GetFunctions() { for (auto &type : LogicalType::Numeric()) { if (type.id() == LogicalTypeId::DECIMAL) { ScalarFunction function({type, type}, type, nullptr, BindDecimalMultiply); + function.SetResolveTypesCallback(DecimalMultiplyResolveTypes); function.SetSerializeCallback(SerializeDecimalArithmetic); function.SetDeserializeCallback( DeserializeDecimalArithmetic); @@ -1165,7 +1175,7 @@ namespace { [[noreturn]] void ThrowDivisionByZero(const Expression &expr) { throw InvalidInputException("Division by zero in expression %s. Use TRY(...) to return NULL for this expression, " - "or SET null_on_division_by_zero=true to return NULL for all divisions by zero.", + "or SET error_on_division_by_zero=false to return NULL for all divisions by zero.", expr.ToString()); } @@ -1285,7 +1295,7 @@ template unique_ptr BindDivisionByZero(BindScalarFunctionInput &input) { auto &context = input.GetClientContext(); auto &bound_function = input.GetBoundFunction(); - auto null_on_zero = Settings::Get(context); + auto null_on_zero = !Settings::Get(context); bound_function.SetFunctionCallback( GetBinaryFunctionZeroCheck(bound_function.GetReturnType().InternalType(), null_on_zero)); return nullptr; @@ -1294,7 +1304,7 @@ unique_ptr BindDivisionByZero(BindScalarFunctionInput &input) { unique_ptr BindIntervalDivide(BindScalarFunctionInput &input) { auto &context = input.GetClientContext(); auto &bound_function = input.GetBoundFunction(); - if (Settings::Get(context)) { + if (!Settings::Get(context)) { bound_function.SetFunctionCallback(BinaryScalarFunctionZeroCheck>); } else { @@ -1312,7 +1322,7 @@ unique_ptr BindBinaryFloatingPoint(BindScalarFunctionInput &input) if (Settings::Get(context)) { bound_function.SetFunctionCallback(GetScalarBinaryFunction(bound_function.GetReturnType().InternalType())); } else { - auto null_on_zero = Settings::Get(context); + auto null_on_zero = !Settings::Get(context); bound_function.SetFunctionCallback( GetBinaryFunctionZeroCheck(bound_function.GetReturnType().InternalType(), null_on_zero)); } @@ -1356,7 +1366,7 @@ struct DividePropagateStatistics { }; // When propagation fails the divisor range may contain zero, and division by zero either raises or (with -// null_on_division_by_zero) produces NULLs that the inputs do not have: return no statistics instead of the +// error_on_division_by_zero=false) produces NULLs that the inputs do not have: return no statistics instead of the // validity-only statistics that PropagateNumericStats falls back to. unique_ptr PropagateIntegerDivideStats(ClientContext &context, FunctionStatisticsInput &input) { auto stats = PropagateNumericStats(context, input); @@ -1403,11 +1413,11 @@ ScalarFunctionSet OperatorIntegerDivideFun::GetFunctions() { // % [modulo] //===--------------------------------------------------------------------===// template -static unique_ptr BindDecimalModulo(BindScalarFunctionInput &input) { +static void DecimalModuloResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); - auto bind_data = BindDecimalArithmetic(input); + auto check_overflow = ResolveDecimalArithmeticTypes(input); // now select the physical function to execute - if (bind_data->check_overflow) { + if (check_overflow) { // fallback to DOUBLE if the decimal type is not guaranteed to fit within the max decimal width for (auto &arg : bound_function.GetArguments()) { arg = LogicalType::DOUBLE; @@ -1415,9 +1425,8 @@ static unique_ptr BindDecimalModulo(BindScalarFunctionInput &input bound_function.SetReturnType(LogicalType::DOUBLE); } auto &result_type = bound_function.GetReturnType(); - auto null_on_zero = Settings::Get(input.GetClientContext()); + auto null_on_zero = !Settings::Get(input.GetClientContext()); bound_function.SetFunctionCallback(GetBinaryFunctionZeroCheck(result_type.InternalType(), null_on_zero)); - return std::move(bind_data); } template <> @@ -1446,7 +1455,9 @@ ScalarFunctionSet OperatorModuloFun::GetFunctions() { if (type.id() == LogicalTypeId::FLOAT || type.id() == LogicalTypeId::DOUBLE) { modulo.AddFunction(ScalarFunction({type, type}, type, nullptr, BindBinaryFloatingPoint)); } else if (type.id() == LogicalTypeId::DECIMAL) { - modulo.AddFunction(ScalarFunction({type, type}, type, nullptr, BindDecimalModulo)); + ScalarFunction function({type, type}, type, nullptr, BindDecimalArithmetic); + function.SetResolveTypesCallback(DecimalModuloResolveTypes); + modulo.AddFunction(std::move(function)); } else { modulo.AddFunction(ScalarFunction({type, type}, type, nullptr, BindDivisionByZero)); } @@ -1471,6 +1482,24 @@ dtime_t InterpolateOperator::Operation(const dtime_t &lo, const double d, const return dtime_t(std::llround(static_cast(lo.value) * (1.0 - d) + static_cast(hi.value) * d)); } +//! TIMETZ values are interpolated by their time normalized to UTC - the result is the normalized time at offset +00 +template <> +dtime_tz_t InterpolateOperator::Operation(const dtime_tz_t &lo, const double d, const dtime_tz_t &hi) { + if (lo.sort_key() == hi.sort_key()) { + return lo; + } + // the sort key holds the normalized time (biased by the maximum offset) in its upper bits + const auto lo_micros = static_cast(dtime_tz_t::decode_micros(lo.sort_key())); + const auto hi_micros = static_cast(dtime_tz_t::decode_micros(hi.sort_key())); + auto micros = std::llround(lo_micros * (1.0 - d) + hi_micros * d); + micros -= int64_t(dtime_tz_t::MAX_OFFSET) * int64_t(dtime_tz_t::OFFSET_MICROS); + micros %= Interval::MICROS_PER_DAY; + if (micros < 0) { + micros += Interval::MICROS_PER_DAY; + } + return dtime_tz_t(dtime_t(micros), 0); +} + template <> timestamp_t InterpolateOperator::Operation(const timestamp_t &lo, const double d, const timestamp_t &hi) { return timestamp_t(std::llround(static_cast(lo.value) * (1.0 - d) + static_cast(hi.value) * d)); diff --git a/src/duckdb/src/function/scalar/operator/cast.cpp b/src/duckdb/src/function/scalar/operator/cast.cpp index 0c4350c30..5cd51b343 100644 --- a/src/duckdb/src/function/scalar/operator/cast.cpp +++ b/src/duckdb/src/function/scalar/operator/cast.cpp @@ -53,9 +53,8 @@ static bool BoundCastCanThrow(const BoundCastInfo &bound_cast, const LogicalType struct CastFunctionData : public FunctionData { CastFunctionData(LogicalType source_type_p, LogicalType target_type_p, BoundCastInfo bound_cast_p, bool try_cast_p, bool is_default_cast_p) - : FunctionData(InternalKind::BOUND_CAST), source_type(std::move(source_type_p)), - target_type(std::move(target_type_p)), bound_cast(std::move(bound_cast_p)), try_cast(try_cast_p), - is_default_cast(is_default_cast_p) { + : source_type(std::move(source_type_p)), target_type(std::move(target_type_p)), + bound_cast(std::move(bound_cast_p)), try_cast(try_cast_p), is_default_cast(is_default_cast_p) { } LogicalType source_type; @@ -180,12 +179,13 @@ static unique_ptr CreateCastExpression(unique_ptr child, vector> children; children.push_back(std::move(child)); + auto can_throw = BoundCastCanThrow(bound_cast, source_type, target_type, try_cast); auto function_data = make_uniq(source_type, target_type, std::move(bound_cast), try_cast, is_default_cast); auto scalar_function = CastFun::GetFunction(); scalar_function.SetReturnType(target_type); - if (BoundCastCanThrow(bound_cast, source_type, target_type, try_cast)) { + if (can_throw) { scalar_function.SetErrorMode(FunctionErrors::CAN_THROW_RUNTIME_ERROR); } SetCastNullHandling(scalar_function, target_type); @@ -235,8 +235,7 @@ bool BoundCastExpression::IsTryCast(const BoundFunctionExpression &cast_expr) { } bool BoundCastExpression::HasValidBindData(const BoundFunctionExpression &cast_expr) { - if (cast_expr.GetChildren().size() != 1 || !cast_expr.GetChildren()[0] || !cast_expr.BindInfo() || - cast_expr.BindInfo()->GetInternalKind() != FunctionData::InternalKind::BOUND_CAST) { + if (cast_expr.GetChildren().size() != 1 || !cast_expr.GetChildren()[0] || !cast_expr.BindInfo()) { return false; } auto &data = cast_expr.BindInfo()->Cast(); @@ -259,6 +258,10 @@ BoundCastInfo &BoundCastExpression::GetBoundCastMutable(BoundFunctionExpression unique_ptr BoundCastExpression::PropagateStatistics(BoundFunctionExpression &cast_expr, const BaseStatistics &child_stats, optional_ptr context) { + if (!child_stats.CanHaveNoNull() && + cast_expr.Function().GetNullHandling() == FunctionNullHandling::DEFAULT_NULL_HANDLING) { + return BaseStatistics::FromConstant(Value(cast_expr.GetReturnType())).ToUnique(); + } auto &cast_data = cast_expr.BindInfoMutable()->Cast(); auto result = cast_data.bound_cast.PropagateStatistics(cast_data.source_type, cast_data.target_type, child_stats, context); diff --git a/src/duckdb/src/function/scalar/operator/decimal_division.cpp b/src/duckdb/src/function/scalar/operator/decimal_division.cpp index 2365179bd..d90950655 100644 --- a/src/duckdb/src/function/scalar/operator/decimal_division.cpp +++ b/src/duckdb/src/function/scalar/operator/decimal_division.cpp @@ -164,18 +164,18 @@ static scalar_function_t GetDecimalDivExecuteFunction(PhysicalType result_physic // result_scale = max(6, s1 + p2 + 1) // result_width = p1 - s1 + s2 + result_scale // Both are capped at Decimal::MAX_WIDTH_DECIMAL (38). -static unique_ptr DecimalDivisionBind(BindScalarFunctionInput &input) { +static void DecimalDivisionResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); + auto &lhs_type = input.GetArgumentType(0); + auto &rhs_type = input.GetArgumentType(1); uint8_t p1, s1, p2, s2; - if (!arguments[0]->GetReturnType().GetDecimalProperties(p1, s1) || - !arguments[1]->GetReturnType().GetDecimalProperties(p2, s2)) { + if (!lhs_type.GetDecimalProperties(p1, s1) || !rhs_type.GetDecimalProperties(p2, s2)) { throw InvalidInputException("decimal_division: both arguments must be DECIMAL"); } uint8_t result_scale; - if (arguments.size() == 3) { + if (input.GetArgumentCount() == 3) { auto scale_val = input.GetNonNullConstant(2); auto scale_i = scale_val.GetValue(); if (scale_i < 0 || scale_i > Decimal::MAX_WIDTH_DECIMAL) { @@ -222,8 +222,8 @@ static unique_ptr DecimalDivisionBind(BindScalarFunctionInput &inp bound_function.SetReturnType(LogicalType::DECIMAL(result_width, result_scale)); - auto lhs_physical = arguments[0]->GetReturnType().InternalType(); - auto rhs_physical = arguments[1]->GetReturnType().InternalType(); + auto lhs_physical = lhs_type.InternalType(); + auto rhs_physical = rhs_type.InternalType(); auto wider = MaxValue(lhs_physical, rhs_physical); int32_t scale_exp = s2 + result_scale - s1; @@ -251,10 +251,8 @@ static unique_ptr DecimalDivisionBind(BindScalarFunctionInput &inp // during plan deserialization (bind is re-called with the stored argument types, and // changing p changes the result-width formula). // For cross-tier inputs, promote the narrower argument to the wider physical tier. - bound_function.GetArguments()[0] = - (lhs_physical != wider) ? LogicalType::DECIMAL(input_max_width, s1) : arguments[0]->GetReturnType(); - bound_function.GetArguments()[1] = - (rhs_physical != wider) ? LogicalType::DECIMAL(input_max_width, s2) : arguments[1]->GetReturnType(); + bound_function.GetArguments()[0] = (lhs_physical != wider) ? LogicalType::DECIMAL(input_max_width, s1) : lhs_type; + bound_function.GetArguments()[1] = (rhs_physical != wider) ? LogicalType::DECIMAL(input_max_width, s2) : rhs_type; switch (wider) { case PhysicalType::INT16: @@ -282,8 +280,14 @@ static unique_ptr DecimalDivisionBind(BindScalarFunctionInput &inp bound_function.SetFunctionCallback(GetDecimalDivExecuteFunction(result_physical)); break; } +} - return make_uniq(scale_exp); +static unique_ptr DecimalDivisionBind(BindScalarFunctionInput &input) { + auto &bound_function = input.GetBoundFunction(); + auto lhs_scale = DecimalType::GetScale(bound_function.GetArguments()[0]); + auto rhs_scale = DecimalType::GetScale(bound_function.GetArguments()[1]); + auto result_scale = DecimalType::GetScale(bound_function.GetReturnType()); + return make_uniq(rhs_scale + result_scale - lhs_scale); } //===--------------------------------------------------------------------===// @@ -296,6 +300,7 @@ ScalarFunctionSet DecimalDivisionFun::GetFunctions() { ScalarFunction two_arg({}, decimal_type, DecimalDivExecute, DecimalDivisionBind); two_arg.GetSignature().AddParameter("x", decimal_type).AddParameter("y", decimal_type); + two_arg.SetResolveTypesCallback(DecimalDivisionResolveTypes); two_arg.SetFallible(); set.AddFunction(two_arg); @@ -304,6 +309,7 @@ ScalarFunctionSet DecimalDivisionFun::GetFunctions() { .AddParameter("x", decimal_type) .AddParameter("y", decimal_type) .AddParameter("scale", LogicalType::INTEGER); + three_arg.SetResolveTypesCallback(DecimalDivisionResolveTypes); three_arg.SetFallible(); set.AddFunction(three_arg); diff --git a/src/duckdb/src/function/scalar/operator/multiply.cpp b/src/duckdb/src/function/scalar/operator/multiply.cpp index ce6c8d6c2..c728cdd58 100644 --- a/src/duckdb/src/function/scalar/operator/multiply.cpp +++ b/src/duckdb/src/function/scalar/operator/multiply.cpp @@ -1,6 +1,7 @@ #include "duckdb/common/operator/multiply.hpp" #include "duckdb/common/limits.hpp" +#include "duckdb/common/operator/add.hpp" #include "duckdb/common/operator/cast_operators.hpp" #include "duckdb/common/types/hugeint.hpp" #include "duckdb/common/types/uhugeint.hpp" @@ -91,12 +92,17 @@ bool TryMultiplyOperator::Operation(interval_t left, double right, interval_t &r * of cascade and the seconds factor operation itself. */ if (std::fabs(sec_remainder) >= Interval::SECS_PER_DAY) { - result.days += LossyNumericCast(sec_remainder / Interval::SECS_PER_DAY); - sec_remainder -= LossyNumericCast(sec_remainder / Interval::SECS_PER_DAY) * Interval::SECS_PER_DAY; + const auto extra_days = LossyNumericCast(sec_remainder / Interval::SECS_PER_DAY); + if (!TryAddOperator::Operation(result.days, extra_days, result.days)) { + return false; + } + sec_remainder -= extra_days * Interval::SECS_PER_DAY; } /* cascade units down */ - result.days += day_remainder; + if (!TryAddOperator::Operation(result.days, day_remainder, result.days)) { + return false; + } if (!TryCast::Operation(left.micros, d)) { return false; } diff --git a/src/duckdb/src/function/scalar/sequence/nextval.cpp b/src/duckdb/src/function/scalar/sequence/nextval.cpp index 6d7e0af5b..b8257b420 100644 --- a/src/duckdb/src/function/scalar/sequence/nextval.cpp +++ b/src/duckdb/src/function/scalar/sequence/nextval.cpp @@ -47,7 +47,12 @@ SequenceCatalogEntry &BindSequenceFromContext(ClientContext &context, const Qual } SequenceCatalogEntry &BindSequence(Binder &binder, const Identifier &name) { - return BindSequence(binder, QualifiedName::Parse(name.GetIdentifierName())); + QualifiedName qname; + string error; + if (!QualifiedName::TryParse(name.GetIdentifierName(), qname, error)) { + throw ParserException(error); + } + return BindSequence(binder, qname); } struct NextValLocalState : public FunctionLocalState { @@ -152,6 +157,16 @@ void NextValModifiedDatabases(ClientContext &context, FunctionModifiedDatabasesI input.properties.RegisterDBModify(seq.sequence.ParentCatalog(), context, DatabaseModificationType::SEQUENCE); } +//! currval reads the sequence but holds a reference to its catalog entry, so a prepared statement must be rebound +//! when the catalog changes (e.g. the sequence is dropped) without marking the database as modified +void CurrValReadDatabases(ClientContext &context, FunctionModifiedDatabasesInput &input) { + if (!input.bind_data) { + return; + } + auto &seq = input.bind_data->Cast(); + input.properties.RegisterDBRead(seq.sequence.ParentCatalog(), context); +} + } // namespace ScalarFunction NextvalFun::GetFunction() { @@ -176,6 +191,7 @@ ScalarFunction CurrvalFun::GetFunction() { curr_val.SetBindCallback(NextValBind); curr_val.SetSerializeCallback(Serialize); curr_val.SetDeserializeCallback(Deserialize); + curr_val.SetModifiedDatabasesCallback(CurrValReadDatabases); curr_val.SetInitStateCallback(NextValLocalFunction); curr_val.SetVolatile(); curr_val.SetFallible(); diff --git a/src/duckdb/src/function/scalar/strftime_format.cpp b/src/duckdb/src/function/scalar/strftime_format.cpp index 895c7f6bd..672224fac 100644 --- a/src/duckdb/src/function/scalar/strftime_format.cpp +++ b/src/duckdb/src/function/scalar/strftime_format.cpp @@ -41,13 +41,19 @@ static idx_t StrfTimepecifierSize(StrTimeSpecifier specifier) { return 3; case StrTimeSpecifier::DAY_OF_YEAR_PADDED: return 3; - case StrTimeSpecifier::YEAR_ISO: - return 4; default: return 0; } } +//! Years within [0, 9999] are padded to 4 digits, other years are written in full (with their sign) +static idx_t StrfTimeYearLength(int32_t year) { + if (0 <= year && year <= 9999) { + return 4; + } + return UnsafeNumericCast(NumericHelper::SignedLength(year)); +} + static void StrfTimeSplitOffset(int offset, int &hh, int &mm, int &ss) { hh = offset / Interval::SECS_PER_HOUR; offset = offset % Interval::SECS_PER_HOUR; @@ -86,15 +92,10 @@ idx_t StrfTimeFormat::GetSpecifierLength(StrTimeSpecifier specifier, date_t date return Date::DAY_NAMES[Date::ExtractISODayOfTheWeek(date) % 7].GetSize(); case StrTimeSpecifier::FULL_MONTH_NAME: return Date::MONTH_NAMES[data[1] - 1].GetSize(); - case StrTimeSpecifier::YEAR_DECIMAL: { - auto year = data[0]; - // Be consistent with WriteStandardSpecifier - if (0 <= year && year <= 9999) { - return 4; - } else { - return UnsafeNumericCast(NumericHelper::SignedLength(year)); - } - } + case StrTimeSpecifier::YEAR_DECIMAL: + return StrfTimeYearLength(data[0]); + case StrTimeSpecifier::YEAR_ISO: + return StrfTimeYearLength(Date::ExtractISOYearNumber(date)); case StrTimeSpecifier::MONTH_DECIMAL: { idx_t len = 1; auto month = data[1]; @@ -218,6 +219,21 @@ char *StrfTimeFormat::WritePadded3(char *target, uint32_t value) const { } } +// write a year padded to 4 digits if it is within [0, 9999], or in full (with its sign) otherwise +char *StrfTimeFormat::WriteYear(char *target, int32_t year) const { + if (year >= 0 && year <= 9999) { + return WritePadded(target, UnsafeNumericCast(year), 4); + } + auto abs_year = year < 0 ? -int64_t(year) : int64_t(year); + if (year < 0) { + *target = '-'; + target++; + } + auto len = NumericHelper::UnsignedLength(UnsafeNumericCast(abs_year)); + NumericHelper::FormatUnsigned(UnsafeNumericCast(abs_year), target + len); + return target + len; +} + // write a value in the range of 0..999999... padded to the given number of digits char *StrfTimeFormat::WritePadded(char *target, uint32_t value, size_t padding) const { D_ASSERT(padding > 1); @@ -292,7 +308,7 @@ char *StrfTimeFormat::WriteDateSpecifier(StrTimeSpecifier specifier, date_t date break; } case StrTimeSpecifier::YEAR_ISO: - target = WritePadded(target, UnsafeNumericCast(Date::ExtractISOYearNumber(date)), 4); + target = WriteYear(target, Date::ExtractISOYearNumber(date)); break; case StrTimeSpecifier::WEEKDAY_ISO: *target = char('0' + uint8_t(Date::ExtractISODayOfTheWeek(date))); @@ -326,19 +342,7 @@ char *StrfTimeFormat::WriteStandardSpecifier(StrTimeSpecifier specifier, int32_t target = WritePadded2(target, UnsafeNumericCast(AbsValue(data[0]) % 100)); break; case StrTimeSpecifier::YEAR_DECIMAL: - if (data[0] >= 0 && data[0] <= 9999) { - target = WritePadded(target, UnsafeNumericCast(data[0]), 4); - } else { - int32_t year = data[0]; - if (data[0] < 0) { - *target = '-'; - year = -year; - target++; - } - auto len = NumericHelper::UnsignedLength(UnsafeNumericCast(year)); - NumericHelper::FormatUnsigned(year, target + len); - target += len; - } + target = WriteYear(target, data[0]); break; case StrTimeSpecifier::HOUR_24_PADDED: { target = WritePadded2(target, UnsafeNumericCast(data[3])); @@ -895,7 +899,7 @@ bool StrpTimeFormat::Parse(const char *data, size_t size, ParseResult &result, b for (size_t l = 0; l < literal.size();) { // Match runs of spaces to runs of spaces. if (StringUtil::CharacterIsSpace(literal[l])) { - if (!StringUtil::CharacterIsSpace(data[pos])) { + if (pos >= size || !StringUtil::CharacterIsSpace(data[pos])) { error_message = "Space does not match, expected " + literals[i]; error_position = pos; return false; @@ -909,11 +913,13 @@ bool StrpTimeFormat::Parse(const char *data, size_t size, ParseResult &result, b continue; } // literal does not match - if (data[pos++] != literal[l++]) { + if (pos >= size || data[pos] != literal[l]) { error_message = "Literal does not match, expected " + literal; error_position = pos; return false; } + pos++; + l++; } if (i == specifiers.size()) { break; diff --git a/src/duckdb/src/function/scalar/string/caseconvert.cpp b/src/duckdb/src/function/scalar/string/caseconvert.cpp index a53b09b85..bfa4d210d 100644 --- a/src/duckdb/src/function/scalar/string/caseconvert.cpp +++ b/src/duckdb/src/function/scalar/string/caseconvert.cpp @@ -149,16 +149,13 @@ static unique_ptr CaseConvertPropagateStats(ClientContext &conte if (!StringStats::HasMinMax(child_stats[0])) { return result.ToUnique(); } - // When min == max, all values share the stored string (exact) or the stored prefix (truncated). - // Case conversion is codepoint-local, so the converted string/prefix bounds the result. + // All values share the common prefix of min and max; case conversion preserves this property. auto min = StringStats::Min(child_stats[0]); auto max = StringStats::Max(child_stats[0]); - if (min != max) { - return result.ToUnique(); - } - const bool is_exact = StringStats::GetMinType(child_stats[0]) == StringStatsType::EXACT_STATS && + const bool is_exact = min == max && StringStats::GetMinType(child_stats[0]) == StringStatsType::EXACT_STATS && StringStats::GetMaxType(child_stats[0]) == StringStatsType::EXACT_STATS; if (!is_exact) { + min.resize(StringUtil::GetCommonPrefixSize(min, max)); // truncated stats can end in the middle of a character - only complete ones can be converted size_t invalid_pos = 0; if (Utf8Proc::Analyze(min.c_str(), min.size(), nullptr, &invalid_pos) == UnicodeType::INVALID) { diff --git a/src/duckdb/src/function/scalar/string/concat.cpp b/src/duckdb/src/function/scalar/string/concat.cpp index 883806dfd..f2b194067 100644 --- a/src/duckdb/src/function/scalar/string/concat.cpp +++ b/src/duckdb/src/function/scalar/string/concat.cpp @@ -212,12 +212,14 @@ void SetArgumentType(BoundScalarFunction &bound_function, const LogicalType &typ bound_function.SetReturnType(type); } -unique_ptr BindListConcat(ClientContext &context, BoundScalarFunction &bound_function, - vector> &arguments, bool is_operator) { +void ResolveListConcatTypes(ResolveScalarFunctionTypesInput &input, bool is_operator) { + auto &context = input.GetClientContext(); + auto &bound_function = input.GetBoundFunction(); LogicalType child_type = LogicalType::SQLNULL; bool all_null = true; - for (auto &arg : arguments) { - auto &return_type = arg->GetReturnType(); + for (idx_t i = 0; i < input.GetArgumentCount(); i++) { + auto &arg = input.GetArgument(i); + auto &return_type = arg.GetReturnType(); if (return_type == LogicalTypeId::SQLNULL) { // we mimic postgres behaviour: list_concat(NULL, my_list) = my_list continue; @@ -235,95 +237,93 @@ unique_ptr BindListConcat(ClientContext &context, BoundScalarFunct break; default: { string type_list; - for (idx_t arg_idx = 0; arg_idx < arguments.size(); arg_idx++) { + for (idx_t arg_idx = 0; arg_idx < input.GetArgumentCount(); arg_idx++) { if (!type_list.empty()) { - if (arg_idx + 1 == arguments.size()) { + if (arg_idx + 1 == input.GetArgumentCount()) { // last argument type_list += " and "; } else { type_list += ", "; } } - type_list += arguments[arg_idx]->GetReturnType().ToString(); + type_list += input.GetArgumentType(arg_idx).ToString(); } - throw BinderException(*arg, "Cannot concatenate types %s - an explicit cast is required", type_list); + throw BinderException(arg, "Cannot concatenate types %s - an explicit cast is required", type_list); } } if (!LogicalType::TryGetMaxLogicalType(context, child_type, next_type, child_type)) { - throw BinderException(*arg, - "Cannot concatenate lists of types %s[] and %s[] - an explicit cast is required", + throw BinderException(arg, "Cannot concatenate lists of types %s[] and %s[] - an explicit cast is required", child_type.ToString(), next_type.ToString()); } } if (all_null) { // all arguments are NULL SetArgumentType(bound_function, LogicalTypeId::SQLNULL, is_operator); - return make_uniq(bound_function.GetReturnType(), is_operator); + return; } - auto list_type = LogicalType::LIST(child_type); - - SetArgumentType(bound_function, list_type, is_operator); - return make_uniq(bound_function.GetReturnType(), is_operator); + SetArgumentType(bound_function, LogicalType::LIST(child_type), is_operator); } -unique_ptr BindConcatFunctionInternal(ClientContext &context, BoundScalarFunction &bound_function, - vector> &arguments, bool is_operator) { +void ResolveConcatTypesInternal(ResolveScalarFunctionTypesInput &input, bool is_operator) { + auto &bound_function = input.GetBoundFunction(); bool list_concat = false; bool all_null = true; // blob concat is only supported for the concat operator - regular concat converts to varchar bool all_blob = is_operator ? true : false; - for (auto &arg : arguments) { - if (arg->GetReturnType().id() == LogicalTypeId::UNKNOWN) { + for (auto &arg_type : input.GetArgumentTypes()) { + if (arg_type.id() == LogicalTypeId::UNKNOWN) { throw ParameterNotResolvedException(); } - if (arg->GetReturnType().id() == LogicalTypeId::LIST || arg->GetReturnType().id() == LogicalTypeId::ARRAY) { + if (arg_type.id() == LogicalTypeId::LIST || arg_type.id() == LogicalTypeId::ARRAY) { list_concat = true; } - if (arg->GetReturnType().id() != LogicalTypeId::BLOB) { + if (arg_type.id() != LogicalTypeId::BLOB) { all_blob = false; } - if (arg->GetReturnType().id() != LogicalTypeId::SQLNULL) { + if (arg_type.id() != LogicalTypeId::SQLNULL) { all_null = false; } } if (list_concat) { - return BindListConcat(context, bound_function, arguments, is_operator); + ResolveListConcatTypes(input, is_operator); + return; } if (all_null) { if (is_operator) { SetArgumentType(bound_function, LogicalTypeId::SQLNULL, is_operator); - return make_uniq(bound_function.GetReturnType(), is_operator); + return; } const auto &func_args = bound_function.GetArguments(); if (!func_args.empty() && (func_args[0].id() == LogicalTypeId::LIST || func_args[0].id() == LogicalTypeId::ARRAY)) { SetArgumentType(bound_function, LogicalTypeId::SQLNULL, is_operator); - return make_uniq(bound_function.GetReturnType(), is_operator); + return; } SetArgumentType(bound_function, LogicalTypeId::VARCHAR, is_operator); - return make_uniq(bound_function.GetReturnType(), is_operator); + return; } auto return_type = all_blob ? LogicalType::BLOB : LogicalType::VARCHAR; // we can now assume that the input is a string or castable to a string SetArgumentType(bound_function, return_type, is_operator); - return make_uniq(bound_function.GetReturnType(), is_operator); +} + +void ConcatFunctionResolveTypes(ResolveScalarFunctionTypesInput &input) { + ResolveConcatTypesInternal(input, false); +} + +void ConcatOperatorResolveTypes(ResolveScalarFunctionTypesInput &input) { + ResolveConcatTypesInternal(input, true); } unique_ptr BindConcatFunction(BindScalarFunctionInput &input) { - auto &context = input.GetClientContext(); - auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - return BindConcatFunctionInternal(context, bound_function, arguments, false); + return make_uniq(input.GetBoundFunction().GetReturnType(), false); } unique_ptr BindConcatOperator(BindScalarFunctionInput &input) { - auto &context = input.GetClientContext(); - auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - return BindConcatFunctionInternal(context, bound_function, arguments, true); + return make_uniq(input.GetBoundFunction().GetReturnType(), true); } unique_ptr ListConcatStats(ClientContext &context, FunctionStatisticsInput &input) { @@ -341,7 +341,8 @@ ScalarFunction ListConcatFun::GetFunction() { // The arguments and return types are set in the binder function. auto fun = ScalarFunction({}, LogicalType::LIST(LogicalType::ANY), ConcatFunction, BindConcatFunction, ListConcatStats); - fun.SetVarArgs(LogicalType::LIST(LogicalType::ANY)); + fun.GetSignature().AddArgs("args", LogicalType::LIST(LogicalType::ANY)); + fun.SetResolveTypesCallback(ConcatFunctionResolveTypes); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); return fun; } @@ -357,7 +358,8 @@ ScalarFunction ListConcatFun::GetFunction() { ScalarFunction ConcatFun::GetFunction() { ScalarFunction concat = ScalarFunction("concat", {}, LogicalType::ANY, ConcatFunction, BindConcatFunction); concat.GetSignature().AddParameter("value", LogicalType::ANY); - concat.SetVarArgs(LogicalType::ANY); + concat.GetSignature().AddArgs("args", LogicalType::ANY); + concat.SetResolveTypesCallback(ConcatFunctionResolveTypes); concat.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); return concat; } @@ -365,6 +367,7 @@ ScalarFunction ConcatFun::GetFunction() { ScalarFunction ConcatOperatorFun::GetFunction() { ScalarFunction concat_op = ScalarFunction("||", {}, LogicalType::ANY, ConcatFunction, BindConcatOperator); concat_op.GetSignature().AddParameter("arg1", LogicalType::ANY).AddParameter("arg2", LogicalType::ANY); + concat_op.SetResolveTypesCallback(ConcatOperatorResolveTypes); return concat_op; } diff --git a/src/duckdb/src/function/scalar/string/concat_ws.cpp b/src/duckdb/src/function/scalar/string/concat_ws.cpp index 0dcc8049d..f19c923a8 100644 --- a/src/duckdb/src/function/scalar/string/concat_ws.cpp +++ b/src/duckdb/src/function/scalar/string/concat_ws.cpp @@ -170,7 +170,7 @@ ScalarFunction ConcatWsFun::GetFunction() { ScalarFunction concat_ws = ScalarFunction("concat_ws", {}, LogicalType::VARCHAR, ConcatWSFunction, BindConcatWSFunction); concat_ws.GetSignature().AddParameter("separator", LogicalType::VARCHAR).AddParameter("string", LogicalType::ANY); - concat_ws.SetVarArgs(LogicalType::ANY); + concat_ws.GetSignature().AddArgs("args", LogicalType::ANY); concat_ws.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); return ScalarFunction(concat_ws); } diff --git a/src/duckdb/src/function/scalar/string/contains.cpp b/src/duckdb/src/function/scalar/string/contains.cpp index a980ce1fc..dece4681e 100644 --- a/src/duckdb/src/function/scalar/string/contains.cpp +++ b/src/duckdb/src/function/scalar/string/contains.cpp @@ -32,13 +32,6 @@ struct ContainsUnaligned { } }; -struct ContainsGeneric { - template - static bool MatchRemainder(const unsigned char *haystack, const unsigned char *needle, idx_t needle_size) { - return memcmp(haystack + sizeof(UNSIGNED), needle + sizeof(UNSIGNED), needle_size - sizeof(UNSIGNED)) == 0; - } -}; - template static idx_t Contains(const unsigned char *haystack, idx_t haystack_size, const unsigned char *needle, idx_t needle_size, idx_t base_offset) { @@ -67,6 +60,66 @@ static idx_t Contains(const unsigned char *haystack, idx_t haystack_size, const return DConstants::INVALID_INDEX; } +//! Linear-time (Knuth-Morris-Pratt) search, used when the needle keeps almost-matching the haystack +static idx_t ContainsLinear(const unsigned char *haystack, idx_t haystack_size, const unsigned char *needle, + idx_t needle_size, idx_t base_offset) { + // prefix[i] is the length of the longest proper prefix of needle[0..i] that is also a suffix of it + vector prefix(needle_size, 0); + for (idx_t i = 1, k = 0; i < needle_size; i++) { + while (k > 0 && needle[i] != needle[k]) { + k = prefix[k - 1]; + } + if (needle[i] == needle[k]) { + k++; + } + prefix[i] = k; + } + for (idx_t i = 0, k = 0; i < haystack_size; i++) { + while (k > 0 && haystack[i] != needle[k]) { + k = prefix[k - 1]; + } + if (haystack[i] == needle[k]) { + k++; + } + if (k == needle_size) { + return base_offset + i + 1 - needle_size; + } + } + return DConstants::INVALID_INDEX; +} + +//! Like Contains, but every candidate position compares up to needle_size bytes, which is +//! quadratic when the needle keeps almost-matching - switch to a linear-time search once that happens +static idx_t ContainsLong(const unsigned char *haystack, idx_t haystack_size, const unsigned char *needle, + idx_t needle_size, idx_t base_offset) { + if (needle_size > haystack_size) { + return DConstants::INVALID_INDEX; + } + const idx_t candidate_end = haystack_size - (needle_size - 1); + auto needle_entry = Load(needle); + // the number of candidates whose remainder we compare before switching, roughly linear in the haystack + const idx_t max_full_compares = 16 + (haystack_size * 8) / needle_size; + idx_t full_compares = 0; + for (idx_t offset = 0; offset < candidate_end; offset++) { + auto location = + static_cast(memchr(haystack + offset, needle[0], candidate_end - offset)); + if (!location) { + return DConstants::INVALID_INDEX; + } + offset = UnsafeNumericCast(location - haystack); + if (Load(location) != needle_entry) { + continue; + } + if (memcmp(location + sizeof(uint64_t), needle + sizeof(uint64_t), needle_size - sizeof(uint64_t)) == 0) { + return base_offset + offset; + } + if (++full_compares > max_full_compares) { + return ContainsLinear(location, haystack_size - offset, needle, needle_size, base_offset + offset); + } + } + return DConstants::INVALID_INDEX; +} + struct ContainsOperator { template static inline TR Operation(TA left, TB right) { @@ -105,7 +158,7 @@ idx_t FindStrInStr(const unsigned char *haystack, idx_t haystack_size, const uns case 8: return Contains(haystack, haystack_size, needle, 8, base_offset); default: - return Contains(haystack, haystack_size, needle, needle_size, base_offset); + return ContainsLong(haystack, haystack_size, needle, needle_size, base_offset); } } diff --git a/src/duckdb/src/function/scalar/string/hive_partition_component.cpp b/src/duckdb/src/function/scalar/string/hive_partition_component.cpp new file mode 100644 index 000000000..4c3aac3b5 --- /dev/null +++ b/src/duckdb/src/function/scalar/string/hive_partition_component.cpp @@ -0,0 +1,33 @@ +#include "duckdb/function/scalar/string_functions.hpp" + +#include "duckdb/common/hive_partitioning.hpp" +#include "duckdb/common/vector/vector_writer.hpp" + +namespace duckdb { + +namespace { + +void HivePartitionComponentFunction(DataChunk &args, ExpressionState &state, Vector &result) { + // type-erased, as the partition value can have any type + auto writer = FlatVector::Writer(result, args.size()); + for (idx_t row = 0; row < args.size(); row++) { + auto value = args.data[1].GetValue(row); + // the name and the value are escaped the same way url_encode does + auto component = HivePartitioning::Escape(args.data[0].GetValue(row).ToString()) + "="; + component += + value.IsNull() ? HivePartitioning::DEFAULT_PARTITION_NAME : HivePartitioning::EscapeValue(value.ToString()); + writer.WriteValue(string_t(component.c_str(), UnsafeNumericCast(component.size()))); + } +} + +} // namespace + +ScalarFunction HivePartitionComponentFun::GetFunction() { + ScalarFunction component(HivePartitionComponentFun::Name, {}, LogicalType::VARCHAR, HivePartitionComponentFunction); + component.GetSignature().AddParameter("name", LogicalType::VARCHAR).AddParameter("value", LogicalType::ANY); + // a NULL partition value has a directory of its own + component.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); + return component; +} + +} // namespace duckdb diff --git a/src/duckdb/src/function/scalar/string/length.cpp b/src/duckdb/src/function/scalar/string/length.cpp index abb1be5e5..e1e3e440e 100644 --- a/src/duckdb/src/function/scalar/string/length.cpp +++ b/src/duckdb/src/function/scalar/string/length.cpp @@ -1,6 +1,8 @@ #include "duckdb/common/exception.hpp" #include "duckdb/common/numeric_utils.hpp" #include "duckdb/common/types/bit.hpp" +#include "duckdb/common/vector/vector_iterator.hpp" +#include "duckdb/common/vector/vector_writer.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" #include "duckdb/storage/statistics/base_statistics.hpp" #include "duckdb/storage/statistics/numeric_stats.hpp" @@ -161,14 +163,14 @@ void ArrayLengthFunction(DataChunk &args, ExpressionState &state, Vector &result } } -unique_ptr ArrayOrListLengthBind(BindScalarFunctionInput &input) { +void ArrayOrListLengthResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - if (arguments[0]->HasParameter() || arguments[0]->GetReturnType().id() == LogicalTypeId::UNKNOWN) { + auto &argument = input.GetArgument(0); + if (argument.HasParameter() || argument.GetReturnType().id() == LogicalTypeId::UNKNOWN) { throw ParameterNotResolvedException(); } - const auto &arg_type = arguments[0]->GetReturnType().id(); + const auto &arg_type = argument.GetReturnType().id(); if (arg_type == LogicalTypeId::ARRAY) { bound_function.SetFunctionCallback(ArrayLengthFunction); } else if (arg_type == LogicalTypeId::LIST) { @@ -177,8 +179,7 @@ unique_ptr ArrayOrListLengthBind(BindScalarFunctionInput &input) { // Unreachable throw BinderException("length can only be used on arrays or lists"); } - bound_function.GetArguments()[0] = arguments[0]->GetReturnType(); - return nullptr; + bound_function.GetArguments()[0] = argument.GetReturnType(); } //------------------------------------------------------------------ @@ -221,26 +222,47 @@ void ArrayLengthBinaryFunction(DataChunk &args, ExpressionState &state, Vector & auto &dimensions = data.dimensions; auto max_dimension = static_cast(dimensions.size()); - UnaryExecutor::Execute(dimension, result, [&](int64_t dimension) { - if (dimension < 1 || dimension > max_dimension) { + // the result is NULL for NULL arrays + const auto count = args.size(); + auto array_validity = args.data[0].Validity(); + auto dimension_values = dimension.Values(); + auto writer = FlatVector::Writer(result, count); + for (idx_t i = 0; i < count; i++) { + auto entry = dimension_values[i]; + if (!array_validity.IsValid(i) || !entry.IsValid()) { + writer.WriteNull(); + continue; + } + auto dim = entry.GetValue(); + if (dim < 1 || dim > max_dimension) { throw OutOfRangeException(StringUtil::Format( - "array_length dimension '%lld' out of range (min: '1', max: '%lld')", dimension, max_dimension)); + "array_length dimension '%lld' out of range (min: '1', max: '%lld')", dim, max_dimension)); } - return dimensions[UnsafeNumericCast(dimension - 1)]; - }); + writer.WriteValue(dimensions[UnsafeNumericCast(dim - 1)]); + } } -unique_ptr ArrayOrListLengthBinaryBind(BindScalarFunctionInput &input) { +void ArrayOrListLengthBinaryResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - if (arguments[0]->HasParameter() || arguments[0]->GetReturnType().id() == LogicalTypeId::UNKNOWN) { + auto &argument = input.GetArgument(0); + if (argument.HasParameter() || argument.GetReturnType().id() == LogicalTypeId::UNKNOWN) { throw ParameterNotResolvedException(); } - auto type = arguments[0]->GetReturnType(); + auto &type = argument.GetReturnType(); if (type.id() == LogicalTypeId::ARRAY) { - bound_function.GetArguments()[0] = type; bound_function.SetFunctionCallback(ArrayLengthBinaryFunction); + } else if (type.id() == LogicalTypeId::LIST) { + bound_function.SetFunctionCallback(ListLengthBinaryFunction); + } else { + // Unreachable + throw BinderException("array_length can only be used on arrays or lists"); + } + bound_function.GetArguments()[0] = type; +} +unique_ptr ArrayOrListLengthBinaryBind(BindScalarFunctionInput &input) { + auto type = input.GetBoundFunction().GetArguments()[0]; + if (type.id() == LogicalTypeId::ARRAY) { // If the input is an array, the dimensions are constant, so we can calculate them at bind time vector dimensions; while (true) { @@ -254,15 +276,8 @@ unique_ptr ArrayOrListLengthBinaryBind(BindScalarFunctionInput &in auto data = make_uniq(); data->dimensions = dimensions; return std::move(data); - - } else if (type.id() == LogicalTypeId::LIST) { - bound_function.SetFunctionCallback(ListLengthBinaryFunction); - bound_function.GetArguments()[0] = type; - return nullptr; - } else { - // Unreachable - throw BinderException("array_length can only be used on arrays or lists"); } + return nullptr; } } // namespace @@ -281,7 +296,8 @@ ScalarFunctionSet LengthFun::GetFunctions() { bit_fun.GetSignature().AddParameter("bit", LogicalType::BIT); length.AddFunction(bit_fun); - ScalarFunction list_fun({}, LogicalType::BIGINT, nullptr, ArrayOrListLengthBind); + ScalarFunction list_fun({}, LogicalType::BIGINT, nullptr); + list_fun.SetResolveTypesCallback(ArrayOrListLengthResolveTypes); list_fun.GetSignature().AddParameter("list", LogicalType::LIST(LogicalType::ANY)); length.AddFunction(list_fun); @@ -300,7 +316,8 @@ ScalarFunctionSet LengthGraphemeFun::GetFunctions() { ScalarFunctionSet ArrayLengthFun::GetFunctions() { ScalarFunctionSet array_length("array_length"); - ScalarFunction unary({}, LogicalType::BIGINT, nullptr, ArrayOrListLengthBind); + ScalarFunction unary({}, LogicalType::BIGINT, nullptr); + unary.SetResolveTypesCallback(ArrayOrListLengthResolveTypes); unary.GetSignature().AddParameter("list", LogicalType::LIST(LogicalType::ANY)); array_length.AddFunction(unary); @@ -308,6 +325,7 @@ ScalarFunctionSet ArrayLengthFun::GetFunctions() { binary.GetSignature() .AddParameter("list", LogicalType::LIST(LogicalType::ANY)) .AddParameter("dimension", LogicalType::BIGINT); + binary.SetResolveTypesCallback(ArrayOrListLengthBinaryResolveTypes); array_length.AddFunction(binary); array_length.SetFallible(); diff --git a/src/duckdb/src/function/scalar/string/like.cpp b/src/duckdb/src/function/scalar/string/like.cpp index 8dc2c6f3c..6ffc841de 100644 --- a/src/duckdb/src/function/scalar/string/like.cpp +++ b/src/duckdb/src/function/scalar/string/like.cpp @@ -182,40 +182,50 @@ template ( - sdata + sidx, slen - sidx, pdata + pidx, plen - pidx, escape)) { - return true; + READER::NextCharacter(sdata, slen, sidx); + continue; + } else if (pchar == PERCENTAGE) { + pidx++; + while (pidx < plen && pdata[pidx] == PERCENTAGE) { + pidx++; } + if (pidx == plen) { + return true; /* tail is acceptable */ + } + has_percentage = true; + retry_pidx = pidx; + retry_sidx = sidx; + continue; + } else if (pchar == schar) { + pidx++; + sidx++; + continue; } - return false; - } else if (pchar == schar) { - sidx++; - } else { + } + if (!has_percentage) { return false; } + pidx = retry_pidx; + sidx = ++retry_sidx; } // a trailing '%' only matches an empty suffix when it is not escaped while (pidx < plen) { @@ -444,7 +454,8 @@ bool ILikeOperatorFunctionInternal(string_t &str, string_t &pattern, char escape if (!HAS_ESCAPE) { return LikeOperatorFunction(str_lcase, pat_lcase); } - return LikeOperatorFunction(str_lcase, pat_lcase, escape); + // the pattern is lowercased, so the escape character must be as well + return LikeOperatorFunction(str_lcase, pat_lcase, StringUtil::CharacterToLower(escape)); } bool ILikeOperatorFunction(string_t &str, string_t &pattern) { @@ -540,7 +551,8 @@ void ILikeEscapeFunction(DataChunk &args, ExpressionState &state, Vector &result escape_vec.GetVectorType() == VectorType::CONSTANT_VECTOR && !ConstantVector::IsNull(escape_vec)) { auto pattern = *ConstantVector::GetData(pattern_vec); auto escape = *ConstantVector::GetData(escape_vec); - char escape_char = GetEscapeChar(escape); + // the pattern is lowercased, so the escape character must be as well + char escape_char = StringUtil::CharacterToLower(GetEscapeChar(escape)); bool has_escape = escape.GetSize() != 0; // lowercase the pattern exactly once, up front diff --git a/src/duckdb/src/function/scalar/string/nfc_normalize.cpp b/src/duckdb/src/function/scalar/string/nfc_normalize.cpp index 7677a3b7d..4858dd8ed 100644 --- a/src/duckdb/src/function/scalar/string/nfc_normalize.cpp +++ b/src/duckdb/src/function/scalar/string/nfc_normalize.cpp @@ -15,7 +15,10 @@ struct NFCNormalizeOperator { return input; } auto normalized_str = Utf8Proc::Normalize(input_data, input_length); - D_ASSERT(normalized_str); + if (!normalized_str) { + // invalid UTF-8 + return input; + } auto result_str = heap.AddString(normalized_str); free(normalized_str); return result_str; diff --git a/src/duckdb/src/function/scalar/string/path_join.cpp b/src/duckdb/src/function/scalar/string/path_join.cpp index 08c3e91a3..3790786de 100644 --- a/src/duckdb/src/function/scalar/string/path_join.cpp +++ b/src/duckdb/src/function/scalar/string/path_join.cpp @@ -69,7 +69,7 @@ void PathJoinFunction(DataChunk &args, ExpressionState &state, Vector &result) { ScalarFunction PathJoinFun::GetFunction() { ScalarFunction path_join(PathJoinFun::Name, {}, LogicalType::VARCHAR, PathJoinFunction); path_join.GetSignature().AddParameter("path", LogicalType::VARCHAR); - path_join.SetVarArgs(LogicalType::VARCHAR); + path_join.GetSignature().AddArgs("args", LogicalType::VARCHAR); path_join.SetNullHandling(FunctionNullHandling::DEFAULT_NULL_HANDLING); // throws if the paths that are joined are incompatible path_join.SetFallible(); diff --git a/src/duckdb/src/function/scalar/string/regexp.cpp b/src/duckdb/src/function/scalar/string/regexp.cpp index 963ac57a1..8143e451b 100644 --- a/src/duckdb/src/function/scalar/string/regexp.cpp +++ b/src/duckdb/src/function/scalar/string/regexp.cpp @@ -21,6 +21,7 @@ namespace duckdb { using regexp_util::CreateStringPiece; using regexp_util::ParseRegexOptions; using regexp_util::TryParseConstantPattern; +using regexp_util::VerifyUTF8Result; // Zero-copy slice of capture group `group_index` from `input`'s buffer; caller must call // StringVector::AddHeapReference(result, source) first. @@ -33,7 +34,9 @@ static inline string_t ExtractCaptureGroup(const string_t &input, const RE2 &re, duckdb_re2::StringPiece submatch[10]; const int nsubmatch = group_index + 1; if (re.Match(in_piece, 0, in_piece.size(), duckdb_re2::RE2::UNANCHORED, submatch, nsubmatch)) { - return string_t(submatch[group_index].data(), UnsafeNumericCast(submatch[group_index].size())); + auto &group = submatch[group_index]; + VerifyUTF8Result(group.data(), group.size()); + return string_t(group.data(), UnsafeNumericCast(group.size())); } return no_match_returns_input ? input : string_t(nullptr, 0); } @@ -207,6 +210,7 @@ static void RegexReplaceFunction(DataChunk &args, ExpressionState &state, Vector } else { RE2::Replace(&sstring, lstate.constant_pattern, replace_piece); } + VerifyUTF8Result(sstring.c_str(), sstring.size()); return heap.AddString(sstring); }); } else { @@ -227,6 +231,7 @@ static void RegexReplaceFunction(DataChunk &args, ExpressionState &state, Vector } else { RE2::Replace(&sstring, re, replace_piece); } + VerifyUTF8Result(sstring.c_str(), sstring.size()); return heap.AddString(sstring); }); } @@ -276,6 +281,9 @@ static void RegexExtractFunction(DataChunk &args, ExpressionState &state, Vector BinaryExecutor::Execute( strings, patterns, result, [&](string_t input, string_t pattern) { RE2 re(CreateStringPiece(pattern), info.options); + if (!re.ok()) { + throw InvalidInputException(re.error()); + } return ExtractCaptureGroup(input, re, info.group_index, info.no_match_returns_input); }); } @@ -325,6 +333,9 @@ static void RegexExtractStructFunction(DataChunk &args, ExpressionState &state, auto &child_entry = child_entries[col]; ConstantVector::SetNull(child_entry, false); auto &extracted = ws[col]; + if (match) { + VerifyUTF8Result(extracted.data(), extracted.size()); + } auto cdata = ConstantVector::GetData(child_entry); cdata[0] = string_t(extracted.data(), UnsafeNumericCast(match ? extracted.size() : 0)); } @@ -351,6 +362,9 @@ static void RegexExtractStructFunction(DataChunk &args, ExpressionState &state, auto &child_entry = child_entries[col]; auto cdata = FlatVector::GetDataMutable(child_entry); auto &extracted = ws[col]; + if (match) { + VerifyUTF8Result(extracted.data(), extracted.size()); + } cdata[entry.GetIndex()] = string_t(extracted.data(), UnsafeNumericCast(match ? extracted.size() : 0)); } @@ -401,6 +415,15 @@ static unique_ptr RegexExtractBind(BindScalarFunctionInput &input) } } + if (constant_pattern && constant_string.find("\\C") == string::npos) { + auto validation_options = options; + validation_options.set_log_errors(false); + if (RE2(constant_string, validation_options).ok()) { + // A valid UTF-8 pattern without byte escapes cannot return a partial character. + bound_function.SetErrorMode(FunctionErrors::CANNOT_ERROR); + } + } + return make_uniq(options, std::move(constant_string), constant_pattern, group_index, no_match_returns_input); } @@ -488,6 +511,8 @@ ScalarFunctionSet RegexpExtractFun::GetFunctions() { {"options", LogicalType::VARCHAR}}, LogicalType::VARCHAR, RegexExtractStructFunction, RegexExtractBind, RegexInitLocalState)); + // throws when \C splits a character + regexp_extract.SetFallible(); return (regexp_extract); } diff --git a/src/duckdb/src/function/scalar/string/regexp/regexp_extract_all.cpp b/src/duckdb/src/function/scalar/string/regexp/regexp_extract_all.cpp index 8794a511d..6d81c6ae8 100644 --- a/src/duckdb/src/function/scalar/string/regexp/regexp_extract_all.cpp +++ b/src/duckdb/src/function/scalar/string/regexp/regexp_extract_all.cpp @@ -44,12 +44,11 @@ bool ExtractAll(duckdb_re2::StringPiece &input, duckdb_re2::RE2 &pattern, idx_t if (!pattern.Match(input, *startpos, input.size(), pattern.UNANCHORED, groups, ngroups + 1)) { return false; } - idx_t consumed = static_cast(groups[0].end() - (input.begin() + *startpos)); - if (!consumed) { - // Empty match: advance exactly one UTF-8 codepoint - consumed = regexp_util::AdvanceOneUTF8Basic(input, *startpos); + *startpos = static_cast(groups[0].end() - input.begin()); + if (groups[0].empty()) { + // Empty match: advance exactly one UTF-8 codepoint past it + *startpos += regexp_util::AdvanceOneUTF8Basic(input, *startpos); } - *startpos += consumed; return true; } @@ -172,6 +171,7 @@ void RegexpExtractAll::Execute(DataChunk &args, ExpressionState &state, Vector & child_writer.WriteStringRef(string_t(string_val.GetData(), 0)); } } else { + regexp_util::VerifyUTF8Result(match_group.data(), match_group.size()); // Every group is a substring of the original, we can find the offset via pointer D_ASSERT(const_char_ptr_cast(match_group.begin()) >= string_val.GetData()); auto offset = UnsafeNumericCast(match_group.begin() - string_val.GetData()); @@ -188,11 +188,10 @@ static inline bool ExtractAllStruct(duckdb_re2::StringPiece &input, duckdb_re2:: if (!re.Match(input, startpos, input.size(), re.UNANCHORED, groups, provided_groups + 1)) { return false; } - idx_t consumed = static_cast(groups[0].end() - (input.begin() + startpos)); - if (!consumed) { - consumed = regexp_util::AdvanceOneUTF8Basic(input, startpos); + startpos = static_cast(groups[0].end() - input.begin()); + if (groups[0].empty()) { + startpos += regexp_util::AdvanceOneUTF8Basic(input, startpos); } - startpos += consumed; return true; } @@ -224,6 +223,7 @@ static list_entry_t ExtractStructAllSingleTuple(const string_t &string_val, duck } cdata[current_list_size] = string_t(string_val.GetData(), 0); } else { + regexp_util::VerifyUTF8Result(span.data(), span.size()); auto offset = span.begin() - string_val.GetData(); cdata[current_list_size] = string_t(string_val.GetData() + offset, UnsafeNumericCast(span.size())); diff --git a/src/duckdb/src/function/scalar/string/regexp/regexp_util.cpp b/src/duckdb/src/function/scalar/string/regexp/regexp_util.cpp index 1dabf68c3..04db81e3e 100644 --- a/src/duckdb/src/function/scalar/string/regexp/regexp_util.cpp +++ b/src/duckdb/src/function/scalar/string/regexp/regexp_util.cpp @@ -2,6 +2,7 @@ #include "duckdb/execution/expression_executor.hpp" #include "re2/re2.h" #include "re2/stringpiece.h" +#include "utf8proc_wrapper.hpp" namespace duckdb { @@ -95,6 +96,9 @@ void ParseGroupNameList(const string &function_name, const Value &list_val, cons throw BinderException("NULL group name in %s", function_name); } auto name = child.ToString(); + if (name.empty()) { + throw BinderException("Empty group name in %s", function_name); + } if (name_set.find(Identifier(name)) != name_set.end()) { throw BinderException("Duplicate group name '%s' in %s", name, function_name); } @@ -133,15 +137,21 @@ idx_t AdvanceOneUTF8Basic(const duckdb_re2::StringPiece &input, idx_t base) { } else if ((first & 0xF8) == 0xF0) { char_len = 4; } else { - // This should be impossible since RE2 operates on codepoints - throw InternalException("Invalid UTF-8 lead byte in regexp_extract_all"); + // \C can leave us in the middle of a character + return 1; } if (base + char_len > input.length()) { - throw InternalException("Invalid UTF-8 sequence in regexp_extract_all"); + return 1; } return char_len; } +void VerifyUTF8Result(const char *data, idx_t size) { + if (!Utf8Proc::IsValid(data, size)) { + throw InvalidInputException("Regular expression result is not valid UTF-8 - \\C matched part of a character"); + } +} + } // namespace regexp_util } // namespace duckdb diff --git a/src/duckdb/src/function/scalar/string/string_split.cpp b/src/duckdb/src/function/scalar/string/string_split.cpp index b41d932fb..388375a56 100644 --- a/src/duckdb/src/function/scalar/string/string_split.cpp +++ b/src/duckdb/src/function/scalar/string/string_split.cpp @@ -14,23 +14,35 @@ namespace duckdb { namespace { struct RegularStringSplit { - static idx_t Find(const char *input_data, idx_t input_size, const char *delim_data, idx_t delim_size, + static constexpr bool VERIFY_UTF8 = false; + + static idx_t Find(const char *input_data, idx_t input_size, idx_t start, const char *delim_data, idx_t delim_size, idx_t &match_size, void *data) { match_size = delim_size; if (delim_size == 0) { - return 0; + return start; + } + auto pos = FindStrInStr(const_uchar_ptr_cast(input_data + start), input_size - start, + const_uchar_ptr_cast(delim_data), delim_size); + if (pos == DConstants::INVALID_INDEX) { + return pos; } - return FindStrInStr(const_uchar_ptr_cast(input_data), input_size, const_uchar_ptr_cast(delim_data), delim_size); + return start + pos; } }; -struct ConstantRegexpStringSplit { - static idx_t Find(const char *input_data, idx_t input_size, const char *delim_data, idx_t delim_size, +struct RegexpStringSplit { + //! \C can split a multi-byte character + static constexpr bool VERIFY_UTF8 = true; + + static idx_t Find(const char *input_data, idx_t input_size, idx_t start, const char *delim_data, idx_t delim_size, idx_t &match_size, void *data) { D_ASSERT(data); auto regex = reinterpret_cast(data); duckdb_re2::StringPiece match; - if (!regex->Match(duckdb_re2::StringPiece(input_data, input_size), 0, input_size, RE2::UNANCHORED, &match, 1)) { + // match against the full input so that anchors and assertions see the original context + if (!regex->Match(duckdb_re2::StringPiece(input_data, input_size), start, input_size, RE2::UNANCHORED, &match, + 1)) { return DConstants::INVALID_INDEX; } match_size = match.size(); @@ -38,17 +50,6 @@ struct ConstantRegexpStringSplit { } }; -struct RegexpStringSplit { - static idx_t Find(const char *input_data, idx_t input_size, const char *delim_data, idx_t delim_size, - idx_t &match_size, void *data) { - duckdb_re2::RE2 regex(duckdb_re2::StringPiece(delim_data, delim_size)); - if (!regex.ok()) { - throw InvalidInputException(regex.error()); - } - return ConstantRegexpStringSplit::Find(input_data, input_size, delim_data, delim_size, match_size, ®ex); - } -}; - struct StringSplitter { template static idx_t Split(string_t input, string_t delim, void *data, CB &&emit) { @@ -57,14 +58,15 @@ struct StringSplitter { auto delim_data = delim.GetData(); auto delim_size = delim.GetSize(); idx_t list_idx = 0; - while (input_size > 0) { + idx_t start = 0; + while (start < input_size) { idx_t match_size = 0; - auto pos = OP::Find(input_data, input_size, delim_data, delim_size, match_size, data); + auto pos = OP::Find(input_data, input_size, start, delim_data, delim_size, match_size, data); if (pos > input_size) { break; } - if (match_size == 0 && pos == 0) { - // special case: 0 length match and pos is 0 + if (match_size == 0 && pos == start) { + // special case: 0 length match at the start // move to the next character for (pos++; pos < input_size; pos++) { if (IsCharacter(input_data[pos])) { @@ -76,19 +78,18 @@ struct StringSplitter { } } D_ASSERT(input_size >= pos + match_size); - emit(input_data, pos); + emit(input_data + start, pos - start); list_idx++; - input_data += (pos + match_size); - input_size -= (pos + match_size); + start = pos + match_size; } - emit(input_data, input_size); + emit(input_data + start, input_size - start); list_idx++; return list_idx; } }; -template -void StringSplitExecutor(DataChunk &args, ExpressionState &state, Vector &result, void *data = nullptr) { +template +void StringSplitExecutor(DataChunk &args, ExpressionState &state, Vector &result, GET_DATA &&get_data) { auto input_entries = args.data[0].Values(); auto delim_entries = args.data[1].Values(); @@ -109,8 +110,12 @@ void StringSplitExecutor(DataChunk &args, ExpressionState &state, Vector &result list.WriteElement().WriteStringRef(input_entry.GetValue()); continue; } + auto delim = delim_entry.GetValue(); StringSplitter::Split( - input_entry.GetValue(), delim_entry.GetValue(), data, [&](const char *split_data, idx_t split_size) { + input_entry.GetValue(), delim, get_data(delim), [&](const char *split_data, idx_t split_size) { + if (OP::VERIFY_UTF8) { + regexp_util::VerifyUTF8Result(split_data, split_size); + } list.WriteElement().WriteStringRef(string_t(split_data, UnsafeNumericCast(split_size))); }); } @@ -119,7 +124,7 @@ void StringSplitExecutor(DataChunk &args, ExpressionState &state, Vector &result } void StringSplitFunction(DataChunk &args, ExpressionState &state, Vector &result) { - StringSplitExecutor(args, state, result, nullptr); + StringSplitExecutor(args, state, result, [](const string_t &) -> void * { return nullptr; }); } void StringSplitRegexFunction(DataChunk &args, ExpressionState &state, Vector &result) { @@ -128,10 +133,18 @@ void StringSplitRegexFunction(DataChunk &args, ExpressionState &state, Vector &r if (info.constant_pattern) { // fast path: pre-compiled regex auto &lstate = ExecuteFunctionState::GetFunctionState(state)->Cast(); - StringSplitExecutor(args, state, result, &lstate.constant_pattern); + StringSplitExecutor(args, state, result, + [&](const string_t &) -> void * { return &lstate.constant_pattern; }); } else { // slow path: have to re-compile regex for every row - StringSplitExecutor(args, state, result); + unique_ptr regex; + StringSplitExecutor(args, state, result, [&](const string_t &delim) -> void * { + regex = make_uniq(regexp_util::CreateStringPiece(delim), info.options); + if (!regex->ok()) { + throw InvalidInputException(regex->error()); + } + return regex.get(); + }); } } diff --git a/src/duckdb/src/function/scalar/string/strip_accents.cpp b/src/duckdb/src/function/scalar/string/strip_accents.cpp index 0b84ba180..520fd65ba 100644 --- a/src/duckdb/src/function/scalar/string/strip_accents.cpp +++ b/src/duckdb/src/function/scalar/string/strip_accents.cpp @@ -1,5 +1,6 @@ #include "duckdb/function/scalar/string_common.hpp" #include "duckdb/function/scalar/string_functions.hpp" +#include "duckdb/common/exception.hpp" #include "duckdb/common/swar.hpp" #include "utf8proc.hpp" @@ -29,6 +30,15 @@ bool IsAscii(const char *input, idx_t n) { return FirstNonAscii(input, n) == n; } +idx_t DecodeCodepoint(const char *input, idx_t size, int32_t &codepoint) { + D_ASSERT(size > 0); + auto bytes = utf8proc_iterate(const_uchar_ptr_cast(input), UnsafeNumericCast(size), &codepoint); + if (bytes <= 0) { + throw InternalException("Invalid UTF-8 encountered in DecodeCodepoint"); + } + return UnsafeNumericCast(bytes); +} + namespace { struct StripAccentsOperator { @@ -41,6 +51,10 @@ struct StripAccentsOperator { // non-ascii, perform collation auto stripped = utf8proc_remove_accents((const utf8proc_uint8_t *)input.GetData(), UnsafeNumericCast(input.GetSize())); + if (!stripped) { + // invalid UTF-8 + return input; + } auto result_str = heap.AddString(const_char_ptr_cast(stripped)); free(stripped); return result_str; diff --git a/src/duckdb/src/function/scalar/string/substring.cpp b/src/duckdb/src/function/scalar/string/substring.cpp index bbb56e558..4588171e5 100644 --- a/src/duckdb/src/function/scalar/string/substring.cpp +++ b/src/duckdb/src/function/scalar/string/substring.cpp @@ -233,7 +233,9 @@ string_t SubstringGrapheme(Vector &result, string_t input, int64_t offset, int64 // we first need to count the number of characters in the string idx_t num_characters = Utf8Proc::GraphemeCount(input_data, input_size); // now call substring start and end again, but with the number of unicode characters this time - SubstringASCIIBounds(UnsafeNumericCast(num_characters), offset, length, start, end); + if (!SubstringASCIIBounds(UnsafeNumericCast(num_characters), offset, length, start, end)) { + return SubstringEmptyString(result); + } } // now scan the graphemes of the string to find the positions of the start and end characters @@ -404,7 +406,7 @@ unique_ptr SubstringStatsFromSharedPrefix(FunctionStatisticsInpu optional_idx max_output_length; if (has_parameters && parameters.character_count.IsValid()) { - auto bytes_per_character = StringStats::CanContainUnicode(input.child_stats[0]) ? 4 : 1; + idx_t bytes_per_character = StringStats::CanContainUnicode(input.child_stats[0]) ? 4 : 1; auto character_count = parameters.character_count.GetIndex(); if (character_count <= NumericLimits::Maximum() / bytes_per_character) { max_output_length = character_count * bytes_per_character; diff --git a/src/duckdb/src/function/scalar/struct/remap_struct.cpp b/src/duckdb/src/function/scalar/struct/remap_struct.cpp index 8a5e61b1e..824eb7832 100644 --- a/src/duckdb/src/function/scalar/struct/remap_struct.cpp +++ b/src/duckdb/src/function/scalar/struct/remap_struct.cpp @@ -335,6 +335,11 @@ struct RemapEntry { RemapEntry remap; remap.index = entry->second.index; remap.target_type = target_entry->second.type; + if (source_type.id() == LogicalTypeId::SQLNULL && struct_val.IsNull()) { + // NULL can be cast directly to a nested type without remapping its children. + result.emplace(remap_target, std::move(remap)); + return; + } if (source_is_nested || target_is_nested || !struct_val.IsNull()) { if (source_type.id() != target_type.id()) { throw BinderException("Can't change source type (%s) to target type (%s), type conversion not allowed", @@ -492,67 +497,51 @@ struct RemapEntry { } } + child_list_t source_children; switch (type.id()) { - case LogicalTypeId::STRUCT: { - auto &source_children = StructType::GetChildTypes(type); - return LogicalType::STRUCT(RemapCastChildren(source_children, remap_map, source_name_map)); - } - case LogicalTypeId::LIST: { - auto &child_type = ListType::GetChildType(type); - - child_list_t source_children; - source_children.emplace_back("list", child_type); - - auto new_source_children = RemapCastChildren(source_children, remap_map, source_name_map); - D_ASSERT(new_source_children.size() == 1); - return LogicalType::LIST(new_source_children[0].second); - } - case LogicalTypeId::MAP: { - auto &key_type = MapType::KeyType(type); - auto &value_type = MapType::ValueType(type); - - child_list_t source_children; - source_children.emplace_back("key", key_type); - source_children.emplace_back("value", value_type); - - auto new_source_children = RemapCastChildren(source_children, remap_map, source_name_map); - D_ASSERT(new_source_children.size() == 2); - return LogicalType::MAP(new_source_children[0].second, new_source_children[1].second); - } + case LogicalTypeId::STRUCT: + source_children = StructType::GetChildTypes(type); + break; + case LogicalTypeId::LIST: + source_children.emplace_back("list", ListType::GetChildType(type)); + break; + case LogicalTypeId::MAP: + source_children.emplace_back("key", MapType::KeyType(type)); + source_children.emplace_back("value", MapType::ValueType(type)); + break; default: throw BinderException("Can't RemapCast for type '%s'", type.ToString()); } + return LogicalType::ConstructNestedType(type, RemapCastChildren(source_children, remap_map, source_name_map)); } }; -unique_ptr RemapStructBind(BindScalarFunctionInput &input) { - auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - D_ASSERT(arguments.size() == 4); +//! Verifies that the (uncast) argument types can be remapped +void VerifyRemapArgumentTypes(const vector &types) { + D_ASSERT(types.size() == 4); for (idx_t arg_idx = 0; arg_idx < 3; arg_idx++) { - auto &arg = arguments[arg_idx]; - if (arg->GetReturnType().id() == LogicalTypeId::UNKNOWN) { + auto &arg_type = types[arg_idx]; + if (arg_type.id() == LogicalTypeId::UNKNOWN) { throw ParameterNotResolvedException(); } - if (arg->GetReturnType().id() == LogicalTypeId::SQLNULL && arg_idx == 2) { + if (arg_type.id() == LogicalTypeId::SQLNULL && arg_idx == 2) { // remap target can be NULL continue; } - if (!IsRemappable(arg->GetReturnType())) { - throw BinderException("Struct remap can only remap nested types, not '%s'", - arg->GetReturnType().ToString()); - } else if (StructType::IsStruct(arg->GetReturnType()) && StructType::IsUnnamed(arg->GetReturnType())) { + if (!IsRemappable(arg_type)) { + throw BinderException("Struct remap can only remap nested types, not '%s'", arg_type.ToString()); + } else if (StructType::IsStruct(arg_type) && StructType::IsUnnamed(arg_type)) { throw BinderException("Struct remap can only remap named structs"); } } - auto &from_type = arguments[0]->GetReturnType(); - auto &to_type = arguments[1]->GetReturnType(); + auto &from_type = types[0]; + auto &to_type = types[1]; - auto &defaults = arguments[3]; - if (defaults->GetReturnType().id() != LogicalTypeId::SQLNULL && !StructType::IsStruct(defaults->GetReturnType())) { + auto &defaults_type = types[3]; + if (defaults_type.id() != LogicalTypeId::SQLNULL && !StructType::IsStruct(defaults_type)) { throw BinderException("The defaults provided to 'remap_struct' should be of type STRUCT if they're not NULL"); } - if (StructType::IsStruct(defaults->GetReturnType()) && StructType::IsUnnamed(defaults->GetReturnType())) { + if (StructType::IsStruct(defaults_type) && StructType::IsUnnamed(defaults_type)) { throw BinderException("The defaults have to be either NULL or a named STRUCT, not an unnamed struct"); } @@ -560,15 +549,19 @@ unique_ptr RemapStructBind(BindScalarFunctionInput &input) { throw BinderException("Can't change source type (%s) to target type (%s), type conversion not allowed", from_type.ToString(), to_type.ToString()); } +} - Value remap_val = input.GetConstant(2); +//! (Recursively) generates the remap entries from the (uncast) argument types +identifier_map_t GetRemapEntries(const vector &types, const Value &remap_val, + bool defaults_are_constant) { + auto &from_type = types[0]; + auto &to_type = types[1]; auto source_map = RemapIndex::GetMap(from_type); auto target_map = RemapIndex::GetMap(to_type); - // (recursively) generate the remap entries identifier_map_t remap_map; if (!remap_val.IsNull()) { - auto &remap_types = StructType::GetChildTypes(arguments[2]->GetReturnType()); + auto &remap_types = StructType::GetChildTypes(types[2]); auto &remap_values = StructValue::GetChildren(remap_val); for (idx_t remap_idx = 0; remap_idx < remap_values.size(); remap_idx++) { auto &remap_val = remap_values[remap_idx]; @@ -576,13 +569,13 @@ unique_ptr RemapStructBind(BindScalarFunctionInput &input) { RemapEntry::PerformRemap(remap_target, remap_val, source_map, target_map, remap_map, from_type); } } - if (!arguments[3]->IsFoldable()) { + if (!defaults_are_constant) { throw BinderException("Default values must be constants"); } - if (arguments[3]->GetReturnType().id() != LogicalTypeId::SQLNULL) { + if (types[3].id() != LogicalTypeId::SQLNULL) { // (recursively) handle the defaults (if there are any) - auto &default_types = StructType::GetChildTypes(arguments[3]->GetReturnType()); + auto &default_types = StructType::GetChildTypes(types[3]); for (idx_t default_idx = 0; default_idx < default_types.size(); default_idx++) { auto &default_target = default_types[default_idx].first; auto &default_type = default_types[default_idx].second; @@ -590,20 +583,28 @@ unique_ptr RemapStructBind(BindScalarFunctionInput &input) { remap_map); } } + return remap_map; +} - // construct the final remapping - auto remap = RemapEntry::ConstructMap(to_type, remap_map); +void RemapStructResolveTypes(ResolveScalarFunctionTypesInput &input) { + auto &bound_function = input.GetBoundFunction(); + auto types = input.GetArgumentTypes(); + VerifyRemapArgumentTypes(types); + auto remap_map = GetRemapEntries(types, input.GetConstant(2), input.GetArgument(3).IsFoldable()); // push a cast for argument 0 to match up the source types to the target - auto new_type = RemapEntry::RemapCast(from_type, remap_map); - - bound_function.GetArguments()[0] = std::move(new_type); - bound_function.GetArguments()[1] = arguments[1]->GetReturnType(); - bound_function.GetArguments()[2] = arguments[2]->GetReturnType(); - bound_function.GetArguments()[3] = arguments[3]->GetReturnType(); - bound_function.SetReturnType(arguments[1]->GetReturnType()); + bound_function.GetArguments()[0] = RemapEntry::RemapCast(types[0], remap_map); + bound_function.GetArguments()[1] = types[1]; + bound_function.GetArguments()[2] = types[2]; + bound_function.GetArguments()[3] = types[3]; + bound_function.SetReturnType(types[1]); +} - return make_uniq(std::move(remap)); +//! The remapping is derived from the types the function was called with, before the input was cast +unique_ptr RemapStructBind(BindScalarFunctionInput &input) { + auto &types = input.GetBoundFunction().GetLogicalArguments(); + auto remap_map = GetRemapEntries(types, input.GetConstant(2), input.GetArguments()[3]->IsFoldable()); + return make_uniq(RemapEntry::ConstructMap(types[1], remap_map)); } } // namespace @@ -615,6 +616,7 @@ ScalarFunction RemapStructFun::GetFunction() { .AddParameter("target_type", LogicalTypeId::ANY) .AddParameter("mapping", LogicalTypeId::ANY) .AddParameter("defaults", LogicalTypeId::ANY); + remap.SetResolveTypesCallback(RemapStructResolveTypes); remap.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); return remap; } diff --git a/src/duckdb/src/function/scalar/struct/struct_concat.cpp b/src/duckdb/src/function/scalar/struct/struct_concat.cpp index 800549e6d..2013b591d 100644 --- a/src/duckdb/src/function/scalar/struct/struct_concat.cpp +++ b/src/duckdb/src/function/scalar/struct/struct_concat.cpp @@ -103,7 +103,7 @@ static unique_ptr StructConcatStats(ClientContext &context, Func ScalarFunction StructConcatFun::GetFunction() { ScalarFunction fun("struct_concat", {}, LogicalTypeId::STRUCT, StructConcatFunction, StructConcatBind, StructConcatStats); - fun.SetVarArgs(LogicalType::ANY); + fun.GetSignature().AddArgs("args", LogicalType::ANY); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); return fun; } diff --git a/src/duckdb/src/function/scalar/struct/struct_contains.cpp b/src/duckdb/src/function/scalar/struct/struct_contains.cpp index 2220a12c5..26ed917df 100644 --- a/src/duckdb/src/function/scalar/struct/struct_contains.cpp +++ b/src/duckdb/src/function/scalar/struct/struct_contains.cpp @@ -6,17 +6,34 @@ #include "duckdb/execution/expression_executor.hpp" #include "duckdb/function/scalar/struct_functions.hpp" #include "duckdb/function/scalar/list/contains_or_position.hpp" +#include "duckdb/planner/expression/bound_function_expression.hpp" namespace duckdb { +struct StructSearchBindData : public FunctionData { + explicit StructSearchBindData(vector matching_members_p) : matching_members(std::move(matching_members_p)) { + } + + //! The indexes of the members that can match the value - the bind casts these to the type of the value + vector matching_members; + +public: + unique_ptr Copy() const override { + return make_uniq(matching_members); + } + bool Equals(const FunctionData &other_p) const override { + return matching_members == other_p.Cast().matching_members; + } +}; + +//! Searches the target in the candidates - the members at the given indexes of the struct template -static void TemplatedStructSearch(const Vector &input_vector, const vector &members, const Vector &target, - const idx_t count, Vector &result) { +static void TemplatedStructSearch(const Vector &input_vector, const vector> &candidates, + const vector &candidate_indexes, const Vector &target, const idx_t count, + Vector &result) { // If the return type is not a bool, return the position const auto return_pos = std::is_same::value; - const auto &target_type = target.GetType(); - UnifiedVectorFormat vector_format; input_vector.ToUnifiedFormat(vector_format); @@ -24,30 +41,23 @@ static void TemplatedStructSearch(const Vector &input_vector, const vector(target_format); + D_ASSERT(candidates.size() == candidate_indexes.size()); vector member_data_ptrs; vector member_vectors; - idx_t total_matches = 0; - for (const auto &member : members) { - if (member.GetType().InternalType() == target_type.InternalType()) { - UnifiedVectorFormat member_format; - member.ToUnifiedFormat(member_format); - member_data_ptrs.push_back(UnifiedVectorFormat::GetData(member_format)); - member_vectors.push_back(std::move(member_format)); - total_matches++; - } else { - member_data_ptrs.push_back(nullptr); - member_vectors.push_back(UnifiedVectorFormat()); - } + for (auto &candidate : candidates) { + UnifiedVectorFormat member_format; + candidate.get().ToUnifiedFormat(member_format); + member_data_ptrs.push_back(UnifiedVectorFormat::GetData(member_format)); + member_vectors.push_back(std::move(member_format)); } - if (total_matches == 0 && return_pos) { + if (candidates.empty() && return_pos) { // if there are no members that match the target type, we cannot return a position ConstantVector::SetNull(result, count_t(count)); return; } auto result_data = FlatVector::Writer(result, count); - const auto member_count = members.size(); for (idx_t row = 0; row < count; row++) { const auto &member_row_idx = vector_format.sel->get_index(row); @@ -74,12 +84,9 @@ static void TemplatedStructSearch(const Vector &input_vector, const vectorget_index(row); const auto col_valid = member_vector.validity.RowIsValid(member_data_idx); @@ -91,10 +98,11 @@ static void TemplatedStructSearch(const Vector &input_vector, const vector(member_idx + 1); + found_value = UnsafeNumericCast(candidate_indexes[candidate_idx] + 1); } else { found_value = RETURN_TYPE(true); } + break; } } @@ -111,65 +119,88 @@ static void TemplatedStructSearch(const Vector &input_vector, const vector -static void StructNestedOp(const Vector &input_vector, const vector &members, const Vector &target, - const idx_t count, Vector &result) { +static void StructNestedOp(const Vector &input_vector, const vector &members, + const vector &matching_members, const Vector &target, const idx_t count, + Vector &result) { const OrderModifiers order_modifiers(OrderType::ASCENDING, OrderByNullType::NULLS_LAST); // Set up sort keys for nested types. - const auto members_size = members.size(); vector member_sort_key_vectors; - for (idx_t i = 0; i < members_size; i++) { - Vector member_sort_key_vec(LogicalType::BLOB, count); - CreateSortKeyHelpers::CreateSortKeyWithValidity(members[i], member_sort_key_vec, order_modifiers); - - member_sort_key_vectors.push_back(std::move(member_sort_key_vec)); + member_sort_key_vectors.reserve(matching_members.size()); + for (auto member_idx : matching_members) { + member_sort_key_vectors.emplace_back(LogicalType::BLOB, count); + CreateSortKeyHelpers::CreateSortKeyWithValidity(members[member_idx], member_sort_key_vectors.back(), + order_modifiers); + } + vector> candidates; + for (auto &sort_key_vector : member_sort_key_vectors) { + candidates.push_back(sort_key_vector); } Vector target_sort_key_vec(LogicalType::BLOB, count); CreateSortKeyHelpers::CreateSortKeyWithValidity(target, target_sort_key_vec, order_modifiers); - TemplatedStructSearch(input_vector, member_sort_key_vectors, target_sort_key_vec, - count, result); + TemplatedStructSearch(input_vector, candidates, matching_members, + target_sort_key_vec, count, result); } template -static void StructSearchOp(const Vector &input_vector, const vector &members, const Vector &target, - const idx_t count, Vector &result) { +static void StructSearchOp(const Vector &input_vector, const vector &members, + const vector &matching_members, const Vector &target, const idx_t count, + Vector &result) { const auto &target_type = target.GetType().InternalType(); + vector> candidates; + for (auto member_idx : matching_members) { + candidates.push_back(members[member_idx]); + } switch (target_type) { case PhysicalType::BOOL: case PhysicalType::INT8: - return TemplatedStructSearch(input_vector, members, target, count, result); + return TemplatedStructSearch(input_vector, candidates, matching_members, + target, count, result); case PhysicalType::INT16: - return TemplatedStructSearch(input_vector, members, target, count, result); + return TemplatedStructSearch(input_vector, candidates, matching_members, + target, count, result); case PhysicalType::INT32: - return TemplatedStructSearch(input_vector, members, target, count, result); + return TemplatedStructSearch(input_vector, candidates, matching_members, + target, count, result); case PhysicalType::INT64: - return TemplatedStructSearch(input_vector, members, target, count, result); + return TemplatedStructSearch(input_vector, candidates, matching_members, + target, count, result); case PhysicalType::INT128: - return TemplatedStructSearch(input_vector, members, target, count, result); + return TemplatedStructSearch(input_vector, candidates, matching_members, + target, count, result); case PhysicalType::UINT8: - return TemplatedStructSearch(input_vector, members, target, count, result); + return TemplatedStructSearch(input_vector, candidates, matching_members, + target, count, result); case PhysicalType::UINT16: - return TemplatedStructSearch(input_vector, members, target, count, result); + return TemplatedStructSearch(input_vector, candidates, matching_members, + target, count, result); case PhysicalType::UINT32: - return TemplatedStructSearch(input_vector, members, target, count, result); + return TemplatedStructSearch(input_vector, candidates, matching_members, + target, count, result); case PhysicalType::UINT64: - return TemplatedStructSearch(input_vector, members, target, count, result); + return TemplatedStructSearch(input_vector, candidates, matching_members, + target, count, result); case PhysicalType::UINT128: - return TemplatedStructSearch(input_vector, members, target, count, result); + return TemplatedStructSearch(input_vector, candidates, matching_members, + target, count, result); case PhysicalType::FLOAT: - return TemplatedStructSearch(input_vector, members, target, count, result); + return TemplatedStructSearch(input_vector, candidates, matching_members, target, + count, result); case PhysicalType::DOUBLE: - return TemplatedStructSearch(input_vector, members, target, count, result); + return TemplatedStructSearch(input_vector, candidates, matching_members, + target, count, result); case PhysicalType::VARCHAR: - return TemplatedStructSearch(input_vector, members, target, count, result); + return TemplatedStructSearch(input_vector, candidates, matching_members, + target, count, result); case PhysicalType::INTERVAL: - return TemplatedStructSearch(input_vector, members, target, count, result); + return TemplatedStructSearch(input_vector, candidates, matching_members, + target, count, result); case PhysicalType::STRUCT: case PhysicalType::LIST: case PhysicalType::ARRAY: - return StructNestedOp(input_vector, members, target, count, result); + return StructNestedOp(input_vector, members, matching_members, target, count, result); default: throw NotImplementedException("This function has not been implemented for logical type %s", TypeIdToString(target_type)); @@ -187,11 +218,13 @@ static void StructSearchFunction(DataChunk &args, ExpressionState &state, Vector const auto &input_vector = args.data[0]; const auto &members = StructVector::GetEntries(input_vector); const auto &target = args.data[1]; + auto &func_expr = state.expr.Cast(); + auto &info = func_expr.BindInfo()->Cast(); - StructSearchOp(input_vector, members, target, count, result); + StructSearchOp(input_vector, members, info.matching_members, target, count, result); } -static unique_ptr StructContainsBind(BindScalarFunctionInput &input) { +static void StructContainsResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &context = input.GetClientContext(); auto &bound_function = input.GetBoundFunction(); auto &arguments = input.GetArguments(); @@ -205,31 +238,40 @@ static unique_ptr StructContainsBind(BindScalarFunctionInput &inpu bound_function.GetArguments()[0] = LogicalTypeId::UNKNOWN; bound_function.GetArguments()[1] = LogicalTypeId::UNKNOWN; bound_function.SetReturnType(LogicalType::SQLNULL); - return nullptr; + return; } auto &struct_children = StructType::GetChildTypes(arguments[0]->GetReturnType()); if (struct_children.empty()) { // an empty struct contains nothing, the search always returns false (or position 0) bound_function.GetArguments()[0] = child_type; - return nullptr; + return; } if (child_type.id() != LogicalTypeId::TUPLE) { throw BinderException("%s can only be used on unnamed structs", bound_function.GetName()); } bound_function.GetArguments()[0] = child_type; - // the value type must match one of the struct's children - LogicalType max_child_type = arguments[1]->GetReturnType(); - vector new_child_types; + // find the type that the value and all the children it can be compared with can be cast to + LogicalType target_type = arguments[1]->GetReturnType(); for (auto &child : struct_children) { - if (!LogicalType::TryGetMaxLogicalType(context, child.second, max_child_type, max_child_type)) { - new_child_types.push_back(child.second); - continue; + LogicalType max_type; + if (LogicalType::TryGetMaxLogicalType(context, child.second, target_type, max_type)) { + target_type = max_type; + } + } + bound_function.GetArguments()[1] = target_type; + // cast the children that can be compared with the value to that type - the others never match + vector new_child_types; + for (idx_t child_idx = 0; child_idx < struct_children.size(); child_idx++) { + auto &struct_child_type = struct_children[child_idx].second; + LogicalType max_type; + if (LogicalType::TryGetMaxLogicalType(context, struct_child_type, target_type, max_type) && + max_type == target_type) { + new_child_types.push_back(target_type); + } else { + new_child_types.push_back(struct_child_type); } - - new_child_types.push_back(max_child_type); - bound_function.GetArguments()[1] = max_child_type; } child_list_t cast_children; @@ -239,20 +281,38 @@ static unique_ptr StructContainsBind(BindScalarFunctionInput &inpu // the input is an unnamed struct - represent it as a TUPLE bound_function.GetArguments()[0] = LogicalType::TUPLE(cast_children); +} - return nullptr; +static unique_ptr StructContainsBind(BindScalarFunctionInput &input) { + auto &arguments = input.GetArguments(); + vector matching_members; + auto &child_type = arguments[0]->GetReturnType(); + if (child_type.id() != LogicalTypeId::SQLNULL) { + // the members that can match the value were cast to the type of the value when resolving the types + auto &target_type = arguments[1]->GetReturnType(); + auto &struct_children = StructType::GetChildTypes(child_type); + for (idx_t child_idx = 0; child_idx < struct_children.size(); child_idx++) { + if (struct_children[child_idx].second == target_type) { + matching_members.push_back(child_idx); + } + } + } + return make_uniq(std::move(matching_members)); } ScalarFunction StructContainsFun::GetFunction() { - ScalarFunction fun("struct_contains", {}, LogicalType::BOOLEAN, StructSearchFunction, StructContainsBind); + ScalarFunction fun("struct_contains", {}, LogicalType::BOOLEAN, StructSearchFunction); fun.GetSignature().AddParameter("struct", LogicalTypeId::TUPLE).AddParameter("entry", LogicalType::ANY); + fun.SetResolveTypesCallback(StructContainsResolveTypes); + fun.SetBindCallback(StructContainsBind); return fun; } ScalarFunction StructPositionFun::GetFunction() { - ScalarFunction fun("struct_contains", {}, LogicalType::INTEGER, StructSearchFunction, - StructContainsBind); + ScalarFunction fun("struct_contains", {}, LogicalType::INTEGER, StructSearchFunction); fun.GetSignature().AddParameter("struct", LogicalTypeId::TUPLE).AddParameter("entry", LogicalType::ANY); + fun.SetResolveTypesCallback(StructContainsResolveTypes); + fun.SetBindCallback(StructContainsBind); fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); return fun; } diff --git a/src/duckdb/src/function/scalar/struct/struct_extract.cpp b/src/duckdb/src/function/scalar/struct/struct_extract.cpp index addc7b19f..030f8a721 100644 --- a/src/duckdb/src/function/scalar/struct/struct_extract.cpp +++ b/src/duckdb/src/function/scalar/struct/struct_extract.cpp @@ -26,24 +26,35 @@ static void StructExtractFunction(DataChunk &args, ExpressionState &state, Vecto result.Verify(); } -static unique_ptr StructExtractBind(BindScalarFunctionInput &input) { +enum class StructExtractKey : uint8_t { NAME, INDEX, INDEX_AT }; + +//! Validates the struct argument - the extracted entry and the return type are resolved in the bind +template +static void StructExtractResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); D_ASSERT(bound_function.GetArguments().size() == 2); - auto &child_type = arguments[0]->GetReturnType(); + auto &child_type = input.GetArgumentType(0); if (child_type.id() == LogicalTypeId::UNKNOWN) { throw ParameterNotResolvedException(); } D_ASSERT(StructType::IsStruct(child_type)); - auto &struct_children = StructType::GetChildTypes(child_type); - if (struct_children.empty()) { + if (StructType::GetChildTypes(child_type).empty()) { throw BinderException("Can't extract something from an empty struct"); } - if (child_type.id() == LogicalTypeId::TUPLE) { + if (KEY == StructExtractKey::NAME && child_type.id() == LogicalTypeId::TUPLE) { throw BinderException( "struct_extract with a string key cannot be used on an unnamed struct, use a numeric index instead"); } + if (KEY == StructExtractKey::INDEX && child_type.id() != LogicalTypeId::TUPLE) { + throw BinderException( + "struct_extract with an integer key can only be used on unnamed structs, use a string key instead"); + } bound_function.GetArguments()[0] = child_type; +} + +static unique_ptr StructExtractBind(BindScalarFunctionInput &input) { + auto &bound_function = input.GetBoundFunction(); + auto &struct_children = StructType::GetChildTypes(bound_function.GetArguments()[0]); auto key_val = input.GetNonNullConstant(1); D_ASSERT(key_val.type().id() == LogicalTypeId::VARCHAR); @@ -82,25 +93,9 @@ static unique_ptr StructExtractBind(BindScalarFunctionInput &input return StructExtractAtFun::GetBindData(key_index); } -static unique_ptr StructExtractBindInternal(BindScalarFunctionInput &input, bool struct_extract) { +static unique_ptr StructExtractBindIndex(BindScalarFunctionInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - D_ASSERT(bound_function.GetArguments().size() == 2); - auto &child_type = arguments[0]->GetReturnType(); - if (child_type.id() == LogicalTypeId::UNKNOWN) { - throw ParameterNotResolvedException(); - } - D_ASSERT(StructType::IsStruct(child_type)); - auto &struct_children = StructType::GetChildTypes(child_type); - if (struct_children.empty()) { - throw BinderException("Can't extract something from an empty struct"); - } - if (struct_extract && child_type.id() != LogicalTypeId::TUPLE) { - throw BinderException( - "struct_extract with an integer key can only be used on unnamed structs, use a string key instead"); - } - bound_function.GetArguments()[0] = child_type; - + auto &struct_children = StructType::GetChildTypes(bound_function.GetArguments()[0]); Value key_val = input.GetConstant(1); auto index = key_val.GetValue(); if (index <= 0 || idx_t(index) > struct_children.size()) { @@ -111,14 +106,6 @@ static unique_ptr StructExtractBindInternal(BindScalarFunctionInpu return StructExtractAtFun::GetBindData(NumericCast(index - 1)); } -static unique_ptr StructExtractBindIndex(BindScalarFunctionInput &input) { - return StructExtractBindInternal(input, true); -} - -static unique_ptr StructExtractAtBind(BindScalarFunctionInput &input) { - return StructExtractBindInternal(input, false); -} - static unique_ptr PropagateStructExtractStats(ClientContext &context, FunctionStatisticsInput &input) { auto &child_stats = input.child_stats; auto &bind_data = input.bind_data; @@ -136,6 +123,7 @@ ScalarFunction GetKeyExtractFunction() { ScalarFunction fun("struct_extract", {}, LogicalType::ANY, StructExtractFunction, StructExtractBind, PropagateStructExtractStats); fun.GetSignature().AddParameter("struct", LogicalTypeId::STRUCT).AddParameter("entry", LogicalType::VARCHAR); + fun.SetResolveTypesCallback(StructExtractResolveTypes); return fun; } @@ -143,13 +131,15 @@ ScalarFunction GetIndexExtractFunction() { ScalarFunction fun("struct_extract", {}, LogicalType::ANY, StructExtractFunction, StructExtractBindIndex, PropagateStructExtractStats); fun.GetSignature().AddParameter("tuple", LogicalTypeId::TUPLE).AddParameter("index", LogicalType::BIGINT); + fun.SetResolveTypesCallback(StructExtractResolveTypes); return fun; } ScalarFunction GetExtractAtFunction() { - ScalarFunction fun("struct_extract_at", {}, LogicalType::ANY, StructExtractFunction, StructExtractAtBind, + ScalarFunction fun("struct_extract_at", {}, LogicalType::ANY, StructExtractFunction, StructExtractBindIndex, PropagateStructExtractStats); fun.GetSignature().AddParameter("struct", LogicalTypeId::STRUCT).AddParameter("index", LogicalType::BIGINT); + fun.SetResolveTypesCallback(StructExtractResolveTypes); return fun; } diff --git a/src/duckdb/src/function/scalar/struct/struct_pack.cpp b/src/duckdb/src/function/scalar/struct/struct_pack.cpp index 46ec492c1..6a05cec46 100644 --- a/src/duckdb/src/function/scalar/struct/struct_pack.cpp +++ b/src/duckdb/src/function/scalar/struct/struct_pack.cpp @@ -1,3 +1,4 @@ +#include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/common/vector/map_vector.hpp" #include "duckdb/common/vector/struct_vector.hpp" #include "duckdb/function/scalar/nested_functions.hpp" @@ -46,34 +47,21 @@ static void StructPackFunction(DataChunk &args, ExpressionState &state, Vector & template static unique_ptr StructPackBind(BindScalarFunctionInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - identifier_set_t name_collision_set; + auto &args = input.GetArguments(); - // collect names and deconflict, construct return type - // note: zero arguments is allowed, producing an empty struct - child_list_t struct_children; - for (idx_t i = 0; i < arguments.size(); i++) { - auto &child = arguments[i]; - string alias; - if (IS_STRUCT_PACK) { - if (child->GetAlias().empty()) { - throw BinderException("Need named argument for struct pack, e.g. STRUCT_PACK(a := b)"); - } - alias = child->GetAlias().GetIdentifierName(); - if (name_collision_set.find(Identifier(alias)) != name_collision_set.end()) { - throw BinderException("Duplicate struct entry name \"%s\"", alias); - } - name_collision_set.insert(Identifier(alias)); - } - struct_children.emplace_back(make_pair(alias, arguments[i]->GetReturnType())); - } - - // this is more for completeness reasons - // row() produces an unnamed TUPLE, struct_pack() produces a named STRUCT if (IS_STRUCT_PACK) { - bound_function.SetReturnType(LogicalType::STRUCT(std::move(struct_children))); + auto &names = *input.GetArgumentNames(); + child_list_t fields; + for (idx_t i = 0; i < args.size(); i++) { + fields.emplace_back(names[i], args[i]->GetReturnType()); + } + bound_function.SetReturnType(LogicalType::STRUCT(std::move(fields))); } else { - bound_function.SetReturnType(LogicalType::TUPLE(std::move(struct_children))); + vector fields; + for (idx_t i = 0; i < args.size(); i++) { + fields.push_back(args[i]->GetReturnType()); + } + bound_function.SetReturnType(LogicalType::TUPLE(std::move(fields))); } return make_uniq(bound_function.GetReturnType()); } @@ -89,19 +77,39 @@ static unique_ptr StructPackStats(ClientContext &context, Functi return struct_stats.ToUnique(); } +template +static unique_ptr StructPackUnbind(FunctionUnbindInput &input) { + auto &expression = input.expression; + vector arguments; + for (idx_t i = 0; i < input.children.size(); i++) { + auto name = IS_STRUCT_PACK ? StructType::GetChildName(expression.GetReturnType(), i) : Identifier(); + arguments.emplace_back(std::move(name), std::move(input.children[i])); + } + return make_uniq(expression.Function().GetDefinition()->GetQualifiedName(), + std::move(arguments)); +} + template static ScalarFunction GetStructPackFunction() { ScalarFunction fun(IS_STRUCT_PACK ? "struct_pack" : "row", {}, IS_STRUCT_PACK ? LogicalTypeId::STRUCT : LogicalTypeId::TUPLE, StructPackFunction, StructPackBind, StructPackStats); - fun.SetVarArgs(LogicalType::ANY); + + if (IS_STRUCT_PACK) { + // struct_pack derives its field names from argument aliases, so the binder must capture argument expression + // aliases as named-argument names. This also preserves the legacy behavior of allowing positional arguments + // after named ones (the positional arguments simply take their expression's name as the field name). + fun.GetSignature().AddKwargs("kwargs", LogicalType::ANY); + fun.SetCaptureArgumentAliases(true); + } else { + // row produces an unnamed TUPLE, so it ignores the names of its arguments + fun.GetSignature().AddArgs("args", LogicalType::ANY); + } + fun.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); - // struct_pack/row derive their (struct field) names from argument aliases, so the binder must capture argument - // expression aliases as named-argument names. This also preserves the legacy behavior of allowing positional - // arguments after named ones (the positional arguments simply take their expression's name as the field name). - fun.SetCaptureArgumentAliases(true); fun.SetSerializeCallback(VariableReturnBindData::Serialize); fun.SetDeserializeCallback(VariableReturnBindData::Deserialize); + fun.SetUnbindCallback(StructPackUnbind); return fun; } diff --git a/src/duckdb/src/function/scalar/system/aggregate_export.cpp b/src/duckdb/src/function/scalar/system/aggregate_export.cpp index bee19d25e..d43e1de71 100644 --- a/src/duckdb/src/function/scalar/system/aggregate_export.cpp +++ b/src/duckdb/src/function/scalar/system/aggregate_export.cpp @@ -20,6 +20,11 @@ #include "duckdb/planner/expression/bound_constant_expression.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" #include "duckdb/function/aggregate/distributive_functions.hpp" +#include "duckdb/parser/expression/cast_expression.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/parser/expression/function_expression.hpp" +#include "duckdb/common/type_visitor.hpp" +#include "duckdb/parser/expression/type_expression.hpp" namespace duckdb { @@ -131,9 +136,13 @@ void AggregateStateFinalize(DataChunk &input, ExpressionState &state_p, Vector & auto &layout = local_state.layout; auto count = input.size(); + // initialize the states - fields that are NULL in the input are not written by the deserialization + AggregateStateInput state_input(bind_data.aggr, bind_data.bind_data.get()); auto state_vec_writer = FlatVector::Writer(local_state.addresses, count); for (idx_t i = 0; i < count; i++) { - state_vec_writer.WriteValue(local_state.state_buffer.get() + i * layout.total_state_size); + data_ptr_t state_ptr = local_state.state_buffer.get() + i * layout.total_state_size; + bind_data.aggr.GetStateInitCallback()(state_input, &state_ptr, 1); + state_vec_writer.WriteValue(state_ptr); } AggregateStateSerialization::DeserializeStates(bind_data.aggr, layout, input.data[0], count, @@ -234,7 +243,7 @@ unique_ptr BindExportedAggregate(ClientContext &context context, QualifiedName(Catalog::GetSystemCatalog(context).GetName(), Identifier::DefaultSchema(), Identifier(function_name))); if (func.type != CatalogType::AGGREGATE_FUNCTION_ENTRY) { - throw InternalException("Could not find aggregate %s", function_name); + throw BinderException("Could not find aggregate %s", function_name); } auto &aggr_entry = func.Cast(); @@ -242,12 +251,28 @@ unique_ptr BindExportedAggregate(ClientContext &context FunctionBinder function_binder(context); auto best_function = function_binder.BindFunction(aggr_entry.name, aggr_entry.functions, argument_types, error); if (!best_function.IsValid()) { - throw InternalException("Could not re-bind exported aggregate %s: %s", function_name, error.Message()); + throw BinderException("Could not re-bind exported aggregate %s: %s", function_name, error.Message()); } const auto &aggr = aggr_entry.functions.GetFunctionByOffset(best_function.GetIndex()); + const auto &signature = aggr->GetSignature(); + for (idx_t arg_idx = 0; arg_idx < argument_types.size(); arg_idx++) { + if (argument_types[arg_idx].id() != LogicalTypeId::ANY) { + continue; + } + auto parameter = arg_idx < signature.GetPositionalParameterCount() + ? optional_ptr(signature.GetParameter(arg_idx)) + : signature.GetArgs(); + // ANY must describe a state-capable implementation, not a placeholder for type specialization. + const bool canonical_any = parameter && parameter->GetType().id() == LogicalTypeId::ANY && + AnyType::GetTargetType(parameter->GetType()) == LogicalType::ANY; + if (!canonical_any || !aggr->HasGetStateTypeCallback()) { + throw BinderException("Aggregate %s requires a concrete type for signature argument %llu", function_name, + arg_idx + 1); + } + } - // FIXME: this is really hacky - // but the aggregate state export needs a rework around how it handles more complex aggregates anyway + // Bind callbacks can use NULL placeholders for parameters carried by the serialized state. + // Parameters required by the binding itself must retain their recorded constant values. vector> args; args.reserve(argument_types.size()); for (idx_t arg_idx = 0; arg_idx < argument_types.size(); arg_idx++) { @@ -265,15 +290,23 @@ unique_ptr BindExportedAggregate(ClientContext &context bool signature_matches = bound_args.size() == argument_types.size(); for (idx_t arg_idx = 0; signature_matches && arg_idx < bound_args.size(); arg_idx++) { // an ANY argument in the function signature (e.g. string_agg's data argument) matches any requested type - if (bound_args[arg_idx].id() != LogicalTypeId::ANY && bound_args[arg_idx] != argument_types[arg_idx]) { + if (bound_args[arg_idx].id() != LogicalTypeId::ANY && + !bound_args[arg_idx].EqualsIncludingCollation(argument_types[arg_idx])) { signature_matches = false; } } if (!signature_matches) { - throw InternalException("Type mismatch for exported aggregate %s: bound=[%s] requested=[%s]", function_name, - StringUtil::ToString(bound_args, ", "), StringUtil::ToString(argument_types, ", ")); + throw BinderException("Type mismatch for exported aggregate %s: bound=[%s] requested=[%s]", function_name, + StringUtil::ToString(bound_args, ", "), StringUtil::ToString(argument_types, ", ")); } + // The signature can retain ANY, but materialized result and state values must have complete types. + if (!bound_aggr.GetReturnType().IsComplete()) { + throw BinderException("Aggregate %s signature does not resolve a complete result type", function_name); + } + if (bound_aggr.HasGetStateTypeCallback() && !bound_aggr.GetStateType(bind_info.get()).type.IsComplete()) { + throw BinderException("Aggregate %s signature does not resolve a complete state type", function_name); + } const auto state_size = bound_aggr.GetStateSize(bind_info.get()); return make_uniq(bound_aggr, std::move(bind_info), state_size); } @@ -355,6 +388,14 @@ unique_ptr BindAggregateStateInternal(ClientContext &co ParseOrderBys(order_entry->second, column_count, orders); // the leading buffered columns are the inner aggregate's bound arguments (post constant-erasure) const idx_t argument_count = inner->aggr.GetArguments().size(); + if (argument_count == 0) { + throw BinderException("Aggregate \"%s\" has no arguments and cannot have an ordered aggregate state", + function_name); + } + if (argument_count > column_count) { + throw BinderException("to_aggregate_state: argument count %llu exceeds the number of state columns (%llu)", + (uint64_t)argument_count, (uint64_t)column_count); + } auto reconstructed = FunctionBinder::BindSortedAggregateState(context, inner->aggr, std::move(inner->bind_data), buffer_struct, orders, argument_count); @@ -614,7 +655,12 @@ LogicalType ParseSignatureType(ClientContext &context, const Value &arg) { return TypeValue::GetType(arg); } if (arg.type().id() == LogicalTypeId::VARCHAR) { - return TransformStringToLogicalType(StringValue::Get(arg), context); + auto &name = StringValue::Get(arg); + // ANY describes a canonical aggregate argument, not an ordinary SQL value type. + if (StringUtil::CIEquals(name, "ANY")) { + return LogicalType::ANY; + } + return TransformStringToLogicalType(name, context); } throw BinderException("to_aggregate_state: the signature must be a list of types"); } @@ -675,6 +721,10 @@ void ParseOrderBys(const Value &order_value, idx_t column_count, vector(); if (state_order.column >= column_count) { @@ -689,7 +739,9 @@ void ParseOrderBys(const Value &order_value, idx_t column_count, vector ToAggregateStateBind(BindScalarFunctionInput &input) { +//! Binds the exported aggregate from the constant arguments, and resolves the state / return types from it +template +unique_ptr BindToAggregateState(INPUT &input) { auto &bound_function = input.GetBoundFunction(); auto &arguments = input.GetArguments(); auto &context = input.GetClientContext(); @@ -702,10 +754,14 @@ unique_ptr ToAggregateStateBind(BindScalarFunctionInput &input) { "constant"); } } - auto function_name_val = input.GetNonNullConstant(1); + // the arguments are not yet cast to the parameter types when binding + auto function_name_val = input.GetNonNullConstant(1).DefaultCastAs(LogicalType::VARCHAR); auto function_name = StringValue::Get(function_name_val); auto signature_val = input.GetNonNullConstant(2); + if (signature_val.type().id() != LogicalTypeId::LIST) { + throw BinderException("to_aggregate_state: the signature must be a list of types"); + } // the signature lists all of the argument types in order vector argument_types; for (auto &arg : ListValue::GetChildren(signature_val)) { @@ -740,8 +796,28 @@ unique_ptr ToAggregateStateBind(BindScalarFunctionInput &input) { throw BinderException("to_aggregate_state: an ordered aggregate state value must be a LIST of STRUCTs (the " "buffer of values), e.g. [{'v0': ...}, ...]"); } - const auto buffer_struct = ListType::GetChildType(state_type); - const idx_t column_count = StructType::GetChildTypes(buffer_struct).size(); + auto buffer_columns = StructType::GetChildTypes(ListType::GetChildType(state_type)); + const idx_t column_count = buffer_columns.size(); + // the leading buffered columns are the aggregate's arguments - cast them to the argument types + auto &aggr_arguments = aggr.GetArguments(); + if (aggr_arguments.empty()) { + throw BinderException("to_aggregate_state: aggregate \"%s\" has no arguments and cannot have an ordered " + "aggregate state", + function_name); + } + if (aggr_arguments.size() > column_count) { + throw BinderException("to_aggregate_state: argument count %llu exceeds the number of state columns (%llu)", + (uint64_t)aggr_arguments.size(), (uint64_t)column_count); + } + for (idx_t i = 0; i < aggr_arguments.size(); i++) { + if (aggr_arguments[i].IsComplete()) { + buffer_columns[i].second = aggr_arguments[i]; + } + } + const auto buffer_struct = LogicalType::STRUCT(std::move(buffer_columns)); + if (!buffer_struct.IsComplete()) { + throw BinderException("to_aggregate_state: the ordered state buffer must have a complete type"); + } vector orders; auto order_value = input.GetConstant(4); ParseOrderBys(order_value, column_count, orders); @@ -760,6 +836,14 @@ unique_ptr ToAggregateStateBind(BindScalarFunctionInput &input) { return std::move(bind_data); } +void ToAggregateStateResolveTypes(ResolveScalarFunctionTypesInput &input) { + BindToAggregateState(input); +} + +unique_ptr ToAggregateStateBind(BindScalarFunctionInput &input) { + return BindToAggregateState(input); +} + void ToAggregateStateFunction(DataChunk &input, ExpressionState &state, Vector &result) { // the input type is verified to match the state layout at bind time - we only need to reinterpret the vector result.Reinterpret(input.data[0]); @@ -767,6 +851,78 @@ void ToAggregateStateFunction(DataChunk &input, ExpressionState &state, Vector & } // namespace +unique_ptr ExportAggregateFunction::StateToSQL(const LogicalType &type, + unique_ptr value) { + auto info = type.GetExtensionInfo(); + if (!type.IsAggregateState() || !info) { + return nullptr; + } + auto name = info->properties.find("function_name"); + auto parameters = info->properties.find("parameters"); + const bool has_function_name = + name != info->properties.end() && !name->second.IsNull() && name->second.type().id() == LogicalTypeId::VARCHAR; + const bool has_parameters = parameters != info->properties.end() && !parameters->second.IsNull() && + parameters->second.type().id() == LogicalTypeId::LIST; + if (!has_function_name || !has_parameters) { + return nullptr; + } + vector types; + map constants; + ParseStateParameters(parameters->second, types, constants); + // Concrete TYPE values retain collations that the SQL type name alone cannot express. + // Canonical ANY arguments are signature markers, not SQL value types. + const bool has_any = std::find(types.begin(), types.end(), LogicalType::ANY) != types.end(); + vector signature; + vector> constant_arguments; + for (idx_t i = 0; i < types.size(); i++) { + if (has_any && ((types[i] != LogicalType::ANY && !TypeExpression::CanRepresent(types[i])) || + TypeVisitor::Contains(types[i], [](const LogicalType &child) { + return child.id() == LogicalTypeId::VARCHAR && !StringType::GetCollation(child).empty(); + }))) { + return nullptr; + } + signature.push_back(has_any ? Value(types[i].ToString()) : Value::TYPE(types[i])); + auto entry = constants.find(i); + unique_ptr constant; + try { + constant = ConstantExpression::FromValue(entry == constants.end() ? Value() : entry->second); + } catch (const NotImplementedException &) { + return nullptr; + } + constant_arguments.push_back(make_uniq(LogicalType::VARIANT(), std::move(constant))); + } + vector> arguments; + arguments.push_back(std::move(value)); + arguments.push_back(ConstantExpression::FromValue(name->second)); + try { + arguments.push_back(ConstantExpression::FromValue( + Value::LIST(has_any ? LogicalType::VARCHAR : LogicalType::TYPE(), std::move(signature)))); + } catch (const NotImplementedException &) { + return nullptr; + } + arguments.push_back( + make_uniq(QualifiedName("system", "main", "list_value"), std::move(constant_arguments))); + auto orders = info->properties.find("order_bys"); + if (orders != info->properties.end()) { + arguments.push_back(ConstantExpression::FromValue(orders->second)); + } + return make_uniq(QualifiedName("system", "main", "to_aggregate_state"), std::move(arguments)); +} + +void ExportAggregateFunction::GetSortedStateOrders(const LogicalType &state_type, + vector &orders) { + auto ext_info = state_type.GetExtensionInfo(); + if (!ext_info || state_type.id() != LogicalTypeId::LIST) { + throw InternalException("GetSortedStateOrders called on a state type that is not an ordered aggregate state"); + } + auto order_entry = ext_info->properties.find("order_bys"); + if (order_entry == ext_info->properties.end()) { + throw InternalException("GetSortedStateOrders called on a state type that is not an ordered aggregate state"); + } + auto &buffer_struct = ListType::GetChildType(state_type); + ParseOrderBys(order_entry->second, StructType::GetChildTypes(buffer_struct).size(), orders); +} + void ExportAggregateFunction::SetStateExport(BoundAggregateExpression &aggregate, LogicalType state_layout) { auto &bound_function = aggregate.FunctionMutable(); // functions with an explicit export callback use it as the finalize; others use the field-based serialization @@ -801,9 +957,11 @@ ExportAggregateFunction::Bind(unique_ptr child_aggrega D_ASSERT(bound_function.HasStateSizeCallback()); D_ASSERT(bound_function.HasStateFinalizeCallback()); D_ASSERT(child_aggregate->Function().GetReturnType().id() != LogicalTypeId::INVALID); - if (child_aggregate->GetOrderBys() && !child_aggregate->GetOrderBys()->orders.empty()) { - // ordered aggregate: export the buffer of values. The sorted wrapper is built later (physical planning); here - // we only fix the AGGREGATE_STATE type so downstream binding (finalize/combine) sees it + if (child_aggregate->GetOrderBys() && !child_aggregate->GetOrderBys()->orders.empty() && + !child_aggregate->GetChildren().empty()) { + // ordered aggregate (the ORDER BY of an aggregate without arguments is ignored): export the buffer of values. + // The sorted wrapper is built later (physical planning); here we only fix the AGGREGATE_STATE type so + // downstream binding (finalize/combine) sees it LogicalType buffer_struct; vector orders; idx_t argument_count; // re-derived from the inner aggregate on re-bind @@ -862,6 +1020,7 @@ ScalarFunctionSet ToAggregateStateFun::GetFunctions() { } ScalarFunction function("to_aggregate_state", arguments, LogicalTypeId::ANY, ToAggregateStateFunction, ToAggregateStateBind); + function.SetResolveTypesCallback(ToAggregateStateResolveTypes); auto &sig = function.GetSignature(); sig.GetParameter(0).SetName("data"); sig.GetParameter(1).SetName("name"); diff --git a/src/duckdb/src/function/scalar/system/parse_log_message.cpp b/src/duckdb/src/function/scalar/system/parse_log_message.cpp index 950af5600..e21c4c65a 100644 --- a/src/duckdb/src/function/scalar/system/parse_log_message.cpp +++ b/src/duckdb/src/function/scalar/system/parse_log_message.cpp @@ -9,6 +9,8 @@ #include "duckdb/logging/log_type.hpp" #include "duckdb/function/cast/vector_cast_helpers.hpp" #include "duckdb/common/types/data_chunk.hpp" +#include "duckdb/main/extension_entries.hpp" +#include "duckdb/main/extension_helper.hpp" #include "utf8proc.hpp" @@ -29,24 +31,31 @@ struct ParseLogMessageData : FunctionData { } }; +void ParseLogMessageResolveTypes(ResolveScalarFunctionTypesInput &input) { + if (input.GetArgumentCount() != 2) { + throw BinderException("structured_log_schema: expects 1 argument", input.GetArgument(0).GetAlias()); + } + // resolve the constant first, so that unresolved parameters are deferred + input.GetConstant(0); + if (input.GetArgumentType(0).id() != LogicalTypeId::VARCHAR) { + throw BinderException("structured_log_schema: 'log_type' argument must be a string"); + } +} + unique_ptr ParseLogMessageBind(BindScalarFunctionInput &input) { auto &context = input.GetClientContext(); auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - - if (arguments.size() != 2) { - throw BinderException("structured_log_schema: expects 1 argument", arguments[0]->GetAlias()); - } auto type_val = input.GetConstant(0); - if (arguments[0]->GetReturnType().id() != LogicalTypeId::VARCHAR) { - throw BinderException("structured_log_schema: 'log_type' argument must be a string"); - } - auto type = StringValue::Get(type_val); auto lookup = LogManager::Get(context).LookupLogType(type); + if (!lookup) { + // the log type may belong to an extension that is not loaded yet + ExtensionHelper::TryAutoloadFromEntry(*context.db, Identifier(type), EXTENSION_LOG_TYPES); + lookup = LogManager::Get(context).LookupLogType(type); + } if (!lookup) { throw InvalidInputException("structured_log_schema: '%s' not found", type); @@ -83,6 +92,7 @@ ScalarFunction ParseLogMessage::GetFunction() { auto fun = ScalarFunction({}, LogicalType::ANY, ParseLogMessageFunction, ParseLogMessageBind, nullptr, nullptr, LogicalType(LogicalTypeId::INVALID)); fun.GetSignature().AddParameter("type", LogicalType::VARCHAR).AddParameter("message", LogicalType::VARCHAR); + fun.SetResolveTypesCallback(ParseLogMessageResolveTypes); fun.SetErrorMode(FunctionErrors::CAN_THROW_RUNTIME_ERROR); return fun; } diff --git a/src/duckdb/src/function/scalar/system/write_log.cpp b/src/duckdb/src/function/scalar/system/write_log.cpp index 526dd86c3..d614b1782 100644 --- a/src/duckdb/src/function/scalar/system/write_log.cpp +++ b/src/duckdb/src/function/scalar/system/write_log.cpp @@ -1,3 +1,4 @@ +#include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/function/scalar/system_functions.hpp" #include "duckdb/execution/expression_executor.hpp" #include "duckdb/main/client_data.hpp" @@ -46,18 +47,19 @@ struct WriteLogBindData : FunctionData { } }; -unique_ptr WriteLogBind(BindScalarFunctionInput &input) { - auto &context = input.GetClientContext(); - auto &bound_function = input.GetBoundFunction(); - auto &arguments = input.GetArguments(); - - if (arguments.empty()) { +void WriteLogResolveTypes(ResolveScalarFunctionTypesInput &input) { + if (input.GetArgumentCount() == 0) { throw BinderException("write_log takes at least one argument"); } - - if (arguments[0]->GetReturnType() != LogicalType::VARCHAR) { + if (input.GetArgumentType(0) != LogicalType::VARCHAR) { throw InvalidTypeException("write_log first argument must be a VARCHAR"); } +} + +unique_ptr WriteLogBind(BindScalarFunctionInput &input) { + auto &context = input.GetClientContext(); + auto &bound_function = input.GetBoundFunction(); + auto &arguments = input.GetArguments(); // Used to replace the actual log call with a nop: useful for benchmarking auto result = make_uniq(); @@ -65,37 +67,38 @@ unique_ptr WriteLogBind(BindScalarFunctionInput &input) { // Default return type bound_function.SetReturnType(LogicalType::VARCHAR); + auto &names = *input.GetArgumentNames(); for (idx_t i = 1; i < arguments.size(); i++) { auto &arg = arguments[i]; if (arg->HasParameter()) { throw ParameterNotResolvedException(); } - if (arg->GetAlias() == "disable_logging") { + if (names[i] == "disable_logging") { if (arg->GetReturnType().id() != LogicalTypeId::BOOLEAN) { throw BinderException("write_log: 'disable_logging' argument must be a boolean"); } result->disable_logging = BooleanValue::Get(input.GetConstant(i)); - } else if (arg->GetAlias() == "scope") { + } else if (names[i] == "scope") { if (arg->GetReturnType().id() != LogicalTypeId::VARCHAR) { throw BinderException("write_log: 'scope' argument must be a string"); } result->scope = StringValue::Get(input.GetConstant(i)); - } else if (arg->GetAlias() == "level") { + } else if (names[i] == "level") { if (arg->GetReturnType().id() != LogicalTypeId::VARCHAR) { throw BinderException("write_log: 'level' argument must be a string"); } result->level = EnumUtil::FromString(StringValue::Get(input.GetConstant(i))); - } else if (arg->GetAlias() == "log_type") { + } else if (names[i] == "log_type") { if (arg->GetReturnType().id() != LogicalTypeId::VARCHAR) { throw BinderException("write_log: 'log_type' argument must be a string"); } result->type = StringValue::Get(input.GetConstant(i)); - } else if (arg->GetAlias() == "return_value") { + } else if (names[i] == "return_value") { result->return_type = arg->GetReturnType(); result->output_col = i; bound_function.SetReturnType(result->return_type); } else { - throw BinderException(StringUtil::Format("write_log: Unknown argument '%s'", arg->GetAlias())); + throw BinderException(StringUtil::Format("write_log: Unknown argument '%s'", names[i])); } } @@ -147,15 +150,34 @@ void WriteLogFunction(DataChunk &args, ExpressionState &state, Vector &result) { } } +//! write_log captures its named arguments as child aliases rather than as named parameters, so they are reattached +//! as names when the call is rendered +static unique_ptr WriteLogUnbind(FunctionUnbindInput &input) { + vector arguments; + for (idx_t i = 0; i < input.children.size(); i++) { + auto name = i == 0 ? Identifier() : input.expression.GetChildren()[i]->GetAlias(); + if (i > 0 && name.empty()) { + return nullptr; + } + arguments.emplace_back(std::move(name), std::move(input.children[i])); + } + return make_uniq(input.expression.Function().GetDefinition()->GetQualifiedName(), + std::move(arguments)); +} + } // namespace ScalarFunctionSet WriteLogFun::GetFunctions() { ScalarFunctionSet set("write_log"); - ScalarFunction fun({}, LogicalType::ANY, WriteLogFunction, WriteLogBind, nullptr, nullptr, LogicalType::ANY, - FunctionStability::VOLATILE); - fun.GetSignature().AddParameter("string", LogicalType::VARCHAR); + ScalarFunction fun({{"string", LogicalType::VARCHAR}}, LogicalType::ANY, WriteLogFunction, WriteLogBind, nullptr, + nullptr, LogicalType(LogicalTypeId::INVALID), FunctionStability::VOLATILE); + fun.GetSignature().AddKwargs("kwargs", LogicalType::ANY); + fun.SetResolveTypesCallback(WriteLogResolveTypes); fun.GetProperties().SetRequiresExpressionNames(true); + // an unknown scope is only detected while writing + fun.SetFallible(); + fun.SetUnbindCallback(WriteLogUnbind); set.AddFunction(std::move(fun)); return set; diff --git a/src/duckdb/src/function/scalar/variant/variant_extract.cpp b/src/duckdb/src/function/scalar/variant/variant_extract.cpp index ee3974668..bd3807cfd 100644 --- a/src/duckdb/src/function/scalar/variant/variant_extract.cpp +++ b/src/duckdb/src/function/scalar/variant/variant_extract.cpp @@ -50,7 +50,12 @@ static unique_ptr VariantExtractPropagateStats(ClientContext &co return nullptr; } - return VariantStats::WrapExtractedFieldAsVariant(variant_stats, *found_stats); + auto result = VariantStats::WrapExtractedFieldAsVariant(variant_stats, *found_stats); + if (info.component.lookup_mode == VariantChildLookupMode::BY_INDEX) { + // Element statistics do not account for NULLs from out-of-bounds array indexes. + result->SetHasNull(); + } + return result; } static unique_ptr VariantExtractBind(BindScalarFunctionInput &input) { @@ -113,7 +118,15 @@ static bool TryShreddedExtractRecursive(const Vector &input, const vector> children_p) + : expression(expression_p), children(std::move(children_p)) { +} + +FunctionUnbindInput::~FunctionUnbindInput() { +} + void ThrowNonFallibleFunctionError(const Identifier &name, std::exception &ex) { ErrorData error(ex); if (!Exception::IsExecutionError(error.Type())) { @@ -15,11 +24,29 @@ void ThrowNonFallibleFunctionError(const Identifier &name, std::exception &ex) { name, error.RawMessage()); } +const Expression &ResolveScalarFunctionTypesInput::GetArgument(idx_t arg_idx) const { + D_ASSERT(arg_idx < arguments.size()); + return *arguments[arg_idx]; +} + +const LogicalType &ResolveScalarFunctionTypesInput::GetArgumentType(idx_t arg_idx) const { + return GetArgument(arg_idx).GetReturnType(); +} + +vector ResolveScalarFunctionTypesInput::GetArgumentTypes() const { + vector result; + result.reserve(arguments.size()); + for (auto &argument : arguments) { + result.push_back(argument->GetReturnType()); + } + return result; +} + bool ScalarFunctionCallbacks::operator==(const ScalarFunctionCallbacks &rhs) const { - return bind == rhs.bind && init_local_state == rhs.init_local_state && statistics == rhs.statistics && - bind_lambda == rhs.bind_lambda && bind_expression == rhs.bind_expression && + return resolve_types == rhs.resolve_types && bind == rhs.bind && init_local_state == rhs.init_local_state && + statistics == rhs.statistics && bind_lambda == rhs.bind_lambda && bind_expression == rhs.bind_expression && get_modified_databases == rhs.get_modified_databases && serialize == rhs.serialize && - deserialize == rhs.deserialize && filter_prune == rhs.filter_prune; + deserialize == rhs.deserialize && filter_prune == rhs.filter_prune && unbind == rhs.unbind; } bool ScalarFunctionCallbacks::operator!=(const ScalarFunctionCallbacks &rhs) const { @@ -34,7 +61,7 @@ ScalarFunctionInfo::~ScalarFunctionInfo() { ScalarFunction::ScalarFunction(Identifier name, FunctionSignature sig, scalar_function_t function) : SimpleFunction(std::move(name), std::move(sig)) { - callbacks.function = std::move(function); + SetFunctionCallback(std::move(function)); } ScalarFunction::ScalarFunction(Identifier name, vector arguments, LogicalType return_type, @@ -46,7 +73,7 @@ ScalarFunction::ScalarFunction(Identifier name, vector arguments, L properties.stability = side_effects; properties.null_handling = null_handling; - callbacks.function = std::move(function); + SetFunctionCallback(std::move(function)); callbacks.bind = bind; callbacks.init_local_state = init_local_state; callbacks.statistics = statistics; @@ -67,11 +94,14 @@ ScalarFunction::ScalarFunction(Identifier name, std::initializer_list= 1); result.Reference(input.data[0]); @@ -125,9 +151,7 @@ BoundScalarFunction::BoundScalarFunction(const ScalarFunction &function) BoundScalarFunction::BoundScalarFunction(shared_ptr function_p) : definition(std::move(function_p)) { auto &function = *definition; - name = function.name; - schema_name = function.GetSchemaName(); - catalog_name = function.GetCatalogName(); + qualified_name = function.GetQualifiedName(); extra_info = function.extra_info; return_type = function.GetReturnType(); callbacks = function.GetCallbacks(); @@ -138,15 +162,19 @@ BoundScalarFunction::BoundScalarFunction(shared_ptr functi // Try to default bind the function, to fill in any missing information in the BoundScalarFunction (e.g. from the // "bind" callback) for (auto ¶m : function.GetSignature().GetParameters()) { - arguments.push_back(param.GetType()); + if (!param.IsVariadic()) { + arguments.push_back(param.GetType()); + } } + positional_arguments = arguments.size(); logical_arguments = arguments; logical_return_type = return_type; } bool BoundScalarFunction::operator==(const BoundScalarFunction &rhs) const { - return callbacks == rhs.callbacks && properties == rhs.properties && name == rhs.name && - return_type == rhs.return_type && arguments == rhs.arguments; + return callbacks == rhs.callbacks && properties == rhs.properties && GetName() == rhs.GetName() && + return_type == rhs.return_type && arguments == rhs.arguments && + positional_arguments == rhs.positional_arguments && named_arguments == rhs.named_arguments; } bool BoundScalarFunction::operator!=(const BoundScalarFunction &rhs) const { return !(*this == rhs); diff --git a/src/duckdb/src/function/table/arrow.cpp b/src/duckdb/src/function/table/arrow.cpp index bc09f3565..4c86ff705 100644 --- a/src/duckdb/src/function/table/arrow.cpp +++ b/src/duckdb/src/function/table/arrow.cpp @@ -13,7 +13,7 @@ #include "duckdb/parser/parsed_data/create_table_function_info.hpp" #include "duckdb/parser/tableref/table_function_ref.hpp" #include "utf8proc_wrapper.hpp" -#include "duckdb/common/extra_type_info.hpp" +#include "duckdb/common/logical_type_info.hpp" #include "duckdb/common/arrow/schema_metadata.hpp" #include "duckdb/main/settings.hpp" @@ -59,28 +59,14 @@ unique_ptr ArrowTableFunction::ArrowScanBindDumb(ClientContext &co unique_ptr ArrowTableFunction::ArrowScanBind(ClientContext &context, TableFunctionBindInput &input, vector &return_types, vector &names) { - if (input.inputs[0].IsNull() || input.inputs[1].IsNull() || input.inputs[2].IsNull()) { - throw BinderException("arrow_scan: pointers cannot be null"); + if (!input.ref.bind_info) { + throw BinderException("arrow_scan requires an ArrowScanFactory bind input"); } - auto &ref = input.ref; - - shared_ptr dependency; - if (ref.external_dependency) { - // This was created during the replacement scan for Python (see python_replacement_scan.cpp) - // this object is the owning reference to 'stream_factory_ptr' and has to be kept alive. - dependency = ref.external_dependency->GetDependency("replacement_cache"); - D_ASSERT(dependency); - } - - auto stream_factory_ptr = input.inputs[0].GetPointer(); - auto stream_factory_produce = (stream_factory_produce_t)input.inputs[1].GetPointer(); // NOLINT - auto stream_factory_get_schema = (stream_factory_get_schema_t)input.inputs[2].GetPointer(); // NOLINT - - auto res = make_uniq(stream_factory_produce, stream_factory_ptr, std::move(dependency)); - - auto &data = *res; - stream_factory_get_schema(reinterpret_cast(stream_factory_ptr), data.schema_root.arrow_schema); - PopulateArrowTableSchema(context, res->arrow_table, data.schema_root.arrow_schema); + DynamicCastCheck(input.ref.bind_info.get()); + auto factory = shared_ptr_cast(input.ref.bind_info); + auto res = make_uniq(std::move(factory)); + res->factory->GetSchema(res->schema_root.arrow_schema); + PopulateArrowTableSchema(context, res->arrow_table, res->schema_root.arrow_schema); names = StringsToIdentifiers(res->arrow_table.GetNames()); return_types = res->arrow_table.GetTypes(); res->all_types = return_types; @@ -107,7 +93,7 @@ unique_ptr ProduceArrowScan(const ArrowScanFunctionData } } parameters.filters = filters; - return function.scanner_producer(function.stream_factory_ptr, parameters); + return function.factory->ProduceStream(parameters); } idx_t ArrowTableFunction::ArrowScanMaxThreads(ClientContext &context, const FunctionData *bind_data_p) { @@ -335,8 +321,7 @@ bool ArrowTableFunction::ArrowPushdownType(const FunctionData &bind_data, idx_t } void ArrowTableFunction::RegisterFunction(BuiltinFunctions &set) { - TableFunction arrow("arrow_scan", {LogicalType::POINTER, LogicalType::POINTER, LogicalType::POINTER}, - ArrowScanFunction, ArrowScanBind, ArrowScanInitGlobal, ArrowScanInitLocal); + TableFunction arrow("arrow_scan", {}, ArrowScanFunction, ArrowScanBind, ArrowScanInitGlobal, ArrowScanInitLocal); arrow.cardinality = ArrowScanCardinality; arrow.get_partition_data = ArrowGetPartitionData; arrow.projection_pushdown = true; @@ -346,8 +331,8 @@ void ArrowTableFunction::RegisterFunction(BuiltinFunctions &set) { arrow.parallelism = TableFunctionParallelism::SEQUENTIAL; set.AddFunction(arrow); - TableFunction arrow_dumb("arrow_scan_dumb", {LogicalType::POINTER, LogicalType::POINTER, LogicalType::POINTER}, - ArrowScanFunction, ArrowScanBindDumb, ArrowScanInitGlobal, ArrowScanInitLocal); + TableFunction arrow_dumb("arrow_scan_dumb", {}, ArrowScanFunction, ArrowScanBindDumb, ArrowScanInitGlobal, + ArrowScanInitLocal); arrow_dumb.cardinality = ArrowScanCardinality; arrow_dumb.get_partition_data = ArrowGetPartitionData; arrow_dumb.projection_pushdown = false; diff --git a/src/duckdb/src/function/table/arrow/arrow_duck_schema.cpp b/src/duckdb/src/function/table/arrow/arrow_duck_schema.cpp index fbdfad1a7..fb4e986d2 100644 --- a/src/duckdb/src/function/table/arrow/arrow_duck_schema.cpp +++ b/src/duckdb/src/function/table/arrow/arrow_duck_schema.cpp @@ -338,6 +338,13 @@ LogicalType ArrowType::GetDuckType(bool use_dictionary) const { if (!use_dictionary) { return type; } + if (HasExtension()) { + // A registered extension type's type info describes the extension's STORAGE tree, which need not + // mirror the logical type's children — reconstructing through it would produce a hybrid type. + // The logical type is authoritative here; any dictionaries inside the storage are handled by the + // storage-side conversion, not by the output type. + return type; + } // Dictionaries can exist in arbitrarily nested schemas // have to reconstruct the type auto id = type.id(); @@ -417,7 +424,11 @@ unique_ptr ArrowType::GetTypeFromSchema(ClientContext &context, Arrow if (config.HasArrowExtension(extension_info)) { auto extension = config.GetArrowExtension(extension_info); arrow_type = extension.GetType(context, schema, schema_metadata); - arrow_type->extension_data = extension.GetTypeExtension(); + if (!arrow_type->extension_data) { + // GetType may have attached per-column extension data (e.g. to record the incoming + // schema's field order); fall back to the registered instance otherwise + arrow_type->extension_data = extension.GetTypeExtension(); + } } } diff --git a/src/duckdb/src/function/table/arrow_conversion.cpp b/src/duckdb/src/function/table/arrow_conversion.cpp index a229c8664..b2bbae167 100644 --- a/src/duckdb/src/function/table/arrow_conversion.cpp +++ b/src/duckdb/src/function/table/arrow_conversion.cpp @@ -48,6 +48,50 @@ T *ArrowBufferData(ArrowArray &array, idx_t buffer_idx) { return (T *)array.buffers[buffer_idx]; // NOLINT } +static bool IsAligned(const_data_ptr_t ptr, idx_t alignment) { + return reinterpret_cast(ptr) % alignment == 0; +} + +//! Arrow buffers are not required to be aligned to their value type, and reading an unaligned buffer through a +//! typed pointer is undefined behavior. This reads the values in place when they are aligned, and from an aligned +//! copy of the [offset, offset + count) range when they are not. +template +class ArrowAlignedBufferData { +public: + ArrowAlignedBufferData(const ArrowArray &array, idx_t buffer_idx, idx_t offset, idx_t count) { + auto source = static_cast(array.buffers[buffer_idx]) + offset * sizeof(T); + if (IsAligned(source, alignof(T))) { + data = reinterpret_cast(source); + return; + } + copy = make_unsafe_uniq_array_uninitialized(count); + memcpy(static_cast(copy.get()), source, count * sizeof(T)); + data = copy.get(); + } + + const T &operator[](idx_t idx) const { + return data[idx]; + } + const T *get() const { + return data; + } + +private: + const T *data; + unsafe_unique_array copy; +}; + +//! Points the vector to the values when they are aligned to their type, and copies them into it when they are not +static void SetVectorData(Vector &vector, data_ptr_t data, idx_t size) { + auto type_size = GetTypeIdSize(vector.GetType().InternalType()); + if (IsAligned(data, MinValue(type_size, sizeof(uint64_t)))) { + FlatVector::SetData(vector, data, count_t(size)); + return; + } + memcpy(FlatVector::GetDataMutable(vector), data, type_size * size); + FlatVector::SetSize(vector, size); +} + static void GetValidityMask(ValidityMask &mask, ArrowArray &array, idx_t chunk_offset, idx_t size, int64_t parent_offset, int64_t nested_offset = -1, bool add_null = false) { // In certain we don't need to or cannot copy arrow's validity mask to duckdb. @@ -65,9 +109,10 @@ static void GetValidityMask(ValidityMask &mask, ArrowArray &array, idx_t chunk_o //! just memcpy nullmask memcpy((void *)mask.GetData(), ArrowBufferData(array, 0) + bit_offset / 8, n_bitmask_bytes); } else { - //! need to re-align nullmask - vector temp_nullmask(n_bitmask_bytes + 1); - memcpy(temp_nullmask.data(), ArrowBufferData(array, 0) + bit_offset / 8, n_bitmask_bytes + 1); + //! need to re-align nullmask - only the bytes that hold the bits of the rows are guaranteed to exist + vector temp_nullmask(n_bitmask_bytes + 1, 0); + auto n_source_bytes = (bit_offset % 8 + size + 8 - 1) / 8; + memcpy(temp_nullmask.data(), ArrowBufferData(array, 0) + bit_offset / 8, n_source_bytes); ShiftRight(temp_nullmask.data(), NumericCast(n_bitmask_bytes + 1), NumericCast(bit_offset % 8ull)); //! why this has to be a right shift is a mystery to me memcpy((void *)mask.GetData(), data_ptr_cast(temp_nullmask.data()), n_bitmask_bytes); @@ -125,7 +170,7 @@ static ArrowListOffsetData ConvertArrowListOffsetsTemplated(Vector &vector, Arro } idx_t cur_offset = 0; - auto offsets = ArrowBufferData(array, 1) + effective_offset; + ArrowAlignedBufferData offsets(array, 1, effective_offset, size + 1); start_offset = offsets[0]; auto list_data = FlatVector::Writer(vector, size); for (idx_t i = 0; i < size; i++) { @@ -148,8 +193,8 @@ static ArrowListOffsetData ConvertArrowListViewOffsetsTemplated(Vector &vector, auto &list_size = result.list_size; list_size = 0; - auto offsets = ArrowBufferData(array, 1) + effective_offset; - auto sizes = ArrowBufferData(array, 2) + effective_offset; + ArrowAlignedBufferData offsets(array, 1, effective_offset, size); + ArrowAlignedBufferData sizes(array, 2, effective_offset, size); // In ListArrays the offsets have to be sequential // ListViewArrays do not have this same constraint @@ -341,7 +386,7 @@ static void ArrowToDuckDBMapVerify(const Vector &vector, idx_t count) { } template -static void SetVectorString(Vector &vector, idx_t size, char *cdata, T *offsets) { +static void SetVectorString(Vector &vector, idx_t size, char *cdata, const T *offsets) { auto strings = FlatVector::GetDataMutable(vector); for (idx_t row_idx = 0; row_idx < size; row_idx++) { if (FlatVector::IsNull(vector, row_idx)) { @@ -358,7 +403,7 @@ static void SetVectorString(Vector &vector, idx_t size, char *cdata, T *offsets) static void SetVectorStringView(Vector &vector, idx_t size, ArrowArray &array, idx_t current_pos) { auto strings = FlatVector::Writer(vector, size); - auto arrow_string = ArrowBufferData(array, 1) + current_pos; + ArrowAlignedBufferData arrow_string(array, 1, current_pos, size); for (idx_t row_idx = 0; row_idx < size; row_idx++) { if (FlatVector::IsNull(vector, row_idx)) { @@ -371,7 +416,7 @@ static void SetVectorStringView(Vector &vector, idx_t size, ArrowArray &array, i // | Bytes 0-3 | Bytes 4-15 | // |------------|---------------------------------------| // | length | data (padded with 0) | - strings.WriteStringRef(string_t(arrow_string[row_idx].GetInlineData(), length)); + strings.WriteValue(string_t(arrow_string[row_idx].GetInlineData(), length)); } else { // This string is not inlined, we have to check a different buffer and offsets // | Bytes 0-3 | Bytes 4-7 | Bytes 8-11 | Bytes 12-15 | @@ -392,7 +437,7 @@ static void DirectConversion(Vector &vector, ArrowArray &array, idx_t chunk_offs auto data_ptr = ArrowBufferData(array, 1) + internal_type * GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset); - FlatVector::SetData(vector, data_ptr, count_t(size)); + SetVectorData(vector, data_ptr, size); } template @@ -400,8 +445,8 @@ static void TimeConversion(Vector &vector, ArrowArray &array, idx_t chunk_offset int64_t parent_offset, idx_t size, int64_t conversion) { auto tgt_writer = FlatVector::Writer(vector, size); auto &validity_mask = FlatVector::ValidityMutable(vector); - auto src_ptr = static_cast(array.buffers[1]) + - GetEffectiveOffset(array, parent_offset, chunk_offset, nested_offset); + ArrowAlignedBufferData src_ptr(array, 1, GetEffectiveOffset(array, parent_offset, chunk_offset, nested_offset), + size); if (validity_mask.CannotHaveNull()) { for (idx_t row = 0; row < size; row++) { int64_t result; @@ -430,8 +475,8 @@ static void TimeNSConversion(Vector &vector, ArrowArray &array, idx_t chunk_offs int64_t parent_offset, idx_t size, int64_t conversion) { auto tgt_ptr = FlatVector::GetDataMutable(vector); auto &validity_mask = FlatVector::ValidityMutable(vector); - auto src_ptr = static_cast(array.buffers[1]) + - GetEffectiveOffset(array, parent_offset, chunk_offset, nested_offset); + ArrowAlignedBufferData src_ptr(array, 1, GetEffectiveOffset(array, parent_offset, chunk_offset, nested_offset), + size); if (validity_mask.CannotHaveNull()) { for (idx_t row = 0; row < size; row++) { // dtime_ns_t.micros actually holds nanos (!) @@ -456,8 +501,8 @@ static void UUIDConversion(Vector &vector, const ArrowArray &array, idx_t chunk_ int64_t parent_offset, idx_t size) { auto tgt_ptr = FlatVector::GetDataMutable(vector); auto &validity_mask = FlatVector::ValidityMutable(vector); - auto src_ptr = static_cast(array.buffers[1]) + - GetEffectiveOffset(array, parent_offset, chunk_offset, nested_offset); + ArrowAlignedBufferData src_ptr( + array, 1, GetEffectiveOffset(array, parent_offset, chunk_offset, nested_offset), size); if (validity_mask.CannotHaveNull()) { for (idx_t row = 0; row < size; row++) { tgt_ptr[row].lower = static_cast(BSwapIfLE(src_ptr[row].upper)); @@ -482,8 +527,8 @@ static void TimestampTZConversion(Vector &vector, ArrowArray &array, idx_t chunk int64_t parent_offset, idx_t size, int64_t conversion) { auto tgt_ptr = FlatVector::GetDataMutable(vector); auto &validity_mask = FlatVector::ValidityMutable(vector); - auto src_ptr = - ArrowBufferData(array, 1) + GetEffectiveOffset(array, parent_offset, chunk_offset, nested_offset); + ArrowAlignedBufferData src_ptr( + array, 1, GetEffectiveOffset(array, parent_offset, chunk_offset, nested_offset), size); if (validity_mask.CannotHaveNull()) { for (idx_t row = 0; row < size; row++) { if (!TryMultiplyOperator::Operation(src_ptr[row], conversion, tgt_ptr[row].value)) { @@ -505,8 +550,8 @@ static void TimestampTZConversion(Vector &vector, ArrowArray &array, idx_t chunk static void IntervalConversionUs(Vector &vector, ArrowArray &array, idx_t chunk_offset, int64_t nested_offset, int64_t parent_offset, idx_t size, int64_t conversion) { auto tgt_ptr = FlatVector::GetDataMutable(vector); - auto src_ptr = - ArrowBufferData(array, 1) + GetEffectiveOffset(array, parent_offset, chunk_offset, nested_offset); + ArrowAlignedBufferData src_ptr( + array, 1, GetEffectiveOffset(array, parent_offset, chunk_offset, nested_offset), size); for (idx_t row = 0; row < size; row++) { tgt_ptr[row].days = 0; tgt_ptr[row].months = 0; @@ -519,8 +564,8 @@ static void IntervalConversionUs(Vector &vector, ArrowArray &array, idx_t chunk_ static void IntervalConversionMonths(Vector &vector, ArrowArray &array, idx_t chunk_offset, int64_t nested_offset, int64_t parent_offset, idx_t size) { auto tgt_ptr = FlatVector::GetDataMutable(vector); - auto src_ptr = - ArrowBufferData(array, 1) + GetEffectiveOffset(array, parent_offset, chunk_offset, nested_offset); + ArrowAlignedBufferData src_ptr( + array, 1, GetEffectiveOffset(array, parent_offset, chunk_offset, nested_offset), size); for (idx_t row = 0; row < size; row++) { tgt_ptr[row].days = 0; tgt_ptr[row].micros = 0; @@ -531,8 +576,8 @@ static void IntervalConversionMonths(Vector &vector, ArrowArray &array, idx_t ch static void IntervalConversionMonthDayNanos(Vector &vector, ArrowArray &array, idx_t chunk_offset, int64_t nested_offset, int64_t parent_offset, idx_t size) { auto tgt_ptr = FlatVector::GetDataMutable(vector); - auto src_ptr = ArrowBufferData(array, 1) + - GetEffectiveOffset(array, parent_offset, chunk_offset, nested_offset); + ArrowAlignedBufferData src_ptr( + array, 1, GetEffectiveOffset(array, parent_offset, chunk_offset, nested_offset), size); for (idx_t row = 0; row < size; row++) { tgt_ptr[row].days = src_ptr[row].days; tgt_ptr[row].micros = src_ptr[row].nanoseconds / Interval::NANOS_PER_MICRO; @@ -731,12 +776,12 @@ void ArrowToDuckDBConversion::ColumnArrowToDuckDBRunEndEncoded(Vector &vector, c run_end_encoding.run_ends = make_uniq(run_ends_type.GetDuckType(), compressed_size); run_end_encoding.values = make_uniq(values_type.GetDuckType(), compressed_size); - ArrowToDuckDBConversion::ColumnArrowToDuckDB(*run_end_encoding.run_ends, run_ends_array, chunk_offset, - array_state, compressed_size, run_ends_type); + // the run ends and values are scanned in full - the offsets of the scan apply to the logical (decoded) rows + ArrowToDuckDBConversion::ColumnArrowToDuckDB(*run_end_encoding.run_ends, run_ends_array, 0, array_state, + compressed_size, run_ends_type); auto &values = *run_end_encoding.values; - ArrowToDuckDBConversion::SetValidityMask(values, values_array, chunk_offset, compressed_size, - NumericCast(parent_offset), nested_offset); - ArrowToDuckDBConversion::ColumnArrowToDuckDB(values, values_array, chunk_offset, array_state, compressed_size, + ArrowToDuckDBConversion::SetValidityMask(values, values_array, 0, compressed_size, 0, -1); + ArrowToDuckDBConversion::ColumnArrowToDuckDB(values, values_array, 0, array_state, compressed_size, values_type); } @@ -757,9 +802,15 @@ void ArrowToDuckDBConversion::ColumnArrowToDuckDBRunEndEncoded(Vector &vector, c } } template -void ConvertDecimal(SRC src_ptr, Vector &vector, ArrowArray &array, idx_t size, int64_t nested_offset, - uint64_t parent_offset, idx_t chunk_offset, ValidityMask &val_mask, - DecimalBitWidth arrow_bit_width) { +static void ConvertDecimal(Vector &vector, ArrowArray &array, idx_t size, idx_t offset) { + if (sizeof(SRC) == GetTypeIdSize(vector.GetType().InternalType())) { + auto data = ArrowBufferData(array, 1) + offset * sizeof(SRC); + SetVectorData(vector, data, size); + return; + } + + ArrowAlignedBufferData src_ptr(array, 1, offset, size); + auto &val_mask = FlatVector::Validity(vector); switch (vector.GetType().InternalType()) { case PhysicalType::INT16: { auto tgt_ptr = FlatVector::GetDataMutable(vector); @@ -773,58 +824,36 @@ void ConvertDecimal(SRC src_ptr, Vector &vector, ArrowArray &array, idx_t size, break; } case PhysicalType::INT32: { - if (arrow_bit_width == DecimalBitWidth::DECIMAL_32) { - auto data = ArrowBufferData(array, 1) + - GetTypeIdSize(vector.GetType().InternalType()) * - GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset); - FlatVector::SetData(vector, data, count_t(size)); - } else { - auto tgt_ptr = FlatVector::GetDataMutable(vector); - for (idx_t row = 0; row < size; row++) { - if (val_mask.RowIsValid(row)) { - auto result = TryCast::Operation(src_ptr[row], tgt_ptr[row]); - D_ASSERT(result); - (void)result; - } + auto tgt_ptr = FlatVector::GetDataMutable(vector); + for (idx_t row = 0; row < size; row++) { + if (val_mask.RowIsValid(row)) { + auto result = TryCast::Operation(src_ptr[row], tgt_ptr[row]); + D_ASSERT(result); + (void)result; } } break; } case PhysicalType::INT64: { - if (arrow_bit_width == DecimalBitWidth::DECIMAL_64) { - auto data = ArrowBufferData(array, 1) + - GetTypeIdSize(vector.GetType().InternalType()) * - GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset); - FlatVector::SetData(vector, data, count_t(size)); - } else { - auto tgt_ptr = FlatVector::GetDataMutable(vector); - for (idx_t row = 0; row < size; row++) { - if (val_mask.RowIsValid(row)) { - auto result = TryCast::Operation(src_ptr[row], tgt_ptr[row]); - D_ASSERT(result); - (void)result; - } + auto tgt_ptr = FlatVector::GetDataMutable(vector); + for (idx_t row = 0; row < size; row++) { + if (val_mask.RowIsValid(row)) { + auto result = TryCast::Operation(src_ptr[row], tgt_ptr[row]); + D_ASSERT(result); + (void)result; } } break; } case PhysicalType::INT128: { - if (arrow_bit_width == DecimalBitWidth::DECIMAL_128) { - auto data = ArrowBufferData(array, 1) + - GetTypeIdSize(vector.GetType().InternalType()) * - GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset); - FlatVector::SetData(vector, data, count_t(size)); - } else { - auto tgt_ptr = FlatVector::GetDataMutable(vector); - for (idx_t row = 0; row < size; row++) { - if (val_mask.RowIsValid(row)) { - auto result = TryCast::Operation(src_ptr[row], tgt_ptr[row]); - D_ASSERT(result); - (void)result; - } + auto tgt_ptr = FlatVector::GetDataMutable(vector); + for (idx_t row = 0; row < size; row++) { + if (val_mask.RowIsValid(row)) { + auto result = TryCast::Operation(src_ptr[row], tgt_ptr[row]); + D_ASSERT(result); + (void)result; } } - break; } default: @@ -842,7 +871,15 @@ void ArrowToDuckDBConversion::ColumnArrowToDuckDB(Vector &vector, ArrowArray &ar if (!ignore_extensions && arrow_type.HasExtension()) { if (arrow_type.extension_data->arrow_to_duckdb) { // Convert the storage and then call the cast function - Vector input_data(arrow_type.extension_data->GetInternalType()); + // `size` is the child's own count, which for a nested child is not bounded by + // STANDARD_VECTOR_SIZE -- size the storage vector for it (as ArrowAppendData::AppendChild does). + Vector input_data(arrow_type.extension_data->GetInternalType(), + MaxValue(size, STANDARD_VECTOR_SIZE)); + // Every caller has already established this column's top-level validity on `vector` — transfer + // it onto the storage vector, since the cast function is responsible for carrying it through to + // its result (a conversion that rebuilds the result does not preserve the pre-set validity, and + // a spec-clean writer need not mirror a parent NULL in the storage children's own validity). + FlatVector::CopyValidity(input_data, vector, size); ColumnArrowToDuckDB(input_data, array, chunk_offset, array_state, size, arrow_type, nested_offset, parent_mask, parent_offset, /*ignore_extensions*/ true); arrow_type.extension_data->arrow_to_duckdb(array_state.context, input_data, vector, size); @@ -909,16 +946,18 @@ void ArrowToDuckDBConversion::ColumnArrowToDuckDB(Vector &vector, ArrowArray &ar switch (size_type) { case ArrowVariableSizeType::SUPER_SIZE: { auto cdata = ArrowBufferData(array, 2); - auto offsets = ArrowBufferData(array, 1) + - GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset); - SetVectorString(vector, size, cdata, offsets); + ArrowAlignedBufferData offsets( + array, 1, GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset), + size + 1); + SetVectorString(vector, size, cdata, offsets.get()); break; } case ArrowVariableSizeType::NORMAL: { auto cdata = ArrowBufferData(array, 2); - auto offsets = ArrowBufferData(array, 1) + - GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset); - SetVectorString(vector, size, cdata, offsets); + ArrowAlignedBufferData offsets( + array, 1, GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset), + size + 1); + SetVectorString(vector, size, cdata, offsets.get()); break; } case ArrowVariableSizeType::VIEW: { @@ -959,8 +998,9 @@ void ArrowToDuckDBConversion::ColumnArrowToDuckDB(Vector &vector, ArrowArray &ar } case ArrowDateTimeType::MILLISECONDS: { //! convert date from nanoseconds to days - auto src_ptr = ArrowBufferData(array, 1) + - GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset); + ArrowAlignedBufferData src_ptr( + array, 1, GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset), + size); auto tgt_ptr = FlatVector::GetDataMutable(vector); for (idx_t row = 0; row < size; row++) { tgt_ptr[row] = date_t(UnsafeNumericCast(static_cast(src_ptr[row]) / @@ -994,8 +1034,9 @@ void ArrowToDuckDBConversion::ColumnArrowToDuckDB(Vector &vector, ArrowArray &ar } case ArrowDateTimeType::NANOSECONDS: { auto tgt_ptr = FlatVector::GetDataMutable(vector); - auto src_ptr = ArrowBufferData(array, 1) + - GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset); + ArrowAlignedBufferData src_ptr( + array, 1, GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset), + size); for (idx_t row = 0; row < size; row++) { tgt_ptr[row].value = src_ptr[row] / 1000; } @@ -1055,8 +1096,9 @@ void ArrowToDuckDBConversion::ColumnArrowToDuckDB(Vector &vector, ArrowArray &ar } case ArrowDateTimeType::NANOSECONDS: { auto tgt_ptr = FlatVector::GetDataMutable(vector); - auto src_ptr = ArrowBufferData(array, 1) + - GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset); + ArrowAlignedBufferData src_ptr( + array, 1, GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset), + size); for (idx_t row = 0; row < size; row++) { tgt_ptr[row].value = src_ptr[row] / 1000; } @@ -1113,8 +1155,9 @@ void ArrowToDuckDBConversion::ColumnArrowToDuckDB(Vector &vector, ArrowArray &ar } case ArrowDateTimeType::NANOSECONDS: { auto tgt_ptr = FlatVector::GetDataMutable(vector); - auto src_ptr = ArrowBufferData(array, 1) + - GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset); + ArrowAlignedBufferData src_ptr( + array, 1, GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset), + size); for (idx_t row = 0; row < size; row++) { tgt_ptr[row].micros = src_ptr[row] / 1000; tgt_ptr[row].days = 0; @@ -1138,34 +1181,18 @@ void ArrowToDuckDBConversion::ColumnArrowToDuckDB(Vector &vector, ArrowArray &ar break; } case LogicalTypeId::DECIMAL: { - auto &val_mask = FlatVector::ValidityMutable(vector); auto &datetime_info = arrow_type.GetTypeInfo(); - auto bit_width = datetime_info.GetBitWidth(); - - switch (bit_width) { - case DecimalBitWidth::DECIMAL_32: { - auto src_ptr = ArrowBufferData(array, 1) + - GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset); - ConvertDecimal(src_ptr, vector, array, size, nested_offset, parent_offset, chunk_offset, val_mask, - bit_width); + auto offset = GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset); + switch (datetime_info.GetBitWidth()) { + case DecimalBitWidth::DECIMAL_32: + ConvertDecimal(vector, array, size, offset); break; - } - - case DecimalBitWidth::DECIMAL_64: { - auto src_ptr = ArrowBufferData(array, 1) + - GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset); - ConvertDecimal(src_ptr, vector, array, size, nested_offset, parent_offset, chunk_offset, val_mask, - bit_width); + case DecimalBitWidth::DECIMAL_64: + ConvertDecimal(vector, array, size, offset); break; - } - - case DecimalBitWidth::DECIMAL_128: { - auto src_ptr = ArrowBufferData(array, 1) + - GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset); - ConvertDecimal(src_ptr, vector, array, size, nested_offset, parent_offset, chunk_offset, val_mask, - bit_width); + case DecimalBitWidth::DECIMAL_128: + ConvertDecimal(vector, array, size, offset); break; - } default: throw NotImplementedException("Unsupported precision for Arrow Decimal Type."); } @@ -1242,6 +1269,7 @@ void ArrowToDuckDBConversion::ColumnArrowToDuckDB(Vector &vector, ArrowArray &ar auto &validity_mask = FlatVector::ValidityMutable(vector); auto &union_info = arrow_type.GetTypeInfo(); + const auto member_offset = parent_offset + NumericCast(array.offset); duckdb::vector children; for (idx_t child_idx = 0; child_idx < NumericCast(array.n_children); child_idx++) { Vector child(members[child_idx].second, size); @@ -1249,22 +1277,25 @@ void ArrowToDuckDBConversion::ColumnArrowToDuckDB(Vector &vector, ArrowArray &ar auto &child_state = array_state.GetChild(child_idx); auto &child_type = union_info.GetChild(child_idx); + // the members of a sparse union have the same length as the union - they are scanned at the same offset ArrowToDuckDBConversion::SetValidityMask(child, child_array, chunk_offset, size, - NumericCast(parent_offset), nested_offset); + NumericCast(member_offset), nested_offset); auto array_physical_type = child_type.GetPhysicalType(); switch (array_physical_type) { case ArrowArrayPhysicalType::DICTIONARY_ENCODED: ArrowToDuckDBConversion::ColumnArrowToDuckDBDictionary(child, child_array, chunk_offset, child_state, - size, child_type); + size, child_type, nested_offset, &validity_mask, + member_offset); break; case ArrowArrayPhysicalType::RUN_END_ENCODED: ArrowToDuckDBConversion::ColumnArrowToDuckDBRunEndEncoded(child, child_array, chunk_offset, child_state, - size, child_type); + size, child_type, nested_offset, + &validity_mask, member_offset); break; case ArrowArrayPhysicalType::DEFAULT: ArrowToDuckDBConversion::ColumnArrowToDuckDB(child, child_array, chunk_offset, child_state, size, - child_type, nested_offset, &validity_mask, false); + child_type, nested_offset, &validity_mask, member_offset); break; default: throw NotImplementedException("ArrowArrayPhysicalType not recognized"); @@ -1418,7 +1449,8 @@ static void SetSelectionVector(SelectionVector &sel, data_ptr_t indices_p, const } static bool CanContainNull(const ArrowArray &array, const ValidityMask *parent_mask) { - if (array.null_count > 0) { + // a null count of -1 means that the number of nulls is unknown + if (array.null_count != 0) { return true; } if (!parent_mask) { @@ -1443,23 +1475,22 @@ void ArrowToDuckDBConversion::ColumnArrowToDuckDBDictionary(Vector &vector, Arro //! so the buffer must be large enough and that entry must be marked invalid. auto dict_length = NumericCast(array.dictionary->length); auto base_vector = make_uniq(vector.GetType(), dict_length + 1); - ArrowToDuckDBConversion::SetValidityMask(*base_vector, *array.dictionary, chunk_offset, dict_length, 0, 0, - has_nulls); + ArrowToDuckDBConversion::SetValidityMask(*base_vector, *array.dictionary, 0, dict_length, 0, 0, has_nulls); FlatVector::ValidityMutable(*base_vector).SetInvalid(dict_length); auto &dictionary_type = arrow_type.GetDictionary(); auto arrow_physical_type = dictionary_type.GetPhysicalType(); ; switch (arrow_physical_type) { case ArrowArrayPhysicalType::DICTIONARY_ENCODED: - ColumnArrowToDuckDBDictionary(*base_vector, *array.dictionary, chunk_offset, array_state, + ColumnArrowToDuckDBDictionary(*base_vector, *array.dictionary, 0, array_state, NumericCast(array.dictionary->length), dictionary_type); break; case ArrowArrayPhysicalType::RUN_END_ENCODED: - ColumnArrowToDuckDBRunEndEncoded(*base_vector, *array.dictionary, chunk_offset, array_state, + ColumnArrowToDuckDBRunEndEncoded(*base_vector, *array.dictionary, 0, array_state, NumericCast(array.dictionary->length), dictionary_type); break; case ArrowArrayPhysicalType::DEFAULT: - ColumnArrowToDuckDB(*base_vector, *array.dictionary, chunk_offset, array_state, + ColumnArrowToDuckDB(*base_vector, *array.dictionary, 0, array_state, NumericCast(array.dictionary->length), dictionary_type); break; default: @@ -1475,9 +1506,16 @@ void ArrowToDuckDBConversion::ColumnArrowToDuckDBDictionary(Vector &vector, Arro } auto offset_type = arrow_type.GetDuckType(); //! Get Pointer to Indices of Dictionary - auto indices = ArrowBufferData(array, 1) + - GetTypeIdSize(offset_type.InternalType()) * - GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset); + auto index_size = GetTypeIdSize(offset_type.InternalType()); + auto indices = + ArrowBufferData(array, 1) + + index_size * GetEffectiveOffset(array, NumericCast(parent_offset), chunk_offset, nested_offset); + unsafe_unique_array aligned_indices; + if (!IsAligned(indices, index_size)) { + aligned_indices = make_unsafe_uniq_array_uninitialized((index_size * size + 7) / 8); + memcpy(aligned_indices.get(), indices, index_size * size); + indices = reinterpret_cast(aligned_indices.get()); + } SelectionVector sel; if (has_nulls) { diff --git a/src/duckdb/src/function/table/checkpoint.cpp b/src/duckdb/src/function/table/checkpoint.cpp index 3dd2474ed..c696a4d4d 100644 --- a/src/duckdb/src/function/table/checkpoint.cpp +++ b/src/duckdb/src/function/table/checkpoint.cpp @@ -11,7 +11,7 @@ struct CheckpointBindData : public FunctionData { explicit CheckpointBindData(optional_ptr db) : db(db) { } - optional_ptr db; + mutable optional_ptr db; public: unique_ptr Copy() const override { @@ -49,20 +49,21 @@ static unique_ptr CheckpointBind(ClientContext &context, TableFunc template static void TemplatedCheckpointFunction(ClientContext &context, TableFunctionInput &data_p, DataChunk &output) { auto &bind_data = data_p.bind_data->Cast(); - auto &transaction_manager = TransactionManager::Get(*bind_data.db.get_mutable()); + auto &transaction_manager = TransactionManager::Get(*bind_data.db); transaction_manager.Checkpoint(context, FORCE); } void CheckpointFunction::RegisterFunction(BuiltinFunctions &set) { TableFunctionSet checkpoint("checkpoint"); checkpoint.AddFunction(TableFunction({}, TemplatedCheckpointFunction, CheckpointBind)); - checkpoint.AddFunction(TableFunction({LogicalType::VARCHAR}, TemplatedCheckpointFunction, CheckpointBind)); + checkpoint.AddFunction(TableFunction(FunctionSignature().AddPositionalOnly("database", LogicalType::VARCHAR), + TemplatedCheckpointFunction, CheckpointBind)); set.AddFunction(checkpoint); TableFunctionSet force_checkpoint("force_checkpoint"); force_checkpoint.AddFunction(TableFunction({}, TemplatedCheckpointFunction, CheckpointBind)); - force_checkpoint.AddFunction( - TableFunction({LogicalType::VARCHAR}, TemplatedCheckpointFunction, CheckpointBind)); + force_checkpoint.AddFunction(TableFunction(FunctionSignature().AddPositionalOnly("database", LogicalType::VARCHAR), + TemplatedCheckpointFunction, CheckpointBind)); set.AddFunction(force_checkpoint); } diff --git a/src/duckdb/src/function/table/copy_csv.cpp b/src/duckdb/src/function/table/copy_csv.cpp index 4fd142f81..d469dff80 100644 --- a/src/duckdb/src/function/table/copy_csv.cpp +++ b/src/duckdb/src/function/table/copy_csv.cpp @@ -10,11 +10,12 @@ #include "duckdb/common/types/column/column_data_collection.hpp" #include "duckdb/common/types/string_type.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" -#include "duckdb/execution/operator/csv_scanner/csv_multi_file_info.hpp" +#include "duckdb/execution/operator/csv_scanner/csv_schema_discovery.hpp" #include "duckdb/execution/operator/csv_scanner/sniffer/csv_sniffer.hpp" #include "duckdb/function/copy_function.hpp" #include "duckdb/function/scalar/string_functions.hpp" #include "duckdb/function/function_binder.hpp" +#include "duckdb/common/multi_file/table_function_multi_file.hpp" #include "duckdb/function/table/read_csv.hpp" #include "duckdb/parser/parsed_data/copy_info.hpp" #include "duckdb/planner/binder.hpp" @@ -139,13 +140,8 @@ static vector> CreateCastExpressions(WriteCSVData &bind_d vector> children; children.push_back(std::move(column)); children.push_back(make_uniq(format)); - ErrorData error; FunctionBinder function_binder(context); - expr = function_binder.BindScalarFunction(Identifier::DefaultSchema(), Identifier("strftime"), - std::move(children), error, false); - if (!expr) { - error.Throw(); - } + expr = function_binder.BindScalarFunction(Identifier::DefaultSchema(), "strftime", std::move(children)); } else { // CAST AS VARCHAR expr = std::move(column); @@ -198,14 +194,14 @@ static void CSVListCopyOptions(ClientContext &context, CopyOptionsInput &input) copy_options["parallel"] = CopyOption(LogicalType::BOOLEAN, CopyOptionMode::READ_ONLY); copy_options["allow_quoted_nulls"] = CopyOption(LogicalType::BOOLEAN, CopyOptionMode::READ_ONLY); copy_options["store_rejects"] = CopyOption(LogicalType::BOOLEAN, CopyOptionMode::READ_ONLY); - copy_options["force_not_null"] = CopyOption(LogicalType::ANY, CopyOptionMode::READ_ONLY); + copy_options["force_not_null"] = CopyOption(LogicalType::LIST(LogicalType::VARCHAR), CopyOptionMode::READ_ONLY); copy_options["rejects_table"] = CopyOption(LogicalType::VARCHAR, CopyOptionMode::READ_ONLY); copy_options["rejects_scan"] = CopyOption(LogicalType::VARCHAR, CopyOptionMode::READ_ONLY); copy_options["rejects_limit"] = CopyOption(LogicalType::BIGINT, CopyOptionMode::READ_ONLY); copy_options["encoding"] = CopyOption(LogicalType::VARCHAR, CopyOptionMode::READ_ONLY); copy_options["thousands"] = CopyOption(LogicalType::VARCHAR, CopyOptionMode::READ_ONLY); - copy_options["force_quote"] = CopyOption(LogicalType::ANY, CopyOptionMode::WRITE_ONLY); + copy_options["force_quote"] = CopyOption(LogicalType::LIST(LogicalType::VARCHAR), CopyOptionMode::WRITE_ONLY); copy_options["prefix"] = CopyOption(LogicalType::VARCHAR, CopyOptionMode::WRITE_ONLY); copy_options["suffix"] = CopyOption(LogicalType::VARCHAR, CopyOptionMode::WRITE_ONLY); @@ -461,7 +457,7 @@ void CSVCopyFunction::RegisterFunction(BuiltinFunctions &set) { info.flush_batch = WriteCSVFlushBatch; info.file_size_bytes = WriteCSVFileSizeBytes; - info.copy_from_bind = MultiFileFunction::MultiFileBindCopy; + info.copy_from_bind = TableFunctionMultiFileWrapper::MultiFileBindCopy; info.copy_from_function = ReadCSVTableFunction::GetFunction(); info.extension = "csv"; diff --git a/src/duckdb/src/function/table/direct_file_reader.cpp b/src/duckdb/src/function/table/direct_file_reader.cpp index 88ac996de..c84fdc485 100644 --- a/src/duckdb/src/function/table/direct_file_reader.cpp +++ b/src/duckdb/src/function/table/direct_file_reader.cpp @@ -27,7 +27,7 @@ unique_ptr DirectFileReader::GetStatistics(ClientContext &contex bool DirectFileReader::TryInitializeScan(ClientContext &context, GlobalTableFunctionState &gstate, LocalTableFunctionState &lstate) { auto &state = gstate.Cast(); - return file_list_idx.GetIndex() < state.file_list->GetTotalFileCount() && !done; + return file_list_idx.GetIndex() < state.file_list->GetTotalFileCount() && !done.load(std::memory_order_relaxed); } static void AssertMaxFileSize(const string &file_name, idx_t file_size) { @@ -52,7 +52,7 @@ static inline void VERIFY(const string &filename, const string_t &content) { AsyncResult DirectFileReader::Scan(ClientContext &context, GlobalTableFunctionState &global_state, LocalTableFunctionState &local_state, DataChunk &output) { auto &state = global_state.Cast(); - if (done || file_list_idx.GetIndex() >= state.file_list->GetTotalFileCount()) { + if (done.load(std::memory_order_relaxed) || file_list_idx.GetIndex() >= state.file_list->GetTotalFileCount()) { return AsyncResult(SourceResultType::FINISHED); } @@ -61,22 +61,29 @@ AsyncResult DirectFileReader::Scan(ClientContext &context, GlobalTableFunctionSt auto &fs = FileSystem::GetFileSystem(context); const idx_t out_idx = 0; - // We utilize projection pushdown here to only read the file content if the 'data' column is requested unique_ptr file_handle = nullptr; + optional file_metadata; + const bool is_remote = FileSystem::IsRemoteFile(file.path); - // Given the columns requested, do we even need to open the file? - if (state.requires_file_open) { + // Remote metadata is obtained from the opened handle so it can use the remote file cache and validators. + if (state.requires_file_open || (state.requires_file_metadata && is_remote)) { auto flags = FileFlags::FILE_FLAGS_READ; - if (FileSystem::IsRemoteFile(file.path)) { + if (is_remote) { flags |= FileFlags::FILE_FLAGS_DIRECT_IO; } flags.SetCachingMode(CachingMode::CACHE_REMOTE_ONLY); file_handle = fs.OpenFile(file, flags); + } else if (state.requires_file_metadata) { + file_metadata = fs.GetStatsIfExists(file); + if (!file_metadata) { + done.store(true, std::memory_order_relaxed); + return SourceResultType::FINISHED; + } } else { // At least verify that the file exist // The globbing behavior in remote filesystems can lead to files being listed that do not actually exist - if (FileSystem::IsRemoteFile(file.path) && !fs.FileExists(file.path)) { - done = true; + if (is_remote && !fs.FileExists(file.path)) { + done.store(true, std::memory_order_relaxed); return SourceResultType::FINISHED; } } @@ -97,6 +104,8 @@ AsyncResult DirectFileReader::Scan(ClientContext &context, GlobalTableFunctionSt case ReadFileBindData::FILE_CONTENT_COLUMN: { const auto file_size = file_handle->GetFileSize(); AssertMaxFileSize(file.path, file_size); + // Zero-sized files could be a special file, so read until EOF. + const bool read_until_eof = file_handle->IsPipe() || file_size == 0; // Initialize write stream if not yet done if (!state.stream) { @@ -106,16 +115,18 @@ AsyncResult DirectFileReader::Scan(ClientContext &context, GlobalTableFunctionSt // Read in batches of 128mb constexpr idx_t MAX_READ_SIZE = 128LL * 1024 * 1024; - auto remaining_bytes = file_handle->IsPipe() ? MAX_READ_SIZE : file_size; + constexpr idx_t UNKNOWN_FILE_READ_SIZE = 64LL * 1024; + const auto read_size = read_until_eof ? UNKNOWN_FILE_READ_SIZE : MAX_READ_SIZE; + auto remaining_bytes = read_until_eof ? read_size : file_size; while (remaining_bytes > 0) { - const auto bytes_to_read = MinValue(remaining_bytes, MAX_READ_SIZE); + const auto bytes_to_read = MinValue(remaining_bytes, read_size); state.stream->GrowCapacity(bytes_to_read); idx_t actually_read = NumericCast(file_handle->Read( context, state.stream->GetData() + state.stream->GetPosition(), bytes_to_read)); state.stream->SetPosition(state.stream->GetPosition() + actually_read); AssertMaxFileSize(file.path, state.stream->GetPosition()); - if (file_handle->IsPipe()) { + if (read_until_eof) { if (actually_read == 0) { remaining_bytes = 0; } @@ -141,15 +152,19 @@ AsyncResult DirectFileReader::Scan(ClientContext &context, GlobalTableFunctionSt } break; case ReadFileBindData::FILE_SIZE_COLUMN: { auto &file_size_vector = output.data[col_idx]; - FlatVector::GetDataMutable(file_size_vector)[out_idx] = - NumericCast(file_handle->GetFileSize()); + D_ASSERT(file_metadata.has_value() || file_handle); + auto file_size = + file_metadata ? file_metadata->file_size : NumericCast(file_handle->GetFileSize()); + FlatVector::GetDataMutable(file_size_vector)[out_idx] = file_size; } break; case ReadFileBindData::FILE_LAST_MODIFIED_COLUMN: { auto &last_modified_vector = output.data[col_idx]; // This can sometimes fail (e.g. httpfs file system cant always parse the last modified time // correctly) try { - const auto timestamp_seconds = fs.GetLastModifiedTime(*file_handle); + D_ASSERT(file_metadata.has_value() || file_handle); + const auto timestamp_seconds = + file_metadata ? file_metadata->last_modification_time : fs.GetLastModifiedTime(*file_handle); FlatVector::GetDataMutable(last_modified_vector)[out_idx] = timestamp_tz_t(timestamp_seconds); } catch (std::exception &ex) { @@ -177,7 +192,7 @@ AsyncResult DirectFileReader::Scan(ClientContext &context, GlobalTableFunctionSt } } output.SetChildCardinality(1); - done = true; + done.store(true, std::memory_order_relaxed); return AsyncResult(SourceResultType::HAVE_MORE_OUTPUT); } @@ -185,4 +200,8 @@ void DirectFileReader::FinishFile(ClientContext &context, GlobalTableFunctionSta return; } +double DirectFileReader::GetProgressInFile(ClientContext &context) { + return done.load(std::memory_order_relaxed) ? 100.0 : 0.0; +} + } // namespace duckdb diff --git a/src/duckdb/src/function/table/glob.cpp b/src/duckdb/src/function/table/glob.cpp index b9ca555ab..1bf3f3164 100644 --- a/src/duckdb/src/function/table/glob.cpp +++ b/src/duckdb/src/function/table/glob.cpp @@ -1,9 +1,13 @@ #include "duckdb/function/table/range.hpp" #include "duckdb/function/table_function.hpp" #include "duckdb/function/function_set.hpp" +#include "duckdb/common/atomic.hpp" #include "duckdb/common/file_system.hpp" #include "duckdb/main/config.hpp" #include "duckdb/common/multi_file/multi_file_reader.hpp" +#include "duckdb/execution/progress_data.hpp" + +#include namespace duckdb { @@ -22,10 +26,13 @@ static unique_ptr GlobFunctionBind(ClientContext &context, TableFu } struct GlobFunctionState : public GlobalTableFunctionState { - GlobFunctionState() { + GlobFunctionState() : files_returned(0) { } MultiFileListScanData file_list_scan; + atomic files_returned; + //! The progress is estimated while the glob is being expanded - keeps it monotonic + MonotonicProgress progress; }; static unique_ptr GlobFunctionInit(ClientContext &context, TableFunctionInitInput &input) { @@ -53,10 +60,32 @@ static void GlobFunction(ClientContext &context, TableFunctionInput &data_p, Dat count++; state.file_list_scan.scan_type = MultiFileListScanType::FETCH_IF_AVAILABLE; } + state.files_returned.fetch_add(count, std::memory_order_relaxed); +} + +static double GlobFunctionProgress(ClientContext &context, const FunctionData *bind_data_p, + const GlobalTableFunctionState *global_state) { + auto &bind_data = bind_data_p->Cast(); + auto &state = global_state->Cast(); + auto files_returned = static_cast(state.files_returned.load(std::memory_order_relaxed)); + // only look at the files that have been expanded so far - expanding the glob here would perform I/O + auto file_count = bind_data.file_list->GetFileCount(0); + double fraction; + if (file_count.type == FileExpansionType::ALL_FILES_EXPANDED) { + fraction = file_count.count == 0 ? 1.0 : files_returned / static_cast(file_count.count); + } else { + // the number of files is not known yet - converge towards 100% as files are returned + fraction = 1 - 1 / (1 + std::log2(1 + files_returned / static_cast(STANDARD_VECTOR_SIZE))); + fraction = MinValue(fraction, 0.99); + } + auto result = state.progress.Update(ProgressData {MinValue(fraction, 1.0), 1.0, false}); + return 100.0 * result.done; } void GlobTableFunction::RegisterFunction(BuiltinFunctions &set) { - TableFunction glob_function("glob", {LogicalType::VARCHAR}, GlobFunction, GlobFunctionBind, GlobFunctionInit); + TableFunction glob_function("glob", FunctionSignature().AddPositionalOnly("pattern", LogicalType::VARCHAR), + GlobFunction, GlobFunctionBind, GlobFunctionInit); + glob_function.table_scan_progress = GlobFunctionProgress; set.AddFunction(MultiFileReader::CreateFunctionSet(glob_function)); } diff --git a/src/duckdb/src/function/table/query_function.cpp b/src/duckdb/src/function/table/query_function.cpp index 3739ac91e..542122a00 100644 --- a/src/duckdb/src/function/table/query_function.cpp +++ b/src/duckdb/src/function/table/query_function.cpp @@ -8,8 +8,8 @@ namespace duckdb { -static unique_ptr ParseSubquery(const string &query, const ParserOptions &options, const string &err_msg) { - Parser parser(options); +static unique_ptr ParseSubquery(const string &query, ClientContext &context, const string &err_msg) { + Parser parser(context); parser.ParseQuery(query); if (parser.statements.size() != 1) { throw ParserException(err_msg); @@ -31,6 +31,32 @@ static unique_ptr ParseSubquery(const string &query, const ParserOp } } +//! Whether the argument is a file path or URL rather than a (qualified) table name: a path separator outside of quotes +static bool IsFilePath(const string &input) { + bool quoted = false; + for (auto c : input) { + if (c == '"') { + quoted = !quoted; + } else if (!quoted && (c == '/' || c == '\\')) { + return true; + } + } + return false; +} + +//! Renders the argument as a table reference: file paths are passed as string literals, like FROM 'file.parquet' +static string TableReference(const string &input) { + if (IsFilePath(input)) { + return SQLString::ToString(input); + } + QualifiedName qname; + string error; + if (!QualifiedName::TryParse(input, qname, error)) { + throw ParserException(error); + } + return qname.ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); +} + static string UnionTablesQuery(TableFunctionBindInput &input) { for (auto &input_val : input.inputs) { if (input_val.IsNull()) { @@ -43,9 +69,7 @@ static string UnionTablesQuery(TableFunctionBindInput &input) { ? "BY NAME " : ""; // 'by_name' variable defaults to false if (input.inputs[0].type().id() == LogicalTypeId::VARCHAR) { - auto from_path = input.inputs[0].ToString(); - auto qualified_name = QualifiedName::Parse(from_path); - result += "FROM " + qualified_name.ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); + result += "FROM " + TableReference(input.inputs[0].ToString()); } else if (input.inputs[0].type() == LogicalType::LIST(LogicalType::VARCHAR)) { string union_all_clause = " UNION ALL " + by_name + "FROM "; const auto &children = ListValue::GetChildren(input.inputs[0]); @@ -53,12 +77,9 @@ static string UnionTablesQuery(TableFunctionBindInput &input) { if (children.empty()) { throw InvalidInputException("Input list is empty"); } - auto qualified_name = QualifiedName::Parse(children[0].ToString()); - result += "FROM " + qualified_name.ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); + result += "FROM " + TableReference(children[0].ToString()); for (size_t i = 1; i < children.size(); ++i) { - auto child = children[i].ToString(); - auto qualified_name = QualifiedName::Parse(child); - result += union_all_clause + qualified_name.ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); + result += union_all_clause + TableReference(children[i].ToString()); } } else { throw InvalidInputException("Expected a table or a list with tables as input"); @@ -68,31 +89,37 @@ static string UnionTablesQuery(TableFunctionBindInput &input) { static unique_ptr QueryBindReplace(ClientContext &context, TableFunctionBindInput &input) { auto query = input.inputs[0].ToString(); - auto subquery_ref = ParseSubquery(query, context.GetParserOptions(), "Expected a single SELECT statement"); + auto subquery_ref = ParseSubquery(query, context, "Expected a single SELECT statement"); return std::move(subquery_ref); } static unique_ptr TableBindReplace(ClientContext &context, TableFunctionBindInput &input) { auto query = UnionTablesQuery(input); - auto subquery_ref = - ParseSubquery(query, context.GetParserOptions(), "Expected a table or a list with tables as input"); + auto subquery_ref = ParseSubquery(query, context, "Expected a table or a list with tables as input"); return std::move(subquery_ref); } void QueryTableFunction::RegisterFunction(BuiltinFunctions &set) { - TableFunction query("query", {LogicalType::VARCHAR}, nullptr, nullptr); + TableFunction query("query", FunctionSignature().AddPositionalOnly("query", LogicalType::VARCHAR), nullptr, + nullptr); query.bind_replace = QueryBindReplace; set.AddFunction(query); TableFunctionSet query_table("query_table"); - TableFunction query_table_function({LogicalType::VARCHAR}, nullptr, nullptr); + TableFunction query_table_function(FunctionSignature().AddPositionalOnly("table_name", LogicalType::VARCHAR), + nullptr, nullptr); query_table_function.bind_replace = TableBindReplace; query_table.AddFunction(query_table_function); - query_table_function.GetArguments() = {LogicalType::LIST(LogicalType::VARCHAR)}; + query_table_function.GetSignature() = + FunctionSignature().AddPositionalOnly("table_names", LogicalType::LIST(LogicalType::VARCHAR)); + query_table.AddFunction(query_table_function); + // add by_name option, for a single table name as well as for a list of them + query_table_function.GetSignature().AddPositionalOnly("by_name", LogicalType::BOOLEAN); query_table.AddFunction(query_table_function); - // add by_name option - query_table_function.GetArguments().emplace_back(LogicalType::BOOLEAN); + query_table_function.GetSignature() = FunctionSignature() + .AddPositionalOnly("table_name", LogicalType::VARCHAR) + .AddPositionalOnly("by_name", LogicalType::BOOLEAN); query_table.AddFunction(query_table_function); set.AddFunction(query_table); } diff --git a/src/duckdb/src/function/table/range.cpp b/src/duckdb/src/function/table/range.cpp index d777c40a2..038189e31 100644 --- a/src/duckdb/src/function/table/range.cpp +++ b/src/duckdb/src/function/table/range.cpp @@ -4,6 +4,7 @@ #include "duckdb/function/function_set.hpp" #include "duckdb/common/operator/add.hpp" #include "duckdb/common/operator/subtract.hpp" +#include "duckdb/common/atomic.hpp" namespace duckdb { @@ -42,7 +43,7 @@ struct RangeFunctionBindData : public TableFunctionData { GetParameters(values, inputs.size(), start, end, increment); if (generate_series) { // generate_series has inclusive bounds on the RHS - end += 1; + end += increment < 0 ? -1 : 1; } cardinality = Hugeint::Cast((end - start) / increment); @@ -69,10 +70,36 @@ static unique_ptr RangeFunctionBind(ClientContext &context, TableF return make_uniq(input.inputs, GENERATE_SERIES); } +//! Progress of a range used as a source (i.e. with a single input row) +struct RangeFunctionGlobalState : public GlobalTableFunctionState { + //! The number of values of the input rows, and the number of values that were generated + atomic total_values {0}; + atomic generated_values {0}; +}; + +static unique_ptr RangeFunctionGlobalInit(ClientContext &context, + TableFunctionInitInput &input) { + return make_uniq(); +} + +static double RangeFunctionProgress(ClientContext &context, const FunctionData *bind_data, + const GlobalTableFunctionState *global_state) { + auto &state = global_state->Cast(); + auto total = state.total_values.load(std::memory_order_relaxed); + if (total == 0) { + return 0; + } + auto generated = MinValue(state.generated_values.load(std::memory_order_relaxed), total); + return 100.0 * static_cast(generated) / static_cast(total); +} + struct RangeFunctionLocalState : public LocalTableFunctionState { - RangeFunctionLocalState() { + explicit RangeFunctionLocalState(optional_ptr global_state) + : global_state(global_state ? &global_state->Cast() : nullptr) { } + optional_ptr global_state; + bool initialized_row = false; idx_t current_input_row = 0; idx_t current_idx = 0; @@ -80,6 +107,8 @@ struct RangeFunctionLocalState : public LocalTableFunctionState { hugeint_t start; hugeint_t end; hugeint_t increment; + //! The number of values generated for the current input row + idx_t row_count = 0; bool empty_range = false; }; @@ -87,7 +116,7 @@ struct RangeFunctionLocalState : public LocalTableFunctionState { static unique_ptr RangeFunctionLocalInit(ExecutionContext &context, TableFunctionInitInput &input, GlobalTableFunctionState *global_state) { - return make_uniq(); + return make_uniq(global_state); } template @@ -98,6 +127,7 @@ static void GenerateRangeParameters(DataChunk &input, idx_t row_id, RangeFunctio result.start = GENERATE_SERIES ? 1 : 0; result.end = 0; result.increment = 1; + result.row_count = 0; return; } } @@ -127,6 +157,18 @@ static void GenerateRangeParameters(DataChunk &input, idx_t row_id, RangeFunctio result.end = result.end + 1; } } + if (result.empty_range) { + result.row_count = 0; + } else { + auto difference = result.end - result.start; + auto row_count = difference / result.increment; + if (difference % result.increment != 0) { + row_count += 1; + } + if (!Hugeint::TryCast(row_count, result.row_count)) { + result.row_count = NumericLimits::Maximum(); + } + } } template @@ -145,6 +187,9 @@ static OperatorResultType RangeFunction(ExecutionContext &context, TableFunction GenerateRangeParameters(input, state.current_input_row, state); state.initialized_row = true; state.current_idx = 0; + if (state.global_state) { + state.global_state->total_values.fetch_add(state.row_count, std::memory_order_relaxed); + } } if (state.empty_range) { // empty range @@ -175,6 +220,9 @@ static OperatorResultType RangeFunction(ExecutionContext &context, TableFunction state.initialized_row = false; continue; } + if (state.global_state) { + state.global_state->generated_values.fetch_add(remaining, std::memory_order_relaxed); + } return OperatorResultType::HAVE_MORE_OUTPUT; } } @@ -214,7 +262,8 @@ struct RangeDateTimeBindData : public TableFunctionData { return; } - cardinality = idx_t(delta / increment); + // a step pointing away from the end produces an empty range + cardinality = delta / increment < 0 ? 0 : idx_t(delta / increment); } idx_t cardinality; @@ -243,10 +292,44 @@ unique_ptr RangeDateTimeCardinality(ClientContext &context, cons return make_uniq(bind_data.cardinality, bind_data.cardinality); } +//! Progress of a timestamp range used as a source (i.e. with a single input row) +struct RangeDateTimeGlobalState : public GlobalTableFunctionState { + //! The bounds of the range, and the timestamp that is generated next + atomic start {0}; + atomic end {0}; + atomic current {0}; + + void Initialize(timestamp_t start_p, timestamp_t end_p) { + start.store(start_p.value, std::memory_order_relaxed); + end.store(end_p.value, std::memory_order_relaxed); + current.store(start_p.value, std::memory_order_relaxed); + } +}; + +static unique_ptr RangeDateTimeGlobalInit(ClientContext &context, + TableFunctionInitInput &input) { + return make_uniq(); +} + +static double RangeDateTimeProgress(ClientContext &context, const FunctionData *bind_data, + const GlobalTableFunctionState *global_state) { + auto &state = global_state->Cast(); + auto start = static_cast(state.start.load(std::memory_order_relaxed)); + auto range = static_cast(state.end.load(std::memory_order_relaxed)) - start; + if (range == 0) { + return 0; + } + auto fraction = (static_cast(state.current.load(std::memory_order_relaxed)) - start) / range; + return 100.0 * MaxValue(MinValue(fraction, 1.0), 0.0); +} + struct RangeDateTimeLocalState : public LocalTableFunctionState { - RangeDateTimeLocalState() { + explicit RangeDateTimeLocalState(optional_ptr global_state) + : global_state(global_state ? &global_state->Cast() : nullptr) { } + optional_ptr global_state; + bool initialized_row = false; idx_t current_input_row = 0; timestamp_t current_state; @@ -332,7 +415,7 @@ static void GenerateRangeDateTimeParameters(DataChunk &input, idx_t row_id, Rang static unique_ptr RangeDateTimeLocalInit(ExecutionContext &context, TableFunctionInitInput &input, GlobalTableFunctionState *global_state) { - return make_uniq(); + return make_uniq(global_state); } template @@ -351,6 +434,9 @@ static OperatorResultType RangeDateTimeFunction(ExecutionContext &context, Table GenerateRangeDateTimeParameters(input, state.current_input_row, state); state.initialized_row = true; state.current_state = state.start; + if (state.global_state) { + state.global_state->Initialize(state.start, state.end); + } } if (state.empty_range) { // empty range @@ -379,6 +465,9 @@ static OperatorResultType RangeDateTimeFunction(ExecutionContext &context, Table continue; } output.SetChildCardinality(size); + if (state.global_state) { + state.global_state->current.store(state.current_state.value, std::memory_order_relaxed); + } return OperatorResultType::HAVE_MORE_OUTPUT; } } @@ -390,8 +479,20 @@ static bool RangeIsRepeatable(optional_ptr) { void RangeTableFunction::RegisterFunction(BuiltinFunctions &set) { TableFunctionSet range("range"); - TableFunction range_function({LogicalType::BIGINT}, nullptr, RangeFunctionBind, nullptr, + const auto stop_only = FunctionSignature().AddPositionalOnly("stop", LogicalType::BIGINT); + const auto start_stop = FunctionSignature() + .AddPositionalOnly("start", LogicalType::BIGINT) + .AddPositionalOnly("stop", LogicalType::BIGINT); + auto start_stop_step = start_stop; + start_stop_step.AddPositionalOnly("step", LogicalType::BIGINT); + const auto timestamps = FunctionSignature() + .AddPositionalOnly("start", LogicalType::TIMESTAMP) + .AddPositionalOnly("stop", LogicalType::TIMESTAMP) + .AddPositionalOnly("step", LogicalType::INTERVAL); + + TableFunction range_function(stop_only, nullptr, RangeFunctionBind, RangeFunctionGlobalInit, RangeFunctionLocalInit); + range_function.table_scan_progress = RangeFunctionProgress; range_function.in_out_function = RangeFunction; range_function.cardinality = RangeCardinality; range_function.is_repeatable = RangeIsRepeatable; @@ -401,13 +502,14 @@ void RangeTableFunction::RegisterFunction(BuiltinFunctions &set) { // single argument range: (end) - implicit start = 0 and increment = 1 range.AddFunction(range_function); // two arguments range: (start, end) - implicit increment = 1 - range_function.GetArguments() = {LogicalType::BIGINT, LogicalType::BIGINT}; + range_function.GetSignature() = start_stop; range.AddFunction(range_function); // three arguments range: (start, end, increment) - range_function.GetArguments() = {LogicalType::BIGINT, LogicalType::BIGINT, LogicalType::BIGINT}; + range_function.GetSignature() = start_stop_step; range.AddFunction(range_function); - TableFunction range_in_out({LogicalType::TIMESTAMP, LogicalType::TIMESTAMP, LogicalType::INTERVAL}, nullptr, - RangeDateTimeBind, nullptr, RangeDateTimeLocalInit); + TableFunction range_in_out(timestamps, nullptr, RangeDateTimeBind, RangeDateTimeGlobalInit, + RangeDateTimeLocalInit); + range_in_out.table_scan_progress = RangeDateTimeProgress; range_in_out.in_out_function = RangeDateTimeFunction; range_in_out.cardinality = RangeDateTimeCardinality; range_in_out.is_repeatable = RangeIsRepeatable; @@ -420,14 +522,15 @@ void RangeTableFunction::RegisterFunction(BuiltinFunctions &set) { range_function.bind = RangeFunctionBind; range_function.in_out_function = RangeFunction; range_function.return_type = TableFunctionReturnType::SET_RETURNING_FUNCTION; - range_function.GetArguments() = {LogicalType::BIGINT}; + range_function.GetSignature() = stop_only; generate_series.AddFunction(range_function); - range_function.GetArguments() = {LogicalType::BIGINT, LogicalType::BIGINT}; + range_function.GetSignature() = start_stop; generate_series.AddFunction(range_function); - range_function.GetArguments() = {LogicalType::BIGINT, LogicalType::BIGINT, LogicalType::BIGINT}; + range_function.GetSignature() = start_stop_step; generate_series.AddFunction(range_function); - TableFunction generate_series_in_out({LogicalType::TIMESTAMP, LogicalType::TIMESTAMP, LogicalType::INTERVAL}, - nullptr, RangeDateTimeBind, nullptr, RangeDateTimeLocalInit); + TableFunction generate_series_in_out(timestamps, nullptr, RangeDateTimeBind, RangeDateTimeGlobalInit, + RangeDateTimeLocalInit); + generate_series_in_out.table_scan_progress = RangeDateTimeProgress; generate_series_in_out.in_out_function = RangeDateTimeFunction; generate_series_in_out.is_repeatable = RangeIsRepeatable; generate_series_in_out.parallelism = TableFunctionParallelism::FORCE_SINGLE_THREADED; diff --git a/src/duckdb/src/function/table/read_csv.cpp b/src/duckdb/src/function/table/read_csv.cpp index 0b02df23c..3512dfef3 100644 --- a/src/duckdb/src/function/table/read_csv.cpp +++ b/src/duckdb/src/function/table/read_csv.cpp @@ -1,3 +1,4 @@ +#include "duckdb/common/multi_file/table_function_multi_file.hpp" #include "duckdb/function/table/read_csv.hpp" #include "duckdb/catalog/catalog.hpp" #include "duckdb/function/table/read_duckdb.hpp" @@ -31,7 +32,7 @@ #include #include "duckdb/execution/operator/csv_scanner/csv_schema.hpp" #include "duckdb/common/multi_file/multi_file_function.hpp" -#include "duckdb/execution/operator/csv_scanner/csv_multi_file_info.hpp" +#include "duckdb/execution/operator/csv_scanner/csv_schema_discovery.hpp" namespace duckdb { @@ -57,53 +58,59 @@ void ReadCSVData::FinalizeRead(ClientContext &context) { } void ReadCSVTableFunction::ReadCSVAddNamedParameters(TableFunction &table_function) { - table_function.named_parameters["sep"] = LogicalType::VARCHAR; - table_function.named_parameters["delim"] = LogicalType::VARCHAR; - table_function.named_parameters["quote"] = LogicalType::VARCHAR; - table_function.named_parameters["new_line"] = LogicalType::VARCHAR; - table_function.named_parameters["escape"] = LogicalType::VARCHAR; - table_function.named_parameters["nullstr"] = LogicalType::ANY; - table_function.named_parameters["columns"] = LogicalType::ANY; - table_function.named_parameters["auto_type_candidates"] = LogicalType::ANY; - table_function.named_parameters["header"] = LogicalType::BOOLEAN; - table_function.named_parameters["auto_detect"] = LogicalType::BOOLEAN; - table_function.named_parameters["sample_size"] = LogicalType::BIGINT; - table_function.named_parameters["all_varchar"] = LogicalType::BOOLEAN; - table_function.named_parameters["dateformat"] = LogicalType::VARCHAR; - table_function.named_parameters["timestampformat"] = LogicalType::VARCHAR; - table_function.named_parameters["normalize_names"] = LogicalType::BOOLEAN; - table_function.named_parameters["compression"] = LogicalType::VARCHAR; - table_function.named_parameters["skip"] = LogicalType::BIGINT; - table_function.named_parameters["max_line_size"] = LogicalType::VARCHAR; - table_function.named_parameters["maximum_line_size"] = LogicalType::VARCHAR; - table_function.named_parameters["ignore_errors"] = LogicalType::BOOLEAN; - table_function.named_parameters["store_rejects"] = LogicalType::BOOLEAN; - table_function.named_parameters["rejects_table"] = LogicalType::VARCHAR; - table_function.named_parameters["rejects_scan"] = LogicalType::VARCHAR; - table_function.named_parameters["rejects_limit"] = LogicalType::BIGINT; - table_function.named_parameters["rejects_line_size_limit"] = LogicalType::BIGINT; - table_function.named_parameters["force_not_null"] = LogicalType::LIST(LogicalType::VARCHAR); - table_function.named_parameters["buffer_size"] = LogicalType::UBIGINT; - table_function.named_parameters["decimal_separator"] = LogicalType::VARCHAR; - table_function.named_parameters["parallel"] = LogicalType::BOOLEAN; - table_function.named_parameters["null_padding"] = LogicalType::BOOLEAN; - table_function.named_parameters["allow_quoted_nulls"] = LogicalType::BOOLEAN; - table_function.named_parameters["column_types"] = LogicalType::ANY; - table_function.named_parameters["dtypes"] = LogicalType::ANY; - table_function.named_parameters["types"] = LogicalType::ANY; - table_function.named_parameters["names"] = LogicalType::LIST(LogicalType::VARCHAR); - table_function.named_parameters["column_names"] = LogicalType::LIST(LogicalType::VARCHAR); - table_function.named_parameters["comment"] = LogicalType::VARCHAR; - table_function.named_parameters["encoding"] = LogicalType::VARCHAR; - table_function.named_parameters["strict_mode"] = LogicalType::BOOLEAN; - table_function.named_parameters["thousands"] = LogicalType::VARCHAR; - table_function.named_parameters["files_to_sniff"] = LogicalType::BIGINT; - - MultiFileReader::AddParameters(table_function); + // the reader keeps the default of every option in CSVReaderOptions - an option the call leaves out is not passed + table_function.GetSignature().WithTypedKwargs("options", [](TypedKwargs &options) { + options.Add("sep", LogicalType::VARCHAR) + .Add("delim", LogicalType::VARCHAR) + .Add("separator", LogicalType::VARCHAR) + .Add("delimiter", LogicalType::VARCHAR) + .Add("null", LogicalType::ANY) + .Add("date_format", LogicalType::VARCHAR) + .Add("timestamp_format", LogicalType::VARCHAR) + .Add("quote", LogicalType::VARCHAR) + .Add("new_line", LogicalType::VARCHAR) + .Add("escape", LogicalType::VARCHAR) + .Add("nullstr", LogicalType::ANY) + .Add("columns", LogicalType::ANY) + .Add("auto_type_candidates", LogicalType::ANY) + .Add("header", LogicalType::BOOLEAN) + .Add("auto_detect", LogicalType::BOOLEAN) + .Add("sample_size", LogicalType::BIGINT) + .Add("all_varchar", LogicalType::BOOLEAN) + .Add("dateformat", LogicalType::VARCHAR) + .Add("timestampformat", LogicalType::VARCHAR) + .Add("normalize_names", LogicalType::BOOLEAN) + .Add("compression", LogicalType::VARCHAR) + .Add("skip", LogicalType::BIGINT) + .Add("max_line_size", LogicalType::BIGINT) + .Add("maximum_line_size", LogicalType::BIGINT) + .Add("ignore_errors", LogicalType::BOOLEAN) + .Add("store_rejects", LogicalType::BOOLEAN) + .Add("rejects_table", LogicalType::VARCHAR) + .Add("rejects_scan", LogicalType::VARCHAR) + .Add("rejects_limit", LogicalType::BIGINT) + .Add("rejects_line_size_limit", LogicalType::BIGINT) + .Add("force_not_null", LogicalType::LIST(LogicalType::VARCHAR)) + .Add("buffer_size", LogicalType::BIGINT) + .Add("decimal_separator", LogicalType::VARCHAR) + .Add("parallel", LogicalType::BOOLEAN) + .Add("null_padding", LogicalType::BOOLEAN) + .Add("allow_quoted_nulls", LogicalType::BOOLEAN) + .Add("column_types", LogicalType::ANY) + .Add("dtypes", LogicalType::ANY) + .Add("types", LogicalType::ANY) + .Add("names", LogicalType::LIST(LogicalType::VARCHAR)) + .Add("column_names", LogicalType::LIST(LogicalType::VARCHAR)) + .Add("comment", LogicalType::VARCHAR) + .Add("encoding", LogicalType::VARCHAR) + .Add("strict_mode", LogicalType::BOOLEAN) + .Add("thousands", LogicalType::VARCHAR) + .Add("files_to_sniff", LogicalType::BIGINT); + }); } static void CSVReaderSerialize(Serializer &serializer, const optional_ptr bind_data_p, - const TableFunction &function) { + const BoundTableFunction &function) { throw NotImplementedException("CSVReaderSerialize not implemented"); // auto &bind_data = bind_data_p->Cast(); // auto &csv_data = bind_data.bind_data->Cast(); @@ -120,7 +127,7 @@ static void CSVReaderSerialize(Serializer &serializer, const optional_ptr CSVReaderDeserialize(Deserializer &deserializer, TableFunction &function) { +static unique_ptr CSVReaderDeserialize(Deserializer &deserializer, BoundTableFunction &function) { throw NotImplementedException("CSVReaderDeserialize not implemented"); // auto &context = deserializer.Get(); // SerializedReadCSVData serialized_data; @@ -168,13 +175,30 @@ static bool PushdownProjectionExpression(ClientContext &context, const TableFunc return true; } +//! The same, for the multi-file wrapper around read_single_csv_file - every file is bound against the schema of the +//! scan, so the pushed-down type has to be applied there too. That way the CSV reader converts to it itself, and +//! "ignore_errors" applies to the conversions that fail +static bool PushdownProjectionExpressionMultiFile(ClientContext &context, + const TableFunctionProjectionExpressionInput &input) { + if (!PushdownProjectionExpression(context, input)) { + return false; + } + auto &bind_data = input.get.bind_data->Cast(); + auto &data = bind_data.bind_data->Cast(); + const idx_t idx = input.get.GetColumnIds()[input.column_index].GetPrimaryIndex(); + if (idx < data.options.expected_types.size()) { + data.options.expected_types[idx] = input.expr.Cast().GetReturnType(); + } + return true; +} + TableFunction ReadCSVTableFunction::GetFunction() { - MultiFileFunction read_csv("read_csv"); + // the multi-file CSV reader is the single-file CSV reader wrapped into a multi-file function + auto read_csv = ReadCSVTableFunction::GetMultiFileFunction("read_csv"); read_csv.serialize = CSVReaderSerialize; read_csv.deserialize = CSVReaderDeserialize; - read_csv.projection_expression_pushdown = PushdownProjectionExpression; - ReadCSVAddNamedParameters(read_csv); - return static_cast(read_csv); + read_csv.projection_expression_pushdown = PushdownProjectionExpressionMultiFile; + return read_csv; } TableFunction ReadCSVTableFunction::GetAutoFunction() { @@ -186,6 +210,10 @@ TableFunction ReadCSVTableFunction::GetAutoFunction() { void ReadCSVTableFunction::RegisterFunction(BuiltinFunctions &set) { set.AddFunction(MultiFileReader::CreateFunctionSet(ReadCSVTableFunction::GetFunction())); set.AddFunction(MultiFileReader::CreateFunctionSet(ReadCSVTableFunction::GetAutoFunction())); + // the single-file CSV reader that read_csv is built on + TableFunctionSet single_file_set("read_single_csv_file"); + single_file_set.AddFunction(ReadCSVTableFunction::GetSingleFileFunction()); + set.AddFunction(std::move(single_file_set)); } unique_ptr ReadCSVReplacement(ClientContext &context, ReplacementScanInput &input, diff --git a/src/duckdb/src/function/table/read_duckdb.cpp b/src/duckdb/src/function/table/read_duckdb.cpp index 86be08fd5..ae436a559 100644 --- a/src/duckdb/src/function/table/read_duckdb.cpp +++ b/src/duckdb/src/function/table/read_duckdb.cpp @@ -113,6 +113,8 @@ class DuckDBReader : public BaseFileReader { unique_ptr bind_data; unique_ptr global_state; atomic finished; + //! The column indexes to scan - the row id is scanned if no columns are projected + vector scan_column_indexes; idx_t column_count; Identifier schema_name; Identifier table_name; @@ -123,6 +125,8 @@ struct DuckDBReadGlobalState : GlobalTableFunctionState {}; struct DuckDBReadLocalState : LocalTableFunctionState { unique_ptr local_state; shared_ptr attached_database; + //! The chunk to scan into when no columns are projected (we then scan the row ids) + DataChunk row_id_chunk; }; string DuckDBFileReaderOptions::GetCandidates(const vector> &tables) const { @@ -234,7 +238,8 @@ DuckDBReader::DuckDBReader(ClientContext &context_p, OpenFileInfo file_p, const } auto &table = tables[0].get(); for (auto &col : table.GetColumns().Logical()) { - columns.emplace_back(col.Name().GetIdentifierName(), col.Type()); + // create the child column definitions as well, so that STRUCT children are mapped by name + columns.push_back(MultiFileColumnDefinition::CreateFromNameAndType(col.Name(), col.Type())); } column_count = columns.size(); schema_name = table.ParentSchema().name; @@ -250,10 +255,9 @@ AttachedDatabase &DuckDBReader::GetAttachedDatabase() { auto &db_manager = DatabaseManager::Get(context); AttachInfo info; info.path = file.path; - // use invalid UTF-8 so that a conflicting database name cannot be attached by a user - info.name = Identifier("\x80__duckdb_reader_" + info.path); - - info.on_conflict = OnCreateConflict::IGNORE_ON_CONFLICT; + // a unique name per reader: each reader detaches its own attachment when it finishes + info.name = GenerateInternalName("__duckdb_reader_"); + info.on_conflict = OnCreateConflict::ERROR_ON_CONFLICT; unordered_map attach_kv; AttachOptions attach_options(attach_kv, AccessMode::READ_ONLY); attach_options.visibility = AttachVisibility::HIDDEN; @@ -297,16 +301,24 @@ bool DuckDBReader::TryInitializeScan(ClientContext &context, GlobalTableFunction } } + scan_column_indexes = column_indexes; + if (scan_column_indexes.empty()) { + // the table scan requires at least one column - scan the row id if no columns are projected + scan_column_indexes.emplace_back(COLUMN_IDENTIFIER_ROW_ID); + } // initialize the scan over this table - TableFunctionInitInput input(bind_data.get(), column_indexes, vector(), filters.get()); + TableFunctionInitInput input(bind_data.get(), scan_column_indexes, vector(), filters.get()); global_state = scan_function.init_global(context, input); } AssignSharedPointer(lstate.attached_database, db_wrapper); // initialize the local scan ThreadContext thread(context); ExecutionContext exec_context(context, thread, nullptr); - TableFunctionInitInput input(bind_data.get(), column_indexes, vector(), filters.get()); + TableFunctionInitInput input(bind_data.get(), scan_column_indexes, vector(), filters.get()); lstate.local_state = scan_function.init_local(exec_context, input, global_state.get()); + if (column_indexes.empty() && lstate.row_id_chunk.ColumnCount() == 0) { + lstate.row_id_chunk.Initialize(context, {LogicalType::ROW_TYPE}); + } return true; } @@ -320,7 +332,14 @@ AsyncResult DuckDBReader::Scan(ClientContext &context, GlobalTableFunctionState } else { input.async_result = AsyncResultType::IMPLICIT; input.results_execution_mode = AsyncResultsExecutionMode::TASK_EXECUTOR; - scan_function.function(context, input, chunk); + if (column_indexes.empty()) { + // no columns are projected - scan the row ids and only emit the count + lstate.row_id_chunk.Reset(); + scan_function.function(context, input, lstate.row_id_chunk); + chunk.SetChildCardinality(lstate.row_id_chunk.size()); + } else { + scan_function.function(context, input, chunk); + } if (input.async_result.GetResultType() == AsyncResultType::BLOCKED) { return std::move(input.async_result); @@ -384,6 +403,9 @@ bool DuckDBMultiFileInfo::ParseCopyOption(ClientContext &context, const Identifi bool DuckDBMultiFileInfo::ParseOption(ClientContext &context, const Identifier &key, const Value &val, MultiFileOptions &file_options, BaseFileReaderOptions &options_p) { auto &options = options_p.Cast(); + if ((key == "schema_name" || key == "table_name") && val.IsNull()) { + throw BinderException("read_duckdb parameter %s cannot be NULL", key); + } if (key == "schema_name") { options.schema_name = Identifier(StringValue::Get(val)); return true; @@ -500,10 +522,10 @@ void DuckDBMultiFileInfo::GetVirtualColumns(ClientContext &, MultiFileBindData & } void ReadDuckDBAddNamedParameters(TableFunction &table_function) { - table_function.named_parameters["schema_name"] = LogicalType::VARCHAR; - table_function.named_parameters["table_name"] = LogicalType::VARCHAR; - - MultiFileReader::AddParameters(table_function); + // extends the options MultiFileFunction declares + table_function.GetSignature().ExtendTypedKwargs([](TypedKwargs &options) { + options.Add("schema_name", LogicalType::VARCHAR).Add("table_name", LogicalType::VARCHAR); + }); } static vector DuckDBGetRowIdColumns(ClientContext &, optional_ptr) { diff --git a/src/duckdb/src/function/table/read_file.cpp b/src/duckdb/src/function/table/read_file.cpp index d04d8423c..2b536e9f8 100644 --- a/src/duckdb/src/function/table/read_file.cpp +++ b/src/duckdb/src/function/table/read_file.cpp @@ -110,10 +110,11 @@ DirectMultiFileInfo::InitializeGlobalState(ClientContext &context, MultiFile } for (const auto &column_id : column_ids) { - // For everything except the 'file' name column, we need to open the file - if (column_id != ReadFileBindData::FILE_NAME_COLUMN && column_id != COLUMN_IDENTIFIER_ROW_ID) { + if (column_id == ReadFileBindData::FILE_CONTENT_COLUMN) { result->requires_file_open = true; - break; + } else if (column_id == ReadFileBindData::FILE_SIZE_COLUMN || + column_id == ReadFileBindData::FILE_LAST_MODIFIED_COLUMN) { + result->requires_file_metadata = true; } } @@ -186,13 +187,9 @@ struct ReadTextOperation { template static TableFunction GetFunction() { - MultiFileFunction> table_function(OP::NAME); - // Erase extra multi file reader options - table_function.named_parameters.erase("filename"); - table_function.named_parameters.erase("hive_partitioning"); - table_function.named_parameters.erase("union_by_name"); - table_function.named_parameters.erase("hive_types"); - table_function.named_parameters.erase("hive_types_autocast"); + // this function reads one file per call, so it takes none of the options that describe combining several + MultiFileFunction> table_function(OP::NAME, + MultiFileReader::MultiFileParameters::ALLOW_EMPTY_ONLY); return table_function; } diff --git a/src/duckdb/src/function/table/repeat.cpp b/src/duckdb/src/function/table/repeat.cpp index d6afd5342..ff9384d53 100644 --- a/src/duckdb/src/function/table/repeat.cpp +++ b/src/duckdb/src/function/table/repeat.cpp @@ -1,5 +1,6 @@ #include "duckdb/function/table/range.hpp" #include "duckdb/common/algorithm.hpp" +#include "duckdb/common/atomic.hpp" namespace duckdb { @@ -14,7 +15,7 @@ struct RepeatFunctionData : public TableFunctionData { struct RepeatOperatorData : public GlobalTableFunctionState { RepeatOperatorData() : current_count(0) { } - idx_t current_count; + atomic current_count; }; static unique_ptr RepeatBind(ClientContext &context, TableFunctionBindInput &input, @@ -47,14 +48,30 @@ static void RepeatFunction(ClientContext &context, TableFunctionInput &data_p, D state.current_count += remaining; } +static double RepeatProgress(ClientContext &, const FunctionData *bind_data_p, + const GlobalTableFunctionState *state_p) { + if (!state_p) { + return -1; + } + auto &bind_data = bind_data_p->Cast(); + auto &state = state_p->Cast(); + return bind_data.target_count == 0 ? 100 + : 100.0 * double(state.current_count.load()) / double(bind_data.target_count); +} + static unique_ptr RepeatCardinality(ClientContext &context, const FunctionData *bind_data_p) { auto &bind_data = bind_data_p->Cast(); return make_uniq(bind_data.target_count, bind_data.target_count); } void RepeatTableFunction::RegisterFunction(BuiltinFunctions &set) { - TableFunction repeat("repeat", {LogicalType::ANY, LogicalType::BIGINT}, RepeatFunction, RepeatBind, RepeatInit); + TableFunction repeat("repeat", + FunctionSignature() + .AddPositionalOnly("value", LogicalType::ANY) + .AddPositionalOnly("count", LogicalType::BIGINT), + RepeatFunction, RepeatBind, RepeatInit); repeat.cardinality = RepeatCardinality; + repeat.table_scan_progress = RepeatProgress; set.AddFunction(repeat); } diff --git a/src/duckdb/src/function/table/repeat_row.cpp b/src/duckdb/src/function/table/repeat_row.cpp index 9dd93a11e..d61d00596 100644 --- a/src/duckdb/src/function/table/repeat_row.cpp +++ b/src/duckdb/src/function/table/repeat_row.cpp @@ -1,5 +1,6 @@ #include "duckdb/function/table/range.hpp" #include "duckdb/common/algorithm.hpp" +#include "duckdb/common/atomic.hpp" namespace duckdb { @@ -15,7 +16,7 @@ struct RepeatRowFunctionData : public TableFunctionData { struct RepeatRowOperatorData : public GlobalTableFunctionState { RepeatRowOperatorData() : current_count(0) { } - idx_t current_count; + atomic current_count; }; static unique_ptr RepeatRowBind(ClientContext &context, TableFunctionBindInput &input, @@ -25,14 +26,18 @@ static unique_ptr RepeatRowBind(ClientContext &context, TableFunct return_types.push_back(inputs[input_idx].type()); names.emplace_back("column" + std::to_string(input_idx)); } - auto entry = input.named_parameters.find("num_rows"); - if (entry == input.named_parameters.end()) { - throw BinderException("repeat_rows requires num_rows to be specified"); - } + auto &num_rows = input.named_parameters.at("num_rows"); if (inputs.empty()) { throw BinderException("repeat_rows requires at least one column to be specified"); } - return make_uniq(inputs, NumericCast(entry->second.GetValue())); + if (num_rows.IsNull()) { + throw BinderException("num_rows should be an integer value >= 0"); + } + auto repeat_rows = num_rows.GetValue(); + if (repeat_rows < 0) { + throw BinderException("num_rows cannot be less than zero"); + } + return make_uniq(inputs, NumericCast(repeat_rows)); } static unique_ptr RepeatRowInit(ClientContext &context, TableFunctionInitInput &input) { @@ -51,6 +56,17 @@ static void RepeatRowFunction(ClientContext &context, TableFunctionInput &data_p state.current_count += remaining; } +static double RepeatRowProgress(ClientContext &, const FunctionData *bind_data_p, + const GlobalTableFunctionState *state_p) { + if (!state_p) { + return -1; + } + auto &bind_data = bind_data_p->Cast(); + auto &state = state_p->Cast(); + return bind_data.target_count == 0 ? 100 + : 100.0 * double(state.current_count.load()) / double(bind_data.target_count); +} + static unique_ptr RepeatRowCardinality(ClientContext &context, const FunctionData *bind_data_p) { auto &bind_data = bind_data_p->Cast(); return make_uniq(bind_data.target_count, bind_data.target_count); @@ -58,9 +74,11 @@ static unique_ptr RepeatRowCardinality(ClientContext &context, c void RepeatRowTableFunction::RegisterFunction(BuiltinFunctions &set) { TableFunction repeat_row("repeat_row", {}, RepeatRowFunction, RepeatRowBind, RepeatRowInit); - repeat_row.SetVarArgs(LogicalType::ANY); - repeat_row.named_parameters["num_rows"] = LogicalType::BIGINT; + // a positional list, so it declares "*args" and no "**kwargs" - an argument named after no parameter is an error + repeat_row.GetSignature().AddArgs("args", LogicalType::ANY); + repeat_row.GetSignature().AddKeywordOnly("num_rows", LogicalType::BIGINT); repeat_row.cardinality = RepeatRowCardinality; + repeat_row.table_scan_progress = RepeatRowProgress; set.AddFunction(repeat_row); } diff --git a/src/duckdb/src/function/table/sniff_csv.cpp b/src/duckdb/src/function/table/sniff_csv.cpp index 6933ebeca..a4d30aedc 100644 --- a/src/duckdb/src/function/table/sniff_csv.cpp +++ b/src/duckdb/src/function/table/sniff_csv.cpp @@ -7,7 +7,7 @@ #include "duckdb/main/client_context.hpp" #include "duckdb/function/table/range.hpp" #include "duckdb/execution/operator/csv_scanner/csv_file_handle.hpp" -#include "duckdb/execution/operator/csv_scanner/csv_multi_file_info.hpp" +#include "duckdb/execution/operator/csv_scanner/csv_schema_discovery.hpp" #include "duckdb/function/table/read_csv.hpp" namespace duckdb { @@ -48,14 +48,17 @@ static unique_ptr CSVSniffBind(ClientContext &context, TableFuncti throw InvalidInputException("sniff_csv function does not accept auto_detect variable set to false"); } // otherwise remove it - input.named_parameters.erase("auto_detect"); + input.named_parameters.erase(it); } // If we want to force the match of the sniffer it = input.named_parameters.find("force_match"); if (it != input.named_parameters.end()) { + if (it->second.IsNull()) { + throw BinderException("\"%s\" expects a non-null boolean value (e.g. TRUE or 1)", it->first); + } result->force_match = it->second.GetValue(); - input.named_parameters.erase("force_match"); + input.named_parameters.erase(it); } MultiFileOptions file_options; result->options.FromNamedParameters(input.named_parameters, context, file_options); @@ -319,10 +322,13 @@ static void CSVSniffFunction(ClientContext &context, TableFunctionInput &data_p, } void CSVSnifferFunction::RegisterFunction(BuiltinFunctions &set) { - TableFunction csv_sniffer("sniff_csv", {LogicalType::VARCHAR}, CSVSniffFunction, CSVSniffBind, CSVSniffInitGlobal); + TableFunction csv_sniffer("sniff_csv", FunctionSignature().AddPositionalOnly("path", LogicalType::VARCHAR), + CSVSniffFunction, CSVSniffBind, CSVSniffInitGlobal); // Accept same options as the actual csv reader ReadCSVTableFunction::ReadCSVAddNamedParameters(csv_sniffer); - csv_sniffer.named_parameters["force_match"] = LogicalType::BOOLEAN; + MultiFileReader::AddParameters(csv_sniffer); + csv_sniffer.GetSignature().ExtendTypedKwargs( + [](TypedKwargs &options) { options.Add("force_match", LogicalType::BOOLEAN); }); set.AddFunction(csv_sniffer); } } // namespace duckdb diff --git a/src/duckdb/src/function/table/summary.cpp b/src/duckdb/src/function/table/summary.cpp index 8c93dfca8..8660cbe93 100644 --- a/src/duckdb/src/function/table/summary.cpp +++ b/src/duckdb/src/function/table/summary.cpp @@ -42,7 +42,8 @@ static OperatorResultType SummaryFunction(ExecutionContext &context, TableFuncti } void SummaryTableFunction::RegisterFunction(BuiltinFunctions &set) { - TableFunction summary_function("summary", {LogicalType::TABLE}, nullptr, SummaryFunctionBind); + TableFunction summary_function("summary", FunctionSignature().AddPositionalOnly("input", LogicalType::TABLE), + nullptr, SummaryFunctionBind); summary_function.in_out_function = SummaryFunction; set.AddFunction(summary_function); } diff --git a/src/duckdb/src/function/table/system/create_external_resource.cpp b/src/duckdb/src/function/table/system/create_external_resource.cpp index f848a3a5d..65a585e5f 100644 --- a/src/duckdb/src/function/table/system/create_external_resource.cpp +++ b/src/duckdb/src/function/table/system/create_external_resource.cpp @@ -123,7 +123,12 @@ static unique_ptr CreateExternalResourceBind(ClientContext &contex } else if (key == "resource_name" && !np.second.IsNull()) { result->resource_name = StringValue::Get(np.second); } else if (key == "handle" && !np.second.IsNull()) { - result->adopt_handle = np.second; + // declared ANY so that a STRUCT of the handle's fields is accepted as readily as a MAP - anything + // that is not convertible to the handle's shape is reported here + result->adopt_handle = + np.second.type().id() == LogicalTypeId::MAP + ? np.second + : np.second.DefaultCastAs(LogicalType::MAP(LogicalType::VARCHAR, LogicalType::VARCHAR)); } else if (key == "teardown_on_failure" && !np.second.IsNull()) { result->teardown_on_failure = BooleanValue::Get(np.second); } else if (key == "timeout_seconds" && !np.second.IsNull()) { @@ -206,7 +211,12 @@ static void CreateExternalResourceFunction(ClientContext &context, TableFunction throw InvalidInputException("create_external_resource: create function \"%s\" returned no rows", type->create_function); } - handle = RequireResourceMap(result->GetValue(0, 0), type->create_function, "handle"); + handle = RequireResourceMap(result->Collection().GetRows().GetValue(0, 0), type->create_function, "handle"); + if (handle.IsNull()) { + // the handle identifies the resource for status and teardown - without one it can never be destroyed + throw InvalidInputException("create_external_resource: create function \"%s\" returned a NULL handle", + type->create_function); + } LogExternalResourceOperation(context, bind_data.type_name, bind_data.resource_name, "create", string(), NoExtraInfo()); } catch (std::exception &ex) { @@ -286,14 +296,15 @@ static void CreateExternalResourceFunction(ClientContext &context, TableFunction throw InvalidInputException("create_external_resource: status function \"%s\" returned no rows", type->status_function); } - auto state_val = sres->GetValue(0, 0); + auto status_rows = sres->Collection().GetRows(); + auto state_val = status_rows.GetValue(0, 0); auto status_state = state_val.IsNull() ? string() : state_val.ToString(); if (status_state == "failed") { throw IOException("create_external_resource: resource \"%s\" reported state 'failed'", bind_data.type_name); } if (status_state == "ready") { - status_result = RequireResourceMap(sres->GetValue(1, 0), type->status_function, "result"); + status_result = RequireResourceMap(status_rows.GetValue(1, 0), type->status_function, "result"); ready = true; } else if (has_deadline && std::chrono::steady_clock::now() >= deadline) { throw IOException("create_external_resource: timed out awaiting readiness for \"%s\" (last state '%s')", @@ -362,14 +373,16 @@ static void CreateExternalResourceFunction(ClientContext &context, TableFunction } void CreateExternalResourceFun::RegisterFunction(BuiltinFunctions &set) { - TableFunction fn("create_external_resource", {LogicalType::VARCHAR}, CreateExternalResourceFunction, - CreateExternalResourceBind, CreateExternalResourceInit); - fn.named_parameters["params"] = LogicalType::MAP(LogicalType::VARCHAR, LogicalType::VARCHAR); - fn.named_parameters["resource_name"] = LogicalType::VARCHAR; - fn.named_parameters["handle"] = LogicalType::MAP(LogicalType::VARCHAR, LogicalType::VARCHAR); - fn.named_parameters["teardown_on_failure"] = LogicalType::BOOLEAN; - fn.named_parameters["timeout_seconds"] = LogicalType::BIGINT; - fn.named_parameters["poll_interval_seconds"] = LogicalType::BIGINT; + TableFunction fn("create_external_resource", FunctionSignature().AddPositionalOnly("type", LogicalType::VARCHAR), + CreateExternalResourceFunction, CreateExternalResourceBind, CreateExternalResourceInit); + fn.GetSignature().WithTypedKwargs("options", [](TypedKwargs &options) { + options.Add("params", LogicalType::MAP(LogicalType::VARCHAR, LogicalType::VARCHAR)) + .Add("resource_name", LogicalType::VARCHAR) + .Add("handle", LogicalType::ANY) + .Add("teardown_on_failure", LogicalType::BOOLEAN) + .Add("timeout_seconds", LogicalType::BIGINT) + .Add("poll_interval_seconds", LogicalType::BIGINT); + }); set.AddFunction(fn); } @@ -437,7 +450,10 @@ static void DestroyExternalResourceFunction(ClientContext &context, TableFunctio } void DestroyExternalResourceFun::RegisterFunction(BuiltinFunctions &set) { - set.AddFunction(TableFunction("destroy_external_resource", {LogicalType::VARCHAR, LogicalType::ANY}, + set.AddFunction(TableFunction("destroy_external_resource", + FunctionSignature() + .AddPositionalOnly("deleter_function", LogicalType::VARCHAR) + .AddPositionalOnly("payload", LogicalType::ANY), DestroyExternalResourceFunction, DestroyExternalResourceBind, DestroyExternalResourceInit)); } diff --git a/src/duckdb/src/function/table/system/duckdb_columns.cpp b/src/duckdb/src/function/table/system/duckdb_columns.cpp index 8e5206b5e..5f6bf7e9d 100644 --- a/src/duckdb/src/function/table/system/duckdb_columns.cpp +++ b/src/duckdb/src/function/table/system/duckdb_columns.cpp @@ -1,4 +1,5 @@ #include "duckdb/function/table/system_functions.hpp" +#include "duckdb/common/atomic.hpp" #include "duckdb/catalog/catalog.hpp" #include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp" @@ -19,7 +20,7 @@ struct DuckDBColumnsData : public GlobalTableFunctionState { } vector> entries; - idx_t offset; + atomic offset; idx_t column_offset; }; @@ -414,12 +415,23 @@ static void DuckDBColumnsFunction(ClientContext &context, TableFunctionInput &da } } // WriteColumns appends to the child vectors - record the resulting cardinality on the chunk - data.offset = next; + data.offset.store(next, std::memory_order_relaxed); data.column_offset = column_offset; } +static double DuckDBColumnsProgress(ClientContext &context, const FunctionData *bind_data, + const GlobalTableFunctionState *global_state) { + auto &data = global_state->Cast(); + if (data.entries.empty()) { + return 100.0; + } + return 100.0 * static_cast(data.offset) / static_cast(data.entries.size()); +} + void DuckDBColumnsFun::RegisterFunction(BuiltinFunctions &set) { - set.AddFunction(TableFunction("duckdb_columns", {}, DuckDBColumnsFunction, DuckDBColumnsBind, DuckDBColumnsInit)); + TableFunction columns("duckdb_columns", {}, DuckDBColumnsFunction, DuckDBColumnsBind, DuckDBColumnsInit); + columns.table_scan_progress = DuckDBColumnsProgress; + set.AddFunction(columns); } } // namespace duckdb diff --git a/src/duckdb/src/function/table/system/duckdb_dialects.cpp b/src/duckdb/src/function/table/system/duckdb_dialects.cpp index 844081ab6..058b3d233 100644 --- a/src/duckdb/src/function/table/system/duckdb_dialects.cpp +++ b/src/duckdb/src/function/table/system/duckdb_dialects.cpp @@ -2,7 +2,7 @@ #include "duckdb/main/client_context.hpp" #include "duckdb/main/extension_callback_manager.hpp" -#include "duckdb/parser/dialect_extension.hpp" +#include "duckdb/parser/peg/dialect_extension.hpp" namespace duckdb { @@ -21,7 +21,7 @@ static unique_ptr DuckDBDialectsBind(ClientContext &context, Table static unique_ptr DuckDBDialectsInit(ClientContext &context, TableFunctionInitInput &input) { auto result = make_uniq(); for (auto &dialect : ExtensionCallbackManager::Get(context).DialectExtensions()) { - result->dialects.push_back(dialect.name); + result->dialects.push_back(dialect->Name()); } return std::move(result); } diff --git a/src/duckdb/src/function/table/system/duckdb_extensions.cpp b/src/duckdb/src/function/table/system/duckdb_extensions.cpp index 25a05438d..0c660e784 100644 --- a/src/duckdb/src/function/table/system/duckdb_extensions.cpp +++ b/src/duckdb/src/function/table/system/duckdb_extensions.cpp @@ -84,11 +84,9 @@ unique_ptr DuckDBExtensionsInit(ClientContext &context auto extension = ExtensionHelper::GetDefaultExtension(i); ExtensionInformation info; info.name = extension.name; - info.installed = extension.statically_loaded; + info.installed = false; info.loaded = false; - info.file_path = extension.statically_loaded ? "(BUILT-IN)" : string(); - info.install_mode = - extension.statically_loaded ? ExtensionInstallMode::STATICALLY_LINKED : ExtensionInstallMode::NOT_INSTALLED; + info.install_mode = ExtensionInstallMode::NOT_INSTALLED; info.description = extension.description; for (idx_t k = 0; k < alias_count; k++) { auto alias = ExtensionHelper::GetInternalExtensionAlias(k); @@ -99,6 +97,18 @@ unique_ptr DuckDBExtensionsInit(ClientContext &context installed_extensions[info.name] = std::move(info); } + // Extensions linked into this binary are built in, whether or not the default list knows them + for (auto &linked : DBConfig::GetConfig(db).linked_extensions) { + if (!linked.load) { + continue; + } + auto &info = installed_extensions[linked.name]; + info.name = linked.name; + info.installed = true; + info.file_path = "(BUILT-IN)"; + info.install_mode = ExtensionInstallMode::STATICALLY_LINKED; + } + // Secondly we scan all installed extensions and their install info #ifndef WASM_LOADABLE_EXTENSIONS auto ext_directories = ExtensionHelper::GetExtensionDirectoryPath(context); diff --git a/src/duckdb/src/function/table/system/duckdb_external_file_cache.cpp b/src/duckdb/src/function/table/system/duckdb_external_file_cache.cpp index 7085874b6..8e13a4078 100644 --- a/src/duckdb/src/function/table/system/duckdb_external_file_cache.cpp +++ b/src/duckdb/src/function/table/system/duckdb_external_file_cache.cpp @@ -1,4 +1,5 @@ #include "duckdb/function/table/system_functions.hpp" +#include "duckdb/common/atomic.hpp" #include "duckdb/storage/external_file_cache/external_file_cache.hpp" namespace duckdb { @@ -9,6 +10,8 @@ struct DuckDBExternalFileCacheData : public GlobalTableFunctionState { vector entries; idx_t offset; + //! The offset, published once per chunk (for progress) + atomic progress_offset {0}; }; static unique_ptr DuckDBExternalFileCacheBind(ClientContext &context, TableFunctionBindInput &input, @@ -69,11 +72,24 @@ void DuckDBExternalFileCacheFunction(ClientContext &context, TableFunctionInput spilled.Append(Value::BOOLEAN(entry.spilled)); count++; } + data.progress_offset.store(data.offset, std::memory_order_relaxed); +} + +static double DuckDBExternalFileCacheProgress(ClientContext &context, const FunctionData *bind_data, + const GlobalTableFunctionState *global_state) { + auto &data = global_state->Cast(); + if (data.entries.empty()) { + return 100.0; + } + auto offset = data.progress_offset.load(std::memory_order_relaxed); + return 100.0 * static_cast(offset) / static_cast(data.entries.size()); } void DuckDBExternalFileCacheFun::RegisterFunction(BuiltinFunctions &set) { - set.AddFunction(TableFunction("duckdb_external_file_cache", {}, DuckDBExternalFileCacheFunction, - DuckDBExternalFileCacheBind, DuckDBExternalFileCacheInit)); + TableFunction function("duckdb_external_file_cache", {}, DuckDBExternalFileCacheFunction, + DuckDBExternalFileCacheBind, DuckDBExternalFileCacheInit); + function.table_scan_progress = DuckDBExternalFileCacheProgress; + set.AddFunction(function); } } // namespace duckdb diff --git a/src/duckdb/src/function/table/system/duckdb_functions.cpp b/src/duckdb/src/function/table/system/duckdb_functions.cpp index daca6a14f..112780a31 100644 --- a/src/duckdb/src/function/table/system/duckdb_functions.cpp +++ b/src/duckdb/src/function/table/system/duckdb_functions.cpp @@ -1,4 +1,5 @@ #include "duckdb/function/table/system_functions.hpp" +#include "duckdb/common/atomic.hpp" #include "duckdb/catalog/catalog.hpp" #include "duckdb/catalog/catalog_entry/schema_catalog_entry.hpp" @@ -28,6 +29,8 @@ struct DuckDBFunctionsData : public GlobalTableFunctionState { vector> entries; idx_t offset; idx_t offset_in_entry; + //! The offset, published once per chunk (for progress) + atomic progress_offset {0}; }; static unique_ptr DuckDBFunctionsBind(ClientContext &context, TableFunctionBindInput &input, @@ -137,6 +140,15 @@ Value FunctionStabilityToValue(FunctionStability stability) { } } +//! The legacy "varargs" column cannot tell "*args" and "**kwargs" apart - report whichever the function has +Value VariadicTypeValue(const FunctionSignature &signature) { + auto variadic = signature.GetArgs(); + if (!variadic) { + variadic = signature.GetKwargs(); + } + return variadic ? Value(variadic->GetType().ToString()) : Value(); +} + struct ScalarFunctionExtractor { static idx_t FunctionCount(ScalarFunctionCatalogEntry &entry) { return entry.functions.Size(); @@ -153,7 +165,9 @@ struct ScalarFunctionExtractor { static vector GetParameters(ScalarFunctionCatalogEntry &entry, idx_t offset) { vector results; for (auto ¶m : entry.functions.GetFunctionByOffset(offset)->GetSignature().GetParameters()) { - results.emplace_back(param.GetName()); + if (!param.IsVariadic()) { + results.emplace_back(param.GetName()); + } } return results; } @@ -162,7 +176,9 @@ struct ScalarFunctionExtractor { vector results; const auto &fun = *entry.functions.GetFunctionByOffset(offset); for (idx_t i = 0; i < fun.GetSignature().GetParameterCount(); i++) { - results.emplace_back(fun.GetSignature().GetParameter(i).GetType().ToString()); + if (!fun.GetSignature().GetParameter(i).IsVariadic()) { + results.emplace_back(fun.GetSignature().GetParameter(i).GetType().ToString()); + } } return Value::LIST(LogicalType::VARCHAR, std::move(results)); } @@ -171,14 +187,15 @@ struct ScalarFunctionExtractor { const auto &fun = *entry.functions.GetFunctionByOffset(offset); vector results; for (idx_t i = 0; i < fun.GetSignature().GetParameterCount(); i++) { - results.emplace_back(fun.GetSignature().GetParameter(i).GetType()); + if (!fun.GetSignature().GetParameter(i).IsVariadic()) { + results.emplace_back(fun.GetSignature().GetParameter(i).GetType()); + } } return results; } static Value GetVarArgs(ScalarFunctionCatalogEntry &entry, idx_t offset) { - const auto &fun = *entry.functions.GetFunctionByOffset(offset); - return !fun.HasVarArgs() ? Value() : Value(fun.GetVarArgs().ToString()); + return VariadicTypeValue(entry.functions.GetFunctionByOffset(offset)->GetSignature()); } static Value GetMacroDefinition(ScalarFunctionCatalogEntry &entry, idx_t offset) { @@ -211,7 +228,9 @@ struct WindowFunctionExtractor { static vector GetParameters(WindowFunctionCatalogEntry &entry, idx_t offset) { vector results; for (auto ¶m : entry.functions.GetFunctionByOffset(offset)->GetSignature().GetParameters()) { - results.emplace_back(param.GetName()); + if (!param.IsVariadic()) { + results.emplace_back(param.GetName()); + } } return results; } @@ -220,7 +239,9 @@ struct WindowFunctionExtractor { vector results; const auto &fun = *entry.functions.GetFunctionByOffset(offset); for (idx_t i = 0; i < fun.GetSignature().GetParameterCount(); i++) { - results.emplace_back(fun.GetSignature().GetParameter(i).GetType().ToString()); + if (!fun.GetSignature().GetParameter(i).IsVariadic()) { + results.emplace_back(fun.GetSignature().GetParameter(i).GetType().ToString()); + } } return Value::LIST(LogicalType::VARCHAR, std::move(results)); } @@ -229,7 +250,9 @@ struct WindowFunctionExtractor { const auto &fun = *entry.functions.GetFunctionByOffset(offset); vector results; for (idx_t i = 0; i < fun.GetSignature().GetParameterCount(); i++) { - results.emplace_back(fun.GetSignature().GetParameter(i).GetType()); + if (!fun.GetSignature().GetParameter(i).IsVariadic()) { + results.emplace_back(fun.GetSignature().GetParameter(i).GetType()); + } } return results; } @@ -267,7 +290,9 @@ struct AggregateFunctionExtractor { static vector GetParameters(AggregateFunctionCatalogEntry &entry, idx_t offset) { vector results; for (auto ¶m : entry.functions.GetFunctionByOffset(offset)->GetSignature().GetParameters()) { - results.emplace_back(param.GetName()); + if (!param.IsVariadic()) { + results.emplace_back(param.GetName()); + } } return results; } @@ -276,7 +301,9 @@ struct AggregateFunctionExtractor { vector results; const auto &fun = *entry.functions.GetFunctionByOffset(offset); for (idx_t i = 0; i < fun.GetSignature().GetParameterCount(); i++) { - results.emplace_back(fun.GetSignature().GetParameter(i).GetType().ToString()); + if (!fun.GetSignature().GetParameter(i).IsVariadic()) { + results.emplace_back(fun.GetSignature().GetParameter(i).GetType().ToString()); + } } return Value::LIST(LogicalType::VARCHAR, std::move(results)); } @@ -285,14 +312,15 @@ struct AggregateFunctionExtractor { const auto &fun = *entry.functions.GetFunctionByOffset(offset); vector results; for (idx_t i = 0; i < fun.GetSignature().GetParameterCount(); i++) { - results.emplace_back(fun.GetSignature().GetParameter(i).GetType()); + if (!fun.GetSignature().GetParameter(i).IsVariadic()) { + results.emplace_back(fun.GetSignature().GetParameter(i).GetType()); + } } return results; } static Value GetVarArgs(AggregateFunctionCatalogEntry &entry, idx_t offset) { - const auto &fun = *entry.functions.GetFunctionByOffset(offset); - return !fun.HasVarArgs() ? Value() : Value(fun.GetVarArgs().ToString()); + return VariadicTypeValue(entry.functions.GetFunctionByOffset(offset)->GetSignature()); } static Value GetMacroDefinition(AggregateFunctionCatalogEntry &entry, idx_t offset) { @@ -437,6 +465,17 @@ struct TableMacroExtractor { } }; +//! The options a "**kwargs" parameter declares are listed after the parameters, as they are passed by name like them +static void AddOptions(const FunctionSignature &signature, vector &results, bool types) { + auto option_schema = signature.GetTypedKwargs(); + if (!option_schema) { + return; + } + for (auto &option : option_schema->GetOptions()) { + results.emplace_back(types ? Value(option.type.ToString()) : Value(option.name)); + } +} + struct TableFunctionExtractor { static idx_t FunctionCount(TableFunctionCatalogEntry &entry) { return entry.functions.Size(); @@ -453,12 +492,13 @@ struct TableFunctionExtractor { static vector GetParameters(TableFunctionCatalogEntry &entry, idx_t offset) { vector results; const auto &fun = *entry.functions.GetFunctionByOffset(offset); - for (idx_t i = 0; i < fun.GetArguments().size(); i++) { - results.emplace_back("col" + to_string(i)); - } - for (auto ¶m : fun.named_parameters) { - results.emplace_back(param.first); + // the variadic parameters are reported in the "varargs" column instead + for (auto ¶m : fun.GetSignature().GetParameters()) { + if (!param.IsVariadic()) { + results.emplace_back(param.GetName()); + } } + AddOptions(fun.GetSignature(), results, false); return results; } @@ -466,23 +506,28 @@ struct TableFunctionExtractor { vector results; const auto &fun = *entry.functions.GetFunctionByOffset(offset); - for (idx_t i = 0; i < fun.GetArguments().size(); i++) { - results.emplace_back(fun.GetArguments()[i].ToString()); - } - for (auto ¶m : fun.named_parameters) { - results.emplace_back(param.second.ToString()); + for (auto ¶m : fun.GetSignature().GetParameters()) { + if (!param.IsVariadic()) { + results.emplace_back(param.GetType().ToString()); + } } + AddOptions(fun.GetSignature(), results, true); return Value::LIST(LogicalType::VARCHAR, std::move(results)); } static vector GetParameterLogicalTypes(TableFunctionCatalogEntry &entry, idx_t offset) { - const auto &fun = *entry.functions.GetFunctionByOffset(offset); - return fun.GetArguments(); + const auto &signature = entry.functions.GetFunctionByOffset(offset)->GetSignature(); + vector result; + for (idx_t i = 0; i < signature.GetPositionalParameterCount(); i++) { + result.push_back(signature.GetParameter(i).GetType()); + } + return result; } static Value GetVarArgs(TableFunctionCatalogEntry &entry, idx_t offset) { const auto &fun = *entry.functions.GetFunctionByOffset(offset); - return !fun.HasVarArgs() ? Value() : Value(fun.GetVarArgs().ToString()); + auto args = fun.GetSignature().GetArgs(); + return args ? Value(args->GetType().ToString()) : Value(); } static Value GetMacroDefinition(TableFunctionCatalogEntry &entry, idx_t offset) { @@ -514,13 +559,13 @@ struct PragmaFunctionExtractor { static vector GetParameters(PragmaFunctionCatalogEntry &entry, idx_t offset) { vector results; const auto &fun = *entry.functions.GetFunctionByOffset(offset); - - for (idx_t i = 0; i < fun.GetArguments().size(); i++) { - results.emplace_back("col" + to_string(i)); - } - for (auto ¶m : fun.named_parameters) { - results.emplace_back(param.first); + // the variadic parameters are reported in the "varargs" column instead + for (auto ¶m : fun.GetSignature().GetParameters()) { + if (!param.IsVariadic()) { + results.emplace_back(param.GetName()); + } } + AddOptions(fun.GetSignature(), results, false); return results; } @@ -528,23 +573,29 @@ struct PragmaFunctionExtractor { vector results; const auto &fun = *entry.functions.GetFunctionByOffset(offset); - for (idx_t i = 0; i < fun.GetArguments().size(); i++) { - results.emplace_back(fun.GetArguments()[i].ToString()); - } - for (auto ¶m : fun.named_parameters) { - results.emplace_back(param.second.ToString()); + for (auto ¶m : fun.GetSignature().GetParameters()) { + if (!param.IsVariadic()) { + results.emplace_back(param.GetType().ToString()); + } } + AddOptions(fun.GetSignature(), results, true); return Value::LIST(LogicalType::VARCHAR, std::move(results)); } static vector GetParameterLogicalTypes(PragmaFunctionCatalogEntry &entry, idx_t offset) { const auto &fun = *entry.functions.GetFunctionByOffset(offset); - return fun.GetArguments(); + const auto &signature = fun.GetSignature(); + vector result; + for (idx_t i = 0; i < signature.GetPositionalParameterCount(); i++) { + result.push_back(signature.GetParameter(i).GetType()); + } + return result; } static Value GetVarArgs(PragmaFunctionCatalogEntry &entry, idx_t offset) { const auto &fun = *entry.functions.GetFunctionByOffset(offset); - return !fun.HasVarArgs() ? Value() : Value(fun.GetVarArgs().ToString()); + auto args = fun.GetSignature().GetArgs(); + return args ? Value(args->GetType().ToString()) : Value(); } static Value GetMacroDefinition(PragmaFunctionCatalogEntry &entry, idx_t offset) { @@ -571,19 +622,20 @@ static vector ToValueVector(vector &string_vector) { template static Value GetParameterNames(CatalogEntry &entry, idx_t function_idx, FunctionDescription &function_description, Value ¶meter_types) { - vector parameter_names; - if (!function_description.parameter_names.empty()) { - for (idx_t param_idx = 0; param_idx < ListValue::GetChildren(parameter_types).size(); param_idx++) { + // default param names from function signature (if it exists), + // otherwise fall back to function.json entry + auto &function = entry.Cast(); + vector parameter_names = OP::GetParameters(function, function_idx); + idx_t parameter_count = ListValue::GetChildren(parameter_types).size(); + if (parameter_names.size() != parameter_count) { + parameter_names.clear(); + for (idx_t param_idx = 0; param_idx < parameter_count; param_idx++) { if (param_idx < function_description.parameter_names.size()) { parameter_names.emplace_back(function_description.parameter_names[param_idx]); } else { parameter_names.emplace_back("col" + to_string(param_idx)); } } - } else { - // fallback - auto &function = entry.Cast(); - parameter_names = OP::GetParameters(function, function_idx); } return Value::LIST(LogicalType::VARCHAR, parameter_names); } @@ -779,11 +831,23 @@ void DuckDBFunctionsFunction(ClientContext &context, TableFunctionInput &data_p, } count++; } + data.progress_offset.store(data.offset, std::memory_order_relaxed); +} + +static double DuckDBFunctionsProgress(ClientContext &context, const FunctionData *bind_data, + const GlobalTableFunctionState *global_state) { + auto &data = global_state->Cast(); + if (data.entries.empty()) { + return 100.0; + } + auto offset = data.progress_offset.load(std::memory_order_relaxed); + return 100.0 * static_cast(offset) / static_cast(data.entries.size()); } void DuckDBFunctionsFun::RegisterFunction(BuiltinFunctions &set) { - set.AddFunction( - TableFunction("duckdb_functions", {}, DuckDBFunctionsFunction, DuckDBFunctionsBind, DuckDBFunctionsInit)); + TableFunction functions("duckdb_functions", {}, DuckDBFunctionsFunction, DuckDBFunctionsBind, DuckDBFunctionsInit); + functions.table_scan_progress = DuckDBFunctionsProgress; + set.AddFunction(functions); } } // namespace duckdb diff --git a/src/duckdb/src/function/table/system/duckdb_keywords.cpp b/src/duckdb/src/function/table/system/duckdb_keywords.cpp index 56ea13ca8..704c63884 100644 --- a/src/duckdb/src/function/table/system/duckdb_keywords.cpp +++ b/src/duckdb/src/function/table/system/duckdb_keywords.cpp @@ -1,6 +1,5 @@ #include "duckdb/function/table/system_functions.hpp" -#include "duckdb/common/exception.hpp" #include "duckdb/main/client_context.hpp" #include "duckdb/parser/parser.hpp" @@ -50,32 +49,16 @@ void DuckDBKeywordsFunction(ClientContext &context, TableFunctionInput &data_p, auto &entry = data.entries[data.offset++]; keyword_name.Append(Value(entry.name)); - string category_name; - switch (entry.category) { - case KeywordCategory::KEYWORD_RESERVED: - category_name = "reserved"; - break; - case KeywordCategory::KEYWORD_UNRESERVED: - category_name = "unreserved"; - break; - case KeywordCategory::KEYWORD_TYPE_FUNC: - category_name = "type_function"; - break; - case KeywordCategory::KEYWORD_COL_NAME: - category_name = "column_name"; - break; - default: - throw InternalException("Unrecognized keyword category"); - } - keyword_category.Append(Value(std::move(category_name))); + keyword_category.Append(Value(entry.category)); count++; } } void DuckDBKeywordsFun::RegisterFunction(BuiltinFunctions &set) { - set.AddFunction( - TableFunction("duckdb_keywords", {}, DuckDBKeywordsFunction, DuckDBKeywordsBind, DuckDBKeywordsInit)); + TableFunction duckdb_keywords("duckdb_keywords", {}, DuckDBKeywordsFunction, DuckDBKeywordsBind, + DuckDBKeywordsInit); + set.AddFunction(duckdb_keywords); } } // namespace duckdb diff --git a/src/duckdb/src/function/table/system/duckdb_log.cpp b/src/duckdb/src/function/table/system/duckdb_log.cpp index dfa8ca71f..40c9ffdc3 100644 --- a/src/duckdb/src/function/table/system/duckdb_log.cpp +++ b/src/duckdb/src/function/table/system/duckdb_log.cpp @@ -13,13 +13,15 @@ struct DuckDBLogData : public GlobalTableFunctionState { explicit DuckDBLogData(shared_ptr log_storage_p) : log_storage(std::move(log_storage_p)) { scan_state = log_storage->CreateScanState(LoggingTargetTable::LOG_ENTRIES); log_storage->InitializeScan(*scan_state); - } - DuckDBLogData() : log_storage(nullptr) { + total_rows = log_storage->GetScanRowCount(LoggingTargetTable::LOG_ENTRIES); } //! The log storage we are scanning shared_ptr log_storage; unique_ptr scan_state; + //! The number of log entries when the scan started, if known (for progress) + optional_idx total_rows; + atomic scanned_rows {0}; }; static unique_ptr DuckDBLogBind(ClientContext &context, TableFunctionBindInput &input, @@ -43,17 +45,37 @@ static unique_ptr DuckDBLogBind(ClientContext &context, TableFunct } unique_ptr DuckDBLogInit(ClientContext &context, TableFunctionInitInput &input) { - if (LogManager::Get(context).CanScan(LoggingTargetTable::LOG_ENTRIES)) { - return make_uniq(LogManager::Get(context).GetLogStorage()); + auto &log_manager = LogManager::Get(context); + if (!log_manager.CanScan(LoggingTargetTable::LOG_ENTRIES)) { + throw InvalidConfigurationException( + "Log storage '%s' does not support this query. Select a queryable storage, such as 'memory' or 'file', " + "before generating logs.", + log_manager.GetConfig().storage); } - return make_uniq(); + return make_uniq(log_manager.GetLogStorage()); } void DuckDBLogFunction(ClientContext &context, TableFunctionInput &data_p, DataChunk &output) { auto &data = data_p.global_state->Cast(); - if (data.log_storage) { - data.log_storage->Scan(*data.scan_state, output); + data.log_storage->Scan(*data.scan_state, output); + data.scanned_rows.fetch_add(output.size(), std::memory_order_relaxed); +} + +static double DuckDBLogProgress(ClientContext &context, const FunctionData *bind_data, + const GlobalTableFunctionState *global_state) { + auto &data = global_state->Cast(); + if (!data.log_storage) { + return 100.0; + } + if (!data.total_rows.IsValid()) { + return -1; + } + auto total_rows = data.total_rows.GetIndex(); + if (total_rows == 0) { + return 100.0; } + // log entries that are added while scanning are not part of the total + return MinValue(100.0 * static_cast(data.scanned_rows) / static_cast(total_rows), 100.0); } unique_ptr DuckDBLogBindReplace(ClientContext &context, TableFunctionBindInput &input) { @@ -86,7 +108,7 @@ unique_ptr DuckDBLogBindReplace(ClientContext &context, TableFunctionB "timestamp, type, log_level, message" " FROM (SELECT row_number() OVER () AS rowid, * FROM duckdb_logs()) as l JOIN " "duckdb_log_contexts() as c ON l.context_id=c.context_id order by timestamp, l.rowid;"; - Parser parser(context.GetParserOptions()); + Parser parser(context); parser.ParseQuery(sub_query_string); auto select_stmt = unique_ptr_cast(std::move(parser.statements[0])); @@ -96,7 +118,9 @@ unique_ptr DuckDBLogBindReplace(ClientContext &context, TableFunctionB void DuckDBLogFun::RegisterFunction(BuiltinFunctions &set) { TableFunction logs_fun("duckdb_logs", {}, DuckDBLogFunction, DuckDBLogBind, DuckDBLogInit); logs_fun.bind_replace = DuckDBLogBindReplace; - logs_fun.named_parameters["denormalized_table"] = LogicalType::BOOLEAN; + logs_fun.table_scan_progress = DuckDBLogProgress; + logs_fun.GetSignature().WithTypedKwargs( + "options", [](TypedKwargs &options) { options.Add("denormalized_table", LogicalType::BOOLEAN); }); set.AddFunction(logs_fun); } diff --git a/src/duckdb/src/function/table/system/duckdb_log_contexts.cpp b/src/duckdb/src/function/table/system/duckdb_log_contexts.cpp index 4c389d6d6..1a8f18a8d 100644 --- a/src/duckdb/src/function/table/system/duckdb_log_contexts.cpp +++ b/src/duckdb/src/function/table/system/duckdb_log_contexts.cpp @@ -16,8 +16,6 @@ struct DuckDBLogContextData : public GlobalTableFunctionState { scan_state = log_storage->CreateScanState(LoggingTargetTable::LOG_CONTEXTS); log_storage->InitializeScan(*scan_state); } - DuckDBLogContextData() : log_storage(nullptr) { - } //! The log storage we are scanning shared_ptr log_storage; @@ -48,17 +46,19 @@ static unique_ptr DuckDBLogContextBind(ClientContext &context, Tab } unique_ptr DuckDBLogContextInit(ClientContext &context, TableFunctionInitInput &input) { - if (LogManager::Get(context).CanScan(LoggingTargetTable::LOG_CONTEXTS)) { - return make_uniq(LogManager::Get(context).GetLogStorage()); + auto &log_manager = LogManager::Get(context); + if (!log_manager.CanScan(LoggingTargetTable::LOG_CONTEXTS)) { + throw InvalidConfigurationException( + "Log storage '%s' does not support this query. Select a queryable storage, such as 'memory' or 'file', " + "before generating logs.", + log_manager.GetConfig().storage); } - return make_uniq(); + return make_uniq(log_manager.GetLogStorage()); } void DuckDBLogContextFunction(ClientContext &context, TableFunctionInput &data_p, DataChunk &output) { auto &data = data_p.global_state->Cast(); - if (data.log_storage) { - data.log_storage->Scan(*data.scan_state, output); - } + data.log_storage->Scan(*data.scan_state, output); } static unique_ptr DuckDBLogContextsBindReplace(ClientContext &context, TableFunctionBindInput &input) { diff --git a/src/duckdb/src/function/table/system/duckdb_optimizers.cpp b/src/duckdb/src/function/table/system/duckdb_optimizers.cpp index 719e10514..c0b6b09b3 100644 --- a/src/duckdb/src/function/table/system/duckdb_optimizers.cpp +++ b/src/duckdb/src/function/table/system/duckdb_optimizers.cpp @@ -51,8 +51,9 @@ void DuckDBOptimizersFunction(ClientContext &context, TableFunctionInput &data_p } void DuckDBOptimizersFun::RegisterFunction(BuiltinFunctions &set) { - set.AddFunction( - TableFunction("duckdb_optimizers", {}, DuckDBOptimizersFunction, DuckDBOptimizersBind, DuckDBOptimizersInit)); + TableFunction duckdb_optimizers("duckdb_optimizers", {}, DuckDBOptimizersFunction, DuckDBOptimizersBind, + DuckDBOptimizersInit); + set.AddFunction(duckdb_optimizers); } } // namespace duckdb diff --git a/src/duckdb/src/function/table/system/duckdb_secrets.cpp b/src/duckdb/src/function/table/system/duckdb_secrets.cpp index 2361ac633..81c59b5a3 100644 --- a/src/duckdb/src/function/table/system/duckdb_secrets.cpp +++ b/src/duckdb/src/function/table/system/duckdb_secrets.cpp @@ -137,7 +137,8 @@ void DuckDBSecretsFunction(ClientContext &context, TableFunctionInput &data_p, D void DuckDBSecretsFun::RegisterFunction(BuiltinFunctions &set) { TableFunctionSet functions("duckdb_secrets"); auto fun = TableFunction({}, DuckDBSecretsFunction, DuckDBSecretsBind, DuckDBSecretsInit); - fun.named_parameters["redact"] = LogicalType::BOOLEAN; + fun.GetSignature().WithTypedKwargs("options", + [](TypedKwargs &options) { options.Add("redact", LogicalType::BOOLEAN); }); functions.AddFunction(fun); set.AddFunction(functions); } diff --git a/src/duckdb/src/function/table/system/duckdb_sequences.cpp b/src/duckdb/src/function/table/system/duckdb_sequences.cpp index 089dfd73d..af589fc83 100644 --- a/src/duckdb/src/function/table/system/duckdb_sequences.cpp +++ b/src/duckdb/src/function/table/system/duckdb_sequences.cpp @@ -156,8 +156,9 @@ void DuckDBSequencesFunction(ClientContext &context, TableFunctionInput &data_p, } void DuckDBSequencesFun::RegisterFunction(BuiltinFunctions &set) { - set.AddFunction( - TableFunction("duckdb_sequences", {}, DuckDBSequencesFunction, DuckDBSequencesBind, DuckDBSequencesInit)); + auto function = + TableFunction("duckdb_sequences", {}, DuckDBSequencesFunction, DuckDBSequencesBind, DuckDBSequencesInit); + set.AddFunction(std::move(function)); } } // namespace duckdb diff --git a/src/duckdb/src/function/table/system/duckdb_settings.cpp b/src/duckdb/src/function/table/system/duckdb_settings.cpp index f3d87c64a..60abe9ce6 100644 --- a/src/duckdb/src/function/table/system/duckdb_settings.cpp +++ b/src/duckdb/src/function/table/system/duckdb_settings.cpp @@ -55,6 +55,12 @@ static unique_ptr DuckDBSettingsBind(ClientContext &context, Table return_types.emplace_back(LogicalType::VARIANT()); auto result = make_uniq(); + for (auto name : {"debug", "deprecated"}) { + auto it = input.named_parameters.find(name); + if (it != input.named_parameters.end() && it->second.IsNull()) { + throw InvalidInputException("Cannot use NULL as argument for %s", name); + } + } if (auto it = input.named_parameters.find("debug"); it != input.named_parameters.end()) { result->debug = it->second.GetValue(); } @@ -81,10 +87,10 @@ unique_ptr DuckDBSettingsInit(ClientContext &context, for (idx_t i = 0; i < options_count; i++) { auto option = DBConfig::GetOptionByIndex(i); D_ASSERT(option); - if (!bind_data.debug && option->is_debug) { + if (bind_data.debug != option->is_debug) { continue; } - if (!bind_data.deprecated && option->is_deprecated) { + if (bind_data.deprecated != option->is_deprecated) { continue; } DuckDBSettingValue value; @@ -116,6 +122,12 @@ unique_ptr DuckDBSettingsInit(ClientContext &context, result->settings.push_back(std::move(value)); } for (auto &ext_param : config.GetExtensionSettings()) { + if (bind_data.debug != ext_param.second.is_debug) { + continue; + } + if (bind_data.deprecated != ext_param.second.is_deprecated) { + continue; + } Value setting_val; auto scope = SettingScope::GLOBAL; auto lookup_result = context.TryGetCurrentSetting(ext_param.first, setting_val); @@ -176,8 +188,9 @@ void DuckDBSettingsFunction(ClientContext &context, TableFunctionInput &data_p, void DuckDBSettingsFun::RegisterFunction(BuiltinFunctions &set) { TableFunction settings_fun("duckdb_settings", {}, DuckDBSettingsFunction, DuckDBSettingsBind, DuckDBSettingsInit); - settings_fun.named_parameters["debug"] = LogicalType::BOOLEAN; - settings_fun.named_parameters["deprecated"] = LogicalType::BOOLEAN; + settings_fun.GetSignature().WithTypedKwargs("options", [](TypedKwargs &options) { + options.Add("debug", LogicalType::BOOLEAN).Add("deprecated", LogicalType::BOOLEAN); + }); set.AddFunction(settings_fun); } diff --git a/src/duckdb/src/function/table/system/duckdb_tables.cpp b/src/duckdb/src/function/table/system/duckdb_tables.cpp index 257b403aa..fbfa53d11 100644 --- a/src/duckdb/src/function/table/system/duckdb_tables.cpp +++ b/src/duckdb/src/function/table/system/duckdb_tables.cpp @@ -175,7 +175,8 @@ void DuckDBTablesFunction(ClientContext &context, TableFunctionInput &data_p, Da } void DuckDBTablesFun::RegisterFunction(BuiltinFunctions &set) { - set.AddFunction(TableFunction("duckdb_tables", {}, DuckDBTablesFunction, DuckDBTablesBind, DuckDBTablesInit)); + auto function = TableFunction("duckdb_tables", {}, DuckDBTablesFunction, DuckDBTablesBind, DuckDBTablesInit); + set.AddFunction(std::move(function)); } } // namespace duckdb diff --git a/src/duckdb/src/function/table/system/duckdb_types.cpp b/src/duckdb/src/function/table/system/duckdb_types.cpp index d7d057028..dc5fb6bbb 100644 --- a/src/duckdb/src/function/table/system/duckdb_types.cpp +++ b/src/duckdb/src/function/table/system/duckdb_types.cpp @@ -222,7 +222,7 @@ void DuckDBTypesFunction(ClientContext &context, TableFunctionInput &data_p, Dat tags.Append(Value::MAP(type_entry.tags)); internal.Append(Value::BOOLEAN(type_entry.internal)); extension_name.Append(type_entry.extension_name.empty() ? Value() : Value(type_entry.extension_name)); - if (type.id() == LogicalTypeId::ENUM && type.AuxInfo()) { + if (type.id() == LogicalTypeId::ENUM && type.HasParameters()) { auto enum_data = FlatVector::GetData(EnumType::GetValuesInsertOrder(type)); idx_t size = EnumType::GetSize(type); @@ -240,12 +240,20 @@ void DuckDBTypesFunction(ClientContext &context, TableFunctionInput &data_p, Dat vector parameter_names; vector parameter_type_names; for (auto ¶m : signature.GetParameters()) { + if (param.IsVariadic()) { + continue; + } parameter_names.emplace_back(param.GetName()); parameter_type_names.emplace_back(param.GetType().ToString()); } parameters.Append(Value::LIST(LogicalType::VARCHAR, std::move(parameter_names))); parameter_types.Append(Value::LIST(LogicalType::VARCHAR, std::move(parameter_type_names))); - varargs.Append(signature.HasVarArgs() ? Value(signature.GetVarArgs().ToString()) : Value()); + auto variadic = signature.GetArgs(); + if (!variadic) { + variadic = signature.GetKwargs(); + } + // the legacy "varargs" column cannot tell "*args" and "**kwargs" apart + varargs.Append(variadic ? Value(variadic->GetType().ToString()) : Value()); count++; } diff --git a/src/duckdb/src/function/table/system/duckdb_which_secret.cpp b/src/duckdb/src/function/table/system/duckdb_which_secret.cpp index fc326fa41..e4462e1e4 100644 --- a/src/duckdb/src/function/table/system/duckdb_which_secret.cpp +++ b/src/duckdb/src/function/table/system/duckdb_which_secret.cpp @@ -67,7 +67,10 @@ void DuckDBWhichSecretFunction(ClientContext &context, TableFunctionInput &data_ } void DuckDBWhichSecretFun::RegisterFunction(BuiltinFunctions &set) { - set.AddFunction(TableFunction("which_secret", {duckdb::LogicalType::VARCHAR, duckdb::LogicalType::VARCHAR}, + set.AddFunction(TableFunction("which_secret", + FunctionSignature() + .AddPositionalOnly("path", LogicalType::VARCHAR) + .AddPositionalOnly("type", LogicalType::VARCHAR), DuckDBWhichSecretFunction, DuckDBWhichSecretBind, DuckDBWhichSecretInit)); } diff --git a/src/duckdb/src/function/table/system/enable_profiling.cpp b/src/duckdb/src/function/table/system/enable_profiling.cpp index b1fd0984d..f5979b71e 100644 --- a/src/duckdb/src/function/table/system/enable_profiling.cpp +++ b/src/duckdb/src/function/table/system/enable_profiling.cpp @@ -1,8 +1,10 @@ #include "duckdb/function/table/system_functions.hpp" #include "duckdb/main/client_context.hpp" +#include "duckdb/main/config.hpp" #include "duckdb/main/settings.hpp" #include "duckdb/common/file_system.hpp" #include "duckdb/main/profiler/gathered_metrics.hpp" +#include "duckdb/function/function_set.hpp" namespace duckdb { @@ -63,14 +65,8 @@ static void EnableProfiling(ClientContext &context, TableFunctionInput &data, Da static unique_ptr BindEnableProfiling(ClientContext &context, TableFunctionBindInput &input, vector &return_types, vector &names) { - if (input.inputs.size() > 1) { - throw InvalidInputException("EnableProfiling: expected 0 or 1 parameter"); - } - auto bind_data = make_uniq(); - bool metrics_set = false; - for (const auto &named_param : input.named_parameters) { const auto key = EnumUtil::FromString(named_param.first.GetIdentifierName()); switch (key) { @@ -86,29 +82,27 @@ static unique_ptr BindEnableProfiling(ClientContext &context, Tabl case ProfilingParameterNames::MODE: bind_data->mode = StringUtil::Lower(named_param.second.ToString()); break; - case ProfilingParameterNames::METRICS: { - if (named_param.second.type() != LogicalType::LIST(LogicalType::VARCHAR) && - named_param.second.type() != LogicalType::VARCHAR) { - throw InvalidInputException("EnableProfiling: metrics must be a list of strings or a VARCHAR pattern"); - } - - bind_data->metrics = named_param.second; - metrics_set = true; - } + case ProfilingParameterNames::METRICS: + throw InternalException("enable_profiling: metrics is placed by position"); } } - // Process positional param: metrics configs - if (!input.inputs.empty()) { - if (metrics_set) { - throw InvalidInputException("EnableProfiling: cannot specify both metrics and positional parameters"); - } - if (input.inputs[0].type() != LogicalType::LIST(LogicalType::VARCHAR) && - input.inputs[0].type() != LogicalType::VARCHAR) { - throw InvalidInputException("EnableProfiling: metrics must be a list of strings or a VARCHAR pattern"); - } + // The metrics are placed by position however they were passed; the overload settled pattern or list of names + bind_data->metrics = input.inputs[0]; - bind_data->metrics = input.inputs[0]; + auto &config = DBConfig::GetConfig(context); + config.CheckLock(EnableProfilingSetting::Name); + if (!bind_data->coverage.IsNull()) { + config.CheckLock(ProfilingCoverageSetting::Name); + } + if (!bind_data->save_location.IsNull()) { + config.CheckLock(ProfilingOutputSetting::Name); + } + if (!bind_data->mode.IsNull()) { + config.CheckLock(ProfilingModeSetting::Name); + } + if (!bind_data->metrics.IsNull()) { + config.CheckLock(TrackedMetricsSetting::Name); } return_types.emplace_back(LogicalType::BOOLEAN); @@ -124,6 +118,7 @@ static void DisableProfiling(ClientContext &context, TableFunctionInput &data, D static unique_ptr BindDisableProfiling(ClientContext &context, TableFunctionBindInput &input, vector &return_types, vector &names) { + DBConfig::GetConfig(context).CheckLock(EnableProfilingSetting::Name); return_types.emplace_back(LogicalType::BOOLEAN); names.emplace_back("Success"); @@ -131,19 +126,31 @@ static unique_ptr BindDisableProfiling(ClientContext &context, Tab } void EnableProfilingFun::RegisterFunction(BuiltinFunctions &set) { - auto enable_fun = TableFunction("enable_profiling", {}, EnableProfiling, BindEnableProfiling, nullptr, nullptr); - - enable_fun.named_parameters.emplace("format", LogicalType::VARCHAR); - enable_fun.named_parameters.emplace("coverage", LogicalType::VARCHAR); - enable_fun.named_parameters.emplace("save_location", LogicalType::VARCHAR); - enable_fun.named_parameters.emplace("mode", LogicalType::VARCHAR); - enable_fun.named_parameters.emplace("metrics", LogicalType::ANY); - - enable_fun.SetVarArgs(LogicalType::LIST(LogicalType::VARCHAR)); - set.AddFunction(enable_fun); + // The metrics are either a single pattern or a list of names + TableFunctionSet enable_set("enable_profiling"); + + enable_set.AddFunction(TableFunction(FunctionSignature() + .AddParameter("metrics", LogicalType::VARCHAR, Value(LogicalType::VARCHAR)) + .AddTypedKwargs("options", TypedKwargs() + .Add("format", LogicalType::VARCHAR) + .Add("coverage", LogicalType::VARCHAR) + .Add("save_location", LogicalType::VARCHAR) + .Add("mode", LogicalType::VARCHAR)), + EnableProfiling, BindEnableProfiling)); + + enable_set.AddFunction(TableFunction(FunctionSignature() + .AddParameter("metrics", LogicalType::LIST(LogicalType::VARCHAR)) + .AddTypedKwargs("options", TypedKwargs() + .Add("format", LogicalType::VARCHAR) + .Add("coverage", LogicalType::VARCHAR) + .Add("save_location", LogicalType::VARCHAR) + .Add("mode", LogicalType::VARCHAR)), + EnableProfiling, BindEnableProfiling)); + + set.AddFunction(std::move(enable_set)); auto disable_fun = TableFunction("disable_profiling", {}, DisableProfiling, BindDisableProfiling, nullptr, nullptr); - set.AddFunction(disable_fun); + set.AddFunction(std::move(disable_fun)); } } // namespace duckdb diff --git a/src/duckdb/src/function/table/system/external_resource_types.cpp b/src/duckdb/src/function/table/system/external_resource_types.cpp index 9ae9eb692..d5ded055c 100644 --- a/src/duckdb/src/function/table/system/external_resource_types.cpp +++ b/src/duckdb/src/function/table/system/external_resource_types.cpp @@ -153,14 +153,19 @@ static void ExternalResourceTypesFunction(ClientContext &context, TableFunctionI //===--------------------------------------------------------------------===// void RegisterExternalResourceTypeFun::RegisterFunction(BuiltinFunctions &set) { - TableFunction fn("register_external_resource_type", {LogicalType::VARCHAR}, RegisterExternalResourceTypeFunction, - RegisterExternalResourceTypeBind, RegisterExternalResourceTypeInit); - fn.named_parameters["kind"] = LogicalType::VARCHAR; - fn.named_parameters["create_function"] = LogicalType::VARCHAR; - fn.named_parameters["status_function"] = LogicalType::VARCHAR; - fn.named_parameters["destroy_function"] = LogicalType::VARCHAR; - fn.named_parameters["resolve_function"] = LogicalType::VARCHAR; - fn.named_parameters["list_function"] = LogicalType::VARCHAR; + TableFunction fn( + "register_external_resource_type", FunctionSignature().AddPositionalOnly("name", LogicalType::VARCHAR), + RegisterExternalResourceTypeFunction, RegisterExternalResourceTypeBind, RegisterExternalResourceTypeInit); + fn.GetSignature() + .AddKeywordOnly("kind", LogicalType::VARCHAR) + .AddKeywordOnly("create_function", LogicalType::VARCHAR) + .WithTypedKwargs("options", [](TypedKwargs &kwargs) { + kwargs.Add("status_function", LogicalType::VARCHAR) + .Add("destroy_function", LogicalType::VARCHAR) + .Add("resolve_function", LogicalType::VARCHAR) + .Add("list_function", LogicalType::VARCHAR); + }); + set.AddFunction(fn); } diff --git a/src/duckdb/src/function/table/system/external_resources.cpp b/src/duckdb/src/function/table/system/external_resources.cpp index 3926864db..c612011ed 100644 --- a/src/duckdb/src/function/table/system/external_resources.cpp +++ b/src/duckdb/src/function/table/system/external_resources.cpp @@ -6,7 +6,6 @@ #include "duckdb/common/set.hpp" #include "duckdb/common/types/value.hpp" #include "duckdb/main/connection.hpp" -#include "duckdb/main/materialized_query_result.hpp" #include "duckdb/main/database.hpp" #include "duckdb/main/external_resource_type_registry.hpp" #include "duckdb/main/external_resources_manager.hpp" @@ -101,7 +100,7 @@ static void DiscoverExternalResources(ClientContext &context, const ExternalReso // con.Query reports query errors in-band, but with statement verification active (a global debug setting // the internal connection inherits) a bind-time failure of the callback is thrown instead. Handle both so // the wrapped message is identical in every mode. - unique_ptr res; + unique_ptr res; try { res = con.Query(sql); } catch (const std::exception &ex) { @@ -130,10 +129,11 @@ static void DiscoverExternalResources(ClientContext &context, const ExternalReso "external resource discovery for type \"%s\": the list callback \"%s\" did not return a \"handle\" column", type.name, type.list_function); } + auto res_rows = res->Collection().GetRows(); for (idx_t r = 0; r < res->RowCount(); r++) { // The handle is a MAP (opaque to us), the same contract create/status enforce - reject anything else // with a clear message rather than a cast failure deep in the output append. - auto handle = RequireResourceMap(res->GetValue(handle_idx, r), type.list_function, "handle"); + auto handle = RequireResourceMap(res_rows.GetValue(handle_idx, r), type.list_function, "handle"); if (managed_handles.count(HandleKey(handle)) > 0) { continue; // already shown as a locally managed resource of this type } @@ -142,11 +142,11 @@ static void DiscoverExternalResources(ClientContext &context, const ExternalReso row.handle = std::move(handle); row.managed = false; if (reference_idx != DConstants::INVALID_INDEX) { - auto ref = res->GetValue(reference_idx, r); + auto ref = res_rows.GetValue(reference_idx, r); row.reference = ref.IsNull() ? string() : ref.ToString(); } if (state_idx != DConstants::INVALID_INDEX) { - auto st = res->GetValue(state_idx, r); + auto st = res_rows.GetValue(state_idx, r); row.state = st.IsNull() ? string() : st.ToString(); } rows.push_back(std::move(row)); @@ -212,7 +212,8 @@ static void ExternalResourcesFunction(ClientContext &context, TableFunctionInput void DuckDBExternalResourcesFun::RegisterFunction(BuiltinFunctions &set) { TableFunction fn("duckdb_external_resources", {}, ExternalResourcesFunction, ExternalResourcesBind, ExternalResourcesInit); - fn.named_parameters["discover"] = LogicalType::BOOLEAN; + fn.GetSignature().WithTypedKwargs("options", + [](TypedKwargs &options) { options.Add("discover", LogicalType::BOOLEAN); }); set.AddFunction(fn); } @@ -240,6 +241,9 @@ static unique_ptr RegisterExternalResourceBind(ClientContext &cont if (input.inputs[0].IsNull() || input.inputs[1].IsNull()) { throw InvalidInputException("register_external_resource: the type and name must not be NULL"); } + if (input.inputs[2].IsNull()) { + throw InvalidInputException("register_external_resource: the handle must not be NULL"); + } resource.type = StringValue::Get(input.inputs[0]); resource.name = StringValue::Get(input.inputs[1]); // The handle is the durable identity and doubles as the deleter payload. @@ -286,13 +290,17 @@ static void RegisterExternalResourceFunction(ClientContext &context, TableFuncti } void RegisterExternalResourceFun::RegisterFunction(BuiltinFunctions &set) { - TableFunction fn( - "register_external_resource", - {LogicalType::VARCHAR, LogicalType::VARCHAR, LogicalType::MAP(LogicalType::VARCHAR, LogicalType::VARCHAR)}, - RegisterExternalResourceFunction, RegisterExternalResourceBind, RegisterExternalResourceInit); - fn.named_parameters["uri"] = LogicalType::VARCHAR; - fn.named_parameters["attached_db_type"] = LogicalType::VARCHAR; - fn.named_parameters["deleter_function"] = LogicalType::VARCHAR; + TableFunction fn("register_external_resource", + FunctionSignature() + .AddPositionalOnly("type", LogicalType::VARCHAR) + .AddPositionalOnly("name", LogicalType::VARCHAR) + .AddPositionalOnly("handle", LogicalType::MAP(LogicalType::VARCHAR, LogicalType::VARCHAR)), + RegisterExternalResourceFunction, RegisterExternalResourceBind, RegisterExternalResourceInit); + fn.GetSignature().WithTypedKwargs("options", [](TypedKwargs &options) { + options.Add("uri", LogicalType::VARCHAR) + .Add("attached_db_type", LogicalType::VARCHAR) + .Add("deleter_function", LogicalType::VARCHAR); + }); set.AddFunction(fn); } @@ -344,9 +352,9 @@ static void DeregisterExternalResourceFunction(ClientContext &context, TableFunc } void DeregisterExternalResourceFun::RegisterFunction(BuiltinFunctions &set) { - set.AddFunction(TableFunction("deregister_external_resource", {LogicalType::VARCHAR}, - DeregisterExternalResourceFunction, DeregisterExternalResourceBind, - DeregisterExternalResourceInit)); + set.AddFunction(TableFunction( + "deregister_external_resource", FunctionSignature().AddPositionalOnly("name", LogicalType::VARCHAR), + DeregisterExternalResourceFunction, DeregisterExternalResourceBind, DeregisterExternalResourceInit)); } } // namespace duckdb diff --git a/src/duckdb/src/function/table/system/logging_utils.cpp b/src/duckdb/src/function/table/system/logging_utils.cpp index 67386917d..1b1a0004d 100644 --- a/src/duckdb/src/function/table/system/logging_utils.cpp +++ b/src/duckdb/src/function/table/system/logging_utils.cpp @@ -6,6 +6,8 @@ #include "duckdb/logging/log_manager.hpp" #include "duckdb/logging/logging.hpp" #include "duckdb/main/client_context.hpp" +#include "duckdb/main/config.hpp" +#include "duckdb/main/settings.hpp" #include "duckdb/main/database.hpp" #include "duckdb/main/extension_entries.hpp" #include "duckdb/main/extension_helper.hpp" @@ -31,18 +33,24 @@ static void EnableLogging(ClientContext &context, TableFunctionInput &data, Data auto &log_manager = context.db->GetLogManager(); - // Apply the config generated from the input - log_manager.SetConfig(*context.db, bind_data.config); + // Apply the config generated from the input - restoring the previous config if that fails part-way + auto previous_config = log_manager.GetConfig(); + try { + log_manager.SetConfig(*context.db, bind_data.config); - if (bind_data.log_types_to_set.empty()) { - log_manager.SetEnableLogging(true); - log_manager.SetLogMode(LogMode::LEVEL_ONLY); - } else { - log_manager.SetEnableStructuredLoggers(bind_data.log_types_to_set); - } + if (bind_data.log_types_to_set.empty()) { + log_manager.SetEnableLogging(true); + log_manager.SetLogMode(LogMode::LEVEL_ONLY); + } else { + log_manager.SetEnableStructuredLoggers(bind_data.log_types_to_set); + } - if (!bind_data.storage_config.empty()) { - log_manager.UpdateLogStorageConfig(*context.db, bind_data.storage_config); + if (!bind_data.storage_config.empty()) { + log_manager.UpdateLogStorageConfig(*context.db, bind_data.storage_config); + } + } catch (...) { + log_manager.SetConfig(*context.db, previous_config); + throw; } } @@ -62,6 +70,13 @@ static unique_ptr BindEnableLogging(ClientContext &context, TableF throw InvalidInputException("EnableLogging: expected 0 or 1 parameter"); } + auto &config = DBConfig::GetConfig(context); + config.CheckLock(EnableLogging::Name); + config.CheckLock(EnabledLogTypes::Name); + config.CheckLock(LoggingLevel::Name); + config.CheckLock(LoggingMode::Name); + config.CheckLock(LoggingStorage::Name); + auto result = make_uniq(); bool storage_isset = false; @@ -69,6 +84,9 @@ static unique_ptr BindEnableLogging(ClientContext &context, TableF for (const auto ¶m : input.named_parameters) { auto &key = param.first; if (key == "level") { + if (param.second.IsNull()) { + throw InvalidInputException("EnableLogging: level cannot be NULL"); + } result->config.level = EnumUtil::FromString(param.second.ToString()); } else if (key == "storage") { storage_isset = true; @@ -137,6 +155,10 @@ static unique_ptr BindEnableLogging(ClientContext &context, TableF for (const auto &log_type : result->log_types_to_set) { TryAutoloadLogTypeExtension(context, log_type); + // verify the log types before changing any of the logging settings + if (!context.db->GetLogManager().LookupLogType(log_type)) { + throw InvalidInputException("Unknown log type: '%s'", log_type); + } } return_types.emplace_back(LogicalType::BOOLEAN); @@ -157,6 +179,7 @@ static void TruncateLogs(ClientContext &context, TableFunctionInput &data, DataC static unique_ptr BindDisableLogging(ClientContext &context, TableFunctionBindInput &input, vector &return_types, vector &names) { + DBConfig::GetConfig(context).CheckLock(EnableLogging::Name); return_types.emplace_back(LogicalType::BOOLEAN); names.emplace_back("Success"); @@ -174,17 +197,18 @@ static unique_ptr BindTruncateLogs(ClientContext &context, TableFu void EnableLoggingFun::RegisterFunction(BuiltinFunctions &set) { auto enable_fun = TableFunction("enable_logging", {}, EnableLogging, BindEnableLogging, nullptr, nullptr); - // Base config - enable_fun.named_parameters.emplace("level", LogicalType::VARCHAR); - enable_fun.named_parameters.emplace("storage", LogicalType::VARCHAR); - enable_fun.named_parameters.emplace("storage_config", LogicalType::ANY); - - // Config that is forwarded to the storage_config struct as syntactic sugar - enable_fun.named_parameters.emplace("storage_path", LogicalType::VARCHAR); - enable_fun.named_parameters.emplace("storage_normalize", LogicalType::BOOLEAN); - enable_fun.named_parameters.emplace("storage_buffer_size", LogicalType::UBIGINT); + enable_fun.GetSignature() + .AddArgs("args", LogicalType::ANY) + .AddKeywordOnly("level", LogicalType::VARCHAR, Value("INFO")) + .WithTypedKwargs("options", [](TypedKwargs &options) { + options.Add("storage", LogicalType::VARCHAR) + .Add("storage_config", LogicalType::ANY) + // forwarded to the storage_config struct as syntactic sugar + .Add("storage_path", LogicalType::VARCHAR) + .Add("storage_normalize", LogicalType::BOOLEAN) + .Add("storage_buffer_size", LogicalType::UBIGINT); + }); - enable_fun.SetVarArgs(LogicalType::ANY); set.AddFunction(enable_fun); auto disable_fun = TableFunction("disable_logging", {}, DisableLogging, BindDisableLogging, nullptr, nullptr); diff --git a/src/duckdb/src/function/table/system/pragma_metadata_info.cpp b/src/duckdb/src/function/table/system/pragma_metadata_info.cpp index 5deb58c3f..9df9956c5 100644 --- a/src/duckdb/src/function/table/system/pragma_metadata_info.cpp +++ b/src/duckdb/src/function/table/system/pragma_metadata_info.cpp @@ -86,7 +86,8 @@ void PragmaMetadataInfo::RegisterFunction(BuiltinFunctions &set) { TableFunctionSet metadata_info("pragma_metadata_info"); metadata_info.AddFunction( TableFunction({}, PragmaMetadataInfoFunction, PragmaMetadataInfoBind, PragmaMetadataInfoInit)); - metadata_info.AddFunction(TableFunction({LogicalType::VARCHAR}, PragmaMetadataInfoFunction, PragmaMetadataInfoBind, + metadata_info.AddFunction(TableFunction(FunctionSignature().AddPositionalOnly("database", LogicalType::VARCHAR), + PragmaMetadataInfoFunction, PragmaMetadataInfoBind, PragmaMetadataInfoInit)); set.AddFunction(metadata_info); } diff --git a/src/duckdb/src/function/table/system/pragma_storage_info.cpp b/src/duckdb/src/function/table/system/pragma_storage_info.cpp index 96e66166a..3927923a9 100644 --- a/src/duckdb/src/function/table/system/pragma_storage_info.cpp +++ b/src/duckdb/src/function/table/system/pragma_storage_info.cpp @@ -4,11 +4,13 @@ #include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp" #include "duckdb/parser/qualified_name.hpp" +#include "duckdb/common/atomic.hpp" #include "duckdb/common/mutex.hpp" #include "duckdb/execution/partition_info.hpp" #include "duckdb/parallel/task_scheduler.hpp" #include "duckdb/storage/data_table.hpp" #include "duckdb/storage/table/row_group_collection.hpp" +#include "duckdb/storage/table/row_group_segment_tree.hpp" #include "duckdb/storage/table_storage_info.hpp" #include "duckdb/planner/binder.hpp" #include "duckdb/common/enums/column_segment_info_scan_type.hpp" @@ -31,6 +33,9 @@ struct PragmaStorageGlobalState : public GlobalTableFunctionState { mutex lock; idx_t max_threads; ColumnSegmentInfoScanState scan_state; + //! The total number of row groups and the number of row groups that were scanned (for progress) + idx_t total_row_groups = 0; + atomic scanned_row_groups {0}; idx_t MaxThreads() const override { return max_threads; @@ -92,10 +97,16 @@ static unique_ptr PragmaStorageInfoBind(ClientContext &context, Ta ColumnSegmentInfoScanOptions options; auto include_entry = input.named_parameters.find("include_segment_info"); if (include_entry != input.named_parameters.end()) { + if (include_entry->second.IsNull()) { + throw InvalidInputException("Cannot use NULL as argument for include_segment_info"); + } options.include_segment_info = include_entry->second.GetValue(); } auto loaded_only_entry = input.named_parameters.find("loaded_segments_only"); if (loaded_only_entry != input.named_parameters.end()) { + if (loaded_only_entry->second.IsNull()) { + throw InvalidInputException("Cannot use NULL as argument for loaded_segments_only"); + } options.loaded_segments_only = loaded_only_entry->second.GetValue(); } @@ -108,7 +119,11 @@ static unique_ptr PragmaStorageInfoBind(ClientContext &context, Ta names.emplace_back("additional_block_ids"); return_types.emplace_back(LogicalType::LIST(LogicalTypeId::BIGINT)); - auto qname = QualifiedName::Parse(input.inputs[0].GetValue()); + QualifiedName qname; + string error; + if (!QualifiedName::TryParse(input.inputs[0].GetValue(), qname, error)) { + throw ParserException(error); + } // look up the table name in the catalog CatalogEntryRetriever retriever(context); @@ -125,6 +140,12 @@ unique_ptr PragmaStorageInfoInitGlobal(ClientContext & auto gstate = make_uniq(max_threads); gstate->scan_state.options = bind_data.options; bind_data.table_entry.InitializeColumnSegmentInfoScan(gstate->scan_state); + if (gstate->scan_state.row_groups) { + for (auto &row_group : gstate->scan_state.row_groups->SegmentNodes()) { + (void)row_group; + gstate->total_row_groups++; + } + } return std::move(gstate); } @@ -177,6 +198,9 @@ static void PragmaStorageInfoFunction(ClientContext &context, TableFunctionInput { lock_guard guard(gstate.lock); has_more = bind_data.table_entry.ScanColumnSegmentInfo(query_context, gstate.scan_state, lstate.buffer); + if (has_more) { + gstate.scanned_row_groups.fetch_add(1, std::memory_order_relaxed); + } } if (!has_more) { break; @@ -228,6 +252,16 @@ static void PragmaStorageInfoFunction(ClientContext &context, TableFunctionInput } } +static double PragmaStorageInfoProgress(ClientContext &context, const FunctionData *bind_data_p, + const GlobalTableFunctionState *global_state) { + auto &gstate = global_state->Cast(); + if (gstate.total_row_groups == 0) { + return 100.0; + } + auto scanned = MinValue(gstate.scanned_row_groups, gstate.total_row_groups); + return 100.0 * static_cast(scanned) / static_cast(gstate.total_row_groups); +} + static OperatorPartitionData PragmaStorageInfoGetPartitionData(ClientContext &context, TableFunctionGetPartitionInput &input) { auto &lstate = input.local_state->Cast(); @@ -235,11 +269,14 @@ static OperatorPartitionData PragmaStorageInfoGetPartitionData(ClientContext &co } void PragmaStorageInfo::RegisterFunction(BuiltinFunctions &set) { - TableFunction storage_info("pragma_storage_info", {LogicalType::VARCHAR}, PragmaStorageInfoFunction, - PragmaStorageInfoBind, PragmaStorageInfoInitGlobal, PragmaStorageInfoInitLocal); + TableFunction storage_info( + "pragma_storage_info", FunctionSignature().AddPositionalOnly("table_name", LogicalType::VARCHAR), + PragmaStorageInfoFunction, PragmaStorageInfoBind, PragmaStorageInfoInitGlobal, PragmaStorageInfoInitLocal); storage_info.get_partition_data = PragmaStorageInfoGetPartitionData; - storage_info.named_parameters["include_segment_info"] = LogicalType::BOOLEAN; - storage_info.named_parameters["loaded_segments_only"] = LogicalType::BOOLEAN; + storage_info.table_scan_progress = PragmaStorageInfoProgress; + storage_info.GetSignature().WithTypedKwargs("options", [](TypedKwargs &options) { + options.Add("include_segment_info", LogicalType::BOOLEAN).Add("loaded_segments_only", LogicalType::BOOLEAN); + }); set.AddFunction(std::move(storage_info)); } diff --git a/src/duckdb/src/function/table/system/pragma_table_info.cpp b/src/duckdb/src/function/table/system/pragma_table_info.cpp index 5b70fddc6..392892e46 100644 --- a/src/duckdb/src/function/table/system/pragma_table_info.cpp +++ b/src/duckdb/src/function/table/system/pragma_table_info.cpp @@ -167,7 +167,11 @@ static unique_ptr PragmaTableInfoBind(ClientContext &context, Tabl PragmaShowHelper::GetSchema(return_types, names); } - auto qname = QualifiedName::Parse(input.inputs[0].GetValue()); + QualifiedName qname; + string error; + if (!QualifiedName::TryParse(input.inputs[0].GetValue(), qname, error)) { + throw ParserException(error); + } // look up the table name in the catalog CatalogEntryRetriever retriever(context); @@ -288,10 +292,12 @@ static void PragmaTableInfoFunction(ClientContext &context, TableFunctionInput & } void PragmaTableInfo::RegisterFunction(BuiltinFunctions &set) { - set.AddFunction(TableFunction("pragma_table_info", {LogicalType::VARCHAR}, PragmaTableInfoFunction, - PragmaTableInfoBind, PragmaTableInfoInit)); - set.AddFunction(TableFunction("pragma_show", {LogicalType::VARCHAR}, PragmaTableInfoFunction, - PragmaTableInfoBind, PragmaTableInfoInit)); + set.AddFunction(TableFunction("pragma_table_info", + FunctionSignature().AddPositionalOnly("table_name", LogicalType::VARCHAR), + PragmaTableInfoFunction, PragmaTableInfoBind, PragmaTableInfoInit)); + set.AddFunction(TableFunction("pragma_show", + FunctionSignature().AddPositionalOnly("table_name", LogicalType::VARCHAR), + PragmaTableInfoFunction, PragmaTableInfoBind, PragmaTableInfoInit)); } } // namespace duckdb diff --git a/src/duckdb/src/function/table/system/pragma_table_sample.cpp b/src/duckdb/src/function/table/system/pragma_table_sample.cpp index 1c83a2f21..4cc7461e9 100644 --- a/src/duckdb/src/function/table/system/pragma_table_sample.cpp +++ b/src/duckdb/src/function/table/system/pragma_table_sample.cpp @@ -33,7 +33,11 @@ struct DuckDBTableSampleOperatorData : public GlobalTableFunctionState { static unique_ptr DuckDBTableSampleBind(ClientContext &context, TableFunctionBindInput &input, vector &return_types, vector &names) { // look up the table name in the catalog - auto qname = QualifiedName::Parse(input.inputs[0].GetValue()); + QualifiedName qname; + string error; + if (!QualifiedName::TryParse(input.inputs[0].GetValue(), qname, error)) { + throw ParserException(error); + } CatalogEntryRetriever retriever(context); qname = Binder::BindTableName(retriever, qname); @@ -89,8 +93,9 @@ static void DuckDBTableSampleFunction(ClientContext &context, TableFunctionInput } void DuckDBTableSample::RegisterFunction(BuiltinFunctions &set) { - set.AddFunction(TableFunction("duckdb_table_sample", {LogicalType::VARCHAR}, DuckDBTableSampleFunction, - DuckDBTableSampleBind, DuckDBTableSampleInit)); + set.AddFunction(TableFunction("duckdb_table_sample", + FunctionSignature().AddPositionalOnly("table_name", LogicalType::VARCHAR), + DuckDBTableSampleFunction, DuckDBTableSampleBind, DuckDBTableSampleInit)); } } // namespace duckdb diff --git a/src/duckdb/src/function/table/system/pragma_user_agent.cpp b/src/duckdb/src/function/table/system/pragma_user_agent.cpp index 82b04eca2..05705d5c0 100644 --- a/src/duckdb/src/function/table/system/pragma_user_agent.cpp +++ b/src/duckdb/src/function/table/system/pragma_user_agent.cpp @@ -41,8 +41,9 @@ void PragmaUserAgentFunction(ClientContext &context, TableFunctionInput &data_p, } void PragmaUserAgent::RegisterFunction(BuiltinFunctions &set) { - set.AddFunction( - TableFunction("pragma_user_agent", {}, PragmaUserAgentFunction, PragmaUserAgentBind, PragmaUserAgentInit)); + TableFunction pragma_user_agent("pragma_user_agent", {}, PragmaUserAgentFunction, PragmaUserAgentBind, + PragmaUserAgentInit); + set.AddFunction(pragma_user_agent); } } // namespace duckdb diff --git a/src/duckdb/src/function/table/system/test_all_types.cpp b/src/duckdb/src/function/table/system/test_all_types.cpp index 2af0bbae7..a7ceecdfc 100644 --- a/src/duckdb/src/function/table/system/test_all_types.cpp +++ b/src/duckdb/src/function/table/system/test_all_types.cpp @@ -445,8 +445,9 @@ static void TestAllTypesFunction(ClientContext &context, TableFunctionInput &dat void TestAllTypesFun::RegisterFunction(BuiltinFunctions &set) { TableFunction test_all_types("test_all_types", {}, TestAllTypesFunction, TestAllTypesBind, TestAllTypesInit); - test_all_types.named_parameters["use_large_enum"] = LogicalType::BOOLEAN; - test_all_types.named_parameters["use_large_bignum"] = LogicalType::BOOLEAN; + test_all_types.GetSignature().WithTypedKwargs("options", [](TypedKwargs &options) { + options.Add("use_large_enum", LogicalType::BOOLEAN).Add("use_large_bignum", LogicalType::BOOLEAN); + }); set.AddFunction(test_all_types); } diff --git a/src/duckdb/src/function/table/system/test_vector_types.cpp b/src/duckdb/src/function/table/system/test_vector_types.cpp index a91964061..cddd1c0b7 100644 --- a/src/duckdb/src/function/table/system/test_vector_types.cpp +++ b/src/duckdb/src/function/table/system/test_vector_types.cpp @@ -1,4 +1,5 @@ #include "duckdb/common/vector/flat_vector.hpp" +#include "duckdb/common/atomic.hpp" #include "duckdb/common/vector/list_vector.hpp" #include "duckdb/common/vector/map_vector.hpp" #include "duckdb/common/vector/struct_vector.hpp" @@ -21,7 +22,7 @@ struct TestVectorTypesData : public GlobalTableFunctionState { } vector> entries; - idx_t offset; + atomic offset; }; struct TestVectorInfo { @@ -329,14 +330,26 @@ void TestVectorTypesFunction(ClientContext &context, TableFunctionInput &data_p, return; } output.Reference(*data.entries[data.offset]); - data.offset++; + data.offset.fetch_add(1, std::memory_order_relaxed); +} + +static double TestVectorTypesProgress(ClientContext &context, const FunctionData *bind_data, + const GlobalTableFunctionState *global_state) { + auto &data = global_state->Cast(); + if (data.entries.empty()) { + return 100.0; + } + return 100.0 * static_cast(data.offset) / static_cast(data.entries.size()); } void TestVectorTypesFun::RegisterFunction(BuiltinFunctions &set) { - TableFunction test_vector_types("test_vector_types", {LogicalType::ANY}, TestVectorTypesFunction, - TestVectorTypesBind, TestVectorTypesInit); - test_vector_types.SetVarArgs(LogicalType::ANY); - test_vector_types.named_parameters["all_flat"] = LogicalType::BOOLEAN; + TableFunction test_vector_types("test_vector_types", + FunctionSignature().AddPositionalOnly("column", LogicalType::ANY), + TestVectorTypesFunction, TestVectorTypesBind, TestVectorTypesInit); + test_vector_types.GetSignature() + .AddArgs("args", LogicalType::ANY) + .WithTypedKwargs("options", [](TypedKwargs &options) { options.Add("all_flat", LogicalType::BOOLEAN); }); + test_vector_types.table_scan_progress = TestVectorTypesProgress; set.AddFunction(std::move(test_vector_types)); } diff --git a/src/duckdb/src/function/table/table_scan.cpp b/src/duckdb/src/function/table/table_scan.cpp index 3fec18b03..63192e552 100644 --- a/src/duckdb/src/function/table/table_scan.cpp +++ b/src/duckdb/src/function/table/table_scan.cpp @@ -42,6 +42,7 @@ #include "duckdb/storage/table/data_table_info.hpp" #include "duckdb/storage/table/scan_state.hpp" #include "duckdb/planner/expression_iterator.hpp" +#include "duckdb/parser/tableref/basetableref.hpp" #include "duckdb/transaction/duck_transaction_manager.hpp" #include "duckdb/main/profiler/profiling_node.hpp" @@ -64,6 +65,44 @@ static unique_ptr CreateTableScanReadAhead(ClientContext &context return ScanReadAhead::Create(context); } +//! Tracks how many rows of a scan assignment have been consumed, for reporting progress +struct ScanAssignmentProgress { + //! Rows of the current assignment + idx_t assignment_rows = 0; + //! Rows of the current assignment that were counted as scanned + idx_t reported_rows = 0; + + void Start(idx_t rows) { + assignment_rows = rows; + reported_rows = 0; + } + //! Returns the rows consumed since the last update + idx_t Update(const TableScanState &scan_state) { + idx_t remaining = 0; + if (scan_state.table_state.GetRowGroup()) { + remaining = scan_state.table_state.RemainingAssignmentRows(); + } else if (scan_state.local_state.GetRowGroup()) { + remaining = scan_state.local_state.RemainingAssignmentRows(); + } + auto consumed = assignment_rows - MinValue(remaining, assignment_rows); + return Report(consumed); + } + //! Returns the rows of the assignment that were not yet counted + idx_t Finish() { + return Report(assignment_rows); + } + +private: + idx_t Report(idx_t consumed) { + if (consumed <= reported_rows) { + return 0; + } + auto delta = consumed - reported_rows; + reported_rows = consumed; + return delta; + } +}; + struct TableScanLocalState : public LocalTableFunctionState { //! The current position in the scan. TableScanState scan_state; @@ -74,6 +113,8 @@ struct TableScanLocalState : public LocalTableFunctionState { unique_ptr job; //! Rows scanned by finished read-ahead jobs, folded in when their scan state is recycled idx_t job_rows_scanned = 0; + //! Consumed rows of the assignment (or read-ahead job) currently being scanned + ScanAssignmentProgress assignment_progress; idx_t row_groups_scanned = 0; }; @@ -319,8 +360,8 @@ class DuckTableScanState : public TableScanGlobalState { //! Batch index assigned to the next produced job idx_t next_job_index = 0; ScanStatePool state_pool; - //! Rows claimed by read-ahead jobs that no thread is decoding yet - atomic queued_rows {0}; + //! Rows of claimed assignments that have been consumed by a scan + atomic scanned_rows {0}; public: //! Retains the scan initialization info shared by all scan states of this scan @@ -360,13 +401,21 @@ class DuckTableScanState : public TableScanGlobalState { //! Claims the next assignment into the thread's own scan state, returns false when none are left bool ClaimAssignment(ClientContext &context, TableScanLocalState &l_state) { - if (!storage.NextParallelScan(context, state, l_state.scan_state).IsValid()) { + scanned_rows.fetch_add(l_state.assignment_progress.Finish(), std::memory_order_relaxed); + auto rows = storage.NextParallelScan(context, state, l_state.scan_state); + if (!rows.IsValid()) { return false; } + l_state.assignment_progress.Start(rows.GetIndex()); l_state.row_groups_scanned++; return true; } + //! Counts the rows of the current assignment that were consumed by the scan + void UpdateScanProgress(TableScanLocalState &l_state, const TableScanState &scan_state) { + scanned_rows.fetch_add(l_state.assignment_progress.Update(scan_state), std::memory_order_relaxed); + } + //! How TableScanFunc's loop proceeds after a persistent scan iteration enum class PersistentScanResult { YIELD, NEXT_VECTOR, EXHAUSTED }; @@ -426,7 +475,6 @@ class DuckTableScanState : public TableScanGlobalState { } job->rows = rows.GetIndex(); job->batch_index = next_job_index++; - queued_rows += job->rows; } job->scan_state->InitializeColumnScans(); // preparing the first vector skips the leading vectors the zonemaps or sampling reject before registering I/O @@ -461,14 +509,16 @@ class DuckTableScanState : public TableScanGlobalState { } l_state.job = unique_ptr_cast(std::move(claimed)); l_state.row_groups_scanned++; - queued_rows -= l_state.job->rows; + l_state.assignment_progress.Start(l_state.job->rows); if (acquired == ScanReadAheadAcquire::PARKED) { return true; } } auto &job_scan = *l_state.job->scan_state; // the job's I/O was registered when it was produced, so no I/O is scheduled here - switch (ScanPersistentStorage(context, data_p, l_state, job_scan, output)) { + auto scan_result = ScanPersistentStorage(context, data_p, l_state, job_scan, output); + UpdateScanProgress(l_state, job_scan); + switch (scan_result) { case PersistentScanResult::YIELD: return true; case PersistentScanResult::NEXT_VECTOR: @@ -484,9 +534,12 @@ class DuckTableScanState : public TableScanGlobalState { return true; } // the job is exhausted, fold its scan counters into this thread and recycle its state + scanned_rows.fetch_add(l_state.assignment_progress.Finish(), std::memory_order_relaxed); l_state.job_rows_scanned += job_scan.RowsScanned(); job_scan.table_state.rows_scanned = 0; job_scan.local_state.rows_scanned = 0; + // a pooled state outlives the pipeline, it must not keep blocks pinned that a sink may still convert + job_scan.ReleasePins(); state_pool.Push(std::move(l_state.job->scan_state)); l_state.job.reset(); if (TryYieldControl(data_p)) { @@ -511,6 +564,7 @@ class DuckTableScanState : public TableScanGlobalState { do { if (bind_data.is_create_index) { storage.CreateIndexScan(l_state.scan_state, output); + UpdateScanProgress(l_state, l_state.scan_state); } else if (read_ahead) { if (!ScanWithReadAhead(context, data_p, l_state, output) && data_p.results_execution_mode == AsyncResultsExecutionMode::TASK_EXECUTOR) { @@ -519,7 +573,9 @@ class DuckTableScanState : public TableScanGlobalState { } return; } else { - switch (ScanPersistentStorage(context, data_p, l_state, l_state.scan_state, output)) { + auto scan_result = ScanPersistentStorage(context, data_p, l_state, l_state.scan_state, output); + UpdateScanProgress(l_state, l_state.scan_state); + switch (scan_result) { case PersistentScanResult::YIELD: return; case PersistentScanResult::NEXT_VECTOR: @@ -553,14 +609,11 @@ class DuckTableScanState : public TableScanGlobalState { return 100; } - // queued rows are read first, a claim raises the processed rows before it is queued - const idx_t queued = queued_rows.load(); - idx_t scanned_rows = state.scan_state.processed_rows; - scanned_rows += state.local_state.processed_rows; - // claimed assignments count once a thread decodes them, like they do without read-ahead - D_ASSERT(queued <= scanned_rows); - scanned_rows -= queued; - auto percentage = 100 * (static_cast(scanned_rows) / static_cast(total_rows)); + // rows count once they are consumed by a scan, or when their assignment is skipped entirely + idx_t done_rows = scanned_rows.load(); + done_rows += state.scan_state.skipped_rows.load(); + done_rows += state.local_state.skipped_rows.load(); + auto percentage = 100 * (static_cast(done_rows) / static_cast(total_rows)); if (percentage > 100) { // If the last chunk has fewer elements than STANDARD_VECTOR_SIZE, and if our percentage is over 100, // then we finished this table. @@ -574,10 +627,10 @@ class DuckTableScanState : public TableScanGlobalState { auto &l_state = input.local_state->Cast(); // with read-ahead the assignment being decoded lives in the claimed job's scan state auto &scan_state = l_state.job ? *l_state.job->scan_state : l_state.scan_state; - if (scan_state.table_state.row_group) { + if (scan_state.table_state.GetRowGroup()) { return OperatorPartitionData(scan_state.table_state.batch_index); } - if (scan_state.local_state.row_group) { + if (scan_state.local_state.GetRowGroup()) { return OperatorPartitionData(scan_state.table_state.batch_index + scan_state.local_state.batch_index); } return OperatorPartitionData(0); @@ -740,21 +793,10 @@ static bool CollectValuesAndComparisonsFromExpression(const Expression &expr, va } if (expr.GetExpressionClass() == ExpressionClass::BOUND_FUNCTION) { auto &func = expr.Cast(); - if (func.Function().GetName() == OptionalFilterScalarFun::NAME) { - if (!func.BindInfo()) { - return true; - } - auto &data = func.BindInfo()->Cast(); - return !data.child_filter_expr || - CollectValuesAndComparisonsFromExpression(*data.child_filter_expr, in_values, comparisons); - } - if (func.Function().GetName() == SelectivityOptionalFilterScalarFun::NAME) { - if (!func.BindInfo()) { - return true; - } - auto &data = func.BindInfo()->Cast(); - return !data.child_filter_expr || - CollectValuesAndComparisonsFromExpression(*data.child_filter_expr, in_values, comparisons); + if (ExpressionFilter::IsRootOptionalExpression(func)) { + auto optional_child = ExpressionFilter::GetOptionalFilterChild(func); + return !optional_child || + CollectValuesAndComparisonsFromExpression(*optional_child, in_values, comparisons); } if (TableFilterFunctions::IsTableFilterFunction(func.Function())) { return true; @@ -929,6 +971,11 @@ unique_ptr TableScanInitGlobal(ClientContext &context, return DuckTableScanInitGlobal(context, input, storage, bind_data); } + // The index scan does not apply the sample. + if (input.sample_options) { + return DuckTableScanInitGlobal(context, input, storage, bind_data); + } + auto &filter_set = *input.filters; // FIXME: We currently only support scanning one ART with one filter. @@ -1034,9 +1081,9 @@ vector TableScanGetPartitionStats(ClientContext &context, G return storage.GetPartitionStats(context); } -BindInfo TableScanGetBindInfo(const optional_ptr bind_data_p) { +optional_ptr TableScanGetTableEntry(optional_ptr bind_data_p) { auto &bind_data = bind_data_p->Cast(); - return BindInfo(bind_data.table); + return &bind_data.table; } void TableScanDependency(LogicalDependencyList &entries, const FunctionData *bind_data_p) { @@ -1072,7 +1119,7 @@ InsertionOrderPreservingMap TableScanToString(TableFunctionToStringInput } static void TableScanSerialize(Serializer &serializer, const optional_ptr bind_data_p, - const TableFunction &function) { + const BoundTableFunction &function) { auto &bind_data = bind_data_p->Cast(); // the catalog/schema/name are only the innermost qualification - "qualified_name" carries the full (possibly // nested) schema path @@ -1086,7 +1133,7 @@ static void TableScanSerialize(Serializer &serializer, const optional_ptr TableScanDeserialize(Deserializer &deserializer, TableFunction &function) { +static unique_ptr TableScanDeserialize(Deserializer &deserializer, BoundTableFunction &function) { auto catalog = deserializer.ReadProperty(100, "catalog"); auto schema = deserializer.ReadProperty(101, "schema"); auto table = deserializer.ReadProperty(102, "table"); @@ -1144,6 +1191,53 @@ void SetPartitionsToScan(vector partition_indices, optional_ptr>(partition_indices.begin(), partition_indices.end()); } +static string TableScanToSQLGuard(const LogicalGet &get, bool has_input) { + auto &data = get.bind_data->Cast(); + if (has_input) { + return "input_child"; + } + if (get.ordinality_idx.IsValid()) { + return "ordinality"; + } + if (!get.scan_partition_indices.empty()) { + return "scan_partitions"; + } + if (data.is_create_index) { + return "create_index"; + } + if (data.partitions_to_scan) { + return "bound_scan_partitions"; + } + const bool has_filters = + get.table_filters.HasFilters() || get.table_filters.HasMultiColumnFilters() || get.dynamic_filters; + for (auto &index : get.GetColumnIds()) { + if (index.IsRowNumberColumn() && has_filters) { + return "row_number_with_filters"; + } + if (index.IsVirtualColumn() && !index.IsRowIdColumn() && !index.IsRowNumberColumn()) { + return "virtual_column"; + } + if (!index.IsVirtualColumn()) { + auto &definition = data.table.GetColumn(index.ToLogical()); + if (!index.IsPushdownExtract() && definition.Type() != get.GetColumnType(index)) { + return "column_type"; + } + } + } + return string(); +} + +static TableFunctionToSQLResult TableScanToSQL(ClientContext &, const LogicalGet &get) { + auto guard = TableScanToSQLGuard(get, !get.children.empty()); + if (!guard.empty()) { + return {nullptr, std::move(guard)}; + } + auto table = make_uniq(); + auto entry = get.GetTable(); + table->SetQualifiedName(entry->schema.GetQualifiedName(entry->name)); + return {std::move(table), {}}; +} + TableFunction TableScanFunction::GetFunction() { TableFunction scan_function("seq_scan", {}, TableScanFunc); scan_function.init_local = TableScanInitLocal; @@ -1154,10 +1248,11 @@ TableFunction TableScanFunction::GetFunction() { scan_function.get_metrics = TableScanGetMetrics; scan_function.pushdown_complex_filter = nullptr; scan_function.to_string = TableScanToString; + scan_function.to_sql = TableScanToSQL; scan_function.table_scan_progress = TableScanProgress; scan_function.get_partition_data = TableScanGetPartitionData; scan_function.get_partition_stats = TableScanGetPartitionStats; - scan_function.get_bind_info = TableScanGetBindInfo; + scan_function.get_table_entry = TableScanGetTableEntry; scan_function.projection_pushdown = true; scan_function.filter_pushdown = true; scan_function.filter_prune = true; diff --git a/src/duckdb/src/function/table/unnest.cpp b/src/duckdb/src/function/table/unnest.cpp index 76cf40ef6..df6b95b3c 100644 --- a/src/duckdb/src/function/table/unnest.cpp +++ b/src/duckdb/src/function/table/unnest.cpp @@ -90,7 +90,8 @@ static OperatorResultType UnnestFunction(ExecutionContext &context, TableFunctio } void UnnestTableFunction::RegisterFunction(BuiltinFunctions &set) { - TableFunction unnest_function("unnest", {LogicalType::ANY}, nullptr, UnnestBind, UnnestInit, UnnestLocalInit); + TableFunction unnest_function("unnest", FunctionSignature().AddPositionalOnly("list", LogicalType::ANY), nullptr, + UnnestBind, UnnestInit, UnnestLocalInit); unnest_function.in_out_function = UnnestFunction; unnest_function.return_type = TableFunctionReturnType::SET_RETURNING_FUNCTION; set.AddFunction(unnest_function); diff --git a/src/duckdb/src/function/table/version/pragma_version.cpp b/src/duckdb/src/function/table/version/pragma_version.cpp index c039a8b94..013a582ec 100644 --- a/src/duckdb/src/function/table/version/pragma_version.cpp +++ b/src/duckdb/src/function/table/version/pragma_version.cpp @@ -1,5 +1,5 @@ #ifndef DUCKDB_PATCH_VERSION -#define DUCKDB_PATCH_VERSION "0-alpha48042" +#define DUCKDB_PATCH_VERSION "0-alpha48443" #endif #ifndef DUCKDB_MINOR_VERSION #define DUCKDB_MINOR_VERSION 1 @@ -8,10 +8,10 @@ #define DUCKDB_MAJOR_VERSION 2 #endif #ifndef DUCKDB_VERSION -#define DUCKDB_VERSION "v2.1.0-alpha48042" +#define DUCKDB_VERSION "v2.1.0-alpha48443" #endif #ifndef DUCKDB_SOURCE_ID -#define DUCKDB_SOURCE_ID "2bb5316a8b" +#define DUCKDB_SOURCE_ID "e08224d653" #endif #include "duckdb/function/table/system_functions.hpp" #include "duckdb/main/database.hpp" diff --git a/src/duckdb/src/function/table_function.cpp b/src/duckdb/src/function/table_function.cpp index 84654a49b..34e7d5001 100644 --- a/src/duckdb/src/function/table_function.cpp +++ b/src/duckdb/src/function/table_function.cpp @@ -1,5 +1,8 @@ #include "duckdb/function/table_function.hpp" #include "duckdb/function/partition_stats.hpp" +#include "duckdb/common/string_util.hpp" + +#include namespace duckdb { @@ -15,15 +18,13 @@ PartitionStatistics::PartitionStatistics() : row_start(0), count(0), count_type( TableFunctionInfo::~TableFunctionInfo() { } -TableFunction::TableFunction(Identifier name, const vector &arguments, table_function_t function_, - table_function_bind_t bind, table_function_init_global_t init_global, - table_function_init_local_t init_local) - : SimpleNamedParameterFunction(std::move(name), arguments), bind(bind), bind_replace(nullptr), - bind_operator(nullptr), init_global(init_global), init_local(init_local), function(function_), - in_out_function(nullptr), in_out_function_final(nullptr), statistics(nullptr), statistics_extended(nullptr), - dependency(nullptr), cardinality(nullptr), get_metrics(nullptr), pushdown_complex_filter(nullptr), - pushdown_expression(nullptr), combine_schema(nullptr), claim_batch(nullptr), finish_batch(nullptr), - to_string(nullptr), table_scan_progress(nullptr), get_partition_data(nullptr), get_bind_info(nullptr), +BaseTableFunction::BaseTableFunction(table_function_t function_, table_function_bind_t bind, + table_function_init_global_t init_global, table_function_init_local_t init_local) + : bind(bind), bind_replace(nullptr), bind_operator(nullptr), init_global(init_global), init_local(init_local), + function(function_), in_out_function(nullptr), in_out_function_final(nullptr), statistics(nullptr), + statistics_extended(nullptr), dependency(nullptr), cardinality(nullptr), get_metrics(nullptr), + pushdown_complex_filter(nullptr), pushdown_expression(nullptr), to_string(nullptr), table_scan_progress(nullptr), + get_partition_data(nullptr), get_bind_info(nullptr), get_table_entry(nullptr), projection_expression_pushdown(nullptr), get_multi_file_reader(nullptr), supports_pushdown_type(nullptr), supports_pushdown_extract(nullptr), is_repeatable(nullptr), get_partition_info(nullptr), get_partition_stats(nullptr), get_virtual_columns(nullptr), get_row_id_columns(nullptr), set_scan_order(nullptr), @@ -32,21 +33,121 @@ TableFunction::TableFunction(Identifier name, const vector &argumen return_type(TableFunctionReturnType::TABLE_RETURNING_FUNCTION) { } -TableFunction::TableFunction(Identifier name, const vector &arguments, std::nullptr_t function_, +TableFunction::TableFunction(Identifier name, const vector &arguments, table_function_t function_, table_function_bind_t bind, table_function_init_global_t init_global, table_function_init_local_t init_local) - : SimpleNamedParameterFunction(std::move(name), arguments), bind(bind), bind_replace(nullptr), - bind_operator(nullptr), init_global(init_global), init_local(init_local), function(nullptr), - in_out_function(nullptr), in_out_function_final(nullptr), statistics(nullptr), statistics_extended(nullptr), - dependency(nullptr), cardinality(nullptr), get_metrics(nullptr), pushdown_complex_filter(nullptr), - pushdown_expression(nullptr), combine_schema(nullptr), claim_batch(nullptr), finish_batch(nullptr), - to_string(nullptr), table_scan_progress(nullptr), get_partition_data(nullptr), get_bind_info(nullptr), - projection_expression_pushdown(nullptr), get_multi_file_reader(nullptr), supports_pushdown_type(nullptr), - supports_pushdown_extract(nullptr), is_repeatable(nullptr), get_partition_info(nullptr), - get_partition_stats(nullptr), get_virtual_columns(nullptr), get_row_id_columns(nullptr), set_scan_order(nullptr), - serialize(nullptr), deserialize(nullptr), projection_pushdown(false), filter_pushdown(false), filter_prune(false), - sampling_pushdown(false), late_materialization(false), - return_type(TableFunctionReturnType::TABLE_RETURNING_FUNCTION) { + : BaseTableFunction(function_, bind, init_global, init_local), + SimpleFunction(std::move(name), arguments, LogicalType::INVALID) { +} + +TableFunction::TableFunction(Identifier name, const vector &arguments, std::nullptr_t, + table_function_bind_t bind, table_function_init_global_t init_global, + table_function_init_local_t init_local) + : BaseTableFunction(nullptr, bind, init_global, init_local), + SimpleFunction(std::move(name), arguments, LogicalType::INVALID) { +} + +TableFunction::TableFunction(Identifier name, FunctionSignature signature, table_function_t function_, + table_function_bind_t bind, table_function_init_global_t init_global, + table_function_init_local_t init_local) + : BaseTableFunction(function_, bind, init_global, init_local), + SimpleFunction(std::move(name), std::move(signature)) { +} + +TableFunction::TableFunction(FunctionSignature signature, table_function_t function_, table_function_bind_t bind, + table_function_init_global_t init_global, table_function_init_local_t init_local) + : BaseTableFunction(function_, bind, init_global, init_local), SimpleFunction("", std::move(signature)) { +} + +TableFunction::TableFunction(Identifier name, FunctionSignature signature, std::nullptr_t, table_function_bind_t bind, + table_function_init_global_t init_global, table_function_init_local_t init_local) + : BaseTableFunction(nullptr, bind, init_global, init_local), SimpleFunction(std::move(name), std::move(signature)) { +} + +BoundTableFunction::BoundTableFunction() : BaseTableFunction(nullptr, nullptr, nullptr, nullptr) { +} + +BoundTableFunction::BoundTableFunction(const TableFunction &function) + // the function does not come from a function set - copy it into a definition of its own + : BoundTableFunction(make_shared_ptr(function)) { +} + +BoundTableFunction::BoundTableFunction(shared_ptr function_p) + : BaseTableFunction(nullptr, nullptr, nullptr, nullptr) { + definition = std::move(function_p); + auto &function = *definition; + qualified_name = function.GetQualifiedName(); + extra_info = function.extra_info; + + // the behaviour is taken over one member at a time - the declaration stays behind on the definition + bind = function.bind; + bind_replace = function.bind_replace; + bind_operator = function.bind_operator; + init_global = function.init_global; + init_local = function.init_local; + this->function = function.function; + in_out_function = function.in_out_function; + in_out_function_final = function.in_out_function_final; + statistics = function.statistics; + statistics_extended = function.statistics_extended; + dependency = function.dependency; + cardinality = function.cardinality; + get_metrics = function.get_metrics; + pushdown_complex_filter = function.pushdown_complex_filter; + pushdown_expression = function.pushdown_expression; + to_string = function.to_string; + to_sql = function.to_sql; + table_scan_progress = function.table_scan_progress; + get_partition_data = function.get_partition_data; + get_bind_info = function.get_bind_info; + get_table_entry = function.get_table_entry; + projection_expression_pushdown = function.projection_expression_pushdown; + get_multi_file_reader = function.get_multi_file_reader; + supports_pushdown_type = function.supports_pushdown_type; + supports_pushdown_extract = function.supports_pushdown_extract; + is_repeatable = function.is_repeatable; + get_partition_info = function.get_partition_info; + get_partition_stats = function.get_partition_stats; + get_virtual_columns = function.get_virtual_columns; + get_row_id_columns = function.get_row_id_columns; + set_scan_order = function.set_scan_order; + set_partitions_to_scan = function.set_partitions_to_scan; + serialize = function.serialize; + deserialize = function.deserialize; + verify_serialization = function.verify_serialization; + projection_pushdown = function.projection_pushdown; + filter_pushdown = function.filter_pushdown; + filter_prune = function.filter_prune; + sampling_pushdown = function.sampling_pushdown; + late_materialization = function.late_materialization; + function_info = function.function_info; + BaseTableFunction::return_type = function.return_type; + call_return_type = function.call_return_type; + order_preservation_type = function.order_preservation_type; + global_initialization = function.global_initialization; + parallelism = function.parallelism; + + // the parameters a call fills by position - these are the types plan serialization records, so the named + // options that follow them take no part + auto &signature = function.GetSignature(); + for (idx_t i = 0; i < signature.GetPositionalParameterCount(); i++) { + arguments.push_back(signature.GetParameter(i).GetType()); + } + positional_arguments = arguments.size(); +} + +void BoundTableFunction::SetCallArguments(const vector ¶meters, + const named_argument_map_t &named_parameters) { + BoundSimpleFunction::SetCallArguments(GetSignature(), parameters, named_parameters); +} + +bool BoundTableFunction::operator==(const BoundTableFunction &rhs) const { + return GetQualifiedName() == rhs.GetQualifiedName() && GetArguments() == rhs.GetArguments() && + BaseTableFunction::operator==(rhs); +} + +bool BoundTableFunction::operator!=(const BoundTableFunction &rhs) const { + return !(*this == rhs); } TableFunction::TableFunction(const vector &arguments, table_function_t function_, @@ -63,15 +164,15 @@ TableFunction::TableFunction(const vector &arguments, std::nullptr_ TableFunction::TableFunction() : TableFunction("", {}, nullptr, nullptr, nullptr, nullptr) { } -bool TableFunction::operator==(const TableFunction &rhs) const { - return name == rhs.name && arguments == rhs.GetArguments() && varargs == rhs.GetVarArgs() && bind == rhs.bind && - bind_replace == rhs.bind_replace && bind_operator == rhs.bind_operator && init_global == rhs.init_global && - init_local == rhs.init_local && function == rhs.function && in_out_function == rhs.in_out_function && - in_out_function_final == rhs.in_out_function_final && statistics == rhs.statistics && - dependency == rhs.dependency && cardinality == rhs.cardinality && +bool BaseTableFunction::operator==(const BaseTableFunction &rhs) const { + return bind == rhs.bind && bind_replace == rhs.bind_replace && bind_operator == rhs.bind_operator && + init_global == rhs.init_global && init_local == rhs.init_local && function == rhs.function && + in_out_function == rhs.in_out_function && in_out_function_final == rhs.in_out_function_final && + statistics == rhs.statistics && dependency == rhs.dependency && cardinality == rhs.cardinality && pushdown_complex_filter == rhs.pushdown_complex_filter && pushdown_expression == rhs.pushdown_expression && - to_string == rhs.to_string && table_scan_progress == rhs.table_scan_progress && + to_string == rhs.to_string && to_sql == rhs.to_sql && table_scan_progress == rhs.table_scan_progress && get_partition_data == rhs.get_partition_data && get_bind_info == rhs.get_bind_info && + get_table_entry == rhs.get_table_entry && projection_expression_pushdown == rhs.projection_expression_pushdown && get_multi_file_reader == rhs.get_multi_file_reader && supports_pushdown_type == rhs.supports_pushdown_type && is_repeatable == rhs.is_repeatable && get_partition_info == rhs.get_partition_info && @@ -84,27 +185,12 @@ bool TableFunction::operator==(const TableFunction &rhs) const { global_initialization == rhs.global_initialization; } -bool TableFunction::operator!=(const TableFunction &rhs) const { - return !(*this == rhs); +bool TableFunction::operator==(const TableFunction &rhs) const { + return name == rhs.name && GetSignature() == rhs.GetSignature() && BaseTableFunction::operator==(rhs); } -bool TableFunction::Equal(const TableFunction &rhs) const { - // number of types - if (this->GetArguments().size() != rhs.GetArguments().size()) { - return false; - } - // argument types - for (idx_t i = 0; i < this->GetArguments().size(); ++i) { - if (this->GetArguments()[i] != rhs.GetArguments()[i]) { - return false; - } - } - // varargs - if (this->GetVarArgs() != rhs.GetVarArgs()) { - return false; - } - - return true; // they are equal +bool TableFunction::operator!=(const TableFunction &rhs) const { + return !(*this == rhs); } bool TableFunctionInput::HandleBlocked(AsyncResult &blocked_result) { diff --git a/src/duckdb/src/function/type_constructor.cpp b/src/duckdb/src/function/type_constructor.cpp index ba9ff64c0..d98c126ea 100644 --- a/src/duckdb/src/function/type_constructor.cpp +++ b/src/duckdb/src/function/type_constructor.cpp @@ -34,7 +34,7 @@ TypeConstructor TypeConstructor::Identity(Identifier name) { TypeConstructor TypeConstructor::Unchecked(Identifier name, bind_logical_type_function_t bind) { // accepts anything, the bind function validates the arguments itself auto signature = Signature(); - signature.SetVarArgs(LogicalType::ANY); + signature.AddArgs("args", LogicalType::ANY).AddKwargs("kwargs", LogicalType::ANY); return TypeConstructor(std::move(name), std::move(signature), bind); } @@ -66,15 +66,22 @@ string ConstructorToString(const Identifier &type_name, const TypeConstructor &c const auto &sig = constructor.GetSignature(); vector parts; for (auto ¶m : sig.GetParameters()) { + switch (param.GetKind()) { + case FunctionParameterKind::VAR_POSITIONAL: + parts.push_back(param.GetType().ToString() + "..."); + continue; + case FunctionParameterKind::VAR_KEYWORD: + parts.push_back(param.GetName().GetIdentifierName() + " := " + param.GetType().ToString() + "..."); + continue; + default: + break; + } string part = param.GetName().GetIdentifierName() + " " + param.GetType().ToString(); if (param.HasDefaultValue()) { part += " := " + param.GetDefaultValue()->ToString(); } parts.push_back(std::move(part)); } - if (sig.HasVarArgs()) { - parts.push_back(sig.GetVarArgs().ToString() + "..."); - } return type_name.GetIdentifierName() + "(" + StringUtil::Join(parts, ", ") + ")"; } @@ -133,7 +140,7 @@ Value CastArgument(const Identifier &type_name, const string &arg_name, const Va vector NormalizeArguments(const Identifier &type_name, const TypeConstructor &constructor, const vector &arguments, QueryLocation type_location) { const auto &sig = constructor.GetSignature(); - const auto param_count = sig.GetParameterCount(); + const auto param_count = sig.GetPositionalParameterCount(); vector result; vector> slots(param_count); @@ -154,12 +161,20 @@ vector NormalizeArguments(const Identifier &type_name, const TypeC positional_count++; } + identifier_set_t seen_names; for (idx_t i = 0; i < arguments.size(); i++) { auto &arg = arguments[i]; if (!arg.HasName()) { continue; } - auto param_idx = sig.GetParameterIndexByName(Identifier(arg.GetName())); + auto name = Identifier(arg.GetName()); + if (seen_names.find(name) != seen_names.end()) { + throw BinderException(arg.GetQueryLocation(), "Duplicate type parameter %s for type %s", arg.GetName(), + type_name); + } + seen_names.insert(name); + + auto param_idx = sig.GetParameterIndexByName(name); if (!param_idx.IsValid()) { // not a declared parameter - it can only be a named vararg varargs.emplace_back(i, arg); @@ -192,8 +207,11 @@ vector NormalizeArguments(const Identifier &type_name, const TypeC auto &arg = entry.second.get(); auto arg_name = ArgumentName(Identifier(arg.GetName()), entry.first); auto location = arg.GetQueryLocation(); - result.emplace_back(arg.GetName(), - CastArgument(type_name, arg_name, arg.GetValue(), sig.GetVarArgs(), location), location); + // a named modifier is received by "**kwargs", an unnamed one by "*args" + auto param = arg.HasName() ? sig.GetKwargs() : sig.GetArgs(); + auto &target = param ? param->GetType() : LogicalType::ANY; + result.emplace_back(arg.GetName(), CastArgument(type_name, arg_name, arg.GetValue(), target, location), + location); } return result; } @@ -208,7 +226,7 @@ LogicalType TypeConstructorSet::Bind(optional_ptr context, const // parameterised. It also gets a dedicated error, as the generic one would list a single empty candidate. if (functions.size() == 1) { const auto &sig = functions[0]->GetSignature(); - if (sig.GetParameterCount() == 0 && !sig.HasVarArgs()) { + if (sig.GetParameterCount() == 0) { if (!arguments.empty()) { throw BinderException(query_location, "Type %s does not take any type parameters", name); } diff --git a/src/duckdb/src/function/window/window_aggregate_states.cpp b/src/duckdb/src/function/window/window_aggregate_states.cpp index 75de72a3c..a11fbfe1c 100644 --- a/src/duckdb/src/function/window/window_aggregate_states.cpp +++ b/src/duckdb/src/function/window/window_aggregate_states.cpp @@ -4,15 +4,18 @@ namespace duckdb { WindowAggregateStates::WindowAggregateStates(ClientContext &client, const AggregateObject &aggr) : client(client), aggr(aggr), state_size(aggr.function.GetStateSize(aggr.GetFunctionData())), - allocator(Allocator::Get(client)) { + allocator(BufferAllocator::Get(client)) { } -void WindowAggregateStates::Initialize(idx_t count) { +void WindowAggregateStates::Initialize(idx_t count_p) { // Don't leak - every Initialize must be matched with a Destroy - D_ASSERT(states.empty()); + D_ASSERT(count == 0); - states.resize(count * state_size); - auto state_ptr = states.data(); + count = count_p; + if (count > 0) { + states = BufferAllocator::Get(client).Allocate(count * state_size); + } + auto state_ptr = states.get(); statef = make_uniq(LogicalType::POINTER, count); auto state_f_data = FlatVector::Writer(*statef, count); @@ -37,7 +40,7 @@ void WindowAggregateStates::Finalize(Vector &result) { } void WindowAggregateStates::Destroy() { - if (states.empty()) { + if (count == 0) { return; } @@ -46,7 +49,8 @@ void WindowAggregateStates::Destroy() { aggr.function.GetStateDestructorCallback()(*statef, aggr_input_data, GetCount()); } - states.clear(); + states.Reset(); + count = 0; } } // namespace duckdb diff --git a/src/duckdb/src/function/window/window_aggregator.cpp b/src/duckdb/src/function/window/window_aggregator.cpp index 3f2846917..d034c2619 100644 --- a/src/duckdb/src/function/window/window_aggregator.cpp +++ b/src/duckdb/src/function/window/window_aggregator.cpp @@ -33,7 +33,9 @@ WindowAggregatorGlobalState::WindowAggregatorGlobalState(ClientContext &client, locals(0), finalized(0) { if (aggr.filter) { // Start with all invalid and set the ones that pass - filter_mask.Initialize(group_count, false); + filter_mask_data = BufferAllocator::Get(client).Allocate(group_count * sizeof(bool)); + memset(filter_mask_data.get(), 0, filter_mask_data.GetSize()); + filter_mask.Initialize(reinterpret_cast(filter_mask_data.get()), group_count); } else { filter_mask.InitializeEmpty(group_count); } diff --git a/src/duckdb/src/function/window/window_collection.cpp b/src/duckdb/src/function/window/window_collection.cpp index 370065e92..5a1b0875f 100644 --- a/src/duckdb/src/function/window/window_collection.cpp +++ b/src/duckdb/src/function/window/window_collection.cpp @@ -37,20 +37,24 @@ void WindowCollection::GetCollection(idx_t row_idx, ColumnDataCollectionSpec &sp } void WindowCollection::Combine(const ColumnSet &validity_cols) { - lock_guard collection_guard(lock); + CallOnce(combine_once, [&] { + try { + CombineInternal(validity_cols); + } catch (...) { + combine_error = std::current_exception(); + } + }); + if (combine_error) { + std::rethrow_exception(combine_error); + } +} +void WindowCollection::CombineInternal(const ColumnSet &validity_cols) { // If there are no columns (COUNT(*)) then this is a NOP if (types.empty()) { return; } - // Have we already combined? - if (inputs->Count()) { - D_ASSERT(collections.empty()); - D_ASSERT(ranges.empty()); - return; - } - // If there are columns, we should have data D_ASSERT(!collections.empty()); D_ASSERT(!ranges.empty()); diff --git a/src/duckdb/src/function/window/window_distinct_aggregator.cpp b/src/duckdb/src/function/window/window_distinct_aggregator.cpp index bf31eaac7..107f54dc9 100644 --- a/src/duckdb/src/function/window/window_distinct_aggregator.cpp +++ b/src/duckdb/src/function/window/window_distinct_aggregator.cpp @@ -22,7 +22,8 @@ bool WindowDistinctAggregator::CanAggregate(const BoundWindowExpression &wexpr) return false; } - if (!wexpr.AggregateFunction()->CanAggregate() || wexpr.AggregateFunction()->CanWindow()) { + auto &aggr = *wexpr.AggregateFunction(); + if (!aggr.CanAggregate() || aggr.CanWindow() || aggr.IsHolistic()) { return false; } @@ -44,7 +45,8 @@ class WindowDistinctSortTree : public MergeSortTree { using ZippedTuple = std::tuple; using ZippedElements = vector; - WindowDistinctSortTree(WindowDistinctAggregatorGlobalState &gdastate, idx_t count) : gdastate(gdastate) { + WindowDistinctSortTree(Allocator &allocator, WindowDistinctAggregatorGlobalState &gdastate, idx_t count) + : MergeSortTree(allocator), gdastate(gdastate) { // Set up for parallel build build_level = 0; build_complete = 0; @@ -117,7 +119,8 @@ WindowDistinctAggregatorGlobalState::WindowDistinctAggregatorGlobalState(ClientC const WindowDistinctAggregator &aggregator, idx_t group_count) : WindowAggregatorGlobalState(client, aggregator, group_count), stage(WindowDistinctSortStage::INIT), - tasks_assigned(0), tasks_completed(0), merge_sort_tree(*this, group_count), levels_flat_native(client, aggr) { + tasks_assigned(0), tasks_completed(0), zipped_tree(BufferAllocator::Get(client)), + merge_sort_tree(BufferAllocator::Get(client), *this, group_count), levels_flat_native(client, aggr) { // 1: functionComputePrevIdcs(𝑖𝑛) // 2: sorted ← [] // We sort the aggregate arguments and use the partition index as a tie-breaker. @@ -159,8 +162,8 @@ WindowDistinctAggregatorGlobalState::WindowDistinctAggregatorGlobalState(ClientC merge_sort_tree.tree.reserve(zipped_tree.tree.size()); for (idx_t level_nr = 0; level_nr < zipped_tree.tree.size(); ++level_nr) { auto &zipped_level = zipped_tree.tree[level_nr].first; - WindowDistinctSortTree::Elements level; - WindowDistinctSortTree::Offsets cascades; + WindowDistinctSortTree::Elements level(merge_sort_tree.allocator); + WindowDistinctSortTree::Offsets cascades(merge_sort_tree.allocator); level.resize(zipped_level.size()); merge_sort_tree.tree.emplace_back(std::move(level), std::move(cascades)); } diff --git a/src/duckdb/src/function/window/window_match_recognize.cpp b/src/duckdb/src/function/window/window_match_recognize.cpp index a18e5df4c..13838e2ad 100644 --- a/src/duckdb/src/function/window/window_match_recognize.cpp +++ b/src/duckdb/src/function/window/window_match_recognize.cpp @@ -1161,11 +1161,10 @@ WindowFunction MatchRecognizeFun::GetFunction() { WindowMatchRecognizeExecutor::GetGlobal, WindowMatchRecognizeExecutor::GetLocal, WindowMatchRecognizeExecutor::Sink, WindowMatchRecognizeExecutor::Finalize, WindowMatchRecognizeExecutor::GetData); - fun.SetVarArgs(LogicalType::ANY); - auto &signature = fun.GetSignature(); signature = FunctionSignature(vector(), WindowMatchRecognizeExecutor::ResultType()); signature.AddParameter(Identifier("columns"), LogicalType::ANY); + signature.AddArgs("args", LogicalType::ANY); fun.SetSerializeCallback(WindowMatchRecognizeExecutor::Serialize); fun.SetDeserializeCallback(WindowMatchRecognizeExecutor::Deserialize); diff --git a/src/duckdb/src/function/window/window_merge_sort_tree.cpp b/src/duckdb/src/function/window/window_merge_sort_tree.cpp index 035dde505..0d330ee74 100644 --- a/src/duckdb/src/function/window/window_merge_sort_tree.cpp +++ b/src/duckdb/src/function/window/window_merge_sort_tree.cpp @@ -16,10 +16,10 @@ WindowMergeSortTree::WindowMergeSortTree(ClientContext &client, const vector::max() && !force_external) { index_type = LogicalType::INTEGER; - mst32 = make_uniq(); + mst32 = make_uniq(BufferAllocator::Get(client)); } else { index_type = LogicalType::BIGINT; - mst64 = make_uniq(); + mst64 = make_uniq(BufferAllocator::Get(client)); } vector orders; diff --git a/src/duckdb/src/function/window/window_naive_aggregator.cpp b/src/duckdb/src/function/window/window_naive_aggregator.cpp index c588c4034..9a7390edc 100644 --- a/src/duckdb/src/function/window/window_naive_aggregator.cpp +++ b/src/duckdb/src/function/window/window_naive_aggregator.cpp @@ -359,6 +359,7 @@ void WindowNaiveLocalState::Evaluate(ExecutionContext &context, const WindowAggr // Destruct the result aggregates if (aggr.function.HasStateDestructorCallback()) { aggr.function.GetStateDestructorCallback()(statef, aggr_input_data, count); + allocator.FreeAll(); } } diff --git a/src/duckdb/src/function/window/window_scalar_function.cpp b/src/duckdb/src/function/window/window_scalar_function.cpp index cf7b70684..61d330bc7 100644 --- a/src/duckdb/src/function/window/window_scalar_function.cpp +++ b/src/duckdb/src/function/window/window_scalar_function.cpp @@ -1,6 +1,8 @@ #include "duckdb/function/window/window_shared_expressions.hpp" #include "duckdb/function/window_function.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" +#include "duckdb/planner/expression/bound_reference_expression.hpp" +#include "duckdb/execution/expression_executor.hpp" #include "duckdb/planner/expression/bound_window_expression.hpp" #include "duckdb/function/aggregate_state.hpp" #include "duckdb/function/aggregate_function.hpp" @@ -40,6 +42,17 @@ struct ScalarWindowBindData : public FunctionData { unique_ptr wexpr; }; +// A degenerate frame holds at most the current row, which these exclusions remove +idx_t DegenerateFrameWidth(const BoundWindowExpression &wexpr) { + switch (wexpr.WindowExclude()) { + case WindowExcludeMode::CURRENT_ROW: + case WindowExcludeMode::GROUP: + return 0; + default: + return 1; + } +} + void AggregateScalarFunc(DataChunk &args, ExpressionState &state, Vector &result) { auto &func_expr = state.expr.Cast(); auto &scalar_info = func_expr.BindInfo()->Cast(); @@ -47,7 +60,7 @@ void AggregateScalarFunc(DataChunk &args, ExpressionState &state, Vector &result auto bind_info = wexpr.BindInfo().get(); // Is the frame empty? - const idx_t width = (wexpr.WindowExclude() == WindowExcludeMode::CURRENT_ROW) ? 0 : 1; + const idx_t width = DegenerateFrameWidth(wexpr); auto &client = scalar_info.client; ThreadContext thread(client); @@ -80,8 +93,34 @@ void AggregateScalarFunc(DataChunk &args, ExpressionState &state, Vector &result // Update the state if the frame is not empty AggregateFinalizeInputData aggr_bind_info(aggr.function, bind_info, arena_allocator); if (width) { + // The arguments reference the input row + DataChunk inputs; + ExpressionExecutor input_exec(client); + vector input_types; + for (const auto &child : wexpr.GetChildren()) { + input_exec.AddExpression(*child); + input_types.emplace_back(child->GetReturnType()); + } + if (!input_types.empty()) { + inputs.Initialize(allocator, input_types); + input_exec.Execute(args, inputs); + inputs.CheckCardinality(count); + } else { + inputs.SetCardinalityUnsafe(count); + } + + // Rows rejected by the filter leave their state empty auto update = aggr.function.GetCallbacks().GetStateUpdateCallback(); - update(args.data.data(), aggr_bind_info, args.data.size(), statev, count); + if (wexpr.Filter()) { + SelectionVector sel(count); + ExpressionExecutor filter_exec(client, *wexpr.Filter()); + const auto update_count = filter_exec.SelectExpression(args, sel); + inputs.Slice(sel, update_count); + Vector update_states(statev, sel, update_count); + update(inputs.data.data(), aggr_bind_info, inputs.ColumnCount(), update_states, update_count); + } else { + update(inputs.data.data(), aggr_bind_info, inputs.ColumnCount(), statev, count); + } } // Finalize the states @@ -101,7 +140,7 @@ void WindowScalarFunc(DataChunk &args, ExpressionState &state, Vector &result) { auto &wexpr = scalar_info.wexpr->Cast(); // Is the frame empty? - const idx_t width = (wexpr.WindowExclude() == WindowExcludeMode::CURRENT_ROW) ? 0 : 1; + const idx_t width = DegenerateFrameWidth(wexpr); auto &client = scalar_info.client; ThreadContext thread(client); @@ -110,20 +149,6 @@ void WindowScalarFunc(DataChunk &args, ExpressionState &state, Vector &result) { ArenaAllocator arena_allocator(allocator); const auto count = args.size(); if (width) { - // Set the bounds to the single frame. - vector bounds_types(8, LogicalType(LogicalTypeId::UBIGINT)); - DataChunk bounds; - bounds.Initialize(allocator, bounds_types); - auto frame_begin = FlatVector::Writer(bounds.data[PARTITION_BEGIN], count); - auto frame_end = FlatVector::Writer(bounds.data[PARTITION_END], count); - for (idx_t i = 0; i < count; ++i) { - frame_begin.WriteValue(i); - frame_end.WriteValue(i + 1); - } - for (column_t col = 2; col < bounds.ColumnCount(); ++col) { - bounds.data[col].Reference(bounds.data[col % 2]); - } - // Build shared expressions WindowSharedExpressions shared; WindowExecutor wexec(wexpr, shared); @@ -133,9 +158,14 @@ void WindowScalarFunc(DataChunk &args, ExpressionState &state, Vector &result) { shared.PrepareCollection(coll_exec, coll_chunk); // Build acceleration data - ValidityMask mask(count); - mask.SetAllInvalid(count); - auto gsink = wexec.GetGlobalState(client, count, mask, mask); + // One partition in which every row is its own peer group, so the frame is the current row + ValidityMask partition_mask; + partition_mask.Initialize(count); + partition_mask.SetAllInvalid(count); + partition_mask.SetValid(0); + ValidityMask order_mask; + order_mask.Initialize(count); + auto gsink = wexec.GetGlobalState(client, count, partition_mask, order_mask); auto lsink = wexec.GetLocalState(context, *gsink); // Compute fully materialised expressions @@ -185,23 +215,26 @@ void WindowScalarFunc(DataChunk &args, ExpressionState &state, Vector &result) { } // namespace -unique_ptr FunctionBinder::BindScalarWindowFunction(BoundWindowExpression &wexpr) { - vector arguments; - for (const auto &child : wexpr.GetChildren()) { - arguments.emplace_back(child->GetReturnType()); +unique_ptr FunctionBinder::BindScalarWindowFunction(BoundWindowExpression &wexpr, + const vector &input_types) { + // The window expressions reference the input row, so the function receives all of it + vector> children; + for (idx_t col_idx = 0; col_idx < input_types.size(); ++col_idx) { + children.emplace_back(make_uniq(input_types[col_idx], col_idx)); } auto &aggr = wexpr.AggregateFunction(); auto func = aggr ? AggregateScalarFunc : WindowScalarFunc; - ScalarFunction scalar(wexpr.GetName(), arguments, wexpr.GetReturnType(), func); + ScalarFunction scalar(wexpr.GetName(), input_types, wexpr.GetReturnType(), func); if (aggr) { scalar.SetProperties(aggr->GetProperties()); } else { scalar.SetProperties(wexpr.WindowFunction()->GetProperties()); } + // NULLs in the input row do not imply a NULL result + scalar.SetNullHandling(FunctionNullHandling::SPECIAL_HANDLING); auto bind_info = make_uniq(context, wexpr); BoundScalarFunction bound(scalar); - - return make_uniq(bound, std::move(wexpr.GetChildrenMutable()), std::move(bind_info)); + return make_uniq(bound, std::move(children), std::move(bind_info)); } } // namespace duckdb diff --git a/src/duckdb/src/function/window/window_segment_tree.cpp b/src/duckdb/src/function/window/window_segment_tree.cpp index df23875e1..7ff165776 100644 --- a/src/duckdb/src/function/window/window_segment_tree.cpp +++ b/src/duckdb/src/function/window/window_segment_tree.cpp @@ -1,5 +1,6 @@ #include "duckdb/function/window/window_segment_tree.hpp" +#include "duckdb/common/error_data.hpp" #include "duckdb/function/window/window_aggregate_states.hpp" #include "duckdb/planner/expression/bound_window_expression.hpp" @@ -15,12 +16,13 @@ bool WindowSegmentTree::CanAggregate(const BoundWindowExpression &wexpr) { return false; } - if (!wexpr.AggregateFunction()->CanAggregate()) { + auto &aggr = *wexpr.AggregateFunction(); + if (!aggr.CanAggregate()) { return false; } - // Don't use segment trees for custom windowing - if (wexpr.AggregateFunction()->CanWindow()) { + // Don't use segment trees for custom windowing or holistic aggregates + if (aggr.CanWindow() || aggr.IsHolistic()) { return false; } @@ -51,6 +53,11 @@ class WindowSegmentTreeGlobalState : public WindowAggregatorGlobalState { unique_ptr build_started; //! The number of entries completed so far at each level unique_ptr build_completed; + //! Whether building an entry failed - the level being built will then never be completed + std::atomic build_failed; + //! The error that made building an entry fail + mutex build_lock; + ErrorData build_error; //! The tree allocators. //! We need to hold onto them for the tree lifetime, //! not the lifetime of the local state that constructed part of the tree @@ -81,6 +88,8 @@ class WindowSegmentTreePart { data_ptr_t current_state); //! Writes result and calls destructors void Finalize(Vector &result, idx_t count); + //! Calls the destructors of the accumulation states (statef) without finalizing + void Destroy(idx_t count); void Combine(WindowSegmentTreePart &other, idx_t count); @@ -296,6 +305,14 @@ void WindowSegmentTreePart::Finalize(Vector &result, idx_t count) { } } +void WindowSegmentTreePart::Destroy(idx_t count) { + if (!aggr.function.HasStateDestructorCallback()) { + return; + } + AggregateFinalizeInputData aggr_input_data(aggr, allocator); + aggr.function.GetStateDestructorCallback()(statef, aggr_input_data, count); +} + WindowSegmentTreeGlobalState::WindowSegmentTreeGlobalState(ClientContext &client, const WindowSegmentTree &aggregator, idx_t group_count, const ValidityMask &partition_mask) : WindowAggregatorGlobalState(client, aggregator, group_count), tree(aggregator), levels_flat_native(client, aggr) { @@ -331,6 +348,7 @@ WindowSegmentTreeGlobalState::WindowSegmentTreeGlobalState(ClientContext &client // Start by building from the bottom level build_level = 0; + build_failed = false; build_started = make_uniq(levels_flat_start.size()); for (auto &counter : *build_started) { @@ -361,7 +379,7 @@ void WindowSegmentTreeLocalState::Finalize(ExecutionContext &context, WindowAggr const auto &partition_offsets = gstate.partition_offsets; idx_t combine_width = gstate.TREE_FANOUT; idx_t combine_level = 0; - auto combine_begin = partition_offsets.rbegin(); + auto combine_begin = partition_offsets.rend(); std::greater combine_cmp; auto &levels_flat_native = gstate.levels_flat_native; @@ -381,45 +399,66 @@ void WindowSegmentTreeLocalState::Finalize(ExecutionContext &context, WindowAggr } const idx_t build_count = (level_size + gstate.TREE_FANOUT - 1) / gstate.TREE_FANOUT; - // Build the next fan-in - const idx_t build_idx = (*gstate.build_started).at(level_current)++; - if (build_idx >= build_count) { + // Keep adjacent leaves on the same cursor to reuse its pinned input chunk. + const idx_t build_batch = + level_current == 0 ? MaxValue(1, STANDARD_VECTOR_SIZE / gstate.TREE_FANOUT) : 1; + const idx_t build_begin = (*gstate.build_started).at(level_current).fetch_add(build_batch); + if (build_begin >= build_count) { // Nothing left at this level, so wait until other threads are done. - // Since we are only building TREE_FANOUT values at a time, this will be quick. - while (level_current == gstate.build_level.load()) { + while (level_current == gstate.build_level.load() && !gstate.build_failed) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } + if (gstate.build_failed) { + // another thread failed to build its part of the level, so the tree will never be complete + const lock_guard guard(gstate.build_lock); + gstate.build_error.Throw(); + } continue; } // Update the combine width to the current level. for (; combine_level < level_current; ++combine_level) { combine_width *= gstate.TREE_FANOUT; - combine_begin = partition_offsets.rbegin(); + combine_begin = partition_offsets.rend(); } - // Check for crossing a partition boundary - // Note that it is OK for a state to _start_ on a partition boundary - // - it just shouldn't _contain_ one. - const idx_t combine_left = build_idx * combine_width; - const idx_t combine_right = MinValue(combine_left + combine_width, leaf_count); - combine_begin = std::lower_bound(partition_offsets.rbegin(), combine_begin, combine_left, combine_cmp); - auto combine_end = std::lower_bound(partition_offsets.rbegin(), combine_begin, combine_right - 1, combine_cmp); - const bool has_boundary = (combine_begin != combine_end); - combine_begin = combine_end; - - // compute the aggregate for this entry in the segment tree - if (!has_boundary) { - const idx_t pos = build_idx * gstate.TREE_FANOUT; - const idx_t levels_flat_offset = levels_flat_start[level_current] + build_idx; - auto state_ptr = levels_flat_native.GetStatePtr(levels_flat_offset); - gtstate.WindowSegmentValue(gstate, level_current, pos, MinValue(level_size, pos + gstate.TREE_FANOUT), - state_ptr); - gtstate.FlushStates(level_current > 0); + const idx_t build_end = MinValue(build_begin + build_batch, build_count); + for (idx_t build_idx = build_begin; build_idx < build_end; ++build_idx) { + // Check for crossing a partition boundary + // Note that it is OK for a state to _start_ on a partition boundary + // - it just shouldn't _contain_ one. + const idx_t combine_left = build_idx * combine_width; + const idx_t combine_right = MinValue(combine_left + combine_width, leaf_count); + combine_begin = std::lower_bound(partition_offsets.rbegin(), combine_begin, combine_left, combine_cmp); + auto combine_end = + std::lower_bound(partition_offsets.rbegin(), combine_begin, combine_right - 1, combine_cmp); + const bool has_boundary = (combine_begin != combine_end); + combine_begin = combine_end; + + // compute the aggregate for this entry in the segment tree + if (!has_boundary) { + const idx_t pos = build_idx * gstate.TREE_FANOUT; + const idx_t levels_flat_offset = levels_flat_start[level_current] + build_idx; + auto state_ptr = levels_flat_native.GetStatePtr(levels_flat_offset); + try { + gtstate.WindowSegmentValue(gstate, level_current, pos, + MinValue(level_size, pos + gstate.TREE_FANOUT), state_ptr); + gtstate.FlushStates(level_current > 0); + } catch (std::exception &ex) { + // wake up the threads waiting for this level to be completed + { + const lock_guard guard(gstate.build_lock); + gstate.build_error = ErrorData(ex); + } + gstate.build_failed = true; + throw; + } + } } // If that was the last one, mark the level as complete. - const idx_t build_complete = ++(*gstate.build_completed).at(level_current); + const idx_t completed = build_end - build_begin; + const idx_t build_complete = (*gstate.build_completed).at(level_current).fetch_add(completed) + completed; if (build_complete == build_count) { gstate.build_level++; continue; @@ -465,6 +504,10 @@ void WindowSegmentTreeLocalState::Evaluate(ExecutionContext &context, const Wind // 4. combine the buffer state into the Segment Tree State part->Combine(*right_part, count); + + // right_part's states are re-initialized on every chunk, so destroy them after combining to avoid leaking + // aggregate states that own heap memory (e.g. histogram, reservoir_quantile) + right_part->Destroy(count); } else { part->Evaluate(gtstate, window_begin, window_end, nullptr, result, count, row_idx, WindowSegmentTreePart::FULL); } diff --git a/src/duckdb/src/function/window/window_token_tree.cpp b/src/duckdb/src/function/window/window_token_tree.cpp index 36673fd27..81dfe8b1e 100644 --- a/src/duckdb/src/function/window/window_token_tree.cpp +++ b/src/duckdb/src/function/window/window_token_tree.cpp @@ -55,8 +55,9 @@ idx_t WindowTokenTree::MeasurePayloadBlocks() { return count; } -template -static void BuildTokens(WindowTokenTree &token_tree, vector &tokens) { +template +static void BuildTokens(WindowTokenTree &token_tree, TOKENS &tokens) { + using T = typename TOKENS::value_type; auto &collection = *token_tree.sorted; if (!collection.Count()) { return; diff --git a/src/duckdb/src/function/window/window_value_function.cpp b/src/duckdb/src/function/window/window_value_function.cpp index 46b9cf04e..6d9e66ecc 100644 --- a/src/duckdb/src/function/window/window_value_function.cpp +++ b/src/duckdb/src/function/window/window_value_function.cpp @@ -149,8 +149,15 @@ class WindowValueStreamingState : public WindowExecutorStreamingState { WindowValueStreamingState(ClientContext &client, DataChunk &input, const BoundWindowExpression &wexpr) : wexpr(wexpr), vec(GetFirstValue(client, input, wexpr), count_t(STANDARD_VECTOR_SIZE)), - sel(STANDARD_VECTOR_SIZE), eval(client), arg(wexpr.GetChildren()[0]->GetReturnType()) { + sel(STANDARD_VECTOR_SIZE), eval(client) { eval.AddExpression(*wexpr.GetChildren()[0]); + arg_chunk.Initialize(client, {wexpr.GetChildren()[0]->GetReturnType()}); + } + + Vector &EvalArg(DataChunk &input) { + arg_chunk.Reset(); + eval.Execute(input, arg_chunk); + return arg_chunk.data[0]; } const BoundWindowExpression &wexpr; @@ -160,8 +167,8 @@ class WindowValueStreamingState : public WindowExecutorStreamingState { SelectionVector sel; //! An executor for computing the argument ExpressionExecutor eval; - //! A reusable argument vector - Vector arg; + //! A reusable argument chunk + DataChunk arg_chunk; }; //===--------------------------------------------------------------------===// @@ -179,6 +186,9 @@ struct WindowValueExecutor { static unique_ptr GetLocal(ExecutionContext &context, const GlobalSinkState &gstate); //! Streaming APIs + static bool ArgumentIsStreamable(const BoundWindowExpression &wexpr) { + return !wexpr.IsVolatile(); + } static unique_ptr GetStreamingState(ClientContext &client, DataChunk &input, const BoundWindowExpression &wexpr) { return make_uniq(client, input, wexpr); @@ -361,6 +371,9 @@ class WindowLeadLagStreamingState : public WindowExecutorStreamingState { return false; } offset = bigint_value->GetValue(); + if (offset == NumericLimits::Minimum()) { + return false; + } } // We can only support LEAD and LAG values within one standard vector @@ -528,6 +541,9 @@ struct WindowLeadLagExecutor : public WindowValueExecutor { if (!WindowLeadLagStreamingState::ComputeOffset(client, wexpr, offset)) { return false; } + if (offset < 0 && !ArgumentIsStreamable(wexpr)) { + return false; + } return UnsafeNumericCast(std::abs(offset)) < max_delta; } @@ -612,6 +628,19 @@ unique_ptr WindowLeadLagExecutor::GetLocal(ExecutionContext &con return make_uniq(context, glstate); } +//! The index of the row at the offset - saturated if it does not fit, as it is outside the partition anyway +static int64_t LeadLagIndex(ExpressionType type, int64_t row_idx, int64_t offset) { + int64_t result; + if (type == ExpressionType::WINDOW_LEAD) { + if (!TryAddOperator::Operation(row_idx, offset, result)) { + result = offset < 0 ? NumericLimits::Minimum() : NumericLimits::Maximum(); + } + } else if (!TrySubtractOperator::Operation(row_idx, offset, result)) { + result = offset < 0 ? NumericLimits::Maximum() : NumericLimits::Minimum(); + } + return result; +} + void WindowLeadLagExecutor::GetData(ExecutionContext &context, DataChunk &eval_chunk, DataChunk &bounds, Vector &result, idx_t row_idx, OperatorSinkInput &sink) { auto &glstate = sink.global_state.Cast(); @@ -652,12 +681,7 @@ void WindowLeadLagExecutor::GetData(ExecutionContext &context, DataChunk &eval_c frame = FrameBounds(frame_begin[i], MaxValue(frame_end[i], frame_begin[i])); const auto own_row = glstate.row_tree->Rank(frame.start, frame.end, row_idx) - 1; // (2) adjust the row number by adding or subtracting an offset - auto val_idx = NumericCast(own_row); - if (wexpr.GetExpressionType() == ExpressionType::WINDOW_LEAD) { - val_idx = AddOperatorOverflowCheck::Operation(val_idx, offset); - } else { - val_idx = SubtractOperatorOverflowCheck::Operation(val_idx, offset); - } + const auto val_idx = LeadLagIndex(wexpr.GetExpressionType(), NumericCast(own_row), offset); const auto frame_width = NumericCast(frame.end - frame.start); if (val_idx >= 0 && val_idx < frame_width) { // (3) find the row at that offset @@ -711,12 +735,7 @@ void WindowLeadLagExecutor::GetData(ExecutionContext &context, DataChunk &eval_c } offset = leadlag_offset.GetCell(i); } - int64_t val_idx = (int64_t)row_idx; - if (wexpr.GetExpressionType() == ExpressionType::WINDOW_LEAD) { - val_idx = AddOperatorOverflowCheck::Operation(val_idx, offset); - } else { - val_idx = SubtractOperatorOverflowCheck::Operation(val_idx, offset); - } + auto val_idx = LeadLagIndex(wexpr.GetExpressionType(), NumericCast(row_idx), offset); idx_t delta = 0; if (val_idx < (int64_t)row_idx) { @@ -783,6 +802,9 @@ struct WindowFirstValueExecutor : public WindowValueExecutor { //! Streaming APIs static bool CanStream(ClientContext &client, const BoundWindowExpression &wexpr, idx_t max_delta) { + if (!ArgumentIsStreamable(wexpr)) { + return false; + } if (wexpr.IgnoreNulls()) { // We can stream first values ignoring NULLs if they are "running totals" return wexpr.WindowStart() == WindowBoundary::UNBOUNDED_PRECEDING && @@ -809,9 +831,7 @@ void WindowFirstValueExecutor::StreamData(ExecutionContext &context, DataChunk & // then look for a non-NULL value and update it if (wexpr.IgnoreNulls() && ConstantVector::IsNull(sstate.vec)) { // Find the first non-NULL value - auto &executor = sstate.eval; - auto &arg = sstate.arg; - executor.ExecuteExpression(input, arg); + auto &arg = sstate.EvalArg(input); UnifiedVectorFormat unified; arg.ToUnifiedFormat(unified); const auto &validity = unified.validity; @@ -914,6 +934,9 @@ struct WindowLastValueExecutor : public WindowValueExecutor { //! Streaming APIs static bool CanStream(ClientContext &client, const BoundWindowExpression &wexpr, idx_t max_delta) { + if (!ArgumentIsStreamable(wexpr)) { + return false; + } // We can stream last values if they are "running totals" return wexpr.WindowStart() == WindowBoundary::UNBOUNDED_PRECEDING && wexpr.WindowEnd() == WindowBoundary::CURRENT_ROW_ROWS; @@ -931,8 +954,7 @@ void WindowLastValueExecutor::StreamData(ExecutionContext &context, DataChunk &i auto &executor = sstate.eval; if (wexpr.IgnoreNulls()) { auto &prev = sstate.vec; - auto &arg = sstate.arg; - executor.ExecuteExpression(input, arg); + auto &arg = sstate.EvalArg(input); UnifiedVectorFormat unified; arg.ToUnifiedFormat(unified); const auto &validity = unified.validity; @@ -943,7 +965,7 @@ void WindowLastValueExecutor::StreamData(ExecutionContext &context, DataChunk &i Vector copy(wexpr.GetChildren()[0]->GetReturnType()); VectorOperations::Copy(arg, copy, count, 0, 0); // Overwrite the previous non-NULL value if the first one is NULL - if (!validity.RowIsValidUnsafe(0)) { + if (!validity.RowIsValidUnsafe(unified.sel->get_index(0))) { VectorOperations::Copy(prev, copy, 1, 0, 0); } // Select appropriate the non-NULL values to copy over @@ -1078,6 +1100,9 @@ struct WindowNthValueExecutor : public WindowValueExecutor { //! Streaming APIs static bool CanStream(ClientContext &client, const BoundWindowExpression &wexpr, idx_t max_delta) { + if (!ArgumentIsStreamable(wexpr)) { + return false; + } // We can only stream Nth Value if N is positive constant. idx_t nth_index; if (!WindowNthValueStreamingState::ComputeNthIndex(client, wexpr, nth_index)) { @@ -1112,7 +1137,7 @@ void WindowNthValueStreamingState::StreamData(ExecutionContext &context, DataChu return; } - eval.ExecuteExpression(input, arg); + auto &arg = EvalArg(input); UnifiedVectorFormat unified; arg.ToUnifiedFormat(unified); @@ -1524,7 +1549,8 @@ void WindowFillExecutor::GetSharing(WindowExecutor &executor, WindowSharedExpres if (arg_order_idx.empty()) { // We use the range ordering, even if it has not been defined if (executor.range_idx == DConstants::INVALID_INDEX) { - D_ASSERT(wexpr.OrderBy().size() == 1); + // the first ordering - also when it is shared with an argument ordering that is a prefix of the orderings + D_ASSERT(!wexpr.OrderBy().empty()); // We don't need the validity mask because we have also requested the valid range for the ordering. executor.range_idx = shared.RegisterCollection(wexpr.OrderBy()[0].expression, false); } diff --git a/src/duckdb/src/function/window_function.cpp b/src/duckdb/src/function/window_function.cpp index f0e638c1e..11fe6b1fd 100644 --- a/src/duckdb/src/function/window_function.cpp +++ b/src/duckdb/src/function/window_function.cpp @@ -26,9 +26,7 @@ BoundWindowFunction::BoundWindowFunction(const WindowFunction &base) BoundWindowFunction::BoundWindowFunction(shared_ptr base_p) : window_enum(base_p->window_enum), definition(std::move(base_p)) { auto &base = *definition; - name = base.name; - schema_name = base.GetSchemaName(); - catalog_name = base.GetCatalogName(); + qualified_name = base.GetQualifiedName(); extra_info = base.extra_info; return_type = base.GetReturnType(); callbacks = base.GetCallbacks(); @@ -38,12 +36,19 @@ BoundWindowFunction::BoundWindowFunction(shared_ptr base_p // Try to default bind the function, to fill in any missing information in the BoundScalarFunction (e.g. from the // "bind" callback) for (auto ¶m : base.GetSignature().GetParameters()) { - arguments.push_back(param.GetType()); + if (!param.IsVariadic()) { + arguments.push_back(param.GetType()); + } } + positional_arguments = arguments.size(); + logical_arguments = arguments; + logical_return_type = return_type; } bool BoundWindowFunction::operator==(const BoundWindowFunction &rhs) const { - return window_enum == rhs.window_enum && arguments == rhs.arguments && return_type == rhs.return_type; + return window_enum == rhs.window_enum && arguments == rhs.arguments && + positional_arguments == rhs.positional_arguments && named_arguments == rhs.named_arguments && + return_type == rhs.return_type; } bool BoundWindowFunction::operator!=(const BoundWindowFunction &rhs) const { diff --git a/src/duckdb/src/include/duckdb.h b/src/duckdb/src/include/duckdb.h index 435115a3a..517bb60b1 100644 --- a/src/duckdb/src/include/duckdb.h +++ b/src/duckdb/src/include/duckdb.h @@ -404,6 +404,7 @@ typedef enum duckdb_error_type { DUCKDB_ERROR_SEQUENCE = 41, DUCKDB_ERROR_DATA_CORRUPTION = 43, DUCKDB_INVALID_CONFIGURATION = 42, + DUCKDB_ERROR_RESOURCE_IN_USE = 44, } duckdb_error_type; /*! @@ -2244,10 +2245,10 @@ DUCKDB_C_API duckdb_error_data duckdb_appender_error_data(duckdb_appender append #if DUCKDB_API_VERSION_AT_LEAST(0, 2, 5) /*! - * Flush the appender to the table, forcing the cache of the appender to be cleared. If flushing the data triggers a - * constraint violation or any other error, then all data is invalidated, and this function returns DuckDBError. It is - * not possible to append more values. Call duckdb_appender_error_data to obtain the error data followed by - * duckdb_appender_destroy to destroy the invalidated appender. + * Flush the appender to the table, forcing the cache of the appender to be cleared. Requires a connection with no open + * result. If flushing the data triggers a constraint violation or any other error, then all data is invalidated, and + * this function returns DuckDBError. It is not possible to append more values. Call duckdb_appender_error_data to + * obtain the error data followed by duckdb_appender_destroy to destroy the invalidated appender. * * history: * - stable: v0.2.5 @@ -5628,6 +5629,14 @@ DUCKDB_C_API duckdb_state duckdb_pending_prepared(duckdb_prepared_statement prep /*! * Closes the pending result and de-allocates all memory allocated for the result. * + * Destroying a pending result whose statement has not completed aborts the statement. On autocommit, its writes are + * rolled back. Inside a transaction, a statement that may write invalidates the transaction, and a read-only statement + * just stops. Until the statement has completed, preparing or running another statement on the connection fails with + * `DUCKDB_ERROR_RESOURCE_IN_USE` and leaves it untouched. `duckdb_pending_execution_is_finished` reporting true does + * not mean that the statement completed. To make sure a statement takes effect, complete it before destroying the + * pending result: call `duckdb_execute_pending`, and fetch a streaming result until `duckdb_fetch_chunk` returns NULL. + * A NULL chunk is also returned on an error, so check `duckdb_result_error` afterwards. + * * history: * - stable: v0.5.0 * @@ -6502,6 +6511,13 @@ DUCKDB_C_API duckdb_state duckdb_query(duckdb_connection connection, const char /*! * Closes the result and de-allocates all memory allocated for that result. * + * Destroying a streaming result whose statement has not completed aborts the statement. On autocommit, its writes are + * rolled back. Inside a transaction, a statement that may write invalidates the transaction, and a read-only statement + * just stops. Until the statement has completed, preparing or running another statement on the connection fails with + * `DUCKDB_ERROR_RESOURCE_IN_USE` and leaves it untouched. To make sure the statement takes effect, complete it by + * fetching until `duckdb_fetch_chunk` returns NULL before destroying the result. A NULL chunk is also returned on an + * error, so check `duckdb_result_error` afterwards. + * * history: * - stable: v0.1.0 * diff --git a/src/duckdb/src/include/duckdb.hpp b/src/duckdb/src/include/duckdb.hpp index 168dfd00c..ce47149a0 100644 --- a/src/duckdb/src/include/duckdb.hpp +++ b/src/duckdb/src/include/duckdb.hpp @@ -11,4 +11,5 @@ #include "duckdb/main/connection.hpp" #include "duckdb/main/database.hpp" #include "duckdb/main/query_result.hpp" +#include "duckdb/main/query_result_stream.hpp" #include "duckdb/main/appender.hpp" diff --git a/src/duckdb/src/include/duckdb/catalog/catalog.hpp b/src/duckdb/src/include/duckdb/catalog/catalog.hpp index fd5a99dc8..393c22471 100644 --- a/src/duckdb/src/include/duckdb/catalog/catalog.hpp +++ b/src/duckdb/src/include/duckdb/catalog/catalog.hpp @@ -33,6 +33,7 @@ struct CreateSchemaInfo; struct DropInfo; struct BoundCreateTableInfo; struct AlterTableInfo; +struct AlterSchemaInfo; struct CreateTableFunctionInfo; struct CreateCopyFunctionInfo; struct CreatePragmaFunctionInfo; @@ -244,6 +245,17 @@ class Catalog { const EntryLookupInfo &schema_lookup, OnEntryNotFound if_not_found) = 0; + //! Whether schema strings can denote nested paths as well as literal schema names. + virtual bool SupportsNestedSchemas() const { + return false; + } + //! Resolve a string schema argument, preferring an existing nested entry over a literal dotted schema. + DUCKDB_API QualifiedName ResolveEntryName(ClientContext &context, const string &schema_arg, + const string &entry_name, CatalogType type = CatalogType::TABLE_ENTRY, + optional_ptr at_clause = nullptr); + //! Resolve a string schema argument, with the same literal-name fallback as ResolveEntryName. + DUCKDB_API SchemaCatalogEntry &ResolveSchema(ClientContext &context, const string &schema_arg); + //! Returns the schema object with the specified name, or throws an exception if it does not exist DUCKDB_API SchemaCatalogEntry &GetSchema(ClientContext &context, const EntryLookupInfo &schema_lookup); DUCKDB_API optional_ptr GetSchema(ClientContext &context, const EntryLookupInfo &schema_lookup, @@ -395,6 +407,9 @@ class Catalog { return string(); } virtual ErrorData SupportsCreateTable(BoundCreateTableInfo &info); + virtual ErrorData SupportsCreateSchema(CreateSchemaInfo &info); + //! Alter a schema of this catalog (e.g. ALTER SCHEMA ... SET/RESET ()) + virtual void AlterSchema(CatalogTransaction transaction, SchemaCatalogEntry &schema, AlterSchemaInfo &info); virtual bool Supports(RemoteCapability capability) const { return false; @@ -493,6 +508,11 @@ class Catalog { DUCKDB_API virtual void OnDetach(ClientContext &context); protected: + //! Resolve a bound schema path, preserving time travel and error context at every level. + DUCKDB_API optional_ptr + LookupSchemaPath(CatalogTransaction transaction, const EntryLookupInfo &schema_lookup, OnEntryNotFound if_not_found, + const std::function(const EntryLookupInfo &)> &lookup_root); + //! Reference to the database AttachedDatabase &db; diff --git a/src/duckdb/src/include/duckdb/catalog/catalog_entry/aggregate_function_catalog_entry.hpp b/src/duckdb/src/include/duckdb/catalog/catalog_entry/aggregate_function_catalog_entry.hpp index 3c8465184..dac3e9ddf 100644 --- a/src/duckdb/src/include/duckdb/catalog/catalog_entry/aggregate_function_catalog_entry.hpp +++ b/src/duckdb/src/include/duckdb/catalog/catalog_entry/aggregate_function_catalog_entry.hpp @@ -26,5 +26,7 @@ class AggregateFunctionCatalogEntry : public FunctionEntry { //! The aggregate functions AggregateFunctionSet functions; + +private: }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/catalog/catalog_entry/column_dependency_manager.hpp b/src/duckdb/src/include/duckdb/catalog/catalog_entry/column_dependency_manager.hpp index 51cc82862..c28587527 100644 --- a/src/duckdb/src/include/duckdb/catalog/catalog_entry/column_dependency_manager.hpp +++ b/src/duckdb/src/include/duckdb/catalog/catalog_entry/column_dependency_manager.hpp @@ -38,6 +38,9 @@ class ColumnDependencyManager { //! Removes the column(s) and outputs the new column indices vector RemoveColumn(LogicalIndex index, idx_t column_amount); + //! Return a deep copy (the copy constructor is deleted to prevent accidental copies) + ColumnDependencyManager Copy() const; + bool IsDependencyOf(LogicalIndex dependent, LogicalIndex dependency) const; bool HasDependencies(LogicalIndex index) const; const logical_index_set_t &GetDependencies(LogicalIndex index) const; diff --git a/src/duckdb/src/include/duckdb/catalog/catalog_entry/duck_index_entry.hpp b/src/duckdb/src/include/duckdb/catalog/catalog_entry/duck_index_entry.hpp index 3063335b6..d023fa5bd 100644 --- a/src/duckdb/src/include/duckdb/catalog/catalog_entry/duck_index_entry.hpp +++ b/src/duckdb/src/include/duckdb/catalog/catalog_entry/duck_index_entry.hpp @@ -16,12 +16,10 @@ class TableCatalogEntry; //! Wrapper class to allow copying a DuckIndexEntry (for altering the DuckIndexEntry metadata such as comments) struct IndexDataTableInfo { - IndexDataTableInfo(shared_ptr info_p, const Identifier &index_name_p); + explicit IndexDataTableInfo(shared_ptr info_p); //! Pointer to the DataTableInfo shared_ptr info; - //! The index to be removed on destruction - Identifier index_name; }; //! A duck index entry diff --git a/src/duckdb/src/include/duckdb/catalog/catalog_entry/duck_schema_entry.hpp b/src/duckdb/src/include/duckdb/catalog/catalog_entry/duck_schema_entry.hpp index 9ee6a5349..32a9f30ef 100644 --- a/src/duckdb/src/include/duckdb/catalog/catalog_entry/duck_schema_entry.hpp +++ b/src/duckdb/src/include/duckdb/catalog/catalog_entry/duck_schema_entry.hpp @@ -19,8 +19,6 @@ class DuckSchemaEntry : public SchemaCatalogEntry { DuckSchemaEntry(Catalog &catalog, CreateSchemaInfo &info, optional_ptr parent_schema = nullptr); private: - //! The parent schema (for a nested schema), or nullptr for a top-level schema - optional_ptr parent_schema; //! The catalog set holding the nested schemas CatalogSet schemas; //! The catalog set holding the tables @@ -70,6 +68,8 @@ class DuckSchemaEntry : public SchemaCatalogEntry { optional_ptr CreateSchema(CatalogTransaction transaction, CreateSchemaInfo &info); void Alter(CatalogTransaction transaction, AlterInfo &info) override; void Scan(ClientContext &context, CatalogType type, const std::function &callback) override; + void Scan(CatalogTransaction transaction, CatalogType type, + const std::function &callback) override; void Scan(CatalogType type, const std::function &callback) override; void Scan(CatalogType type, VisibilityBound bound, const std::function &callback); void DropEntry(ClientContext &context, DropInfo &info) override; @@ -82,16 +82,13 @@ class DuckSchemaEntry : public SchemaCatalogEntry { void Verify(Catalog &catalog) override; - unique_ptr GetInfo() const override; - - optional_ptr GetParentSchema() const override { - return parent_schema; - } - //! Get the catalog set for the specified type CatalogSet &GetCatalogSet(CatalogType type); private: + //! Drop an entry and remove the foreign key references to it from its referenced tables + bool DropEntryInternal(CatalogTransaction transaction, CatalogEntry &entry, const Identifier &name, bool cascade, + bool allow_drop_internal); void OnDropEntry(CatalogTransaction transaction, CatalogEntry &entry); }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/catalog/catalog_entry/duck_table_entry.hpp b/src/duckdb/src/include/duckdb/catalog/catalog_entry/duck_table_entry.hpp index a4181124e..f6bae8aaa 100644 --- a/src/duckdb/src/include/duckdb/catalog/catalog_entry/duck_table_entry.hpp +++ b/src/duckdb/src/include/duckdb/catalog/catalog_entry/duck_table_entry.hpp @@ -52,6 +52,9 @@ class DuckTableEntry : public TableCatalogEntry { void CommitAlter(string &column_name, CommitDropState &drop_state); void CommitDrop(CommitDropState &drop_state); + //! Returns the backing index OIDs of UNIQUE constraints that are not in prev_table. + vector GetAddedUniqueIndexOids(const DuckTableEntry &prev_table) const; + TableFunction GetScanFunction(ClientContext &context, unique_ptr &bind_data) override; vector @@ -66,6 +69,9 @@ class DuckTableEntry : public TableCatalogEntry { bool IsDuckTable() const override { return true; } + optional_ptr TryGetDuckTableEntry() override { + return this; + } //! Returns the virtual columns for this table virtual_column_map_t GetVirtualColumns() const override; diff --git a/src/duckdb/src/include/duckdb/catalog/catalog_entry/macro_catalog_entry.hpp b/src/duckdb/src/include/duckdb/catalog/catalog_entry/macro_catalog_entry.hpp index 669fc499f..6e368156c 100644 --- a/src/duckdb/src/include/duckdb/catalog/catalog_entry/macro_catalog_entry.hpp +++ b/src/duckdb/src/include/duckdb/catalog/catalog_entry/macro_catalog_entry.hpp @@ -21,6 +21,8 @@ class MacroCatalogEntry : public FunctionEntry { public: MacroCatalogEntry(Catalog &catalog, SchemaCatalogEntry &schema, CreateMacroInfo &info); + static unique_ptr Create(Catalog &catalog, SchemaCatalogEntry &schema, CreateMacroInfo &info); + //! The macro function vector> macros; diff --git a/src/duckdb/src/include/duckdb/catalog/catalog_entry/schema_catalog_entry.hpp b/src/duckdb/src/include/duckdb/catalog/catalog_entry/schema_catalog_entry.hpp index 84c171df7..eeace24d5 100644 --- a/src/duckdb/src/include/duckdb/catalog/catalog_entry/schema_catalog_entry.hpp +++ b/src/duckdb/src/include/duckdb/catalog/catalog_entry/schema_catalog_entry.hpp @@ -45,26 +45,37 @@ class SchemaCatalogEntry : public InCatalogEntry { static constexpr const char *Name = "schema"; public: - SchemaCatalogEntry(Catalog &catalog, CreateSchemaInfo &info); + SchemaCatalogEntry(Catalog &catalog, CreateSchemaInfo &info, + optional_ptr parent_schema = nullptr); public: unique_ptr GetInfo() const override; //! The parent schema if this is a nested schema, or nullptr for a top-level schema virtual optional_ptr GetParentSchema() const { - return nullptr; + return parent_schema; } //! The full path of this schema (its parent chain outermost first, ending with this schema's own name) vector GetSchemaPath() const; + //! The schema path formatted as a SQL name, without the catalog. + DUCKDB_API string GetSchemaName() const; //! The fully qualified name of an entry in this schema: [catalog, schema path..., entry_name] QualifiedName GetQualifiedName(const Identifier &entry_name) const; //! Scan the specified catalog set, invoking the callback method for every entry virtual void Scan(ClientContext &context, CatalogType type, const std::function &callback) = 0; + //! Scan using an existing transaction. Override when scans can run under catalog locks. + DUCKDB_API virtual void Scan(CatalogTransaction transaction, CatalogType type, + const std::function &callback); //! Scan the specified catalog set, invoking the callback method for every committed entry virtual void Scan(CatalogType type, const std::function &callback) = 0; + //! Visit this schema and its descendants in depth-first, parent-before-child order. + DUCKDB_API void ScanSchemaTree(CatalogTransaction transaction, + const std::function &callback); + //! Visit the committed schema tree in the same order. + DUCKDB_API void ScanSchemaTree(const std::function &callback); string ToSQL() const override; @@ -82,15 +93,23 @@ class SchemaCatalogEntry : public InCatalogEntry { virtual optional_ptr CreateSequence(CatalogTransaction transaction, CreateSequenceInfo &info) = 0; //! Create a table function within the given schema virtual optional_ptr CreateTableFunction(CatalogTransaction transaction, - CreateTableFunctionInfo &info) = 0; + CreateTableFunctionInfo &info) { + throw NotImplementedException("Table functions are not supported in schema %s", name); + } //! Create a copy function within the given schema virtual optional_ptr CreateCopyFunction(CatalogTransaction transaction, - CreateCopyFunctionInfo &info) = 0; + CreateCopyFunctionInfo &info) { + throw NotImplementedException("Copy functions are not supported in schema %s", name); + } //! Create a pragma function within the given schema virtual optional_ptr CreatePragmaFunction(CatalogTransaction transaction, - CreatePragmaFunctionInfo &info) = 0; + CreatePragmaFunctionInfo &info) { + throw NotImplementedException("Pragma functions are not supported in schema %s", name); + } //! Create a collation within the given schema - virtual optional_ptr CreateCollation(CatalogTransaction transaction, CreateCollationInfo &info) = 0; + virtual optional_ptr CreateCollation(CatalogTransaction transaction, CreateCollationInfo &info) { + throw NotImplementedException("Collations are not supported in schema %s", name); + } //! Create a coordinate system within the given schema virtual optional_ptr CreateCoordinateSystem(CatalogTransaction transaction, CreateCoordinateSystemInfo &info) { @@ -118,5 +137,8 @@ class SchemaCatalogEntry : public InCatalogEntry { virtual void Alter(CatalogTransaction transaction, AlterInfo &info) = 0; CatalogTransaction GetCatalogTransaction(ClientContext &context); + +protected: + optional_ptr parent_schema; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/catalog/catalog_entry/sequence_catalog_entry.hpp b/src/duckdb/src/include/duckdb/catalog/catalog_entry/sequence_catalog_entry.hpp index cc7ef078c..b859af388 100644 --- a/src/duckdb/src/include/duckdb/catalog/catalog_entry/sequence_catalog_entry.hpp +++ b/src/duckdb/src/include/duckdb/catalog/catalog_entry/sequence_catalog_entry.hpp @@ -64,6 +64,8 @@ class SequenceCatalogEntry : public StandardEntry { int64_t NextValue(DuckTransaction &transaction); int64_t SetValue(DuckTransaction &transaction, int64_t value, bool is_called); void ReplayValue(uint64_t usage_count, int64_t counter, optional last_value); + //! Derive the last value from the counter - storage versions prior to v2.0.0 do not store it + void DeriveLastValue(); string ToSQL() const override; diff --git a/src/duckdb/src/include/duckdb/catalog/catalog_entry/table_catalog_entry.hpp b/src/duckdb/src/include/duckdb/catalog/catalog_entry/table_catalog_entry.hpp index 6c0177e67..53fa36533 100644 --- a/src/duckdb/src/include/duckdb/catalog/catalog_entry/table_catalog_entry.hpp +++ b/src/duckdb/src/include/duckdb/catalog/catalog_entry/table_catalog_entry.hpp @@ -27,6 +27,7 @@ namespace duckdb { class DataTable; +class DuckTableEntry; struct CreateTriggerInfo; struct RenameColumnInfo; @@ -109,9 +110,16 @@ class TableCatalogEntry : public StandardEntry { virtual TableFunction GetScanFunction(ClientContext &context, unique_ptr &bind_data, const EntryLookupInfo &lookup_info); + //! Whether this table should be bound like a DuckDB table. Entries that are not a DuckTableEntry can also + //! return true (e.g. to have DML bound with the DuckDB table layout), so use TryGetDuckTableEntry() instead of + //! casting. virtual bool IsDuckTable() const { return false; } + //! Returns this entry as a DuckTableEntry, or nullptr if it is not one + virtual optional_ptr TryGetDuckTableEntry() { + return nullptr; + } DUCKDB_API static string ColumnsToSQL(const ColumnList &columns, const vector> &constraints); diff --git a/src/duckdb/src/include/duckdb/catalog/catalog_entry/table_function_catalog_entry.hpp b/src/duckdb/src/include/duckdb/catalog/catalog_entry/table_function_catalog_entry.hpp index a2e275700..fad041ded 100644 --- a/src/duckdb/src/include/duckdb/catalog/catalog_entry/table_function_catalog_entry.hpp +++ b/src/duckdb/src/include/duckdb/catalog/catalog_entry/table_function_catalog_entry.hpp @@ -29,5 +29,9 @@ class TableFunctionCatalogEntry : public FunctionEntry { public: unique_ptr AlterEntry(CatalogTransaction transaction, AlterInfo &info) override; + +private: + friend class ExtensionLoader; + void FinalizeFunction(TableFunction &function) const; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/catalog/catalog_entry/view_catalog_entry.hpp b/src/duckdb/src/include/duckdb/catalog/catalog_entry/view_catalog_entry.hpp index 1d7dd8a86..8f80fea12 100644 --- a/src/duckdb/src/include/duckdb/catalog/catalog_entry/view_catalog_entry.hpp +++ b/src/duckdb/src/include/duckdb/catalog/catalog_entry/view_catalog_entry.hpp @@ -55,6 +55,8 @@ class ViewCatalogEntry : public StandardEntry { //! Update the view with a new set of types / names virtual void UpdateBinding(const vector &types, const vector &names); Value GetColumnComment(idx_t column_index); + //! Returns the bound name of a visible column + Identifier ResolveColumnName(const Identifier &column_name) const; public: unique_ptr GetInfo() const override; diff --git a/src/duckdb/src/include/duckdb/catalog/catalog_search_path.hpp b/src/duckdb/src/include/duckdb/catalog/catalog_search_path.hpp index 0005004f4..00923ae2f 100644 --- a/src/duckdb/src/include/duckdb/catalog/catalog_search_path.hpp +++ b/src/duckdb/src/include/duckdb/catalog/catalog_search_path.hpp @@ -80,6 +80,8 @@ class CatalogSearchPath { //! Returns nullopt if the catalog exists but has no default schema DUCKDB_API optional GetDefaultSchema(ClientContext &context, const Identifier &catalog) const; DUCKDB_API Identifier GetDefaultCatalog(const Identifier &schema) const; + //! Resolve a schema's catalog, falling back to the search-path default and then the default database. + DUCKDB_API Identifier ResolveCatalog(const Identifier &schema) const; DUCKDB_API vector GetSchemasForCatalog(const Identifier &catalog) const; DUCKDB_API vector GetCatalogsForSchema(const Identifier &schema) const; diff --git a/src/duckdb/src/include/duckdb/catalog/default/default_functions.hpp b/src/duckdb/src/include/duckdb/catalog/default/default_functions.hpp index cf73398a1..6aa04a380 100644 --- a/src/duckdb/src/include/duckdb/catalog/default/default_functions.hpp +++ b/src/duckdb/src/include/duckdb/catalog/default/default_functions.hpp @@ -10,7 +10,6 @@ #include "duckdb/catalog/default/default_generator.hpp" #include "duckdb/parser/parsed_data/create_macro_info.hpp" -#include "duckdb/parser/parser_options.hpp" namespace duckdb { class SchemaCatalogEntry; @@ -28,9 +27,6 @@ class DefaultFunctionGenerator : public DefaultGenerator { SchemaCatalogEntry &schema; DUCKDB_API static unique_ptr CreateInternalMacroInfo(const DefaultMacro &default_macro); - //! Overload taking ParserOptions, so the caller's compiled grammar is reused instead of rebuilt per macro. - DUCKDB_API static unique_ptr CreateInternalMacroInfo(const DefaultMacro &default_macro, - const ParserOptions &options); public: unique_ptr CreateDefaultEntry(ClientContext &context, const Identifier &entry_name) override; diff --git a/src/duckdb/src/include/duckdb/catalog/default/default_table_functions.hpp b/src/duckdb/src/include/duckdb/catalog/default/default_table_functions.hpp index fbdac333e..995b1eedd 100644 --- a/src/duckdb/src/include/duckdb/catalog/default/default_table_functions.hpp +++ b/src/duckdb/src/include/duckdb/catalog/default/default_table_functions.hpp @@ -10,7 +10,6 @@ #include "duckdb/catalog/default/default_generator.hpp" #include "duckdb/parser/parsed_data/create_macro_info.hpp" -#include "duckdb/parser/parser_options.hpp" namespace duckdb { class SchemaCatalogEntry; @@ -39,9 +38,12 @@ class DefaultTableFunctionGenerator : public DefaultGenerator { vector GetDefaultEntries() override; static unique_ptr CreateTableMacroInfo(const DefaultTableMacro &default_macro); - //! Overload taking ParserOptions, so the caller's compiled grammar is reused instead of rebuilt per macro. - static unique_ptr CreateTableMacroInfo(const DefaultTableMacro &default_macro, - const ParserOptions &options); + //! Finds a macro in a nullptr-terminated list + static optional_ptr FindTableMacro(const DefaultTableMacro macros[], + const Identifier &name, + optional_ptr schema_name = nullptr); + static unique_ptr CreateTableMacroEntry(Catalog &catalog, SchemaCatalogEntry &schema, + const DefaultTableMacro &default_macro); private: static unique_ptr CreateInternalTableMacroInfo(const DefaultTableMacro &default_macro, diff --git a/src/duckdb/src/include/duckdb/catalog/dependency.hpp b/src/duckdb/src/include/duckdb/catalog/dependency.hpp index 1d35dd681..0f85c5165 100644 --- a/src/duckdb/src/include/duckdb/catalog/dependency.hpp +++ b/src/duckdb/src/include/duckdb/catalog/dependency.hpp @@ -65,7 +65,7 @@ struct DependencySubjectFlags : public DependencyFlags { static constexpr uint8_t OWNERSHIP = 0; public: - DependencySubjectFlags &Apply(DependencySubjectFlags other) { + DependencySubjectFlags &Apply(const DependencySubjectFlags &other) { Merge(other.Value()); return *this; } @@ -104,7 +104,7 @@ struct DependencyDependentFlags : public DependencyFlags { } public: - DependencyDependentFlags &Apply(DependencyDependentFlags other) { + DependencyDependentFlags &Apply(const DependencyDependentFlags &other) { Merge(other.Value()); return *this; } diff --git a/src/duckdb/src/include/duckdb/catalog/dependency_manager.hpp b/src/duckdb/src/include/duckdb/catalog/dependency_manager.hpp index e6b35ae32..536709abf 100644 --- a/src/duckdb/src/include/duckdb/catalog/dependency_manager.hpp +++ b/src/duckdb/src/include/duckdb/catalog/dependency_manager.hpp @@ -119,6 +119,9 @@ class DependencyManager { static MangledEntryName MangleName(const CatalogEntryInfo &info); static MangledEntryName MangleName(const CatalogEntry &entry); static CatalogEntryInfo GetLookupProperties(const CatalogEntry &entry); + //! The drop error for an entry with dependents, for catalogs that track their own dependents. + DUCKDB_API static string FormatDropError(const CatalogEntry &object, + const vector> &dependents); //! Navigate the given schema path (outermost first) and return the deepest schema in it. Returns nullptr for an //! empty path (the entry lives in the catalog root) or if a schema along the path does not exist. optional_ptr NavigateSchemaPath(CatalogTransaction transaction, diff --git a/src/duckdb/src/include/duckdb/catalog/duck_catalog.hpp b/src/duckdb/src/include/duckdb/catalog/duck_catalog.hpp index 461244a4e..f02fee1c0 100644 --- a/src/duckdb/src/include/duckdb/catalog/duck_catalog.hpp +++ b/src/duckdb/src/include/duckdb/catalog/duck_catalog.hpp @@ -21,6 +21,9 @@ class DuckCatalog : public Catalog { public: bool IsDuckCatalog() override; + bool SupportsNestedSchemas() const override { + return true; + } void Initialize(bool load_builtin) override; string GetCatalogType() override { @@ -39,6 +42,8 @@ class DuckCatalog : public Catalog { public: DUCKDB_API optional_ptr CreateSchema(CatalogTransaction transaction, CreateSchemaInfo &info) override; + DUCKDB_API void AlterSchema(CatalogTransaction transaction, SchemaCatalogEntry &schema, + AlterSchemaInfo &info) override; DUCKDB_API void ScanSchemas(ClientContext &context, std::function callback) override; DUCKDB_API void ScanSchemas(VisibilityBound bound, std::function callback); diff --git a/src/duckdb/src/include/duckdb/common/adbc/wrappers.hpp b/src/duckdb/src/include/duckdb/common/adbc/wrappers.hpp index 20f6bbb11..b22fda3ae 100644 --- a/src/duckdb/src/include/duckdb/common/adbc/wrappers.hpp +++ b/src/duckdb/src/include/duckdb/common/adbc/wrappers.hpp @@ -9,6 +9,7 @@ #pragma once #include "duckdb.h" +#include "duckdb/common/atomic.hpp" #include "duckdb/common/mutex.hpp" #include "duckdb/common/string.hpp" #include "duckdb/common/unordered_map.hpp" @@ -23,6 +24,9 @@ namespace duckdb { struct DuckDBAdbcConnectionWrapper { duckdb_connection connection; unordered_map options; + //! A cancel and a max_execution_time timeout raise the same exception type, and errors_as_json rewrites the + //! message that would tell them apart. Cleared when a statement starts executing + atomic cancel_requested {false}; //! Register a stream wrapper so it can be materialized if another query runs on this connection. void RegisterStream(duckdb_adbc::DuckDBAdbcStreamWrapper *stream); @@ -30,6 +34,8 @@ struct DuckDBAdbcConnectionWrapper { void UnregisterStream(duckdb_adbc::DuckDBAdbcStreamWrapper *stream); //! Materialize all active streams, fetching remaining data into memory. void MaterializeStreams(); + //! Ends every open stream on this connection, which then reports `reason` once its materialized arrays are read + void CloseStreams(const char *reason); //! Detach all streams from this connection and clear the list (called on connection release). void DetachAndClearStreams(); diff --git a/src/duckdb/src/include/duckdb/common/allocator.hpp b/src/duckdb/src/include/duckdb/common/allocator.hpp index 9f0a72c11..c69fbe2cf 100644 --- a/src/duckdb/src/include/duckdb/common/allocator.hpp +++ b/src/duckdb/src/include/duckdb/common/allocator.hpp @@ -107,6 +107,9 @@ class Allocator { AllocatedData Allocate(idx_t size) { return AllocatedData(*this, AllocateData(size), size); } + //! Allocate normally, requesting huge-page backing where supported. Advice failure is ignored. + DUCKDB_API AllocatedData TryAllocateHuge(idx_t size); + static data_ptr_t DefaultAllocate(PrivateAllocatorData *private_data, idx_t size); static void DefaultFree(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t size); static data_ptr_t DefaultReallocate(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t old_size, @@ -122,6 +125,8 @@ class Allocator { DUCKDB_API static Allocator &DefaultAllocator(); DUCKDB_API static shared_ptr &DefaultAllocatorReference(); + //! Total mapped allocator memory, if supported by this allocator. + optional_idx GetMappedMemory() const; static bool SupportsFlush(); static optional_idx DecayDelay(); static void ThreadFlush(bool allocator_background_threads, idx_t threshold, idx_t thread_count); diff --git a/src/duckdb/src/include/duckdb/common/arrow/appender/append_data.hpp b/src/duckdb/src/include/duckdb/common/arrow/appender/append_data.hpp index 0ce7493e0..9b88a98ff 100644 --- a/src/duckdb/src/include/duckdb/common/arrow/appender/append_data.hpp +++ b/src/duckdb/src/include/duckdb/common/arrow/appender/append_data.hpp @@ -101,6 +101,18 @@ struct ArrowAppendData { //! the schema declared by SetArrowFormat. void AppendChild(const Vector &input, idx_t from, idx_t to, idx_t input_size); + //! The bytes appended so far: this column's buffers plus those of its children + idx_t ByteSize() const { + idx_t bytes = 0; + for (auto &buffer : arrow_buffers) { + bytes += buffer.size(); + } + for (auto &child : child_data) { + bytes += child->ByteSize(); + } + return bytes; + } + public: idx_t row_count = 0; idx_t null_count = 0; diff --git a/src/duckdb/src/include/duckdb/common/arrow/arrow_appender.hpp b/src/duckdb/src/include/duckdb/common/arrow/arrow_appender.hpp index 8c7579706..2e3b16661 100644 --- a/src/duckdb/src/include/duckdb/common/arrow/arrow_appender.hpp +++ b/src/duckdb/src/include/duckdb/common/arrow/arrow_appender.hpp @@ -30,6 +30,8 @@ class ArrowAppender { //! Returns the underlying arrow array DUCKDB_API ArrowArray Finalize(); idx_t RowCount() const; + //! The bytes the appended rows occupy. Valid until Finalize hands the buffers to the array + DUCKDB_API idx_t ByteSize() const; static void ReleaseArray(ArrowArray *array); static ArrowArray *FinalizeChild(const LogicalType &type, unique_ptr append_data_p); static unique_ptr diff --git a/src/duckdb/src/include/duckdb/common/arrow/arrow_buffer.hpp b/src/duckdb/src/include/duckdb/common/arrow/arrow_buffer.hpp index 167930d88..03e3eec12 100644 --- a/src/duckdb/src/include/duckdb/common/arrow/arrow_buffer.hpp +++ b/src/duckdb/src/include/duckdb/common/arrow/arrow_buffer.hpp @@ -72,7 +72,7 @@ struct ArrowBuffer { count++; } - idx_t size() { // NOLINT + idx_t size() const { // NOLINT return count; } diff --git a/src/duckdb/src/include/duckdb/common/arrow/arrow_format.hpp b/src/duckdb/src/include/duckdb/common/arrow/arrow_format.hpp new file mode 100644 index 000000000..1f5e2b097 --- /dev/null +++ b/src/duckdb/src/include/duckdb/common/arrow/arrow_format.hpp @@ -0,0 +1,112 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/common/arrow/arrow_format.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/arrow/arrow.hpp" +#include "duckdb/common/arrow/arrow_wrapper.hpp" +#include "duckdb/common/shared_ptr.hpp" +#include "duckdb/common/types.hpp" +#include "duckdb/common/unique_ptr.hpp" +#include "duckdb/common/unordered_map.hpp" +#include "duckdb/common/vector.hpp" +#include "duckdb/common/winapi.hpp" +#include "duckdb/main/client_properties.hpp" +#include "duckdb/main/result_format.hpp" +#include "duckdb/main/retained_result_collection.hpp" + +namespace duckdb { + +class ArrowRetainedCollection; +class ArrowTypeExtensionData; + +//! An appender's finalized array, never mutated once shared because every export borrows its buffers +using ArrowArrayOwner = shared_ptr; +using ArrowArrayCollection = vector; + +//! Per-query Arrow state, built at submission. The schema and the extension type map are resolved once +//! and shared by every array. Resolving them needs the client context the properties carry +class ArrowFormatGlobalState : public ResultFormatGlobalState { +public: + DUCKDB_API explicit ArrowFormatGlobalState(const ResultFormatContext &context); + DUCKDB_API ~ArrowFormatGlobalState() override; + +public: + //! Owned by this state and released with it. Hand consumers a copy + const ArrowSchema &Schema() const { + return schema.arrow_schema; + } + const vector &Types() const { + return types; + } + const ClientProperties &Properties() const { + return properties; + } + const unordered_map> &ExtensionTypes() const { + return extension_types; + } + +private: + vector types; + ClientProperties properties; + unordered_map> extension_types; + ArrowSchemaWrapper schema; +}; + +//! Turns chunks into Arrow arrays of at most batch_size rows, one appender per producer +class ArrowFormat : public ResultFormat { +public: + using T = ArrowArrayWrapper; + using C = ArrowArrayCollection; + using Collection = ArrowRetainedCollection; + using GlobalState = ArrowFormatGlobalState; + static constexpr const char *NAME = "arrow"; + +public: + DUCKDB_API explicit ArrowFormat(idx_t batch_size); + +public: + DUCKDB_API const char *Name() const override; + DUCKDB_API unique_ptr InitGlobal(const ResultFormatContext &context) override; + DUCKDB_API unique_ptr InitLocal(ResultFormatGlobalState &gstate) override; + DUCKDB_API void AppendToUnit(ResultFormatGlobalState &gstate, ResultFormatLocalState &lstate, + DataChunk &chunk) override; + DUCKDB_API bool IsUnitFinished(ResultFormatLocalState &lstate) override; + DUCKDB_API unique_ptr FinishUnit(ResultFormatGlobalState &gstate, + ResultFormatLocalState &lstate) override; + DUCKDB_API unique_ptr + CreateCollection(ClientContext &context, ResultFormatGlobalState &gstate, + const ResultFormatContext &format_context) override; + +public: + //! The array the appender finalized, owned by the caller from here on + DUCKDB_API static unique_ptr UnpackUnit(unique_ptr unit); + //! An export over the owner's buffers whose every node holds the owner, so a moved-out child outlives the rest + DUCKDB_API static unique_ptr ShareArray(const ArrowArrayOwner &owner); + //! The count in a CHANGED_ROWS result's array, a single BIGINT row; 0 for an empty array + DUCKDB_API static idx_t ChangedRows(const ArrowArray &array); + +private: + //! The rows an array holds, except at a batch boundary and at a producer's end of input + idx_t batch_size; +}; + +//! Fetch exports with ShareArray, so the collection stays whole +class ArrowRetainedCollection : public DefaultRetainedCollection { +public: + using DefaultRetainedCollection::DefaultRetainedCollection; + +public: + //! An export of the next array; null at the end and forever after + DUCKDB_API unique_ptr Fetch(); + +private: + idx_t fetch_index = 0; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/arrow/arrow_merge_event.hpp b/src/duckdb/src/include/duckdb/common/arrow/arrow_merge_event.hpp deleted file mode 100644 index f8dd0918b..000000000 --- a/src/duckdb/src/include/duckdb/common/arrow/arrow_merge_event.hpp +++ /dev/null @@ -1,61 +0,0 @@ -//===----------------------------------------------------------------------===// -// DuckDB -// -// duckdb/common/arrow/arrow_merge_event.hpp -// -// -//===----------------------------------------------------------------------===// - -#pragma once - -#include "duckdb/parallel/base_pipeline_event.hpp" -#include "duckdb/parallel/task.hpp" -#include "duckdb/common/types/batched_data_collection.hpp" -#include "duckdb/parallel/pipeline.hpp" -#include "duckdb/common/vector.hpp" -#include "duckdb/common/shared_ptr.hpp" -#include "duckdb/common/arrow/arrow_converter.hpp" -#include "duckdb/main/chunk_scan_state/batched_data_collection.hpp" -#include "duckdb/execution/executor.hpp" -#include "duckdb/common/unique_ptr.hpp" -#include "duckdb/common/helper.hpp" -#include "duckdb/common/arrow/arrow_query_result.hpp" - -namespace duckdb { - -// Task to create one RecordBatch by (partially) scanning a BatchedDataCollection -class ArrowBatchTask : public ExecutorTask { -public: - ArrowBatchTask(ArrowQueryResult &result, vector record_batch_indices, Executor &executor, - shared_ptr event_p, BatchCollectionChunkScanState scan_state, idx_t batch_size); - void ProduceRecordBatches(); - TaskExecutionResult ExecuteTask(TaskExecutionMode mode) override; - - string TaskType() const override { - return "ArrowBatchTask"; - } - -private: - ArrowQueryResult &result; - vector record_batch_indices; - idx_t batch_size; - BatchCollectionChunkScanState scan_state; -}; - -class ArrowMergeEvent : public BasePipelineEvent { -public: - ArrowMergeEvent(ArrowQueryResult &result, BatchedDataCollection &batches, Pipeline &pipeline_p); - -public: - void Schedule() override; - -public: - ArrowQueryResult &result; - BatchedDataCollection &batches; - -private: - //! The max size of a record batch to output - idx_t record_batch_size; -}; - -} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/arrow/arrow_query_result.hpp b/src/duckdb/src/include/duckdb/common/arrow/arrow_query_result.hpp deleted file mode 100644 index d7232fd31..000000000 --- a/src/duckdb/src/include/duckdb/common/arrow/arrow_query_result.hpp +++ /dev/null @@ -1,52 +0,0 @@ -//===----------------------------------------------------------------------===// -// DuckDB -// -// duckdb_python/arrow/arrow_query_result.hpp -// -// -//===----------------------------------------------------------------------===// - -#pragma once - -#include "duckdb/common/winapi.hpp" -#include "duckdb/common/arrow/arrow_wrapper.hpp" -#include "duckdb/main/query_result.hpp" -#include "duckdb/common/error_data.hpp" -#include "duckdb/common/unique_ptr.hpp" - -namespace duckdb { - -class ClientContext; - -class ArrowQueryResult : public QueryResult { -public: - static constexpr QueryResultType TYPE = QueryResultType::ARROW_RESULT; - -public: - friend class ClientContext; - //! Creates a successful query result with the specified names and types - DUCKDB_API ArrowQueryResult(StatementType statement_type, StatementProperties properties, - vector names_p, vector types_p, - ClientProperties client_properties, idx_t batch_size); - //! Creates an unsuccessful query result with error condition - DUCKDB_API explicit ArrowQueryResult(ErrorData error); - -public: - //! Converts the QueryResult to a string - DUCKDB_API string ToString() override; - -public: - vector> ConsumeArrays(); - vector> &Arrays(); - void SetArrowData(vector> arrays); - idx_t BatchSize() const; - -protected: - DUCKDB_API unique_ptr FetchInternal() override; - -private: - vector> arrays; - idx_t batch_size; -}; - -} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/arrow/arrow_util.hpp b/src/duckdb/src/include/duckdb/common/arrow/arrow_util.hpp deleted file mode 100644 index eb9e45e71..000000000 --- a/src/duckdb/src/include/duckdb/common/arrow/arrow_util.hpp +++ /dev/null @@ -1,34 +0,0 @@ -//===----------------------------------------------------------------------===// -// DuckDB -// -// duckdb/common/arrow/arrow_util.hpp -// -// -//===----------------------------------------------------------------------===// - -#pragma once -#include "duckdb/common/arrow/arrow.hpp" -#include "duckdb/main/chunk_scan_state.hpp" -#include "duckdb/main/client_properties.hpp" -#include "duckdb/common/helper.hpp" -#include "duckdb/common/error_data.hpp" - -namespace duckdb { - -class QueryResult; -class DataChunk; -class ArrowTypeExtensionData; - -class ArrowUtil { -public: - static bool TryFetchChunk(ChunkScanState &scan_state, ClientProperties options, idx_t chunk_size, ArrowArray *out, - idx_t &result_count, ErrorData &error, - unordered_map> extension_type_cast); - static idx_t FetchChunk(ChunkScanState &scan_state, ClientProperties options, idx_t chunk_size, ArrowArray *out, - const unordered_map> &extension_type_cast); - -private: - static bool TryFetchNext(QueryResult &result, unique_ptr &out, ErrorData &error); -}; - -} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/arrow/arrow_wrapper.hpp b/src/duckdb/src/include/duckdb/common/arrow/arrow_wrapper.hpp index 47d87dd61..18b3e683a 100644 --- a/src/duckdb/src/include/duckdb/common/arrow/arrow_wrapper.hpp +++ b/src/duckdb/src/include/duckdb/common/arrow/arrow_wrapper.hpp @@ -33,6 +33,11 @@ class ArrowArrayWrapper { ArrowArrayWrapper(ArrowArrayWrapper &&other) noexcept : arrow_array(other.arrow_array) { other.arrow_array.release = nullptr; } + //! Hands the array to the caller, who releases it from then on + void MoveTo(ArrowArray &out) { + out = arrow_array; + arrow_array.release = nullptr; + } ArrowArrayWrapper &operator=(ArrowArrayWrapper &&other) noexcept { if (this != &other) { if (arrow_array.release) { diff --git a/src/duckdb/src/include/duckdb/common/arrow/nanoarrow/nanoarrow.h b/src/duckdb/src/include/duckdb/common/arrow/nanoarrow/nanoarrow.h index 722d66c57..1cea5203f 100644 --- a/src/duckdb/src/include/duckdb/common/arrow/nanoarrow/nanoarrow.h +++ b/src/duckdb/src/include/duckdb/common/arrow/nanoarrow/nanoarrow.h @@ -218,7 +218,7 @@ ArrowErrorCode ArrowSchemaInitDateTime(struct ArrowSchema *schema, enum ArrowTyp /// \brief Make a (recursive) copy of a schema /// /// Allocates and copies fields of schema into schema_out. -ArrowErrorCode ArrowSchemaDeepCopy(struct ArrowSchema *schema, struct ArrowSchema *schema_out); +ArrowErrorCode ArrowSchemaDeepCopy(const struct ArrowSchema *schema, struct ArrowSchema *schema_out); /// \brief Copy format into schema->format /// diff --git a/src/duckdb/src/include/duckdb/common/arrow/physical_arrow_batch_collector.hpp b/src/duckdb/src/include/duckdb/common/arrow/physical_arrow_batch_collector.hpp deleted file mode 100644 index 71d0806d4..000000000 --- a/src/duckdb/src/include/duckdb/common/arrow/physical_arrow_batch_collector.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "duckdb/execution/operator/helper/physical_batch_collector.hpp" - -namespace duckdb { - -class ArrowBatchGlobalState : public BatchCollectorGlobalState { -public: - ArrowBatchGlobalState(ClientContext &context, const PhysicalBatchCollector &op) - : BatchCollectorGlobalState(context, op) { - } -}; - -class PhysicalArrowBatchCollector : public PhysicalBatchCollector { -public: - PhysicalArrowBatchCollector(PhysicalPlan &physical_plan, PreparedStatementData &data, idx_t batch_size) - : PhysicalBatchCollector(physical_plan, data), record_batch_size(batch_size) { - } - -public: - SinkFinalizeType Finalize(Pipeline &pipeline, Event &event, ClientContext &context, - OperatorSinkFinalizeInput &input) const override; - unique_ptr GetGlobalSinkState(ClientContext &context) const override; - -public: - //! User provided batch size - idx_t record_batch_size; -}; - -} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/arrow/physical_arrow_collector.hpp b/src/duckdb/src/include/duckdb/common/arrow/physical_arrow_collector.hpp deleted file mode 100644 index 3b1372f4b..000000000 --- a/src/duckdb/src/include/duckdb/common/arrow/physical_arrow_collector.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include "duckdb/execution/operator/helper/physical_result_collector.hpp" -#include "duckdb/execution/physical_operator_states.hpp" -#include "duckdb/common/arrow/arrow.hpp" -#include "duckdb/common/types/batched_data_collection.hpp" -#include "duckdb/common/map.hpp" -#include "duckdb/common/arrow/arrow_appender.hpp" -#include "duckdb/common/arrow/arrow_wrapper.hpp" - -namespace duckdb { - -struct ArrowCollectorGlobalState : public GlobalSinkState { -public: - //! The result returned by GetResult - unique_ptr result; - vector> chunks; - mutex glock; - shared_ptr context; - idx_t tuple_count = 0; -}; - -struct ArrowCollectorLocalState : public LocalSinkState { -public: - // The appender for the current chunk we're creating - unique_ptr appender; - vector> finished_arrays; - idx_t tuple_count = 0; - -public: - void FinishArray() { - auto finished_array = make_uniq(); - auto row_count = appender->RowCount(); - finished_array->arrow_array = appender->Finalize(); - appender.reset(); - finished_arrays.push_back(std::move(finished_array)); - tuple_count += row_count; - } -}; - -class PhysicalArrowCollector : public PhysicalResultCollector { -public: - PhysicalArrowCollector(PhysicalPlan &physical_plan, PreparedStatementData &data, bool parallel, idx_t batch_size) - : PhysicalResultCollector(physical_plan, data), record_batch_size(batch_size), parallel(parallel) { - } - -public: - static unique_ptr Create(ClientContext &context, PreparedStatementData &data, idx_t batch_size); - SinkResultType Sink(ExecutionContext &context, DataChunk &chunk, OperatorSinkInput &input) const override; - SinkCombineResultType Combine(ExecutionContext &context, OperatorSinkCombineInput &input) const override; - unique_ptr GetResult(GlobalSinkState &state) const override; - unique_ptr GetGlobalSinkState(ClientContext &context) const override; - unique_ptr GetLocalSinkState(ExecutionContext &context) const override; - SinkFinalizeType Finalize(Pipeline &pipeline, Event &event, ClientContext &context, - OperatorSinkFinalizeInput &input) const override; - bool ParallelSink() const override; - bool SinkOrderDependent() const override; - -public: - //! User provided batch size - idx_t record_batch_size; - bool parallel; -}; - -} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/arrow/result_arrow_wrapper.hpp b/src/duckdb/src/include/duckdb/common/arrow/result_arrow_wrapper.hpp deleted file mode 100644 index 67e8cf12d..000000000 --- a/src/duckdb/src/include/duckdb/common/arrow/result_arrow_wrapper.hpp +++ /dev/null @@ -1,37 +0,0 @@ -//===----------------------------------------------------------------------===// -// DuckDB -// -// duckdb/common/arrow/result_arrow_wrapper.hpp -// -// -//===----------------------------------------------------------------------===// - -#pragma once - -#include "duckdb/main/query_result.hpp" -#include "duckdb/common/arrow/arrow_wrapper.hpp" -#include "duckdb/main/chunk_scan_state.hpp" -#include "duckdb/function/table/arrow/arrow_duck_schema.hpp" - -namespace duckdb { -class ResultArrowArrayStreamWrapper { -public: - explicit ResultArrowArrayStreamWrapper(unique_ptr result, idx_t batch_size); - -public: - ArrowArrayStream stream; - unique_ptr result; - ErrorData last_error; - idx_t batch_size; - vector column_types; - vector column_names; - unique_ptr scan_state; - unordered_map> extension_types; - -private: - static int MyStreamGetSchema(struct ArrowArrayStream *stream, struct ArrowSchema *out); - static int MyStreamGetNext(struct ArrowArrayStream *stream, struct ArrowArray *out); - static void MyStreamRelease(struct ArrowArrayStream *stream); - static const char *MyStreamGetLastError(struct ArrowArrayStream *stream); -}; -} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/bind_helpers.hpp b/src/duckdb/src/include/duckdb/common/bind_helpers.hpp index 99c8c9861..b0880a79b 100644 --- a/src/duckdb/src/include/duckdb/common/bind_helpers.hpp +++ b/src/duckdb/src/include/duckdb/common/bind_helpers.hpp @@ -16,6 +16,9 @@ namespace duckdb { class Value; struct BoundOrderByNode; struct BoundStatement; +struct LogicalType; +struct OrderByNode; +struct TableIndex; class Binder; Value ConvertVectorToValue(vector set); @@ -26,5 +29,8 @@ vector ParseColumnsOrdered(const vector &set, const vector ParseColumnsOrdered(const Value &value, const vector &names, const Identifier &option_name); vector ParseOrderByColumns(Binder &binder, const vector &set, const BoundStatement &bound_statement, const Identifier &option_name); +DUCKDB_API vector BindOrderByNodes(Binder &binder, TableIndex table_index, const Identifier &alias, + const vector &names, const vector &types, + vector &orders); } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/clustered_aggregate.hpp b/src/duckdb/src/include/duckdb/common/clustered_aggregate.hpp index bf0d9d206..5998c383c 100644 --- a/src/duckdb/src/include/duckdb/common/clustered_aggregate.hpp +++ b/src/duckdb/src/include/duckdb/common/clustered_aggregate.hpp @@ -21,7 +21,24 @@ struct ClusteredAggrState; using DictProps = unsafe_unique_array; -static constexpr uint64_t SUM_OVERFLOW_MASK = ~((uint64_t(1) << 53) - 1); +// log2 is constexpr only from C++26. +// Only powers of two are supported +static_assert((STANDARD_VECTOR_SIZE & (STANDARD_VECTOR_SIZE - 1)) == 0); +template +constexpr uint64_t c_log2() { + if constexpr (Arg < 2) { + return 0; + } else { + return 1 + c_log2(); + } +} + +// Users of I64VectorSumSafe accumulate sum into int64_t. One bit is for sign, +// and there are log2(vector size) bits occupied by Vector values, so we can +// guarantee no overflow if at most 64 - 1 - log2(size) bits are set. +static constexpr uint64_t VECTOR_SIZE_LOG2 = c_log2(); +static constexpr uint64_t SUM_OVERFLOW_MAX_BITS = 64 - 1 - VECTOR_SIZE_LOG2; +static constexpr uint64_t SUM_OVERFLOW_MASK = ~((uint64_t(1) << SUM_OVERFLOW_MAX_BITS) - 1); static inline bool I64VectorSumSafe(int64_t v) { return ((static_cast(v) ^ static_cast(v >> 63)) & SUM_OVERFLOW_MASK) == 0; } @@ -45,22 +62,38 @@ struct ClusteredAggr { struct GroupRun { data_ptr_t state; //! caller fills this after TryClustered; advanced between aggregates - const sel_t *sel; //! points to the tuple positions for this run + const sel_t *sel; //! strictly increasing input row positions, or nullptr for 0..count-1 uint64_t gid; //! raw group id for this run idx_t count; //! number of tuples in this group }; - idx_t n_group_runs = 0; - GroupRun group_runs[MAX_RUNS]; + ClusteredAggr() : group_runs(&single_run) { + } - const ClusteredAggrState *state = nullptr; + struct RunRange { + const GroupRun *begin_ptr; + const GroupRun *end_ptr; + const GroupRun *begin() const { + return begin_ptr; + } + const GroupRun *end() const { + return end_ptr; + } + idx_t size() const { + return static_cast(end_ptr - begin_ptr); + } + const GroupRun &operator[](idx_t i) const { + return begin_ptr[i]; + } + }; + RunRange runs() const { + return RunRange {group_runs, group_runs + n_group_runs}; + } - //! Build a clustered permutation of 0..count-1 from raw integer group ids. - //! On success fills group_runs[].sel/gid/count. - bool TryClustered(const uint64_t *group_ids, sel_t count, sel_t *arena, uint64_t *slots); + const ClusteredAggrState *state = nullptr; //! Initialize a single run covering 0..count-1 for one aggregate state. - void SetSingleRun(data_ptr_t state, idx_t count); + void SetSingleRun(data_ptr_t state_ptr, idx_t count); //! Advance all run state pointers by payload_size. void AdvanceStates(idx_t payload_size); @@ -73,10 +106,19 @@ struct ClusteredAggr { } //! Returns a composed dict sel for simple dictionary input, or nullptr. - const sel_t *ClusterIter(const Vector &input, idx_t count) const; + const sel_t *ClusterIter(const Vector &input) const; private: - mutable sel_t composed_sel_data[STANDARD_VECTOR_SIZE]; + friend struct ClusteredAggrState; + + //! Build a clustered permutation of 0..count-1 from group ids into runs. + //! On success fills runs[].sel/gid/count. + bool TryClustered(const uint64_t *group_ids, sel_t count, sel_t *arena, uint64_t *slots, GroupRun *runs); + + idx_t n_group_runs = 0; + GroupRun single_run; + GroupRun *group_runs; + mutable const sel_t *cached_dict_sel = nullptr; }; @@ -84,6 +126,8 @@ struct ClusteredAggr { struct ClusteredAggrState { unsafe_unique_array arena; unsafe_unique_array slots; + unsafe_unique_array group_runs; + mutable unsafe_unique_array composed_sel_data; bool all_clustered = false; idx_t n_clustered = 0; idx_t skipped_opportunities = 0; diff --git a/src/duckdb/src/include/duckdb/common/compressed_file_system.hpp b/src/duckdb/src/include/duckdb/common/compressed_file_system.hpp index 786d4cba4..515c2fb43 100644 --- a/src/duckdb/src/include/duckdb/common/compressed_file_system.hpp +++ b/src/duckdb/src/include/duckdb/common/compressed_file_system.hpp @@ -8,6 +8,8 @@ #pragma once +#include "duckdb/common/allocator.hpp" +#include "duckdb/common/atomic.hpp" #include "duckdb/common/common.hpp" #include "duckdb/common/file_system.hpp" #include "duckdb/common/query_context.hpp" @@ -19,8 +21,8 @@ struct StreamData { // various buffers & pointers bool write = false; bool refresh = false; - unsafe_unique_array in_buff; - unsafe_unique_array out_buff; + AllocatedData in_buff; + AllocatedData out_buff; data_ptr_t out_buff_start = nullptr; data_ptr_t out_buff_end = nullptr; data_ptr_t in_buff_start = nullptr; @@ -35,6 +37,7 @@ struct StreamWrapper { DUCKDB_API virtual void Initialize(QueryContext context, CompressedFile &file, bool write) = 0; DUCKDB_API virtual bool Read(StreamData &stream_data) = 0; + DUCKDB_API virtual void FinalizeRead(StreamData &stream_data); DUCKDB_API virtual void Write(CompressedFile &file, StreamData &stream_data, data_ptr_t buffer, int64_t nr_bytes) = 0; DUCKDB_API virtual void Close() = 0; @@ -91,7 +94,10 @@ class CompressedFile : public FileHandle { void Clear(); // for Initialize re-use to support FS.Reset() void ResetStreamData(); - idx_t current_position = 0; + //! The number of compressed bytes read from the child handle + idx_t compressed_bytes_read = 0; + //! The number of compressed bytes consumed by the decompressor (for progress) + atomic compressed_bytes_consumed {0}; bool initialized = false; unique_ptr stream_wrapper; }; diff --git a/src/duckdb/src/include/duckdb/common/enable_shared_from_this_ipp.hpp b/src/duckdb/src/include/duckdb/common/enable_shared_from_this_ipp.hpp index 5c707082e..855bed430 100644 --- a/src/duckdb/src/include/duckdb/common/enable_shared_from_this_ipp.hpp +++ b/src/duckdb/src/include/duckdb/common/enable_shared_from_this_ipp.hpp @@ -15,7 +15,7 @@ class enable_shared_from_this { // NOLINT: invalid case style mutable weak_ptr __weak_this_; // NOLINT: __weak_this_ is reserved protected: - constexpr enable_shared_from_this() noexcept { + constexpr enable_shared_from_this() noexcept { // NOLINT(bugprone-crtp-constructor-accessibility) } enable_shared_from_this(enable_shared_from_this const &) noexcept { // NOLINT: not marked as explicit } diff --git a/src/duckdb/src/include/duckdb/common/encryption_functions.hpp b/src/duckdb/src/include/duckdb/common/encryption_functions.hpp index 3dba1e0bc..ea3722af0 100644 --- a/src/duckdb/src/include/duckdb/common/encryption_functions.hpp +++ b/src/duckdb/src/include/duckdb/common/encryption_functions.hpp @@ -55,6 +55,8 @@ class EncryptionEngine { static void AddKeyToCache(DatabaseInstance &db, data_ptr_t key, const string &key_name, bool wipe = true); static string AddKeyToCache(DatabaseInstance &db, data_ptr_t key); static void AddTempKeyToCache(DatabaseInstance &db); + //! Fill key with cryptographically random bytes + DUCKDB_API static void GenerateRandomKey(DatabaseInstance &db, data_ptr_t key, idx_t key_length); //! Encryption Functions static void EncryptBlock(AttachedDatabase &attached_db, const string &key_id, FileBuffer &block, diff --git a/src/duckdb/src/include/duckdb/common/enum_util.hpp b/src/duckdb/src/include/duckdb/common/enum_util.hpp index 16b224a20..780f29063 100644 --- a/src/duckdb/src/include/duckdb/common/enum_util.hpp +++ b/src/duckdb/src/include/duckdb/common/enum_util.hpp @@ -70,6 +70,8 @@ enum class AlterForeignKeyType : uint8_t; enum class AlterScalarFunctionType : uint8_t; +enum class AlterSchemaType : uint8_t; + enum class AlterTableFunctionType : uint8_t; enum class AlterTableType : uint8_t; @@ -124,6 +126,8 @@ enum class CacheValidationMode : uint8_t; enum class CachingMode : uint8_t; +enum class CanUnloadResult : uint8_t; + enum class CatalogLookupBehavior : uint8_t; enum class CatalogType : uint8_t; @@ -154,6 +158,8 @@ enum class CompressionValidity : uint8_t; enum class ConflictManagerMode : uint8_t; +enum class ConnectionType : uint8_t; + enum class ConstraintCheckMode : uint8_t; enum class ConstraintType : uint8_t; @@ -180,6 +186,8 @@ enum class DebugInitialize : uint8_t; enum class DebugOrderVerification : uint8_t; +enum class DebugProgressVerification : uint8_t; + enum class DebugStatementVerification : uint8_t; enum class DebugVectorVerification : uint8_t; @@ -220,6 +228,10 @@ enum class ExponentType : uint8_t; enum class ExpressionClass : uint8_t; +enum class ExpressionDepthKind : uint8_t; + +enum class ExpressionTailType : uint8_t; + enum class ExpressionType : uint8_t; enum class ExtensionABIType : uint8_t; @@ -234,14 +246,14 @@ enum class ExtensionRepositoryType : uint8_t; enum class ExtensionUpdateResultTag : uint8_t; +enum class ExternalFileCacheRequestSizing : uint8_t; + enum class ExternalResourceOperation : uint8_t; enum class ExtraDropInfoType : uint8_t; enum class ExtraPersistentColumnDataType : uint8_t; -enum class ExtraTypeInfoType : uint8_t; - enum class FileBufferType : uint8_t; enum class FileExpandResult : uint8_t; @@ -254,6 +266,8 @@ enum class FileLockType : uint8_t; enum class FileNameSegmentType : uint8_t; +enum class FileSyncMode : uint8_t; + enum class FileWriteMode : uint8_t; enum class FilterPropagateResult : uint8_t; @@ -266,6 +280,8 @@ enum class FunctionErrors : uint8_t; enum class FunctionNullHandling : uint8_t; +enum class FunctionParameterKind : uint8_t; + enum class FunctionStability : uint8_t; enum class GateStatus : uint8_t; @@ -282,6 +298,8 @@ enum class HLLStorageType : uint8_t; enum class HTTPClientCachePolicy : uint8_t; +enum class HTTPRetryOutcomeOwner : uint8_t; + enum class HTTPStatusCode : uint16_t; enum class HTTPTransportReusePolicy : uint8_t; @@ -338,8 +356,16 @@ enum class LogicalOperatorRepeatability : uint8_t; enum class LogicalOperatorType : uint8_t; +enum class LogicalPlanVerificationIssueCode : int32_t; + +enum class LogicalPlanVerificationPathComponentType : int32_t; + +enum class LogicalPlanVerificationPhase : int32_t; + enum class LogicalTypeId : uint8_t; +enum class LogicalTypeInfoType : uint8_t; + enum class LookupResultType : uint8_t; enum class MacroType : uint8_t; @@ -392,6 +418,8 @@ enum class OperatorFinalResultType : uint8_t; enum class OperatorFinalizeResultType : uint8_t; +enum class OperatorMatcherMode : uint8_t; + enum class OperatorResultType : uint8_t; enum class OptimizerType : uint32_t; @@ -410,8 +438,6 @@ enum class OrdinalityType : uint8_t; enum class OutputStream : uint8_t; -enum class PEGKeywordCategory : uint8_t; - enum class ParseInfoType : uint8_t; enum class ParseResultType : uint8_t; @@ -428,8 +454,6 @@ enum class PatternMemo : uint8_t; enum class PatternOp : uint8_t; -enum class PendingExecutionResult : uint8_t; - enum class PhysicalOperatorType : uint8_t; enum class PhysicalTableScanExecutionStrategy : uint8_t; @@ -460,6 +484,8 @@ enum class ProfilingCoverage : uint8_t; enum class ProfilingParameterNames : uint8_t; +enum class ProgressInvariant : uint8_t; + enum class PushdownExtractSupport : uint8_t; enum class QualifiedNameToStringMode : uint8_t; @@ -470,9 +496,7 @@ enum class QueryNodeType : uint8_t; enum class QueryResultMemoryType : uint8_t; -enum class QueryResultOutputType : uint8_t; - -enum class QueryResultType : uint8_t; +enum class QueryResultState : uint8_t; enum class RecoveryMode : uint8_t; @@ -492,6 +516,8 @@ enum class RemoveUnusedColumnsMode : uint8_t; enum class RenderMode : uint8_t; +enum class RequestSizing : uint8_t; + enum class RequestType : uint8_t; enum class ResultLifetime : uint8_t; @@ -578,8 +604,6 @@ enum class StorageVersion : uint64_t; enum class StrTimeSpecifier : uint8_t; -enum class StreamExecutionResult : uint8_t; - enum class SubqueryType : uint8_t; enum class SuggestionState : uint8_t; @@ -724,6 +748,9 @@ const char* EnumUtil::ToChars(AlterForeignKeyType value); template<> const char* EnumUtil::ToChars(AlterScalarFunctionType value); +template<> +const char* EnumUtil::ToChars(AlterSchemaType value); + template<> const char* EnumUtil::ToChars(AlterTableFunctionType value); @@ -805,6 +832,9 @@ const char* EnumUtil::ToChars(CacheValidationMode value); template<> const char* EnumUtil::ToChars(CachingMode value); +template<> +const char* EnumUtil::ToChars(CanUnloadResult value); + template<> const char* EnumUtil::ToChars(CatalogLookupBehavior value); @@ -850,6 +880,9 @@ const char* EnumUtil::ToChars(CompressionValidity value); template<> const char* EnumUtil::ToChars(ConflictManagerMode value); +template<> +const char* EnumUtil::ToChars(ConnectionType value); + template<> const char* EnumUtil::ToChars(ConstraintCheckMode value); @@ -889,6 +922,9 @@ const char* EnumUtil::ToChars(DebugInitialize value); template<> const char* EnumUtil::ToChars(DebugOrderVerification value); +template<> +const char* EnumUtil::ToChars(DebugProgressVerification value); + template<> const char* EnumUtil::ToChars(DebugStatementVerification value); @@ -949,6 +985,12 @@ const char* EnumUtil::ToChars(ExponentType value); template<> const char* EnumUtil::ToChars(ExpressionClass value); +template<> +const char* EnumUtil::ToChars(ExpressionDepthKind value); + +template<> +const char* EnumUtil::ToChars(ExpressionTailType value); + template<> const char* EnumUtil::ToChars(ExpressionType value); @@ -970,6 +1012,9 @@ const char* EnumUtil::ToChars(ExtensionRepositoryType v template<> const char* EnumUtil::ToChars(ExtensionUpdateResultTag value); +template<> +const char* EnumUtil::ToChars(ExternalFileCacheRequestSizing value); + template<> const char* EnumUtil::ToChars(ExternalResourceOperation value); @@ -979,9 +1024,6 @@ const char* EnumUtil::ToChars(ExtraDropInfoType value); template<> const char* EnumUtil::ToChars(ExtraPersistentColumnDataType value); -template<> -const char* EnumUtil::ToChars(ExtraTypeInfoType value); - template<> const char* EnumUtil::ToChars(FileBufferType value); @@ -1000,6 +1042,9 @@ const char* EnumUtil::ToChars(FileLockType value); template<> const char* EnumUtil::ToChars(FileNameSegmentType value); +template<> +const char* EnumUtil::ToChars(FileSyncMode value); + template<> const char* EnumUtil::ToChars(FileWriteMode value); @@ -1018,6 +1063,9 @@ const char* EnumUtil::ToChars(FunctionErrors value); template<> const char* EnumUtil::ToChars(FunctionNullHandling value); +template<> +const char* EnumUtil::ToChars(FunctionParameterKind value); + template<> const char* EnumUtil::ToChars(FunctionStability value); @@ -1042,6 +1090,9 @@ const char* EnumUtil::ToChars(HLLStorageType value); template<> const char* EnumUtil::ToChars(HTTPClientCachePolicy value); +template<> +const char* EnumUtil::ToChars(HTTPRetryOutcomeOwner value); + template<> const char* EnumUtil::ToChars(HTTPStatusCode value); @@ -1126,9 +1177,21 @@ const char* EnumUtil::ToChars(LogicalOperatorRepea template<> const char* EnumUtil::ToChars(LogicalOperatorType value); +template<> +const char* EnumUtil::ToChars(LogicalPlanVerificationIssueCode value); + +template<> +const char* EnumUtil::ToChars(LogicalPlanVerificationPathComponentType value); + +template<> +const char* EnumUtil::ToChars(LogicalPlanVerificationPhase value); + template<> const char* EnumUtil::ToChars(LogicalTypeId value); +template<> +const char* EnumUtil::ToChars(LogicalTypeInfoType value); + template<> const char* EnumUtil::ToChars(LookupResultType value); @@ -1207,6 +1270,9 @@ const char* EnumUtil::ToChars(OperatorFinalResultType v template<> const char* EnumUtil::ToChars(OperatorFinalizeResultType value); +template<> +const char* EnumUtil::ToChars(OperatorMatcherMode value); + template<> const char* EnumUtil::ToChars(OperatorResultType value); @@ -1234,9 +1300,6 @@ const char* EnumUtil::ToChars(OrdinalityType value); template<> const char* EnumUtil::ToChars(OutputStream value); -template<> -const char* EnumUtil::ToChars(PEGKeywordCategory value); - template<> const char* EnumUtil::ToChars(ParseInfoType value); @@ -1261,9 +1324,6 @@ const char* EnumUtil::ToChars(PatternMemo value); template<> const char* EnumUtil::ToChars(PatternOp value); -template<> -const char* EnumUtil::ToChars(PendingExecutionResult value); - template<> const char* EnumUtil::ToChars(PhysicalOperatorType value); @@ -1309,6 +1369,9 @@ const char* EnumUtil::ToChars(ProfilingCoverage value); template<> const char* EnumUtil::ToChars(ProfilingParameterNames value); +template<> +const char* EnumUtil::ToChars(ProgressInvariant value); + template<> const char* EnumUtil::ToChars(PushdownExtractSupport value); @@ -1325,10 +1388,7 @@ template<> const char* EnumUtil::ToChars(QueryResultMemoryType value); template<> -const char* EnumUtil::ToChars(QueryResultOutputType value); - -template<> -const char* EnumUtil::ToChars(QueryResultType value); +const char* EnumUtil::ToChars(QueryResultState value); template<> const char* EnumUtil::ToChars(RecoveryMode value); @@ -1357,6 +1417,9 @@ const char* EnumUtil::ToChars(RemoveUnusedColumnsMode v template<> const char* EnumUtil::ToChars(RenderMode value); +template<> +const char* EnumUtil::ToChars(RequestSizing value); + template<> const char* EnumUtil::ToChars(RequestType value); @@ -1486,9 +1549,6 @@ const char* EnumUtil::ToChars(StorageVersion value); template<> const char* EnumUtil::ToChars(StrTimeSpecifier value); -template<> -const char* EnumUtil::ToChars(StreamExecutionResult value); - template<> const char* EnumUtil::ToChars(SubqueryType value); @@ -1676,6 +1736,9 @@ AlterForeignKeyType EnumUtil::FromString(const char *value) template<> AlterScalarFunctionType EnumUtil::FromString(const char *value); +template<> +AlterSchemaType EnumUtil::FromString(const char *value); + template<> AlterTableFunctionType EnumUtil::FromString(const char *value); @@ -1757,6 +1820,9 @@ CacheValidationMode EnumUtil::FromString(const char *value) template<> CachingMode EnumUtil::FromString(const char *value); +template<> +CanUnloadResult EnumUtil::FromString(const char *value); + template<> CatalogLookupBehavior EnumUtil::FromString(const char *value); @@ -1802,6 +1868,9 @@ CompressionValidity EnumUtil::FromString(const char *value) template<> ConflictManagerMode EnumUtil::FromString(const char *value); +template<> +ConnectionType EnumUtil::FromString(const char *value); + template<> ConstraintCheckMode EnumUtil::FromString(const char *value); @@ -1841,6 +1910,9 @@ DebugInitialize EnumUtil::FromString(const char *value); template<> DebugOrderVerification EnumUtil::FromString(const char *value); +template<> +DebugProgressVerification EnumUtil::FromString(const char *value); + template<> DebugStatementVerification EnumUtil::FromString(const char *value); @@ -1901,6 +1973,12 @@ ExponentType EnumUtil::FromString(const char *value); template<> ExpressionClass EnumUtil::FromString(const char *value); +template<> +ExpressionDepthKind EnumUtil::FromString(const char *value); + +template<> +ExpressionTailType EnumUtil::FromString(const char *value); + template<> ExpressionType EnumUtil::FromString(const char *value); @@ -1922,6 +2000,9 @@ ExtensionRepositoryType EnumUtil::FromString(const char template<> ExtensionUpdateResultTag EnumUtil::FromString(const char *value); +template<> +ExternalFileCacheRequestSizing EnumUtil::FromString(const char *value); + template<> ExternalResourceOperation EnumUtil::FromString(const char *value); @@ -1931,9 +2012,6 @@ ExtraDropInfoType EnumUtil::FromString(const char *value); template<> ExtraPersistentColumnDataType EnumUtil::FromString(const char *value); -template<> -ExtraTypeInfoType EnumUtil::FromString(const char *value); - template<> FileBufferType EnumUtil::FromString(const char *value); @@ -1952,6 +2030,9 @@ FileLockType EnumUtil::FromString(const char *value); template<> FileNameSegmentType EnumUtil::FromString(const char *value); +template<> +FileSyncMode EnumUtil::FromString(const char *value); + template<> FileWriteMode EnumUtil::FromString(const char *value); @@ -1970,6 +2051,9 @@ FunctionErrors EnumUtil::FromString(const char *value); template<> FunctionNullHandling EnumUtil::FromString(const char *value); +template<> +FunctionParameterKind EnumUtil::FromString(const char *value); + template<> FunctionStability EnumUtil::FromString(const char *value); @@ -1994,6 +2078,9 @@ HLLStorageType EnumUtil::FromString(const char *value); template<> HTTPClientCachePolicy EnumUtil::FromString(const char *value); +template<> +HTTPRetryOutcomeOwner EnumUtil::FromString(const char *value); + template<> HTTPStatusCode EnumUtil::FromString(const char *value); @@ -2078,9 +2165,21 @@ LogicalOperatorRepeatability EnumUtil::FromString( template<> LogicalOperatorType EnumUtil::FromString(const char *value); +template<> +LogicalPlanVerificationIssueCode EnumUtil::FromString(const char *value); + +template<> +LogicalPlanVerificationPathComponentType EnumUtil::FromString(const char *value); + +template<> +LogicalPlanVerificationPhase EnumUtil::FromString(const char *value); + template<> LogicalTypeId EnumUtil::FromString(const char *value); +template<> +LogicalTypeInfoType EnumUtil::FromString(const char *value); + template<> LookupResultType EnumUtil::FromString(const char *value); @@ -2159,6 +2258,9 @@ OperatorFinalResultType EnumUtil::FromString(const char template<> OperatorFinalizeResultType EnumUtil::FromString(const char *value); +template<> +OperatorMatcherMode EnumUtil::FromString(const char *value); + template<> OperatorResultType EnumUtil::FromString(const char *value); @@ -2186,9 +2288,6 @@ OrdinalityType EnumUtil::FromString(const char *value); template<> OutputStream EnumUtil::FromString(const char *value); -template<> -PEGKeywordCategory EnumUtil::FromString(const char *value); - template<> ParseInfoType EnumUtil::FromString(const char *value); @@ -2213,9 +2312,6 @@ PatternMemo EnumUtil::FromString(const char *value); template<> PatternOp EnumUtil::FromString(const char *value); -template<> -PendingExecutionResult EnumUtil::FromString(const char *value); - template<> PhysicalOperatorType EnumUtil::FromString(const char *value); @@ -2261,6 +2357,9 @@ ProfilingCoverage EnumUtil::FromString(const char *value); template<> ProfilingParameterNames EnumUtil::FromString(const char *value); +template<> +ProgressInvariant EnumUtil::FromString(const char *value); + template<> PushdownExtractSupport EnumUtil::FromString(const char *value); @@ -2277,10 +2376,7 @@ template<> QueryResultMemoryType EnumUtil::FromString(const char *value); template<> -QueryResultOutputType EnumUtil::FromString(const char *value); - -template<> -QueryResultType EnumUtil::FromString(const char *value); +QueryResultState EnumUtil::FromString(const char *value); template<> RecoveryMode EnumUtil::FromString(const char *value); @@ -2309,6 +2405,9 @@ RemoveUnusedColumnsMode EnumUtil::FromString(const char template<> RenderMode EnumUtil::FromString(const char *value); +template<> +RequestSizing EnumUtil::FromString(const char *value); + template<> RequestType EnumUtil::FromString(const char *value); @@ -2438,9 +2537,6 @@ StorageVersion EnumUtil::FromString(const char *value); template<> StrTimeSpecifier EnumUtil::FromString(const char *value); -template<> -StreamExecutionResult EnumUtil::FromString(const char *value); - template<> SubqueryType EnumUtil::FromString(const char *value); diff --git a/src/duckdb/src/include/duckdb/common/enums/checkpoint_abort.hpp b/src/duckdb/src/include/duckdb/common/enums/checkpoint_abort.hpp index aa8cd04a2..a0a1cf7fc 100644 --- a/src/duckdb/src/include/duckdb/common/enums/checkpoint_abort.hpp +++ b/src/duckdb/src/include/duckdb/common/enums/checkpoint_abort.hpp @@ -21,7 +21,8 @@ enum class CheckpointAbort : uint8_t { DEBUG_ABORT_BEFORE_MOVING_RECOVERY = 5, DEBUG_ABORT_BEFORE_DELETING_CHECKPOINT_WAL = 6, DEBUG_ABORT_BEFORE_HEADER_NON_FATAL = 7, - DEBUG_ABORT_IN_MEMORY_CHECKPOINT = 8 + DEBUG_ABORT_IN_MEMORY_CHECKPOINT = 8, + DEBUG_ABORT_BEFORE_WAL_FLUSH = 9 }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/enums/compression_type.hpp b/src/duckdb/src/include/duckdb/common/enums/compression_type.hpp index 988667145..a38841719 100644 --- a/src/duckdb/src/include/duckdb/common/enums/compression_type.hpp +++ b/src/duckdb/src/include/duckdb/common/enums/compression_type.hpp @@ -33,7 +33,7 @@ enum class CompressionType : uint8_t { COMPRESSION_ROARING = 13, COMPRESSION_EMPTY = 14, // internal only COMPRESSION_DICT_FSST = 15, - COMPRESSION_COUNT // This has to stay the last entry of the type! + ENUM_SIZE // This has to stay the last entry of the type! }; struct CompressionAvailabilityResult { diff --git a/src/duckdb/src/include/duckdb/common/enums/pending_execution_result.hpp b/src/duckdb/src/include/duckdb/common/enums/connection_type.hpp similarity index 60% rename from src/duckdb/src/include/duckdb/common/enums/pending_execution_result.hpp rename to src/duckdb/src/include/duckdb/common/enums/connection_type.hpp index 406ffbe9e..b6193a260 100644 --- a/src/duckdb/src/include/duckdb/common/enums/pending_execution_result.hpp +++ b/src/duckdb/src/include/duckdb/common/enums/connection_type.hpp @@ -1,7 +1,7 @@ //===----------------------------------------------------------------------===// // DuckDB // -// duckdb/common/enums/pending_execution_result.hpp +// duckdb/common/enums/connection_type.hpp // // //===----------------------------------------------------------------------===// @@ -12,13 +12,11 @@ namespace duckdb { -enum class PendingExecutionResult : uint8_t { - RESULT_READY, - RESULT_NOT_READY, - EXECUTION_ERROR, - BLOCKED, - NO_TASKS_AVAILABLE, - EXECUTION_FINISHED +enum class ConnectionType : uint8_t { + //! A connection opened by a user + USER, + //! A connection DuckDB opened for its own use (e.g. a checkpoint) + INTERNAL, }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/enums/database_modification_type.hpp b/src/duckdb/src/include/duckdb/common/enums/database_modification_type.hpp index b9dc97b60..15950d302 100644 --- a/src/duckdb/src/include/duckdb/common/enums/database_modification_type.hpp +++ b/src/duckdb/src/include/duckdb/common/enums/database_modification_type.hpp @@ -39,6 +39,10 @@ struct DatabaseModificationType { return *this; } + bool Contains(DatabaseModificationType other) const { + return (value & other.value) == other.value; + } + bool InsertData() const { return value & INSERT_DATA; } diff --git a/src/duckdb/src/include/duckdb/common/enums/debug_progress_verification.hpp b/src/duckdb/src/include/duckdb/common/enums/debug_progress_verification.hpp new file mode 100644 index 000000000..d891e4bb1 --- /dev/null +++ b/src/duckdb/src/include/duckdb/common/enums/debug_progress_verification.hpp @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/common/enums/debug_progress_verification.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/constants.hpp" +#include "duckdb/common/windows_undefs.hpp" + +namespace duckdb { + +//! How operator progress reporting is verified (debug setting) +enum class DebugProgressVerification : uint8_t { + //! No verification (default) + NONE = 0, + //! Violations are written to the log (log type "ProgressVerification") + LOG = 1, + //! Violations that are not ignored fail the query + ERROR = 2 +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/enums/debug_statement_verification.hpp b/src/duckdb/src/include/duckdb/common/enums/debug_statement_verification.hpp index 0b7617f9d..c79345cac 100644 --- a/src/duckdb/src/include/duckdb/common/enums/debug_statement_verification.hpp +++ b/src/duckdb/src/include/duckdb/common/enums/debug_statement_verification.hpp @@ -18,7 +18,9 @@ enum class DebugStatementVerification : uint8_t { REPARSE_STATEMENT, SERIALIZE_STATEMENT, PREPARED_STATEMENT, - EXPLAIN_STATEMENT + EXPLAIN_STATEMENT, + EXPLAIN_SQL, + EXPLAIN_SQL_STRICT }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/enums/current_transaction_state.hpp b/src/duckdb/src/include/duckdb/common/enums/file_sync_mode.hpp similarity index 64% rename from src/duckdb/src/include/duckdb/common/enums/current_transaction_state.hpp rename to src/duckdb/src/include/duckdb/common/enums/file_sync_mode.hpp index aff42da2a..300e38db3 100644 --- a/src/duckdb/src/include/duckdb/common/enums/current_transaction_state.hpp +++ b/src/duckdb/src/include/duckdb/common/enums/file_sync_mode.hpp @@ -1,15 +1,17 @@ //===----------------------------------------------------------------------===// // DuckDB // -// duckdb/common/enums/current_transaction_state.hpp +// duckdb/common/enums/file_sync_mode.hpp // // //===----------------------------------------------------------------------===// #pragma once +#include "duckdb/common/constants.hpp" + namespace duckdb { -enum CurrentTransactionState { IN_ACTIVE_TRANSACTION, NOT_IN_ACTIVE_TRANSACTION }; +enum class FileSyncMode : uint8_t { STANDARD, NONE, FULL }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/enums/stream_execution_result.hpp b/src/duckdb/src/include/duckdb/common/enums/query_result_memory_type.hpp similarity index 57% rename from src/duckdb/src/include/duckdb/common/enums/stream_execution_result.hpp rename to src/duckdb/src/include/duckdb/common/enums/query_result_memory_type.hpp index 19ba824dd..3ed1f97c3 100644 --- a/src/duckdb/src/include/duckdb/common/enums/stream_execution_result.hpp +++ b/src/duckdb/src/include/duckdb/common/enums/query_result_memory_type.hpp @@ -1,7 +1,7 @@ //===----------------------------------------------------------------------===// // DuckDB // -// duckdb/common/enums/stream_execution_result.hpp +// duckdb/common/enums/query_result_memory_type.hpp // // //===----------------------------------------------------------------------===// @@ -12,14 +12,7 @@ namespace duckdb { -enum class StreamExecutionResult : uint8_t { - CHUNK_READY, - CHUNK_NOT_READY, - EXECUTION_ERROR, - EXECUTION_CANCELLED, - BLOCKED, - NO_TASKS_AVAILABLE, - EXECUTION_FINISHED -}; +//! Where a retained result keeps its rows +enum class QueryResultMemoryType : uint8_t { IN_MEMORY, BUFFER_MANAGED }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/enums/query_result_state.hpp b/src/duckdb/src/include/duckdb/common/enums/query_result_state.hpp new file mode 100644 index 000000000..a91680914 --- /dev/null +++ b/src/duckdb/src/include/duckdb/common/enums/query_result_state.hpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/common/enums/query_result_state.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/constants.hpp" + +namespace duckdb { + +//! Where a submitted query stands. READY means the engine is waiting on the consumer: producers are +//! parked for a retention decision, or parked for buffer space with a chunk to pop +enum class QueryResultState : uint8_t { READY, NOT_READY, BLOCKED, NO_TASKS_AVAILABLE, FINISHED, EXECUTION_ERROR }; + +//! Whether execution reached a terminal state +constexpr bool IsTerminal(QueryResultState state) { + return state == QueryResultState::FINISHED || state == QueryResultState::EXECUTION_ERROR; +} + +//! Whether the result is ready to be observed by a consumer. Execution is in ready state or in a final state. +constexpr bool IsObservable(QueryResultState state) { + return IsTerminal(state) || state == QueryResultState::READY; +} + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/enums/result_ordering.hpp b/src/duckdb/src/include/duckdb/common/enums/result_ordering.hpp new file mode 100644 index 000000000..55cce192d --- /dev/null +++ b/src/duckdb/src/include/duckdb/common/enums/result_ordering.hpp @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/common/enums/result_ordering.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/constants.hpp" + +namespace duckdb { + +//! How the consumer-visible chunk order is established +enum class ResultOrdering : uint8_t { + //! No order guarantee: a parallel sink stores chunks as they arrive + UNORDERED, + //! Source order, preserved by sinking through a single thread + SOURCE_ORDERED, + //! Source order, restored from batch indexes under a parallel sink + BATCH_INDEX_ORDERED +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/enums/statement_type.hpp b/src/duckdb/src/include/duckdb/common/enums/statement_type.hpp index a780ca990..d458a00c6 100644 --- a/src/duckdb/src/include/duckdb/common/enums/statement_type.hpp +++ b/src/duckdb/src/include/duckdb/common/enums/statement_type.hpp @@ -12,7 +12,6 @@ #include "duckdb/common/identifier.hpp" #include "duckdb/common/optional_idx.hpp" #include "duckdb/common/unordered_set.hpp" -#include "duckdb/common/query_parameters.hpp" #include "duckdb/common/enums/database_modification_type.hpp" namespace duckdb { @@ -54,7 +53,9 @@ enum class StatementType : uint8_t { MERGE_INTO_STATEMENT, CONNECT_STATEMENT, DISCONNECT_STATEMENT, - EXTERNAL_RESOURCE_STATEMENT + EXTERNAL_RESOURCE_STATEMENT, + PASSTHROUGH_STATEMENT, + ENUM_SIZE // This has to stay the last entry of the type! }; DUCKDB_API string StatementTypeToString(StatementType type); @@ -73,9 +74,8 @@ class ClientContext; //! A struct containing various properties of a SQL statement struct StatementProperties { StatementProperties() - : requires_valid_transaction(true), output_type(QueryResultOutputType::FORCE_MATERIALIZED), - bound_all_parameters(true), return_type(StatementReturnType::QUERY_RESULT), parameter_count(0), - always_require_rebind(false) { + : requires_valid_transaction(true), bound_all_parameters(true), return_type(StatementReturnType::QUERY_RESULT), + parameter_count(0), always_require_rebind(false) { } struct CatalogIdentity { @@ -103,8 +103,6 @@ struct StatementProperties { //! Whether or not the statement requires a valid transaction. Almost all statements require this, with the //! exception of ROLLBACK bool requires_valid_transaction; - //! Whether or not the result can be streamed to the client - QueryResultOutputType output_type; //! Whether or not all parameters have successfully had their types determined bool bound_all_parameters; //! What type of data the statement returns @@ -114,7 +112,7 @@ struct StatementProperties { //! Whether or not the statement ALWAYS requires a rebind bool always_require_rebind; - bool IsReadOnly() { + bool IsReadOnly() const { return modified_databases.empty(); } diff --git a/src/duckdb/src/include/duckdb/common/exception/http_exception.hpp b/src/duckdb/src/include/duckdb/common/exception/http_exception.hpp index b0d0e9c2d..10617fde2 100644 --- a/src/duckdb/src/include/duckdb/common/exception/http_exception.hpp +++ b/src/duckdb/src/include/duckdb/common/exception/http_exception.hpp @@ -23,6 +23,11 @@ class HTTPException : public Exception { explicit HTTPException(const string &message) : Exception(ExceptionType::HTTP, message) { } + //! Rebuild an HTTP exception from extra info captured earlier (e.g. to extend its message). + explicit HTTPException(const unordered_map &extra_info, const string &message) + : Exception(extra_info, ExceptionType::HTTP, message) { + } + template ::status = 0, typename... ARGS> explicit HTTPException(RESPONSE &response, const string &msg, ARGS &&...params) : HTTPException(static_cast(response.status), response.body, response.headers, response.reason, msg, diff --git a/src/duckdb/src/include/duckdb/common/extra_operator_info.hpp b/src/duckdb/src/include/duckdb/common/extra_operator_info.hpp index 14055d9fc..ae452cc44 100644 --- a/src/duckdb/src/include/duckdb/common/extra_operator_info.hpp +++ b/src/duckdb/src/include/duckdb/common/extra_operator_info.hpp @@ -13,36 +13,21 @@ #include #include "duckdb/common/operator/comparison_operators.hpp" #include "duckdb/common/optional_idx.hpp" +#include "duckdb/common/optional.hpp" +#include "duckdb/planner/expression.hpp" #include "duckdb/parser/parsed_data/sample_options.hpp" namespace duckdb { class ExtraOperatorInfo { public: + //! Table index of retained file-filter bindings, reserved above any binder-generated index + static constexpr idx_t FILE_FILTER_TABLE_INDEX = DConstants::INVALID_INDEX - 1; + ExtraOperatorInfo() : file_filters(""), sample_options(nullptr) { } - ExtraOperatorInfo(ExtraOperatorInfo &&extra_info) noexcept - : file_filters(std::move(extra_info.file_filters)), sample_options(std::move(extra_info.sample_options)) { - if (extra_info.total_files.IsValid()) { - total_files = extra_info.total_files.GetIndex(); - } - if (extra_info.filtered_files.IsValid()) { - filtered_files = extra_info.filtered_files.GetIndex(); - } - } - ExtraOperatorInfo &operator=(ExtraOperatorInfo &&extra_info) noexcept { - if (this != &extra_info) { - file_filters = extra_info.file_filters; - if (extra_info.total_files.IsValid()) { - total_files = extra_info.total_files.GetIndex(); - } - if (extra_info.filtered_files.IsValid()) { - filtered_files = extra_info.filtered_files.GetIndex(); - } - sample_options = std::move(extra_info.sample_options); - } - return *this; - } + ExtraOperatorInfo(ExtraOperatorInfo &&extra_info) noexcept = default; + ExtraOperatorInfo &operator=(ExtraOperatorInfo &&extra_info) noexcept = default; bool operator==(const ExtraOperatorInfo &other) const { return file_filters == other.file_filters && total_files == other.total_files && @@ -51,6 +36,8 @@ class ExtraOperatorInfo { //! Filters that have been pushed down into the main file list string file_filters; + //! Consumed file predicates with bindings in the unprojected source column space. + optional>> file_filter_expressions; //! Total size of file list optional_idx total_files; //! Size of file list after applying filters diff --git a/src/duckdb/src/include/duckdb/common/extra_type_info.hpp b/src/duckdb/src/include/duckdb/common/extra_type_info.hpp deleted file mode 100644 index 6ceb3d843..000000000 --- a/src/duckdb/src/include/duckdb/common/extra_type_info.hpp +++ /dev/null @@ -1,296 +0,0 @@ -//===----------------------------------------------------------------------===// -// DuckDB -// -// duckdb/common/extra_type_info.hpp -// -// -//===----------------------------------------------------------------------===// - -#pragma once - -#include "duckdb/common/common.hpp" -#include "duckdb/common/types/vector.hpp" -#include "duckdb/common/extension_type_info.hpp" -#include "duckdb/common/types/geometry_crs.hpp" - -namespace duckdb { - -class ParsedExpression; - -//! Extra Type Info Type -enum class ExtraTypeInfoType : uint8_t { - INVALID_TYPE_INFO = 0, - GENERIC_TYPE_INFO = 1, - DECIMAL_TYPE_INFO = 2, - STRING_TYPE_INFO = 3, - LIST_TYPE_INFO = 4, - STRUCT_TYPE_INFO = 5, - ENUM_TYPE_INFO = 6, - UNBOUND_TYPE_INFO = 7, - LEGACY_AGGREGATE_STATE_TYPE_INFO = 8, - ARRAY_TYPE_INFO = 9, - ANY_TYPE_INFO = 10, - INTEGER_LITERAL_TYPE_INFO = 11, - TEMPLATE_TYPE_INFO = 12, - GEO_TYPE_INFO = 13 -}; - -struct ExtraTypeInfo { - ExtraTypeInfoType type; - string alias; - unique_ptr extension_info; - - explicit ExtraTypeInfo(ExtraTypeInfoType type); - explicit ExtraTypeInfo(ExtraTypeInfoType type, string alias); - virtual ~ExtraTypeInfo(); - -protected: - // copy constructor (protected) - ExtraTypeInfo(const ExtraTypeInfo &other); - ExtraTypeInfo &operator=(const ExtraTypeInfo &other); - -public: - bool Equals(const ExtraTypeInfo *other_p) const; - - virtual void Serialize(Serializer &serializer) const; - static shared_ptr Deserialize(Deserializer &source); - virtual shared_ptr Copy() const; - virtual shared_ptr DeepCopy() const; - //! Copy the base fields (alias, extension info) into "target" - used by Copy/DeepCopy implementations that - //! reconstruct the type info instead of copy-constructing it - void CopyBaseInfo(ExtraTypeInfo &target) const; - - template - TARGET &Cast() { - DynamicCastCheck(this); - return reinterpret_cast(*this); - } - template - const TARGET &Cast() const { - DynamicCastCheck(this); - return reinterpret_cast(*this); - } - -protected: - virtual bool EqualsInternal(const ExtraTypeInfo *other_p) const; -}; - -struct DecimalTypeInfo : public ExtraTypeInfo { - DecimalTypeInfo(uint8_t width_p, uint8_t scale_p); - - uint8_t width; - uint8_t scale; - -public: - void Serialize(Serializer &serializer) const override; - static shared_ptr Deserialize(Deserializer &source); - shared_ptr Copy() const override; - -protected: - bool EqualsInternal(const ExtraTypeInfo *other_p) const override; - -private: - DecimalTypeInfo(); -}; - -struct StringTypeInfo : public ExtraTypeInfo { - explicit StringTypeInfo(string collation_p); - - string collation; - -public: - void Serialize(Serializer &serializer) const override; - static shared_ptr Deserialize(Deserializer &source); - shared_ptr Copy() const override; - -protected: - bool EqualsInternal(const ExtraTypeInfo *other_p) const override; - -private: - StringTypeInfo(); -}; - -struct ListTypeInfo : public ExtraTypeInfo { - explicit ListTypeInfo(LogicalType child_type_p); - - LogicalType child_type; - -public: - void Serialize(Serializer &serializer) const override; - static shared_ptr Deserialize(Deserializer &source); - shared_ptr Copy() const override; - shared_ptr DeepCopy() const override; - -protected: - bool EqualsInternal(const ExtraTypeInfo *other_p) const override; - -private: - ListTypeInfo(); -}; - -struct StructTypeInfo : public ExtraTypeInfo { - explicit StructTypeInfo(child_list_t child_types_p); - explicit StructTypeInfo(ExtraTypeInfoType type, child_list_t child_types_p); - - child_list_t child_types; - -public: - void Serialize(Serializer &serializer) const override; - static shared_ptr Deserialize(Deserializer &deserializer); - shared_ptr Copy() const override; - shared_ptr DeepCopy() const override; - -protected: - bool EqualsInternal(const ExtraTypeInfo *other_p) const override; - -private: - StructTypeInfo(); -}; - -struct LegacyAggregateStateTypeInfo : public ExtraTypeInfo { -public: - void Serialize(Serializer &serializer) const override; - // Legacy deserialize method kept only for compatibility with old database files - static shared_ptr Deserialize(Deserializer &source); - - static shared_ptr LegacyDeserialize(); - -protected: - bool EqualsInternal(const ExtraTypeInfo *other_p) const override; - -private: - LegacyAggregateStateTypeInfo(); -}; - -// If this type is primarily stored in the catalog or not. Enums from Pandas/Factors are not in the catalog. -enum EnumDictType : uint8_t { INVALID = 0, VECTOR_DICT = 1 }; - -struct EnumTypeInfo : public ExtraTypeInfo { - explicit EnumTypeInfo(const Vector &values_insert_order_p, idx_t dict_size_p); - EnumTypeInfo(const EnumTypeInfo &) = delete; - EnumTypeInfo &operator=(const EnumTypeInfo &) = delete; - -public: - const EnumDictType &GetEnumDictType() const; - const Vector &GetValuesInsertOrder() const; - const idx_t &GetDictSize() const; - static PhysicalType DictType(idx_t size); - - static LogicalType CreateType(const Vector &ordered_data, idx_t size); - static shared_ptr CreateTypeInfo(const Vector &ordered_data, idx_t size); - - void Serialize(Serializer &serializer) const override; - static shared_ptr Deserialize(Deserializer &source); - shared_ptr Copy() const override; - -protected: - // Equalities are only used in enums with different catalog entries - bool EqualsInternal(const ExtraTypeInfo *other_p) const override; - - Vector values_insert_order; - -private: - EnumDictType dict_type; - idx_t dict_size; -}; - -struct ArrayTypeInfo : public ExtraTypeInfo { - LogicalType child_type; - uint32_t size; - explicit ArrayTypeInfo(LogicalType child_type_p, uint32_t size_p); - -public: - void Serialize(Serializer &serializer) const override; - static shared_ptr Deserialize(Deserializer &reader); - shared_ptr Copy() const override; - shared_ptr DeepCopy() const override; - -protected: - bool EqualsInternal(const ExtraTypeInfo *other_p) const override; -}; - -struct AnyTypeInfo : public ExtraTypeInfo { - AnyTypeInfo(LogicalType target_type, idx_t cast_score); - - LogicalType target_type; - idx_t cast_score; - -public: - void Serialize(Serializer &serializer) const override; - static shared_ptr Deserialize(Deserializer &source); - shared_ptr Copy() const override; - shared_ptr DeepCopy() const override; - -protected: - bool EqualsInternal(const ExtraTypeInfo *other_p) const override; - -private: - AnyTypeInfo(); -}; - -struct IntegerLiteralTypeInfo : public ExtraTypeInfo { - explicit IntegerLiteralTypeInfo(Value constant_value); - - Value constant_value; - -public: - void Serialize(Serializer &serializer) const override; - static shared_ptr Deserialize(Deserializer &source); - shared_ptr Copy() const override; - -protected: - bool EqualsInternal(const ExtraTypeInfo *other_p) const override; - -private: - IntegerLiteralTypeInfo(); -}; - -struct TemplateTypeInfo : public ExtraTypeInfo { - explicit TemplateTypeInfo(string name_p); - - // The name of the template, e.g. `T`, or `KEY_TYPE`. Used to distinguish between different template types within - // the same function. The binder tries to resolve all templates with the same name to the same concrete type. - string name; - -public: - void Serialize(Serializer &serializer) const override; - static shared_ptr Deserialize(Deserializer &source); - shared_ptr Copy() const override; - -protected: - bool EqualsInternal(const ExtraTypeInfo *other_p) const override; - TemplateTypeInfo(); -}; - -struct GeoTypeInfo : public ExtraTypeInfo { -public: - GeoTypeInfo(); - - void Serialize(Serializer &serializer) const override; - static shared_ptr Deserialize(Deserializer &source); - shared_ptr Copy() const override; - - // The Coordinate Reference System associated with this geometry type - CoordinateReferenceSystem crs; - -protected: - bool EqualsInternal(const ExtraTypeInfo *other_p) const override; -}; - -struct UnboundTypeInfo : public ExtraTypeInfo { - explicit UnboundTypeInfo(unique_ptr expr_p); - - unique_ptr expr; - - void Serialize(Serializer &serializer) const override; - static shared_ptr Deserialize(Deserializer &source); - shared_ptr Copy() const override; - -protected: - bool EqualsInternal(const ExtraTypeInfo *other_p) const override; - -private: - UnboundTypeInfo(); -}; - -} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/file_open_flags.hpp b/src/duckdb/src/include/duckdb/common/file_open_flags.hpp index 6dd57c010..69c2e7af8 100644 --- a/src/duckdb/src/include/duckdb/common/file_open_flags.hpp +++ b/src/duckdb/src/include/duckdb/common/file_open_flags.hpp @@ -59,14 +59,21 @@ class FileOpenFlags { return a == CachingMode::NO_CACHING ? b : a; } + static constexpr RequestSizing MergeRequestSizing(RequestSizing a, RequestSizing b) { + return a == RequestSizing::BY_CACHE ? b : a; + } + inline FileOpenFlags operator|(const FileOpenFlags &b) const { - return FileOpenFlags(flags | b.flags, MergeLock(lock, b.lock), MergeCompression(compression, b.compression)); + FileOpenFlags result(flags | b.flags, MergeLock(lock, b.lock), MergeCompression(compression, b.compression)); + result.request_sizing = MergeRequestSizing(request_sizing, b.request_sizing); + return result; } inline FileOpenFlags &operator|=(const FileOpenFlags &b) { flags |= b.flags; lock = MergeLock(lock, b.lock); compression = MergeCompression(compression, b.compression); caching_mode = MergeCachingMode(caching_mode, b.caching_mode); + request_sizing = MergeRequestSizing(request_sizing, b.request_sizing); return *this; } @@ -89,6 +96,13 @@ class FileOpenFlags { caching_mode = new_caching_mode; } + RequestSizing GetRequestSizing() const { + return request_sizing; + } + void SetRequestSizing(RequestSizing new_request_sizing) { + request_sizing = new_request_sizing; + } + void Verify(); inline bool OpenForReading() const { @@ -141,6 +155,7 @@ class FileOpenFlags { idx_t flags = 0; FileLockType lock = FileLockType::NO_LOCK; CachingMode caching_mode = CachingMode::NO_CACHING; + RequestSizing request_sizing = RequestSizing::BY_CACHE; //! Default-constructed FileCompressionType is UNCOMPRESSED FileCompressionType compression; }; diff --git a/src/duckdb/src/include/duckdb/common/file_system.hpp b/src/duckdb/src/include/duckdb/common/file_system.hpp index e356342bc..7261cc5d3 100644 --- a/src/duckdb/src/include/duckdb/common/file_system.hpp +++ b/src/duckdb/src/include/duckdb/common/file_system.hpp @@ -236,6 +236,9 @@ class FileSystem { DUCKDB_API virtual FileType GetFileType(FileHandle &handle); //! Returns the file stats of the attached handle. DUCKDB_API virtual FileMetadata Stats(FileHandle &handle); + //! Returns the stats of a file, or nullopt if the file does not exist. + DUCKDB_API virtual optional GetStatsIfExists(const OpenFileInfo &file, + optional_ptr opener = nullptr); //! Truncate a file to a maximum size of new_size, new_size should be smaller than or equal to the current size of //! the file DUCKDB_API virtual void Truncate(FileHandle &handle, int64_t new_size); diff --git a/src/duckdb/src/include/duckdb/common/filename_pattern.hpp b/src/duckdb/src/include/duckdb/common/filename_pattern.hpp index 9c7911d8d..ad7bf7e84 100644 --- a/src/duckdb/src/include/duckdb/common/filename_pattern.hpp +++ b/src/duckdb/src/include/duckdb/common/filename_pattern.hpp @@ -45,6 +45,10 @@ class FilenamePattern { bool HasUUID() const; + const vector &Segments() const { + return segments; + } + public: // serialization code for backwards compatibility string SerializeBase() const; diff --git a/src/duckdb/src/include/duckdb/common/fsst.hpp b/src/duckdb/src/include/duckdb/common/fsst.hpp index 2d01d2522..40c583057 100644 --- a/src/duckdb/src/include/duckdb/common/fsst.hpp +++ b/src/duckdb/src/include/duckdb/common/fsst.hpp @@ -38,6 +38,8 @@ class FSSTPrimitives { uint64_t extra_space[string_t::INLINE_BYTES]; }; + [[noreturn]] static void ThrowInvalidCompressedString(); + public: static string_t DecompressValue(void *duckdb_fsst_decoder, ArenaAllocator &str_allocator, const char *compressed_string, const idx_t compressed_string_len) { @@ -47,6 +49,9 @@ class FSSTPrimitives { const auto target_ptr = StringVector::AllocateShrinkableBuffer(str_allocator, max_uncompressed_length); const auto decompressed_string_size = duckdb_fsst_decompress( fsst_decoder, compressed_string_len, compressed_string_ptr, max_uncompressed_length, target_ptr); + if (decompressed_string_size == DUCKDB_FSST_DECOMPRESS_INVALID_INPUT) { + ThrowInvalidCompressedString(); + } return StringVector::FinalizeShrinkableBuffer(str_allocator, target_ptr, max_uncompressed_length, decompressed_string_size); } @@ -59,6 +64,9 @@ class FSSTPrimitives { const auto decompressed_string_size = duckdb_fsst_decompress(fsst_decoder, compressed_string_len, compressed_string_ptr, string_t::INLINE_LENGTH + sizeof(StringWithExtraSpace::extra_space), target_ptr); + if (decompressed_string_size == DUCKDB_FSST_DECOMPRESS_INVALID_INPUT) { + ThrowInvalidCompressedString(); + } if (decompressed_string_size > string_t::INLINE_LENGTH) { throw DataCorruptionException( "Corrupt database file: decoded FSST string of >=%llu bytes (should be <=%llu bytes)", diff --git a/src/duckdb/src/include/duckdb/common/hive_partitioning.hpp b/src/duckdb/src/include/duckdb/common/hive_partitioning.hpp index 534020b92..a5b0d97fc 100644 --- a/src/duckdb/src/include/duckdb/common/hive_partitioning.hpp +++ b/src/duckdb/src/include/duckdb/common/hive_partitioning.hpp @@ -8,6 +8,7 @@ #pragma once +#include "duckdb/common/identifier.hpp" #include "duckdb/common/types/column/partitioned_column_data.hpp" #include "duckdb/common/open_file_info.hpp" #include "duckdb/original/std/sstream.hpp" @@ -18,7 +19,8 @@ namespace duckdb { struct MultiFilePushdownInfo; struct HivePartitioningFilterInfo { - unordered_map column_map; + identifier_map_t column_map; + Identifier filename_column; bool hive_enabled; bool filename_enabled; }; diff --git a/src/duckdb/src/include/duckdb/common/hugeint.hpp b/src/duckdb/src/include/duckdb/common/hugeint.hpp index acdc4fb4b..844106506 100644 --- a/src/duckdb/src/include/duckdb/common/hugeint.hpp +++ b/src/duckdb/src/include/duckdb/common/hugeint.hpp @@ -17,7 +17,9 @@ struct hugeint_t { // NOLINT: use numeric casing public: hugeint_t() = default; - DUCKDB_API hugeint_t(int64_t value); // NOLINT: Allow implicit conversion from `int64_t` + //! constexpr so that arithmetic against a constant operand can be folded at compile + //! time; the body is at the bottom of this header + constexpr hugeint_t(int64_t value); // NOLINT: Allow implicit conversion from `int64_t` constexpr hugeint_t(int64_t upper, uint64_t lower) : lower(lower), upper(upper) { } constexpr hugeint_t(const hugeint_t &rhs) = default; @@ -28,20 +30,22 @@ struct hugeint_t { // NOLINT: use numeric casing DUCKDB_API string ToString() const; // comparison operators - DUCKDB_API bool operator==(const hugeint_t &rhs) const; - DUCKDB_API bool operator!=(const hugeint_t &rhs) const; - DUCKDB_API bool operator<=(const hugeint_t &rhs) const; - DUCKDB_API bool operator<(const hugeint_t &rhs) const; - DUCKDB_API bool operator>(const hugeint_t &rhs) const; - DUCKDB_API bool operator>=(const hugeint_t &rhs) const; + //! constexpr so that comparisons fold at compile time + constexpr bool operator==(const hugeint_t &rhs) const; + constexpr bool operator!=(const hugeint_t &rhs) const; + constexpr bool operator<=(const hugeint_t &rhs) const; + constexpr bool operator<(const hugeint_t &rhs) const; + constexpr bool operator>(const hugeint_t &rhs) const; + constexpr bool operator>=(const hugeint_t &rhs) const; // arithmetic operators - DUCKDB_API hugeint_t operator+(const hugeint_t &rhs) const; - DUCKDB_API hugeint_t operator-(const hugeint_t &rhs) const; - DUCKDB_API hugeint_t operator*(const hugeint_t &rhs) const; - DUCKDB_API hugeint_t operator/(const hugeint_t &rhs) const; + //! constexpr so the compiler can fold these when an operand is a compile-time constant + constexpr hugeint_t operator+(const hugeint_t &rhs) const; + constexpr hugeint_t operator-(const hugeint_t &rhs) const; + constexpr hugeint_t operator*(const hugeint_t &rhs) const; + constexpr hugeint_t operator/(const hugeint_t &rhs) const; DUCKDB_API hugeint_t operator%(const hugeint_t &rhs) const; - DUCKDB_API hugeint_t operator-() const; + constexpr hugeint_t operator-() const; // bitwise operators DUCKDB_API hugeint_t operator>>(const hugeint_t &rhs) const; @@ -79,6 +83,321 @@ struct hugeint_t { // NOLINT: use numeric casing DUCKDB_API explicit operator uhugeint_t() const; }; +//===--------------------------------------------------------------------===// +// constexpr 128-bit arithmetic +// +// These live here rather than next to the rest of Hugeint because +// duckdb/common/types/hugeint.hpp includes duckdb/common/limits.hpp, which +// includes this file back. Everything the digit-parsing loops need has to be +// reachable from here, and the bodies are at the bottom of the file because a +// member function body defined inside the class is looked up at the class's +// closing brace -- above the helpers below. +//===--------------------------------------------------------------------===// + +//! NumericLimits::Minimum()/Maximum(), spelled out because limits.hpp +//! includes this file back so the traits cannot be used here +constexpr hugeint_t HugeintMinimum() { + return hugeint_t(INT64_MIN, 0); +} + +constexpr hugeint_t HugeintMaximum() { + return hugeint_t(INT64_MAX, UINT64_MAX); +} + +constexpr bool HugeintEquals(hugeint_t lhs, hugeint_t rhs) { + return lhs.upper == rhs.upper && lhs.lower == rhs.lower; +} + +constexpr bool HugeintNotEquals(hugeint_t lhs, hugeint_t rhs) { + return !HugeintEquals(lhs, rhs); +} + +constexpr bool HugeintGreaterThan(hugeint_t lhs, hugeint_t rhs) { + return lhs.upper > rhs.upper || (lhs.upper == rhs.upper && lhs.lower > rhs.lower); +} + +constexpr bool HugeintGreaterThanEquals(hugeint_t lhs, hugeint_t rhs) { + return lhs.upper > rhs.upper || (lhs.upper == rhs.upper && lhs.lower >= rhs.lower); +} + +constexpr bool HugeintLessThan(hugeint_t lhs, hugeint_t rhs) { + return lhs.upper < rhs.upper || (lhs.upper == rhs.upper && lhs.lower < rhs.lower); +} + +constexpr bool HugeintLessThanEquals(hugeint_t lhs, hugeint_t rhs) { + return lhs.upper < rhs.upper || (lhs.upper == rhs.upper && lhs.lower <= rhs.lower); +} + +//! Two's-complement negation, written out because the operators below cannot call +//! operator-() on themselves without the fold failing +constexpr hugeint_t HugeintNegate(hugeint_t n) { + return hugeint_t(static_cast(~static_cast(n.upper) + (n.lower == 0)), 0 - n.lower); +} + +constexpr int HugeintSign(hugeint_t n) { + // the sign is not decided by upper alone: upper == 0 with a non-zero lower is positive + return int(HugeintGreaterThan(n, hugeint_t(0))) - int(HugeintLessThan(n, hugeint_t(0))); +} + +constexpr hugeint_t HugeintAbs(hugeint_t n) { + return n.upper < 0 ? HugeintNegate(n) : n; +} + +constexpr hugeint_t HugeintAddOne(hugeint_t n) { + const int carry = (n.lower == UINT64_MAX) ? 1 : 0; + return hugeint_t(static_cast(static_cast(n.upper) + carry), n.lower + 1); +} + +constexpr hugeint_t HugeintSubtractOne(hugeint_t n) { + // lower wraps to UINT64_MAX, so a borrow is taken out of upper + const int borrow = (n.lower == 0) ? 1 : 0; + return hugeint_t(static_cast(static_cast(n.upper) - borrow), n.lower - 1); +} + +//! Helpers for the bit-at-a-time division, from the same uint128_t reference the +//! runtime implementation uses +constexpr uint8_t HugeintHighestBit(hugeint_t bits) { + uint8_t out = 0; + if (bits.upper) { + // bit index 64 is the lowest bit of upper + out = 64; + uint64_t up = static_cast(bits.upper); + while (up) { + up >>= 1; + out++; + } + } else { + uint64_t low = bits.lower; + while (low) { + low >>= 1; + out++; + } + } + return out; +} + +constexpr bool HugeintIsBitSet(hugeint_t lhs, uint8_t bit_position) { + if (bit_position < 64) { + return lhs.lower & (uint64_t(1) << uint64_t(bit_position)); + } + return static_cast(lhs.upper) & (uint64_t(1) << uint64_t(bit_position - 64)); +} + +//! Shift left by 1..63 bits. Called by the division loop below with amount == 1. +//! Takes no D_ASSERT: in assertion builds that macro is an unconditional function call, which +//! would make this function unusable in a constant expression. +constexpr hugeint_t HugeintLeftShift(hugeint_t lhs, uint32_t amount) { + hugeint_t result {0, 0}; + result.lower = lhs.lower << amount; + result.upper = static_cast((static_cast(lhs.upper) << amount) + (lhs.lower >> (64 - amount))); + return result; +} + +constexpr hugeint_t HugeintMultiplyUnchecked(hugeint_t lhs, hugeint_t rhs) { + // Unchecked multiplication uses the low 128 bits, including for negative operands. +#if ((__GNUC__ >= 5) || defined(__clang__)) && defined(__SIZEOF_INT128__) + const auto left = (static_cast<__uint128_t>(static_cast(lhs.upper)) << 64) | lhs.lower; + const auto right = (static_cast<__uint128_t>(static_cast(rhs.upper)) << 64) | rhs.lower; + const auto result = left * right; + return hugeint_t(static_cast(result >> 64), static_cast(result)); +#else + // Multiply code adapted from: + // https://github.com/calccrypto/uint128_t/blob/master/uint128_t.cpp + + // split values into 4 32-bit parts + uint64_t top[4] {uint64_t(lhs.upper) >> 32, uint64_t(lhs.upper) & 0xffffffff, lhs.lower >> 32, + lhs.lower & 0xffffffff}; + uint64_t bottom[4] {uint64_t(rhs.upper) >> 32, uint64_t(rhs.upper) & 0xffffffff, rhs.lower >> 32, + rhs.lower & 0xffffffff}; + uint64_t products[4][4] {}; + + // multiply each component of the values + for (idx_t x = 0; x < 4; x++) { + for (idx_t y = 0; y < 4; y++) { + products[x][y] = top[x] * bottom[y]; + } + } + + // first row + uint64_t fourth32 = (products[3][3] & 0xffffffff); + uint64_t third32 = (products[3][2] & 0xffffffff) + (products[3][3] >> 32); + uint64_t second32 = (products[3][1] & 0xffffffff) + (products[3][2] >> 32); + uint64_t first32 = (products[3][0] & 0xffffffff) + (products[3][1] >> 32); + + // second row + third32 += (products[2][3] & 0xffffffff); + second32 += (products[2][2] & 0xffffffff) + (products[2][3] >> 32); + first32 += (products[2][1] & 0xffffffff) + (products[2][2] >> 32); + + // third row + second32 += (products[1][3] & 0xffffffff); + first32 += (products[1][2] & 0xffffffff) + (products[1][3] >> 32); + + // fourth row + first32 += (products[0][3] & 0xffffffff); + + // move carry to next digit + third32 += fourth32 >> 32; + second32 += third32 >> 32; + first32 += second32 >> 32; + + // remove carry from current digit + fourth32 &= 0xffffffff; + third32 &= 0xffffffff; + second32 &= 0xffffffff; + first32 &= 0xffffffff; + + return hugeint_t(static_cast((first32 << 32) + second32), (third32 << 32) + fourth32); +#endif +} + +//! Result of a 128-bit division. Hugeint::DivMod's out-parameter form is declared in +//! types/hugeint.hpp, which is not visible from this header +struct HugeintDivModResult { + hugeint_t quotient; + hugeint_t remainder; +}; + +constexpr HugeintDivModResult HugeintDivMod(hugeint_t lhs, hugeint_t rhs); + +//! Precondition: lhs or rhs equals HugeintMinimum(). Only reached through HugeintDivMod. +//! Takes no D_ASSERT for the same reason as HugeintLeftShift. +constexpr HugeintDivModResult HugeintDivModMinimum(hugeint_t lhs, hugeint_t rhs) { + // rhs == MINIMUM: the quotient is 1 with remainder 0 only when lhs == MINIMUM; + // every other lhs has |lhs| < |MINIMUM|, so the quotient is 0 and the remainder is lhs + if (rhs.upper == INT64_MIN && rhs.lower == 0) { + if (lhs.upper == INT64_MIN && lhs.lower == 0) { + return HugeintDivModResult {hugeint_t(1), hugeint_t(0)}; + } + return HugeintDivModResult {hugeint_t(0), lhs}; + } + + auto shifted = HugeintDivMod(HugeintAddOne(lhs), rhs); + auto quotient = shifted.quotient; + auto remainder = shifted.remainder; + + // If the 1 mattered we need to adjust the result, otherwise the remainder + if (HugeintEquals(HugeintAddOne(HugeintAbs(remainder)), HugeintAbs(rhs))) { + quotient = HugeintSign(rhs) > 0 ? HugeintSubtractOne(quotient) : HugeintAddOne(quotient); + remainder = hugeint_t(0); + } else { + remainder = HugeintSubtractOne(remainder); + } + return HugeintDivModResult {quotient, remainder}; +} + +//! Bit-at-a-time 128-bit division, adapted from +//! https://github.com/calccrypto/uint128_t/blob/master/uint128_t.cpp +constexpr HugeintDivModResult HugeintDivMod(hugeint_t lhs, hugeint_t rhs) { + if (HugeintEquals(rhs, hugeint_t(0))) { + return HugeintDivModResult {hugeint_t(0), lhs}; + } + + // MINIMUM cannot be negated, so it has to be handled before any sign is stripped + if (HugeintEquals(lhs, HugeintMinimum()) || HugeintEquals(rhs, HugeintMinimum())) { + return HugeintDivModMinimum(lhs, rhs); + } + + bool lhs_negative = lhs.upper < 0; + bool rhs_negative = rhs.upper < 0; + if (lhs_negative) { + lhs = HugeintNegate(lhs); + } + if (rhs_negative) { + rhs = HugeintNegate(rhs); + } + + hugeint_t quotient {0, 0}; + hugeint_t remainder {0, 0}; + + const uint8_t highest_bit_set = HugeintHighestBit(lhs); + for (uint8_t x = highest_bit_set; x > 0; x--) { + // left-shift the current quotient and remainder by 1 + quotient = HugeintLeftShift(quotient, 1); + remainder = HugeintLeftShift(remainder, 1); + + // we get the value of the bit at position X, where position 0 is the least-significant bit + if (HugeintIsBitSet(lhs, x - 1)) { + remainder.lower += 1; + if (remainder.lower == 0) { + remainder.upper += 1; + } + } + if (HugeintGreaterThanEquals(remainder, rhs)) { + // the remainder has passed the division multiplier: subtract the divisor, + // carrying a borrow out of lower, and add one to the quotient + const int64_t borrow = remainder.lower < rhs.lower ? 1 : 0; + remainder.lower -= rhs.lower; + remainder.upper -= rhs.upper + borrow; + quotient.lower += 1; + if (quotient.lower == 0) { + quotient.upper += 1; + } + } + } + if (lhs_negative ^ rhs_negative) { + quotient = HugeintNegate(quotient); + } + if (lhs_negative) { + remainder = HugeintNegate(remainder); + } + return HugeintDivModResult {quotient, remainder}; +} + +constexpr hugeint_t::hugeint_t(int64_t value) // NOLINT: Allow implicit conversion from `int64_t` + : lower(static_cast(value)), upper(value < 0 ? -1 : 0) { +} + +constexpr bool hugeint_t::operator==(const hugeint_t &rhs) const { + return HugeintEquals(*this, rhs); +} + +constexpr bool hugeint_t::operator!=(const hugeint_t &rhs) const { + return HugeintNotEquals(*this, rhs); +} + +constexpr bool hugeint_t::operator<(const hugeint_t &rhs) const { + return HugeintLessThan(*this, rhs); +} + +constexpr bool hugeint_t::operator<=(const hugeint_t &rhs) const { + return HugeintLessThanEquals(*this, rhs); +} + +constexpr bool hugeint_t::operator>(const hugeint_t &rhs) const { + return HugeintGreaterThan(*this, rhs); +} + +constexpr bool hugeint_t::operator>=(const hugeint_t &rhs) const { + return HugeintGreaterThanEquals(*this, rhs); +} + +constexpr hugeint_t hugeint_t::operator+(const hugeint_t &rhs) const { + return hugeint_t(static_cast(static_cast(upper) + static_cast(rhs.upper) + + ((lower + rhs.lower) < lower)), + lower + rhs.lower); +} + +constexpr hugeint_t hugeint_t::operator-(const hugeint_t &rhs) const { + return hugeint_t(static_cast(static_cast(upper) - static_cast(rhs.upper) - + ((lower - rhs.lower) > lower)), + lower - rhs.lower); +} + +constexpr hugeint_t hugeint_t::operator*(const hugeint_t &rhs) const { + return HugeintMultiplyUnchecked(*this, rhs); +} + +constexpr hugeint_t hugeint_t::operator/(const hugeint_t &rhs) const { + // this is the unchecked division: dividing by zero yields 0 and MINIMUM / -1 wraps, + // matching Hugeint::Divide. Callers that need overflow checks use TryDivMod. + return HugeintDivMod(*this, rhs).quotient; +} + +constexpr hugeint_t hugeint_t::operator-() const { + return HugeintNegate(*this); +} + } // namespace duckdb namespace std { diff --git a/src/duckdb/src/include/duckdb/common/identifier.hpp b/src/duckdb/src/include/duckdb/common/identifier.hpp index c0023b6c7..195c54154 100644 --- a/src/duckdb/src/include/duckdb/common/identifier.hpp +++ b/src/duckdb/src/include/duckdb/common/identifier.hpp @@ -93,6 +93,9 @@ class Identifier { string value; }; +//! Generate an internal name: the given prefix followed by a random UUID +Identifier GenerateInternalName(const char *prefix); + //! Equality (case-insensitive) DUCKDB_API bool operator==(const Identifier &a, const Identifier &b); DUCKDB_API bool operator==(const Identifier &a, const string &b); diff --git a/src/duckdb/src/include/duckdb/common/json_document.hpp b/src/duckdb/src/include/duckdb/common/json_document.hpp index 4e0cbefcc..44f12fe1e 100644 --- a/src/duckdb/src/include/duckdb/common/json_document.hpp +++ b/src/duckdb/src/include/duckdb/common/json_document.hpp @@ -80,6 +80,8 @@ class JSONValue { bool IsObject() const; //! Whether or not this is an integer (signed or unsigned) bool IsInteger() const; + //! Whether or not this is a number + bool IsNumber() const; //! Get the value as a string (only valid if IsString()) string GetString() const; @@ -91,6 +93,8 @@ class JSONValue { int64_t GetSignedInteger() const; //! Get the value as a double double GetDouble() const; + //! Get any number as a double + double GetNumber() const; //! Look up a member of an object by key - returns an invalid value if this is not an object or the key is absent JSONValue GetMember(const string &key) const; diff --git a/src/duckdb/src/include/duckdb/common/local_file_system.hpp b/src/duckdb/src/include/duckdb/common/local_file_system.hpp index 69107e428..d4f76a057 100644 --- a/src/duckdb/src/include/duckdb/common/local_file_system.hpp +++ b/src/duckdb/src/include/duckdb/common/local_file_system.hpp @@ -49,6 +49,8 @@ class LocalFileSystem : public FileSystem { FileType GetFileType(FileHandle &handle) override; //! Returns the file stats of the attached handle. FileMetadata Stats(FileHandle &handle) override; + optional GetStatsIfExists(const OpenFileInfo &file, + optional_ptr opener = nullptr) override; //! Truncate a file to a maximum size of new_size, new_size should be smaller than or equal to the current size of //! the file void Truncate(FileHandle &handle, int64_t new_size) override; diff --git a/src/duckdb/src/include/duckdb/common/logical_type_info.hpp b/src/duckdb/src/include/duckdb/common/logical_type_info.hpp new file mode 100644 index 000000000..ceb65bdd2 --- /dev/null +++ b/src/duckdb/src/include/duckdb/common/logical_type_info.hpp @@ -0,0 +1,233 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/common/logical_type_info.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/common.hpp" +#include "duckdb/common/types/vector.hpp" +#include "duckdb/common/extension_type_info.hpp" +#include "duckdb/common/types/geometry_crs.hpp" + +namespace duckdb { + +class ParsedExpression; + +struct DecimalTypeInfo : public LogicalTypeInfo { + DecimalTypeInfo(uint8_t width_p, uint8_t scale_p); + + uint8_t width; + uint8_t scale; + +public: + void Serialize(Serializer &serializer) const override; + static unique_ptr Deserialize(Deserializer &source); + unique_ptr Copy() const override; + +protected: + bool EqualsInternal(const LogicalTypeInfo *other_p) const override; + +private: + DecimalTypeInfo(); +}; + +struct StringTypeInfo : public LogicalTypeInfo { + explicit StringTypeInfo(string collation_p); + + string collation; + +public: + void Serialize(Serializer &serializer) const override; + static unique_ptr Deserialize(Deserializer &source); + unique_ptr Copy() const override; + +protected: + bool EqualsInternal(const LogicalTypeInfo *other_p) const override; + +private: + StringTypeInfo(); +}; + +struct ListTypeInfo : public LogicalTypeInfo { + explicit ListTypeInfo(LogicalType child_type_p); + + LogicalType child_type; + +public: + void Serialize(Serializer &serializer) const override; + static unique_ptr Deserialize(Deserializer &source); + unique_ptr Copy() const override; + +protected: + bool EqualsInternal(const LogicalTypeInfo *other_p) const override; + +private: + ListTypeInfo(); +}; + +struct StructTypeInfo : public LogicalTypeInfo { + explicit StructTypeInfo(child_list_t child_types_p); + + child_list_t child_types; + +public: + void Serialize(Serializer &serializer) const override; + static unique_ptr Deserialize(Deserializer &deserializer); + unique_ptr Copy() const override; + +protected: + bool EqualsInternal(const LogicalTypeInfo *other_p) const override; + +private: + StructTypeInfo(); +}; + +struct LegacyAggregateStateTypeInfo : public LogicalTypeInfo { +public: + void Serialize(Serializer &serializer) const override; + // Legacy deserialize method kept only for compatibility with old database files + static unique_ptr Deserialize(Deserializer &source); + + static unique_ptr LegacyDeserialize(); + +protected: + bool EqualsInternal(const LogicalTypeInfo *other_p) const override; + +private: + LegacyAggregateStateTypeInfo(); +}; + +// If this type is primarily stored in the catalog or not. Enums from Pandas/Factors are not in the catalog. +enum EnumDictType : uint8_t { INVALID = 0, VECTOR_DICT = 1 }; + +struct EnumTypeInfo : public LogicalTypeInfo { + explicit EnumTypeInfo(const Vector &values_insert_order_p, idx_t dict_size_p); + EnumTypeInfo(const EnumTypeInfo &) = delete; + EnumTypeInfo &operator=(const EnumTypeInfo &) = delete; + +public: + const EnumDictType &GetEnumDictType() const; + const Vector &GetValuesInsertOrder() const; + const idx_t &GetDictSize() const; + static PhysicalType DictType(idx_t size); + + static LogicalType CreateType(const Vector &ordered_data, idx_t size); + static unique_ptr CreateTypeInfo(const Vector &ordered_data, idx_t size); + + void Serialize(Serializer &serializer) const override; + static unique_ptr Deserialize(Deserializer &source); + unique_ptr Copy() const override; + +protected: + // Equalities are only used in enums with different catalog entries + bool EqualsInternal(const LogicalTypeInfo *other_p) const override; + + Vector values_insert_order; + +private: + EnumDictType dict_type; + idx_t dict_size; +}; + +struct ArrayTypeInfo : public LogicalTypeInfo { + LogicalType child_type; + uint32_t size; + explicit ArrayTypeInfo(LogicalType child_type_p, uint32_t size_p); + +public: + void Serialize(Serializer &serializer) const override; + static unique_ptr Deserialize(Deserializer &reader); + unique_ptr Copy() const override; + +protected: + bool EqualsInternal(const LogicalTypeInfo *other_p) const override; +}; + +struct AnyTypeInfo : public LogicalTypeInfo { + AnyTypeInfo(LogicalType target_type, idx_t cast_score); + + LogicalType target_type; + idx_t cast_score; + +public: + void Serialize(Serializer &serializer) const override; + static unique_ptr Deserialize(Deserializer &source); + unique_ptr Copy() const override; + +protected: + bool EqualsInternal(const LogicalTypeInfo *other_p) const override; + +private: + AnyTypeInfo(); +}; + +struct IntegerLiteralTypeInfo : public LogicalTypeInfo { + explicit IntegerLiteralTypeInfo(Value constant_value); + + Value constant_value; + +public: + void Serialize(Serializer &serializer) const override; + static unique_ptr Deserialize(Deserializer &source); + unique_ptr Copy() const override; + +protected: + bool EqualsInternal(const LogicalTypeInfo *other_p) const override; + +private: + IntegerLiteralTypeInfo(); +}; + +struct TemplateTypeInfo : public LogicalTypeInfo { + explicit TemplateTypeInfo(string name_p); + + // The name of the template, e.g. `T`, or `KEY_TYPE`. Used to distinguish between different template types within + // the same function. The binder tries to resolve all templates with the same name to the same concrete type. + string name; + +public: + void Serialize(Serializer &serializer) const override; + static unique_ptr Deserialize(Deserializer &source); + unique_ptr Copy() const override; + +protected: + bool EqualsInternal(const LogicalTypeInfo *other_p) const override; + TemplateTypeInfo(); +}; + +struct GeoTypeInfo : public LogicalTypeInfo { +public: + GeoTypeInfo(); + + void Serialize(Serializer &serializer) const override; + static unique_ptr Deserialize(Deserializer &source); + unique_ptr Copy() const override; + + // The Coordinate Reference System associated with this geometry type + CoordinateReferenceSystem crs; + +protected: + bool EqualsInternal(const LogicalTypeInfo *other_p) const override; +}; + +struct UnboundTypeInfo : public LogicalTypeInfo { + explicit UnboundTypeInfo(unique_ptr expr_p); + + unique_ptr expr; + + void Serialize(Serializer &serializer) const override; + static unique_ptr Deserialize(Deserializer &source); + unique_ptr Copy() const override; + +protected: + bool EqualsInternal(const LogicalTypeInfo *other_p) const override; + +private: + UnboundTypeInfo(); +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/extra_type_info/enum_type_info.hpp b/src/duckdb/src/include/duckdb/common/logical_type_info/enum_type_info.hpp similarity index 76% rename from src/duckdb/src/include/duckdb/common/extra_type_info/enum_type_info.hpp rename to src/duckdb/src/include/duckdb/common/logical_type_info/enum_type_info.hpp index 226113ea4..eedaaecb0 100644 --- a/src/duckdb/src/include/duckdb/common/extra_type_info/enum_type_info.hpp +++ b/src/duckdb/src/include/duckdb/common/logical_type_info/enum_type_info.hpp @@ -1,6 +1,7 @@ #pragma once -#include "duckdb/common/extra_type_info.hpp" +#include "duckdb/common/exception.hpp" +#include "duckdb/common/logical_type_info.hpp" #include "duckdb/common/serializer/deserializer.hpp" #include "duckdb/common/string_map_set.hpp" #include "duckdb/common/vector/string_vector.hpp" @@ -30,17 +31,22 @@ struct EnumTypeInfoTemplated : public EnumTypeInfo { } } - static shared_ptr Deserialize(Deserializer &deserializer, uint32_t size) { + static unique_ptr Deserialize(Deserializer &deserializer, uint32_t size) { Vector values_insert_order(LogicalType::VARCHAR, size); auto strings = FlatVector::ScatterWriter(values_insert_order); + idx_t read_count = 0; deserializer.ReadList(201, "values", [&](Deserializer::List &list, idx_t i) { if (i >= size) { - throw SerializationException("Failed to deserialize enum: string value out of range"); + throw SerializationException("Corrupted enum: string value index %llu exceeds enum size %u", i, size); } strings[i] = list.ReadElement(); + read_count++; }); - return make_shared_ptr(values_insert_order, size); + if (read_count != size) { + throw SerializationException("Corrupted enum: expected %u values, but found %llu", size, read_count); + } + return make_uniq(values_insert_order, size); } const string_map_t &GetValues() const { diff --git a/src/duckdb/src/include/duckdb/common/multi_file/base_file_reader.hpp b/src/duckdb/src/include/duckdb/common/multi_file/base_file_reader.hpp index 390803bdc..569d2f438 100644 --- a/src/duckdb/src/include/duckdb/common/multi_file/base_file_reader.hpp +++ b/src/duckdb/src/include/duckdb/common/multi_file/base_file_reader.hpp @@ -90,6 +90,9 @@ class BaseFileReader : public enable_shared_from_this { DUCKDB_API virtual shared_ptr GetUnionData(idx_t file_idx); //! Get statistics for a specific column DUCKDB_API virtual unique_ptr GetStatistics(ClientContext &context, const Identifier &name); + //! Get statistics for a virtual column the reader produces (e.g. the row number of every row within the file) + DUCKDB_API virtual unique_ptr GetVirtualColumnStatistics(ClientContext &context, + column_t virtual_column_id); //! Prepare reader for scanning DUCKDB_API virtual void PrepareReader(ClientContext &context, GlobalTableFunctionState &); //! Called after opening when the scan is driven by read-ahead, lets the reader pre-open scan resources diff --git a/src/duckdb/src/include/duckdb/common/multi_file/multi_file_data.hpp b/src/duckdb/src/include/duckdb/common/multi_file/multi_file_data.hpp index acb8e460c..208249d4a 100644 --- a/src/duckdb/src/include/duckdb/common/multi_file/multi_file_data.hpp +++ b/src/duckdb/src/include/duckdb/common/multi_file/multi_file_data.hpp @@ -26,6 +26,22 @@ class DeleteFilter { virtual idx_t Filter(row_t start_row_index, idx_t count, SelectionVector &result_sel) = 0; }; +//! A delete filter that forwards to one owned by somebody else - used to hand a reader the filter of the scan it is +//! part of, which outlives it +class BorrowedDeleteFilter : public DeleteFilter { +public: + explicit BorrowedDeleteFilter(DeleteFilter &filter_p) : filter(filter_p) { + } + +public: + idx_t Filter(row_t start_row_index, idx_t count, SelectionVector &result_sel) override { + return filter.Filter(start_row_index, count, result_sel); + } + +private: + DeleteFilter &filter; +}; + struct HivePartitioningIndex { HivePartitioningIndex(string value, idx_t index); @@ -38,7 +54,8 @@ struct HivePartitioningIndex { struct MultiFileColumnDefinition { public: - MultiFileColumnDefinition(const Identifier &name, const LogicalType &type) : name(name), type(type) { + MultiFileColumnDefinition(Identifier name_p, LogicalType type_p) + : name(std::move(name_p)), type(std::move(type_p)) { } MultiFileColumnDefinition(const char *name, const LogicalType &type) : name(name), type(type) { } @@ -74,6 +91,14 @@ struct MultiFileColumnDefinition { return result; } + //! The definition of a column, with a child for every nested field an identifier can be attached to: the fields of + //! a STRUCT, the elements of a LIST or ARRAY, the "key_value" entries of a MAP, and the members of a UNION + DUCKDB_API static MultiFileColumnDefinition CreateNested(const Identifier &name, const LogicalType &type); + //! The definition of the nested field a path of child indexes addresses, starting at this column: a STRUCT field by + //! its index, the elements of a LIST or ARRAY by 0, the keys of a MAP by 0 and its values by 1, and a UNION member + //! by its index. Requires a definition from CreateNested; throws when the path addresses no nested field + DUCKDB_API MultiFileColumnDefinition &ResolveChildPath(const vector &child_path); + static vector ColumnsFromNamesAndTypes(const vector &names, const vector &types) { vector columns; @@ -102,6 +127,9 @@ struct MultiFileColumnDefinition { return identifier.GetValue(); } + DUCKDB_API void Serialize(Serializer &serializer) const; + DUCKDB_API static MultiFileColumnDefinition Deserialize(Deserializer &deserializer); + string GetIdentifierName() const { if (identifier.IsNull()) { // No identifier was provided, assume the name as the identifier @@ -115,6 +143,10 @@ struct MultiFileColumnDefinition { Identifier name; LogicalType type; vector children; + //! Fallback when no file column/field matches this definition. A NULL fallback must be an explicit + //! ConstantExpression containing a typed NULL, rather than a nullptr. + //! With field-id mapping, nullptr requires a matching field; a missing field raises InvalidInputException. + //! With name mapping, nullptr rejects missing root columns but fills missing nested fields with NULL. unique_ptr default_expression; //! Either the field_id or the name to map on diff --git a/src/duckdb/src/include/duckdb/common/multi_file/multi_file_function.hpp b/src/duckdb/src/include/duckdb/common/multi_file/multi_file_function.hpp index 2ffaf71c3..5a34fef5e 100644 --- a/src/duckdb/src/include/duckdb/common/multi_file/multi_file_function.hpp +++ b/src/duckdb/src/include/duckdb/common/multi_file/multi_file_function.hpp @@ -46,7 +46,7 @@ struct MultiFileReaderInterface { //! Combine the schemas of a set of files that were bound individually into a single schema //! The default implementation combines the return types of the files by name virtual void CombineSchemas(ClientContext &context, const vector> &union_data, - vector &return_types, vector &names); + bool union_by_name, vector &return_types, vector &names); virtual void FinalizeBindData(MultiFileBindData &multi_file_data); virtual void GetBindInfo(const TableFunctionData &bind_data, BindInfo &info); virtual optional_idx MaxThreads(const MultiFileBindData &bind_data_p, const MultiFileGlobalState &global_state, @@ -91,9 +91,10 @@ struct MultiFileReaderInterface { template class MultiFileFunction : public TableFunction { public: - explicit MultiFileFunction(Identifier name_p) - : TableFunction(std::move(name_p), {LogicalType::VARCHAR}, MultiFileScan, MultiFileBind, MultiFileInitGlobal, - MultiFileInitLocal) { + explicit MultiFileFunction( + Identifier name_p, MultiFileReader::MultiFileParameters parameters = MultiFileReader::MultiFileParameters::ALL) + : TableFunction(std::move(name_p), FunctionSignature().AddPositionalOnly("path", LogicalType::VARCHAR), + MultiFileScan, MultiFileBind, MultiFileInitGlobal, MultiFileInitLocal) { cardinality = MultiFileCardinality; table_scan_progress = MultiFileProgress; get_partition_data = MultiFileGetPartitionData; @@ -103,7 +104,7 @@ class MultiFileFunction : public TableFunction { get_partition_info = MultiFileGetPartitionInfo; get_virtual_columns = MultiFileGetVirtualColumns; get_metrics = MultiFileGetMetrics; - MultiFileReader::AddParameters(*this); + MultiFileReader::AddParameters(*this, parameters); } static bool IsEmptyResult(const MultiFileBindData &bind_data) { @@ -117,6 +118,7 @@ class MultiFileFunction : public TableFunction { MultiFileOptions file_options_p, unique_ptr options_p, unique_ptr interface_p) { + file_options_p.ValidateBoundSchema(); auto &interface = *interface_p; auto result = make_uniq(); @@ -136,7 +138,9 @@ class MultiFileFunction : public TableFunction { return std::move(result); } - if (result->file_list->IsEmpty() && !return_types.empty()) { + // NOTE: the types are checked first on purpose - asking a file list whether it is empty expands it, and a + // list that is built lazily (e.g. Iceberg's) is not ready to be expanded before its reader has bound + if (!return_types.empty() && result->file_list->IsEmpty()) { // restoring a serialized plan whose files were all pruned away by filter pushdown - there is no file // left to bind the readers on, but the schema is already known so we can use it as-is result->types = return_types; @@ -160,6 +164,16 @@ class MultiFileFunction : public TableFunction { interface.BindReader(context, result->types, result->names, *result); } interface.FinalizeBindData(*result); + if (result->file_options.file_row_number) { + // the column is read from the row number virtual column, which not every reader provides + virtual_column_map_t virtual_columns; + interface.GetVirtualColumns(context, *result, virtual_columns); + auto entry = virtual_columns.find(MultiFileReader::COLUMN_IDENTIFIER_FILE_ROW_NUMBER); + if (entry == virtual_columns.end()) { + throw BinderException("The file_row_number option is not supported by this reader"); + } + result->virtual_columns.insert(*entry); + } if (return_types.empty()) { // no expected types - just copy the types @@ -207,6 +221,7 @@ class MultiFileFunction : public TableFunction { result->table_columns = IdentifiersToStrings(names); } result->columns = MultiFileColumnDefinition::ColumnsFromNamesAndTypes(result->names, result->types); + result->file_options.VerifyColumnStatistics(result->names, result->types); return std::move(result); } @@ -279,6 +294,9 @@ class MultiFileFunction : public TableFunction { file_options.auto_detect_hive_partitioning = false; for (auto &[option_name, option_values] : input.info.options) { + if (multi_file_reader->ParseCopyOption(option_name, option_values, file_options)) { + continue; + } if (interface->ParseCopyOption(context, option_name, option_values, *options, expected_names, expected_types)) { continue; @@ -408,6 +426,15 @@ class MultiFileFunction : public TableFunction { return OpenMarkedFile(context, bind_data, global_state, current_reader_data, current_file_index, parallel_lock); } + //! Record an error of an async file open. Read-ahead is optional, and a scan drains the opens it scheduled + //! before it goes away - without a read-ahead to report to, the error only marks the scan as failed + static void PushAsyncOpenError(MultiFileGlobalState &gstate, ErrorData error) { + if (gstate.read_ahead) { + gstate.read_ahead->PushError(std::move(error)); + } + gstate.error_opening_file = true; + } + //! Open a file on the read-ahead async pool. Runs off the operator thread; records errors instead of throwing. static void OpenMarkedFileAsync(ClientContext &context, const MultiFileBindData &bind_data, MultiFileGlobalState &gstate, MultiFileReaderData &reader_data, idx_t file_index) { @@ -419,15 +446,14 @@ class MultiFileFunction : public TableFunction { if (!parallel_lock.owns_lock()) { parallel_lock.lock(); } - gstate.read_ahead->PushError(ErrorData(ex)); - gstate.error_opening_file = true; + PushAsyncOpenError(gstate, ErrorData(ex)); } catch (...) { // LCOV_EXCL_START if (!parallel_lock.owns_lock()) { parallel_lock.lock(); } - gstate.read_ahead->PushError(ErrorData("Unknown exception while opening a file")); - gstate.error_opening_file = true; + PushAsyncOpenError(gstate, ErrorData("Unknown exception while opening a file")); } // LCOV_EXCL_STOP + gstate.async_open_settled.notify_all(); } //! Schedule async opens for upcoming unopened files on the async pool, ahead of decoding. Lock held on entry. @@ -452,9 +478,15 @@ class MultiFileFunction : public TableFunction { reader_data.file_state = MultiFileFileState::OPENING; { MultiFileReaderData *reader_ptr = &reader_data; - read_ahead.ScheduleFileOpen([&context, &bind_data, &gstate, reader_ptr, current_file_index]() { - OpenMarkedFileAsync(context, bind_data, gstate, *reader_ptr, current_file_index); - }); + read_ahead.ScheduleFileOpen( + [&context, &bind_data, &gstate, reader_ptr, current_file_index]() { + OpenMarkedFileAsync(context, bind_data, gstate, *reader_ptr, current_file_index); + }, + [&gstate]() { + // the reader stays in OPENING, so tell every waiter to stop instead of polling forever + gstate.error_opening_file = true; + gstate.async_open_settled.notify_all(); + }); } progress_guaranteed = true; break; @@ -537,14 +569,23 @@ class MultiFileFunction : public TableFunction { unique_lock ¶llel_lock) { D_ASSERT(parallel_lock.owns_lock()); auto &read_ahead = *gstate.read_ahead; - while (HasFilesToRead(gstate, parallel_lock) && !gstate.error_opening_file && - gstate.readers[gstate.file_index]->file_state == MultiFileFileState::OPENING) { + auto still_opening = [&]() { + return HasFilesToRead(gstate, parallel_lock) && !gstate.error_opening_file && + gstate.readers[gstate.file_index]->file_state == MultiFileFileState::OPENING; + }; + while (still_opening()) { parallel_lock.unlock(); // the open may be queued behind other async work or the async pool may be gone, so run tasks inline - if (!read_ahead.TryRunPendingTask()) { - context.InterruptCheck(); - TaskScheduler::YieldThread(); + const bool ran_task = read_ahead.TryRunPendingTask(); + parallel_lock.lock(); + if (ran_task || !still_opening()) { + continue; } + // the open is in flight: sleep until it settles; the timeout bounds interrupt latency and + // covers a cancellation that signals without the lock + gstate.async_open_settled.wait_for(parallel_lock, std::chrono::milliseconds(10)); + parallel_lock.unlock(); + context.InterruptCheck(); parallel_lock.lock(); } } @@ -641,6 +682,10 @@ class MultiFileFunction : public TableFunction { while (true) { if (gstate.error_opening_file) { + // the flag only says a file failed, the error itself lives on the read-ahead - report before ending + if (gstate.read_ahead) { + gstate.read_ahead->ThrowIfError(); + } return MultiFileClaimResult::EXHAUSTED; } @@ -727,7 +772,9 @@ class MultiFileFunction : public TableFunction { result->job->scan_state = bind_data.interface->InitializeLocalState(context.client, *gstate.global_state); if (!ClaimNextJob(context.client, bind_data, gstate, *result->job)) { - return nullptr; + // keep the local state so the scan can still emit FinalizeScan output + result->job.reset(); + return std::move(result); } result->job_state = MultiFileJobState::SCHEDULE; return std::move(result); @@ -860,6 +907,12 @@ class MultiFileFunction : public TableFunction { auto &bind_data = input.bind_data->CastNoConst(); auto &data = input.local_state->Cast(); auto &gstate = input.global_state->Cast(); + if (data.finalize_batch_index.IsValid()) { + if (input.partition_info.RequiresPartitionColumns()) { + throw InternalException("Cannot get partition columns for FinalizeScan output"); + } + return OperatorPartitionData(data.finalize_batch_index.GetIndex()); + } auto &job = *data.job; OperatorPartitionData partition_data(job.batch_index); bind_data.multi_file_reader->GetPartitionData(context, bind_data.reader_bind, *job.reader_data, @@ -944,7 +997,7 @@ class MultiFileFunction : public TableFunction { MultiFileLocalState &lstate, MultiFileGlobalState &gstate, MultiFileBindData &bind_data) { if (lstate.job_state == MultiFileJobState::NONE) { - if (!ClaimNextJob(context, bind_data, gstate, *lstate.job)) { + if (!lstate.job || !ClaimNextJob(context, bind_data, gstate, *lstate.job)) { return ScanReadAheadAcquire::EXHAUSTED; } lstate.job_state = MultiFileJobState::SCHEDULE; @@ -993,13 +1046,6 @@ class MultiFileFunction : public TableFunction { static void MultiFileScan(ClientContext &context, TableFunctionInput &data_p, DataChunk &output) { if (!data_p.local_state) { - auto &gstate = data_p.global_state->Cast(); - auto &bind_data = data_p.bind_data->CastNoConst(); - if (gstate.global_state && bind_data.interface && - bind_data.interface->FinalizeScan(context, *gstate.global_state, output)) { - data_p.async_result = SourceResultType::HAVE_MORE_OUTPUT; - return; - } data_p.async_result = SourceResultType::FINISHED; return; } @@ -1017,6 +1063,11 @@ class MultiFileFunction : public TableFunction { return; case ScanReadAheadAcquire::EXHAUSTED: if (bind_data.interface->FinalizeScan(context, *gstate.global_state, output)) { + // finalized output has no job, give it its own batch index for GetPartitionData + if (!data.finalize_batch_index.IsValid()) { + lock_guard guard(gstate.lock); + data.finalize_batch_index = gstate.batch_index++; + } data_p.async_result = SourceResultType::HAVE_MORE_OUTPUT; return; } @@ -1049,6 +1100,23 @@ class MultiFileFunction : public TableFunction { static unique_ptr MultiFileScanStatsExtended(ClientContext &context, TableFunctionGetStatisticsInput &input) { + auto result = MultiFileScanStatsInternal(context, input); + if (!result) { + return nullptr; + } + auto &bind_data = input.bind_data->Cast(); + auto &column_index = input.column_index; + if (!column_index.IsVirtualColumn() && !column_index.IsPushdownExtract() && + result->GetType() != bind_data.types[column_index.GetPrimaryIndex()]) { + // the column is read as a type other than the one the file stores it as - the statistics of the file + // describe the stored type, so they say nothing about the column the scan produces + return nullptr; + } + return result; + } + + static unique_ptr MultiFileScanStatsInternal(ClientContext &context, + TableFunctionGetStatisticsInput &input) { auto &bind_data = input.bind_data->Cast(); auto &column_index = input.column_index; @@ -1063,6 +1131,15 @@ class MultiFileFunction : public TableFunction { auto primary_index = column_index.GetPrimaryIndex(); const auto &col_name = bind_data.names[primary_index]; + auto &file_row_number_idx = bind_data.reader_bind.file_row_number_idx; + if (file_row_number_idx.IsValid() && file_row_number_idx.GetIndex() == primary_index) { + // the column is read from the row number virtual column of the reader + if (bind_data.file_list->GetExpandResult() == FileExpandResult::MULTIPLE_FILES) { + return nullptr; + } + return bind_data.initial_reader->GetVirtualColumnStatistics( + context, MultiFileReader::COLUMN_IDENTIFIER_FILE_ROW_NUMBER); + } // a hive partitioning column overrides any file column of the same name - the statistics stored in the // file describe the overridden column and can even have a different type, so they cannot be used here @@ -1072,29 +1149,45 @@ class MultiFileFunction : public TableFunction { } } - // NOTE: we do not want to parse the file metadata for the sole purpose of getting column statistics - if (bind_data.file_list->GetExpandResult() == FileExpandResult::MULTIPLE_FILES) { - if (!bind_data.file_options.union_by_name) { - // multiple files, but no union_by_name: no luck! - return nullptr; - } - - auto merged_stats = bind_data.initial_reader->GetStatistics(context, col_name); - if (!merged_stats) { - return nullptr; + // statistics the caller gave for the column (the "column_statistics" option) stand in for the files' - + // they describe the scan as a whole, which is what the optimizer asks about, where a file's describe one file + if (!bind_data.file_options.column_statistics.IsNull()) { + auto explicit_stats = bind_data.file_options.GetColumnStatistics(col_name, bind_data.types[primary_index]); + if (explicit_stats) { + if (!column_index.IsPushdownExtract()) { + return explicit_stats; + } + auto storage_index = StorageIndex::FromColumnIndex(column_index); + return explicit_stats->PushdownExtract(storage_index.GetChildIndexes()[0]); } + } + if (!bind_data.file_options.bound_schema.empty() && bind_data.IsGeneratedColumn(primary_index)) { + // A physical shadow's bounds do not describe the generated path. Explicit scan statistics above still do. + return nullptr; + } + // NOTE: we do not want to parse the file metadata for the sole purpose of getting column statistics + const bool multiple_files = bind_data.file_list->GetExpandResult() == FileExpandResult::MULTIPLE_FILES; + if (multiple_files && !bind_data.file_options.union_by_name) { + // multiple files, but no union_by_name: no luck! + return nullptr; + } + auto result = bind_data.initial_reader->GetStatistics(context, col_name); + if (result && !bind_data.file_options.bound_schema.empty() && + !bind_data.MatchesPhysicalColumn(primary_index, col_name, result->GetType())) { + // Check the whole root before interpreting an extracted child's positional statistics. + return nullptr; + } + if (result && multiple_files) { for (idx_t i = 1; i < bind_data.union_readers.size(); i++) { auto &union_reader = *bind_data.union_readers[i]; auto stats = union_reader.GetStatistics(context, col_name); - if (!stats || merged_stats->GetType() != stats->GetType()) { + if (!stats || result->GetType() != stats->GetType()) { return nullptr; } - merged_stats->Merge(*stats); + result->Merge(*stats); } - return merged_stats; } - auto result = bind_data.initial_reader->GetStatistics(context, col_name); if (!result || !column_index.IsPushdownExtract()) { return result; } @@ -1139,6 +1232,9 @@ class MultiFileFunction : public TableFunction { if (reader_data.file_state == MultiFileFileState::OPEN) { // file is currently open - get the progress within the file progress_in_file = reader_data.reader->GetProgressInFile(context); + } else if (reader_data.file_state == MultiFileFileState::SKIPPED) { + // file was skipped (e.g. pruned by a filter) - there is nothing left to read + progress_in_file = 100.0; } else if (reader_data.file_state == MultiFileFileState::CLOSED) { // file has been closed - check if the reader is still in use auto reader = reader_data.closed_reader.lock(); @@ -1200,7 +1296,7 @@ class MultiFileFunction : public TableFunction { vector> &filters) { auto &data = bind_data_p->Cast(); - MultiFilePushdownInfo info(get); + MultiFilePushdownInfo info(get.table_index, data.names, get.GetColumnIds(), get.extra_info); auto new_list = data.multi_file_reader->ComplexFilterPushdown(context, *data.file_list, data.file_options, info, filters); @@ -1210,9 +1306,9 @@ class MultiFileFunction : public TableFunction { } } - static BindInfo MultiFileGetBindInfo(const optional_ptr bind_data_p) { + static BindInfo MultiFileGetBindInfo(TableFunctionGetBindInfoInput &input) { BindInfo bind_info(ScanType::EXTERNAL); - auto &bind_data = bind_data_p->Cast(); + auto &bind_data = input.bind_data->Cast(); vector file_path; for (const auto &file : bind_data.file_list->GetDisplayFileList()) { diff --git a/src/duckdb/src/include/duckdb/common/multi_file/multi_file_list.hpp b/src/duckdb/src/include/duckdb/common/multi_file/multi_file_list.hpp index ebb13701d..33e4d8953 100644 --- a/src/duckdb/src/include/duckdb/common/multi_file/multi_file_list.hpp +++ b/src/duckdb/src/include/duckdb/common/multi_file/multi_file_list.hpp @@ -73,7 +73,6 @@ class MultiFileListIterationHelper { }; struct MultiFilePushdownInfo { - explicit MultiFilePushdownInfo(LogicalGet &get); MultiFilePushdownInfo(TableIndex table_index, const vector &column_names, const vector &column_indexes, ExtraOperatorInfo &extra_info); @@ -111,7 +110,7 @@ class MultiFileList { vector> &filters) const; virtual unique_ptr DynamicFilterPushdown(MultiFileDynamicPushdownInfo &dynamic_pushdown_info) const; - virtual vector GetAllFiles() const = 0; + virtual vector GetAllFiles() const; virtual FileExpandResult GetExpandResult() const = 0; //! Get the total file count - forces all files to be expanded / known so the exact count can be computed virtual idx_t GetTotalFileCount() const = 0; diff --git a/src/duckdb/src/include/duckdb/common/multi_file/multi_file_options.hpp b/src/duckdb/src/include/duckdb/common/multi_file/multi_file_options.hpp index 40a6bbe1c..bacc4526d 100644 --- a/src/duckdb/src/include/duckdb/common/multi_file/multi_file_options.hpp +++ b/src/duckdb/src/include/duckdb/common/multi_file/multi_file_options.hpp @@ -11,8 +11,10 @@ #include "duckdb/common/case_insensitive_map.hpp" #include "duckdb/common/types.hpp" #include "duckdb/common/hive_partitioning.hpp" +#include "duckdb/common/multi_file/multi_file_data.hpp" namespace duckdb { +class BaseStatistics; struct BindInfo; class MultiFileList; @@ -28,7 +30,32 @@ struct MultiFileOptions { //! The maximum number of files that are opened to determine the schema - the schemas of the sampled files are //! combined into one. NumericLimits::Maximum() samples every file idx_t maximum_sample_files = 1; + //! Whether the schemas of the sampled files are combined into a union of their columns - files are then allowed + //! to be missing columns of the combined schema, the way they are with union_by_name. This is how the schemas of + //! sampled files are combined unless the reader combines them itself + bool sampled_schema_is_union = true; MultiFileColumnMappingMode mapping = MultiFileColumnMappingMode::BY_NAME; + //! (Optional) The schema of the scan, given by the "schema" option. Every file is mapped onto it - by field id + //! when its columns are identified by INTEGER, by name otherwise - instead of the schema being read from the files + vector schema; + //! (Optional) Complete ordered scan schema, mapped by name before conversion. Unlike schema, no defaults or + //! field identifiers are supplied. Physical reader columns remain independent of these output columns. + vector bound_schema; + //! Whether to add a "file_row_number" column with the row number of each row within its file - it is read from the + //! row number virtual column of the reader + bool file_row_number = false; + //! (Optional) Column statistics given by the "column_statistics" option: a MAP of column key to a MAP of statistic + //! name to its value as text - "min", "max", "count", "null_count" and "distinct_count" - the shape + //! COPY ... (RETURN_STATS) reports them in, keys included: a quoted column path, "s"."x" for a struct field, + //! "l"."element" for list elements, "m"."key" / "m"."value" for a map (an unquoted key is a plain column name). + //! The keys name the columns OF THE SCAN - what it produces after union_by_name, schema mapping or renames + //! across its files - not the columns of any one file. They stand in for the statistics the files would be + //! opened for, which lets a caller that knows the data (a catalog, a query coordinator) hand them to the + //! optimizer without any file being read for them - and lets a scan of many files have statistics at all. + //! NULL when not given. + //! They never set the scan's cardinality: that is the reader's estimate or, when given, explicit_cardinality. + //! A column's "count" only tells, against its "null_count", whether the column has any non-NULL values. + Value column_statistics; case_insensitive_map_t hive_types_schema; @@ -48,10 +75,26 @@ struct MultiFileOptions { DUCKDB_API LogicalType GetHiveLogicalType(const string &hive_partition_column) const; DUCKDB_API Value GetHivePartitionValue(const string &base, const string &entry, ClientContext &context) const; DUCKDB_API bool AnySet() const; + DUCKDB_API void SetBoundSchema(const Value &value, ClientContext &context); + //! Validate owned declarations before binding or restoring even an empty file list. + DUCKDB_API void ValidateBoundSchema() const; + //! Set "column_statistics" from an option value - see column_statistics for its shape + DUCKDB_API void SetColumnStatistics(const Identifier &key, const Value &val); + //! Check "column_statistics" against the scan's columns once they are bound: every key must name a column, or a + //! part of one, and every value must parse for it. Errors here, at bind, rather than when the optimizer asks. + DUCKDB_API void VerifyColumnStatistics(const vector &names, const vector &types) const; + //! The statistics the "column_statistics" option gives for a column of the given type, or nullptr when it says + //! nothing about it. The bounds are cast to the column's type; a value that does not cast is an error. + DUCKDB_API unique_ptr GetColumnStatistics(const Identifier &column_name, + const LogicalType &type) const; + //! Set "maximum_sample_files" from an option value - a positive count, or -1 for all files + DUCKDB_API void SetMaximumSampleFiles(const Identifier &key, const Value &val); + //! Set "maximum_sample_files" from an option value, returning false if it is not a valid count + DUCKDB_API bool TrySetMaximumSampleFiles(const Value &val); //! Whether the global schema is a union of the schemas of several files - individual files are then allowed to //! be missing columns that are present in the global schema bool SchemaIsUnion() const { - return union_by_name || maximum_sample_files > 1; + return union_by_name || (maximum_sample_files > 1 && sampled_schema_is_union); } }; diff --git a/src/duckdb/src/include/duckdb/common/multi_file/multi_file_reader.hpp b/src/duckdb/src/include/duckdb/common/multi_file/multi_file_reader.hpp index c60760b9e..0c03bfd79 100644 --- a/src/duckdb/src/include/duckdb/common/multi_file/multi_file_reader.hpp +++ b/src/duckdb/src/include/duckdb/common/multi_file/multi_file_reader.hpp @@ -101,13 +101,13 @@ struct MultiFileReader { // Reserved field id used for the "_last_updated_sequence_number" field according to the iceberg spec static constexpr int32_t LAST_UPDATED_SEQUENCE_NUMBER_ID = 2147483539; //! The field of a file STRUCT that holds the path of the file - all other fields are open options - static constexpr const char *FILE_PATH_FIELD = "filename"; + static constexpr const char *FILE_PATH_FIELD = OpenFileInfo::PATH_FIELD; public: virtual ~MultiFileReader(); //! Create a MultiFileReader for a specific TableFunction, using its function name for errors - DUCKDB_API static unique_ptr Create(const TableFunction &table_function); + DUCKDB_API static unique_ptr Create(const BoundTableFunction &table_function); //! Create a default MultiFileReader, function_name is used for errors DUCKDB_API static unique_ptr CreateDefault(const string &function_name = ""); @@ -115,7 +115,15 @@ struct MultiFileReader { static Value CreateValueFromFileList(const vector &files); //! Add the parameters for multi-file readers (e.g. union_by_name, filename) to a table function - DUCKDB_API static void AddParameters(TableFunction &table_function); + //! Which of the multi-file options to declare on a function. A function that reads exactly one file per call + //! takes only "allow_empty"; the others describe how several files are combined + enum class MultiFileParameters { ALL, ALLOW_EMPTY_ONLY }; + DUCKDB_API static void AddParameters(TableFunction &table_function, + MultiFileParameters which = MultiFileParameters::ALL); + //! Parse the value of the "schema" option - a MAP from field id (INTEGER) or name (VARCHAR) to a STRUCT(name, + //! type, default_value[, children]) describing a column + DUCKDB_API static vector ParseSchemaOption(ClientContext &context, + const Value &schema_value); //! Creates a table function set from a single reader function (including e.g. list parameters, etc) DUCKDB_API static TableFunctionSet CreateFunctionSet(TableFunction table_function); @@ -139,6 +147,9 @@ struct MultiFileReader { const FileGlobInput &glob_input = FileGlobOptions::DISALLOW_EMPTY); //! Parse the named parameters of a multi-file reader + //! Parse an option of a COPY ... FROM that the multi-file reader handles itself + DUCKDB_API virtual bool ParseCopyOption(const Identifier &key, const vector &values, + MultiFileOptions &options); DUCKDB_API virtual bool ParseOption(const Identifier &key, const Value &val, MultiFileOptions &options, ClientContext &context); //! Perform filter pushdown into the MultiFileList. Returns a new MultiFileList if filters were pushed down @@ -150,7 +161,7 @@ struct MultiFileReader { MultiFileDynamicPushdownInfo &pushdown_info); //! Try to use the MultiFileReader for binding. Returns true if a bind could be made, returns false if the //! MultiFileReader can not perform the bind and binding should be performed on 1 or more files in the MultiFileList - //! directly. + //! directly. The default MultiFileReader binds when the "schema" option was given. DUCKDB_API virtual bool Bind(MultiFileOptions &options, MultiFileList &files, vector &return_types, vector &names, MultiFileReaderBindData &bind_data); //! Bind the options of the multi-file reader, potentially emitting any extra columns that are required diff --git a/src/duckdb/src/include/duckdb/common/multi_file/multi_file_states.hpp b/src/duckdb/src/include/duckdb/common/multi_file/multi_file_states.hpp index 8fb179deb..7ccf59ddd 100644 --- a/src/duckdb/src/include/duckdb/common/multi_file/multi_file_states.hpp +++ b/src/duckdb/src/include/duckdb/common/multi_file/multi_file_states.hpp @@ -10,6 +10,7 @@ #include "duckdb/common/multi_file/multi_file_data.hpp" #include "duckdb/common/atomic.hpp" +#include "duckdb/common/condition_variable.hpp" #include "duckdb/common/multi_file/multi_file_options.hpp" #include "duckdb/common/multi_file/base_file_reader.hpp" #include "duckdb/common/multi_file/multi_file_list.hpp" @@ -27,6 +28,8 @@ struct MultiFileReaderInterface; struct MultiFileReaderBindData { //! The (global) column id of the filename column (if any) optional_idx filename_idx; + //! The (global) column id of the file_row_number column (if any) - it is read from the row number virtual column + optional_idx file_row_number_idx; //! The set of hive partitioning indexes (if any) vector hive_partitioning_indexes; //! (optional) The schema set by the multi file reader @@ -100,6 +103,14 @@ struct MultiFileBindData : public TableFunctionData { } unique_ptr Copy() const override; + + //! Whether an output column is supplied by a filename, row-number or Hive partition role. + DUCKDB_API bool IsGeneratedColumn(idx_t column_idx) const; + //! Match a physical column to an output column without a generated override or conversion (including collations). + DUCKDB_API bool MatchesPhysicalColumn(idx_t column_idx, const Identifier &physical_name, + const LogicalType &physical_type) const; + //! Whether all physical roots have the output names and types at the same positions, without generated roles. + DUCKDB_API bool HasIdentityMapping(const vector &physical_columns) const; }; //! Per-file data for the multi file reader @@ -168,7 +179,10 @@ struct MultiFileGlobalState : public GlobalTableFunctionState { //! Lock mutable mutex lock; //! Signal to other threads that a file failed to open, letting every thread abort. - bool error_opening_file = false; + //! Atomic because a cancelled file open settles it while the scheduling thread may hold the lock. + atomic error_opening_file {false}; + //! Signalled when an async file open settles, waking the threads waiting for the front file + condition_variable async_open_settled; //! Index of file currently up for scanning atomic file_index; @@ -257,6 +271,8 @@ struct MultiFileLocalState : public LocalTableFunctionState { ExpressionExecutor executor; //! Number of rows scanned by this thread (for profiling) idx_t rows_scanned = 0; + //! FinalizeScan may have no job, here's a special batch index for it + optional_idx finalize_batch_index; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/multi_file/table_function_multi_file.hpp b/src/duckdb/src/include/duckdb/common/multi_file/table_function_multi_file.hpp index 64ff7760d..7691d6f6f 100644 --- a/src/duckdb/src/include/duckdb/common/multi_file/table_function_multi_file.hpp +++ b/src/duckdb/src/include/duckdb/common/multi_file/table_function_multi_file.hpp @@ -11,18 +11,150 @@ #include "duckdb/common/atomic.hpp" #include "duckdb/common/multi_file/multi_file_function.hpp" #include "duckdb/common/mutex.hpp" +#include "duckdb/common/once_flag.hpp" namespace duckdb { +//===--------------------------------------------------------------------===// +// How a single-file table function takes part in a multi-file scan +//===--------------------------------------------------------------------===// +//! What the multi-file scan tells the bind of the single-file function that reads one of its files - see +//! TableFunctionBindInput::multi_file_input. The file itself is the input of the bind: see GetFile +struct TableFunctionFileBindInput { + //! (Optional) The schema this bind is expected to produce, when the schema of the scan was already determined - + //! the bind should read the file using this schema instead of determining a schema of its own + optional_ptr> expected_names; + optional_ptr> expected_types; + //! (Optional) The bind data that determined the schema above, when it came from another bind of this same + //! function. This lets the bind read the file exactly the way the schema was determined, rather than deriving + //! that from the names and types alone + optional_ptr expected_bind_data; + //! The options of the scan. They tell the bind how its file is combined with the other files of the scan - e.g. + //! whether their columns are unified by name, in which case a type that could not be determined should be + //! reported as SQLNULL so the other files can determine it + optional_ptr multi_file_options; + //! Whether the scan reads several files. Options that describe the schema then describe the scan rather than + //! this one file, so the bind should not hold this file to them exactly + bool multi_file_scan = false; + //! Whether the file is only bound to determine the schema of the scan - it is not read with the resulting bind + //! data, so the bind should not keep resources around for scanning it + bool schema_only = false; + //! (Optional) The bind data an earlier bind of this same file produced, when it was bound before to determine + //! the schema of the scan. The bind can take whatever it read from the file from there again + optional_ptr file_bind_data; + + bool HasExpectedSchema() const { + return expected_names && expected_types; + } + //! The multi-file input of a bind - an empty one when the function is not bound as part of a multi-file scan + static const TableFunctionFileBindInput &Get(const TableFunctionBindInput &input) { + static const TableFunctionFileBindInput EMPTY; + return input.multi_file_input ? *input.multi_file_input : EMPTY; + } + //! The file a single-file function reads: its first input, which is the path of the file or a file struct + //! that also holds the options to open the file with (e.g. its size or encryption key) - see + //! OpenFileInfo::FromValue. A multi-file scan passes every file it reads this way + static OpenFileInfo GetFile(const TableFunctionBindInput &input) { + auto &file = input.inputs[0]; + auto type = file.type().id(); + if (type != LogicalTypeId::VARCHAR && type != LogicalTypeId::STRUCT && type != LogicalTypeId::VARIANT) { + throw BinderException("%s reads a single file - given as a VARCHAR path or as a file struct, not as %s", + input.table_function.GetName(), file.type().ToString()); + } + return OpenFileInfo::FromValue(file, input.table_function.GetName()); + } +}; + +//! What the multi-file scan tells the initialization of the single-file function that reads one of its files - see +//! TableFunctionInitInput::multi_file_input +struct TableFunctionFileInitInput { + //! (Optional) The types the columns must be produced as, when they differ from the types the function bound to. + //! Only set for functions that declare "supports_cast_map" - the function converts to these types while reading, + //! rather than having the conversion applied to its output + optional_ptr> cast_map; + //! (Optional) The index each of the filters has in the scan - a function that keeps state per filter across the + //! files of a scan (like an adaptive filter order) identifies them by these + optional_ptr> filter_global_indices; + //! (Optional) Expressions the function must evaluate on the columns of its file before the filters are applied - + //! used when a filter could not be expressed in the types the file stores + optional_ptr> expression_map; + //! (Optional) The rows that were deleted from this file, which the function must not produce. The scan keeps + //! ownership of the filter - it outlives the scan the function initializes. It is used (and updated) while the + //! file is read, so it is not part of what makes this input const + mutable optional_ptr deletion_filter; + //! (Optional) The virtual columns among the column indexes, as a map of the index they are projected in to the + //! virtual column id wanted there. A virtual column gets an index of its own, past the columns the function bound + optional_ptr> virtual_columns; + //! The index of the file this function reads within the scan, and the number of files the scan reads in total. + //! TableFunctionInitInput::op is the operator all those files are read for + optional_idx file_index; + idx_t file_count = 1; + + //! The multi-file input of an initialization - an empty one when the function is not read as part of a + //! multi-file scan + static const TableFunctionFileInitInput &Get(const TableFunctionInitInput &input) { + static const TableFunctionFileInitInput EMPTY; + return input.multi_file_input ? *input.multi_file_input : EMPTY; + } +}; + +//! Input for combining the schemas of several files that were bound individually into one schema +struct TableFunctionCombineSchemaInput { + TableFunctionCombineSchemaInput(const vector> &bind_data_p, bool union_by_name_p) + : bind_data(bind_data_p), union_by_name(union_by_name_p) { + } + + //! The bind data of each of the files whose schemas are being combined - in file order + const vector> &bind_data; + //! Whether the schemas are combined because of union_by_name - the files are then expected to have different + //! columns, which are unified by name. Otherwise the files are expected to have the same columns, and the + //! schemas are combined to determine the schema of the scan more accurately + bool union_by_name; +}; + +//! Claims the next batch for the given local state - returns false when there is nothing left to scan. +//! A function that implements this is scanned one batch at a time, rather than being run until it returns an empty +//! chunk. This lets the scan tell the batches apart, so that batches scanned in parallel can be put back in order +typedef bool (*table_function_claim_batch_t)(ClientContext &context, TableFunctionInput &input); +//! Prepares the batch a local state has just claimed - called without the lock of the scan held, so that preparing +//! batches is done in parallel. Only meaningful together with table_function_claim_batch_t +typedef void (*table_function_prepare_batch_t)(ClientContext &context, TableFunctionInput &input); +//! Called when a local state will not scan any more batches - lets the function release the resources of the batch +//! it scanned last. The counterpart of table_function_claim_batch_t +typedef void (*table_function_finish_batch_t)(ClientContext &context, TableFunctionInput &input); +//! Whether the scan of this function can be driven by read-ahead - batches are then claimed and have their I/O +//! scheduled ahead of being scanned. Only meaningful together with table_function_claim_batch_t +//! Bind data can be absent when the schema is supplied without opening a file, e.g. by a table format. +typedef bool (*table_function_supports_read_ahead_t)(optional_ptr bind_data); +//! Schedules the I/O needed by the batch a local state has claimed, so it can be loaded before it is scanned +typedef AsyncResult (*table_function_schedule_io_t)(ClientContext &context, TableFunctionInput &input); +//! Called on the read-ahead pool once the scan of this function has been initialized, before any batch is claimed. +//! Lets the function pre-open the resources its scan needs, so that claiming a batch does no I/O +typedef void (*table_function_prepare_read_ahead_t)(ClientContext &context, TableFunctionInput &input); +//! Combines the schemas of several individually bound files into one. The names and types are pre-filled with the +//! schemas of the files combined by name - the function can replace or adjust them. Returns the bind data describing +//! the combined schema, which is then handed to the bind of every file that is read - or nullptr when the files must +//! be bound individually and reconciled with the combined schema +typedef unique_ptr (*table_function_combine_schema_t)(ClientContext &context, + TableFunctionCombineSchemaInput &input, + vector &return_types, + vector &names); +//! The columns of the file a function reads, with their nested structure and identifiers. The files of a scan are +//! mapped onto one another with these, so reporting only names and types is not enough +typedef vector (*table_function_get_file_columns_t)(ClientContext &context, + const FunctionData &bind_data); + //! The options of a wrapped single-file table function - the named parameters that are forwarded to it as-is class TableFunctionFileReaderOptions : public BaseFileReaderOptions { public: - named_parameter_map_t named_parameters; + named_argument_map_t named_parameters; //! The schema the files are expected to produce - set by COPY, which takes its columns from the target table vector expected_names; vector expected_types; //! The bind data that determined the schema above, if it came from binding a file of this same scan shared_ptr schema_bind_data; + //! Whether the scan reads several files + bool multi_file_scan = false; }; //! Bind data of a multi-file function that wraps a single-file table function @@ -36,6 +168,13 @@ struct TableFunctionMultiFileData : public TableFunctionData { bool HasExpectedSchema() const { return !options.expected_names.empty(); } + + unique_ptr Copy() const override { + auto result = make_uniq(); + result->options = options; + result->cardinality = cardinality; + return std::move(result); + } }; //! Settings of a multi-file wrapper - how the wrapped single-file function is exposed as a multi-file function @@ -46,6 +185,29 @@ struct TableFunctionMultiFileSettings { string reader_type; //! How many files are sampled by default to determine the schema idx_t maximum_sample_files = 1; + //! The named parameter of the wrapped function that sets how many files are sampled (if it has one) + Identifier sample_files_parameter; + //! Whether the schemas of the sampled files are combined into a union of their columns - files are then allowed + //! to be missing columns of the combined schema + bool sampled_schema_is_union = true; + + //! How the wrapped function takes part in the scan - all optional, see the typedefs above + table_function_claim_batch_t claim_batch = nullptr; + table_function_prepare_batch_t prepare_batch = nullptr; + table_function_finish_batch_t finish_batch = nullptr; + table_function_supports_read_ahead_t supports_read_ahead = nullptr; + table_function_schedule_io_t schedule_io = nullptr; + table_function_prepare_read_ahead_t prepare_read_ahead = nullptr; + table_function_combine_schema_t combine_schema = nullptr; + table_function_get_file_columns_t get_file_columns = nullptr; + //! Whether one local state may be used to scan several files in turn - the scan then keeps the state it created + //! rather than making a new one per file, so that what the function learns while reading a file (like the order + //! its filters are best applied in) carries over to the next + bool reuses_local_state = false; + //! Whether the function can produce columns as a different type than it bound them - see + //! TableFunctionFileInitInput::cast_map. The function then reports the schema of its own file, and still + //! produces the types of the scan + bool supports_cast_map = false; }; //! The function info of a multi-file wrapper - holds the single-file function that is wrapped @@ -65,29 +227,41 @@ class TableFunctionUnionData : public BaseUnionData { } optional_idx cardinality; - //! The bind data of the wrapped function for this file - used to combine the schemas of several files + //! The bind data of the wrapped function for this file. It is used to combine the schemas of several files, and + //! kept afterwards so that the bind that reads the file can reuse what this one read from it shared_ptr bind_data; + //! The statistics callback of the wrapped function, which reads the statistics of this file from its bind data + table_statistics_t statistics = nullptr; optional_idx TryGetCardinalityEstimate() const override { return cardinality; } + unique_ptr GetStatistics(ClientContext &context, const Identifier &name) override; }; //! Reads a single file by binding and executing the wrapped table function over that file class TableFunctionFileReader : public BaseFileReader { public: - TableFunctionFileReader(TableFunction function, OpenFileInfo file, named_parameter_map_t named_parameters, - string reader_type); + TableFunctionFileReader(TableFunction function, OpenFileInfo file, named_argument_map_t named_parameters, + TableFunctionMultiFileSettings settings); ~TableFunctionFileReader() override; public: string GetReaderType() const override; + bool UseCastMap() const override; shared_ptr GetUnionData(idx_t file_idx) override; unique_ptr GetStatistics(ClientContext &context, const Identifier &name) override; + unique_ptr GetVirtualColumnStatistics(ClientContext &context, column_t virtual_column_id) override; void AddVirtualColumn(column_t virtual_column_id) override; void PrepareReader(ClientContext &context, GlobalTableFunctionState &gstate) override; + void PrepareReadAhead(ClientContext &context, GlobalTableFunctionState &gstate) override; + void FinishFile(ClientContext &context, GlobalTableFunctionState &gstate) override; bool TryInitializeScan(ClientContext &context, GlobalTableFunctionState &gstate, LocalTableFunctionState &lstate) override; + void PrepareScan(ClientContext &context, GlobalTableFunctionState &gstate, + LocalTableFunctionState &lstate) override; + AsyncResult ScheduleIO(ClientContext &context, GlobalTableFunctionState &gstate, + LocalTableFunctionState &lstate) override; AsyncResult Scan(ClientContext &context, GlobalTableFunctionState &gstate, LocalTableFunctionState &lstate, DataChunk &chunk) override; double GetProgressInFile(ClientContext &context) override; @@ -97,13 +271,19 @@ class TableFunctionFileReader : public BaseFileReader { void FinishBatch(ClientContext &context, LocalTableFunctionState &local_state); //! Bind the wrapped table function over this file - this sets up the columns of the reader. //! When the schema of the scan is known upfront, the file is bound against that schema - void BindFunction(ClientContext &context, const TableFunctionFileReaderOptions &options); + //! When schema_only is set the file is bound only to determine the schema of the scan and is not read with the + //! resulting bind data. file_bind_data is what an earlier bind of this same file produced, if there was one + void BindFunction(ClientContext &context, const TableFunctionFileReaderOptions &options, + const MultiFileOptions &file_options, bool schema_only = false, + optional_ptr file_bind_data = nullptr); //! The cardinality of this file (if the wrapped function can provide one) optional_idx GetCardinality() const { return cardinality; } //! The number of threads the wrapped function can use to scan this file - optional_idx MaxThreads(ClientContext &context); + optional_idx MaxThreads(ClientContext &context, GlobalTableFunctionState &gstate); + //! Collect what the wrapped function counted for this file since it was last asked into the state of the scan + void CollectMetrics(ClientContext &context, GlobalTableFunctionState &gstate); public: //! The wrapped single-file table function @@ -115,21 +295,43 @@ class TableFunctionFileReader : public BaseFileReader { vector types; //! The cardinality estimate of the wrapped function for this file (if it has one) optional_idx cardinality; + //! The virtual columns that are read, as a map of the index they are projected in to their virtual column id + unordered_map virtual_columns; + //! The key-value metadata the wrapped function reported for this file + InsertionOrderPreservingMap metadata; + //! The operator this file is scanned for, and the number of files that scan reads + optional_ptr scan_op; + idx_t scan_file_count = 1; + +public: + //! The state of the wrapped function for this file, if it has been initialized + optional_ptr GetFunctionState() { + return global_state.get(); + } + //! The wrapped single-file function + const TableFunction &GetFunction() const { + return function; + } private: - TableFunctionInitInput GetInitInput() const; + //! Take the operator and file count of the scan this file is read for from its state + void SetScanState(GlobalTableFunctionState &gstate); + //! The input the wrapped function is initialized with - "file_input" receives what the scan adds to it + TableFunctionInitInput GetInitInput(TableFunctionFileInitInput &file_input) const; //! Initialize the global state of the wrapped function (if it has not been initialized yet) void InitializeFunctionState(ClientContext &context); private: //! The named parameters that are passed on to the wrapped function - named_parameter_map_t named_parameters; - //! How the files are referred to in error messages - string reader_type; - //! Guards the initialization of the global state below - mutex lock; + named_argument_map_t named_parameters; + //! How the wrapped function takes part in the scan + TableFunctionMultiFileSettings settings; + //! Initializes the global state once per file reader + OnceFlag initialize_once; //! The global state of the wrapped function - shared by all threads scanning this file unique_ptr global_state; + //! The number of row groups to scan of this file that has been collected into the state of the scan + idx_t collected_file_total = 0; //! Set when the wrapped function has no local state - only a single thread can scan the file in that case atomic file_is_assigned; //! Set when the wrapped function has emitted its last chunk for this file @@ -137,7 +339,8 @@ class TableFunctionFileReader : public BaseFileReader { }; //! A MultiFileReaderInterface that turns a table function reading a single file into a multi-file table function. -//! The wrapped function must accept a single VARCHAR file path as its only positional parameter - all of its named +//! The wrapped function must accept the file as its only positional parameter: as ANY, to receive the file together +//! with the options to open it with (see TableFunctionFileBindInput::GetFile), or as a VARCHAR path - all of its named //! parameters are forwarded to it, and all multi-file options (union_by_name, hive partitioning, filename, ...) are //! provided by the multi-file framework on top of it. class TableFunctionMultiFileWrapper : public MultiFileReaderInterface { @@ -145,13 +348,27 @@ class TableFunctionMultiFileWrapper : public MultiFileReaderInterface { TableFunctionMultiFileWrapper(TableFunction function, TableFunctionMultiFileSettings settings); public: - //! Wrap a single-file table function into a multi-file table function + //! Wrap a single-file table function into a multi-file table function. + //! The wrapped function and the settings are kept in the result's "function_info", where the default bind reads + //! them. A format whose function is bound through a TableFunction other than this one - because a caller copies + //! it and puts info of its own in that slot, as DuckLake, Iceberg and Delta do with the parquet scan - passes a + //! "bind" of its own here instead, built on MultiFileBindWith. No info is then stored, leaving the slot free. static TableFunction CreateFunction(TableFunction single_file_function, Identifier name, - TableFunctionMultiFileSettings settings = TableFunctionMultiFileSettings()); + TableFunctionMultiFileSettings settings = TableFunctionMultiFileSettings(), + table_function_bind_t bind = nullptr); //! Wrap a single-file table function into a multi-file table function set (VARCHAR and LIST(VARCHAR) variants) static TableFunctionSet CreateFunctionSet(TableFunction single_file_function, Identifier name, - TableFunctionMultiFileSettings settings = TableFunctionMultiFileSettings()); + TableFunctionMultiFileSettings settings = TableFunctionMultiFileSettings(), + table_function_bind_t bind = nullptr); + + //! Bind a multi-file scan over the given single-file function. Use this to build a "bind" of your own when the + //! function may be bound through a TableFunction that is not the one this wrapper created - the wrapped + //! function is then taken from here rather than from the function being bound + static unique_ptr MultiFileBindWith(ClientContext &context, TableFunctionBindInput &input, + vector &return_types, vector &names, + TableFunction single_file_function, + TableFunctionMultiFileSettings settings); //! Binds a COPY ... FROM over the wrapped function - assign this to CopyFunction::copy_from_bind, together with //! the wrapped multi-file function as CopyFunction::copy_from_function @@ -159,6 +376,18 @@ class TableFunctionMultiFileWrapper : public MultiFileReaderInterface { vector &expected_names, vector &expected_types); + //! Binds a COPY ... FROM over the given single-file function. Use this to build a "copy_from_bind" of your own + //! when the multi-file function carries no info of ours - the counterpart of MultiFileBindWith + static unique_ptr MultiFileBindCopyWith(ClientContext &context, CopyFromFunctionBindInput &input, + vector &expected_names, + vector &expected_types, + TableFunction single_file_function, + TableFunctionMultiFileSettings settings); + + //! The row groups of a scan that reads a single file, as the wrapped function describes them - the files of a + //! scan over several files have not been opened at this point, so their row groups are unknown + static vector GetPartitionStats(ClientContext &context, GetPartitionStatsInput &input); + //! Only present to satisfy MultiFileFunction - the wrapper builds its interface from the function info instead static unique_ptr CreateInterface(ClientContext &context); @@ -179,15 +408,17 @@ class TableFunctionMultiFileWrapper : public MultiFileReaderInterface { const MultiFileGlobalState &global_state, FileExpandResult expand_result) override; void BindReader(ClientContext &context, vector &return_types, vector &names, MultiFileBindData &bind_data) override; - void CombineSchemas(ClientContext &context, const vector> &union_data, + void CombineSchemas(ClientContext &context, const vector> &union_data, bool union_by_name, vector &return_types, vector &names) override; void FinalizeBindData(MultiFileBindData &multi_file_data) override; + void GetVirtualColumns(ClientContext &context, MultiFileBindData &bind_data, virtual_column_map_t &result) override; unique_ptr InitializeGlobalState(ClientContext &context, MultiFileBindData &bind_data, MultiFileGlobalState &global_state) override; unique_ptr InitializeLocalState(ClientContext &context, GlobalTableFunctionState &global_state) override; void FinishReading(ClientContext &context, GlobalTableFunctionState &global_state, LocalTableFunctionState &local_state) override; + bool SupportsReadAhead(const MultiFileBindData &bind_data) const override; shared_ptr CreateReader(ClientContext &context, GlobalTableFunctionState &gstate, BaseUnionData &union_data, const MultiFileBindData &bind_data) override; shared_ptr CreateReader(ClientContext &context, GlobalTableFunctionState &gstate, @@ -204,6 +435,9 @@ class TableFunctionMultiFileWrapper : public MultiFileReaderInterface { private: //! Parse a named parameter of the wrapped function - returns false if the function has no such parameter bool ParseNamedParameter(const Identifier &key, const Value &val, TableFunctionFileReaderOptions &options) const; + //! The name and type of the wrapped function's option of that name or alias - a keyword-only parameter or an + //! option its "**kwargs" declares - or empty if it declares none + optional> GetDeclaredOption(const Identifier &key) const; //! Release the per-file bind data that is only needed while combining schemas static void ReleaseBindData(const vector> &union_data); diff --git a/src/duckdb/src/include/duckdb/common/mutex.hpp b/src/duckdb/src/include/duckdb/common/mutex.hpp index 11e3b9bd9..9d55a8141 100644 --- a/src/duckdb/src/include/duckdb/common/mutex.hpp +++ b/src/duckdb/src/include/duckdb/common/mutex.hpp @@ -34,6 +34,32 @@ class DUCKDB_CAPABILITY("mutex") annotated_mutex : public internal::mutex_impl_t } }; +// Annotated shared_mutex implementation. +class DUCKDB_CAPABILITY("mutex") annotated_shared_mutex : public internal::mutex_impl_t { +private: + using Impl = internal::mutex_impl_t; + +public: + void lock() DUCKDB_ACQUIRE() { + Impl::lock(); + } + void unlock() DUCKDB_RELEASE() { + Impl::unlock(); + } + bool try_lock() DUCKDB_TRY_ACQUIRE(true) { + return Impl::try_lock(); + } + void lock_shared() DUCKDB_ACQUIRE_SHARED() { + Impl::lock_shared(); + } + void unlock_shared() DUCKDB_RELEASE_SHARED() { + Impl::unlock_shared(); + } + bool try_lock_shared() DUCKDB_TRY_ACQUIRE_SHARED(true) { + return Impl::try_lock_shared(); + } +}; + // Annotated lock_guard implementation. template class DUCKDB_SCOPED_CAPABILITY annotated_lock_guard : public internal::lock_impl_t> { @@ -99,6 +125,50 @@ class DUCKDB_SCOPED_CAPABILITY annotated_unique_lock : public internal::lock_imp } }; +// Annotated shared_lock implementation. +template +class DUCKDB_SCOPED_CAPABILITY annotated_shared_lock : public internal::lock_impl_t> { +private: + using Impl = internal::lock_impl_t>; + +public: + annotated_shared_lock() = default; + explicit annotated_shared_lock(M &m) DUCKDB_ACQUIRE_SHARED(m) : Impl(m) { + } + annotated_shared_lock(M &m, std::defer_lock_t t) noexcept DUCKDB_EXCLUDES(m) : Impl(m, t) { + } + annotated_shared_lock(M &m, std::try_to_lock_t t) DUCKDB_TRY_ACQUIRE_SHARED(true, m) : Impl(m, t) { + } + annotated_shared_lock(M &m, std::adopt_lock_t t) DUCKDB_REQUIRES_SHARED(m) : Impl(m, t) { + } + + // Disable copy and enable move. + annotated_shared_lock(const annotated_shared_lock &) = delete; + annotated_shared_lock &operator=(const annotated_shared_lock &) = delete; + annotated_shared_lock(annotated_shared_lock &&) = default; + annotated_shared_lock &operator=(annotated_shared_lock &&) = default; + + ~annotated_shared_lock() DUCKDB_RELEASE() = default; + + void lock() DUCKDB_ACQUIRE_SHARED() { + Impl::lock(); + } + bool try_lock() DUCKDB_TRY_ACQUIRE_SHARED(true) { + return Impl::try_lock(); + } + template + bool try_lock_for(const std::chrono::duration &timeout) DUCKDB_TRY_ACQUIRE_SHARED(true) { + return Impl::try_lock_for(timeout); + } + template + bool try_lock_until(const std::chrono::time_point &timeout) DUCKDB_TRY_ACQUIRE_SHARED(true) { + return Impl::try_lock_until(timeout); + } + void unlock() DUCKDB_RELEASE() { + Impl::unlock(); + } +}; + // Unannotated mutex type, which is alias for STL ones. using mutex = std::mutex; template diff --git a/src/duckdb/src/include/duckdb/common/named_parameter_map.hpp b/src/duckdb/src/include/duckdb/common/named_parameter_map.hpp index 690eb6249..8ed134089 100644 --- a/src/duckdb/src/include/duckdb/common/named_parameter_map.hpp +++ b/src/duckdb/src/include/duckdb/common/named_parameter_map.hpp @@ -9,10 +9,14 @@ #pragma once #include "duckdb/common/case_insensitive_map.hpp" +#include "duckdb/common/identifier.hpp" +#include "duckdb/common/insertion_order_preserving_map.hpp" #include "duckdb/common/types.hpp" namespace duckdb { using named_parameter_type_map_t = identifier_map_t; using named_parameter_map_t = identifier_map_t; +//! The named arguments of a call, in the order they were passed +using named_argument_map_t = InsertionOrderPreservingMap>; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/once_flag.hpp b/src/duckdb/src/include/duckdb/common/once_flag.hpp new file mode 100644 index 000000000..0a1d4f356 --- /dev/null +++ b/src/duckdb/src/include/duckdb/common/once_flag.hpp @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/common/once_flag.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/atomic.hpp" +#include "duckdb/common/mutex.hpp" + +#include + +namespace duckdb { + +//! One-time initialization flag that stays usable when the callable throws. +//! std::call_once is built on pthread_once with libstdc++, which never resets the flag after an exception on +//! MinGW and under ThreadSanitizer, so every other caller then waits forever. +class OnceFlag { +public: + //! Runs func unless an earlier call completed; a throwing func leaves the flag unset so the next caller retries + template + void Call(FUNC &&func) { + if (done.load(std::memory_order_acquire)) { + return; + } + lock_guard guard(lock); + if (done.load(std::memory_order_relaxed)) { + return; + } + func(); + done.store(true, std::memory_order_release); + } + +private: + atomic done {false}; + mutex lock; +}; + +//! Drop-in replacement for std::call_once +template +void CallOnce(OnceFlag &flag, FUNC &&func) { + flag.Call(std::forward(func)); +} + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/open_file_info.hpp b/src/duckdb/src/include/duckdb/common/open_file_info.hpp index 6a8a3f256..ea4b85c89 100644 --- a/src/duckdb/src/include/duckdb/common/open_file_info.hpp +++ b/src/duckdb/src/include/duckdb/common/open_file_info.hpp @@ -9,6 +9,7 @@ #pragma once #include "duckdb/common/common.hpp" +#include "duckdb/common/identifier.hpp" #include "duckdb/common/shared_ptr.hpp" #include "duckdb/common/types/value.hpp" @@ -18,10 +19,6 @@ struct ExtendedOpenFileInfo { unordered_map options; public: - //! Set an option to a value that originates from the user, casting it to the type the option is read back - //! as and storing it under its canonical name. Throws when the value cannot be converted to that type. - //! Options the core does not know about are stored as-is, for extensions to interpret - DUCKDB_API void SetUserOption(const string &name, const Value &value); //! Read an option as a T - returns false when the option is not set, throws when it is set to a value //! that cannot be read as a T. Only the types specialized below are supported template @@ -31,12 +28,15 @@ struct ExtendedOpenFileInfo { //! A boolean option - anything that casts to BOOLEAN is accepted template <> DUCKDB_API bool ExtendedOpenFileInfo::TryGetOption(const string &name, bool &result) const; -//! A string option - VARCHAR and BLOB are both stored as a string and both are accepted +//! A string option - VARCHAR and BLOB are read back as-is, other values as their text representation template <> DUCKDB_API bool ExtendedOpenFileInfo::TryGetOption(const string &name, string &result) const; //! An unsigned integer option - anything that casts to UBIGINT is accepted template <> DUCKDB_API bool ExtendedOpenFileInfo::TryGetOption(const string &name, idx_t &result) const; +//! A timestamp option - anything that casts to TIMESTAMP is accepted +template <> +DUCKDB_API bool ExtendedOpenFileInfo::TryGetOption(const string &name, timestamp_t &result) const; struct OpenFileInfo { OpenFileInfo() = default; @@ -47,7 +47,17 @@ struct OpenFileInfo { string path; shared_ptr extended_info; + //! The field of a file struct that holds the path of the file + static constexpr const char *PATH_FIELD = "filename"; + public: + //! The file a value specifies: a VARCHAR path, or a STRUCT holding the path in its "filename" field and the + //! options to open the file with in its other fields (a VARIANT holding either is unpacked). Errors name the + //! function that was given the file + DUCKDB_API static OpenFileInfo FromValue(const Value &input, const Identifier &function_name); + //! The inverse of FromValue: the path as a VARCHAR when there are no options, a file struct otherwise + DUCKDB_API Value ToValue() const; + bool operator<(const OpenFileInfo &rhs) const { return path < rhs.path; } diff --git a/src/duckdb/src/include/duckdb/common/opener_file_system.hpp b/src/duckdb/src/include/duckdb/common/opener_file_system.hpp index 576e610c9..309d3a06d 100644 --- a/src/duckdb/src/include/duckdb/common/opener_file_system.hpp +++ b/src/duckdb/src/include/duckdb/common/opener_file_system.hpp @@ -145,6 +145,9 @@ class OpenerFileSystem : public FileSystem { void MoveFile(const string &source, const string &target) { MoveFile(source, target, nullptr); } + optional GetStatsIfExists(const OpenFileInfo &file) { + return GetStatsIfExists(file, nullptr); + } bool FileExists(const string &filename) { return FileExists(filename, nullptr); } @@ -175,6 +178,12 @@ class OpenerFileSystem : public FileSystem { return GetFileSystem().FileExists(filename, GetOpener()); } + optional GetStatsIfExists(const OpenFileInfo &file, optional_ptr opener) override { + VerifyNoOpener(opener); + VerifyCanAccessFile(file.path); + return GetFileSystem().GetStatsIfExists(file, GetOpener()); + } + bool IsPipe(const string &filename, optional_ptr opener) override { VerifyNoOpener(opener); return GetFileSystem().IsPipe(filename, GetOpener()); @@ -276,11 +285,7 @@ class OpenerFileSystem : public FileSystem { } unique_ptr GlobFilesExtended(const string &path, const FileGlobInput &input, - optional_ptr opener) override { - VerifyNoOpener(opener); - VerifyCanAccessFile(path); - return GetFileSystem().Glob(path, input, GetOpener()); - } + optional_ptr opener) override; bool SupportsGlobExtended() const override { return true; diff --git a/src/duckdb/src/include/duckdb/common/operator/aggregate_operators.hpp b/src/duckdb/src/include/duckdb/common/operator/aggregate_operators.hpp index e0da13d4f..05a403c9d 100644 --- a/src/duckdb/src/include/duckdb/common/operator/aggregate_operators.hpp +++ b/src/duckdb/src/include/duckdb/common/operator/aggregate_operators.hpp @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include #include "duckdb/common/operator/comparison_operators.hpp" @@ -29,6 +30,48 @@ struct Max { } }; +template +inline T MinFloatingPoint(T left, T right) { + if (std::isnan(left)) { + return right; + } + if (std::isnan(right)) { + return left; + } + return right > left ? left : right; +} + +template +inline T MaxFloatingPoint(T left, T right) { + if (std::isnan(right)) { + return right; + } + if (std::isnan(left)) { + return left; + } + return left > right ? left : right; +} + +template <> +inline float Min::Operation(float left, float right) { + return MinFloatingPoint(left, right); +} + +template <> +inline double Min::Operation(double left, double right) { + return MinFloatingPoint(left, right); +} + +template <> +inline float Max::Operation(float left, float right) { + return MaxFloatingPoint(left, right); +} + +template <> +inline double Max::Operation(double left, double right) { + return MaxFloatingPoint(left, right); +} + struct LogicalAnd { template static inline T Operation(T left, T right) { diff --git a/src/duckdb/src/include/duckdb/common/operator/cast_operators.hpp b/src/duckdb/src/include/duckdb/common/operator/cast_operators.hpp index 7bc95424a..111b4f9b1 100644 --- a/src/duckdb/src/include/duckdb/common/operator/cast_operators.hpp +++ b/src/duckdb/src/include/duckdb/common/operator/cast_operators.hpp @@ -23,6 +23,7 @@ #include "duckdb/common/types/vector.hpp" #include "duckdb/common/vector/string_vector.hpp" #include "duckdb/common/exception/conversion_exception.hpp" +#include "duckdb/common/string_util.hpp" namespace duckdb { struct CastParameters; @@ -69,6 +70,23 @@ static string CastExceptionText(SRC input) { " can't be cast to the destination type " + TypeIdToString(GetTypeId()); } +template +static string CastExceptionText(SRC input, const LogicalType &target_type) { + if constexpr (std::is_same::value) { + return StringUtil::Format("Could not convert string '%s' to %s", ConvertToString::Operation(input), + target_type.ToString()); + } + return StringUtil::Format("Type %s with value %s can't be cast to the destination type %s", + TypeIdToString(GetTypeId()), ConvertToString::Operation(input), + target_type.ToString()); +} + +template +static string CastExceptionText(SRC input, const LogicalType &source_type, const LogicalType &target_type) { + return StringUtil::Format("Type %s with value %s can't be cast to the destination type %s", source_type.ToString(), + ConvertToString::Operation(input), target_type.ToString()); +} + struct Cast { template static inline DST Operation(SRC input) { @@ -672,6 +690,10 @@ DUCKDB_API bool TryCast::Operation(timestamp_ns_t input, timestamp_tz_ns_t &resu template <> DUCKDB_API bool TryCast::Operation(timestamp_tz_ns_t input, timestamp_ns_t &result, bool strict); template <> +DUCKDB_API bool TryCast::Operation(timestamp_tz_ns_t input, timestamp_ms_t &result, bool strict); +template <> +DUCKDB_API bool TryCast::Operation(timestamp_tz_ns_t input, timestamp_sec_t &result, bool strict); +template <> DUCKDB_API bool TryCast::Operation(timestamp_ms_t input, timestamp_sec_t &result, bool strict); template <> DUCKDB_API bool TryCast::Operation(timestamp_ms_t input, timestamp_ns_t &result, bool strict); @@ -891,14 +913,14 @@ struct CastFromBlob { template <> duckdb::string_t CastFromBlob::Operation(duckdb::string_t input, StringHeap &heap); -struct CastFromBlobToBit { - template - static inline string_t Operation(SRC input, StringHeap &heap) { +struct TryCastBlobToBit { + template + static inline bool Operation(SRC input, DST &result, Vector &result_vector, CastParameters ¶meters) { throw NotImplementedException("Cast from blob could not be performed!"); } }; template <> -string_t CastFromBlobToBit::Operation(string_t input, StringHeap &heap); +bool TryCastBlobToBit::Operation(string_t input, string_t &result, Vector &result_vector, CastParameters ¶meters); struct TryCastToBlob { template diff --git a/src/duckdb/src/include/duckdb/common/operator/decimal_cast_operators.hpp b/src/duckdb/src/include/duckdb/common/operator/decimal_cast_operators.hpp index 0f9b6a338..755515ff9 100644 --- a/src/duckdb/src/include/duckdb/common/operator/decimal_cast_operators.hpp +++ b/src/duckdb/src/include/duckdb/common/operator/decimal_cast_operators.hpp @@ -497,6 +497,17 @@ struct DecimalCastData { StoreType limit; }; +//! StoreType max/min divided by 10, for the overflow check in DecimalCastOperation::HandleDigit. +//! These are forced to be compile-time constants so the division is folded away: with a hugeint_t +//! store type it is a 128-bit division that would otherwise run for every digit. hugeint_t::operator/ +//! is constexpr, but it is too large to be inlined at the call site, so the compiler does not fold it +//! unless constant evaluation is actually required. +template +struct DecimalCastLimits { + static constexpr T MaximumDiv10 = NumericLimits::Maximum() / 10; + static constexpr T MinimumDiv10 = NumericLimits::Minimum() / 10; +}; + struct DecimalCastOperation { template static bool HandleDigit(T &state, uint8_t digit) { @@ -510,12 +521,12 @@ struct DecimalCastOperation { } state.digit_count++; if (NEGATIVE) { - if (state.result < (NumericLimits::Minimum() / 10)) { + if (state.result < DecimalCastLimits::MinimumDiv10) { return false; } state.result = state.result * 10 - digit; } else { - if (state.result > (NumericLimits::Maximum() / 10)) { + if (state.result > DecimalCastLimits::MaximumDiv10) { return false; } state.result = state.result * 10 + digit; diff --git a/src/duckdb/src/include/duckdb/common/operator/integer_cast_operator.hpp b/src/duckdb/src/include/duckdb/common/operator/integer_cast_operator.hpp index ccaef767c..4debd3d80 100644 --- a/src/duckdb/src/include/duckdb/common/operator/integer_cast_operator.hpp +++ b/src/duckdb/src/include/duckdb/common/operator/integer_cast_operator.hpp @@ -83,7 +83,7 @@ struct IntegerDecimalCastData { using StoreType = int64_t; StoreType result; StoreType decimal; - uint16_t decimal_digits; + idx_t decimal_digits; }; template <> @@ -92,7 +92,7 @@ struct IntegerDecimalCastData { using StoreType = uint64_t; StoreType result; StoreType decimal; - uint16_t decimal_digits; + idx_t decimal_digits; }; struct IntegerDecimalCastOperation : IntegerCastOperation { @@ -129,21 +129,23 @@ struct IntegerDecimalCastOperation : IntegerCastOperation { } // Handle decimals - e = UnsafeNumericCast(exponent - state.decimal_digits); + auto shift = static_cast(exponent) - static_cast(state.decimal_digits); store_t remainder = 0; - if (e < 0) { - if (static_cast(-e) <= NumericLimits::Digits()) { + if (shift < 0) { + if (static_cast(-shift) < NumericLimits::Digits()) { store_t power = 1; - while (e++ < 0) { + while (shift++ < 0) { power *= 10; } remainder = state.decimal % power; state.decimal /= power; } else { + // power would overflow and exceed decimal: all of decimal is fractional + remainder = state.decimal; state.decimal = 0; } } else { - while (e-- > 0) { + while (shift-- > 0) { if (!TryMultiplyOperator::Operation(state.decimal, (store_t)10, state.decimal)) { return false; } @@ -166,7 +168,10 @@ struct IntegerDecimalCastOperation : IntegerCastOperation { template static bool HandleDecimal(T &state, uint8_t digit) { using store_t = typename T::StoreType; - if (DUCKDB_UNLIKELY(state.decimal > (NumericLimits::Maximum() - digit) / 10)) { + // digits past this position cannot affect the result for any int16_t exponent + static constexpr uint16_t MAX_DECIMAL_DIGITS = -NumericLimits::Minimum(); + if (DUCKDB_UNLIKELY(state.decimal_digits == MAX_DECIMAL_DIGITS || + state.decimal > (NumericLimits::Maximum() - digit) / 10)) { // Simply ignore any more decimals return true; } @@ -261,6 +266,10 @@ static bool IntegerCastLoop(const char *buf, idx_t len, T &result, bool strict) } } if (StringUtil::CharacterIsSpace(buf[pos])) { + if (pos == start_pos) { + // a sign followed only by whitespace is not a number + return false; + } // skip any trailing spaces while (++pos < len) { if (!StringUtil::CharacterIsSpace(buf[pos])) { diff --git a/src/duckdb/src/include/duckdb/common/operator/interpolate.hpp b/src/duckdb/src/include/duckdb/common/operator/interpolate.hpp index 00e75039a..18d9b0f08 100644 --- a/src/duckdb/src/include/duckdb/common/operator/interpolate.hpp +++ b/src/duckdb/src/include/duckdb/common/operator/interpolate.hpp @@ -28,6 +28,8 @@ double InterpolateOperator::Operation(const double &lo, const double d, const do template <> dtime_t InterpolateOperator::Operation(const dtime_t &lo, const double d, const dtime_t &hi); template <> +dtime_tz_t InterpolateOperator::Operation(const dtime_tz_t &lo, const double d, const dtime_tz_t &hi); +template <> timestamp_t InterpolateOperator::Operation(const timestamp_t &lo, const double d, const timestamp_t &hi); template <> hugeint_t InterpolateOperator::Operation(const hugeint_t &lo, const double d, const hugeint_t &hi); diff --git a/src/duckdb/src/include/duckdb/common/operator/numeric_binary_operators.hpp b/src/duckdb/src/include/duckdb/common/operator/numeric_binary_operators.hpp index 380e667b6..a65cf052f 100644 --- a/src/duckdb/src/include/duckdb/common/operator/numeric_binary_operators.hpp +++ b/src/duckdb/src/include/duckdb/common/operator/numeric_binary_operators.hpp @@ -27,8 +27,10 @@ struct TryDivideOperator { static inline bool Operation(T left, T right, T &result) { D_ASSERT(right != 0); // this should be checked before! // with a non-zero divisor, division only overflows for the minimum value divided by -1 - if (left == NumericLimits::Minimum() && right == T(-1)) { - return false; + if constexpr (NumericLimits::IsSigned()) { + if (left == NumericLimits::Minimum() && right == T(-1)) { + return false; + } } result = DivideOperator::Operation(left, right); return true; diff --git a/src/duckdb/src/include/duckdb/common/optional_idx.hpp b/src/duckdb/src/include/duckdb/common/optional_idx.hpp index d2da740bb..540897cfe 100644 --- a/src/duckdb/src/include/duckdb/common/optional_idx.hpp +++ b/src/duckdb/src/include/duckdb/common/optional_idx.hpp @@ -9,19 +9,16 @@ #pragma once #include "duckdb/common/exception.hpp" +#include "duckdb/common/optional.hpp" namespace duckdb { class optional_idx { - static constexpr const idx_t INVALID_INDEX = idx_t(-1); - public: - optional_idx() : index(INVALID_INDEX) { + constexpr optional_idx() { } - optional_idx(idx_t index) : index(index) { // NOLINT: allow implicit conversion from idx_t - if (index == INVALID_INDEX) { - ThrowInvalidInitialization(); - } + // NOLINTNEXTLINE: allow implicit conversion from idx_t + constexpr optional_idx(idx_t index_p) : index(index_p) { } static optional_idx Invalid() { @@ -29,18 +26,18 @@ class optional_idx { } bool IsValid() const { - return index != INVALID_INDEX; + return index.has_value(); } void SetInvalid() { - index = INVALID_INDEX; + index.reset(); } idx_t GetIndex() const { - if (index == INVALID_INDEX) { + if (!index.has_value()) { ThrowNotSet(); } - return index; + return *index; } inline bool operator==(const optional_idx &rhs) const { @@ -52,12 +49,11 @@ class optional_idx { } private: - //! Kept out-of-line so that the throwing paths do not block inlining of the accessors - [[noreturn]] DUCKDB_API static void ThrowInvalidInitialization(); + //! Kept out-of-line so that the throwing path does not block inlining of the accessors [[noreturn]] DUCKDB_API static void ThrowNotSet(); private: - idx_t index; + optional index; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/progress_bar/unscented_kalman_filter.hpp b/src/duckdb/src/include/duckdb/common/progress_bar/unscented_kalman_filter.hpp deleted file mode 100644 index d53aba6ca..000000000 --- a/src/duckdb/src/include/duckdb/common/progress_bar/unscented_kalman_filter.hpp +++ /dev/null @@ -1,65 +0,0 @@ -//===----------------------------------------------------------------------===// -// DuckDB -// -// duckdb/common/progress_bar/unscented_kalman_filter.hpp -// -// -//===----------------------------------------------------------------------===// - -#pragma once - -#include "duckdb.hpp" -#include - -namespace duckdb { - -class UnscentedKalmanFilter { -private: - static constexpr size_t STATE_DIM = 2; // [progress, velocity] - static constexpr size_t OBS_DIM = 1; // [progress] - static constexpr size_t SIGMA_POINTS = 2 * STATE_DIM + 1; - - // UKF parameters - static constexpr double ALPHA = 0.044; - static constexpr double BETA = 1.0; - static constexpr double KAPPA = 0.0; - - double lambda; - vector wm, wc; // weights for mean and covariance - - // State: [progress (0-1), velocity (progress/second)] - vector x; // state estimate - vector> P; // covariance matrix - vector> Q; // process noise - vector> R; // measurement noise - - double last_time; - bool initialized; - double last_progress; - double scale_factor; - - // Helper functions - vector> MatrixSqrt(const vector> &mat); - vector> GenerateSigmaPoints(); - vector StateTransition(const vector &state, double dt); - vector MeasurementFunction(const vector &state); - -public: - UnscentedKalmanFilter(); - - void Update(double progress, double time); - - double GetEstimatedRemainingSeconds() const; - -private: - void Initialize(double initial_progress, double current_time); - void Predict(double current_time); - void UpdateInternal(double measured_progress); - - double GetProgress() const; - double GetVelocity() const; - double GetProgressVariance() const; - double GetVelocityVariance() const; -}; - -} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/query_parameters.hpp b/src/duckdb/src/include/duckdb/common/query_parameters.hpp deleted file mode 100644 index 19d0d9d8d..000000000 --- a/src/duckdb/src/include/duckdb/common/query_parameters.hpp +++ /dev/null @@ -1,33 +0,0 @@ -//===----------------------------------------------------------------------===// -// DuckDB -// -// duckdb/common/query_parameters.hpp -// -// -//===----------------------------------------------------------------------===// - -#pragma once - -#include "duckdb/common/common.hpp" - -namespace duckdb { - -enum class QueryResultOutputType : uint8_t { FORCE_MATERIALIZED, ALLOW_STREAMING }; - -enum class QueryResultMemoryType : uint8_t { IN_MEMORY, BUFFER_MANAGED }; - -struct QueryParameters { - QueryParameters() { - } - QueryParameters(bool allow_streaming) // NOLINT: allow implicit conversion - : output_type(allow_streaming ? QueryResultOutputType::ALLOW_STREAMING - : QueryResultOutputType::FORCE_MATERIALIZED) { - } - QueryParameters(QueryResultOutputType output_type) // NOLINT: allow implicit conversion - : output_type(output_type) { - } - QueryResultOutputType output_type = QueryResultOutputType::FORCE_MATERIALIZED; - QueryResultMemoryType memory_type = QueryResultMemoryType::IN_MEMORY; -}; - -} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/serializer/async_memory_governor.hpp b/src/duckdb/src/include/duckdb/common/serializer/async_memory_governor.hpp index fd4174356..44f31da40 100644 --- a/src/duckdb/src/include/duckdb/common/serializer/async_memory_governor.hpp +++ b/src/duckdb/src/include/duckdb/common/serializer/async_memory_governor.hpp @@ -21,6 +21,10 @@ struct ManagedAsyncMemoryConfig { static constexpr idx_t MAX_PENDING_BYTES_PER_THREAD = 64ULL * 1024ULL * 1024ULL; //! Minimum async reservation requested per regular execution thread. static constexpr idx_t MIN_PENDING_BYTES_PER_THREAD = 8ULL * 1024ULL * 1024ULL; + //! Maximum fraction of the memory limit the async backlog may claim. + static constexpr idx_t MAX_PENDING_MEMORY_LIMIT_DIVISOR = 8ULL; + //! Maximum fraction of the memory limit the minimum async reservation may claim. + static constexpr idx_t MIN_PENDING_MEMORY_LIMIT_DIVISOR = 16ULL; //! Below this reservation, do not retain an async backlog (behave close to synchronous draining). static constexpr idx_t MIN_RESERVATION_FOR_BACKLOG = 8ULL * 1024ULL * 1024ULL; }; diff --git a/src/duckdb/src/include/duckdb/common/serializer/serializer.hpp b/src/duckdb/src/include/duckdb/common/serializer/serializer.hpp index 6885aeb2a..ab043aa4f 100644 --- a/src/duckdb/src/include/duckdb/common/serializer/serializer.hpp +++ b/src/duckdb/src/include/duckdb/common/serializer/serializer.hpp @@ -197,6 +197,12 @@ class Serializer { WriteValue(ptr.get()); } + // Optional Pointer Ref + template + void WriteValue(const optional_ptr &ptr) { + WriteValue(ptr.get()); + } + // Pointer template void WriteValue(const T *ptr) { @@ -400,7 +406,15 @@ class Serializer { WriteValue(value.GetIndexUnsafe()); } void WriteValue(optional_idx value) { - WriteValue(value.IsValid() ? value.GetIndex() : DConstants::INVALID_INDEX); + if (!value.IsValid()) { + WriteValue(DConstants::INVALID_INDEX); + return; + } + // INVALID_INDEX marks an unset optional_idx in the serialized format + if (value.GetIndex() == DConstants::INVALID_INDEX) { + throw InternalException("Cannot serialize an optional_idx that is set to INVALID_INDEX"); + } + WriteValue(value.GetIndex()); } void WriteValue(PerColumnMetadataBlock value) { WriteValue(value.GetPacked()); diff --git a/src/duckdb/src/include/duckdb/common/shared_ptr_ipp.hpp b/src/duckdb/src/include/duckdb/common/shared_ptr_ipp.hpp index e60681c03..b313c233a 100644 --- a/src/duckdb/src/include/duckdb/common/shared_ptr_ipp.hpp +++ b/src/duckdb/src/include/duckdb/common/shared_ptr_ipp.hpp @@ -55,7 +55,7 @@ class shared_ptr { // NOLINT: invalid case style } // From raw pointer of type T with custom DELETER template - shared_ptr(T *ptr, DELETER deleter) : internal(ptr, deleter) { + shared_ptr(T *ptr, DELETER deleter) : internal(ptr, std::move(deleter)) { __enable_weak_this(internal.get(), internal.get()); } // Aliasing constructor: shares ownership information with ref but contains ptr instead @@ -92,7 +92,7 @@ class shared_ptr { // NOLINT: invalid case style } // Construct from std::shared_ptr - explicit shared_ptr(std::shared_ptr other) : internal(other) { + explicit shared_ptr(std::shared_ptr other) : internal(std::move(other)) { // FIXME: should we __enable_weak_this here? // *our* enable_shared_from_this hasn't initialized yet, so I think so? __enable_weak_this(internal.get(), internal.get()); @@ -100,7 +100,7 @@ class shared_ptr { // NOLINT: invalid case style // Construct from weak_ptr template - explicit shared_ptr(weak_ptr other) : internal(other.internal) { + explicit shared_ptr(const weak_ptr &other) : internal(other.internal) { } // Construct from unique_ptr, takes over ownership of the unique_ptr diff --git a/src/duckdb/src/include/duckdb/common/sorting/buffered_merge.hpp b/src/duckdb/src/include/duckdb/common/sorting/buffered_merge.hpp new file mode 100644 index 000000000..773357fdc --- /dev/null +++ b/src/duckdb/src/include/duckdb/common/sorting/buffered_merge.hpp @@ -0,0 +1,192 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/common/sorting/buffered_merge.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/algorithm.hpp" +#include "duckdb/common/helper.hpp" +#include "duckdb/common/typedefs.hpp" +#include "duckdb/common/allocator.hpp" +#include "duckdb/common/exception.hpp" + +namespace duckdb { + +//! Scratch buffer for merging sorted runs: the merge is correct without it, so the allocation must never fail a sort. +//! Starting at half the keys, the size is halved until the buffer manager can provide it (or it becomes too small to +//! be worth having). +class SortMergeBuffer { +public: + //! Number of keys the buffer holds at most: the smaller side of a merge is at most half the keys + static idx_t MaximumKeyCount(idx_t key_count) { + return key_count / 2 + 1; + } + //! Memory the buffer takes at most, so that it can be budgeted along with the keys it merges + static idx_t MaximumSize(idx_t key_count, idx_t key_size) { + return MaximumKeyCount(key_count) * key_size; + } + + SortMergeBuffer(Allocator &allocator, idx_t key_count, idx_t key_size) : count(0) { + static constexpr idx_t MINIMUM_KEYS = 1024; + for (idx_t keys = MaximumKeyCount(key_count); keys >= MINIMUM_KEYS; keys /= 2) { + try { + data = allocator.Allocate(keys * key_size); + count = keys; + return; + } catch (OutOfMemoryException &) { + // try a smaller buffer + } + } + } + + template + SORT_KEY *Keys() { + return reinterpret_cast(data.get()); + } + idx_t Count() const { + return count; + } + +private: + AllocatedData data; + idx_t count; +}; + +//! Rotates [first, last) so that [middle, last) comes before [first, middle) and returns the new middle. The smaller +//! of the two pieces goes through the scratch buffer when it fits, otherwise this is a plain std::rotate; neither +//! allocates. +template +ITERATOR BufferedRotate(ITERATOR first, ITERATOR middle, ITERATOR last, SORT_KEY *buffer, const idx_t buffer_count) { + const auto left_count = static_cast(middle - first); + const auto right_count = static_cast(last - middle); + if (left_count > right_count && right_count <= buffer_count) { + std::move(middle, last, buffer); + std::move_backward(first, middle, last); + std::move(buffer, buffer + right_count, first); + return first + right_count; + } + if (left_count <= buffer_count) { + std::move(first, middle, buffer); + std::move(middle, last, first); + std::move_backward(buffer, buffer + left_count, last); + return last - left_count; + } + return std::rotate(first, middle, last); +} + +//! Merges the sorted ranges [first, middle) and [middle, last) in place using the given scratch buffer, which may be +//! any size (including empty): nothing is allocated. When the smaller side fits in the buffer it is moved out and +//! merged back, forward when it is the left side and backward when it is the right side, which keeps equal keys in +//! order. Otherwise the larger side is split in half, the other side is split at the matching key by binary search, +//! the two middle pieces are rotated and both halves are merged recursively, until the pieces fit the buffer. Without a +//! buffer this is the O(n log n) in-place merge that std::inplace_merge uses when it cannot get memory, with a buffer +//! of half the keys it is a single linear pass, and anything in between makes progressively fewer passes. +template +void BufferedMerge(ITERATOR first, ITERATOR middle, ITERATOR last, const COMPARE &compare, SORT_KEY *buffer, + const idx_t buffer_count) { + while (true) { + const auto left_count = static_cast(middle - first); + const auto right_count = static_cast(last - middle); + if (left_count == 0 || right_count == 0) { + return; + } + // the ranges are already in order, nothing to merge + if (!compare(*middle, *(middle - 1))) { + return; + } + // one key on each side: swap them (the split below needs at least two keys on the larger side to progress) + if (left_count + right_count == 2) { + std::iter_swap(first, middle); + return; + } + if (left_count <= right_count && left_count <= buffer_count) { + // move the left side out and merge forward into [first, last) + std::move(first, middle, buffer); + auto buffer_end = buffer + left_count; + auto buffer_it = buffer; + auto right_it = middle; + auto out = first; + while (buffer_it != buffer_end && right_it != last) { + if (compare(*right_it, *buffer_it)) { + *out = std::move(*right_it); + ++right_it; + } else { + *out = std::move(*buffer_it); + ++buffer_it; + } + ++out; + } + std::move(buffer_it, buffer_end, out); + return; + } + if (right_count <= buffer_count) { + // move the right side out and merge backward into [first, last) + std::move(middle, last, buffer); + auto buffer_begin = buffer; + auto buffer_it = buffer + right_count; + auto left_it = middle; + auto out = last; + while (buffer_it != buffer_begin && left_it != first) { + --out; + if (compare(*(buffer_it - 1), *(left_it - 1))) { + --left_it; + *out = std::move(*left_it); + } else { + --buffer_it; + *out = std::move(*buffer_it); + } + } + // anything left in the buffer goes to the front of the remaining gap + std::move_backward(buffer_begin, buffer_it, out); + return; + } + // neither side fits: split the larger side in half and the other side at the matching key, so that + // everything in [first_cut, middle) is greater than or equal to everything in [middle, second_cut) ... + // (copies of existing iterators: a default-constructed block iterator has no state to iterate over) + ITERATOR first_cut = first; + ITERATOR second_cut = middle; + if (left_count > right_count) { + first_cut = first + left_count / 2; + second_cut = std::lower_bound(middle, last, *first_cut, compare); + } else { + second_cut = middle + right_count / 2; + first_cut = std::upper_bound(first, middle, *second_cut, compare); + } + // ... swap those two pieces, which leaves two independent merges on either side of the new middle + auto new_middle = BufferedRotate(first_cut, middle, second_cut, buffer, buffer_count); + // recurse into the smaller one and loop on the larger one, which bounds the recursion depth by log2(n) + if (new_middle - first < last - new_middle) { + BufferedMerge(first, first_cut, new_middle, compare, buffer, buffer_count); + first = new_middle; + middle = second_cut; + } else { + BufferedMerge(new_middle, second_cut, last, compare, buffer, buffer_count); + last = new_middle; + middle = first_cut; + } + } +} + +//! Merge policy for vergesort (and the sorts it calls into) that merges through a SortMergeBuffer, so that every run +//! merge of a sort, including the nested ones, uses the one buffer that was budgeted for it and nothing else +template +class BufferedMergePolicy { +public: + explicit BufferedMergePolicy(SortMergeBuffer &buffer) : keys(buffer.Keys()), count(buffer.Count()) { + } + + template + void operator()(ITERATOR first, ITERATOR middle, ITERATOR last, const COMPARE &compare) const { + BufferedMerge(first, middle, last, compare, keys, count); + } + +private: + SORT_KEY *keys; + idx_t count; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/sorting/sort.hpp b/src/duckdb/src/include/duckdb/common/sorting/sort.hpp index 254f36c8f..e7a21ad69 100644 --- a/src/duckdb/src/include/duckdb/common/sorting/sort.hpp +++ b/src/duckdb/src/include/duckdb/common/sorting/sort.hpp @@ -8,6 +8,7 @@ #pragma once +#include "duckdb/common/atomic.hpp" #include "duckdb/execution/physical_operator_states.hpp" #include "duckdb/execution/progress_data.hpp" #include "duckdb/common/sorting/sort_projection_column.hpp" @@ -82,6 +83,11 @@ class Sort { SinkFinalizeType Finalize(ClientContext &context, OperatorSinkFinalizeInput &input) const; ProgressData GetSinkProgress(ClientContext &context, GlobalSinkState &gstate, const ProgressData source_progress) const; + //! The number of tuples that have been sorted so far + idx_t GetSortedCount(GlobalSinkState &gstate) const; + //! Sink progress of a sort: half of the effort is sinking, half is sorting the sunk tuples + static ProgressData GetSinkProgress(const ProgressData &source_progress, idx_t sorted_count, idx_t sunk_count, + MonotonicProgress &monotonic_progress); public: //===--------------------------------------------------------------------===// diff --git a/src/duckdb/src/include/duckdb/common/sorting/sorted_run.hpp b/src/duckdb/src/include/duckdb/common/sorting/sorted_run.hpp index fedb2dba4..560c58078 100644 --- a/src/duckdb/src/include/duckdb/common/sorting/sorted_run.hpp +++ b/src/duckdb/src/include/duckdb/common/sorting/sorted_run.hpp @@ -58,6 +58,8 @@ class SortedRun { idx_t Count() const; //! Size of this sorted run idx_t SizeInBytes() const; + //! Size of this sorted run plus the merge buffer that Finalize allocates to sort it + idx_t SizeInBytesForSorting() const; private: mutex merger_global_state_lock; diff --git a/src/duckdb/src/include/duckdb/common/sorting/sorted_run_merger.hpp b/src/duckdb/src/include/duckdb/common/sorting/sorted_run_merger.hpp index 7ce6f763c..d4b7ee364 100644 --- a/src/duckdb/src/include/duckdb/common/sorting/sorted_run_merger.hpp +++ b/src/duckdb/src/include/duckdb/common/sorting/sorted_run_merger.hpp @@ -39,6 +39,8 @@ class SortedRunMerger { OperatorPartitionData GetPartitionData(ExecutionContext &context, DataChunk &chunk, GlobalSourceState &gstate, LocalSourceState &lstate, const OperatorPartitionInfo &partition_info) const; ProgressData GetProgress(ClientContext &context, GlobalSourceState &gstate) const; + //! Whether all partitions have been scanned + bool IsFinished(GlobalSourceState &gstate) const; public: //===--------------------------------------------------------------------===// diff --git a/src/duckdb/src/include/duckdb/common/stl_allocator.hpp b/src/duckdb/src/include/duckdb/common/stl_allocator.hpp new file mode 100644 index 000000000..a11937e8f --- /dev/null +++ b/src/duckdb/src/include/duckdb/common/stl_allocator.hpp @@ -0,0 +1,93 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/common/stl_allocator.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/allocator.hpp" +#include "duckdb/common/helper.hpp" + +#include + +namespace duckdb { + +//! An STL-compatible allocator that forwards to a DuckDB Allocator, e.g. the BufferAllocator so that the memory of a +//! standard container is tracked by the buffer manager. +template +class stl_allocator { // NOLINT: match stl case +public: + //! Typedefs + typedef T value_type; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef value_type &reference; + typedef value_type const &const_reference; + typedef value_type *pointer; + typedef value_type const *const_pointer; + + //! Propagation traits + using propagate_on_container_copy_assignment = std::true_type; + using propagate_on_container_move_assignment = std::true_type; + using propagate_on_container_swap = std::true_type; + using is_always_equal = std::false_type; + + //! Rebind + template + struct rebind { + using other = stl_allocator; + }; + +public: + stl_allocator(Allocator &allocator_p) noexcept // NOLINT: allow implicit conversion + : allocator(allocator_p) { + } + template + stl_allocator(const stl_allocator &other) noexcept // NOLINT: allow implicit conversion + : allocator(other.GetAllocator()) { + } + +public: + pointer allocate(size_type n) { // NOLINT: match stl case + return reinterpret_cast(allocator.get().AllocateData(n * sizeof(T))); + } + + void deallocate(pointer p, size_type n) noexcept { // NOLINT: match stl case + allocator.get().FreeData(reinterpret_cast(p), n * sizeof(T)); + } + + template + void construct(U *p, Args &&...args) { // NOLINT: match stl case + ::new (p) U(std::forward(args)...); + } + + template + void destroy(U *p) noexcept { // NOLINT: match stl case + p->~U(); + } + + size_type max_size() const noexcept { // NOLINT: match stl case + return std::numeric_limits::max() / sizeof(T); + } + +public: + Allocator &GetAllocator() const noexcept { + return allocator.get(); + } + + bool operator==(const stl_allocator &other) const noexcept { + return RefersToSameObject(allocator.get(), other.allocator.get()); + } + bool operator!=(const stl_allocator &other) const noexcept { + return !(*this == other); + } + +private: + //! Need to use std::reference_wrapper because "reference" is already a typedef + std::reference_wrapper allocator; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/string_util.hpp b/src/duckdb/src/include/duckdb/common/string_util.hpp index 9411a9178..037eada55 100644 --- a/src/duckdb/src/include/duckdb/common/string_util.hpp +++ b/src/duckdb/src/include/duckdb/common/string_util.hpp @@ -146,12 +146,29 @@ class StringUtil { //! Split the input string along a quote. Note that any escaping is NOT supported. DUCKDB_API static vector SplitWithQuote(const string &str, char delimiter = ',', char quote = '"'); + //! Read one quoted token with doubled-quote escaping. On failure, leave pos and result unchanged. + DUCKDB_API static bool TryParseQuotedString(const string &str, idx_t &pos, string &result, char quote = '"'); //! Join multiple strings into one string. Components are concatenated by the given separator DUCKDB_API static string Join(const vector &input, const string &separator); DUCKDB_API static string Join(const vector &input, const string &separator); DUCKDB_API static string Join(const set &input, const string &separator); + //! Join container elements transformed to strings using the given separator + template + static string Join(const CONTAINER &input, const string &separator, const FUNC &f) { + string result; + bool first = true; + for (const auto &entry : input) { + if (!first) { + result += separator; + } + result += f(entry); + first = false; + } + return result; + } + //! Encode special URL characters in a string DUCKDB_API static string URLEncode(const string &str, bool encode_slash = true); DUCKDB_API static idx_t URLEncodeSize(const char *input, idx_t input_size, bool encode_slash = true); @@ -181,7 +198,7 @@ class StringUtil { //! Join multiple items of container with given size, transformed to string //! using function, into one string using the given separator template - static string Join(const C &input, S count, const string &separator, FUNC f) { + static string Join(const C &input, S count, const string &separator, const FUNC &f) { // The result std::string result; @@ -243,7 +260,7 @@ class StringUtil { //! Format a string using printf semantics template - static string Format(const string fmt_str, ARGS... params) { + static string Format(const string &fmt_str, const ARGS &...params) { return Exception::ConstructMessage(fmt_str, params...); } @@ -347,6 +364,9 @@ class StringUtil { DUCKDB_API static string GetFileExtension(const string &file_name); DUCKDB_API static string GetFileStem(const string &file_name); DUCKDB_API static string GetFilePath(const string &file_path); + //! Replace the file name of a path, keeping its directory part as written (also for files directly below a + //! scheme root, where splitting into GetFilePath and JoinPath would not round-trip: "opfs://a" -> "opfs://b") + DUCKDB_API static string ReplaceFileName(const string &file_path, const string &file_name); struct EnumStringLiteral { uint32_t number; diff --git a/src/duckdb/src/include/duckdb/common/table_index.hpp b/src/duckdb/src/include/duckdb/common/table_index.hpp index c8ca62c7a..a8dc05dbc 100644 --- a/src/duckdb/src/include/duckdb/common/table_index.hpp +++ b/src/duckdb/src/include/duckdb/common/table_index.hpp @@ -8,40 +8,13 @@ #pragma once -#include "duckdb/common/constants.hpp" +#include "duckdb/common/typed_index.hpp" #include namespace duckdb { -struct TableIndex { - TableIndex() : index(DConstants::INVALID_INDEX) { - } - explicit TableIndex(idx_t index) : index(index) { - } - - idx_t index; - - inline bool operator==(const TableIndex &rhs) const { - return index == rhs.index; - }; - inline bool operator<(const TableIndex &rhs) const { - return index < rhs.index; - }; - bool operator!=(const TableIndex &other) const { - return !(*this == other); - } - bool operator>(const TableIndex &other) const { - return other < *this; - } - bool operator<=(const TableIndex &other) const { - return !(other < *this); - } - bool operator>=(const TableIndex &other) const { - return !(*this < other); - } - bool IsValid() const { - return index != DConstants::INVALID_INDEX; - } +struct TableIndex : public TypedIndex { + using TypedIndex::TypedIndex; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/thread_annotation/mutex.hpp b/src/duckdb/src/include/duckdb/common/thread_annotation/mutex.hpp index 5bc8261cc..61355a8ac 100644 --- a/src/duckdb/src/include/duckdb/common/thread_annotation/mutex.hpp +++ b/src/duckdb/src/include/duckdb/common/thread_annotation/mutex.hpp @@ -12,17 +12,21 @@ #pragma once #include +#include namespace duckdb { // Forward declaration for annotated mutex types. class annotated_mutex; +class annotated_shared_mutex; // Forward declaration for annotated lock types. template class annotated_unique_lock; template class annotated_lock_guard; +template +class annotated_shared_lock; namespace internal { @@ -40,6 +44,12 @@ struct standard_impl<::duckdb::annotated_mutex> { using type = std::mutex; }; +// Specialization for `std::shared_mutex`. +template <> +struct standard_impl<::duckdb::annotated_shared_mutex> { + using type = std::shared_mutex; +}; + // Type alias for lock types. template using mutex_impl_t = standard_impl_t; @@ -56,6 +66,12 @@ struct standard_impl<::duckdb::annotated_lock_guard> { using type = std::lock_guard>; }; +// Specialization for `std::shared_lock`. +template +struct standard_impl<::duckdb::annotated_shared_lock> { + using type = std::shared_lock>; +}; + template using lock_impl_t = standard_impl_t; diff --git a/src/duckdb/src/include/duckdb/common/tree_renderer/compact_tree_renderer.hpp b/src/duckdb/src/include/duckdb/common/tree_renderer/compact_tree_renderer.hpp new file mode 100644 index 000000000..d0492cbf6 --- /dev/null +++ b/src/duckdb/src/include/duckdb/common/tree_renderer/compact_tree_renderer.hpp @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/common/tree_renderer/compact_tree_renderer.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/tree_renderer.hpp" +#include "duckdb/common/render_tree.hpp" + +namespace duckdb { +class LogicalOperator; +class PhysicalOperator; +class Pipeline; + +//! Renders a plan as indented text with one operator per line: the name, the cardinality/timing in parentheses and +//! the operator's properties separated by semicolons. Meant for machine consumers (e.g. AI coding agents) that read +//! the plan as text, where the box-drawing tree is expensive and the JSON/YAML formats are verbose. +class CompactTreeRenderer : public TreeRenderer { +public: + explicit CompactTreeRenderer() { + } + ~CompactTreeRenderer() override { + } + +public: + string ToString(const LogicalOperator &op); + string ToString(const PhysicalOperator &op); + string ToString(const ProfilingNode &op); + string ToString(const Pipeline &op); + + //! A summary line (total time, bytes read/written) before the tree - also when there is no tree, e.g. a + //! count(*) answered from metadata + void RenderProfiler(const QueryProfiler &profiler, BaseTreeRenderer &ss) override; + + void Render(const LogicalOperator &op, BaseTreeRenderer &ss); + void Render(const PhysicalOperator &op, BaseTreeRenderer &ss); + void Render(const ProfilingNode &op, BaseTreeRenderer &ss) override; + void Render(const Pipeline &op, BaseTreeRenderer &ss); + + void ToStreamInternal(RenderTree &root, BaseTreeRenderer &ss) override; + bool UsesRawKeyNames() override { + return true; + } + +private: + void RenderRecursive(RenderTree &node, BaseTreeRenderer &ss, idx_t depth, idx_t x, idx_t y); +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/type_visitor.hpp b/src/duckdb/src/include/duckdb/common/type_visitor.hpp index d41cd2424..7d1e5329f 100644 --- a/src/duckdb/src/include/duckdb/common/type_visitor.hpp +++ b/src/duckdb/src/include/duckdb/common/type_visitor.hpp @@ -8,8 +8,12 @@ #pragma once +#include "duckdb/common/numeric_utils.hpp" +#include "duckdb/common/optional_idx.hpp" #include "duckdb/common/types.hpp" +#include + namespace duckdb { struct TypeVisitor { @@ -20,114 +24,135 @@ struct TypeVisitor { template static LogicalType VisitReplace(const LogicalType &type, F &&func); + +private: + struct VisitorFrame { + LogicalType original_type; + idx_t expected_children_count; + }; + + static idx_t PushChildren(const LogicalType &type, vector> &worklist); + static LogicalType PopType(vector &result_stack); + static LogicalType RebuildType(const VisitorFrame &frame, vector &result_stack); }; -template -inline LogicalType TypeVisitor::VisitReplace(const LogicalType &type, F &&func) { - switch (type.id()) { - case LogicalTypeId::STRUCT: { - if (!type.AuxInfo()) { - return func(type); - } - auto children = StructType::GetChildTypes(type); - for (auto &child : children) { - child.second = VisitReplace(child.second, func); - } - return func(LogicalType::STRUCT(children)); +inline idx_t TypeVisitor::PushChildren(const LogicalType &type, vector> &worklist) { + if (!type.HasParameters()) { + return 0; } - case LogicalTypeId::TUPLE: { - if (!type.AuxInfo()) { - return func(type); + + auto initial_size = worklist.size(); + switch (type.id()) { + case LogicalTypeId::STRUCT: + case LogicalTypeId::TUPLE: + for (const auto &child : StructType::GetChildTypes(type)) { + worklist.push_back(child.second); } - auto children = StructType::GetChildTypes(type); - for (auto &child : children) { - child.second = VisitReplace(child.second, func); + break; + case LogicalTypeId::UNION: + for (idx_t i = 0; i < UnionType::GetMemberCount(type); i++) { + worklist.push_back(UnionType::GetMemberType(type, i)); } - return func(LogicalType::TUPLE(children)); + break; + case LogicalTypeId::MAP: + worklist.push_back(MapType::KeyType(type)); + worklist.push_back(MapType::ValueType(type)); + break; + case LogicalTypeId::LIST: + worklist.push_back(ListType::GetChildType(type)); + break; + case LogicalTypeId::ARRAY: + worklist.push_back(ArrayType::GetChildType(type)); + break; + default: + break; } + return worklist.size() - initial_size; +} + +inline LogicalType TypeVisitor::PopType(vector &result_stack) { + auto type = std::move(result_stack.back()); + result_stack.pop_back(); + return type; +} + +inline LogicalType TypeVisitor::RebuildType(const VisitorFrame &frame, vector &result_stack) { + D_ASSERT(frame.expected_children_count > 0); + D_ASSERT(result_stack.size() >= frame.expected_children_count); + const auto &type = frame.original_type; + switch (type.id()) { + case LogicalTypeId::STRUCT: + case LogicalTypeId::TUPLE: case LogicalTypeId::UNION: { - if (!type.AuxInfo()) { - return func(type); + auto children = + type.id() == LogicalTypeId::UNION ? UnionType::CopyMemberTypes(type) : StructType::GetChildTypes(type); + for (idx_t i = children.size(); i > 0; i--) { + children[i - 1].second = PopType(result_stack); } - auto children = UnionType::CopyMemberTypes(type); - for (auto &child : children) { - child.second = VisitReplace(child.second, func); + if (type.id() == LogicalTypeId::STRUCT) { + return LogicalType::STRUCT(std::move(children)); } - return func(LogicalType::UNION(children)); - } - case LogicalTypeId::LIST: { - if (!type.AuxInfo()) { - return func(type); + if (type.id() == LogicalTypeId::TUPLE) { + return LogicalType::TUPLE(std::move(children)); } - const auto &child = ListType::GetChildType(type); - return func(LogicalType::LIST(VisitReplace(child, func))); - } - case LogicalTypeId::ARRAY: { - if (!type.AuxInfo()) { - return func(type); - } - const auto &child = ArrayType::GetChildType(type); - return func(LogicalType::ARRAY(VisitReplace(child, func), ArrayType::GetSize(type))); + return LogicalType::UNION(std::move(children)); } case LogicalTypeId::MAP: { - if (!type.AuxInfo()) { - return func(type); - } - const auto &key = MapType::KeyType(type); - const auto &value = MapType::ValueType(type); - return func(LogicalType::MAP(VisitReplace(key, func), VisitReplace(value, func))); + auto value_type = PopType(result_stack); + auto key_type = PopType(result_stack); + return LogicalType::MAP(std::move(key_type), std::move(value_type)); } + case LogicalTypeId::LIST: + return LogicalType::LIST(PopType(result_stack)); + case LogicalTypeId::ARRAY: + return LogicalType::ARRAY(PopType(result_stack), ArrayType::GetSize(type)); default: - return func(type); + throw InternalException("Cannot rebuild children of a non-nested type"); } } template -inline bool TypeVisitor::Contains(const LogicalType &type, F &&predicate) { - if (predicate(type)) { - return true; +inline LogicalType TypeVisitor::VisitReplace(const LogicalType &type, F &&func) { + vector postfix_stack; + vector> worklist {type}; + + while (!worklist.empty()) { + const auto ¤t = worklist.back().get(); + worklist.pop_back(); + auto child_count = PushChildren(current, worklist); + postfix_stack.push_back({current, child_count}); } - switch (type.id()) { - case LogicalTypeId::STRUCT: - case LogicalTypeId::TUPLE: { - if (!type.AuxInfo()) { - return false; - } - for (const auto &child : StructType::GetChildTypes(type)) { - if (Contains(child.second, predicate)) { - return true; - } + + vector result_stack; + while (!postfix_stack.empty()) { + auto frame = std::move(postfix_stack.back()); + postfix_stack.pop_back(); + + if (frame.expected_children_count == 0) { + result_stack.push_back(func(frame.original_type)); + } else { + result_stack.push_back(func(RebuildType(frame, result_stack))); } - return false; } - case LogicalTypeId::UNION: - if (!type.AuxInfo()) { - return false; - } - for (idx_t i = 0; i < UnionType::GetMemberCount(type); i++) { - if (Contains(UnionType::GetMemberType(type, i), predicate)) { - return true; - } - } - return false; - case LogicalTypeId::LIST: - if (!type.AuxInfo()) { - return false; - } - return Contains(ListType::GetChildType(type), predicate); - case LogicalTypeId::ARRAY: - if (!type.AuxInfo()) { - return false; - } - return Contains(ArrayType::GetChildType(type), predicate); - case LogicalTypeId::MAP: - if (!type.AuxInfo()) { - return false; + D_ASSERT(result_stack.size() == 1); + return result_stack.back(); +} + +template +inline bool TypeVisitor::Contains(const LogicalType &type, F &&predicate) { + vector> worklist {type}; + + while (!worklist.empty()) { + const auto ¤t = worklist.back().get(); + worklist.pop_back(); + + if (predicate(current)) { + return true; } - return Contains(MapType::KeyType(type), predicate) || Contains(MapType::ValueType(type), predicate); - default: - return false; + auto child_count = PushChildren(current, worklist); + std::reverse(worklist.end() - NumericCast(child_count), worklist.end()); } + return false; } inline bool TypeVisitor::Contains(const LogicalType &type, LogicalTypeId type_id) { diff --git a/src/duckdb/src/include/duckdb/common/typed_index.hpp b/src/duckdb/src/include/duckdb/common/typed_index.hpp new file mode 100644 index 000000000..55e96138d --- /dev/null +++ b/src/duckdb/src/include/duckdb/common/typed_index.hpp @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/common/typed_index.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/constants.hpp" + +namespace duckdb { + +//! CRTP base for strongly typed index wrappers +template +struct TypedIndex { + //! Public so derived types can inherit these via `using TypedIndex::TypedIndex` + TypedIndex() : index(DConstants::INVALID_INDEX) { // NOLINT(bugprone-crtp-constructor-accessibility) + } + explicit TypedIndex(idx_t index) : index(index) { // NOLINT(bugprone-crtp-constructor-accessibility) + } + + idx_t index; + + bool IsValid() const { + return index != DConstants::INVALID_INDEX; + } + + friend bool operator==(const T &lhs, const T &rhs) { + return lhs.index == rhs.index; + } + friend bool operator!=(const T &lhs, const T &rhs) { + return lhs.index != rhs.index; + } + friend bool operator<(const T &lhs, const T &rhs) { + return lhs.index < rhs.index; + } + friend bool operator>(const T &lhs, const T &rhs) { + return lhs.index > rhs.index; + } + friend bool operator<=(const T &lhs, const T &rhs) { + return lhs.index <= rhs.index; + } + friend bool operator>=(const T &lhs, const T &rhs) { + return lhs.index >= rhs.index; + } +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/types.hpp b/src/duckdb/src/include/duckdb/common/types.hpp index a1f029e13..68c96362d 100644 --- a/src/duckdb/src/include/duckdb/common/types.hpp +++ b/src/duckdb/src/include/duckdb/common/types.hpp @@ -13,6 +13,9 @@ #include "duckdb/common/identifier.hpp" #include "duckdb/common/vector.hpp" #include "duckdb/common/helper.hpp" +#include "duckdb/common/atomic.hpp" + +#include namespace duckdb { @@ -257,25 +260,122 @@ enum class LogicalTypeId : uint8_t { TUPLE = 110 /* unnamed struct - shares the physical representation of STRUCT */ }; -struct ExtraTypeInfo; struct ExtensionTypeInfo; +//! The kind of a LogicalTypeInfo +enum class LogicalTypeInfoType : uint8_t { + INVALID_TYPE_INFO = 0, + GENERIC_TYPE_INFO = 1, + DECIMAL_TYPE_INFO = 2, + STRING_TYPE_INFO = 3, + LIST_TYPE_INFO = 4, + STRUCT_TYPE_INFO = 5, + ENUM_TYPE_INFO = 6, + UNBOUND_TYPE_INFO = 7, + LEGACY_AGGREGATE_STATE_TYPE_INFO = 8, + ARRAY_TYPE_INFO = 9, + ANY_TYPE_INFO = 10, + INTEGER_LITERAL_TYPE_INFO = 11, + TEMPLATE_TYPE_INFO = 12, + GEO_TYPE_INFO = 13 +}; + +//! The intrusively ref-counted data behind a LogicalType. Types without parameters use an INVALID_TYPE_INFO. +//! Once adopted by a LogicalType the info is shared and only ever accessed as const. +struct LogicalTypeInfo { + explicit LogicalTypeInfo(LogicalTypeInfoType type) noexcept; + explicit LogicalTypeInfo(LogicalTypeInfoType type, string alias); + virtual ~LogicalTypeInfo(); + + // the 1-byte fields and the ref count are declared first so they pack into one word after the vtable pointer + const LogicalTypeInfoType type; + +private: + friend struct LogicalType; + //! The id and physical type of the LogicalType that owns this info - set when a LogicalType adopts it + LogicalTypeId id = LogicalTypeId::INVALID; + PhysicalType physical_type = PhysicalType::INVALID; + //! Immortal infos (e.g. the one behind LogicalType::INTEGER) are shared without ref-counting and never freed. + bool immortal = false; + //! Atomic intrusive ref-count. This comes after the other members to avoid extra padding. + mutable atomic ref_count {0}; + +public: + string alias; + unique_ptr extension_info; + +protected: + // copy constructor (protected) + LogicalTypeInfo(const LogicalTypeInfo &other); + +public: + LogicalTypeInfo &operator=(const LogicalTypeInfo &other) = delete; + + inline void AddRef() const { + if (immortal) { + return; + } + auto previous = ref_count.fetch_add(1, std::memory_order_relaxed); + D_ASSERT(previous < std::numeric_limits::max()); + (void)previous; + } + inline void Release() const { + if (immortal) { + return; + } + auto previous = ref_count.fetch_sub(1, std::memory_order_acq_rel); + D_ASSERT(previous > 0); + if (previous == 1) { + delete this; + } + } + + bool Equals(const LogicalTypeInfo &other) const; + + virtual void Serialize(Serializer &serializer) const; + static unique_ptr Deserialize(Deserializer &source); + //! Creates an unshared copy of the info - nested types are shared with the original + virtual unique_ptr Copy() const; + //! Copy the base fields (alias, extension info) into "target" - used by Copy implementations that + //! reconstruct the type info instead of copy-constructing it + void CopyBaseInfo(LogicalTypeInfo &target) const; + + template + TARGET &Cast() { + DynamicCastCheck(this); + return reinterpret_cast(*this); + } + template + const TARGET &Cast() const { + DynamicCastCheck(this); + return reinterpret_cast(*this); + } + +protected: + virtual bool EqualsInternal(const LogicalTypeInfo *other_p) const; +}; + struct LogicalType { DUCKDB_API LogicalType(); DUCKDB_API LogicalType(LogicalTypeId id); // NOLINT: Allow implicit conversion from `LogicalTypeId` - DUCKDB_API LogicalType(LogicalTypeId id, shared_ptr type_info); - DUCKDB_API LogicalType(const LogicalType &other); + DUCKDB_API LogicalType(LogicalTypeId id, unique_ptr type_info); + inline LogicalType(const LogicalType &other) : type_info_(other.type_info_) { + type_info_.get().AddRef(); + } DUCKDB_API LogicalType(LogicalType &&other) noexcept; - DUCKDB_API ~LogicalType(); + inline ~LogicalType() { + type_info_.get().Release(); + } inline LogicalTypeId id() const { // NOLINT: mimic std casing - return id_; + return type_info_.get().id; } inline PhysicalType InternalType() const { - return physical_type_; + return type_info_.get().physical_type; } - inline optional_ptr AuxInfo() const { + //! The type info - always present, of type INVALID_TYPE_INFO for types without parameters + inline const LogicalTypeInfo &GetTypeInfo() const { return type_info_.get(); } inline bool IsNested() const { @@ -292,38 +392,41 @@ struct LogicalType { return false; } inline bool IsUnknown() const { - return id_ == LogicalTypeId::UNKNOWN; + return id() == LogicalTypeId::UNKNOWN; } inline bool IsUnbound() const { - return id_ == LogicalTypeId::UNBOUND; + return id() == LogicalTypeId::UNBOUND; } - - //! Copies the logical type, making a new ExtraTypeInfo + //! Gives up this type's reference to its info without releasing it - the reference must be taken back with + //! AdoptTypeInfo. Used to hand types across an ABI boundary (e.g. the C API) without copying them. + DUCKDB_API const LogicalTypeInfo &ReleaseTypeInfo() &&; + //! Creates a type that takes over a reference given up by ReleaseTypeInfo + DUCKDB_API static LogicalType AdoptTypeInfo(const LogicalTypeInfo &type_info); + //! Whether this type carries the type info its id is parameterized by (e.g. the child type of a LIST). Does not + //! look at child types - false for a bare LogicalTypeId::LIST, true for LIST(ANY). + DUCKDB_API bool HasParameters() const; + + //! Copies the logical type, making a new (unshared) LogicalTypeInfo LogicalType Copy() const; - //! DeepCopy() will make a unique copy of any nested ExtraTypeInfo as well - LogicalType DeepCopy() const; bool EqualTypeInfo(const LogicalType &rhs) const; // copy assignment inline LogicalType &operator=(const LogicalType &other) { - if (this == &other) { - return *this; - } - id_ = other.id_; - physical_type_ = other.physical_type_; + other.type_info_.get().AddRef(); + type_info_.get().Release(); type_info_ = other.type_info_; return *this; } // move assignment inline LogicalType &operator=(LogicalType &&other) noexcept { - id_ = other.id_; - physical_type_ = other.physical_type_; std::swap(type_info_, other.type_info_); return *this; } DUCKDB_API bool operator==(const LogicalType &rhs) const; + //! Logical type equality including collations in this type and its children. + DUCKDB_API bool EqualsIncludingCollation(const LogicalType &rhs) const; inline bool operator!=(const LogicalType &rhs) const { return !(*this == rhs); } @@ -377,6 +480,10 @@ struct LogicalType { DUCKDB_API static LogicalType DefaultForceMaxLogicalType(const LogicalType &left, const LogicalType &right); //! Normalize a type - removing literals DUCKDB_API static LogicalType NormalizeType(const LogicalType &type); + //! Nested type children with Parquet style names + DUCKDB_API static child_list_t GetNamedChildTypes(const LogicalType &type); + //! Rebuild a nested type with new children + DUCKDB_API static LogicalType ConstructNestedType(const LogicalType &type, child_list_t children); //! Gets the decimal properties of a numeric type. Fails if the type is not numeric. DUCKDB_API bool GetDecimalProperties(uint8_t &width, uint8_t &scale) const; @@ -394,12 +501,21 @@ struct LogicalType { bool SupportsRegularUpdate() const; private: - LogicalTypeId id_; // NOLINT: allow this naming for legacy reasons - PhysicalType physical_type_; // NOLINT: allow this naming for legacy reasons - shared_ptr type_info_; // NOLINT: allow this naming for legacy reasons + //! Takes over a reference to "type_info" + explicit LogicalType(const LogicalTypeInfo &type_info) : type_info_(type_info) { + } + + const_reference type_info_; // NOLINT: allow this naming for legacy reasons private: - PhysicalType GetInternalType(); + //! The immortal type info of a type without parameters - created once per id + DUCKDB_API static const LogicalTypeInfo &GetBuiltinTypeInfo(LogicalTypeId id); + //! The builtin info for an id, or nullptr for an invalid id - creating the builtins never allocates, so cannot fail + static const LogicalTypeInfo *TryGetBuiltinTypeInfo(LogicalTypeId id) noexcept; + //! Takes ownership of a new info for a type with the given id - or returns the builtin info if there is none + static const LogicalTypeInfo &AdoptNewTypeInfo(LogicalTypeId id, unique_ptr type_info); + //! Creates a type whose info is never ref-counted or freed - for types that are built once and shared + static LogicalType CreateImmortal(LogicalTypeId id, unique_ptr type_info); public: static constexpr const LogicalTypeId SQLNULL = LogicalTypeId::SQLNULL; @@ -443,19 +559,19 @@ struct LogicalType { static constexpr const LogicalTypeId ROW_TYPE = LogicalTypeId::BIGINT; // explicitly allowing these functions to be capitalized to be in-line with the remaining functions - DUCKDB_API static LogicalType DECIMAL(uint8_t width, uint8_t scale); // NOLINT - DUCKDB_API static LogicalType VARCHAR_COLLATION(string collation); // NOLINT - DUCKDB_API static LogicalType LIST(const LogicalType &child); // NOLINT - DUCKDB_API static LogicalType STRUCT(child_list_t children); // NOLINT + DUCKDB_API static LogicalType DECIMAL(uint8_t width, uint8_t scale); // NOLINT + DUCKDB_API static LogicalType VARCHAR_COLLATION(string collation); // NOLINT + DUCKDB_API static LogicalType LIST(const LogicalType &child); // NOLINT + DUCKDB_API static LogicalType STRUCT(child_list_t children); // NOLINT //! Unnamed struct - shares the physical representation of STRUCT but is a distinct logical type - DUCKDB_API static LogicalType TUPLE(child_list_t children); // NOLINT - DUCKDB_API static LogicalType TUPLE(vector children); // NOLINT - DUCKDB_API static LogicalType MAP(const LogicalType &child); // NOLINT - DUCKDB_API static LogicalType MAP(LogicalType key, LogicalType value); // NOLINT - DUCKDB_API static LogicalType UNION(child_list_t members); // NOLINT - DUCKDB_API static LogicalType ARRAY(const LogicalType &child, optional_idx index); // NOLINT - DUCKDB_API static LogicalType ENUM(const Vector &ordered_data, idx_t size); // NOLINT - DUCKDB_API static LogicalType GEOMETRY(); // NOLINT + DUCKDB_API static LogicalType TUPLE(child_list_t children); // NOLINT + DUCKDB_API static LogicalType TUPLE(vector children); // NOLINT + DUCKDB_API static LogicalType MAP(const LogicalType &child); // NOLINT + DUCKDB_API static LogicalType MAP(LogicalType key, LogicalType value); // NOLINT + DUCKDB_API static LogicalType UNION(child_list_t members); // NOLINT + DUCKDB_API static LogicalType ARRAY(const LogicalType &child, optional_idx index); // NOLINT + DUCKDB_API static LogicalType ENUM(const Vector &ordered_data, idx_t size); // NOLINT + DUCKDB_API static LogicalType GEOMETRY(); // NOLINT DUCKDB_API static LogicalType GEOMETRY(const string &crs); DUCKDB_API static LogicalType GEOMETRY(const CoordinateReferenceSystem &crs); // ANY but with special rules (default is LogicalType::ANY, 5) @@ -522,7 +638,10 @@ struct StructType { DUCKDB_API static const Identifier &GetChildName(const LogicalType &type, idx_t index); DUCKDB_API static idx_t GetChildIndexUnsafe(const LogicalType &type, const string &name); DUCKDB_API static idx_t GetChildCount(const LogicalType &type); + //! Whether the type is an unnamed struct, i.e. a TUPLE DUCKDB_API static bool IsUnnamed(const LogicalType &type); + //! Whether all (and at least one) of the children are unnamed - LogicalType::STRUCT turns these into a TUPLE + DUCKDB_API static bool AllUnnamed(const child_list_t &children); //! Whether the type is backed by StructTypeInfo and laid out as a struct (STRUCT or TUPLE) DUCKDB_API static bool IsStruct(const LogicalType &type); DUCKDB_API static bool IsStruct(LogicalTypeId id); diff --git a/src/duckdb/src/include/duckdb/common/types/arrow_string_view_type.hpp b/src/duckdb/src/include/duckdb/common/types/arrow_string_view_type.hpp index cf943612d..b1346c193 100644 --- a/src/duckdb/src/include/duckdb/common/types/arrow_string_view_type.hpp +++ b/src/duckdb/src/include/duckdb/common/types/arrow_string_view_type.hpp @@ -71,11 +71,11 @@ union arrow_string_view_t { const char *GetInlineData() const { return IsInline() ? inlined.data : ref.prefix; } - int32_t GetBufferIndex() { + int32_t GetBufferIndex() const { D_ASSERT(!IsInline()); return ref.buffer_index; } - int32_t GetOffset() { + int32_t GetOffset() const { D_ASSERT(!IsInline()); return ref.offset; } diff --git a/src/duckdb/src/include/duckdb/common/types/batched_data_collection.hpp b/src/duckdb/src/include/duckdb/common/types/batched_data_collection.hpp index d52852c89..9d04e268e 100644 --- a/src/duckdb/src/include/duckdb/common/types/batched_data_collection.hpp +++ b/src/duckdb/src/include/duckdb/common/types/batched_data_collection.hpp @@ -10,7 +10,6 @@ #include "duckdb/common/map.hpp" #include "duckdb/common/types/column/column_data_collection.hpp" -#include "duckdb/common/query_parameters.hpp" namespace duckdb { @@ -39,8 +38,6 @@ class BatchedDataCollection { ColumnDataAllocatorType allocator_type = ColumnDataAllocatorType::IN_MEMORY_ALLOCATOR, ColumnDataCollectionLifetime lifetime = ColumnDataCollectionLifetime::REGULAR); DUCKDB_API - BatchedDataCollection(ClientContext &context, vector types, QueryResultMemoryType memory_type); - DUCKDB_API BatchedDataCollection(ClientContext &context, vector types, batch_map_t batches, ColumnDataAllocatorType allocator_type = ColumnDataAllocatorType::IN_MEMORY_ALLOCATOR, ColumnDataCollectionLifetime lifetime = ColumnDataCollectionLifetime::REGULAR); diff --git a/src/duckdb/src/include/duckdb/common/types/cast_helpers.hpp b/src/duckdb/src/include/duckdb/common/types/cast_helpers.hpp index 2033079bc..2e14ed463 100644 --- a/src/duckdb/src/include/duckdb/common/types/cast_helpers.hpp +++ b/src/duckdb/src/include/duckdb/common/types/cast_helpers.hpp @@ -9,6 +9,7 @@ #pragma once #include "duckdb/common/common.hpp" +#include "duckdb/common/exception.hpp" #include "duckdb/common/numeric_utils.hpp" #include "duckdb/common/types/string_type.hpp" #include "duckdb/common/types/decimal.hpp" @@ -307,7 +308,9 @@ struct TimeToStringCast { } static void FormatTwoDigits(char *ptr, int32_t value) { - D_ASSERT(value >= 0 && value <= 99); + if (value < 0 || value > 99) { + throw InternalException("FormatTwoDigits called with out-of-range value %d", value); + } if (value < 10) { ptr[0] = '0'; ptr[1] = UnsafeNumericCast('0' + value); diff --git a/src/duckdb/src/include/duckdb/common/types/column/column_data_allocator.hpp b/src/duckdb/src/include/duckdb/common/types/column/column_data_allocator.hpp index 8ae321eb6..ef64a0df7 100644 --- a/src/duckdb/src/include/duckdb/common/types/column/column_data_allocator.hpp +++ b/src/duckdb/src/include/duckdb/common/types/column/column_data_allocator.hpp @@ -32,6 +32,7 @@ struct BlockMetaData { public: shared_ptr GetHandle() const; + BufferHandle Pin(BufferManager &buffer_manager); void SetHandle(ManagedResultSet &managed_result_set, shared_ptr handle); uint32_t Capacity(); }; diff --git a/src/duckdb/src/include/duckdb/common/types/column/column_data_collection.hpp b/src/duckdb/src/include/duckdb/common/types/column/column_data_collection.hpp index c9f7770db..cf1c59792 100644 --- a/src/duckdb/src/include/duckdb/common/types/column/column_data_collection.hpp +++ b/src/duckdb/src/include/duckdb/common/types/column/column_data_collection.hpp @@ -23,6 +23,7 @@ class ColumnDataRowCollection; //! The ColumnDataCollection represents a set of (buffer-managed) data stored in columnar format //! It is efficient to read and scan +//! A collection with no columns still stores row counts. class ColumnDataCollection { public: //! Constructs an in-memory column data collection from an allocator @@ -108,6 +109,8 @@ class ColumnDataCollection { //! Append a DataChunk directly to this ColumnDataCollection - calls InitializeAppend and Append internally DUCKDB_API void Append(DataChunk &new_chunk); + //! Appends a copy of another ColumnDataCollection to this + DUCKDB_API void Append(const ColumnDataCollection &other); //! Appends the other ColumnDataCollection to this, destroying the other data collection DUCKDB_API void Combine(ColumnDataCollection &other); @@ -143,6 +146,9 @@ class ColumnDataCollection { //! about performance DUCKDB_API ColumnDataRowCollection GetRows() const; + //! Materializes every row on each call, so read many values through GetRows instead + DUCKDB_API Value GetValue(idx_t column, idx_t index) const; + //! Compare two column data collections to another. If they are equal according to result equality rules, //! return true. That means null values are equal, and approx equality is used for floating point values. //! If they are not equal, return false and fill in the error message. diff --git a/src/duckdb/src/include/duckdb/common/types/column/column_data_collection_segment.hpp b/src/duckdb/src/include/duckdb/common/types/column/column_data_collection_segment.hpp index ee000c023..22a52980a 100644 --- a/src/duckdb/src/include/duckdb/common/types/column/column_data_collection_segment.hpp +++ b/src/duckdb/src/include/duckdb/common/types/column/column_data_collection_segment.hpp @@ -8,6 +8,7 @@ #pragma once +#include "duckdb/common/atomic.hpp" #include "duckdb/common/types/column/column_data_allocator.hpp" #include "duckdb/common/types/column/column_data_collection.hpp" @@ -39,10 +40,13 @@ struct SwizzleMetaData { SwizzleMetaData(VectorDataIndex child_index_p, data_ptr_t ptr_p, uint16_t offset_p, uint16_t count_p) : child_index(child_index_p), ptr(ptr_p), offset(offset_p), count(count_p) { } + SwizzleMetaData(const SwizzleMetaData &other) noexcept + : SwizzleMetaData(other.child_index, other.ptr.load(std::memory_order_relaxed), other.offset, other.count) { + } //! Index of block storing heap VectorDataIndex child_index; //! Last-known pointer - data_ptr_t ptr; + atomic ptr; //! Offset into the string_t vector uint16_t offset; //! Number of strings starting at 'offset' that have strings stored in the block with index 'child_index' diff --git a/src/duckdb/src/include/duckdb/common/types/column/column_data_consumer.hpp b/src/duckdb/src/include/duckdb/common/types/column/column_data_consumer.hpp index d056cf085..8946815b7 100644 --- a/src/duckdb/src/include/duckdb/common/types/column/column_data_consumer.hpp +++ b/src/duckdb/src/include/duckdb/common/types/column/column_data_consumer.hpp @@ -55,7 +55,7 @@ class ColumnDataConsumer { public: //! Initialize the scan of the ColumnDataCollection void InitializeScan(); - //! Assign a chunk to the scan state + //! Assign a chunk to the scan state; the caller must release its handles if no chunk is assigned. bool AssignChunk(ColumnDataConsumerScanState &state); //! Scan the assigned chunk void ScanChunk(ColumnDataConsumerScanState &state, DataChunk &chunk) const; diff --git a/src/duckdb/src/include/duckdb/common/types/conflict_manager.hpp b/src/duckdb/src/include/duckdb/common/types/conflict_manager.hpp index 440e1ad70..aa7a21be1 100644 --- a/src/duckdb/src/include/duckdb/common/types/conflict_manager.hpp +++ b/src/duckdb/src/include/duckdb/common/types/conflict_manager.hpp @@ -69,15 +69,18 @@ class ConflictManager { } //! Adds an index and its respective delete_index. - void AddIndex(const shared_ptr &index, const Identifier &index_name, - shared_ptr delete_index) { + void AddIndex(const shared_ptr &index, shared_ptr delete_index) { matching_indexes.push_back(index); matching_delete_indexes.push_back(std::move(delete_index)); - index_names.insert(index_name); } //! Returns true, if the index is in this conflict manager. - bool IndexMatches(const Identifier &index_name) const { - return index_names.find(index_name) != index_names.end(); + bool IndexMatches(const shared_ptr &index) const { + for (const auto &matching_index : matching_indexes) { + if (matching_index == index) { + return true; + } + } + return false; } //! Returns a reference to the matching indexes. const vector> &MatchingIndexes() const { @@ -139,11 +142,7 @@ class ConflictManager { vector> matching_indexes; //! Delete indexes matching the conflict target. vector> matching_delete_indexes; - //! All matching indexes by their name (unique identifier). - identifier_set_t index_names; - //! Registers all conflicting rows in a data chunk. - unordered_set conflict_rows; //! True, if we can skip recording any further conflicts. bool finished = false; @@ -201,7 +200,7 @@ class ConflictManager { //! Returns true, if we register a conflict for the lookup type. bool IsConflict(LookupResultType type); //! Adds a hit to the conflicts. - bool AddHit(const idx_t index_in_chunk, const std::function &callback); + bool AddHitInternal(const idx_t index_in_chunk, const row_t row_id, const bool second); //! Determine visible row ID for each index with two possible row IDs. void Finalize(const std::function &callback); //! Returns true, if the conflict manager should throw an exception, else false. @@ -224,23 +223,10 @@ class ConflictManager { } //! Adds a row ID to the primary conflict data. - void AddRowId(const idx_t index_in_chunk, const row_t row_id) { - // Only ON CONFLICT DO NOTHING can have multiple conflict targets, - // which can cause multiple row IDs per index_in_chunk. - // We let them overwrite each other, as we don't need the row IDs later. - auto elem = conflict_rows.find(index_in_chunk); - if (elem == conflict_rows.end()) { - // We have not yet seen this conflict: insert. - conflict_rows.insert(index_in_chunk); - GetConflictData(FIRST).Insert(index_in_chunk, row_id); - } - } + void AddRowId(const idx_t index_in_chunk, const row_t row_id); //! Adds a row ID to the secondary conflict data. - void AddSecondRowId(const idx_t index_in_chunk, const row_t row_id) { - D_ASSERT(conflict_rows.find(index_in_chunk) != conflict_rows.end()); - GetConflictData(SECOND).Insert(index_in_chunk, row_id); - } + void AddSecondRowId(const idx_t index_in_chunk, const row_t row_id); }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/types/data_chunk.hpp b/src/duckdb/src/include/duckdb/common/types/data_chunk.hpp index a4a46807b..9a2ef3c95 100644 --- a/src/duckdb/src/include/duckdb/common/types/data_chunk.hpp +++ b/src/duckdb/src/include/duckdb/common/types/data_chunk.hpp @@ -60,6 +60,8 @@ class DataChunk { inline idx_t ColumnCount() const { return data.size(); } + //! The number of rows the vectors of this chunk were initialized to hold + DUCKDB_API idx_t GetCapacity() const; //! Verify all child vectors have the expected cardinality void CheckCardinality(idx_t count_p); //! Sets the cardinality of all child vectors of this chunk diff --git a/src/duckdb/src/include/duckdb/common/types/date.hpp b/src/duckdb/src/include/duckdb/common/types/date.hpp index 92eee3cc4..412291757 100644 --- a/src/duckdb/src/include/duckdb/common/types/date.hpp +++ b/src/duckdb/src/include/duckdb/common/types/date.hpp @@ -213,6 +213,8 @@ class Date { DUCKDB_API static int32_t ExtractWeekNumberRegular(date_t date, bool monday_first = true); //! Returns the date of the monday of the current week. DUCKDB_API static date_t GetMondayOfCurrentWeek(date_t date); + //! Subtract a number of days from a (finite) date, throwing if the result is out of range + DUCKDB_API static date_t SubtractDays(date_t date, int64_t days); //! Helper function to parse two digits from a string (e.g. "30" -> 30, "03" -> 3, "3" -> 3) DUCKDB_API static bool ParseDoubleDigit(const char *buf, idx_t len, idx_t &pos, int32_t &result); diff --git a/src/duckdb/src/include/duckdb/common/types/double_na_equal.hpp b/src/duckdb/src/include/duckdb/common/types/double_na_equal.hpp index b18c34497..1c40e02f1 100644 --- a/src/duckdb/src/include/duckdb/common/types/double_na_equal.hpp +++ b/src/duckdb/src/include/duckdb/common/types/double_na_equal.hpp @@ -28,7 +28,7 @@ struct double_na_equal { if (std::isnan(val) && std::isnan(right)) { return true; } - return val == right; + return val == right && std::signbit(val) == std::signbit(right); } bool operator!=(const double &right) const { @@ -52,7 +52,7 @@ struct float_na_equal { if (std::isnan(val) && std::isnan(right)) { return true; } - return val == right; + return val == right && std::signbit(val) == std::signbit(right); } bool operator!=(const float &right) const { diff --git a/src/duckdb/src/include/duckdb/common/types/geometry.hpp b/src/duckdb/src/include/duckdb/common/types/geometry.hpp index bd4e46583..20c8d3cf4 100644 --- a/src/duckdb/src/include/duckdb/common/types/geometry.hpp +++ b/src/duckdb/src/include/duckdb/common/types/geometry.hpp @@ -154,51 +154,34 @@ class GeometryExtent { } void Extend(const VertexXY &vertex) { - x_min = MinValue(x_min, vertex.x); - x_max = MaxValue(x_max, vertex.x); - y_min = MinValue(y_min, vertex.y); - y_max = MaxValue(y_max, vertex.y); + ExtendAxis(x_min, x_max, vertex.x); + ExtendAxis(y_min, y_max, vertex.y); } void Extend(const VertexXYZ &vertex) { - x_min = MinValue(x_min, vertex.x); - x_max = MaxValue(x_max, vertex.x); - y_min = MinValue(y_min, vertex.y); - y_max = MaxValue(y_max, vertex.y); - z_min = MinValue(z_min, vertex.z); - z_max = MaxValue(z_max, vertex.z); + ExtendAxis(x_min, x_max, vertex.x); + ExtendAxis(y_min, y_max, vertex.y); + ExtendAxis(z_min, z_max, vertex.z); } void Extend(const VertexXYM &vertex) { - x_min = MinValue(x_min, vertex.x); - x_max = MaxValue(x_max, vertex.x); - y_min = MinValue(y_min, vertex.y); - y_max = MaxValue(y_max, vertex.y); - m_min = MinValue(m_min, vertex.m); - m_max = MaxValue(m_max, vertex.m); + ExtendAxis(x_min, x_max, vertex.x); + ExtendAxis(y_min, y_max, vertex.y); + ExtendAxis(m_min, m_max, vertex.m); } void Extend(const VertexXYZM &vertex) { - x_min = MinValue(x_min, vertex.x); - x_max = MaxValue(x_max, vertex.x); - y_min = MinValue(y_min, vertex.y); - y_max = MaxValue(y_max, vertex.y); - z_min = MinValue(z_min, vertex.z); - z_max = MaxValue(z_max, vertex.z); - m_min = MinValue(m_min, vertex.m); - m_max = MaxValue(m_max, vertex.m); + ExtendAxis(x_min, x_max, vertex.x); + ExtendAxis(y_min, y_max, vertex.y); + ExtendAxis(z_min, z_max, vertex.z); + ExtendAxis(m_min, m_max, vertex.m); } void Merge(const GeometryExtent &other) { - x_min = MinValue(x_min, other.x_min); - y_min = MinValue(y_min, other.y_min); - z_min = MinValue(z_min, other.z_min); - m_min = MinValue(m_min, other.m_min); - - x_max = MaxValue(x_max, other.x_max); - y_max = MaxValue(y_max, other.y_max); - z_max = MaxValue(z_max, other.z_max); - m_max = MaxValue(m_max, other.m_max); + MergeAxis(x_min, x_max, other.x_min, other.x_max); + MergeAxis(y_min, y_max, other.y_min, other.y_max); + MergeAxis(z_min, z_max, other.z_min, other.z_max); + MergeAxis(m_min, m_max, other.m_min, other.m_max); } bool IntersectsXY(const GeometryExtent &other) const { @@ -214,6 +197,29 @@ class GeometryExtent { return x_min <= other.x_min && x_max >= other.x_max && y_min <= other.y_min && y_max >= other.y_max; } +private: + //! A NaN coordinate cannot be placed on the axis, so it makes the range of the axis unknown + static void ExtendAxis(double &min, double &max, double value) { + if (std::isnan(value)) { + min = UNKNOWN_MIN; + max = UNKNOWN_MAX; + return; + } + min = MinValue(min, value); + max = MaxValue(max, value); + } + + static void MergeAxis(double &min, double &max, double other_min, double other_max) { + if (std::isnan(other_min) || std::isnan(other_max)) { + min = UNKNOWN_MIN; + max = UNKNOWN_MAX; + return; + } + min = MinValue(min, other_min); + max = MaxValue(max, other_max); + } + +public: double x_min; double y_min; double z_min; @@ -273,6 +279,9 @@ class Geometry { //! Get legacy geometry type (pre v1.5) static LogicalType GetSpatialGeometryType(); + //! Convert from WKT, returns false and sets error_message if the text is not valid WKT + DUCKDB_API static bool TryFromString(const string_t &wkt_text, string_t &result, StringHeap &heap, + string &error_message); //! Convert from WKT DUCKDB_API static bool FromString(const string_t &wkt_text, string_t &result, StringHeap &heap, bool strict, QueryLocation query_location); diff --git a/src/duckdb/src/include/duckdb/common/types/hyperloglog.hpp b/src/duckdb/src/include/duckdb/common/types/hyperloglog.hpp index 5a18c4379..79c53e915 100644 --- a/src/duckdb/src/include/duckdb/common/types/hyperloglog.hpp +++ b/src/duckdb/src/include/duckdb/common/types/hyperloglog.hpp @@ -34,6 +34,7 @@ class HyperLogLogBase { template class HyperLogLogP : public HyperLogLogBase { public: + static constexpr idx_t PRECISION = P; static constexpr idx_t Q = 64 - P; static constexpr idx_t M = 1 << P; static constexpr double ALPHA = 0.721347520444481703680; // 1 / (2 log(2)) @@ -64,6 +65,13 @@ class HyperLogLogP : public HyperLogLogBase { return k[i]; } + //! Overwrites a register, for importing an exported sketch. A register holds the position of the first set bit of + //! a hash, at most Q + 1 - ExtractCounts indexes an array of Q + 2 counts with it, so callers must check that. + void SetRegister(const idx_t &i, const uint8_t &value) { + D_ASSERT(value <= Q + 1); + k[i] = value; + } + idx_t Count() const { uint32_t c[Q + 2] = {0}; ExtractCounts(c); diff --git a/src/duckdb/src/include/duckdb/common/types/row/partitioned_tuple_data.hpp b/src/duckdb/src/include/duckdb/common/types/row/partitioned_tuple_data.hpp index 81400b3df..e72bd229a 100644 --- a/src/duckdb/src/include/duckdb/common/types/row/partitioned_tuple_data.hpp +++ b/src/duckdb/src/include/duckdb/common/types/row/partitioned_tuple_data.hpp @@ -135,6 +135,8 @@ class PartitionedTupleData { idx_t Count() const; //! Get the size (in bytes) of this PartitionedTupleData idx_t SizeInBytes() const; + //! Buffer-manager memory charged for row and heap blocks, plus allocator metadata + idx_t GetAllocatedSizeInBytes() const; //! Get the number of partitions of this PartitionedTupleData idx_t PartitionCount() const; //! Get the count and size of the largest partition diff --git a/src/duckdb/src/include/duckdb/common/types/row/tuple_data_allocator.hpp b/src/duckdb/src/include/duckdb/common/types/row/tuple_data_allocator.hpp index f648f0d18..7fc8eafcc 100644 --- a/src/duckdb/src/include/duckdb/common/types/row/tuple_data_allocator.hpp +++ b/src/duckdb/src/include/duckdb/common/types/row/tuple_data_allocator.hpp @@ -8,6 +8,7 @@ #pragma once +#include "duckdb/common/atomic.hpp" #include "duckdb/common/types/row/tuple_data_layout.hpp" #include "duckdb/common/types/row/tuple_data_states.hpp" #include "duckdb/common/arena_containers/arena_vector.hpp" @@ -78,6 +79,8 @@ class TupleDataAllocator { idx_t RowBlockCount() const; //! Number of heap blocks idx_t HeapBlockCount() const; + //! Cached allocation size of retained row and heap blocks + idx_t GetBlockAllocationSize() const; //! Sets the partition index of this tuple data allocator void SetPartitionIndex(idx_t index); //! Gets the partition index of this tuple data allocator @@ -122,11 +125,9 @@ class TupleDataAllocator { unsafe_vector> &parts, optional_ptr sort_key_payload_state = nullptr); //! Internal function for ReleaseOrStoreHandles - static void ReleaseOrStoreHandlesInternal(TupleDataSegment &segment, - unsafe_arena_vector &pinned_row_handles, - buffer_handle_map_t &handles, const ContinuousIdSet &block_ids, - unsafe_arena_vector &blocks, - TupleDataPinProperties properties); + void ReleaseOrStoreHandlesInternal(TupleDataSegment &segment, unsafe_arena_vector &pinned_row_handles, + buffer_handle_map_t &handles, const ContinuousIdSet &block_ids, + unsafe_arena_vector &blocks, TupleDataPinProperties properties); //! Create a row/heap block, store the handle in pin_state so the block stays pinned void CreateRowBlock(TupleDataSegment &segment, TupleDataPinState &pin_state); void CreateHeapBlock(TupleDataSegment &segment, TupleDataPinState &pin_state, idx_t size); @@ -157,6 +158,8 @@ class TupleDataAllocator { unsafe_arena_vector row_blocks; //! Blocks storing the variable-size data of the fixed-size rows (e.g., string, list) unsafe_arena_vector heap_blocks; + //! Blocks can be released by parallel scans of separate segments + atomic block_allocation_size {0}; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/types/row/tuple_data_collection.hpp b/src/duckdb/src/include/duckdb/common/types/row/tuple_data_collection.hpp index 16a9a7e93..5b5b468ae 100644 --- a/src/duckdb/src/include/duckdb/common/types/row/tuple_data_collection.hpp +++ b/src/duckdb/src/include/duckdb/common/types/row/tuple_data_collection.hpp @@ -82,6 +82,8 @@ class TupleDataCollection { idx_t ChunkCount() const; //! The size (in bytes) of the blocks held by this tuple data collection idx_t SizeInBytes() const; + //! Buffer-manager memory charged for row and heap blocks + idx_t GetBlockAllocationSize() const; //! Unpins all held pins void Unpin(); //! Sets the partition index of this tuple data collection @@ -147,7 +149,7 @@ class TupleDataCollection { static void GetVectorData(const TupleDataChunkState &chunk_state, UnifiedVectorFormat result[]); //! Resets the cached cache vectors (used for ARRAY/LIST casts) static void ResetCachedCastVectors(TupleDataChunkState &chunk_state, const vector &column_ids); - //! Computes the heap sizes for the new DataChunk that will be appended + //! Computes heap sizes for the columns selected in chunk_state static void ComputeHeapSizes(TupleDataChunkState &chunk_state, const DataChunk &new_chunk, const SelectionVector &append_sel, const idx_t append_count); //! Computes the heap sizes for a SortKey layout diff --git a/src/duckdb/src/include/duckdb/common/types/time.hpp b/src/duckdb/src/include/duckdb/common/types/time.hpp index 764ed6857..d15c2752d 100644 --- a/src/duckdb/src/include/duckdb/common/types/time.hpp +++ b/src/duckdb/src/include/duckdb/common/types/time.hpp @@ -29,7 +29,8 @@ class Time { optional_ptr nanos = nullptr); // No hour limit DUCKDB_API static bool TryConvertInterval(const char *buf, idx_t len, idx_t &pos, dtime_t &result, - bool strict = false, optional_ptr nanos = nullptr); + bool strict = false, optional_ptr nanos = nullptr, + bool negative = false); //! Convert a time object to a string in the format "hh:mm:ss" DUCKDB_API static string ToString(dtime_t time); @@ -56,7 +57,7 @@ class Time { private: static bool TryConvertInternal(const char *buf, idx_t len, idx_t &pos, dtime_t &result, bool strict, - optional_ptr nanos = nullptr); + optional_ptr nanos = nullptr, bool negative = false); }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/types/timestamp.hpp b/src/duckdb/src/include/duckdb/common/types/timestamp.hpp index fbe421c4f..4865158bc 100644 --- a/src/duckdb/src/include/duckdb/common/types/timestamp.hpp +++ b/src/duckdb/src/include/duckdb/common/types/timestamp.hpp @@ -70,6 +70,16 @@ class Timestamp { DUCKDB_API static date_t GetDate(timestamp_t timestamp); DUCKDB_API static date_t GetDateNS(timestamp_ns_t timestamp); + // Split a time at some precision, producing positive values. + static inline void SplitTime(const int64_t value, const int64_t precision, int64_t &whole, int64_t &frac) { + whole = value / precision; + frac = value % precision; + if (frac < 0) { + frac += precision; + --whole; + } + } + DUCKDB_API static dtime_t GetTime(timestamp_t timestamp); DUCKDB_API static dtime_ns_t GetTimeNs(timestamp_ns_t timestamp); //! Create a Timestamp object from a specified (date, time) combination diff --git a/src/duckdb/src/include/duckdb/common/types/type_manager.hpp b/src/duckdb/src/include/duckdb/common/types/type_manager.hpp index 2347bba75..401df3371 100644 --- a/src/duckdb/src/include/duckdb/common/types/type_manager.hpp +++ b/src/duckdb/src/include/duckdb/common/types/type_manager.hpp @@ -18,9 +18,12 @@ class TypeManager { //! Get the CastFunctionSet from the TypeManager CastFunctionSet &GetCastFunctions(); + const CastFunctionSet &GetCastFunctions() const; //! Try to parse and bind a logical type from a string. Throws an exception if the type could not be parsed. LogicalType ParseLogicalType(const string &type_str, ClientContext &context) const; + //! Parse and bind a logical type from a string with the default parser, resolving the built-in types only. + LogicalType ParseLogicalType(const string &type_str) const; //! Get the TypeManager from the DatabaseInstance static TypeManager &Get(DatabaseInstance &db); diff --git a/src/duckdb/src/include/duckdb/common/types/validity_mask.hpp b/src/duckdb/src/include/duckdb/common/types/validity_mask.hpp index 5ad74dbfc..f3c351bf8 100644 --- a/src/duckdb/src/include/duckdb/common/types/validity_mask.hpp +++ b/src/duckdb/src/include/duckdb/common/types/validity_mask.hpp @@ -419,6 +419,13 @@ struct ValidityArray { validity_mask = validity_data.get(); memset(validity_mask, initial, sizeof(bool) * count); } + //! Points the array at storage of `count` entries owned by the caller (e.g. a buffer-allocated block, so that an + //! O(rows) array counts against the memory limit); the storage must outlive the array + inline void Initialize(bool *validity, idx_t count) { + validity_data.reset(); + validity_mask = validity; + capacity = count; + } inline void InitializeEmpty(idx_t count) { capacity = count; } diff --git a/src/duckdb/src/include/duckdb/common/types/value.hpp b/src/duckdb/src/include/duckdb/common/types/value.hpp index e76e74c3b..7dcfca937 100644 --- a/src/duckdb/src/include/duckdb/common/types/value.hpp +++ b/src/duckdb/src/include/duckdb/common/types/value.hpp @@ -22,6 +22,7 @@ #include "duckdb/common/insertion_order_preserving_map.hpp" #include +#include namespace duckdb { @@ -66,7 +67,7 @@ class Value { //! Create a VARCHAR value DUCKDB_API Value(String val); // NOLINT: Allow implicit conversion from `string` //! Create a VARCHAR value - DUCKDB_API Value(std::string_view val); + DUCKDB_API Value(std::string_view val); // NOLINT: Allow implicit conversion from `std::string_view` //! Copy constructor DUCKDB_API Value(const Value &other); //! Move constructor @@ -85,6 +86,8 @@ class Value { inline bool IsNull() const { return is_null; } + //! Whether the value is within the domain of its type (e.g. valid UTF-8, ENUM index within the dictionary) + DUCKDB_API bool IsValid() const; //! Create the lowest possible value of a given type (numeric only) DUCKDB_API static Value MinimumValue(const LogicalType &type); @@ -203,7 +206,9 @@ class Value { return Value::BLOB(const_data_ptr_cast(data.c_str()), data.size()); } static Value BLOB_RAW(std::string_view data) { - return Value::BLOB(const_data_ptr_cast(data.data()), data.size()); + return Value::BLOB( + const_data_ptr_cast(data.data()), // NOLINT(bugprone-suspicious-stringview-data-usage): length is passed + data.size()); } //! Creates a blob by casting a specified string to a blob (i.e. interpreting \x characters) DUCKDB_API static Value BLOB(const string &data); @@ -249,6 +254,9 @@ class Value { DUCKDB_API string ToString() const; //! Convert this value to a SQL-parseable string DUCKDB_API string ToSQLString() const; + //! Render a nested value as SQL via child_to_sql + DUCKDB_API static string NestedToSQLString(const Value &value, + const std::function &child_to_sql); DUCKDB_API uintptr_t GetPointer() const; diff --git a/src/duckdb/extension/parquet/include/reader/variant/parquet_variant_iterator.hpp b/src/duckdb/src/include/duckdb/common/types/variant/parquet_variant_iterator.hpp similarity index 79% rename from src/duckdb/extension/parquet/include/reader/variant/parquet_variant_iterator.hpp rename to src/duckdb/src/include/duckdb/common/types/variant/parquet_variant_iterator.hpp index c4097f625..dcdbf6495 100644 --- a/src/duckdb/extension/parquet/include/reader/variant/parquet_variant_iterator.hpp +++ b/src/duckdb/src/include/duckdb/common/types/variant/parquet_variant_iterator.hpp @@ -1,7 +1,7 @@ //===----------------------------------------------------------------------===// // DuckDB // -// reader/variant/parquet_variant_iterator.hpp +// duckdb/common/types/variant/parquet_variant_iterator.hpp // // //===----------------------------------------------------------------------===// @@ -16,7 +16,7 @@ #include "duckdb/common/unique_ptr.hpp" #include "duckdb/common/helper.hpp" #include "duckdb/function/scalar_function.hpp" -#include "reader/variant/variant_binary_decoder.hpp" +#include "duckdb/common/types/variant/variant_binary_decoder.hpp" namespace duckdb { @@ -52,7 +52,7 @@ struct ShreddedGroupView { vector> fields; //! Build the view recursively from a group Vector - void Build(Vector &group); + DUCKDB_API void Build(Vector &group); }; class ParquetVariantIterator; @@ -111,14 +111,14 @@ class ParquetVariantNode { return kind == Kind::MISSING; } - VariantLogicalType GetTypeId() const; + DUCKDB_API VariantLogicalType GetTypeId() const; //! Returns the fixed-width primitive payload (loaded / re-encoded as T) template T GetData() const; - string_t GetString() const; - VariantDecimalProperties GetDecimalProperties() const; - ParquetObjectIterator GetObjectChildren(VariantIterationOrder order) const; - ParquetArrayIterator GetArrayChildren() const; + DUCKDB_API string_t GetString() const; + DUCKDB_API VariantDecimalProperties GetDecimalProperties() const; + DUCKDB_API ParquetObjectIterator GetObjectChildren(VariantIterationOrder order) const; + DUCKDB_API ParquetArrayIterator GetArrayChildren() const; private: explicit ParquetVariantNode(Kind kind) : kind(kind) { @@ -149,11 +149,11 @@ struct ParquetObjectEntry { class ParquetObjectIterator { public: //! Shredded object (typed fields + optional binary overlay) - ParquetObjectIterator(const ParquetVariantIterator &state, const ShreddedGroupView &view, idx_t index, - const_data_ptr_t overlay, const_data_ptr_t overlay_end); + DUCKDB_API ParquetObjectIterator(const ParquetVariantIterator &state, const ShreddedGroupView &view, idx_t index, + const_data_ptr_t overlay, const_data_ptr_t overlay_end); //! Binary object - ParquetObjectIterator(const ParquetVariantIterator &state, const VariantMetadata &metadata, const_data_ptr_t data, - const_data_ptr_t end); + DUCKDB_API ParquetObjectIterator(const ParquetVariantIterator &state, const VariantMetadata &metadata, + const_data_ptr_t data, const_data_ptr_t end); public: const ParquetObjectEntry *begin() const { // NOLINT: match stl API @@ -174,16 +174,16 @@ class ParquetObjectIterator { class ParquetArrayIterator { public: //! Shredded array - ParquetArrayIterator(const ParquetVariantIterator &state, const ShreddedGroupView &view, idx_t index); + DUCKDB_API ParquetArrayIterator(const ParquetVariantIterator &state, const ShreddedGroupView &view, idx_t index); //! Binary array - ParquetArrayIterator(const ParquetVariantIterator &state, const VariantMetadata &metadata, const_data_ptr_t data, - const_data_ptr_t end); + DUCKDB_API ParquetArrayIterator(const ParquetVariantIterator &state, const VariantMetadata &metadata, + const_data_ptr_t data, const_data_ptr_t end); public: idx_t size() const { return length; } - ParquetVariantNode operator[](idx_t i) const; + DUCKDB_API ParquetVariantNode operator[](idx_t i) const; private: reference state; @@ -207,30 +207,30 @@ class ParquetArrayIterator { //! are fetched by value (no materialization). class ParquetVariantIterator { public: - ParquetVariantIterator(Vector &metadata, Vector &group); + DUCKDB_API ParquetVariantIterator(Vector &metadata, Vector &group); //! Binary-only: each row is a full Spark variant-encoded value (the metadata blob immediately followed //! by the value blob). There is no shredded group - the value is read right after the metadata. - explicit ParquetVariantIterator(Vector &metadata); + DUCKDB_API explicit ParquetVariantIterator(Vector &metadata); public: //! Reset the per-row state (lazily-decoded metadata) for a new row - void BeginRow(idx_t row); + DUCKDB_API void BeginRow(idx_t row); //! Resolve the root value of 'row' (a missing root is promoted to a SQL NULL) - ParquetVariantNode Root(idx_t row) const; + DUCKDB_API ParquetVariantNode Root(idx_t row) const; //! Resolve the root of a binary-only row: the value blob starts right after the metadata - ParquetVariantNode BinaryRoot() const; + DUCKDB_API ParquetVariantNode BinaryRoot() const; //! Resolve the value of the group 'view' at logical position 'index' - ParquetVariantNode ResolveGroup(const ShreddedGroupView &view, idx_t index) const; + DUCKDB_API ParquetVariantNode ResolveGroup(const ShreddedGroupView &view, idx_t index) const; //! The (lazily-decoded) Variant metadata of the current row - const VariantMetadata &GetMetadata() const; + DUCKDB_API const VariantMetadata &GetMetadata() const; //! The recursive view of the Parquet group tree (used by the shredded-conversion path) const ShreddedGroupView &GetRootView() const { return root_view; } //! Emit the binary value in ['data', 'end') of the current row into the builder (BeginRow must precede) - void EmitBinary(const_data_ptr_t data, const_data_ptr_t end, VariantBuilder &builder) const; + DUCKDB_API void EmitBinary(const_data_ptr_t data, const_data_ptr_t end, VariantBuilder &builder) const; private: ShreddedGroupView root_view; @@ -248,10 +248,12 @@ class ParquetVariantConversion { public: //! Convert binary Variant values (each row being the metadata blob followed by the value blob) into the //! canonical VARIANT 'result' in a single pass - static void ConvertBinary(Vector &metadata_and_value, Vector &result, idx_t count); - //! 'variant_bytes_to_variant': decode a binary Variant value (metadata followed by value) into a VARIANT. - //! The inverse of 'variant_to_parquet_variant'. - static ScalarFunction GetBytesToVariantFunction(); + DUCKDB_API static void ConvertBinary(Vector &metadata_and_value, Vector &result, idx_t count); + + //! Convert VARIANT values into the Parquet Variant layout declared by 'result's type — + //! struct. This is 'variant_to_parquet_variant's + //! conversion, and also the storage of the arrow.parquet.variant canonical extension type. + DUCKDB_API static void ToParquetVariant(const Vector &variant, idx_t count, Vector &result); }; } // namespace duckdb diff --git a/src/duckdb/extension/parquet/include/reader/variant/variant_binary_decoder.hpp b/src/duckdb/src/include/duckdb/common/types/variant/variant_binary_decoder.hpp similarity index 92% rename from src/duckdb/extension/parquet/include/reader/variant/variant_binary_decoder.hpp rename to src/duckdb/src/include/duckdb/common/types/variant/variant_binary_decoder.hpp index d9bfacdd7..a4a9459ee 100644 --- a/src/duckdb/extension/parquet/include/reader/variant/variant_binary_decoder.hpp +++ b/src/duckdb/src/include/duckdb/common/types/variant/variant_binary_decoder.hpp @@ -16,7 +16,7 @@ namespace duckdb { struct VariantMetadataHeader { public: - static VariantMetadataHeader FromHeaderByte(uint8_t byte); + DUCKDB_API static VariantMetadataHeader FromHeaderByte(uint8_t byte); public: //! The version of the protocol used (only '1' supported for now) @@ -29,7 +29,7 @@ struct VariantMetadataHeader { struct VariantMetadata { public: - explicit VariantMetadata(const string_t &metadata); + DUCKDB_API explicit VariantMetadata(const string_t &metadata); public: const string_t &metadata; @@ -80,7 +80,7 @@ struct VariantValueMetadata { } public: - static VariantValueMetadata FromHeaderByte(uint8_t byte); + DUCKDB_API static VariantValueMetadata FromHeaderByte(uint8_t byte); static VariantBasicType VariantBasicTypeFromByte(uint8_t byte) { if (byte >= static_cast(VariantBasicType::INVALID)) { throw NotImplementedException("Variant BasicType (%d) is not supported", byte); diff --git a/src/duckdb/src/include/duckdb/common/types/variant/variant_builder.hpp b/src/duckdb/src/include/duckdb/common/types/variant/variant_builder.hpp index 3ca61f325..e0dbff42a 100644 --- a/src/duckdb/src/include/duckdb/common/types/variant/variant_builder.hpp +++ b/src/duckdb/src/include/duckdb/common/types/variant/variant_builder.hpp @@ -101,6 +101,8 @@ struct VariantBuilder { //! maps a key string to its (unsorted) dictionary index, owned by the result's keys vector OrderedOwningStringMap &dictionary; + static constexpr idx_t INIT_NESTING_DEPTH = 8; + //! the offsets at which the current row's entries begin idx_t row_values = 0; idx_t row_children = 0; @@ -126,14 +128,18 @@ struct VariantBuilder { //! Emit a VARIANT_NULL value void EmitNull() { type_ids.push_back(static_cast(VariantLogicalType::VARIANT_NULL)); - byte_offsets.push_back(NumericCast(blob.size())); + //! 'blob' may already exceed uint32_t mid-row; this offset is only read once the row is known to fit + //! (checked in BuildVariant once the row is fully emitted), so a narrowing wrap here is harmless. + byte_offsets.push_back(static_cast(blob.size())); } //! Emit an OBJECT value with 'n' children (assumed to be in lexicographic key order). 'key_fn(i)' //! returns the (string_t) key of child i; 'emit_fn(i)' must emit exactly one value for child i. template void EmitObject(idx_t n, KEY_FN &&key_fn, EMIT_FN &&emit_fn) { - auto byte_offset = NumericCast(blob.size()); + //! 'blob' may already exceed uint32_t mid-row; this offset is only read once the row is known to fit + //! (checked in BuildVariant once the row is fully emitted), so a narrowing wrap here is harmless. + auto byte_offset = static_cast(blob.size()); type_ids.push_back(static_cast(VariantLogicalType::OBJECT)); byte_offsets.push_back(byte_offset); VariantBuilderAppendVarint(blob, NumericCast(n)); @@ -155,7 +161,9 @@ struct VariantBuilder { //! Emit an ARRAY value with 'n' elements. 'emit_fn(i)' must emit exactly one value for element i. template void EmitArray(idx_t n, EMIT_FN &&emit_fn) { - auto byte_offset = NumericCast(blob.size()); + //! 'blob' may already exceed uint32_t mid-row; this offset is only read once the row is known to fit + //! (checked in BuildVariant once the row is fully emitted), so a narrowing wrap here is harmless. + auto byte_offset = static_cast(blob.size()); type_ids.push_back(static_cast(VariantLogicalType::ARRAY)); byte_offsets.push_back(byte_offset); VariantBuilderAppendVarint(blob, NumericCast(n)); @@ -341,7 +349,9 @@ struct VariantBuilder { //! 'it.GetDecimalProperties()' followed by 'it.GetData()' at the physical type implied by the width. template void EmitPrimitiveNode(const NODE &it, VariantLogicalType type_id) { - auto byte_offset = NumericCast(blob.size()); + //! 'blob' may already exceed uint32_t mid-row; this offset is only read once the row is known to fit + //! (checked in BuildVariant once the row is fully emitted), so a narrowing wrap here is harmless. + auto byte_offset = static_cast(blob.size()); type_ids.push_back(static_cast(type_id)); byte_offsets.push_back(byte_offset); switch (type_id) { @@ -455,6 +465,32 @@ struct VariantBuilder { throw InternalException("EmitPrimitiveNode: unhandled VariantLogicalType (%d)", static_cast(type_id)); } } + + optional_idx BeginContainer(VariantLogicalType type_id, idx_t n) { + auto byte_offset = NumericCast(blob.size()); + type_ids.push_back(static_cast(type_id)); + byte_offsets.push_back(byte_offset); + VariantBuilderAppendVarint(blob, NumericCast(n)); + if (!n) { + return optional_idx(); + } + VariantBuilderAppendVarint(blob, LocalChild()); + auto block = child_value_ids.size(); + child_value_ids.resize(block + n); + child_key_ids.resize(block + n); + return optional_idx(block); + } + + void AssignObjectChild(idx_t block, idx_t i, string_t key) { + child_value_ids[block + i] = LocalValue(); + child_key_ids[block + i] = LocalKey(); + key_slots.push_back(VariantBuilderGetOrCreateIndex(dictionary, key)); + } + + void AssignArrayChild(idx_t block, idx_t i) { + child_value_ids[block + i] = LocalValue(); + child_key_ids[block + i] = VARIANT_INVALID_KEY; + } }; //===--------------------------------------------------------------------===// @@ -474,29 +510,101 @@ auto CollectObjectChildren(const NODE &it) { //! Traverse a VariantNode-like cursor 'it' (any type exposing the node concept) into the builder. template -void EmitIterator(const NODE &it, VariantBuilder &builder) { - if (it.IsNull() || it.IsMissing()) { - builder.EmitNull(); - return; - } +void EmitIterator(const NODE &root, VariantBuilder &builder) { + using EntryT = typename decltype(CollectObjectChildren(root))::value_type; + using ArrayIterT = decltype(std::declval().GetArrayChildren()); + + struct Frame { + bool is_object = false; + idx_t index = 0; + idx_t block = 0; + vector object_children; + optional array_children; + }; + + vector stack; + stack.reserve(VariantBuilder::INIT_NESTING_DEPTH); + + auto ProcessNode = [&](const NODE &node) { + if (node.IsNull() || node.IsMissing()) { + builder.EmitNull(); + return; + } + auto type_id = node.GetTypeId(); + switch (type_id) { + case VariantLogicalType::OBJECT: { + auto children = CollectObjectChildren(node); + auto n = children.size(); + if (stack.size() >= stack.capacity()) { + stack.reserve(NextPowerOfTwo(stack.size() + 1)); + } + auto block = builder.BeginContainer(VariantLogicalType::OBJECT, n); + if (block.IsValid()) { + Frame frame; + frame.is_object = true; + frame.block = block.GetIndex(); + frame.object_children = std::move(children); + stack.push_back(std::move(frame)); + } + break; + } + case VariantLogicalType::ARRAY: { + auto array_iter = node.GetArrayChildren(); + auto n = array_iter.size(); + if (stack.size() >= stack.capacity()) { + stack.reserve(NextPowerOfTwo(stack.size() + 1)); + } + auto block = builder.BeginContainer(VariantLogicalType::ARRAY, n); + if (block.IsValid()) { + Frame frame; + frame.is_object = false; + frame.block = block.GetIndex(); + frame.array_children.emplace(std::move(array_iter)); + stack.push_back(std::move(frame)); + } + break; + } + default: + builder.EmitPrimitiveNode(node, type_id); + break; + } + }; - auto type_id = it.GetTypeId(); - switch (type_id) { - case VariantLogicalType::OBJECT: { - auto children = CollectObjectChildren(it); - builder.EmitObject( - children.size(), [&](idx_t i) { return children[i].key; }, - [&](idx_t i) { EmitIterator(children[i].value, builder); }); - break; - } - case VariantLogicalType::ARRAY: { - auto array = it.GetArrayChildren(); - builder.EmitArray(array.size(), [&](idx_t i) { EmitIterator(array[i], builder); }); - break; - } - default: - builder.EmitPrimitiveNode(it, type_id); - break; + ProcessNode(root); + + while (!stack.empty()) { + auto &frame = stack.back(); + + idx_t count; + if (frame.is_object) { + count = frame.object_children.size(); + } else { + if (!frame.array_children) { + throw InternalException("Variant array frame has no children"); + } + count = frame.array_children->size(); + } + + if (frame.index >= count) { + stack.pop_back(); + continue; + } + + const bool is_object = frame.is_object; + const idx_t block = frame.block; + const idx_t i = frame.index++; + + if (is_object) { + auto &entry = frame.object_children[i]; + builder.AssignObjectChild(block, i, entry.key); + ProcessNode(entry.value); + } else { + if (!frame.array_children) { + throw InternalException("Variant array frame has no children"); + } + builder.AssignArrayChild(block, i); + ProcessNode((*frame.array_children)[i]); + } } } @@ -531,6 +639,12 @@ void BuildVariant(SOURCE &source, idx_t count, Vector &result) { for (idx_t row = 0; row < count; row++) { builder.BeginRow(); bool is_null = source.Emit(row, builder); + size_t temp = builder.blob.size(); + if (temp > NumericLimits::Maximum()) { + throw InvalidInputException( + "Cannot convert value to VARIANT: encoded row size exceeds the maximum supported %u bytes", + NumericLimits::Maximum()); + } blob_writer.WriteValue(string_t(builder.blob.data(), NumericCast(builder.blob.size()))); if (is_null) { //! SPEC: If a Variant is missing in a context where a value is required, readers must return a Variant null diff --git a/src/duckdb/src/include/duckdb/common/types/variant_visitor.hpp b/src/duckdb/src/include/duckdb/common/types/variant_visitor.hpp index f8cd314d5..7787fc0a1 100644 --- a/src/duckdb/src/include/duckdb/common/types/variant_visitor.hpp +++ b/src/duckdb/src/include/duckdb/common/types/variant_visitor.hpp @@ -1,5 +1,6 @@ #pragma once +#include "duckdb/common/algorithm.hpp" #include "duckdb/common/types/variant.hpp" #include "duckdb/common/types/variant_iterator.hpp" #include "duckdb/function/scalar/variant_utils.hpp" @@ -34,7 +35,7 @@ class VariantVisitor { template static ReturnType Visit(const UnifiedVariantVectorData &variant, idx_t row, uint32_t values_idx, Args &&...args) { if (!variant.RowIsValid(row)) { - VisitMetadata(VariantLogicalType::VARIANT_NULL, std::forward(args)...); + VisitMetadata(VariantLogicalType::VARIANT_NULL, args...); return Visitor::VisitNull(std::forward(args)...); } @@ -43,7 +44,7 @@ class VariantVisitor { auto blob_data = const_data_ptr_cast(variant.GetData(row).GetData()); auto ptr = const_data_ptr_cast(blob_data + byte_offset); - VisitMetadata(type_id, std::forward(args)...); + VisitMetadata(type_id, args...); switch (type_id) { case VariantLogicalType::VARIANT_NULL: @@ -120,12 +121,12 @@ class VariantVisitor { template static ReturnType Visit(const VariantNode &node, Args &&...args) { if (node.IsNull() || node.IsMissing()) { - VisitMetadata(VariantLogicalType::VARIANT_NULL, std::forward(args)...); + VisitMetadata(VariantLogicalType::VARIANT_NULL, args...); return Visitor::VisitNull(std::forward(args)...); } auto type_id = node.GetTypeId(); - VisitMetadata(type_id, std::forward(args)...); + VisitMetadata(type_id, args...); switch (type_id) { case VariantLogicalType::VARIANT_NULL: @@ -208,7 +209,7 @@ class VariantVisitor { array_items.reserve(array_data.child_count); for (idx_t i = 0; i < array_data.child_count; i++) { auto values_index = variant.GetValuesIndex(row, array_data.children_idx + i); - array_items.emplace_back(Visit(variant, row, values_index, std::forward(args)...)); + array_items.emplace_back(Visit(variant, row, values_index, args...)); } return array_items; } @@ -220,7 +221,7 @@ class VariantVisitor { Args &&...args) { for (idx_t i = 0; i < array_data.child_count; i++) { auto values_index = variant.GetValuesIndex(row, array_data.children_idx + i); - Visit(variant, row, values_index, std::forward(args)...); + Visit(variant, row, values_index, args...); } } @@ -230,7 +231,7 @@ class VariantVisitor { vector array_items; array_items.reserve(array.GetArrayChildren().size()); for (const auto &child : array.GetArrayChildren()) { - array_items.emplace_back(Visit(child, std::forward(args)...)); + array_items.emplace_back(Visit(child, args...)); } return array_items; } @@ -239,7 +240,7 @@ class VariantVisitor { template static std::enable_if_t, void> VisitArrayItems(const VariantNode &array, Args &&...args) { for (const auto &child : array.GetArrayChildren()) { - Visit(child, std::forward(args)...); + Visit(child, args...); } } @@ -249,13 +250,18 @@ class VariantVisitor { child_list_t object_items; for (idx_t i = 0; i < object_data.child_count; i++) { auto values_index = variant.GetValuesIndex(row, object_data.children_idx + i); - auto val = Visit(variant, row, values_index, std::forward(args)...); + auto val = Visit(variant, row, values_index, args...); auto keys_index = variant.GetKeysIndex(row, object_data.children_idx + i); auto &key = variant.GetKey(row, keys_index); object_items.emplace_back(key.GetString(), std::move(val)); } + std::stable_sort( + object_items.begin(), object_items.end(), + [](const std::pair &lhs, const std::pair &rhs) { + return lhs.first.GetIdentifierName() < rhs.first.GetIdentifierName(); + }); return object_items; } diff --git a/src/duckdb/src/include/duckdb/common/types/vector_buffer.hpp b/src/duckdb/src/include/duckdb/common/types/vector_buffer.hpp index b974841d3..b10b9f667 100644 --- a/src/duckdb/src/include/duckdb/common/types/vector_buffer.hpp +++ b/src/duckdb/src/include/duckdb/common/types/vector_buffer.hpp @@ -26,6 +26,8 @@ class VectorBuffer; class Vector; struct ValidityMask; struct SelCache; +class Serializer; +class Deserializer; enum class VectorBufferType : uint8_t { STANDARD_BUFFER, // VectorType::FLAT/CONSTANT - Fixed-Size Type - Holds a single array of data @@ -190,6 +192,11 @@ class VectorBuffer : public enable_shared_from_this { const SelectionVector &sel, idx_t count); //! Create a UnifiedVectorFormat from the buffer's data virtual void ToUnifiedFormat(UnifiedVectorFormat &format) const; + //! Serialize the buffer as-is (including the vector_type), returns false if the vector must be serialized flat + virtual bool TrySerialize(Serializer &serializer, const LogicalType &type, bool compressed_serialization) const; + //! Deserialize a buffer written by TrySerialize + static buffer_ptr Deserialize(Deserializer &deserializer, VectorType vector_type, + const LogicalType &type, idx_t count); protected: //! Slice a constant vector with a specific count diff --git a/src/duckdb/src/include/duckdb/common/types/vector_cache.hpp b/src/duckdb/src/include/duckdb/common/types/vector_cache.hpp index f7437a2c4..309d80207 100644 --- a/src/duckdb/src/include/duckdb/common/types/vector_cache.hpp +++ b/src/duckdb/src/include/duckdb/common/types/vector_cache.hpp @@ -8,6 +8,7 @@ #pragma once +#include "duckdb/common/optional_idx.hpp" #include "duckdb/common/types.hpp" #include "duckdb/common/types/vector_buffer.hpp" #include "duckdb/common/vector.hpp" @@ -35,6 +36,8 @@ class VectorCache { public: void ResetFromCache(Vector &result) const; const LogicalType &GetType() const; + //! The capacity of the cached vector, or an invalid index if the cache is empty + optional_idx GetCapacity() const; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/vector/dictionary_vector.hpp b/src/duckdb/src/include/duckdb/common/vector/dictionary_vector.hpp index 5afa22ef3..6190cc143 100644 --- a/src/duckdb/src/include/duckdb/common/vector/dictionary_vector.hpp +++ b/src/duckdb/src/include/duckdb/common/vector/dictionary_vector.hpp @@ -8,6 +8,7 @@ #pragma once +#include "duckdb/common/once_flag.hpp" #include "duckdb/common/types/vector.hpp" namespace duckdb { @@ -26,7 +27,7 @@ class DictionaryEntry { //! fall-through), making it a global dictionary. Set only via CreateReusableGlobalDictionary. bool global_dictionary = false; //! For caching the hashes of a child buffer (mutable: cache is logically const) - mutable mutex cached_hashes_lock; + mutable OnceFlag cached_hashes_once; mutable unique_ptr cached_hashes; }; @@ -87,6 +88,8 @@ class DictionaryBuffer : public VectorBuffer { Value GetValue(const LogicalType &type, idx_t index) const override; buffer_ptr SliceWithCache(SelCache &cache, const LogicalType &type, const SelectionVector &sel, idx_t count) override; + bool TrySerialize(Serializer &serializer, const LogicalType &type, bool compressed_serialization) const override; + static buffer_ptr Deserialize(Deserializer &deserializer, const LogicalType &type, idx_t count); protected: buffer_ptr SliceInternal(const LogicalType &type, idx_t offset, idx_t end) override; diff --git a/src/duckdb/src/include/duckdb/common/vector/sequence_vector.hpp b/src/duckdb/src/include/duckdb/common/vector/sequence_vector.hpp index dc77d5e9b..39876ceb6 100644 --- a/src/duckdb/src/include/duckdb/common/vector/sequence_vector.hpp +++ b/src/duckdb/src/include/duckdb/common/vector/sequence_vector.hpp @@ -27,6 +27,8 @@ class SequenceBuffer : public VectorBuffer { idx_t GetAllocationSize() const override; string ToString(const LogicalType &type, idx_t count) const override; Value GetValue(const LogicalType &type, idx_t index) const override; + bool TrySerialize(Serializer &serializer, const LogicalType &type, bool compressed_serialization) const override; + static buffer_ptr Deserialize(Deserializer &deserializer, const LogicalType &type, idx_t count); protected: buffer_ptr FlattenSliceInternal(const LogicalType &type, const SelectionVector &sel, diff --git a/src/duckdb/src/include/duckdb/common/vector/shredded_vector.hpp b/src/duckdb/src/include/duckdb/common/vector/shredded_vector.hpp index 852b48afe..0a6fd12b8 100644 --- a/src/duckdb/src/include/duckdb/common/vector/shredded_vector.hpp +++ b/src/duckdb/src/include/duckdb/common/vector/shredded_vector.hpp @@ -31,6 +31,8 @@ class ShreddedVectorBuffer : public VectorBuffer { string ToString(const LogicalType &type, idx_t count) const override; Value GetValue(const LogicalType &type, idx_t index) const override; void SetVectorType(VectorType new_vector_type) override; + bool TrySerialize(Serializer &serializer, const LogicalType &type, bool compressed_serialization) const override; + static buffer_ptr Deserialize(Deserializer &deserializer, const LogicalType &type, idx_t count); protected: buffer_ptr SliceInternal(const LogicalType &type, idx_t offset, idx_t end) override; diff --git a/src/duckdb/src/include/duckdb/common/vector/vector_writer.hpp b/src/duckdb/src/include/duckdb/common/vector/vector_writer.hpp index 7cf533a69..1681a9aa7 100644 --- a/src/duckdb/src/include/duckdb/common/vector/vector_writer.hpp +++ b/src/duckdb/src/include/duckdb/common/vector/vector_writer.hpp @@ -500,7 +500,7 @@ class DynamicListAppender { } DynamicListAppender(const DynamicListAppender &) = delete; DynamicListAppender(DynamicListAppender &&) = delete; - ~DynamicListAppender() { + ~DynamicListAppender() { // NOLINT(bugprone-exception-escape): writer invariants make these setters non-throwing. parent.list_data[row_idx] = {base_offset, current_length}; parent.child_offset = base_offset + current_length; VectorWriterSetListSize(parent.list_vec, base_offset + current_length); diff --git a/src/duckdb/src/include/duckdb/common/vector_operations/aggregate_executor.hpp b/src/duckdb/src/include/duckdb/common/vector_operations/aggregate_executor.hpp index 693c06239..618bc14ff 100644 --- a/src/duckdb/src/include/duckdb/common/vector_operations/aggregate_executor.hpp +++ b/src/duckdb/src/include/duckdb/common/vector_operations/aggregate_executor.hpp @@ -8,6 +8,8 @@ #pragma once +#include "duckdb/common/bit_utils.hpp" +#include "duckdb/common/bitset.hpp" #include "duckdb/common/exception.hpp" #include "duckdb/common/smaller_binary.hpp" #include "duckdb/common/types/vector.hpp" @@ -312,9 +314,9 @@ class AggregateExecutor { // COUNT(*) can add run lengths directly. if (aggr_input_data.clustered) { auto &cs = *aggr_input_data.clustered; - for (idx_t r = 0; r < cs.n_group_runs; r++) { - OP::template ConstantOperation(*reinterpret_cast(cs.group_runs[r].state), - aggr_input_data, cs.group_runs[r].count); + for (auto &run : cs.runs()) { + OP::template ConstantOperation(*reinterpret_cast(run.state), + aggr_input_data, run.count); } return; } @@ -335,10 +337,9 @@ class AggregateExecutor { template static void NullaryClustUpdate(AggregateInputData &aggr_input_data, const ClusteredAggr &clustered, idx_t count) { - for (idx_t r = 0; r < clustered.n_group_runs; r++) { - OP::template ConstantOperation( - *reinterpret_cast(clustered.group_runs[r].state), aggr_input_data, - clustered.group_runs[r].count); + for (auto &run : clustered.runs()) { + OP::template ConstantOperation(*reinterpret_cast(run.state), aggr_input_data, + run.count); } } @@ -444,41 +445,78 @@ class AggregateExecutor { } } - template + template + static inline bool UpdateUnaryClusteredRange(const INPUT_TYPE *__restrict vals, LOCAL_TYPE &local, idx_t start, + idx_t count, const ValidityMask &mask) { + bool saw_value = false; + const auto end = start + count; + while (start < end) { + const auto offset = start % ValidityMask::BITS_PER_VALUE; + const auto n = MinValue(end - start, ValidityMask::BITS_PER_VALUE - offset); + const auto range_mask = ValidityMask::EntryWithValidBits(n); + auto bits = (mask.GetValidityEntry(start / ValidityMask::BITS_PER_VALUE) >> offset) & range_mask; + if (bits == range_mask) { + saw_value = true; + for (idx_t i = start; i < start + n; i++) { + OP::template UpdateClusteredLocal(local, vals[i]); + } + } else { + while (bits) { + const auto idx = start + CountZeros::Trailing(bits); + OP::template UpdateClusteredLocal(local, vals[idx]); + bits &= bits - 1; + saw_value = true; + } + } + start += n; + } + return saw_value; + } + + template static void ExecuteUnaryClusteredOpt(const INPUT_TYPE *vals, const ClusteredAggr &clustered, const ValidityMask &validity, const SelectionVector *isel = nullptr, const sel_t *cluster_iter = nullptr) { idx_t pos = 0; using local_type = clustered_local_state_t; if (cluster_iter) { - for (idx_t r = 0; r < clustered.n_group_runs; r++) { - auto &state = *reinterpret_cast(clustered.group_runs[r].state); + for (auto &run : clustered.runs()) { + auto &state = *reinterpret_cast(run.state); local_type local; OP::template InitializeClusteredLocal(local, state); - auto run_count = clustered.group_runs[r].count; + auto run_count = run.count; auto saw_value = UpdateUnaryClusteredOpt( vals, local, run_count, validity, cluster_iter + pos); OP::template FlushClusteredLocal(state, local, saw_value); pos += run_count; } } else if (isel) { - for (idx_t r = 0; r < clustered.n_group_runs; r++) { - auto &state = *reinterpret_cast(clustered.group_runs[r].state); + for (auto &run : clustered.runs()) { + auto &state = *reinterpret_cast(run.state); local_type local; OP::template InitializeClusteredLocal(local, state); - auto run_count = clustered.group_runs[r].count; + auto run_count = run.count; auto saw_value = UpdateUnaryClusteredOpt( - vals, local, run_count, validity, clustered.group_runs[r].sel, isel); + vals, local, run_count, validity, run.sel, isel); OP::template FlushClusteredLocal(state, local, saw_value); } } else { - for (idx_t r = 0; r < clustered.n_group_runs; r++) { - auto &state = *reinterpret_cast(clustered.group_runs[r].state); + for (auto &run : clustered.runs()) { + auto &state = *reinterpret_cast(run.state); local_type local; OP::template InitializeClusteredLocal(local, state); - auto run_count = clustered.group_runs[r].count; + auto run_count = run.count; + if constexpr (SPARSE) { + if (run_count && (!run.sel || run.sel[run_count - 1] - run.sel[0] == run_count - 1)) { + const auto start = run.sel ? run.sel[0] : 0; + auto saw_value = UpdateUnaryClusteredRange(vals, local, start, + run_count, validity); + OP::template FlushClusteredLocal(state, local, saw_value); + continue; + } + } auto saw_value = UpdateUnaryClusteredOpt( - vals, local, run_count, validity, clustered.group_runs[r].sel); + vals, local, run_count, validity, run.sel); OP::template FlushClusteredLocal(state, local, saw_value); } } @@ -502,8 +540,8 @@ class AggregateExecutor { } static inline bool IsDenseSingleRun(const ClusteredAggr &clustered, idx_t count) { - return clustered.n_group_runs == 1 && clustered.group_runs[0].count == count && - clustered.group_runs[0].sel == nullptr; + auto runs = clustered.runs(); + return runs.size() == 1 && runs[0].count == count && runs[0].sel == nullptr; } template @@ -520,12 +558,31 @@ class AggregateExecutor { } } + static inline bool IsSparseClusteredInput(const ValidityMask &mask, idx_t count) { + const auto max_valid = count / 4; + idx_t valid_count = 0; + const auto full_entries = count / ValidityMask::BITS_PER_VALUE; + for (idx_t entry_idx = 0; entry_idx < full_entries; entry_idx++) { + valid_count += bitset(mask.GetValidityEntry(entry_idx)).count(); + if (valid_count > max_valid) { + return false; + } + } + const auto tail_count = count % ValidityMask::BITS_PER_VALUE; + if (tail_count) { + const auto entry = mask.GetValidityEntry(full_entries) & ValidityMask::EntryWithValidBits(tail_count); + valid_count += bitset(entry).count(); + } + return valid_count <= max_valid; + } + template static void ExecuteUnaryClusteredOpt(Vector &input, const ClusteredAggr &clustered, idx_t count) { auto vals = FlatVector::GetData(input); auto &validity = FlatVector::Validity(input); if (IsDenseSingleRun(clustered, count)) { - auto &state = *reinterpret_cast(clustered.group_runs[0].state); + auto runs = clustered.runs(); + auto &state = *reinterpret_cast(runs[0].state); using local_type = clustered_local_state_t; local_type local; OP::template InitializeClusteredLocal(local, state); @@ -533,6 +590,10 @@ class AggregateExecutor { OP::template FlushClusteredLocal(state, local, saw_value); return; } + if (OP::IgnoreNull() && validity.CanHaveNull() && IsSparseClusteredInput(validity, count)) { + ExecuteUnaryClusteredOpt(vals, clustered, validity); + return; + } ExecuteUnaryClusteredDispatch(vals, clustered, validity); } @@ -544,11 +605,11 @@ class AggregateExecutor { } const auto &constant_input = *ConstantVector::GetData(input); using has_repeat_count = HasClusteredLocalRepeatCount; - for (idx_t r = 0; r < clustered.n_group_runs; r++) { - auto &state = *reinterpret_cast(clustered.group_runs[r].state); + for (auto &run : clustered.runs()) { + auto &state = *reinterpret_cast(run.state); local_type local; OP::template InitializeClusteredLocal(local, state); - auto run_count = clustered.group_runs[r].count; + auto run_count = run.count; if (run_count != 0) { UpdateUnaryClusteredLocalRepeat(local, constant_input, run_count, has_repeat_count {}); @@ -589,10 +650,9 @@ class AggregateExecutor { } auto idata = ConstantVector::GetData(input); AggregateUnaryInput unary_input(aggr_input_data, ConstantVector::Validity(input)); - for (idx_t r = 0; r < clustered.n_group_runs; r++) { - auto &state = *reinterpret_cast(clustered.group_runs[r].state); - OP::template ConstantOperation(state, *idata, unary_input, - clustered.group_runs[r].count); + for (auto &run : clustered.runs()) { + auto &state = *reinterpret_cast(run.state); + OP::template ConstantOperation(state, *idata, unary_input, run.count); } } @@ -608,11 +668,10 @@ class AggregateExecutor { input.ToUnifiedFormat(idata); auto vals = UnifiedVectorFormat::GetData(idata); AggregateUnaryInput unary_input(aggr_input_data, idata.validity); - for (idx_t r = 0; r < clustered.n_group_runs; r++) { - auto &state = *reinterpret_cast(clustered.group_runs[r].state); - OP::template ClusteredOp(state, vals, unary_input, clustered.group_runs[r].sel, - *idata.sel, idata.validity, 0, - clustered.group_runs[r].count); + for (auto &run : clustered.runs()) { + auto &state = *reinterpret_cast(run.state); + OP::template ClusteredOp(state, vals, unary_input, run.sel, *idata.sel, + idata.validity, 0, run.count); } } @@ -637,7 +696,7 @@ class AggregateExecutor { } } } - auto *cluster_iter = clustered.ClusterIter(input, count); + auto *cluster_iter = clustered.ClusterIter(input); if (cluster_iter) { ExecuteUnaryClusteredDictOpt(input, clustered, count, cluster_iter); diff --git a/src/duckdb/src/include/duckdb/common/virtual_file_system.hpp b/src/duckdb/src/include/duckdb/common/virtual_file_system.hpp index 45b39856f..50fbefecc 100644 --- a/src/duckdb/src/include/duckdb/common/virtual_file_system.hpp +++ b/src/duckdb/src/include/duckdb/common/virtual_file_system.hpp @@ -37,6 +37,8 @@ class VirtualFileSystem : public FileSystem { string GetVersionTag(FileHandle &handle) override; FileType GetFileType(FileHandle &handle) override; FileMetadata Stats(FileHandle &handle) override; + optional GetStatsIfExists(const OpenFileInfo &file, + optional_ptr opener = nullptr) override; void Truncate(FileHandle &handle, int64_t new_size) override; diff --git a/src/duckdb/src/include/duckdb/common/winapi.hpp b/src/duckdb/src/include/duckdb/common/winapi.hpp index 67287565f..3bde64945 100644 --- a/src/duckdb/src/include/duckdb/common/winapi.hpp +++ b/src/duckdb/src/include/duckdb/common/winapi.hpp @@ -13,7 +13,7 @@ #ifdef DUCKDB_STATIC_BUILD #define DUCKDB_API #else -#if defined(DUCKDB_BUILD_LIBRARY) && !defined(DUCKDB_BUILD_LOADABLE_EXTENSION) +#ifdef DUCKDB_BUILD_LIBRARY #define DUCKDB_API __declspec(dllexport) #else #define DUCKDB_API __declspec(dllimport) diff --git a/src/duckdb/src/include/duckdb/execution/aggregate_hashtable.hpp b/src/duckdb/src/include/duckdb/execution/aggregate_hashtable.hpp index d3aab0a8a..696ee5b68 100644 --- a/src/duckdb/src/include/duckdb/execution/aggregate_hashtable.hpp +++ b/src/duckdb/src/include/duckdb/execution/aggregate_hashtable.hpp @@ -11,10 +11,10 @@ #include "duckdb/common/row_operations/row_matcher.hpp" #include "duckdb/common/types/row/partitioned_tuple_data.hpp" #include "duckdb/execution/base_aggregate_hashtable.hpp" +#include "duckdb/execution/aggregate_ht_adaptivity_state.hpp" #include "duckdb/execution/ht_entry.hpp" #include "duckdb/storage/arena_allocator.hpp" #include "duckdb/common/row_operations/row_operations.hpp" -#include "duckdb/common/types/hyperloglog.hpp" #include "duckdb/common/clustered_aggregate.hpp" #include "duckdb/common/atomic.hpp" @@ -145,12 +145,14 @@ class GroupedAggregateHashTable : public BaseAggregateHashTable { idx_t group_count, DataChunk &result) const; const PartitionedTupleData &GetPartitionedData() const; + //! Retained tuple blocks, allocator metadata, aggregate arena, and pointer table + idx_t GetSizeInBytes() const; unique_ptr AcquirePartitionedData(); void Abandon(); void Repartition(); shared_ptr GetAggregateAllocator(); - //! Resize the HT to the specified size. Must be larger than the current size. + //! Resize an empty HT, or grow an HT with no abandoned groups. void Resize(idx_t size); //! Resets the pointer table of the HT to all 0's void ClearPointerTable(); @@ -158,22 +160,23 @@ class GroupedAggregateHashTable : public BaseAggregateHashTable { void SetRadixBits(idx_t radix_bits); //! Get the radix bits for this HT idx_t GetRadixBits() const; - //! Get the total number of tuples sunk into this HT - idx_t GetSinkCount() const; //! Get the total number of tuples materialized currently in this HT idx_t GetMaterializedCount() const; - //! Skips lookups from here on out + //! Read-only adaptivity observations; mutations follow the HT's lifecycle. + const AggregateHTAdaptivityState &GetAdaptivityState() const { + return adaptivity; + } + //! Skip lookups until resuming at an empty pointer table void SkipLookups(); - //! Enable/disable HLL + //! Resume lookups after abandoning the previous pointer table. + void ResumeLookups(); + //! Enable/disable HLL; a fresh sketch must start before inserting groups void EnableHLL(bool enable); - //! Whether HLL is enabled - bool HLLEnabled() const; - //! Get HLL count - idx_t GetHLLUpperBound() const; //! Executes the filter(if any) and update the aggregates void Combine(GroupedAggregateHashTable &other); - void Combine(TupleDataCollection &other_data, optional_ptr> progress = nullptr); + //! Combines the data into this HT - combined_chunks (if set) is incremented for every combined chunk + void Combine(TupleDataCollection &other_data, optional_ptr> combined_chunks = nullptr); //! Reset the HT for a new execution while reusing internal allocations where possible void ResetForNewIteration(idx_t radix_bits); @@ -214,6 +217,12 @@ class GroupedAggregateHashTable : public BaseAggregateHashTable { //! The data of the HT unique_ptr partitioned_data; unique_ptr unpartitioned_data; + //! Cache tuple allocations while the materialized groups and their storage stay unchanged. + struct TupleSizeCache { + optional_idx count; + idx_t size = 0; + }; + mutable TupleSizeCache tuple_size_cache; //! Predicates for matching groups (always ExpressionType::COMPARE_EQUAL) vector predicates; @@ -230,14 +239,7 @@ class GroupedAggregateHashTable : public BaseAggregateHashTable { //! Bitmask for getting relevant bits from the hashes to determine the position hash_t bitmask; - //! How many tuples went into this HT (before de-duplication) - idx_t sink_count; - //! If true, we just append, skipping HT lookups - bool skip_lookups; - //! Whether to enable HLL counting the hashes - bool enable_hll; - //! The associated HLL - HyperLogLog hll; + AggregateHTAdaptivityState adaptivity; //! The active arena allocator used by the aggregates for their internal state shared_ptr aggregate_allocator; diff --git a/src/duckdb/src/include/duckdb/execution/aggregate_ht_adaptivity_state.hpp b/src/duckdb/src/include/duckdb/execution/aggregate_ht_adaptivity_state.hpp new file mode 100644 index 000000000..355bf18cc --- /dev/null +++ b/src/duckdb/src/include/duckdb/execution/aggregate_ht_adaptivity_state.hpp @@ -0,0 +1,89 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/execution/aggregate_ht_adaptivity_state.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/types/hyperloglog.hpp" +#include "duckdb/common/vector/vector_iterator.hpp" + +namespace duckdb { + +//! Observations and lookup mode owned by a grouped aggregate HT. +class AggregateHTAdaptivityState { +public: + idx_t GetSinkCount() const { + return sink_count; + } + //! Logical input since the current pointer table was cleared + idx_t GetCycleInputCount() const { + return sink_count - sink_count_at_abandon; + } + bool LookupsSkipped() const { + return skip_lookups; + } + //! Logical input since lookups were skipped + idx_t GetSkippedInputCount() const { + return skip_lookups ? sink_count - sink_count_at_skip : 0; + } + bool HLLEnabled() const { + return enable_hll; + } + idx_t GetHLLUpperBound() const { + D_ASSERT(enable_hll); + return LossyNumericCast((1 + HyperLogLogP<8>::GetErrorRate()) * static_cast(hll.Count())); + } + +private: + friend class GroupedAggregateHashTable; + + void AddInput(idx_t count) { + sink_count += count; + } + void BeginCycle() { + sink_count_at_abandon = sink_count; + } + void SkipLookups() { + skip_lookups = true; + sink_count_at_skip = sink_count; + } + void ResumeLookups() { + skip_lookups = false; + } + void EnableHLL(bool enable) { + enable_hll = enable; + } + void ObserveHashes(const Vector &hashes) { + D_ASSERT(enable_hll); + hll.Update(hashes); + } + void ObserveHashes(const VectorIterator &hashes, const SelectionVector &new_groups, idx_t count) { + D_ASSERT(enable_hll); + // Lookup hits were already observed when their groups were inserted. + for (idx_t i = 0; i < count; i++) { + hll.InsertElement(hashes[new_groups.get_index(i)].GetValue()); + } + } + void Reset() { + *this = AggregateHTAdaptivityState(); + } + +private: + //! How many tuples went into this HT (before de-duplication) + idx_t sink_count = 0; + //! Start of the current pointer-table fill cycle + idx_t sink_count_at_abandon = 0; + //! If true, we just append, skipping HT lookups + bool skip_lookups = false; + //! Start of the current append-only interval + idx_t sink_count_at_skip = 0; + //! Whether to enable HLL counting the hashes + bool enable_hll = false; + HyperLogLogP<8> hll; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/column_binding_resolver.hpp b/src/duckdb/src/include/duckdb/execution/column_binding_resolver.hpp index 81a6e1715..655fe8a73 100644 --- a/src/duckdb/src/include/duckdb/execution/column_binding_resolver.hpp +++ b/src/duckdb/src/include/duckdb/execution/column_binding_resolver.hpp @@ -20,6 +20,7 @@ namespace duckdb { class ColumnBindingResolver : public LogicalOperatorVisitor { public: explicit ColumnBindingResolver(bool verify_only = false); + DUCKDB_API ColumnBindingResolver(vector bindings, vector types); void VisitOperator(LogicalOperator &op) override; diff --git a/src/duckdb/src/include/duckdb/execution/executor.hpp b/src/duckdb/src/include/duckdb/execution/executor.hpp index 1ccf7468f..9e17a1736 100644 --- a/src/duckdb/src/include/duckdb/execution/executor.hpp +++ b/src/duckdb/src/include/duckdb/execution/executor.hpp @@ -9,7 +9,7 @@ #pragma once #include "duckdb/common/common.hpp" -#include "duckdb/common/enums/pending_execution_result.hpp" +#include "duckdb/common/enums/query_result_state.hpp" #include "duckdb/common/mutex.hpp" #include "duckdb/common/pair.hpp" #include "duckdb/common/reference_map.hpp" @@ -21,10 +21,12 @@ #include namespace duckdb { +class BufferedData; class ClientContext; class DataChunk; class PhysicalOperator; class PipelineExecutor; +class ProgressVerifier; class OperatorState; class QueryProfiler; class ThreadContext; @@ -60,7 +62,10 @@ class Executor { bool HasTaskInProgress() const { return task != nullptr; } - PendingExecutionResult ExecuteTask(bool dry_run = false); + //! Run one partial task slice on the calling thread and report the resulting state + QueryResultState ExecuteTask(); + //! Report the execution state without running any task + QueryResultState Poll(); void WaitForTask(); void SignalTaskRescheduled(lock_guard &); @@ -120,6 +125,10 @@ class Executor { } void UnregisterTask(); + //! Set the buffer of the result this query produces. Called at submission, before execution starts + void SetResultBuffer(shared_ptr result_buffer_p); + shared_ptr GetResultBuffer(); + idx_t GetTotalPipelines() const { return total_pipelines; } @@ -128,10 +137,18 @@ class Executor { return completed_pipelines.load(); } + //! The progress verifier of this query (if debug_verify_progress is enabled) + optional_ptr GetProgressVerifier() { + return progress_verifier.get(); + } + private: - //! Whether a producer is parked on the result sink's buffer. A parked producer - //! implies a poppable chunk, and only consumption restarts it + //! Whether the result sink waits on the consumer: a producer is parked for the retention + //! decision, or for space that only a pop frees bool ResultCollectorIsBlocked(); + //! Whether this query's store can park a producer for the consumer at all. A store settled on + //! retained never parks, so the retained hot path skips the readiness checks + bool ResultStoreCanPark(); void InitializeInternal(PhysicalOperator &physical_plan); void ScheduleEvents(const vector> &meta_pipelines); @@ -142,6 +159,12 @@ class Executor { vector &visited, vector &recursion_stack); bool NextExecutor(); + //! The state to report when this thread has no task to run + QueryResultState IdleState(); + //! Cancel all tasks and throw the recorded error + void FailExecution(); + //! Advance to the next executor, or record and return FINISHED + QueryResultState FinishExecution(); shared_ptr CreateChildPipeline(Pipeline ¤t, PhysicalOperator &op); @@ -171,6 +194,8 @@ class Executor { shared_ptr profiler; //! Task error manager TaskErrorManager error_manager; + //! Verifies the progress reported by the pipelines (debug setting) + unique_ptr progress_verifier; //! The amount of completed pipelines of the query atomic completed_pipelines; @@ -180,7 +205,7 @@ class Executor { bool cancelled; //! The last pending execution result (if any) - PendingExecutionResult execution_result; + QueryResultState execution_result; //! The current task in process (if any) shared_ptr task; @@ -191,6 +216,11 @@ class Executor { //! Currently alive executor tasks atomic executor_tasks; + //! Leaf lock for the result buffer slot. It must not share executor_lock, which is held while + //! readiness is checked + mutex result_buffer_lock; + //! The buffer of the result this query produces, or null for a query that has none + shared_ptr result_buffer; //! Total time blocked while waiting on tasks, in microseconds atomic blocked_thread_time; diff --git a/src/duckdb/src/include/duckdb/execution/expression_executor.hpp b/src/duckdb/src/include/duckdb/execution/expression_executor.hpp index 98440b84d..6fda56208 100644 --- a/src/duckdb/src/include/duckdb/execution/expression_executor.hpp +++ b/src/duckdb/src/include/duckdb/execution/expression_executor.hpp @@ -144,6 +144,8 @@ class ExpressionExecutor { idx_t count, SelectionVector *true_sel, SelectionVector *false_sel); idx_t Select(const BoundFunctionExpression &expr, ExpressionState *state, const SelectionVector *sel, idx_t count, SelectionVector *true_sel, SelectionVector *false_sel); + idx_t Select(const BoundOperatorExpression &expr, ExpressionState &state, optional_ptr sel, + idx_t count, optional_ptr true_sel, optional_ptr false_sel); //! Verify that the output of a step in the ExpressionExecutor is correct void Verify(const Expression &expr, Vector &result, idx_t count); diff --git a/src/duckdb/src/include/duckdb/execution/expression_executor_state.hpp b/src/duckdb/src/include/duckdb/execution/expression_executor_state.hpp index 96c1612de..a2b86440c 100644 --- a/src/duckdb/src/include/duckdb/execution/expression_executor_state.hpp +++ b/src/duckdb/src/include/duckdb/execution/expression_executor_state.hpp @@ -74,6 +74,8 @@ struct ExecuteFunctionState : public ExpressionState { public: unique_ptr local_state; + //! The buffers of all-constant arguments, which can be shared with the input and have to get their size back + vector> constant_argument_buffers; private: //! The column index of the "unary" input column that may be a dictionary vector diff --git a/src/duckdb/src/include/duckdb/execution/ie_join_union.hpp b/src/duckdb/src/include/duckdb/execution/ie_join_union.hpp new file mode 100644 index 000000000..9ab28bfb9 --- /dev/null +++ b/src/duckdb/src/include/duckdb/execution/ie_join_union.hpp @@ -0,0 +1,209 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/execution/ie_join_union.hpp +// +//===----------------------------------------------------------------------===// +#pragma once +#include "duckdb/execution/operator/join/physical_range_join.hpp" +#include "duckdb/common/types/column/column_data_collection.hpp" +#include "duckdb/common/types/column/column_data_scan_states.hpp" +#include "duckdb/common/vector/flat_vector.hpp" + +namespace duckdb { + +template +class IEJoinCursor { +public: + explicit IEJoinCursor(ColumnDataCollection &collection) : collection(collection) { + collection.InitializeScan(state); + collection.InitializeScanChunk(state, chunk); + } + + //! The row count of the paged collection + idx_t size() const { // NOLINT + return collection.Count(); + } + + //! Read a typed cell + const T &operator[](idx_t row_idx) { + auto index = Seek(row_idx); + const auto &source = chunk.data[0]; + const auto data_ptr = reinterpret_cast(FlatVector::GetData(source)); + return data_ptr[index]; + } + +private: + //! Is the scan in range? + inline bool RowIsVisible(idx_t row_idx) const { + return (row_idx < state.next_row_index && state.current_row_index <= row_idx); + } + //! The offset of the row in the given state + inline sel_t RowOffset(idx_t row_idx) const { + D_ASSERT(RowIsVisible(row_idx)); + return UnsafeNumericCast(row_idx - state.current_row_index); + } + //! Scan the next chunk + inline bool Scan() { + return collection.Scan(state, chunk); + } + //! Seek to the given row + inline idx_t Seek(idx_t row_idx) { + if (!RowIsVisible(row_idx)) { + collection.Seek(row_idx, state, chunk); + } + return RowOffset(row_idx); + } + + //! The pageable data + const ColumnDataCollection &collection; + //! The state used for reading the collection + ColumnDataScanState state; + //! The data chunk read into + DataChunk chunk; +}; + +struct IEJoinUnion { + using SortedTable = PhysicalRangeJoin::GlobalSortedTable; + using ChunkRange = std::pair; + + // Comparison utilities + static bool IsStrictComparison(ExpressionType comparison) { + switch (comparison) { + case ExpressionType::COMPARE_LESSTHAN: + case ExpressionType::COMPARE_GREATERTHAN: + return true; + case ExpressionType::COMPARE_LESSTHANOREQUALTO: + case ExpressionType::COMPARE_GREATERTHANOREQUALTO: + return false; + default: + throw InternalException("Unimplemented comparison type for IEJoin!"); + } + } + + template + static inline bool Compare(const T &lhs, const T &rhs, const bool strict) { + const bool less_than = lhs < rhs; + if (!less_than && !strict) { + return !(rhs < lhs); + } + return less_than; + } + + static idx_t AppendKey(ExecutionContext &context, InterruptState &interrupt, SortedTable &table, + ExpressionExecutor &executor, SortedTable &marked, int64_t increment, int64_t rid, + const ChunkRange &range); + + static unique_ptr ExtractColumn(SortedTable &table, idx_t col_idx, + BufferManager &buffer_manager) { + auto &collection = *table.sorted->payload_data; + vector scan_ids(1, col_idx); + TupleDataScanState scan_state; + collection.InitializeScan(scan_state, scan_ids); + + DataChunk payload; + collection.InitializeScanChunk(scan_state, payload); + + auto result = make_uniq(buffer_manager, payload.GetTypes()); + ColumnDataAppendState append_state; + result->InitializeAppend(append_state); + while (collection.Scan(scan_state, payload)) { + result->Append(append_state, payload); + } + + return result; + } + + class UnionIterator { + public: + UnionIterator(SortedTable &table, bool strict) : state(table.CreateIteratorState()), strict(strict) { + } + + inline idx_t GetIndex() const { + return index; + } + + inline void SetIndex(idx_t i) { + index = i; + } + + inline idx_t GetChunkIndex() const { + idx_t chunk_idx; + idx_t tuple_idx; + state->RandomAccess(chunk_idx, tuple_idx, index); + return chunk_idx; + } + + inline void SetChunkIndex(idx_t chunk_idx) { + index = state->GetDivisor() * chunk_idx; + } + + UnionIterator &operator++() { + ++index; + return *this; + } + + void Repin() { + state->SetKeepPinned(true); + state->SetPinPayload(true); + } + + unique_ptr state; + idx_t index = 0; + const bool strict; + }; + + IEJoinUnion(SortedTable &l2, ColumnDataCollection &li, ColumnDataCollection &p, + const vector &conditions, const ChunkRange &chunks); + + static void InitializeTables(ClientContext &client, const PhysicalComparisonJoin &op, + const vector &conditions, unique_ptr &l1, + unique_ptr &l2); + + //! Start the current row. + //! Returns false if there are no more rows to process + template + bool NextRow(); + + //! NextRow pointer to member for the sort key type. + using next_row_t = bool (duckdb::IEJoinUnion::*)(); + next_row_t next_row_func; + + //! Finish this row and move to the next one. + //! Returns false if there are no more rows to process + bool FinishRow() { + ++i; + return (this->*next_row_func)(); + } + + //! Inverted loop + idx_t JoinBlocks(unsafe_vector &lsel, unsafe_vector &rsel); + + //! B + vector bit_array; + ValidityMask bit_mask; + //! Bloom Filter + static constexpr idx_t BLOOM_CHUNK_BITS = 1024; + idx_t bloom_count; + vector bloom_array; + ValidityMask bloom_filter; + + //! Iteration state + idx_t n; + idx_t i; + idx_t n_j; + idx_t j; + unique_ptr op2; + unique_ptr off2; + int64_t lrid = std::numeric_limits::max(); + + //! ANTI JOIN bookmark + idx_t anti_i; + + //! Li + IEJoinCursor li; + //! P + IEJoinCursor p; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/index/unbound_index.hpp b/src/duckdb/src/include/duckdb/execution/index/unbound_index.hpp index c31deec2c..08e7a9510 100644 --- a/src/duckdb/src/include/duckdb/execution/index/unbound_index.hpp +++ b/src/duckdb/src/include/duckdb/execution/index/unbound_index.hpp @@ -61,7 +61,7 @@ struct BufferedIndexReplays { } }; -class UnboundIndex final : public Index { +class UnboundIndex : public Index { private: //! The CreateInfo of the index. unique_ptr create_info; diff --git a/src/duckdb/src/include/duckdb/execution/join_hashtable.hpp b/src/duckdb/src/include/duckdb/execution/join_hashtable.hpp index d0af8bdd1..15860231e 100644 --- a/src/duckdb/src/include/duckdb/execution/join_hashtable.hpp +++ b/src/duckdb/src/include/duckdb/execution/join_hashtable.hpp @@ -37,11 +37,13 @@ struct JoinHTScanState { public: JoinHTScanState(TupleDataCollection &collection, idx_t chunk_idx_from, idx_t chunk_idx_to, TupleDataPinProperties properties = TupleDataPinProperties::ALREADY_PINNED) - : iterator(collection, properties, chunk_idx_from, chunk_idx_to, false), offset_in_chunk(0) { + : iterator(collection, properties, chunk_idx_from, chunk_idx_to, false), offset_in_chunk(0), chunks_done(0) { } TupleDataChunkIterator iterator; idx_t offset_in_chunk; + //! The number of chunks that have been scanned entirely + idx_t chunks_done; private: //! Implicit copying is not allowed @@ -63,10 +65,55 @@ struct JoinHTScanState { Two-phase lifecycle: the constructor populates only layout-INDEPENDENT state; all layout-DEPENDENT state (layout_ptr, row matchers, tuple_size/pointer_offset/entry_size, data_collection, sink_collection, dead_end, - dict_registry) is published by FinishInitWithLayout on the first build chunk, so slot widths can be chosen from - the data's actual runtime encoding. Until then the JHT is unusable except for the null-safe Count() / - SizeInBytes() accessors; the layout-dependent accessors assert IsLayoutFinalized(). + dict_info) is published by FinishInitWithLayout on the first build chunk, so slot widths (and the dictionary + a narrowed slot indexes into) can be chosen from the data's actual runtime encoding. Until then the JHT is + unusable except for the null-safe Count() / SizeInBytes() accessors; the layout-dependent accessors assert + IsLayoutFinalized(). ResetForNewIterationSinglePartition returns the JHT to the unpublished state so a recursive + CTE iteration publishes afresh. */ +//! One build payload column of the dict-surviving optimisation: the row store carries a narrow index into `entry` +//! instead of the native value. +struct DictSurvivingColumn { + //! Byte width of the narrowed dict-index slot (1/2/4), 0 when the column is stored at native width + uint8_t index_width = 0; + //! Self-owned copy of the upstream dictionary plus one trailing NULL slot; set iff narrowed + buffer_ptr entry; + //! Id of the upstream dictionary the column was narrowed against; later chunks must carry the same id + string upstream_id; + + //! The reserved trailing slot that encodes a NULL-only chunk + idx_t NullIndex() const { + return entry->data.size() - 1; + } +}; + +//! Descriptor of the dict-surviving optimisation for one hash join. Chosen by the layout publisher from the first +//! build chunk together with the layout and shared read-only by every per-thread JHT. Re-chosen whenever the layout +//! is (e.g. per recursive CTE iteration), since a rerun build may deliver a different dictionary or none at all. +struct DictSurvivingInfo { + //! One record per build payload column (parallel to build_types); index_width 0 = stored at native width + vector columns; + + //! True iff any column is narrowed + bool Any() const { + for (const auto &column : columns) { + if (column.index_width != 0) { + return true; + } + } + return false; + } + //! The record of build payload column build_col_idx, or nullptr when it is stored at native width + optional_ptr Get(idx_t build_col_idx) const { + if (build_col_idx >= columns.size() || columns[build_col_idx].index_width == 0) { + return nullptr; + } + return &columns[build_col_idx]; + } + //! Row-store slot type of a narrowed column of the given index width + static LogicalType IndexType(uint8_t index_width); +}; + class JoinHashTable { public: using ValidityBytes = TemplatedValidityMask; @@ -238,16 +285,31 @@ class JoinHashTable { optional_ptr predicate_ptr = nullptr, const vector &output_in_probe = {}); ~JoinHashTable(); - //! Initialize layout-dependent state from a layout shared across all per-thread JHTs (deferred ctor body) - void FinishInitWithLayout(shared_ptr published_layout, vector dict_index_width_p = {}); + //! Initialize layout-dependent state from a layout (and dict-surviving descriptor, nullptr = no narrowed column) + //! shared across all per-thread JHTs (deferred ctor body) + void FinishInitWithLayout(shared_ptr published_layout, + shared_ptr dict_info_p = nullptr); //! True iff FinishInitWithLayout has populated layout-dependent state bool IsLayoutFinalized() const { return layout_ptr.get() != nullptr; } //! Per-column index-width decision for the dict-surviving optimisation, consulted by the layout publisher on - //! the first build chunk. Returns the narrowed index byte width (1/2/4), or 0 to keep native width. + //! the first build chunk. Returns the narrowed index byte width (1/2/4), or 0 to keep native width. The width + //! leaves room for one extra slot: the reserved NULL index that encodes NULL-only chunks. uint8_t GetDictSurvivingIndexWidth(idx_t build_col_idx, const Vector &incoming) const; + //! Create the record of a narrowed column: a self-owned, global copy of the incoming global dictionary with one + //! trailing NULL slot (index = upstream size) and a fresh id, plus the width and the upstream id to check later + //! chunks against. + static DictSurvivingColumn CreateDictSurvivingColumn(const Vector &incoming, uint8_t index_width); + //! True iff any build payload column is stored as a narrow dictionary index + bool HasDictSurvivingColumns() const { + return dict_info && dict_info->Any(); + } + //! The dict-surviving record of build payload column build_col_idx, or nullptr when it is stored natively + optional_ptr GetDictSurvivingColumn(idx_t build_col_idx) const { + return dict_info ? dict_info->Get(build_col_idx) : nullptr; + } //! Add the given data to the HT void Build(PartitionedTupleDataAppendState &append_state, DataChunk &keys, DataChunk &input); @@ -408,12 +470,9 @@ class JoinHashTable { bool use_dict_emission = false; //! Pre-materialized columnar data, one entry per RHS output column vector> dict_arrays; - //! Per build payload column: pinned upstream dict entry. Non-null means the row store carries a narrow dict - //! index for this column instead of the native value. - vector> dict_registry; - //! Per build payload column: byte width of the narrowed dict-index slot (0 = native, else 1/2/4). Parallel to - //! build_types; set by FinishInitWithLayout. - vector dict_index_width; + //! Dict-surviving descriptor shared with every other per-thread JHT of the same join; set by + //! FinishInitWithLayout, nullptr when no build payload column is narrowed + shared_ptr dict_info; //! Saved NEXT_PTR values, indexed by dict index; only allocated when chains_longer_than_one AllocatedData aux_next_ptrs; //! Typed pointer into aux_next_ptrs; set by BuildDictionaryArrays alongside the allocation @@ -495,10 +554,12 @@ class JoinHashTable { //! An empty tuple that's a "dead end", can be used to stop chains early unsafe_unique_array dead_end; - //! Whether or not to use a bloom filter will be determined by the operator - BloomFilter bloom_filter; + //! Bloom filter over the join keys. An external join fills it during Sink, an in-memory join during + //! finalize. Shared by every thread-local hash table of this operator, and shared again with any scan it + //! gets published to, so it outlives a reset of this table + shared_ptr bloom_filter; + //! Whether finalize should build the bloom filter from the materialized build side bool should_build_bloom_filter = false; - idx_t bloom_filter_init_count = 0; unique_ptr prefix_range_filter; bool should_build_prefix_range_filter = false; @@ -580,14 +641,27 @@ class JoinHashTable { return PointerTableCapacity(count) * sizeof(data_ptr_t); } + void SetBloomFilter(shared_ptr filter) { + bloom_filter = std::move(filter); + } + //! Requests a filter built during finalize, from the materialized build side (in-memory joins only) void SetBuildBloomFilter(const bool should_build) { this->should_build_bloom_filter = should_build; } - void PrepareBloomFilterForFinalize(); - BloomFilter &GetBloomFilter() { + shared_ptr GetBloomFilter() { return bloom_filter; } + //! Marks the bloom filter complete. The reference stays, because the thread-local tables hold it too + void SealBloomFilter() { + if (bloom_filter) { + bloom_filter->Seal(context); + } + } + //! Whether the filter is complete and rejects enough for pushing it into a scan to pay off + bool HasPublishableBloomFilter() const { + return bloom_filter && bloom_filter->IsSealed() && bloom_filter->IsUseful(); + } void SetPrefixRangeFilter(unique_ptr filter) { prefix_range_filter = std::move(filter); @@ -649,9 +723,11 @@ class JoinHashTable { private: //! True iff the residual predicate (if any) reads build payload column build_col_idx from its row slot bool ColumnReferencedByResidual(idx_t build_col_idx) const; - //! Validate the incoming dict chunk and pin a self-owned copy of its dictionary into dict_registry on the first - //! chunk; on later chunks assert id continuity. Called per narrowed column from Build. - void PinDictSurvivingColumn(idx_t build_col_idx, const Vector &incoming, uint8_t index_width); + //! Materialise the narrow index vector for a narrowed column: the dictionary indices of a chunk that wraps the + //! pinned upstream dictionary, or the reserved NULL index for a NULL-only chunk (e.g. the unmatched-build scan of + //! a FULL/RIGHT join upstream). Any other shape means the build pipeline is not single-source and throws. + void EncodeDictSurvivingColumn(const DictSurvivingColumn &column, idx_t build_col_idx, const Vector &incoming, + idx_t count, vector &buffers, vector &vectors); private: //! The current number of radix bits used to partition diff --git a/src/duckdb/src/include/duckdb/execution/mark_join_row_comparison.hpp b/src/duckdb/src/include/duckdb/execution/mark_join_row_comparison.hpp index 4ea7bc3a8..52ac55a23 100644 --- a/src/duckdb/src/include/duckdb/execution/mark_join_row_comparison.hpp +++ b/src/duckdb/src/include/duckdb/execution/mark_join_row_comparison.hpp @@ -8,11 +8,13 @@ #pragma once -#include "duckdb/common/types/vector.hpp" +#include "duckdb/common/enums/expression_type.hpp" +#include "duckdb/common/types/data_chunk.hpp" namespace duckdb { struct MarkJoinRowComparison { + static void Compare(const Vector &left, const Vector &right, ExpressionType comparison_type, Vector &result); static void CompareEquality(const Vector &left, idx_t left_row, idx_t left_count, const Vector &right, idx_t right_count, bool row_is_false[], bool row_is_unknown[]); }; diff --git a/src/duckdb/src/include/duckdb/execution/merge_sort_tree.hpp b/src/duckdb/src/include/duckdb/execution/merge_sort_tree.hpp index 8cb9823d0..9ed0dc06b 100644 --- a/src/duckdb/src/include/duckdb/execution/merge_sort_tree.hpp +++ b/src/duckdb/src/include/duckdb/execution/merge_sort_tree.hpp @@ -14,6 +14,7 @@ #include "duckdb/common/mutex.hpp" #include "duckdb/common/pair.hpp" #include "duckdb/common/printer.hpp" +#include "duckdb/common/stl_allocator.hpp" #include "duckdb/common/typedefs.hpp" #include "duckdb/common/vector.hpp" #include "duckdb/common/vector_operations/aggregate_executor.hpp" @@ -77,8 +78,9 @@ template , u struct MergeSortTree { using ElementType = E; using OffsetType = O; - using Elements = vector; - using Offsets = vector; + //! The levels are allocated through the tree's allocator so that their memory can be tracked + using Elements = vector>; + using Offsets = vector>; using Level = pair; using Tree = vector; @@ -105,7 +107,10 @@ struct MergeSortTree { CMP cmp; }; - explicit MergeSortTree(const CMP &cmp = CMP()) : cmp(cmp) { + explicit MergeSortTree(Allocator &allocator = Allocator::DefaultAllocator(), const CMP &cmp = CMP()) + : allocator(allocator), cmp(cmp) { + } + explicit MergeSortTree(const CMP &cmp) : MergeSortTree(Allocator::DefaultAllocator(), cmp) { } inline Elements &LowestLevel() { @@ -133,6 +138,8 @@ struct MergeSortTree { template void AggregateLowerBound(const idx_t lower, const idx_t upper, const E needle, L aggregate) const; + //! The allocator used for the levels of the tree + Allocator &allocator; Tree tree; CompareElements cmp; @@ -312,21 +319,22 @@ struct MergeSortTree { }; template -vector &MergeSortTree::Allocate(idx_t count) { +typename MergeSortTree::Elements &MergeSortTree::Allocate(idx_t count) { const auto fanout = F; const auto cascading = C; - Elements lowest_level(count); - tree.emplace_back(Level(std::move(lowest_level), Offsets())); + Elements lowest_level(allocator); + lowest_level.resize(count); + tree.emplace_back(Level(std::move(lowest_level), Offsets(allocator))); for (idx_t child_run_length = 1; child_run_length < count;) { const auto run_length = child_run_length * fanout; const auto num_runs = (count + run_length - 1) / run_length; - Elements elements; + Elements elements(allocator); elements.resize(count); // Allocate cascading pointers only if there is room - Offsets cascades; + Offsets cascades(allocator); if (cascading > 0 && run_length > cascading) { const auto num_cascades = fanout * num_runs * (run_length / cascading + 2); cascades.resize(num_cascades); @@ -498,6 +506,7 @@ pair MergeSortTree::SelectNth(const SubFrames &fr // Go over all children until we found enough in range const auto *level_data = tree[level_no].first.data(); + const auto level_size = tree[level_no].first.size(); while (true) { idx_t matched = 0; std::array matches; @@ -518,7 +527,8 @@ pair MergeSortTree::SelectNth(const SubFrames &fr matched += idx_t(match.second - match.first); } - if (matched > n) { + // Move down if there are enough in this child, or if this is the last child of the level + if (matched > n || (result + 1) * level_width >= level_size) { // Too much in this level, so move down to leftmost child candidate within the cascade range for (idx_t f = 0; f < frames.size(); ++f) { auto &cascade_idx = cascades[f]; diff --git a/src/duckdb/src/include/duckdb/execution/operator/aggregate/physical_limited_distinct.hpp b/src/duckdb/src/include/duckdb/execution/operator/aggregate/physical_limited_distinct.hpp index 3d154a6ca..64a6705f4 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/aggregate/physical_limited_distinct.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/aggregate/physical_limited_distinct.hpp @@ -40,6 +40,9 @@ class PhysicalLimitedDistinct : public PhysicalOperator { SourceResultType GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const override; + ProgressData GetProgress(ClientContext &context, GlobalSourceState &gstate) const override; + void SourceFinished(ClientContext &context, GlobalSourceState &gstate) const override; + bool IsSource() const override { return true; } diff --git a/src/duckdb/src/include/duckdb/execution/operator/aggregate/physical_perfecthash_aggregate.hpp b/src/duckdb/src/include/duckdb/execution/operator/aggregate/physical_perfecthash_aggregate.hpp index 06db430a0..ee191422a 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/aggregate/physical_perfecthash_aggregate.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/aggregate/physical_perfecthash_aggregate.hpp @@ -35,6 +35,8 @@ class PhysicalPerfectHashAggregate : public PhysicalOperator { public: // Source interface unique_ptr GetGlobalSourceState(ClientContext &context) const override; + ProgressData GetProgress(ClientContext &context, GlobalSourceState &gstate) const override; + void SourceFinished(ClientContext &context, GlobalSourceState &gstate) const override; SourceResultType GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const override; diff --git a/src/duckdb/src/include/duckdb/execution/operator/aggregate/physical_ungrouped_aggregate.hpp b/src/duckdb/src/include/duckdb/execution/operator/aggregate/physical_ungrouped_aggregate.hpp index b46987e63..f2ff51ad7 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/aggregate/physical_ungrouped_aggregate.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/aggregate/physical_ungrouped_aggregate.hpp @@ -35,6 +35,8 @@ class PhysicalUngroupedAggregate : public PhysicalOperator { public: // Source interface + unique_ptr GetGlobalSourceState(ClientContext &context) const override; + ProgressData GetProgress(ClientContext &context, GlobalSourceState &gstate) const override; SourceResultType GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const override; diff --git a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_error.hpp b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_error.hpp index 597bcb169..fbc2cb086 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_error.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_error.hpp @@ -118,7 +118,8 @@ class CSVError { class CSVErrorHandler { public: - explicit CSVErrorHandler(bool ignore_errors = false); + //! If store_ignored_errors is false, ignored errors are dropped instead of kept for the rejects table + explicit CSVErrorHandler(bool ignore_errors = false, bool store_ignored_errors = true); //! Throws the error void Error(const CSVError &csv_error, bool force_error = false); //! If we have a cached error, and we can now error, we error. @@ -143,7 +144,7 @@ class CSVErrorHandler { idx_t GetSize(); void FillRejectsTable(InternalAppender &errors_appender, idx_t file_idx, idx_t scan_idx, const CSVFileScan &file, - CSVRejectsTable &rejects, const MultiFileBindData &bind_data, idx_t limit); + CSVRejectsTable &rejects, const vector &column_names, idx_t limit); private: //! Private methods should always be locked by parent method. @@ -161,6 +162,8 @@ class CSVErrorHandler { unordered_map lines_per_batch_map; idx_t max_line_length = 0; bool ignore_errors = false; + //! Whether ignored errors have to be kept, e.g. to fill the rejects table + bool store_ignored_errors = true; bool print_line = true; //! Set of errors vector errors; diff --git a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_file_handle.hpp b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_file_handle.hpp index aad2e83ff..2c245b67e 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_file_handle.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_file_handle.hpp @@ -8,6 +8,7 @@ #pragma once +#include "duckdb/common/atomic.hpp" #include "duckdb/common/file_system.hpp" #include "duckdb/common/mutex.hpp" #include "duckdb/common/helper.hpp" @@ -58,6 +59,10 @@ class CSVFileHandle { FileCompressionType compression_type; double GetProgress() const; + //! The number of (decompressed) bytes that have been read from the file + idx_t UncompressedBytesRead() const { + return uncompressed_bytes_read.load(std::memory_order_relaxed); + } private: QueryContext context; @@ -67,7 +72,7 @@ class CSVFileHandle { bool can_seek = false; bool on_disk_file = false; bool is_pipe = false; - idx_t uncompressed_bytes_read = 0; + atomic uncompressed_bytes_read {0}; idx_t file_size = 0; diff --git a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_file_scanner.hpp b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_file_scanner.hpp index b55af49d5..b62ed51d8 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_file_scanner.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_file_scanner.hpp @@ -14,22 +14,15 @@ #include "duckdb/execution/operator/csv_scanner/csv_error.hpp" #include "duckdb/execution/operator/csv_scanner/csv_schema.hpp" #include "duckdb/execution/operator/csv_scanner/csv_validator.hpp" -#include "duckdb/common/multi_file/base_file_reader.hpp" +#include "duckdb/common/multi_file/multi_file_data.hpp" +#include "duckdb/execution/progress_data.hpp" namespace duckdb { struct ReadCSVData; class CSVFileScan; -struct CSVUnionData : public BaseUnionData { - explicit CSVUnionData(OpenFileInfo file_p) : BaseUnionData(std::move(file_p)) { - } - ~CSVUnionData() override; - - CSVReaderOptions options; -}; - //! Struct holding information over a CSV File we will scan -class CSVFileScan : public BaseFileReader { +class CSVFileScan { public: //! Constructor for new CSV Files, we must initialize the buffer manager and the state machine //! Path to this file @@ -46,27 +39,12 @@ class CSVFileScan : public BaseFileReader { void SetNamesAndTypes(const vector &names, const vector &types); public: - string GetReaderType() const override { - return "CSV"; - } + //! How far along we are in reading this file + double GetProgressInFile(ClientContext &context); - bool UseCastMap() const override { - //! Whether or not to push casts into the cast map - return true; + const string &GetFileName() const { + return file.path; } - - shared_ptr GetUnionData(idx_t file_idx) override; - void PrepareReader(ClientContext &context, GlobalTableFunctionState &) override; - bool TryInitializeScan(ClientContext &context, GlobalTableFunctionState &gstate, - LocalTableFunctionState &lstate) override; - AsyncResult ScheduleIO(ClientContext &context, GlobalTableFunctionState &gstate, - LocalTableFunctionState &lstate) override; - AsyncResult Scan(ClientContext &context, GlobalTableFunctionState &global_state, - LocalTableFunctionState &local_state, DataChunk &chunk) override; - void FinishFile(ClientContext &context, GlobalTableFunctionState &gstate_p) override; - double GetProgressInFile(ClientContext &context) override; - -public: idx_t GetFileIndex() const { return file_list_idx.GetIndex(); } @@ -79,12 +57,24 @@ class CSVFileScan : public BaseFileReader { void InitializeFileNamesTypes(); public: + //! The file that is read + OpenFileInfo file; + //! The index of this file within the scan it is part of - it identifies the file in the rejects tables + optional_idx file_list_idx; + //! The columns of this file + vector columns; + //! The columns that are read from this file, in the order they are emitted + MultiFileLocalColumnIds column_ids; + //! The types the columns must be read as, when they differ from the types this file has + unordered_map cast_map; //! Buffer Manager for the CSV File shared_ptr buffer_manager; //! State Machine for this file shared_ptr state_machine; //! How many bytes were read up to this point atomic bytes_read {0}; + //! The progress of a compressed file is estimated - keeps it monotonic + MonotonicProgress compressed_progress; //! Size of this file idx_t file_size; //! Line Info used in error messages diff --git a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_multi_file_info.hpp b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_multi_file_info.hpp deleted file mode 100644 index a9dc55f1a..000000000 --- a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_multi_file_info.hpp +++ /dev/null @@ -1,71 +0,0 @@ -//===----------------------------------------------------------------------===// -// DuckDB -// -// duckdb/execution/operator/csv_scanner/csv_multi_file_info.hpp -// -// -//===----------------------------------------------------------------------===// - -#pragma once - -#include "duckdb/common/multi_file/multi_file_function.hpp" -#include "duckdb/execution/operator/csv_scanner/csv_reader_options.hpp" -#include "duckdb/execution/operator/csv_scanner/csv_buffer_manager.hpp" -#include "duckdb/execution/operator/csv_scanner/csv_schema.hpp" - -namespace duckdb { - -class CSVFileReaderOptions : public BaseFileReaderOptions { -public: - CSVFileReaderOptions() = default; - explicit CSVFileReaderOptions(CSVReaderOptions options_p) : options(std::move(options_p)) { - } - - CSVReaderOptions options; -}; - -struct CSVSchemaDiscovery { - static CSVSchema SchemaDiscovery(ClientContext &context, shared_ptr &buffer_manager, - CSVReaderOptions &options, const MultiFileOptions &file_options, - vector &return_types, vector &names, - MultiFileList &multi_file_list); -}; - -struct CSVMultiFileInfo : MultiFileReaderInterface { - static unique_ptr CreateInterface(ClientContext &context); - - unique_ptr InitializeOptions(ClientContext &context, - optional_ptr info) override; - bool ParseCopyOption(ClientContext &context, const Identifier &key, const vector &values, - BaseFileReaderOptions &options, vector &expected_names, - vector &expected_types) override; - bool ParseOption(ClientContext &context, const Identifier &key, const Value &val, MultiFileOptions &file_options, - BaseFileReaderOptions &options) override; - void FinalizeCopyBind(ClientContext &context, BaseFileReaderOptions &options, - const vector &expected_names, const vector &expected_types) override; - unique_ptr InitializeBindData(MultiFileBindData &multi_file_data, - unique_ptr options) override; - void BindReader(ClientContext &context, vector &return_types, vector &names, - MultiFileBindData &bind_data) override; - void FinalizeBindData(MultiFileBindData &multi_file_data) override; - optional_idx MaxThreads(const MultiFileBindData &bind_data_p, const MultiFileGlobalState &global_state, - FileExpandResult expand_result) override; - bool SupportsReadAhead(const MultiFileBindData &bind_data) const override; - unique_ptr InitializeGlobalState(ClientContext &context, MultiFileBindData &bind_data, - MultiFileGlobalState &global_state) override; - unique_ptr InitializeLocalState(ClientContext &, GlobalTableFunctionState &) override; - shared_ptr CreateReader(ClientContext &context, GlobalTableFunctionState &gstate, - BaseUnionData &union_data, const MultiFileBindData &bind_data_p) override; - shared_ptr CreateReader(ClientContext &context, GlobalTableFunctionState &gstate, - const OpenFileInfo &file, idx_t file_idx, - const MultiFileBindData &bind_data) override; - shared_ptr CreateReader(ClientContext &context, const OpenFileInfo &file, - BaseFileReaderOptions &options, - const MultiFileOptions &file_options) override; - void FinishReading(ClientContext &context, GlobalTableFunctionState &global_state, - LocalTableFunctionState &local_state) override; - unique_ptr GetCardinality(const MultiFileBindData &bind_data, idx_t file_count) override; - FileGlobInput GetGlobInput() override; -}; - -} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_option.hpp b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_option.hpp index 7e02f9ba5..cb19031dc 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_option.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_option.hpp @@ -41,7 +41,7 @@ struct CSVOption { // NOLINT: work-around bug in clang-tidy D_ASSERT(!(by_user && set_by_user)); if (!set_by_user) { // If it's not set by user we can change the value - value = value_p; + value = std::move(value_p); // NOLINT(performance-move-const-arg) set_by_user = by_user; } } diff --git a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_reader_options.hpp b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_reader_options.hpp index eae558048..60905b57b 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_reader_options.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_reader_options.hpp @@ -193,7 +193,7 @@ struct CSVReaderOptions { void SetWriteOption(const Identifier &loption, const Value &value); void SetDateFormat(LogicalTypeId type, const string &format, bool read_format); void ToNamedParameters(named_parameter_map_t &out) const; - void FromNamedParameters(const named_parameter_map_t &in, ClientContext &context, MultiFileOptions &file_options); + void FromNamedParameters(const named_argument_map_t &in, ClientContext &context, MultiFileOptions &file_options); void ParseOption(ClientContext &context, const Identifier &key, const Value &val); //! Verify options are not conflicting void Verify(MultiFileOptions &file_options); diff --git a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_schema.hpp b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_schema.hpp index a7c40001e..2d303e551 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_schema.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_schema.hpp @@ -34,6 +34,11 @@ struct CSVSchema { //! If the schema is empty bool Empty() const; + //! Whether this schema was determined on a file without any data + bool IsEmptyFile() const { + return empty; + } + //! If the columns of the schema match bool MatchColumns(const CSVSchema &other) const; diff --git a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_schema_discovery.hpp b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_schema_discovery.hpp new file mode 100644 index 000000000..c52435df0 --- /dev/null +++ b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_schema_discovery.hpp @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/execution/operator/csv_scanner/csv_schema_discovery.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/multi_file/multi_file_list.hpp" +#include "duckdb/execution/operator/csv_scanner/csv_reader_options.hpp" +#include "duckdb/execution/operator/csv_scanner/csv_buffer_manager.hpp" +#include "duckdb/execution/operator/csv_scanner/csv_schema.hpp" + +namespace duckdb { + +struct CSVSchemaDiscovery { + //! "replace_null_with_varchar" is false when the caller combines this schema with the schemas of other files - + //! columns without any value must stay SQLNULL until every file has been seen, or they widen the other schemas + static CSVSchema SchemaDiscovery(ClientContext &context, shared_ptr &buffer_manager, + CSVReaderOptions &options, const MultiFileOptions &file_options, + vector &return_types, vector &names, + MultiFileList &multi_file_list, bool replace_null_with_varchar = true); +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/global_csv_state.hpp b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/global_csv_state.hpp index 7232ccf8c..a40a9e4bd 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/global_csv_state.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/global_csv_state.hpp @@ -18,6 +18,7 @@ #include "duckdb/execution/operator/csv_scanner/csv_validator.hpp" namespace duckdb { +class PhysicalOperator; struct MultiFileBindData; //! Local state of the CSV scan @@ -46,8 +47,8 @@ struct CSVLocalState : public LocalTableFunctionState { //! CSV Global State is used in the CSV Reader Table Function, it controls what each thread struct CSVGlobalState : public GlobalTableFunctionState { public: - CSVGlobalState(ClientContext &context_p, const CSVReaderOptions &options, idx_t total_file_count, - const MultiFileBindData &bind_data); + CSVGlobalState(ClientContext &context_p, ReadCSVData &csv_data, const vector &column_names, + idx_t total_file_count, optional_ptr scan_op = nullptr); ~CSVGlobalState() override { } @@ -70,7 +71,13 @@ struct CSVGlobalState : public GlobalTableFunctionState { private: //! Reference to the client context that created this scan ClientContext &context; - const MultiFileBindData &bind_data; + ReadCSVData &csv_data; + //! The operator the files are scanned for, and how many files it scans - together they tell the rejects tables + //! which file of which scan an error belongs to + optional_ptr scan_op; + idx_t total_file_count; + //! The names of the columns that are read - used when filling the rejects table + const vector &column_names; string sniffer_mismatch_error; @@ -84,8 +91,9 @@ struct CSVGlobalState : public GlobalTableFunctionState { //! We hold information on the current scanner boundary CSVIterator current_boundary; - - vector rejects_file_indexes; }; +//! The I/O tasks needed by the part of the file a local state has claimed, for the buffers that are not in memory +vector> CSVCollectClaimIOTasks(CSVLocalState &lstate); + } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/sniffer/csv_sniffer.hpp b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/sniffer/csv_sniffer.hpp index f12e8dea0..d54cf02bc 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/sniffer/csv_sniffer.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/sniffer/csv_sniffer.hpp @@ -87,13 +87,13 @@ class CSVSniffer { SnifferResult SniffCSV(bool force_match = false); //! I call it adaptive, since that's a sexier term. - //! In practice this Function that only sniffs the first two rows, to verify if a header exists and what are the - //! data types It does this considering a priorly set CSV schema. If there is a mismatch of the schema it runs the + //! In practice this function sniffs up to STANDARD_VECTOR_SIZE rows to verify if a header exists and determine the + //! data types. It does this considering a priorly set CSV schema. If there is a mismatch of the schema it runs the //! full on blazing all guns sniffer, if that still fails it tells the user to union_by_name. //! It returns the projection order. SnifferResult AdaptiveSniff(const CSVSchema &file_schema); - //! Function that only sniffs the first two rows, to verify if a header exists and what are the data types + //! Function that sniffs up to STANDARD_VECTOR_SIZE rows to detect a header and data types AdaptiveSnifferResult MinimalSniff(); static NewLineIdentifier DetectNewLineDelimiter(CSVBufferManager &buffer_manager); diff --git a/src/duckdb/src/include/duckdb/execution/operator/helper/physical_batch_collector.hpp b/src/duckdb/src/include/duckdb/execution/operator/helper/physical_batch_collector.hpp deleted file mode 100644 index 47d329590..000000000 --- a/src/duckdb/src/include/duckdb/execution/operator/helper/physical_batch_collector.hpp +++ /dev/null @@ -1,68 +0,0 @@ -//===----------------------------------------------------------------------===// -// DuckDB -// -// duckdb/execution/operator/helper/physical_batch_collector.hpp -// -// -//===----------------------------------------------------------------------===// - -#pragma once - -#include "duckdb/execution/operator/helper/physical_result_collector.hpp" -#include "duckdb/common/types/batched_data_collection.hpp" - -namespace duckdb { - -class PhysicalBatchCollector : public PhysicalResultCollector { -public: - PhysicalBatchCollector(PhysicalPlan &physical_plan, PreparedStatementData &data); - -public: - unique_ptr GetResult(GlobalSinkState &state) const override; - -public: - // Sink interface - SinkResultType Sink(ExecutionContext &context, DataChunk &chunk, OperatorSinkInput &input) const override; - SinkCombineResultType Combine(ExecutionContext &context, OperatorSinkCombineInput &input) const override; - SinkFinalizeType Finalize(Pipeline &pipeline, Event &event, ClientContext &context, - OperatorSinkFinalizeInput &input) const override; - - unique_ptr GetLocalSinkState(ExecutionContext &context) const override; - unique_ptr GetGlobalSinkState(ClientContext &context) const override; - - OperatorPartitionInfo RequiredPartitionInfo() const override { - return OperatorPartitionInfo::BatchIndex(); - } - - bool ParallelSink() const override { - return true; - } - - PipelineExternalInputSupport GetExternalInputSupport() const override { - return PipelineExternalInputSupport::SUPPORTED; - } -}; - -//===--------------------------------------------------------------------===// -// Sink -//===--------------------------------------------------------------------===// -class BatchCollectorGlobalState : public GlobalSinkState { -public: - BatchCollectorGlobalState(ClientContext &context, const PhysicalBatchCollector &op) - : data(context, op.types, op.memory_type) { - } - - mutex glock; - BatchedDataCollection data; - unique_ptr result; -}; - -class BatchCollectorLocalState : public LocalSinkState { -public: - BatchCollectorLocalState(ClientContext &context, const PhysicalBatchCollector &op) - : data(context, op.types, op.memory_type) { - } - - BatchedDataCollection data; -}; -} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/operator/helper/physical_limit.hpp b/src/duckdb/src/include/duckdb/execution/operator/helper/physical_limit.hpp index db8599722..ba626b0aa 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/helper/physical_limit.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/helper/physical_limit.hpp @@ -36,6 +36,7 @@ class PhysicalLimit : public PhysicalOperator { public: // Source interface unique_ptr GetGlobalSourceState(ClientContext &context) const override; + ProgressData GetProgress(ClientContext &context, GlobalSourceState &gstate) const override; SourceResultType GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const override; @@ -73,6 +74,9 @@ class PhysicalLimit : public PhysicalOperator { const BoundLimitNode &offset_val); static bool HandleOffset(DataChunk &input, idx_t ¤t_offset, idx_t offset, idx_t limit); static Value GetDelimiter(ExecutionContext &context, DataChunk &input, const Expression &expr); + //! Reads the value of a non-constant LIMIT or OFFSET (null_value if it is NULL), which must be at most + //! MAX_LIMIT_VALUE + static idx_t GetLimitValue(const Value &val, idx_t null_value); InsertionOrderPreservingMap ParamsToString() const override; }; diff --git a/src/duckdb/src/include/duckdb/execution/operator/helper/physical_limit_percent.hpp b/src/duckdb/src/include/duckdb/execution/operator/helper/physical_limit_percent.hpp index eab1186b6..82da6e783 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/helper/physical_limit_percent.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/helper/physical_limit_percent.hpp @@ -37,6 +37,9 @@ class PhysicalLimitPercent : public PhysicalOperator { SourceResultType GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const override; + ProgressData GetProgress(ClientContext &context, GlobalSourceState &gstate) const override; + void SourceFinished(ClientContext &context, GlobalSourceState &gstate) const override; + bool IsSource() const override { return true; } diff --git a/src/duckdb/src/include/duckdb/execution/operator/helper/physical_reservoir_sample.hpp b/src/duckdb/src/include/duckdb/execution/operator/helper/physical_reservoir_sample.hpp index c38844e59..a51ad1cfb 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/helper/physical_reservoir_sample.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/helper/physical_reservoir_sample.hpp @@ -29,6 +29,9 @@ class PhysicalReservoirSample : public PhysicalOperator { public: // Source interface + unique_ptr GetGlobalSourceState(ClientContext &context) const override; + ProgressData GetProgress(ClientContext &context, GlobalSourceState &gstate) const override; + void SourceFinished(ClientContext &context, GlobalSourceState &gstate) const override; SourceResultType GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const override; diff --git a/src/duckdb/src/include/duckdb/execution/operator/helper/physical_result_collector.hpp b/src/duckdb/src/include/duckdb/execution/operator/helper/physical_result_collector.hpp index bc08e1a34..68cf0f7ee 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/helper/physical_result_collector.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/helper/physical_result_collector.hpp @@ -15,7 +15,6 @@ namespace duckdb { class PreparedStatementData; -class ColumnDataCollection; //! PhysicalResultCollector is an abstract class that is used to generate the final result of a query class PhysicalResultCollector : public PhysicalOperator { @@ -27,7 +26,6 @@ class PhysicalResultCollector : public PhysicalOperator { StatementType statement_type; StatementProperties properties; - QueryResultMemoryType memory_type; PhysicalOperator &plan; vector names; @@ -56,15 +54,10 @@ class PhysicalResultCollector : public PhysicalOperator { virtual bool IsStreaming() const { return false; } - //! Whether a producer is parked on this sink and only the consumer can release it. A streaming - //! collector without a parked-producer notion reports true: it is never waited on forever, at - //! the price of returning to the consumer on every unrelated block - virtual bool HasBlockedResultProducer(GlobalSinkState &state) const { - return IsStreaming(); + //! Whether this collector builds its own result object, rather than serving it through a result buffer + virtual bool BuildsOwnResult() const { + return true; } - -protected: - unique_ptr CreateCollection(ClientContext &context) const; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/operator/helper/physical_result_sink.hpp b/src/duckdb/src/include/duckdb/execution/operator/helper/physical_result_sink.hpp index e331a2506..c4c379ad7 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/helper/physical_result_sink.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/helper/physical_result_sink.hpp @@ -9,40 +9,36 @@ #pragma once #include "duckdb/common/enums/result_lifetime.hpp" +#include "duckdb/common/enums/result_ordering.hpp" #include "duckdb/execution/operator/helper/physical_result_collector.hpp" namespace duckdb { +class BufferedData; +class ResultFormatLocalState; +class ResultUnit; +class RetainedResultCollection; + class ResultSinkGlobalState; class ResultSinkLocalState; -//! How the consumer-visible chunk order is established -enum class ResultOrdering : uint8_t { - //! No order guarantee: a parallel sink stores chunks as they arrive - UNORDERED, - //! Source order, preserved by sinking through a single thread - SOURCE_ORDERED, - //! Source order, restored from batch indexes under a parallel sink - BATCH_INDEX_ORDERED -}; - //! The root operator of every chunk-producing plan. class PhysicalResultSink : public PhysicalResultCollector { public: - PhysicalResultSink(PhysicalPlan &physical_plan, PreparedStatementData &data, ResultLifetime lifetime, - ResultOrdering ordering); + PhysicalResultSink(PhysicalPlan &physical_plan, PreparedStatementData &data, ResultOrdering ordering); - //! The retention fixed by the plan. UNDECIDED leaves it to the consumer's first call - ResultLifetime lifetime; ResultOrdering ordering; public: unique_ptr GetResult(GlobalSinkState &state) const override; - bool HasBlockedResultProducer(GlobalSinkState &state) const override; + //! Hand the sink the buffer created at submission. Called once, before execution starts + void SetResultBuffer(shared_ptr buffer); // Sink interface SinkResultType Sink(ExecutionContext &context, DataChunk &chunk, OperatorSinkInput &input) const override; SinkCombineResultType Combine(ExecutionContext &context, OperatorSinkCombineInput &input) const override; + SinkFinalizeType Finalize(Pipeline &pipeline, Event &event, ClientContext &context, + OperatorSinkFinalizeInput &input) const override; SinkNextBatchType NextBatch(ExecutionContext &context, OperatorSinkNextBatchInput &input) const override; SinkNextBatchType UpdateMinBatchIndex(ExecutionContext &context, OperatorSinkNextBatchInput &input) const override; @@ -53,23 +49,42 @@ class PhysicalResultSink : public PhysicalResultCollector { bool ParallelSink() const override; bool SinkOrderDependent() const override; PipelineExternalInputSupport GetExternalInputSupport() const override; - //! The plan-time answer: a deferred sink may stream, whatever the consumer decides later + //! Always true: the buffer's lifetime, not the plan, decides whether producers drain or retain bool IsStreaming() const override; + bool BuildsOwnResult() const override { + return false; + } private: bool BatchOrdered() const { return ordering == ResultOrdering::BATCH_INDEX_ORDERED; } - //! The retention in effect: the consumer's decision for a deferred sink, the plan's otherwise + //! The retention in effect: the buffer's own lifetime, settled by the consumer's first call ResultLifetime CurrentLifetime(ResultSinkGlobalState &gstate) const; bool DrainsByBatchIndex(ResultSinkGlobalState &gstate) const; + ResultFormatLocalState &LocalFormatState(ResultSinkGlobalState &gstate, ResultSinkLocalState &lstate) const; + //! Feeds the chunk into the format's unit under construction + void AppendChunk(ResultSinkGlobalState &gstate, ResultSinkLocalState &lstate, DataChunk &chunk) const; + //! True when the producer parked holding the unit + bool HandOver(ResultSinkGlobalState &gstate, ResultSinkLocalState &lstate, unique_ptr unit, + const InterruptState &interrupt) const; + //! Deliver every unit that has reached the format's cap. One append can finish several, so a + //! re-invocation after a blocked hand-over continues from whatever the format still holds + bool DrainFinishedUnits(ResultSinkGlobalState &gstate, ResultSinkLocalState &lstate, + const InterruptState &interrupt) const; + //! Deliver the finished units and the one under construction, so no unit spans two batch indexes + bool FlushUnits(ResultSinkGlobalState &gstate, ResultSinkLocalState &lstate, const InterruptState &interrupt) const; SinkResultType SinkDraining(ResultSinkGlobalState &gstate, ResultSinkLocalState &lstate, DataChunk &chunk, OperatorSinkInput &input) const; - SinkResultType SinkRetained(ExecutionContext &context, ResultSinkLocalState &lstate, DataChunk &chunk) const; + SinkResultType SinkRetained(ExecutionContext &context, ResultSinkGlobalState &gstate, ResultSinkLocalState &lstate, + DataChunk &chunk) const; SinkCombineResultType CombineDraining(ResultSinkGlobalState &gstate, ResultSinkLocalState &lstate) const; - SinkCombineResultType CombineRetained(ResultSinkGlobalState &gstate, ResultSinkLocalState &lstate) const; - unique_ptr GetStreamResult(ResultSinkGlobalState &gstate) const; - unique_ptr GetMaterializedResult(ResultSinkGlobalState &gstate) const; + SinkCombineResultType CombineRetained(ClientContext &context, ResultSinkGlobalState &gstate, + ResultSinkLocalState &lstate) const; + +private: + //! The buffer created at submission, which also holds the retention decision + shared_ptr result_buffer; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/operator/helper/physical_set.hpp b/src/duckdb/src/include/duckdb/execution/operator/helper/physical_set.hpp index e931dd5e2..a261281d1 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/helper/physical_set.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/helper/physical_set.hpp @@ -43,6 +43,8 @@ class PhysicalSet : public PhysicalOperator { static void SetExtensionVariable(ClientContext &context, ExtensionOption &extension_option, SetScope scope, const Value &value); + static void SetExtensionVariable(ClientContext &context, const Identifier &name, ExtensionOption &extension_option, + SetScope scope, const Value &value); static void SetVariable(ClientContext &context, const Identifier &name, SetScope scope, const Value &value); static void SetGenericVariable(ClientContext &context, idx_t setting_index, SetScope scope, Value target_value); diff --git a/src/duckdb/src/include/duckdb/execution/operator/join/join_filter_pushdown.hpp b/src/duckdb/src/include/duckdb/execution/operator/join/join_filter_pushdown.hpp index 02013abb6..72d511a22 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/join/join_filter_pushdown.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/join/join_filter_pushdown.hpp @@ -77,6 +77,8 @@ struct JoinFilterPushdownFilter { shared_ptr dynamic_filters; //! The columns for which we should generate filters vector columns; + //! Target scan estimate for filter costing when intervening operators reduce the probe cardinality. + idx_t estimated_scan_cardinality = 0; }; struct PushdownFilterTarget { @@ -132,10 +134,12 @@ struct JoinFilterPushdownInfo { idx_t max_bits, JoinFilterGlobalState &gstate) const; bool CanUseInFilter(const ClientContext &context, optional_ptr ht, const ExpressionType &cmp) const; - bool CanUseBloomFilter(const ClientContext &context, const PhysicalComparisonJoin &op, const ExpressionType &cmp, + bool CanUseBloomFilter(const JoinFilterPushdownFilter &info, const ClientContext &context, + const PhysicalComparisonJoin &op, const ExpressionType &cmp, optional_ptr ht = nullptr) const; - bool CanUsePrefixRangeFilter(const ClientContext &context, const PhysicalComparisonJoin &op, - optional_ptr ht, const ExpressionType &cmp) const; + bool CanUsePrefixRangeFilter(const JoinFilterPushdownFilter &info, const ClientContext &context, + const PhysicalComparisonJoin &op, optional_ptr ht, + const ExpressionType &cmp) const; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/operator/join/outer_join_marker.hpp b/src/duckdb/src/include/duckdb/execution/operator/join/outer_join_marker.hpp index 07663cd2a..17de0f8cf 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/join/outer_join_marker.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/join/outer_join_marker.hpp @@ -8,6 +8,7 @@ #pragma once +#include "duckdb/common/atomic.hpp" #include "duckdb/common/mutex.hpp" #include "duckdb/common/types/column/column_data_collection.hpp" #include "duckdb/execution/operator/join/physical_comparison_join.hpp" @@ -19,6 +20,9 @@ struct OuterJoinGlobalScanState { mutex lock; ColumnDataCollection *data = nullptr; ColumnDataParallelScanState global_scan; + atomic rows_scanned {0}; + + ProgressData GetProgress() const; }; struct OuterJoinLocalScanState { diff --git a/src/duckdb/src/include/duckdb/execution/operator/join/physical_blockwise_nl_join.hpp b/src/duckdb/src/include/duckdb/execution/operator/join/physical_blockwise_nl_join.hpp index 304479383..8a340e6de 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/join/physical_blockwise_nl_join.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/join/physical_blockwise_nl_join.hpp @@ -41,6 +41,7 @@ class PhysicalBlockwiseNLJoin : public PhysicalJoin { public: // Source interface + ProgressData GetProgress(ClientContext &context, GlobalSourceState &gstate) const override; unique_ptr GetGlobalSourceState(ClientContext &context) const override; unique_ptr GetLocalSourceState(ExecutionContext &context, GlobalSourceState &gstate) const override; diff --git a/src/duckdb/src/include/duckdb/execution/operator/join/physical_nested_loop_join.hpp b/src/duckdb/src/include/duckdb/execution/operator/join/physical_nested_loop_join.hpp index ef7cc91e4..718b50c0a 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/join/physical_nested_loop_join.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/join/physical_nested_loop_join.hpp @@ -40,6 +40,7 @@ class PhysicalNestedLoopJoin : public PhysicalComparisonJoin { public: // Source interface + ProgressData GetProgress(ClientContext &context, GlobalSourceState &gstate) const override; unique_ptr GetGlobalSourceState(ClientContext &context) const override; unique_ptr GetLocalSourceState(ExecutionContext &context, GlobalSourceState &gstate) const override; @@ -75,6 +76,10 @@ class PhysicalNestedLoopJoin : public PhysicalComparisonJoin { //! Returns a list of the types of the join conditions vector GetJoinTypes() const; +private: + vector mark_projection_map; + bool track_unknown; + private: // resolve joins that output max N elements (SEMI, ANTI, MARK) void ResolveSimpleJoin(ExecutionContext &context, DataChunk &input, DataChunk &chunk, OperatorState &state) const; diff --git a/src/duckdb/src/include/duckdb/execution/operator/join/physical_piecewise_merge_join.hpp b/src/duckdb/src/include/duckdb/execution/operator/join/physical_piecewise_merge_join.hpp index faed6e0c1..f9cfb756a 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/join/physical_piecewise_merge_join.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/join/physical_piecewise_merge_join.hpp @@ -46,6 +46,7 @@ class PhysicalPiecewiseMergeJoin : public PhysicalRangeJoin { public: // Source interface unique_ptr GetGlobalSourceState(ClientContext &context) const override; + ProgressData GetProgress(ClientContext &context, GlobalSourceState &gstate) const override; unique_ptr GetLocalSourceState(ExecutionContext &context, GlobalSourceState &gstate) const override; SourceResultType GetDataInternal(ExecutionContext &context, DataChunk &chunk, diff --git a/src/duckdb/src/include/duckdb/execution/operator/join/physical_positional_join.hpp b/src/duckdb/src/include/duckdb/execution/operator/join/physical_positional_join.hpp index 51fd38043..abab010c4 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/join/physical_positional_join.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/join/physical_positional_join.hpp @@ -32,6 +32,9 @@ class PhysicalPositionalJoin : public PhysicalOperator { SourceResultType GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const override; + ProgressData GetProgress(ClientContext &context, GlobalSourceState &gstate) const override; + void SourceFinished(ClientContext &context, GlobalSourceState &gstate) const override; + bool IsSource() const override { return true; } diff --git a/src/duckdb/src/include/duckdb/execution/operator/join/physical_range_join.hpp b/src/duckdb/src/include/duckdb/execution/operator/join/physical_range_join.hpp index 9cee4889e..bc37edaa6 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/join/physical_range_join.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/join/physical_range_join.hpp @@ -51,7 +51,7 @@ class PhysicalRangeJoin : public PhysicalComparisonJoin { class GlobalSortedTable { public: GlobalSortedTable(ClientContext &client, const vector &orders, - const vector &payload_layout, const PhysicalRangeJoin &op); + const vector &payload_layout, const PhysicalComparisonJoin &op); inline idx_t Count() const { return count; @@ -121,7 +121,7 @@ class PhysicalRangeJoin : public PhysicalComparisonJoin { } //! The hosting operator - const PhysicalRangeJoin &op; + const PhysicalComparisonJoin &op; //! The sort description unique_ptr sort; //! The shared sort state diff --git a/src/duckdb/src/include/duckdb/execution/operator/order/physical_top_n.hpp b/src/duckdb/src/include/duckdb/execution/operator/order/physical_top_n.hpp index dd79b094f..c99b9ef4f 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/order/physical_top_n.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/order/physical_top_n.hpp @@ -36,6 +36,7 @@ class PhysicalTopN : public PhysicalOperator { unique_ptr GetLocalSourceState(ExecutionContext &context, GlobalSourceState &gstate) const override; unique_ptr GetGlobalSourceState(ClientContext &context) const override; + ProgressData GetProgress(ClientContext &context, GlobalSourceState &gstate) const override; SourceResultType GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const override; OperatorPartitionData GetPartitionData(ExecutionContext &context, DataChunk &chunk, GlobalSourceState &gstate, diff --git a/src/duckdb/src/include/duckdb/execution/operator/persistent/copy_batch_slicer.hpp b/src/duckdb/src/include/duckdb/execution/operator/persistent/copy_batch_slicer.hpp new file mode 100644 index 000000000..4f11d55e0 --- /dev/null +++ b/src/duckdb/src/include/duckdb/execution/operator/persistent/copy_batch_slicer.hpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/execution/operator/persistent/copy_batch_slicer.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/types/data_chunk.hpp" +#include "duckdb/common/types/row/tuple_data_layout.hpp" + +namespace duckdb { + +class ColumnDataCollection; +struct TupleDataChunkState; + +//! Sizes and slices chunks for byte-limited COPY batches. +class CopyBatchSlicer { +public: + CopyBatchSlicer(const vector &types, const optional_idx &batch_size_bytes); + ~CopyBatchSlicer(); + +public: + //! Computes row sizes, including fixed-width values and variable payloads. + const Vector &ComputeRowSizes(DataChunk &chunk); + //! Returns the next slice and advances offset, reusing row sizes until the chunk is exhausted. + DataChunk &Slice(DataChunk &chunk, idx_t &offset, const ColumnDataCollection &batch); + +private: + idx_t RowsThatFit(idx_t count, idx_t offset, idx_t budget) const; + +private: + const optional_idx batch_size_bytes; + TupleDataLayout layout; + idx_t fixed_row_bytes = 0; + unique_ptr row_bytes_state; + DataChunk partial; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/operator/persistent/copy_output_lifecycle.hpp b/src/duckdb/src/include/duckdb/execution/operator/persistent/copy_output_lifecycle.hpp index 730ca68dd..c9aa3cfae 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/persistent/copy_output_lifecycle.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/persistent/copy_output_lifecycle.hpp @@ -19,25 +19,17 @@ class ClientContext; enum class CopyOutputOwnership : uint8_t { //! Preserve the path because this query cannot prove that it created it. PRESERVE, - //! Remove the path if it was finalized before the query failed. + //! Remove the path if the query fails. REMOVE_ON_FAILURE }; -enum class CopyOutputPublicationState : uint8_t { - //! The COPY function has not completed finalization for this path. - UNFINALIZED, - //! The COPY function completed finalization for this path. - FINALIZED -}; - class CopyOutputLifecycle { public: explicit CopyOutputLifecycle(ClientContext &context); ~CopyOutputLifecycle(); public: - idx_t RegisterFile(string path) DUCKDB_EXCLUDES(lock); - void MarkFileFinalized(idx_t file_index) DUCKDB_EXCLUDES(lock); + void RegisterFile(string path) DUCKDB_EXCLUDES(lock); void RegisterCreatedDirectory(string path) DUCKDB_EXCLUDES(lock); void MarkSuccessful() noexcept DUCKDB_EXCLUDES(lock); @@ -45,7 +37,6 @@ class CopyOutputLifecycle { struct FileEntry { string path; CopyOutputOwnership ownership = CopyOutputOwnership::PRESERVE; - CopyOutputPublicationState publication = CopyOutputPublicationState::UNFINALIZED; }; private: diff --git a/src/duckdb/src/include/duckdb/execution/operator/persistent/csv_rejects_table.hpp b/src/duckdb/src/include/duckdb/execution/operator/persistent/csv_rejects_table.hpp index 415f217b2..daa7b961d 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/persistent/csv_rejects_table.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/persistent/csv_rejects_table.hpp @@ -13,6 +13,8 @@ #include "duckdb/common/typedefs.hpp" #include "duckdb/common/shared_ptr.hpp" #include "duckdb/common/string.hpp" +#include "duckdb/common/constants.hpp" +#include "duckdb/common/unordered_map.hpp" namespace duckdb { @@ -26,6 +28,11 @@ class CSVRejectsTable : public ObjectCacheEntry { : count(0), scan_table(std::move(rejects_scan)), errors_table(std::move(rejects_error)) { } mutex write_lock; + //! Guards the file indexes below - separate from the write lock, which is held while the tables are written + mutex file_index_lock; + idx_t file_index_query_id = DConstants::INVALID_INDEX; + idx_t next_file_index = 0; + unordered_map scan_file_index_base; string name; idx_t count; string scan_table; @@ -35,11 +42,12 @@ class CSVRejectsTable : public ObjectCacheEntry { const string &rejects_error); void InitializeTable(ClientContext &context, const ReadCSVData &options); + //! The first file index of a scan. The files of a scan get a block of indexes, so that they keep the order they + //! have in the scan, and the scans of a query do not report their files under the same indexes + idx_t GetFileIndexBase(idx_t query_id, const void *scan_key, idx_t file_count); TableCatalogEntry &GetErrorsTable(ClientContext &context); TableCatalogEntry &GetScansTable(ClientContext &context); - idx_t GetCurrentFileIndex(idx_t query_id); - static string ObjectType() { return "csv_rejects_table_cache"; } @@ -55,9 +63,7 @@ class CSVRejectsTable : public ObjectCacheEntry { private: //! Current File Index being used in the query - idx_t current_file_idx = 0; //! Current Query ID being executed - idx_t current_query_id = 0; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_batch_insert.hpp b/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_batch_insert.hpp index d2eab3a84..1671b85fc 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_batch_insert.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_batch_insert.hpp @@ -27,13 +27,13 @@ class PhysicalBatchInsert : public PhysicalOperator { unique_ptr info, idx_t estimated_cardinality); //! The table to insert into - optional_ptr insert_table; + mutable optional_ptr insert_table; //! The insert types vector insert_types; //! The bound constraints for the table vector> bound_constraints; //! Table schema, in case of CREATE TABLE AS - optional_ptr schema; + mutable optional_ptr schema; //! Create table info, in case of CREATE TABLE AS unique_ptr info; //! Preferred input batch size diff --git a/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_copy_to_file.hpp b/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_copy_to_file.hpp index 5c53acb77..8d0352529 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_copy_to_file.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_copy_to_file.hpp @@ -143,6 +143,9 @@ class PhysicalCopyToFile : public PhysicalOperator { bool partition_output; bool write_partition_columns; bool hive_file_pattern; + //! Directory (relative to file_path) of a partition's files, replacing the hive layout if set. Evaluated per + //! partition over a single row holding the partition values, in partition_columns order. + unique_ptr partition_path_expression; //! If the data should be sorted vector order_columns; diff --git a/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_delete.hpp b/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_delete.hpp index e58a69b69..52bb3e5ba 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_delete.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_delete.hpp @@ -35,6 +35,7 @@ class PhysicalDelete : public PhysicalOperator { public: // Source interface unique_ptr GetGlobalSourceState(ClientContext &context) const override; + ProgressData GetProgress(ClientContext &context, GlobalSourceState &gstate) const override; unique_ptr GetLocalSourceState(ExecutionContext &context, GlobalSourceState &gstate) const override; SourceResultType GetDataInternal(ExecutionContext &context, DataChunk &chunk, diff --git a/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_insert.hpp b/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_insert.hpp index 9673d84d2..965f4ff11 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_insert.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_insert.hpp @@ -87,7 +87,7 @@ class PhysicalInsert : public PhysicalOperator { unique_ptr info, idx_t estimated_cardinality, bool parallel); //! The table to insert into - optional_ptr insert_table; + mutable optional_ptr insert_table; //! The insert types vector insert_types; //! The bound constraints for the table @@ -95,7 +95,7 @@ class PhysicalInsert : public PhysicalOperator { //! If the returning statement is present, return the whole chunk bool return_chunk; //! Table schema, in case of CREATE TABLE AS - optional_ptr schema; + mutable optional_ptr schema; //! Create table info, in case of CREATE TABLE AS unique_ptr info; //! Whether or not the INSERT can be executed in parallel @@ -131,6 +131,8 @@ class PhysicalInsert : public PhysicalOperator { SourceResultType GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const override; + ProgressData GetProgress(ClientContext &context, GlobalSourceState &gstate) const override; + bool IsSource() const override { return true; } diff --git a/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_update.hpp b/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_update.hpp index 2145f8990..4a573d560 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_update.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_update.hpp @@ -51,6 +51,7 @@ class PhysicalUpdate : public PhysicalOperator { public: // Source interface unique_ptr GetGlobalSourceState(ClientContext &context) const override; + ProgressData GetProgress(ClientContext &context, GlobalSourceState &gstate) const override; SourceResultType GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const override; diff --git a/src/duckdb/src/include/duckdb/execution/operator/projection/physical_projection.hpp b/src/duckdb/src/include/duckdb/execution/operator/projection/physical_projection.hpp index b12125a65..417427b69 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/projection/physical_projection.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/projection/physical_projection.hpp @@ -24,6 +24,7 @@ class PhysicalProjection : public PhysicalOperator { vector> select_list; public: + void InitializeOutputChunk(Allocator &allocator, DataChunk &chunk) const override; unique_ptr GetOperatorState(ExecutionContext &context) const override; OperatorResultType Execute(ExecutionContext &context, DataChunk &input, DataChunk &chunk, GlobalOperatorState &gstate, OperatorState &state) const override; diff --git a/src/duckdb/src/include/duckdb/execution/operator/projection/physical_tableinout_function.hpp b/src/duckdb/src/include/duckdb/execution/operator/projection/physical_tableinout_function.hpp index 4820db315..b1ef99565 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/projection/physical_tableinout_function.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/projection/physical_tableinout_function.hpp @@ -11,6 +11,7 @@ #include "duckdb/execution/physical_operator.hpp" #include "duckdb/function/function.hpp" #include "duckdb/function/table_function.hpp" +#include "duckdb/planner/table_filter_set.hpp" namespace duckdb { @@ -19,8 +20,9 @@ class PhysicalTableInOutFunction : public PhysicalOperator { static constexpr const PhysicalOperatorType TYPE = PhysicalOperatorType::INOUT_FUNCTION; public: - PhysicalTableInOutFunction(PhysicalPlan &physical_plan, vector types, TableFunction function_p, + PhysicalTableInOutFunction(PhysicalPlan &physical_plan, vector types, BoundTableFunction function_p, unique_ptr bind_data_p, vector column_ids_p, + vector projection_ids_p, unique_ptr table_filters_p, idx_t estimated_cardinality, vector projected_input); public: @@ -52,11 +54,15 @@ class PhysicalTableInOutFunction : public PhysicalOperator { private: //! The table function - TableFunction function; + BoundTableFunction function; //! Bind data of the function unique_ptr bind_data; //! The set of column ids to fetch vector column_ids; + //! The projected-out column ids + vector projection_ids; + //! Filters pushed into the table in-out function + unique_ptr table_filters; //! The set of input columns to project out vector projected_input; }; diff --git a/src/duckdb/src/include/duckdb/execution/operator/scan/physical_table_scan.hpp b/src/duckdb/src/include/duckdb/execution/operator/scan/physical_table_scan.hpp index f76265eb6..84d390952 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/scan/physical_table_scan.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/scan/physical_table_scan.hpp @@ -28,14 +28,14 @@ class PhysicalTableScan : public PhysicalOperator { public: //! Table scan that immediately projects out filter columns that are unused in the remainder of the query plan - PhysicalTableScan(PhysicalPlan &physical_plan, vector types, TableFunction function, + PhysicalTableScan(PhysicalPlan &physical_plan, vector types, BoundTableFunction function, unique_ptr bind_data, vector returned_types, vector column_ids, vector projection_ids, vector names, unique_ptr table_filters, idx_t estimated_cardinality, ExtraOperatorInfo extra_info, vector parameters, virtual_column_map_t virtual_columns); //! The table function - TableFunction function; + BoundTableFunction function; //! Bind data of the function unique_ptr bind_data; //! The types of ALL columns that can be returned by the table function diff --git a/src/duckdb/src/include/duckdb/execution/operator/set/physical_recursive_cte.hpp b/src/duckdb/src/include/duckdb/execution/operator/set/physical_recursive_cte.hpp index e6b8c29cf..b73105c9e 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/set/physical_recursive_cte.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/set/physical_recursive_cte.hpp @@ -103,8 +103,10 @@ class PhysicalRecursiveCTE : public PhysicalOperator { public: // Source interface + unique_ptr GetGlobalSourceState(ClientContext &context) const override; SourceResultType GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const override; + ProgressData GetProgress(ClientContext &context, GlobalSourceState &gstate) const override; bool IsSource() const override { return true; @@ -169,6 +171,7 @@ class PhysicalRecursiveCTEStateScan : public PhysicalColumnDataScan { GlobalSourceState &gstate) const override; SourceResultType GetDataInternal(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const override; + ProgressData GetProgress(ClientContext &context, GlobalSourceState &gstate) const override; bool IsSource() const override { return true; diff --git a/src/duckdb/src/include/duckdb/execution/operator/set/physical_recursive_cte_state.hpp b/src/duckdb/src/include/duckdb/execution/operator/set/physical_recursive_cte_state.hpp index d562b9836..317fed79b 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/set/physical_recursive_cte_state.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/set/physical_recursive_cte_state.hpp @@ -65,14 +65,15 @@ class RecursiveCTEPartialKeyIndex { struct RecursiveCTEScheduleStage { RecursiveCTEScheduleStage(PipelineScheduleStageType type_p, Pipeline &pipeline_p, bool has_source_tasks_p, - RecursiveCTEPipelineMetricType metric_type_p) - : type(type_p), pipeline(pipeline_p), has_source_tasks(has_source_tasks_p), metric_type(metric_type_p), - dependency_count(0) { + RecursiveCTEPipelineMetricType metric_type_p, bool is_invariant_build_p) + : type(type_p), pipeline(pipeline_p), has_source_tasks(has_source_tasks_p), + is_invariant_build(is_invariant_build_p), metric_type(metric_type_p), dependency_count(0) { } PipelineScheduleStageType type; reference pipeline; bool has_source_tasks; + bool is_invariant_build; RecursiveCTEPipelineMetricType metric_type; vector dependents; idx_t dependency_count; @@ -100,6 +101,8 @@ struct RecursiveCTEMetricDistribution { }; struct RecursiveCTEEpochMetrics { + void RecordPipelineWorkers(idx_t workers); + void Record(idx_t frontier_rows, idx_t workers, idx_t tasks, idx_t elapsed_us, idx_t frontier_storage_bytes, idx_t frontier_allocation_bytes); void RecordDirectProbeLookup(idx_t elapsed_ns); @@ -119,6 +122,8 @@ struct RecursiveCTEEpochMetrics { void RecordDistinctGrouping(idx_t candidate_rows, idx_t inserted_rows, idx_t elapsed_ns); void RecordPipelineExecution(RecursiveCTEPipelineMetricType metric_type, idx_t elapsed_ns); + //! Maximum scheduled worker count of one EXECUTE stage across this invocation. + atomic max_pipeline_workers {1}; RecursiveCTEMetricDistribution frontier_rows; RecursiveCTEMetricDistribution workers; RecursiveCTEMetricDistribution tasks; @@ -245,12 +250,43 @@ class RecursiveCTESchedulerState { vector ready_schedule_stages; }; +//! The source state of a recursive CTE - tracks the rows emitted to estimate the progress, as the number of recursive +//! iterations is not known up front +class RecursiveCTEGlobalSourceState : public GlobalSourceState { +public: + void StartIteration(idx_t row_count); + void StartFinalDrain(idx_t row_count); + void AddEmittedRows(idx_t row_count) { + emitted_rows.fetch_add(row_count, std::memory_order_relaxed); + } + void Finish() { + finished.store(true, std::memory_order_relaxed); + } + ProgressData GetProgress(); + +private: + atomic emitted_rows {0}; + atomic finished {false}; + //! Protects the iteration statistics, which are updated once per iteration + mutex iteration_lock; + //! Whether the final state of a USING KEY recursion is being emitted (its size is then known) + bool draining = false; + idx_t iterations = 0; + //! Rows of the first, the current and the previous iteration + idx_t anchor_rows = 0; + idx_t iteration_rows = 0; + idx_t previous_iteration_rows = 0; + //! Rows emitted before the current iteration started + idx_t iteration_start_rows = 0; + MonotonicProgress progress; +}; + class RecursiveCTEState : public GlobalSinkState { public: explicit RecursiveCTEState(ClientContext &context, const PhysicalRecursiveCTE &op); ~RecursiveCTEState() override; - SourceResultType GetData(ExecutionContext &context, DataChunk &chunk); + SourceResultType GetData(ExecutionContext &context, DataChunk &chunk, RecursiveCTEGlobalSourceState &source); const ColumnDataCollection &CurrentInputTable() const; idx_t CurrentInputCount() const { return CurrentInputTable().Count(); @@ -361,7 +397,6 @@ class RecursiveCTEState : public GlobalSinkState { ColumnDataCollection intermediate_table; ColumnDataAppendState intermediate_append_state; ColumnDataAppendState working_append_state; - ColumnDataAppendState recurring_append_state; ColumnDataScanState scan_state; vector> local_preaggregates; idx_t local_preaggregate_candidate_count = 0; @@ -369,8 +404,7 @@ class RecursiveCTEState : public GlobalSinkState { bool output_is_working = false; //! Cached chunk for distinct key extraction in the using_key Sink path DataChunk distinct_rows; - //! Cached chunks for source-side hash table scans and recurring table copy paths - DataChunk source_result; + //! Cached chunks for source-side hash table scans DataChunk update_rows; DataChunk source_aggregate_rows; DataChunk source_distinct_rows; @@ -395,10 +429,12 @@ class RecursiveCTEState : public GlobalSinkState { bool can_reuse_new_group_candidates = false; bool can_reuse_changed_group_candidates = false; - SourceResultType GetUsingKeyData(ExecutionContext &context, DataChunk &chunk); + SourceResultType GetUsingKeyData(ExecutionContext &context, DataChunk &chunk, + RecursiveCTEGlobalSourceState &source); template - SourceResultType GetUsingKeyDataInternal(ExecutionContext &context, DataChunk &chunk); - SourceResultType GetUnionData(ExecutionContext &context, DataChunk &chunk); + SourceResultType GetUsingKeyDataInternal(ExecutionContext &context, DataChunk &chunk, + RecursiveCTEGlobalSourceState &source); + SourceResultType GetUnionData(ExecutionContext &context, DataChunk &chunk, RecursiveCTEGlobalSourceState &source); void InitializeIntermediateAppend(); ColumnDataCollection &CurrentOutputTable(); ColumnDataCollection &CurrentInputTable(); diff --git a/src/duckdb/src/include/duckdb/execution/perfect_aggregate_hashtable.hpp b/src/duckdb/src/include/duckdb/execution/perfect_aggregate_hashtable.hpp index 17bec3b99..5ceecbff5 100644 --- a/src/duckdb/src/include/duckdb/execution/perfect_aggregate_hashtable.hpp +++ b/src/duckdb/src/include/duckdb/execution/perfect_aggregate_hashtable.hpp @@ -28,6 +28,10 @@ class PerfectAggregateHashTable : public BaseAggregateHashTable { //! Combines the target perfect aggregate HT into this one void Combine(PerfectAggregateHashTable &other); + idx_t Capacity() const { + return total_groups; + } + //! Scan the HT starting from the scan_position void Scan(idx_t &scan_position, DataChunk &result); diff --git a/src/duckdb/src/include/duckdb/execution/physical_operator.hpp b/src/duckdb/src/include/duckdb/execution/physical_operator.hpp index ddb6e10b1..df0a6d347 100644 --- a/src/duckdb/src/include/duckdb/execution/physical_operator.hpp +++ b/src/duckdb/src/include/duckdb/execution/physical_operator.hpp @@ -91,6 +91,11 @@ class PhysicalOperator { return false; } + //! Initialize the vectors this operator uses for its output + virtual void InitializeOutputChunk(Allocator &allocator, DataChunk &chunk) const { + chunk.Initialize(allocator, types); + } + //! Functions to help decide how to set up pipeline dependencies idx_t EstimatedThreadCount() const; bool CanSaturateThreads(ClientContext &context) const; diff --git a/src/duckdb/src/include/duckdb/execution/physical_plan_generator.hpp b/src/duckdb/src/include/duckdb/execution/physical_plan_generator.hpp index 8a2aebc6c..efccfac14 100644 --- a/src/duckdb/src/include/duckdb/execution/physical_plan_generator.hpp +++ b/src/duckdb/src/include/duckdb/execution/physical_plan_generator.hpp @@ -128,6 +128,8 @@ class PhysicalPlanGenerator { public: PhysicalOperator &ResolveDefaultsProjection(LogicalInsert &op, PhysicalOperator &child); + //! Plans the copy without expanding op.file_path + PhysicalOperator &CreatePlan(LogicalCopyToFile &op, PhysicalOperator &plan); protected: PhysicalOperator &CreatePlan(LogicalAggregate &op); @@ -204,7 +206,7 @@ class PhysicalPlanGenerator { bool PreserveInsertionOrder(PhysicalOperator &plan); bool UseBatchIndex(PhysicalOperator &plan); optional_ptr PlanAsOfLoopJoin(LogicalComparisonJoin &op, PhysicalOperator &probe, - PhysicalOperator &build); + PhysicalOperator &build, const idx_t join_cardinality); optional_ptr PlanAsOfInequalityJoin(LogicalComparisonJoin &op, PhysicalOperator &probe, PhysicalOperator &build, const idx_t lhs_cardinality, const idx_t rhs_cardinality); diff --git a/src/duckdb/src/include/duckdb/execution/progress_data.hpp b/src/duckdb/src/include/duckdb/execution/progress_data.hpp index 1d234d653..fdafc1a0e 100644 --- a/src/duckdb/src/include/duckdb/execution/progress_data.hpp +++ b/src/duckdb/src/include/duckdb/execution/progress_data.hpp @@ -9,6 +9,8 @@ #pragma once #include "duckdb/common/assert.hpp" +#include "duckdb/common/atomic.hpp" +#include "duckdb/common/constants.hpp" namespace duckdb { @@ -55,4 +57,89 @@ struct ProgressData { } }; +//! Keeps an estimated progress from decreasing, for operators whose total is only known once they finish. +//! The highest fraction reported so far is kept as fixed-point, and is only touched when progress is requested. +struct MonotonicProgress { +public: + //! Returns the given progress, raised to the highest fraction that was returned before + ProgressData Update(const ProgressData &progress) const { + if (!progress.IsValid() || progress.total <= 0) { + return progress; + } + auto fraction = static_cast(progress.done / progress.total * static_cast(PRECISION)); + fraction = fraction > PRECISION ? PRECISION : fraction; + auto previous = max_fraction.load(std::memory_order_relaxed); + while (fraction > previous && + !max_fraction.compare_exchange_weak(previous, fraction, std::memory_order_relaxed)) { + } + auto result_fraction = fraction > previous ? fraction : previous; + ProgressData result; + result.done = static_cast(result_fraction) / static_cast(PRECISION) * progress.total; + result.total = progress.total; + return result; + } + void Reset() { + max_fraction.store(0, std::memory_order_relaxed); + } + +private: + static constexpr idx_t PRECISION = 1000000000; + //! Updated when progress is requested, which only has const access to the operator state + mutable atomic max_fraction {0}; +}; + +//! Progress of a source that executes a known number of tasks - every task contributes UNITS_PER_TASK units once it +//! is finished, and threads report the units of the task they are working on as it advances +struct TaskProgress { +public: + static constexpr idx_t UNITS_PER_TASK = 1 << 20; + + void AddUnits(idx_t count) { + units.fetch_add(count, std::memory_order_relaxed); + } + ProgressData GetProgress(idx_t task_count) const { + ProgressData result; + result.total = static_cast(task_count); + result.done = static_cast(units.load(std::memory_order_relaxed)) / static_cast(UNITS_PER_TASK); + if (result.done > result.total) { + result.done = result.total; + } + return result; + } + void Reset() { + units.store(0, std::memory_order_relaxed); + } + +private: + atomic units {0}; +}; + +//! Reports the progress of the task a thread is working on to a TaskProgress +struct TaskProgressTracker { +public: + //! Starts reporting a new task + void Start() { + reported_units = 0; + } + //! Reports that the current task has done "done" out of "total" work + void Update(TaskProgress &progress, idx_t done, idx_t total) { + idx_t task_units = TaskProgress::UNITS_PER_TASK; + if (total > 0 && done < total) { + task_units = static_cast(static_cast(TaskProgress::UNITS_PER_TASK) * + static_cast(done) / static_cast(total)); + } + if (task_units > reported_units) { + progress.AddUnits(task_units - reported_units); + reported_units = task_units; + } + } + //! Reports that the current task is finished + void Finish(TaskProgress &progress) { + Update(progress, 1, 1); + } + +private: + idx_t reported_units = 0; +}; + } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/radix_ht_adaptivity.hpp b/src/duckdb/src/include/duckdb/execution/radix_ht_adaptivity.hpp new file mode 100644 index 000000000..23ce5ef97 --- /dev/null +++ b/src/duckdb/src/include/duckdb/execution/radix_ht_adaptivity.hpp @@ -0,0 +1,36 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/execution/radix_ht_adaptivity.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/common.hpp" + +namespace duckdb { + +class RadixHTGlobalSinkState; +class RadixHTLocalSinkState; + +//! Policy for the local sink HT. Called at fill-cycle boundaries without memory pressure. +class RadixHTAdaptivity { +public: + //! Grow a profitable table within the memory budget, resuming lookups on success. + static bool TryGrow(RadixHTGlobalSinkState &gstate, RadixHTLocalSinkState &lstate); + //! Consider skipping lookups using the completed cycle, before abandonment. + static void MaybeSkipLookups(RadixHTGlobalSinkState &gstate, RadixHTLocalSinkState &lstate); + //! Periodically retry lookups at the existing capacity, after abandonment. + static void MaybeResumeLookups(RadixHTGlobalSinkState &gstate, RadixHTLocalSinkState &lstate); + + //! Minimum input before skipping lookups and between subsequent retries + static constexpr idx_t LOOKUP_SAMPLE_SIZE = 1048576; + +private: + static constexpr double GROWTH_DUPLICATE_BYTES_FACTOR = 4.0; + static constexpr double MINIMUM_SMALL_TABLE_REDUCTION = 0.05; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/radix_ht_sink_state.hpp b/src/duckdb/src/include/duckdb/execution/radix_ht_sink_state.hpp new file mode 100644 index 000000000..689bfd886 --- /dev/null +++ b/src/duckdb/src/include/duckdb/execution/radix_ht_sink_state.hpp @@ -0,0 +1,183 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/execution/radix_ht_sink_state.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/execution/radix_partitioned_hashtable.hpp" +#include "duckdb/execution/aggregate_hashtable.hpp" +#include "duckdb/execution/aggregate_state_spilling.hpp" +#include "duckdb/storage/temporary_memory_manager.hpp" + +namespace duckdb { + +class RadixHTGlobalSinkState; + +//! How aggregate state spilling and native row combining are allowed to interleave +enum class SpillPhase { + //! Rows may still be combined natively, and the exported width may still grow beyond it + NATIVE_ALLOWED, + //! The exported width grew beyond the native width: every combine must export its rows + EXPORTED_ONLY, + //! Rows were combined natively: the exported width can no longer grow beyond the native width + NATIVE_COMBINE_STARTED +}; + +struct RadixHTConfig { +public: + explicit RadixHTConfig(RadixHTGlobalSinkState &sink); + + void Reset(); + void SetRadixBits(const idx_t &radix_bits_p); + bool SetRadixBitsToExternal(); + idx_t GetRadixBits() const; + idx_t GetMaximumSinkRadixBits() const; + +private: + void SetRadixBitsInternal(idx_t radix_bits_p, bool external); + idx_t InitialSinkRadixBits() const; + idx_t ExternalRadixBits(bool dynamic) const; + idx_t MaximumSinkRadixBits() const; + idx_t SinkCapacity() const; + +private: + //! The global sink state + RadixHTGlobalSinkState &sink; + +public: + //! Width of tuples + const idx_t row_width; + //! Capacity of HTs during the Sink + const idx_t sink_capacity; + +private: + //! Sink radix bits to initialize with + static constexpr idx_t MAXIMUM_INITIAL_SINK_RADIX_BITS = 4; + +public: + //! Maximum Sink radix bits (independent of threads) + static constexpr idx_t MAXIMUM_FINAL_SINK_RADIX_BITS = 8; + +private: + //! Current thread-global sink radix bits + atomic sink_radix_bits; + //! Maximum Sink radix bits (set based on number of threads, if not external) + const idx_t maximum_sink_radix_bits; + + //! Thresholds at which we reduce the sink radix bits + //! This needed to reduce cache misses when we have very wide rows + static constexpr idx_t ROW_WIDTH_THRESHOLD_ONE = 32; + static constexpr idx_t ROW_WIDTH_THRESHOLD_TWO = 64; + +public: + //! If we have this many or less threads, we grow the HT, otherwise we abandon + static constexpr idx_t GROW_STRATEGY_THREAD_THRESHOLD = 2; + //! If we fill this many blocks per partition, we trigger a repartition + static constexpr double BLOCK_FILL_FACTOR = 0.5; + //! By how many bits to repartition if a repartition is triggered + static constexpr idx_t REPARTITION_RADIX_BITS = 2; + //! Thread-limit divisor for state export and exported partition sizing + static constexpr idx_t AGGREGATE_STATE_SPILL_DIVISOR = 8; + //! Arena-only pressure that forces external aggregation + static constexpr idx_t AGGREGATE_STATE_PRESSURE_DIVISOR = 2; + //! Estimated memory amplification when importing exported states + static constexpr idx_t EXPORTED_STATE_MEMORY_MULTIPLIER = 2; +}; + +class RadixHTGlobalSinkState : public GlobalSinkState { +public: + RadixHTGlobalSinkState(ClientContext &context, const RadixPartitionedHashTable &radix_ht); + + //! Destroys aggregate states (if multi-scan) + ~RadixHTGlobalSinkState() override; + void Destroy(); + +public: + idx_t GetThreadLimit() const { + return temporary_memory_state->GetReservation() / number_of_threads / 10 * 8; + } + //! Whether the aggregate arena alone approaches the thread memory limit + bool StatePressureExceeded(GroupedAggregateHashTable &ht) const; + +public: + ClientContext &context; + //! Temporary memory state for managing this hash table's memory usage + unique_ptr temporary_memory_state; + atomic minimum_reservation; + + //! Whether we've called Finalize + bool finalized; + //! Whether we are doing an external aggregation + atomic external; + //! Threads that have called Sink + atomic active_threads; + //! Number of threads (from TaskScheduler) + const idx_t number_of_threads; + //! Memory limit (from BufferManager) + const idx_t memory_limit; + //! Block size (from BufferManager) + const idx_t block_alloc_size; + //! If any thread has called combine + atomic any_combined; + //! If any thread has called ht.Abandon() during Sink (meaning uncombined_data may have duplicates) + atomic any_abandoned; + + //! The radix HT + const RadixPartitionedHashTable &radix_ht; + //! Config for partitioning + RadixHTConfig config; + + //! Uncombined partitioned data that will be put into the AggregatePartitions + unique_ptr uncombined_data; + //! The spill metadata of the aggregate layout, set if the states can spill + unique_ptr spill_plan; + //! Synchronizes the transition to exported-only aggregation with concurrent combines + SpillPhase spill_phase DUCKDB_GUARDED_BY(lock); + //! Uncombined exported data, aligned one-to-one with the partitions of uncombined_data + vector> uncombined_exported_data; + //! Allocators used during the Sink/Finalize + vector> stored_allocators; + idx_t stored_allocators_size; + + //! Partitions that are finalized during GetData + vector> partitions; + //! For keeping track of progress + atomic finalize_done; + + //! Pin properties when scanning + TupleDataPinProperties scan_pin_properties; + //! Total count before combining + idx_t count_before_combining; + //! Maximum partition size if all unique + idx_t max_partition_size; +}; + +class RadixHTLocalSinkState : public LocalSinkState { +public: + RadixHTLocalSinkState(ClientContext &context, const RadixPartitionedHashTable &radix_ht); + void ResetForReuse(const RadixPartitionedHashTable &radix_ht, RadixHTGlobalSinkState &gstate); + void PrepareForSpill(RadixHTGlobalSinkState &gstate); + +public: + //! Thread-local HT that is re-used after abandoning + unique_ptr ht; + //! Chunk with group columns + DataChunk group_chunk; + + //! Whether this local state has already registered itself as active for the current iteration + bool registered; + //! Whether this local table has entered spilling or state export + bool spilling = false; + //! Sink capacity for this thread + idx_t local_sink_capacity; + //! Data that is abandoned ends up here (only if we're doing external aggregation) + unique_ptr abandoned_data; + //! Exported abandoned aggregate states, aligned one-to-one with the partitions of abandoned_data + vector> abandoned_exported_data; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/reservoir_sample.hpp b/src/duckdb/src/include/duckdb/execution/reservoir_sample.hpp index a751260b5..63fabfc3f 100644 --- a/src/duckdb/src/include/duckdb/execution/reservoir_sample.hpp +++ b/src/duckdb/src/include/duckdb/execution/reservoir_sample.hpp @@ -299,6 +299,7 @@ class ReservoirSamplePercentage : public BlockingSample { //! Fetches a chunk from the sample. If destroy = true this method is descructive unique_ptr GetChunk() override; void Finalize() override; + idx_t GetActiveSampleCount() const; void Serialize(Serializer &serializer) const override; static unique_ptr Deserialize(Deserializer &deserializer); diff --git a/src/duckdb/src/include/duckdb/function/aggregate/distributive_functions.hpp b/src/duckdb/src/include/duckdb/function/aggregate/distributive_functions.hpp index f29d0961d..c704a26bf 100644 --- a/src/duckdb/src/include/duckdb/function/aggregate/distributive_functions.hpp +++ b/src/duckdb/src/include/duckdb/function/aggregate/distributive_functions.hpp @@ -73,7 +73,7 @@ struct AnyValueFun { struct MinFun { static constexpr const char *Name = "min"; - static constexpr const char *Parameters = "arg"; + static constexpr const char *Parameters = "arg,n"; static constexpr const char *Description = "Returns the minimum value present in arg."; static constexpr const char *Example = "min(A)"; static constexpr const char *Categories = ""; @@ -83,7 +83,7 @@ struct MinFun { struct MaxFun { static constexpr const char *Name = "max"; - static constexpr const char *Parameters = "arg"; + static constexpr const char *Parameters = "arg,n"; static constexpr const char *Description = "Returns the maximum value present in arg."; static constexpr const char *Example = "max(A)"; static constexpr const char *Categories = ""; @@ -103,7 +103,7 @@ struct DecimalAverageFun { struct CombineAggrFun { static constexpr const char *Name = "combine_aggr"; - static constexpr const char *Parameters = "arg"; + static constexpr const char *Parameters = "arg,multiplicities"; static constexpr const char *Description = "Combines exported aggregate states"; static constexpr const char *Example = "combine_aggr(state_column)"; static constexpr const char *Categories = ""; diff --git a/src/duckdb/src/include/duckdb/function/aggregate/list_aggregate.hpp b/src/duckdb/src/include/duckdb/function/aggregate/list_aggregate.hpp index d663c4d61..32c7d73b8 100644 --- a/src/duckdb/src/include/duckdb/function/aggregate/list_aggregate.hpp +++ b/src/duckdb/src/include/duckdb/function/aggregate/list_aggregate.hpp @@ -72,8 +72,7 @@ inline void ListClusterUpdate(Vector inputs[], AggregateInputData &aggr_input_da ListSegmentFunctions functions; GetSegmentDataFunctions(functions, input.GetType()); - for (idx_t run_idx = 0; run_idx < clustered.n_group_runs; run_idx++) { - auto &run = clustered.group_runs[run_idx]; + for (auto &run : clustered.runs()) { auto &state = *reinterpret_cast(run.state); auto run_sel = run.sel; diff --git a/src/duckdb/src/include/duckdb/function/aggregate_function.hpp b/src/duckdb/src/include/duckdb/function/aggregate_function.hpp index b94c78966..f8b4ef6e5 100644 --- a/src/duckdb/src/include/duckdb/function/aggregate_function.hpp +++ b/src/duckdb/src/include/duckdb/function/aggregate_function.hpp @@ -77,6 +77,19 @@ class BindAggregateFunctionInput : public BindFunctionInput { BoundAggregateFunction &bound_function; }; +class FunctionExpression; + +struct AggregateFunctionUnbindInput { + AggregateFunctionUnbindInput(const BoundAggregateExpression &expression_p, + vector> children_p); + ~AggregateFunctionUnbindInput(); + + const BoundAggregateExpression &expression; + vector> children; +}; + +typedef unique_ptr (*aggregate_function_unbind_t)(AggregateFunctionUnbindInput &input); + //! The type used for sizing hashed aggregate function states typedef idx_t (*aggregate_size_t)(AggregateStateInput &input); //! The type used for initializing hashed aggregate function states (batched: initializes `count` states) @@ -194,6 +207,9 @@ class AggregateFunctionCallbacks { bool HasBindCallback() const { return bind != nullptr; } bind_aggregate_function_t GetBindCallback() const { return bind; } void SetBindCallback(bind_aggregate_function_t callback) { bind = callback; } + bool HasUnbindCallback() const { return unbind != nullptr; } + aggregate_function_unbind_t GetUnbindCallback() const { return unbind; } + void SetUnbindCallback(aggregate_function_unbind_t callback) { unbind = callback; } bool HasStateInitCallback() const { return initialize != nullptr; } aggregate_initialize_t GetStateInitCallback() const { return initialize; } @@ -296,6 +312,7 @@ class AggregateFunctionCallbacks { //! The bind function (may be null) bind_aggregate_function_t bind = nullptr; + aggregate_function_unbind_t unbind = nullptr; //! The destructor method (may be null) aggregate_destructor_t destructor = nullptr; @@ -338,6 +355,9 @@ class AggregateFunctionProperties : public FunctionProperties { //! Whether a single input row finalizes to that input's first argument unchanged bool single_value_identity = false; + //! Whether an aggregate is holistic (e.g., don't use it for window segment trees...) + bool is_holistic = false; + bool operator==(const AggregateFunctionProperties &rhs) const; bool operator!=(const AggregateFunctionProperties &rhs) const; }; @@ -384,6 +404,10 @@ class BaseAggregateFunction { auto HasSingleValueIdentity() const -> bool { return properties.single_value_identity; } auto SetSingleValueIdentity(bool value) -> void { properties.single_value_identity = value; } + //! Whether the aggregate is holistic. + auto IsHolistic() const -> bool { return properties.is_holistic; } + auto SetIsHolistic(bool value) -> void { properties.is_holistic = value; } + // Derived properties bool CanAggregate() const { return callbacks.update || callbacks.combine || callbacks.finalize; } bool CanWindow() const { return callbacks.window || callbacks.window_batch; } @@ -393,6 +417,9 @@ class BaseAggregateFunction { auto HasBindCallback() const -> bool { return callbacks.bind != nullptr; } auto GetBindCallback() const -> bind_aggregate_function_t { return callbacks.bind; } auto SetBindCallback(bind_aggregate_function_t callback) -> void { callbacks.bind = callback; } + auto HasUnbindCallback() const -> bool { return callbacks.unbind != nullptr; } + auto GetUnbindCallback() const -> aggregate_function_unbind_t { return callbacks.unbind; } + auto SetUnbindCallback(aggregate_function_unbind_t callback) -> void { callbacks.unbind = callback; } auto HasStateInitCallback() const -> bool { return callbacks.initialize != nullptr; } auto GetStateInitCallback() const -> aggregate_initialize_t { return callbacks.initialize; } @@ -646,7 +673,7 @@ class AggregateFunction : public BaseAggregateFunction, public SimpleFunction { } template - static AggregateFunction NullaryAggregate(LogicalType return_type) { + static AggregateFunction NullaryAggregate(const LogicalType &return_type) { AggregateFunction result( Identifier(), {}, return_type, AggregateFunction::StateSize, AggregateFunction::StateInitialize, AggregateFunction::NullaryScatterUpdate, @@ -659,7 +686,7 @@ class AggregateFunction : public BaseAggregateFunction, public SimpleFunction { template static AggregateFunction - UnaryAggregate(const LogicalType &input_type, LogicalType return_type, + UnaryAggregate(const LogicalType &input_type, const LogicalType &return_type, FunctionNullHandling null_handling = FunctionNullHandling::DEFAULT_NULL_HANDLING) { AggregateFunction result(Identifier(), {input_type}, return_type, AggregateFunction::StateSize, AggregateFunction::StateInitialize, @@ -689,7 +716,7 @@ class AggregateFunction : public BaseAggregateFunction, public SimpleFunction { template static AggregateFunction BinaryAggregate(const LogicalType &a_type, const LogicalType &b_type, - LogicalType return_type) { + const LogicalType &return_type) { AggregateFunction result({a_type, b_type}, return_type, AggregateFunction::StateSize, AggregateFunction::StateInitialize, AggregateFunction::BinaryScatterUpdate, @@ -710,6 +737,10 @@ class AggregateFunction : public BaseAggregateFunction, public SimpleFunction { template static void WireStructStateType(AggregateFunction &result); + //! The state layout of a STATE with a STATE_TYPE - defined out-of-line (after BoundAggregateFunction is complete) + template + static AggregateStateLayout StructStateLayout(AggregateLayoutInput &input); + template static idx_t StateSize(AggregateStateInput &) { return sizeof(STATE); @@ -856,13 +887,32 @@ class BoundAggregateFunction : public BaseAggregateFunction, public BoundSimpleF const shared_ptr &GetDefinition() const { return definition; } + //! The number of arguments that were received by the standard and positional-only parameters, they come first + idx_t GetStandardArgumentCount() const { + return BoundSimpleFunction::GetStandardArgumentCount(definition->GetSignature()); + } + //! The number of arguments that were received by "*args", they directly follow the standard parameters + idx_t GetVarArgsCount() const { + return BoundSimpleFunction::GetVarArgsCount(definition->GetSignature()); + } + //! The number of arguments that were received by the keyword-only parameters, they follow "*args" + idx_t GetKeywordOnlyArgumentCount() const { + return BoundSimpleFunction::GetKeywordOnlyArgumentCount(definition->GetSignature()); + } + //! The number of arguments that were received by "**kwargs", they are the last arguments + idx_t GetKwargsCount() const { + return BoundSimpleFunction::GetKwargsCount(definition->GetSignature()); + } + //! The kind of the parameter that received the argument at the given index + FunctionParameterKind GetArgumentParameterKind(idx_t argument_index) const { + return BoundSimpleFunction::GetArgumentParameterKind(definition->GetSignature(), argument_index); + } //! Restore the definition after the bound function has been replaced wholesale, together with the //! qualification it carries - the replacement is a specialized implementation, not a different function void SetDefinition(shared_ptr definition_p) { definition = std::move(definition_p); if (definition) { - schema_name = definition->GetSchemaName(); - catalog_name = definition->GetCatalogName(); + qualified_name = definition->GetQualifiedName().WithName(GetName()); } } const vector &GetLogicalArguments() const { @@ -903,22 +953,7 @@ class BoundAggregateFunction : public BaseAggregateFunction, public BoundSimpleF template inline void AggregateFunction::WireStructStateType(AggregateFunction &result) { if constexpr (HasStructStateType::value) { - using ST = typename STATE::STATE_TYPE; - result.SetStructStateExport([](AggregateLayoutInput &input) { - auto &bound = input.function; - AggregateStateLayout layout; - if (bound.GetReturnType().IsAggregateState()) { - // the function has been modified for state export (see ExportAggregateFunction::SetStateExport) - - // its return type IS the state type already - layout.type = bound.GetReturnType(); - } else { - layout.type = AggregateFunction::BuildStateLogical(bound); - } - layout.total_state_size = AlignValue(sizeof(STATE)); - layout.field = BuildStateField(); - AggregateStateField::PopulateListFunctions(layout.type, layout.field); - return layout; - }); + result.SetStructStateExport(StructStateLayout); } else if constexpr (HasPrimitiveLogicalType::value) { result.SetStructStateExport([](AggregateLayoutInput &) { return AggregateStateLayout(PrimitiveToLogicalType(), AlignValue(sizeof(STATE))); @@ -926,6 +961,24 @@ inline void AggregateFunction::WireStructStateType(AggregateFunction &result) { } } +template +inline AggregateStateLayout AggregateFunction::StructStateLayout(AggregateLayoutInput &input) { + using ST = typename STATE::STATE_TYPE; + auto &bound = input.function; + AggregateStateLayout layout; + if (bound.GetReturnType().IsAggregateState()) { + // the function has been modified for state export (see ExportAggregateFunction::SetStateExport) - + // its return type IS the state type already + layout.type = bound.GetReturnType(); + } else { + layout.type = AggregateFunction::BuildStateLogical(bound); + } + layout.total_state_size = AlignValue(sizeof(STATE)); + layout.field = BuildStateField(); + AggregateStateField::PopulateListFunctions(layout.type, layout.field); + return layout; +} + // Defined here (after BoundAggregateFunction is complete) so the body can access the bound function's types. template inline LogicalType AggregateFunction::BuildStateLogical(const BoundAggregateFunction &bound_function) { diff --git a/src/duckdb/src/include/duckdb/function/aggregate_state_layout.hpp b/src/duckdb/src/include/duckdb/function/aggregate_state_layout.hpp index e7c5a137d..b30423af4 100644 --- a/src/duckdb/src/include/duckdb/function/aggregate_state_layout.hpp +++ b/src/duckdb/src/include/duckdb/function/aggregate_state_layout.hpp @@ -248,6 +248,8 @@ struct AggregateStateField { //! The segment functions used to read/write the linked list - only set when kind is LIST //! (populated by PopulateListFunctions, which requires the resolved logical type) ListSegmentFunctions list_functions; + //! For LIST: whether the linked list cannot contain NULL elements - importing a NULL element is an error + bool reject_null_elements = false; //! The alignment of this field when placed as a struct member, mirroring the C++ struct layout rules. //! For OPTIONAL_VALUE the alignment is that of the wrapped value - the trailing is_set bool does not affect it. @@ -488,6 +490,9 @@ AggregateStateField BuildStateField() { //! field.children=[{kind=STRUCT, field_offset=0, children=[struct fields]}]. //! - Non-optional struct: field.kind=STRUCT, field.field_offset=0, field.children=[struct fields]. //! total_state_size is the aligned stride between consecutive states in a packed buffer. +//! Checks `count` imported states, `stride` bytes apart - throws if a state is invalid +typedef void (*aggregate_validate_state_t)(const_data_ptr_t states, idx_t count, idx_t stride); + struct AggregateStateLayout { AggregateStateLayout() = default; AggregateStateLayout(LogicalType type_p, idx_t total_state_size_p, bool is_optional = false) @@ -518,6 +523,8 @@ struct AggregateStateLayout { idx_t total_state_size = 0; //! Constant values for arguments that must be re-bound with a specific constant rather than only the type unordered_map constant_parameters; + //! Checks the imported states for invariants the fields cannot express (e.g. between fields) + aggregate_validate_state_t validate_state = nullptr; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/optimizer/builtin_function_lookup.hpp b/src/duckdb/src/include/duckdb/function/builtin_function_lookup.hpp similarity index 73% rename from src/duckdb/src/include/duckdb/optimizer/builtin_function_lookup.hpp rename to src/duckdb/src/include/duckdb/function/builtin_function_lookup.hpp index 05b70bf16..081dfeec0 100644 --- a/src/duckdb/src/include/duckdb/optimizer/builtin_function_lookup.hpp +++ b/src/duckdb/src/include/duckdb/function/builtin_function_lookup.hpp @@ -1,7 +1,7 @@ //===----------------------------------------------------------------------===// // DuckDB // -// duckdb/optimizer/builtin_function_lookup.hpp +// duckdb/function/builtin_function_lookup.hpp // // //===----------------------------------------------------------------------===// @@ -19,6 +19,7 @@ class BoundFunctionExpression; class ClientContext; class Expression; class ScalarFunction; +class WindowFunction; //! Look up a built-in scalar function in the system catalog and select the overload matching the given argument //! types. Rewrites that introduce a registered built-in resolve it this way so that the bound function keeps the @@ -32,7 +33,15 @@ shared_ptr GetBuiltinAggregateFunction(ClientContext &c shared_ptr TryGetBuiltinAggregateFunction(ClientContext &context, const Identifier &name, const vector &arguments); +//! Window counterpart of GetBuiltinScalarFunction +shared_ptr GetBuiltinWindowFunction(ClientContext &context, const Identifier &name, + const vector &arguments); + //! Look up a built-in scalar function as GetBuiltinScalarFunction does, and bind it to the given children +//! The result can be any expression, e.g. a constant if the function was folded during binding +unique_ptr BindBuiltinScalarExpression(ClientContext &context, const Identifier &name, + vector> children); +//! As BindBuiltinScalarExpression, but throws if the result is not a BoundFunctionExpression unique_ptr BindBuiltinScalarFunction(ClientContext &context, const Identifier &name, vector> children); diff --git a/src/duckdb/src/include/duckdb/function/cast/cast_function_set.hpp b/src/duckdb/src/include/duckdb/function/cast/cast_function_set.hpp index 97e539441..c1dcc8ff9 100644 --- a/src/duckdb/src/include/duckdb/function/cast/cast_function_set.hpp +++ b/src/duckdb/src/include/duckdb/function/cast/cast_function_set.hpp @@ -45,6 +45,7 @@ class CastFunctionSet { public: DUCKDB_API static CastFunctionSet &Get(ClientContext &context); + DUCKDB_API static const CastFunctionSet &Get(const ClientContext &context); DUCKDB_API static CastFunctionSet &Get(DatabaseInstance &db); //! Returns a cast function (from source -> target) @@ -64,6 +65,9 @@ class CastFunctionSet { int64_t implicit_cast_cost = -1); DUCKDB_API void RegisterCastFunction(const LogicalType &source, const LogicalType &target, bind_cast_function_t bind, int64_t implicit_cast_cost = -1); + //! Whether a registered cast can change this default conversion, including nested casts. + //! Data-dependent VARIANT conversions are treated conservatively. + DUCKDB_API bool CanOverrideDefaultCast(const LogicalType &source, const LogicalType &target) const; //! Register a combine rule for LogicalType::TryGetMaxLogicalType, consulted before previously registered rules //! and the built-in rules @@ -85,9 +89,13 @@ class CastFunctionSet { vector combine_rules; //! If any custom cast functions have been defined using RegisterCastFunction, this holds the map optional_ptr map_info; + //! A default-only binding probe observes registrations without invoking them. + optional_ptr registration_probe; + bool registered_cast_found = false; private: void RegisterCastFunction(const LogicalType &source, const LogicalType &target, MapCastNode node); + bool HasRegisteredCast(const LogicalType &source, const LogicalType &target) const; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/function/cast/default_casts.hpp b/src/duckdb/src/include/duckdb/function/cast/default_casts.hpp index f49d2ba5e..30e19bb6a 100644 --- a/src/duckdb/src/include/duckdb/function/cast/default_casts.hpp +++ b/src/duckdb/src/include/duckdb/function/cast/default_casts.hpp @@ -241,6 +241,7 @@ struct DefaultCasts { static BoundCastInfo VariantCastSwitch(BindCastInput &input, const LogicalType &source, const LogicalType &target); static BoundCastInfo UUIDCastSwitch(BindCastInput &input, const LogicalType &source, const LogicalType &target); static BoundCastInfo GeoCastSwitch(BindCastInput &input, const LogicalType &source, const LogicalType &target); + static BoundCastInfo BlobToGeoCast(BindCastInput &input, const LogicalType &source, const LogicalType &target); static BoundCastInfo TypeCastSwitch(BindCastInput &input, const LogicalType &source, const LogicalType &target); static BoundCastInfo BignumCastSwitch(BindCastInput &input, const LogicalType &source, const LogicalType &target); static BoundCastInfo ImplicitToUnionCast(BindCastInput &input, const LogicalType &source, diff --git a/src/duckdb/src/include/duckdb/function/cast/variant/json_to_variant.hpp b/src/duckdb/src/include/duckdb/function/cast/variant/json_to_variant.hpp index 68c30b4e5..25faa7444 100644 --- a/src/duckdb/src/include/duckdb/function/cast/variant/json_to_variant.hpp +++ b/src/duckdb/src/include/duckdb/function/cast/variant/json_to_variant.hpp @@ -4,6 +4,7 @@ #include "duckdb/function/scalar/variant_utils.hpp" #include "duckdb/function/cast/default_casts.hpp" #include "yyjson.hpp" +#include "yyjson_memory.hpp" #include "duckdb/common/serializer/memory_stream.hpp" #include "duckdb/common/serializer/varint.hpp" #include "duckdb/common/typedefs.hpp" @@ -21,22 +22,6 @@ using namespace duckdb_yyjson; // NOLINT namespace { -struct ReadJSONHolder { -public: - ~ReadJSONHolder() { - if (doc) { - yyjson_doc_free(doc); - } - } - void Reset() { - yyjson_doc_free(doc); - doc = nullptr; - } - -public: - yyjson_doc *doc = nullptr; -}; - static inline string_t GetString(yyjson_val *val) { return string_t(unsafe_yyjson_get_str(val), NumericCast(unsafe_yyjson_get_len(val))); } @@ -177,7 +162,8 @@ static bool ConvertJSONPrimitive(yyjson_val *val, ToVariantGlobalResultData &res VarintEncode(length, str_blob_data); memcpy(str_blob_data + length_varint_size, const_data_ptr_cast(str), length); } - blob_offset_data[result_index] += length_varint_size + length; + OffsetData::AddBlobOffset(blob_offset_data[result_index], length_varint_size); + OffsetData::AddBlobOffset(blob_offset_data[result_index], length); break; } case YYJSON_TYPE_BOOL | YYJSON_SUBTYPE_TRUE: { @@ -197,7 +183,7 @@ static bool ConvertJSONPrimitive(yyjson_val *val, ToVariantGlobalResultData &res auto value = unsafe_yyjson_get_uint(val); memcpy(blob_data + blob_offset_data[result_index], const_data_ptr_cast(&value), sizeof(uint64_t)); } - blob_offset_data[result_index] += sizeof(uint64_t); + OffsetData::AddBlobOffset(blob_offset_data[result_index], sizeof(uint64_t)); break; } case YYJSON_TYPE_NUM | YYJSON_SUBTYPE_SINT: { @@ -207,7 +193,7 @@ static bool ConvertJSONPrimitive(yyjson_val *val, ToVariantGlobalResultData &res auto value = unsafe_yyjson_get_sint(val); memcpy(blob_data + blob_offset_data[result_index], const_data_ptr_cast(&value), sizeof(int64_t)); } - blob_offset_data[result_index] += sizeof(int64_t); + OffsetData::AddBlobOffset(blob_offset_data[result_index], sizeof(int64_t)); break; } case YYJSON_TYPE_RAW | YYJSON_SUBTYPE_NONE: @@ -226,7 +212,7 @@ static bool ConvertJSONPrimitive(yyjson_val *val, ToVariantGlobalResultData &res } memcpy(blob_data + blob_offset_data[result_index], const_data_ptr_cast(&value), sizeof(double)); } - blob_offset_data[result_index] += sizeof(double); + OffsetData::AddBlobOffset(blob_offset_data[result_index], sizeof(double)); break; } default: @@ -271,7 +257,7 @@ bool ConvertJSONToVariant(ToVariantSourceData &source, ToVariantGlobalResultData auto blob_offset_data = OffsetData::GetBlob(result.offsets); auto &variant = result.variant; - ReadJSONHolder json_holder; + JSONAllocator json_allocator(result.allocator); for (idx_t i = 0; i < count; i++) { auto source_index = source[i]; auto result_index = selvec ? selvec->get_index(i) : i; @@ -290,22 +276,24 @@ bool ConvertJSONToVariant(ToVariantSourceData &source, ToVariantGlobalResultData } auto &val = source_data[source_index]; - json_holder.doc = - yyjson_read(val.GetData(), val.GetSize(), - YYJSON_READ_ALLOW_INF_AND_NAN | YYJSON_READ_ALLOW_TRAILING_COMMAS | YYJSON_READ_BIGNUM_AS_RAW); - if (!json_holder.doc) { + auto doc = yyjson_read_opts(val.GetDataWriteable(), val.GetSize(), + YYJSON_READ_ALLOW_INF_AND_NAN | YYJSON_READ_ALLOW_TRAILING_COMMAS | + YYJSON_READ_BIGNUM_AS_RAW, + json_allocator.GetYYAlc(), nullptr); + if (!doc) { + json_allocator.Reset(); if (!IGNORE_NULLS) { HandleVariantNull(result, result_index, values_offset_data, blob_offset_data[result_index], values_index_selvec, i, is_root); } continue; } - auto *root = yyjson_doc_get_root(json_holder.doc); + auto *root = yyjson_doc_get_root(doc); if (!ConvertJSON(root, result, result_index, is_root)) { return false; } - json_holder.Reset(); + json_allocator.Reset(); } return true; } diff --git a/src/duckdb/src/include/duckdb/function/cast/variant/primitive_to_variant.hpp b/src/duckdb/src/include/duckdb/function/cast/variant/primitive_to_variant.hpp index 54635f12f..86abb7d4b 100644 --- a/src/duckdb/src/include/duckdb/function/cast/variant/primitive_to_variant.hpp +++ b/src/duckdb/src/include/duckdb/function/cast/variant/primitive_to_variant.hpp @@ -16,6 +16,14 @@ struct EnumConversionPayload { EnumConversionPayload(const string_t *values, idx_t size) : values(values), size(size) { } +public: + const string_t &GetString(idx_t index) const { + if (index >= size) { + throw InternalException("ENUM index %llu is out of range for an ENUM with %llu values", index, size); + } + return values[index]; + } + public: const string_t *values; idx_t size; @@ -101,19 +109,19 @@ static void WriteData(data_ptr_t ptr, const T &val, uint32_t lengths[3], EnumCon template <> void WriteData(data_ptr_t ptr, const uint8_t &val, uint32_t lengths[3], EnumConversionPayload &payload) { EmptyConversionPayloadToVariant empty_payload; - auto &str = payload.values[val]; + auto &str = payload.GetString(val); WriteData(ptr, str, lengths, empty_payload); } template <> void WriteData(data_ptr_t ptr, const uint16_t &val, uint32_t lengths[3], EnumConversionPayload &payload) { EmptyConversionPayloadToVariant empty_payload; - auto &str = payload.values[val]; + auto &str = payload.GetString(val); WriteData(ptr, str, lengths, empty_payload); } template <> void WriteData(data_ptr_t ptr, const uint32_t &val, uint32_t lengths[3], EnumConversionPayload &payload) { EmptyConversionPayloadToVariant empty_payload; - auto &str = payload.values[val]; + auto &str = payload.GetString(val); WriteData(ptr, str, lengths, empty_payload); } @@ -184,19 +192,19 @@ static void GetValueSize(const T &val, uint32_t lengths[3], idx_t &lengths_size, template <> void GetValueSize(const uint8_t &val, uint32_t lengths[3], idx_t &lengths_size, EnumConversionPayload &payload) { EmptyConversionPayloadToVariant empty_payload; - auto &str = payload.values[val]; + auto &str = payload.GetString(val); GetValueSize(str, lengths, lengths_size, empty_payload); } template <> void GetValueSize(const uint16_t &val, uint32_t lengths[3], idx_t &lengths_size, EnumConversionPayload &payload) { EmptyConversionPayloadToVariant empty_payload; - auto &str = payload.values[val]; + auto &str = payload.GetString(val); GetValueSize(str, lengths, lengths_size, empty_payload); } template <> void GetValueSize(const uint32_t &val, uint32_t lengths[3], idx_t &lengths_size, EnumConversionPayload &payload) { EmptyConversionPayloadToVariant empty_payload; - auto &str = payload.values[val]; + auto &str = payload.GetString(val); GetValueSize(str, lengths, lengths_size, empty_payload); } @@ -215,9 +223,10 @@ static bool ConvertPrimitiveTemplated(ToVariantSourceData &source, ToVariantGlob auto &variant = result.variant; uint32_t lengths[3]; - idx_t lengths_size = 0; for (idx_t i = 0; i < count; i++) { auto index = source[i]; + // the blob space to reserve for this row - nothing for NULL rows + idx_t lengths_size = 0; auto result_index = selvec ? selvec->get_index(i) : i; auto &blob_offset = blob_offset_data[result_index]; @@ -225,7 +234,6 @@ static bool ConvertPrimitiveTemplated(ToVariantSourceData &source, ToVariantGlob if (TYPE_ID != VariantLogicalType::VARIANT_NULL && source_validity.RowIsValid(index)) { //! Write the value auto &val = source_data[index]; - lengths_size = 0; GetValueSize(val, lengths, lengths_size, payload); WriteVariantMetadata(result, result_index, values_offset_data, blob_offset, values_index_selvec, i, GetTypeId(val)); @@ -240,7 +248,7 @@ static bool ConvertPrimitiveTemplated(ToVariantSourceData &source, ToVariantGlob } for (idx_t j = 0; j < lengths_size; j++) { - blob_offset += lengths[j]; + OffsetData::AddBlobOffset(blob_offset, lengths[j]); } } return true; diff --git a/src/duckdb/src/include/duckdb/function/cast/variant/struct_to_variant.hpp b/src/duckdb/src/include/duckdb/function/cast/variant/struct_to_variant.hpp index 465d21244..5b421514d 100644 --- a/src/duckdb/src/include/duckdb/function/cast/variant/struct_to_variant.hpp +++ b/src/duckdb/src/include/duckdb/function/cast/variant/struct_to_variant.hpp @@ -17,10 +17,7 @@ bool ConvertStructToVariant(ToVariantSourceData &source, ToVariantGlobalResultDa auto blob_offset_data = OffsetData::GetBlob(result.offsets); auto children_offset_data = OffsetData::GetChildren(result.offsets); auto &type = source.vec.GetType(); - // unnamed STRUCTs can't be represented as a VARIANT OBJECT (duplicate empty keys lose data) - they should be TUPLEs - if (StructType::IsUnnamed(type)) { - throw ConversionException("Can't cast unnamed struct to VARIANT"); - } + D_ASSERT(type.id() == LogicalTypeId::STRUCT); auto &source_format = source.source_format; auto &source_validity = source_format.validity; diff --git a/src/duckdb/src/include/duckdb/function/cast/variant/to_variant_fwd.hpp b/src/duckdb/src/include/duckdb/function/cast/variant/to_variant_fwd.hpp index 2924ecf51..65ead7f16 100644 --- a/src/duckdb/src/include/duckdb/function/cast/variant/to_variant_fwd.hpp +++ b/src/duckdb/src/include/duckdb/function/cast/variant/to_variant_fwd.hpp @@ -11,6 +11,7 @@ #include "duckdb/common/serializer/varint.hpp" #include "duckdb/common/types/decimal.hpp" #include "duckdb/common/exception/conversion_exception.hpp" +#include "duckdb/common/checked_integer.hpp" namespace duckdb { namespace variant { @@ -31,13 +32,24 @@ struct OffsetData { static uint32_t *GetBlob(DataChunk &offsets) { return FlatVector::GetDataMutable(offsets.data[3]); } + //! Adds len to offset, throwing if the running total would exceed what a uint32_t blob offset can represent. + static void AddBlobOffset(uint32_t &offset, uint32_t len) { + try { + offset = uinteger_t(offset) += len; + } catch (OutOfRangeException &) { + throw InvalidInputException( + "Cannot convert value to VARIANT: encoded row size exceeds the maximum supported %u bytes", + NumericLimits::Maximum()); + } + } }; struct ToVariantGlobalResultData { public: ToVariantGlobalResultData(VariantVectorData &variant, DataChunk &offsets, - OrderedOwningStringMap &dictionary, SelectionVector &keys_selvec) - : variant(variant), offsets(offsets), dictionary(dictionary), keys_selvec(keys_selvec) { + OrderedOwningStringMap &dictionary, SelectionVector &keys_selvec, + Allocator &allocator) + : variant(variant), offsets(offsets), dictionary(dictionary), keys_selvec(keys_selvec), allocator(allocator) { } public: @@ -54,14 +66,15 @@ struct ToVariantGlobalResultData { OrderedOwningStringMap &dictionary; //! The selection vector to populate with mapping from keys index -> dictionary index SelectionVector &keys_selvec; + //! Allocator for temporary conversion memory (e.g. JSON parsing), tracked by DuckDB's memory accounting + Allocator &allocator; }; template void WriteContainerData(VariantVectorData &result, idx_t result_index, uint32_t &blob_offset, idx_t length, idx_t children_offset) { const auto length_varint_size = GetVarintSize(length); - const auto child_offset_varint_size = - NumericCast(length_varint_size ? GetVarintSize(children_offset) : 0); + const auto child_offset_varint_size = NumericCast(length ? GetVarintSize(children_offset) : 0); if (WRITE_DATA) { auto &blob_value = result.blob_data[result_index]; @@ -72,7 +85,7 @@ void WriteContainerData(VariantVectorData &result, idx_t result_index, uint32_t } } - blob_offset += length_varint_size + child_offset_varint_size; + OffsetData::AddBlobOffset(blob_offset, length_varint_size + child_offset_varint_size); } struct ContainerSelectionVectors { diff --git a/src/duckdb/src/include/duckdb/function/cast/variant/variant_to_variant.hpp b/src/duckdb/src/include/duckdb/function/cast/variant/variant_to_variant.hpp index c71dd269f..f437e8929 100644 --- a/src/duckdb/src/include/duckdb/function/cast/variant/variant_to_variant.hpp +++ b/src/duckdb/src/include/duckdb/function/cast/variant/variant_to_variant.hpp @@ -290,9 +290,6 @@ bool ConvertVariantToVariant(ToVariantSourceData &source_data, ToVariantGlobalRe result.values_index_data[values_index_selvec->get_index(source_index)] = values_offset; } - //! FIXME: we might want to add some checks to make sure the NumericLimits::Maximum isn't exceeded, - //! but that's hard to test - //! First write all children //! NOTE: this has to happen first because we use 'values_offset', which is increased when we write the values auto source_children_list_entry = source.GetChildrenListEntry(scan_index); @@ -345,14 +342,14 @@ bool ConvertVariantToVariant(ToVariantSourceData &source_data, ToVariantGlobalRe for (uint32_t source_value_index = 0; source_value_index < source_values_list_entry.length; source_value_index++) { values_offset_data[result_index]++; - blob_size += VariantVisitor::Visit(source, scan_index, source_value_index, - analyze_state); + OffsetData::AddBlobOffset(blob_size, VariantVisitor::Visit( + source, scan_index, source_value_index, analyze_state)); } } keys_offset += keys_count; children_offset += source_children_list_entry.length; - blob_offset += blob_size; + OffsetData::AddBlobOffset(blob_offset, blob_size); } return true; } diff --git a/src/duckdb/src/include/duckdb/function/cast/vector_cast_helpers.hpp b/src/duckdb/src/include/duckdb/function/cast/vector_cast_helpers.hpp index f507cca67..05879dcc5 100644 --- a/src/duckdb/src/include/duckdb/function/cast/vector_cast_helpers.hpp +++ b/src/duckdb/src/include/duckdb/function/cast/vector_cast_helpers.hpp @@ -76,8 +76,8 @@ struct VectorTryCastStringOperator { OP::template Operation(input, output, data.result, data.parameters))) { return output; } - return HandleVectorCastError::Operation(CastExceptionText(input), mask, - idx, data); + return HandleVectorCastError::Operation( + CastExceptionText(input, data.result.GetType()), mask, idx, data); } }; diff --git a/src/duckdb/src/include/duckdb/function/compression_function.hpp b/src/duckdb/src/include/duckdb/function/compression_function.hpp index 54cebc0c5..0068ee52b 100644 --- a/src/duckdb/src/include/duckdb/function/compression_function.hpp +++ b/src/duckdb/src/include/duckdb/function/compression_function.hpp @@ -10,6 +10,7 @@ #include "duckdb/common/common.hpp" #include "duckdb/common/enums/compression_type.hpp" +#include "duckdb/common/enums/scan_vector_type.hpp" #include "duckdb/common/map.hpp" #include "duckdb/common/insertion_order_preserving_map.hpp" #include "duckdb/common/mutex.hpp" @@ -162,8 +163,11 @@ typedef void (*compression_scan_vector_t)(ColumnSegment &segment, ColumnScanStat typedef void (*compression_scan_partial_t)(ColumnSegment &segment, ColumnScanState &state, idx_t scan_count, Vector &result, idx_t result_offset); //! Function prototype used for reading a subset of the values of a vector indicated by a selection vector +//! The selected values are written to 'result' starting at 'result_offset'. Emitting a compressed vector (e.g. a +//! constant vector) is only allowed if 'scan_type' is SCAN_ENTIRE_VECTOR typedef void (*compression_select_t)(ColumnSegment &segment, ColumnScanState &state, idx_t vector_count, Vector &result, - const SelectionVector &sel, idx_t sel_count); + const SelectionVector &sel, idx_t sel_count, idx_t result_offset, + ScanVectorType scan_type); //! Function prototype used for applying a filter to a vector while scanning that vector typedef void (*compression_filter_t)(ColumnSegment &segment, ColumnScanState &state, idx_t vector_count, Vector &result, SelectionVector &sel, idx_t &sel_count, const TableFilter &filter, diff --git a/src/duckdb/src/include/duckdb/function/create_sort_key.hpp b/src/duckdb/src/include/duckdb/function/create_sort_key.hpp index eeffb0b42..f2e0e235a 100644 --- a/src/duckdb/src/include/duckdb/function/create_sort_key.hpp +++ b/src/duckdb/src/include/duckdb/function/create_sort_key.hpp @@ -12,6 +12,8 @@ namespace duckdb { +class Expression; + struct OrderModifiers { OrderModifiers(OrderType order_type, OrderByNullType null_type) : order_type(order_type), null_type(null_type) { } @@ -69,6 +71,9 @@ struct DecodeSortKeyFun { static constexpr const char *Categories = ""; static ScalarFunction GetFunction(); + //! Binds decode_sort_key from bound column types, bypassing the VARCHAR column definitions the SQL form parses + static unique_ptr Bind(unique_ptr sort_key, child_list_t columns, + vector modifiers); }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/function/function.hpp b/src/duckdb/src/include/duckdb/function/function.hpp index 9fc1bd567..49fba1165 100644 --- a/src/duckdb/src/include/duckdb/function/function.hpp +++ b/src/duckdb/src/include/duckdb/function/function.hpp @@ -17,6 +17,7 @@ #include "duckdb/common/optional_idx.hpp" #include "duckdb/common/optional.hpp" #include "duckdb/common/optional_ptr.hpp" +#include "duckdb/parser/qualified_name.hpp" namespace duckdb { class CatalogEntry; @@ -40,10 +41,6 @@ class WindowFunction; class WindowFunctionSet; class BoundSimpleFunction; -struct BoundBetweenExpression; -struct BoundCastExpression; -struct BetweenFunctionData; -struct CastFunctionData; struct PragmaInfo; //! The default null handling is NULL in, NULL out @@ -59,31 +56,18 @@ enum class FunctionStability : uint8_t { CONSISTENT = 0, VOLATILE = 1, CONSISTEN //! PROPAGATE_COLLATIONS -> this function combines collation from its inputs and emits them again (default) //! PUSH_COMBINABLE_COLLATIONS -> combinable collations are executed for the input arguments //! IGNORE_COLLATIONS -> collations are completely ignored by the function +//! PUSH_DISTINCT_COLLATIONS -> aggregate DISTINCT arguments are replaced by their comparison keys +//! Only valid for aggregates that can consume keys instead of original values. +//! Ordinary (non-DISTINCT) calls keep their original arguments. enum class FunctionCollationHandling : uint8_t { PROPAGATE_COLLATIONS = 0, PUSH_COMBINABLE_COLLATIONS = 1, - IGNORE_COLLATIONS = 2 + IGNORE_COLLATIONS = 2, + PUSH_DISTINCT_COLLATIONS = 3 }; struct FunctionData { public: - FunctionData() = default; - FunctionData(const FunctionData &) : internal_kind(InternalKind::GENERIC) { - } - FunctionData(FunctionData &&) : internal_kind(InternalKind::GENERIC) { - } - FunctionData &operator=(const FunctionData &other) { - if (this != &other) { - internal_kind = InternalKind::GENERIC; - } - return *this; - } - FunctionData &operator=(FunctionData &&other) { - if (this != &other) { - internal_kind = InternalKind::GENERIC; - } - return *this; - } DUCKDB_API virtual ~FunctionData(); DUCKDB_API virtual unique_ptr Copy() const = 0; @@ -106,22 +90,6 @@ struct FunctionData { TARGET &CastNoConst() const { return const_cast(Cast()); // NOLINT: FIXME } - -private: - enum InternalKind : uint8_t { GENERIC = 0, BOUND_CAST, BOUND_BETWEEN }; - - explicit FunctionData(InternalKind internal_kind_p) : internal_kind(internal_kind_p) { - } - InternalKind GetInternalKind() const { - return internal_kind; - } - - InternalKind internal_kind = InternalKind::GENERIC; - - friend struct BoundBetweenExpression; - friend struct BoundCastExpression; - friend struct BetweenFunctionData; - friend struct CastFunctionData; }; struct TableFunctionData : public FunctionData { @@ -151,17 +119,36 @@ struct FunctionLocalState { struct FunctionParameters { vector values; - named_parameter_map_t named_parameters; + named_argument_map_t named_parameters; + //! The pragma function the parameters were bound for + optional_ptr function; +}; + +//! How a parameter receives its arguments, mirroring Python's parameter kinds, in the order a signature declares them +//! POSITIONAL_ONLY -> declared before "/", can only be passed by position. Its name is invisible to a caller, so an +//! argument of that name is unmatched and reaches "**kwargs" instead +//! STANDARD -> can be passed by position or by name +//! VAR_POSITIONAL -> "*args", receives all remaining positional arguments +//! KEYWORD_ONLY -> declared after "*args", can only be passed by name +//! VAR_KEYWORD -> "**kwargs", receives all named arguments that do not match another parameter +enum class FunctionParameterKind : uint8_t { + POSITIONAL_ONLY = 0, + STANDARD = 1, + VAR_POSITIONAL = 2, + KEYWORD_ONLY = 3, + VAR_KEYWORD = 4 }; class FunctionParameter { public: - FunctionParameter(Identifier name, LogicalType type) - : name(std::move(name)), type(std::move(type)), default_value(nullptr) { - } - - FunctionParameter(Identifier name, LogicalType type, Value value) - : name(std::move(name)), type(std::move(type)), default_value(make_shared_ptr(std::move(value))) { + FunctionParameter(Identifier name, LogicalType type, optional value = {}, + FunctionParameterKind kind = FunctionParameterKind::STANDARD) + : name(std::move(name)), type(std::move(type)), kind(kind) { + if (value) { + default_value = make_shared_ptr(std::move(*value)); + } else { + default_value = nullptr; + } } string ToString() const; @@ -193,31 +180,92 @@ class FunctionParameter { return default_value != nullptr; } + auto GetKind() const -> FunctionParameterKind { + return kind; + } + //! Whether this is a "*args" or "**kwargs" parameter + auto IsVariadic() const -> bool { + return kind == FunctionParameterKind::VAR_POSITIONAL || kind == FunctionParameterKind::VAR_KEYWORD; + } + //! Whether a caller can pass this parameter by position + auto AcceptsPosition() const -> bool { + return kind == FunctionParameterKind::STANDARD || kind == FunctionParameterKind::POSITIONAL_ONLY; + } + //! Whether a caller can pass this parameter by name + auto AcceptsName() const -> bool { + return kind == FunctionParameterKind::STANDARD || kind == FunctionParameterKind::KEYWORD_ONLY; + } + private: Identifier name; LogicalType type; shared_ptr default_value; + FunctionParameterKind kind; +}; + +//! An option a function receives through its "**kwargs" parameter +class TypedKwarg { +public: + TypedKwarg(Identifier name, LogicalType type); + + //! The name the function receives the option under + Identifier name; + //! Other names a caller can pass the option by + vector aliases; + //! The type a passed value is cast to - ANY passes it through as-is, for the function to check + LogicalType type; + +public: + DUCKDB_API bool operator==(const TypedKwarg &other) const; + DUCKDB_API bool operator!=(const TypedKwarg &other) const; + DUCKDB_API string ToString() const; +}; + +//! The "options" a function receives through its "**kwargs" parameter +//! This is basically just a way to have the binder enforce a set of accepted types and names +//! as trailing keyword-only parameters, without having to declare them as actual parameters. +//! "options" are not really part of the function signature, so they do not affect overload resolution +class TypedKwargs { +public: + //! Adds an option - a call that leaves it out does not pass it, the function decides what that means + DUCKDB_API TypedKwargs &Add(Identifier name, LogicalType type); + //! Adds another name for the option added last + DUCKDB_API TypedKwargs &Alias(Identifier alias); + + //! A schema holding the options of this one followed by those of the other + DUCKDB_API TypedKwargs Merge(const TypedKwargs &other) const; + + //! The option a name or an alias refers to, or nullptr if the schema declares no such name + DUCKDB_API optional_ptr Find(const Identifier &name) const; + //! The options in the order they were added + DUCKDB_API const vector &GetOptions() const; + //! Every name and alias the schema declares + DUCKDB_API vector GetNames() const; + + DUCKDB_API bool operator==(const TypedKwargs &other) const; + DUCKDB_API bool operator!=(const TypedKwargs &other) const; + DUCKDB_API hash_t Hash() const; + + //! Adding does not check the names - this does, once the schema is complete + //! @throws InvalidInputException if a name or an alias is declared twice + DUCKDB_API void Verify() const; + +private: + vector options; }; class FunctionSignature { public: FunctionSignature() = default; - FunctionSignature(vector arguments, LogicalType varargs, LogicalType return_type) - : varargs(std::move(varargs)), return_type(std::move(return_type)) { + FunctionSignature(vector parameters, LogicalType return_type) + : parameters(std::move(parameters)), return_type(std::move(return_type)) { + } + FunctionSignature(vector arguments, LogicalType return_type) : return_type(std::move(return_type)) { for (auto &arg : arguments) { AddParameter(std::move(arg)); } } - FunctionSignature(vector parameters, LogicalType return_type) - : parameters(std::move(parameters)), varargs(LogicalTypeId::INVALID), return_type(std::move(return_type)) { - } - FunctionSignature(vector parameters, LogicalType varargs, LogicalType return_type) - : parameters(std::move(parameters)), varargs(std::move(varargs)), return_type(std::move(return_type)) { - } - FunctionSignature(vector arguments, LogicalType return_type) - : FunctionSignature(std::move(arguments), LogicalType(LogicalTypeId::INVALID), std::move(return_type)) { - } string ToString() const; @@ -225,6 +273,15 @@ class FunctionSignature { bool operator!=(const FunctionSignature &other) const; bool Equal(const FunctionSignature &other) const; + //! Whether both declare the same overload. As in C++ and Postgres, the types identify an overload, and a call that + //! matches two distinct ones is reported as ambiguous when it is made. They are the same when: + //! - the parameters a caller can pass by position match in order, by type - whatever their names, and whether + //! they are positional-only or not + //! - "*args" and "**kwargs" match, by type + //! - the keyword-only parameters match by name and type, in any order - a caller can only pass them by name + //! Defaults and the options of "**kwargs" are ignored: every call the overload without a default accepts, both + //! accept at the same cost, and options take no part in overload selection + DUCKDB_API bool IsSameOverload(const FunctionSignature &other) const; public: auto GetParameter(idx_t index) const -> const FunctionParameter & { @@ -239,7 +296,6 @@ class FunctionSignature { auto GetParameterCount() const -> idx_t { return parameters.size(); } - auto GetReturnType() const -> const LogicalType & { return return_type; } @@ -247,81 +303,124 @@ class FunctionSignature { return_type = std::move(return_type_p); } - auto HasVarArgs() const -> bool { - return varargs.id() != LogicalTypeId::INVALID; + auto IsVariadic() const -> bool { + for (auto ¶m : parameters) { + if (param.IsVariadic()) { + return true; + } + } + return false; } - auto GetVarArgs() const -> const LogicalType & { - return varargs; + + auto GetArgs() const -> optional_ptr { + for (auto ¶m : parameters) { + if (param.GetKind() == FunctionParameterKind::VAR_POSITIONAL) { + return ¶m; + } + } + return nullptr; } - auto SetVarArgs(LogicalType varargs_p) -> void { - varargs = std::move(varargs_p); + + auto GetKwargs() const -> optional_ptr { + for (auto ¶m : parameters) { + if (param.GetKind() == FunctionParameterKind::VAR_KEYWORD) { + return ¶m; + } + } + return nullptr; } - auto AddParameter(Identifier name, LogicalType type, Value default_value) -> FunctionSignature & { - parameters.emplace_back(std::move(name), std::move(type), std::move(default_value)); - return *this; + //! The schema the "**kwargs" parameter receives, or nullptr if it accepts any kwargs + auto GetTypedKwargs() const -> optional_ptr { + return typed_kwargs.get(); } - auto AddParameter(Identifier name, LogicalType type) -> FunctionSignature & { - parameters.emplace_back(std::move(name), std::move(type)); + auto AddParameter(Identifier name, LogicalType type, optional default_value = {}, + FunctionParameterKind kind = FunctionParameterKind::STANDARD) -> FunctionSignature & { + parameters.emplace_back(std::move(name), std::move(type), std::move(default_value), kind); return *this; } - + //! Adds a positional-only parameter named "col", after its position - a caller cannot pass it by a name that + //! was never declared auto AddParameter(LogicalType type) -> FunctionSignature & { - auto name = StringUtil::Format("col%d", parameters.size()); - parameters.emplace_back(Identifier(name), std::move(type)); - return *this; + auto name = Identifier(StringUtil::Format("col%d", parameters.size())); + return AddPositionalOnly(std::move(name), std::move(type)); + } + auto AddKeywordOnly(Identifier name, LogicalType type, optional default_value = {}) -> FunctionSignature & { + return AddParameter(std::move(name), std::move(type), std::move(default_value), + FunctionParameterKind::KEYWORD_ONLY); + } + auto AddPositionalOnly(Identifier name, LogicalType type) -> FunctionSignature & { + return AddParameter(std::move(name), std::move(type), {}, FunctionParameterKind::POSITIONAL_ONLY); + } + auto AddArgs(Identifier name, LogicalType type) -> FunctionSignature & { + return AddParameter(std::move(name), std::move(type), {}, FunctionParameterKind::VAR_POSITIONAL); } + auto AddKwargs(Identifier name, LogicalType type) -> FunctionSignature & { + return AddParameter(std::move(name), std::move(type), {}, FunctionParameterKind::VAR_KEYWORD); + } + + //! Adds a "**name" parameter that receives only the options of the given schema + DUCKDB_API auto AddTypedKwargs(Identifier name, TypedKwargs schema) -> FunctionSignature &; + //! The same, with the schema declared by the given callback + DUCKDB_API auto WithTypedKwargs(Identifier name, const std::function &configure) + -> FunctionSignature &; + //! Adds options to the schema of the "**kwargs" parameter + //! @throws InternalException if the signature has no typed "**kwargs" parameter + DUCKDB_API auto ExtendTypedKwargs(const std::function &configure) -> FunctionSignature &; + //! Returns the index of the parameter a caller can pass by the given name. Skips the variadic parameters and the + //! positional-only ones, whose names a caller cannot use auto GetParameterIndexByName(const Identifier &name) const -> optional_idx { // Parameter names are matched case-insensitively, consistent with SQL identifier semantics. for (idx_t i = 0; i < parameters.size(); i++) { - if (parameters[i].GetName() == name) { + if (parameters[i].AcceptsName() && parameters[i].GetName() == name) { return i; } } return optional_idx(); } - auto GetRequiredParameterCount() const -> idx_t { + //! The number of leading parameters that can be passed by position + auto GetPositionalParameterCount() const -> idx_t { idx_t result = 0; - for (const auto ¶m : parameters) { - if (!param.HasDefaultValue()) { - result++; - } + while (result < parameters.size() && parameters[result].AcceptsPosition()) { + result++; } return result; } - void Verify() const { - // Check for duplicate parameter names - identifier_set_t seen_names; - for (const auto ¶m : parameters) { - if (seen_names.find(param.GetName()) != seen_names.end()) { - throw InvalidInputException("Duplicate parameter name: %s", param.GetName()); - } - seen_names.insert(param.GetName()); + //! The number of leading parameters that can ONLY be passed by position + auto GetPositionalOnlyParameterCount() const -> idx_t { + idx_t result = 0; + while (result < parameters.size() && parameters[result].GetKind() == FunctionParameterKind::POSITIONAL_ONLY) { + result++; } + return result; + } - // Also check for default values that are not at the end of the parameter list - bool found_default_value = false; + auto GetRequiredParameterCount() const -> idx_t { + idx_t result = 0; for (const auto ¶m : parameters) { - if (param.HasDefaultValue()) { - found_default_value = true; - } else if (found_default_value) { - throw InvalidInputException( - "Parameters with default values must be at the end of the parameter list. Parameter '%s' does not " - "have a default value but follows a parameter with a default value.", - param.GetName()); + if (!param.IsVariadic() && !param.HasDefaultValue()) { + result++; } } + return result; } + //! Puts the named arguments in binding order: every keyword-only parameter in declaration order, with its default + //! if the call left it out, then the arguments "**kwargs" receives in the order they were passed + //! A default is cast to its parameter's type, as FunctionBinder::CastToParameterType casts an argument + DUCKDB_API void FillNamedDefaults(ClientContext &context, named_argument_map_t &named_parameters) const; + + DUCKDB_API void Verify() const; + hash_t Hash() const; private: vector parameters; - LogicalType varargs; + shared_ptr typed_kwargs; // optional "schema" for the accepted **kwargs LogicalType return_type; }; @@ -341,20 +440,34 @@ class Function { name = std::move(name_p); } auto SetSchemaName(Identifier schema_name_p) -> void { - schema_name = std::move(schema_name_p); + qualified_name = QualifiedName(GetCatalogName(), std::move(schema_name_p), name); } auto SetCatalogName(Identifier catalog_name_p) -> void { - catalog_name = std::move(catalog_name_p); + auto path = qualified_name.Path(); + if (path.size() < 3) { + qualified_name = QualifiedName(std::move(catalog_name_p), GetSchemaName(), name); + } else { + path.pop_back(); + path[0] = std::move(catalog_name_p); + qualified_name = QualifiedName(std::move(path), name); + } + } + void SetQualifiedName(QualifiedName name_p) { + name = name_p.Name(); + qualified_name = std::move(name_p); + } + QualifiedName GetQualifiedName() const { + return qualified_name.WithName(name); } const Identifier &GetName() const { return name; } const Identifier &GetSchemaName() const { - return schema_name; + return qualified_name.Schema(); } const Identifier &GetCatalogName() const { - return catalog_name; + return qualified_name.Catalog(); } //! Returns the formatted string name(arg1, arg2, ...) @@ -366,16 +479,9 @@ class Function { DUCKDB_API static string CallToString(const Identifier &catalog_name, const Identifier &schema_name, const Identifier &name, const vector &arguments, const LogicalType &varargs, const LogicalType &return_type); - //! Returns the formatted string name(arg1, arg2.., np1=a, np2=b, ...) - DUCKDB_API static string CallToString(const Identifier &catalog_name, const Identifier &schema_name, - const Identifier &name, const vector &arguments, - const named_parameter_type_map_t &named_parameters); private: - //! Optional catalog name of the function - Identifier catalog_name; - //! Optional schema name of the function - Identifier schema_name; + QualifiedName qualified_name; }; class SimpleFunction : public Function { @@ -399,18 +505,6 @@ class SimpleFunction : public Function { return signature; } - const LogicalType &GetVarArgs() const { - return signature.GetVarArgs(); - } - - void SetVarArgs(LogicalType varargs_p) { - signature.SetVarArgs(std::move(varargs_p)); - } - - DUCKDB_API bool HasVarArgs() const { - return signature.HasVarArgs(); - } - void SetReturnType(LogicalType return_type_p) { signature.SetReturnType(std::move(return_type_p)); } @@ -419,47 +513,6 @@ class SimpleFunction : public Function { } }; -class SimpleNamedParameterFunction : public Function { -public: - DUCKDB_API SimpleNamedParameterFunction(Identifier name, vector arguments, - LogicalType varargs = LogicalType(LogicalTypeId::INVALID)); - DUCKDB_API ~SimpleNamedParameterFunction() override; - - //! The set of arguments of the function - vector arguments; - //! The type of varargs to support, or LogicalTypeId::INVALID if the function does not accept variable length - //! arguments - LogicalType varargs; - - //! The named parameters of the function - named_parameter_type_map_t named_parameters; - -public: - DUCKDB_API virtual string ToString() const; - DUCKDB_API bool HasNamedParameters() const; - - vector &GetArguments() { - return arguments; - } - const vector &GetArguments() const { - return arguments; - } - - const LogicalType &GetVarArgs() const { - return varargs; - } - LogicalType &GetVarArgs() { - return varargs; - } - // TODO: Dont expose mutable accessor - void SetVarArgs(LogicalType varargs_p) { - varargs = std::move(varargs_p); - } - bool HasVarArgs() const { - return varargs.id() != LogicalTypeId::INVALID; - } -}; - class FunctionProperties { public: auto GetStability() const -> FunctionStability { @@ -541,29 +594,38 @@ class FunctionProperties { class BoundSimpleFunction { protected: - Identifier name; - Identifier schema_name; - Identifier catalog_name; + QualifiedName qualified_name; string extra_info; //! The set of arguments of the function vector arguments; + //! The number of leading arguments that are matched to the parameters by position + idx_t positional_arguments = 0; + //! The names of the remaining arguments, which are matched to the parameters by name + vector named_arguments; //! Return type of the function LogicalType return_type; public: void SetName(Identifier name_p) { - name = std::move(name_p); + qualified_name = qualified_name.WithName(std::move(name_p)); } const Identifier &GetName() const { - return name; + return qualified_name.Name(); } const Identifier &GetSchemaName() const { - return schema_name; + return qualified_name.Schema(); } const Identifier &GetCatalogName() const { - return catalog_name; + return qualified_name.Catalog(); + } + + const QualifiedName &GetQualifiedName() const { + return qualified_name; + } + void SetQualifiedName(QualifiedName name_p) { + qualified_name = std::move(name_p); } const string &GetExtraInfo() const { @@ -580,6 +642,33 @@ class BoundSimpleFunction { return arguments; } + auto GetPositionalArgumentCount() const -> idx_t { + return positional_arguments; + } + auto GetNamedArguments() const -> const vector & { + return named_arguments; + } + auto SetNamedArguments(idx_t positional_arguments_p, vector named_arguments_p) -> void { + positional_arguments = positional_arguments_p; + named_arguments = std::move(named_arguments_p); + } + + //! The arguments are laid out as [standard | *args | keyword-only | **kwargs], these need the signature of the + //! function to tell them apart + DUCKDB_API auto GetStandardArgumentCount(const FunctionSignature &signature) const -> idx_t; + DUCKDB_API auto GetVarArgsCount(const FunctionSignature &signature) const -> idx_t; + DUCKDB_API auto GetKeywordOnlyArgumentCount(const FunctionSignature &signature) const -> idx_t; + DUCKDB_API auto GetKwargsCount(const FunctionSignature &signature) const -> idx_t; + + //! Records the arguments of a call with constant arguments, as the function binder placed them for `signature` + DUCKDB_API void SetCallArguments(const FunctionSignature &signature, const vector ¶meters, + const named_argument_map_t &named_parameters); + +protected: + DUCKDB_API auto GetArgumentParameterKind(const FunctionSignature &signature, idx_t argument_index) const + -> FunctionParameterKind; + +public: auto GetReturnType() const -> const LogicalType & { return return_type; } @@ -627,10 +716,11 @@ class BindFunctionInput { } //! Try to get the constant value of an argument. - //! Never throws: returns none if the argument... + //! Returns none if the argument... //! - is not constant (unresolved parameter or a non-foldable expression) //! - index is out of range //! - was not found when looking up by name + //! - raises an evaluation error (internal errors still propagate) //! Use this when a non-constant argument should fall back to the runtime value instead of being an error. DUCKDB_API optional TryGetConstant(idx_t arg_idx) const; DUCKDB_API optional TryGetConstant(const Identifier &name) const; @@ -641,7 +731,6 @@ class BindFunctionInput { private: ClientContext &context; - //! Only used to name the function in error messages const BoundSimpleFunction &function; vector> &arguments; optional_ptr> argument_names; diff --git a/src/duckdb/src/include/duckdb/function/function_binder.hpp b/src/duckdb/src/include/duckdb/function/function_binder.hpp index 2e6fdc684..9e14e7806 100644 --- a/src/duckdb/src/include/duckdb/function/function_binder.hpp +++ b/src/duckdb/src/include/duckdb/function/function_binder.hpp @@ -28,6 +28,14 @@ struct SortedAggregateStateOrder { OrderByNullType null_order; }; +//! A scalar function with its argument and return types resolved, before the bind callback is invoked +struct ResolvedScalarFunctionTypes { + BoundScalarFunction bound_function; + vector argument_names; + //! The collation pushed into the arguments, to be propagated to the return type after binding + string pushed_collation; +}; + //! Overload selection: picks the cheapest matching overload out of a function set, given the argument types of a //! call. Free-standing so that callers without a ClientContext - which only limits the set of implicit casts //! considered, can select overloads too. @@ -36,9 +44,6 @@ struct FunctionOverloads { DUCKDB_API static optional_idx Cost(optional_ptr context, const SimpleFunction &func, const vector &arguments, const vector> &named_arguments); - DUCKDB_API static optional_idx Cost(optional_ptr context, const SimpleNamedParameterFunction &func, - const vector &arguments, - const vector> &); //! All overloads that match at the lowest cost. Empty (and error set) if none match. template @@ -119,15 +124,41 @@ class FunctionBinder { const vector>> &keyword_args, ErrorData &error); + //! Bind a table function: select the overload, check the named arguments against its signature, and cast both + //! the positional and the named arguments to the types it declares + DUCKDB_API optional_idx BindFunction(const Identifier &name, const TableFunctionSet &functions, + vector> &positional_arguments, + vector>> &named_arguments, + vector ¶meters, named_argument_map_t &named_parameters, + ErrorData &error); + + //! Casts a constant to the type of the parameter it fills, as an argument of any function is cast - a parameter + //! whose type is not concrete, such as ANY, takes it as it is + DUCKDB_API static Value CastToParameterType(ClientContext &context, Value value, const LogicalType ¶meter_type); + + //! Bind a table in-out function. Its arguments are the columns of an input table, so there is nothing to place + //! or fold - only the overload is chosen + DUCKDB_API optional_idx BindTableInOutFunction(const Identifier &name, const TableFunctionSet &functions, + const vector &input_types, ErrorData &error); + //! Bind a pragma function from the set of functions and input arguments DUCKDB_API optional_idx BindFunction(const Identifier &name, const PragmaFunctionSet &functions, - vector ¶meters, ErrorData &error); + vector> &positional_arguments, + vector>> &named_arguments, + vector ¶meters, named_argument_map_t &named_parameters, + ErrorData &error); DUCKDB_API unique_ptr BindScalarFunction(const Identifier &schema, const Identifier &name, vector> children, ErrorData &error, bool is_operator = false, optional_ptr binder = nullptr); + //! Throws the binding error instead of returning nullptr + DUCKDB_API unique_ptr BindScalarFunction(const Identifier &schema, const Identifier &name, + vector> children, + bool is_operator = false, + optional_ptr binder = nullptr); + DUCKDB_API unique_ptr BindScalarFunction(const ScalarFunctionCatalogEntry &function, vector> children, ErrorData &error, bool is_operator = false, @@ -237,7 +268,8 @@ class FunctionBinder { vector>> arguments, ErrorData &error, vector &order_types, vector &arg_order_types); - DUCKDB_API unique_ptr BindScalarWindowFunction(BoundWindowExpression &wexpr); + DUCKDB_API unique_ptr BindScalarWindowFunction(BoundWindowExpression &wexpr, + const vector &input_types); //! Turn a function into a BoundScalarFunction bound to the given arguments. The function is kept as the //! definition of the resulting bound function - see BoundScalarFunction::GetDefinition(). @@ -311,6 +343,11 @@ class FunctionBinder { } private: + //! Resolves the argument / return types of the function and casts the children to them - everything that happens + //! before the bind callback is invoked + ResolvedScalarFunctionTypes + ResolveScalarFunctionTypes(shared_ptr function, vector> &children, + vector>> &keyword_args); //! Cast a set of expressions to the arguments of this function void CastToFunctionArguments(BoundSimpleFunction &function, vector> &children); diff --git a/src/duckdb/src/include/duckdb/function/function_serialization.hpp b/src/duckdb/src/include/duckdb/function/function_serialization.hpp index e6ec89f17..99a7504f1 100644 --- a/src/duckdb/src/include/duckdb/function/function_serialization.hpp +++ b/src/duckdb/src/include/duckdb/function/function_serialization.hpp @@ -20,6 +20,21 @@ class ClientContext; class FunctionSerializer { private: + static QualifiedName DeserializeQualifiedName(Deserializer &deserializer, Identifier name) { + auto catalog = deserializer.ReadPropertyWithDefault(505, "catalog_name"); + auto schema = deserializer.ReadPropertyWithDefault(506, "schema_name"); + auto qname = deserializer.ReadPropertyWithExplicitDefault(507, "qname", QualifiedName()); + if (!qname.Name().empty()) { + if (qname.Schema().empty()) { + return QualifiedName(qname.Catalog().empty() ? Identifier::SystemCatalog() : qname.Catalog(), + Identifier::DefaultSchema(), qname.Name()); + } + return qname.Catalog().empty() ? qname.WithCatalog(Identifier::SystemCatalog()) : qname; + } + return QualifiedName(catalog.empty() ? Identifier::SystemCatalog() : catalog, + schema.empty() ? Identifier::DefaultSchema() : schema, std::move(name)); + } + class DeserializeContext { public: DeserializeContext(Deserializer &deserializer_p, const LogicalType &return_type, @@ -33,7 +48,8 @@ class FunctionSerializer { throw; } } - ~DeserializeContext() { + ~DeserializeContext() { // NOLINT(bugprone-exception-escape): Unset only throws if the stack invariant is + // broken. deserializer.Unset(); deserializer.Unset(); } @@ -57,29 +73,32 @@ class FunctionSerializer { function.SetLogicalReturnType(return_type.IsAggregateState() ? function.GetReturnType() : return_type); } - static void RestoreLogicalSignature(BoundWindowFunction &, const vector> &, - const LogicalType &) { - } - public: template static void Serialize(Serializer &serializer, const FUNC &function, optional_ptr bind_info) { D_ASSERT(!function.GetName().empty()); - serializer.WriteProperty(500, "name", function.GetName()); - serializer.WriteProperty(501, "arguments", function.GetArguments()); + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WriteProperty(500, "name", function.GetName()); + } + serializer.WriteProperty(501, "arguments", GetSerializedArguments(serializer, function)); if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { // binds no longer erase the arguments they fold into their bind data, so the argument list above is // always the full one - older versions read this field unconditionally, so write it (empty) for them serializer.WriteProperty(502, "original_arguments", vector()); } - // These are optional fields that are written out of numeric order, older - // databases won't contain the fields, so the defaults will be used, but if - // the fields are present, they will be used. - serializer.WritePropertyWithDefault(505, "catalog_name", function.GetCatalogName(), Identifier()); - serializer.WritePropertyWithDefault(506, "schema_name", function.GetSchemaName(), Identifier()); + + if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WriteProperty(507, "qname", function.GetQualifiedName()); + } else { + serializer.WritePropertyWithDefault(505, "catalog_name", function.GetCatalogName(), Identifier()); + serializer.WritePropertyWithDefault(506, "schema_name", function.GetSchemaName(), Identifier()); + } bool has_serialize = function.HasSerializationCallbacks(); serializer.WriteProperty(503, "has_serialize", has_serialize); + if constexpr (std::is_base_of_v) { + SerializeNamedArguments(serializer, function); + } if (has_serialize) { serializer.WriteObject(504, "function_data", [&](Serializer &obj) { function.GetSerializeCallback()(obj, bind_info, function); }); @@ -87,6 +106,59 @@ class FunctionSerializer { } } + //! Older versions select a table function by its positional arguments alone + template + static vector GetSerializedArguments(Serializer &serializer, const FUNC &function) { + if constexpr (std::is_same_v) { + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + VerifyLegacyTableFunctionArguments(function); + auto &arguments = function.GetArguments(); + auto positional_end = arguments.begin() + NumericCast(function.GetPositionalArgumentCount()); + return vector(arguments.begin(), positional_end); + } + } + return function.GetArguments(); + } + + //! A required keyword-only option selects its overload, which an older version cannot be told about + static void VerifyLegacyTableFunctionArguments(const BoundTableFunction &function) { + auto &signature = function.GetSignature(); + for (auto &name : function.GetNamedArguments()) { + auto param_idx = signature.GetParameterIndexByName(name); + if (param_idx.IsValid() && !signature.GetParameter(param_idx.GetIndex()).HasDefaultValue()) { + throw SerializationException("Table function %s has a required keyword-only parameter %s, which " + "cannot be serialized to a storage version older than v2.0.0", + function.GetName(), name); + } + } + } + + //! Only written if the function was called with keyword-only or "**kwargs" arguments + template + static void SerializeNamedArguments(Serializer &serializer, const FUNC &function) { + auto &named_arguments = function.GetNamedArguments(); + if (named_arguments.empty()) { + return; + } + if constexpr (std::is_same_v) { + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + // older versions read a table function's options from the operator's named parameters instead + return; + } + } + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + if (named_arguments.size() > function.GetKwargsCount()) { + throw SerializationException("Function %s has keyword-only parameters, which cannot be serialized " + "to a storage version older than v2.0.0", + function.GetName()); + } + // the "**kwargs" arguments are matched to "*args" instead, or named after their alias + return; + } + serializer.WriteProperty(508, "positional_arguments", function.GetPositionalArgumentCount()); + serializer.WriteProperty(509, "named_arguments", named_arguments); + } + //! Plans written by versions whose binds erased the arguments they folded into their bind data record the //! pre-erase list separately - use that as the argument list, so that the function looks the same either way static void RestoreErasedArguments(vector &arguments, vector &original_arguments) { @@ -95,63 +167,95 @@ class FunctionSerializer { } } - template - static FUNC DeserializeFunction(ClientContext &context, CatalogType catalog_type, const Identifier &catalog_name, - const Identifier &schema_name, const Identifier &name, - const vector &arguments) { - EntryLookupInfo lookup_info(catalog_type, QualifiedName(name)); - auto &func_catalog = - Catalog::GetEntry(context, catalog_type, - QualifiedName(catalog_name.empty() ? Identifier::SystemCatalog() : catalog_name, - schema_name.empty() ? Identifier::DefaultSchema() : schema_name, name)); - - if (func_catalog.type != catalog_type) { - throw InternalException("DeserializeFunction - cant find catalog entry for function %s", - name.GetIdentifierName()); + //! Splits the argument types a plan recorded into those passed by position and those passed by name + static void SplitArgumentTypes(const QualifiedName &qualified_name, const vector &arguments, + idx_t positional_count, const vector &named_arguments, + vector &positional_types, + vector> &keyword_types) { + if (positional_count + named_arguments.size() != arguments.size()) { + throw SerializationException("Function %s has %llu argument types, but %llu positional and %llu named " + "arguments", + qualified_name.ToString(), arguments.size(), positional_count, + named_arguments.size()); + } + for (idx_t arg_idx = 0; arg_idx < arguments.size(); arg_idx++) { + if (arg_idx < positional_count) { + positional_types.push_back(arguments[arg_idx]); + } else { + keyword_types.emplace_back(named_arguments[arg_idx - positional_count], arguments[arg_idx]); + } } - auto &functions = func_catalog.Cast(); - return *functions.functions.GetFunctionByArguments(context, arguments); } - template - static pair DeserializeBase(Deserializer &deserializer, CatalogType catalog_type, - optional_ptr>> children = nullptr) { + //! Selects the overload of a plan written before v2.0.0, which records its named arguments only as the operator's + //! named parameters. Required keyword-only parameters are treated as if they had a default - the bind receives + //! those named parameters and rejects a missing one, as it did when the plan was written + static optional_idx SelectWithoutNamedArguments(FunctionBinder &binder, const Identifier &name, + const TableFunctionSet &functions, + const vector &positional_types) { + auto relaxed = functions; + relaxed.ApplyToFunctions([](TableFunction &function) { + auto &signature = function.GetSignature(); + for (idx_t i = 0; i < signature.GetParameterCount(); i++) { + auto ¶m = signature.GetParameter(i); + if (param.GetKind() == FunctionParameterKind::KEYWORD_ONLY && !param.HasDefaultValue()) { + // selection only checks that there is a default - an untyped NULL suits a parameter of any type + param.SetDefaultValue(Value()); + } + } + }); + ErrorData error; + return binder.BindFunction(name, relaxed, positional_types, {}, error); + } + + //! Deserializes a table function, and whether the plan holds its bind data. Without it, the caller binds again + static pair DeserializeTableFunction(Deserializer &deserializer) { auto &context = deserializer.Get(); - auto name = deserializer.ReadProperty(500, "name"); + auto name = deserializer.ReadPropertyWithDefault(500, "name"); auto arguments = deserializer.ReadProperty>(501, "arguments"); auto original_arguments = deserializer.ReadPropertyWithDefault>(502, "original_arguments"); - auto catalog_name = deserializer.ReadPropertyWithDefault(505, "catalog_name"); - auto schema_name = deserializer.ReadPropertyWithDefault(506, "schema_name"); - if (catalog_name.empty()) { - catalog_name = Identifier::SystemCatalog(); - } - if (schema_name.empty()) { - schema_name = Identifier::DefaultSchema(); - } - RestoreErasedArguments(arguments, original_arguments); + auto qualified_name = DeserializeQualifiedName(deserializer, std::move(name)); + auto has_serialize = deserializer.ReadProperty(503, "has_serialize"); + auto positional_count = deserializer.ReadPropertyWithDefault(508, "positional_arguments"); + auto named_arguments = deserializer.ReadPropertyWithDefault>(509, "named_arguments"); - if (arguments.empty() && children && !children->empty()) { - // The function is specified as having no arguments, but somehow expressions were passed anyway - // Assume this is a "varargs" function and use the types of the expressions as the arguments - // This can happen when we change a function that used to take varargs, to no longer do so. - arguments.reserve(children->size()); - for (auto &child : *children) { - arguments.push_back(child->GetReturnType()); - } + RestoreErasedArguments(arguments, original_arguments); + if (named_arguments.empty()) { + positional_count = arguments.size(); } - auto function = - DeserializeFunction(context, catalog_type, catalog_name, schema_name, name, arguments); - auto has_serialize = deserializer.ReadProperty(503, "has_serialize"); - if (has_serialize) { - function.GetArguments() = std::move(arguments); + auto &func_catalog = Catalog::GetEntry(context, CatalogType::TABLE_FUNCTION_ENTRY, qualified_name); + if (func_catalog.type != CatalogType::TABLE_FUNCTION_ENTRY) { + throw InternalException("DeserializeTableFunction - cant find catalog entry for function %s", + qualified_name.Name().GetIdentifierName()); + } + auto &functions = func_catalog.Cast().functions; + + // the named arguments take part in selecting the overload, exactly as they did when the call was bound + vector positional_types; + vector> keyword_types; + SplitArgumentTypes(qualified_name, arguments, positional_count, named_arguments, positional_types, + keyword_types); + FunctionBinder binder(context); + ErrorData error; + auto func_idx = binder.BindFunction(qualified_name.Name(), functions, positional_types, keyword_types, error); + if (!func_idx.IsValid() && named_arguments.empty()) { + func_idx = SelectWithoutNamedArguments(binder, qualified_name.Name(), functions, positional_types); } + if (!func_idx.IsValid()) { + throw SerializationException("Failed to find function %s(%s)\n%s", qualified_name.ToString(), + StringUtil::ToString(arguments, ","), error.RawMessage()); + } + + BoundTableFunction function(functions.GetFunctionByOffset(func_idx.GetIndex())); + function.GetArguments() = std::move(arguments); + function.SetNamedArguments(positional_count, std::move(named_arguments)); return make_pair(std::move(function), has_serialize); } template static unique_ptr FunctionDeserialize(Deserializer &deserializer, FUNC &function) { - if (!function.HasSerializationCallbacks()) { + if (!function.GetDeserializeCallback()) { throw SerializationException("Function requires deserialization but no deserialization function for %s", function.GetName()); } @@ -171,23 +275,23 @@ class FunctionSerializer { case LogicalTypeId::UNION: case LogicalTypeId::VARIANT: case LogicalTypeId::MAP: - if (!type.AuxInfo()) { + if (!type.HasParameters()) { return true; } return false; case LogicalTypeId::LIST: - if (!type.AuxInfo()) { + if (!type.HasParameters()) { return true; } return TypeRequiresAssignment(ListType::GetChildType(type)); case LogicalTypeId::ARRAY: - if (!type.AuxInfo()) { + if (!type.HasParameters()) { return true; } return TypeRequiresAssignment(ArrayType::GetChildType(type)); case LogicalTypeId::STRUCT: case LogicalTypeId::TUPLE: - if (!type.AuxInfo()) { + if (!type.HasParameters()) { return true; } if (StructType::GetChildCount(type) == 0) { @@ -199,25 +303,48 @@ class FunctionSerializer { } } + //! Plans written before the argument names were serialized hold the names of the named arguments in the aliases + //! of the trailing arguments. Only the arguments behind the standard parameters can have been named. + template + static void RestoreNamesFromAliases(const FUNCTION_SET &functions, const vector> &children, + idx_t &positional_count, vector &named_arguments) { + bool takes_named = false; + idx_t standard_count = 0; + for (auto &function : functions.functions) { + auto &signature = function->GetSignature(); + if (!signature.GetKwargs()) { + continue; + } + takes_named = true; + standard_count = MaxValue(standard_count, signature.GetPositionalParameterCount()); + } + if (!takes_named || children.size() != positional_count) { + return; + } + idx_t named_offset = children.size(); + while (named_offset > standard_count && !children[named_offset - 1]->GetAlias().empty()) { + named_offset--; + } + for (idx_t i = named_offset; i < children.size(); i++) { + named_arguments.push_back(children[i]->GetAlias()); + } + positional_count = named_offset; + } + template static pair> Deserialize(Deserializer &deserializer, CatalogType catalog_type, vector> &children, LogicalType return_type) { // NOLINT: clang-tidy bug auto &context = deserializer.Get(); - auto name = deserializer.ReadProperty(500, "name"); + auto name = deserializer.ReadPropertyWithDefault(500, "name"); auto arguments = deserializer.ReadProperty>(501, "arguments"); auto original_arguments = deserializer.ReadPropertyWithDefault>(502, "original_arguments"); - auto catalog_name = deserializer.ReadPropertyWithDefault(505, "catalog_name"); - auto schema_name = deserializer.ReadPropertyWithDefault(506, "schema_name"); + auto qualified_name = DeserializeQualifiedName(deserializer, std::move(name)); auto has_serialize = deserializer.ReadProperty(503, "has_serialize"); + auto positional_count = deserializer.ReadPropertyWithDefault(508, "positional_arguments"); + auto named_arguments = deserializer.ReadPropertyWithDefault>(509, "named_arguments"); - if (catalog_name.empty()) { - catalog_name = Identifier::SystemCatalog(); - } - if (schema_name.empty()) { - schema_name = Identifier::DefaultSchema(); - } RestoreErasedArguments(arguments, original_arguments); if (arguments.empty() && !children.empty()) { @@ -231,23 +358,71 @@ class FunctionSerializer { } // Now lookup the function in the catalog. - EntryLookupInfo lookup_info(catalog_type, QualifiedName(name)); - auto &func_catalog = Catalog::GetEntry(context, catalog_type, QualifiedName(catalog_name, schema_name, name)); + auto &func_catalog = Catalog::GetEntry(context, catalog_type, qualified_name); if (func_catalog.type != catalog_type) { throw InternalException("DeserializeFunction - cant find catalog entry for function %s", - name.GetIdentifierName()); + qualified_name.Name().GetIdentifierName()); + } + + auto &functions = func_catalog.Cast().functions; + + // If there are no argument names serialized, treat all arguments as positional + if (named_arguments.empty()) { + positional_count = arguments.size(); + RestoreNamesFromAliases(functions, children, positional_count, named_arguments); } - auto &functions = func_catalog.Cast(); - const auto function = functions.functions.GetFunctionByArguments(context, arguments); + + // Sanity check: The named arguments are the last arguments, so the number of arguments has to add up + const auto has_valid_count = positional_count + named_arguments.size() == arguments.size(); + if (!has_valid_count || (!named_arguments.empty() && children.size() != arguments.size())) { + throw SerializationException( + "Function %s has %llu argument types and %llu arguments, but %llu positional and %llu named arguments", + qualified_name.ToString(), arguments.size(), children.size(), positional_count, named_arguments.size()); + } + + // Split types by positional and keyword arguments + vector positional_types; + vector> keyword_types; + + for (idx_t arg_idx = 0; arg_idx < arguments.size(); arg_idx++) { + if (arg_idx < positional_count) { + positional_types.push_back(arguments[arg_idx]); + } else { + const auto kw_idx = arg_idx - positional_count; + keyword_types.emplace_back(named_arguments[kw_idx], arguments[arg_idx]); + } + } + + // Lookup the function (and the correct overload) + FunctionBinder binder(context); + ErrorData error; + const auto func_idx = + binder.BindFunction(qualified_name.Name(), functions, positional_types, keyword_types, error); + + // Ensure the function overload was found + if (!func_idx.IsValid()) { + throw SerializationException("Failed to find function %s(%s)\n%s", qualified_name.ToString(), + StringUtil::ToString(arguments, ","), error.RawMessage()); + } + + const auto function = functions.GetFunctionByOffset(func_idx.GetIndex()); // Does this function support serializing its bound data? if (!has_serialize) { // No, then just rebind the function try { - FunctionBinder binder(context); + // Split children into positional and keyword args expression + vector>> keyword_args; + if (!named_arguments.empty()) { + for (idx_t i = positional_count; i < children.size(); i++) { + keyword_args.emplace_back(named_arguments[i - positional_count], std::move(children[i])); + } + children.resize(positional_count); + } - auto [bound_function, bound_data] = binder.ResolveFunction(function, children); + // Resolve function + auto [bound_function, bound_data] = binder.ResolveFunction(function, children, keyword_args); if (TypeRequiresAssignment(bound_function.GetReturnType())) { bound_function.SetReturnType(std::move(return_type)); @@ -255,15 +430,17 @@ class FunctionSerializer { return make_pair(std::move(bound_function), std::move(bound_data)); } catch (std::exception &ex) { - ErrorData error(ex); + ErrorData inner_error(ex); throw SerializationException("Error during bind of function in deserialization: %s", - error.RawMessage()); + inner_error.RawMessage()); } } // Otherwise, construct the bound function from its parts FUNC bound_function(function); bound_function.GetArguments() = std::move(arguments); + + bound_function.SetNamedArguments(positional_count, std::move(named_arguments)); RestoreLogicalSignature(bound_function, children, return_type); // Invoke deserialization function diff --git a/src/duckdb/src/include/duckdb/function/function_set_base.hpp b/src/duckdb/src/include/duckdb/function/function_set_base.hpp index 50cc73f71..5778a2267 100644 --- a/src/duckdb/src/include/duckdb/function/function_set_base.hpp +++ b/src/duckdb/src/include/duckdb/function/function_set_base.hpp @@ -30,8 +30,9 @@ class FunctionSet { vector> functions; public: - void AddFunction(T function) { + auto AddFunction(T function) & -> FunctionSet & { functions.push_back(make_shared_ptr(std::move(function))); + return *this; } void AddFunction(shared_ptr function) { functions.push_back(std::move(function)); @@ -56,12 +57,13 @@ class FunctionSet { } } - bool MergeFunctionSet(FunctionSet new_functions, bool override = false) { + bool MergeFunctionSet(const FunctionSet &new_functions, bool override = false) { D_ASSERT(!new_functions.functions.empty()); for (auto &new_func : new_functions.functions) { bool overwritten = false; for (auto &func : functions) { - if (new_func->Equal(*func)) { + // the same rule for every kind of function - see FunctionSignature::IsSameOverload + if (new_func->GetSignature().IsSameOverload(func->GetSignature())) { // function overload already exists if (override) { // override it diff --git a/src/duckdb/src/include/duckdb/function/pragma_function.hpp b/src/duckdb/src/include/duckdb/function/pragma_function.hpp index f06783b57..d6b245404 100644 --- a/src/duckdb/src/include/duckdb/function/pragma_function.hpp +++ b/src/duckdb/src/include/duckdb/function/pragma_function.hpp @@ -20,6 +20,21 @@ typedef string (*pragma_query_t)(ClientContext &context, const FunctionParameter //! Execute the main pragma function typedef void (*pragma_function_t)(ClientContext &context, const FunctionParameters ¶meters); +struct PragmaFunctionInfo { + DUCKDB_API virtual ~PragmaFunctionInfo(); + + template + TARGET &Cast() { + DynamicCastCheck(this); + return reinterpret_cast(*this); + } + template + const TARGET &Cast() const { + DynamicCastCheck(this); + return reinterpret_cast(*this); + } +}; + //! Pragma functions are invoked by calling PRAGMA x //! Pragma functions come in three types: //! * Call: function call, e.g. PRAGMA table_info('tbl') @@ -28,7 +43,7 @@ typedef void (*pragma_function_t)(ClientContext &context, const FunctionParamete //! -> this is similar to a call pragma but without parameters //! Pragma functions can either return a new query to execute (pragma_query_t) //! or they can -class PragmaFunction : public SimpleNamedParameterFunction { // NOLINT: work-around bug in clang-tidy +class PragmaFunction : public SimpleFunction { // NOLINT: work-around bug in clang-tidy public: // Call DUCKDB_API static PragmaFunction PragmaCall(const Identifier &name, pragma_query_t query, @@ -37,22 +52,42 @@ class PragmaFunction : public SimpleNamedParameterFunction { // NOLINT: work-aro DUCKDB_API static PragmaFunction PragmaCall(const Identifier &name, pragma_function_t function, vector arguments, LogicalType varargs = LogicalType::INVALID); + //! Overloads taking a braced list, so that "{}" does not also match FunctionSignature + static PragmaFunction PragmaCall(const Identifier &name, pragma_query_t query, + std::initializer_list arguments, + LogicalType varargs = LogicalType::INVALID) { + return PragmaCall(name, query, vector(arguments), std::move(varargs)); + } + static PragmaFunction PragmaCall(const Identifier &name, pragma_function_t function, + std::initializer_list arguments, + LogicalType varargs = LogicalType::INVALID) { + return PragmaCall(name, function, vector(arguments), std::move(varargs)); + } + //! The same, declaring the parameters in full - so that they carry their own names and defaults instead of the + //! synthetic "colN" the type-only overloads give them + DUCKDB_API static PragmaFunction PragmaCall(const Identifier &name, pragma_query_t query, + FunctionSignature signature); + DUCKDB_API static PragmaFunction PragmaCall(const Identifier &name, pragma_function_t function, + FunctionSignature signature); // Statement DUCKDB_API static PragmaFunction PragmaStatement(const Identifier &name, pragma_query_t query); DUCKDB_API static PragmaFunction PragmaStatement(const Identifier &name, pragma_function_t function); - DUCKDB_API string ToString() const override; + DUCKDB_API string ToString() const; public: PragmaType type; pragma_query_t query; pragma_function_t function; - named_parameter_type_map_t named_parameters; + //! Additional function info, passed to the callbacks through FunctionParameters::function + shared_ptr function_info; private: PragmaFunction(Identifier name, PragmaType pragma_type, pragma_query_t query, pragma_function_t function, vector arguments, LogicalType varargs); + PragmaFunction(Identifier name, PragmaType pragma_type, pragma_query_t query, pragma_function_t function, + FunctionSignature signature); }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/function/replacement_scan.hpp b/src/duckdb/src/include/duckdb/function/replacement_scan.hpp index 58d8d5fbd..d88521034 100644 --- a/src/duckdb/src/include/duckdb/function/replacement_scan.hpp +++ b/src/duckdb/src/include/duckdb/function/replacement_scan.hpp @@ -90,8 +90,20 @@ struct ReplacementScan { return table_name; } + //! Joins every component of the name, which can be qualified deeper than [catalog, schema] + static string GetFullPath(const QualifiedName &name) { + string table_name; + for (auto &component : name.Path()) { + if (component.empty()) { + continue; + } + table_name += (!table_name.empty() ? "." : "") + component.GetIdentifierName(); + } + return table_name; + } + static string GetFullPath(ReplacementScanInput &input) { - return GetFullPath(input.catalog_name, input.schema_name, input.table_name); + return GetFullPath(input.name); } replacement_scan_t function; diff --git a/src/duckdb/src/include/duckdb/function/scalar/compressed_materialization_functions.hpp b/src/duckdb/src/include/duckdb/function/scalar/compressed_materialization_functions.hpp index 606a7394a..9cfe05c52 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/compressed_materialization_functions.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/compressed_materialization_functions.hpp @@ -17,7 +17,7 @@ namespace duckdb { struct InternalCompressIntegralUtinyintFun { static constexpr const char *Name = "__internal_compress_integral_utinyint"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value,min"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -27,7 +27,7 @@ struct InternalCompressIntegralUtinyintFun { struct InternalCompressIntegralUsmallintFun { static constexpr const char *Name = "__internal_compress_integral_usmallint"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value,min"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -37,7 +37,7 @@ struct InternalCompressIntegralUsmallintFun { struct InternalCompressIntegralUintegerFun { static constexpr const char *Name = "__internal_compress_integral_uinteger"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value,min"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -47,7 +47,7 @@ struct InternalCompressIntegralUintegerFun { struct InternalCompressIntegralUbigintFun { static constexpr const char *Name = "__internal_compress_integral_ubigint"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value,min"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -57,7 +57,7 @@ struct InternalCompressIntegralUbigintFun { struct InternalCompressStringUtinyintFun { static constexpr const char *Name = "__internal_compress_string_utinyint"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -67,7 +67,7 @@ struct InternalCompressStringUtinyintFun { struct InternalCompressStringUsmallintFun { static constexpr const char *Name = "__internal_compress_string_usmallint"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -77,7 +77,7 @@ struct InternalCompressStringUsmallintFun { struct InternalCompressStringUintegerFun { static constexpr const char *Name = "__internal_compress_string_uinteger"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -87,7 +87,7 @@ struct InternalCompressStringUintegerFun { struct InternalCompressStringUbigintFun { static constexpr const char *Name = "__internal_compress_string_ubigint"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -97,7 +97,7 @@ struct InternalCompressStringUbigintFun { struct InternalCompressStringUhugeintFun { static constexpr const char *Name = "__internal_compress_string_uhugeint"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -107,7 +107,7 @@ struct InternalCompressStringUhugeintFun { struct InternalCompressStringHugeintFun { static constexpr const char *Name = "__internal_compress_string_hugeint"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -117,7 +117,7 @@ struct InternalCompressStringHugeintFun { struct InternalCompressGeometryPointFun { static constexpr const char *Name = "__internal_compress_geometry_point"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -127,7 +127,7 @@ struct InternalCompressGeometryPointFun { struct InternalDecompressGeometryPointFun { static constexpr const char *Name = "__internal_decompress_geometry_point"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -137,7 +137,7 @@ struct InternalDecompressGeometryPointFun { struct InternalDecompressIntegralSmallintFun { static constexpr const char *Name = "__internal_decompress_integral_smallint"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value,min"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -147,7 +147,7 @@ struct InternalDecompressIntegralSmallintFun { struct InternalDecompressIntegralIntegerFun { static constexpr const char *Name = "__internal_decompress_integral_integer"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value,min"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -157,7 +157,7 @@ struct InternalDecompressIntegralIntegerFun { struct InternalDecompressIntegralBigintFun { static constexpr const char *Name = "__internal_decompress_integral_bigint"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value,min"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -167,7 +167,7 @@ struct InternalDecompressIntegralBigintFun { struct InternalDecompressIntegralHugeintFun { static constexpr const char *Name = "__internal_decompress_integral_hugeint"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value,min"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -177,7 +177,7 @@ struct InternalDecompressIntegralHugeintFun { struct InternalDecompressIntegralUsmallintFun { static constexpr const char *Name = "__internal_decompress_integral_usmallint"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value,min"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -187,7 +187,7 @@ struct InternalDecompressIntegralUsmallintFun { struct InternalDecompressIntegralUintegerFun { static constexpr const char *Name = "__internal_decompress_integral_uinteger"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value,min"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -197,7 +197,7 @@ struct InternalDecompressIntegralUintegerFun { struct InternalDecompressIntegralUbigintFun { static constexpr const char *Name = "__internal_decompress_integral_ubigint"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value,min"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -207,7 +207,7 @@ struct InternalDecompressIntegralUbigintFun { struct InternalDecompressIntegralUhugeintFun { static constexpr const char *Name = "__internal_decompress_integral_uhugeint"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value,min"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -217,7 +217,7 @@ struct InternalDecompressIntegralUhugeintFun { struct InternalDecompressStringFun { static constexpr const char *Name = "__internal_decompress_string"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "value"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -225,4 +225,14 @@ struct InternalDecompressStringFun { static ScalarFunctionSet GetFunctions(); }; +struct InternalCompressedMaterializationCastFun { + static constexpr const char *Name = "__internal_compressed_materialization_cast"; + static constexpr const char *Parameters = ""; + static constexpr const char *Description = ""; + static constexpr const char *Example = ""; + static constexpr const char *Categories = ""; + + static ScalarFunction GetFunction(); +}; + } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/function/scalar/compressed_materialization_utils.hpp b/src/duckdb/src/include/duckdb/function/scalar/compressed_materialization_utils.hpp index 51bd0dce2..ff8fb362b 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/compressed_materialization_utils.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/compressed_materialization_utils.hpp @@ -13,6 +13,8 @@ namespace duckdb { +enum class CMExpressionType : uint8_t { NONE, CAST, COMPRESS, DECOMPRESS }; + struct CMUtils { //! The types we compress integral types to static const vector IntegralTypes(); @@ -20,6 +22,15 @@ struct CMUtils { static const vector StringTypes(); static unique_ptr Bind(BindScalarFunctionInput &input); + static CMExpressionType GetExpressionType(const BoundFunctionExpression &expression); + //! The input wrapped by a compressed materialization expression, or nullptr for any other expression + static optional_ptr GetWrappedInput(const Expression &expression); + static void MarkCast(BoundFunctionExpression &expression); + +private: + static CMExpressionType GetIntegralType(const BoundScalarFunction &function); + static CMExpressionType GetStringType(const BoundScalarFunction &function); + static CMExpressionType GetGeometryType(const BoundScalarFunction &function); }; //! Needed for (de)serialization without binding diff --git a/src/duckdb/src/include/duckdb/function/scalar/generic_common.hpp b/src/duckdb/src/include/duckdb/function/scalar/generic_common.hpp index 1987825ac..6e77374f9 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/generic_common.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/generic_common.hpp @@ -16,6 +16,7 @@ namespace duckdb { class BoundFunctionExpression; +struct SortedAggregateStateOrder; struct ConstantOrNull { static bool IsConstantOrNull(BoundFunctionExpression &expr, const Value &val); @@ -31,6 +32,9 @@ struct ExportAggregateFunctionBindData : public FunctionData { struct ExportAggregateFunction { static unique_ptr Bind(unique_ptr child_aggregate); static void SetStateExport(BoundAggregateExpression &aggregate, LogicalType state_layout); + static unique_ptr StateToSQL(const LogicalType &type, unique_ptr value); + //! The per-key (column, modifiers) of the AGGREGATE_STATE type of an ordered aggregate + static void GetSortedStateOrders(const LogicalType &state_type, vector &orders); }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/function/scalar/generic_functions.hpp b/src/duckdb/src/include/duckdb/function/scalar/generic_functions.hpp index 01b6b0f79..eb9c2fc3d 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/generic_functions.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/generic_functions.hpp @@ -57,7 +57,7 @@ struct BarrierFun { struct CreateSortKeyFun { static constexpr const char *Name = "create_sort_key"; - static constexpr const char *Parameters = "parameters..."; + static constexpr const char *Parameters = "key1,..."; static constexpr const char *Description = "Constructs a binary-comparable sort key based on a set of input parameters and sort qualifiers"; static constexpr const char *Example = "create_sort_key('A', 'DESC')"; static constexpr const char *Categories = ""; @@ -67,7 +67,7 @@ struct CreateSortKeyFun { struct InvokeFun { static constexpr const char *Name = "invoke"; - static constexpr const char *Parameters = "lambda,arg1,arg2,..."; + static constexpr const char *Parameters = "lambda,arg1,..."; static constexpr const char *Description = "Invokes a lambda function with the given arguments"; static constexpr const char *Example = "invoke(x -> x + 1, 5)"; static constexpr const char *Categories = ""; diff --git a/src/duckdb/src/include/duckdb/function/scalar/list_functions.hpp b/src/duckdb/src/include/duckdb/function/scalar/list_functions.hpp index 3211731c6..466f339f2 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/list_functions.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/list_functions.hpp @@ -153,7 +153,7 @@ struct ListElementFun { struct ListResizeFun { static constexpr const char *Name = "list_resize"; - static constexpr const char *Parameters = "list,size[,value]"; + static constexpr const char *Parameters = "list,size,value"; static constexpr const char *Description = "Resizes the `list` to contain `size` elements. Initializes new elements with `value` or `NULL` if `value` is not set."; static constexpr const char *Example = "list_resize([1, 2, 3], 5, 0)"; static constexpr const char *Categories = "list"; @@ -169,7 +169,7 @@ struct ArrayResizeFun { struct ArrayExtractFun { static constexpr const char *Name = "array_extract"; - static constexpr const char *Parameters = "string::VARCHAR,index::ANY\001struct::STRUCT,entry::VARCHAR\001struct::STRUCT,index::ANY\001list::ANY[],index::ANY"; + static constexpr const char *Parameters = "string::VARCHAR,index::ANY\001struct::STRUCT,entry::VARCHAR\001tuple::STRUCT,index::ANY\001list::ANY[],index::ANY"; static constexpr const char *Description = "Extracts a single character from a `string` using a (1-based) `index`.\001Extracts the named `entry` from the `STRUCT`.\001Extracts the entry from an unnamed `STRUCT` (tuple) using an index (1-based).\001Extracts the `index`th (1-based) value from the `list`."; static constexpr const char *Example = "array_extract('DuckDB', 2)\001array_extract({'i': 3, 'v2': 3, 'v3': 0}, 'i')\001array_extract(row(42, 84), 1)\001array_extract([4, 5, 6], 3)"; static constexpr const char *Categories = "string\001struct\001struct\001list"; diff --git a/src/duckdb/src/include/duckdb/function/scalar/nested_functions.hpp b/src/duckdb/src/include/duckdb/function/scalar/nested_functions.hpp index e332c581d..284132e7a 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/nested_functions.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/nested_functions.hpp @@ -76,6 +76,10 @@ struct VariableReturnBindData : public FunctionData { auto stype = deserializer.ReadProperty(100, "variable_return_type"); return make_uniq(std::move(stype)); } + //! Bind callback that stores the (resolved) return type of the function + static unique_ptr Bind(BindScalarFunctionInput &input) { + return make_uniq(input.GetBoundFunction().GetReturnType()); + } }; template > diff --git a/src/duckdb/src/include/duckdb/function/scalar/operator_functions.hpp b/src/duckdb/src/include/duckdb/function/scalar/operator_functions.hpp index 1e0c6fd7e..a1aa59cc6 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/operator_functions.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/operator_functions.hpp @@ -17,7 +17,7 @@ namespace duckdb { struct OperatorAddFun { static constexpr const char *Name = "+"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "left,right"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -33,7 +33,7 @@ struct AddFun { struct OperatorSubtractFun { static constexpr const char *Name = "-"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "left,right"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -49,7 +49,7 @@ struct SubtractFun { struct OperatorMultiplyFun { static constexpr const char *Name = "*"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "left,right"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -65,7 +65,7 @@ struct MultiplyFun { struct OperatorFloatDivideFun { static constexpr const char *Name = "/"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "left,right"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -75,7 +75,7 @@ struct OperatorFloatDivideFun { struct OperatorIntegerDivideFun { static constexpr const char *Name = "//"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "left,right"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -91,7 +91,7 @@ struct DivideFun { struct OperatorModuloFun { static constexpr const char *Name = "%"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "left,right"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -117,7 +117,7 @@ struct CastFun { struct DecimalDivisionFun { static constexpr const char *Name = "decimal_division"; - static constexpr const char *Parameters = "x,y[,scale]"; + static constexpr const char *Parameters = "x,y,scale"; static constexpr const char *Description = "Divides two DECIMAL values using exact integer arithmetic and round-half-to-even, returning a DECIMAL result with scale determined by SQL Server semantics (result_scale = max(6, s1 + p2 + 1))."; static constexpr const char *Example = "decimal_division(10.00::DECIMAL(10,2), 3.00::DECIMAL(10,2))"; static constexpr const char *Categories = ""; diff --git a/src/duckdb/src/include/duckdb/function/scalar/regexp.hpp b/src/duckdb/src/include/duckdb/function/scalar/regexp.hpp index ac67e14ca..ae8a52307 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/regexp.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/regexp.hpp @@ -27,6 +27,8 @@ void ParseGroupNameList(const string &function_name, const Value &list_val, cons child_list_t &out_struct_children); idx_t AdvanceOneUTF8Basic(const duckdb_re2::StringPiece &input, idx_t base); +//! Throws if a regex result is not valid UTF-8 +void VerifyUTF8Result(const char *data, idx_t size); inline duckdb_re2::StringPiece CreateStringPiece(const string_t &input) { return duckdb_re2::StringPiece(input.GetData(), input.GetSize()); diff --git a/src/duckdb/src/include/duckdb/function/scalar/sequence_functions.hpp b/src/duckdb/src/include/duckdb/function/scalar/sequence_functions.hpp index e264ee11c..05e96dcdf 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/sequence_functions.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/sequence_functions.hpp @@ -37,7 +37,7 @@ struct NextvalFun { struct SetvalFun { static constexpr const char *Name = "setval"; - static constexpr const char *Parameters = "sequence_name,value[,is_called]"; + static constexpr const char *Parameters = "sequence_name,value,is_called"; static constexpr const char *Description = "Set the value of the sequence to the specified value. If is_called is true, the sequence is called. If is_called is false, the sequence is not called."; static constexpr const char *Example = "setval('my_sequence_name', 100, true)"; static constexpr const char *Categories = ""; diff --git a/src/duckdb/src/include/duckdb/function/scalar/strftime_format.hpp b/src/duckdb/src/include/duckdb/function/scalar/strftime_format.hpp index 9bf059d53..d4fcacd70 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/strftime_format.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/strftime_format.hpp @@ -128,6 +128,8 @@ struct StrfTimeFormat : public StrTimeFormat { // NOLINT: work-around bug in cla char *WritePadded2(char *target, uint32_t value) const; char *WritePadded3(char *target, uint32_t value) const; char *WritePadded(char *target, uint32_t value, size_t padding) const; + //! Write a year - padded to 4 digits within [0, 9999], and in full (with its sign) otherwise + char *WriteYear(char *target, int32_t year) const; bool IsDateSpecifier(StrTimeSpecifier specifier); char *WriteDateSpecifier(StrTimeSpecifier specifier, date_t date, char *target) const; char *WriteStandardSpecifier(StrTimeSpecifier specifier, int32_t data[], const char *tz_name, size_t tz_len, diff --git a/src/duckdb/src/include/duckdb/function/scalar/string_common.hpp b/src/duckdb/src/include/duckdb/function/scalar/string_common.hpp index f5baa3911..fa06fd8d6 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/string_common.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/string_common.hpp @@ -9,6 +9,8 @@ namespace duckdb { bool IsAscii(const char *input, idx_t n); //! Returns the index of the first byte with the high bit set, or n if all bytes are ASCII idx_t FirstNonAscii(const char *input, idx_t n); +//! Decodes one codepoint and returns its length - throws an InternalException on invalid UTF-8 +idx_t DecodeCodepoint(const char *input, idx_t size, int32_t &codepoint); idx_t LowerLength(const char *input_data, idx_t input_length); void LowerCase(const char *input_data, idx_t input_length, char *result_data); idx_t FindStrInStr(const string_t &haystack_s, const string_t &needle_s); diff --git a/src/duckdb/src/include/duckdb/function/scalar/string_functions.hpp b/src/duckdb/src/include/duckdb/function/scalar/string_functions.hpp index 39cdba409..5851885cf 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/string_functions.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/string_functions.hpp @@ -327,7 +327,7 @@ struct RegexpSplitToArrayFun { struct RegexpFun { static constexpr const char *Name = "regexp_full_match"; - static constexpr const char *Parameters = "string,regex"; + static constexpr const char *Parameters = "string,regex,options"; static constexpr const char *Description = "Returns `true` if the entire `string` matches the `regex`. A set of optional regex `options` can be set."; static constexpr const char *Example = "regexp_full_match('anabanana', '(an)*')"; static constexpr const char *Categories = "regex"; @@ -395,9 +395,19 @@ struct PathJoinFun { static ScalarFunction GetFunction(); }; +struct HivePartitionComponentFun { + static constexpr const char *Name = "hive_partition_component"; + static constexpr const char *Parameters = "name,value"; + static constexpr const char *Description = "Returns the hive partition directory for a partition column and its value, e.g. name=value. The name and the value are escaped like url_encode does, a NULL value becomes __HIVE_DEFAULT_PARTITION__."; + static constexpr const char *Example = "hive_partition_component('year', 2024)"; + static constexpr const char *Categories = "string"; + + static ScalarFunction GetFunction(); +}; + struct LikeFun { static constexpr const char *Name = "~~"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "string,pattern"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -407,7 +417,7 @@ struct LikeFun { struct NotLikeFun { static constexpr const char *Name = "!~~"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "string,pattern"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -417,7 +427,7 @@ struct NotLikeFun { struct GlobPatternFun { static constexpr const char *Name = "~~~"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "string,pattern"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -427,7 +437,7 @@ struct GlobPatternFun { struct ILikeFun { static constexpr const char *Name = "~~*"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "string,pattern"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -437,7 +447,7 @@ struct ILikeFun { struct NotILikeFun { static constexpr const char *Name = "!~~*"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "string,pattern"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; diff --git a/src/duckdb/src/include/duckdb/function/scalar/struct_functions.hpp b/src/duckdb/src/include/duckdb/function/scalar/struct_functions.hpp index 4e481653d..70c78173d 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/struct_functions.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/struct_functions.hpp @@ -17,7 +17,7 @@ namespace duckdb { struct StructExtractFun { static constexpr const char *Name = "struct_extract"; - static constexpr const char *Parameters = "struct,'entry'"; + static constexpr const char *Parameters = "struct,entry"; static constexpr const char *Description = "Extract the named entry from the STRUCT."; static constexpr const char *Example = "struct_extract({'i': 3, 'v2': 3, 'v3': 0}, 'i')"; static constexpr const char *Categories = ""; @@ -27,7 +27,7 @@ struct StructExtractFun { struct StructExtractAtFun { static constexpr const char *Name = "struct_extract_at"; - static constexpr const char *Parameters = "struct,'entry'"; + static constexpr const char *Parameters = "struct,index"; static constexpr const char *Description = "Extract the entry from the STRUCT by position (starts at 1!)."; static constexpr const char *Example = "struct_extract_at({'i': 3, 'v2': 3, 'v3': 0}, 2)"; static constexpr const char *Categories = ""; @@ -78,7 +78,7 @@ struct StructConcatFun { struct StructContainsFun { static constexpr const char *Name = "struct_contains"; - static constexpr const char *Parameters = "struct,'entry'"; + static constexpr const char *Parameters = "struct,entry"; static constexpr const char *Description = "Check if an unnamed STRUCT contains the value."; static constexpr const char *Example = "struct_contains(ROW(3, 3, 0), 3)"; static constexpr const char *Categories = ""; @@ -94,7 +94,7 @@ struct StructHasFun { struct StructPositionFun { static constexpr const char *Name = "struct_position"; - static constexpr const char *Parameters = "struct,'entry'"; + static constexpr const char *Parameters = "struct,entry"; static constexpr const char *Description = "Get the position of the entry in an unnamed STRUCT, starting at 1."; static constexpr const char *Example = "struct_position(ROW(3, 3, 0), 3)"; static constexpr const char *Categories = ""; diff --git a/src/duckdb/src/include/duckdb/function/scalar/struct_utils.hpp b/src/duckdb/src/include/duckdb/function/scalar/struct_utils.hpp index 0461b51a9..998f39b1c 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/struct_utils.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/struct_utils.hpp @@ -66,4 +66,32 @@ inline bool TryGetStructExtractChildIndex(const BoundFunctionExpression &func, i return true; } +struct StructExtractPathEntry { + idx_t child_idx; + //! Empty when the input struct is unnamed + Identifier child_name; +}; + +//! Strips struct extracts, appending the path base first +inline const Expression &PeelStructExtractPath(const Expression &expr, vector &path) { + if (expr.GetExpressionClass() != ExpressionClass::BOUND_FUNCTION) { + return expr; + } + auto &func = expr.Cast(); + idx_t child_idx; + if (!TryGetStructExtractChildIndex(func, child_idx) || func.GetChildren().empty()) { + return expr; + } + auto &base = PeelStructExtractPath(*func.GetChildren()[0], path); + auto &input_type = func.GetChildren()[0]->GetReturnType(); + Identifier child_name; + if (input_type.id() == LogicalTypeId::STRUCT) { + child_name = StructType::GetChildName(input_type, child_idx); + } + path.push_back({child_idx, std::move(child_name)}); + return base; +} + +const Expression &PeelStructExtractPath(Expression &&expr, vector &path) = delete; + } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/function/scalar/system_functions.hpp b/src/duckdb/src/include/duckdb/function/scalar/system_functions.hpp index 14161572a..774b6d2a3 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/system_functions.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/system_functions.hpp @@ -37,7 +37,7 @@ struct CombineFun { struct ToAggregateStateFun { static constexpr const char *Name = "to_aggregate_state"; - static constexpr const char *Parameters = "data,name,signature"; + static constexpr const char *Parameters = "data,name,signature,constant_parameters,order_by"; static constexpr const char *Description = "Converts a value into the aggregate state of the aggregate function with the given name and signature. The type of the value must exactly match the state layout of the aggregate function. An optional fourth argument supplies constant parameter values (e.g. string_agg's separator) as a list with one entry per argument, using NULL for arguments that are not bound to a constant."; static constexpr const char *Example = "to_aggregate_state({'count': 1, 'value': 42.0}, 'avg', ['DOUBLE'])"; static constexpr const char *Categories = ""; diff --git a/src/duckdb/src/include/duckdb/function/scalar/tablefilter_functions.hpp b/src/duckdb/src/include/duckdb/function/scalar/tablefilter_functions.hpp index 6d1a27029..6ea5f6212 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/tablefilter_functions.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/tablefilter_functions.hpp @@ -17,7 +17,7 @@ namespace duckdb { struct TableFilterBloomFilterFun { static constexpr const char *Name = "__internal_tablefilter_bloom_filter"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "col"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -27,7 +27,7 @@ struct TableFilterBloomFilterFun { struct TableFilterDynamicFun { static constexpr const char *Name = "__internal_tablefilter_dynamic"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "col"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -37,7 +37,7 @@ struct TableFilterDynamicFun { struct TableFilterOptionalFun { static constexpr const char *Name = "__internal_tablefilter_optional"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "col"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -47,7 +47,7 @@ struct TableFilterOptionalFun { struct TableFilterPrefixRangeFun { static constexpr const char *Name = "__internal_tablefilter_prefix_range"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "col"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; @@ -57,7 +57,7 @@ struct TableFilterPrefixRangeFun { struct TableFilterSelectivityOptionalFun { static constexpr const char *Name = "__internal_tablefilter_selectivity_optional"; - static constexpr const char *Parameters = ""; + static constexpr const char *Parameters = "col"; static constexpr const char *Description = ""; static constexpr const char *Example = ""; static constexpr const char *Categories = ""; diff --git a/src/duckdb/src/include/duckdb/function/scalar/variant_utils.hpp b/src/duckdb/src/include/duckdb/function/scalar/variant_utils.hpp index 3881180ce..fc898ac80 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/variant_utils.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/variant_utils.hpp @@ -152,6 +152,8 @@ struct VariantUtils { //! Whether or not a type is natively supported in variant DUCKDB_API static bool VariantSupportsType(const LogicalType &type); + //! Whether casting a value shredded as "shredded_type" to "target" gives the same result as casting the VARIANT + DUCKDB_API static bool ShreddedCastMatchesVariantCast(const LogicalType &shredded_type, const LogicalType &target); //! Build a canonical (unshredded) VARIANT vector by traversing a variant directly through a //! VariantIterator - avoids materializing the intermediate vector tree diff --git a/src/duckdb/src/include/duckdb/function/scalar_function.hpp b/src/duckdb/src/include/duckdb/function/scalar_function.hpp index c0df75185..1cc218b74 100644 --- a/src/duckdb/src/include/duckdb/function/scalar_function.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar_function.hpp @@ -56,6 +56,7 @@ struct BindLambdaContext { class Binder; class BoundFunctionExpression; class BoundScalarFunction; +class ParsedExpression; class ScalarFunctionCatalogEntry; struct StatementProperties; @@ -112,6 +113,14 @@ struct FunctionBindExpressionInput { vector> &children; }; +struct FunctionUnbindInput { + FunctionUnbindInput(const BoundFunctionExpression &expression_p, vector> children_p); + ~FunctionUnbindInput(); + + const BoundFunctionExpression &expression; + vector> children; +}; + struct FunctionToStringInput { FunctionToStringInput(const BoundScalarFunction &bound_function, optional_ptr bind_data_p, const vector> &children_p) @@ -128,11 +137,14 @@ struct FunctionToStringInput { }; class BindScalarFunctionInput; +class ResolveScalarFunctionTypesInput; //! The scalar function type typedef std::function scalar_function_t; //! The type to bind the scalar function and to create the function data typedef unique_ptr (*bind_scalar_function_t)(BindScalarFunctionInput &input); +//! The type to resolve the argument and return types of the scalar function, before casts are added to the arguments +typedef void (*resolve_scalar_types_t)(ResolveScalarFunctionTypesInput &input); //! The type to initialize a thread local state for the scalar function typedef unique_ptr (*init_local_state_t)(ExpressionState &state, const BoundFunctionExpression &expr, @@ -165,6 +177,9 @@ typedef unique_ptr (*function_bind_expression_t)(FunctionBindExpress //! Convert a scalar function to string typedef string (*function_to_string_t)(FunctionToStringInput &input); +//! Reconstruct an invocation from children exported in the current binding scope +typedef unique_ptr (*scalar_function_unbind_t)(FunctionUnbindInput &input); + //! Get the expression type of a function typedef ExpressionType (*function_get_expression_type_t)(FunctionToStringInput &input); @@ -177,6 +192,8 @@ class ScalarFunctionCallbacks { scalar_function_t function = nullptr; //! Direct selection callback (if any) scalar_function_select_t select_function = nullptr; + //! Resolves the argument / return types (if any) + resolve_scalar_types_t resolve_types = nullptr; //! The bind function (if any) bind_scalar_function_t bind = nullptr; //! Init thread local state for the function (if any) @@ -191,6 +208,8 @@ class ScalarFunctionCallbacks { get_modified_databases_t get_modified_databases = nullptr; //! Convert a scalar function to string function_to_string_t to_string = nullptr; + //! Reconstruct the bound SQL invocation + scalar_function_unbind_t unbind = nullptr; //! Get the expression type function_get_expression_type_t get_expression_type = nullptr; @@ -261,6 +280,10 @@ class BaseScalarFunction { auto GetSelectCallback() const -> scalar_function_select_t { return callbacks.select_function; } auto SetSelectCallback(scalar_function_select_t callback) -> void { callbacks.select_function = callback; } + auto HasResolveTypesCallback() const -> bool { return callbacks.resolve_types != nullptr; } + auto GetResolveTypesCallback() const -> resolve_scalar_types_t { return callbacks.resolve_types; } + auto SetResolveTypesCallback(resolve_scalar_types_t callback) -> void { callbacks.resolve_types = callback; } + auto HasBindCallback() const -> bool { return callbacks.bind != nullptr; }; auto GetBindCallback() const -> bind_scalar_function_t { return callbacks.bind; }; auto SetBindCallback(bind_scalar_function_t callback) -> void { callbacks.bind = callback; } @@ -299,6 +322,10 @@ class BaseScalarFunction { auto SetToStringCallback(function_to_string_t callback) -> void { callbacks.to_string = callback; } auto FunctionToString(FunctionToStringInput &input) const -> string { return callbacks.to_string(input); } + auto HasUnbindCallback() const -> bool { return callbacks.unbind != nullptr; } + auto SetUnbindCallback(scalar_function_unbind_t callback) -> void { callbacks.unbind = callback; } + auto GetUnbindCallback() const -> scalar_function_unbind_t { return callbacks.unbind; } + auto HasLegacySerializeCallback() const -> bool { return callbacks.legacy_serialize != nullptr; } auto SetLegacySerializeCallback(function_legacy_serialize_t callback) -> void { callbacks.legacy_serialize = callback; } auto GetLegacySerializeCallback() const -> function_legacy_serialize_t { return callbacks.legacy_serialize; } @@ -365,7 +392,11 @@ class BaseScalarFunction { protected: FunctionProperties properties; + +private: ScalarFunctionCallbacks callbacks; + +protected: shared_ptr function_info; //! Per-argument declarative properties (monotonicity). Empty = no claims made. @@ -438,8 +469,6 @@ class ScalarFunction : public BaseScalarFunction, DUCKDB_API bool operator==(const ScalarFunction &rhs) const; DUCKDB_API bool operator!=(const ScalarFunction &rhs) const; - DUCKDB_API bool Equal(const ScalarFunction &rhs) const; - public: unique_ptr Bind(ClientContext &context, vector> arguments, optional_ptr binder = nullptr) const; @@ -576,13 +605,32 @@ class BoundScalarFunction : public BaseScalarFunction, publ const shared_ptr &GetDefinition() const { return definition; } + //! The number of arguments that were received by the standard and positional-only parameters, they come first + idx_t GetStandardArgumentCount() const { + return BoundSimpleFunction::GetStandardArgumentCount(definition->GetSignature()); + } + //! The number of arguments that were received by "*args", they directly follow the standard parameters + idx_t GetVarArgsCount() const { + return BoundSimpleFunction::GetVarArgsCount(definition->GetSignature()); + } + //! The number of arguments that were received by the keyword-only parameters, they follow "*args" + idx_t GetKeywordOnlyArgumentCount() const { + return BoundSimpleFunction::GetKeywordOnlyArgumentCount(definition->GetSignature()); + } + //! The number of arguments that were received by "**kwargs", they are the last arguments + idx_t GetKwargsCount() const { + return BoundSimpleFunction::GetKwargsCount(definition->GetSignature()); + } + //! The kind of the parameter that received the argument at the given index + FunctionParameterKind GetArgumentParameterKind(idx_t argument_index) const { + return BoundSimpleFunction::GetArgumentParameterKind(definition->GetSignature(), argument_index); + } //! Restore the definition after the bound function has been replaced wholesale, together with the //! qualification it carries - the replacement is a specialized implementation, not a different function void SetDefinition(shared_ptr definition_p) { definition = std::move(definition_p); if (definition) { - schema_name = definition->GetSchemaName(); - catalog_name = definition->GetCatalogName(); + qualified_name = definition->GetQualifiedName().WithName(GetName()); } } const vector &GetLogicalArguments() const { @@ -641,4 +689,40 @@ class BindScalarFunctionInput : public BindFunctionInput { optional_ptr binder; }; +//! Input of the resolve_types callback - the arguments are read-only, the bound function can be modified +class ResolveScalarFunctionTypesInput : private BindFunctionInput { +public: + ResolveScalarFunctionTypesInput(ClientContext &context_p, BoundScalarFunction &bound_function_p, + vector> &arguments_p, + const vector &argument_names_p) + : BindFunctionInput(context_p, bound_function_p, arguments_p, &argument_names_p), + bound_function(bound_function_p), arguments(arguments_p) { + } + + using BindFunctionInput::GetArgumentNames; + using BindFunctionInput::GetClientContext; + using BindFunctionInput::GetConstant; + using BindFunctionInput::GetNonNullConstant; + using BindFunctionInput::TryGetConstant; + + BoundScalarFunction &GetBoundFunction() const { + return bound_function; + } + //! The argument expressions - these must not be modified + const vector> &GetArguments() const { + return arguments; + } + idx_t GetArgumentCount() const { + return arguments.size(); + } + DUCKDB_API const Expression &GetArgument(idx_t arg_idx) const; + //! The type of the argument expression, before it is cast to the argument type of the bound function + DUCKDB_API const LogicalType &GetArgumentType(idx_t arg_idx) const; + DUCKDB_API vector GetArgumentTypes() const; + +private: + BoundScalarFunction &bound_function; + const vector> &arguments; +}; + } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/function/table/arrow.hpp b/src/duckdb/src/include/duckdb/function/table/arrow.hpp index bd0b78398..27760b48b 100644 --- a/src/duckdb/src/include/duckdb/function/table/arrow.hpp +++ b/src/duckdb/src/include/duckdb/function/table/arrow.hpp @@ -42,28 +42,24 @@ struct ArrowStreamParameters { TableFilterSet *filters; }; -typedef unique_ptr (*stream_factory_produce_t)(uintptr_t stream_factory_ptr, - ArrowStreamParameters ¶meters); -typedef void (*stream_factory_get_schema_t)(ArrowArrayStream *stream_factory_ptr, ArrowSchema &schema); +//! Produces Arrow schemas and streams for a single scan invocation. +//! Implementations retain any external objects needed by the produced streams. +struct ArrowScanFactory : public TableFunctionInfo { + virtual void GetSchema(ArrowSchema &schema) = 0; + virtual unique_ptr ProduceStream(ArrowStreamParameters ¶meters) = 0; +}; struct ArrowScanFunctionData : public TableFunctionData { public: - ArrowScanFunctionData(stream_factory_produce_t scanner_producer_p, uintptr_t stream_factory_ptr_p, - shared_ptr dependency = nullptr) - : lines_read(0), rows_per_thread(0), stream_factory_ptr(stream_factory_ptr_p), - scanner_producer(scanner_producer_p), dependency(std::move(dependency)) { + explicit ArrowScanFunctionData(shared_ptr factory_p) + : lines_read(0), rows_per_thread(0), factory(std::move(factory_p)) { } vector all_types; atomic lines_read; ArrowSchemaWrapper schema_root; idx_t rows_per_thread; - //! Pointer to the scanner factory - uintptr_t stream_factory_ptr; - //! Pointer to the scanner factory produce - stream_factory_produce_t scanner_producer; - //! The (optional) dependency of this function (used in Python for example) - shared_ptr dependency; + shared_ptr factory; //! Arrow table data ArrowTableSchema arrow_table; //! Whether projection pushdown is enabled on the scan diff --git a/src/duckdb/src/include/duckdb/function/table/direct_file_reader.hpp b/src/duckdb/src/include/duckdb/function/table/direct_file_reader.hpp index f853074d3..979e472fd 100644 --- a/src/duckdb/src/include/duckdb/function/table/direct_file_reader.hpp +++ b/src/duckdb/src/include/duckdb/function/table/direct_file_reader.hpp @@ -9,6 +9,7 @@ #pragma once +#include "duckdb/common/atomic.hpp" #include "duckdb/common/multi_file/base_file_reader.hpp" namespace duckdb { @@ -26,13 +27,14 @@ class DirectFileReader : public BaseFileReader { AsyncResult Scan(ClientContext &context, GlobalTableFunctionState &global_state, LocalTableFunctionState &local_state, DataChunk &chunk) override; void FinishFile(ClientContext &context, GlobalTableFunctionState &gstate) override; + double GetProgressInFile(ClientContext &context) override; string GetReaderType() const override { return "File"; }; private: - bool done; + atomic done; LogicalType type; }; diff --git a/src/duckdb/src/include/duckdb/function/table/read_csv.hpp b/src/duckdb/src/include/duckdb/function/table/read_csv.hpp index c8eb1a9f7..4e2d07fe1 100644 --- a/src/duckdb/src/include/duckdb/function/table/read_csv.hpp +++ b/src/duckdb/src/include/duckdb/function/table/read_csv.hpp @@ -72,8 +72,6 @@ struct ReadCSVData : public BaseCSVData { //! The buffer manager (if any): this is used when automatic detection is used during binding. //! In this case, some CSV buffers have already been read and can be reused. shared_ptr buffer_manager; - //! Column info (used for union reader serialization) - vector column_info; //! The CSV schema, in case there is a unified schema that all files must read CSVSchema csv_schema; @@ -114,6 +112,10 @@ struct CSVCopyFunction { struct ReadCSVTableFunction { static TableFunction GetFunction(); static TableFunction GetAutoFunction(); + //! The CSV reader that reads a single file - this is what the multi-file CSV reader wraps + static TableFunction GetSingleFileFunction(); + //! The single-file CSV reader wrapped into a multi-file function + static TableFunction GetMultiFileFunction(Identifier name); static void ReadCSVAddNamedParameters(TableFunction &table_function); static void RegisterFunction(BuiltinFunctions &set); }; diff --git a/src/duckdb/src/include/duckdb/function/table/read_file.hpp b/src/duckdb/src/include/duckdb/function/table/read_file.hpp index e0d2a51c2..9e2531c63 100644 --- a/src/duckdb/src/include/duckdb/function/table/read_file.hpp +++ b/src/duckdb/src/include/duckdb/function/table/read_file.hpp @@ -30,6 +30,7 @@ struct ReadFileGlobalState : public GlobalTableFunctionState { shared_ptr file_list; vector column_ids; bool requires_file_open = false; + bool requires_file_metadata = false; unique_ptr stream; }; diff --git a/src/duckdb/src/include/duckdb/function/table_function.hpp b/src/duckdb/src/include/duckdb/function/table_function.hpp index 736e2a477..eea0000df 100644 --- a/src/duckdb/src/include/duckdb/function/table_function.hpp +++ b/src/duckdb/src/include/duckdb/function/table_function.hpp @@ -25,6 +25,8 @@ namespace duckdb { enum class TablePartitionInfo : uint8_t; struct PartitionStatistics; +struct TableFunctionFileBindInput; +struct TableFunctionFileInitInput; //! Controls how a table function manages parallelism. enum class TableFunctionParallelism : uint8_t { @@ -37,6 +39,7 @@ class BaseStatistics; class LogicalDependencyList; class LogicalGet; class TableFunction; +class BoundTableFunction; class TableFilterSet; class TableFunctionRef; class TableCatalogEntry; @@ -51,6 +54,7 @@ enum class OrderByStatistics : uint8_t; struct RowGroupOrderOptions; class LogicalOperator; class Binder; +class QueryNode; struct TableFunctionInfo { DUCKDB_API virtual ~TableFunctionInfo(); @@ -107,10 +111,10 @@ struct LocalTableFunctionState { }; struct TableFunctionBindInput { - TableFunctionBindInput(vector &inputs, named_parameter_map_t &named_parameters, + TableFunctionBindInput(vector &inputs, named_argument_map_t &named_parameters, vector &input_table_types, vector &input_table_names, optional_ptr info, optional_ptr binder, - TableFunction &table_function, const TableFunctionRef &ref, + BoundTableFunction &table_function, const TableFunctionRef &ref, optional_ptr> input_plan = nullptr) : inputs(inputs), named_parameters(named_parameters), input_table_types(input_table_types), input_table_names(input_table_names), info(info), binder(binder), table_function(table_function), ref(ref), @@ -118,27 +122,16 @@ struct TableFunctionBindInput { } vector &inputs; - named_parameter_map_t &named_parameters; + named_argument_map_t &named_parameters; vector &input_table_types; vector &input_table_names; optional_ptr info; optional_ptr binder; - TableFunction &table_function; + BoundTableFunction &table_function; const TableFunctionRef &ref; optional_ptr> input_plan; - //! (Optional) The schema this bind is expected to produce. This is set when binding a single file of a - //! multi-file scan whose schema was already determined - the bind should read the file using this schema - //! instead of determining a schema of its own - optional_ptr> expected_names; - optional_ptr> expected_types; - //! (Optional) The bind data that determined the schema above, when it came from another bind of this same - //! function. This lets the bind read the file exactly the way the schema was determined, rather than deriving - //! that from the names and types alone - optional_ptr expected_bind_data; - - bool HasExpectedSchema() const { - return expected_names && expected_types; - } + //! (Optional) Set when a multi-file scan binds this function to read one of its files + optional_ptr multi_file_input; }; struct TableFunctionInitInput { @@ -171,6 +164,8 @@ struct TableFunctionInitInput { optional_ptr filters; optional_ptr sample_options; optional_ptr op; + //! (Optional) Set when a multi-file scan initializes this function to read one of its files + optional_ptr multi_file_input; bool CanRemoveFilterColumns() const { if (projection_ids.empty()) { @@ -186,16 +181,6 @@ struct TableFunctionInitInput { } }; -//! Input for combining the schemas of several files that were bound individually into one schema -struct TableFunctionCombineSchemaInput { - explicit TableFunctionCombineSchemaInput(const vector> &bind_data_p) - : bind_data(bind_data_p) { - } - - //! The bind data of each of the files whose schemas are being combined - in file order - const vector> &bind_data; -}; - struct TableFunctionInput { public: TableFunctionInput(optional_ptr bind_data_p, @@ -235,10 +220,10 @@ struct TableFunctionProjectionExpressionInput { }; struct TableFunctionToStringInput { - TableFunctionToStringInput(const TableFunction &table_function_p, optional_ptr bind_data_p) + TableFunctionToStringInput(const BoundTableFunction &table_function_p, optional_ptr bind_data_p) : table_function(table_function_p), bind_data(bind_data_p) { } - const TableFunction &table_function; + const BoundTableFunction &table_function; optional_ptr bind_data; }; @@ -271,14 +256,25 @@ struct TableFunctionGetStatisticsInput { }; struct GetPartitionStatsInput { - GetPartitionStatsInput(const TableFunction &table_function_p, optional_ptr bind_data_p) + GetPartitionStatsInput(const BoundTableFunction &table_function_p, optional_ptr bind_data_p) : table_function(table_function_p), bind_data(bind_data_p) { } - const TableFunction &table_function; + const BoundTableFunction &table_function; optional_ptr bind_data; }; +struct TableFunctionGetBindInfoInput { +public: + TableFunctionGetBindInfoInput(ClientContext &context, optional_ptr bind_data_p) + : context(context), bind_data(bind_data_p) { + } + +public: + ClientContext &context; + optional_ptr bind_data; +}; + struct TableFunctionGetMetricsInput { public: TableFunctionGetMetricsInput(ClientContext &context, optional_ptr bind_data_p, @@ -301,11 +297,19 @@ enum class ScanType : uint8_t { TABLE, PARQUET, EXTERNAL }; struct BindInfo { public: explicit BindInfo(ScanType type_p) : type(type_p) {}; - explicit BindInfo(TableCatalogEntry &table) : type(ScanType::TABLE), table(&table) {}; unordered_map options; ScanType type; - optional_ptr table; + + //! An identifier attached to a result column of the bound call, or to a field nested inside one (addressed as in + //! MultiFileColumnDefinition::ResolveChildPath) - e.g. a field id, by which a multi-file function wrapping the + //! function maps the columns of its files onto one another + struct ColumnIdentifier { + idx_t column_index; + vector child_path; + Value identifier; + }; + vector column_identifiers; void InsertOption(const string &name, Value value) { // NOLINT: work-around bug in clang-tidy if (options.find(name) != options.end()) { @@ -362,9 +366,11 @@ typedef OperatorFinalizeResultType (*table_in_out_function_final_t)(ExecutionCon typedef OperatorPartitionData (*table_function_get_partition_data_t)(ClientContext &context, TableFunctionGetPartitionInput &input); -typedef BindInfo (*table_function_get_bind_info_t)(const optional_ptr bind_data); +typedef BindInfo (*table_function_get_bind_info_t)(TableFunctionGetBindInfoInput &input); +//! The table a bound call scans, if it scans one - see LogicalGet::GetTable +typedef optional_ptr (*table_function_get_table_entry_t)(optional_ptr bind_data); -typedef unique_ptr (*table_function_get_multi_file_reader_t)(const TableFunction &); +typedef unique_ptr (*table_function_get_multi_file_reader_t)(const BoundTableFunction &); typedef bool (*table_function_supports_pushdown_type_t)(const FunctionData &bind_data, idx_t col_idx); @@ -383,26 +389,21 @@ typedef void (*table_function_pushdown_complex_filter_t)(ClientContext &context, FunctionData *bind_data, vector> &filters); typedef bool (*table_function_pushdown_expression_t)(ClientContext &context, const LogicalGet &get, Expression &expr); -//! Claims the next batch for the given local state - returns false when there is nothing left to scan. -//! A function that implements this is scanned one batch at a time by its caller, rather than being run until it -//! returns an empty chunk. This lets the caller tell the batches apart, so that batches scanned in parallel can be -//! put back in order -typedef bool (*table_function_claim_batch_t)(ClientContext &context, TableFunctionInput &input); -//! Called when a local state will not scan any more batches - lets the function release the resources of the batch -//! it scanned last. The counterpart of table_function_claim_batch_t -typedef void (*table_function_finish_batch_t)(ClientContext &context, TableFunctionInput &input); -//! Combines the schemas of several individually bound files into one. Returns the bind data describing the combined -//! schema, which is handed to the bind of every file that is read - or nullptr if the schemas could not be combined, -//! in which case the caller falls back to combining the return types -typedef unique_ptr (*table_function_combine_schema_t)(ClientContext &context, - TableFunctionCombineSchemaInput &input, - vector &return_types, - vector &names); typedef InsertionOrderPreservingMap (*table_function_to_string_t)(TableFunctionToStringInput &input); +struct TableFunctionToSQLResult { + unique_ptr source; + string unsupported_reason; +}; + +//! Reconstruct the unprojected source; the exporter applies scan projections, predicates and ordinality. +//! Return an owned table reference without executing the source, or a reason why reconstruction is unsupported. +typedef TableFunctionToSQLResult (*table_function_to_sql_t)(ClientContext &context, const LogicalGet &get); + typedef void (*table_function_serialize_t)(Serializer &serializer, const optional_ptr bind_data, - const TableFunction &function); -typedef unique_ptr (*table_function_deserialize_t)(Deserializer &deserializer, TableFunction &function); + const BoundTableFunction &function); +typedef unique_ptr (*table_function_deserialize_t)(Deserializer &deserializer, + BoundTableFunction &function); typedef bool (*table_function_projection_expression_pushdown_t)(ClientContext &context, const TableFunctionProjectionExpressionInput &input); @@ -429,25 +430,13 @@ enum class TableFunctionInitialization { INITIALIZE_ON_EXECUTE, INITIALIZE_ON_SC enum class TableFunctionReturnType { TABLE_RETURNING_FUNCTION, SET_RETURNING_FUNCTION }; -class TableFunction : public SimpleNamedParameterFunction { // NOLINT: work-around bug in clang-tidy +//! The callbacks and flags that make up a table function's behaviour, shared by the unbound TableFunction and by the +//! BoundTableFunction that a bound call produces. It deliberately does not derive from Function, so that each of the +//! two can pair it with its own name and argument representation - the split BaseScalarFunction makes for scalars +class BaseTableFunction { public: - DUCKDB_API TableFunction(); - // Overloads taking table_function_t - DUCKDB_API - TableFunction(Identifier name, const vector &arguments, table_function_t function, - table_function_bind_t bind = nullptr, table_function_init_global_t init_global = nullptr, - table_function_init_local_t init_local = nullptr); - DUCKDB_API - TableFunction(const vector &arguments, table_function_t function, table_function_bind_t bind = nullptr, - table_function_init_global_t init_global = nullptr, table_function_init_local_t init_local = nullptr); - // Overloads taking std::nullptr - DUCKDB_API - TableFunction(Identifier name, const vector &arguments, std::nullptr_t function, - table_function_bind_t bind = nullptr, table_function_init_global_t init_global = nullptr, - table_function_init_local_t init_local = nullptr); - DUCKDB_API - TableFunction(const vector &arguments, std::nullptr_t function, table_function_bind_t bind = nullptr, - table_function_init_global_t init_global = nullptr, table_function_init_local_t init_local = nullptr); + DUCKDB_API BaseTableFunction(table_function_t function, table_function_bind_t bind, + table_function_init_global_t init_global, table_function_init_local_t init_local); bool HasBindCallback() const { return bind != nullptr; @@ -518,21 +507,19 @@ class TableFunction : public SimpleNamedParameterFunction { // NOLINT: work-arou table_function_pushdown_complex_filter_t pushdown_complex_filter; //! (Optional) whether or not this table function supports pushing down an expression into a TableFilter table_function_pushdown_expression_t pushdown_expression; - //! (Optional) combines the schemas of several files that were bound individually into a single schema - //! Used when this function reads a single file and is wrapped into a multi-file function - table_function_combine_schema_t combine_schema; - //! (Optional) claims the next batch for a local state - see table_function_claim_batch_t - table_function_claim_batch_t claim_batch; - //! (Optional) called when a local state will not scan any more batches - see table_function_finish_batch_t - table_function_finish_batch_t finish_batch; //! (Optional) function for rendering the operator to a string in explain/profiling output (invoked pre-execution) table_function_to_string_t to_string; + //! (Optional) reconstruct the source's SQL-visible state without retaining native objects. + //! Must not execute the source or perform effects during export. + table_function_to_sql_t to_sql = nullptr; //! (Optional) return how much of the table we have scanned up to this point (% of the data) table_function_progress_t table_scan_progress; //! (Optional) returns the partition info of the current scan operator table_function_get_partition_data_t get_partition_data; //! (Optional) returns extra bind info table_function_get_bind_info_t get_bind_info; + //! (Optional) the table a bound call scans, e.g. for a scan of a catalog table + table_function_get_table_entry_t get_table_entry; //! (Optional) pushes down projection expressions like len() in "SELECT len(str)" or //! casts like "col as UINTEGER" in "SELECT col::UINTEGER" to scanner. //! Returns true if pushdown was successful @@ -593,9 +580,117 @@ class TableFunction : public SimpleNamedParameterFunction { // NOLINT: work-arou //! How this table function manages parallelism TableFunctionParallelism parallelism = TableFunctionParallelism::SELF_MANAGED_PARALLELISM; - DUCKDB_API bool Equal(const TableFunction &rhs) const; + DUCKDB_API bool operator==(const BaseTableFunction &rhs) const; +}; + +//! A table function as it is registered in the catalog: its behaviour, plus the signature declaring what it accepts +class TableFunction : public BaseTableFunction, public SimpleFunction { // NOLINT: work-around bug in clang-tidy +public: + DUCKDB_API TableFunction(); + // Overloads taking table_function_t + DUCKDB_API + TableFunction(Identifier name, const vector &arguments, table_function_t function, + table_function_bind_t bind = nullptr, table_function_init_global_t init_global = nullptr, + table_function_init_local_t init_local = nullptr); + DUCKDB_API + TableFunction(const vector &arguments, table_function_t function, table_function_bind_t bind = nullptr, + table_function_init_global_t init_global = nullptr, table_function_init_local_t init_local = nullptr); + // Overloads taking std::nullptr + DUCKDB_API + TableFunction(Identifier name, const vector &arguments, std::nullptr_t function, + table_function_bind_t bind = nullptr, table_function_init_global_t init_global = nullptr, + table_function_init_local_t init_local = nullptr); + DUCKDB_API + TableFunction(const vector &arguments, std::nullptr_t function, table_function_bind_t bind = nullptr, + table_function_init_global_t init_global = nullptr, table_function_init_local_t init_local = nullptr); + // Overloads taking a braced list, so that "{}" does not also match FunctionSignature + TableFunction(Identifier name, std::initializer_list arguments, table_function_t function, + table_function_bind_t bind = nullptr, table_function_init_global_t init_global = nullptr, + table_function_init_local_t init_local = nullptr) + : TableFunction(std::move(name), vector(arguments), function, bind, init_global, init_local) { + } + TableFunction(Identifier name, std::initializer_list arguments, std::nullptr_t function, + table_function_bind_t bind = nullptr, table_function_init_global_t init_global = nullptr, + table_function_init_local_t init_local = nullptr) + : TableFunction(std::move(name), vector(arguments), function, bind, init_global, init_local) { + } + TableFunction(std::initializer_list arguments, table_function_t function, + table_function_bind_t bind = nullptr, table_function_init_global_t init_global = nullptr, + table_function_init_local_t init_local = nullptr) + : TableFunction(vector(arguments), function, bind, init_global, init_local) { + } + TableFunction(std::initializer_list arguments, std::nullptr_t function, + table_function_bind_t bind = nullptr, table_function_init_global_t init_global = nullptr, + table_function_init_local_t init_local = nullptr) + : TableFunction(vector(arguments), function, bind, init_global, init_local) { + } + // Overloads taking proper signatures + DUCKDB_API + TableFunction(Identifier name, FunctionSignature signature, table_function_t function, + table_function_bind_t bind = nullptr, table_function_init_global_t init_global = nullptr, + table_function_init_local_t init_local = nullptr); + DUCKDB_API + TableFunction(Identifier name, FunctionSignature signature, std::nullptr_t function, + table_function_bind_t bind = nullptr, table_function_init_global_t init_global = nullptr, + table_function_init_local_t init_local = nullptr); + + DUCKDB_API + TableFunction(FunctionSignature signature, table_function_t function, table_function_bind_t bind = nullptr, + table_function_init_global_t init_global = nullptr, table_function_init_local_t init_local = nullptr); + +public: DUCKDB_API bool operator==(const TableFunction &rhs) const; DUCKDB_API bool operator!=(const TableFunction &rhs) const; }; +//! A table function as a bound call refers to it: its behaviour, plus the argument types the call was bound with. +//! Those are the types plan serialization records, so they must survive independently of the catalog's declaration +class BoundTableFunction : public BaseTableFunction, public BoundSimpleFunction { +public: + DUCKDB_API BoundTableFunction(); + DUCKDB_API explicit BoundTableFunction(const TableFunction &function); + DUCKDB_API explicit BoundTableFunction(shared_ptr function); + + //! The function this was bound from. For a function bound from a TableFunctionSet this is the set's own + //! overload, so it compares equal by pointer across binds. Functions bound outside of a set are copied into a + //! definition of their own. Only null in a moved-from bound function + const shared_ptr &GetDefinition() const { + return definition; + } + //! The signature this call was bound against + const FunctionSignature &GetSignature() const { + return definition->GetSignature(); + } + //! The number of arguments that were received by the standard and positional-only parameters, they come first + idx_t GetStandardArgumentCount() const { + return BoundSimpleFunction::GetStandardArgumentCount(definition->GetSignature()); + } + //! The number of arguments that were received by "*args", they directly follow the standard parameters + idx_t GetVarArgsCount() const { + return BoundSimpleFunction::GetVarArgsCount(definition->GetSignature()); + } + //! The number of arguments that were received by the keyword-only parameters, they follow "*args" + idx_t GetKeywordOnlyArgumentCount() const { + return BoundSimpleFunction::GetKeywordOnlyArgumentCount(definition->GetSignature()); + } + //! The number of arguments that were received by "**kwargs", they are the last arguments + idx_t GetKwargsCount() const { + return BoundSimpleFunction::GetKwargsCount(definition->GetSignature()); + } + //! The kind of the parameter that received the argument at the given index + FunctionParameterKind GetArgumentParameterKind(idx_t argument_index) const { + return BoundSimpleFunction::GetArgumentParameterKind(definition->GetSignature(), argument_index); + } + //! Records the arguments of the call this was bound to, laid out as [standard | *args | keyword-only | **kwargs]. + //! Plan serialization writes them, so that deserialization selects the same overload the call did + DUCKDB_API void SetCallArguments(const vector ¶meters, const named_argument_map_t &named_parameters); + +private: + shared_ptr definition; + +public: + DUCKDB_API bool operator==(const BoundTableFunction &rhs) const; + DUCKDB_API bool operator!=(const BoundTableFunction &rhs) const; +}; + } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/function/variant/variant_value_convert.hpp b/src/duckdb/src/include/duckdb/function/variant/variant_value_convert.hpp index fd93a25bd..c57f74f98 100644 --- a/src/duckdb/src/include/duckdb/function/variant/variant_value_convert.hpp +++ b/src/duckdb/src/include/duckdb/function/variant/variant_value_convert.hpp @@ -121,6 +121,11 @@ struct ValueConverter { static Value VisitObject(const UnifiedVariantVectorData &variant, idx_t row, const VariantNestedData &nested_data) { auto object_children = VariantVisitor::VisitObjectItems(variant, row, nested_data); + if (object_children.size() == 1 && object_children[0].first.empty()) { + // an empty key can't be a STRUCT member name (it would become a TUPLE) - use a MAP instead + auto &child = object_children[0].second; + return Value::MAP(LogicalType::VARCHAR, child.type(), {Value("")}, {child}); + } return Value::STRUCT(std::move(object_children)); } diff --git a/src/duckdb/src/include/duckdb/function/window/value_functions.hpp b/src/duckdb/src/include/duckdb/function/window/value_functions.hpp index 121266c51..fa04466c0 100644 --- a/src/duckdb/src/include/duckdb/function/window/value_functions.hpp +++ b/src/duckdb/src/include/duckdb/function/window/value_functions.hpp @@ -47,7 +47,7 @@ struct NthValueFun { struct LeadFun { static constexpr const char *Name = "lead"; - static constexpr const char *Parameters = "expr[,n[,default]]"; + static constexpr const char *Parameters = "col,offset,default"; static constexpr const char *Description = "The value of expr n rows after the current row, or the default. Can IGNORE or RESPECT NULLS."; static constexpr const char *Example = "LEAD(athlete, 1, NULL) OVER (PARTITION BY event ORDER BY points) AS victorious_over"; static constexpr const char *Categories = ""; @@ -58,7 +58,7 @@ struct LeadFun { struct LagFun { static constexpr const char *Name = "lag"; - static constexpr const char *Parameters = "expr[,n[,default]]"; + static constexpr const char *Parameters = "col,offset,default"; static constexpr const char *Description = "The value of expr n rows after the current row, or the default. Can IGNORE or RESPECT NULLS."; static constexpr const char *Example = "LAG(athlete, 1, NULL) OVER (PARTITION BY event ORDER BY points) AS defeated_by"; static constexpr const char *Categories = ""; diff --git a/src/duckdb/src/include/duckdb/function/window/window_aggregate_states.hpp b/src/duckdb/src/include/duckdb/function/window/window_aggregate_states.hpp index 59d64793f..afefe112f 100644 --- a/src/duckdb/src/include/duckdb/function/window/window_aggregate_states.hpp +++ b/src/duckdb/src/include/duckdb/function/window/window_aggregate_states.hpp @@ -20,16 +20,16 @@ struct WindowAggregateStates { //! The number of states idx_t GetCount() const { - return states.size() / state_size; + return count; } data_ptr_t *GetData() { return FlatVector::GetDataMutable(*statef); } data_ptr_t GetStatePtr(idx_t idx) { - return states.data() + idx * state_size; + return states.get() + idx * state_size; } const_data_ptr_t GetStatePtr(idx_t idx) const { - return states.data() + idx * state_size; + return states.get() + idx * state_size; } //! Initialise all the states void Initialize(idx_t count); @@ -48,8 +48,10 @@ struct WindowAggregateStates { const idx_t state_size; //! The allocator to use ArenaAllocator allocator; - //! Data pointer that contains the state data - vector states; + //! The number of states + idx_t count = 0; + //! Buffer that contains the state data, allocated through the buffer allocator + AllocatedData states; //! Reused result state container for the window functions unique_ptr statef; }; diff --git a/src/duckdb/src/include/duckdb/function/window/window_aggregator.hpp b/src/duckdb/src/include/duckdb/function/window/window_aggregator.hpp index ae015928c..e5fe8cd3e 100644 --- a/src/duckdb/src/include/duckdb/function/window/window_aggregator.hpp +++ b/src/duckdb/src/include/duckdb/function/window/window_aggregator.hpp @@ -141,6 +141,8 @@ class WindowAggregatorGlobalState : public GlobalSinkState { //! The filtered rows in inputs. ValidityArray filter_mask; + //! Storage of the filter mask (one byte per row), from the buffer allocator so that it counts against the limit + AllocatedData filter_mask_data; //! Lock for single threading mutable mutex lock; diff --git a/src/duckdb/src/include/duckdb/function/window/window_collection.hpp b/src/duckdb/src/include/duckdb/function/window/window_collection.hpp index 234ed1617..3ec325801 100644 --- a/src/duckdb/src/include/duckdb/function/window/window_collection.hpp +++ b/src/duckdb/src/include/duckdb/function/window/window_collection.hpp @@ -9,10 +9,14 @@ #pragma once #include "duckdb/common/atomic.hpp" +#include "duckdb/common/mutex.hpp" +#include "duckdb/common/once_flag.hpp" #include "duckdb/common/types/column/column_data_collection.hpp" #include "duckdb/common/vector/flat_vector.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" +#include + namespace duckdb { // A wrapper for building ColumnDataCollections in parallel @@ -38,7 +42,7 @@ class WindowCollection { //! Update a thread-local collection for appending data to a given row void GetCollection(idx_t row_idx, ColumnDataCollectionSpec &spec); - //! Single-threaded, idempotent ordered combining of all the appended data. + //! Combine once in order after all appends have finished. void Combine(const ColumnSet &build_validity); //! The collection data. May be null if the column count is 0. @@ -62,6 +66,14 @@ class WindowCollection { //! The (sorted) collection ranges using Range = pair; vector ranges; + +private: + void CombineInternal(const ColumnSet &validity_cols); + +private: + OnceFlag combine_once; + //! Combining consumes its inputs, so failures must be shared rather than retried. + std::exception_ptr combine_error; }; class WindowBuilder { diff --git a/src/duckdb/src/include/duckdb/function/window_function.hpp b/src/duckdb/src/include/duckdb/function/window_function.hpp index 1b114ce61..5f46a7683 100644 --- a/src/duckdb/src/include/duckdb/function/window_function.hpp +++ b/src/duckdb/src/include/duckdb/function/window_function.hpp @@ -378,13 +378,38 @@ class BoundWindowFunction : public BaseWindowFunction, public BoundSimpleFunctio const shared_ptr &GetDefinition() const { return definition; } + //! The number of arguments that were received by the standard and positional-only parameters, they come first + idx_t GetStandardArgumentCount() const { + return BoundSimpleFunction::GetStandardArgumentCount(definition->GetSignature()); + } + //! The number of arguments that were received by "*args", they directly follow the standard parameters + idx_t GetVarArgsCount() const { + return BoundSimpleFunction::GetVarArgsCount(definition->GetSignature()); + } + //! The number of arguments that were received by the keyword-only parameters, they follow "*args" + idx_t GetKeywordOnlyArgumentCount() const { + return BoundSimpleFunction::GetKeywordOnlyArgumentCount(definition->GetSignature()); + } + //! The number of arguments that were received by "**kwargs", they are the last arguments + idx_t GetKwargsCount() const { + return BoundSimpleFunction::GetKwargsCount(definition->GetSignature()); + } + //! The kind of the parameter that received the argument at the given index + FunctionParameterKind GetArgumentParameterKind(idx_t argument_index) const { + return BoundSimpleFunction::GetArgumentParameterKind(definition->GetSignature(), argument_index); + } + const vector &GetLogicalArguments() const { + return logical_arguments; + } + const LogicalType &GetLogicalReturnType() const { + return logical_return_type; + } //! Restore the definition after the bound function has been replaced wholesale, together with the //! qualification it carries - the replacement is a specialized implementation, not a different function void SetDefinition(shared_ptr definition_p) { definition = std::move(definition_p); if (definition) { - schema_name = definition->GetSchemaName(); - catalog_name = definition->GetCatalogName(); + qualified_name = definition->GetQualifiedName().WithName(GetName()); } } @@ -446,7 +471,18 @@ class BoundWindowFunction : public BaseWindowFunction, public BoundSimpleFunctio } private: + void SetLogicalArguments(vector arguments_p) { + logical_arguments = std::move(arguments_p); + } + void SetLogicalReturnType(LogicalType return_type_p) { + logical_return_type = std::move(return_type_p); + } shared_ptr definition; + vector logical_arguments; + LogicalType logical_return_type; + + friend class FunctionBinder; + friend class FunctionSerializer; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/logging/log_storage.hpp b/src/duckdb/src/include/duckdb/logging/log_storage.hpp index 48be5586b..7e05e0108 100644 --- a/src/duckdb/src/include/duckdb/logging/log_storage.hpp +++ b/src/duckdb/src/include/duckdb/logging/log_storage.hpp @@ -90,6 +90,8 @@ class LogStorage { DUCKDB_API virtual unique_ptr CreateScanState(LoggingTargetTable table) const; DUCKDB_API virtual bool Scan(LogStorageScanState &state, DataChunk &result) const; DUCKDB_API virtual void InitializeScan(LogStorageScanState &state) const; + //! The number of rows a scan of the table produces, if known (used for progress reporting) + DUCKDB_API virtual optional_idx GetScanRowCount(LoggingTargetTable table) const; // Reading interface 2: using bind_replace DUCKDB_API virtual unique_ptr BindReplace(ClientContext &context, TableFunctionBindInput &input, @@ -338,6 +340,7 @@ class InMemoryLogStorage : public BufferingLogStorage { unique_ptr CreateScanState(LoggingTargetTable table) const override; bool Scan(LogStorageScanState &state, DataChunk &result) const override; void InitializeScan(LogStorageScanState &state) const override; + optional_idx GetScanRowCount(LoggingTargetTable table) const override; protected: /// Implement BufferingLogStorage interface diff --git a/src/duckdb/src/include/duckdb/logging/log_type.hpp b/src/duckdb/src/include/duckdb/logging/log_type.hpp index 874f9ea85..bf8758d60 100644 --- a/src/duckdb/src/include/duckdb/logging/log_type.hpp +++ b/src/duckdb/src/include/duckdb/logging/log_type.hpp @@ -10,12 +10,14 @@ #include "duckdb/logging/logging.hpp" #include "duckdb/common/string_util.hpp" +#include "duckdb/common/case_insensitive_map.hpp" namespace duckdb { struct FileHandle; struct BaseRequest; struct HTTPResponse; +struct MetricTiming; class PhysicalOperator; enum class PhysicalOperatorType : uint8_t; class AttachedDatabase; @@ -81,13 +83,15 @@ class HTTPLogType : public LogType { public: static constexpr const char *NAME = "HTTP"; static constexpr LogLevel LEVEL = LogLevel::LOG_DEBUG; + static constexpr auto REDACTED_VALUE = "redacted"; //! Construct the log types HTTPLogType(); static LogicalType GetLogType(); - static string ConstructLogMessage(BaseRequest &request, optional_ptr response); + static string ConstructLogMessage(BaseRequest &request, optional_ptr response, + bool redact_http_logs = true); // FIXME: HTTPLogType should be structured probably static string ConstructLogMessage(const string &str) { @@ -122,7 +126,8 @@ class MetricsLogType : public LogType { static LogicalType GetLogType(); - static string ConstructLogMessage(const string &metric, const Value &value); + static string ConstructLogMessage(const string &metric, const Value &value, + optional_ptr timing = nullptr); }; class CheckpointLogType : public LogType { @@ -200,6 +205,19 @@ class AsyncTaskScheduleLogType : public LogType { static string ConstructLogMessage(const string &pool, idx_t task_count); }; +class ProgressVerificationLogType : public LogType { +public: + static constexpr const char *NAME = "ProgressVerification"; + static constexpr LogLevel LEVEL = LogLevel::LOG_INFO; + + ProgressVerificationLogType(); + + static LogicalType GetLogType(); + + static string ConstructLogMessage(const string &invariant, const string &operator_name, const string &pipeline, + const string &detail); +}; + class ExternalResourceLogType : public LogType { public: static constexpr const char *NAME = "ExternalResource"; diff --git a/src/duckdb/src/include/duckdb/logging/logger.hpp b/src/duckdb/src/include/duckdb/logging/logger.hpp index 01655bde1..a5b6d1243 100644 --- a/src/duckdb/src/include/duckdb/logging/logger.hpp +++ b/src/duckdb/src/include/duckdb/logging/logger.hpp @@ -70,7 +70,7 @@ class Logger { // Syntactic sugar for formatted strings template - void WriteLog(const char *log_type, LogLevel log_level, const char *format_string, ARGS... params) { + void WriteLog(const char *log_type, LogLevel log_level, const char *format_string, const ARGS &...params) { auto formatted_string = StringUtil::Format(format_string, params...); WriteLog(log_type, log_level, formatted_string.c_str()); } diff --git a/src/duckdb/src/include/duckdb/main/active_query_context.hpp b/src/duckdb/src/include/duckdb/main/active_query_context.hpp new file mode 100644 index 000000000..f7c87d98b --- /dev/null +++ b/src/duckdb/src/include/duckdb/main/active_query_context.hpp @@ -0,0 +1,111 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/main/active_query_context.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/common.hpp" +#include "duckdb/common/enums/result_lifetime.hpp" +#include "duckdb/common/progress_bar/progress_bar.hpp" +#include "duckdb/execution/executor.hpp" +#include "duckdb/main/prepared_statement_data.hpp" +#include "duckdb/main/query_parameters.hpp" +#include "duckdb/planner/statement_preprocessor.hpp" + +namespace duckdb { +class BaseQueryResult; +class QueryResult; + +//! The user statement a connection runs, from its submission until a call ends it. A statement that expands into +//! several runs them one after another, each with its own executor, as one query +struct ActiveQueryContext { +public: + explicit ActiveQueryContext(PreprocessedStatement statements_p) : statements(std::move(statements_p.statements)) { + } + + //! The text of the statement that is running + string query; + //! Prepared statement data of the statement that is running + shared_ptr prepared; + //! The executor of the statement that is running + unique_ptr executor; + //! The progress bar of the statement that is running + unique_ptr progress_bar; + //! Whether a collector built the open result, which has no way to end the query + bool collector_built_result = false; + //! The format every statement of the query is submitted in; null is the chunk format + shared_ptr format; + //! The consumer's retention decision, applied to the result statement's buffer when it is created + ResultLifetime retention = ResultLifetime::UNDECIDED; + //! Whether the statement whose rows the result carries has started + bool result_statement_started = false; + //! The handle of a statement that expands into several, which the statement producing the result fills in + optional_ptr handle; + //! Whether the statements run inside the submission, where a result collector may build its own result object + bool inside_submission = false; + +public: + void SetOpenResult(BaseQueryResult &result) { + open_result = &result; + } + bool IsOpenResult(BaseQueryResult &result) { + return open_result == &result; + } + bool HasOpenResult() const { + return open_result != nullptr; + } + //! Whether the user's statement expanded into several + bool ExpandsIntoSeveral() const { + // Started statements stay in the list, moved out + return statements.size() > 1; + } + bool HasNextStatement() const { + return next_statement < statements.size(); + } + unique_ptr TakeNextStatement() { + D_ASSERT(HasNextStatement()); + return std::move(statements[next_statement++]); + } + //! Records the databases a statement of the query read and modified + void RecordDatabases(const StatementProperties &properties) { + MergeDatabases(properties, databases); + } + //! Whether a statement of the query that started modifies a database + bool Wrote() const { + return !databases.IsReadOnly(); + } + //! The properties of the statement that produces the result, describing the databases of the whole query + StatementProperties QueryProperties(StatementProperties properties) const { + MergeDatabases(databases, properties); + return properties; + } + +private: + //! The currently open result + BaseQueryResult *open_result = nullptr; + //! The statements the user's statement preprocessed into + vector> statements; + //! The index of the statement that starts next + idx_t next_statement = 0; + //! The databases the statements that already ran read and modified + StatementProperties databases; + +private: + static void MergeDatabases(const StatementProperties &source, StatementProperties &target) { + for (auto &entry : source.read_databases) { + target.read_databases.insert(entry); + } + for (auto &entry : source.modified_databases) { + auto inserted = target.modified_databases.insert(entry); + if (!inserted.second) { + inserted.first->second.modifications |= entry.second.modifications; + } + } + } +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/appender.hpp b/src/duckdb/src/include/duckdb/main/appender.hpp index 532cb23cc..055b9a577 100644 --- a/src/duckdb/src/include/duckdb/main/appender.hpp +++ b/src/duckdb/src/include/duckdb/main/appender.hpp @@ -37,8 +37,8 @@ class BaseAppender { static unique_ptr GetColumnDataTableRef(ColumnDataCollection &collection, const Identifier &table_name, const vector &expected_names); //! Parses the statement to append data. - static unique_ptr ParseStatement(unique_ptr table_ref, const string &query, - const string &table_name); + static unique_ptr ParseStatement(ClientContext &context, unique_ptr table_ref, + const string &query, const string &table_name); protected: //! The allocator for the column data collection. diff --git a/src/duckdb/src/include/duckdb/main/attached_database.hpp b/src/duckdb/src/include/duckdb/main/attached_database.hpp index ffd5fc124..d3c3adffb 100644 --- a/src/duckdb/src/include/duckdb/main/attached_database.hpp +++ b/src/duckdb/src/include/duckdb/main/attached_database.hpp @@ -15,12 +15,14 @@ #include "duckdb/main/valid_checker.hpp" namespace duckdb { +struct CompiledGrammar; class Catalog; class DatabaseInstance; class StorageManager; class TransactionManager; class StorageExtension; class DatabaseManager; +class ResourceDeleter; struct AttachInfo; struct StoredDatabasePath; @@ -67,6 +69,11 @@ struct AttachOptions { //! Constructor for databases we attach when using ATTACH DATABASE. AttachOptions(const unordered_map &options, const AccessMode default_access_mode); + //! The setting an attach option controls, lower-cased. All four spellings of the access mode + //! (readonly, read_only, readwrite, read_write) share one setting, so two of them in the same + //! statement are a collision rather than two independent options. + static string OptionSetting(const string &name); + //! Defaults to the access mode configured in the DBConfig, unless specified otherwise. AccessMode access_mode; //! The recovery type of the database. @@ -90,6 +97,17 @@ struct AttachOptions { unique_ptr stored_database_path; //! Per-database override of vacuum_rebuild_indexes. If not set, the global setting value is used. optional_idx vacuum_rebuild_indexes_threshold; + //! Deleter binding (from ATTACH/CONNECT TO EXTERNAL RESOURCE): on detach, `()` + //! runs to tear down the external resource the attachment owns. + string deleter_function; + Value deleter_payload; + //! Type and name of the owned resource, for teardown logging only. The name is the database alias, + //! or empty where there is none (CONNECT). + string deleter_resource_type; + string deleter_resource_name; + //! Registered resource this attachment BORROWS (from `ATTACH/CONNECT TO EXTERNAL RESOURCE `): it binds + //! no deleter and does not block DESTROY, it only lets DESTROY report what it invalidated. Empty if none. + string borrowed_resource_name; //! Header prefetched during file-type detection, reused when opening the file. Empty for non-DuckDB files. PrefetchedFileData prefetched; }; @@ -137,6 +155,9 @@ class AttachedDatabase : public CatalogEntry, public enable_shared_from_this ExtractDeleter(); bool IsSystem() const; bool IsTemporary() const; bool IsReadOnly() const; @@ -154,12 +175,23 @@ class AttachedDatabase : public CatalogEntry, public enable_shared_from_this &GetAttachOptions() const { return attach_options; } + //! The grammar used for statements issued while a client is CONNECT-ed to this database. Defaults to the + //! passthrough grammar, which interprets only DISCONNECT and forwards everything else verbatim; a backend that + //! speaks DuckDB SQL can override it with the grammar it wants those statements parsed by. + DUCKDB_API shared_ptr GetConnectedGrammar(const ClientContext &context); + void SetConnectedGrammar(shared_ptr grammar) { + connected_grammar = std::move(grammar); + } string StoredPath() const; //! The verbatim ATTACH path before extension-prefix stripping. Unset if not from an ATTACH statement. const optional &GetOriginalPath() const { @@ -193,6 +225,15 @@ class AttachedDatabase : public CatalogEntry, public enable_shared_from_this attach_options; optional original_path; + //! Deleter binding (from ATTACH/CONNECT TO EXTERNAL RESOURCE): moved out via ExtractDeleter on detach. + string deleter_function; + Value deleter_payload; + string deleter_resource_type; + string deleter_resource_name; + //! Registered resource this attachment borrows without owning; see AttachOptions. + string borrowed_resource_name; + //! Overrides the default passthrough grammar used while CONNECT-ed; see GetConnectedGrammar. + shared_ptr connected_grammar; private: //! Clean any (shared) resources held by the database. diff --git a/src/duckdb/src/include/duckdb/main/buffered_data/batched_buffered_data.hpp b/src/duckdb/src/include/duckdb/main/buffered_data/batched_buffered_data.hpp index faf5cd8ef..8adaf1df3 100644 --- a/src/duckdb/src/include/duckdb/main/buffered_data/batched_buffered_data.hpp +++ b/src/duckdb/src/include/duckdb/main/buffered_data/batched_buffered_data.hpp @@ -19,31 +19,32 @@ namespace duckdb { -class StreamQueryResult; +class QueryResult; class InProgressBatch { public: - //! The batch's chunks, in arrival order. - vector chunk_refs; + //! The batch's units, in arrival order. + vector> units; //! Whether the batch is completed (CompleteBatch has been called). bool completed = false; }; -//! The order-preserving streaming buffer: chunks wait in `buffer` until their batch becomes the minimum, then move -//! to the `read_queue` the consumer pops. A minimum-batch producer only parks while chunks are poppable, so a pop +//! The order-preserving streaming buffer: units wait in `buffer` until their batch becomes the minimum, then move +//! to the `read_queue` the consumer pops. A minimum-batch producer only parks while units are poppable, so a pop //! always wakes it class BatchedBufferedData : public BufferedData { public: static constexpr const BufferedData::Type TYPE = BufferedData::Type::BATCHED; public: - BatchedBufferedData(ClientContext &context, ResultLifetime lifetime); + BatchedBufferedData(ClientContext &context, ResultLifetime lifetime, ResultFormatContext format_context, + shared_ptr format); public: - //! Buffer a copy of the chunk, or block the sink when the chunk does not fit. Returns true on block. - bool AppendOrBlock(DataChunk &chunk, idx_t batch, const InterruptState &blocked_sink); + //! Buffer the finished unit under its batch, or block the sink when the unit does not fit. Returns true on block. + bool AppendOrBlock(unique_ptr unit, idx_t batch, const InterruptState &blocked_sink); - unique_ptr Scan() override; + unique_ptr Scan() override; void UpdateMinBatchIndex(idx_t min_batch_index); bool IsMinimumBatchIndex(annotated_lock_guard &lock, idx_t batch) DUCKDB_REQUIRES(glock); void CompleteBatch(idx_t batch); @@ -52,6 +53,9 @@ class BatchedBufferedData : public BufferedData { annotated_lock_guard lock(glock); return !blocked_sinks.empty(); } + bool HasObservableUnit() override { + return !BufferIsEmpty(); + } void UnblockSinks() override; void AssertNoBlockedSinks() override; @@ -60,43 +64,50 @@ class BatchedBufferedData : public BufferedData { annotated_lock_guard lock(glock); return peak_buffered_bytes; } + idx_t PeakStreamingBytes() override { + annotated_lock_guard lock(glock); + return peak_streaming_bytes; + } protected: - //! Consumption order is the read queue, so the replenish stops once it holds a chunk + //! Consumption order is the read queue, so the replenish stops once it holds a unit bool ReplenishSatisfied() override { return !BufferIsEmpty(); } void MoveCompletedBatches(annotated_lock_guard &lock) DUCKDB_REQUIRES(glock); - //! Whether a chunk of incoming_bytes may not be buffered for this batch right now. + //! Whether a unit of incoming_bytes may not be buffered for this batch right now. bool ShouldBlockBatch(annotated_lock_guard &lock, idx_t batch, idx_t incoming_bytes) DUCKDB_REQUIRES(glock); //! Pop restartable sinks into to_unblock. void CollectRestartableSinks(annotated_lock_guard &lock, vector> &to_unblock) DUCKDB_REQUIRES(glock); - //! Deposit a restarting sink's parked chunk. + //! Deposit a restarting sink's parked unit. void DepositParked(annotated_lock_guard &lock, idx_t batch, BlockedSink &sink) DUCKDB_REQUIRES(glock); //! Invoke the collected restartable sinks in to_unblock. void InvokeUnblocks(const vector> &to_unblock) DUCKDB_EXCLUDES(glock); protected: - //! The buffer where chunks are written before they are ready to be read. + //! The buffer where units are written before they are ready to be read. map buffer DUCKDB_GUARDED_BY(glock); idx_t buffer_byte_count DUCKDB_GUARDED_BY(glock); - //! The readable chunks, in consumption order - queue read_queue DUCKDB_GUARDED_BY(glock); + //! The readable units, in consumption order + queue> read_queue DUCKDB_GUARDED_BY(glock); idx_t read_queue_byte_count DUCKDB_GUARDED_BY(glock); map blocked_sinks DUCKDB_GUARDED_BY(glock); + //! Counted by the streaming peak, never against the cap + idx_t parked_bytes DUCKDB_GUARDED_BY(glock) = 0; idx_t min_batch DUCKDB_GUARDED_BY(glock); //! Debug variable to verify that order is preserved correctly. idx_t lowest_moved_batch DUCKDB_GUARDED_BY(glock) = 0; //! The highest number of bytes ever buffered idx_t peak_buffered_bytes DUCKDB_GUARDED_BY(glock) = 0; - //! The largest single chunk seen so far - idx_t max_seen_chunk_bytes DUCKDB_GUARDED_BY(glock) = 0; + idx_t peak_streaming_bytes DUCKDB_GUARDED_BY(glock) = 0; + //! The largest single unit seen so far + idx_t max_seen_unit_bytes DUCKDB_GUARDED_BY(glock) = 0; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/buffered_data/buffered_data.hpp b/src/duckdb/src/include/duckdb/main/buffered_data/buffered_data.hpp index ece32aedd..ba6b149f6 100644 --- a/src/duckdb/src/include/duckdb/main/buffered_data/buffered_data.hpp +++ b/src/duckdb/src/include/duckdb/main/buffered_data/buffered_data.hpp @@ -16,33 +16,30 @@ #include "duckdb/common/types/data_chunk.hpp" #include "duckdb/common/optional_idx.hpp" #include "duckdb/execution/physical_operator_states.hpp" -#include "duckdb/common/enums/pending_execution_result.hpp" +#include "duckdb/common/enums/query_result_state.hpp" #include "duckdb/common/enums/result_lifetime.hpp" -#include "duckdb/common/enums/stream_execution_result.hpp" #include "duckdb/common/mutex.hpp" #include "duckdb/common/shared_ptr.hpp" #include "duckdb/common/thread_annotation.hpp" +#include "duckdb/main/result_format.hpp" +#include "duckdb/main/result_unit.hpp" namespace duckdb { -class StreamQueryResult; +class QueryResult; class ClientContextLock; -//! A blocked sink. Holds the InterruptState and owns the finished copy of the chunk it could not append. +//! A blocked sink. Holds the InterruptState and owns the finished unit it could not append. struct BlockedSink { public: InterruptState state; - idx_t pending_bytes; - unique_ptr pending_chunk; -}; + unique_ptr pending_unit; -//! One unread buffered chunk, in consumption order. The buffer owns the copy until it is popped, and the consumer -//! owns it afterwards. -struct BufferedChunk { public: - unique_ptr chunk; - //! The bytes this chunk counts against the cap - idx_t data_size; + //! The bytes the parked unit counts against the cap, zero once the unit has been deposited + idx_t PendingBytes() const { + return pending_unit ? pending_unit->byte_size : 0; + } }; class BufferedData { @@ -50,7 +47,9 @@ class BufferedData { enum class Type { SIMPLE, BATCHED }; public: - BufferedData(Type type, ClientContext &context, ResultLifetime lifetime); + //! Runs the format's InitGlobal on the constructing thread, before any worker sees it. A null format means chunks + BufferedData(Type type, ClientContext &context, ResultLifetime lifetime, ResultFormatContext format_context, + shared_ptr format); virtual ~BufferedData(); public: @@ -58,28 +57,56 @@ class BufferedData { ResultLifetime Lifetime() const { return lifetime; } - //! Settle the retention and wake the producers parked on it. The first decision stands + //! The first decision stands ResultLifetime Decide(ResultLifetime decision); + //! Fixed at construction; safe to read without synchronization + ResultFormat &Format() const { + D_ASSERT(format); + return *format; + } + //! Fixed at construction; safe to read without synchronization + ResultFormatGlobalState &FormatState() const { + D_ASSERT(format_state); + return *format_state; + } + const shared_ptr &SharedFormat() const { + return format; + } + const shared_ptr &SharedFormatState() const { + return format_state; + } + //! Fixed at construction; safe to read without synchronization + const ResultFormatContext &FormatContext() const { + return format_context; + } //! Choose draining, as every fetch-shaped call does. Throws when the result is being materialized void DecideDraining(); //! Park a producer until the retention is decided. False when it already is bool ParkUndecided(const InterruptState &blocked_sink); - //! Whether a producer is parked on this buffer, for a decision or for space. Only the consumer releases it - bool HasParkedProducer(); - //! Blocking call that executes tasks on the calling thread until a chunk is buffered or execution reaches a + //! Whether the engine waits on the consumer: a producer is parked for the retention decision, or for + //! space that only a pop frees + bool WaitsOnConsumer(); + //! Blocking call that executes tasks on the calling thread until a unit is buffered or execution reaches a //! terminal state. - StreamExecutionResult ReplenishBuffer(StreamQueryResult &result, ClientContextLock &context_lock); - //! One blocking replenish step: run executor tasks until a chunk is poppable - StreamExecutionResult ExecuteTaskInternal(StreamQueryResult &result, ClientContextLock &context_lock); - virtual unique_ptr Scan() = 0; + QueryResultState ReplenishBuffer(ClientContextLock &context_lock, QueryResult &result); + //! Lend the calling thread to production: settle draining, wake parked producers and run at most one + //! task slice. Reports whether a unit is poppable. Never waits + QueryResultState Participate(ClientContextLock &context_lock, QueryResult &result); + //! Reports whether a unit is poppable, else where execution stands. Runs no task, never waits + QueryResultState Poll(ClientContextLock &context_lock, QueryResult &result); + virtual unique_ptr Scan() = 0; virtual void UnblockSinks() = 0; shared_ptr GetContext() { return context.lock(); } - //! The highest number of bytes the buffer ever held. + //! The highest number of bytes the buffer ever queued. This is what the cap governs virtual idx_t PeakBufferedBytes() = 0; - //! Whether a producer is parked for space. A parked producer implies a poppable chunk. + //! Queued bytes plus the units parked producers hold, as system.peak_streaming_buffer_size reports + virtual idx_t PeakStreamingBytes() = 0; + //! Whether a producer is parked for space. virtual bool HasBlockedSink() = 0; + //! Whether a unit is ready for the consumer to pop. + virtual bool HasObservableUnit() = 0; //! Debug assert that no blocked sinks exist. virtual void AssertNoBlockedSinks() = 0; //! An owned, exactly-sized copy: the producer reuses its output chunk, and the copy's GetDataSize is what the @@ -114,8 +141,10 @@ class BufferedData { } protected: - static StreamExecutionResult MapExecutionResult(PendingExecutionResult execution_result); - //! Whether the blocking replenish can stop: a chunk is poppable, and the buffer will + //! Record on the result that it is no longer the connection's active query, and report it as an + //! error state + static QueryResultState Cancelled(QueryResult &result); + //! Whether the blocking replenish can stop: a unit is poppable, and the buffer will //! not accept more input right now virtual bool ReplenishSatisfied() = 0; //! Pops below this mark restart blocked sinks. The floor keeps it reachable for tiny buffers @@ -133,6 +162,10 @@ class BufferedData { atomic lifetime; //! Producers parked with their first chunk unconsumed, until the retention is decided vector undecided_sinks DUCKDB_GUARDED_BY(glock); + const ResultFormatContext format_context; + //! Set at construction and never changed afterwards, so workers read these without synchronization + shared_ptr format; + shared_ptr format_state; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/buffered_data/simple_buffered_data.hpp b/src/duckdb/src/include/duckdb/main/buffered_data/simple_buffered_data.hpp index 0a79d4eea..42cb1c29b 100644 --- a/src/duckdb/src/include/duckdb/main/buffered_data/simple_buffered_data.hpp +++ b/src/duckdb/src/include/duckdb/main/buffered_data/simple_buffered_data.hpp @@ -18,34 +18,37 @@ namespace duckdb { -class StreamQueryResult; +class QueryResult; class SimpleBufferedData : public BufferedData { public: static constexpr const BufferedData::Type TYPE = BufferedData::Type::SIMPLE; public: - SimpleBufferedData(ClientContext &context, ResultLifetime lifetime); + SimpleBufferedData(ClientContext &context, ResultLifetime lifetime, ResultFormatContext format_context, + shared_ptr format); ~SimpleBufferedData() override; public: - //! Buffer a copy of the chunk, or block the sink when the chunk does not fit. - bool AppendOrBlock(DataChunk &chunk, const InterruptState &blocked_sink); - //! Whether the buffer is saturated, t.e. can't accept a new chunk right now. + //! Buffer the finished unit, or block the sink when the unit does not fit. + bool AppendOrBlock(unique_ptr unit, const InterruptState &blocked_sink); + //! Whether the buffer is saturated, t.e. can't accept a new unit right now. bool BufferSaturated(); - //! The highest number of bytes the buffer ever held. + //! The highest number of bytes the buffer ever queued. idx_t PeakBufferedBytes() override; + idx_t PeakStreamingBytes() override; bool HasBlockedSink() override; + bool HasObservableUnit() override; void UnblockSinks() override; void AssertNoBlockedSinks() override; - unique_ptr Scan() override; + unique_ptr Scan() override; inline idx_t BufferSize() const { return buffer_size; } protected: //! The buffer will not accept more input, and blocking requires a non-empty buffer, - //! so a saturated buffer always holds a poppable chunk + //! so a saturated buffer always holds a poppable unit bool ReplenishSatisfied() override { return BufferSaturated(); } @@ -60,10 +63,13 @@ class SimpleBufferedData : public BufferedData { private: //! Our handles to reschedule the blocked sink tasks queue blocked_sinks DUCKDB_GUARDED_BY(glock); - //! The unread chunks, in arrival order. The buffer owns each copy until its pop - queue unread_chunks DUCKDB_GUARDED_BY(glock); + //! The unread units, in arrival order. The buffer owns each unit until its pop + queue> unread_units DUCKDB_GUARDED_BY(glock); //! The bytes currently buffered atomic buffered_count; + //! Counted by the streaming peak, never against the cap + idx_t parked_bytes DUCKDB_GUARDED_BY(glock) = 0; + idx_t peak_streaming_bytes DUCKDB_GUARDED_BY(glock) = 0; //! The byte cap of the buffer const idx_t buffer_size; //! The highest number of bytes ever buffered diff --git a/src/duckdb/src/include/duckdb/main/capi/capi_function_signature.hpp b/src/duckdb/src/include/duckdb/main/capi/capi_function_signature.hpp new file mode 100644 index 000000000..74dcaee5b --- /dev/null +++ b/src/duckdb/src/include/duckdb/main/capi/capi_function_signature.hpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/main/capi/capi_function_signature.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/string_util.hpp" +#include "duckdb/function/function.hpp" + +#include + +namespace duckdb { + +//! The C APIs let a caller declare parameters and varargs in any order, while a signature expects its parameters +//! ordered by kind. These rebuild the signature in that order. +struct CAPIFunctionSignature { + //! Adds the parameter after the existing ones of its kind + static void AddParameter(FunctionSignature &signature, FunctionParameter parameter) { + auto parameters = signature.GetParameters(); + parameters.push_back(std::move(parameter)); + SetParameters(signature, std::move(parameters)); + } + + //! Adds a positional-only parameter named "col", after the existing positional-only ones + static void AddPositionalOnly(FunctionSignature &signature, LogicalType type) { + auto name = Identifier(StringUtil::Format("col%d", signature.GetPositionalOnlyParameterCount())); + AddParameter(signature, + FunctionParameter(std::move(name), std::move(type), {}, FunctionParameterKind::POSITIONAL_ONLY)); + } + + //! Replaces the "*args" by one of the given type, or removes it if the type is INVALID. A named argument the + //! signature does not declare stays an error, as the callback could not tell its name + static void SetArgs(FunctionSignature &signature, const LogicalType &type) { + SetVariadic(signature, type, false); + } + + //! The same, adding a "**kwargs" of the type too - a named argument the signature does not declare is received + //! as a trailing value, as v1 functions always received it + static void SetVarArgs(FunctionSignature &signature, const LogicalType &type) { + SetVariadic(signature, type, true); + } + +private: + static void SetVariadic(FunctionSignature &signature, const LogicalType &type, bool with_kwargs) { + vector parameters; + for (auto ¶m : signature.GetParameters()) { + if (!param.IsVariadic()) { + parameters.push_back(param); + } + } + if (type.id() != LogicalTypeId::INVALID) { + parameters.emplace_back("args", type, optional(), FunctionParameterKind::VAR_POSITIONAL); + if (with_kwargs) { + parameters.emplace_back("kwargs", type, optional(), FunctionParameterKind::VAR_KEYWORD); + } + } + SetParameters(signature, std::move(parameters)); + } + + static void SetParameters(FunctionSignature &signature, vector parameters) { + // the kinds are declared in the order a signature expects them + std::stable_sort( + parameters.begin(), parameters.end(), + [](const FunctionParameter &lhs, const FunctionParameter &rhs) { return lhs.GetKind() < rhs.GetKind(); }); + // a typed "**kwargs" keeps its schema + auto typed_kwargs = signature.GetTypedKwargs(); + optional kwargs_name; + if (typed_kwargs && !parameters.empty() && parameters.back().GetKind() == FunctionParameterKind::VAR_KEYWORD) { + kwargs_name = parameters.back().GetName(); + parameters.pop_back(); + } + FunctionSignature result(std::move(parameters), signature.GetReturnType()); + if (kwargs_name) { + result.AddTypedKwargs(*kwargs_name, *typed_kwargs); + } + signature = std::move(result); + } +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/capi/capi_internal.hpp b/src/duckdb/src/include/duckdb/main/capi/capi_internal.hpp index 2434b9c41..51682358a 100644 --- a/src/duckdb/src/include/duckdb/main/capi/capi_internal.hpp +++ b/src/duckdb/src/include/duckdb/main/capi/capi_internal.hpp @@ -62,13 +62,12 @@ struct ExtractStatementsWrapper { }; struct PendingStatementWrapper { - unique_ptr statement; + unique_ptr statement; bool allow_streaming; }; struct ArrowResultWrapper { - unique_ptr result; - unique_ptr current_chunk; + unique_ptr result; }; struct AppenderWrapper { @@ -97,12 +96,55 @@ enum class CAPIResultSetType : uint8_t { CAPI_RESULT_TYPE_DEPRECATED }; +//! Either a query handle or a stream opened from one. Only the streaming entry points care which; +//! everything else reads the metadata and the error, which both carry struct DuckDBResultData { - //! The underlying query result + //! The query handle, or null once a stream was opened from it unique_ptr result; + //! The stream opened from the handle (may be null) + unique_ptr> stream; // Results can only use either the new API or the old API, not a mix of the two // They start off as "none" and switch to one or the other when an API method is used CAPIResultSetType result_set_type; + + bool IsStreaming() const { + return stream != nullptr; + } + //! The retained result. Only valid when the result is not streaming + QueryResult &Retained() const { + D_ASSERT(result); + return *result; + } + bool HasError() const { + return stream ? stream->HasError() : result->HasError(); + } + const string &GetError() const { + return stream ? stream->GetError() : result->GetError(); + } + const ExceptionType &GetErrorType() const { + return stream ? stream->GetErrorType() : result->GetErrorType(); + } + idx_t ColumnCount() const { + return stream ? stream->ColumnCount() : result->ColumnCount(); + } + const vector &GetTypes() const { + return stream ? stream->GetTypes() : result->GetTypes(); + } + const Identifier &ColumnName(idx_t index) const { + return stream ? stream->ColumnName(index) : result->ColumnName(index); + } + StatementType GetStatementType() const { + return stream ? stream->GetStatementType() : result->GetStatementType(); + } + const StatementProperties &GetStatementProperties() const { + return stream ? stream->GetStatementProperties() : result->GetStatementProperties(); + } + ClientProperties &GetClientProperties() { + return stream ? stream->GetClientProperties() : result->client_properties; + } + unique_ptr Fetch() { + return stream ? stream->Fetch() : result->Fetch(); + } }; duckdb_type LogicalTypeIdToC(const LogicalTypeId type); @@ -113,6 +155,7 @@ duckdb_error_type ErrorTypeToC(const ExceptionType type); ExceptionType ErrorTypeFromC(const duckdb_error_type type); duckdb_state DuckDBTranslateResult(unique_ptr result, duckdb_result *out); +duckdb_state DuckDBTranslateStreamResult(unique_ptr> stream, duckdb_result *out); bool DeprecatedMaterializeResult(duckdb_result *result); } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/capi/capi_internal_table.hpp b/src/duckdb/src/include/duckdb/main/capi/capi_internal_table.hpp index 6384f2ba6..bcd57340f 100644 --- a/src/duckdb/src/include/duckdb/main/capi/capi_internal_table.hpp +++ b/src/duckdb/src/include/duckdb/main/capi/capi_internal_table.hpp @@ -56,7 +56,7 @@ struct CTableBindData : public TableFunctionData { struct CTableInternalBindInfo { CTableInternalBindInfo(ClientContext &context, const vector ¶meters, - const named_parameter_map_t &named_parameters, vector &return_types, + const named_argument_map_t &named_parameters, vector &return_types, vector &names, CTableBindData &bind_data, CTableFunctionInfo &function_info) : context(context), parameters(parameters), named_parameters(named_parameters), return_types(return_types), names(names), bind_data(bind_data), function_info(function_info), success(true) { @@ -65,7 +65,7 @@ struct CTableInternalBindInfo { ClientContext &context; vector parameters; - named_parameter_map_t named_parameters; + named_argument_map_t named_parameters; vector &return_types; vector &names; diff --git a/src/duckdb/src/include/duckdb/main/capi_v2/capi_v2_function_internal.hpp b/src/duckdb/src/include/duckdb/main/capi_v2/capi_v2_function_internal.hpp new file mode 100644 index 000000000..4deccc93f --- /dev/null +++ b/src/duckdb/src/include/duckdb/main/capi_v2/capi_v2_function_internal.hpp @@ -0,0 +1,144 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/main/capi_v2/capi_v2_function_internal.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/main/capi_v2/capi_v2_internal.hpp" +#include "duckdb/common/named_parameter_map.hpp" +#include "duckdb/function/function.hpp" +#include "duckdb/function/table_function.hpp" +#include "duckdb/parser/parsed_data/create_function_info.hpp" + +namespace duckdb::capiv2 { + +//! An example of calling a function, with an optional title. +struct CV2FunctionExample { + string title; + string example; +}; + +//! The documentation of one overload, as `duckdb_functions()` reports it. +class CV2FunctionDocs { +public: + //! Adds this overload's description to `info`, matched to the overload by its parameter types; the parameter names + //! come from the signature itself. Adds nothing when no documentation was set. + void AddTo(const FunctionSignature &signature, CreateFunctionInfo &info) const { + vector parameter_types; + for (idx_t i = 0; i < signature.GetParameterCount(); i++) { + auto ¶meter = signature.GetParameter(i); + if (parameter.IsVariadic()) { + continue; + } + parameter_types.push_back(parameter.GetType()); + } + AddTo(std::move(parameter_types), info); + } + //! As above, matched to the overload by the given parameter types + void AddTo(vector parameter_types, CreateFunctionInfo &info) const { + if (description.empty() && examples.empty() && categories.empty()) { + return; + } + FunctionDescription result; + result.parameter_types = std::move(parameter_types); + result.description = description; + for (auto &entry : examples) { + result.examples.push_back(entry.example); + } + result.categories = categories; + info.descriptions.push_back(std::move(result)); + } + +public: + string description; + vector examples; + vector categories; +}; + +inline auto Convert(duckdb_v2_function_docs_handle docs) -> CV2FunctionDocs * { + return reinterpret_cast(docs); +} + +inline auto Convert(CV2FunctionDocs *docs) -> duckdb_v2_function_docs_handle { + return reinterpret_cast(docs); +} + +//! The part of a bind callback every function family shares: the arguments of the call site, the user data and the +//! bind data. The arguments are one list: the positional arguments, then the named arguments +class CV2FunctionBindInfo { +public: + template + CV2FunctionBindInfo(const FUNC &function, void *user_data) + : CV2FunctionBindInfo(function, function.GetDefinition()->GetSignature(), user_data) { + } + CV2FunctionBindInfo(const BoundSimpleFunction &function, const FunctionSignature &signature, void *user_data) + : in_user_data(user_data), function(function), signature(signature) { + } + virtual ~CV2FunctionBindInfo() = default; + + virtual LogicalType GetArgType(idx_t index) const = 0; + virtual Value GetArgValue(idx_t index) const = 0; + + idx_t GetArgCount() const { + return function.GetPositionalArgumentCount() + function.GetNamedArguments().size(); + } + //! The parameter name for a declared parameter, the name the caller passed for "**kwargs", empty for "*args" + const Identifier &GetArgName(idx_t index) const; + //! Looks up an argument by a name a caller can pass it by, so positional-only parameters are skipped + optional_idx FindArg(const Identifier &name) const; + +public: + void *in_user_data; + //! Why the bind data cannot be set through this handle, empty when it can + string bind_data_error; + //! The bind data the callback set, or nullptr + shared_ptr out_bind_data; + + const BoundSimpleFunction &function; + const FunctionSignature &signature; +}; + +//! The arguments of a scalar or aggregate call: the bound expressions +class CV2ExpressionBindInfo final : public CV2FunctionBindInfo { +public: + template + CV2ExpressionBindInfo(const FUNC &function, void *user_data, const BindFunctionInput &input) + : CV2FunctionBindInfo(function, user_data), input(input) { + D_ASSERT(GetArgCount() == input.GetArguments().size()); + } + + LogicalType GetArgType(idx_t index) const override; + Value GetArgValue(idx_t index) const override; + +private: + const BindFunctionInput &input; +}; + +//! The arguments of a table or pragma function call: constants, the positional arguments and the named ones held apart +class CV2ConstantBindInfo final : public CV2FunctionBindInfo { +public: + CV2ConstantBindInfo(const BoundSimpleFunction &function, const FunctionSignature &signature, void *user_data, + const vector &positional, const named_argument_map_t &named); + + LogicalType GetArgType(idx_t index) const override; + Value GetArgValue(idx_t index) const override; + +private: + const Value &GetArg(idx_t index) const; + + const vector &positional; + const named_argument_map_t &named; +}; + +inline auto Convert(duckdb_v2_function_bind_info_handle info) -> CV2FunctionBindInfo * { + return reinterpret_cast(info); +} +inline auto Convert(CV2FunctionBindInfo *info) -> duckdb_v2_function_bind_info_handle { + return reinterpret_cast(info); +} + +} // namespace duckdb::capiv2 diff --git a/src/duckdb/src/include/duckdb/main/capi_v2/capi_v2_internal.hpp b/src/duckdb/src/include/duckdb/main/capi_v2/capi_v2_internal.hpp index abb10bcaa..eedc6ce06 100644 --- a/src/duckdb/src/include/duckdb/main/capi_v2/capi_v2_internal.hpp +++ b/src/duckdb/src/include/duckdb/main/capi_v2/capi_v2_internal.hpp @@ -21,8 +21,6 @@ #include "duckdb/main/client_context_state.hpp" #include "duckdb/main/extension/extension_loader.hpp" #include "duckdb/main/parse_iterator.hpp" -#include "duckdb/main/pending_query_result.hpp" -#include "duckdb/main/stream_query_result.hpp" #include "duckdb/main/table_description.hpp" #include "duckdb/parser/qualified_name.hpp" #include "duckdb/parser/sql_statement.hpp" @@ -34,7 +32,13 @@ #include "duckdb/main/setting_info.hpp" #include "duckdb/execution/operator/helper/physical_set.hpp" #include "duckdb/main/database.hpp" +#include "duckdb/common/query_context.hpp" +#include "duckdb/common/file_system.hpp" +// The engine implements the whole V2 C API, including the unstable surface +#ifndef DUCKDB_V2_API_ALLOW_UNSTABLE +#define DUCKDB_V2_API_ALLOW_UNSTABLE 1 +#endif // V2 C API header -- all types use duckdb_v2_ prefix, no collision with V1. #include "duckdb_v2.h" @@ -76,6 +80,22 @@ inline auto Convert(duckdb_v2_str str) -> std::string_view { } return std::string_view(str.ptr, str.len); } +inline auto Convert(const duckdb_v2_str *str) -> std::string_view { + if (!str) { + throw InvalidInputException("byte range cannot be null"); + } + return Convert(*str); +} + +// Validates identifier UTF-8 and returns its text; throws on invalid input. Defined in capi_v2_utf8.cpp. +auto ConvertIdentifierName(duckdb_v2_identifier_t name) -> std::string_view; +inline auto ConvertIdentifierName(const duckdb_v2_identifier_t *name) -> std::string_view { + if (!name) { + throw InvalidInputException("identifier cannot be null"); + } + return ConvertIdentifierName(*name); +} + inline auto Convert(std::string_view str) -> duckdb_v2_str { return duckdb_v2_str {str.data(), str.size()}; } @@ -91,6 +111,18 @@ inline auto Convert(duckdb_v2_hugeint_t value) -> hugeint_t { inline auto Convert(duckdb_v2_uhugeint_t value) -> uhugeint_t { return uhugeint_t(value.upper, value.lower); } +inline auto Convert(const duckdb_v2_hugeint_t *value) -> hugeint_t { + if (!value) { + throw InvalidInputException("hugeint value cannot be null"); + } + return Convert(*value); +} +inline auto Convert(const duckdb_v2_uhugeint_t *value) -> uhugeint_t { + if (!value) { + throw InvalidInputException("uhugeint value cannot be null"); + } + return Convert(*value); +} inline auto Convert(hugeint_t value) -> duckdb_v2_hugeint_t { return duckdb_v2_hugeint_t {value.lower, value.upper}; } @@ -107,9 +139,15 @@ inline auto Convert(duckdb_v2_interval_t value) -> interval_t { out.micros = value.micros; return out; } +inline auto Convert(const duckdb_v2_interval_t *value) -> interval_t { + if (!value) { + throw InvalidInputException("interval value cannot be null"); + } + return Convert(*value); +} -// The V2 enum surfaces core's StatementType under the same numeric values; every spec member is pinned. Core has no -// count sentinel, so a member appended in core is caught by the test over the values past the last spec member. +// The V2 enum surfaces core's StatementType under the same numeric values; every spec member is pinned, and the count +// pins the highest one - appending a member in core fails to compile until the v2 spec mirrors it. #define DUCKDB_V2_ASSERT_STATEMENT_TYPE(member) \ static_assert(static_cast(StatementType::member##_STATEMENT) == DUCKDB_V2_STATEMENT_TYPE_##member, \ "StatementType::" #member "_STATEMENT must mirror DUCKDB_V2_STATEMENT_TYPE_" #member) @@ -147,7 +185,10 @@ DUCKDB_V2_ASSERT_STATEMENT_TYPE(MERGE_INTO); DUCKDB_V2_ASSERT_STATEMENT_TYPE(CONNECT); DUCKDB_V2_ASSERT_STATEMENT_TYPE(DISCONNECT); DUCKDB_V2_ASSERT_STATEMENT_TYPE(EXTERNAL_RESOURCE); +DUCKDB_V2_ASSERT_STATEMENT_TYPE(PASSTHROUGH); #undef DUCKDB_V2_ASSERT_STATEMENT_TYPE +static_assert(static_cast(StatementType::ENUM_SIZE) == DUCKDB_V2_STATEMENT_TYPE_PASSTHROUGH + 1, + "a StatementType was added: give it a DUCKDB_V2_STATEMENT_TYPE id in the v2 spec and pin it above"); inline auto Convert(StatementType type) -> DUCKDB_V2_STATEMENT_TYPE { return static_cast(type); } @@ -159,7 +200,7 @@ inline auto Convert(StatementType type) -> DUCKDB_V2_STATEMENT_TYPE { class CV2Environment { public: unique_ptr cache; - std::atomic open_database_count {0}; + std::atomic instance_count {0}; }; inline auto Convert(CV2Environment *env) -> duckdb_v2_environment_handle { @@ -170,26 +211,264 @@ inline auto Convert(duckdb_v2_environment_handle env) -> CV2Environment * { return reinterpret_cast(env); } -class CV2Database { +class CV2Option; +class CV2Instance; +class CV2OptionSource; + +//! A config handle: the scope config options are read from and written at. +class CV2Config { +public: + virtual ~CV2Config() = default; + + //! The effective value of `name`, and the scope DuckDB attributes it to. Throws for an unknown option. + virtual DUCKDB_V2_SETTING_SCOPE ReadValue(std::string_view name, Value &result) = 0; + //! Writes `name` at `scope`, which the option must support. + virtual void Write(const Identifier &name, const Value &value, DUCKDB_V2_SETTING_SCOPE scope) = 0; + //! Where option descriptors are read from. The same from every config of one instance. + virtual unique_ptr GetOption(std::string_view name) = 0; + virtual unique_ptr GetOptionByIndex(idx_t index) = 0; + virtual idx_t GetOptionCount() = 0; +}; + +inline auto Convert(duckdb_v2_config_handle config) -> CV2Config * { + return reinterpret_cast(config); +} + +inline auto Convert(CV2Config *config) -> duckdb_v2_config_handle { + return reinterpret_cast(config); +} + +//! An instance's config: GLOBAL only. +class CV2InstanceConfig final : public CV2Config { public: - CV2Database(CV2Environment &env, shared_ptr database) : env(env), database(std::move(database)) { - internal_connection = make_uniq(*this->database); + explicit CV2InstanceConfig(CV2Instance &instance) : instance(instance) { } - CV2Environment &env; + DUCKDB_V2_SETTING_SCOPE ReadValue(std::string_view name, Value &result) override; + void Write(const Identifier &name, const Value &value, DUCKDB_V2_SETTING_SCOPE scope) override; + unique_ptr GetOption(std::string_view name) override; + unique_ptr GetOptionByIndex(idx_t index) override; + idx_t GetOptionCount() override; + +private: + CV2Instance &instance; +}; + +//! A factory handle: the scope values, types and data chunks are created in. +class CV2Factory { +public: + virtual ~CV2Factory() = default; + + //! The database whose allocator and buffer manager back what is created. + virtual DatabaseInstance &GetDatabase() = 0; + //! The client whose catalog and settings apply, or nullptr when only the built-ins do. + virtual optional_ptr TryGetClientContext() = 0; + //! Runs `fn` with a transaction active on the client, for catalog lookups. + virtual void WithTransaction(const std::function &fn) = 0; +}; + +inline auto Convert(duckdb_v2_factory_handle factory) -> CV2Factory * { + return reinterpret_cast(factory); +} + +inline auto Convert(CV2Factory *factory) -> duckdb_v2_factory_handle { + return reinterpret_cast(factory); +} + +//! An instance's factory: built-in types and casts only, no catalog. +class CV2InstanceFactory final : public CV2Factory { +public: + explicit CV2InstanceFactory(CV2Instance &instance) : instance(instance) { + } + + DatabaseInstance &GetDatabase() override; + optional_ptr TryGetClientContext() override { + return nullptr; + } + void WithTransaction(const std::function &fn) override; + +private: + CV2Instance &instance; +}; + +//! The common type of a non-empty set of types, folded left to right in the factory's scope. Throws +//! NotImplementedException when there is none. Defined in capi_v2_logical_type.cpp. +auto ResolveCommonType(CV2Factory &factory, const vector &types) -> LogicalType; + +//! The SQL ATTACH `(KEY value)` options of one attach, as the text values a quoted literal produces. Bound to the +//! instance handle it was created from, which is what future per-instance resources (an allocator, say) would be +//! taken from. +class CV2AttachOptions { +public: + explicit CV2AttachOptions(CV2Instance &instance) : instance(instance) { + } + + CV2Instance &instance; + unordered_map options; +}; + +inline auto Convert(duckdb_v2_attach_options_handle options) -> CV2AttachOptions * { + return reinterpret_cast(options); +} + +inline auto Convert(CV2AttachOptions *options) -> duckdb_v2_attach_options_handle { + return reinterpret_cast(options); +} + +//! Throws unless `target` is the database `what` was created for, through its factory. +inline void CheckRegistrationTarget(const DatabaseInstance &created_for, const DatabaseInstance &target, + const char *what) { + if (&created_for != &target) { + throw InvalidInputException("the %s was created through a factory of a different database", what); + } +} + +//! A file system handle: the file system plus the query its reads and writes are attributed to, if any. +class CV2FileSystem { +public: + CV2FileSystem(FileSystem &fs, QueryContext query) : fs(fs), query(query) { + } + + FileSystem &fs; + QueryContext query; +}; + +//! An instance handle: a DuckDB instance, built from its startup options when the handle is created. +class CV2Instance { +public: + CV2Instance(CV2Environment &env, DBConfig &startup_config); + + //! Attaches the database at `path` under `name` (derived from the path when empty), like ATTACH, optionally as + //! the default for new connections. + void Attach(const string &path, const Identifier &name, optional_ptr options, + bool make_default); + //! Detaches the database attached under `name`. + void Detach(const Identifier &name); + //! Makes the database attached under `name` the default for new connections. + void SetDefault(const Identifier &name); + DuckDB &GetDatabase() { + return *database; + } + +private: + //! Declared first: the members below are built from it. shared_ptr database; - unique_ptr internal_connection; + +public: + CV2Environment &env; + CV2InstanceFactory factory; + CV2InstanceConfig config_handle; + CV2FileSystem file_system; +}; + +inline auto Convert(duckdb_v2_instance_handle instance) -> CV2Instance * { + return reinterpret_cast(instance); +} + +inline auto Convert(CV2Instance *instance) -> duckdb_v2_instance_handle { + return reinterpret_cast(instance); +} + +class CV2Context; + +//! A client's config: the connection's cascade (SESSION, then GLOBAL, then the default), written like SQL `SET`. +class CV2ClientConfig final : public CV2Config { +public: + explicit CV2ClientConfig(ClientContext &context) : context(context) { + } + + DUCKDB_V2_SETTING_SCOPE ReadValue(std::string_view name, Value &result) override; + void Write(const Identifier &name, const Value &value, DUCKDB_V2_SETTING_SCOPE scope) override; + unique_ptr GetOption(std::string_view name) override; + unique_ptr GetOptionByIndex(idx_t index) override; + idx_t GetOptionCount() override; + +private: + ClientContext &context; +}; + +//! A context's factory: the context's catalog and settings. +class CV2ContextFactory final : public CV2Factory { +public: + explicit CV2ContextFactory(CV2Context &context) : context(context) { + } + + DatabaseInstance &GetDatabase() override; + optional_ptr TryGetClientContext() override; + void WithTransaction(const std::function &fn) override; + +private: + CV2Context &context; }; -inline auto Convert(duckdb_v2_database_handle db) -> CV2Database * { - return reinterpret_cast(db); +//! A context handle: the client context plus how a call through it obtains a transaction. +class CV2Context { +public: + explicit CV2Context(ClientContext &context) + : context(context), factory(*this), config_handle(context), + file_system(FileSystem::GetFileSystem(context), QueryContext(context)) { + } + virtual ~CV2Context() = default; + + //! Runs `fn` with a transaction active on the context. + virtual void WithContext(const std::function &fn) = 0; + + ClientContext &context; + CV2ContextFactory factory; + CV2ClientConfig config_handle; + CV2FileSystem file_system; +}; + +inline DatabaseInstance &CV2ContextFactory::GetDatabase() { + return DatabaseInstance::GetDatabase(context.context); } -inline auto Convert(CV2Database *db) -> duckdb_v2_database_handle { - return reinterpret_cast(db); +inline optional_ptr CV2ContextFactory::TryGetClientContext() { + return &context.context; } -using CV2Connection = duckdb::Connection; +inline void CV2ContextFactory::WithTransaction(const std::function &fn) { + context.WithContext(fn); +} + +//! A context handed to a callback: the context lock is held and a transaction is already active. +class CV2CallbackContext final : public CV2Context { +public: + explicit CV2CallbackContext(ClientContext &context) : CV2Context(context) { + } + + void WithContext(const std::function &fn) override { + fn(context); + } +}; + +//! A context taken from a connection: each call joins the connection's transaction, or runs in one of its own. +class CV2ConnectionContext final : public CV2Context { +public: + explicit CV2ConnectionContext(ClientContext &context) : CV2Context(context) { + } + + void WithContext(const std::function &fn) override { + context.RunFunctionInTransaction([&]() { fn(context); }); + } +}; + +inline auto Convert(duckdb_v2_context_handle ctx) -> CV2Context * { + return reinterpret_cast(ctx); +} + +inline auto Convert(CV2Context *ctx) -> duckdb_v2_context_handle { + return reinterpret_cast(ctx); +} + +//! A connection handle: the connection plus the context handle it lends out. +class CV2Connection : public Connection { +public: + explicit CV2Connection(DuckDB &database) : Connection(database), context_handle(*context) { + } + + CV2ConnectionContext context_handle; +}; inline auto Convert(duckdb_v2_connection_handle conn) -> CV2Connection * { return reinterpret_cast(conn); @@ -208,16 +487,6 @@ inline auto Convert(CV2SQLStatement *stmt) -> duckdb_v2_sql_statement_handle { return reinterpret_cast(stmt); } -using CV2Context = duckdb::ClientContext; - -inline auto Convert(duckdb_v2_context_handle ctx) -> CV2Context * { - return reinterpret_cast(ctx); -} - -inline auto Convert(CV2Context *ctx) -> duckdb_v2_context_handle { - return reinterpret_cast(ctx); -} - //! The extension handle's backing struct is the load state in capi_v2_extension.cpp, not an ExtensionLoader, so it //! cannot be Convert'ed with a cast: the loader is resolved through the state instead. Valid only while the //! extension's entrypoint is running. @@ -240,19 +509,53 @@ inline auto Convert(CV2FunctionSignature *func) -> duckdb_v2_function_signature_ return reinterpret_cast(func); } +//! Where an option's current value is read from: a client context's cascade (SESSION -> GLOBAL -> default). +class CV2OptionSource { +public: + explicit CV2OptionSource(ClientContext &context) : context(&context), config(DBConfig::GetConfig(context)) { + } + //! GLOBAL -> default only. + explicit CV2OptionSource(DatabaseInstance &db) : config(DBConfig::GetConfig(db)) { + } + + const DBConfig &GetConfig() const { + return config; + } + //! The effective value of `name`, and the scope DuckDB attributes it to. Throws for an unknown option. + DUCKDB_V2_SETTING_SCOPE ReadValue(std::string_view name, Value &result) const; + +private: + optional_ptr context; + const DBConfig &config; +}; + class CV2Option { public: Identifier name; - string setting; - string default_setting; + Value default_value; string description; - DUCKDB_V2_OPTION_TARGET_SCOPE target_scope = DUCKDB_V2_OPTION_TARGET_SCOPE_UNKNOWN; + bool supports_global = false; + bool supports_session = false; + DUCKDB_V2_SETTING_SCOPE default_scope = DUCKDB_V2_SETTING_SCOPE_GLOBAL; vector aliases; - static unique_ptr FromIndex(ClientContext &context, DBConfig &config, idx_t index); - static unique_ptr FromName(ClientContext &context, DBConfig &config, std::string_view name); + static unique_ptr FromIndex(const CV2OptionSource &source, idx_t index); + static unique_ptr FromName(const CV2OptionSource &source, std::string_view name); + static idx_t Count(const CV2OptionSource &source); }; +//! The engine's write scope for a V2 scope. +inline SetScope ConvertSetScope(DUCKDB_V2_SETTING_SCOPE scope) { + switch (scope) { + case DUCKDB_V2_SETTING_SCOPE_GLOBAL: + return SetScope::GLOBAL; + case DUCKDB_V2_SETTING_SCOPE_SESSION: + return SetScope::SESSION; + default: + return SetScope::AUTOMATIC; + } +} + inline auto Convert(duckdb_v2_option_handle opt) -> CV2Option * { return reinterpret_cast(opt); } @@ -263,11 +566,52 @@ inline auto Convert(CV2Option *opt) -> duckdb_v2_option_handle { using CV2LogicalType = duckdb::LogicalType; -inline auto Convert(duckdb_v2_logical_type_handle opt) -> CV2LogicalType * { - return reinterpret_cast(opt); +//! A logical type handle is the LogicalTypeInfo of the type. An owned handle holds one reference to it; a borrowed +//! handle holds none and stays valid for as long as the type it was taken from. + +//! Views the type behind a handle without touching its reference count +class CV2LogicalTypeRef { +public: + explicit CV2LogicalTypeRef(duckdb_v2_logical_type_handle handle) + : type(CV2LogicalType::AdoptTypeInfo(*reinterpret_cast(handle))) { + } + ~CV2LogicalTypeRef() { + std::move(type).ReleaseTypeInfo(); + } + CV2LogicalTypeRef(const CV2LogicalTypeRef &) = delete; + CV2LogicalTypeRef &operator=(const CV2LogicalTypeRef &) = delete; + + //! Only on a named view - a reference into a temporary view would dangle + const CV2LogicalType &operator*() const & { + return type; + } + const CV2LogicalType &operator*() const && = delete; + const CV2LogicalType *operator->() const { + return &type; + } + +private: + CV2LogicalType type; +}; + +inline auto Convert(duckdb_v2_logical_type_handle handle) -> CV2LogicalTypeRef { + return CV2LogicalTypeRef(handle); +} +inline auto ConvertTypeInfo(const duckdb::LogicalTypeInfo &type_info) -> duckdb_v2_logical_type_handle { + // the handle is opaque - the type info is only ever read through it + return reinterpret_cast(reinterpret_cast(&type_info)); +} +//! Creates an owned handle, transferring the reference of "type" to it +inline auto Convert(CV2LogicalType type) -> duckdb_v2_logical_type_handle { + return ConvertTypeInfo(std::move(type).ReleaseTypeInfo()); } -inline auto Convert(CV2LogicalType *opt) -> duckdb_v2_logical_type_handle { - return reinterpret_cast(opt); +//! Creates a borrowed handle to "type" +inline auto ConvertBorrowed(const CV2LogicalType &type) -> duckdb_v2_logical_type_handle { + return ConvertTypeInfo(type.GetTypeInfo()); +} +//! Takes back the reference held by an owned handle +inline auto TakeOwnership(duckdb_v2_logical_type_handle handle) -> CV2LogicalType { + return CV2LogicalType::AdoptTypeInfo(*reinterpret_cast(handle)); } using CV2QualifiedName = duckdb::QualifiedName; @@ -297,6 +641,15 @@ inline auto Convert(CV2ColumnDescription *column) -> duckdb_v2_column_descriptio return reinterpret_cast(column); } +using CV2ColumnDataCollection = duckdb::ColumnDataCollection; + +inline auto Convert(duckdb_v2_column_data_collection_handle cdc) -> CV2ColumnDataCollection * { + return reinterpret_cast(cdc); +} +inline auto Convert(CV2ColumnDataCollection *cdc) -> duckdb_v2_column_data_collection_handle { + return reinterpret_cast(cdc); +} + using CV2Value = duckdb::Value; inline auto Convert(duckdb_v2_value_handle val) -> CV2Value * { @@ -361,20 +714,29 @@ inline auto Convert(CV2Schema *schema) -> duckdb_v2_schema_handle { auto NullArgumentError(duckdb_v2_error_info_handle *err, const char *function, const char *argument) noexcept -> DUCKDB_V2_ERROR; +//! Raised for metadata asked for before execution bound the statement that produces it. Reported as +//! ERROR_INPUT_METADATA_DEFERRED +class MetadataDeferredException : public InvalidInputException { +public: + explicit MetadataDeferredException(const string &msg) : InvalidInputException(msg) { + } +}; + // Classify the exception currently being handled into a V2 error code and detail strings. Must be called from inside // a catch block. Never throws: if rendering the detail itself fails, it degrades to a bare code with empty detail // (RESOURCE_OUT_OF_MEMORY on allocation failure). Defined in capi_v2.cpp. -auto RenderCaughtError(DUCKDB_V2_ERROR &code, string &text, string &raw_message) noexcept -> void; +auto RenderCaughtError(DUCKDB_V2_ERROR &code, string &text, optional &raw_message) noexcept -> void; // The null test behind DUCKDB_CHECK_ARG: a pointer/handle is invalid when null; a string/identifier view is invalid -// when its pointer is null while it carries a non-zero length. +// when it is null, or when its pointer is null while it carries a non-zero length. template bool IsNullArgument(const T &arg) { - if constexpr (std::is_pointer_v) { - return arg == nullptr; - } else { - return !arg.ptr && arg.len > 0; - } + static_assert(std::is_pointer_v, "DUCKDB_CHECK_ARG takes a pointer or handle"); + return arg == nullptr; +} + +inline bool IsNullArgument(const duckdb_v2_str *arg) { + return !arg || (!arg->ptr && arg->len > 0); } // Check if an argument is null and return DUCKDB_V2_ERROR_INPUT_INVALID with a message if it is. @@ -402,7 +764,7 @@ struct CV2ErrorInfo { // rendered form (caret block, or JSON under errors_as_json); empty for a // directly-set message. Both written on the error path (WithErrorHandler). string message; - string raw_message; + optional raw_message; bool HasError() const { return code != DUCKDB_V2_ERROR_NONE; @@ -437,7 +799,7 @@ template DUCKDB_V2_ERROR WithErrorHandler(duckdb_v2_error_info_handle *err, T callback) noexcept { auto code = static_cast(DUCKDB_V2_ERROR_NONE); auto text = string(); - auto raw_message = string(); + optional raw_message; try { // Invoke the callback @@ -480,13 +842,11 @@ DUCKDB_V2_ERROR WithErrorHandler(duckdb_v2_error_info_handle *err, T callback) n //---------------------------------------------------------------------------------------------------------------------- struct ConnectionBusySlotV2 : public ClientContextState { - std::atomic owner {nullptr}; - // True once the consumer called `connection_interrupt` for the active result. - // This flag is how we distinguish a consumer cancellation (-> CANCELLED status) from an DuckDB-initiated interrupt - // that shares the INTERRUPT exception type, e.g. a `max_execution_time` timeout (-> error). - // The DuckDB's internal interrupt_state is not suitable for this, it is set to stop sibling tasks on any error. - // Reset when a new query claims the slot. - std::atomic cancel_requested {false}; + // The number of `connection_interrupt` calls. A result records it when its statement starts, and an INTERRUPT + // error is a consumer cancellation (-> CANCELLED status) only if it moved since: a DuckDB-initiated interrupt, + // e.g. a `max_execution_time` timeout, shares the INTERRUPT exception type (-> error). The DuckDB's internal + // interrupt_state is not suitable for this, it is set to stop sibling tasks on any error. + std::atomic cancel_requests {0}; }; inline shared_ptr GetBusySlot(ClientContext &context) { @@ -505,7 +865,7 @@ inline void InvokeTextSink(duckdb_v2_text_sink_fn sink, duckdb_v2_str text, void // The slot is always live: sinks populate it, they never allocate or destroy it. CV2ErrorInfo info; auto handle = Convert(&info); - sink(text, user_data, &handle); + sink(&text, user_data, &handle); if (info.HasError()) { info.ThrowAsException(); } @@ -633,7 +993,7 @@ inline void BuildParameterMap(const duckdb_v2_identifier_t *parameter_names, throw InvalidInputException("null parameter value passed to %s", function_name); } // Named iff the name view is non-empty; otherwise positional - auto str = Convert(name); + auto str = ConvertIdentifierName(name); Identifier key = (name.ptr && name.len > 0) ? Identifier(str) : Identifier(std::to_string(i + 1)); out[key] = BoundParameterData(*Convert(parameter_values[i])); } diff --git a/src/duckdb/src/include/duckdb/main/capi_v2/capi_v2_result_internal.hpp b/src/duckdb/src/include/duckdb/main/capi_v2/capi_v2_result_internal.hpp index d76fc2861..031190fa6 100644 --- a/src/duckdb/src/include/duckdb/main/capi_v2/capi_v2_result_internal.hpp +++ b/src/duckdb/src/include/duckdb/main/capi_v2/capi_v2_result_internal.hpp @@ -8,7 +8,10 @@ #pragma once +#include "duckdb/common/arrow/arrow_wrapper.hpp" #include "duckdb/main/capi_v2/capi_v2_internal.hpp" +#include "duckdb/main/query_result_stream.hpp" +#include "duckdb/main/result_format.hpp" //! The result state machine, shared by the modules that build a result or consume one wholesale. //! Not part of any public surface: only the V2 bridge's own translation units include this. @@ -23,130 +26,88 @@ struct ResultWrapperV2 { ~ResultWrapperV2() { // Finalize() (engine cleanup) runs in duckdb_v2_result_destroy, not here: // a destructor must not drive locked engine state behind a catch-all. - pending.reset(); - result.reset(); - ReleaseBusySlot(); + stream.reset(); + handle.reset(); } State state = State::PENDING; - //! Live while state == PENDING. - unique_ptr pending; - //! Live while state == STREAMING. - unique_ptr result; - - //! Keeps the ClientContext alive for starting subsequent fragments and - //! preserves the guarantee that an undrained result survives disconnect: + //! The handle of the running query. Live while state == PENDING, and afterwards for a result + //! that is retained rather than streamed. + unique_ptr handle; + //! Live while state == STREAMING, for a statement whose result can be streamed. A QueryResultStream in `format`. + unique_ptr stream; + //! Decided when the handle is adopted, since a prepared execution's handle reports the EXECUTE that runs it + //! rather than the statement as written + bool stream_when_ready = false; + //! The statement is submitted in this format; null is the chunk format. + shared_ptr format; + + //! Preserves the guarantee that an undrained result survives disconnect: //! the connection handle (a bare Connection *) may be destroyed while a //! result is live, but the context it shared with us stays alive here. shared_ptr context; - //! The preprocessed statement group; fragment_index points at the next - //! fragment to start, fragment_count remembers the group size after - //! fragments is cleared on terminal transitions. - vector> fragments; - idx_t fragment_index = 0; - idx_t fragment_count = 0; - //! Positional parameter values for this execution, keyed by binding identifier - //! ("1".."N"). Empty for an unparameterized statement (StartNextFragment takes - //! the no-values path). Applied only to the first fragment (a parameterized - //! statement does not expand into a group). - identifier_map_t param_values; - //! True while the currently executing fragment is the principal one - //! (its chunks are surfaced; other fragments' output is discarded). - bool principal_active = false; - //! True once a row-producing fragment has been selected as principal. - bool principal_seen = false; - //! True once the principal fragment's metadata has been captured. + //! True once the metadata has been captured. For a statement that expands into several, that is once execution + //! bound the statement that produces the result, or the group ended without one. bool metadata_available = false; - //! True when statement_execute injected its own wrapping transaction for - //! this group (autocommit input that preprocessing expanded and wrapped - //! in BEGIN ... COMMIT). Distinguishes a bridge-owned transaction, which - //! must be rolled back on incomplete destroy, from a user-managed one, - //! which must not be touched. Captured at query time because the engine's - //! auto_rollback flag is not reliably observable from the bridge's - //! fragment execution path. - bool owns_wrapping_transaction = false; - - //! The owning connection's busy slot; released on terminal transition - //! or destroy, whichever comes first. + + //! The connection's busy slot, kept for the result's lifetime for its cancel count. shared_ptr busy_slot; + //! The slot's cancel count when this result's statement started + idx_t cancel_requests_at_start = 0; - //! Principal fragment's metadata, valid once metadata_available. + //! The result's metadata, valid once metadata_available. The statement type is known at submission. vector types; vector names; StatementType statement_type = StatementType::INVALID_STATEMENT; StatementProperties properties; + //! Set with the metadata when `format` is Arrow: a copy of the format's schema, which is gone once the query ends. + ArrowSchemaWrapper arrow_schema; //! Sticky error, recorded when state == ERRORED. ErrorData error; - //! Mirrors ClientContext::Query's error handling for expanded groups - //! (client_context.cpp, chain-append loop): when the group cannot - //! complete, roll back the transaction statement_execute injected to wrap - //! it. A no-op for non-expanded statements, for groups that ran inside a - //! user-managed transaction (which the bridge never wraps), and when the - //! transaction is already gone. - void RollbackIncompleteGroup() { - if (!owns_wrapping_transaction || !context) { - return; - } - if (context->transaction.HasActiveTransaction()) { - // Mirrors Connection::Rollback (Query("ROLLBACK") + throw on error), - // driven through the retained context so it works after disconnect. - auto result = context->Query("ROLLBACK", QueryResultOutputType::FORCE_MATERIALIZED); - if (result->HasError()) { - result->ThrowError(); - } - } - } - //! Close() the live engine result so an abandoned active query is cleaned - //! up (freeing the executor, which breaks the ClientContext ref cycle), - //! then roll back an injected group transaction. May throw; the terminal - //! states leave pending/result null, so this is then a no-op. + //! up (freeing the executor, which breaks the ClientContext ref cycle). The + //! engine undoes a statement that expands into several as a whole. May throw. void Finalize() { - if (pending) { - pending->Close(); - } else if (result && result->GetResultType() == QueryResultType::STREAM_RESULT) { - result->Cast().Close(); + if (stream) { + stream->Close(); + } else if (handle) { + handle->Close(); } - RollbackIncompleteGroup(); } - //! Frees the connection for its next query. Only the current owner can - //! release the slot, so a release after the connection moved on is a - //! no-op. - void ReleaseBusySlot() { - if (busy_slot) { - void *expected = this; - busy_slot->owner.compare_exchange_strong(expected, nullptr); - busy_slot.reset(); - } - } - - // State-machine entry points; defined in query_result-v2.cpp. All of + // State-machine entry points; defined in capi_v2_result.cpp. All of // them throw DuckDB exceptions on failure (callers wrap in // WithErrorHandler) and record sticky errors before throwing. - //! Adopts an already-produced pending query into the state machine: the single - //! seam both the stateless (fragment) and prepared paths reach. When is_principal, - //! captures its metadata and surfaces its chunks. Throws on a pending prepare error. - void BeginPending(unique_ptr pending, bool is_principal); - //! Starts the pending query for the next fragment, selecting it as - //! principal per the engine-mirrored rule and adopting it via BeginPending. - //! Throws on prepare errors. - void StartNextFragment(); - //! Drives one unit of work; never blocks. On CHUNK, out_chunk holds the - //! produced chunk; on every other status it is reset. - DUCKDB_V2_RESULT_STEP_STATUS Step(unique_ptr &out_chunk); + //! Adopts an already-submitted query into the state machine: the single seam both the + //! stateless and prepared paths reach. written_type is the type of the statement as written, since a prepared + //! execution's handle reports the EXECUTE that runs it. Throws on a submission error. + void BeginPending(unique_ptr handle, StatementType written_type); + //! Drives one unit of work; never blocks. On CHUNK, out_unit holds the + //! produced unit; on every other status it is reset. FORMAT must be `format`. + template + DUCKDB_V2_RESULT_STEP_STATUS Step(unique_ptr &out_unit); //! Blocks until Step can make progress. No-op on terminal states. void Wait(); - //! Blocking convenience: steps/waits until a chunk is produced (returned) + //! Blocking convenience: steps/waits until a unit is produced (returned) //! or the stream ends (nullptr). Cancellation throws InterruptException. - unique_ptr FetchChunkBlocking(); - //! Throws unless the principal fragment's metadata is available. - void RequireMetadata() const; + template + unique_ptr FetchBlocking(); + //! Steps a statement that expands into several until its metadata is available. Stops early when the result + //! ends without it. + template + void AdvanceToMetadata(); + //! Runs to the end. Returns the changed-row count of a CHANGED_ROWS result, 0 otherwise. + template + idx_t Drain(); + //! Throws MetadataDeferredException unless the metadata is available. + void RequireMetadata(); private: + //! Copies the metadata off the live handle or stream once the engine knows it, since both go when the result ends + void CaptureMetadata(); //! Shared error sink: interrupts become the sticky CANCELLED state //! (returned as a status), everything else becomes the sticky ERRORED //! state and throws. @@ -155,11 +116,23 @@ struct ResultWrapperV2 { auto Convert(ResultWrapperV2 *wrapper) -> duckdb_v2_result_handle; auto Convert(duckdb_v2_result_handle handle) -> ResultWrapperV2 *; - -//! Runs a prepared statement as a single-statement result, claiming the connection's -//! live-result slot first. `context` is the session the result holds on to, so it survives -//! disconnect. Throws ResourceInUseException when the connection already has a live result. +auto ConvertArrowResult(ResultWrapperV2 *wrapper) -> duckdb_v2_arrow_result_handle; +auto Convert(duckdb_v2_arrow_result_handle handle) -> ResultWrapperV2 *; + +//! Submits a borrowed statement in `format`. Throws ResourceInUseException while a statement on the connection is +//! still running. +auto ExecuteStatementV2(const shared_ptr &context, const SQLStatement &statement, + const duckdb_v2_identifier_t *parameter_names, const duckdb_v2_value_handle *parameter_values, + idx_t parameter_count, const char *function_name, shared_ptr format) + -> unique_ptr; +//! Runs a prepared statement. `context` is the session the result holds on to, so it +//! survives disconnect. Throws ResourceInUseException while a statement on the connection is still running. auto ExecutePreparedStatementV2(const shared_ptr &context, PreparedStatement &prepared, - identifier_map_t &values) -> duckdb_v2_result_handle; + identifier_map_t &values, shared_ptr format) + -> unique_ptr; +//! A deep copy the caller owns. `out` is left released on failure. +void CopyArrowSchema(const ArrowSchema &source, ArrowSchema &out); +//! The format of an Arrow result; a batch size of 0 selects the default. +auto ArrowResultFormat(idx_t batch_size) -> shared_ptr; } // namespace duckdb::capiv2 diff --git a/src/duckdb/src/include/duckdb/main/capi_v2/extension_api_v2.hpp b/src/duckdb/src/include/duckdb/main/capi_v2/extension_api_v2.hpp index 45428c794..f749148a5 100644 --- a/src/duckdb/src/include/duckdb/main/capi_v2/extension_api_v2.hpp +++ b/src/duckdb/src/include/duckdb/main/capi_v2/extension_api_v2.hpp @@ -15,7 +15,6 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_bignum_encode) (const uint8_t *in_data, idx_t in_length, bool is_negative, uint8_t *out_data, idx_t out_capacity, idx_t *out_length, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR (*duckdb_v2_close)(duckdb_v2_database_handle *db); DUCKDB_V2_ERROR(*duckdb_v2_column_data_collection_append) (duckdb_v2_column_data_collection_handle collection, duckdb_v2_column_data_collection_append_state_handle state, duckdb_v2_data_chunk_handle chunk, duckdb_v2_error_info_handle *err); @@ -27,11 +26,8 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_column_data_collection_combine) (duckdb_v2_column_data_collection_handle target, duckdb_v2_column_data_collection_handle *source, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_column_data_collection_create_with_connection) - (duckdb_v2_connection_handle conn, const duckdb_v2_logical_type_handle *types_array, idx_t types_count, - duckdb_v2_column_data_collection_handle *out_collection, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_column_data_collection_create_with_context) - (duckdb_v2_context_handle context, const duckdb_v2_logical_type_handle *types_array, idx_t types_count, + DUCKDB_V2_ERROR(*duckdb_v2_column_data_collection_create) + (duckdb_v2_factory_handle factory, const duckdb_v2_logical_type_handle *types_array, idx_t types_count, duckdb_v2_column_data_collection_handle *out_collection, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_column_data_collection_destroy)(duckdb_v2_column_data_collection_handle *collection); DUCKDB_V2_ERROR(*duckdb_v2_column_data_collection_reset) @@ -53,68 +49,27 @@ typedef struct { duckdb_v2_column_data_collection_worker_scan_state_handle *out_state, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_column_data_collection_worker_scan_state_destroy) (duckdb_v2_column_data_collection_worker_scan_state_handle *state); - DUCKDB_V2_ERROR(*duckdb_v2_connect) - (duckdb_v2_database_handle db, duckdb_v2_connection_handle *out_conn, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_connection_create_type_from_id) - (duckdb_v2_connection_handle conn, DUCKDB_V2_LOGICAL_TYPE_ID type_id, const duckdb_v2_identifier_t *param_names, - const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_connection_create_type_from_name) - (duckdb_v2_connection_handle conn, duckdb_v2_qname_handle name, const duckdb_v2_identifier_t *param_names, - const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, + DUCKDB_V2_ERROR(*duckdb_v2_config_get_option_by_index) + (duckdb_v2_config_handle config, idx_t index, duckdb_v2_option_handle *out_option, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_connection_create_type_from_text) - (duckdb_v2_connection_handle conn, duckdb_v2_str text, duckdb_v2_logical_type_handle *out_type, + DUCKDB_V2_ERROR(*duckdb_v2_config_get_option_by_name) + (duckdb_v2_config_handle config, const duckdb_v2_identifier_t *name, duckdb_v2_option_handle *out_option, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_connection_create_type_with_alias) - (duckdb_v2_connection_handle conn, duckdb_v2_logical_type_handle base_type, duckdb_v2_identifier_t alias_name, - duckdb_v2_logical_type_handle *out_type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_config_get_option_count) + (duckdb_v2_config_handle config, idx_t *out_count, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_create) + (duckdb_v2_instance_handle instance, duckdb_v2_connection_handle *out_conn, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_connection_destroy)(duckdb_v2_connection_handle *conn); DUCKDB_V2_ERROR(*duckdb_v2_connection_interrupt) (duckdb_v2_connection_handle conn, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_connection_option_get) - (duckdb_v2_connection_handle conn, duckdb_v2_identifier_t name, duckdb_v2_option_handle *out_option, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_connection_option_get_by_index) - (duckdb_v2_connection_handle conn, idx_t index, duckdb_v2_option_handle *out_option, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_connection_option_get_count) - (duckdb_v2_connection_handle conn, idx_t *out_count, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_connection_option_set) - (duckdb_v2_connection_handle conn, duckdb_v2_option_handle option, DUCKDB_V2_SETTING_SCOPE scope, - duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_connection_progress_get) (duckdb_v2_connection_handle conn, double *out_percentage, uint64_t *out_rows_processed, uint64_t *out_total_rows_to_process, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_context_create_type_from_id) - (duckdb_v2_context_handle ctx, DUCKDB_V2_LOGICAL_TYPE_ID type_id, const duckdb_v2_identifier_t *param_names, - const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_context_create_type_from_name) - (duckdb_v2_context_handle ctx, duckdb_v2_qname_handle name, const duckdb_v2_identifier_t *param_names, - const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_context_create_type_from_text) - (duckdb_v2_context_handle ctx, duckdb_v2_str text, duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_context_create_type_with_alias) - (duckdb_v2_context_handle ctx, duckdb_v2_logical_type_handle base_type, duckdb_v2_identifier_t alias_name, - duckdb_v2_logical_type_handle *out_type, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_create_environment) - (duckdb_v2_environment_handle *out_env, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_data_chunk_copy_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_data_chunk_handle chunk, duckdb_v2_data_chunk_handle *out_chunk, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_data_chunk_copy_with_context) - (duckdb_v2_context_handle context, duckdb_v2_data_chunk_handle chunk, duckdb_v2_data_chunk_handle *out_chunk, + DUCKDB_V2_ERROR(*duckdb_v2_data_chunk_copy) + (duckdb_v2_factory_handle factory, duckdb_v2_data_chunk_handle chunk, duckdb_v2_data_chunk_handle *out_chunk, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_data_chunk_create) - (const duckdb_v2_logical_type_handle *types, idx_t column_count, duckdb_v2_data_chunk_handle *out_chunk, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_data_chunk_create_with_connection) - (duckdb_v2_connection_handle conn, const duckdb_v2_logical_type_handle *types, idx_t column_count, - duckdb_v2_data_chunk_handle *out_chunk, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_data_chunk_create_with_context) - (duckdb_v2_context_handle context, const duckdb_v2_logical_type_handle *types, idx_t column_count, + (duckdb_v2_factory_handle factory, const duckdb_v2_logical_type_handle *types, idx_t column_count, duckdb_v2_data_chunk_handle *out_chunk, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_data_chunk_destroy)(duckdb_v2_data_chunk_handle *chunk); DUCKDB_V2_ERROR(*duckdb_v2_data_chunk_get_size) @@ -124,26 +79,43 @@ typedef struct { duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_data_chunk_get_vector_count) (duckdb_v2_data_chunk_handle chunk, idx_t *out_count, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_database_option_get) - (duckdb_v2_database_handle db, duckdb_v2_identifier_t name, duckdb_v2_option_handle *out_option, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_database_option_get_by_index) - (duckdb_v2_database_handle db, idx_t index, duckdb_v2_option_handle *out_option, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_database_option_get_count) - (duckdb_v2_database_handle db, idx_t *out_count, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_database_option_set) - (duckdb_v2_database_handle db, duckdb_v2_option_handle option, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR (*duckdb_v2_destroy_environment)(duckdb_v2_environment_handle *env); - DUCKDB_V2_ERROR (*duckdb_v2_disconnect)(duckdb_v2_connection_handle *conn); - DUCKDB_V2_ERROR(*duckdb_v2_environment_database_count) + DUCKDB_V2_ERROR(*duckdb_v2_environment_create) + (duckdb_v2_environment_handle *out_env, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_environment_destroy)(duckdb_v2_environment_handle *env); + DUCKDB_V2_ERROR(*duckdb_v2_environment_get_instance_count) (duckdb_v2_environment_handle env, idx_t *out_count, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_error_info_destroy)(duckdb_v2_error_info_handle *info); DUCKDB_V2_ERROR (*duckdb_v2_error_info_get_code)(duckdb_v2_error_info_handle info, DUCKDB_V2_ERROR *out_code); - DUCKDB_V2_ERROR(*duckdb_v2_error_info_get_raw_message) - (duckdb_v2_error_info_handle info, duckdb_v2_str *out_raw_message); + DUCKDB_V2_ERROR (*duckdb_v2_error_info_get_raw_text)(duckdb_v2_error_info_handle info, duckdb_v2_str *out_raw_text); DUCKDB_V2_ERROR (*duckdb_v2_error_info_get_text)(duckdb_v2_error_info_handle info, duckdb_v2_str *out_text); DUCKDB_V2_ERROR (*duckdb_v2_error_info_set_code)(duckdb_v2_error_info_handle info, DUCKDB_V2_ERROR code); - DUCKDB_V2_ERROR (*duckdb_v2_error_info_set_text)(duckdb_v2_error_info_handle info, duckdb_v2_str text); + DUCKDB_V2_ERROR (*duckdb_v2_error_info_set_text)(duckdb_v2_error_info_handle info, const duckdb_v2_str *text); + DUCKDB_V2_ERROR(*duckdb_v2_factory_create_type_from_id) + (duckdb_v2_factory_handle factory, DUCKDB_V2_LOGICAL_TYPE_ID type_id, const duckdb_v2_identifier_t *param_names, + const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_factory_create_type_from_name) + (duckdb_v2_factory_handle factory, duckdb_v2_qname_handle name, const duckdb_v2_identifier_t *param_names, + const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_factory_create_type_from_text) + (duckdb_v2_factory_handle factory, const duckdb_v2_str *text, duckdb_v2_logical_type_handle *out_type, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_factory_create_type_with_alias) + (duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle base_type, + const duckdb_v2_identifier_t *alias_name, duckdb_v2_logical_type_handle *out_type, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_attach) + (duckdb_v2_instance_handle instance, const duckdb_v2_identifier_t *name, const duckdb_v2_str *path, + duckdb_v2_attach_options_handle options, bool make_default, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_create) + (duckdb_v2_environment_handle env, duckdb_v2_instance_handle *out_instance, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_create_with_options) + (duckdb_v2_environment_handle env, const duckdb_v2_str *option_names, const duckdb_v2_str *option_values, + idx_t option_count, duckdb_v2_instance_handle *out_instance, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_instance_destroy)(duckdb_v2_instance_handle *instance); + DUCKDB_V2_ERROR(*duckdb_v2_instance_detach) + (duckdb_v2_instance_handle instance, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_library_version)(duckdb_v2_str *out_version, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_logical_type_copy) (duckdb_v2_logical_type_handle type, duckdb_v2_logical_type_handle *out_type, duckdb_v2_error_info_handle *err); @@ -163,27 +135,17 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_logical_type_to_text) (duckdb_v2_logical_type_handle type, char *out_text, idx_t out_capacity, idx_t *out_length, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_open) - (duckdb_v2_environment_handle env, duckdb_v2_str path, duckdb_v2_option_handle *options, idx_t option_count, - duckdb_v2_database_handle *out_db, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_option_create) - (duckdb_v2_identifier_t name, duckdb_v2_str setting, duckdb_v2_option_handle *out_option, - duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_option_destroy)(duckdb_v2_option_handle *option); DUCKDB_V2_ERROR(*duckdb_v2_option_get_alias) (duckdb_v2_option_handle option, idx_t index, duckdb_v2_identifier_t *out_alias, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_option_get_alias_count) (duckdb_v2_option_handle option, idx_t *out_count, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_option_get_default_setting) - (duckdb_v2_option_handle option, duckdb_v2_str *out_default_setting, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_option_get_default_value) + (duckdb_v2_option_handle option, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_option_get_description) (duckdb_v2_option_handle option, duckdb_v2_str *out_description, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_option_get_name) (duckdb_v2_option_handle option, duckdb_v2_identifier_t *out_name, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_option_get_setting) - (duckdb_v2_option_handle option, duckdb_v2_str *out_setting, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_option_get_target_scope) - (duckdb_v2_option_handle option, DUCKDB_V2_OPTION_TARGET_SCOPE *out_target_scope, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_parse_sql) (duckdb_v2_connection_handle conn, const char *sql, duckdb_v2_statement_iterator_handle *out_iterator, duckdb_v2_error_info_handle *err); @@ -199,8 +161,9 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_result_get_statement_type) (duckdb_v2_result_handle result, DUCKDB_V2_STATEMENT_TYPE *out_type, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_result_render_box) - (duckdb_v2_result_handle *result, idx_t max_rows, idx_t max_width, idx_t max_col_width, duckdb_v2_str null_value, - idx_t render_mode, idx_t limit, duckdb_v2_text_sink_fn sink, void *user_data, duckdb_v2_error_info_handle *err); + (duckdb_v2_result_handle *result, idx_t max_rows, idx_t max_width, idx_t max_col_width, + const duckdb_v2_str *null_value, idx_t render_mode, idx_t limit, duckdb_v2_text_sink_fn sink, void *user_data, + duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_result_step) (duckdb_v2_result_handle result, duckdb_v2_data_chunk_handle *out_chunk, DUCKDB_V2_RESULT_STEP_STATUS *out_status, duckdb_v2_error_info_handle *err); @@ -223,235 +186,120 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_statement_iterator_next) (duckdb_v2_statement_iterator_handle iterator, duckdb_v2_sql_statement_handle *out_statement, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_cast_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_value_handle value, duckdb_v2_logical_type_handle target_type, + DUCKDB_V2_ERROR(*duckdb_v2_value_cast) + (duckdb_v2_factory_handle factory, duckdb_v2_value_handle value, duckdb_v2_logical_type_handle target_type, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_cast_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_value_handle value, duckdb_v2_logical_type_handle target_type, - duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_array_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_logical_type_handle child_type, const duckdb_v2_value_handle *children, - idx_t child_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_array_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_logical_type_handle child_type, const duckdb_v2_value_handle *children, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_array) + (duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle child_type, const duckdb_v2_value_handle *children, idx_t child_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_bigint_with_connection) - (duckdb_v2_connection_handle conn, int64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_bigint_with_context) - (duckdb_v2_context_handle ctx, int64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_bignum_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_str in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_bignum_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_str in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_bit_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_str in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_bit_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_str in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_bigint) + (duckdb_v2_factory_handle factory, int64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_blob_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_str in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_bignum) + (duckdb_v2_factory_handle factory, const duckdb_v2_str *in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_blob_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_str in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_bit) + (duckdb_v2_factory_handle factory, const duckdb_v2_str *in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_bool_with_connection) - (duckdb_v2_connection_handle conn, bool in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_blob) + (duckdb_v2_factory_handle factory, const duckdb_v2_str *in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_bool_with_context) - (duckdb_v2_context_handle ctx, bool in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_date_with_connection) - (duckdb_v2_connection_handle conn, int32_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_bool) + (duckdb_v2_factory_handle factory, bool in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_date_with_context) - (duckdb_v2_context_handle ctx, int32_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_date) + (duckdb_v2_factory_handle factory, int32_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_decimal_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_hugeint_t in_value, uint8_t width, uint8_t scale, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_decimal) + (duckdb_v2_factory_handle factory, const duckdb_v2_hugeint_t *in_value, uint8_t width, uint8_t scale, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_decimal_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_hugeint_t in_value, uint8_t width, uint8_t scale, - duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_double_with_connection) - (duckdb_v2_connection_handle conn, double in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_double_with_context) - (duckdb_v2_context_handle ctx, double in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_float_with_connection) - (duckdb_v2_connection_handle conn, float in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_double) + (duckdb_v2_factory_handle factory, double in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_float_with_context) - (duckdb_v2_context_handle ctx, float in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_hugeint_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_hugeint_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_float) + (duckdb_v2_factory_handle factory, float in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_hugeint_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_hugeint_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_hugeint) + (duckdb_v2_factory_handle factory, const duckdb_v2_hugeint_t *in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_int_with_connection) - (duckdb_v2_connection_handle conn, int32_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_int) + (duckdb_v2_factory_handle factory, int32_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_int_with_context) - (duckdb_v2_context_handle ctx, int32_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_interval) + (duckdb_v2_factory_handle factory, const duckdb_v2_interval_t *in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_interval_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_interval_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_interval_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_interval_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_list_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_logical_type_handle child_type, const duckdb_v2_value_handle *children, - idx_t child_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_list_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_logical_type_handle child_type, const duckdb_v2_value_handle *children, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_list) + (duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle child_type, const duckdb_v2_value_handle *children, idx_t child_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_map_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_logical_type_handle key_type, duckdb_v2_logical_type_handle value_type, - const duckdb_v2_value_handle *keys, const duckdb_v2_value_handle *values, idx_t entry_count, - duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_map_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_logical_type_handle key_type, duckdb_v2_logical_type_handle value_type, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_map) + (duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle key_type, duckdb_v2_logical_type_handle value_type, const duckdb_v2_value_handle *keys, const duckdb_v2_value_handle *values, idx_t entry_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_value_create_null) - (duckdb_v2_logical_type_handle type, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_null_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_logical_type_handle type, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_null_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_logical_type_handle type, duckdb_v2_value_handle *out_value, + (duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle type, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_smallint_with_connection) - (duckdb_v2_connection_handle conn, int16_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_smallint) + (duckdb_v2_factory_handle factory, int16_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_smallint_with_context) - (duckdb_v2_context_handle ctx, int16_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_struct_with_connection) - (duckdb_v2_connection_handle conn, const duckdb_v2_identifier_t *names, const duckdb_v2_value_handle *children, - idx_t field_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_struct_with_context) - (duckdb_v2_context_handle ctx, const duckdb_v2_identifier_t *names, const duckdb_v2_value_handle *children, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_struct) + (duckdb_v2_factory_handle factory, const duckdb_v2_identifier_t *names, const duckdb_v2_value_handle *children, idx_t field_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_time_ns_with_connection) - (duckdb_v2_connection_handle conn, int64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_time_ns_with_context) - (duckdb_v2_context_handle ctx, int64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_time_tz_with_connection) - (duckdb_v2_connection_handle conn, uint64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_time_tz_with_context) - (duckdb_v2_context_handle ctx, uint64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_time_with_connection) - (duckdb_v2_connection_handle conn, int64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_time_with_context) - (duckdb_v2_context_handle ctx, int64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_ms_with_connection) - (duckdb_v2_connection_handle conn, int64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_ms_with_context) - (duckdb_v2_context_handle ctx, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_time) + (duckdb_v2_factory_handle factory, int64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_ns_with_connection) - (duckdb_v2_connection_handle conn, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_time_ns) + (duckdb_v2_factory_handle factory, int64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_ns_with_context) - (duckdb_v2_context_handle ctx, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_time_tz) + (duckdb_v2_factory_handle factory, uint64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_sec_with_connection) - (duckdb_v2_connection_handle conn, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp) + (duckdb_v2_factory_handle factory, int64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_sec_with_context) - (duckdb_v2_context_handle ctx, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_ms) + (duckdb_v2_factory_handle factory, int64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_tz_ns_with_connection) - (duckdb_v2_connection_handle conn, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_ns) + (duckdb_v2_factory_handle factory, int64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_tz_ns_with_context) - (duckdb_v2_context_handle ctx, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_sec) + (duckdb_v2_factory_handle factory, int64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_tz_with_connection) - (duckdb_v2_connection_handle conn, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_tz) + (duckdb_v2_factory_handle factory, int64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_tz_with_context) - (duckdb_v2_context_handle ctx, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_tz_ns) + (duckdb_v2_factory_handle factory, int64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_with_connection) - (duckdb_v2_connection_handle conn, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_tinyint) + (duckdb_v2_factory_handle factory, int8_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_with_context) - (duckdb_v2_context_handle ctx, int64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_tinyint_with_connection) - (duckdb_v2_connection_handle conn, int8_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_tinyint_with_context) - (duckdb_v2_context_handle ctx, int8_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_tuple_with_connection) - (duckdb_v2_connection_handle conn, const duckdb_v2_value_handle *children, idx_t field_count, - duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_tuple_with_context) - (duckdb_v2_context_handle ctx, const duckdb_v2_value_handle *children, idx_t field_count, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_tuple) + (duckdb_v2_factory_handle factory, const duckdb_v2_value_handle *children, idx_t field_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_type_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_logical_type_handle type, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_type_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_logical_type_handle in_type, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_ubigint_with_connection) - (duckdb_v2_connection_handle conn, uint64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_ubigint_with_context) - (duckdb_v2_context_handle ctx, uint64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_type) + (duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle in_type, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_uhugeint_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_uhugeint_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_ubigint) + (duckdb_v2_factory_handle factory, uint64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_uhugeint_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_uhugeint_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_uhugeint) + (duckdb_v2_factory_handle factory, const duckdb_v2_uhugeint_t *in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_uint_with_connection) - (duckdb_v2_connection_handle conn, uint32_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_uint) + (duckdb_v2_factory_handle factory, uint32_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_uint_with_context) - (duckdb_v2_context_handle ctx, uint32_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_usmallint) + (duckdb_v2_factory_handle factory, uint16_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_usmallint_with_connection) - (duckdb_v2_connection_handle conn, uint16_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_utinyint) + (duckdb_v2_factory_handle factory, uint8_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_usmallint_with_context) - (duckdb_v2_context_handle ctx, uint16_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_uuid) + (duckdb_v2_factory_handle factory, const duckdb_v2_hugeint_t *in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_utinyint_with_connection) - (duckdb_v2_connection_handle conn, uint8_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_utinyint_with_context) - (duckdb_v2_context_handle ctx, uint8_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_uuid_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_hugeint_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_uuid_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_hugeint_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_varchar_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_str in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_varchar_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_str in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_varchar) + (duckdb_v2_factory_handle factory, const duckdb_v2_str *in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_value_destroy)(duckdb_v2_value_handle *value); DUCKDB_V2_ERROR(*duckdb_v2_value_get_bigint) @@ -559,51 +407,27 @@ typedef struct { (duckdb_v2_vector_handle vector, idx_t size, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_vector_set_value) (duckdb_v2_vector_handle vector, idx_t row, duckdb_v2_value_handle value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_bind_get_arg_count) - (duckdb_v2_aggregate_function_bind_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_bind_get_arg_type) - (duckdb_v2_aggregate_function_bind_info_handle info, idx_t index, duckdb_v2_logical_type_handle *type, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_bind_get_arg_value) - (duckdb_v2_aggregate_function_bind_info_handle info, idx_t index, duckdb_v2_value_handle *value, - duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_scalar_function) + (duckdb_v2_connection_handle conn, duckdb_v2_scalar_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_context_log) - (duckdb_v2_context_handle ctx, DUCKDB_V2_LOG_LEVEL level, duckdb_v2_str log_type, duckdb_v2_str message, - duckdb_v2_error_info_handle *err); + (duckdb_v2_context_handle ctx, DUCKDB_V2_LOG_LEVEL level, const duckdb_v2_str *log_type, + const duckdb_v2_str *message, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_scalar_function) + (duckdb_v2_extension_handle extension, duckdb_v2_scalar_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_function_signature_add_parameter) - (duckdb_v2_function_signature_handle sig, duckdb_v2_identifier_t name, duckdb_v2_logical_type_handle type, - duckdb_v2_value_handle value, duckdb_v2_error_info_handle *err); + (duckdb_v2_function_signature_handle sig, const duckdb_v2_identifier_t *name, duckdb_v2_logical_type_handle type, + duckdb_v2_value_handle value, DUCKDB_V2_FUNCTION_PARAMETER_KIND kind, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_function_signature_set_return_type) (duckdb_v2_function_signature_handle sig, duckdb_v2_logical_type_handle type, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_function_signature_set_varargs) - (duckdb_v2_function_signature_handle sig, duckdb_v2_logical_type_handle type, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_bind_get_arg_count) - (duckdb_v2_scalar_function_bind_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_bind_get_arg_type) - (duckdb_v2_scalar_function_bind_info_handle info, idx_t index, duckdb_v2_logical_type_handle *type, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_bind_get_arg_value) - (duckdb_v2_scalar_function_bind_info_handle info, idx_t index, duckdb_v2_value_handle *value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_bind_get_user_data) - (duckdb_v2_scalar_function_bind_info_handle info, void **data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_bind_set_bind_data) - (duckdb_v2_scalar_function_bind_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_bind_set_return_type) - (duckdb_v2_scalar_function_bind_info_handle info, duckdb_v2_logical_type_handle return_type, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_create_with_connection) - (duckdb_v2_connection_handle connection, duckdb_v2_scalar_function_handle *function, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_create_with_extension) - (duckdb_v2_extension_handle extension, duckdb_v2_scalar_function_handle *function, - duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_create) + (duckdb_v2_factory_handle factory, duckdb_v2_scalar_function_handle *function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_scalar_function_destroy)(duckdb_v2_scalar_function_handle *function); DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_exec_get_arg) - (duckdb_v2_scalar_function_exec_info_handle info, uint32_t index, duckdb_v2_vector_handle *vector, + (duckdb_v2_scalar_function_exec_info_handle info, idx_t index, duckdb_v2_vector_handle *vector, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_exec_get_arg_count) - (duckdb_v2_scalar_function_exec_info_handle info, uint32_t *count, duckdb_v2_error_info_handle *err); + (duckdb_v2_scalar_function_exec_info_handle info, idx_t *positional_fixed, idx_t *positional_variadic, + idx_t *named_fixed, idx_t *named_variadic, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_exec_get_bind_data) (duckdb_v2_scalar_function_exec_info_handle info, void **data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_exec_get_init_data) @@ -624,8 +448,6 @@ typedef struct { (duckdb_v2_scalar_function_init_info_handle info, void **data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_init_set_init_data) (duckdb_v2_scalar_function_init_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_register) - (duckdb_v2_scalar_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_set_bind_callback) (duckdb_v2_scalar_function_handle function, duckdb_v2_scalar_function_bind_callback_fn callback, duckdb_v2_error_info_handle *err); @@ -636,13 +458,9 @@ typedef struct { (duckdb_v2_scalar_function_handle function, duckdb_v2_scalar_function_init_callback_fn callback, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_set_name) - (duckdb_v2_scalar_function_handle function, duckdb_v2_str *name, duckdb_v2_error_info_handle *err); + (duckdb_v2_scalar_function_handle function, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_set_user_data) (duckdb_v2_scalar_function_handle function, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_bind_get_user_data) - (duckdb_v2_aggregate_function_bind_info_handle info, void **data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_bind_set_bind_data) - (duckdb_v2_aggregate_function_bind_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_bind_set_return_type) (duckdb_v2_aggregate_function_bind_info_handle info, duckdb_v2_logical_type_handle return_type, duckdb_v2_error_info_handle *err); @@ -656,12 +474,8 @@ typedef struct { (duckdb_v2_aggregate_function_combine_info_handle info, void ***states, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_combine_get_user_data) (duckdb_v2_aggregate_function_combine_info_handle info, void **data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_create_with_connection) - (duckdb_v2_connection_handle connection, duckdb_v2_aggregate_function_handle *function, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_create_with_extension) - (duckdb_v2_extension_handle extension, duckdb_v2_aggregate_function_handle *function, - duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_create) + (duckdb_v2_factory_handle factory, duckdb_v2_aggregate_function_handle *function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_aggregate_function_destroy)(duckdb_v2_aggregate_function_handle *function); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_destroy_get_bind_data) (duckdb_v2_aggregate_function_destroy_info_handle info, void **data, duckdb_v2_error_info_handle *err); @@ -695,8 +509,6 @@ typedef struct { (duckdb_v2_aggregate_function_init_info_handle info, void ***states, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_init_get_user_data) (duckdb_v2_aggregate_function_init_info_handle info, void **data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_register) - (duckdb_v2_aggregate_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_set_bind_callback) (duckdb_v2_aggregate_function_handle function, duckdb_v2_aggregate_function_bind_callback_fn callback, duckdb_v2_error_info_handle *err); @@ -713,7 +525,8 @@ typedef struct { (duckdb_v2_aggregate_function_handle function, duckdb_v2_aggregate_function_init_callback_fn callback, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_set_name) - (duckdb_v2_aggregate_function_handle function, duckdb_v2_str *name, duckdb_v2_error_info_handle *err); + (duckdb_v2_aggregate_function_handle function, const duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_set_size_callback) (duckdb_v2_aggregate_function_handle function, duckdb_v2_aggregate_function_size_callback_fn callback, duckdb_v2_error_info_handle *err); @@ -729,10 +542,11 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_size_set_state_size) (duckdb_v2_aggregate_function_size_info_handle info, idx_t size, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_update_get_arg) - (duckdb_v2_aggregate_function_update_info_handle info, uint32_t index, duckdb_v2_vector_handle *vector, + (duckdb_v2_aggregate_function_update_info_handle info, idx_t index, duckdb_v2_vector_handle *vector, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_update_get_arg_count) - (duckdb_v2_aggregate_function_update_info_handle info, uint32_t *count, duckdb_v2_error_info_handle *err); + (duckdb_v2_aggregate_function_update_info_handle info, idx_t *positional_fixed, idx_t *positional_variadic, + idx_t *named_fixed, idx_t *named_variadic, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_update_get_bind_data) (duckdb_v2_aggregate_function_update_info_handle info, void **data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_update_get_row_count) @@ -741,35 +555,29 @@ typedef struct { (duckdb_v2_aggregate_function_update_info_handle info, void ***states, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_update_get_user_data) (duckdb_v2_aggregate_function_update_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_aggregate_function) + (duckdb_v2_connection_handle conn, duckdb_v2_aggregate_function_handle function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_aggregate_function) + (duckdb_v2_extension_handle extension, duckdb_v2_aggregate_function_handle function, + duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_set_property) (duckdb_v2_aggregate_function_handle function, DUCKDB_V2_FUNCTION_PROPERTY_KEY key, DUCKDB_V2_FUNCTION_PROPERTY_VALUE value, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_set_property) (duckdb_v2_scalar_function_handle function, DUCKDB_V2_FUNCTION_PROPERTY_KEY key, DUCKDB_V2_FUNCTION_PROPERTY_VALUE value, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_table_function) + (duckdb_v2_connection_handle conn, duckdb_v2_table_function_handle function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_table_function) + (duckdb_v2_extension_handle extension, duckdb_v2_table_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_table_function_bind_add_result_column) - (duckdb_v2_table_function_bind_info_handle info, duckdb_v2_identifier_t name, duckdb_v2_logical_type_handle type, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_table_function_bind_get_arg_count) - (duckdb_v2_table_function_bind_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_table_function_bind_get_arg_type) - (duckdb_v2_table_function_bind_info_handle info, idx_t index, duckdb_v2_logical_type_handle *type, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_table_function_bind_get_arg_value) - (duckdb_v2_table_function_bind_info_handle info, idx_t index, duckdb_v2_value_handle *value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_table_function_bind_get_user_data) - (duckdb_v2_table_function_bind_info_handle info, void **data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_table_function_bind_set_bind_data) - (duckdb_v2_table_function_bind_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); + (duckdb_v2_table_function_bind_info_handle info, const duckdb_v2_identifier_t *name, + duckdb_v2_logical_type_handle type, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_table_function_bind_set_cardinality) (duckdb_v2_table_function_bind_info_handle info, idx_t cardinality, bool is_exact, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_table_function_create_with_connection) - (duckdb_v2_connection_handle connection, duckdb_v2_table_function_handle *function, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_table_function_create_with_extension) - (duckdb_v2_extension_handle extension, duckdb_v2_table_function_handle *function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_create) + (duckdb_v2_factory_handle factory, duckdb_v2_table_function_handle *function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_table_function_destroy)(duckdb_v2_table_function_handle *function); DUCKDB_V2_ERROR(*duckdb_v2_table_function_exec_get_bind_data) (duckdb_v2_table_function_exec_info_handle info, void **data, duckdb_v2_error_info_handle *err); @@ -809,8 +617,6 @@ typedef struct { (duckdb_v2_table_function_progress_info_handle info, void **data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_table_function_progress_set_progress) (duckdb_v2_table_function_progress_info_handle info, double progress, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_table_function_register) - (duckdb_v2_table_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_bind_callback) (duckdb_v2_table_function_handle function, duckdb_v2_table_function_bind_callback_fn callback, duckdb_v2_error_info_handle *err); @@ -824,17 +630,14 @@ typedef struct { (duckdb_v2_table_function_handle function, duckdb_v2_table_function_init_local_callback_fn callback, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_name) - (duckdb_v2_table_function_handle function, duckdb_v2_str *name, duckdb_v2_error_info_handle *err); + (duckdb_v2_table_function_handle function, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_progress_callback) (duckdb_v2_table_function_handle function, duckdb_v2_table_function_progress_callback_fn callback, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_user_data) (duckdb_v2_table_function_handle function, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_cast_function_create_with_connection) - (duckdb_v2_connection_handle connection, duckdb_v2_cast_function_handle *function, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_cast_function_create_with_extension) - (duckdb_v2_extension_handle extension, duckdb_v2_cast_function_handle *function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_cast_function_create) + (duckdb_v2_factory_handle factory, duckdb_v2_cast_function_handle *function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_cast_function_destroy)(duckdb_v2_cast_function_handle *function); DUCKDB_V2_ERROR(*duckdb_v2_cast_function_exec_get_input) (duckdb_v2_cast_function_exec_info_handle info, duckdb_v2_vector_handle *vector, duckdb_v2_error_info_handle *err); @@ -846,8 +649,6 @@ typedef struct { (duckdb_v2_cast_function_exec_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_cast_function_exec_get_user_data) (duckdb_v2_cast_function_exec_info_handle info, void **data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_cast_function_register) - (duckdb_v2_cast_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_cast_function_set_exec_callback) (duckdb_v2_cast_function_handle function, duckdb_v2_cast_function_exec_callback_fn callback, duckdb_v2_error_info_handle *err); @@ -863,17 +664,27 @@ typedef struct { (duckdb_v2_cast_function_handle function, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_column_data_collection_clear) (duckdb_v2_column_data_collection_handle collection, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_custom_type_create_with_connection) - (duckdb_v2_connection_handle connection, duckdb_v2_custom_type_handle *type, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_custom_type_create_with_extension) - (duckdb_v2_extension_handle extension, duckdb_v2_custom_type_handle *type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_cast_function) + (duckdb_v2_connection_handle conn, duckdb_v2_cast_function_handle function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_custom_type) + (duckdb_v2_connection_handle conn, duckdb_v2_custom_type_handle type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_replacement_scan) + (duckdb_v2_connection_handle conn, duckdb_v2_replacement_scan_handle scan, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_context_get_file_system) + (duckdb_v2_context_handle context, duckdb_v2_file_system_handle *file_system, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_type_create) + (duckdb_v2_factory_handle factory, duckdb_v2_custom_type_handle *type, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_custom_type_destroy)(duckdb_v2_custom_type_handle *type); - DUCKDB_V2_ERROR(*duckdb_v2_custom_type_register) - (duckdb_v2_custom_type_handle type, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_custom_type_set_base_type) (duckdb_v2_custom_type_handle type, duckdb_v2_logical_type_handle base_type, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_custom_type_set_name) - (duckdb_v2_custom_type_handle type, duckdb_v2_identifier_t name, duckdb_v2_error_info_handle *err); + (duckdb_v2_custom_type_handle type, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_cast_function) + (duckdb_v2_extension_handle extension, duckdb_v2_cast_function_handle function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_custom_type) + (duckdb_v2_extension_handle extension, duckdb_v2_custom_type_handle type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_replacement_scan) + (duckdb_v2_extension_handle extension, duckdb_v2_replacement_scan_handle scan, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_file_close)(duckdb_v2_file_handle file, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_file_destroy)(duckdb_v2_file_handle *file); DUCKDB_V2_ERROR(*duckdb_v2_file_open_options_create) @@ -883,7 +694,7 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_file_open_options_set_flag) (duckdb_v2_file_open_options_handle options, DUCKDB_V2_FILE_FLAG flag, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_file_open_options_set_value) - (duckdb_v2_file_open_options_handle options, duckdb_v2_str name, duckdb_v2_value_handle value, + (duckdb_v2_file_open_options_handle options, const duckdb_v2_str *name, duckdb_v2_value_handle value, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_file_read) (duckdb_v2_file_handle file, void *buffer, idx_t buffer_size, idx_t *bytes_read, duckdb_v2_error_info_handle *err); @@ -893,14 +704,9 @@ typedef struct { (duckdb_v2_file_handle file, idx_t position, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_file_size)(duckdb_v2_file_handle file, idx_t *size, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_file_sync)(duckdb_v2_file_handle file, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_file_system_get_from_connection) - (duckdb_v2_connection_handle connection, duckdb_v2_file_system_handle *file_system, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_file_system_get_from_context) - (duckdb_v2_context_handle context, duckdb_v2_file_system_handle *file_system, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_file_system_open) - (duckdb_v2_file_system_handle file_system, duckdb_v2_str file_path, duckdb_v2_file_open_options_handle options, - duckdb_v2_file_handle *file, duckdb_v2_error_info_handle *err); + (duckdb_v2_file_system_handle file_system, const duckdb_v2_str *file_path, + duckdb_v2_file_open_options_handle options, duckdb_v2_file_handle *file, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_file_tell) (duckdb_v2_file_handle file, idx_t *position, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_file_write) @@ -909,6 +715,8 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_file_write_at) (duckdb_v2_file_handle file, const void *buffer, idx_t buffer_size, idx_t location, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_register_replacement_scan) + (duckdb_v2_instance_handle instance, duckdb_v2_replacement_scan_handle scan, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_qname_create) (const duckdb_v2_identifier_t *parts, idx_t part_count, duckdb_v2_qname_handle *name, duckdb_v2_error_info_handle *err); @@ -922,30 +730,25 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_qname_hash) (duckdb_v2_qname_handle name, uint64_t *hash, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_qname_parse) - (duckdb_v2_str text, duckdb_v2_qname_handle *name, duckdb_v2_error_info_handle *err); + (const duckdb_v2_str *text, duckdb_v2_qname_handle *name, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_qname_render) (duckdb_v2_qname_handle name, char *out_text, idx_t out_capacity, idx_t *out_length, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_add_argument) (duckdb_v2_replacement_scan_info_handle info, duckdb_v2_value_handle value, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_add_named_argument) - (duckdb_v2_replacement_scan_info_handle info, duckdb_v2_identifier_t name, duckdb_v2_value_handle value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_create_with_connection) - (duckdb_v2_connection_handle connection, duckdb_v2_replacement_scan_handle *scan, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_create_with_database) - (duckdb_v2_database_handle database, duckdb_v2_replacement_scan_handle *scan, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_create_with_extension) - (duckdb_v2_extension_handle extension, duckdb_v2_replacement_scan_handle *scan, duckdb_v2_error_info_handle *err); + (duckdb_v2_replacement_scan_info_handle info, const duckdb_v2_identifier_t *name, duckdb_v2_value_handle value, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_create) + (duckdb_v2_factory_handle factory, duckdb_v2_replacement_scan_handle *scan, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_replacement_scan_destroy)(duckdb_v2_replacement_scan_handle *scan); DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_get_name) (duckdb_v2_replacement_scan_info_handle info, duckdb_v2_qname_handle *name, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_get_user_data) (duckdb_v2_replacement_scan_info_handle info, void **data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_register) - (duckdb_v2_replacement_scan_handle scan, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_set_alias) - (duckdb_v2_replacement_scan_info_handle info, duckdb_v2_identifier_t alias, duckdb_v2_error_info_handle *err); + (duckdb_v2_replacement_scan_info_handle info, const duckdb_v2_identifier_t *alias, + duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_set_callback) (duckdb_v2_replacement_scan_handle scan, duckdb_v2_replacement_scan_callback_fn callback, duckdb_v2_error_info_handle *err); @@ -955,7 +758,7 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_set_function_name) (duckdb_v2_replacement_scan_info_handle info, duckdb_v2_qname_handle name, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_set_subquery) - (duckdb_v2_replacement_scan_info_handle info, duckdb_v2_str sql, duckdb_v2_error_info_handle *err); + (duckdb_v2_replacement_scan_info_handle info, const duckdb_v2_str *sql, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_set_user_data) (duckdb_v2_replacement_scan_handle scan, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_arrow_exporter_append) @@ -993,6 +796,8 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_connection_describe_table) (duckdb_v2_connection_handle conn, duckdb_v2_qname_handle name, duckdb_v2_table_description_handle *desc, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_copy_function) + (duckdb_v2_connection_handle conn, duckdb_v2_copy_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_copy_from_bind_get_column_count) (duckdb_v2_copy_from_bind_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_copy_from_bind_get_column_name) @@ -1066,16 +871,11 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_copy_from_set_progress_callback) (duckdb_v2_copy_function_handle function, duckdb_v2_copy_from_progress_callback_fn callback, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_copy_function_create_with_connection) - (duckdb_v2_connection_handle connection, duckdb_v2_copy_function_handle *function, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_copy_function_create_with_extension) - (duckdb_v2_extension_handle extension, duckdb_v2_copy_function_handle *function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_copy_function_create) + (duckdb_v2_factory_handle factory, duckdb_v2_copy_function_handle *function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_copy_function_destroy)(duckdb_v2_copy_function_handle *function); - DUCKDB_V2_ERROR(*duckdb_v2_copy_function_register) - (duckdb_v2_copy_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_copy_function_set_name) - (duckdb_v2_copy_function_handle function, duckdb_v2_str *name, duckdb_v2_error_info_handle *err); + (duckdb_v2_copy_function_handle function, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_copy_function_set_user_data) (duckdb_v2_copy_function_handle function, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_copy_to_batch_get_bind_data) @@ -1176,8 +976,10 @@ typedef struct { (duckdb_v2_expression_handle expression, duckdb_v2_logical_type_handle *type, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_expression_get_type) (duckdb_v2_expression_handle expression, DUCKDB_V2_EXPRESSION_TYPE *type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_copy_function) + (duckdb_v2_extension_handle extension, duckdb_v2_copy_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_identifier_render_quoted) - (duckdb_v2_identifier_t name, char *out_text, idx_t out_capacity, idx_t *out_length, + (const duckdb_v2_identifier_t *name, char *out_text, idx_t out_capacity, idx_t *out_length, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_prepared_statement_create) (duckdb_v2_connection_handle conn, duckdb_v2_sql_statement_handle statement, bool require_cacheable, @@ -1189,9 +991,6 @@ typedef struct { duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_prepared_statement_reuses_plan) (duckdb_v2_prepared_statement_handle prepared, bool *out_reuses, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_result_to_arrow_stream) - (duckdb_v2_result_handle *result, idx_t batch_size, struct ArrowArrayStream *out_stream, - duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_table_description_destroy)(duckdb_v2_table_description_handle *desc); DUCKDB_V2_ERROR(*duckdb_v2_table_description_get_column) (duckdb_v2_table_description_handle desc, idx_t index, duckdb_v2_column_description_handle *column, @@ -1247,6 +1046,359 @@ typedef struct { (duckdb_v2_sql_statement_handle statement, duckdb_v2_str *out_text, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_sql_statement_get_type) (duckdb_v2_sql_statement_handle statement, DUCKDB_V2_STATEMENT_TYPE *out_type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_token_iterator_destroy)(duckdb_v2_token_iterator_handle *iterator); + DUCKDB_V2_ERROR(*duckdb_v2_token_iterator_ends_unterminated) + (duckdb_v2_token_iterator_handle iterator, bool *out_ends_unterminated, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_token_iterator_next) + (duckdb_v2_token_iterator_handle iterator, DUCKDB_V2_TOKEN_TYPE *out_type, idx_t *out_start, idx_t *out_length, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_tokenize_sql) + (duckdb_v2_connection_handle conn, const duckdb_v2_str *sql, duckdb_v2_token_iterator_handle *out_iterator, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_attach_options_create) + (duckdb_v2_instance_handle instance, duckdb_v2_attach_options_handle *out_options, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_attach_options_destroy)(duckdb_v2_attach_options_handle *options); + DUCKDB_V2_ERROR(*duckdb_v2_attach_options_set) + (duckdb_v2_attach_options_handle options, const duckdb_v2_identifier_t *key, const duckdb_v2_str *setting, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_set_default) + (duckdb_v2_instance_handle instance, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_get_bind_data) + (duckdb_v2_table_function_partition_data_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_get_global_state) + (duckdb_v2_table_function_partition_data_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_get_local_state) + (duckdb_v2_table_function_partition_data_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_get_partition_column_count) + (duckdb_v2_table_function_partition_data_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_get_partition_column_index) + (duckdb_v2_table_function_partition_data_info_handle info, idx_t index, idx_t *column_index, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_get_user_data) + (duckdb_v2_table_function_partition_data_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_requires_batch_index) + (duckdb_v2_table_function_partition_data_info_handle info, bool *required, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_requires_partition_columns) + (duckdb_v2_table_function_partition_data_info_handle info, bool *required, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_set_batch_index) + (duckdb_v2_table_function_partition_data_info_handle info, idx_t batch_index, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_set_partition_value) + (duckdb_v2_table_function_partition_data_info_handle info, idx_t index, duckdb_v2_value_handle value, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partitioning_get_bind_data) + (duckdb_v2_table_function_partitioning_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partitioning_get_partition_column_count) + (duckdb_v2_table_function_partitioning_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partitioning_get_partition_column_index) + (duckdb_v2_table_function_partitioning_info_handle info, idx_t index, idx_t *column_index, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partitioning_get_user_data) + (duckdb_v2_table_function_partitioning_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partitioning_set_partition_info) + (duckdb_v2_table_function_partitioning_info_handle info, DUCKDB_V2_TABLE_PARTITION_INFO partition_info, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_partition_data_callback) + (duckdb_v2_table_function_handle function, duckdb_v2_table_function_partition_data_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_partitioning_callback) + (duckdb_v2_table_function_handle function, duckdb_v2_table_function_partitioning_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_validate_utf8)(const duckdb_v2_str *text, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_bind_get_arg_count) + (duckdb_v2_function_bind_info_handle info, idx_t *positional_fixed, idx_t *positional_variadic, idx_t *named_fixed, + idx_t *named_variadic, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_bind_get_arg_index) + (duckdb_v2_function_bind_info_handle info, const duckdb_v2_identifier_t *name, idx_t *index, bool *found, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_bind_get_arg_name) + (duckdb_v2_function_bind_info_handle info, idx_t index, duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_bind_get_arg_type) + (duckdb_v2_function_bind_info_handle info, idx_t index, duckdb_v2_logical_type_handle *type, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_bind_get_arg_value) + (duckdb_v2_function_bind_info_handle info, idx_t index, duckdb_v2_value_handle *value, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_bind_get_user_data) + (duckdb_v2_function_bind_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_bind_set_bind_data) + (duckdb_v2_function_bind_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_bind_set_order_preservation) + (duckdb_v2_table_function_bind_info_handle info, DUCKDB_V2_ORDER_PRESERVATION order, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_arrow_result_destroy)(duckdb_v2_arrow_result_handle *result); + DUCKDB_V2_ERROR(*duckdb_v2_arrow_result_drain) + (duckdb_v2_arrow_result_handle result, idx_t *out_rows_changed, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_arrow_result_fetch_array) + (duckdb_v2_arrow_result_handle result, struct ArrowArray *out_array, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_arrow_result_get_result_type) + (duckdb_v2_arrow_result_handle result, DUCKDB_V2_RESULT_TYPE *out_type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_arrow_result_get_schema) + (duckdb_v2_arrow_result_handle result, struct ArrowSchema *out_schema, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_arrow_result_get_statement_type) + (duckdb_v2_arrow_result_handle result, DUCKDB_V2_STATEMENT_TYPE *out_type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_arrow_result_step) + (duckdb_v2_arrow_result_handle result, struct ArrowArray *out_array, DUCKDB_V2_RESULT_STEP_STATUS *out_status, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_arrow_result_to_arrow_c_stream) + (duckdb_v2_arrow_result_handle *result, struct ArrowArrayStream *out_stream, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_arrow_result_wait) + (duckdb_v2_arrow_result_handle result, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_prepared_statement_execute_arrow) + (duckdb_v2_prepared_statement_handle prepared, const duckdb_v2_identifier_t *parameter_names, + const duckdb_v2_value_handle *parameter_values, idx_t parameter_count, idx_t batch_size, + duckdb_v2_arrow_result_handle *out_result, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_statement_execute_arrow) + (duckdb_v2_connection_handle conn, duckdb_v2_sql_statement_handle statement, + const duckdb_v2_identifier_t *parameter_names, const duckdb_v2_value_handle *parameter_values, + idx_t parameter_count, idx_t batch_size, duckdb_v2_arrow_result_handle *out_result, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_resolve_types_set_return_type) + (duckdb_v2_scalar_function_resolve_types_info_handle info, duckdb_v2_logical_type_handle return_type, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_set_resolve_types_callback) + (duckdb_v2_scalar_function_handle function, duckdb_v2_scalar_function_resolve_types_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_get_factory) + (duckdb_v2_connection_handle conn, duckdb_v2_factory_handle *out_factory, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_context_get_factory) + (duckdb_v2_context_handle ctx, duckdb_v2_factory_handle *out_factory, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_get_factory) + (duckdb_v2_instance_handle instance, duckdb_v2_factory_handle *out_factory, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_bind_set_bind_data) + (duckdb_v2_scalar_function_bind_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_config_get_option_value) + (duckdb_v2_config_handle config, const duckdb_v2_identifier_t *name, duckdb_v2_value_handle *out_value, + DUCKDB_V2_SETTING_SCOPE *out_scope, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_config_set_option_text) + (duckdb_v2_config_handle config, const duckdb_v2_identifier_t *name, const duckdb_v2_str *setting, + DUCKDB_V2_SETTING_SCOPE scope, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_config_set_option_value) + (duckdb_v2_config_handle config, const duckdb_v2_identifier_t *name, duckdb_v2_value_handle value, + DUCKDB_V2_SETTING_SCOPE scope, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_get_config) + (duckdb_v2_connection_handle conn, duckdb_v2_config_handle *out_config, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_get_file_system) + (duckdb_v2_connection_handle conn, duckdb_v2_file_system_handle *file_system, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_log) + (duckdb_v2_connection_handle conn, DUCKDB_V2_LOG_LEVEL level, const duckdb_v2_str *log_type, + const duckdb_v2_str *message, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_context_get_config) + (duckdb_v2_context_handle ctx, duckdb_v2_config_handle *out_config, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_get_config) + (duckdb_v2_instance_handle instance, duckdb_v2_config_handle *out_config, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_get_file_system) + (duckdb_v2_instance_handle instance, duckdb_v2_file_system_handle *file_system, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_log) + (duckdb_v2_instance_handle instance, DUCKDB_V2_LOG_LEVEL level, const duckdb_v2_str *log_type, + const duckdb_v2_str *message, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_option_get_default_scope) + (duckdb_v2_option_handle option, DUCKDB_V2_SETTING_SCOPE *out_scope, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_option_supports_scope) + (duckdb_v2_option_handle option, DUCKDB_V2_SETTING_SCOPE scope, bool *out_supported, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_get_docs) + (duckdb_v2_aggregate_function_handle function, duckdb_v2_function_docs_handle *docs, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_pragma_function) + (duckdb_v2_connection_handle conn, duckdb_v2_pragma_function_handle function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_pragma_function) + (duckdb_v2_extension_handle extension, duckdb_v2_pragma_function_handle function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_docs_add_category) + (duckdb_v2_function_docs_handle docs, const duckdb_v2_str *category, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_docs_add_example) + (duckdb_v2_function_docs_handle docs, const duckdb_v2_str *title, const duckdb_v2_str *example, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_docs_set_description) + (duckdb_v2_function_docs_handle docs, const duckdb_v2_str *description, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_pragma_function_create) + (duckdb_v2_factory_handle factory, duckdb_v2_pragma_function_handle *function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_pragma_function_destroy)(duckdb_v2_pragma_function_handle *function); + DUCKDB_V2_ERROR(*duckdb_v2_pragma_function_get_docs) + (duckdb_v2_pragma_function_handle function, duckdb_v2_function_docs_handle *docs, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_pragma_function_get_signature) + (duckdb_v2_pragma_function_handle function, duckdb_v2_function_signature_handle *sig, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_pragma_function_query_set_query) + (duckdb_v2_pragma_function_query_info_handle info, const duckdb_v2_str *query, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_pragma_function_set_exec_callback) + (duckdb_v2_pragma_function_handle function, duckdb_v2_pragma_function_exec_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_pragma_function_set_name) + (duckdb_v2_pragma_function_handle function, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_pragma_function_set_query_callback) + (duckdb_v2_pragma_function_handle function, duckdb_v2_pragma_function_query_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_pragma_function_set_user_data) + (duckdb_v2_pragma_function_handle function, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_get_docs) + (duckdb_v2_scalar_function_handle function, duckdb_v2_function_docs_handle *docs, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_get_docs) + (duckdb_v2_table_function_handle function, duckdb_v2_function_docs_handle *docs, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_custom_option) + (duckdb_v2_connection_handle conn, duckdb_v2_custom_option_handle option, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_create) + (duckdb_v2_factory_handle factory, duckdb_v2_custom_option_handle *option, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_custom_option_destroy)(duckdb_v2_custom_option_handle *option); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_set_default_scope) + (duckdb_v2_custom_option_handle option, DUCKDB_V2_SETTING_SCOPE scope, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_set_default_value) + (duckdb_v2_custom_option_handle option, duckdb_v2_value_handle value, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_set_description) + (duckdb_v2_custom_option_handle option, const duckdb_v2_str *description, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_set_name) + (duckdb_v2_custom_option_handle option, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_set_supports_scope) + (duckdb_v2_custom_option_handle option, DUCKDB_V2_SETTING_SCOPE scope, bool supported, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_set_type) + (duckdb_v2_custom_option_handle option, duckdb_v2_logical_type_handle type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_set_update_callback) + (duckdb_v2_custom_option_handle option, duckdb_v2_custom_option_update_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_set_user_data) + (duckdb_v2_custom_option_handle option, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_update_get_scope) + (duckdb_v2_custom_option_update_info_handle info, DUCKDB_V2_SETTING_SCOPE *scope, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_update_get_user_data) + (duckdb_v2_custom_option_update_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_update_get_value) + (duckdb_v2_custom_option_update_info_handle info, duckdb_v2_value_handle *value, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_update_is_reset) + (duckdb_v2_custom_option_update_info_handle info, bool *is_reset, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_update_set_value) + (duckdb_v2_custom_option_update_info_handle info, duckdb_v2_value_handle value, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_custom_option) + (duckdb_v2_extension_handle extension, duckdb_v2_custom_option_handle option, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_log_sink) + (duckdb_v2_extension_handle extension, duckdb_v2_log_sink_handle sink, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_macro_function) + (duckdb_v2_extension_handle extension, duckdb_v2_macro_function_handle function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_file_system_path_exists) + (duckdb_v2_file_system_handle file_system, const duckdb_v2_str *path, bool *out_exists, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_file_system_path_expand) + (duckdb_v2_file_system_handle file_system, const duckdb_v2_str *path, duckdb_v2_text_sink_fn sink, void *user_data, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_file_system_path_is_remote) + (duckdb_v2_file_system_handle file_system, const duckdb_v2_str *path, bool *out_is_remote, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_register_log_sink) + (duckdb_v2_instance_handle instance, duckdb_v2_log_sink_handle sink, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_log_sink_create) + (duckdb_v2_factory_handle factory, duckdb_v2_log_sink_handle *sink, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_log_sink_destroy)(duckdb_v2_log_sink_handle *sink); + DUCKDB_V2_ERROR(*duckdb_v2_log_sink_set_flush_callback) + (duckdb_v2_log_sink_handle sink, duckdb_v2_log_sink_flush_callback_fn callback, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_log_sink_set_name) + (duckdb_v2_log_sink_handle sink, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_log_sink_set_user_data) + (duckdb_v2_log_sink_handle sink, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_log_sink_set_write_callback) + (duckdb_v2_log_sink_handle sink, duckdb_v2_log_sink_write_callback_fn callback, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_log_sink_write_info_get_connection_id) + (duckdb_v2_log_sink_write_info_handle info, idx_t *id, bool *present, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_log_sink_write_info_get_query_id) + (duckdb_v2_log_sink_write_info_handle info, idx_t *id, bool *present, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_log_sink_write_info_get_thread_id) + (duckdb_v2_log_sink_write_info_handle info, idx_t *id, bool *present, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_log_sink_write_info_get_transaction_id) + (duckdb_v2_log_sink_write_info_handle info, idx_t *id, bool *present, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_macro_function_create) + (duckdb_v2_factory_handle factory, DUCKDB_V2_MACRO_FUNCTION_TYPE type, duckdb_v2_macro_function_handle *function, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_macro_function_destroy)(duckdb_v2_macro_function_handle *function); + DUCKDB_V2_ERROR(*duckdb_v2_macro_function_get_docs) + (duckdb_v2_macro_function_handle function, duckdb_v2_function_docs_handle *docs, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_macro_function_get_signature) + (duckdb_v2_macro_function_handle function, duckdb_v2_function_signature_handle *sig, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_macro_function_set_body) + (duckdb_v2_macro_function_handle function, const duckdb_v2_str *body, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_macro_function_set_name) + (duckdb_v2_macro_function_handle function, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_vector_cast) + (duckdb_v2_factory_handle factory, duckdb_v2_vector_handle source, duckdb_v2_vector_handle target, + DUCKDB_V2_CAST_MODE mode, bool *out_converted, duckdb_v2_error_info_handle *cast_err, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_copy_function_set_file_extension) + (duckdb_v2_copy_function_handle function, const duckdb_v2_str *extension, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_data_chunk_clear)(duckdb_v2_data_chunk_handle chunk, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_file_system_path_glob) + (duckdb_v2_file_system_handle file_system, const duckdb_v2_str *pattern, + duckdb_v2_file_system_path_glob_callback_fn callback, void *user_data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_file_system_path_move) + (duckdb_v2_file_system_handle file_system, const duckdb_v2_str *source, const duckdb_v2_str *target, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_file_system_path_remove) + (duckdb_v2_file_system_handle file_system, const duckdb_v2_str *path, bool *out_removed, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_statement_add_collection) + (duckdb_v2_sql_statement_handle statement, const duckdb_v2_identifier_t *name, + duckdb_v2_column_data_collection_handle collection, const duckdb_v2_identifier_t *column_names, idx_t column_count, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_multi_file_function) + (duckdb_v2_connection_handle conn, duckdb_v2_multi_file_function_handle function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_copy_to_set_statistics_callback) + (duckdb_v2_copy_function_handle function, duckdb_v2_copy_to_statistics_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_copy_to_statistics_get_bind_data) + (duckdb_v2_copy_to_statistics_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_copy_to_statistics_get_init_data) + (duckdb_v2_copy_to_statistics_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_copy_to_statistics_get_user_data) + (duckdb_v2_copy_to_statistics_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_copy_to_statistics_set_file_size) + (duckdb_v2_copy_to_statistics_info_handle info, idx_t file_size_bytes, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_copy_to_statistics_set_row_count) + (duckdb_v2_copy_to_statistics_info_handle info, idx_t row_count, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_data_chunk_get_capacity) + (duckdb_v2_data_chunk_handle chunk, idx_t *out_capacity, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_multi_file_function) + (duckdb_v2_extension_handle extension, duckdb_v2_multi_file_function_handle function, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_multi_file_function_create) + (duckdb_v2_factory_handle factory, duckdb_v2_multi_file_function_handle *function, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_multi_file_function_destroy)(duckdb_v2_multi_file_function_handle *function); + DUCKDB_V2_ERROR(*duckdb_v2_multi_file_function_set_file_extension) + (duckdb_v2_multi_file_function_handle function, duckdb_v2_str *extension, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_multi_file_function_set_name) + (duckdb_v2_multi_file_function_handle function, duckdb_v2_str *name, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_multi_file_function_set_reader_type) + (duckdb_v2_multi_file_function_handle function, duckdb_v2_str *reader_type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_multi_file_function_set_single_file_function) + (duckdb_v2_multi_file_function_handle function, duckdb_v2_str *name, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_claim_batch_get_bind_data) + (duckdb_v2_table_function_claim_batch_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_claim_batch_get_global_state) + (duckdb_v2_table_function_claim_batch_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_claim_batch_get_local_state) + (duckdb_v2_table_function_claim_batch_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_claim_batch_get_user_data) + (duckdb_v2_table_function_claim_batch_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_claim_batch_set_claimed) + (duckdb_v2_table_function_claim_batch_info_handle info, bool claimed, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_claim_batch_callback) + (duckdb_v2_table_function_handle function, duckdb_v2_table_function_claim_batch_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_get_bind_info_get_bind_data) + (duckdb_v2_table_function_get_bind_info_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_get_bind_info_get_user_data) + (duckdb_v2_table_function_get_bind_info_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_get_bind_info_set_column_identifier) + (duckdb_v2_table_function_get_bind_info_info_handle info, idx_t column_index, const idx_t *child_path, + idx_t child_path_length, duckdb_v2_value_handle identifier, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_get_bind_info_set_option) + (duckdb_v2_table_function_get_bind_info_info_handle info, duckdb_v2_str *key, duckdb_v2_value_handle value, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_get_bind_info_callback) + (duckdb_v2_table_function_handle function, duckdb_v2_table_function_get_bind_info_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_factory_create_common_type) + (duckdb_v2_factory_handle factory, const duckdb_v2_logical_type_handle *types, idx_t type_count, + duckdb_v2_logical_type_handle *out_type, duckdb_v2_error_info_handle *err); } duckdb_ext_api_v2; //===--------------------------------------------------------------------===// @@ -1257,15 +1409,12 @@ inline duckdb_ext_api_v2 CreateAPIv2(void) { result.duckdb_v2_arena_allocate = duckdb_v2_arena_allocate; result.duckdb_v2_bignum_decode = duckdb_v2_bignum_decode; result.duckdb_v2_bignum_encode = duckdb_v2_bignum_encode; - result.duckdb_v2_close = duckdb_v2_close; result.duckdb_v2_column_data_collection_append = duckdb_v2_column_data_collection_append; result.duckdb_v2_column_data_collection_append_state_create = duckdb_v2_column_data_collection_append_state_create; result.duckdb_v2_column_data_collection_append_state_destroy = duckdb_v2_column_data_collection_append_state_destroy; result.duckdb_v2_column_data_collection_combine = duckdb_v2_column_data_collection_combine; - result.duckdb_v2_column_data_collection_create_with_connection = - duckdb_v2_column_data_collection_create_with_connection; - result.duckdb_v2_column_data_collection_create_with_context = duckdb_v2_column_data_collection_create_with_context; + result.duckdb_v2_column_data_collection_create = duckdb_v2_column_data_collection_create; result.duckdb_v2_column_data_collection_destroy = duckdb_v2_column_data_collection_destroy; result.duckdb_v2_column_data_collection_reset = duckdb_v2_column_data_collection_reset; result.duckdb_v2_column_data_collection_row_count = duckdb_v2_column_data_collection_row_count; @@ -1278,44 +1427,37 @@ inline duckdb_ext_api_v2 CreateAPIv2(void) { duckdb_v2_column_data_collection_worker_scan_state_create; result.duckdb_v2_column_data_collection_worker_scan_state_destroy = duckdb_v2_column_data_collection_worker_scan_state_destroy; - result.duckdb_v2_connect = duckdb_v2_connect; - result.duckdb_v2_connection_create_type_from_id = duckdb_v2_connection_create_type_from_id; - result.duckdb_v2_connection_create_type_from_name = duckdb_v2_connection_create_type_from_name; - result.duckdb_v2_connection_create_type_from_text = duckdb_v2_connection_create_type_from_text; - result.duckdb_v2_connection_create_type_with_alias = duckdb_v2_connection_create_type_with_alias; + result.duckdb_v2_config_get_option_by_index = duckdb_v2_config_get_option_by_index; + result.duckdb_v2_config_get_option_by_name = duckdb_v2_config_get_option_by_name; + result.duckdb_v2_config_get_option_count = duckdb_v2_config_get_option_count; + result.duckdb_v2_connection_create = duckdb_v2_connection_create; + result.duckdb_v2_connection_destroy = duckdb_v2_connection_destroy; result.duckdb_v2_connection_interrupt = duckdb_v2_connection_interrupt; - result.duckdb_v2_connection_option_get = duckdb_v2_connection_option_get; - result.duckdb_v2_connection_option_get_by_index = duckdb_v2_connection_option_get_by_index; - result.duckdb_v2_connection_option_get_count = duckdb_v2_connection_option_get_count; - result.duckdb_v2_connection_option_set = duckdb_v2_connection_option_set; result.duckdb_v2_connection_progress_get = duckdb_v2_connection_progress_get; - result.duckdb_v2_context_create_type_from_id = duckdb_v2_context_create_type_from_id; - result.duckdb_v2_context_create_type_from_name = duckdb_v2_context_create_type_from_name; - result.duckdb_v2_context_create_type_from_text = duckdb_v2_context_create_type_from_text; - result.duckdb_v2_context_create_type_with_alias = duckdb_v2_context_create_type_with_alias; - result.duckdb_v2_create_environment = duckdb_v2_create_environment; - result.duckdb_v2_data_chunk_copy_with_connection = duckdb_v2_data_chunk_copy_with_connection; - result.duckdb_v2_data_chunk_copy_with_context = duckdb_v2_data_chunk_copy_with_context; + result.duckdb_v2_data_chunk_copy = duckdb_v2_data_chunk_copy; result.duckdb_v2_data_chunk_create = duckdb_v2_data_chunk_create; - result.duckdb_v2_data_chunk_create_with_connection = duckdb_v2_data_chunk_create_with_connection; - result.duckdb_v2_data_chunk_create_with_context = duckdb_v2_data_chunk_create_with_context; result.duckdb_v2_data_chunk_destroy = duckdb_v2_data_chunk_destroy; result.duckdb_v2_data_chunk_get_size = duckdb_v2_data_chunk_get_size; result.duckdb_v2_data_chunk_get_vector = duckdb_v2_data_chunk_get_vector; result.duckdb_v2_data_chunk_get_vector_count = duckdb_v2_data_chunk_get_vector_count; - result.duckdb_v2_database_option_get = duckdb_v2_database_option_get; - result.duckdb_v2_database_option_get_by_index = duckdb_v2_database_option_get_by_index; - result.duckdb_v2_database_option_get_count = duckdb_v2_database_option_get_count; - result.duckdb_v2_database_option_set = duckdb_v2_database_option_set; - result.duckdb_v2_destroy_environment = duckdb_v2_destroy_environment; - result.duckdb_v2_disconnect = duckdb_v2_disconnect; - result.duckdb_v2_environment_database_count = duckdb_v2_environment_database_count; + result.duckdb_v2_environment_create = duckdb_v2_environment_create; + result.duckdb_v2_environment_destroy = duckdb_v2_environment_destroy; + result.duckdb_v2_environment_get_instance_count = duckdb_v2_environment_get_instance_count; result.duckdb_v2_error_info_destroy = duckdb_v2_error_info_destroy; result.duckdb_v2_error_info_get_code = duckdb_v2_error_info_get_code; - result.duckdb_v2_error_info_get_raw_message = duckdb_v2_error_info_get_raw_message; + result.duckdb_v2_error_info_get_raw_text = duckdb_v2_error_info_get_raw_text; result.duckdb_v2_error_info_get_text = duckdb_v2_error_info_get_text; result.duckdb_v2_error_info_set_code = duckdb_v2_error_info_set_code; result.duckdb_v2_error_info_set_text = duckdb_v2_error_info_set_text; + result.duckdb_v2_factory_create_type_from_id = duckdb_v2_factory_create_type_from_id; + result.duckdb_v2_factory_create_type_from_name = duckdb_v2_factory_create_type_from_name; + result.duckdb_v2_factory_create_type_from_text = duckdb_v2_factory_create_type_from_text; + result.duckdb_v2_factory_create_type_with_alias = duckdb_v2_factory_create_type_with_alias; + result.duckdb_v2_instance_attach = duckdb_v2_instance_attach; + result.duckdb_v2_instance_create = duckdb_v2_instance_create; + result.duckdb_v2_instance_create_with_options = duckdb_v2_instance_create_with_options; + result.duckdb_v2_instance_destroy = duckdb_v2_instance_destroy; + result.duckdb_v2_instance_detach = duckdb_v2_instance_detach; result.duckdb_v2_library_version = duckdb_v2_library_version; result.duckdb_v2_logical_type_copy = duckdb_v2_logical_type_copy; result.duckdb_v2_logical_type_destroy = duckdb_v2_logical_type_destroy; @@ -1325,16 +1467,12 @@ inline duckdb_ext_api_v2 CreateAPIv2(void) { result.duckdb_v2_logical_type_get_param_count = duckdb_v2_logical_type_get_param_count; result.duckdb_v2_logical_type_is_equal = duckdb_v2_logical_type_is_equal; result.duckdb_v2_logical_type_to_text = duckdb_v2_logical_type_to_text; - result.duckdb_v2_open = duckdb_v2_open; - result.duckdb_v2_option_create = duckdb_v2_option_create; result.duckdb_v2_option_destroy = duckdb_v2_option_destroy; result.duckdb_v2_option_get_alias = duckdb_v2_option_get_alias; result.duckdb_v2_option_get_alias_count = duckdb_v2_option_get_alias_count; - result.duckdb_v2_option_get_default_setting = duckdb_v2_option_get_default_setting; + result.duckdb_v2_option_get_default_value = duckdb_v2_option_get_default_value; result.duckdb_v2_option_get_description = duckdb_v2_option_get_description; result.duckdb_v2_option_get_name = duckdb_v2_option_get_name; - result.duckdb_v2_option_get_setting = duckdb_v2_option_get_setting; - result.duckdb_v2_option_get_target_scope = duckdb_v2_option_get_target_scope; result.duckdb_v2_parse_sql = duckdb_v2_parse_sql; result.duckdb_v2_result_destroy = duckdb_v2_result_destroy; result.duckdb_v2_result_drain = duckdb_v2_result_drain; @@ -1353,84 +1491,44 @@ inline duckdb_ext_api_v2 CreateAPIv2(void) { result.duckdb_v2_statement_execute = duckdb_v2_statement_execute; result.duckdb_v2_statement_iterator_destroy = duckdb_v2_statement_iterator_destroy; result.duckdb_v2_statement_iterator_next = duckdb_v2_statement_iterator_next; - result.duckdb_v2_value_cast_with_connection = duckdb_v2_value_cast_with_connection; - result.duckdb_v2_value_cast_with_context = duckdb_v2_value_cast_with_context; - result.duckdb_v2_value_create_array_with_connection = duckdb_v2_value_create_array_with_connection; - result.duckdb_v2_value_create_array_with_context = duckdb_v2_value_create_array_with_context; - result.duckdb_v2_value_create_bigint_with_connection = duckdb_v2_value_create_bigint_with_connection; - result.duckdb_v2_value_create_bigint_with_context = duckdb_v2_value_create_bigint_with_context; - result.duckdb_v2_value_create_bignum_with_connection = duckdb_v2_value_create_bignum_with_connection; - result.duckdb_v2_value_create_bignum_with_context = duckdb_v2_value_create_bignum_with_context; - result.duckdb_v2_value_create_bit_with_connection = duckdb_v2_value_create_bit_with_connection; - result.duckdb_v2_value_create_bit_with_context = duckdb_v2_value_create_bit_with_context; - result.duckdb_v2_value_create_blob_with_connection = duckdb_v2_value_create_blob_with_connection; - result.duckdb_v2_value_create_blob_with_context = duckdb_v2_value_create_blob_with_context; - result.duckdb_v2_value_create_bool_with_connection = duckdb_v2_value_create_bool_with_connection; - result.duckdb_v2_value_create_bool_with_context = duckdb_v2_value_create_bool_with_context; - result.duckdb_v2_value_create_date_with_connection = duckdb_v2_value_create_date_with_connection; - result.duckdb_v2_value_create_date_with_context = duckdb_v2_value_create_date_with_context; - result.duckdb_v2_value_create_decimal_with_connection = duckdb_v2_value_create_decimal_with_connection; - result.duckdb_v2_value_create_decimal_with_context = duckdb_v2_value_create_decimal_with_context; - result.duckdb_v2_value_create_double_with_connection = duckdb_v2_value_create_double_with_connection; - result.duckdb_v2_value_create_double_with_context = duckdb_v2_value_create_double_with_context; - result.duckdb_v2_value_create_float_with_connection = duckdb_v2_value_create_float_with_connection; - result.duckdb_v2_value_create_float_with_context = duckdb_v2_value_create_float_with_context; - result.duckdb_v2_value_create_hugeint_with_connection = duckdb_v2_value_create_hugeint_with_connection; - result.duckdb_v2_value_create_hugeint_with_context = duckdb_v2_value_create_hugeint_with_context; - result.duckdb_v2_value_create_int_with_connection = duckdb_v2_value_create_int_with_connection; - result.duckdb_v2_value_create_int_with_context = duckdb_v2_value_create_int_with_context; - result.duckdb_v2_value_create_interval_with_connection = duckdb_v2_value_create_interval_with_connection; - result.duckdb_v2_value_create_interval_with_context = duckdb_v2_value_create_interval_with_context; - result.duckdb_v2_value_create_list_with_connection = duckdb_v2_value_create_list_with_connection; - result.duckdb_v2_value_create_list_with_context = duckdb_v2_value_create_list_with_context; - result.duckdb_v2_value_create_map_with_connection = duckdb_v2_value_create_map_with_connection; - result.duckdb_v2_value_create_map_with_context = duckdb_v2_value_create_map_with_context; + result.duckdb_v2_value_cast = duckdb_v2_value_cast; + result.duckdb_v2_value_create_array = duckdb_v2_value_create_array; + result.duckdb_v2_value_create_bigint = duckdb_v2_value_create_bigint; + result.duckdb_v2_value_create_bignum = duckdb_v2_value_create_bignum; + result.duckdb_v2_value_create_bit = duckdb_v2_value_create_bit; + result.duckdb_v2_value_create_blob = duckdb_v2_value_create_blob; + result.duckdb_v2_value_create_bool = duckdb_v2_value_create_bool; + result.duckdb_v2_value_create_date = duckdb_v2_value_create_date; + result.duckdb_v2_value_create_decimal = duckdb_v2_value_create_decimal; + result.duckdb_v2_value_create_double = duckdb_v2_value_create_double; + result.duckdb_v2_value_create_float = duckdb_v2_value_create_float; + result.duckdb_v2_value_create_hugeint = duckdb_v2_value_create_hugeint; + result.duckdb_v2_value_create_int = duckdb_v2_value_create_int; + result.duckdb_v2_value_create_interval = duckdb_v2_value_create_interval; + result.duckdb_v2_value_create_list = duckdb_v2_value_create_list; + result.duckdb_v2_value_create_map = duckdb_v2_value_create_map; result.duckdb_v2_value_create_null = duckdb_v2_value_create_null; - result.duckdb_v2_value_create_null_with_connection = duckdb_v2_value_create_null_with_connection; - result.duckdb_v2_value_create_null_with_context = duckdb_v2_value_create_null_with_context; - result.duckdb_v2_value_create_smallint_with_connection = duckdb_v2_value_create_smallint_with_connection; - result.duckdb_v2_value_create_smallint_with_context = duckdb_v2_value_create_smallint_with_context; - result.duckdb_v2_value_create_struct_with_connection = duckdb_v2_value_create_struct_with_connection; - result.duckdb_v2_value_create_struct_with_context = duckdb_v2_value_create_struct_with_context; - result.duckdb_v2_value_create_time_ns_with_connection = duckdb_v2_value_create_time_ns_with_connection; - result.duckdb_v2_value_create_time_ns_with_context = duckdb_v2_value_create_time_ns_with_context; - result.duckdb_v2_value_create_time_tz_with_connection = duckdb_v2_value_create_time_tz_with_connection; - result.duckdb_v2_value_create_time_tz_with_context = duckdb_v2_value_create_time_tz_with_context; - result.duckdb_v2_value_create_time_with_connection = duckdb_v2_value_create_time_with_connection; - result.duckdb_v2_value_create_time_with_context = duckdb_v2_value_create_time_with_context; - result.duckdb_v2_value_create_timestamp_ms_with_connection = duckdb_v2_value_create_timestamp_ms_with_connection; - result.duckdb_v2_value_create_timestamp_ms_with_context = duckdb_v2_value_create_timestamp_ms_with_context; - result.duckdb_v2_value_create_timestamp_ns_with_connection = duckdb_v2_value_create_timestamp_ns_with_connection; - result.duckdb_v2_value_create_timestamp_ns_with_context = duckdb_v2_value_create_timestamp_ns_with_context; - result.duckdb_v2_value_create_timestamp_sec_with_connection = duckdb_v2_value_create_timestamp_sec_with_connection; - result.duckdb_v2_value_create_timestamp_sec_with_context = duckdb_v2_value_create_timestamp_sec_with_context; - result.duckdb_v2_value_create_timestamp_tz_ns_with_connection = - duckdb_v2_value_create_timestamp_tz_ns_with_connection; - result.duckdb_v2_value_create_timestamp_tz_ns_with_context = duckdb_v2_value_create_timestamp_tz_ns_with_context; - result.duckdb_v2_value_create_timestamp_tz_with_connection = duckdb_v2_value_create_timestamp_tz_with_connection; - result.duckdb_v2_value_create_timestamp_tz_with_context = duckdb_v2_value_create_timestamp_tz_with_context; - result.duckdb_v2_value_create_timestamp_with_connection = duckdb_v2_value_create_timestamp_with_connection; - result.duckdb_v2_value_create_timestamp_with_context = duckdb_v2_value_create_timestamp_with_context; - result.duckdb_v2_value_create_tinyint_with_connection = duckdb_v2_value_create_tinyint_with_connection; - result.duckdb_v2_value_create_tinyint_with_context = duckdb_v2_value_create_tinyint_with_context; - result.duckdb_v2_value_create_tuple_with_connection = duckdb_v2_value_create_tuple_with_connection; - result.duckdb_v2_value_create_tuple_with_context = duckdb_v2_value_create_tuple_with_context; - result.duckdb_v2_value_create_type_with_connection = duckdb_v2_value_create_type_with_connection; - result.duckdb_v2_value_create_type_with_context = duckdb_v2_value_create_type_with_context; - result.duckdb_v2_value_create_ubigint_with_connection = duckdb_v2_value_create_ubigint_with_connection; - result.duckdb_v2_value_create_ubigint_with_context = duckdb_v2_value_create_ubigint_with_context; - result.duckdb_v2_value_create_uhugeint_with_connection = duckdb_v2_value_create_uhugeint_with_connection; - result.duckdb_v2_value_create_uhugeint_with_context = duckdb_v2_value_create_uhugeint_with_context; - result.duckdb_v2_value_create_uint_with_connection = duckdb_v2_value_create_uint_with_connection; - result.duckdb_v2_value_create_uint_with_context = duckdb_v2_value_create_uint_with_context; - result.duckdb_v2_value_create_usmallint_with_connection = duckdb_v2_value_create_usmallint_with_connection; - result.duckdb_v2_value_create_usmallint_with_context = duckdb_v2_value_create_usmallint_with_context; - result.duckdb_v2_value_create_utinyint_with_connection = duckdb_v2_value_create_utinyint_with_connection; - result.duckdb_v2_value_create_utinyint_with_context = duckdb_v2_value_create_utinyint_with_context; - result.duckdb_v2_value_create_uuid_with_connection = duckdb_v2_value_create_uuid_with_connection; - result.duckdb_v2_value_create_uuid_with_context = duckdb_v2_value_create_uuid_with_context; - result.duckdb_v2_value_create_varchar_with_connection = duckdb_v2_value_create_varchar_with_connection; - result.duckdb_v2_value_create_varchar_with_context = duckdb_v2_value_create_varchar_with_context; + result.duckdb_v2_value_create_smallint = duckdb_v2_value_create_smallint; + result.duckdb_v2_value_create_struct = duckdb_v2_value_create_struct; + result.duckdb_v2_value_create_time = duckdb_v2_value_create_time; + result.duckdb_v2_value_create_time_ns = duckdb_v2_value_create_time_ns; + result.duckdb_v2_value_create_time_tz = duckdb_v2_value_create_time_tz; + result.duckdb_v2_value_create_timestamp = duckdb_v2_value_create_timestamp; + result.duckdb_v2_value_create_timestamp_ms = duckdb_v2_value_create_timestamp_ms; + result.duckdb_v2_value_create_timestamp_ns = duckdb_v2_value_create_timestamp_ns; + result.duckdb_v2_value_create_timestamp_sec = duckdb_v2_value_create_timestamp_sec; + result.duckdb_v2_value_create_timestamp_tz = duckdb_v2_value_create_timestamp_tz; + result.duckdb_v2_value_create_timestamp_tz_ns = duckdb_v2_value_create_timestamp_tz_ns; + result.duckdb_v2_value_create_tinyint = duckdb_v2_value_create_tinyint; + result.duckdb_v2_value_create_tuple = duckdb_v2_value_create_tuple; + result.duckdb_v2_value_create_type = duckdb_v2_value_create_type; + result.duckdb_v2_value_create_ubigint = duckdb_v2_value_create_ubigint; + result.duckdb_v2_value_create_uhugeint = duckdb_v2_value_create_uhugeint; + result.duckdb_v2_value_create_uint = duckdb_v2_value_create_uint; + result.duckdb_v2_value_create_usmallint = duckdb_v2_value_create_usmallint; + result.duckdb_v2_value_create_utinyint = duckdb_v2_value_create_utinyint; + result.duckdb_v2_value_create_uuid = duckdb_v2_value_create_uuid; + result.duckdb_v2_value_create_varchar = duckdb_v2_value_create_varchar; result.duckdb_v2_value_destroy = duckdb_v2_value_destroy; result.duckdb_v2_value_get_bigint = duckdb_v2_value_get_bigint; result.duckdb_v2_value_get_blob = duckdb_v2_value_get_blob; @@ -1484,21 +1582,12 @@ inline duckdb_ext_api_v2 CreateAPIv2(void) { result.duckdb_v2_vector_set_null = duckdb_v2_vector_set_null; result.duckdb_v2_vector_set_size = duckdb_v2_vector_set_size; result.duckdb_v2_vector_set_value = duckdb_v2_vector_set_value; - result.duckdb_v2_aggregate_function_bind_get_arg_count = duckdb_v2_aggregate_function_bind_get_arg_count; - result.duckdb_v2_aggregate_function_bind_get_arg_type = duckdb_v2_aggregate_function_bind_get_arg_type; - result.duckdb_v2_aggregate_function_bind_get_arg_value = duckdb_v2_aggregate_function_bind_get_arg_value; + result.duckdb_v2_connection_register_scalar_function = duckdb_v2_connection_register_scalar_function; result.duckdb_v2_context_log = duckdb_v2_context_log; + result.duckdb_v2_extension_register_scalar_function = duckdb_v2_extension_register_scalar_function; result.duckdb_v2_function_signature_add_parameter = duckdb_v2_function_signature_add_parameter; result.duckdb_v2_function_signature_set_return_type = duckdb_v2_function_signature_set_return_type; - result.duckdb_v2_function_signature_set_varargs = duckdb_v2_function_signature_set_varargs; - result.duckdb_v2_scalar_function_bind_get_arg_count = duckdb_v2_scalar_function_bind_get_arg_count; - result.duckdb_v2_scalar_function_bind_get_arg_type = duckdb_v2_scalar_function_bind_get_arg_type; - result.duckdb_v2_scalar_function_bind_get_arg_value = duckdb_v2_scalar_function_bind_get_arg_value; - result.duckdb_v2_scalar_function_bind_get_user_data = duckdb_v2_scalar_function_bind_get_user_data; - result.duckdb_v2_scalar_function_bind_set_bind_data = duckdb_v2_scalar_function_bind_set_bind_data; - result.duckdb_v2_scalar_function_bind_set_return_type = duckdb_v2_scalar_function_bind_set_return_type; - result.duckdb_v2_scalar_function_create_with_connection = duckdb_v2_scalar_function_create_with_connection; - result.duckdb_v2_scalar_function_create_with_extension = duckdb_v2_scalar_function_create_with_extension; + result.duckdb_v2_scalar_function_create = duckdb_v2_scalar_function_create; result.duckdb_v2_scalar_function_destroy = duckdb_v2_scalar_function_destroy; result.duckdb_v2_scalar_function_exec_get_arg = duckdb_v2_scalar_function_exec_get_arg; result.duckdb_v2_scalar_function_exec_get_arg_count = duckdb_v2_scalar_function_exec_get_arg_count; @@ -1511,22 +1600,18 @@ inline duckdb_ext_api_v2 CreateAPIv2(void) { result.duckdb_v2_scalar_function_init_get_bind_data = duckdb_v2_scalar_function_init_get_bind_data; result.duckdb_v2_scalar_function_init_get_user_data = duckdb_v2_scalar_function_init_get_user_data; result.duckdb_v2_scalar_function_init_set_init_data = duckdb_v2_scalar_function_init_set_init_data; - result.duckdb_v2_scalar_function_register = duckdb_v2_scalar_function_register; result.duckdb_v2_scalar_function_set_bind_callback = duckdb_v2_scalar_function_set_bind_callback; result.duckdb_v2_scalar_function_set_exec_callback = duckdb_v2_scalar_function_set_exec_callback; result.duckdb_v2_scalar_function_set_init_callback = duckdb_v2_scalar_function_set_init_callback; result.duckdb_v2_scalar_function_set_name = duckdb_v2_scalar_function_set_name; result.duckdb_v2_scalar_function_set_user_data = duckdb_v2_scalar_function_set_user_data; - result.duckdb_v2_aggregate_function_bind_get_user_data = duckdb_v2_aggregate_function_bind_get_user_data; - result.duckdb_v2_aggregate_function_bind_set_bind_data = duckdb_v2_aggregate_function_bind_set_bind_data; result.duckdb_v2_aggregate_function_bind_set_return_type = duckdb_v2_aggregate_function_bind_set_return_type; result.duckdb_v2_aggregate_function_combine_get_bind_data = duckdb_v2_aggregate_function_combine_get_bind_data; result.duckdb_v2_aggregate_function_combine_get_sources = duckdb_v2_aggregate_function_combine_get_sources; result.duckdb_v2_aggregate_function_combine_get_state_count = duckdb_v2_aggregate_function_combine_get_state_count; result.duckdb_v2_aggregate_function_combine_get_targets = duckdb_v2_aggregate_function_combine_get_targets; result.duckdb_v2_aggregate_function_combine_get_user_data = duckdb_v2_aggregate_function_combine_get_user_data; - result.duckdb_v2_aggregate_function_create_with_connection = duckdb_v2_aggregate_function_create_with_connection; - result.duckdb_v2_aggregate_function_create_with_extension = duckdb_v2_aggregate_function_create_with_extension; + result.duckdb_v2_aggregate_function_create = duckdb_v2_aggregate_function_create; result.duckdb_v2_aggregate_function_destroy = duckdb_v2_aggregate_function_destroy; result.duckdb_v2_aggregate_function_destroy_get_bind_data = duckdb_v2_aggregate_function_destroy_get_bind_data; result.duckdb_v2_aggregate_function_destroy_get_state_count = duckdb_v2_aggregate_function_destroy_get_state_count; @@ -1545,7 +1630,6 @@ inline duckdb_ext_api_v2 CreateAPIv2(void) { result.duckdb_v2_aggregate_function_init_get_state_count = duckdb_v2_aggregate_function_init_get_state_count; result.duckdb_v2_aggregate_function_init_get_states = duckdb_v2_aggregate_function_init_get_states; result.duckdb_v2_aggregate_function_init_get_user_data = duckdb_v2_aggregate_function_init_get_user_data; - result.duckdb_v2_aggregate_function_register = duckdb_v2_aggregate_function_register; result.duckdb_v2_aggregate_function_set_bind_callback = duckdb_v2_aggregate_function_set_bind_callback; result.duckdb_v2_aggregate_function_set_combine_callback = duckdb_v2_aggregate_function_set_combine_callback; result.duckdb_v2_aggregate_function_set_destroy_callback = duckdb_v2_aggregate_function_set_destroy_callback; @@ -1564,17 +1648,15 @@ inline duckdb_ext_api_v2 CreateAPIv2(void) { result.duckdb_v2_aggregate_function_update_get_row_count = duckdb_v2_aggregate_function_update_get_row_count; result.duckdb_v2_aggregate_function_update_get_states = duckdb_v2_aggregate_function_update_get_states; result.duckdb_v2_aggregate_function_update_get_user_data = duckdb_v2_aggregate_function_update_get_user_data; + result.duckdb_v2_connection_register_aggregate_function = duckdb_v2_connection_register_aggregate_function; + result.duckdb_v2_extension_register_aggregate_function = duckdb_v2_extension_register_aggregate_function; result.duckdb_v2_aggregate_function_set_property = duckdb_v2_aggregate_function_set_property; result.duckdb_v2_scalar_function_set_property = duckdb_v2_scalar_function_set_property; + result.duckdb_v2_connection_register_table_function = duckdb_v2_connection_register_table_function; + result.duckdb_v2_extension_register_table_function = duckdb_v2_extension_register_table_function; result.duckdb_v2_table_function_bind_add_result_column = duckdb_v2_table_function_bind_add_result_column; - result.duckdb_v2_table_function_bind_get_arg_count = duckdb_v2_table_function_bind_get_arg_count; - result.duckdb_v2_table_function_bind_get_arg_type = duckdb_v2_table_function_bind_get_arg_type; - result.duckdb_v2_table_function_bind_get_arg_value = duckdb_v2_table_function_bind_get_arg_value; - result.duckdb_v2_table_function_bind_get_user_data = duckdb_v2_table_function_bind_get_user_data; - result.duckdb_v2_table_function_bind_set_bind_data = duckdb_v2_table_function_bind_set_bind_data; result.duckdb_v2_table_function_bind_set_cardinality = duckdb_v2_table_function_bind_set_cardinality; - result.duckdb_v2_table_function_create_with_connection = duckdb_v2_table_function_create_with_connection; - result.duckdb_v2_table_function_create_with_extension = duckdb_v2_table_function_create_with_extension; + result.duckdb_v2_table_function_create = duckdb_v2_table_function_create; result.duckdb_v2_table_function_destroy = duckdb_v2_table_function_destroy; result.duckdb_v2_table_function_exec_get_bind_data = duckdb_v2_table_function_exec_get_bind_data; result.duckdb_v2_table_function_exec_get_global_state = duckdb_v2_table_function_exec_get_global_state; @@ -1595,7 +1677,6 @@ inline duckdb_ext_api_v2 CreateAPIv2(void) { result.duckdb_v2_table_function_progress_get_global_state = duckdb_v2_table_function_progress_get_global_state; result.duckdb_v2_table_function_progress_get_user_data = duckdb_v2_table_function_progress_get_user_data; result.duckdb_v2_table_function_progress_set_progress = duckdb_v2_table_function_progress_set_progress; - result.duckdb_v2_table_function_register = duckdb_v2_table_function_register; result.duckdb_v2_table_function_set_bind_callback = duckdb_v2_table_function_set_bind_callback; result.duckdb_v2_table_function_set_exec_callback = duckdb_v2_table_function_set_exec_callback; result.duckdb_v2_table_function_set_init_global_callback = duckdb_v2_table_function_set_init_global_callback; @@ -1603,27 +1684,30 @@ inline duckdb_ext_api_v2 CreateAPIv2(void) { result.duckdb_v2_table_function_set_name = duckdb_v2_table_function_set_name; result.duckdb_v2_table_function_set_progress_callback = duckdb_v2_table_function_set_progress_callback; result.duckdb_v2_table_function_set_user_data = duckdb_v2_table_function_set_user_data; - result.duckdb_v2_cast_function_create_with_connection = duckdb_v2_cast_function_create_with_connection; - result.duckdb_v2_cast_function_create_with_extension = duckdb_v2_cast_function_create_with_extension; + result.duckdb_v2_cast_function_create = duckdb_v2_cast_function_create; result.duckdb_v2_cast_function_destroy = duckdb_v2_cast_function_destroy; result.duckdb_v2_cast_function_exec_get_input = duckdb_v2_cast_function_exec_get_input; result.duckdb_v2_cast_function_exec_get_mode = duckdb_v2_cast_function_exec_get_mode; result.duckdb_v2_cast_function_exec_get_output = duckdb_v2_cast_function_exec_get_output; result.duckdb_v2_cast_function_exec_get_row_count = duckdb_v2_cast_function_exec_get_row_count; result.duckdb_v2_cast_function_exec_get_user_data = duckdb_v2_cast_function_exec_get_user_data; - result.duckdb_v2_cast_function_register = duckdb_v2_cast_function_register; result.duckdb_v2_cast_function_set_exec_callback = duckdb_v2_cast_function_set_exec_callback; result.duckdb_v2_cast_function_set_implicit_cast_cost = duckdb_v2_cast_function_set_implicit_cast_cost; result.duckdb_v2_cast_function_set_source_type = duckdb_v2_cast_function_set_source_type; result.duckdb_v2_cast_function_set_target_type = duckdb_v2_cast_function_set_target_type; result.duckdb_v2_cast_function_set_user_data = duckdb_v2_cast_function_set_user_data; result.duckdb_v2_column_data_collection_clear = duckdb_v2_column_data_collection_clear; - result.duckdb_v2_custom_type_create_with_connection = duckdb_v2_custom_type_create_with_connection; - result.duckdb_v2_custom_type_create_with_extension = duckdb_v2_custom_type_create_with_extension; + result.duckdb_v2_connection_register_cast_function = duckdb_v2_connection_register_cast_function; + result.duckdb_v2_connection_register_custom_type = duckdb_v2_connection_register_custom_type; + result.duckdb_v2_connection_register_replacement_scan = duckdb_v2_connection_register_replacement_scan; + result.duckdb_v2_context_get_file_system = duckdb_v2_context_get_file_system; + result.duckdb_v2_custom_type_create = duckdb_v2_custom_type_create; result.duckdb_v2_custom_type_destroy = duckdb_v2_custom_type_destroy; - result.duckdb_v2_custom_type_register = duckdb_v2_custom_type_register; result.duckdb_v2_custom_type_set_base_type = duckdb_v2_custom_type_set_base_type; result.duckdb_v2_custom_type_set_name = duckdb_v2_custom_type_set_name; + result.duckdb_v2_extension_register_cast_function = duckdb_v2_extension_register_cast_function; + result.duckdb_v2_extension_register_custom_type = duckdb_v2_extension_register_custom_type; + result.duckdb_v2_extension_register_replacement_scan = duckdb_v2_extension_register_replacement_scan; result.duckdb_v2_file_close = duckdb_v2_file_close; result.duckdb_v2_file_destroy = duckdb_v2_file_destroy; result.duckdb_v2_file_open_options_create = duckdb_v2_file_open_options_create; @@ -1635,12 +1719,11 @@ inline duckdb_ext_api_v2 CreateAPIv2(void) { result.duckdb_v2_file_seek = duckdb_v2_file_seek; result.duckdb_v2_file_size = duckdb_v2_file_size; result.duckdb_v2_file_sync = duckdb_v2_file_sync; - result.duckdb_v2_file_system_get_from_connection = duckdb_v2_file_system_get_from_connection; - result.duckdb_v2_file_system_get_from_context = duckdb_v2_file_system_get_from_context; result.duckdb_v2_file_system_open = duckdb_v2_file_system_open; result.duckdb_v2_file_tell = duckdb_v2_file_tell; result.duckdb_v2_file_write = duckdb_v2_file_write; result.duckdb_v2_file_write_at = duckdb_v2_file_write_at; + result.duckdb_v2_instance_register_replacement_scan = duckdb_v2_instance_register_replacement_scan; result.duckdb_v2_qname_create = duckdb_v2_qname_create; result.duckdb_v2_qname_destroy = duckdb_v2_qname_destroy; result.duckdb_v2_qname_equals = duckdb_v2_qname_equals; @@ -1651,13 +1734,10 @@ inline duckdb_ext_api_v2 CreateAPIv2(void) { result.duckdb_v2_qname_render = duckdb_v2_qname_render; result.duckdb_v2_replacement_scan_add_argument = duckdb_v2_replacement_scan_add_argument; result.duckdb_v2_replacement_scan_add_named_argument = duckdb_v2_replacement_scan_add_named_argument; - result.duckdb_v2_replacement_scan_create_with_connection = duckdb_v2_replacement_scan_create_with_connection; - result.duckdb_v2_replacement_scan_create_with_database = duckdb_v2_replacement_scan_create_with_database; - result.duckdb_v2_replacement_scan_create_with_extension = duckdb_v2_replacement_scan_create_with_extension; + result.duckdb_v2_replacement_scan_create = duckdb_v2_replacement_scan_create; result.duckdb_v2_replacement_scan_destroy = duckdb_v2_replacement_scan_destroy; result.duckdb_v2_replacement_scan_get_name = duckdb_v2_replacement_scan_get_name; result.duckdb_v2_replacement_scan_get_user_data = duckdb_v2_replacement_scan_get_user_data; - result.duckdb_v2_replacement_scan_register = duckdb_v2_replacement_scan_register; result.duckdb_v2_replacement_scan_set_alias = duckdb_v2_replacement_scan_set_alias; result.duckdb_v2_replacement_scan_set_callback = duckdb_v2_replacement_scan_set_callback; result.duckdb_v2_replacement_scan_set_collection = duckdb_v2_replacement_scan_set_collection; @@ -1680,6 +1760,7 @@ inline duckdb_ext_api_v2 CreateAPIv2(void) { result.duckdb_v2_column_description_has_default = duckdb_v2_column_description_has_default; result.duckdb_v2_column_description_has_generated = duckdb_v2_column_description_has_generated; result.duckdb_v2_connection_describe_table = duckdb_v2_connection_describe_table; + result.duckdb_v2_connection_register_copy_function = duckdb_v2_connection_register_copy_function; result.duckdb_v2_copy_from_bind_get_column_count = duckdb_v2_copy_from_bind_get_column_count; result.duckdb_v2_copy_from_bind_get_column_name = duckdb_v2_copy_from_bind_get_column_name; result.duckdb_v2_copy_from_bind_get_column_type = duckdb_v2_copy_from_bind_get_column_type; @@ -1712,10 +1793,8 @@ inline duckdb_ext_api_v2 CreateAPIv2(void) { result.duckdb_v2_copy_from_set_init_global_callback = duckdb_v2_copy_from_set_init_global_callback; result.duckdb_v2_copy_from_set_init_local_callback = duckdb_v2_copy_from_set_init_local_callback; result.duckdb_v2_copy_from_set_progress_callback = duckdb_v2_copy_from_set_progress_callback; - result.duckdb_v2_copy_function_create_with_connection = duckdb_v2_copy_function_create_with_connection; - result.duckdb_v2_copy_function_create_with_extension = duckdb_v2_copy_function_create_with_extension; + result.duckdb_v2_copy_function_create = duckdb_v2_copy_function_create; result.duckdb_v2_copy_function_destroy = duckdb_v2_copy_function_destroy; - result.duckdb_v2_copy_function_register = duckdb_v2_copy_function_register; result.duckdb_v2_copy_function_set_name = duckdb_v2_copy_function_set_name; result.duckdb_v2_copy_function_set_user_data = duckdb_v2_copy_function_set_user_data; result.duckdb_v2_copy_to_batch_get_bind_data = duckdb_v2_copy_to_batch_get_bind_data; @@ -1761,12 +1840,12 @@ inline duckdb_ext_api_v2 CreateAPIv2(void) { result.duckdb_v2_expression_get_child_count = duckdb_v2_expression_get_child_count; result.duckdb_v2_expression_get_return_type = duckdb_v2_expression_get_return_type; result.duckdb_v2_expression_get_type = duckdb_v2_expression_get_type; + result.duckdb_v2_extension_register_copy_function = duckdb_v2_extension_register_copy_function; result.duckdb_v2_identifier_render_quoted = duckdb_v2_identifier_render_quoted; result.duckdb_v2_prepared_statement_create = duckdb_v2_prepared_statement_create; result.duckdb_v2_prepared_statement_destroy = duckdb_v2_prepared_statement_destroy; result.duckdb_v2_prepared_statement_execute = duckdb_v2_prepared_statement_execute; result.duckdb_v2_prepared_statement_reuses_plan = duckdb_v2_prepared_statement_reuses_plan; - result.duckdb_v2_result_to_arrow_stream = duckdb_v2_result_to_arrow_stream; result.duckdb_v2_table_description_destroy = duckdb_v2_table_description_destroy; result.duckdb_v2_table_description_get_column = duckdb_v2_table_description_get_column; result.duckdb_v2_table_description_get_column_count = duckdb_v2_table_description_get_column_count; @@ -1799,6 +1878,176 @@ inline duckdb_ext_api_v2 CreateAPIv2(void) { result.duckdb_v2_sql_statement_get_parameter_name = duckdb_v2_sql_statement_get_parameter_name; result.duckdb_v2_sql_statement_get_text = duckdb_v2_sql_statement_get_text; result.duckdb_v2_sql_statement_get_type = duckdb_v2_sql_statement_get_type; + result.duckdb_v2_token_iterator_destroy = duckdb_v2_token_iterator_destroy; + result.duckdb_v2_token_iterator_ends_unterminated = duckdb_v2_token_iterator_ends_unterminated; + result.duckdb_v2_token_iterator_next = duckdb_v2_token_iterator_next; + result.duckdb_v2_tokenize_sql = duckdb_v2_tokenize_sql; + result.duckdb_v2_attach_options_create = duckdb_v2_attach_options_create; + result.duckdb_v2_attach_options_destroy = duckdb_v2_attach_options_destroy; + result.duckdb_v2_attach_options_set = duckdb_v2_attach_options_set; + result.duckdb_v2_instance_set_default = duckdb_v2_instance_set_default; + result.duckdb_v2_table_function_partition_data_get_bind_data = + duckdb_v2_table_function_partition_data_get_bind_data; + result.duckdb_v2_table_function_partition_data_get_global_state = + duckdb_v2_table_function_partition_data_get_global_state; + result.duckdb_v2_table_function_partition_data_get_local_state = + duckdb_v2_table_function_partition_data_get_local_state; + result.duckdb_v2_table_function_partition_data_get_partition_column_count = + duckdb_v2_table_function_partition_data_get_partition_column_count; + result.duckdb_v2_table_function_partition_data_get_partition_column_index = + duckdb_v2_table_function_partition_data_get_partition_column_index; + result.duckdb_v2_table_function_partition_data_get_user_data = + duckdb_v2_table_function_partition_data_get_user_data; + result.duckdb_v2_table_function_partition_data_requires_batch_index = + duckdb_v2_table_function_partition_data_requires_batch_index; + result.duckdb_v2_table_function_partition_data_requires_partition_columns = + duckdb_v2_table_function_partition_data_requires_partition_columns; + result.duckdb_v2_table_function_partition_data_set_batch_index = + duckdb_v2_table_function_partition_data_set_batch_index; + result.duckdb_v2_table_function_partition_data_set_partition_value = + duckdb_v2_table_function_partition_data_set_partition_value; + result.duckdb_v2_table_function_partitioning_get_bind_data = duckdb_v2_table_function_partitioning_get_bind_data; + result.duckdb_v2_table_function_partitioning_get_partition_column_count = + duckdb_v2_table_function_partitioning_get_partition_column_count; + result.duckdb_v2_table_function_partitioning_get_partition_column_index = + duckdb_v2_table_function_partitioning_get_partition_column_index; + result.duckdb_v2_table_function_partitioning_get_user_data = duckdb_v2_table_function_partitioning_get_user_data; + result.duckdb_v2_table_function_partitioning_set_partition_info = + duckdb_v2_table_function_partitioning_set_partition_info; + result.duckdb_v2_table_function_set_partition_data_callback = duckdb_v2_table_function_set_partition_data_callback; + result.duckdb_v2_table_function_set_partitioning_callback = duckdb_v2_table_function_set_partitioning_callback; + result.duckdb_v2_validate_utf8 = duckdb_v2_validate_utf8; + result.duckdb_v2_function_bind_get_arg_count = duckdb_v2_function_bind_get_arg_count; + result.duckdb_v2_function_bind_get_arg_index = duckdb_v2_function_bind_get_arg_index; + result.duckdb_v2_function_bind_get_arg_name = duckdb_v2_function_bind_get_arg_name; + result.duckdb_v2_function_bind_get_arg_type = duckdb_v2_function_bind_get_arg_type; + result.duckdb_v2_function_bind_get_arg_value = duckdb_v2_function_bind_get_arg_value; + result.duckdb_v2_function_bind_get_user_data = duckdb_v2_function_bind_get_user_data; + result.duckdb_v2_function_bind_set_bind_data = duckdb_v2_function_bind_set_bind_data; + result.duckdb_v2_table_function_bind_set_order_preservation = duckdb_v2_table_function_bind_set_order_preservation; + result.duckdb_v2_arrow_result_destroy = duckdb_v2_arrow_result_destroy; + result.duckdb_v2_arrow_result_drain = duckdb_v2_arrow_result_drain; + result.duckdb_v2_arrow_result_fetch_array = duckdb_v2_arrow_result_fetch_array; + result.duckdb_v2_arrow_result_get_result_type = duckdb_v2_arrow_result_get_result_type; + result.duckdb_v2_arrow_result_get_schema = duckdb_v2_arrow_result_get_schema; + result.duckdb_v2_arrow_result_get_statement_type = duckdb_v2_arrow_result_get_statement_type; + result.duckdb_v2_arrow_result_step = duckdb_v2_arrow_result_step; + result.duckdb_v2_arrow_result_to_arrow_c_stream = duckdb_v2_arrow_result_to_arrow_c_stream; + result.duckdb_v2_arrow_result_wait = duckdb_v2_arrow_result_wait; + result.duckdb_v2_prepared_statement_execute_arrow = duckdb_v2_prepared_statement_execute_arrow; + result.duckdb_v2_statement_execute_arrow = duckdb_v2_statement_execute_arrow; + result.duckdb_v2_scalar_function_resolve_types_set_return_type = + duckdb_v2_scalar_function_resolve_types_set_return_type; + result.duckdb_v2_scalar_function_set_resolve_types_callback = duckdb_v2_scalar_function_set_resolve_types_callback; + result.duckdb_v2_connection_get_factory = duckdb_v2_connection_get_factory; + result.duckdb_v2_context_get_factory = duckdb_v2_context_get_factory; + result.duckdb_v2_instance_get_factory = duckdb_v2_instance_get_factory; + result.duckdb_v2_scalar_function_bind_set_bind_data = duckdb_v2_scalar_function_bind_set_bind_data; + result.duckdb_v2_config_get_option_value = duckdb_v2_config_get_option_value; + result.duckdb_v2_config_set_option_text = duckdb_v2_config_set_option_text; + result.duckdb_v2_config_set_option_value = duckdb_v2_config_set_option_value; + result.duckdb_v2_connection_get_config = duckdb_v2_connection_get_config; + result.duckdb_v2_connection_get_file_system = duckdb_v2_connection_get_file_system; + result.duckdb_v2_connection_log = duckdb_v2_connection_log; + result.duckdb_v2_context_get_config = duckdb_v2_context_get_config; + result.duckdb_v2_instance_get_config = duckdb_v2_instance_get_config; + result.duckdb_v2_instance_get_file_system = duckdb_v2_instance_get_file_system; + result.duckdb_v2_instance_log = duckdb_v2_instance_log; + result.duckdb_v2_option_get_default_scope = duckdb_v2_option_get_default_scope; + result.duckdb_v2_option_supports_scope = duckdb_v2_option_supports_scope; + result.duckdb_v2_aggregate_function_get_docs = duckdb_v2_aggregate_function_get_docs; + result.duckdb_v2_connection_register_pragma_function = duckdb_v2_connection_register_pragma_function; + result.duckdb_v2_extension_register_pragma_function = duckdb_v2_extension_register_pragma_function; + result.duckdb_v2_function_docs_add_category = duckdb_v2_function_docs_add_category; + result.duckdb_v2_function_docs_add_example = duckdb_v2_function_docs_add_example; + result.duckdb_v2_function_docs_set_description = duckdb_v2_function_docs_set_description; + result.duckdb_v2_pragma_function_create = duckdb_v2_pragma_function_create; + result.duckdb_v2_pragma_function_destroy = duckdb_v2_pragma_function_destroy; + result.duckdb_v2_pragma_function_get_docs = duckdb_v2_pragma_function_get_docs; + result.duckdb_v2_pragma_function_get_signature = duckdb_v2_pragma_function_get_signature; + result.duckdb_v2_pragma_function_query_set_query = duckdb_v2_pragma_function_query_set_query; + result.duckdb_v2_pragma_function_set_exec_callback = duckdb_v2_pragma_function_set_exec_callback; + result.duckdb_v2_pragma_function_set_name = duckdb_v2_pragma_function_set_name; + result.duckdb_v2_pragma_function_set_query_callback = duckdb_v2_pragma_function_set_query_callback; + result.duckdb_v2_pragma_function_set_user_data = duckdb_v2_pragma_function_set_user_data; + result.duckdb_v2_scalar_function_get_docs = duckdb_v2_scalar_function_get_docs; + result.duckdb_v2_table_function_get_docs = duckdb_v2_table_function_get_docs; + result.duckdb_v2_connection_register_custom_option = duckdb_v2_connection_register_custom_option; + result.duckdb_v2_custom_option_create = duckdb_v2_custom_option_create; + result.duckdb_v2_custom_option_destroy = duckdb_v2_custom_option_destroy; + result.duckdb_v2_custom_option_set_default_scope = duckdb_v2_custom_option_set_default_scope; + result.duckdb_v2_custom_option_set_default_value = duckdb_v2_custom_option_set_default_value; + result.duckdb_v2_custom_option_set_description = duckdb_v2_custom_option_set_description; + result.duckdb_v2_custom_option_set_name = duckdb_v2_custom_option_set_name; + result.duckdb_v2_custom_option_set_supports_scope = duckdb_v2_custom_option_set_supports_scope; + result.duckdb_v2_custom_option_set_type = duckdb_v2_custom_option_set_type; + result.duckdb_v2_custom_option_set_update_callback = duckdb_v2_custom_option_set_update_callback; + result.duckdb_v2_custom_option_set_user_data = duckdb_v2_custom_option_set_user_data; + result.duckdb_v2_custom_option_update_get_scope = duckdb_v2_custom_option_update_get_scope; + result.duckdb_v2_custom_option_update_get_user_data = duckdb_v2_custom_option_update_get_user_data; + result.duckdb_v2_custom_option_update_get_value = duckdb_v2_custom_option_update_get_value; + result.duckdb_v2_custom_option_update_is_reset = duckdb_v2_custom_option_update_is_reset; + result.duckdb_v2_custom_option_update_set_value = duckdb_v2_custom_option_update_set_value; + result.duckdb_v2_extension_register_custom_option = duckdb_v2_extension_register_custom_option; + result.duckdb_v2_extension_register_log_sink = duckdb_v2_extension_register_log_sink; + result.duckdb_v2_extension_register_macro_function = duckdb_v2_extension_register_macro_function; + result.duckdb_v2_file_system_path_exists = duckdb_v2_file_system_path_exists; + result.duckdb_v2_file_system_path_expand = duckdb_v2_file_system_path_expand; + result.duckdb_v2_file_system_path_is_remote = duckdb_v2_file_system_path_is_remote; + result.duckdb_v2_instance_register_log_sink = duckdb_v2_instance_register_log_sink; + result.duckdb_v2_log_sink_create = duckdb_v2_log_sink_create; + result.duckdb_v2_log_sink_destroy = duckdb_v2_log_sink_destroy; + result.duckdb_v2_log_sink_set_flush_callback = duckdb_v2_log_sink_set_flush_callback; + result.duckdb_v2_log_sink_set_name = duckdb_v2_log_sink_set_name; + result.duckdb_v2_log_sink_set_user_data = duckdb_v2_log_sink_set_user_data; + result.duckdb_v2_log_sink_set_write_callback = duckdb_v2_log_sink_set_write_callback; + result.duckdb_v2_log_sink_write_info_get_connection_id = duckdb_v2_log_sink_write_info_get_connection_id; + result.duckdb_v2_log_sink_write_info_get_query_id = duckdb_v2_log_sink_write_info_get_query_id; + result.duckdb_v2_log_sink_write_info_get_thread_id = duckdb_v2_log_sink_write_info_get_thread_id; + result.duckdb_v2_log_sink_write_info_get_transaction_id = duckdb_v2_log_sink_write_info_get_transaction_id; + result.duckdb_v2_macro_function_create = duckdb_v2_macro_function_create; + result.duckdb_v2_macro_function_destroy = duckdb_v2_macro_function_destroy; + result.duckdb_v2_macro_function_get_docs = duckdb_v2_macro_function_get_docs; + result.duckdb_v2_macro_function_get_signature = duckdb_v2_macro_function_get_signature; + result.duckdb_v2_macro_function_set_body = duckdb_v2_macro_function_set_body; + result.duckdb_v2_macro_function_set_name = duckdb_v2_macro_function_set_name; + result.duckdb_v2_vector_cast = duckdb_v2_vector_cast; + result.duckdb_v2_copy_function_set_file_extension = duckdb_v2_copy_function_set_file_extension; + result.duckdb_v2_data_chunk_clear = duckdb_v2_data_chunk_clear; + result.duckdb_v2_file_system_path_glob = duckdb_v2_file_system_path_glob; + result.duckdb_v2_file_system_path_move = duckdb_v2_file_system_path_move; + result.duckdb_v2_file_system_path_remove = duckdb_v2_file_system_path_remove; + result.duckdb_v2_statement_add_collection = duckdb_v2_statement_add_collection; + result.duckdb_v2_connection_register_multi_file_function = duckdb_v2_connection_register_multi_file_function; + result.duckdb_v2_copy_to_set_statistics_callback = duckdb_v2_copy_to_set_statistics_callback; + result.duckdb_v2_copy_to_statistics_get_bind_data = duckdb_v2_copy_to_statistics_get_bind_data; + result.duckdb_v2_copy_to_statistics_get_init_data = duckdb_v2_copy_to_statistics_get_init_data; + result.duckdb_v2_copy_to_statistics_get_user_data = duckdb_v2_copy_to_statistics_get_user_data; + result.duckdb_v2_copy_to_statistics_set_file_size = duckdb_v2_copy_to_statistics_set_file_size; + result.duckdb_v2_copy_to_statistics_set_row_count = duckdb_v2_copy_to_statistics_set_row_count; + result.duckdb_v2_data_chunk_get_capacity = duckdb_v2_data_chunk_get_capacity; + result.duckdb_v2_extension_register_multi_file_function = duckdb_v2_extension_register_multi_file_function; + result.duckdb_v2_multi_file_function_create = duckdb_v2_multi_file_function_create; + result.duckdb_v2_multi_file_function_destroy = duckdb_v2_multi_file_function_destroy; + result.duckdb_v2_multi_file_function_set_file_extension = duckdb_v2_multi_file_function_set_file_extension; + result.duckdb_v2_multi_file_function_set_name = duckdb_v2_multi_file_function_set_name; + result.duckdb_v2_multi_file_function_set_reader_type = duckdb_v2_multi_file_function_set_reader_type; + result.duckdb_v2_multi_file_function_set_single_file_function = + duckdb_v2_multi_file_function_set_single_file_function; + result.duckdb_v2_table_function_claim_batch_get_bind_data = duckdb_v2_table_function_claim_batch_get_bind_data; + result.duckdb_v2_table_function_claim_batch_get_global_state = + duckdb_v2_table_function_claim_batch_get_global_state; + result.duckdb_v2_table_function_claim_batch_get_local_state = duckdb_v2_table_function_claim_batch_get_local_state; + result.duckdb_v2_table_function_claim_batch_get_user_data = duckdb_v2_table_function_claim_batch_get_user_data; + result.duckdb_v2_table_function_claim_batch_set_claimed = duckdb_v2_table_function_claim_batch_set_claimed; + result.duckdb_v2_table_function_set_claim_batch_callback = duckdb_v2_table_function_set_claim_batch_callback; + result.duckdb_v2_table_function_get_bind_info_get_bind_data = duckdb_v2_table_function_get_bind_info_get_bind_data; + result.duckdb_v2_table_function_get_bind_info_get_user_data = duckdb_v2_table_function_get_bind_info_get_user_data; + result.duckdb_v2_table_function_get_bind_info_set_column_identifier = + duckdb_v2_table_function_get_bind_info_set_column_identifier; + result.duckdb_v2_table_function_get_bind_info_set_option = duckdb_v2_table_function_get_bind_info_set_option; + result.duckdb_v2_table_function_set_get_bind_info_callback = duckdb_v2_table_function_set_get_bind_info_callback; + result.duckdb_v2_factory_create_common_type = duckdb_v2_factory_create_common_type; return result; } diff --git a/src/duckdb/src/include/duckdb/main/chunk_scan_state.hpp b/src/duckdb/src/include/duckdb/main/chunk_scan_state.hpp deleted file mode 100644 index ef568415e..000000000 --- a/src/duckdb/src/include/duckdb/main/chunk_scan_state.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "duckdb/common/vector.hpp" -#include "duckdb/common/unique_ptr.hpp" -#include "duckdb/common/error_data.hpp" - -namespace duckdb { - -class DataChunk; - -//! Abstract chunk fetcher -class ChunkScanState { -public: - explicit ChunkScanState(); - virtual ~ChunkScanState(); - -public: - ChunkScanState(const ChunkScanState &other) = delete; - ChunkScanState(ChunkScanState &&other) = default; - ChunkScanState &operator=(const ChunkScanState &other) = delete; - ChunkScanState &operator=(ChunkScanState &&other) = default; - -public: - virtual bool LoadNextChunk(ErrorData &error) = 0; - virtual bool HasError() const = 0; - virtual ErrorData &GetError() = 0; - virtual const vector &Types() const = 0; - virtual const vector &Names() const = 0; - idx_t CurrentOffset() const; - idx_t RemainingInChunk() const; - DataChunk &CurrentChunk(); - bool ChunkIsEmpty() const; - bool Finished() const; - bool ScanStarted() const; - void IncreaseOffset(idx_t increment, bool unsafe = false); - -protected: - idx_t offset = 0; - bool finished = false; - unique_ptr current_chunk; -}; - -} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/chunk_scan_state/batched_data_collection.hpp b/src/duckdb/src/include/duckdb/main/chunk_scan_state/batched_data_collection.hpp deleted file mode 100644 index fbacfc295..000000000 --- a/src/duckdb/src/include/duckdb/main/chunk_scan_state/batched_data_collection.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "duckdb/main/chunk_scan_state.hpp" -#include "duckdb/common/types/batched_data_collection.hpp" -#include "duckdb/common/error_data.hpp" - -namespace duckdb { - -class BatchCollectionChunkScanState : public ChunkScanState { -public: - BatchCollectionChunkScanState(BatchedDataCollection &collection, BatchedChunkIteratorRange &range, - ClientContext &context); - ~BatchCollectionChunkScanState() override; - -public: - BatchCollectionChunkScanState(const BatchCollectionChunkScanState &other) = delete; - BatchCollectionChunkScanState &operator=(const BatchCollectionChunkScanState &other) = delete; - BatchCollectionChunkScanState(BatchCollectionChunkScanState &&other) = default; - -public: - bool LoadNextChunk(ErrorData &error) override; - bool HasError() const override; - ErrorData &GetError() override; - const vector &Types() const override; - const vector &Names() const override; - -private: - void InternalLoad(ErrorData &error); - -private: - BatchedDataCollection &collection; - BatchedChunkScanState state; -}; - -} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/chunk_scan_state/query_result.hpp b/src/duckdb/src/include/duckdb/main/chunk_scan_state/query_result.hpp deleted file mode 100644 index c6d5160d7..000000000 --- a/src/duckdb/src/include/duckdb/main/chunk_scan_state/query_result.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "duckdb/main/chunk_scan_state.hpp" -#include "duckdb/common/error_data.hpp" - -namespace duckdb { - -class QueryResult; - -class QueryResultChunkScanState : public ChunkScanState { -public: - explicit QueryResultChunkScanState(QueryResult &result); - ~QueryResultChunkScanState() override; - -public: - bool LoadNextChunk(ErrorData &error) override; - bool HasError() const override; - ErrorData &GetError() override; - const vector &Types() const override; - const vector &Names() const override; - -private: - bool InternalLoad(ErrorData &error); - -private: - QueryResult &result; -}; - -} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/client_config.hpp b/src/duckdb/src/include/duckdb/main/client_config.hpp index 2109a538f..cddfde387 100644 --- a/src/duckdb/src/include/duckdb/main/client_config.hpp +++ b/src/duckdb/src/include/duckdb/main/client_config.hpp @@ -15,6 +15,7 @@ #include "duckdb/common/enums/output_type.hpp" #include "duckdb/common/progress_bar/progress_bar.hpp" #include "duckdb/common/types/value.hpp" +#include "duckdb/common/optional.hpp" #include "duckdb/common/enums/profiling_coverage.hpp" #include "duckdb/main/user_settings.hpp" @@ -64,7 +65,7 @@ struct ClientConfig { vector> replacement_scans; //! The maximum amount of memory to keep buffered in a streaming query result. Default: 10mb. - idx_t max_streaming_buffer_size = 10 * 1024 * 1024; + idx_t max_streaming_buffer_size = idx_t(10) * 1024 * 1024; //! The maximum memory for query intermediates (sorts, hash tables) per connection (in bytes). Default: Global //! memory limit. @@ -86,10 +87,13 @@ struct ClientConfig { //! Function that is used to create the result collector for a materialized result. get_result_collector_t get_result_collector = nullptr; + optional current_dialect; //! The (ordered) list of grammar extensions currently used by the parser - case_insensitive_set_t active_grammar_extensions; + vector active_grammar_extensions; //! The compiled grammar active for the connection shared_ptr cached_grammar; + //! The grammar of the database this client is CONNECT-ed to; unset when not connected + shared_ptr connected_grammar; public: static ClientConfig &GetConfig(ClientContext &context); diff --git a/src/duckdb/src/include/duckdb/main/client_context.hpp b/src/duckdb/src/include/duckdb/main/client_context.hpp index e64e91ff4..62ef1312e 100644 --- a/src/duckdb/src/include/duckdb/main/client_context.hpp +++ b/src/duckdb/src/include/duckdb/main/client_context.hpp @@ -13,8 +13,10 @@ #include "duckdb/common/atomic.hpp" #include "duckdb/common/deque.hpp" #include "duckdb/common/optional_idx.hpp" -#include "duckdb/common/enums/pending_execution_result.hpp" +#include "duckdb/common/enums/query_result_state.hpp" #include "duckdb/common/enums/prepared_statement_mode.hpp" +#include "duckdb/common/enums/connection_type.hpp" +#include "duckdb/common/enums/result_lifetime.hpp" #include "duckdb/common/error_data.hpp" #include "duckdb/common/pair.hpp" #include "duckdb/common/unordered_set.hpp" @@ -23,12 +25,12 @@ #include "duckdb/main/client_context_state.hpp" #include "duckdb/main/client_properties.hpp" #include "duckdb/main/external_dependencies.hpp" -#include "duckdb/main/pending_query_result.hpp" +#include "duckdb/main/query_result.hpp" #include "duckdb/main/table_description.hpp" #include "duckdb/planner/expression/bound_parameter_data.hpp" #include "duckdb/transaction/transaction_context.hpp" #include "duckdb/common/query_context.hpp" -#include "duckdb/common/query_parameters.hpp" +#include "duckdb/main/query_parameters.hpp" namespace duckdb { class Logger; @@ -43,8 +45,8 @@ class FileOpener; class LogicalOperator; class PreparedStatement; class PreparedStatementData; -class StreamQueryResult; class StatementIterator; +struct PreprocessedStatement; class Relation; class BufferedFileWriter; class QueryProfiler; @@ -57,14 +59,10 @@ class SimpleBufferedData; class BufferedData; struct ClientData; class ClientContextState; +class Connection; +class PhysicalTransaction; class RegisteredStateManager; - -struct PendingQueryParameters { - //! Prepared statement parameters (if any) - optional_ptr> parameters; - //! Whether a stream/buffer-managed result should be allowed - QueryParameters query_parameters; -}; +struct TransactionInfo; //! A statement parameter: identifier ($1 -> "1"), binding index, and inferred type (UNKNOWN if not inferred). struct StatementParameter { @@ -80,6 +78,9 @@ struct StatementSignature { vector types; vector parameters; StatementProperties properties; + //! The statement expands into several; its result is known only once execution binds the statement that + //! produces it + bool metadata_deferred = false; }; //! Interrupt state for the client context @@ -88,12 +89,24 @@ enum class ClientInterruptState : uint8_t { NOT_INTERRUPTED, INTERRUPTED, INTERR //! The ClientContext holds information relevant to the current client session //! during execution class ClientContext : public enable_shared_from_this { - friend class PendingQueryResult; // LockContext - friend class BufferedData; // ExecuteTaskInternal - friend class SimpleBufferedData; // ExecuteTaskInternal - friend class BatchedBufferedData; // ExecuteTaskInternal - friend class StreamQueryResult; // LockContext + friend class QueryResult; // LockContext + friend class BufferedData; // ExecuteTaskInternal, PollInternal + friend class SimpleBufferedData; // ExecuteTaskInternal, PollInternal + friend class BatchedBufferedData; // ExecuteTaskInternal, PollInternal + friend class ResultStreamBase; // drives a statement that expands before its result buffer exists friend class ConnectionManager; + friend class Connection; + friend class PhysicalTransaction; + +public: + //! The error of a statement submitted while a result that no call ended holds the connection + static constexpr const char *OPEN_RESULT_ERROR = + "connection has an open result; drain or destroy it before starting a new query"; + //! The error of a query that ran past max_execution_time + static constexpr const char *QUERY_DEADLINE_ERROR = "Query exceeded maximum execution time"; + //! The error of a statement that expands into several, given parameters + static constexpr const char *EXPANDING_PARAMETERS_ERROR = + "parameters are not supported for a statement that expands into several"; public: DUCKDB_API explicit ClientContext(shared_ptr db); @@ -131,11 +144,14 @@ class ClientContext : public enable_shared_from_this { //! directly to disambiguate "never connected" from "was connected, target was detached elsewhere"). DUCKDB_API shared_ptr TryGetConnectedCatalog() const; + bool HasActiveTransaction() const { + return transaction.HasActiveTransaction(); + } MetaTransaction &ActiveTransaction() { return transaction.ActiveTransaction(); } - //! Interrupt execution of a query + //! Interrupts the running query. Async-signal-safe: sets a flag and nothing else DUCKDB_API void Interrupt(); DUCKDB_API bool IsInterrupted() const; DUCKDB_API void ClearInterrupt(); @@ -145,40 +161,48 @@ class ClientContext : public enable_shared_from_this { //! Check for interrupt or timeout, throws InterruptException if triggered DUCKDB_API void InterruptCheck() const; + //! InterruptCheck that reads the clock on every call, for waits that block between checks + DUCKDB_API void BlockingInterruptCheck() const; //! Enable query profiling DUCKDB_API void EnableProfiling(); //! Disable query profiling DUCKDB_API void DisableProfiling(); - //! Issue a query, returning a QueryResult. The QueryResult can be either a StreamQueryResult or a - //! MaterializedQueryResult. The StreamQueryResult will only be returned in the case of a successful SELECT - //! statement. + //! Blocking. Runs the query to completion and returns its handle. The result is retained DUCKDB_API unique_ptr Query(const string &query, QueryParameters query_parameters); - DUCKDB_API unique_ptr Query(unique_ptr statement, QueryParameters query_parameters); - - //! Issues a query to the database and returns a Pending Query Result. Note that "query" may only contain - //! a single statement. - DUCKDB_API unique_ptr PendingQuery(const string &query, QueryParameters query_parameters); - //! Issues a query to the database and returns a Pending Query Result - DUCKDB_API unique_ptr PendingQuery(unique_ptr statement, - QueryParameters query_parameters); - - //! Create a pending query with a list of parameters - DUCKDB_API unique_ptr PendingQuery(unique_ptr statement, - identifier_map_t &values, - QueryParameters query_parameters); - DUCKDB_API unique_ptr - PendingQuery(const string &query, identifier_map_t &values, QueryParameters query_parameters); - DUCKDB_API unique_ptr PendingQuery(const string &query, PendingQueryParameters parameters); - - //! Run a statement that was generated internally rather than parsed from user SQL. Statement verification - //! is skipped, and the client context lock is held for the entire duration of the query. + DUCKDB_API unique_ptr Query(unique_ptr statement, + const QueryParameters &query_parameters); + //! As above, in the given result format + DUCKDB_API unique_ptr Query(const string &query, shared_ptr format); + DUCKDB_API unique_ptr Query(unique_ptr statement, shared_ptr format); + + //! Non-blocking. Submits the query and returns its handle. The engine runs it iff threads - external_threads > 0, + //! but produces no data until the caller either calls a materializing method on the handle or opens a + //! QueryResultStream on it. The query may only contain a single statement. + DUCKDB_API unique_ptr Submit(const string &query, const QueryParameters &query_parameters); + //! Non-blocking. As above, for a parsed statement + DUCKDB_API unique_ptr Submit(unique_ptr statement, + const QueryParameters &query_parameters); + //! Non-blocking. As above, in the given result format + DUCKDB_API unique_ptr Submit(const string &query, shared_ptr format); + DUCKDB_API unique_ptr Submit(unique_ptr statement, shared_ptr format); + + //! Non-blocking. As above, for bound parameter values + DUCKDB_API unique_ptr Submit(unique_ptr statement, + identifier_map_t &values, + QueryParameters query_parameters); + DUCKDB_API unique_ptr Submit(const string &query, identifier_map_t &values, + QueryParameters query_parameters); + + //! Blocking. Runs a statement that was generated internally rather than parsed from user SQL. Statement + //! verification is skipped, and the client context lock is held for the entire duration of the query. DUCKDB_API unique_ptr RunInternalStatement(unique_ptr statement, - const PendingQueryParameters ¶meters); - //! Same as RunInternalStatement, but returns a pending query result that the caller drives - DUCKDB_API unique_ptr PendingInternalStatement(unique_ptr statement, - const PendingQueryParameters ¶meters); + const QueryParameters ¶meters); + //! Non-blocking. Same as RunInternalStatement, but returns the handle of the submitted query, which the caller + //! drives + DUCKDB_API unique_ptr SubmitInternalStatement(unique_ptr statement, + const QueryParameters ¶meters); //! Destroy the client context DUCKDB_API void Destroy(); @@ -200,20 +224,25 @@ class ClientContext : public enable_shared_from_this { //! Internal function for try bind relation. It does not require a client-context lock. DUCKDB_API void InternalTryBindRelation(Relation &relation, vector &result_columns); - //! Execute a relation - DUCKDB_API unique_ptr PendingQuery(const shared_ptr &relation, - QueryParameters query_parameters); + //! Non-blocking. Submits a relation and returns its handle + DUCKDB_API unique_ptr Submit(const shared_ptr &relation, + const QueryParameters &query_parameters); + //! Blocking. Runs a relation to completion and returns its handle DUCKDB_API unique_ptr Execute(const shared_ptr &relation); - //! Prepare a query + //! Prepare a query. A statement that expands into several is kept as written and expanded at each execution, so + //! its metadata is known only from the result DUCKDB_API unique_ptr Prepare(const string &query); - //! Directly prepare a SQL statement + //! Directly prepare a SQL statement, as Prepare(query) does DUCKDB_API unique_ptr Prepare(unique_ptr statement); //! Deallocate the prepared statement with the given name - does nothing if it does not exist DUCKDB_API void RemovePreparedStatement(const string &name); //! Bind a statement and return its signature, without building a PreparedStatement, optimizing, or //! executing. Read-only: binding touches no in-flight query state, so a live result survives. Throws on error. + //! A statement that expands into several reports metadata_deferred with a placeholder column DUCKDB_API StatementSignature BindStatement(unique_ptr statement); + //! Throws the error a new statement gets while a result that no call ended holds the connection + DUCKDB_API void VerifyNoOpenResult(); //! Gets current percentage of the query's progress, returns 0 in case the progress bar is disabled. DUCKDB_API QueryProgress GetQueryProgress(); @@ -221,17 +250,10 @@ class ClientContext : public enable_shared_from_this { //! Register function in the temporary schema DUCKDB_API void RegisterFunction(CreateFunctionInfo &info); - //! Iterate a query's statements as a StatementIterator (iterator-style API). The caller drives - //! Peek() + GetStatement() to walk through ready-to-execute statements one by one + //! Iterate the user statements of a script as parsed. The submission of each one expands it, so a statement that + //! the engine rewrites into several is still one statement here DUCKDB_API StatementIterator IterateStatements(const string &query); - //! Preprocess a peel of parse-facing statements into engine-facing ones (PRAGMA reparse, - //! MULTI_STATEMENT unpack, transaction wrapping), replacing `buffer` in place. Acquires the - //! context lock internally when `lock` is null (callers that do not already hold it, e.g. the - //! shell). Drives StatementIterator's preprocessing. - DUCKDB_API void PreprocessStatements(vector> &buffer, - optional_ptr lock = nullptr); - //! Extract the logical plan of a query DUCKDB_API unique_ptr ExtractPlan(const string &query); @@ -245,13 +267,20 @@ class ClientContext : public enable_shared_from_this { //! Equivalent to CURRENT_SETTING(key) SQL function. DUCKDB_API SettingLookupResult TryGetCurrentSetting(const Identifier &key, Value &result) const; + //! Typed variant, leaves result untouched if not found + template + SettingLookupResult TryGetCurrentSetting(const Identifier &key, TYPE &result) const { + Value output; + auto lookup_result = TryGetCurrentSetting(key, output); + if (lookup_result) { + result = output.GetValue(); + } + return lookup_result; + } //! Returns the value of the current setting set by the user - if the user has set it. DUCKDB_API SettingLookupResult TryGetCurrentUserSetting(idx_t setting_index, Value &result) const; - //! Returns the parser options for this client context - DUCKDB_API ParserOptions GetParserOptions(); - - //! Whether or not the given result object (streaming query result or pending query result) is active + //! Whether or not the given result object is the connection's open result DUCKDB_API bool IsActiveResult(ClientContextLock &lock, BaseQueryResult &result); //! Returns the current executor @@ -265,6 +294,10 @@ class ClientContext : public enable_shared_from_this { connection_t GetConnectionId() const; + ConnectionType GetConnectionType() const { + return connection_type; + } + //! Fetch the set of tables names of the query. //! Returns the fully qualified, escaped table names, if qualified is set to true, //! else returns the not qualified, not escaped table names. @@ -281,61 +314,131 @@ class ClientContext : public enable_shared_from_this { DUCKDB_API LogicalType ParseLogicalType(const string &type); private: - //! Issues a query to the database and returns a Pending Query Result - unique_ptr PendingQueryInternal(ClientContextLock &lock, unique_ptr statement, - const PendingQueryParameters ¶meters, bool verify = true); - unique_ptr ExecutePendingQueryInternal(ClientContextLock &lock, PendingQueryResult &query); - - //! Parse statements from a query + friend class Parser; + friend class ParseIterator; + ParserOptions GetParserOptions(); + + //! Runs a transaction statement without going through the local query processing pipeline. + void RunTransactionStatement(const TransactionInfo &info); + //! Same as RunTransactionStatement, but does not obtain a lock or route CONNECT statements. + void RunTransactionStatementInternal(const TransactionInfo &info); + + //! Whether a submission settles its result as retained before the first task is scheduled. A caller that + //! completes the query itself settles it, so no producer parks for a decision it has already made + enum class Retention : uint8_t { UNDECIDED, RETAINED_AT_SUBMISSION }; + + //! Submits a query to the database and returns its handle + unique_ptr SubmitInternal(ClientContextLock &lock, unique_ptr statement, + const QueryParameters ¶meters, bool verify = true, + Retention retention = Retention::UNDECIDED); + //! Drives a submitted query to completion and retains its result + unique_ptr CompleteInternal(ClientContextLock &lock, unique_ptr result); + //! Drives a query whose collector builds its own result object, and hands that object out. Null + //! when the query failed: the error is on the handle + unique_ptr CompleteDelegatedInternal(ClientContextLock &lock, QueryResult &result); + + //! The user statements of a query, as parsed: a submission expands each one vector> ParseStatementsInternal(ClientContextLock &lock, const string &query); + //! Expands a user statement for a caller that binds it without running it; a statement that expands into + //! several is refused with the given message + unique_ptr ExpandSingleStatementInternal(ClientContextLock &lock, unique_ptr statement, + const char *refusal); + //! A verification that runs a statement of its own, like a prepared or an explained copy, is skipped when + //! allow_nested_statements is false void StatementVerification(ClientContextLock &lock, unique_ptr &statement, - PendingQueryParameters query_parameters); + QueryParameters query_parameters, bool allow_nested_statements = true); - void InitialCleanup(ClientContextLock &lock); + //! Whether a result that no call ended holds the connection, so that a new statement is refused + bool OpenResultHoldsConnection(ClientContextLock &lock); + //! The refusal of a statement submitted while a result holds the connection; otherwise null, after abandoning + //! what is left of the previous query + template + [[nodiscard]] unique_ptr InitialCleanup(ClientContextLock &lock); + //! Abandons the query of an open result that no call ended, as Close would, and resets the interrupted flag + void AbandonActiveQuery(ClientContextLock &lock); //! Internal clean up, does not lock. Caller must hold the context_lock. void CleanupInternal(ClientContextLock &lock, BaseQueryResult *result = nullptr, bool invalidate_transaction = false); - unique_ptr PendingStatement(ClientContextLock &lock, unique_ptr statement, - const PendingQueryParameters ¶meters); - unique_ptr PendingPreparedStatementInternal(ClientContextLock &lock, - shared_ptr statement_data_p, - const PendingQueryParameters ¶meters); + //! Ends the active query as abandoned: nothing it did is committed + void AbortInternal(ClientContextLock &lock); + unique_ptr SubmitStatement(ClientContextLock &lock, unique_ptr statement, + const QueryParameters ¶meters, bool verify, Retention retention); + //! Starts a bound statement of the active query. Returns its handle; for a statement of a query that expands + //! into several, null, unless a result collector built its own result object + unique_ptr SubmitPreparedStatementInternal(ClientContextLock &lock, + shared_ptr statement_data_p, + const QueryParameters ¶meters); void CheckIfPreparedStatementIsExecutable(PreparedStatementData &statement); //! Internally prepare a SQL statement. Caller must hold the context_lock. shared_ptr CreatePreparedStatement(ClientContextLock &lock, unique_ptr statement, - PendingQueryParameters parameters); - unique_ptr PendingStatementInternal(ClientContextLock &lock, unique_ptr statement, - const PendingQueryParameters ¶meters); + const QueryParameters ¶meters); + unique_ptr SubmitStatementInternal(ClientContextLock &lock, unique_ptr statement, + const QueryParameters ¶meters); + //! Expands a user statement into the statements it runs as, without the statements of its transaction handling + PreprocessedStatement PreprocessStatementInternal(ClientContextLock &lock, unique_ptr statement); + //! Runs the statements of a query that expands up to the one producing its result, inside the submission, so + //! that a result collector building its own result object serves that statement. Returns that object, if any + unique_ptr RunToResultStatementInternal(ClientContextLock &lock, QueryResult &handle); unique_ptr RunStatementInternal(ClientContextLock &lock, unique_ptr statement, - const PendingQueryParameters ¶meters, bool verify = true); + const QueryParameters ¶meters, bool verify = true); unique_ptr PrepareInternal(ClientContextLock &lock, unique_ptr statement); void LogQueryInternal(ClientContextLock &lock, const string &query); - unique_ptr FetchResultInternal(ClientContextLock &lock, PendingQueryResult &pending); - unique_ptr LockContext(); - void BeginQueryInternal(ClientContextLock &lock, const SQLStatement &statement); + //! Begins the query of a user statement, whose first statement runs next + void BeginQueryInternal(ClientContextLock &lock, PreprocessedStatement statements, + const QueryParameters ¶meters, Retention retention); + //! Begins one statement of the active query: its query number, its text and its query log line + void BeginStatementInternal(ClientContextLock &lock, const string &query); + //! Refreshes the logger for the statement that begins + void RefreshLoggerInternal(); + //! Ends the statement of a query that expands into several once it finished, and starts the next one + QueryResultState StartNextStatementInternal(ClientContextLock &lock); + //! Ends the statement that finished and starts the next one. Returns the result object a collector built for it, + //! which only a statement started inside the submission may have + unique_ptr AdvanceStatementInternal(ClientContextLock &lock); + bool QueryDeadlinePassed() const; + //! Ends the statement of the active query that finished, when another one follows + void EndStatementInternal(ClientContextLock &lock); ErrorData EndQueryInternal(ClientContextLock &lock, bool success, bool invalidate_transaction, optional_ptr previous_error); + //! The result of the finished query. For a statement that expands into several and returns no rows, that is an + //! empty result in the query's format, the way a statement that returns nothing reports + unique_ptr TakeFinishedResultInternal(ClientContextLock &lock); + //! A finished result without rows in `format`, the chunk format when null + unique_ptr EmptyResultInternal(StatementType statement_type, StatementProperties properties, + vector types, vector names, + shared_ptr format); + //! Records the consumer's retention decision for a result statement that has not started. The first decision + //! stands; returns it + ResultLifetime DecideResultLifetimeInternal(ClientContextLock &lock, ResultLifetime decision); //! Wait until a task is available to execute void WaitForTask(ClientContextLock &lock, BaseQueryResult &result); - PendingExecutionResult ExecuteTaskInternal(ClientContextLock &lock, BaseQueryResult &result, bool dry_run = false); + //! Run one partial task slice of the open result on the calling thread + QueryResultState ExecuteTaskInternal(ClientContextLock &lock, BaseQueryResult &result); + //! Report the execution state of the open result without running any task + QueryResultState PollInternal(ClientContextLock &lock, BaseQueryResult &result); + //! Record the error on the result, end the query and report EXECUTION_ERROR + QueryResultState FailQueryInternal(ClientContextLock &lock, BaseQueryResult &result, ErrorData error); + void UpdateProgressInternal(QueryResultState state); - unique_ptr PendingQueryInternal(ClientContextLock &, const shared_ptr &relation, - QueryParameters query_parameters); + unique_ptr SubmitInternal(ClientContextLock &, const shared_ptr &relation, + const QueryParameters &query_parameters); template unique_ptr ErrorResult(ErrorData error, const string &query = string()); shared_ptr CreatePreparedStatementInternal(ClientContextLock &lock, unique_ptr statement, - PendingQueryParameters parameters); + const QueryParameters ¶meters); bool ErrorInvalidatesTransaction(ExceptionType type); + //! Whether an error ends the active query by invalidating the transaction it runs in + bool ErrorInvalidatesQueryTransaction(ExceptionType type); private: //! Lock on using the ClientContext in parallel @@ -346,6 +449,8 @@ class ClientContext : public enable_shared_from_this { QueryProgress query_progress; //! The connection corresponding to this client context connection_t connection_id; + //! Type of connection (USER or INTERNAL) + ConnectionType connection_type = ConnectionType::USER; //! Routing target for SQL execution while CONNECT-ed (CONNECT/DISCONNECT). When is_connected is //! true and connected_to_database can be locked, the chokepoint dispatches non-control SQL via //! `Catalog::RemoteExecute(string)` and wraps the returned TableRef into a SelectStatement. diff --git a/src/duckdb/src/include/duckdb/main/client_context_state.hpp b/src/duckdb/src/include/duckdb/main/client_context_state.hpp index be459bf95..240dd0a1d 100644 --- a/src/duckdb/src/include/duckdb/main/client_context_state.hpp +++ b/src/duckdb/src/include/duckdb/main/client_context_state.hpp @@ -8,6 +8,7 @@ #pragma once +#include "duckdb/common/atomic.hpp" #include "duckdb/common/enums/prepared_statement_mode.hpp" #include "duckdb/common/exception/transaction_exception.hpp" #include "duckdb/common/optional_ptr.hpp" @@ -21,7 +22,6 @@ class ErrorData; class MetaTransaction; class PreparedStatementData; class SQLStatement; -struct PendingQueryParameters; class RegisteredStateManager; enum class RebindQueryInfo { DO_NOT_REBIND, ATTEMPT_TO_REBIND }; @@ -101,6 +101,7 @@ class RegisteredStateManager { } auto cache = make_shared_ptr(std::forward(args)...); registered_state[key] = cache; + InvalidateSnapshot(); return cache; } @@ -116,26 +117,74 @@ class RegisteredStateManager { void Insert(const string &key, shared_ptr state_p) { lock_guard l(lock); - registered_state.insert(make_pair(key, std::move(state_p))); + if (registered_state.insert(make_pair(key, std::move(state_p))).second) { + InvalidateSnapshot(); + } } void Remove(const string &key) { lock_guard l(lock); - registered_state.erase(key); + if (registered_state.erase(key)) { + InvalidateSnapshot(); + } } vector> States() { lock_guard l(lock); + return StatesInternal(); + } + + //! Invokes callbacks outside the registry lock, retaining snapshot ownership throughout dispatch. + template + void ForEachState(STATE_CALLBACK &&callback) { + auto states = StatesSnapshot(); + if (!states) { + return; + } + for (auto &state : *states) { + callback(*state); + } + } + +private: + shared_ptr>> StatesSnapshot() { + if (!has_states.load(std::memory_order_acquire)) { + return nullptr; + } + auto snapshot = state_snapshot.atomic_load(); + if (snapshot) { + return snapshot; + } + lock_guard l(lock); + snapshot = state_snapshot; + if (!snapshot && !registered_state.empty()) { + snapshot = make_shared_ptr>>(StatesInternal()); + state_snapshot.atomic_store(snapshot); + } + return snapshot; + } + + vector> StatesInternal() { vector> states; + states.reserve(registered_state.size()); for (auto &entry : registered_state) { states.push_back(entry.second); } return states; } + //! Called with the registry lock held after a successful mutation. + void InvalidateSnapshot() { + state_snapshot.atomic_store(nullptr); + has_states.store(!registered_state.empty(), std::memory_order_release); + } + private: mutex lock; unordered_map> registered_state; + shared_ptr>> state_snapshot; + //! Avoid atomic shared_ptr loads for empty registries. + atomic has_states {false}; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/client_data.hpp b/src/duckdb/src/include/duckdb/main/client_data.hpp index 44b4b71d0..405594c57 100644 --- a/src/duckdb/src/include/duckdb/main/client_data.hpp +++ b/src/duckdb/src/include/duckdb/main/client_data.hpp @@ -44,6 +44,9 @@ struct ClientData { //! The catalog search path. unique_ptr catalog_search_path; + //! The instance's default database as it was when this client connected; later DatabaseManager::SetDefaultDatabase + //! calls only affect new clients. Empty when there was none. + Identifier default_database; //! The file opener of the client context. unique_ptr file_opener; diff --git a/src/duckdb/src/include/duckdb/main/config.hpp b/src/duckdb/src/include/duckdb/main/config.hpp index 56f1fe3b9..757da6cd5 100644 --- a/src/duckdb/src/include/duckdb/main/config.hpp +++ b/src/duckdb/src/include/duckdb/main/config.hpp @@ -29,6 +29,7 @@ #include "duckdb/function/replacement_scan.hpp" #include "duckdb/storage/compression/bitpacking.hpp" #include "duckdb/function/encoding_function.hpp" +#include "duckdb/main/extension/linked_extension_registry.hpp" #include "duckdb/main/setting_info.hpp" #include "duckdb/execution/index/index_type_set.hpp" #include "duckdb/logging/logging.hpp" @@ -39,6 +40,7 @@ #include "duckdb/common/enums/debug_order_verification.hpp" namespace duckdb { +class ExternalExtensionProvider; class ArrowTypeExtension; struct ArrowExtensionMetadata; struct ArrowTypeExtensionSet; @@ -52,10 +54,6 @@ class ClientContext; class DuckDB; //! An extension linked into the binary, and how to load it into a database. -struct LinkedExtension { - string name; - std::function load; -}; class ErrorManager; class CompressionFunction; class TableFunctionRef; @@ -92,6 +90,10 @@ struct DBConfigOptions { idx_t checkpoint_wal_size = 1 << 24; //! Whether extensions should be loaded on start-up bool load_extensions = true; + //! Where automatic installs go when neither autoinstall_extension_repository nor custom_extension_repository is + //! set; empty means the core repository. The local_extension_repository capability sets it to its build's + //! repository + string default_autoinstall_repository; //! The maximum memory used by the database system (in bytes). Default: 80% of System available memory idx_t maximum_memory = DConstants::INVALID_INDEX; //! The maximum size of the 'temp_directory' folder when set (in bytes). Default: 90% of available disk space. @@ -100,6 +102,8 @@ struct DBConfigOptions { idx_t maximum_threads = DConstants::INVALID_INDEX; //! The maximum amount of async threads used by the database system. Default: all available. idx_t async_threads = DConstants::INVALID_INDEX; + //! HTTP client limit derived from thread counts unless configured + idx_t http_client_pool_capacity = DConstants::INVALID_INDEX; //! Whether or not to create and use a temporary directory to store intermediates that do not fit in memory bool use_temporary_directory = true; //! Directory to store temporary structures that do not fit in memory @@ -168,6 +172,45 @@ struct DBConfigOptions { bool operator==(const DBConfigOptions &other) const; }; +//! The replacement scans of a database. Scans can be registered while queries are being bound, so the list is +//! guarded by a lock and the binder iterates over a snapshot. +class ReplacementScanList { +public: + ReplacementScanList() = default; + ReplacementScanList(ReplacementScanList &&other) noexcept { + lock_guard guard(other.lock); + scans = std::move(other.scans); + } + ReplacementScanList &operator=(ReplacementScanList &&other) noexcept { + if (this != &other) { + lock_guard other_guard(other.lock); + lock_guard guard(lock); + scans = std::move(other.scans); + } + return *this; + } + + template + void emplace_back(ARGS &&... args) { // NOLINT + auto scan = make_shared_ptr(std::forward(args)...); + lock_guard guard(lock); + scans.push_back(std::move(scan)); + } + void push_back(ReplacementScan scan) { // NOLINT + auto shared_scan = make_shared_ptr(std::move(scan)); + lock_guard guard(lock); + scans.push_back(std::move(shared_scan)); + } + vector> Snapshot() const { + lock_guard guard(lock); + return scans; + } + +private: + mutable mutex lock; + vector> scans; +}; + struct DBConfig { friend class DatabaseInstance; friend class StorageManager; @@ -179,7 +222,7 @@ struct DBConfig { DUCKDB_API ~DBConfig(); //! Replacement table scans are automatically attempted when a table name cannot be found in the schema - vector replacement_scans; + ReplacementScanList replacement_scans; //! The FileSystem to use, can be overwritten to allow for injecting custom file systems for testing purposes (e.g. //! RamFS or something similar) @@ -224,7 +267,9 @@ struct DBConfig { DUCKDB_API void AddExtensionOption(const Identifier &name, string description, LogicalType parameter, const Value &default_value = Value(), set_option_callback_t function = nullptr, - SetScope default_scope = SetScope::SESSION); + SetScope default_scope = SetScope::SESSION, bool is_debug = false, + bool is_deprecated = false); + DUCKDB_API void AddExtensionOption(const Identifier &name, ExtensionOption extension_option); DUCKDB_API bool HasExtensionOption(const Identifier &name) const; DUCKDB_API identifier_map_t GetExtensionSettings() const; DUCKDB_API bool TryGetExtensionOption(const Identifier &name, ExtensionOption &result) const; @@ -249,7 +294,10 @@ struct DBConfig { DUCKDB_API void CheckLock(const Identifier &name); - DUCKDB_API static idx_t ParseMemoryLimit(const string &arg); + //! Parse a memory size - an unlimited size (e.g. "-1" or "none") is returned as an invalid optional_idx + DUCKDB_API static optional_idx ParseMemoryLimit(const string &arg); + //! Parse a memory size, returning NumericLimits::Maximum() for an unlimited size + DUCKDB_API static idx_t ParseMemoryLimitOrMaximum(const string &arg); //! Returns the list of possible compression functions for the physical type. DUCKDB_API vector> @@ -289,6 +337,7 @@ struct DBConfig { bool operator!=(const DBConfig &other); DUCKDB_API CastFunctionSet &GetCastFunctions(); + DUCKDB_API const CastFunctionSet &GetCastFunctions() const; DUCKDB_API TypeManager &GetTypeManager(); DUCKDB_API CollationBinding &GetCollationBinding(); DUCKDB_API IndexTypeSet &GetIndexTypes(); @@ -310,20 +359,38 @@ struct DBConfig { //! Returns the default value of an option static SettingLookupResult TryGetDefaultValue(optional_ptr option, Value &result); - bool CanAccessFile(const string &path, FileType type); + //! The opener (if any) is used to expand "~" using the connection-specific home_directory + bool CanAccessFile(const string &path, FileType type, optional_ptr opener = nullptr); void AddAllowedConfig(const Identifier &config_name); void AddAllowedDirectory(const string &path); void AddAllowedPath(const string &path); - string SanitizeAllowedPath(const string &path) const; + //! Whether the given optimizer is disabled (disabled_optimizers) + DUCKDB_API bool IsOptimizerDisabled(OptimizerType type) const; + DUCKDB_API set GetDisabledOptimizers() const; + DUCKDB_API void SetDisabledOptimizers(set disabled_optimizers); + //! Allows a database file and its WAL files, so a database can be opened while external access is disabled. + //! Only possible through API calls, not SQL calls. + void AddAllowedDatabasePath(const string &database_path); + vector GetAllowedDirectories() const; + vector GetAllowedPaths() const; + string SanitizeAllowedPath(const string &path, optional_ptr opener = nullptr) const; ExtensionCallbackManager &GetCallbackManager(); const ExtensionCallbackManager &GetCallbackManager() const; void SetHTTPUtil(const shared_ptr &new_http_util); HTTPUtil &GetHTTPUtil() const; + //! Replace how external extensions are installed and loaded, before the database runs queries + DUCKDB_API void SetExternalExtensionProvider(const shared_ptr &new_provider); + DUCKDB_API ExternalExtensionProvider &GetExternalExtensionProvider() const; DUCKDB_API HTTPTransportManager &GetHTTPTransportManager(); + DUCKDB_API const HTTPTransportManager &GetHTTPTransportManager() const; private: mutable mutex config_lock; + //! Guards allowed_paths and allowed_directories, which a running instance can extend while files are being opened + mutable mutex allowed_paths_lock; + //! Guards disabled_optimizers, which can be changed while other connections plan queries + mutable mutex disabled_optimizers_lock; unique_ptr compression_functions; unique_ptr encoding_functions; unique_ptr arrow_extensions; @@ -334,6 +401,8 @@ struct DBConfig { bool is_user_config = true; //! HTTP provider publication and bounded client ownership unique_ptr http_transport_manager; + //! Installs and loads external extensions; "none" unless a loader library is linked + shared_ptr external_extension_provider; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/connection.hpp b/src/duckdb/src/include/duckdb/main/connection.hpp index 644f5cfa8..bd1d62923 100644 --- a/src/duckdb/src/include/duckdb/main/connection.hpp +++ b/src/duckdb/src/include/duckdb/main/connection.hpp @@ -8,15 +8,15 @@ #pragma once +#include + #include "duckdb/main/profiler/profiler_print_format.hpp" #include "duckdb/common/serializer/buffered_file_writer.hpp" #include "duckdb/common/winapi.hpp" -#include "duckdb/main/materialized_query_result.hpp" -#include "duckdb/main/pending_query_result.hpp" #include "duckdb/main/prepared_statement.hpp" #include "duckdb/main/query_result.hpp" +#include "duckdb/main/query_result_stream.hpp" #include "duckdb/main/relation.hpp" -#include "duckdb/main/stream_query_result.hpp" #include "duckdb/main/table_description.hpp" #include "duckdb/parser/sql_statement.hpp" @@ -28,8 +28,10 @@ class ClientContext; class DatabaseInstance; class DuckDB; class LogicalOperator; +class ResultFormat; class SelectStatement; struct CSVReaderOptions; +struct TableFunctionInfo; typedef void (*warning_callback_t)(std::string); @@ -39,6 +41,7 @@ class Connection { public: DUCKDB_API explicit Connection(DuckDB &database); DUCKDB_API explicit Connection(DatabaseInstance &database); + DUCKDB_API static Connection CreateInternal(DatabaseInstance &database); // disable copy constructors Connection(const Connection &other) = delete; Connection &operator=(const Connection &) = delete; @@ -49,12 +52,15 @@ class Connection { shared_ptr context; +private: + Connection(DatabaseInstance &database, ConnectionType connection_type); + public: //! Returns query profiling information for the current query, formatted according to the given ProfilerPrintFormat //! (e.g. ProfilerPrintFormat::JSON()). ProfilerPrintFormat::Default() uses the configured default profiler format. DUCKDB_API string GetProfilingInformation(const ProfilerPrintFormat &format = ProfilerPrintFormat::Default()); - //! Interrupt execution of the current query + //! Interrupts the current query. Async-signal-safe DUCKDB_API void Interrupt(); //! Get query progress of current query @@ -74,50 +80,44 @@ class Connection { //! Force parallel execution, even for smaller tables. Should only be used in testing. DUCKDB_API void ForceParallelism(); - //! Issues a query to the database and returns a QueryResult. This result can be either a StreamQueryResult or a - //! MaterializedQueryResult. The result can be stepped through with calls to Fetch(). Note that there can only be - //! one active StreamQueryResult per Connection object. Calling SendQuery() will invalidate any previously existing - //! StreamQueryResult. - DUCKDB_API unique_ptr - SendQuery(const string &query, QueryParameters query_parameters = QueryResultOutputType::ALLOW_STREAMING); - DUCKDB_API unique_ptr - SendQuery(unique_ptr statement, - QueryParameters query_parameters = QueryResultOutputType::ALLOW_STREAMING); - //! Issues a query to the database and materializes the result (if necessary). Always returns a - //! MaterializedQueryResult. - DUCKDB_API unique_ptr Query(const string &query); - //! Issues a query to the database and materializes the result (if necessary). Always returns a - //! MaterializedQueryResult. - DUCKDB_API unique_ptr - Query(unique_ptr statement, QueryResultMemoryType memory_type = QueryResultMemoryType::IN_MEMORY); + //! Blocking. Runs the query to completion and returns its handle. The result is retained: it can be read + //! repeatedly and at random. + DUCKDB_API unique_ptr Query(const string &query); + DUCKDB_API unique_ptr Query(unique_ptr statement, + shared_ptr format = nullptr); + //! As above, in the given result format + DUCKDB_API unique_ptr Query(const string &query, shared_ptr format); // prepared statements template unique_ptr Query(const string &query, ARGS... args) { vector values; return QueryParamsRecursive(query, values, args...); } + //! Overload resolution otherwise prefers the prepared-statement template above for a derived format pointer + template ::value, int>::type = 0> + unique_ptr Query(const string &query, shared_ptr format) { + return Query(query, shared_ptr(std::move(format))); + } - //! Issues a query to the database and returns a Pending Query Result. Note that "query" may only contain - //! a single statement. - DUCKDB_API unique_ptr - PendingQuery(const string &query, QueryParameters query_parameters = QueryResultOutputType::FORCE_MATERIALIZED); - //! Issues a query to the database and returns a Pending Query Result - DUCKDB_API unique_ptr - PendingQuery(unique_ptr statement, - QueryParameters query_parameters = QueryResultOutputType::FORCE_MATERIALIZED); - DUCKDB_API unique_ptr - PendingQuery(unique_ptr statement, identifier_map_t &named_values, - QueryParameters query_parameters = QueryResultOutputType::FORCE_MATERIALIZED); - DUCKDB_API unique_ptr - PendingQuery(const string &query, identifier_map_t &named_values, - QueryParameters query_parameters = QueryResultOutputType::FORCE_MATERIALIZED); - DUCKDB_API unique_ptr - PendingQuery(const string &query, vector &values, - QueryParameters query_parameters = QueryResultOutputType::FORCE_MATERIALIZED); - DUCKDB_API unique_ptr PendingQuery(const string &query, PendingQueryParameters parameters); - DUCKDB_API unique_ptr - PendingQuery(unique_ptr statement, vector &values, - QueryParameters query_parameters = QueryResultOutputType::FORCE_MATERIALIZED); + //! Non-blocking. Submits the query and returns its handle. The engine runs it iff threads - external_threads > 0, + //! but produces no data until the caller either calls a materializing method on the handle or opens a + //! QueryResultStream on it. The query may only contain a single statement. + DUCKDB_API unique_ptr Submit(const string &query, const QueryParameters &query_parameters = {}); + //! Non-blocking. As above, for a parsed statement and for bound parameter values + DUCKDB_API unique_ptr Submit(unique_ptr statement, + const QueryParameters &query_parameters = {}); + //! Non-blocking. As above, in the given result format + DUCKDB_API unique_ptr Submit(const string &query, shared_ptr format); + DUCKDB_API unique_ptr Submit(unique_ptr statement, shared_ptr format); + DUCKDB_API unique_ptr Submit(unique_ptr statement, + identifier_map_t &named_values, + const QueryParameters &query_parameters = {}); + DUCKDB_API unique_ptr Submit(const string &query, identifier_map_t &named_values, + const QueryParameters &query_parameters = {}); + DUCKDB_API unique_ptr Submit(const string &query, vector &values, + const QueryParameters &query_parameters = {}); + DUCKDB_API unique_ptr Submit(unique_ptr statement, vector &values, + const QueryParameters &query_parameters = {}); //! Prepare the specified query, returning a prepared statement object DUCKDB_API unique_ptr Prepare(const string &query); @@ -133,7 +133,7 @@ class Connection { //! DEFAULT_SCHEMA. DUCKDB_API unique_ptr TableInfo(const Identifier &table_name); - //! Extract a set of SQL statements from a specific query + //! Extract the user statements of a script as parsed, without expanding any of them DUCKDB_API vector> ExtractStatements(const string &query); //! Extract the logical plan that corresponds to a query DUCKDB_API unique_ptr ExtractPlan(const string &query); @@ -154,6 +154,10 @@ class Connection { DUCKDB_API shared_ptr TableFunction(const string &tname, const vector &values, const named_parameter_map_t &named_parameters); DUCKDB_API shared_ptr TableFunction(const string &tname, const vector &values); + //! Calls a table function with process-local input for this invocation + DUCKDB_API shared_ptr TableFunction(const string &tname, const vector &values, + const named_parameter_map_t &named_parameters, + shared_ptr bind_info); //! Returns a relation that produces values DUCKDB_API shared_ptr Values(const vector> &values); DUCKDB_API shared_ptr Values(vector>> &&values); diff --git a/src/duckdb/src/include/duckdb/main/database.hpp b/src/duckdb/src/include/duckdb/main/database.hpp index 2cec74dc3..0f4942193 100644 --- a/src/duckdb/src/include/duckdb/main/database.hpp +++ b/src/duckdb/src/include/duckdb/main/database.hpp @@ -16,6 +16,7 @@ #include "duckdb/main/valid_checker.hpp" #include "duckdb/main/extension/extension_loader.hpp" #include "duckdb/main/extension_manager.hpp" +#include "duckdb_static_extension.h" namespace duckdb { class LocalDatabaseFileSystem; @@ -94,7 +95,15 @@ class DatabaseInstance : public enable_shared_from_this { AttachOptions &options); private: + //! Initializes the instance and attaches the main database at `path` (in-memory when null). void Initialize(const char *path, DBConfig *config); + //! Initializes the instance without attaching a database: the system catalog is the only catalog until one is + //! attached (ATTACH, or DatabaseManager::AttachDatabase). + void InitializeEmpty(DBConfig *config); + //! The part of initialization shared by both, up to the main database. + void InitializeInstance(const char *path, DBConfig *config); + //! Launches the scheduler threads; last, since storage init races on the catalog otherwise. + void StartScheduler(); void LoadExtensionSettings(); void CreateMainDatabase(); @@ -126,6 +135,25 @@ class DatabaseInstance : public enable_shared_from_this { invoke_ext_capi_v2_fun_t invoke_capi_v2; }; +//! A describe function for an extension class, so that loading it by class goes through the same path as linked +//! extensions. +template +struct StaticExtensionDescriber { + static void Entry(ExtensionLoader &loader) { + T extension; + extension.Load(loader); + } + static int32_t Describe(duckdb_extension_descriptor *descriptor) { + static const std::string name = T().Name(); + static const std::string version = T().Version(); + descriptor->version = 1; + descriptor->name = name.c_str(); + descriptor->extension_version = version.c_str(); + descriptor->entry_cpp = reinterpret_cast(&Entry); + return 0; + } +}; + //! The database object. This object holds the catalog and all the //! database-specific meta information. class DuckDB { @@ -136,36 +164,25 @@ class DuckDB { DUCKDB_API ~DuckDB(); + //! Creates an instance with no database attached. Databases are attached to it later (ATTACH, or + //! DatabaseManager::AttachDatabase); until then only the system catalog and each connection's temporary catalog + //! exist, and statements that need a default database fail. + DUCKDB_API static shared_ptr CreateEmpty(DBConfig *config = nullptr); + //! Reference to the actual database instance shared_ptr instance; public: - // Load a statically loaded extension by its class + // Load a statically linked extension by its class, through a describe function generated for it template void LoadStaticExtension() { - T extension; - auto &manager = ExtensionManager::Get(*instance); - auto load_info = manager.BeginLoad({extension.Name()}); - if (!load_info) { - // already loaded - return - return; - } - - // Instantiate a new loader - ExtensionLoader loader(*load_info); - - // Call the Load method of the extension - extension.Load(loader); - - // Finalize the loading process - loader.FinalizeLoad(); - - ExtensionInstallInfo install_info; - install_info.mode = ExtensionInstallMode::STATICALLY_LINKED; - install_info.version = extension.Version(); - load_info->FinishLoad(install_info); + LoadStaticExtension(&StaticExtensionDescriber::Describe); } + // Load the statically linked extension a describe function describes into this database + DUCKDB_API void LoadStaticExtension(duckdb_extension_describe_t describe); + // Function pointer type for the C++ extension entrypoint, _duckdb_cpp_init + typedef void (*ext_init_cpp_fun_t)(ExtensionLoader &loader); // Function pointer type for the C API extension init function typedef bool (*ext_init_c_api_fun_t)(duckdb_extension_info info, duckdb_extension_access *access); // Load a statically compiled C API extension by calling its init function directly (no vtable needed) diff --git a/src/duckdb/src/include/duckdb/main/database_manager.hpp b/src/duckdb/src/include/duckdb/main/database_manager.hpp index a139f4b76..e3e8efd7e 100644 --- a/src/duckdb/src/include/duckdb/main/database_manager.hpp +++ b/src/duckdb/src/include/duckdb/main/database_manager.hpp @@ -24,6 +24,7 @@ class CatalogEntryRetriever; class CatalogSet; class ClientContext; class DatabaseInstance; +class ResourceDeleter; class TaskScheduler; struct AttachOptions; struct AlterInfo; @@ -54,8 +55,19 @@ class DatabaseManager { //! Attach a new database shared_ptr AttachDatabase(ClientContext &context, AttachInfo &info, AttachOptions &options); - //! Detach an existing database - void DetachDatabase(ClientContext &context, const Identifier &name, OnEntryNotFound if_not_found); + //! Detach an existing database. SQL DETACH refuses the connection's default database, which would leave it + //! without one; a host closing a database it opened passes `allow_default_database` to detach it regardless. + void DetachDatabase(ClientContext &context, const Identifier &name, OnEntryNotFound if_not_found, + bool allow_default_database = false); + //! Queue the teardown of an external resource from a context that cannot run SQL (e.g. transaction + //! rollback, under the transaction lock). + void AddPendingTeardown(unique_ptr deleter); + //! Run queued teardowns, best-effort. Called once no transaction locks are held. + void DrainPendingTeardowns(); + //! Detach every attachment borrowing the named resource, once it has been destroyed. Best-effort: + //! DETACH may refuse (the default database) and DESTROY goes through regardless. Scans rather than + //! keeping a refcount -- the attachment list cannot drift, and this follows a network round-trip. + void DetachResourceBorrowers(ClientContext &context, const string &resource_name); //! Alter operation dispatcher void Alter(ClientContext &context, AlterInfo &info); //! Rollback the attach of a database @@ -63,7 +75,17 @@ class DatabaseManager { //! Returns a reference to the system catalog Catalog &GetSystemCatalog(); + //! The default database of the connection: its USE'd catalog, else the default database it connected with, as long + //! as that is still attached. Throws when there is none; TryGetDefaultDatabase returns the empty identifier + //! instead, for lookups that can skip it. static Identifier GetDefaultDatabase(ClientContext &context); + static Identifier TryGetDefaultDatabase(ClientContext &context); + //! The default database new connections start with; empty when none is set. + Identifier GetDefaultDatabase(); + //! Sets the default database for new connections, which must be attached; the empty identifier clears it. Set to + //! the main database at startup and never set implicitly by an attach; detaching it falls back to the oldest + //! remaining database, if any. + void SetDefaultDatabase(const Identifier &name); //! Inserts a path to name mapping to the database paths map InsertDatabasePathResult InsertDatabasePath(const AttachInfo &info, AttachOptions &options); @@ -124,6 +146,8 @@ class DatabaseManager { mutex databases_lock; //! The set of attached databases identifier_map_t> databases; + //! The default database for new connections; empty when none is set (guarded by databases_lock) + Identifier default_database; //! The next object id handed out by the NextOid method atomic next_oid; //! The current query number @@ -132,6 +156,11 @@ class DatabaseManager { atomic current_transaction_id; //! Count of remote catalogs currently attached; used to skip the remote pushdown optimizer when zero atomic> remote_catalog_count; + //! Lock for pending_teardowns + mutex pending_teardowns_lock; + //! External-resource teardowns queued from contexts that cannot run SQL (see AddPendingTeardown); + //! drained best-effort once no transaction locks are held. + vector> pending_teardowns; //! Manager for ensuring we never open the same database file twice in the same program shared_ptr path_manager; diff --git a/src/duckdb/src/include/duckdb/main/db_instance_cache.hpp b/src/duckdb/src/include/duckdb/main/db_instance_cache.hpp index a8a14c416..2a5a86660 100644 --- a/src/duckdb/src/include/duckdb/main/db_instance_cache.hpp +++ b/src/duckdb/src/include/duckdb/main/db_instance_cache.hpp @@ -47,6 +47,11 @@ class DBInstanceCache { CacheBehavior cache_behavior = CacheBehavior::AUTOMATIC, const std::function &on_create = nullptr); + //! Creates an uncached instance with no database attached (see DuckDB::CreateEmpty). Databases attached to it + //! later go through this cache's path manager, so opening one file twice is detected across every instance + //! created through the cache. + shared_ptr CreateEmptyInstance(DBConfig &config); + private: shared_ptr path_manager; //! A map with the cached instances diff --git a/src/duckdb/src/include/duckdb/main/error_manager.hpp b/src/duckdb/src/include/duckdb/main/error_manager.hpp index 2f1542e0a..f4849df1f 100644 --- a/src/duckdb/src/include/duckdb/main/error_manager.hpp +++ b/src/duckdb/src/include/duckdb/main/error_manager.hpp @@ -43,7 +43,7 @@ class ErrorManager { DUCKDB_API string FormatExceptionRecursive(ErrorType error_type, vector &values); template - string FormatExceptionRecursive(ErrorType error_type, vector &values, T param, + string FormatExceptionRecursive(ErrorType error_type, vector &values, const T ¶m, ARGS &&...params) { values.push_back(ExceptionFormatValue::CreateFormatValue(param)); return FormatExceptionRecursive(error_type, values, std::forward(params)...); diff --git a/src/duckdb/src/include/duckdb/main/extension/extension_loader.hpp b/src/duckdb/src/include/duckdb/main/extension/extension_loader.hpp index 6eecc3d94..0151b8101 100644 --- a/src/duckdb/src/include/duckdb/main/extension/extension_loader.hpp +++ b/src/duckdb/src/include/duckdb/main/extension/extension_loader.hpp @@ -27,6 +27,7 @@ struct CreateCollationInfo; struct CreateAggregateFunctionInfo; struct CreateScalarFunctionInfo; struct CreateTableFunctionInfo; +struct CreatePragmaFunctionInfo; struct CreateWindowFunctionInfo; struct ExtensionLoaderInfo { @@ -92,6 +93,7 @@ class ExtensionLoader { //! Register a new pragma function set - throw an exception if the function already exists DUCKDB_API void RegisterFunction(PragmaFunctionSet function); + DUCKDB_API void RegisterFunction(CreatePragmaFunctionInfo info); //! Register a CreateSecretFunction DUCKDB_API void RegisterFunction(CreateSecretFunction function); diff --git a/src/duckdb/src/include/duckdb/main/extension/external_extension_provider.hpp b/src/duckdb/src/include/duckdb/main/extension/external_extension_provider.hpp new file mode 100644 index 000000000..7aadf0076 --- /dev/null +++ b/src/duckdb/src/include/duckdb/main/extension/external_extension_provider.hpp @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/main/extension/external_extension_provider.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/optional_ptr.hpp" +#include "duckdb/common/string.hpp" +#include "duckdb/common/unique_ptr.hpp" + +namespace duckdb { +class ClientContext; +class DatabaseInstance; +class FileSystem; +class ExtensionInstallInfo; +struct ExtensionInstallOptions; + +//! Installs external extensions and opens their libraries. The base class is the "none" provider, which refuses both; +//! a linked loadable_extensions library replaces it for every database opened. +class ExternalExtensionProvider { +public: + virtual ~ExternalExtensionProvider() = default; + +public: + virtual string GetName() const; + //! Whether this provider can install and load external extensions at all + virtual bool SupportsExternalExtensions() const; + virtual unique_ptr Install(DatabaseInstance &db, FileSystem &fs, const string &local_path, + const string &extension, ExtensionInstallOptions &options, + optional_ptr context); + //! Open the library of an external extension, throws if it cannot be opened + virtual void *OpenLibrary(const string &filename, const string &filebase); + //! Look up a function in an opened library, returns nullptr if it is not there + virtual void *TryLoadFunction(void *library, const string &function_name); + //! The error of the last failed library operation + virtual string GetLibraryError(); +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/extension/generated_extension_loader.hpp b/src/duckdb/src/include/duckdb/main/extension/generated_extension_loader.hpp deleted file mode 100644 index f00f338a1..000000000 --- a/src/duckdb/src/include/duckdb/main/extension/generated_extension_loader.hpp +++ /dev/null @@ -1,25 +0,0 @@ -//===----------------------------------------------------------------------===// -// DuckDB -// -// duckdb/main/extension/generated_extension_loader.hpp -// -// -//===----------------------------------------------------------------------===// - -#pragma once - -#include "duckdb/common/string.hpp" -#include "duckdb/common/vector.hpp" -#include "duckdb/main/database.hpp" - -#if defined(GENERATED_EXTENSION_HEADERS) -#include "duckdb/common/common.hpp" -#include "generated_extension_headers.hpp" - -namespace duckdb { - -vector LinkedExtensions(); -vector LoadedExtensionTestPaths(); - -} // namespace duckdb -#endif diff --git a/src/duckdb/src/include/duckdb/main/extension/linked_extension_registry.hpp b/src/duckdb/src/include/duckdb/main/extension/linked_extension_registry.hpp new file mode 100644 index 000000000..4bee4891d --- /dev/null +++ b/src/duckdb/src/include/duckdb/main/extension/linked_extension_registry.hpp @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/main/extension/linked_extension_registry.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/string.hpp" +#include "duckdb/common/vector.hpp" +#include "duckdb/common/winapi.hpp" +#include "duckdb_static_extension.h" + +#include + +namespace duckdb { +class DatabaseInstance; +class DuckDB; + +//! An extension compiled into this binary, as a name and the callable that loads it. +struct LinkedExtension { + string name; + //! empty when what was registered is not an extension + std::function load; + //! set instead of load when what was registered is not an extension, called for every database opened + std::function database_callback; +}; + +//! What a describe function described, once validated. +struct StaticExtensionDescription { + string name; + string version; + //! the DuckDB version a C++ entry point was built against, or the C API version a C entry point targets + string api_version; + duckdb_extension_descriptor descriptor; +}; + +//! The extensions linked into this binary, registered through duckdb_register_static_extension. +class LinkedExtensionRegistry { +public: + //! Calls describe and validates the descriptor it fills. Returns the reason on failure, otherwise an empty string. + DUCKDB_API static string Describe(duckdb_extension_describe_t describe, StaticExtensionDescription &result); + //! A copy, in registration order. Throws if a registration failed. + DUCKDB_API static vector Get(); +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/extension_callback_manager.hpp b/src/duckdb/src/include/duckdb/main/extension_callback_manager.hpp index 2b4bb1ce6..fca546d2e 100644 --- a/src/duckdb/src/include/duckdb/main/extension_callback_manager.hpp +++ b/src/duckdb/src/include/duckdb/main/extension_callback_manager.hpp @@ -46,7 +46,7 @@ class ExtensionCallbackManager { void Register(ParserExtension extension); void Register(shared_ptr change); - void Register(DialectExtension extension); + void Register(shared_ptr extension); void Register(PlannerExtension extension); void Register(OptimizerExtension extension); void Register(shared_ptr extension); @@ -57,7 +57,7 @@ class ExtensionCallbackManager { ExtensionCallbackIteratorHelper> OperatorExtensions() const; ExtensionCallbackIteratorHelper OptimizerExtensions() const; ExtensionCallbackIteratorHelper ParserExtensions() const; - ExtensionCallbackIteratorHelper DialectExtensions() const; + ExtensionCallbackIteratorHelper> DialectExtensions() const; ExtensionCallbackIteratorHelper PlannerExtensions() const; ExtensionCallbackIteratorHelper> ExtensionCallbacks() const; optional_ptr FindStorageExtension(const string &name) const; @@ -65,7 +65,7 @@ class ExtensionCallbackManager { case_insensitive_map_t> GrammarExtensions() const; optional_ptr FindProfilerExtension(const string &name) const; bool HasParserExtensions() const; - bool HasDialectExtension(const string &name) const; + optional_ptr GetDialectExtension(const string &name) const; private: mutex registry_lock; diff --git a/src/duckdb/src/include/duckdb/main/extension_entries.hpp b/src/duckdb/src/include/duckdb/main/extension_entries.hpp index 3fda1200f..0671844d9 100644 --- a/src/duckdb/src/include/duckdb/main/extension_entries.hpp +++ b/src/duckdb/src/include/duckdb/main/extension_entries.hpp @@ -375,6 +375,16 @@ static constexpr ExtensionFunctionEntry EXTENSION_FUNCTIONS[] = { {"get_current_time", "icu", CatalogType::SCALAR_FUNCTION_ENTRY}, {"get_current_timestamp", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, {"get_type", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, + {"glue_add_partition", "glue", CatalogType::TABLE_FUNCTION_ENTRY}, + {"glue_alter_table", "glue", CatalogType::TABLE_FUNCTION_ENTRY}, + {"glue_drop_partition", "glue", CatalogType::TABLE_FUNCTION_ENTRY}, + {"glue_get_database_response", "glue", CatalogType::TABLE_FUNCTION_ENTRY}, + {"glue_get_table_response", "glue", CatalogType::TABLE_FUNCTION_ENTRY}, + {"glue_partitions", "glue", CatalogType::TABLE_FUNCTION_ENTRY}, + {"glue_rename_partition", "glue", CatalogType::TABLE_FUNCTION_ENTRY}, + {"glue_replace_columns", "glue", CatalogType::TABLE_FUNCTION_ENTRY}, + {"glue_set_partition_location", "glue", CatalogType::TABLE_FUNCTION_ENTRY}, + {"glue_set_table_location", "glue", CatalogType::TABLE_FUNCTION_ENTRY}, {"grade_up", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, {"greatest", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, {"greatest_common_divisor", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, @@ -384,6 +394,7 @@ static constexpr ExtensionFunctionEntry EXTENSION_FUNCTIONS[] = { {"hex", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, {"histogram", "core_functions", CatalogType::AGGREGATE_FUNCTION_ENTRY}, {"histogram_exact", "core_functions", CatalogType::AGGREGATE_FUNCTION_ENTRY}, + {"hive_scan", "glue", CatalogType::TABLE_FUNCTION_ENTRY}, {"hnsw_compact_index", "vss", CatalogType::PRAGMA_FUNCTION_ENTRY}, {"hnsw_index_scan", "vss", CatalogType::TABLE_FUNCTION_ENTRY}, {"host", "inet", CatalogType::SCALAR_FUNCTION_ENTRY}, @@ -397,6 +408,8 @@ static constexpr ExtensionFunctionEntry EXTENSION_FUNCTIONS[] = { {"iceberg_rewrite_data_files", "iceberg", CatalogType::TABLE_FUNCTION_ENTRY}, {"iceberg_rollback_to_snapshot", "iceberg", CatalogType::TABLE_FUNCTION_ENTRY}, {"iceberg_scan", "iceberg", CatalogType::TABLE_FUNCTION_ENTRY}, + {"iceberg_scan_plan", "iceberg", CatalogType::TABLE_FUNCTION_ENTRY}, + {"iceberg_scan_tasks", "iceberg", CatalogType::TABLE_FUNCTION_ENTRY}, {"iceberg_schema_properties", "iceberg", CatalogType::TABLE_FUNCTION_ENTRY}, {"iceberg_snapshots", "iceberg", CatalogType::TABLE_FUNCTION_ENTRY}, {"iceberg_table_properties", "iceberg", CatalogType::TABLE_FUNCTION_ENTRY}, @@ -677,6 +690,7 @@ static constexpr ExtensionFunctionEntry EXTENSION_FUNCTIONS[] = { {"now", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, {"odbc_begin_transaction", "odbc_scanner", CatalogType::SCALAR_FUNCTION_ENTRY}, {"odbc_bind_params", "odbc_scanner", CatalogType::SCALAR_FUNCTION_ENTRY}, + {"odbc_cancel_query", "odbc_scanner", CatalogType::SCALAR_FUNCTION_ENTRY}, {"odbc_close", "odbc_scanner", CatalogType::SCALAR_FUNCTION_ENTRY}, {"odbc_commit", "odbc_scanner", CatalogType::SCALAR_FUNCTION_ENTRY}, {"odbc_connect", "odbc_scanner", CatalogType::SCALAR_FUNCTION_ENTRY}, @@ -699,18 +713,17 @@ static constexpr ExtensionFunctionEntry EXTENSION_FUNCTIONS[] = { {"parse_filename", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, {"parse_formatted_bytes", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, {"parse_path", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, - {"pg_clear_cache", "postgres_scanner", CatalogType::TABLE_FUNCTION_ENTRY}, {"pg_timezone_names", "icu", CatalogType::TABLE_FUNCTION_ENTRY}, {"pi", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, {"position", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, - {"postgres_attach", "postgres_scanner", CatalogType::TABLE_FUNCTION_ENTRY}, + {"postgres_clear_cache", "postgres_scanner", CatalogType::TABLE_FUNCTION_ENTRY}, + {"postgres_close_pinned_connection", "postgres_scanner", CatalogType::SCALAR_FUNCTION_ENTRY}, {"postgres_configure_pool", "postgres_scanner", CatalogType::TABLE_FUNCTION_ENTRY}, {"postgres_execute", "postgres_scanner", CatalogType::TABLE_FUNCTION_ENTRY}, {"postgres_hstore_get", "postgres_scanner", CatalogType::SCALAR_FUNCTION_ENTRY}, {"postgres_hstore_to_json", "postgres_scanner", CatalogType::SCALAR_FUNCTION_ENTRY}, + {"postgres_pin_connection", "postgres_scanner", CatalogType::SCALAR_FUNCTION_ENTRY}, {"postgres_query", "postgres_scanner", CatalogType::TABLE_FUNCTION_ENTRY}, - {"postgres_scan", "postgres_scanner", CatalogType::TABLE_FUNCTION_ENTRY}, - {"postgres_scan_pushdown", "postgres_scanner", CatalogType::TABLE_FUNCTION_ENTRY}, {"pow", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, {"power", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, {"pragma_hnsw_index_info", "vss", CatalogType::TABLE_FUNCTION_ENTRY}, @@ -746,6 +759,7 @@ static constexpr ExtensionFunctionEntry EXTENSION_FUNCTIONS[] = { {"read_parquet", "parquet", CatalogType::TABLE_FUNCTION_ENTRY}, {"read_postgres_binary", "postgres_scanner", CatalogType::TABLE_FUNCTION_ENTRY}, {"read_single_json_file", "json", CatalogType::TABLE_FUNCTION_ENTRY}, + {"read_single_parquet_file", "parquet", CatalogType::TABLE_FUNCTION_ENTRY}, {"read_xlsx", "excel", CatalogType::TABLE_FUNCTION_ENTRY}, {"reduce", "core_functions", CatalogType::SCALAR_FUNCTION_ENTRY}, {"reduce_sql_statement", "sqlsmith", CatalogType::TABLE_FUNCTION_ENTRY}, @@ -930,7 +944,9 @@ static constexpr ExtensionFunctionEntry EXTENSION_FUNCTIONS[] = { {"st_shortestline", "spatial", CatalogType::SCALAR_FUNCTION_ENTRY}, {"st_simplify", "spatial", CatalogType::SCALAR_FUNCTION_ENTRY}, {"st_simplifypreservetopology", "spatial", CatalogType::SCALAR_FUNCTION_ENTRY}, + {"st_snap", "spatial", CatalogType::SCALAR_FUNCTION_ENTRY}, {"st_startpoint", "spatial", CatalogType::SCALAR_FUNCTION_ENTRY}, + {"st_subdivide", "spatial", CatalogType::SCALAR_FUNCTION_ENTRY}, {"st_symdifference", "spatial", CatalogType::SCALAR_FUNCTION_ENTRY}, {"st_tileenvelope", "spatial", CatalogType::SCALAR_FUNCTION_ENTRY}, {"st_touches", "spatial", CatalogType::SCALAR_FUNCTION_ENTRY}, @@ -1308,15 +1324,21 @@ static constexpr ExtensionEntry EXTENSION_SETTINGS[] = { {"enable_curl_server_cert_verification", "httpfs"}, {"enable_geoparquet_conversion", "parquet"}, {"enable_global_s3_configuration", "httpfs"}, + {"enable_legacy_parser", "legacy_parser"}, {"enable_server_cert_verification", "httpfs"}, {"extra_http_headers", "httpfs"}, {"force_download", "httpfs"}, {"force_download_threshold", "httpfs"}, {"gcs_user_project", "httpfs"}, {"geometry_always_xy", "spatial"}, + {"glue_create_bucketed_tables", "glue"}, + {"glue_get_partitions_segments", "glue"}, + {"glue_network_calls_via_duckdb", "glue"}, {"hf_max_per_page", "httpfs"}, + {"hive_partition_listing_threshold", "glue"}, {"hnsw_ef_search", "vss"}, {"hnsw_enable_experimental_persistence", "vss"}, + {"hnsw_prefilter", "vss"}, {"http_keep_alive", "httpfs"}, {"http_retries", "httpfs"}, {"http_retry_backoff", "httpfs"}, @@ -1332,12 +1354,12 @@ static constexpr ExtensionEntry EXTENSION_SETTINGS[] = { {"iceberg_unsafe_skip_puffin_verification", "iceberg"}, {"iceberg_use_metadata_log", "iceberg"}, {"iceberg_use_server_side_scan_planning", "iceberg"}, - {"iceberg_via_aws_sdk_for_catalog_interactions", "iceberg"}, {"json_geometry_format", "json"}, {"merge_http_secret_into_s3_request", "httpfs"}, {"mysql_bit1_as_boolean", "mysql_scanner"}, {"mysql_debug_show_queries", "mysql_scanner"}, {"mysql_enable_filter_pushdown", "mysql_scanner"}, + {"mysql_enable_remote_pushdown", "mysql_scanner"}, {"mysql_enable_transactions", "mysql_scanner"}, {"mysql_incomplete_dates_as_nulls", "mysql_scanner"}, {"mysql_pool_acquire_mode", "mysql_scanner"}, @@ -1357,13 +1379,12 @@ static constexpr ExtensionEntry EXTENSION_SETTINGS[] = { {"pg_connection_cache", "postgres_scanner"}, {"pg_connection_limit", "postgres_scanner"}, {"pg_debug_show_queries", "postgres_scanner"}, - {"pg_experimental_filter_pushdown", "postgres_scanner"}, + {"pg_enable_filter_pushdown", "postgres_scanner"}, {"pg_idle_in_transaction_timeout_millis", "postgres_scanner"}, {"pg_null_byte_replacement", "postgres_scanner"}, {"pg_numeric_as_varchar", "postgres_scanner"}, {"pg_numeric_nan_as_null", "postgres_scanner"}, {"pg_oauth_token", "postgres_scanner"}, - {"pg_order_pushdown", "postgres_scanner"}, {"pg_pages_per_task", "postgres_scanner"}, {"pg_pool_acquire_mode", "postgres_scanner"}, {"pg_pool_enable_reaper_thread", "postgres_scanner"}, @@ -1373,6 +1394,7 @@ static constexpr ExtensionEntry EXTENSION_SETTINGS[] = { {"pg_pool_max_connections", "postgres_scanner"}, {"pg_pool_max_lifetime_millis", "postgres_scanner"}, {"pg_pool_wait_timeout_millis", "postgres_scanner"}, + {"pg_query_mode", "postgres_scanner"}, {"pg_staleness_query", "postgres_scanner"}, {"pg_staleness_query_enabled", "postgres_scanner"}, {"pg_statement_timeout_millis", "postgres_scanner"}, @@ -1401,10 +1423,13 @@ static constexpr ExtensionEntry EXTENSION_SETTINGS[] = { {"quack_server_keep_alive_timeout", "quack"}, {"quack_server_max_connections", "quack"}, {"quack_target_batch_bytes", "quack"}, + {"rtree_index_scan_min_rows", "spatial"}, + {"rtree_index_scan_ratio", "spatial"}, {"s3_access_key_id", "httpfs"}, {"s3_allow_recursive_globbing", "httpfs"}, {"s3_endpoint", "httpfs"}, {"s3_kms_key_id", "httpfs"}, + {"s3_list_concurrency", "httpfs"}, {"s3_region", "httpfs"}, {"s3_requester_pays", "httpfs"}, {"s3_secret_access_key", "httpfs"}, @@ -1537,14 +1562,36 @@ static constexpr ExtensionEntry EXTENSION_SECRET_PROVIDERS[] = { // Note: these are currently hardcoded in scripts/generate_extensions_function.py // TODO: automate by passing though to script via duckdb static constexpr ExtensionEntry EXTENSION_LOG_TYPES[] = { - {"DuckLakeMetadata", "ducklake"}, - {"Iceberg", "iceberg"}, + {"DeltaKernel", "delta"}, {"DuckLakeMetadata", "ducklake"}, {"HTTPFSInfo", "httpfs"}, + {"Iceberg", "iceberg"}, {"PostgresQueryLog", "postgres_scanner"}, {"Quack", "quack"}, }; // END_OF_EXTENSION_LOG_TYPES -static constexpr const char *AUTOLOADABLE_EXTENSIONS[] = { - "autocomplete", "avro", "aws", "azure", "core_functions", "delta", "ducklake", - "encodings", "excel", "fts", "httpfs", "iceberg", "icu", "inet", - "json", "motherduck", "mysql_scanner", "parquet", "postgres_scanner", "quack", "sqlite_scanner", - "sqlsmith", "tpcds", "tpch", "ui", "unity_catalog"}; // END_OF_AUTOLOADABLE_EXTENSIONS +static constexpr const char *AUTOLOADABLE_EXTENSIONS[] = {"autocomplete", + "avro", + "aws", + "azure", + "core_functions", + "delta", + "ducklake", + "encodings", + "excel", + "fts", + "httpfs", + "iceberg", + "icu", + "inet", + "json", + "legacy_parser", + "motherduck", + "mysql_scanner", + "parquet", + "postgres_scanner", + "quack", + "sqlite_scanner", + "sqlsmith", + "tpcds", + "tpch", + "ui", + "unity_catalog"}; // END_OF_AUTOLOADABLE_EXTENSIONS } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/extension_helper.hpp b/src/duckdb/src/include/duckdb/main/extension_helper.hpp index 2d89dd6e2..dd5b8a878 100644 --- a/src/duckdb/src/include/duckdb/main/extension_helper.hpp +++ b/src/duckdb/src/include/duckdb/main/extension_helper.hpp @@ -26,7 +26,6 @@ enum class ExtensionLoadResult : uint8_t { LOADED_EXTENSION = 0, EXTENSION_UNKNO struct DefaultExtension { const char *name; const char *description; - bool statically_loaded; }; struct ExtensionAlias { @@ -100,10 +99,8 @@ struct ExtensionInstallOptions { class ExtensionHelper { public: static void LoadAllExtensions(DuckDB &db); - static vector LoadedExtensionTestPaths(); static ExtensionLoadResult LoadExtension(DuckDB &db, const std::string &extension); - //! Publishes the extensions linked into this binary onto the config. Generated at build time; - //! a build that links none (or an extension carrying its own DuckDB) registers nothing. + //! Publishes the extensions linked into this binary, as collected by LinkedExtensionRegistry, onto the config static void RegisterLinkedExtensions(DBConfig &config); //! Install an extension @@ -120,10 +117,16 @@ class ExtensionHelper { //! Autoload an extension (depending on config, potentially a nop. Throws when installation fails) static void AutoLoadExtension(ClientContext &context, const string &extension_name); static void AutoLoadExtension(DatabaseInstance &db, const string &extension_name); + //! Autoload the extension a remote path requires + DUCKDB_API static void AutoLoadExtensionForPath(DatabaseInstance &db, const string &path, + const string &path_kind = "File"); //! Autoload an extension (depending on config, potentially a nop. Returns false on failure) DUCKDB_API static bool TryAutoLoadExtension(DatabaseInstance &db, const string &extension_name) noexcept; DUCKDB_API static bool TryAutoLoadExtension(ClientContext &context, const string &extension_name) noexcept; + //! Where automatic installs go: autoinstall_extension_repository, else custom_extension_repository, else + //! DBConfigOptions::default_autoinstall_repository, else the core repository + DUCKDB_API static ExtensionRepository GetAutoinstallRepository(DatabaseInstance &db); //! Autoload an extension, only if available locally DUCKDB_API static bool TryAutoLoadAvailableExtension(DatabaseInstance &instance, @@ -243,19 +246,17 @@ class ExtensionHelper { template static void TryAutoloadFromEntry(DatabaseInstance &db, const Identifier &entry, const ExtensionEntry (&entries)[N]) { -#ifndef DUCKDB_DISABLE_EXTENSION_LOAD if (Settings::Get(db)) { auto extension_name = ExtensionHelper::FindExtensionInEntries(entry, entries); - if (ExtensionHelper::CanAutoloadExtension(extension_name)) { + if (ExtensionHelper::CanAutoloadExtension(db, extension_name)) { ExtensionHelper::AutoLoadExtension(db, extension_name); } } -#endif } //! Whether an extension can be autoloaded (i.e. it's registered as an autoloadable extension in //! extension_entries.hpp) - static bool CanAutoloadExtension(const string &ext_name); + static bool CanAutoloadExtension(DatabaseInstance &db, const string &ext_name); //! Utility functions for creating meaningful error messages regarding missing extensions static string WrapAutoLoadExtensionErrorMsg(ClientContext &context, const string &base_error, diff --git a/src/duckdb/src/include/duckdb/main/extension_install_info.hpp b/src/duckdb/src/include/duckdb/main/extension_install_info.hpp index c62cea70d..8047e9293 100644 --- a/src/duckdb/src/include/duckdb/main/extension_install_info.hpp +++ b/src/duckdb/src/include/duckdb/main/extension_install_info.hpp @@ -92,6 +92,11 @@ struct ExtensionRepository { static constexpr const char *CORE_NIGHTLY_REPOSITORY_URL = "http://nightly-extensions.duckdb.org"; static constexpr const char *COMMUNITY_REPOSITORY_URL = "http://community-extensions.duckdb.org"; + //! Backup servers for the core and community repositories: they serve identical binaries and are used when the + //! primary server is unavailable + static constexpr const char *CORE_BACKUP_REPOSITORY_URL = "http://extensions.duckdb-backup.org"; + static constexpr const char *COMMUNITY_BACKUP_REPOSITORY_URL = "http://community-extensions.duckdb-backup.org"; + //! Debugging repositories (target local, relative paths that are produced by DuckDB's build system) static constexpr const char *BUILD_DEBUG_REPOSITORY_PATH = "./build/debug/repository"; static constexpr const char *BUILD_RELEASE_REPOSITORY_PATH = "./build/release/repository"; @@ -110,6 +115,10 @@ struct ExtensionRepository { static string TryGetRepositoryUrl(const string &repository); //! Try to convert a url to a known repository name, will return empty string if the repository is unknown static string TryConvertUrlToKnownRepository(const string &url); + //! Try to get the backup server url of a repository url, will return empty string if the repository has no backup + static string TryGetBackupRepositoryUrl(const string &url); + //! Whether a url points into one of the official DuckDB repositories (core, nightly, community or their backups) + static bool IsOfficialRepositoryUrl(const string &url); //! Get the default repository, optionally passing a config to allow static ExtensionRepository GetDefaultRepository(optional_ptr config); diff --git a/src/duckdb/src/include/duckdb/main/extension_manager.hpp b/src/duckdb/src/include/duckdb/main/extension_manager.hpp index b7c0f731b..d45cc4e4b 100644 --- a/src/duckdb/src/include/duckdb/main/extension_manager.hpp +++ b/src/duckdb/src/include/duckdb/main/extension_manager.hpp @@ -32,10 +32,16 @@ class ExtensionActiveLoad { ExtensionActiveLoad(DatabaseInstance &db, ExtensionInfo &info, Identifier extension_name_p, Identifier alias_p) : db(db), load_lock(info.lock), info(info), extension_name(std::move(extension_name_p)), alias(std::move(alias_p)) {}; + ExtensionActiveLoad(DatabaseInstance &db, shared_ptr info_p, Identifier extension_name_p, + Identifier alias_p) + : db(db), info_owner(std::move(info_p)), load_lock(info_owner->lock), info(*info_owner), + extension_name(std::move(extension_name_p)), alias(std::move(alias_p)) {}; ~ExtensionActiveLoad() = default; DatabaseInstance &db; + //! Keeps the info alive when the extension is removed from the manager during the load + shared_ptr info_owner; unique_lock load_lock; ExtensionInfo &info; Identifier extension_name; @@ -52,7 +58,7 @@ class ExtensionManager { DUCKDB_API bool ExtensionIsLoaded(const string &name); DUCKDB_API vector GetExtensions(); - DUCKDB_API optional_ptr GetExtensionInfo(const string &name); + DUCKDB_API shared_ptr GetExtensionInfo(const string &name); DUCKDB_API unique_ptr BeginLoad(const ExtensionLoadOptions &options); DUCKDB_API static ExtensionManager &Get(DatabaseInstance &db); @@ -63,7 +69,7 @@ class ExtensionManager { private: DatabaseInstance &db; mutex lock; - identifier_map_t> loaded_extensions_info; + identifier_map_t> loaded_extensions_info; string extension_load_prefix; }; diff --git a/src/duckdb/src/include/duckdb/main/http/http_client_pool.hpp b/src/duckdb/src/include/duckdb/main/http/http_client_pool.hpp index caded02f3..3c5dd7a04 100644 --- a/src/duckdb/src/include/duckdb/main/http/http_client_pool.hpp +++ b/src/duckdb/src/include/duckdb/main/http/http_client_pool.hpp @@ -76,7 +76,7 @@ class HTTPClientPool { }; enum class ReservationKind : uint8_t { REUSE, NEW_CLIENT }; - enum class IdleFilter : uint8_t { PROVIDER, CONNECTION, SESSION, ALL }; + enum class IdleFilter : uint8_t { PROVIDER, CONNECTION, SESSION, EXCESS, ALL }; struct Reservation { public: @@ -104,6 +104,9 @@ class HTTPClientPool { void Close() noexcept; bool IsClosed() const; bool HasAdmissionResource() const; + //! Reserve storage before increasing the admission limit + void SetCapacity(idx_t new_capacity); + idx_t GetCapacity() const; Reservation Reserve(const ClientKey &key, const string &origin, const HTTPTransportConfig &transport_config, bool cacheable); void AdoptPreparedBucket(Reservation &reservation); diff --git a/src/duckdb/src/include/duckdb/main/http/http_retry_budget.hpp b/src/duckdb/src/include/duckdb/main/http/http_retry_budget.hpp index 635f0ec00..7ea6228e3 100644 --- a/src/duckdb/src/include/duckdb/main/http/http_retry_budget.hpp +++ b/src/duckdb/src/include/duckdb/main/http/http_retry_budget.hpp @@ -9,6 +9,9 @@ #pragma once #include "duckdb/common/common.hpp" +#include "duckdb/common/mutex.hpp" +#include "duckdb/common/shared_ptr.hpp" +#include "duckdb/common/time_point.hpp" #include namespace duckdb { @@ -16,31 +19,116 @@ namespace duckdb { struct HTTPParams; //! An attempt's decision; constructing one does not consume retry budget. +//! +//! The loop that runs an operation is its coordinator: after inspecting a response (status, headers and body) it +//! decides explicitly whether the operation succeeded, failed for good, is retried, or is re-sent corrected. The budget +//! only admits retries, waits between them and keeps the host quota's books, refunding once after a reported success. +//! Finish() is the decision of a loop that does not judge the operation itself (core's request loop, or a provider +//! that has not adopted the explicit protocol): with a status-owned budget the quota is then settled from the HTTP +//! status core recorded; a coordinator-owned budget waits for the coordinator's report. class HTTPRetryDecision { friend class HTTPRetryBudget; friend class HTTPUtil; public: + //! Stop without judging the operation: a nested loop leaves the decision to the coordinator. The outermost loop of + //! a status-owned budget settles the quota from the HTTP status core recorded (the pre-protocol behaviour); a + //! coordinator-owned budget stays unsettled until ReportSuccess() or ReportFailure(). DUCKDB_API static HTTPRetryDecision Finish(); + //! The operation succeeded: the coordinator validated the response, the quota is refunded once. Only the owner + //! of a coordinator-owned budget decides this. + DUCKDB_API static HTTPRetryDecision Success(); + //! The operation failed for good (e.g. an error document behind HTTP 200): nothing is refunded. Only the owner of + //! a coordinator-owned budget decides this. + DUCKDB_API static HTTPRetryDecision Failure(); + //! Re-send after correcting the request (credentials, region, endpoint): no retry is consumed and no back-off is + //! waited, bounded by HTTPRetryBudget::MAX_CORRECTIONS per operation. + DUCKDB_API static HTTPRetryDecision Correction(); + //! Retry after a received server error (e.g. HTTP 500). DUCKDB_API static HTTPRetryDecision Retry(); + //! Retry after a transport failure (connection error, timeout, truncated body): no response was received. + DUCKDB_API static HTTPRetryDecision Transient(); + //! Retry after a throttling response (HTTP 429/503), honoring an optional Retry-After header value. + DUCKDB_API static HTTPRetryDecision Throttled(const string &retry_after = string()); private: - enum class Type : uint8_t { FINISH, RETRY, THROTTLED }; - HTTPRetryDecision(Type type_p, string retry_after_p = {}) : type(type_p), retry_after(std::move(retry_after_p)) { + enum class Type : uint8_t { FINISH, SUCCESS, FAILURE, CORRECTION, RETRY, TRANSIENT, THROTTLED }; + explicit HTTPRetryDecision(Type type_p, string retry_after_p = {}) + : type(type_p), retry_after(std::move(retry_after_p)) { } - static HTTPRetryDecision Throttled(const string &retry_after); private: Type type; string retry_after; }; -//! Operation-local budget shared by synchronous nested retry loops, never concurrent loops. +//! Retry tokens shared by every operation against one host, after the AWS SDK retry quota. +//! Retries spend tokens and successes refund them, so a host that keeps failing stops being retried. +//! A host nobody has talked to for a while starts over with a full quota, like the CRT drops an idle host's bucket. +class HTTPRetryQuota { +public: + static constexpr int64_t INITIAL_CAPACITY = 500; + //! Cost of retrying a received server error or throttle: the server answered, it is just unhappy. + static constexpr int64_t RETRY_COST = 5; + //! Cost of retrying a transport failure: nothing is known about the server, so it costs more. + static constexpr int64_t TRANSIENT_RETRY_COST = 10; + //! Refund for a request that succeeded without retrying. + static constexpr int64_t NO_RETRY_REFUND = 1; + //! A quota untouched for this long is full again when it is next used. + static constexpr int64_t IDLE_RESET_MS = 60 * 1000; + +public: + //! Take `cost` tokens; false (and nothing taken) when fewer are available. + DUCKDB_API bool TryAcquire(int64_t cost, int64_t now_ms = TimePoint::GetTickMs()); + //! Return tokens, never above the initial capacity. + DUCKDB_API void Release(int64_t amount, int64_t now_ms = TimePoint::GetTickMs()); + DUCKDB_API int64_t Available() const; + //! Milliseconds since the quota was last charged or refunded. + DUCKDB_API int64_t IdleMs(int64_t now_ms = TimePoint::GetTickMs()) const; + +private: + //! Refill an idle quota and record the use; the lock must be held. + void Touch(int64_t now_ms); + +private: + mutable mutex lock; + int64_t capacity = INITIAL_CAPACITY; + int64_t last_used_ms = TimePoint::GetTickMs(); +}; + +//! Who settles an operation's outcome with the host quota, fixed when the operation starts. +enum class HTTPRetryOutcomeOwner : uint8_t { + //! Nobody validates beyond the HTTP status: the status core recorded settles the quota once the outermost loop + //! stops. Core's own request loop and loops that have not adopted the explicit protocol. + STATUS, + //! The coordinator that runs the operation validates the response and settles the quota itself, with a + //! Success/Failure decision or ReportSuccess/ReportFailure after its loop; core's status is never final for it. + //! An operation whose coordinator never reports stays charged, like one whose outcome is unknown. + COORDINATOR +}; + +//! Operation-local budget shared by synchronous nested retry loops, never concurrent loops. One budget is one +//! logical operation (one `amz-sdk-invocation-id`): every send it makes, including corrected re-sends, is one of +//! its attempts, as in the AWS SDK's attempt loop. The host quota is settled once per operation, when its outcome is +//! known, by the owner fixed at construction: a coordinator reports it (Success/Failure decisions or +//! ReportSuccess/ReportFailure, whenever it has validated the response, inside or after its loop); for a status-owned +//! budget it is taken from the HTTP status core recorded (RecordStatusOutcome). A status recorded inside an outer Run +//! waits for that loop to stop, since an outer loop may still retry. class HTTPRetryBudget { friend class HTTPUtil; public: - DUCKDB_API explicit HTTPRetryBudget(const HTTPParams ¶ms); + //! Longest single back-off sleep, as in the AWS SDKs. + static constexpr uint64_t MAX_BACKOFF_MS = 20000; + //! Retries granted beyond `http_retries` for throttled responses: the server asked to slow down, not to stop. + static constexpr uint64_t THROTTLE_EXTRA_RETRIES = 5; + //! Corrected re-sends admitted per operation: a credential refresh and a region redirect, with room for a + //! provider that needs both again after the other; past this the operation ends with the last response. + static constexpr uint64_t MAX_CORRECTIONS = 4; + +public: + DUCKDB_API explicit HTTPRetryBudget(const HTTPParams ¶ms, + HTTPRetryOutcomeOwner owner = HTTPRetryOutcomeOwner::STATUS); HTTPRetryBudget(const HTTPRetryBudget &) = delete; HTTPRetryBudget &operator=(const HTTPRetryBudget &) = delete; @@ -48,20 +136,90 @@ class HTTPRetryBudget { //! Run once, then consume budget and wait before each requested retry. Exhaustion returns normally. //! Callbacks establish replay safety; exceptions propagate without further retries. DUCKDB_API void Run(const std::function &attempt); + //! Retries admitted for HTTP 429 or 503 responses. + DUCKDB_API uint64_t ThrottledRetries() const; + //! Retries admitted across all participating requests. + DUCKDB_API uint64_t RetriesUsed() const; + //! Number the next send of the operation: the first call returns 1. + DUCKDB_API uint64_t BeginAttempt(); + //! Sends so far that were neither the first nor an admitted retry: re-sends after the coordinator corrected the + //! request (credentials, region), which do not consume retries. + DUCKDB_API uint64_t Corrections() const; + //! Most attempts the operation can make: the first one, `http_retries`, the throttle extras and the corrected + //! re-sends made so far, so that every numbered attempt stays within the bound advertised with it. + DUCKDB_API uint64_t MaxAttempts() const; + //! Whether the current Run refused a retry because the host's quota ran out. + DUCKDB_API bool QuotaExhausted() const; + //! Whether the current Run refused a corrected re-send because MAX_CORRECTIONS were made already. + DUCKDB_API bool CorrectionsExhausted() const; + //! Charge the next admitted retries to this host's quota; a request to another host rebinds. + DUCKDB_API void BindQuota(shared_ptr quota); + //! Who settles this operation's outcome. + DUCKDB_API HTTPRetryOutcomeOwner Owner() const; + //! The coordinator's final word, for a coordinator that validates the response after its Run returned: the + //! operation succeeded, refund the last admitted retry to the quota it was charged to, or NO_RETRY_REFUND to the + //! bound quota. Settles once; a later report of the same operation changes nothing. Coordinator-owned budgets only. + DUCKDB_API void ReportSuccess(); + //! The coordinator's final word: the operation failed, the last admitted retry is not refunded. Coordinator-owned + //! budgets only. + DUCKDB_API void ReportFailure(); + //! What the HTTP status of the last response says, recorded by core's request loop. For a status-owned budget it + //! settles the quota, at once outside a Run or when the outermost Run stops; a coordinator-owned budget ignores + //! it, its coordinator judges the response. + DUCKDB_API void RecordStatusOutcome(bool success); + //! Whether a Run of this budget is in progress, i.e. a nested loop is running inside an outer one. + DUCKDB_API bool InRun() const; + + //! Back-off window for `retry` is [0, min(wait * backoff^(retry - 1), MAX_BACKOFF_MS)]: full jitter, or its upper + //! half for throttled retries so they always keep some distance. The result is raised to `floor_ms`. + DUCKDB_API static uint64_t BackoffMs(uint64_t retry_wait_ms, float retry_backoff, uint64_t retry, uint64_t floor_ms, + uint64_t random_value, bool throttled = false); + //! Parse a Retry-After header (delay in seconds or an HTTP-date) into a delay in milliseconds. + DUCKDB_API static bool TryParseRetryAfterMs(const string &retry_after, uint64_t &delay_ms); private: //! Core's hook runs after admission/backoff, outside the attempt's transport-error handling. void Run(const std::function &attempt, const std::function &before_retry); bool ConsumeAndWait(const HTTPRetryDecision &decision); + //! Admit a corrected re-send within MAX_CORRECTIONS. + bool AdmitCorrection(); + uint64_t ThrottleExtraRetries() const; + //! Settle the operation's outcome with the quota once: refund on success, then forget the last admitted retry. + void Settle(bool success); + //! Explicit decisions and reports belong to a coordinator-owned budget; anything else is a programming error. + void RequireCoordinator(const char *what) const; + //! Forget the last admitted retry, so that nothing is refunded for it. + void ForgetLastRetry(); private: //! Retry settings captured for the operation. const uint64_t retries; const uint64_t retry_wait_ms; const float retry_backoff; + //! Who settles the outcome with the quota. + const HTTPRetryOutcomeOwner owner; //! Retries admitted across all participating requests. uint64_t retries_used = 0; + uint64_t throttled_retries = 0; + //! Sends numbered so far. + uint64_t attempts = 0; + //! Corrected re-sends admitted by Correction() decisions. + uint64_t corrections_admitted = 0; + //! Nesting depth of Run; status outcomes recorded while this is positive wait for the outermost loop's decision. + uint64_t run_depth = 0; + //! Outcome core recorded from the HTTP status, pending the outermost loop's stop (status-owned budgets only). + enum class RecordedOutcome : uint8_t { NONE, SUCCESS, FAILURE }; + RecordedOutcome status_outcome = RecordedOutcome::NONE; + //! Whether the operation's outcome has been settled with the quota; a new admitted retry starts a new settlement. + bool outcome_settled = false; + //! Quota of the host the current request goes to, if any. + shared_ptr quota; + //! Quota and tokens of the most recent admitted retry, refunded on success. + shared_ptr last_retry_quota; + int64_t last_retry_cost = 0; + bool quota_exhausted = false; + bool corrections_exhausted = false; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/http/http_transport_manager.hpp b/src/duckdb/src/include/duckdb/main/http/http_transport_manager.hpp index c286fe343..0176bf846 100644 --- a/src/duckdb/src/include/duckdb/main/http/http_transport_manager.hpp +++ b/src/duckdb/src/include/duckdb/main/http/http_transport_manager.hpp @@ -57,6 +57,11 @@ class HTTPTransportManager { public: DUCKDB_API ~HTTPTransportManager(); + //! Retire excess idle clients and discard excess active clients on release + DUCKDB_API void SetCapacity(idx_t capacity) DUCKDB_EXCLUDES(lock); + DUCKDB_API idx_t GetCapacity() const DUCKDB_EXCLUDES(lock); + DUCKDB_API static idx_t AutomaticCapacity(const DBConfig &config); + private: explicit HTTPTransportManager(const shared_ptr &initial_http_util); @@ -120,9 +125,10 @@ class HTTPTransportManager { //! Database lifecycle helpers. static unique_ptr Create(const shared_ptr &initial_http_util); - void Initialize(idx_t system_concurrency) DUCKDB_EXCLUDES(lock); + void Initialize(const DBConfig &config) DUCKDB_EXCLUDES(lock); void Close() noexcept DUCKDB_EXCLUDES(lock); - static idx_t CalculateCapacity(idx_t system_concurrency, optional_idx file_descriptor_limit); + static idx_t CalculateCapacity(idx_t system_concurrency, optional_idx file_descriptor_limit, + idx_t io_concurrency = 0); static optional_idx GetFileDescriptorLimit(); static bool AdvanceConnectionEpoch(uint64_t &connection_epoch, bool &reuse_poisoned) noexcept; void SetHTTPUtil(const shared_ptr &new_http_util) DUCKDB_EXCLUDES(lock); diff --git a/src/duckdb/src/include/duckdb/main/http/http_util.hpp b/src/duckdb/src/include/duckdb/main/http/http_util.hpp index ef431386a..40385438e 100644 --- a/src/duckdb/src/include/duckdb/main/http/http_util.hpp +++ b/src/duckdb/src/include/duckdb/main/http/http_util.hpp @@ -14,6 +14,9 @@ #include "duckdb/common/enums/http_status_code.hpp" #include "duckdb/common/types/timestamp.hpp" #include "duckdb/common/time_point.hpp" +#include "duckdb/common/mutex.hpp" +#include "duckdb/common/unordered_map.hpp" +#include "duckdb/main/http/http_retry_budget.hpp" #include namespace duckdb { @@ -46,6 +49,9 @@ struct HTTPParams { bool follow_location = true; bool override_verify_ssl = false; bool verify_ssl = true; + //! When set, the caller has already checked the url against the file access settings, so the central check in + //! HTTPUtil::Request is skipped (used by extension installation, which also allows the official repositories) + bool skip_external_access_check = false; string http_proxy; idx_t http_proxy_port = 0; @@ -78,6 +84,10 @@ struct HTTPParams { friend class HTTPUtil; friend class HTTPTransportManager; + bool ShouldRedactLogs() const; + + //! Borrowed database. + optional_ptr db; //! Manager borrowed from the owning DatabaseInstance. optional_ptr transport_manager; //! Provider publication captured by the creating manager session. @@ -392,8 +402,18 @@ class HTTPUtil { virtual unique_ptr SendRequest(BaseRequest &request, unique_ptr &client); virtual void LogRequest(BaseRequest &request, optional_ptr response); - //! Whether a failed request should be retried, possibly using HTTPResponse information, and allowing overrides + //! Whether a failed request should be retried. Judges received responses and statuses a handler turned into an + //! exception alike; core knows only the generic statuses (IsRetryableStatus), a provider adds what its service + //! says in the body (e.g. an S3 error document whose code is retryable behind a final-looking status). DUCKDB_API virtual bool ShouldRetry(const BaseRequest &request, const HTTPResponse &response); + //! Runs before every attempt with the 1-based attempt number and the configured maximum, e.g. to tag headers. + DUCKDB_API virtual void PrepareAttempt(BaseRequest &request, idx_t attempt, idx_t max_attempts); + //! Retry quota shared by all requests of this provider against one host. + DUCKDB_API shared_ptr GetRetryQuota(const string &proto_host_port); + //! Whether a received status is worth retrying: 408, 418, 429 and the server errors that can clear up. + DUCKDB_API static bool IsRetryableStatus(HTTPStatusCode status); + //! Hosts remembered for retry quotas; past this the host unused for longest makes room. + static constexpr idx_t MAX_RETRY_QUOTAS = 1024; //! Whether replaying this request is safe. POST is the only method we cannot assume is idempotent. DUCKDB_API static bool IsIdempotent(RequestType type); @@ -412,9 +432,15 @@ class HTTPUtil { HTTPClientCachePolicy initial_cache_policy, HTTPClientCachePolicy &retry_cache_policy); +private: + //! Protects retry_quotas. + mutex retry_quota_lock; + //! Per-host retry quotas, created on first use; budgets keep theirs alive across an eviction. + unordered_map> retry_quotas; + public: static duckdb::unique_ptr - RunRequestWithRetry(const std::function(void)> &on_request, const BaseRequest &request, + RunRequestWithRetry(const std::function(void)> &on_request, BaseRequest &request, const std::function &retry_cb); }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/materialized_query_result.hpp b/src/duckdb/src/include/duckdb/main/materialized_query_result.hpp deleted file mode 100644 index f656a07aa..000000000 --- a/src/duckdb/src/include/duckdb/main/materialized_query_result.hpp +++ /dev/null @@ -1,67 +0,0 @@ -//===----------------------------------------------------------------------===// -// DuckDB -// -// duckdb/main/materialized_query_result.hpp -// -// -//===----------------------------------------------------------------------===// - -#pragma once - -#include "duckdb/common/types/column/column_data_collection.hpp" -#include "duckdb/common/winapi.hpp" -#include "duckdb/main/query_result.hpp" - -namespace duckdb { - -class ClientContext; - -class MaterializedQueryResult : public QueryResult { -public: - static constexpr const QueryResultType TYPE = QueryResultType::MATERIALIZED_RESULT; - -public: - friend class ClientContext; - //! Creates a successful query result with the specified names and types - DUCKDB_API MaterializedQueryResult(StatementType statement_type, StatementProperties properties, - vector names, unique_ptr collection, - ClientProperties client_properties); - //! Creates an unsuccessful query result with error condition - DUCKDB_API explicit MaterializedQueryResult(ErrorData error); - -public: - //! Converts the QueryResult to a string - DUCKDB_API string ToString() override; - DUCKDB_API string ToBox(BoxRendererContext &context, const BoxRendererConfig &config) override; - - //! Gets the (index) value of the (column index) column. - //! Note: this is very slow. Scanning over the underlying collection is much faster. - DUCKDB_API Value GetValue(idx_t column, idx_t index); - - template - T GetValue(idx_t column, idx_t index) { - auto value = GetValue(column, index); - return (T)value.GetValue(); - } - - DUCKDB_API idx_t RowCount() const; - - //! Returns a reference to the underlying column data collection - ColumnDataCollection &Collection(); - - //! Takes ownership of the collection, 'collection' is null after this operation - unique_ptr TakeCollection(); - -protected: - DUCKDB_API unique_ptr FetchInternal() override; - -private: - unique_ptr collection; - //! Row collection, only created if GetValue is called - unique_ptr row_collection; - //! Scan state for Fetch calls - ColumnDataScanState scan_state; - bool scan_initialized; -}; - -} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/pending_query_result.hpp b/src/duckdb/src/include/duckdb/main/pending_query_result.hpp deleted file mode 100644 index cf0268712..000000000 --- a/src/duckdb/src/include/duckdb/main/pending_query_result.hpp +++ /dev/null @@ -1,70 +0,0 @@ -//===----------------------------------------------------------------------===// -// DuckDB -// -// duckdb/main/pending_query_result.hpp -// -// -//===----------------------------------------------------------------------===// - -#pragma once - -#include "duckdb/main/query_result.hpp" -#include "duckdb/common/enums/pending_execution_result.hpp" -#include "duckdb/execution/executor.hpp" - -namespace duckdb { -class ClientContext; -class ClientContextLock; -class PreparedStatementData; - -class PendingQueryResult : public BaseQueryResult { - friend class ClientContext; - -public: - static constexpr const QueryResultType TYPE = QueryResultType::PENDING_RESULT; - -public: - DUCKDB_API PendingQueryResult(shared_ptr context, PreparedStatementData &statement, - vector types, bool allow_stream_result); - DUCKDB_API explicit PendingQueryResult(ErrorData error_message); - DUCKDB_API ~PendingQueryResult() override; - DUCKDB_API bool AllowStreamResult() const; - PendingQueryResult(const PendingQueryResult &) = delete; - PendingQueryResult &operator=(const PendingQueryResult &) = delete; - -public: - //! Executes a single task within the query, returning whether or not the query is ready. - //! If this returns RESULT_READY, the Execute function can be called to obtain a pointer to the result. - //! If this returns RESULT_NOT_READY, the ExecuteTask function should be called again. - //! If this returns EXECUTION_ERROR, an error occurred during execution. - //! If this returns NO_TASKS_AVAILABLE, this means currently no meaningful work can be done by the current executor, - //! but tasks may become available in the future. - //! The error message can be obtained by calling GetError() on the PendingQueryResult. - DUCKDB_API PendingExecutionResult ExecuteTask(); - DUCKDB_API PendingExecutionResult CheckPulse(); - //! Halt execution of the thread until a Task is ready to be executed (use with caution) - void WaitForTask(); - - //! Returns the result of the query as an actual query result. - //! This returns (mostly) instantly if ExecuteTask has been called until RESULT_READY was returned. - DUCKDB_API unique_ptr Execute(); - - DUCKDB_API void Close(); - - //! Function to determine whether execution is considered finished - DUCKDB_API static bool IsResultReady(PendingExecutionResult result); - DUCKDB_API static bool IsExecutionFinished(PendingExecutionResult result); - -private: - shared_ptr context; - bool allow_stream_result; - -private: - void CheckExecutableInternal(ClientContextLock &lock); - - PendingExecutionResult ExecuteTaskInternal(ClientContextLock &lock); - unique_ptr ExecuteInternal(ClientContextLock &lock); - unique_ptr LockContext(); -}; - -} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/prepared_statement.hpp b/src/duckdb/src/include/duckdb/main/prepared_statement.hpp index 827516661..b99700d0a 100644 --- a/src/duckdb/src/include/duckdb/main/prepared_statement.hpp +++ b/src/duckdb/src/include/duckdb/main/prepared_statement.hpp @@ -12,8 +12,8 @@ #include "duckdb/common/identifier.hpp" #include "duckdb/common/pair.hpp" #include "duckdb/common/winapi.hpp" -#include "duckdb/main/materialized_query_result.hpp" -#include "duckdb/main/pending_query_result.hpp" +#include "duckdb/main/query_parameters.hpp" +#include "duckdb/main/query_result.hpp" #include "duckdb/main/client_config.hpp" #include "duckdb/common/error_data.hpp" #include "duckdb/common/case_insensitive_map.hpp" @@ -43,11 +43,14 @@ struct PreparedStatementInfo { //! A handle to a prepared statement that lives in the client context. The statement itself is prepared through //! `PREPARE AS ...` and executed through `EXECUTE (...)` - this class only holds the name. +//! A statement that expands into several is not prepared in the client context: each execution submits it anew. class PreparedStatement { public: //! Create a handle to the prepared statement with the given name in the client context DUCKDB_API PreparedStatement(const shared_ptr &context, string name, string query, PreparedStatementInfo info); + //! Create a handle to a statement that does not expand into exactly one, whose result is known only at execution + DUCKDB_API PreparedStatement(const shared_ptr &context, unique_ptr statement); //! Create a prepared statement that was not successfully prepared DUCKDB_API explicit PreparedStatement(ErrorData error); @@ -88,29 +91,31 @@ class PreparedStatement { DUCKDB_API bool TryGetParameterType(const Identifier &identifier, LogicalType &result) const; //! Returns the map of parameter index to the expected type of parameter DUCKDB_API case_insensitive_map_t GetExpectedParameterTypes() const; + //! Whether the result names, types and properties are known before execution; runs nothing + DUCKDB_API bool MetadataAvailable() const; - //! Create a pending query result of the prepared statement with the given set of arguments + //! Non-blocking. Submits the prepared statement with the given arguments and returns its handle template - unique_ptr PendingQuery(ARGS... args) { + unique_ptr Submit(ARGS... args) { vector values; - return PendingQueryRecursive(values, args...); + return SubmitRecursive(values, args...); } - //! Create a pending query result of the prepared statement with the given set of arguments - DUCKDB_API unique_ptr PendingQuery(vector &values, bool allow_stream_result = true); + //! Non-blocking. Submits the prepared statement with the given values and returns its handle + DUCKDB_API unique_ptr Submit(vector &values, const QueryParameters &query_parameters = {}); - //! Create a pending query result of the prepared statement with the given set named arguments - DUCKDB_API unique_ptr PendingQuery(identifier_map_t &named_values, - bool allow_stream_result = true); + //! Non-blocking. Submits the prepared statement with the given named values and returns its handle + DUCKDB_API unique_ptr Submit(identifier_map_t &named_values, + const QueryParameters &query_parameters = {}); - //! Execute the prepared statement with the given set of values - DUCKDB_API unique_ptr Execute(vector &values, bool allow_stream_result = true); + //! Blocking. Runs the prepared statement with the given values to completion + DUCKDB_API unique_ptr Execute(vector &values, const QueryParameters &query_parameters = {}); - //! Execute the prepared statement with the given set of named+unnamed values + //! Blocking. Runs the prepared statement with the given named and unnamed values to completion DUCKDB_API unique_ptr Execute(identifier_map_t &named_values, - bool allow_stream_result = true); + const QueryParameters &query_parameters = {}); - //! Execute the prepared statement with the given set of arguments + //! Blocking. Runs the prepared statement with the given arguments to completion template unique_ptr Execute(ARGS... args) { vector values; @@ -206,19 +211,23 @@ class PreparedStatement { ErrorData error; //! The metadata of the statement, as it was prepared PreparedStatementInfo info; + //! The statement as the user wrote it, when it does not expand into exactly one + unique_ptr expanding_statement; private: - //! Create the `EXECUTE (...)` statement that runs this prepared statement with the given values - unique_ptr CreateExecuteStatement(const identifier_map_t &named_values) const; + //! Create the statement that runs this prepared statement with the given values: `EXECUTE (...)`, or a + //! copy of the statement that expands, whose values are handed to the submission in the parameters + unique_ptr CreateStatement(identifier_map_t &named_values, + QueryParameters ¶meters) const; - unique_ptr PendingQueryRecursive(vector &values) { - return PendingQuery(values); + unique_ptr SubmitRecursive(vector &values) { + return Submit(values); } template - unique_ptr PendingQueryRecursive(vector &values, T value, ARGS... args) { + unique_ptr SubmitRecursive(vector &values, T value, ARGS... args) { values.push_back(Value::CreateValue(value)); - return PendingQueryRecursive(values, args...); + return SubmitRecursive(values, args...); } unique_ptr ExecuteRecursive(vector &values) { diff --git a/src/duckdb/src/include/duckdb/main/prepared_statement_data.hpp b/src/duckdb/src/include/duckdb/main/prepared_statement_data.hpp index 5030408ca..80d7618a8 100644 --- a/src/duckdb/src/include/duckdb/main/prepared_statement_data.hpp +++ b/src/duckdb/src/include/duckdb/main/prepared_statement_data.hpp @@ -44,10 +44,6 @@ class PreparedStatementData { //! The map of parameter index to the actual value entry bound_parameter_map_t value_map; - //! Whether we are creating a streaming result or not - QueryResultOutputType output_type; - //! Whether we are creating a buffer-managed result or not - QueryResultMemoryType memory_type; public: void CheckParameterCount(idx_t parameter_count); @@ -63,4 +59,7 @@ class PreparedStatementData { DUCKDB_API bool TryGetType(const Identifier &identifier, LogicalType &result); }; +DUCKDB_API bool CheckCatalogIdentity(ClientContext &context, const Identifier &catalog_name, + StatementProperties::CatalogIdentity catalog_identity); + } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/profiler/gathered_metrics.hpp b/src/duckdb/src/include/duckdb/main/profiler/gathered_metrics.hpp index d9319c0db..9e0261a74 100644 --- a/src/duckdb/src/include/duckdb/main/profiler/gathered_metrics.hpp +++ b/src/duckdb/src/include/duckdb/main/profiler/gathered_metrics.hpp @@ -12,6 +12,7 @@ #include "duckdb/common/enums/output_type.hpp" #include "duckdb/common/progress_bar/progress_bar.hpp" #include "duckdb/common/types/value.hpp" +#include "duckdb/common/types/timestamp.hpp" #include "duckdb/common/unordered_set.hpp" #include "duckdb/common/constants.hpp" #include "duckdb/common/enums/metric_type.hpp" @@ -28,6 +29,41 @@ namespace duckdb { struct QueryProfileResult; enum class ProfilingParameterNames : uint8_t { FORMAT, COVERAGE, SAVE_LOCATION, MODE, METRICS }; +struct MetricTiming { + idx_t duration = 0; + timestamp_t start_time = timestamp_t::epoch(); + timestamp_t end_time = timestamp_t::epoch(); + bool has_bounds = false; + + void Add(idx_t addition) { + duration += addition; + } + + void Add(idx_t addition, timestamp_t start, timestamp_t end) { + Add(addition); + UpdateBounds(start, end); + } + + void Merge(const MetricTiming &other) { + Add(other.duration); + if (other.has_bounds) { + UpdateBounds(other.start_time, other.end_time); + } + } + +private: + void UpdateBounds(timestamp_t start, timestamp_t end) { + if (!has_bounds) { + start_time = start; + end_time = end; + has_bounds = true; + return; + } + start_time = MinValue(start_time, start); + end_time = MaxValue(end_time, end); + } +}; + class GatheredMetrics { public: GatheredMetrics() = default; @@ -72,7 +108,7 @@ class GatheredMetrics { } public: - void WriteMetricsToLog(ClientContext &context) const; + void WriteMetricsToLog(ClientContext &context, const unordered_map &timings) const; //! Copy all enabled metrics into a QueryProfileResult node using lowercase string keys void MetricsToProfileResult(QueryProfileResult &result) const; diff --git a/src/duckdb/src/include/duckdb/main/profiler/metrics.hpp b/src/duckdb/src/include/duckdb/main/profiler/metrics.hpp index e2b79316b..efca6ee1f 100644 --- a/src/duckdb/src/include/duckdb/main/profiler/metrics.hpp +++ b/src/duckdb/src/include/duckdb/main/profiler/metrics.hpp @@ -90,7 +90,7 @@ struct MetricSystemPeakBufferMemory { struct MetricSystemPeakStreamingBufferSize { using METRIC_TYPE = uint64_t; static constexpr const char *Name = "system.peak_streaming_buffer_size"; - static constexpr const char *Description = "Peak number of bytes buffered by a streaming query result"; + static constexpr const char *Description = "Peak number of bytes a streaming query result held: queued bytes plus the units blocked workers hold, both exact. Units still under construction are not counted"; static constexpr const char *Unit = "bytes"; static constexpr const char *TypeStr = "uint64"; }; diff --git a/src/duckdb/src/include/duckdb/main/profiler/profiling_utils.hpp b/src/duckdb/src/include/duckdb/main/profiler/profiling_utils.hpp index 7ea85913e..8dadb39eb 100644 --- a/src/duckdb/src/include/duckdb/main/profiler/profiling_utils.hpp +++ b/src/duckdb/src/include/duckdb/main/profiler/profiling_utils.hpp @@ -13,6 +13,7 @@ #include "duckdb/main/profiler/gathered_metrics.hpp" #include "duckdb/main/profiler/profiling_node.hpp" #include "duckdb/common/profiler.hpp" +#include "duckdb/common/types/timestamp.hpp" namespace duckdb_yyjson { struct yyjson_mut_doc; @@ -50,7 +51,11 @@ struct QueryMetrics { public: void UpdateMetric(const string &key, idx_t addition) { - string_timings[key] += addition; + string_timings[key].Add(addition); + } + + void UpdateMetric(const string &key, idx_t addition, timestamp_t start_time, timestamp_t end_time) { + string_timings[key].Add(addition, start_time, end_time); } void UpdateMetricCounter(const string &key, idx_t addition) { @@ -82,7 +87,7 @@ struct QueryMetrics { if (it == string_timings.end()) { return 0.0; } - return static_cast(it->second) / 1e9; + return static_cast(it->second.duration) / 1e9; } idx_t GetStringCounter(const string &key) const { @@ -125,7 +130,7 @@ struct QueryMetrics { return total_memory_allocated.load(); } - const unordered_map &GetMetricTimings() const { + const unordered_map &GetMetricTimings() const { return string_timings; } @@ -158,7 +163,7 @@ struct QueryMetrics { void Merge(const QueryMetrics &other) { for (const auto &entry : other.string_timings) { - string_timings[entry.first] += entry.second; + string_timings[entry.first].Merge(entry.second); } for (const auto &entry : other.string_counters) { string_counters[entry.first] += entry.second; @@ -175,7 +180,7 @@ struct QueryMetrics { private: // String-keyed timings for optimizer, storage and phase timing metrics - unordered_map string_timings; + unordered_map string_timings; // String-keyed counters for storage counter metrics (e.g. "storage.wal_replay_entry_count") unordered_map string_counters; @@ -193,6 +198,7 @@ struct MetricsTimer { if (!is_active) { return; } + start_time = Timestamp::GetCurrentTimestamp(); profiler.Start(); } ~MetricsTimer() { @@ -208,12 +214,14 @@ struct MetricsTimer { std::swap(query_metrics, other.query_metrics); std::swap(metric_name, other.metric_name); std::swap(profiler, other.profiler); + std::swap(start_time, other.start_time); std::swap(is_active, other.is_active); } MetricsTimer &operator=(MetricsTimer &&other) noexcept { std::swap(query_metrics, other.query_metrics); std::swap(metric_name, other.metric_name); std::swap(profiler, other.profiler); + std::swap(start_time, other.start_time); std::swap(is_active, other.is_active); return *this; } @@ -225,7 +233,7 @@ struct MetricsTimer { } is_active = false; profiler.End(); - query_metrics->UpdateMetric(metric_name, profiler.ElapsedNanos()); + query_metrics->UpdateMetric(metric_name, profiler.ElapsedNanos(), start_time, Timestamp::GetCurrentTimestamp()); } void Reset() { @@ -240,6 +248,7 @@ struct MetricsTimer { optional_ptr query_metrics; string metric_name; Profiler profiler; + timestamp_t start_time = timestamp_t::epoch(); bool is_active; }; diff --git a/src/duckdb/src/include/duckdb/main/query_parameters.hpp b/src/duckdb/src/include/duckdb/main/query_parameters.hpp new file mode 100644 index 000000000..8df4726fc --- /dev/null +++ b/src/duckdb/src/include/duckdb/main/query_parameters.hpp @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/main/query_parameters.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/identifier.hpp" +#include "duckdb/common/optional_ptr.hpp" +#include "duckdb/common/shared_ptr.hpp" +#include "duckdb/planner/expression/bound_parameter_data.hpp" + +namespace duckdb { + +class ResultFormat; + +struct QueryParameters { + QueryParameters() = default; + //! So a caller that wants only a format does not have to spell out the other fields + explicit QueryParameters(shared_ptr format_p) : format(std::move(format_p)) { + } + + //! Arguments for a parameterized statement (may be null) + optional_ptr> statement_args; + shared_ptr format; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/query_profiler.hpp b/src/duckdb/src/include/duckdb/main/query_profiler.hpp index 45053bf65..d147a186b 100644 --- a/src/duckdb/src/include/duckdb/main/query_profiler.hpp +++ b/src/duckdb/src/include/duckdb/main/query_profiler.hpp @@ -8,6 +8,7 @@ #pragma once +#include "duckdb/common/atomic.hpp" #include "duckdb/common/common.hpp" #include "duckdb/common/enums/profiling_coverage.hpp" #include "duckdb/common/deque.hpp" @@ -114,7 +115,7 @@ class QueryProfiler { //! Add to a metric counter (profiling-only). DUCKDB_API void AddToMetricCounter(const string &key, idx_t amount); //! Add parser time measured before the per-statement profiler was started. - void AddParserTime(const Profiler &parser_timer); + void AddParserTime(const Profiler &parser_timer, timestamp_t start_time, timestamp_t end_time); //! Set an arbitrary metric value (profiling-only; no-op when profiling is disabled). DUCKDB_API void SetMetric(const string &key, Value new_value); @@ -183,11 +184,19 @@ class QueryProfiler { //! disabled case. Only used on the terminal-print paths. void PrintProfilerOutput(optional_ptr renderer) const; +public: + //! Provides access to the root of the query tree, but ensures there are no concurrent modifications + //! This can be useful when implementing continuous profiling or making customizations + DUCKDB_API void GetRootUnderLock(const std::function)> &callback) { + lock_guard guard(lock); + callback(root.get()); + } + private: ClientContext &context; - //! Whether or not the query profiler is running - bool running; + //! Atomic so inactive flushes can skip acquiring the profiler lock. + atomic running; //! The lock used for accessing the global query profiler or flushing information to it from a thread mutable std::mutex lock; diff --git a/src/duckdb/src/include/duckdb/main/query_result.hpp b/src/duckdb/src/include/duckdb/main/query_result.hpp index ea605daf9..0ec695e99 100644 --- a/src/duckdb/src/include/duckdb/main/query_result.hpp +++ b/src/duckdb/src/include/duckdb/main/query_result.hpp @@ -8,31 +8,42 @@ #pragma once +#include "duckdb/common/enums/query_result_state.hpp" #include "duckdb/common/enums/statement_type.hpp" #include "duckdb/common/identifier.hpp" +#include "duckdb/common/types/column/column_data_collection.hpp" #include "duckdb/common/types/data_chunk.hpp" #include "duckdb/common/winapi.hpp" #include "duckdb/common/error_data.hpp" #include "duckdb/main/client_properties.hpp" +#include "duckdb/main/result_format.hpp" +#include "duckdb/main/result_unit.hpp" +#include "duckdb/main/retained_result_collection.hpp" + +#include namespace duckdb { class BoxRendererContext; struct BoxRendererConfig; - -enum class QueryResultType : uint8_t { MATERIALIZED_RESULT, STREAM_RESULT, PENDING_RESULT, ARROW_RESULT }; +class BufferedData; +class ClientContext; +class ClientContextLock; +class PreparedStatementData; +class QueryResult; class BaseQueryResult { public: //! Creates a successful query result with the specified names and types - DUCKDB_API BaseQueryResult(QueryResultType type, StatementType statement_type, StatementProperties properties, - vector types, vector names); + DUCKDB_API BaseQueryResult(StatementType statement_type, StatementProperties properties, vector types, + vector names); //! Creates an unsuccessful query result with error condition - DUCKDB_API BaseQueryResult(QueryResultType type, ErrorData error); + DUCKDB_API explicit BaseQueryResult(ErrorData error); DUCKDB_API virtual ~BaseQueryResult(); public: - //! Returns the type of the result (MATERIALIZED or STREAMING) - DUCKDB_API QueryResultType GetResultType() const; + //! Whether the result's schema and properties are known. False for a statement that expands into several until + //! the one producing the result is bound, unless it failed. Runs nothing + DUCKDB_API bool MetadataAvailable() const; //! Returns the type of the statement that created this result DUCKDB_API StatementType GetStatementType() const; //! Returns the properties of the statement that created this result @@ -45,6 +56,7 @@ class BaseQueryResult { DUCKDB_API idx_t ColumnCount() const; [[noreturn]] DUCKDB_API void ThrowError(const string &prepended_message = "") const; + DUCKDB_API void ThrowIfError(const string &prepended_message = "") const; DUCKDB_API void SetError(ErrorData error); DUCKDB_API bool HasError() const; DUCKDB_API const ExceptionType &GetErrorType() const; @@ -52,9 +64,13 @@ class BaseQueryResult { DUCKDB_API ErrorData &GetErrorObject(); DUCKDB_API const ErrorData &GetErrorObject() const; +protected: + //! Creates a successful result whose metadata the statement producing it will provide + explicit BaseQueryResult(StatementType statement_type); + void SetMetadata(StatementProperties properties, vector types, vector names); + void RequireMetadata() const; + private: - //! The type of the result (MATERIALIZED or STREAMING) - QueryResultType type; //! The type of the statement that created this result StatementType statement_type; //! Properties of the statement @@ -63,22 +79,41 @@ class BaseQueryResult { vector types; //! The names of the result vector names; + bool metadata_available = true; //! Whether or not execution was successful bool success; //! The error (in case execution was not successful) ErrorData error; }; -//! The QueryResult object holds the result of a query. It can either be a MaterializedQueryResult, in which case the -//! result contains the entire result set, or a StreamQueryResult in which case the Fetch method can be called to -//! incrementally fetch data from the database. +//! A query result. The format is fixed at submission; calling Materialize, Collection, TakeCollection, Fetch or +//! RowCount will materialize the result's data into that format's collection. If instead the caller wants a +//! streaming interface, it can be moved into a QueryResultStream. +//! An accessor taking FORMAT throws InvalidInputException when FORMAT is not the result's format class QueryResult : public BaseQueryResult { + friend class BufferedData; + friend class ClientContext; + friend class ResultStreamBase; + public: - //! Creates a successful query result with the specified names and types - DUCKDB_API QueryResult(QueryResultType type, StatementType statement_type, StatementProperties properties, - vector types, vector names, ClientProperties client_properties); + //! Creates the handle of a freshly submitted query + DUCKDB_API QueryResult(shared_ptr context, PreparedStatementData &statement, + vector types, ClientProperties client_properties, + shared_ptr buffer); + //! Creates the handle of a freshly submitted statement that expands into several: its metadata and buffer come + //! from the statement that produces the result, once that is bound + QueryResult(shared_ptr context, StatementType statement_type, shared_ptr format, + ClientProperties client_properties); + //! Creates a detached result over an existing, finalized collection + DUCKDB_API QueryResult(StatementType statement_type, StatementProperties properties, vector names, + unique_ptr collection, ClientProperties client_properties); + //! Creates a detached result over an existing, finalized retained collection of any format + DUCKDB_API QueryResult(StatementType statement_type, StatementProperties properties, vector types, + vector names, unique_ptr collection, + shared_ptr format, shared_ptr format_state, + ClientProperties client_properties); //! Creates an unsuccessful query result with error condition - DUCKDB_API QueryResult(QueryResultType type, ErrorData error); + DUCKDB_API explicit QueryResult(ErrorData error); DUCKDB_API ~QueryResult() override; //! Properties from the client context @@ -86,48 +121,85 @@ class QueryResult : public BaseQueryResult { //! The next result (if any) unique_ptr next; -public: - template - TARGET &Cast() { - if (GetResultType() != TARGET::TYPE) { - throw InternalException("Failed to cast query result to type - query result type mismatch"); - } - return reinterpret_cast(*this); - } - - template - const TARGET &Cast() const { - if (GetResultType() != TARGET::TYPE) { - throw InternalException("Failed to cast query result to type - query result type mismatch"); - } - return reinterpret_cast(*this); - } - public: //! Deduplicate column names for interop with external libraries static void DeduplicateColumns(vector &names); static void DeduplicateColumns(vector &names); public: + //! Returns the query's current state. Runs no task; for a statement that expands into several, it starts the + //! next statement once the running one finished. + DUCKDB_API QueryResultState Poll(); + //! Executes a single task of the query on the calling thread. Decides nothing: READY means the engine is + //! waiting for the retention decision, and every further call returns READY, running nothing, until a + //! stream is opened or a retained-side call (Materialize, Complete, Collection, ...) is made. Unless a + //! stream drains the result, the call that reports FINISHED ends the query and collects the rows. + DUCKDB_API QueryResultState ExecuteTask(); + //! Blocks until a task is runnable or the engine is waiting on the caller. Runs no task. + DUCKDB_API void WaitForTask(); + DUCKDB_API const ResultFormat &Format() const; + //! Non-blocking. Tells the engine to fully materialize the result. Call Collection(), Fetch[Raw](), or + //! ExecuteTask() to execute tasks, or (if multithreaded) Poll until the result is complete. + DUCKDB_API void Materialize(); + //! Blocking. Tells the engine to fully materialize the result. Participates in execution of the query. + DUCKDB_API void Complete(); + template + RetainedPayloadsOf &Collection() { + PrepareCollected(FORMAT::NAME); + return collection->Cast>().Get(); + } + //! Blocking. Same as Collection() but takes ownership of the collection. The QueryResult is empty afterward. + template + unique_ptr> TakeCollection() { + PrepareCollected(FORMAT::NAME); + auto taken = collection->Cast>().Take(); + collection.reset(); + return taken; + } + //! Throws when FORMAT is not the result's format + template + const typename FORMAT::GlobalState &FormatState() const { + return CheckedFormatState(FORMAT::NAME).template Cast(); + } + //! Get the rowcount of the result. Will materialize the full result if it hadn't yet. + DUCKDB_API idx_t RowCount(); + //! Aborts the query unless a call already ended it: autocommit rolls it back, and inside a transaction a + //! statement that may write invalidates the transaction. Idempotent. + DUCKDB_API void Close(); + //! Whether this result is still the connection's open result. + DUCKDB_API bool IsOpen(); + //! Returns the name of the column for the given index DUCKDB_API const Identifier &ColumnName(idx_t index) const; - //! Fetches a DataChunk of normalized (flat) vectors from the query result. - //! Returns nullptr if there are no more results to fetch. - DUCKDB_API unique_ptr Fetch(); + //! Copies the next unit, leaving the collection intact, and materializes the result first if needed + template + unique_ptr Fetch() { + if constexpr (std::is_same::value) { + auto chunk = FetchRaw(); + if (!chunk) { + return nullptr; + } + chunk->Flatten(); + return chunk; + } else { + PrepareCollected(FORMAT::NAME); + return collection->Cast>().Fetch(); + } + } //! Fetches a DataChunk from the query result. The vectors are not normalized and hence any vector types can be - //! returned. + //! returned. Will materialize the full result into a CDC if it hadn't yet. Chunk format only DUCKDB_API unique_ptr FetchRaw(); //! Converts the QueryResult to a string - DUCKDB_API virtual string ToString() = 0; + DUCKDB_API string ToString(); //! Converts the QueryResult to a box-rendered string - DUCKDB_API virtual string ToBox(BoxRendererContext &context, const BoxRendererConfig &config); + DUCKDB_API string ToBox(BoxRendererContext &context, const BoxRendererConfig &config); //! Prints the QueryResult to the console DUCKDB_API void Print(); //! Returns true if the two results are identical; false otherwise. Note that this method is destructive; it calls //! Fetch() until both results are exhausted. The data in the results will be lost. DUCKDB_API bool Equals(QueryResult &other, bool compare_names = true); - bool TryFetch(unique_ptr &result, ErrorData &error) { + bool TryFetchOrError(unique_ptr &result, ErrorData &error) { try { result = Fetch(); return !HasError(); @@ -140,8 +212,64 @@ class QueryResult : public BaseQueryResult { } } -protected: - DUCKDB_API virtual unique_ptr FetchInternal() = 0; + //! False for a detached result and for an error result, which never had a buffer + bool HasBufferedData() const { + return buffer != nullptr; + } + //! Test helper: the buffer created for this query. + BufferedData &GetBufferedData() const { + D_ASSERT(buffer); + return *buffer; + } + +private: + unique_ptr LockContext(); + void CheckExecutableInternal(ClientContextLock &lock); + bool IsOpenInternal(ClientContextLock &lock); + //! Records that this result is no longer the connection's active query, the way an interrupt is + //! recorded, and reports it as an error state + QueryResultState Cancelled(); + void CompleteInternal(ClientContextLock &lock); + //! Ends the query as failed after an exception escaped a call that holds the context lock, and releases the + //! context + void AbandonAfterThrowInternal(ClientContextLock &lock, ErrorData error); + void SettleRetainedInternal(ClientContextLock &lock); + //! Ends a query whose retained execution finished and collects its rows + QueryResultState EndFinishedInternal(ClientContextLock &lock); + void HandleFetchFailure(ClientContextLock &lock, ErrorData error); + //! Ends the query and records a commit failure on this result without throwing + void EndQuery(ClientContextLock &lock, bool invalidate_transaction = false); + [[noreturn]] void ThrowNoCollection() const; + bool IsCollected() const { + return collection != nullptr; + } + //! A result without a buffer is in the chunk format, with a state built from its own metadata + void InitializeChunkFormatState(); + //! Takes the rows only: the handle already holds the format and state its buffer settled at submission + void AdoptCollected(QueryResult &produced); + //! Takes the metadata and buffer of the statement that produces the result of a statement that expands + void AdoptResultStatement(StatementProperties properties, PreparedStatementData &statement, + shared_ptr buffer); + //! Takes the metadata, format state and rows of a finished statement that expands into several and returns no + //! rows + void AdoptRowlessResult(QueryResult &produced); + DUCKDB_API void PrepareCollected(const char *expected); + DUCKDB_API const ResultFormatGlobalState &CheckedFormatState(const char *expected) const; + [[noreturn]] void ThrowFormatMismatch(const char *expected) const; + +private: + //! The client context this result belongs to. Null once the query has ended + shared_ptr context; + //! The buffer created for this query at submission. It carries the retention decision and, for + //! a stream, the units (null for a detached or an error result, and for a statement that expands until the one + //! producing the result has started) + shared_ptr buffer; + //! Set at construction; never null for a non-error result + shared_ptr format; + //! Never null for a non-error result whose metadata is available + shared_ptr format_state; + //! In the format's own storage, which also holds the fetch cursor + unique_ptr collection; private: class QueryResultIterator; diff --git a/src/duckdb/src/include/duckdb/main/query_result_stream.hpp b/src/duckdb/src/include/duckdb/main/query_result_stream.hpp new file mode 100644 index 000000000..20570e70a --- /dev/null +++ b/src/duckdb/src/include/duckdb/main/query_result_stream.hpp @@ -0,0 +1,122 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/main/query_result_stream.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/enums/query_result_state.hpp" +#include "duckdb/main/query_result.hpp" +#include "duckdb/main/result_format.hpp" +#include "duckdb/main/result_unit.hpp" + +#include + +namespace duckdb { + +//! Whether a reader that picks the consumption mode on its consumer's behalf streams a statement: only a SELECT that +//! returns rows does. written_type is the statement as written, since a prepared execution reports the EXECUTE that +//! runs it; the result's properties are read only for a SELECT, since a statement that expands into several has none +//! before it runs. A reader that may stop reading also needs a statement that writes nothing, since a statement +//! abandoned before its end is rolled back +DUCKDB_API bool StreamsByDefault(StatementType written_type, const QueryResult &result, bool reads_to_the_end = true); + +//! Opening a stream settles retention on draining and consumes the handle: no random access +class ResultStreamBase { +public: + //! Opens a stream on a submitted query. Throws InvalidInputException when the handle carries an + //! error, when its retention is already retained, or when the result's format is not the expected one. + //! A throw consumes the handle: it is destroyed with this object, which ends the query + DUCKDB_API ResultStreamBase(unique_ptr result, const char *expected_format); + DUCKDB_API virtual ~ResultStreamBase(); + ResultStreamBase(const ResultStreamBase &) = delete; + ResultStreamBase &operator=(const ResultStreamBase &) = delete; + +public: + //! Reports READY while a unit is poppable, else where execution stands. Runs no task. An interrupt + //! or an execution error is recorded on the stream and reported as EXECUTION_ERROR. Like ExecuteTask, it does + //! not end the query at FINISHED; reading the end of the stream does + DUCKDB_API QueryResultState Poll(); + //! Executes a single task of the query on the calling thread. An interrupt or an execution error + //! is recorded on the stream and reported as EXECUTION_ERROR + DUCKDB_API QueryResultState ExecuteTask(); + //! Blocks until a task is runnable or the engine is waiting on the caller. Runs no task + DUCKDB_API void WaitForTask(); + //! Ends the stream. Idempotent; the destructor calls it + DUCKDB_API void Close(); + //! Whether this stream is still the connection's open result + DUCKDB_API bool IsOpen(); + //! Whether the result's schema and properties are known. Runs nothing + DUCKDB_API bool MetadataAvailable() const; + + DUCKDB_API void SetError(ErrorData error); + DUCKDB_API bool HasError() const; + DUCKDB_API const string &GetError() const; + DUCKDB_API const ErrorData &GetErrorObject() const; + DUCKDB_API const ExceptionType &GetErrorType() const; + + DUCKDB_API const vector &GetTypes() const; + DUCKDB_API const vector &GetNames() const; + DUCKDB_API const Identifier &ColumnName(idx_t index) const; + DUCKDB_API idx_t ColumnCount() const; + DUCKDB_API StatementType GetStatementType() const; + DUCKDB_API const StatementProperties &GetStatementProperties() const; + DUCKDB_API const ClientProperties &GetClientProperties() const; + DUCKDB_API ClientProperties &GetClientProperties(); + + //! Test hook: the buffer this stream drains + BufferedData &GetBufferedData() { + return handle->GetBufferedData(); + } + +protected: + DUCKDB_API unique_ptr FetchUnit(); + DUCKDB_API QueryResultState TryFetchUnit(unique_ptr &out_unit); + DUCKDB_API const ResultFormatGlobalState &FormatStateInternal() const; + +private: + unique_ptr FetchUnitInternal(ClientContextLock &lock); + //! Before the statement producing the result of a statement that expands into several started, the stream + //! drives the statements before it through the query + QueryResultState DriveWithoutBufferInternal(ClientContextLock &lock, bool execute_task); + //! Runs a buffer call under the context lock and maps any failure onto the stream as + //! EXECUTION_ERROR. Once the stream has ended it keeps reporting the terminal state + QueryResultState GuardedInternal(const char *name, + const std::function &call); + +private: + //! The handle of the query this stream drains. Never handed out + unique_ptr handle; +}; + +template +class QueryResultStream : public ResultStreamBase { +public: + explicit QueryResultStream(unique_ptr result) : ResultStreamBase(std::move(result), FORMAT::NAME) { + } + +public: + //! Pops a payload when one is observable, else reports where execution stands. Runs no task: + //! payloads are produced by worker threads or by participating calls such as Fetch. After the end + //! of the stream the terminal state keeps repeating + QueryResultState TryFetch(unique_ptr &out) { + unique_ptr unit; + auto state = TryFetchUnit(unit); + out = FORMAT::UnpackUnit(std::move(unit)); + return state; + } + //! Runs tasks on the calling thread until a payload is buffered or the stream ends. Returns null + //! at the end of the stream, and on an execution error, which is recorded on the stream. After a + //! clean end it keeps returning null; after an error it throws + unique_ptr Fetch() { + return FORMAT::UnpackUnit(FetchUnit()); + } + const typename FORMAT::GlobalState &FormatState() const { + return FormatStateInternal().template Cast(); + } +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/relation.hpp b/src/duckdb/src/include/duckdb/main/relation.hpp index ad280b0f4..d96510150 100644 --- a/src/duckdb/src/include/duckdb/main/relation.hpp +++ b/src/duckdb/src/include/duckdb/main/relation.hpp @@ -83,7 +83,9 @@ class Relation : public enable_shared_from_this { DUCKDB_API virtual BoundStatement Bind(Binder &binder); DUCKDB_API virtual Identifier GetAlias(); + //! Blocking. Runs the relation to completion and returns its handle; throws on error DUCKDB_API unique_ptr ExecuteOrThrow(); + //! Blocking. Runs the relation to completion and returns its handle DUCKDB_API unique_ptr Execute(); DUCKDB_API string ToString(); DUCKDB_API virtual string ToString(idx_t depth) = 0; @@ -94,6 +96,7 @@ class Relation : public enable_shared_from_this { DUCKDB_API shared_ptr CreateView(const Identifier &name, bool replace = true, bool temporary = false); DUCKDB_API shared_ptr CreateView(const Identifier &schema_name, const Identifier &name, bool replace = true, bool temporary = false); + //! Blocking. Runs a query to completion and returns its handle; the second form exposes the relation as a view DUCKDB_API unique_ptr Query(const string &sql) const; DUCKDB_API unique_ptr Query(const Identifier &name, const string &sql); diff --git a/src/duckdb/src/include/duckdb/main/relation/table_function_relation.hpp b/src/duckdb/src/include/duckdb/main/relation/table_function_relation.hpp index efd84979d..11a7949d5 100644 --- a/src/duckdb/src/include/duckdb/main/relation/table_function_relation.hpp +++ b/src/duckdb/src/include/duckdb/main/relation/table_function_relation.hpp @@ -11,17 +11,19 @@ #include "duckdb/main/relation.hpp" namespace duckdb { +struct TableFunctionInfo; class TableFunctionRelation : public Relation { public: TableFunctionRelation(const shared_ptr &context, string name, vector parameters, named_parameter_map_t named_parameters, shared_ptr input_relation_p = nullptr, - bool auto_init = true); + bool auto_init = true, shared_ptr bind_info = nullptr); TableFunctionRelation(const shared_ptr &context, string name, vector parameters, named_parameter_map_t named_parameters, shared_ptr input_relation_p = nullptr, - bool auto_init = true); + bool auto_init = true, shared_ptr bind_info = nullptr); TableFunctionRelation(const shared_ptr &context, string name, vector parameters, - shared_ptr input_relation_p = nullptr, bool auto_init = true); + shared_ptr input_relation_p = nullptr, bool auto_init = true, + shared_ptr bind_info = nullptr); ~TableFunctionRelation() override { } @@ -30,6 +32,7 @@ class TableFunctionRelation : public Relation { named_parameter_map_t named_parameters; vector columns; shared_ptr input_relation; + shared_ptr bind_info; public: unique_ptr GetQueryNode() override; diff --git a/src/duckdb/src/include/duckdb/main/result_format.hpp b/src/duckdb/src/include/duckdb/main/result_format.hpp new file mode 100644 index 000000000..3e58a3911 --- /dev/null +++ b/src/duckdb/src/include/duckdb/main/result_format.hpp @@ -0,0 +1,158 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/main/result_format.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/enums/query_result_memory_type.hpp" +#include "duckdb/common/enums/result_ordering.hpp" +#include "duckdb/common/helper.hpp" +#include "duckdb/common/identifier.hpp" +#include "duckdb/common/shared_ptr.hpp" +#include "duckdb/common/types.hpp" +#include "duckdb/common/unique_ptr.hpp" +#include "duckdb/common/vector.hpp" +#include "duckdb/common/winapi.hpp" +#include "duckdb/main/client_properties.hpp" +#include "duckdb/main/result_unit.hpp" + +namespace duckdb { + +class BatchedDataCollection; +class ChunkRetainedCollection; +class ClientContext; +class ColumnDataCollection; +class DataChunk; +class RetainedResultCollection; + +class ResultFormatGlobalState { +public: + DUCKDB_API virtual ~ResultFormatGlobalState(); + +public: + template + TARGET &Cast() { + DynamicCastCheck(this); + return reinterpret_cast(*this); + } + + template + const TARGET &Cast() const { + DynamicCastCheck(this); + return reinterpret_cast(*this); + } +}; + +class ResultFormatLocalState { +public: + DUCKDB_API virtual ~ResultFormatLocalState(); + +public: + template + TARGET &Cast() { + DynamicCastCheck(this); + return reinterpret_cast(*this); + } +}; + +//! Captured on the client thread at submission, because a worker may not read settings while the query runs +struct ResultFormatContext { + vector types; + vector names; + ClientProperties client_properties; + ResultOrdering ordering = ResultOrdering::UNORDERED; +}; + +//! Subclasses declare NAME, GlobalState, the payload T and a static UnpackUnit, which maps null to null; +//! ResultFormatBase supplies the retained store unless the format declares C and Collection. +//! Formats are identified by NAME, never by type +//! Workers call AppendToUnit, IsUnitFinished and FinishUnit concurrently and share the format and global +//! state, so only local state is mutable +//! AppendToUnit must copy out of the chunk: the pipeline reuses it (DataChunk::Reset restores the vector +//! cache's buffers), and this is not enforced structurally +class ResultFormat { +public: + DUCKDB_API virtual ~ResultFormat(); + +public: + virtual const char *Name() const = 0; + //! Runs once, on the submitting thread, before any worker starts + virtual unique_ptr InitGlobal(const ResultFormatContext &context) = 0; + //! Runs on each producer's own thread, concurrently with other producers' calls, against the shared global state + virtual unique_ptr InitLocal(ResultFormatGlobalState &gstate) = 0; + virtual void AppendToUnit(ResultFormatGlobalState &gstate, ResultFormatLocalState &lstate, DataChunk &chunk) = 0; + //! True while a unit that reached the cap is ready to be taken + virtual bool IsUnitFinished(ResultFormatLocalState &lstate) = 0; + //! The next finished unit; with none ready, the partial unit under construction; null when empty. Never a + //! unit without rows: a stream reads only a missing unit as its end + virtual unique_ptr FinishUnit(ResultFormatGlobalState &gstate, ResultFormatLocalState &lstate) = 0; + //! Called for each producer and once for the global instance the producers are merged into + virtual unique_ptr CreateCollection(ClientContext &context, + ResultFormatGlobalState &gstate, + const ResultFormatContext &format_context) = 0; + +public: + template + bool Is() const { + return NameEquals(F::NAME); + } + + template + TARGET &Cast() { + DynamicCastCheck(this); + return reinterpret_cast(*this); + } + + template + const TARGET &Cast() const { + DynamicCastCheck(this); + return reinterpret_cast(*this); + } + +private: + DUCKDB_API bool NameEquals(const char *name) const; +}; + +class ChunkFormat : public ResultFormat { +public: + using T = DataChunk; + using C = ColumnDataCollection; + using Collection = ChunkRetainedCollection; + using GlobalState = ResultFormatGlobalState; + static constexpr const char *NAME = "chunk"; + +public: + DUCKDB_API explicit ChunkFormat(QueryResultMemoryType memory_type = QueryResultMemoryType::IN_MEMORY); + +public: + DUCKDB_API static const shared_ptr &InMemory(); + //! Rows count against memory_limit and can spill, and the result throws once the database has closed + DUCKDB_API static const shared_ptr &BufferManaged(); + + DUCKDB_API QueryResultMemoryType MemoryType() const; + +public: + DUCKDB_API const char *Name() const override; + DUCKDB_API unique_ptr InitGlobal(const ResultFormatContext &context) override; + DUCKDB_API unique_ptr InitLocal(ResultFormatGlobalState &gstate) override; + DUCKDB_API void AppendToUnit(ResultFormatGlobalState &gstate, ResultFormatLocalState &lstate, + DataChunk &chunk) override; + DUCKDB_API bool IsUnitFinished(ResultFormatLocalState &lstate) override; + DUCKDB_API unique_ptr FinishUnit(ResultFormatGlobalState &gstate, + ResultFormatLocalState &lstate) override; + DUCKDB_API unique_ptr + CreateCollection(ClientContext &context, ResultFormatGlobalState &gstate, + const ResultFormatContext &format_context) override; + +public: + DUCKDB_API static unique_ptr UnpackUnit(unique_ptr unit); + +private: + QueryResultMemoryType memory_type; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/result_unit.hpp b/src/duckdb/src/include/duckdb/main/result_unit.hpp new file mode 100644 index 000000000..28be1cf90 --- /dev/null +++ b/src/duckdb/src/include/duckdb/main/result_unit.hpp @@ -0,0 +1,60 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/main/result_unit.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/constants.hpp" +#include "duckdb/common/helper.hpp" +#include "duckdb/common/types/data_chunk.hpp" +#include "duckdb/common/unique_ptr.hpp" +#include "duckdb/common/vector.hpp" +#include "duckdb/common/winapi.hpp" + +namespace duckdb { + +//! A piece of query output: what the buffer holds and what the consumer pops. Its concrete type is +//! fixed by the format that produced it, which the consumer checks by name before casting +class ResultUnit { +public: + DUCKDB_API ResultUnit(idx_t row_count, idx_t byte_size); + DUCKDB_API virtual ~ResultUnit(); + +public: + template + TARGET &Cast() { + DynamicCastCheck(this); + return reinterpret_cast(*this); + } + + template + const TARGET &Cast() const { + DynamicCastCheck(this); + return reinterpret_cast(*this); + } + +public: + //! The rows in this unit + const idx_t row_count; + //! The bytes this unit counts against the buffer's cap + const idx_t byte_size; + +private: + ResultUnit(const ResultUnit &) = delete; + ResultUnit &operator=(const ResultUnit &) = delete; +}; + +//! A unit holding a single buffered chunk. +class ChunkUnit : public ResultUnit { +public: + DUCKDB_API explicit ChunkUnit(unique_ptr chunk); + +public: + unique_ptr chunk; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/retained_result_collection.hpp b/src/duckdb/src/include/duckdb/main/retained_result_collection.hpp new file mode 100644 index 000000000..a871355a2 --- /dev/null +++ b/src/duckdb/src/include/duckdb/main/retained_result_collection.hpp @@ -0,0 +1,246 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/main/retained_result_collection.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/algorithm.hpp" +#include "duckdb/common/enums/query_result_memory_type.hpp" +#include "duckdb/common/helper.hpp" +#include "duckdb/common/types/column/column_data_scan_states.hpp" +#include "duckdb/common/unique_ptr.hpp" +#include "duckdb/common/vector.hpp" +#include "duckdb/common/winapi.hpp" +#include "duckdb/main/result_format.hpp" +#include "duckdb/main/result_unit.hpp" + +namespace duckdb { + +//! One instance per producer, merged into one global instance under the sink's lock +class RetainedResultCollection { +public: + DUCKDB_API virtual ~RetainedResultCollection(); + +public: + virtual void Append(DataChunk &chunk, idx_t batch_index) = 0; + //! Flushes the local's partial unit, then merges it in + virtual void Combine(RetainedResultCollection &local) = 0; + //! Once, after the last Combine + virtual void Finalize() = 0; + //! Rows stored so far; a format's unfinished partial unit counts once Combine flushes it + virtual idx_t Count() const = 0; + + template + TARGET &Cast() { + DynamicCastCheck(this); + return reinterpret_cast(*this); + } + + template + const TARGET &Cast() const { + DynamicCastCheck(this); + return reinterpret_cast(*this); + } +}; + +//! The chunk format's retained deposit: a ColumnDataCollection (unordered or source ordered) or a +//! BatchedDataCollection (batch ordered), built for the given memory type +class ChunkRetainedCollection : public RetainedResultCollection { +public: + DUCKDB_API ChunkRetainedCollection(ClientContext &context, const vector &types, + QueryResultMemoryType memory_type, bool batch_ordered); + //! Adopts an already finalized collection, for a detached result built directly over one + DUCKDB_API explicit ChunkRetainedCollection(unique_ptr collection); + DUCKDB_API ~ChunkRetainedCollection() override; + +public: + DUCKDB_API void Append(DataChunk &chunk, idx_t batch_index) override; + DUCKDB_API void Combine(RetainedResultCollection &local) override; + DUCKDB_API void Finalize() override; + DUCKDB_API idx_t Count() const override; + +public: + DUCKDB_API ColumnDataCollection &Get(); + //! Hands the collection over; the instance holds nothing afterward + DUCKDB_API unique_ptr Take(); + //! The next chunk, scanned with DISALLOW_ZERO_COPY; null at the end and forever after. Never changes + //! the stored data + DUCKDB_API unique_ptr FetchRaw(); + +private: + bool batch_ordered; + unique_ptr collection; + ColumnDataAppendState append_state; + bool append_initialized = false; + unique_ptr batched; + ColumnDataScanState scan_state; + bool scan_initialized = false; + bool scan_exhausted = false; +}; + +namespace result_format_detail { + +template +using void_t_helper = void; + +//! Defaults to vector> unless the format declares its own C +template +struct PayloadsOf { + using type = vector>; +}; +template +struct PayloadsOf> { + using type = typename FORMAT::C; +}; + +} // namespace result_format_detail + +//! Only used from inside DefaultRetainedCollection, where FORMAT is always a complete type by the time it +//! is instantiated: a CRTP base like ResultFormatBase cannot resolve FORMAT::T in its own member +//! typedefs, because FORMAT is still incomplete while its base classes are being instantiated +template +using RetainedPayloadsOf = typename result_format_detail::PayloadsOf::type; + +//! The retained store of a format without its own. Units are unpacked as they are stored +template +class DefaultRetainedCollection : public RetainedResultCollection { +public: + using PayloadCollection = RetainedPayloadsOf; + + DefaultRetainedCollection(ResultFormat &format_p, ResultFormatGlobalState &gstate_p) + : format(format_p), gstate(gstate_p) { + } + +public: + void Append(DataChunk &chunk, idx_t batch_index) override { + if (has_current_batch && batch_index != current_batch) { + FlushPartial(); + } + current_batch = batch_index; + has_current_batch = true; + if (!local_state) { + // On the producer's thread, and only for an instance that receives rows: the global one never does + local_state = format.InitLocal(gstate); + } + format.AppendToUnit(gstate, *local_state, chunk); + while (format.IsUnitFinished(*local_state)) { + StoreUnit(format.FinishUnit(gstate, *local_state)); + } + } + + void Combine(RetainedResultCollection &local_p) override { + auto &local = local_p.Cast>(); + local.FlushPartial(); + for (auto &entry : local.entries) { + entries.push_back(std::move(entry)); + } + total_rows += local.total_rows; + local.entries.clear(); + local.total_rows = 0; + } + + void Finalize() override { + // The global instance never receives an append; only a local ever builds a format local state + D_ASSERT(!local_state); + std::stable_sort(entries.begin(), entries.end(), + [](const Entry &lhs, const Entry &rhs) { return lhs.batch < rhs.batch; }); + for (auto &entry : entries) { + payloads.push_back(std::move(entry.payload)); + } + entries.clear(); + finalized = true; + } + + idx_t Count() const override { + return total_rows; + } + +public: + PayloadCollection &Get() { + D_ASSERT(finalized); + return payloads; + } + unique_ptr Take() { + D_ASSERT(finalized); + return make_uniq(std::move(payloads)); + } + //! Copies the next payload through FORMAT::CopyPayload; null at the end and forever after + unique_ptr Fetch() { + D_ASSERT(finalized); + if (fetch_index >= payloads.size()) { + return nullptr; + } + return FORMAT::CopyPayload(*payloads[fetch_index++]); + } + +private: + struct Entry { + idx_t batch; + unique_ptr payload; + }; + + void FlushPartial() { + if (!local_state) { + return; + } + while (auto unit = format.FinishUnit(gstate, *local_state)) { + StoreUnit(std::move(unit)); + } + } + + void StoreUnit(unique_ptr unit) { + if (!unit) { + return; + } + total_rows += unit->row_count; + entries.push_back(Entry {current_batch, FORMAT::UnpackUnit(std::move(unit))}); + } + +private: + ResultFormat &format; + ResultFormatGlobalState &gstate; + unique_ptr local_state; + idx_t current_batch = 0; + bool has_current_batch = false; + vector entries; + PayloadCollection payloads; + idx_t total_rows = 0; + idx_t fetch_index = 0; + bool finalized = false; +}; + +namespace result_format_detail { + +//! Defaults to DefaultRetainedCollection unless the format declares its own concrete Collection +template +struct CollectionOf { + using type = DefaultRetainedCollection; +}; +template +struct CollectionOf> { + using type = typename FORMAT::Collection; +}; + +} // namespace result_format_detail + +//! The concrete RetainedResultCollection a format's retained data is stored in, reached from the +//! handle by RetainedResultCollection::Cast>() +template +using RetainedCollectionOf = typename result_format_detail::CollectionOf::type; + +//! Supplies CreateCollection for a format with no custom retained store: derive from ResultFormatBase +//! instead of ResultFormat directly to get a DefaultRetainedCollection +template +class ResultFormatBase : public ResultFormat { +public: + unique_ptr CreateCollection(ClientContext &context, ResultFormatGlobalState &gstate, + const ResultFormatContext &format_context) override { + return make_uniq>(*this, gstate); + } +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/secret/secret.hpp b/src/duckdb/src/include/duckdb/main/secret/secret.hpp index 3c1782661..6183a6e5c 100644 --- a/src/duckdb/src/include/duckdb/main/secret/secret.hpp +++ b/src/duckdb/src/include/duckdb/main/secret/secret.hpp @@ -213,7 +213,9 @@ class KeyValueSecret : public BaseSecret { // FIXME: use serialization scripts template - static unique_ptr Deserialize(Deserializer &deserializer, BaseSecret base_secret) { + static unique_ptr + Deserialize(Deserializer &deserializer, + BaseSecret base_secret) { // NOLINT(performance-unnecessary-value-param): callback signature. auto result = make_uniq(base_secret); Value secret_map_value; deserializer.ReadProperty(201, "secret_map", secret_map_value); diff --git a/src/duckdb/src/include/duckdb/main/setting_info.hpp b/src/duckdb/src/include/duckdb/main/setting_info.hpp index 8dd428598..ad0d5029d 100644 --- a/src/duckdb/src/include/duckdb/main/setting_info.hpp +++ b/src/duckdb/src/include/duckdb/main/setting_info.hpp @@ -107,14 +107,35 @@ struct ConfigurationAlias { typedef void (*set_option_callback_t)(ClientContext &context, SetScope scope, Value ¶meter); +//! Extra state and behavior of an extension option +struct ExtensionOptionInfo { + DUCKDB_API virtual ~ExtensionOptionInfo(); + + //! Called when the option is set or reset, with the scope resolved to SESSION or GLOBAL, before the value is + //! stored. May replace the value, or throw to refuse it + virtual void OnSet(ClientContext &context, SetScope scope, Value ¶meter, bool is_reset) const = 0; + + template + TARGET &Cast() { + DynamicCastCheck(this); + return reinterpret_cast(*this); + } + template + const TARGET &Cast() const { + DynamicCastCheck(this); + return reinterpret_cast(*this); + } +}; + struct ExtensionOption { ExtensionOption() : set_function(nullptr), default_scope(SetScope::AUTOMATIC) { } // NOLINTNEXTLINE: work around bug in clang-tidy ExtensionOption(string description_p, LogicalType type_p, set_option_callback_t set_function_p, - Value default_value_p, SetScope default_scope_p) + Value default_value_p, SetScope default_scope_p, bool is_debug_p, bool is_deprecated_p) : description(std::move(description_p)), type(std::move(type_p)), set_function(set_function_p), - default_value(std::move(default_value_p)), default_scope(default_scope_p) { + default_value(std::move(default_value_p)), default_scope(default_scope_p), is_debug(is_debug_p), + is_deprecated(is_deprecated_p) { } string description; @@ -123,6 +144,17 @@ struct ExtensionOption { Value default_value; SetScope default_scope; optional_idx setting_index; + bool is_debug = false; + bool is_deprecated = false; + //! Whether the option can be set globally, and per session + bool supports_global = true; + bool supports_session = true; + //! Optional extra state and behavior, invoked after set_function + shared_ptr info; + +public: + //! Resolves AUTOMATIC to the default scope, and throws when the option cannot be set at the resulting scope + DUCKDB_API SetScope ResolveScope(const Identifier &name, SetScope scope) const; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/settings.hpp b/src/duckdb/src/include/duckdb/main/settings.hpp index 8bc14e731..8e7e71d55 100644 --- a/src/duckdb/src/include/duckdb/main/settings.hpp +++ b/src/duckdb/src/include/duckdb/main/settings.hpp @@ -105,22 +105,10 @@ static constexpr idx_t SETTING_INDEX_BASE = __COUNTER__ + 1; // Start of the auto-generated list of settings structures //===----------------------------------------------------------------------===// -struct DeltaOnlyVariantEncodingEnabledSetting { - using RETURN_TYPE = bool; - static constexpr const char *Name = "__delta_only_variant_encoding_enabled"; - static constexpr const char *Description = "Enables the Parquet reader to identify a Variant structurally."; - static constexpr const char *InputType = "BOOLEAN"; - static constexpr bool IsDebug = false; - static constexpr bool IsDeprecated = false; - static void SetGlobal(DatabaseInstance *db, DBConfig &config, const Value ¶meter); - static void ResetGlobal(DatabaseInstance *db, DBConfig &config); - static Value GetSetting(const ClientContext &context); -}; - struct AccessModeSetting { using RETURN_TYPE = AccessMode; static constexpr const char *Name = "access_mode"; - static constexpr const char *Description = "Access mode of the database (AUTOMATIC, READ_ONLY or READ_WRITE)"; + static constexpr const char *Description = "Access mode of the database (AUTOMATIC, READ_ONLY or READ_WRITE)."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -133,7 +121,7 @@ struct AccessModeSetting { struct ActiveGrammarExtensionsSetting { using RETURN_TYPE = vector; static constexpr const char *Name = "active_grammar_extensions"; - static constexpr const char *Description = "The grammar extensions used by the parser"; + static constexpr const char *Description = "The grammar extensions used by the parser."; static constexpr const char *InputType = "VARCHAR[]"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -147,7 +135,7 @@ struct ActiveGrammarExtensionsSetting { struct AllocatorBackgroundThreadsSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "allocator_background_threads"; - static constexpr const char *Description = "Whether to enable the allocator background thread."; + static constexpr const char *Description = "Whether to enable the allocator background threads."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -187,7 +175,7 @@ struct AllocatorFlushThresholdSetting { struct AllowCommunityExtensionsSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "allow_community_extensions"; - static constexpr const char *Description = "Allow to load community built extensions"; + static constexpr const char *Description = "Allow loading extensions build by the community."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -204,7 +192,7 @@ struct AllowExtensionRepositoriesSetting { "Whether custom trusted extension repositories are 'allowed', 'forbidden' (which also distrusts existing " "repositories) or 'undecided' (the default: blocks adding new repositories, but keeps trusting existing ones). " "While the database is running the setting can only move from 'undecided' to 'allowed' or 'forbidden', or from " - "'allowed' to 'forbidden'"; + "'allowed' to 'forbidden'."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -217,7 +205,7 @@ struct AllowExtensionRepositoriesSetting { struct AllowExtensionsMetadataMismatchSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "allow_extensions_metadata_mismatch"; - static constexpr const char *Description = "Allow to load extensions with not compatible metadata"; + static constexpr const char *Description = "Allow to load extensions with no compatible metadata."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -229,7 +217,7 @@ struct AllowExtensionsMetadataMismatchSetting { struct AllowParserOverrideExtensionSetting { using RETURN_TYPE = AllowParserOverride; static constexpr const char *Name = "allow_parser_override_extension"; - static constexpr const char *Description = "Allow extensions to override the current parser"; + static constexpr const char *Description = "Allow extensions to override the current parser."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -243,7 +231,7 @@ struct AllowPersistentSecretsSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "allow_persistent_secrets"; static constexpr const char *Description = - "Allow the creation of persistent secrets, that are stored and loaded on restarts"; + "Allow the creation of persistent secrets that are stored and loaded on restarts."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -255,7 +243,7 @@ struct AllowPersistentSecretsSetting { struct AllowUnredactedSecretsSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "allow_unredacted_secrets"; - static constexpr const char *Description = "Allow printing unredacted secrets"; + static constexpr const char *Description = "Allow printing unredacted secrets."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -268,7 +256,7 @@ struct AllowUnredactedSecretsSetting { struct AllowUnsignedExtensionsSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "allow_unsigned_extensions"; - static constexpr const char *Description = "Allow to load extensions with invalid or missing signatures"; + static constexpr const char *Description = "Allow to load extensions with invalid or missing signatures."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -282,7 +270,7 @@ struct AllowedConfigsSetting { using RETURN_TYPE = vector; static constexpr const char *Name = "allowed_configs"; static constexpr const char *Description = - "List of configuration options that are ALWAYS allowed to be changed - even when lock_configuration is true"; + "List of configuration options that are ALWAYS allowed to be changed, even when lock_configuration is true."; static constexpr const char *InputType = "VARCHAR[]"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -294,8 +282,8 @@ struct AllowedConfigsSetting { struct AllowedDirectoriesSetting { using RETURN_TYPE = vector; static constexpr const char *Name = "allowed_directories"; - static constexpr const char *Description = "List of directories/prefixes that are ALWAYS allowed to be queried - " - "even when enable_external_access is false"; + static constexpr const char *Description = "List of directories, and directory prefixes, that are ALWAYS allowed " + "to be queried, even when enable_external_access is false."; static constexpr const char *InputType = "VARCHAR[]"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -308,7 +296,7 @@ struct AllowedPathsSetting { using RETURN_TYPE = vector; static constexpr const char *Name = "allowed_paths"; static constexpr const char *Description = - "List of files that are ALWAYS allowed to be queried - even when enable_external_access is false"; + "List of files that are ALWAYS allowed to be queried, even when enable_external_access is false."; static constexpr const char *InputType = "VARCHAR[]"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -334,7 +322,7 @@ struct ArrowLargeBufferSizeSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "arrow_large_buffer_size"; static constexpr const char *Description = - "Whether Arrow buffers for strings, blobs, uuids and bits should be exported using large buffers"; + "Whether Arrow buffers for strings, blobs, UUIDs and bits should be exported using large buffers."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -361,7 +349,7 @@ struct ArrowOutputListViewSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "arrow_output_list_view"; static constexpr const char *Description = - "Whether export to Arrow format should use ListView as the physical layout for LIST columns"; + "Whether the export to the Arrow format should use ListView as the physical layout for LIST columns."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -374,7 +362,7 @@ struct ArrowOutputVersionSetting { using RETURN_TYPE = ArrowFormatVersion; static constexpr const char *Name = "arrow_output_version"; static constexpr const char *Description = - "Whether strings should be produced by DuckDB in Utf8View format instead of Utf8"; + "Whether strings should be produced by DuckDB in Utf8View format instead of Utf8."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -388,7 +376,7 @@ struct AsofLoopJoinThresholdSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "asof_loop_join_threshold"; static constexpr const char *Description = - "The maximum number of rows we need on the left side of an ASOF join to use a nested loop join"; + "The maximum number of rows we need on the left side of an ASOF join to use a nested loop join."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -428,7 +416,8 @@ struct AutoinstallExtensionRepositorySetting { using RETURN_TYPE = string; static constexpr const char *Name = "autoinstall_extension_repository"; static constexpr const char *Description = - "Overrides the custom endpoint for extension installation on autoloading"; + "Overrides the custom endpoint for extension installation on autoloading. Falls back to " + "custom_extension_repository, if not specified."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -441,15 +430,11 @@ struct AutoinstallKnownExtensionsSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "autoinstall_known_extensions"; static constexpr const char *Description = - "Whether known extensions are allowed to be automatically installed when a query depends on them"; + "Whether known extensions are allowed to be automatically installed when a query depends on them."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; -#if defined(DUCKDB_EXTENSION_AUTOINSTALL_DEFAULT) && DUCKDB_EXTENSION_AUTOINSTALL_DEFAULT static constexpr const char *DefaultValue = "true"; -#else - static constexpr const char *DefaultValue = "false"; -#endif static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_ONLY; static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); }; @@ -458,15 +443,11 @@ struct AutoloadKnownExtensionsSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "autoload_known_extensions"; static constexpr const char *Description = - "Whether known extensions are allowed to be automatically loaded when a query depends on them"; + "Whether known extensions are allowed to be automatically loaded when a query depends on them."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; -#if defined(DUCKDB_EXTENSION_AUTOLOAD_DEFAULT) && DUCKDB_EXTENSION_AUTOLOAD_DEFAULT static constexpr const char *DefaultValue = "true"; -#else - static constexpr const char *DefaultValue = "false"; -#endif static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_ONLY; static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); }; @@ -474,8 +455,8 @@ struct AutoloadKnownExtensionsSetting { struct BlockAllocatorMemorySetting { using RETURN_TYPE = string; static constexpr const char *Name = "block_allocator_memory"; - static constexpr const char *Description = "Physical memory that the block allocator is allowed to use (this " - "memory is never freed and cannot be reduced)."; + static constexpr const char *Description = "Physical memory that the block allocator is allowed to use. This " + "memory is allocated once, never freed, and cannot be reduced."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -488,7 +469,7 @@ struct CacheLocalFilesSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "cache_local_files"; static constexpr const char *Description = - "Whether the external file cache also caches local files (remote files are always cached)"; + "Whether the external file cache also caches local files. Remote files are always cached."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -500,8 +481,8 @@ struct CacheLocalFilesSetting { struct CatalogErrorMaxSchemasSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "catalog_error_max_schemas"; - static constexpr const char *Description = - "The maximum number of schemas the system will scan for \"did you mean...\" style errors in the catalog"; + static constexpr const char *Description = "The maximum number of schemas in the catalog that the system will scan " + "to generate the \"did you mean...\" error message."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -530,7 +511,7 @@ struct CheckpointThresholdSetting { using RETURN_TYPE = string; static constexpr const char *Name = "checkpoint_threshold"; static constexpr const char *Description = - "The WAL size threshold at which to automatically trigger a checkpoint (e.g. 1GB)"; + "The WAL size threshold at which to automatically trigger a checkpoint, e.g. 1GB."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -542,21 +523,22 @@ struct CheckpointThresholdSetting { struct CurrentDialectSetting { using RETURN_TYPE = string; static constexpr const char *Name = "current_dialect"; - static constexpr const char *Description = "The SQL dialect used by the parser"; + static constexpr const char *Description = "The SQL dialect used by the parser."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; - static constexpr const char *DefaultValue = "duckdb"; - static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_ONLY; - static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); - static void OnSet(SettingCallbackInfo &info, Value &input); + static void SetLocal(ClientContext &context, const Value ¶meter); + static void ResetLocal(ClientContext &context); + static bool OnLocalSet(ClientContext &context, const Value &input); + static bool OnLocalReset(ClientContext &context); + static Value GetSetting(const ClientContext &context); }; struct CurrentTransactionInvalidationPolicySetting { using RETURN_TYPE = string; static constexpr const char *Name = "current_transaction_invalidation_policy"; static constexpr const char *Description = - "Which types of exceptions invalidate the database for the current transaction"; + "Which types of exceptions invalidate the database for the current transaction."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -569,7 +551,7 @@ struct CurrentTransactionInvalidationPolicySetting { struct CustomExtensionRepositorySetting { using RETURN_TYPE = string; static constexpr const char *Name = "custom_extension_repository"; - static constexpr const char *Description = "Overrides the custom endpoint for remote extension installation"; + static constexpr const char *Description = "Overrides the custom endpoint for extension installation."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -581,7 +563,8 @@ struct CustomExtensionRepositorySetting { struct CustomUserAgentSetting { using RETURN_TYPE = string; static constexpr const char *Name = "custom_user_agent"; - static constexpr const char *Description = "Metadata from DuckDB callers"; + static constexpr const char *Description = "HTTP request metadata that can be set by callers/wrappers to tag their " + "own traffic when DuckDB makes a request."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -590,12 +573,25 @@ struct CustomUserAgentSetting { static Value GetSetting(const ClientContext &context); }; +struct DebugAbortOnWalFailureSetting { + using RETURN_TYPE = bool; + static constexpr const char *Name = "debug_abort_on_wal_failure"; + static constexpr const char *Description = + "Whether or not to abort if a serialization exception is thrown during WAL playback."; + static constexpr const char *InputType = "BOOLEAN"; + static constexpr bool IsDebug = true; + static constexpr bool IsDeprecated = false; + static void SetGlobal(DatabaseInstance *db, DBConfig &config, const Value ¶meter); + static void ResetGlobal(DatabaseInstance *db, DBConfig &config); + static Value GetSetting(const ClientContext &context); +}; + struct DebugAsofIejoinSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "debug_asof_iejoin"; - static constexpr const char *Description = "DEBUG SETTING: force use of IEJoin to implement AsOf joins"; + static constexpr const char *Description = "Force the use of an IEJoin to implement ASOF joins."; static constexpr const char *InputType = "BOOLEAN"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "false"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::LOCAL_DEFAULT; @@ -605,10 +601,9 @@ struct DebugAsofIejoinSetting { struct DebugCheckpointAbortSetting { using RETURN_TYPE = CheckpointAbort; static constexpr const char *Name = "debug_checkpoint_abort"; - static constexpr const char *Description = - "DEBUG SETTING: trigger an abort while checkpointing for testing purposes"; + static constexpr const char *Description = "Trigger an abort while checkpointing."; static constexpr const char *InputType = "VARCHAR"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "NONE"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; @@ -632,21 +627,33 @@ struct DebugCheckpointScanSleepMsSetting { struct DebugCheckpointSleepMsSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "debug_checkpoint_sleep_ms"; - static constexpr const char *Description = "DEBUG SETTING: time to sleep before a checkpoint"; + static constexpr const char *Description = "Time to sleep before a checkpoint is invoked."; static constexpr const char *InputType = "UBIGINT"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "0"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); }; +struct DebugDeltaOnlyVariantEncodingEnabledSetting { + using RETURN_TYPE = bool; + static constexpr const char *Name = "debug_delta_only_variant_encoding_enabled"; + static constexpr const char *Description = "Enables the Parquet reader to identify a Variant structurally."; + static constexpr const char *InputType = "BOOLEAN"; + static constexpr bool IsDebug = true; + static constexpr bool IsDeprecated = false; + static void SetGlobal(DatabaseInstance *db, DBConfig &config, const Value ¶meter); + static void ResetGlobal(DatabaseInstance *db, DBConfig &config); + static Value GetSetting(const ClientContext &context); +}; + struct DebugDisableOptimizerSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "debug_disable_optimizer"; - static constexpr const char *Description = "DEBUG SETTING: disable optimizer for most queries"; + static constexpr const char *Description = "Disable running the optimizer."; static constexpr const char *InputType = "BOOLEAN"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "false"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::LOCAL_DEFAULT; @@ -656,7 +663,7 @@ struct DebugDisableOptimizerSetting { struct EnableCachingOperatorsSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "debug_enable_caching_operators"; - static constexpr const char *Description = "Enables caching operators that cache intermediate results"; + static constexpr const char *Description = "Enables caching operators that cache intermediate results."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; @@ -669,9 +676,9 @@ struct DebugEvictionQueueSleepMicroSecondsSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "debug_eviction_queue_sleep_micro_seconds"; static constexpr const char *Description = - "DEBUG SETTING: time for the eviction queue to sleep before acquiring shared ownership of block memory"; + "Set a time for the eviction queue to sleep before acquiring the shared ownership of some block memory."; static constexpr const char *InputType = "UBIGINT"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "0"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_ONLY; @@ -681,7 +688,7 @@ struct DebugEvictionQueueSleepMicroSecondsSetting { struct ForceBitpackingModeSetting { using RETURN_TYPE = BitpackingMode; static constexpr const char *Name = "debug_force_bitpacking_mode"; - static constexpr const char *Description = "DEBUG SETTING: forces a specific bitpacking mode"; + static constexpr const char *Description = "Forces a specific bitpacking mode."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; @@ -694,10 +701,10 @@ struct ForceBitpackingModeSetting { struct DebugForceCommitFailureSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "debug_force_commit_failure"; - static constexpr const char *Description = "DEBUG SETTING: force transaction commit to fail after the undo buffer " - "has been committed, used for testing commit error recovery"; + static constexpr const char *Description = "Force transaction commit to fail after the undo buffer has been " + "committed. Can be used for testing commit error recovery."; static constexpr const char *InputType = "BOOLEAN"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "false"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; @@ -707,10 +714,9 @@ struct DebugForceCommitFailureSetting { struct DebugForceCommitRevertFailureSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "debug_force_commit_revert_failure"; - static constexpr const char *Description = - "DEBUG SETTING: force RevertCommit to fail while recovering from a commit failure, used for testing"; + static constexpr const char *Description = "Force RevertCommit to fail while recovering from a commit failure."; static constexpr const char *InputType = "BOOLEAN"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "false"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; @@ -720,10 +726,9 @@ struct DebugForceCommitRevertFailureSetting { struct DebugForceExternalSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "debug_force_external"; - static constexpr const char *Description = - "DEBUG SETTING: force out-of-core computation for operators that support it, used for testing"; + static constexpr const char *Description = "Force out-of-core computation for operators that support it."; static constexpr const char *InputType = "BOOLEAN"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "false"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::LOCAL_DEFAULT; @@ -733,9 +738,9 @@ struct DebugForceExternalSetting { struct DebugForceFetchRowSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "debug_force_fetch_row"; - static constexpr const char *Description = "DEBUG SETTING: force per-row fetching during scans, used for testing"; + static constexpr const char *Description = "Force per-row fetching during scans."; static constexpr const char *InputType = "BOOLEAN"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "false"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::LOCAL_DEFAULT; @@ -745,7 +750,7 @@ struct DebugForceFetchRowSetting { struct ForceMbedtlsUnsafeSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "debug_force_mbedtls_unsafe"; - static constexpr const char *Description = "Enable mbedtls for encryption (WARNING: unsafe to use)"; + static constexpr const char *Description = "Enable mbedtls for encryption. Unsafe to use."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; @@ -758,9 +763,9 @@ struct DebugForceNoCrossProductSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "debug_force_no_cross_product"; static constexpr const char *Description = - "DEBUG SETTING: Force disable cross product generation when hyper graph isn't connected, used for testing"; + "Force disable cross product generation when the hyper graph isn't connected."; static constexpr const char *InputType = "BOOLEAN"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "false"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::LOCAL_DEFAULT; @@ -771,7 +776,7 @@ struct ForceUpdateToDelAndInsertSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "debug_force_update_to_del_and_insert"; static constexpr const char *Description = - "DEBUG SETTING: forces all updates to use the delete + insert code path instead of in-place updates"; + "Forces all updates to use the DELETE + INSERT code path instead of in-place updates."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; @@ -784,7 +789,7 @@ struct ForceVariantShredding { using RETURN_TYPE = string; static constexpr const char *Name = "debug_force_variant_shredding"; static constexpr const char *Description = - "Forces the VARIANT shredding that happens at checkpoint to use the provided schema for the shredding."; + "Forces the VARIANT shredding that happens during checkpointing to use the provided schema for the shredding."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; @@ -809,10 +814,9 @@ struct DebugForceWalFsyncFailureSetting { struct DebugLocalFileSystemDelayMsSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "debug_local_file_system_delay_ms"; - static constexpr const char *Description = - "DEBUG SETTING: time to sleep before local file system open/read/write operations"; + static constexpr const char *Description = "Time to sleep before local file system open/read/write operations."; static constexpr const char *InputType = "UBIGINT"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "0"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_ONLY; @@ -823,9 +827,9 @@ struct DebugOrderVerificationSetting { using RETURN_TYPE = DebugOrderVerification; static constexpr const char *Name = "debug_order_verification"; static constexpr const char *Description = - "DEBUG SETTING: verify ORDER BY results by rewriting the ordering (NONE, CREATE_SORT_KEY or VARIANT)"; + "Verify ORDER BY results by rewriting the ordering (NONE, CREATE_SORT_KEY or VARIANT)."; static constexpr const char *InputType = "VARCHAR"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static void SetGlobal(DatabaseInstance *db, DBConfig &config, const Value ¶meter); static void ResetGlobal(DatabaseInstance *db, DBConfig &config); @@ -835,10 +839,9 @@ struct DebugOrderVerificationSetting { struct DebugPhysicalTableScanExecutionStrategySetting { using RETURN_TYPE = PhysicalTableScanExecutionStrategy; static constexpr const char *Name = "debug_physical_table_scan_execution_strategy"; - static constexpr const char *Description = - "DEBUG SETTING: force use of given strategy for executing physical table scans"; + static constexpr const char *Description = "Force the use of a given strategy for executing physical table scans."; static constexpr const char *InputType = "VARCHAR"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "DEFAULT"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; @@ -849,33 +852,21 @@ struct DebugPhysicalTableScanExecutionStrategySetting { struct DebugSkipCheckpointOnCommitSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "debug_skip_checkpoint_on_commit"; - static constexpr const char *Description = "DEBUG SETTING: skip checkpointing on commit"; + static constexpr const char *Description = "Skip checkpointing on commit."; static constexpr const char *InputType = "BOOLEAN"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "false"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); }; -struct DebugTransformerTrampolineStyleSetting { - using RETURN_TYPE = bool; - static constexpr const char *Name = "debug_transformer_trampoline_style"; - static constexpr const char *Description = "Use the experimental trampoline-style parser transformer"; - static constexpr const char *InputType = "BOOLEAN"; - static constexpr bool IsDebug = false; - static constexpr bool IsDeprecated = false; - static constexpr const char *DefaultValue = "false"; - static constexpr SettingScopeTarget Scope = SettingScopeTarget::LOCAL_DEFAULT; - static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); -}; - struct DebugVerificationModeSetting { using RETURN_TYPE = string; static constexpr const char *Name = "debug_verification_mode"; - static constexpr const char *Description = "DEBUG SETTING: toggle the verification mode."; + static constexpr const char *Description = "Toggle the verification mode."; static constexpr const char *InputType = "VARCHAR"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static void SetGlobal(DatabaseInstance *db, DBConfig &config, const Value ¶meter); static void ResetGlobal(DatabaseInstance *db, DBConfig &config); @@ -885,10 +876,9 @@ struct DebugVerificationModeSetting { struct DebugVerificationProjectionSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "debug_verification_projection"; - static constexpr const char *Description = - "DEBUG SETTING: add internal verification projections to stress optimizers"; + static constexpr const char *Description = "Add internal verification projections to stress-test optimizers."; static constexpr const char *InputType = "BOOLEAN"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "false"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; @@ -898,9 +888,9 @@ struct DebugVerificationProjectionSetting { struct DebugVerifyAggregateStateExportSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "debug_verify_aggregate_state_export"; - static constexpr const char *Description = "DEBUG SETTING: enable verification of aggregate state export"; + static constexpr const char *Description = "Enable verification of aggregate state export."; static constexpr const char *InputType = "BOOLEAN"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "false"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; @@ -910,9 +900,9 @@ struct DebugVerifyAggregateStateExportSetting { struct DebugVerifyBlocksSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "debug_verify_blocks"; - static constexpr const char *Description = "DEBUG SETTING: verify block metadata during checkpointing"; + static constexpr const char *Description = "Verify block metadata during checkpointing."; static constexpr const char *InputType = "BOOLEAN"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "false"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; @@ -922,21 +912,48 @@ struct DebugVerifyBlocksSetting { struct DebugVerifyColumnBindingsSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "debug_verify_column_bindings"; - static constexpr const char *Description = "DEBUG SETTING: run extra internal verification of column bindings"; + static constexpr const char *Description = "Run extra internal verification of column bindings."; static constexpr const char *InputType = "BOOLEAN"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "false"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); }; +struct DebugVerifyProgressSetting { + using RETURN_TYPE = DebugProgressVerification; + static constexpr const char *Name = "debug_verify_progress"; + static constexpr const char *Description = "Verify that operators report well-formed, monotonic and complete " + "progress during execution (NONE, LOG or ERROR)."; + static constexpr const char *InputType = "VARCHAR"; + static constexpr bool IsDebug = true; + static constexpr bool IsDeprecated = false; + static constexpr const char *DefaultValue = "NONE"; + static constexpr SettingScopeTarget Scope = SettingScopeTarget::LOCAL_DEFAULT; + static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); + static void OnSet(SettingCallbackInfo &info, Value &input); +}; + +struct DebugVerifyProgressIgnoreSetting { + using RETURN_TYPE = string; + static constexpr const char *Name = "debug_verify_progress_ignore"; + static constexpr const char *Description = "Comma-separated list of progress violations to ignore, as INVARIANT or " + "INVARIANT:OPERATOR (e.g. UNSUPPORTED_SOURCE:WINDOW)."; + static constexpr const char *InputType = "VARCHAR"; + static constexpr bool IsDebug = true; + static constexpr bool IsDeprecated = false; + static constexpr const char *DefaultValue = ""; + static constexpr SettingScopeTarget Scope = SettingScopeTarget::LOCAL_DEFAULT; + static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); +}; + struct DebugVerifySerializerSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "debug_verify_serializer"; - static constexpr const char *Description = "DEBUG SETTING: verify logical plan serializer"; + static constexpr const char *Description = "Verify the logical plan serializer."; static constexpr const char *InputType = "BOOLEAN"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "false"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; @@ -946,9 +963,9 @@ struct DebugVerifySerializerSetting { struct DebugVerifyStatementSetting { using RETURN_TYPE = DebugStatementVerification; static constexpr const char *Name = "debug_verify_statement"; - static constexpr const char *Description = "DEBUG SETTING: the type of statement verification to perform"; + static constexpr const char *Description = "Specify the type of statement verification to perform."; static constexpr const char *InputType = "VARCHAR"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "NONE"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; @@ -959,10 +976,9 @@ struct DebugVerifyStatementSetting { struct DebugVerifyStatsSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "debug_verify_stats"; - static constexpr const char *Description = - "DEBUG SETTING: verify statistics are correct during execution, instead of assuming"; + static constexpr const char *Description = "Verify that statistics are correct during execution."; static constexpr const char *InputType = "BOOLEAN"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "false"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; @@ -972,9 +988,9 @@ struct DebugVerifyStatsSetting { struct DebugVerifyVectorSetting { using RETURN_TYPE = DebugVectorVerification; static constexpr const char *Name = "debug_verify_vector"; - static constexpr const char *Description = "DEBUG SETTING: enable vector verification"; + static constexpr const char *Description = "Enable a specific type of vector verification."; static constexpr const char *InputType = "VARCHAR"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "NONE"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; @@ -998,9 +1014,9 @@ struct DebugWalFsyncSleepMsSetting { struct DebugWindowModeSetting { using RETURN_TYPE = WindowAggregationMode; static constexpr const char *Name = "debug_window_mode"; - static constexpr const char *Description = "DEBUG SETTING: switch window mode to use"; + static constexpr const char *Description = "Switch the window mode being used."; static constexpr const char *InputType = "VARCHAR"; - static constexpr bool IsDebug = false; + static constexpr bool IsDebug = true; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "WINDOW"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; @@ -1011,8 +1027,8 @@ struct DebugWindowModeSetting { struct DefaultBlockSizeSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "default_block_size"; - static constexpr const char *Description = - "The default block size for new duckdb database files (new as-in, they do not yet exist)."; + static constexpr const char *Description = "The default block size for new duckdb database files. New means that " + "the file is being created and does not yet exist."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1025,7 +1041,7 @@ struct DefaultBlockSizeSetting { struct DefaultCollationSetting { using RETURN_TYPE = string; static constexpr const char *Name = "default_collation"; - static constexpr const char *Description = "The collation setting used when none is specified"; + static constexpr const char *Description = "The collation setting used when none is specified."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1038,7 +1054,8 @@ struct DefaultCollationSetting { struct DefaultNullOrderSetting { using RETURN_TYPE = DefaultOrderByNullType; static constexpr const char *Name = "default_null_order"; - static constexpr const char *Description = "NULL ordering used when none is specified (NULLS_FIRST or NULLS_LAST)"; + static constexpr const char *Description = + "The NULL ordering used when none is specified (NULLS_FIRST or NULLS_LAST)."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1051,7 +1068,7 @@ struct DefaultNullOrderSetting { struct DefaultOrderSetting { using RETURN_TYPE = OrderType; static constexpr const char *Name = "default_order"; - static constexpr const char *Description = "The order type used when none is specified (ASC or DESC)"; + static constexpr const char *Description = "The order type used when none is specified (ASC or DESC)."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1064,7 +1081,7 @@ struct DefaultOrderSetting { struct DefaultSecretStorageSetting { using RETURN_TYPE = string; static constexpr const char *Name = "default_secret_storage"; - static constexpr const char *Description = "Allows switching the default storage for secrets"; + static constexpr const char *Description = "Allows switching the default storage for secrets."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1077,8 +1094,9 @@ struct DefaultTransactionInvalidationPolicySetting { using RETURN_TYPE = TransactionInvalidationPolicy; static constexpr const char *Name = "default_transaction_invalidation_policy"; static constexpr const char *Description = - "When to invalidate transactions when errors occur (SYNTACTIC_ERRORS_DO_NOT_INVALIDATE, i.e. parser and binder " - "exceptions do not invalidate, or ALL_ERRORS_INVALIDATE_TRANSACTION)"; + "When to invalidate transactions when errors occur. SYNTACTIC_ERRORS_DO_NOT_INVALIDATE means that parser and " + "binder exceptions do not invalidate. ALL_ERRORS_INVALIDATE_TRANSACTION means all errors invalidate a " + "transaction."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1092,7 +1110,7 @@ struct DelimJoinAsCteSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "delim_join_as_cte"; static constexpr const char *Description = - "Rewrite delim joins to materialized CTEs during dependent join flattening"; + "Rewrite delim joins to materialized CTEs during dependent join flattening."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1106,7 +1124,7 @@ struct DialectCompatibilityModeSetting { using RETURN_TYPE = DialectCompatibilityMode; static constexpr const char *Name = "dialect_compatibility_mode"; static constexpr const char *Description = - "Enable SQL dialect compatibility for a certain engine (e.g. `SET dialect_compatibility_mode='spark'`)"; + "Enable SQL dialect compatibility for a certain engine, e.g. `SET dialect_compatibility_mode='spark'`."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1134,7 +1152,7 @@ struct DisableDatabaseInvalidationSetting { struct DisableTimestamptzCastsSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "disable_timestamptz_casts"; - static constexpr const char *Description = "Disable casting from timestamp to timestamptz "; + static constexpr const char *Description = "Disable casting from timestamp to timestamptz."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1146,7 +1164,7 @@ struct DisableTimestamptzCastsSetting { struct DisabledCompressionMethodsSetting { using RETURN_TYPE = string; static constexpr const char *Name = "disabled_compression_methods"; - static constexpr const char *Description = "Disable a specific set of compression methods (comma separated)"; + static constexpr const char *Description = "Disable a specific, comma-separated set of compression methods."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1158,7 +1176,8 @@ struct DisabledCompressionMethodsSetting { struct DisabledFilesystemsSetting { using RETURN_TYPE = string; static constexpr const char *Name = "disabled_filesystems"; - static constexpr const char *Description = "Disable specific file systems preventing access (e.g. LocalFileSystem)"; + static constexpr const char *Description = + "Disable specific file systems, thereby preventing their access, e.g. LocalFileSystem."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1170,7 +1189,7 @@ struct DisabledFilesystemsSetting { struct DisabledLogTypes { using RETURN_TYPE = string; static constexpr const char *Name = "disabled_log_types"; - static constexpr const char *Description = "Sets the list of disabled loggers"; + static constexpr const char *Description = "Sets the list of disabled log types."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1182,7 +1201,7 @@ struct DisabledLogTypes { struct DisabledOptimizersSetting { using RETURN_TYPE = string; static constexpr const char *Name = "disabled_optimizers"; - static constexpr const char *Description = "DEBUG SETTING: disable a specific set of optimizers (comma separated)"; + static constexpr const char *Description = "Disable a specific, comma-separated set of optimizers."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1194,7 +1213,9 @@ struct DisabledOptimizersSetting { struct DuckDBAPISetting { using RETURN_TYPE = string; static constexpr const char *Name = "duckdb_api"; - static constexpr const char *Description = "DuckDB API surface"; + static constexpr const char *Description = + "Specifies which client API/binding opened the database connection, e.g. cpp, capi, cli. The value gets folded " + "into the HTTP User-Agent string DuckDB sends on any HTTP request."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1208,7 +1229,7 @@ struct DynamicOrFilterThresholdSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "dynamic_or_filter_threshold"; static constexpr const char *Description = - "The maximum amount of OR filters we generate dynamically from a hash join"; + "The maximum amount of OR filters that are generated dynamically from a hash join."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1221,8 +1242,8 @@ struct EnableExternalAccessSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "enable_external_access"; static constexpr const char *Description = - "Allow the database to access external state (through e.g. loading/installing modules, COPY TO/FROM, CSV " - "readers, pandas replacement scans, etc)"; + "Allow the database to access external state, e.g. through loading/installing modules, COPY TO/FROM, CSV " + "readers, pandas replacement scans, etc."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1235,7 +1256,7 @@ struct EnableExternalAccessSetting { struct EnableExternalFileCacheSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "enable_external_file_cache"; - static constexpr const char *Description = "Allow the database to cache external files (e.g., Parquet) in memory."; + static constexpr const char *Description = "Allow the database to cache external files like Parquet in memory."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1249,7 +1270,7 @@ struct EnableFSSTVectorsSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "enable_fsst_vectors"; static constexpr const char *Description = - "Allow scans on FSST compressed segments to emit compressed vectors to utilize late decompression"; + "Allow scans on FSST compressed segments to emit compressed vectors to utilize late decompression."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1261,7 +1282,8 @@ struct EnableFSSTVectorsSetting { struct EnableHTTPMetadataCacheSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "enable_http_metadata_cache"; - static constexpr const char *Description = "Whether or not the global http metadata is used to cache HTTP metadata"; + static constexpr const char *Description = + "Whether or not the global http metadata is used to cache HTTP metadata."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1273,7 +1295,7 @@ struct EnableHTTPMetadataCacheSetting { struct EnableLogging { using RETURN_TYPE = bool; static constexpr const char *Name = "enable_logging"; - static constexpr const char *Description = "Enables the logger"; + static constexpr const char *Description = "Enables the logger."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1286,7 +1308,7 @@ struct EnableMacroDependenciesSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "enable_macro_dependencies"; static constexpr const char *Description = - "Enable created MACROs to create dependencies on the referenced objects (such as tables)"; + "Enables created MACROs to create dependencies on the referenced objects, such as tables."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1298,10 +1320,10 @@ struct EnableMacroDependenciesSetting { struct EnableObjectCacheSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "enable_object_cache"; - static constexpr const char *Description = "[PLACEHOLDER] Legacy setting - does nothing"; + static constexpr const char *Description = "Deprecated legacy setting, does nothing."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; - static constexpr bool IsDeprecated = false; + static constexpr bool IsDeprecated = true; static constexpr const char *DefaultValue = "false"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); @@ -1313,7 +1335,7 @@ struct EnableOptimisticWriteSetting { static constexpr const char *Name = "enable_optimistic_write"; static constexpr const char *Description = "Whether or not to optimistically write large appends to disk before committing. Disable this to keep bulk " - "appends in memory (e.g. for in-memory benchmarks)."; + "appends in memory, e.g. for in-memory benchmarks."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1325,7 +1347,7 @@ struct EnableOptimisticWriteSetting { struct EnableOptimizerSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "enable_optimizer"; - static constexpr const char *Description = "Whether or not query optimization is enabled"; + static constexpr const char *Description = "Whether the query optimizer is enabled."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1338,7 +1360,7 @@ struct EnableProfilingSetting { using RETURN_TYPE = string; static constexpr const char *Name = "enable_profiling"; static constexpr const char *Description = - "Enables profiling, and sets the output format (JSON, QUERY_TREE, QUERY_TREE_OPTIMIZER)"; + "Enables profiling and sets the output format, which can be JSON, QUERY_TREE, or QUERY_TREE_OPTIMIZER."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1351,7 +1373,7 @@ struct EnableProgressBarSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "enable_progress_bar"; static constexpr const char *Description = - "Enables the progress bar, printing progress to the terminal for long queries"; + "Enables the progress bar, which prints progress to the terminal during long queries."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1366,7 +1388,7 @@ struct EnableProgressBarPrintSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "enable_progress_bar_print"; static constexpr const char *Description = - "Controls the printing of the progress bar, when 'enable_progress_bar' is true"; + "Controls the printing of the progress bar and requires 'enable_progress_bar' to be true."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1379,7 +1401,7 @@ struct EnableViewDependenciesSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "enable_view_dependencies"; static constexpr const char *Description = - "Enable created VIEWs to create dependencies on the referenced objects (such as tables)"; + "Enable created VIEWs to create dependencies on the referenced objects, such as tables."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1391,7 +1413,7 @@ struct EnableViewDependenciesSetting { struct EnabledLogTypes { using RETURN_TYPE = string; static constexpr const char *Name = "enabled_log_types"; - static constexpr const char *Description = "Sets the list of enabled loggers"; + static constexpr const char *Description = "Sets the list of enabled log types."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1400,10 +1422,23 @@ struct EnabledLogTypes { static Value GetSetting(const ClientContext &context); }; +struct ErrorOnDivisionByZeroSetting { + using RETURN_TYPE = bool; + static constexpr const char *Name = "error_on_division_by_zero"; + static constexpr const char *Description = "Throw an error instead of returning NULL when dividing by zero."; + static constexpr const char *InputType = "BOOLEAN"; + static constexpr bool IsDebug = false; + static constexpr bool IsDeprecated = false; + static constexpr const char *DefaultValue = "true"; + static constexpr SettingScopeTarget Scope = SettingScopeTarget::LOCAL_DEFAULT; + static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); + static void OnSet(SettingCallbackInfo &info, Value &input); +}; + struct ErrorsAsJSONSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "errors_as_json"; - static constexpr const char *Description = "Output error messages as structured JSON instead of as a raw string"; + static constexpr const char *Description = "Output error messages as structured JSON instead of as a raw string."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1428,7 +1463,8 @@ struct ExperimentalMetadataReuseSetting { struct ExplainOutputSetting { using RETURN_TYPE = ExplainOutputType; static constexpr const char *Name = "explain_output"; - static constexpr const char *Description = "Output of EXPLAIN statements (ALL, OPTIMIZED_ONLY, PHYSICAL_ONLY)"; + static constexpr const char *Description = + "Configure the output of EXPLAIN statements (ALL, OPTIMIZED_ONLY, PHYSICAL_ONLY)."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1441,7 +1477,7 @@ struct ExplainOutputSetting { struct ExtensionDirectoriesSetting { using RETURN_TYPE = vector; static constexpr const char *Name = "extension_directories"; - static constexpr const char *Description = "Set the directories to store extensions in"; + static constexpr const char *Description = "Set the directories to store extensions in."; static constexpr const char *InputType = "VARCHAR[]"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1453,7 +1489,7 @@ struct ExtensionDirectoriesSetting { struct ExtensionDirectorySetting { using RETURN_TYPE = string; static constexpr const char *Name = "extension_directory"; - static constexpr const char *Description = "DEPRECATED: Use extension_directories instead"; + static constexpr const char *Description = "Deprecated setting. Use extension_directories instead"; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = true; @@ -1469,7 +1505,7 @@ struct ExtensionRepositoryDirectorySetting { static constexpr const char *Description = "Set the directory in which trusted extension repositories are stored. This is the trust anchor for " "user-provided repositories, so while signature checking is enabled (allow_unsigned_extensions=false) it can " - "only be set at startup, not while the database is running"; + "only be set at startup, not while the database is running."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1479,30 +1515,62 @@ struct ExtensionRepositoryDirectorySetting { static void OnSet(SettingCallbackInfo &info, Value &input); }; -struct ExternalFileCacheLocalBlockSizeSetting { +struct ExternalFileCacheLocalMaxBlockSizeSetting { using RETURN_TYPE = idx_t; - static constexpr const char *Name = "external_file_cache_local_block_size"; + static constexpr const char *Name = "external_file_cache_local_max_block_size"; static constexpr const char *Description = - "Block size in bytes for the external file cache when reading local (non-remote) files."; + "Maximum size in bytes of an external file cache block for local (non-remote) files. Larger reads are split " + "into blocks of this size, which are fetched in parallel."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "16384"; - static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; + static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_ONLY; static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); static void OnSet(SettingCallbackInfo &info, Value &input); }; -struct ExternalFileCacheRemoteBlockSizeSetting { +struct ExternalFileCacheRemoteMaxBlockSizeSetting { using RETURN_TYPE = idx_t; - static constexpr const char *Name = "external_file_cache_remote_block_size"; + static constexpr const char *Name = "external_file_cache_remote_max_block_size"; static constexpr const char *Description = - "Block size in bytes for the external file cache when reading remote files (e.g. HTTP/S3)."; + "Maximum size in bytes of an external file cache block for remote files (e.g. HTTP/S3). Larger reads are split " + "into blocks of this size, which are fetched in parallel."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; static constexpr const char *DefaultValue = "2097152"; - static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; + static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_ONLY; + static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); + static void OnSet(SettingCallbackInfo &info, Value &input); +}; + +struct ExternalFileCacheRemoteMinBlockSizeSetting { + using RETURN_TYPE = idx_t; + static constexpr const char *Name = "external_file_cache_remote_min_block_size"; + static constexpr const char *Description = "Reads of remote files are rounded out to aligned blocks of this size, " + "so tiny reads do not each cost a request."; + static constexpr const char *InputType = "UBIGINT"; + static constexpr bool IsDebug = false; + static constexpr bool IsDeprecated = false; + static constexpr const char *DefaultValue = "4096"; + static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_ONLY; + static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); + static void OnSet(SettingCallbackInfo &info, Value &input); +}; + +struct ExternalFileCacheRequestSizingSetting { + using RETURN_TYPE = ExternalFileCacheRequestSizing; + static constexpr const char *Name = "external_file_cache_request_sizing"; + static constexpr const char *Description = + "How reads of cached files are sized: AUTO lets each reader choose, GRID reads the aligned blocks of the cache " + "block size around each read, EXACT reads exactly the requested bytes. Reads of files that are not cached are " + "always exact."; + static constexpr const char *InputType = "VARCHAR"; + static constexpr bool IsDebug = false; + static constexpr bool IsDeprecated = false; + static constexpr const char *DefaultValue = "AUTO"; + static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_ONLY; static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); static void OnSet(SettingCallbackInfo &info, Value &input); }; @@ -1512,7 +1580,7 @@ struct ExternalFileCacheSpillSetting { static constexpr const char *Name = "external_file_cache_spill"; static constexpr const char *Description = "Whether evicted external file cache blocks of remote files spill to the temporary directory instead of being " - "dropped, so that they are re-read from there rather than re-fetched from the source"; + "dropped, so that they are re-read from there rather than re-fetched from the source."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1537,7 +1605,7 @@ struct ExternalThreadsSetting { struct FileSearchPathSetting { using RETURN_TYPE = string; static constexpr const char *Name = "file_search_path"; - static constexpr const char *Description = "A comma separated list of directories to search for input files"; + static constexpr const char *Description = "A comma separated list of directories to search for input files."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1564,7 +1632,7 @@ struct ForceColumnMetadataReuseSetting { struct ForceCompressionSetting { using RETURN_TYPE = CompressionType; static constexpr const char *Name = "force_compression"; - static constexpr const char *Description = "DEBUG SETTING: forces a specific compression method to be used"; + static constexpr const char *Description = "Forces the usage of a specific compression method."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1574,35 +1642,38 @@ struct ForceCompressionSetting { static void OnSet(SettingCallbackInfo &info, Value &input); }; -struct GeometryMinimumShreddingSize { - using RETURN_TYPE = int64_t; - static constexpr const char *Name = "geometry_minimum_shredding_size"; - static constexpr const char *Description = "Minimum size of a rowgroup to enable GEOMETRY shredding, or set to -1 " - "to disable entirely. Defaults to 1/4th of a rowgroup"; - static constexpr const char *InputType = "BIGINT"; +struct FsyncModeSetting { + using RETURN_TYPE = FileSyncMode; + static constexpr const char *Name = "fsync_mode"; + static constexpr const char *Description = + "How to synchronize database file changes to stable storage (STANDARD, NONE or FULL). NONE may cause database " + "corruption after an operating system or hardware crash. FULL uses fullfsync on macOS."; + static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; - static constexpr const char *DefaultValue = "30000"; + static constexpr const char *DefaultValue = "STANDARD"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_ONLY; static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); + static void OnSet(SettingCallbackInfo &info, Value &input); }; -struct HeapBasedParserSetting { - using RETURN_TYPE = bool; - static constexpr const char *Name = "heap_based_parser"; - static constexpr const char *Description = "Use the heap-based PEG parser"; - static constexpr const char *InputType = "BOOLEAN"; +struct GeometryMinimumShreddingSize { + using RETURN_TYPE = int64_t; + static constexpr const char *Name = "geometry_minimum_shredding_size"; + static constexpr const char *Description = "Minimum size of a row group to enable GEOMETRY shredding. Set to -1 to " + "disable shredding entirely. Defaults to 1/4th of a row group."; + static constexpr const char *InputType = "BIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; - static constexpr const char *DefaultValue = "true"; - static constexpr SettingScopeTarget Scope = SettingScopeTarget::LOCAL_DEFAULT; + static constexpr const char *DefaultValue = "30000"; + static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_ONLY; static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); }; struct HomeDirectorySetting { using RETURN_TYPE = string; static constexpr const char *Name = "home_directory"; - static constexpr const char *Description = "Sets the home directory used by the system"; + static constexpr const char *Description = "Sets the home directory of the database instance."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1612,11 +1683,23 @@ struct HomeDirectorySetting { static void OnSet(SettingCallbackInfo &info, Value &input); }; +struct HTTPClientPoolCapacitySetting { + using RETURN_TYPE = int64_t; + static constexpr const char *Name = "http_client_pool_capacity"; + static constexpr const char *Description = + "HTTP client pool capacity derived from CPU and async thread counts unless explicitly set"; + static constexpr const char *InputType = "BIGINT"; + static constexpr bool IsDebug = false; + static constexpr bool IsDeprecated = false; + static void SetGlobal(DatabaseInstance *db, DBConfig &config, const Value ¶meter); + static void ResetGlobal(DatabaseInstance *db, DBConfig &config); + static Value GetSetting(const ClientContext &context); +}; + struct HTTPProxySetting { using RETURN_TYPE = string; static constexpr const char *Name = "http_proxy"; - static constexpr const char *Description = - "HTTP proxy host (defaults to the HTTP_PROXY environment variable when unset)"; + static constexpr const char *Description = "The HTTP proxy host. Defaults to the HTTP_PROXY environment variable."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1628,7 +1711,7 @@ struct HTTPProxySetting { struct HTTPProxyPasswordSetting { using RETURN_TYPE = string; static constexpr const char *Name = "http_proxy_password"; - static constexpr const char *Description = "Password for HTTP proxy"; + static constexpr const char *Description = "The password for the HTTP proxy."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1640,7 +1723,7 @@ struct HTTPProxyPasswordSetting { struct HTTPProxyUsernameSetting { using RETURN_TYPE = string; static constexpr const char *Name = "http_proxy_username"; - static constexpr const char *Description = "Username for HTTP proxy"; + static constexpr const char *Description = "The username for the HTTP proxy."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1679,7 +1762,7 @@ struct ImmediateTransactionModeSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "immediate_transaction_mode"; static constexpr const char *Description = - "Whether transactions should be started lazily when needed, or immediately when BEGIN TRANSACTION is called"; + "Whether transactions should be started lazily when needed, or immediately when BEGIN TRANSACTION is called."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1737,7 +1820,7 @@ struct IntegerDivisionSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "integer_division"; static constexpr const char *Description = - "Whether or not the / operator defaults to integer division, or to floating point division"; + "Whether or not the / operator defaults to integer division, or to floating point division."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1750,10 +1833,10 @@ struct LambdaSyntaxSetting { using RETURN_TYPE = LambdaSyntax; static constexpr const char *Name = "lambda_syntax"; static constexpr const char *Description = - "Configures the use of the deprecated single arrow operator (->) for lambda functions."; + "Deprecated setting. Configures the use of the deprecated single arrow operator (->) for lambda functions."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; - static constexpr bool IsDeprecated = false; + static constexpr bool IsDeprecated = true; static constexpr const char *DefaultValue = "DEFAULT"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::LOCAL_DEFAULT; static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); @@ -1764,7 +1847,7 @@ struct LateMaterializationMaxRowsSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "late_materialization_max_rows"; static constexpr const char *Description = - "The maximum amount of rows in the LIMIT/SAMPLE for which we trigger late materialization"; + "The maximum amount of rows in the LIMIT/SAMPLE operator for which we trigger late materialization."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1776,8 +1859,8 @@ struct LateMaterializationMaxRowsSetting { struct LegacyDisableNullTypeSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "legacy_disable_null_type"; - static constexpr const char *Description = - "When enabled, prevent the NULL type from leaving the binder (< v2.0 default behavior)"; + static constexpr const char *Description = "When enabled, prevent the NULL type from leaving the binder. This was " + "the default behavior prior to DuckDB v2.0."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1790,11 +1873,11 @@ struct LegacyDisableNullTypeSetting { struct LegacyMetricsFormatSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "legacy_metrics_format"; - static constexpr const char *Description = - "When enabled, profiling output uses the legacy flat format instead of the current grouped format"; + static constexpr const char *Description = "Deprecated setting. When enabled, profiling output uses the legacy " + "flat format instead of the current grouped format."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; - static constexpr bool IsDeprecated = false; + static constexpr bool IsDeprecated = true; static constexpr const char *DefaultValue = "false"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::LOCAL_DEFAULT; static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); @@ -1804,7 +1887,7 @@ struct LegacyMetricsFormatSetting { struct LockConfigurationSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "lock_configuration"; - static constexpr const char *Description = "Whether or not configurations can be altered"; + static constexpr const char *Description = "Whether configurations can be altered."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1817,7 +1900,7 @@ struct LogQueryPathSetting { using RETURN_TYPE = string; static constexpr const char *Name = "log_query_path"; static constexpr const char *Description = - "Specifies the path to which queries should be logged (default: NULL, queries are not logged)"; + "Specifies the path to which queries should be logged. The default is NULL, meaning queries are not logged."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1830,7 +1913,7 @@ struct LogQueryPathSetting { struct LoggingLevel { using RETURN_TYPE = string; static constexpr const char *Name = "logging_level"; - static constexpr const char *Description = "The log level which will be recorded in the log"; + static constexpr const char *Description = "Determines which 'severity' of log messages will be logged."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1842,7 +1925,7 @@ struct LoggingLevel { struct LoggingMode { using RETURN_TYPE = string; static constexpr const char *Name = "logging_mode"; - static constexpr const char *Description = "Determines which types of log messages are logged"; + static constexpr const char *Description = "Determines which types of log messages are logged."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1854,7 +1937,8 @@ struct LoggingMode { struct LoggingStorage { using RETURN_TYPE = string; static constexpr const char *Name = "logging_storage"; - static constexpr const char *Description = "Set the logging storage (memory/stdout/file/)"; + static constexpr const char *Description = + "Set the logging storage, options are 'memory', 'stdout', 'file', ''."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1881,7 +1965,7 @@ struct MatchRecognizeMaxStatesSetting { struct MaxExecutionTimeSetting { using RETURN_TYPE = int64_t; static constexpr const char *Name = "max_execution_time"; - static constexpr const char *Description = "The maximum execution time per query in milliseconds (0 = no limit)"; + static constexpr const char *Description = "The maximum execution time per query in milliseconds (0 = no limit)."; static constexpr const char *InputType = "BIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1907,7 +1991,7 @@ struct MaxExpressionDepthSetting { struct MaxMemorySetting { using RETURN_TYPE = string; static constexpr const char *Name = "max_memory"; - static constexpr const char *Description = "The maximum memory of the system (e.g. 1GB)"; + static constexpr const char *Description = "The maximum memory of the system (e.g. 1GB)."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1920,8 +2004,9 @@ struct MaxStreamingBufferSizeSetting { using RETURN_TYPE = string; static constexpr const char *Name = "max_streaming_buffer_size"; static constexpr const char *Description = - "The maximum number of bytes a streaming query result buffers (e.g. 1GB). Buffered bytes stay under this cap " - "plus at most one chunk: an oversized chunk is only admitted into an empty queue"; + "The maximum number of bytes a streaming query result buffers (e.g. 1GB). Queued output stays under this cap, " + "and each engine thread may hold, beyond the queue, the units it is still building or handing over from its " + "current chunk."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1934,7 +2019,7 @@ struct MaxTempDirectorySizeSetting { using RETURN_TYPE = string; static constexpr const char *Name = "max_temp_directory_size"; static constexpr const char *Description = - "The maximum amount of data stored inside the 'temp_directory' (when set) (e.g. 1GB)"; + "The maximum amount of data stored inside the 'temp_directory' (when set) (e.g. 1GB)."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1946,7 +2031,7 @@ struct MaxTempDirectorySizeSetting { struct MaxVacuumTasksSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "max_vacuum_tasks"; - static constexpr const char *Description = "The maximum vacuum tasks to schedule during a checkpoint."; + static constexpr const char *Description = "The maximum number of vacuum tasks to schedule during a checkpoint."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1958,7 +2043,7 @@ struct MaxVacuumTasksSetting { struct MergeJoinThresholdSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "merge_join_threshold"; - static constexpr const char *Description = "The maximum number of rows on either table to choose a merge join"; + static constexpr const char *Description = "The maximum number of rows on either table to choose a merge join."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1971,7 +2056,7 @@ struct NestedLoopJoinThresholdSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "nested_loop_join_threshold"; static constexpr const char *Description = - "The maximum number of rows on either table to choose a nested loop join"; + "The maximum number of rows on either table to choose a nested loop join."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -1980,24 +2065,11 @@ struct NestedLoopJoinThresholdSetting { static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); }; -struct NullOnDivisionByZeroSetting { - using RETURN_TYPE = bool; - static constexpr const char *Name = "null_on_division_by_zero"; - static constexpr const char *Description = "Return NULL instead of throwing an error when dividing by zero."; - static constexpr const char *InputType = "BOOLEAN"; - static constexpr bool IsDebug = false; - static constexpr bool IsDeprecated = false; - static constexpr const char *DefaultValue = "false"; - static constexpr SettingScopeTarget Scope = SettingScopeTarget::LOCAL_DEFAULT; - static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); - static void OnSet(SettingCallbackInfo &info, Value &input); -}; - struct OldImplicitCastingSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "old_implicit_casting"; static constexpr const char *Description = - "DEPRECATED: Compatibility setting for the pre-0.10 implicit VARCHAR casting behavior; will be removed"; + "Deprecated setting. Used for compatibility for the pre-0.10 implicit VARCHAR casting behavior."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = true; @@ -2011,7 +2083,7 @@ struct OperatorMemoryLimitSetting { using RETURN_TYPE = string; static constexpr const char *Name = "operator_memory_limit"; static constexpr const char *Description = - "The maximum memory for query intermediates (sorts, hash tables) per connection (e.g. 256MB)"; + "The maximum memory available for query intermediates (sorts, hash tables) per connection (e.g. 256MB)."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2024,7 +2096,8 @@ struct OrderByNonIntegerLiteralSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "order_by_non_integer_literal"; static constexpr const char *Description = - "Allow ordering by non-integer literals - ordering by such literals has no effect."; + "Allow ordering by non-integer literals. Ordering by such literals has no effect. Enabling this relaxes the " + "check to a silent no-op instead of a binder error."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2036,7 +2109,7 @@ struct OrderByNonIntegerLiteralSetting { struct OrderedAggregateThresholdSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "ordered_aggregate_threshold"; - static constexpr const char *Description = "The number of rows to accumulate before sorting, used for tuning"; + static constexpr const char *Description = "The number of rows to accumulate before sorting."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2050,7 +2123,7 @@ struct PartitionedWriteFlushThresholdSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "partitioned_write_flush_threshold"; static constexpr const char *Description = - "The threshold in number of rows after which we flush a thread state when writing using PARTITION_BY"; + "The number of rows threshold after which a thread state is flushed when writing using PARTITION_BY."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2063,7 +2136,7 @@ struct PartitionedWriteMaxOpenFilesSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "partitioned_write_max_open_files"; static constexpr const char *Description = - "The maximum amount of files the system can keep open before flushing to disk when writing using PARTITION_BY"; + "The maximum amount of files the system can keep open before flushing to disk when writing using PARTITION_BY."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2075,7 +2148,7 @@ struct PartitionedWriteMaxOpenFilesSetting { struct PasswordSetting { using RETURN_TYPE = string; static constexpr const char *Name = "password"; - static constexpr const char *Description = "The password to use. Ignored for legacy compatibility."; + static constexpr const char *Description = "The password to use. Kept for legacy compatibility."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2087,7 +2160,7 @@ struct PasswordSetting { struct PerfectHtThresholdSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "perfect_ht_threshold"; - static constexpr const char *Description = "Threshold in bytes for when to use a perfect hash table"; + static constexpr const char *Description = "Threshold in bytes for when to use a perfect hash table."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2100,8 +2173,8 @@ struct PerfectHtThresholdSetting { struct PinThreadsSetting { using RETURN_TYPE = ThreadPinMode; static constexpr const char *Name = "pin_threads"; - static constexpr const char *Description = - "Whether to pin threads to cores (Linux only, default AUTO: on when there are more than 64 cores)"; + static constexpr const char *Description = "Whether to pin threads to cores. Linux only, and the default is AUTO. " + "AUTO means the setting is on when there are more than 64 cores."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2115,7 +2188,7 @@ struct PivotFilterThresholdSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "pivot_filter_threshold"; static constexpr const char *Description = - "The threshold to switch from using filtered aggregates to LIST with a dedicated pivot operator"; + "The threshold determining when a dedicated pivot operator switches from using filtered aggregates to LIST."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2127,7 +2200,7 @@ struct PivotFilterThresholdSetting { struct PivotLimitSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "pivot_limit"; - static constexpr const char *Description = "The maximum number of pivot columns in a pivot statement"; + static constexpr const char *Description = "The maximum number of pivot columns in a pivot statement."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2139,7 +2212,7 @@ struct PivotLimitSetting { struct PreferRangeJoinsSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "prefer_range_joins"; - static constexpr const char *Description = "Force use of range joins with mixed predicates"; + static constexpr const char *Description = "Force the use of range joins with mixed predicates."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2151,8 +2224,8 @@ struct PreferRangeJoinsSetting { struct PreserveIdentifierCaseSetting { using RETURN_TYPE = IdentifierCaseMode; static constexpr const char *Name = "preserve_identifier_case"; - static constexpr const char *Description = "How to fold non-quoted identifiers: 'preserve_case' keeps the case as " - "written, 'lowercase' lowercases them, 'uppercase' uppercases them"; + static constexpr const char *Description = "How to fold non-quoted identifiers. 'preserve_case' keeps the case as " + "written, 'lowercase' lowercases them, 'uppercase' uppercases them."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2166,8 +2239,8 @@ struct PreserveInsertionOrderSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "preserve_insertion_order"; static constexpr const char *Description = - "Whether or not to preserve insertion order. If set to false the system is allowed to re-order any results " - "that do not contain ORDER BY clauses."; + "Whether or not to preserve insertion order. If set to false, then the system is allowed to re-order any " + "results that do not contain ORDER BY clauses."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2179,7 +2252,7 @@ struct PreserveInsertionOrderSetting { struct ProduceArrowStringViewSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "produce_arrow_string_view"; - static constexpr const char *Description = "DEPRECATED: Already controlled by enable_arrow_string_view"; + static constexpr const char *Description = "Deprecated setting. Controlled via enable_arrow_string_view."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = true; @@ -2192,7 +2265,8 @@ struct ProduceArrowStringViewSetting { struct ProfilingCoverageSetting { using RETURN_TYPE = string; static constexpr const char *Name = "profiling_coverage"; - static constexpr const char *Description = "The profiling coverage (SELECT or ALL)"; + static constexpr const char *Description = + "The profiling coverage, either SELECT (only SELECT queries) or ALL (all queries are profiled)."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2204,7 +2278,8 @@ struct ProfilingCoverageSetting { struct ProfilingModeSetting { using RETURN_TYPE = string; static constexpr const char *Name = "profiling_mode"; - static constexpr const char *Description = "DEPRECATED: Use enable_profiling and profiling_coverage instead"; + static constexpr const char *Description = + "Deprecated setting. Use enable_profiling and profiling_coverage instead."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = true; @@ -2217,7 +2292,7 @@ struct ProfilingOutputSetting { using RETURN_TYPE = string; static constexpr const char *Name = "profiling_output"; static constexpr const char *Description = - "The file to which profile output should be saved, or empty to print to the terminal"; + "The file to which profiler output is written. When empty, the output is printed to the terminal."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2230,8 +2305,8 @@ struct ProfilingRendererSettingsSetting { using RETURN_TYPE = unordered_map; static constexpr const char *Name = "profiling_renderer_settings"; static constexpr const char *Description = - "A map of settings passed to the renderer of the profiler output (e.g. {'max_extra_lines': 100}) - settings " - "not recognized by the active renderer are ignored"; + "A map of settings passed to the renderer of the profiler output, e.g. {'max_extra_lines': 100}. Settings not " + "recognized by the active renderer are ignored."; static constexpr const char *InputType = "MAP(VARCHAR, VARCHAR)"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2244,7 +2319,7 @@ struct ProgressBarTimeSetting { using RETURN_TYPE = int64_t; static constexpr const char *Name = "progress_bar_time"; static constexpr const char *Description = - "Sets the time (in milliseconds) how long a query needs to take before we start printing a progress bar"; + "Configures how long (in milliseconds) a query needs to take before we start printing a progress bar."; static constexpr const char *InputType = "BIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2256,8 +2331,8 @@ struct ProgressBarTimeSetting { struct ReadAheadDepthSetting { using RETURN_TYPE = int64_t; static constexpr const char *Name = "read_ahead_depth"; - static constexpr const char *Description = "Number of scan jobs prefetched ahead of decoding. -1 = automatic " - "(backlog bounded by a memory budget), 0 = disabled."; + static constexpr const char *Description = "The number of scan jobs prefetched ahead of decoding. -1 = automatic, " + "with the backlog being bound by a memory budget, 0 = disabled."; static constexpr const char *InputType = "BIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2267,11 +2342,24 @@ struct ReadAheadDepthSetting { static void OnSet(SettingCallbackInfo &info, Value &input); }; +struct RedactHttpLogsSetting { + using RETURN_TYPE = bool; + static constexpr const char *Name = "redact_http_logs"; + static constexpr const char *Description = + "Redact HTTP header values in logs unless their names are explicitly allowed."; + static constexpr const char *InputType = "BOOLEAN"; + static constexpr bool IsDebug = false; + static constexpr bool IsDeprecated = false; + static constexpr const char *DefaultValue = "true"; + static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_ONLY; + static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); +}; + struct RegexMatchOperatorSemanticsSetting { using RETURN_TYPE = RegexMatchOperatorSemantics; static constexpr const char *Name = "regex_match_operator_semantics"; static constexpr const char *Description = - "Configures whether regex match operators use partial or full string matching"; + "Configures whether regex match operators use partial or full string matching."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2284,8 +2372,9 @@ struct RegexMatchOperatorSemanticsSetting { struct ScalarSubqueryErrorOnMultipleRowsSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "scalar_subquery_error_on_multiple_rows"; - static constexpr const char *Description = "Throw an error when a scalar subquery returns more than one row. When " - "disabled, an arbitrary row is returned instead."; + static constexpr const char *Description = + "Deprecated setting. Throw an error when a scalar subquery returns more than one row. This will become the " + "default behavior. When disabled, an arbitrary row is returned instead."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2297,8 +2386,8 @@ struct ScalarSubqueryErrorOnMultipleRowsSetting { struct SchedulerProcessPartialSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "scheduler_process_partial"; - static constexpr const char *Description = - "Partially process tasks before rescheduling - allows for more scheduler fairness between separate queries"; + static constexpr const char *Description = "Partially process tasks before rescheduling. Enabling this allows for " + "more scheduler fairness between separate queries."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2328,7 +2417,7 @@ struct SearchPathSetting { using RETURN_TYPE = string; static constexpr const char *Name = "search_path"; static constexpr const char *Description = - "Sets the default catalog search path as a comma-separated list of values"; + "Sets the default catalog search path as a comma-separated list of values."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2340,7 +2429,7 @@ struct SearchPathSetting { struct SecretDirectorySetting { using RETURN_TYPE = string; static constexpr const char *Name = "secret_directory"; - static constexpr const char *Description = "Set the directory to which persistent secrets are stored"; + static constexpr const char *Description = "Set the directory in which persistent secrets are stored."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2353,8 +2442,8 @@ struct ShowBehaviorSetting { using RETURN_TYPE = ShowBehaviorType; static constexpr const char *Name = "show_behavior"; static constexpr const char *Description = - "How SHOW resolves a bare identifier: 'auto' (describe a table if one exists, else a setting; deprecated), " - "'table' (always a table), or 'setting' (always a setting)"; + "Configures how SHOW resolves a bare identifier. 'auto' describes a table if one exists, else it describes a " + "setting (deprecated). 'table' always describes a table, and 'setting' always describes a setting."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2367,7 +2456,7 @@ struct ShowBehaviorSetting { struct StandardVectorSizeSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "standard_vector_size"; - static constexpr const char *Description = "The compiled-in STANDARD_VECTOR_SIZE (read-only)"; + static constexpr const char *Description = "The compiled-in STANDARD_VECTOR_SIZE (read-only)."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2379,7 +2468,7 @@ struct StandardVectorSizeSetting { struct StorageBlockPrefetchSetting { using RETURN_TYPE = StorageBlockPrefetch; static constexpr const char *Name = "storage_block_prefetch"; - static constexpr const char *Description = "In which scenarios to use storage block prefetching"; + static constexpr const char *Description = "Configures the scenario in which to use storage block prefetching."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2392,7 +2481,9 @@ struct StorageBlockPrefetchSetting { struct StorageCompatibilityVersionSetting { using RETURN_TYPE = string; static constexpr const char *Name = "storage_compatibility_version"; - static constexpr const char *Description = "Serialize on checkpoint with compatibility for a given duckdb version"; + static constexpr const char *Description = + "Sets the DuckDB version for which data is serialized to the DuckDB file during checkpointing. Can differ from " + "the version of the DuckDB instance."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2418,7 +2509,7 @@ struct TableFunctionIdentifierConversionSetting { struct TempDirectorySetting { using RETURN_TYPE = string; static constexpr const char *Name = "temp_directory"; - static constexpr const char *Description = "Set the directory to which to write temp files"; + static constexpr const char *Description = "Set the directory to which temporary files are written."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2430,7 +2521,7 @@ struct TempDirectorySetting { struct TempFileEncryptionSetting { using RETURN_TYPE = bool; static constexpr const char *Name = "temp_file_encryption"; - static constexpr const char *Description = "Encrypt all temporary files if database is encrypted"; + static constexpr const char *Description = "Encrypt all temporary files, if the database is encrypted."; static constexpr const char *InputType = "BOOLEAN"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2443,7 +2534,7 @@ struct TempFileEncryptionSetting { struct ThreadsSetting { using RETURN_TYPE = int64_t; static constexpr const char *Name = "threads"; - static constexpr const char *Description = "The number of total threads used by the system."; + static constexpr const char *Description = "The number of total background threads used by the system."; static constexpr const char *InputType = "BIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2456,7 +2547,7 @@ struct TrackedMetricsSetting { using RETURN_TYPE = vector; static constexpr const char *Name = "tracked_metrics"; static constexpr const char *Description = - "A list of metric glob patterns to enable for collection (e.g. ['query.*', 'optimizer.*'])"; + "A list of metric glob patterns to enable for collection, e.g. ['query.*', 'optimizer.*']."; static constexpr const char *InputType = "VARCHAR[]"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2468,7 +2559,7 @@ struct TrackedMetricsSetting { struct UsernameSetting { using RETURN_TYPE = string; static constexpr const char *Name = "username"; - static constexpr const char *Description = "The username to use. Ignored for legacy compatibility."; + static constexpr const char *Description = "The username to use. Kept for legacy compatibility."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2481,12 +2572,12 @@ struct VacuumRebuildIndexesSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "vacuum_rebuild_indexes"; static constexpr const char *Description = - "(Experimental) Allow vacuum to compact row groups on tables with bound ART indexes, rebuilding the indexes " - "afterward. Tables with a row count exceeding this threshold are skipped. 0 = disabled. Can also be set " - "per-database via the 'vacuum_rebuild_indexes' ATTACH option, which overrides this default."; + "Deprecated compatibility setting: maximum table row count for rebuilding bound ART indexes when checkpoint " + "vacuum cannot remap their row IDs. 0 disables the rebuild fallback but does not disable index remapping on " + "v2.0 storage. The 'vacuum_rebuild_indexes' ATTACH option overrides this default per database."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; - static constexpr bool IsDeprecated = false; + static constexpr bool IsDeprecated = true; static constexpr const char *DefaultValue = "0"; static constexpr SettingScopeTarget Scope = SettingScopeTarget::GLOBAL_DEFAULT; static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); @@ -2497,8 +2588,8 @@ struct ValidateExternalFileCacheSetting { using RETURN_TYPE = CacheValidationMode; static constexpr const char *Name = "validate_external_file_cache"; static constexpr const char *Description = - "Cache validation mode: VALIDATE_ALL (default, validate all cache entries), VALIDATE_REMOTE (validate only " - "remote cache entries), or NO_VALIDATION (disable cache validation)."; + "The cache validation mode. VALIDATE_ALL is the default, it validates all cache entries., VALIDATE_REMOTE only " + "validates remote cache entries. NO_VALIDATION disables cache validation."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2511,8 +2602,9 @@ struct ValidateExternalFileCacheSetting { struct VariantMinimumShreddingSizeSetting { using RETURN_TYPE = int64_t; static constexpr const char *Name = "variant_minimum_shredding_size"; - static constexpr const char *Description = "Minimum size of a rowgroup to enable VARIANT shredding, or set to -1 " - "to disable entirely. Defaults to 1/4th of a rowgroup"; + static constexpr const char *Description = + "Minimum size of a row group to have VARIANT shredding enabled. Set to -1 to disable shredding entirely. " + "Defaults to 1/4th of the row group size."; static constexpr const char *InputType = "BIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2525,7 +2617,7 @@ struct WalAutocheckpointEntriesSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "wal_autocheckpoint_entries"; static constexpr const char *Description = - "Trigger automatic checkpoint when WAL entry count reaches or exceeds N (0 = disabled)"; + "Trigger automatic checkpoint when WAL entry count reaches or exceeds N (0 = disabled)."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2566,7 +2658,7 @@ struct WriteBufferRowGroupMemoryLimitSetting { static constexpr const char *Description = "The maximum data to buffer in row groups (in bytes) to buffer prior to flushing them together. When either " "this limit is reached, or write_buffer_row_group_count is reached, we flush the data to disk. Defaults to 20% " - "of memory limit divided by thread count."; + "of the memory limit divided by the thread count."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -2579,7 +2671,7 @@ struct ZstdMinStringLengthSetting { using RETURN_TYPE = idx_t; static constexpr const char *Name = "zstd_min_string_length"; static constexpr const char *Description = - "The (average) length at which to enable ZSTD compression, defaults to 4096"; + "The (average) minimum length at which to enable ZSTD compression, defaults to 4096."; static constexpr const char *InputType = "UBIGINT"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; diff --git a/src/duckdb/src/include/duckdb/main/statement_iterator.hpp b/src/duckdb/src/include/duckdb/main/statement_iterator.hpp index 9e587a873..a7a8bc886 100644 --- a/src/duckdb/src/include/duckdb/main/statement_iterator.hpp +++ b/src/duckdb/src/include/duckdb/main/statement_iterator.hpp @@ -8,27 +8,21 @@ #pragma once -#include "duckdb/common/optional_ptr.hpp" #include "duckdb/common/profiler.hpp" +#include "duckdb/common/types/timestamp.hpp" #include "duckdb/common/unique_ptr.hpp" -#include "duckdb/common/vector.hpp" #include "duckdb/main/parse_iterator.hpp" namespace duckdb { class ClientContext; -class ClientContextLock; class SQLStatement; -//! Iterator over the engine-facing statements of a query. +//! Iterator over the user statements of a script, as parsed. The submission of each statement expands it. //! //! Usage: //! StatementIterator it(ParseIterator(context, sql)); //! while (it.Peek()) { //! auto stmt = it.GetStatement(); -//! if (!stmt) { -//! continue; // a peel that preprocessing swallowed (empty expansion) -//! } -//! // ready-to-execute statement //! } //! class StatementIterator { @@ -44,45 +38,33 @@ class StatementIterator { // Not move-assignable: holds a ClientContext reference, which cannot be rebound. StatementIterator &operator=(StatementIterator &&) = delete; - //! Returns true while more input remains (a buffered engine statement, or another parse-facing - //! statement to pull). Parses ahead as needed but does NOT preprocess — safe as a lookahead. + //! Returns true while another statement remains. Parses ahead, so it throws a parser error. DUCKDB_API bool Peek(); //! Grammar-free predicate: is there another statement after the current one? Never parses and //! never throws (see ParseIterator::HasMore), so it is safe to consult before the current - //! statement has executed — e.g. to decide whether the current statement is the last. + //! statement has executed, for example to decide whether the current statement is the last. DUCKDB_API bool HasMore(); - //! Pull + preprocess the next engine-facing statement. Returns nullptr when a peel preprocesses - //! to nothing (skip with `continue`) or when the input is exhausted (Peek would return false). - //! Self-locking variant for callers that do not hold the context lock. + //! Pull the next statement. Returns nullptr once the input is exhausted (Peek would return false). DUCKDB_API unique_ptr GetStatement(); - //! Same, for callers that already hold the context lock. - DUCKDB_API unique_ptr GetStatementWithLock(ClientContextLock &lock); //! Pull the next statement and attach its parser timing to the per-statement query profiler. DUCKDB_API unique_ptr GetStatementForExecution(); - //! Same, for callers that already hold the context lock. - DUCKDB_API unique_ptr GetStatementForExecutionWithLock(ClientContextLock &lock); private: - //! Shared body for both Get variants. `lock` is null for the self-locking path (preprocessing - //! then acquires the lock itself) or the held lock for callers that already have it. - unique_ptr GetStatementInternal(optional_ptr lock, bool profile_statement); + unique_ptr GetStatementInternal(bool profile_statement); private: - //! The single parse-facing source this iterator preprocesses. Always constructed — the - //! single-statement ctor forwards to ParseIterator's single-statement ctor. + //! The single parse-facing source this iterator pulls from. ParseIterator source; - //! The bound context, inherited from `source`. Used for preprocessing / transaction state / locking. + //! The bound context, inherited from `source`, whose profiler receives the parser timing. ClientContext &context; - //! The parse-facing statement buffered by Peek, waiting to be preprocessed. + //! The statement parsed ahead by Peek. unique_ptr pending_statement; - //! Parser timing for the currently buffered parse-facing statement. + //! Parser timing for the statement parsed ahead. Profiler parser_timer; - //! Engine-facing statements produced by preprocessing one parse-facing peel. Drained - //! one-at-a-time across GetStatement calls before pulling + preprocessing the next peel. - vector> buffer; - idx_t buffer_cursor = 0; + timestamp_t parser_start_time = timestamp_t::epoch(); + timestamp_t parser_end_time = timestamp_t::epoch(); }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/stream_query_result.hpp b/src/duckdb/src/include/duckdb/main/stream_query_result.hpp deleted file mode 100644 index d8e10185c..000000000 --- a/src/duckdb/src/include/duckdb/main/stream_query_result.hpp +++ /dev/null @@ -1,86 +0,0 @@ -//===----------------------------------------------------------------------===// -// DuckDB -// -// duckdb/main/stream_query_result.hpp -// -// -//===----------------------------------------------------------------------===// - -#pragma once - -#include "duckdb/common/winapi.hpp" -#include "duckdb/main/query_result.hpp" -#include "duckdb/parallel/interrupt.hpp" -#include "duckdb/common/queue.hpp" -#include "duckdb/common/enums/stream_execution_result.hpp" -#include "duckdb/main/buffered_data/simple_buffered_data.hpp" - -namespace duckdb { - -class ClientContext; -class ClientContextLock; -class Executor; -class MaterializedQueryResult; -class PreparedStatementData; - -class StreamQueryResult : public QueryResult { - friend class ClientContext; - -public: - static constexpr const QueryResultType TYPE = QueryResultType::STREAM_RESULT; - -public: - //! Create a successful StreamQueryResult. StreamQueryResults should always be successful initially (it makes no - //! sense to stream an error). - DUCKDB_API StreamQueryResult(StatementType statement_type, StatementProperties properties, - vector types, vector names, - ClientProperties client_properties, shared_ptr buffered_data); - DUCKDB_API explicit StreamQueryResult(ErrorData error); - DUCKDB_API ~StreamQueryResult() override; - -public: - static bool IsChunkReady(StreamExecutionResult result); - //! Reschedules the tasks that work on producing a result chunk, returning when at least one task can be executed - DUCKDB_API void WaitForTask(); - //! Executes a single task within the final pipeline, returning whether or not a chunk is ready to be fetched - DUCKDB_API StreamExecutionResult ExecuteTask(); - //! Converts the QueryResult to a string - DUCKDB_API string ToString() override; - //! Materializes the query result and turns it into a materialized query result - DUCKDB_API unique_ptr Materialize(); - - DUCKDB_API bool IsOpen(); - - //! The buffer backing this stream result - BufferedData &GetBufferedData() { - D_ASSERT(buffered_data); - return *buffered_data; - } - //! False for an error result, which never had a buffer - bool HasBufferedData() const { - return buffered_data != nullptr; - } - - //! Closes the StreamQueryResult - DUCKDB_API void Close(); - - //! The client context this StreamQueryResult belongs to - shared_ptr context; - -protected: - DUCKDB_API unique_ptr FetchInternal() override; - -private: - StreamExecutionResult ExecuteTaskInternal(ClientContextLock &lock); - unique_ptr FetchNextInternal(ClientContextLock &lock); - //! The materialize of a stream a fetch already chose: the remainder is copied out under the cap - unique_ptr MaterializeByDraining(); - unique_ptr LockContext(); - void CheckExecutableInternal(ClientContextLock &lock); - bool IsOpenInternal(ClientContextLock &lock); - -private: - shared_ptr buffered_data; -}; - -} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/optimizer/aggregate_function_rewriter.hpp b/src/duckdb/src/include/duckdb/optimizer/aggregate_function_rewriter.hpp index 6cb34ff77..0acb74669 100644 --- a/src/duckdb/src/include/duckdb/optimizer/aggregate_function_rewriter.hpp +++ b/src/duckdb/src/include/duckdb/optimizer/aggregate_function_rewriter.hpp @@ -13,7 +13,6 @@ namespace duckdb { class Optimizer; -class AggregateRewriteRule; //! Rewrites aggregate functions. Currently registered rules: //! AVG(x) -> SUM(x) / COUNT(x) @@ -27,7 +26,6 @@ class AggregateFunctionRewriter { private: Optimizer &optimizer; - vector> rules; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/optimizer/column_binding_replacer.hpp b/src/duckdb/src/include/duckdb/optimizer/column_binding_replacer.hpp index 8c4ba6116..47277c4c4 100644 --- a/src/duckdb/src/include/duckdb/optimizer/column_binding_replacer.hpp +++ b/src/duckdb/src/include/duckdb/optimizer/column_binding_replacer.hpp @@ -10,6 +10,8 @@ #include "duckdb/planner/logical_operator.hpp" +#include + namespace duckdb { class BoundColumnRefExpression; @@ -61,7 +63,7 @@ class ColumnBindingReplacer : public LogicalOperatorVisitor { ColumnBindingReplacer(); public: - //! Update each operator of the plan + //! Update each operator of the plan, recursing directly into children without resolving projection maps void VisitOperator(LogicalOperator &op) override; //! Update bindings owned by this operator without visiting its children virtual void VisitOperatorBindings(LogicalOperator &op); @@ -92,6 +94,12 @@ class CorrelatedColumnBindingReplacer : public ColumnBindingReplacer { //! Applies binding replacements together with their projection-layout invariants. class ColumnBindingRewrite { public: + //! Rewrite a child, preserving the parent's explicit and implicit projection selections and updating its bindings. + //! The callback may replace or wrap the child and must record its binding replacements in the supplied graph. + //! Callers must still rewrite affected ancestors and resolve operator types after the complete transformation. + static void + RewriteChild(unique_ptr &op, idx_t child_index, + const std::function &, BindingReplacementGraph &)> &rewrite); //! Apply the output-boundary view of a complete replacement graph to one parent-child edge. static void ApplyToChild(unique_ptr &op, idx_t child_index, vector old_child_bindings, const BindingReplacementGraph &replacements); diff --git a/src/duckdb/src/include/duckdb/optimizer/compressed_materialization.hpp b/src/duckdb/src/include/duckdb/optimizer/compressed_materialization.hpp index 44242c165..b9d04cb0d 100644 --- a/src/duckdb/src/include/duckdb/optimizer/compressed_materialization.hpp +++ b/src/duckdb/src/include/duckdb/optimizer/compressed_materialization.hpp @@ -16,6 +16,7 @@ namespace duckdb { class Optimizer; class ClientContext; +class ColumnBindingReplacer; class LogicalOperator; class Expression; class BoundColumnRefExpression; @@ -104,7 +105,9 @@ class CompressedMaterialization { static constexpr double JOIN_CARDINALITY_RATIO_THRESHOLD = 8; public: - CompressedMaterialization(Optimizer &optimizer, LogicalOperator &root, statistics_map_t &statistics_map); + CompressedMaterialization(Optimizer &optimizer, LogicalOperator &root, + const vector>> &ancestors, + statistics_map_t &statistics_map); void Compress(unique_ptr &op); @@ -137,6 +140,8 @@ class CompressedMaterialization { vector> compress_exprs, CompressedMaterializationInfo &info, CMChildInfo &child_info); void CreateDecompressProjection(unique_ptr &op, CompressedMaterializationInfo &info); + //! Rebind references to compressed bindings, which can only occur in the ancestors of the compressed operator + void RebindAncestors(ColumnBindingReplacer &replacer); unique_ptr CreateRestoreExpression(unique_ptr input, const CMBindingInfo &binding_info, const BaseStatistics &stats); @@ -172,6 +177,8 @@ class CompressedMaterialization { ClientContext &context; //! The root of the query plan optional_ptr root; + //! The chain of operators from the root down to the operator being compressed + const vector>> &ancestors; //! The map of ColumnBinding -> statistics for the various nodes statistics_map_t &statistics_map; }; diff --git a/src/duckdb/src/include/duckdb/optimizer/cte_filter_pusher.hpp b/src/duckdb/src/include/duckdb/optimizer/cte_filter_pusher.hpp index 4923dd5da..aeb2c7311 100644 --- a/src/duckdb/src/include/duckdb/optimizer/cte_filter_pusher.hpp +++ b/src/duckdb/src/include/duckdb/optimizer/cte_filter_pusher.hpp @@ -13,13 +13,16 @@ namespace duckdb { class LogicalOperator; +class LogicalCTERef; class Optimizer; class CTEFilterPusher { public: explicit CTEFilterPusher(Optimizer &optimizer); - //! Finds all materialized CTEs and pushes OR filters into them (if applicable) + //! Push consumer predicates and eligible join-key restrictions into materialized CTEs. unique_ptr Optimize(unique_ptr op); + //! Discard planner guarantees before transformations that do not preserve consumer identity. + static void ClearDependencies(LogicalOperator &op); private: //! CTE info needed for creating OR filters that can be pushed down @@ -27,12 +30,16 @@ class CTEFilterPusher { explicit MaterializedCTEInfo(LogicalOperator &materialized_cte); LogicalOperator &materialized_cte; vector> filters; + vector> references; bool all_cte_refs_are_filtered; + bool has_filter_dependency; }; private: //! Find all materialized CTEs and their refs void FindCandidates(LogicalOperator &op); + bool CanPushFilter(const MaterializedCTEInfo &info); + bool HasValidDependency(const MaterializedCTEInfo &info); //! Creates an OR filter and pushes it into a materialized CTE void PushFilterIntoCTE(MaterializedCTEInfo &info); diff --git a/src/duckdb/src/include/duckdb/optimizer/cte_join_filter_collector.hpp b/src/duckdb/src/include/duckdb/optimizer/cte_join_filter_collector.hpp new file mode 100644 index 000000000..288893119 --- /dev/null +++ b/src/duckdb/src/include/duckdb/optimizer/cte_join_filter_collector.hpp @@ -0,0 +1,46 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/optimizer/cte_join_filter_collector.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/enums/expression_type.hpp" +#include "duckdb/common/unordered_map.hpp" +#include "duckdb/planner/column_binding.hpp" + +namespace duckdb { + +class LogicalOperator; +class LogicalCTERef; + +struct CTEJoinFilter { + TableIndex source; + TableIndex target; + TableIndex target_scan; + vector source_columns; + vector target_columns; + vector comparisons; +}; + +class CTEJoinFilterCollector { +public: + static vector Collect(LogicalOperator &op, const unordered_map &targets); + +private: + explicit CTEJoinFilterCollector(const unordered_map &targets); + void VisitOperator(LogicalOperator &op); + void PushFilter(LogicalOperator &op, const LogicalCTERef &source, const vector &source_keys, + vector target_keys, const vector &comparisons); + void AddFilter(const LogicalCTERef &source, const LogicalCTERef &target, const vector &source_keys, + const vector &target_keys, const vector &comparisons); + +private: + const unordered_map &join_targets; + vector join_filters; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/optimizer/cte_join_filter_pusher.hpp b/src/duckdb/src/include/duckdb/optimizer/cte_join_filter_pusher.hpp new file mode 100644 index 000000000..5a8046561 --- /dev/null +++ b/src/duckdb/src/include/duckdb/optimizer/cte_join_filter_pusher.hpp @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/optimizer/cte_join_filter_pusher.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/insertion_order_preserving_map.hpp" +#include "duckdb/planner/column_binding.hpp" + +namespace duckdb { + +class LogicalOperator; +class LogicalCTERef; +class Optimizer; + +class CTEJoinFilterPusher { +public: + explicit CTEJoinFilterPusher(Optimizer &optimizer); + void Optimize(LogicalOperator &op); + +private: + struct MaterializedCTEInfo { + explicit MaterializedCTEInfo(LogicalOperator &cte) : materialized_cte(cte) { + } + LogicalOperator &materialized_cte; + vector> references; + vector ancestors; + }; + + void FindCandidates(LogicalOperator &op); + bool HasValidDependency(const MaterializedCTEInfo &info); + +private: + Optimizer &optimizer; + InsertionOrderPreservingMap> cte_info_map; + vector available_ctes; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/optimizer/filter_combiner.hpp b/src/duckdb/src/include/duckdb/optimizer/filter_combiner.hpp index 2b2761825..ebe038154 100644 --- a/src/duckdb/src/include/duckdb/optimizer/filter_combiner.hpp +++ b/src/duckdb/src/include/duckdb/optimizer/filter_combiner.hpp @@ -44,6 +44,8 @@ class FilterCombiner { }; FilterResult AddFilter(unique_ptr expr); + //! Splits on AND and adds each conjunct + FilterResult AddConjuncts(unique_ptr expr); //! Returns whether or not a set of integral values is a dense range (i.e. 1, 2, 3, 4, 5) //! If this returns true - this sorts "in_list" as a side-effect diff --git a/src/duckdb/src/include/duckdb/optimizer/join_order/join_order_optimizer.hpp b/src/duckdb/src/include/duckdb/optimizer/join_order/join_order_optimizer.hpp index 0647b333d..25407233e 100644 --- a/src/duckdb/src/include/duckdb/optimizer/join_order/join_order_optimizer.hpp +++ b/src/duckdb/src/include/duckdb/optimizer/join_order/join_order_optimizer.hpp @@ -21,6 +21,12 @@ class JoinOrderOptimizer { explicit JoinOrderOptimizer(ClientContext &context); public: + //! Deepest nesting of child optimizers (one per non-reorderable operator, projection, aggregate, ...) that is still + //! join-order optimized. Every level recurses through Optimize/Build/ExtractJoinRelations, so a very deep plan + //! (e.g. a long chain of EXCEPT/INTERSECT) would otherwise overflow the stack long before the plan depth limit is + //! hit; subplans below this depth keep their original join order, which is always a valid plan. + static constexpr idx_t MAX_RECURSION_DEPTH = 128; + JoinOrderOptimizer CreateChildOptimizer(); //! Perform join reordering inside a plan unique_ptr Optimize(unique_ptr plan, optional_ptr stats = nullptr); diff --git a/src/duckdb/src/include/duckdb/optimizer/join_order/join_predicate.hpp b/src/duckdb/src/include/duckdb/optimizer/join_order/join_predicate.hpp index 7052f7cc2..fea23a458 100644 --- a/src/duckdb/src/include/duckdb/optimizer/join_order/join_predicate.hpp +++ b/src/duckdb/src/include/duckdb/optimizer/join_order/join_predicate.hpp @@ -59,6 +59,8 @@ class JoinPredicate { bool IsEquivalencePredicate() const; void SetEqualityClassIndex(optional_idx equality_class_index); optional_idx GetEqualityClassIndex() const; + void SetCompositeJoinPair(JoinRelationSet &pair); + optional_ptr GetCompositeJoinPair() const; private: idx_t index; @@ -68,6 +70,7 @@ class JoinPredicate { ColumnBinding left_equality_binding; ColumnBinding right_equality_binding; optional_idx equality_class_index; + optional_ptr composite_join_pair; }; struct JoinEqualityPredicateEdge { @@ -90,7 +93,22 @@ struct JoinEqualityClass { vector edges; }; +class JoinEqualityGroups { +public: + void AddEquality(const ColumnBinding &left, const ColumnBinding &right); + idx_t GetGroup(const ColumnBinding &binding); + +private: + idx_t GetOrCreateGroup(const ColumnBinding &binding); + idx_t FindRoot(idx_t group); + +private: + column_binding_map_t binding_to_group; + vector parents; +}; + struct RelationPairEqualitySummary { + void AddEquality(idx_t equality_class_index, ColumnBinding first_binding, ColumnBinding second_binding); bool HasDirectCompositeEquality() const; vector direct_equality_class_indices; @@ -116,9 +134,6 @@ class JoinPredicateModel { bool HasDirectCompositeEquality(JoinRelationSet &pair) const; -private: - static bool ContainsClassIndex(const vector &class_indices, idx_t equality_class_index); - private: vector> predicates; vector> all_predicates; diff --git a/src/duckdb/src/include/duckdb/optimizer/join_order/join_relation_set.hpp b/src/duckdb/src/include/duckdb/optimizer/join_order/join_relation_set.hpp index c138e02f3..60462dfbf 100644 --- a/src/duckdb/src/include/duckdb/optimizer/join_order/join_relation_set.hpp +++ b/src/duckdb/src/include/duckdb/optimizer/join_order/join_relation_set.hpp @@ -29,6 +29,8 @@ struct JoinRelationSet { public: unsafe_unique_array relations; idx_t count; + //! Bitmask over the relation indexes for fast subset/intersection checks + vector bitmask; }; //! The JoinRelationTree is a structure holding all the created JoinRelationSet objects and allowing fast lookup on to diff --git a/src/duckdb/src/include/duckdb/optimizer/join_order/query_graph_manager.hpp b/src/duckdb/src/include/duckdb/optimizer/join_order/query_graph_manager.hpp index 3ae992bd6..55129f156 100644 --- a/src/duckdb/src/include/duckdb/optimizer/join_order/query_graph_manager.hpp +++ b/src/duckdb/src/include/duckdb/optimizer/join_order/query_graph_manager.hpp @@ -69,6 +69,7 @@ class QueryGraphManager { //! Build the normalized predicate model after filter endpoints and stats bindings are populated. void BuildPredicateModel(); + void BuildLeftEqualityPairs(); void BuildInnerCompanionSets(); void ClearExtractedExpressions(); idx_t FindGraphComponent(RelationIndex relation); diff --git a/src/duckdb/src/include/duckdb/optimizer/join_order/relation_index.hpp b/src/duckdb/src/include/duckdb/optimizer/join_order/relation_index.hpp index 12fabe524..ed978d675 100644 --- a/src/duckdb/src/include/duckdb/optimizer/join_order/relation_index.hpp +++ b/src/duckdb/src/include/duckdb/optimizer/join_order/relation_index.hpp @@ -9,37 +9,11 @@ #pragma once #include "duckdb/common/common.hpp" +#include "duckdb/common/typed_index.hpp" namespace duckdb { -struct RelationIndex { - RelationIndex() : index(DConstants::INVALID_INDEX) { - } - explicit RelationIndex(idx_t index) : index(index) { - } - - idx_t index; - - bool operator==(const RelationIndex &rhs) const { - return index == rhs.index; - } - bool operator<(const RelationIndex &rhs) const { - return index < rhs.index; - } - bool operator!=(const RelationIndex &other) const { - return !(*this == other); - } - bool operator>(const RelationIndex &other) const { - return other < *this; - } - bool operator<=(const RelationIndex &other) const { - return !(other < *this); - } - bool operator>=(const RelationIndex &other) const { - return !(*this < other); - } - bool IsValid() const { - return index != DConstants::INVALID_INDEX; - } +struct RelationIndex : public TypedIndex { + using TypedIndex::TypedIndex; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/optimizer/relation_statistics/relation_statistics.hpp b/src/duckdb/src/include/duckdb/optimizer/relation_statistics/relation_statistics.hpp index 334f3cde2..c7171f095 100644 --- a/src/duckdb/src/include/duckdb/optimizer/relation_statistics/relation_statistics.hpp +++ b/src/duckdb/src/include/duckdb/optimizer/relation_statistics/relation_statistics.hpp @@ -12,6 +12,7 @@ #include "duckdb/common/optional_ptr.hpp" #include "duckdb/common/types.hpp" #include "duckdb/planner/column_binding.hpp" +#include "duckdb/planner/column_binding_map.hpp" namespace duckdb { @@ -54,4 +55,18 @@ struct RelationStats { Identifier table_name; }; +//! Looks up column statistics by binding, the first relation and column with a binding wins +class RelationColumnStatsLookup { +public: + RelationColumnStatsLookup() = default; + explicit RelationColumnStatsLookup(const RelationStats &stats); + +public: + void AddRelation(const RelationStats &stats); + optional_ptr Find(ColumnBinding binding) const; + +private: + column_binding_map_t> columns; +}; + } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/optimizer/remote_pushdown_optimizer.hpp b/src/duckdb/src/include/duckdb/optimizer/remote_pushdown_optimizer.hpp index 6a70d9a9c..772681d60 100644 --- a/src/duckdb/src/include/duckdb/optimizer/remote_pushdown_optimizer.hpp +++ b/src/duckdb/src/include/duckdb/optimizer/remote_pushdown_optimizer.hpp @@ -90,6 +90,17 @@ class RemotePushdownOptimizer { void Rewrite(unique_ptr &statement); + //! Drop the given catalog qualifier from every name in the tree. A two-part name (e.g. "db1.t") parses as + //! schema.name, so the catalog can sit in either slot + static void StripCatalogName(SQLStatement &statement, const Identifier &catalog_name); + static void StripCatalogName(QueryNode &node, const Identifier &catalog_name); + static void StripCatalogName(TableRef &ref, const Identifier &catalog_name); + static void StripCatalogName(CreateInfo &info, const Identifier &catalog_name); + static void StripCatalogName(AlterInfo &info, const Identifier &catalog_name); + //! Strip catalog prefix from expression column refs. When strip_subquery_bodies=false, leaves subquery + //! bodies untouched (used for partial pushdown where inner subqueries are not being pushed). + static void StripCatalogName(ParsedExpression &expr, const Identifier &catalog_name); + private: void FindRemoteCatalogsInSearchPath(); CatalogPushdownResult Rewrite(QueryNode &node); @@ -183,14 +194,6 @@ class RemotePushdownOptimizer { static CatalogPushdownResult Merge(CatalogPushdownResult a, CatalogPushdownResult b); unique_ptr CreateRemoteFunctionRef(CatalogPushdownResult &result, unique_ptr node); - static void StripCatalogName(SQLStatement &statement, const Identifier &catalog_name); - static void StripCatalogName(QueryNode &node, const Identifier &catalog_name); - static void StripCatalogName(TableRef &ref, const Identifier &catalog_name); - static void StripCatalogName(CreateInfo &info, const Identifier &catalog_name); - static void StripCatalogName(AlterInfo &info, const Identifier &catalog_name); - //! Strip catalog prefix from expression column refs. When strip_subquery_bodies=false, leaves subquery - //! bodies untouched (used for partial pushdown where inner subqueries are not being pushed). - static void StripCatalogName(ParsedExpression &expr, const Identifier &catalog_name); bool RefersToLocalTable(const ColumnRefExpression &col_ref) const; bool RefersToCTE(const Identifier &cte_name, CatalogPushdownResult &result) const; diff --git a/src/duckdb/src/include/duckdb/optimizer/remove_unused_columns.hpp b/src/duckdb/src/include/duckdb/optimizer/remove_unused_columns.hpp index 82073c32f..82a6d54d4 100644 --- a/src/duckdb/src/include/duckdb/optimizer/remove_unused_columns.hpp +++ b/src/duckdb/src/include/duckdb/optimizer/remove_unused_columns.hpp @@ -24,6 +24,7 @@ class BoundColumnRefExpression; class ClientContext; class LogicalColumnDataGet; class LogicalRecursiveCTE; +class LogicalSecureView; class Optimizer; struct ReferencedExtractComponent { @@ -128,6 +129,8 @@ class BaseColumnPruner : public LogicalOperatorVisitor { private: //! The current mode of the pruner, enables/disables certain behaviors BaseColumnPrunerMode mode = BaseColumnPrunerMode::DEFAULT; + //! True while visiting the child of OPERATOR_TRY. Do not push throwing CASTs (#25236). + bool inside_try = false; }; //! The RemoveUnusedColumns optimizer traverses the logical operator tree and removes any columns that are not required @@ -151,12 +154,23 @@ class RemoveUnusedColumns : public BaseColumnPruner { RemoveUnusedColumns &root; unique_ptr> root_cte_map; column_binding_map_t> projection_map_replacements; + struct PrunedViewColumn { + ColumnBinding binding; + //! Expression over the previous binding; nullptr for a rename. + unique_ptr expression; + }; + vector> active_secure_views; + column_binding_map_t> secure_view_replacements; private: template void ClearUnusedExpressions(vector &list, TableIndex table_idx, bool replace = true); + void RecordSecureViewReplacement(ColumnBinding old_binding, ColumnBinding new_binding, + unique_ptr expression = nullptr); + void ApplySecureViewReplacements(); void RemoveColumnsFromLogicalColumnDataGet(LogicalColumnDataGet &get); void RemoveColumnsFromLogicalGet(LogicalGet &get, unique_ptr &op_ref); + void VisitSecureView(LogicalSecureView &view); void CheckPushdownExtract(LogicalOperator &op); void RewriteExpressions(LogicalProjection &proj, idx_t expression_count); void GatherRecursiveDependencies(unique_ptr &bottom, TableIndex cte_index, diff --git a/src/duckdb/src/include/duckdb/optimizer/statistics_propagator.hpp b/src/duckdb/src/include/duckdb/optimizer/statistics_propagator.hpp index d18660fa0..04142adc8 100644 --- a/src/duckdb/src/include/duckdb/optimizer/statistics_propagator.hpp +++ b/src/duckdb/src/include/duckdb/optimizer/statistics_propagator.hpp @@ -99,6 +99,8 @@ class StatisticsPropagator { FilterPropagateResult ClassifyFilter(Expression &condition); //! Simplify conjunctions using filter truth semantics bool SimplifyFilter(unique_ptr &condition); + //! Remove constant_or_null(TRUE, ...) conjuncts whose NULL checks are implied by retained predicates + void SimplifyConstantOrNull(vector> &expressions); //! Propagate a filter condition FilterPropagateResult HandleFilter(unique_ptr &condition); //! Rewrite a join whose condition can never match; returns true if the operator was replaced @@ -168,6 +170,8 @@ class StatisticsPropagator { unique_ptr node_stats; //! Whether statistics changed which relations a filter depends on bool filter_bindings_changed = false; + //! The chain of operators from the root down to the operator currently being propagated + vector>> propagation_path; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/optimizer/type_pushdown.hpp b/src/duckdb/src/include/duckdb/optimizer/type_pushdown.hpp index a25dd46e4..3d4d5eb07 100644 --- a/src/duckdb/src/include/duckdb/optimizer/type_pushdown.hpp +++ b/src/duckdb/src/include/duckdb/optimizer/type_pushdown.hpp @@ -88,9 +88,13 @@ void FindGetsAndProjections(LogicalOperator &op, Analyses &analyses, Projections struct GetBinding { GetAnalysis &analysis; + // Column index within LogicalGet ProjectionIndex column_index; // If column binding was part of a projection, this is non-nullptr LogicalProjection *projection; + // If column binding was part of projection, column index within + // LogicalProjection wrapping a LogicalGet + ProjectionIndex projection_column_index; }; /* @@ -116,11 +120,11 @@ unique_ptr PushdownOptimize(ClientContext &context, unique_ptr< if (expr == nullptr) { // Conflict for column continue; } - if (analysis.get.GetColumnIds()[column_index].IsVirtualColumn()) { - continue; - } + // Resolve() never yields a virtual column as a candidate + D_ASSERT(!analysis.get.GetColumnIds()[column_index].IsVirtualColumn()); TableFunctionProjectionExpressionInput input {analysis.get, *expr, column_index}; if (analysis.get.function.projection_expression_pushdown(context, input)) { + analysis.get.has_pushed_projection = true; analysis.get.returned_types[analysis.StorageIndex(column_index)] = expr->GetReturnType(); if (!analysis.get.types.empty()) { analysis.get.ResolveOperatorTypes(); diff --git a/src/duckdb/src/include/duckdb/optimizer/unnest_rewriter.hpp b/src/duckdb/src/include/duckdb/optimizer/unnest_rewriter.hpp index 6b05ae024..e50b15993 100644 --- a/src/duckdb/src/include/duckdb/optimizer/unnest_rewriter.hpp +++ b/src/duckdb/src/include/duckdb/optimizer/unnest_rewriter.hpp @@ -54,12 +54,13 @@ class UnnestRewriterPlanUpdater : LogicalOperatorVisitor { //! the SELECT class UnnestRewriter { public: - UnnestRewriter() { + explicit UnnestRewriter(ClientContext &context) : context(context) { } //! Rewrite duplicate eliminated joins with UNNESTs unique_ptr Optimize(unique_ptr op); private: + ClientContext &context; //! Find delim joins that contain an UNNEST void FindCandidates(unique_ptr &root, unique_ptr &op, vector>> &candidates); diff --git a/src/duckdb/src/include/duckdb/parallel/interrupt.hpp b/src/duckdb/src/include/duckdb/parallel/interrupt.hpp index 191ad9b69..16307c177 100644 --- a/src/duckdb/src/include/duckdb/parallel/interrupt.hpp +++ b/src/duckdb/src/include/duckdb/parallel/interrupt.hpp @@ -91,6 +91,13 @@ class StateWithBlockableTasks { return can_block; } + //! Take the blocked tasks so their callbacks can run after releasing the lock + vector TakeBlockedTasks() DUCKDB_REQUIRES(lock) { + vector result; + result.swap(blocked_tasks); + return result; + } + //! Unblock all tasks (must hold the lock) bool UnblockTasks() DUCKDB_REQUIRES(lock) { if (blocked_tasks.empty()) { diff --git a/src/duckdb/src/include/duckdb/parallel/pipeline.hpp b/src/duckdb/src/include/duckdb/parallel/pipeline.hpp index edad24075..47965464a 100644 --- a/src/duckdb/src/include/duckdb/parallel/pipeline.hpp +++ b/src/duckdb/src/include/duckdb/parallel/pipeline.hpp @@ -99,6 +99,14 @@ class PipelineBuildState { unique_ptr data; }; +//! Progress of a pipeline, split into the source and the (sink-adjusted) pipeline progress +struct PipelineProgress { + //! Progress as reported by the source, before normalization + ProgressData source; + //! Progress of the pipeline: the normalized source progress, adjusted by the sink + ProgressData pipeline; +}; + //! The Pipeline class represents an execution pipeline starting at a class Pipeline : public enable_shared_from_this { friend class Executor; @@ -154,6 +162,8 @@ class Pipeline : public enable_shared_from_this { //! Returns query progress bool GetProgress(ProgressData &progress_data); + //! Returns the progress of the source and of the pipeline separately + void GetDetailedProgress(PipelineProgress &progress); //! Returns a list of all operators (including source and sink) involved in this pipeline vector> GetOperators(); @@ -195,6 +205,10 @@ class Pipeline : public enable_shared_from_this { //! Updates the batch index of a pipeline (and returns the new minimum batch index) idx_t UpdateBatchIndex(idx_t old_index, idx_t new_index); +private: + //! Tells the progress verifier (if any) that a new run of this pipeline starts + void NotifyProgressReset(); + private: //! Whether or not the pipeline has been readied bool ready; diff --git a/src/duckdb/src/include/duckdb/parallel/pipeline_broadcast_exchange.hpp b/src/duckdb/src/include/duckdb/parallel/pipeline_broadcast_exchange.hpp index acaf571fb..a31d8c3d6 100644 --- a/src/duckdb/src/include/duckdb/parallel/pipeline_broadcast_exchange.hpp +++ b/src/duckdb/src/include/duckdb/parallel/pipeline_broadcast_exchange.hpp @@ -292,7 +292,7 @@ class PipelineBroadcastExchange { idx_t active_consumers = 0; bool has_batch_scan_consumer DUCKDB_GUARDED_BY(lock) = false; atomic produced_rows {0}; - ProducerState producer_state = ProducerState::ACTIVE; + ProducerState producer_state DUCKDB_GUARDED_BY(lock) = ProducerState::ACTIVE; WatermarkState watermark_state = WatermarkState::BELOW_HIGH_WATERMARK; optional_ptr log_operator; }; diff --git a/src/duckdb/src/include/duckdb/parallel/pipeline_executor.hpp b/src/duckdb/src/include/duckdb/parallel/pipeline_executor.hpp index 4377e68dc..14bc31b9d 100644 --- a/src/duckdb/src/include/duckdb/parallel/pipeline_executor.hpp +++ b/src/duckdb/src/include/duckdb/parallel/pipeline_executor.hpp @@ -173,6 +173,8 @@ class PipelineExecutor { bool has_executed = false; //! Whether an externally-fed sink has observed its initial batch bool external_batch_initialized = false; + //! Rows of the last source chunk that have not been reported to the progress verifier yet + idx_t unsampled_progress_rows = 0; private: void StartOperator(PhysicalOperator &op); @@ -181,6 +183,8 @@ class PipelineExecutor { //! Reset the operator index to the first operator void GoToSource(idx_t ¤t_idx, idx_t initial_idx); SourceFetchResult FetchFromSource(DataChunk &result); + //! Reports the last source chunk to the progress verifier (if any), once it has been fully processed + void SampleProgress(); void FinishProcessing(int32_t operator_idx = -1); void NotifySourceFinished(); diff --git a/src/duckdb/src/include/duckdb/parallel/progress_verifier.hpp b/src/duckdb/src/include/duckdb/parallel/progress_verifier.hpp new file mode 100644 index 000000000..26af45182 --- /dev/null +++ b/src/duckdb/src/include/duckdb/parallel/progress_verifier.hpp @@ -0,0 +1,116 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/parallel/progress_verifier.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/common.hpp" +#include "duckdb/common/enums/debug_progress_verification.hpp" +#include "duckdb/common/mutex.hpp" +#include "duckdb/common/optional_idx.hpp" +#include "duckdb/common/unordered_map.hpp" +#include "duckdb/common/unordered_set.hpp" + +namespace duckdb { +class ClientContext; +class PhysicalOperator; +class Pipeline; +struct PipelineProgress; + +enum class ProgressInvariant : uint8_t { + //! The source reported invalid progress while it was producing data + UNSUPPORTED_SOURCE, + //! The sink turned valid source progress into invalid progress + UNSUPPORTED_SINK, + //! The source reported progress that is not finite or outside of [0, total] + MALFORMED_SOURCE, + //! The sink reported progress that is not finite or outside of [0, total] + MALFORMED_SINK, + //! The progress of the pipeline decreased + NON_MONOTONIC, + //! The pipeline ran to completion but did not report 100% progress + INCOMPLETE, + //! The progress of the pipeline did not change while it produced many chunks + STALLED, + //! The source progress deviates strongly from the fraction of rows produced (log-only) + INACCURATE +}; + +//! The ProgressVerifier samples the progress of every pipeline after each source chunk and at the end of the query, +//! and verifies that the reported progress is well-formed, monotonic, moving and complete +class ProgressVerifier { +public: + ProgressVerifier(ClientContext &context, DebugProgressVerification mode, const string &ignore_list); + +public: + //! Samples the progress of a pipeline after a non-empty source chunk was pushed through the pipeline + void OnSourceChunk(Pipeline &pipeline, idx_t count); + //! Called when a pipeline executor finished without exhausting its source (e.g. because of a LIMIT) + void OnEarlyExit(Pipeline &pipeline); + //! Called when the source or sink state of a pipeline is reset - this starts a new run of the pipeline + void OnReset(Pipeline &pipeline); + //! Takes a final sample of the given (completed) pipelines and verifies all traces + //! Returns an error message if any violation that fails the query was found + string Finalize(const vector> &pipelines); + + //! The name that is used to attribute a violation to an operator + static string OperatorName(const PhysicalOperator &op); + +private: + struct ProgressSample { + //! Progress fraction reported by the source - or NaN if the source progress was invalid + double source_fraction; + //! Progress fraction of the pipeline - or NaN if the pipeline progress was invalid + double fraction; + //! Rows produced by the source at the time of the sample + idx_t rows; + }; + struct PipelineRun { + vector samples; + idx_t rows = 0; + idx_t chunks = 0; + bool early_exit = false; + bool has_final_sample = false; + optional_idx first_unsupported_source; + optional_idx first_unsupported_sink; + }; + struct PipelineTrace { + //! Held while sampling, so samples are recorded in the order in which they were taken + mutex lock; + string source_name; + string sink_name; + PipelineRun run; + }; + struct Violation { + ProgressInvariant invariant; + string operator_name; + string pipeline; + string detail; + }; + +private: + PipelineTrace &GetTrace(Pipeline &pipeline); + void Sample(Pipeline &pipeline, PipelineTrace &trace, bool is_final); + void VerifyRun(PipelineTrace &trace); + void AddViolation(const PipelineTrace &trace, ProgressInvariant invariant, const string &operator_name, + const string &detail); + bool IsIgnored(const Violation &violation) const; + static bool FailsQuery(ProgressInvariant invariant); + +private: + ClientContext &context; + DebugProgressVerification mode; + unordered_set ignore_list; + + //! Protects the traces map and the violations + mutex lock; + unordered_map> traces; + vector violations; + unordered_set reported; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parallel/scan_read_ahead.hpp b/src/duckdb/src/include/duckdb/parallel/scan_read_ahead.hpp index 78b985bce..b40a20121 100644 --- a/src/duckdb/src/include/duckdb/parallel/scan_read_ahead.hpp +++ b/src/duckdb/src/include/duckdb/parallel/scan_read_ahead.hpp @@ -14,6 +14,8 @@ #include "duckdb/common/limits.hpp" #include "duckdb/common/map.hpp" #include "duckdb/common/mutex.hpp" +#include "duckdb/common/condition_variable.hpp" +#include "duckdb/common/optional.hpp" #include "duckdb/common/deque.hpp" #include "duckdb/common/vector.hpp" #include "duckdb/common/serializer/async_memory_governor.hpp" @@ -154,11 +156,17 @@ class ScanReadAhead { void PushError(ErrorData error); //! Schedule a file-open closure on the async pool, opening files ahead of decoding - void ScheduleFileOpen(std::function open_fn); + //! cancel_fn runs instead when the open is retired without running, and must settle whatever a scan waits on + void ScheduleFileOpen(std::function open_fn, std::function cancel_fn); //! Whether another file-open may be scheduled without exceeding the open-ahead window bool CanScheduleOpen() const; - //! Run one queued async task inline, throwing any recorded async error; returns false when none is queued + //! Run one queued async task inline, returns false when none is queued bool TryRunPendingTask(); + //! Throw the first error recorded on the async executor, if there is one + void ThrowIfError(); + //! Cancel the tasks that have not started yet and wait for the ones that are running. The scheduled closures + //! reference the scan they were scheduled for, so they must not outlive it + void CancelAndDrain(); private: //! Settles the reservation taken by TryReserveSlot @@ -168,8 +176,13 @@ class ScanReadAhead { ~ProducerReservation() { if (!committed) { read_ahead.ReleaseSlot(); + read_ahead.pending_io_bytes -= MINIMUM_JOB_IO_CHARGE; } --read_ahead.active_producers; + // a pushed job was signalled already, a released slot frees one thread and a done scan all of them + if (!committed || read_ahead.IsDone()) { + read_ahead.SignalProgress(read_ahead.IsDone()); + } } ScanReadAhead &read_ahead; @@ -191,14 +204,29 @@ class ScanReadAhead { lock_guard guard(lock); return !pending_jobs.empty(); } + //! The number of jobs that may be in flight under the current budget + idx_t ReservationDepth() const; //! Reserve an in-flight job slot for producing a job bool TryReserveSlot(); //! Schedule the job's I/O and admit the job to the queue void PushJob(unique_ptr job, vector> io_tasks); - //! Throw if any read-ahead thread or task pushed an error - void ThrowIfError(); //! Release a read-ahead slot void ReleaseSlot(); + //! Wake a thread waiting for another thread to push a job, release a slot or finish producing, or all of them + void SignalProgress(bool wake_all); + //! SignalProgress for a caller that holds the lock + void NotifyProgress(bool wake_all); + //! Wake one waiting thread, or all of them + void WakeWaiters(bool wake_all); + //! Wait until the read-ahead made progress since the given epoch, or the timeout passed + void WaitForProgress(ClientContext &context, idx_t epoch); + idx_t ProgressEpoch() const; + +private: + //! Minimum budget charge per job, beyond its scheduled I/O a job carries scan-state overhead (decode buffers) + static constexpr idx_t MINIMUM_JOB_IO_CHARGE = 16ULL * 1024 * 1024; + //! How long a thread yields for another thread to push a job before it waits for it + static constexpr idx_t PRODUCER_YIELD_MICROS = 1000; private: ClientContext &context; @@ -219,11 +247,16 @@ class ScanReadAhead { idx_t next_batch_index = 0; //! Jobs scheduled ahead of decoding atomic active_jobs {0}; - //! Bytes of scheduled I/O that has not completed yet, released once the claimed job's I/O finished + //! Budget charge of the reserved producer slots and scheduled jobs, released per job in WaitForJob atomic pending_io_bytes {0}; atomic done {false}; //! Threads that reserved a slot but have not pushed their job yet atomic active_producers {0}; + //! Incremented whenever a waiting thread could make progress + atomic progress_epoch {0}; + //! Threads waiting for progress, only these need a notification + atomic waiting_threads {0}; + condition_variable progress; //! File-opens scheduled on the async pool that have not completed yet, shared with the open tasks shared_ptr> pending_opens; //! Async I/O executor (async pool) diff --git a/src/duckdb/src/include/duckdb/parallel/task_executor.hpp b/src/duckdb/src/include/duckdb/parallel/task_executor.hpp index 361ea04e5..22103abf4 100644 --- a/src/duckdb/src/include/duckdb/parallel/task_executor.hpp +++ b/src/duckdb/src/include/duckdb/parallel/task_executor.hpp @@ -17,6 +17,49 @@ namespace duckdb { +class TaskExecutor; +class TaskExecutorTask; + +//! A unit of work scheduled on a TaskExecutor +//! Deliberately not a Task: it cannot be handed to the TaskScheduler directly, it cannot be descheduled or +//! rescheduled, and it cannot report partial progress. The executor wraps it and owns cancellation, error handling +//! and task accounting, so implementations only have to describe their work. +class BaseExecutorTask { +public: + explicit BaseExecutorTask(TaskExecutor &executor); + virtual ~BaseExecutorTask() = default; + +public: + //! Perform the task's work in one call - throwing is allowed, the executor captures the error and cancels the + //! other tasks. Run-to-completion tasks override this. + virtual void ExecuteTask() { + throw InternalException("BaseExecutorTask::ExecuteTask was not implemented"); + } + //! Perform one unit of work, returning TASK_NOT_FINISHED while more remains and TASK_FINISHED when done. + //! Incremental tasks override this instead of ExecuteTask; the default runs the whole task in one call. The + //! executor loops this to completion while draining, and calls it once per turn on a background thread so the + //! task yields cooperatively. The mode never reaches the task: the wrapper owns the loop. + virtual TaskExecutionResult ExecuteTaskStep() { + ExecuteTask(); + return TaskExecutionResult::TASK_FINISHED; + } + //! Called instead of ExecuteTask when the task is retired without running its work, because another task errored, + //! because the executor was cancelled, or because the task could not be queued at all. Exactly one of ExecuteTask + //! or Cancel runs for every task passed to TaskExecutor::ScheduleTask or ScheduleTasks. + //! Anything a waiter watches must change on every exit path, or that waiter never wakes up. Either settle it here + //! as well as in ExecuteTask, or settle it in the destructor, which also covers a task that was constructed but + //! never scheduled. A destructor settle runs after the executor has already counted the task as finished, so it + //! must not be something a drain is expected to observe. + virtual void Cancel() { + } + virtual string TaskType() const { + return "UnnamedTask"; + } + +protected: + TaskExecutor &executor; +}; + //! The TaskExecutor is a helper class that enables parallel scheduling and execution of tasks class TaskExecutor { public: @@ -30,11 +73,15 @@ class TaskExecutor { bool HasError(); //! Throw an error that was encountered during execution (if HasError() is true) void ThrowError(); + //! Get the first error that was encountered during execution (if HasError() is true) + ErrorData GetError(); + //! Whether the executor has been cancelled + bool IsCancelled() const; //! Schedule a new task - void ScheduleTask(unique_ptr task); - //! Label a task as finished - void FinishTask(); + void ScheduleTask(unique_ptr task); + //! Schedule a batch, cancelling unscheduled tasks if submission fails. + void ScheduleTasks(vector> tasks); //! Work on tasks until all tasks are finished. Throws an exception if any error occurred while executing the tasks. void WorkOnTasks(); @@ -44,12 +91,27 @@ class TaskExecutor { //! Get a task - returns true if a task was found bool GetTask(shared_ptr &task); +public: + //! Joins the executor when the scope is left, so that its tasks never outlive the state they reference. + //! Only needed for an executor that outlives the scope scheduling onto it, where its own destructor is not the + //! join. Errors are swallowed: the exception that is unwinding wins, and a recorded task error is still there. + class JoinGuard { + public: + explicit JoinGuard(TaskExecutor &executor); + ~JoinGuard(); + + private: + TaskExecutor &executor; + }; + private: //! Work on tasks until all tasks are finished void DrainTasks(); + //! Label a task as finished - called by the wrapper the executor puts around every scheduled task + void FinishTask(); private: - friend class BaseExecutorTask; + friend class TaskExecutorTask; TaskScheduler &scheduler; const TaskSchedulerType type; @@ -61,15 +123,4 @@ class TaskExecutor { optional_ptr context; }; -class BaseExecutorTask : public Task { -public: - explicit BaseExecutorTask(TaskExecutor &executor); - - virtual void ExecuteTask() = 0; - TaskExecutionResult Execute(TaskExecutionMode mode) override; - -protected: - TaskExecutor &executor; -}; - } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parallel/task_scheduler.hpp b/src/duckdb/src/include/duckdb/parallel/task_scheduler.hpp index 17dab3513..cd691967b 100644 --- a/src/duckdb/src/include/duckdb/parallel/task_scheduler.hpp +++ b/src/duckdb/src/include/duckdb/parallel/task_scheduler.hpp @@ -88,9 +88,9 @@ class TaskScheduler { TaskSchedulerQueue &GetQueue(TaskSchedulerType pool_type) const; //! Fetches a task, returns true if successful or false if no tasks were available - bool GetTaskInternal(shared_ptr &task); - bool GetTaskInternal(shared_ptr &task, TaskSchedulerType pool_type); - bool TryDequeueAndProcessTask(const DBConfig &config, TaskSchedulerQueue &queue, shared_ptr &task); + bool GetTaskInternal(ConsumerToken &consumer, shared_ptr &task); + bool TryDequeueAndProcessTask(const DBConfig &config, TaskSchedulerQueue &queue, ConsumerToken &consumer, + shared_ptr &task); void SetThreadsInternal(TaskSchedulerType pool_type, idx_t n); void Signal(TaskSchedulerType pool_type, idx_t n); diff --git a/src/duckdb/src/include/duckdb/parallel/task_scheduler_queue.hpp b/src/duckdb/src/include/duckdb/parallel/task_scheduler_queue.hpp index 36ca7b3d3..87d66cd32 100644 --- a/src/duckdb/src/include/duckdb/parallel/task_scheduler_queue.hpp +++ b/src/duckdb/src/include/duckdb/parallel/task_scheduler_queue.hpp @@ -37,7 +37,7 @@ class TaskSchedulerQueue { void Enqueue(ProducerToken &token, shared_ptr task); void EnqueueBulk(ProducerToken &token, vector> &tasks); bool DequeueFromProducer(ProducerToken &token, shared_ptr &task); - bool Dequeue(shared_ptr &task); + bool Dequeue(ConsumerToken &token, shared_ptr &task); idx_t GetTasksInQueue() const; idx_t GetApproxSize() const; idx_t GetProducerCount() const; diff --git a/src/duckdb/src/include/duckdb/parallel/task_scheduler_token.hpp b/src/duckdb/src/include/duckdb/parallel/task_scheduler_token.hpp index ff249bddd..34296c10f 100644 --- a/src/duckdb/src/include/duckdb/parallel/task_scheduler_token.hpp +++ b/src/duckdb/src/include/duckdb/parallel/task_scheduler_token.hpp @@ -19,6 +19,7 @@ namespace duckdb { // Forward declarations. struct QueueProducerToken; +struct QueueConsumerToken; class TaskSchedulerQueue; struct ProducerToken { @@ -37,4 +38,17 @@ struct ProducerToken { array, TASK_SCHEDULER_TYPE_COUNT> tokens; }; +//! Dequeues tasks from any producer, rotating across producers so that every query gets a turn +struct ConsumerToken { +public: + explicit ConsumerToken(array, TASK_SCHEDULER_TYPE_COUNT> &queues); + ~ConsumerToken(); + +public: + QueueConsumerToken &GetQueueConsumerToken(TaskSchedulerType pool_type); + +private: + array, TASK_SCHEDULER_TYPE_COUNT> tokens; +}; + } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/constraint.hpp b/src/duckdb/src/include/duckdb/parser/constraint.hpp index e9e3c01a2..f4e5b907d 100644 --- a/src/duckdb/src/include/duckdb/parser/constraint.hpp +++ b/src/duckdb/src/include/duckdb/parser/constraint.hpp @@ -10,6 +10,7 @@ #include "duckdb/common/constants.hpp" #include "duckdb/common/identifier.hpp" +#include "duckdb/common/optional_idx.hpp" #include "duckdb/common/vector.hpp" #include "duckdb/common/exception.hpp" @@ -73,6 +74,9 @@ class Constraint { DUCKDB_API virtual void Serialize(Serializer &serializer) const; DUCKDB_API static unique_ptr Deserialize(Deserializer &deserializer); + idx_t GetBackingIndexOid() const; + void SetBackingIndexOid(idx_t oid); + public: template TARGET &Cast() { @@ -89,5 +93,12 @@ class Constraint { } return reinterpret_cast(*this); } + +private: + //! Whether the table enforces this constraint through an index it owns. + bool NeedsBackingIndex() const; + + //! The backing index identity is local to this database instance and is never persisted. + optional_idx backing_index_oid; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/dialect_extension.hpp b/src/duckdb/src/include/duckdb/parser/dialect_extension.hpp deleted file mode 100644 index 8ae41ef2b..000000000 --- a/src/duckdb/src/include/duckdb/parser/dialect_extension.hpp +++ /dev/null @@ -1,27 +0,0 @@ -//===----------------------------------------------------------------------===// -// DuckDB -// -// duckdb/parser/dialect_extension.hpp -// -// -//===----------------------------------------------------------------------===// - -#pragma once - -#include "duckdb/common/common.hpp" - -namespace duckdb { -struct DBConfig; - -//! A named SQL dialect that can customize the PEG parser. -class DialectExtension { -public: - explicit DialectExtension(string name_p) : name(std::move(name_p)) { - } - - string name; - - static void Register(DBConfig &config, DialectExtension extension); -}; - -} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/expression/constant_expression.hpp b/src/duckdb/src/include/duckdb/parser/expression/constant_expression.hpp index e8b60ded4..ec754eb8b 100644 --- a/src/duckdb/src/include/duckdb/parser/expression/constant_expression.hpp +++ b/src/duckdb/src/include/duckdb/parser/expression/constant_expression.hpp @@ -42,6 +42,8 @@ class ConstantExpression : public ParsedExpression { //! Builds the parsed expression for a value: a literal when it re-binds to the same value, the constructor //! call the parser produces for a nested value, or a cast otherwise DUCKDB_API static unique_ptr FromValue(const Value &value); + //! Whether SQL needs a value constructor to preserve the type's metadata + DUCKDB_API static bool RequiresTypeWitness(const LogicalType &type); public: string ToString() const override; diff --git a/src/duckdb/src/include/duckdb/parser/expression/type_expression.hpp b/src/duckdb/src/include/duckdb/parser/expression/type_expression.hpp index 0e398c547..ffe595d13 100644 --- a/src/duckdb/src/include/duckdb/parser/expression/type_expression.hpp +++ b/src/duckdb/src/include/duckdb/parser/expression/type_expression.hpp @@ -29,6 +29,10 @@ class TypeExpression : public ParsedExpression { //! parameterised built-in is written out with its parameters, and a type that carries an alias (a //! user-defined type) is named by that alias. Throws for types that have no SQL spelling. DUCKDB_API static unique_ptr FromLogicalType(const LogicalType &type); + //! Whether the type ID has a SQL spelling + DUCKDB_API static bool IsSQLType(LogicalTypeId id); + //! Whether the complete type can be written as a SQL type expression + DUCKDB_API static bool CanRepresent(const LogicalType &type); const QualifiedName &GetQualifiedName() const { return qualified_name; diff --git a/src/duckdb/src/include/duckdb/parser/expression/window_expression.hpp b/src/duckdb/src/include/duckdb/parser/expression/window_expression.hpp index 878c6e0bb..cedda0e33 100644 --- a/src/duckdb/src/include/duckdb/parser/expression/window_expression.hpp +++ b/src/duckdb/src/include/duckdb/parser/expression/window_expression.hpp @@ -73,9 +73,10 @@ class WindowExpression : public ParsedExpression { } void SetQualifiedName(QualifiedName name) { qualified_name = std::move(name); + type = WindowToExpressionType(qualified_name.Name().GetIdentifierName()); } void SetQualifiedName(Identifier catalog, Identifier schema, Identifier name) { - qualified_name = QualifiedName(std::move(catalog), std::move(schema), std::move(name)); + SetQualifiedName(QualifiedName(std::move(catalog), std::move(schema), std::move(name))); } const Identifier &FunctionName() const { return qualified_name.Name(); diff --git a/src/duckdb/src/include/duckdb/parser/grammar_change.hpp b/src/duckdb/src/include/duckdb/parser/grammar_change.hpp index dfc0565e7..298c2820e 100644 --- a/src/duckdb/src/include/duckdb/parser/grammar_change.hpp +++ b/src/duckdb/src/include/duckdb/parser/grammar_change.hpp @@ -27,8 +27,10 @@ enum class GrammarChangeType : uint8_t { //! A structured change to apply while constructing a PEG grammar. class GrammarChange { public: + //! See ParsedGrammar::AddRule for the meaning of suppress_suggestions DUCKDB_API static GrammarChange AddRule(const string &rule_definition, - grammar_transform_process_function_t transform_process = nullptr); + grammar_transform_process_function_t transform_process = nullptr, + bool suppress_suggestions = false); DUCKDB_API static GrammarChange AddChoice(const string &rule_name, const string &choice, grammar_cursor_function_t find_cursor = nullptr); DUCKDB_API static GrammarChange PrependChoice(const string &rule_name, const string &choice, @@ -38,8 +40,10 @@ class GrammarChange { grammar_cursor_function_t find_cursor); DUCKDB_API static GrammarChange ReplaceRule(const string &rule_definition, grammar_transform_process_function_t transform_process = nullptr); + //! See ParsedGrammar::SetTransformProcess for the meaning of collapsible DUCKDB_API static GrammarChange SetTransformProcess(const string &rule_name, - grammar_transform_process_function_t transform_process); + grammar_transform_process_function_t transform_process, + bool collapsible = false, bool suppress_suggestions = false); DUCKDB_API static GrammarChange AddTerminalRuleOverride(const string &rule_name, terminal_rule_matcher_factory_t matcher_factory); @@ -60,14 +64,16 @@ class GrammarChange { static GrammarChange Create(GrammarChangeType type, string rule_name, string definition, grammar_transform_process_function_t transform_process = nullptr, grammar_cursor_function_t find_cursor = nullptr, - terminal_rule_matcher_factory_t matcher_factory = nullptr); + terminal_rule_matcher_factory_t matcher_factory = nullptr, bool collapsible = false, + bool suppress_suggestions = false); GrammarChange(GrammarChangeType type_p, string rule_name_p, string definition_p, grammar_transform_process_function_t transform_process_p, grammar_cursor_function_t find_cursor_p, - terminal_rule_matcher_factory_t matcher_factory_p) + terminal_rule_matcher_factory_t matcher_factory_p, bool collapsible_p, bool suppress_suggestions_p) : type(type_p), rule_name(std::move(rule_name_p)), definition(std::move(definition_p)), transform_process(std::move(transform_process_p)), find_cursor(std::move(find_cursor_p)), - matcher_factory(std::move(matcher_factory_p)) { + matcher_factory(std::move(matcher_factory_p)), collapsible(collapsible_p), + suppress_suggestions(suppress_suggestions_p) { } private: @@ -78,6 +84,8 @@ class GrammarChange { grammar_transform_process_function_t transform_process; grammar_cursor_function_t find_cursor; terminal_rule_matcher_factory_t matcher_factory; + bool collapsible; + bool suppress_suggestions; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/parsed_data/alter_info.hpp b/src/duckdb/src/include/duckdb/parser/parsed_data/alter_info.hpp index f5c32a814..d55a8a45e 100644 --- a/src/duckdb/src/include/duckdb/parser/parsed_data/alter_info.hpp +++ b/src/duckdb/src/include/duckdb/parser/parsed_data/alter_info.hpp @@ -28,7 +28,8 @@ enum class AlterType : uint8_t { ALTER_TABLE_FUNCTION = 6, SET_COMMENT = 7, SET_COLUMN_COMMENT = 8, - ALTER_DATABASE = 9 + ALTER_DATABASE = 9, + ALTER_SCHEMA = 10 }; enum class AlterBindMode { BIND_ON_ALTER, SKIP_BINDING }; diff --git a/src/duckdb/src/include/duckdb/parser/parsed_data/alter_schema_info.hpp b/src/duckdb/src/include/duckdb/parser/parsed_data/alter_schema_info.hpp new file mode 100644 index 000000000..e2faad208 --- /dev/null +++ b/src/duckdb/src/include/duckdb/parser/parsed_data/alter_schema_info.hpp @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/parser/parsed_data/alter_schema_info.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/parser/parsed_data/alter_info.hpp" +#include "duckdb/parser/parsed_expression.hpp" +#include "duckdb/common/case_insensitive_map.hpp" + +namespace duckdb { + +enum class AlterSchemaType : uint8_t { INVALID = 0, SET_SCHEMA_OPTIONS = 1, RESET_SCHEMA_OPTIONS = 2 }; + +//! The qualified name uses the same layout as CreateSchemaInfo: [catalog, parent_schemas..., schema, ] +struct AlterSchemaInfo : public AlterInfo { +public: + AlterSchemaInfo(AlterSchemaType alter_schema_type, const AlterEntryData &data); + ~AlterSchemaInfo() override; + + AlterSchemaType alter_schema_type; + +public: + //! The name of the schema being altered + const Identifier &SchemaName() const; + //! The catalog the schema lives in (empty if the path carries no catalog component) + const Identifier &SchemaCatalog() const; + //! The full schema path (parent schemas followed by the altered schema), excluding the catalog + vector SchemaPath() const; + + CatalogType GetCatalogType() const override; + + static unique_ptr Deserialize(Deserializer &deserializer); + +protected: + explicit AlterSchemaInfo(AlterSchemaType alter_schema_type); + + void Serialize(Serializer &serializer) const override; + string QualifiedSchemaToString() const; +}; + +//===--------------------------------------------------------------------===// +// SetSchemaOptionsInfo +//===--------------------------------------------------------------------===// +struct SetSchemaOptionsInfo : public AlterSchemaInfo { + SetSchemaOptionsInfo(const AlterEntryData &data, case_insensitive_map_t> options); + ~SetSchemaOptionsInfo() override; + + case_insensitive_map_t> options; + +public: + unique_ptr Copy() const override; + string ToString() const override; + + void Serialize(Serializer &serializer) const override; + static unique_ptr Deserialize(Deserializer &deserializer); + +private: + SetSchemaOptionsInfo(); +}; + +//===--------------------------------------------------------------------===// +// ResetSchemaOptionsInfo +//===--------------------------------------------------------------------===// +struct ResetSchemaOptionsInfo : public AlterSchemaInfo { + ResetSchemaOptionsInfo(const AlterEntryData &data, identifier_set_t options); + ~ResetSchemaOptionsInfo() override; + + identifier_set_t options; + +public: + unique_ptr Copy() const override; + string ToString() const override; + + void Serialize(Serializer &serializer) const override; + static unique_ptr Deserialize(Deserializer &deserializer); + +private: + ResetSchemaOptionsInfo(); +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/parsed_data/alter_table_function_info.hpp b/src/duckdb/src/include/duckdb/parser/parsed_data/alter_table_function_info.hpp index c087c2594..116e579c6 100644 --- a/src/duckdb/src/include/duckdb/parser/parsed_data/alter_table_function_info.hpp +++ b/src/duckdb/src/include/duckdb/parser/parsed_data/alter_table_function_info.hpp @@ -10,6 +10,7 @@ #include "duckdb/function/function_set.hpp" #include "duckdb/parser/parsed_data/alter_info.hpp" +#include "duckdb/parser/parsed_data/create_function_info.hpp" namespace duckdb { @@ -32,10 +33,13 @@ struct AlterTableFunctionInfo : public AlterInfo { // AddTableFunctionOverloadInfo //===--------------------------------------------------------------------===// struct AddTableFunctionOverloadInfo : public AlterTableFunctionInfo { - AddTableFunctionOverloadInfo(const AlterEntryData &data, TableFunctionSet new_overloads); + AddTableFunctionOverloadInfo(const AlterEntryData &data, TableFunctionSet new_overloads, + vector new_descriptions = {}); ~AddTableFunctionOverloadInfo() override; TableFunctionSet new_overloads; + //! The descriptions of the new overloads + vector new_descriptions; public: unique_ptr Copy() const override; diff --git a/src/duckdb/src/include/duckdb/parser/parsed_data/alter_table_info.hpp b/src/duckdb/src/include/duckdb/parser/parsed_data/alter_table_info.hpp index bdf58e13a..a849846e7 100644 --- a/src/duckdb/src/include/duckdb/parser/parsed_data/alter_table_info.hpp +++ b/src/duckdb/src/include/duckdb/parser/parsed_data/alter_table_info.hpp @@ -180,17 +180,23 @@ struct RenameTableInfo : public AlterTableInfo { //===--------------------------------------------------------------------===// // AddColumnInfo //===--------------------------------------------------------------------===// + +struct AddColumnConstraints { + bool add_not_null = false; + bool add_unique = false; +}; + struct AddColumnInfo : public AlterTableInfo { - AddColumnInfo(const AlterEntryData &data, ColumnDefinition new_column, bool if_column_not_exists, - bool add_not_null); + AddColumnInfo(const AlterEntryData &data, ColumnDefinition new_column_p, bool if_column_not_exists_p, + AddColumnConstraints add_column_constraints_p); ~AddColumnInfo() override; //! New column ColumnDefinition new_column; //! Whether or not an error should be thrown if the column exist bool if_column_not_exists; - //! Whether to add a NOT NULL constraint after adding the column - bool add_not_null = false; + //! Constraints applied via extra ALTER statements after the column is added + AddColumnConstraints add_column_constraints; public: unique_ptr Copy() const override; @@ -290,12 +296,12 @@ struct RemoveFieldInfo : public AlterTableInfo { // ChangeColumnTypeInfo //===--------------------------------------------------------------------===// struct ChangeColumnTypeInfo : public AlterTableInfo { - ChangeColumnTypeInfo(const AlterEntryData &data, Identifier column_name, LogicalType target_type, + ChangeColumnTypeInfo(const AlterEntryData &data, vector column_path, LogicalType target_type, unique_ptr expression); ~ChangeColumnTypeInfo() override; - //! The column name to alter - Identifier column_name; + //! Path to the column to alter, e.g. ["s", "a"] for "s.a" + vector column_path; //! The target type of the column LogicalType target_type; //! The expression used for data conversion @@ -307,7 +313,7 @@ struct ChangeColumnTypeInfo : public AlterTableInfo { void Serialize(Serializer &serializer) const override; static unique_ptr Deserialize(Deserializer &deserializer); Identifier GetColumnName() const override { - return column_name; + return column_path[0]; } private: @@ -318,11 +324,12 @@ struct ChangeColumnTypeInfo : public AlterTableInfo { // SetDefaultInfo //===--------------------------------------------------------------------===// struct SetDefaultInfo : public AlterTableInfo { - SetDefaultInfo(const AlterEntryData &data, Identifier column_name, unique_ptr new_default); + SetDefaultInfo(const AlterEntryData &data, vector column_path, + unique_ptr new_default); ~SetDefaultInfo() override; - //! The column name to alter - Identifier column_name; + //! Path to the column to alter, e.g. ["s", "a"] for "s.a" + vector column_path; //! The expression used for data conversion unique_ptr expression; @@ -366,11 +373,11 @@ struct AlterForeignKeyInfo : public AlterTableInfo { // SetNotNullInfo //===--------------------------------------------------------------------===// struct SetNotNullInfo : public AlterTableInfo { - SetNotNullInfo(const AlterEntryData &data, Identifier column_name); + SetNotNullInfo(const AlterEntryData &data, vector column_path); ~SetNotNullInfo() override; - //! The column name to alter - Identifier column_name; + //! Path to the column to alter, e.g. ["s", "a"] for "s.a" + vector column_path; public: unique_ptr Copy() const override; @@ -386,11 +393,11 @@ struct SetNotNullInfo : public AlterTableInfo { // DropNotNullInfo //===--------------------------------------------------------------------===// struct DropNotNullInfo : public AlterTableInfo { - DropNotNullInfo(const AlterEntryData &data, Identifier column_name); + DropNotNullInfo(const AlterEntryData &data, vector column_path); ~DropNotNullInfo() override; - //! The column name to alter - Identifier column_name; + //! Path to the column to alter, e.g. ["s", "a"] for "s.a" + vector column_path; public: unique_ptr Copy() const override; diff --git a/src/duckdb/src/include/duckdb/parser/parsed_data/attach_info.hpp b/src/duckdb/src/include/duckdb/parser/parsed_data/attach_info.hpp index e0b8b55d6..c531943ed 100644 --- a/src/duckdb/src/include/duckdb/parser/parsed_data/attach_info.hpp +++ b/src/duckdb/src/include/duckdb/parser/parsed_data/attach_info.hpp @@ -10,6 +10,7 @@ #include "duckdb/common/identifier.hpp" #include "duckdb/parser/parsed_data/parse_info.hpp" +#include "duckdb/parser/parsed_data/external_resource_options.hpp" #include "duckdb/common/unordered_map.hpp" #include "duckdb/common/types/value.hpp" #include "duckdb/common/enums/on_create_conflict.hpp" @@ -37,6 +38,8 @@ struct AttachInfo : public ParseInfo { unordered_map options; //! What to do on create conflict OnCreateConflict on_conflict = OnCreateConflict::ERROR_ON_CONFLICT; + //! Set iff parsed as `ATTACH TO [NEW TEMPORARY] EXTERNAL RESOURCE ...`: the resource to attach. + unique_ptr external_resource; public: //! Copies this AttachInfo and returns an unique pointer to the new AttachInfo. diff --git a/src/duckdb/src/include/duckdb/parser/parsed_data/bound_pragma_info.hpp b/src/duckdb/src/include/duckdb/parser/parsed_data/bound_pragma_info.hpp index 90f7c27b3..0422b7741 100644 --- a/src/duckdb/src/include/duckdb/parser/parsed_data/bound_pragma_info.hpp +++ b/src/duckdb/src/include/duckdb/parser/parsed_data/bound_pragma_info.hpp @@ -13,7 +13,7 @@ namespace duckdb { struct BoundPragmaInfo { - BoundPragmaInfo(PragmaFunction function_p, vector parameters_p, named_parameter_map_t named_parameters_p) + BoundPragmaInfo(PragmaFunction function_p, vector parameters_p, named_argument_map_t named_parameters_p) : function(std::move(function_p)), parameters(std::move(parameters_p)), named_parameters(std::move(named_parameters_p)) { } @@ -22,7 +22,7 @@ struct BoundPragmaInfo { //! Parameter list (if any) vector parameters; //! Named parameter list (if any) - named_parameter_map_t named_parameters; + named_argument_map_t named_parameters; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/parsed_data/connect_info.hpp b/src/duckdb/src/include/duckdb/parser/parsed_data/connect_info.hpp index ce5af3826..0d272c2bb 100644 --- a/src/duckdb/src/include/duckdb/parser/parsed_data/connect_info.hpp +++ b/src/duckdb/src/include/duckdb/parser/parsed_data/connect_info.hpp @@ -13,6 +13,7 @@ #include "duckdb/common/types/value.hpp" #include "duckdb/common/unordered_map.hpp" #include "duckdb/parser/parsed_data/parse_info.hpp" +#include "duckdb/parser/parsed_data/external_resource_options.hpp" #include "duckdb/parser/parsed_expression.hpp" namespace duckdb { @@ -41,6 +42,8 @@ struct ConnectInfo : public ParseInfo { case_insensitive_map_t> parsed_options; //! Set of bound (key, value) options forwarded to the implicit ATTACH. unordered_map options; + //! Set iff parsed as `CONNECT TO [NEW TEMPORARY] EXTERNAL RESOURCE ...`: the resource to connect to. + unique_ptr external_resource; public: unique_ptr Copy() const; diff --git a/src/duckdb/src/include/duckdb/parser/parsed_data/create_schema_info.hpp b/src/duckdb/src/include/duckdb/parser/parsed_data/create_schema_info.hpp index a9ff5d087..6ce431a86 100644 --- a/src/duckdb/src/include/duckdb/parser/parsed_data/create_schema_info.hpp +++ b/src/duckdb/src/include/duckdb/parser/parsed_data/create_schema_info.hpp @@ -9,12 +9,17 @@ #pragma once #include "duckdb/parser/parsed_data/create_info.hpp" +#include "duckdb/parser/parsed_expression.hpp" +#include "duckdb/common/case_insensitive_map.hpp" namespace duckdb { struct CreateSchemaInfo : public CreateInfo { CreateSchemaInfo(); + //! Extra schema options if any + case_insensitive_map_t> options; + public: //! The qualified name encodes the full path as [catalog, parent_schemas..., new_schema, ]. The empty //! trailing name keeps the new schema in the Schema() slot (so catalog/schema serialization stays correct). These @@ -27,6 +32,8 @@ struct CreateSchemaInfo : public CreateInfo { vector ParentSchemas() const; //! Whether this is a nested schema (i.e. it has at least one parent schema) bool IsNested() const; + //! On an existing schema, throw for ERROR, return false for IGNORE, or true for REPLACE. + DUCKDB_API bool ShouldReplaceOnConflict() const; public: DUCKDB_API void Serialize(Serializer &serializer) const override; diff --git a/src/duckdb/src/include/duckdb/parser/parsed_data/create_table_info.hpp b/src/duckdb/src/include/duckdb/parser/parsed_data/create_table_info.hpp index 329c0b7d7..da35eed82 100644 --- a/src/duckdb/src/include/duckdb/parser/parsed_data/create_table_info.hpp +++ b/src/duckdb/src/include/duckdb/parser/parsed_data/create_table_info.hpp @@ -14,6 +14,7 @@ #include "duckdb/parser/column_list.hpp" #include "duckdb/common/identifier.hpp" +#include "duckdb/common/optional_idx.hpp" namespace duckdb { class SchemaCatalogEntry; @@ -44,6 +45,7 @@ struct CreateTableInfo : public CreateInfo { public: DUCKDB_API unique_ptr Copy() const override; + DUCKDB_API optional_idx FindNotNullConstraint(LogicalIndex column) const; DUCKDB_API void Serialize(Serializer &serializer) const override; DUCKDB_API static unique_ptr Deserialize(Deserializer &deserializer); diff --git a/src/duckdb/src/include/duckdb/parser/parsed_data/create_view_info.hpp b/src/duckdb/src/include/duckdb/parser/parsed_data/create_view_info.hpp index d3006d171..6cccd1509 100644 --- a/src/duckdb/src/include/duckdb/parser/parsed_data/create_view_info.hpp +++ b/src/duckdb/src/include/duckdb/parser/parsed_data/create_view_info.hpp @@ -14,6 +14,7 @@ #include "duckdb/common/enums/view_security_type.hpp" #include "duckdb/common/identifier.hpp" namespace duckdb { +class Parser; class SchemaCatalogEntry; enum class CreateViewBindingMode { BIND_ON_CREATE, SKIP_BINDING }; @@ -51,12 +52,12 @@ struct CreateViewInfo : public CreateInfo { unique_ptr Copy() const override; //! Gets a bound CreateViewInfo object from a SELECT statement and a view name, schema name, etc - DUCKDB_API static unique_ptr FromSelect(ClientContext &context, unique_ptr info); + DUCKDB_API static unique_ptr FromSelect(Parser &parser, unique_ptr info); //! Gets a bound CreateViewInfo object from a CREATE VIEW statement DUCKDB_API static unique_ptr FromCreateView(ClientContext &context, SchemaCatalogEntry &schema, const string &sql); //! Parse a SELECT statement from a SQL string - DUCKDB_API static unique_ptr ParseSelect(const string &sql); + DUCKDB_API static unique_ptr ParseSelect(Parser &parser, const string &sql); DUCKDB_API void Serialize(Serializer &serializer) const override; DUCKDB_API static unique_ptr Deserialize(Deserializer &deserializer); diff --git a/src/duckdb/src/include/duckdb/parser/parsed_data/external_resource_options.hpp b/src/duckdb/src/include/duckdb/parser/parsed_data/external_resource_options.hpp new file mode 100644 index 000000000..7a7b7cdae --- /dev/null +++ b/src/duckdb/src/include/duckdb/parser/parsed_data/external_resource_options.hpp @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/parser/parsed_data/external_resource_options.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/case_insensitive_map.hpp" +#include "duckdb/common/identifier.hpp" +#include "duckdb/common/types/value.hpp" +#include "duckdb/common/unordered_map.hpp" +#include "duckdb/parser/parsed_expression.hpp" + +namespace duckdb { +class Serializer; +class Deserializer; + +//! The `[NEW TEMPORARY] EXTERNAL RESOURCE '' [(params)]` clause, owned by AttachInfo/ConnectInfo. +//! provider is its own field rather than a magic key in params: the TYPE is the registry key, not a param. +struct ExternalResourceOptions { + //! Create params (key -> expression); transient — consumed at bind, then `params` holds them. + case_insensitive_map_t> parsed_params; + //! The resource type (provider) — the registered external-resource-type name to provision. A string + //! literal, so it is fixed at parse time. + string provider; + //! Bound create params forwarded to the type's create function as a MAP(VARCHAR, VARCHAR). + unordered_map params; + //! Reference form: a bare identifier naming a registered resource to BORROW rather than provision, + //! so no teardown is owned. Mutually exclusive with `provider`. + string reference_name; + + unique_ptr Copy() const; + //! Renders `NEW TEMPORARY EXTERNAL RESOURCE '' [(k v, ...)]`, or the reference form. + string ToString() const; + + void Serialize(Serializer &serializer) const; + static unique_ptr Deserialize(Deserializer &deserializer); +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/parsed_data/parse_info.hpp b/src/duckdb/src/include/duckdb/parser/parsed_data/parse_info.hpp index 9b32d2dc0..ede19422b 100644 --- a/src/duckdb/src/include/duckdb/parser/parsed_data/parse_info.hpp +++ b/src/duckdb/src/include/duckdb/parser/parsed_data/parse_info.hpp @@ -10,9 +10,19 @@ #include "duckdb/common/common.hpp" #include "duckdb/common/enums/catalog_type.hpp" +#include "duckdb/common/case_insensitive_map.hpp" #include "duckdb/common/identifier.hpp" +#include "duckdb/common/types/value.hpp" +#include "duckdb/common/unordered_map.hpp" namespace duckdb { +class ParsedExpression; +class Value; + +//! Render a statement's `(k v, ...)` option list, quoting each name, from the unbound and bound maps +//! alike. Empty string when there is nothing to render, so callers can append it unconditionally. +string RenderOptionList(const case_insensitive_map_t> &parsed_options, + const unordered_map &options); enum class CatalogType : uint8_t; diff --git a/src/duckdb/src/include/duckdb/parser/parsed_expression.hpp b/src/duckdb/src/include/duckdb/parser/parsed_expression.hpp index 3398ab01b..d5af41829 100644 --- a/src/duckdb/src/include/duckdb/parser/parsed_expression.hpp +++ b/src/duckdb/src/include/duckdb/parser/parsed_expression.hpp @@ -195,6 +195,9 @@ class ParsedExpression : public BaseExpression { virtual void Serialize(Serializer &serializer) const; static unique_ptr Deserialize(Deserializer &deserializer); + //! Throws a SerializationException if a deserialized expression does not have a valid shape + //! Only this expression is verified - its children are verified when they are deserialized + void VerifyDeserialized(); //! Copies the base class properties (type, expression_class, alias, query_location) from other into this void CopyBase(const ParsedExpression &other); diff --git a/src/duckdb/src/include/duckdb/parser/parser.hpp b/src/duckdb/src/include/duckdb/parser/parser.hpp index 4c410f791..ba5202c9a 100644 --- a/src/duckdb/src/include/duckdb/parser/parser.hpp +++ b/src/duckdb/src/include/duckdb/parser/parser.hpp @@ -19,6 +19,7 @@ namespace duckdb { +class ClientContext; struct CompiledGrammar; struct MatcherToken; class TokenIterator; @@ -36,7 +37,15 @@ struct UnicodeSpace { //! plan and executed. class Parser { public: - explicit Parser(const ParserOptions &options = ParserOptions()); + //! Snapshot the connection's parser settings, extensions and cached grammar. + explicit Parser(ClientContext &context); + //! Preserve identifier spelling when reparsing generated SQL. + explicit Parser(ClientContext &context, IdentifierCaseMode identifier_case_mode); + //! Explicit standalone configuration, without a client context. + explicit Parser(const ParserOptions &options); + Parser(Parser &&other) noexcept; + //! Built-in semantics and shared built-in grammar, independent of any connection. + static Parser GetBuiltinParser(); ~Parser(); //! The parsed SQL statements from an invocation to ParseQuery. @@ -78,24 +87,23 @@ class Parser { // Returns the Keyword category static KeywordCategory ToKeywordCategory(const string &text); //! Parses a list of expressions (i.e. the list found in a SELECT clause) - DUCKDB_API static vector> - ParseExpressionList(const string &select_list, const ParserOptions &options = ParserOptions()); + DUCKDB_API vector> ParseExpressionList(const string &select_list); + //! Parses exactly one expression, throwing an InternalException otherwise + DUCKDB_API unique_ptr ParseSingleExpression(const string &expression); + //! Parses a single SELECT statement into its node + DUCKDB_API unique_ptr ParseSelectNode(const string &query); //! Parses a list of GROUP BY expressions - static GroupByNode ParseGroupByList(const string &group_by, const ParserOptions &options = ParserOptions()); + GroupByNode ParseGroupByList(const string &group_by); //! Parses a list as found in an ORDER BY expression (i.e. including optional ASCENDING/DESCENDING modifiers) - static vector ParseOrderList(const string &select_list, - const ParserOptions &options = ParserOptions()); + vector ParseOrderList(const string &select_list); //! Parses an update list (i.e. the list found in the SET clause of an UPDATE statement) - static void ParseUpdateList(const string &update_list, vector &update_columns, - vector> &expressions, - const ParserOptions &options = ParserOptions()); + void ParseUpdateList(const string &update_list, vector &update_columns, + vector> &expressions); //! Parses a VALUES list (i.e. the list of expressions after a VALUES clause) - static vector>> ParseValuesList(const string &value_list, - const ParserOptions &options = ParserOptions()); + vector>> ParseValuesList(const string &value_list); //! Parses a column list (i.e. as found in a CREATE TABLE statement) - static ColumnList ParseColumnList(const string &column_list, const ParserOptions &options = ParserOptions()); - static ColumnDefinition ParseColumnDefinition(const string &column_definition, - const ParserOptions &options = ParserOptions()); + ColumnList ParseColumnList(const string &column_list); + ColumnDefinition ParseColumnDefinition(const string &column_definition); static bool StripUnicodeSpaces(const string &query_str, string &new_query); diff --git a/src/duckdb/src/include/duckdb/parser/parser_options.hpp b/src/duckdb/src/include/duckdb/parser/parser_options.hpp index 467880ae0..c36fc63fc 100644 --- a/src/duckdb/src/include/duckdb/parser/parser_options.hpp +++ b/src/duckdb/src/include/duckdb/parser/parser_options.hpp @@ -15,14 +15,24 @@ #include "duckdb/common/optional_ptr.hpp" namespace duckdb { +class ClientContext; class ExtensionCallbackManager; class ParserExtension; struct CompiledGrammar; struct ParserOptions { +private: + ParserOptions() { + } + friend class ClientContext; + +public: + //! Explicit configuration for parsing without a client context. + DUCKDB_API static ParserOptions Builtin(); + +public: IdentifierCaseMode identifier_case_mode = IdentifierCaseMode::PRESERVE_CASE; bool integer_division = false; - bool heap_based_parser = true; RegexMatchOperatorSemantics regex_match_operator_semantics = RegexMatchOperatorSemantics::PARTIAL; idx_t max_expression_depth = 1000; optional_ptr extensions; diff --git a/src/duckdb/src/include/duckdb/parser/peg/ast/add_column_entry.hpp b/src/duckdb/src/include/duckdb/parser/peg/ast/add_column_entry.hpp index 1b89bc4a1..0f819849e 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/ast/add_column_entry.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/ast/add_column_entry.hpp @@ -3,6 +3,7 @@ #include "duckdb/common/types/value.hpp" #include "duckdb/parser/column_definition.hpp" #include "duckdb/parser/constraint.hpp" +#include "duckdb/parser/parsed_data/alter_table_info.hpp" #include "duckdb/common/identifier.hpp" namespace duckdb { @@ -11,7 +12,9 @@ struct AddColumnEntry { LogicalType type; vector column_path; unique_ptr default_value; - bool is_not_null = false; + //! Constraints applied via extra ALTER statements after the column is added + AddColumnConstraints add_column_constraints; + CompressionType compression_type = CompressionType::COMPRESSION_AUTO; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/ast/expression_chain.hpp b/src/duckdb/src/include/duckdb/parser/peg/ast/expression_chain.hpp index db766b8c8..d1d76b01e 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/ast/expression_chain.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/ast/expression_chain.hpp @@ -31,6 +31,16 @@ struct OtherOperatorTail { unique_ptr expression; }; +enum class ExpressionTailType : uint8_t { IS_TEST, DISTINCT, COMPARISON, OTHER_OPERATOR }; + +struct IsExpressionTail { + ExpressionTailType type = ExpressionTailType::IS_TEST; + unique_ptr test; + IsDistinctFromTail distinct; + ComparisonExpressionTail comparison; + OtherOperatorTail other; +}; + struct BinaryExpressionTail { string op; unique_ptr expression; diff --git a/src/duckdb/src/include/duckdb/parser/peg/autocomplete_catalog_provider.hpp b/src/duckdb/src/include/duckdb/parser/peg/autocomplete_catalog_provider.hpp index 6581fcc85..bf99fb89e 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/autocomplete_catalog_provider.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/autocomplete_catalog_provider.hpp @@ -18,7 +18,6 @@ struct AutoCompleteParameters { idx_t max_file_suggestion_count = 1; idx_t max_exact_suggestion_count = 100; bool suggestion_contains_files = false; - bool use_heap_based_parser = false; }; //! Abstract interface for providing catalog-aware autocomplete suggestions. diff --git a/src/duckdb/src/include/duckdb/parser/peg/compiled_grammar.hpp b/src/duckdb/src/include/duckdb/parser/peg/compiled_grammar.hpp index a637d50ed..f8a55ed8f 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/compiled_grammar.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/compiled_grammar.hpp @@ -1,5 +1,6 @@ #pragma once +#include "duckdb/common/pair.hpp" #include "duckdb/parser/peg/matcher.hpp" #include "duckdb/parser/peg/transformer/peg_transformer.hpp" #include "duckdb/parser/peg/parsed_grammar.hpp" @@ -7,62 +8,75 @@ namespace duckdb { class ClientContext; +class DialectExtension; class GrammarExtension; +class PassthroughDialect; + +using compiled_rules_map_t = case_insensitive_map_t>; struct CompiledGrammar { -private: - CompiledGrammar(const ParsedGrammar &grammar, bool has_grammar_changes); - static shared_ptr - Create(const case_insensitive_map_t> &grammar_extensions); +public: + CompiledGrammar(MatcherAllocator &&allocator, unique_ptr &&keyword_helper, + unique_ptr &&tokenizer, compiled_rules_map_t &&rules, const Matcher &program_matcher, + const Matcher &top_level_statement_matcher); + static shared_ptr Create(const vector> &grammar_extensions); public: const Matcher &ProgramMatcher() const { - return *program_matcher; + return program_matcher; } const Matcher &TopLevelStatementMatcher() const { - return *top_level_statement_matcher; + return top_level_statement_matcher; } const PEGKeywordHelper &GetKeywordHelper() const { - return keyword_helper; + return *keyword_helper; } const Tokenizer &GetTokenizer() const { - return tokenizer; + return *tokenizer; } optional_ptr GetRule(const string &rule_name) const; - bool HasGrammarChanges() const { - return has_grammar_changes; - } public: static shared_ptr Get(ClientContext &context); + //! Get the shared, lazily compiled base DuckDB grammar. + static shared_ptr DefaultGrammar(); //! Compile the base DuckDB grammar. static shared_ptr Create(); - //! Compile a grammar for the selected extensions without changing the client configuration. - static shared_ptr Create(const ClientContext &context, - const case_insensitive_set_t &active_extensions); + //! Get the (cached) grammar for the selected extensions without changing the client configuration. + static shared_ptr Create(const ClientContext &context, const vector &active_extensions); private: MatcherAllocator allocator; - optional_ptr program_matcher; - optional_ptr top_level_statement_matcher; - - unique_ptr owned_keyword_helper; - const PEGKeywordHelper &keyword_helper; - Tokenizer tokenizer; + unique_ptr keyword_helper; + unique_ptr tokenizer; case_insensitive_map_t> rules; - -private: - const bool has_grammar_changes; + const Matcher &program_matcher; + const Matcher &top_level_statement_matcher; }; -//! Per-database holder for the compiled base grammar. +//! Per-database holder for the compiled base grammar and the grammars compiled for active grammar extensions. struct ParserCache { public: + ParserCache(); + ~ParserCache(); + + //! The maximum number of grammars compiled for grammar extensions that are kept + static constexpr idx_t MAX_CACHED_EXTENSION_MATCHERS = 16; + shared_ptr GetMatcher(); + //! The grammar compiled for the given grammar extensions, in order - shared by all connections that use them + shared_ptr GetMatcher(const ClientContext &context, const vector &active_extensions); + //! The grammar that forwards statements instead of interpreting them, used while CONNECT-ed + shared_ptr GetPassthroughMatcher(const ClientContext &context); private: std::mutex mutex; shared_ptr matcher; + std::mutex extension_mutex; + //! The compiled grammars with the names of their grammar extensions in order, from least to most recently used + vector, shared_ptr>> extension_matchers; + std::mutex passthrough_mutex; + unique_ptr passthrough_dialect; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/dialect_extension.hpp b/src/duckdb/src/include/duckdb/parser/peg/dialect_extension.hpp new file mode 100644 index 000000000..ceb6c6b90 --- /dev/null +++ b/src/duckdb/src/include/duckdb/parser/peg/dialect_extension.hpp @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/parser/peg/dialect_extension.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/common.hpp" +#include "duckdb/common/mutex.hpp" +#include "duckdb/common/optional.hpp" +#include "duckdb/common/case_insensitive_map.hpp" +#include "duckdb/common/enums/dialect_compatibility_mode.hpp" +#include "duckdb/main/client_context.hpp" + +namespace duckdb { +struct DBConfig; +class MatcherFactory; +class Tokenizer; +class PEGKeywordHelper; +class MatcherAllocator; +class ParsedGrammar; +struct CompiledGrammar; +struct CompiledGrammarRule; +class Matcher; + +using compiled_rules_map_t = case_insensitive_map_t>; + +using terminal_rule_overrides_t = case_insensitive_map_t>; + +struct CreateTokenizerInput { + const PEGKeywordHelper &keyword_helper; +}; + +struct CreateMatcherFactoryInput { + MatcherAllocator &allocator; + const ParsedGrammar &parsed_grammar; + const compiled_rules_map_t &rules; + const PEGKeywordHelper &keyword_helper; + terminal_rule_overrides_t terminal_rules; +}; + +struct CreateKeywordHelperInput { + const ParsedGrammar &grammar; +}; + +struct GrammarChangesInput { + ParsedGrammar &parsed_grammar; + const ClientContext &context; +}; + +//! A named SQL dialect that can customize the PEG parser. +class DialectExtension { +public: + explicit DialectExtension(string name_p, optional compatibility_mode = std::nullopt) + : name(std::move(name_p)), compatibility_mode(compatibility_mode) { + } + virtual ~DialectExtension() = default; + +public: + static void Register(DBConfig &config, shared_ptr extension); + +public: + shared_ptr GetCompiledGrammar(const ClientContext &context); + const string &Name() const; + const optional &GetCompatibilityMode() const; + +public: + virtual void ApplyGrammarChanges(GrammarChangesInput &input); + virtual unique_ptr CreateMatcherFactory(CreateMatcherFactoryInput &input); + virtual unique_ptr CreateTokenizer(CreateTokenizerInput &input); + virtual unique_ptr CreateKeywordHelper(CreateKeywordHelperInput &input); + +private: + //! The name to reference this dialect with the 'current_dialect' setting. + string name; + //! If set, the name of the 'dialect_compatibility_mode' setting to use upon selection. + optional compatibility_mode; + mutex lock; + //! Cache the compiled grammar, since it never needs to be invalidated + shared_ptr cache; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/grammar_literal_table.hpp b/src/duckdb/src/include/duckdb/parser/peg/grammar_literal_table.hpp new file mode 100644 index 000000000..71989138d --- /dev/null +++ b/src/duckdb/src/include/duckdb/parser/peg/grammar_literal_table.hpp @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/parser/peg/grammar_literal_table.hpp +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/case_insensitive_map.hpp" +#include "duckdb/parser/peg/literal_info.hpp" + +namespace duckdb { + +class ParsedGrammar; + +//! Immutable after construction, including literals only present in keyword-category rules. +class GrammarLiteralTable { +public: + DUCKDB_API GrammarLiteralTable(const ParsedGrammar &grammar, const case_insensitive_map_t &keywords); + GrammarLiteralTable(const GrammarLiteralTable &) = delete; + GrammarLiteralTable &operator=(const GrammarLiteralTable &) = delete; + + uint64_t CacheId() const { + return cache_id; + } + + LiteralInfo Lookup(const string &text) const { + auto entry = literals.find(text); + return entry == literals.end() ? LiteralInfo() : entry->second; + } + +private: + void Register(const string &text, keyword_categories_t categories = keyword_categories_t()); + +private: + const uint64_t cache_id; + case_insensitive_map_t literals; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/inlined_grammar.hpp b/src/duckdb/src/include/duckdb/parser/peg/inlined_grammar.hpp index 55c98d992..a0dc651e1 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/inlined_grammar.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/inlined_grammar.hpp @@ -3,3564 +3,2287 @@ namespace duckdb { +// clang-format off const unsigned char INLINED_PEG_GRAMMAR[] = { - 80, 114, 111, 103, 114, 97, 109, 32, 60, 45, 32, 84, 111, 112, 76, 101, 118, 101, 108, 83, - 116, 97, 116, 101, 109, 101, 110, 116, 42, 10, 84, 111, 112, 76, 101, 118, 101, 108, 83, 116, - 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 83, 116, 97, 116, 101, 109, 101, 110, 116, - 63, 32, 40, 39, 59, 39, 43, 32, 47, 32, 69, 110, 100, 79, 102, 73, 110, 112, 117, 116, - 41, 10, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 10, 9, 69, 120, 116, 101, - 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 83, 116, 97, 116, 101, 109, 101, 110, - 116, 32, 47, 10, 9, 69, 120, 116, 101, 110, 115, 105, 111, 110, 82, 101, 112, 111, 115, 105, - 116, 111, 114, 121, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 67, 114, 101, - 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 83, 101, 108, 101, - 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 83, 101, 116, 83, 116, - 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 80, 114, 97, 103, 109, 97, 83, 116, 97, - 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 67, 97, 108, 108, 83, 116, 97, 116, 101, 109, - 101, 110, 116, 32, 47, 10, 9, 73, 110, 115, 101, 114, 116, 83, 116, 97, 116, 101, 109, 101, - 110, 116, 32, 47, 10, 9, 68, 114, 111, 112, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, - 47, 10, 9, 67, 111, 112, 121, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, - 69, 120, 112, 108, 97, 105, 110, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, - 85, 112, 100, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 80, - 114, 101, 112, 97, 114, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 69, - 120, 101, 99, 117, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 65, - 108, 116, 101, 114, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 84, 114, 97, - 110, 115, 97, 99, 116, 105, 111, 110, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, - 9, 68, 101, 108, 101, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, - 65, 116, 116, 97, 99, 104, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 85, - 115, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 68, 101, 116, 97, 99, - 104, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 67, 104, 101, 99, 107, 112, - 111, 105, 110, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 86, 97, 99, - 117, 117, 109, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 82, 101, 115, 101, - 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 69, 120, 112, 111, 114, 116, - 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 73, 109, 112, 111, 114, 116, 83, - 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 67, 111, 109, 109, 101, 110, 116, 83, - 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 68, 101, 97, 108, 108, 111, 99, 97, - 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 84, 114, 117, 110, 99, - 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 76, 111, 97, 100, - 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 73, 110, 115, 116, 97, 108, 108, - 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 9, 85, 112, 100, 97, 116, 101, 69, - 120, 116, 101, 110, 115, 105, 111, 110, 115, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, - 10, 9, 65, 110, 97, 108, 121, 122, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, - 10, 9, 77, 101, 114, 103, 101, 73, 110, 116, 111, 83, 116, 97, 116, 101, 109, 101, 110, 116, - 32, 47, 10, 9, 67, 111, 110, 110, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, - 32, 47, 10, 9, 68, 105, 115, 99, 111, 110, 110, 101, 99, 116, 83, 116, 97, 116, 101, 109, - 101, 110, 116, 32, 47, 10, 9, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 83, 116, 97, - 116, 101, 109, 101, 110, 116, 10, 65, 110, 97, 108, 121, 122, 101, 75, 101, 121, 119, 111, 114, - 100, 32, 60, 45, 32, 39, 65, 78, 65, 76, 89, 90, 69, 39, 32, 47, 32, 39, 65, 78, - 65, 76, 89, 83, 69, 39, 10, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 83, 116, 97, - 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 65, 108, 105, 97, 115, 41, 10, 69, 120, 112, 114, 101, 115, 115, 105, - 111, 110, 65, 108, 105, 97, 115, 32, 60, 45, 32, 67, 111, 108, 73, 100, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 32, 47, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 65, - 115, 67, 111, 108, 108, 97, 98, 101, 108, 32, 47, 32, 69, 120, 112, 114, 101, 115, 115, 105, - 111, 110, 10, 67, 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, - 101, 110, 116, 105, 102, 105, 101, 114, 10, 83, 99, 104, 101, 109, 97, 78, 97, 109, 101, 32, - 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 82, 101, 115, 101, 114, 118, - 101, 100, 83, 99, 104, 101, 109, 97, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, - 116, 105, 102, 105, 101, 114, 10, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 60, 45, 32, - 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 82, 101, 115, 101, 114, 118, 101, 100, 84, - 97, 98, 108, 101, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, - 101, 114, 10, 82, 101, 115, 101, 114, 118, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, - 114, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 67, 111, 108, 117, - 109, 110, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, - 10, 82, 101, 115, 101, 114, 118, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, - 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 73, 110, 100, 101, 120, 78, - 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 82, 101, - 115, 101, 114, 118, 101, 100, 73, 110, 100, 101, 120, 78, 97, 109, 101, 32, 60, 45, 32, 73, - 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 83, 101, 116, 116, 105, 110, 103, 78, 97, 109, - 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 80, 114, 97, 103, - 109, 97, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, - 10, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, - 110, 116, 105, 102, 105, 101, 114, 10, 82, 101, 115, 101, 114, 118, 101, 100, 70, 117, 110, 99, - 116, 105, 111, 110, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, - 101, 114, 10, 82, 101, 115, 101, 114, 118, 101, 100, 84, 121, 112, 101, 78, 97, 109, 101, 32, - 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 84, 97, 98, 108, 101, 70, - 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, - 105, 102, 105, 101, 114, 10, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 78, 97, 109, 101, - 32, 60, 45, 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 10, 83, 101, - 113, 117, 101, 110, 99, 101, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, - 102, 105, 101, 114, 10, 67, 111, 108, 108, 97, 116, 105, 111, 110, 78, 97, 109, 101, 32, 60, - 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 67, 111, 112, 121, 79, 112, 116, - 105, 111, 110, 78, 97, 109, 101, 32, 60, 45, 32, 67, 111, 108, 76, 97, 98, 101, 108, 10, - 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 60, 32, 91, - 43, 45, 93, 63, 91, 48, 45, 57, 93, 42, 40, 91, 46, 93, 91, 48, 45, 57, 93, 42, - 41, 63, 32, 62, 10, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 60, - 45, 32, 39, 92, 39, 39, 32, 91, 94, 92, 39, 93, 42, 32, 39, 92, 39, 39, 10, 84, - 121, 112, 101, 32, 60, 45, 32, 84, 121, 112, 101, 86, 97, 114, 105, 97, 116, 105, 111, 110, - 115, 32, 65, 114, 114, 97, 121, 66, 111, 117, 110, 100, 115, 42, 10, 84, 121, 112, 101, 86, - 97, 114, 105, 97, 116, 105, 111, 110, 115, 32, 60, 45, 32, 84, 105, 109, 101, 84, 121, 112, - 101, 32, 47, 32, 73, 110, 116, 101, 114, 118, 97, 108, 84, 121, 112, 101, 32, 47, 32, 66, - 105, 116, 84, 121, 112, 101, 32, 47, 32, 82, 111, 119, 84, 121, 112, 101, 32, 47, 32, 86, - 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 32, 47, 32, 77, 97, 112, 84, 121, 112, 101, - 32, 47, 32, 84, 117, 112, 108, 101, 84, 121, 112, 101, 32, 47, 32, 71, 101, 111, 109, 101, - 116, 114, 121, 84, 121, 112, 101, 32, 47, 32, 85, 110, 105, 111, 110, 84, 121, 112, 101, 32, - 47, 32, 78, 117, 109, 101, 114, 105, 99, 84, 121, 112, 101, 32, 47, 32, 83, 101, 116, 111, - 102, 84, 121, 112, 101, 32, 47, 32, 83, 105, 109, 112, 108, 101, 84, 121, 112, 101, 10, 83, - 105, 109, 112, 108, 101, 84, 121, 112, 101, 32, 60, 45, 32, 67, 104, 97, 114, 97, 99, 116, - 101, 114, 83, 105, 109, 112, 108, 101, 84, 121, 112, 101, 32, 47, 32, 81, 117, 97, 108, 105, - 102, 105, 101, 100, 83, 105, 109, 112, 108, 101, 84, 121, 112, 101, 10, 67, 104, 97, 114, 97, - 99, 116, 101, 114, 83, 105, 109, 112, 108, 101, 84, 121, 112, 101, 32, 60, 45, 32, 67, 104, - 97, 114, 97, 99, 116, 101, 114, 84, 121, 112, 101, 32, 84, 121, 112, 101, 77, 111, 100, 105, - 102, 105, 101, 114, 115, 63, 10, 81, 117, 97, 108, 105, 102, 105, 101, 100, 83, 105, 109, 112, - 108, 101, 84, 121, 112, 101, 32, 60, 45, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, - 121, 112, 101, 78, 97, 109, 101, 32, 84, 121, 112, 101, 77, 111, 100, 105, 102, 105, 101, 114, - 115, 63, 10, 67, 104, 97, 114, 97, 99, 116, 101, 114, 84, 121, 112, 101, 32, 60, 45, 32, - 40, 39, 67, 72, 65, 82, 65, 67, 84, 69, 82, 39, 32, 39, 86, 65, 82, 89, 73, 78, - 71, 39, 63, 41, 32, 47, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 40, 39, 67, 72, 65, 82, 39, 32, 39, 86, 65, 82, 89, 73, 78, 71, - 39, 63, 41, 32, 47, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 40, 39, 78, 65, 84, 73, 79, 78, 65, 76, 39, 32, 39, 67, 72, 65, 82, - 65, 67, 84, 69, 82, 39, 32, 39, 86, 65, 82, 89, 73, 78, 71, 39, 63, 41, 32, 47, - 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 40, 39, - 78, 65, 84, 73, 79, 78, 65, 76, 39, 32, 39, 67, 72, 65, 82, 39, 32, 39, 86, 65, - 82, 89, 73, 78, 71, 39, 63, 41, 32, 47, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 40, 39, 78, 67, 72, 65, 82, 39, 32, 39, 86, 65, - 82, 89, 73, 78, 71, 39, 63, 41, 32, 47, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 39, 86, 65, 82, 67, 72, 65, 82, 39, 10, 73, 110, - 116, 101, 114, 118, 97, 108, 84, 121, 112, 101, 32, 60, 45, 32, 73, 110, 116, 101, 114, 118, - 97, 108, 73, 110, 116, 101, 114, 118, 97, 108, 32, 47, 32, 73, 110, 116, 101, 114, 118, 97, - 108, 78, 117, 109, 98, 101, 114, 10, 73, 110, 116, 101, 114, 118, 97, 108, 73, 110, 116, 101, - 114, 118, 97, 108, 32, 60, 45, 32, 73, 110, 116, 101, 114, 118, 97, 108, 87, 105, 116, 104, - 83, 112, 101, 99, 105, 102, 105, 101, 114, 32, 47, 32, 73, 110, 116, 101, 114, 118, 97, 108, - 87, 105, 116, 104, 111, 117, 116, 83, 112, 101, 99, 105, 102, 105, 101, 114, 10, 73, 110, 116, - 101, 114, 118, 97, 108, 87, 105, 116, 104, 83, 112, 101, 99, 105, 102, 105, 101, 114, 32, 60, - 45, 32, 73, 110, 116, 101, 114, 118, 97, 108, 87, 105, 116, 104, 82, 97, 110, 103, 101, 83, - 112, 101, 99, 105, 102, 105, 101, 114, 32, 47, 32, 73, 110, 116, 101, 114, 118, 97, 108, 87, - 105, 116, 104, 83, 105, 109, 112, 108, 101, 83, 112, 101, 99, 105, 102, 105, 101, 114, 10, 73, - 110, 116, 101, 114, 118, 97, 108, 87, 105, 116, 104, 82, 97, 110, 103, 101, 83, 112, 101, 99, - 105, 102, 105, 101, 114, 32, 60, 45, 32, 39, 73, 78, 84, 69, 82, 86, 65, 76, 39, 32, - 73, 110, 116, 101, 114, 118, 97, 108, 84, 111, 73, 110, 116, 101, 114, 118, 97, 108, 65, 115, - 84, 121, 112, 101, 10, 73, 110, 116, 101, 114, 118, 97, 108, 87, 105, 116, 104, 83, 105, 109, - 112, 108, 101, 83, 112, 101, 99, 105, 102, 105, 101, 114, 32, 60, 45, 32, 39, 73, 78, 84, - 69, 82, 86, 65, 76, 39, 32, 73, 110, 116, 101, 114, 118, 97, 108, 10, 73, 110, 116, 101, - 114, 118, 97, 108, 87, 105, 116, 104, 111, 117, 116, 83, 112, 101, 99, 105, 102, 105, 101, 114, - 32, 60, 45, 32, 39, 73, 78, 84, 69, 82, 86, 65, 76, 39, 10, 73, 110, 116, 101, 114, - 118, 97, 108, 84, 111, 73, 110, 116, 101, 114, 118, 97, 108, 65, 115, 84, 121, 112, 101, 32, - 60, 45, 32, 40, 89, 101, 97, 114, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, - 32, 77, 111, 110, 116, 104, 75, 101, 121, 119, 111, 114, 100, 41, 32, 47, 10, 32, 32, 32, - 32, 40, 68, 97, 121, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 72, 111, - 117, 114, 75, 101, 121, 119, 111, 114, 100, 41, 32, 47, 10, 32, 32, 32, 32, 40, 68, 97, - 121, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 77, 105, 110, 117, 116, 101, - 75, 101, 121, 119, 111, 114, 100, 41, 32, 47, 10, 32, 32, 32, 32, 40, 68, 97, 121, 75, - 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 83, 101, 99, 111, 110, 100, 75, 101, - 121, 119, 111, 114, 100, 41, 32, 47, 10, 32, 32, 32, 32, 40, 72, 111, 117, 114, 75, 101, - 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 77, 105, 110, 117, 116, 101, 75, 101, 121, - 119, 111, 114, 100, 41, 32, 47, 10, 32, 32, 32, 32, 40, 72, 111, 117, 114, 75, 101, 121, - 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 83, 101, 99, 111, 110, 100, 75, 101, 121, 119, - 111, 114, 100, 41, 32, 47, 10, 32, 32, 32, 32, 40, 77, 105, 110, 117, 116, 101, 75, 101, - 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 83, 101, 99, 111, 110, 100, 75, 101, 121, - 119, 111, 114, 100, 41, 10, 73, 110, 116, 101, 114, 118, 97, 108, 78, 117, 109, 98, 101, 114, - 32, 60, 45, 32, 39, 73, 78, 84, 69, 82, 86, 65, 76, 39, 32, 80, 97, 114, 101, 110, - 115, 40, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 41, 10, 89, 101, 97, - 114, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 45, 32, - 39, 89, 69, 65, 82, 39, 32, 47, 32, 39, 89, 69, 65, 82, 83, 39, 10, 77, 111, 110, - 116, 104, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, 32, 32, 60, 45, 32, - 39, 77, 79, 78, 84, 72, 39, 32, 47, 32, 39, 77, 79, 78, 84, 72, 83, 39, 10, 68, - 97, 121, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, - 45, 32, 39, 68, 65, 89, 39, 32, 47, 32, 39, 68, 65, 89, 83, 39, 10, 72, 111, 117, - 114, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 45, 32, - 39, 72, 79, 85, 82, 39, 32, 47, 32, 39, 72, 79, 85, 82, 83, 39, 10, 77, 105, 110, - 117, 116, 101, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, 32, 60, 45, 32, - 39, 77, 73, 78, 85, 84, 69, 39, 32, 47, 32, 39, 77, 73, 78, 85, 84, 69, 83, 39, - 10, 83, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, - 32, 60, 45, 32, 39, 83, 69, 67, 79, 78, 68, 39, 32, 47, 32, 39, 83, 69, 67, 79, - 78, 68, 83, 39, 10, 77, 105, 108, 108, 105, 115, 101, 99, 111, 110, 100, 75, 101, 121, 119, - 111, 114, 100, 32, 32, 60, 45, 32, 39, 77, 73, 76, 76, 73, 83, 69, 67, 79, 78, 68, - 39, 32, 47, 32, 39, 77, 73, 76, 76, 73, 83, 69, 67, 79, 78, 68, 83, 39, 10, 77, - 105, 99, 114, 111, 115, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 32, 32, 60, - 45, 32, 39, 77, 73, 67, 82, 79, 83, 69, 67, 79, 78, 68, 39, 32, 47, 32, 39, 77, - 73, 67, 82, 79, 83, 69, 67, 79, 78, 68, 83, 39, 10, 87, 101, 101, 107, 75, 101, 121, - 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 45, 32, 39, 87, 69, 69, - 75, 39, 32, 47, 32, 39, 87, 69, 69, 75, 83, 39, 10, 81, 117, 97, 114, 116, 101, 114, - 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, 60, 45, 32, 39, 81, 85, 65, - 82, 84, 69, 82, 39, 32, 47, 32, 39, 81, 85, 65, 82, 84, 69, 82, 83, 39, 10, 68, - 101, 99, 97, 100, 101, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, 32, 60, - 45, 32, 39, 68, 69, 67, 65, 68, 69, 39, 32, 47, 32, 39, 68, 69, 67, 65, 68, 69, - 83, 39, 10, 67, 101, 110, 116, 117, 114, 121, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, - 32, 32, 32, 60, 45, 32, 39, 67, 69, 78, 84, 85, 82, 89, 39, 32, 47, 32, 39, 67, - 69, 78, 84, 85, 82, 73, 69, 83, 39, 10, 77, 105, 108, 108, 101, 110, 110, 105, 117, 109, - 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 60, 45, 32, 39, 77, 73, 76, 76, 69, 78, - 78, 73, 85, 77, 39, 32, 47, 32, 39, 77, 73, 76, 76, 69, 78, 78, 73, 65, 39, 10, - 73, 110, 116, 101, 114, 118, 97, 108, 32, 60, 45, 32, 73, 110, 116, 101, 114, 118, 97, 108, - 84, 111, 73, 110, 116, 101, 114, 118, 97, 108, 32, 47, 10, 32, 32, 32, 32, 89, 101, 97, - 114, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, 32, 32, 32, 32, 77, 111, 110, 116, 104, - 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, 32, 32, 32, 32, 68, 97, 121, 75, 101, 121, - 119, 111, 114, 100, 32, 47, 10, 32, 32, 32, 32, 72, 111, 117, 114, 75, 101, 121, 119, 111, - 114, 100, 32, 47, 10, 32, 32, 32, 32, 77, 105, 110, 117, 116, 101, 75, 101, 121, 119, 111, - 114, 100, 32, 47, 10, 32, 32, 32, 32, 83, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, - 114, 100, 32, 47, 10, 32, 32, 32, 32, 77, 105, 108, 108, 105, 115, 101, 99, 111, 110, 100, - 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, 32, 32, 32, 32, 77, 105, 99, 114, 111, 115, - 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, 32, 32, 32, 32, 87, - 101, 101, 107, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, 32, 32, 32, 32, 81, 117, 97, - 114, 116, 101, 114, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, 32, 32, 32, 32, 68, 101, - 99, 97, 100, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, 32, 32, 32, 32, 67, 101, - 110, 116, 117, 114, 121, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, 32, 32, 32, 32, 77, - 105, 108, 108, 101, 110, 110, 105, 117, 109, 75, 101, 121, 119, 111, 114, 100, 10, 73, 110, 116, - 101, 114, 118, 97, 108, 84, 111, 73, 110, 116, 101, 114, 118, 97, 108, 32, 60, 45, 32, 89, - 101, 97, 114, 84, 111, 77, 111, 110, 116, 104, 32, 47, 10, 32, 32, 32, 32, 68, 97, 121, - 84, 111, 72, 111, 117, 114, 32, 47, 10, 32, 32, 32, 32, 68, 97, 121, 84, 111, 77, 105, - 110, 117, 116, 101, 32, 47, 10, 32, 32, 32, 32, 68, 97, 121, 84, 111, 83, 101, 99, 111, - 110, 100, 32, 47, 10, 32, 32, 32, 32, 72, 111, 117, 114, 84, 111, 77, 105, 110, 117, 116, - 101, 32, 47, 10, 32, 32, 32, 32, 72, 111, 117, 114, 84, 111, 83, 101, 99, 111, 110, 100, - 32, 47, 10, 32, 32, 32, 32, 77, 105, 110, 117, 116, 101, 84, 111, 83, 101, 99, 111, 110, - 100, 10, 89, 101, 97, 114, 84, 111, 77, 111, 110, 116, 104, 32, 60, 45, 32, 89, 101, 97, - 114, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 77, 111, 110, 116, 104, 75, - 101, 121, 119, 111, 114, 100, 10, 68, 97, 121, 84, 111, 72, 111, 117, 114, 32, 60, 45, 32, - 68, 97, 121, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 72, 111, 117, 114, - 75, 101, 121, 119, 111, 114, 100, 10, 68, 97, 121, 84, 111, 77, 105, 110, 117, 116, 101, 32, - 60, 45, 32, 68, 97, 121, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 77, - 105, 110, 117, 116, 101, 75, 101, 121, 119, 111, 114, 100, 10, 68, 97, 121, 84, 111, 83, 101, - 99, 111, 110, 100, 32, 60, 45, 32, 68, 97, 121, 75, 101, 121, 119, 111, 114, 100, 32, 39, - 84, 79, 39, 32, 83, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 10, 72, 111, - 117, 114, 84, 111, 77, 105, 110, 117, 116, 101, 32, 60, 45, 32, 72, 111, 117, 114, 75, 101, - 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 77, 105, 110, 117, 116, 101, 75, 101, 121, - 119, 111, 114, 100, 10, 72, 111, 117, 114, 84, 111, 83, 101, 99, 111, 110, 100, 32, 60, 45, - 32, 72, 111, 117, 114, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 83, 101, - 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 10, 77, 105, 110, 117, 116, 101, 84, 111, - 83, 101, 99, 111, 110, 100, 32, 60, 45, 32, 77, 105, 110, 117, 116, 101, 75, 101, 121, 119, - 111, 114, 100, 32, 39, 84, 79, 39, 32, 83, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, - 114, 100, 10, 66, 105, 116, 84, 121, 112, 101, 32, 60, 45, 32, 39, 66, 73, 84, 39, 32, - 39, 86, 65, 82, 89, 73, 78, 71, 39, 63, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, - 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 41, 63, 10, 71, 101, 111, - 109, 101, 116, 114, 121, 84, 121, 112, 101, 32, 60, 45, 32, 39, 71, 69, 79, 77, 69, 84, - 82, 89, 39, 32, 80, 97, 114, 101, 110, 115, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, - 110, 41, 63, 10, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 32, 60, 45, 32, 39, - 86, 65, 82, 73, 65, 78, 84, 39, 10, 78, 117, 109, 101, 114, 105, 99, 84, 121, 112, 101, - 32, 60, 45, 32, 83, 105, 109, 112, 108, 101, 78, 117, 109, 101, 114, 105, 99, 84, 121, 112, - 101, 32, 47, 32, 68, 101, 99, 105, 109, 97, 108, 78, 117, 109, 101, 114, 105, 99, 84, 121, - 112, 101, 10, 83, 105, 109, 112, 108, 101, 78, 117, 109, 101, 114, 105, 99, 84, 121, 112, 101, - 32, 60, 45, 32, 73, 110, 116, 84, 121, 112, 101, 32, 47, 32, 73, 110, 116, 101, 103, 101, - 114, 84, 121, 112, 101, 32, 47, 32, 83, 109, 97, 108, 108, 105, 110, 116, 84, 121, 112, 101, - 32, 47, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 66, - 105, 103, 105, 110, 116, 84, 121, 112, 101, 32, 47, 32, 82, 101, 97, 108, 84, 121, 112, 101, - 32, 47, 32, 66, 111, 111, 108, 101, 97, 110, 84, 121, 112, 101, 32, 47, 32, 68, 111, 117, - 98, 108, 101, 84, 121, 112, 101, 10, 68, 101, 99, 105, 109, 97, 108, 78, 117, 109, 101, 114, - 105, 99, 84, 121, 112, 101, 32, 60, 45, 32, 70, 108, 111, 97, 116, 84, 121, 112, 101, 32, - 47, 32, 68, 101, 99, 105, 109, 97, 108, 84, 121, 112, 101, 32, 47, 32, 68, 101, 99, 84, - 121, 112, 101, 32, 47, 32, 78, 117, 109, 101, 114, 105, 99, 77, 111, 100, 84, 121, 112, 101, - 10, 73, 110, 116, 84, 121, 112, 101, 32, 60, 45, 32, 39, 73, 78, 84, 39, 10, 73, 110, - 116, 101, 103, 101, 114, 84, 121, 112, 101, 32, 60, 45, 32, 39, 73, 78, 84, 69, 71, 69, - 82, 39, 10, 83, 109, 97, 108, 108, 105, 110, 116, 84, 121, 112, 101, 32, 60, 45, 32, 39, - 83, 77, 65, 76, 76, 73, 78, 84, 39, 10, 66, 105, 103, 105, 110, 116, 84, 121, 112, 101, - 32, 60, 45, 32, 39, 66, 73, 71, 73, 78, 84, 39, 10, 82, 101, 97, 108, 84, 121, 112, - 101, 32, 60, 45, 32, 39, 82, 69, 65, 76, 39, 10, 66, 111, 111, 108, 101, 97, 110, 84, - 121, 112, 101, 32, 60, 45, 32, 39, 66, 79, 79, 76, 69, 65, 78, 39, 10, 68, 111, 117, - 98, 108, 101, 84, 121, 112, 101, 32, 60, 45, 32, 40, 39, 68, 79, 85, 66, 76, 69, 39, - 32, 39, 80, 82, 69, 67, 73, 83, 73, 79, 78, 39, 41, 10, 70, 108, 111, 97, 116, 84, - 121, 112, 101, 32, 60, 45, 32, 39, 70, 76, 79, 65, 84, 39, 32, 80, 97, 114, 101, 110, - 115, 40, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 41, 63, 10, 68, 101, - 99, 105, 109, 97, 108, 84, 121, 112, 101, 32, 60, 45, 32, 39, 68, 69, 67, 73, 77, 65, - 76, 39, 32, 84, 121, 112, 101, 77, 111, 100, 105, 102, 105, 101, 114, 115, 63, 10, 68, 101, - 99, 84, 121, 112, 101, 32, 60, 45, 32, 39, 68, 69, 67, 39, 32, 84, 121, 112, 101, 77, - 111, 100, 105, 102, 105, 101, 114, 115, 63, 10, 78, 117, 109, 101, 114, 105, 99, 77, 111, 100, - 84, 121, 112, 101, 32, 60, 45, 32, 39, 78, 85, 77, 69, 82, 73, 67, 39, 32, 84, 121, - 112, 101, 77, 111, 100, 105, 102, 105, 101, 114, 115, 63, 10, 81, 117, 97, 108, 105, 102, 105, - 101, 100, 84, 121, 112, 101, 78, 97, 109, 101, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, - 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 84, 121, 112, 101, 78, - 97, 109, 101, 32, 47, 32, 83, 99, 104, 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, - 84, 121, 112, 101, 78, 97, 109, 101, 32, 47, 32, 84, 121, 112, 101, 78, 97, 109, 101, 65, - 115, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 10, 84, 121, 112, 101, 78, - 97, 109, 101, 65, 115, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 32, 60, - 45, 32, 84, 121, 112, 101, 78, 97, 109, 101, 10, 67, 97, 116, 97, 108, 111, 103, 82, 101, - 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 84, 121, 112, 101, 78, 97, 109, 101, - 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, - 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 81, - 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 43, 32, 82, 101, 115, 101, 114, 118, - 101, 100, 84, 121, 112, 101, 78, 97, 109, 101, 10, 83, 99, 104, 101, 109, 97, 82, 101, 115, - 101, 114, 118, 101, 100, 84, 121, 112, 101, 78, 97, 109, 101, 32, 60, 45, 32, 83, 99, 104, - 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, - 101, 114, 118, 101, 100, 84, 121, 112, 101, 78, 97, 109, 101, 10, 84, 121, 112, 101, 77, 111, - 100, 105, 102, 105, 101, 114, 115, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, - 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 63, 41, 10, 82, 111, 119, - 84, 121, 112, 101, 32, 60, 45, 32, 82, 111, 119, 79, 114, 83, 116, 114, 117, 99, 116, 32, - 67, 111, 108, 73, 100, 84, 121, 112, 101, 76, 105, 115, 116, 63, 10, 83, 101, 116, 111, 102, - 84, 121, 112, 101, 32, 60, 45, 32, 39, 83, 69, 84, 79, 70, 39, 32, 84, 121, 112, 101, - 10, 85, 110, 105, 111, 110, 84, 121, 112, 101, 32, 60, 45, 32, 39, 85, 78, 73, 79, 78, - 39, 32, 67, 111, 108, 73, 100, 84, 121, 112, 101, 76, 105, 115, 116, 10, 67, 111, 108, 73, - 100, 84, 121, 112, 101, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, - 76, 105, 115, 116, 40, 67, 111, 108, 73, 100, 84, 121, 112, 101, 41, 41, 10, 77, 97, 112, - 84, 121, 112, 101, 32, 60, 45, 32, 39, 77, 65, 80, 39, 32, 80, 97, 114, 101, 110, 115, - 40, 76, 105, 115, 116, 40, 84, 121, 112, 101, 41, 41, 63, 10, 84, 117, 112, 108, 101, 84, - 121, 112, 101, 32, 60, 45, 32, 39, 84, 85, 80, 76, 69, 39, 32, 80, 97, 114, 101, 110, - 115, 40, 76, 105, 115, 116, 40, 84, 121, 112, 101, 41, 41, 10, 67, 111, 108, 73, 100, 84, - 121, 112, 101, 32, 60, 45, 32, 67, 111, 108, 73, 100, 32, 84, 121, 112, 101, 10, 65, 114, - 114, 97, 121, 66, 111, 117, 110, 100, 115, 32, 60, 45, 32, 65, 114, 114, 97, 121, 75, 101, - 121, 119, 111, 114, 100, 87, 105, 116, 104, 66, 111, 117, 110, 100, 115, 32, 47, 32, 83, 113, - 117, 97, 114, 101, 66, 114, 97, 99, 107, 101, 116, 115, 65, 114, 114, 97, 121, 32, 47, 32, - 65, 114, 114, 97, 121, 75, 101, 121, 119, 111, 114, 100, 10, 65, 114, 114, 97, 121, 75, 101, - 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 65, 82, 82, 65, 89, 39, 10, 65, 114, 114, - 97, 121, 75, 101, 121, 119, 111, 114, 100, 87, 105, 116, 104, 66, 111, 117, 110, 100, 115, 32, - 60, 45, 32, 39, 65, 82, 82, 65, 89, 39, 32, 83, 113, 117, 97, 114, 101, 66, 114, 97, - 99, 107, 101, 116, 115, 65, 114, 114, 97, 121, 10, 83, 113, 117, 97, 114, 101, 66, 114, 97, - 99, 107, 101, 116, 115, 65, 114, 114, 97, 121, 32, 60, 45, 32, 39, 91, 39, 32, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 63, 32, 39, 93, 39, 10, 84, 105, 109, 101, 84, 121, - 112, 101, 32, 60, 45, 32, 84, 105, 109, 101, 79, 114, 84, 105, 109, 101, 115, 116, 97, 109, - 112, 32, 84, 121, 112, 101, 77, 111, 100, 105, 102, 105, 101, 114, 115, 63, 32, 84, 105, 109, - 101, 90, 111, 110, 101, 63, 10, 84, 105, 109, 101, 79, 114, 84, 105, 109, 101, 115, 116, 97, - 109, 112, 32, 60, 45, 32, 84, 105, 109, 101, 84, 121, 112, 101, 73, 100, 32, 47, 32, 84, - 105, 109, 101, 115, 116, 97, 109, 112, 84, 121, 112, 101, 73, 100, 10, 84, 105, 109, 101, 84, - 121, 112, 101, 73, 100, 32, 60, 45, 32, 39, 84, 73, 77, 69, 39, 10, 84, 105, 109, 101, - 115, 116, 97, 109, 112, 84, 121, 112, 101, 73, 100, 32, 60, 45, 32, 39, 84, 73, 77, 69, - 83, 84, 65, 77, 80, 39, 10, 84, 105, 109, 101, 90, 111, 110, 101, 32, 60, 45, 32, 87, - 105, 116, 104, 79, 114, 87, 105, 116, 104, 111, 117, 116, 32, 39, 84, 73, 77, 69, 39, 32, - 39, 90, 79, 78, 69, 39, 10, 87, 105, 116, 104, 79, 114, 87, 105, 116, 104, 111, 117, 116, - 32, 60, 45, 32, 87, 105, 116, 104, 82, 117, 108, 101, 32, 47, 32, 87, 105, 116, 104, 111, - 117, 116, 82, 117, 108, 101, 10, 87, 105, 116, 104, 82, 117, 108, 101, 32, 60, 45, 32, 39, - 87, 73, 84, 72, 39, 10, 87, 105, 116, 104, 111, 117, 116, 82, 117, 108, 101, 32, 60, 45, - 32, 39, 87, 73, 84, 72, 79, 85, 84, 39, 10, 82, 111, 119, 79, 114, 83, 116, 114, 117, - 99, 116, 32, 60, 45, 32, 39, 82, 79, 87, 39, 32, 47, 32, 39, 83, 84, 82, 85, 67, - 84, 39, 10, 35, 32, 105, 110, 116, 101, 114, 110, 97, 108, 32, 100, 101, 102, 105, 110, 105, - 116, 105, 111, 110, 115, 10, 37, 119, 104, 105, 116, 101, 115, 112, 97, 99, 101, 32, 60, 45, - 32, 91, 32, 92, 116, 92, 110, 92, 114, 93, 42, 10, 76, 105, 115, 116, 40, 68, 41, 32, - 60, 45, 32, 68, 32, 40, 39, 44, 39, 32, 68, 41, 42, 32, 39, 44, 39, 63, 10, 80, - 97, 114, 101, 110, 115, 40, 68, 41, 32, 60, 45, 32, 39, 40, 39, 32, 68, 32, 39, 41, - 39, 10, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 75, 101, 121, 119, 111, 114, 100, 32, - 60, 45, 32, 39, 66, 69, 84, 87, 69, 69, 78, 39, 32, 47, 10, 39, 66, 73, 71, 73, - 78, 84, 39, 32, 47, 10, 39, 66, 73, 84, 39, 32, 47, 10, 39, 66, 79, 79, 76, 69, - 65, 78, 39, 32, 47, 10, 39, 67, 72, 65, 82, 39, 32, 47, 10, 39, 67, 72, 65, 82, - 65, 67, 84, 69, 82, 39, 32, 47, 10, 39, 67, 79, 65, 76, 69, 83, 67, 69, 39, 32, - 47, 10, 39, 67, 79, 76, 85, 77, 78, 83, 39, 32, 47, 10, 39, 68, 69, 67, 39, 32, - 47, 10, 39, 68, 69, 67, 73, 77, 65, 76, 39, 32, 47, 10, 39, 69, 88, 73, 83, 84, - 83, 39, 32, 47, 10, 39, 69, 88, 84, 82, 65, 67, 84, 39, 32, 47, 10, 39, 70, 76, - 79, 65, 84, 39, 32, 47, 10, 39, 71, 69, 78, 69, 82, 65, 84, 69, 68, 39, 32, 47, - 10, 39, 71, 82, 79, 85, 80, 73, 78, 71, 39, 32, 47, 10, 39, 71, 82, 79, 85, 80, - 73, 78, 71, 95, 73, 68, 39, 32, 47, 10, 39, 73, 78, 79, 85, 84, 39, 32, 47, 10, - 39, 73, 78, 84, 39, 32, 47, 10, 39, 73, 78, 84, 69, 71, 69, 82, 39, 32, 47, 10, - 39, 73, 78, 84, 69, 82, 86, 65, 76, 39, 32, 47, 10, 39, 77, 65, 80, 39, 32, 47, - 10, 39, 78, 65, 84, 73, 79, 78, 65, 76, 39, 32, 47, 10, 39, 78, 67, 72, 65, 82, - 39, 32, 47, 10, 39, 78, 79, 78, 69, 39, 32, 47, 10, 39, 78, 85, 76, 76, 73, 70, - 39, 32, 47, 10, 39, 78, 85, 77, 69, 82, 73, 67, 39, 32, 47, 10, 39, 79, 85, 84, - 39, 32, 47, 10, 39, 79, 86, 69, 82, 76, 65, 89, 39, 32, 47, 10, 39, 80, 79, 83, - 73, 84, 73, 79, 78, 39, 32, 47, 10, 39, 80, 82, 69, 67, 73, 83, 73, 79, 78, 39, - 32, 47, 10, 39, 82, 69, 65, 76, 39, 32, 47, 10, 39, 82, 79, 87, 39, 32, 47, 10, - 39, 83, 69, 84, 79, 70, 39, 32, 47, 10, 39, 83, 77, 65, 76, 76, 73, 78, 84, 39, - 32, 47, 10, 39, 83, 85, 66, 83, 84, 82, 73, 78, 71, 39, 32, 47, 10, 39, 83, 84, - 82, 85, 67, 84, 39, 32, 47, 10, 39, 84, 73, 77, 69, 39, 32, 47, 10, 39, 84, 73, - 77, 69, 83, 84, 65, 77, 80, 39, 32, 47, 10, 39, 84, 82, 69, 65, 84, 39, 32, 47, - 10, 39, 84, 82, 73, 77, 39, 32, 47, 10, 39, 84, 82, 89, 95, 67, 65, 83, 84, 39, - 32, 47, 10, 39, 84, 85, 80, 76, 69, 39, 32, 47, 10, 39, 86, 65, 76, 85, 69, 83, - 39, 32, 47, 10, 39, 86, 65, 82, 67, 72, 65, 82, 39, 32, 47, 10, 39, 88, 77, 76, - 65, 84, 84, 82, 73, 66, 85, 84, 69, 83, 39, 32, 47, 10, 39, 88, 77, 76, 67, 79, - 78, 67, 65, 84, 39, 32, 47, 10, 39, 88, 77, 76, 69, 76, 69, 77, 69, 78, 84, 39, - 32, 47, 10, 39, 88, 77, 76, 69, 88, 73, 83, 84, 83, 39, 32, 47, 10, 39, 88, 77, - 76, 70, 79, 82, 69, 83, 84, 39, 32, 47, 10, 39, 88, 77, 76, 78, 65, 77, 69, 83, - 80, 65, 67, 69, 83, 39, 32, 47, 10, 39, 88, 77, 76, 80, 65, 82, 83, 69, 39, 32, - 47, 10, 39, 88, 77, 76, 80, 73, 39, 32, 47, 10, 39, 88, 77, 76, 82, 79, 79, 84, - 39, 32, 47, 10, 39, 88, 77, 76, 83, 69, 82, 73, 65, 76, 73, 90, 69, 39, 32, 47, - 10, 39, 88, 77, 76, 84, 65, 66, 76, 69, 39, 10, 70, 117, 110, 99, 78, 97, 109, 101, - 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 65, 83, 79, 70, 39, 32, 47, 10, - 39, 65, 84, 39, 32, 47, 10, 39, 65, 85, 84, 72, 79, 82, 73, 90, 65, 84, 73, 79, - 78, 39, 32, 47, 10, 39, 66, 73, 78, 65, 82, 89, 39, 32, 47, 10, 39, 67, 79, 76, - 76, 65, 84, 73, 79, 78, 39, 32, 47, 10, 39, 67, 79, 78, 67, 85, 82, 82, 69, 78, - 84, 76, 89, 39, 32, 47, 10, 39, 67, 82, 79, 83, 83, 39, 32, 47, 10, 39, 70, 82, - 69, 69, 90, 69, 39, 32, 47, 10, 39, 70, 85, 76, 76, 39, 32, 47, 10, 39, 71, 69, - 78, 69, 82, 65, 84, 69, 68, 39, 32, 47, 10, 39, 71, 76, 79, 66, 39, 32, 47, 10, - 39, 73, 76, 73, 75, 69, 39, 32, 47, 10, 39, 73, 78, 78, 69, 82, 39, 32, 47, 10, - 39, 73, 83, 39, 32, 47, 10, 39, 73, 83, 78, 85, 76, 76, 39, 32, 47, 10, 39, 74, - 79, 73, 78, 39, 32, 47, 10, 39, 76, 69, 70, 84, 39, 32, 47, 10, 39, 76, 73, 75, - 69, 39, 32, 47, 10, 39, 77, 65, 80, 39, 32, 47, 10, 39, 78, 65, 84, 85, 82, 65, - 76, 39, 32, 47, 10, 39, 78, 79, 84, 78, 85, 76, 76, 39, 32, 47, 10, 39, 79, 85, - 84, 69, 82, 39, 32, 47, 10, 39, 79, 86, 69, 82, 76, 65, 80, 83, 39, 32, 47, 10, - 39, 80, 79, 83, 73, 84, 73, 79, 78, 65, 76, 39, 32, 47, 10, 39, 82, 73, 71, 72, - 84, 39, 32, 47, 10, 39, 83, 73, 77, 73, 76, 65, 82, 39, 32, 47, 10, 39, 83, 84, - 82, 85, 67, 84, 39, 32, 47, 10, 39, 84, 65, 66, 76, 69, 83, 65, 77, 80, 76, 69, - 39, 32, 47, 10, 39, 84, 85, 80, 76, 69, 39, 32, 47, 10, 39, 86, 69, 82, 66, 79, - 83, 69, 39, 10, 82, 101, 115, 101, 114, 118, 101, 100, 75, 101, 121, 119, 111, 114, 100, 32, - 60, 45, 32, 39, 65, 76, 76, 39, 32, 47, 10, 39, 65, 78, 65, 76, 89, 83, 69, 39, - 32, 47, 10, 39, 65, 78, 65, 76, 89, 90, 69, 39, 32, 47, 10, 39, 65, 78, 68, 39, - 32, 47, 10, 39, 65, 78, 89, 39, 32, 47, 10, 39, 65, 82, 82, 65, 89, 39, 32, 47, - 10, 39, 65, 83, 39, 32, 47, 10, 39, 65, 83, 67, 39, 32, 47, 10, 39, 65, 83, 89, - 77, 77, 69, 84, 82, 73, 67, 39, 32, 47, 10, 39, 66, 79, 84, 72, 39, 32, 47, 10, - 39, 67, 65, 83, 69, 39, 32, 47, 10, 39, 67, 65, 83, 84, 39, 32, 47, 10, 39, 67, - 72, 69, 67, 75, 39, 32, 47, 10, 39, 67, 79, 76, 76, 65, 84, 69, 39, 32, 47, 10, - 39, 67, 79, 76, 85, 77, 78, 39, 32, 47, 10, 39, 67, 79, 78, 83, 84, 82, 65, 73, - 78, 84, 39, 32, 47, 10, 39, 67, 82, 69, 65, 84, 69, 39, 32, 47, 10, 39, 68, 69, - 70, 65, 85, 76, 84, 39, 32, 47, 10, 39, 68, 69, 70, 69, 82, 82, 65, 66, 76, 69, - 39, 32, 47, 10, 39, 68, 69, 83, 67, 39, 32, 47, 10, 39, 68, 69, 83, 67, 82, 73, - 66, 69, 39, 32, 47, 10, 39, 68, 73, 83, 84, 73, 78, 67, 84, 39, 32, 47, 10, 39, - 68, 79, 39, 32, 47, 10, 39, 69, 76, 83, 69, 39, 32, 47, 10, 39, 69, 78, 68, 39, - 32, 47, 10, 39, 69, 88, 67, 69, 80, 84, 39, 32, 47, 10, 39, 70, 65, 76, 83, 69, - 39, 32, 47, 10, 39, 70, 69, 84, 67, 72, 39, 32, 47, 10, 39, 70, 79, 82, 39, 32, - 47, 10, 39, 70, 79, 82, 69, 73, 71, 78, 39, 32, 47, 10, 39, 70, 82, 79, 77, 39, - 32, 47, 10, 39, 71, 82, 79, 85, 80, 39, 32, 47, 10, 39, 72, 65, 86, 73, 78, 71, - 39, 32, 47, 10, 39, 73, 78, 39, 32, 47, 10, 39, 73, 78, 73, 84, 73, 65, 76, 76, - 89, 39, 32, 47, 10, 39, 73, 78, 84, 69, 82, 83, 69, 67, 84, 39, 32, 47, 10, 39, - 73, 78, 84, 79, 39, 32, 47, 10, 39, 76, 65, 77, 66, 68, 65, 39, 32, 47, 10, 39, - 76, 65, 84, 69, 82, 65, 76, 39, 32, 47, 10, 39, 76, 69, 65, 68, 73, 78, 71, 39, - 32, 47, 10, 39, 76, 73, 77, 73, 84, 39, 32, 47, 10, 39, 77, 65, 84, 67, 72, 95, - 82, 69, 67, 79, 71, 78, 73, 90, 69, 39, 32, 47, 10, 39, 78, 79, 84, 39, 32, 47, - 10, 39, 78, 85, 76, 76, 39, 32, 47, 10, 39, 79, 70, 70, 83, 69, 84, 39, 32, 47, - 10, 39, 79, 78, 39, 32, 47, 10, 39, 79, 78, 76, 89, 39, 32, 47, 10, 39, 79, 82, - 39, 32, 47, 10, 39, 79, 82, 68, 69, 82, 39, 32, 47, 10, 39, 80, 73, 86, 79, 84, - 39, 32, 47, 10, 39, 80, 73, 86, 79, 84, 95, 76, 79, 78, 71, 69, 82, 39, 32, 47, - 10, 39, 80, 73, 86, 79, 84, 95, 87, 73, 68, 69, 82, 39, 32, 47, 10, 39, 80, 76, - 65, 67, 73, 78, 71, 39, 32, 47, 10, 39, 80, 82, 73, 77, 65, 82, 89, 39, 32, 47, - 10, 39, 81, 85, 65, 76, 73, 70, 89, 39, 32, 47, 10, 39, 82, 69, 70, 69, 82, 69, - 78, 67, 69, 83, 39, 32, 47, 10, 39, 82, 69, 84, 85, 82, 78, 73, 78, 71, 39, 32, - 47, 10, 39, 83, 69, 76, 69, 67, 84, 39, 32, 47, 10, 39, 83, 72, 79, 87, 39, 32, - 47, 10, 39, 83, 79, 77, 69, 39, 32, 47, 10, 39, 83, 85, 77, 77, 65, 82, 73, 90, - 69, 39, 32, 47, 10, 39, 83, 89, 77, 77, 69, 84, 82, 73, 67, 39, 32, 47, 10, 39, - 84, 65, 66, 76, 69, 39, 32, 47, 10, 39, 84, 72, 69, 78, 39, 32, 47, 10, 39, 84, - 79, 39, 32, 47, 10, 39, 84, 82, 65, 73, 76, 73, 78, 71, 39, 32, 47, 10, 39, 84, - 82, 85, 69, 39, 32, 47, 10, 39, 85, 78, 73, 79, 78, 39, 32, 47, 10, 39, 85, 78, - 73, 81, 85, 69, 39, 32, 47, 10, 39, 85, 78, 80, 73, 86, 79, 84, 39, 32, 47, 10, - 39, 85, 83, 73, 78, 71, 39, 32, 47, 10, 39, 86, 65, 82, 73, 65, 68, 73, 67, 39, - 32, 47, 10, 39, 87, 72, 69, 78, 39, 32, 47, 10, 39, 87, 72, 69, 82, 69, 39, 32, - 47, 10, 39, 87, 73, 78, 68, 79, 87, 39, 32, 47, 10, 39, 87, 73, 84, 72, 39, 10, - 84, 121, 112, 101, 78, 97, 109, 101, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, - 65, 83, 79, 70, 39, 32, 47, 10, 39, 65, 84, 39, 32, 47, 10, 39, 65, 85, 84, 72, - 79, 82, 73, 90, 65, 84, 73, 79, 78, 39, 32, 47, 10, 39, 66, 73, 78, 65, 82, 89, - 39, 32, 47, 10, 39, 66, 89, 39, 32, 47, 10, 39, 67, 79, 76, 76, 65, 84, 73, 79, - 78, 39, 32, 47, 10, 39, 67, 79, 76, 85, 77, 78, 83, 39, 32, 47, 10, 39, 67, 79, - 78, 67, 85, 82, 82, 69, 78, 84, 76, 89, 39, 32, 47, 10, 39, 67, 82, 79, 83, 83, - 39, 32, 47, 10, 39, 70, 82, 69, 69, 90, 69, 39, 32, 47, 10, 39, 70, 85, 76, 76, - 39, 32, 47, 10, 39, 71, 76, 79, 66, 39, 32, 47, 10, 39, 73, 76, 73, 75, 69, 39, - 32, 47, 10, 39, 73, 78, 78, 69, 82, 39, 32, 47, 10, 39, 73, 83, 39, 32, 47, 10, - 39, 73, 83, 78, 85, 76, 76, 39, 32, 47, 10, 39, 74, 79, 73, 78, 39, 32, 47, 10, - 39, 76, 69, 70, 84, 39, 32, 47, 10, 39, 76, 73, 75, 69, 39, 32, 47, 10, 39, 78, - 65, 84, 85, 82, 65, 76, 39, 32, 47, 10, 39, 78, 79, 84, 78, 85, 76, 76, 39, 32, - 47, 10, 39, 79, 85, 84, 69, 82, 39, 32, 47, 10, 39, 79, 86, 69, 82, 76, 65, 80, - 83, 39, 32, 47, 10, 39, 80, 79, 83, 73, 84, 73, 79, 78, 65, 76, 39, 32, 47, 10, - 39, 82, 73, 71, 72, 84, 39, 32, 47, 10, 39, 85, 78, 80, 65, 67, 75, 39, 32, 47, - 10, 39, 83, 73, 77, 73, 76, 65, 82, 39, 32, 47, 10, 39, 84, 65, 66, 76, 69, 83, - 65, 77, 80, 76, 69, 39, 32, 47, 10, 39, 84, 82, 89, 95, 67, 65, 83, 84, 39, 32, - 47, 10, 39, 86, 69, 82, 66, 79, 83, 69, 39, 32, 47, 10, 39, 83, 69, 77, 73, 39, - 32, 47, 10, 39, 65, 78, 84, 73, 39, 10, 85, 110, 114, 101, 115, 101, 114, 118, 101, 100, - 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 65, 66, 79, 82, 84, 39, 32, 47, - 10, 39, 65, 66, 83, 79, 76, 85, 84, 69, 39, 32, 47, 10, 39, 65, 67, 67, 69, 83, - 83, 39, 32, 47, 10, 39, 65, 67, 84, 73, 79, 78, 39, 32, 47, 10, 39, 65, 68, 68, - 39, 32, 47, 10, 39, 65, 68, 77, 73, 78, 39, 32, 47, 10, 39, 65, 70, 84, 69, 82, - 39, 32, 47, 10, 39, 65, 71, 71, 82, 69, 71, 65, 84, 69, 39, 32, 47, 10, 39, 65, - 76, 83, 79, 39, 32, 47, 10, 39, 65, 76, 84, 69, 82, 39, 32, 47, 10, 39, 65, 76, - 87, 65, 89, 83, 39, 32, 47, 10, 39, 65, 80, 80, 82, 79, 88, 39, 32, 47, 10, 39, - 65, 83, 83, 69, 82, 84, 73, 79, 78, 39, 32, 47, 10, 39, 65, 83, 83, 73, 71, 78, - 77, 69, 78, 84, 39, 32, 47, 10, 39, 65, 84, 84, 65, 67, 72, 39, 32, 47, 10, 39, - 65, 84, 84, 82, 73, 66, 85, 84, 69, 39, 32, 47, 10, 39, 65, 85, 84, 79, 39, 32, - 47, 10, 39, 66, 65, 67, 75, 87, 65, 82, 68, 39, 32, 47, 10, 39, 66, 69, 70, 79, - 82, 69, 39, 32, 47, 10, 39, 66, 69, 71, 73, 78, 39, 32, 47, 10, 39, 67, 65, 67, - 72, 69, 39, 32, 47, 10, 39, 67, 65, 76, 76, 39, 32, 47, 10, 39, 67, 65, 76, 76, - 69, 68, 39, 32, 47, 10, 39, 67, 65, 83, 67, 65, 68, 69, 39, 32, 47, 10, 39, 67, - 65, 83, 67, 65, 68, 69, 68, 39, 32, 47, 10, 39, 67, 65, 84, 65, 76, 79, 71, 39, - 32, 47, 10, 39, 67, 69, 78, 84, 85, 82, 73, 69, 83, 39, 32, 47, 10, 39, 67, 69, - 78, 84, 85, 82, 89, 39, 32, 47, 10, 39, 67, 72, 65, 73, 78, 39, 32, 47, 10, 39, - 67, 72, 65, 82, 65, 67, 84, 69, 82, 73, 83, 84, 73, 67, 83, 39, 32, 47, 10, 39, - 67, 72, 69, 67, 75, 80, 79, 73, 78, 84, 39, 32, 47, 10, 39, 67, 76, 65, 83, 83, - 39, 32, 47, 10, 39, 67, 76, 79, 83, 69, 39, 32, 47, 10, 39, 67, 76, 85, 83, 84, - 69, 82, 39, 32, 47, 10, 39, 67, 79, 77, 77, 69, 78, 84, 39, 32, 47, 10, 39, 67, - 79, 77, 77, 69, 78, 84, 83, 39, 32, 47, 10, 39, 67, 79, 77, 77, 73, 84, 39, 32, - 47, 10, 39, 67, 79, 77, 77, 73, 84, 84, 69, 68, 39, 32, 47, 10, 39, 67, 79, 77, - 80, 82, 69, 83, 83, 73, 79, 78, 39, 32, 47, 10, 39, 67, 79, 78, 70, 73, 71, 85, - 82, 65, 84, 73, 79, 78, 39, 32, 47, 10, 39, 67, 79, 78, 70, 76, 73, 67, 84, 39, - 32, 47, 10, 39, 67, 79, 78, 78, 69, 67, 84, 39, 32, 47, 10, 39, 67, 79, 78, 78, - 69, 67, 84, 73, 79, 78, 39, 32, 47, 10, 39, 67, 79, 78, 83, 84, 82, 65, 73, 78, - 84, 83, 39, 32, 47, 10, 39, 67, 79, 78, 84, 69, 78, 84, 39, 32, 47, 10, 39, 67, - 79, 78, 84, 73, 78, 85, 69, 39, 32, 47, 10, 39, 67, 79, 78, 86, 69, 82, 83, 73, - 79, 78, 39, 32, 47, 10, 39, 67, 79, 80, 89, 39, 32, 47, 10, 39, 67, 79, 83, 84, - 39, 32, 47, 10, 39, 67, 83, 86, 39, 32, 47, 10, 39, 67, 85, 66, 69, 39, 32, 47, - 10, 39, 67, 85, 82, 82, 69, 78, 84, 39, 32, 47, 10, 39, 67, 85, 82, 83, 79, 82, - 39, 32, 47, 10, 39, 67, 89, 67, 76, 69, 39, 32, 47, 10, 39, 68, 65, 84, 65, 39, - 32, 47, 10, 39, 68, 65, 84, 65, 66, 65, 83, 69, 39, 32, 47, 10, 39, 68, 65, 89, - 39, 32, 47, 10, 39, 68, 65, 89, 83, 39, 32, 47, 10, 39, 68, 69, 65, 76, 76, 79, - 67, 65, 84, 69, 39, 32, 47, 10, 39, 68, 69, 67, 65, 68, 69, 39, 32, 47, 10, 39, - 68, 69, 67, 65, 68, 69, 83, 39, 32, 47, 10, 39, 68, 69, 67, 76, 65, 82, 69, 39, - 32, 47, 10, 39, 68, 69, 70, 65, 85, 76, 84, 83, 39, 32, 47, 10, 39, 68, 69, 70, - 69, 82, 82, 69, 68, 39, 32, 47, 10, 39, 68, 69, 70, 73, 78, 69, 82, 39, 32, 47, - 10, 39, 68, 69, 70, 73, 78, 69, 83, 39, 32, 47, 10, 39, 68, 69, 76, 69, 84, 69, - 39, 32, 47, 10, 39, 68, 69, 76, 73, 77, 73, 84, 69, 82, 39, 32, 47, 10, 39, 68, - 69, 76, 73, 77, 73, 84, 69, 82, 83, 39, 32, 47, 10, 39, 68, 69, 80, 69, 78, 68, - 83, 39, 32, 47, 10, 39, 68, 69, 84, 65, 67, 72, 39, 32, 47, 10, 39, 68, 73, 67, - 84, 73, 79, 78, 65, 82, 89, 39, 32, 47, 10, 39, 68, 73, 83, 65, 66, 76, 69, 39, - 32, 47, 10, 39, 68, 73, 83, 67, 65, 82, 68, 39, 32, 47, 10, 39, 68, 73, 83, 67, - 79, 78, 78, 69, 67, 84, 39, 32, 47, 10, 39, 68, 73, 83, 84, 65, 78, 67, 69, 39, - 32, 47, 10, 39, 68, 79, 67, 85, 77, 69, 78, 84, 39, 32, 47, 10, 39, 68, 79, 77, - 65, 73, 78, 39, 32, 47, 10, 39, 68, 79, 85, 66, 76, 69, 39, 32, 47, 10, 39, 68, - 82, 79, 80, 39, 32, 47, 10, 39, 69, 65, 67, 72, 39, 32, 47, 10, 39, 69, 78, 65, - 66, 76, 69, 39, 32, 47, 10, 39, 69, 78, 67, 79, 68, 73, 78, 71, 39, 32, 47, 10, - 39, 69, 78, 67, 82, 89, 80, 84, 69, 68, 39, 32, 47, 10, 39, 69, 78, 85, 77, 39, - 32, 47, 10, 39, 69, 82, 82, 79, 82, 39, 32, 47, 10, 39, 69, 83, 67, 65, 80, 69, - 39, 32, 47, 10, 39, 69, 86, 69, 78, 84, 39, 32, 47, 10, 39, 69, 88, 65, 67, 84, - 39, 32, 47, 10, 39, 69, 88, 67, 76, 85, 68, 69, 39, 32, 47, 10, 39, 69, 88, 67, - 76, 85, 68, 73, 78, 71, 39, 32, 47, 10, 39, 69, 88, 67, 76, 85, 83, 73, 86, 69, - 39, 32, 47, 10, 39, 69, 88, 69, 67, 85, 84, 69, 39, 32, 47, 10, 39, 69, 88, 80, - 76, 65, 73, 78, 39, 32, 47, 10, 39, 69, 88, 80, 79, 82, 84, 39, 32, 47, 10, 39, - 69, 88, 80, 79, 82, 84, 95, 83, 84, 65, 84, 69, 39, 32, 47, 10, 39, 69, 88, 84, - 69, 78, 83, 73, 79, 78, 39, 32, 47, 10, 39, 69, 88, 84, 69, 78, 83, 73, 79, 78, - 83, 39, 32, 47, 10, 39, 69, 88, 84, 69, 82, 78, 65, 76, 39, 32, 47, 10, 39, 70, - 65, 77, 73, 76, 89, 39, 32, 47, 10, 39, 70, 73, 76, 84, 69, 82, 39, 32, 47, 10, - 39, 70, 73, 82, 83, 84, 39, 32, 47, 10, 39, 70, 79, 76, 76, 79, 87, 73, 78, 71, - 39, 32, 47, 10, 39, 70, 79, 82, 67, 69, 39, 32, 47, 10, 39, 70, 79, 82, 87, 65, - 82, 68, 39, 32, 47, 10, 39, 70, 85, 78, 67, 84, 73, 79, 78, 39, 32, 47, 10, 39, - 70, 85, 78, 67, 84, 73, 79, 78, 83, 39, 32, 47, 10, 39, 71, 76, 79, 66, 65, 76, - 39, 32, 47, 10, 39, 71, 82, 65, 78, 84, 39, 32, 47, 10, 39, 71, 82, 65, 78, 84, - 69, 68, 39, 32, 47, 10, 39, 71, 82, 79, 85, 80, 83, 39, 32, 47, 10, 39, 72, 65, - 78, 68, 76, 69, 82, 39, 32, 47, 10, 39, 72, 69, 65, 68, 69, 82, 39, 32, 47, 10, - 39, 72, 79, 76, 68, 39, 32, 47, 10, 39, 72, 79, 85, 82, 39, 32, 47, 10, 39, 72, - 79, 85, 82, 83, 39, 32, 47, 10, 39, 73, 68, 69, 78, 84, 73, 84, 89, 39, 32, 47, - 10, 39, 73, 70, 39, 32, 47, 10, 39, 73, 71, 78, 79, 82, 69, 39, 32, 47, 10, 39, - 73, 77, 77, 69, 68, 73, 65, 84, 69, 39, 32, 47, 10, 39, 73, 77, 77, 85, 84, 65, - 66, 76, 69, 39, 32, 47, 10, 39, 73, 77, 80, 76, 73, 67, 73, 84, 39, 32, 47, 10, - 39, 73, 77, 80, 79, 82, 84, 39, 32, 47, 10, 39, 73, 78, 67, 76, 85, 68, 69, 39, - 32, 47, 10, 39, 73, 78, 67, 76, 85, 68, 73, 78, 71, 39, 32, 47, 10, 39, 73, 78, - 67, 82, 69, 77, 69, 78, 84, 39, 32, 47, 10, 39, 73, 78, 68, 69, 88, 39, 32, 47, - 10, 39, 73, 78, 68, 69, 88, 69, 83, 39, 32, 47, 10, 39, 73, 78, 72, 69, 82, 73, - 84, 39, 32, 47, 10, 39, 73, 78, 72, 69, 82, 73, 84, 83, 39, 32, 47, 10, 39, 73, - 78, 76, 73, 78, 69, 39, 32, 47, 10, 39, 73, 78, 80, 85, 84, 39, 32, 47, 10, 39, - 73, 78, 83, 69, 78, 83, 73, 84, 73, 86, 69, 39, 32, 47, 10, 39, 73, 78, 83, 69, - 82, 84, 39, 32, 47, 10, 39, 73, 78, 83, 84, 65, 76, 76, 39, 32, 47, 10, 39, 73, - 78, 83, 84, 69, 65, 68, 39, 32, 47, 10, 39, 73, 78, 86, 79, 75, 69, 82, 39, 32, - 47, 10, 39, 73, 83, 79, 76, 65, 84, 73, 79, 78, 39, 32, 47, 10, 39, 74, 83, 79, - 78, 39, 32, 47, 10, 39, 75, 69, 89, 39, 32, 47, 10, 39, 76, 65, 66, 69, 76, 39, - 32, 47, 10, 39, 76, 65, 78, 71, 85, 65, 71, 69, 39, 32, 47, 10, 39, 76, 65, 82, - 71, 69, 39, 32, 47, 10, 39, 76, 65, 83, 84, 39, 32, 47, 10, 39, 76, 69, 65, 75, - 80, 82, 79, 79, 70, 39, 32, 47, 10, 39, 76, 69, 86, 69, 76, 39, 32, 47, 10, 39, - 76, 73, 83, 84, 69, 78, 39, 32, 47, 10, 39, 76, 79, 65, 68, 39, 32, 47, 10, 39, - 76, 79, 67, 65, 76, 39, 32, 47, 10, 39, 76, 79, 67, 65, 84, 73, 79, 78, 39, 32, - 47, 10, 39, 76, 79, 67, 75, 39, 32, 47, 10, 39, 76, 79, 67, 75, 69, 68, 39, 32, - 47, 10, 39, 76, 79, 71, 71, 69, 68, 39, 32, 47, 10, 39, 77, 65, 67, 82, 79, 39, - 32, 47, 10, 39, 77, 65, 80, 80, 73, 78, 71, 39, 32, 47, 10, 39, 77, 65, 84, 67, - 72, 39, 32, 47, 10, 39, 77, 65, 84, 67, 72, 69, 68, 39, 32, 47, 10, 39, 77, 65, - 84, 69, 82, 73, 65, 76, 73, 90, 69, 68, 39, 32, 47, 10, 39, 77, 65, 88, 86, 65, - 76, 85, 69, 39, 32, 47, 10, 39, 77, 69, 82, 71, 69, 39, 32, 47, 10, 39, 77, 69, - 84, 72, 79, 68, 39, 32, 47, 10, 39, 77, 73, 67, 82, 79, 83, 69, 67, 79, 78, 68, - 39, 32, 47, 10, 39, 77, 73, 67, 82, 79, 83, 69, 67, 79, 78, 68, 83, 39, 32, 47, - 10, 39, 77, 73, 76, 76, 69, 78, 78, 73, 65, 39, 32, 47, 10, 39, 77, 73, 76, 76, - 69, 78, 78, 73, 85, 77, 39, 32, 47, 10, 39, 77, 73, 76, 76, 73, 83, 69, 67, 79, - 78, 68, 39, 32, 47, 10, 39, 77, 73, 76, 76, 73, 83, 69, 67, 79, 78, 68, 83, 39, - 32, 47, 10, 39, 77, 73, 78, 85, 84, 69, 39, 32, 47, 10, 39, 77, 73, 78, 85, 84, - 69, 83, 39, 32, 47, 10, 39, 77, 73, 78, 86, 65, 76, 85, 69, 39, 32, 47, 10, 39, - 77, 79, 68, 69, 39, 32, 47, 10, 39, 77, 79, 78, 84, 72, 39, 32, 47, 10, 39, 77, - 79, 78, 84, 72, 83, 39, 32, 47, 10, 39, 77, 79, 86, 69, 39, 32, 47, 10, 39, 78, - 65, 77, 69, 39, 32, 47, 10, 39, 78, 65, 77, 69, 83, 39, 32, 47, 10, 39, 78, 69, - 65, 82, 69, 83, 84, 39, 32, 47, 10, 39, 78, 69, 87, 39, 32, 47, 10, 39, 78, 69, - 88, 84, 39, 32, 47, 10, 39, 78, 79, 39, 32, 47, 10, 39, 78, 79, 84, 72, 73, 78, - 71, 39, 32, 47, 10, 39, 78, 79, 84, 73, 70, 89, 39, 32, 47, 10, 39, 78, 79, 87, - 65, 73, 84, 39, 32, 47, 10, 39, 78, 85, 76, 76, 83, 39, 32, 47, 10, 39, 79, 66, - 74, 69, 67, 84, 39, 32, 47, 10, 39, 79, 70, 39, 32, 47, 10, 39, 79, 70, 70, 39, - 32, 47, 10, 39, 79, 73, 68, 83, 39, 32, 47, 10, 39, 79, 76, 68, 39, 32, 47, 10, - 39, 79, 80, 69, 82, 65, 84, 79, 82, 39, 32, 47, 10, 39, 79, 80, 84, 73, 79, 78, - 39, 32, 47, 10, 39, 79, 80, 84, 73, 79, 78, 83, 39, 32, 47, 10, 39, 79, 82, 68, - 73, 78, 65, 76, 73, 84, 89, 39, 32, 47, 10, 39, 79, 84, 72, 69, 82, 83, 39, 32, - 47, 10, 39, 79, 86, 69, 82, 39, 32, 47, 10, 39, 79, 86, 69, 82, 82, 73, 68, 73, - 78, 71, 39, 32, 47, 10, 39, 79, 87, 78, 69, 68, 39, 32, 47, 10, 39, 79, 87, 78, - 69, 82, 39, 32, 47, 10, 39, 80, 65, 82, 65, 76, 76, 69, 76, 39, 32, 47, 10, 39, - 80, 65, 82, 83, 69, 82, 39, 32, 47, 10, 39, 80, 65, 82, 84, 73, 65, 76, 39, 32, - 47, 10, 39, 80, 65, 82, 84, 73, 84, 73, 79, 78, 39, 32, 47, 10, 39, 80, 65, 82, - 84, 73, 84, 73, 79, 78, 69, 68, 39, 32, 47, 10, 39, 80, 65, 83, 83, 73, 78, 71, - 39, 32, 47, 10, 39, 80, 65, 83, 83, 87, 79, 82, 68, 39, 32, 47, 10, 39, 80, 69, - 82, 67, 69, 78, 84, 39, 32, 47, 10, 39, 80, 69, 82, 83, 73, 83, 84, 69, 78, 84, - 39, 32, 47, 10, 39, 80, 76, 65, 78, 83, 39, 32, 47, 10, 39, 80, 79, 76, 73, 67, - 89, 39, 32, 47, 10, 39, 80, 82, 65, 71, 77, 65, 39, 32, 47, 10, 39, 80, 82, 69, - 67, 69, 68, 73, 78, 71, 39, 32, 47, 10, 39, 80, 82, 69, 80, 65, 82, 69, 39, 32, - 47, 10, 39, 80, 82, 69, 80, 65, 82, 69, 68, 39, 32, 47, 10, 39, 80, 82, 69, 83, - 69, 82, 86, 69, 39, 32, 47, 10, 39, 80, 82, 73, 79, 82, 39, 32, 47, 10, 39, 80, - 82, 73, 86, 73, 76, 69, 71, 69, 83, 39, 32, 47, 10, 39, 80, 82, 79, 67, 69, 68, - 85, 82, 65, 76, 39, 32, 47, 10, 39, 80, 82, 79, 67, 69, 68, 85, 82, 69, 39, 32, - 47, 10, 39, 80, 82, 79, 71, 82, 65, 77, 39, 32, 47, 10, 39, 80, 85, 66, 76, 73, - 67, 65, 84, 73, 79, 78, 39, 32, 47, 10, 39, 81, 85, 65, 82, 84, 69, 82, 39, 32, - 47, 10, 39, 81, 85, 65, 82, 84, 69, 82, 83, 39, 32, 47, 10, 39, 81, 85, 79, 84, - 69, 39, 32, 47, 10, 39, 82, 65, 78, 71, 69, 39, 32, 47, 10, 39, 82, 69, 65, 68, - 39, 32, 47, 10, 39, 82, 69, 65, 83, 83, 73, 71, 78, 39, 32, 47, 10, 39, 82, 69, - 67, 72, 69, 67, 75, 39, 32, 47, 10, 39, 82, 69, 67, 85, 82, 83, 73, 86, 69, 39, - 32, 47, 10, 39, 82, 69, 70, 39, 32, 47, 10, 39, 82, 69, 70, 69, 82, 69, 78, 67, - 73, 78, 71, 39, 32, 47, 10, 39, 82, 69, 70, 82, 69, 83, 72, 39, 32, 47, 10, 39, - 82, 69, 73, 78, 68, 69, 88, 39, 32, 47, 10, 39, 82, 69, 76, 65, 84, 73, 86, 69, - 39, 32, 47, 10, 39, 82, 69, 76, 69, 65, 83, 69, 39, 32, 47, 10, 39, 82, 69, 78, - 65, 77, 69, 39, 32, 47, 10, 39, 82, 69, 80, 69, 65, 84, 65, 66, 76, 69, 39, 32, - 47, 10, 39, 82, 69, 80, 76, 65, 67, 69, 39, 32, 47, 10, 39, 82, 69, 80, 76, 73, - 67, 65, 39, 32, 47, 10, 39, 82, 69, 83, 69, 84, 39, 32, 47, 10, 39, 82, 69, 83, - 79, 85, 82, 67, 69, 39, 32, 47, 10, 39, 82, 69, 83, 80, 69, 67, 84, 39, 32, 47, - 10, 39, 82, 69, 83, 84, 65, 82, 84, 39, 32, 47, 10, 39, 82, 69, 83, 84, 82, 73, - 67, 84, 39, 32, 47, 10, 39, 82, 69, 84, 85, 82, 78, 83, 39, 32, 47, 10, 39, 82, - 69, 86, 79, 75, 69, 39, 32, 47, 10, 39, 82, 79, 76, 69, 39, 32, 47, 10, 39, 82, - 79, 76, 76, 66, 65, 67, 75, 39, 32, 47, 10, 39, 82, 79, 76, 76, 85, 80, 39, 32, - 47, 10, 39, 82, 79, 87, 83, 39, 32, 47, 10, 39, 82, 85, 76, 69, 39, 32, 47, 10, - 39, 83, 65, 77, 80, 76, 69, 39, 32, 47, 10, 39, 83, 65, 86, 69, 80, 79, 73, 78, - 84, 39, 32, 47, 10, 39, 83, 67, 72, 69, 77, 65, 39, 32, 47, 10, 39, 83, 67, 72, - 69, 77, 65, 83, 39, 32, 47, 10, 39, 83, 67, 79, 80, 69, 39, 32, 47, 10, 39, 83, - 67, 82, 79, 76, 76, 39, 32, 47, 10, 39, 83, 69, 65, 82, 67, 72, 39, 32, 47, 10, - 39, 83, 69, 67, 79, 78, 68, 39, 32, 47, 10, 39, 83, 69, 67, 79, 78, 68, 83, 39, - 32, 47, 10, 39, 83, 69, 67, 82, 69, 84, 39, 32, 47, 10, 39, 83, 69, 67, 85, 82, - 69, 39, 32, 47, 10, 39, 83, 69, 67, 85, 82, 73, 84, 89, 39, 32, 47, 10, 39, 83, - 69, 81, 85, 69, 78, 67, 69, 39, 32, 47, 10, 39, 83, 69, 81, 85, 69, 78, 67, 69, - 83, 39, 32, 47, 10, 39, 83, 69, 82, 73, 65, 76, 73, 90, 65, 66, 76, 69, 39, 32, - 47, 10, 39, 83, 69, 82, 86, 69, 82, 39, 32, 47, 10, 39, 83, 69, 83, 83, 73, 79, - 78, 39, 32, 47, 10, 39, 83, 69, 84, 39, 32, 47, 10, 39, 83, 69, 84, 83, 39, 32, - 47, 10, 39, 83, 72, 65, 82, 69, 39, 32, 47, 10, 39, 83, 73, 77, 73, 76, 65, 82, - 73, 84, 89, 39, 32, 47, 10, 39, 83, 73, 77, 80, 76, 69, 39, 32, 47, 10, 39, 83, - 75, 73, 80, 39, 32, 47, 10, 39, 83, 78, 65, 80, 83, 72, 79, 84, 39, 32, 47, 10, - 39, 83, 79, 82, 84, 69, 68, 39, 32, 47, 10, 39, 83, 79, 85, 82, 67, 69, 39, 32, - 47, 10, 39, 83, 81, 76, 39, 32, 47, 10, 39, 83, 84, 65, 66, 76, 69, 39, 32, 47, - 10, 39, 83, 84, 65, 78, 68, 65, 76, 79, 78, 69, 39, 32, 47, 10, 39, 83, 84, 65, - 82, 84, 39, 32, 47, 10, 39, 83, 84, 65, 84, 69, 77, 69, 78, 84, 39, 32, 47, 10, - 39, 83, 84, 65, 84, 73, 83, 84, 73, 67, 83, 39, 32, 47, 10, 39, 83, 84, 68, 73, - 78, 39, 32, 47, 10, 39, 83, 84, 68, 79, 85, 84, 39, 32, 47, 10, 39, 83, 84, 79, - 82, 65, 71, 69, 39, 32, 47, 10, 39, 83, 84, 79, 82, 69, 68, 39, 32, 47, 10, 39, - 83, 84, 82, 73, 67, 84, 39, 32, 47, 10, 39, 83, 84, 82, 73, 80, 39, 32, 47, 10, - 39, 83, 85, 66, 83, 67, 82, 73, 80, 84, 73, 79, 78, 39, 32, 47, 10, 39, 83, 85, - 66, 83, 69, 84, 39, 32, 47, 10, 39, 83, 89, 83, 73, 68, 39, 32, 47, 10, 39, 83, - 89, 83, 84, 69, 77, 39, 32, 47, 10, 39, 84, 65, 66, 76, 69, 83, 39, 32, 47, 10, - 39, 84, 65, 66, 76, 69, 83, 80, 65, 67, 69, 39, 32, 47, 10, 39, 84, 65, 82, 71, - 69, 84, 39, 32, 47, 10, 39, 84, 69, 77, 80, 39, 32, 47, 10, 39, 84, 69, 77, 80, - 76, 65, 84, 69, 39, 32, 47, 10, 39, 84, 69, 77, 80, 79, 82, 65, 82, 89, 39, 32, - 47, 10, 39, 84, 69, 88, 84, 39, 32, 47, 10, 39, 84, 73, 69, 83, 39, 32, 47, 10, - 39, 84, 82, 65, 78, 83, 65, 67, 84, 73, 79, 78, 39, 32, 47, 10, 39, 84, 82, 65, - 78, 83, 70, 79, 82, 77, 39, 32, 47, 10, 39, 84, 82, 73, 71, 71, 69, 82, 39, 32, - 47, 10, 39, 84, 82, 85, 78, 67, 65, 84, 69, 39, 32, 47, 10, 39, 84, 82, 85, 83, - 84, 69, 68, 39, 32, 47, 10, 39, 84, 89, 80, 69, 39, 32, 47, 10, 39, 84, 89, 80, - 69, 83, 39, 32, 47, 10, 39, 85, 78, 66, 79, 85, 78, 68, 69, 68, 39, 32, 47, 10, - 39, 85, 78, 67, 79, 77, 77, 73, 84, 84, 69, 68, 39, 32, 47, 10, 39, 85, 78, 69, - 78, 67, 82, 89, 80, 84, 69, 68, 39, 32, 47, 10, 39, 85, 78, 75, 78, 79, 87, 78, - 39, 32, 47, 10, 39, 85, 78, 76, 73, 83, 84, 69, 78, 39, 32, 47, 10, 39, 85, 78, - 76, 79, 71, 71, 69, 68, 39, 32, 47, 10, 39, 85, 78, 84, 73, 76, 39, 32, 47, 10, - 39, 85, 80, 68, 65, 84, 69, 39, 32, 47, 10, 39, 85, 83, 69, 39, 32, 47, 10, 39, - 85, 83, 69, 82, 39, 32, 47, 10, 39, 86, 65, 67, 85, 85, 77, 39, 32, 47, 10, 39, - 86, 65, 76, 73, 68, 39, 32, 47, 10, 39, 86, 65, 76, 73, 68, 65, 84, 69, 39, 32, - 47, 10, 39, 86, 65, 76, 73, 68, 65, 84, 79, 82, 39, 32, 47, 10, 39, 86, 65, 76, - 85, 69, 39, 32, 47, 10, 39, 86, 65, 82, 73, 65, 66, 76, 69, 39, 32, 47, 10, 39, - 86, 65, 82, 89, 73, 78, 71, 39, 32, 47, 10, 39, 86, 69, 82, 83, 73, 79, 78, 39, - 32, 47, 10, 39, 86, 73, 69, 87, 39, 32, 47, 10, 39, 86, 73, 69, 87, 83, 39, 32, - 47, 10, 39, 86, 73, 82, 84, 85, 65, 76, 39, 32, 47, 10, 39, 86, 79, 76, 65, 84, - 73, 76, 69, 39, 32, 47, 10, 39, 87, 69, 69, 75, 39, 32, 47, 10, 39, 87, 69, 69, - 75, 83, 39, 32, 47, 10, 39, 87, 72, 73, 84, 69, 83, 80, 65, 67, 69, 39, 32, 47, - 10, 39, 87, 73, 84, 72, 73, 78, 39, 32, 47, 10, 39, 87, 73, 84, 72, 79, 85, 84, - 39, 32, 47, 10, 39, 87, 79, 82, 75, 39, 32, 47, 10, 39, 87, 82, 65, 80, 80, 69, - 82, 39, 32, 47, 10, 39, 87, 82, 73, 84, 69, 39, 32, 47, 10, 39, 88, 77, 76, 39, - 32, 47, 10, 39, 89, 69, 65, 82, 39, 32, 47, 10, 39, 89, 69, 65, 82, 83, 39, 32, - 47, 10, 39, 89, 69, 83, 39, 32, 47, 10, 39, 90, 79, 78, 69, 39, 10, 65, 108, 116, - 101, 114, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 65, 76, 84, 69, - 82, 39, 32, 65, 108, 116, 101, 114, 79, 112, 116, 105, 111, 110, 115, 10, 65, 108, 116, 101, - 114, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 65, 108, 116, 101, 114, 84, 97, 98, - 108, 101, 83, 116, 109, 116, 32, 47, 32, 65, 108, 116, 101, 114, 86, 105, 101, 119, 83, 116, - 109, 116, 32, 47, 32, 65, 108, 116, 101, 114, 83, 101, 113, 117, 101, 110, 99, 101, 83, 116, - 109, 116, 32, 47, 32, 65, 108, 116, 101, 114, 68, 97, 116, 97, 98, 97, 115, 101, 83, 116, - 109, 116, 32, 47, 32, 65, 108, 116, 101, 114, 83, 99, 104, 101, 109, 97, 83, 116, 109, 116, - 10, 65, 108, 116, 101, 114, 84, 97, 98, 108, 101, 83, 116, 109, 116, 32, 60, 45, 32, 39, - 84, 65, 66, 76, 69, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 66, 97, 115, - 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 76, 105, 115, 116, 40, 65, 108, 116, 101, - 114, 84, 97, 98, 108, 101, 79, 112, 116, 105, 111, 110, 115, 41, 10, 65, 108, 116, 101, 114, - 83, 99, 104, 101, 109, 97, 83, 116, 109, 116, 32, 60, 45, 32, 39, 83, 67, 72, 69, 77, - 65, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 81, 117, 97, 108, 105, 102, 105, - 101, 100, 78, 97, 109, 101, 32, 82, 101, 110, 97, 109, 101, 65, 108, 116, 101, 114, 10, 65, - 108, 116, 101, 114, 84, 97, 98, 108, 101, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, - 65, 100, 100, 67, 111, 108, 117, 109, 110, 32, 47, 32, 68, 114, 111, 112, 67, 111, 110, 115, - 116, 114, 97, 105, 110, 116, 32, 47, 32, 68, 114, 111, 112, 67, 111, 108, 117, 109, 110, 32, - 47, 32, 65, 108, 116, 101, 114, 67, 111, 108, 117, 109, 110, 32, 47, 32, 65, 100, 100, 67, - 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 47, 32, 67, 104, 97, 110, 103, 101, 78, 117, - 108, 108, 97, 98, 105, 108, 105, 116, 121, 32, 47, 10, 32, 32, 32, 32, 32, 32, 32, 32, - 82, 101, 110, 97, 109, 101, 67, 111, 108, 117, 109, 110, 32, 47, 32, 82, 101, 110, 97, 109, - 101, 65, 108, 116, 101, 114, 32, 47, 32, 83, 101, 116, 80, 97, 114, 116, 105, 116, 105, 111, - 110, 101, 100, 66, 121, 32, 47, 32, 82, 101, 115, 101, 116, 80, 97, 114, 116, 105, 116, 105, - 111, 110, 101, 100, 66, 121, 32, 47, 32, 83, 101, 116, 83, 111, 114, 116, 101, 100, 66, 121, - 32, 47, 32, 82, 101, 115, 101, 116, 83, 111, 114, 116, 101, 100, 66, 121, 32, 47, 32, 83, - 101, 116, 79, 112, 116, 105, 111, 110, 115, 32, 47, 32, 82, 101, 115, 101, 116, 79, 112, 116, - 105, 111, 110, 115, 10, 65, 100, 100, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, - 45, 32, 39, 65, 68, 68, 39, 32, 84, 111, 112, 76, 101, 118, 101, 108, 67, 111, 110, 115, - 116, 114, 97, 105, 110, 116, 10, 68, 114, 111, 112, 67, 111, 110, 115, 116, 114, 97, 105, 110, - 116, 32, 60, 45, 32, 39, 68, 82, 79, 80, 39, 32, 39, 67, 79, 78, 83, 84, 82, 65, - 73, 78, 84, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 73, 100, 101, 110, 116, - 105, 102, 105, 101, 114, 32, 68, 114, 111, 112, 66, 101, 104, 97, 118, 105, 111, 114, 63, 10, - 65, 100, 100, 67, 111, 108, 117, 109, 110, 32, 60, 45, 32, 39, 65, 68, 68, 39, 32, 39, - 67, 79, 76, 85, 77, 78, 39, 63, 32, 73, 102, 78, 111, 116, 69, 120, 105, 115, 116, 115, - 63, 32, 65, 100, 100, 67, 111, 108, 117, 109, 110, 69, 110, 116, 114, 121, 10, 65, 100, 100, - 67, 111, 108, 117, 109, 110, 69, 110, 116, 114, 121, 32, 60, 45, 32, 68, 111, 116, 116, 101, - 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 84, 121, 112, 101, 63, 32, 71, 101, - 110, 101, 114, 97, 116, 101, 100, 67, 111, 108, 117, 109, 110, 63, 32, 67, 111, 108, 117, 109, - 110, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 42, 10, 68, 114, 111, 112, 67, 111, 108, - 117, 109, 110, 32, 60, 45, 32, 39, 68, 82, 79, 80, 39, 32, 39, 67, 79, 76, 85, 77, - 78, 39, 63, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 78, 101, 115, 116, 101, 100, - 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 68, 114, 111, 112, 66, 101, 104, 97, 118, - 105, 111, 114, 63, 10, 65, 108, 116, 101, 114, 67, 111, 108, 117, 109, 110, 32, 60, 45, 32, - 39, 65, 76, 84, 69, 82, 39, 32, 39, 67, 79, 76, 85, 77, 78, 39, 63, 32, 78, 101, - 115, 116, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 65, 108, 116, 101, 114, - 67, 111, 108, 117, 109, 110, 69, 110, 116, 114, 121, 10, 82, 101, 110, 97, 109, 101, 67, 111, - 108, 117, 109, 110, 32, 60, 45, 32, 39, 82, 69, 78, 65, 77, 69, 39, 32, 39, 67, 79, - 76, 85, 77, 78, 39, 63, 32, 78, 101, 115, 116, 101, 100, 67, 111, 108, 117, 109, 110, 78, - 97, 109, 101, 32, 39, 84, 79, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, - 78, 101, 115, 116, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 60, 45, 32, - 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 68, 111, 116, 42, 32, 67, 111, 108, 117, 109, - 110, 78, 97, 109, 101, 10, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 68, 111, 116, 32, - 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 39, 46, 39, 10, 82, 101, - 110, 97, 109, 101, 65, 108, 116, 101, 114, 32, 60, 45, 32, 39, 82, 69, 78, 65, 77, 69, - 39, 32, 39, 84, 79, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 83, 101, - 116, 80, 97, 114, 116, 105, 116, 105, 111, 110, 101, 100, 66, 121, 32, 60, 45, 32, 39, 83, - 69, 84, 39, 32, 39, 80, 65, 82, 84, 73, 84, 73, 79, 78, 69, 68, 39, 32, 39, 66, - 89, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 41, 41, 10, 82, 101, 115, 101, 116, 80, 97, 114, 116, 105, 116, 105, - 111, 110, 101, 100, 66, 121, 32, 60, 45, 32, 39, 82, 69, 83, 69, 84, 39, 32, 39, 80, - 65, 82, 84, 73, 84, 73, 79, 78, 69, 68, 39, 32, 39, 66, 89, 39, 10, 83, 101, 116, - 83, 111, 114, 116, 101, 100, 66, 121, 32, 60, 45, 32, 39, 83, 69, 84, 39, 32, 39, 83, - 79, 82, 84, 69, 68, 39, 32, 39, 66, 89, 39, 32, 80, 97, 114, 101, 110, 115, 40, 79, - 114, 100, 101, 114, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 115, 41, 10, 82, - 101, 115, 101, 116, 83, 111, 114, 116, 101, 100, 66, 121, 32, 60, 45, 32, 39, 82, 69, 83, - 69, 84, 39, 32, 39, 83, 79, 82, 84, 69, 68, 39, 32, 39, 66, 89, 39, 10, 83, 101, - 116, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 39, 83, 69, 84, 39, 32, 82, 101, - 108, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 10, 82, 101, 115, 101, 116, 79, 112, 116, - 105, 111, 110, 115, 32, 60, 45, 32, 39, 82, 69, 83, 69, 84, 39, 32, 82, 101, 108, 79, - 112, 116, 105, 111, 110, 76, 105, 115, 116, 10, 65, 108, 116, 101, 114, 67, 111, 108, 117, 109, - 110, 69, 110, 116, 114, 121, 32, 60, 45, 32, 65, 100, 100, 79, 114, 68, 114, 111, 112, 68, - 101, 102, 97, 117, 108, 116, 32, 47, 32, 67, 104, 97, 110, 103, 101, 78, 117, 108, 108, 97, - 98, 105, 108, 105, 116, 121, 32, 47, 32, 65, 108, 116, 101, 114, 84, 121, 112, 101, 10, 65, - 100, 100, 79, 114, 68, 114, 111, 112, 68, 101, 102, 97, 117, 108, 116, 32, 60, 45, 32, 65, - 100, 100, 68, 101, 102, 97, 117, 108, 116, 32, 47, 32, 68, 114, 111, 112, 68, 101, 102, 97, - 117, 108, 116, 10, 65, 100, 100, 68, 101, 102, 97, 117, 108, 116, 32, 60, 45, 32, 39, 83, - 69, 84, 39, 32, 39, 68, 69, 70, 65, 85, 76, 84, 39, 32, 69, 120, 112, 114, 101, 115, - 115, 105, 111, 110, 10, 68, 114, 111, 112, 68, 101, 102, 97, 117, 108, 116, 32, 60, 45, 32, - 39, 68, 82, 79, 80, 39, 32, 39, 68, 69, 70, 65, 85, 76, 84, 39, 10, 67, 104, 97, - 110, 103, 101, 78, 117, 108, 108, 97, 98, 105, 108, 105, 116, 121, 32, 60, 45, 32, 68, 114, - 111, 112, 79, 114, 83, 101, 116, 32, 39, 78, 79, 84, 39, 32, 39, 78, 85, 76, 76, 39, - 10, 68, 114, 111, 112, 79, 114, 83, 101, 116, 32, 60, 45, 32, 68, 114, 111, 112, 78, 117, - 108, 108, 97, 98, 105, 108, 105, 116, 121, 32, 47, 32, 83, 101, 116, 78, 117, 108, 108, 97, - 98, 105, 108, 105, 116, 121, 10, 68, 114, 111, 112, 78, 117, 108, 108, 97, 98, 105, 108, 105, - 116, 121, 32, 60, 45, 32, 39, 68, 82, 79, 80, 39, 10, 83, 101, 116, 78, 117, 108, 108, - 97, 98, 105, 108, 105, 116, 121, 32, 60, 45, 32, 39, 83, 69, 84, 39, 10, 65, 108, 116, - 101, 114, 84, 121, 112, 101, 32, 60, 45, 32, 83, 101, 116, 68, 97, 116, 97, 63, 32, 39, - 84, 89, 80, 69, 39, 32, 84, 121, 112, 101, 63, 32, 85, 115, 105, 110, 103, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 63, 10, 83, 101, 116, 68, 97, 116, 97, 32, 60, 45, 32, - 39, 83, 69, 84, 39, 32, 39, 68, 65, 84, 65, 39, 63, 10, 85, 115, 105, 110, 103, 69, - 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 85, 83, 73, 78, 71, 39, - 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 65, 108, 116, 101, 114, 86, 105, 101, - 119, 83, 116, 109, 116, 32, 60, 45, 32, 39, 86, 73, 69, 87, 39, 32, 73, 102, 69, 120, - 105, 115, 116, 115, 63, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, - 82, 101, 110, 97, 109, 101, 65, 108, 116, 101, 114, 10, 65, 108, 116, 101, 114, 83, 101, 113, - 117, 101, 110, 99, 101, 83, 116, 109, 116, 32, 60, 45, 32, 39, 83, 69, 81, 85, 69, 78, - 67, 69, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 81, 117, 97, 108, 105, 102, - 105, 101, 100, 83, 101, 113, 117, 101, 110, 99, 101, 78, 97, 109, 101, 32, 65, 108, 116, 101, - 114, 83, 101, 113, 117, 101, 110, 99, 101, 79, 112, 116, 105, 111, 110, 115, 10, 81, 117, 97, - 108, 105, 102, 105, 101, 100, 83, 101, 113, 117, 101, 110, 99, 101, 78, 97, 109, 101, 32, 60, - 45, 32, 67, 97, 116, 97, 108, 111, 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, - 111, 110, 63, 32, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, - 105, 111, 110, 63, 32, 83, 101, 113, 117, 101, 110, 99, 101, 78, 97, 109, 101, 10, 65, 108, - 116, 101, 114, 83, 101, 113, 117, 101, 110, 99, 101, 79, 112, 116, 105, 111, 110, 115, 32, 60, - 45, 32, 82, 101, 110, 97, 109, 101, 65, 108, 116, 101, 114, 83, 101, 113, 117, 101, 110, 99, - 101, 79, 112, 116, 105, 111, 110, 115, 32, 47, 32, 83, 101, 116, 83, 101, 113, 117, 101, 110, - 99, 101, 79, 112, 116, 105, 111, 110, 10, 82, 101, 110, 97, 109, 101, 65, 108, 116, 101, 114, - 83, 101, 113, 117, 101, 110, 99, 101, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 82, - 101, 110, 97, 109, 101, 65, 108, 116, 101, 114, 10, 83, 101, 116, 83, 101, 113, 117, 101, 110, - 99, 101, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 83, 101, 113, 117, 101, 110, 99, 101, - 79, 112, 116, 105, 111, 110, 43, 10, 65, 108, 116, 101, 114, 68, 97, 116, 97, 98, 97, 115, - 101, 83, 116, 109, 116, 32, 60, 45, 32, 39, 68, 65, 84, 65, 66, 65, 83, 69, 39, 32, - 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, - 32, 39, 83, 69, 84, 39, 32, 39, 65, 76, 73, 65, 83, 39, 32, 39, 84, 79, 39, 32, - 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 65, 110, 97, 108, 121, 122, 101, 83, 116, - 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 65, 110, 97, 108, 121, 122, 101, 75, 101, - 121, 119, 111, 114, 100, 32, 65, 110, 97, 108, 121, 122, 101, 86, 101, 114, 98, 111, 115, 101, - 63, 32, 65, 110, 97, 108, 121, 122, 101, 84, 97, 114, 103, 101, 116, 63, 10, 65, 110, 97, - 108, 121, 122, 101, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 66, 97, 115, 101, 84, 97, - 98, 108, 101, 78, 97, 109, 101, 32, 78, 97, 109, 101, 76, 105, 115, 116, 63, 10, 65, 110, - 97, 108, 121, 122, 101, 86, 101, 114, 98, 111, 115, 101, 32, 60, 45, 32, 39, 86, 69, 82, - 66, 79, 83, 69, 39, 10, 65, 116, 116, 97, 99, 104, 83, 116, 97, 116, 101, 109, 101, 110, - 116, 32, 60, 45, 32, 39, 65, 84, 84, 65, 67, 72, 39, 32, 79, 114, 82, 101, 112, 108, - 97, 99, 101, 63, 32, 73, 102, 78, 111, 116, 69, 120, 105, 115, 116, 115, 63, 32, 68, 97, - 116, 97, 98, 97, 115, 101, 63, 32, 68, 97, 116, 97, 98, 97, 115, 101, 80, 97, 116, 104, - 32, 65, 116, 116, 97, 99, 104, 65, 108, 105, 97, 115, 63, 32, 65, 116, 116, 97, 99, 104, - 79, 112, 116, 105, 111, 110, 115, 63, 10, 68, 97, 116, 97, 98, 97, 115, 101, 32, 60, 45, - 32, 39, 68, 65, 84, 65, 66, 65, 83, 69, 39, 10, 68, 97, 116, 97, 98, 97, 115, 101, - 80, 97, 116, 104, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 65, - 116, 116, 97, 99, 104, 65, 108, 105, 97, 115, 32, 60, 45, 32, 39, 65, 83, 39, 32, 67, - 111, 108, 73, 100, 10, 65, 116, 116, 97, 99, 104, 79, 112, 116, 105, 111, 110, 115, 32, 60, - 45, 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 76, - 105, 115, 116, 10, 67, 97, 108, 108, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, - 32, 39, 67, 65, 76, 76, 39, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, - 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 84, 97, 98, 108, 101, 70, 117, 110, 99, - 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 10, 67, 104, 101, 99, 107, 112, - 111, 105, 110, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 67, 104, 101, - 99, 107, 112, 111, 105, 110, 116, 70, 111, 114, 99, 101, 63, 32, 39, 67, 72, 69, 67, 75, - 80, 79, 73, 78, 84, 39, 32, 67, 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, 63, 10, - 67, 104, 101, 99, 107, 112, 111, 105, 110, 116, 70, 111, 114, 99, 101, 32, 60, 45, 32, 39, - 70, 79, 82, 67, 69, 39, 10, 67, 111, 109, 109, 101, 110, 116, 83, 116, 97, 116, 101, 109, - 101, 110, 116, 32, 60, 45, 32, 39, 67, 79, 77, 77, 69, 78, 84, 39, 32, 39, 79, 78, - 39, 32, 67, 111, 109, 109, 101, 110, 116, 79, 110, 84, 121, 112, 101, 32, 68, 111, 116, 116, - 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 39, 73, 83, 39, 32, 67, 111, - 109, 109, 101, 110, 116, 86, 97, 108, 117, 101, 10, 67, 111, 109, 109, 101, 110, 116, 79, 110, - 84, 121, 112, 101, 32, 60, 45, 32, 32, 32, 32, 67, 111, 109, 109, 101, 110, 116, 84, 97, - 98, 108, 101, 32, 47, 32, 67, 111, 109, 109, 101, 110, 116, 83, 101, 113, 117, 101, 110, 99, - 101, 32, 47, 32, 67, 111, 109, 109, 101, 110, 116, 70, 117, 110, 99, 116, 105, 111, 110, 32, - 47, 32, 67, 111, 109, 109, 101, 110, 116, 77, 97, 99, 114, 111, 84, 97, 98, 108, 101, 32, - 47, 32, 67, 111, 109, 109, 101, 110, 116, 77, 97, 99, 114, 111, 32, 47, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 67, 111, 109, - 109, 101, 110, 116, 86, 105, 101, 119, 32, 47, 32, 67, 111, 109, 109, 101, 110, 116, 68, 97, - 116, 97, 98, 97, 115, 101, 32, 47, 32, 67, 111, 109, 109, 101, 110, 116, 73, 110, 100, 101, - 120, 32, 47, 32, 67, 111, 109, 109, 101, 110, 116, 83, 99, 104, 101, 109, 97, 32, 47, 32, - 67, 111, 109, 109, 101, 110, 116, 84, 121, 112, 101, 32, 47, 32, 67, 111, 109, 109, 101, 110, - 116, 67, 111, 108, 117, 109, 110, 10, 67, 111, 109, 109, 101, 110, 116, 84, 97, 98, 108, 101, - 32, 60, 45, 32, 39, 84, 65, 66, 76, 69, 39, 10, 67, 111, 109, 109, 101, 110, 116, 83, - 101, 113, 117, 101, 110, 99, 101, 32, 60, 45, 32, 39, 83, 69, 81, 85, 69, 78, 67, 69, - 39, 10, 67, 111, 109, 109, 101, 110, 116, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, - 32, 39, 70, 85, 78, 67, 84, 73, 79, 78, 39, 10, 67, 111, 109, 109, 101, 110, 116, 77, - 97, 99, 114, 111, 84, 97, 98, 108, 101, 32, 60, 45, 32, 39, 77, 65, 67, 82, 79, 39, - 32, 39, 84, 65, 66, 76, 69, 39, 10, 67, 111, 109, 109, 101, 110, 116, 77, 97, 99, 114, - 111, 32, 60, 45, 32, 39, 77, 65, 67, 82, 79, 39, 10, 67, 111, 109, 109, 101, 110, 116, - 86, 105, 101, 119, 32, 60, 45, 32, 39, 86, 73, 69, 87, 39, 10, 67, 111, 109, 109, 101, - 110, 116, 68, 97, 116, 97, 98, 97, 115, 101, 32, 60, 45, 32, 39, 68, 65, 84, 65, 66, - 65, 83, 69, 39, 10, 67, 111, 109, 109, 101, 110, 116, 73, 110, 100, 101, 120, 32, 60, 45, - 32, 39, 73, 78, 68, 69, 88, 39, 10, 67, 111, 109, 109, 101, 110, 116, 83, 99, 104, 101, - 109, 97, 32, 60, 45, 32, 39, 83, 67, 72, 69, 77, 65, 39, 10, 67, 111, 109, 109, 101, - 110, 116, 84, 121, 112, 101, 32, 60, 45, 32, 39, 84, 89, 80, 69, 39, 10, 67, 111, 109, - 109, 101, 110, 116, 67, 111, 108, 117, 109, 110, 32, 60, 45, 32, 39, 67, 79, 76, 85, 77, - 78, 39, 10, 67, 111, 109, 109, 101, 110, 116, 86, 97, 108, 117, 101, 32, 60, 45, 32, 78, - 117, 108, 108, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 83, 116, 114, 105, 110, 103, 76, - 105, 116, 101, 114, 97, 108, 86, 97, 108, 117, 101, 10, 83, 116, 114, 105, 110, 103, 76, 105, - 116, 101, 114, 97, 108, 86, 97, 108, 117, 101, 32, 60, 45, 32, 83, 116, 114, 105, 110, 103, - 76, 105, 116, 101, 114, 97, 108, 10, 67, 111, 110, 110, 101, 99, 116, 83, 116, 97, 116, 101, - 109, 101, 110, 116, 32, 60, 45, 32, 39, 67, 79, 78, 78, 69, 67, 84, 39, 32, 83, 101, - 115, 115, 105, 111, 110, 84, 97, 114, 103, 101, 116, 63, 10, 68, 105, 115, 99, 111, 110, 110, - 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 68, 73, 83, - 67, 79, 78, 78, 69, 67, 84, 39, 10, 83, 101, 115, 115, 105, 111, 110, 84, 97, 114, 103, - 101, 116, 32, 60, 45, 32, 76, 111, 99, 97, 108, 83, 101, 115, 115, 105, 111, 110, 84, 97, - 114, 103, 101, 116, 32, 47, 32, 83, 116, 114, 105, 110, 103, 83, 101, 115, 115, 105, 111, 110, - 84, 97, 114, 103, 101, 116, 32, 47, 32, 67, 97, 116, 97, 108, 111, 103, 83, 101, 115, 115, - 105, 111, 110, 84, 97, 114, 103, 101, 116, 10, 76, 111, 99, 97, 108, 83, 101, 115, 115, 105, - 111, 110, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 39, 76, 79, 67, 65, 76, 39, 10, - 83, 116, 114, 105, 110, 103, 83, 101, 115, 115, 105, 111, 110, 84, 97, 114, 103, 101, 116, 32, - 60, 45, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 71, 101, 110, - 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 63, 10, - 67, 97, 116, 97, 108, 111, 103, 83, 101, 115, 115, 105, 111, 110, 84, 97, 114, 103, 101, 116, - 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, 10, 67, 111, 112, 121, - 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 67, 79, 80, 89, 39, 32, - 67, 111, 112, 121, 86, 97, 114, 105, 97, 116, 105, 111, 110, 115, 10, 67, 111, 112, 121, 86, - 97, 114, 105, 97, 116, 105, 111, 110, 115, 32, 60, 45, 32, 67, 111, 112, 121, 84, 97, 98, - 108, 101, 32, 47, 32, 67, 111, 112, 121, 83, 101, 108, 101, 99, 116, 32, 47, 32, 67, 111, - 112, 121, 70, 114, 111, 109, 68, 97, 116, 97, 98, 97, 115, 101, 10, 67, 111, 112, 121, 84, - 97, 98, 108, 101, 32, 60, 45, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, - 101, 32, 73, 110, 115, 101, 114, 116, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 63, 32, - 70, 114, 111, 109, 79, 114, 84, 111, 32, 67, 111, 112, 121, 70, 105, 108, 101, 78, 97, 109, - 101, 32, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 115, 63, 10, 70, 114, 111, 109, 79, - 114, 84, 111, 32, 60, 45, 32, 67, 111, 112, 121, 70, 114, 111, 109, 32, 47, 32, 67, 111, - 112, 121, 84, 111, 10, 67, 111, 112, 121, 70, 114, 111, 109, 32, 60, 45, 32, 39, 70, 82, - 79, 77, 39, 10, 67, 111, 112, 121, 84, 111, 32, 60, 45, 32, 39, 84, 79, 39, 10, 67, - 111, 112, 121, 83, 101, 108, 101, 99, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, - 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, - 110, 97, 108, 41, 32, 39, 84, 79, 39, 32, 67, 111, 112, 121, 70, 105, 108, 101, 78, 97, - 109, 101, 32, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 115, 63, 10, 67, 111, 112, 121, - 70, 105, 108, 101, 78, 97, 109, 101, 32, 60, 45, 32, 67, 111, 112, 121, 70, 105, 108, 101, - 78, 97, 109, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 67, 111, 112, - 121, 70, 105, 108, 101, 78, 97, 109, 101, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, - 97, 108, 32, 47, 32, 67, 111, 112, 121, 70, 105, 108, 101, 78, 97, 109, 101, 73, 100, 101, - 110, 116, 105, 102, 105, 101, 114, 32, 47, 32, 67, 111, 112, 121, 70, 105, 108, 101, 78, 97, - 109, 101, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 67, 111, 108, 73, 100, 10, 67, 111, - 112, 121, 70, 105, 108, 101, 78, 97, 109, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, - 32, 60, 45, 32, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 47, 32, 80, 97, 114, 101, - 110, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 67, 111, 112, 121, 70, 105, 108, - 101, 78, 97, 109, 101, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 60, - 45, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, 67, 111, 112, 121, - 70, 105, 108, 101, 78, 97, 109, 101, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, - 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 67, 111, 112, 121, 70, 105, 108, - 101, 78, 97, 109, 101, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 67, 111, 108, 73, 100, - 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 67, 111, 108, 73, 100, 10, - 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 67, 111, 108, 73, 100, 32, 60, 45, 32, 73, - 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 39, 46, 39, 32, 67, 111, 108, 73, 100, 10, - 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 39, 87, 73, 84, 72, - 39, 63, 32, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 10, 67, 111, - 112, 121, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 67, 111, 112, 121, - 71, 101, 110, 101, 114, 105, 99, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 32, 47, 32, - 83, 112, 101, 99, 105, 97, 108, 105, 122, 101, 100, 79, 112, 116, 105, 111, 110, 76, 105, 115, - 116, 10, 83, 112, 101, 99, 105, 97, 108, 105, 122, 101, 100, 79, 112, 116, 105, 111, 110, 76, - 105, 115, 116, 32, 60, 45, 32, 83, 112, 101, 99, 105, 97, 108, 105, 122, 101, 100, 79, 112, - 116, 105, 111, 110, 32, 83, 112, 101, 99, 105, 97, 108, 105, 122, 101, 100, 79, 112, 116, 105, - 111, 110, 84, 97, 105, 108, 42, 10, 83, 112, 101, 99, 105, 97, 108, 105, 122, 101, 100, 79, - 112, 116, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 39, 44, 39, 63, 32, 83, 112, - 101, 99, 105, 97, 108, 105, 122, 101, 100, 79, 112, 116, 105, 111, 110, 10, 83, 112, 101, 99, - 105, 97, 108, 105, 122, 101, 100, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 83, 105, 110, - 103, 108, 101, 79, 112, 116, 105, 111, 110, 32, 47, 32, 78, 117, 108, 108, 65, 115, 79, 112, - 116, 105, 111, 110, 32, 47, 32, 68, 101, 108, 105, 109, 105, 116, 101, 114, 65, 115, 79, 112, - 116, 105, 111, 110, 32, 47, 32, 69, 115, 99, 97, 112, 101, 65, 115, 79, 112, 116, 105, 111, - 110, 32, 47, 10, 9, 69, 110, 99, 111, 100, 105, 110, 103, 79, 112, 116, 105, 111, 110, 32, - 47, 32, 81, 117, 111, 116, 101, 65, 115, 79, 112, 116, 105, 111, 110, 32, 47, 32, 70, 111, - 114, 99, 101, 81, 117, 111, 116, 101, 79, 112, 116, 105, 111, 110, 32, 47, 32, 80, 97, 114, - 116, 105, 116, 105, 111, 110, 66, 121, 79, 112, 116, 105, 111, 110, 32, 47, 32, 70, 111, 114, - 99, 101, 78, 117, 108, 108, 79, 112, 116, 105, 111, 110, 10, 83, 105, 110, 103, 108, 101, 79, - 112, 116, 105, 111, 110, 32, 60, 45, 32, 66, 105, 110, 97, 114, 121, 79, 112, 116, 105, 111, - 110, 32, 47, 32, 70, 114, 101, 101, 122, 101, 79, 112, 116, 105, 111, 110, 32, 47, 32, 79, - 105, 100, 115, 79, 112, 116, 105, 111, 110, 32, 47, 32, 67, 115, 118, 79, 112, 116, 105, 111, - 110, 32, 47, 32, 72, 101, 97, 100, 101, 114, 79, 112, 116, 105, 111, 110, 10, 66, 105, 110, - 97, 114, 121, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 39, 66, 73, 78, 65, 82, 89, - 39, 10, 70, 114, 101, 101, 122, 101, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 39, 70, - 82, 69, 69, 90, 69, 39, 10, 79, 105, 100, 115, 79, 112, 116, 105, 111, 110, 32, 60, 45, - 32, 39, 79, 73, 68, 83, 39, 10, 67, 115, 118, 79, 112, 116, 105, 111, 110, 32, 60, 45, - 32, 39, 67, 83, 86, 39, 10, 72, 101, 97, 100, 101, 114, 79, 112, 116, 105, 111, 110, 32, - 60, 45, 32, 39, 72, 69, 65, 68, 69, 82, 39, 10, 78, 117, 108, 108, 65, 115, 79, 112, - 116, 105, 111, 110, 32, 60, 45, 32, 39, 78, 85, 76, 76, 39, 32, 39, 65, 83, 39, 63, - 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, 68, 101, 108, 105, 109, - 105, 116, 101, 114, 65, 115, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 39, 68, 69, 76, - 73, 77, 73, 84, 69, 82, 39, 32, 39, 65, 83, 39, 63, 32, 83, 116, 114, 105, 110, 103, - 76, 105, 116, 101, 114, 97, 108, 10, 81, 117, 111, 116, 101, 65, 115, 79, 112, 116, 105, 111, - 110, 32, 60, 45, 32, 39, 81, 85, 79, 84, 69, 39, 32, 39, 65, 83, 39, 63, 32, 83, - 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, 69, 115, 99, 97, 112, 101, 65, - 115, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 39, 69, 83, 67, 65, 80, 69, 39, 32, - 39, 65, 83, 39, 63, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, - 69, 110, 99, 111, 100, 105, 110, 103, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 39, 69, - 78, 67, 79, 68, 73, 78, 71, 39, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, - 97, 108, 10, 70, 111, 114, 99, 101, 81, 117, 111, 116, 101, 79, 112, 116, 105, 111, 110, 32, - 60, 45, 32, 70, 111, 114, 99, 101, 81, 117, 111, 116, 101, 63, 32, 39, 81, 85, 79, 84, - 69, 39, 32, 83, 116, 97, 114, 83, 121, 109, 98, 111, 108, 67, 111, 108, 117, 109, 110, 76, - 105, 115, 116, 10, 83, 116, 97, 114, 83, 121, 109, 98, 111, 108, 67, 111, 108, 117, 109, 110, - 76, 105, 115, 116, 32, 60, 45, 32, 83, 116, 97, 114, 83, 121, 109, 98, 111, 108, 32, 47, - 32, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 10, 70, 111, 114, 99, 101, 81, 117, 111, - 116, 101, 32, 60, 45, 32, 39, 70, 79, 82, 67, 69, 39, 10, 80, 97, 114, 116, 105, 116, - 105, 111, 110, 66, 121, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 40, 39, 80, 65, 82, - 84, 73, 84, 73, 79, 78, 39, 32, 47, 32, 39, 80, 65, 82, 84, 73, 84, 73, 79, 78, - 69, 68, 39, 41, 32, 39, 66, 89, 39, 32, 80, 97, 114, 116, 105, 116, 105, 111, 110, 66, - 121, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 10, 80, 97, 114, 116, 105, 116, 105, 111, - 110, 66, 121, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, 83, 116, 97, - 114, 80, 97, 114, 116, 105, 116, 105, 111, 110, 66, 121, 67, 111, 108, 117, 109, 110, 76, 105, - 115, 116, 32, 47, 32, 80, 97, 114, 101, 110, 116, 104, 101, 115, 105, 122, 101, 100, 80, 97, - 114, 116, 105, 116, 105, 111, 110, 66, 121, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, - 47, 32, 83, 105, 110, 103, 108, 101, 80, 97, 114, 116, 105, 116, 105, 111, 110, 66, 121, 67, - 111, 108, 117, 109, 110, 76, 105, 115, 116, 10, 83, 116, 97, 114, 80, 97, 114, 116, 105, 116, - 105, 111, 110, 66, 121, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, 83, - 116, 97, 114, 83, 121, 109, 98, 111, 108, 10, 80, 97, 114, 101, 110, 116, 104, 101, 115, 105, - 122, 101, 100, 80, 97, 114, 116, 105, 116, 105, 111, 110, 66, 121, 67, 111, 108, 117, 109, 110, - 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 67, 111, 108, 117, 109, - 110, 76, 105, 115, 116, 41, 10, 83, 105, 110, 103, 108, 101, 80, 97, 114, 116, 105, 116, 105, - 111, 110, 66, 121, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, 67, 111, - 108, 73, 100, 10, 70, 111, 114, 99, 101, 78, 117, 108, 108, 79, 112, 116, 105, 111, 110, 32, - 60, 45, 32, 39, 70, 79, 82, 67, 69, 39, 32, 70, 111, 114, 99, 101, 78, 111, 116, 78, - 117, 108, 108, 63, 32, 39, 78, 85, 76, 76, 39, 32, 67, 111, 108, 117, 109, 110, 76, 105, - 115, 116, 10, 70, 111, 114, 99, 101, 78, 111, 116, 78, 117, 108, 108, 32, 60, 45, 32, 39, - 78, 79, 84, 39, 10, 83, 116, 97, 114, 83, 121, 109, 98, 111, 108, 32, 60, 45, 32, 39, - 42, 39, 10, 67, 111, 112, 121, 71, 101, 110, 101, 114, 105, 99, 79, 112, 116, 105, 111, 110, - 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, - 67, 111, 112, 121, 71, 101, 110, 101, 114, 105, 99, 79, 112, 116, 105, 111, 110, 41, 41, 10, - 67, 111, 112, 121, 71, 101, 110, 101, 114, 105, 99, 79, 112, 116, 105, 111, 110, 32, 60, 45, - 32, 79, 114, 100, 101, 114, 66, 121, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 32, 47, - 32, 80, 97, 114, 116, 105, 116, 105, 111, 110, 101, 100, 66, 121, 67, 111, 112, 121, 79, 112, - 116, 105, 111, 110, 32, 47, 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, - 116, 105, 111, 110, 10, 79, 114, 100, 101, 114, 66, 121, 67, 111, 112, 121, 79, 112, 116, 105, - 111, 110, 32, 60, 45, 32, 39, 79, 82, 68, 69, 82, 39, 32, 39, 66, 89, 39, 32, 71, - 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 86, 97, 108, 117, - 101, 63, 10, 80, 97, 114, 116, 105, 116, 105, 111, 110, 101, 100, 66, 121, 67, 111, 112, 121, - 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 40, 39, 80, 65, 82, 84, 73, 84, 73, 79, - 78, 39, 32, 47, 32, 39, 80, 65, 82, 84, 73, 84, 73, 79, 78, 69, 68, 39, 41, 32, - 39, 66, 89, 39, 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, - 111, 110, 86, 97, 108, 117, 101, 63, 10, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, - 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, - 40, 76, 105, 115, 116, 40, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, - 105, 111, 110, 41, 41, 10, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, - 105, 111, 110, 32, 60, 45, 32, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 78, 97, 109, - 101, 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 86, - 97, 108, 117, 101, 63, 10, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, - 105, 111, 110, 86, 97, 108, 117, 101, 32, 60, 45, 32, 71, 101, 110, 101, 114, 105, 99, 67, - 111, 112, 121, 79, 112, 116, 105, 111, 110, 79, 114, 100, 101, 114, 76, 105, 115, 116, 32, 47, - 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 10, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, - 79, 112, 116, 105, 111, 110, 79, 114, 100, 101, 114, 76, 105, 115, 116, 32, 60, 45, 32, 71, - 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 80, 97, 114, 101, - 110, 116, 104, 101, 115, 105, 122, 101, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 76, - 105, 115, 116, 10, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, - 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 10, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, - 105, 111, 110, 80, 97, 114, 101, 110, 116, 104, 101, 115, 105, 122, 101, 100, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, - 40, 79, 114, 100, 101, 114, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 76, 105, - 115, 116, 41, 10, 67, 111, 112, 121, 70, 114, 111, 109, 68, 97, 116, 97, 98, 97, 115, 101, - 32, 60, 45, 32, 67, 111, 112, 121, 70, 114, 111, 109, 68, 97, 116, 97, 98, 97, 115, 101, - 87, 105, 116, 104, 70, 108, 97, 103, 32, 47, 32, 67, 111, 112, 121, 70, 114, 111, 109, 68, - 97, 116, 97, 98, 97, 115, 101, 87, 105, 116, 104, 111, 117, 116, 70, 108, 97, 103, 10, 67, - 111, 112, 121, 70, 114, 111, 109, 68, 97, 116, 97, 98, 97, 115, 101, 87, 105, 116, 104, 70, - 108, 97, 103, 32, 60, 45, 32, 39, 70, 82, 79, 77, 39, 32, 39, 68, 65, 84, 65, 66, - 65, 83, 69, 39, 32, 67, 111, 108, 73, 100, 32, 39, 84, 79, 39, 32, 67, 111, 108, 73, - 100, 32, 67, 111, 112, 121, 68, 97, 116, 97, 98, 97, 115, 101, 70, 108, 97, 103, 10, 67, - 111, 112, 121, 70, 114, 111, 109, 68, 97, 116, 97, 98, 97, 115, 101, 87, 105, 116, 104, 111, - 117, 116, 70, 108, 97, 103, 32, 60, 45, 32, 39, 70, 82, 79, 77, 39, 32, 39, 68, 65, - 84, 65, 66, 65, 83, 69, 39, 32, 67, 111, 108, 73, 100, 32, 39, 84, 79, 39, 32, 67, - 111, 108, 73, 100, 10, 67, 111, 112, 121, 68, 97, 116, 97, 98, 97, 115, 101, 70, 108, 97, - 103, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 83, 99, 104, 101, 109, 97, 79, 114, - 68, 97, 116, 97, 41, 10, 83, 99, 104, 101, 109, 97, 79, 114, 68, 97, 116, 97, 32, 60, - 45, 32, 67, 111, 112, 121, 83, 99, 104, 101, 109, 97, 32, 47, 32, 67, 111, 112, 121, 68, - 97, 116, 97, 10, 67, 111, 112, 121, 83, 99, 104, 101, 109, 97, 32, 60, 45, 32, 39, 83, - 67, 72, 69, 77, 65, 39, 10, 67, 111, 112, 121, 68, 97, 116, 97, 32, 60, 45, 32, 39, - 68, 65, 84, 65, 39, 10, 67, 114, 101, 97, 116, 101, 73, 110, 100, 101, 120, 83, 116, 109, - 116, 32, 60, 45, 32, 85, 110, 105, 113, 117, 101, 73, 110, 100, 101, 120, 63, 32, 39, 73, - 78, 68, 69, 88, 39, 32, 73, 102, 78, 111, 116, 69, 120, 105, 115, 116, 115, 63, 32, 73, - 110, 100, 101, 120, 78, 97, 109, 101, 63, 32, 39, 79, 78, 39, 32, 66, 97, 115, 101, 84, - 97, 98, 108, 101, 78, 97, 109, 101, 32, 73, 110, 115, 101, 114, 116, 67, 111, 108, 117, 109, - 110, 76, 105, 115, 116, 63, 32, 73, 110, 100, 101, 120, 84, 121, 112, 101, 63, 32, 80, 97, - 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 73, 110, 100, 101, 120, 69, 108, 101, 109, 101, - 110, 116, 41, 41, 63, 32, 87, 105, 116, 104, 76, 105, 115, 116, 63, 32, 87, 104, 101, 114, - 101, 67, 108, 97, 117, 115, 101, 63, 10, 87, 105, 116, 104, 76, 105, 115, 116, 32, 60, 45, - 32, 39, 87, 73, 84, 72, 39, 32, 82, 101, 108, 79, 112, 116, 105, 111, 110, 79, 114, 79, - 105, 100, 115, 10, 82, 101, 108, 79, 112, 116, 105, 111, 110, 79, 114, 79, 105, 100, 115, 32, - 60, 45, 32, 82, 101, 108, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 32, 47, 32, 79, - 105, 100, 115, 10, 82, 101, 108, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, - 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 82, 101, 108, 79, 112, 116, 105, - 111, 110, 41, 41, 10, 79, 105, 100, 115, 32, 60, 45, 32, 87, 105, 116, 104, 79, 114, 87, - 105, 116, 104, 111, 117, 116, 79, 105, 100, 115, 32, 39, 79, 73, 68, 83, 39, 10, 87, 105, - 116, 104, 79, 114, 87, 105, 116, 104, 111, 117, 116, 79, 105, 100, 115, 32, 60, 45, 32, 87, - 105, 116, 104, 79, 105, 100, 115, 32, 47, 32, 87, 105, 116, 104, 111, 117, 116, 79, 105, 100, - 115, 10, 87, 105, 116, 104, 79, 105, 100, 115, 32, 60, 45, 32, 39, 87, 73, 84, 72, 39, - 10, 87, 105, 116, 104, 111, 117, 116, 79, 105, 100, 115, 32, 60, 45, 32, 39, 87, 73, 84, - 72, 79, 85, 84, 39, 10, 73, 110, 100, 101, 120, 69, 108, 101, 109, 101, 110, 116, 32, 60, - 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 68, 101, 115, 99, 79, 114, 65, - 115, 99, 63, 32, 78, 117, 108, 108, 115, 70, 105, 114, 115, 116, 79, 114, 76, 97, 115, 116, - 63, 10, 85, 110, 105, 113, 117, 101, 73, 110, 100, 101, 120, 32, 60, 45, 32, 39, 85, 78, - 73, 81, 85, 69, 39, 10, 73, 110, 100, 101, 120, 84, 121, 112, 101, 32, 60, 45, 32, 39, - 85, 83, 73, 78, 71, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 82, 101, - 108, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 82, 101, 108, 79, 112, 116, 105, 111, 110, - 78, 97, 109, 101, 32, 82, 101, 108, 79, 112, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, - 110, 116, 79, 112, 116, 63, 10, 82, 101, 108, 79, 112, 116, 105, 111, 110, 78, 97, 109, 101, - 32, 60, 45, 32, 68, 111, 116, 116, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, - 83, 116, 114, 105, 110, 103, 32, 47, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, - 97, 108, 10, 68, 111, 116, 116, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 83, - 116, 114, 105, 110, 103, 32, 60, 45, 32, 68, 111, 116, 116, 101, 100, 73, 100, 101, 110, 116, - 105, 102, 105, 101, 114, 10, 82, 101, 108, 79, 112, 116, 105, 111, 110, 65, 114, 103, 117, 109, - 101, 110, 116, 79, 112, 116, 32, 60, 45, 32, 39, 61, 39, 32, 68, 101, 102, 65, 114, 103, - 10, 68, 101, 102, 65, 114, 103, 32, 60, 45, 32, 68, 101, 102, 65, 114, 103, 78, 117, 108, - 108, 32, 47, 32, 68, 101, 102, 65, 114, 103, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, - 114, 97, 108, 32, 47, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 32, - 47, 32, 78, 111, 110, 101, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 32, 47, 32, 68, 101, 102, 65, 114, 103, 75, 101, 121, 119, 111, - 114, 100, 10, 68, 101, 102, 65, 114, 103, 78, 117, 108, 108, 32, 60, 45, 32, 78, 117, 108, - 108, 76, 105, 116, 101, 114, 97, 108, 10, 68, 101, 102, 65, 114, 103, 75, 101, 121, 119, 111, - 114, 100, 32, 60, 45, 32, 82, 101, 115, 101, 114, 118, 101, 100, 75, 101, 121, 119, 111, 114, - 100, 10, 68, 101, 102, 65, 114, 103, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, - 108, 32, 60, 45, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, 78, - 111, 110, 101, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 39, 78, 79, 78, 69, 39, - 10, 67, 114, 101, 97, 116, 101, 77, 97, 99, 114, 111, 83, 116, 109, 116, 32, 60, 45, 32, - 77, 97, 99, 114, 111, 79, 114, 70, 117, 110, 99, 116, 105, 111, 110, 32, 73, 102, 78, 111, - 116, 69, 120, 105, 115, 116, 115, 63, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, - 109, 101, 32, 76, 105, 115, 116, 40, 77, 97, 99, 114, 111, 68, 101, 102, 105, 110, 105, 116, - 105, 111, 110, 41, 10, 77, 97, 99, 114, 111, 79, 114, 70, 117, 110, 99, 116, 105, 111, 110, - 32, 60, 45, 32, 77, 97, 99, 114, 111, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 70, - 117, 110, 99, 116, 105, 111, 110, 75, 101, 121, 119, 111, 114, 100, 10, 77, 97, 99, 114, 111, - 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 77, 65, 67, 82, 79, 39, 10, 70, - 117, 110, 99, 116, 105, 111, 110, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 70, - 85, 78, 67, 84, 73, 79, 78, 39, 10, 77, 97, 99, 114, 111, 68, 101, 102, 105, 110, 105, - 116, 105, 111, 110, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 77, 97, 99, 114, 111, - 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 63, 41, 32, 39, 65, 83, 39, 32, 77, 97, - 99, 114, 111, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 66, 111, 100, 121, 10, 77, 97, - 99, 114, 111, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 66, 111, 100, 121, 32, 60, 45, - 32, 84, 97, 98, 108, 101, 77, 97, 99, 114, 111, 68, 101, 102, 105, 110, 105, 116, 105, 111, - 110, 32, 47, 32, 83, 99, 97, 108, 97, 114, 77, 97, 99, 114, 111, 68, 101, 102, 105, 110, - 105, 116, 105, 111, 110, 10, 77, 97, 99, 114, 111, 80, 97, 114, 97, 109, 101, 116, 101, 114, - 115, 32, 60, 45, 32, 76, 105, 115, 116, 40, 77, 97, 99, 114, 111, 80, 97, 114, 97, 109, - 101, 116, 101, 114, 41, 10, 77, 97, 99, 114, 111, 80, 97, 114, 97, 109, 101, 116, 101, 114, - 32, 60, 45, 32, 78, 97, 109, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 47, - 32, 83, 105, 109, 112, 108, 101, 80, 97, 114, 97, 109, 101, 116, 101, 114, 10, 83, 105, 109, - 112, 108, 101, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 60, 45, 32, 84, 121, 112, 101, - 70, 117, 110, 99, 78, 97, 109, 101, 32, 84, 121, 112, 101, 63, 10, 83, 99, 97, 108, 97, - 114, 77, 97, 99, 114, 111, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 32, 60, 45, 32, - 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 84, 97, 98, 108, 101, 77, 97, 99, 114, - 111, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 32, 60, 45, 32, 39, 84, 65, 66, 76, - 69, 39, 32, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, - 116, 101, 114, 110, 97, 108, 10, 67, 114, 101, 97, 116, 101, 83, 99, 104, 101, 109, 97, 83, - 116, 109, 116, 32, 60, 45, 32, 39, 83, 67, 72, 69, 77, 65, 39, 32, 73, 102, 78, 111, - 116, 69, 120, 105, 115, 116, 115, 63, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, - 109, 101, 10, 67, 114, 101, 97, 116, 101, 83, 101, 99, 114, 101, 116, 83, 116, 109, 116, 32, - 60, 45, 32, 39, 83, 69, 67, 82, 69, 84, 39, 32, 73, 102, 78, 111, 116, 69, 120, 105, - 115, 116, 115, 63, 32, 83, 101, 99, 114, 101, 116, 78, 97, 109, 101, 63, 32, 83, 101, 99, - 114, 101, 116, 83, 116, 111, 114, 97, 103, 101, 83, 112, 101, 99, 105, 102, 105, 101, 114, 63, - 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 76, 105, - 115, 116, 10, 83, 101, 99, 114, 101, 116, 83, 116, 111, 114, 97, 103, 101, 83, 112, 101, 99, - 105, 102, 105, 101, 114, 32, 60, 45, 32, 39, 73, 78, 39, 32, 73, 100, 101, 110, 116, 105, - 102, 105, 101, 114, 10, 83, 101, 99, 114, 101, 116, 78, 97, 109, 101, 32, 60, 45, 32, 67, - 111, 108, 73, 100, 10, 67, 114, 101, 97, 116, 101, 83, 101, 113, 117, 101, 110, 99, 101, 83, - 116, 109, 116, 32, 60, 45, 32, 39, 83, 69, 81, 85, 69, 78, 67, 69, 39, 32, 73, 102, - 78, 111, 116, 69, 120, 105, 115, 116, 115, 63, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, - 78, 97, 109, 101, 32, 83, 101, 113, 117, 101, 110, 99, 101, 79, 112, 116, 105, 111, 110, 42, - 10, 83, 101, 113, 117, 101, 110, 99, 101, 79, 112, 116, 105, 111, 110, 32, 60, 45, 10, 9, - 83, 101, 113, 83, 101, 116, 67, 121, 99, 108, 101, 32, 47, 10, 9, 83, 101, 113, 83, 101, - 116, 73, 110, 99, 114, 101, 109, 101, 110, 116, 32, 47, 10, 9, 83, 101, 113, 83, 101, 116, - 77, 105, 110, 77, 97, 120, 32, 47, 10, 9, 83, 101, 113, 78, 111, 77, 105, 110, 77, 97, - 120, 32, 47, 10, 9, 83, 101, 113, 83, 116, 97, 114, 116, 87, 105, 116, 104, 32, 47, 10, - 9, 83, 101, 113, 79, 119, 110, 101, 100, 66, 121, 10, 83, 101, 113, 83, 101, 116, 67, 121, - 99, 108, 101, 32, 60, 45, 32, 83, 101, 113, 67, 121, 99, 108, 101, 32, 47, 32, 83, 101, - 113, 78, 111, 67, 121, 99, 108, 101, 10, 83, 101, 113, 67, 121, 99, 108, 101, 32, 60, 45, - 32, 39, 67, 89, 67, 76, 69, 39, 10, 83, 101, 113, 78, 111, 67, 121, 99, 108, 101, 32, - 60, 45, 32, 39, 78, 79, 39, 32, 39, 67, 89, 67, 76, 69, 39, 10, 83, 101, 113, 83, - 101, 116, 73, 110, 99, 114, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 73, 78, 67, 82, - 69, 77, 69, 78, 84, 39, 32, 39, 66, 89, 39, 63, 32, 69, 120, 112, 114, 101, 115, 115, - 105, 111, 110, 10, 83, 101, 113, 83, 101, 116, 77, 105, 110, 77, 97, 120, 32, 60, 45, 32, - 83, 101, 113, 77, 105, 110, 79, 114, 77, 97, 120, 32, 69, 120, 112, 114, 101, 115, 115, 105, - 111, 110, 10, 83, 101, 113, 78, 111, 77, 105, 110, 77, 97, 120, 32, 60, 45, 32, 39, 78, - 79, 39, 32, 83, 101, 113, 77, 105, 110, 79, 114, 77, 97, 120, 10, 83, 101, 113, 83, 116, - 97, 114, 116, 87, 105, 116, 104, 32, 60, 45, 32, 39, 83, 84, 65, 82, 84, 39, 32, 39, - 87, 73, 84, 72, 39, 63, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 83, 101, - 113, 79, 119, 110, 101, 100, 66, 121, 32, 60, 45, 32, 39, 79, 87, 78, 69, 68, 39, 32, - 39, 66, 89, 39, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 10, 83, - 101, 113, 77, 105, 110, 79, 114, 77, 97, 120, 32, 60, 45, 32, 77, 105, 110, 86, 97, 108, - 117, 101, 32, 47, 32, 77, 97, 120, 86, 97, 108, 117, 101, 10, 77, 105, 110, 86, 97, 108, - 117, 101, 32, 60, 45, 32, 39, 77, 73, 78, 86, 65, 76, 85, 69, 39, 10, 77, 97, 120, - 86, 97, 108, 117, 101, 32, 60, 45, 32, 39, 77, 65, 88, 86, 65, 76, 85, 69, 39, 10, - 67, 114, 101, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, - 67, 82, 69, 65, 84, 69, 39, 32, 79, 114, 82, 101, 112, 108, 97, 99, 101, 63, 32, 84, - 101, 109, 112, 111, 114, 97, 114, 121, 63, 32, 67, 114, 101, 97, 116, 101, 83, 116, 97, 116, - 101, 109, 101, 110, 116, 86, 97, 114, 105, 97, 116, 105, 111, 110, 10, 67, 114, 101, 97, 116, - 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 86, 97, 114, 105, 97, 116, 105, 111, 110, 32, - 60, 45, 32, 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 83, 116, 109, 116, 32, 47, - 32, 67, 114, 101, 97, 116, 101, 77, 97, 99, 114, 111, 83, 116, 109, 116, 32, 47, 32, 67, - 114, 101, 97, 116, 101, 83, 101, 113, 117, 101, 110, 99, 101, 83, 116, 109, 116, 32, 47, 32, - 67, 114, 101, 97, 116, 101, 84, 121, 112, 101, 83, 116, 109, 116, 32, 47, 32, 67, 114, 101, - 97, 116, 101, 83, 99, 104, 101, 109, 97, 83, 116, 109, 116, 32, 47, 32, 67, 114, 101, 97, - 116, 101, 86, 105, 101, 119, 83, 116, 109, 116, 32, 47, 32, 67, 114, 101, 97, 116, 101, 73, - 110, 100, 101, 120, 83, 116, 109, 116, 32, 47, 32, 67, 114, 101, 97, 116, 101, 83, 101, 99, - 114, 101, 116, 83, 116, 109, 116, 32, 47, 32, 67, 114, 101, 97, 116, 101, 84, 114, 105, 103, - 103, 101, 114, 83, 116, 109, 116, 10, 79, 114, 82, 101, 112, 108, 97, 99, 101, 32, 60, 45, - 32, 39, 79, 82, 39, 32, 39, 82, 69, 80, 76, 65, 67, 69, 39, 10, 84, 101, 109, 112, - 111, 114, 97, 114, 121, 32, 60, 45, 32, 80, 101, 114, 115, 105, 115, 116, 101, 110, 116, 32, - 47, 32, 84, 101, 109, 112, 80, 101, 114, 115, 105, 115, 116, 101, 110, 116, 32, 47, 32, 84, - 101, 109, 112, 111, 114, 97, 114, 121, 80, 101, 114, 115, 105, 115, 116, 101, 110, 116, 10, 80, - 101, 114, 115, 105, 115, 116, 101, 110, 116, 32, 60, 45, 32, 39, 80, 69, 82, 83, 73, 83, - 84, 69, 78, 84, 39, 10, 84, 101, 109, 112, 80, 101, 114, 115, 105, 115, 116, 101, 110, 116, - 32, 60, 45, 32, 39, 84, 69, 77, 80, 39, 10, 84, 101, 109, 112, 111, 114, 97, 114, 121, - 80, 101, 114, 115, 105, 115, 116, 101, 110, 116, 32, 60, 45, 32, 39, 84, 69, 77, 80, 79, - 82, 65, 82, 89, 39, 10, 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 83, 116, 109, - 116, 32, 60, 45, 32, 39, 84, 65, 66, 76, 69, 39, 32, 73, 102, 78, 111, 116, 69, 120, - 105, 115, 116, 115, 63, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 32, - 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, - 110, 32, 67, 111, 109, 109, 105, 116, 65, 99, 116, 105, 111, 110, 63, 10, 67, 114, 101, 97, - 116, 101, 84, 97, 98, 108, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 32, 60, 45, - 32, 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 65, 115, 32, 47, 32, 67, 114, 101, - 97, 116, 101, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 10, 67, 114, 101, 97, 116, 101, - 84, 97, 98, 108, 101, 65, 115, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, - 114, 76, 105, 115, 116, 63, 32, 80, 97, 114, 116, 105, 116, 105, 111, 110, 83, 111, 114, 116, - 101, 100, 79, 112, 116, 105, 111, 110, 115, 63, 32, 87, 105, 116, 104, 76, 105, 115, 116, 63, - 32, 39, 65, 83, 39, 32, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 87, 105, 116, 104, - 68, 97, 116, 97, 63, 10, 80, 97, 114, 116, 105, 116, 105, 111, 110, 83, 111, 114, 116, 101, - 100, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 80, 97, 114, 116, 105, 116, 105, 111, - 110, 79, 112, 116, 83, 111, 114, 116, 101, 100, 79, 112, 116, 105, 111, 110, 115, 32, 47, 32, - 83, 111, 114, 116, 101, 100, 79, 112, 116, 80, 97, 114, 116, 105, 116, 105, 111, 110, 79, 112, - 116, 105, 111, 110, 115, 10, 80, 97, 114, 116, 105, 116, 105, 111, 110, 79, 112, 116, 83, 111, - 114, 116, 101, 100, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 80, 97, 114, 116, 105, - 116, 105, 111, 110, 79, 112, 116, 105, 111, 110, 115, 32, 83, 111, 114, 116, 101, 100, 79, 112, - 116, 105, 111, 110, 115, 63, 10, 83, 111, 114, 116, 101, 100, 79, 112, 116, 80, 97, 114, 116, - 105, 116, 105, 111, 110, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 83, 111, 114, 116, - 101, 100, 79, 112, 116, 105, 111, 110, 115, 32, 80, 97, 114, 116, 105, 116, 105, 111, 110, 79, - 112, 116, 105, 111, 110, 115, 63, 10, 80, 97, 114, 116, 105, 116, 105, 111, 110, 79, 112, 116, - 105, 111, 110, 115, 32, 60, 45, 32, 39, 80, 65, 82, 84, 73, 84, 73, 79, 78, 69, 68, - 39, 32, 39, 66, 89, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, - 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 41, 10, 83, 111, 114, 116, 101, 100, 79, 112, - 116, 105, 111, 110, 115, 32, 60, 45, 32, 39, 83, 79, 82, 84, 69, 68, 39, 32, 39, 66, - 89, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 41, 41, 10, 87, 105, 116, 104, 68, 97, 116, 97, 32, 60, 45, 32, - 87, 105, 116, 104, 68, 97, 116, 97, 79, 110, 108, 121, 32, 47, 32, 87, 105, 116, 104, 78, - 111, 68, 97, 116, 97, 10, 87, 105, 116, 104, 68, 97, 116, 97, 79, 110, 108, 121, 32, 60, - 45, 32, 39, 87, 73, 84, 72, 39, 32, 39, 68, 65, 84, 65, 39, 10, 87, 105, 116, 104, - 78, 111, 68, 97, 116, 97, 32, 60, 45, 32, 39, 87, 73, 84, 72, 39, 32, 39, 78, 79, - 39, 32, 39, 68, 65, 84, 65, 39, 10, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 76, - 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 73, - 100, 101, 110, 116, 105, 102, 105, 101, 114, 41, 41, 10, 67, 114, 101, 97, 116, 101, 67, 111, - 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 67, - 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, - 63, 41, 32, 80, 97, 114, 116, 105, 116, 105, 111, 110, 83, 111, 114, 116, 101, 100, 79, 112, - 116, 105, 111, 110, 115, 63, 32, 87, 105, 116, 104, 76, 105, 115, 116, 63, 10, 73, 102, 78, - 111, 116, 69, 120, 105, 115, 116, 115, 32, 60, 45, 32, 39, 73, 70, 39, 32, 39, 78, 79, - 84, 39, 32, 39, 69, 88, 73, 83, 84, 83, 39, 10, 81, 117, 97, 108, 105, 102, 105, 101, - 100, 78, 97, 109, 101, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 82, 101, 115, 101, - 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, - 32, 47, 32, 83, 99, 104, 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 73, 100, 101, - 110, 116, 105, 102, 105, 101, 114, 79, 114, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, - 97, 108, 32, 47, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 79, 114, 83, 116, 114, - 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, 83, 99, 104, 101, 109, 97, 82, 101, 115, - 101, 114, 118, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 79, 114, 83, 116, 114, - 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 83, 99, 104, 101, 109, 97, - 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, - 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 79, 114, 83, 116, 114, 105, 110, 103, - 76, 105, 116, 101, 114, 97, 108, 10, 67, 97, 116, 97, 108, 111, 103, 82, 101, 115, 101, 114, - 118, 101, 100, 83, 99, 104, 101, 109, 97, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, - 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, - 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 81, 117, - 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 43, 32, 82, 101, 115, 101, 114, 118, 101, - 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 79, 114, 83, 116, 114, 105, 110, 103, 76, - 105, 116, 101, 114, 97, 108, 10, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 79, 114, 83, - 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 73, 100, 101, 110, - 116, 105, 102, 105, 101, 114, 32, 47, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, - 97, 108, 10, 82, 101, 115, 101, 114, 118, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, - 114, 79, 114, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, - 82, 101, 115, 101, 114, 118, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 47, - 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, 67, 97, 116, 97, 108, - 111, 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 60, 45, 32, 67, - 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, 32, 39, 46, 39, 10, 83, 99, 104, 101, 109, - 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 60, 45, 32, 83, 99, - 104, 101, 109, 97, 78, 97, 109, 101, 32, 39, 46, 39, 10, 82, 101, 115, 101, 114, 118, 101, - 100, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, - 32, 60, 45, 32, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 78, 97, - 109, 101, 32, 39, 46, 39, 10, 84, 97, 98, 108, 101, 81, 117, 97, 108, 105, 102, 105, 99, - 97, 116, 105, 111, 110, 32, 60, 45, 32, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 39, - 46, 39, 10, 82, 101, 115, 101, 114, 118, 101, 100, 84, 97, 98, 108, 101, 81, 117, 97, 108, - 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 60, 45, 32, 82, 101, 115, 101, 114, 118, 101, - 100, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 39, 46, 39, 10, 67, 114, 101, 97, 116, - 101, 84, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, - 76, 105, 115, 116, 40, 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 67, 111, 108, 117, - 109, 110, 69, 108, 101, 109, 101, 110, 116, 41, 10, 67, 114, 101, 97, 116, 101, 84, 97, 98, - 108, 101, 67, 111, 108, 117, 109, 110, 69, 108, 101, 109, 101, 110, 116, 32, 60, 45, 32, 67, - 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 68, 101, 102, 105, - 110, 105, 116, 105, 111, 110, 32, 47, 32, 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, - 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 10, 67, 114, 101, 97, 116, 101, 84, 97, 98, - 108, 101, 67, 111, 108, 117, 109, 110, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 32, 60, - 45, 32, 67, 111, 108, 117, 109, 110, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 10, 67, - 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, - 32, 60, 45, 32, 84, 111, 112, 76, 101, 118, 101, 108, 67, 111, 110, 115, 116, 114, 97, 105, - 110, 116, 10, 67, 111, 108, 117, 109, 110, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 32, - 60, 45, 32, 68, 111, 116, 116, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, - 84, 121, 112, 101, 63, 32, 71, 101, 110, 101, 114, 97, 116, 101, 100, 67, 111, 108, 117, 109, - 110, 63, 32, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 78, 97, 109, 101, 67, 108, 97, - 117, 115, 101, 63, 32, 67, 111, 108, 117, 109, 110, 67, 111, 110, 115, 116, 114, 97, 105, 110, - 116, 42, 10, 67, 111, 108, 117, 109, 110, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, - 60, 45, 32, 78, 111, 116, 78, 117, 108, 108, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, - 32, 47, 32, 85, 110, 105, 113, 117, 101, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, - 47, 32, 80, 114, 105, 109, 97, 114, 121, 75, 101, 121, 67, 111, 110, 115, 116, 114, 97, 105, - 110, 116, 32, 47, 32, 68, 101, 102, 97, 117, 108, 116, 86, 97, 108, 117, 101, 32, 47, 32, - 67, 104, 101, 99, 107, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 47, 32, 70, 111, - 114, 101, 105, 103, 110, 75, 101, 121, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 47, - 32, 67, 111, 108, 117, 109, 110, 67, 111, 108, 108, 97, 116, 105, 111, 110, 32, 47, 32, 67, - 111, 108, 117, 109, 110, 67, 111, 109, 112, 114, 101, 115, 115, 105, 111, 110, 10, 78, 111, 116, - 78, 117, 108, 108, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 78, 117, - 108, 108, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 47, 32, 78, 111, 116, 78, 117, - 108, 108, 67, 111, 108, 117, 109, 110, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 10, 78, - 117, 108, 108, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 78, 85, - 76, 76, 39, 10, 78, 111, 116, 78, 117, 108, 108, 67, 111, 108, 117, 109, 110, 67, 111, 110, - 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 78, 79, 84, 39, 32, 39, 78, 85, - 76, 76, 39, 10, 85, 110, 105, 113, 117, 101, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, - 32, 60, 45, 32, 39, 85, 78, 73, 81, 85, 69, 39, 32, 67, 111, 110, 115, 116, 114, 97, - 105, 110, 116, 84, 105, 109, 105, 110, 103, 63, 10, 80, 114, 105, 109, 97, 114, 121, 75, 101, - 121, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 80, 82, 73, 77, - 65, 82, 89, 39, 32, 39, 75, 69, 89, 39, 32, 67, 111, 110, 115, 116, 114, 97, 105, 110, - 116, 84, 105, 109, 105, 110, 103, 63, 10, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 84, - 105, 109, 105, 110, 103, 32, 60, 45, 32, 73, 109, 109, 101, 100, 105, 97, 116, 101, 67, 111, - 110, 115, 116, 114, 97, 105, 110, 116, 32, 47, 32, 68, 101, 102, 101, 114, 114, 101, 100, 67, - 111, 110, 115, 116, 114, 97, 105, 110, 116, 10, 73, 109, 109, 101, 100, 105, 97, 116, 101, 67, - 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 73, 77, 77, 69, 68, 73, - 65, 84, 69, 39, 10, 68, 101, 102, 101, 114, 114, 101, 100, 67, 111, 110, 115, 116, 114, 97, - 105, 110, 116, 32, 60, 45, 32, 39, 68, 69, 70, 69, 82, 82, 69, 68, 39, 10, 68, 101, - 102, 97, 117, 108, 116, 86, 97, 108, 117, 101, 32, 60, 45, 32, 39, 68, 69, 70, 65, 85, - 76, 84, 39, 32, 67, 111, 108, 117, 109, 110, 68, 101, 102, 97, 117, 108, 116, 69, 120, 112, - 114, 10, 67, 104, 101, 99, 107, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, - 32, 39, 67, 72, 69, 67, 75, 39, 32, 80, 97, 114, 101, 110, 115, 40, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 41, 10, 70, 111, 114, 101, 105, 103, 110, 75, 101, 121, 67, 111, - 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 82, 69, 70, 69, 82, 69, 78, - 67, 69, 83, 39, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 80, - 97, 114, 101, 110, 115, 40, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 41, 63, 32, 75, - 101, 121, 65, 99, 116, 105, 111, 110, 115, 10, 67, 111, 108, 117, 109, 110, 67, 111, 108, 108, - 97, 116, 105, 111, 110, 32, 60, 45, 32, 39, 67, 79, 76, 76, 65, 84, 69, 39, 32, 68, - 111, 116, 116, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 67, 111, 108, 117, - 109, 110, 67, 111, 109, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 85, 83, - 73, 78, 71, 39, 32, 39, 67, 79, 77, 80, 82, 69, 83, 83, 73, 79, 78, 39, 32, 67, - 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 10, 75, 101, 121, 65, 99, 116, 105, - 111, 110, 115, 32, 60, 45, 32, 85, 112, 100, 97, 116, 101, 65, 99, 116, 105, 111, 110, 63, - 32, 68, 101, 108, 101, 116, 101, 65, 99, 116, 105, 111, 110, 63, 10, 85, 112, 100, 97, 116, - 101, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 79, 78, 39, 32, 39, 85, 80, 68, - 65, 84, 69, 39, 32, 75, 101, 121, 65, 99, 116, 105, 111, 110, 10, 68, 101, 108, 101, 116, - 101, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 79, 78, 39, 32, 39, 68, 69, 76, - 69, 84, 69, 39, 32, 75, 101, 121, 65, 99, 116, 105, 111, 110, 10, 75, 101, 121, 65, 99, - 116, 105, 111, 110, 32, 60, 45, 32, 78, 111, 75, 101, 121, 65, 99, 116, 105, 111, 110, 32, - 47, 32, 82, 101, 115, 116, 114, 105, 99, 116, 75, 101, 121, 65, 99, 116, 105, 111, 110, 32, - 47, 32, 67, 97, 115, 99, 97, 100, 101, 75, 101, 121, 65, 99, 116, 105, 111, 110, 32, 47, - 32, 83, 101, 116, 78, 117, 108, 108, 75, 101, 121, 65, 99, 116, 105, 111, 110, 32, 47, 32, - 83, 101, 116, 68, 101, 102, 97, 117, 108, 116, 75, 101, 121, 65, 99, 116, 105, 111, 110, 10, - 78, 111, 75, 101, 121, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 78, 79, 39, 32, - 39, 65, 67, 84, 73, 79, 78, 39, 10, 82, 101, 115, 116, 114, 105, 99, 116, 75, 101, 121, - 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 82, 69, 83, 84, 82, 73, 67, 84, 39, - 10, 67, 97, 115, 99, 97, 100, 101, 75, 101, 121, 65, 99, 116, 105, 111, 110, 32, 60, 45, - 32, 39, 67, 65, 83, 67, 65, 68, 69, 39, 10, 83, 101, 116, 78, 117, 108, 108, 75, 101, - 121, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 83, 69, 84, 39, 32, 39, 78, 85, - 76, 76, 39, 10, 83, 101, 116, 68, 101, 102, 97, 117, 108, 116, 75, 101, 121, 65, 99, 116, - 105, 111, 110, 32, 60, 45, 32, 39, 83, 69, 84, 39, 32, 39, 68, 69, 70, 65, 85, 76, - 84, 39, 10, 84, 111, 112, 76, 101, 118, 101, 108, 67, 111, 110, 115, 116, 114, 97, 105, 110, - 116, 32, 60, 45, 32, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 78, 97, 109, 101, 67, - 108, 97, 117, 115, 101, 63, 32, 84, 111, 112, 76, 101, 118, 101, 108, 67, 111, 110, 115, 116, - 114, 97, 105, 110, 116, 76, 105, 115, 116, 10, 84, 111, 112, 76, 101, 118, 101, 108, 67, 111, - 110, 115, 116, 114, 97, 105, 110, 116, 76, 105, 115, 116, 32, 60, 45, 32, 84, 111, 112, 80, - 114, 105, 109, 97, 114, 121, 75, 101, 121, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, - 47, 32, 84, 111, 112, 67, 104, 101, 99, 107, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, - 32, 47, 32, 84, 111, 112, 85, 110, 105, 113, 117, 101, 67, 111, 110, 115, 116, 114, 97, 105, - 110, 116, 32, 47, 32, 84, 111, 112, 70, 111, 114, 101, 105, 103, 110, 75, 101, 121, 67, 111, - 110, 115, 116, 114, 97, 105, 110, 116, 10, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 78, - 97, 109, 101, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 67, 79, 78, 83, 84, 82, - 65, 73, 78, 84, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 84, 111, 112, - 67, 104, 101, 99, 107, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 67, - 104, 101, 99, 107, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 10, 84, 111, 112, 80, 114, - 105, 109, 97, 114, 121, 75, 101, 121, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, - 45, 32, 39, 80, 82, 73, 77, 65, 82, 89, 39, 32, 39, 75, 69, 89, 39, 32, 67, 111, - 108, 117, 109, 110, 73, 100, 76, 105, 115, 116, 32, 67, 111, 110, 115, 116, 114, 97, 105, 110, - 116, 84, 105, 109, 105, 110, 103, 63, 10, 84, 111, 112, 85, 110, 105, 113, 117, 101, 67, 111, - 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 85, 78, 73, 81, 85, 69, 39, - 32, 67, 111, 108, 117, 109, 110, 73, 100, 76, 105, 115, 116, 32, 67, 111, 110, 115, 116, 114, - 97, 105, 110, 116, 84, 105, 109, 105, 110, 103, 63, 10, 84, 111, 112, 70, 111, 114, 101, 105, - 103, 110, 75, 101, 121, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, - 70, 79, 82, 69, 73, 71, 78, 39, 32, 39, 75, 69, 89, 39, 32, 67, 111, 108, 117, 109, - 110, 73, 100, 76, 105, 115, 116, 32, 70, 111, 114, 101, 105, 103, 110, 75, 101, 121, 67, 111, - 110, 115, 116, 114, 97, 105, 110, 116, 10, 67, 111, 108, 117, 109, 110, 73, 100, 76, 105, 115, - 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 67, 111, 108, - 73, 100, 41, 41, 10, 80, 108, 97, 105, 110, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, - 32, 60, 45, 32, 33, 82, 101, 115, 101, 114, 118, 101, 100, 75, 101, 121, 119, 111, 114, 100, - 32, 60, 91, 97, 45, 122, 95, 93, 105, 91, 97, 45, 122, 48, 45, 57, 95, 93, 105, 42, - 62, 10, 81, 117, 111, 116, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, - 45, 32, 39, 34, 39, 32, 91, 94, 34, 93, 42, 32, 39, 34, 39, 10, 68, 111, 116, 116, - 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 73, 100, 101, 110, - 116, 105, 102, 105, 101, 114, 32, 68, 111, 116, 67, 111, 108, 76, 97, 98, 101, 108, 42, 10, - 68, 111, 116, 67, 111, 108, 76, 97, 98, 101, 108, 32, 60, 45, 32, 39, 46, 39, 32, 67, - 111, 108, 76, 97, 98, 101, 108, 10, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, - 45, 32, 81, 117, 111, 116, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 47, - 32, 80, 108, 97, 105, 110, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 67, 111, 108, - 73, 100, 32, 60, 45, 32, 85, 110, 114, 101, 115, 101, 114, 118, 101, 100, 75, 101, 121, 119, - 111, 114, 100, 32, 47, 32, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 75, 101, 121, 119, - 111, 114, 100, 32, 47, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 67, 111, 108, - 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 32, 60, 45, 32, 67, 111, 108, 73, 100, 32, - 47, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 73, 100, 101, 110, 116, - 105, 102, 105, 101, 114, 10, 84, 121, 112, 101, 70, 117, 110, 99, 78, 97, 109, 101, 32, 60, - 45, 32, 85, 110, 114, 101, 115, 101, 114, 118, 101, 100, 75, 101, 121, 119, 111, 114, 100, 32, - 47, 32, 84, 121, 112, 101, 70, 117, 110, 99, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, - 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 84, 121, 112, 101, 70, 117, 110, 99, 75, - 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 84, 121, 112, 101, 78, 97, 109, 101, 75, 101, - 121, 119, 111, 114, 100, 32, 47, 32, 70, 117, 110, 99, 78, 97, 109, 101, 75, 101, 121, 119, - 111, 114, 100, 10, 84, 121, 112, 101, 78, 97, 109, 101, 32, 60, 45, 32, 85, 110, 114, 101, - 115, 101, 114, 118, 101, 100, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 84, 121, 112, 101, - 78, 97, 109, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 73, 100, 101, 110, 116, 105, - 102, 105, 101, 114, 10, 67, 111, 108, 76, 97, 98, 101, 108, 32, 60, 45, 32, 82, 101, 115, - 101, 114, 118, 101, 100, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 85, 110, 114, 101, 115, - 101, 114, 118, 101, 100, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 67, 111, 108, 117, 109, - 110, 78, 97, 109, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 70, 117, 110, 99, 78, - 97, 109, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 84, 121, 112, 101, 78, 97, 109, - 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, - 114, 10, 67, 111, 108, 76, 97, 98, 101, 108, 79, 114, 83, 116, 114, 105, 110, 103, 32, 60, - 45, 32, 67, 111, 108, 76, 97, 98, 101, 108, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, - 32, 47, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 73, 100, 101, 110, - 116, 105, 102, 105, 101, 114, 10, 67, 111, 108, 76, 97, 98, 101, 108, 73, 100, 101, 110, 116, - 105, 102, 105, 101, 114, 32, 60, 45, 32, 67, 111, 108, 76, 97, 98, 101, 108, 10, 83, 116, - 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 73, 100, 101, 110, 116, 105, 102, 105, 101, - 114, 32, 60, 45, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, 71, - 101, 110, 101, 114, 97, 116, 101, 100, 67, 111, 108, 117, 109, 110, 32, 60, 45, 32, 71, 101, - 110, 101, 114, 97, 116, 101, 100, 63, 32, 39, 65, 83, 39, 32, 80, 97, 114, 101, 110, 115, - 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 32, 71, 101, 110, 101, 114, 97, 116, - 101, 100, 67, 111, 108, 117, 109, 110, 84, 121, 112, 101, 63, 10, 71, 101, 110, 101, 114, 97, - 116, 101, 100, 32, 60, 45, 32, 39, 71, 69, 78, 69, 82, 65, 84, 69, 68, 39, 32, 65, - 108, 119, 97, 121, 115, 79, 114, 66, 121, 68, 101, 102, 97, 117, 108, 116, 63, 10, 65, 108, - 119, 97, 121, 115, 79, 114, 66, 121, 68, 101, 102, 97, 117, 108, 116, 32, 60, 45, 32, 39, - 65, 76, 87, 65, 89, 83, 39, 32, 47, 32, 40, 39, 66, 89, 39, 32, 39, 68, 69, 70, - 65, 85, 76, 84, 39, 41, 10, 71, 101, 110, 101, 114, 97, 116, 101, 100, 67, 111, 108, 117, - 109, 110, 84, 121, 112, 101, 32, 60, 45, 32, 86, 105, 114, 116, 117, 97, 108, 71, 101, 110, - 101, 114, 97, 116, 101, 100, 67, 111, 108, 117, 109, 110, 32, 47, 32, 83, 116, 111, 114, 101, - 100, 71, 101, 110, 101, 114, 97, 116, 101, 100, 67, 111, 108, 117, 109, 110, 10, 67, 111, 109, - 109, 105, 116, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 79, 78, 39, 32, 39, 67, - 79, 77, 77, 73, 84, 39, 32, 80, 114, 101, 115, 101, 114, 118, 101, 79, 114, 68, 101, 108, - 101, 116, 101, 32, 39, 82, 79, 87, 83, 39, 10, 80, 114, 101, 115, 101, 114, 118, 101, 79, - 114, 68, 101, 108, 101, 116, 101, 32, 60, 45, 32, 80, 114, 101, 115, 101, 114, 118, 101, 82, - 111, 119, 115, 32, 47, 32, 68, 101, 108, 101, 116, 101, 82, 111, 119, 115, 10, 80, 114, 101, - 115, 101, 114, 118, 101, 82, 111, 119, 115, 32, 60, 45, 32, 39, 80, 82, 69, 83, 69, 82, - 86, 69, 39, 10, 68, 101, 108, 101, 116, 101, 82, 111, 119, 115, 32, 60, 45, 32, 39, 68, - 69, 76, 69, 84, 69, 39, 10, 86, 105, 114, 116, 117, 97, 108, 71, 101, 110, 101, 114, 97, - 116, 101, 100, 67, 111, 108, 117, 109, 110, 32, 60, 45, 32, 39, 86, 73, 82, 84, 85, 65, - 76, 39, 10, 83, 116, 111, 114, 101, 100, 71, 101, 110, 101, 114, 97, 116, 101, 100, 67, 111, - 108, 117, 109, 110, 32, 60, 45, 32, 39, 83, 84, 79, 82, 69, 68, 39, 10, 67, 114, 101, - 97, 116, 101, 84, 114, 105, 103, 103, 101, 114, 83, 116, 109, 116, 32, 60, 45, 32, 39, 84, - 82, 73, 71, 71, 69, 82, 39, 32, 73, 102, 78, 111, 116, 69, 120, 105, 115, 116, 115, 63, - 32, 84, 114, 105, 103, 103, 101, 114, 78, 97, 109, 101, 32, 84, 114, 105, 103, 103, 101, 114, - 84, 105, 109, 105, 110, 103, 32, 84, 114, 105, 103, 103, 101, 114, 69, 118, 101, 110, 116, 32, - 39, 79, 78, 39, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 82, - 101, 102, 101, 114, 101, 110, 99, 105, 110, 103, 67, 108, 97, 117, 115, 101, 63, 32, 70, 111, - 114, 69, 97, 99, 104, 67, 108, 97, 117, 115, 101, 63, 32, 84, 114, 105, 103, 103, 101, 114, - 66, 111, 100, 121, 10, 84, 114, 105, 103, 103, 101, 114, 66, 111, 100, 121, 32, 60, 45, 32, - 73, 110, 115, 101, 114, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 32, 85, 112, - 100, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 32, 68, 101, 108, 101, - 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 32, 77, 101, 114, 103, 101, 73, - 110, 116, 111, 83, 116, 97, 116, 101, 109, 101, 110, 116, 10, 84, 114, 105, 103, 103, 101, 114, - 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 82, - 101, 102, 101, 114, 101, 110, 99, 105, 110, 103, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, - 39, 82, 69, 70, 69, 82, 69, 78, 67, 73, 78, 71, 39, 32, 82, 101, 102, 101, 114, 101, - 110, 99, 105, 110, 103, 73, 116, 101, 109, 32, 82, 101, 102, 101, 114, 101, 110, 99, 105, 110, - 103, 73, 116, 101, 109, 63, 10, 82, 101, 102, 101, 114, 101, 110, 99, 105, 110, 103, 73, 116, - 101, 109, 32, 60, 45, 32, 82, 101, 102, 101, 114, 101, 110, 99, 105, 110, 103, 78, 101, 119, - 84, 97, 98, 108, 101, 65, 115, 32, 47, 32, 82, 101, 102, 101, 114, 101, 110, 99, 105, 110, - 103, 79, 108, 100, 84, 97, 98, 108, 101, 65, 115, 10, 82, 101, 102, 101, 114, 101, 110, 99, - 105, 110, 103, 78, 101, 119, 84, 97, 98, 108, 101, 65, 115, 32, 60, 45, 32, 39, 78, 69, - 87, 39, 32, 39, 84, 65, 66, 76, 69, 39, 32, 39, 65, 83, 39, 32, 67, 111, 108, 73, - 100, 10, 82, 101, 102, 101, 114, 101, 110, 99, 105, 110, 103, 79, 108, 100, 84, 97, 98, 108, - 101, 65, 115, 32, 60, 45, 32, 39, 79, 76, 68, 39, 32, 39, 84, 65, 66, 76, 69, 39, - 32, 39, 65, 83, 39, 32, 67, 111, 108, 73, 100, 10, 84, 114, 105, 103, 103, 101, 114, 84, - 105, 109, 105, 110, 103, 32, 60, 45, 32, 84, 114, 105, 103, 103, 101, 114, 66, 101, 102, 111, - 114, 101, 32, 47, 32, 84, 114, 105, 103, 103, 101, 114, 65, 102, 116, 101, 114, 32, 47, 32, - 84, 114, 105, 103, 103, 101, 114, 73, 110, 115, 116, 101, 97, 100, 79, 102, 10, 84, 114, 105, - 103, 103, 101, 114, 66, 101, 102, 111, 114, 101, 32, 60, 45, 32, 39, 66, 69, 70, 79, 82, - 69, 39, 10, 84, 114, 105, 103, 103, 101, 114, 65, 102, 116, 101, 114, 32, 60, 45, 32, 39, - 65, 70, 84, 69, 82, 39, 10, 84, 114, 105, 103, 103, 101, 114, 73, 110, 115, 116, 101, 97, - 100, 79, 102, 32, 60, 45, 32, 39, 73, 78, 83, 84, 69, 65, 68, 39, 32, 39, 79, 70, - 39, 10, 84, 114, 105, 103, 103, 101, 114, 69, 118, 101, 110, 116, 32, 60, 45, 32, 84, 114, - 105, 103, 103, 101, 114, 69, 118, 101, 110, 116, 85, 112, 100, 97, 116, 101, 79, 102, 32, 47, - 32, 84, 114, 105, 103, 103, 101, 114, 69, 118, 101, 110, 116, 73, 110, 115, 101, 114, 116, 32, - 47, 32, 84, 114, 105, 103, 103, 101, 114, 69, 118, 101, 110, 116, 68, 101, 108, 101, 116, 101, - 32, 47, 32, 84, 114, 105, 103, 103, 101, 114, 69, 118, 101, 110, 116, 85, 112, 100, 97, 116, - 101, 10, 84, 114, 105, 103, 103, 101, 114, 69, 118, 101, 110, 116, 73, 110, 115, 101, 114, 116, - 32, 60, 45, 32, 39, 73, 78, 83, 69, 82, 84, 39, 10, 84, 114, 105, 103, 103, 101, 114, - 69, 118, 101, 110, 116, 68, 101, 108, 101, 116, 101, 32, 60, 45, 32, 39, 68, 69, 76, 69, - 84, 69, 39, 10, 84, 114, 105, 103, 103, 101, 114, 69, 118, 101, 110, 116, 85, 112, 100, 97, - 116, 101, 32, 60, 45, 32, 39, 85, 80, 68, 65, 84, 69, 39, 10, 84, 114, 105, 103, 103, - 101, 114, 69, 118, 101, 110, 116, 85, 112, 100, 97, 116, 101, 79, 102, 32, 60, 45, 32, 39, - 85, 80, 68, 65, 84, 69, 39, 32, 39, 79, 70, 39, 32, 84, 114, 105, 103, 103, 101, 114, - 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 10, 84, 114, 105, 103, 103, 101, 114, 67, 111, - 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 67, 111, 108, - 73, 100, 41, 10, 70, 111, 114, 69, 97, 99, 104, 67, 108, 97, 117, 115, 101, 32, 60, 45, - 32, 70, 111, 114, 69, 97, 99, 104, 82, 111, 119, 32, 47, 32, 70, 111, 114, 69, 97, 99, - 104, 83, 116, 97, 116, 101, 109, 101, 110, 116, 10, 70, 111, 114, 69, 97, 99, 104, 82, 111, - 119, 32, 60, 45, 32, 39, 70, 79, 82, 39, 32, 39, 69, 65, 67, 72, 39, 32, 39, 82, - 79, 87, 39, 10, 70, 111, 114, 69, 97, 99, 104, 83, 116, 97, 116, 101, 109, 101, 110, 116, - 32, 60, 45, 32, 39, 70, 79, 82, 39, 32, 39, 69, 65, 67, 72, 39, 32, 39, 83, 84, - 65, 84, 69, 77, 69, 78, 84, 39, 10, 67, 114, 101, 97, 116, 101, 84, 121, 112, 101, 83, - 116, 109, 116, 32, 60, 45, 32, 39, 84, 89, 80, 69, 39, 32, 73, 102, 78, 111, 116, 69, - 120, 105, 115, 116, 115, 63, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, - 32, 39, 65, 83, 39, 32, 67, 114, 101, 97, 116, 101, 84, 121, 112, 101, 10, 67, 114, 101, - 97, 116, 101, 84, 121, 112, 101, 32, 60, 45, 32, 69, 110, 117, 109, 83, 101, 108, 101, 99, - 116, 84, 121, 112, 101, 32, 47, 32, 69, 110, 117, 109, 83, 116, 114, 105, 110, 103, 76, 105, - 116, 101, 114, 97, 108, 76, 105, 115, 116, 32, 47, 32, 67, 114, 101, 97, 116, 101, 84, 121, - 112, 101, 70, 114, 111, 109, 84, 121, 112, 101, 10, 67, 114, 101, 97, 116, 101, 84, 121, 112, - 101, 70, 114, 111, 109, 84, 121, 112, 101, 32, 60, 45, 32, 84, 121, 112, 101, 10, 69, 110, - 117, 109, 83, 101, 108, 101, 99, 116, 84, 121, 112, 101, 32, 60, 45, 32, 39, 69, 78, 85, - 77, 39, 32, 80, 97, 114, 101, 110, 115, 40, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, - 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 41, 10, 69, 110, 117, 109, 83, - 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 76, 105, 115, 116, 32, 60, 45, 32, - 39, 69, 78, 85, 77, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 83, - 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 41, 63, 41, 10, 67, 114, 101, 97, - 116, 101, 86, 105, 101, 119, 83, 116, 109, 116, 32, 60, 45, 32, 67, 114, 101, 97, 116, 101, - 83, 101, 99, 117, 114, 101, 63, 32, 67, 114, 101, 97, 116, 101, 82, 101, 99, 117, 114, 115, - 105, 118, 101, 63, 32, 39, 86, 73, 69, 87, 39, 32, 73, 102, 78, 111, 116, 69, 120, 105, - 115, 116, 115, 63, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 32, 73, - 110, 115, 101, 114, 116, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 63, 32, 87, 105, 116, - 104, 76, 105, 115, 116, 63, 32, 39, 65, 83, 39, 32, 83, 101, 108, 101, 99, 116, 83, 116, - 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 10, 67, 114, 101, 97, - 116, 101, 82, 101, 99, 117, 114, 115, 105, 118, 101, 32, 60, 45, 32, 39, 82, 69, 67, 85, - 82, 83, 73, 86, 69, 39, 10, 67, 114, 101, 97, 116, 101, 83, 101, 99, 117, 114, 101, 32, - 60, 45, 32, 39, 83, 69, 67, 85, 82, 69, 39, 10, 68, 101, 97, 108, 108, 111, 99, 97, - 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 68, 69, 65, 76, - 76, 79, 67, 65, 84, 69, 39, 32, 68, 101, 97, 108, 108, 111, 99, 97, 116, 101, 80, 114, - 101, 112, 97, 114, 101, 63, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 68, 101, - 97, 108, 108, 111, 99, 97, 116, 101, 80, 114, 101, 112, 97, 114, 101, 32, 60, 45, 32, 39, - 80, 82, 69, 80, 65, 82, 69, 39, 10, 68, 101, 108, 101, 116, 101, 83, 116, 97, 116, 101, - 109, 101, 110, 116, 32, 60, 45, 32, 87, 105, 116, 104, 67, 108, 97, 117, 115, 101, 63, 32, - 39, 68, 69, 76, 69, 84, 69, 39, 32, 39, 70, 82, 79, 77, 39, 32, 84, 97, 114, 103, - 101, 116, 79, 112, 116, 65, 108, 105, 97, 115, 32, 68, 101, 108, 101, 116, 101, 85, 115, 105, - 110, 103, 67, 108, 97, 117, 115, 101, 63, 32, 87, 104, 101, 114, 101, 67, 108, 97, 117, 115, - 101, 63, 32, 82, 101, 116, 117, 114, 110, 105, 110, 103, 67, 108, 97, 117, 115, 101, 63, 10, - 84, 114, 117, 110, 99, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, - 32, 39, 84, 82, 85, 78, 67, 65, 84, 69, 39, 32, 39, 84, 65, 66, 76, 69, 39, 63, - 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 10, 84, 97, 114, 103, 101, - 116, 79, 112, 116, 65, 108, 105, 97, 115, 32, 60, 45, 32, 66, 97, 115, 101, 84, 97, 98, - 108, 101, 78, 97, 109, 101, 32, 39, 65, 83, 39, 63, 32, 67, 111, 108, 73, 100, 63, 10, - 68, 101, 108, 101, 116, 101, 85, 115, 105, 110, 103, 67, 108, 97, 117, 115, 101, 32, 60, 45, - 32, 39, 85, 83, 73, 78, 71, 39, 32, 76, 105, 115, 116, 40, 84, 97, 98, 108, 101, 82, - 101, 102, 41, 10, 68, 101, 115, 99, 114, 105, 98, 101, 83, 116, 97, 116, 101, 109, 101, 110, - 116, 32, 60, 45, 32, 83, 104, 111, 119, 84, 97, 98, 108, 101, 115, 32, 47, 32, 83, 104, - 111, 119, 68, 101, 112, 114, 101, 99, 97, 116, 101, 100, 83, 101, 108, 101, 99, 116, 32, 47, - 32, 68, 101, 115, 99, 114, 105, 98, 101, 83, 101, 108, 101, 99, 116, 32, 47, 32, 83, 104, - 111, 119, 65, 108, 108, 84, 97, 98, 108, 101, 115, 32, 47, 32, 83, 104, 111, 119, 66, 121, - 78, 97, 109, 101, 32, 47, 32, 68, 101, 115, 99, 114, 105, 98, 101, 66, 121, 78, 97, 109, - 101, 10, 35, 32, 34, 68, 69, 83, 67, 82, 73, 66, 69, 32, 40, 113, 117, 101, 114, 121, - 41, 34, 32, 97, 110, 100, 32, 34, 83, 85, 77, 77, 65, 82, 73, 90, 69, 32, 40, 113, - 117, 101, 114, 121, 41, 34, 32, 100, 101, 115, 99, 114, 105, 98, 101, 32, 116, 104, 101, 32, - 113, 117, 101, 114, 121, 39, 115, 32, 99, 111, 108, 117, 109, 110, 115, 46, 32, 34, 83, 72, - 79, 87, 32, 40, 113, 117, 101, 114, 121, 41, 34, 32, 100, 111, 101, 115, 32, 116, 111, 111, - 32, 98, 117, 116, 32, 105, 115, 32, 100, 101, 112, 114, 101, 99, 97, 116, 101, 100, 44, 10, - 35, 32, 115, 111, 32, 83, 104, 111, 119, 68, 101, 112, 114, 101, 99, 97, 116, 101, 100, 83, - 101, 108, 101, 99, 116, 32, 105, 115, 32, 112, 97, 114, 115, 101, 100, 32, 121, 101, 116, 32, - 115, 117, 112, 112, 114, 101, 115, 115, 101, 100, 32, 102, 114, 111, 109, 32, 97, 117, 116, 111, - 99, 111, 109, 112, 108, 101, 116, 101, 32, 40, 109, 97, 116, 99, 104, 101, 114, 95, 102, 97, - 99, 116, 111, 114, 121, 46, 99, 112, 112, 41, 32, 45, 32, 83, 72, 79, 87, 32, 119, 111, - 110, 39, 116, 32, 111, 102, 102, 101, 114, 32, 116, 111, 32, 99, 111, 109, 112, 108, 101, 116, - 101, 32, 97, 32, 113, 117, 101, 114, 121, 46, 10, 83, 104, 111, 119, 68, 101, 112, 114, 101, - 99, 97, 116, 101, 100, 83, 101, 108, 101, 99, 116, 32, 60, 45, 32, 83, 104, 111, 119, 82, - 117, 108, 101, 32, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, - 110, 116, 101, 114, 110, 97, 108, 10, 68, 101, 115, 99, 114, 105, 98, 101, 83, 101, 108, 101, - 99, 116, 32, 60, 45, 32, 68, 101, 115, 99, 114, 105, 98, 101, 79, 114, 83, 117, 109, 109, - 97, 114, 105, 122, 101, 32, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, - 116, 73, 110, 116, 101, 114, 110, 97, 108, 10, 83, 104, 111, 119, 65, 108, 108, 84, 97, 98, - 108, 101, 115, 32, 60, 45, 32, 83, 104, 111, 119, 79, 114, 68, 101, 115, 99, 114, 105, 98, - 101, 32, 39, 65, 76, 76, 39, 32, 39, 84, 65, 66, 76, 69, 83, 39, 63, 10, 83, 104, - 111, 119, 84, 97, 98, 108, 101, 115, 32, 60, 45, 32, 83, 104, 111, 119, 79, 114, 68, 101, - 115, 99, 114, 105, 98, 101, 32, 39, 84, 65, 66, 76, 69, 83, 39, 32, 39, 70, 82, 79, - 77, 39, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 10, 35, 32, 34, - 83, 72, 79, 87, 32, 110, 97, 109, 101, 34, 32, 105, 115, 32, 115, 101, 116, 116, 105, 110, - 103, 115, 45, 102, 105, 114, 115, 116, 44, 32, 115, 111, 32, 105, 116, 115, 32, 116, 97, 114, - 103, 101, 116, 32, 97, 117, 116, 111, 99, 111, 109, 112, 108, 101, 116, 101, 115, 32, 115, 101, - 116, 116, 105, 110, 103, 32, 110, 97, 109, 101, 115, 32, 40, 97, 110, 100, 32, 116, 104, 101, - 32, 115, 112, 101, 99, 105, 97, 108, 32, 77, 121, 83, 81, 76, 32, 102, 111, 114, 109, 115, - 41, 44, 32, 110, 111, 116, 10, 35, 32, 97, 114, 98, 105, 116, 114, 97, 114, 121, 32, 116, - 97, 98, 108, 101, 115, 46, 32, 34, 68, 69, 83, 67, 82, 73, 66, 69, 34, 47, 34, 83, - 85, 77, 77, 65, 82, 73, 90, 69, 34, 32, 100, 101, 115, 99, 114, 105, 98, 101, 32, 97, - 32, 114, 101, 108, 97, 116, 105, 111, 110, 44, 32, 115, 111, 32, 116, 104, 101, 105, 114, 32, - 116, 97, 114, 103, 101, 116, 32, 97, 117, 116, 111, 99, 111, 109, 112, 108, 101, 116, 101, 115, - 32, 116, 97, 98, 108, 101, 32, 110, 97, 109, 101, 115, 46, 10, 83, 104, 111, 119, 66, 121, - 78, 97, 109, 101, 32, 60, 45, 32, 83, 104, 111, 119, 82, 117, 108, 101, 32, 83, 104, 111, - 119, 84, 97, 114, 103, 101, 116, 63, 10, 68, 101, 115, 99, 114, 105, 98, 101, 66, 121, 78, - 97, 109, 101, 32, 60, 45, 32, 68, 101, 115, 99, 114, 105, 98, 101, 79, 114, 83, 117, 109, - 109, 97, 114, 105, 122, 101, 32, 68, 101, 115, 99, 114, 105, 98, 101, 84, 97, 114, 103, 101, - 116, 63, 10, 68, 101, 115, 99, 114, 105, 98, 101, 79, 114, 83, 117, 109, 109, 97, 114, 105, - 122, 101, 32, 60, 45, 32, 68, 101, 115, 99, 114, 105, 98, 101, 82, 117, 108, 101, 32, 47, - 32, 83, 117, 109, 109, 97, 114, 105, 122, 101, 10, 35, 32, 83, 72, 79, 87, 39, 115, 32, - 111, 119, 110, 32, 102, 111, 114, 109, 115, 32, 40, 97, 32, 115, 101, 116, 116, 105, 110, 103, - 115, 45, 102, 105, 114, 115, 116, 32, 110, 97, 109, 101, 32, 97, 110, 100, 32, 116, 104, 101, - 32, 115, 112, 101, 99, 105, 97, 108, 32, 77, 121, 83, 81, 76, 32, 102, 111, 114, 109, 115, - 41, 32, 112, 108, 117, 115, 32, 116, 104, 101, 32, 100, 101, 115, 99, 114, 105, 98, 101, 45, - 115, 116, 121, 108, 101, 32, 116, 97, 114, 103, 101, 116, 115, 32, 105, 116, 32, 115, 116, 105, - 108, 108, 10, 35, 32, 116, 111, 108, 101, 114, 97, 116, 101, 115, 32, 102, 111, 114, 32, 116, - 104, 101, 32, 100, 101, 112, 114, 101, 99, 97, 116, 101, 100, 32, 34, 100, 101, 115, 99, 114, - 105, 98, 101, 32, 118, 105, 97, 32, 83, 72, 79, 87, 34, 46, 32, 79, 110, 108, 121, 32, - 115, 101, 116, 116, 105, 110, 103, 115, 32, 97, 110, 100, 32, 116, 104, 101, 32, 115, 112, 101, - 99, 105, 97, 108, 32, 102, 111, 114, 109, 115, 32, 97, 114, 101, 32, 97, 117, 116, 111, 99, - 111, 109, 112, 108, 101, 116, 101, 100, 32, 97, 102, 116, 101, 114, 32, 83, 72, 79, 87, 59, - 10, 35, 32, 97, 32, 113, 117, 97, 108, 105, 102, 105, 101, 100, 32, 116, 97, 98, 108, 101, - 32, 105, 115, 32, 112, 97, 114, 115, 101, 100, 32, 98, 117, 116, 32, 105, 116, 115, 32, 115, - 117, 103, 103, 101, 115, 116, 105, 111, 110, 115, 32, 97, 114, 101, 32, 115, 117, 112, 112, 114, - 101, 115, 115, 101, 100, 32, 40, 115, 101, 101, 32, 109, 97, 116, 99, 104, 101, 114, 46, 99, - 112, 112, 41, 46, 10, 83, 104, 111, 119, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 83, - 104, 111, 119, 68, 101, 112, 114, 101, 99, 97, 116, 101, 100, 81, 117, 97, 108, 105, 102, 105, - 101, 100, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 47, 32, 83, 104, 111, 119, 83, 101, - 116, 116, 105, 110, 103, 78, 97, 109, 101, 32, 47, 32, 68, 101, 115, 99, 114, 105, 98, 101, - 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 83, 104, 111, 119, - 83, 112, 101, 99, 105, 97, 108, 70, 111, 114, 109, 10, 35, 32, 34, 83, 72, 79, 87, 32, - 99, 97, 116, 46, 116, 98, 108, 34, 32, 115, 116, 105, 108, 108, 32, 100, 101, 115, 99, 114, - 105, 98, 101, 115, 32, 97, 32, 116, 97, 98, 108, 101, 32, 40, 100, 101, 112, 114, 101, 99, - 97, 116, 101, 100, 41, 44, 32, 98, 117, 116, 32, 105, 115, 32, 115, 117, 112, 112, 114, 101, - 115, 115, 101, 100, 32, 102, 114, 111, 109, 32, 97, 117, 116, 111, 99, 111, 109, 112, 108, 101, - 116, 101, 32, 40, 115, 101, 101, 32, 109, 97, 116, 99, 104, 101, 114, 46, 99, 112, 112, 41, - 32, 115, 111, 32, 83, 72, 79, 87, 10, 35, 32, 100, 111, 101, 115, 32, 110, 111, 116, 32, - 99, 111, 109, 112, 108, 101, 116, 101, 32, 113, 117, 97, 108, 105, 102, 105, 101, 100, 32, 116, - 97, 98, 108, 101, 115, 32, 40, 117, 110, 108, 105, 107, 101, 32, 68, 69, 83, 67, 82, 73, - 66, 69, 44, 32, 119, 104, 105, 99, 104, 32, 100, 111, 101, 115, 41, 46, 32, 87, 114, 97, - 112, 115, 32, 116, 104, 101, 32, 103, 101, 110, 101, 114, 105, 99, 32, 81, 117, 97, 108, 105, - 102, 105, 101, 100, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 40, 115, 101, 108, 101, 99, - 116, 46, 103, 114, 97, 109, 41, 46, 10, 83, 104, 111, 119, 68, 101, 112, 114, 101, 99, 97, - 116, 101, 100, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 78, 97, 109, - 101, 32, 60, 45, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 78, - 97, 109, 101, 10, 35, 32, 65, 32, 98, 97, 114, 101, 32, 105, 100, 101, 110, 116, 105, 102, - 105, 101, 114, 32, 105, 115, 32, 115, 117, 103, 103, 101, 115, 116, 101, 100, 32, 97, 115, 32, - 97, 32, 115, 101, 116, 116, 105, 110, 103, 32, 110, 97, 109, 101, 32, 40, 83, 101, 116, 116, - 105, 110, 103, 78, 97, 109, 101, 32, 105, 115, 32, 111, 118, 101, 114, 114, 105, 100, 100, 101, - 110, 32, 116, 111, 32, 83, 85, 71, 71, 69, 83, 84, 95, 83, 69, 84, 84, 73, 78, 71, - 95, 78, 65, 77, 69, 41, 46, 10, 83, 104, 111, 119, 83, 101, 116, 116, 105, 110, 103, 78, - 97, 109, 101, 32, 60, 45, 32, 83, 101, 116, 116, 105, 110, 103, 78, 97, 109, 101, 10, 35, - 32, 84, 104, 101, 32, 115, 112, 101, 99, 105, 97, 108, 32, 77, 121, 83, 81, 76, 32, 102, - 111, 114, 109, 115, 32, 97, 114, 101, 32, 111, 110, 108, 121, 32, 104, 101, 114, 101, 32, 115, - 111, 32, 97, 117, 116, 111, 99, 111, 109, 112, 108, 101, 116, 101, 32, 115, 117, 103, 103, 101, - 115, 116, 115, 32, 116, 104, 101, 115, 101, 32, 107, 101, 121, 119, 111, 114, 100, 115, 32, 97, - 102, 116, 101, 114, 32, 83, 72, 79, 87, 59, 32, 112, 97, 114, 115, 105, 110, 103, 32, 111, - 102, 32, 101, 46, 103, 46, 10, 35, 32, 34, 83, 72, 79, 87, 32, 100, 97, 116, 97, 98, - 97, 115, 101, 115, 34, 32, 114, 101, 115, 111, 108, 118, 101, 115, 32, 116, 104, 114, 111, 117, - 103, 104, 32, 83, 104, 111, 119, 83, 101, 116, 116, 105, 110, 103, 78, 97, 109, 101, 32, 40, - 116, 104, 101, 32, 105, 100, 101, 110, 116, 105, 102, 105, 101, 114, 41, 32, 97, 98, 111, 118, - 101, 44, 32, 119, 104, 105, 99, 104, 32, 116, 104, 101, 32, 116, 114, 97, 110, 115, 102, 111, - 114, 109, 101, 114, 32, 115, 112, 101, 99, 105, 97, 108, 45, 99, 97, 115, 101, 115, 46, 10, - 83, 104, 111, 119, 83, 112, 101, 99, 105, 97, 108, 70, 111, 114, 109, 32, 60, 45, 32, 39, - 68, 65, 84, 65, 66, 65, 83, 69, 83, 39, 32, 47, 32, 39, 83, 67, 72, 69, 77, 65, - 83, 39, 32, 47, 32, 39, 84, 65, 66, 76, 69, 83, 39, 32, 47, 32, 39, 86, 65, 82, - 73, 65, 66, 76, 69, 83, 39, 10, 68, 101, 115, 99, 114, 105, 98, 101, 84, 97, 114, 103, - 101, 116, 32, 60, 45, 32, 68, 101, 115, 99, 114, 105, 98, 101, 66, 97, 115, 101, 84, 97, - 98, 108, 101, 78, 97, 109, 101, 32, 47, 32, 68, 101, 115, 99, 114, 105, 98, 101, 83, 116, - 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, 68, 101, 115, 99, 114, 105, 98, 101, - 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 60, 45, 32, 66, 97, 115, - 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 10, 68, 101, 115, 99, 114, 105, 98, 101, 83, - 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 83, 116, 114, 105, - 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, 83, 117, 109, 109, 97, 114, 105, 122, 101, 32, - 60, 45, 32, 83, 117, 109, 109, 97, 114, 105, 122, 101, 82, 117, 108, 101, 10, 83, 117, 109, - 109, 97, 114, 105, 122, 101, 82, 117, 108, 101, 32, 60, 45, 32, 39, 83, 85, 77, 77, 65, - 82, 73, 90, 69, 39, 10, 83, 104, 111, 119, 79, 114, 68, 101, 115, 99, 114, 105, 98, 101, - 32, 60, 45, 32, 83, 104, 111, 119, 82, 117, 108, 101, 32, 47, 32, 68, 101, 115, 99, 114, - 105, 98, 101, 82, 117, 108, 101, 10, 83, 104, 111, 119, 82, 117, 108, 101, 32, 60, 45, 32, - 39, 83, 72, 79, 87, 39, 10, 68, 101, 115, 99, 114, 105, 98, 101, 82, 117, 108, 101, 32, - 60, 45, 32, 68, 101, 115, 99, 114, 105, 98, 101, 76, 111, 110, 103, 82, 117, 108, 101, 32, - 47, 32, 68, 101, 115, 99, 82, 117, 108, 101, 10, 68, 101, 115, 99, 114, 105, 98, 101, 76, - 111, 110, 103, 82, 117, 108, 101, 32, 60, 45, 32, 39, 68, 69, 83, 67, 82, 73, 66, 69, - 39, 10, 68, 101, 115, 99, 82, 117, 108, 101, 32, 60, 45, 32, 39, 68, 69, 83, 67, 39, - 10, 68, 101, 116, 97, 99, 104, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, - 39, 68, 69, 84, 65, 67, 72, 39, 32, 68, 97, 116, 97, 98, 97, 115, 101, 63, 32, 73, - 102, 69, 120, 105, 115, 116, 115, 63, 32, 67, 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, - 10, 68, 114, 111, 112, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 68, - 82, 79, 80, 39, 32, 68, 114, 111, 112, 69, 110, 116, 114, 105, 101, 115, 32, 68, 114, 111, - 112, 66, 101, 104, 97, 118, 105, 111, 114, 63, 10, 68, 114, 111, 112, 69, 110, 116, 114, 105, - 101, 115, 32, 60, 45, 10, 9, 68, 114, 111, 112, 84, 97, 98, 108, 101, 32, 47, 10, 9, - 68, 114, 111, 112, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 47, 10, - 9, 68, 114, 111, 112, 70, 117, 110, 99, 116, 105, 111, 110, 32, 47, 10, 9, 68, 114, 111, - 112, 83, 99, 104, 101, 109, 97, 32, 47, 10, 9, 68, 114, 111, 112, 73, 110, 100, 101, 120, - 32, 47, 10, 9, 68, 114, 111, 112, 83, 101, 113, 117, 101, 110, 99, 101, 32, 47, 10, 9, - 68, 114, 111, 112, 67, 111, 108, 108, 97, 116, 105, 111, 110, 32, 47, 10, 9, 68, 114, 111, - 112, 84, 121, 112, 101, 32, 47, 10, 9, 68, 114, 111, 112, 83, 101, 99, 114, 101, 116, 32, - 47, 10, 9, 68, 114, 111, 112, 84, 114, 105, 103, 103, 101, 114, 10, 68, 114, 111, 112, 84, - 114, 105, 103, 103, 101, 114, 32, 60, 45, 32, 39, 84, 82, 73, 71, 71, 69, 82, 39, 32, - 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 84, 114, 105, 103, 103, 101, 114, 78, 97, 109, - 101, 32, 39, 79, 78, 39, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, - 10, 68, 114, 111, 112, 84, 97, 98, 108, 101, 32, 60, 45, 32, 84, 97, 98, 108, 101, 79, - 114, 86, 105, 101, 119, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, 115, 116, - 40, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 41, 10, 68, 114, 111, 112, - 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 67, 111, 109, - 109, 101, 110, 116, 77, 97, 99, 114, 111, 84, 97, 98, 108, 101, 32, 73, 102, 69, 120, 105, - 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, - 105, 111, 110, 78, 97, 109, 101, 41, 10, 68, 114, 111, 112, 70, 117, 110, 99, 116, 105, 111, - 110, 32, 60, 45, 32, 70, 117, 110, 99, 116, 105, 111, 110, 84, 121, 112, 101, 77, 97, 99, - 114, 111, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 70, 117, - 110, 99, 116, 105, 111, 110, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 41, 10, 68, 114, - 111, 112, 83, 99, 104, 101, 109, 97, 32, 60, 45, 32, 39, 83, 67, 72, 69, 77, 65, 39, - 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 81, 117, 97, 108, - 105, 102, 105, 101, 100, 78, 97, 109, 101, 41, 10, 68, 114, 111, 112, 73, 110, 100, 101, 120, - 32, 60, 45, 32, 39, 73, 78, 68, 69, 88, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, - 63, 32, 76, 105, 115, 116, 40, 81, 117, 97, 108, 105, 102, 105, 101, 100, 73, 110, 100, 101, - 120, 78, 97, 109, 101, 41, 10, 81, 117, 97, 108, 105, 102, 105, 101, 100, 73, 110, 100, 101, - 120, 78, 97, 109, 101, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 82, 101, 115, 101, - 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 73, 110, 100, 101, 120, 32, 47, 32, 83, 99, - 104, 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 73, 110, 100, 101, 120, 32, 47, 32, - 81, 117, 97, 108, 105, 102, 105, 101, 100, 73, 110, 100, 101, 120, 78, 97, 109, 101, 83, 116, - 114, 105, 110, 103, 10, 81, 117, 97, 108, 105, 102, 105, 101, 100, 73, 110, 100, 101, 120, 78, - 97, 109, 101, 83, 116, 114, 105, 110, 103, 32, 60, 45, 32, 73, 110, 100, 101, 120, 78, 97, - 109, 101, 10, 83, 99, 104, 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 73, 110, 100, - 101, 120, 32, 60, 45, 32, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, - 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 73, 110, 100, 101, 120, 78, - 97, 109, 101, 10, 67, 97, 116, 97, 108, 111, 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, - 99, 104, 101, 109, 97, 73, 110, 100, 101, 120, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, - 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, - 118, 101, 100, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, - 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 73, 110, 100, 101, 120, 78, 97, 109, 101, - 10, 68, 114, 111, 112, 83, 101, 113, 117, 101, 110, 99, 101, 32, 60, 45, 32, 39, 83, 69, - 81, 85, 69, 78, 67, 69, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, - 115, 116, 40, 81, 117, 97, 108, 105, 102, 105, 101, 100, 83, 101, 113, 117, 101, 110, 99, 101, - 78, 97, 109, 101, 41, 10, 68, 114, 111, 112, 67, 111, 108, 108, 97, 116, 105, 111, 110, 32, - 60, 45, 32, 39, 67, 79, 76, 76, 65, 84, 73, 79, 78, 39, 32, 73, 102, 69, 120, 105, - 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 67, 111, 108, 108, 97, 116, 105, 111, 110, 78, - 97, 109, 101, 41, 10, 68, 114, 111, 112, 84, 121, 112, 101, 32, 60, 45, 32, 39, 84, 89, - 80, 69, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 81, - 117, 97, 108, 105, 102, 105, 101, 100, 84, 121, 112, 101, 78, 97, 109, 101, 41, 10, 68, 114, - 111, 112, 83, 101, 99, 114, 101, 116, 32, 60, 45, 32, 84, 101, 109, 112, 111, 114, 97, 114, - 121, 63, 32, 39, 83, 69, 67, 82, 69, 84, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, - 63, 32, 83, 101, 99, 114, 101, 116, 78, 97, 109, 101, 32, 68, 114, 111, 112, 83, 101, 99, - 114, 101, 116, 83, 116, 111, 114, 97, 103, 101, 63, 10, 84, 97, 98, 108, 101, 79, 114, 86, - 105, 101, 119, 32, 60, 45, 32, 67, 111, 109, 109, 101, 110, 116, 84, 97, 98, 108, 101, 32, - 47, 32, 67, 111, 109, 109, 101, 110, 116, 86, 105, 101, 119, 32, 47, 32, 77, 97, 116, 101, - 114, 105, 97, 108, 105, 122, 101, 100, 86, 105, 101, 119, 69, 110, 116, 114, 121, 10, 77, 97, - 116, 101, 114, 105, 97, 108, 105, 122, 101, 100, 86, 105, 101, 119, 69, 110, 116, 114, 121, 32, - 60, 45, 32, 39, 77, 65, 84, 69, 82, 73, 65, 76, 73, 90, 69, 68, 39, 32, 39, 86, - 73, 69, 87, 39, 10, 70, 117, 110, 99, 116, 105, 111, 110, 84, 121, 112, 101, 77, 97, 99, - 114, 111, 32, 60, 45, 32, 70, 117, 110, 99, 116, 105, 111, 110, 84, 121, 112, 101, 77, 97, - 99, 114, 111, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 70, 117, 110, 99, 116, 105, 111, - 110, 84, 121, 112, 101, 70, 117, 110, 99, 116, 105, 111, 110, 10, 70, 117, 110, 99, 116, 105, - 111, 110, 84, 121, 112, 101, 77, 97, 99, 114, 111, 75, 101, 121, 119, 111, 114, 100, 32, 60, - 45, 32, 39, 77, 65, 67, 82, 79, 39, 10, 70, 117, 110, 99, 116, 105, 111, 110, 84, 121, - 112, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 70, 85, 78, 67, 84, - 73, 79, 78, 39, 10, 68, 114, 111, 112, 66, 101, 104, 97, 118, 105, 111, 114, 32, 60, 45, - 32, 67, 97, 115, 99, 97, 100, 101, 68, 114, 111, 112, 66, 101, 104, 97, 118, 105, 111, 114, - 32, 47, 32, 82, 101, 115, 116, 114, 105, 99, 116, 68, 114, 111, 112, 66, 101, 104, 97, 118, - 105, 111, 114, 10, 67, 97, 115, 99, 97, 100, 101, 68, 114, 111, 112, 66, 101, 104, 97, 118, - 105, 111, 114, 32, 60, 45, 32, 39, 67, 65, 83, 67, 65, 68, 69, 39, 10, 82, 101, 115, - 116, 114, 105, 99, 116, 68, 114, 111, 112, 66, 101, 104, 97, 118, 105, 111, 114, 32, 60, 45, - 32, 39, 82, 69, 83, 84, 82, 73, 67, 84, 39, 10, 73, 102, 69, 120, 105, 115, 116, 115, - 32, 60, 45, 32, 39, 73, 70, 39, 32, 39, 69, 88, 73, 83, 84, 83, 39, 10, 68, 114, - 111, 112, 83, 101, 99, 114, 101, 116, 83, 116, 111, 114, 97, 103, 101, 32, 60, 45, 32, 39, - 70, 82, 79, 77, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 69, 120, 101, - 99, 117, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 69, 88, - 69, 67, 85, 84, 69, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 84, 97, - 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, - 63, 10, 69, 120, 112, 108, 97, 105, 110, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, - 45, 32, 39, 69, 88, 80, 76, 65, 73, 78, 39, 32, 65, 110, 97, 108, 121, 122, 101, 75, - 101, 121, 119, 111, 114, 100, 63, 32, 69, 120, 112, 108, 97, 105, 110, 79, 112, 116, 105, 111, - 110, 76, 105, 115, 116, 63, 32, 69, 120, 112, 108, 97, 105, 110, 97, 98, 108, 101, 83, 116, - 97, 116, 101, 109, 101, 110, 116, 115, 10, 69, 120, 112, 108, 97, 105, 110, 79, 112, 116, 105, - 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, - 116, 40, 69, 120, 112, 108, 97, 105, 110, 79, 112, 116, 105, 111, 110, 41, 41, 10, 69, 120, - 112, 108, 97, 105, 110, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 69, 120, 112, 108, 97, - 105, 110, 79, 112, 116, 105, 111, 110, 78, 97, 109, 101, 32, 69, 120, 112, 114, 101, 115, 115, - 105, 111, 110, 63, 10, 69, 120, 112, 108, 97, 105, 110, 79, 112, 116, 105, 111, 110, 78, 97, - 109, 101, 32, 60, 45, 32, 65, 110, 97, 108, 121, 122, 101, 75, 101, 121, 119, 111, 114, 100, - 32, 47, 32, 67, 111, 108, 73, 100, 32, 47, 32, 70, 117, 110, 99, 78, 97, 109, 101, 75, - 101, 121, 119, 111, 114, 100, 32, 47, 32, 84, 121, 112, 101, 78, 97, 109, 101, 75, 101, 121, - 119, 111, 114, 100, 10, 69, 120, 112, 108, 97, 105, 110, 83, 101, 108, 101, 99, 116, 83, 116, - 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 83, 101, 108, 101, 99, 116, 83, 116, 97, - 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 10, 69, 120, 112, 108, 97, - 105, 110, 97, 98, 108, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 115, 32, 60, 45, 10, - 32, 32, 32, 32, 65, 108, 116, 101, 114, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, - 10, 32, 32, 32, 32, 65, 110, 97, 108, 121, 122, 101, 83, 116, 97, 116, 101, 109, 101, 110, - 116, 32, 47, 10, 32, 32, 32, 32, 67, 97, 108, 108, 83, 116, 97, 116, 101, 109, 101, 110, - 116, 32, 47, 10, 32, 32, 32, 32, 67, 104, 101, 99, 107, 112, 111, 105, 110, 116, 83, 116, - 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 32, 32, 32, 32, 67, 111, 112, 121, 83, 116, - 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 32, 32, 32, 32, 67, 114, 101, 97, 116, 101, - 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 32, 32, 32, 32, 68, 101, 97, 108, - 108, 111, 99, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 32, 32, - 32, 32, 68, 101, 108, 101, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 80, 114, 111, 103, 114, 97, 109, 32, 60, 45, 32, 84, 111, 112, 76, 101, 118, 101, 108, 83, 116, 97, 116, 101, 109, 101, 110, 116, 42, 10, + 84, 111, 112, 76, 101, 118, 101, 108, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 83, 116, 97, 116, 101, 109, 101, 110, 116, 63, 32, 40, 39, 59, 39, 43, 32, 47, 32, 69, 110, 100, 79, 102, 73, 110, 112, 117, 116, 41, 10, + 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 10, + 9, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 69, 120, 116, 101, 110, 115, 105, 111, 110, 82, 101, 112, 111, 115, 105, 116, 111, 114, 121, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 67, 114, 101, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 83, 101, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 80, 114, 97, 103, 109, 97, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 67, 97, 108, 108, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 73, 110, 115, 101, 114, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 68, 114, 111, 112, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 67, 111, 112, 121, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 69, 120, 112, 108, 97, 105, 110, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 85, 112, 100, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 80, 114, 101, 112, 97, 114, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 69, 120, 101, 99, 117, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 65, 108, 116, 101, 114, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 68, 101, 108, 101, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 65, 116, 116, 97, 99, 104, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 85, 115, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 68, 101, 116, 97, 99, 104, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 67, 104, 101, 99, 107, 112, 111, 105, 110, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 86, 97, 99, 117, 117, 109, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 82, 101, 115, 101, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 69, 120, 112, 111, 114, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 73, 109, 112, 111, 114, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 67, 111, 109, 109, 101, 110, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 68, 101, 97, 108, 108, 111, 99, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 84, 114, 117, 110, 99, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 76, 111, 97, 100, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 73, 110, 115, 116, 97, 108, 108, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 85, 112, 100, 97, 116, 101, 69, 120, 116, 101, 110, 115, 105, 111, 110, 115, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 65, 110, 97, 108, 121, 122, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 77, 101, 114, 103, 101, 73, 110, 116, 111, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 67, 111, 110, 110, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 68, 105, 115, 99, 111, 110, 110, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 10, + 65, 110, 97, 108, 121, 122, 101, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 65, 78, 65, 76, 89, 90, 69, 39, 32, 47, 32, 39, 65, 78, 65, 76, 89, 83, 69, 39, 10, + 67, 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 83, 99, 104, 101, 109, 97, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 82, 101, 115, 101, 114, 118, 101, 100, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 82, 101, 115, 101, 114, 118, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 82, 101, 115, 101, 114, 118, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 73, 110, 100, 101, 120, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 82, 101, 115, 101, 114, 118, 101, 100, 73, 110, 100, 101, 120, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 83, 101, 116, 116, 105, 110, 103, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 80, 114, 97, 103, 109, 97, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 82, 101, 115, 101, 114, 118, 101, 100, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 82, 101, 115, 101, 114, 118, 101, 100, 84, 121, 112, 101, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 78, 97, 109, 101, 32, 60, 45, 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 10, + 83, 101, 113, 117, 101, 110, 99, 101, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 67, 111, 108, 108, 97, 116, 105, 111, 110, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 78, 97, 109, 101, 32, 60, 45, 32, 67, 111, 108, 76, 97, 98, 101, 108, 10, + 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 60, 32, 91, 43, 45, 93, 63, 91, 48, 45, 57, 93, 42, 40, 91, 46, 93, 91, 48, 45, 57, 93, 42, 41, 63, 32, 62, 10, + 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 39, 92, 39, 39, 32, 91, 94, 92, 39, 93, 42, 32, 39, 92, 39, 39, 10, + 84, 121, 112, 101, 32, 60, 45, 32, 84, 121, 112, 101, 86, 97, 114, 105, 97, 116, 105, 111, 110, 115, 32, 65, 114, 114, 97, 121, 66, 111, 117, 110, 100, 115, 42, 10, + 84, 121, 112, 101, 86, 97, 114, 105, 97, 116, 105, 111, 110, 115, 32, 60, 45, 32, 84, 105, 109, 101, 84, 121, 112, 101, 32, 47, 32, 73, 110, 116, 101, 114, 118, 97, 108, 84, 121, 112, 101, 32, 47, 32, 66, 105, 116, 84, 121, 112, 101, 32, 47, 32, 82, 111, 119, 84, 121, 112, 101, 32, 47, 32, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 32, 47, 32, 77, 97, 112, 84, 121, 112, 101, 32, 47, 32, 84, 117, 112, 108, 101, 84, 121, 112, 101, 32, 47, 32, 71, 101, 111, 109, 101, 116, 114, 121, 84, 121, 112, 101, 32, 47, 32, 85, 110, 105, 111, 110, 84, 121, 112, 101, 32, 47, 32, 78, 117, 109, 101, 114, 105, 99, 84, 121, 112, 101, 32, 47, 32, 83, 101, 116, 111, 102, 84, 121, 112, 101, 32, 47, 32, 83, 105, 109, 112, 108, 101, 84, 121, 112, 101, 10, + 83, 105, 109, 112, 108, 101, 84, 121, 112, 101, 32, 60, 45, 32, 67, 104, 97, 114, 97, 99, 116, 101, 114, 83, 105, 109, 112, 108, 101, 84, 121, 112, 101, 32, 47, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 83, 105, 109, 112, 108, 101, 84, 121, 112, 101, 10, + 67, 104, 97, 114, 97, 99, 116, 101, 114, 83, 105, 109, 112, 108, 101, 84, 121, 112, 101, 32, 60, 45, 32, 67, 104, 97, 114, 97, 99, 116, 101, 114, 84, 121, 112, 101, 32, 84, 121, 112, 101, 77, 111, 100, 105, 102, 105, 101, 114, 115, 63, 10, + 81, 117, 97, 108, 105, 102, 105, 101, 100, 83, 105, 109, 112, 108, 101, 84, 121, 112, 101, 32, 60, 45, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 121, 112, 101, 78, 97, 109, 101, 32, 84, 121, 112, 101, 77, 111, 100, 105, 102, 105, 101, 114, 115, 63, 10, + 67, 104, 97, 114, 97, 99, 116, 101, 114, 84, 121, 112, 101, 32, 60, 45, 32, 40, 39, 67, 72, 65, 82, 65, 67, 84, 69, 82, 39, 32, 39, 86, 65, 82, 89, 73, 78, 71, 39, 63, 41, 32, 47, 10, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 40, 39, 67, 72, 65, 82, 39, 32, 39, 86, 65, 82, 89, 73, 78, 71, 39, 63, 41, 32, 47, 10, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 40, 39, 78, 65, 84, 73, 79, 78, 65, 76, 39, 32, 39, 67, 72, 65, 82, 65, 67, 84, 69, 82, 39, 32, 39, 86, 65, 82, 89, 73, 78, 71, 39, 63, 41, 32, 47, 10, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 40, 39, 78, 65, 84, 73, 79, 78, 65, 76, 39, 32, 39, 67, 72, 65, 82, 39, 32, 39, 86, 65, 82, 89, 73, 78, 71, 39, 63, 41, 32, 47, 10, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 40, 39, 78, 67, 72, 65, 82, 39, 32, 39, 86, 65, 82, 89, 73, 78, 71, 39, 63, 41, 32, 47, 10, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 39, 86, 65, 82, 67, 72, 65, 82, 39, 10, + 73, 110, 116, 101, 114, 118, 97, 108, 84, 121, 112, 101, 32, 60, 45, 32, 73, 110, 116, 101, 114, 118, 97, 108, 73, 110, 116, 101, 114, 118, 97, 108, 32, 47, 32, 73, 110, 116, 101, 114, 118, 97, 108, 78, 117, 109, 98, 101, 114, 10, + 73, 110, 116, 101, 114, 118, 97, 108, 73, 110, 116, 101, 114, 118, 97, 108, 32, 60, 45, 32, 73, 110, 116, 101, 114, 118, 97, 108, 87, 105, 116, 104, 83, 112, 101, 99, 105, 102, 105, 101, 114, 32, 47, 32, 73, 110, 116, 101, 114, 118, 97, 108, 87, 105, 116, 104, 111, 117, 116, 83, 112, 101, 99, 105, 102, 105, 101, 114, 10, + 73, 110, 116, 101, 114, 118, 97, 108, 87, 105, 116, 104, 83, 112, 101, 99, 105, 102, 105, 101, 114, 32, 60, 45, 32, 73, 110, 116, 101, 114, 118, 97, 108, 87, 105, 116, 104, 82, 97, 110, 103, 101, 83, 112, 101, 99, 105, 102, 105, 101, 114, 32, 47, 32, 73, 110, 116, 101, 114, 118, 97, 108, 87, 105, 116, 104, 83, 105, 109, 112, 108, 101, 83, 112, 101, 99, 105, 102, 105, 101, 114, 10, + 73, 110, 116, 101, 114, 118, 97, 108, 87, 105, 116, 104, 82, 97, 110, 103, 101, 83, 112, 101, 99, 105, 102, 105, 101, 114, 32, 60, 45, 32, 39, 73, 78, 84, 69, 82, 86, 65, 76, 39, 32, 73, 110, 116, 101, 114, 118, 97, 108, 84, 111, 73, 110, 116, 101, 114, 118, 97, 108, 65, 115, 84, 121, 112, 101, 10, + 73, 110, 116, 101, 114, 118, 97, 108, 87, 105, 116, 104, 83, 105, 109, 112, 108, 101, 83, 112, 101, 99, 105, 102, 105, 101, 114, 32, 60, 45, 32, 39, 73, 78, 84, 69, 82, 86, 65, 76, 39, 32, 73, 110, 116, 101, 114, 118, 97, 108, 10, + 73, 110, 116, 101, 114, 118, 97, 108, 87, 105, 116, 104, 111, 117, 116, 83, 112, 101, 99, 105, 102, 105, 101, 114, 32, 60, 45, 32, 39, 73, 78, 84, 69, 82, 86, 65, 76, 39, 10, + 73, 110, 116, 101, 114, 118, 97, 108, 84, 111, 73, 110, 116, 101, 114, 118, 97, 108, 65, 115, 84, 121, 112, 101, 32, 60, 45, 32, 40, 89, 101, 97, 114, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 77, 111, 110, 116, 104, 75, 101, 121, 119, 111, 114, 100, 41, 32, 47, 10, + 32, 32, 32, 32, 40, 68, 97, 121, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 72, 111, 117, 114, 75, 101, 121, 119, 111, 114, 100, 41, 32, 47, 10, + 32, 32, 32, 32, 40, 68, 97, 121, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 77, 105, 110, 117, 116, 101, 75, 101, 121, 119, 111, 114, 100, 41, 32, 47, 10, + 32, 32, 32, 32, 40, 68, 97, 121, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 83, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 41, 32, 47, 10, + 32, 32, 32, 32, 40, 72, 111, 117, 114, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 77, 105, 110, 117, 116, 101, 75, 101, 121, 119, 111, 114, 100, 41, 32, 47, 10, + 32, 32, 32, 32, 40, 72, 111, 117, 114, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 83, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 41, 32, 47, 10, + 32, 32, 32, 32, 40, 77, 105, 110, 117, 116, 101, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 83, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 41, 10, + 73, 110, 116, 101, 114, 118, 97, 108, 78, 117, 109, 98, 101, 114, 32, 60, 45, 32, 39, 73, 78, 84, 69, 82, 86, 65, 76, 39, 32, 80, 97, 114, 101, 110, 115, 40, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 41, 10, + 89, 101, 97, 114, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 45, 32, 39, 89, 69, 65, 82, 39, 32, 47, 32, 39, 89, 69, 65, 82, 83, 39, 10, + 77, 111, 110, 116, 104, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, 32, 32, 60, 45, 32, 39, 77, 79, 78, 84, 72, 39, 32, 47, 32, 39, 77, 79, 78, 84, 72, 83, 39, 10, + 68, 97, 121, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 45, 32, 39, 68, 65, 89, 39, 32, 47, 32, 39, 68, 65, 89, 83, 39, 10, + 72, 111, 117, 114, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 45, 32, 39, 72, 79, 85, 82, 39, 32, 47, 32, 39, 72, 79, 85, 82, 83, 39, 10, + 77, 105, 110, 117, 116, 101, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, 32, 60, 45, 32, 39, 77, 73, 78, 85, 84, 69, 39, 32, 47, 32, 39, 77, 73, 78, 85, 84, 69, 83, 39, 10, + 83, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, 32, 60, 45, 32, 39, 83, 69, 67, 79, 78, 68, 39, 32, 47, 32, 39, 83, 69, 67, 79, 78, 68, 83, 39, 10, + 77, 105, 108, 108, 105, 115, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 32, 32, 60, 45, 32, 39, 77, 73, 76, 76, 73, 83, 69, 67, 79, 78, 68, 39, 32, 47, 32, 39, 77, 73, 76, 76, 73, 83, 69, 67, 79, 78, 68, 83, 39, 10, + 77, 105, 99, 114, 111, 115, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 32, 32, 60, 45, 32, 39, 77, 73, 67, 82, 79, 83, 69, 67, 79, 78, 68, 39, 32, 47, 32, 39, 77, 73, 67, 82, 79, 83, 69, 67, 79, 78, 68, 83, 39, 10, + 87, 101, 101, 107, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 45, 32, 39, 87, 69, 69, 75, 39, 32, 47, 32, 39, 87, 69, 69, 75, 83, 39, 10, + 81, 117, 97, 114, 116, 101, 114, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, 60, 45, 32, 39, 81, 85, 65, 82, 84, 69, 82, 39, 32, 47, 32, 39, 81, 85, 65, 82, 84, 69, 82, 83, 39, 10, + 68, 101, 99, 97, 100, 101, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, 32, 60, 45, 32, 39, 68, 69, 67, 65, 68, 69, 39, 32, 47, 32, 39, 68, 69, 67, 65, 68, 69, 83, 39, 10, + 67, 101, 110, 116, 117, 114, 121, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 32, 32, 32, 60, 45, 32, 39, 67, 69, 78, 84, 85, 82, 89, 39, 32, 47, 32, 39, 67, 69, 78, 84, 85, 82, 73, 69, 83, 39, 10, + 77, 105, 108, 108, 101, 110, 110, 105, 117, 109, 75, 101, 121, 119, 111, 114, 100, 32, 32, 32, 60, 45, 32, 39, 77, 73, 76, 76, 69, 78, 78, 73, 85, 77, 39, 32, 47, 32, 39, 77, 73, 76, 76, 69, 78, 78, 73, 65, 39, 10, + 73, 110, 116, 101, 114, 118, 97, 108, 32, 60, 45, 32, 73, 110, 116, 101, 114, 118, 97, 108, 84, 111, 73, 110, 116, 101, 114, 118, 97, 108, 32, 47, 10, + 32, 32, 32, 32, 89, 101, 97, 114, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, + 32, 32, 32, 32, 77, 111, 110, 116, 104, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, + 32, 32, 32, 32, 68, 97, 121, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, + 32, 32, 32, 32, 72, 111, 117, 114, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, + 32, 32, 32, 32, 77, 105, 110, 117, 116, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, + 32, 32, 32, 32, 83, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, + 32, 32, 32, 32, 77, 105, 108, 108, 105, 115, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, + 32, 32, 32, 32, 77, 105, 99, 114, 111, 115, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, + 32, 32, 32, 32, 87, 101, 101, 107, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, + 32, 32, 32, 32, 81, 117, 97, 114, 116, 101, 114, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, + 32, 32, 32, 32, 68, 101, 99, 97, 100, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, + 32, 32, 32, 32, 67, 101, 110, 116, 117, 114, 121, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, + 32, 32, 32, 32, 77, 105, 108, 108, 101, 110, 110, 105, 117, 109, 75, 101, 121, 119, 111, 114, 100, 10, + 73, 110, 116, 101, 114, 118, 97, 108, 84, 111, 73, 110, 116, 101, 114, 118, 97, 108, 32, 60, 45, 32, 89, 101, 97, 114, 84, 111, 77, 111, 110, 116, 104, 32, 47, 10, + 32, 32, 32, 32, 68, 97, 121, 84, 111, 72, 111, 117, 114, 32, 47, 10, + 32, 32, 32, 32, 68, 97, 121, 84, 111, 77, 105, 110, 117, 116, 101, 32, 47, 10, + 32, 32, 32, 32, 68, 97, 121, 84, 111, 83, 101, 99, 111, 110, 100, 32, 47, 10, + 32, 32, 32, 32, 72, 111, 117, 114, 84, 111, 77, 105, 110, 117, 116, 101, 32, 47, 10, + 32, 32, 32, 32, 72, 111, 117, 114, 84, 111, 83, 101, 99, 111, 110, 100, 32, 47, 10, + 32, 32, 32, 32, 77, 105, 110, 117, 116, 101, 84, 111, 83, 101, 99, 111, 110, 100, 10, + 89, 101, 97, 114, 84, 111, 77, 111, 110, 116, 104, 32, 60, 45, 32, 89, 101, 97, 114, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 77, 111, 110, 116, 104, 75, 101, 121, 119, 111, 114, 100, 10, + 68, 97, 121, 84, 111, 72, 111, 117, 114, 32, 60, 45, 32, 68, 97, 121, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 72, 111, 117, 114, 75, 101, 121, 119, 111, 114, 100, 10, + 68, 97, 121, 84, 111, 77, 105, 110, 117, 116, 101, 32, 60, 45, 32, 68, 97, 121, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 77, 105, 110, 117, 116, 101, 75, 101, 121, 119, 111, 114, 100, 10, + 68, 97, 121, 84, 111, 83, 101, 99, 111, 110, 100, 32, 60, 45, 32, 68, 97, 121, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 83, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 10, + 72, 111, 117, 114, 84, 111, 77, 105, 110, 117, 116, 101, 32, 60, 45, 32, 72, 111, 117, 114, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 77, 105, 110, 117, 116, 101, 75, 101, 121, 119, 111, 114, 100, 10, + 72, 111, 117, 114, 84, 111, 83, 101, 99, 111, 110, 100, 32, 60, 45, 32, 72, 111, 117, 114, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 83, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 10, + 77, 105, 110, 117, 116, 101, 84, 111, 83, 101, 99, 111, 110, 100, 32, 60, 45, 32, 77, 105, 110, 117, 116, 101, 75, 101, 121, 119, 111, 114, 100, 32, 39, 84, 79, 39, 32, 83, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 10, + 66, 105, 116, 84, 121, 112, 101, 32, 60, 45, 32, 39, 66, 73, 84, 39, 32, 39, 86, 65, 82, 89, 73, 78, 71, 39, 63, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 41, 63, 10, + 71, 101, 111, 109, 101, 116, 114, 121, 84, 121, 112, 101, 32, 60, 45, 32, 39, 71, 69, 79, 77, 69, 84, 82, 89, 39, 32, 80, 97, 114, 101, 110, 115, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 63, 10, + 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 32, 60, 45, 32, 39, 86, 65, 82, 73, 65, 78, 84, 39, 10, + 78, 117, 109, 101, 114, 105, 99, 84, 121, 112, 101, 32, 60, 45, 32, 83, 105, 109, 112, 108, 101, 78, 117, 109, 101, 114, 105, 99, 84, 121, 112, 101, 32, 47, 32, 68, 101, 99, 105, 109, 97, 108, 78, 117, 109, 101, 114, 105, 99, 84, 121, 112, 101, 10, + 83, 105, 109, 112, 108, 101, 78, 117, 109, 101, 114, 105, 99, 84, 121, 112, 101, 32, 60, 45, 32, 73, 110, 116, 84, 121, 112, 101, 32, 47, 32, 73, 110, 116, 101, 103, 101, 114, 84, 121, 112, 101, 32, 47, 32, 83, 109, 97, 108, 108, 105, 110, 116, 84, 121, 112, 101, 32, 47, 10, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 66, 105, 103, 105, 110, 116, 84, 121, 112, 101, 32, 47, 32, 82, 101, 97, 108, 84, 121, 112, 101, 32, 47, 32, 66, 111, 111, 108, 101, 97, 110, 84, 121, 112, 101, 32, 47, 32, 68, 111, 117, 98, 108, 101, 84, 121, 112, 101, 10, + 68, 101, 99, 105, 109, 97, 108, 78, 117, 109, 101, 114, 105, 99, 84, 121, 112, 101, 32, 60, 45, 32, 70, 108, 111, 97, 116, 84, 121, 112, 101, 32, 47, 32, 68, 101, 99, 105, 109, 97, 108, 84, 121, 112, 101, 32, 47, 32, 68, 101, 99, 84, 121, 112, 101, 32, 47, 32, 78, 117, 109, 101, 114, 105, 99, 77, 111, 100, 84, 121, 112, 101, 10, + 73, 110, 116, 84, 121, 112, 101, 32, 60, 45, 32, 39, 73, 78, 84, 39, 10, + 73, 110, 116, 101, 103, 101, 114, 84, 121, 112, 101, 32, 60, 45, 32, 39, 73, 78, 84, 69, 71, 69, 82, 39, 10, + 83, 109, 97, 108, 108, 105, 110, 116, 84, 121, 112, 101, 32, 60, 45, 32, 39, 83, 77, 65, 76, 76, 73, 78, 84, 39, 10, + 66, 105, 103, 105, 110, 116, 84, 121, 112, 101, 32, 60, 45, 32, 39, 66, 73, 71, 73, 78, 84, 39, 10, + 82, 101, 97, 108, 84, 121, 112, 101, 32, 60, 45, 32, 39, 82, 69, 65, 76, 39, 10, + 66, 111, 111, 108, 101, 97, 110, 84, 121, 112, 101, 32, 60, 45, 32, 39, 66, 79, 79, 76, 69, 65, 78, 39, 10, + 68, 111, 117, 98, 108, 101, 84, 121, 112, 101, 32, 60, 45, 32, 40, 39, 68, 79, 85, 66, 76, 69, 39, 32, 39, 80, 82, 69, 67, 73, 83, 73, 79, 78, 39, 41, 10, + 70, 108, 111, 97, 116, 84, 121, 112, 101, 32, 60, 45, 32, 39, 70, 76, 79, 65, 84, 39, 32, 80, 97, 114, 101, 110, 115, 40, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 41, 63, 10, + 68, 101, 99, 105, 109, 97, 108, 84, 121, 112, 101, 32, 60, 45, 32, 39, 68, 69, 67, 73, 77, 65, 76, 39, 32, 84, 121, 112, 101, 77, 111, 100, 105, 102, 105, 101, 114, 115, 63, 10, + 68, 101, 99, 84, 121, 112, 101, 32, 60, 45, 32, 39, 68, 69, 67, 39, 32, 84, 121, 112, 101, 77, 111, 100, 105, 102, 105, 101, 114, 115, 63, 10, + 78, 117, 109, 101, 114, 105, 99, 77, 111, 100, 84, 121, 112, 101, 32, 60, 45, 32, 39, 78, 85, 77, 69, 82, 73, 67, 39, 32, 84, 121, 112, 101, 77, 111, 100, 105, 102, 105, 101, 114, 115, 63, 10, + 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 121, 112, 101, 78, 97, 109, 101, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 84, 121, 112, 101, 78, 97, 109, 101, 32, 47, 32, 83, 99, 104, 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 84, 121, 112, 101, 78, 97, 109, 101, 32, 47, 32, 84, 121, 112, 101, 78, 97, 109, 101, 65, 115, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 10, + 84, 121, 112, 101, 78, 97, 109, 101, 65, 115, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 32, 60, 45, 32, 84, 121, 112, 101, 78, 97, 109, 101, 10, + 67, 97, 116, 97, 108, 111, 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 84, 121, 112, 101, 78, 97, 109, 101, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 43, 32, 82, 101, 115, 101, 114, 118, 101, 100, 84, 121, 112, 101, 78, 97, 109, 101, 10, + 83, 99, 104, 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 84, 121, 112, 101, 78, 97, 109, 101, 32, 60, 45, 32, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 84, 121, 112, 101, 78, 97, 109, 101, 10, + 84, 121, 112, 101, 77, 111, 100, 105, 102, 105, 101, 114, 115, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 63, 41, 10, + 78, 101, 115, 116, 101, 100, 84, 121, 112, 101, 32, 60, 45, 32, 84, 121, 112, 101, 86, 97, 114, 105, 97, 116, 105, 111, 110, 115, 32, 65, 114, 114, 97, 121, 66, 111, 117, 110, 100, 115, 42, 10, + 82, 111, 119, 84, 121, 112, 101, 32, 60, 45, 32, 82, 111, 119, 79, 114, 83, 116, 114, 117, 99, 116, 32, 67, 111, 108, 73, 100, 84, 121, 112, 101, 76, 105, 115, 116, 63, 10, + 83, 101, 116, 111, 102, 84, 121, 112, 101, 32, 60, 45, 32, 39, 83, 69, 84, 79, 70, 39, 32, 78, 101, 115, 116, 101, 100, 84, 121, 112, 101, 10, + 85, 110, 105, 111, 110, 84, 121, 112, 101, 32, 60, 45, 32, 39, 85, 78, 73, 79, 78, 39, 32, 67, 111, 108, 73, 100, 84, 121, 112, 101, 76, 105, 115, 116, 10, + 67, 111, 108, 73, 100, 84, 121, 112, 101, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 67, 111, 108, 73, 100, 84, 121, 112, 101, 41, 41, 10, + 77, 97, 112, 84, 121, 112, 101, 32, 60, 45, 32, 39, 77, 65, 80, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 78, 101, 115, 116, 101, 100, 84, 121, 112, 101, 41, 41, 63, 10, + 84, 117, 112, 108, 101, 84, 121, 112, 101, 32, 60, 45, 32, 39, 84, 85, 80, 76, 69, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 78, 101, 115, 116, 101, 100, 84, 121, 112, 101, 41, 41, 10, + 67, 111, 108, 73, 100, 84, 121, 112, 101, 32, 60, 45, 32, 67, 111, 108, 73, 100, 32, 78, 101, 115, 116, 101, 100, 84, 121, 112, 101, 10, + 65, 114, 114, 97, 121, 66, 111, 117, 110, 100, 115, 32, 60, 45, 32, 65, 114, 114, 97, 121, 75, 101, 121, 119, 111, 114, 100, 87, 105, 116, 104, 66, 111, 117, 110, 100, 115, 32, 47, 32, 83, 113, 117, 97, 114, 101, 66, 114, 97, 99, 107, 101, 116, 115, 65, 114, 114, 97, 121, 32, 47, 32, 65, 114, 114, 97, 121, 75, 101, 121, 119, 111, 114, 100, 10, + 65, 114, 114, 97, 121, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 65, 82, 82, 65, 89, 39, 10, + 65, 114, 114, 97, 121, 75, 101, 121, 119, 111, 114, 100, 87, 105, 116, 104, 66, 111, 117, 110, 100, 115, 32, 60, 45, 32, 39, 65, 82, 82, 65, 89, 39, 32, 83, 113, 117, 97, 114, 101, 66, 114, 97, 99, 107, 101, 116, 115, 65, 114, 114, 97, 121, 10, + 83, 113, 117, 97, 114, 101, 66, 114, 97, 99, 107, 101, 116, 115, 65, 114, 114, 97, 121, 32, 60, 45, 32, 39, 91, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 32, 39, 93, 39, 10, + 84, 105, 109, 101, 84, 121, 112, 101, 32, 60, 45, 32, 84, 105, 109, 101, 79, 114, 84, 105, 109, 101, 115, 116, 97, 109, 112, 32, 84, 121, 112, 101, 77, 111, 100, 105, 102, 105, 101, 114, 115, 63, 32, 84, 105, 109, 101, 90, 111, 110, 101, 63, 10, + 84, 105, 109, 101, 79, 114, 84, 105, 109, 101, 115, 116, 97, 109, 112, 32, 60, 45, 32, 84, 105, 109, 101, 84, 121, 112, 101, 73, 100, 32, 47, 32, 84, 105, 109, 101, 115, 116, 97, 109, 112, 84, 121, 112, 101, 73, 100, 10, + 84, 105, 109, 101, 84, 121, 112, 101, 73, 100, 32, 60, 45, 32, 39, 84, 73, 77, 69, 39, 10, + 84, 105, 109, 101, 115, 116, 97, 109, 112, 84, 121, 112, 101, 73, 100, 32, 60, 45, 32, 39, 84, 73, 77, 69, 83, 84, 65, 77, 80, 39, 10, + 84, 105, 109, 101, 90, 111, 110, 101, 32, 60, 45, 32, 87, 105, 116, 104, 79, 114, 87, 105, 116, 104, 111, 117, 116, 32, 39, 84, 73, 77, 69, 39, 32, 39, 90, 79, 78, 69, 39, 10, + 87, 105, 116, 104, 79, 114, 87, 105, 116, 104, 111, 117, 116, 32, 60, 45, 32, 87, 105, 116, 104, 82, 117, 108, 101, 32, 47, 32, 87, 105, 116, 104, 111, 117, 116, 82, 117, 108, 101, 10, + 87, 105, 116, 104, 82, 117, 108, 101, 32, 60, 45, 32, 39, 87, 73, 84, 72, 39, 10, + 87, 105, 116, 104, 111, 117, 116, 82, 117, 108, 101, 32, 60, 45, 32, 39, 87, 73, 84, 72, 79, 85, 84, 39, 10, + 82, 111, 119, 79, 114, 83, 116, 114, 117, 99, 116, 32, 60, 45, 32, 39, 82, 79, 87, 39, 32, 47, 32, 39, 83, 84, 82, 85, 67, 84, 39, 10, + 35, 32, 105, 110, 116, 101, 114, 110, 97, 108, 32, 100, 101, 102, 105, 110, 105, 116, 105, 111, 110, 115, 10, + 37, 119, 104, 105, 116, 101, 115, 112, 97, 99, 101, 32, 60, 45, 32, 91, 32, 92, 116, 92, 110, 92, 114, 93, 42, 10, + 76, 105, 115, 116, 40, 68, 41, 32, 60, 45, 32, 68, 32, 40, 39, 44, 39, 32, 68, 41, 42, 32, 39, 44, 39, 63, 10, + 80, 97, 114, 101, 110, 115, 40, 68, 41, 32, 60, 45, 32, 39, 40, 39, 32, 68, 32, 39, 41, 39, 10, + 66, 97, 114, 101, 76, 97, 98, 101, 108, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 97, 98, 111, 114, 116, 39, 32, 47, 10, + 39, 97, 98, 115, 111, 108, 117, 116, 101, 39, 32, 47, 10, + 39, 97, 99, 99, 101, 115, 115, 39, 32, 47, 10, + 39, 97, 99, 116, 105, 111, 110, 39, 32, 47, 10, + 39, 97, 100, 100, 39, 32, 47, 10, + 39, 97, 100, 109, 105, 110, 39, 32, 47, 10, + 39, 97, 102, 116, 101, 114, 39, 32, 47, 10, + 39, 97, 103, 103, 114, 101, 103, 97, 116, 101, 39, 32, 47, 10, + 39, 97, 108, 115, 111, 39, 32, 47, 10, + 39, 97, 108, 116, 101, 114, 39, 32, 47, 10, + 39, 97, 108, 119, 97, 121, 115, 39, 32, 47, 10, + 39, 97, 112, 112, 114, 111, 120, 39, 32, 47, 10, + 39, 97, 115, 115, 101, 114, 116, 105, 111, 110, 39, 32, 47, 10, + 39, 97, 115, 115, 105, 103, 110, 109, 101, 110, 116, 39, 32, 47, 10, + 39, 97, 116, 116, 97, 99, 104, 39, 32, 47, 10, + 39, 97, 116, 116, 114, 105, 98, 117, 116, 101, 39, 32, 47, 10, + 39, 98, 97, 99, 107, 119, 97, 114, 100, 39, 32, 47, 10, + 39, 98, 101, 102, 111, 114, 101, 39, 32, 47, 10, + 39, 98, 101, 103, 105, 110, 39, 32, 47, 10, + 39, 98, 101, 116, 119, 101, 101, 110, 39, 32, 47, 10, + 39, 98, 105, 103, 105, 110, 116, 39, 32, 47, 10, + 39, 98, 105, 116, 39, 32, 47, 10, + 39, 98, 111, 111, 108, 101, 97, 110, 39, 32, 47, 10, + 39, 99, 97, 99, 104, 101, 39, 32, 47, 10, + 39, 99, 97, 108, 108, 39, 32, 47, 10, + 39, 99, 97, 108, 108, 101, 100, 39, 32, 47, 10, + 39, 99, 97, 115, 99, 97, 100, 101, 39, 32, 47, 10, + 39, 99, 97, 115, 99, 97, 100, 101, 100, 39, 32, 47, 10, + 39, 99, 97, 116, 97, 108, 111, 103, 39, 32, 47, 10, + 39, 99, 104, 97, 105, 110, 39, 32, 47, 10, + 39, 99, 104, 97, 114, 39, 32, 47, 10, + 39, 99, 104, 97, 114, 97, 99, 116, 101, 114, 39, 32, 47, 10, + 39, 99, 104, 97, 114, 97, 99, 116, 101, 114, 105, 115, 116, 105, 99, 115, 39, 32, 47, 10, + 39, 99, 104, 101, 99, 107, 112, 111, 105, 110, 116, 39, 32, 47, 10, + 39, 99, 108, 97, 115, 115, 39, 32, 47, 10, + 39, 99, 108, 111, 115, 101, 39, 32, 47, 10, + 39, 99, 108, 117, 115, 116, 101, 114, 39, 32, 47, 10, + 39, 99, 111, 97, 108, 101, 115, 99, 101, 39, 32, 47, 10, + 39, 99, 111, 108, 117, 109, 110, 115, 39, 32, 47, 10, + 39, 99, 111, 109, 109, 101, 110, 116, 39, 32, 47, 10, + 39, 99, 111, 109, 109, 101, 110, 116, 115, 39, 32, 47, 10, + 39, 99, 111, 109, 109, 105, 116, 39, 32, 47, 10, + 39, 99, 111, 109, 109, 105, 116, 116, 101, 100, 39, 32, 47, 10, + 39, 99, 111, 109, 112, 114, 101, 115, 115, 105, 111, 110, 39, 32, 47, 10, + 39, 99, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 39, 32, 47, 10, + 39, 99, 111, 110, 102, 108, 105, 99, 116, 39, 32, 47, 10, + 39, 99, 111, 110, 110, 101, 99, 116, 39, 32, 47, 10, + 39, 99, 111, 110, 110, 101, 99, 116, 105, 111, 110, 39, 32, 47, 10, + 39, 99, 111, 110, 115, 116, 114, 97, 105, 110, 116, 115, 39, 32, 47, 10, + 39, 99, 111, 110, 116, 101, 110, 116, 39, 32, 47, 10, + 39, 99, 111, 110, 116, 105, 110, 117, 101, 39, 32, 47, 10, + 39, 99, 111, 110, 118, 101, 114, 115, 105, 111, 110, 39, 32, 47, 10, + 39, 99, 111, 112, 121, 39, 32, 47, 10, + 39, 99, 111, 115, 116, 39, 32, 47, 10, + 39, 99, 115, 118, 39, 32, 47, 10, + 39, 99, 117, 98, 101, 39, 32, 47, 10, + 39, 99, 117, 114, 114, 101, 110, 116, 39, 32, 47, 10, + 39, 99, 117, 114, 115, 111, 114, 39, 32, 47, 10, + 39, 99, 121, 99, 108, 101, 39, 32, 47, 10, + 39, 100, 97, 116, 97, 39, 32, 47, 10, + 39, 100, 97, 116, 97, 98, 97, 115, 101, 39, 32, 47, 10, + 39, 100, 101, 97, 108, 108, 111, 99, 97, 116, 101, 39, 32, 47, 10, + 39, 100, 101, 99, 39, 32, 47, 10, + 39, 100, 101, 99, 105, 109, 97, 108, 39, 32, 47, 10, + 39, 100, 101, 99, 108, 97, 114, 101, 39, 32, 47, 10, + 39, 100, 101, 102, 97, 117, 108, 116, 115, 39, 32, 47, 10, + 39, 100, 101, 102, 101, 114, 114, 101, 100, 39, 32, 47, 10, + 39, 100, 101, 102, 105, 110, 101, 114, 39, 32, 47, 10, + 39, 100, 101, 108, 101, 116, 101, 39, 32, 47, 10, + 39, 100, 101, 108, 105, 109, 105, 116, 101, 114, 39, 32, 47, 10, + 39, 100, 101, 108, 105, 109, 105, 116, 101, 114, 115, 39, 32, 47, 10, + 39, 100, 101, 112, 101, 110, 100, 115, 39, 32, 47, 10, + 39, 100, 101, 115, 116, 114, 111, 121, 39, 32, 47, 10, + 39, 100, 101, 116, 97, 99, 104, 39, 32, 47, 10, + 39, 100, 105, 99, 116, 105, 111, 110, 97, 114, 121, 39, 32, 47, 10, + 39, 100, 105, 115, 97, 98, 108, 101, 39, 32, 47, 10, + 39, 100, 105, 115, 99, 97, 114, 100, 39, 32, 47, 10, + 39, 100, 105, 115, 99, 111, 110, 110, 101, 99, 116, 39, 32, 47, 10, + 39, 100, 105, 115, 116, 97, 110, 99, 101, 39, 32, 47, 10, + 39, 100, 111, 99, 117, 109, 101, 110, 116, 39, 32, 47, 10, + 39, 100, 111, 109, 97, 105, 110, 39, 32, 47, 10, + 39, 100, 111, 117, 98, 108, 101, 39, 32, 47, 10, + 39, 100, 114, 111, 112, 39, 32, 47, 10, + 39, 101, 97, 99, 104, 39, 32, 47, 10, + 39, 101, 110, 97, 98, 108, 101, 39, 32, 47, 10, + 39, 101, 110, 99, 111, 100, 105, 110, 103, 39, 32, 47, 10, + 39, 101, 110, 99, 114, 121, 112, 116, 101, 100, 39, 32, 47, 10, + 39, 101, 110, 117, 109, 39, 32, 47, 10, + 39, 101, 114, 114, 111, 114, 39, 32, 47, 10, + 39, 101, 118, 101, 110, 116, 39, 32, 47, 10, + 39, 101, 120, 97, 99, 116, 39, 32, 47, 10, + 39, 101, 120, 99, 108, 117, 100, 105, 110, 103, 39, 32, 47, 10, + 39, 101, 120, 99, 108, 117, 115, 105, 118, 101, 39, 32, 47, 10, + 39, 101, 120, 101, 99, 117, 116, 101, 39, 32, 47, 10, + 39, 101, 120, 105, 115, 116, 115, 39, 32, 47, 10, + 39, 101, 120, 112, 108, 97, 105, 110, 39, 32, 47, 10, + 39, 101, 120, 112, 111, 114, 116, 39, 32, 47, 10, + 39, 101, 120, 116, 101, 110, 115, 105, 111, 110, 39, 32, 47, 10, + 39, 101, 120, 116, 101, 110, 115, 105, 111, 110, 115, 39, 32, 47, 10, + 39, 101, 120, 116, 101, 114, 110, 97, 108, 39, 32, 47, 10, + 39, 101, 120, 116, 114, 97, 99, 116, 39, 32, 47, 10, + 39, 102, 97, 109, 105, 108, 121, 39, 32, 47, 10, + 39, 102, 105, 114, 115, 116, 39, 32, 47, 10, + 39, 102, 108, 111, 97, 116, 39, 32, 47, 10, + 39, 102, 111, 108, 108, 111, 119, 105, 110, 103, 39, 32, 47, 10, + 39, 102, 111, 114, 99, 101, 39, 32, 47, 10, + 39, 102, 111, 114, 119, 97, 114, 100, 39, 32, 47, 10, + 39, 102, 117, 110, 99, 116, 105, 111, 110, 39, 32, 47, 10, + 39, 102, 117, 110, 99, 116, 105, 111, 110, 115, 39, 32, 47, 10, + 39, 103, 101, 110, 101, 114, 97, 116, 101, 100, 39, 32, 47, 10, + 39, 103, 108, 111, 98, 97, 108, 39, 32, 47, 10, + 39, 103, 114, 97, 110, 116, 39, 32, 47, 10, + 39, 103, 114, 97, 110, 116, 101, 100, 39, 32, 47, 10, + 39, 103, 114, 111, 117, 112, 105, 110, 103, 39, 32, 47, 10, + 39, 103, 114, 111, 117, 112, 105, 110, 103, 95, 105, 100, 39, 32, 47, 10, + 39, 103, 114, 111, 117, 112, 115, 39, 32, 47, 10, + 39, 104, 97, 110, 100, 108, 101, 114, 39, 32, 47, 10, + 39, 104, 101, 97, 100, 101, 114, 39, 32, 47, 10, + 39, 104, 111, 108, 100, 39, 32, 47, 10, + 39, 105, 100, 101, 110, 116, 105, 116, 121, 39, 32, 47, 10, + 39, 105, 102, 39, 32, 47, 10, + 39, 105, 109, 109, 101, 100, 105, 97, 116, 101, 39, 32, 47, 10, + 39, 105, 109, 109, 117, 116, 97, 98, 108, 101, 39, 32, 47, 10, + 39, 105, 109, 112, 108, 105, 99, 105, 116, 39, 32, 47, 10, + 39, 105, 109, 112, 111, 114, 116, 39, 32, 47, 10, + 39, 105, 110, 99, 108, 117, 100, 101, 39, 32, 47, 10, + 39, 105, 110, 99, 108, 117, 100, 105, 110, 103, 39, 32, 47, 10, + 39, 105, 110, 99, 114, 101, 109, 101, 110, 116, 39, 32, 47, 10, + 39, 105, 110, 100, 101, 120, 39, 32, 47, 10, + 39, 105, 110, 100, 101, 120, 101, 115, 39, 32, 47, 10, + 39, 105, 110, 104, 101, 114, 105, 116, 39, 32, 47, 10, + 39, 105, 110, 104, 101, 114, 105, 116, 115, 39, 32, 47, 10, + 39, 105, 110, 108, 105, 110, 101, 39, 32, 47, 10, + 39, 105, 110, 111, 117, 116, 39, 32, 47, 10, + 39, 105, 110, 112, 117, 116, 39, 32, 47, 10, + 39, 105, 110, 115, 101, 110, 115, 105, 116, 105, 118, 101, 39, 32, 47, 10, + 39, 105, 110, 115, 101, 114, 116, 39, 32, 47, 10, + 39, 105, 110, 115, 116, 97, 108, 108, 39, 32, 47, 10, + 39, 105, 110, 115, 116, 101, 97, 100, 39, 32, 47, 10, + 39, 105, 110, 116, 39, 32, 47, 10, + 39, 105, 110, 116, 101, 103, 101, 114, 39, 32, 47, 10, + 39, 105, 110, 116, 101, 114, 118, 97, 108, 39, 32, 47, 10, + 39, 105, 110, 118, 111, 107, 101, 114, 39, 32, 47, 10, + 39, 105, 115, 111, 108, 97, 116, 105, 111, 110, 39, 32, 47, 10, + 39, 106, 115, 111, 110, 39, 32, 47, 10, + 39, 107, 101, 121, 39, 32, 47, 10, + 39, 108, 97, 98, 101, 108, 39, 32, 47, 10, + 39, 108, 97, 110, 103, 117, 97, 103, 101, 39, 32, 47, 10, + 39, 108, 97, 114, 103, 101, 39, 32, 47, 10, + 39, 108, 97, 115, 116, 39, 32, 47, 10, + 39, 108, 101, 97, 107, 112, 114, 111, 111, 102, 39, 32, 47, 10, + 39, 108, 101, 118, 101, 108, 39, 32, 47, 10, + 39, 108, 105, 115, 116, 101, 110, 39, 32, 47, 10, + 39, 108, 111, 97, 100, 39, 32, 47, 10, + 39, 108, 111, 99, 97, 108, 39, 32, 47, 10, + 39, 108, 111, 99, 97, 116, 105, 111, 110, 39, 32, 47, 10, + 39, 108, 111, 99, 107, 39, 32, 47, 10, + 39, 108, 111, 99, 107, 101, 100, 39, 32, 47, 10, + 39, 108, 111, 103, 103, 101, 100, 39, 32, 47, 10, + 39, 109, 97, 99, 114, 111, 39, 32, 47, 10, + 39, 109, 97, 112, 39, 32, 47, 10, + 39, 109, 97, 112, 112, 105, 110, 103, 39, 32, 47, 10, + 39, 109, 97, 116, 99, 104, 39, 32, 47, 10, + 39, 109, 97, 116, 99, 104, 101, 100, 39, 32, 47, 10, + 39, 109, 97, 116, 101, 114, 105, 97, 108, 105, 122, 101, 100, 39, 32, 47, 10, + 39, 109, 97, 120, 118, 97, 108, 117, 101, 39, 32, 47, 10, + 39, 109, 101, 114, 103, 101, 39, 32, 47, 10, + 39, 109, 101, 116, 104, 111, 100, 39, 32, 47, 10, + 39, 109, 105, 110, 118, 97, 108, 117, 101, 39, 32, 47, 10, + 39, 109, 111, 100, 101, 39, 32, 47, 10, + 39, 109, 111, 118, 101, 39, 32, 47, 10, + 39, 110, 97, 109, 101, 39, 32, 47, 10, + 39, 110, 97, 109, 101, 115, 39, 32, 47, 10, + 39, 110, 97, 116, 105, 111, 110, 97, 108, 39, 32, 47, 10, + 39, 110, 99, 104, 97, 114, 39, 32, 47, 10, + 39, 110, 101, 97, 114, 101, 115, 116, 39, 32, 47, 10, + 39, 110, 101, 119, 39, 32, 47, 10, + 39, 110, 101, 120, 116, 39, 32, 47, 10, + 39, 110, 111, 39, 32, 47, 10, + 39, 110, 111, 110, 101, 39, 32, 47, 10, + 39, 110, 111, 116, 104, 105, 110, 103, 39, 32, 47, 10, + 39, 110, 111, 116, 105, 102, 121, 39, 32, 47, 10, + 39, 110, 111, 119, 97, 105, 116, 39, 32, 47, 10, + 39, 110, 117, 108, 108, 105, 102, 39, 32, 47, 10, + 39, 110, 117, 108, 108, 115, 39, 32, 47, 10, + 39, 110, 117, 109, 101, 114, 105, 99, 39, 32, 47, 10, + 39, 111, 98, 106, 101, 99, 116, 39, 32, 47, 10, + 39, 111, 102, 39, 32, 47, 10, + 39, 111, 102, 102, 39, 32, 47, 10, + 39, 111, 105, 100, 115, 39, 32, 47, 10, + 39, 111, 108, 100, 39, 32, 47, 10, + 39, 111, 112, 101, 114, 97, 116, 111, 114, 39, 32, 47, 10, + 39, 111, 112, 116, 105, 111, 110, 39, 32, 47, 10, + 39, 111, 112, 116, 105, 111, 110, 115, 39, 32, 47, 10, + 39, 111, 114, 100, 105, 110, 97, 108, 105, 116, 121, 39, 32, 47, 10, + 39, 111, 116, 104, 101, 114, 115, 39, 32, 47, 10, + 39, 111, 117, 116, 39, 32, 47, 10, + 39, 111, 118, 101, 114, 108, 97, 121, 39, 32, 47, 10, + 39, 111, 118, 101, 114, 114, 105, 100, 105, 110, 103, 39, 32, 47, 10, + 39, 111, 119, 110, 101, 100, 39, 32, 47, 10, + 39, 111, 119, 110, 101, 114, 39, 32, 47, 10, + 39, 112, 97, 114, 97, 108, 108, 101, 108, 39, 32, 47, 10, + 39, 112, 97, 114, 115, 101, 114, 39, 32, 47, 10, + 39, 112, 97, 114, 116, 105, 97, 108, 39, 32, 47, 10, + 39, 112, 97, 114, 116, 105, 116, 105, 111, 110, 39, 32, 47, 10, + 39, 112, 97, 114, 116, 105, 116, 105, 111, 110, 101, 100, 39, 32, 47, 10, + 39, 112, 97, 115, 115, 105, 110, 103, 39, 32, 47, 10, + 39, 112, 97, 115, 115, 119, 111, 114, 100, 39, 32, 47, 10, + 39, 112, 101, 114, 99, 101, 110, 116, 39, 32, 47, 10, + 39, 112, 101, 114, 115, 105, 115, 116, 101, 110, 116, 39, 32, 47, 10, + 39, 112, 108, 97, 110, 115, 39, 32, 47, 10, + 39, 112, 111, 108, 105, 99, 121, 39, 32, 47, 10, + 39, 112, 111, 115, 105, 116, 105, 111, 110, 39, 32, 47, 10, + 39, 112, 114, 97, 103, 109, 97, 39, 32, 47, 10, + 39, 112, 114, 101, 99, 101, 100, 105, 110, 103, 39, 32, 47, 10, + 39, 112, 114, 101, 112, 97, 114, 101, 39, 32, 47, 10, + 39, 112, 114, 101, 112, 97, 114, 101, 100, 39, 32, 47, 10, + 39, 112, 114, 101, 115, 101, 114, 118, 101, 39, 32, 47, 10, + 39, 112, 114, 105, 111, 114, 39, 32, 47, 10, + 39, 112, 114, 105, 118, 105, 108, 101, 103, 101, 115, 39, 32, 47, 10, + 39, 112, 114, 111, 99, 101, 100, 117, 114, 97, 108, 39, 32, 47, 10, + 39, 112, 114, 111, 99, 101, 100, 117, 114, 101, 39, 32, 47, 10, + 39, 112, 114, 111, 103, 114, 97, 109, 39, 32, 47, 10, + 39, 112, 117, 98, 108, 105, 99, 97, 116, 105, 111, 110, 39, 32, 47, 10, + 39, 113, 117, 111, 116, 101, 39, 32, 47, 10, + 39, 114, 97, 110, 103, 101, 39, 32, 47, 10, + 39, 114, 101, 97, 100, 39, 32, 47, 10, + 39, 114, 101, 97, 108, 39, 32, 47, 10, + 39, 114, 101, 97, 115, 115, 105, 103, 110, 39, 32, 47, 10, + 39, 114, 101, 99, 104, 101, 99, 107, 39, 32, 47, 10, + 39, 114, 101, 99, 117, 114, 115, 105, 118, 101, 39, 32, 47, 10, + 39, 114, 101, 102, 39, 32, 47, 10, + 39, 114, 101, 102, 101, 114, 101, 110, 99, 105, 110, 103, 39, 32, 47, 10, + 39, 114, 101, 102, 114, 101, 115, 104, 39, 32, 47, 10, + 39, 114, 101, 103, 105, 115, 116, 101, 114, 39, 32, 47, 10, + 39, 114, 101, 105, 110, 100, 101, 120, 39, 32, 47, 10, + 39, 114, 101, 108, 97, 116, 105, 118, 101, 39, 32, 47, 10, + 39, 114, 101, 108, 101, 97, 115, 101, 39, 32, 47, 10, + 39, 114, 101, 112, 101, 97, 116, 97, 98, 108, 101, 39, 32, 47, 10, + 39, 114, 101, 112, 108, 105, 99, 97, 39, 32, 47, 10, + 39, 114, 101, 115, 101, 116, 39, 32, 47, 10, + 39, 114, 101, 115, 111, 117, 114, 99, 101, 39, 32, 47, 10, + 39, 114, 101, 115, 111, 117, 114, 99, 101, 115, 39, 32, 47, 10, + 39, 114, 101, 115, 116, 97, 114, 116, 39, 32, 47, 10, + 39, 114, 101, 115, 116, 114, 105, 99, 116, 39, 32, 47, 10, + 39, 114, 101, 116, 117, 114, 110, 115, 39, 32, 47, 10, + 39, 114, 101, 118, 111, 107, 101, 39, 32, 47, 10, + 39, 114, 111, 108, 101, 39, 32, 47, 10, + 39, 114, 111, 108, 108, 98, 97, 99, 107, 39, 32, 47, 10, + 39, 114, 111, 108, 108, 117, 112, 39, 32, 47, 10, + 39, 114, 111, 119, 39, 32, 47, 10, + 39, 114, 111, 119, 115, 39, 32, 47, 10, + 39, 114, 117, 108, 101, 39, 32, 47, 10, + 39, 115, 97, 109, 112, 108, 101, 39, 32, 47, 10, + 39, 115, 97, 118, 101, 112, 111, 105, 110, 116, 39, 32, 47, 10, + 39, 115, 99, 104, 101, 109, 97, 39, 32, 47, 10, + 39, 115, 99, 104, 101, 109, 97, 115, 39, 32, 47, 10, + 39, 115, 99, 111, 112, 101, 39, 32, 47, 10, + 39, 115, 99, 114, 111, 108, 108, 39, 32, 47, 10, + 39, 115, 101, 97, 114, 99, 104, 39, 32, 47, 10, + 39, 115, 101, 99, 114, 101, 116, 39, 32, 47, 10, + 39, 115, 101, 99, 117, 114, 101, 39, 32, 47, 10, + 39, 115, 101, 99, 117, 114, 105, 116, 121, 39, 32, 47, 10, + 39, 115, 101, 113, 117, 101, 110, 99, 101, 39, 32, 47, 10, + 39, 115, 101, 113, 117, 101, 110, 99, 101, 115, 39, 32, 47, 10, + 39, 115, 101, 114, 105, 97, 108, 105, 122, 97, 98, 108, 101, 39, 32, 47, 10, + 39, 115, 101, 114, 118, 101, 114, 39, 32, 47, 10, + 39, 115, 101, 115, 115, 105, 111, 110, 39, 32, 47, 10, + 39, 115, 101, 116, 39, 32, 47, 10, + 39, 115, 101, 116, 111, 102, 39, 32, 47, 10, + 39, 115, 101, 116, 115, 39, 32, 47, 10, + 39, 115, 104, 97, 114, 101, 39, 32, 47, 10, + 39, 115, 105, 109, 105, 108, 97, 114, 105, 116, 121, 39, 32, 47, 10, + 39, 115, 105, 109, 112, 108, 101, 39, 32, 47, 10, + 39, 115, 107, 105, 112, 39, 32, 47, 10, + 39, 115, 109, 97, 108, 108, 105, 110, 116, 39, 32, 47, 10, + 39, 115, 110, 97, 112, 115, 104, 111, 116, 39, 32, 47, 10, + 39, 115, 111, 114, 116, 101, 100, 39, 32, 47, 10, + 39, 115, 111, 117, 114, 99, 101, 39, 32, 47, 10, + 39, 115, 113, 108, 39, 32, 47, 10, + 39, 115, 116, 97, 98, 108, 101, 39, 32, 47, 10, + 39, 115, 116, 97, 110, 100, 97, 108, 111, 110, 101, 39, 32, 47, 10, + 39, 115, 116, 97, 114, 116, 39, 32, 47, 10, + 39, 115, 116, 97, 116, 101, 109, 101, 110, 116, 39, 32, 47, 10, + 39, 115, 116, 97, 116, 105, 115, 116, 105, 99, 115, 39, 32, 47, 10, + 39, 115, 116, 100, 105, 110, 39, 32, 47, 10, + 39, 115, 116, 100, 111, 117, 116, 39, 32, 47, 10, + 39, 115, 116, 111, 114, 97, 103, 101, 39, 32, 47, 10, + 39, 115, 116, 111, 114, 101, 100, 39, 32, 47, 10, + 39, 115, 116, 114, 105, 99, 116, 39, 32, 47, 10, + 39, 115, 116, 114, 105, 112, 39, 32, 47, 10, + 39, 115, 116, 114, 117, 99, 116, 39, 32, 47, 10, + 39, 115, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 39, 32, 47, 10, + 39, 115, 117, 98, 115, 116, 114, 105, 110, 103, 39, 32, 47, 10, + 39, 115, 121, 115, 105, 100, 39, 32, 47, 10, + 39, 115, 121, 115, 116, 101, 109, 39, 32, 47, 10, + 39, 116, 97, 98, 108, 101, 115, 39, 32, 47, 10, + 39, 116, 97, 98, 108, 101, 115, 112, 97, 99, 101, 39, 32, 47, 10, + 39, 116, 97, 114, 103, 101, 116, 39, 32, 47, 10, + 39, 116, 101, 109, 112, 39, 32, 47, 10, + 39, 116, 101, 109, 112, 108, 97, 116, 101, 39, 32, 47, 10, + 39, 116, 101, 109, 112, 111, 114, 97, 114, 121, 39, 32, 47, 10, + 39, 116, 101, 120, 116, 39, 32, 47, 10, + 39, 116, 105, 101, 115, 39, 32, 47, 10, + 39, 116, 105, 109, 101, 39, 32, 47, 10, + 39, 116, 105, 109, 101, 115, 116, 97, 109, 112, 39, 32, 47, 10, + 39, 116, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 39, 32, 47, 10, + 39, 116, 114, 97, 110, 115, 102, 111, 114, 109, 39, 32, 47, 10, + 39, 116, 114, 101, 97, 116, 39, 32, 47, 10, + 39, 116, 114, 105, 103, 103, 101, 114, 39, 32, 47, 10, + 39, 116, 114, 105, 109, 39, 32, 47, 10, + 39, 116, 114, 117, 110, 99, 97, 116, 101, 39, 32, 47, 10, + 39, 116, 114, 117, 115, 116, 101, 100, 39, 32, 47, 10, + 39, 116, 114, 121, 95, 99, 97, 115, 116, 39, 32, 47, 10, + 39, 116, 117, 112, 108, 101, 39, 32, 47, 10, + 39, 116, 121, 112, 101, 39, 32, 47, 10, + 39, 116, 121, 112, 101, 115, 39, 32, 47, 10, + 39, 117, 110, 98, 111, 117, 110, 100, 101, 100, 39, 32, 47, 10, + 39, 117, 110, 99, 111, 109, 109, 105, 116, 116, 101, 100, 39, 32, 47, 10, + 39, 117, 110, 101, 110, 99, 114, 121, 112, 116, 101, 100, 39, 32, 47, 10, + 39, 117, 110, 107, 110, 111, 119, 110, 39, 32, 47, 10, + 39, 117, 110, 108, 105, 115, 116, 101, 110, 39, 32, 47, 10, + 39, 117, 110, 108, 111, 103, 103, 101, 100, 39, 32, 47, 10, + 39, 117, 110, 116, 105, 108, 39, 32, 47, 10, + 39, 117, 112, 100, 97, 116, 101, 39, 32, 47, 10, + 39, 117, 115, 101, 39, 32, 47, 10, + 39, 117, 115, 101, 114, 39, 32, 47, 10, + 39, 118, 97, 99, 117, 117, 109, 39, 32, 47, 10, + 39, 118, 97, 108, 105, 100, 39, 32, 47, 10, + 39, 118, 97, 108, 105, 100, 97, 116, 101, 39, 32, 47, 10, + 39, 118, 97, 108, 105, 100, 97, 116, 111, 114, 39, 32, 47, 10, + 39, 118, 97, 108, 117, 101, 39, 32, 47, 10, + 39, 118, 97, 108, 117, 101, 115, 39, 32, 47, 10, + 39, 118, 97, 114, 99, 104, 97, 114, 39, 32, 47, 10, + 39, 118, 97, 114, 105, 97, 98, 108, 101, 39, 32, 47, 10, + 39, 118, 101, 114, 115, 105, 111, 110, 39, 32, 47, 10, + 39, 118, 105, 101, 119, 39, 32, 47, 10, + 39, 118, 105, 101, 119, 115, 39, 32, 47, 10, + 39, 118, 105, 114, 116, 117, 97, 108, 39, 32, 47, 10, + 39, 118, 111, 108, 97, 116, 105, 108, 101, 39, 32, 47, 10, + 39, 119, 104, 105, 116, 101, 115, 112, 97, 99, 101, 39, 32, 47, 10, + 39, 119, 111, 114, 107, 39, 32, 47, 10, + 39, 119, 114, 97, 112, 112, 101, 114, 39, 32, 47, 10, + 39, 119, 114, 105, 116, 101, 39, 32, 47, 10, + 39, 120, 109, 108, 39, 32, 47, 10, + 39, 120, 109, 108, 97, 116, 116, 114, 105, 98, 117, 116, 101, 115, 39, 32, 47, 10, + 39, 120, 109, 108, 99, 111, 110, 99, 97, 116, 39, 32, 47, 10, + 39, 120, 109, 108, 101, 108, 101, 109, 101, 110, 116, 39, 32, 47, 10, + 39, 120, 109, 108, 101, 120, 105, 115, 116, 115, 39, 32, 47, 10, + 39, 120, 109, 108, 102, 111, 114, 101, 115, 116, 39, 32, 47, 10, + 39, 120, 109, 108, 110, 97, 109, 101, 115, 112, 97, 99, 101, 115, 39, 32, 47, 10, + 39, 120, 109, 108, 112, 97, 114, 115, 101, 39, 32, 47, 10, + 39, 120, 109, 108, 112, 105, 39, 32, 47, 10, + 39, 120, 109, 108, 114, 111, 111, 116, 39, 32, 47, 10, + 39, 120, 109, 108, 115, 101, 114, 105, 97, 108, 105, 122, 101, 39, 32, 47, 10, + 39, 120, 109, 108, 116, 97, 98, 108, 101, 39, 32, 47, 10, + 39, 121, 101, 115, 39, 32, 47, 10, + 39, 122, 111, 110, 101, 39, 10, + 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 66, 69, 84, 87, 69, 69, 78, 39, 32, 47, 10, + 39, 66, 73, 71, 73, 78, 84, 39, 32, 47, 10, + 39, 66, 73, 84, 39, 32, 47, 10, + 39, 66, 79, 79, 76, 69, 65, 78, 39, 32, 47, 10, + 39, 67, 72, 65, 82, 39, 32, 47, 10, + 39, 67, 72, 65, 82, 65, 67, 84, 69, 82, 39, 32, 47, 10, + 39, 67, 79, 65, 76, 69, 83, 67, 69, 39, 32, 47, 10, + 39, 67, 79, 76, 85, 77, 78, 83, 39, 32, 47, 10, + 39, 68, 69, 67, 39, 32, 47, 10, + 39, 68, 69, 67, 73, 77, 65, 76, 39, 32, 47, 10, + 39, 69, 88, 73, 83, 84, 83, 39, 32, 47, 10, + 39, 69, 88, 84, 82, 65, 67, 84, 39, 32, 47, 10, + 39, 70, 76, 79, 65, 84, 39, 32, 47, 10, + 39, 71, 69, 78, 69, 82, 65, 84, 69, 68, 39, 32, 47, 10, + 39, 71, 82, 79, 85, 80, 73, 78, 71, 39, 32, 47, 10, + 39, 71, 82, 79, 85, 80, 73, 78, 71, 95, 73, 68, 39, 32, 47, 10, + 39, 73, 78, 79, 85, 84, 39, 32, 47, 10, + 39, 73, 78, 84, 39, 32, 47, 10, + 39, 73, 78, 84, 69, 71, 69, 82, 39, 32, 47, 10, + 39, 73, 78, 84, 69, 82, 86, 65, 76, 39, 32, 47, 10, + 39, 77, 65, 80, 39, 32, 47, 10, + 39, 78, 65, 84, 73, 79, 78, 65, 76, 39, 32, 47, 10, + 39, 78, 67, 72, 65, 82, 39, 32, 47, 10, + 39, 78, 79, 78, 69, 39, 32, 47, 10, + 39, 78, 85, 76, 76, 73, 70, 39, 32, 47, 10, + 39, 78, 85, 77, 69, 82, 73, 67, 39, 32, 47, 10, + 39, 79, 85, 84, 39, 32, 47, 10, + 39, 79, 86, 69, 82, 76, 65, 89, 39, 32, 47, 10, + 39, 80, 79, 83, 73, 84, 73, 79, 78, 39, 32, 47, 10, + 39, 80, 82, 69, 67, 73, 83, 73, 79, 78, 39, 32, 47, 10, + 39, 82, 69, 65, 76, 39, 32, 47, 10, + 39, 82, 79, 87, 39, 32, 47, 10, + 39, 83, 69, 84, 79, 70, 39, 32, 47, 10, + 39, 83, 77, 65, 76, 76, 73, 78, 84, 39, 32, 47, 10, + 39, 83, 85, 66, 83, 84, 82, 73, 78, 71, 39, 32, 47, 10, + 39, 83, 84, 82, 85, 67, 84, 39, 32, 47, 10, + 39, 84, 73, 77, 69, 39, 32, 47, 10, + 39, 84, 73, 77, 69, 83, 84, 65, 77, 80, 39, 32, 47, 10, + 39, 84, 82, 69, 65, 84, 39, 32, 47, 10, + 39, 84, 82, 73, 77, 39, 32, 47, 10, + 39, 84, 82, 89, 95, 67, 65, 83, 84, 39, 32, 47, 10, + 39, 84, 85, 80, 76, 69, 39, 32, 47, 10, + 39, 86, 65, 76, 85, 69, 83, 39, 32, 47, 10, + 39, 86, 65, 82, 67, 72, 65, 82, 39, 32, 47, 10, + 39, 88, 77, 76, 65, 84, 84, 82, 73, 66, 85, 84, 69, 83, 39, 32, 47, 10, + 39, 88, 77, 76, 67, 79, 78, 67, 65, 84, 39, 32, 47, 10, + 39, 88, 77, 76, 69, 76, 69, 77, 69, 78, 84, 39, 32, 47, 10, + 39, 88, 77, 76, 69, 88, 73, 83, 84, 83, 39, 32, 47, 10, + 39, 88, 77, 76, 70, 79, 82, 69, 83, 84, 39, 32, 47, 10, + 39, 88, 77, 76, 78, 65, 77, 69, 83, 80, 65, 67, 69, 83, 39, 32, 47, 10, + 39, 88, 77, 76, 80, 65, 82, 83, 69, 39, 32, 47, 10, + 39, 88, 77, 76, 80, 73, 39, 32, 47, 10, + 39, 88, 77, 76, 82, 79, 79, 84, 39, 32, 47, 10, + 39, 88, 77, 76, 83, 69, 82, 73, 65, 76, 73, 90, 69, 39, 32, 47, 10, + 39, 88, 77, 76, 84, 65, 66, 76, 69, 39, 10, + 70, 117, 110, 99, 78, 97, 109, 101, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 65, 83, 79, 70, 39, 32, 47, 10, + 39, 65, 84, 39, 32, 47, 10, + 39, 65, 85, 84, 72, 79, 82, 73, 90, 65, 84, 73, 79, 78, 39, 32, 47, 10, + 39, 66, 73, 78, 65, 82, 89, 39, 32, 47, 10, + 39, 67, 79, 76, 76, 65, 84, 73, 79, 78, 39, 32, 47, 10, + 39, 67, 79, 78, 67, 85, 82, 82, 69, 78, 84, 76, 89, 39, 32, 47, 10, + 39, 67, 82, 79, 83, 83, 39, 32, 47, 10, + 39, 70, 82, 69, 69, 90, 69, 39, 32, 47, 10, + 39, 70, 85, 76, 76, 39, 32, 47, 10, + 39, 71, 69, 78, 69, 82, 65, 84, 69, 68, 39, 32, 47, 10, + 39, 71, 76, 79, 66, 39, 32, 47, 10, + 39, 73, 76, 73, 75, 69, 39, 32, 47, 10, + 39, 73, 78, 78, 69, 82, 39, 32, 47, 10, + 39, 73, 83, 39, 32, 47, 10, + 39, 73, 83, 78, 85, 76, 76, 39, 32, 47, 10, + 39, 74, 79, 73, 78, 39, 32, 47, 10, + 39, 76, 69, 70, 84, 39, 32, 47, 10, + 39, 76, 73, 75, 69, 39, 32, 47, 10, + 39, 77, 65, 80, 39, 32, 47, 10, + 39, 78, 65, 84, 85, 82, 65, 76, 39, 32, 47, 10, + 39, 78, 79, 84, 78, 85, 76, 76, 39, 32, 47, 10, + 39, 79, 85, 84, 69, 82, 39, 32, 47, 10, + 39, 79, 86, 69, 82, 76, 65, 80, 83, 39, 32, 47, 10, + 39, 80, 79, 83, 73, 84, 73, 79, 78, 65, 76, 39, 32, 47, 10, + 39, 82, 73, 71, 72, 84, 39, 32, 47, 10, + 39, 83, 73, 77, 73, 76, 65, 82, 39, 32, 47, 10, + 39, 83, 84, 82, 85, 67, 84, 39, 32, 47, 10, + 39, 84, 65, 66, 76, 69, 83, 65, 77, 80, 76, 69, 39, 32, 47, 10, + 39, 84, 85, 80, 76, 69, 39, 32, 47, 10, + 39, 86, 69, 82, 66, 79, 83, 69, 39, 10, + 82, 101, 115, 101, 114, 118, 101, 100, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 65, 76, 76, 39, 32, 47, 10, + 39, 65, 78, 65, 76, 89, 83, 69, 39, 32, 47, 10, + 39, 65, 78, 65, 76, 89, 90, 69, 39, 32, 47, 10, + 39, 65, 78, 68, 39, 32, 47, 10, + 39, 65, 78, 89, 39, 32, 47, 10, + 39, 65, 82, 82, 65, 89, 39, 32, 47, 10, + 39, 65, 83, 39, 32, 47, 10, + 39, 65, 83, 67, 39, 32, 47, 10, + 39, 65, 83, 89, 77, 77, 69, 84, 82, 73, 67, 39, 32, 47, 10, + 39, 66, 79, 84, 72, 39, 32, 47, 10, + 39, 67, 65, 83, 69, 39, 32, 47, 10, + 39, 67, 65, 83, 84, 39, 32, 47, 10, + 39, 67, 72, 69, 67, 75, 39, 32, 47, 10, + 39, 67, 79, 76, 76, 65, 84, 69, 39, 32, 47, 10, + 39, 67, 79, 76, 85, 77, 78, 39, 32, 47, 10, + 39, 67, 79, 78, 83, 84, 82, 65, 73, 78, 84, 39, 32, 47, 10, + 39, 67, 82, 69, 65, 84, 69, 39, 32, 47, 10, + 39, 68, 69, 70, 65, 85, 76, 84, 39, 32, 47, 10, + 39, 68, 69, 70, 69, 82, 82, 65, 66, 76, 69, 39, 32, 47, 10, + 39, 68, 69, 83, 67, 39, 32, 47, 10, + 39, 68, 69, 83, 67, 82, 73, 66, 69, 39, 32, 47, 10, + 39, 68, 73, 83, 84, 73, 78, 67, 84, 39, 32, 47, 10, + 39, 68, 79, 39, 32, 47, 10, + 39, 69, 76, 83, 69, 39, 32, 47, 10, + 39, 69, 78, 68, 39, 32, 47, 10, + 39, 69, 88, 67, 69, 80, 84, 39, 32, 47, 10, + 39, 70, 65, 76, 83, 69, 39, 32, 47, 10, + 39, 70, 69, 84, 67, 72, 39, 32, 47, 10, + 39, 70, 79, 82, 39, 32, 47, 10, + 39, 70, 79, 82, 69, 73, 71, 78, 39, 32, 47, 10, + 39, 70, 82, 79, 77, 39, 32, 47, 10, + 39, 71, 82, 79, 85, 80, 39, 32, 47, 10, + 39, 72, 65, 86, 73, 78, 71, 39, 32, 47, 10, + 39, 73, 78, 39, 32, 47, 10, + 39, 73, 78, 73, 84, 73, 65, 76, 76, 89, 39, 32, 47, 10, + 39, 73, 78, 84, 69, 82, 83, 69, 67, 84, 39, 32, 47, 10, + 39, 73, 78, 84, 79, 39, 32, 47, 10, + 39, 76, 65, 77, 66, 68, 65, 39, 32, 47, 10, + 39, 76, 65, 84, 69, 82, 65, 76, 39, 32, 47, 10, + 39, 76, 69, 65, 68, 73, 78, 71, 39, 32, 47, 10, + 39, 76, 73, 77, 73, 84, 39, 32, 47, 10, + 39, 77, 65, 84, 67, 72, 95, 82, 69, 67, 79, 71, 78, 73, 90, 69, 39, 32, 47, 10, + 39, 78, 79, 84, 39, 32, 47, 10, + 39, 78, 85, 76, 76, 39, 32, 47, 10, + 39, 79, 70, 70, 83, 69, 84, 39, 32, 47, 10, + 39, 79, 78, 39, 32, 47, 10, + 39, 79, 78, 76, 89, 39, 32, 47, 10, + 39, 79, 82, 39, 32, 47, 10, + 39, 79, 82, 68, 69, 82, 39, 32, 47, 10, + 39, 80, 73, 86, 79, 84, 39, 32, 47, 10, + 39, 80, 73, 86, 79, 84, 95, 76, 79, 78, 71, 69, 82, 39, 32, 47, 10, + 39, 80, 73, 86, 79, 84, 95, 87, 73, 68, 69, 82, 39, 32, 47, 10, + 39, 80, 76, 65, 67, 73, 78, 71, 39, 32, 47, 10, + 39, 80, 82, 73, 77, 65, 82, 89, 39, 32, 47, 10, + 39, 81, 85, 65, 76, 73, 70, 89, 39, 32, 47, 10, + 39, 82, 69, 70, 69, 82, 69, 78, 67, 69, 83, 39, 32, 47, 10, + 39, 82, 69, 84, 85, 82, 78, 73, 78, 71, 39, 32, 47, 10, + 39, 83, 69, 76, 69, 67, 84, 39, 32, 47, 10, + 39, 83, 72, 79, 87, 39, 32, 47, 10, + 39, 83, 79, 77, 69, 39, 32, 47, 10, + 39, 83, 85, 77, 77, 65, 82, 73, 90, 69, 39, 32, 47, 10, + 39, 83, 89, 77, 77, 69, 84, 82, 73, 67, 39, 32, 47, 10, + 39, 84, 65, 66, 76, 69, 39, 32, 47, 10, + 39, 84, 72, 69, 78, 39, 32, 47, 10, + 39, 84, 79, 39, 32, 47, 10, + 39, 84, 82, 65, 73, 76, 73, 78, 71, 39, 32, 47, 10, + 39, 84, 82, 85, 69, 39, 32, 47, 10, + 39, 85, 78, 73, 79, 78, 39, 32, 47, 10, + 39, 85, 78, 73, 81, 85, 69, 39, 32, 47, 10, + 39, 85, 78, 80, 73, 86, 79, 84, 39, 32, 47, 10, + 39, 85, 83, 73, 78, 71, 39, 32, 47, 10, + 39, 86, 65, 82, 73, 65, 68, 73, 67, 39, 32, 47, 10, + 39, 87, 72, 69, 78, 39, 32, 47, 10, + 39, 87, 72, 69, 82, 69, 39, 32, 47, 10, + 39, 87, 73, 78, 68, 79, 87, 39, 32, 47, 10, + 39, 87, 73, 84, 72, 39, 10, + 84, 121, 112, 101, 78, 97, 109, 101, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 65, 83, 79, 70, 39, 32, 47, 10, + 39, 65, 84, 39, 32, 47, 10, + 39, 65, 85, 84, 72, 79, 82, 73, 90, 65, 84, 73, 79, 78, 39, 32, 47, 10, + 39, 66, 73, 78, 65, 82, 89, 39, 32, 47, 10, + 39, 66, 89, 39, 32, 47, 10, + 39, 67, 79, 76, 76, 65, 84, 73, 79, 78, 39, 32, 47, 10, + 39, 67, 79, 76, 85, 77, 78, 83, 39, 32, 47, 10, + 39, 67, 79, 78, 67, 85, 82, 82, 69, 78, 84, 76, 89, 39, 32, 47, 10, + 39, 67, 82, 79, 83, 83, 39, 32, 47, 10, + 39, 70, 82, 69, 69, 90, 69, 39, 32, 47, 10, + 39, 70, 85, 76, 76, 39, 32, 47, 10, + 39, 71, 76, 79, 66, 39, 32, 47, 10, + 39, 73, 76, 73, 75, 69, 39, 32, 47, 10, + 39, 73, 78, 78, 69, 82, 39, 32, 47, 10, + 39, 73, 83, 39, 32, 47, 10, + 39, 73, 83, 78, 85, 76, 76, 39, 32, 47, 10, + 39, 74, 79, 73, 78, 39, 32, 47, 10, + 39, 76, 69, 70, 84, 39, 32, 47, 10, + 39, 76, 73, 75, 69, 39, 32, 47, 10, + 39, 78, 65, 84, 85, 82, 65, 76, 39, 32, 47, 10, + 39, 78, 79, 84, 78, 85, 76, 76, 39, 32, 47, 10, + 39, 79, 85, 84, 69, 82, 39, 32, 47, 10, + 39, 79, 86, 69, 82, 76, 65, 80, 83, 39, 32, 47, 10, + 39, 80, 79, 83, 73, 84, 73, 79, 78, 65, 76, 39, 32, 47, 10, + 39, 82, 73, 71, 72, 84, 39, 32, 47, 10, + 39, 85, 78, 80, 65, 67, 75, 39, 32, 47, 10, + 39, 83, 73, 77, 73, 76, 65, 82, 39, 32, 47, 10, + 39, 84, 65, 66, 76, 69, 83, 65, 77, 80, 76, 69, 39, 32, 47, 10, + 39, 84, 82, 89, 95, 67, 65, 83, 84, 39, 32, 47, 10, + 39, 86, 69, 82, 66, 79, 83, 69, 39, 32, 47, 10, + 39, 83, 69, 77, 73, 39, 32, 47, 10, + 39, 65, 78, 84, 73, 39, 10, + 85, 110, 114, 101, 115, 101, 114, 118, 101, 100, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 65, 66, 79, 82, 84, 39, 32, 47, 10, + 39, 65, 66, 83, 79, 76, 85, 84, 69, 39, 32, 47, 10, + 39, 65, 67, 67, 69, 83, 83, 39, 32, 47, 10, + 39, 65, 67, 84, 73, 79, 78, 39, 32, 47, 10, + 39, 65, 68, 68, 39, 32, 47, 10, + 39, 65, 68, 77, 73, 78, 39, 32, 47, 10, + 39, 65, 70, 84, 69, 82, 39, 32, 47, 10, + 39, 65, 71, 71, 82, 69, 71, 65, 84, 69, 39, 32, 47, 10, + 39, 65, 76, 83, 79, 39, 32, 47, 10, + 39, 65, 76, 84, 69, 82, 39, 32, 47, 10, + 39, 65, 76, 87, 65, 89, 83, 39, 32, 47, 10, + 39, 65, 80, 80, 82, 79, 88, 39, 32, 47, 10, + 39, 65, 83, 83, 69, 82, 84, 73, 79, 78, 39, 32, 47, 10, + 39, 65, 83, 83, 73, 71, 78, 77, 69, 78, 84, 39, 32, 47, 10, + 39, 65, 84, 84, 65, 67, 72, 39, 32, 47, 10, + 39, 65, 84, 84, 82, 73, 66, 85, 84, 69, 39, 32, 47, 10, + 39, 65, 85, 84, 79, 39, 32, 47, 10, + 39, 66, 65, 67, 75, 87, 65, 82, 68, 39, 32, 47, 10, + 39, 66, 69, 70, 79, 82, 69, 39, 32, 47, 10, + 39, 66, 69, 71, 73, 78, 39, 32, 47, 10, + 39, 67, 65, 67, 72, 69, 39, 32, 47, 10, + 39, 67, 65, 76, 76, 39, 32, 47, 10, + 39, 67, 65, 76, 76, 69, 68, 39, 32, 47, 10, + 39, 67, 65, 83, 67, 65, 68, 69, 39, 32, 47, 10, + 39, 67, 65, 83, 67, 65, 68, 69, 68, 39, 32, 47, 10, + 39, 67, 65, 84, 65, 76, 79, 71, 39, 32, 47, 10, + 39, 67, 69, 78, 84, 85, 82, 73, 69, 83, 39, 32, 47, 10, + 39, 67, 69, 78, 84, 85, 82, 89, 39, 32, 47, 10, + 39, 67, 72, 65, 73, 78, 39, 32, 47, 10, + 39, 67, 72, 65, 82, 65, 67, 84, 69, 82, 73, 83, 84, 73, 67, 83, 39, 32, 47, 10, + 39, 67, 72, 69, 67, 75, 80, 79, 73, 78, 84, 39, 32, 47, 10, + 39, 67, 76, 65, 83, 83, 39, 32, 47, 10, + 39, 67, 76, 79, 83, 69, 39, 32, 47, 10, + 39, 67, 76, 85, 83, 84, 69, 82, 39, 32, 47, 10, + 39, 67, 79, 77, 77, 69, 78, 84, 39, 32, 47, 10, + 39, 67, 79, 77, 77, 69, 78, 84, 83, 39, 32, 47, 10, + 39, 67, 79, 77, 77, 73, 84, 39, 32, 47, 10, + 39, 67, 79, 77, 77, 73, 84, 84, 69, 68, 39, 32, 47, 10, + 39, 67, 79, 77, 80, 82, 69, 83, 83, 73, 79, 78, 39, 32, 47, 10, + 39, 67, 79, 78, 70, 73, 71, 85, 82, 65, 84, 73, 79, 78, 39, 32, 47, 10, + 39, 67, 79, 78, 70, 76, 73, 67, 84, 39, 32, 47, 10, + 39, 67, 79, 78, 78, 69, 67, 84, 39, 32, 47, 10, + 39, 67, 79, 78, 78, 69, 67, 84, 73, 79, 78, 39, 32, 47, 10, + 39, 67, 79, 78, 83, 84, 82, 65, 73, 78, 84, 83, 39, 32, 47, 10, + 39, 67, 79, 78, 84, 69, 78, 84, 39, 32, 47, 10, + 39, 67, 79, 78, 84, 73, 78, 85, 69, 39, 32, 47, 10, + 39, 67, 79, 78, 86, 69, 82, 83, 73, 79, 78, 39, 32, 47, 10, + 39, 67, 79, 80, 89, 39, 32, 47, 10, + 39, 67, 79, 83, 84, 39, 32, 47, 10, + 39, 67, 83, 86, 39, 32, 47, 10, + 39, 67, 85, 66, 69, 39, 32, 47, 10, + 39, 67, 85, 82, 82, 69, 78, 84, 39, 32, 47, 10, + 39, 67, 85, 82, 83, 79, 82, 39, 32, 47, 10, + 39, 67, 89, 67, 76, 69, 39, 32, 47, 10, + 39, 68, 65, 84, 65, 39, 32, 47, 10, + 39, 68, 65, 84, 65, 66, 65, 83, 69, 39, 32, 47, 10, + 39, 68, 65, 89, 39, 32, 47, 10, + 39, 68, 65, 89, 83, 39, 32, 47, 10, + 39, 68, 69, 65, 76, 76, 79, 67, 65, 84, 69, 39, 32, 47, 10, + 39, 68, 69, 67, 65, 68, 69, 39, 32, 47, 10, + 39, 68, 69, 67, 65, 68, 69, 83, 39, 32, 47, 10, + 39, 68, 69, 67, 76, 65, 82, 69, 39, 32, 47, 10, + 39, 68, 69, 70, 65, 85, 76, 84, 83, 39, 32, 47, 10, + 39, 68, 69, 70, 69, 82, 82, 69, 68, 39, 32, 47, 10, + 39, 68, 69, 70, 73, 78, 69, 82, 39, 32, 47, 10, + 39, 68, 69, 70, 73, 78, 69, 83, 39, 32, 47, 10, + 39, 68, 69, 76, 69, 84, 69, 39, 32, 47, 10, + 39, 68, 69, 76, 73, 77, 73, 84, 69, 82, 39, 32, 47, 10, + 39, 68, 69, 76, 73, 77, 73, 84, 69, 82, 83, 39, 32, 47, 10, + 39, 68, 69, 80, 69, 78, 68, 83, 39, 32, 47, 10, + 39, 68, 69, 83, 84, 82, 79, 89, 39, 32, 47, 10, + 39, 68, 69, 84, 65, 67, 72, 39, 32, 47, 10, + 39, 68, 73, 67, 84, 73, 79, 78, 65, 82, 89, 39, 32, 47, 10, + 39, 68, 73, 83, 65, 66, 76, 69, 39, 32, 47, 10, + 39, 68, 73, 83, 67, 65, 82, 68, 39, 32, 47, 10, + 39, 68, 73, 83, 67, 79, 78, 78, 69, 67, 84, 39, 32, 47, 10, + 39, 68, 73, 83, 84, 65, 78, 67, 69, 39, 32, 47, 10, + 39, 68, 79, 67, 85, 77, 69, 78, 84, 39, 32, 47, 10, + 39, 68, 79, 77, 65, 73, 78, 39, 32, 47, 10, + 39, 68, 79, 85, 66, 76, 69, 39, 32, 47, 10, + 39, 68, 82, 79, 80, 39, 32, 47, 10, + 39, 69, 65, 67, 72, 39, 32, 47, 10, + 39, 69, 78, 65, 66, 76, 69, 39, 32, 47, 10, + 39, 69, 78, 67, 79, 68, 73, 78, 71, 39, 32, 47, 10, + 39, 69, 78, 67, 82, 89, 80, 84, 69, 68, 39, 32, 47, 10, + 39, 69, 78, 85, 77, 39, 32, 47, 10, + 39, 69, 82, 82, 79, 82, 39, 32, 47, 10, + 39, 69, 83, 67, 65, 80, 69, 39, 32, 47, 10, + 39, 69, 86, 69, 78, 84, 39, 32, 47, 10, + 39, 69, 88, 65, 67, 84, 39, 32, 47, 10, + 39, 69, 88, 67, 76, 85, 68, 69, 39, 32, 47, 10, + 39, 69, 88, 67, 76, 85, 68, 73, 78, 71, 39, 32, 47, 10, + 39, 69, 88, 67, 76, 85, 83, 73, 86, 69, 39, 32, 47, 10, + 39, 69, 88, 69, 67, 85, 84, 69, 39, 32, 47, 10, + 39, 69, 88, 80, 76, 65, 73, 78, 39, 32, 47, 10, + 39, 69, 88, 80, 79, 82, 84, 39, 32, 47, 10, + 39, 69, 88, 80, 79, 82, 84, 95, 83, 84, 65, 84, 69, 39, 32, 47, 10, + 39, 69, 88, 84, 69, 78, 83, 73, 79, 78, 39, 32, 47, 10, + 39, 69, 88, 84, 69, 78, 83, 73, 79, 78, 83, 39, 32, 47, 10, + 39, 69, 88, 84, 69, 82, 78, 65, 76, 39, 32, 47, 10, + 39, 70, 65, 77, 73, 76, 89, 39, 32, 47, 10, + 39, 70, 73, 76, 84, 69, 82, 39, 32, 47, 10, + 39, 70, 73, 82, 83, 84, 39, 32, 47, 10, + 39, 70, 79, 76, 76, 79, 87, 73, 78, 71, 39, 32, 47, 10, + 39, 70, 79, 82, 67, 69, 39, 32, 47, 10, + 39, 70, 79, 82, 87, 65, 82, 68, 39, 32, 47, 10, + 39, 70, 85, 78, 67, 84, 73, 79, 78, 39, 32, 47, 10, + 39, 70, 85, 78, 67, 84, 73, 79, 78, 83, 39, 32, 47, 10, + 39, 71, 76, 79, 66, 65, 76, 39, 32, 47, 10, + 39, 71, 82, 65, 78, 84, 39, 32, 47, 10, + 39, 71, 82, 65, 78, 84, 69, 68, 39, 32, 47, 10, + 39, 71, 82, 79, 85, 80, 83, 39, 32, 47, 10, + 39, 72, 65, 78, 68, 76, 69, 82, 39, 32, 47, 10, + 39, 72, 69, 65, 68, 69, 82, 39, 32, 47, 10, + 39, 72, 79, 76, 68, 39, 32, 47, 10, + 39, 72, 79, 85, 82, 39, 32, 47, 10, + 39, 72, 79, 85, 82, 83, 39, 32, 47, 10, + 39, 73, 68, 69, 78, 84, 73, 84, 89, 39, 32, 47, 10, + 39, 73, 70, 39, 32, 47, 10, + 39, 73, 71, 78, 79, 82, 69, 39, 32, 47, 10, + 39, 73, 77, 77, 69, 68, 73, 65, 84, 69, 39, 32, 47, 10, + 39, 73, 77, 77, 85, 84, 65, 66, 76, 69, 39, 32, 47, 10, + 39, 73, 77, 80, 76, 73, 67, 73, 84, 39, 32, 47, 10, + 39, 73, 77, 80, 79, 82, 84, 39, 32, 47, 10, + 39, 73, 78, 67, 76, 85, 68, 69, 39, 32, 47, 10, + 39, 73, 78, 67, 76, 85, 68, 73, 78, 71, 39, 32, 47, 10, + 39, 73, 78, 67, 82, 69, 77, 69, 78, 84, 39, 32, 47, 10, + 39, 73, 78, 68, 69, 88, 39, 32, 47, 10, + 39, 73, 78, 68, 69, 88, 69, 83, 39, 32, 47, 10, + 39, 73, 78, 72, 69, 82, 73, 84, 39, 32, 47, 10, + 39, 73, 78, 72, 69, 82, 73, 84, 83, 39, 32, 47, 10, + 39, 73, 78, 76, 73, 78, 69, 39, 32, 47, 10, + 39, 73, 78, 80, 85, 84, 39, 32, 47, 10, + 39, 73, 78, 83, 69, 78, 83, 73, 84, 73, 86, 69, 39, 32, 47, 10, + 39, 73, 78, 83, 69, 82, 84, 39, 32, 47, 10, + 39, 73, 78, 83, 84, 65, 76, 76, 39, 32, 47, 10, + 39, 73, 78, 83, 84, 69, 65, 68, 39, 32, 47, 10, + 39, 73, 78, 86, 79, 75, 69, 82, 39, 32, 47, 10, + 39, 73, 83, 79, 76, 65, 84, 73, 79, 78, 39, 32, 47, 10, + 39, 74, 83, 79, 78, 39, 32, 47, 10, + 39, 75, 69, 89, 39, 32, 47, 10, + 39, 76, 65, 66, 69, 76, 39, 32, 47, 10, + 39, 76, 65, 78, 71, 85, 65, 71, 69, 39, 32, 47, 10, + 39, 76, 65, 82, 71, 69, 39, 32, 47, 10, + 39, 76, 65, 83, 84, 39, 32, 47, 10, + 39, 76, 69, 65, 75, 80, 82, 79, 79, 70, 39, 32, 47, 10, + 39, 76, 69, 86, 69, 76, 39, 32, 47, 10, + 39, 76, 73, 83, 84, 69, 78, 39, 32, 47, 10, + 39, 76, 79, 65, 68, 39, 32, 47, 10, + 39, 76, 79, 67, 65, 76, 39, 32, 47, 10, + 39, 76, 79, 67, 65, 84, 73, 79, 78, 39, 32, 47, 10, + 39, 76, 79, 67, 75, 39, 32, 47, 10, + 39, 76, 79, 67, 75, 69, 68, 39, 32, 47, 10, + 39, 76, 79, 71, 71, 69, 68, 39, 32, 47, 10, + 39, 77, 65, 67, 82, 79, 39, 32, 47, 10, + 39, 77, 65, 80, 80, 73, 78, 71, 39, 32, 47, 10, + 39, 77, 65, 84, 67, 72, 39, 32, 47, 10, + 39, 77, 65, 84, 67, 72, 69, 68, 39, 32, 47, 10, + 39, 77, 65, 84, 69, 82, 73, 65, 76, 73, 90, 69, 68, 39, 32, 47, 10, + 39, 77, 65, 88, 86, 65, 76, 85, 69, 39, 32, 47, 10, + 39, 77, 69, 82, 71, 69, 39, 32, 47, 10, + 39, 77, 69, 84, 72, 79, 68, 39, 32, 47, 10, + 39, 77, 73, 67, 82, 79, 83, 69, 67, 79, 78, 68, 39, 32, 47, 10, + 39, 77, 73, 67, 82, 79, 83, 69, 67, 79, 78, 68, 83, 39, 32, 47, 10, + 39, 77, 73, 76, 76, 69, 78, 78, 73, 65, 39, 32, 47, 10, + 39, 77, 73, 76, 76, 69, 78, 78, 73, 85, 77, 39, 32, 47, 10, + 39, 77, 73, 76, 76, 73, 83, 69, 67, 79, 78, 68, 39, 32, 47, 10, + 39, 77, 73, 76, 76, 73, 83, 69, 67, 79, 78, 68, 83, 39, 32, 47, 10, + 39, 77, 73, 78, 85, 84, 69, 39, 32, 47, 10, + 39, 77, 73, 78, 85, 84, 69, 83, 39, 32, 47, 10, + 39, 77, 73, 78, 86, 65, 76, 85, 69, 39, 32, 47, 10, + 39, 77, 79, 68, 69, 39, 32, 47, 10, + 39, 77, 79, 78, 84, 72, 39, 32, 47, 10, + 39, 77, 79, 78, 84, 72, 83, 39, 32, 47, 10, + 39, 77, 79, 86, 69, 39, 32, 47, 10, + 39, 78, 65, 77, 69, 39, 32, 47, 10, + 39, 78, 65, 77, 69, 83, 39, 32, 47, 10, + 39, 78, 69, 65, 82, 69, 83, 84, 39, 32, 47, 10, + 39, 78, 69, 87, 39, 32, 47, 10, + 39, 78, 69, 88, 84, 39, 32, 47, 10, + 39, 78, 79, 39, 32, 47, 10, + 39, 78, 79, 84, 72, 73, 78, 71, 39, 32, 47, 10, + 39, 78, 79, 84, 73, 70, 89, 39, 32, 47, 10, + 39, 78, 79, 87, 65, 73, 84, 39, 32, 47, 10, + 39, 78, 85, 76, 76, 83, 39, 32, 47, 10, + 39, 79, 66, 74, 69, 67, 84, 39, 32, 47, 10, + 39, 79, 70, 39, 32, 47, 10, + 39, 79, 70, 70, 39, 32, 47, 10, + 39, 79, 73, 68, 83, 39, 32, 47, 10, + 39, 79, 76, 68, 39, 32, 47, 10, + 39, 79, 80, 69, 82, 65, 84, 79, 82, 39, 32, 47, 10, + 39, 79, 80, 84, 73, 79, 78, 39, 32, 47, 10, + 39, 79, 80, 84, 73, 79, 78, 83, 39, 32, 47, 10, + 39, 79, 82, 68, 73, 78, 65, 76, 73, 84, 89, 39, 32, 47, 10, + 39, 79, 84, 72, 69, 82, 83, 39, 32, 47, 10, + 39, 79, 86, 69, 82, 39, 32, 47, 10, + 39, 79, 86, 69, 82, 82, 73, 68, 73, 78, 71, 39, 32, 47, 10, + 39, 79, 87, 78, 69, 68, 39, 32, 47, 10, + 39, 79, 87, 78, 69, 82, 39, 32, 47, 10, + 39, 80, 65, 82, 65, 76, 76, 69, 76, 39, 32, 47, 10, + 39, 80, 65, 82, 83, 69, 82, 39, 32, 47, 10, + 39, 80, 65, 82, 84, 73, 65, 76, 39, 32, 47, 10, + 39, 80, 65, 82, 84, 73, 84, 73, 79, 78, 39, 32, 47, 10, + 39, 80, 65, 82, 84, 73, 84, 73, 79, 78, 69, 68, 39, 32, 47, 10, + 39, 80, 65, 83, 83, 73, 78, 71, 39, 32, 47, 10, + 39, 80, 65, 83, 83, 87, 79, 82, 68, 39, 32, 47, 10, + 39, 80, 69, 82, 67, 69, 78, 84, 39, 32, 47, 10, + 39, 80, 69, 82, 83, 73, 83, 84, 69, 78, 84, 39, 32, 47, 10, + 39, 80, 76, 65, 78, 83, 39, 32, 47, 10, + 39, 80, 79, 76, 73, 67, 89, 39, 32, 47, 10, + 39, 80, 82, 65, 71, 77, 65, 39, 32, 47, 10, + 39, 80, 82, 69, 67, 69, 68, 73, 78, 71, 39, 32, 47, 10, + 39, 80, 82, 69, 80, 65, 82, 69, 39, 32, 47, 10, + 39, 80, 82, 69, 80, 65, 82, 69, 68, 39, 32, 47, 10, + 39, 80, 82, 69, 83, 69, 82, 86, 69, 39, 32, 47, 10, + 39, 80, 82, 73, 79, 82, 39, 32, 47, 10, + 39, 80, 82, 73, 86, 73, 76, 69, 71, 69, 83, 39, 32, 47, 10, + 39, 80, 82, 79, 67, 69, 68, 85, 82, 65, 76, 39, 32, 47, 10, + 39, 80, 82, 79, 67, 69, 68, 85, 82, 69, 39, 32, 47, 10, + 39, 80, 82, 79, 71, 82, 65, 77, 39, 32, 47, 10, + 39, 80, 85, 66, 76, 73, 67, 65, 84, 73, 79, 78, 39, 32, 47, 10, + 39, 81, 85, 65, 82, 84, 69, 82, 39, 32, 47, 10, + 39, 81, 85, 65, 82, 84, 69, 82, 83, 39, 32, 47, 10, + 39, 81, 85, 79, 84, 69, 39, 32, 47, 10, + 39, 82, 65, 78, 71, 69, 39, 32, 47, 10, + 39, 82, 69, 65, 68, 39, 32, 47, 10, + 39, 82, 69, 65, 83, 83, 73, 71, 78, 39, 32, 47, 10, + 39, 82, 69, 67, 72, 69, 67, 75, 39, 32, 47, 10, + 39, 82, 69, 67, 85, 82, 83, 73, 86, 69, 39, 32, 47, 10, + 39, 82, 69, 70, 39, 32, 47, 10, + 39, 82, 69, 70, 69, 82, 69, 78, 67, 73, 78, 71, 39, 32, 47, 10, + 39, 82, 69, 70, 82, 69, 83, 72, 39, 32, 47, 10, + 39, 82, 69, 71, 73, 83, 84, 69, 82, 39, 32, 47, 10, + 39, 82, 69, 73, 78, 68, 69, 88, 39, 32, 47, 10, + 39, 82, 69, 76, 65, 84, 73, 86, 69, 39, 32, 47, 10, + 39, 82, 69, 76, 69, 65, 83, 69, 39, 32, 47, 10, + 39, 82, 69, 78, 65, 77, 69, 39, 32, 47, 10, + 39, 82, 69, 80, 69, 65, 84, 65, 66, 76, 69, 39, 32, 47, 10, + 39, 82, 69, 80, 76, 65, 67, 69, 39, 32, 47, 10, + 39, 82, 69, 80, 76, 73, 67, 65, 39, 32, 47, 10, + 39, 82, 69, 83, 69, 84, 39, 32, 47, 10, + 39, 82, 69, 83, 79, 85, 82, 67, 69, 39, 32, 47, 10, + 39, 82, 69, 83, 79, 85, 82, 67, 69, 83, 39, 32, 47, 10, + 39, 82, 69, 83, 80, 69, 67, 84, 39, 32, 47, 10, + 39, 82, 69, 83, 84, 65, 82, 84, 39, 32, 47, 10, + 39, 82, 69, 83, 84, 82, 73, 67, 84, 39, 32, 47, 10, + 39, 82, 69, 84, 85, 82, 78, 83, 39, 32, 47, 10, + 39, 82, 69, 86, 79, 75, 69, 39, 32, 47, 10, + 39, 82, 79, 76, 69, 39, 32, 47, 10, + 39, 82, 79, 76, 76, 66, 65, 67, 75, 39, 32, 47, 10, + 39, 82, 79, 76, 76, 85, 80, 39, 32, 47, 10, + 39, 82, 79, 87, 83, 39, 32, 47, 10, + 39, 82, 85, 76, 69, 39, 32, 47, 10, + 39, 83, 65, 77, 80, 76, 69, 39, 32, 47, 10, + 39, 83, 65, 86, 69, 80, 79, 73, 78, 84, 39, 32, 47, 10, + 39, 83, 67, 72, 69, 77, 65, 39, 32, 47, 10, + 39, 83, 67, 72, 69, 77, 65, 83, 39, 32, 47, 10, + 39, 83, 67, 79, 80, 69, 39, 32, 47, 10, + 39, 83, 67, 82, 79, 76, 76, 39, 32, 47, 10, + 39, 83, 69, 65, 82, 67, 72, 39, 32, 47, 10, + 39, 83, 69, 67, 79, 78, 68, 39, 32, 47, 10, + 39, 83, 69, 67, 79, 78, 68, 83, 39, 32, 47, 10, + 39, 83, 69, 67, 82, 69, 84, 39, 32, 47, 10, + 39, 83, 69, 67, 85, 82, 69, 39, 32, 47, 10, + 39, 83, 69, 67, 85, 82, 73, 84, 89, 39, 32, 47, 10, + 39, 83, 69, 81, 85, 69, 78, 67, 69, 39, 32, 47, 10, + 39, 83, 69, 81, 85, 69, 78, 67, 69, 83, 39, 32, 47, 10, + 39, 83, 69, 82, 73, 65, 76, 73, 90, 65, 66, 76, 69, 39, 32, 47, 10, + 39, 83, 69, 82, 86, 69, 82, 39, 32, 47, 10, + 39, 83, 69, 83, 83, 73, 79, 78, 39, 32, 47, 10, + 39, 83, 69, 84, 39, 32, 47, 10, + 39, 83, 69, 84, 83, 39, 32, 47, 10, + 39, 83, 72, 65, 82, 69, 39, 32, 47, 10, + 39, 83, 73, 77, 73, 76, 65, 82, 73, 84, 89, 39, 32, 47, 10, + 39, 83, 73, 77, 80, 76, 69, 39, 32, 47, 10, + 39, 83, 75, 73, 80, 39, 32, 47, 10, + 39, 83, 78, 65, 80, 83, 72, 79, 84, 39, 32, 47, 10, + 39, 83, 79, 82, 84, 69, 68, 39, 32, 47, 10, + 39, 83, 79, 85, 82, 67, 69, 39, 32, 47, 10, + 39, 83, 81, 76, 39, 32, 47, 10, + 39, 83, 84, 65, 66, 76, 69, 39, 32, 47, 10, + 39, 83, 84, 65, 78, 68, 65, 76, 79, 78, 69, 39, 32, 47, 10, + 39, 83, 84, 65, 82, 84, 39, 32, 47, 10, + 39, 83, 84, 65, 84, 69, 77, 69, 78, 84, 39, 32, 47, 10, + 39, 83, 84, 65, 84, 73, 83, 84, 73, 67, 83, 39, 32, 47, 10, + 39, 83, 84, 68, 73, 78, 39, 32, 47, 10, + 39, 83, 84, 68, 79, 85, 84, 39, 32, 47, 10, + 39, 83, 84, 79, 82, 65, 71, 69, 39, 32, 47, 10, + 39, 83, 84, 79, 82, 69, 68, 39, 32, 47, 10, + 39, 83, 84, 82, 73, 67, 84, 39, 32, 47, 10, + 39, 83, 84, 82, 73, 80, 39, 32, 47, 10, + 39, 83, 85, 66, 83, 67, 82, 73, 80, 84, 73, 79, 78, 39, 32, 47, 10, + 39, 83, 85, 66, 83, 69, 84, 39, 32, 47, 10, + 39, 83, 89, 83, 73, 68, 39, 32, 47, 10, + 39, 83, 89, 83, 84, 69, 77, 39, 32, 47, 10, + 39, 84, 65, 66, 76, 69, 83, 39, 32, 47, 10, + 39, 84, 65, 66, 76, 69, 83, 80, 65, 67, 69, 39, 32, 47, 10, + 39, 84, 65, 82, 71, 69, 84, 39, 32, 47, 10, + 39, 84, 69, 77, 80, 39, 32, 47, 10, + 39, 84, 69, 77, 80, 76, 65, 84, 69, 39, 32, 47, 10, + 39, 84, 69, 77, 80, 79, 82, 65, 82, 89, 39, 32, 47, 10, + 39, 84, 69, 88, 84, 39, 32, 47, 10, + 39, 84, 73, 69, 83, 39, 32, 47, 10, + 39, 84, 82, 65, 78, 83, 65, 67, 84, 73, 79, 78, 39, 32, 47, 10, + 39, 84, 82, 65, 78, 83, 70, 79, 82, 77, 39, 32, 47, 10, + 39, 84, 82, 73, 71, 71, 69, 82, 39, 32, 47, 10, + 39, 84, 82, 85, 78, 67, 65, 84, 69, 39, 32, 47, 10, + 39, 84, 82, 85, 83, 84, 69, 68, 39, 32, 47, 10, + 39, 84, 89, 80, 69, 39, 32, 47, 10, + 39, 84, 89, 80, 69, 83, 39, 32, 47, 10, + 39, 85, 78, 66, 79, 85, 78, 68, 69, 68, 39, 32, 47, 10, + 39, 85, 78, 67, 79, 77, 77, 73, 84, 84, 69, 68, 39, 32, 47, 10, + 39, 85, 78, 69, 78, 67, 82, 89, 80, 84, 69, 68, 39, 32, 47, 10, + 39, 85, 78, 75, 78, 79, 87, 78, 39, 32, 47, 10, + 39, 85, 78, 76, 73, 83, 84, 69, 78, 39, 32, 47, 10, + 39, 85, 78, 76, 79, 71, 71, 69, 68, 39, 32, 47, 10, + 39, 85, 78, 84, 73, 76, 39, 32, 47, 10, + 39, 85, 80, 68, 65, 84, 69, 39, 32, 47, 10, + 39, 85, 83, 69, 39, 32, 47, 10, + 39, 85, 83, 69, 82, 39, 32, 47, 10, + 39, 86, 65, 67, 85, 85, 77, 39, 32, 47, 10, + 39, 86, 65, 76, 73, 68, 39, 32, 47, 10, + 39, 86, 65, 76, 73, 68, 65, 84, 69, 39, 32, 47, 10, + 39, 86, 65, 76, 73, 68, 65, 84, 79, 82, 39, 32, 47, 10, + 39, 86, 65, 76, 85, 69, 39, 32, 47, 10, + 39, 86, 65, 82, 73, 65, 66, 76, 69, 39, 32, 47, 10, + 39, 86, 65, 82, 89, 73, 78, 71, 39, 32, 47, 10, + 39, 86, 69, 82, 83, 73, 79, 78, 39, 32, 47, 10, + 39, 86, 73, 69, 87, 39, 32, 47, 10, + 39, 86, 73, 69, 87, 83, 39, 32, 47, 10, + 39, 86, 73, 82, 84, 85, 65, 76, 39, 32, 47, 10, + 39, 86, 79, 76, 65, 84, 73, 76, 69, 39, 32, 47, 10, + 39, 87, 69, 69, 75, 39, 32, 47, 10, + 39, 87, 69, 69, 75, 83, 39, 32, 47, 10, + 39, 87, 72, 73, 84, 69, 83, 80, 65, 67, 69, 39, 32, 47, 10, + 39, 87, 73, 84, 72, 73, 78, 39, 32, 47, 10, + 39, 87, 73, 84, 72, 79, 85, 84, 39, 32, 47, 10, + 39, 87, 79, 82, 75, 39, 32, 47, 10, + 39, 87, 82, 65, 80, 80, 69, 82, 39, 32, 47, 10, + 39, 87, 82, 73, 84, 69, 39, 32, 47, 10, + 39, 88, 77, 76, 39, 32, 47, 10, + 39, 89, 69, 65, 82, 39, 32, 47, 10, + 39, 89, 69, 65, 82, 83, 39, 32, 47, 10, + 39, 89, 69, 83, 39, 32, 47, 10, + 39, 90, 79, 78, 69, 39, 10, + 65, 108, 116, 101, 114, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 65, 76, 84, 69, 82, 39, 32, 65, 108, 116, 101, 114, 79, 112, 116, 105, 111, 110, 115, 10, + 65, 108, 116, 101, 114, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 65, 108, 116, 101, 114, 84, 97, 98, 108, 101, 83, 116, 109, 116, 32, 47, 32, 65, 108, 116, 101, 114, 86, 105, 101, 119, 83, 116, 109, 116, 32, 47, 32, 65, 108, 116, 101, 114, 83, 101, 113, 117, 101, 110, 99, 101, 83, 116, 109, 116, 32, 47, 32, 65, 108, 116, 101, 114, 68, 97, 116, 97, 98, 97, 115, 101, 83, 116, 109, 116, 32, 47, 32, 65, 108, 116, 101, 114, 83, 99, 104, 101, 109, 97, 83, 116, 109, 116, 10, + 65, 108, 116, 101, 114, 84, 97, 98, 108, 101, 83, 116, 109, 116, 32, 60, 45, 32, 39, 84, 65, 66, 76, 69, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 76, 105, 115, 116, 40, 65, 108, 116, 101, 114, 84, 97, 98, 108, 101, 79, 112, 116, 105, 111, 110, 115, 41, 10, + 65, 108, 116, 101, 114, 83, 99, 104, 101, 109, 97, 83, 116, 109, 116, 32, 60, 45, 32, 39, 83, 67, 72, 69, 77, 65, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 32, 65, 108, 116, 101, 114, 83, 99, 104, 101, 109, 97, 79, 112, 116, 105, 111, 110, 115, 10, + 65, 108, 116, 101, 114, 83, 99, 104, 101, 109, 97, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 82, 101, 110, 97, 109, 101, 65, 108, 116, 101, 114, 32, 47, 32, 83, 101, 116, 79, 112, 116, 105, 111, 110, 115, 32, 47, 32, 82, 101, 115, 101, 116, 79, 112, 116, 105, 111, 110, 115, 10, + 65, 108, 116, 101, 114, 84, 97, 98, 108, 101, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 65, 100, 100, 67, 111, 108, 117, 109, 110, 32, 47, 32, 68, 114, 111, 112, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 47, 32, 68, 114, 111, 112, 67, 111, 108, 117, 109, 110, 32, 47, 32, 65, 108, 116, 101, 114, 67, 111, 108, 117, 109, 110, 32, 47, 32, 65, 100, 100, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 32, 32, 32, 32, 82, 101, 110, 97, 109, 101, 67, 111, 108, 117, 109, 110, 32, 47, 32, 82, 101, 110, 97, 109, 101, 65, 108, 116, 101, 114, 32, 47, 32, 83, 101, 116, 80, 97, 114, 116, 105, 116, 105, 111, 110, 101, 100, 66, 121, 32, 47, 32, 82, 101, 115, 101, 116, 80, 97, 114, 116, 105, 116, 105, 111, 110, 101, 100, 66, 121, 32, 47, 32, 83, 101, 116, 83, 111, 114, 116, 101, 100, 66, 121, 32, 47, 32, 82, 101, 115, 101, 116, 83, 111, 114, 116, 101, 100, 66, 121, 32, 47, 32, 83, 101, 116, 79, 112, 116, 105, 111, 110, 115, 32, 47, 32, 82, 101, 115, 101, 116, 79, 112, 116, 105, 111, 110, 115, 10, + 65, 100, 100, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 65, 68, 68, 39, 32, 84, 111, 112, 76, 101, 118, 101, 108, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 10, + 68, 114, 111, 112, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 68, 82, 79, 80, 39, 32, 39, 67, 79, 78, 83, 84, 82, 65, 73, 78, 84, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 78, 97, 109, 101, 32, 68, 114, 111, 112, 66, 101, 104, 97, 118, 105, 111, 114, 63, 10, + 65, 100, 100, 67, 111, 108, 117, 109, 110, 32, 60, 45, 32, 39, 65, 68, 68, 39, 32, 39, 67, 79, 76, 85, 77, 78, 39, 63, 32, 73, 102, 78, 111, 116, 69, 120, 105, 115, 116, 115, 63, 32, 65, 100, 100, 67, 111, 108, 117, 109, 110, 69, 110, 116, 114, 121, 10, + 65, 100, 100, 67, 111, 108, 117, 109, 110, 69, 110, 116, 114, 121, 32, 60, 45, 32, 68, 111, 116, 116, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 84, 121, 112, 101, 63, 32, 71, 101, 110, 101, 114, 97, 116, 101, 100, 67, 111, 108, 117, 109, 110, 63, 32, 67, 111, 108, 117, 109, 110, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 42, 10, + 68, 114, 111, 112, 67, 111, 108, 117, 109, 110, 32, 60, 45, 32, 39, 68, 82, 79, 80, 39, 32, 39, 67, 79, 76, 85, 77, 78, 39, 63, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 78, 101, 115, 116, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 68, 114, 111, 112, 66, 101, 104, 97, 118, 105, 111, 114, 63, 10, + 65, 108, 116, 101, 114, 67, 111, 108, 117, 109, 110, 32, 60, 45, 32, 39, 65, 76, 84, 69, 82, 39, 32, 39, 67, 79, 76, 85, 77, 78, 39, 63, 32, 78, 101, 115, 116, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 65, 108, 116, 101, 114, 67, 111, 108, 117, 109, 110, 69, 110, 116, 114, 121, 10, + 82, 101, 110, 97, 109, 101, 67, 111, 108, 117, 109, 110, 32, 60, 45, 32, 39, 82, 69, 78, 65, 77, 69, 39, 32, 39, 67, 79, 76, 85, 77, 78, 39, 63, 32, 78, 101, 115, 116, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 39, 84, 79, 39, 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 10, + 78, 101, 115, 116, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 68, 111, 116, 42, 32, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 10, + 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 68, 111, 116, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 39, 46, 39, 10, + 82, 101, 110, 97, 109, 101, 65, 108, 116, 101, 114, 32, 60, 45, 32, 39, 82, 69, 78, 65, 77, 69, 39, 32, 39, 84, 79, 39, 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 10, + 83, 101, 116, 80, 97, 114, 116, 105, 116, 105, 111, 110, 101, 100, 66, 121, 32, 60, 45, 32, 39, 83, 69, 84, 39, 32, 39, 80, 65, 82, 84, 73, 84, 73, 79, 78, 69, 68, 39, 32, 39, 66, 89, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 41, 10, + 82, 101, 115, 101, 116, 80, 97, 114, 116, 105, 116, 105, 111, 110, 101, 100, 66, 121, 32, 60, 45, 32, 39, 82, 69, 83, 69, 84, 39, 32, 39, 80, 65, 82, 84, 73, 84, 73, 79, 78, 69, 68, 39, 32, 39, 66, 89, 39, 10, + 83, 101, 116, 83, 111, 114, 116, 101, 100, 66, 121, 32, 60, 45, 32, 39, 83, 69, 84, 39, 32, 39, 83, 79, 82, 84, 69, 68, 39, 32, 39, 66, 89, 39, 32, 80, 97, 114, 101, 110, 115, 40, 79, 114, 100, 101, 114, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 115, 41, 10, + 82, 101, 115, 101, 116, 83, 111, 114, 116, 101, 100, 66, 121, 32, 60, 45, 32, 39, 82, 69, 83, 69, 84, 39, 32, 39, 83, 79, 82, 84, 69, 68, 39, 32, 39, 66, 89, 39, 10, + 83, 101, 116, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 39, 83, 69, 84, 39, 32, 82, 101, 108, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 10, + 82, 101, 115, 101, 116, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 39, 82, 69, 83, 69, 84, 39, 32, 82, 101, 108, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 10, + 65, 108, 116, 101, 114, 67, 111, 108, 117, 109, 110, 69, 110, 116, 114, 121, 32, 60, 45, 32, 65, 100, 100, 79, 114, 68, 114, 111, 112, 68, 101, 102, 97, 117, 108, 116, 32, 47, 32, 67, 104, 97, 110, 103, 101, 78, 117, 108, 108, 97, 98, 105, 108, 105, 116, 121, 32, 47, 32, 65, 108, 116, 101, 114, 84, 121, 112, 101, 10, + 65, 100, 100, 79, 114, 68, 114, 111, 112, 68, 101, 102, 97, 117, 108, 116, 32, 60, 45, 32, 65, 100, 100, 68, 101, 102, 97, 117, 108, 116, 32, 47, 32, 68, 114, 111, 112, 68, 101, 102, 97, 117, 108, 116, 10, + 65, 100, 100, 68, 101, 102, 97, 117, 108, 116, 32, 60, 45, 32, 39, 83, 69, 84, 39, 32, 39, 68, 69, 70, 65, 85, 76, 84, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 68, 114, 111, 112, 68, 101, 102, 97, 117, 108, 116, 32, 60, 45, 32, 39, 68, 82, 79, 80, 39, 32, 39, 68, 69, 70, 65, 85, 76, 84, 39, 10, + 67, 104, 97, 110, 103, 101, 78, 117, 108, 108, 97, 98, 105, 108, 105, 116, 121, 32, 60, 45, 32, 68, 114, 111, 112, 79, 114, 83, 101, 116, 32, 39, 78, 79, 84, 39, 32, 39, 78, 85, 76, 76, 39, 10, + 68, 114, 111, 112, 79, 114, 83, 101, 116, 32, 60, 45, 32, 68, 114, 111, 112, 78, 117, 108, 108, 97, 98, 105, 108, 105, 116, 121, 32, 47, 32, 83, 101, 116, 78, 117, 108, 108, 97, 98, 105, 108, 105, 116, 121, 10, + 68, 114, 111, 112, 78, 117, 108, 108, 97, 98, 105, 108, 105, 116, 121, 32, 60, 45, 32, 39, 68, 82, 79, 80, 39, 10, + 83, 101, 116, 78, 117, 108, 108, 97, 98, 105, 108, 105, 116, 121, 32, 60, 45, 32, 39, 83, 69, 84, 39, 10, + 65, 108, 116, 101, 114, 84, 121, 112, 101, 32, 60, 45, 32, 83, 101, 116, 68, 97, 116, 97, 63, 32, 39, 84, 89, 80, 69, 39, 32, 84, 121, 112, 101, 63, 32, 67, 111, 108, 117, 109, 110, 67, 111, 108, 108, 97, 116, 105, 111, 110, 63, 32, 85, 115, 105, 110, 103, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 10, + 83, 101, 116, 68, 97, 116, 97, 32, 60, 45, 32, 39, 83, 69, 84, 39, 32, 39, 68, 65, 84, 65, 39, 63, 10, + 85, 115, 105, 110, 103, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 85, 83, 73, 78, 71, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 65, 108, 116, 101, 114, 86, 105, 101, 119, 83, 116, 109, 116, 32, 60, 45, 32, 39, 86, 73, 69, 87, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 82, 101, 110, 97, 109, 101, 65, 108, 116, 101, 114, 10, + 65, 108, 116, 101, 114, 83, 101, 113, 117, 101, 110, 99, 101, 83, 116, 109, 116, 32, 60, 45, 32, 39, 83, 69, 81, 85, 69, 78, 67, 69, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 83, 101, 113, 117, 101, 110, 99, 101, 78, 97, 109, 101, 32, 65, 108, 116, 101, 114, 83, 101, 113, 117, 101, 110, 99, 101, 79, 112, 116, 105, 111, 110, 115, 10, + 81, 117, 97, 108, 105, 102, 105, 101, 100, 83, 101, 113, 117, 101, 110, 99, 101, 78, 97, 109, 101, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 63, 32, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 63, 32, 83, 101, 113, 117, 101, 110, 99, 101, 78, 97, 109, 101, 10, + 65, 108, 116, 101, 114, 83, 101, 113, 117, 101, 110, 99, 101, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 82, 101, 110, 97, 109, 101, 65, 108, 116, 101, 114, 83, 101, 113, 117, 101, 110, 99, 101, 79, 112, 116, 105, 111, 110, 115, 32, 47, 32, 83, 101, 116, 83, 101, 113, 117, 101, 110, 99, 101, 79, 112, 116, 105, 111, 110, 10, + 82, 101, 110, 97, 109, 101, 65, 108, 116, 101, 114, 83, 101, 113, 117, 101, 110, 99, 101, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 82, 101, 110, 97, 109, 101, 65, 108, 116, 101, 114, 10, + 83, 101, 116, 83, 101, 113, 117, 101, 110, 99, 101, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 83, 101, 113, 117, 101, 110, 99, 101, 79, 112, 116, 105, 111, 110, 43, 10, + 65, 108, 116, 101, 114, 68, 97, 116, 97, 98, 97, 115, 101, 83, 116, 109, 116, 32, 60, 45, 32, 39, 68, 65, 84, 65, 66, 65, 83, 69, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 39, 83, 69, 84, 39, 32, 39, 65, 76, 73, 65, 83, 39, 32, 39, 84, 79, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 65, 110, 97, 108, 121, 122, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 65, 110, 97, 108, 121, 122, 101, 75, 101, 121, 119, 111, 114, 100, 32, 65, 110, 97, 108, 121, 122, 101, 86, 101, 114, 98, 111, 115, 101, 63, 32, 65, 110, 97, 108, 121, 122, 101, 84, 97, 114, 103, 101, 116, 63, 10, + 65, 110, 97, 108, 121, 122, 101, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 78, 97, 109, 101, 76, 105, 115, 116, 63, 10, + 65, 110, 97, 108, 121, 122, 101, 86, 101, 114, 98, 111, 115, 101, 32, 60, 45, 32, 39, 86, 69, 82, 66, 79, 83, 69, 39, 10, + 65, 116, 116, 97, 99, 104, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 65, 84, 84, 65, 67, 72, 39, 32, 79, 114, 82, 101, 112, 108, 97, 99, 101, 63, 32, 73, 102, 78, 111, 116, 69, 120, 105, 115, 116, 115, 63, 32, 68, 97, 116, 97, 98, 97, 115, 101, 63, 32, 68, 97, 116, 97, 98, 97, 115, 101, 80, 97, 116, 104, 32, 65, 116, 116, 97, 99, 104, 65, 108, 105, 97, 115, 63, 32, 65, 116, 116, 97, 99, 104, 79, 112, 116, 105, 111, 110, 115, 63, 10, + 68, 97, 116, 97, 98, 97, 115, 101, 32, 60, 45, 32, 39, 68, 65, 84, 65, 66, 65, 83, 69, 39, 10, + 68, 97, 116, 97, 98, 97, 115, 101, 80, 97, 116, 104, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 65, 116, 116, 97, 99, 104, 65, 108, 105, 97, 115, 32, 60, 45, 32, 39, 65, 83, 39, 32, 67, 111, 108, 73, 100, 10, + 65, 116, 116, 97, 99, 104, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 10, + 67, 97, 108, 108, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 67, 65, 76, 76, 39, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 10, + 67, 104, 101, 99, 107, 112, 111, 105, 110, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 67, 104, 101, 99, 107, 112, 111, 105, 110, 116, 70, 111, 114, 99, 101, 63, 32, 39, 67, 72, 69, 67, 75, 80, 79, 73, 78, 84, 39, 32, 67, 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, 63, 10, + 67, 104, 101, 99, 107, 112, 111, 105, 110, 116, 70, 111, 114, 99, 101, 32, 60, 45, 32, 39, 70, 79, 82, 67, 69, 39, 10, + 67, 111, 109, 109, 101, 110, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 67, 79, 77, 77, 69, 78, 84, 39, 32, 39, 79, 78, 39, 32, 67, 111, 109, 109, 101, 110, 116, 79, 110, 84, 121, 112, 101, 32, 67, 111, 109, 109, 101, 110, 116, 84, 97, 114, 103, 101, 116, 32, 39, 73, 83, 39, 32, 67, 111, 109, 109, 101, 110, 116, 86, 97, 108, 117, 101, 10, + 67, 111, 109, 109, 101, 110, 116, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 68, 111, 116, 116, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 47, 32, 67, 111, 109, 109, 101, 110, 116, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 67, 111, 109, 109, 101, 110, 116, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 67, 111, 109, 109, 101, 110, 116, 79, 110, 84, 121, 112, 101, 32, 60, 45, 32, 32, 32, 32, 67, 111, 109, 109, 101, 110, 116, 84, 97, 98, 108, 101, 32, 47, 32, 67, 111, 109, 109, 101, 110, 116, 83, 101, 113, 117, 101, 110, 99, 101, 32, 47, 32, 67, 111, 109, 109, 101, 110, 116, 70, 117, 110, 99, 116, 105, 111, 110, 32, 47, 32, 67, 111, 109, 109, 101, 110, 116, 77, 97, 99, 114, 111, 84, 97, 98, 108, 101, 32, 47, 32, 67, 111, 109, 109, 101, 110, 116, 77, 97, 99, 114, 111, 32, 47, 10, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 67, 111, 109, 109, 101, 110, 116, 86, 105, 101, 119, 32, 47, 32, 67, 111, 109, 109, 101, 110, 116, 68, 97, 116, 97, 98, 97, 115, 101, 32, 47, 32, 67, 111, 109, 109, 101, 110, 116, 73, 110, 100, 101, 120, 32, 47, 32, 67, 111, 109, 109, 101, 110, 116, 83, 99, 104, 101, 109, 97, 32, 47, 32, 67, 111, 109, 109, 101, 110, 116, 84, 121, 112, 101, 32, 47, 32, 67, 111, 109, 109, 101, 110, 116, 67, 111, 108, 117, 109, 110, 10, + 67, 111, 109, 109, 101, 110, 116, 84, 97, 98, 108, 101, 32, 60, 45, 32, 39, 84, 65, 66, 76, 69, 39, 10, + 67, 111, 109, 109, 101, 110, 116, 83, 101, 113, 117, 101, 110, 99, 101, 32, 60, 45, 32, 39, 83, 69, 81, 85, 69, 78, 67, 69, 39, 10, + 67, 111, 109, 109, 101, 110, 116, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 70, 85, 78, 67, 84, 73, 79, 78, 39, 10, + 67, 111, 109, 109, 101, 110, 116, 77, 97, 99, 114, 111, 84, 97, 98, 108, 101, 32, 60, 45, 32, 39, 77, 65, 67, 82, 79, 39, 32, 39, 84, 65, 66, 76, 69, 39, 10, + 67, 111, 109, 109, 101, 110, 116, 77, 97, 99, 114, 111, 32, 60, 45, 32, 39, 77, 65, 67, 82, 79, 39, 10, + 67, 111, 109, 109, 101, 110, 116, 86, 105, 101, 119, 32, 60, 45, 32, 39, 86, 73, 69, 87, 39, 10, + 67, 111, 109, 109, 101, 110, 116, 68, 97, 116, 97, 98, 97, 115, 101, 32, 60, 45, 32, 39, 68, 65, 84, 65, 66, 65, 83, 69, 39, 10, + 67, 111, 109, 109, 101, 110, 116, 73, 110, 100, 101, 120, 32, 60, 45, 32, 39, 73, 78, 68, 69, 88, 39, 10, + 67, 111, 109, 109, 101, 110, 116, 83, 99, 104, 101, 109, 97, 32, 60, 45, 32, 39, 83, 67, 72, 69, 77, 65, 39, 10, + 67, 111, 109, 109, 101, 110, 116, 84, 121, 112, 101, 32, 60, 45, 32, 39, 84, 89, 80, 69, 39, 10, + 67, 111, 109, 109, 101, 110, 116, 67, 111, 108, 117, 109, 110, 32, 60, 45, 32, 39, 67, 79, 76, 85, 77, 78, 39, 10, + 67, 111, 109, 109, 101, 110, 116, 86, 97, 108, 117, 101, 32, 60, 45, 32, 78, 117, 108, 108, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 86, 97, 108, 117, 101, 10, + 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 86, 97, 108, 117, 101, 32, 60, 45, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 67, 111, 110, 110, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 67, 79, 78, 78, 69, 67, 84, 39, 32, 83, 101, 115, 115, 105, 111, 110, 84, 97, 114, 103, 101, 116, 63, 10, + 68, 105, 115, 99, 111, 110, 110, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 68, 73, 83, 67, 79, 78, 78, 69, 67, 84, 39, 10, + 83, 101, 115, 115, 105, 111, 110, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 76, 111, 99, 97, 108, 83, 101, 115, 115, 105, 111, 110, 84, 97, 114, 103, 101, 116, 32, 47, 32, 83, 116, 114, 105, 110, 103, 83, 101, 115, 115, 105, 111, 110, 84, 97, 114, 103, 101, 116, 32, 47, 32, 67, 97, 116, 97, 108, 111, 103, 83, 101, 115, 115, 105, 111, 110, 84, 97, 114, 103, 101, 116, 10, + 76, 111, 99, 97, 108, 83, 101, 115, 115, 105, 111, 110, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 39, 76, 79, 67, 65, 76, 39, 10, + 83, 116, 114, 105, 110, 103, 83, 101, 115, 115, 105, 111, 110, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 63, 10, + 67, 97, 116, 97, 108, 111, 103, 83, 101, 115, 115, 105, 111, 110, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, 10, + 67, 111, 112, 121, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 67, 79, 80, 89, 39, 32, 67, 111, 112, 121, 86, 97, 114, 105, 97, 116, 105, 111, 110, 115, 10, + 67, 111, 112, 121, 86, 97, 114, 105, 97, 116, 105, 111, 110, 115, 32, 60, 45, 32, 67, 111, 112, 121, 84, 97, 98, 108, 101, 32, 47, 32, 67, 111, 112, 121, 83, 101, 108, 101, 99, 116, 32, 47, 32, 67, 111, 112, 121, 70, 114, 111, 109, 68, 97, 116, 97, 98, 97, 115, 101, 10, + 67, 111, 112, 121, 84, 97, 98, 108, 101, 32, 60, 45, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 73, 110, 115, 101, 114, 116, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 63, 32, 70, 114, 111, 109, 79, 114, 84, 111, 32, 67, 111, 112, 121, 70, 105, 108, 101, 78, 97, 109, 101, 32, 67, 111, 112, 121, 76, 101, 103, 97, 99, 121, 79, 112, 116, 105, 111, 110, 63, 32, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 115, 63, 10, + 70, 114, 111, 109, 79, 114, 84, 111, 32, 60, 45, 32, 67, 111, 112, 121, 70, 114, 111, 109, 32, 47, 32, 67, 111, 112, 121, 84, 111, 10, + 67, 111, 112, 121, 70, 114, 111, 109, 32, 60, 45, 32, 39, 70, 82, 79, 77, 39, 10, + 67, 111, 112, 121, 84, 111, 32, 60, 45, 32, 39, 84, 79, 39, 10, + 67, 111, 112, 121, 83, 101, 108, 101, 99, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 41, 32, 39, 84, 79, 39, 32, 67, 111, 112, 121, 70, 105, 108, 101, 78, 97, 109, 101, 32, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 115, 63, 10, + 67, 111, 112, 121, 70, 105, 108, 101, 78, 97, 109, 101, 32, 60, 45, 32, 67, 111, 112, 121, 70, 105, 108, 101, 78, 97, 109, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 67, 111, 112, 121, 70, 105, 108, 101, 78, 97, 109, 101, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 67, 111, 112, 121, 70, 105, 108, 101, 78, 97, 109, 101, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 67, 111, 108, 73, 100, 32, 47, 32, 67, 111, 112, 121, 70, 105, 108, 101, 78, 97, 109, 101, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 67, 111, 112, 121, 70, 105, 108, 101, 78, 97, 109, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 47, 32, 80, 97, 114, 101, 110, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 67, 111, 112, 121, 70, 105, 108, 101, 78, 97, 109, 101, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 67, 111, 112, 121, 70, 105, 108, 101, 78, 97, 109, 101, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 67, 111, 112, 121, 70, 105, 108, 101, 78, 97, 109, 101, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 67, 111, 108, 73, 100, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 67, 111, 108, 73, 100, 10, + 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 67, 111, 108, 73, 100, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 67, 111, 112, 121, 70, 105, 108, 101, 78, 97, 109, 101, 83, 117, 102, 102, 105, 120, 43, 10, + 67, 111, 112, 121, 70, 105, 108, 101, 78, 97, 109, 101, 83, 117, 102, 102, 105, 120, 32, 60, 45, 32, 39, 46, 39, 32, 67, 111, 108, 73, 100, 10, + 67, 111, 112, 121, 76, 101, 103, 97, 99, 121, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 39, 85, 83, 73, 78, 71, 39, 63, 32, 39, 68, 69, 76, 73, 77, 73, 84, 69, 82, 83, 39, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 39, 87, 73, 84, 72, 39, 63, 32, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 10, + 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 67, 111, 112, 121, 71, 101, 110, 101, 114, 105, 99, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 32, 47, 32, 83, 112, 101, 99, 105, 97, 108, 105, 122, 101, 100, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 10, + 83, 112, 101, 99, 105, 97, 108, 105, 122, 101, 100, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 83, 112, 101, 99, 105, 97, 108, 105, 122, 101, 100, 79, 112, 116, 105, 111, 110, 32, 83, 112, 101, 99, 105, 97, 108, 105, 122, 101, 100, 79, 112, 116, 105, 111, 110, 84, 97, 105, 108, 42, 10, + 83, 112, 101, 99, 105, 97, 108, 105, 122, 101, 100, 79, 112, 116, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 39, 44, 39, 63, 32, 83, 112, 101, 99, 105, 97, 108, 105, 122, 101, 100, 79, 112, 116, 105, 111, 110, 10, + 83, 112, 101, 99, 105, 97, 108, 105, 122, 101, 100, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 83, 105, 110, 103, 108, 101, 79, 112, 116, 105, 111, 110, 32, 47, 32, 78, 117, 108, 108, 65, 115, 79, 112, 116, 105, 111, 110, 32, 47, 32, 68, 101, 108, 105, 109, 105, 116, 101, 114, 65, 115, 79, 112, 116, 105, 111, 110, 32, 47, 32, 69, 115, 99, 97, 112, 101, 65, 115, 79, 112, 116, 105, 111, 110, 32, 47, 10, + 9, 69, 110, 99, 111, 100, 105, 110, 103, 79, 112, 116, 105, 111, 110, 32, 47, 32, 81, 117, 111, 116, 101, 65, 115, 79, 112, 116, 105, 111, 110, 32, 47, 32, 70, 111, 114, 99, 101, 81, 117, 111, 116, 101, 79, 112, 116, 105, 111, 110, 32, 47, 32, 80, 97, 114, 116, 105, 116, 105, 111, 110, 66, 121, 79, 112, 116, 105, 111, 110, 32, 47, 32, 70, 111, 114, 99, 101, 78, 117, 108, 108, 79, 112, 116, 105, 111, 110, 10, + 83, 105, 110, 103, 108, 101, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 66, 105, 110, 97, 114, 121, 79, 112, 116, 105, 111, 110, 32, 47, 32, 70, 114, 101, 101, 122, 101, 79, 112, 116, 105, 111, 110, 32, 47, 32, 79, 105, 100, 115, 79, 112, 116, 105, 111, 110, 32, 47, 32, 67, 115, 118, 79, 112, 116, 105, 111, 110, 32, 47, 32, 72, 101, 97, 100, 101, 114, 79, 112, 116, 105, 111, 110, 10, + 66, 105, 110, 97, 114, 121, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 39, 66, 73, 78, 65, 82, 89, 39, 10, + 70, 114, 101, 101, 122, 101, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 39, 70, 82, 69, 69, 90, 69, 39, 10, + 79, 105, 100, 115, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 39, 79, 73, 68, 83, 39, 10, + 67, 115, 118, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 39, 67, 83, 86, 39, 10, + 72, 101, 97, 100, 101, 114, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 39, 72, 69, 65, 68, 69, 82, 39, 10, + 78, 117, 108, 108, 65, 115, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 39, 78, 85, 76, 76, 39, 32, 39, 65, 83, 39, 63, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 68, 101, 108, 105, 109, 105, 116, 101, 114, 65, 115, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 39, 68, 69, 76, 73, 77, 73, 84, 69, 82, 39, 32, 39, 65, 83, 39, 63, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 81, 117, 111, 116, 101, 65, 115, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 39, 81, 85, 79, 84, 69, 39, 32, 39, 65, 83, 39, 63, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 69, 115, 99, 97, 112, 101, 65, 115, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 39, 69, 83, 67, 65, 80, 69, 39, 32, 39, 65, 83, 39, 63, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 69, 110, 99, 111, 100, 105, 110, 103, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 39, 69, 78, 67, 79, 68, 73, 78, 71, 39, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 70, 111, 114, 99, 101, 81, 117, 111, 116, 101, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 70, 111, 114, 99, 101, 81, 117, 111, 116, 101, 63, 32, 39, 81, 85, 79, 84, 69, 39, 32, 83, 116, 97, 114, 83, 121, 109, 98, 111, 108, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 10, + 83, 116, 97, 114, 83, 121, 109, 98, 111, 108, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, 83, 116, 97, 114, 83, 121, 109, 98, 111, 108, 32, 47, 32, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 10, + 70, 111, 114, 99, 101, 81, 117, 111, 116, 101, 32, 60, 45, 32, 39, 70, 79, 82, 67, 69, 39, 10, + 80, 97, 114, 116, 105, 116, 105, 111, 110, 66, 121, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 40, 39, 80, 65, 82, 84, 73, 84, 73, 79, 78, 39, 32, 47, 32, 39, 80, 65, 82, 84, 73, 84, 73, 79, 78, 69, 68, 39, 41, 32, 39, 66, 89, 39, 32, 80, 97, 114, 116, 105, 116, 105, 111, 110, 66, 121, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 10, + 80, 97, 114, 116, 105, 116, 105, 111, 110, 66, 121, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, 83, 116, 97, 114, 80, 97, 114, 116, 105, 116, 105, 111, 110, 66, 121, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, 47, 32, 80, 97, 114, 101, 110, 116, 104, 101, 115, 105, 122, 101, 100, 80, 97, 114, 116, 105, 116, 105, 111, 110, 66, 121, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, 47, 32, 83, 105, 110, 103, 108, 101, 80, 97, 114, 116, 105, 116, 105, 111, 110, 66, 121, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 10, + 83, 116, 97, 114, 80, 97, 114, 116, 105, 116, 105, 111, 110, 66, 121, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, 83, 116, 97, 114, 83, 121, 109, 98, 111, 108, 10, + 80, 97, 114, 101, 110, 116, 104, 101, 115, 105, 122, 101, 100, 80, 97, 114, 116, 105, 116, 105, 111, 110, 66, 121, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 41, 10, + 83, 105, 110, 103, 108, 101, 80, 97, 114, 116, 105, 116, 105, 111, 110, 66, 121, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, 67, 111, 108, 73, 100, 10, + 70, 111, 114, 99, 101, 78, 117, 108, 108, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 39, 70, 79, 82, 67, 69, 39, 32, 70, 111, 114, 99, 101, 78, 111, 116, 78, 117, 108, 108, 63, 32, 39, 78, 85, 76, 76, 39, 32, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 10, + 70, 111, 114, 99, 101, 78, 111, 116, 78, 117, 108, 108, 32, 60, 45, 32, 39, 78, 79, 84, 39, 10, + 83, 116, 97, 114, 83, 121, 109, 98, 111, 108, 32, 60, 45, 32, 39, 42, 39, 10, + 67, 111, 112, 121, 71, 101, 110, 101, 114, 105, 99, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 67, 111, 112, 121, 71, 101, 110, 101, 114, 105, 99, 79, 112, 116, 105, 111, 110, 41, 41, 10, + 67, 111, 112, 121, 71, 101, 110, 101, 114, 105, 99, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 79, 114, 100, 101, 114, 66, 121, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 32, 47, 32, 80, 97, 114, 116, 105, 116, 105, 111, 110, 101, 100, 66, 121, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 32, 47, 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 10, + 79, 114, 100, 101, 114, 66, 121, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 39, 79, 82, 68, 69, 82, 39, 32, 39, 66, 89, 39, 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 86, 97, 108, 117, 101, 63, 10, + 80, 97, 114, 116, 105, 116, 105, 111, 110, 101, 100, 66, 121, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 40, 39, 80, 65, 82, 84, 73, 84, 73, 79, 78, 39, 32, 47, 32, 39, 80, 65, 82, 84, 73, 84, 73, 79, 78, 69, 68, 39, 41, 32, 39, 66, 89, 39, 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 86, 97, 108, 117, 101, 63, 10, + 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 41, 41, 10, + 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 78, 97, 109, 101, 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 86, 97, 108, 117, 101, 63, 10, + 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 86, 97, 108, 117, 101, 32, 60, 45, 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 79, 114, 100, 101, 114, 76, 105, 115, 116, 32, 47, 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 79, 114, 100, 101, 114, 76, 105, 115, 116, 32, 60, 45, 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 80, 97, 114, 101, 110, 116, 104, 101, 115, 105, 122, 101, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 76, 105, 115, 116, 10, + 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 80, 97, 114, 101, 110, 116, 104, 101, 115, 105, 122, 101, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 79, 114, 100, 101, 114, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 76, 105, 115, 116, 41, 10, + 67, 111, 112, 121, 70, 114, 111, 109, 68, 97, 116, 97, 98, 97, 115, 101, 32, 60, 45, 32, 67, 111, 112, 121, 70, 114, 111, 109, 68, 97, 116, 97, 98, 97, 115, 101, 87, 105, 116, 104, 70, 108, 97, 103, 32, 47, 32, 67, 111, 112, 121, 70, 114, 111, 109, 68, 97, 116, 97, 98, 97, 115, 101, 87, 105, 116, 104, 111, 117, 116, 70, 108, 97, 103, 10, + 67, 111, 112, 121, 70, 114, 111, 109, 68, 97, 116, 97, 98, 97, 115, 101, 87, 105, 116, 104, 70, 108, 97, 103, 32, 60, 45, 32, 39, 70, 82, 79, 77, 39, 32, 39, 68, 65, 84, 65, 66, 65, 83, 69, 39, 32, 67, 111, 108, 73, 100, 32, 39, 84, 79, 39, 32, 67, 111, 108, 73, 100, 32, 67, 111, 112, 121, 68, 97, 116, 97, 98, 97, 115, 101, 70, 108, 97, 103, 10, + 67, 111, 112, 121, 70, 114, 111, 109, 68, 97, 116, 97, 98, 97, 115, 101, 87, 105, 116, 104, 111, 117, 116, 70, 108, 97, 103, 32, 60, 45, 32, 39, 70, 82, 79, 77, 39, 32, 39, 68, 65, 84, 65, 66, 65, 83, 69, 39, 32, 67, 111, 108, 73, 100, 32, 39, 84, 79, 39, 32, 67, 111, 108, 73, 100, 10, + 67, 111, 112, 121, 68, 97, 116, 97, 98, 97, 115, 101, 70, 108, 97, 103, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 83, 99, 104, 101, 109, 97, 79, 114, 68, 97, 116, 97, 41, 10, + 83, 99, 104, 101, 109, 97, 79, 114, 68, 97, 116, 97, 32, 60, 45, 32, 67, 111, 112, 121, 83, 99, 104, 101, 109, 97, 32, 47, 32, 67, 111, 112, 121, 68, 97, 116, 97, 10, + 67, 111, 112, 121, 83, 99, 104, 101, 109, 97, 32, 60, 45, 32, 39, 83, 67, 72, 69, 77, 65, 39, 10, + 67, 111, 112, 121, 68, 97, 116, 97, 32, 60, 45, 32, 39, 68, 65, 84, 65, 39, 10, + 67, 114, 101, 97, 116, 101, 73, 110, 100, 101, 120, 83, 116, 109, 116, 32, 60, 45, 32, 85, 110, 105, 113, 117, 101, 73, 110, 100, 101, 120, 63, 32, 39, 73, 78, 68, 69, 88, 39, 32, 73, 102, 78, 111, 116, 69, 120, 105, 115, 116, 115, 63, 32, 73, 110, 100, 101, 120, 78, 97, 109, 101, 63, 32, 39, 79, 78, 39, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 73, 110, 100, 101, 120, 84, 121, 112, 101, 63, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 73, 110, 100, 101, 120, 69, 108, 101, 109, 101, 110, 116, 41, 41, 32, 87, 105, 116, 104, 76, 105, 115, 116, 63, 32, 87, 104, 101, 114, 101, 67, 108, 97, 117, 115, 101, 63, 10, + 87, 105, 116, 104, 76, 105, 115, 116, 32, 60, 45, 32, 39, 87, 73, 84, 72, 39, 32, 82, 101, 108, 79, 112, 116, 105, 111, 110, 79, 114, 79, 105, 100, 115, 10, + 82, 101, 108, 79, 112, 116, 105, 111, 110, 79, 114, 79, 105, 100, 115, 32, 60, 45, 32, 82, 101, 108, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 32, 47, 32, 79, 105, 100, 115, 10, + 82, 101, 108, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 82, 101, 108, 79, 112, 116, 105, 111, 110, 41, 41, 10, + 79, 105, 100, 115, 32, 60, 45, 32, 87, 105, 116, 104, 79, 114, 87, 105, 116, 104, 111, 117, 116, 79, 105, 100, 115, 32, 39, 79, 73, 68, 83, 39, 10, + 87, 105, 116, 104, 79, 114, 87, 105, 116, 104, 111, 117, 116, 79, 105, 100, 115, 32, 60, 45, 32, 87, 105, 116, 104, 79, 105, 100, 115, 32, 47, 32, 87, 105, 116, 104, 111, 117, 116, 79, 105, 100, 115, 10, + 87, 105, 116, 104, 79, 105, 100, 115, 32, 60, 45, 32, 39, 87, 73, 84, 72, 39, 10, + 87, 105, 116, 104, 111, 117, 116, 79, 105, 100, 115, 32, 60, 45, 32, 39, 87, 73, 84, 72, 79, 85, 84, 39, 10, + 73, 110, 100, 101, 120, 69, 108, 101, 109, 101, 110, 116, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 68, 101, 115, 99, 79, 114, 65, 115, 99, 63, 32, 78, 117, 108, 108, 115, 70, 105, 114, 115, 116, 79, 114, 76, 97, 115, 116, 63, 10, + 85, 110, 105, 113, 117, 101, 73, 110, 100, 101, 120, 32, 60, 45, 32, 39, 85, 78, 73, 81, 85, 69, 39, 10, + 73, 110, 100, 101, 120, 84, 121, 112, 101, 32, 60, 45, 32, 39, 85, 83, 73, 78, 71, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 82, 101, 108, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 82, 101, 108, 79, 112, 116, 105, 111, 110, 78, 97, 109, 101, 32, 82, 101, 108, 79, 112, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 79, 112, 116, 63, 10, + 82, 101, 108, 79, 112, 116, 105, 111, 110, 78, 97, 109, 101, 32, 60, 45, 32, 68, 111, 116, 116, 101, 100, 67, 111, 108, 76, 97, 98, 101, 108, 32, 47, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 68, 111, 116, 116, 101, 100, 67, 111, 108, 76, 97, 98, 101, 108, 32, 60, 45, 32, 67, 111, 108, 76, 97, 98, 101, 108, 32, 68, 111, 116, 67, 111, 108, 76, 97, 98, 101, 108, 42, 10, + 82, 101, 108, 79, 112, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 79, 112, 116, 32, 60, 45, 32, 39, 61, 39, 32, 68, 101, 102, 65, 114, 103, 10, + 68, 101, 102, 65, 114, 103, 32, 60, 45, 32, 68, 101, 102, 65, 114, 103, 78, 117, 108, 108, 32, 47, 32, 68, 101, 102, 65, 114, 103, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 78, 111, 110, 101, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 68, 101, 102, 65, 114, 103, 75, 101, 121, 119, 111, 114, 100, 10, + 68, 101, 102, 65, 114, 103, 78, 117, 108, 108, 32, 60, 45, 32, 78, 117, 108, 108, 76, 105, 116, 101, 114, 97, 108, 10, + 68, 101, 102, 65, 114, 103, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 82, 101, 115, 101, 114, 118, 101, 100, 75, 101, 121, 119, 111, 114, 100, 10, + 68, 101, 102, 65, 114, 103, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 78, 111, 110, 101, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 39, 78, 79, 78, 69, 39, 10, + 67, 114, 101, 97, 116, 101, 77, 97, 99, 114, 111, 83, 116, 109, 116, 32, 60, 45, 32, 77, 97, 99, 114, 111, 79, 114, 70, 117, 110, 99, 116, 105, 111, 110, 32, 73, 102, 78, 111, 116, 69, 120, 105, 115, 116, 115, 63, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 32, 76, 105, 115, 116, 40, 77, 97, 99, 114, 111, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 41, 10, + 77, 97, 99, 114, 111, 79, 114, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 77, 97, 99, 114, 111, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 70, 117, 110, 99, 116, 105, 111, 110, 75, 101, 121, 119, 111, 114, 100, 10, + 77, 97, 99, 114, 111, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 77, 65, 67, 82, 79, 39, 10, + 70, 117, 110, 99, 116, 105, 111, 110, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 70, 85, 78, 67, 84, 73, 79, 78, 39, 10, + 77, 97, 99, 114, 111, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 77, 97, 99, 114, 111, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 63, 41, 32, 39, 65, 83, 39, 32, 77, 97, 99, 114, 111, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 66, 111, 100, 121, 10, + 77, 97, 99, 114, 111, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 66, 111, 100, 121, 32, 60, 45, 32, 84, 97, 98, 108, 101, 77, 97, 99, 114, 111, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 32, 47, 32, 83, 99, 97, 108, 97, 114, 77, 97, 99, 114, 111, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 10, + 77, 97, 99, 114, 111, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 32, 60, 45, 32, 76, 105, 115, 116, 40, 77, 97, 99, 114, 111, 80, 97, 114, 97, 109, 101, 116, 101, 114, 41, 10, + 77, 97, 99, 114, 111, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 60, 45, 32, 78, 97, 109, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 47, 32, 83, 105, 109, 112, 108, 101, 80, 97, 114, 97, 109, 101, 116, 101, 114, 10, + 83, 105, 109, 112, 108, 101, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 60, 45, 32, 84, 121, 112, 101, 70, 117, 110, 99, 78, 97, 109, 101, 32, 84, 121, 112, 101, 63, 10, + 83, 99, 97, 108, 97, 114, 77, 97, 99, 114, 111, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 84, 97, 98, 108, 101, 77, 97, 99, 114, 111, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 32, 60, 45, 32, 39, 84, 65, 66, 76, 69, 39, 32, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 10, + 67, 114, 101, 97, 116, 101, 83, 99, 104, 101, 109, 97, 83, 116, 109, 116, 32, 60, 45, 32, 39, 83, 67, 72, 69, 77, 65, 39, 32, 73, 102, 78, 111, 116, 69, 120, 105, 115, 116, 115, 63, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 32, 87, 105, 116, 104, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 63, 10, + 87, 105, 116, 104, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 39, 87, 73, 84, 72, 39, 32, 82, 101, 108, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 10, + 67, 114, 101, 97, 116, 101, 83, 101, 99, 114, 101, 116, 83, 116, 109, 116, 32, 60, 45, 32, 39, 83, 69, 67, 82, 69, 84, 39, 32, 73, 102, 78, 111, 116, 69, 120, 105, 115, 116, 115, 63, 32, 83, 101, 99, 114, 101, 116, 78, 97, 109, 101, 63, 32, 83, 101, 99, 114, 101, 116, 83, 116, 111, 114, 97, 103, 101, 83, 112, 101, 99, 105, 102, 105, 101, 114, 63, 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 10, + 83, 101, 99, 114, 101, 116, 83, 116, 111, 114, 97, 103, 101, 83, 112, 101, 99, 105, 102, 105, 101, 114, 32, 60, 45, 32, 39, 73, 78, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 83, 101, 99, 114, 101, 116, 78, 97, 109, 101, 32, 60, 45, 32, 67, 111, 108, 73, 100, 10, + 67, 114, 101, 97, 116, 101, 83, 101, 113, 117, 101, 110, 99, 101, 83, 116, 109, 116, 32, 60, 45, 32, 39, 83, 69, 81, 85, 69, 78, 67, 69, 39, 32, 73, 102, 78, 111, 116, 69, 120, 105, 115, 116, 115, 63, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 32, 83, 101, 113, 117, 101, 110, 99, 101, 79, 112, 116, 105, 111, 110, 42, 10, + 83, 101, 113, 117, 101, 110, 99, 101, 79, 112, 116, 105, 111, 110, 32, 60, 45, 10, + 9, 83, 101, 113, 83, 101, 116, 67, 121, 99, 108, 101, 32, 47, 10, + 9, 83, 101, 113, 83, 101, 116, 73, 110, 99, 114, 101, 109, 101, 110, 116, 32, 47, 10, + 9, 83, 101, 113, 83, 101, 116, 77, 105, 110, 77, 97, 120, 32, 47, 10, + 9, 83, 101, 113, 78, 111, 77, 105, 110, 77, 97, 120, 32, 47, 10, + 9, 83, 101, 113, 83, 116, 97, 114, 116, 87, 105, 116, 104, 32, 47, 10, + 9, 83, 101, 113, 79, 119, 110, 101, 100, 66, 121, 10, + 83, 101, 113, 83, 101, 116, 67, 121, 99, 108, 101, 32, 60, 45, 32, 83, 101, 113, 67, 121, 99, 108, 101, 32, 47, 32, 83, 101, 113, 78, 111, 67, 121, 99, 108, 101, 10, + 83, 101, 113, 67, 121, 99, 108, 101, 32, 60, 45, 32, 39, 67, 89, 67, 76, 69, 39, 10, + 83, 101, 113, 78, 111, 67, 121, 99, 108, 101, 32, 60, 45, 32, 39, 78, 79, 39, 32, 39, 67, 89, 67, 76, 69, 39, 10, + 83, 101, 113, 83, 101, 116, 73, 110, 99, 114, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 73, 78, 67, 82, 69, 77, 69, 78, 84, 39, 32, 39, 66, 89, 39, 63, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 83, 101, 113, 83, 101, 116, 77, 105, 110, 77, 97, 120, 32, 60, 45, 32, 83, 101, 113, 77, 105, 110, 79, 114, 77, 97, 120, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 83, 101, 113, 78, 111, 77, 105, 110, 77, 97, 120, 32, 60, 45, 32, 39, 78, 79, 39, 32, 83, 101, 113, 77, 105, 110, 79, 114, 77, 97, 120, 10, + 83, 101, 113, 83, 116, 97, 114, 116, 87, 105, 116, 104, 32, 60, 45, 32, 39, 83, 84, 65, 82, 84, 39, 32, 39, 87, 73, 84, 72, 39, 63, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 83, 101, 113, 79, 119, 110, 101, 100, 66, 121, 32, 60, 45, 32, 39, 79, 87, 78, 69, 68, 39, 32, 39, 66, 89, 39, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 10, + 83, 101, 113, 77, 105, 110, 79, 114, 77, 97, 120, 32, 60, 45, 32, 77, 105, 110, 86, 97, 108, 117, 101, 32, 47, 32, 77, 97, 120, 86, 97, 108, 117, 101, 10, + 77, 105, 110, 86, 97, 108, 117, 101, 32, 60, 45, 32, 39, 77, 73, 78, 86, 65, 76, 85, 69, 39, 10, + 77, 97, 120, 86, 97, 108, 117, 101, 32, 60, 45, 32, 39, 77, 65, 88, 86, 65, 76, 85, 69, 39, 10, + 67, 114, 101, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 67, 82, 69, 65, 84, 69, 39, 32, 79, 114, 82, 101, 112, 108, 97, 99, 101, 63, 32, 84, 101, 109, 112, 111, 114, 97, 114, 121, 63, 32, 67, 114, 101, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 86, 97, 114, 105, 97, 116, 105, 111, 110, 10, + 67, 114, 101, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 86, 97, 114, 105, 97, 116, 105, 111, 110, 32, 60, 45, 32, 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 83, 116, 109, 116, 32, 47, 32, 67, 114, 101, 97, 116, 101, 77, 97, 99, 114, 111, 83, 116, 109, 116, 32, 47, 32, 67, 114, 101, 97, 116, 101, 83, 101, 113, 117, 101, 110, 99, 101, 83, 116, 109, 116, 32, 47, 32, 67, 114, 101, 97, 116, 101, 84, 121, 112, 101, 83, 116, 109, 116, 32, 47, 32, 67, 114, 101, 97, 116, 101, 83, 99, 104, 101, 109, 97, 83, 116, 109, 116, 32, 47, 32, 67, 114, 101, 97, 116, 101, 86, 105, 101, 119, 83, 116, 109, 116, 32, 47, 32, 67, 114, 101, 97, 116, 101, 73, 110, 100, 101, 120, 83, 116, 109, 116, 32, 47, 32, 67, 114, 101, 97, 116, 101, 83, 101, 99, 114, 101, 116, 83, 116, 109, 116, 32, 47, 32, 67, 114, 101, 97, 116, 101, 84, 114, 105, 103, 103, 101, 114, 83, 116, 109, 116, 10, + 79, 114, 82, 101, 112, 108, 97, 99, 101, 32, 60, 45, 32, 39, 79, 82, 39, 32, 39, 82, 69, 80, 76, 65, 67, 69, 39, 10, + 84, 101, 109, 112, 111, 114, 97, 114, 121, 32, 60, 45, 32, 80, 101, 114, 115, 105, 115, 116, 101, 110, 116, 32, 47, 32, 84, 101, 109, 112, 80, 101, 114, 115, 105, 115, 116, 101, 110, 116, 32, 47, 32, 84, 101, 109, 112, 111, 114, 97, 114, 121, 80, 101, 114, 115, 105, 115, 116, 101, 110, 116, 10, + 80, 101, 114, 115, 105, 115, 116, 101, 110, 116, 32, 60, 45, 32, 39, 80, 69, 82, 83, 73, 83, 84, 69, 78, 84, 39, 10, + 84, 101, 109, 112, 80, 101, 114, 115, 105, 115, 116, 101, 110, 116, 32, 60, 45, 32, 39, 84, 69, 77, 80, 39, 10, + 84, 101, 109, 112, 111, 114, 97, 114, 121, 80, 101, 114, 115, 105, 115, 116, 101, 110, 116, 32, 60, 45, 32, 39, 84, 69, 77, 80, 79, 82, 65, 82, 89, 39, 10, + 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 83, 116, 109, 116, 32, 60, 45, 32, 39, 84, 65, 66, 76, 69, 39, 32, 73, 102, 78, 111, 116, 69, 120, 105, 115, 116, 115, 63, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 32, 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 32, 67, 111, 109, 109, 105, 116, 65, 99, 116, 105, 111, 110, 63, 10, + 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 32, 60, 45, 32, 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 65, 115, 32, 47, 32, 67, 114, 101, 97, 116, 101, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 10, + 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 65, 115, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 76, 105, 115, 116, 63, 32, 80, 97, 114, 116, 105, 116, 105, 111, 110, 83, 111, 114, 116, 101, 100, 79, 112, 116, 105, 111, 110, 115, 63, 32, 87, 105, 116, 104, 76, 105, 115, 116, 63, 32, 39, 65, 83, 39, 32, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 87, 105, 116, 104, 68, 97, 116, 97, 63, 10, + 80, 97, 114, 116, 105, 116, 105, 111, 110, 83, 111, 114, 116, 101, 100, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 80, 97, 114, 116, 105, 116, 105, 111, 110, 79, 112, 116, 83, 111, 114, 116, 101, 100, 79, 112, 116, 105, 111, 110, 115, 32, 47, 32, 83, 111, 114, 116, 101, 100, 79, 112, 116, 80, 97, 114, 116, 105, 116, 105, 111, 110, 79, 112, 116, 105, 111, 110, 115, 10, + 80, 97, 114, 116, 105, 116, 105, 111, 110, 79, 112, 116, 83, 111, 114, 116, 101, 100, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 80, 97, 114, 116, 105, 116, 105, 111, 110, 79, 112, 116, 105, 111, 110, 115, 32, 83, 111, 114, 116, 101, 100, 79, 112, 116, 105, 111, 110, 115, 63, 10, + 83, 111, 114, 116, 101, 100, 79, 112, 116, 80, 97, 114, 116, 105, 116, 105, 111, 110, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 83, 111, 114, 116, 101, 100, 79, 112, 116, 105, 111, 110, 115, 32, 80, 97, 114, 116, 105, 116, 105, 111, 110, 79, 112, 116, 105, 111, 110, 115, 63, 10, + 80, 97, 114, 116, 105, 116, 105, 111, 110, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 39, 80, 65, 82, 84, 73, 84, 73, 79, 78, 69, 68, 39, 32, 39, 66, 89, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 41, 10, + 83, 111, 114, 116, 101, 100, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 39, 83, 79, 82, 84, 69, 68, 39, 32, 39, 66, 89, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 41, 10, + 87, 105, 116, 104, 68, 97, 116, 97, 32, 60, 45, 32, 87, 105, 116, 104, 68, 97, 116, 97, 79, 110, 108, 121, 32, 47, 32, 87, 105, 116, 104, 78, 111, 68, 97, 116, 97, 10, + 87, 105, 116, 104, 68, 97, 116, 97, 79, 110, 108, 121, 32, 60, 45, 32, 39, 87, 73, 84, 72, 39, 32, 39, 68, 65, 84, 65, 39, 10, + 87, 105, 116, 104, 78, 111, 68, 97, 116, 97, 32, 60, 45, 32, 39, 87, 73, 84, 72, 39, 32, 39, 78, 79, 39, 32, 39, 68, 65, 84, 65, 39, 10, + 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 41, 41, 10, + 67, 114, 101, 97, 116, 101, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 63, 41, 32, 80, 97, 114, 116, 105, 116, 105, 111, 110, 83, 111, 114, 116, 101, 100, 79, 112, 116, 105, 111, 110, 115, 63, 32, 87, 105, 116, 104, 76, 105, 115, 116, 63, 10, + 73, 102, 78, 111, 116, 69, 120, 105, 115, 116, 115, 32, 60, 45, 32, 39, 73, 70, 39, 32, 39, 78, 79, 84, 39, 32, 39, 69, 88, 73, 83, 84, 83, 39, 10, + 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 47, 32, 83, 99, 104, 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 79, 114, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 79, 114, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 83, 99, 104, 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 79, 114, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 79, 114, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 67, 97, 116, 97, 108, 111, 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 43, 32, 82, 101, 115, 101, 114, 118, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 79, 114, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 79, 114, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 47, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 82, 101, 115, 101, 114, 118, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 79, 114, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 82, 101, 115, 101, 114, 118, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 47, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 67, 97, 116, 97, 108, 111, 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, 32, 39, 46, 39, 10, + 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 60, 45, 32, 83, 99, 104, 101, 109, 97, 78, 97, 109, 101, 32, 39, 46, 39, 10, + 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 60, 45, 32, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 78, 97, 109, 101, 32, 39, 46, 39, 10, + 84, 97, 98, 108, 101, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 60, 45, 32, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 39, 46, 39, 10, + 82, 101, 115, 101, 114, 118, 101, 100, 84, 97, 98, 108, 101, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 60, 45, 32, 82, 101, 115, 101, 114, 118, 101, 100, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 39, 46, 39, 10, + 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 69, 108, 101, 109, 101, 110, 116, 41, 10, + 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 69, 108, 101, 109, 101, 110, 116, 32, 60, 45, 32, 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 32, 47, 32, 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 10, + 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 32, 60, 45, 32, 67, 111, 108, 117, 109, 110, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 10, + 67, 114, 101, 97, 116, 101, 84, 97, 98, 108, 101, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 84, 111, 112, 76, 101, 118, 101, 108, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 10, + 67, 111, 108, 117, 109, 110, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 84, 121, 112, 101, 63, 32, 71, 101, 110, 101, 114, 97, 116, 101, 100, 67, 111, 108, 117, 109, 110, 63, 32, 67, 111, 108, 117, 109, 110, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 42, 10, + 67, 111, 108, 117, 109, 110, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 78, 97, 109, 101, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 47, 32, 67, 111, 108, 117, 109, 110, 67, 111, 108, 108, 97, 116, 105, 111, 110, 10, + 78, 97, 109, 101, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 78, 97, 109, 101, 67, 108, 97, 117, 115, 101, 63, 32, 67, 111, 108, 117, 109, 110, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 69, 108, 101, 109, 101, 110, 116, 10, + 67, 111, 108, 117, 109, 110, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 69, 108, 101, 109, 101, 110, 116, 32, 60, 45, 32, 78, 111, 116, 78, 117, 108, 108, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 47, 32, 85, 110, 105, 113, 117, 101, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 47, 32, 80, 114, 105, 109, 97, 114, 121, 75, 101, 121, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 47, 32, 68, 101, 102, 97, 117, 108, 116, 86, 97, 108, 117, 101, 32, 47, 32, 67, 104, 101, 99, 107, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 47, 32, 70, 111, 114, 101, 105, 103, 110, 75, 101, 121, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 47, 32, 67, 111, 108, 117, 109, 110, 67, 111, 109, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 78, 111, 116, 78, 117, 108, 108, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 78, 117, 108, 108, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 47, 32, 78, 111, 116, 78, 117, 108, 108, 67, 111, 108, 117, 109, 110, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 10, + 78, 117, 108, 108, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 78, 85, 76, 76, 39, 10, + 78, 111, 116, 78, 117, 108, 108, 67, 111, 108, 117, 109, 110, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 78, 79, 84, 39, 32, 39, 78, 85, 76, 76, 39, 10, + 85, 110, 105, 113, 117, 101, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 85, 78, 73, 81, 85, 69, 39, 32, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 84, 105, 109, 105, 110, 103, 63, 10, + 80, 114, 105, 109, 97, 114, 121, 75, 101, 121, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 80, 82, 73, 77, 65, 82, 89, 39, 32, 39, 75, 69, 89, 39, 32, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 84, 105, 109, 105, 110, 103, 63, 10, + 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 84, 105, 109, 105, 110, 103, 32, 60, 45, 32, 73, 109, 109, 101, 100, 105, 97, 116, 101, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 47, 32, 68, 101, 102, 101, 114, 114, 101, 100, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 10, + 73, 109, 109, 101, 100, 105, 97, 116, 101, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 73, 77, 77, 69, 68, 73, 65, 84, 69, 39, 10, + 68, 101, 102, 101, 114, 114, 101, 100, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 68, 69, 70, 69, 82, 82, 69, 68, 39, 10, + 68, 101, 102, 97, 117, 108, 116, 86, 97, 108, 117, 101, 32, 60, 45, 32, 39, 68, 69, 70, 65, 85, 76, 84, 39, 32, 67, 111, 108, 117, 109, 110, 68, 101, 102, 97, 117, 108, 116, 69, 120, 112, 114, 10, + 67, 104, 101, 99, 107, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 67, 72, 69, 67, 75, 39, 32, 80, 97, 114, 101, 110, 115, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, + 70, 111, 114, 101, 105, 103, 110, 75, 101, 121, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 82, 69, 70, 69, 82, 69, 78, 67, 69, 83, 39, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 80, 97, 114, 101, 110, 115, 40, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 41, 63, 32, 75, 101, 121, 65, 99, 116, 105, 111, 110, 115, 63, 10, + 67, 111, 108, 117, 109, 110, 67, 111, 108, 108, 97, 116, 105, 111, 110, 32, 60, 45, 32, 39, 67, 79, 76, 76, 65, 84, 69, 39, 32, 68, 111, 116, 116, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 67, 111, 108, 117, 109, 110, 67, 111, 109, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 85, 83, 73, 78, 71, 39, 32, 39, 67, 79, 77, 80, 82, 69, 83, 83, 73, 79, 78, 39, 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 10, + 75, 101, 121, 65, 99, 116, 105, 111, 110, 115, 32, 60, 45, 32, 85, 112, 100, 97, 116, 101, 70, 105, 114, 115, 116, 75, 101, 121, 65, 99, 116, 105, 111, 110, 115, 32, 47, 32, 68, 101, 108, 101, 116, 101, 70, 105, 114, 115, 116, 75, 101, 121, 65, 99, 116, 105, 111, 110, 115, 10, + 85, 112, 100, 97, 116, 101, 70, 105, 114, 115, 116, 75, 101, 121, 65, 99, 116, 105, 111, 110, 115, 32, 60, 45, 32, 85, 112, 100, 97, 116, 101, 65, 99, 116, 105, 111, 110, 32, 68, 101, 108, 101, 116, 101, 65, 99, 116, 105, 111, 110, 63, 10, + 68, 101, 108, 101, 116, 101, 70, 105, 114, 115, 116, 75, 101, 121, 65, 99, 116, 105, 111, 110, 115, 32, 60, 45, 32, 68, 101, 108, 101, 116, 101, 65, 99, 116, 105, 111, 110, 32, 85, 112, 100, 97, 116, 101, 65, 99, 116, 105, 111, 110, 63, 10, + 85, 112, 100, 97, 116, 101, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 79, 78, 39, 32, 39, 85, 80, 68, 65, 84, 69, 39, 32, 75, 101, 121, 65, 99, 116, 105, 111, 110, 10, + 68, 101, 108, 101, 116, 101, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 79, 78, 39, 32, 39, 68, 69, 76, 69, 84, 69, 39, 32, 75, 101, 121, 65, 99, 116, 105, 111, 110, 10, + 75, 101, 121, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 78, 111, 75, 101, 121, 65, 99, 116, 105, 111, 110, 32, 47, 32, 82, 101, 115, 116, 114, 105, 99, 116, 75, 101, 121, 65, 99, 116, 105, 111, 110, 32, 47, 32, 67, 97, 115, 99, 97, 100, 101, 75, 101, 121, 65, 99, 116, 105, 111, 110, 32, 47, 32, 83, 101, 116, 78, 117, 108, 108, 75, 101, 121, 65, 99, 116, 105, 111, 110, 32, 47, 32, 83, 101, 116, 68, 101, 102, 97, 117, 108, 116, 75, 101, 121, 65, 99, 116, 105, 111, 110, 10, + 78, 111, 75, 101, 121, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 78, 79, 39, 32, 39, 65, 67, 84, 73, 79, 78, 39, 10, + 82, 101, 115, 116, 114, 105, 99, 116, 75, 101, 121, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 82, 69, 83, 84, 82, 73, 67, 84, 39, 10, + 67, 97, 115, 99, 97, 100, 101, 75, 101, 121, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 67, 65, 83, 67, 65, 68, 69, 39, 10, + 83, 101, 116, 78, 117, 108, 108, 75, 101, 121, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 83, 69, 84, 39, 32, 39, 78, 85, 76, 76, 39, 10, + 83, 101, 116, 68, 101, 102, 97, 117, 108, 116, 75, 101, 121, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 83, 69, 84, 39, 32, 39, 68, 69, 70, 65, 85, 76, 84, 39, 10, + 84, 111, 112, 76, 101, 118, 101, 108, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 78, 97, 109, 101, 67, 108, 97, 117, 115, 101, 63, 32, 84, 111, 112, 76, 101, 118, 101, 108, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 76, 105, 115, 116, 10, + 84, 111, 112, 76, 101, 118, 101, 108, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 76, 105, 115, 116, 32, 60, 45, 32, 84, 111, 112, 80, 114, 105, 109, 97, 114, 121, 75, 101, 121, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 47, 32, 84, 111, 112, 67, 104, 101, 99, 107, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 47, 32, 84, 111, 112, 85, 110, 105, 113, 117, 101, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 47, 32, 84, 111, 112, 70, 111, 114, 101, 105, 103, 110, 75, 101, 121, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 10, + 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 78, 97, 109, 101, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 67, 79, 78, 83, 84, 82, 65, 73, 78, 84, 39, 32, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 78, 97, 109, 101, 10, + 84, 111, 112, 67, 104, 101, 99, 107, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 67, 104, 101, 99, 107, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 10, + 84, 111, 112, 80, 114, 105, 109, 97, 114, 121, 75, 101, 121, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 80, 82, 73, 77, 65, 82, 89, 39, 32, 39, 75, 69, 89, 39, 32, 67, 111, 108, 117, 109, 110, 73, 100, 76, 105, 115, 116, 32, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 84, 105, 109, 105, 110, 103, 63, 10, + 84, 111, 112, 85, 110, 105, 113, 117, 101, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 85, 78, 73, 81, 85, 69, 39, 32, 67, 111, 108, 117, 109, 110, 73, 100, 76, 105, 115, 116, 32, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 84, 105, 109, 105, 110, 103, 63, 10, + 84, 111, 112, 70, 111, 114, 101, 105, 103, 110, 75, 101, 121, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 32, 60, 45, 32, 39, 70, 79, 82, 69, 73, 71, 78, 39, 32, 39, 75, 69, 89, 39, 32, 67, 111, 108, 117, 109, 110, 73, 100, 76, 105, 115, 116, 32, 70, 111, 114, 101, 105, 103, 110, 75, 101, 121, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 10, + 67, 111, 108, 117, 109, 110, 73, 100, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 67, 111, 108, 73, 100, 41, 41, 10, + 80, 108, 97, 105, 110, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 33, 82, 101, 115, 101, 114, 118, 101, 100, 75, 101, 121, 119, 111, 114, 100, 32, 60, 91, 97, 45, 122, 95, 93, 105, 91, 97, 45, 122, 48, 45, 57, 95, 93, 105, 42, 62, 10, + 81, 117, 111, 116, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 40, 39, 34, 39, 32, 91, 94, 34, 93, 42, 32, 39, 34, 39, 41, 32, 47, 32, 40, 39, 96, 39, 32, 91, 94, 96, 93, 42, 32, 39, 96, 39, 41, 10, + 68, 111, 116, 116, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 68, 111, 116, 67, 111, 108, 76, 97, 98, 101, 108, 42, 10, + 68, 111, 116, 67, 111, 108, 76, 97, 98, 101, 108, 32, 60, 45, 32, 39, 46, 39, 32, 67, 111, 108, 76, 97, 98, 101, 108, 10, + 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 81, 117, 111, 116, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 47, 32, 80, 108, 97, 105, 110, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 67, 111, 108, 73, 100, 32, 60, 45, 32, 85, 110, 114, 101, 115, 101, 114, 118, 101, 100, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 32, 60, 45, 32, 67, 111, 108, 73, 100, 32, 47, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 84, 121, 112, 101, 70, 117, 110, 99, 78, 97, 109, 101, 32, 60, 45, 32, 85, 110, 114, 101, 115, 101, 114, 118, 101, 100, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 84, 121, 112, 101, 70, 117, 110, 99, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 84, 121, 112, 101, 70, 117, 110, 99, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 84, 121, 112, 101, 78, 97, 109, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 70, 117, 110, 99, 78, 97, 109, 101, 75, 101, 121, 119, 111, 114, 100, 10, + 84, 121, 112, 101, 78, 97, 109, 101, 32, 60, 45, 32, 85, 110, 114, 101, 115, 101, 114, 118, 101, 100, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 84, 121, 112, 101, 78, 97, 109, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 67, 111, 108, 76, 97, 98, 101, 108, 32, 60, 45, 32, 82, 101, 115, 101, 114, 118, 101, 100, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 85, 110, 114, 101, 115, 101, 114, 118, 101, 100, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 70, 117, 110, 99, 78, 97, 109, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 84, 121, 112, 101, 78, 97, 109, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 67, 111, 108, 76, 97, 98, 101, 108, 79, 114, 83, 116, 114, 105, 110, 103, 32, 60, 45, 32, 67, 111, 108, 76, 97, 98, 101, 108, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 47, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 67, 111, 108, 76, 97, 98, 101, 108, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 67, 111, 108, 76, 97, 98, 101, 108, 10, + 66, 97, 114, 101, 67, 111, 108, 76, 97, 98, 101, 108, 32, 60, 45, 32, 66, 97, 114, 101, 76, 97, 98, 101, 108, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 71, 101, 110, 101, 114, 97, 116, 101, 100, 67, 111, 108, 117, 109, 110, 32, 60, 45, 32, 71, 101, 110, 101, 114, 97, 116, 101, 100, 63, 32, 39, 65, 83, 39, 32, 80, 97, 114, 101, 110, 115, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 32, 71, 101, 110, 101, 114, 97, 116, 101, 100, 67, 111, 108, 117, 109, 110, 84, 121, 112, 101, 63, 10, + 71, 101, 110, 101, 114, 97, 116, 101, 100, 32, 60, 45, 32, 71, 101, 110, 101, 114, 97, 116, 101, 100, 65, 108, 119, 97, 121, 115, 32, 47, 32, 71, 101, 110, 101, 114, 97, 116, 101, 100, 66, 121, 68, 101, 102, 97, 117, 108, 116, 10, + 71, 101, 110, 101, 114, 97, 116, 101, 100, 65, 108, 119, 97, 121, 115, 32, 60, 45, 32, 39, 71, 69, 78, 69, 82, 65, 84, 69, 68, 39, 32, 39, 65, 76, 87, 65, 89, 83, 39, 10, + 71, 101, 110, 101, 114, 97, 116, 101, 100, 66, 121, 68, 101, 102, 97, 117, 108, 116, 32, 60, 45, 32, 39, 71, 69, 78, 69, 82, 65, 84, 69, 68, 39, 32, 39, 66, 89, 39, 32, 39, 68, 69, 70, 65, 85, 76, 84, 39, 10, + 71, 101, 110, 101, 114, 97, 116, 101, 100, 67, 111, 108, 117, 109, 110, 84, 121, 112, 101, 32, 60, 45, 32, 86, 105, 114, 116, 117, 97, 108, 71, 101, 110, 101, 114, 97, 116, 101, 100, 67, 111, 108, 117, 109, 110, 32, 47, 32, 83, 116, 111, 114, 101, 100, 71, 101, 110, 101, 114, 97, 116, 101, 100, 67, 111, 108, 117, 109, 110, 10, + 67, 111, 109, 109, 105, 116, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 79, 78, 39, 32, 39, 67, 79, 77, 77, 73, 84, 39, 32, 80, 114, 101, 115, 101, 114, 118, 101, 79, 114, 68, 101, 108, 101, 116, 101, 32, 39, 82, 79, 87, 83, 39, 10, + 80, 114, 101, 115, 101, 114, 118, 101, 79, 114, 68, 101, 108, 101, 116, 101, 32, 60, 45, 32, 80, 114, 101, 115, 101, 114, 118, 101, 82, 111, 119, 115, 32, 47, 32, 68, 101, 108, 101, 116, 101, 82, 111, 119, 115, 10, + 80, 114, 101, 115, 101, 114, 118, 101, 82, 111, 119, 115, 32, 60, 45, 32, 39, 80, 82, 69, 83, 69, 82, 86, 69, 39, 10, + 68, 101, 108, 101, 116, 101, 82, 111, 119, 115, 32, 60, 45, 32, 39, 68, 69, 76, 69, 84, 69, 39, 10, + 86, 105, 114, 116, 117, 97, 108, 71, 101, 110, 101, 114, 97, 116, 101, 100, 67, 111, 108, 117, 109, 110, 32, 60, 45, 32, 39, 86, 73, 82, 84, 85, 65, 76, 39, 10, + 83, 116, 111, 114, 101, 100, 71, 101, 110, 101, 114, 97, 116, 101, 100, 67, 111, 108, 117, 109, 110, 32, 60, 45, 32, 39, 83, 84, 79, 82, 69, 68, 39, 10, + 67, 114, 101, 97, 116, 101, 84, 114, 105, 103, 103, 101, 114, 83, 116, 109, 116, 32, 60, 45, 32, 39, 84, 82, 73, 71, 71, 69, 82, 39, 32, 73, 102, 78, 111, 116, 69, 120, 105, 115, 116, 115, 63, 32, 84, 114, 105, 103, 103, 101, 114, 78, 97, 109, 101, 32, 84, 114, 105, 103, 103, 101, 114, 84, 105, 109, 105, 110, 103, 32, 84, 114, 105, 103, 103, 101, 114, 69, 118, 101, 110, 116, 32, 39, 79, 78, 39, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 82, 101, 102, 101, 114, 101, 110, 99, 105, 110, 103, 67, 108, 97, 117, 115, 101, 63, 32, 70, 111, 114, 69, 97, 99, 104, 67, 108, 97, 117, 115, 101, 63, 32, 84, 114, 105, 103, 103, 101, 114, 66, 111, 100, 121, 10, + 84, 114, 105, 103, 103, 101, 114, 66, 111, 100, 121, 32, 60, 45, 32, 73, 110, 115, 101, 114, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 32, 85, 112, 100, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 32, 68, 101, 108, 101, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 32, 77, 101, 114, 103, 101, 73, 110, 116, 111, 83, 116, 97, 116, 101, 109, 101, 110, 116, 10, + 84, 114, 105, 103, 103, 101, 114, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 82, 101, 102, 101, 114, 101, 110, 99, 105, 110, 103, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 82, 69, 70, 69, 82, 69, 78, 67, 73, 78, 71, 39, 32, 82, 101, 102, 101, 114, 101, 110, 99, 105, 110, 103, 73, 116, 101, 109, 32, 82, 101, 102, 101, 114, 101, 110, 99, 105, 110, 103, 73, 116, 101, 109, 63, 10, + 82, 101, 102, 101, 114, 101, 110, 99, 105, 110, 103, 73, 116, 101, 109, 32, 60, 45, 32, 82, 101, 102, 101, 114, 101, 110, 99, 105, 110, 103, 78, 101, 119, 84, 97, 98, 108, 101, 65, 115, 32, 47, 32, 82, 101, 102, 101, 114, 101, 110, 99, 105, 110, 103, 79, 108, 100, 84, 97, 98, 108, 101, 65, 115, 10, + 82, 101, 102, 101, 114, 101, 110, 99, 105, 110, 103, 78, 101, 119, 84, 97, 98, 108, 101, 65, 115, 32, 60, 45, 32, 39, 78, 69, 87, 39, 32, 39, 84, 65, 66, 76, 69, 39, 32, 39, 65, 83, 39, 32, 67, 111, 108, 73, 100, 10, + 82, 101, 102, 101, 114, 101, 110, 99, 105, 110, 103, 79, 108, 100, 84, 97, 98, 108, 101, 65, 115, 32, 60, 45, 32, 39, 79, 76, 68, 39, 32, 39, 84, 65, 66, 76, 69, 39, 32, 39, 65, 83, 39, 32, 67, 111, 108, 73, 100, 10, + 84, 114, 105, 103, 103, 101, 114, 84, 105, 109, 105, 110, 103, 32, 60, 45, 32, 84, 114, 105, 103, 103, 101, 114, 66, 101, 102, 111, 114, 101, 32, 47, 32, 84, 114, 105, 103, 103, 101, 114, 65, 102, 116, 101, 114, 32, 47, 32, 84, 114, 105, 103, 103, 101, 114, 73, 110, 115, 116, 101, 97, 100, 79, 102, 10, + 84, 114, 105, 103, 103, 101, 114, 66, 101, 102, 111, 114, 101, 32, 60, 45, 32, 39, 66, 69, 70, 79, 82, 69, 39, 10, + 84, 114, 105, 103, 103, 101, 114, 65, 102, 116, 101, 114, 32, 60, 45, 32, 39, 65, 70, 84, 69, 82, 39, 10, + 84, 114, 105, 103, 103, 101, 114, 73, 110, 115, 116, 101, 97, 100, 79, 102, 32, 60, 45, 32, 39, 73, 78, 83, 84, 69, 65, 68, 39, 32, 39, 79, 70, 39, 10, + 84, 114, 105, 103, 103, 101, 114, 69, 118, 101, 110, 116, 32, 60, 45, 32, 84, 114, 105, 103, 103, 101, 114, 69, 118, 101, 110, 116, 85, 112, 100, 97, 116, 101, 79, 102, 32, 47, 32, 84, 114, 105, 103, 103, 101, 114, 69, 118, 101, 110, 116, 73, 110, 115, 101, 114, 116, 32, 47, 32, 84, 114, 105, 103, 103, 101, 114, 69, 118, 101, 110, 116, 68, 101, 108, 101, 116, 101, 32, 47, 32, 84, 114, 105, 103, 103, 101, 114, 69, 118, 101, 110, 116, 85, 112, 100, 97, 116, 101, 10, + 84, 114, 105, 103, 103, 101, 114, 69, 118, 101, 110, 116, 73, 110, 115, 101, 114, 116, 32, 60, 45, 32, 39, 73, 78, 83, 69, 82, 84, 39, 10, + 84, 114, 105, 103, 103, 101, 114, 69, 118, 101, 110, 116, 68, 101, 108, 101, 116, 101, 32, 60, 45, 32, 39, 68, 69, 76, 69, 84, 69, 39, 10, + 84, 114, 105, 103, 103, 101, 114, 69, 118, 101, 110, 116, 85, 112, 100, 97, 116, 101, 32, 60, 45, 32, 39, 85, 80, 68, 65, 84, 69, 39, 10, + 84, 114, 105, 103, 103, 101, 114, 69, 118, 101, 110, 116, 85, 112, 100, 97, 116, 101, 79, 102, 32, 60, 45, 32, 39, 85, 80, 68, 65, 84, 69, 39, 32, 39, 79, 70, 39, 32, 84, 114, 105, 103, 103, 101, 114, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 10, + 84, 114, 105, 103, 103, 101, 114, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 67, 111, 108, 73, 100, 41, 10, + 70, 111, 114, 69, 97, 99, 104, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 70, 111, 114, 69, 97, 99, 104, 82, 111, 119, 32, 47, 32, 70, 111, 114, 69, 97, 99, 104, 83, 116, 97, 116, 101, 109, 101, 110, 116, 10, + 70, 111, 114, 69, 97, 99, 104, 82, 111, 119, 32, 60, 45, 32, 39, 70, 79, 82, 39, 32, 39, 69, 65, 67, 72, 39, 32, 39, 82, 79, 87, 39, 10, + 70, 111, 114, 69, 97, 99, 104, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 70, 79, 82, 39, 32, 39, 69, 65, 67, 72, 39, 32, 39, 83, 84, 65, 84, 69, 77, 69, 78, 84, 39, 10, + 67, 114, 101, 97, 116, 101, 84, 121, 112, 101, 83, 116, 109, 116, 32, 60, 45, 32, 39, 84, 89, 80, 69, 39, 32, 73, 102, 78, 111, 116, 69, 120, 105, 115, 116, 115, 63, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 32, 39, 65, 83, 39, 32, 67, 114, 101, 97, 116, 101, 84, 121, 112, 101, 10, + 67, 114, 101, 97, 116, 101, 84, 121, 112, 101, 32, 60, 45, 32, 69, 110, 117, 109, 83, 101, 108, 101, 99, 116, 84, 121, 112, 101, 32, 47, 32, 69, 110, 117, 109, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 76, 105, 115, 116, 32, 47, 32, 67, 114, 101, 97, 116, 101, 84, 121, 112, 101, 70, 114, 111, 109, 84, 121, 112, 101, 10, + 67, 114, 101, 97, 116, 101, 84, 121, 112, 101, 70, 114, 111, 109, 84, 121, 112, 101, 32, 60, 45, 32, 84, 121, 112, 101, 10, + 69, 110, 117, 109, 83, 101, 108, 101, 99, 116, 84, 121, 112, 101, 32, 60, 45, 32, 39, 69, 78, 85, 77, 39, 32, 80, 97, 114, 101, 110, 115, 40, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 41, 10, + 69, 110, 117, 109, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 76, 105, 115, 116, 32, 60, 45, 32, 39, 69, 78, 85, 77, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 41, 63, 41, 32, 65, 114, 114, 97, 121, 66, 111, 117, 110, 100, 115, 42, 10, + 67, 114, 101, 97, 116, 101, 86, 105, 101, 119, 83, 116, 109, 116, 32, 60, 45, 32, 67, 114, 101, 97, 116, 101, 83, 101, 99, 117, 114, 101, 63, 32, 67, 114, 101, 97, 116, 101, 82, 101, 99, 117, 114, 115, 105, 118, 101, 63, 32, 39, 86, 73, 69, 87, 39, 32, 73, 102, 78, 111, 116, 69, 120, 105, 115, 116, 115, 63, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 32, 73, 110, 115, 101, 114, 116, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 63, 32, 87, 105, 116, 104, 76, 105, 115, 116, 63, 32, 39, 65, 83, 39, 32, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 10, + 67, 114, 101, 97, 116, 101, 82, 101, 99, 117, 114, 115, 105, 118, 101, 32, 60, 45, 32, 39, 82, 69, 67, 85, 82, 83, 73, 86, 69, 39, 10, + 67, 114, 101, 97, 116, 101, 83, 101, 99, 117, 114, 101, 32, 60, 45, 32, 39, 83, 69, 67, 85, 82, 69, 39, 10, + 68, 101, 97, 108, 108, 111, 99, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 68, 69, 65, 76, 76, 79, 67, 65, 84, 69, 39, 32, 68, 101, 97, 108, 108, 111, 99, 97, 116, 101, 80, 114, 101, 112, 97, 114, 101, 63, 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 10, + 68, 101, 97, 108, 108, 111, 99, 97, 116, 101, 80, 114, 101, 112, 97, 114, 101, 32, 60, 45, 32, 39, 80, 82, 69, 80, 65, 82, 69, 39, 10, + 68, 101, 108, 101, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 87, 105, 116, 104, 67, 108, 97, 117, 115, 101, 63, 32, 39, 68, 69, 76, 69, 84, 69, 39, 32, 39, 70, 82, 79, 77, 39, 32, 84, 97, 114, 103, 101, 116, 79, 112, 116, 65, 108, 105, 97, 115, 32, 68, 101, 108, 101, 116, 101, 85, 115, 105, 110, 103, 67, 108, 97, 117, 115, 101, 63, 32, 87, 104, 101, 114, 101, 67, 108, 97, 117, 115, 101, 63, 32, 82, 101, 116, 117, 114, 110, 105, 110, 103, 67, 108, 97, 117, 115, 101, 63, 10, + 84, 114, 117, 110, 99, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 84, 82, 85, 78, 67, 65, 84, 69, 39, 32, 39, 84, 65, 66, 76, 69, 39, 63, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 84, 114, 117, 110, 99, 97, 116, 101, 79, 112, 116, 105, 111, 110, 115, 63, 10, + 35, 32, 68, 114, 111, 112, 66, 101, 104, 97, 118, 105, 111, 114, 32, 102, 105, 114, 115, 116, 44, 32, 115, 111, 32, 67, 65, 83, 67, 65, 68, 69, 47, 82, 69, 83, 84, 82, 73, 67, 84, 32, 97, 114, 101, 32, 110, 111, 116, 32, 99, 111, 110, 115, 117, 109, 101, 100, 32, 97, 115, 32, 97, 110, 32, 97, 108, 105, 97, 115, 10, + 84, 114, 117, 110, 99, 97, 116, 101, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 84, 114, 117, 110, 99, 97, 116, 101, 68, 114, 111, 112, 66, 101, 104, 97, 118, 105, 111, 114, 32, 47, 32, 84, 114, 117, 110, 99, 97, 116, 101, 65, 108, 105, 97, 115, 79, 112, 116, 105, 111, 110, 115, 10, + 84, 114, 117, 110, 99, 97, 116, 101, 65, 108, 105, 97, 115, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 84, 97, 114, 103, 101, 116, 65, 108, 105, 97, 115, 32, 84, 114, 117, 110, 99, 97, 116, 101, 68, 114, 111, 112, 66, 101, 104, 97, 118, 105, 111, 114, 63, 10, + 84, 114, 117, 110, 99, 97, 116, 101, 68, 114, 111, 112, 66, 101, 104, 97, 118, 105, 111, 114, 32, 60, 45, 32, 68, 114, 111, 112, 66, 101, 104, 97, 118, 105, 111, 114, 10, + 84, 97, 114, 103, 101, 116, 79, 112, 116, 65, 108, 105, 97, 115, 32, 60, 45, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 84, 97, 114, 103, 101, 116, 65, 108, 105, 97, 115, 63, 10, + 84, 97, 114, 103, 101, 116, 65, 108, 105, 97, 115, 32, 60, 45, 32, 39, 65, 83, 39, 63, 32, 67, 111, 108, 73, 100, 10, + 68, 101, 108, 101, 116, 101, 85, 115, 105, 110, 103, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 85, 83, 73, 78, 71, 39, 32, 76, 105, 115, 116, 40, 84, 97, 98, 108, 101, 82, 101, 102, 41, 10, + 68, 101, 115, 99, 114, 105, 98, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 83, 104, 111, 119, 84, 97, 98, 108, 101, 115, 32, 47, 32, 83, 104, 111, 119, 68, 101, 112, 114, 101, 99, 97, 116, 101, 100, 83, 101, 108, 101, 99, 116, 32, 47, 32, 68, 101, 115, 99, 114, 105, 98, 101, 83, 101, 108, 101, 99, 116, 32, 47, 32, 83, 104, 111, 119, 65, 108, 108, 84, 97, 98, 108, 101, 115, 32, 47, 32, 83, 104, 111, 119, 66, 121, 78, 97, 109, 101, 32, 47, 32, 68, 101, 115, 99, 114, 105, 98, 101, 66, 121, 78, 97, 109, 101, 10, + 35, 32, 34, 68, 69, 83, 67, 82, 73, 66, 69, 32, 40, 113, 117, 101, 114, 121, 41, 34, 32, 97, 110, 100, 32, 34, 83, 85, 77, 77, 65, 82, 73, 90, 69, 32, 40, 113, 117, 101, 114, 121, 41, 34, 32, 100, 101, 115, 99, 114, 105, 98, 101, 32, 116, 104, 101, 32, 113, 117, 101, 114, 121, 39, 115, 32, 99, 111, 108, 117, 109, 110, 115, 46, 32, 34, 83, 72, 79, 87, 32, 40, 113, 117, 101, 114, 121, 41, 34, 32, 100, 111, 101, 115, 32, 116, 111, 111, 32, 98, 117, 116, 32, 105, 115, 32, 100, 101, 112, 114, 101, 99, 97, 116, 101, 100, 44, 10, + 35, 32, 115, 111, 32, 83, 104, 111, 119, 68, 101, 112, 114, 101, 99, 97, 116, 101, 100, 83, 101, 108, 101, 99, 116, 32, 105, 115, 32, 112, 97, 114, 115, 101, 100, 32, 121, 101, 116, 32, 115, 117, 112, 112, 114, 101, 115, 115, 101, 100, 32, 102, 114, 111, 109, 32, 97, 117, 116, 111, 99, 111, 109, 112, 108, 101, 116, 101, 32, 40, 109, 97, 116, 99, 104, 101, 114, 95, 102, 97, 99, 116, 111, 114, 121, 46, 99, 112, 112, 41, 32, 45, 32, 83, 72, 79, 87, 32, 119, 111, 110, 39, 116, 32, 111, 102, 102, 101, 114, 32, 116, 111, 32, 99, 111, 109, 112, 108, 101, 116, 101, 32, 97, 32, 113, 117, 101, 114, 121, 46, 10, + 83, 104, 111, 119, 68, 101, 112, 114, 101, 99, 97, 116, 101, 100, 83, 101, 108, 101, 99, 116, 32, 60, 45, 32, 83, 104, 111, 119, 82, 117, 108, 101, 32, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 10, + 68, 101, 115, 99, 114, 105, 98, 101, 83, 101, 108, 101, 99, 116, 32, 60, 45, 32, 68, 101, 115, 99, 114, 105, 98, 101, 79, 114, 83, 117, 109, 109, 97, 114, 105, 122, 101, 32, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 10, + 83, 104, 111, 119, 65, 108, 108, 84, 97, 98, 108, 101, 115, 32, 60, 45, 32, 83, 104, 111, 119, 79, 114, 68, 101, 115, 99, 114, 105, 98, 101, 32, 39, 65, 76, 76, 39, 32, 39, 84, 65, 66, 76, 69, 83, 39, 63, 10, + 83, 104, 111, 119, 84, 97, 98, 108, 101, 115, 32, 60, 45, 32, 83, 104, 111, 119, 79, 114, 68, 101, 115, 99, 114, 105, 98, 101, 32, 39, 84, 65, 66, 76, 69, 83, 39, 32, 39, 70, 82, 79, 77, 39, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 10, + 35, 32, 34, 83, 72, 79, 87, 32, 110, 97, 109, 101, 34, 32, 105, 115, 32, 115, 101, 116, 116, 105, 110, 103, 115, 45, 102, 105, 114, 115, 116, 44, 32, 115, 111, 32, 105, 116, 115, 32, 116, 97, 114, 103, 101, 116, 32, 97, 117, 116, 111, 99, 111, 109, 112, 108, 101, 116, 101, 115, 32, 115, 101, 116, 116, 105, 110, 103, 32, 110, 97, 109, 101, 115, 32, 40, 97, 110, 100, 32, 116, 104, 101, 32, 115, 112, 101, 99, 105, 97, 108, 32, 77, 121, 83, 81, 76, 32, 102, 111, 114, 109, 115, 41, 44, 32, 110, 111, 116, 10, + 35, 32, 97, 114, 98, 105, 116, 114, 97, 114, 121, 32, 116, 97, 98, 108, 101, 115, 46, 32, 34, 68, 69, 83, 67, 82, 73, 66, 69, 34, 47, 34, 83, 85, 77, 77, 65, 82, 73, 90, 69, 34, 32, 100, 101, 115, 99, 114, 105, 98, 101, 32, 97, 32, 114, 101, 108, 97, 116, 105, 111, 110, 44, 32, 115, 111, 32, 116, 104, 101, 105, 114, 32, 116, 97, 114, 103, 101, 116, 32, 97, 117, 116, 111, 99, 111, 109, 112, 108, 101, 116, 101, 115, 32, 116, 97, 98, 108, 101, 32, 110, 97, 109, 101, 115, 46, 10, + 83, 104, 111, 119, 66, 121, 78, 97, 109, 101, 32, 60, 45, 32, 83, 104, 111, 119, 82, 117, 108, 101, 32, 83, 104, 111, 119, 84, 97, 114, 103, 101, 116, 63, 10, + 68, 101, 115, 99, 114, 105, 98, 101, 66, 121, 78, 97, 109, 101, 32, 60, 45, 32, 68, 101, 115, 99, 114, 105, 98, 101, 79, 114, 83, 117, 109, 109, 97, 114, 105, 122, 101, 32, 68, 101, 115, 99, 114, 105, 98, 101, 84, 97, 114, 103, 101, 116, 63, 10, + 68, 101, 115, 99, 114, 105, 98, 101, 79, 114, 83, 117, 109, 109, 97, 114, 105, 122, 101, 32, 60, 45, 32, 68, 101, 115, 99, 114, 105, 98, 101, 82, 117, 108, 101, 32, 47, 32, 83, 117, 109, 109, 97, 114, 105, 122, 101, 10, + 35, 32, 83, 72, 79, 87, 39, 115, 32, 111, 119, 110, 32, 102, 111, 114, 109, 115, 32, 40, 97, 32, 115, 101, 116, 116, 105, 110, 103, 115, 45, 102, 105, 114, 115, 116, 32, 110, 97, 109, 101, 32, 97, 110, 100, 32, 116, 104, 101, 32, 115, 112, 101, 99, 105, 97, 108, 32, 77, 121, 83, 81, 76, 32, 102, 111, 114, 109, 115, 41, 32, 112, 108, 117, 115, 32, 116, 104, 101, 32, 100, 101, 115, 99, 114, 105, 98, 101, 45, 115, 116, 121, 108, 101, 32, 116, 97, 114, 103, 101, 116, 115, 32, 105, 116, 32, 115, 116, 105, 108, 108, 10, + 35, 32, 116, 111, 108, 101, 114, 97, 116, 101, 115, 32, 102, 111, 114, 32, 116, 104, 101, 32, 100, 101, 112, 114, 101, 99, 97, 116, 101, 100, 32, 34, 100, 101, 115, 99, 114, 105, 98, 101, 32, 118, 105, 97, 32, 83, 72, 79, 87, 34, 46, 32, 79, 110, 108, 121, 32, 115, 101, 116, 116, 105, 110, 103, 115, 32, 97, 110, 100, 32, 116, 104, 101, 32, 115, 112, 101, 99, 105, 97, 108, 32, 102, 111, 114, 109, 115, 32, 97, 114, 101, 32, 97, 117, 116, 111, 99, 111, 109, 112, 108, 101, 116, 101, 100, 32, 97, 102, 116, 101, 114, 32, 83, 72, 79, 87, 59, 10, + 35, 32, 97, 32, 113, 117, 97, 108, 105, 102, 105, 101, 100, 32, 116, 97, 98, 108, 101, 32, 105, 115, 32, 112, 97, 114, 115, 101, 100, 32, 98, 117, 116, 32, 105, 116, 115, 32, 115, 117, 103, 103, 101, 115, 116, 105, 111, 110, 115, 32, 97, 114, 101, 32, 115, 117, 112, 112, 114, 101, 115, 115, 101, 100, 32, 40, 115, 101, 101, 32, 109, 97, 116, 99, 104, 101, 114, 46, 99, 112, 112, 41, 46, 10, + 83, 104, 111, 119, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 83, 104, 111, 119, 68, 101, 112, 114, 101, 99, 97, 116, 101, 100, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 47, 32, 83, 104, 111, 119, 83, 101, 116, 116, 105, 110, 103, 78, 97, 109, 101, 32, 47, 32, 68, 101, 115, 99, 114, 105, 98, 101, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 83, 104, 111, 119, 83, 112, 101, 99, 105, 97, 108, 70, 111, 114, 109, 10, + 35, 32, 34, 83, 72, 79, 87, 32, 99, 97, 116, 46, 116, 98, 108, 34, 32, 115, 116, 105, 108, 108, 32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97, 32, 116, 97, 98, 108, 101, 32, 40, 100, 101, 112, 114, 101, 99, 97, 116, 101, 100, 41, 44, 32, 98, 117, 116, 32, 105, 115, 32, 115, 117, 112, 112, 114, 101, 115, 115, 101, 100, 32, 102, 114, 111, 109, 32, 97, 117, 116, 111, 99, 111, 109, 112, 108, 101, 116, 101, 32, 40, 115, 101, 101, 32, 109, 97, 116, 99, 104, 101, 114, 46, 99, 112, 112, 41, 32, 115, 111, 32, 83, 72, 79, 87, 10, + 35, 32, 100, 111, 101, 115, 32, 110, 111, 116, 32, 99, 111, 109, 112, 108, 101, 116, 101, 32, 113, 117, 97, 108, 105, 102, 105, 101, 100, 32, 116, 97, 98, 108, 101, 115, 32, 40, 117, 110, 108, 105, 107, 101, 32, 68, 69, 83, 67, 82, 73, 66, 69, 44, 32, 119, 104, 105, 99, 104, 32, 100, 111, 101, 115, 41, 46, 32, 87, 114, 97, 112, 115, 32, 116, 104, 101, 32, 103, 101, 110, 101, 114, 105, 99, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 40, 115, 101, 108, 101, 99, 116, 46, 103, 114, 97, 109, 41, 46, 10, + 83, 104, 111, 119, 68, 101, 112, 114, 101, 99, 97, 116, 101, 100, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 60, 45, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 78, 97, 109, 101, 10, + 35, 32, 65, 32, 98, 97, 114, 101, 32, 105, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 105, 115, 32, 115, 117, 103, 103, 101, 115, 116, 101, 100, 32, 97, 115, 32, 97, 32, 115, 101, 116, 116, 105, 110, 103, 32, 110, 97, 109, 101, 32, 40, 83, 101, 116, 116, 105, 110, 103, 78, 97, 109, 101, 32, 105, 115, 32, 111, 118, 101, 114, 114, 105, 100, 100, 101, 110, 32, 116, 111, 32, 83, 85, 71, 71, 69, 83, 84, 95, 83, 69, 84, 84, 73, 78, 71, 95, 78, 65, 77, 69, 41, 46, 10, + 83, 104, 111, 119, 83, 101, 116, 116, 105, 110, 103, 78, 97, 109, 101, 32, 60, 45, 32, 83, 101, 116, 116, 105, 110, 103, 78, 97, 109, 101, 10, + 35, 32, 84, 104, 101, 32, 115, 112, 101, 99, 105, 97, 108, 32, 77, 121, 83, 81, 76, 32, 102, 111, 114, 109, 115, 32, 97, 114, 101, 32, 111, 110, 108, 121, 32, 104, 101, 114, 101, 32, 115, 111, 32, 97, 117, 116, 111, 99, 111, 109, 112, 108, 101, 116, 101, 32, 115, 117, 103, 103, 101, 115, 116, 115, 32, 116, 104, 101, 115, 101, 32, 107, 101, 121, 119, 111, 114, 100, 115, 32, 97, 102, 116, 101, 114, 32, 83, 72, 79, 87, 59, 32, 112, 97, 114, 115, 105, 110, 103, 32, 111, 102, 32, 101, 46, 103, 46, 10, + 35, 32, 34, 83, 72, 79, 87, 32, 100, 97, 116, 97, 98, 97, 115, 101, 115, 34, 32, 114, 101, 115, 111, 108, 118, 101, 115, 32, 116, 104, 114, 111, 117, 103, 104, 32, 83, 104, 111, 119, 83, 101, 116, 116, 105, 110, 103, 78, 97, 109, 101, 32, 40, 116, 104, 101, 32, 105, 100, 101, 110, 116, 105, 102, 105, 101, 114, 41, 32, 97, 98, 111, 118, 101, 44, 32, 119, 104, 105, 99, 104, 32, 116, 104, 101, 32, 116, 114, 97, 110, 115, 102, 111, 114, 109, 101, 114, 32, 115, 112, 101, 99, 105, 97, 108, 45, 99, 97, 115, 101, 115, 46, 10, + 83, 104, 111, 119, 83, 112, 101, 99, 105, 97, 108, 70, 111, 114, 109, 32, 60, 45, 32, 39, 68, 65, 84, 65, 66, 65, 83, 69, 83, 39, 32, 47, 32, 39, 83, 67, 72, 69, 77, 65, 83, 39, 32, 47, 32, 39, 84, 65, 66, 76, 69, 83, 39, 32, 47, 32, 39, 86, 65, 82, 73, 65, 66, 76, 69, 83, 39, 10, + 68, 101, 115, 99, 114, 105, 98, 101, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 68, 101, 115, 99, 114, 105, 98, 101, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 47, 32, 68, 101, 115, 99, 114, 105, 98, 101, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 68, 101, 115, 99, 114, 105, 98, 101, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 60, 45, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 10, + 68, 101, 115, 99, 114, 105, 98, 101, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 83, 117, 109, 109, 97, 114, 105, 122, 101, 32, 60, 45, 32, 83, 117, 109, 109, 97, 114, 105, 122, 101, 82, 117, 108, 101, 10, + 83, 117, 109, 109, 97, 114, 105, 122, 101, 82, 117, 108, 101, 32, 60, 45, 32, 39, 83, 85, 77, 77, 65, 82, 73, 90, 69, 39, 10, + 83, 104, 111, 119, 79, 114, 68, 101, 115, 99, 114, 105, 98, 101, 32, 60, 45, 32, 83, 104, 111, 119, 82, 117, 108, 101, 32, 47, 32, 68, 101, 115, 99, 114, 105, 98, 101, 82, 117, 108, 101, 10, + 83, 104, 111, 119, 82, 117, 108, 101, 32, 60, 45, 32, 39, 83, 72, 79, 87, 39, 10, + 68, 101, 115, 99, 114, 105, 98, 101, 82, 117, 108, 101, 32, 60, 45, 32, 68, 101, 115, 99, 114, 105, 98, 101, 76, 111, 110, 103, 82, 117, 108, 101, 32, 47, 32, 68, 101, 115, 99, 82, 117, 108, 101, 10, + 68, 101, 115, 99, 114, 105, 98, 101, 76, 111, 110, 103, 82, 117, 108, 101, 32, 60, 45, 32, 39, 68, 69, 83, 67, 82, 73, 66, 69, 39, 10, + 68, 101, 115, 99, 82, 117, 108, 101, 32, 60, 45, 32, 39, 68, 69, 83, 67, 39, 10, + 68, 101, 116, 97, 99, 104, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 68, 69, 84, 65, 67, 72, 39, 32, 68, 97, 116, 97, 98, 97, 115, 101, 63, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 67, 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, 10, + 68, 114, 111, 112, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 68, 82, 79, 80, 39, 32, 68, 114, 111, 112, 69, 110, 116, 114, 105, 101, 115, 32, 68, 114, 111, 112, 66, 101, 104, 97, 118, 105, 111, 114, 63, 10, + 68, 114, 111, 112, 69, 110, 116, 114, 105, 101, 115, 32, 60, 45, 10, + 9, 68, 114, 111, 112, 84, 97, 98, 108, 101, 32, 47, 10, + 9, 68, 114, 111, 112, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 47, 10, + 9, 68, 114, 111, 112, 70, 117, 110, 99, 116, 105, 111, 110, 32, 47, 10, + 9, 68, 114, 111, 112, 83, 99, 104, 101, 109, 97, 32, 47, 10, + 9, 68, 114, 111, 112, 73, 110, 100, 101, 120, 32, 47, 10, + 9, 68, 114, 111, 112, 83, 101, 113, 117, 101, 110, 99, 101, 32, 47, 10, + 9, 68, 114, 111, 112, 67, 111, 108, 108, 97, 116, 105, 111, 110, 32, 47, 10, + 9, 68, 114, 111, 112, 84, 121, 112, 101, 32, 47, 10, + 9, 68, 114, 111, 112, 83, 101, 99, 114, 101, 116, 32, 47, 10, + 9, 68, 114, 111, 112, 84, 114, 105, 103, 103, 101, 114, 10, + 68, 114, 111, 112, 84, 114, 105, 103, 103, 101, 114, 32, 60, 45, 32, 39, 84, 82, 73, 71, 71, 69, 82, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 84, 114, 105, 103, 103, 101, 114, 78, 97, 109, 101, 32, 39, 79, 78, 39, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 10, + 68, 114, 111, 112, 84, 97, 98, 108, 101, 32, 60, 45, 32, 84, 97, 98, 108, 101, 79, 114, 86, 105, 101, 119, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 41, 10, + 68, 114, 111, 112, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 67, 111, 109, 109, 101, 110, 116, 77, 97, 99, 114, 111, 84, 97, 98, 108, 101, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 41, 10, + 68, 114, 111, 112, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 70, 117, 110, 99, 116, 105, 111, 110, 84, 121, 112, 101, 77, 97, 99, 114, 111, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 70, 117, 110, 99, 116, 105, 111, 110, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 41, 10, + 68, 114, 111, 112, 83, 99, 104, 101, 109, 97, 32, 60, 45, 32, 39, 83, 67, 72, 69, 77, 65, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 41, 10, + 68, 114, 111, 112, 73, 110, 100, 101, 120, 32, 60, 45, 32, 39, 73, 78, 68, 69, 88, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 81, 117, 97, 108, 105, 102, 105, 101, 100, 73, 110, 100, 101, 120, 78, 97, 109, 101, 41, 10, + 81, 117, 97, 108, 105, 102, 105, 101, 100, 73, 110, 100, 101, 120, 78, 97, 109, 101, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 73, 110, 100, 101, 120, 32, 47, 32, 83, 99, 104, 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 73, 110, 100, 101, 120, 32, 47, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 73, 110, 100, 101, 120, 78, 97, 109, 101, 83, 116, 114, 105, 110, 103, 10, + 81, 117, 97, 108, 105, 102, 105, 101, 100, 73, 110, 100, 101, 120, 78, 97, 109, 101, 83, 116, 114, 105, 110, 103, 32, 60, 45, 32, 73, 110, 100, 101, 120, 78, 97, 109, 101, 10, + 83, 99, 104, 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 73, 110, 100, 101, 120, 32, 60, 45, 32, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 73, 110, 100, 101, 120, 78, 97, 109, 101, 10, + 67, 97, 116, 97, 108, 111, 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 73, 110, 100, 101, 120, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 73, 110, 100, 101, 120, 78, 97, 109, 101, 10, + 68, 114, 111, 112, 83, 101, 113, 117, 101, 110, 99, 101, 32, 60, 45, 32, 39, 83, 69, 81, 85, 69, 78, 67, 69, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 81, 117, 97, 108, 105, 102, 105, 101, 100, 83, 101, 113, 117, 101, 110, 99, 101, 78, 97, 109, 101, 41, 10, + 68, 114, 111, 112, 67, 111, 108, 108, 97, 116, 105, 111, 110, 32, 60, 45, 32, 39, 67, 79, 76, 76, 65, 84, 73, 79, 78, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 67, 111, 108, 108, 97, 116, 105, 111, 110, 78, 97, 109, 101, 41, 10, + 68, 114, 111, 112, 84, 121, 112, 101, 32, 60, 45, 32, 39, 84, 89, 80, 69, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 121, 112, 101, 78, 97, 109, 101, 41, 10, + 68, 114, 111, 112, 83, 101, 99, 114, 101, 116, 32, 60, 45, 32, 84, 101, 109, 112, 111, 114, 97, 114, 121, 63, 32, 39, 83, 69, 67, 82, 69, 84, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 83, 101, 99, 114, 101, 116, 78, 97, 109, 101, 32, 68, 114, 111, 112, 83, 101, 99, 114, 101, 116, 83, 116, 111, 114, 97, 103, 101, 63, 10, + 84, 97, 98, 108, 101, 79, 114, 86, 105, 101, 119, 32, 60, 45, 32, 67, 111, 109, 109, 101, 110, 116, 84, 97, 98, 108, 101, 32, 47, 32, 67, 111, 109, 109, 101, 110, 116, 86, 105, 101, 119, 32, 47, 32, 77, 97, 116, 101, 114, 105, 97, 108, 105, 122, 101, 100, 86, 105, 101, 119, 69, 110, 116, 114, 121, 10, + 77, 97, 116, 101, 114, 105, 97, 108, 105, 122, 101, 100, 86, 105, 101, 119, 69, 110, 116, 114, 121, 32, 60, 45, 32, 39, 77, 65, 84, 69, 82, 73, 65, 76, 73, 90, 69, 68, 39, 32, 39, 86, 73, 69, 87, 39, 10, + 70, 117, 110, 99, 116, 105, 111, 110, 84, 121, 112, 101, 77, 97, 99, 114, 111, 32, 60, 45, 32, 70, 117, 110, 99, 116, 105, 111, 110, 84, 121, 112, 101, 77, 97, 99, 114, 111, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 70, 117, 110, 99, 116, 105, 111, 110, 84, 121, 112, 101, 70, 117, 110, 99, 116, 105, 111, 110, 10, + 70, 117, 110, 99, 116, 105, 111, 110, 84, 121, 112, 101, 77, 97, 99, 114, 111, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 77, 65, 67, 82, 79, 39, 10, + 70, 117, 110, 99, 116, 105, 111, 110, 84, 121, 112, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 70, 85, 78, 67, 84, 73, 79, 78, 39, 10, + 68, 114, 111, 112, 66, 101, 104, 97, 118, 105, 111, 114, 32, 60, 45, 32, 67, 97, 115, 99, 97, 100, 101, 68, 114, 111, 112, 66, 101, 104, 97, 118, 105, 111, 114, 32, 47, 32, 82, 101, 115, 116, 114, 105, 99, 116, 68, 114, 111, 112, 66, 101, 104, 97, 118, 105, 111, 114, 10, + 67, 97, 115, 99, 97, 100, 101, 68, 114, 111, 112, 66, 101, 104, 97, 118, 105, 111, 114, 32, 60, 45, 32, 39, 67, 65, 83, 67, 65, 68, 69, 39, 10, + 82, 101, 115, 116, 114, 105, 99, 116, 68, 114, 111, 112, 66, 101, 104, 97, 118, 105, 111, 114, 32, 60, 45, 32, 39, 82, 69, 83, 84, 82, 73, 67, 84, 39, 10, + 73, 102, 69, 120, 105, 115, 116, 115, 32, 60, 45, 32, 39, 73, 70, 39, 32, 39, 69, 88, 73, 83, 84, 83, 39, 10, + 68, 114, 111, 112, 83, 101, 99, 114, 101, 116, 83, 116, 111, 114, 97, 103, 101, 32, 60, 45, 32, 39, 70, 82, 79, 77, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 69, 120, 101, 99, 117, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 69, 88, 69, 67, 85, 84, 69, 39, 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 32, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 63, 10, + 69, 120, 112, 108, 97, 105, 110, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 69, 88, 80, 76, 65, 73, 78, 39, 32, 65, 110, 97, 108, 121, 122, 101, 75, 101, 121, 119, 111, 114, 100, 63, 32, 69, 120, 112, 108, 97, 105, 110, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 63, 32, 69, 120, 112, 108, 97, 105, 110, 97, 98, 108, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 115, 10, + 69, 120, 112, 108, 97, 105, 110, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 108, 97, 105, 110, 79, 112, 116, 105, 111, 110, 41, 41, 10, + 69, 120, 112, 108, 97, 105, 110, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 69, 120, 112, 108, 97, 105, 110, 79, 112, 116, 105, 111, 110, 78, 97, 109, 101, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 10, + 69, 120, 112, 108, 97, 105, 110, 79, 112, 116, 105, 111, 110, 78, 97, 109, 101, 32, 60, 45, 32, 65, 110, 97, 108, 121, 122, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 67, 111, 108, 73, 100, 32, 47, 32, 70, 117, 110, 99, 78, 97, 109, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 84, 121, 112, 101, 78, 97, 109, 101, 75, 101, 121, 119, 111, 114, 100, 10, + 69, 120, 112, 108, 97, 105, 110, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 10, + 69, 120, 112, 108, 97, 105, 110, 97, 98, 108, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 115, 32, 60, 45, 10, + 32, 32, 32, 32, 65, 108, 116, 101, 114, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 65, 110, 97, 108, 121, 122, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 67, 97, 108, 108, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 67, 104, 101, 99, 107, 112, 111, 105, 110, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 67, 111, 112, 121, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 67, 114, 101, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 68, 101, 97, 108, 108, 111, 99, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 68, 101, 108, 101, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 32, 32, 32, 32, 68, 114, 111, 112, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, - 32, 32, 32, 32, 69, 120, 101, 99, 117, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, - 32, 47, 10, 32, 32, 32, 32, 73, 110, 115, 101, 114, 116, 83, 116, 97, 116, 101, 109, 101, - 110, 116, 32, 47, 10, 32, 32, 32, 32, 76, 111, 97, 100, 83, 116, 97, 116, 101, 109, 101, - 110, 116, 32, 47, 10, 32, 32, 32, 32, 77, 101, 114, 103, 101, 73, 110, 116, 111, 83, 116, - 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 32, 32, 32, 32, 80, 114, 97, 103, 109, 97, - 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 32, 32, 32, 32, 80, 114, 101, 112, - 97, 114, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 32, 32, 32, 32, 69, - 120, 112, 108, 97, 105, 110, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, - 116, 32, 47, 10, 32, 32, 32, 32, 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 83, - 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 32, 32, 32, 32, 85, 112, 100, 97, 116, - 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 32, 32, 32, 32, 86, 97, 99, - 117, 117, 109, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 32, 32, 32, 32, 83, - 101, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, 32, 32, 32, 32, 82, 101, - 115, 101, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 10, 69, 120, 112, 111, 114, 116, 83, - 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 69, 88, 80, 79, 82, 84, 39, - 32, 39, 68, 65, 84, 65, 66, 65, 83, 69, 39, 32, 69, 120, 112, 111, 114, 116, 83, 111, - 117, 114, 99, 101, 63, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, - 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 76, 105, 115, - 116, 63, 10, 69, 120, 112, 111, 114, 116, 83, 111, 117, 114, 99, 101, 32, 60, 45, 32, 67, - 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, 32, 39, 84, 79, 39, 10, 73, 109, 112, 111, - 114, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 73, 77, 80, 79, - 82, 84, 39, 32, 39, 68, 65, 84, 65, 66, 65, 83, 69, 39, 32, 83, 116, 114, 105, 110, - 103, 76, 105, 116, 101, 114, 97, 108, 10, 67, 111, 108, 117, 109, 110, 82, 101, 102, 101, 114, - 101, 110, 99, 101, 32, 60, 45, 32, 78, 101, 115, 116, 101, 100, 83, 99, 104, 101, 109, 97, - 84, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 47, 32, 67, 97, - 116, 97, 108, 111, 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 84, - 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 47, 32, 83, 99, 104, - 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 84, 97, 98, 108, 101, 67, 111, 108, 117, - 109, 110, 78, 97, 109, 101, 32, 47, 32, 84, 97, 98, 108, 101, 82, 101, 115, 101, 114, 118, - 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 47, 32, 78, 101, 115, 116, 101, - 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 10, 35, 32, 97, 32, 99, 111, 108, 117, - 109, 110, 32, 111, 102, 32, 97, 32, 116, 97, 98, 108, 101, 32, 105, 110, 32, 97, 32, 110, - 101, 115, 116, 101, 100, 32, 115, 99, 104, 101, 109, 97, 32, 99, 104, 97, 105, 110, 32, 45, - 32, 102, 105, 118, 101, 32, 111, 114, 32, 109, 111, 114, 101, 32, 99, 111, 109, 112, 111, 110, - 101, 110, 116, 115, 44, 32, 119, 104, 101, 114, 101, 32, 116, 104, 101, 32, 113, 117, 97, 108, - 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 105, 115, 32, 100, 101, 101, 112, 101, 114, 32, - 116, 104, 97, 110, 10, 35, 32, 99, 97, 116, 97, 108, 111, 103, 32, 43, 32, 115, 99, 104, - 101, 109, 97, 32, 97, 110, 100, 32, 105, 116, 32, 105, 115, 32, 110, 111, 32, 108, 111, 110, - 103, 101, 114, 32, 112, 111, 115, 105, 116, 105, 111, 110, 97, 108, 32, 119, 104, 105, 99, 104, - 32, 99, 111, 109, 112, 111, 110, 101, 110, 116, 115, 32, 97, 114, 101, 32, 115, 99, 104, 101, - 109, 97, 115, 32, 97, 110, 100, 32, 119, 104, 105, 99, 104, 32, 105, 115, 32, 116, 104, 101, - 32, 116, 97, 98, 108, 101, 10, 78, 101, 115, 116, 101, 100, 83, 99, 104, 101, 109, 97, 84, - 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 60, 45, 32, 67, 97, - 116, 97, 108, 111, 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, - 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, - 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, - 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, - 118, 101, 100, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, - 111, 110, 43, 32, 82, 101, 115, 101, 114, 118, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, - 109, 101, 10, 67, 97, 116, 97, 108, 111, 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, - 104, 101, 109, 97, 84, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, - 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, - 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 81, 117, - 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, - 84, 97, 98, 108, 101, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, - 101, 115, 101, 114, 118, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 10, 83, 99, - 104, 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 84, 97, 98, 108, 101, 67, 111, 108, - 117, 109, 110, 78, 97, 109, 101, 32, 60, 45, 32, 83, 99, 104, 101, 109, 97, 81, 117, 97, - 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 84, - 97, 98, 108, 101, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, - 115, 101, 114, 118, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 10, 84, 97, 98, - 108, 101, 82, 101, 115, 101, 114, 118, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, - 32, 60, 45, 32, 84, 97, 98, 108, 101, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, - 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, - 101, 10, 70, 117, 110, 99, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, - 32, 60, 45, 32, 70, 117, 110, 99, 116, 105, 111, 110, 73, 100, 101, 110, 116, 105, 102, 105, - 101, 114, 32, 70, 117, 110, 99, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, - 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 87, 105, 116, 104, 105, 110, 71, 114, 111, - 117, 112, 67, 108, 97, 117, 115, 101, 63, 32, 70, 105, 108, 116, 101, 114, 67, 108, 97, 117, - 115, 101, 63, 32, 69, 120, 112, 111, 114, 116, 67, 108, 97, 117, 115, 101, 63, 32, 79, 118, - 101, 114, 67, 108, 97, 117, 115, 101, 63, 10, 70, 117, 110, 99, 116, 105, 111, 110, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, 45, - 32, 80, 97, 114, 101, 110, 115, 40, 70, 117, 110, 99, 116, 105, 111, 110, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 76, 105, 115, 116, 41, 10, - 70, 117, 110, 99, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 65, 114, - 103, 117, 109, 101, 110, 116, 76, 105, 115, 116, 32, 60, 45, 32, 68, 105, 115, 116, 105, 110, - 99, 116, 79, 114, 65, 108, 108, 63, 32, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, - 117, 109, 101, 110, 116, 76, 105, 115, 116, 63, 32, 79, 114, 100, 101, 114, 66, 121, 67, 108, - 97, 117, 115, 101, 63, 32, 73, 103, 110, 111, 114, 101, 79, 114, 82, 101, 115, 112, 101, 99, - 116, 78, 117, 108, 108, 115, 63, 10, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, - 109, 101, 110, 116, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 70, 117, 110, - 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 41, 10, 70, 117, 110, 99, 116, - 105, 111, 110, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 67, 97, 116, - 97, 108, 111, 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 70, 117, - 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 32, 47, 32, 83, 99, 104, 101, 109, 97, 82, - 101, 115, 101, 114, 118, 101, 100, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 32, - 47, 32, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 65, 115, 81, 117, 97, 108, - 105, 102, 105, 101, 100, 78, 97, 109, 101, 10, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, - 109, 101, 65, 115, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 32, 60, 45, - 32, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 10, 67, 97, 116, 97, 108, 111, - 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 70, 117, 110, 99, 116, - 105, 111, 110, 78, 97, 109, 101, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 81, 117, - 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, - 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 42, - 32, 82, 101, 115, 101, 114, 118, 101, 100, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, - 101, 10, 83, 99, 104, 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 70, 117, 110, 99, - 116, 105, 111, 110, 78, 97, 109, 101, 32, 60, 45, 32, 83, 99, 104, 101, 109, 97, 81, 117, - 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, - 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 10, 68, 105, 115, 116, 105, 110, 99, - 116, 79, 114, 65, 108, 108, 32, 60, 45, 32, 68, 105, 115, 116, 105, 110, 99, 116, 75, 101, - 121, 119, 111, 114, 100, 32, 47, 32, 65, 108, 108, 75, 101, 121, 119, 111, 114, 100, 10, 68, - 105, 115, 116, 105, 110, 99, 116, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 68, - 73, 83, 84, 73, 78, 67, 84, 39, 10, 65, 108, 108, 75, 101, 121, 119, 111, 114, 100, 32, - 60, 45, 32, 39, 65, 76, 76, 39, 10, 69, 120, 112, 111, 114, 116, 67, 108, 97, 117, 115, - 101, 32, 60, 45, 32, 39, 69, 88, 80, 79, 82, 84, 95, 83, 84, 65, 84, 69, 39, 10, - 87, 105, 116, 104, 105, 110, 71, 114, 111, 117, 112, 67, 108, 97, 117, 115, 101, 32, 60, 45, - 32, 39, 87, 73, 84, 72, 73, 78, 39, 32, 39, 71, 82, 79, 85, 80, 39, 32, 80, 97, - 114, 101, 110, 115, 40, 79, 114, 100, 101, 114, 66, 121, 67, 108, 97, 117, 115, 101, 41, 10, - 70, 105, 108, 116, 101, 114, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 70, 73, 76, - 84, 69, 82, 39, 32, 70, 105, 108, 116, 101, 114, 67, 108, 97, 117, 115, 101, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 10, 70, 105, 108, 116, 101, 114, 67, 108, 97, 117, 115, 101, - 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, - 40, 70, 105, 108, 116, 101, 114, 67, 108, 97, 117, 115, 101, 67, 111, 110, 116, 101, 110, 116, - 115, 41, 10, 70, 105, 108, 116, 101, 114, 67, 108, 97, 117, 115, 101, 67, 111, 110, 116, 101, - 110, 116, 115, 32, 60, 45, 32, 39, 87, 72, 69, 82, 69, 39, 63, 32, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 10, 73, 103, 110, 111, 114, 101, 79, 114, 82, 101, 115, 112, 101, - 99, 116, 78, 117, 108, 108, 115, 32, 60, 45, 32, 73, 103, 110, 111, 114, 101, 78, 117, 108, - 108, 115, 32, 47, 32, 82, 101, 115, 112, 101, 99, 116, 78, 117, 108, 108, 115, 10, 73, 103, - 110, 111, 114, 101, 78, 117, 108, 108, 115, 32, 60, 45, 32, 39, 73, 71, 78, 79, 82, 69, - 39, 32, 39, 78, 85, 76, 76, 83, 39, 10, 82, 101, 115, 112, 101, 99, 116, 78, 117, 108, - 108, 115, 32, 60, 45, 32, 39, 82, 69, 83, 80, 69, 67, 84, 39, 32, 39, 78, 85, 76, - 76, 83, 39, 10, 80, 97, 114, 101, 110, 116, 104, 101, 115, 105, 115, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, - 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 63, 41, 10, 76, 105, 116, 101, 114, - 97, 108, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 83, 116, 114, 105, - 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 78, 117, 109, 98, 101, 114, 76, 105, - 116, 101, 114, 97, 108, 32, 47, 32, 67, 111, 110, 115, 116, 97, 110, 116, 76, 105, 116, 101, - 114, 97, 108, 10, 67, 111, 110, 115, 116, 97, 110, 116, 76, 105, 116, 101, 114, 97, 108, 32, - 60, 45, 32, 78, 117, 108, 108, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 84, 114, 117, - 101, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 70, 97, 108, 115, 101, 76, 105, 116, 101, - 114, 97, 108, 10, 78, 117, 108, 108, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 39, - 78, 85, 76, 76, 39, 10, 84, 114, 117, 101, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, - 32, 39, 84, 82, 85, 69, 39, 10, 70, 97, 108, 115, 101, 76, 105, 116, 101, 114, 97, 108, - 32, 60, 45, 32, 39, 70, 65, 76, 83, 69, 39, 10, 67, 97, 115, 116, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 67, 97, 115, 116, 79, 114, 84, 114, 121, 67, - 97, 115, 116, 32, 80, 97, 114, 101, 110, 115, 40, 67, 97, 115, 116, 65, 114, 103, 117, 109, - 101, 110, 116, 115, 41, 10, 67, 97, 115, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, - 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 65, 83, 39, 32, 84, - 121, 112, 101, 10, 67, 97, 115, 116, 79, 114, 84, 114, 121, 67, 97, 115, 116, 32, 60, 45, - 32, 67, 97, 115, 116, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 84, 114, 121, 67, 97, - 115, 116, 75, 101, 121, 119, 111, 114, 100, 10, 67, 97, 115, 116, 75, 101, 121, 119, 111, 114, - 100, 32, 60, 45, 32, 39, 67, 65, 83, 84, 39, 10, 84, 114, 121, 67, 97, 115, 116, 75, - 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 84, 82, 89, 95, 67, 65, 83, 84, 39, - 10, 67, 111, 108, 73, 100, 68, 111, 116, 32, 60, 45, 32, 67, 111, 108, 73, 100, 32, 39, - 46, 39, 10, 83, 116, 97, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, - 32, 83, 116, 97, 114, 81, 117, 97, 108, 105, 102, 105, 101, 114, 76, 105, 115, 116, 63, 32, - 39, 42, 39, 32, 69, 120, 99, 108, 117, 100, 101, 76, 105, 115, 116, 63, 32, 82, 101, 112, - 108, 97, 99, 101, 76, 105, 115, 116, 63, 32, 82, 101, 110, 97, 109, 101, 76, 105, 115, 116, - 63, 10, 83, 116, 97, 114, 81, 117, 97, 108, 105, 102, 105, 101, 114, 76, 105, 115, 116, 32, - 60, 45, 32, 67, 111, 108, 73, 100, 68, 111, 116, 43, 10, 69, 120, 99, 108, 117, 100, 101, - 76, 105, 115, 116, 32, 60, 45, 32, 69, 120, 99, 108, 117, 100, 101, 79, 114, 69, 120, 99, - 101, 112, 116, 32, 69, 120, 99, 108, 117, 100, 101, 78, 97, 109, 101, 115, 10, 69, 120, 99, - 108, 117, 100, 101, 79, 114, 69, 120, 99, 101, 112, 116, 32, 60, 45, 32, 39, 69, 88, 67, - 76, 85, 68, 69, 39, 32, 47, 32, 39, 69, 88, 67, 69, 80, 84, 39, 10, 69, 120, 99, - 108, 117, 100, 101, 78, 97, 109, 101, 115, 32, 60, 45, 32, 69, 120, 99, 108, 117, 100, 101, - 78, 97, 109, 101, 76, 105, 115, 116, 32, 47, 32, 69, 120, 99, 108, 117, 100, 101, 78, 97, - 109, 101, 83, 105, 110, 103, 108, 101, 10, 69, 120, 99, 108, 117, 100, 101, 78, 97, 109, 101, - 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, - 69, 120, 99, 108, 117, 100, 101, 78, 97, 109, 101, 41, 41, 10, 69, 120, 99, 108, 117, 100, - 101, 78, 97, 109, 101, 83, 105, 110, 103, 108, 101, 32, 60, 45, 32, 69, 120, 99, 108, 117, - 100, 101, 78, 97, 109, 101, 10, 69, 120, 99, 108, 117, 100, 101, 78, 97, 109, 101, 32, 60, - 45, 32, 69, 120, 99, 108, 117, 100, 101, 68, 111, 116, 116, 101, 100, 78, 97, 109, 101, 32, - 47, 32, 69, 120, 99, 108, 117, 100, 101, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 10, - 69, 120, 99, 108, 117, 100, 101, 68, 111, 116, 116, 101, 100, 78, 97, 109, 101, 32, 60, 45, - 32, 68, 111, 116, 116, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 69, 120, - 99, 108, 117, 100, 101, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 60, 45, 32, 67, - 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 10, 82, 101, 112, 108, 97, 99, 101, - 76, 105, 115, 116, 32, 60, 45, 32, 39, 82, 69, 80, 76, 65, 67, 69, 39, 32, 82, 101, - 112, 108, 97, 99, 101, 69, 110, 116, 114, 105, 101, 115, 10, 82, 101, 112, 108, 97, 99, 101, - 69, 110, 116, 114, 105, 101, 115, 32, 60, 45, 32, 82, 101, 112, 108, 97, 99, 101, 69, 110, - 116, 114, 121, 83, 105, 110, 103, 108, 101, 32, 47, 32, 82, 101, 112, 108, 97, 99, 101, 69, - 110, 116, 114, 121, 76, 105, 115, 116, 10, 82, 101, 112, 108, 97, 99, 101, 69, 110, 116, 114, - 121, 83, 105, 110, 103, 108, 101, 32, 60, 45, 32, 82, 101, 112, 108, 97, 99, 101, 69, 110, - 116, 114, 121, 10, 82, 101, 112, 108, 97, 99, 101, 69, 110, 116, 114, 121, 76, 105, 115, 116, - 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 82, 101, 112, 108, - 97, 99, 101, 69, 110, 116, 114, 121, 41, 41, 10, 82, 101, 112, 108, 97, 99, 101, 69, 110, - 116, 114, 121, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 65, - 83, 39, 32, 67, 111, 108, 117, 109, 110, 82, 101, 102, 101, 114, 101, 110, 99, 101, 10, 82, - 101, 110, 97, 109, 101, 76, 105, 115, 116, 32, 60, 45, 32, 39, 82, 69, 78, 65, 77, 69, - 39, 32, 82, 101, 110, 97, 109, 101, 69, 110, 116, 114, 105, 101, 115, 10, 82, 101, 110, 97, - 109, 101, 69, 110, 116, 114, 105, 101, 115, 32, 60, 45, 32, 82, 101, 110, 97, 109, 101, 69, - 110, 116, 114, 121, 76, 105, 115, 116, 32, 47, 32, 83, 105, 110, 103, 108, 101, 82, 101, 110, - 97, 109, 101, 69, 110, 116, 114, 121, 10, 82, 101, 110, 97, 109, 101, 69, 110, 116, 114, 121, - 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, - 82, 101, 110, 97, 109, 101, 69, 110, 116, 114, 121, 41, 41, 10, 83, 105, 110, 103, 108, 101, - 82, 101, 110, 97, 109, 101, 69, 110, 116, 114, 121, 32, 60, 45, 32, 82, 101, 110, 97, 109, - 101, 69, 110, 116, 114, 121, 10, 82, 101, 110, 97, 109, 101, 69, 110, 116, 114, 121, 32, 60, - 45, 32, 69, 120, 99, 108, 117, 100, 101, 78, 97, 109, 101, 32, 39, 65, 83, 39, 32, 73, - 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 83, 117, 98, 113, 117, 101, 114, 121, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 83, 117, 98, 113, 117, 101, 114, 121, - 78, 111, 116, 63, 32, 83, 117, 98, 113, 117, 101, 114, 121, 69, 120, 105, 115, 116, 115, 63, - 32, 83, 117, 98, 113, 117, 101, 114, 121, 82, 101, 102, 101, 114, 101, 110, 99, 101, 10, 83, - 117, 98, 113, 117, 101, 114, 121, 78, 111, 116, 32, 60, 45, 32, 39, 78, 79, 84, 39, 10, - 83, 117, 98, 113, 117, 101, 114, 121, 69, 120, 105, 115, 116, 115, 32, 60, 45, 32, 39, 69, - 88, 73, 83, 84, 83, 39, 10, 67, 97, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, - 110, 32, 60, 45, 32, 39, 67, 65, 83, 69, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, - 111, 110, 63, 32, 67, 97, 115, 101, 87, 104, 101, 110, 84, 104, 101, 110, 43, 32, 67, 97, - 115, 101, 69, 108, 115, 101, 63, 32, 39, 69, 78, 68, 39, 10, 67, 97, 115, 101, 87, 104, - 101, 110, 84, 104, 101, 110, 32, 60, 45, 32, 39, 87, 72, 69, 78, 39, 32, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 32, 39, 84, 72, 69, 78, 39, 32, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 10, 67, 97, 115, 101, 69, 108, 115, 101, 32, 60, 45, 32, 39, 69, - 76, 83, 69, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 84, 121, 112, 101, - 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 84, 121, 112, 101, 32, 83, 116, 114, 105, - 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, 73, 110, 116, 101, 114, 118, 97, 108, 76, 105, - 116, 101, 114, 97, 108, 32, 60, 45, 32, 39, 73, 78, 84, 69, 82, 86, 65, 76, 39, 32, - 73, 110, 116, 101, 114, 118, 97, 108, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 73, 110, - 116, 101, 114, 118, 97, 108, 63, 10, 73, 110, 116, 101, 114, 118, 97, 108, 80, 97, 114, 97, - 109, 101, 116, 101, 114, 32, 60, 45, 32, 73, 110, 116, 101, 114, 118, 97, 108, 83, 116, 114, - 105, 110, 103, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 47, 32, 78, 117, 109, 98, 101, - 114, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 80, 97, 114, 101, 110, 115, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 10, 73, 110, 116, 101, 114, 118, 97, 108, 83, 116, 114, 105, - 110, 103, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 60, 45, 32, 83, 116, 114, 105, 110, - 103, 76, 105, 116, 101, 114, 97, 108, 10, 70, 114, 97, 109, 101, 67, 108, 97, 117, 115, 101, - 32, 60, 45, 32, 70, 114, 97, 109, 105, 110, 103, 32, 70, 114, 97, 109, 101, 69, 120, 116, - 101, 110, 116, 32, 87, 105, 110, 100, 111, 119, 69, 120, 99, 108, 117, 100, 101, 67, 108, 97, - 117, 115, 101, 63, 10, 70, 114, 97, 109, 105, 110, 103, 32, 60, 45, 32, 82, 111, 119, 115, - 70, 114, 97, 109, 105, 110, 103, 32, 47, 32, 82, 97, 110, 103, 101, 70, 114, 97, 109, 105, - 110, 103, 32, 47, 32, 71, 114, 111, 117, 112, 115, 70, 114, 97, 109, 105, 110, 103, 10, 82, - 111, 119, 115, 70, 114, 97, 109, 105, 110, 103, 32, 60, 45, 32, 39, 82, 79, 87, 83, 39, - 10, 82, 97, 110, 103, 101, 70, 114, 97, 109, 105, 110, 103, 32, 60, 45, 32, 39, 82, 65, - 78, 71, 69, 39, 10, 71, 114, 111, 117, 112, 115, 70, 114, 97, 109, 105, 110, 103, 32, 60, - 45, 32, 39, 71, 82, 79, 85, 80, 83, 39, 10, 70, 114, 97, 109, 101, 69, 120, 116, 101, - 110, 116, 32, 60, 45, 32, 66, 101, 116, 119, 101, 101, 110, 70, 114, 97, 109, 101, 69, 120, - 116, 101, 110, 116, 32, 47, 32, 83, 105, 110, 103, 108, 101, 70, 114, 97, 109, 101, 69, 120, - 116, 101, 110, 116, 10, 83, 105, 110, 103, 108, 101, 70, 114, 97, 109, 101, 69, 120, 116, 101, - 110, 116, 32, 60, 45, 32, 70, 114, 97, 109, 101, 66, 111, 117, 110, 100, 10, 66, 101, 116, - 119, 101, 101, 110, 70, 114, 97, 109, 101, 69, 120, 116, 101, 110, 116, 32, 60, 45, 32, 39, - 66, 69, 84, 87, 69, 69, 78, 39, 32, 70, 114, 97, 109, 101, 66, 111, 117, 110, 100, 32, - 39, 65, 78, 68, 39, 32, 70, 114, 97, 109, 101, 66, 111, 117, 110, 100, 10, 70, 114, 97, - 109, 101, 66, 111, 117, 110, 100, 32, 60, 45, 32, 70, 114, 97, 109, 101, 85, 110, 98, 111, - 117, 110, 100, 101, 100, 32, 47, 32, 70, 114, 97, 109, 101, 67, 117, 114, 114, 101, 110, 116, - 82, 111, 119, 32, 47, 32, 70, 114, 97, 109, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, - 110, 10, 70, 114, 97, 109, 101, 85, 110, 98, 111, 117, 110, 100, 101, 100, 32, 60, 45, 32, - 39, 85, 78, 66, 79, 85, 78, 68, 69, 68, 39, 32, 80, 114, 101, 99, 101, 100, 105, 110, - 103, 79, 114, 70, 111, 108, 108, 111, 119, 105, 110, 103, 10, 70, 114, 97, 109, 101, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, - 111, 110, 32, 80, 114, 101, 99, 101, 100, 105, 110, 103, 79, 114, 70, 111, 108, 108, 111, 119, - 105, 110, 103, 10, 70, 114, 97, 109, 101, 67, 117, 114, 114, 101, 110, 116, 82, 111, 119, 32, - 60, 45, 32, 39, 67, 85, 82, 82, 69, 78, 84, 39, 32, 39, 82, 79, 87, 39, 10, 80, - 114, 101, 99, 101, 100, 105, 110, 103, 79, 114, 70, 111, 108, 108, 111, 119, 105, 110, 103, 32, - 60, 45, 32, 80, 114, 101, 99, 101, 100, 105, 110, 103, 70, 114, 97, 109, 101, 32, 47, 32, - 70, 111, 108, 108, 111, 119, 105, 110, 103, 70, 114, 97, 109, 101, 10, 80, 114, 101, 99, 101, - 100, 105, 110, 103, 70, 114, 97, 109, 101, 32, 60, 45, 32, 39, 80, 82, 69, 67, 69, 68, - 73, 78, 71, 39, 10, 70, 111, 108, 108, 111, 119, 105, 110, 103, 70, 114, 97, 109, 101, 32, - 60, 45, 32, 39, 70, 79, 76, 76, 79, 87, 73, 78, 71, 39, 10, 87, 105, 110, 100, 111, - 119, 69, 120, 99, 108, 117, 100, 101, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 69, - 88, 67, 76, 85, 68, 69, 39, 32, 87, 105, 110, 100, 111, 119, 69, 120, 99, 108, 117, 100, - 101, 69, 108, 101, 109, 101, 110, 116, 10, 87, 105, 110, 100, 111, 119, 69, 120, 99, 108, 117, - 100, 101, 69, 108, 101, 109, 101, 110, 116, 32, 60, 45, 32, 69, 120, 99, 108, 117, 100, 101, - 67, 117, 114, 114, 101, 110, 116, 82, 111, 119, 32, 47, 32, 69, 120, 99, 108, 117, 100, 101, - 71, 114, 111, 117, 112, 32, 47, 32, 69, 120, 99, 108, 117, 100, 101, 84, 105, 101, 115, 32, - 47, 32, 69, 120, 99, 108, 117, 100, 101, 78, 111, 79, 116, 104, 101, 114, 115, 10, 69, 120, - 99, 108, 117, 100, 101, 67, 117, 114, 114, 101, 110, 116, 82, 111, 119, 32, 60, 45, 32, 39, - 67, 85, 82, 82, 69, 78, 84, 39, 32, 39, 82, 79, 87, 39, 10, 69, 120, 99, 108, 117, - 100, 101, 71, 114, 111, 117, 112, 32, 60, 45, 32, 39, 71, 82, 79, 85, 80, 39, 10, 69, - 120, 99, 108, 117, 100, 101, 84, 105, 101, 115, 32, 60, 45, 32, 39, 84, 73, 69, 83, 39, - 10, 69, 120, 99, 108, 117, 100, 101, 78, 111, 79, 116, 104, 101, 114, 115, 32, 60, 45, 32, - 39, 78, 79, 39, 32, 39, 79, 84, 72, 69, 82, 83, 39, 10, 79, 118, 101, 114, 67, 108, - 97, 117, 115, 101, 32, 60, 45, 32, 39, 79, 86, 69, 82, 39, 32, 87, 105, 110, 100, 111, - 119, 70, 114, 97, 109, 101, 10, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 32, 60, - 45, 32, 80, 97, 114, 101, 110, 115, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 47, - 32, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 68, 101, 102, 105, 110, 105, 116, 105, - 111, 110, 32, 47, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 87, 105, 110, 100, 111, - 119, 70, 114, 97, 109, 101, 10, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 87, 105, 110, - 100, 111, 119, 70, 114, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, - 101, 114, 10, 80, 97, 114, 101, 110, 115, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, - 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, - 41, 10, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 68, 101, 102, 105, 110, 105, 116, - 105, 111, 110, 32, 60, 45, 32, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 78, 97, - 109, 101, 67, 111, 110, 116, 101, 110, 116, 115, 80, 97, 114, 101, 110, 115, 32, 47, 32, 87, - 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 67, 111, 110, 116, 101, 110, 116, 115, 80, 97, - 114, 101, 110, 115, 10, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 78, 97, 109, 101, - 67, 111, 110, 116, 101, 110, 116, 115, 80, 97, 114, 101, 110, 115, 32, 60, 45, 32, 80, 97, - 114, 101, 110, 115, 40, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 78, 97, 109, 101, - 67, 111, 110, 116, 101, 110, 116, 115, 41, 10, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, - 101, 78, 97, 109, 101, 67, 111, 110, 116, 101, 110, 116, 115, 32, 60, 45, 32, 66, 97, 115, - 101, 87, 105, 110, 100, 111, 119, 78, 97, 109, 101, 63, 32, 87, 105, 110, 100, 111, 119, 70, - 114, 97, 109, 101, 67, 111, 110, 116, 101, 110, 116, 115, 10, 87, 105, 110, 100, 111, 119, 70, - 114, 97, 109, 101, 67, 111, 110, 116, 101, 110, 116, 115, 80, 97, 114, 101, 110, 115, 32, 60, - 45, 32, 80, 97, 114, 101, 110, 115, 40, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, - 67, 111, 110, 116, 101, 110, 116, 115, 41, 10, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, - 101, 67, 111, 110, 116, 101, 110, 116, 115, 32, 60, 45, 32, 87, 105, 110, 100, 111, 119, 80, - 97, 114, 116, 105, 116, 105, 111, 110, 63, 32, 79, 114, 100, 101, 114, 66, 121, 67, 108, 97, - 117, 115, 101, 63, 32, 70, 114, 97, 109, 101, 67, 108, 97, 117, 115, 101, 63, 10, 66, 97, - 115, 101, 87, 105, 110, 100, 111, 119, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, - 116, 105, 102, 105, 101, 114, 10, 87, 105, 110, 100, 111, 119, 80, 97, 114, 116, 105, 116, 105, - 111, 110, 32, 60, 45, 32, 39, 80, 65, 82, 84, 73, 84, 73, 79, 78, 39, 32, 39, 66, - 89, 39, 32, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, - 76, 105, 115, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 65, 114, - 114, 97, 121, 66, 111, 117, 110, 100, 101, 100, 76, 105, 115, 116, 69, 120, 112, 114, 101, 115, - 115, 105, 111, 110, 32, 47, 32, 65, 114, 114, 97, 121, 80, 97, 114, 101, 110, 115, 83, 101, - 108, 101, 99, 116, 10, 65, 114, 114, 97, 121, 66, 111, 117, 110, 100, 101, 100, 76, 105, 115, - 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 65, 82, 82, 65, - 89, 39, 63, 32, 66, 111, 117, 110, 100, 101, 100, 76, 105, 115, 116, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 10, 65, 114, 114, 97, 121, 80, 97, 114, 101, 110, 115, 83, 101, 108, - 101, 99, 116, 32, 60, 45, 32, 39, 65, 82, 82, 65, 89, 39, 32, 80, 97, 114, 101, 110, - 115, 40, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, - 101, 114, 110, 97, 108, 41, 10, 66, 111, 117, 110, 100, 101, 100, 76, 105, 115, 116, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 91, 39, 32, 76, 105, 115, 116, - 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 63, 32, 39, 93, 39, 10, 83, 116, - 114, 117, 99, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 123, - 39, 32, 76, 105, 115, 116, 40, 83, 116, 114, 117, 99, 116, 70, 105, 101, 108, 100, 41, 63, - 32, 39, 125, 39, 10, 83, 116, 114, 117, 99, 116, 70, 105, 101, 108, 100, 32, 60, 45, 32, - 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 32, 39, 58, 39, 32, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 10, 77, 97, 112, 69, 120, 112, 114, 101, 115, 115, 105, - 111, 110, 32, 60, 45, 32, 39, 77, 65, 80, 39, 32, 77, 97, 112, 83, 116, 114, 117, 99, - 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 77, 97, 112, 83, 116, 114, 117, 99, - 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 123, 39, 32, 76, - 105, 115, 116, 40, 77, 97, 112, 83, 116, 114, 117, 99, 116, 70, 105, 101, 108, 100, 41, 63, - 32, 39, 125, 39, 10, 77, 97, 112, 83, 116, 114, 117, 99, 116, 70, 105, 101, 108, 100, 32, - 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 58, 39, 32, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 10, 71, 114, 111, 117, 112, 105, 110, 103, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 71, 114, 111, 117, 112, 105, 110, 103, 79, - 114, 71, 114, 111, 117, 112, 105, 110, 103, 73, 100, 32, 80, 97, 114, 101, 110, 115, 40, 76, - 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 63, 41, 10, 71, 114, - 111, 117, 112, 105, 110, 103, 79, 114, 71, 114, 111, 117, 112, 105, 110, 103, 73, 100, 32, 60, - 45, 32, 71, 114, 111, 117, 112, 105, 110, 103, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, - 71, 114, 111, 117, 112, 105, 110, 103, 73, 100, 75, 101, 121, 119, 111, 114, 100, 10, 71, 114, - 111, 117, 112, 105, 110, 103, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 71, 82, - 79, 85, 80, 73, 78, 71, 39, 10, 71, 114, 111, 117, 112, 105, 110, 103, 73, 100, 75, 101, - 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 71, 82, 79, 85, 80, 73, 78, 71, 95, 73, - 68, 39, 10, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 60, 45, 32, 81, 117, 101, 115, - 116, 105, 111, 110, 77, 97, 114, 107, 78, 117, 109, 98, 101, 114, 101, 100, 80, 97, 114, 97, - 109, 101, 116, 101, 114, 32, 47, 32, 65, 110, 111, 110, 121, 109, 111, 117, 115, 80, 97, 114, - 97, 109, 101, 116, 101, 114, 32, 47, 32, 78, 117, 109, 98, 101, 114, 101, 100, 80, 97, 114, - 97, 109, 101, 116, 101, 114, 32, 47, 32, 67, 111, 108, 76, 97, 98, 101, 108, 80, 97, 114, - 97, 109, 101, 116, 101, 114, 10, 81, 117, 101, 115, 116, 105, 111, 110, 77, 97, 114, 107, 78, - 117, 109, 98, 101, 114, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 60, 45, 32, - 39, 63, 39, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 10, 65, 110, - 111, 110, 121, 109, 111, 117, 115, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 60, 45, 32, - 39, 63, 39, 10, 78, 117, 109, 98, 101, 114, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, - 114, 32, 60, 45, 32, 39, 36, 39, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, - 97, 108, 10, 67, 111, 108, 76, 97, 98, 101, 108, 80, 97, 114, 97, 109, 101, 116, 101, 114, - 32, 60, 45, 32, 39, 36, 39, 32, 67, 111, 108, 76, 97, 98, 101, 108, 10, 80, 111, 115, - 105, 116, 105, 111, 110, 97, 108, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, - 32, 39, 35, 39, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 10, 68, - 101, 102, 97, 117, 108, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, - 39, 68, 69, 70, 65, 85, 76, 84, 39, 10, 76, 105, 115, 116, 67, 111, 109, 112, 114, 101, - 104, 101, 110, 115, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, - 32, 39, 91, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 70, 79, 82, - 39, 32, 76, 105, 115, 116, 40, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, - 41, 32, 39, 73, 78, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 76, 105, - 115, 116, 67, 111, 109, 112, 114, 101, 104, 101, 110, 115, 105, 111, 110, 70, 105, 108, 116, 101, - 114, 63, 32, 39, 93, 39, 10, 76, 105, 115, 116, 67, 111, 109, 112, 114, 101, 104, 101, 110, - 115, 105, 111, 110, 70, 105, 108, 116, 101, 114, 32, 60, 45, 32, 39, 73, 70, 39, 32, 69, - 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 80, 97, 114, 101, 110, 115, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 41, 10, 35, 32, 82, 85, 78, 78, 73, 78, 71, 32, 97, - 110, 100, 32, 70, 73, 78, 65, 76, 32, 115, 97, 121, 32, 104, 111, 119, 32, 109, 117, 99, - 104, 32, 111, 102, 32, 97, 32, 77, 65, 84, 67, 72, 95, 82, 69, 67, 79, 71, 78, 73, - 90, 69, 32, 109, 97, 116, 99, 104, 32, 116, 104, 101, 32, 99, 97, 108, 108, 32, 98, 101, - 108, 111, 119, 32, 116, 104, 101, 109, 32, 114, 101, 97, 100, 115, 46, 32, 84, 104, 101, 121, - 32, 97, 114, 101, 32, 111, 110, 108, 121, 10, 35, 32, 109, 101, 97, 110, 105, 110, 103, 102, - 117, 108, 32, 105, 110, 32, 77, 69, 65, 83, 85, 82, 69, 83, 44, 32, 119, 104, 105, 99, - 104, 32, 105, 115, 32, 119, 104, 101, 114, 101, 32, 116, 104, 101, 32, 98, 105, 110, 100, 101, - 114, 32, 99, 104, 101, 99, 107, 115, 32, 102, 111, 114, 32, 116, 104, 101, 109, 46, 10, 77, - 101, 97, 115, 117, 114, 101, 83, 101, 109, 97, 110, 116, 105, 99, 115, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 32, 60, 45, 32, 77, 101, 97, 115, 117, 114, 101, 83, 101, 109, 97, - 110, 116, 105, 99, 115, 32, 70, 117, 110, 99, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, - 115, 105, 111, 110, 10, 83, 105, 110, 103, 108, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, - 110, 32, 60, 45, 10, 32, 32, 32, 32, 80, 97, 114, 101, 110, 115, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 32, 47, 10, 9, 76, 105, 116, 101, 114, 97, 108, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 32, 47, 10, 9, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, - 47, 10, 32, 32, 32, 32, 83, 117, 98, 113, 117, 101, 114, 121, 69, 120, 112, 114, 101, 115, - 115, 105, 111, 110, 32, 47, 10, 32, 32, 32, 32, 83, 112, 101, 99, 105, 97, 108, 70, 117, - 110, 99, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, 32, - 32, 32, 32, 80, 97, 114, 101, 110, 116, 104, 101, 115, 105, 115, 69, 120, 112, 114, 101, 115, - 115, 105, 111, 110, 32, 47, 10, 32, 32, 32, 32, 73, 110, 116, 101, 114, 118, 97, 108, 76, - 105, 116, 101, 114, 97, 108, 32, 47, 10, 32, 32, 32, 32, 84, 121, 112, 101, 76, 105, 116, - 101, 114, 97, 108, 32, 47, 10, 32, 32, 32, 32, 67, 97, 115, 101, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 32, 47, 10, 32, 32, 32, 32, 83, 116, 97, 114, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 32, 47, 10, 32, 32, 32, 32, 67, 97, 115, 116, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, 32, 32, 32, 32, 71, 114, 111, 117, 112, 105, - 110, 103, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, 32, 32, 32, 32, 77, - 97, 112, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, 32, 32, 32, 32, 77, - 101, 97, 115, 117, 114, 101, 83, 101, 109, 97, 110, 116, 105, 99, 115, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 32, 47, 10, 32, 32, 32, 32, 70, 117, 110, 99, 116, 105, 111, 110, - 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, 32, 32, 32, 32, 67, 111, 108, - 117, 109, 110, 82, 101, 102, 101, 114, 101, 110, 99, 101, 32, 47, 10, 32, 32, 32, 32, 76, - 105, 115, 116, 67, 111, 109, 112, 114, 101, 104, 101, 110, 115, 105, 111, 110, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 32, 47, 10, 32, 32, 32, 32, 76, 105, 115, 116, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, 32, 32, 32, 32, 83, 116, 114, 117, 99, 116, - 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, 32, 32, 32, 32, 80, 111, 115, - 105, 116, 105, 111, 110, 97, 108, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, - 32, 32, 32, 32, 68, 101, 102, 97, 117, 108, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, - 110, 10, 35, 32, 76, 69, 86, 69, 76, 32, 49, 32, 40, 76, 111, 119, 101, 115, 116, 41, - 10, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 76, 97, 109, 98, 100, - 97, 65, 114, 114, 111, 119, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 67, 111, 108, - 117, 109, 110, 68, 101, 102, 97, 117, 108, 116, 69, 120, 112, 114, 32, 60, 45, 32, 67, 111, - 108, 68, 101, 102, 79, 114, 69, 120, 112, 114, 10, 35, 32, 76, 69, 86, 69, 76, 32, 49, - 46, 53, 10, 76, 97, 109, 98, 100, 97, 65, 114, 114, 111, 119, 69, 120, 112, 114, 101, 115, - 115, 105, 111, 110, 32, 60, 45, 32, 76, 111, 103, 105, 99, 97, 108, 79, 114, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 32, 83, 105, 110, 103, 108, 101, 65, 114, 114, 111, 119, 80, - 97, 105, 114, 42, 10, 83, 105, 110, 103, 108, 101, 65, 114, 114, 111, 119, 80, 97, 105, 114, - 32, 60, 45, 32, 39, 45, 62, 39, 32, 76, 111, 103, 105, 99, 97, 108, 79, 114, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 10, 76, 111, 103, 105, 99, 97, 108, 79, 114, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 76, 111, 103, 105, 99, 97, 108, 65, - 110, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 76, 111, 103, 105, 99, 97, 108, - 79, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, 76, 111, - 103, 105, 99, 97, 108, 79, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, - 108, 32, 60, 45, 32, 39, 79, 82, 39, 32, 76, 111, 103, 105, 99, 97, 108, 65, 110, 100, - 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 67, 111, 108, 68, 101, 102, 79, 114, 69, - 120, 112, 114, 32, 60, 45, 32, 67, 111, 108, 68, 101, 102, 65, 110, 100, 69, 120, 112, 114, - 32, 67, 111, 108, 68, 101, 102, 79, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, - 97, 105, 108, 42, 10, 67, 111, 108, 68, 101, 102, 79, 114, 69, 120, 112, 114, 101, 115, 115, - 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 39, 79, 82, 39, 32, 67, 111, 108, 68, - 101, 102, 65, 110, 100, 69, 120, 112, 114, 10, 35, 32, 76, 69, 86, 69, 76, 32, 50, 10, - 76, 111, 103, 105, 99, 97, 108, 65, 110, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, - 32, 60, 45, 32, 76, 111, 103, 105, 99, 97, 108, 78, 111, 116, 69, 120, 112, 114, 101, 115, - 115, 105, 111, 110, 32, 76, 111, 103, 105, 99, 97, 108, 65, 110, 100, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, 76, 111, 103, 105, 99, 97, 108, 65, 110, - 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 39, - 65, 78, 68, 39, 32, 76, 111, 103, 105, 99, 97, 108, 78, 111, 116, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 10, 67, 111, 108, 68, 101, 102, 65, 110, 100, 69, 120, 112, 114, 32, - 60, 45, 32, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, 114, 111, 109, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 32, 67, 111, 108, 68, 101, 102, 65, 110, 100, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, 67, 111, 108, 68, 101, 102, 65, - 110, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, - 39, 65, 78, 68, 39, 32, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, 114, 111, 109, - 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 35, 32, 76, 69, 86, 69, 76, 32, 51, - 10, 76, 111, 103, 105, 99, 97, 108, 78, 111, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, - 110, 32, 60, 45, 32, 78, 111, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 32, - 73, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 78, 111, 116, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 78, 111, 116, 75, 101, 121, 119, 111, 114, 100, - 43, 10, 78, 111, 116, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 78, 79, 84, - 39, 10, 35, 32, 76, 69, 86, 69, 76, 32, 52, 10, 73, 115, 69, 120, 112, 114, 101, 115, - 115, 105, 111, 110, 32, 60, 45, 32, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, 114, - 111, 109, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 73, 115, 84, 101, 115, 116, 42, - 10, 73, 115, 84, 101, 115, 116, 32, 60, 45, 32, 73, 115, 76, 105, 116, 101, 114, 97, 108, - 32, 47, 32, 78, 111, 116, 78, 117, 108, 108, 32, 47, 32, 73, 115, 78, 117, 108, 108, 10, - 73, 115, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 39, 73, 83, 39, 32, 39, 78, - 79, 84, 39, 63, 32, 73, 115, 76, 105, 116, 101, 114, 97, 108, 86, 97, 108, 117, 101, 10, - 73, 115, 76, 105, 116, 101, 114, 97, 108, 86, 97, 108, 117, 101, 32, 60, 45, 32, 84, 114, - 117, 101, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 70, 97, 108, 115, 101, 76, 105, 116, - 101, 114, 97, 108, 32, 47, 32, 78, 117, 108, 108, 76, 105, 116, 101, 114, 97, 108, 32, 47, - 32, 85, 110, 107, 110, 111, 119, 110, 76, 105, 116, 101, 114, 97, 108, 10, 85, 110, 107, 110, - 111, 119, 110, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 39, 85, 78, 75, 78, 79, - 87, 78, 39, 10, 78, 111, 116, 78, 117, 108, 108, 32, 60, 45, 32, 78, 111, 116, 78, 117, - 108, 108, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 78, 111, 116, 78, 117, 108, 108, 79, - 112, 101, 114, 97, 116, 111, 114, 10, 78, 111, 116, 78, 117, 108, 108, 75, 101, 121, 119, 111, - 114, 100, 32, 60, 45, 32, 39, 78, 79, 84, 39, 32, 39, 78, 85, 76, 76, 39, 10, 78, - 111, 116, 78, 117, 108, 108, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 78, - 79, 84, 78, 85, 76, 76, 39, 10, 73, 115, 78, 117, 108, 108, 32, 60, 45, 32, 73, 115, - 78, 117, 108, 108, 79, 112, 101, 114, 97, 116, 111, 114, 10, 73, 115, 78, 117, 108, 108, 79, - 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 73, 83, 78, 85, 76, 76, 39, 10, - 35, 32, 76, 69, 86, 69, 76, 32, 53, 32, 40, 83, 112, 108, 105, 116, 32, 98, 101, 99, - 97, 117, 115, 101, 32, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, 114, 111, 109, 69, - 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 97, 108, 108, 111, 119, 115, 32, 112, 111, 115, - 116, 32, 101, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 119, 104, 105, 108, 101, 32, 73, - 115, 79, 112, 101, 114, 97, 116, 111, 114, 32, 100, 111, 101, 115, 32, 110, 111, 116, 41, 10, - 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, 114, 111, 109, 69, 120, 112, 114, 101, 115, - 115, 105, 111, 110, 32, 60, 45, 32, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 32, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, - 114, 111, 109, 84, 97, 105, 108, 42, 10, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, - 114, 111, 109, 84, 97, 105, 108, 32, 60, 45, 32, 73, 115, 68, 105, 115, 116, 105, 110, 99, - 116, 70, 114, 111, 109, 79, 112, 32, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 10, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, - 114, 111, 109, 79, 112, 32, 60, 45, 32, 39, 73, 83, 39, 32, 39, 78, 79, 84, 39, 63, - 32, 39, 68, 73, 83, 84, 73, 78, 67, 84, 39, 32, 39, 70, 82, 79, 77, 39, 10, 35, - 32, 76, 69, 86, 69, 76, 32, 54, 10, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 69, - 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 66, 101, 116, 119, 101, 101, 110, - 73, 110, 76, 105, 107, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 67, 111, 109, - 112, 97, 114, 105, 115, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, - 108, 42, 10, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 69, 120, 112, 114, 101, 115, 115, - 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 67, 111, 109, 112, 97, 114, 105, 115, 111, - 110, 79, 112, 101, 114, 97, 116, 111, 114, 32, 78, 111, 116, 69, 120, 112, 114, 101, 115, 115, - 105, 111, 110, 63, 32, 66, 101, 116, 119, 101, 101, 110, 73, 110, 76, 105, 107, 101, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 10, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 79, - 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 10, 32, 32, 32, 32, 79, 112, 101, 114, 97, - 116, 111, 114, 69, 113, 117, 97, 108, 32, 47, 10, 32, 32, 32, 32, 79, 112, 101, 114, 97, - 116, 111, 114, 78, 111, 116, 69, 113, 117, 97, 108, 32, 47, 10, 32, 32, 32, 32, 79, 112, - 101, 114, 97, 116, 111, 114, 76, 101, 115, 115, 84, 104, 97, 110, 32, 47, 10, 32, 32, 32, - 32, 79, 112, 101, 114, 97, 116, 111, 114, 71, 114, 101, 97, 116, 101, 114, 84, 104, 97, 110, - 32, 47, 10, 32, 32, 32, 32, 79, 112, 101, 114, 97, 116, 111, 114, 76, 101, 115, 115, 84, - 104, 97, 110, 69, 113, 117, 97, 108, 115, 32, 47, 10, 32, 32, 32, 32, 79, 112, 101, 114, - 97, 116, 111, 114, 71, 114, 101, 97, 116, 101, 114, 84, 104, 97, 110, 69, 113, 117, 97, 108, - 115, 10, 79, 112, 101, 114, 97, 116, 111, 114, 69, 113, 117, 97, 108, 32, 60, 45, 32, 39, - 61, 39, 32, 47, 32, 39, 61, 61, 39, 10, 79, 112, 101, 114, 97, 116, 111, 114, 78, 111, - 116, 69, 113, 117, 97, 108, 32, 60, 45, 32, 39, 33, 61, 39, 32, 47, 32, 39, 60, 62, - 39, 10, 79, 112, 101, 114, 97, 116, 111, 114, 76, 101, 115, 115, 84, 104, 97, 110, 32, 60, - 45, 32, 39, 60, 39, 10, 79, 112, 101, 114, 97, 116, 111, 114, 71, 114, 101, 97, 116, 101, - 114, 84, 104, 97, 110, 32, 60, 45, 32, 39, 62, 39, 10, 79, 112, 101, 114, 97, 116, 111, - 114, 76, 101, 115, 115, 84, 104, 97, 110, 69, 113, 117, 97, 108, 115, 32, 60, 45, 32, 39, - 60, 61, 39, 10, 79, 112, 101, 114, 97, 116, 111, 114, 71, 114, 101, 97, 116, 101, 114, 84, - 104, 97, 110, 69, 113, 117, 97, 108, 115, 32, 60, 45, 32, 39, 62, 61, 39, 10, 35, 32, - 76, 69, 86, 69, 76, 32, 55, 10, 66, 101, 116, 119, 101, 101, 110, 73, 110, 76, 105, 107, - 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 79, 116, 104, 101, 114, - 79, 112, 101, 114, 97, 116, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 66, - 101, 116, 119, 101, 101, 110, 73, 110, 76, 105, 107, 101, 79, 112, 63, 10, 66, 101, 116, 119, - 101, 101, 110, 73, 110, 76, 105, 107, 101, 79, 112, 32, 60, 45, 32, 39, 78, 79, 84, 39, - 63, 32, 66, 101, 116, 119, 101, 101, 110, 73, 110, 76, 105, 107, 101, 79, 112, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 10, 66, 101, 116, 119, 101, 101, 110, 73, 110, 76, 105, 107, - 101, 79, 112, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 66, 101, 116, - 119, 101, 101, 110, 67, 108, 97, 117, 115, 101, 32, 47, 32, 73, 110, 67, 108, 97, 117, 115, - 101, 32, 47, 32, 76, 105, 107, 101, 67, 108, 97, 117, 115, 101, 10, 76, 105, 107, 101, 67, - 108, 97, 117, 115, 101, 32, 60, 45, 32, 76, 105, 107, 101, 86, 97, 114, 105, 97, 116, 105, - 111, 110, 115, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 32, 69, 115, 99, 97, 112, 101, 67, 108, 97, 117, 115, 101, - 63, 10, 69, 115, 99, 97, 112, 101, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 69, - 83, 67, 65, 80, 69, 39, 32, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 10, 76, 105, 107, 101, 86, 97, 114, 105, 97, 116, 105, 111, - 110, 115, 32, 60, 45, 32, 83, 105, 109, 105, 108, 97, 114, 84, 111, 84, 111, 107, 101, 110, - 32, 47, 32, 82, 101, 103, 101, 120, 73, 110, 115, 101, 110, 115, 105, 116, 105, 118, 101, 77, - 97, 116, 99, 104, 84, 111, 107, 101, 110, 32, 47, 32, 82, 101, 103, 101, 120, 77, 97, 116, - 99, 104, 84, 111, 107, 101, 110, 32, 47, 32, 73, 76, 105, 107, 101, 84, 111, 107, 101, 110, - 32, 47, 32, 76, 105, 107, 101, 84, 111, 107, 101, 110, 32, 47, 32, 71, 108, 111, 98, 84, - 111, 107, 101, 110, 32, 47, 32, 78, 111, 116, 73, 76, 105, 107, 101, 79, 112, 32, 47, 32, - 78, 111, 116, 76, 105, 107, 101, 79, 112, 32, 47, 32, 78, 111, 116, 82, 101, 103, 101, 120, - 73, 110, 115, 101, 110, 115, 105, 116, 105, 118, 101, 77, 97, 116, 99, 104, 79, 112, 32, 47, - 32, 78, 111, 116, 83, 105, 109, 105, 108, 97, 114, 84, 111, 79, 112, 10, 76, 105, 107, 101, - 84, 111, 107, 101, 110, 32, 32, 32, 32, 32, 32, 60, 45, 32, 39, 76, 73, 75, 69, 39, - 32, 47, 32, 39, 126, 126, 39, 10, 73, 76, 105, 107, 101, 84, 111, 107, 101, 110, 32, 32, - 32, 32, 32, 60, 45, 32, 39, 73, 76, 73, 75, 69, 39, 32, 47, 32, 39, 126, 126, 42, - 39, 10, 71, 108, 111, 98, 84, 111, 107, 101, 110, 32, 32, 32, 32, 32, 32, 60, 45, 32, - 39, 71, 76, 79, 66, 39, 32, 47, 32, 39, 126, 126, 126, 39, 10, 83, 105, 109, 105, 108, - 97, 114, 84, 111, 84, 111, 107, 101, 110, 32, 60, 45, 32, 39, 83, 73, 77, 73, 76, 65, - 82, 39, 32, 39, 84, 79, 39, 10, 82, 101, 103, 101, 120, 77, 97, 116, 99, 104, 84, 111, - 107, 101, 110, 32, 60, 45, 32, 39, 126, 39, 10, 82, 101, 103, 101, 120, 73, 110, 115, 101, - 110, 115, 105, 116, 105, 118, 101, 77, 97, 116, 99, 104, 84, 111, 107, 101, 110, 32, 60, 45, - 32, 39, 126, 42, 39, 10, 78, 111, 116, 73, 76, 105, 107, 101, 79, 112, 32, 32, 32, 32, - 32, 60, 45, 32, 39, 33, 126, 126, 42, 39, 10, 78, 111, 116, 76, 105, 107, 101, 79, 112, - 32, 32, 32, 32, 32, 32, 60, 45, 32, 39, 33, 126, 126, 39, 10, 78, 111, 116, 82, 101, - 103, 101, 120, 73, 110, 115, 101, 110, 115, 105, 116, 105, 118, 101, 77, 97, 116, 99, 104, 79, - 112, 32, 60, 45, 32, 39, 33, 126, 42, 39, 10, 78, 111, 116, 83, 105, 109, 105, 108, 97, - 114, 84, 111, 79, 112, 32, 60, 45, 32, 39, 33, 126, 39, 10, 73, 110, 67, 108, 97, 117, - 115, 101, 32, 60, 45, 32, 39, 73, 78, 39, 32, 73, 110, 69, 120, 112, 114, 101, 115, 115, - 105, 111, 110, 10, 73, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, - 73, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 76, 105, 115, 116, 32, 47, 32, 73, - 110, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 32, 73, - 110, 67, 111, 110, 116, 97, 105, 110, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, - 73, 110, 67, 111, 110, 116, 97, 105, 110, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, - 32, 60, 45, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 10, 73, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, - 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, - 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 41, 10, 73, 110, 83, 101, 108, 101, 99, - 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, - 40, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, - 114, 110, 97, 108, 41, 10, 66, 101, 116, 119, 101, 101, 110, 67, 108, 97, 117, 115, 101, 32, - 60, 45, 32, 39, 66, 69, 84, 87, 69, 69, 78, 39, 32, 79, 116, 104, 101, 114, 79, 112, - 101, 114, 97, 116, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 65, 78, - 68, 39, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 10, 35, 32, 76, 69, 86, 69, 76, 32, 56, 10, 79, 116, 104, - 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, - 32, 60, 45, 32, 66, 105, 116, 119, 105, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, - 110, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 84, 97, 105, 108, 42, - 10, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 84, 97, 105, 108, 32, 60, - 45, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 32, 66, 105, 116, 119, - 105, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 79, 116, 104, 101, 114, 79, - 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 10, 32, 32, 32, 32, 65, 110, 121, 65, 108, - 108, 80, 97, 114, 115, 101, 100, 79, 112, 101, 114, 97, 116, 111, 114, 32, 47, 32, 78, 97, - 109, 101, 100, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 10, 65, 110, 121, - 65, 108, 108, 80, 97, 114, 115, 101, 100, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, - 32, 65, 110, 121, 65, 108, 108, 79, 112, 101, 114, 97, 116, 111, 114, 10, 78, 97, 109, 101, - 100, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 10, 32, 32, - 32, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 79, 112, 101, 114, 97, 116, 111, 114, 32, - 47, 32, 73, 110, 101, 116, 79, 112, 101, 114, 97, 116, 111, 114, 32, 47, 32, 74, 115, 111, - 110, 79, 112, 101, 114, 97, 116, 111, 114, 32, 47, 32, 76, 105, 115, 116, 79, 112, 101, 114, - 97, 116, 111, 114, 32, 47, 32, 83, 116, 114, 105, 110, 103, 79, 112, 101, 114, 97, 116, 111, - 114, 32, 47, 32, 79, 112, 101, 114, 97, 116, 111, 114, 76, 105, 116, 101, 114, 97, 108, 10, - 79, 112, 101, 114, 97, 116, 111, 114, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 73, - 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 65, 110, 121, 65, 108, 108, 79, 112, 101, 114, - 97, 116, 111, 114, 32, 60, 45, 32, 65, 110, 121, 79, 112, 32, 65, 110, 121, 79, 114, 65, - 108, 108, 10, 65, 110, 121, 79, 114, 65, 108, 108, 32, 60, 45, 32, 83, 117, 98, 113, 117, - 101, 114, 121, 65, 110, 121, 32, 47, 32, 83, 117, 98, 113, 117, 101, 114, 121, 65, 108, 108, - 10, 83, 117, 98, 113, 117, 101, 114, 121, 65, 110, 121, 32, 60, 45, 32, 39, 65, 78, 89, - 39, 10, 83, 117, 98, 113, 117, 101, 114, 121, 65, 108, 108, 32, 60, 45, 32, 39, 65, 76, - 76, 39, 10, 73, 110, 101, 116, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, - 62, 62, 61, 39, 32, 47, 32, 39, 60, 60, 61, 39, 10, 74, 115, 111, 110, 79, 112, 101, - 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 45, 62, 62, 39, 10, 76, 105, 115, 116, 79, - 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 38, 38, 39, 32, 47, 32, 39, 64, - 62, 39, 32, 47, 32, 39, 60, 64, 39, 10, 83, 116, 114, 105, 110, 103, 79, 112, 101, 114, - 97, 116, 111, 114, 32, 60, 45, 32, 39, 94, 64, 39, 32, 47, 32, 39, 124, 124, 39, 10, - 81, 117, 97, 108, 105, 102, 105, 101, 100, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, - 32, 39, 79, 80, 69, 82, 65, 84, 79, 82, 39, 32, 80, 97, 114, 101, 110, 115, 40, 81, - 117, 97, 108, 105, 102, 105, 101, 100, 79, 112, 101, 114, 97, 116, 111, 114, 67, 111, 110, 116, - 101, 110, 116, 115, 41, 10, 81, 117, 97, 108, 105, 102, 105, 101, 100, 79, 112, 101, 114, 97, - 116, 111, 114, 67, 111, 110, 116, 101, 110, 116, 115, 32, 60, 45, 32, 67, 111, 108, 73, 100, - 68, 111, 116, 42, 32, 65, 110, 121, 79, 112, 10, 65, 110, 121, 79, 112, 32, 60, 45, 32, - 39, 33, 126, 126, 42, 39, 32, 47, 32, 39, 62, 62, 61, 39, 32, 47, 32, 39, 60, 60, - 61, 39, 32, 47, 32, 39, 45, 62, 62, 39, 32, 47, 32, 39, 33, 126, 126, 39, 32, 47, - 32, 39, 33, 126, 42, 39, 32, 47, 32, 39, 126, 126, 42, 39, 32, 47, 32, 39, 126, 126, - 126, 39, 32, 47, 32, 39, 126, 42, 39, 32, 47, 32, 39, 33, 126, 39, 32, 47, 32, 39, - 94, 64, 39, 32, 47, 32, 39, 124, 124, 39, 32, 47, 32, 39, 38, 38, 39, 32, 47, 32, - 39, 64, 62, 39, 32, 47, 32, 39, 60, 64, 39, 32, 47, 32, 39, 60, 61, 39, 32, 47, - 32, 39, 62, 61, 39, 32, 47, 32, 39, 60, 62, 39, 32, 47, 32, 39, 33, 61, 39, 32, - 47, 32, 39, 61, 61, 39, 32, 47, 32, 39, 60, 60, 39, 32, 47, 32, 39, 62, 62, 39, - 32, 47, 32, 39, 47, 47, 39, 32, 47, 32, 39, 42, 42, 39, 32, 47, 32, 39, 45, 62, - 39, 32, 47, 32, 39, 126, 126, 39, 32, 47, 32, 39, 43, 39, 32, 47, 32, 39, 45, 39, - 32, 47, 32, 39, 42, 39, 32, 47, 32, 39, 47, 39, 32, 47, 32, 39, 37, 39, 32, 47, - 32, 39, 94, 39, 32, 47, 32, 39, 60, 39, 32, 47, 32, 39, 62, 39, 32, 47, 32, 39, - 61, 39, 32, 47, 32, 39, 38, 39, 32, 47, 32, 39, 124, 39, 32, 47, 32, 39, 126, 39, - 32, 47, 32, 39, 33, 39, 10, 35, 32, 76, 69, 86, 69, 76, 32, 57, 10, 66, 105, 116, - 119, 105, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 65, 100, - 100, 105, 116, 105, 118, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 66, 105, 116, - 119, 105, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, - 66, 105, 116, 119, 105, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, - 108, 32, 60, 45, 32, 66, 105, 116, 79, 112, 101, 114, 97, 116, 111, 114, 32, 65, 100, 100, - 105, 116, 105, 118, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 66, 105, 116, 79, - 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 38, 39, 32, 47, 32, 39, 124, 39, - 32, 47, 32, 39, 60, 60, 39, 32, 47, 32, 39, 62, 62, 39, 10, 35, 32, 76, 69, 86, - 69, 76, 32, 49, 48, 10, 65, 100, 100, 105, 116, 105, 118, 101, 69, 120, 112, 114, 101, 115, - 115, 105, 111, 110, 32, 60, 45, 32, 77, 117, 108, 116, 105, 112, 108, 105, 99, 97, 116, 105, - 118, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 65, 100, 100, 105, 116, 105, 118, - 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, 65, 100, 100, - 105, 116, 105, 118, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, - 60, 45, 32, 84, 101, 114, 109, 32, 77, 117, 108, 116, 105, 112, 108, 105, 99, 97, 116, 105, - 118, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 84, 101, 114, 109, 32, 60, 45, - 32, 39, 43, 39, 32, 47, 32, 39, 45, 39, 10, 35, 32, 76, 69, 86, 69, 76, 32, 49, - 49, 10, 77, 117, 108, 116, 105, 112, 108, 105, 99, 97, 116, 105, 118, 101, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 69, 120, 112, 111, 110, 101, 110, 116, 105, 97, - 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 77, 117, 108, 116, 105, - 112, 108, 105, 99, 97, 116, 105, 118, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, - 97, 105, 108, 42, 10, 77, 117, 108, 116, 105, 112, 108, 105, 99, 97, 116, 105, 118, 101, 69, - 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 70, 97, 99, - 116, 111, 114, 32, 69, 120, 112, 111, 110, 101, 110, 116, 105, 97, 116, 105, 111, 110, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 10, 70, 97, 99, 116, 111, 114, 32, 60, 45, 32, 39, - 42, 39, 32, 47, 32, 39, 47, 39, 32, 47, 32, 39, 47, 47, 39, 32, 47, 32, 39, 37, - 39, 10, 35, 32, 76, 69, 86, 69, 76, 32, 49, 50, 10, 69, 120, 112, 111, 110, 101, 110, - 116, 105, 97, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, - 32, 67, 111, 108, 108, 97, 116, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 69, - 120, 112, 111, 110, 101, 110, 116, 105, 97, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, - 105, 111, 110, 84, 97, 105, 108, 42, 10, 69, 120, 112, 111, 110, 101, 110, 116, 105, 97, 116, - 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, - 32, 69, 120, 112, 111, 110, 101, 110, 116, 79, 112, 101, 114, 97, 116, 111, 114, 32, 67, 111, - 108, 108, 97, 116, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 69, 120, 112, 111, - 110, 101, 110, 116, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 94, 39, 32, - 47, 32, 39, 42, 42, 39, 10, 35, 32, 76, 69, 86, 69, 76, 32, 49, 51, 10, 67, 111, - 108, 108, 97, 116, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 65, - 116, 84, 105, 109, 101, 90, 111, 110, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, - 67, 111, 108, 108, 97, 116, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, - 108, 42, 10, 67, 111, 108, 108, 97, 116, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, - 84, 97, 105, 108, 32, 60, 45, 32, 67, 111, 108, 108, 97, 116, 101, 79, 112, 101, 114, 97, - 116, 111, 114, 32, 65, 116, 84, 105, 109, 101, 90, 111, 110, 101, 69, 120, 112, 114, 101, 115, - 115, 105, 111, 110, 10, 67, 111, 108, 108, 97, 116, 101, 79, 112, 101, 114, 97, 116, 111, 114, - 32, 60, 45, 32, 39, 67, 79, 76, 76, 65, 84, 69, 39, 10, 35, 32, 76, 69, 86, 69, - 76, 32, 49, 52, 10, 65, 116, 84, 105, 109, 101, 90, 111, 110, 101, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 32, 60, 45, 32, 80, 114, 101, 102, 105, 120, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 32, 65, 116, 84, 105, 109, 101, 90, 111, 110, 101, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, 65, 116, 84, 105, 109, 101, 90, 111, - 110, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, - 65, 116, 84, 105, 109, 101, 90, 111, 110, 101, 79, 112, 101, 114, 97, 116, 111, 114, 32, 80, - 114, 101, 102, 105, 120, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 65, 116, 84, 105, - 109, 101, 90, 111, 110, 101, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 65, - 84, 39, 32, 39, 84, 73, 77, 69, 39, 32, 39, 90, 79, 78, 69, 39, 10, 35, 32, 76, - 69, 86, 69, 76, 32, 49, 53, 10, 80, 114, 101, 102, 105, 120, 69, 120, 112, 114, 101, 115, - 115, 105, 111, 110, 32, 60, 45, 32, 80, 114, 101, 102, 105, 120, 79, 112, 101, 114, 97, 116, - 111, 114, 42, 32, 66, 97, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 80, - 114, 101, 102, 105, 120, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 81, 117, 97, - 108, 105, 102, 105, 101, 100, 79, 112, 101, 114, 97, 116, 111, 114, 32, 47, 32, 77, 105, 110, - 117, 115, 80, 114, 101, 102, 105, 120, 79, 112, 101, 114, 97, 116, 111, 114, 32, 47, 32, 80, - 108, 117, 115, 80, 114, 101, 102, 105, 120, 79, 112, 101, 114, 97, 116, 111, 114, 32, 47, 32, - 84, 105, 108, 100, 101, 80, 114, 101, 102, 105, 120, 79, 112, 101, 114, 97, 116, 111, 114, 10, - 77, 105, 110, 117, 115, 80, 114, 101, 102, 105, 120, 79, 112, 101, 114, 97, 116, 111, 114, 32, - 60, 45, 32, 39, 45, 39, 10, 80, 108, 117, 115, 80, 114, 101, 102, 105, 120, 79, 112, 101, - 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 43, 39, 10, 84, 105, 108, 100, 101, 80, 114, - 101, 102, 105, 120, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 126, 39, 10, - 35, 32, 76, 69, 86, 69, 76, 32, 49, 54, 32, 40, 72, 105, 103, 104, 101, 115, 116, 41, - 10, 66, 97, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 83, - 105, 110, 103, 108, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 73, 110, 100, 105, - 114, 101, 99, 116, 105, 111, 110, 76, 105, 115, 116, 63, 10, 73, 110, 100, 105, 114, 101, 99, - 116, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 73, 110, 100, 105, 114, 101, 99, 116, - 105, 111, 110, 43, 10, 73, 110, 100, 105, 114, 101, 99, 116, 105, 111, 110, 32, 60, 45, 32, - 67, 97, 115, 116, 79, 112, 101, 114, 97, 116, 111, 114, 32, 47, 32, 68, 111, 116, 79, 112, - 101, 114, 97, 116, 111, 114, 32, 47, 32, 83, 108, 105, 99, 101, 69, 120, 112, 114, 101, 115, - 115, 105, 111, 110, 32, 47, 32, 80, 111, 115, 116, 102, 105, 120, 79, 112, 101, 114, 97, 116, - 111, 114, 10, 67, 97, 115, 116, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, - 58, 58, 39, 32, 84, 121, 112, 101, 10, 68, 111, 116, 79, 112, 101, 114, 97, 116, 111, 114, - 32, 60, 45, 32, 68, 111, 116, 77, 101, 116, 104, 111, 100, 79, 112, 101, 114, 97, 116, 111, - 114, 32, 47, 32, 68, 111, 116, 67, 111, 108, 117, 109, 110, 79, 112, 101, 114, 97, 116, 111, - 114, 10, 68, 111, 116, 77, 101, 116, 104, 111, 100, 79, 112, 101, 114, 97, 116, 111, 114, 32, - 60, 45, 32, 39, 46, 39, 32, 77, 101, 116, 104, 111, 100, 69, 120, 112, 114, 101, 115, 115, - 105, 111, 110, 10, 68, 111, 116, 67, 111, 108, 117, 109, 110, 79, 112, 101, 114, 97, 116, 111, - 114, 32, 60, 45, 32, 39, 46, 39, 32, 67, 111, 108, 76, 97, 98, 101, 108, 10, 77, 101, - 116, 104, 111, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 67, 111, - 108, 76, 97, 98, 101, 108, 32, 77, 101, 116, 104, 111, 100, 69, 120, 112, 114, 101, 115, 115, - 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 10, 77, 101, 116, 104, 111, 100, 69, - 120, 112, 114, 101, 115, 115, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, - 45, 32, 80, 97, 114, 101, 110, 115, 40, 77, 101, 116, 104, 111, 100, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 76, 105, 115, 116, 41, 10, 77, - 101, 116, 104, 111, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 65, 114, 103, 117, 109, - 101, 110, 116, 76, 105, 115, 116, 32, 60, 45, 32, 68, 105, 115, 116, 105, 110, 99, 116, 79, - 114, 65, 108, 108, 63, 32, 77, 101, 116, 104, 111, 100, 70, 117, 110, 99, 116, 105, 111, 110, - 65, 114, 103, 117, 109, 101, 110, 116, 115, 63, 32, 79, 114, 100, 101, 114, 66, 121, 67, 108, - 97, 117, 115, 101, 63, 32, 73, 103, 110, 111, 114, 101, 79, 114, 82, 101, 115, 112, 101, 99, - 116, 78, 117, 108, 108, 115, 63, 10, 77, 101, 116, 104, 111, 100, 70, 117, 110, 99, 116, 105, - 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, 45, 32, 76, 105, 115, 116, 40, - 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 41, 10, 83, 108, - 105, 99, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 91, 39, - 32, 83, 108, 105, 99, 101, 66, 111, 117, 110, 100, 32, 39, 93, 39, 10, 83, 108, 105, 99, - 101, 66, 111, 117, 110, 100, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, - 63, 32, 69, 110, 100, 83, 108, 105, 99, 101, 66, 111, 117, 110, 100, 63, 32, 83, 116, 101, - 112, 83, 108, 105, 99, 101, 66, 111, 117, 110, 100, 63, 10, 69, 110, 100, 83, 108, 105, 99, - 101, 66, 111, 117, 110, 100, 32, 60, 45, 32, 39, 58, 39, 32, 69, 110, 100, 83, 108, 105, - 99, 101, 86, 97, 108, 117, 101, 63, 10, 69, 110, 100, 83, 108, 105, 99, 101, 86, 97, 108, - 117, 101, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 69, - 110, 100, 83, 108, 105, 99, 101, 77, 105, 110, 117, 115, 10, 69, 110, 100, 83, 108, 105, 99, - 101, 77, 105, 110, 117, 115, 32, 60, 45, 32, 39, 45, 39, 10, 83, 116, 101, 112, 83, 108, - 105, 99, 101, 66, 111, 117, 110, 100, 32, 60, 45, 32, 39, 58, 39, 32, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 63, 10, 80, 111, 115, 116, 102, 105, 120, 79, 112, 101, 114, 97, - 116, 111, 114, 32, 60, 45, 32, 39, 33, 39, 10, 83, 112, 101, 99, 105, 97, 108, 70, 117, - 110, 99, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, - 67, 111, 97, 108, 101, 115, 99, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, - 32, 85, 110, 112, 97, 99, 107, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, - 84, 114, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 67, 111, 108, 117, - 109, 110, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 69, 120, 116, 114, - 97, 99, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 76, 97, 109, 98, - 100, 97, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 78, 117, 108, 108, 73, - 102, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 80, 111, 115, 105, 116, 105, - 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 82, 111, 119, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 83, 117, 98, 115, 116, 114, 105, 110, 103, - 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 84, 114, 105, 109, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 79, 118, 101, 114, 108, 97, 121, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 10, 67, 111, 97, 108, 101, 115, 99, 101, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 67, 79, 65, 76, 69, 83, 67, 69, 39, - 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, - 105, 111, 110, 41, 41, 10, 85, 110, 112, 97, 99, 107, 69, 120, 112, 114, 101, 115, 115, 105, - 111, 110, 32, 60, 45, 32, 39, 85, 78, 80, 65, 67, 75, 39, 32, 80, 97, 114, 101, 110, - 115, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, 84, 114, 121, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 84, 82, 89, 39, 32, 80, 97, 114, - 101, 110, 115, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, 67, 111, 108, 117, - 109, 110, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 83, 116, 97, - 114, 83, 121, 109, 98, 111, 108, 63, 32, 39, 67, 79, 76, 85, 77, 78, 83, 39, 32, 80, - 97, 114, 101, 110, 115, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, 69, 120, - 116, 114, 97, 99, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, - 69, 88, 84, 82, 65, 67, 84, 39, 32, 80, 97, 114, 101, 110, 115, 40, 69, 120, 116, 114, - 97, 99, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 41, 10, 69, 120, 116, 114, 97, 99, - 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, 45, 32, 69, 120, 116, 114, 97, 99, - 116, 65, 114, 103, 117, 109, 101, 110, 116, 32, 39, 70, 82, 79, 77, 39, 32, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 10, 76, 97, 109, 98, 100, 97, 69, 120, 112, 114, 101, 115, - 115, 105, 111, 110, 32, 60, 45, 32, 39, 76, 65, 77, 66, 68, 65, 39, 32, 76, 105, 115, - 116, 40, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 41, 32, 39, 58, 39, - 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 78, 117, 108, 108, 73, 102, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 78, 85, 76, 76, 73, 70, 39, - 32, 80, 97, 114, 101, 110, 115, 40, 78, 117, 108, 108, 73, 102, 65, 114, 103, 117, 109, 101, - 110, 116, 115, 41, 10, 78, 117, 108, 108, 73, 102, 65, 114, 103, 117, 109, 101, 110, 116, 115, - 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 44, 39, 32, 69, - 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 80, 111, 115, 105, 116, 105, 111, 110, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 80, 79, 83, 73, 84, 73, 79, - 78, 39, 32, 80, 97, 114, 101, 110, 115, 40, 80, 111, 115, 105, 116, 105, 111, 110, 65, 114, - 103, 117, 109, 101, 110, 116, 115, 41, 10, 80, 111, 115, 105, 116, 105, 111, 110, 65, 114, 103, - 117, 109, 101, 110, 116, 115, 32, 60, 45, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, - 116, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 73, 78, 39, 32, 69, - 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 82, 111, 119, 69, 120, 112, 114, 101, 115, 115, - 105, 111, 110, 32, 60, 45, 32, 39, 82, 79, 87, 39, 32, 80, 97, 114, 101, 110, 115, 40, - 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 63, 41, 10, 83, - 117, 98, 115, 116, 114, 105, 110, 103, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, - 45, 32, 39, 83, 85, 66, 83, 84, 82, 73, 78, 71, 39, 32, 80, 97, 114, 101, 110, 115, - 40, 83, 117, 98, 115, 116, 114, 105, 110, 103, 65, 114, 103, 117, 109, 101, 110, 116, 115, 41, - 10, 83, 117, 98, 115, 116, 114, 105, 110, 103, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, - 60, 45, 32, 83, 117, 98, 115, 116, 114, 105, 110, 103, 80, 97, 114, 97, 109, 101, 116, 101, - 114, 115, 32, 47, 32, 83, 117, 98, 115, 116, 114, 105, 110, 103, 69, 120, 112, 114, 101, 115, - 115, 105, 111, 110, 76, 105, 115, 116, 10, 83, 117, 98, 115, 116, 114, 105, 110, 103, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, - 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, 83, 117, 98, 115, 116, 114, 105, - 110, 103, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 32, 60, 45, 32, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 32, 83, 117, 98, 115, 116, 114, 105, 110, 103, 70, 114, 111, 109, - 70, 111, 114, 10, 83, 117, 98, 115, 116, 114, 105, 110, 103, 70, 114, 111, 109, 70, 111, 114, - 32, 60, 45, 32, 83, 117, 98, 115, 116, 114, 105, 110, 103, 70, 114, 111, 109, 79, 112, 116, - 105, 111, 110, 97, 108, 70, 111, 114, 32, 47, 32, 83, 117, 98, 115, 116, 114, 105, 110, 103, - 70, 111, 114, 10, 83, 117, 98, 115, 116, 114, 105, 110, 103, 70, 114, 111, 109, 79, 112, 116, - 105, 111, 110, 97, 108, 70, 111, 114, 32, 60, 45, 32, 70, 114, 111, 109, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 32, 70, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, - 63, 10, 83, 117, 98, 115, 116, 114, 105, 110, 103, 70, 111, 114, 32, 60, 45, 32, 70, 111, - 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 84, 114, 105, 109, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 84, 82, 73, 77, 39, 32, 80, 97, 114, - 101, 110, 115, 40, 84, 114, 105, 109, 65, 114, 103, 117, 109, 101, 110, 116, 115, 41, 10, 84, - 114, 105, 109, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, 45, 32, 84, 114, 105, 109, - 68, 105, 114, 101, 99, 116, 105, 111, 110, 63, 32, 84, 114, 105, 109, 83, 111, 117, 114, 99, - 101, 63, 32, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, - 84, 114, 105, 109, 68, 105, 114, 101, 99, 116, 105, 111, 110, 32, 60, 45, 32, 84, 114, 105, - 109, 66, 111, 116, 104, 32, 47, 32, 84, 114, 105, 109, 76, 101, 97, 100, 105, 110, 103, 32, - 47, 32, 84, 114, 105, 109, 84, 114, 97, 105, 108, 105, 110, 103, 10, 84, 114, 105, 109, 66, - 111, 116, 104, 32, 60, 45, 32, 39, 66, 79, 84, 72, 39, 10, 84, 114, 105, 109, 76, 101, - 97, 100, 105, 110, 103, 32, 60, 45, 32, 39, 76, 69, 65, 68, 73, 78, 71, 39, 10, 84, - 114, 105, 109, 84, 114, 97, 105, 108, 105, 110, 103, 32, 60, 45, 32, 39, 84, 82, 65, 73, - 76, 73, 78, 71, 39, 10, 84, 114, 105, 109, 83, 111, 117, 114, 99, 101, 32, 60, 45, 32, - 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 32, 39, 70, 82, 79, 77, 39, 10, 79, - 118, 101, 114, 108, 97, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, - 39, 79, 86, 69, 82, 76, 65, 89, 39, 32, 80, 97, 114, 101, 110, 115, 40, 79, 118, 101, - 114, 108, 97, 121, 65, 114, 103, 117, 109, 101, 110, 116, 115, 41, 10, 79, 118, 101, 114, 108, - 97, 121, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, 45, 32, 79, 118, 101, 114, 108, - 97, 121, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 32, 47, 32, 79, 118, 101, 114, 108, - 97, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 76, 105, 115, 116, 10, 79, 118, 101, - 114, 108, 97, 121, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 32, 60, 45, 32, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 80, 76, 65, 67, 73, 78, 71, 39, 32, 69, - 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 70, 114, 111, 109, 69, 120, 112, 114, 101, 115, - 115, 105, 111, 110, 32, 70, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 10, - 70, 114, 111, 109, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 70, - 82, 79, 77, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 70, 111, 114, 69, - 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 70, 79, 82, 39, 32, 69, - 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 79, 118, 101, 114, 108, 97, 121, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, - 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, 69, 120, 116, 114, 97, 99, 116, 65, - 114, 103, 117, 109, 101, 110, 116, 32, 60, 45, 32, 69, 120, 116, 114, 97, 99, 116, 68, 97, - 116, 101, 80, 97, 114, 116, 65, 114, 103, 117, 109, 101, 110, 116, 32, 47, 32, 69, 120, 116, - 114, 97, 99, 116, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 65, 114, 103, 117, 109, 101, - 110, 116, 32, 47, 32, 69, 120, 116, 114, 97, 99, 116, 83, 116, 114, 105, 110, 103, 65, 114, - 103, 117, 109, 101, 110, 116, 10, 69, 120, 116, 114, 97, 99, 116, 68, 97, 116, 101, 80, 97, - 114, 116, 65, 114, 103, 117, 109, 101, 110, 116, 32, 60, 45, 32, 69, 120, 116, 114, 97, 99, - 116, 68, 97, 116, 101, 80, 97, 114, 116, 10, 69, 120, 116, 114, 97, 99, 116, 73, 100, 101, - 110, 116, 105, 102, 105, 101, 114, 65, 114, 103, 117, 109, 101, 110, 116, 32, 60, 45, 32, 73, - 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 69, 120, 116, 114, 97, 99, 116, 83, 116, 114, - 105, 110, 103, 65, 114, 103, 117, 109, 101, 110, 116, 32, 60, 45, 32, 83, 116, 114, 105, 110, - 103, 76, 105, 116, 101, 114, 97, 108, 10, 69, 120, 116, 114, 97, 99, 116, 68, 97, 116, 101, - 80, 97, 114, 116, 32, 60, 45, 10, 32, 32, 32, 32, 89, 101, 97, 114, 75, 101, 121, 119, - 111, 114, 100, 32, 47, 32, 77, 111, 110, 116, 104, 75, 101, 121, 119, 111, 114, 100, 32, 47, - 32, 68, 97, 121, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 72, 111, 117, 114, 75, 101, - 121, 119, 111, 114, 100, 32, 47, 32, 77, 105, 110, 117, 116, 101, 75, 101, 121, 119, 111, 114, - 100, 32, 47, 32, 83, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, - 32, 32, 32, 32, 77, 105, 108, 108, 105, 115, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, - 114, 100, 32, 47, 32, 77, 105, 99, 114, 111, 115, 101, 99, 111, 110, 100, 75, 101, 121, 119, - 111, 114, 100, 32, 47, 32, 87, 101, 101, 107, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, - 81, 117, 97, 114, 116, 101, 114, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 68, 101, 99, - 97, 100, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, 32, 32, 32, 32, 67, 101, 110, - 116, 117, 114, 121, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 77, 105, 108, 108, 101, 110, - 110, 105, 117, 109, 75, 101, 121, 119, 111, 114, 100, 10, 69, 120, 116, 101, 114, 110, 97, 108, - 82, 101, 115, 111, 117, 114, 99, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, - 32, 67, 114, 101, 97, 116, 101, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, - 114, 99, 101, 83, 116, 109, 116, 32, 47, 32, 82, 101, 103, 105, 115, 116, 101, 114, 69, 120, - 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 83, 116, 109, 116, 32, 47, - 32, 68, 101, 115, 116, 114, 111, 121, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, - 117, 114, 99, 101, 83, 116, 109, 116, 32, 47, 32, 83, 104, 111, 119, 69, 120, 116, 101, 114, - 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 115, 83, 116, 109, 116, 10, 67, 114, 101, - 97, 116, 101, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 83, - 116, 109, 116, 32, 60, 45, 32, 39, 67, 82, 69, 65, 84, 69, 39, 32, 39, 69, 88, 84, - 69, 82, 78, 65, 76, 39, 32, 39, 82, 69, 83, 79, 85, 82, 67, 69, 39, 32, 83, 116, - 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 65, 116, 116, 97, 99, 104, 65, 108, - 105, 97, 115, 63, 32, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, - 101, 67, 114, 101, 97, 116, 105, 111, 110, 79, 112, 116, 105, 111, 110, 115, 63, 10, 82, 101, - 103, 105, 115, 116, 101, 114, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, - 99, 101, 83, 116, 109, 116, 32, 60, 45, 32, 39, 82, 69, 71, 73, 83, 84, 69, 82, 39, - 32, 39, 69, 88, 84, 69, 82, 78, 65, 76, 39, 32, 39, 82, 69, 83, 79, 85, 82, 67, - 69, 39, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 65, 116, 116, - 97, 99, 104, 65, 108, 105, 97, 115, 63, 32, 39, 70, 82, 79, 77, 39, 32, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 10, 68, 101, 115, 116, 114, 111, 121, 69, 120, 116, 101, 114, - 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 83, 116, 109, 116, 32, 60, 45, 32, 39, - 68, 69, 83, 84, 82, 79, 89, 39, 32, 39, 69, 88, 84, 69, 82, 78, 65, 76, 39, 32, - 39, 82, 69, 83, 79, 85, 82, 67, 69, 39, 32, 67, 111, 108, 73, 100, 10, 83, 104, 111, - 119, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 115, 83, 116, - 109, 116, 32, 60, 45, 32, 39, 83, 72, 79, 87, 39, 32, 83, 104, 111, 119, 65, 108, 108, - 77, 111, 100, 105, 102, 105, 101, 114, 63, 32, 39, 69, 88, 84, 69, 82, 78, 65, 76, 39, - 32, 39, 82, 69, 83, 79, 85, 82, 67, 69, 83, 39, 10, 83, 104, 111, 119, 65, 108, 108, - 77, 111, 100, 105, 102, 105, 101, 114, 32, 60, 45, 32, 39, 65, 76, 76, 39, 10, 35, 32, - 84, 104, 101, 32, 114, 101, 99, 105, 112, 101, 39, 115, 32, 99, 114, 101, 97, 116, 101, 32, - 112, 97, 114, 97, 109, 115, 46, 32, 73, 116, 115, 32, 111, 119, 110, 32, 114, 117, 108, 101, - 32, 114, 97, 116, 104, 101, 114, 32, 116, 104, 97, 110, 32, 65, 116, 116, 97, 99, 104, 79, - 112, 116, 105, 111, 110, 115, 58, 32, 116, 104, 101, 32, 116, 119, 111, 32, 111, 112, 116, 105, - 111, 110, 32, 98, 97, 103, 115, 32, 97, 114, 101, 10, 35, 32, 100, 105, 115, 116, 105, 110, - 99, 116, 44, 32, 97, 110, 100, 32, 97, 32, 115, 116, 97, 116, 101, 109, 101, 110, 116, 32, - 99, 97, 110, 32, 99, 97, 114, 114, 121, 32, 98, 111, 116, 104, 46, 10, 69, 120, 116, 101, - 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 67, 114, 101, 97, 116, 105, 111, 110, - 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, - 112, 121, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 10, 73, 110, 115, 101, 114, 116, 83, - 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 87, 105, 116, 104, 67, 108, 97, 117, - 115, 101, 63, 32, 39, 73, 78, 83, 69, 82, 84, 39, 32, 79, 114, 65, 99, 116, 105, 111, - 110, 63, 32, 39, 73, 78, 84, 79, 39, 32, 73, 110, 115, 101, 114, 116, 84, 97, 114, 103, - 101, 116, 32, 66, 121, 78, 97, 109, 101, 79, 114, 80, 111, 115, 105, 116, 105, 111, 110, 63, - 32, 73, 110, 115, 101, 114, 116, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 63, 32, 73, - 110, 115, 101, 114, 116, 86, 97, 108, 117, 101, 115, 32, 79, 110, 67, 111, 110, 102, 108, 105, - 99, 116, 67, 108, 97, 117, 115, 101, 63, 32, 82, 101, 116, 117, 114, 110, 105, 110, 103, 67, - 108, 97, 117, 115, 101, 63, 10, 79, 114, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 73, - 110, 115, 101, 114, 116, 79, 114, 82, 101, 112, 108, 97, 99, 101, 32, 47, 32, 73, 110, 115, - 101, 114, 116, 79, 114, 73, 103, 110, 111, 114, 101, 10, 73, 110, 115, 101, 114, 116, 79, 114, - 82, 101, 112, 108, 97, 99, 101, 32, 60, 45, 32, 39, 79, 82, 39, 32, 39, 82, 69, 80, - 76, 65, 67, 69, 39, 10, 73, 110, 115, 101, 114, 116, 79, 114, 73, 103, 110, 111, 114, 101, - 32, 60, 45, 32, 39, 79, 82, 39, 32, 39, 73, 71, 78, 79, 82, 69, 39, 10, 66, 121, - 78, 97, 109, 101, 79, 114, 80, 111, 115, 105, 116, 105, 111, 110, 32, 60, 45, 32, 73, 110, - 115, 101, 114, 116, 66, 121, 78, 97, 109, 101, 79, 114, 100, 101, 114, 32, 47, 32, 73, 110, - 115, 101, 114, 116, 66, 121, 80, 111, 115, 105, 116, 105, 111, 110, 79, 114, 100, 101, 114, 10, - 73, 110, 115, 101, 114, 116, 66, 121, 78, 97, 109, 101, 79, 114, 100, 101, 114, 32, 60, 45, - 32, 39, 66, 89, 39, 32, 73, 110, 115, 101, 114, 116, 66, 121, 78, 97, 109, 101, 10, 73, - 110, 115, 101, 114, 116, 66, 121, 80, 111, 115, 105, 116, 105, 111, 110, 79, 114, 100, 101, 114, - 32, 60, 45, 32, 39, 66, 89, 39, 32, 73, 110, 115, 101, 114, 116, 66, 121, 80, 111, 115, - 105, 116, 105, 111, 110, 10, 73, 110, 115, 101, 114, 116, 66, 121, 78, 97, 109, 101, 32, 60, - 45, 32, 39, 78, 65, 77, 69, 39, 10, 73, 110, 115, 101, 114, 116, 66, 121, 80, 111, 115, - 105, 116, 105, 111, 110, 32, 60, 45, 32, 39, 80, 79, 83, 73, 84, 73, 79, 78, 39, 10, - 73, 110, 115, 101, 114, 116, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 66, 97, 115, 101, - 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 73, 110, 115, 101, 114, 116, 65, 108, 105, 97, - 115, 63, 10, 73, 110, 115, 101, 114, 116, 65, 108, 105, 97, 115, 32, 60, 45, 32, 39, 65, - 83, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 67, 111, 108, 117, 109, 110, - 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 67, 111, 108, 73, 100, 41, 10, - 73, 110, 115, 101, 114, 116, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, - 80, 97, 114, 101, 110, 115, 40, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 41, 10, 73, - 110, 115, 101, 114, 116, 86, 97, 108, 117, 101, 115, 32, 60, 45, 32, 83, 101, 108, 101, 99, - 116, 73, 110, 115, 101, 114, 116, 86, 97, 108, 117, 101, 115, 32, 47, 32, 68, 101, 102, 97, - 117, 108, 116, 86, 97, 108, 117, 101, 115, 10, 83, 101, 108, 101, 99, 116, 73, 110, 115, 101, - 114, 116, 86, 97, 108, 117, 101, 115, 32, 60, 45, 32, 83, 101, 108, 101, 99, 116, 83, 116, - 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 10, 68, 101, 102, 97, - 117, 108, 116, 86, 97, 108, 117, 101, 115, 32, 60, 45, 32, 39, 68, 69, 70, 65, 85, 76, - 84, 39, 32, 39, 86, 65, 76, 85, 69, 83, 39, 10, 79, 110, 67, 111, 110, 102, 108, 105, - 99, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 79, 78, 39, 32, 39, 67, 79, - 78, 70, 76, 73, 67, 84, 39, 32, 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 84, 97, - 114, 103, 101, 116, 63, 32, 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 65, 99, 116, 105, - 111, 110, 10, 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 84, 97, 114, 103, 101, 116, 32, - 60, 45, 32, 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 69, 120, 112, 114, 101, 115, 115, - 105, 111, 110, 84, 97, 114, 103, 101, 116, 32, 47, 32, 79, 110, 67, 111, 110, 102, 108, 105, - 99, 116, 73, 110, 100, 101, 120, 84, 97, 114, 103, 101, 116, 10, 79, 110, 67, 111, 110, 102, - 108, 105, 99, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 114, 103, 101, 116, - 32, 60, 45, 32, 67, 111, 108, 117, 109, 110, 73, 100, 76, 105, 115, 116, 32, 87, 104, 101, - 114, 101, 67, 108, 97, 117, 115, 101, 63, 10, 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, - 73, 110, 100, 101, 120, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 39, 79, 78, 39, 32, - 39, 67, 79, 78, 83, 84, 82, 65, 73, 78, 84, 39, 32, 67, 111, 110, 115, 116, 114, 97, - 105, 110, 116, 78, 97, 109, 101, 10, 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 65, 99, - 116, 105, 111, 110, 32, 60, 45, 32, 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 85, 112, - 100, 97, 116, 101, 32, 47, 32, 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 78, 111, 116, - 104, 105, 110, 103, 10, 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 85, 112, 100, 97, 116, - 101, 32, 60, 45, 32, 39, 68, 79, 39, 32, 39, 85, 80, 68, 65, 84, 69, 39, 32, 39, - 83, 69, 84, 39, 32, 85, 112, 100, 97, 116, 101, 83, 101, 116, 67, 108, 97, 117, 115, 101, - 32, 87, 104, 101, 114, 101, 67, 108, 97, 117, 115, 101, 63, 10, 79, 110, 67, 111, 110, 102, - 108, 105, 99, 116, 78, 111, 116, 104, 105, 110, 103, 32, 60, 45, 32, 39, 68, 79, 39, 32, - 39, 78, 79, 84, 72, 73, 78, 71, 39, 10, 82, 101, 116, 117, 114, 110, 105, 110, 103, 67, - 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 82, 69, 84, 85, 82, 78, 73, 78, 71, 39, - 32, 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, 10, 76, 111, 97, 100, 83, 116, 97, 116, - 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 76, 79, 65, 68, 39, 32, 67, 111, 108, 73, - 100, 79, 114, 83, 116, 114, 105, 110, 103, 32, 70, 114, 111, 109, 83, 111, 117, 114, 99, 101, - 63, 32, 69, 120, 116, 101, 110, 115, 105, 111, 110, 65, 108, 105, 97, 115, 63, 10, 69, 120, - 116, 101, 110, 115, 105, 111, 110, 65, 108, 105, 97, 115, 32, 60, 45, 32, 39, 65, 83, 39, - 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 73, 110, 115, 116, 97, 108, 108, 83, - 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 70, 79, 82, 67, 69, 39, 63, - 32, 39, 73, 78, 83, 84, 65, 76, 76, 39, 32, 73, 110, 115, 116, 97, 108, 108, 65, 110, - 100, 76, 111, 97, 100, 63, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 79, 114, 83, - 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 70, 114, 111, 109, 83, 111, 117, - 114, 99, 101, 63, 32, 86, 101, 114, 115, 105, 111, 110, 78, 117, 109, 98, 101, 114, 63, 10, - 73, 110, 115, 116, 97, 108, 108, 65, 110, 100, 76, 111, 97, 100, 32, 60, 45, 32, 39, 65, - 78, 68, 39, 32, 39, 76, 79, 65, 68, 39, 10, 85, 112, 100, 97, 116, 101, 69, 120, 116, - 101, 110, 115, 105, 111, 110, 115, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, - 39, 85, 80, 68, 65, 84, 69, 39, 32, 39, 69, 88, 84, 69, 78, 83, 73, 79, 78, 83, - 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 73, 100, 101, 110, 116, 105, - 102, 105, 101, 114, 41, 41, 63, 10, 70, 114, 111, 109, 83, 111, 117, 114, 99, 101, 32, 60, - 45, 32, 70, 114, 111, 109, 83, 111, 117, 114, 99, 101, 73, 100, 101, 110, 116, 105, 102, 105, - 101, 114, 32, 47, 32, 70, 114, 111, 109, 83, 111, 117, 114, 99, 101, 83, 116, 114, 105, 110, - 103, 10, 70, 114, 111, 109, 83, 111, 117, 114, 99, 101, 73, 100, 101, 110, 116, 105, 102, 105, - 101, 114, 32, 60, 45, 32, 39, 70, 82, 79, 77, 39, 32, 73, 100, 101, 110, 116, 105, 102, - 105, 101, 114, 10, 70, 114, 111, 109, 83, 111, 117, 114, 99, 101, 83, 116, 114, 105, 110, 103, - 32, 60, 45, 32, 39, 70, 82, 79, 77, 39, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, - 101, 114, 97, 108, 10, 86, 101, 114, 115, 105, 111, 110, 78, 117, 109, 98, 101, 114, 32, 60, - 45, 32, 39, 86, 69, 82, 83, 73, 79, 78, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, - 101, 114, 79, 114, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, 69, 120, - 116, 101, 110, 115, 105, 111, 110, 82, 101, 112, 111, 115, 105, 116, 111, 114, 121, 83, 116, 97, - 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 67, 114, 101, 97, 116, 101, 69, 120, 116, 101, - 110, 115, 105, 111, 110, 82, 101, 112, 111, 115, 105, 116, 111, 114, 121, 83, 116, 109, 116, 32, - 47, 32, 68, 114, 111, 112, 69, 120, 116, 101, 110, 115, 105, 111, 110, 82, 101, 112, 111, 115, - 105, 116, 111, 114, 121, 83, 116, 109, 116, 10, 67, 114, 101, 97, 116, 101, 69, 120, 116, 101, - 110, 115, 105, 111, 110, 82, 101, 112, 111, 115, 105, 116, 111, 114, 121, 83, 116, 109, 116, 32, - 60, 45, 32, 39, 67, 82, 69, 65, 84, 69, 39, 32, 79, 114, 82, 101, 112, 108, 97, 99, - 101, 63, 32, 39, 69, 88, 84, 69, 78, 83, 73, 79, 78, 39, 32, 39, 82, 69, 80, 79, - 83, 73, 84, 79, 82, 89, 39, 32, 73, 102, 78, 111, 116, 69, 120, 105, 115, 116, 115, 63, - 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 32, 82, 101, 112, 111, 115, - 105, 116, 111, 114, 121, 80, 114, 101, 102, 105, 120, 32, 82, 101, 112, 111, 115, 105, 116, 111, - 114, 121, 80, 117, 98, 108, 105, 99, 75, 101, 121, 63, 10, 82, 101, 112, 111, 115, 105, 116, - 111, 114, 121, 80, 114, 101, 102, 105, 120, 32, 60, 45, 32, 39, 87, 73, 84, 72, 39, 63, - 32, 39, 80, 82, 69, 70, 73, 88, 39, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, - 114, 97, 108, 10, 82, 101, 112, 111, 115, 105, 116, 111, 114, 121, 80, 117, 98, 108, 105, 99, - 75, 101, 121, 32, 60, 45, 32, 39, 85, 83, 73, 78, 71, 39, 63, 32, 39, 80, 85, 66, - 76, 73, 67, 39, 32, 40, 39, 75, 69, 89, 83, 39, 32, 47, 32, 39, 75, 69, 89, 39, - 41, 32, 76, 105, 115, 116, 40, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, - 41, 10, 68, 114, 111, 112, 69, 120, 116, 101, 110, 115, 105, 111, 110, 82, 101, 112, 111, 115, - 105, 116, 111, 114, 121, 83, 116, 109, 116, 32, 60, 45, 32, 39, 68, 82, 79, 80, 39, 32, - 39, 69, 88, 84, 69, 78, 83, 73, 79, 78, 39, 32, 39, 82, 69, 80, 79, 83, 73, 84, - 79, 82, 89, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 67, 111, 108, 73, 100, - 79, 114, 83, 116, 114, 105, 110, 103, 10, 84, 97, 98, 108, 101, 77, 97, 116, 99, 104, 82, - 101, 99, 111, 103, 110, 105, 122, 101, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 77, - 65, 84, 67, 72, 95, 82, 69, 67, 79, 71, 78, 73, 90, 69, 39, 32, 80, 97, 114, 101, - 110, 115, 40, 77, 97, 116, 99, 104, 82, 101, 99, 111, 103, 110, 105, 122, 101, 66, 111, 100, - 121, 41, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 10, 77, 97, 116, 99, 104, - 82, 101, 99, 111, 103, 110, 105, 122, 101, 66, 111, 100, 121, 32, 60, 45, 32, 77, 97, 116, - 99, 104, 82, 101, 99, 111, 103, 110, 105, 122, 101, 67, 108, 97, 117, 115, 101, 43, 10, 77, - 97, 116, 99, 104, 82, 101, 99, 111, 103, 110, 105, 122, 101, 67, 108, 97, 117, 115, 101, 32, - 60, 45, 32, 77, 82, 80, 97, 114, 116, 105, 116, 105, 111, 110, 32, 47, 32, 77, 82, 79, - 114, 100, 101, 114, 66, 121, 32, 47, 32, 77, 82, 77, 101, 97, 115, 117, 114, 101, 115, 32, - 47, 32, 77, 82, 82, 111, 119, 115, 32, 47, 32, 77, 82, 83, 107, 105, 112, 32, 47, 32, - 77, 82, 80, 97, 116, 116, 101, 114, 110, 32, 47, 32, 77, 82, 83, 117, 98, 115, 101, 116, - 32, 47, 32, 77, 82, 68, 101, 102, 105, 110, 101, 65, 117, 116, 111, 32, 47, 32, 77, 82, - 68, 101, 102, 105, 110, 101, 10, 77, 82, 80, 97, 114, 116, 105, 116, 105, 111, 110, 32, 60, - 45, 32, 87, 105, 110, 100, 111, 119, 80, 97, 114, 116, 105, 116, 105, 111, 110, 10, 77, 82, - 79, 114, 100, 101, 114, 66, 121, 32, 60, 45, 32, 79, 114, 100, 101, 114, 66, 121, 67, 108, - 97, 117, 115, 101, 10, 77, 82, 77, 101, 97, 115, 117, 114, 101, 115, 32, 60, 45, 32, 77, - 101, 97, 115, 117, 114, 101, 115, 67, 108, 97, 117, 115, 101, 10, 77, 82, 82, 111, 119, 115, - 32, 60, 45, 32, 82, 111, 119, 115, 80, 101, 114, 77, 97, 116, 99, 104, 10, 77, 82, 83, - 107, 105, 112, 32, 60, 45, 32, 65, 102, 116, 101, 114, 77, 97, 116, 99, 104, 83, 107, 105, - 112, 10, 77, 82, 80, 97, 116, 116, 101, 114, 110, 32, 60, 45, 32, 80, 97, 116, 116, 101, - 114, 110, 67, 108, 97, 117, 115, 101, 10, 77, 82, 83, 117, 98, 115, 101, 116, 32, 60, 45, - 32, 83, 117, 98, 115, 101, 116, 67, 108, 97, 117, 115, 101, 10, 77, 82, 68, 101, 102, 105, - 110, 101, 65, 117, 116, 111, 32, 60, 45, 32, 40, 39, 68, 69, 70, 73, 78, 69, 39, 32, - 47, 32, 39, 68, 69, 70, 73, 78, 69, 83, 39, 41, 32, 39, 65, 85, 84, 79, 39, 10, - 77, 82, 68, 101, 102, 105, 110, 101, 32, 60, 45, 32, 68, 101, 102, 105, 110, 101, 67, 108, - 97, 117, 115, 101, 10, 77, 101, 97, 115, 117, 114, 101, 115, 67, 108, 97, 117, 115, 101, 32, - 60, 45, 32, 39, 77, 69, 65, 83, 85, 82, 69, 83, 39, 32, 76, 105, 115, 116, 40, 77, - 101, 97, 115, 117, 114, 101, 115, 69, 108, 101, 109, 101, 110, 116, 41, 10, 77, 101, 97, 115, - 117, 114, 101, 115, 69, 108, 101, 109, 101, 110, 116, 32, 60, 45, 32, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 32, 39, 65, 83, 39, 32, 67, 111, 108, 76, 97, 98, 101, 108, 79, - 114, 83, 116, 114, 105, 110, 103, 10, 77, 101, 97, 115, 117, 114, 101, 83, 101, 109, 97, 110, - 116, 105, 99, 115, 32, 60, 45, 32, 82, 117, 110, 110, 105, 110, 103, 83, 101, 109, 97, 110, - 116, 105, 99, 115, 32, 47, 32, 70, 105, 110, 97, 108, 83, 101, 109, 97, 110, 116, 105, 99, - 115, 10, 82, 117, 110, 110, 105, 110, 103, 83, 101, 109, 97, 110, 116, 105, 99, 115, 32, 60, - 45, 32, 39, 82, 85, 78, 78, 73, 78, 71, 39, 10, 70, 105, 110, 97, 108, 83, 101, 109, - 97, 110, 116, 105, 99, 115, 32, 60, 45, 32, 39, 70, 73, 78, 65, 76, 39, 10, 82, 111, - 119, 115, 80, 101, 114, 77, 97, 116, 99, 104, 32, 60, 45, 32, 79, 110, 101, 82, 111, 119, - 80, 101, 114, 77, 97, 116, 99, 104, 32, 47, 32, 65, 108, 108, 82, 111, 119, 115, 80, 101, - 114, 77, 97, 116, 99, 104, 10, 79, 110, 101, 82, 111, 119, 80, 101, 114, 77, 97, 116, 99, - 104, 32, 60, 45, 32, 39, 79, 78, 69, 39, 32, 39, 82, 79, 87, 39, 32, 39, 80, 69, - 82, 39, 32, 39, 77, 65, 84, 67, 72, 39, 10, 65, 108, 108, 82, 111, 119, 115, 80, 101, - 114, 77, 97, 116, 99, 104, 32, 60, 45, 32, 39, 65, 76, 76, 39, 32, 39, 82, 79, 87, - 83, 39, 32, 39, 80, 69, 82, 39, 32, 39, 77, 65, 84, 67, 72, 39, 32, 65, 108, 108, - 82, 111, 119, 115, 69, 109, 112, 116, 121, 77, 97, 116, 99, 104, 101, 115, 63, 10, 65, 108, - 108, 82, 111, 119, 115, 69, 109, 112, 116, 121, 77, 97, 116, 99, 104, 101, 115, 32, 60, 45, - 32, 83, 104, 111, 119, 69, 109, 112, 116, 121, 77, 97, 116, 99, 104, 101, 115, 32, 47, 32, - 79, 109, 105, 116, 69, 109, 112, 116, 121, 77, 97, 116, 99, 104, 101, 115, 32, 47, 32, 87, - 105, 116, 104, 85, 110, 109, 97, 116, 99, 104, 101, 100, 82, 111, 119, 115, 10, 83, 104, 111, - 119, 69, 109, 112, 116, 121, 77, 97, 116, 99, 104, 101, 115, 32, 60, 45, 32, 39, 83, 72, - 79, 87, 39, 32, 39, 69, 77, 80, 84, 89, 39, 32, 39, 77, 65, 84, 67, 72, 69, 83, - 39, 10, 79, 109, 105, 116, 69, 109, 112, 116, 121, 77, 97, 116, 99, 104, 101, 115, 32, 60, - 45, 32, 39, 79, 77, 73, 84, 39, 32, 39, 69, 77, 80, 84, 89, 39, 32, 39, 77, 65, - 84, 67, 72, 69, 83, 39, 10, 87, 105, 116, 104, 85, 110, 109, 97, 116, 99, 104, 101, 100, - 82, 111, 119, 115, 32, 60, 45, 32, 39, 87, 73, 84, 72, 39, 32, 39, 85, 78, 77, 65, - 84, 67, 72, 69, 68, 39, 32, 39, 82, 79, 87, 83, 39, 10, 65, 102, 116, 101, 114, 77, - 97, 116, 99, 104, 83, 107, 105, 112, 32, 60, 45, 32, 39, 65, 70, 84, 69, 82, 39, 32, - 39, 77, 65, 84, 67, 72, 39, 32, 39, 83, 75, 73, 80, 39, 32, 65, 102, 116, 101, 114, - 77, 97, 116, 99, 104, 83, 107, 105, 112, 84, 97, 114, 103, 101, 116, 10, 65, 102, 116, 101, - 114, 77, 97, 116, 99, 104, 83, 107, 105, 112, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, - 83, 107, 105, 112, 84, 111, 78, 101, 120, 116, 82, 111, 119, 32, 47, 32, 83, 107, 105, 112, - 80, 97, 115, 116, 76, 97, 115, 116, 82, 111, 119, 32, 47, 32, 83, 107, 105, 112, 84, 111, - 70, 105, 114, 115, 116, 86, 97, 114, 32, 47, 32, 83, 107, 105, 112, 84, 111, 76, 97, 115, - 116, 86, 97, 114, 32, 47, 32, 83, 107, 105, 112, 84, 111, 86, 97, 114, 10, 83, 107, 105, - 112, 84, 111, 78, 101, 120, 116, 82, 111, 119, 32, 60, 45, 32, 39, 84, 79, 39, 32, 39, - 78, 69, 88, 84, 39, 32, 39, 82, 79, 87, 39, 10, 83, 107, 105, 112, 80, 97, 115, 116, - 76, 97, 115, 116, 82, 111, 119, 32, 60, 45, 32, 39, 80, 65, 83, 84, 39, 32, 39, 76, - 65, 83, 84, 39, 32, 39, 82, 79, 87, 39, 10, 83, 107, 105, 112, 84, 111, 70, 105, 114, - 115, 116, 86, 97, 114, 32, 60, 45, 32, 39, 84, 79, 39, 32, 39, 70, 73, 82, 83, 84, - 39, 32, 67, 111, 108, 76, 97, 98, 101, 108, 79, 114, 83, 116, 114, 105, 110, 103, 10, 83, - 107, 105, 112, 84, 111, 76, 97, 115, 116, 86, 97, 114, 32, 60, 45, 32, 39, 84, 79, 39, - 32, 39, 76, 65, 83, 84, 39, 32, 67, 111, 108, 76, 97, 98, 101, 108, 79, 114, 83, 116, - 114, 105, 110, 103, 10, 35, 32, 110, 97, 109, 105, 110, 103, 32, 97, 32, 118, 97, 114, 105, - 97, 98, 108, 101, 32, 119, 105, 116, 104, 111, 117, 116, 32, 115, 97, 121, 105, 110, 103, 32, - 119, 104, 105, 99, 104, 32, 111, 102, 32, 105, 116, 115, 32, 114, 111, 119, 115, 32, 109, 101, - 97, 110, 115, 32, 105, 116, 115, 32, 108, 97, 115, 116, 32, 111, 110, 101, 10, 83, 107, 105, - 112, 84, 111, 86, 97, 114, 32, 60, 45, 32, 39, 84, 79, 39, 32, 67, 111, 108, 76, 97, - 98, 101, 108, 79, 114, 83, 116, 114, 105, 110, 103, 10, 80, 97, 116, 116, 101, 114, 110, 67, - 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 80, 65, 84, 84, 69, 82, 78, 39, 32, 80, - 97, 114, 101, 110, 115, 40, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 41, 10, 35, 32, - 97, 108, 116, 101, 114, 110, 97, 116, 105, 111, 110, 32, 98, 105, 110, 100, 115, 32, 108, 111, - 111, 115, 101, 115, 116, 44, 32, 116, 104, 101, 110, 32, 99, 111, 110, 99, 97, 116, 101, 110, - 97, 116, 105, 111, 110, 44, 32, 116, 104, 101, 110, 32, 113, 117, 97, 110, 116, 105, 102, 105, - 101, 114, 115, 10, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 32, 60, 45, 32, 82, 111, - 119, 80, 97, 116, 116, 101, 114, 110, 84, 101, 114, 109, 32, 82, 111, 119, 80, 97, 116, 116, - 101, 114, 110, 65, 108, 116, 101, 114, 110, 97, 116, 105, 118, 101, 42, 10, 82, 111, 119, 80, - 97, 116, 116, 101, 114, 110, 65, 108, 116, 101, 114, 110, 97, 116, 105, 118, 101, 32, 60, 45, - 32, 39, 124, 39, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 84, 101, 114, 109, 10, - 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 84, 101, 114, 109, 32, 60, 45, 32, 82, 111, - 119, 80, 97, 116, 116, 101, 114, 110, 70, 97, 99, 116, 111, 114, 43, 10, 82, 111, 119, 80, - 97, 116, 116, 101, 114, 110, 70, 97, 99, 116, 111, 114, 32, 60, 45, 32, 82, 111, 119, 80, - 97, 116, 116, 101, 114, 110, 80, 114, 105, 109, 97, 114, 121, 32, 82, 111, 119, 80, 97, 116, - 116, 101, 114, 110, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 63, 10, 35, 32, 80, 69, - 82, 77, 85, 84, 69, 32, 104, 97, 115, 32, 116, 111, 32, 98, 101, 32, 116, 114, 105, 101, - 100, 32, 98, 101, 102, 111, 114, 101, 32, 97, 32, 112, 108, 97, 105, 110, 32, 108, 97, 98, - 101, 108, 44, 32, 119, 104, 105, 99, 104, 32, 119, 111, 117, 108, 100, 32, 111, 116, 104, 101, - 114, 119, 105, 115, 101, 32, 99, 111, 110, 115, 117, 109, 101, 32, 116, 104, 101, 32, 107, 101, - 121, 119, 111, 114, 100, 10, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 80, 114, 105, 109, - 97, 114, 121, 32, 60, 45, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 80, 101, 114, - 109, 117, 116, 101, 32, 47, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 69, 120, 99, - 108, 117, 115, 105, 111, 110, 32, 47, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 69, - 109, 112, 116, 121, 32, 47, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 71, 114, 111, - 117, 112, 32, 47, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 65, 110, 99, 104, 111, - 114, 32, 47, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 76, 97, 98, 101, 108, 10, - 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 80, 101, 114, 109, 117, 116, 101, 32, 60, 45, - 32, 39, 80, 69, 82, 77, 85, 84, 69, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, - 115, 116, 40, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 41, 41, 10, 35, 32, 94, 32, - 97, 110, 100, 32, 36, 32, 104, 111, 108, 100, 32, 97, 116, 32, 116, 104, 101, 32, 101, 110, - 100, 115, 32, 111, 102, 32, 116, 104, 101, 32, 112, 97, 114, 116, 105, 116, 105, 111, 110, 32, - 119, 105, 116, 104, 111, 117, 116, 32, 116, 97, 107, 105, 110, 103, 32, 97, 32, 114, 111, 119, - 10, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 65, 110, 99, 104, 111, 114, 32, 60, 45, - 32, 80, 97, 116, 116, 101, 114, 110, 83, 116, 97, 114, 116, 32, 47, 32, 80, 97, 116, 116, - 101, 114, 110, 69, 110, 100, 10, 80, 97, 116, 116, 101, 114, 110, 83, 116, 97, 114, 116, 32, - 60, 45, 32, 39, 94, 39, 10, 80, 97, 116, 116, 101, 114, 110, 69, 110, 100, 32, 60, 45, - 32, 39, 36, 39, 10, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 71, 114, 111, 117, 112, - 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 82, 111, 119, 80, 97, 116, 116, 101, 114, - 110, 41, 10, 35, 32, 40, 41, 32, 109, 97, 116, 99, 104, 101, 115, 32, 119, 104, 101, 114, - 101, 32, 105, 116, 32, 115, 116, 97, 110, 100, 115, 32, 119, 105, 116, 104, 111, 117, 116, 32, - 116, 97, 107, 105, 110, 103, 32, 97, 32, 114, 111, 119, 10, 82, 111, 119, 80, 97, 116, 116, - 101, 114, 110, 69, 109, 112, 116, 121, 32, 60, 45, 32, 39, 40, 39, 32, 39, 41, 39, 10, - 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 69, 120, 99, 108, 117, 115, 105, 111, 110, 32, - 60, 45, 32, 39, 123, 39, 32, 39, 45, 39, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, - 110, 32, 39, 45, 39, 32, 39, 125, 39, 10, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, - 76, 97, 98, 101, 108, 32, 60, 45, 32, 67, 111, 108, 76, 97, 98, 101, 108, 79, 114, 83, - 116, 114, 105, 110, 103, 10, 35, 32, 97, 32, 116, 114, 97, 105, 108, 105, 110, 103, 32, 63, - 32, 109, 97, 107, 101, 115, 32, 116, 104, 101, 32, 113, 117, 97, 110, 116, 105, 102, 105, 101, - 114, 32, 114, 101, 108, 117, 99, 116, 97, 110, 116, 58, 32, 105, 116, 32, 112, 114, 101, 102, - 101, 114, 115, 32, 116, 104, 101, 32, 102, 101, 119, 101, 115, 116, 32, 114, 101, 112, 101, 116, - 105, 116, 105, 111, 110, 115, 32, 114, 97, 116, 104, 101, 114, 32, 116, 104, 97, 110, 32, 116, - 104, 101, 32, 109, 111, 115, 116, 10, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 81, 117, - 97, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 82, 111, 119, 80, 97, 116, 116, 101, - 114, 110, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 75, 105, 110, 100, 32, 81, 117, 97, - 110, 116, 105, 102, 105, 101, 114, 82, 101, 108, 117, 99, 116, 97, 110, 116, 63, 10, 82, 111, - 119, 80, 97, 116, 116, 101, 114, 110, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 75, 105, - 110, 100, 32, 60, 45, 32, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 83, 116, 97, 114, - 32, 47, 32, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 80, 108, 117, 115, 32, 47, 32, - 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 79, 112, 116, 105, 111, 110, 97, 108, 32, 47, - 32, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 82, 97, 110, 103, 101, 10, 81, 117, 97, - 110, 116, 105, 102, 105, 101, 114, 82, 101, 108, 117, 99, 116, 97, 110, 116, 32, 60, 45, 32, - 39, 63, 39, 10, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 83, 116, 97, 114, 32, 60, - 45, 32, 39, 42, 39, 10, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 80, 108, 117, 115, - 32, 60, 45, 32, 39, 43, 39, 10, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 79, 112, - 116, 105, 111, 110, 97, 108, 32, 60, 45, 32, 39, 63, 39, 10, 81, 117, 97, 110, 116, 105, - 102, 105, 101, 114, 82, 97, 110, 103, 101, 32, 60, 45, 32, 39, 123, 39, 32, 81, 117, 97, - 110, 116, 105, 102, 105, 101, 114, 66, 111, 117, 110, 100, 115, 32, 39, 125, 39, 10, 81, 117, - 97, 110, 116, 105, 102, 105, 101, 114, 66, 111, 117, 110, 100, 115, 32, 60, 45, 32, 81, 117, - 97, 110, 116, 105, 102, 105, 101, 114, 77, 105, 110, 77, 97, 120, 32, 47, 32, 81, 117, 97, - 110, 116, 105, 102, 105, 101, 114, 77, 105, 110, 32, 47, 32, 81, 117, 97, 110, 116, 105, 102, - 105, 101, 114, 77, 97, 120, 32, 47, 32, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 69, - 120, 97, 99, 116, 10, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 77, 105, 110, 77, 97, - 120, 32, 60, 45, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 32, 39, - 44, 39, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 10, 81, 117, 97, - 110, 116, 105, 102, 105, 101, 114, 77, 105, 110, 32, 60, 45, 32, 78, 117, 109, 98, 101, 114, - 76, 105, 116, 101, 114, 97, 108, 32, 39, 44, 39, 10, 81, 117, 97, 110, 116, 105, 102, 105, - 101, 114, 77, 97, 120, 32, 60, 45, 32, 39, 44, 39, 32, 78, 117, 109, 98, 101, 114, 76, - 105, 116, 101, 114, 97, 108, 10, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 69, 120, 97, - 99, 116, 32, 60, 45, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 10, - 83, 117, 98, 115, 101, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 83, 85, 66, - 83, 69, 84, 39, 32, 76, 105, 115, 116, 40, 83, 117, 98, 115, 101, 116, 69, 108, 101, 109, - 101, 110, 116, 41, 10, 83, 117, 98, 115, 101, 116, 69, 108, 101, 109, 101, 110, 116, 32, 60, - 45, 32, 67, 111, 108, 76, 97, 98, 101, 108, 79, 114, 83, 116, 114, 105, 110, 103, 32, 39, - 61, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 67, 111, 108, 76, 97, - 98, 101, 108, 79, 114, 83, 116, 114, 105, 110, 103, 41, 41, 10, 68, 101, 102, 105, 110, 101, - 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 40, 39, 68, 69, 70, 73, 78, 69, 39, 32, - 47, 32, 39, 68, 69, 70, 73, 78, 69, 83, 39, 41, 32, 76, 105, 115, 116, 40, 68, 101, - 102, 105, 110, 101, 69, 108, 101, 109, 101, 110, 116, 41, 10, 68, 101, 102, 105, 110, 101, 69, - 108, 101, 109, 101, 110, 116, 32, 60, 45, 32, 67, 111, 108, 76, 97, 98, 101, 108, 79, 114, - 83, 116, 114, 105, 110, 103, 32, 39, 65, 83, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, - 111, 110, 10, 77, 101, 114, 103, 101, 73, 110, 116, 111, 83, 116, 97, 116, 101, 109, 101, 110, - 116, 32, 60, 45, 32, 87, 105, 116, 104, 67, 108, 97, 117, 115, 101, 63, 32, 39, 77, 69, - 82, 71, 69, 39, 32, 39, 73, 78, 84, 79, 39, 32, 84, 97, 114, 103, 101, 116, 79, 112, - 116, 65, 108, 105, 97, 115, 32, 77, 101, 114, 103, 101, 73, 110, 116, 111, 85, 115, 105, 110, - 103, 67, 108, 97, 117, 115, 101, 32, 74, 111, 105, 110, 81, 117, 97, 108, 105, 102, 105, 101, - 114, 32, 77, 101, 114, 103, 101, 77, 97, 116, 99, 104, 43, 32, 82, 101, 116, 117, 114, 110, - 105, 110, 103, 67, 108, 97, 117, 115, 101, 63, 10, 77, 101, 114, 103, 101, 73, 110, 116, 111, - 85, 115, 105, 110, 103, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 85, 83, 73, 78, - 71, 39, 32, 84, 97, 98, 108, 101, 82, 101, 102, 10, 77, 101, 114, 103, 101, 77, 97, 116, - 99, 104, 32, 60, 45, 32, 77, 97, 116, 99, 104, 101, 100, 67, 108, 97, 117, 115, 101, 32, - 47, 32, 78, 111, 116, 77, 97, 116, 99, 104, 101, 100, 67, 108, 97, 117, 115, 101, 10, 77, - 97, 116, 99, 104, 101, 100, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 87, 72, 69, - 78, 39, 32, 39, 77, 65, 84, 67, 72, 69, 68, 39, 32, 65, 110, 100, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 63, 32, 39, 84, 72, 69, 78, 39, 32, 77, 97, 116, 99, 104, - 101, 100, 67, 108, 97, 117, 115, 101, 65, 99, 116, 105, 111, 110, 10, 77, 97, 116, 99, 104, - 101, 100, 67, 108, 97, 117, 115, 101, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 85, 112, - 100, 97, 116, 101, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 47, 32, 68, 101, - 108, 101, 116, 101, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 47, 32, 73, 110, - 115, 101, 114, 116, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 47, 32, 68, 111, - 78, 111, 116, 104, 105, 110, 103, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 47, - 32, 69, 114, 114, 111, 114, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, 101, 10, 85, 112, - 100, 97, 116, 101, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, - 85, 80, 68, 65, 84, 69, 39, 32, 85, 112, 100, 97, 116, 101, 77, 97, 116, 99, 104, 73, - 110, 102, 111, 63, 10, 85, 112, 100, 97, 116, 101, 77, 97, 116, 99, 104, 73, 110, 102, 111, - 32, 60, 45, 32, 85, 112, 100, 97, 116, 101, 77, 97, 116, 99, 104, 83, 101, 116, 65, 99, - 116, 105, 111, 110, 32, 47, 32, 85, 112, 100, 97, 116, 101, 66, 121, 78, 97, 109, 101, 79, - 114, 80, 111, 115, 105, 116, 105, 111, 110, 10, 85, 112, 100, 97, 116, 101, 77, 97, 116, 99, - 104, 83, 101, 116, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 85, 112, 100, 97, 116, 101, - 77, 97, 116, 99, 104, 83, 101, 116, 67, 108, 97, 117, 115, 101, 10, 85, 112, 100, 97, 116, - 101, 66, 121, 78, 97, 109, 101, 79, 114, 80, 111, 115, 105, 116, 105, 111, 110, 32, 60, 45, - 32, 66, 121, 78, 97, 109, 101, 79, 114, 80, 111, 115, 105, 116, 105, 111, 110, 10, 68, 101, - 108, 101, 116, 101, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, - 68, 69, 76, 69, 84, 69, 39, 10, 73, 110, 115, 101, 114, 116, 77, 97, 116, 99, 104, 67, - 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 73, 78, 83, 69, 82, 84, 39, 32, 73, 110, - 115, 101, 114, 116, 77, 97, 116, 99, 104, 73, 110, 102, 111, 63, 10, 73, 110, 115, 101, 114, - 116, 77, 97, 116, 99, 104, 73, 110, 102, 111, 32, 60, 45, 32, 73, 110, 115, 101, 114, 116, - 86, 97, 108, 117, 101, 115, 76, 105, 115, 116, 32, 47, 32, 73, 110, 115, 101, 114, 116, 68, - 101, 102, 97, 117, 108, 116, 86, 97, 108, 117, 101, 115, 32, 47, 32, 73, 110, 115, 101, 114, - 116, 66, 121, 78, 97, 109, 101, 79, 114, 80, 111, 115, 105, 116, 105, 111, 110, 10, 73, 110, - 115, 101, 114, 116, 68, 101, 102, 97, 117, 108, 116, 86, 97, 108, 117, 101, 115, 32, 60, 45, - 32, 39, 68, 69, 70, 65, 85, 76, 84, 39, 32, 39, 86, 65, 76, 85, 69, 83, 39, 10, - 73, 110, 115, 101, 114, 116, 66, 121, 78, 97, 109, 101, 79, 114, 80, 111, 115, 105, 116, 105, - 111, 110, 32, 60, 45, 32, 66, 121, 78, 97, 109, 101, 79, 114, 80, 111, 115, 105, 116, 105, - 111, 110, 63, 32, 39, 42, 39, 63, 10, 73, 110, 115, 101, 114, 116, 86, 97, 108, 117, 101, - 115, 76, 105, 115, 116, 32, 60, 45, 32, 73, 110, 115, 101, 114, 116, 67, 111, 108, 117, 109, - 110, 76, 105, 115, 116, 63, 32, 39, 86, 65, 76, 85, 69, 83, 39, 32, 80, 97, 114, 101, - 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 41, - 10, 68, 111, 78, 111, 116, 104, 105, 110, 103, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, - 101, 32, 60, 45, 32, 39, 68, 79, 39, 32, 39, 78, 79, 84, 72, 73, 78, 71, 39, 10, - 69, 114, 114, 111, 114, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, - 39, 69, 82, 82, 79, 82, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 10, - 85, 112, 100, 97, 116, 101, 77, 97, 116, 99, 104, 83, 101, 116, 67, 108, 97, 117, 115, 101, - 32, 60, 45, 32, 39, 83, 69, 84, 39, 32, 85, 112, 100, 97, 116, 101, 77, 97, 116, 99, - 104, 83, 101, 116, 73, 110, 102, 111, 10, 85, 112, 100, 97, 116, 101, 77, 97, 116, 99, 104, - 83, 101, 116, 73, 110, 102, 111, 32, 60, 45, 32, 85, 112, 100, 97, 116, 101, 83, 101, 116, - 67, 108, 97, 117, 115, 101, 32, 47, 32, 83, 116, 97, 114, 83, 121, 109, 98, 111, 108, 10, - 65, 110, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 65, 78, - 68, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 78, 111, 116, 77, 97, 116, - 99, 104, 101, 100, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 87, 72, 69, 78, 39, - 32, 39, 78, 79, 84, 39, 32, 39, 77, 65, 84, 67, 72, 69, 68, 39, 32, 66, 121, 83, - 111, 117, 114, 99, 101, 79, 114, 84, 97, 114, 103, 101, 116, 63, 32, 65, 110, 100, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 63, 32, 39, 84, 72, 69, 78, 39, 32, 77, 97, 116, - 99, 104, 101, 100, 67, 108, 97, 117, 115, 101, 65, 99, 116, 105, 111, 110, 10, 66, 121, 83, - 111, 117, 114, 99, 101, 79, 114, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 66, 121, 83, - 111, 117, 114, 99, 101, 32, 47, 32, 66, 121, 84, 97, 114, 103, 101, 116, 10, 66, 121, 83, - 111, 117, 114, 99, 101, 32, 60, 45, 32, 39, 66, 89, 39, 32, 39, 83, 79, 85, 82, 67, - 69, 39, 10, 66, 121, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 39, 66, 89, 39, 32, - 39, 84, 65, 82, 71, 69, 84, 39, 10, 80, 105, 118, 111, 116, 83, 116, 97, 116, 101, 109, - 101, 110, 116, 32, 60, 45, 32, 80, 105, 118, 111, 116, 75, 101, 121, 119, 111, 114, 100, 32, - 84, 97, 98, 108, 101, 82, 101, 102, 32, 80, 105, 118, 111, 116, 79, 110, 63, 32, 80, 105, - 118, 111, 116, 85, 115, 105, 110, 103, 63, 32, 80, 105, 118, 111, 116, 71, 114, 111, 117, 112, - 66, 121, 76, 105, 115, 116, 63, 10, 80, 105, 118, 111, 116, 79, 110, 32, 60, 45, 32, 39, - 79, 78, 39, 32, 80, 105, 118, 111, 116, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 10, - 80, 105, 118, 111, 116, 85, 115, 105, 110, 103, 32, 60, 45, 32, 39, 85, 83, 73, 78, 71, - 39, 32, 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, 10, 80, 105, 118, 111, 116, 67, 111, - 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 80, 105, 118, - 111, 116, 67, 111, 108, 117, 109, 110, 69, 110, 116, 114, 121, 41, 10, 80, 105, 118, 111, 116, - 67, 111, 108, 117, 109, 110, 69, 110, 116, 114, 121, 32, 60, 45, 32, 80, 105, 118, 111, 116, - 67, 111, 108, 117, 109, 110, 83, 117, 98, 113, 117, 101, 114, 121, 32, 47, 32, 80, 105, 118, - 111, 116, 86, 97, 108, 117, 101, 76, 105, 115, 116, 32, 47, 32, 80, 105, 118, 111, 116, 67, - 111, 108, 117, 109, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 80, 105, 118, 111, - 116, 67, 111, 108, 117, 109, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, - 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 80, 105, 118, 111, 116, 67, 111, 108, - 117, 109, 110, 83, 117, 98, 113, 117, 101, 114, 121, 32, 60, 45, 32, 66, 97, 115, 101, 69, - 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 73, 78, 39, 32, 80, 97, 114, 101, 110, - 115, 40, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, - 101, 114, 110, 97, 108, 41, 10, 80, 105, 118, 111, 116, 75, 101, 121, 119, 111, 114, 100, 32, - 60, 45, 32, 39, 80, 73, 86, 79, 84, 39, 32, 47, 32, 39, 80, 73, 86, 79, 84, 95, - 87, 73, 68, 69, 82, 39, 10, 85, 110, 112, 105, 118, 111, 116, 75, 101, 121, 119, 111, 114, - 100, 32, 60, 45, 32, 39, 85, 78, 80, 73, 86, 79, 84, 39, 32, 47, 32, 39, 80, 73, - 86, 79, 84, 95, 76, 79, 78, 71, 69, 82, 39, 10, 85, 110, 112, 105, 118, 111, 116, 83, - 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 85, 110, 112, 105, 118, 111, 116, 75, - 101, 121, 119, 111, 114, 100, 32, 84, 97, 98, 108, 101, 82, 101, 102, 32, 39, 79, 78, 39, - 32, 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, 32, 73, 110, 116, 111, 78, 97, 109, 101, - 86, 97, 108, 117, 101, 115, 63, 10, 73, 110, 116, 111, 78, 97, 109, 101, 86, 97, 108, 117, - 101, 115, 32, 60, 45, 32, 39, 73, 78, 84, 79, 39, 32, 39, 78, 65, 77, 69, 39, 32, - 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 32, 86, 97, 108, 117, 101, 79, - 114, 86, 97, 108, 117, 101, 115, 32, 79, 112, 116, 105, 111, 110, 97, 108, 80, 97, 114, 101, - 110, 115, 78, 97, 109, 101, 76, 105, 115, 116, 10, 86, 97, 108, 117, 101, 79, 114, 86, 97, - 108, 117, 101, 115, 32, 60, 45, 32, 39, 86, 65, 76, 85, 69, 39, 32, 47, 32, 39, 86, - 65, 76, 85, 69, 83, 39, 10, 79, 112, 116, 105, 111, 110, 97, 108, 80, 97, 114, 101, 110, - 115, 78, 97, 109, 101, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 116, 104, - 101, 115, 105, 122, 101, 100, 78, 97, 109, 101, 76, 105, 115, 116, 32, 47, 32, 66, 97, 114, - 101, 78, 97, 109, 101, 76, 105, 115, 116, 10, 80, 97, 114, 101, 110, 116, 104, 101, 115, 105, - 122, 101, 100, 78, 97, 109, 101, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, - 115, 40, 76, 105, 115, 116, 40, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, - 41, 41, 10, 66, 97, 114, 101, 78, 97, 109, 101, 76, 105, 115, 116, 32, 60, 45, 32, 76, - 105, 115, 116, 40, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 41, 10, 73, - 110, 99, 108, 117, 100, 101, 79, 114, 69, 120, 99, 108, 117, 100, 101, 78, 117, 108, 108, 115, - 32, 60, 45, 32, 73, 110, 99, 108, 117, 100, 101, 78, 117, 108, 108, 115, 32, 47, 32, 69, - 120, 99, 108, 117, 100, 101, 78, 117, 108, 108, 115, 10, 73, 110, 99, 108, 117, 100, 101, 78, - 117, 108, 108, 115, 32, 60, 45, 32, 39, 73, 78, 67, 76, 85, 68, 69, 39, 32, 39, 78, - 85, 76, 76, 83, 39, 10, 69, 120, 99, 108, 117, 100, 101, 78, 117, 108, 108, 115, 32, 60, - 45, 32, 39, 69, 88, 67, 76, 85, 68, 69, 39, 32, 39, 78, 85, 76, 76, 83, 39, 10, - 85, 110, 112, 105, 118, 111, 116, 72, 101, 97, 100, 101, 114, 32, 60, 45, 32, 85, 110, 112, - 105, 118, 111, 116, 72, 101, 97, 100, 101, 114, 83, 105, 110, 103, 108, 101, 32, 47, 32, 85, - 110, 112, 105, 118, 111, 116, 72, 101, 97, 100, 101, 114, 76, 105, 115, 116, 10, 85, 110, 112, - 105, 118, 111, 116, 72, 101, 97, 100, 101, 114, 83, 105, 110, 103, 108, 101, 32, 60, 45, 32, - 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 10, 85, 110, 112, 105, 118, 111, - 116, 72, 101, 97, 100, 101, 114, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, - 115, 40, 76, 105, 115, 116, 40, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, - 41, 41, 10, 80, 114, 97, 103, 109, 97, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, - 45, 32, 39, 80, 82, 65, 71, 77, 65, 39, 32, 80, 114, 97, 103, 109, 97, 65, 115, 115, - 105, 103, 110, 79, 114, 70, 117, 110, 99, 116, 105, 111, 110, 10, 80, 114, 97, 103, 109, 97, - 65, 115, 115, 105, 103, 110, 79, 114, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, - 80, 114, 97, 103, 109, 97, 65, 115, 115, 105, 103, 110, 32, 47, 32, 80, 114, 97, 103, 109, - 97, 70, 117, 110, 99, 116, 105, 111, 110, 10, 80, 114, 97, 103, 109, 97, 65, 115, 115, 105, - 103, 110, 32, 60, 45, 32, 83, 101, 116, 116, 105, 110, 103, 78, 97, 109, 101, 32, 39, 61, - 39, 32, 86, 97, 114, 105, 97, 98, 108, 101, 76, 105, 115, 116, 10, 80, 114, 97, 103, 109, - 97, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 80, 114, 97, 103, 109, 97, 78, - 97, 109, 101, 32, 80, 114, 97, 103, 109, 97, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, - 63, 10, 80, 114, 97, 103, 109, 97, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 32, 60, - 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, - 115, 105, 111, 110, 41, 41, 10, 80, 114, 101, 112, 97, 114, 101, 83, 116, 97, 116, 101, 109, - 101, 110, 116, 32, 60, 45, 32, 39, 80, 82, 69, 80, 65, 82, 69, 39, 32, 73, 100, 101, - 110, 116, 105, 102, 105, 101, 114, 32, 84, 121, 112, 101, 76, 105, 115, 116, 63, 32, 39, 65, - 83, 39, 32, 83, 116, 97, 116, 101, 109, 101, 110, 116, 10, 84, 121, 112, 101, 76, 105, 115, - 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 84, 121, 112, - 101, 41, 41, 10, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, - 60, 45, 32, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, - 116, 101, 114, 110, 97, 108, 10, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, - 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 32, 60, 45, 32, 87, 105, 116, 104, 67, 108, - 97, 117, 115, 101, 63, 32, 83, 101, 108, 101, 99, 116, 83, 101, 116, 79, 112, 67, 104, 97, - 105, 110, 32, 82, 101, 115, 117, 108, 116, 77, 111, 100, 105, 102, 105, 101, 114, 115, 63, 10, - 83, 101, 108, 101, 99, 116, 83, 101, 116, 79, 112, 67, 104, 97, 105, 110, 32, 60, 45, 32, - 73, 110, 116, 101, 114, 115, 101, 99, 116, 67, 104, 97, 105, 110, 32, 83, 101, 108, 101, 99, - 116, 83, 101, 116, 79, 112, 67, 104, 97, 105, 110, 84, 97, 105, 108, 42, 10, 83, 101, 108, - 101, 99, 116, 83, 101, 116, 79, 112, 67, 104, 97, 105, 110, 84, 97, 105, 108, 32, 60, 45, - 32, 83, 101, 116, 111, 112, 67, 108, 97, 117, 115, 101, 32, 73, 110, 116, 101, 114, 115, 101, - 99, 116, 67, 104, 97, 105, 110, 10, 73, 110, 116, 101, 114, 115, 101, 99, 116, 67, 104, 97, - 105, 110, 32, 60, 45, 32, 83, 101, 108, 101, 99, 116, 65, 116, 111, 109, 32, 73, 110, 116, - 101, 114, 115, 101, 99, 116, 67, 104, 97, 105, 110, 84, 97, 105, 108, 42, 10, 73, 110, 116, - 101, 114, 115, 101, 99, 116, 67, 104, 97, 105, 110, 84, 97, 105, 108, 32, 60, 45, 32, 83, - 101, 116, 73, 110, 116, 101, 114, 115, 101, 99, 116, 67, 108, 97, 117, 115, 101, 32, 83, 101, - 108, 101, 99, 116, 65, 116, 111, 109, 10, 83, 101, 116, 73, 110, 116, 101, 114, 115, 101, 99, - 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 73, 78, 84, 69, 82, 83, 69, 67, - 84, 39, 32, 68, 105, 115, 116, 105, 110, 99, 116, 79, 114, 65, 108, 108, 63, 10, 83, 101, - 108, 101, 99, 116, 65, 116, 111, 109, 32, 60, 45, 32, 83, 101, 108, 101, 99, 116, 80, 97, - 114, 101, 110, 115, 32, 47, 32, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, - 110, 116, 84, 121, 112, 101, 10, 83, 101, 108, 101, 99, 116, 80, 97, 114, 101, 110, 115, 32, - 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, - 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 41, 10, 83, 101, 116, 111, 112, - 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 83, 101, 116, 111, 112, 84, 121, 112, 101, 32, - 68, 105, 115, 116, 105, 110, 99, 116, 79, 114, 65, 108, 108, 63, 32, 66, 121, 78, 97, 109, - 101, 63, 10, 83, 101, 116, 111, 112, 84, 121, 112, 101, 32, 60, 45, 32, 83, 101, 116, 111, - 112, 85, 110, 105, 111, 110, 32, 47, 32, 83, 101, 116, 111, 112, 69, 120, 99, 101, 112, 116, - 10, 83, 101, 116, 111, 112, 85, 110, 105, 111, 110, 32, 60, 45, 32, 39, 85, 78, 73, 79, - 78, 39, 10, 83, 101, 116, 111, 112, 69, 120, 99, 101, 112, 116, 32, 60, 45, 32, 39, 69, - 88, 67, 69, 80, 84, 39, 10, 66, 121, 78, 97, 109, 101, 32, 60, 45, 32, 39, 66, 89, - 39, 32, 39, 78, 65, 77, 69, 39, 10, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, - 109, 101, 110, 116, 84, 121, 112, 101, 32, 60, 45, 32, 79, 112, 116, 105, 111, 110, 97, 108, - 80, 97, 114, 101, 110, 115, 83, 105, 109, 112, 108, 101, 83, 101, 108, 101, 99, 116, 32, 47, - 32, 86, 97, 108, 117, 101, 115, 67, 108, 97, 117, 115, 101, 32, 47, 32, 68, 101, 115, 99, - 114, 105, 98, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 32, 84, 97, 98, 108, - 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 32, 80, 105, 118, 111, 116, 83, 116, - 97, 116, 101, 109, 101, 110, 116, 32, 47, 32, 85, 110, 112, 105, 118, 111, 116, 83, 116, 97, - 116, 101, 109, 101, 110, 116, 10, 82, 101, 115, 117, 108, 116, 77, 111, 100, 105, 102, 105, 101, - 114, 115, 32, 60, 45, 32, 79, 114, 100, 101, 114, 66, 121, 67, 108, 97, 117, 115, 101, 63, - 32, 76, 105, 109, 105, 116, 79, 102, 102, 115, 101, 116, 63, 10, 76, 105, 109, 105, 116, 79, - 102, 102, 115, 101, 116, 32, 60, 45, 32, 76, 105, 109, 105, 116, 79, 102, 102, 115, 101, 116, - 67, 108, 97, 117, 115, 101, 32, 47, 32, 79, 102, 102, 115, 101, 116, 70, 101, 116, 99, 104, - 67, 108, 97, 117, 115, 101, 32, 47, 32, 79, 102, 102, 115, 101, 116, 76, 105, 109, 105, 116, - 67, 108, 97, 117, 115, 101, 32, 47, 32, 70, 101, 116, 99, 104, 79, 110, 108, 121, 67, 108, - 97, 117, 115, 101, 10, 76, 105, 109, 105, 116, 79, 102, 102, 115, 101, 116, 67, 108, 97, 117, - 115, 101, 32, 60, 45, 32, 76, 105, 109, 105, 116, 67, 108, 97, 117, 115, 101, 32, 79, 102, - 102, 115, 101, 116, 67, 108, 97, 117, 115, 101, 63, 10, 79, 102, 102, 115, 101, 116, 76, 105, - 109, 105, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 79, 102, 102, 115, 101, 116, 67, - 108, 97, 117, 115, 101, 32, 76, 105, 109, 105, 116, 67, 108, 97, 117, 115, 101, 63, 10, 79, - 102, 102, 115, 101, 116, 70, 101, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, - 79, 102, 102, 115, 101, 116, 67, 108, 97, 117, 115, 101, 32, 70, 101, 116, 99, 104, 67, 108, - 97, 117, 115, 101, 10, 70, 101, 116, 99, 104, 79, 110, 108, 121, 67, 108, 97, 117, 115, 101, - 32, 60, 45, 32, 70, 101, 116, 99, 104, 67, 108, 97, 117, 115, 101, 10, 84, 97, 98, 108, - 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 84, 65, 66, 76, 69, - 39, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 10, 79, 112, 116, 105, - 111, 110, 97, 108, 80, 97, 114, 101, 110, 115, 83, 105, 109, 112, 108, 101, 83, 101, 108, 101, - 99, 116, 32, 60, 45, 32, 83, 105, 109, 112, 108, 101, 83, 101, 108, 101, 99, 116, 80, 97, - 114, 101, 110, 115, 32, 47, 32, 83, 105, 109, 112, 108, 101, 83, 101, 108, 101, 99, 116, 10, - 83, 105, 109, 112, 108, 101, 83, 101, 108, 101, 99, 116, 80, 97, 114, 101, 110, 115, 32, 60, - 45, 32, 80, 97, 114, 101, 110, 115, 40, 83, 105, 109, 112, 108, 101, 83, 101, 108, 101, 99, - 116, 41, 10, 83, 105, 109, 112, 108, 101, 83, 101, 108, 101, 99, 116, 32, 60, 45, 32, 83, - 101, 108, 101, 99, 116, 70, 114, 111, 109, 32, 87, 104, 101, 114, 101, 67, 108, 97, 117, 115, - 101, 63, 32, 71, 114, 111, 117, 112, 66, 121, 67, 108, 97, 117, 115, 101, 63, 32, 72, 97, - 118, 105, 110, 103, 67, 108, 97, 117, 115, 101, 63, 32, 87, 105, 110, 100, 111, 119, 67, 108, - 97, 117, 115, 101, 63, 32, 81, 117, 97, 108, 105, 102, 121, 67, 108, 97, 117, 115, 101, 63, - 32, 83, 97, 109, 112, 108, 101, 67, 108, 97, 117, 115, 101, 63, 10, 83, 101, 108, 101, 99, - 116, 70, 114, 111, 109, 32, 60, 45, 32, 83, 101, 108, 101, 99, 116, 70, 114, 111, 109, 67, - 108, 97, 117, 115, 101, 32, 47, 32, 70, 114, 111, 109, 83, 101, 108, 101, 99, 116, 67, 108, - 97, 117, 115, 101, 10, 83, 101, 108, 101, 99, 116, 70, 114, 111, 109, 67, 108, 97, 117, 115, - 101, 32, 60, 45, 32, 83, 101, 108, 101, 99, 116, 67, 108, 97, 117, 115, 101, 32, 70, 114, - 111, 109, 67, 108, 97, 117, 115, 101, 63, 10, 70, 114, 111, 109, 83, 101, 108, 101, 99, 116, - 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 70, 114, 111, 109, 67, 108, 97, 117, 115, 101, - 32, 83, 101, 108, 101, 99, 116, 67, 108, 97, 117, 115, 101, 63, 10, 87, 105, 116, 104, 83, - 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 67, 111, 108, 73, 100, 79, 114, 83, - 116, 114, 105, 110, 103, 32, 73, 110, 115, 101, 114, 116, 67, 111, 108, 117, 109, 110, 76, 105, - 115, 116, 63, 32, 85, 115, 105, 110, 103, 75, 101, 121, 63, 32, 39, 65, 83, 39, 32, 77, - 97, 116, 101, 114, 105, 97, 108, 105, 122, 101, 100, 63, 32, 67, 84, 69, 66, 111, 100, 121, - 10, 67, 84, 69, 66, 111, 100, 121, 32, 60, 45, 32, 67, 84, 69, 83, 101, 108, 101, 99, - 116, 66, 111, 100, 121, 32, 47, 32, 67, 84, 69, 68, 77, 76, 66, 111, 100, 121, 10, 67, - 84, 69, 83, 101, 108, 101, 99, 116, 66, 111, 100, 121, 32, 60, 45, 32, 80, 97, 114, 101, - 110, 115, 40, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, - 116, 101, 114, 110, 97, 108, 41, 10, 67, 84, 69, 68, 77, 76, 66, 111, 100, 121, 32, 60, - 45, 32, 80, 97, 114, 101, 110, 115, 40, 83, 116, 97, 116, 101, 109, 101, 110, 116, 41, 10, - 85, 115, 105, 110, 103, 75, 101, 121, 32, 60, 45, 32, 39, 85, 83, 73, 78, 71, 39, 32, - 39, 75, 69, 89, 39, 32, 80, 97, 114, 101, 110, 115, 40, 84, 97, 114, 103, 101, 116, 76, - 105, 115, 116, 41, 10, 77, 97, 116, 101, 114, 105, 97, 108, 105, 122, 101, 100, 32, 60, 45, - 32, 39, 78, 79, 84, 39, 63, 32, 39, 77, 65, 84, 69, 82, 73, 65, 76, 73, 90, 69, - 68, 39, 10, 87, 105, 116, 104, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 87, 73, - 84, 72, 39, 32, 82, 101, 99, 117, 114, 115, 105, 118, 101, 63, 32, 76, 105, 115, 116, 40, - 87, 105, 116, 104, 83, 116, 97, 116, 101, 109, 101, 110, 116, 41, 10, 82, 101, 99, 117, 114, - 115, 105, 118, 101, 32, 60, 45, 32, 39, 82, 69, 67, 85, 82, 83, 73, 86, 69, 39, 10, - 83, 101, 108, 101, 99, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 83, 69, 76, - 69, 67, 84, 39, 32, 68, 105, 115, 116, 105, 110, 99, 116, 67, 108, 97, 117, 115, 101, 63, - 32, 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, 63, 10, 84, 97, 114, 103, 101, 116, 76, - 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 65, 108, 105, 97, 115, 101, 100, 69, - 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, 67, 111, 108, 117, 109, 110, 65, 108, 105, - 97, 115, 101, 115, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, - 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 41, 41, 10, 68, 105, 115, 116, - 105, 110, 99, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 68, 105, 115, 116, 105, 110, - 99, 116, 79, 110, 32, 47, 32, 68, 105, 115, 116, 105, 110, 99, 116, 65, 108, 108, 10, 68, - 105, 115, 116, 105, 110, 99, 116, 65, 108, 108, 32, 60, 45, 32, 39, 65, 76, 76, 39, 10, - 68, 105, 115, 116, 105, 110, 99, 116, 79, 110, 32, 60, 45, 32, 39, 68, 73, 83, 84, 73, - 78, 67, 84, 39, 32, 68, 105, 115, 116, 105, 110, 99, 116, 79, 110, 84, 97, 114, 103, 101, - 116, 115, 63, 10, 68, 105, 115, 116, 105, 110, 99, 116, 79, 110, 84, 97, 114, 103, 101, 116, - 115, 32, 60, 45, 32, 39, 79, 78, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, - 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 41, 10, 73, 110, 110, 101, 114, - 84, 97, 98, 108, 101, 82, 101, 102, 32, 60, 45, 32, 86, 97, 108, 117, 101, 115, 82, 101, - 102, 32, 47, 32, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 47, 32, - 84, 97, 98, 108, 101, 83, 117, 98, 113, 117, 101, 114, 121, 32, 47, 32, 66, 97, 115, 101, - 84, 97, 98, 108, 101, 82, 101, 102, 32, 47, 32, 80, 97, 114, 101, 110, 115, 84, 97, 98, - 108, 101, 82, 101, 102, 10, 84, 97, 98, 108, 101, 82, 101, 102, 32, 60, 45, 32, 73, 110, - 110, 101, 114, 84, 97, 98, 108, 101, 82, 101, 102, 32, 74, 111, 105, 110, 79, 114, 80, 105, - 118, 111, 116, 42, 10, 84, 97, 98, 108, 101, 83, 117, 98, 113, 117, 101, 114, 121, 32, 60, - 45, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 67, 111, 108, 111, 110, 63, 32, 76, - 97, 116, 101, 114, 97, 108, 63, 32, 83, 117, 98, 113, 117, 101, 114, 121, 82, 101, 102, 101, - 114, 101, 110, 99, 101, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 10, 66, 97, - 115, 101, 84, 97, 98, 108, 101, 82, 101, 102, 32, 60, 45, 32, 84, 97, 98, 108, 101, 65, - 108, 105, 97, 115, 67, 111, 108, 111, 110, 63, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, - 78, 97, 109, 101, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 32, 65, 116, 67, - 108, 97, 117, 115, 101, 63, 32, 83, 97, 109, 112, 108, 101, 67, 108, 97, 117, 115, 101, 63, - 10, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 67, 111, 108, 111, 110, 32, 60, 45, 32, - 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 32, 39, 58, 39, 10, 86, 97, - 108, 117, 101, 115, 82, 101, 102, 32, 60, 45, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, - 115, 67, 111, 108, 111, 110, 63, 32, 86, 97, 108, 117, 101, 115, 67, 108, 97, 117, 115, 101, - 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 10, 80, 97, 114, 101, 110, 115, 84, - 97, 98, 108, 101, 82, 101, 102, 32, 60, 45, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, - 115, 67, 111, 108, 111, 110, 63, 32, 80, 97, 114, 101, 110, 115, 40, 84, 97, 98, 108, 101, - 82, 101, 102, 41, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 32, 83, 97, 109, - 112, 108, 101, 67, 108, 97, 117, 115, 101, 63, 10, 74, 111, 105, 110, 79, 114, 80, 105, 118, - 111, 116, 32, 60, 45, 32, 74, 111, 105, 110, 67, 108, 97, 117, 115, 101, 32, 47, 32, 84, - 97, 98, 108, 101, 80, 105, 118, 111, 116, 67, 108, 97, 117, 115, 101, 32, 47, 32, 84, 97, - 98, 108, 101, 85, 110, 112, 105, 118, 111, 116, 67, 108, 97, 117, 115, 101, 32, 47, 32, 84, - 97, 98, 108, 101, 77, 97, 116, 99, 104, 82, 101, 99, 111, 103, 110, 105, 122, 101, 67, 108, - 97, 117, 115, 101, 10, 84, 97, 98, 108, 101, 80, 105, 118, 111, 116, 67, 108, 97, 117, 115, - 101, 32, 60, 45, 32, 39, 80, 73, 86, 79, 84, 39, 32, 80, 97, 114, 101, 110, 115, 40, - 84, 97, 98, 108, 101, 80, 105, 118, 111, 116, 67, 108, 97, 117, 115, 101, 66, 111, 100, 121, - 41, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 10, 84, 97, 98, 108, 101, 80, - 105, 118, 111, 116, 67, 108, 97, 117, 115, 101, 66, 111, 100, 121, 32, 60, 45, 32, 84, 97, - 114, 103, 101, 116, 76, 105, 115, 116, 32, 39, 70, 79, 82, 39, 32, 80, 105, 118, 111, 116, - 86, 97, 108, 117, 101, 76, 105, 115, 116, 43, 32, 80, 105, 118, 111, 116, 71, 114, 111, 117, - 112, 66, 121, 76, 105, 115, 116, 63, 10, 80, 105, 118, 111, 116, 71, 114, 111, 117, 112, 66, - 121, 76, 105, 115, 116, 32, 60, 45, 32, 39, 71, 82, 79, 85, 80, 39, 32, 39, 66, 89, - 39, 32, 79, 112, 116, 105, 111, 110, 97, 108, 80, 97, 114, 101, 110, 115, 78, 97, 109, 101, - 76, 105, 115, 116, 10, 84, 97, 98, 108, 101, 85, 110, 112, 105, 118, 111, 116, 67, 108, 97, - 117, 115, 101, 32, 60, 45, 32, 39, 85, 78, 80, 73, 86, 79, 84, 39, 32, 73, 110, 99, - 108, 117, 100, 101, 79, 114, 69, 120, 99, 108, 117, 100, 101, 78, 117, 108, 108, 115, 63, 32, - 80, 97, 114, 101, 110, 115, 40, 84, 97, 98, 108, 101, 85, 110, 112, 105, 118, 111, 116, 67, - 108, 97, 117, 115, 101, 66, 111, 100, 121, 41, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, - 115, 63, 10, 84, 97, 98, 108, 101, 85, 110, 112, 105, 118, 111, 116, 67, 108, 97, 117, 115, - 101, 66, 111, 100, 121, 32, 60, 45, 32, 85, 110, 112, 105, 118, 111, 116, 72, 101, 97, 100, - 101, 114, 32, 39, 70, 79, 82, 39, 32, 85, 110, 112, 105, 118, 111, 116, 86, 97, 108, 117, - 101, 76, 105, 115, 116, 43, 10, 80, 105, 118, 111, 116, 72, 101, 97, 100, 101, 114, 32, 60, - 45, 32, 66, 97, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 80, 105, 118, - 111, 116, 86, 97, 108, 117, 101, 76, 105, 115, 116, 32, 60, 45, 32, 80, 105, 118, 111, 116, - 72, 101, 97, 100, 101, 114, 32, 39, 73, 78, 39, 32, 80, 105, 118, 111, 116, 86, 97, 108, - 117, 101, 84, 97, 114, 103, 101, 116, 10, 80, 105, 118, 111, 116, 86, 97, 108, 117, 101, 84, - 97, 114, 103, 101, 116, 32, 60, 45, 32, 80, 105, 118, 111, 116, 69, 110, 117, 109, 84, 97, - 114, 103, 101, 116, 32, 47, 32, 80, 105, 118, 111, 116, 76, 105, 115, 116, 84, 97, 114, 103, - 101, 116, 10, 80, 105, 118, 111, 116, 69, 110, 117, 109, 84, 97, 114, 103, 101, 116, 32, 60, - 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 80, 105, 118, 111, 116, 76, 105, - 115, 116, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 80, 105, 118, 111, 116, 84, 97, 114, - 103, 101, 116, 76, 105, 115, 116, 10, 85, 110, 112, 105, 118, 111, 116, 86, 97, 108, 117, 101, - 76, 105, 115, 116, 32, 60, 45, 32, 85, 110, 112, 105, 118, 111, 116, 72, 101, 97, 100, 101, - 114, 32, 39, 73, 78, 39, 32, 85, 110, 112, 105, 118, 111, 116, 84, 97, 114, 103, 101, 116, - 76, 105, 115, 116, 10, 80, 105, 118, 111, 116, 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, - 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 84, 97, 114, 103, 101, 116, 76, 105, 115, - 116, 41, 10, 85, 110, 112, 105, 118, 111, 116, 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, - 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 84, 97, 114, 103, 101, 116, 76, 105, 115, - 116, 41, 10, 76, 97, 116, 101, 114, 97, 108, 32, 60, 45, 32, 39, 76, 65, 84, 69, 82, - 65, 76, 39, 10, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 60, 45, - 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, - 47, 32, 85, 110, 113, 117, 97, 108, 105, 102, 105, 101, 100, 66, 97, 115, 101, 84, 97, 98, - 108, 101, 78, 97, 109, 101, 10, 85, 110, 113, 117, 97, 108, 105, 102, 105, 101, 100, 66, 97, - 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 60, 45, 32, 84, 97, 98, 108, 101, - 78, 97, 109, 101, 10, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 78, - 97, 109, 101, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 82, 101, 115, 101, 114, 118, - 101, 100, 83, 99, 104, 101, 109, 97, 84, 97, 98, 108, 101, 32, 47, 32, 83, 99, 104, 101, - 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 84, 97, 98, 108, 101, 10, 83, 99, 104, 101, - 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 84, 97, 98, 108, 101, 32, 60, 45, 32, 83, - 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, - 101, 115, 101, 114, 118, 101, 100, 84, 97, 98, 108, 101, 78, 97, 109, 101, 10, 67, 97, 116, - 97, 108, 111, 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 84, 97, - 98, 108, 101, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 81, 117, 97, 108, 105, 102, - 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, - 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 43, 32, 82, 101, 115, - 101, 114, 118, 101, 100, 84, 97, 98, 108, 101, 78, 97, 109, 101, 10, 84, 97, 98, 108, 101, - 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 84, 97, 98, 108, 101, 70, 117, 110, - 99, 116, 105, 111, 110, 76, 97, 116, 101, 114, 97, 108, 79, 112, 116, 32, 47, 32, 84, 97, - 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 65, 108, 105, 97, 115, 67, 111, 108, 111, - 110, 10, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 76, 97, 116, 101, 114, - 97, 108, 79, 112, 116, 32, 60, 45, 32, 76, 97, 116, 101, 114, 97, 108, 63, 32, 81, 117, - 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, - 32, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, - 110, 116, 115, 32, 87, 105, 116, 104, 79, 114, 100, 105, 110, 97, 108, 105, 116, 121, 63, 32, - 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 10, 84, 97, 98, 108, 101, 70, 117, 110, - 99, 116, 105, 111, 110, 65, 108, 105, 97, 115, 67, 111, 108, 111, 110, 32, 60, 45, 32, 84, - 97, 98, 108, 101, 65, 108, 105, 97, 115, 67, 111, 108, 111, 110, 32, 81, 117, 97, 108, 105, - 102, 105, 101, 100, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 84, 97, - 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, - 32, 87, 105, 116, 104, 79, 114, 100, 105, 110, 97, 108, 105, 116, 121, 63, 32, 83, 97, 109, - 112, 108, 101, 67, 108, 97, 117, 115, 101, 63, 10, 87, 105, 116, 104, 79, 114, 100, 105, 110, - 97, 108, 105, 116, 121, 32, 60, 45, 32, 39, 87, 73, 84, 72, 39, 32, 39, 79, 82, 68, - 73, 78, 65, 76, 73, 84, 89, 39, 10, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, - 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 67, 97, 116, 97, 108, - 111, 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 63, 32, 83, 99, 104, - 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 42, 32, 84, 97, - 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 10, 84, 97, 98, 108, - 101, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, - 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 70, 117, 110, 99, 116, 105, - 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 41, 63, 41, 10, 70, 117, 110, 99, 116, 105, - 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 32, 60, 45, 32, 78, 97, 109, 101, 100, 70, - 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 32, 47, 32, 80, 111, - 115, 105, 116, 105, 111, 110, 97, 108, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, - 109, 101, 110, 116, 10, 78, 97, 109, 101, 100, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, - 103, 117, 109, 101, 110, 116, 32, 60, 45, 32, 78, 97, 109, 101, 100, 80, 97, 114, 97, 109, - 101, 116, 101, 114, 10, 80, 111, 115, 105, 116, 105, 111, 110, 97, 108, 70, 117, 110, 99, 116, - 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 32, 60, 45, 32, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 10, 78, 97, 109, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, - 32, 60, 45, 32, 84, 121, 112, 101, 70, 117, 110, 99, 78, 97, 109, 101, 32, 84, 121, 112, - 101, 63, 32, 78, 97, 109, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 65, 115, 115, - 105, 103, 110, 109, 101, 110, 116, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 78, - 97, 109, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 65, 115, 115, 105, 103, 110, 109, - 101, 110, 116, 32, 60, 45, 32, 39, 58, 61, 39, 32, 47, 32, 39, 61, 62, 39, 10, 84, - 97, 98, 108, 101, 65, 108, 105, 97, 115, 32, 60, 45, 32, 84, 97, 98, 108, 101, 65, 108, - 105, 97, 115, 65, 115, 32, 47, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 87, 105, - 116, 104, 111, 117, 116, 65, 115, 10, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 65, 115, - 32, 60, 45, 32, 39, 65, 83, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 79, - 114, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 67, 111, 108, 117, 109, - 110, 65, 108, 105, 97, 115, 101, 115, 63, 10, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, - 87, 105, 116, 104, 111, 117, 116, 65, 115, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, - 105, 101, 114, 32, 67, 111, 108, 117, 109, 110, 65, 108, 105, 97, 115, 101, 115, 63, 10, 65, - 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 65, 84, 39, 32, 80, 97, 114, 101, - 110, 115, 40, 65, 116, 83, 112, 101, 99, 105, 102, 105, 101, 114, 41, 10, 65, 116, 83, 112, - 101, 99, 105, 102, 105, 101, 114, 32, 60, 45, 32, 65, 116, 85, 110, 105, 116, 32, 39, 61, - 62, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 65, 116, 85, 110, 105, 116, - 32, 60, 45, 32, 86, 101, 114, 115, 105, 111, 110, 65, 116, 85, 110, 105, 116, 32, 47, 32, - 84, 105, 109, 101, 115, 116, 97, 109, 112, 65, 116, 85, 110, 105, 116, 10, 86, 101, 114, 115, - 105, 111, 110, 65, 116, 85, 110, 105, 116, 32, 60, 45, 32, 39, 86, 69, 82, 83, 73, 79, - 78, 39, 10, 84, 105, 109, 101, 115, 116, 97, 109, 112, 65, 116, 85, 110, 105, 116, 32, 60, - 45, 32, 39, 84, 73, 77, 69, 83, 84, 65, 77, 80, 39, 10, 74, 111, 105, 110, 67, 108, - 97, 117, 115, 101, 32, 60, 45, 32, 74, 111, 105, 110, 66, 121, 67, 108, 97, 117, 115, 101, - 32, 47, 32, 82, 101, 103, 117, 108, 97, 114, 74, 111, 105, 110, 67, 108, 97, 117, 115, 101, - 32, 47, 32, 74, 111, 105, 110, 87, 105, 116, 104, 111, 117, 116, 79, 110, 67, 108, 97, 117, - 115, 101, 32, 47, 32, 78, 101, 97, 114, 101, 115, 116, 74, 111, 105, 110, 67, 108, 97, 117, - 115, 101, 10, 35, 32, 78, 69, 65, 82, 69, 83, 84, 32, 66, 89, 32, 104, 97, 115, 32, - 116, 119, 111, 32, 97, 108, 116, 101, 114, 110, 97, 116, 105, 118, 101, 115, 32, 115, 111, 32, - 80, 69, 71, 32, 99, 97, 110, 32, 98, 97, 99, 107, 116, 114, 97, 99, 107, 58, 32, 116, - 104, 101, 32, 98, 97, 114, 101, 32, 102, 111, 114, 109, 32, 117, 115, 101, 115, 32, 97, 108, - 105, 97, 115, 45, 108, 101, 115, 115, 32, 116, 97, 114, 103, 101, 116, 32, 109, 105, 114, 114, - 111, 114, 115, 32, 40, 116, 114, 105, 101, 100, 10, 35, 32, 102, 105, 114, 115, 116, 44, 32, - 115, 111, 32, 97, 110, 32, 117, 110, 97, 108, 105, 97, 115, 101, 100, 32, 116, 97, 114, 103, - 101, 116, 32, 100, 111, 101, 115, 32, 110, 111, 116, 32, 103, 114, 101, 101, 100, 105, 108, 121, - 32, 99, 111, 110, 115, 117, 109, 101, 32, 78, 69, 65, 82, 69, 83, 84, 47, 65, 80, 80, - 82, 79, 88, 47, 69, 88, 65, 67, 84, 32, 97, 115, 32, 97, 110, 32, 97, 108, 105, 97, - 115, 41, 44, 32, 97, 110, 100, 32, 116, 104, 101, 32, 97, 108, 105, 97, 115, 101, 100, 10, - 35, 32, 102, 111, 114, 109, 32, 102, 97, 108, 108, 115, 32, 98, 97, 99, 107, 32, 116, 111, - 32, 97, 32, 102, 117, 108, 108, 32, 84, 97, 98, 108, 101, 82, 101, 102, 32, 119, 104, 101, - 110, 32, 97, 32, 114, 101, 97, 108, 32, 97, 108, 105, 97, 115, 32, 105, 100, 101, 110, 116, - 105, 102, 105, 101, 114, 32, 102, 111, 108, 108, 111, 119, 115, 32, 116, 104, 101, 32, 116, 97, - 114, 103, 101, 116, 46, 10, 78, 101, 97, 114, 101, 115, 116, 74, 111, 105, 110, 67, 108, 97, - 117, 115, 101, 32, 60, 45, 32, 78, 101, 97, 114, 101, 115, 116, 74, 111, 105, 110, 66, 97, - 114, 101, 32, 47, 32, 78, 101, 97, 114, 101, 115, 116, 74, 111, 105, 110, 65, 108, 105, 97, - 115, 101, 100, 10, 78, 101, 97, 114, 101, 115, 116, 74, 111, 105, 110, 65, 108, 105, 97, 115, - 101, 100, 32, 60, 45, 32, 74, 111, 105, 110, 84, 121, 112, 101, 63, 32, 39, 74, 79, 73, - 78, 39, 32, 84, 97, 98, 108, 101, 82, 101, 102, 32, 65, 112, 112, 114, 111, 120, 79, 114, - 69, 120, 97, 99, 116, 63, 32, 39, 78, 69, 65, 82, 69, 83, 84, 39, 32, 78, 117, 109, - 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 63, 32, 39, 66, 89, 39, 32, 68, 105, 115, - 116, 97, 110, 99, 101, 79, 114, 83, 105, 109, 105, 108, 97, 114, 105, 116, 121, 32, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 10, 78, 101, 97, 114, 101, 115, 116, 74, 111, 105, 110, - 66, 97, 114, 101, 32, 60, 45, 32, 74, 111, 105, 110, 84, 121, 112, 101, 63, 32, 39, 74, - 79, 73, 78, 39, 32, 78, 101, 97, 114, 101, 115, 116, 66, 97, 114, 101, 84, 97, 98, 108, - 101, 82, 101, 102, 32, 65, 112, 112, 114, 111, 120, 79, 114, 69, 120, 97, 99, 116, 63, 32, - 39, 78, 69, 65, 82, 69, 83, 84, 39, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, - 114, 97, 108, 63, 32, 39, 66, 89, 39, 32, 68, 105, 115, 116, 97, 110, 99, 101, 79, 114, - 83, 105, 109, 105, 108, 97, 114, 105, 116, 121, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, - 110, 10, 78, 101, 97, 114, 101, 115, 116, 66, 97, 114, 101, 84, 97, 98, 108, 101, 82, 101, - 102, 32, 60, 45, 32, 78, 101, 97, 114, 101, 115, 116, 86, 97, 108, 117, 101, 115, 82, 101, - 102, 32, 47, 32, 78, 101, 97, 114, 101, 115, 116, 84, 97, 98, 108, 101, 70, 117, 110, 99, - 116, 105, 111, 110, 32, 47, 32, 78, 101, 97, 114, 101, 115, 116, 84, 97, 98, 108, 101, 83, - 117, 98, 113, 117, 101, 114, 121, 32, 47, 32, 78, 101, 97, 114, 101, 115, 116, 66, 97, 115, - 101, 84, 97, 98, 108, 101, 82, 101, 102, 32, 47, 32, 78, 101, 97, 114, 101, 115, 116, 80, - 97, 114, 101, 110, 115, 84, 97, 98, 108, 101, 82, 101, 102, 10, 78, 101, 97, 114, 101, 115, - 116, 86, 97, 108, 117, 101, 115, 82, 101, 102, 32, 60, 45, 32, 86, 97, 108, 117, 101, 115, - 67, 108, 97, 117, 115, 101, 10, 78, 101, 97, 114, 101, 115, 116, 84, 97, 98, 108, 101, 70, - 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 76, 97, 116, 101, 114, 97, 108, 63, 32, - 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, - 111, 110, 32, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, - 109, 101, 110, 116, 115, 32, 87, 105, 116, 104, 79, 114, 100, 105, 110, 97, 108, 105, 116, 121, - 63, 10, 78, 101, 97, 114, 101, 115, 116, 84, 97, 98, 108, 101, 83, 117, 98, 113, 117, 101, - 114, 121, 32, 60, 45, 32, 76, 97, 116, 101, 114, 97, 108, 63, 32, 83, 117, 98, 113, 117, - 101, 114, 121, 82, 101, 102, 101, 114, 101, 110, 99, 101, 10, 78, 101, 97, 114, 101, 115, 116, - 66, 97, 115, 101, 84, 97, 98, 108, 101, 82, 101, 102, 32, 60, 45, 32, 66, 97, 115, 101, - 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 65, 116, 67, 108, 97, 117, 115, 101, 63, 32, - 83, 97, 109, 112, 108, 101, 67, 108, 97, 117, 115, 101, 63, 10, 78, 101, 97, 114, 101, 115, - 116, 80, 97, 114, 101, 110, 115, 84, 97, 98, 108, 101, 82, 101, 102, 32, 60, 45, 32, 80, - 97, 114, 101, 110, 115, 40, 84, 97, 98, 108, 101, 82, 101, 102, 41, 32, 83, 97, 109, 112, - 108, 101, 67, 108, 97, 117, 115, 101, 63, 10, 65, 112, 112, 114, 111, 120, 79, 114, 69, 120, - 97, 99, 116, 32, 60, 45, 32, 78, 101, 97, 114, 101, 115, 116, 65, 112, 112, 114, 111, 120, - 32, 47, 32, 78, 101, 97, 114, 101, 115, 116, 69, 120, 97, 99, 116, 10, 78, 101, 97, 114, - 101, 115, 116, 65, 112, 112, 114, 111, 120, 32, 60, 45, 32, 39, 65, 80, 80, 82, 79, 88, - 39, 10, 78, 101, 97, 114, 101, 115, 116, 69, 120, 97, 99, 116, 32, 60, 45, 32, 39, 69, - 88, 65, 67, 84, 39, 10, 68, 105, 115, 116, 97, 110, 99, 101, 79, 114, 83, 105, 109, 105, - 108, 97, 114, 105, 116, 121, 32, 60, 45, 32, 78, 101, 97, 114, 101, 115, 116, 68, 105, 115, - 116, 97, 110, 99, 101, 32, 47, 32, 78, 101, 97, 114, 101, 115, 116, 83, 105, 109, 105, 108, - 97, 114, 105, 116, 121, 10, 78, 101, 97, 114, 101, 115, 116, 68, 105, 115, 116, 97, 110, 99, - 101, 32, 60, 45, 32, 39, 68, 73, 83, 84, 65, 78, 67, 69, 39, 10, 78, 101, 97, 114, - 101, 115, 116, 83, 105, 109, 105, 108, 97, 114, 105, 116, 121, 32, 60, 45, 32, 39, 83, 73, - 77, 73, 76, 65, 82, 73, 84, 89, 39, 10, 82, 101, 103, 117, 108, 97, 114, 74, 111, 105, - 110, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 65, 115, 111, 102, 63, 32, 74, 111, 105, - 110, 84, 121, 112, 101, 63, 32, 39, 74, 79, 73, 78, 39, 32, 84, 97, 98, 108, 101, 82, - 101, 102, 32, 74, 111, 105, 110, 81, 117, 97, 108, 105, 102, 105, 101, 114, 10, 74, 111, 105, - 110, 66, 121, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 74, 79, 73, 78, 39, 32, - 39, 66, 89, 39, 32, 80, 97, 114, 101, 110, 115, 40, 39, 84, 89, 80, 69, 39, 32, 67, - 111, 108, 76, 97, 98, 101, 108, 41, 32, 84, 97, 98, 108, 101, 82, 101, 102, 32, 74, 111, - 105, 110, 81, 117, 97, 108, 105, 102, 105, 101, 114, 10, 65, 115, 111, 102, 32, 60, 45, 32, - 39, 65, 83, 79, 70, 39, 10, 74, 111, 105, 110, 87, 105, 116, 104, 111, 117, 116, 79, 110, - 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 74, 111, 105, 110, 80, 114, 101, 102, 105, 120, - 32, 39, 74, 79, 73, 78, 39, 32, 73, 110, 110, 101, 114, 84, 97, 98, 108, 101, 82, 101, - 102, 10, 74, 111, 105, 110, 81, 117, 97, 108, 105, 102, 105, 101, 114, 32, 60, 45, 32, 79, - 110, 67, 108, 97, 117, 115, 101, 32, 47, 32, 85, 115, 105, 110, 103, 67, 108, 97, 117, 115, - 101, 10, 79, 110, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 79, 78, 39, 32, 69, - 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 85, 115, 105, 110, 103, 67, 108, 97, 117, 115, - 101, 32, 60, 45, 32, 39, 85, 83, 73, 78, 71, 39, 32, 80, 97, 114, 101, 110, 115, 40, - 76, 105, 115, 116, 40, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 41, 41, 10, 74, 111, - 105, 110, 84, 121, 112, 101, 32, 60, 45, 32, 70, 117, 108, 108, 74, 111, 105, 110, 32, 47, - 32, 76, 101, 102, 116, 74, 111, 105, 110, 32, 47, 32, 82, 105, 103, 104, 116, 74, 111, 105, - 110, 32, 47, 32, 83, 101, 109, 105, 74, 111, 105, 110, 32, 47, 32, 65, 110, 116, 105, 74, - 111, 105, 110, 32, 47, 32, 73, 110, 110, 101, 114, 74, 111, 105, 110, 10, 74, 111, 105, 110, - 80, 114, 101, 102, 105, 120, 32, 60, 45, 32, 67, 114, 111, 115, 115, 74, 111, 105, 110, 80, - 114, 101, 102, 105, 120, 32, 47, 32, 78, 97, 116, 117, 114, 97, 108, 74, 111, 105, 110, 80, - 114, 101, 102, 105, 120, 32, 47, 32, 80, 111, 115, 105, 116, 105, 111, 110, 97, 108, 74, 111, - 105, 110, 80, 114, 101, 102, 105, 120, 10, 67, 114, 111, 115, 115, 74, 111, 105, 110, 80, 114, - 101, 102, 105, 120, 32, 60, 45, 32, 39, 67, 82, 79, 83, 83, 39, 10, 78, 97, 116, 117, - 114, 97, 108, 74, 111, 105, 110, 80, 114, 101, 102, 105, 120, 32, 60, 45, 32, 39, 78, 65, - 84, 85, 82, 65, 76, 39, 32, 74, 111, 105, 110, 84, 121, 112, 101, 63, 10, 80, 111, 115, - 105, 116, 105, 111, 110, 97, 108, 74, 111, 105, 110, 80, 114, 101, 102, 105, 120, 32, 60, 45, - 32, 39, 80, 79, 83, 73, 84, 73, 79, 78, 65, 76, 39, 10, 70, 117, 108, 108, 74, 111, - 105, 110, 32, 60, 45, 32, 39, 70, 85, 76, 76, 39, 32, 39, 79, 85, 84, 69, 82, 39, - 63, 10, 76, 101, 102, 116, 74, 111, 105, 110, 32, 60, 45, 32, 39, 76, 69, 70, 84, 39, - 32, 39, 79, 85, 84, 69, 82, 39, 63, 10, 82, 105, 103, 104, 116, 74, 111, 105, 110, 32, - 60, 45, 32, 39, 82, 73, 71, 72, 84, 39, 32, 39, 79, 85, 84, 69, 82, 39, 63, 10, - 83, 101, 109, 105, 74, 111, 105, 110, 32, 60, 45, 32, 39, 83, 69, 77, 73, 39, 10, 65, - 110, 116, 105, 74, 111, 105, 110, 32, 60, 45, 32, 39, 65, 78, 84, 73, 39, 10, 73, 110, - 110, 101, 114, 74, 111, 105, 110, 32, 60, 45, 32, 39, 73, 78, 78, 69, 82, 39, 10, 70, - 114, 111, 109, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 70, 82, 79, 77, 39, 32, - 76, 105, 115, 116, 40, 84, 97, 98, 108, 101, 82, 101, 102, 41, 10, 87, 104, 101, 114, 101, - 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 87, 72, 69, 82, 69, 39, 32, 69, 120, - 112, 114, 101, 115, 115, 105, 111, 110, 10, 71, 114, 111, 117, 112, 66, 121, 67, 108, 97, 117, - 115, 101, 32, 60, 45, 32, 39, 71, 82, 79, 85, 80, 39, 32, 39, 66, 89, 39, 32, 71, - 114, 111, 117, 112, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 115, 10, 72, 97, - 118, 105, 110, 103, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 72, 65, 86, 73, 78, - 71, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 81, 117, 97, 108, 105, 102, - 121, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 81, 85, 65, 76, 73, 70, 89, 39, - 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 83, 97, 109, 112, 108, 101, 67, 108, - 97, 117, 115, 101, 32, 60, 45, 32, 40, 84, 97, 98, 108, 101, 83, 97, 109, 112, 108, 101, - 32, 47, 32, 85, 115, 105, 110, 103, 83, 97, 109, 112, 108, 101, 41, 32, 83, 97, 109, 112, - 108, 101, 69, 110, 116, 114, 121, 10, 85, 115, 105, 110, 103, 83, 97, 109, 112, 108, 101, 32, - 60, 45, 32, 39, 85, 83, 73, 78, 71, 39, 32, 39, 83, 65, 77, 80, 76, 69, 39, 10, - 84, 97, 98, 108, 101, 83, 97, 109, 112, 108, 101, 32, 60, 45, 32, 39, 84, 65, 66, 76, - 69, 83, 65, 77, 80, 76, 69, 39, 10, 87, 105, 110, 100, 111, 119, 67, 108, 97, 117, 115, - 101, 32, 60, 45, 32, 39, 87, 73, 78, 68, 79, 87, 39, 32, 76, 105, 115, 116, 40, 87, - 105, 110, 100, 111, 119, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 41, 10, 87, 105, 110, - 100, 111, 119, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 32, 60, 45, 32, 73, 100, 101, - 110, 116, 105, 102, 105, 101, 114, 32, 39, 65, 83, 39, 32, 87, 105, 110, 100, 111, 119, 70, - 114, 97, 109, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 10, 83, 97, 109, 112, 108, - 101, 69, 110, 116, 114, 121, 32, 60, 45, 32, 83, 97, 109, 112, 108, 101, 69, 110, 116, 114, - 121, 70, 117, 110, 99, 116, 105, 111, 110, 32, 47, 32, 83, 97, 109, 112, 108, 101, 69, 110, - 116, 114, 121, 67, 111, 117, 110, 116, 10, 83, 97, 109, 112, 108, 101, 69, 110, 116, 114, 121, - 67, 111, 117, 110, 116, 32, 60, 45, 32, 83, 97, 109, 112, 108, 101, 67, 111, 117, 110, 116, - 32, 80, 97, 114, 101, 110, 115, 40, 83, 97, 109, 112, 108, 101, 80, 114, 111, 112, 101, 114, - 116, 105, 101, 115, 41, 63, 10, 83, 97, 109, 112, 108, 101, 69, 110, 116, 114, 121, 70, 117, - 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 83, 97, 109, 112, 108, 101, 70, 117, 110, 99, - 116, 105, 111, 110, 63, 32, 80, 97, 114, 101, 110, 115, 40, 83, 97, 109, 112, 108, 101, 67, - 111, 117, 110, 116, 41, 32, 82, 101, 112, 101, 97, 116, 97, 98, 108, 101, 83, 97, 109, 112, - 108, 101, 63, 10, 83, 97, 109, 112, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, - 45, 32, 67, 111, 108, 73, 100, 10, 83, 97, 109, 112, 108, 101, 80, 114, 111, 112, 101, 114, - 116, 105, 101, 115, 32, 60, 45, 32, 67, 111, 108, 73, 100, 32, 40, 39, 44, 39, 32, 83, - 97, 109, 112, 108, 101, 83, 101, 101, 100, 41, 63, 10, 82, 101, 112, 101, 97, 116, 97, 98, - 108, 101, 83, 97, 109, 112, 108, 101, 32, 60, 45, 32, 39, 82, 69, 80, 69, 65, 84, 65, - 66, 76, 69, 39, 32, 80, 97, 114, 101, 110, 115, 40, 83, 97, 109, 112, 108, 101, 83, 101, - 101, 100, 41, 10, 83, 97, 109, 112, 108, 101, 83, 101, 101, 100, 32, 60, 45, 32, 78, 117, - 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 10, 83, 97, 109, 112, 108, 101, 67, 111, - 117, 110, 116, 32, 60, 45, 32, 83, 97, 109, 112, 108, 101, 86, 97, 108, 117, 101, 32, 83, - 97, 109, 112, 108, 101, 85, 110, 105, 116, 63, 10, 83, 97, 109, 112, 108, 101, 86, 97, 108, - 117, 101, 32, 60, 45, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 32, - 47, 32, 80, 97, 114, 97, 109, 101, 116, 101, 114, 10, 83, 97, 109, 112, 108, 101, 85, 110, - 105, 116, 32, 60, 45, 32, 83, 97, 109, 112, 108, 101, 80, 101, 114, 99, 101, 110, 116, 97, - 103, 101, 32, 47, 32, 83, 97, 109, 112, 108, 101, 82, 111, 119, 115, 10, 83, 97, 109, 112, - 108, 101, 80, 101, 114, 99, 101, 110, 116, 97, 103, 101, 32, 60, 45, 32, 39, 37, 39, 32, - 47, 32, 39, 80, 69, 82, 67, 69, 78, 84, 39, 10, 83, 97, 109, 112, 108, 101, 82, 111, - 119, 115, 32, 60, 45, 32, 39, 82, 79, 87, 83, 39, 10, 71, 114, 111, 117, 112, 66, 121, - 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 115, 32, 60, 45, 32, 71, 114, 111, 117, 112, - 66, 121, 76, 105, 115, 116, 32, 47, 32, 71, 114, 111, 117, 112, 66, 121, 65, 108, 108, 10, + 32, 32, 32, 32, 69, 120, 101, 99, 117, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 73, 110, 115, 101, 114, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 76, 111, 97, 100, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 77, 101, 114, 103, 101, 73, 110, 116, 111, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 80, 114, 97, 103, 109, 97, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 80, 114, 101, 112, 97, 114, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 69, 120, 112, 108, 97, 105, 110, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 85, 112, 100, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 86, 97, 99, 117, 117, 109, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 83, 101, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 10, + 32, 32, 32, 32, 82, 101, 115, 101, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 10, + 69, 120, 112, 111, 114, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 69, 88, 80, 79, 82, 84, 39, 32, 39, 68, 65, 84, 65, 66, 65, 83, 69, 39, 32, 69, 120, 112, 111, 114, 116, 83, 111, 117, 114, 99, 101, 63, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 63, 10, + 69, 120, 112, 111, 114, 116, 83, 111, 117, 114, 99, 101, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, 32, 39, 84, 79, 39, 10, + 73, 109, 112, 111, 114, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 73, 77, 80, 79, 82, 84, 39, 32, 39, 68, 65, 84, 65, 66, 65, 83, 69, 39, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 67, 111, 108, 117, 109, 110, 82, 101, 102, 101, 114, 101, 110, 99, 101, 32, 60, 45, 32, 78, 101, 115, 116, 101, 100, 83, 99, 104, 101, 109, 97, 84, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 47, 32, 67, 97, 116, 97, 108, 111, 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 84, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 47, 32, 83, 99, 104, 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 84, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 47, 32, 84, 97, 98, 108, 101, 82, 101, 115, 101, 114, 118, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 47, 32, 78, 101, 115, 116, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 10, + 35, 32, 97, 32, 99, 111, 108, 117, 109, 110, 32, 111, 102, 32, 97, 32, 116, 97, 98, 108, 101, 32, 105, 110, 32, 97, 32, 110, 101, 115, 116, 101, 100, 32, 115, 99, 104, 101, 109, 97, 32, 99, 104, 97, 105, 110, 32, 45, 32, 102, 105, 118, 101, 32, 111, 114, 32, 109, 111, 114, 101, 32, 99, 111, 109, 112, 111, 110, 101, 110, 116, 115, 44, 32, 119, 104, 101, 114, 101, 32, 116, 104, 101, 32, 113, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 105, 115, 32, 100, 101, 101, 112, 101, 114, 32, 116, 104, 97, 110, 10, + 35, 32, 99, 97, 116, 97, 108, 111, 103, 32, 43, 32, 115, 99, 104, 101, 109, 97, 32, 97, 110, 100, 32, 105, 116, 32, 105, 115, 32, 110, 111, 32, 108, 111, 110, 103, 101, 114, 32, 112, 111, 115, 105, 116, 105, 111, 110, 97, 108, 32, 119, 104, 105, 99, 104, 32, 99, 111, 109, 112, 111, 110, 101, 110, 116, 115, 32, 97, 114, 101, 32, 115, 99, 104, 101, 109, 97, 115, 32, 97, 110, 100, 32, 119, 104, 105, 99, 104, 32, 105, 115, 32, 116, 104, 101, 32, 116, 97, 98, 108, 101, 10, + 78, 101, 115, 116, 101, 100, 83, 99, 104, 101, 109, 97, 84, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 43, 32, 82, 101, 115, 101, 114, 118, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 10, + 67, 97, 116, 97, 108, 111, 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 84, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 84, 97, 98, 108, 101, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 10, + 83, 99, 104, 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 84, 97, 98, 108, 101, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 60, 45, 32, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 84, 97, 98, 108, 101, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 10, + 84, 97, 98, 108, 101, 82, 101, 115, 101, 114, 118, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 60, 45, 32, 84, 97, 98, 108, 101, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 10, + 70, 117, 110, 99, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 70, 117, 110, 99, 116, 105, 111, 110, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 70, 117, 110, 99, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 87, 105, 116, 104, 105, 110, 71, 114, 111, 117, 112, 67, 108, 97, 117, 115, 101, 63, 32, 70, 105, 108, 116, 101, 114, 67, 108, 97, 117, 115, 101, 63, 32, 69, 120, 112, 111, 114, 116, 67, 108, 97, 117, 115, 101, 63, 32, 79, 118, 101, 114, 67, 108, 97, 117, 115, 101, 63, 10, + 70, 117, 110, 99, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 70, 117, 110, 99, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 76, 105, 115, 116, 41, 10, + 70, 117, 110, 99, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 76, 105, 115, 116, 32, 60, 45, 32, 68, 105, 115, 116, 105, 110, 99, 116, 79, 114, 65, 108, 108, 63, 32, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 76, 105, 115, 116, 63, 32, 79, 114, 100, 101, 114, 66, 121, 67, 108, 97, 117, 115, 101, 63, 32, 73, 103, 110, 111, 114, 101, 79, 114, 82, 101, 115, 112, 101, 99, 116, 78, 117, 108, 108, 115, 63, 10, + 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 41, 10, + 70, 117, 110, 99, 116, 105, 111, 110, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 32, 47, 32, 83, 99, 104, 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 32, 47, 32, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 65, 115, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 10, + 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 65, 115, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 32, 60, 45, 32, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 10, + 67, 97, 116, 97, 108, 111, 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 42, 32, 82, 101, 115, 101, 114, 118, 101, 100, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 10, + 83, 99, 104, 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 32, 60, 45, 32, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 10, + 68, 105, 115, 116, 105, 110, 99, 116, 79, 114, 65, 108, 108, 32, 60, 45, 32, 68, 105, 115, 116, 105, 110, 99, 116, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 65, 108, 108, 75, 101, 121, 119, 111, 114, 100, 10, + 68, 105, 115, 116, 105, 110, 99, 116, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 68, 73, 83, 84, 73, 78, 67, 84, 39, 10, + 65, 108, 108, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 65, 76, 76, 39, 10, + 69, 120, 112, 111, 114, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 69, 88, 80, 79, 82, 84, 95, 83, 84, 65, 84, 69, 39, 10, + 87, 105, 116, 104, 105, 110, 71, 114, 111, 117, 112, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 87, 73, 84, 72, 73, 78, 39, 32, 39, 71, 82, 79, 85, 80, 39, 32, 80, 97, 114, 101, 110, 115, 40, 79, 114, 100, 101, 114, 66, 121, 67, 108, 97, 117, 115, 101, 41, 10, + 70, 105, 108, 116, 101, 114, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 70, 73, 76, 84, 69, 82, 39, 32, 70, 105, 108, 116, 101, 114, 67, 108, 97, 117, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 70, 105, 108, 116, 101, 114, 67, 108, 97, 117, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 70, 105, 108, 116, 101, 114, 67, 108, 97, 117, 115, 101, 67, 111, 110, 116, 101, 110, 116, 115, 41, 10, + 70, 105, 108, 116, 101, 114, 67, 108, 97, 117, 115, 101, 67, 111, 110, 116, 101, 110, 116, 115, 32, 60, 45, 32, 39, 87, 72, 69, 82, 69, 39, 63, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 73, 103, 110, 111, 114, 101, 79, 114, 82, 101, 115, 112, 101, 99, 116, 78, 117, 108, 108, 115, 32, 60, 45, 32, 73, 103, 110, 111, 114, 101, 78, 117, 108, 108, 115, 32, 47, 32, 82, 101, 115, 112, 101, 99, 116, 78, 117, 108, 108, 115, 10, + 73, 103, 110, 111, 114, 101, 78, 117, 108, 108, 115, 32, 60, 45, 32, 39, 73, 71, 78, 79, 82, 69, 39, 32, 39, 78, 85, 76, 76, 83, 39, 10, + 82, 101, 115, 112, 101, 99, 116, 78, 117, 108, 108, 115, 32, 60, 45, 32, 39, 82, 69, 83, 80, 69, 67, 84, 39, 32, 39, 78, 85, 76, 76, 83, 39, 10, + 80, 97, 114, 101, 110, 116, 104, 101, 115, 105, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 63, 41, 10, + 76, 105, 116, 101, 114, 97, 108, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 67, 111, 110, 115, 116, 97, 110, 116, 76, 105, 116, 101, 114, 97, 108, 10, + 67, 111, 110, 115, 116, 97, 110, 116, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 78, 117, 108, 108, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 84, 114, 117, 101, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 70, 97, 108, 115, 101, 76, 105, 116, 101, 114, 97, 108, 10, + 78, 117, 108, 108, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 39, 78, 85, 76, 76, 39, 10, + 84, 114, 117, 101, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 39, 84, 82, 85, 69, 39, 10, + 70, 97, 108, 115, 101, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 39, 70, 65, 76, 83, 69, 39, 10, + 67, 97, 115, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 67, 97, 115, 116, 79, 114, 84, 114, 121, 67, 97, 115, 116, 32, 80, 97, 114, 101, 110, 115, 40, 67, 97, 115, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 41, 10, + 67, 97, 115, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 65, 83, 39, 32, 84, 121, 112, 101, 10, + 67, 97, 115, 116, 79, 114, 84, 114, 121, 67, 97, 115, 116, 32, 60, 45, 32, 67, 97, 115, 116, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 84, 114, 121, 67, 97, 115, 116, 75, 101, 121, 119, 111, 114, 100, 10, + 67, 97, 115, 116, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 67, 65, 83, 84, 39, 10, + 84, 114, 121, 67, 97, 115, 116, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 84, 82, 89, 95, 67, 65, 83, 84, 39, 10, + 67, 111, 108, 73, 100, 68, 111, 116, 32, 60, 45, 32, 67, 111, 108, 73, 100, 32, 39, 46, 39, 10, + 83, 116, 97, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 83, 116, 97, 114, 81, 117, 97, 108, 105, 102, 105, 101, 114, 76, 105, 115, 116, 63, 32, 39, 42, 39, 32, 69, 120, 99, 108, 117, 100, 101, 76, 105, 115, 116, 63, 32, 82, 101, 112, 108, 97, 99, 101, 76, 105, 115, 116, 63, 32, 82, 101, 110, 97, 109, 101, 76, 105, 115, 116, 63, 10, + 83, 116, 97, 114, 81, 117, 97, 108, 105, 102, 105, 101, 114, 76, 105, 115, 116, 32, 60, 45, 32, 67, 111, 108, 73, 100, 68, 111, 116, 43, 10, + 69, 120, 99, 108, 117, 100, 101, 76, 105, 115, 116, 32, 60, 45, 32, 69, 120, 99, 108, 117, 100, 101, 79, 114, 69, 120, 99, 101, 112, 116, 32, 69, 120, 99, 108, 117, 100, 101, 78, 97, 109, 101, 115, 10, + 69, 120, 99, 108, 117, 100, 101, 79, 114, 69, 120, 99, 101, 112, 116, 32, 60, 45, 32, 39, 69, 88, 67, 76, 85, 68, 69, 39, 32, 47, 32, 39, 69, 88, 67, 69, 80, 84, 39, 10, + 69, 120, 99, 108, 117, 100, 101, 78, 97, 109, 101, 115, 32, 60, 45, 32, 69, 120, 99, 108, 117, 100, 101, 78, 97, 109, 101, 76, 105, 115, 116, 32, 47, 32, 69, 120, 99, 108, 117, 100, 101, 78, 97, 109, 101, 83, 105, 110, 103, 108, 101, 10, + 69, 120, 99, 108, 117, 100, 101, 78, 97, 109, 101, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 99, 108, 117, 100, 101, 78, 97, 109, 101, 41, 41, 10, + 69, 120, 99, 108, 117, 100, 101, 78, 97, 109, 101, 83, 105, 110, 103, 108, 101, 32, 60, 45, 32, 69, 120, 99, 108, 117, 100, 101, 78, 97, 109, 101, 10, + 69, 120, 99, 108, 117, 100, 101, 78, 97, 109, 101, 32, 60, 45, 32, 69, 120, 99, 108, 117, 100, 101, 68, 111, 116, 116, 101, 100, 78, 97, 109, 101, 32, 47, 32, 69, 120, 99, 108, 117, 100, 101, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 10, + 69, 120, 99, 108, 117, 100, 101, 68, 111, 116, 116, 101, 100, 78, 97, 109, 101, 32, 60, 45, 32, 68, 111, 116, 116, 101, 100, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 69, 120, 99, 108, 117, 100, 101, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 60, 45, 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 10, + 82, 101, 112, 108, 97, 99, 101, 76, 105, 115, 116, 32, 60, 45, 32, 39, 82, 69, 80, 76, 65, 67, 69, 39, 32, 82, 101, 112, 108, 97, 99, 101, 69, 110, 116, 114, 105, 101, 115, 10, + 82, 101, 112, 108, 97, 99, 101, 69, 110, 116, 114, 105, 101, 115, 32, 60, 45, 32, 82, 101, 112, 108, 97, 99, 101, 69, 110, 116, 114, 121, 83, 105, 110, 103, 108, 101, 32, 47, 32, 82, 101, 112, 108, 97, 99, 101, 69, 110, 116, 114, 121, 76, 105, 115, 116, 10, + 82, 101, 112, 108, 97, 99, 101, 69, 110, 116, 114, 121, 83, 105, 110, 103, 108, 101, 32, 60, 45, 32, 82, 101, 112, 108, 97, 99, 101, 69, 110, 116, 114, 121, 10, + 82, 101, 112, 108, 97, 99, 101, 69, 110, 116, 114, 121, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 82, 101, 112, 108, 97, 99, 101, 69, 110, 116, 114, 121, 41, 41, 10, + 82, 101, 112, 108, 97, 99, 101, 69, 110, 116, 114, 121, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 65, 83, 39, 32, 67, 111, 108, 117, 109, 110, 82, 101, 102, 101, 114, 101, 110, 99, 101, 10, + 82, 101, 110, 97, 109, 101, 76, 105, 115, 116, 32, 60, 45, 32, 39, 82, 69, 78, 65, 77, 69, 39, 32, 82, 101, 110, 97, 109, 101, 69, 110, 116, 114, 105, 101, 115, 10, + 82, 101, 110, 97, 109, 101, 69, 110, 116, 114, 105, 101, 115, 32, 60, 45, 32, 82, 101, 110, 97, 109, 101, 69, 110, 116, 114, 121, 76, 105, 115, 116, 32, 47, 32, 83, 105, 110, 103, 108, 101, 82, 101, 110, 97, 109, 101, 69, 110, 116, 114, 121, 10, + 82, 101, 110, 97, 109, 101, 69, 110, 116, 114, 121, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 82, 101, 110, 97, 109, 101, 69, 110, 116, 114, 121, 41, 41, 10, + 83, 105, 110, 103, 108, 101, 82, 101, 110, 97, 109, 101, 69, 110, 116, 114, 121, 32, 60, 45, 32, 82, 101, 110, 97, 109, 101, 69, 110, 116, 114, 121, 10, + 82, 101, 110, 97, 109, 101, 69, 110, 116, 114, 121, 32, 60, 45, 32, 69, 120, 99, 108, 117, 100, 101, 78, 97, 109, 101, 32, 39, 65, 83, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 83, 117, 98, 113, 117, 101, 114, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 83, 117, 98, 113, 117, 101, 114, 121, 78, 111, 116, 63, 32, 83, 117, 98, 113, 117, 101, 114, 121, 69, 120, 105, 115, 116, 115, 63, 32, 83, 117, 98, 113, 117, 101, 114, 121, 82, 101, 102, 101, 114, 101, 110, 99, 101, 10, + 83, 117, 98, 113, 117, 101, 114, 121, 78, 111, 116, 32, 60, 45, 32, 39, 78, 79, 84, 39, 10, + 83, 117, 98, 113, 117, 101, 114, 121, 69, 120, 105, 115, 116, 115, 32, 60, 45, 32, 39, 69, 88, 73, 83, 84, 83, 39, 10, + 67, 97, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 67, 65, 83, 69, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 32, 67, 97, 115, 101, 87, 104, 101, 110, 84, 104, 101, 110, 43, 32, 67, 97, 115, 101, 69, 108, 115, 101, 63, 32, 39, 69, 78, 68, 39, 10, + 67, 97, 115, 101, 87, 104, 101, 110, 84, 104, 101, 110, 32, 60, 45, 32, 39, 87, 72, 69, 78, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 84, 72, 69, 78, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 67, 97, 115, 101, 69, 108, 115, 101, 32, 60, 45, 32, 39, 69, 76, 83, 69, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 84, 121, 112, 101, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 84, 121, 112, 101, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 73, 110, 116, 101, 114, 118, 97, 108, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 39, 73, 78, 84, 69, 82, 86, 65, 76, 39, 32, 73, 110, 116, 101, 114, 118, 97, 108, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 73, 110, 116, 101, 114, 118, 97, 108, 63, 10, + 73, 110, 116, 101, 114, 118, 97, 108, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 60, 45, 32, 73, 110, 116, 101, 114, 118, 97, 108, 83, 116, 114, 105, 110, 103, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 47, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 80, 97, 114, 101, 110, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 73, 110, 116, 101, 114, 118, 97, 108, 83, 116, 114, 105, 110, 103, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 60, 45, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 70, 114, 97, 109, 101, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 70, 114, 97, 109, 105, 110, 103, 32, 70, 114, 97, 109, 101, 69, 120, 116, 101, 110, 116, 32, 87, 105, 110, 100, 111, 119, 69, 120, 99, 108, 117, 100, 101, 67, 108, 97, 117, 115, 101, 63, 10, + 70, 114, 97, 109, 105, 110, 103, 32, 60, 45, 32, 82, 111, 119, 115, 70, 114, 97, 109, 105, 110, 103, 32, 47, 32, 82, 97, 110, 103, 101, 70, 114, 97, 109, 105, 110, 103, 32, 47, 32, 71, 114, 111, 117, 112, 115, 70, 114, 97, 109, 105, 110, 103, 10, + 82, 111, 119, 115, 70, 114, 97, 109, 105, 110, 103, 32, 60, 45, 32, 39, 82, 79, 87, 83, 39, 10, + 82, 97, 110, 103, 101, 70, 114, 97, 109, 105, 110, 103, 32, 60, 45, 32, 39, 82, 65, 78, 71, 69, 39, 10, + 71, 114, 111, 117, 112, 115, 70, 114, 97, 109, 105, 110, 103, 32, 60, 45, 32, 39, 71, 82, 79, 85, 80, 83, 39, 10, + 70, 114, 97, 109, 101, 69, 120, 116, 101, 110, 116, 32, 60, 45, 32, 66, 101, 116, 119, 101, 101, 110, 70, 114, 97, 109, 101, 69, 120, 116, 101, 110, 116, 32, 47, 32, 83, 105, 110, 103, 108, 101, 70, 114, 97, 109, 101, 69, 120, 116, 101, 110, 116, 10, + 83, 105, 110, 103, 108, 101, 70, 114, 97, 109, 101, 69, 120, 116, 101, 110, 116, 32, 60, 45, 32, 70, 114, 97, 109, 101, 66, 111, 117, 110, 100, 10, + 66, 101, 116, 119, 101, 101, 110, 70, 114, 97, 109, 101, 69, 120, 116, 101, 110, 116, 32, 60, 45, 32, 39, 66, 69, 84, 87, 69, 69, 78, 39, 32, 70, 114, 97, 109, 101, 66, 111, 117, 110, 100, 32, 39, 65, 78, 68, 39, 32, 70, 114, 97, 109, 101, 66, 111, 117, 110, 100, 10, + 70, 114, 97, 109, 101, 66, 111, 117, 110, 100, 32, 60, 45, 32, 70, 114, 97, 109, 101, 85, 110, 98, 111, 117, 110, 100, 101, 100, 32, 47, 32, 70, 114, 97, 109, 101, 67, 117, 114, 114, 101, 110, 116, 82, 111, 119, 32, 47, 32, 70, 114, 97, 109, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 70, 114, 97, 109, 101, 85, 110, 98, 111, 117, 110, 100, 101, 100, 32, 60, 45, 32, 39, 85, 78, 66, 79, 85, 78, 68, 69, 68, 39, 32, 80, 114, 101, 99, 101, 100, 105, 110, 103, 79, 114, 70, 111, 108, 108, 111, 119, 105, 110, 103, 10, + 70, 114, 97, 109, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 80, 114, 101, 99, 101, 100, 105, 110, 103, 79, 114, 70, 111, 108, 108, 111, 119, 105, 110, 103, 10, + 70, 114, 97, 109, 101, 67, 117, 114, 114, 101, 110, 116, 82, 111, 119, 32, 60, 45, 32, 39, 67, 85, 82, 82, 69, 78, 84, 39, 32, 39, 82, 79, 87, 39, 10, + 80, 114, 101, 99, 101, 100, 105, 110, 103, 79, 114, 70, 111, 108, 108, 111, 119, 105, 110, 103, 32, 60, 45, 32, 80, 114, 101, 99, 101, 100, 105, 110, 103, 70, 114, 97, 109, 101, 32, 47, 32, 70, 111, 108, 108, 111, 119, 105, 110, 103, 70, 114, 97, 109, 101, 10, + 80, 114, 101, 99, 101, 100, 105, 110, 103, 70, 114, 97, 109, 101, 32, 60, 45, 32, 39, 80, 82, 69, 67, 69, 68, 73, 78, 71, 39, 10, + 70, 111, 108, 108, 111, 119, 105, 110, 103, 70, 114, 97, 109, 101, 32, 60, 45, 32, 39, 70, 79, 76, 76, 79, 87, 73, 78, 71, 39, 10, + 87, 105, 110, 100, 111, 119, 69, 120, 99, 108, 117, 100, 101, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 69, 88, 67, 76, 85, 68, 69, 39, 32, 87, 105, 110, 100, 111, 119, 69, 120, 99, 108, 117, 100, 101, 69, 108, 101, 109, 101, 110, 116, 10, + 87, 105, 110, 100, 111, 119, 69, 120, 99, 108, 117, 100, 101, 69, 108, 101, 109, 101, 110, 116, 32, 60, 45, 32, 69, 120, 99, 108, 117, 100, 101, 67, 117, 114, 114, 101, 110, 116, 82, 111, 119, 32, 47, 32, 69, 120, 99, 108, 117, 100, 101, 71, 114, 111, 117, 112, 32, 47, 32, 69, 120, 99, 108, 117, 100, 101, 84, 105, 101, 115, 32, 47, 32, 69, 120, 99, 108, 117, 100, 101, 78, 111, 79, 116, 104, 101, 114, 115, 10, + 69, 120, 99, 108, 117, 100, 101, 67, 117, 114, 114, 101, 110, 116, 82, 111, 119, 32, 60, 45, 32, 39, 67, 85, 82, 82, 69, 78, 84, 39, 32, 39, 82, 79, 87, 39, 10, + 69, 120, 99, 108, 117, 100, 101, 71, 114, 111, 117, 112, 32, 60, 45, 32, 39, 71, 82, 79, 85, 80, 39, 10, + 69, 120, 99, 108, 117, 100, 101, 84, 105, 101, 115, 32, 60, 45, 32, 39, 84, 73, 69, 83, 39, 10, + 69, 120, 99, 108, 117, 100, 101, 78, 111, 79, 116, 104, 101, 114, 115, 32, 60, 45, 32, 39, 78, 79, 39, 32, 39, 79, 84, 72, 69, 82, 83, 39, 10, + 79, 118, 101, 114, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 79, 86, 69, 82, 39, 32, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 10, + 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 47, 32, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 32, 47, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 10, + 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 80, 97, 114, 101, 110, 115, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 41, 10, + 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 32, 60, 45, 32, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 78, 97, 109, 101, 67, 111, 110, 116, 101, 110, 116, 115, 80, 97, 114, 101, 110, 115, 32, 47, 32, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 67, 111, 110, 116, 101, 110, 116, 115, 80, 97, 114, 101, 110, 115, 10, + 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 78, 97, 109, 101, 67, 111, 110, 116, 101, 110, 116, 115, 80, 97, 114, 101, 110, 115, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 78, 97, 109, 101, 67, 111, 110, 116, 101, 110, 116, 115, 41, 10, + 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 78, 97, 109, 101, 67, 111, 110, 116, 101, 110, 116, 115, 32, 60, 45, 32, 66, 97, 115, 101, 87, 105, 110, 100, 111, 119, 78, 97, 109, 101, 63, 32, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 67, 111, 110, 116, 101, 110, 116, 115, 10, + 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 67, 111, 110, 116, 101, 110, 116, 115, 80, 97, 114, 101, 110, 115, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 67, 111, 110, 116, 101, 110, 116, 115, 41, 10, + 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 67, 111, 110, 116, 101, 110, 116, 115, 32, 60, 45, 32, 87, 105, 110, 100, 111, 119, 80, 97, 114, 116, 105, 116, 105, 111, 110, 63, 32, 79, 114, 100, 101, 114, 66, 121, 67, 108, 97, 117, 115, 101, 63, 32, 70, 114, 97, 109, 101, 67, 108, 97, 117, 115, 101, 63, 10, + 66, 97, 115, 101, 87, 105, 110, 100, 111, 119, 78, 97, 109, 101, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 87, 105, 110, 100, 111, 119, 80, 97, 114, 116, 105, 116, 105, 111, 110, 32, 60, 45, 32, 39, 80, 65, 82, 84, 73, 84, 73, 79, 78, 39, 32, 39, 66, 89, 39, 32, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, + 76, 105, 115, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 65, 114, 114, 97, 121, 66, 111, 117, 110, 100, 101, 100, 76, 105, 115, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 65, 114, 114, 97, 121, 80, 97, 114, 101, 110, 115, 83, 101, 108, 101, 99, 116, 10, + 65, 114, 114, 97, 121, 66, 111, 117, 110, 100, 101, 100, 76, 105, 115, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 65, 82, 82, 65, 89, 39, 63, 32, 66, 111, 117, 110, 100, 101, 100, 76, 105, 115, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 65, 114, 114, 97, 121, 80, 97, 114, 101, 110, 115, 83, 101, 108, 101, 99, 116, 32, 60, 45, 32, 39, 65, 82, 82, 65, 89, 39, 32, 80, 97, 114, 101, 110, 115, 40, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 41, 10, + 66, 111, 117, 110, 100, 101, 100, 76, 105, 115, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 91, 39, 32, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 63, 32, 39, 93, 39, 10, + 83, 116, 114, 117, 99, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 123, 39, 32, 76, 105, 115, 116, 40, 83, 116, 114, 117, 99, 116, 70, 105, 101, 108, 100, 41, 63, 32, 39, 125, 39, 10, + 83, 116, 114, 117, 99, 116, 70, 105, 101, 108, 100, 32, 60, 45, 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 32, 39, 58, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 77, 97, 112, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 77, 65, 80, 39, 32, 77, 97, 112, 83, 116, 114, 117, 99, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 77, 97, 112, 83, 116, 114, 117, 99, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 123, 39, 32, 76, 105, 115, 116, 40, 77, 97, 112, 83, 116, 114, 117, 99, 116, 70, 105, 101, 108, 100, 41, 63, 32, 39, 125, 39, 10, + 77, 97, 112, 83, 116, 114, 117, 99, 116, 70, 105, 101, 108, 100, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 58, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 71, 114, 111, 117, 112, 105, 110, 103, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 71, 114, 111, 117, 112, 105, 110, 103, 79, 114, 71, 114, 111, 117, 112, 105, 110, 103, 73, 100, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 63, 41, 10, + 71, 114, 111, 117, 112, 105, 110, 103, 79, 114, 71, 114, 111, 117, 112, 105, 110, 103, 73, 100, 32, 60, 45, 32, 71, 114, 111, 117, 112, 105, 110, 103, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 71, 114, 111, 117, 112, 105, 110, 103, 73, 100, 75, 101, 121, 119, 111, 114, 100, 10, + 71, 114, 111, 117, 112, 105, 110, 103, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 71, 82, 79, 85, 80, 73, 78, 71, 39, 10, + 71, 114, 111, 117, 112, 105, 110, 103, 73, 100, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 71, 82, 79, 85, 80, 73, 78, 71, 95, 73, 68, 39, 10, + 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 60, 45, 32, 81, 117, 101, 115, 116, 105, 111, 110, 77, 97, 114, 107, 78, 117, 109, 98, 101, 114, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 47, 32, 65, 110, 111, 110, 121, 109, 111, 117, 115, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 47, 32, 78, 117, 109, 98, 101, 114, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 47, 32, 67, 111, 108, 76, 97, 98, 101, 108, 80, 97, 114, 97, 109, 101, 116, 101, 114, 10, + 81, 117, 101, 115, 116, 105, 111, 110, 77, 97, 114, 107, 78, 117, 109, 98, 101, 114, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 60, 45, 32, 39, 63, 39, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 10, + 65, 110, 111, 110, 121, 109, 111, 117, 115, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 60, 45, 32, 39, 63, 39, 10, + 78, 117, 109, 98, 101, 114, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 60, 45, 32, 39, 36, 39, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 10, + 67, 111, 108, 76, 97, 98, 101, 108, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 60, 45, 32, 39, 36, 39, 32, 67, 111, 108, 76, 97, 98, 101, 108, 10, + 80, 111, 115, 105, 116, 105, 111, 110, 97, 108, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 35, 39, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 10, + 68, 101, 102, 97, 117, 108, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 68, 69, 70, 65, 85, 76, 84, 39, 10, + 76, 105, 115, 116, 67, 111, 109, 112, 114, 101, 104, 101, 110, 115, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 91, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 70, 79, 82, 39, 32, 76, 105, 115, 116, 40, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 41, 32, 39, 73, 78, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 76, 105, 115, 116, 67, 111, 109, 112, 114, 101, 104, 101, 110, 115, 105, 111, 110, 70, 105, 108, 116, 101, 114, 63, 32, 39, 93, 39, 10, + 76, 105, 115, 116, 67, 111, 109, 112, 114, 101, 104, 101, 110, 115, 105, 111, 110, 70, 105, 108, 116, 101, 114, 32, 60, 45, 32, 39, 73, 70, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 80, 97, 114, 101, 110, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, + 35, 32, 82, 85, 78, 78, 73, 78, 71, 32, 97, 110, 100, 32, 70, 73, 78, 65, 76, 32, 115, 97, 121, 32, 104, 111, 119, 32, 109, 117, 99, 104, 32, 111, 102, 32, 97, 32, 77, 65, 84, 67, 72, 95, 82, 69, 67, 79, 71, 78, 73, 90, 69, 32, 109, 97, 116, 99, 104, 32, 116, 104, 101, 32, 99, 97, 108, 108, 32, 98, 101, 108, 111, 119, 32, 116, 104, 101, 109, 32, 114, 101, 97, 100, 115, 46, 32, 84, 104, 101, 121, 32, 97, 114, 101, 32, 111, 110, 108, 121, 10, + 35, 32, 109, 101, 97, 110, 105, 110, 103, 102, 117, 108, 32, 105, 110, 32, 77, 69, 65, 83, 85, 82, 69, 83, 44, 32, 119, 104, 105, 99, 104, 32, 105, 115, 32, 119, 104, 101, 114, 101, 32, 116, 104, 101, 32, 98, 105, 110, 100, 101, 114, 32, 99, 104, 101, 99, 107, 115, 32, 102, 111, 114, 32, 116, 104, 101, 109, 46, 10, + 77, 101, 97, 115, 117, 114, 101, 83, 101, 109, 97, 110, 116, 105, 99, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 77, 101, 97, 115, 117, 114, 101, 83, 101, 109, 97, 110, 116, 105, 99, 115, 32, 70, 117, 110, 99, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 83, 105, 110, 103, 108, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 10, + 32, 32, 32, 32, 80, 97, 114, 101, 110, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, + 9, 76, 105, 116, 101, 114, 97, 108, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, + 9, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 47, 10, + 32, 32, 32, 32, 83, 117, 98, 113, 117, 101, 114, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, + 32, 32, 32, 32, 83, 112, 101, 99, 105, 97, 108, 70, 117, 110, 99, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, + 32, 32, 32, 32, 80, 97, 114, 101, 110, 116, 104, 101, 115, 105, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, + 32, 32, 32, 32, 73, 110, 116, 101, 114, 118, 97, 108, 76, 105, 116, 101, 114, 97, 108, 32, 47, 10, + 32, 32, 32, 32, 84, 121, 112, 101, 76, 105, 116, 101, 114, 97, 108, 32, 47, 10, + 32, 32, 32, 32, 67, 97, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, + 32, 32, 32, 32, 83, 116, 97, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, + 32, 32, 32, 32, 67, 97, 115, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, + 32, 32, 32, 32, 71, 114, 111, 117, 112, 105, 110, 103, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, + 32, 32, 32, 32, 77, 97, 112, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, + 32, 32, 32, 32, 77, 101, 97, 115, 117, 114, 101, 83, 101, 109, 97, 110, 116, 105, 99, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, + 32, 32, 32, 32, 70, 117, 110, 99, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, + 32, 32, 32, 32, 67, 111, 108, 117, 109, 110, 82, 101, 102, 101, 114, 101, 110, 99, 101, 32, 47, 10, + 32, 32, 32, 32, 76, 105, 115, 116, 67, 111, 109, 112, 114, 101, 104, 101, 110, 115, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, + 32, 32, 32, 32, 76, 105, 115, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, + 32, 32, 32, 32, 83, 116, 114, 117, 99, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, + 32, 32, 32, 32, 80, 111, 115, 105, 116, 105, 111, 110, 97, 108, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 10, + 32, 32, 32, 32, 68, 101, 102, 97, 117, 108, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 35, 32, 76, 69, 86, 69, 76, 32, 49, 32, 40, 76, 111, 119, 101, 115, 116, 41, 10, + 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 76, 97, 109, 98, 100, 97, 65, 114, 114, 111, 119, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 67, 111, 108, 117, 109, 110, 68, 101, 102, 97, 117, 108, 116, 69, 120, 112, 114, 32, 60, 45, 32, 67, 111, 108, 68, 101, 102, 79, 114, 69, 120, 112, 114, 10, + 35, 32, 76, 69, 86, 69, 76, 32, 49, 46, 53, 10, + 76, 97, 109, 98, 100, 97, 65, 114, 114, 111, 119, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 76, 111, 103, 105, 99, 97, 108, 79, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 83, 105, 110, 103, 108, 101, 65, 114, 114, 111, 119, 80, 97, 105, 114, 42, 10, + 83, 105, 110, 103, 108, 101, 65, 114, 114, 111, 119, 80, 97, 105, 114, 32, 60, 45, 32, 39, 45, 62, 39, 32, 76, 111, 103, 105, 99, 97, 108, 79, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 76, 111, 103, 105, 99, 97, 108, 79, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 76, 111, 103, 105, 99, 97, 108, 65, 110, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 76, 111, 103, 105, 99, 97, 108, 79, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, + 76, 111, 103, 105, 99, 97, 108, 79, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 39, 79, 82, 39, 32, 76, 111, 103, 105, 99, 97, 108, 65, 110, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 67, 111, 108, 68, 101, 102, 79, 114, 69, 120, 112, 114, 32, 60, 45, 32, 67, 111, 108, 68, 101, 102, 65, 110, 100, 69, 120, 112, 114, 32, 67, 111, 108, 68, 101, 102, 79, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, + 67, 111, 108, 68, 101, 102, 79, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 39, 79, 82, 39, 32, 67, 111, 108, 68, 101, 102, 65, 110, 100, 69, 120, 112, 114, 10, + 35, 32, 76, 69, 86, 69, 76, 32, 50, 10, + 76, 111, 103, 105, 99, 97, 108, 65, 110, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 76, 111, 103, 105, 99, 97, 108, 78, 111, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 76, 111, 103, 105, 99, 97, 108, 65, 110, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, + 76, 111, 103, 105, 99, 97, 108, 65, 110, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 39, 65, 78, 68, 39, 32, 76, 111, 103, 105, 99, 97, 108, 78, 111, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 67, 111, 108, 68, 101, 102, 65, 110, 100, 69, 120, 112, 114, 32, 60, 45, 32, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, 114, 111, 109, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 67, 111, 108, 68, 101, 102, 65, 110, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, + 67, 111, 108, 68, 101, 102, 65, 110, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 39, 65, 78, 68, 39, 32, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, 114, 111, 109, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 35, 32, 76, 69, 86, 69, 76, 32, 51, 10, + 76, 111, 103, 105, 99, 97, 108, 78, 111, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 78, 111, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 32, 73, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 78, 111, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 78, 111, 116, 75, 101, 121, 119, 111, 114, 100, 43, 10, + 78, 111, 116, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 78, 79, 84, 39, 10, + 35, 32, 76, 69, 86, 69, 76, 32, 52, 10, + 73, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, 114, 111, 109, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 73, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 67, 111, 110, 116, 105, 110, 117, 97, 116, 105, 111, 110, 63, 10, + 73, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 67, 111, 110, 116, 105, 110, 117, 97, 116, 105, 111, 110, 32, 60, 45, 32, 73, 115, 84, 101, 115, 116, 32, 73, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, + 73, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 73, 115, 84, 101, 115, 116, 84, 97, 105, 108, 32, 47, 32, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 84, 97, 105, 108, 32, 47, 32, 73, 115, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 84, 97, 105, 108, 32, 47, 32, 73, 115, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 84, 97, 105, 108, 10, + 73, 115, 84, 101, 115, 116, 84, 97, 105, 108, 32, 60, 45, 32, 73, 115, 84, 101, 115, 116, 10, + 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 84, 97, 105, 108, 32, 60, 45, 32, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, 114, 111, 109, 84, 97, 105, 108, 10, + 73, 115, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 10, + 73, 115, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 84, 97, 105, 108, 32, 60, 45, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 84, 97, 105, 108, 10, + 73, 115, 84, 101, 115, 116, 32, 60, 45, 32, 73, 115, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 78, 111, 116, 78, 117, 108, 108, 32, 47, 32, 73, 115, 78, 117, 108, 108, 10, + 73, 115, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 39, 73, 83, 39, 32, 39, 78, 79, 84, 39, 63, 32, 73, 115, 76, 105, 116, 101, 114, 97, 108, 86, 97, 108, 117, 101, 10, + 73, 115, 76, 105, 116, 101, 114, 97, 108, 86, 97, 108, 117, 101, 32, 60, 45, 32, 84, 114, 117, 101, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 70, 97, 108, 115, 101, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 78, 117, 108, 108, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 85, 110, 107, 110, 111, 119, 110, 76, 105, 116, 101, 114, 97, 108, 10, + 85, 110, 107, 110, 111, 119, 110, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 39, 85, 78, 75, 78, 79, 87, 78, 39, 10, + 78, 111, 116, 78, 117, 108, 108, 32, 60, 45, 32, 78, 111, 116, 78, 117, 108, 108, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 78, 111, 116, 78, 117, 108, 108, 79, 112, 101, 114, 97, 116, 111, 114, 10, + 78, 111, 116, 78, 117, 108, 108, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 78, 79, 84, 39, 32, 39, 78, 85, 76, 76, 39, 10, + 78, 111, 116, 78, 117, 108, 108, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 78, 79, 84, 78, 85, 76, 76, 39, 10, + 73, 115, 78, 117, 108, 108, 32, 60, 45, 32, 73, 115, 78, 117, 108, 108, 79, 112, 101, 114, 97, 116, 111, 114, 10, + 73, 115, 78, 117, 108, 108, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 73, 83, 78, 85, 76, 76, 39, 10, + 35, 32, 76, 69, 86, 69, 76, 32, 53, 32, 40, 73, 83, 32, 68, 73, 83, 84, 73, 78, 67, 84, 32, 70, 82, 79, 77, 32, 114, 101, 116, 97, 105, 110, 115, 32, 116, 104, 101, 32, 99, 111, 109, 112, 97, 114, 105, 115, 111, 110, 32, 98, 105, 110, 100, 105, 110, 103, 41, 10, + 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, 114, 111, 109, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, 114, 111, 109, 84, 97, 105, 108, 42, 10, + 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, 114, 111, 109, 84, 97, 105, 108, 32, 60, 45, 32, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, 114, 111, 109, 79, 112, 32, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, 114, 111, 109, 79, 112, 32, 60, 45, 32, 39, 73, 83, 39, 32, 39, 78, 79, 84, 39, 63, 32, 39, 68, 73, 83, 84, 73, 78, 67, 84, 39, 32, 39, 70, 82, 79, 77, 39, 10, + 35, 32, 76, 69, 86, 69, 76, 32, 54, 10, + 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 66, 101, 116, 119, 101, 101, 110, 73, 110, 76, 105, 107, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, + 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 79, 112, 101, 114, 97, 116, 111, 114, 32, 78, 111, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 32, 66, 101, 116, 119, 101, 101, 110, 73, 110, 76, 105, 107, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 10, + 32, 32, 32, 32, 79, 112, 101, 114, 97, 116, 111, 114, 69, 113, 117, 97, 108, 32, 47, 10, + 32, 32, 32, 32, 79, 112, 101, 114, 97, 116, 111, 114, 78, 111, 116, 69, 113, 117, 97, 108, 32, 47, 10, + 32, 32, 32, 32, 79, 112, 101, 114, 97, 116, 111, 114, 76, 101, 115, 115, 84, 104, 97, 110, 32, 47, 10, + 32, 32, 32, 32, 79, 112, 101, 114, 97, 116, 111, 114, 71, 114, 101, 97, 116, 101, 114, 84, 104, 97, 110, 32, 47, 10, + 32, 32, 32, 32, 79, 112, 101, 114, 97, 116, 111, 114, 76, 101, 115, 115, 84, 104, 97, 110, 69, 113, 117, 97, 108, 115, 32, 47, 10, + 32, 32, 32, 32, 79, 112, 101, 114, 97, 116, 111, 114, 71, 114, 101, 97, 116, 101, 114, 84, 104, 97, 110, 69, 113, 117, 97, 108, 115, 10, + 79, 112, 101, 114, 97, 116, 111, 114, 69, 113, 117, 97, 108, 32, 60, 45, 32, 39, 61, 39, 32, 47, 32, 39, 61, 61, 39, 10, + 79, 112, 101, 114, 97, 116, 111, 114, 78, 111, 116, 69, 113, 117, 97, 108, 32, 60, 45, 32, 39, 33, 61, 39, 32, 47, 32, 39, 60, 62, 39, 10, + 79, 112, 101, 114, 97, 116, 111, 114, 76, 101, 115, 115, 84, 104, 97, 110, 32, 60, 45, 32, 39, 60, 39, 10, + 79, 112, 101, 114, 97, 116, 111, 114, 71, 114, 101, 97, 116, 101, 114, 84, 104, 97, 110, 32, 60, 45, 32, 39, 62, 39, 10, + 79, 112, 101, 114, 97, 116, 111, 114, 76, 101, 115, 115, 84, 104, 97, 110, 69, 113, 117, 97, 108, 115, 32, 60, 45, 32, 39, 60, 61, 39, 10, + 79, 112, 101, 114, 97, 116, 111, 114, 71, 114, 101, 97, 116, 101, 114, 84, 104, 97, 110, 69, 113, 117, 97, 108, 115, 32, 60, 45, 32, 39, 62, 61, 39, 10, + 35, 32, 76, 69, 86, 69, 76, 32, 55, 10, + 66, 101, 116, 119, 101, 101, 110, 73, 110, 76, 105, 107, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 73, 110, 80, 114, 101, 100, 105, 99, 97, 116, 101, 42, 32, 66, 101, 116, 119, 101, 101, 110, 76, 105, 107, 101, 79, 112, 63, 10, + 73, 110, 80, 114, 101, 100, 105, 99, 97, 116, 101, 32, 60, 45, 32, 39, 78, 79, 84, 39, 63, 32, 73, 110, 67, 108, 97, 117, 115, 101, 10, + 66, 101, 116, 119, 101, 101, 110, 76, 105, 107, 101, 79, 112, 32, 60, 45, 32, 39, 78, 79, 84, 39, 63, 32, 66, 101, 116, 119, 101, 101, 110, 76, 105, 107, 101, 79, 112, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 66, 101, 116, 119, 101, 101, 110, 76, 105, 107, 101, 79, 112, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 66, 101, 116, 119, 101, 101, 110, 67, 108, 97, 117, 115, 101, 32, 47, 32, 76, 105, 107, 101, 67, 108, 97, 117, 115, 101, 10, + 76, 105, 107, 101, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 76, 105, 107, 101, 86, 97, 114, 105, 97, 116, 105, 111, 110, 115, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 69, 115, 99, 97, 112, 101, 67, 108, 97, 117, 115, 101, 63, 10, + 69, 115, 99, 97, 112, 101, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 69, 83, 67, 65, 80, 69, 39, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 76, 105, 107, 101, 86, 97, 114, 105, 97, 116, 105, 111, 110, 115, 32, 60, 45, 32, 83, 105, 109, 105, 108, 97, 114, 84, 111, 84, 111, 107, 101, 110, 32, 47, 32, 82, 101, 103, 101, 120, 73, 110, 115, 101, 110, 115, 105, 116, 105, 118, 101, 77, 97, 116, 99, 104, 84, 111, 107, 101, 110, 32, 47, 32, 82, 101, 103, 101, 120, 77, 97, 116, 99, 104, 84, 111, 107, 101, 110, 32, 47, 32, 73, 76, 105, 107, 101, 84, 111, 107, 101, 110, 32, 47, 32, 76, 105, 107, 101, 84, 111, 107, 101, 110, 32, 47, 32, 71, 108, 111, 98, 84, 111, 107, 101, 110, 32, 47, 32, 78, 111, 116, 73, 76, 105, 107, 101, 79, 112, 32, 47, 32, 78, 111, 116, 76, 105, 107, 101, 79, 112, 32, 47, 32, 78, 111, 116, 82, 101, 103, 101, 120, 73, 110, 115, 101, 110, 115, 105, 116, 105, 118, 101, 77, 97, 116, 99, 104, 79, 112, 32, 47, 32, 78, 111, 116, 83, 105, 109, 105, 108, 97, 114, 84, 111, 79, 112, 10, + 76, 105, 107, 101, 84, 111, 107, 101, 110, 32, 32, 32, 32, 32, 32, 60, 45, 32, 39, 76, 73, 75, 69, 39, 32, 47, 32, 39, 126, 126, 39, 10, + 73, 76, 105, 107, 101, 84, 111, 107, 101, 110, 32, 32, 32, 32, 32, 60, 45, 32, 39, 73, 76, 73, 75, 69, 39, 32, 47, 32, 39, 126, 126, 42, 39, 10, + 71, 108, 111, 98, 84, 111, 107, 101, 110, 32, 32, 32, 32, 32, 32, 60, 45, 32, 39, 71, 76, 79, 66, 39, 32, 47, 32, 39, 126, 126, 126, 39, 10, + 83, 105, 109, 105, 108, 97, 114, 84, 111, 84, 111, 107, 101, 110, 32, 60, 45, 32, 39, 83, 73, 77, 73, 76, 65, 82, 39, 32, 39, 84, 79, 39, 10, + 82, 101, 103, 101, 120, 77, 97, 116, 99, 104, 84, 111, 107, 101, 110, 32, 60, 45, 32, 39, 126, 39, 10, + 82, 101, 103, 101, 120, 73, 110, 115, 101, 110, 115, 105, 116, 105, 118, 101, 77, 97, 116, 99, 104, 84, 111, 107, 101, 110, 32, 60, 45, 32, 39, 126, 42, 39, 10, + 78, 111, 116, 73, 76, 105, 107, 101, 79, 112, 32, 32, 32, 32, 32, 60, 45, 32, 39, 33, 126, 126, 42, 39, 10, + 78, 111, 116, 76, 105, 107, 101, 79, 112, 32, 32, 32, 32, 32, 32, 60, 45, 32, 39, 33, 126, 126, 39, 10, + 78, 111, 116, 82, 101, 103, 101, 120, 73, 110, 115, 101, 110, 115, 105, 116, 105, 118, 101, 77, 97, 116, 99, 104, 79, 112, 32, 60, 45, 32, 39, 33, 126, 42, 39, 10, + 78, 111, 116, 83, 105, 109, 105, 108, 97, 114, 84, 111, 79, 112, 32, 60, 45, 32, 39, 33, 126, 39, 10, + 73, 110, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 73, 78, 39, 32, 73, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 73, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 73, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 76, 105, 115, 116, 32, 47, 32, 73, 110, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 32, 73, 110, 67, 111, 110, 116, 97, 105, 110, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 73, 110, 67, 111, 110, 116, 97, 105, 110, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 73, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 41, 10, + 73, 110, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 41, 10, + 66, 101, 116, 119, 101, 101, 110, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 66, 69, 84, 87, 69, 69, 78, 39, 32, 65, 115, 121, 109, 109, 101, 116, 114, 105, 99, 63, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 65, 78, 68, 39, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 65, 115, 121, 109, 109, 101, 116, 114, 105, 99, 32, 60, 45, 32, 39, 65, 83, 89, 77, 77, 69, 84, 82, 73, 67, 39, 10, + 35, 32, 76, 69, 86, 69, 76, 32, 56, 10, + 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 73, 110, 102, 105, 120, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 67, 117, 115, 116, 111, 109, 80, 114, 101, 102, 105, 120, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 73, 110, 102, 105, 120, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 66, 105, 116, 119, 105, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 84, 97, 105, 108, 42, 10, + 67, 117, 115, 116, 111, 109, 80, 114, 101, 102, 105, 120, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 79, 112, 101, 114, 97, 116, 111, 114, 76, 105, 116, 101, 114, 97, 108, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 84, 97, 105, 108, 32, 60, 45, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 32, 66, 105, 116, 119, 105, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 10, + 32, 32, 32, 32, 65, 110, 121, 65, 108, 108, 80, 97, 114, 115, 101, 100, 79, 112, 101, 114, 97, 116, 111, 114, 32, 47, 32, 78, 97, 109, 101, 100, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 10, + 65, 110, 121, 65, 108, 108, 80, 97, 114, 115, 101, 100, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 65, 110, 121, 65, 108, 108, 79, 112, 101, 114, 97, 116, 111, 114, 10, + 78, 97, 109, 101, 100, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 10, + 32, 32, 32, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 79, 112, 101, 114, 97, 116, 111, 114, 32, 47, 32, 79, 112, 101, 114, 97, 116, 111, 114, 76, 105, 116, 101, 114, 97, 108, 10, + 79, 112, 101, 114, 97, 116, 111, 114, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 65, 110, 121, 79, 112, 101, 114, 97, 116, 111, 114, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 65, 110, 121, 65, 108, 108, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 65, 110, 121, 79, 112, 101, 114, 97, 116, 111, 114, 76, 105, 116, 101, 114, 97, 108, 32, 65, 110, 121, 79, 114, 65, 108, 108, 10, + 65, 110, 121, 79, 114, 65, 108, 108, 32, 60, 45, 32, 83, 117, 98, 113, 117, 101, 114, 121, 65, 110, 121, 32, 47, 32, 83, 117, 98, 113, 117, 101, 114, 121, 65, 108, 108, 10, + 83, 117, 98, 113, 117, 101, 114, 121, 65, 110, 121, 32, 60, 45, 32, 39, 65, 78, 89, 39, 32, 47, 32, 39, 83, 79, 77, 69, 39, 10, + 83, 117, 98, 113, 117, 101, 114, 121, 65, 108, 108, 32, 60, 45, 32, 39, 65, 76, 76, 39, 10, + 81, 117, 97, 108, 105, 102, 105, 101, 100, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 79, 80, 69, 82, 65, 84, 79, 82, 39, 32, 80, 97, 114, 101, 110, 115, 40, 81, 117, 97, 108, 105, 102, 105, 101, 100, 79, 112, 101, 114, 97, 116, 111, 114, 67, 111, 110, 116, 101, 110, 116, 115, 41, 10, + 81, 117, 97, 108, 105, 102, 105, 101, 100, 79, 112, 101, 114, 97, 116, 111, 114, 67, 111, 110, 116, 101, 110, 116, 115, 32, 60, 45, 32, 67, 111, 108, 73, 100, 68, 111, 116, 42, 32, 65, 110, 121, 79, 112, 101, 114, 97, 116, 111, 114, 76, 105, 116, 101, 114, 97, 108, 10, + 35, 32, 76, 69, 86, 69, 76, 32, 57, 10, + 66, 105, 116, 119, 105, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 84, 105, 108, 100, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 66, 105, 116, 119, 105, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, + 66, 105, 116, 119, 105, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 66, 105, 116, 79, 112, 101, 114, 97, 116, 111, 114, 32, 84, 105, 108, 100, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 66, 105, 116, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 38, 39, 32, 47, 32, 39, 124, 39, 32, 47, 32, 39, 60, 60, 39, 32, 47, 32, 39, 62, 62, 39, 10, + 35, 32, 76, 69, 86, 69, 76, 32, 57, 46, 53, 10, + 84, 105, 108, 100, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 84, 105, 108, 100, 101, 80, 114, 101, 102, 105, 120, 79, 112, 101, 114, 97, 116, 111, 114, 42, 32, 65, 100, 100, 105, 116, 105, 118, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 35, 32, 76, 69, 86, 69, 76, 32, 49, 48, 10, + 65, 100, 100, 105, 116, 105, 118, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 77, 117, 108, 116, 105, 112, 108, 105, 99, 97, 116, 105, 118, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 65, 100, 100, 105, 116, 105, 118, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, + 65, 100, 100, 105, 116, 105, 118, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 84, 101, 114, 109, 32, 77, 117, 108, 116, 105, 112, 108, 105, 99, 97, 116, 105, 118, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 84, 101, 114, 109, 32, 60, 45, 32, 39, 43, 39, 32, 47, 32, 39, 45, 39, 10, + 35, 32, 76, 69, 86, 69, 76, 32, 49, 49, 10, + 77, 117, 108, 116, 105, 112, 108, 105, 99, 97, 116, 105, 118, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 69, 120, 112, 111, 110, 101, 110, 116, 105, 97, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 77, 117, 108, 116, 105, 112, 108, 105, 99, 97, 116, 105, 118, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, + 77, 117, 108, 116, 105, 112, 108, 105, 99, 97, 116, 105, 118, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 70, 97, 99, 116, 111, 114, 32, 69, 120, 112, 111, 110, 101, 110, 116, 105, 97, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 70, 97, 99, 116, 111, 114, 32, 60, 45, 32, 39, 42, 39, 32, 47, 32, 39, 47, 39, 32, 47, 32, 39, 47, 47, 39, 32, 47, 32, 39, 37, 39, 10, + 35, 32, 76, 69, 86, 69, 76, 32, 49, 50, 10, + 69, 120, 112, 111, 110, 101, 110, 116, 105, 97, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 67, 111, 108, 108, 97, 116, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 69, 120, 112, 111, 110, 101, 110, 116, 105, 97, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, + 69, 120, 112, 111, 110, 101, 110, 116, 105, 97, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 69, 120, 112, 111, 110, 101, 110, 116, 79, 112, 101, 114, 97, 116, 111, 114, 32, 67, 111, 108, 108, 97, 116, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 69, 120, 112, 111, 110, 101, 110, 116, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 94, 39, 32, 47, 32, 39, 42, 42, 39, 10, + 35, 32, 76, 69, 86, 69, 76, 32, 49, 51, 10, + 67, 111, 108, 108, 97, 116, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 65, 116, 84, 105, 109, 101, 90, 111, 110, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 67, 111, 108, 108, 97, 116, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, + 67, 111, 108, 108, 97, 116, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 67, 111, 108, 108, 97, 116, 101, 79, 112, 101, 114, 97, 116, 111, 114, 32, 65, 116, 84, 105, 109, 101, 90, 111, 110, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 67, 111, 108, 108, 97, 116, 101, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 67, 79, 76, 76, 65, 84, 69, 39, 10, + 35, 32, 76, 69, 86, 69, 76, 32, 49, 52, 10, + 65, 116, 84, 105, 109, 101, 90, 111, 110, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 80, 114, 101, 102, 105, 120, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 65, 116, 84, 105, 109, 101, 90, 111, 110, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, + 65, 116, 84, 105, 109, 101, 90, 111, 110, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 65, 116, 84, 105, 109, 101, 90, 111, 110, 101, 79, 112, 101, 114, 97, 116, 111, 114, 32, 80, 114, 101, 102, 105, 120, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 65, 116, 84, 105, 109, 101, 90, 111, 110, 101, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 65, 84, 39, 32, 39, 84, 73, 77, 69, 39, 32, 39, 90, 79, 78, 69, 39, 10, + 35, 32, 76, 69, 86, 69, 76, 32, 49, 53, 10, + 80, 114, 101, 102, 105, 120, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 80, 114, 101, 102, 105, 120, 79, 112, 101, 114, 97, 116, 111, 114, 42, 32, 66, 97, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 80, 114, 101, 102, 105, 120, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 79, 112, 101, 114, 97, 116, 111, 114, 32, 47, 32, 77, 105, 110, 117, 115, 80, 114, 101, 102, 105, 120, 79, 112, 101, 114, 97, 116, 111, 114, 32, 47, 32, 80, 108, 117, 115, 80, 114, 101, 102, 105, 120, 79, 112, 101, 114, 97, 116, 111, 114, 10, + 77, 105, 110, 117, 115, 80, 114, 101, 102, 105, 120, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 45, 39, 10, + 80, 108, 117, 115, 80, 114, 101, 102, 105, 120, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 43, 39, 10, + 84, 105, 108, 100, 101, 80, 114, 101, 102, 105, 120, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 126, 39, 10, + 35, 32, 76, 69, 86, 69, 76, 32, 49, 54, 32, 40, 72, 105, 103, 104, 101, 115, 116, 41, 10, + 66, 97, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 83, 105, 110, 103, 108, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 73, 110, 100, 105, 114, 101, 99, 116, 105, 111, 110, 76, 105, 115, 116, 63, 10, + 73, 110, 100, 105, 114, 101, 99, 116, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 73, 110, 100, 105, 114, 101, 99, 116, 105, 111, 110, 43, 10, + 73, 110, 100, 105, 114, 101, 99, 116, 105, 111, 110, 32, 60, 45, 32, 67, 97, 115, 116, 79, 112, 101, 114, 97, 116, 111, 114, 32, 47, 32, 68, 111, 116, 79, 112, 101, 114, 97, 116, 111, 114, 32, 47, 32, 83, 108, 105, 99, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 80, 111, 115, 116, 102, 105, 120, 79, 112, 101, 114, 97, 116, 111, 114, 10, + 67, 97, 115, 116, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 58, 58, 39, 32, 84, 121, 112, 101, 10, + 68, 111, 116, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 68, 111, 116, 77, 101, 116, 104, 111, 100, 79, 112, 101, 114, 97, 116, 111, 114, 32, 47, 32, 68, 111, 116, 67, 111, 108, 117, 109, 110, 79, 112, 101, 114, 97, 116, 111, 114, 10, + 68, 111, 116, 77, 101, 116, 104, 111, 100, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 46, 39, 32, 77, 101, 116, 104, 111, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 68, 111, 116, 67, 111, 108, 117, 109, 110, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 46, 39, 32, 67, 111, 108, 76, 97, 98, 101, 108, 10, + 77, 101, 116, 104, 111, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 67, 111, 108, 76, 97, 98, 101, 108, 32, 77, 101, 116, 104, 111, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 10, + 77, 101, 116, 104, 111, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 77, 101, 116, 104, 111, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 76, 105, 115, 116, 41, 10, + 77, 101, 116, 104, 111, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 76, 105, 115, 116, 32, 60, 45, 32, 68, 105, 115, 116, 105, 110, 99, 116, 79, 114, 65, 108, 108, 63, 32, 77, 101, 116, 104, 111, 100, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 63, 32, 79, 114, 100, 101, 114, 66, 121, 67, 108, 97, 117, 115, 101, 63, 32, 73, 103, 110, 111, 114, 101, 79, 114, 82, 101, 115, 112, 101, 99, 116, 78, 117, 108, 108, 115, 63, 10, + 77, 101, 116, 104, 111, 100, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, 45, 32, 76, 105, 115, 116, 40, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 41, 10, + 83, 108, 105, 99, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 91, 39, 32, 83, 108, 105, 99, 101, 66, 111, 117, 110, 100, 32, 39, 93, 39, 10, + 83, 108, 105, 99, 101, 66, 111, 117, 110, 100, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 32, 83, 108, 105, 99, 101, 85, 112, 112, 101, 114, 66, 111, 117, 110, 100, 63, 10, + 83, 108, 105, 99, 101, 85, 112, 112, 101, 114, 66, 111, 117, 110, 100, 32, 60, 45, 32, 77, 105, 110, 117, 115, 83, 116, 101, 112, 83, 108, 105, 99, 101, 66, 111, 117, 110, 100, 32, 47, 32, 69, 110, 100, 83, 116, 101, 112, 83, 108, 105, 99, 101, 66, 111, 117, 110, 100, 10, + 77, 105, 110, 117, 115, 83, 116, 101, 112, 83, 108, 105, 99, 101, 66, 111, 117, 110, 100, 32, 60, 45, 32, 39, 58, 39, 32, 39, 45, 39, 32, 83, 116, 101, 112, 83, 108, 105, 99, 101, 66, 111, 117, 110, 100, 10, + 69, 110, 100, 83, 116, 101, 112, 83, 108, 105, 99, 101, 66, 111, 117, 110, 100, 32, 60, 45, 32, 69, 110, 100, 83, 108, 105, 99, 101, 66, 111, 117, 110, 100, 32, 83, 116, 101, 112, 83, 108, 105, 99, 101, 66, 111, 117, 110, 100, 63, 10, + 69, 110, 100, 83, 108, 105, 99, 101, 66, 111, 117, 110, 100, 32, 60, 45, 32, 39, 58, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 10, + 83, 116, 101, 112, 83, 108, 105, 99, 101, 66, 111, 117, 110, 100, 32, 60, 45, 32, 39, 58, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 10, + 80, 111, 115, 116, 102, 105, 120, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 32, 39, 33, 39, 10, + 83, 112, 101, 99, 105, 97, 108, 70, 117, 110, 99, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 67, 111, 97, 108, 101, 115, 99, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 85, 110, 112, 97, 99, 107, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 84, 114, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 67, 111, 108, 117, 109, 110, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 69, 120, 116, 114, 97, 99, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 76, 97, 109, 98, 100, 97, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 78, 117, 108, 108, 73, 102, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 80, 111, 115, 105, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 82, 111, 119, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 83, 117, 98, 115, 116, 114, 105, 110, 103, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 84, 114, 105, 109, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 79, 118, 101, 114, 108, 97, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 67, 111, 97, 108, 101, 115, 99, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 67, 79, 65, 76, 69, 83, 67, 69, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 41, 10, + 85, 110, 112, 97, 99, 107, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 85, 78, 80, 65, 67, 75, 39, 32, 80, 97, 114, 101, 110, 115, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, + 84, 114, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 84, 82, 89, 39, 32, 80, 97, 114, 101, 110, 115, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, + 67, 111, 108, 117, 109, 110, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 83, 116, 97, 114, 83, 121, 109, 98, 111, 108, 63, 32, 39, 67, 79, 76, 85, 77, 78, 83, 39, 32, 80, 97, 114, 101, 110, 115, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, + 69, 120, 116, 114, 97, 99, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 69, 88, 84, 82, 65, 67, 84, 39, 32, 80, 97, 114, 101, 110, 115, 40, 69, 120, 116, 114, 97, 99, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 41, 10, + 69, 120, 116, 114, 97, 99, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, 45, 32, 69, 120, 116, 114, 97, 99, 116, 65, 114, 103, 117, 109, 101, 110, 116, 32, 39, 70, 82, 79, 77, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 76, 97, 109, 98, 100, 97, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 76, 65, 77, 66, 68, 65, 39, 32, 76, 105, 115, 116, 40, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 41, 32, 39, 58, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 78, 117, 108, 108, 73, 102, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 78, 85, 76, 76, 73, 70, 39, 32, 80, 97, 114, 101, 110, 115, 40, 78, 117, 108, 108, 73, 102, 65, 114, 103, 117, 109, 101, 110, 116, 115, 41, 10, + 78, 117, 108, 108, 73, 102, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 44, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 80, 111, 115, 105, 116, 105, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 80, 79, 83, 73, 84, 73, 79, 78, 39, 32, 80, 97, 114, 101, 110, 115, 40, 80, 111, 115, 105, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 41, 10, + 80, 111, 115, 105, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, 45, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 73, 78, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 82, 111, 119, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 82, 79, 87, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 63, 41, 10, + 83, 117, 98, 115, 116, 114, 105, 110, 103, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 83, 85, 66, 83, 84, 82, 73, 78, 71, 39, 32, 80, 97, 114, 101, 110, 115, 40, 83, 117, 98, 115, 116, 114, 105, 110, 103, 65, 114, 103, 117, 109, 101, 110, 116, 115, 41, 10, + 83, 117, 98, 115, 116, 114, 105, 110, 103, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, 45, 32, 83, 117, 98, 115, 116, 114, 105, 110, 103, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 32, 47, 32, 83, 117, 98, 115, 116, 114, 105, 110, 103, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 76, 105, 115, 116, 10, + 83, 117, 98, 115, 116, 114, 105, 110, 103, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, + 83, 117, 98, 115, 116, 114, 105, 110, 103, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 83, 117, 98, 115, 116, 114, 105, 110, 103, 70, 114, 111, 109, 70, 111, 114, 10, + 83, 117, 98, 115, 116, 114, 105, 110, 103, 70, 114, 111, 109, 70, 111, 114, 32, 60, 45, 32, 83, 117, 98, 115, 116, 114, 105, 110, 103, 70, 114, 111, 109, 79, 112, 116, 105, 111, 110, 97, 108, 70, 111, 114, 32, 47, 32, 83, 117, 98, 115, 116, 114, 105, 110, 103, 70, 111, 114, 10, + 83, 117, 98, 115, 116, 114, 105, 110, 103, 70, 114, 111, 109, 79, 112, 116, 105, 111, 110, 97, 108, 70, 111, 114, 32, 60, 45, 32, 70, 114, 111, 109, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 70, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 10, + 83, 117, 98, 115, 116, 114, 105, 110, 103, 70, 111, 114, 32, 60, 45, 32, 70, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 84, 114, 105, 109, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 84, 82, 73, 77, 39, 32, 80, 97, 114, 101, 110, 115, 40, 84, 114, 105, 109, 65, 114, 103, 117, 109, 101, 110, 116, 115, 41, 10, + 84, 114, 105, 109, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, 45, 32, 84, 114, 105, 109, 68, 105, 114, 101, 99, 116, 105, 111, 110, 63, 32, 84, 114, 105, 109, 83, 111, 117, 114, 99, 101, 63, 32, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, + 84, 114, 105, 109, 68, 105, 114, 101, 99, 116, 105, 111, 110, 32, 60, 45, 32, 84, 114, 105, 109, 66, 111, 116, 104, 32, 47, 32, 84, 114, 105, 109, 76, 101, 97, 100, 105, 110, 103, 32, 47, 32, 84, 114, 105, 109, 84, 114, 97, 105, 108, 105, 110, 103, 10, + 84, 114, 105, 109, 66, 111, 116, 104, 32, 60, 45, 32, 39, 66, 79, 84, 72, 39, 10, + 84, 114, 105, 109, 76, 101, 97, 100, 105, 110, 103, 32, 60, 45, 32, 39, 76, 69, 65, 68, 73, 78, 71, 39, 10, + 84, 114, 105, 109, 84, 114, 97, 105, 108, 105, 110, 103, 32, 60, 45, 32, 39, 84, 82, 65, 73, 76, 73, 78, 71, 39, 10, + 84, 114, 105, 109, 83, 111, 117, 114, 99, 101, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 32, 39, 70, 82, 79, 77, 39, 10, + 79, 118, 101, 114, 108, 97, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 79, 86, 69, 82, 76, 65, 89, 39, 32, 80, 97, 114, 101, 110, 115, 40, 79, 118, 101, 114, 108, 97, 121, 65, 114, 103, 117, 109, 101, 110, 116, 115, 41, 10, + 79, 118, 101, 114, 108, 97, 121, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, 45, 32, 79, 118, 101, 114, 108, 97, 121, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 32, 47, 32, 79, 118, 101, 114, 108, 97, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 76, 105, 115, 116, 10, + 79, 118, 101, 114, 108, 97, 121, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 80, 76, 65, 67, 73, 78, 71, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 70, 114, 111, 109, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 70, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 10, + 70, 114, 111, 109, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 70, 82, 79, 77, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 70, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 70, 79, 82, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 79, 118, 101, 114, 108, 97, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, + 69, 120, 116, 114, 97, 99, 116, 65, 114, 103, 117, 109, 101, 110, 116, 32, 60, 45, 32, 69, 120, 116, 114, 97, 99, 116, 68, 97, 116, 101, 80, 97, 114, 116, 65, 114, 103, 117, 109, 101, 110, 116, 32, 47, 32, 69, 120, 116, 114, 97, 99, 116, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 65, 114, 103, 117, 109, 101, 110, 116, 32, 47, 32, 69, 120, 116, 114, 97, 99, 116, 83, 116, 114, 105, 110, 103, 65, 114, 103, 117, 109, 101, 110, 116, 10, + 69, 120, 116, 114, 97, 99, 116, 68, 97, 116, 101, 80, 97, 114, 116, 65, 114, 103, 117, 109, 101, 110, 116, 32, 60, 45, 32, 69, 120, 116, 114, 97, 99, 116, 68, 97, 116, 101, 80, 97, 114, 116, 10, + 69, 120, 116, 114, 97, 99, 116, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 65, 114, 103, 117, 109, 101, 110, 116, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 69, 120, 116, 114, 97, 99, 116, 83, 116, 114, 105, 110, 103, 65, 114, 103, 117, 109, 101, 110, 116, 32, 60, 45, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 69, 120, 116, 114, 97, 99, 116, 68, 97, 116, 101, 80, 97, 114, 116, 32, 60, 45, 10, + 32, 32, 32, 32, 89, 101, 97, 114, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 77, 111, 110, 116, 104, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 68, 97, 121, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 72, 111, 117, 114, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 77, 105, 110, 117, 116, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 83, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, + 32, 32, 32, 32, 77, 105, 108, 108, 105, 115, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 77, 105, 99, 114, 111, 115, 101, 99, 111, 110, 100, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 87, 101, 101, 107, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 81, 117, 97, 114, 116, 101, 114, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 68, 101, 99, 97, 100, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 10, + 32, 32, 32, 32, 67, 101, 110, 116, 117, 114, 121, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 77, 105, 108, 108, 101, 110, 110, 105, 117, 109, 75, 101, 121, 119, 111, 114, 100, 10, + 35, 32, 69, 118, 101, 114, 121, 32, 115, 116, 97, 116, 101, 109, 101, 110, 116, 32, 97, 98, 111, 117, 116, 32, 101, 120, 116, 101, 114, 110, 97, 108, 32, 114, 101, 115, 111, 117, 114, 99, 101, 115, 44, 32, 115, 111, 32, 116, 104, 101, 32, 116, 111, 112, 45, 108, 101, 118, 101, 108, 32, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 99, 104, 111, 105, 99, 101, 32, 99, 97, 114, 114, 105, 101, 115, 32, 111, 110, 101, 32, 101, 110, 116, 114, 121, 10, + 35, 32, 114, 97, 116, 104, 101, 114, 32, 116, 104, 97, 110, 32, 111, 110, 101, 32, 112, 101, 114, 32, 102, 111, 114, 109, 46, 10, + 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 67, 114, 101, 97, 116, 101, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 83, 116, 109, 116, 32, 47, 32, 82, 101, 103, 105, 115, 116, 101, 114, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 83, 116, 109, 116, 32, 47, 32, 68, 101, 115, 116, 114, 111, 121, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 83, 116, 109, 116, 32, 47, 32, 83, 104, 111, 119, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 115, 83, 116, 109, 116, 32, 47, 32, 65, 116, 116, 97, 99, 104, 84, 111, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 32, 47, 32, 67, 111, 110, 110, 101, 99, 116, 84, 111, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 10, + 67, 114, 101, 97, 116, 101, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 83, 116, 109, 116, 32, 60, 45, 32, 39, 67, 82, 69, 65, 84, 69, 39, 32, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 75, 101, 121, 119, 111, 114, 100, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 65, 116, 116, 97, 99, 104, 65, 108, 105, 97, 115, 63, 32, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 67, 114, 101, 97, 116, 105, 111, 110, 79, 112, 116, 105, 111, 110, 115, 63, 10, + 82, 101, 103, 105, 115, 116, 101, 114, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 83, 116, 109, 116, 32, 60, 45, 32, 39, 82, 69, 71, 73, 83, 84, 69, 82, 39, 32, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 75, 101, 121, 119, 111, 114, 100, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 65, 116, 116, 97, 99, 104, 65, 108, 105, 97, 115, 63, 32, 39, 70, 82, 79, 77, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 68, 101, 115, 116, 114, 111, 121, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 83, 116, 109, 116, 32, 60, 45, 32, 39, 68, 69, 83, 84, 82, 79, 89, 39, 32, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 75, 101, 121, 119, 111, 114, 100, 32, 67, 111, 108, 73, 100, 10, + 83, 104, 111, 119, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 115, 83, 116, 109, 116, 32, 60, 45, 32, 39, 83, 72, 79, 87, 39, 32, 83, 104, 111, 119, 65, 108, 108, 77, 111, 100, 105, 102, 105, 101, 114, 63, 32, 39, 69, 88, 84, 69, 82, 78, 65, 76, 39, 32, 39, 82, 69, 83, 79, 85, 82, 67, 69, 83, 39, 10, + 83, 104, 111, 119, 65, 108, 108, 77, 111, 100, 105, 102, 105, 101, 114, 32, 60, 45, 32, 39, 65, 76, 76, 39, 10, + 35, 32, 83, 121, 110, 116, 97, 120, 32, 111, 110, 108, 121, 58, 32, 116, 104, 101, 32, 107, 101, 121, 119, 111, 114, 100, 32, 112, 97, 105, 114, 32, 101, 118, 101, 114, 121, 32, 115, 105, 110, 103, 117, 108, 97, 114, 32, 102, 111, 114, 109, 32, 114, 101, 112, 101, 97, 116, 115, 46, 32, 84, 104, 101, 32, 112, 108, 117, 114, 97, 108, 32, 105, 110, 32, 83, 72, 79, 87, 32, 99, 97, 110, 110, 111, 116, 32, 115, 104, 97, 114, 101, 32, 105, 116, 46, 10, + 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 69, 88, 84, 69, 82, 78, 65, 76, 39, 32, 39, 82, 69, 83, 79, 85, 82, 67, 69, 39, 10, + 35, 32, 84, 104, 101, 32, 114, 101, 99, 105, 112, 101, 39, 115, 32, 99, 114, 101, 97, 116, 101, 32, 112, 97, 114, 97, 109, 115, 46, 32, 73, 116, 115, 32, 111, 119, 110, 32, 114, 117, 108, 101, 32, 114, 97, 116, 104, 101, 114, 32, 116, 104, 97, 110, 32, 65, 116, 116, 97, 99, 104, 79, 112, 116, 105, 111, 110, 115, 58, 32, 116, 104, 101, 32, 116, 119, 111, 32, 111, 112, 116, 105, 111, 110, 32, 98, 97, 103, 115, 32, 97, 114, 101, 10, + 35, 32, 100, 105, 115, 116, 105, 110, 99, 116, 44, 32, 97, 110, 100, 32, 97, 32, 115, 116, 97, 116, 101, 109, 101, 110, 116, 32, 99, 97, 110, 32, 99, 97, 114, 114, 121, 32, 98, 111, 116, 104, 46, 10, + 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 67, 114, 101, 97, 116, 105, 111, 110, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 71, 101, 110, 101, 114, 105, 99, 67, 111, 112, 121, 79, 112, 116, 105, 111, 110, 76, 105, 115, 116, 10, + 35, 32, 84, 104, 101, 32, 97, 116, 116, 97, 99, 104, 32, 97, 108, 105, 97, 115, 32, 105, 115, 32, 109, 97, 110, 100, 97, 116, 111, 114, 121, 58, 32, 105, 116, 32, 115, 101, 112, 97, 114, 97, 116, 101, 115, 32, 116, 104, 101, 32, 114, 101, 99, 105, 112, 101, 39, 115, 32, 99, 114, 101, 97, 116, 101, 32, 112, 97, 114, 97, 109, 115, 44, 32, 119, 104, 105, 99, 104, 32, 112, 114, 101, 99, 101, 100, 101, 32, 105, 116, 44, 32, 102, 114, 111, 109, 32, 116, 104, 101, 10, + 35, 32, 97, 116, 116, 97, 99, 104, 109, 101, 110, 116, 39, 115, 32, 111, 119, 110, 32, 111, 112, 116, 105, 111, 110, 115, 44, 32, 119, 104, 105, 99, 104, 32, 102, 111, 108, 108, 111, 119, 46, 32, 87, 105, 116, 104, 111, 117, 116, 32, 105, 116, 32, 97, 32, 115, 105, 110, 103, 108, 101, 32, 111, 112, 116, 105, 111, 110, 32, 108, 105, 115, 116, 32, 119, 111, 117, 108, 100, 32, 98, 101, 32, 97, 109, 98, 105, 103, 117, 111, 117, 115, 46, 10, + 35, 32, 67, 79, 78, 78, 69, 67, 84, 32, 104, 97, 115, 32, 110, 111, 32, 115, 117, 99, 104, 32, 115, 101, 112, 97, 114, 97, 116, 111, 114, 46, 32, 66, 111, 114, 114, 111, 119, 105, 110, 103, 32, 97, 32, 114, 101, 103, 105, 115, 116, 101, 114, 101, 100, 32, 114, 101, 115, 111, 117, 114, 99, 101, 32, 104, 97, 115, 32, 110, 111, 32, 99, 114, 101, 97, 116, 101, 32, 112, 97, 114, 97, 109, 115, 32, 116, 111, 32, 98, 101, 32, 99, 111, 110, 102, 117, 115, 101, 100, 10, + 35, 32, 119, 105, 116, 104, 44, 32, 115, 111, 32, 97, 32, 108, 105, 115, 116, 32, 116, 104, 101, 114, 101, 32, 105, 115, 32, 116, 104, 101, 32, 99, 111, 110, 110, 101, 99, 116, 105, 111, 110, 39, 115, 59, 32, 112, 114, 111, 118, 105, 115, 105, 111, 110, 105, 110, 103, 32, 100, 111, 101, 115, 44, 32, 97, 110, 100, 32, 116, 104, 101, 32, 116, 114, 97, 110, 115, 102, 111, 114, 109, 101, 114, 32, 114, 101, 106, 101, 99, 116, 115, 32, 116, 104, 101, 32, 115, 101, 99, 111, 110, 100, 10, + 35, 32, 108, 105, 115, 116, 32, 114, 97, 116, 104, 101, 114, 32, 116, 104, 97, 110, 32, 108, 101, 116, 32, 112, 111, 115, 105, 116, 105, 111, 110, 32, 100, 101, 99, 105, 100, 101, 46, 10, + 65, 116, 116, 97, 99, 104, 84, 111, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 32, 60, 45, 32, 39, 65, 84, 84, 65, 67, 72, 39, 32, 39, 84, 79, 39, 32, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 83, 111, 117, 114, 99, 101, 32, 65, 116, 116, 97, 99, 104, 65, 108, 105, 97, 115, 32, 65, 116, 116, 97, 99, 104, 79, 112, 116, 105, 111, 110, 115, 63, 10, + 67, 111, 110, 110, 101, 99, 116, 84, 111, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 32, 60, 45, 32, 39, 67, 79, 78, 78, 69, 67, 84, 39, 32, 39, 84, 79, 39, 32, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 83, 111, 117, 114, 99, 101, 32, 65, 116, 116, 97, 99, 104, 79, 112, 116, 105, 111, 110, 115, 63, 10, + 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 83, 111, 117, 114, 99, 101, 32, 60, 45, 32, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 67, 114, 101, 97, 116, 101, 67, 108, 97, 117, 115, 101, 32, 47, 32, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 82, 101, 102, 101, 114, 101, 110, 99, 101, 67, 108, 97, 117, 115, 101, 10, + 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 67, 114, 101, 97, 116, 101, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 78, 69, 87, 39, 32, 39, 84, 69, 77, 80, 79, 82, 65, 82, 89, 39, 32, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 75, 101, 121, 119, 111, 114, 100, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 67, 114, 101, 97, 116, 105, 111, 110, 79, 112, 116, 105, 111, 110, 115, 63, 10, + 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 82, 101, 102, 101, 114, 101, 110, 99, 101, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 69, 120, 116, 101, 114, 110, 97, 108, 82, 101, 115, 111, 117, 114, 99, 101, 75, 101, 121, 119, 111, 114, 100, 32, 67, 111, 108, 73, 100, 10, + 73, 110, 115, 101, 114, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 87, 105, 116, 104, 67, 108, 97, 117, 115, 101, 63, 32, 39, 73, 78, 83, 69, 82, 84, 39, 32, 79, 114, 65, 99, 116, 105, 111, 110, 63, 32, 39, 73, 78, 84, 79, 39, 32, 73, 110, 115, 101, 114, 116, 84, 97, 114, 103, 101, 116, 32, 66, 121, 78, 97, 109, 101, 79, 114, 80, 111, 115, 105, 116, 105, 111, 110, 63, 32, 73, 110, 115, 101, 114, 116, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 63, 32, 79, 118, 101, 114, 114, 105, 100, 105, 110, 103, 67, 108, 97, 117, 115, 101, 63, 32, 73, 110, 115, 101, 114, 116, 86, 97, 108, 117, 101, 115, 32, 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 67, 108, 97, 117, 115, 101, 63, 32, 82, 101, 116, 117, 114, 110, 105, 110, 103, 67, 108, 97, 117, 115, 101, 63, 10, + 79, 114, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 73, 110, 115, 101, 114, 116, 79, 114, 82, 101, 112, 108, 97, 99, 101, 32, 47, 32, 73, 110, 115, 101, 114, 116, 79, 114, 73, 103, 110, 111, 114, 101, 10, + 73, 110, 115, 101, 114, 116, 79, 114, 82, 101, 112, 108, 97, 99, 101, 32, 60, 45, 32, 39, 79, 82, 39, 32, 39, 82, 69, 80, 76, 65, 67, 69, 39, 10, + 73, 110, 115, 101, 114, 116, 79, 114, 73, 103, 110, 111, 114, 101, 32, 60, 45, 32, 39, 79, 82, 39, 32, 39, 73, 71, 78, 79, 82, 69, 39, 10, + 66, 121, 78, 97, 109, 101, 79, 114, 80, 111, 115, 105, 116, 105, 111, 110, 32, 60, 45, 32, 73, 110, 115, 101, 114, 116, 66, 121, 78, 97, 109, 101, 79, 114, 100, 101, 114, 32, 47, 32, 73, 110, 115, 101, 114, 116, 66, 121, 80, 111, 115, 105, 116, 105, 111, 110, 79, 114, 100, 101, 114, 10, + 73, 110, 115, 101, 114, 116, 66, 121, 78, 97, 109, 101, 79, 114, 100, 101, 114, 32, 60, 45, 32, 39, 66, 89, 39, 32, 73, 110, 115, 101, 114, 116, 66, 121, 78, 97, 109, 101, 10, + 73, 110, 115, 101, 114, 116, 66, 121, 80, 111, 115, 105, 116, 105, 111, 110, 79, 114, 100, 101, 114, 32, 60, 45, 32, 39, 66, 89, 39, 32, 73, 110, 115, 101, 114, 116, 66, 121, 80, 111, 115, 105, 116, 105, 111, 110, 10, + 73, 110, 115, 101, 114, 116, 66, 121, 78, 97, 109, 101, 32, 60, 45, 32, 39, 78, 65, 77, 69, 39, 10, + 73, 110, 115, 101, 114, 116, 66, 121, 80, 111, 115, 105, 116, 105, 111, 110, 32, 60, 45, 32, 39, 80, 79, 83, 73, 84, 73, 79, 78, 39, 10, + 79, 118, 101, 114, 114, 105, 100, 105, 110, 103, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 79, 86, 69, 82, 82, 73, 68, 73, 78, 71, 39, 32, 79, 118, 101, 114, 114, 105, 100, 105, 110, 103, 83, 99, 111, 112, 101, 32, 39, 86, 65, 76, 85, 69, 39, 10, + 79, 118, 101, 114, 114, 105, 100, 105, 110, 103, 83, 99, 111, 112, 101, 32, 60, 45, 32, 39, 85, 83, 69, 82, 39, 32, 47, 32, 39, 83, 89, 83, 84, 69, 77, 39, 10, + 73, 110, 115, 101, 114, 116, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 73, 110, 115, 101, 114, 116, 65, 108, 105, 97, 115, 63, 10, + 73, 110, 115, 101, 114, 116, 65, 108, 105, 97, 115, 32, 60, 45, 32, 39, 65, 83, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 67, 111, 108, 73, 100, 41, 10, + 73, 110, 115, 101, 114, 116, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 41, 10, + 73, 110, 115, 101, 114, 116, 86, 97, 108, 117, 101, 115, 32, 60, 45, 32, 83, 101, 108, 101, 99, 116, 73, 110, 115, 101, 114, 116, 86, 97, 108, 117, 101, 115, 32, 47, 32, 68, 101, 102, 97, 117, 108, 116, 86, 97, 108, 117, 101, 115, 10, + 83, 101, 108, 101, 99, 116, 73, 110, 115, 101, 114, 116, 86, 97, 108, 117, 101, 115, 32, 60, 45, 32, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 10, + 68, 101, 102, 97, 117, 108, 116, 86, 97, 108, 117, 101, 115, 32, 60, 45, 32, 39, 68, 69, 70, 65, 85, 76, 84, 39, 32, 39, 86, 65, 76, 85, 69, 83, 39, 10, + 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 79, 78, 39, 32, 39, 67, 79, 78, 70, 76, 73, 67, 84, 39, 32, 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 84, 97, 114, 103, 101, 116, 63, 32, 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 65, 99, 116, 105, 111, 110, 10, + 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 114, 103, 101, 116, 32, 47, 32, 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 73, 110, 100, 101, 120, 84, 97, 114, 103, 101, 116, 10, + 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 67, 111, 108, 117, 109, 110, 73, 100, 76, 105, 115, 116, 32, 87, 104, 101, 114, 101, 67, 108, 97, 117, 115, 101, 63, 10, + 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 73, 110, 100, 101, 120, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 39, 79, 78, 39, 32, 39, 67, 79, 78, 83, 84, 82, 65, 73, 78, 84, 39, 32, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 78, 97, 109, 101, 10, + 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 85, 112, 100, 97, 116, 101, 32, 47, 32, 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 78, 111, 116, 104, 105, 110, 103, 10, + 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 85, 112, 100, 97, 116, 101, 32, 60, 45, 32, 39, 68, 79, 39, 32, 39, 85, 80, 68, 65, 84, 69, 39, 32, 39, 83, 69, 84, 39, 32, 85, 112, 100, 97, 116, 101, 83, 101, 116, 67, 108, 97, 117, 115, 101, 76, 105, 115, 116, 32, 87, 104, 101, 114, 101, 67, 108, 97, 117, 115, 101, 63, 10, + 79, 110, 67, 111, 110, 102, 108, 105, 99, 116, 78, 111, 116, 104, 105, 110, 103, 32, 60, 45, 32, 39, 68, 79, 39, 32, 39, 78, 79, 84, 72, 73, 78, 71, 39, 10, + 82, 101, 116, 117, 114, 110, 105, 110, 103, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 82, 69, 84, 85, 82, 78, 73, 78, 71, 39, 32, 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, 10, + 76, 111, 97, 100, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 76, 79, 65, 68, 39, 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 32, 70, 114, 111, 109, 83, 111, 117, 114, 99, 101, 63, 32, 69, 120, 116, 101, 110, 115, 105, 111, 110, 65, 108, 105, 97, 115, 63, 10, + 69, 120, 116, 101, 110, 115, 105, 111, 110, 65, 108, 105, 97, 115, 32, 60, 45, 32, 39, 65, 83, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 73, 110, 115, 116, 97, 108, 108, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 70, 79, 82, 67, 69, 39, 63, 32, 39, 73, 78, 83, 84, 65, 76, 76, 39, 32, 73, 110, 115, 116, 97, 108, 108, 65, 110, 100, 76, 111, 97, 100, 63, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 79, 114, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 70, 114, 111, 109, 83, 111, 117, 114, 99, 101, 63, 32, 86, 101, 114, 115, 105, 111, 110, 78, 117, 109, 98, 101, 114, 63, 10, + 73, 110, 115, 116, 97, 108, 108, 65, 110, 100, 76, 111, 97, 100, 32, 60, 45, 32, 39, 65, 78, 68, 39, 32, 39, 76, 79, 65, 68, 39, 10, + 85, 112, 100, 97, 116, 101, 69, 120, 116, 101, 110, 115, 105, 111, 110, 115, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 85, 80, 68, 65, 84, 69, 39, 32, 39, 69, 88, 84, 69, 78, 83, 73, 79, 78, 83, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 41, 41, 63, 10, + 70, 114, 111, 109, 83, 111, 117, 114, 99, 101, 32, 60, 45, 32, 70, 114, 111, 109, 83, 111, 117, 114, 99, 101, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 47, 32, 70, 114, 111, 109, 83, 111, 117, 114, 99, 101, 83, 116, 114, 105, 110, 103, 10, + 70, 114, 111, 109, 83, 111, 117, 114, 99, 101, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 39, 70, 82, 79, 77, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 70, 114, 111, 109, 83, 111, 117, 114, 99, 101, 83, 116, 114, 105, 110, 103, 32, 60, 45, 32, 39, 70, 82, 79, 77, 39, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 86, 101, 114, 115, 105, 111, 110, 78, 117, 109, 98, 101, 114, 32, 60, 45, 32, 39, 86, 69, 82, 83, 73, 79, 78, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 79, 114, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 69, 120, 116, 101, 110, 115, 105, 111, 110, 82, 101, 112, 111, 115, 105, 116, 111, 114, 121, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 67, 114, 101, 97, 116, 101, 69, 120, 116, 101, 110, 115, 105, 111, 110, 82, 101, 112, 111, 115, 105, 116, 111, 114, 121, 83, 116, 109, 116, 32, 47, 32, 68, 114, 111, 112, 69, 120, 116, 101, 110, 115, 105, 111, 110, 82, 101, 112, 111, 115, 105, 116, 111, 114, 121, 83, 116, 109, 116, 10, + 67, 114, 101, 97, 116, 101, 69, 120, 116, 101, 110, 115, 105, 111, 110, 82, 101, 112, 111, 115, 105, 116, 111, 114, 121, 83, 116, 109, 116, 32, 60, 45, 32, 39, 67, 82, 69, 65, 84, 69, 39, 32, 79, 114, 82, 101, 112, 108, 97, 99, 101, 63, 32, 39, 69, 88, 84, 69, 78, 83, 73, 79, 78, 39, 32, 39, 82, 69, 80, 79, 83, 73, 84, 79, 82, 89, 39, 32, 73, 102, 78, 111, 116, 69, 120, 105, 115, 116, 115, 63, 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 32, 82, 101, 112, 111, 115, 105, 116, 111, 114, 121, 80, 114, 101, 102, 105, 120, 32, 82, 101, 112, 111, 115, 105, 116, 111, 114, 121, 80, 117, 98, 108, 105, 99, 75, 101, 121, 63, 10, + 82, 101, 112, 111, 115, 105, 116, 111, 114, 121, 80, 114, 101, 102, 105, 120, 32, 60, 45, 32, 39, 87, 73, 84, 72, 39, 63, 32, 39, 80, 82, 69, 70, 73, 88, 39, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 82, 101, 112, 111, 115, 105, 116, 111, 114, 121, 80, 117, 98, 108, 105, 99, 75, 101, 121, 32, 60, 45, 32, 39, 85, 83, 73, 78, 71, 39, 63, 32, 39, 80, 85, 66, 76, 73, 67, 39, 32, 40, 39, 75, 69, 89, 83, 39, 32, 47, 32, 39, 75, 69, 89, 39, 41, 32, 76, 105, 115, 116, 40, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 41, 10, + 68, 114, 111, 112, 69, 120, 116, 101, 110, 115, 105, 111, 110, 82, 101, 112, 111, 115, 105, 116, 111, 114, 121, 83, 116, 109, 116, 32, 60, 45, 32, 39, 68, 82, 79, 80, 39, 32, 39, 69, 88, 84, 69, 78, 83, 73, 79, 78, 39, 32, 39, 82, 69, 80, 79, 83, 73, 84, 79, 82, 89, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 10, + 84, 97, 98, 108, 101, 77, 97, 116, 99, 104, 82, 101, 99, 111, 103, 110, 105, 122, 101, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 77, 65, 84, 67, 72, 95, 82, 69, 67, 79, 71, 78, 73, 90, 69, 39, 32, 80, 97, 114, 101, 110, 115, 40, 77, 97, 116, 99, 104, 82, 101, 99, 111, 103, 110, 105, 122, 101, 66, 111, 100, 121, 41, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 10, + 77, 97, 116, 99, 104, 82, 101, 99, 111, 103, 110, 105, 122, 101, 66, 111, 100, 121, 32, 60, 45, 32, 77, 97, 116, 99, 104, 82, 101, 99, 111, 103, 110, 105, 122, 101, 67, 108, 97, 117, 115, 101, 43, 10, + 77, 97, 116, 99, 104, 82, 101, 99, 111, 103, 110, 105, 122, 101, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 77, 82, 80, 97, 114, 116, 105, 116, 105, 111, 110, 32, 47, 32, 77, 82, 79, 114, 100, 101, 114, 66, 121, 32, 47, 32, 77, 82, 77, 101, 97, 115, 117, 114, 101, 115, 32, 47, 32, 77, 82, 82, 111, 119, 115, 32, 47, 32, 77, 82, 83, 107, 105, 112, 32, 47, 32, 77, 82, 80, 97, 116, 116, 101, 114, 110, 32, 47, 32, 77, 82, 83, 117, 98, 115, 101, 116, 32, 47, 32, 77, 82, 68, 101, 102, 105, 110, 101, 65, 117, 116, 111, 32, 47, 32, 77, 82, 68, 101, 102, 105, 110, 101, 10, + 77, 82, 80, 97, 114, 116, 105, 116, 105, 111, 110, 32, 60, 45, 32, 87, 105, 110, 100, 111, 119, 80, 97, 114, 116, 105, 116, 105, 111, 110, 10, + 77, 82, 79, 114, 100, 101, 114, 66, 121, 32, 60, 45, 32, 79, 114, 100, 101, 114, 66, 121, 67, 108, 97, 117, 115, 101, 10, + 77, 82, 77, 101, 97, 115, 117, 114, 101, 115, 32, 60, 45, 32, 77, 101, 97, 115, 117, 114, 101, 115, 67, 108, 97, 117, 115, 101, 10, + 77, 82, 82, 111, 119, 115, 32, 60, 45, 32, 82, 111, 119, 115, 80, 101, 114, 77, 97, 116, 99, 104, 10, + 77, 82, 83, 107, 105, 112, 32, 60, 45, 32, 65, 102, 116, 101, 114, 77, 97, 116, 99, 104, 83, 107, 105, 112, 10, + 77, 82, 80, 97, 116, 116, 101, 114, 110, 32, 60, 45, 32, 80, 97, 116, 116, 101, 114, 110, 67, 108, 97, 117, 115, 101, 10, + 77, 82, 83, 117, 98, 115, 101, 116, 32, 60, 45, 32, 83, 117, 98, 115, 101, 116, 67, 108, 97, 117, 115, 101, 10, + 77, 82, 68, 101, 102, 105, 110, 101, 65, 117, 116, 111, 32, 60, 45, 32, 40, 39, 68, 69, 70, 73, 78, 69, 39, 32, 47, 32, 39, 68, 69, 70, 73, 78, 69, 83, 39, 41, 32, 39, 65, 85, 84, 79, 39, 10, + 77, 82, 68, 101, 102, 105, 110, 101, 32, 60, 45, 32, 68, 101, 102, 105, 110, 101, 67, 108, 97, 117, 115, 101, 10, + 77, 101, 97, 115, 117, 114, 101, 115, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 77, 69, 65, 83, 85, 82, 69, 83, 39, 32, 76, 105, 115, 116, 40, 77, 101, 97, 115, 117, 114, 101, 115, 69, 108, 101, 109, 101, 110, 116, 41, 10, + 77, 101, 97, 115, 117, 114, 101, 115, 69, 108, 101, 109, 101, 110, 116, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 65, 83, 39, 32, 67, 111, 108, 76, 97, 98, 101, 108, 79, 114, 83, 116, 114, 105, 110, 103, 10, + 77, 101, 97, 115, 117, 114, 101, 83, 101, 109, 97, 110, 116, 105, 99, 115, 32, 60, 45, 32, 82, 117, 110, 110, 105, 110, 103, 83, 101, 109, 97, 110, 116, 105, 99, 115, 32, 47, 32, 70, 105, 110, 97, 108, 83, 101, 109, 97, 110, 116, 105, 99, 115, 10, + 82, 117, 110, 110, 105, 110, 103, 83, 101, 109, 97, 110, 116, 105, 99, 115, 32, 60, 45, 32, 39, 82, 85, 78, 78, 73, 78, 71, 39, 10, + 70, 105, 110, 97, 108, 83, 101, 109, 97, 110, 116, 105, 99, 115, 32, 60, 45, 32, 39, 70, 73, 78, 65, 76, 39, 10, + 82, 111, 119, 115, 80, 101, 114, 77, 97, 116, 99, 104, 32, 60, 45, 32, 79, 110, 101, 82, 111, 119, 80, 101, 114, 77, 97, 116, 99, 104, 32, 47, 32, 65, 108, 108, 82, 111, 119, 115, 80, 101, 114, 77, 97, 116, 99, 104, 10, + 79, 110, 101, 82, 111, 119, 80, 101, 114, 77, 97, 116, 99, 104, 32, 60, 45, 32, 39, 79, 78, 69, 39, 32, 39, 82, 79, 87, 39, 32, 39, 80, 69, 82, 39, 32, 39, 77, 65, 84, 67, 72, 39, 10, + 65, 108, 108, 82, 111, 119, 115, 80, 101, 114, 77, 97, 116, 99, 104, 32, 60, 45, 32, 39, 65, 76, 76, 39, 32, 39, 82, 79, 87, 83, 39, 32, 39, 80, 69, 82, 39, 32, 39, 77, 65, 84, 67, 72, 39, 32, 65, 108, 108, 82, 111, 119, 115, 69, 109, 112, 116, 121, 77, 97, 116, 99, 104, 101, 115, 63, 10, + 65, 108, 108, 82, 111, 119, 115, 69, 109, 112, 116, 121, 77, 97, 116, 99, 104, 101, 115, 32, 60, 45, 32, 83, 104, 111, 119, 69, 109, 112, 116, 121, 77, 97, 116, 99, 104, 101, 115, 32, 47, 32, 79, 109, 105, 116, 69, 109, 112, 116, 121, 77, 97, 116, 99, 104, 101, 115, 32, 47, 32, 87, 105, 116, 104, 85, 110, 109, 97, 116, 99, 104, 101, 100, 82, 111, 119, 115, 10, + 83, 104, 111, 119, 69, 109, 112, 116, 121, 77, 97, 116, 99, 104, 101, 115, 32, 60, 45, 32, 39, 83, 72, 79, 87, 39, 32, 39, 69, 77, 80, 84, 89, 39, 32, 39, 77, 65, 84, 67, 72, 69, 83, 39, 10, + 79, 109, 105, 116, 69, 109, 112, 116, 121, 77, 97, 116, 99, 104, 101, 115, 32, 60, 45, 32, 39, 79, 77, 73, 84, 39, 32, 39, 69, 77, 80, 84, 89, 39, 32, 39, 77, 65, 84, 67, 72, 69, 83, 39, 10, + 87, 105, 116, 104, 85, 110, 109, 97, 116, 99, 104, 101, 100, 82, 111, 119, 115, 32, 60, 45, 32, 39, 87, 73, 84, 72, 39, 32, 39, 85, 78, 77, 65, 84, 67, 72, 69, 68, 39, 32, 39, 82, 79, 87, 83, 39, 10, + 65, 102, 116, 101, 114, 77, 97, 116, 99, 104, 83, 107, 105, 112, 32, 60, 45, 32, 39, 65, 70, 84, 69, 82, 39, 32, 39, 77, 65, 84, 67, 72, 39, 32, 39, 83, 75, 73, 80, 39, 32, 65, 102, 116, 101, 114, 77, 97, 116, 99, 104, 83, 107, 105, 112, 84, 97, 114, 103, 101, 116, 10, + 65, 102, 116, 101, 114, 77, 97, 116, 99, 104, 83, 107, 105, 112, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 83, 107, 105, 112, 84, 111, 78, 101, 120, 116, 82, 111, 119, 32, 47, 32, 83, 107, 105, 112, 80, 97, 115, 116, 76, 97, 115, 116, 82, 111, 119, 32, 47, 32, 83, 107, 105, 112, 84, 111, 70, 105, 114, 115, 116, 86, 97, 114, 32, 47, 32, 83, 107, 105, 112, 84, 111, 76, 97, 115, 116, 86, 97, 114, 32, 47, 32, 83, 107, 105, 112, 84, 111, 86, 97, 114, 10, + 83, 107, 105, 112, 84, 111, 78, 101, 120, 116, 82, 111, 119, 32, 60, 45, 32, 39, 84, 79, 39, 32, 39, 78, 69, 88, 84, 39, 32, 39, 82, 79, 87, 39, 10, + 83, 107, 105, 112, 80, 97, 115, 116, 76, 97, 115, 116, 82, 111, 119, 32, 60, 45, 32, 39, 80, 65, 83, 84, 39, 32, 39, 76, 65, 83, 84, 39, 32, 39, 82, 79, 87, 39, 10, + 83, 107, 105, 112, 84, 111, 70, 105, 114, 115, 116, 86, 97, 114, 32, 60, 45, 32, 39, 84, 79, 39, 32, 39, 70, 73, 82, 83, 84, 39, 32, 67, 111, 108, 76, 97, 98, 101, 108, 79, 114, 83, 116, 114, 105, 110, 103, 10, + 83, 107, 105, 112, 84, 111, 76, 97, 115, 116, 86, 97, 114, 32, 60, 45, 32, 39, 84, 79, 39, 32, 39, 76, 65, 83, 84, 39, 32, 67, 111, 108, 76, 97, 98, 101, 108, 79, 114, 83, 116, 114, 105, 110, 103, 10, + 35, 32, 110, 97, 109, 105, 110, 103, 32, 97, 32, 118, 97, 114, 105, 97, 98, 108, 101, 32, 119, 105, 116, 104, 111, 117, 116, 32, 115, 97, 121, 105, 110, 103, 32, 119, 104, 105, 99, 104, 32, 111, 102, 32, 105, 116, 115, 32, 114, 111, 119, 115, 32, 109, 101, 97, 110, 115, 32, 105, 116, 115, 32, 108, 97, 115, 116, 32, 111, 110, 101, 10, + 83, 107, 105, 112, 84, 111, 86, 97, 114, 32, 60, 45, 32, 39, 84, 79, 39, 32, 67, 111, 108, 76, 97, 98, 101, 108, 79, 114, 83, 116, 114, 105, 110, 103, 10, + 80, 97, 116, 116, 101, 114, 110, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 80, 65, 84, 84, 69, 82, 78, 39, 32, 80, 97, 114, 101, 110, 115, 40, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 41, 10, + 35, 32, 97, 108, 116, 101, 114, 110, 97, 116, 105, 111, 110, 32, 98, 105, 110, 100, 115, 32, 108, 111, 111, 115, 101, 115, 116, 44, 32, 116, 104, 101, 110, 32, 99, 111, 110, 99, 97, 116, 101, 110, 97, 116, 105, 111, 110, 44, 32, 116, 104, 101, 110, 32, 113, 117, 97, 110, 116, 105, 102, 105, 101, 114, 115, 10, + 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 32, 60, 45, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 84, 101, 114, 109, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 65, 108, 116, 101, 114, 110, 97, 116, 105, 118, 101, 42, 10, + 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 65, 108, 116, 101, 114, 110, 97, 116, 105, 118, 101, 32, 60, 45, 32, 39, 124, 39, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 84, 101, 114, 109, 10, + 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 84, 101, 114, 109, 32, 60, 45, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 70, 97, 99, 116, 111, 114, 43, 10, + 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 70, 97, 99, 116, 111, 114, 32, 60, 45, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 80, 114, 105, 109, 97, 114, 121, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 63, 10, + 35, 32, 80, 69, 82, 77, 85, 84, 69, 32, 104, 97, 115, 32, 116, 111, 32, 98, 101, 32, 116, 114, 105, 101, 100, 32, 98, 101, 102, 111, 114, 101, 32, 97, 32, 112, 108, 97, 105, 110, 32, 108, 97, 98, 101, 108, 44, 32, 119, 104, 105, 99, 104, 32, 119, 111, 117, 108, 100, 32, 111, 116, 104, 101, 114, 119, 105, 115, 101, 32, 99, 111, 110, 115, 117, 109, 101, 32, 116, 104, 101, 32, 107, 101, 121, 119, 111, 114, 100, 10, + 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 80, 114, 105, 109, 97, 114, 121, 32, 60, 45, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 80, 101, 114, 109, 117, 116, 101, 32, 47, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 69, 120, 99, 108, 117, 115, 105, 111, 110, 32, 47, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 69, 109, 112, 116, 121, 32, 47, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 71, 114, 111, 117, 112, 32, 47, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 65, 110, 99, 104, 111, 114, 32, 47, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 76, 97, 98, 101, 108, 10, + 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 80, 101, 114, 109, 117, 116, 101, 32, 60, 45, 32, 39, 80, 69, 82, 77, 85, 84, 69, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 41, 41, 10, + 35, 32, 94, 32, 97, 110, 100, 32, 36, 32, 104, 111, 108, 100, 32, 97, 116, 32, 116, 104, 101, 32, 101, 110, 100, 115, 32, 111, 102, 32, 116, 104, 101, 32, 112, 97, 114, 116, 105, 116, 105, 111, 110, 32, 119, 105, 116, 104, 111, 117, 116, 32, 116, 97, 107, 105, 110, 103, 32, 97, 32, 114, 111, 119, 10, + 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 65, 110, 99, 104, 111, 114, 32, 60, 45, 32, 80, 97, 116, 116, 101, 114, 110, 83, 116, 97, 114, 116, 32, 47, 32, 80, 97, 116, 116, 101, 114, 110, 69, 110, 100, 10, + 80, 97, 116, 116, 101, 114, 110, 83, 116, 97, 114, 116, 32, 60, 45, 32, 39, 94, 39, 10, + 80, 97, 116, 116, 101, 114, 110, 69, 110, 100, 32, 60, 45, 32, 39, 36, 39, 10, + 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 71, 114, 111, 117, 112, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 41, 10, + 35, 32, 40, 41, 32, 109, 97, 116, 99, 104, 101, 115, 32, 119, 104, 101, 114, 101, 32, 105, 116, 32, 115, 116, 97, 110, 100, 115, 32, 119, 105, 116, 104, 111, 117, 116, 32, 116, 97, 107, 105, 110, 103, 32, 97, 32, 114, 111, 119, 10, + 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 69, 109, 112, 116, 121, 32, 60, 45, 32, 39, 40, 39, 32, 39, 41, 39, 10, + 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 69, 120, 99, 108, 117, 115, 105, 111, 110, 32, 60, 45, 32, 39, 123, 39, 32, 39, 45, 39, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 32, 39, 45, 39, 32, 39, 125, 39, 10, + 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 76, 97, 98, 101, 108, 32, 60, 45, 32, 67, 111, 108, 76, 97, 98, 101, 108, 79, 114, 83, 116, 114, 105, 110, 103, 10, + 35, 32, 97, 32, 116, 114, 97, 105, 108, 105, 110, 103, 32, 63, 32, 109, 97, 107, 101, 115, 32, 116, 104, 101, 32, 113, 117, 97, 110, 116, 105, 102, 105, 101, 114, 32, 114, 101, 108, 117, 99, 116, 97, 110, 116, 58, 32, 105, 116, 32, 112, 114, 101, 102, 101, 114, 115, 32, 116, 104, 101, 32, 102, 101, 119, 101, 115, 116, 32, 114, 101, 112, 101, 116, 105, 116, 105, 111, 110, 115, 32, 114, 97, 116, 104, 101, 114, 32, 116, 104, 97, 110, 32, 116, 104, 101, 32, 109, 111, 115, 116, 10, + 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 75, 105, 110, 100, 32, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 82, 101, 108, 117, 99, 116, 97, 110, 116, 63, 10, + 82, 111, 119, 80, 97, 116, 116, 101, 114, 110, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 75, 105, 110, 100, 32, 60, 45, 32, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 83, 116, 97, 114, 32, 47, 32, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 80, 108, 117, 115, 32, 47, 32, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 79, 112, 116, 105, 111, 110, 97, 108, 32, 47, 32, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 82, 97, 110, 103, 101, 10, + 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 82, 101, 108, 117, 99, 116, 97, 110, 116, 32, 60, 45, 32, 39, 63, 39, 10, + 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 83, 116, 97, 114, 32, 60, 45, 32, 39, 42, 39, 10, + 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 80, 108, 117, 115, 32, 60, 45, 32, 39, 43, 39, 10, + 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 79, 112, 116, 105, 111, 110, 97, 108, 32, 60, 45, 32, 39, 63, 39, 10, + 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 82, 97, 110, 103, 101, 32, 60, 45, 32, 39, 123, 39, 32, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 66, 111, 117, 110, 100, 115, 32, 39, 125, 39, 10, + 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 66, 111, 117, 110, 100, 115, 32, 60, 45, 32, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 77, 105, 110, 77, 97, 120, 32, 47, 32, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 77, 105, 110, 32, 47, 32, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 77, 97, 120, 32, 47, 32, 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 69, 120, 97, 99, 116, 10, + 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 77, 105, 110, 77, 97, 120, 32, 60, 45, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 32, 39, 44, 39, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 10, + 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 77, 105, 110, 32, 60, 45, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 32, 39, 44, 39, 10, + 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 77, 97, 120, 32, 60, 45, 32, 39, 44, 39, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 10, + 81, 117, 97, 110, 116, 105, 102, 105, 101, 114, 69, 120, 97, 99, 116, 32, 60, 45, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 10, + 83, 117, 98, 115, 101, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 83, 85, 66, 83, 69, 84, 39, 32, 76, 105, 115, 116, 40, 83, 117, 98, 115, 101, 116, 69, 108, 101, 109, 101, 110, 116, 41, 10, + 83, 117, 98, 115, 101, 116, 69, 108, 101, 109, 101, 110, 116, 32, 60, 45, 32, 67, 111, 108, 76, 97, 98, 101, 108, 79, 114, 83, 116, 114, 105, 110, 103, 32, 39, 61, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 67, 111, 108, 76, 97, 98, 101, 108, 79, 114, 83, 116, 114, 105, 110, 103, 41, 41, 10, + 68, 101, 102, 105, 110, 101, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 40, 39, 68, 69, 70, 73, 78, 69, 39, 32, 47, 32, 39, 68, 69, 70, 73, 78, 69, 83, 39, 41, 32, 76, 105, 115, 116, 40, 68, 101, 102, 105, 110, 101, 69, 108, 101, 109, 101, 110, 116, 41, 10, + 68, 101, 102, 105, 110, 101, 69, 108, 101, 109, 101, 110, 116, 32, 60, 45, 32, 67, 111, 108, 76, 97, 98, 101, 108, 79, 114, 83, 116, 114, 105, 110, 103, 32, 39, 65, 83, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 77, 101, 114, 103, 101, 73, 110, 116, 111, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 87, 105, 116, 104, 67, 108, 97, 117, 115, 101, 63, 32, 39, 77, 69, 82, 71, 69, 39, 32, 39, 73, 78, 84, 79, 39, 32, 84, 97, 114, 103, 101, 116, 79, 112, 116, 65, 108, 105, 97, 115, 32, 77, 101, 114, 103, 101, 73, 110, 116, 111, 85, 115, 105, 110, 103, 67, 108, 97, 117, 115, 101, 32, 74, 111, 105, 110, 81, 117, 97, 108, 105, 102, 105, 101, 114, 32, 77, 101, 114, 103, 101, 77, 97, 116, 99, 104, 43, 32, 82, 101, 116, 117, 114, 110, 105, 110, 103, 67, 108, 97, 117, 115, 101, 63, 10, + 77, 101, 114, 103, 101, 73, 110, 116, 111, 85, 115, 105, 110, 103, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 85, 83, 73, 78, 71, 39, 32, 84, 97, 98, 108, 101, 82, 101, 102, 10, + 77, 101, 114, 103, 101, 77, 97, 116, 99, 104, 32, 60, 45, 32, 77, 97, 116, 99, 104, 101, 100, 67, 108, 97, 117, 115, 101, 32, 47, 32, 78, 111, 116, 77, 97, 116, 99, 104, 101, 100, 67, 108, 97, 117, 115, 101, 10, + 77, 97, 116, 99, 104, 101, 100, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 87, 72, 69, 78, 39, 32, 39, 77, 65, 84, 67, 72, 69, 68, 39, 32, 65, 110, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 32, 39, 84, 72, 69, 78, 39, 32, 77, 97, 116, 99, 104, 101, 100, 67, 108, 97, 117, 115, 101, 65, 99, 116, 105, 111, 110, 10, + 77, 97, 116, 99, 104, 101, 100, 67, 108, 97, 117, 115, 101, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 85, 112, 100, 97, 116, 101, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 47, 32, 68, 101, 108, 101, 116, 101, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 47, 32, 73, 110, 115, 101, 114, 116, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 47, 32, 68, 111, 78, 111, 116, 104, 105, 110, 103, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 47, 32, 69, 114, 114, 111, 114, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, 101, 10, + 85, 112, 100, 97, 116, 101, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 85, 80, 68, 65, 84, 69, 39, 32, 85, 112, 100, 97, 116, 101, 77, 97, 116, 99, 104, 73, 110, 102, 111, 63, 10, + 85, 112, 100, 97, 116, 101, 77, 97, 116, 99, 104, 73, 110, 102, 111, 32, 60, 45, 32, 85, 112, 100, 97, 116, 101, 77, 97, 116, 99, 104, 83, 101, 116, 65, 99, 116, 105, 111, 110, 32, 47, 32, 85, 112, 100, 97, 116, 101, 66, 121, 78, 97, 109, 101, 79, 114, 80, 111, 115, 105, 116, 105, 111, 110, 10, + 85, 112, 100, 97, 116, 101, 77, 97, 116, 99, 104, 83, 101, 116, 65, 99, 116, 105, 111, 110, 32, 60, 45, 32, 85, 112, 100, 97, 116, 101, 77, 97, 116, 99, 104, 83, 101, 116, 67, 108, 97, 117, 115, 101, 10, + 85, 112, 100, 97, 116, 101, 66, 121, 78, 97, 109, 101, 79, 114, 80, 111, 115, 105, 116, 105, 111, 110, 32, 60, 45, 32, 66, 121, 78, 97, 109, 101, 79, 114, 80, 111, 115, 105, 116, 105, 111, 110, 10, + 68, 101, 108, 101, 116, 101, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 68, 69, 76, 69, 84, 69, 39, 10, + 73, 110, 115, 101, 114, 116, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 73, 78, 83, 69, 82, 84, 39, 32, 73, 110, 115, 101, 114, 116, 77, 97, 116, 99, 104, 73, 110, 102, 111, 63, 10, + 73, 110, 115, 101, 114, 116, 77, 97, 116, 99, 104, 73, 110, 102, 111, 32, 60, 45, 32, 73, 110, 115, 101, 114, 116, 86, 97, 108, 117, 101, 115, 76, 105, 115, 116, 32, 47, 32, 73, 110, 115, 101, 114, 116, 68, 101, 102, 97, 117, 108, 116, 86, 97, 108, 117, 101, 115, 32, 47, 32, 73, 110, 115, 101, 114, 116, 66, 121, 78, 97, 109, 101, 79, 114, 80, 111, 115, 105, 116, 105, 111, 110, 10, + 73, 110, 115, 101, 114, 116, 68, 101, 102, 97, 117, 108, 116, 86, 97, 108, 117, 101, 115, 32, 60, 45, 32, 39, 68, 69, 70, 65, 85, 76, 84, 39, 32, 39, 86, 65, 76, 85, 69, 83, 39, 10, + 73, 110, 115, 101, 114, 116, 66, 121, 78, 97, 109, 101, 79, 114, 80, 111, 115, 105, 116, 105, 111, 110, 32, 60, 45, 32, 66, 121, 78, 97, 109, 101, 79, 114, 80, 111, 115, 105, 116, 105, 111, 110, 63, 32, 39, 42, 39, 63, 10, + 73, 110, 115, 101, 114, 116, 86, 97, 108, 117, 101, 115, 76, 105, 115, 116, 32, 60, 45, 32, 73, 110, 115, 101, 114, 116, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 63, 32, 39, 86, 65, 76, 85, 69, 83, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 41, 10, + 68, 111, 78, 111, 116, 104, 105, 110, 103, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 68, 79, 39, 32, 39, 78, 79, 84, 72, 73, 78, 71, 39, 10, + 69, 114, 114, 111, 114, 77, 97, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 69, 82, 82, 79, 82, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 10, + 85, 112, 100, 97, 116, 101, 77, 97, 116, 99, 104, 83, 101, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 83, 69, 84, 39, 32, 85, 112, 100, 97, 116, 101, 77, 97, 116, 99, 104, 83, 101, 116, 73, 110, 102, 111, 10, + 85, 112, 100, 97, 116, 101, 77, 97, 116, 99, 104, 83, 101, 116, 73, 110, 102, 111, 32, 60, 45, 32, 85, 112, 100, 97, 116, 101, 83, 101, 116, 67, 108, 97, 117, 115, 101, 76, 105, 115, 116, 32, 47, 32, 83, 116, 97, 114, 83, 121, 109, 98, 111, 108, 10, + 65, 110, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 39, 65, 78, 68, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 78, 111, 116, 77, 97, 116, 99, 104, 101, 100, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 87, 72, 69, 78, 39, 32, 39, 78, 79, 84, 39, 32, 39, 77, 65, 84, 67, 72, 69, 68, 39, 32, 66, 121, 83, 111, 117, 114, 99, 101, 79, 114, 84, 97, 114, 103, 101, 116, 63, 32, 65, 110, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 32, 39, 84, 72, 69, 78, 39, 32, 77, 97, 116, 99, 104, 101, 100, 67, 108, 97, 117, 115, 101, 65, 99, 116, 105, 111, 110, 10, + 66, 121, 83, 111, 117, 114, 99, 101, 79, 114, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 66, 121, 83, 111, 117, 114, 99, 101, 32, 47, 32, 66, 121, 84, 97, 114, 103, 101, 116, 10, + 66, 121, 83, 111, 117, 114, 99, 101, 32, 60, 45, 32, 39, 66, 89, 39, 32, 39, 83, 79, 85, 82, 67, 69, 39, 10, + 66, 121, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 39, 66, 89, 39, 32, 39, 84, 65, 82, 71, 69, 84, 39, 10, + 80, 105, 118, 111, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 80, 105, 118, 111, 116, 75, 101, 121, 119, 111, 114, 100, 32, 84, 97, 98, 108, 101, 82, 101, 102, 32, 80, 105, 118, 111, 116, 79, 110, 63, 32, 80, 105, 118, 111, 116, 85, 115, 105, 110, 103, 63, 32, 80, 105, 118, 111, 116, 71, 114, 111, 117, 112, 66, 121, 76, 105, 115, 116, 63, 10, + 80, 105, 118, 111, 116, 79, 110, 32, 60, 45, 32, 39, 79, 78, 39, 32, 80, 105, 118, 111, 116, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 10, + 80, 105, 118, 111, 116, 85, 115, 105, 110, 103, 32, 60, 45, 32, 39, 85, 83, 73, 78, 71, 39, 32, 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, 10, + 80, 105, 118, 111, 116, 67, 111, 108, 117, 109, 110, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 80, 105, 118, 111, 116, 67, 111, 108, 117, 109, 110, 69, 110, 116, 114, 121, 41, 10, + 80, 105, 118, 111, 116, 67, 111, 108, 117, 109, 110, 69, 110, 116, 114, 121, 32, 60, 45, 32, 80, 105, 118, 111, 116, 67, 111, 108, 117, 109, 110, 83, 117, 98, 113, 117, 101, 114, 121, 32, 47, 32, 80, 105, 118, 111, 116, 86, 97, 108, 117, 101, 76, 105, 115, 116, 32, 47, 32, 80, 105, 118, 111, 116, 67, 111, 108, 117, 109, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 80, 105, 118, 111, 116, 67, 111, 108, 117, 109, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 80, 105, 118, 111, 116, 67, 111, 108, 117, 109, 110, 83, 117, 98, 113, 117, 101, 114, 121, 32, 60, 45, 32, 66, 97, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 73, 78, 39, 32, 80, 97, 114, 101, 110, 115, 40, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 41, 10, + 80, 105, 118, 111, 116, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 80, 73, 86, 79, 84, 39, 32, 47, 32, 39, 80, 73, 86, 79, 84, 95, 87, 73, 68, 69, 82, 39, 10, + 85, 110, 112, 105, 118, 111, 116, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 85, 78, 80, 73, 86, 79, 84, 39, 32, 47, 32, 39, 80, 73, 86, 79, 84, 95, 76, 79, 78, 71, 69, 82, 39, 10, + 85, 110, 112, 105, 118, 111, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 85, 110, 112, 105, 118, 111, 116, 75, 101, 121, 119, 111, 114, 100, 32, 84, 97, 98, 108, 101, 82, 101, 102, 32, 39, 79, 78, 39, 32, 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, 32, 73, 110, 116, 111, 78, 97, 109, 101, 86, 97, 108, 117, 101, 115, 63, 10, + 73, 110, 116, 111, 78, 97, 109, 101, 86, 97, 108, 117, 101, 115, 32, 60, 45, 32, 39, 73, 78, 84, 79, 39, 32, 39, 78, 65, 77, 69, 39, 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 32, 86, 97, 108, 117, 101, 79, 114, 86, 97, 108, 117, 101, 115, 32, 79, 112, 116, 105, 111, 110, 97, 108, 80, 97, 114, 101, 110, 115, 78, 97, 109, 101, 76, 105, 115, 116, 10, + 86, 97, 108, 117, 101, 79, 114, 86, 97, 108, 117, 101, 115, 32, 60, 45, 32, 39, 86, 65, 76, 85, 69, 39, 32, 47, 32, 39, 86, 65, 76, 85, 69, 83, 39, 10, + 79, 112, 116, 105, 111, 110, 97, 108, 80, 97, 114, 101, 110, 115, 78, 97, 109, 101, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 116, 104, 101, 115, 105, 122, 101, 100, 78, 97, 109, 101, 76, 105, 115, 116, 32, 47, 32, 66, 97, 114, 101, 78, 97, 109, 101, 76, 105, 115, 116, 10, + 80, 97, 114, 101, 110, 116, 104, 101, 115, 105, 122, 101, 100, 78, 97, 109, 101, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 41, 41, 10, + 66, 97, 114, 101, 78, 97, 109, 101, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 41, 10, + 73, 110, 99, 108, 117, 100, 101, 79, 114, 69, 120, 99, 108, 117, 100, 101, 78, 117, 108, 108, 115, 32, 60, 45, 32, 73, 110, 99, 108, 117, 100, 101, 78, 117, 108, 108, 115, 32, 47, 32, 69, 120, 99, 108, 117, 100, 101, 78, 117, 108, 108, 115, 10, + 73, 110, 99, 108, 117, 100, 101, 78, 117, 108, 108, 115, 32, 60, 45, 32, 39, 73, 78, 67, 76, 85, 68, 69, 39, 32, 39, 78, 85, 76, 76, 83, 39, 10, + 69, 120, 99, 108, 117, 100, 101, 78, 117, 108, 108, 115, 32, 60, 45, 32, 39, 69, 88, 67, 76, 85, 68, 69, 39, 32, 39, 78, 85, 76, 76, 83, 39, 10, + 85, 110, 112, 105, 118, 111, 116, 72, 101, 97, 100, 101, 114, 32, 60, 45, 32, 85, 110, 112, 105, 118, 111, 116, 72, 101, 97, 100, 101, 114, 83, 105, 110, 103, 108, 101, 32, 47, 32, 85, 110, 112, 105, 118, 111, 116, 72, 101, 97, 100, 101, 114, 76, 105, 115, 116, 10, + 85, 110, 112, 105, 118, 111, 116, 72, 101, 97, 100, 101, 114, 83, 105, 110, 103, 108, 101, 32, 60, 45, 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 10, + 85, 110, 112, 105, 118, 111, 116, 72, 101, 97, 100, 101, 114, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 41, 41, 10, + 80, 114, 97, 103, 109, 97, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 80, 82, 65, 71, 77, 65, 39, 32, 80, 114, 97, 103, 109, 97, 65, 115, 115, 105, 103, 110, 79, 114, 70, 117, 110, 99, 116, 105, 111, 110, 10, + 80, 114, 97, 103, 109, 97, 65, 115, 115, 105, 103, 110, 79, 114, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 80, 114, 97, 103, 109, 97, 65, 115, 115, 105, 103, 110, 32, 47, 32, 80, 114, 97, 103, 109, 97, 70, 117, 110, 99, 116, 105, 111, 110, 10, + 80, 114, 97, 103, 109, 97, 65, 115, 115, 105, 103, 110, 32, 60, 45, 32, 83, 101, 116, 116, 105, 110, 103, 78, 97, 109, 101, 32, 39, 61, 39, 32, 86, 97, 114, 105, 97, 98, 108, 101, 76, 105, 115, 116, 10, + 80, 114, 97, 103, 109, 97, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 80, 114, 97, 103, 109, 97, 78, 97, 109, 101, 32, 80, 114, 97, 103, 109, 97, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 63, 10, + 80, 114, 97, 103, 109, 97, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 41, 10, + 80, 114, 101, 112, 97, 114, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 80, 82, 69, 80, 65, 82, 69, 39, 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 32, 84, 121, 112, 101, 76, 105, 115, 116, 63, 32, 39, 65, 83, 39, 32, 83, 116, 97, 116, 101, 109, 101, 110, 116, 10, + 84, 121, 112, 101, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 84, 121, 112, 101, 41, 41, 10, + 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 10, + 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 32, 60, 45, 32, 87, 105, 116, 104, 67, 108, 97, 117, 115, 101, 63, 32, 83, 101, 108, 101, 99, 116, 83, 101, 116, 79, 112, 67, 104, 97, 105, 110, 32, 82, 101, 115, 117, 108, 116, 77, 111, 100, 105, 102, 105, 101, 114, 115, 63, 10, + 83, 101, 108, 101, 99, 116, 83, 101, 116, 79, 112, 67, 104, 97, 105, 110, 32, 60, 45, 32, 73, 110, 116, 101, 114, 115, 101, 99, 116, 67, 104, 97, 105, 110, 32, 83, 101, 108, 101, 99, 116, 83, 101, 116, 79, 112, 67, 104, 97, 105, 110, 84, 97, 105, 108, 42, 10, + 83, 101, 108, 101, 99, 116, 83, 101, 116, 79, 112, 67, 104, 97, 105, 110, 84, 97, 105, 108, 32, 60, 45, 32, 83, 101, 116, 111, 112, 67, 108, 97, 117, 115, 101, 32, 73, 110, 116, 101, 114, 115, 101, 99, 116, 67, 104, 97, 105, 110, 10, + 73, 110, 116, 101, 114, 115, 101, 99, 116, 67, 104, 97, 105, 110, 32, 60, 45, 32, 83, 101, 108, 101, 99, 116, 65, 116, 111, 109, 32, 73, 110, 116, 101, 114, 115, 101, 99, 116, 67, 104, 97, 105, 110, 84, 97, 105, 108, 42, 10, + 73, 110, 116, 101, 114, 115, 101, 99, 116, 67, 104, 97, 105, 110, 84, 97, 105, 108, 32, 60, 45, 32, 83, 101, 116, 73, 110, 116, 101, 114, 115, 101, 99, 116, 67, 108, 97, 117, 115, 101, 32, 83, 101, 108, 101, 99, 116, 65, 116, 111, 109, 10, + 83, 101, 116, 73, 110, 116, 101, 114, 115, 101, 99, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 73, 78, 84, 69, 82, 83, 69, 67, 84, 39, 32, 68, 105, 115, 116, 105, 110, 99, 116, 79, 114, 65, 108, 108, 63, 10, + 83, 101, 108, 101, 99, 116, 65, 116, 111, 109, 32, 60, 45, 32, 83, 101, 108, 101, 99, 116, 80, 97, 114, 101, 110, 115, 32, 47, 32, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 84, 121, 112, 101, 10, + 83, 101, 108, 101, 99, 116, 80, 97, 114, 101, 110, 115, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 41, 10, + 83, 101, 116, 111, 112, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 83, 101, 116, 111, 112, 84, 121, 112, 101, 32, 68, 105, 115, 116, 105, 110, 99, 116, 79, 114, 65, 108, 108, 63, 32, 66, 121, 78, 97, 109, 101, 63, 10, + 83, 101, 116, 111, 112, 84, 121, 112, 101, 32, 60, 45, 32, 83, 101, 116, 111, 112, 85, 110, 105, 111, 110, 32, 47, 32, 83, 101, 116, 111, 112, 69, 120, 99, 101, 112, 116, 10, + 83, 101, 116, 111, 112, 85, 110, 105, 111, 110, 32, 60, 45, 32, 39, 85, 78, 73, 79, 78, 39, 10, + 83, 101, 116, 111, 112, 69, 120, 99, 101, 112, 116, 32, 60, 45, 32, 39, 69, 88, 67, 69, 80, 84, 39, 10, + 66, 121, 78, 97, 109, 101, 32, 60, 45, 32, 39, 66, 89, 39, 32, 39, 78, 65, 77, 69, 39, 10, + 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 84, 121, 112, 101, 32, 60, 45, 32, 79, 112, 116, 105, 111, 110, 97, 108, 80, 97, 114, 101, 110, 115, 83, 105, 109, 112, 108, 101, 83, 101, 108, 101, 99, 116, 32, 47, 32, 86, 97, 108, 117, 101, 115, 67, 108, 97, 117, 115, 101, 32, 47, 32, 68, 101, 115, 99, 114, 105, 98, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 32, 84, 97, 98, 108, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 32, 80, 105, 118, 111, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 47, 32, 85, 110, 112, 105, 118, 111, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 10, + 82, 101, 115, 117, 108, 116, 77, 111, 100, 105, 102, 105, 101, 114, 115, 32, 60, 45, 32, 79, 114, 100, 101, 114, 66, 121, 67, 108, 97, 117, 115, 101, 63, 32, 76, 105, 109, 105, 116, 79, 102, 102, 115, 101, 116, 63, 10, + 76, 105, 109, 105, 116, 79, 102, 102, 115, 101, 116, 32, 60, 45, 32, 76, 105, 109, 105, 116, 79, 102, 102, 115, 101, 116, 67, 108, 97, 117, 115, 101, 32, 47, 32, 79, 102, 102, 115, 101, 116, 70, 101, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 47, 32, 79, 102, 102, 115, 101, 116, 76, 105, 109, 105, 116, 67, 108, 97, 117, 115, 101, 32, 47, 32, 70, 101, 116, 99, 104, 79, 102, 102, 115, 101, 116, 67, 108, 97, 117, 115, 101, 32, 47, 32, 70, 101, 116, 99, 104, 79, 110, 108, 121, 67, 108, 97, 117, 115, 101, 10, + 76, 105, 109, 105, 116, 79, 102, 102, 115, 101, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 76, 105, 109, 105, 116, 67, 108, 97, 117, 115, 101, 32, 79, 102, 102, 115, 101, 116, 67, 108, 97, 117, 115, 101, 63, 10, + 79, 102, 102, 115, 101, 116, 76, 105, 109, 105, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 79, 102, 102, 115, 101, 116, 67, 108, 97, 117, 115, 101, 32, 76, 105, 109, 105, 116, 67, 108, 97, 117, 115, 101, 63, 10, + 79, 102, 102, 115, 101, 116, 70, 101, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 79, 102, 102, 115, 101, 116, 67, 108, 97, 117, 115, 101, 32, 70, 101, 116, 99, 104, 67, 108, 97, 117, 115, 101, 10, + 70, 101, 116, 99, 104, 79, 102, 102, 115, 101, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 70, 101, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 79, 102, 102, 115, 101, 116, 67, 108, 97, 117, 115, 101, 10, + 70, 101, 116, 99, 104, 79, 110, 108, 121, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 70, 101, 116, 99, 104, 67, 108, 97, 117, 115, 101, 10, + 84, 97, 98, 108, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 84, 65, 66, 76, 69, 39, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 10, + 79, 112, 116, 105, 111, 110, 97, 108, 80, 97, 114, 101, 110, 115, 83, 105, 109, 112, 108, 101, 83, 101, 108, 101, 99, 116, 32, 60, 45, 32, 83, 105, 109, 112, 108, 101, 83, 101, 108, 101, 99, 116, 80, 97, 114, 101, 110, 115, 32, 47, 32, 83, 105, 109, 112, 108, 101, 83, 101, 108, 101, 99, 116, 10, + 83, 105, 109, 112, 108, 101, 83, 101, 108, 101, 99, 116, 80, 97, 114, 101, 110, 115, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 83, 105, 109, 112, 108, 101, 83, 101, 108, 101, 99, 116, 41, 10, + 83, 105, 109, 112, 108, 101, 83, 101, 108, 101, 99, 116, 32, 60, 45, 32, 83, 101, 108, 101, 99, 116, 70, 114, 111, 109, 32, 87, 104, 101, 114, 101, 67, 108, 97, 117, 115, 101, 63, 32, 71, 114, 111, 117, 112, 66, 121, 67, 108, 97, 117, 115, 101, 63, 32, 72, 97, 118, 105, 110, 103, 67, 108, 97, 117, 115, 101, 63, 32, 87, 105, 110, 100, 111, 119, 67, 108, 97, 117, 115, 101, 63, 32, 81, 117, 97, 108, 105, 102, 121, 67, 108, 97, 117, 115, 101, 63, 32, 83, 101, 108, 101, 99, 116, 83, 97, 109, 112, 108, 101, 67, 108, 97, 117, 115, 101, 63, 10, + 83, 101, 108, 101, 99, 116, 70, 114, 111, 109, 32, 60, 45, 32, 83, 101, 108, 101, 99, 116, 70, 114, 111, 109, 67, 108, 97, 117, 115, 101, 32, 47, 32, 70, 114, 111, 109, 83, 101, 108, 101, 99, 116, 67, 108, 97, 117, 115, 101, 10, + 83, 101, 108, 101, 99, 116, 70, 114, 111, 109, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 83, 101, 108, 101, 99, 116, 67, 108, 97, 117, 115, 101, 32, 70, 114, 111, 109, 67, 108, 97, 117, 115, 101, 63, 10, + 70, 114, 111, 109, 83, 101, 108, 101, 99, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 70, 114, 111, 109, 67, 108, 97, 117, 115, 101, 32, 83, 101, 108, 101, 99, 116, 67, 108, 97, 117, 115, 101, 63, 10, + 87, 105, 116, 104, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 32, 67, 111, 108, 117, 109, 110, 65, 108, 105, 97, 115, 101, 115, 63, 32, 85, 115, 105, 110, 103, 75, 101, 121, 63, 32, 39, 65, 83, 39, 32, 77, 97, 116, 101, 114, 105, 97, 108, 105, 122, 101, 100, 63, 32, 67, 84, 69, 66, 111, 100, 121, 10, + 67, 84, 69, 66, 111, 100, 121, 32, 60, 45, 32, 67, 84, 69, 83, 101, 108, 101, 99, 116, 66, 111, 100, 121, 32, 47, 32, 67, 84, 69, 68, 77, 76, 66, 111, 100, 121, 10, + 67, 84, 69, 83, 101, 108, 101, 99, 116, 66, 111, 100, 121, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 41, 10, + 67, 84, 69, 68, 77, 76, 66, 111, 100, 121, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 83, 116, 97, 116, 101, 109, 101, 110, 116, 41, 10, + 85, 115, 105, 110, 103, 75, 101, 121, 32, 60, 45, 32, 39, 85, 83, 73, 78, 71, 39, 32, 39, 75, 69, 89, 39, 32, 80, 97, 114, 101, 110, 115, 40, 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, 41, 10, + 77, 97, 116, 101, 114, 105, 97, 108, 105, 122, 101, 100, 32, 60, 45, 32, 39, 78, 79, 84, 39, 63, 32, 39, 77, 65, 84, 69, 82, 73, 65, 76, 73, 90, 69, 68, 39, 10, + 87, 105, 116, 104, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 87, 73, 84, 72, 39, 32, 82, 101, 99, 117, 114, 115, 105, 118, 101, 63, 32, 76, 105, 115, 116, 40, 87, 105, 116, 104, 83, 116, 97, 116, 101, 109, 101, 110, 116, 41, 10, + 82, 101, 99, 117, 114, 115, 105, 118, 101, 32, 60, 45, 32, 39, 82, 69, 67, 85, 82, 83, 73, 86, 69, 39, 10, + 83, 101, 108, 101, 99, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 83, 69, 76, 69, 67, 84, 39, 32, 68, 105, 115, 116, 105, 110, 99, 116, 67, 108, 97, 117, 115, 101, 63, 32, 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, 63, 10, + 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 65, 108, 105, 97, 115, 101, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, + 67, 111, 108, 117, 109, 110, 65, 108, 105, 97, 115, 101, 115, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 41, 41, 10, + 68, 105, 115, 116, 105, 110, 99, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 68, 105, 115, 116, 105, 110, 99, 116, 79, 110, 32, 47, 32, 68, 105, 115, 116, 105, 110, 99, 116, 65, 108, 108, 10, + 68, 105, 115, 116, 105, 110, 99, 116, 65, 108, 108, 32, 60, 45, 32, 39, 65, 76, 76, 39, 10, + 68, 105, 115, 116, 105, 110, 99, 116, 79, 110, 32, 60, 45, 32, 39, 68, 73, 83, 84, 73, 78, 67, 84, 39, 32, 68, 105, 115, 116, 105, 110, 99, 116, 79, 110, 84, 97, 114, 103, 101, 116, 115, 63, 10, + 68, 105, 115, 116, 105, 110, 99, 116, 79, 110, 84, 97, 114, 103, 101, 116, 115, 32, 60, 45, 32, 39, 79, 78, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 41, 10, + 73, 110, 110, 101, 114, 84, 97, 98, 108, 101, 82, 101, 102, 32, 60, 45, 32, 86, 97, 108, 117, 101, 115, 82, 101, 102, 32, 47, 32, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 47, 32, 84, 97, 98, 108, 101, 83, 117, 98, 113, 117, 101, 114, 121, 32, 47, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 82, 101, 102, 32, 47, 32, 80, 97, 114, 101, 110, 115, 84, 97, 98, 108, 101, 82, 101, 102, 10, + 84, 97, 98, 108, 101, 82, 101, 102, 32, 60, 45, 32, 73, 110, 110, 101, 114, 84, 97, 98, 108, 101, 82, 101, 102, 32, 74, 111, 105, 110, 79, 114, 80, 105, 118, 111, 116, 42, 10, + 84, 97, 98, 108, 101, 83, 117, 98, 113, 117, 101, 114, 121, 32, 60, 45, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 67, 111, 108, 111, 110, 63, 32, 76, 97, 116, 101, 114, 97, 108, 63, 32, 83, 117, 98, 113, 117, 101, 114, 121, 82, 101, 102, 101, 114, 101, 110, 99, 101, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 32, 83, 97, 109, 112, 108, 101, 67, 108, 97, 117, 115, 101, 63, 10, + 66, 97, 115, 101, 84, 97, 98, 108, 101, 82, 101, 102, 32, 60, 45, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 67, 111, 108, 111, 110, 63, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 32, 65, 116, 67, 108, 97, 117, 115, 101, 63, 32, 83, 97, 109, 112, 108, 101, 67, 108, 97, 117, 115, 101, 63, 10, + 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 67, 111, 108, 111, 110, 32, 60, 45, 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 32, 39, 58, 39, 10, + 86, 97, 108, 117, 101, 115, 82, 101, 102, 32, 60, 45, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 67, 111, 108, 111, 110, 63, 32, 86, 97, 108, 117, 101, 115, 67, 108, 97, 117, 115, 101, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 10, + 80, 97, 114, 101, 110, 115, 84, 97, 98, 108, 101, 82, 101, 102, 32, 60, 45, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 67, 111, 108, 111, 110, 63, 32, 80, 97, 114, 101, 110, 115, 40, 84, 97, 98, 108, 101, 82, 101, 102, 41, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 32, 83, 97, 109, 112, 108, 101, 67, 108, 97, 117, 115, 101, 63, 10, + 74, 111, 105, 110, 79, 114, 80, 105, 118, 111, 116, 32, 60, 45, 32, 74, 111, 105, 110, 67, 108, 97, 117, 115, 101, 32, 47, 32, 84, 97, 98, 108, 101, 80, 105, 118, 111, 116, 67, 108, 97, 117, 115, 101, 32, 47, 32, 84, 97, 98, 108, 101, 85, 110, 112, 105, 118, 111, 116, 67, 108, 97, 117, 115, 101, 32, 47, 32, 84, 97, 98, 108, 101, 77, 97, 116, 99, 104, 82, 101, 99, 111, 103, 110, 105, 122, 101, 67, 108, 97, 117, 115, 101, 10, + 84, 97, 98, 108, 101, 80, 105, 118, 111, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 80, 73, 86, 79, 84, 39, 32, 80, 97, 114, 101, 110, 115, 40, 84, 97, 98, 108, 101, 80, 105, 118, 111, 116, 67, 108, 97, 117, 115, 101, 66, 111, 100, 121, 41, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 10, + 84, 97, 98, 108, 101, 80, 105, 118, 111, 116, 67, 108, 97, 117, 115, 101, 66, 111, 100, 121, 32, 60, 45, 32, 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, 32, 39, 70, 79, 82, 39, 32, 80, 105, 118, 111, 116, 86, 97, 108, 117, 101, 76, 105, 115, 116, 43, 32, 80, 105, 118, 111, 116, 71, 114, 111, 117, 112, 66, 121, 76, 105, 115, 116, 63, 10, + 80, 105, 118, 111, 116, 71, 114, 111, 117, 112, 66, 121, 76, 105, 115, 116, 32, 60, 45, 32, 39, 71, 82, 79, 85, 80, 39, 32, 39, 66, 89, 39, 32, 79, 112, 116, 105, 111, 110, 97, 108, 80, 97, 114, 101, 110, 115, 78, 97, 109, 101, 76, 105, 115, 116, 10, + 84, 97, 98, 108, 101, 85, 110, 112, 105, 118, 111, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 85, 78, 80, 73, 86, 79, 84, 39, 32, 73, 110, 99, 108, 117, 100, 101, 79, 114, 69, 120, 99, 108, 117, 100, 101, 78, 117, 108, 108, 115, 63, 32, 80, 97, 114, 101, 110, 115, 40, 84, 97, 98, 108, 101, 85, 110, 112, 105, 118, 111, 116, 67, 108, 97, 117, 115, 101, 66, 111, 100, 121, 41, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 10, + 84, 97, 98, 108, 101, 85, 110, 112, 105, 118, 111, 116, 67, 108, 97, 117, 115, 101, 66, 111, 100, 121, 32, 60, 45, 32, 85, 110, 112, 105, 118, 111, 116, 72, 101, 97, 100, 101, 114, 32, 39, 70, 79, 82, 39, 32, 85, 110, 112, 105, 118, 111, 116, 86, 97, 108, 117, 101, 76, 105, 115, 116, 43, 10, + 80, 105, 118, 111, 116, 72, 101, 97, 100, 101, 114, 32, 60, 45, 32, 66, 97, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 80, 105, 118, 111, 116, 86, 97, 108, 117, 101, 76, 105, 115, 116, 32, 60, 45, 32, 80, 105, 118, 111, 116, 72, 101, 97, 100, 101, 114, 32, 39, 73, 78, 39, 32, 80, 105, 118, 111, 116, 86, 97, 108, 117, 101, 84, 97, 114, 103, 101, 116, 10, + 80, 105, 118, 111, 116, 86, 97, 108, 117, 101, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 80, 105, 118, 111, 116, 69, 110, 117, 109, 84, 97, 114, 103, 101, 116, 32, 47, 32, 80, 105, 118, 111, 116, 76, 105, 115, 116, 84, 97, 114, 103, 101, 116, 10, + 80, 105, 118, 111, 116, 69, 110, 117, 109, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 80, 105, 118, 111, 116, 76, 105, 115, 116, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 80, 105, 118, 111, 116, 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, 10, + 85, 110, 112, 105, 118, 111, 116, 86, 97, 108, 117, 101, 76, 105, 115, 116, 32, 60, 45, 32, 85, 110, 112, 105, 118, 111, 116, 72, 101, 97, 100, 101, 114, 32, 39, 73, 78, 39, 32, 85, 110, 112, 105, 118, 111, 116, 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, 10, + 80, 105, 118, 111, 116, 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, 41, 10, + 85, 110, 112, 105, 118, 111, 116, 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 84, 97, 114, 103, 101, 116, 76, 105, 115, 116, 41, 10, + 76, 97, 116, 101, 114, 97, 108, 32, 60, 45, 32, 39, 76, 65, 84, 69, 82, 65, 76, 39, 10, + 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 60, 45, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 47, 32, 85, 110, 113, 117, 97, 108, 105, 102, 105, 101, 100, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 10, + 85, 110, 113, 117, 97, 108, 105, 102, 105, 101, 100, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 60, 45, 32, 84, 97, 98, 108, 101, 78, 97, 109, 101, 10, + 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 84, 97, 98, 108, 101, 32, 47, 32, 83, 99, 104, 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 84, 97, 98, 108, 101, 10, + 83, 99, 104, 101, 109, 97, 82, 101, 115, 101, 114, 118, 101, 100, 84, 97, 98, 108, 101, 32, 60, 45, 32, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 84, 97, 98, 108, 101, 78, 97, 109, 101, 10, + 67, 97, 116, 97, 108, 111, 103, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 84, 97, 98, 108, 101, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 43, 32, 82, 101, 115, 101, 114, 118, 101, 100, 84, 97, 98, 108, 101, 78, 97, 109, 101, 10, + 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 76, 97, 116, 101, 114, 97, 108, 79, 112, 116, 32, 47, 32, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 65, 108, 105, 97, 115, 67, 111, 108, 111, 110, 10, + 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 76, 97, 116, 101, 114, 97, 108, 79, 112, 116, 32, 60, 45, 32, 76, 97, 116, 101, 114, 97, 108, 63, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 87, 105, 116, 104, 79, 114, 100, 105, 110, 97, 108, 105, 116, 121, 63, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 32, 83, 97, 109, 112, 108, 101, 67, 108, 97, 117, 115, 101, 63, 10, + 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 65, 108, 105, 97, 115, 67, 111, 108, 111, 110, 32, 60, 45, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 67, 111, 108, 111, 110, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 87, 105, 116, 104, 79, 114, 100, 105, 110, 97, 108, 105, 116, 121, 63, 32, 83, 97, 109, 112, 108, 101, 67, 108, 97, 117, 115, 101, 63, 10, + 87, 105, 116, 104, 79, 114, 100, 105, 110, 97, 108, 105, 116, 121, 32, 60, 45, 32, 39, 87, 73, 84, 72, 39, 32, 39, 79, 82, 68, 73, 78, 65, 76, 73, 84, 89, 39, 10, + 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 63, 32, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 42, 32, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 10, + 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 41, 63, 41, 10, + 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 32, 60, 45, 32, 78, 97, 109, 101, 100, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 32, 47, 32, 80, 111, 115, 105, 116, 105, 111, 110, 97, 108, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 10, + 78, 97, 109, 101, 100, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 32, 60, 45, 32, 78, 97, 109, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 10, + 80, 111, 115, 105, 116, 105, 111, 110, 97, 108, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 78, 97, 109, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 60, 45, 32, 84, 121, 112, 101, 70, 117, 110, 99, 78, 97, 109, 101, 32, 84, 121, 112, 101, 63, 32, 78, 97, 109, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 65, 115, 115, 105, 103, 110, 109, 101, 110, 116, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 78, 97, 109, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 65, 115, 115, 105, 103, 110, 109, 101, 110, 116, 32, 60, 45, 32, 39, 58, 61, 39, 32, 47, 32, 39, 61, 62, 39, 10, + 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 32, 60, 45, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 65, 115, 32, 47, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 87, 105, 116, 104, 111, 117, 116, 65, 115, 10, + 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 65, 115, 32, 60, 45, 32, 39, 65, 83, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 79, 114, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 67, 111, 108, 117, 109, 110, 65, 108, 105, 97, 115, 101, 115, 63, 10, + 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 87, 105, 116, 104, 111, 117, 116, 65, 115, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 67, 111, 108, 117, 109, 110, 65, 108, 105, 97, 115, 101, 115, 63, 10, + 65, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 65, 84, 39, 32, 80, 97, 114, 101, 110, 115, 40, 65, 116, 83, 112, 101, 99, 105, 102, 105, 101, 114, 41, 10, + 65, 116, 83, 112, 101, 99, 105, 102, 105, 101, 114, 32, 60, 45, 32, 65, 116, 85, 110, 105, 116, 32, 39, 61, 62, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 65, 116, 85, 110, 105, 116, 32, 60, 45, 32, 86, 101, 114, 115, 105, 111, 110, 65, 116, 85, 110, 105, 116, 32, 47, 32, 84, 105, 109, 101, 115, 116, 97, 109, 112, 65, 116, 85, 110, 105, 116, 10, + 86, 101, 114, 115, 105, 111, 110, 65, 116, 85, 110, 105, 116, 32, 60, 45, 32, 39, 86, 69, 82, 83, 73, 79, 78, 39, 10, + 84, 105, 109, 101, 115, 116, 97, 109, 112, 65, 116, 85, 110, 105, 116, 32, 60, 45, 32, 39, 84, 73, 77, 69, 83, 84, 65, 77, 80, 39, 10, + 74, 111, 105, 110, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 74, 111, 105, 110, 66, 121, 67, 108, 97, 117, 115, 101, 32, 47, 32, 82, 101, 103, 117, 108, 97, 114, 74, 111, 105, 110, 67, 108, 97, 117, 115, 101, 32, 47, 32, 74, 111, 105, 110, 87, 105, 116, 104, 111, 117, 116, 79, 110, 67, 108, 97, 117, 115, 101, 32, 47, 32, 78, 101, 97, 114, 101, 115, 116, 74, 111, 105, 110, 67, 108, 97, 117, 115, 101, 10, + 35, 32, 78, 69, 65, 82, 69, 83, 84, 32, 66, 89, 32, 104, 97, 115, 32, 116, 119, 111, 32, 97, 108, 116, 101, 114, 110, 97, 116, 105, 118, 101, 115, 32, 115, 111, 32, 80, 69, 71, 32, 99, 97, 110, 32, 98, 97, 99, 107, 116, 114, 97, 99, 107, 58, 32, 116, 104, 101, 32, 98, 97, 114, 101, 32, 102, 111, 114, 109, 32, 117, 115, 101, 115, 32, 97, 108, 105, 97, 115, 45, 108, 101, 115, 115, 32, 116, 97, 114, 103, 101, 116, 32, 109, 105, 114, 114, 111, 114, 115, 32, 40, 116, 114, 105, 101, 100, 10, + 35, 32, 102, 105, 114, 115, 116, 44, 32, 115, 111, 32, 97, 110, 32, 117, 110, 97, 108, 105, 97, 115, 101, 100, 32, 116, 97, 114, 103, 101, 116, 32, 100, 111, 101, 115, 32, 110, 111, 116, 32, 103, 114, 101, 101, 100, 105, 108, 121, 32, 99, 111, 110, 115, 117, 109, 101, 32, 78, 69, 65, 82, 69, 83, 84, 47, 65, 80, 80, 82, 79, 88, 47, 69, 88, 65, 67, 84, 32, 97, 115, 32, 97, 110, 32, 97, 108, 105, 97, 115, 41, 44, 32, 97, 110, 100, 32, 116, 104, 101, 32, 97, 108, 105, 97, 115, 101, 100, 10, + 35, 32, 102, 111, 114, 109, 32, 102, 97, 108, 108, 115, 32, 98, 97, 99, 107, 32, 116, 111, 32, 97, 32, 102, 117, 108, 108, 32, 84, 97, 98, 108, 101, 82, 101, 102, 32, 119, 104, 101, 110, 32, 97, 32, 114, 101, 97, 108, 32, 97, 108, 105, 97, 115, 32, 105, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 102, 111, 108, 108, 111, 119, 115, 32, 116, 104, 101, 32, 116, 97, 114, 103, 101, 116, 46, 10, + 78, 101, 97, 114, 101, 115, 116, 74, 111, 105, 110, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 78, 101, 97, 114, 101, 115, 116, 74, 111, 105, 110, 66, 97, 114, 101, 32, 47, 32, 78, 101, 97, 114, 101, 115, 116, 74, 111, 105, 110, 65, 108, 105, 97, 115, 101, 100, 10, + 78, 101, 97, 114, 101, 115, 116, 74, 111, 105, 110, 65, 108, 105, 97, 115, 101, 100, 32, 60, 45, 32, 74, 111, 105, 110, 84, 121, 112, 101, 63, 32, 39, 74, 79, 73, 78, 39, 32, 84, 97, 98, 108, 101, 82, 101, 102, 32, 65, 112, 112, 114, 111, 120, 79, 114, 69, 120, 97, 99, 116, 63, 32, 39, 78, 69, 65, 82, 69, 83, 84, 39, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 63, 32, 39, 66, 89, 39, 32, 68, 105, 115, 116, 97, 110, 99, 101, 79, 114, 83, 105, 109, 105, 108, 97, 114, 105, 116, 121, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 78, 101, 97, 114, 101, 115, 116, 74, 111, 105, 110, 66, 97, 114, 101, 32, 60, 45, 32, 74, 111, 105, 110, 84, 121, 112, 101, 63, 32, 39, 74, 79, 73, 78, 39, 32, 78, 101, 97, 114, 101, 115, 116, 66, 97, 114, 101, 84, 97, 98, 108, 101, 82, 101, 102, 32, 65, 112, 112, 114, 111, 120, 79, 114, 69, 120, 97, 99, 116, 63, 32, 39, 78, 69, 65, 82, 69, 83, 84, 39, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 63, 32, 39, 66, 89, 39, 32, 68, 105, 115, 116, 97, 110, 99, 101, 79, 114, 83, 105, 109, 105, 108, 97, 114, 105, 116, 121, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 78, 101, 97, 114, 101, 115, 116, 66, 97, 114, 101, 84, 97, 98, 108, 101, 82, 101, 102, 32, 60, 45, 32, 78, 101, 97, 114, 101, 115, 116, 86, 97, 108, 117, 101, 115, 82, 101, 102, 32, 47, 32, 78, 101, 97, 114, 101, 115, 116, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 47, 32, 78, 101, 97, 114, 101, 115, 116, 84, 97, 98, 108, 101, 83, 117, 98, 113, 117, 101, 114, 121, 32, 47, 32, 78, 101, 97, 114, 101, 115, 116, 66, 97, 115, 101, 84, 97, 98, 108, 101, 82, 101, 102, 32, 47, 32, 78, 101, 97, 114, 101, 115, 116, 80, 97, 114, 101, 110, 115, 84, 97, 98, 108, 101, 82, 101, 102, 10, + 78, 101, 97, 114, 101, 115, 116, 86, 97, 108, 117, 101, 115, 82, 101, 102, 32, 60, 45, 32, 86, 97, 108, 117, 101, 115, 67, 108, 97, 117, 115, 101, 10, + 78, 101, 97, 114, 101, 115, 116, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 76, 97, 116, 101, 114, 97, 108, 63, 32, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 87, 105, 116, 104, 79, 114, 100, 105, 110, 97, 108, 105, 116, 121, 63, 10, + 78, 101, 97, 114, 101, 115, 116, 84, 97, 98, 108, 101, 83, 117, 98, 113, 117, 101, 114, 121, 32, 60, 45, 32, 76, 97, 116, 101, 114, 97, 108, 63, 32, 83, 117, 98, 113, 117, 101, 114, 121, 82, 101, 102, 101, 114, 101, 110, 99, 101, 10, + 78, 101, 97, 114, 101, 115, 116, 66, 97, 115, 101, 84, 97, 98, 108, 101, 82, 101, 102, 32, 60, 45, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 65, 116, 67, 108, 97, 117, 115, 101, 63, 32, 83, 97, 109, 112, 108, 101, 67, 108, 97, 117, 115, 101, 63, 10, + 78, 101, 97, 114, 101, 115, 116, 80, 97, 114, 101, 110, 115, 84, 97, 98, 108, 101, 82, 101, 102, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 84, 97, 98, 108, 101, 82, 101, 102, 41, 32, 83, 97, 109, 112, 108, 101, 67, 108, 97, 117, 115, 101, 63, 10, + 65, 112, 112, 114, 111, 120, 79, 114, 69, 120, 97, 99, 116, 32, 60, 45, 32, 78, 101, 97, 114, 101, 115, 116, 65, 112, 112, 114, 111, 120, 32, 47, 32, 78, 101, 97, 114, 101, 115, 116, 69, 120, 97, 99, 116, 10, + 78, 101, 97, 114, 101, 115, 116, 65, 112, 112, 114, 111, 120, 32, 60, 45, 32, 39, 65, 80, 80, 82, 79, 88, 39, 10, + 78, 101, 97, 114, 101, 115, 116, 69, 120, 97, 99, 116, 32, 60, 45, 32, 39, 69, 88, 65, 67, 84, 39, 10, + 68, 105, 115, 116, 97, 110, 99, 101, 79, 114, 83, 105, 109, 105, 108, 97, 114, 105, 116, 121, 32, 60, 45, 32, 78, 101, 97, 114, 101, 115, 116, 68, 105, 115, 116, 97, 110, 99, 101, 32, 47, 32, 78, 101, 97, 114, 101, 115, 116, 83, 105, 109, 105, 108, 97, 114, 105, 116, 121, 10, + 78, 101, 97, 114, 101, 115, 116, 68, 105, 115, 116, 97, 110, 99, 101, 32, 60, 45, 32, 39, 68, 73, 83, 84, 65, 78, 67, 69, 39, 10, + 78, 101, 97, 114, 101, 115, 116, 83, 105, 109, 105, 108, 97, 114, 105, 116, 121, 32, 60, 45, 32, 39, 83, 73, 77, 73, 76, 65, 82, 73, 84, 89, 39, 10, + 82, 101, 103, 117, 108, 97, 114, 74, 111, 105, 110, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 65, 115, 111, 102, 63, 32, 74, 111, 105, 110, 84, 121, 112, 101, 63, 32, 39, 74, 79, 73, 78, 39, 32, 84, 97, 98, 108, 101, 82, 101, 102, 32, 74, 111, 105, 110, 81, 117, 97, 108, 105, 102, 105, 101, 114, 10, + 74, 111, 105, 110, 66, 121, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 74, 79, 73, 78, 39, 32, 39, 66, 89, 39, 32, 80, 97, 114, 101, 110, 115, 40, 39, 84, 89, 80, 69, 39, 32, 67, 111, 108, 76, 97, 98, 101, 108, 41, 32, 84, 97, 98, 108, 101, 82, 101, 102, 32, 74, 111, 105, 110, 81, 117, 97, 108, 105, 102, 105, 101, 114, 10, + 65, 115, 111, 102, 32, 60, 45, 32, 39, 65, 83, 79, 70, 39, 10, + 74, 111, 105, 110, 87, 105, 116, 104, 111, 117, 116, 79, 110, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 74, 111, 105, 110, 80, 114, 101, 102, 105, 120, 32, 39, 74, 79, 73, 78, 39, 32, 73, 110, 110, 101, 114, 84, 97, 98, 108, 101, 82, 101, 102, 10, + 74, 111, 105, 110, 81, 117, 97, 108, 105, 102, 105, 101, 114, 32, 60, 45, 32, 79, 110, 67, 108, 97, 117, 115, 101, 32, 47, 32, 85, 115, 105, 110, 103, 67, 108, 97, 117, 115, 101, 10, + 79, 110, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 79, 78, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 85, 115, 105, 110, 103, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 85, 83, 73, 78, 71, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 85, 115, 105, 110, 103, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 41, 41, 10, + 85, 115, 105, 110, 103, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 60, 45, 32, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 47, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 74, 111, 105, 110, 84, 121, 112, 101, 32, 60, 45, 32, 70, 117, 108, 108, 74, 111, 105, 110, 32, 47, 32, 76, 101, 102, 116, 74, 111, 105, 110, 32, 47, 32, 82, 105, 103, 104, 116, 74, 111, 105, 110, 32, 47, 32, 83, 101, 109, 105, 74, 111, 105, 110, 32, 47, 32, 65, 110, 116, 105, 74, 111, 105, 110, 32, 47, 32, 73, 110, 110, 101, 114, 74, 111, 105, 110, 10, + 74, 111, 105, 110, 80, 114, 101, 102, 105, 120, 32, 60, 45, 32, 67, 114, 111, 115, 115, 74, 111, 105, 110, 80, 114, 101, 102, 105, 120, 32, 47, 32, 78, 97, 116, 117, 114, 97, 108, 74, 111, 105, 110, 80, 114, 101, 102, 105, 120, 32, 47, 32, 80, 111, 115, 105, 116, 105, 111, 110, 97, 108, 74, 111, 105, 110, 80, 114, 101, 102, 105, 120, 10, + 67, 114, 111, 115, 115, 74, 111, 105, 110, 80, 114, 101, 102, 105, 120, 32, 60, 45, 32, 39, 67, 82, 79, 83, 83, 39, 10, + 78, 97, 116, 117, 114, 97, 108, 74, 111, 105, 110, 80, 114, 101, 102, 105, 120, 32, 60, 45, 32, 39, 78, 65, 84, 85, 82, 65, 76, 39, 32, 74, 111, 105, 110, 84, 121, 112, 101, 63, 10, + 80, 111, 115, 105, 116, 105, 111, 110, 97, 108, 74, 111, 105, 110, 80, 114, 101, 102, 105, 120, 32, 60, 45, 32, 39, 80, 79, 83, 73, 84, 73, 79, 78, 65, 76, 39, 10, + 70, 117, 108, 108, 74, 111, 105, 110, 32, 60, 45, 32, 39, 70, 85, 76, 76, 39, 32, 39, 79, 85, 84, 69, 82, 39, 63, 10, + 76, 101, 102, 116, 74, 111, 105, 110, 32, 60, 45, 32, 39, 76, 69, 70, 84, 39, 32, 39, 79, 85, 84, 69, 82, 39, 63, 10, + 82, 105, 103, 104, 116, 74, 111, 105, 110, 32, 60, 45, 32, 39, 82, 73, 71, 72, 84, 39, 32, 39, 79, 85, 84, 69, 82, 39, 63, 10, + 83, 101, 109, 105, 74, 111, 105, 110, 32, 60, 45, 32, 39, 83, 69, 77, 73, 39, 10, + 65, 110, 116, 105, 74, 111, 105, 110, 32, 60, 45, 32, 39, 65, 78, 84, 73, 39, 10, + 73, 110, 110, 101, 114, 74, 111, 105, 110, 32, 60, 45, 32, 39, 73, 78, 78, 69, 82, 39, 10, + 70, 114, 111, 109, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 70, 82, 79, 77, 39, 32, 76, 105, 115, 116, 40, 84, 97, 98, 108, 101, 82, 101, 102, 41, 10, + 87, 104, 101, 114, 101, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 87, 72, 69, 82, 69, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 71, 114, 111, 117, 112, 66, 121, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 71, 82, 79, 85, 80, 39, 32, 39, 66, 89, 39, 32, 71, 114, 111, 117, 112, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 115, 10, + 72, 97, 118, 105, 110, 103, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 72, 65, 86, 73, 78, 71, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 81, 117, 97, 108, 105, 102, 121, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 81, 85, 65, 76, 73, 70, 89, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 83, 97, 109, 112, 108, 101, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 84, 97, 98, 108, 101, 83, 97, 109, 112, 108, 101, 32, 83, 97, 109, 112, 108, 101, 69, 110, 116, 114, 121, 10, + 83, 101, 108, 101, 99, 116, 83, 97, 109, 112, 108, 101, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 85, 115, 105, 110, 103, 83, 97, 109, 112, 108, 101, 32, 83, 97, 109, 112, 108, 101, 69, 110, 116, 114, 121, 10, + 85, 115, 105, 110, 103, 83, 97, 109, 112, 108, 101, 32, 60, 45, 32, 39, 85, 83, 73, 78, 71, 39, 32, 39, 83, 65, 77, 80, 76, 69, 39, 10, + 84, 97, 98, 108, 101, 83, 97, 109, 112, 108, 101, 32, 60, 45, 32, 39, 84, 65, 66, 76, 69, 83, 65, 77, 80, 76, 69, 39, 10, + 87, 105, 110, 100, 111, 119, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 87, 73, 78, 68, 79, 87, 39, 32, 76, 105, 115, 116, 40, 87, 105, 110, 100, 111, 119, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 41, 10, + 87, 105, 110, 100, 111, 119, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 39, 65, 83, 39, 32, 87, 105, 110, 100, 111, 119, 70, 114, 97, 109, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 10, + 83, 97, 109, 112, 108, 101, 69, 110, 116, 114, 121, 32, 60, 45, 32, 83, 97, 109, 112, 108, 101, 69, 110, 116, 114, 121, 70, 117, 110, 99, 116, 105, 111, 110, 32, 47, 32, 83, 97, 109, 112, 108, 101, 69, 110, 116, 114, 121, 67, 111, 117, 110, 116, 10, + 83, 97, 109, 112, 108, 101, 69, 110, 116, 114, 121, 67, 111, 117, 110, 116, 32, 60, 45, 32, 83, 97, 109, 112, 108, 101, 67, 111, 117, 110, 116, 32, 80, 97, 114, 101, 110, 115, 40, 83, 97, 109, 112, 108, 101, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 41, 63, 10, + 83, 97, 109, 112, 108, 101, 69, 110, 116, 114, 121, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 83, 97, 109, 112, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 63, 32, 80, 97, 114, 101, 110, 115, 40, 83, 97, 109, 112, 108, 101, 67, 111, 117, 110, 116, 41, 32, 82, 101, 112, 101, 97, 116, 97, 98, 108, 101, 83, 97, 109, 112, 108, 101, 63, 10, + 83, 97, 109, 112, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 67, 111, 108, 73, 100, 10, + 83, 97, 109, 112, 108, 101, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 32, 60, 45, 32, 67, 111, 108, 73, 100, 32, 40, 39, 44, 39, 32, 83, 97, 109, 112, 108, 101, 83, 101, 101, 100, 41, 63, 10, + 82, 101, 112, 101, 97, 116, 97, 98, 108, 101, 83, 97, 109, 112, 108, 101, 32, 60, 45, 32, 39, 82, 69, 80, 69, 65, 84, 65, 66, 76, 69, 39, 32, 80, 97, 114, 101, 110, 115, 40, 83, 97, 109, 112, 108, 101, 83, 101, 101, 100, 41, 10, + 83, 97, 109, 112, 108, 101, 83, 101, 101, 100, 32, 60, 45, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 10, + 83, 97, 109, 112, 108, 101, 67, 111, 117, 110, 116, 32, 60, 45, 32, 83, 97, 109, 112, 108, 101, 86, 97, 108, 117, 101, 32, 83, 97, 109, 112, 108, 101, 85, 110, 105, 116, 63, 10, + 83, 97, 109, 112, 108, 101, 86, 97, 108, 117, 101, 32, 60, 45, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 80, 97, 114, 97, 109, 101, 116, 101, 114, 10, + 83, 97, 109, 112, 108, 101, 85, 110, 105, 116, 32, 60, 45, 32, 83, 97, 109, 112, 108, 101, 80, 101, 114, 99, 101, 110, 116, 97, 103, 101, 32, 47, 32, 83, 97, 109, 112, 108, 101, 82, 111, 119, 115, 10, + 83, 97, 109, 112, 108, 101, 80, 101, 114, 99, 101, 110, 116, 97, 103, 101, 32, 60, 45, 32, 39, 37, 39, 32, 47, 32, 39, 80, 69, 82, 67, 69, 78, 84, 39, 10, + 83, 97, 109, 112, 108, 101, 82, 111, 119, 115, 32, 60, 45, 32, 39, 82, 79, 87, 83, 39, 10, + 71, 114, 111, 117, 112, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 115, 32, 60, 45, 32, 71, 114, 111, 117, 112, 66, 121, 76, 105, 115, 116, 32, 47, 32, 71, 114, 111, 117, 112, 66, 121, 65, 108, 108, 10, 71, 114, 111, 117, 112, 66, 121, 65, 108, 108, 32, 60, 45, 32, 39, 65, 76, 76, 39, 10, - 71, 114, 111, 117, 112, 66, 121, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, - 71, 114, 111, 117, 112, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, 71, - 114, 111, 117, 112, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, - 69, 109, 112, 116, 121, 71, 114, 111, 117, 112, 105, 110, 103, 73, 116, 101, 109, 32, 47, 32, - 67, 117, 98, 101, 79, 114, 82, 111, 108, 108, 117, 112, 67, 108, 97, 117, 115, 101, 32, 47, - 32, 71, 114, 111, 117, 112, 105, 110, 103, 83, 101, 116, 115, 67, 108, 97, 117, 115, 101, 32, - 47, 32, 71, 114, 111, 117, 112, 66, 121, 66, 97, 115, 101, 69, 120, 112, 114, 101, 115, 115, - 105, 111, 110, 10, 71, 114, 111, 117, 112, 66, 121, 66, 97, 115, 101, 69, 120, 112, 114, 101, - 115, 115, 105, 111, 110, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, - 69, 109, 112, 116, 121, 71, 114, 111, 117, 112, 105, 110, 103, 73, 116, 101, 109, 32, 60, 45, - 32, 39, 40, 39, 32, 39, 41, 39, 10, 67, 117, 98, 101, 79, 114, 82, 111, 108, 108, 117, - 112, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 67, 117, 98, 101, 79, 114, 82, 111, 108, - 108, 117, 112, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 41, 63, 41, 10, 67, 117, 98, 101, 79, 114, 82, 111, 108, 108, - 117, 112, 32, 60, 45, 32, 67, 117, 98, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, - 82, 111, 108, 108, 117, 112, 75, 101, 121, 119, 111, 114, 100, 10, 67, 117, 98, 101, 75, 101, - 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 67, 85, 66, 69, 39, 10, 82, 111, 108, 108, - 117, 112, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 82, 79, 76, 76, 85, 80, - 39, 10, 71, 114, 111, 117, 112, 105, 110, 103, 83, 101, 116, 115, 67, 108, 97, 117, 115, 101, - 32, 60, 45, 32, 39, 71, 82, 79, 85, 80, 73, 78, 71, 39, 32, 39, 83, 69, 84, 83, - 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 71, 114, 111, 117, 112, 66, - 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 41, 10, 83, 117, 98, 113, 117, 101, - 114, 121, 82, 101, 102, 101, 114, 101, 110, 99, 101, 32, 60, 45, 32, 80, 97, 114, 101, 110, - 115, 40, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, - 101, 114, 110, 97, 108, 41, 10, 79, 114, 100, 101, 114, 66, 121, 69, 120, 112, 114, 101, 115, - 115, 105, 111, 110, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 68, - 101, 115, 99, 79, 114, 65, 115, 99, 63, 32, 78, 117, 108, 108, 115, 70, 105, 114, 115, 116, - 79, 114, 76, 97, 115, 116, 63, 10, 68, 101, 115, 99, 79, 114, 65, 115, 99, 32, 60, 45, - 32, 68, 101, 115, 99, 101, 110, 100, 105, 110, 103, 79, 114, 100, 101, 114, 32, 47, 32, 65, - 115, 99, 101, 110, 100, 105, 110, 103, 79, 114, 100, 101, 114, 10, 68, 101, 115, 99, 101, 110, - 100, 105, 110, 103, 79, 114, 100, 101, 114, 32, 60, 45, 32, 39, 68, 69, 83, 67, 39, 32, - 47, 32, 39, 68, 69, 83, 67, 69, 78, 68, 73, 78, 71, 39, 10, 65, 115, 99, 101, 110, - 100, 105, 110, 103, 79, 114, 100, 101, 114, 32, 60, 45, 32, 39, 65, 83, 67, 39, 32, 47, - 32, 39, 65, 83, 67, 69, 78, 68, 73, 78, 71, 39, 10, 78, 117, 108, 108, 115, 70, 105, - 114, 115, 116, 79, 114, 76, 97, 115, 116, 32, 60, 45, 32, 78, 117, 108, 108, 115, 70, 105, - 114, 115, 116, 32, 47, 32, 78, 117, 108, 108, 115, 76, 97, 115, 116, 10, 78, 117, 108, 108, - 115, 70, 105, 114, 115, 116, 32, 60, 45, 32, 39, 78, 85, 76, 76, 83, 39, 32, 39, 70, - 73, 82, 83, 84, 39, 10, 78, 117, 108, 108, 115, 76, 97, 115, 116, 32, 60, 45, 32, 39, - 78, 85, 76, 76, 83, 39, 32, 39, 76, 65, 83, 84, 39, 10, 79, 114, 100, 101, 114, 66, - 121, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 79, 82, 68, 69, 82, 39, 32, 39, - 66, 89, 39, 32, 79, 114, 100, 101, 114, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, - 110, 115, 10, 79, 114, 100, 101, 114, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, - 115, 32, 60, 45, 32, 79, 114, 100, 101, 114, 66, 121, 65, 108, 108, 32, 47, 32, 79, 114, - 100, 101, 114, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 76, 105, 115, 116, 10, - 79, 114, 100, 101, 114, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 76, 105, 115, - 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 79, 114, 100, 101, 114, 66, 121, 69, 120, 112, - 114, 101, 115, 115, 105, 111, 110, 41, 10, 79, 114, 100, 101, 114, 66, 121, 65, 108, 108, 32, - 60, 45, 32, 39, 65, 76, 76, 39, 32, 68, 101, 115, 99, 79, 114, 65, 115, 99, 63, 32, - 78, 117, 108, 108, 115, 70, 105, 114, 115, 116, 79, 114, 76, 97, 115, 116, 63, 10, 76, 105, - 109, 105, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 76, 73, 77, 73, 84, 39, - 32, 76, 105, 109, 105, 116, 86, 97, 108, 117, 101, 10, 79, 102, 102, 115, 101, 116, 67, 108, - 97, 117, 115, 101, 32, 60, 45, 32, 39, 79, 70, 70, 83, 69, 84, 39, 32, 79, 102, 102, - 115, 101, 116, 86, 97, 108, 117, 101, 10, 79, 102, 102, 115, 101, 116, 86, 97, 108, 117, 101, - 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 82, 111, 119, 79, 114, - 82, 111, 119, 115, 63, 10, 82, 111, 119, 79, 114, 82, 111, 119, 115, 32, 60, 45, 32, 39, - 82, 79, 87, 39, 32, 47, 32, 39, 82, 79, 87, 83, 39, 10, 76, 105, 109, 105, 116, 86, - 97, 108, 117, 101, 32, 60, 45, 32, 76, 105, 109, 105, 116, 65, 108, 108, 32, 47, 32, 76, - 105, 109, 105, 116, 76, 105, 116, 101, 114, 97, 108, 80, 101, 114, 99, 101, 110, 116, 32, 47, - 32, 76, 105, 109, 105, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 76, 105, 109, - 105, 116, 65, 108, 108, 32, 60, 45, 32, 39, 65, 76, 76, 39, 10, 76, 105, 109, 105, 116, - 76, 105, 116, 101, 114, 97, 108, 80, 101, 114, 99, 101, 110, 116, 32, 60, 45, 32, 78, 117, - 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 32, 39, 80, 69, 82, 67, 69, 78, 84, - 39, 10, 76, 105, 109, 105, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, - 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 37, 39, 63, 10, 70, 101, 116, - 99, 104, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 70, 69, 84, 67, 72, 39, 32, - 70, 105, 114, 115, 116, 79, 114, 78, 101, 120, 116, 32, 70, 101, 116, 99, 104, 86, 97, 108, - 117, 101, 32, 82, 111, 119, 79, 114, 82, 111, 119, 115, 32, 39, 79, 78, 76, 89, 39, 10, - 70, 105, 114, 115, 116, 79, 114, 78, 101, 120, 116, 32, 60, 45, 32, 39, 70, 73, 82, 83, - 84, 39, 32, 47, 32, 39, 78, 69, 88, 84, 39, 10, 70, 101, 116, 99, 104, 86, 97, 108, - 117, 101, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 65, 108, 105, - 97, 115, 101, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 67, 111, - 108, 73, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 65, 115, 67, 111, 108, 108, 97, 98, 101, 108, 32, 47, 32, 69, - 120, 112, 114, 101, 115, 115, 105, 111, 110, 79, 112, 116, 73, 100, 101, 110, 116, 105, 102, 105, - 101, 114, 10, 67, 111, 108, 73, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, - 45, 32, 67, 111, 108, 73, 100, 32, 39, 58, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, - 111, 110, 10, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 65, 115, 67, 111, 108, 108, 97, - 98, 101, 108, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 65, - 83, 39, 32, 67, 111, 108, 76, 97, 98, 101, 108, 79, 114, 83, 116, 114, 105, 110, 103, 10, - 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 79, 112, 116, 73, 100, 101, 110, 116, 105, 102, - 105, 101, 114, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 73, 100, - 101, 110, 116, 105, 102, 105, 101, 114, 63, 10, 86, 97, 108, 117, 101, 115, 67, 108, 97, 117, - 115, 101, 32, 60, 45, 32, 39, 86, 65, 76, 85, 69, 83, 39, 32, 76, 105, 115, 116, 40, - 86, 97, 108, 117, 101, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 115, 41, 10, 86, - 97, 108, 117, 101, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 115, 32, 60, 45, 32, - 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, - 111, 110, 41, 41, 10, 83, 101, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, - 32, 39, 83, 69, 84, 39, 32, 83, 101, 116, 65, 115, 115, 105, 103, 110, 109, 101, 110, 116, - 79, 114, 84, 105, 109, 101, 90, 111, 110, 101, 10, 83, 101, 116, 65, 115, 115, 105, 103, 110, - 109, 101, 110, 116, 79, 114, 84, 105, 109, 101, 90, 111, 110, 101, 32, 60, 45, 32, 83, 101, - 116, 83, 99, 104, 101, 109, 97, 32, 47, 32, 83, 116, 97, 110, 100, 97, 114, 100, 65, 115, - 115, 105, 103, 110, 109, 101, 110, 116, 32, 47, 32, 83, 101, 116, 84, 105, 109, 101, 90, 111, - 110, 101, 10, 82, 101, 115, 101, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, - 32, 39, 82, 69, 83, 69, 84, 39, 32, 83, 101, 116, 86, 97, 114, 105, 97, 98, 108, 101, - 79, 114, 83, 101, 116, 116, 105, 110, 103, 10, 83, 101, 116, 83, 99, 104, 101, 109, 97, 32, - 60, 45, 32, 39, 83, 67, 72, 69, 77, 65, 39, 32, 83, 116, 114, 105, 110, 103, 76, 105, - 116, 101, 114, 97, 108, 10, 83, 116, 97, 110, 100, 97, 114, 100, 65, 115, 115, 105, 103, 110, - 109, 101, 110, 116, 32, 60, 45, 32, 83, 101, 116, 86, 97, 114, 105, 97, 98, 108, 101, 79, - 114, 83, 101, 116, 116, 105, 110, 103, 32, 83, 101, 116, 65, 115, 115, 105, 103, 110, 109, 101, - 110, 116, 10, 83, 101, 116, 86, 97, 114, 105, 97, 98, 108, 101, 79, 114, 83, 101, 116, 116, - 105, 110, 103, 32, 60, 45, 32, 83, 101, 116, 86, 97, 114, 105, 97, 98, 108, 101, 32, 47, - 32, 83, 101, 116, 83, 101, 116, 116, 105, 110, 103, 10, 83, 101, 116, 84, 105, 109, 101, 90, - 111, 110, 101, 32, 60, 45, 32, 39, 84, 73, 77, 69, 39, 32, 39, 90, 79, 78, 69, 39, - 32, 90, 111, 110, 101, 86, 97, 108, 117, 101, 10, 90, 111, 110, 101, 86, 97, 108, 117, 101, - 32, 60, 45, 32, 90, 111, 110, 101, 73, 110, 116, 101, 114, 118, 97, 108, 87, 105, 116, 104, - 80, 114, 101, 99, 105, 115, 105, 111, 110, 32, 47, 32, 90, 111, 110, 101, 73, 110, 116, 101, - 114, 118, 97, 108, 87, 105, 116, 104, 73, 110, 116, 101, 114, 118, 97, 108, 32, 47, 32, 90, - 111, 110, 101, 76, 111, 99, 97, 108, 32, 47, 32, 90, 111, 110, 101, 68, 101, 102, 97, 117, - 108, 116, 32, 47, 32, 90, 111, 110, 101, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, - 97, 108, 32, 47, 32, 90, 111, 110, 101, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, - 47, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 10, 90, 111, 110, 101, - 76, 111, 99, 97, 108, 32, 60, 45, 32, 39, 76, 79, 67, 65, 76, 39, 10, 90, 111, 110, - 101, 68, 101, 102, 97, 117, 108, 116, 32, 60, 45, 32, 39, 68, 69, 70, 65, 85, 76, 84, - 39, 10, 90, 111, 110, 101, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, - 60, 45, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, 90, 111, 110, - 101, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 73, 100, 101, 110, 116, - 105, 102, 105, 101, 114, 10, 90, 111, 110, 101, 73, 110, 116, 101, 114, 118, 97, 108, 87, 105, - 116, 104, 73, 110, 116, 101, 114, 118, 97, 108, 32, 60, 45, 32, 39, 73, 78, 84, 69, 82, - 86, 65, 76, 39, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 73, - 110, 116, 101, 114, 118, 97, 108, 63, 10, 90, 111, 110, 101, 73, 110, 116, 101, 114, 118, 97, - 108, 87, 105, 116, 104, 80, 114, 101, 99, 105, 115, 105, 111, 110, 32, 60, 45, 32, 39, 73, - 78, 84, 69, 82, 86, 65, 76, 39, 32, 80, 97, 114, 101, 110, 115, 40, 78, 117, 109, 98, - 101, 114, 76, 105, 116, 101, 114, 97, 108, 41, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, - 101, 114, 97, 108, 10, 83, 101, 116, 83, 101, 116, 116, 105, 110, 103, 32, 60, 45, 32, 83, - 101, 116, 116, 105, 110, 103, 83, 99, 111, 112, 101, 63, 32, 83, 101, 116, 116, 105, 110, 103, - 78, 97, 109, 101, 10, 83, 101, 116, 86, 97, 114, 105, 97, 98, 108, 101, 32, 60, 45, 32, - 86, 97, 114, 105, 97, 98, 108, 101, 83, 99, 111, 112, 101, 32, 73, 100, 101, 110, 116, 105, - 102, 105, 101, 114, 10, 86, 97, 114, 105, 97, 98, 108, 101, 83, 99, 111, 112, 101, 32, 60, - 45, 32, 39, 86, 65, 82, 73, 65, 66, 76, 69, 39, 10, 83, 101, 116, 116, 105, 110, 103, - 83, 99, 111, 112, 101, 32, 60, 45, 32, 76, 111, 99, 97, 108, 83, 99, 111, 112, 101, 32, - 47, 32, 83, 101, 115, 115, 105, 111, 110, 83, 99, 111, 112, 101, 32, 47, 32, 71, 108, 111, - 98, 97, 108, 83, 99, 111, 112, 101, 10, 76, 111, 99, 97, 108, 83, 99, 111, 112, 101, 32, - 60, 45, 32, 39, 76, 79, 67, 65, 76, 39, 10, 83, 101, 115, 115, 105, 111, 110, 83, 99, - 111, 112, 101, 32, 60, 45, 32, 39, 83, 69, 83, 83, 73, 79, 78, 39, 10, 71, 108, 111, - 98, 97, 108, 83, 99, 111, 112, 101, 32, 60, 45, 32, 39, 71, 76, 79, 66, 65, 76, 39, - 10, 83, 101, 116, 65, 115, 115, 105, 103, 110, 109, 101, 110, 116, 32, 60, 45, 32, 86, 97, - 114, 105, 97, 98, 108, 101, 65, 115, 115, 105, 103, 110, 32, 86, 97, 114, 105, 97, 98, 108, - 101, 76, 105, 115, 116, 10, 86, 97, 114, 105, 97, 98, 108, 101, 65, 115, 115, 105, 103, 110, - 32, 60, 45, 32, 39, 61, 39, 32, 47, 32, 39, 84, 79, 39, 10, 86, 97, 114, 105, 97, - 98, 108, 101, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 69, 120, 112, 114, - 101, 115, 115, 105, 111, 110, 41, 10, 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 83, - 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 66, 101, 103, 105, 110, 84, 114, 97, - 110, 115, 97, 99, 116, 105, 111, 110, 32, 47, 32, 82, 111, 108, 108, 98, 97, 99, 107, 84, - 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 32, 47, 32, 67, 111, 109, 109, 105, 116, 84, - 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 10, 66, 101, 103, 105, 110, 84, 114, 97, 110, - 115, 97, 99, 116, 105, 111, 110, 32, 60, 45, 32, 83, 116, 97, 114, 116, 79, 114, 66, 101, - 103, 105, 110, 32, 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 63, 32, 82, 101, 97, - 100, 79, 114, 87, 114, 105, 116, 101, 63, 10, 82, 111, 108, 108, 98, 97, 99, 107, 84, 114, - 97, 110, 115, 97, 99, 116, 105, 111, 110, 32, 60, 45, 32, 65, 98, 111, 114, 116, 79, 114, - 82, 111, 108, 108, 98, 97, 99, 107, 32, 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, - 63, 10, 67, 111, 109, 109, 105, 116, 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 32, - 60, 45, 32, 67, 111, 109, 109, 105, 116, 79, 114, 69, 110, 100, 32, 84, 114, 97, 110, 115, - 97, 99, 116, 105, 111, 110, 63, 10, 83, 116, 97, 114, 116, 79, 114, 66, 101, 103, 105, 110, - 32, 60, 45, 32, 39, 83, 84, 65, 82, 84, 39, 32, 47, 32, 39, 66, 69, 71, 73, 78, - 39, 10, 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 87, 79, - 82, 75, 39, 32, 47, 32, 39, 84, 82, 65, 78, 83, 65, 67, 84, 73, 79, 78, 39, 10, - 82, 101, 97, 100, 79, 114, 87, 114, 105, 116, 101, 32, 60, 45, 32, 39, 82, 69, 65, 68, - 39, 32, 82, 101, 97, 100, 79, 110, 108, 121, 79, 114, 82, 101, 97, 100, 87, 114, 105, 116, - 101, 10, 82, 101, 97, 100, 79, 110, 108, 121, 79, 114, 82, 101, 97, 100, 87, 114, 105, 116, - 101, 32, 60, 45, 32, 82, 101, 97, 100, 79, 110, 108, 121, 32, 47, 32, 82, 101, 97, 100, - 87, 114, 105, 116, 101, 10, 82, 101, 97, 100, 79, 110, 108, 121, 32, 60, 45, 32, 39, 79, - 78, 76, 89, 39, 10, 82, 101, 97, 100, 87, 114, 105, 116, 101, 32, 60, 45, 32, 39, 87, - 82, 73, 84, 69, 39, 10, 65, 98, 111, 114, 116, 79, 114, 82, 111, 108, 108, 98, 97, 99, - 107, 32, 60, 45, 32, 39, 65, 66, 79, 82, 84, 39, 32, 47, 32, 39, 82, 79, 76, 76, - 66, 65, 67, 75, 39, 10, 67, 111, 109, 109, 105, 116, 79, 114, 69, 110, 100, 32, 60, 45, - 32, 39, 67, 79, 77, 77, 73, 84, 39, 32, 47, 32, 39, 69, 78, 68, 39, 10, 85, 112, - 100, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 87, 105, 116, - 104, 67, 108, 97, 117, 115, 101, 63, 32, 39, 85, 80, 68, 65, 84, 69, 39, 32, 85, 112, - 100, 97, 116, 101, 84, 97, 114, 103, 101, 116, 32, 85, 112, 100, 97, 116, 101, 83, 101, 116, - 67, 108, 97, 117, 115, 101, 32, 70, 114, 111, 109, 67, 108, 97, 117, 115, 101, 63, 32, 87, - 104, 101, 114, 101, 67, 108, 97, 117, 115, 101, 63, 32, 82, 101, 116, 117, 114, 110, 105, 110, - 103, 67, 108, 97, 117, 115, 101, 63, 10, 85, 112, 100, 97, 116, 101, 84, 97, 114, 103, 101, - 116, 32, 60, 45, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 83, 101, 116, 32, 47, 32, - 66, 97, 115, 101, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 83, 101, 116, 10, 66, 97, - 115, 101, 84, 97, 98, 108, 101, 83, 101, 116, 32, 60, 45, 32, 66, 97, 115, 101, 84, 97, - 98, 108, 101, 78, 97, 109, 101, 32, 39, 83, 69, 84, 39, 10, 66, 97, 115, 101, 84, 97, - 98, 108, 101, 65, 108, 105, 97, 115, 83, 101, 116, 32, 60, 45, 32, 66, 97, 115, 101, 84, - 97, 98, 108, 101, 78, 97, 109, 101, 32, 85, 112, 100, 97, 116, 101, 65, 108, 105, 97, 115, - 63, 32, 39, 83, 69, 84, 39, 10, 85, 112, 100, 97, 116, 101, 65, 108, 105, 97, 115, 32, - 60, 45, 32, 39, 65, 83, 39, 63, 32, 67, 111, 108, 73, 100, 10, 85, 112, 100, 97, 116, - 101, 83, 101, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 85, 112, 100, 97, 116, 101, - 83, 101, 116, 69, 108, 101, 109, 101, 110, 116, 76, 105, 115, 116, 32, 47, 32, 85, 112, 100, - 97, 116, 101, 83, 101, 116, 84, 117, 112, 108, 101, 10, 85, 112, 100, 97, 116, 101, 83, 101, - 116, 84, 117, 112, 108, 101, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, - 116, 40, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 41, 41, 32, 39, 61, 39, 32, 69, - 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 85, 112, 100, 97, 116, 101, 83, 101, 116, 69, - 108, 101, 109, 101, 110, 116, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 85, - 112, 100, 97, 116, 101, 83, 101, 116, 69, 108, 101, 109, 101, 110, 116, 41, 10, 85, 112, 100, - 97, 116, 101, 83, 101, 116, 69, 108, 101, 109, 101, 110, 116, 32, 60, 45, 32, 85, 112, 100, - 97, 116, 101, 83, 101, 116, 67, 111, 108, 117, 109, 110, 84, 97, 114, 103, 101, 116, 32, 39, - 61, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 85, 112, 100, 97, 116, 101, - 83, 101, 116, 67, 111, 108, 117, 109, 110, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 67, - 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 68, 111, 116, 73, 100, 101, 110, 116, 105, 102, - 105, 101, 114, 42, 10, 85, 115, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, - 32, 39, 85, 83, 69, 39, 32, 85, 115, 101, 84, 97, 114, 103, 101, 116, 10, 85, 115, 101, - 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 85, 115, 101, 84, 97, 114, 103, 101, 116, 67, - 97, 116, 97, 108, 111, 103, 83, 99, 104, 101, 109, 97, 32, 47, 32, 83, 99, 104, 101, 109, - 97, 78, 97, 109, 101, 65, 115, 85, 115, 101, 84, 97, 114, 103, 101, 116, 32, 47, 32, 67, - 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, 65, 115, 85, 115, 101, 84, 97, 114, 103, 101, - 116, 10, 83, 99, 104, 101, 109, 97, 78, 97, 109, 101, 65, 115, 85, 115, 101, 84, 97, 114, - 103, 101, 116, 32, 60, 45, 32, 83, 99, 104, 101, 109, 97, 78, 97, 109, 101, 10, 67, 97, - 116, 97, 108, 111, 103, 78, 97, 109, 101, 65, 115, 85, 115, 101, 84, 97, 114, 103, 101, 116, - 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, 10, 85, 115, 101, 84, - 97, 114, 103, 101, 116, 67, 97, 116, 97, 108, 111, 103, 83, 99, 104, 101, 109, 97, 32, 60, - 45, 32, 67, 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, 32, 39, 46, 39, 32, 82, 101, - 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 78, 97, 109, 101, 32, 68, 111, 116, - 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 42, 10, 68, 111, 116, 73, 100, 101, 110, 116, - 105, 102, 105, 101, 114, 32, 60, 45, 32, 39, 46, 39, 32, 73, 100, 101, 110, 116, 105, 102, - 105, 101, 114, 10, 86, 97, 99, 117, 117, 109, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, - 60, 45, 32, 39, 86, 65, 67, 85, 85, 77, 39, 32, 86, 97, 99, 117, 117, 109, 79, 112, - 116, 105, 111, 110, 115, 63, 32, 65, 110, 97, 108, 121, 122, 101, 84, 97, 114, 103, 101, 116, - 63, 10, 86, 97, 99, 117, 117, 109, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 86, - 97, 99, 117, 117, 109, 80, 97, 114, 101, 110, 115, 79, 112, 116, 105, 111, 110, 115, 32, 47, - 32, 86, 97, 99, 117, 117, 109, 76, 101, 103, 97, 99, 121, 79, 112, 116, 105, 111, 110, 115, - 10, 86, 97, 99, 117, 117, 109, 80, 97, 114, 101, 110, 115, 79, 112, 116, 105, 111, 110, 115, - 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 86, 97, 99, 117, - 117, 109, 79, 112, 116, 105, 111, 110, 41, 41, 10, 86, 97, 99, 117, 117, 109, 76, 101, 103, - 97, 99, 121, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 79, 112, 116, 70, 117, 108, - 108, 63, 32, 79, 112, 116, 70, 114, 101, 101, 122, 101, 63, 32, 79, 112, 116, 86, 101, 114, - 98, 111, 115, 101, 63, 32, 79, 112, 116, 65, 110, 97, 108, 121, 122, 101, 63, 10, 86, 97, - 99, 117, 117, 109, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 79, 112, 116, 65, 110, 97, - 108, 121, 122, 101, 32, 47, 32, 79, 112, 116, 70, 114, 101, 101, 122, 101, 32, 47, 32, 79, - 112, 116, 70, 117, 108, 108, 32, 47, 32, 79, 112, 116, 86, 101, 114, 98, 111, 115, 101, 32, - 47, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, 79, 112, 116, 65, 110, 97, 108, - 121, 122, 101, 32, 60, 45, 32, 65, 110, 97, 108, 121, 122, 101, 75, 101, 121, 119, 111, 114, - 100, 10, 79, 112, 116, 70, 117, 108, 108, 32, 60, 45, 32, 39, 70, 85, 76, 76, 39, 10, - 79, 112, 116, 70, 114, 101, 101, 122, 101, 32, 60, 45, 32, 39, 70, 82, 69, 69, 90, 69, - 39, 10, 79, 112, 116, 86, 101, 114, 98, 111, 115, 101, 32, 60, 45, 32, 39, 86, 69, 82, - 66, 79, 83, 69, 39, 10, 78, 97, 109, 101, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, - 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 67, 111, 108, 73, 100, 41, 41, 10, 0, + 71, 114, 111, 117, 112, 66, 121, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 71, 114, 111, 117, 112, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, + 71, 114, 111, 117, 112, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 69, 109, 112, 116, 121, 71, 114, 111, 117, 112, 105, 110, 103, 73, 116, 101, 109, 32, 47, 32, 67, 117, 98, 101, 79, 114, 82, 111, 108, 108, 117, 112, 67, 108, 97, 117, 115, 101, 32, 47, 32, 71, 114, 111, 117, 112, 105, 110, 103, 83, 101, 116, 115, 67, 108, 97, 117, 115, 101, 32, 47, 32, 71, 114, 111, 117, 112, 66, 121, 66, 97, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 71, 114, 111, 117, 112, 66, 121, 66, 97, 115, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 69, 109, 112, 116, 121, 71, 114, 111, 117, 112, 105, 110, 103, 73, 116, 101, 109, 32, 60, 45, 32, 39, 40, 39, 32, 39, 41, 39, 10, + 67, 117, 98, 101, 79, 114, 82, 111, 108, 108, 117, 112, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 67, 117, 98, 101, 79, 114, 82, 111, 108, 108, 117, 112, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 63, 41, 10, + 67, 117, 98, 101, 79, 114, 82, 111, 108, 108, 117, 112, 32, 60, 45, 32, 67, 117, 98, 101, 75, 101, 121, 119, 111, 114, 100, 32, 47, 32, 82, 111, 108, 108, 117, 112, 75, 101, 121, 119, 111, 114, 100, 10, + 67, 117, 98, 101, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 67, 85, 66, 69, 39, 10, + 82, 111, 108, 108, 117, 112, 75, 101, 121, 119, 111, 114, 100, 32, 60, 45, 32, 39, 82, 79, 76, 76, 85, 80, 39, 10, + 71, 114, 111, 117, 112, 105, 110, 103, 83, 101, 116, 115, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 71, 82, 79, 85, 80, 73, 78, 71, 39, 32, 39, 83, 69, 84, 83, 39, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 71, 114, 111, 117, 112, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 41, 10, + 83, 117, 98, 113, 117, 101, 114, 121, 82, 101, 102, 101, 114, 101, 110, 99, 101, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 83, 101, 108, 101, 99, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 73, 110, 116, 101, 114, 110, 97, 108, 41, 10, + 79, 114, 100, 101, 114, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 68, 101, 115, 99, 79, 114, 65, 115, 99, 63, 32, 78, 117, 108, 108, 115, 70, 105, 114, 115, 116, 79, 114, 76, 97, 115, 116, 63, 10, + 68, 101, 115, 99, 79, 114, 65, 115, 99, 32, 60, 45, 32, 68, 101, 115, 99, 101, 110, 100, 105, 110, 103, 79, 114, 100, 101, 114, 32, 47, 32, 65, 115, 99, 101, 110, 100, 105, 110, 103, 79, 114, 100, 101, 114, 10, + 68, 101, 115, 99, 101, 110, 100, 105, 110, 103, 79, 114, 100, 101, 114, 32, 60, 45, 32, 39, 68, 69, 83, 67, 39, 32, 47, 32, 39, 68, 69, 83, 67, 69, 78, 68, 73, 78, 71, 39, 10, + 65, 115, 99, 101, 110, 100, 105, 110, 103, 79, 114, 100, 101, 114, 32, 60, 45, 32, 39, 65, 83, 67, 39, 32, 47, 32, 39, 65, 83, 67, 69, 78, 68, 73, 78, 71, 39, 10, + 78, 117, 108, 108, 115, 70, 105, 114, 115, 116, 79, 114, 76, 97, 115, 116, 32, 60, 45, 32, 78, 117, 108, 108, 115, 70, 105, 114, 115, 116, 32, 47, 32, 78, 117, 108, 108, 115, 76, 97, 115, 116, 10, + 78, 117, 108, 108, 115, 70, 105, 114, 115, 116, 32, 60, 45, 32, 39, 78, 85, 76, 76, 83, 39, 32, 39, 70, 73, 82, 83, 84, 39, 10, + 78, 117, 108, 108, 115, 76, 97, 115, 116, 32, 60, 45, 32, 39, 78, 85, 76, 76, 83, 39, 32, 39, 76, 65, 83, 84, 39, 10, + 79, 114, 100, 101, 114, 66, 121, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 79, 82, 68, 69, 82, 39, 32, 39, 66, 89, 39, 32, 79, 114, 100, 101, 114, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 115, 10, + 79, 114, 100, 101, 114, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 115, 32, 60, 45, 32, 79, 114, 100, 101, 114, 66, 121, 65, 108, 108, 32, 47, 32, 79, 114, 100, 101, 114, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 76, 105, 115, 116, 10, + 79, 114, 100, 101, 114, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 79, 114, 100, 101, 114, 66, 121, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, + 79, 114, 100, 101, 114, 66, 121, 65, 108, 108, 32, 60, 45, 32, 39, 65, 76, 76, 39, 32, 68, 101, 115, 99, 79, 114, 65, 115, 99, 63, 32, 78, 117, 108, 108, 115, 70, 105, 114, 115, 116, 79, 114, 76, 97, 115, 116, 63, 10, + 76, 105, 109, 105, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 76, 73, 77, 73, 84, 39, 32, 76, 105, 109, 105, 116, 86, 97, 108, 117, 101, 10, + 79, 102, 102, 115, 101, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 79, 70, 70, 83, 69, 84, 39, 32, 79, 102, 102, 115, 101, 116, 86, 97, 108, 117, 101, 10, + 79, 102, 102, 115, 101, 116, 86, 97, 108, 117, 101, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 82, 111, 119, 79, 114, 82, 111, 119, 115, 63, 10, + 82, 111, 119, 79, 114, 82, 111, 119, 115, 32, 60, 45, 32, 39, 82, 79, 87, 39, 32, 47, 32, 39, 82, 79, 87, 83, 39, 10, + 76, 105, 109, 105, 116, 86, 97, 108, 117, 101, 32, 60, 45, 32, 76, 105, 109, 105, 116, 65, 108, 108, 32, 47, 32, 76, 105, 109, 105, 116, 76, 105, 116, 101, 114, 97, 108, 80, 101, 114, 99, 101, 110, 116, 32, 47, 32, 76, 105, 109, 105, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 76, 105, 109, 105, 116, 65, 108, 108, 32, 60, 45, 32, 39, 65, 76, 76, 39, 10, + 76, 105, 109, 105, 116, 76, 105, 116, 101, 114, 97, 108, 80, 101, 114, 99, 101, 110, 116, 32, 60, 45, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 32, 39, 80, 69, 82, 67, 69, 78, 84, 39, 10, + 76, 105, 109, 105, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 37, 39, 63, 10, + 70, 101, 116, 99, 104, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 70, 101, 116, 99, 104, 67, 108, 97, 117, 115, 101, 87, 105, 116, 104, 111, 117, 116, 86, 97, 108, 117, 101, 32, 47, 32, 70, 101, 116, 99, 104, 67, 108, 97, 117, 115, 101, 87, 105, 116, 104, 86, 97, 108, 117, 101, 10, + 70, 101, 116, 99, 104, 67, 108, 97, 117, 115, 101, 87, 105, 116, 104, 111, 117, 116, 86, 97, 108, 117, 101, 32, 60, 45, 32, 39, 70, 69, 84, 67, 72, 39, 32, 70, 105, 114, 115, 116, 79, 114, 78, 101, 120, 116, 32, 82, 111, 119, 79, 114, 82, 111, 119, 115, 32, 39, 79, 78, 76, 89, 39, 10, + 70, 101, 116, 99, 104, 67, 108, 97, 117, 115, 101, 87, 105, 116, 104, 86, 97, 108, 117, 101, 32, 60, 45, 32, 39, 70, 69, 84, 67, 72, 39, 32, 70, 105, 114, 115, 116, 79, 114, 78, 101, 120, 116, 32, 70, 101, 116, 99, 104, 86, 97, 108, 117, 101, 32, 82, 111, 119, 79, 114, 82, 111, 119, 115, 32, 39, 79, 78, 76, 89, 39, 10, + 70, 105, 114, 115, 116, 79, 114, 78, 101, 120, 116, 32, 60, 45, 32, 39, 70, 73, 82, 83, 84, 39, 32, 47, 32, 39, 78, 69, 88, 84, 39, 10, + 70, 101, 116, 99, 104, 86, 97, 108, 117, 101, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 65, 108, 105, 97, 115, 101, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 67, 111, 108, 73, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 47, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 65, 115, 67, 111, 108, 108, 97, 98, 101, 108, 32, 47, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 79, 112, 116, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 67, 111, 108, 73, 100, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 67, 111, 108, 73, 100, 32, 39, 58, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 65, 115, 67, 111, 108, 108, 97, 98, 101, 108, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 39, 65, 83, 39, 32, 67, 111, 108, 76, 97, 98, 101, 108, 79, 114, 83, 116, 114, 105, 110, 103, 10, + 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 79, 112, 116, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 66, 97, 114, 101, 67, 111, 108, 76, 97, 98, 101, 108, 63, 10, + 86, 97, 108, 117, 101, 115, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 86, 65, 76, 85, 69, 83, 39, 32, 76, 105, 115, 116, 40, 86, 97, 108, 117, 101, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 115, 41, 10, + 86, 97, 108, 117, 101, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 115, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 41, 10, + 83, 101, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 83, 69, 84, 39, 32, 83, 101, 116, 65, 115, 115, 105, 103, 110, 109, 101, 110, 116, 79, 114, 84, 105, 109, 101, 90, 111, 110, 101, 10, + 83, 101, 116, 65, 115, 115, 105, 103, 110, 109, 101, 110, 116, 79, 114, 84, 105, 109, 101, 90, 111, 110, 101, 32, 60, 45, 32, 83, 101, 116, 83, 99, 104, 101, 109, 97, 32, 47, 32, 83, 116, 97, 110, 100, 97, 114, 100, 65, 115, 115, 105, 103, 110, 109, 101, 110, 116, 32, 47, 32, 83, 101, 116, 84, 105, 109, 101, 90, 111, 110, 101, 10, + 82, 101, 115, 101, 116, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 82, 69, 83, 69, 84, 39, 32, 83, 101, 116, 86, 97, 114, 105, 97, 98, 108, 101, 79, 114, 83, 101, 116, 116, 105, 110, 103, 10, + 83, 101, 116, 83, 99, 104, 101, 109, 97, 32, 60, 45, 32, 39, 83, 67, 72, 69, 77, 65, 39, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 83, 116, 97, 110, 100, 97, 114, 100, 65, 115, 115, 105, 103, 110, 109, 101, 110, 116, 32, 60, 45, 32, 83, 101, 116, 86, 97, 114, 105, 97, 98, 108, 101, 79, 114, 83, 101, 116, 116, 105, 110, 103, 32, 83, 101, 116, 65, 115, 115, 105, 103, 110, 109, 101, 110, 116, 10, + 83, 101, 116, 86, 97, 114, 105, 97, 98, 108, 101, 79, 114, 83, 101, 116, 116, 105, 110, 103, 32, 60, 45, 32, 83, 101, 116, 86, 97, 114, 105, 97, 98, 108, 101, 32, 47, 32, 83, 101, 116, 83, 101, 116, 116, 105, 110, 103, 10, + 83, 101, 116, 84, 105, 109, 101, 90, 111, 110, 101, 32, 60, 45, 32, 39, 84, 73, 77, 69, 39, 32, 39, 90, 79, 78, 69, 39, 32, 90, 111, 110, 101, 86, 97, 108, 117, 101, 10, + 90, 111, 110, 101, 86, 97, 108, 117, 101, 32, 60, 45, 32, 90, 111, 110, 101, 73, 110, 116, 101, 114, 118, 97, 108, 87, 105, 116, 104, 80, 114, 101, 99, 105, 115, 105, 111, 110, 32, 47, 32, 90, 111, 110, 101, 73, 110, 116, 101, 114, 118, 97, 108, 87, 105, 116, 104, 73, 110, 116, 101, 114, 118, 97, 108, 32, 47, 32, 90, 111, 110, 101, 76, 111, 99, 97, 108, 32, 47, 32, 90, 111, 110, 101, 68, 101, 102, 97, 117, 108, 116, 32, 47, 32, 90, 111, 110, 101, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 90, 111, 110, 101, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 47, 32, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 10, + 90, 111, 110, 101, 76, 111, 99, 97, 108, 32, 60, 45, 32, 39, 76, 79, 67, 65, 76, 39, 10, + 90, 111, 110, 101, 68, 101, 102, 97, 117, 108, 116, 32, 60, 45, 32, 39, 68, 69, 70, 65, 85, 76, 84, 39, 10, + 90, 111, 110, 101, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 90, 111, 110, 101, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 90, 111, 110, 101, 73, 110, 116, 101, 114, 118, 97, 108, 87, 105, 116, 104, 73, 110, 116, 101, 114, 118, 97, 108, 32, 60, 45, 32, 39, 73, 78, 84, 69, 82, 86, 65, 76, 39, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 32, 73, 110, 116, 101, 114, 118, 97, 108, 63, 10, + 90, 111, 110, 101, 73, 110, 116, 101, 114, 118, 97, 108, 87, 105, 116, 104, 80, 114, 101, 99, 105, 115, 105, 111, 110, 32, 60, 45, 32, 39, 73, 78, 84, 69, 82, 86, 65, 76, 39, 32, 80, 97, 114, 101, 110, 115, 40, 78, 117, 109, 98, 101, 114, 76, 105, 116, 101, 114, 97, 108, 41, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 83, 101, 116, 83, 101, 116, 116, 105, 110, 103, 32, 60, 45, 32, 83, 101, 116, 116, 105, 110, 103, 83, 99, 111, 112, 101, 63, 32, 83, 101, 116, 116, 105, 110, 103, 78, 97, 109, 101, 10, + 83, 101, 116, 86, 97, 114, 105, 97, 98, 108, 101, 32, 60, 45, 32, 86, 97, 114, 105, 97, 98, 108, 101, 83, 99, 111, 112, 101, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 86, 97, 114, 105, 97, 98, 108, 101, 83, 99, 111, 112, 101, 32, 60, 45, 32, 39, 86, 65, 82, 73, 65, 66, 76, 69, 39, 10, + 83, 101, 116, 116, 105, 110, 103, 83, 99, 111, 112, 101, 32, 60, 45, 32, 76, 111, 99, 97, 108, 83, 99, 111, 112, 101, 32, 47, 32, 83, 101, 115, 115, 105, 111, 110, 83, 99, 111, 112, 101, 32, 47, 32, 71, 108, 111, 98, 97, 108, 83, 99, 111, 112, 101, 10, + 76, 111, 99, 97, 108, 83, 99, 111, 112, 101, 32, 60, 45, 32, 39, 76, 79, 67, 65, 76, 39, 10, + 83, 101, 115, 115, 105, 111, 110, 83, 99, 111, 112, 101, 32, 60, 45, 32, 39, 83, 69, 83, 83, 73, 79, 78, 39, 10, + 71, 108, 111, 98, 97, 108, 83, 99, 111, 112, 101, 32, 60, 45, 32, 39, 71, 76, 79, 66, 65, 76, 39, 10, + 83, 101, 116, 65, 115, 115, 105, 103, 110, 109, 101, 110, 116, 32, 60, 45, 32, 86, 97, 114, 105, 97, 98, 108, 101, 65, 115, 115, 105, 103, 110, 32, 86, 97, 114, 105, 97, 98, 108, 101, 76, 105, 115, 116, 10, + 86, 97, 114, 105, 97, 98, 108, 101, 65, 115, 115, 105, 103, 110, 32, 60, 45, 32, 39, 61, 39, 32, 47, 32, 39, 84, 79, 39, 10, + 86, 97, 114, 105, 97, 98, 108, 101, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 41, 10, + 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 66, 101, 103, 105, 110, 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 32, 47, 32, 82, 111, 108, 108, 98, 97, 99, 107, 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 32, 47, 32, 67, 111, 109, 109, 105, 116, 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 10, + 66, 101, 103, 105, 110, 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 32, 60, 45, 32, 83, 116, 97, 114, 116, 79, 114, 66, 101, 103, 105, 110, 32, 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 63, 32, 82, 101, 97, 100, 79, 114, 87, 114, 105, 116, 101, 63, 10, + 82, 111, 108, 108, 98, 97, 99, 107, 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 32, 60, 45, 32, 65, 98, 111, 114, 116, 79, 114, 82, 111, 108, 108, 98, 97, 99, 107, 32, 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 63, 10, + 67, 111, 109, 109, 105, 116, 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 32, 60, 45, 32, 67, 111, 109, 109, 105, 116, 79, 114, 69, 110, 100, 32, 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 63, 10, + 83, 116, 97, 114, 116, 79, 114, 66, 101, 103, 105, 110, 32, 60, 45, 32, 39, 83, 84, 65, 82, 84, 39, 32, 47, 32, 39, 66, 69, 71, 73, 78, 39, 10, + 84, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 32, 60, 45, 32, 39, 87, 79, 82, 75, 39, 32, 47, 32, 39, 84, 82, 65, 78, 83, 65, 67, 84, 73, 79, 78, 39, 10, + 82, 101, 97, 100, 79, 114, 87, 114, 105, 116, 101, 32, 60, 45, 32, 39, 82, 69, 65, 68, 39, 32, 82, 101, 97, 100, 79, 110, 108, 121, 79, 114, 82, 101, 97, 100, 87, 114, 105, 116, 101, 10, + 82, 101, 97, 100, 79, 110, 108, 121, 79, 114, 82, 101, 97, 100, 87, 114, 105, 116, 101, 32, 60, 45, 32, 82, 101, 97, 100, 79, 110, 108, 121, 32, 47, 32, 82, 101, 97, 100, 87, 114, 105, 116, 101, 10, + 82, 101, 97, 100, 79, 110, 108, 121, 32, 60, 45, 32, 39, 79, 78, 76, 89, 39, 10, + 82, 101, 97, 100, 87, 114, 105, 116, 101, 32, 60, 45, 32, 39, 87, 82, 73, 84, 69, 39, 10, + 65, 98, 111, 114, 116, 79, 114, 82, 111, 108, 108, 98, 97, 99, 107, 32, 60, 45, 32, 39, 65, 66, 79, 82, 84, 39, 32, 47, 32, 39, 82, 79, 76, 76, 66, 65, 67, 75, 39, 10, + 67, 111, 109, 109, 105, 116, 79, 114, 69, 110, 100, 32, 60, 45, 32, 39, 67, 79, 77, 77, 73, 84, 39, 32, 47, 32, 39, 69, 78, 68, 39, 10, + 85, 112, 100, 97, 116, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 87, 105, 116, 104, 67, 108, 97, 117, 115, 101, 63, 32, 39, 85, 80, 68, 65, 84, 69, 39, 32, 85, 112, 100, 97, 116, 101, 84, 97, 114, 103, 101, 116, 32, 85, 112, 100, 97, 116, 101, 83, 101, 116, 67, 108, 97, 117, 115, 101, 76, 105, 115, 116, 32, 70, 114, 111, 109, 67, 108, 97, 117, 115, 101, 63, 32, 87, 104, 101, 114, 101, 67, 108, 97, 117, 115, 101, 63, 32, 82, 101, 116, 117, 114, 110, 105, 110, 103, 67, 108, 97, 117, 115, 101, 63, 10, + 85, 112, 100, 97, 116, 101, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 83, 101, 116, 32, 47, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 83, 101, 116, 10, + 66, 97, 115, 101, 84, 97, 98, 108, 101, 83, 101, 116, 32, 60, 45, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 39, 83, 69, 84, 39, 10, + 66, 97, 115, 101, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 83, 101, 116, 32, 60, 45, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 85, 112, 100, 97, 116, 101, 65, 108, 105, 97, 115, 63, 32, 39, 83, 69, 84, 39, 10, + 85, 112, 100, 97, 116, 101, 65, 108, 105, 97, 115, 32, 60, 45, 32, 39, 65, 83, 39, 63, 32, 67, 111, 108, 73, 100, 10, + 85, 112, 100, 97, 116, 101, 83, 101, 116, 67, 108, 97, 117, 115, 101, 76, 105, 115, 116, 32, 60, 45, 32, 76, 105, 115, 116, 40, 85, 112, 100, 97, 116, 101, 83, 101, 116, 67, 108, 97, 117, 115, 101, 41, 10, + 85, 112, 100, 97, 116, 101, 83, 101, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 85, 112, 100, 97, 116, 101, 83, 101, 116, 69, 108, 101, 109, 101, 110, 116, 32, 47, 32, 85, 112, 100, 97, 116, 101, 83, 101, 116, 84, 117, 112, 108, 101, 10, + 85, 112, 100, 97, 116, 101, 83, 101, 116, 84, 117, 112, 108, 101, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 41, 41, 32, 39, 61, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 85, 112, 100, 97, 116, 101, 83, 101, 116, 69, 108, 101, 109, 101, 110, 116, 32, 60, 45, 32, 85, 112, 100, 97, 116, 101, 83, 101, 116, 67, 111, 108, 117, 109, 110, 84, 97, 114, 103, 101, 116, 32, 39, 61, 39, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 85, 112, 100, 97, 116, 101, 83, 101, 116, 67, 111, 108, 117, 109, 110, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 67, 111, 108, 117, 109, 110, 78, 97, 109, 101, 32, 68, 111, 116, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 42, 10, + 85, 115, 101, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 85, 83, 69, 39, 32, 85, 115, 101, 84, 97, 114, 103, 101, 116, 10, + 85, 115, 101, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 85, 115, 101, 84, 97, 114, 103, 101, 116, 67, 97, 116, 97, 108, 111, 103, 83, 99, 104, 101, 109, 97, 32, 47, 32, 83, 99, 104, 101, 109, 97, 78, 97, 109, 101, 65, 115, 85, 115, 101, 84, 97, 114, 103, 101, 116, 32, 47, 32, 67, 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, 65, 115, 85, 115, 101, 84, 97, 114, 103, 101, 116, 10, + 83, 99, 104, 101, 109, 97, 78, 97, 109, 101, 65, 115, 85, 115, 101, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 83, 99, 104, 101, 109, 97, 78, 97, 109, 101, 32, 47, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 67, 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, 65, 115, 85, 115, 101, 84, 97, 114, 103, 101, 116, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, 32, 47, 32, 83, 116, 114, 105, 110, 103, 76, 105, 116, 101, 114, 97, 108, 10, + 85, 115, 101, 84, 97, 114, 103, 101, 116, 67, 97, 116, 97, 108, 111, 103, 83, 99, 104, 101, 109, 97, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 78, 97, 109, 101, 32, 39, 46, 39, 32, 82, 101, 115, 101, 114, 118, 101, 100, 83, 99, 104, 101, 109, 97, 78, 97, 109, 101, 32, 68, 111, 116, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 42, 10, + 68, 111, 116, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 60, 45, 32, 39, 46, 39, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 86, 97, 99, 117, 117, 109, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 60, 45, 32, 39, 86, 65, 67, 85, 85, 77, 39, 32, 86, 97, 99, 117, 117, 109, 79, 112, 116, 105, 111, 110, 115, 63, 32, 65, 110, 97, 108, 121, 122, 101, 84, 97, 114, 103, 101, 116, 63, 10, + 86, 97, 99, 117, 117, 109, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 86, 97, 99, 117, 117, 109, 80, 97, 114, 101, 110, 115, 79, 112, 116, 105, 111, 110, 115, 32, 47, 32, 86, 97, 99, 117, 117, 109, 76, 101, 103, 97, 99, 121, 79, 112, 116, 105, 111, 110, 115, 10, + 86, 97, 99, 117, 117, 109, 80, 97, 114, 101, 110, 115, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 86, 97, 99, 117, 117, 109, 79, 112, 116, 105, 111, 110, 41, 41, 10, + 86, 97, 99, 117, 117, 109, 76, 101, 103, 97, 99, 121, 79, 112, 116, 105, 111, 110, 115, 32, 60, 45, 32, 79, 112, 116, 70, 117, 108, 108, 63, 32, 79, 112, 116, 70, 114, 101, 101, 122, 101, 63, 32, 79, 112, 116, 86, 101, 114, 98, 111, 115, 101, 63, 32, 79, 112, 116, 65, 110, 97, 108, 121, 122, 101, 63, 10, + 86, 97, 99, 117, 117, 109, 79, 112, 116, 105, 111, 110, 32, 60, 45, 32, 79, 112, 116, 65, 110, 97, 108, 121, 122, 101, 32, 47, 32, 79, 112, 116, 70, 114, 101, 101, 122, 101, 32, 47, 32, 79, 112, 116, 70, 117, 108, 108, 32, 47, 32, 79, 112, 116, 86, 101, 114, 98, 111, 115, 101, 32, 47, 32, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 10, + 79, 112, 116, 65, 110, 97, 108, 121, 122, 101, 32, 60, 45, 32, 65, 110, 97, 108, 121, 122, 101, 75, 101, 121, 119, 111, 114, 100, 10, + 79, 112, 116, 70, 117, 108, 108, 32, 60, 45, 32, 39, 70, 85, 76, 76, 39, 10, + 79, 112, 116, 70, 114, 101, 101, 122, 101, 32, 60, 45, 32, 39, 70, 82, 69, 69, 90, 69, 39, 10, + 79, 112, 116, 86, 101, 114, 98, 111, 115, 101, 32, 60, 45, 32, 39, 86, 69, 82, 66, 79, 83, 69, 39, 10, + 78, 97, 109, 101, 76, 105, 115, 116, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 41, 41, 10, + 0, }; +// clang-format on } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/keyword_categories.hpp b/src/duckdb/src/include/duckdb/parser/peg/keyword_categories.hpp new file mode 100644 index 000000000..df8de58f3 --- /dev/null +++ b/src/duckdb/src/include/duckdb/parser/peg/keyword_categories.hpp @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/parser/peg/keyword_categories.hpp +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/exception.hpp" + +namespace duckdb { + +//! Opaque, dialect-defined keyword category flags. +struct keyword_categories_t { // NOLINT +private: + using UNDERLYING_TYPE = uint8_t; + +public: + //! Category IDs are zero-based; the empty mask has no bits set. + static constexpr idx_t MAX_CATEGORY_ID = sizeof(UNDERLYING_TYPE) * 8; + + constexpr keyword_categories_t() : value(0) { + } + explicit constexpr keyword_categories_t(UNDERLYING_TYPE value) : value(value) { + } + + static constexpr keyword_categories_t CreateCategory(idx_t category_id) { + if (category_id >= MAX_CATEGORY_ID) { + throw InvalidInputException("Keyword category ID must be between 0 and %llu", MAX_CATEGORY_ID - 1); + } + return keyword_categories_t(static_cast(UNDERLYING_TYPE(1) << category_id)); + } + + explicit constexpr operator UNDERLYING_TYPE() const { + return value; + } + + constexpr bool operator==(keyword_categories_t other) const { + return value == other.value; + } + constexpr bool operator!=(keyword_categories_t other) const { + return value != other.value; + } + constexpr keyword_categories_t operator|(keyword_categories_t other) const { + return keyword_categories_t(static_cast(value | other.value)); + } + keyword_categories_t &operator|=(keyword_categories_t other) { + value |= other.value; + return *this; + } + constexpr keyword_categories_t operator&(keyword_categories_t other) const { + return keyword_categories_t(static_cast(value & other.value)); + } + constexpr keyword_categories_t operator~() const { + return keyword_categories_t(static_cast(~value)); + } + +private: + UNDERLYING_TYPE value; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/keyword_helper.hpp b/src/duckdb/src/include/duckdb/parser/peg/keyword_helper.hpp index cc5d72904..1d986f4f9 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/keyword_helper.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/keyword_helper.hpp @@ -9,27 +9,32 @@ #pragma once #include "duckdb/common/case_insensitive_map.hpp" +#include "duckdb/parser/peg/grammar_literal_table.hpp" #include "duckdb/parser/simplified_token.hpp" +#include "duckdb/parser/peg/literal_info.hpp" namespace duckdb { -enum class PEGKeywordCategory : uint8_t { - KEYWORD_NONE, - KEYWORD_UNRESERVED, - KEYWORD_RESERVED, - KEYWORD_TYPE_FUNC, - KEYWORD_COL_NAME, - KEYWORD_TYPE_NAME -}; +class GrammarLiteralTable; + +enum class SuggestionState : uint8_t; class PEGKeywordHelper { public: virtual ~PEGKeywordHelper() = default; public: - virtual bool KeywordCategoryType(const string &text, PEGKeywordCategory type) const = 0; - virtual bool IsKeyword(const string &text) const = 0; + LiteralInfo LookupKeyword(const string &text) const { + return GetLiteralTable().Lookup(text); + } + bool IsKeyword(const string &text) const { + return LookupKeyword(text).IsKeyword(); + } + //! Opaque flags accepted in this identifier position, computed when creating a matcher. + virtual keyword_categories_t GetIdentifierMask(SuggestionState type) const = 0; virtual vector KeywordList() const = 0; + //! Every helper provides an immutable table containing its literals and keyword flags. + virtual const GrammarLiteralTable &GetLiteralTable() const = 0; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/keyword_helper/default_keyword_maps.hpp b/src/duckdb/src/include/duckdb/parser/peg/keyword_helper/default_keyword_maps.hpp index 07bd09dd9..2cd19d5a1 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/keyword_helper/default_keyword_maps.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/keyword_helper/default_keyword_maps.hpp @@ -1,15 +1,21 @@ #pragma once #include "duckdb/common/case_insensitive_map.hpp" -#include "duckdb/parser/peg/keyword_helper.hpp" +#include "duckdb/parser/peg/literal_info.hpp" #include "duckdb/parser/simplified_token.hpp" namespace duckdb { +enum class SuggestionState : uint8_t; + class DefaultKeywordMaps { public: - bool IsKeywordOfCategory(const string &text, PEGKeywordCategory type) const; - bool IsKeyword(const string &text) const; + DUCKDB_API LiteralInfo LookupKeyword(const string &text, uint16_t literal_id = 0) const; + DUCKDB_API static keyword_categories_t GetIdentifierMask(SuggestionState type); + DUCKDB_API static KeywordCategory GetKeywordCategory(LiteralInfo info); + //! All recognized categories, in reserved, unreserved, type-function, column-name, type-name order. + DUCKDB_API static vector GetKeywordCategories(LiteralInfo info); + DUCKDB_API case_insensitive_map_t ToLiteralMap() const; vector ToList() const; public: @@ -18,6 +24,8 @@ class DefaultKeywordMaps { case_insensitive_set_t colname_keyword_map; case_insensitive_set_t typefunc_keyword_map; case_insensitive_set_t typename_keyword_map; + //! Keywords that can be used as a column alias without AS (orthogonal to the other categories) + case_insensitive_set_t bare_label_keyword_map; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/keyword_helper/duckdb_keyword_helper.hpp b/src/duckdb/src/include/duckdb/parser/peg/keyword_helper/duckdb_keyword_helper.hpp index 4e58bf314..c152a33bd 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/keyword_helper/duckdb_keyword_helper.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/keyword_helper/duckdb_keyword_helper.hpp @@ -13,18 +13,19 @@ class DuckDBKeywordHelper : public PEGKeywordHelper { static const DuckDBKeywordHelper &Instance(); public: - bool KeywordCategoryType(const std::string &text, const PEGKeywordCategory type) const override; - bool IsKeyword(const string &text) const override; + keyword_categories_t GetIdentifierMask(SuggestionState type) const override; + KeywordCategory GetKeywordCategory(const string &text) const; vector KeywordList() const override; + const GrammarLiteralTable &GetLiteralTable() const override { + return literal_table; + } private: - void InitializeKeywordMaps(); + static DefaultKeywordMaps InitializeKeywordMaps(); private: DefaultKeywordMaps keyword_maps; - -private: - bool initialized; + GrammarLiteralTable literal_table; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/keyword_helper/parsed_grammar_keyword_helper.hpp b/src/duckdb/src/include/duckdb/parser/peg/keyword_helper/parsed_grammar_keyword_helper.hpp index c5b890d8d..76fd70c80 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/keyword_helper/parsed_grammar_keyword_helper.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/keyword_helper/parsed_grammar_keyword_helper.hpp @@ -2,6 +2,7 @@ #include "duckdb/parser/peg/keyword_helper.hpp" #include "duckdb/parser/peg/keyword_helper/default_keyword_maps.hpp" +#include "duckdb/parser/peg/grammar_literal_table.hpp" namespace duckdb { @@ -12,12 +13,15 @@ class ParsedGrammarKeywordHelper : public PEGKeywordHelper { explicit ParsedGrammarKeywordHelper(const ParsedGrammar &grammar); public: - bool KeywordCategoryType(const string &text, PEGKeywordCategory type) const override; - bool IsKeyword(const string &text) const override; + keyword_categories_t GetIdentifierMask(SuggestionState type) const override; vector KeywordList() const override; + const GrammarLiteralTable &GetLiteralTable() const override { + return literal_table; + } private: DefaultKeywordMaps keyword_maps; + GrammarLiteralTable literal_table; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/literal_info.hpp b/src/duckdb/src/include/duckdb/parser/peg/literal_info.hpp new file mode 100644 index 000000000..94a1927ef --- /dev/null +++ b/src/duckdb/src/include/duckdb/parser/peg/literal_info.hpp @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/parser/peg/literal_info.hpp +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/parser/peg/keyword_categories.hpp" +#include + +namespace duckdb { + +//! A grammar-local literal ID and opaque keyword properties. +class LiteralInfo { +public: + static constexpr uint16_t MAX_LITERAL_ID = 0xFFFF; + +public: + //! Zero denotes an unknown literal with no keyword flags, such as a non-keyword identifier. + LiteralInfo() : literal_id(0) { + } + explicit LiteralInfo(uint16_t literal_id, keyword_categories_t category_flags = keyword_categories_t()) + : literal_id(literal_id), category_flags(category_flags) { + } + + //! Zero means no grammar-local ID has been assigned; keyword flags may still be present. + uint16_t LiteralId() const { + return literal_id; + } + + keyword_categories_t CategoryFlags() const { + return category_flags; + } + + void AddCategories(keyword_categories_t flags) { + category_flags |= flags; + } + + bool IsKeyword() const { + return category_flags != keyword_categories_t(); + } + + bool HasAnyFlags(keyword_categories_t mask) const { + return (category_flags & mask) != keyword_categories_t(); + } + + bool operator==(const LiteralInfo &other) const { + return literal_id == other.literal_id && category_flags == other.category_flags; + } + +private: + uint16_t literal_id = 0; + keyword_categories_t category_flags = keyword_categories_t(); +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/matcher.hpp b/src/duckdb/src/include/duckdb/parser/peg/matcher.hpp index 4079345ca..b4150dd88 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/matcher.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/matcher.hpp @@ -9,6 +9,7 @@ #pragma once #include "duckdb/common/arena_containers/arena_ptr.hpp" +#include "duckdb/common/arena_containers/arena_vector.hpp" #include "duckdb/common/string_util.hpp" #include "duckdb/common/identifier.hpp" #include "duckdb/common/vector.hpp" @@ -45,7 +46,8 @@ enum class SuggestionState : uint8_t { SUGGEST_TABLE_FUNCTION_NAME, SUGGEST_PRAGMA_NAME, SUGGEST_SETTING_NAME, - SUGGEST_RESERVED_VARIABLE + SUGGEST_RESERVED_VARIABLE, + SUGGEST_BARE_LABEL }; enum class CandidateType { KEYWORD, IDENTIFIER, LITERAL }; @@ -151,10 +153,10 @@ struct MatchContext { ArenaAllocator &process_allocator_p, idx_t &max_token_index_p, MatchMode mode_p = MatchMode::BUILD_PARSE_RESULT, IdentifierCaseMode identifier_case_mode_p = IdentifierCaseMode::PRESERVE_CASE, - bool use_heap_based_parser_p = false, ParserPackratCache *packrat_cache_p = nullptr) + ParserPackratCache *packrat_cache_p = nullptr) : suggestions(suggestions_p), allocator(allocator_p), process_allocator(process_allocator_p), max_token_index(max_token_index_p), identifier_case_mode(identifier_case_mode_p), - packrat_cache(packrat_cache_p), mode(mode_p), use_heap_based_parser(use_heap_based_parser_p) { + packrat_cache(packrat_cache_p), mode(mode_p) { } vector &suggestions; @@ -164,7 +166,6 @@ struct MatchContext { IdentifierCaseMode identifier_case_mode; ParserPackratCache *packrat_cache; MatchMode mode; - bool use_heap_based_parser; }; struct MatchState { @@ -235,7 +236,7 @@ class MatchStep { private: MatchStep(optional child_p, optional result_p) - : child(std::move(child_p)), result(std::move(result_p)) { + : child(std::move(child_p)), result(result_p) { } private: @@ -265,6 +266,31 @@ enum class MatcherType { CUSTOM }; +//! Over-approximation of the tokens a matcher can start with (its FIRST set), computed once per grammar. Lets the +//! matcher skip sub-matchers that certainly cannot match at the current token. +struct MatcherFirstSet { + bool computed = false; + //! the matcher can succeed without consuming a token + bool nullable = false; + //! no information: never skip + bool any = false; + //! MatcherTokenClass bits + uint8_t class_mask = 0; + //! bitset over grammar literal ids (keywords and symbols) + vector literals; + optional_ptr table; + + bool HasLiteral(idx_t literal_id) const { + auto word = literal_id / 64; + return word < literals.size() && (literals[word] >> (literal_id % 64)) & 1; + } + DUCKDB_API void AddLiteral(idx_t literal_id); + //! Merge other into this set, returns whether anything changed + DUCKDB_API bool Merge(const MatcherFirstSet &other); + //! False only if the matcher certainly cannot match at the current token + DUCKDB_API bool MightMatch(MatchState &state) const; +}; + class Matcher { public: explicit Matcher(MatcherType type = MatcherType::CUSTOM) : type(type) { @@ -299,7 +325,12 @@ class Matcher { bool HasName() const { return !name.empty(); } - string GetName() const; + //! The stored name, which lives as long as the grammar and can therefore be referenced by a parse result + const string &GetName() const { + return name; + } + //! The name to print for this matcher, which for an unnamed one renders the grammar it matches + string GetPrintName() const; optional_idx GetPackratId() const { return packrat_id; } @@ -309,6 +340,20 @@ class Matcher { bool IsPackratMemoized() const { return packrat_memoized; } + //! See ParsedGrammar::SetTransformProcess + void SetCollapsible() { + collapsible = true; + } + bool IsCollapsible() const { + return collapsible; + } + //! The matcher runs the built-in implementation of its type (set by MatcherFactory, never for derived matchers) + void SetStructural() { + structural = true; + } + bool IsStructural() const { + return structural; + } public: template @@ -327,15 +372,23 @@ class Matcher { return reinterpret_cast(*this); } +public: + MatcherFirstSet first_set; + protected: friend class MatcherAllocator; MatcherType type; string name; optional_idx packrat_id; bool packrat_memoized = false; + bool collapsible = false; + bool structural = false; optional_ptr rule; }; +//! Compute the FIRST sets of all matchers reachable from root +DUCKDB_API void ComputeFirstSets(Matcher &root, const GrammarLiteralTable &table); + class AtomicMatcher : public Matcher { public: explicit AtomicMatcher(MatcherType type) : Matcher(type) { @@ -346,6 +399,10 @@ class AtomicMatcher : public Matcher { } DUCKDB_API arena_ptr StartMatch(MatchState &state) const final; virtual MatcherResult MatchAtomic(MatchState &state) const = 0; + //! Describes the tokens this matcher can start with; the default (no information) never skips the matcher + virtual void InitializeFirstSet(MatcherFirstSet &first_set, const GrammarLiteralTable &table) const { + first_set.any = true; + } }; class KeywordInfo { @@ -371,10 +428,42 @@ class MatcherAllocator { class ParseResultAllocator { public: - optional_ptr Allocate(unique_ptr parse_result); + ParseResultAllocator(); + ~ParseResultAllocator(); + + template + optional_ptr Make(ARGS &&... args) { + static_assert(std::is_base_of::value, "Expected a parse result"); + auto result = arena.Make(std::forward(args)...); + if (ParseResultNeedsDestructor::value) { + // held by an owner before growing the list, so the result is still destroyed if the growth throws + arena_ptr owned(result); + pending_destructors.push_back(std::move(owned)); + } + return optional_ptr(result); + } + + //! Copy a collected set of children into the arena, where it lives as long as the results it belongs to + unsafe_array_ptr> MakeChildren(const arena_vector> &children) { + auto count = children.size(); + arena.AlignNext(); + // an empty set still takes an address from the arena, which costs nothing and keeps the span non-null + auto target = + reinterpret_cast *>(arena.Allocate(count * sizeof(reference))); + if (count > 0) { + memcpy(static_cast(target), static_cast(children.data()), + count * sizeof(reference)); + } + return unsafe_array_ptr>(target, count); + } private: - vector> parse_results; + ArenaAllocator arena; + //! Dropping the arena reclaims the memory of every result at once but calls no destructors, so a result that + //! owns something has to be destroyed before that happens. An `arena_ptr` destroys what it points at without + //! freeing it, which is all these are here for. Only the node types that `ParseResultNeedsDestructor` selects end + //! up in the list, and nothing ever reads it. + arena_vector> pending_destructors; }; template @@ -383,15 +472,23 @@ arena_ptr MatchState::Make(ARGS &&... args) { return arena_ptr(context.process_allocator.Make(std::forward(args)...)); } +void ParseResult::SetNameFrom(const CompiledGrammarRule &rule_p) { + name = &rule_p.name; +} + +void ParseResult::SetNameFrom(const Matcher &matcher_p) { + name = &matcher_p.GetName(); +} + template MatcherResult MatchState::AllocateParseResult(ARGS &&... args) { if (!BuildParseResult()) { return MatcherResult::Success(); } - auto result = context.allocator.Allocate(make_uniq(std::forward(args)...)); + auto result = context.allocator.Make(std::forward(args)...); if (rule) { result->SetRule(*rule); - result->name = rule->name; + result->SetNameFrom(*rule); } return MatcherResult::Success(result); } diff --git a/src/duckdb/src/include/duckdb/parser/peg/matcher/arithmetic_operator_matcher.hpp b/src/duckdb/src/include/duckdb/parser/peg/matcher/arithmetic_operator_matcher.hpp index 2ac185f48..93f31f77f 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/matcher/arithmetic_operator_matcher.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/matcher/arithmetic_operator_matcher.hpp @@ -36,6 +36,10 @@ class ArithmeticOperatorMatcher : public AtomicMatcher { return "ARITHMETICOPERATOR"; } + void InitializeFirstSet(MatcherFirstSet &first_set, const GrammarLiteralTable &table) const override { + first_set.class_mask = MatcherTokenClass::OPERATOR; + } + private: bool MatchArithmeticOperator(MatchState &state) const { auto token = state.token_iterator.Current(); diff --git a/src/duckdb/src/include/duckdb/parser/peg/matcher/choice_matcher.hpp b/src/duckdb/src/include/duckdb/parser/peg/matcher/choice_matcher.hpp index 89b31ee3b..e76912036 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/matcher/choice_matcher.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/matcher/choice_matcher.hpp @@ -30,7 +30,7 @@ class ChoiceMatcher : public Matcher { if (!result.empty()) { result += " / "; } - result += matcher.get().GetName(); + result += matcher.get().GetPrintName(); } return result; } diff --git a/src/duckdb/src/include/duckdb/parser/peg/matcher/identifier_matcher.hpp b/src/duckdb/src/include/duckdb/parser/peg/matcher/identifier_matcher.hpp index fbb302b73..a8163c699 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/matcher/identifier_matcher.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/matcher/identifier_matcher.hpp @@ -12,14 +12,26 @@ class IdentifierMatcher : public AtomicMatcher { public: IdentifierMatcher(SuggestionState suggestion_type, const PEGKeywordHelper &keyword_helper_p) - : AtomicMatcher(TYPE), suggestion_type(suggestion_type), keyword_helper(keyword_helper_p) { + : AtomicMatcher(TYPE), suggestion_type(suggestion_type), literal_table(keyword_helper_p.GetLiteralTable()), + identifier_mask(keyword_helper_p.GetIdentifierMask(suggestion_type)) { + } + + static bool IsQuoteDelimiter(char c) { + return c == '"' || c == '`'; } bool IsQuoted(const string &text) const { - if (text.front() == '"' && text.back() == '"') { - return true; + if (text.size() < 2) { + return false; } - return false; + return IsQuoteDelimiter(text.front()) && text.front() == text.back(); + } + + //! Strip the surrounding delimiters of a quoted identifier and un-double any escaped delimiters + static void UnquoteIdentifier(string &text) { + const string delimiter(1, text.front()); + text = text.substr(1, text.size() - 2); + text = StringUtil::Replace(text, delimiter + delimiter, delimiter); } bool IsSingleQuoted(const string &text) const { @@ -42,6 +54,10 @@ class IdentifierMatcher : public AtomicMatcher { if (Tokenizer::CharacterIsInitialNumber(text[0])) { return false; } + if (text.back() == '\'') { + // a prefixed string literal such as x'41' or e'ab' + return false; + } return Tokenizer::CharacterIsKeyword(text[0]); } @@ -63,8 +79,7 @@ class IdentifierMatcher : public AtomicMatcher { string result_text = token_text; if (IsQuoted(result_text)) { - result_text = result_text.substr(1, result_text.size() - 2); - result_text = StringUtil::Replace(result_text, "\"\"", "\""); + UnquoteIdentifier(result_text); } else if (IsSingleQuoted(result_text) && SupportsStringLiteral()) { // a single-quoted token in a table or file-name position is a path, so it is unwrapped but never folded result_text = result_text.substr(1, result_text.size() - 2); @@ -110,18 +125,6 @@ class IdentifierMatcher : public AtomicMatcher { } } - PEGKeywordCategory GetAllowedCategory() const { - switch (suggestion_type) { - case SuggestionState::SUGGEST_TYPE_NAME: - return PEGKeywordCategory::KEYWORD_TYPE_NAME; - case SuggestionState::SUGGEST_SCALAR_FUNCTION_NAME: - case SuggestionState::SUGGEST_TABLE_FUNCTION_NAME: - return PEGKeywordCategory::KEYWORD_TYPE_FUNC; - default: - return PEGKeywordCategory::KEYWORD_COL_NAME; - } - } - SuggestionType AddSuggestionInternal(MatchState &state) const override { state.AddSuggestion(MatcherSuggestion(suggestion_type)); return SuggestionType::MANDATORY; @@ -158,15 +161,14 @@ class IdentifierMatcher : public AtomicMatcher { } } + void InitializeFirstSet(MatcherFirstSet &first_set, const GrammarLiteralTable &table) const override { + first_set.class_mask = MatcherTokenClass::WORD; + } + private: - bool IsAllowedKeyword(const string &token_text) const { - if (!keyword_helper.IsKeyword(token_text)) { - return true; - } - if (keyword_helper.KeywordCategoryType(token_text, PEGKeywordCategory::KEYWORD_UNRESERVED)) { - return true; - } - return keyword_helper.KeywordCategoryType(token_text, GetAllowedCategory()); + bool IsAllowedKeyword(TokenIterator &tokens) const { + auto info = tokens.CurrentLiteralInfo(literal_table); + return !info.IsKeyword() || info.HasAnyFlags(identifier_mask); } bool MatchIdentifier(MatchState &state) const { @@ -175,7 +177,7 @@ class IdentifierMatcher : public AtomicMatcher { return false; } auto &token_text = token->text; - if (!IsAllowedKeyword(token_text) || !IsIdentifier(token_text)) { + if (!IsAllowedKeyword(state.token_iterator) || !IsIdentifier(token_text)) { return false; } state.token_iterator.Advance(); @@ -184,7 +186,8 @@ class IdentifierMatcher : public AtomicMatcher { } SuggestionState suggestion_type; - const PEGKeywordHelper &keyword_helper; + const GrammarLiteralTable &literal_table; + const keyword_categories_t identifier_mask; }; class ReservedIdentifierMatcher : public IdentifierMatcher { @@ -215,8 +218,7 @@ class ReservedIdentifierMatcher : public IdentifierMatcher { // unlike IdentifierMatcher this rule does not unwrap path literals, it only has to avoid folding them const bool is_path_literal = IsSingleQuoted(result_text) && SupportsStringLiteral(); if (IsQuoted(result_text)) { - result_text = result_text.substr(1, result_text.size() - 2); - result_text = StringUtil::Replace(result_text, "\"\"", "\""); + UnquoteIdentifier(result_text); } else if (!is_path_literal) { state.FoldIdentifier(result_text); } diff --git a/src/duckdb/src/include/duckdb/parser/peg/matcher/keyword_matcher.hpp b/src/duckdb/src/include/duckdb/parser/peg/matcher/keyword_matcher.hpp index e433215ce..cc111d606 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/matcher/keyword_matcher.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/matcher/keyword_matcher.hpp @@ -13,6 +13,14 @@ class KeywordMatcher : public AtomicMatcher { explicit KeywordMatcher(string keyword_p, const KeywordInfo &info) : AtomicMatcher(TYPE), keyword(std::move(keyword_p)), info(info) { } + KeywordMatcher(string keyword_p, const KeywordInfo &info, const PEGKeywordHelper &keyword_helper) + : KeywordMatcher(std::move(keyword_p), info) { + literal_table = keyword_helper.GetLiteralTable(); + literal_info = literal_table->Lookup(keyword); + if (!literal_info.LiteralId()) { + literal_table = nullptr; + } + } MatcherResult MatchAtomic(MatchState &state) const override { auto token = state.token_iterator.Current(); @@ -27,7 +35,7 @@ class KeywordMatcher : public AtomicMatcher { } auto result = state.AllocateParseResult(token_text, start_offset, token_length); if (result.HasParseResult()) { - result.GetParseResult()->name = name; + result.GetParseResult()->SetNameFrom(*this); } return result; } @@ -44,13 +52,29 @@ class KeywordMatcher : public AtomicMatcher { return "'" + keyword + "'"; } + //! Custom matchers must not be filtered using ordinary literal semantics by default. + virtual optional_idx GetDispatchLiteral(const GrammarLiteralTable &) const { + return optional_idx(); + } + + void InitializeFirstSet(MatcherFirstSet &first_set, const GrammarLiteralTable &table) const override { + auto literal = GetDispatchLiteral(table); + if (literal.IsValid()) { + first_set.AddLiteral(literal.GetIndex()); + } else { + first_set.any = true; + } + } + private: bool MatchKeyword(MatchState &state) const { auto token = state.token_iterator.Current(); if (!token) { return false; } - if (StringUtil::CIEquals(keyword, token->text)) { + const auto matches = literal_table ? state.token_iterator.CurrentLiteralInfo(*literal_table) == literal_info + : StringUtil::CIEquals(keyword, token->text); + if (matches) { // move to the next token state.token_iterator.Advance(); state.UpdateMaxTokenIndex(); @@ -62,6 +86,10 @@ class KeywordMatcher : public AtomicMatcher { private: const string keyword; const KeywordInfo info; + +protected: + optional_ptr literal_table; + LiteralInfo literal_info; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/matcher/list_matcher.hpp b/src/duckdb/src/include/duckdb/parser/peg/matcher/list_matcher.hpp index 4cf93c474..4d6f6b56e 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/matcher/list_matcher.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/matcher/list_matcher.hpp @@ -37,7 +37,7 @@ class ListMatcher : public Matcher { if (!result.empty()) { result += " "; } - result += matcher.get().GetName(); + result += matcher.get().GetPrintName(); } return "(" + result + ")"; } diff --git a/src/duckdb/src/include/duckdb/parser/peg/matcher/literal_choice_matcher.hpp b/src/duckdb/src/include/duckdb/parser/peg/matcher/literal_choice_matcher.hpp new file mode 100644 index 000000000..d078f4d5d --- /dev/null +++ b/src/duckdb/src/include/duckdb/parser/peg/matcher/literal_choice_matcher.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "duckdb/parser/peg/matcher/choice_matcher.hpp" + +namespace duckdb { + +class LiteralChoiceMatcher final : public ChoiceMatcher { +public: + LiteralChoiceMatcher(vector> &&matchers, const GrammarLiteralTable &table_p, + unordered_map &&literal_children_p) + : ChoiceMatcher(std::move(matchers)), table(table_p), literal_children(std::move(literal_children_p)) { + } + + arena_ptr StartMatch(MatchState &state) const override; + +private: + const GrammarLiteralTable &table; + unordered_map literal_children; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/matcher/number_literal_matcher.hpp b/src/duckdb/src/include/duckdb/parser/peg/matcher/number_literal_matcher.hpp index cb6fc468e..b7358cb14 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/matcher/number_literal_matcher.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/matcher/number_literal_matcher.hpp @@ -29,7 +29,7 @@ class NumberLiteralMatcher : public AtomicMatcher { state.token_iterator.SetPreviousTokenType(TokenType::NUMBER_LITERAL); auto result = state.AllocateParseResult(token_text, start_offset, token_length); if (result.HasParseResult()) { - result.GetParseResult()->name = name; + result.GetParseResult()->SetNameFrom(*this); } return result; } @@ -42,6 +42,10 @@ class NumberLiteralMatcher : public AtomicMatcher { return "NUMBER_LITERAL"; } + void InitializeFirstSet(MatcherFirstSet &first_set, const GrammarLiteralTable &table) const override { + first_set.class_mask = MatcherTokenClass::NUMBER; + } + private: static bool MatchNumberLiteral(MatchState &state) { auto token = state.token_iterator.Current(); diff --git a/src/duckdb/src/include/duckdb/parser/peg/matcher/operator_matcher.hpp b/src/duckdb/src/include/duckdb/parser/peg/matcher/operator_matcher.hpp index 9f369448d..581774acc 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/matcher/operator_matcher.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/matcher/operator_matcher.hpp @@ -6,12 +6,15 @@ namespace duckdb { +enum class OperatorMatcherMode : uint8_t { GENERIC_PRECEDENCE, ALL_OPERATORS }; + class OperatorMatcher : public AtomicMatcher { public: static constexpr MatcherType TYPE = MatcherType::OPERATOR; public: - explicit OperatorMatcher() : AtomicMatcher(TYPE) { + explicit OperatorMatcher(OperatorMatcherMode mode_p = OperatorMatcherMode::GENERIC_PRECEDENCE) + : AtomicMatcher(TYPE), mode(mode_p) { } MatcherResult MatchAtomic(MatchState &state) const override { @@ -36,6 +39,11 @@ class OperatorMatcher : public AtomicMatcher { return "OPERATOR"; } + void InitializeFirstSet(MatcherFirstSet &first_set, const GrammarLiteralTable &table) const override { + first_set.class_mask = mode == OperatorMatcherMode::GENERIC_PRECEDENCE ? MatcherTokenClass::GENERIC_OPERATOR + : MatcherTokenClass::OPERATOR; + } + private: bool MatchOperator(MatchState &state) const { auto token = state.token_iterator.Current(); @@ -43,26 +51,11 @@ class OperatorMatcher : public AtomicMatcher { return false; } auto &token_text = token->text; - // Exclude the lambda arrow and JSON arrow — these have dedicated grammar roles - if (token_text == "->" || token_text == "->>") { - return false; - } - // Single-character operators are handled at specific precedence levels (comparison, additive, etc.) - if (token_text.size() == 1) { - return false; - } - // Exclude known comparison operators — handled by ComparisonExpression, not as function calls - if (token_text == "<=" || token_text == ">=" || token_text == "!=" || token_text == "==" || - token_text == "<>") { - return false; - } - // Exclude LIKE/SIMILAR operators — handled by LikeVariations at a higher precedence level - if (token_text == "~~" || token_text == "~~*" || token_text == "~~~" || token_text == "~*" || - token_text == "!~~" || token_text == "!~~*" || token_text == "!~" || token_text == "!~*") { + if (mode == OperatorMatcherMode::GENERIC_PRECEDENCE && HasSpecialPrecedence(token_text)) { return false; } for (auto &c : token_text) { - if (!IsOperatorChar(c)) { + if (!Tokenizer::CharacterIsOperator(c)) { return false; } } @@ -71,28 +64,11 @@ class OperatorMatcher : public AtomicMatcher { return true; } -protected: - bool IsOperatorChar(char c) const { - switch (c) { - case '+': - case '-': - case '*': - case '/': - case '%': - case '^': - case '<': - case '>': - case '=': - case '~': - case '!': - case '@': - case '&': - case '|': - return true; - default: - return false; - } - } +public: + DUCKDB_API static bool HasSpecialPrecedence(const string &operator_name); + +private: + OperatorMatcherMode mode; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/matcher/optional_matcher.hpp b/src/duckdb/src/include/duckdb/parser/peg/matcher/optional_matcher.hpp index 66fcc8227..fcb770c1f 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/matcher/optional_matcher.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/matcher/optional_matcher.hpp @@ -21,7 +21,10 @@ class OptionalMatcher : public Matcher { } string ToString() const override { - return matcher.GetName() + "?"; + return matcher.GetPrintName() + "?"; + } + Matcher &GetChildMatcher() { + return matcher; } const Matcher &GetChildMatcher() const { return matcher; diff --git a/src/duckdb/src/include/duckdb/parser/peg/matcher/repeat_matcher.hpp b/src/duckdb/src/include/duckdb/parser/peg/matcher/repeat_matcher.hpp index 7053f5c88..48ac642ad 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/matcher/repeat_matcher.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/matcher/repeat_matcher.hpp @@ -20,7 +20,10 @@ class RepeatMatcher : public Matcher { } string ToString() const override { - return element.GetName() + "*"; + return element.GetPrintName() + "*"; + } + Matcher &GetChildMatcher() { + return element; } const Matcher &GetChildMatcher() const { return element; diff --git a/src/duckdb/src/include/duckdb/parser/peg/matcher/string_literal_matcher.hpp b/src/duckdb/src/include/duckdb/parser/peg/matcher/string_literal_matcher.hpp index 4ae03939a..086a51ecc 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/matcher/string_literal_matcher.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/matcher/string_literal_matcher.hpp @@ -55,7 +55,7 @@ class StringLiteralMatcher : public AtomicMatcher { auto result = state.AllocateParseResult(stripped_string, string_info.type, start_offset, token_length); if (result.HasParseResult()) { - result.GetParseResult()->name = name; + result.GetParseResult()->SetNameFrom(*this); } return result; } @@ -68,6 +68,10 @@ class StringLiteralMatcher : public AtomicMatcher { return "STRING_LITERAL"; } + void InitializeFirstSet(MatcherFirstSet &first_set, const GrammarLiteralTable &table) const override { + first_set.class_mask = MatcherTokenClass::STRING; + } + private: static bool IsStringLiteral(const MatcherToken &token, const SpecialStringInfo &string_info) { idx_t dollar_quote_delimiter_length; diff --git a/src/duckdb/src/include/duckdb/parser/peg/matcher_factory.hpp b/src/duckdb/src/include/duckdb/parser/peg/matcher_factory.hpp index 72ad6bce4..b1387caab 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/matcher_factory.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/matcher_factory.hpp @@ -8,8 +8,10 @@ namespace duckdb { struct CompiledGrammar; struct PEGExpression; - class MatcherFactory; +struct CompiledGrammarRule; + +using compiled_rules_map_t = case_insensitive_map_t>; //! Class for building matchers class MatcherFactory { @@ -28,8 +30,8 @@ class MatcherFactory { }; public: - MatcherFactory(MatcherAllocator &allocator, const ParsedGrammar &grammar_p, CompiledGrammar &compiled_p, - terminal_rule_overrides_t terminal_rule_overrides_p); + MatcherFactory(MatcherAllocator &allocator, const ParsedGrammar &grammar_p, const compiled_rules_map_t &rules, + const PEGKeywordHelper &keyword_helper, terminal_rule_overrides_t terminal_rule_overrides_p); virtual ~MatcherFactory() = default; public: @@ -37,7 +39,7 @@ class MatcherFactory { //! Look up a matcher for a rule that was built by CreateRootMatcher. Throws if the rule has not been built. Matcher &GetMatcher(const string &rule_name); -private: +protected: // Base primitives KeywordMatcher &Keyword(const string &keyword) const; ListMatcher &List() const; @@ -52,8 +54,6 @@ class MatcherFactory { virtual unique_ptr CreateOptional(Matcher &matcher) const; virtual unique_ptr CreateRepeat(Matcher &matcher) const; - void SetRuleOverrides(); - void AddKeywordOverride(const char *name, KeywordInfo keyword_info); void AddRuleOverride(const char *name, unique_ptr &&matcher_p); void AddPackratMemoizedRule(const char *name); @@ -63,10 +63,14 @@ class MatcherFactory { Matcher &CreateMatcher(const PEGExpression &expression, const string_map_t ¶meter_map, vector> ¶meters); +private: + optional_ptr GetRule(const string &name) const; + private: MatcherAllocator &allocator; const ParsedGrammar &grammar; - CompiledGrammar &compiled; + const compiled_rules_map_t &rules; + const PEGKeywordHelper &keyword_helper; //! Keeps terminal rule names alive while the matcher graph is constructed. terminal_rule_overrides_t terminal_rule_overrides; string_map_t> matchers; diff --git a/src/duckdb/src/include/duckdb/parser/peg/matcher_stack.hpp b/src/duckdb/src/include/duckdb/parser/peg/matcher_stack.hpp index 57f7d30a9..d9b87cafa 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/matcher_stack.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/matcher_stack.hpp @@ -40,6 +40,9 @@ struct MatchStackFrame { optional child_result; optional result; PackratMatchState packrat_state; + //! The position of the process arena when the frame was pushed - the arena is rewound to it when the frame is + //! popped + ArenaAllocator::Position process_arena_position; }; class MatchStack { diff --git a/src/duckdb/src/include/duckdb/parser/peg/matcher_token.hpp b/src/duckdb/src/include/duckdb/parser/peg/matcher_token.hpp index a5eb32eeb..cbd5dd427 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/matcher_token.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/matcher_token.hpp @@ -9,15 +9,33 @@ #pragma once #include "duckdb/common/string.hpp" +#include "duckdb/common/winapi.hpp" #include "duckdb/parser/peg/token_type.hpp" +#include "duckdb/parser/peg/grammar_literal_table.hpp" namespace duckdb { +//! Token classes for FIRST-set checks, derived from the TokenType assigned by the tokenizer +//! Each class is a superset of the tokens the corresponding atomic matchers accept +struct MatcherTokenClass { + static constexpr uint8_t WORD = 1; + static constexpr uint8_t OPERATOR = 2; + //! an operator without a precedence level of its own in the grammar + static constexpr uint8_t GENERIC_OPERATOR = 4; + static constexpr uint8_t STRING = 8; + static constexpr uint8_t NUMBER = 16; +}; + +//! The token class of a token as emitted by the tokenizer (implemented in base_tokenizer.cpp) +DUCKDB_API uint8_t ComputeMatcherTokenClass(TokenType type, const string &text); + +//! text, length and token_class are set together on construction: replace a token instead of editing its text struct MatcherToken { // NOLINTNEXTLINE: allow implicit conversion from text MatcherToken(string text_p, idx_t offset_p, TokenType type_p, bool unterminated_p = false) : type(type_p), text(std::move(text_p)), offset(offset_p), unterminated(unterminated_p) { length = text.length(); + token_class = ComputeMatcherTokenClass(type, text); } TokenType type; @@ -27,6 +45,24 @@ struct MatcherToken { bool unterminated = false; bool preceded_by_newline = false; bool preceded_by_block_comment = false; + //! MatcherTokenClass bits - fixed at tokenization, unaffected by later re-typing of the token + uint8_t token_class = 0; + + LiteralInfo GetLiteralInfo(const GrammarLiteralTable &table) { + if (literal_table_id != table.CacheId()) { + literal_info = table.Lookup(text); + literal_table_id = table.CacheId(); + } + return literal_info; + } + + void ResetLiteralInfo() { + literal_table_id = 0; + } + +private: + LiteralInfo literal_info; + uint64_t literal_table_id = 0; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/parsed_grammar.hpp b/src/duckdb/src/include/duckdb/parser/peg/parsed_grammar.hpp index db381858f..76c22f1f2 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/parsed_grammar.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/parsed_grammar.hpp @@ -7,6 +7,7 @@ #pragma once +#include "duckdb/common/arena_containers/arena_ptr.hpp" #include "duckdb/common/case_insensitive_map.hpp" #include "duckdb/common/types/string_heap.hpp" #include "duckdb/parser/peg/peg_parser.hpp" @@ -19,17 +20,27 @@ namespace duckdb { class ParseResult; class PEGTransformer; class PEGKeywordHelper; +class DialectExtension; class Matcher; class TransformProcess; class GrammarChange; using grammar_transform_process_function_t = - std::function(PEGTransformer &, ParseResult &)>; + std::function(PEGTransformer &, ParseResult &)>; using grammar_cursor_function_t = std::function; using terminal_rule_overrides_t = case_insensitive_map_t>; using terminal_rule_matcher_factory_t = std::function(const PEGKeywordHelper &)>; using terminal_rule_override_callback_t = std::function; +//! How a rule contributes to the expression depth while it is transformed (see ParsedGrammar::SetExpressionDepth) +enum class ExpressionDepthKind : uint8_t { + NONE, + //! one level: the rule nests an expression or type + NESTING, + //! one level per repeated element: the rule's repetitions nest its result, like operator chains + CHAIN +}; + struct ParsedGrammarRule { ParsedGrammarRule(string name_p, PEGRule recipe_p) : name(std::move(name_p)), recipe(std::move(recipe_p)) { } @@ -37,6 +48,12 @@ struct ParsedGrammarRule { string name; PEGRule recipe; grammar_transform_process_function_t transform_process; + //! See ParsedGrammar::SetTransformProcess + bool collapsible = false; + //! See ParsedGrammar::AddRule + bool suppress_suggestions = false; + //! See ParsedGrammar::SetExpressionDepth + ExpressionDepthKind expression_depth = ExpressionDepthKind::NONE; }; //! Mutable, owning representation of a PEG grammar before matcher compilation. @@ -52,8 +69,11 @@ class ParsedGrammar { DUCKDB_API static ParsedGrammar CreateDefault(); DUCKDB_API optional_ptr GetRule(const string &rule_name) const; + //! A rule with suppress_suggestions set contributes no autocomplete suggestions. This keeps catch-all rules, such + //! as a bare expression accepted as a statement, from polluting completion at the position where they apply. DUCKDB_API void AddRule(const string &rule_definition, - grammar_transform_process_function_t transform_process = nullptr); + grammar_transform_process_function_t transform_process = nullptr, + bool suppress_suggestions = false); DUCKDB_API void AddChoice(const string &rule_name, const string &choice, const grammar_cursor_function_t &find_cursor = nullptr); DUCKDB_API void PrependChoice(const string &rule_name, const string &choice, @@ -63,12 +83,21 @@ class ParsedGrammar { const grammar_cursor_function_t &find_cursor); DUCKDB_API void ReplaceRule(const string &rule_definition, grammar_transform_process_function_t transform_process = nullptr); - DUCKDB_API void SetTransformProcess(const string &rule_name, - grammar_transform_process_function_t transform_process); + //! A collapsible transform promises to return its only child's result unchanged whenever the rule matched no + //! other child with a result, such as a level of the operator precedence hierarchy that matched no tail. The + //! matcher then skips building a result for the rule and the child is transformed as itself. Setting a new + //! transform clears that promise unless the caller repeats it, since it is a property of the transform. + DUCKDB_API void SetTransformProcess(const string &rule_name, grammar_transform_process_function_t transform_process, + bool collapsible = false, bool suppress_suggestions = false); + //! Declares how transforming the rule nests the resulting expression, so that max_expression_depth can be enforced + //! while transforming: the depth of the expression trees is bounded by the levels of the rules on the stack + DUCKDB_API void SetExpressionDepth(const string &rule_name, ExpressionDepthKind kind); DUCKDB_API void AddTerminalRuleOverride(const string &rule_name, terminal_rule_matcher_factory_t matcher_factory); private: + friend class DialectExtension; friend class MatcherFactory; + friend class GrammarLiteralTable; friend struct CompiledGrammar; friend class PEGTransformerFactory; friend class GrammarChange; @@ -90,14 +119,23 @@ class ParsedGrammar { //! Immutable semantic data referenced directly by matchers and parse results. struct CompiledGrammarRule { - CompiledGrammarRule(string name_p, grammar_transform_process_function_t transform_process_p) - : name(std::move(name_p)), transform_process(std::move(transform_process_p)) { + CompiledGrammarRule(string name_p, grammar_transform_process_function_t transform_process_p, + bool collapsible_p = false, ExpressionDepthKind expression_depth_p = ExpressionDepthKind::NONE, + bool suppress_suggestions_p = false) + : name(std::move(name_p)), transform_process(std::move(transform_process_p)), collapsible(collapsible_p), + suppress_suggestions(suppress_suggestions_p), expression_depth(expression_depth_p) { } - unique_ptr StartTransform(PEGTransformer &transformer, ParseResult &parse_result) const; + arena_ptr StartTransform(PEGTransformer &transformer, ParseResult &parse_result) const; string name; grammar_transform_process_function_t transform_process; + //! See ParsedGrammar::SetTransformProcess + bool collapsible; + //! See ParsedGrammar::AddRule + bool suppress_suggestions; + //! See ParsedGrammar::SetExpressionDepth + ExpressionDepthKind expression_depth; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/parser_packrat.hpp b/src/duckdb/src/include/duckdb/parser/peg/parser_packrat.hpp index 75f444fdf..bd15b4d82 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/parser_packrat.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/parser_packrat.hpp @@ -10,6 +10,7 @@ #include "duckdb/common/common.hpp" #include "duckdb/common/optional_ptr.hpp" #include "duckdb/common/unordered_map.hpp" +#include "duckdb/common/arena_containers/arena_unordered_map.hpp" namespace duckdb { class Matcher; @@ -37,14 +38,14 @@ struct ParserPackratEntry { class ParserPackratCache { public: - ParserPackratCache(); + explicit ParserPackratCache(ArenaAllocator &arena); ~ParserPackratCache(); optional_ptr Lookup(const Matcher &matcher, idx_t token_index) const; void Store(const Matcher &matcher, idx_t token_index, ParserPackratEntry entry); private: - unordered_map entries; + arena_unordered_map entries; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/passthrough_dialect.hpp b/src/duckdb/src/include/duckdb/parser/peg/passthrough_dialect.hpp new file mode 100644 index 000000000..cb2b08f7a --- /dev/null +++ b/src/duckdb/src/include/duckdb/parser/peg/passthrough_dialect.hpp @@ -0,0 +1,69 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/parser/peg/passthrough_dialect.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/parser/peg/dialect_extension.hpp" +#include "duckdb/parser/peg/matcher.hpp" + +namespace duckdb { + +//! Consumes any single token that is not a statement boundary, without interpreting it. Repeating this matcher +//! covers a statement whose syntax this parser does not know, e.g. SQL destined for a remote engine. +class PassthroughTokenMatcher : public AtomicMatcher { +public: + static constexpr MatcherType TYPE = MatcherType::CUSTOM; + +public: + PassthroughTokenMatcher() : AtomicMatcher(TYPE) { + } + + MatcherResult MatchAtomic(MatchState &state) const override { + auto current = state.token_iterator.Current(); + if (!current || current->type == TokenType::TERMINATOR || current->type == TokenType::END_OF_INPUT || + current->type == TokenType::END_OF_INPUT_AUTOCOMPLETE) { + return MatcherResult::Failure(); + } + // always advances, so a repeat over this matcher terminates + auto text = current->text; + auto offset = current->offset; + auto length = current->length; + state.token_iterator.Advance(); + state.UpdateMaxTokenIndex(); + return state.AllocateParseResult(std::move(text), offset, length); + } + + SuggestionType AddSuggestionInternal(MatchState &state) const override { + // any token matches - there is nothing to suggest + return SuggestionType::MANDATORY; + } + + string ToString() const override { + return "PassthroughToken"; + } +}; + +//! The grammar used while a client is CONNECT-ed to a database that parses nothing locally: +//! +//! Statement <- DisconnectStatement / PassthroughStatement +//! PassthroughStatement <- PassthroughToken+ +//! +//! DISCONNECT stays interpreted so the client can always end the connection; every other statement is handed to +//! the remote verbatim. The tokenizer is unchanged, so statement boundaries are DuckDB's. +class PassthroughDialect : public DialectExtension { +public: + static constexpr const char *NAME = "passthrough"; + +public: + PassthroughDialect() : DialectExtension(NAME) { + } + + void ApplyGrammarChanges(GrammarChangesInput &input) override; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/peg_parser.hpp b/src/duckdb/src/include/duckdb/parser/peg/peg_parser.hpp index 3edcb2bd7..f91f06df4 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/peg_parser.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/peg_parser.hpp @@ -23,7 +23,7 @@ struct PEGExpression { public: explicit PEGExpression(Type type_p) : type(type_p), text(string_t("")) { } - PEGExpression(Type type_p, string_t text_p) : type(type_p), text(std::move(text_p)) { + PEGExpression(Type type_p, string_t text_p) : type(type_p), text(text_p) { } public: diff --git a/src/duckdb/src/include/duckdb/parser/peg/tokenizer/tokenizer.hpp b/src/duckdb/src/include/duckdb/parser/peg/tokenizer/tokenizer.hpp index 123c4b896..c2e3dcb5f 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/tokenizer/tokenizer.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/tokenizer/tokenizer.hpp @@ -45,12 +45,11 @@ class TokenizerBehavior { return TokenType::END_OF_INPUT; } -protected: +public: const string &sql; vector &tokens; bool has_block_comment = false; idx_t last_block_comment_position = 0; - friend class Tokenizer; }; class Tokenizer { @@ -62,16 +61,22 @@ class Tokenizer { //! Tokenize the behavior's input and return whether autocomplete can be offered. virtual bool TokenizeInput(TokenizerBehavior &behavior) const; +protected: + virtual bool BackslashEscapesStringLiterals() const; + virtual bool IsQuotedIdentifierDelimiter(char character) const; + virtual void PushOperatorToken(TokenizerBehavior &behavior, idx_t start, idx_t end) const; + virtual void HandleLastToken(TokenizerBehavior &behavior, TokenizeState state, const string &sql, + idx_t last_pos) const; + private: //! Core tokenization loop. Returns true on a clean exit, false if the input ended inside an //! unterminated comment / dollar-quoted string. Does NOT append the trailing sentinel — //! `TokenizeInput()` is the one that appends `GetTerminator()` (clean) or `END_OF_INPUT` //! (dirty) based on the return value. bool TokenizeInputInternal(TokenizerBehavior &behavior) const; - static void PushOperatorToken(TokenizerBehavior &behavior, idx_t start, idx_t end); + bool IsCompoundColonToken(const string &sql, idx_t pos, idx_t &token_length) const; public: - bool IsSpecialOperator(const string &sql, idx_t pos, idx_t &op_len) const; static bool IsSingleByteOperator(char c); static bool CharacterIsInitialNumber(char c); static bool CharacterIsNumber(char c); diff --git a/src/duckdb/src/include/duckdb/parser/peg/transformer/parse_result.hpp b/src/duckdb/src/include/duckdb/parser/peg/transformer/parse_result.hpp index 1303b6f0f..215848489 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/transformer/parse_result.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/transformer/parse_result.hpp @@ -1,6 +1,7 @@ #pragma once #include "utf8proc_wrapper.hpp" #include "duckdb/common/arena_linked_list.hpp" +#include "duckdb/common/array_ptr.hpp" #include "duckdb/common/exception.hpp" #include "duckdb/common/optional_ptr.hpp" #include "duckdb/common/string.hpp" @@ -62,6 +63,7 @@ inline string TokenTypeToString(TokenType type) { class PEGTransformer; // Forward declaration struct CompiledGrammarRule; +class Matcher; enum class ParseResultType : uint8_t { LIST, @@ -77,6 +79,7 @@ enum class ParseResultType : uint8_t { NUMBER, STRING, END_OF_INPUT, + TOKEN, INVALID }; @@ -108,6 +111,8 @@ inline const char *ParseResultToString(ParseResultType type) { return "STRING"; case ParseResultType::END_OF_INPUT: return "END_OF_INPUT"; + case ParseResultType::TOKEN: + return "TOKEN"; case ParseResultType::INVALID: return "INVALID"; } @@ -128,14 +133,16 @@ class ParseResult { TARGET &Cast() { if (TARGET::TYPE != ParseResultType::INVALID && type != TARGET::TYPE) { throw InternalException("Failed to cast parse result of type %s to type %s for rule %s", - ParseResultToString(TARGET::TYPE), ParseResultToString(type), name); + ParseResultToString(TARGET::TYPE), ParseResultToString(type), Name()); } return reinterpret_cast(*this); } ParseResultType type; - string name; optional_ptr rule; + //! Set when a collapsible rule handed this result out in place of its own, so the transformer runs this + //! result's rule rather than the one the parent asked for + bool collapsed = false; optional_idx offset; //! Source length: for leaf tokens the token length; for composite results the enclosing extent of children optional_idx length; @@ -143,6 +150,19 @@ class ParseResult { void SetRule(const CompiledGrammarRule &rule_p) { rule = rule_p; } + //! The name of the rule (or matcher) that produced this result. Rule names live in the compiled grammar and + //! outlive every parse, so a result points at one instead of owning a copy of it. + const string &Name() const { + static const string EMPTY; + return name ? *name : EMPTY; + } + //! Only a pointer to the name is kept, so these only take a grammar object that owns it, or a result that already + //! points at one. + inline void SetNameFrom(const CompiledGrammarRule &rule_p); + inline void SetNameFrom(const Matcher &matcher_p); + void SetNameFrom(const ParseResult &other) { + name = other.name; + } optional_ptr GetRule() const { return rule; } @@ -169,8 +189,8 @@ class ParseResult { virtual void ToStringInternal(std::stringstream &ss, std::unordered_set &visited, const std::string &indent, bool is_last) const { ss << indent << (is_last ? "└─" : "├─") << " " << ParseResultToString(type); - if (!name.empty()) { - ss << " (" << name << ")"; + if (!Name().empty()) { + ss << " (" << Name() << ")"; } } @@ -182,6 +202,9 @@ class ParseResult { ToStringInternal(ss, visited, "", true); return ss.str(); } + +private: + const string *name = nullptr; }; struct IdentifierParseResult : ParseResult { @@ -199,6 +222,22 @@ struct IdentifierParseResult : ParseResult { } }; +//! A single token consumed without interpreting it; carries its text for debugging only +struct TokenParseResult : ParseResult { + static constexpr ParseResultType TYPE = ParseResultType::TOKEN; + string text; + + TokenParseResult(string text_p, optional_idx offset, optional_idx length) + : ParseResult(TYPE, offset, length), text(std::move(text_p)) { + } + + void ToStringInternal(std::stringstream &ss, std::unordered_set &visited, + const std::string &indent, bool is_last) const override { + ParseResult::ToStringInternal(ss, visited, indent, is_last); + ss << ": " << text << "\n"; + } +}; + struct EndOfInputParseResult : ParseResult { static constexpr ParseResultType TYPE = ParseResultType::END_OF_INPUT; @@ -231,15 +270,18 @@ struct ListParseResult : ParseResult { static constexpr ParseResultType TYPE = ParseResultType::LIST; public: - explicit ListParseResult(vector> results_p, string name_p, optional_idx offset) - : ParseResult(TYPE, offset), children(std::move(results_p)) { - name = std::move(name_p); + explicit ListParseResult(unsafe_array_ptr> results_p, + optional_ptr named_matcher, optional_idx offset) + : ParseResult(TYPE, offset), children(results_p) { + if (named_matcher) { + SetNameFrom(*named_matcher); + } for (auto &child : children) { EncloseChild(child.get()); } } - vector> GetChildren() const { + unsafe_array_ptr> GetChildren() const { return children; } @@ -260,14 +302,14 @@ struct ListParseResult : ParseResult { ss << indent << (is_last ? "└─" : "├─"); if (visited.count(this)) { - ss << " List (" << name << ") [... already printed ...]\n"; + ss << " List (" << Name() << ") [... already printed ...]\n"; return; } visited.insert(this); ss << " " << ParseResultToString(type); - if (!name.empty()) { - ss << " (" << name << ")"; + if (!Name().empty()) { + ss << " (" << Name() << ")"; } ss << " [" << children.size() << " children]\n"; @@ -278,20 +320,20 @@ struct ListParseResult : ParseResult { } private: - vector> children; + unsafe_array_ptr> children; }; struct RepeatParseResult : ParseResult { static constexpr ParseResultType TYPE = ParseResultType::REPEAT; - explicit RepeatParseResult(vector> results_p, optional_idx offset) - : ParseResult(TYPE, offset), children(std::move(results_p)) { + explicit RepeatParseResult(unsafe_array_ptr> results_p, optional_idx offset) + : ParseResult(TYPE, offset), children(results_p) { for (auto &child : children) { EncloseChild(child.get()); } } - vector> GetChildren() const { + unsafe_array_ptr> GetChildren() const { return children; } @@ -308,14 +350,14 @@ struct RepeatParseResult : ParseResult { ss << indent << (is_last ? "└─" : "├─"); if (visited.count(this)) { - ss << " Repeat (" << name << ") [... already printed ...]\n"; + ss << " Repeat (" << Name() << ") [... already printed ...]\n"; return; } visited.insert(this); ss << " " << ParseResultToString(type); - if (!name.empty()) { - ss << " (" << name << ")"; + if (!Name().empty()) { + ss << " (" << Name() << ")"; } ss << " [" << children.size() << " children]\n"; @@ -326,7 +368,7 @@ struct RepeatParseResult : ParseResult { } private: - vector> children; + unsafe_array_ptr> children; }; struct OptionalParseResult : ParseResult { @@ -336,7 +378,7 @@ struct OptionalParseResult : ParseResult { } explicit OptionalParseResult(optional_ptr result_p, optional_idx offset) : ParseResult(TYPE, offset), optional_result(result_p) { - name = result_p->name; + SetNameFrom(*result_p); EncloseChild(*result_p); } @@ -378,7 +420,7 @@ class ChoiceParseResult : public ParseResult { explicit ChoiceParseResult(ParseResult &parse_result_p, idx_t selected_idx_p, optional_idx offset) : ParseResult(TYPE, offset), result(parse_result_p), selected_idx(selected_idx_p) { - name = parse_result_p.name; + SetNameFrom(parse_result_p); EncloseChild(parse_result_p); } @@ -402,6 +444,22 @@ class ChoiceParseResult : public ParseResult { idx_t selected_idx; }; +//! Whether the parse result allocator has to keep a pointer to a node of exactly this type, to destroy it before the +//! arena is dropped. Most node types only hold spans and pointers into the arena and can be left alone. This is a +//! trait on the exact type rather than a member, so a subclass that adds an owning member does not inherit the opt-out. +template +struct ParseResultNeedsDestructor : std::true_type {}; +template <> +struct ParseResultNeedsDestructor : std::false_type {}; +template <> +struct ParseResultNeedsDestructor : std::false_type {}; +template <> +struct ParseResultNeedsDestructor : std::false_type {}; +template <> +struct ParseResultNeedsDestructor : std::false_type {}; +template <> +struct ParseResultNeedsDestructor : std::false_type {}; + class NumberParseResult : public ParseResult { public: static constexpr ParseResultType TYPE = ParseResultType::NUMBER; @@ -431,7 +489,7 @@ class StringLiteralParseResult : public ParseResult { return result; } - unique_ptr ToExpression() { + virtual unique_ptr ToExpression() { switch (string_type) { case SpecialStringCharacter::STANDARD: return ConstantExpression::String(result); diff --git a/src/duckdb/src/include/duckdb/parser/peg/transformer/peg_transformer.hpp b/src/duckdb/src/include/duckdb/parser/peg/transformer/peg_transformer.hpp index 33733c0b1..6d4eb30c7 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/transformer/peg_transformer.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/transformer/peg_transformer.hpp @@ -1,8 +1,10 @@ #pragma once +#include "duckdb/common/deque.hpp" #include "duckdb/parser/peg/ast/unpivot_name_values.hpp" #include "duckdb/parser/qualified_name_set.hpp" #include "duckdb/parser/peg/transformer/parse_result.hpp" +#include "duckdb/parser/peg/matcher.hpp" #include "duckdb/parser/peg/transformer/transform_result.hpp" #include "duckdb/parser/peg/ast/add_column_entry.hpp" #include "duckdb/parser/peg/ast/column_constraint_entry.hpp" @@ -55,6 +57,8 @@ #include "duckdb/parser/tableref/basetableref.hpp" #include "duckdb/parser/peg/parsed_grammar.hpp" #include "duckdb/storage/arena_allocator.hpp" +#include "duckdb/common/arena_containers/arena_vector.hpp" +#include "duckdb/common/arena_containers/arena_ptr.hpp" #include "duckdb/parser/query_node/select_node.hpp" #include "duckdb/parser/statement/drop_statement.hpp" #include "duckdb/parser/statement/insert_statement.hpp" @@ -116,6 +120,7 @@ DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.Identifier", Iden DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.InsertColumnOrder", InsertColumnOrder); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.InsertValues", InsertValues); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.IsDistinctFromTail", IsDistinctFromTail); +DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.IsExpressionTail", IsExpressionTail); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.JoinPrefix", JoinPrefix); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.JoinQualifier", JoinQualifier); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.JoinType", JoinType); @@ -163,14 +168,13 @@ DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.Value", Value); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.WindowBoundaryExpression", WindowBoundaryExpression); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.WindowExcludeMode", WindowExcludeMode); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.WindowFrame", WindowFrame); +DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.arena_ptr", + arena_ptr); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.bool", bool); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.case_insensitive_map_t>", case_insensitive_map_t>); -DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.child_list_t", child_list_t); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.int64_t", int64_t); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.optional_idx", optional_idx); -DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.pair", - pair); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.pair>", pair>); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.pair>", @@ -205,6 +209,8 @@ DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.unique_ptr); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.unique_ptr", unique_ptr); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.unique_ptr", unique_ptr); +DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.unique_ptr", + unique_ptr); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.unique_ptr", unique_ptr); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.unique_ptr", unique_ptr); @@ -222,14 +228,13 @@ DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.unique_ptr); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.unique_ptr", unique_ptr); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.unique_ptr", unique_ptr); -DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.unique_ptr", - unique_ptr); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.unique_ptr", unique_ptr); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.unique_ptr", unique_ptr); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.vector", vector); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.vector", vector); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.vector", vector); +DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.vector", vector); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.vector", vector); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.vector", vector); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.vector", @@ -240,6 +245,8 @@ DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.vector", vector); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.vector", vector); +DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.vector>>", + vector>>); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.vector", vector); DUCKDB_REGISTER_TRANSFORM_RESULT_TYPE("duckdb.transform_result.vector>", vector>); @@ -259,8 +266,8 @@ class TransformProcess; class GeneratedTransformProcess; using transform_process_initialize_t = void (*)(PEGTransformer &transformer, GeneratedTransformProcess &process); -using transform_process_finalize_t = unique_ptr (*)(PEGTransformer &transformer, - GeneratedTransformProcess &process); +using transform_process_finalize_t = arena_ptr (*)(PEGTransformer &transformer, + GeneratedTransformProcess &process); struct TransformFrameOps { const char *name; @@ -273,13 +280,18 @@ unique_ptr> TryBridgeTransformResultValue(TransformResul //! Input to start a transformer execution. The rule can be supplied explicitly for transparent parse nodes. struct TransformInput { - TransformInput(ParseResult &parse_result_p) : parse_result(parse_result_p) { + TransformInput(ParseResult &parse_result_p) // NOLINT(google-explicit-constructor): enables braced PushChild calls. + : parse_result(parse_result_p) { } TransformInput(const CompiledGrammarRule &rule_p, ParseResult &parse_result_p) : rule(rule_p), parse_result(parse_result_p) { } optional_ptr GetRule() const { + // A collapsed result stands in for a rule that would have returned it unchanged; transform it as itself + if (parse_result.collapsed) { + return parse_result.GetRule(); + } return rule ? rule : parse_result.GetRule(); } @@ -287,24 +299,24 @@ struct TransformInput { ParseResult &parse_result; }; -//! Essentially a std::variant>. +//! Essentially a std::variant>. //! Produced by TransformProcess::Resume to control the next execution step. class TransformStep { public: static TransformStep Child(TransformInput input); - static TransformStep Complete(unique_ptr result); + static TransformStep Complete(arena_ptr result); optional GetChild(); - unique_ptr TakeResult(); + arena_ptr TakeResult(); private: - TransformStep(optional child_p, unique_ptr result_p) + TransformStep(optional child_p, arena_ptr result_p) : child(std::move(child_p)), result(std::move(result_p)) { } private: optional child; - unique_ptr result; + arena_ptr result; }; class TransformProcess { @@ -312,7 +324,7 @@ class TransformProcess { virtual ~TransformProcess() = default; //! Resume transforming, optionally with the result of the previously requested child. - virtual TransformStep Resume(unique_ptr child_result) = 0; + virtual TransformStep Resume(arena_ptr child_result) = 0; }; class GeneratedTransformProcess final : public TransformProcess { @@ -320,9 +332,9 @@ class GeneratedTransformProcess final : public TransformProcess { GeneratedTransformProcess(PEGTransformer &transformer, TransformInput input, const TransformFrameOps &info); void ReserveChildSlots(idx_t count); - void SetChildResult(idx_t slot, unique_ptr result); + void SetChildResult(idx_t slot, arena_ptr result); void PushChild(TransformInput input, idx_t slot); - TransformStep Resume(unique_ptr child_result) override; + TransformStep Resume(arena_ptr child_result) override; template T TakeResult(idx_t slot) { @@ -359,7 +371,7 @@ class GeneratedTransformProcess final : public TransformProcess { ParseResult &parse_result; const TransformFrameOps &info; idx_t manual_state = 0; - vector> child_results; + arena_vector> child_results; private: struct PendingChild { @@ -371,19 +383,19 @@ class GeneratedTransformProcess final : public TransformProcess { private: PEGTransformer &transformer; - vector pending_children; + arena_vector pending_children; optional_idx child_result_slot; bool completed = false; }; using transform_finalize_function_t = - std::function(PEGTransformer &transformer, ParseResult &parse_result)>; + std::function(PEGTransformer &transformer, ParseResult &parse_result)>; class FinalizeTransformProcess final : public TransformProcess { public: FinalizeTransformProcess(PEGTransformer &transformer, ParseResult &parse_result, transform_finalize_function_t finalize); - TransformStep Resume(unique_ptr child_result) override; + TransformStep Resume(arena_ptr child_result) override; private: PEGTransformer &transformer; @@ -397,8 +409,10 @@ struct TransformStackFrame { optional_ptr rule; ParseResult &parse_result; - unique_ptr process; - unique_ptr child_result; + arena_ptr process; + arena_ptr child_result; + //! How much this frame adds to the expression depth while it is on the stack + idx_t expression_depth_weight = 0; }; #ifdef DEBUG @@ -410,14 +424,15 @@ using frame_stack_t = stack; class TransformStack { public: explicit TransformStack(PEGTransformer &transformer); - unique_ptr Execute(TransformInput input); + ~TransformStack(); + arena_ptr Execute(TransformInput input); template T Execute(TransformInput input) { auto base_result = Execute(input); auto *result_value = TryGetTransformResult(*base_result); if (!result_value) { - throw InternalException("Unexpected transformer result type for root rule '%s'", input.parse_result.name); + throw InternalException("Unexpected transformer result type for root rule '%s'", input.parse_result.Name()); } return std::move(*result_value); } @@ -428,8 +443,9 @@ class TransformStack { private: void PushFrame(TransformInput input); + void PopFrame(); void InitializeFrame(TransformStackFrame &frame); - unique_ptr ExecuteFrame(TransformStackFrame &frame); + arena_ptr ExecuteFrame(TransformStackFrame &frame); private: PEGTransformer &transformer; @@ -459,7 +475,7 @@ class PEGTransformer { SetResultLocation(bridged_result, parse_result.GetLocation()); return bridged_result; } - throw InternalException("Transformer for rule '" + parse_result.name + "' returned an unexpected type."); + throw InternalException("Transformer for rule '" + parse_result.Name() + "' returned an unexpected type."); } auto result = std::move(*result_value); @@ -494,6 +510,22 @@ class PEGTransformer { return allocator.Make(std::forward(args)...); } + //! Transform processes live exactly as long as one transform run, so they are carved out of the same arena + template + arena_ptr MakeProcess(Args &&...args) { + return arena_ptr(allocator.Make(std::forward(args)...)); + } + + //! The result a transformer hands back, carved out of the same arena + template + arena_ptr MakeResult(Args &&...args) { + return arena_ptr(allocator.Make>(std::forward(args)...)); + } + + ArenaAllocator &GetAllocator() { + return allocator; + } + void Clear(); void ClearParameters(); static void ParamTypeCheck(PreparedParamType last_type, PreparedParamType new_type); @@ -511,8 +543,7 @@ class PEGTransformer { void SetQueryLocation(TableRef &ref, QueryLocation query_location); private: - unique_ptr TransformInternal(ParseResult &parse_result); - unique_ptr ExecuteRecursive(TransformInput input); + arena_ptr TransformInternal(ParseResult &parse_result); void SetResultLocation(ParseResult &parse_result, TransformResultValue &result); template @@ -542,7 +573,7 @@ class PEGTransformer { idx_t prepared_statement_parameter_index = 0; PreparedParamType last_param_type = PreparedParamType::INVALID; - identifier_map_t> window_clauses; + deque>> window_clauses; vector> pivot_entries; vector> stored_cte_map; @@ -552,6 +583,13 @@ class PEGTransformer { friend class StackChecker; idx_t stack_depth = 0; + //! An upper bound of the depth of the expression trees currently being transformed - tracked by the transform + //! stacks: every nested expression, nested type and link of an operator chain counts while it is transformed + idx_t expression_depth = 0; + //! The depth a frame of this rule adds while it is on the stack (see ParsedGrammar::SetExpressionDepth) + static idx_t ExpressionDepthWeight(optional_ptr rule, ParseResult &parse_result); + void EnterExpression(idx_t weight); + void ExitExpression(idx_t weight); StackChecker StackCheck(idx_t extra_stack = 1) { if (stack_depth + extra_stack >= options.max_expression_depth) { @@ -614,14 +652,6 @@ TryBridgeTransformResultValue>(TransformResultValue &base_res return nullptr; } -typedef unique_ptr (*transform_finalize_t)(PEGTransformer &transformer, - ParseResult &parse_result); - -struct TransformFinalizeRule { - const char *name; - transform_finalize_t finalize; -}; - class PEGTransformerFactory { public: static void RegisterDefaultTransforms(ParsedGrammar &grammar); @@ -638,6 +668,7 @@ class PEGTransformerFactory { static QualifiedName StringToQualifiedName(vector input); static QualifiedColumnName StringToQualifiedColumnName(const vector &input); static LogicalType GetIntervalTargetType(DatePartSpecifier date_part); + static LogicalType ApplyColumnCollation(const optional &type, unique_ptr collation); static void AddGroupByExpression(unique_ptr expression, GroupingExpressionMap &map, GroupByNode &result, vector &result_set); static vector GroupByExpressionUnfolding(GroupByExpressionInfo &group_by_expr, @@ -658,6 +689,11 @@ class PEGTransformerFactory { static void SplitGenericOptions(const vector &options_in, case_insensitive_map_t> &parsed_options, unordered_map &options, const char *statement_name); + //! Fold `(k v, ...)` into unbound expressions, for statements that resolve every option at bind time + //! instead of splitting literals out at parse time. Validates like SplitGenericOptions. + static void CollectGenericOptions(const vector &options_in, + case_insensitive_map_t> &options, + const char *statement_name); static void AddToMultiStatement(const unique_ptr &multi_statement, unique_ptr alter_info); static void AddUpdateToMultiStatement(const unique_ptr &multi_statement, const string &column_name, @@ -669,7573 +705,5528 @@ class PEGTransformerFactory { unique_ptr expression); static void InitializePivotStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePivotStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePivotStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUnpivotStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUnpivotStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUnpivotStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLiteralExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLiteralExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializePrefixExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePrefixExpressionTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeLiteralExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializePrefixExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizePrefixExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOverClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOverClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOverClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSelectStatementInternalTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeSelectStatementInternalTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeSimpleSelectTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSimpleSelectTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSimpleSelectTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTableRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTableRefTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTableRefTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWithClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWithClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWithClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWindowDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWindowDefinitionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWindowDefinitionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); //===--------------------------------------------------------------------===// // START GENERATED TRAMPOLINE RULES //===--------------------------------------------------------------------===// static void InitializeStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeIdentifierOrKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr FinalizeIdentifierOrKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAlterStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAlterStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAlterStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAlterOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAlterOptionsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAlterOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAlterTableStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAlterTableStmtTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAlterTableStmtTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAlterSchemaStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAlterSchemaStmtTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeAlterTableOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAlterTableOptionsTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeAlterSchemaStmtTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeAlterSchemaOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeAlterSchemaOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeAlterTableOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeAlterTableOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAddConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAddConstraintTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAddConstraintTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDropConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropConstraintTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDropConstraintTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAddColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAddColumnTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAddColumnTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAddColumnEntryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAddColumnEntryTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAddColumnEntryTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDropColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropColumnTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDropColumnTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAlterColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAlterColumnTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAlterColumnTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRenameColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRenameColumnTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRenameColumnTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNestedColumnNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNestedColumnNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNestedColumnNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIdentifierDotTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIdentifierDotTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIdentifierDotTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRenameAlterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRenameAlterTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRenameAlterTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSetPartitionedByTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetPartitionedByTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSetPartitionedByTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeResetPartitionedByTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeResetPartitionedByTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeResetPartitionedByTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSetSortedByTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetSortedByTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSetSortedByTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeResetSortedByTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeResetSortedByTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeResetSortedByTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSetOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetOptionsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSetOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeResetOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeResetOptionsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeResetOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAlterColumnEntryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAlterColumnEntryTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAlterColumnEntryTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAddOrDropDefaultTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAddOrDropDefaultTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAddOrDropDefaultTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAddDefaultTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAddDefaultTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAddDefaultTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDropDefaultTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropDefaultTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDropDefaultTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeChangeNullabilityTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeChangeNullabilityTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeChangeNullabilityTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDropOrSetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropOrSetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDropOrSetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDropNullabilityTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropNullabilityTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeSetNullabilityTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetNullabilityTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeDropNullabilityTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeSetNullabilityTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeSetNullabilityTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAlterTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAlterTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAlterTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUsingExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUsingExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUsingExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAlterViewStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAlterViewStmtTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAlterViewStmtTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAlterSequenceStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAlterSequenceStmtTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAlterSequenceStmtTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQualifiedSequenceNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQualifiedSequenceNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeQualifiedSequenceNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAlterSequenceOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAlterSequenceOptionsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAlterSequenceOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRenameAlterSequenceOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeRenameAlterSequenceOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeSetSequenceOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetSequenceOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSetSequenceOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAlterDatabaseStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAlterDatabaseStmtTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeAnalyzeStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAnalyzeStatementTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeAlterDatabaseStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeAnalyzeStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeAnalyzeStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAnalyzeTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAnalyzeTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAnalyzeTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAnalyzeVerboseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAnalyzeVerboseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAnalyzeVerboseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAttachStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAttachStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAttachStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDatabasePathTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDatabasePathTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeAttachAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAttachAliasTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeDatabasePathTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeAttachAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeAttachAliasTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAttachOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAttachOptionsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAttachOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCallStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCallStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCallStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCheckpointStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCheckpointStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCheckpointStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCheckpointForceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCheckpointForceTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCheckpointForceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCommentStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCommentStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCommentStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeCommentTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeCommentTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeCommentStringLiteralIdentifierTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr + FinalizeCommentStringLiteralIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeCommentOnTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCommentOnTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeCommentTableTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCommentTableTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeCommentOnTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeCommentTableTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeCommentTableTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCommentSequenceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCommentSequenceTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCommentSequenceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCommentFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCommentFunctionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCommentFunctionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCommentMacroTableTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCommentMacroTableTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCommentMacroTableTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCommentMacroTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCommentMacroTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeCommentViewTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCommentViewTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeCommentMacroTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeCommentViewTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeCommentViewTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCommentDatabaseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCommentDatabaseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCommentDatabaseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCommentIndexTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCommentIndexTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCommentIndexTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCommentSchemaTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCommentSchemaTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCommentSchemaTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCommentTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCommentTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCommentTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCommentColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCommentColumnTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeCommentValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCommentValueTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeCommentColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeCommentValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeCommentValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeStringLiteralValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeStringLiteralValueTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeStringLiteralValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAnalyzeKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAnalyzeKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeExpressionStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static unique_ptr FinalizeExpressionStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeExpressionAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExpressionAliasTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAnalyzeKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIndexNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIndexNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIndexNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeConstraintNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeConstraintNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeConstraintNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSequenceNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSequenceNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSequenceNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCollationNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCollationNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCollationNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNumberLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNumberLiteralTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNumberLiteralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeStringLiteralTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeStringLiteralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTypeVariationsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTypeVariationsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTypeVariationsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSimpleTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSimpleTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSimpleTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCharacterSimpleTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCharacterSimpleTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCharacterSimpleTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQualifiedSimpleTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQualifiedSimpleTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeQualifiedSimpleTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIntervalTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIntervalTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIntervalTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIntervalIntervalTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIntervalIntervalTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIntervalIntervalTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIntervalWithSpecifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIntervalWithSpecifierTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIntervalWithSpecifierTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIntervalWithRangeSpecifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeIntervalWithRangeSpecifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeIntervalWithSimpleSpecifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeIntervalWithSimpleSpecifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeIntervalWithoutSpecifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeIntervalWithoutSpecifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeIntervalToIntervalAsTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeIntervalToIntervalAsTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeYearKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeYearKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeYearKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMonthKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMonthKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMonthKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDayKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDayKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDayKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeHourKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeHourKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeHourKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMinuteKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMinuteKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMinuteKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSecondKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSecondKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSecondKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMillisecondKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMillisecondKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMillisecondKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMicrosecondKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMicrosecondKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMicrosecondKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWeekKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWeekKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWeekKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQuarterKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQuarterKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeDecadeKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDecadeKeywordTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeQuarterKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeDecadeKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeDecadeKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCenturyKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCenturyKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCenturyKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMillenniumKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMillenniumKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMillenniumKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIntervalTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIntervalTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIntervalTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIntervalToIntervalTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIntervalToIntervalTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIntervalToIntervalTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeYearToMonthTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeYearToMonthTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeYearToMonthTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDayToHourTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDayToHourTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDayToHourTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDayToMinuteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDayToMinuteTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDayToMinuteTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDayToSecondTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDayToSecondTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDayToSecondTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeHourToMinuteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeHourToMinuteTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeHourToMinuteTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeHourToSecondTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeHourToSecondTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeHourToSecondTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMinuteToSecondTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMinuteToSecondTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMinuteToSecondTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeBitTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBitTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeBitTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeGeometryTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGeometryTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeVariantTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeVariantTypeTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeGeometryTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeVariantTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeVariantTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNumericTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNumericTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNumericTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSimpleNumericTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSimpleNumericTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSimpleNumericTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDecimalNumericTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDecimalNumericTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDecimalNumericTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIntTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIntTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIntTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIntegerTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIntegerTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIntegerTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSmallintTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSmallintTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSmallintTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeBigintTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBigintTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeBigintTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRealTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRealTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRealTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeBooleanTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBooleanTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeDoubleTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDoubleTypeTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeBooleanTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static void InitializeFloatTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFloatTypeTrampoline(PEGTransformer &transformer, + static void InitializeDoubleTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeDoubleTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeFloatTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeFloatTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDecimalTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDecimalTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDecimalTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDecTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDecTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDecTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNumericModTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNumericModTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNumericModTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQualifiedTypeNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQualifiedTypeNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeQualifiedTypeNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTypeNameAsQualifiedNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeTypeNameAsQualifiedNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeCatalogReservedSchemaTypeNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeCatalogReservedSchemaTypeNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeSchemaReservedTypeNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeSchemaReservedTypeNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeSchemaReservedTypeNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTypeModifiersTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTypeModifiersTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTypeModifiersTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeNestedTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeNestedTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRowTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRowTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRowTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSetofTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetofTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSetofTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUnionTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUnionTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUnionTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColIdTypeListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColIdTypeListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColIdTypeListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMapTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMapTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMapTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTupleTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTupleTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTupleTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColIdTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColIdTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColIdTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeArrayBoundsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeArrayBoundsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeArrayBoundsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeArrayKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeArrayKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeArrayKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeArrayKeywordWithBoundsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeArrayKeywordWithBoundsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeArrayKeywordWithBoundsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSquareBracketsArrayTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSquareBracketsArrayTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSquareBracketsArrayTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTimeTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTimeTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTimeTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTimeOrTimestampTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTimeOrTimestampTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTimeOrTimestampTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTimeTypeIdTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTimeTypeIdTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTimeTypeIdTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTimestampTypeIdTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTimestampTypeIdTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTimestampTypeIdTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTimeZoneTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTimeZoneTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTimeZoneTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWithOrWithoutTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWithOrWithoutTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWithOrWithoutTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWithRuleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWithRuleTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWithRuleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWithoutRuleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWithoutRuleTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWithoutRuleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeConnectStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeConnectStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeConnectStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDisconnectStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDisconnectStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDisconnectStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSessionTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSessionTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSessionTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLocalSessionTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLocalSessionTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeLocalSessionTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeStringSessionTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeStringSessionTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeStringSessionTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCatalogSessionTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCatalogSessionTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCatalogSessionTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCopyStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCopyStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCopyStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCopyVariationsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCopyVariationsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCopyVariationsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCopyTableTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCopyTableTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeFromOrToTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFromOrToTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeCopyTableTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeFromOrToTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeFromOrToTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCopyFromTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCopyFromTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCopyFromTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCopyToTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCopyToTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCopyToTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCopySelectTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCopySelectTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCopySelectTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCopyFileNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCopyFileNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCopyFileNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCopyFileNameExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeCopyFileNameExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeCopyFileNameExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCopyFileNameStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeCopyFileNameStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeCopyFileNameIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeCopyFileNameIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeCopyFileNameIdentifierTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCopyFileNameIdentifierColIdTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeCopyFileNameIdentifierColIdTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeIdentifierColIdTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIdentifierColIdTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeIdentifierColIdTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeCopyFileNameSuffixTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeCopyFileNameSuffixTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeCopyLegacyOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeCopyLegacyOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeCopyOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCopyOptionsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCopyOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCopyOptionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCopyOptionListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCopyOptionListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSpecializedOptionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSpecializedOptionListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSpecializedOptionListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSpecializedOptionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSpecializedOptionTailTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSpecializedOptionTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSpecializedOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSpecializedOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSpecializedOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSingleOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSingleOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSingleOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeBinaryOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBinaryOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeBinaryOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFreezeOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFreezeOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFreezeOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOidsOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOidsOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeCsvOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCsvOptionTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeOidsOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeCsvOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeCsvOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeHeaderOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeHeaderOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeHeaderOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNullAsOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNullAsOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNullAsOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDelimiterAsOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDelimiterAsOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDelimiterAsOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQuoteAsOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQuoteAsOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeQuoteAsOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeEscapeAsOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeEscapeAsOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeEscapeAsOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeEncodingOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeEncodingOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeEncodingOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeForceQuoteOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeForceQuoteOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeForceQuoteOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeStarSymbolColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeStarSymbolColumnListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeStarSymbolColumnListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeForceQuoteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeForceQuoteTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeForceQuoteTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePartitionByOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePartitionByOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePartitionByOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePartitionByColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePartitionByColumnListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePartitionByColumnListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeStarPartitionByColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeStarPartitionByColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeParenthesizedPartitionByColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeParenthesizedPartitionByColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeSinglePartitionByColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeSinglePartitionByColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeForceNullOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeForceNullOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeForceNullOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeForceNotNullTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeForceNotNullTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeForceNotNullTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCopyGenericOptionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCopyGenericOptionListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCopyGenericOptionListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCopyGenericOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCopyGenericOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCopyGenericOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOrderByCopyOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOrderByCopyOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOrderByCopyOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePartitionedByCopyOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizePartitionedByCopyOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeGenericCopyOptionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGenericCopyOptionListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeGenericCopyOptionListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeGenericCopyOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGenericCopyOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeGenericCopyOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeGenericCopyOptionValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeGenericCopyOptionValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeGenericCopyOptionValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeGenericCopyOptionOrderListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeGenericCopyOptionOrderListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeGenericCopyOptionExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeGenericCopyOptionExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeGenericCopyOptionParenthesizedExpressionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeGenericCopyOptionParenthesizedExpressionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeCopyFromDatabaseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCopyFromDatabaseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCopyFromDatabaseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCopyFromDatabaseWithFlagTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeCopyFromDatabaseWithFlagTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeCopyFromDatabaseWithoutFlagTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeCopyFromDatabaseWithoutFlagTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeCopyDatabaseFlagTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCopyDatabaseFlagTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCopyDatabaseFlagTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSchemaOrDataTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSchemaOrDataTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSchemaOrDataTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCopySchemaTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCopySchemaTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCopySchemaTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCopyDataTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCopyDataTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCopyDataTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateIndexStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateIndexStmtTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateIndexStmtTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWithListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWithListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWithListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRelOptionOrOidsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRelOptionOrOidsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRelOptionOrOidsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRelOptionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRelOptionListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRelOptionListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOidsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOidsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOidsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWithOrWithoutOidsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWithOrWithoutOidsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWithOrWithoutOidsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWithOidsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWithOidsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWithOidsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWithoutOidsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWithoutOidsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWithoutOidsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIndexElementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIndexElementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeUniqueIndexTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUniqueIndexTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeIndexElementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeUniqueIndexTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeUniqueIndexTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIndexTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIndexTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIndexTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRelOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRelOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRelOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRelOptionNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRelOptionNameTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeRelOptionNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeDottedColLabelTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeDottedColLabelTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static void InitializeDottedIdentifierStringTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static unique_ptr - FinalizeDottedIdentifierStringTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeRelOptionArgumentOptTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRelOptionArgumentOptTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRelOptionArgumentOptTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDefArgTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDefArgTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDefArgTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDefArgNullTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDefArgNullTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDefArgNullTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDefArgKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDefArgKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDefArgKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDefArgStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDefArgStringLiteralTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDefArgStringLiteralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNoneLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNoneLiteralTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNoneLiteralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateMacroStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateMacroStmtTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateMacroStmtTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMacroOrFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMacroOrFunctionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMacroOrFunctionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMacroKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMacroKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMacroKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFunctionKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFunctionKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFunctionKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMacroDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMacroDefinitionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMacroDefinitionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMacroDefinitionBodyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMacroDefinitionBodyTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMacroDefinitionBodyTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMacroParametersTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMacroParametersTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeMacroParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMacroParameterTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeMacroParametersTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeMacroParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeMacroParameterTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSimpleParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSimpleParameterTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSimpleParameterTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeScalarMacroDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeScalarMacroDefinitionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeScalarMacroDefinitionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTableMacroDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTableMacroDefinitionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTableMacroDefinitionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateSchemaStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateSchemaStmtTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateSchemaStmtTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeWithOptionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeWithOptionListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateSecretStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateSecretStmtTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateSecretStmtTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSecretStorageSpecifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeSecretStorageSpecifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeSecretStorageSpecifierTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSecretNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSecretNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSecretNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateSequenceStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateSequenceStmtTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateSequenceStmtTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSequenceOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSequenceOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSequenceOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSeqSetCycleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSeqSetCycleTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSeqSetCycleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSeqCycleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSeqCycleTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSeqCycleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSeqNoCycleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSeqNoCycleTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSeqNoCycleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSeqSetIncrementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSeqSetIncrementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSeqSetIncrementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSeqSetMinMaxTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSeqSetMinMaxTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeSeqNoMinMaxTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSeqNoMinMaxTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeSeqSetMinMaxTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeSeqNoMinMaxTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeSeqNoMinMaxTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSeqStartWithTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSeqStartWithTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSeqStartWithTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSeqOwnedByTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSeqOwnedByTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSeqOwnedByTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSeqMinOrMaxTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSeqMinOrMaxTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSeqMinOrMaxTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMinValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMinValueTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMinValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMaxValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMaxValueTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMaxValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateStatementVariationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeCreateStatementVariationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeOrReplaceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOrReplaceTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOrReplaceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTemporaryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTemporaryTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTemporaryTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePersistentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePersistentTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePersistentTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTempPersistentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTempPersistentTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTempPersistentTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTemporaryPersistentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTemporaryPersistentTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTemporaryPersistentTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateTableStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateTableStmtTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateTableStmtTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateTableDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateTableDefinitionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateTableDefinitionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateTableAsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateTableAsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateTableAsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePartitionSortedOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizePartitionSortedOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizePartitionSortedOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePartitionOptSortedOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizePartitionOptSortedOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeSortedOptPartitionOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeSortedOptPartitionOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializePartitionOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePartitionOptionsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePartitionOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSortedOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSortedOptionsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSortedOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWithDataTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWithDataTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWithDataTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWithDataOnlyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWithDataOnlyTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWithDataOnlyTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWithNoDataTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWithNoDataTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWithNoDataTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIdentifierListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIdentifierListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIdentifierListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateColumnListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateColumnListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIfNotExistsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIfNotExistsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIfNotExistsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQualifiedNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQualifiedNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeQualifiedNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSchemaReservedIdentifierOrStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeSchemaReservedIdentifierOrStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeCatalogReservedSchemaIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeCatalogReservedSchemaIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeIdentifierOrStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeIdentifierOrStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeReservedIdentifierOrStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeReservedIdentifierOrStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeCatalogQualificationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCatalogQualificationTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCatalogQualificationTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSchemaQualificationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSchemaQualificationTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSchemaQualificationTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeReservedSchemaQualificationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeReservedSchemaQualificationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeTableQualificationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTableQualificationTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTableQualificationTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeReservedTableQualificationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeReservedTableQualificationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeCreateTableColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateTableColumnListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateTableColumnListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateTableColumnElementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeCreateTableColumnElementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeCreateTableColumnDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeCreateTableColumnDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeCreateTableConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateTableConstraintTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateTableConstraintTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColumnDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColumnDefinitionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColumnDefinitionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColumnConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColumnConstraintTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColumnConstraintTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeNameableColumnConstraintTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr + FinalizeNameableColumnConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeColumnConstraintElementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr + FinalizeColumnConstraintElementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeNotNullConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNotNullConstraintTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNotNullConstraintTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNullConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNullConstraintTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNullConstraintTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNotNullColumnConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeNotNullColumnConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeUniqueConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUniqueConstraintTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUniqueConstraintTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePrimaryKeyConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePrimaryKeyConstraintTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePrimaryKeyConstraintTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeConstraintTimingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeConstraintTimingTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeConstraintTimingTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeImmediateConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeImmediateConstraintTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeDeferredConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDeferredConstraintTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeImmediateConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeDeferredConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeDeferredConstraintTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDefaultValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDefaultValueTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDefaultValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCheckConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCheckConstraintTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCheckConstraintTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeForeignKeyConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeForeignKeyConstraintTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeForeignKeyConstraintTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColumnCollationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColumnCollationTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColumnCollationTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColumnCompressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColumnCompressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColumnCompressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeKeyActionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeKeyActionsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeKeyActionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeUpdateFirstKeyActionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr FinalizeUpdateFirstKeyActionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeDeleteFirstKeyActionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr FinalizeDeleteFirstKeyActionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUpdateActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUpdateActionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUpdateActionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDeleteActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDeleteActionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDeleteActionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeKeyActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeKeyActionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeKeyActionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNoKeyActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNoKeyActionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNoKeyActionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRestrictKeyActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRestrictKeyActionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeCascadeKeyActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCascadeKeyActionTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeRestrictKeyActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeCascadeKeyActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeCascadeKeyActionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSetNullKeyActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetNullKeyActionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSetNullKeyActionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSetDefaultKeyActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetDefaultKeyActionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeTopLevelConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTopLevelConstraintTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeSetDefaultKeyActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeTopLevelConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeTopLevelConstraintTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTopLevelConstraintListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeTopLevelConstraintListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeTopLevelConstraintListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTopCheckConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTopCheckConstraintTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTopCheckConstraintTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTopPrimaryKeyConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeTopPrimaryKeyConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeTopUniqueConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTopUniqueConstraintTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTopUniqueConstraintTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTopForeignKeyConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeTopForeignKeyConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeColumnIdListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColumnIdListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColumnIdListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDottedIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDottedIdentifierTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDottedIdentifierTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDotColLabelTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDotColLabelTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIdentifierTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeDotColLabelTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeIdentifierTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColIdTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColIdTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColIdTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColIdOrStringTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColIdOrStringTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeTypeFuncNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTypeFuncNameTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeColIdOrStringTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeTypeFuncNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeTypeFuncNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTypeFuncKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTypeFuncKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTypeFuncKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColLabelTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColLabelTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColLabelTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColLabelOrStringTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColLabelOrStringTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColLabelOrStringTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColLabelIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColLabelIdentifierTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColLabelIdentifierTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeStringLiteralIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeStringLiteralIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeGeneratedColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGeneratedColumnTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeGeneratedColumnTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeGeneratedTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeGeneratedTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeGeneratedAlwaysTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeGeneratedAlwaysTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeGeneratedByDefaultTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeGeneratedByDefaultTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeGeneratedColumnTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGeneratedColumnTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeGeneratedColumnTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCommitActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCommitActionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCommitActionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePreserveOrDeleteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePreserveOrDeleteTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePreserveOrDeleteTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePreserveRowsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePreserveRowsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePreserveRowsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDeleteRowsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDeleteRowsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDeleteRowsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeVirtualGeneratedColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeVirtualGeneratedColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeVirtualGeneratedColumnTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeStoredGeneratedColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeStoredGeneratedColumnTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeStoredGeneratedColumnTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateTriggerStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateTriggerStmtTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateTriggerStmtTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTriggerBodyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTriggerBodyTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTriggerBodyTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTriggerNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTriggerNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTriggerNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeReferencingClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeReferencingClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeReferencingClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeReferencingItemTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeReferencingItemTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeReferencingItemTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeReferencingNewTableAsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeReferencingNewTableAsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeReferencingNewTableAsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeReferencingOldTableAsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeReferencingOldTableAsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeReferencingOldTableAsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTriggerTimingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTriggerTimingTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTriggerTimingTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTriggerBeforeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTriggerBeforeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeTriggerAfterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTriggerAfterTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeTriggerBeforeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeTriggerAfterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeTriggerAfterTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTriggerInsteadOfTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTriggerInsteadOfTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTriggerInsteadOfTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTriggerEventTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTriggerEventTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTriggerEventTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTriggerEventInsertTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTriggerEventInsertTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTriggerEventInsertTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTriggerEventDeleteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTriggerEventDeleteTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTriggerEventDeleteTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTriggerEventUpdateTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTriggerEventUpdateTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTriggerEventUpdateTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTriggerEventUpdateOfTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTriggerEventUpdateOfTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTriggerEventUpdateOfTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTriggerColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTriggerColumnListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTriggerColumnListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeForEachClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeForEachClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeForEachClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeForEachRowTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeForEachRowTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeForEachRowTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeForEachStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeForEachStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeForEachStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateTypeStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateTypeStmtTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateTypeStmtTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateTypeFromTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateTypeFromTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateTypeFromTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeEnumSelectTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeEnumSelectTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeEnumSelectTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeEnumStringLiteralListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeEnumStringLiteralListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeEnumStringLiteralListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateViewStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateViewStmtTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateViewStmtTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateRecursiveTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateRecursiveTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateRecursiveTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCreateSecureTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCreateSecureTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCreateSecureTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDeallocateStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDeallocateStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDeallocateStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDeallocatePrepareTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDeallocatePrepareTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDeallocatePrepareTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDeleteStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDeleteStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDeleteStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTruncateStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTruncateStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeTargetOptAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTargetOptAliasTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeTruncateStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeTruncateOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeTruncateOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeTruncateAliasOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr FinalizeTruncateAliasOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeTruncateDropBehaviorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr FinalizeTruncateDropBehaviorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeTargetOptAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeTargetOptAliasTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeTargetAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeTargetAliasTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDeleteUsingClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDeleteUsingClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDeleteUsingClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDescribeStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDescribeStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDescribeStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeShowDeprecatedSelectTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeShowDeprecatedSelectTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeShowDeprecatedSelectTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDescribeSelectTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDescribeSelectTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeShowAllTablesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeShowAllTablesTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeDescribeSelectTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeShowAllTablesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeShowAllTablesTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeShowTablesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeShowTablesTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeShowTablesTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeShowByNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeShowByNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeShowByNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDescribeByNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDescribeByNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDescribeByNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDescribeOrSummarizeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDescribeOrSummarizeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDescribeOrSummarizeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeShowTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeShowTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeShowTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeShowDeprecatedQualifiedTableNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeShowDeprecatedQualifiedTableNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeShowSettingNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeShowSettingNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeDescribeTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDescribeTargetTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeShowSettingNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeDescribeTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeDescribeTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDescribeBaseTableNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDescribeBaseTableNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDescribeBaseTableNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDescribeStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDescribeStringLiteralTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDescribeStringLiteralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSummarizeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSummarizeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSummarizeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSummarizeRuleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSummarizeRuleTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSummarizeRuleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeShowOrDescribeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeShowOrDescribeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeShowOrDescribeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeShowRuleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeShowRuleTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeShowRuleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDescribeRuleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDescribeRuleTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDescribeRuleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDescribeLongRuleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDescribeLongRuleTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDescribeLongRuleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDescRuleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDescRuleTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDescRuleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDetachStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDetachStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDetachStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDropStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDropStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDropEntriesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropEntriesTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDropEntriesTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDropTriggerTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropTriggerTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDropTriggerTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDropTableTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropTableTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDropTableTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDropTableFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropTableFunctionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDropTableFunctionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDropFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropFunctionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDropFunctionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDropSchemaTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropSchemaTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeDropIndexTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropIndexTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeDropSchemaTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeDropIndexTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeDropIndexTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQualifiedIndexNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQualifiedIndexNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeQualifiedIndexNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQualifiedIndexNameStringTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeQualifiedIndexNameStringTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeSchemaReservedIndexTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSchemaReservedIndexTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSchemaReservedIndexTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCatalogReservedSchemaIndexTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeCatalogReservedSchemaIndexTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeDropSequenceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropSequenceTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDropSequenceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDropCollationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropCollationTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDropCollationTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDropTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDropTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDropSecretTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropSecretTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDropSecretTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTableOrViewTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTableOrViewTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTableOrViewTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMaterializedViewEntryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMaterializedViewEntryTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMaterializedViewEntryTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFunctionTypeMacroTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFunctionTypeMacroTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFunctionTypeMacroTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFunctionTypeMacroKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeFunctionTypeMacroKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeFunctionTypeFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFunctionTypeFunctionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFunctionTypeFunctionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDropBehaviorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropBehaviorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDropBehaviorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCascadeDropBehaviorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCascadeDropBehaviorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCascadeDropBehaviorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRestrictDropBehaviorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRestrictDropBehaviorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRestrictDropBehaviorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIfExistsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIfExistsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIfExistsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDropSecretStorageTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDropSecretStorageTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeExecuteStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExecuteStatementTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeDropSecretStorageTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeExecuteStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeExecuteStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExplainStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExplainStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExplainStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExplainOptionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExplainOptionListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExplainOptionListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExplainOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExplainOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExplainOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExplainOptionNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExplainOptionNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExplainOptionNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExplainSelectStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeExplainSelectStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeExplainSelectStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExplainableStatementsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExplainableStatementsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExplainableStatementsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExportStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExportStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExportStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExportSourceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExportSourceTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExportSourceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeImportStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeImportStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeImportStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColumnReferenceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColumnReferenceTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColumnReferenceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNestedSchemaTableColumnNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeNestedSchemaTableColumnNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeCatalogReservedSchemaTableColumnNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeCatalogReservedSchemaTableColumnNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeSchemaReservedTableColumnNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeSchemaReservedTableColumnNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeTableReservedColumnNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeTableReservedColumnNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeFunctionExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFunctionExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFunctionExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFunctionExpressionArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeFunctionExpressionArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeFunctionExpressionArgumentListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeFunctionExpressionArgumentListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeFunctionArgumentListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFunctionArgumentListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFunctionArgumentListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFunctionIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFunctionIdentifierTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFunctionIdentifierTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFunctionNameAsQualifiedNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeFunctionNameAsQualifiedNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeCatalogReservedSchemaFunctionNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeCatalogReservedSchemaFunctionNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeSchemaReservedFunctionNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeSchemaReservedFunctionNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeDistinctOrAllTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDistinctOrAllTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDistinctOrAllTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDistinctKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDistinctKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDistinctKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAllKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAllKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAllKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWithinGroupClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWithinGroupClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWithinGroupClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFilterClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFilterClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFilterClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFilterClauseExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeFilterClauseExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeFilterClauseExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFilterClauseContentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFilterClauseContentsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFilterClauseContentsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIgnoreOrRespectNullsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIgnoreOrRespectNullsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIgnoreOrRespectNullsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIgnoreNullsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIgnoreNullsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIgnoreNullsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRespectNullsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRespectNullsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRespectNullsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeParenthesisExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeParenthesisExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeParenthesisExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeConstantLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeConstantLiteralTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeConstantLiteralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNullLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNullLiteralTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNullLiteralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTrueLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTrueLiteralTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTrueLiteralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFalseLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFalseLiteralTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFalseLiteralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCastExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCastExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeCastArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCastArgumentsTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeCastExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeCastArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeCastArgumentsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCastOrTryCastTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCastOrTryCastTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCastOrTryCastTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCastKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCastKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCastKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTryCastKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTryCastKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTryCastKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColIdDotTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColIdDotTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColIdDotTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeStarExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeStarExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeStarExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeStarQualifierListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeStarQualifierListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeStarQualifierListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExcludeListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExcludeListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExcludeListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExcludeNamesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExcludeNamesTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExcludeNamesTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExcludeNameListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExcludeNameListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExcludeNameListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExcludeNameSingleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExcludeNameSingleTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExcludeNameSingleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExcludeNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExcludeNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExcludeNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExcludeDottedNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExcludeDottedNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExcludeDottedNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExcludeColumnNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExcludeColumnNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExcludeColumnNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeReplaceListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeReplaceListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeReplaceListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeReplaceEntriesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeReplaceEntriesTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeReplaceEntriesTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeReplaceEntrySingleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeReplaceEntrySingleTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeReplaceEntrySingleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeReplaceEntryListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeReplaceEntryListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeReplaceEntryListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeReplaceEntryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeReplaceEntryTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeReplaceEntryTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRenameListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRenameListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRenameListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRenameEntriesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRenameEntriesTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRenameEntriesTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRenameEntryListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRenameEntryListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRenameEntryListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSingleRenameEntryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSingleRenameEntryTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSingleRenameEntryTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRenameEntryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRenameEntryTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRenameEntryTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSubqueryExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSubqueryExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSubqueryExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSubqueryNotTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSubqueryNotTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSubqueryNotTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSubqueryExistsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSubqueryExistsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSubqueryExistsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCaseExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCaseExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCaseExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCaseWhenThenTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCaseWhenThenTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCaseWhenThenTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCaseElseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCaseElseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCaseElseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTypeLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTypeLiteralTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTypeLiteralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIntervalLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIntervalLiteralTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIntervalLiteralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIntervalParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIntervalParameterTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIntervalParameterTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIntervalStringParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeIntervalStringParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeFrameClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFrameClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFrameClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFramingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFramingTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFramingTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRowsFramingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRowsFramingTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRowsFramingTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRangeFramingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRangeFramingTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRangeFramingTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeGroupsFramingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGroupsFramingTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeGroupsFramingTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFrameExtentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFrameExtentTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFrameExtentTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSingleFrameExtentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSingleFrameExtentTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSingleFrameExtentTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeBetweenFrameExtentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBetweenFrameExtentTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeBetweenFrameExtentTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFrameBoundTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFrameBoundTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFrameBoundTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFrameUnboundedTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFrameUnboundedTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFrameUnboundedTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFrameExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFrameExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFrameExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFrameCurrentRowTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFrameCurrentRowTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFrameCurrentRowTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePrecedingOrFollowingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePrecedingOrFollowingTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePrecedingOrFollowingTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePrecedingFrameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePrecedingFrameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePrecedingFrameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFollowingFrameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFollowingFrameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFollowingFrameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWindowExcludeClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWindowExcludeClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWindowExcludeClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWindowExcludeElementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWindowExcludeElementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWindowExcludeElementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExcludeCurrentRowTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExcludeCurrentRowTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExcludeCurrentRowTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExcludeGroupTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExcludeGroupTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeExcludeTiesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExcludeTiesTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeExcludeGroupTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeExcludeTiesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeExcludeTiesTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExcludeNoOthersTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExcludeNoOthersTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExcludeNoOthersTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWindowFrameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWindowFrameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWindowFrameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIdentifierWindowFrameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIdentifierWindowFrameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIdentifierWindowFrameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeParensIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeParensIdentifierTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeParensIdentifierTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWindowFrameDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWindowFrameDefinitionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWindowFrameDefinitionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWindowFrameNameContentsParensTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeWindowFrameNameContentsParensTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeWindowFrameNameContentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeWindowFrameNameContentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeWindowFrameContentsParensTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeWindowFrameContentsParensTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeWindowFrameContentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWindowFrameContentsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWindowFrameContentsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeBaseWindowNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBaseWindowNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeBaseWindowNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWindowPartitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWindowPartitionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeListExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeListExpressionTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeWindowPartitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeListExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeListExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeArrayBoundedListExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeArrayBoundedListExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeArrayParensSelectTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeArrayParensSelectTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeArrayParensSelectTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeBoundedListExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBoundedListExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeBoundedListExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeStructExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeStructExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeStructExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeStructFieldTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeStructFieldTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeStructFieldTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMapExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMapExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMapExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMapStructExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMapStructExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMapStructExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMapStructFieldTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMapStructFieldTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMapStructFieldTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeGroupingExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGroupingExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeGroupingExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeGroupingOrGroupingIdTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGroupingOrGroupingIdTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeGroupingOrGroupingIdTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeGroupingKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGroupingKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeGroupingKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeGroupingIdKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGroupingIdKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeGroupingIdKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeParameterTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeParameterTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQuestionMarkNumberedParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeQuestionMarkNumberedParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeAnonymousParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAnonymousParameterTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeNumberedParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNumberedParameterTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeAnonymousParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeNumberedParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeNumberedParameterTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColLabelParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColLabelParameterTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColLabelParameterTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePositionalExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePositionalExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePositionalExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDefaultExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDefaultExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDefaultExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeListComprehensionExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeListComprehensionExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeListComprehensionFilterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeListComprehensionFilterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeParensExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeParensExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeParensExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMeasureSemanticsExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeMeasureSemanticsExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeSingleExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSingleExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSingleExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColumnDefaultExprTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColumnDefaultExprTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColumnDefaultExprTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLambdaArrowExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLambdaArrowExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeLambdaArrowExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSingleArrowPairTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSingleArrowPairTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSingleArrowPairTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLogicalOrExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLogicalOrExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeLogicalOrExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLogicalOrExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeLogicalOrExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeColDefOrExprTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColDefOrExprTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColDefOrExprTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColDefOrExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeColDefOrExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeColDefOrExpressionTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLogicalAndExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLogicalAndExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeLogicalAndExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLogicalAndExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeLogicalAndExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeColDefAndExprTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColDefAndExprTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColDefAndExprTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColDefAndExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeColDefAndExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeLogicalNotExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLogicalNotExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeLogicalNotExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNotExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNotExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNotExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNotKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNotKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNotKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIsExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIsExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIsExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeIsExpressionContinuationTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr + FinalizeIsExpressionContinuationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeIsExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeIsExpressionTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeIsTestTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeIsTestTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeIsDistinctTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeIsDistinctTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeIsComparisonTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeIsComparisonTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeIsOtherOperatorTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr FinalizeIsOtherOperatorTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIsTestTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIsTestTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIsTestTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIsLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIsLiteralTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIsLiteralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIsLiteralValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIsLiteralValueTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIsLiteralValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUnknownLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUnknownLiteralTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUnknownLiteralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNotNullTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNotNullTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNotNullTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNotNullKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNotNullKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNotNullKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNotNullOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNotNullOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNotNullOperatorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIsNullTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIsNullTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIsNullTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIsNullOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIsNullOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIsNullOperatorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIsDistinctFromExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeIsDistinctFromExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeIsDistinctFromTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIsDistinctFromTailTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIsDistinctFromTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIsDistinctFromOpTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIsDistinctFromOpTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIsDistinctFromOpTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeComparisonExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeComparisonExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeComparisonExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeComparisonExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeComparisonExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeComparisonOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeComparisonOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeComparisonOperatorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOperatorEqualTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOperatorEqualTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOperatorEqualTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOperatorNotEqualTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOperatorNotEqualTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOperatorNotEqualTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOperatorLessThanTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOperatorLessThanTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOperatorLessThanTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOperatorGreaterThanTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOperatorGreaterThanTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOperatorGreaterThanTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOperatorLessThanEqualsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeOperatorLessThanEqualsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeOperatorLessThanEqualsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOperatorGreaterThanEqualsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeOperatorGreaterThanEqualsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeBetweenInLikeExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeBetweenInLikeExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static void InitializeBetweenInLikeOpTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBetweenInLikeOpTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeBetweenInLikeOpExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static unique_ptr - FinalizeBetweenInLikeOpExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static void InitializeLikeClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLikeClauseTrampoline(PEGTransformer &transformer, + static void InitializeInPredicateTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeInPredicateTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static void InitializeEscapeClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeEscapeClauseTrampoline(PEGTransformer &transformer, + static void InitializeBetweenLikeOpTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeBetweenLikeOpTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeBetweenLikeOpExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr + FinalizeBetweenLikeOpExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeLikeClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeLikeClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeEscapeClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeEscapeClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLikeVariationsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLikeVariationsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeLikeVariationsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLikeTokenTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLikeTokenTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeLikeTokenTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeILikeTokenTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeILikeTokenTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeGlobTokenTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGlobTokenTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeILikeTokenTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeGlobTokenTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeGlobTokenTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSimilarToTokenTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSimilarToTokenTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSimilarToTokenTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRegexMatchTokenTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRegexMatchTokenTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRegexMatchTokenTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRegexInsensitiveMatchTokenTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeRegexInsensitiveMatchTokenTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeNotILikeOpTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNotILikeOpTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeNotLikeOpTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNotLikeOpTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeNotILikeOpTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeNotLikeOpTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeNotLikeOpTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNotRegexInsensitiveMatchOpTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeNotRegexInsensitiveMatchOpTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeNotSimilarToOpTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNotSimilarToOpTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNotSimilarToOpTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInContainsExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInContainsExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInContainsExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInExpressionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInExpressionListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInExpressionListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInSelectStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInSelectStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInSelectStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeBetweenClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBetweenClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeBetweenClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOtherOperatorExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeOtherOperatorExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeInfixOtherOperatorExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr + FinalizeInfixOtherOperatorExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeCustomPrefixExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr FinalizeCustomPrefixExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOtherOperatorTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOtherOperatorTailTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOtherOperatorTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOtherOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOtherOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOtherOperatorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAnyAllParsedOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAnyAllParsedOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAnyAllParsedOperatorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNamedOtherOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNamedOtherOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNamedOtherOperatorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOperatorLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOperatorLiteralTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeAnyAllOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAnyAllOperatorTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeOperatorLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeAnyOperatorLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeAnyOperatorLiteralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeAnyAllOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeAnyAllOperatorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAnyOrAllTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAnyOrAllTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAnyOrAllTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSubqueryAnyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSubqueryAnyTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSubqueryAnyTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSubqueryAllTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSubqueryAllTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeInetOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInetOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeJsonOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeJsonOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeListOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeListOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeStringOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeStringOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSubqueryAllTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQualifiedOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQualifiedOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeQualifiedOperatorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQualifiedOperatorContentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeQualifiedOperatorContentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static void InitializeAnyOpTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAnyOpTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); static void InitializeBitwiseExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBitwiseExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeBitwiseExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeBitwiseExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBitwiseExpressionTailTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeBitwiseExpressionTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeBitOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBitOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeBitOperatorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeTildeExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeTildeExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAdditiveExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAdditiveExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAdditiveExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAdditiveExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeAdditiveExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeAdditiveExpressionTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTermTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTermTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTermTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMultiplicativeExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeMultiplicativeExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeMultiplicativeExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeMultiplicativeExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeFactorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFactorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFactorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExponentiationExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeExponentiationExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeExponentiationExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeExponentiationExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeExponentOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExponentOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExponentOperatorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCollateExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCollateExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCollateExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCollateExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCollateExpressionTailTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCollateExpressionTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAtTimeZoneExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAtTimeZoneExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAtTimeZoneExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAtTimeZoneExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeAtTimeZoneExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializePrefixOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePrefixOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePrefixOperatorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMinusPrefixOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMinusPrefixOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializePlusPrefixOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePlusPrefixOperatorTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeMinusPrefixOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializePlusPrefixOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizePlusPrefixOperatorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTildePrefixOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTildePrefixOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTildePrefixOperatorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeBaseExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBaseExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeBaseExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIndirectionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIndirectionListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIndirectionListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIndirectionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIndirectionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIndirectionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCastOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCastOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeDotOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDotOperatorTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeCastOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeDotOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeDotOperatorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDotMethodOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDotMethodOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDotMethodOperatorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDotColumnOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDotColumnOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeMethodExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMethodExpressionTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeDotColumnOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeMethodExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeMethodExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMethodExpressionArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeMethodExpressionArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeMethodExpressionArgumentListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeMethodExpressionArgumentListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeMethodFunctionArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeMethodFunctionArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeSliceExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSliceExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSliceExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSliceBoundTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSliceBoundTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSliceBoundTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeSliceUpperBoundTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeSliceUpperBoundTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeMinusStepSliceBoundTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr FinalizeMinusStepSliceBoundTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeEndStepSliceBoundTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeEndStepSliceBoundTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeEndSliceBoundTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeEndSliceBoundTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeEndSliceValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeEndSliceValueTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeEndSliceMinusTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeEndSliceMinusTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeEndSliceBoundTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeStepSliceBoundTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeStepSliceBoundTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeStepSliceBoundTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePostfixOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePostfixOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePostfixOperatorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSpecialFunctionExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeSpecialFunctionExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeCoalesceExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCoalesceExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCoalesceExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUnpackExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUnpackExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUnpackExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTryExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTryExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTryExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColumnsExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColumnsExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColumnsExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExtractExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExtractExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeExtractArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExtractArgumentsTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeExtractExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeExtractArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeExtractArgumentsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLambdaExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLambdaExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeLambdaExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNullIfExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNullIfExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeNullIfArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNullIfArgumentsTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeNullIfExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeNullIfArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeNullIfArgumentsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePositionExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePositionExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializePositionArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePositionArgumentsTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizePositionExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializePositionArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizePositionArgumentsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRowExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRowExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRowExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSubstringExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSubstringExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeSubstringArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSubstringArgumentsTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeSubstringExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeSubstringArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeSubstringArgumentsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSubstringExpressionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeSubstringExpressionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeSubstringParametersTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSubstringParametersTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSubstringParametersTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSubstringFromForTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSubstringFromForTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSubstringFromForTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSubstringFromOptionalForTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeSubstringFromOptionalForTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeSubstringForTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSubstringForTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSubstringForTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTrimExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTrimExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeTrimArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTrimArgumentsTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeTrimExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeTrimArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeTrimArgumentsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTrimDirectionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTrimDirectionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTrimDirectionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTrimBothTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTrimBothTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTrimBothTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTrimLeadingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTrimLeadingTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTrimLeadingTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTrimTrailingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTrimTrailingTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTrimTrailingTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTrimSourceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTrimSourceTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTrimSourceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOverlayExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOverlayExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeOverlayArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOverlayArgumentsTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeOverlayExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeOverlayArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeOverlayArgumentsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOverlayParametersTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOverlayParametersTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOverlayParametersTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFromExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFromExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeForExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeForExpressionTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeFromExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeForExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeForExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOverlayExpressionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOverlayExpressionListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOverlayExpressionListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExtractArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExtractArgumentTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExtractArgumentTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExtractDatePartArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeExtractDatePartArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeExtractIdentifierArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeExtractIdentifierArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeExtractStringArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExtractStringArgumentTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExtractStringArgumentTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExtractDatePartTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExtractDatePartTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExtractDatePartTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExternalResourceStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeExternalResourceStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeCreateExternalResourceStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeCreateExternalResourceStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeRegisterExternalResourceStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeRegisterExternalResourceStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeDestroyExternalResourceStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeDestroyExternalResourceStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeShowExternalResourcesStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeShowExternalResourcesStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeShowAllModifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeShowAllModifierTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeShowAllModifierTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExternalResourceCreationOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeExternalResourceCreationOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeAttachToExternalResourceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr + FinalizeAttachToExternalResourceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeConnectToExternalResourceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr + FinalizeConnectToExternalResourceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeExternalResourceSourceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr FinalizeExternalResourceSourceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeExternalResourceCreateClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr + FinalizeExternalResourceCreateClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeExternalResourceReferenceClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr + FinalizeExternalResourceReferenceClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeInsertStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInsertStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInsertStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOrActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOrActionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOrActionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInsertOrReplaceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInsertOrReplaceTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeInsertOrIgnoreTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInsertOrIgnoreTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeInsertOrReplaceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeInsertOrIgnoreTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeInsertOrIgnoreTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeByNameOrPositionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeByNameOrPositionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeByNameOrPositionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInsertByNameOrderTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInsertByNameOrderTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInsertByNameOrderTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInsertByPositionOrderTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInsertByPositionOrderTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInsertByPositionOrderTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInsertByNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInsertByNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInsertByNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInsertByPositionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInsertByPositionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInsertByPositionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInsertTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInsertTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeInsertAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInsertAliasTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeInsertTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static void InitializeColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColumnListTrampoline(PEGTransformer &transformer, + static void InitializeInsertAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeInsertAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeColumnListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInsertColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInsertColumnListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInsertColumnListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInsertValuesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInsertValuesTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInsertValuesTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSelectInsertValuesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSelectInsertValuesTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSelectInsertValuesTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDefaultValuesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDefaultValuesTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDefaultValuesTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOnConflictClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOnConflictClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOnConflictClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOnConflictTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOnConflictTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOnConflictTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOnConflictExpressionTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeOnConflictExpressionTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeOnConflictIndexTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOnConflictIndexTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOnConflictIndexTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOnConflictActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOnConflictActionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOnConflictActionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOnConflictUpdateTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOnConflictUpdateTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOnConflictUpdateTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOnConflictNothingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOnConflictNothingTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOnConflictNothingTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeReturningClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeReturningClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeReturningClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLoadStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLoadStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeLoadStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExtensionAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExtensionAliasTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExtensionAliasTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInstallStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInstallStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInstallStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInstallAndLoadTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInstallAndLoadTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInstallAndLoadTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUpdateExtensionsStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeUpdateExtensionsStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeFromSourceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFromSourceTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFromSourceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFromSourceIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFromSourceIdentifierTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFromSourceIdentifierTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFromSourceStringTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFromSourceStringTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFromSourceStringTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeVersionNumberTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeVersionNumberTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeVersionNumberTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExtensionRepositoryStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeExtensionRepositoryStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeCreateExtensionRepositoryStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeCreateExtensionRepositoryStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeRepositoryPrefixTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRepositoryPrefixTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRepositoryPrefixTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRepositoryPublicKeyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRepositoryPublicKeyTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRepositoryPublicKeyTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDropExtensionRepositoryStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeDropExtensionRepositoryStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeTableMatchRecognizeClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeTableMatchRecognizeClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeMatchRecognizeBodyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMatchRecognizeBodyTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMatchRecognizeBodyTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMatchRecognizeClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMatchRecognizeClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMatchRecognizeClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMRPartitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMRPartitionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMRPartitionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMROrderByTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMROrderByTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMROrderByTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMRMeasuresTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMRMeasuresTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMRMeasuresTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMRRowsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMRRowsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMRRowsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMRSkipTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMRSkipTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMRSkipTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMRPatternTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMRPatternTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeMRSubsetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMRSubsetTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeMRPatternTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeMRSubsetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeMRSubsetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMRDefineAutoTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMRDefineAutoTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMRDefineAutoTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMRDefineTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMRDefineTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMRDefineTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMeasuresClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMeasuresClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMeasuresClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMeasuresElementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMeasuresElementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMeasuresElementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMeasureSemanticsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMeasureSemanticsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMeasureSemanticsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRunningSemanticsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRunningSemanticsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRunningSemanticsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFinalSemanticsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFinalSemanticsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFinalSemanticsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRowsPerMatchTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRowsPerMatchTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRowsPerMatchTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOneRowPerMatchTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOneRowPerMatchTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOneRowPerMatchTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAllRowsPerMatchTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAllRowsPerMatchTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAllRowsPerMatchTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAllRowsEmptyMatchesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAllRowsEmptyMatchesTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAllRowsEmptyMatchesTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeShowEmptyMatchesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeShowEmptyMatchesTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeShowEmptyMatchesTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOmitEmptyMatchesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOmitEmptyMatchesTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOmitEmptyMatchesTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWithUnmatchedRowsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWithUnmatchedRowsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWithUnmatchedRowsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAfterMatchSkipTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAfterMatchSkipTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAfterMatchSkipTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAfterMatchSkipTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAfterMatchSkipTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAfterMatchSkipTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSkipToNextRowTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSkipToNextRowTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSkipToNextRowTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSkipPastLastRowTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSkipPastLastRowTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeSkipToFirstVarTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSkipToFirstVarTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeSkipPastLastRowTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static void InitializeSkipToLastVarTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSkipToLastVarTrampoline(PEGTransformer &transformer, + static void InitializeSkipToFirstVarTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeSkipToFirstVarTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeSkipToLastVarTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeSkipToLastVarTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSkipToVarTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSkipToVarTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSkipToVarTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePatternClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePatternClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePatternClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRowPatternTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRowPatternTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRowPatternTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRowPatternAlternativeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRowPatternAlternativeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRowPatternAlternativeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRowPatternTermTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRowPatternTermTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRowPatternTermTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRowPatternFactorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRowPatternFactorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRowPatternFactorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRowPatternPrimaryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRowPatternPrimaryTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRowPatternPrimaryTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRowPatternPermuteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRowPatternPermuteTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeRowPatternAnchorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRowPatternAnchorTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeRowPatternPermuteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeRowPatternAnchorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeRowPatternAnchorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePatternStartTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePatternStartTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePatternStartTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePatternEndTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePatternEndTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePatternEndTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRowPatternGroupTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRowPatternGroupTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRowPatternGroupTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRowPatternEmptyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRowPatternEmptyTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRowPatternEmptyTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRowPatternExclusionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRowPatternExclusionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRowPatternExclusionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRowPatternLabelTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRowPatternLabelTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRowPatternLabelTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRowPatternQuantifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRowPatternQuantifierTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRowPatternQuantifierTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRowPatternQuantifierKindTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeRowPatternQuantifierKindTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeQuantifierReluctantTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQuantifierReluctantTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeQuantifierReluctantTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQuantifierStarTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQuantifierStarTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeQuantifierStarTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQuantifierPlusTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQuantifierPlusTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeQuantifierPlusTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQuantifierOptionalTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQuantifierOptionalTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeQuantifierOptionalTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQuantifierRangeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQuantifierRangeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeQuantifierRangeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQuantifierBoundsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQuantifierBoundsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeQuantifierBoundsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQuantifierMinMaxTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQuantifierMinMaxTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeQuantifierMinMaxTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQuantifierMinTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQuantifierMinTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeQuantifierMinTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQuantifierMaxTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQuantifierMaxTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeQuantifierMaxTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQuantifierExactTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQuantifierExactTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeQuantifierExactTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSubsetClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSubsetClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSubsetClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSubsetElementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSubsetElementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeDefineClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDefineClauseTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeSubsetElementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeDefineClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeDefineClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDefineElementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDefineElementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDefineElementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMergeIntoStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMergeIntoStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMergeIntoStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMergeIntoUsingClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMergeIntoUsingClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMergeIntoUsingClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMergeMatchTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMergeMatchTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMergeMatchTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMatchedClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMatchedClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMatchedClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMatchedClauseActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMatchedClauseActionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMatchedClauseActionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUpdateMatchClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUpdateMatchClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUpdateMatchClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUpdateMatchInfoTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUpdateMatchInfoTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUpdateMatchInfoTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUpdateMatchSetActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUpdateMatchSetActionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUpdateMatchSetActionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUpdateByNameOrPositionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeUpdateByNameOrPositionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeUpdateByNameOrPositionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDeleteMatchClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDeleteMatchClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDeleteMatchClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInsertMatchClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInsertMatchClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInsertMatchClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInsertMatchInfoTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInsertMatchInfoTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInsertMatchInfoTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInsertDefaultValuesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInsertDefaultValuesTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInsertDefaultValuesTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInsertByNameOrPositionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeInsertByNameOrPositionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeInsertByNameOrPositionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInsertValuesListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInsertValuesListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInsertValuesListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDoNothingMatchClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDoNothingMatchClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDoNothingMatchClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeErrorMatchClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeErrorMatchClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeErrorMatchClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUpdateMatchSetClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUpdateMatchSetClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUpdateMatchSetClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUpdateMatchSetInfoTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUpdateMatchSetInfoTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUpdateMatchSetInfoTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAndExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAndExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAndExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNotMatchedClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNotMatchedClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNotMatchedClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeBySourceOrTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBySourceOrTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeBySourceOrTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeBySourceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBySourceTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeBySourceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeByTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeByTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializePivotOnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePivotOnTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeByTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializePivotOnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizePivotOnTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePivotUsingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePivotUsingTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePivotUsingTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePivotColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePivotColumnListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePivotColumnListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePivotColumnEntryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePivotColumnEntryTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePivotColumnEntryTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePivotColumnExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePivotColumnExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePivotColumnExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePivotColumnSubqueryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePivotColumnSubqueryTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePivotColumnSubqueryTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIntoNameValuesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIntoNameValuesTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIntoNameValuesTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOptionalParensNameListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeOptionalParensNameListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeOptionalParensNameListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeParenthesizedNameListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeParenthesizedNameListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeParenthesizedNameListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeBareNameListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBareNameListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeBareNameListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIncludeOrExcludeNullsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIncludeOrExcludeNullsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIncludeOrExcludeNullsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIncludeNullsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIncludeNullsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIncludeNullsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExcludeNullsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExcludeNullsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExcludeNullsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUnpivotHeaderTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUnpivotHeaderTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUnpivotHeaderTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUnpivotHeaderSingleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUnpivotHeaderSingleTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUnpivotHeaderSingleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUnpivotHeaderListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUnpivotHeaderListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUnpivotHeaderListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePragmaStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePragmaStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePragmaStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePragmaAssignOrFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizePragmaAssignOrFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizePragmaAssignOrFunctionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePragmaAssignTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePragmaAssignTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePragmaAssignTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePragmaFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePragmaFunctionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePragmaFunctionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePragmaParametersTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePragmaParametersTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePragmaParametersTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePrepareStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePrepareStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePrepareStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTypeListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTypeListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTypeListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSelectStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSelectStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSelectStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSelectSetOpChainTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSelectSetOpChainTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSelectSetOpChainTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSelectSetOpChainTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSelectSetOpChainTailTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSelectSetOpChainTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIntersectChainTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIntersectChainTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIntersectChainTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIntersectChainTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeIntersectChainTailTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeIntersectChainTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSetIntersectClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetIntersectClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSetIntersectClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSelectAtomTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSelectAtomTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSelectAtomTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSelectParensTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSelectParensTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeSetopClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetopClauseTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeSelectParensTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeSetopClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeSetopClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSetopTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetopTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSetopTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSetopUnionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetopUnionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSetopUnionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSetopExceptTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetopExceptTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSetopExceptTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSelectStatementTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSelectStatementTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSelectStatementTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeResultModifiersTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeResultModifiersTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeResultModifiersTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLimitOffsetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLimitOffsetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeLimitOffsetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLimitOffsetClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLimitOffsetClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeLimitOffsetClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOffsetLimitClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOffsetLimitClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOffsetLimitClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOffsetFetchClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOffsetFetchClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOffsetFetchClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeFetchOffsetClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeFetchOffsetClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFetchOnlyClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFetchOnlyClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeTableStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTableStatementTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeFetchOnlyClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeTableStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeTableStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOptionalParensSimpleSelectTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeOptionalParensSimpleSelectTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeSimpleSelectParensTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSimpleSelectParensTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSimpleSelectParensTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSelectFromTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSelectFromTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSelectFromTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSelectFromClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSelectFromClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSelectFromClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFromSelectClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFromSelectClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFromSelectClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWithStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWithStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWithStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCTEBodyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCTEBodyTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCTEBodyTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCTESelectBodyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCTESelectBodyTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCTESelectBodyTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCTEDMLBodyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCTEDMLBodyTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCTEDMLBodyTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUsingKeyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUsingKeyTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUsingKeyTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeMaterializedTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeMaterializedTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeMaterializedTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSelectClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSelectClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSelectClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTargetListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTargetListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTargetListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColumnAliasesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColumnAliasesTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColumnAliasesTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDistinctClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDistinctClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDistinctClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDistinctAllTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDistinctAllTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeDistinctOnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDistinctOnTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeDistinctAllTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeDistinctOnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeDistinctOnTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDistinctOnTargetsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDistinctOnTargetsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDistinctOnTargetsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInnerTableRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInnerTableRefTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInnerTableRefTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTableSubqueryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTableSubqueryTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeBaseTableRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBaseTableRefTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeTableSubqueryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeBaseTableRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeBaseTableRefTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTableAliasColonTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTableAliasColonTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTableAliasColonTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeValuesRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeValuesRefTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeValuesRefTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeParensTableRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeParensTableRefTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeParensTableRefTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeJoinOrPivotTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeJoinOrPivotTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeJoinOrPivotTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTablePivotClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTablePivotClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTablePivotClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTablePivotClauseBodyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTablePivotClauseBodyTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTablePivotClauseBodyTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePivotGroupByListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePivotGroupByListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePivotGroupByListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTableUnpivotClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTableUnpivotClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTableUnpivotClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTableUnpivotClauseBodyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeTableUnpivotClauseBodyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeTableUnpivotClauseBodyTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePivotHeaderTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePivotHeaderTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePivotHeaderTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePivotValueListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePivotValueListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePivotValueListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePivotValueTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePivotValueTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializePivotEnumTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePivotEnumTargetTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizePivotValueTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializePivotEnumTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizePivotEnumTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePivotListTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePivotListTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePivotListTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUnpivotValueListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUnpivotValueListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializePivotTargetListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePivotTargetListTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeUnpivotValueListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializePivotTargetListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizePivotTargetListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUnpivotTargetListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUnpivotTargetListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUnpivotTargetListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLateralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLateralTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeLateralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeBaseTableNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBaseTableNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeBaseTableNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUnqualifiedBaseTableNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeUnqualifiedBaseTableNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeQualifiedTableNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQualifiedTableNameTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeQualifiedTableNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSchemaReservedTableTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSchemaReservedTableTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSchemaReservedTableTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCatalogReservedSchemaTableTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeCatalogReservedSchemaTableTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeTableFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTableFunctionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTableFunctionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTableFunctionLateralOptTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeTableFunctionLateralOptTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeTableFunctionAliasColonTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeTableFunctionAliasColonTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeWithOrdinalityTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWithOrdinalityTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWithOrdinalityTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQualifiedTableFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeQualifiedTableFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeQualifiedTableFunctionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTableFunctionArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeTableFunctionArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeTableFunctionArgumentsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFunctionArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFunctionArgumentTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFunctionArgumentTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNamedFunctionArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNamedFunctionArgumentTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNamedFunctionArgumentTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePositionalFunctionArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizePositionalFunctionArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeNamedParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNamedParameterTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNamedParameterTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTableAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTableAliasTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTableAliasTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTableAliasAsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTableAliasAsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTableAliasAsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTableAliasWithoutAsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTableAliasWithoutAsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTableAliasWithoutAsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAtClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAtClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAtClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAtSpecifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAtSpecifierTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAtSpecifierTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAtUnitTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAtUnitTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAtUnitTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeVersionAtUnitTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeVersionAtUnitTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeVersionAtUnitTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTimestampAtUnitTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTimestampAtUnitTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTimestampAtUnitTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeJoinClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeJoinClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeJoinClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNearestJoinClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNearestJoinClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNearestJoinClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNearestJoinAliasedTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNearestJoinAliasedTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNearestJoinAliasedTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNearestJoinBareTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNearestJoinBareTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNearestJoinBareTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNearestBareTableRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNearestBareTableRefTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNearestBareTableRefTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNearestValuesRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNearestValuesRefTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNearestValuesRefTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNearestTableFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNearestTableFunctionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNearestTableFunctionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNearestTableSubqueryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNearestTableSubqueryTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNearestTableSubqueryTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNearestBaseTableRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNearestBaseTableRefTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNearestBaseTableRefTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNearestParensTableRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNearestParensTableRefTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNearestParensTableRefTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeApproxOrExactTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeApproxOrExactTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeApproxOrExactTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNearestApproxTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNearestApproxTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeNearestExactTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNearestExactTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeNearestApproxTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeNearestExactTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeNearestExactTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDistanceOrSimilarityTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDistanceOrSimilarityTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDistanceOrSimilarityTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNearestDistanceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNearestDistanceTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNearestDistanceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNearestSimilarityTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNearestSimilarityTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNearestSimilarityTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRegularJoinClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRegularJoinClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRegularJoinClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeJoinByClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeJoinByClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeJoinByClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAsofTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAsofTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAsofTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeJoinWithoutOnClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeJoinWithoutOnClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeJoinWithoutOnClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeJoinQualifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeJoinQualifierTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeJoinQualifierTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOnClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOnClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOnClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUsingClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUsingClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUsingClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeUsingColumnNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeUsingColumnNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeJoinTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeJoinTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeJoinTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeJoinPrefixTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeJoinPrefixTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeJoinPrefixTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCrossJoinPrefixTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCrossJoinPrefixTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCrossJoinPrefixTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNaturalJoinPrefixTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNaturalJoinPrefixTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNaturalJoinPrefixTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializePositionalJoinPrefixTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizePositionalJoinPrefixTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizePositionalJoinPrefixTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFullJoinTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFullJoinTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFullJoinTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLeftJoinTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLeftJoinTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeLeftJoinTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRightJoinTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRightJoinTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeSemiJoinTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSemiJoinTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeRightJoinTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeSemiJoinTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeSemiJoinTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAntiJoinTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAntiJoinTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAntiJoinTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeInnerJoinTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeInnerJoinTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeInnerJoinTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFromClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFromClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeFromClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWhereClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWhereClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeWhereClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeGroupByClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGroupByClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeHavingClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeHavingClauseTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeGroupByClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeHavingClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeHavingClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeQualifyClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeQualifyClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeSampleClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSampleClauseTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeQualifyClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeSampleClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeSampleClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeSelectSampleClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeSelectSampleClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeWindowClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeWindowClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeSampleEntryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSampleEntryTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeWindowClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeSampleEntryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeSampleEntryTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSampleEntryCountTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSampleEntryCountTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSampleEntryCountTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSampleEntryFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSampleEntryFunctionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSampleEntryFunctionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSampleFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSampleFunctionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSampleFunctionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSamplePropertiesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSamplePropertiesTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSamplePropertiesTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRepeatableSampleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRepeatableSampleTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRepeatableSampleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSampleSeedTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSampleSeedTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSampleSeedTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSampleCountTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSampleCountTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSampleCountTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSampleValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSampleValueTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeSampleUnitTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSampleUnitTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeSampleValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeSampleUnitTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeSampleUnitTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSamplePercentageTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSamplePercentageTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSamplePercentageTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSampleRowsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSampleRowsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSampleRowsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeGroupByExpressionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGroupByExpressionsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeGroupByExpressionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeGroupByAllTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGroupByAllTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeGroupByAllTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeGroupByListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGroupByListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeGroupByListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeGroupByExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGroupByExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeGroupByExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeGroupByBaseExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGroupByBaseExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeGroupByBaseExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeEmptyGroupingItemTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeEmptyGroupingItemTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeEmptyGroupingItemTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCubeOrRollupClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCubeOrRollupClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCubeOrRollupClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCubeOrRollupTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCubeOrRollupTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeCubeKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCubeKeywordTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeCubeOrRollupTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeCubeKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeCubeKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRollupKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRollupKeywordTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRollupKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeGroupingSetsClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGroupingSetsClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeSubqueryReferenceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSubqueryReferenceTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeGroupingSetsClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeSubqueryReferenceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeSubqueryReferenceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOrderByExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOrderByExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOrderByExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDescOrAscTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDescOrAscTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDescOrAscTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDescendingOrderTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDescendingOrderTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeAscendingOrderTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAscendingOrderTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeDescendingOrderTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeAscendingOrderTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeAscendingOrderTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNullsFirstOrLastTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNullsFirstOrLastTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeNullsFirstOrLastTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNullsFirstTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNullsFirstTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeNullsLastTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNullsLastTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeNullsFirstTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeNullsLastTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeNullsLastTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOrderByClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOrderByClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOrderByClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOrderByExpressionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOrderByExpressionsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOrderByExpressionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOrderByExpressionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOrderByExpressionListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOrderByExpressionListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOrderByAllTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOrderByAllTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOrderByAllTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLimitClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLimitClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeLimitClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOffsetClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOffsetClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeOffsetValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOffsetValueTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeOffsetClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static void InitializeLimitValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLimitValueTrampoline(PEGTransformer &transformer, + static void InitializeOffsetValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeOffsetValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeLimitValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeLimitValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLimitAllTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLimitAllTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeLimitAllTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLimitLiteralPercentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLimitLiteralPercentTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeLimitLiteralPercentTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLimitExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLimitExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeLimitExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeFetchClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFetchClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeFetchValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeFetchValueTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeFetchClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeFetchClauseWithoutValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr + FinalizeFetchClauseWithoutValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeFetchClauseWithValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr FinalizeFetchClauseWithValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeFetchValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeFetchValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeAliasedExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeAliasedExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeAliasedExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeColIdExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeColIdExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeColIdExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExpressionAsCollabelTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeExpressionAsCollabelTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeExpressionAsCollabelTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeExpressionOptIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeExpressionOptIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeValuesClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeValuesClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeValuesClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeValuesExpressionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeValuesExpressionsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeValuesExpressionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSetStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSetStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSetAssignmentOrTimeZoneTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeSetAssignmentOrTimeZoneTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeResetStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeResetStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeResetStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSetSchemaTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetSchemaTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSetSchemaTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeStandardAssignmentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeStandardAssignmentTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeStandardAssignmentTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSetVariableOrSettingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetVariableOrSettingTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSetVariableOrSettingTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSetTimeZoneTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetTimeZoneTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSetTimeZoneTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeZoneValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeZoneValueTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeZoneValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeZoneLocalTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeZoneLocalTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeZoneLocalTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeZoneDefaultTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeZoneDefaultTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeZoneDefaultTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeZoneStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeZoneStringLiteralTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeZoneStringLiteralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeZoneIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeZoneIdentifierTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeZoneIdentifierTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeZoneIntervalWithIntervalTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeZoneIntervalWithIntervalTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeZoneIntervalWithPrecisionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr + static arena_ptr FinalizeZoneIntervalWithPrecisionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static void InitializeSetSettingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetSettingTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSetSettingTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSetVariableTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetVariableTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSetVariableTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeVariableScopeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeVariableScopeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeSettingScopeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSettingScopeTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeVariableScopeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeSettingScopeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeSettingScopeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeLocalScopeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeLocalScopeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeLocalScopeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSessionScopeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSessionScopeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeGlobalScopeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeGlobalScopeTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeSessionScopeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeGlobalScopeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeGlobalScopeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSetAssignmentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSetAssignmentTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeVariableListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeVariableListTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeSetAssignmentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeVariableListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeVariableListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeTransactionStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeTransactionStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeTransactionStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeBeginTransactionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBeginTransactionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeBeginTransactionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeRollbackTransactionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeRollbackTransactionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeRollbackTransactionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCommitTransactionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeCommitTransactionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeCommitTransactionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeReadOrWriteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeReadOrWriteTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeReadOrWriteTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeReadOnlyOrReadWriteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeReadOnlyOrReadWriteTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeReadOnlyOrReadWriteTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeReadOnlyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeReadOnlyTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeReadOnlyTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeReadWriteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeReadWriteTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeReadWriteTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUpdateStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUpdateStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUpdateStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUpdateTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUpdateTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUpdateTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeBaseTableSetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBaseTableSetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeBaseTableSetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeBaseTableAliasSetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeBaseTableAliasSetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeBaseTableAliasSetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUpdateAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUpdateAliasTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUpdateAliasTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeUpdateSetClauseListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr FinalizeUpdateSetClauseListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUpdateSetClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUpdateSetClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static void InitializeUpdateSetTupleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUpdateSetTupleTrampoline(PEGTransformer &transformer, + static arena_ptr FinalizeUpdateSetClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static void InitializeUpdateSetElementListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - static unique_ptr FinalizeUpdateSetElementListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static void InitializeUpdateSetTupleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeUpdateSetTupleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUpdateSetElementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUpdateSetElementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUpdateSetElementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUpdateSetColumnTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUpdateSetColumnTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUpdateSetColumnTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUseStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUseStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUseStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUseTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeUseTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeUseTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeSchemaNameAsUseTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeSchemaNameAsUseTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeSchemaNameAsUseTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeCatalogNameAsUseTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeCatalogNameAsUseTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeCatalogNameAsUseTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeUseTargetCatalogSchemaTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr - FinalizeUseTargetCatalogSchemaTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeUseTargetCatalogSchemaTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeDotIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeDotIdentifierTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeDotIdentifierTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeVacuumStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeVacuumStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeVacuumStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeVacuumOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeVacuumOptionsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeVacuumOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeVacuumParensOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeVacuumParensOptionsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeVacuumParensOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeVacuumLegacyOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeVacuumLegacyOptionsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeVacuumLegacyOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeVacuumOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeVacuumOptionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeVacuumOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOptAnalyzeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOptAnalyzeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOptAnalyzeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOptFullTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOptFullTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOptFullTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOptFreezeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOptFreezeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOptFreezeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeOptVerboseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeOptVerboseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); + static arena_ptr FinalizeOptVerboseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeNameListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); - static unique_ptr FinalizeNameListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process); - //===--------------------------------------------------------------------===// - // END GENERATED TRAMPOLINE RULES - //===--------------------------------------------------------------------===// - - // Registration methods - void RegisterCommon(); - void RegisterCreateTable(); - void RegisterExpression(); - void RegisterPivot(); - void RegisterSelect(); - void RegisterKeywordsAndIdentifiers(); - void RegisterGenerated(); - template - void Register(const string &rule_name, FUNC function) { - auto &rule = grammar.GetMutableRule(rule_name); - if (rule.transform_process) { - throw InternalException("Rule %s already exists", rule_name); - } - grammar.SetTransformProcess( - rule_name, - [function](PEGTransformer &transformer, ParseResult &parse_result) -> unique_ptr { - transform_finalize_function_t finalize = - [function](PEGTransformer &transformer, - ParseResult &parse_result) -> unique_ptr { - auto result_value = function(transformer, parse_result); - return make_uniq>(std::move(result_value)); - }; - return make_uniq(transformer, parse_result, std::move(finalize)); - }); - } - - PEGTransformerFactory(const PEGTransformerFactory &) = delete; - - static unique_ptr TransformStatement(PEGTransformer &, ParseResult &list); - static const case_insensitive_map_t &GeneratedTransformFrameOps(); - - // common.gram - static unique_ptr TransformNumberLiteral(PEGTransformer &transformer, ParseResult &parse_result); - static string TransformStringLiteral(PEGTransformer &transformer, ParseResult &parse_result); - static DatePartSpecifier TransformIntervalToIntervalAsType(PEGTransformer &transformer, ParseResult &parse_result); - - static string ExtractFormat(const string &file_path); - - // create_table.gram - static string TransformIdentifier(PEGTransformer &transformer, ParseResult &parse_result); - - // expression.gram - static unique_ptr TransformExpression(PEGTransformer &transformer, ParseResult &parse_result); - static unique_ptr TransformPrefixExpression(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformOverClause(PEGTransformer &transformer, ParseResult &parse_result); - - // pivot.gram - static unique_ptr TransformPivotStatement(PEGTransformer &transformer, ParseResult &parse_result); - static unique_ptr TransformUnpivotStatement(PEGTransformer &transformer, - ParseResult &parse_result); - - // select.gram - static unique_ptr TransformSelectStatementInternalRule(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSimpleSelect(PEGTransformer &transformer, ParseResult &parse_result); - - static unique_ptr TransformTableRef(PEGTransformer &transformer, ParseResult &parse_result); - - static CommonTableExpressionMap TransformWithClause(PEGTransformer &transformer, ParseResult &parse_result); - static unique_ptr TransformWindowDefinition(PEGTransformer &transformer, - ParseResult &parse_result); - static string TransformIdentifierOrKeyword(PEGTransformer &transformer, ParseResult &parse_result); - - //===--------------------------------------------------------------------===// - // START GENERATED RULES - //===--------------------------------------------------------------------===// - static unique_ptr TransformAlterStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static arena_ptr FinalizeNameListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static unique_ptr TransformAlterStatement(PEGTransformer &transformer, unique_ptr alter_options); - static unique_ptr TransformAlterOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformAlterTableStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformAlterTableStmt(PEGTransformer &transformer, const optional &if_exists, unique_ptr base_table_name, vector> alter_table_options); - static unique_ptr TransformAlterSchemaStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformAlterSchemaStmt(PEGTransformer &transformer, const optional &if_exists, const QualifiedName &qualified_name, - unique_ptr rename_alter); - static unique_ptr TransformAlterTableOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformAddConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result); + unique_ptr alter_schema_options); static unique_ptr TransformAddConstraint(PEGTransformer &transformer, unique_ptr top_level_constraint); - static unique_ptr TransformDropConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDropConstraint(PEGTransformer &transformer, const optional &if_exists, const Identifier &identifier, const optional &drop_behavior); - static unique_ptr TransformAddColumnInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformAddColumn(PEGTransformer &transformer, const bool &has_result, const optional &if_not_exists, AddColumnEntry add_column_entry); - static unique_ptr TransformAddColumnEntryInternal(PEGTransformer &transformer, - ParseResult &parse_result); static AddColumnEntry TransformAddColumnEntry(PEGTransformer &transformer, const vector &dotted_identifier, const optional &type, optional generated_column, optional> column_constraint); - static unique_ptr TransformDropColumnInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDropColumn(PEGTransformer &transformer, const bool &has_result, const optional &if_exists, unique_ptr nested_column_name, const optional &drop_behavior); - static unique_ptr TransformAlterColumnInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformAlterColumn(PEGTransformer &transformer, const bool &has_result, unique_ptr nested_column_name, unique_ptr alter_column_entry); - static unique_ptr TransformRenameColumnInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformRenameColumn(PEGTransformer &transformer, const bool &has_result, unique_ptr nested_column_name, const Identifier &identifier); - static unique_ptr TransformNestedColumnNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformNestedColumnName(PEGTransformer &transformer, const optional> &identifier_dot, const Identifier &column_name); - static unique_ptr TransformIdentifierDotInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformIdentifierDot(PEGTransformer &transformer, const Identifier &identifier); - static unique_ptr TransformRenameAlterInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformRenameAlter(PEGTransformer &transformer, const Identifier &identifier); - static unique_ptr TransformSetPartitionedByInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSetPartitionedBy(PEGTransformer &transformer, vector> expression); - static unique_ptr TransformResetPartitionedByInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformResetPartitionedBy(PEGTransformer &transformer); - static unique_ptr TransformSetSortedByInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSetSortedBy(PEGTransformer &transformer, vector order_by_expressions); - static unique_ptr TransformResetSortedByInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformResetSortedBy(PEGTransformer &transformer); - static unique_ptr TransformSetOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSetOptions(PEGTransformer &transformer, case_insensitive_map_t> rel_option_list); - static unique_ptr TransformResetOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformResetOptions(PEGTransformer &transformer, case_insensitive_map_t> rel_option_list); - static unique_ptr TransformAlterColumnEntryInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformAddOrDropDefaultInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformAddDefaultInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformAddDefault(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformDropDefaultInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDropDefault(PEGTransformer &transformer); - static unique_ptr TransformChangeNullabilityInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformChangeNullability(PEGTransformer &transformer, const string &drop_or_set); - static unique_ptr TransformDropOrSetInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformDropNullabilityInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformDropNullability(PEGTransformer &transformer); - static unique_ptr TransformSetNullabilityInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformSetNullability(PEGTransformer &transformer); - static unique_ptr TransformAlterTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformAlterType(PEGTransformer &transformer, const bool &has_result, const optional &type, + optional column_collation, optional> using_expression); - static unique_ptr TransformUsingExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformUsingExpression(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformAlterViewStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformAlterViewStmt(PEGTransformer &transformer, const optional &if_exists, unique_ptr base_table_name, unique_ptr rename_alter); - static unique_ptr TransformAlterSequenceStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformAlterSequenceStmt(PEGTransformer &transformer, const optional &if_exists, const QualifiedName &qualified_sequence_name, unique_ptr alter_sequence_options); - static unique_ptr TransformQualifiedSequenceNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static QualifiedName TransformQualifiedSequenceName(PEGTransformer &transformer, const optional &catalog_qualification, const optional &schema_qualification, const Identifier &sequence_name); - static unique_ptr TransformAlterSequenceOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformRenameAlterSequenceOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformRenameAlterSequenceOptions(PEGTransformer &transformer, unique_ptr rename_alter); - static unique_ptr TransformSetSequenceOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSetSequenceOption(PEGTransformer &transformer, vector>> sequence_option); - static unique_ptr TransformAlterDatabaseStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformAlterDatabaseStmt(PEGTransformer &transformer, const optional &if_exists, const Identifier &identifier, const Identifier &identifier_1); - static unique_ptr TransformAnalyzeStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformAnalyzeStatement(PEGTransformer &transformer, const Identifier &analyze_keyword, const optional &analyze_verbose, optional analyze_target); - static unique_ptr TransformAnalyzeTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); static AnalyzeTarget TransformAnalyzeTarget(PEGTransformer &transformer, unique_ptr base_table_name, const optional> &name_list); - static unique_ptr TransformAnalyzeVerboseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformAnalyzeVerbose(PEGTransformer &transformer); - static unique_ptr TransformAttachStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformAttachStatement(PEGTransformer &transformer, const optional &or_replace, const optional &if_not_exists, const bool &has_result, unique_ptr database_path, const optional &attach_alias, const optional> &attach_options); - static unique_ptr TransformDatabasePathInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDatabasePath(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformAttachAliasInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformAttachAlias(PEGTransformer &transformer, const Identifier &col_id); - static unique_ptr TransformAttachOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformAttachOptions(PEGTransformer &transformer, const vector &generic_copy_option_list); - static unique_ptr TransformCallStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCallStatement(PEGTransformer &transformer, const QualifiedName &qualified_table_function, vector table_function_arguments); - static unique_ptr TransformCheckpointStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCheckpointStatement(PEGTransformer &transformer, const optional &checkpoint_force, const optional &catalog_name); - static unique_ptr TransformCheckpointForceInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformCheckpointForce(PEGTransformer &transformer); - static unique_ptr TransformCommentStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCommentStatement(PEGTransformer &transformer, const CatalogType &comment_on_type, - const vector &dotted_identifier, + const vector &comment_target, const Value &comment_value); - static unique_ptr TransformCommentOnTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCommentTableInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static vector TransformCommentStringLiteralIdentifier(PEGTransformer &transformer, + const Identifier &string_literal_identifier); static CatalogType TransformCommentTable(PEGTransformer &transformer); - static unique_ptr TransformCommentSequenceInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CatalogType TransformCommentSequence(PEGTransformer &transformer); - static unique_ptr TransformCommentFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CatalogType TransformCommentFunction(PEGTransformer &transformer); - static unique_ptr TransformCommentMacroTableInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CatalogType TransformCommentMacroTable(PEGTransformer &transformer); - static unique_ptr TransformCommentMacroInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CatalogType TransformCommentMacro(PEGTransformer &transformer); - static unique_ptr TransformCommentViewInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CatalogType TransformCommentView(PEGTransformer &transformer); - static unique_ptr TransformCommentDatabaseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CatalogType TransformCommentDatabase(PEGTransformer &transformer); - static unique_ptr TransformCommentIndexInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CatalogType TransformCommentIndex(PEGTransformer &transformer); - static unique_ptr TransformCommentSchemaInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CatalogType TransformCommentSchema(PEGTransformer &transformer); - static unique_ptr TransformCommentTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CatalogType TransformCommentType(PEGTransformer &transformer); - static unique_ptr TransformCommentColumnInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CatalogType TransformCommentColumn(PEGTransformer &transformer); - static unique_ptr TransformCommentValueInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformStringLiteralValueInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Value TransformStringLiteralValue(PEGTransformer &transformer, const string &string_literal); - static unique_ptr TransformAnalyzeKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformAnalyzeKeyword(PEGTransformer &transformer); - static unique_ptr TransformExpressionStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformExpressionStatement(PEGTransformer &transformer, - vector> expression_alias); - static unique_ptr TransformExpressionAliasInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformConstraintNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformConstraintName(PEGTransformer &transformer, const Identifier &col_id_or_string); - static unique_ptr TransformCollationNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformCollationName(PEGTransformer &transformer, const Identifier &identifier); - static unique_ptr TransformTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static unique_ptr TransformNumberLiteral(PEGTransformer &transformer, ParseResult &parse_result); + static string TransformStringLiteral(PEGTransformer &transformer, ParseResult &parse_result); static LogicalType TransformType(PEGTransformer &transformer, unique_ptr type_variations, const optional> &array_bounds); - static unique_ptr TransformTypeVariationsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSimpleTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCharacterSimpleTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCharacterSimpleType(PEGTransformer &transformer, optional>> type_modifiers); - static unique_ptr TransformQualifiedSimpleTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformQualifiedSimpleType(PEGTransformer &transformer, const QualifiedName &qualified_type_name, optional>> type_modifiers); - static unique_ptr TransformIntervalTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformIntervalIntervalInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformIntervalWithSpecifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformIntervalWithRangeSpecifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformIntervalWithRangeSpecifier(PEGTransformer &transformer, const DatePartSpecifier &interval_to_interval_as_type); - static unique_ptr TransformIntervalWithSimpleSpecifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformIntervalWithSimpleSpecifier(PEGTransformer &transformer, const DatePartSpecifier &interval); - static unique_ptr TransformIntervalWithoutSpecifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformIntervalWithoutSpecifier(PEGTransformer &transformer); - static unique_ptr TransformYearKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static DatePartSpecifier TransformIntervalToIntervalAsType(PEGTransformer &transformer, ParseResult &parse_result); static DatePartSpecifier TransformYearKeyword(PEGTransformer &transformer); - static unique_ptr TransformMonthKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformMonthKeyword(PEGTransformer &transformer); - static unique_ptr TransformDayKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformDayKeyword(PEGTransformer &transformer); - static unique_ptr TransformHourKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformHourKeyword(PEGTransformer &transformer); - static unique_ptr TransformMinuteKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformMinuteKeyword(PEGTransformer &transformer); - static unique_ptr TransformSecondKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformSecondKeyword(PEGTransformer &transformer); - static unique_ptr TransformMillisecondKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformMillisecondKeyword(PEGTransformer &transformer); - static unique_ptr TransformMicrosecondKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformMicrosecondKeyword(PEGTransformer &transformer); - static unique_ptr TransformWeekKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformWeekKeyword(PEGTransformer &transformer); - static unique_ptr TransformQuarterKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformQuarterKeyword(PEGTransformer &transformer); - static unique_ptr TransformDecadeKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformDecadeKeyword(PEGTransformer &transformer); - static unique_ptr TransformCenturyKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformCenturyKeyword(PEGTransformer &transformer); - static unique_ptr TransformMillenniumKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformMillenniumKeyword(PEGTransformer &transformer); - static unique_ptr TransformIntervalInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformIntervalToIntervalInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformYearToMonthInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformYearToMonth(PEGTransformer &transformer, const DatePartSpecifier &year_keyword, const DatePartSpecifier &month_keyword); - static unique_ptr TransformDayToHourInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformDayToHour(PEGTransformer &transformer, const DatePartSpecifier &day_keyword, const DatePartSpecifier &hour_keyword); - static unique_ptr TransformDayToMinuteInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformDayToMinute(PEGTransformer &transformer, const DatePartSpecifier &day_keyword, const DatePartSpecifier &minute_keyword); - static unique_ptr TransformDayToSecondInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformDayToSecond(PEGTransformer &transformer, const DatePartSpecifier &day_keyword, const DatePartSpecifier &second_keyword); - static unique_ptr TransformHourToMinuteInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformHourToMinute(PEGTransformer &transformer, const DatePartSpecifier &hour_keyword, const DatePartSpecifier &minute_keyword); - static unique_ptr TransformHourToSecondInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformHourToSecond(PEGTransformer &transformer, const DatePartSpecifier &hour_keyword, const DatePartSpecifier &second_keyword); - static unique_ptr TransformMinuteToSecondInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DatePartSpecifier TransformMinuteToSecond(PEGTransformer &transformer, const DatePartSpecifier &minute_keyword, const DatePartSpecifier &second_keyword); - static unique_ptr TransformBitTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformBitType(PEGTransformer &transformer, const bool &has_result, optional>> expression); - static unique_ptr TransformGeometryTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformGeometryType(PEGTransformer &transformer, optional> expression); - static unique_ptr TransformVariantTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformVariantType(PEGTransformer &transformer); - static unique_ptr TransformNumericTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSimpleNumericTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSimpleNumericType(PEGTransformer &transformer, const string &child); - static unique_ptr TransformDecimalNumericTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformIntTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformIntType(PEGTransformer &transformer); - static unique_ptr TransformIntegerTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformIntegerType(PEGTransformer &transformer); - static unique_ptr TransformSmallintTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformSmallintType(PEGTransformer &transformer); - static unique_ptr TransformBigintTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformBigintType(PEGTransformer &transformer); - static unique_ptr TransformRealTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformRealType(PEGTransformer &transformer); - static unique_ptr TransformBooleanTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformBooleanType(PEGTransformer &transformer); - static unique_ptr TransformDoubleTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformDoubleType(PEGTransformer &transformer); - static unique_ptr TransformFloatTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformFloatType(PEGTransformer &transformer, optional> number_literal); - static unique_ptr TransformDecimalTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDecimalType(PEGTransformer &transformer, optional>> type_modifiers); - static unique_ptr TransformDecTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDecType(PEGTransformer &transformer, optional>> type_modifiers); - static unique_ptr TransformNumericModTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformNumericModType(PEGTransformer &transformer, optional>> type_modifiers); - static unique_ptr TransformQualifiedTypeNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformTypeNameAsQualifiedNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static QualifiedName TransformTypeNameAsQualifiedName(PEGTransformer &transformer, const Identifier &type_name); - static unique_ptr TransformCatalogReservedSchemaTypeNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static QualifiedName TransformCatalogReservedSchemaTypeName(PEGTransformer &transformer, const Identifier &catalog_qualification, const vector &reserved_schema_qualification, const Identifier &reserved_type_name); - static unique_ptr TransformSchemaReservedTypeNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static QualifiedName TransformSchemaReservedTypeName(PEGTransformer &transformer, const Identifier &schema_qualification, const Identifier &reserved_type_name); - static unique_ptr TransformTypeModifiersInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformTypeModifiers(PEGTransformer &transformer, optional>> expression); - static unique_ptr TransformRowTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformRowType(PEGTransformer &transformer, - const optional> &col_id_type_list); - static unique_ptr TransformSetofTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSetofType(PEGTransformer &transformer, const LogicalType &type); - static unique_ptr TransformUnionTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformUnionType(PEGTransformer &transformer, - const child_list_t &col_id_type_list); - static unique_ptr TransformColIdTypeListInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static child_list_t TransformColIdTypeList(PEGTransformer &transformer, - const vector> &col_id_type); - static unique_ptr TransformMapTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static unique_ptr TransformNestedType(PEGTransformer &transformer, + unique_ptr type_variations, + const optional> &array_bounds); + static unique_ptr + TransformRowType(PEGTransformer &transformer, + optional>>> col_id_type_list); + static unique_ptr TransformSetofType(PEGTransformer &transformer, + unique_ptr nested_type); + static unique_ptr + TransformUnionType(PEGTransformer &transformer, + vector>> col_id_type_list); + static vector>> + TransformColIdTypeList(PEGTransformer &transformer, + vector>> col_id_type); static unique_ptr TransformMapType(PEGTransformer &transformer, - const optional> &type); - static unique_ptr TransformTupleTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); + optional>> nested_type); static unique_ptr TransformTupleType(PEGTransformer &transformer, - const vector &type); - static unique_ptr TransformColIdTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static pair TransformColIdType(PEGTransformer &transformer, const Identifier &col_id, - const LogicalType &type); - static unique_ptr TransformArrayBoundsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformArrayKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); + vector> nested_type); + static pair> + TransformColIdType(PEGTransformer &transformer, const Identifier &col_id, unique_ptr nested_type); static int64_t TransformArrayKeyword(PEGTransformer &transformer); - static unique_ptr TransformArrayKeywordWithBoundsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static int64_t TransformArrayKeywordWithBounds(PEGTransformer &transformer, const int64_t &square_brackets_array); - static unique_ptr TransformSquareBracketsArrayInternal(PEGTransformer &transformer, - ParseResult &parse_result); static int64_t TransformSquareBracketsArray(PEGTransformer &transformer, optional> expression); - static unique_ptr TransformTimeTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTimeType(PEGTransformer &transformer, const LogicalTypeId &time_or_timestamp, optional>> type_modifiers, const optional &time_zone); - static unique_ptr TransformTimeOrTimestampInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformTimeTypeIdInternal(PEGTransformer &transformer, - ParseResult &parse_result); static LogicalTypeId TransformTimeTypeId(PEGTransformer &transformer); - static unique_ptr TransformTimestampTypeIdInternal(PEGTransformer &transformer, - ParseResult &parse_result); static LogicalTypeId TransformTimestampTypeId(PEGTransformer &transformer); - static unique_ptr TransformTimeZoneInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformTimeZone(PEGTransformer &transformer, const bool &with_or_without); - static unique_ptr TransformWithOrWithoutInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformWithRuleInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformWithRule(PEGTransformer &transformer); - static unique_ptr TransformWithoutRuleInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformWithoutRule(PEGTransformer &transformer); - static unique_ptr TransformConnectStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformConnectStatement(PEGTransformer &transformer, optional> session_target); - static unique_ptr TransformDisconnectStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDisconnectStatement(PEGTransformer &transformer); - static unique_ptr TransformSessionTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformLocalSessionTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformLocalSessionTarget(PEGTransformer &transformer); - static unique_ptr TransformStringSessionTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformStringSessionTarget(PEGTransformer &transformer, const string &string_literal, const optional> &generic_copy_option_list); - static unique_ptr TransformCatalogSessionTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCatalogSessionTarget(PEGTransformer &transformer, const Identifier &catalog_name); - static unique_ptr TransformCopyStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCopyStatement(PEGTransformer &transformer, unique_ptr copy_variations); - static unique_ptr TransformCopyVariationsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCopyTableInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCopyTable(PEGTransformer &transformer, - unique_ptr base_table_name, - const optional> &insert_column_list, - const bool &from_or_to, - unique_ptr copy_file_name, - const optional> ©_options); - static unique_ptr TransformFromOrToInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCopyFromInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static unique_ptr + TransformCopyTable(PEGTransformer &transformer, unique_ptr base_table_name, + const optional> &insert_column_list, const bool &from_or_to, + unique_ptr copy_file_name, const optional &legacy_option, + const optional> ©_options); static bool TransformCopyFrom(PEGTransformer &transformer); - static unique_ptr TransformCopyToInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformCopyTo(PEGTransformer &transformer); - static unique_ptr TransformCopySelectInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCopySelect(PEGTransformer &transformer, unique_ptr select_statement_internal, unique_ptr copy_file_name, const optional> ©_options); - static unique_ptr TransformCopyFileNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCopyFileNameExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCopyFileNameStringLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCopyFileNameStringLiteral(PEGTransformer &transformer, const string &string_literal); - static unique_ptr TransformCopyFileNameIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCopyFileNameIdentifier(PEGTransformer &transformer, const Identifier &identifier); - static unique_ptr TransformCopyFileNameIdentifierColIdInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCopyFileNameIdentifierColId(PEGTransformer &transformer, const Identifier &identifier_col_id); - static unique_ptr TransformIdentifierColIdInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformIdentifierColId(PEGTransformer &transformer, const Identifier &identifier, - const Identifier &col_id); - static unique_ptr TransformCopyOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); + const vector ©_file_name_suffix); + static GenericCopyOption TransformCopyLegacyOption(PEGTransformer &transformer, const bool &has_result, + const string &delimiter); static vector TransformCopyOptions(PEGTransformer &transformer, const bool &has_result, const vector ©_option_list); - static unique_ptr TransformCopyOptionListInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSpecializedOptionListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformSpecializedOptionList(PEGTransformer &transformer, const GenericCopyOption &specialized_option, const optional> &specialized_option_tail); - static unique_ptr TransformSpecializedOptionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOption TransformSpecializedOptionTail(PEGTransformer &transformer, const bool &has_result, const GenericCopyOption &specialized_option); - static unique_ptr TransformSpecializedOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSingleOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformBinaryOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOption TransformBinaryOption(PEGTransformer &transformer); - static unique_ptr TransformFreezeOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOption TransformFreezeOption(PEGTransformer &transformer); - static unique_ptr TransformOidsOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOption TransformOidsOption(PEGTransformer &transformer); - static unique_ptr TransformCsvOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOption TransformCsvOption(PEGTransformer &transformer); - static unique_ptr TransformHeaderOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOption TransformHeaderOption(PEGTransformer &transformer); - static unique_ptr TransformNullAsOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOption TransformNullAsOption(PEGTransformer &transformer, const bool &has_result, const string &string_literal); - static unique_ptr TransformDelimiterAsOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOption TransformDelimiterAsOption(PEGTransformer &transformer, const bool &has_result, const string &string_literal); - static unique_ptr TransformQuoteAsOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOption TransformQuoteAsOption(PEGTransformer &transformer, const bool &has_result, const string &string_literal); - static unique_ptr TransformEscapeAsOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOption TransformEscapeAsOption(PEGTransformer &transformer, const bool &has_result, const string &string_literal); - static unique_ptr TransformEncodingOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOption TransformEncodingOption(PEGTransformer &transformer, const string &string_literal); - static unique_ptr TransformForceQuoteOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOption TransformForceQuoteOption(PEGTransformer &transformer, const optional &force_quote, const vector &star_symbol_column_list); - static unique_ptr TransformStarSymbolColumnListInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformForceQuoteInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformForceQuote(PEGTransformer &transformer); - static unique_ptr TransformPartitionByOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOption TransformPartitionByOption(PEGTransformer &transformer, const vector &partition_by_column_list); - static unique_ptr TransformPartitionByColumnListInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformStarPartitionByColumnListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformStarPartitionByColumnList(PEGTransformer &transformer); - static unique_ptr - TransformParenthesizedPartitionByColumnListInternal(PEGTransformer &transformer, ParseResult &parse_result); static vector TransformParenthesizedPartitionByColumnList(PEGTransformer &transformer, const vector &column_list); - static unique_ptr TransformSinglePartitionByColumnListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformSinglePartitionByColumnList(PEGTransformer &transformer, const Identifier &col_id); - static unique_ptr TransformForceNullOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOption TransformForceNullOption(PEGTransformer &transformer, const optional &force_not_null, const vector &column_list); - static unique_ptr TransformForceNotNullInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformForceNotNull(PEGTransformer &transformer); - static unique_ptr TransformCopyGenericOptionListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformCopyGenericOptionList(PEGTransformer &transformer, const vector ©_generic_option); - static unique_ptr TransformCopyGenericOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformOrderByCopyOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOption TransformOrderByCopyOption(PEGTransformer &transformer, optional generic_copy_option_value); - static unique_ptr TransformPartitionedByCopyOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOption TransformPartitionedByCopyOption(PEGTransformer &transformer, optional generic_copy_option_value); - static unique_ptr TransformGenericCopyOptionListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformGenericCopyOptionList(PEGTransformer &transformer, const vector &generic_copy_option); - static unique_ptr TransformGenericCopyOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOption TransformGenericCopyOption(PEGTransformer &transformer, const Identifier ©_option_name, optional generic_copy_option_value); - static unique_ptr TransformGenericCopyOptionValueInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformGenericCopyOptionOrderListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOptionValue TransformGenericCopyOptionOrderList(PEGTransformer &transformer, vector generic_copy_option_parenthesized_expression_list); - static unique_ptr TransformGenericCopyOptionExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOptionValue TransformGenericCopyOptionExpression(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr - TransformGenericCopyOptionParenthesizedExpressionListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformGenericCopyOptionParenthesizedExpressionList(PEGTransformer &transformer, vector order_by_expression_list); - static unique_ptr TransformCopyFromDatabaseInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCopyFromDatabaseWithFlagInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCopyFromDatabaseWithFlag(PEGTransformer &transformer, const Identifier &col_id, const Identifier &col_id_1, const CopyDatabaseType ©_database_flag); - static unique_ptr TransformCopyFromDatabaseWithoutFlagInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCopyFromDatabaseWithoutFlag(PEGTransformer &transformer, const Identifier &col_id, const Identifier &col_id_1); - static unique_ptr TransformCopyDatabaseFlagInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CopyDatabaseType TransformCopyDatabaseFlag(PEGTransformer &transformer, const CopyDatabaseType &schema_or_data); - static unique_ptr TransformSchemaOrDataInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCopySchemaInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CopyDatabaseType TransformCopySchema(PEGTransformer &transformer); - static unique_ptr TransformCopyDataInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CopyDatabaseType TransformCopyData(PEGTransformer &transformer); - static unique_ptr TransformCreateIndexStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCreateIndexStmt(PEGTransformer &transformer, const optional &unique_index, const optional &if_not_exists, const optional &index_name, - unique_ptr base_table_name, - const optional> &insert_column_list, const optional &index_type, - optional>> index_element, + unique_ptr base_table_name, const optional &index_type, + vector> index_element, optional>> with_list, optional> where_clause); - static unique_ptr TransformWithListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static case_insensitive_map_t> TransformWithList(PEGTransformer &transformer, case_insensitive_map_t> rel_option_or_oids); - static unique_ptr TransformRelOptionOrOidsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformRelOptionListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static case_insensitive_map_t> TransformRelOptionList(PEGTransformer &transformer, vector>> rel_option); - static unique_ptr TransformOidsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static case_insensitive_map_t> TransformOids(PEGTransformer &transformer, const bool &with_or_without_oids); - static unique_ptr TransformWithOrWithoutOidsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformWithOidsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformWithOids(PEGTransformer &transformer); - static unique_ptr TransformWithoutOidsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformWithoutOids(PEGTransformer &transformer); - static unique_ptr TransformIndexElementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformIndexElement(PEGTransformer &transformer, unique_ptr expression, const optional &desc_or_asc, const optional &nulls_first_or_last); - static unique_ptr TransformUniqueIndexInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformUniqueIndex(PEGTransformer &transformer); - static unique_ptr TransformIndexTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformIndexType(PEGTransformer &transformer, const Identifier &identifier); - static unique_ptr TransformRelOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static pair> TransformRelOption(PEGTransformer &transformer, const Identifier &rel_option_name, optional> rel_option_argument_opt); - static unique_ptr TransformRelOptionNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformRelOptionName(PEGTransformer &transformer, const string &child); - static unique_ptr TransformDottedIdentifierStringInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static string TransformDottedIdentifierString(PEGTransformer &transformer, const vector &dotted_identifier); - static unique_ptr TransformRelOptionArgumentOptInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static string TransformDottedColLabel(PEGTransformer &transformer, const string &col_label, + const optional> &dot_col_label); static unique_ptr TransformRelOptionArgumentOpt(PEGTransformer &transformer, unique_ptr def_arg); - static unique_ptr TransformDefArgInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformDefArgNullInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDefArgNull(PEGTransformer &transformer, const Value &null_literal); - static unique_ptr TransformDefArgKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDefArgKeyword(PEGTransformer &transformer, const string &reserved_keyword); - static unique_ptr TransformDefArgStringLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDefArgStringLiteral(PEGTransformer &transformer, const string &string_literal); - static unique_ptr TransformNoneLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformNoneLiteral(PEGTransformer &transformer); - static unique_ptr TransformCreateMacroStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCreateMacroStmt(PEGTransformer &transformer, const bool ¯o_or_function, const optional &if_not_exists, const QualifiedName &qualified_name, vector> macro_definition); - static unique_ptr TransformMacroOrFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformMacroKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformMacroKeyword(PEGTransformer &transformer); - static unique_ptr TransformFunctionKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformFunctionKeyword(PEGTransformer &transformer); - static unique_ptr TransformMacroDefinitionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformMacroDefinition(PEGTransformer &transformer, optional> macro_parameters, unique_ptr macro_definition_body); - static unique_ptr TransformMacroDefinitionBodyInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformMacroParametersInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformMacroParameters(PEGTransformer &transformer, vector macro_parameter); - static unique_ptr TransformMacroParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSimpleParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MacroParameter TransformSimpleParameter(PEGTransformer &transformer, const Identifier &type_func_name, const optional &type); - static unique_ptr TransformScalarMacroDefinitionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformScalarMacroDefinition(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformTableMacroDefinitionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTableMacroDefinition(PEGTransformer &transformer, unique_ptr select_statement_internal); - static unique_ptr TransformCreateSchemaStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCreateSchemaStmt(PEGTransformer &transformer, - const optional &if_not_exists, - const QualifiedName &qualified_name); - static unique_ptr TransformCreateSecretStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static unique_ptr + TransformCreateSchemaStmt(PEGTransformer &transformer, const optional &if_not_exists, + const QualifiedName &qualified_name, + optional>> with_option_list); + static case_insensitive_map_t> + TransformWithOptionList(PEGTransformer &transformer, + case_insensitive_map_t> rel_option_list); static unique_ptr TransformCreateSecretStmt(PEGTransformer &transformer, const optional &if_not_exists, const optional &secret_name, const optional &secret_storage_specifier, const vector &generic_copy_option_list); - static unique_ptr TransformSecretStorageSpecifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformSecretStorageSpecifier(PEGTransformer &transformer, const Identifier &identifier); - static unique_ptr TransformSecretNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformSecretName(PEGTransformer &transformer, const Identifier &col_id); - static unique_ptr TransformCreateSequenceStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCreateSequenceStmt(PEGTransformer &transformer, const optional &if_not_exists, const QualifiedName &qualified_name, optional>>> sequence_option); - static unique_ptr TransformSequenceOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSeqSetCycleInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSeqCycleInternal(PEGTransformer &transformer, - ParseResult &parse_result); static pair> TransformSeqCycle(PEGTransformer &transformer); - static unique_ptr TransformSeqNoCycleInternal(PEGTransformer &transformer, - ParseResult &parse_result); static pair> TransformSeqNoCycle(PEGTransformer &transformer); - static unique_ptr TransformSeqSetIncrementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static pair> TransformSeqSetIncrement(PEGTransformer &transformer, const bool &has_result, unique_ptr expression); - static unique_ptr TransformSeqSetMinMaxInternal(PEGTransformer &transformer, - ParseResult &parse_result); static pair> TransformSeqSetMinMax(PEGTransformer &transformer, const string &seq_min_or_max, unique_ptr expression); - static unique_ptr TransformSeqNoMinMaxInternal(PEGTransformer &transformer, - ParseResult &parse_result); static pair> TransformSeqNoMinMax(PEGTransformer &transformer, const string &seq_min_or_max); - static unique_ptr TransformSeqStartWithInternal(PEGTransformer &transformer, - ParseResult &parse_result); static pair> TransformSeqStartWith(PEGTransformer &transformer, const bool &has_result, unique_ptr expression); - static unique_ptr TransformSeqOwnedByInternal(PEGTransformer &transformer, - ParseResult &parse_result); static pair> TransformSeqOwnedBy(PEGTransformer &transformer, const QualifiedName &qualified_name); - static unique_ptr TransformSeqMinOrMaxInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformMinValueInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformMinValue(PEGTransformer &transformer); - static unique_ptr TransformMaxValueInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformMaxValue(PEGTransformer &transformer); - static unique_ptr TransformCreateStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCreateStatement(PEGTransformer &transformer, const optional &or_replace, const optional &temporary, unique_ptr create_statement_variation); - static unique_ptr TransformCreateStatementVariationInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformOrReplaceInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformOrReplace(PEGTransformer &transformer); - static unique_ptr TransformTemporaryInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformPersistentInternal(PEGTransformer &transformer, - ParseResult &parse_result); static SecretPersistType TransformPersistent(PEGTransformer &transformer); - static unique_ptr TransformTempPersistentInternal(PEGTransformer &transformer, - ParseResult &parse_result); static SecretPersistType TransformTempPersistent(PEGTransformer &transformer); - static unique_ptr TransformTemporaryPersistentInternal(PEGTransformer &transformer, - ParseResult &parse_result); static SecretPersistType TransformTemporaryPersistent(PEGTransformer &transformer); - static unique_ptr TransformCreateTableStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCreateTableStmt(PEGTransformer &transformer, const optional &if_not_exists, const QualifiedName &qualified_name, CreateTableDefinition create_table_definition, const optional &commit_action); - static unique_ptr TransformCreateTableDefinitionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCreateTableAsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CreateTableDefinition TransformCreateTableAs(PEGTransformer &transformer, optional identifier_list, optional partition_sorted_options, optional>> with_list, unique_ptr statement, const optional &with_data); - static unique_ptr TransformPartitionSortedOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformPartitionOptSortedOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static PartitionSortedOptions TransformPartitionOptSortedOptions(PEGTransformer &transformer, vector> partition_options, optional>> sorted_options); - static unique_ptr TransformSortedOptPartitionOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static PartitionSortedOptions TransformSortedOptPartitionOptions(PEGTransformer &transformer, vector> sorted_options, optional>> partition_options); - static unique_ptr TransformPartitionOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformPartitionOptions(PEGTransformer &transformer, vector> expression); - static unique_ptr TransformSortedOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformSortedOptions(PEGTransformer &transformer, vector> expression); - static unique_ptr TransformWithDataInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformWithDataOnlyInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformWithDataOnly(PEGTransformer &transformer); - static unique_ptr TransformWithNoDataInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformWithNoData(PEGTransformer &transformer); - static unique_ptr TransformIdentifierListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ColumnList TransformIdentifierList(PEGTransformer &transformer, const vector &identifier); - static unique_ptr TransformCreateColumnListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CreateTableDefinition TransformCreateColumnList(PEGTransformer &transformer, optional create_table_column_list, optional partition_sorted_options, optional>> with_list); - static unique_ptr TransformIfNotExistsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformIfNotExists(PEGTransformer &transformer); - static unique_ptr TransformQualifiedNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr - TransformSchemaReservedIdentifierOrStringLiteralInternal(PEGTransformer &transformer, ParseResult &parse_result); static QualifiedName TransformSchemaReservedIdentifierOrStringLiteral(PEGTransformer &transformer, const Identifier &schema_qualification, const Identifier &reserved_identifier_or_string_literal); - static unique_ptr - TransformCatalogReservedSchemaIdentifierInternal(PEGTransformer &transformer, ParseResult &parse_result); static QualifiedName TransformCatalogReservedSchemaIdentifier(PEGTransformer &transformer, const Identifier &catalog_qualification, const vector &reserved_schema_qualification, const Identifier &reserved_identifier_or_string_literal); - static unique_ptr TransformIdentifierOrStringLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result); static QualifiedName TransformIdentifierOrStringLiteral(PEGTransformer &transformer, const string &child); - static unique_ptr - TransformReservedIdentifierOrStringLiteralInternal(PEGTransformer &transformer, ParseResult &parse_result); - static unique_ptr TransformCatalogQualificationInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformCatalogQualification(PEGTransformer &transformer, const Identifier &catalog_name); - static unique_ptr TransformSchemaQualificationInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformSchemaQualification(PEGTransformer &transformer, const Identifier &schema_name); - static unique_ptr TransformReservedSchemaQualificationInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformReservedSchemaQualification(PEGTransformer &transformer, const Identifier &reserved_schema_name); - static unique_ptr TransformTableQualificationInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformTableQualification(PEGTransformer &transformer, const Identifier &table_name); - static unique_ptr TransformReservedTableQualificationInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformReservedTableQualification(PEGTransformer &transformer, const Identifier &reserved_table_name); - static unique_ptr TransformCreateTableColumnListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ColumnElements TransformCreateTableColumnList(PEGTransformer &transformer, vector create_table_column_element); - static unique_ptr TransformCreateTableColumnElementInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCreateTableColumnDefinitionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CreateTableColumnElement TransformCreateTableColumnDefinition(PEGTransformer &transformer, ConstraintColumnDefinition column_definition); - static unique_ptr TransformCreateTableConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CreateTableColumnElement TransformCreateTableConstraint(PEGTransformer &transformer, unique_ptr top_level_constraint); - static unique_ptr TransformColumnDefinitionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ConstraintColumnDefinition - TransformColumnDefinition(PEGTransformer &transformer, const vector &dotted_identifier, + TransformColumnDefinition(PEGTransformer &transformer, const Identifier &identifier, const optional &type, optional generated_column, - const bool &has_result, optional> column_constraint); - static unique_ptr TransformColumnConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformNotNullConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result); + optional> column_constraint); + static ColumnConstraintEntry TransformNameableColumnConstraint(PEGTransformer &transformer, const bool &has_result, + ColumnConstraintEntry child); static ColumnConstraintEntry TransformNotNullConstraint(PEGTransformer &transformer, const bool &child); - static unique_ptr TransformNullConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformNullConstraint(PEGTransformer &transformer); - static unique_ptr TransformNotNullColumnConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformNotNullColumnConstraint(PEGTransformer &transformer); - static unique_ptr TransformUniqueConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ColumnConstraintEntry TransformUniqueConstraint(PEGTransformer &transformer, const optional &constraint_timing); - static unique_ptr TransformPrimaryKeyConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ColumnConstraintEntry TransformPrimaryKeyConstraint(PEGTransformer &transformer, const optional &constraint_timing); - static unique_ptr TransformConstraintTimingInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformImmediateConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ConstraintCheckMode TransformImmediateConstraint(PEGTransformer &transformer); - static unique_ptr TransformDeferredConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ConstraintCheckMode TransformDeferredConstraint(PEGTransformer &transformer); - static unique_ptr TransformDefaultValueInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ColumnConstraintEntry TransformDefaultValue(PEGTransformer &transformer, unique_ptr column_default_expr); - static unique_ptr TransformCheckConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ColumnConstraintEntry TransformCheckConstraint(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformForeignKeyConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ColumnConstraintEntry TransformForeignKeyConstraint(PEGTransformer &transformer, unique_ptr base_table_name, const optional> &column_list, - const KeyActions &key_actions); - static unique_ptr TransformColumnCollationInternal(PEGTransformer &transformer, - ParseResult &parse_result); + const optional &key_actions); static ColumnConstraintEntry TransformColumnCollation(PEGTransformer &transformer, const vector &dotted_identifier); - static unique_ptr TransformColumnCompressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ColumnConstraintEntry TransformColumnCompression(PEGTransformer &transformer, const Identifier &col_id_or_string); - static unique_ptr TransformKeyActionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static KeyActions TransformKeyActions(PEGTransformer &transformer, const optional &update_action, - const optional &delete_action); - static unique_ptr TransformUpdateActionInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static KeyActions TransformUpdateFirstKeyActions(PEGTransformer &transformer, const string &update_action, + const optional &delete_action); + static KeyActions TransformDeleteFirstKeyActions(PEGTransformer &transformer, const string &delete_action, + const optional &update_action); static string TransformUpdateAction(PEGTransformer &transformer, const string &key_action); - static unique_ptr TransformDeleteActionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformDeleteAction(PEGTransformer &transformer, const string &key_action); - static unique_ptr TransformKeyActionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformNoKeyActionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformNoKeyAction(PEGTransformer &transformer); - static unique_ptr TransformRestrictKeyActionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformRestrictKeyAction(PEGTransformer &transformer); - static unique_ptr TransformCascadeKeyActionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformCascadeKeyAction(PEGTransformer &transformer); - static unique_ptr TransformSetNullKeyActionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformSetNullKeyAction(PEGTransformer &transformer); - static unique_ptr TransformSetDefaultKeyActionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformSetDefaultKeyAction(PEGTransformer &transformer); - static unique_ptr TransformTopLevelConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTopLevelConstraint(PEGTransformer &transformer, const bool &has_result, unique_ptr top_level_constraint_list); - static unique_ptr TransformTopLevelConstraintListInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformTopCheckConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTopCheckConstraint(PEGTransformer &transformer, ColumnConstraintEntry check_constraint); - static unique_ptr TransformTopPrimaryKeyConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTopPrimaryKeyConstraint(PEGTransformer &transformer, const vector &column_id_list, const optional &constraint_timing); - static unique_ptr TransformTopUniqueConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTopUniqueConstraint(PEGTransformer &transformer, const vector &column_id_list, const optional &constraint_timing); - static unique_ptr TransformTopForeignKeyConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTopForeignKeyConstraint(PEGTransformer &transformer, const vector &column_id_list, ColumnConstraintEntry foreign_key_constraint); - static unique_ptr TransformColumnIdListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformColumnIdList(PEGTransformer &transformer, const vector &col_id); - static unique_ptr TransformDottedIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformDottedIdentifier(PEGTransformer &transformer, const Identifier &identifier, const optional> &dot_col_label); - static unique_ptr TransformDotColLabelInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformDotColLabel(PEGTransformer &transformer, const string &col_label); - static unique_ptr TransformColIdInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformColIdOrStringInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformTypeFuncNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformTypeFuncKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformColLabelInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformColLabelOrStringInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformColLabelIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformColLabelIdentifier(PEGTransformer &transformer, const string &col_label); - static unique_ptr TransformStringLiteralIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformStringLiteralIdentifier(PEGTransformer &transformer, const string &string_literal); - static unique_ptr TransformGeneratedColumnInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static GeneratedColumnDefinition TransformGeneratedColumn(PEGTransformer &transformer, const bool &has_result, + static GeneratedColumnDefinition TransformGeneratedColumn(PEGTransformer &transformer, + const optional &generated_always, unique_ptr expression, const optional &generated_column_type); - static unique_ptr TransformGeneratedColumnTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCommitActionInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static bool TransformGeneratedAlways(PEGTransformer &transformer); + static bool TransformGeneratedByDefault(PEGTransformer &transformer); static bool TransformCommitAction(PEGTransformer &transformer, const bool &preserve_or_delete); - static unique_ptr TransformPreserveOrDeleteInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformPreserveRowsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformPreserveRows(PEGTransformer &transformer); - static unique_ptr TransformDeleteRowsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformDeleteRows(PEGTransformer &transformer); - static unique_ptr TransformVirtualGeneratedColumnInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformVirtualGeneratedColumn(PEGTransformer &transformer); - static unique_ptr TransformStoredGeneratedColumnInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformStoredGeneratedColumn(PEGTransformer &transformer); - static unique_ptr TransformCreateTriggerStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCreateTriggerStmt(PEGTransformer &transformer, const optional &if_not_exists, const Identifier &trigger_name, const TriggerTiming &trigger_timing, const TriggerEventInfo &trigger_event, unique_ptr base_table_name, const optional &referencing_clause, const optional &for_each_clause, unique_ptr trigger_body); - static unique_ptr TransformTriggerBodyInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformTriggerNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformTriggerName(PEGTransformer &transformer, const Identifier &identifier); - static unique_ptr TransformReferencingClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static TriggerTableReferencingInfo TransformReferencingClause(PEGTransformer &transformer, const TriggerTableReferencingInfo &referencing_item, const optional &referencing_item_1); - static unique_ptr TransformReferencingItemInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformReferencingNewTableAsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static TriggerTableReferencingInfo TransformReferencingNewTableAs(PEGTransformer &transformer, const Identifier &col_id); - static unique_ptr TransformReferencingOldTableAsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static TriggerTableReferencingInfo TransformReferencingOldTableAs(PEGTransformer &transformer, const Identifier &col_id); - static unique_ptr TransformTriggerTimingInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformTriggerBeforeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static TriggerTiming TransformTriggerBefore(PEGTransformer &transformer); - static unique_ptr TransformTriggerAfterInternal(PEGTransformer &transformer, - ParseResult &parse_result); static TriggerTiming TransformTriggerAfter(PEGTransformer &transformer); - static unique_ptr TransformTriggerInsteadOfInternal(PEGTransformer &transformer, - ParseResult &parse_result); static TriggerTiming TransformTriggerInsteadOf(PEGTransformer &transformer); - static unique_ptr TransformTriggerEventInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformTriggerEventInsertInternal(PEGTransformer &transformer, - ParseResult &parse_result); static TriggerEventInfo TransformTriggerEventInsert(PEGTransformer &transformer); - static unique_ptr TransformTriggerEventDeleteInternal(PEGTransformer &transformer, - ParseResult &parse_result); static TriggerEventInfo TransformTriggerEventDelete(PEGTransformer &transformer); - static unique_ptr TransformTriggerEventUpdateInternal(PEGTransformer &transformer, - ParseResult &parse_result); static TriggerEventInfo TransformTriggerEventUpdate(PEGTransformer &transformer); - static unique_ptr TransformTriggerEventUpdateOfInternal(PEGTransformer &transformer, - ParseResult &parse_result); static TriggerEventInfo TransformTriggerEventUpdateOf(PEGTransformer &transformer, const vector &trigger_column_list); - static unique_ptr TransformTriggerColumnListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformTriggerColumnList(PEGTransformer &transformer, const vector &col_id); - static unique_ptr TransformForEachClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformForEachRowInternal(PEGTransformer &transformer, - ParseResult &parse_result); static TriggerForEach TransformForEachRow(PEGTransformer &transformer); - static unique_ptr TransformForEachStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static TriggerForEach TransformForEachStatement(PEGTransformer &transformer); - static unique_ptr TransformCreateTypeStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCreateTypeStmt(PEGTransformer &transformer, const optional &if_not_exists, const QualifiedName &qualified_name, unique_ptr create_type); - static unique_ptr TransformCreateTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCreateTypeFromTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCreateTypeFromType(PEGTransformer &transformer, const LogicalType &type); - static unique_ptr TransformEnumSelectTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformEnumSelectType(PEGTransformer &transformer, unique_ptr select_statement_internal); - static unique_ptr TransformEnumStringLiteralListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformEnumStringLiteralList(PEGTransformer &transformer, - const optional> &string_literal); - static unique_ptr TransformCreateViewStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); + const optional> &string_literal, + const optional> &array_bounds); static unique_ptr TransformCreateViewStmt(PEGTransformer &transformer, const optional &create_secure, const optional &create_recursive, const optional &if_not_exists, const QualifiedName &qualified_name, const optional> &insert_column_list, optional>> with_list, unique_ptr select_statement_internal); - static unique_ptr TransformCreateRecursiveInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformCreateRecursive(PEGTransformer &transformer); - static unique_ptr TransformCreateSecureInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformCreateSecure(PEGTransformer &transformer); - static unique_ptr TransformDeallocateStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDeallocateStatement(PEGTransformer &transformer, const optional &deallocate_prepare, const Identifier &identifier); - static unique_ptr TransformDeallocatePrepareInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformDeallocatePrepare(PEGTransformer &transformer); - static unique_ptr TransformDeleteStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDeleteStatement(PEGTransformer &transformer, optional with_clause, unique_ptr target_opt_alias, optional>> delete_using_clause, optional> where_clause, optional>> returning_clause); - static unique_ptr TransformTruncateStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTruncateStatement(PEGTransformer &transformer, const bool &has_result, - unique_ptr base_table_name); - static unique_ptr TransformTargetOptAliasInternal(PEGTransformer &transformer, - ParseResult &parse_result); + unique_ptr base_table_name, + optional truncate_options); + static bool TransformTruncateAliasOptions(PEGTransformer &transformer, const Identifier &target_alias, + optional truncate_drop_behavior); + static bool TransformTruncateDropBehavior(PEGTransformer &transformer, const bool &drop_behavior); static unique_ptr TransformTargetOptAlias(PEGTransformer &transformer, unique_ptr base_table_name, - const bool &has_result, const optional &col_id); - static unique_ptr TransformDeleteUsingClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); + const optional &target_alias); + static Identifier TransformTargetAlias(PEGTransformer &transformer, const bool &has_result, + const Identifier &col_id); static vector> TransformDeleteUsingClause(PEGTransformer &transformer, vector> table_ref); - static unique_ptr TransformDescribeStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDescribeStatement(PEGTransformer &transformer, unique_ptr child); - static unique_ptr TransformShowDeprecatedSelectInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformShowDeprecatedSelect(PEGTransformer &transformer, const ShowType &show_rule, unique_ptr select_statement_internal); - static unique_ptr TransformDescribeSelectInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDescribeSelect(PEGTransformer &transformer, const ShowType &describe_or_summarize, unique_ptr select_statement_internal); - static unique_ptr TransformShowAllTablesInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformShowAllTables(PEGTransformer &transformer, const ShowType &show_or_describe, const bool &has_result); - static unique_ptr TransformShowTablesInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformShowTables(PEGTransformer &transformer, const ShowType &show_or_describe, const QualifiedName &qualified_name); - static unique_ptr TransformShowByNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformShowByName(PEGTransformer &transformer, const ShowType &show_rule, optional show_target); - static unique_ptr TransformDescribeByNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDescribeByName(PEGTransformer &transformer, const ShowType &describe_or_summarize, optional describe_target); - static unique_ptr TransformDescribeOrSummarizeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformShowTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr - TransformShowDeprecatedQualifiedTableNameInternal(PEGTransformer &transformer, ParseResult &parse_result); static DescribeTarget TransformShowDeprecatedQualifiedTableName(PEGTransformer &transformer, unique_ptr qualified_table_name); - static unique_ptr TransformShowSettingNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DescribeTarget TransformShowSettingName(PEGTransformer &transformer, const Identifier &setting_name); - static unique_ptr TransformDescribeTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformDescribeBaseTableNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DescribeTarget TransformDescribeBaseTableName(PEGTransformer &transformer, unique_ptr base_table_name); - static unique_ptr TransformDescribeStringLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DescribeTarget TransformDescribeStringLiteral(PEGTransformer &transformer, const string &string_literal); - static unique_ptr TransformSummarizeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ShowType TransformSummarize(PEGTransformer &transformer, const ShowType &summarize_rule); - static unique_ptr TransformSummarizeRuleInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ShowType TransformSummarizeRule(PEGTransformer &transformer); - static unique_ptr TransformShowOrDescribeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformShowRuleInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ShowType TransformShowRule(PEGTransformer &transformer); - static unique_ptr TransformDescribeRuleInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformDescribeLongRuleInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ShowType TransformDescribeLongRule(PEGTransformer &transformer); - static unique_ptr TransformDescRuleInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ShowType TransformDescRule(PEGTransformer &transformer); - static unique_ptr TransformDetachStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDetachStatement(PEGTransformer &transformer, const bool &has_result, const optional &if_exists, const Identifier &catalog_name); - static unique_ptr TransformDropStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDropStatement(PEGTransformer &transformer, unique_ptr drop_entries, const optional &drop_behavior); - static unique_ptr TransformDropEntriesInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformDropTriggerInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDropTrigger(PEGTransformer &transformer, const optional &if_exists, const Identifier &trigger_name, unique_ptr base_table_name); - static unique_ptr TransformDropTableInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDropTable(PEGTransformer &transformer, const CatalogType &table_or_view, const optional &if_exists, vector> base_table_name); - static unique_ptr TransformDropTableFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDropTableFunction(PEGTransformer &transformer, const CatalogType &comment_macro_table, const optional &if_exists, const vector &table_function_name); - static unique_ptr TransformDropFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDropFunction(PEGTransformer &transformer, const bool &function_type_macro, const optional &if_exists, const vector &function_identifier); - static unique_ptr TransformDropSchemaInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDropSchema(PEGTransformer &transformer, const optional &if_exists, const vector &qualified_name); - static unique_ptr TransformDropIndexInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDropIndex(PEGTransformer &transformer, const optional &if_exists, const vector &qualified_index_name); - static unique_ptr TransformQualifiedIndexNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformQualifiedIndexNameStringInternal(PEGTransformer &transformer, - ParseResult &parse_result); static QualifiedName TransformQualifiedIndexNameString(PEGTransformer &transformer, const Identifier &index_name); - static unique_ptr TransformSchemaReservedIndexInternal(PEGTransformer &transformer, - ParseResult &parse_result); static QualifiedName TransformSchemaReservedIndex(PEGTransformer &transformer, const Identifier &schema_qualification, const Identifier &reserved_index_name); - static unique_ptr TransformCatalogReservedSchemaIndexInternal(PEGTransformer &transformer, - ParseResult &parse_result); static QualifiedName TransformCatalogReservedSchemaIndex(PEGTransformer &transformer, const Identifier &catalog_qualification, const Identifier &reserved_schema_qualification, const Identifier &reserved_index_name); - static unique_ptr TransformDropSequenceInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDropSequence(PEGTransformer &transformer, const optional &if_exists, const vector &qualified_sequence_name); - static unique_ptr TransformDropCollationInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDropCollation(PEGTransformer &transformer, const optional &if_exists, const vector &collation_name); - static unique_ptr TransformDropTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDropType(PEGTransformer &transformer, const optional &if_exists, const vector &qualified_type_name); - static unique_ptr TransformDropSecretInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDropSecret(PEGTransformer &transformer, const optional &temporary, const optional &if_exists, const Identifier &secret_name, const optional &drop_secret_storage); - static unique_ptr TransformTableOrViewInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformMaterializedViewEntryInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CatalogType TransformMaterializedViewEntry(PEGTransformer &transformer); - static unique_ptr TransformFunctionTypeMacroInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformFunctionTypeMacroKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformFunctionTypeMacroKeyword(PEGTransformer &transformer); - static unique_ptr TransformFunctionTypeFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformFunctionTypeFunction(PEGTransformer &transformer); - static unique_ptr TransformDropBehaviorInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCascadeDropBehaviorInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformCascadeDropBehavior(PEGTransformer &transformer); - static unique_ptr TransformRestrictDropBehaviorInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformRestrictDropBehavior(PEGTransformer &transformer); - static unique_ptr TransformIfExistsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformIfExists(PEGTransformer &transformer); - static unique_ptr TransformDropSecretStorageInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformDropSecretStorage(PEGTransformer &transformer, const Identifier &identifier); - static unique_ptr TransformExecuteStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformExecuteStatement(PEGTransformer &transformer, const Identifier &identifier, optional> table_function_arguments); - static unique_ptr TransformExplainStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformExplainStatement(PEGTransformer &transformer, const optional &analyze_keyword, const optional> &explain_option_list, unique_ptr explainable_statements); - static unique_ptr TransformExplainOptionListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformExplainOptionList(PEGTransformer &transformer, const vector &explain_option); - static unique_ptr TransformExplainOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GenericCopyOption TransformExplainOption(PEGTransformer &transformer, const Identifier &explain_option_name, optional> expression); - static unique_ptr TransformExplainOptionNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformExplainOptionName(PEGTransformer &transformer, ParseResult &choice_result); - static unique_ptr TransformExplainSelectStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformExplainSelectStatement(PEGTransformer &transformer, unique_ptr select_statement_internal); - static unique_ptr TransformExplainableStatementsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformExportStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformExportStatement(PEGTransformer &transformer, const optional &export_source, const string &string_literal, const optional> &generic_copy_option_list); - static unique_ptr TransformExportSourceInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformExportSource(PEGTransformer &transformer, const Identifier &catalog_name); - static unique_ptr TransformImportStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformImportStatement(PEGTransformer &transformer, const string &string_literal); - static unique_ptr TransformColumnReferenceInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformColumnReference(PEGTransformer &transformer, unique_ptr child); - static unique_ptr TransformNestedSchemaTableColumnNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformNestedSchemaTableColumnName( PEGTransformer &transformer, const Identifier &catalog_qualification, const Identifier &reserved_schema_qualification, const Identifier &reserved_schema_qualification_1, const vector &reserved_schema_qualification_2, const Identifier &reserved_column_name); - static unique_ptr - TransformCatalogReservedSchemaTableColumnNameInternal(PEGTransformer &transformer, ParseResult &parse_result); static unique_ptr TransformCatalogReservedSchemaTableColumnName(PEGTransformer &transformer, const Identifier &catalog_qualification, const Identifier &reserved_schema_qualification, const Identifier &reserved_table_qualification, const Identifier &reserved_column_name); - static unique_ptr TransformSchemaReservedTableColumnNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSchemaReservedTableColumnName(PEGTransformer &transformer, const Identifier &schema_qualification, const Identifier &reserved_table_qualification, const Identifier &reserved_column_name); - static unique_ptr TransformTableReservedColumnNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTableReservedColumnName(PEGTransformer &transformer, const Identifier &table_qualification, const Identifier &reserved_column_name); - static unique_ptr TransformFunctionExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformFunctionExpression(PEGTransformer &transformer, const QualifiedName &function_identifier, MethodArguments function_expression_arguments, optional> within_group_clause, optional> filter_clause, const bool &has_result, optional> over_clause); - static unique_ptr TransformFunctionExpressionArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MethodArguments TransformFunctionExpressionArguments(PEGTransformer &transformer, MethodArguments function_expression_argument_list); - static unique_ptr TransformFunctionExpressionArgumentListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MethodArguments TransformFunctionExpressionArgumentList(PEGTransformer &transformer, const optional &distinct_or_all, optional> function_argument_list, optional> order_by_clause, const optional &ignore_or_respect_nulls); - static unique_ptr TransformFunctionArgumentListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformFunctionArgumentList(PEGTransformer &transformer, vector function_argument); - static unique_ptr TransformFunctionIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformFunctionNameAsQualifiedNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static QualifiedName TransformFunctionNameAsQualifiedName(PEGTransformer &transformer, const Identifier &function_name); - static unique_ptr - TransformCatalogReservedSchemaFunctionNameInternal(PEGTransformer &transformer, ParseResult &parse_result); static QualifiedName TransformCatalogReservedSchemaFunctionName(PEGTransformer &transformer, const Identifier &catalog_qualification, const optional> &reserved_schema_qualification, const Identifier &reserved_function_name); - static unique_ptr TransformSchemaReservedFunctionNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static QualifiedName TransformSchemaReservedFunctionName(PEGTransformer &transformer, const Identifier &schema_qualification, const Identifier &reserved_function_name); - static unique_ptr TransformDistinctOrAllInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformDistinctKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformDistinctKeyword(PEGTransformer &transformer); - static unique_ptr TransformAllKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformAllKeyword(PEGTransformer &transformer); - static unique_ptr TransformWithinGroupClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformWithinGroupClause(PEGTransformer &transformer, vector order_by_clause); - static unique_ptr TransformFilterClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformFilterClause(PEGTransformer &transformer, unique_ptr filter_clause_expression); - static unique_ptr TransformFilterClauseExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformFilterClauseExpression(PEGTransformer &transformer, unique_ptr filter_clause_contents); - static unique_ptr TransformFilterClauseContentsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformFilterClauseContents(PEGTransformer &transformer, const bool &has_result, unique_ptr expression); - static unique_ptr TransformIgnoreOrRespectNullsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformIgnoreNullsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformIgnoreNulls(PEGTransformer &transformer); - static unique_ptr TransformRespectNullsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformRespectNulls(PEGTransformer &transformer); - static unique_ptr TransformParenthesisExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformParenthesisExpression(PEGTransformer &transformer, optional>> expression); - static unique_ptr TransformLiteralExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformLiteralExpression(PEGTransformer &transformer, ParseResult &choice_result); - static unique_ptr TransformConstantLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformConstantLiteral(PEGTransformer &transformer, const Value &child); - static unique_ptr TransformNullLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Value TransformNullLiteral(PEGTransformer &transformer); - static unique_ptr TransformTrueLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Value TransformTrueLiteral(PEGTransformer &transformer); - static unique_ptr TransformFalseLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Value TransformFalseLiteral(PEGTransformer &transformer); - static unique_ptr TransformCastExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCastExpression(PEGTransformer &transformer, const bool &cast_or_try_cast, CastArguments cast_arguments); - static unique_ptr TransformCastArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CastArguments TransformCastArguments(PEGTransformer &transformer, unique_ptr expression, const LogicalType &type); - static unique_ptr TransformCastOrTryCastInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCastKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformCastKeyword(PEGTransformer &transformer); - static unique_ptr TransformTryCastKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformTryCastKeyword(PEGTransformer &transformer); - static unique_ptr TransformColIdDotInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformColIdDot(PEGTransformer &transformer, const Identifier &col_id); - static unique_ptr TransformStarExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformStarExpression(PEGTransformer &transformer, const optional> &star_qualifier_list, const optional &exclude_list, optional>> replace_list, const optional> &rename_list); - static unique_ptr TransformStarQualifierListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformStarQualifierList(PEGTransformer &transformer, const vector &col_id_dot); - static unique_ptr TransformExcludeListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static qualified_column_set_t TransformExcludeList(PEGTransformer &transformer, const qualified_column_set_t &exclude_names); - static unique_ptr TransformExcludeNamesInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformExcludeNameListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static qualified_column_set_t TransformExcludeNameList(PEGTransformer &transformer, const vector &exclude_name); - static unique_ptr TransformExcludeNameSingleInternal(PEGTransformer &transformer, - ParseResult &parse_result); static qualified_column_set_t TransformExcludeNameSingle(PEGTransformer &transformer, const QualifiedColumnName &exclude_name); - static unique_ptr TransformExcludeNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformExcludeDottedNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static QualifiedColumnName TransformExcludeDottedName(PEGTransformer &transformer, const vector &dotted_identifier); - static unique_ptr TransformExcludeColumnNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static QualifiedColumnName TransformExcludeColumnName(PEGTransformer &transformer, const Identifier &col_id_or_string); - static unique_ptr TransformReplaceListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static case_insensitive_map_t> TransformReplaceList(PEGTransformer &transformer, case_insensitive_map_t> replace_entries); - static unique_ptr TransformReplaceEntriesInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformReplaceEntrySingleInternal(PEGTransformer &transformer, - ParseResult &parse_result); static case_insensitive_map_t> TransformReplaceEntrySingle(PEGTransformer &transformer, pair> replace_entry); - static unique_ptr TransformReplaceEntryListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static case_insensitive_map_t> TransformReplaceEntryList(PEGTransformer &transformer, vector>> replace_entry); - static unique_ptr TransformReplaceEntryInternal(PEGTransformer &transformer, - ParseResult &parse_result); static pair> TransformReplaceEntry(PEGTransformer &transformer, unique_ptr expression, unique_ptr column_reference); - static unique_ptr TransformRenameListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static qualified_column_map_t TransformRenameList(PEGTransformer &transformer, const qualified_column_map_t &rename_entries); - static unique_ptr TransformRenameEntriesInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformRenameEntryListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static qualified_column_map_t TransformRenameEntryList(PEGTransformer &transformer, const vector> &rename_entry); - static unique_ptr TransformSingleRenameEntryInternal(PEGTransformer &transformer, - ParseResult &parse_result); static qualified_column_map_t TransformSingleRenameEntry(PEGTransformer &transformer, const pair &rename_entry); - static unique_ptr TransformRenameEntryInternal(PEGTransformer &transformer, - ParseResult &parse_result); static pair TransformRenameEntry(PEGTransformer &transformer, const QualifiedColumnName &exclude_name, const Identifier &identifier); - static unique_ptr TransformSubqueryExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSubqueryExpression(PEGTransformer &transformer, const optional &subquery_not, const optional &subquery_exists, unique_ptr subquery_reference); - static unique_ptr TransformSubqueryNotInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformSubqueryNot(PEGTransformer &transformer); - static unique_ptr TransformSubqueryExistsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformSubqueryExists(PEGTransformer &transformer); - static unique_ptr TransformCaseExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCaseExpression(PEGTransformer &transformer, optional> expression, vector case_when_then, optional> case_else); - static unique_ptr TransformCaseWhenThenInternal(PEGTransformer &transformer, - ParseResult &parse_result); static CaseCheck TransformCaseWhenThen(PEGTransformer &transformer, unique_ptr expression, unique_ptr expression_1); - static unique_ptr TransformCaseElseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCaseElse(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformTypeLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTypeLiteral(PEGTransformer &transformer, const LogicalType &type, const string &string_literal); - static unique_ptr TransformIntervalLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformIntervalLiteral(PEGTransformer &transformer, unique_ptr interval_parameter, const optional &interval); - static unique_ptr TransformIntervalParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformIntervalStringParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformIntervalStringParameter(PEGTransformer &transformer, const string &string_literal); - static unique_ptr TransformFrameClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static WindowFrame TransformFrameClause(PEGTransformer &transformer, const string &framing, vector frame_extent, const optional &window_exclude_clause); - static unique_ptr TransformFramingInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformRowsFramingInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformRowsFraming(PEGTransformer &transformer); - static unique_ptr TransformRangeFramingInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformRangeFraming(PEGTransformer &transformer); - static unique_ptr TransformGroupsFramingInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformGroupsFraming(PEGTransformer &transformer); - static unique_ptr TransformFrameExtentInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSingleFrameExtentInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformSingleFrameExtent(PEGTransformer &transformer, WindowBoundaryExpression frame_bound); - static unique_ptr TransformBetweenFrameExtentInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformBetweenFrameExtent(PEGTransformer &transformer, WindowBoundaryExpression frame_bound, WindowBoundaryExpression frame_bound_1); - static unique_ptr TransformFrameBoundInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformFrameUnboundedInternal(PEGTransformer &transformer, - ParseResult &parse_result); static WindowBoundaryExpression TransformFrameUnbounded(PEGTransformer &transformer, const bool &preceding_or_following); - static unique_ptr TransformFrameExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static WindowBoundaryExpression TransformFrameExpression(PEGTransformer &transformer, unique_ptr expression, const bool &preceding_or_following); - static unique_ptr TransformFrameCurrentRowInternal(PEGTransformer &transformer, - ParseResult &parse_result); static WindowBoundaryExpression TransformFrameCurrentRow(PEGTransformer &transformer); - static unique_ptr TransformPrecedingOrFollowingInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformPrecedingFrameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformPrecedingFrame(PEGTransformer &transformer); - static unique_ptr TransformFollowingFrameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformFollowingFrame(PEGTransformer &transformer); - static unique_ptr TransformWindowExcludeClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static WindowExcludeMode TransformWindowExcludeClause(PEGTransformer &transformer, const WindowExcludeMode &window_exclude_element); - static unique_ptr TransformWindowExcludeElementInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformExcludeCurrentRowInternal(PEGTransformer &transformer, - ParseResult &parse_result); static WindowExcludeMode TransformExcludeCurrentRow(PEGTransformer &transformer); - static unique_ptr TransformExcludeGroupInternal(PEGTransformer &transformer, - ParseResult &parse_result); static WindowExcludeMode TransformExcludeGroup(PEGTransformer &transformer); - static unique_ptr TransformExcludeTiesInternal(PEGTransformer &transformer, - ParseResult &parse_result); static WindowExcludeMode TransformExcludeTies(PEGTransformer &transformer); - static unique_ptr TransformExcludeNoOthersInternal(PEGTransformer &transformer, - ParseResult &parse_result); static WindowExcludeMode TransformExcludeNoOthers(PEGTransformer &transformer); - static unique_ptr TransformWindowFrameInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformIdentifierWindowFrameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformIdentifierWindowFrame(PEGTransformer &transformer, const Identifier &identifier); - static unique_ptr TransformParensIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformParensIdentifier(PEGTransformer &transformer, const Identifier &identifier); - static unique_ptr TransformWindowFrameDefinitionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformWindowFrameNameContentsParensInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformWindowFrameNameContentsParens(PEGTransformer &transformer, unique_ptr window_frame_name_contents); - static unique_ptr TransformWindowFrameNameContentsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformWindowFrameNameContents(PEGTransformer &transformer, const optional &base_window_name, unique_ptr window_frame_contents); - static unique_ptr TransformWindowFrameContentsParensInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformWindowFrameContentsParens(PEGTransformer &transformer, unique_ptr window_frame_contents); - static unique_ptr TransformWindowFrameContentsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformWindowFrameContents(PEGTransformer &transformer, optional>> window_partition, optional> order_by_clause, optional frame_clause); - static unique_ptr TransformBaseWindowNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformBaseWindowName(PEGTransformer &transformer, const Identifier &identifier); - static unique_ptr TransformWindowPartitionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformWindowPartition(PEGTransformer &transformer, vector> expression); - static unique_ptr TransformListExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformArrayBoundedListExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformArrayBoundedListExpression(PEGTransformer &transformer, const bool &has_result, vector> bounded_list_expression); - static unique_ptr TransformArrayParensSelectInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformArrayParensSelect(PEGTransformer &transformer, unique_ptr select_statement_internal); - static unique_ptr TransformBoundedListExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformBoundedListExpression(PEGTransformer &transformer, optional>> expression); - static unique_ptr TransformStructExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformStructExpression(PEGTransformer &transformer, optional> struct_field); - static unique_ptr TransformStructFieldInternal(PEGTransformer &transformer, - ParseResult &parse_result); static FunctionArgument TransformStructField(PEGTransformer &transformer, const Identifier &col_id_or_string, unique_ptr expression); - static unique_ptr TransformMapExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformMapExpression(PEGTransformer &transformer, vector> map_struct_expression); - static unique_ptr TransformMapStructExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformMapStructExpression(PEGTransformer &transformer, optional>>> map_struct_field); - static unique_ptr TransformMapStructFieldInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformMapStructField(PEGTransformer &transformer, unique_ptr expression, unique_ptr expression_1); - static unique_ptr TransformGroupingExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformGroupingExpression(PEGTransformer &transformer, const bool &grouping_or_grouping_id, optional>> expression); - static unique_ptr TransformGroupingOrGroupingIdInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformGroupingKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformGroupingKeyword(PEGTransformer &transformer); - static unique_ptr TransformGroupingIdKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformGroupingIdKeyword(PEGTransformer &transformer); - static unique_ptr TransformParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformQuestionMarkNumberedParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformQuestionMarkNumberedParameter(PEGTransformer &transformer, unique_ptr number_literal); - static unique_ptr TransformAnonymousParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformAnonymousParameter(PEGTransformer &transformer); - static unique_ptr TransformNumberedParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformNumberedParameter(PEGTransformer &transformer, unique_ptr number_literal); - static unique_ptr TransformColLabelParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformColLabelParameter(PEGTransformer &transformer, const string &col_label); - static unique_ptr TransformPositionalExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformPositionalExpression(PEGTransformer &transformer, unique_ptr number_literal); - static unique_ptr TransformDefaultExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDefaultExpression(PEGTransformer &transformer); - static unique_ptr TransformListComprehensionExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformListComprehensionExpression(PEGTransformer &transformer, unique_ptr expression, const vector &col_id_or_string, unique_ptr expression_1, optional> list_comprehension_filter); - static unique_ptr TransformListComprehensionFilterInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformListComprehensionFilter(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformParensExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformParensExpression(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformMeasureSemanticsExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformMeasureSemanticsExpression(PEGTransformer &transformer, const bool &measure_semantics, unique_ptr function_expression); - static unique_ptr TransformSingleExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformExpression(PEGTransformer &transformer, - unique_ptr lambda_arrow_expression); - static unique_ptr TransformColumnDefaultExprInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformColumnDefaultExpr(PEGTransformer &transformer, unique_ptr col_def_or_expr); - static unique_ptr TransformLambdaArrowExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformLambdaArrowExpression(PEGTransformer &transformer, unique_ptr logical_or_expression, optional>> single_arrow_pair); - static unique_ptr TransformSingleArrowPairInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSingleArrowPair(PEGTransformer &transformer, unique_ptr logical_or_expression); - static unique_ptr TransformLogicalOrExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformLogicalOrExpression(PEGTransformer &transformer, unique_ptr logical_and_expression, optional>> logical_or_expression_tail); - static unique_ptr TransformLogicalOrExpressionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformLogicalOrExpressionTail(PEGTransformer &transformer, unique_ptr logical_and_expression); - static unique_ptr TransformColDefOrExprInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformColDefOrExpr(PEGTransformer &transformer, unique_ptr col_def_and_expr, optional>> col_def_or_expression_tail); - static unique_ptr TransformColDefOrExpressionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformColDefOrExpressionTail(PEGTransformer &transformer, unique_ptr col_def_and_expr); - static unique_ptr TransformLogicalAndExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformLogicalAndExpression(PEGTransformer &transformer, unique_ptr logical_not_expression, optional>> logical_and_expression_tail); - static unique_ptr TransformLogicalAndExpressionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformLogicalAndExpressionTail(PEGTransformer &transformer, unique_ptr logical_not_expression); - static unique_ptr TransformColDefAndExprInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformColDefAndExpr(PEGTransformer &transformer, unique_ptr is_distinct_from_expression, optional>> col_def_and_expression_tail); - static unique_ptr TransformColDefAndExpressionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformColDefAndExpressionTail(PEGTransformer &transformer, unique_ptr is_distinct_from_expression); - static unique_ptr TransformLogicalNotExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformLogicalNotExpression(PEGTransformer &transformer, optional> not_expression, unique_ptr is_expression); - static unique_ptr TransformNotExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformNotExpression(PEGTransformer &transformer, const vector ¬_keyword); - static unique_ptr TransformNotKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformNotKeyword(PEGTransformer &transformer); - static unique_ptr TransformIsExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformIsExpression(PEGTransformer &transformer, - unique_ptr is_distinct_from_expression, - optional>> is_test); - static unique_ptr TransformIsTestInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformIsLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static unique_ptr + TransformIsExpression(PEGTransformer &transformer, unique_ptr is_distinct_from_expression, + optional> is_expression_continuation); + static vector + TransformIsExpressionContinuation(PEGTransformer &transformer, unique_ptr is_test, + optional> is_expression_tail); + static IsExpressionTail TransformIsTestTail(PEGTransformer &transformer, unique_ptr is_test); + static IsExpressionTail TransformIsDistinctTail(PEGTransformer &transformer, + IsDistinctFromTail is_distinct_from_tail); + static IsExpressionTail TransformIsComparisonTail(PEGTransformer &transformer, + ComparisonExpressionTail comparison_expression_tail); + static IsExpressionTail TransformIsOtherOperatorTail(PEGTransformer &transformer, + OtherOperatorTail other_operator_tail); static unique_ptr TransformIsLiteral(PEGTransformer &transformer, const bool &has_result, const Value &is_literal_value); - static unique_ptr TransformIsLiteralValueInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformUnknownLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Value TransformUnknownLiteral(PEGTransformer &transformer); - static unique_ptr TransformNotNullInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformNotNullKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformNotNullKeyword(PEGTransformer &transformer); - static unique_ptr TransformNotNullOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformNotNullOperator(PEGTransformer &transformer); - static unique_ptr TransformIsNullInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformIsNull(PEGTransformer &transformer, unique_ptr is_null_operator); - static unique_ptr TransformIsNullOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformIsNullOperator(PEGTransformer &transformer); - static unique_ptr TransformIsDistinctFromExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformIsDistinctFromExpression(PEGTransformer &transformer, unique_ptr comparison_expression, optional> is_distinct_from_tail); - static unique_ptr TransformIsDistinctFromTailInternal(PEGTransformer &transformer, - ParseResult &parse_result); static IsDistinctFromTail TransformIsDistinctFromTail(PEGTransformer &transformer, const ExpressionType &is_distinct_from_op, unique_ptr comparison_expression); - static unique_ptr TransformIsDistinctFromOpInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ExpressionType TransformIsDistinctFromOp(PEGTransformer &transformer, const bool &has_result); - static unique_ptr TransformComparisonExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformComparisonExpression(PEGTransformer &transformer, unique_ptr between_in_like_expression, optional> comparison_expression_tail); - static unique_ptr TransformComparisonExpressionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ComparisonExpressionTail TransformComparisonExpressionTail(PEGTransformer &transformer, const ExpressionType &comparison_operator, optional> not_expression, unique_ptr between_in_like_expression); - static unique_ptr TransformComparisonOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformOperatorEqualInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ExpressionType TransformOperatorEqual(PEGTransformer &transformer); - static unique_ptr TransformOperatorNotEqualInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ExpressionType TransformOperatorNotEqual(PEGTransformer &transformer); - static unique_ptr TransformOperatorLessThanInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ExpressionType TransformOperatorLessThan(PEGTransformer &transformer); - static unique_ptr TransformOperatorGreaterThanInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ExpressionType TransformOperatorGreaterThan(PEGTransformer &transformer); - static unique_ptr TransformOperatorLessThanEqualsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ExpressionType TransformOperatorLessThanEquals(PEGTransformer &transformer); - static unique_ptr TransformOperatorGreaterThanEqualsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ExpressionType TransformOperatorGreaterThanEquals(PEGTransformer &transformer); - static unique_ptr TransformBetweenInLikeExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr - TransformBetweenInLikeExpression(PEGTransformer &transformer, - unique_ptr other_operator_expression, - optional between_in_like_op); - static unique_ptr TransformBetweenInLikeOpInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static BetweenInLikeOperator TransformBetweenInLikeOp(PEGTransformer &transformer, const bool &has_result, - unique_ptr between_in_like_op_expression); - static unique_ptr TransformBetweenInLikeOpExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformLikeClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static unique_ptr TransformBetweenInLikeExpression( + PEGTransformer &transformer, unique_ptr other_operator_expression, + optional> in_predicate, optional between_like_op); + static BetweenInLikeOperator TransformInPredicate(PEGTransformer &transformer, const bool &has_result, + unique_ptr in_clause); + static BetweenInLikeOperator TransformBetweenLikeOp(PEGTransformer &transformer, const bool &has_result, + unique_ptr between_like_op_expression); static unique_ptr TransformLikeClause(PEGTransformer &transformer, const string &like_variations, unique_ptr other_operator_expression, optional> escape_clause); - static unique_ptr TransformEscapeClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformEscapeClause(PEGTransformer &transformer, - unique_ptr comparison_expression); - static unique_ptr TransformLikeVariationsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformLikeTokenInternal(PEGTransformer &transformer, - ParseResult &parse_result); + unique_ptr other_operator_expression); static string TransformLikeToken(PEGTransformer &transformer); - static unique_ptr TransformILikeTokenInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformILikeToken(PEGTransformer &transformer); - static unique_ptr TransformGlobTokenInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformGlobToken(PEGTransformer &transformer); - static unique_ptr TransformSimilarToTokenInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformSimilarToToken(PEGTransformer &transformer); - static unique_ptr TransformRegexMatchTokenInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformRegexMatchToken(PEGTransformer &transformer); - static unique_ptr TransformRegexInsensitiveMatchTokenInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformRegexInsensitiveMatchToken(PEGTransformer &transformer); - static unique_ptr TransformNotILikeOpInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformNotILikeOp(PEGTransformer &transformer); - static unique_ptr TransformNotLikeOpInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformNotLikeOp(PEGTransformer &transformer); - static unique_ptr TransformNotRegexInsensitiveMatchOpInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformNotRegexInsensitiveMatchOp(PEGTransformer &transformer); - static unique_ptr TransformNotSimilarToOpInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformNotSimilarToOp(PEGTransformer &transformer); - static unique_ptr TransformInClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformInClause(PEGTransformer &transformer, unique_ptr in_expression); - static unique_ptr TransformInExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformInContainsExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformInContainsExpression(PEGTransformer &transformer, unique_ptr other_operator_expression); - static unique_ptr TransformInExpressionListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformInExpressionList(PEGTransformer &transformer, vector> expression); - static unique_ptr TransformInSelectStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformInSelectStatement(PEGTransformer &transformer, unique_ptr select_statement_internal); - static unique_ptr TransformBetweenClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr - TransformBetweenClause(PEGTransformer &transformer, unique_ptr other_operator_expression, + TransformBetweenClause(PEGTransformer &transformer, const bool &, + unique_ptr other_operator_expression, unique_ptr other_operator_expression_1); - static unique_ptr TransformOtherOperatorExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr - TransformOtherOperatorExpression(PEGTransformer &transformer, unique_ptr bitwise_expression, - optional> other_operator_tail); - static unique_ptr TransformOtherOperatorTailInternal(PEGTransformer &transformer, - ParseResult &parse_result); + TransformInfixOtherOperatorExpression(PEGTransformer &transformer, unique_ptr bitwise_expression, + optional> other_operator_tail); + static unique_ptr + TransformCustomPrefixExpression(PEGTransformer &transformer, const string &operator_literal, + unique_ptr other_operator_expression); static OtherOperatorTail TransformOtherOperatorTail(PEGTransformer &transformer, ParsedOperator other_operator, unique_ptr bitwise_expression); - static unique_ptr TransformOtherOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformAnyAllParsedOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ParsedOperator TransformAnyAllParsedOperator(PEGTransformer &transformer, const pair &any_all_operator); - static unique_ptr TransformNamedOtherOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ParsedOperator TransformNamedOtherOperator(PEGTransformer &transformer, const string &child); - static unique_ptr TransformAnyAllOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static pair TransformAnyAllOperator(PEGTransformer &transformer, const string &any_op, + static pair TransformAnyAllOperator(PEGTransformer &transformer, const string &any_operator_literal, const bool &any_or_all); - static unique_ptr TransformAnyOrAllInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSubqueryAnyInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformSubqueryAny(PEGTransformer &transformer); - static unique_ptr TransformSubqueryAllInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformSubqueryAll(PEGTransformer &transformer); - static unique_ptr TransformInetOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformJsonOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformListOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformStringOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformQualifiedOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformQualifiedOperator(PEGTransformer &transformer, const string &qualified_operator_contents); - static unique_ptr TransformQualifiedOperatorContentsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformQualifiedOperatorContents(PEGTransformer &transformer, - const optional> &col_id_dot, const string &any_op); - static unique_ptr TransformAnyOpInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformBitwiseExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); + const optional> &col_id_dot, + const string &any_operator_literal); static unique_ptr - TransformBitwiseExpression(PEGTransformer &transformer, unique_ptr additive_expression, + TransformBitwiseExpression(PEGTransformer &transformer, unique_ptr tilde_expression, optional> bitwise_expression_tail); - static unique_ptr TransformBitwiseExpressionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result); static BinaryExpressionTail TransformBitwiseExpressionTail(PEGTransformer &transformer, const string &bit_operator, - unique_ptr additive_expression); - static unique_ptr TransformBitOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformAdditiveExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); + unique_ptr tilde_expression); + static unique_ptr TransformTildeExpression(PEGTransformer &transformer, + optional> tilde_prefix_operator, + unique_ptr additive_expression); static unique_ptr TransformAdditiveExpression(PEGTransformer &transformer, unique_ptr multiplicative_expression, optional> additive_expression_tail); - static unique_ptr TransformAdditiveExpressionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result); static BinaryExpressionTail TransformAdditiveExpressionTail(PEGTransformer &transformer, const string &term, unique_ptr multiplicative_expression, optional_idx query_location); - static unique_ptr TransformTermInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformMultiplicativeExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformMultiplicativeExpression(PEGTransformer &transformer, unique_ptr exponentiation_expression, optional> multiplicative_expression_tail); - static unique_ptr TransformMultiplicativeExpressionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result); static BinaryExpressionTail TransformMultiplicativeExpressionTail(PEGTransformer &transformer, const string &factor, unique_ptr exponentiation_expression); - static unique_ptr TransformFactorInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformExponentiationExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformExponentiationExpression(PEGTransformer &transformer, unique_ptr collate_expression, optional> exponentiation_expression_tail); - static unique_ptr TransformExponentiationExpressionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result); static BinaryExpressionTail TransformExponentiationExpressionTail(PEGTransformer &transformer, const string &exponent_operator, unique_ptr collate_expression); - static unique_ptr TransformExponentOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCollateExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCollateExpression(PEGTransformer &transformer, unique_ptr at_time_zone_expression, optional>> collate_expression_tail); - static unique_ptr TransformCollateExpressionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCollateExpressionTail(PEGTransformer &transformer, unique_ptr at_time_zone_expression); - static unique_ptr TransformAtTimeZoneExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformAtTimeZoneExpression(PEGTransformer &transformer, unique_ptr prefix_expression, optional>> at_time_zone_expression_tail); - static unique_ptr TransformAtTimeZoneExpressionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformAtTimeZoneExpressionTail(PEGTransformer &transformer, unique_ptr prefix_expression); - static unique_ptr TransformPrefixOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformMinusPrefixOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformPlusPrefixOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformTildePrefixOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformBaseExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformBaseExpression(PEGTransformer &transformer, unique_ptr single_expression, optional>> indirection_list); - static unique_ptr TransformIndirectionListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformIndirectionList(PEGTransformer &transformer, vector> indirection); - static unique_ptr TransformIndirectionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCastOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCastOperator(PEGTransformer &transformer, const LogicalType &type); - static unique_ptr TransformDotOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformDotMethodOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDotMethodOperator(PEGTransformer &transformer, unique_ptr method_expression); - static unique_ptr TransformDotColumnOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDotColumnOperator(PEGTransformer &transformer, const string &col_label); - static unique_ptr TransformMethodExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformMethodExpression(PEGTransformer &transformer, const string &col_label, MethodArguments method_expression_arguments); - static unique_ptr TransformMethodExpressionArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MethodArguments TransformMethodExpressionArguments(PEGTransformer &transformer, MethodArguments method_expression_argument_list); - static unique_ptr TransformMethodExpressionArgumentListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MethodArguments TransformMethodExpressionArgumentList(PEGTransformer &transformer, const optional &distinct_or_all, optional> method_function_arguments, optional> order_by_clause, const optional &ignore_or_respect_nulls); - static unique_ptr TransformMethodFunctionArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformMethodFunctionArguments(PEGTransformer &transformer, vector function_argument); - static unique_ptr TransformSliceExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSliceExpression(PEGTransformer &transformer, vector> slice_bound); - static unique_ptr TransformSliceBoundInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformSliceBound(PEGTransformer &transformer, optional> expression, - optional> end_slice_bound, - optional> step_slice_bound); - static unique_ptr TransformEndSliceBoundInternal(PEGTransformer &transformer, - ParseResult &parse_result); + optional>> slice_upper_bound); + static vector> + TransformMinusStepSliceBound(PEGTransformer &transformer, unique_ptr step_slice_bound); + static vector> + TransformEndStepSliceBound(PEGTransformer &transformer, unique_ptr end_slice_bound, + optional> step_slice_bound); static unique_ptr TransformEndSliceBound(PEGTransformer &transformer, - optional> end_slice_value); - static unique_ptr TransformEndSliceValueInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformEndSliceMinusInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformEndSliceMinus(PEGTransformer &transformer); - static unique_ptr TransformStepSliceBoundInternal(PEGTransformer &transformer, - ParseResult &parse_result); + optional> expression); static unique_ptr TransformStepSliceBound(PEGTransformer &transformer, optional> expression); - static unique_ptr TransformPostfixOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformPostfixOperator(PEGTransformer &transformer); - static unique_ptr TransformSpecialFunctionExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCoalesceExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCoalesceExpression(PEGTransformer &transformer, vector> expression); - static unique_ptr TransformUnpackExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformUnpackExpression(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformTryExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTryExpression(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformColumnsExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformColumnsExpression(PEGTransformer &transformer, const bool &has_result, unique_ptr expression); - static unique_ptr TransformExtractExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformExtractExpression(PEGTransformer &transformer, vector> extract_arguments); - static unique_ptr TransformExtractArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformExtractArguments(PEGTransformer &transformer, unique_ptr extract_argument, unique_ptr expression); - static unique_ptr TransformLambdaExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformLambdaExpression(PEGTransformer &transformer, const vector &col_id_or_string, unique_ptr expression); - static unique_ptr TransformNullIfExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformNullIfExpression(PEGTransformer &transformer, vector> null_if_arguments); - static unique_ptr TransformNullIfArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformNullIfArguments(PEGTransformer &transformer, unique_ptr expression, unique_ptr expression_1); - static unique_ptr TransformPositionExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformPositionExpression(PEGTransformer &transformer, vector> position_arguments); - static unique_ptr TransformPositionArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformPositionArguments(PEGTransformer &transformer, unique_ptr other_operator_expression, unique_ptr expression); - static unique_ptr TransformRowExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformRowExpression(PEGTransformer &transformer, optional>> expression); - static unique_ptr TransformSubstringExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSubstringExpression(PEGTransformer &transformer, vector> substring_arguments); - static unique_ptr TransformSubstringArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSubstringExpressionListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformSubstringExpressionList(PEGTransformer &transformer, vector> expression); - static unique_ptr TransformSubstringParametersInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformSubstringParameters(PEGTransformer &transformer, unique_ptr expression, vector> substring_from_for); - static unique_ptr TransformSubstringFromForInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSubstringFromOptionalForInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformSubstringFromOptionalFor(PEGTransformer &transformer, unique_ptr from_expression, optional> for_expression); - static unique_ptr TransformSubstringForInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformSubstringFor(PEGTransformer &transformer, unique_ptr for_expression); - static unique_ptr TransformTrimExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTrimExpression(PEGTransformer &transformer, TrimArguments trim_arguments); - static unique_ptr TransformTrimArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static TrimArguments TransformTrimArguments(PEGTransformer &transformer, const optional &trim_direction, optional> trim_source, vector> expression); - static unique_ptr TransformTrimDirectionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformTrimBothInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformTrimBoth(PEGTransformer &transformer); - static unique_ptr TransformTrimLeadingInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformTrimLeading(PEGTransformer &transformer); - static unique_ptr TransformTrimTrailingInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformTrimTrailing(PEGTransformer &transformer); - static unique_ptr TransformTrimSourceInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTrimSource(PEGTransformer &transformer, optional> expression); - static unique_ptr TransformOverlayExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformOverlayExpression(PEGTransformer &transformer, vector> overlay_arguments); - static unique_ptr TransformOverlayArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformOverlayParametersInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformOverlayParameters(PEGTransformer &transformer, unique_ptr expression, unique_ptr expression_1, unique_ptr from_expression, optional> for_expression); - static unique_ptr TransformFromExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformFromExpression(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformForExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformForExpression(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformOverlayExpressionListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformOverlayExpressionList(PEGTransformer &transformer, vector> expression); - static unique_ptr TransformExtractArgumentInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformExtractDatePartArgumentInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformExtractDatePartArgument(PEGTransformer &transformer, const DatePartSpecifier &extract_date_part); - static unique_ptr TransformExtractIdentifierArgumentInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformExtractIdentifierArgument(PEGTransformer &transformer, const Identifier &identifier); - static unique_ptr TransformExtractStringArgumentInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformExtractStringArgument(PEGTransformer &transformer, const string &string_literal); - static unique_ptr TransformExtractDatePartInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformExternalResourceStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCreateExternalResourceStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCreateExternalResourceStmt(PEGTransformer &transformer, const string &string_literal, const optional &attach_alias, const optional> &external_resource_creation_options); - static unique_ptr TransformRegisterExternalResourceStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformRegisterExternalResourceStmt(PEGTransformer &transformer, const string &string_literal, const optional &attach_alias, unique_ptr expression); - static unique_ptr TransformDestroyExternalResourceStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDestroyExternalResourceStmt(PEGTransformer &transformer, const Identifier &col_id); - static unique_ptr TransformShowExternalResourcesStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformShowExternalResourcesStmt(PEGTransformer &transformer, const optional &show_all_modifier); - static unique_ptr TransformShowAllModifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformShowAllModifier(PEGTransformer &transformer); - static unique_ptr - TransformExternalResourceCreationOptionsInternal(PEGTransformer &transformer, ParseResult &parse_result); - static unique_ptr TransformInsertStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static unique_ptr TransformAttachToExternalResource( + PEGTransformer &transformer, unique_ptr external_resource_source, + const Identifier &attach_alias, const optional> &attach_options); + static unique_ptr + TransformConnectToExternalResource(PEGTransformer &transformer, + unique_ptr external_resource_source, + const optional> &attach_options); + static unique_ptr TransformExternalResourceCreateClause( + PEGTransformer &transformer, const string &string_literal, + const optional> &external_resource_creation_options); + static unique_ptr TransformExternalResourceReferenceClause(PEGTransformer &transformer, + const Identifier &col_id); static unique_ptr TransformInsertStatement(PEGTransformer &transformer, optional with_clause, const optional &or_action, unique_ptr insert_target, const optional &by_name_or_position, - const optional> &insert_column_list, InsertValues insert_values, - optional> on_conflict_clause, + const optional> &insert_column_list, bool has_overriding_clause, + InsertValues insert_values, optional> on_conflict_clause, optional>> returning_clause); - static unique_ptr TransformOrActionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformInsertOrReplaceInternal(PEGTransformer &transformer, - ParseResult &parse_result); static OnConflictAction TransformInsertOrReplace(PEGTransformer &transformer); - static unique_ptr TransformInsertOrIgnoreInternal(PEGTransformer &transformer, - ParseResult &parse_result); static OnConflictAction TransformInsertOrIgnore(PEGTransformer &transformer); - static unique_ptr TransformByNameOrPositionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformInsertByNameOrderInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformInsertByPositionOrderInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformInsertByNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static InsertColumnOrder TransformInsertByName(PEGTransformer &transformer); - static unique_ptr TransformInsertByPositionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static InsertColumnOrder TransformInsertByPosition(PEGTransformer &transformer); - static unique_ptr TransformInsertTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformInsertTarget(PEGTransformer &transformer, unique_ptr base_table_name, const optional &insert_alias); - static unique_ptr TransformInsertAliasInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformInsertAlias(PEGTransformer &transformer, const Identifier &identifier); - static unique_ptr TransformColumnListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformColumnList(PEGTransformer &transformer, const vector &col_id); - static unique_ptr TransformInsertColumnListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformInsertColumnList(PEGTransformer &transformer, const vector &column_list); - static unique_ptr TransformInsertValuesInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSelectInsertValuesInternal(PEGTransformer &transformer, - ParseResult &parse_result); static InsertValues TransformSelectInsertValues(PEGTransformer &transformer, unique_ptr select_statement_internal); - static unique_ptr TransformDefaultValuesInternal(PEGTransformer &transformer, - ParseResult &parse_result); static InsertValues TransformDefaultValues(PEGTransformer &transformer); - static unique_ptr TransformOnConflictClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformOnConflictClause(PEGTransformer &transformer, optional on_conflict_target, unique_ptr on_conflict_action); - static unique_ptr TransformOnConflictTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformOnConflictExpressionTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); static OnConflictExpressionTarget TransformOnConflictExpressionTarget(PEGTransformer &transformer, const vector &column_id_list, optional> where_clause); - static unique_ptr TransformOnConflictIndexTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); static OnConflictExpressionTarget TransformOnConflictIndexTarget(PEGTransformer &transformer, const Identifier &constraint_name); - static unique_ptr TransformOnConflictActionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformOnConflictUpdateInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformOnConflictUpdate(PEGTransformer &transformer, unique_ptr update_set_clause, optional> where_clause); - static unique_ptr TransformOnConflictNothingInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformOnConflictNothing(PEGTransformer &transformer); - static unique_ptr TransformReturningClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformReturningClause(PEGTransformer &transformer, vector> target_list); - static unique_ptr TransformLoadStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformLoadStatement(PEGTransformer &transformer, const Identifier &col_id_or_string, const optional &from_source, const optional &extension_alias); - static unique_ptr TransformExtensionAliasInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformExtensionAlias(PEGTransformer &transformer, const Identifier &identifier); - static unique_ptr TransformInstallStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformInstallStatement(PEGTransformer &transformer, const bool &has_result, const optional &install_and_load, const QualifiedName &identifier_or_string_literal, const optional &from_source, const optional &version_number); - static unique_ptr TransformInstallAndLoadInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformInstallAndLoad(PEGTransformer &transformer); - static unique_ptr TransformUpdateExtensionsStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformUpdateExtensionsStatement(PEGTransformer &transformer, const optional> &identifier); - static unique_ptr TransformFromSourceInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformFromSourceIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ExtensionRepositoryInfo TransformFromSourceIdentifier(PEGTransformer &transformer, const Identifier &identifier); - static unique_ptr TransformFromSourceStringInternal(PEGTransformer &transformer, - ParseResult &parse_result); static ExtensionRepositoryInfo TransformFromSourceString(PEGTransformer &transformer, const string &string_literal); - static unique_ptr TransformVersionNumberInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformVersionNumber(PEGTransformer &transformer, const QualifiedName &identifier_or_string_literal); - static unique_ptr TransformExtensionRepositoryStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCreateExtensionRepositoryStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCreateExtensionRepositoryStmt(PEGTransformer &transformer, const optional &or_replace, const optional &if_not_exists, const Identifier &col_id_or_string, const string &repository_prefix, const optional> &repository_public_key); - static unique_ptr TransformRepositoryPrefixInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformRepositoryPrefix(PEGTransformer &transformer, const bool &has_result, const string &string_literal); - static unique_ptr TransformRepositoryPublicKeyInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformRepositoryPublicKey(PEGTransformer &transformer, const bool &has_result, const vector &string_literal); - static unique_ptr TransformDropExtensionRepositoryStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDropExtensionRepositoryStmt(PEGTransformer &transformer, const optional &if_exists, const Identifier &col_id_or_string); - static unique_ptr TransformTableMatchRecognizeClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTableMatchRecognizeClause(PEGTransformer &transformer, unique_ptr match_recognize_body, const optional &table_alias); - static unique_ptr TransformMatchRecognizeBodyInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformMatchRecognizeBody(PEGTransformer &transformer, vector match_recognize_clause); - static unique_ptr TransformMatchRecognizeClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformMRPartitionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeClause TransformMRPartition(PEGTransformer &transformer, vector> window_partition); - static unique_ptr TransformMROrderByInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeClause TransformMROrderBy(PEGTransformer &transformer, vector order_by_clause); - static unique_ptr TransformMRMeasuresInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeClause TransformMRMeasures(PEGTransformer &transformer, vector> measures_clause); - static unique_ptr TransformMRRowsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeClause TransformMRRows(PEGTransformer &transformer, const MatchRecognizeRows &rows_per_match); - static unique_ptr TransformMRSkipInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeClause TransformMRSkip(PEGTransformer &transformer, MatchRecognizeAfterMatchClause after_match_skip); - static unique_ptr TransformMRPatternInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeClause TransformMRPattern(PEGTransformer &transformer, unique_ptr pattern_clause); - static unique_ptr TransformMRSubsetInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeClause TransformMRSubset(PEGTransformer &transformer, vector subset_clause); - static unique_ptr TransformMRDefineAutoInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeClause TransformMRDefineAuto(PEGTransformer &transformer); - static unique_ptr TransformMRDefineInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeClause TransformMRDefine(PEGTransformer &transformer, vector> define_clause); - static unique_ptr TransformMeasuresClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformMeasuresClause(PEGTransformer &transformer, vector> measures_element); - static unique_ptr TransformMeasuresElementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformMeasuresElement(PEGTransformer &transformer, unique_ptr expression, const Identifier &col_label_or_string); - static unique_ptr TransformMeasureSemanticsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformRunningSemanticsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformRunningSemantics(PEGTransformer &transformer); - static unique_ptr TransformFinalSemanticsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformFinalSemantics(PEGTransformer &transformer); - static unique_ptr TransformRowsPerMatchInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformOneRowPerMatchInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeRows TransformOneRowPerMatch(PEGTransformer &transformer); - static unique_ptr TransformAllRowsPerMatchInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeRows TransformAllRowsPerMatch(PEGTransformer &transformer, const optional &all_rows_empty_matches); - static unique_ptr TransformAllRowsEmptyMatchesInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformShowEmptyMatchesInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeRows TransformShowEmptyMatches(PEGTransformer &transformer); - static unique_ptr TransformOmitEmptyMatchesInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeRows TransformOmitEmptyMatches(PEGTransformer &transformer); - static unique_ptr TransformWithUnmatchedRowsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeRows TransformWithUnmatchedRows(PEGTransformer &transformer); - static unique_ptr TransformAfterMatchSkipInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformAfterMatchSkipTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSkipToNextRowInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeAfterMatchClause TransformSkipToNextRow(PEGTransformer &transformer); - static unique_ptr TransformSkipPastLastRowInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeAfterMatchClause TransformSkipPastLastRow(PEGTransformer &transformer); - static unique_ptr TransformSkipToFirstVarInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeAfterMatchClause TransformSkipToFirstVar(PEGTransformer &transformer, const Identifier &col_label_or_string); - static unique_ptr TransformSkipToLastVarInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeAfterMatchClause TransformSkipToLastVar(PEGTransformer &transformer, const Identifier &col_label_or_string); - static unique_ptr TransformSkipToVarInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeAfterMatchClause TransformSkipToVar(PEGTransformer &transformer, const Identifier &col_label_or_string); - static unique_ptr TransformPatternClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformRowPatternInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformRowPattern(PEGTransformer &transformer, unique_ptr row_pattern_term, optional>> row_pattern_alternative); - static unique_ptr TransformRowPatternAlternativeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformRowPatternTermInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformRowPatternTerm(PEGTransformer &transformer, vector> row_pattern_factor); - static unique_ptr TransformRowPatternFactorInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformRowPatternFactor(PEGTransformer &transformer, unique_ptr row_pattern_primary, const optional &row_pattern_quantifier); - static unique_ptr TransformRowPatternPrimaryInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformRowPatternPermuteInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformRowPatternPermute(PEGTransformer &transformer, vector> row_pattern); - static unique_ptr TransformRowPatternAnchorInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformPatternStartInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformPatternStart(PEGTransformer &transformer); - static unique_ptr TransformPatternEndInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformPatternEnd(PEGTransformer &transformer); - static unique_ptr TransformRowPatternGroupInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformRowPatternEmptyInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformRowPatternEmpty(PEGTransformer &transformer); - static unique_ptr TransformRowPatternExclusionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformRowPatternExclusion(PEGTransformer &transformer, unique_ptr row_pattern); - static unique_ptr TransformRowPatternLabelInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformRowPatternLabel(PEGTransformer &transformer, const Identifier &col_label_or_string); - static unique_ptr TransformRowPatternQuantifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeQuantifier TransformRowPatternQuantifier(PEGTransformer &transformer, const MatchRecognizeQuantifier &row_pattern_quantifier_kind, const optional &quantifier_reluctant); - static unique_ptr TransformRowPatternQuantifierKindInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformQuantifierReluctantInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformQuantifierReluctant(PEGTransformer &transformer); - static unique_ptr TransformQuantifierStarInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeQuantifier TransformQuantifierStar(PEGTransformer &transformer); - static unique_ptr TransformQuantifierPlusInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeQuantifier TransformQuantifierPlus(PEGTransformer &transformer); - static unique_ptr TransformQuantifierOptionalInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeQuantifier TransformQuantifierOptional(PEGTransformer &transformer); - static unique_ptr TransformQuantifierRangeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformQuantifierBoundsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformQuantifierMinMaxInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeQuantifier TransformQuantifierMinMax(PEGTransformer &transformer, unique_ptr number_literal, unique_ptr number_literal_1); - static unique_ptr TransformQuantifierMinInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeQuantifier TransformQuantifierMin(PEGTransformer &transformer, unique_ptr number_literal); - static unique_ptr TransformQuantifierMaxInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeQuantifier TransformQuantifierMax(PEGTransformer &transformer, unique_ptr number_literal); - static unique_ptr TransformQuantifierExactInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeQuantifier TransformQuantifierExact(PEGTransformer &transformer, unique_ptr number_literal); - static unique_ptr TransformSubsetClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformSubsetClause(PEGTransformer &transformer, vector subset_element); - static unique_ptr TransformSubsetElementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MatchRecognizeSubset TransformSubsetElement(PEGTransformer &transformer, const Identifier &col_label_or_string, const vector &col_label_or_string_1); - static unique_ptr TransformDefineClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformDefineClause(PEGTransformer &transformer, vector> define_element); - static unique_ptr TransformDefineElementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDefineElement(PEGTransformer &transformer, const Identifier &col_label_or_string, unique_ptr expression); - static unique_ptr TransformMergeIntoStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformMergeIntoStatement(PEGTransformer &transformer, optional with_clause, unique_ptr target_opt_alias, unique_ptr merge_into_using_clause, JoinQualifier join_qualifier, vector>> merge_match, optional>> returning_clause); - static unique_ptr TransformMergeIntoUsingClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformMergeIntoUsingClause(PEGTransformer &transformer, unique_ptr table_ref); - static unique_ptr TransformMergeMatchInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformMatchedClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static pair> TransformMatchedClause(PEGTransformer &transformer, optional> and_expression, unique_ptr matched_clause_action); - static unique_ptr TransformMatchedClauseActionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformUpdateMatchClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformUpdateMatchClause(PEGTransformer &transformer, optional> update_match_info); - static unique_ptr TransformUpdateMatchInfoInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformUpdateMatchSetActionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformUpdateMatchSetAction(PEGTransformer &transformer, unique_ptr update_match_set_clause); - static unique_ptr TransformUpdateByNameOrPositionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformUpdateByNameOrPosition(PEGTransformer &transformer, const InsertColumnOrder &by_name_or_position); - static unique_ptr TransformDeleteMatchClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDeleteMatchClause(PEGTransformer &transformer); - static unique_ptr TransformInsertMatchClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformInsertMatchClause(PEGTransformer &transformer, optional> insert_match_info); - static unique_ptr TransformInsertMatchInfoInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformInsertDefaultValuesInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformInsertDefaultValues(PEGTransformer &transformer); - static unique_ptr TransformInsertByNameOrPositionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformInsertByNameOrPosition(PEGTransformer &transformer, const optional &by_name_or_position, const bool &has_result); - static unique_ptr TransformInsertValuesListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformInsertValuesList(PEGTransformer &transformer, const optional> &insert_column_list, vector> expression); - static unique_ptr TransformDoNothingMatchClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformDoNothingMatchClause(PEGTransformer &transformer); - static unique_ptr TransformErrorMatchClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformErrorMatchClause(PEGTransformer &transformer, optional> expression); - static unique_ptr TransformUpdateMatchSetClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformUpdateMatchSetInfoInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformAndExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformAndExpression(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformNotMatchedClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static pair> TransformNotMatchedClause(PEGTransformer &transformer, const optional &by_source_or_target, optional> and_expression, unique_ptr matched_clause_action); - static unique_ptr TransformBySourceOrTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformBySourceInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MergeActionCondition TransformBySource(PEGTransformer &transformer); - static unique_ptr TransformByTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MergeActionCondition TransformByTarget(PEGTransformer &transformer); - static unique_ptr TransformPivotOnInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformPivotOn(PEGTransformer &transformer, vector pivot_column_list); - static unique_ptr TransformPivotUsingInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformPivotUsing(PEGTransformer &transformer, vector> target_list); - static unique_ptr TransformPivotColumnListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformPivotColumnList(PEGTransformer &transformer, vector pivot_column_entry); - static unique_ptr TransformPivotColumnEntryInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformPivotColumnExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static PivotColumn TransformPivotColumnExpression(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformPivotColumnSubqueryInternal(PEGTransformer &transformer, - ParseResult &parse_result); static PivotColumn TransformPivotColumnSubquery(PEGTransformer &transformer, unique_ptr base_expression, unique_ptr select_statement_internal); - static unique_ptr TransformIntoNameValuesInternal(PEGTransformer &transformer, - ParseResult &parse_result); static UnpivotNameValues TransformIntoNameValues(PEGTransformer &transformer, const Identifier &col_id_or_string, const vector &optional_parens_name_list); - static unique_ptr TransformOptionalParensNameListInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformParenthesizedNameListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformParenthesizedNameList(PEGTransformer &transformer, const vector &col_id_or_string); - static unique_ptr TransformBareNameListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformBareNameList(PEGTransformer &transformer, const vector &col_id_or_string); - static unique_ptr TransformIncludeOrExcludeNullsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformIncludeNullsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformIncludeNulls(PEGTransformer &transformer); - static unique_ptr TransformExcludeNullsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformExcludeNulls(PEGTransformer &transformer); - static unique_ptr TransformUnpivotHeaderInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformUnpivotHeaderSingleInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformUnpivotHeaderSingle(PEGTransformer &transformer, const Identifier &col_id_or_string); - static unique_ptr TransformUnpivotHeaderListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformUnpivotHeaderList(PEGTransformer &transformer, const vector &col_id_or_string); - static unique_ptr TransformPragmaStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformPragmaStatement(PEGTransformer &transformer, unique_ptr pragma_assign_or_function); - static unique_ptr TransformPragmaAssignOrFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformPragmaAssignInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformPragmaAssign(PEGTransformer &transformer, const Identifier &setting_name, vector> variable_list); - static unique_ptr TransformPragmaFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformPragmaFunction(PEGTransformer &transformer, const Identifier &pragma_name, optional>> pragma_parameters); - static unique_ptr TransformPragmaParametersInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformPragmaParameters(PEGTransformer &transformer, vector> expression); - static unique_ptr TransformPrepareStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformPrepareStatement(PEGTransformer &transformer, const Identifier &identifier, const optional> &type_list, unique_ptr statement); - static unique_ptr TransformTypeListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformTypeList(PEGTransformer &transformer, const vector &type); - static unique_ptr TransformSelectStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSelectStatement(PEGTransformer &transformer, unique_ptr select_statement_internal); - static unique_ptr TransformSelectSetOpChainInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSelectSetOpChain( PEGTransformer &transformer, unique_ptr intersect_chain, optional, unique_ptr>>> select_set_op_chain_tail); - static unique_ptr TransformSelectSetOpChainTailInternal(PEGTransformer &transformer, - ParseResult &parse_result); static pair, unique_ptr> TransformSelectSetOpChainTail(PEGTransformer &transformer, unique_ptr setop_clause, unique_ptr intersect_chain); - static unique_ptr TransformIntersectChainInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformIntersectChain( PEGTransformer &transformer, unique_ptr select_atom, optional, unique_ptr>>> intersect_chain_tail); - static unique_ptr TransformIntersectChainTailInternal(PEGTransformer &transformer, - ParseResult &parse_result); static pair, unique_ptr> TransformIntersectChainTail(PEGTransformer &transformer, unique_ptr set_intersect_clause, unique_ptr select_atom); - static unique_ptr TransformSetIntersectClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSetIntersectClause(PEGTransformer &transformer, const optional &distinct_or_all); - static unique_ptr TransformSelectAtomInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSelectParensInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSelectParens(PEGTransformer &transformer, unique_ptr select_statement_internal); - static unique_ptr TransformSetopClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSetopClause(PEGTransformer &transformer, const SetOperationType &setop_type, const optional &distinct_or_all, const bool &has_result); - static unique_ptr TransformSetopTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSetopUnionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static SetOperationType TransformSetopUnion(PEGTransformer &transformer); - static unique_ptr TransformSetopExceptInternal(PEGTransformer &transformer, - ParseResult &parse_result); static SetOperationType TransformSetopExcept(PEGTransformer &transformer); - static unique_ptr TransformSelectStatementTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformResultModifiersInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformResultModifiers(PEGTransformer &transformer, optional> order_by_clause, optional> limit_offset); - static unique_ptr TransformLimitOffsetInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformLimitOffsetClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformLimitOffsetClause(PEGTransformer &transformer, LimitPercentResult limit_clause, optional offset_clause); - static unique_ptr TransformOffsetLimitClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformOffsetLimitClause(PEGTransformer &transformer, LimitPercentResult offset_clause, optional limit_clause); - static unique_ptr TransformOffsetFetchClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformOffsetFetchClause(PEGTransformer &transformer, LimitPercentResult offset_clause, LimitPercentResult fetch_clause); - static unique_ptr TransformFetchOnlyClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static unique_ptr TransformFetchOffsetClause(PEGTransformer &transformer, + LimitPercentResult fetch_clause, + LimitPercentResult offset_clause); static unique_ptr TransformFetchOnlyClause(PEGTransformer &transformer, LimitPercentResult fetch_clause); - static unique_ptr TransformTableStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTableStatement(PEGTransformer &transformer, unique_ptr base_table_name); - static unique_ptr TransformOptionalParensSimpleSelectInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSimpleSelectParensInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSimpleSelectParens(PEGTransformer &transformer, unique_ptr simple_select); - static unique_ptr TransformSelectFromInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSelectFromClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSelectFromClause(PEGTransformer &transformer, unique_ptr select_clause, optional> from_clause); - static unique_ptr TransformFromSelectClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformFromSelectClause(PEGTransformer &transformer, unique_ptr from_clause, optional> select_clause); - static unique_ptr TransformWithStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static pair> TransformWithStatement(PEGTransformer &transformer, const Identifier &col_id_or_string, - const optional> &insert_column_list, + const optional> &column_aliases, optional>> using_key, const optional &materialized, unique_ptr cte_body); - static unique_ptr TransformCTEBodyInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCTESelectBodyInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCTESelectBody(PEGTransformer &transformer, unique_ptr select_statement_internal); - static unique_ptr TransformCTEDMLBodyInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCTEDMLBody(PEGTransformer &transformer, unique_ptr statement); - static unique_ptr TransformUsingKeyInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformUsingKey(PEGTransformer &transformer, vector> target_list); - static unique_ptr TransformMaterializedInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformMaterialized(PEGTransformer &transformer, const bool &has_result); - static unique_ptr TransformSelectClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSelectClause(PEGTransformer &transformer, optional distinct_clause, optional>> target_list); - static unique_ptr TransformTargetListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformTargetList(PEGTransformer &transformer, vector> aliased_expression); - static unique_ptr TransformColumnAliasesInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformColumnAliases(PEGTransformer &transformer, const vector &col_id_or_string); - static unique_ptr TransformDistinctClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformDistinctAllInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DistinctClause TransformDistinctAll(PEGTransformer &transformer); - static unique_ptr TransformDistinctOnInternal(PEGTransformer &transformer, - ParseResult &parse_result); static DistinctClause TransformDistinctOn(PEGTransformer &transformer, optional>> distinct_on_targets); - static unique_ptr TransformDistinctOnTargetsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformDistinctOnTargets(PEGTransformer &transformer, vector> expression); - static unique_ptr TransformInnerTableRefInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformTableSubqueryInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformTableSubquery(PEGTransformer &transformer, - const optional &table_alias_colon, - const optional &lateral, - unique_ptr subquery_reference, - const optional &table_alias); - static unique_ptr TransformBaseTableRefInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static unique_ptr + TransformTableSubquery(PEGTransformer &transformer, const optional &table_alias_colon, + const optional &lateral, unique_ptr subquery_reference, + const optional &table_alias, optional> sample_clause); static unique_ptr TransformBaseTableRef(PEGTransformer &transformer, const optional &table_alias_colon, unique_ptr base_table_name, const optional &table_alias, optional> at_clause, optional> sample_clause); - static unique_ptr TransformTableAliasColonInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformTableAliasColon(PEGTransformer &transformer, const Identifier &col_id_or_string); - static unique_ptr TransformValuesRefInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformValuesRef(PEGTransformer &transformer, const optional &table_alias_colon, unique_ptr values_clause, const optional &table_alias); - static unique_ptr TransformParensTableRefInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformParensTableRef(PEGTransformer &transformer, const optional &table_alias_colon, unique_ptr table_ref, const optional &table_alias, optional> sample_clause); - static unique_ptr TransformJoinOrPivotInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformTablePivotClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTablePivotClause(PEGTransformer &transformer, unique_ptr table_pivot_clause_body, const optional &table_alias); - static unique_ptr TransformTablePivotClauseBodyInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTablePivotClauseBody(PEGTransformer &transformer, vector> target_list, vector pivot_value_list, const optional> &pivot_group_by_list); - static unique_ptr TransformPivotGroupByListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformPivotGroupByList(PEGTransformer &transformer, const vector &optional_parens_name_list); - static unique_ptr TransformTableUnpivotClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTableUnpivotClause(PEGTransformer &transformer, const optional &include_or_exclude_nulls, unique_ptr table_unpivot_clause_body, const optional &table_alias); - static unique_ptr TransformTableUnpivotClauseBodyInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformTableUnpivotClauseBody(PEGTransformer &transformer, const vector &unpivot_header, vector unpivot_value_list); - static unique_ptr TransformPivotHeaderInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformPivotHeader(PEGTransformer &transformer, unique_ptr base_expression); - static unique_ptr TransformPivotValueListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static PivotColumn TransformPivotValueList(PEGTransformer &transformer, unique_ptr pivot_header, PivotColumn pivot_value_target); - static unique_ptr TransformPivotValueTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformPivotEnumTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); static PivotColumn TransformPivotEnumTarget(PEGTransformer &transformer, const Identifier &identifier); - static unique_ptr TransformPivotListTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); static PivotColumn TransformPivotListTarget(PEGTransformer &transformer, vector pivot_target_list); - static unique_ptr TransformUnpivotValueListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static PivotColumn TransformUnpivotValueList(PEGTransformer &transformer, const vector &unpivot_header, vector unpivot_target_list); - static unique_ptr TransformPivotTargetListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformPivotTargetList(PEGTransformer &transformer, vector> target_list); - static unique_ptr TransformUnpivotTargetListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformUnpivotTargetList(PEGTransformer &transformer, vector> target_list); - static unique_ptr TransformLateralInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformLateral(PEGTransformer &transformer); - static unique_ptr TransformBaseTableNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformUnqualifiedBaseTableNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformUnqualifiedBaseTableName(PEGTransformer &transformer, const Identifier &table_name); - static unique_ptr TransformQualifiedTableNameInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSchemaReservedTableInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSchemaReservedTable(PEGTransformer &transformer, const Identifier &schema_qualification, const Identifier &reserved_table_name); - static unique_ptr TransformCatalogReservedSchemaTableInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCatalogReservedSchemaTable(PEGTransformer &transformer, const Identifier &catalog_qualification, const vector &reserved_schema_qualification, const Identifier &reserved_table_name); - static unique_ptr TransformTableFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformTableFunctionLateralOptInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformTableFunctionLateralOpt(PEGTransformer &transformer, - const optional &lateral, - const QualifiedName &qualified_table_function, - vector table_function_arguments, - const optional &with_ordinality, - const optional &table_alias); - static unique_ptr TransformTableFunctionAliasColonInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static unique_ptr TransformTableFunctionLateralOpt( + PEGTransformer &transformer, const optional &lateral, const QualifiedName &qualified_table_function, + vector table_function_arguments, const optional &with_ordinality, + const optional &table_alias, optional> sample_clause); static unique_ptr TransformTableFunctionAliasColon(PEGTransformer &transformer, const Identifier &table_alias_colon, const QualifiedName &qualified_table_function, vector table_function_arguments, const optional &with_ordinality, optional> sample_clause); - static unique_ptr TransformWithOrdinalityInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformWithOrdinality(PEGTransformer &transformer); - static unique_ptr TransformQualifiedTableFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static QualifiedName TransformQualifiedTableFunction(PEGTransformer &transformer, const optional &catalog_qualification, const optional> &schema_qualification, const Identifier &table_function_name); - static unique_ptr TransformTableFunctionArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformTableFunctionArguments(PEGTransformer &transformer, optional> function_argument); - static unique_ptr TransformFunctionArgumentInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformNamedFunctionArgumentInternal(PEGTransformer &transformer, - ParseResult &parse_result); static FunctionArgument TransformNamedFunctionArgument(PEGTransformer &transformer, MacroParameter named_parameter); - static unique_ptr TransformPositionalFunctionArgumentInternal(PEGTransformer &transformer, - ParseResult &parse_result); static FunctionArgument TransformPositionalFunctionArgument(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformNamedParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result); static MacroParameter TransformNamedParameter(PEGTransformer &transformer, const Identifier &type_func_name, const optional &type, unique_ptr expression); - static unique_ptr TransformTableAliasInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformTableAliasAsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static TableAlias TransformTableAliasAs(PEGTransformer &transformer, const QualifiedName &identifier_or_string_literal, const optional> &column_aliases); - static unique_ptr TransformTableAliasWithoutAsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static TableAlias TransformTableAliasWithoutAs(PEGTransformer &transformer, const Identifier &identifier, const optional> &column_aliases); - static unique_ptr TransformAtClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformAtClause(PEGTransformer &transformer, unique_ptr at_specifier); - static unique_ptr TransformAtSpecifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformAtSpecifier(PEGTransformer &transformer, const string &at_unit, unique_ptr expression); - static unique_ptr TransformAtUnitInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformVersionAtUnitInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformVersionAtUnit(PEGTransformer &transformer); - static unique_ptr TransformTimestampAtUnitInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformTimestampAtUnit(PEGTransformer &transformer); - static unique_ptr TransformJoinClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformNearestJoinClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformNearestJoinAliasedInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformNearestJoinAliased(PEGTransformer &transformer, const optional &join_type, unique_ptr table_ref, const optional &approx_or_exact, optional> number_literal, const OrderType &distance_or_similarity, unique_ptr expression); - static unique_ptr TransformNearestJoinBareInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformNearestJoinBare(PEGTransformer &transformer, const optional &join_type, unique_ptr nearest_bare_table_ref, const optional &approx_or_exact, optional> number_literal, const OrderType &distance_or_similarity, unique_ptr expression); - static unique_ptr TransformNearestBareTableRefInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformNearestValuesRefInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformNearestValuesRef(PEGTransformer &transformer, unique_ptr values_clause); - static unique_ptr TransformNearestTableFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformNearestTableFunction(PEGTransformer &transformer, const optional &lateral, const QualifiedName &qualified_table_function, vector table_function_arguments, const optional &with_ordinality); - static unique_ptr TransformNearestTableSubqueryInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformNearestTableSubquery(PEGTransformer &transformer, const optional &lateral, unique_ptr subquery_reference); - static unique_ptr TransformNearestBaseTableRefInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformNearestBaseTableRef(PEGTransformer &transformer, unique_ptr base_table_name, optional> at_clause, optional> sample_clause); - static unique_ptr TransformNearestParensTableRefInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformNearestParensTableRef(PEGTransformer &transformer, unique_ptr table_ref, optional> sample_clause); - static unique_ptr TransformApproxOrExactInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformNearestApproxInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformNearestApprox(PEGTransformer &transformer); - static unique_ptr TransformNearestExactInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformNearestExact(PEGTransformer &transformer); - static unique_ptr TransformDistanceOrSimilarityInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformNearestDistanceInternal(PEGTransformer &transformer, - ParseResult &parse_result); static OrderType TransformNearestDistance(PEGTransformer &transformer); - static unique_ptr TransformNearestSimilarityInternal(PEGTransformer &transformer, - ParseResult &parse_result); static OrderType TransformNearestSimilarity(PEGTransformer &transformer); - static unique_ptr TransformRegularJoinClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformRegularJoinClause(PEGTransformer &transformer, const optional &asof, const optional &join_type, unique_ptr table_ref, JoinQualifier join_qualifier); - static unique_ptr TransformJoinByClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformJoinByClause(PEGTransformer &transformer, const string &col_label, unique_ptr table_ref, JoinQualifier join_qualifier); - static unique_ptr TransformAsofInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformAsof(PEGTransformer &transformer); - static unique_ptr TransformJoinWithoutOnClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformJoinWithoutOnClause(PEGTransformer &transformer, const JoinPrefix &join_prefix, unique_ptr inner_table_ref); - static unique_ptr TransformJoinQualifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformOnClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static JoinQualifier TransformOnClause(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformUsingClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static JoinQualifier TransformUsingClause(PEGTransformer &transformer, const vector &column_name); - static unique_ptr TransformJoinTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformJoinPrefixInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCrossJoinPrefixInternal(PEGTransformer &transformer, - ParseResult &parse_result); static JoinPrefix TransformCrossJoinPrefix(PEGTransformer &transformer); - static unique_ptr TransformNaturalJoinPrefixInternal(PEGTransformer &transformer, - ParseResult &parse_result); static JoinPrefix TransformNaturalJoinPrefix(PEGTransformer &transformer, const optional &join_type); - static unique_ptr TransformPositionalJoinPrefixInternal(PEGTransformer &transformer, - ParseResult &parse_result); static JoinPrefix TransformPositionalJoinPrefix(PEGTransformer &transformer); - static unique_ptr TransformFullJoinInternal(PEGTransformer &transformer, - ParseResult &parse_result); static JoinType TransformFullJoin(PEGTransformer &transformer, const bool &has_result); - static unique_ptr TransformLeftJoinInternal(PEGTransformer &transformer, - ParseResult &parse_result); static JoinType TransformLeftJoin(PEGTransformer &transformer, const bool &has_result); - static unique_ptr TransformRightJoinInternal(PEGTransformer &transformer, - ParseResult &parse_result); static JoinType TransformRightJoin(PEGTransformer &transformer, const bool &has_result); - static unique_ptr TransformSemiJoinInternal(PEGTransformer &transformer, - ParseResult &parse_result); static JoinType TransformSemiJoin(PEGTransformer &transformer); - static unique_ptr TransformAntiJoinInternal(PEGTransformer &transformer, - ParseResult &parse_result); static JoinType TransformAntiJoin(PEGTransformer &transformer); - static unique_ptr TransformInnerJoinInternal(PEGTransformer &transformer, - ParseResult &parse_result); static JoinType TransformInnerJoin(PEGTransformer &transformer); - static unique_ptr TransformFromClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformFromClause(PEGTransformer &transformer, vector> table_ref); - static unique_ptr TransformWhereClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformWhereClause(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformGroupByClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GroupByNode TransformGroupByClause(PEGTransformer &transformer, GroupByNode group_by_expressions); - static unique_ptr TransformHavingClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformHavingClause(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformQualifyClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformQualifyClause(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformSampleClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSampleClause(PEGTransformer &transformer, unique_ptr sample_entry); - static unique_ptr TransformWindowClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static unique_ptr TransformSelectSampleClause(PEGTransformer &transformer, + unique_ptr sample_entry); static vector> TransformWindowClause(PEGTransformer &transformer, vector> window_definition); - static unique_ptr TransformSampleEntryInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSampleEntryCountInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSampleEntryCount(PEGTransformer &transformer, unique_ptr sample_count, const optional> &sample_properties); - static unique_ptr TransformSampleEntryFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSampleEntryFunction(PEGTransformer &transformer, const optional &sample_function, unique_ptr sample_count, const optional &repeatable_sample); - static unique_ptr TransformSampleFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static SampleMethod TransformSampleFunction(PEGTransformer &transformer, const Identifier &col_id); - static unique_ptr TransformSamplePropertiesInternal(PEGTransformer &transformer, - ParseResult &parse_result); static pair TransformSampleProperties(PEGTransformer &transformer, const Identifier &col_id, const optional &sample_seed); - static unique_ptr TransformRepeatableSampleInternal(PEGTransformer &transformer, - ParseResult &parse_result); static optional_idx TransformRepeatableSample(PEGTransformer &transformer, const optional_idx &sample_seed); - static unique_ptr TransformSampleSeedInternal(PEGTransformer &transformer, - ParseResult &parse_result); static optional_idx TransformSampleSeed(PEGTransformer &transformer, unique_ptr number_literal); - static unique_ptr TransformSampleCountInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSampleCount(PEGTransformer &transformer, unique_ptr sample_value, const optional &sample_unit); - static unique_ptr TransformSampleValueInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSampleUnitInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSamplePercentageInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformSamplePercentage(PEGTransformer &transformer); - static unique_ptr TransformSampleRowsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static bool TransformSampleRows(PEGTransformer &transformer); - static unique_ptr TransformGroupByExpressionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformGroupByAllInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GroupByNode TransformGroupByAll(PEGTransformer &transformer); - static unique_ptr TransformGroupByListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GroupByNode TransformGroupByList(PEGTransformer &transformer, vector group_by_expression); - static unique_ptr TransformGroupByExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformGroupByBaseExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GroupByExpressionInfo TransformGroupByBaseExpression(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformEmptyGroupingItemInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GroupByExpressionInfo TransformEmptyGroupingItem(PEGTransformer &transformer); - static unique_ptr TransformCubeOrRollupClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GroupByExpressionInfo TransformCubeOrRollupClause(PEGTransformer &transformer, const string &cube_or_rollup, optional>> expression); - static unique_ptr TransformCubeOrRollupInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformCubeKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformCubeKeyword(PEGTransformer &transformer); - static unique_ptr TransformRollupKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformRollupKeyword(PEGTransformer &transformer); - static unique_ptr TransformGroupingSetsClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static GroupByExpressionInfo TransformGroupingSetsClause(PEGTransformer &transformer, vector group_by_expression); - static unique_ptr TransformSubqueryReferenceInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSubqueryReference(PEGTransformer &transformer, unique_ptr select_statement_internal); - static unique_ptr TransformOrderByExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static OrderByNode TransformOrderByExpression(PEGTransformer &transformer, unique_ptr expression, const optional &desc_or_asc, const optional &nulls_first_or_last); - static unique_ptr TransformDescOrAscInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformDescendingOrderInternal(PEGTransformer &transformer, - ParseResult &parse_result); static OrderType TransformDescendingOrder(PEGTransformer &transformer); - static unique_ptr TransformAscendingOrderInternal(PEGTransformer &transformer, - ParseResult &parse_result); static OrderType TransformAscendingOrder(PEGTransformer &transformer); - static unique_ptr TransformNullsFirstOrLastInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformNullsFirstInternal(PEGTransformer &transformer, - ParseResult &parse_result); static OrderByNullType TransformNullsFirst(PEGTransformer &transformer); - static unique_ptr TransformNullsLastInternal(PEGTransformer &transformer, - ParseResult &parse_result); static OrderByNullType TransformNullsLast(PEGTransformer &transformer); - static unique_ptr TransformOrderByClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformOrderByClause(PEGTransformer &transformer, vector order_by_expressions); - static unique_ptr TransformOrderByExpressionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformOrderByExpressionListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformOrderByExpressionList(PEGTransformer &transformer, vector order_by_expression); - static unique_ptr TransformOrderByAllInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformOrderByAll(PEGTransformer &transformer, const optional &desc_or_asc, const optional &nulls_first_or_last); - static unique_ptr TransformLimitClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static LimitPercentResult TransformLimitClause(PEGTransformer &transformer, LimitPercentResult limit_value); - static unique_ptr TransformOffsetClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static LimitPercentResult TransformOffsetClause(PEGTransformer &transformer, LimitPercentResult offset_value); - static unique_ptr TransformOffsetValueInternal(PEGTransformer &transformer, - ParseResult &parse_result); static LimitPercentResult TransformOffsetValue(PEGTransformer &transformer, unique_ptr expression, const bool &has_result); - static unique_ptr TransformLimitValueInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformLimitAllInternal(PEGTransformer &transformer, - ParseResult &parse_result); static LimitPercentResult TransformLimitAll(PEGTransformer &transformer); - static unique_ptr TransformLimitLiteralPercentInternal(PEGTransformer &transformer, - ParseResult &parse_result); static LimitPercentResult TransformLimitLiteralPercent(PEGTransformer &transformer, unique_ptr number_literal); - static unique_ptr TransformLimitExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static LimitPercentResult TransformLimitExpression(PEGTransformer &transformer, unique_ptr expression, const bool &has_result); - static unique_ptr TransformFetchClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformFetchValueInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static LimitPercentResult TransformFetchClauseWithoutValue(PEGTransformer &transformer); static LimitPercentResult TransformFetchValue(PEGTransformer &transformer, unique_ptr expression); - static unique_ptr TransformAliasedExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformColIdExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformColIdExpression(PEGTransformer &transformer, const Identifier &col_id, unique_ptr expression); - static unique_ptr TransformExpressionAsCollabelInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformExpressionAsCollabel(PEGTransformer &transformer, unique_ptr expression, const Identifier &col_label_or_string); - static unique_ptr TransformExpressionOptIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformExpressionOptIdentifier(PEGTransformer &transformer, unique_ptr expression, const optional &identifier); - static unique_ptr TransformValuesClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformValuesClause(PEGTransformer &transformer, vector>> values_expressions); - static unique_ptr TransformValuesExpressionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformValuesExpressions(PEGTransformer &transformer, vector> expression); - static unique_ptr TransformSetStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSetStatement(PEGTransformer &transformer, unique_ptr set_assignment_or_time_zone); - static unique_ptr TransformSetAssignmentOrTimeZoneInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformResetStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformResetStatement(PEGTransformer &transformer, const SettingInfo &set_variable_or_setting); - static unique_ptr TransformSetSchemaInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSetSchema(PEGTransformer &transformer, const string &string_literal); - static unique_ptr TransformStandardAssignmentInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformStandardAssignment(PEGTransformer &transformer, const SettingInfo &set_variable_or_setting, vector> set_assignment); - static unique_ptr TransformSetVariableOrSettingInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSetTimeZoneInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformSetTimeZone(PEGTransformer &transformer, unique_ptr zone_value); - static unique_ptr TransformZoneValueInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformZoneLocalInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformZoneLocal(PEGTransformer &transformer); - static unique_ptr TransformZoneDefaultInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformZoneDefault(PEGTransformer &transformer); - static unique_ptr TransformZoneStringLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformZoneStringLiteral(PEGTransformer &transformer, const string &string_literal); - static unique_ptr TransformZoneIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformZoneIdentifier(PEGTransformer &transformer, const Identifier &identifier); - static unique_ptr TransformZoneIntervalWithIntervalInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformZoneIntervalWithInterval(PEGTransformer &transformer, const string &string_literal, const optional &interval); - static unique_ptr TransformZoneIntervalWithPrecisionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformZoneIntervalWithPrecision(PEGTransformer &transformer, unique_ptr number_literal, const string &string_literal); - static unique_ptr TransformSetSettingInternal(PEGTransformer &transformer, - ParseResult &parse_result); static SettingInfo TransformSetSetting(PEGTransformer &transformer, const optional &setting_scope, const Identifier &setting_name); - static unique_ptr TransformSetVariableInternal(PEGTransformer &transformer, - ParseResult &parse_result); static SettingInfo TransformSetVariable(PEGTransformer &transformer, const SetScope &variable_scope, const Identifier &identifier); - static unique_ptr TransformVariableScopeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static SetScope TransformVariableScope(PEGTransformer &transformer); - static unique_ptr TransformSettingScopeInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformLocalScopeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static SetScope TransformLocalScope(PEGTransformer &transformer); - static unique_ptr TransformSessionScopeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static SetScope TransformSessionScope(PEGTransformer &transformer); - static unique_ptr TransformGlobalScopeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static SetScope TransformGlobalScope(PEGTransformer &transformer); - static unique_ptr TransformSetAssignmentInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformSetAssignment(PEGTransformer &transformer, vector> variable_list); - static unique_ptr TransformVariableListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector> TransformVariableList(PEGTransformer &transformer, vector> expression); - static unique_ptr TransformTransactionStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformBeginTransactionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformBeginTransaction(PEGTransformer &transformer, const bool &has_result, const optional &read_or_write); - static unique_ptr TransformRollbackTransactionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformRollbackTransaction(PEGTransformer &transformer, const bool &has_result); - static unique_ptr TransformCommitTransactionInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformCommitTransaction(PEGTransformer &transformer, const bool &has_result); - static unique_ptr TransformReadOrWriteInternal(PEGTransformer &transformer, - ParseResult &parse_result); static TransactionModifierType TransformReadOrWrite(PEGTransformer &transformer, const TransactionModifierType &read_only_or_read_write); - static unique_ptr TransformReadOnlyOrReadWriteInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformReadOnlyInternal(PEGTransformer &transformer, - ParseResult &parse_result); static TransactionModifierType TransformReadOnly(PEGTransformer &transformer); - static unique_ptr TransformReadWriteInternal(PEGTransformer &transformer, - ParseResult &parse_result); static TransactionModifierType TransformReadWrite(PEGTransformer &transformer); - static unique_ptr TransformUpdateStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformUpdateStatement(PEGTransformer &transformer, optional with_clause, unique_ptr update_target, unique_ptr update_set_clause, optional> from_clause, optional> where_clause, optional>> returning_clause); - static unique_ptr TransformUpdateTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformBaseTableSetInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformBaseTableSet(PEGTransformer &transformer, unique_ptr base_table_name); - static unique_ptr TransformBaseTableAliasSetInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformBaseTableAliasSet(PEGTransformer &transformer, unique_ptr base_table_name, const optional &update_alias); - static unique_ptr TransformUpdateAliasInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformUpdateAlias(PEGTransformer &transformer, const bool &has_result, const Identifier &col_id); - static unique_ptr TransformUpdateSetClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformUpdateSetTupleInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static unique_ptr TransformUpdateSetClauseList(PEGTransformer &transformer, + vector> update_set_info); static unique_ptr TransformUpdateSetTuple(PEGTransformer &transformer, const vector &column_name, unique_ptr expression); - static unique_ptr TransformUpdateSetElementListInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr - TransformUpdateSetElementList(PEGTransformer &transformer, - vector>> update_set_element); - static unique_ptr TransformUpdateSetElementInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static pair> - TransformUpdateSetElement(PEGTransformer &transformer, const string &update_set_column_target, - unique_ptr expression); - static unique_ptr TransformUpdateSetColumnTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static unique_ptr TransformUpdateSetElement(PEGTransformer &transformer, + const string &update_set_column_target, + unique_ptr expression); static string TransformUpdateSetColumnTarget(PEGTransformer &transformer, const Identifier &column_name, const optional> &dot_identifier); - static unique_ptr TransformUseStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformUseStatement(PEGTransformer &transformer, const QualifiedName &use_target); - static unique_ptr TransformUseTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformSchemaNameAsUseTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static QualifiedName TransformSchemaNameAsUseTarget(PEGTransformer &transformer, const Identifier &schema_name); - static unique_ptr TransformCatalogNameAsUseTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static QualifiedName TransformCatalogNameAsUseTarget(PEGTransformer &transformer, const Identifier &catalog_name); - static unique_ptr TransformUseTargetCatalogSchemaInternal(PEGTransformer &transformer, - ParseResult &parse_result); + static QualifiedName TransformSchemaNameAsUseTarget(PEGTransformer &transformer, const string &schema_name); + static QualifiedName TransformCatalogNameAsUseTarget(PEGTransformer &transformer, const string &catalog_name); static QualifiedName TransformUseTargetCatalogSchema(PEGTransformer &transformer, const Identifier &catalog_name, const Identifier &reserved_schema_name, const optional> &dot_identifier); - static unique_ptr TransformDotIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result); static Identifier TransformDotIdentifier(PEGTransformer &transformer, const Identifier &identifier); - static unique_ptr TransformVacuumStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result); static unique_ptr TransformVacuumStatement(PEGTransformer &transformer, const optional &vacuum_options, optional analyze_target); - static unique_ptr TransformVacuumOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformVacuumParensOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static VacuumOptions TransformVacuumParensOptions(PEGTransformer &transformer, const vector &vacuum_option); - static unique_ptr TransformVacuumLegacyOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result); static VacuumOptions TransformVacuumLegacyOptions(PEGTransformer &transformer, const optional &opt_full, const optional &opt_freeze, const optional &opt_verbose, const optional &opt_analyze); - static unique_ptr TransformVacuumOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result); - static unique_ptr TransformOptAnalyzeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformOptAnalyze(PEGTransformer &transformer, const Identifier &analyze_keyword); - static unique_ptr TransformOptFullInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformOptFull(PEGTransformer &transformer); - static unique_ptr TransformOptFreezeInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformOptFreeze(PEGTransformer &transformer); - static unique_ptr TransformOptVerboseInternal(PEGTransformer &transformer, - ParseResult &parse_result); static string TransformOptVerbose(PEGTransformer &transformer); - static unique_ptr TransformNameListInternal(PEGTransformer &transformer, - ParseResult &parse_result); static vector TransformNameList(PEGTransformer &transformer, const vector &col_id); //===--------------------------------------------------------------------===// - // END GENERATED RULES + // END GENERATED TRAMPOLINE RULES //===--------------------------------------------------------------------===// + PEGTransformerFactory(const PEGTransformerFactory &) = delete; + + static const case_insensitive_map_t &GeneratedTransformFrameOps(); + + static string ExtractFormat(const string &file_path); + + static string TransformIdentifierOrKeyword(PEGTransformer &transformer, ParseResult &parse_result); + private: explicit PEGTransformerFactory(ParsedGrammar &grammar_p); ParsedGrammar &grammar; diff --git a/src/duckdb/src/include/duckdb/parser/peg/transformer/transform_result.hpp b/src/duckdb/src/include/duckdb/parser/peg/transformer/transform_result.hpp index f8d3f0b2d..a97eca852 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/transformer/transform_result.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/transformer/transform_result.hpp @@ -1,6 +1,7 @@ #pragma once #include "duckdb/common/common.hpp" +#include "duckdb/common/arena_containers/arena_ptr.hpp" #include diff --git a/src/duckdb/src/include/duckdb/parser/qualified_name.hpp b/src/duckdb/src/include/duckdb/parser/qualified_name.hpp index aa8c13ae1..eb1a9096b 100644 --- a/src/duckdb/src/include/duckdb/parser/qualified_name.hpp +++ b/src/duckdb/src/include/duckdb/parser/qualified_name.hpp @@ -50,6 +50,30 @@ struct QualifiedName { QualifiedName(vector schema_path_p, Identifier name_p) : path(std::move(schema_path_p)) { path.push_back(std::move(name_p)); } + //! Construct from a complete path, including its final name. + static QualifiedName FromPath(vector path) { + QualifiedName result; + result.path = std::move(path); + return result; + } + //! Qualify a schema path with an explicit catalog, without inferring a catalog from the path's length. + static QualifiedName FromCatalogSchema(Identifier catalog, vector schema_path, Identifier name) { + if (schema_path.empty()) { + return QualifiedName(std::move(catalog), Identifier(), std::move(name)); + } + if (!catalog.empty()) { + schema_path.insert(schema_path.begin(), std::move(catalog)); + } + return QualifiedName(std::move(schema_path), std::move(name)); + } + //! Return the qualification as a name in its own right. + QualifiedName Parent() const { + auto result = *this; + if (!result.path.empty()) { + result.path.pop_back(); + } + return result; + } //! The catalog is the first element of the path, but only when the path is fully qualified ([catalog, //! schema..., name]) @@ -116,6 +140,8 @@ struct QualifiedName { //! Parse the (optional) schema and a name from a string in the format of e.g. "schema"."table"; if there is no dot //! the schema will be set to INVALID_SCHEMA static QualifiedName Parse(const string &input); + //! Parse a qualified name, returning false and setting the error if it is malformed or any component is empty + static bool TryParse(const string &input, QualifiedName &result, string &error); static vector ParseComponents(const string &input); string ToString(QualifiedNameToStringMode mode = QualifiedNameToStringMode::DEFAULT) const; //! Render only the qualification (every component before the name), with a trailing "." after each component diff --git a/src/duckdb/src/include/duckdb/parser/query_node.hpp b/src/duckdb/src/include/duckdb/parser/query_node.hpp index d0e05aa69..a63375ea6 100644 --- a/src/duckdb/src/include/duckdb/parser/query_node.hpp +++ b/src/duckdb/src/include/duckdb/parser/query_node.hpp @@ -79,6 +79,8 @@ class QueryNode { virtual void Serialize(Serializer &serializer) const; static unique_ptr Deserialize(Deserializer &deserializer); + //! Throws a SerializationException if a deserialized query node does not have a valid shape + static void VerifyDeserialized(const unique_ptr &node); protected: //! Copy base QueryNode properties from another expression to this one, diff --git a/src/duckdb/src/include/duckdb/parser/query_node/statement_node.hpp b/src/duckdb/src/include/duckdb/parser/query_node/statement_node.hpp index 26db46a58..67b0044b8 100644 --- a/src/duckdb/src/include/duckdb/parser/query_node/statement_node.hpp +++ b/src/duckdb/src/include/duckdb/parser/query_node/statement_node.hpp @@ -20,8 +20,12 @@ class StatementNode : public QueryNode { public: explicit StatementNode(SQLStatement &stmt_p); + //! A node that owns the statement it represents (e.g. reconstructed by the SQL exporter) + explicit StatementNode(unique_ptr stmt_p); SQLStatement &stmt; + //! Set when this node owns the referenced statement + unique_ptr owned_statement; public: //! Convert the query node to a string diff --git a/src/duckdb/src/include/duckdb/parser/simplified_token.hpp b/src/duckdb/src/include/duckdb/parser/simplified_token.hpp index ff073a3cf..2c98f805a 100644 --- a/src/duckdb/src/include/duckdb/parser/simplified_token.hpp +++ b/src/duckdb/src/include/duckdb/parser/simplified_token.hpp @@ -36,12 +36,13 @@ enum class KeywordCategory : uint8_t { KEYWORD_UNRESERVED, KEYWORD_TYPE_FUNC, KEYWORD_COL_NAME, - KEYWORD_NONE + KEYWORD_NONE, + KEYWORD_TYPE_NAME }; struct ParserKeyword { string name; - KeywordCategory category; + string category; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/statement/explain_statement.hpp b/src/duckdb/src/include/duckdb/parser/statement/explain_statement.hpp index fcc9423de..a9a8a1d44 100644 --- a/src/duckdb/src/include/duckdb/parser/statement/explain_statement.hpp +++ b/src/duckdb/src/include/duckdb/parser/statement/explain_statement.hpp @@ -13,7 +13,7 @@ namespace duckdb { -enum class ExplainType : uint8_t { EXPLAIN_STANDARD, EXPLAIN_ANALYZE }; +enum class ExplainType : uint8_t { EXPLAIN_STANDARD, EXPLAIN_ANALYZE, EXPLAIN_SQL }; class ExplainStatement : public SQLStatement { public: @@ -25,6 +25,8 @@ class ExplainStatement : public SQLStatement { unique_ptr stmt; ExplainType explain_type; + //! Internal verification may request an empty result for unsupported SQL export. + bool allow_unsupported_sql = false; ProfilerPrintFormat format = ProfilerPrintFormat::Default(); protected: diff --git a/src/duckdb/src/include/duckdb/parser/statement/list.hpp b/src/duckdb/src/include/duckdb/parser/statement/list.hpp index d0a2b9b06..ef2bb5f57 100644 --- a/src/duckdb/src/include/duckdb/parser/statement/list.hpp +++ b/src/duckdb/src/include/duckdb/parser/statement/list.hpp @@ -9,6 +9,7 @@ #include "duckdb/parser/statement/delete_statement.hpp" #include "duckdb/parser/statement/detach_statement.hpp" #include "duckdb/parser/statement/disconnect_statement.hpp" +#include "duckdb/parser/statement/passthrough_statement.hpp" #include "duckdb/parser/statement/drop_statement.hpp" #include "duckdb/parser/statement/execute_statement.hpp" #include "duckdb/parser/statement/explain_statement.hpp" diff --git a/src/duckdb/src/include/duckdb/parser/statement/passthrough_statement.hpp b/src/duckdb/src/include/duckdb/parser/statement/passthrough_statement.hpp new file mode 100644 index 000000000..6b7e64dfe --- /dev/null +++ b/src/duckdb/src/include/duckdb/parser/statement/passthrough_statement.hpp @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/parser/statement/passthrough_statement.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/parser/sql_statement.hpp" + +namespace duckdb { + +//! Unparsed SQL forwarded as-is to the CONNECT-ed database; the text lives in `query` +class PassthroughStatement : public SQLStatement { +public: + static constexpr const StatementType TYPE = StatementType::PASSTHROUGH_STATEMENT; + +public: + PassthroughStatement(); + +protected: + PassthroughStatement(const PassthroughStatement &other) = default; + +public: + unique_ptr Copy() const override; + string ToString() const override; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/tableref.hpp b/src/duckdb/src/include/duckdb/parser/tableref.hpp index b09320f67..00b3e99eb 100644 --- a/src/duckdb/src/include/duckdb/parser/tableref.hpp +++ b/src/duckdb/src/include/duckdb/parser/tableref.hpp @@ -55,6 +55,8 @@ class TableRef { virtual void Serialize(Serializer &serializer) const; static unique_ptr Deserialize(Deserializer &deserializer); + //! Throws a SerializationException if a deserialized table reference does not have a valid shape + void VerifyDeserialized() const; public: template diff --git a/src/duckdb/src/include/duckdb/parser/tableref/table_function_ref.hpp b/src/duckdb/src/include/duckdb/parser/tableref/table_function_ref.hpp index e20a8eb49..41d488858 100644 --- a/src/duckdb/src/include/duckdb/parser/tableref/table_function_ref.hpp +++ b/src/duckdb/src/include/duckdb/parser/tableref/table_function_ref.hpp @@ -14,6 +14,8 @@ #include "duckdb/common/enums/ordinality_request_type.hpp" namespace duckdb { +struct TableFunctionInfo; + //! Represents a Table producing function class TableFunctionRef : public TableRef { public: @@ -24,6 +26,9 @@ class TableFunctionRef : public TableRef { unique_ptr function; + //! Process-local input for this invocation, retained when copying the reference + shared_ptr bind_info; + // if the function takes a subquery as argument its in here unique_ptr subquery; @@ -38,6 +43,7 @@ class TableFunctionRef : public TableRef { unique_ptr Copy() override; //! Deserializes a blob back into a BaseTableRef + const unique_ptr &SerializableFunction() const; void Serialize(Serializer &serializer) const override; static unique_ptr Deserialize(Deserializer &source); }; diff --git a/src/duckdb/src/include/duckdb/parser/token_iterator.hpp b/src/duckdb/src/include/duckdb/parser/token_iterator.hpp index 74f732f52..8e72ee327 100644 --- a/src/duckdb/src/include/duckdb/parser/token_iterator.hpp +++ b/src/duckdb/src/include/duckdb/parser/token_iterator.hpp @@ -34,6 +34,12 @@ class TokenIterator { DUCKDB_API idx_t EndOffset() const; DUCKDB_API optional_ptr Current() const; + LiteralInfo CurrentLiteralInfo(const GrammarLiteralTable &table) { + if (position >= tokens.size()) { + return LiteralInfo(); + } + return tokens[position].GetLiteralInfo(table); + } DUCKDB_API const MatcherToken &Previous() const; DUCKDB_API const MatcherToken &GetToken(idx_t index) const; diff --git a/src/duckdb/src/include/duckdb/planner/binder.hpp b/src/duckdb/src/include/duckdb/planner/binder.hpp index 96d366208..e930e0fe9 100644 --- a/src/duckdb/src/include/duckdb/planner/binder.hpp +++ b/src/duckdb/src/include/duckdb/planner/binder.hpp @@ -20,6 +20,7 @@ #include "duckdb/parser/column_definition.hpp" #include "duckdb/parser/tableref/match_recognize_ref.hpp" #include "duckdb/parser/query_node.hpp" +#include "duckdb/parser/tableref/table_function_ref.hpp" #include "duckdb/parser/result_modifier.hpp" #include "duckdb/parser/tableref/delimgetref.hpp" #include "duckdb/parser/tokens.hpp" @@ -45,6 +46,7 @@ class BoundResultModifier; class BoundSelectNode; class ClientContext; class ExpressionBinder; +struct ExternalResourceOptions; class LimitModifier; class OrderBinder; class TableCatalogEntry; @@ -59,7 +61,9 @@ class LogicalVacuum; class ColumnList; class ExternalDependency; +class FunctionSignature; class TableFunction; +class BoundTableFunction; class TableStorageInfo; class BoundConstraint; class AtClause; @@ -239,6 +243,12 @@ class Binder : public enable_shared_from_this { unordered_map recursive_ctes; +public: + //! Whether the expression is COUNT(tbl.*) + static bool IsQualifiedCountStar(const ParsedExpression &expr); + //! Rewrites COUNT(tbl.*) into a COUNT that skips the rows NULL-extended by an outer join, or returns nullptr + unique_ptr TryRewriteQualifiedCountStar(const ParsedExpression &expr); + public: DUCKDB_API BoundStatement Bind(SQLStatement &statement); DUCKDB_API BoundStatement Bind(QueryNode &node); @@ -302,9 +312,6 @@ class Binder : public enable_shared_from_this { SchemaCatalogEntry &BindCreateFunctionInfo(CreateInfo &info); SchemaCatalogEntry &BindCreateTriggerInfo(CreateTriggerInfo &info); - //! Check usage, and cast named parameters to their types - static void BindNamedParameters(named_parameter_type_map_t &types, named_parameter_map_t &values, - QueryErrorContext &error_context, const Identifier &func_name); unique_ptr BindPragma(PragmaInfo &info, QueryErrorContext error_context); BoundStatement Bind(TableRef &ref); @@ -322,6 +329,7 @@ class Binder : public enable_shared_from_this { //! Add the view to the set of currently bound views - used for detecting recursive view definitions void AddBoundView(ViewCatalogEntry &view); + void AddBoundTableMacro(CatalogEntry &table_macro); void BeginSubqueryBind(Binder &parent, ExpressionBinder &binder); //! The innermost enclosing scope @@ -401,7 +409,8 @@ class Binder : public enable_shared_from_this { static string ReplaceColumnsAlias(const string &alias, const string &column_name, optional_ptr regex); - unique_ptr UnionOperators(vector> nodes); + unique_ptr UnionOperators(vector> nodes, idx_t column_count = 1, + TableIndex table_index = TableIndex()); void SetSearchPath(Catalog &catalog, const Identifier &schema); @@ -427,6 +436,8 @@ class Binder : public enable_shared_from_this { bool inside_subquery = false; //! The set of bound views reference_set_t bound_views; + //! The table macros that are being expanded by this binder + reference_set_t bound_table_macros; //! Used to retrieve CatalogEntry's CatalogEntryRetriever entry_retriever; //! Unnamed subquery index @@ -485,6 +496,13 @@ class Binder : public enable_shared_from_this { BoundStatement Bind(ConnectStatement &stmt); BoundStatement Bind(DisconnectStatement &stmt); BoundStatement Bind(ExternalResourceStatement &stmt); + //! Bind + constant-fold a single EXTERNAL RESOURCE expression (a create param, or the REGISTER handle). + Value BindExternalResourceValue(unique_ptr &expr); + //! Bind + constant-fold the create params of an EXTERNAL RESOURCE statement or clause. + void BindExternalResourceParams(case_insensitive_map_t> &parsed_params, + unordered_map ¶ms); + //! Bind the `ATTACH/CONNECT TO EXTERNAL RESOURCE ...` clause. Shared by ATTACH and CONNECT. + void BindExternalResource(ExternalResourceOptions &external_resource); //! Resolves the base table for DROP TRIGGER, stamps catalog/schema onto stmt.info, //! and registers the catalog modification. IF EXISTS only guards the trigger, not the table. @@ -582,13 +600,14 @@ class Binder : public enable_shared_from_this { unique_ptr BindAtClause(optional_ptr at_clause); bool BindTableFunctionParameters(TableFunctionCatalogEntry &table_function, - vector> &expressions, vector &arguments, - vector ¶meters, named_parameter_map_t &named_parameters, - BoundStatement &subquery, ErrorData &error); + vector> &expressions, + vector> &positional_arguments, + vector>> &named_arguments, + BoundStatement &subquery, bool &table_in_out, ErrorData &error); void BindTableInTableOutFunction(vector> &expressions, BoundStatement &subquery); BoundStatement BindTableFunction(TableFunction &function, vector parameters); - BoundStatement BindTableFunctionInternal(TableFunction &table_function, const TableFunctionRef &ref, - vector parameters, named_parameter_map_t named_parameters, + BoundStatement BindTableFunctionInternal(BoundTableFunction &table_function, const TableFunctionRef &ref, + vector parameters, named_argument_map_t named_parameters, vector input_table_types, vector input_table_names, optional_ptr> input_plan); @@ -597,7 +616,8 @@ class Binder : public enable_shared_from_this { BoundStatement BindCopyTo(CopyStatement &stmt, const CopyFunction &function, CopyToType copy_to_type); BoundStatement BindCopyFrom(CopyStatement &stmt, const CopyFunction &function); - void BindCopyOptions(CopyInfo &info); + void BindCopyFormat(CopyInfo &info); + void BindCopyOptions(CopyInfo &info, const CopyFunction &function); identifier_map_t GetFullCopyOptionsList(const CopyFunction &function, CopyOptionMode mode); void PrepareModifiers(OrderBinder &order_binder, QueryNode &statement, BoundQueryNode &result); diff --git a/src/duckdb/src/include/duckdb/planner/bound_expression_sql_exporter.hpp b/src/duckdb/src/include/duckdb/planner/bound_expression_sql_exporter.hpp index 19cacf637..1ec341255 100644 --- a/src/duckdb/src/include/duckdb/planner/bound_expression_sql_exporter.hpp +++ b/src/duckdb/src/include/duckdb/planner/bound_expression_sql_exporter.hpp @@ -12,6 +12,7 @@ #include "duckdb/common/optional.hpp" #include "duckdb/common/types.hpp" #include "duckdb/parser/parsed_expression.hpp" +#include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/planner/column_binding.hpp" #include "duckdb/planner/logical_plan_verification_result.hpp" @@ -20,10 +21,17 @@ namespace duckdb { class Expression; +class ClientContext; +class BoundWindowExpression; +class BoundAggregateExpression; +class BoundUnnestExpression; struct ResolvedSQLColumnReference { vector names; + //! Type represented by the exported SQL expression. LogicalType type; + //! Optimizer-selected type accepted from the bound plan, when different. + optional optimizer_type = {}; }; using BoundExpressionSQLBindingResolver = @@ -31,6 +39,10 @@ using BoundExpressionSQLBindingResolver = struct BoundExpressionSQLExportContext { BoundExpressionSQLBindingResolver resolve_binding; + //! The context that will bind the exported expression, when known + optional_ptr client_context; + //! Internal pruning expressions may be discarded only while exporting their complete logical plan. + bool discard_optimizer_metadata = false; }; //! Reconstructs SQL from bound/optimized logical expressions before physical planning lowers their structure. @@ -43,6 +55,19 @@ class BoundExpressionSQLExporter { DUCKDB_API static LogicalPlanVerificationResult> ExportAtPath(const Expression &expression, const BoundExpressionSQLExportContext &context, const LogicalPlanVerificationPath &path); + + //! Only for root expressions placed by their owning logical relation. + DUCKDB_API static LogicalPlanVerificationResult> + ExportAggregateCallAtPath(const BoundAggregateExpression &expression, + const BoundExpressionSQLExportContext &context, const LogicalPlanVerificationPath &path); + + DUCKDB_API static LogicalPlanVerificationResult> + ExportWindowAtPath(const BoundWindowExpression &expression, const BoundExpressionSQLExportContext &context, + const LogicalPlanVerificationPath &path); + + DUCKDB_API static LogicalPlanVerificationResult> + ExportUnnestAtPath(const BoundUnnestExpression &expression, const BoundExpressionSQLExportContext &context, + const LogicalPlanVerificationPath &path); }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/planner/expression.hpp b/src/duckdb/src/include/duckdb/planner/expression.hpp index 757053990..9fe3c0fa0 100644 --- a/src/duckdb/src/include/duckdb/planner/expression.hpp +++ b/src/duckdb/src/include/duckdb/planner/expression.hpp @@ -33,10 +33,13 @@ class Expression : public BaseExpression { const LogicalType &GetReturnType() const { return return_type; } - void SetReturnType(LogicalType type) { + virtual void SetReturnType(LogicalType type) { return_type = std::move(type); } + //! Preserve the SQL result type when replacing an expression with an equivalent value. + static unique_ptr PreserveReturnType(const LogicalType &type, unique_ptr replacement); + const unique_ptr &GetVerificationStats() const { return verification_stats; } @@ -80,7 +83,7 @@ class Expression : public BaseExpression { type = other.type; expression_class = other.expression_class; alias = other.alias; - return_type = other.return_type; + SetReturnType(other.return_type); query_location = other.query_location; } }; diff --git a/src/duckdb/src/include/duckdb/planner/expression/bound_conjunction_expression.hpp b/src/duckdb/src/include/duckdb/planner/expression/bound_conjunction_expression.hpp index c99a7d153..b0030b1aa 100644 --- a/src/duckdb/src/include/duckdb/planner/expression/bound_conjunction_expression.hpp +++ b/src/duckdb/src/include/duckdb/planner/expression/bound_conjunction_expression.hpp @@ -20,6 +20,9 @@ class BoundConjunctionExpression : public Expression { explicit BoundConjunctionExpression(ExpressionType type); BoundConjunctionExpression(ExpressionType type, unique_ptr left, unique_ptr right); + //! Returns a lone child as is + static unique_ptr Create(ExpressionType type, vector> children); + public: const vector> &GetChildren() const { return children; diff --git a/src/duckdb/src/include/duckdb/planner/expression/bound_constant_expression.hpp b/src/duckdb/src/include/duckdb/planner/expression/bound_constant_expression.hpp index 270f98d68..28342b237 100644 --- a/src/duckdb/src/include/duckdb/planner/expression/bound_constant_expression.hpp +++ b/src/duckdb/src/include/duckdb/planner/expression/bound_constant_expression.hpp @@ -24,9 +24,9 @@ class BoundConstantExpression : public Expression { const Value &GetValue() const { return value; } - Value &GetValueMutable() { - return value; - } + void SetValue(Value value_p); + Value TakeValue(); + void SetReturnType(LogicalType type) override; string ToString() const override; bool Equals(const BaseExpression &other) const override; diff --git a/src/duckdb/src/include/duckdb/planner/expression/bound_window_expression.hpp b/src/duckdb/src/include/duckdb/planner/expression/bound_window_expression.hpp index 0d437ac34..2757cf2fd 100644 --- a/src/duckdb/src/include/duckdb/planner/expression/bound_window_expression.hpp +++ b/src/duckdb/src/include/duckdb/planner/expression/bound_window_expression.hpp @@ -11,6 +11,8 @@ #include "duckdb/parser/expression/window_expression.hpp" #include "duckdb/function/function.hpp" #include "duckdb/planner/expression.hpp" +#include "duckdb/planner/expression/window_range_info.hpp" +#include "duckdb/parser/parsed_expression.hpp" #include "duckdb/planner/bound_result_modifier.hpp" namespace duckdb { @@ -111,6 +113,35 @@ class BoundWindowExpression : public Expression { unique_ptr &EndExprMutable() { return end_expr; } + void RetainSQLRange(optional_ptr start, optional_ptr end, + const LogicalType &order_type); + const unique_ptr &SQLRangeStart() const { + return sql_range_start; + } + const unique_ptr &SQLRangeEnd() const { + return sql_range_end; + } + const LogicalType &SQLRangeOrderType() const { + return sql_range_order_type; + } + const unique_ptr &SQLRangeStartBoundary() const { + return sql_range_start_boundary; + } + unique_ptr &SQLRangeStartBoundaryMutable() { + return sql_range_start_boundary; + } + const unique_ptr &SQLRangeEndBoundary() const { + return sql_range_end_boundary; + } + unique_ptr &SQLRangeEndBoundaryMutable() { + return sql_range_end_boundary; + } + const vector &SQLRangeOrderCasts() const { + return sql_range_order_casts; + } + vector &SQLRangeOrderCastsMutable() { + return sql_range_order_casts; + } const vector &ArgOrders() const { return arg_orders; } @@ -189,6 +220,15 @@ class BoundWindowExpression : public Expression { unique_ptr start_expr; unique_ptr end_expr; + //! Literal SQL offsets before endpoint arithmetic; these are not execution children. + unique_ptr sql_range_start; + unique_ptr sql_range_end; + LogicalType sql_range_order_type = LogicalType::INVALID; + + unique_ptr sql_range_start_boundary; + unique_ptr sql_range_end_boundary; + vector sql_range_order_casts; + //! The set of argument ordering clauses //! These are distinct from the frame ordering clauses e.g., the "x" in //! FIRST_VALUE(a ORDER BY x) OVER (PARTITION BY p ORDER BY s) diff --git a/src/duckdb/src/include/duckdb/planner/expression/window_range_info.hpp b/src/duckdb/src/include/duckdb/planner/expression/window_range_info.hpp new file mode 100644 index 000000000..7efff37f6 --- /dev/null +++ b/src/duckdb/src/include/duckdb/planner/expression/window_range_info.hpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/planner/expression/window_range_info.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/types.hpp" +#include "duckdb/common/optional_ptr.hpp" +#include "duckdb/parser/qualified_name.hpp" +#include "duckdb/parser/expression/window_expression.hpp" + +namespace duckdb { +class Expression; + +//! A coercion inserted while binding a RANGE boundary, without executable bind data. +struct WindowRangeCast { + LogicalType source_type; + LogicalType target_type; + bool try_cast; + bool default_cast; + + void Serialize(Serializer &serializer) const; + static WindowRangeCast Deserialize(Deserializer &deserializer); + + //! Records the casts the binder wrapped around an operand of the range arithmetic. The operand is the expression + //! still in place, or a copy of a constant operand the function binder may have folded - the chain then ends in + //! that constant + static bool Capture(const Expression &expression, const Expression &operand, vector &casts); + static optional_ptr Match(const Expression &expression, const vector &casts); +}; + +//! Identifies the binder-produced call and its live operands; it owns no column bindings or expressions. +struct WindowRangeBoundary { + WindowBoundary boundary = WindowBoundary::INVALID; + OrderType direction = OrderType::INVALID; + QualifiedName function_name; + vector arguments; + LogicalType return_type; + LogicalType order_type; + LogicalType offset_type; + vector order_casts; + vector result_casts; + + void Serialize(Serializer &serializer) const; + static unique_ptr Deserialize(Deserializer &deserializer); + + static unique_ptr Capture(const Expression &expression, const Expression &order, + const Expression &offset); + optional_ptr Match(const Expression &expression, const Expression &order) const; +}; +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/planner/expression_binder.hpp b/src/duckdb/src/include/duckdb/planner/expression_binder.hpp index 28ab414a8..2745d3e32 100644 --- a/src/duckdb/src/include/duckdb/planner/expression_binder.hpp +++ b/src/duckdb/src/include/duckdb/planner/expression_binder.hpp @@ -112,6 +112,8 @@ class ExpressionBinder { optional_ptr macro_binding; optional_ptr> lambda_bindings; + //! The number of (outer) lambda bindings whose parameters can not be referenced, e.g. inside an aggregate + idx_t inaccessible_lambda_binding_count = 0; public: unique_ptr Bind(unique_ptr &expr, optional_ptr result_type = nullptr, @@ -233,6 +235,9 @@ class ExpressionBinder { //! Returns true if the function name is an alias for the UNNEST function static bool IsUnnestFunction(const Identifier &function_name); + //! Continues the expression depth of the binder this one binds a nested expression for + void ContinueStackDepth(const ExpressionBinder &parent); + private: //! Current stack depth idx_t stack_depth = DConstants::INVALID_INDEX; diff --git a/src/duckdb/src/include/duckdb/planner/expression_binder/table_function_binder.hpp b/src/duckdb/src/include/duckdb/planner/expression_binder/table_function_binder.hpp index a86c2ed45..91b9cd55b 100644 --- a/src/duckdb/src/include/duckdb/planner/expression_binder/table_function_binder.hpp +++ b/src/duckdb/src/include/duckdb/planner/expression_binder/table_function_binder.hpp @@ -15,16 +15,15 @@ namespace duckdb { //! The table function binder can bind standard table function parameters (i.e., non-table-in-out functions) class TableFunctionBinder : public ExpressionBinder { public: - TableFunctionBinder(Binder &binder, ClientContext &context, string table_function_name = string(), - string clause = "Table function"); + //! How an identifier that is not a lambda parameter or lateral column is bound. FOLLOW_SETTING: resolve SQL value + //! functions (e.g. user), then convert to a string as table_function_identifier_conversion allows. ALLOW: the + //! identifier is a name (e.g. a COPY column list): bind it as the string it spells, without warning and without + //! resolving SQL value functions. + enum class IdentifierConversionPolicy : uint8_t { FOLLOW_SETTING, ALLOW }; -public: - void DisableSQLValueFunctions() { - accept_sql_value_functions = false; - } - void EnableSQLValueFunctions() { - accept_sql_value_functions = true; - } + TableFunctionBinder( + Binder &binder, ClientContext &context, string table_function_name = string(), string clause = "Table function", + IdentifierConversionPolicy identifier_conversion_policy = IdentifierConversionPolicy::FOLLOW_SETTING); protected: BindResult BindLambdaReference(LambdaRefExpression &expr, idx_t depth); @@ -36,8 +35,7 @@ class TableFunctionBinder : public ExpressionBinder { private: string table_function_name; string clause; - //! Whether sql_value_functions (GetSQLValueFunctionName) are considered when binding column refs - bool accept_sql_value_functions = true; + IdentifierConversionPolicy identifier_conversion_policy; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/planner/expression_iterator.hpp b/src/duckdb/src/include/duckdb/planner/expression_iterator.hpp index 52599a1c8..ed7be0786 100644 --- a/src/duckdb/src/include/duckdb/planner/expression_iterator.hpp +++ b/src/duckdb/src/include/duckdb/planner/expression_iterator.hpp @@ -32,6 +32,8 @@ class ExpressionIterator { const std::function &callback); static void VisitExpressionClassMutable(unique_ptr &expr, ExpressionClass expr_class, const std::function &child)> &callback); + static unique_ptr ReplaceExpression(const Expression &expr, const Expression &target, + const Expression &replacement); template static void VisitExpressionMutable(unique_ptr &expr, diff --git a/src/duckdb/src/include/duckdb/planner/expression_nullability.hpp b/src/duckdb/src/include/duckdb/planner/expression_nullability.hpp index e94c413d1..8cbe1191c 100644 --- a/src/duckdb/src/include/duckdb/planner/expression_nullability.hpp +++ b/src/duckdb/src/include/duckdb/planner/expression_nullability.hpp @@ -12,13 +12,23 @@ #include "duckdb/common/table_index.hpp" #include "duckdb/common/vector.hpp" +#include + namespace duckdb { class ClientContext; +class BoundColumnRefExpression; class Expression; class LogicalCTE; class LogicalOperator; +//! Returns whether an expression becomes NULL when the provided column reference becomes NULL. +bool ExpressionBecomesNull(const Expression &expr, + const std::function &column_becomes_null); + +//! Returns whether the filter cannot be TRUE when expr is NULL. +bool FilterRejectsNull(const Expression &filter, const Expression &expr); + //! Conservatively proves that an expression cannot be NULL at a logical operator's output. class NotNullExpressionAnalyzer { public: diff --git a/src/duckdb/src/include/duckdb/planner/filter/bloom_filter.hpp b/src/duckdb/src/include/duckdb/planner/filter/bloom_filter.hpp index bdd01b0fd..ef1494222 100644 --- a/src/duckdb/src/include/duckdb/planner/filter/bloom_filter.hpp +++ b/src/duckdb/src/include/duckdb/planner/filter/bloom_filter.hpp @@ -20,7 +20,7 @@ class BloomFilter; //! DEPRECATED - only preserved for backwards-compatible expression conversion class LegacyBFTableFilter final : public TableFilter { private: - optional_ptr filter; + shared_ptr filter; bool filters_null_values; string key_column_name; @@ -30,9 +30,9 @@ class LegacyBFTableFilter final : public TableFilter { static constexpr auto TYPE = TableFilterType::LEGACY_BLOOM_FILTER; public: - explicit LegacyBFTableFilter(optional_ptr filter_p, const bool filters_null_values_p, + explicit LegacyBFTableFilter(shared_ptr filter_p, const bool filters_null_values_p, const string &key_column_name_p, const LogicalType &key_type_p) - : TableFilter(TYPE), filter(filter_p), filters_null_values(filters_null_values_p), + : TableFilter(TYPE), filter(std::move(filter_p)), filters_null_values(filters_null_values_p), key_column_name(key_column_name_p), key_type(key_type_p) { } diff --git a/src/duckdb/src/include/duckdb/planner/filter/expression_filter.hpp b/src/duckdb/src/include/duckdb/planner/filter/expression_filter.hpp index 25efc85f1..5a0c7acb6 100644 --- a/src/duckdb/src/include/duckdb/planner/filter/expression_filter.hpp +++ b/src/duckdb/src/include/duckdb/planner/filter/expression_filter.hpp @@ -50,6 +50,12 @@ class ExpressionFilter : public TableFilter { //! Build an IS NULL/IS NOT NULL expression over a single-column filter subject. static unique_ptr CreateNullCheckExpression(unique_ptr column, ExpressionType expression_type); + //! Build a single-column filter comparing against a constant. + static unique_ptr CreateComparisonFilter(ExpressionType comparison_type, Value constant); + static bool IsSimpleFilterColumnRef(const Expression &expr); + //! Flips comparison_type for a constant on the left + static optional_ptr + TryGetColumnConstantComparison(const BoundFunctionExpression &comparison, ExpressionType &comparison_type); //! Enhanced CheckStatistics that recognizes standard expression patterns static FilterPropagateResult CheckExpressionStatistics(const Expression &expr, const BaseStatistics &stats); @@ -71,6 +77,8 @@ class ExpressionFilter : public TableFilter { static bool IsOptionalExpression(const Expression &expr); //! Check if the root of an expression tree is an optional filter wrapper static bool IsRootOptionalExpression(const Expression &expr); + //! Child of a root optional filter wrapper + static optional_ptr GetOptionalFilterChild(const Expression &expr); //! Check if a table filter tree is entirely optional filter semantics static bool IsOptionalFilter(const TableFilter &filter); //! Check if the root of a table filter tree is an optional filter wrapper @@ -80,6 +88,8 @@ class ExpressionFilter : public TableFilter { //! If this is an optional/selectivity-optional wrapper around a root dynamic filter, //! return the shared dynamic filter state. static shared_ptr GetRootOptionalDynamicFilterData(const TableFilter &filter); + //! Dynamic filter data under optional wrappers or ANDs + static shared_ptr GetOptionalDynamicFilterData(const TableFilter &filter); FilterPropagateResult CheckStatistics(const BaseStatistics &stats) const; FilterPropagateResult CheckStatistics(ClientContext &context, const BaseStatistics &stats) const; diff --git a/src/duckdb/src/include/duckdb/planner/filter/table_filter_function_helpers.hpp b/src/duckdb/src/include/duckdb/planner/filter/table_filter_function_helpers.hpp index ed1cc23e5..88b12ad54 100644 --- a/src/duckdb/src/include/duckdb/planner/filter/table_filter_function_helpers.hpp +++ b/src/duckdb/src/include/duckdb/planner/filter/table_filter_function_helpers.hpp @@ -72,6 +72,18 @@ inline idx_t FillSelectionInversion(idx_t count, const SelectionVector &true_sel return SelectionVector::Inverted(true_sel, *false_sel, true_count, count); } +inline SelectionVector &GetFilterResultSelection(idx_t count, optional_ptr input_sel, + optional_ptr true_sel, SelectionVector &scratch_sel) { + if (true_sel && (!input_sel || true_sel.get() != input_sel.get())) { + return *true_sel; + } + // Preserve the input selection until TranslateSelection has mapped the local matches. + if (scratch_sel.Capacity() < count) { + scratch_sel.Initialize(count); + } + return scratch_sel; +} + inline idx_t TranslateSelection(idx_t count, optional_ptr input_sel, const SelectionVector &local_true_sel, idx_t local_true_count, optional_ptr true_sel, optional_ptr false_sel) { diff --git a/src/duckdb/src/include/duckdb/planner/filter/table_filter_functions.hpp b/src/duckdb/src/include/duckdb/planner/filter/table_filter_functions.hpp index 0c2e3c147..aa07f4491 100644 --- a/src/duckdb/src/include/duckdb/planner/filter/table_filter_functions.hpp +++ b/src/duckdb/src/include/duckdb/planner/filter/table_filter_functions.hpp @@ -9,6 +9,7 @@ #pragma once #include "duckdb/common/common.hpp" +#include "duckdb/common/mutex.hpp" #include "duckdb/common/optional_ptr.hpp" #include "duckdb/common/enums/expression_type.hpp" #include "duckdb/common/enums/filter_propagate_result.hpp" @@ -87,44 +88,70 @@ struct TableFilterFunctions { }; //! Runtime bloom-filter state used by join pushdown and internal tablefilter functions. +//! A filter is first built (Initialize, then InsertHashes) and afterwards sealed. Only a sealed filter +//! describes a complete key set, so only a sealed filter may be looked up or handed to a scan. class BloomFilter { public: BloomFilter() = default; void Initialize(ClientContext &context_p, idx_t number_of_rows); - void InsertHashes(const Vector &hashes_v) const; + //! VectorOperations::Hash writes at the source row positions, so the hashes to insert are the ones sel + //! points at rather than the first count entries + void InsertHashes(const Vector &hashes_v, const SelectionVector &sel, idx_t count); + //! Inserts every hash the vector carries + void InsertHashes(const Vector &hashes_v); idx_t LookupHashes(const Vector &hashes_v, SelectionVector &result_sel, idx_t count) const; //! result_sel contains local positions into sel rather than source row ids. idx_t LookupHashes(const Vector &hashes_v, const SelectionVector &sel, SelectionVector &result_sel, idx_t count) const; - void InsertOne(hash_t hash) const; + void InsertOne(hash_t hash); bool LookupOne(hash_t hash) const; - void Merge(const BloomFilter &other); - void Reset(); + //! Folds the filter down to the smallest size that still meets the target false positive rate, releases the + //! memory that folding freed up, and marks the filter complete. + void Seal(ClientContext &context); bool IsInitialized() const { return initialized; } + bool IsSealed() const { + return sealed; + } + //! Whether the sealed filter still rejects enough for pushing it into a scan to pay off + bool IsUseful() const { + D_ASSERT(sealed); + return useful; + } static idx_t GetNumberOfSectors(idx_t number_of_rows); +private: + //! Halves the filter until only new_num_sectors sectors remain. Bit-exact with a filter that was built at + //! the smaller size from the start, because the sector index is the low bits of the hash and the bits set + //! within a sector are taken from the high bits. + void Fold(idx_t new_num_sectors); + //! Fraction of bits that are set, over the sectors currently in use + double Density() const; + private: idx_t num_sectors; uint64_t bitmask; // num_sectors - 1 -> used to get the sector offset bool initialized = false; + bool sealed = false; + bool useful = false; AllocatedData buf_; uint64_t *bf; }; //! FunctionData for bloom filter internal function struct BloomFilterFunctionData : public FunctionData { - BloomFilterFunctionData(optional_ptr filter_p, bool filters_null_values_p, + BloomFilterFunctionData(shared_ptr filter_p, bool filters_null_values_p, const string &key_column_name_p, const LogicalType &key_type_p, float selectivity_threshold_p, idx_t n_vectors_to_check_p); - optional_ptr filter; + //! Published filters are shared and immutable: the hash table may drop its own reference at any time + shared_ptr filter; bool filters_null_values; string key_column_name; LogicalType key_type; @@ -193,9 +220,9 @@ struct DynamicFilterData { public: DynamicFilterData(ExpressionType comparison_type, Value constant); - mutex lock; + mutable annotated_shared_mutex lock; ExpressionType comparison_type; - Value constant; + Value constant DUCKDB_GUARDED_BY(lock); atomic initialized = {false}; unique_ptr ToExpression(const Expression &column) const; diff --git a/src/duckdb/src/include/duckdb/planner/logical_operator.hpp b/src/duckdb/src/include/duckdb/planner/logical_operator.hpp index 239b08425..a65bdc553 100644 --- a/src/duckdb/src/include/duckdb/planner/logical_operator.hpp +++ b/src/duckdb/src/include/duckdb/planner/logical_operator.hpp @@ -23,6 +23,12 @@ namespace duckdb { class LogicalPlanVerifier; +struct LogicalPlanSQLExportRelation; +struct LogicalPlanVerificationPath; +template +class LogicalPlanVerificationResult; +class LogicalPlanSQLExportContext; +using LogicalPlanSQLExportResult = LogicalPlanVerificationResult; //! LogicalOperator is the base class of the logical operators present in the //! logical query tree @@ -47,6 +53,8 @@ class LogicalOperator { bool has_estimated_cardinality; public: + virtual LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path); virtual vector GetColumnBindings(); virtual TableIndex GetRootIndex(); static string ColumnBindingsToString(const vector &bindings); diff --git a/src/duckdb/src/include/duckdb/planner/logical_plan_sql_export_context.hpp b/src/duckdb/src/include/duckdb/planner/logical_plan_sql_export_context.hpp new file mode 100644 index 000000000..17721f08f --- /dev/null +++ b/src/duckdb/src/include/duckdb/planner/logical_plan_sql_export_context.hpp @@ -0,0 +1,109 @@ +#pragma once + +#include "duckdb/planner/logical_plan_sql_exporter.hpp" +#include "duckdb/parser/query_node/select_node.hpp" +#include "duckdb/common/reference_map.hpp" + +namespace duckdb { +class Expression; +class BoundExpressionSQLExportState; +class LogicalOperator; +class LogicalComparisonJoin; +struct LogicalExtensionOperator; +class ClientContext; + +struct LogicalPlanSQLExportedChild { + LogicalPlanSQLExportRelation relation; + Identifier relation_alias; +}; + +struct LogicalPlanSQLExportSource { + reference op; + Identifier name; + LogicalPlanSQLExportRelation relation; +}; + +class LogicalPlanSQLExportContext { +public: + explicit LogicalPlanSQLExportContext(ClientContext &context_p); + LogicalPlanSQLExportResult Export(LogicalOperator &op, const LogicalPlanVerificationPath &path); + +public: + ClientContext &GetClientContext() const { + return context; + } + + Identifier NextRelationAlias(const Identifier &preferred = Identifier()); + //! Reserve parsed relation names before introducing generated CTEs around a query. + void ReserveRelationNames(QueryNode &query); + //! An unqualified relation reference a generated source name would rebind; empty when there is none. + optional FindCapturedSourceReference(QueryNode &query, const identifier_set_t &source_names) const; + //! The effective source boundary, including enclosing export scopes. + optional_ptr FindSource(const LogicalOperator &op) const; + LogicalPlanVerificationResult ExportChild(LogicalOperator &child, + const LogicalPlanVerificationPath &path); + LogicalPlanVerificationResult + ExportChild(LogicalOperator &child, const LogicalPlanVerificationPath &path, + const vector &sources); + //! Export a plan root inside the given source scope. A RETURNING root exports as its statement, + //! aliasing the RETURNING items with the given output names, or positionally without them. + LogicalPlanVerificationResult + ExportRoot(LogicalOperator &root, const LogicalPlanVerificationPath &path, + const vector &sources, const optional> &output_names); + LogicalPlanVerificationResult> + ExportExpression(const LogicalOperator &op, const vector> &expressions, + idx_t expression_ordinal, const BoundExpressionSQLExportContext &expression_context, + const LogicalPlanVerificationPath &path, + optional_ptr composition = nullptr); + unique_ptr CreateNamedSource(const Identifier &name, const vector &fields, + bool recurring = false); + + LogicalPlanVerificationResult + ExportNamedProducer(LogicalOperator &op, const LogicalPlanVerificationPath &path, const Identifier &name); + + LogicalPlanSQLExportResult ExportRow(unique_ptr select, unique_ptr value, + vector fields); + unique_ptr ForwardFields(const LogicalPlanSQLExportedChild &child, + const vector &fields, + optional_ptr plain = nullptr); + + //! Operators from the root down to the operator currently being exported + const vector> &Ancestors() const { + return ancestors; + } + //! Enters an operator exported outside the standard child recursion (e.g. a RETURNING root) + struct AncestorScope { + AncestorScope(LogicalPlanSQLExportContext &context_p, LogicalOperator &op) : context(context_p) { + context.ancestors.push_back(op); + } + ~AncestorScope() { + context.ancestors.pop_back(); + } + LogicalPlanSQLExportContext &context; + }; + //! Make a CTE relation referenceable while its consumers are exported + void PushNamedRelation(TableIndex index, const Identifier &name, bool is_recurring); + //! Remove the innermost named relation and return how often it was referenced + idx_t PopNamedRelation(); + //! Reference the innermost named relation with this index; empty when it is out of scope + optional ReferenceNamedRelation(TableIndex index, bool is_recurring); + +private: + struct SourceScope; + optional_ptr source_scope; + struct NamedRelation { + TableIndex index; + Identifier name; + bool is_recurring; + idx_t references; + }; + ClientContext &context; + idx_t next_relation_ordinal = 0; + identifier_set_t relation_aliases; + //! The named-source reads this export created; any other reference to a source name is a capture. + reference_set_t source_reads; + vector> ancestors; + vector named_relations; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/planner/logical_plan_sql_exporter.hpp b/src/duckdb/src/include/duckdb/planner/logical_plan_sql_exporter.hpp new file mode 100644 index 000000000..33331fa18 --- /dev/null +++ b/src/duckdb/src/include/duckdb/planner/logical_plan_sql_exporter.hpp @@ -0,0 +1,67 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/planner/logical_plan_sql_exporter.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/identifier.hpp" +#include "duckdb/common/optional.hpp" +#include "duckdb/parser/parsed_expression.hpp" +#include "duckdb/parser/query_node.hpp" +#include "duckdb/parser/tableref.hpp" +#include "duckdb/planner/column_binding.hpp" +#include "duckdb/planner/bound_expression_sql_exporter.hpp" +#include "duckdb/planner/logical_plan_verification_result.hpp" + +namespace duckdb { + +class ClientContext; +class LogicalOperator; +struct LogicalExtensionOperator; + +struct LogicalPlanSQLExportField { + ColumnBinding source_binding; + //! Type represented by the exported SQL expression. + LogicalType type; + //! Optimizer-selected type accepted from the bound plan, when different. + optional optimizer_type = {}; +}; + +struct LogicalPlanSQLExportRelation { + unique_ptr query; + //! A zero-column relation has a SQL placeholder column that is absent from fields. + vector fields; +}; + +using LogicalPlanSQLExportResult = LogicalPlanVerificationResult; + +struct LogicalPlanSQLExportOptions { + optional> output_names; +}; + +struct LogicalPlanSQLExportReplacement { + reference op; + LogicalPlanSQLExportRelation relation; +}; + +class LogicalPlanSQLExporter { +public: + //! Export a verified plan to an owned query with positional binding/type fields. + DUCKDB_API static LogicalPlanVerificationResult + Export(ClientContext &context, LogicalOperator &root, const LogicalPlanSQLExportOptions &options = {}); + + //! Export an already resolved plan using owned SQL relations at explicit subtree boundaries. + //! Checks existing binding/type metadata without re-resolving the input. Each replacement preserves + //! the replaced subtree's visible bindings and types and is materialized once in the returned SQL. + //! Replacements must be disjoint, reachable subtrees. Neither the plan nor replacement ASTs are consumed. + DUCKDB_API static LogicalPlanSQLExportResult + ExportWithSources(ClientContext &context, LogicalOperator &root, + const vector &replacements, + const LogicalPlanSQLExportOptions &options = {}); +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/planner/logical_plan_verification_result.hpp b/src/duckdb/src/include/duckdb/planner/logical_plan_verification_result.hpp index f7f77ee85..568e1025f 100644 --- a/src/duckdb/src/include/duckdb/planner/logical_plan_verification_result.hpp +++ b/src/duckdb/src/include/duckdb/planner/logical_plan_verification_result.hpp @@ -18,7 +18,7 @@ namespace duckdb { enum class LogicalPlanVerificationPathRoot { LOGICAL_PLAN, STANDALONE_EXPRESSION }; -enum class LogicalPlanVerificationPathComponentType { OPERATOR_CHILD, OPERATOR_EXPRESSION, EXPRESSION_CHILD }; +enum class LogicalPlanVerificationPathComponentType : int32_t { OPERATOR_CHILD, OPERATOR_EXPRESSION, EXPRESSION_CHILD }; struct LogicalPlanVerificationPathComponent { LogicalPlanVerificationPathComponentType type; @@ -92,7 +92,7 @@ struct LogicalPlanVerificationConstructIdentity { DUCKDB_API bool operator<(const LogicalPlanVerificationConstructIdentity &other) const; }; -enum class LogicalPlanVerificationIssueCode { +enum class LogicalPlanVerificationIssueCode : int32_t { INVALID_BINDING, TYPE_MISMATCH, UNSUPPORTED_OPERATOR, @@ -105,7 +105,7 @@ enum class LogicalPlanVerificationIssueCode { INTERNAL_INVARIANT }; -enum class LogicalPlanVerificationPhase { VERIFY, EXPRESSION_EXPORT, PLAN_EXPORT }; +enum class LogicalPlanVerificationPhase : int32_t { VERIFY, EXPRESSION_EXPORT, PLAN_EXPORT }; struct LogicalPlanVerificationIssue { LogicalPlanVerificationIssueCode code = LogicalPlanVerificationIssueCode::INTERNAL_INVARIANT; @@ -134,6 +134,13 @@ class LogicalPlanVerificationResult { return LogicalPlanVerificationResult(optional(std::move(value)), {}); } + //! Propagate the normalized issues of a failed result carrying a different value type + template + static LogicalPlanVerificationResult Failure(const LogicalPlanVerificationResult &failed) { + D_ASSERT(failed.HasError()); + return LogicalPlanVerificationResult(optional(), failed.GetIssues()); + } + static LogicalPlanVerificationResult Failure(vector issues) { for (auto &issue : issues) { if (!issue.IsValid()) { @@ -157,12 +164,16 @@ class LogicalPlanVerificationResult { } T &GetValue() { - D_ASSERT(IsSuccess()); + if (!value) { + throw InternalException("Cannot get the value of a failed logical plan verification result"); + } return *value; } const T &GetValue() const { - D_ASSERT(IsSuccess()); + if (!value) { + throw InternalException("Cannot get the value of a failed logical plan verification result"); + } return *value; } diff --git a/src/duckdb/src/include/duckdb/planner/logical_plan_verifier.hpp b/src/duckdb/src/include/duckdb/planner/logical_plan_verifier.hpp index 3ed31043e..7a7852449 100644 --- a/src/duckdb/src/include/duckdb/planner/logical_plan_verifier.hpp +++ b/src/duckdb/src/include/duckdb/planner/logical_plan_verifier.hpp @@ -19,12 +19,18 @@ class LogicalPlanVerifier : private ColumnBindingResolver { public: static void Verify(ClientContext &context, LogicalOperator &op); DUCKDB_API static LogicalPlanVerificationResult VerifyAlways(LogicalOperator &op); + //! Check bindings against an already resolved plan without resolving or changing its output metadata. + //! The caller supplies the resolved types; this does not infer or repair stale operator types. + DUCKDB_API static LogicalPlanVerificationResult + VerifyResolvedBindings(LogicalOperator &op); private: + enum class TypeResolution { RESOLVE, EXISTING }; explicit LogicalPlanVerifier(LogicalPlanVerificationState &verification_state); unique_ptr VisitReplace(BoundColumnRefExpression &expr, unique_ptr *expr_ptr) override; - static bool ResolveOperatorTypes(LogicalOperator &op, LogicalPlanVerificationState &verification_state); + static bool ResolveOperatorTypes(LogicalOperator &op, LogicalPlanVerificationState &verification_state, + TypeResolution resolution = TypeResolution::RESOLVE); static void VerifyColumnBindings(LogicalOperator &op, LogicalPlanVerificationState &verification_state); static LogicalPlanVerificationResult VerifyAlwaysInternal(LogicalOperator &op, optional_ptr first_error); diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_aggregate.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_aggregate.hpp index edbd30030..39574d026 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_aggregate.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_aggregate.hpp @@ -20,6 +20,9 @@ namespace duckdb { //! operator. class LogicalAggregate : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_AGGREGATE_AND_GROUP_BY; public: diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_column_data_get.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_column_data_get.hpp index 02621f16d..fd4665cda 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_column_data_get.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_column_data_get.hpp @@ -19,6 +19,9 @@ class ManagedResultSet; //! LogicalColumnDataGet represents a scan operation from a ColumnDataCollection class LogicalColumnDataGet : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_CHUNK_GET; public: diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_comparison_join.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_comparison_join.hpp index 8a24c75b4..4a2f03bd3 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_comparison_join.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_comparison_join.hpp @@ -69,6 +69,9 @@ class LogicalComparisonJoin : public LogicalJoin { bool HasEquality(idx_t &range_count) const; bool HasArbitraryConditions() const; + //! Two-valued MARK comparisons with at least one null-safe equality hash key. + bool HasNullSafeMarkJoinConditions() const; + bool TryGetMarkJoinGroupTypes(vector &group_types) const; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_copy_to_file.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_copy_to_file.hpp index efbb095e6..0622e193a 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_copy_to_file.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_copy_to_file.hpp @@ -56,11 +56,16 @@ class LogicalCopyToFile : public LogicalOperator { PreserveOrderType preserve_order = PreserveOrderType::AUTOMATIC; vector partition_columns; vector order_columns; + //! Directory of a partition's files relative to file_path (see PhysicalCopyToFile) + unique_ptr partition_path_expression; vector names; vector expected_types; public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + vector GetColumnBindings() override; idx_t EstimateCardinality(ClientContext &context) override; void Serialize(Serializer &serializer) const override; diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_create_table.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_create_table.hpp index 573a40e26..3c55d340b 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_create_table.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_create_table.hpp @@ -31,6 +31,9 @@ class LogicalCreateTable : public LogicalOperator { void Serialize(Serializer &serializer) const override; static unique_ptr Deserialize(Deserializer &deserializer); + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + idx_t EstimateCardinality(ClientContext &context) override; protected: diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_cteref.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_cteref.hpp index d77a064b1..34f7d25e1 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_cteref.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_cteref.hpp @@ -16,6 +16,9 @@ namespace duckdb { //! LogicalCTERef represents a reference to a recursive CTE class LogicalCTERef : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_CTE_REF; public: diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_delete.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_delete.hpp index cc7c51086..1bca8cfb6 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_delete.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_delete.hpp @@ -32,6 +32,9 @@ class LogicalDelete : public LogicalOperator { void Serialize(Serializer &serializer) const override; static unique_ptr Deserialize(Deserializer &deserializer); + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + idx_t EstimateCardinality(ClientContext &context) override; vector GetTableIndex() const override; string GetName() const override; diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_distinct.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_distinct.hpp index c09a6aeac..a6fc5d3cc 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_distinct.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_distinct.hpp @@ -16,6 +16,9 @@ namespace duckdb { //! LogicalDistinct filters duplicate entries from its child operator class LogicalDistinct : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_DISTINCT; public: diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_dummy_scan.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_dummy_scan.hpp index 9b29954d5..e800c69b5 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_dummy_scan.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_dummy_scan.hpp @@ -15,6 +15,9 @@ namespace duckdb { //! LogicalDummyScan represents a dummy scan returning a single row class LogicalDummyScan : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_DUMMY_SCAN; public: diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_empty_result.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_empty_result.hpp index 253f1838f..dbced54d3 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_empty_result.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_empty_result.hpp @@ -18,6 +18,9 @@ class LogicalEmptyResult : public LogicalOperator { LogicalEmptyResult(); public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_EMPTY_RESULT; public: diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_explain.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_explain.hpp index 338261103..35e4088af 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_explain.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_explain.hpp @@ -28,11 +28,15 @@ class LogicalExplain : public LogicalOperator { string physical_plan; string logical_plan_unopt; string logical_plan_opt; + vector sql_output_names; + bool allow_unsupported_sql = false; public: void Serialize(Serializer &serializer) const override; static unique_ptr Deserialize(Deserializer &deserializer); + unique_ptr CreateSQLResult(ClientContext &context, TableIndex table_index); + idx_t EstimateCardinality(ClientContext &context) override; bool SupportSerialization() const override; diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_expression_get.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_expression_get.hpp index 19d893421..d707a1996 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_expression_get.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_expression_get.hpp @@ -12,9 +12,14 @@ namespace duckdb { +struct LogicalPlanSQLExportField; + //! LogicalExpressionGet represents a scan operation over a set of to-be-executed expressions class LogicalExpressionGet : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_EXPRESSION_GET; public: @@ -44,6 +49,11 @@ class LogicalExpressionGet : public LogicalOperator { vector GetTableIndex() const override; string GetName() const override; +private: + LogicalPlanVerificationResult + ExportSQLInput(LogicalPlanSQLExportContext &context, const LogicalPlanVerificationPath &path, + vector fields); + protected: void ResolveTypes() override { // types are resolved in the constructor diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_extension_operator.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_extension_operator.hpp index 7eb3c3bde..5946618b2 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_extension_operator.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_extension_operator.hpp @@ -17,6 +17,9 @@ class ColumnBindingResolver; struct LogicalExtensionOperator : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_EXTENSION_OPERATOR; public: diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_filter.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_filter.hpp index b594d8980..ce2f31eaa 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_filter.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_filter.hpp @@ -15,6 +15,9 @@ namespace duckdb { //! LogicalFilter represents a filter operation (e.g. WHERE or HAVING clause) class LogicalFilter : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_FILTER; public: diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_get.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_get.hpp index b9dc9958b..8fc063528 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_get.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_get.hpp @@ -9,8 +9,10 @@ #pragma once #include "duckdb/function/table_function.hpp" +#include "duckdb/common/enums/ordinality_request_type.hpp" #include "duckdb/planner/logical_operator.hpp" #include "duckdb/planner/table_filter_set.hpp" +#include "duckdb/planner/tableref/bound_at_clause.hpp" #include "duckdb/common/extra_operator_info.hpp" #include "duckdb/storage/table/row_group_order_options.hpp" @@ -18,23 +20,29 @@ namespace duckdb { class TableCatalogEntry; class DynamicTableFilterSet; +struct LogicalPlanSQLExportField; //! LogicalGet represents a scan operation from a data source class LogicalGet : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_GET; public: - LogicalGet(TableIndex table_index, TableFunction function, unique_ptr bind_data, + LogicalGet(TableIndex table_index, BoundTableFunction function, unique_ptr bind_data, vector returned_types, vector returned_names, virtual_column_map_t virtual_columns = virtual_column_map_t()); //! The table index in the current bind context TableIndex table_index; - //! The function that is called - TableFunction function; + //! The function that is called, together with the argument types this call was bound with + BoundTableFunction function; //! The bind data of the function unique_ptr bind_data; + //! Process-local input that cannot be reconstructed from SQL parameters + shared_ptr bind_info; //! The types of ALL columns that can be returned by the table function vector returned_types; //! The names of ALL columns that can be returned by the table function @@ -48,7 +56,9 @@ class LogicalGet : public LogicalOperator { //! The set of input parameters for the table function vector parameters; //! The set of named input parameters for the table function - named_parameter_map_t named_parameters; + named_argument_map_t named_parameters; + //! Whether the source invocation requested an ordinality column + OrdinalityType source_ordinality = OrdinalityType::WITHOUT_ORDINALITY; //! The set of named input table types for the table-in table-out function vector input_table_types; //! The set of named input table names for the table-in table-out function @@ -59,6 +69,10 @@ class LogicalGet : public LogicalOperator { //! pushed down into the table scan //! Stored so the can be included in explain output ExtraOperatorInfo extra_info; + //! The scan consumed a projection whose source expression is no longer retained. + bool has_pushed_projection = false; + //! The effective AT clause the table was looked up with, retained for SQL reconstruction + unique_ptr at_clause; //! Contains a reference to dynamically generated table filters (through e.g. a join up in the tree) shared_ptr dynamic_filters; //! Information for WITH ORDINALITY @@ -88,6 +102,8 @@ class LogicalGet : public LogicalOperator { vector &GetMutableColumnIds(); vector GetColumnBindings() override; idx_t EstimateCardinality(ClientContext &context) override; + //! Estimate cardinality from the source without using this operator's cached estimate. + idx_t EstimateSourceCardinality(ClientContext &context); bool TryGetStorageIndex(const ColumnIndex &column_index, StorageIndex &out_index) const; void SetScanOrder(unique_ptr options); void SetPartitionsToScan(vector partition_indices); @@ -114,6 +130,10 @@ class LogicalGet : public LogicalOperator { void ResolveTypes() override; private: + friend class LogicalWindow; + LogicalPlanVerificationResult + ExportSQLSource(LogicalPlanSQLExportContext &context, const LogicalPlanVerificationPath &path, + optional_ptr ordinality = nullptr); LogicalGet(); private: diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_insert.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_insert.hpp index 39dc666e0..4f0d7d1ec 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_insert.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_insert.hpp @@ -75,6 +75,9 @@ class LogicalInsert : public LogicalOperator { void Serialize(Serializer &serializer) const override; static unique_ptr Deserialize(Deserializer &deserializer); + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + protected: vector GetColumnBindings() override; void ResolveTypes() override; diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_join.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_join.hpp index 666a1b3b4..3e0a7a6a4 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_join.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_join.hpp @@ -17,6 +17,9 @@ namespace duckdb { //! LogicalJoin represents a join between two relations class LogicalJoin : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_INVALID; public: @@ -28,6 +31,10 @@ class LogicalJoin : public LogicalOperator { static void GetExpressionBindings(const Expression &expr, unordered_set &bindings); //! Moves the state shared by all logical joins when replacing one join operator with another static void MoveJoinState(LogicalJoin &source, LogicalJoin &target); + //! Replaces a join by one of its sides while keeping the join's output layout: the side's projection map, if + //! any, is carried by a filter without conditions so that operators above the join keep seeing the same columns + static unique_ptr PromoteJoinSide(unique_ptr child, + vector projection_map); bool HasProjectionMap() const override { return !left_projection_map.empty() || !right_projection_map.empty(); diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_limit.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_limit.hpp index a8becefec..9579f27da 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_limit.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_limit.hpp @@ -16,6 +16,9 @@ namespace duckdb { //! LogicalLimit represents a LIMIT clause class LogicalLimit : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_LIMIT; public: @@ -23,6 +26,8 @@ class LogicalLimit : public LogicalOperator { BoundLimitNode limit_val; BoundLimitNode offset_val; + //! SQL offset before row group pruning removed rows from the scan. + optional_idx unpruned_offset; public: vector GetColumnBindings() override; diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_materialized_cte.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_materialized_cte.hpp index 1a5eb1773..460233558 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_materialized_cte.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_materialized_cte.hpp @@ -13,11 +13,28 @@ namespace duckdb { +class LogicalCTERef; + +//! Retaining outer rows preserves every domain partition needed for their results. +//! The domain scan has no consumers independent of the outer-row scan. +struct CTEFilterDependency { + CTEFilterDependency(TableIndex row_scan, TableIndex domain_scan) : row_scan(row_scan), domain_scan(domain_scan) { + } + + bool MatchesConsumers(const vector> &references) const; + + TableIndex row_scan; + TableIndex domain_scan; +}; + class LogicalMaterializedCTE : public LogicalCTE { explicit LogicalMaterializedCTE() : LogicalCTE(LogicalOperatorType::LOGICAL_MATERIALIZED_CTE) { } public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_MATERIALIZED_CTE; public: @@ -30,6 +47,8 @@ class LogicalMaterializedCTE : public LogicalCTE { } CTEMaterialize materialize = CTEMaterialize::CTE_MATERIALIZE_ALWAYS; + //! Valid only for the original generated consumers, until CTE filter pushdown. Deliberately not serialized. + unique_ptr filter_dependency; public: InsertionOrderPreservingMap ParamsToString() const override; diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_merge_into.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_merge_into.hpp index e8cb2ed0d..eef39e6ad 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_merge_into.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_merge_into.hpp @@ -66,6 +66,9 @@ class LogicalMergeInto : public LogicalOperator { void Serialize(Serializer &serializer) const override; static unique_ptr Deserialize(Deserializer &deserializer); + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + idx_t EstimateCardinality(ClientContext &context) override; vector GetTableIndex() const override; diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_order.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_order.hpp index b601033d5..42169f345 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_order.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_order.hpp @@ -18,6 +18,9 @@ namespace duckdb { //! LogicalOrder represents an ORDER BY clause, sorting the data class LogicalOrder : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_ORDER_BY; public: diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_pivot.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_pivot.hpp index b124e2126..644bd161d 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_pivot.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_pivot.hpp @@ -18,6 +18,9 @@ namespace duckdb { class LogicalPivot : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_PIVOT; public: diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_projection.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_projection.hpp index 357128c61..6f3c63a5c 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_projection.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_projection.hpp @@ -15,6 +15,9 @@ namespace duckdb { //! LogicalProjection represents the projection list in a SELECT clause class LogicalProjection : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_PROJECTION; public: @@ -23,6 +26,9 @@ class LogicalProjection : public LogicalOperator { TableIndex table_index; public: + //! Projects every column of the child unchanged + static unique_ptr CreateIdentity(TableIndex table_index, unique_ptr child); + vector GetColumnBindings() override; void Serialize(Serializer &serializer) const override; static unique_ptr Deserialize(Deserializer &deserializer); diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_recursive_cte.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_recursive_cte.hpp index c6e3b631e..6e7b79f95 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_recursive_cte.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_recursive_cte.hpp @@ -18,6 +18,9 @@ class LogicalRecursiveCTE : public LogicalCTE { LogicalRecursiveCTE(); public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_RECURSIVE_CTE; public: @@ -45,6 +48,12 @@ class LogicalRecursiveCTE : public LogicalCTE { vector GetTableIndex() const override; string GetName() const override; +private: + friend class LogicalPlanSQLExportContext; + LogicalPlanVerificationResult + ExportSQLDefinition(LogicalPlanSQLExportContext &context, const LogicalPlanVerificationPath &path, + const Identifier &name); + protected: void ResolveTypes() override; }; diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_sample.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_sample.hpp index 7bce81409..490b06fec 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_sample.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_sample.hpp @@ -16,6 +16,9 @@ namespace duckdb { //! LogicalSample represents a SAMPLE clause class LogicalSample : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_SAMPLE; public: diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_secure_view.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_secure_view.hpp index 6b3278cb8..b9546dbc0 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_secure_view.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_secure_view.hpp @@ -8,26 +8,54 @@ #pragma once +#include "duckdb/common/types/value.hpp" +#include "duckdb/parser/qualified_name.hpp" #include "duckdb/planner/logical_operator.hpp" namespace duckdb { +class BoundAtClause; + //! LogicalSecureView wraps the expanded plan of a secure view. It does not alter the result of its child - it only //! acts as an optimization barrier that prevents the optimizer from pushing anything into the view. class LogicalSecureView : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_SECURE_VIEW; public: LogicalSecureView(string view_name, unique_ptr child); + LogicalSecureView(string view_name, QualifiedName source_name, vector source_types, + optional_ptr at_clause, unique_ptr child); //! The name of the view - used for printing the plan string view_name; //! The filters that the optimizer pushed into the view - these are the caller's own expressions, and they are //! reported as part of the boundary node because the operators inside the view are never shown vector pushed_filters; + //! Whether the statistics of the columns the view emits may escape the boundary. Set by AnalyzeStatistics + bool propagate_statistics = false; + //! Qualified source identity and the original positional schema used by SQL export + bool has_source = false; + QualifiedName source_name; + vector source_types; + bool has_at_clause = false; + Identifier at_unit; + Value at_value; + //! Current child bindings and their expressions over the original positional schema + vector output_bindings; + vector> output_expressions; + //! Caller predicates expressed over the original positional view schema + vector> source_filters; public: + //! Determine for every secure view in the plan whether the statistics of the columns it emits may escape it. + //! This must run before anything is pushed into the views - the filters that the optimizer pushes into a view + //! are the caller's own, and must not be mistaken for the view restricting the rows it emits + static void AnalyzeStatistics(LogicalOperator &plan); + vector GetColumnBindings() override; idx_t EstimateCardinality(ClientContext &context) override; InsertionOrderPreservingMap ParamsToString() const override; diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_set_operation.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_set_operation.hpp index ce256bbba..342a8c6aa 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_set_operation.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_set_operation.hpp @@ -17,6 +17,9 @@ class LogicalSetOperation : public LogicalOperator { bool allow_out_of_order); public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_INVALID; public: diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_top_n.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_top_n.hpp index 6a2f38ab6..0348034c9 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_top_n.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_top_n.hpp @@ -17,6 +17,9 @@ struct DynamicFilterData; //! LogicalTopN represents a comibination of ORDER BY and LIMIT clause, using Min/Max Heap class LogicalTopN : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_TOP_N; public: @@ -28,6 +31,8 @@ class LogicalTopN : public LogicalOperator { idx_t limit; //! The offset from the start to begin emitting elements idx_t offset; + //! SQL offset before row group pruning removed rows from the scan. + optional_idx unpruned_offset; //! Dynamic table filter (if any) shared_ptr dynamic_filter; diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_unconditional_join.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_unconditional_join.hpp index c25509bbf..7635bc64b 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_unconditional_join.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_unconditional_join.hpp @@ -16,6 +16,9 @@ namespace duckdb { //! where the join condition is implicit (cross product, position, etc.) class LogicalUnconditionalJoin : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_INVALID; public: diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_unnest.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_unnest.hpp index 7398b2fb4..779e3d7a8 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_unnest.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_unnest.hpp @@ -15,6 +15,9 @@ namespace duckdb { //! LogicalUnnest represents the logical UNNEST operator. class LogicalUnnest : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_UNNEST; public: diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_update.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_update.hpp index 388ac24b1..2a85e5909 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_update.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_update.hpp @@ -48,11 +48,17 @@ class LogicalUpdate : public LogicalOperator { void Serialize(Serializer &serializer) const override; static unique_ptr Deserialize(Deserializer &deserializer); + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + idx_t EstimateCardinality(ClientContext &context) override; string GetName() const override; DUCKDB_API static void BindExtraColumns(TableCatalogEntry &table, LogicalGet &get, LogicalProjection &proj, - LogicalUpdate &update, physical_index_set_t &bound_columns); + LogicalUpdate &update, physical_index_set_t &bound_columns, + bool reuse_projected_columns = false); + DUCKDB_API static void BindAllColumns(TableCatalogEntry &table, LogicalGet &get, LogicalProjection &proj, + LogicalUpdate &update, bool reuse_projected_columns = false); static void RewriteInPlaceUpdates(LogicalOperator &update_op); diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_window.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_window.hpp index 6dcf5f02d..2f73c5245 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_window.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_window.hpp @@ -16,6 +16,9 @@ namespace duckdb { //! operator. class LogicalWindow : public LogicalOperator { public: + LogicalPlanSQLExportResult ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) override; + static constexpr const LogicalOperatorType TYPE = LogicalOperatorType::LOGICAL_WINDOW; public: diff --git a/src/duckdb/src/include/duckdb/planner/planner.hpp b/src/duckdb/src/include/duckdb/planner/planner.hpp index 380f53961..9d0e0914a 100644 --- a/src/duckdb/src/include/duckdb/planner/planner.hpp +++ b/src/duckdb/src/include/duckdb/planner/planner.hpp @@ -39,6 +39,8 @@ class Planner { public: void CreatePlan(unique_ptr statement); + //! Apply the execution planner's optimizer policy, including mandatory rewrites. + void Optimize(); static void VerifyPlan(ClientContext &context, unique_ptr &op, optional_ptr map = nullptr); diff --git a/src/duckdb/src/include/duckdb/planner/sql_export/bound_expression_sql_exporter_internal.hpp b/src/duckdb/src/include/duckdb/planner/sql_export/bound_expression_sql_exporter_internal.hpp new file mode 100644 index 000000000..8d62721e9 --- /dev/null +++ b/src/duckdb/src/include/duckdb/planner/sql_export/bound_expression_sql_exporter_internal.hpp @@ -0,0 +1,175 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/planner/sql_export/bound_expression_sql_exporter_internal.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/planner/bound_expression_sql_exporter.hpp" +#include "duckdb/planner/column_binding_map.hpp" +#include "duckdb/planner/sql_export_helpers.hpp" +#include "duckdb/function/function.hpp" +#include "duckdb/parser/qualified_name.hpp" + +namespace duckdb { + +class BoundAggregateExpression; +class BoundCaseExpression; +class BoundColumnRefExpression; +class BoundConjunctionExpression; +class BoundConstantExpression; +class BoundFunctionExpression; +class BoundLambdaExpression; +class BoundOperatorExpression; +class BoundReferenceExpression; +class BoundUnnestExpression; +class BoundWindowExpression; + +using BoundExpressionSQLExportResult = LogicalPlanVerificationResult>; +using BoundAggregateSQLExportResult = LogicalPlanVerificationResult>; + +class BoundExpressionSQLExportState { +public: + static LogicalPlanVerificationIssue + InternalInvariant(optional path, string message, + optional construct = {}); + + static LogicalPlanVerificationIssue InternalExpressionInvariant(const LogicalPlanVerificationPath &path, + const Expression &expression, string message); + + static LogicalPlanVerificationIssue UnsupportedFeature(const LogicalPlanVerificationPath &path, string feature, + string message); + + static LogicalPlanVerificationIssue UnsupportedFunction(const LogicalPlanVerificationPath &path, + LogicalPlanVerificationFunctionIdentity identity, + string message); + + static bool HasNestedCollation(const LogicalType &type); + + static BoundExpressionSQLExportResult PreserveCollation(const LogicalType &type, + BoundExpressionSQLExportResult result, + const LogicalPlanVerificationPath &path); + + template + static LogicalPlanVerificationFunctionIdentity DefinitionFunctionIdentity(const FUNCTION &definition, + const vector &arguments, + const LogicalType &return_type) { + LogicalPlanVerificationFunctionIdentity identity; + identity.catalog = definition.GetCatalogName().GetIdentifierName(); + identity.schema = definition.GetSchemaName().GetIdentifierName(); + identity.name = definition.GetName().GetIdentifierName(); + identity.arguments = arguments; + identity.return_type = return_type; + return identity; + } + + template + static optional RebindableFunctionName(const FUNCTION &definition) { + auto name = definition.GetQualifiedName(); + if (name.Catalog().empty()) { + name = name.WithCatalog(Identifier::SystemCatalog()); + } + if (name.Schema().empty()) { + name = QualifiedName(name.Catalog(), Identifier::DefaultSchema(), name.Name()); + } + if (name.Path().empty()) { + return {}; + } + for (auto &component : name.Path()) { + if (component.empty()) { + return {}; + } + } + return name; + } + + static LogicalType SQLCastType(const LogicalType &type); + + static unique_ptr SQLCast(const LogicalType &type, unique_ptr child, + bool try_cast = false); + + column_binding_map_t> substitutions; + + explicit BoundExpressionSQLExportState(const BoundExpressionSQLExportContext &context_p); + BoundExpressionSQLExportResult Export(const Expression &expression, const LogicalPlanVerificationPath &path); + BoundExpressionSQLExportResult ExportWindow(const BoundWindowExpression &expression, + const LogicalPlanVerificationPath &path); + BoundExpressionSQLExportResult ExportUnnest(const BoundUnnestExpression &expression, + const LogicalPlanVerificationPath &path); + BoundAggregateSQLExportResult ExportAggregateCall(const BoundAggregateExpression &expression, + const LogicalPlanVerificationPath &path); + +private: + struct ChildExpression { + explicit ChildExpression(optional_ptr expression_p, + optional expected_type_p = {}) + : expression(expression_p), expected_type(std::move(expected_type_p)) { + } + + optional_ptr expression; + optional expected_type; + }; + BoundExpressionSQLExportResult ExportInternal(const Expression &expression, + const LogicalPlanVerificationPath &path); + template + BoundExpressionSQLExportResult ExportWindowFunction(const BoundWindowExpression &expression, + const FUNCTION &function, + const LogicalPlanVerificationPath &path); + BoundExpressionSQLExportResult RestoreResultType(const LogicalType &type, unique_ptr result, + const LogicalPlanVerificationPath &path); + static bool RequiresConstantConstructor(const LogicalType &type); + static BoundExpressionSQLExportResult CastToConstructedType(const LogicalType &type, + unique_ptr child, + const LogicalPlanVerificationPath &path); + BoundExpressionSQLExportResult ExportConstant(const BoundConstantExpression &expression, + const LogicalPlanVerificationPath &path); + BoundExpressionSQLExportResult ExportReference(const BoundReferenceExpression &expression, + const LogicalPlanVerificationPath &path); + BoundExpressionSQLExportResult ExportLambda(const BoundLambdaExpression &lambda, + const BoundFunctionExpression &function, idx_t logical_argument_count, + const LogicalPlanVerificationPath &path); + BoundExpressionSQLExportResult ExportColumnRef(const BoundColumnRefExpression &expression, + const LogicalPlanVerificationPath &path); + LogicalPlanVerificationIssue InvalidBinding(const LogicalPlanVerificationPath &path, const ColumnBinding &binding, + string message); + BoundExpressionSQLExportResult ExportFunction(const BoundFunctionExpression &expression, + const LogicalPlanVerificationPath &path); + BoundExpressionSQLExportResult ExportCast(const BoundFunctionExpression &expression, + const LogicalPlanVerificationPath &path); + BoundExpressionSQLExportResult ExportComparison(const BoundFunctionExpression &expression, + const LogicalPlanVerificationPath &path); + BoundExpressionSQLExportResult ExportBetween(const BoundFunctionExpression &expression, + const LogicalPlanVerificationPath &path); + BoundExpressionSQLExportResult ExportConjunction(const BoundConjunctionExpression &expression, + const LogicalPlanVerificationPath &path); + BoundExpressionSQLExportResult ExportCase(const BoundCaseExpression &expression, + const LogicalPlanVerificationPath &path); + BoundExpressionSQLExportResult ExportOperator(const BoundOperatorExpression &expression, + const LogicalPlanVerificationPath &path); + BoundExpressionSQLExportResult CompressedMaterializationFailure(const BoundFunctionExpression &expression, + const LogicalPlanVerificationPath &path, + string message); + optional + TryExportCompressedMaterialization(const BoundFunctionExpression &expression, + const LogicalPlanVerificationPath &path); + BoundExpressionSQLExportResult ExportScalarFunction(const BoundFunctionExpression &expression, + const LogicalPlanVerificationPath &path); + BoundAggregateSQLExportResult BuildAggregateCall(const BoundAggregateExpression &expression, + const LogicalPlanVerificationPath &path); + BoundExpressionSQLExportResult ExportAggregate(const BoundAggregateExpression &expression, + const LogicalPlanVerificationPath &path); + BoundExpressionSQLExportResult ExportChild(const Expression &expression, const LogicalPlanVerificationPath &path, + idx_t child_index); + void ExportChildren(const vector> &source, const LogicalPlanVerificationPath &path, + vector> &result, vector &issues, + const optional &expected_type = {}); + void ExportChildren(const vector &source, const LogicalPlanVerificationPath &path, + vector> &result, vector &issues); + const BoundExpressionSQLExportContext &context; + vector>> lambda_reference_scopes; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/planner/sql_export/logical_plan_sql_exporter_internal.hpp b/src/duckdb/src/include/duckdb/planner/sql_export/logical_plan_sql_exporter_internal.hpp new file mode 100644 index 000000000..f32bb43d1 --- /dev/null +++ b/src/duckdb/src/include/duckdb/planner/sql_export/logical_plan_sql_exporter_internal.hpp @@ -0,0 +1,146 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/planner/sql_export/logical_plan_sql_exporter_internal.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/planner/logical_plan_sql_export_context.hpp" +#include "duckdb/planner/column_binding_map.hpp" +#include "duckdb/planner/sql_export_helpers.hpp" +#include "duckdb/parser/query_node/select_node.hpp" + +namespace duckdb { + +class Expression; +class LogicalOperator; +class LogicalMaterializedCTE; +class LogicalCTERef; +class LogicalRecursiveCTE; +class LogicalComparisonJoin; +class LogicalColumnDataGet; +class LogicalGet; +class LogicalExpressionGet; +class LogicalCopyToFile; +class LogicalCreateTable; +class LogicalFilter; +class LogicalProjection; +class LogicalSecureView; +class LogicalSample; +class LogicalPivot; +class LogicalLimit; +class LogicalSetOperation; +class LogicalAggregate; +struct LogicalExtensionOperator; +class ClientContext; + +using LogicalPlanSQLFieldResult = LogicalPlanVerificationResult>; +struct SQLSourceQueryResult { + unique_ptr query; + string unsupported_reason; +}; + +//! Helpers shared by the logical operators' SQL reconstruction +struct LogicalPlanSQLExportHelpers { + static SQLSourceQueryResult ReconstructSQLSource(ClientContext &context, const LogicalGet &get, + unique_ptr input, const Identifier &relation_alias, + bool source_ordinality); + + static LogicalPlanVerificationPath PlanChildPath(const LogicalPlanVerificationPath &path, idx_t ordinal); + + static LogicalPlanVerificationPath PlanExpressionPath(const LogicalPlanVerificationPath &path, idx_t ordinal); + + static LogicalPlanVerificationResult> + ExportTypedNull(const LogicalType &type, const LogicalPlanVerificationPath &path); + + static LogicalPlanVerificationIssue PlanUnsupportedFeature(const LogicalPlanVerificationPath &path, string feature, + string message); + + static LogicalPlanVerificationFunctionIdentity LogicalSourceIdentity(); + + static LogicalPlanVerificationFunctionIdentity LogicalSourceIdentity(const LogicalGet &get); + + static LogicalPlanVerificationIssue UnsupportedSource(const LogicalPlanVerificationPath &path, + LogicalPlanVerificationFunctionIdentity source, string guard); + + static Identifier FieldIdentifier(idx_t ordinal); + + static LogicalPlanSQLFieldResult CreateFields(LogicalOperator &op, const LogicalPlanVerificationPath &path); + + static BoundExpressionSQLExportContext + CreateBindingContext(ClientContext &context, const vector> &children, + const vector> &plain_scopes = {}); + + static void PropagateSemanticTypes(vector &fields, + const vector> &children); + + static unique_ptr CreateSubquery(LogicalPlanSQLExportedChild child); + + static unique_ptr ChildColumn(const LogicalPlanSQLExportedChild &child, idx_t field_index, + optional_ptr plain = nullptr); + + static vector> CollectExpressions(const LogicalOperator &op); + + static bool HasEffectfulExpressions(const LogicalOperator &op); + + static bool CollectScopeAliases(const TableRef &table, identifier_set_t &aliases); + + static optional_ptr PlainScope(const QueryNode &query); + + static void SetChildScope(SelectNode &select, LogicalPlanSQLExportedChild child, + optional_ptr plain); + + static optional_ptr ComposeInput(LogicalOperator &op, const LogicalPlanSQLExportedChild &child, + BoundExpressionSQLExportState &state); + + static bool HasSimpleGroups(const LogicalAggregate &aggregate); + + static bool ComposeProjection(LogicalProjection &projection, LogicalPlanSQLExportedChild &child, SelectNode &select, + const BoundExpressionSQLExportContext &context, + const LogicalPlanVerificationPath &path); + + static bool ComposeOrder(LogicalOperator &op, LogicalPlanSQLExportedChild &child, SelectNode &select, + const BoundExpressionSQLExportContext &context, const LogicalPlanVerificationPath &path); + + static bool IsIdentityProjection(const LogicalProjection &projection, + const vector &fields); + + //! Alias the relation's visible columns with the statement's output names. + static LogicalPlanSQLExportResult ApplyOutputNames(LogicalPlanSQLExportResult result, + const vector &output_names); +}; + +//! Export of write statements with a query source (sql_export_statements.cpp) +struct SQLExportStatementHelpers { + static LogicalPlanSQLExportResult ExportCopyToNode(LogicalCopyToFile &op, LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path); + static LogicalPlanSQLExportResult ExportCreateTableAsNode(LogicalCreateTable &op, + LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path); + //! The query node that carries WITH definitions for this exported relation. + static QueryNode &SourceDefinitionNode(QueryNode &query); +}; + +//! Export of data-modifying plan roots and CTE bodies (sql_export_dml.cpp) +struct SQLExportDMLHelpers { + //! The RETURNING projection over a data-modifying operator, when the root has that shape. + static optional_ptr MatchReturningRoot(LogicalOperator &root); + //! Export a RETURNING root; output names become the RETURNING aliases. + static LogicalPlanSQLExportResult ExportReturning(LogicalPlanSQLExportContext &context, LogicalProjection &root, + const optional> &output_names, + const LogicalPlanVerificationPath &path); + //! Whether this operator is a data-modifying statement body: a DML operator or its RETURNING projection. + static bool MatchDMLBody(LogicalOperator &op); + //! Whether the statement-body chain itself crosses a replaced source boundary, in which case + //! the ordinary relational export must handle it instead of the statement export. + static bool ContainsReplacedBoundary(const LogicalPlanSQLExportContext &context, LogicalOperator &op); + //! Export a data-modifying statement body outside the plan root (e.g. a materialized CTE side). + static LogicalPlanSQLExportResult ExportBody(LogicalPlanSQLExportContext &context, LogicalOperator &op, + const LogicalPlanVerificationPath &path); + //! Whether this exported query is a data-modifying statement node. + static bool IsDMLNode(const QueryNode &query); +}; +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/planner/sql_export_helpers.hpp b/src/duckdb/src/include/duckdb/planner/sql_export_helpers.hpp index 1c358698c..211a6158b 100644 --- a/src/duckdb/src/include/duckdb/planner/sql_export_helpers.hpp +++ b/src/duckdb/src/include/duckdb/planner/sql_export_helpers.hpp @@ -1,57 +1,118 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/planner/sql_export_helpers.hpp +// +// +//===----------------------------------------------------------------------===// + #pragma once #include "duckdb/common/identifier.hpp" +#include "duckdb/parser/expression/cast_expression.hpp" +#include "duckdb/parser/expression/collate_expression.hpp" +#include "duckdb/parser/expression/conjunction_expression.hpp" +#include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/common/type_visitor.hpp" -#include "duckdb/common/unordered_set.hpp" +#include "duckdb/planner/expression.hpp" #include "duckdb/planner/logical_plan_verification_result.hpp" namespace duckdb { -namespace SQLExportHelpers { - -inline LogicalPlanVerificationPath -ChildPath(const LogicalPlanVerificationPath &path, idx_t ordinal, - LogicalPlanVerificationPathComponentType type = LogicalPlanVerificationPathComponentType::EXPRESSION_CHILD) { - auto result = path; - result.components.push_back({type, ordinal}); - return result; -} - -inline LogicalPlanVerificationIssue MakeIssue(LogicalPlanVerificationIssueCode code, LogicalPlanVerificationPhase phase, - optional path, - optional construct, - string message) { - LogicalPlanVerificationIssue issue; - issue.code = code; - issue.phase = phase; - issue.path = std::move(path); - issue.construct = std::move(construct); - issue.message = std::move(message); - return issue; -} - -inline bool IsValidIdentifier(const Identifier &identifier) { - auto &name = identifier.GetIdentifierName(); - return !name.empty() && name.find('\0') == string::npos && Value::StringIsValid(name); -} - -inline bool IsSQLExportType(LogicalTypeId id) { - static const auto admitted_ids = [] { - // SQL export follows AllTypes' value-type coverage, with SQLNULL included and TUPLE excluded. - unordered_set ids {LogicalTypeId::SQLNULL}; - for (auto &type : LogicalType::AllTypes()) { - if (type.id() != LogicalTypeId::TUPLE) { - ids.insert(type.id()); +struct SQLExportHelpers { + template + static unique_ptr SystemFunction(Identifier name, vector arguments) { + return make_uniq( + QualifiedName(Identifier::SystemCatalog(), Identifier::DefaultSchema(), std::move(name)), + std::move(arguments)); + } + + static unique_ptr Conjoin(unique_ptr left, unique_ptr right) { + if (!left) { + return right; + } + if (!right) { + return left; + } + return make_uniq(ExpressionType::CONJUNCTION_AND, std::move(left), std::move(right)); + } + + static unique_ptr Conjoin(vector> predicates) { + if (predicates.empty()) { + return nullptr; + } + if (predicates.size() == 1) { + return std::move(predicates[0]); + } + return make_uniq(ExpressionType::CONJUNCTION_AND, std::move(predicates)); + } + + static LogicalPlanVerificationPath ChildPath( + const LogicalPlanVerificationPath &path, idx_t ordinal, + LogicalPlanVerificationPathComponentType type = LogicalPlanVerificationPathComponentType::EXPRESSION_CHILD) { + auto result = path; + result.components.push_back({type, ordinal}); + return result; + } + + static LogicalPlanVerificationIssue MakeIssue(LogicalPlanVerificationIssueCode code, + LogicalPlanVerificationPhase phase, + optional path, + optional construct, + string message) { + LogicalPlanVerificationIssue issue; + issue.code = code; + issue.phase = phase; + issue.path = std::move(path); + issue.construct = std::move(construct); + issue.message = std::move(message); + return issue; + } + + //! Constant ORDER BY keys have no effect, and a literal key would be bound as a position or rejected + static bool IsConstantOrderKey(const Expression &bound, const ParsedExpression &exported) { + return bound.GetExpressionClass() == ExpressionClass::BOUND_CONSTANT || + exported.GetExpressionClass() == ExpressionClass::CONSTANT; + } + + static unique_ptr OrderExpression(const LogicalType &type, + unique_ptr expression) { + if (type.id() == LogicalTypeId::VARCHAR) { + if (type.HasAlias()) { + expression = make_uniq(LogicalType::VARCHAR, std::move(expression)); } + return make_uniq("C", std::move(expression)); } - return ids; - }(); - return admitted_ids.count(id) != 0; -} + return expression; + } + + static bool IsValidIdentifier(const Identifier &identifier) { + auto &name = identifier.GetIdentifierName(); + return !name.empty() && name.find('\0') == string::npos && Value::StringIsValid(name); + } -inline bool IsSQLRepresentableType(const LogicalType &type) { - return type.IsComplete() && - !TypeVisitor::Contains(type, [](const LogicalType &child) { return !IsSQLExportType(child.id()); }); -} + static bool IsSQLExportType(LogicalTypeId id) { + return TypeExpression::IsSQLType(id); + } -} // namespace SQLExportHelpers + static bool IsSQLRepresentableType(const LogicalType &type) { + return TypeExpression::CanRepresent(type); + } + + static bool IsSQLValueType(const LogicalType &type) { + return type.IsComplete() && + !TypeVisitor::Contains(type, [](const LogicalType &child) { return !IsSQLExportType(child.id()); }); + } + + static string TypeCollationSignature(const LogicalType &type) { + string result; + TypeVisitor::Contains(type, [&](const LogicalType &child) { + if (child.id() == LogicalTypeId::VARCHAR) { + const auto collation = StringType::GetCollation(child); + result += std::to_string(collation.size()) + ":" + collation + ";"; + } + return false; + }); + return result; + } +}; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/planner/statement_preprocessor.hpp b/src/duckdb/src/include/duckdb/planner/statement_preprocessor.hpp index be8492f99..580cc4ec4 100644 --- a/src/duckdb/src/include/duckdb/planner/statement_preprocessor.hpp +++ b/src/duckdb/src/include/duckdb/planner/statement_preprocessor.hpp @@ -11,22 +11,27 @@ #include "duckdb/common/common.hpp" #include "duckdb/common/vector.hpp" #include "duckdb/parser/statement/pragma_statement.hpp" -#include "duckdb/transaction/transaction_context.hpp" -#include "duckdb/common/enums/current_transaction_state.hpp" namespace duckdb { class ClientContext; class ClientContextLock; class SQLStatement; struct PragmaInfo; -//! Preprocesses parsed statements: expands pragmas, unpacks multi-statements, and wraps in transactions + +//! The statements one user statement expands into +struct PreprocessedStatement { + vector> statements; +}; + +//! Expands a parsed user statement: reparses pragmas and unpacks multi-statements class StatementPreprocessor { public: explicit StatementPreprocessor(ClientContext &context); - void Preprocess(ClientContextLock &lock, vector> &statements, - CurrentTransactionState transaction_context_state); - void PreprocessInternal(ClientContextLock &lock, vector> &statements, - CurrentTransactionState transaction_context_state); + + //! Expands one user statement, in a transaction of its own unless one is active + PreprocessedStatement PreprocessStatement(ClientContextLock &lock, unique_ptr statement); + //! Expands one user statement, for a caller that already runs inside a transaction + PreprocessedStatement ExpandStatement(unique_ptr statement) const; private: ClientContext &context; @@ -36,4 +41,5 @@ class StatementPreprocessor { //! statement(s) to replace the current one. Otherwise, it just returns back the original statement in a vector. vector> TryReparsePragma(unique_ptr statement) const; }; + } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/planner/table_binding.hpp b/src/duckdb/src/include/duckdb/planner/table_binding.hpp index 1d7cf1f2e..993f8fe1c 100644 --- a/src/duckdb/src/include/duckdb/planner/table_binding.hpp +++ b/src/duckdb/src/include/duckdb/planner/table_binding.hpp @@ -59,6 +59,9 @@ struct Binding { const vector &GetColumnNames(); idx_t GetColumnCount(); void SetColumnType(idx_t col_idx, LogicalType type); + //! Marks this binding as the NULL-producing side of an outer join + void SetNullExtended(); + bool IsNullExtended() const; static BindingAlias GetAlias(const Identifier &explicit_alias, const StandardEntry &entry); static BindingAlias GetAlias(const Identifier &explicit_alias, optional_ptr entry); @@ -98,6 +101,8 @@ struct Binding { vector names; //! Name -> index for the names identifier_map_t name_map; + //! Whether rows of this binding can be NULL-extended by an outer join + bool null_extended = false; }; struct EntryBinding : public Binding { diff --git a/src/duckdb/src/include/duckdb/planner/tableref/bound_at_clause.hpp b/src/duckdb/src/include/duckdb/planner/tableref/bound_at_clause.hpp index b5e27efda..039a1335c 100644 --- a/src/duckdb/src/include/duckdb/planner/tableref/bound_at_clause.hpp +++ b/src/duckdb/src/include/duckdb/planner/tableref/bound_at_clause.hpp @@ -12,6 +12,9 @@ namespace duckdb { +class Serializer; +class Deserializer; + //! The AT clause specifies which version of a table to read class BoundAtClause { public: @@ -26,6 +29,9 @@ class BoundAtClause { return val; } + void Serialize(Serializer &serializer) const; + static unique_ptr Deserialize(Deserializer &deserializer); + private: //! The unit (e.g. TIMESTAMP or VERSION) Identifier unit; diff --git a/src/duckdb/src/include/duckdb/storage/arena_allocator.hpp b/src/duckdb/src/include/duckdb/storage/arena_allocator.hpp index 6830cbb9f..dd3c40b24 100644 --- a/src/duckdb/src/include/duckdb/storage/arena_allocator.hpp +++ b/src/duckdb/src/include/duckdb/storage/arena_allocator.hpp @@ -60,6 +60,16 @@ class ArenaAllocator { head->current_position -= shrink_size; } + //! A position in the arena - everything allocated after it was taken can be released again with Rewind + struct Position { + ArenaChunk *chunk = nullptr; + idx_t current_position = 0; + }; + //! Returns the current position of the arena + DUCKDB_API Position GetPosition() const; + //! Releases everything that was allocated after the position was taken - those allocations may no longer be used + DUCKDB_API void Rewind(const Position &position); + //! Resets the current head and destroys all previous arena chunks DUCKDB_API void Reset(); //! Like Reset, but also destroys the current chunk, so no memory stays allocated @@ -80,6 +90,11 @@ class ArenaAllocator { Allocator &GetAllocator() { return arena_allocator; } + //! Returns the allocator the arena itself allocates its blocks from - for memory that must be freeable + //! individually but should share the arena's memory accounting + Allocator &GetUnderlyingAllocator() { + return allocator; + } template T *Make(ARGS &&... args) { diff --git a/src/duckdb/src/include/duckdb/storage/block_allocator.hpp b/src/duckdb/src/include/duckdb/storage/block_allocator.hpp index bb95b18b7..329c24704 100644 --- a/src/duckdb/src/include/duckdb/storage/block_allocator.hpp +++ b/src/duckdb/src/include/duckdb/storage/block_allocator.hpp @@ -46,6 +46,10 @@ class BlockAllocator { bool SupportsFlush() const; void ThreadFlush(bool allocator_background_threads, idx_t threshold, idx_t thread_count) const; void FlushAll(optional_idx extra_memory = optional_idx()) const; + //! Estimated memory freed but still held by the allocator + idx_t GetEstimatedUnflushedMemory() const; + //! Flush if at least threshold bytes were freed since the last reclamation check, returns whether a flush happened + bool TryFlushDeallocated(idx_t threshold, idx_t memory_limit, idx_t used_memory) const; private: bool IsActive() const; @@ -68,6 +72,8 @@ class BlockAllocator { const hugeint_t uuid; //! Fallback allocator Allocator &allocator; + //! Estimated memory freed but still held by the allocator + mutable atomic estimated_unflushed_memory {0}; //! Block size (power of two) const idx_t block_size; diff --git a/src/duckdb/src/include/duckdb/storage/block_manager.hpp b/src/duckdb/src/include/duckdb/storage/block_manager.hpp index b36ebf1aa..14e17686a 100644 --- a/src/duckdb/src/include/duckdb/storage/block_manager.hpp +++ b/src/duckdb/src/include/duckdb/storage/block_manager.hpp @@ -117,6 +117,11 @@ class BlockManager { void UnregisterPersistentBlock(BlockHandle &block); //! UnregisterBlock, only accepts non-temporary block ids virtual void UnregisterBlock(block_id_t id); + //! Unregister the block id if no live handle is registered for it. Used when destroying a + //! handle: a newer handle may have been registered for the same block id in the meantime + //! (metadata blocks are re-registered in place) and must not be unregistered by a stale handle. + //! Returns true if the block id is no longer registered. + virtual bool UnregisterExpiredBlock(block_id_t id); //! Returns a reference to the metadata manager of this block manager. MetadataManager &GetMetadataManager(); diff --git a/src/duckdb/src/include/duckdb/storage/buffer/block_handle.hpp b/src/duckdb/src/include/duckdb/storage/buffer/block_handle.hpp index 331743ad0..26002f55d 100644 --- a/src/duckdb/src/include/duckdb/storage/buffer/block_handle.hpp +++ b/src/duckdb/src/include/duckdb/storage/buffer/block_handle.hpp @@ -29,6 +29,8 @@ class BlockHandle; using BlockLock = unique_lock; +enum class CanUnloadResult : uint8_t { CAN_UNLOAD, ALREADY_UNLOADED, PINNED, NO_TEMP_DIRECTORY }; + class BlockMemory : public enable_shared_from_this { public: BlockMemory(BufferManager &buffer_manager, block_id_t block_id_p, MemoryTag tag_p, idx_t block_alloc_size); @@ -72,19 +74,27 @@ class BlockMemory : public enable_shared_from_this { } //! Returns the number of readers. int32_t GetReaders() const { - return readers; + return readers.load(std::memory_order_relaxed); } //! Increments the number of readers prior to returning it. - int32_t IncrementReaders() { - return ++readers; + int32_t IncrementReaders(BlockLock &l) { + VerifyMutex(l); + // Tiny buffers do not decrement on unpin, so their reader count can wrap. + auto new_readers = static_cast(static_cast(readers.load(std::memory_order_relaxed)) + 1); + readers.store(new_readers, std::memory_order_relaxed); + return new_readers; } //! Decrements the number of readers prior to returning it. - int32_t DecrementReaders() { - return --readers; + int32_t DecrementReaders(BlockLock &l) { + VerifyMutex(l); + auto new_readers = static_cast(static_cast(readers.load(std::memory_order_relaxed)) - 1); + readers.store(new_readers, std::memory_order_relaxed); + return new_readers; } //! Sets the number of readers. - void SetReaders(int32_t n) { - readers = n; + void SetReaders(BlockLock &l, int32_t n) { + VerifyMutex(l); + readers.store(n, std::memory_order_relaxed); } //! Returns the memory tag. MemoryTag GetMemoryTag() const { @@ -109,11 +119,15 @@ class BlockMemory : public enable_shared_from_this { } //! Returns the eviction sequence number. idx_t GetEvictionSequenceNumber() const { - return eviction_seq_num; + return eviction_seq_num.load(std::memory_order_acquire); } //! Increments the eviction sequence number prior to returning it. - idx_t NextEvictionSequenceNumber() { - return ++eviction_seq_num; + idx_t NextEvictionSequenceNumber(BlockLock &l) { + VerifyMutex(l); + auto new_sequence_number = eviction_seq_num.load(std::memory_order_relaxed) + 1; + // Publish preceding dead-node accounting to lock-free queue readers. + eviction_seq_num.store(new_sequence_number, std::memory_order_release); + return new_sequence_number; } //! Returns true, if the block has a live (not yet dead-counted) entry in the eviction queue. bool HasLiveQueueEntry(BlockLock &l) const { @@ -150,6 +164,13 @@ class BlockMemory : public enable_shared_from_this { bool MustWriteToTemporaryFile() const { return destroy_buffer_upon == DestroyBufferUpon::BLOCK; } + //! Returns true, if the buffer can be loaded again after it was destroyed, i.e., it is backed + //! by its disk block or kept alive in a temporary file. False for buffers that are destroyed + //! upon eviction/unpin - including replaced persistent handles whose disk block may be + //! rewritten in place and must not be read through them again. + bool CanReload() const { + return destroy_buffer_upon == DestroyBufferUpon::BLOCK; + } //! Returns the memory usage. idx_t GetMemoryUsage() const { return memory_usage; @@ -177,14 +198,6 @@ class BlockMemory : public enable_shared_from_this { VerifyMutex(l); memory_charge.Merge(std::move(reservation)); } - //! Returns true, if there is a swizzled memory pointer, else false. - bool IsSwizzled() const { - return !unswizzled; - } - //! Sets the swizzled memory pointer. - void SetSwizzling(const char *unswizzler) { - unswizzled = unswizzler; - } //! Sets the eviction queue index. void SetEvictionQueueIndex(const idx_t index) { // The index can only be set once. @@ -202,10 +215,10 @@ class BlockMemory : public enable_shared_from_this { void ChangeMemoryUsage(BlockLock &l, int64_t delta); void ConvertToPersistent(BlockLock &l, BlockHandle &new_block, unique_ptr new_buffer); void ResizeBuffer(BlockLock &l, idx_t block_size, idx_t block_header_size, int64_t memory_delta); - //! Returns whether the block can be unloaded or not. + //! Returns whether the block can be unloaded or the reason why it cannot be unloaded. //! The state here can change if the block lock is held. //! However, this method does not hold the block lock. - bool CanUnload() const; + CanUnloadResult CanUnload() const; unique_ptr UnloadAndTakeBlock(BlockLock &l, QueryContext context = QueryContext()); void Unload(BlockLock &l, QueryContext context = QueryContext()); @@ -218,7 +231,7 @@ class BlockMemory : public enable_shared_from_this { mutex lock; //! Whether the block is loaded or unloaded. atomic state; - //! The number of concurrent readers. + //! The number of concurrent readers. Writers hold the block lock; reads may be unlocked. atomic readers; //! The memory tag. const MemoryTag tag; @@ -227,7 +240,7 @@ class BlockMemory : public enable_shared_from_this { //! A pointer to the loaded data, if any. unique_ptr buffer; //! The internal eviction sequence number. Monotonic: it is never reset, so an eviction queue - //! entry is stale if and only if its sequence number differs from this one. + //! entry is stale if and only if its sequence number differs from this one. Writers hold the block lock. atomic eviction_seq_num; //! Whether the block has a live entry in the eviction queue, i.e., an entry whose sequence //! number matches eviction_seq_num and which has not been counted as a dead node. @@ -242,8 +255,6 @@ class BlockMemory : public enable_shared_from_this { atomic memory_usage; //! The current memory reservation/usage. BufferPoolReservation memory_charge; - //! Swizzled memory pointers. - const char *unswizzled; //! The eviction queue index, currently only FileBufferType::MANAGED_BUFFER. atomic eviction_queue_idx; }; @@ -294,7 +305,7 @@ class BlockHandle : public enable_shared_from_this { public: BufferHandle LoadFromBuffer(BlockLock &l, data_ptr_t data, unique_ptr reusable_buffer, BufferPoolReservation reservation); - BufferHandle Load(QueryContext context, unique_ptr buffer = nullptr); + BufferHandle Load(BlockLock &l, QueryContext context, unique_ptr buffer = nullptr); private: //! The block manager, which loads the block. diff --git a/src/duckdb/src/include/duckdb/storage/buffer/buffer_pool.hpp b/src/duckdb/src/include/duckdb/storage/buffer/buffer_pool.hpp index 6d2c3aa39..3a04f77f5 100644 --- a/src/duckdb/src/include/duckdb/storage/buffer/buffer_pool.hpp +++ b/src/duckdb/src/include/duckdb/storage/buffer/buffer_pool.hpp @@ -98,6 +98,13 @@ class BufferPool { //! Evict object cache entries if needed. EvictionResult EvictObjectCacheEntries(MemoryTag tag, idx_t extra_memory, idx_t memory_limit); + //! Return freed memory to the OS, optionally releasing up to extra_memory from the block allocator's pool + void FlushAllocator(optional_idx extra_memory = optional_idx()); + //! Flush the allocator if the memory freed since the last flush could push resident memory past the limit + void FlushOnBulkDeallocation(); + //! Freed memory that has to accumulate before the allocator is flushed + idx_t GetBulkDeallocationFlushThreshold() const; + //! Purge all blocks that haven't been pinned within the last N seconds idx_t PurgeAgedBlocks(uint32_t max_age_sec); idx_t PurgeAgedBlocksInternal(EvictionQueue &queue, uint32_t max_age_sec, int64_t now, int64_t limit); @@ -128,16 +135,23 @@ class BufferPool { }; struct MemoryUsage { - //! The maximum difference between memory statistics and actual usage is 2MB (64 * 32k) + //! Total memory accounting differs from actual usage by at most 2MB (64 * 32k) static constexpr idx_t MEMORY_USAGE_CACHE_COUNT = 64; static constexpr idx_t MEMORY_USAGE_CACHE_THRESHOLD = 32 << 10; + //! Per-tag statistics are flushed when read and can use a larger cache threshold. + static constexpr idx_t MEMORY_USAGE_TAG_CACHE_THRESHOLD = 1 << 20; static constexpr idx_t TOTAL_MEMORY_USAGE_INDEX = MEMORY_TAG_COUNT; using MemoryUsageCounters = array, MEMORY_TAG_COUNT + 1>; + //! Keep neighboring caches separate on machines with 64- or 128-byte cache lines. + struct alignas(128) MemoryUsageCache { + MemoryUsageCounters counters; + }; + //! global memory usage counters MemoryUsageCounters memory_usage; //! cache memory usage to improve performance - array memory_usage_caches; + array memory_usage_caches; MemoryUsage(); @@ -156,7 +170,7 @@ class BufferPool { } int64_t cached = 0; for (auto &cache : memory_usage_caches) { - cached += cache[index].exchange(0, std::memory_order_relaxed); + cached += cache.counters[index].exchange(0, std::memory_order_relaxed); } auto used_memory = memory_usage[index].fetch_add(cached, std::memory_order_relaxed) + cached; return used_memory > 0 ? static_cast(used_memory) : 0; @@ -171,6 +185,8 @@ class BufferPool { atomic maximum_memory; //! If bulk deallocation larger than this occurs, flush outstanding allocations atomic allocator_bulk_deallocation_flush_threshold; + //! Freed memory is flushed once it exceeds this fraction of the memory limit + static constexpr idx_t BULK_DEALLOCATION_FLUSH_DIVISOR = 16; //! Record timestamps of buffer manager unpin() events. Usable by custom eviction policies. bool track_eviction_timestamps; //! Eviction queues diff --git a/src/duckdb/src/include/duckdb/storage/caching_mode.hpp b/src/duckdb/src/include/duckdb/storage/caching_mode.hpp index 564e008d1..3f9992511 100644 --- a/src/duckdb/src/include/duckdb/storage/caching_mode.hpp +++ b/src/duckdb/src/include/duckdb/storage/caching_mode.hpp @@ -23,4 +23,19 @@ enum class CachingMode : uint8_t { NO_CACHING = 2, }; +enum class RequestSizing : uint8_t { + BY_CACHE = 0, + BY_READER = 1, +}; + +//! How reads of cached files are sized, overriding the RequestSizing of their readers. +enum class ExternalFileCacheRequestSizing : uint8_t { + // Each reader chooses through its RequestSizing. + AUTO = 0, + // Every read covers the aligned blocks of the cache block size around it. + GRID = 1, + // Every read covers exactly the bytes it requests. + EXACT = 2, +}; + } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/storage/checkpoint/string_checkpoint_state.hpp b/src/duckdb/src/include/duckdb/storage/checkpoint/string_checkpoint_state.hpp index a598e74a9..594ad9112 100644 --- a/src/duckdb/src/include/duckdb/storage/checkpoint/string_checkpoint_state.hpp +++ b/src/duckdb/src/include/duckdb/storage/checkpoint/string_checkpoint_state.hpp @@ -9,6 +9,7 @@ #pragma once #include "duckdb/common/common.hpp" +#include "duckdb/common/unordered_set.hpp" #include "duckdb/storage/storage_info.hpp" #include "duckdb/storage/buffer/block_handle.hpp" #include "duckdb/function/compression_function.hpp" @@ -45,10 +46,15 @@ struct UncompressedStringSegmentState : public CompressedSegmentState { unique_ptr overflow_writer; //! The block manager with which to write optional_ptr block_manager; - //! The set of overflow blocks written to disk (if any) - vector on_disk_blocks; public: + void InitializeOnDiskBlocks(vector blocks); + + const vector &GetOnDiskBlocks() const { + D_ASSERT(on_disk_blocks.size() == on_disk_block_set.size()); + return on_disk_blocks; + } + shared_ptr GetHandle(BlockManager &manager, block_id_t block_id); void RegisterBlock(BlockManager &manager, block_id_t block_id); @@ -59,6 +65,11 @@ struct UncompressedStringSegmentState : public CompressedSegmentState { reference FindOverflowBlock(block_id_t block_id); private: + //! Overflow block IDs in write order, preserved for serialization. + vector on_disk_blocks; + //! Membership index for on_disk_blocks. + unordered_set on_disk_block_set; + mutex block_lock; unordered_map> handles; diff --git a/src/duckdb/src/include/duckdb/storage/checkpoint/table_data_writer.hpp b/src/duckdb/src/include/duckdb/storage/checkpoint/table_data_writer.hpp index 19bf21a6b..c7c8ac85b 100644 --- a/src/duckdb/src/include/duckdb/storage/checkpoint/table_data_writer.hpp +++ b/src/duckdb/src/include/duckdb/storage/checkpoint/table_data_writer.hpp @@ -38,6 +38,7 @@ class TableDataWriter { virtual void WriteUnchangedTable(MetaBlockPointer pointer, const vector &metadata_pointers, idx_t total_rows, idx_t next_row_id) = 0; + //! global_stats is empty when reusing existing table metadata. virtual void FinalizeTable(const TableStatistics &global_stats, DataTableInfo &info, RowGroupCollection &collection, const vector> &index_infos, Serializer &serializer) = 0; diff --git a/src/duckdb/src/include/duckdb/storage/checkpoint_manager.hpp b/src/duckdb/src/include/duckdb/storage/checkpoint_manager.hpp index 5cca2ef96..f7db33e2f 100644 --- a/src/duckdb/src/include/duckdb/storage/checkpoint_manager.hpp +++ b/src/duckdb/src/include/duckdb/storage/checkpoint_manager.hpp @@ -56,6 +56,8 @@ class ActiveCheckpointWrapper { //! Registers the checkpoint (id, bound, active checkpoint), through a checkpoint transaction if there is a context void Begin(CheckpointOptions &options); void Commit(); + //! The context of the checkpoint connection, null when there is none (shutdown path) + optional_ptr GetCheckpointContext() const; private: AttachedDatabase &db; @@ -165,6 +167,9 @@ class SingleFileCheckpointWriter final : public CheckpointWriter { private: optional_ptr context; + //! Context of the checkpoint transaction, used for catalog lookups during the checkpoint. + //! Only set while CreateCheckpoint runs, which owns the connection it belongs to. + optional_ptr checkpoint_context; //! The metadata writer is responsible for writing schema information unique_ptr metadata_writer; //! The table data writer is responsible for writing the DataPointers used by the table chunks diff --git a/src/duckdb/src/include/duckdb/storage/compression/alp/alp_utils.hpp b/src/duckdb/src/include/duckdb/storage/compression/alp/alp_utils.hpp index dbb6fbd91..fd56272ec 100644 --- a/src/duckdb/src/include/duckdb/storage/compression/alp/alp_utils.hpp +++ b/src/duckdb/src/include/duckdb/storage/compression/alp/alp_utils.hpp @@ -67,22 +67,24 @@ class AlpUtils { } template - static T FindFirstValueNotInPositionsArray(const T *input_vector, const uint16_t *positions, idx_t values_count) { - T a_non_special_value = 0; + static T FindFirstValueNotInPositionsArray(const T *input_vector, const uint16_t *null_positions, + idx_t values_count, idx_t nulls_count) { + idx_t null_idx = 0; for (idx_t i = 0; i < values_count; i++) { - if (i != positions[i]) { - a_non_special_value = input_vector[i]; - break; + if (null_idx < nulls_count && null_positions[null_idx] == i) { + null_idx++; + continue; } + return input_vector[i]; } - return a_non_special_value; + return T(0); } template - static void ReplaceValueInVectorPositions(T *input_vector, const uint16_t *positions_to_replace, - idx_t special_values_count, T value_to_replace) { - for (idx_t i = 0; i < special_values_count; i++) { - uint16_t null_value_pos = positions_to_replace[i]; + static void ReplaceValueInVectorPositions(T *input_vector, const uint16_t *null_positions, idx_t nulls_count, + T value_to_replace) { + for (idx_t i = 0; i < nulls_count; i++) { + uint16_t null_value_pos = null_positions[i]; input_vector[null_value_pos] = value_to_replace; } } @@ -93,7 +95,8 @@ class AlpUtils { if (nulls_count == 0) { return; } - T a_non_null_value = FindFirstValueNotInPositionsArray(input_vector, vector_null_positions, values_count); + T a_non_null_value = + FindFirstValueNotInPositionsArray(input_vector, vector_null_positions, values_count, nulls_count); ReplaceValueInVectorPositions(input_vector, vector_null_positions, nulls_count, a_non_null_value); } }; diff --git a/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/bit_reader.hpp b/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/bit_reader.hpp index 901fb0e68..28f943c21 100644 --- a/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/bit_reader.hpp +++ b/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/bit_reader.hpp @@ -8,6 +8,9 @@ #pragma once +#include "duckdb/common/array_ptr.hpp" +#include "duckdb/common/assert.hpp" +#include "duckdb/common/likely.hpp" #include "duckdb/common/numeric_utils.hpp" namespace duckdb { @@ -51,28 +54,41 @@ struct BitReader { }; public: -public: - BitReader() : input(nullptr), index(0) { + explicit BitReader(unsafe_array_ptr input, uint8_t bit_offset = 0) + : input(input), index(bit_offset) { + D_ASSERT(bit_offset < 8); } - uint8_t *input; - uint32_t index; -public: - void SetStream(uint8_t *input) { - this->input = input; - index = 0; + template + inline T ReadValue() { + static_assert(SIZE <= sizeof(T) * 8, "Bit width exceeds the result type"); + constexpr uint8_t BYTES = (SIZE >> 3); + constexpr uint8_t REMAINDER = (SIZE & 7); + return ReadBytes(REMAINDER); + } + + template + inline T ReadValue(const uint8_t &size) { + D_ASSERT(size <= sizeof(T) * 8); + const uint8_t bytes = size >> 3; // divide by 8; + const uint8_t remainder = size & 7; + return ReadBytes(bytes, remainder); } +private: inline uint8_t BitIndex() const { return (index & 7); } - inline uint64_t ByteIndex() const { + inline idx_t ByteIndex() const { return (index >> 3); } inline uint8_t InnerReadByte(const uint8_t &offset) { - uint8_t result = static_cast(input[ByteIndex() + offset] << BitIndex()) | - ((input[ByteIndex() + offset + 1] & REMAINDER_MASKS[8 + BitIndex()]) >> (8 - BitIndex())); + const auto bit_index = BitIndex(); + const bool spill_to_next_byte = bit_index != 0; + uint8_t result = + static_cast(input[ByteIndex() + offset] << bit_index) | + ((input[ByteIndex() + offset + spill_to_next_byte] & REMAINDER_MASKS[8 + bit_index]) >> (8 - bit_index)); return result; } @@ -96,14 +112,14 @@ struct BitReader { // Right-shift this result (the most significant bits) // Sometimes we will need to read from the second byte - // But to make this branchless, we will perform what is basically a no-op if this condition is not true - // SPILL = (index + size >= 8) + // But to make this branchless, we will read from the current byte as a no-op if this condition is not true + // SPILL = (index + size > 8) // // If SPILL is true: // The REMAINDER_MASKS gives us the mask for the bits we're interested in // We bit-wise AND these together (no need to shift anything because the index is essentially zero for this new // byte) And we then right-shift these bits in place (to the right of the previous bits) - const bool spill_to_next_byte = (size + BitIndex() >= 8); + const bool spill_to_next_byte = (size + BitIndex() > 8); uint8_t result = ((input[ByteIndex() + offset] << BitIndex()) & MASKS[size]) >> right_shift | ((input[ByteIndex() + offset + spill_to_next_byte] & REMAINDER_MASKS[size + BitIndex()]) >> bit_remainder); @@ -137,7 +153,9 @@ struct BitReader { if (BYTES > 7) { result = UnsafeNumericCast(result << 8 | InnerReadByte(7)); } - result = UnsafeNumericCast(result << remainder | InnerRead(remainder, BYTES)); + if (remainder > 0) { + result = UnsafeNumericCast(result << remainder | InnerRead(remainder, BYTES)); + } index += (BYTES << 3) + remainder; return result; } @@ -148,24 +166,15 @@ struct BitReader { for (uint8_t i = 0; i < bytes; i++) { result = result << 8 | InnerReadByte(i); } - result = result << remainder | InnerRead(remainder, bytes); - index += static_cast(bytes << 3) + remainder; + if (DUCKDB_LIKELY(remainder > 0)) { + result = result << remainder | InnerRead(remainder, bytes); + } + index += static_cast(bytes << 3) + remainder; return result; } - template - inline T ReadValue() { - constexpr uint8_t BYTES = (SIZE >> 3); - constexpr uint8_t REMAINDER = (SIZE & 7); - return ReadBytes(REMAINDER); - } - - template - inline T ReadValue(const uint8_t &size) { - const uint8_t bytes = size >> 3; // divide by 8; - const uint8_t remainder = size & 7; - return ReadBytes(bytes, remainder); - } + unsafe_array_ptr input; + idx_t index; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/chimp128.hpp b/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/chimp128.hpp index 4dc8aaff4..6985a86e7 100644 --- a/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/chimp128.hpp +++ b/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/chimp128.hpp @@ -207,7 +207,6 @@ struct Chimp128DecompressionState { return trailing_zeros; } - BitReader input; uint8_t leading_zeros; uint8_t trailing_zeros; CHIMP_TYPE reference_value = 0; @@ -229,16 +228,18 @@ struct Chimp128Decompression { } static inline CHIMP_TYPE Load(ChimpConstants::Flags flag, uint8_t leading_zeros[], uint32_t &leading_zero_index, - UnpackedData unpacked_data[], uint32_t &unpacked_index, DecompressState &state) { + UnpackedData unpacked_data[], uint32_t &unpacked_index, DecompressState &state, + BitReader &input) { if (DUCKDB_UNLIKELY(state.first)) { - return LoadFirst(state); + return LoadFirst(state, input); } else { - return DecompressValue(flag, leading_zeros, leading_zero_index, unpacked_data, unpacked_index, state); + return DecompressValue(flag, leading_zeros, leading_zero_index, unpacked_data, unpacked_index, state, + input); } } - static inline CHIMP_TYPE LoadFirst(DecompressState &state) { - CHIMP_TYPE result = state.input.template ReadValue(); + static inline CHIMP_TYPE LoadFirst(DecompressState &state, BitReader &input) { + CHIMP_TYPE result = input.ReadValue(); state.ring_buffer.template InsertScan(result); state.first = false; state.reference_value = result; @@ -247,12 +248,12 @@ struct Chimp128Decompression { static inline CHIMP_TYPE DecompressValue(ChimpConstants::Flags flag, uint8_t leading_zeros[], uint32_t &leading_zero_index, UnpackedData unpacked_data[], - uint32_t &unpacked_index, DecompressState &state) { + uint32_t &unpacked_index, DecompressState &state, BitReader &input) { CHIMP_TYPE result; switch (flag) { case ChimpConstants::Flags::VALUE_IDENTICAL: { //! Value is identical to previous value - auto index = state.input.template ReadValue(); + auto index = input.ReadValue(); result = UnsafeNumericCast(state.ring_buffer.Value(index)); break; } @@ -260,20 +261,20 @@ struct Chimp128Decompression { const UnpackedData &unpacked = unpacked_data[unpacked_index++]; state.leading_zeros = unpacked.leading_zero; state.trailing_zeros = BIT_SIZE - unpacked.significant_bits - state.leading_zeros; - result = state.input.template ReadValue(unpacked.significant_bits); + result = input.ReadValue(unpacked.significant_bits); result <<= state.trailing_zeros; result ^= state.ring_buffer.Value(unpacked.index); break; } case ChimpConstants::Flags::LEADING_ZERO_EQUALITY: { - result = state.input.template ReadValue(BIT_SIZE - state.leading_zeros); + result = input.ReadValue(BIT_SIZE - state.leading_zeros); result ^= state.reference_value; break; } case ChimpConstants::Flags::LEADING_ZERO_LOAD: { state.leading_zeros = leading_zeros[leading_zero_index++]; D_ASSERT(state.leading_zeros <= BIT_SIZE); - result = state.input.template ReadValue(BIT_SIZE - state.leading_zeros); + result = input.ReadValue(BIT_SIZE - state.leading_zeros); result ^= state.reference_value; break; } diff --git a/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/flag_buffer.hpp b/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/flag_buffer.hpp index a31eac7ad..fd193e9b8 100644 --- a/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/flag_buffer.hpp +++ b/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/flag_buffer.hpp @@ -8,6 +8,7 @@ #pragma once +#include "duckdb/common/array_ptr.hpp" #include "duckdb/storage/compression/chimp/algorithm/chimp_utils.hpp" #ifdef DEBUG #include "duckdb/common/vector.hpp" @@ -34,12 +35,13 @@ struct FlagBufferConstants { template class FlagBuffer { public: - FlagBuffer() : counter(0), buffer(nullptr) { + explicit FlagBuffer(unsafe_array_ptr buffer) + : counter(0), read_buffer(buffer), write_buffer(nullptr) { } public: void SetBuffer(uint8_t *buffer) { - this->buffer = buffer; + write_buffer = buffer; this->counter = 0; } void Reset() { @@ -63,7 +65,7 @@ class FlagBuffer { if (!EMPTY) { if ((counter & 3) == 0) { // Start the new byte fresh - buffer[counter >> 2] = 0; + write_buffer[counter >> 2] = 0; #ifdef DEBUG flags.clear(); #endif @@ -71,16 +73,16 @@ class FlagBuffer { #ifdef DEBUG flags.push_back((uint8_t)value); #endif - buffer[counter >> 2] |= (((uint8_t)value & 3) << FlagBufferConstants::SHIFTS[counter & 3]); + write_buffer[counter >> 2] |= (((uint8_t)value & 3) << FlagBufferConstants::SHIFTS[counter & 3]); #ifdef DEBUG // Verify that the bits are serialized correctly - D_ASSERT(flags[counter & 3] == ExtractValue(buffer[counter >> 2], counter & 3)); + D_ASSERT(flags[counter & 3] == ExtractValue(write_buffer[counter >> 2], counter & 3)); #endif } counter++; } inline uint8_t Extract() { - const uint8_t result = (buffer[counter >> 2] & FlagBufferConstants::MASKS[counter & 3]) >> + const uint8_t result = (read_buffer[counter >> 2] & FlagBufferConstants::MASKS[counter & 3]) >> FlagBufferConstants::SHIFTS[counter & 3]; counter++; return result; @@ -97,7 +99,8 @@ class FlagBuffer { private: private: uint32_t counter = 0; - uint8_t *buffer; + unsafe_array_ptr read_buffer; + uint8_t *write_buffer; #ifdef DEBUG vector flags; #endif diff --git a/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/leading_zero_buffer.hpp b/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/leading_zero_buffer.hpp index 04dc130a7..b1f3aead8 100644 --- a/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/leading_zero_buffer.hpp +++ b/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/leading_zero_buffer.hpp @@ -8,6 +8,7 @@ #pragma once +#include "duckdb/common/array_ptr.hpp" #include "duckdb/common/helper.hpp" #include "duckdb/common/numeric_utils.hpp" #ifdef DEBUG @@ -47,25 +48,16 @@ class LeadingZeroBuffer { static constexpr uint32_t MAX_LEADING_ZERO_BLOCKS = CHIMP_GROUP_SIZE / LEADING_ZERO_BLOCK_SIZE; static constexpr uint32_t MAX_BITS_USED_BY_ZERO_BLOCKS = MAX_LEADING_ZERO_BLOCKS * LEADING_ZERO_BLOCK_BIT_SIZE; static constexpr uint32_t MAX_BYTES_USED_BY_ZERO_BLOCKS = MAX_BITS_USED_BY_ZERO_BLOCKS / 8; - - // Add an extra byte to prevent heap buffer overflow on the last group, because we'll be addressing 4 bytes each - static constexpr uint32_t BUFFER_SIZE = - MAX_BYTES_USED_BY_ZERO_BLOCKS + (sizeof(uint32_t) - (LEADING_ZERO_BLOCK_BIT_SIZE / 8)); - - template - const T Load(const uint8_t *ptr) { - T ret; - memcpy(&ret, ptr, sizeof(ret)); - return ret; - } + static constexpr uint32_t BUFFER_SIZE = MAX_BYTES_USED_BY_ZERO_BLOCKS; public: - LeadingZeroBuffer() : current(0), counter(0), buffer(nullptr) { + explicit LeadingZeroBuffer(unsafe_array_ptr buffer) + : current(0), counter(0), read_buffer(buffer), write_buffer(nullptr) { } void SetBuffer(uint8_t *buffer) { // Set the internal buffer, when inserting this should be BUFFER_SIZE bytes in length // This buffer does not need to be zero-initialized for inserting - this->buffer = buffer; + write_buffer = buffer; this->counter = 0; } void Flush() { @@ -104,12 +96,12 @@ class LeadingZeroBuffer { return; } const auto buffer_idx = BlockIndex(); - memcpy((void *)(buffer + buffer_idx), (uint8_t *)¤t, 3); + memcpy((void *)(write_buffer + buffer_idx), (uint8_t *)¤t, 3); #ifdef DEBUG // Verify that the bits are copied correctly uint32_t temp_value = 0; - memcpy(reinterpret_cast(&temp_value), (void *)(buffer + buffer_idx), 3); + memcpy(reinterpret_cast(&temp_value), (void *)(write_buffer + buffer_idx), 3); for (idx_t i = 0; i < flags.size(); i++) { D_ASSERT(flags[i] == ExtractValue(temp_value, i)); } @@ -138,7 +130,8 @@ class LeadingZeroBuffer { inline uint8_t Extract() { const auto buffer_idx = BlockIndex(); - auto const temp = Load(buffer + buffer_idx); + uint32_t temp = 0; + memcpy(&temp, read_buffer.data() + buffer_idx, 3); const uint8_t result = UnsafeNumericCast((temp & LeadingZeroBufferConstants::MASKS[counter & 7]) >> LeadingZeroBufferConstants::SHIFTS[counter & 7]); @@ -156,7 +149,8 @@ class LeadingZeroBuffer { private: uint32_t current; uint32_t counter = 0; // block_index * 8 - uint8_t *buffer; + unsafe_array_ptr read_buffer; + uint8_t *write_buffer; #ifdef DEBUG vector flags; #endif diff --git a/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/packed_data.hpp b/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/packed_data.hpp index b2d432d5d..eaa896852 100644 --- a/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/packed_data.hpp +++ b/src/duckdb/src/include/duckdb/storage/compression/chimp/algorithm/packed_data.hpp @@ -39,10 +39,17 @@ struct PackedDataUtils { // IIIIIII //! Index (7 bits, shifted by 9) // LLL //! LeadingZeros (3 bits, shifted by 6) // SSSSSS //! SignificantBits (6 bits) + static inline UnpackedData Unpack(uint16_t packed_data) { + UnpackedData result; + result.index = packed_data >> INDEX_SHIFT_AMOUNT & INDEX_MASK; + result.leading_zero = packed_data >> LEADING_SHIFT_AMOUNT & LEADING_MASK; + result.significant_bits = packed_data & SignificantBits::mask; + return result; + } + + // FIXME: remove once chimp has been moved over to the above. static inline void Unpack(uint16_t packed_data, UnpackedData &dest) { - dest.index = packed_data >> INDEX_SHIFT_AMOUNT & INDEX_MASK; - dest.leading_zero = packed_data >> LEADING_SHIFT_AMOUNT & LEADING_MASK; - dest.significant_bits = packed_data & SignificantBits::mask; + dest = Unpack(packed_data); // Verify that combined, this is not bigger than the full size of the type D_ASSERT(dest.significant_bits + dest.leading_zero <= (sizeof(CHIMP_TYPE) * 8)); } diff --git a/src/duckdb/src/include/duckdb/storage/compression/chimp/chimp.hpp b/src/duckdb/src/include/duckdb/storage/compression/chimp/chimp.hpp index 85bcb6ea3..70653c998 100644 --- a/src/duckdb/src/include/duckdb/storage/compression/chimp/chimp.hpp +++ b/src/duckdb/src/include/duckdb/storage/compression/chimp/chimp.hpp @@ -29,9 +29,11 @@ struct ChimpType { class ChimpPrimitives { public: + using METADATA_POINTER_TYPE = uint32_t; + static constexpr uint32_t CHIMP_SEQUENCE_SIZE = 1024; static constexpr uint8_t MAX_BYTES_PER_VALUE = sizeof(double) + 1; // extra wiggle room - static constexpr uint8_t HEADER_SIZE = sizeof(uint32_t); + static constexpr uint8_t HEADER_SIZE = sizeof(METADATA_POINTER_TYPE); static constexpr uint8_t FLAG_BIT_SIZE = 2; static constexpr uint32_t LEADING_ZERO_BLOCK_BUFFERSIZE = 1 + (CHIMP_SEQUENCE_SIZE / 8) * 3; }; diff --git a/src/duckdb/src/include/duckdb/storage/compression/chimp/chimp_fetch.hpp b/src/duckdb/src/include/duckdb/storage/compression/chimp/chimp_fetch.hpp index e44457b4d..8261fcd75 100644 --- a/src/duckdb/src/include/duckdb/storage/compression/chimp/chimp_fetch.hpp +++ b/src/duckdb/src/include/duckdb/storage/compression/chimp/chimp_fetch.hpp @@ -19,17 +19,21 @@ namespace duckdb { template void ChimpFetchRow(ColumnSegment &segment, ColumnFetchState &state, row_t row_id, Vector &result, idx_t result_idx) { using INTERNAL_TYPE = typename ChimpType::TYPE; + D_ASSERT(row_id >= 0); + D_ASSERT(UnsafeNumericCast(row_id) < segment.count); - ChimpScanState scan_state(segment); - scan_state.Skip(segment, UnsafeNumericCast(row_id)); - auto result_data = FlatVector::GetDataMutable(result); + auto &buffer_manager = BufferManager::GetBufferManager(segment.GetDatabase()); + auto handle = buffer_manager.Pin(state.context, segment.GetBlockHandle()); + auto scan_state = make_uniq>(std::move(handle), segment); + scan_state->Skip(segment, UnsafeNumericCast(row_id)); + auto result_data = FlatVector::GetDataMutableUnsafe(result); - if (scan_state.GroupFinished() && scan_state.total_value_count < scan_state.segment_count) { - scan_state.LoadGroup(scan_state.group_state.values); + if (scan_state->GroupFinished() && scan_state->total_value_count < scan_state->segment_count) { + scan_state->LoadGroup(scan_state->group_state.values); } - scan_state.group_state.Scan(&result_data[result_idx], 1); + scan_state->group_state.Scan(&result_data[result_idx], 1); - scan_state.total_value_count++; + scan_state->total_value_count++; } } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/storage/compression/chimp/chimp_scan.hpp b/src/duckdb/src/include/duckdb/storage/compression/chimp/chimp_scan.hpp index 2e87685bd..829b00981 100644 --- a/src/duckdb/src/include/duckdb/storage/compression/chimp/chimp_scan.hpp +++ b/src/duckdb/src/include/duckdb/storage/compression/chimp/chimp_scan.hpp @@ -16,6 +16,7 @@ #include "duckdb/common/vector/flat_vector.hpp" #include "duckdb/function/compression_function.hpp" #include "duckdb/storage/buffer_manager.hpp" +#include "duckdb/storage/compression/compression_segment_reader.hpp" #include "duckdb/storage/table/column_segment.hpp" @@ -25,14 +26,15 @@ namespace duckdb { +[[noreturn]] void ThrowChimpMetadataBeforeHeader(); +[[noreturn]] void ThrowChimpLeadingZeroBlockCountOutOfBounds(uint8_t block_count); +[[noreturn]] void ThrowChimpLeadingZeroCountMismatch(idx_t stored_count, idx_t required_count); +[[noreturn]] void ThrowChimpPackedDataExceedsType(uint8_t leading_zero, uint8_t significant_bits, idx_t bit_width); +[[noreturn]] void ThrowChimpLeadingZeroStateMissing(); + template struct ChimpGroupState { public: - void Init(uint8_t *data) { - chimp_state.input.SetStream(data); - Reset(); - } - void Reset() { chimp_state.Reset(); index = 0; @@ -52,33 +54,22 @@ struct ChimpGroupState { index += count; } - void LoadFlags(uint8_t *packed_data, idx_t group_size) { - FlagBuffer flag_buffer; - flag_buffer.SetBuffer(packed_data); + void LoadFlags(unsafe_array_ptr packed_data, idx_t group_size) { + FlagBuffer flag_buffer(packed_data); flags[0] = ChimpConstants::Flags::VALUE_IDENTICAL; // First value doesn't require a flag for (idx_t i = 0; i < group_size; i++) { - flags[1 + i] = (ChimpConstants::Flags)flag_buffer.Extract(); + flags[1 + i] = static_cast(flag_buffer.Extract()); } max_flags_to_read = group_size; index = 0; } - void LoadLeadingZeros(uint8_t *packed_data, idx_t leading_zero_block_size) { -#ifdef DEBUG - idx_t flag_one_count = 0; - for (idx_t i = 0; i < max_flags_to_read; i++) { - flag_one_count += flags[1 + i] == ChimpConstants::Flags::LEADING_ZERO_LOAD; - } - // There are 8 leading zero values packed in one block, the block could be partially filled - flag_one_count = AlignValue(flag_one_count); - D_ASSERT(flag_one_count == leading_zero_block_size); -#endif - LeadingZeroBuffer leading_zero_buffer; - leading_zero_buffer.SetBuffer(packed_data); - for (idx_t i = 0; i < leading_zero_block_size; i++) { + void LoadLeadingZeros(unsafe_array_ptr packed_data, idx_t leading_zero_count) { + LeadingZeroBuffer leading_zero_buffer(packed_data); + for (idx_t i = 0; i < leading_zero_count; i++) { leading_zeros[i] = ChimpConstants::Decompression::LEADING_REPRESENTATION[leading_zero_buffer.Extract()]; } - max_leading_zeros_to_read = leading_zero_block_size; + max_leading_zeros_to_read = leading_zero_count; leading_zero_index = 0; } @@ -90,23 +81,80 @@ struct ChimpGroupState { return count; } - void LoadPackedData(uint16_t *packed_data, idx_t packed_data_block_count) { + idx_t CalculateLeadingZeroCount() const { + idx_t count = 0; + for (idx_t i = 0; i < max_flags_to_read; i++) { + count += flags[1 + i] == ChimpConstants::Flags::LEADING_ZERO_LOAD; + } + return AlignValue(count); + } + + void LoadPackedData(unsafe_array_ptr packed_data) { + auto packed_data_block_count = packed_data.size(); for (idx_t i = 0; i < packed_data_block_count; i++) { - PackedDataUtils::Unpack(packed_data[i], unpacked_data_blocks[i]); - if (unpacked_data_blocks[i].significant_bits == 0) { - unpacked_data_blocks[i].significant_bits = 64; + auto unpacked = PackedDataUtils::Unpack(packed_data[i]); + if (unpacked.significant_bits == 0) { + unpacked.significant_bits = 64; } - unpacked_data_blocks[i].leading_zero = - ChimpConstants::Decompression::LEADING_REPRESENTATION[unpacked_data_blocks[i].leading_zero]; + unpacked.leading_zero = ChimpConstants::Decompression::LEADING_REPRESENTATION[unpacked.leading_zero]; + unpacked_data_blocks[i] = unpacked; } unpacked_index = 0; max_packed_data_to_read = packed_data_block_count; } - void LoadValues(CHIMP_TYPE *result, idx_t count) { + // Count how many bits the decoder should read based on the metadata. + // LoadGroup checks the resulting range through CompressionSegmentReader before the BitReader reads any values. + idx_t ValidateAndCalculateDataBitCount(idx_t group_size) const { + using Decompression = Chimp128Decompression; + D_ASSERT(group_size == max_flags_to_read + 1); + + // The first value is stored at full width and has no flag. + idx_t data_bit_count = Decompression::BIT_SIZE; + idx_t leading_zero_position = 0; + idx_t packed_data_position = 0; + uint8_t leading_zero = NumericLimits::Maximum(); + for (idx_t i = 1; i < group_size; i++) { + switch (flags[i]) { + case ChimpConstants::Flags::VALUE_IDENTICAL: + // Identical values still read a ring-buffer index from the bitstream. + data_bit_count += Decompression::INDEX_BITS_SIZE; + break; + case ChimpConstants::Flags::TRAILING_EXCEEDS_THRESHOLD: { + D_ASSERT(packed_data_position < max_packed_data_to_read); + auto &unpacked = unpacked_data_blocks[packed_data_position++]; + // The decoder derives its left shift as BIT_SIZE - significant_bits - leading_zero. + if (unpacked.leading_zero > Decompression::BIT_SIZE || + unpacked.significant_bits > Decompression::BIT_SIZE - unpacked.leading_zero) { + ThrowChimpPackedDataExceedsType(unpacked.leading_zero, unpacked.significant_bits, + Decompression::BIT_SIZE); + } + leading_zero = unpacked.leading_zero; + data_bit_count += unpacked.significant_bits; + break; + } + case ChimpConstants::Flags::LEADING_ZERO_EQUALITY: + // Equality reuses the decoder's leading-zero count, the initial value of 255 is invalid. + if (leading_zero > Decompression::BIT_SIZE) { + ThrowChimpLeadingZeroStateMissing(); + } + data_bit_count += Decompression::BIT_SIZE - leading_zero; + break; + case ChimpConstants::Flags::LEADING_ZERO_LOAD: + D_ASSERT(leading_zero_position < max_leading_zeros_to_read); + leading_zero = leading_zeros[leading_zero_position++]; + data_bit_count += Decompression::BIT_SIZE - leading_zero; + break; + } + } + D_ASSERT(packed_data_position == max_packed_data_to_read); + return data_bit_count; + } + + void LoadValues(BitReader &input, CHIMP_TYPE *result, idx_t count) { for (idx_t i = 0; i < count; i++) { - result[i] = Chimp128Decompression::Load(flags[i], leading_zeros, leading_zero_index, - unpacked_data_blocks, unpacked_index, chimp_state); + result[i] = Chimp128Decompression::Load( + flags[i], leading_zeros, leading_zero_index, unpacked_data_blocks, unpacked_index, chimp_state, input); } } @@ -121,7 +169,7 @@ struct ChimpGroupState { CHIMP_TYPE values[ChimpPrimitives::CHIMP_SEQUENCE_SIZE]; private: - idx_t index; + idx_t index = 0; idx_t max_leading_zeros_to_read; idx_t max_flags_to_read; idx_t max_packed_data_to_read; @@ -133,22 +181,23 @@ struct ChimpScanState : public SegmentScanState { public: using CHIMP_TYPE = typename ChimpType::TYPE; - explicit ChimpScanState(ColumnSegment &segment) : segment(segment), segment_count(segment.count) { - auto &buffer_manager = BufferManager::GetBufferManager(segment.GetDatabase()); - - handle = buffer_manager.Pin(segment.GetBlockHandle()); - auto dataptr = handle.GetDataMutable(); - // ScanStates never exceed the boundaries of a Segment, - // but are not guaranteed to start at the beginning of the Block - auto start_of_data_segment = dataptr + segment.GetBlockOffset() + ChimpPrimitives::HEADER_SIZE; - group_state.Init(start_of_data_segment); - auto metadata_offset = Load(dataptr + segment.GetBlockOffset()); - metadata_ptr = dataptr + segment.GetBlockOffset() + metadata_offset; + explicit ChimpScanState(BufferHandle handle_p, ColumnSegment &segment) + : handle(std::move(handle_p)), + metadata(CompressionSegmentReader::FromSegment(handle, segment, "Chimp segment")), segment(segment), + segment_count(segment.count) { + auto metadata_end = metadata.template Read(); + if (metadata_end < ChimpPrimitives::HEADER_SIZE) { + ThrowChimpMetadataBeforeHeader(); + } + metadata = metadata.GetSubReader(0, metadata_end, "Chimp segment"); + + metadata.SetPosition(metadata_end); } BufferHandle handle; - data_ptr_t metadata_ptr; + CompressionSegmentReader metadata; idx_t total_value_count = 0; + idx_t data_bit_position = 0; ChimpGroupState group_state; ColumnSegment &segment; @@ -181,25 +230,24 @@ struct ChimpScanState : public SegmentScanState { } void LoadGroup(CHIMP_TYPE *value_buffer) { + D_ASSERT(GroupFinished()); + D_ASSERT(total_value_count < segment_count); + //! FIXME: If we change the order of this to flag -> leading_zero_blocks -> packed_data //! We can leave out the leading zero block count as well, because it can be derived from //! Extracting all the flags and counting the 3's - // Load the offset indicating where a groups data starts - metadata_ptr -= sizeof(uint32_t); - auto data_byte_offset = Load(metadata_ptr); - D_ASSERT(data_byte_offset < segment.GetBlockSize()); - // Only used for point queries - (void)data_byte_offset; + // The stored group data offset is unused by scan and fetch + (void)metadata.ReadBackward(); // Load how many blocks of leading zero bits we have - metadata_ptr -= sizeof(uint8_t); - auto leading_zero_block_count = Load(metadata_ptr); - D_ASSERT(leading_zero_block_count <= ChimpPrimitives::CHIMP_SEQUENCE_SIZE / 8); + auto leading_zero_block_count = metadata.ReadBackward(); + if (leading_zero_block_count > ChimpPrimitives::CHIMP_SEQUENCE_SIZE / 8) { + ThrowChimpLeadingZeroBlockCountOutOfBounds(leading_zero_block_count); + } - // Load the leading zero block count - metadata_ptr -= 3ULL * leading_zero_block_count; - const auto leading_zero_block_ptr = metadata_ptr; + // Load the leading zero blocks + auto leading_zero_blocks = metadata.ReadBytesBackward(3ULL * leading_zero_block_count); // Figure out how many flags there are D_ASSERT(segment_count >= total_value_count); @@ -209,26 +257,37 @@ struct ChimpScanState : public SegmentScanState { uint16_t flag_byte_count = AlignValue(UnsafeNumericCast(flag_count)) / 4; // Load the flags - metadata_ptr -= flag_byte_count; - auto flags = metadata_ptr; + auto flags = metadata.ReadBytesBackward(flag_byte_count); group_state.LoadFlags(flags, flag_count); // Load the leading zero blocks - group_state.LoadLeadingZeros(leading_zero_block_ptr, (uint32_t)leading_zero_block_count * 8); + auto leading_zero_count = static_cast(leading_zero_block_count) * 8; + auto required_leading_zero_count = group_state.CalculateLeadingZeroCount(); + if (leading_zero_count != required_leading_zero_count) { + ThrowChimpLeadingZeroCountMismatch(leading_zero_count, required_leading_zero_count); + } + group_state.LoadLeadingZeros(leading_zero_blocks, leading_zero_count); // Load packed data blocks auto packed_data_block_count = group_state.CalculatePackedDataCount(); - metadata_ptr -= packed_data_block_count * 2; - if ((uint64_t)metadata_ptr & 1) { - // Align on a two-byte boundary - metadata_ptr--; - } - group_state.LoadPackedData((uint16_t *)metadata_ptr, packed_data_block_count); - + // Align (backwards) on a two-byte boundary + metadata.AlignBackward(sizeof(uint16_t)); + auto packed_data = metadata.ReadArrayBackward(packed_data_block_count); + group_state.LoadPackedData(packed_data); + + // Validate the full group data range before using the unchecked bit reader + auto data_bit_count = group_state.ValidateAndCalculateDataBitCount(group_size); + auto bit_offset = UnsafeNumericCast(data_bit_position & 7); + auto data_byte_position = data_bit_position >> 3; + auto data_byte_count = (static_cast(bit_offset) + data_bit_count + 7) / 8; + auto data = metadata.GetSubReader(ChimpPrimitives::HEADER_SIZE, metadata.Size() - ChimpPrimitives::HEADER_SIZE, + "Chimp data"); + BitReader input(data.GetBytes(data_byte_position, data_byte_count), bit_offset); group_state.Reset(); // Load all values for the group - group_state.LoadValues(value_buffer, group_size); + group_state.LoadValues(input, value_buffer, group_size); + data_bit_position += data_bit_count; } public: @@ -237,6 +296,8 @@ struct ChimpScanState : public SegmentScanState { void Skip(ColumnSegment &segment, idx_t skip_count) { using INTERNAL_TYPE = typename ChimpType::TYPE; INTERNAL_TYPE buffer[ChimpPrimitives::CHIMP_SEQUENCE_SIZE]; + D_ASSERT(total_value_count <= segment_count); + D_ASSERT(skip_count <= segment_count - total_value_count); while (skip_count) { auto skip_size = MinValue(skip_count, LeftInGroup()); @@ -248,7 +309,9 @@ struct ChimpScanState : public SegmentScanState { template unique_ptr ChimpInitScan(const QueryContext &context, ColumnSegment &segment) { - auto result = make_uniq_base>(segment); + auto &buffer_manager = BufferManager::GetBufferManager(segment.GetDatabase()); + auto handle = buffer_manager.Pin(context, segment.GetBlockHandle()); + auto result = make_uniq_base>(std::move(handle), segment); return result; } @@ -260,6 +323,8 @@ void ChimpScanPartial(ColumnSegment &segment, ColumnScanState &state, idx_t scan idx_t result_offset) { using INTERNAL_TYPE = typename ChimpType::TYPE; auto &scan_state = state.scan_state->Cast>(); + D_ASSERT(scan_state.total_value_count <= scan_state.segment_count); + D_ASSERT(scan_count <= scan_state.segment_count - scan_state.total_value_count); T *result_data = FlatVector::GetDataMutable(result); result.SetVectorType(VectorType::FLAT_VECTOR); diff --git a/src/duckdb/src/include/duckdb/storage/compression/compression_segment_reader.hpp b/src/duckdb/src/include/duckdb/storage/compression/compression_segment_reader.hpp index 8bdfa894d..ab7be12db 100644 --- a/src/duckdb/src/include/duckdb/storage/compression/compression_segment_reader.hpp +++ b/src/duckdb/src/include/duckdb/storage/compression/compression_segment_reader.hpp @@ -80,6 +80,27 @@ class CompressionSegmentReader { return result; } + //! Returns a view over the preceding length bytes and moves the position backward. + unsafe_array_ptr ReadBytesBackward(idx_t length) { + CheckBackwardRead(length); + position -= length; + return unsafe_array_ptr(data + position, length); + } + + //! Returns a view over the preceding count aligned elements and moves the position backward. + template + unsafe_array_ptr ReadArrayBackward(idx_t count) { + static_assert(std::is_trivially_copyable_v, + "ReadArrayBackward element must be a trivially copyable data type"); + if (DUCKDB_UNLIKELY(count > position / sizeof(T))) { + ThrowBackwardReadOutOfBounds(); + } + auto offset = position - count * sizeof(T); + auto result = GetArray(offset, count); + position = offset; + return result; + } + //! Copies the next length bytes into destination and advances the position. //! The caller must ensure destination has capacity for length bytes. void ReadBytesInto(data_ptr_t destination, idx_t length) { @@ -203,6 +224,10 @@ class CompressionSegmentReader { //! Throws if alignment is zero or the padding exceeds the range. void Align(idx_t alignment); + //! Aligns the position backward relative to this reader's origin. + //! Throws if alignment is zero. + void AlignBackward(idx_t alignment); + private: void CheckForwardRead(idx_t length) const { if (DUCKDB_UNLIKELY(length > size - position)) { diff --git a/src/duckdb/src/include/duckdb/storage/compression/dict_fsst/decompression.hpp b/src/duckdb/src/include/duckdb/storage/compression/dict_fsst/decompression.hpp index 0af0858c3..25a39088e 100644 --- a/src/duckdb/src/include/duckdb/storage/compression/dict_fsst/decompression.hpp +++ b/src/duckdb/src/include/duckdb/storage/compression/dict_fsst/decompression.hpp @@ -26,7 +26,7 @@ struct CompressedStringScanState : public SegmentScanState { void ScanToDictionaryVector(ColumnSegment &segment, Vector &result, idx_t result_offset, idx_t start, idx_t scan_count); const SelectionVector &GetSelVec(idx_t start, idx_t scan_count); - void Select(Vector &result, idx_t start, const SelectionVector &sel, idx_t sel_count); + void Select(Vector &result, idx_t result_offset, idx_t start, const SelectionVector &sel, idx_t sel_count); bool AllowDictionaryScan(idx_t scan_count); diff --git a/src/duckdb/src/include/duckdb/storage/compression/dictionary/decompression.hpp b/src/duckdb/src/include/duckdb/storage/compression/dictionary/decompression.hpp index 5e8769103..5cbe76412 100644 --- a/src/duckdb/src/include/duckdb/storage/compression/dictionary/decompression.hpp +++ b/src/duckdb/src/include/duckdb/storage/compression/dictionary/decompression.hpp @@ -1,5 +1,6 @@ #pragma once +#include "duckdb/storage/compression/compression_segment_reader.hpp" #include "duckdb/storage/compression/dictionary/common.hpp" namespace duckdb { @@ -8,46 +9,59 @@ namespace duckdb { // Scan //===--------------------------------------------------------------------===// struct CompressedStringScanState : public SegmentScanState { +private: + //! Dictionary segment data from disk, with byte ranges checked by ReadLayout. + struct SegmentLayout { + //! Bits per dictionary index (derived from the entry count and checked against the stored width) + bitpacking_width_t current_width; + //! Packed indices, mapping each row to a dictionary entry. + CompressionSegmentReader selection_reader; + //! String contents stored backwards from dict_end. + CompressionSegmentReader dictionary_reader; + //! Maps dictionary indices to byte offsets measured backwards from dict_end. + //! Consecutive offsets determine each (non NULL) string's length. + unsafe_array_ptr index_buffer; + }; + public: - explicit CompressedStringScanState(BufferHandle &&handle_p) - : owned_handle(std::move(handle_p)), handle(owned_handle) { + explicit CompressedStringScanState(BufferHandle &&handle_p, const ColumnSegment &segment) + : owned_handle(std::move(handle_p)), layout(ReadLayout(owned_handle, segment)) { } - explicit CompressedStringScanState(BufferHandle &handle_p) : owned_handle(), handle(handle_p) { + explicit CompressedStringScanState(BufferHandle &handle_p, const ColumnSegment &segment) + : layout(ReadLayout(handle_p, segment)) { } public: - void Initialize(ColumnSegment &segment, bool initialize_dictionary = true); + void InitializeDictionary(const ColumnSegment &segment); + //! Scans require InitializeDictionary, fetch validates its selected entry instead. template void ScanToFlatVector(Vector &result, idx_t result_offset, idx_t start, idx_t scan_count); void ScanToDictionaryVector(ColumnSegment &segment, Vector &result, idx_t result_offset, idx_t start, idx_t scan_count); private: - string_t FetchStringFromDict(uint32_t dict_offset, uint16_t string_len); - uint16_t GetStringLength(sel_t index); - void ValidateDictionary(const SelectionVector &sel, idx_t scan_count) const; - //! Validate the index buffer (offsets monotonic and within the dictionary) so scans can trust it. + //! The offset must be within the dictionary and the length must not extend past its end. + string_t FetchStringFromDict(uint32_t dict_offset, uint32_t string_len) const; + //! The index must be within the table and the offsets must be nondecreasing to avoid underflow. + uint32_t GetStringLength(idx_t index) const; + //! Checks each selected dictionary index against the offset table. + //! The selection must be set and the requested range must fit within its capacity. + void ValidateDictionaryIndices(const SelectionVector &sel, idx_t start_offset, idx_t scan_count) const; + //! Validate the index buffer (offsets must be monotonic and within the dictionary) so scans can trust it. void ValidateIndexBuffer() const; + static SegmentLayout ReadLayout(const BufferHandle &handle, const ColumnSegment &segment); + //! Returns packed bytes starting at the group containing start. + //! decompress_count must cover whole bitpacking groups that fit within the selection buffer. + unsafe_array_ptr GetSelectionBytes(idx_t start, idx_t decompress_count) const; public: BufferHandle owned_handle; - optional_ptr handle; - bitpacking_width_t current_width; + SegmentLayout layout; buffer_ptr sel_vec; idx_t sel_vec_size = 0; - //! Start of the block (pointing to the dictionary_header) - data_ptr_t baseptr; - //! Start of the data (pointing to the start of the selection buffer) - data_ptr_t base_data; - uint32_t *index_buffer_ptr; - uint32_t index_buffer_count; - buffer_ptr dictionary; - idx_t dictionary_size; - StringDictionaryContainer dict; - idx_t block_size; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/storage/compression/empty_validity.hpp b/src/duckdb/src/include/duckdb/storage/compression/empty_validity.hpp index 3d13ccad0..099a2d2f6 100644 --- a/src/duckdb/src/include/duckdb/storage/compression/empty_validity.hpp +++ b/src/duckdb/src/include/duckdb/storage/compression/empty_validity.hpp @@ -98,7 +98,7 @@ class EmptyValidityCompression { TableFilterState &filter_state) { } static void Select(ColumnSegment &segment, ColumnScanState &state, idx_t vector_count, Vector &result, - const SelectionVector &sel, idx_t sel_count) { + const SelectionVector &sel, idx_t sel_count, idx_t result_offset, ScanVectorType scan_type) { } }; diff --git a/src/duckdb/src/include/duckdb/storage/compression/patas/patas_fetch.hpp b/src/duckdb/src/include/duckdb/storage/compression/patas/patas_fetch.hpp index 594cfb8ae..a343452de 100644 --- a/src/duckdb/src/include/duckdb/storage/compression/patas/patas_fetch.hpp +++ b/src/duckdb/src/include/duckdb/storage/compression/patas/patas_fetch.hpp @@ -19,8 +19,12 @@ namespace duckdb { template void PatasFetchRow(ColumnSegment &segment, ColumnFetchState &state, row_t row_id, Vector &result, idx_t result_idx) { using EXACT_TYPE = typename FloatingToExact::TYPE; + D_ASSERT(row_id >= 0); + D_ASSERT(UnsafeNumericCast(row_id) < segment.count); - PatasScanState scan_state(segment); + auto &buffer_manager = BufferManager::GetBufferManager(segment.GetDatabase()); + auto handle = buffer_manager.Pin(state.context, segment.GetBlockHandle()); + PatasScanState scan_state(std::move(handle), segment); scan_state.Skip(segment, UnsafeNumericCast(row_id)); auto result_data = FlatVector::GetDataMutableUnsafe(result); result_data[result_idx] = (EXACT_TYPE)0; diff --git a/src/duckdb/src/include/duckdb/storage/compression/patas/patas_scan.hpp b/src/duckdb/src/include/duckdb/storage/compression/patas/patas_scan.hpp index e41c3aced..eda924e65 100644 --- a/src/duckdb/src/include/duckdb/storage/compression/patas/patas_scan.hpp +++ b/src/duckdb/src/include/duckdb/storage/compression/patas/patas_scan.hpp @@ -18,12 +18,19 @@ #include "duckdb/common/vector/flat_vector.hpp" #include "duckdb/function/compression_function.hpp" #include "duckdb/storage/buffer_manager.hpp" +#include "duckdb/storage/compression/compression_segment_reader.hpp" #include "duckdb/storage/table/column_segment.hpp" #include "duckdb/storage/table/scan_state.hpp" namespace duckdb { +[[noreturn]] void ThrowPatasInvalidBackwardReference(); +[[noreturn]] void ThrowPatasInvalidPackedValueMetadata(); +[[noreturn]] void ThrowPatasMetadataBeforeHeader(); +[[noreturn]] void ThrowPatasMetadataTableOutOfBounds(); +[[noreturn]] void ThrowPatasGroupDataOutOfBounds(); + //! Do not change order of these variables struct PatasUnpackedValueStats { uint8_t significant_bytes; @@ -34,7 +41,7 @@ struct PatasUnpackedValueStats { template struct PatasGroupState { public: - void Init(uint8_t *data) { + void Init(const uint8_t *data) { byte_reader.SetStream(data); } @@ -46,11 +53,33 @@ struct PatasGroupState { index = 0; } - void LoadPackedData(uint16_t *packed_data, idx_t count) { + // Calculate the bytes ByteReader will consume so LoadGroup can check the full span before decoding. + idx_t LoadPackedData(const PatasPrimitives::PACKED_DATA_TYPE *packed_data, idx_t count) { + idx_t data_size = 0; for (idx_t i = 0; i < count; i++) { auto &unpacked = unpacked_data[i]; - PackedDataUtils::Unpack(packed_data[i], (UnpackedData &)unpacked); + unpacked = Unpack(packed_data[i]); + // LoadValues initializes value_buffer[0] to zero, all later references must point to earlier values. + if (unpacked.index_diff > i || (i > 0 && unpacked.index_diff == 0)) { + ThrowPatasInvalidBackwardReference(); + } + // Keep reads within EXACT_TYPE and the left shift during decoding below its bit width. + if (unpacked.significant_bytes > sizeof(EXACT_TYPE) || unpacked.trailing_zeros >= sizeof(EXACT_TYPE) * 8) { + ThrowPatasInvalidPackedValueMetadata(); + } + // ByteReader treats zero bytes with fewer than eight trailing zeros as a full-width read. + if (unpacked.significant_bytes == 0 && unpacked.trailing_zeros < 8) { + data_size += sizeof(EXACT_TYPE); + } else { + data_size += unpacked.significant_bytes; + } } + return data_size; + } + + static PatasUnpackedValueStats Unpack(PatasPrimitives::PACKED_DATA_TYPE packed_data) { + auto unpacked = PackedDataUtils::Unpack(packed_data); + return {unpacked.leading_zero, unpacked.significant_bits, unpacked.index}; } template @@ -68,14 +97,6 @@ struct PatasGroupState { } value_buffer[0] = (EXACT_TYPE)0; for (idx_t i = 0; i < count; i++) { - if (unpacked_data[i].index_diff > i) { - throw DataCorruptionException("Corrupted Patas segment: invalid backward reference"); - } - if (unpacked_data[i].significant_bytes > sizeof(EXACT_TYPE) || - unpacked_data[i].trailing_zeros >= sizeof(EXACT_TYPE) * 8) { - throw DataCorruptionException("Corrupted Patas segment: invalid packed value metadata"); - } - value_buffer[i] = patas::PatasDecompression::DecompressValue( byte_reader, unpacked_data[i].significant_bytes, unpacked_data[i].trailing_zeros, value_buffer[i - unpacked_data[i].index_diff]); @@ -96,30 +117,49 @@ struct PatasScanState : public SegmentScanState { public: using EXACT_TYPE = typename FloatingToExact::TYPE; - explicit PatasScanState(ColumnSegment &segment) : segment(segment), count(segment.count) { - auto &buffer_manager = BufferManager::GetBufferManager(segment.GetDatabase()); - - handle = buffer_manager.Pin(segment.GetBlockHandle()); - // ScanStates never exceed the boundaries of a Segment, - // but are not guaranteed to start at the beginning of the Block - segment_data = handle.GetDataMutable() + segment.GetBlockOffset(); - auto metadata_offset = Load(segment_data); - if (segment.GetBlockOffset() + metadata_offset > segment.GetBlockSize()) { - throw DataCorruptionException( - "Corrupted Patas segment: metadata_offset reaches outside of the blocks memory"); + struct SegmentLayout { + CompressionSegmentReader data; + CompressionSegmentReader metadata; + }; + + static SegmentLayout ReadSegmentLayout(const BufferHandle &handle, ColumnSegment &segment, idx_t count) { + auto reader = CompressionSegmentReader::FromSegment(handle, segment, "Patas segment"); + auto metadata_end = reader.template Read(); + if (metadata_end < PatasPrimitives::HEADER_SIZE) { + ThrowPatasMetadataBeforeHeader(); + } + reader = reader.GetSubReader(0, metadata_end, "Patas segment"); + + auto group_count = count / PatasPrimitives::PATAS_GROUP_SIZE + (count % PatasPrimitives::PATAS_GROUP_SIZE != 0); + auto metadata_capacity = metadata_end - PatasPrimitives::HEADER_SIZE; + if (group_count > metadata_capacity / PatasPrimitives::GROUP_OFFSET_SIZE) { + ThrowPatasMetadataTableOutOfBounds(); } - metadata_ptr = segment_data + metadata_offset; + auto group_offsets_size = group_count * PatasPrimitives::GROUP_OFFSET_SIZE; + metadata_capacity -= group_offsets_size; + if (count > metadata_capacity / PatasPrimitives::PACKED_DATA_SIZE) { + ThrowPatasMetadataTableOutOfBounds(); + } + auto metadata_size = group_offsets_size + count * PatasPrimitives::PACKED_DATA_SIZE; + auto metadata_start = metadata_end - metadata_size; + auto data = reader.GetSubReader(PatasPrimitives::HEADER_SIZE, metadata_start - PatasPrimitives::HEADER_SIZE, + "Patas data"); + auto metadata = reader.GetSubReader(metadata_start, metadata_size, "Patas metadata"); + return {data, metadata}; + } + + explicit PatasScanState(BufferHandle handle_p, ColumnSegment &segment) + : handle(std::move(handle_p)), count(segment.count), layout(ReadSegmentLayout(handle, segment, count)), + metadata_position(layout.metadata.Size()) { } BufferHandle handle; - data_ptr_t metadata_ptr; - data_ptr_t segment_data; + idx_t count; + SegmentLayout layout; + idx_t metadata_position; idx_t total_value_count = 0; PatasGroupState group_state; - ColumnSegment &segment; - idx_t count; - idx_t LeftInGroup() const { return PatasPrimitives::PATAS_GROUP_SIZE - (total_value_count % PatasPrimitives::PATAS_GROUP_SIZE); } @@ -151,35 +191,41 @@ struct PatasScanState : public SegmentScanState { // Using the metadata, we can avoid loading any of the data if we don't care about the group at all void SkipGroup() { - // Skip the offset indicating where the data starts - metadata_ptr -= sizeof(uint32_t); + D_ASSERT(GroupFinished()); + D_ASSERT(total_value_count < count); idx_t group_size = MinValue((idx_t)PatasPrimitives::PATAS_GROUP_SIZE, count - total_value_count); - // Skip the blocks of packed data - metadata_ptr -= sizeof(uint16_t) * group_size; - + auto group_metadata_size = PatasPrimitives::GROUP_OFFSET_SIZE + PatasPrimitives::PACKED_DATA_SIZE * group_size; + D_ASSERT(group_metadata_size <= metadata_position); + metadata_position -= group_metadata_size; total_value_count += group_size; } template void LoadGroup(EXACT_TYPE *value_buffer) { + D_ASSERT(GroupFinished()); + D_ASSERT(total_value_count < count); group_state.Reset(); - // Load the offset indicating where a groups data starts - metadata_ptr -= sizeof(uint32_t); - auto data_byte_offset = Load(metadata_ptr); - if (segment.GetBlockOffset() + data_byte_offset >= segment.GetBlockSize()) { - throw DataCorruptionException( - "Corrupted Patas segment: data_byte_offset would reach outside of the blocks memory"); + idx_t group_size = MinValue((idx_t)PatasPrimitives::PATAS_GROUP_SIZE, count - total_value_count); + auto group_metadata_size = PatasPrimitives::GROUP_OFFSET_SIZE + PatasPrimitives::PACKED_DATA_SIZE * group_size; + D_ASSERT(group_metadata_size <= metadata_position); + metadata_position -= group_metadata_size; + auto metadata = layout.metadata.GetSubReader(metadata_position, group_metadata_size, "Patas group metadata"); + metadata.SetPosition(metadata.Size()); + auto data_byte_offset = metadata.template ReadBackward(); + if (data_byte_offset < PatasPrimitives::HEADER_SIZE || + data_byte_offset - PatasPrimitives::HEADER_SIZE > layout.data.Size()) { + ThrowPatasGroupDataOutOfBounds(); } - // Initialize the byte_reader with the data values for the group - group_state.Init(segment_data + data_byte_offset); - - idx_t group_size = MinValue((idx_t)PatasPrimitives::PATAS_GROUP_SIZE, (count - total_value_count)); - - // Read the compacted blocks of (7 + 6 + 3 bits) value stats - metadata_ptr -= sizeof(uint16_t) * group_size; - group_state.LoadPackedData((uint16_t *)metadata_ptr, group_size); + auto packed_data = metadata.template GetArray(0, group_size); + auto data_size = group_state.LoadPackedData(packed_data.data(), group_size); + auto data_offset = data_byte_offset - PatasPrimitives::HEADER_SIZE; + if (data_size > layout.data.Size() - data_offset) { + ThrowPatasGroupDataOutOfBounds(); + } + auto data = layout.data.GetSubReader(data_offset, data_size, "Patas group data"); + group_state.Init(data.GetBytes(0, data.Size()).data()); // Read all the values to the specified 'value_buffer' group_state.template LoadValues(value_buffer, group_size); @@ -189,12 +235,14 @@ struct PatasScanState : public SegmentScanState { //! Skip the next 'skip_count' values, we don't store the values void Skip(ColumnSegment &segment, idx_t skip_count) { using EXACT_TYPE = typename FloatingToExact::TYPE; + D_ASSERT(total_value_count <= count); + D_ASSERT(skip_count <= count - total_value_count); if (total_value_count != 0 && !GroupFinished()) { // Finish skipping the current group - idx_t to_skip = LeftInGroup(); - skip_count -= to_skip; + idx_t to_skip = MinValue(skip_count, LeftInGroup()); ScanGroup(nullptr, to_skip); + skip_count -= to_skip; } // Figure out how many entire groups we can skip // For these groups, we don't even need to process the metadata or values @@ -214,7 +262,9 @@ struct PatasScanState : public SegmentScanState { template unique_ptr PatasInitScan(const QueryContext &context, ColumnSegment &segment) { - auto result = make_uniq_base>(segment); + auto &buffer_manager = BufferManager::GetBufferManager(segment.GetDatabase()); + auto handle = buffer_manager.Pin(context, segment.GetBlockHandle()); + auto result = make_uniq_base>(std::move(handle), segment); return result; } @@ -226,6 +276,8 @@ void PatasScanPartial(ColumnSegment &segment, ColumnScanState &state, idx_t scan idx_t result_offset) { using EXACT_TYPE = typename FloatingToExact::TYPE; auto &scan_state = (PatasScanState &)*state.scan_state; + D_ASSERT(scan_state.total_value_count <= scan_state.count); + D_ASSERT(scan_count <= scan_state.count - scan_state.total_value_count); // Get the pointer to the result values auto current_result_ptr = FlatVector::GetDataMutableUnsafe(result); diff --git a/src/duckdb/src/include/duckdb/storage/compression/patas/shared.hpp b/src/duckdb/src/include/duckdb/storage/compression/patas/shared.hpp index a26143aea..9a59a0ea5 100644 --- a/src/duckdb/src/include/duckdb/storage/compression/patas/shared.hpp +++ b/src/duckdb/src/include/duckdb/storage/compression/patas/shared.hpp @@ -4,8 +4,14 @@ namespace duckdb { class PatasPrimitives { public: + using METADATA_POINTER_TYPE = uint32_t; + using GROUP_OFFSET_TYPE = uint32_t; + using PACKED_DATA_TYPE = uint16_t; + static constexpr uint32_t PATAS_GROUP_SIZE = 1024; - static constexpr uint8_t HEADER_SIZE = sizeof(uint32_t); + static constexpr uint8_t HEADER_SIZE = sizeof(METADATA_POINTER_TYPE); + static constexpr uint8_t GROUP_OFFSET_SIZE = sizeof(GROUP_OFFSET_TYPE); + static constexpr uint8_t PACKED_DATA_SIZE = sizeof(PACKED_DATA_TYPE); static constexpr uint8_t BYTECOUNT_BITSIZE = 3; static constexpr uint8_t INDEX_BITSIZE = 7; }; diff --git a/src/duckdb/src/include/duckdb/storage/data_table.hpp b/src/duckdb/src/include/duckdb/storage/data_table.hpp index 7be39c290..1a91416e1 100644 --- a/src/duckdb/src/include/duckdb/storage/data_table.hpp +++ b/src/duckdb/src/include/duckdb/storage/data_table.hpp @@ -251,10 +251,6 @@ class DataTable : public enable_shared_from_this { //! Scans the next chunk for the CREATE INDEX operator bool CreateIndexScan(TableScanState &state, DataChunk &result); - //! Returns true, if the index name is unique (i.e., no PK, UNIQUE, FK constraint has the same name) - //! FIXME: This is only necessary until we treat all indexes as catalog entries, allowing to alter constraints - bool IndexNameIsUnique(const string &name); - //! Initialize constraint verification state unique_ptr InitializeConstraintState(TableCatalogEntry &table, const vector> &bound_constraints); @@ -290,9 +286,9 @@ class DataTable : public enable_shared_from_this { //! AddIndex initializes an index and adds it to the table's index list. //! It is either empty, or initialized via its index storage information. void AddIndex(const ColumnList &columns, const vector &column_indexes, const IndexConstraintType type, - IndexStorageInfo index_info, ConstraintCheckMode check_mode); + IndexStorageInfo index_info, idx_t index_oid, ConstraintCheckMode check_mode); //! AddIndex moves an index to this table's index list. - void AddIndex(unique_ptr index); + void AddIndex(unique_ptr index, idx_t index_oid); //! Returns a list of the partition stats vector GetPartitionStats(ClientContext &context); diff --git a/src/duckdb/src/include/duckdb/storage/database_handle.hpp b/src/duckdb/src/include/duckdb/storage/database_handle.hpp index 119f4505f..60ea2c828 100644 --- a/src/duckdb/src/include/duckdb/storage/database_handle.hpp +++ b/src/duckdb/src/include/duckdb/storage/database_handle.hpp @@ -23,8 +23,9 @@ struct DatabaseHandle { explicit DatabaseHandle(unique_ptr mmap_handle); //! Open a fresh handle for the database file. A header prefix in `options.prefetched` (from file-type - //! detection) is adopted to serve the initial header reads from memory. - static unique_ptr Open(AttachedDatabase &db, const string &path, + //! detection) is adopted to serve the initial header reads from memory. With a client in `context`, the file + //! is opened through that client's file system, so the handle uses its settings and logger. + static unique_ptr Open(QueryContext context, AttachedDatabase &db, const string &path, const StorageManagerOptions &options, DatabaseOpenMode open_mode); bool OnDiskFile() const; @@ -35,6 +36,7 @@ struct DatabaseHandle { void Truncate(idx_t new_size); void Trim(idx_t offset, idx_t length); + idx_t GetFileSize(); FileHandle &GetFileHandle(); @@ -42,9 +44,9 @@ struct DatabaseHandle { //! Throws if a write at [required_size] would exceed the mmap reserve. No-op otherwise. void EnsureMappedSize(idx_t required_size) const; - static unique_ptr OpenMemoryMap(AttachedDatabase &db, const string &path, + static unique_ptr OpenMemoryMap(QueryContext context, AttachedDatabase &db, const string &path, const StorageManagerOptions &options, DatabaseOpenMode open_mode); - static unique_ptr OpenFile(AttachedDatabase &db, const string &path, + static unique_ptr OpenFile(QueryContext context, AttachedDatabase &db, const string &path, const StorageManagerOptions &options, DatabaseOpenMode open_mode); private: diff --git a/src/duckdb/src/include/duckdb/storage/external_file_cache/caching_file_system.hpp b/src/duckdb/src/include/duckdb/storage/external_file_cache/caching_file_system.hpp index 270519cd9..2b28b8088 100644 --- a/src/duckdb/src/include/duckdb/storage/external_file_cache/caching_file_system.hpp +++ b/src/duckdb/src/include/duckdb/storage/external_file_cache/caching_file_system.hpp @@ -92,17 +92,20 @@ struct CachingFileHandle { shared_ptr EnsureCachedFileCurrent(); //! Whether validation metadata permits using cached blocks. bool CanUseCache(); + //! Read without caching, splitting large reads of cacheable files into parallel reads of at most a block. + void ReadUncached(data_ptr_t buffer, idx_t nr_bytes, idx_t location); //! Reconcile cached blocks with validation metadata observed while reading them. - void ReconcileCacheAfterRead(CachedFile &cached_file, idx_t first_block, - const vector> &blocks); + void ReconcileCacheAfterRead(CachedFile &cached_file); //! Record a timed read of a local file into the throughput estimate void RecordReadThroughput(double total_seconds, idx_t bytes); private: QueryContext context; - //! The client caching file system that was used to create this CachingFileHandle - CachingFileSystem &caching_file_system; + //! The client file system, held directly because the caching file system can be destroyed before this handle + FileSystem &file_system; + //! The DatabaseInstance + DatabaseInstance &db; //! The DB external file cache ExternalFileCache &external_file_cache; //! For opening the file (possibly with extra info) @@ -145,10 +148,11 @@ class CachingFileSystem { public: DUCKDB_API static CachingFileSystem Get(ClientContext &context); - DUCKDB_API unique_ptr OpenFile(const OpenFileInfo &path, FileOpenFlags flags, + DUCKDB_API unique_ptr OpenFile(const OpenFileInfo &path, const FileOpenFlags &flags, optional_ptr opener = nullptr); DUCKDB_API unique_ptr OpenFile(QueryContext context, const OpenFileInfo &path, - FileOpenFlags flags, optional_ptr opener = nullptr); + const FileOpenFlags &flags, + optional_ptr opener = nullptr); private: //! The Client FileSystem (needs to be client-specific so we can do, e.g., HTTPFS profiling) diff --git a/src/duckdb/src/include/duckdb/storage/external_file_cache/caching_file_system_wrapper.hpp b/src/duckdb/src/include/duckdb/storage/external_file_cache/caching_file_system_wrapper.hpp index ebb57c290..c89514b63 100644 --- a/src/duckdb/src/include/duckdb/storage/external_file_cache/caching_file_system_wrapper.hpp +++ b/src/duckdb/src/include/duckdb/storage/external_file_cache/caching_file_system_wrapper.hpp @@ -73,6 +73,8 @@ class CachingFileSystemWrapper : public FileSystem, public enable_shared_from_th DUCKDB_API string GetVersionTag(FileHandle &handle) override; DUCKDB_API FileType GetFileType(FileHandle &handle) override; DUCKDB_API FileMetadata Stats(FileHandle &handle) override; + DUCKDB_API optional GetStatsIfExists(const OpenFileInfo &file, + optional_ptr opener = nullptr) override; DUCKDB_API void Truncate(FileHandle &handle, int64_t new_size) override; DUCKDB_API void FileSync(FileHandle &handle) override; diff --git a/src/duckdb/src/include/duckdb/storage/external_file_cache/external_file_cache.hpp b/src/duckdb/src/include/duckdb/storage/external_file_cache/external_file_cache.hpp index d56714954..1c5e2a048 100644 --- a/src/duckdb/src/include/duckdb/storage/external_file_cache/external_file_cache.hpp +++ b/src/duckdb/src/include/duckdb/storage/external_file_cache/external_file_cache.hpp @@ -9,6 +9,7 @@ #pragma once #include "duckdb/common/atomic.hpp" +#include "duckdb/common/map.hpp" #include "duckdb/common/mutex.hpp" #include "duckdb/common/optional.hpp" #include "duckdb/common/optional_idx.hpp" @@ -54,8 +55,10 @@ struct CacheValidationInfo { class ExternalFileCache { public: - //! Get the cache block size for a given file path. - DUCKDB_API idx_t GetCacheBlockSize(const string &path) const; + //! Get the maximum cache block size for a given file path. + DUCKDB_API idx_t GetCacheMaxBlockSize(const string &path) const; + //! Get the block size that reads of a given file path are rounded out to. + DUCKDB_API idx_t GetCacheMinBlockSize(const string &path) const; //! Whether reads of the given file should go through the cache (remote files only, unless forced). DUCKDB_API bool ShouldCacheFile(const string &path) const; @@ -69,10 +72,10 @@ class ExternalFileCache { const idx_t generation; mutable annotated_mutex map_lock; - //! The block size used to index the current block map. Invalid if no blocks have been cached yet. - optional_idx cached_block_size DUCKDB_GUARDED_BY(map_lock); - //! Maps from block index to cached block. - unordered_map> blocks DUCKDB_GUARDED_BY(map_lock); + //! Non-overlapping cached blocks, keyed by file offset. + map> blocks DUCKDB_GUARDED_BY(map_lock); + //! Incremented whenever the blocks of the file are dropped. + idx_t content_generation DUCKDB_GUARDED_BY(map_lock) = 0; mutable annotated_mutex meta_lock; //! Metadata for validating the cached blocks against the current file. @@ -95,13 +98,12 @@ class ExternalFileCache { //! Number of files tracked in the ObjectCache, exposed for testing. idx_t GetCachedFileCount() const; - //! Re-index to `current_block_size` if it differs from the cache block size. - //! Return the blocks cached for the given range. - vector> ReindexAndAcquireBlocks(CachedFile &cached_file, idx_t current_block_size, - idx_t first_block, idx_t num_blocks); - //! Remove an acquired block range from the cache without mutating blocks that may still be used by readers. - //! A block is only removed when it is still the current entry for its index. - void RetireBlocks(CachedFile &cached_file, idx_t first_block, const vector> &blocks); + //! Get the blocks covering [location, location + nr_bytes), creating empty blocks for the missing bytes. + vector> AcquireBlocks(CachedFile &cached_file, idx_t location, idx_t nr_bytes, + idx_t max_block_size); + //! Drop all blocks of a file, e.g. because the file changed. Readers keep the blocks they hold. + void DropBlocks(CachedFile &cached_file); + idx_t GetContentGeneration(CachedFile &cached_file); BufferManager &GetBufferManager() const; //! Gets the shared cached file for the given path, creating it if not yet present. @@ -125,10 +127,6 @@ class ExternalFileCache { private: class ExternalFileCacheObjectCacheEntry; - //! Re-index blocks of a single cached file. - void ReindexCachedFileCore(CachedFile &cached_file, idx_t file_size, idx_t old_block_size, idx_t new_block_size) - DUCKDB_REQUIRES(cached_file.map_lock); - //! Registers a cached file path in the tracked set. void InsertCachedFileKey(const string &path); //! Removes a cached file path from the tracked set. diff --git a/src/duckdb/src/include/duckdb/storage/external_file_cache/external_file_cache_block.hpp b/src/duckdb/src/include/duckdb/storage/external_file_cache/external_file_cache_block.hpp index 19e5cb155..e9121b315 100644 --- a/src/duckdb/src/include/duckdb/storage/external_file_cache/external_file_cache_block.hpp +++ b/src/duckdb/src/include/duckdb/storage/external_file_cache/external_file_cache_block.hpp @@ -22,6 +22,12 @@ namespace duckdb { class BlockHandle; struct CacheBlock { + CacheBlock(idx_t location_p, idx_t size_p) : location(location_p), size(size_p) { + } + + const idx_t location; + const idx_t size; + mutable annotated_mutex mtx; mutable std::condition_variable cv DUCKDB_GUARDED_BY(mtx); CacheBlockState state DUCKDB_GUARDED_BY(mtx) = CacheBlockState::EMPTY; diff --git a/src/duckdb/src/include/duckdb/storage/object_cache.hpp b/src/duckdb/src/include/duckdb/storage/object_cache.hpp index 7245c4526..b232ffe8e 100644 --- a/src/duckdb/src/include/duckdb/storage/object_cache.hpp +++ b/src/duckdb/src/include/duckdb/storage/object_cache.hpp @@ -108,7 +108,7 @@ class ObjectCache { } // Create new entry while holding lock - auto value = make_shared_ptr(args...); + auto value = make_shared_ptr(std::forward(args)...); const auto estimated_memory = value->GetEstimatedCacheMemory(); const bool is_evictable = estimated_memory.IsValid(); if (!is_evictable) { diff --git a/src/duckdb/src/include/duckdb/storage/optimistic_data_writer.hpp b/src/duckdb/src/include/duckdb/storage/optimistic_data_writer.hpp index d5ea54be8..abaae1f13 100644 --- a/src/duckdb/src/include/duckdb/storage/optimistic_data_writer.hpp +++ b/src/duckdb/src/include/duckdb/storage/optimistic_data_writer.hpp @@ -26,6 +26,8 @@ struct OptimisticWriteCollection { void MergeStorage(OptimisticWriteCollection &collection); void FinalizeFlush(); + // Reset the accounting fields to match a freshly-replaced collection + void ResetCollectionAccounting(); }; enum class OptimisticWritePartialManagers { PER_COLUMN, GLOBAL }; diff --git a/src/duckdb/src/include/duckdb/storage/single_file_block_manager.hpp b/src/duckdb/src/include/duckdb/storage/single_file_block_manager.hpp index ddb31fdf7..950d09c04 100644 --- a/src/duckdb/src/include/duckdb/storage/single_file_block_manager.hpp +++ b/src/duckdb/src/include/duckdb/storage/single_file_block_manager.hpp @@ -100,6 +100,8 @@ class SingleFileBlockManager : public BlockManager { void IncreaseBlockReferenceCount(block_id_t block_id) override; //! UnregisterBlock, only accepts non-temporary block ids void UnregisterBlock(block_id_t id) override; + //! Unregister the block id if no live handle is registered for it + bool UnregisterExpiredBlock(block_id_t id) override; //! Return the meta block id idx_t GetMetaBlock() override; //! Read the content of the block from disk @@ -193,6 +195,10 @@ class SingleFileBlockManager : public BlockManager { block_id_t GetFreeBlockIdInternal(FreeBlockType type); //! Adds a free block to the free_list, returns true if it was added to the regular free_list bool AddFreeBlock(unique_lock &lock, block_id_t block_id); + //! Move the block from free_blocks_in_use to the free list, if present + void ReleaseFreeBlockInUse(unique_lock &lock, block_id_t id); + //! Returns true, if the block is in the free list. Takes the lock (verification only). + bool BlockIsFreeListed(block_id_t block_id); private: AttachedDatabase &db; diff --git a/src/duckdb/src/include/duckdb/storage/standard_buffer_manager.hpp b/src/duckdb/src/include/duckdb/storage/standard_buffer_manager.hpp index a62d5c9f4..cd2d661eb 100644 --- a/src/duckdb/src/include/duckdb/storage/standard_buffer_manager.hpp +++ b/src/duckdb/src/include/duckdb/storage/standard_buffer_manager.hpp @@ -120,7 +120,7 @@ class StandardBufferManager : public BufferManager { //! Helper template TempBufferPoolReservation EvictBlocksOrThrow(QueryContext context, MemoryTag tag, idx_t memory_delta, - unique_ptr *buffer, ARGS...); + unique_ptr *buffer, const ARGS &...); //! Register an in-memory buffer of arbitrary size, as long as it is >= BLOCK_SIZE. can_destroy signifies whether or //! not the buffer can be destroyed instead of evicted, @@ -187,7 +187,9 @@ class StandardBufferManager : public BufferManager { struct TemporaryFileData { //! The directory name where temporary files are stored string path; - //! Lock for creating the temp handle (marked mutable so 'GetMaxSwap' can be const) + //! Publishes successful temporary-directory initialization + atomic initialized {false}; + //! Protects configuration and handle creation (mutable so 'GetMaxSwap' can be const) mutable mutex lock; //! Size of temp file on disk atomic size_on_disk = {0}; diff --git a/src/duckdb/src/include/duckdb/storage/statistics/geometry_stats.hpp b/src/duckdb/src/include/duckdb/storage/statistics/geometry_stats.hpp index f457fc624..bc56a4c9a 100644 --- a/src/duckdb/src/include/duckdb/storage/statistics/geometry_stats.hpp +++ b/src/duckdb/src/include/duckdb/storage/statistics/geometry_stats.hpp @@ -216,6 +216,8 @@ class GeometryTypeSet { } vector ToString(bool snake_case) const; + //! Add a type given its snake_case name + DUCKDB_API bool TryAdd(const string &type_name); uint8_t sets[VERT_TYPES]; }; diff --git a/src/duckdb/src/include/duckdb/storage/statistics/numeric_stats.hpp b/src/duckdb/src/include/duckdb/storage/statistics/numeric_stats.hpp index df47d6075..95c5a1711 100644 --- a/src/duckdb/src/include/duckdb/storage/statistics/numeric_stats.hpp +++ b/src/duckdb/src/include/duckdb/storage/statistics/numeric_stats.hpp @@ -15,6 +15,9 @@ #include "duckdb/storage/statistics/numeric_stats_union.hpp" #include "duckdb/common/array_ptr.hpp" +#include +#include + namespace duckdb { class BaseStatistics; struct SelectionVector; @@ -35,7 +38,7 @@ struct NumericStatsData { struct NumericStats { //! Unknown statistics - i.e. "has_min" is false, "has_max" is false DUCKDB_API static BaseStatistics CreateUnknown(LogicalType type); - //! Empty statistics - i.e. "min = MaxValue, max = MinValue" + //! Empty statistics - min and max are initialized to the domain's maximum and minimum DUCKDB_API static BaseStatistics CreateEmpty(LogicalType type); //! Returns true if the stats has a constant value @@ -87,6 +90,16 @@ struct NumericStats { static inline void UpdateValue(T new_value, T &min, T &max) { min = LessThan::Operation(new_value, min) ? new_value : min; max = GreaterThan::Operation(new_value, max) ? new_value : max; + if constexpr (std::is_floating_point_v) { + // -0.0 == 0.0: prefer -0.0 as min and 0.0 as max so IsConstant can detect mixed zeros + if (new_value == T(0)) { + if (std::signbit(new_value)) { + min = min == T(0) ? new_value : min; + } else { + max = max == T(0) ? new_value : max; + } + } + } } template static inline void Update(NumericStatsData &nstats, T new_value) { diff --git a/src/duckdb/src/include/duckdb/storage/statistics/stats_writer.hpp b/src/duckdb/src/include/duckdb/storage/statistics/stats_writer.hpp index 90de93b2a..d4be2a15b 100644 --- a/src/duckdb/src/include/duckdb/storage/statistics/stats_writer.hpp +++ b/src/duckdb/src/include/duckdb/storage/statistics/stats_writer.hpp @@ -8,6 +8,7 @@ #pragma once +#include "duckdb/common/bswap.hpp" #include "duckdb/common/types/string_type.hpp" #include "duckdb/storage/statistics/numeric_stats.hpp" #include "duckdb/storage/statistics/string_stats.hpp" @@ -16,6 +17,9 @@ #include "utf8proc_wrapper.hpp" #include "duckdb/main/error_manager.hpp" +#include +#include + namespace duckdb { struct BaseStatsWriter { @@ -61,7 +65,11 @@ struct StatsWriter : public BaseStatsWriter { inline void Clear() { ClearBase(); - min = NumericLimits::Maximum(); + if constexpr (std::is_floating_point_v) { + min = std::numeric_limits::quiet_NaN(); + } else { + min = NumericLimits::Maximum(); + } max = NumericLimits::Minimum(); } @@ -71,8 +79,7 @@ struct StatsWriter : public BaseStatsWriter { } void UpdateMinMax(T new_value) { - min = LessThan::Operation(new_value, min) ? new_value : min; - max = GreaterThan::Operation(new_value, max) ? new_value : max; + NumericStats::UpdateValue(new_value, min, max); } void Merge(BaseStatistics &target) const { @@ -103,6 +110,22 @@ struct StatsWriter : public BaseStatsWriter { } }; +//! Lexicographic compare of two 12-byte string prefixes that are zero-padded past the string length, as the +//! inline area of a string_t is. Returns <0, 0 or >0 like memcmp. +static inline int CompareInlineBytes(const_data_ptr_t a, const_data_ptr_t b) { + const uint32_t a_prefix = Load(a); + const uint32_t b_prefix = Load(b); + if (a_prefix != b_prefix) { + return BSwapIfLE(a_prefix) < BSwapIfLE(b_prefix) ? -1 : 1; + } + const uint64_t a_rest = Load(a + sizeof(uint32_t)); + const uint64_t b_rest = Load(b + sizeof(uint32_t)); + if (a_rest != b_rest) { + return BSwapIfLE(a_rest) < BSwapIfLE(b_rest) ? -1 : 1; + } + return 0; +} + template <> struct StatsWriter : public BaseStatsWriter { friend struct StringStats; @@ -135,7 +158,8 @@ struct StatsWriter : public BaseStatsWriter { } auto data = const_data_ptr_cast(value.GetData()); auto size = value.GetSize(); - +#ifdef DUCKDB_DEBUG_NO_INLINE + // without inlining a short string only has `size` readable bytes auto copy_count = MinValue(size, StringStatsData::CURRENT_MAX_STRING_MINMAX_SIZE); if (is_set) { // compare to current min/max @@ -158,6 +182,32 @@ struct StatsWriter : public BaseStatsWriter { max_size = size; is_set = true; } +#else + // a string_t always has CURRENT_MAX_STRING_MINMAX_SIZE readable bytes at its data pointer, zero-padded + // past the length when inlined, so min and max are kept as zero-padded prefixes and compared as integers + static_assert(StringStatsData::CURRENT_MAX_STRING_MINMAX_SIZE == string_t::INLINE_LENGTH, + "the string statistics prefix is the inline area of a string_t"); + if (is_set) { + // the prefix compare covers the first min(size, min_size) bytes exactly because the shorter side is + // zero-padded, and the length breaks a tie + auto min_cmp = CompareInlineBytes(data, min); + if (min_cmp < 0 || (min_cmp == 0 && size < min_size)) { + memcpy(min, data, StringStatsData::CURRENT_MAX_STRING_MINMAX_SIZE); + min_size = size; + } + auto max_cmp = CompareInlineBytes(data, max); + if (max_cmp > 0 || (max_cmp == 0 && size > max_size)) { + memcpy(max, data, StringStatsData::CURRENT_MAX_STRING_MINMAX_SIZE); + max_size = size; + } + } else { + memcpy(min, data, StringStatsData::CURRENT_MAX_STRING_MINMAX_SIZE); + memcpy(max, data, StringStatsData::CURRENT_MAX_STRING_MINMAX_SIZE); + min_size = size; + max_size = size; + is_set = true; + } +#endif if (size > max_string_length) { max_string_length = UnsafeNumericCast(size); } diff --git a/src/duckdb/src/include/duckdb/storage/statistics/variant_stats.hpp b/src/duckdb/src/include/duckdb/storage/statistics/variant_stats.hpp index 8675c9156..654e8798c 100644 --- a/src/duckdb/src/include/duckdb/storage/statistics/variant_stats.hpp +++ b/src/duckdb/src/include/duckdb/storage/statistics/variant_stats.hpp @@ -85,6 +85,8 @@ struct VariantStats { //! Stats related to the 'shredded' column, which holds all structured data created during shredding //! Returns the LogicalType that represents the shredding as a single DuckDB LogicalType (i.e STRUCT(col1 VARCHAR)) DUCKDB_API static LogicalType GetShreddedStructuredType(const BaseStatistics &stats); + //! Wraps each level of structured_type in typed_value + DUCKDB_API static LogicalType GetShreddingType(const LogicalType &structured_type); DUCKDB_API static void CreateShreddedStats(BaseStatistics &stats, const LogicalType &shredded_type); DUCKDB_API static bool IsShredded(const BaseStatistics &stats); //! Determine if a given path inside the variant stats is shredded diff --git a/src/duckdb/src/include/duckdb/storage/string_uncompressed.hpp b/src/duckdb/src/include/duckdb/storage/string_uncompressed.hpp index b14aeb5a8..df0b6ac82 100644 --- a/src/duckdb/src/include/duckdb/storage/string_uncompressed.hpp +++ b/src/duckdb/src/include/duckdb/storage/string_uncompressed.hpp @@ -12,6 +12,7 @@ #include "duckdb/common/types/vector.hpp" #include "duckdb/function/compression_function.hpp" #include "duckdb/storage/buffer_manager.hpp" +#include "duckdb/storage/compression/compression_segment_reader.hpp" #include "duckdb/storage/segment/uncompressed.hpp" #include "duckdb/storage/table/column_segment.hpp" #include "duckdb/storage/table/scan_state.hpp" @@ -31,7 +32,59 @@ struct StringDictionaryContainer { } }; +struct StringDictionaryEntry { + unsafe_array_ptr data; + bool is_overflow; +}; + +struct ValidatedStringRange { +public: + StringDictionaryEntry GetEntry(idx_t index) const { + D_ASSERT(index < offsets.size()); + const auto current_offset = offsets[index]; + const auto dictionary_offset = UnsafeNumericCast(AbsValue(current_offset)); + const auto previous_offset = index > 0 ? UnsafeNumericCast(AbsValue(offsets[index - 1])) + : preceding_dictionary_offset; + const auto string_length = dictionary_offset - previous_offset; + return {dictionary_data.SubArray(dictionary_data.size() - dictionary_offset, string_length), + current_offset < 0 && string_length > 0}; + } + +private: + friend struct StringSegmentLayout; + ValidatedStringRange(unsafe_array_ptr dictionary_data_p, unsafe_array_ptr offsets_p, + uint32_t preceding_dictionary_offset_p) + : dictionary_data(dictionary_data_p), offsets(offsets_p), + preceding_dictionary_offset(preceding_dictionary_offset_p) { + } + + unsafe_array_ptr dictionary_data; + unsafe_array_ptr offsets; + uint32_t preceding_dictionary_offset; +}; + +struct StringSegmentLayout { +public: + static StringSegmentLayout Read(const BufferHandle &handle, const ColumnSegment &segment); + StringDictionaryEntry ValidateAndGetEntry(idx_t row_index) const; + ValidatedStringRange ValidateRange(idx_t start, idx_t count) const; + +private: + StringSegmentLayout(const unsafe_array_ptr dictionary_data_p, + const unsafe_array_ptr offsets_p) + : dictionary_data(dictionary_data_p), offsets(offsets_p) { + } + + //! Dictionary bytes [DICTIONARY_HEADER_SIZE + segment.count * sizeof(int32_t), dictionary end) + unsafe_array_ptr dictionary_data; + //! Dictionary offsets read from the segment after validating the array's byte range. + unsafe_array_ptr offsets; +}; + struct StringScanState : public SegmentScanState { +public: + explicit StringScanState(BufferHandle handle); + BufferHandle handle; }; @@ -67,7 +120,7 @@ struct UncompressedStringStorage { idx_t result_offset); static void StringScan(ColumnSegment &segment, ColumnScanState &state, idx_t scan_count, Vector &result); static void Select(ColumnSegment &segment, ColumnScanState &state, idx_t vector_count, Vector &result, - const SelectionVector &sel, idx_t sel_count); + const SelectionVector &sel, idx_t sel_count, idx_t result_offset, ScanVectorType scan_type); static void StringFetchRow(ColumnSegment &segment, ColumnFetchState &state, row_t row_id, Vector &result, idx_t result_idx); @@ -224,18 +277,16 @@ struct UncompressedStringStorage { public: static void SetDictionary(ColumnSegment &segment, BufferHandle &handle, StringDictionaryContainer dict); static StringDictionaryContainer GetDictionary(ColumnSegment &segment, BufferHandle &handle); - static uint32_t GetDictionaryEnd(ColumnSegment &segment, BufferHandle &handle); static idx_t RemainingSpace(ColumnSegment &segment, BufferHandle &handle); static void WriteString(ColumnSegment &segment, string_t string, block_id_t &result_block, int32_t &result_offset); static void WriteStringMemory(ColumnSegment &segment, string_t string, block_id_t &result_block, int32_t &result_offset); static string_t ReadOverflowString(const QueryContext &context, ColumnSegment &segment, Vector &result, block_id_t block, int32_t offset); - static string_t ReadString(data_ptr_t target, int32_t offset, uint32_t string_length); - static string_t ReadStringWithLength(data_ptr_t target, int32_t offset); + static string_t ReadStringWithLength(CompressionSegmentReader reader, int32_t offset); static void WriteStringMarker(data_ptr_t target, block_id_t block_id, int32_t offset); - static void ReadStringMarker(data_ptr_t target, block_id_t &block_id, int32_t &offset); + // FIXME: Remove this raw pointer helper once FSST no longer needs it. inline static string_t FetchStringFromDict(const QueryContext &context, ColumnSegment &segment, uint32_t dict_end_offset, Vector &result, data_ptr_t base_ptr, int32_t dict_offset, uint32_t string_length) { @@ -244,7 +295,6 @@ struct UncompressedStringStorage { // regular string - fetch from dictionary auto dict_end = base_ptr + dict_end_offset; auto dict_pos = dict_end - dict_offset; - auto str_ptr = char_ptr_cast(dict_pos); return string_t(str_ptr, string_length); } else if (string_length == 0) { @@ -258,9 +308,25 @@ struct UncompressedStringStorage { return string_t(char_ptr_cast(base_ptr), 0); } else { // read overflow string - block_id_t block_id; - int32_t offset; - ReadStringMarker(base_ptr + dict_end_offset - AbsValue(dict_offset), block_id, offset); + auto marker = base_ptr + dict_end_offset - AbsValue(dict_offset); + block_id_t block_id = Load(marker); + int32_t offset = Load(marker + sizeof(block_id_t)); + + return ReadOverflowString(context, segment, result, block_id, offset); + } + } + + inline static string_t FetchStringFromEntry(const QueryContext &context, ColumnSegment &segment, Vector &result, + const StringDictionaryEntry &entry) { + if (DUCKDB_LIKELY(!entry.is_overflow)) { + // regular string - fetch from dictionary + auto str_ptr = const_char_ptr_cast(entry.data.data()); + return string_t(str_ptr, NumericCast(entry.data.size())); + } else { + // read overflow string + D_ASSERT(entry.data.size() == BIG_STRING_MARKER_SIZE); + auto block_id = Load(entry.data.data()); + auto offset = Load(entry.data.data() + sizeof(block_id_t)); return ReadOverflowString(context, segment, result, block_id, offset); } diff --git a/src/duckdb/src/include/duckdb/storage/table/append_state.hpp b/src/duckdb/src/include/duckdb/storage/table/append_state.hpp index 912c93037..12a1e3fc3 100644 --- a/src/duckdb/src/include/duckdb/storage/table/append_state.hpp +++ b/src/duckdb/src/include/duckdb/storage/table/append_state.hpp @@ -89,8 +89,6 @@ struct RowGroupAppendState { unsafe_unique_array states; //! Offset within the row_group idx_t offset_in_row_group; - //! A sub-allocation block for transient storage - SuballocationBlock transient; }; struct TableAppendState { diff --git a/src/duckdb/src/include/duckdb/storage/table/column_data.hpp b/src/duckdb/src/include/duckdb/storage/table/column_data.hpp index 47b8893ea..8456d63dd 100644 --- a/src/duckdb/src/include/duckdb/storage/table/column_data.hpp +++ b/src/duckdb/src/include/duckdb/storage/table/column_data.hpp @@ -247,7 +247,7 @@ class ColumnData : public enable_shared_from_this { idx_t ScanVector(TransactionData transaction, idx_t vector_index, ColumnScanState &state, Vector &result, idx_t target_scan, UpdateScanType update_type); void SelectVector(ColumnScanState &state, Vector &result, idx_t target_count, const SelectionVector &sel, - idx_t sel_count); + idx_t sel_count, ScanVectorType scan_type); void FilterVector(ColumnScanState &state, Vector &result, idx_t target_count, SelectionVector &sel, idx_t &sel_count, const TableFilter &filter, TableFilterState &filter_state); diff --git a/src/duckdb/src/include/duckdb/storage/table/column_segment.hpp b/src/duckdb/src/include/duckdb/storage/table/column_segment.hpp index aac02c130..25a5cf012 100644 --- a/src/duckdb/src/include/duckdb/storage/table/column_segment.hpp +++ b/src/duckdb/src/include/duckdb/storage/table/column_segment.hpp @@ -63,7 +63,8 @@ class ColumnSegment : public SegmentBase { //! Scan one vector from this segment void Scan(ColumnScanState &state, idx_t scan_count, Vector &result, idx_t result_offset, ScanVectorType scan_type); //! Scan a subset of a vector (defined by the selection vector) - void Select(ColumnScanState &state, idx_t scan_count, Vector &result, const SelectionVector &sel, idx_t sel_count); + void Select(ColumnScanState &state, idx_t scan_count, Vector &result, const SelectionVector &sel, idx_t sel_count, + idx_t result_offset, ScanVectorType scan_type); //! Scan one vector while applying a filter to the vector, returning only the matching elements void Filter(ColumnScanState &state, idx_t scan_count, Vector &result, SelectionVector &sel, idx_t &sel_count, const TableFilter &filter, TableFilterState &filter_state); diff --git a/src/duckdb/src/include/duckdb/storage/table/index_entry.hpp b/src/duckdb/src/include/duckdb/storage/table/index_entry.hpp index 840429f10..fe9783e5b 100644 --- a/src/duckdb/src/include/duckdb/storage/table/index_entry.hpp +++ b/src/duckdb/src/include/duckdb/storage/table/index_entry.hpp @@ -114,7 +114,7 @@ class IndexWriteHandle { //! The IndexEntry provides a stable logical identity which refers to an interchangeable snapshot of an index. class IndexEntry : public enable_shared_from_this { public: - IndexEntry(unique_ptr index, ConstraintCheckMode check_mode); + IndexEntry(unique_ptr index, idx_t index_oid, ConstraintCheckMode check_mode); //! Append a chunk to the physical index, buffering it while the index is unbound. void Append(DataChunk &chunk, Vector &row_ids); //! Appends a chunk using delete and checkpoint indexes where required. @@ -137,6 +137,8 @@ class IndexEntry : public enable_shared_from_this { Identifier GetName() const; //! Returns the physical index type. string GetIndexType() const; + //! Returns whether an unbound index has buffered WAL operations. + bool HasBufferedReplays() const; //! Destroys the physical index. void Retire(); //! Binds the unbound physical index without replacing it. @@ -196,6 +198,9 @@ class IndexEntry : public enable_shared_from_this { void SetBindState(IndexBindState state) { bind_state = state; } + idx_t GetIndexOid() const { + return index_oid; + } private: template @@ -210,6 +215,8 @@ class IndexEntry : public enable_shared_from_this { private: atomic bind_state; + //! The OID of this index. + const idx_t index_oid; //! Phase-fair lock protecting the physical index and all delta indexes owned by this entry. mutable StorageLock lock; //! The physical index owned by this stable logical entry. diff --git a/src/duckdb/src/include/duckdb/storage/table/row_group.hpp b/src/duckdb/src/include/duckdb/storage/table/row_group.hpp index b740aa728..644278d20 100644 --- a/src/duckdb/src/include/duckdb/storage/table/row_group.hpp +++ b/src/duckdb/src/include/duckdb/storage/table/row_group.hpp @@ -51,6 +51,7 @@ class TableFilterSet; struct ColumnFetchState; struct PrefetchState; struct RowGroupAppendState; +struct SuballocationBlock; class MetadataManager; class RowVersionManager; class CommitDropState; @@ -239,6 +240,8 @@ class RowGroup : public SegmentBase { PersistentRowGroupData SerializeRowGroupInfo(idx_t row_group_start) const; static void InitializeAppend(SegmentNode &row_group, RowGroupAppendState &append_state); + //! Continue a finalized append in a new row group, retaining unused transient block space + static void InitializeNextAppend(SegmentNode &row_group, RowGroupAppendState &append_state); void Append(RowGroupAppendState &append_state, DataChunk &chunk, idx_t append_count); void FinalizeAppend(RowGroupAppendState &append_state); @@ -327,6 +330,8 @@ class RowGroup : public SegmentBase { bool has_per_column_metadata_blocks = false; PerColumnMetadataBlocks per_column_metadata_blocks; atomic allocation_size; + //! Append cursor, retained between appends and moved when appending to the next row group + unique_ptr transient; //! The row id column data (mutable because `const` can lazy load) mutable unique_ptr row_id_column_data; //! Whether or not `row_id_column_data` is loaded (mutable because `const` can lazy load) diff --git a/src/duckdb/src/include/duckdb/storage/table/row_group_reorderer.hpp b/src/duckdb/src/include/duckdb/storage/table/row_group_reorderer.hpp index 7fd665a3c..822917336 100644 --- a/src/duckdb/src/include/duckdb/storage/table/row_group_reorderer.hpp +++ b/src/duckdb/src/include/duckdb/storage/table/row_group_reorderer.hpp @@ -27,6 +27,8 @@ class RowGroupReorderer { RowGroupReorderer(const RowGroupOrderOptions &options_p, TransactionData transaction_p); optional_ptr> GetRootSegment(RowGroupSegmentTree &row_groups); optional_ptr> GetNextRowGroup(SegmentNode &row_group); + //! The number of rows in the row groups that will be scanned (after pruning) + idx_t ScanRowCount() const; static Value RetrieveStat(const BaseStatistics &stats, OrderByStatistics order_by, OrderByColumnType column_type); static OffsetPruningResult GetOffsetAfterPruning(OrderByStatistics order_by, OrderByColumnType column_type, diff --git a/src/duckdb/src/include/duckdb/storage/table/scan_state.hpp b/src/duckdb/src/include/duckdb/storage/table/scan_state.hpp index 3ae022601..25c241d2b 100644 --- a/src/duckdb/src/include/duckdb/storage/table/scan_state.hpp +++ b/src/duckdb/src/include/duckdb/storage/table/scan_state.hpp @@ -148,6 +148,8 @@ struct ColumnScanState { void NextInternal(idx_t count); //! Returns the current row position in the segment idx_t GetPositionInSegment() const; + //! Releases the segment scan states (and the blocks they pin), including those of the child states + void ReleasePins(); }; enum class FetchType { @@ -269,8 +271,6 @@ class CollectionScanState { explicit CollectionScanState(TableScanState &parent_p); //! The query context for this scan QueryContext context; - //! The current row_group we are scanning - optional_ptr> row_group; //! The vector index within the row_group idx_t vector_index; //! The maximum row within the row group @@ -309,6 +309,8 @@ class CollectionScanState { ScanFilterInfo &GetFilterInfo(); ScanSamplingInfo &GetSamplingInfo(); TableScanOptions &GetOptions(); + optional_ptr> GetRowGroup() const; + void SetRowGroup(optional_ptr> row_group); optional_ptr> GetNextRowGroup(SegmentNode &row_group) const; optional_ptr> GetNextRowGroup(SegmentLock &l, SegmentNode &row_group) const; optional_ptr> GetRootSegment() const; @@ -324,12 +326,17 @@ class CollectionScanState { void InitializeColumnScans(); //! Processes the vector prepared by PrepareScanIO void ProcessPreparedScan(DuckTransaction &transaction, DataChunk &result); + //! Releases the row group and the blocks the column scans pin, the next assignment initializes them again + void ReleasePins(); private: //! Registers the remaining assignment's scan I/O, returning the async tasks that execute it vector> RegisterAssignmentIO(); private: + //! The segment node is the traversal cursor, while the shared pointer pins its replaceable payload + optional_ptr> row_group; + shared_ptr pinned_row_group; TableScanState &parent; }; @@ -381,6 +388,8 @@ class TableScanState { void InitializeColumnScans(); //! Rows scanned from persistent and transaction-local storage idx_t RowsScanned() const; + //! Releases what the scans of both storages pin, for a state that is kept around between assignments + void ReleasePins(); private: //! The column identifiers of the scan @@ -401,7 +410,8 @@ struct ParallelCollectionScanState { idx_t vector_index; idx_t max_row; idx_t batch_index; - atomic processed_rows; + //! Rows of assignments that were skipped entirely (e.g. by zonemaps) without being handed out + atomic skipped_rows; optional_idx row_number_base; mutex lock; diff --git a/src/duckdb/src/include/duckdb/storage/table/segment_base.hpp b/src/duckdb/src/include/duckdb/storage/table/segment_base.hpp index a56501c0d..4c1db1eae 100644 --- a/src/duckdb/src/include/duckdb/storage/table/segment_base.hpp +++ b/src/duckdb/src/include/duckdb/storage/table/segment_base.hpp @@ -15,10 +15,13 @@ namespace duckdb { template class SegmentBase { -public: +private: + friend T; + explicit SegmentBase(idx_t count) : count(count) { } +public: //! The amount of entries in this storage chunk atomic count; }; diff --git a/src/duckdb/src/include/duckdb/storage/table/table_index_list.hpp b/src/duckdb/src/include/duckdb/storage/table/table_index_list.hpp index 16a59a1c8..2c02f8a72 100644 --- a/src/duckdb/src/include/duckdb/storage/table/table_index_list.hpp +++ b/src/duckdb/src/include/duckdb/storage/table/table_index_list.hpp @@ -42,7 +42,7 @@ class TableIndexList { //! Iterates over shared ownership of stable index entries while holding the entry-list lock. TableIndexIterationHelper> IndexEntries() const; //! Adds an index entry to the list of index entries, and returns it. - shared_ptr AddIndex(unique_ptr index, ConstraintCheckMode check_mode); + shared_ptr AddIndex(unique_ptr index, idx_t index_oid, ConstraintCheckMode check_mode); //! Initializes the transaction-local delete and append indexes. void InitializeLocalIndexes(TableIndexList &delete_indexes, TableIndexList &append_indexes) const; //! Appends a chunk to all index entries. @@ -60,13 +60,11 @@ class TableIndexList { void RemoveFromIndexes(DataChunk &chunk, Vector &row_ids, IndexRemovalType removal_type, optional_idx active_checkpoint = optional_idx()); //! Removes an index entry from the list of index entries and release any storage the index owns. - void RemoveIndex(const Identifier &name); - //! Returns true, if the index name does not exist. - bool NameIsUnique(const string &name) const; - //! Returns true if an index with the given name exists. - bool Contains(const Identifier &name) const; + void RemoveIndex(idx_t index_oid); //! Returns shared ownership of the stable logical index entry matching the name. shared_ptr FindEntry(const Identifier &name) const; + //! Returns the entry matching the index OID. + shared_ptr FindEntry(const IndexEntry &index) const; //! Binds unbound indexes possibly present after loading an extension. void Bind(ClientContext &context, DataTableInfo &table_info, const optional &index_type = {}); //! Returns true, if there are no index entries. @@ -83,6 +81,8 @@ class TableIndexList { annotated_lock_guard lock(index_entries_lock); return unbound_count != 0; } + //! Returns true if an unbound index has buffered WAL operations. + bool HasBufferedReplays() const; //! Returns true, if there are unique indexes. bool HasUniqueIndexes() const; //! Verifies all unique ART indexes that are not deferred, optionally recording conflicts. @@ -131,8 +131,8 @@ class TableIndexList { vector> GetConflictTargetColumns(const ConflictInfo &conflict_info) const; //! Get the combined column ids of the unique indexes. unordered_set GetUniqueIndexColumns() const; - //! Serializes the index matching the name for the write-ahead log, if it exists. - unique_ptr SerializeToWAL(const Identifier &name, const StorageVersion version); + //! Serializes the index matching the OID for the write-ahead log, if it exists. + unique_ptr SerializeToWAL(idx_t index_oid, const StorageVersion version); public: //! Initialize an index_chunk from a table. diff --git a/src/duckdb/src/include/duckdb/storage/temporary_file_manager.hpp b/src/duckdb/src/include/duckdb/storage/temporary_file_manager.hpp index 970b9995b..c6c5aa2e0 100644 --- a/src/duckdb/src/include/duckdb/storage/temporary_file_manager.hpp +++ b/src/duckdb/src/include/duckdb/storage/temporary_file_manager.hpp @@ -112,7 +112,7 @@ struct TemporaryFileIndex { struct BlockIndexManager { public: BlockIndexManager(); - explicit BlockIndexManager(TemporaryFileManager &manager); + explicit BlockIndexManager(TemporaryFileManager &manager, bool encrypted = false); public: //! Obtains a new block index from the index manager @@ -140,6 +140,8 @@ struct BlockIndexManager { set indexes_in_use; //! The TemporaryFileManager that "owns" this BlockIndexManager optional_ptr manager; + //! Whether the tracked file is encrypted + bool encrypted; }; //===--------------------------------------------------------------------===// diff --git a/src/duckdb/src/include/duckdb/transaction/commit_state.hpp b/src/duckdb/src/include/duckdb/transaction/commit_state.hpp index bb2715f7a..3c4988d81 100644 --- a/src/duckdb/src/include/duckdb/transaction/commit_state.hpp +++ b/src/duckdb/src/include/duckdb/transaction/commit_state.hpp @@ -35,7 +35,7 @@ enum class CommitMode { COMMIT, REVERT_COMMIT }; //! An index that has been marked for removal from a table's index list once the commit chain succeeds. struct PendingIndexRemoval { reference indexes; - Identifier name; + idx_t index_oid; }; //! Accumulates block marks and index removals during commit so they can be applied together once the @@ -51,7 +51,7 @@ class CommitDropState { //! Register an index to be removed from a table's index list during FinalizeCommit. Index removal will drop in //! memory index data and also marks all blocks on disk as free blocks allowing for reclamation. Block marking for //! indexes is handled implicitly along destruction paths for index memory. - void RemoveIndex(TableIndexList &indexes, Identifier name); + void RemoveIndex(TableIndexList &indexes, idx_t index_oid); //! Finalize accumulated block marks and index removals. void FinalizeCommit(); //! True if no work has been queued. diff --git a/src/duckdb/src/include/duckdb/transaction/local_storage.hpp b/src/duckdb/src/include/duckdb/transaction/local_storage.hpp index 37f3b6f67..7462f5337 100644 --- a/src/duckdb/src/include/duckdb/transaction/local_storage.hpp +++ b/src/duckdb/src/include/duckdb/transaction/local_storage.hpp @@ -69,8 +69,8 @@ class LocalTableStorage : public enable_shared_from_this { TableIndexList delete_indexes; //! Set to INSERT_DUPLICATES, if we are skipping constraint checking during, e.g., WAL replay. IndexAppendMode index_append_mode = IndexAppendMode::DEFAULT; - //! The number of deleted rows - idx_t deleted_rows; + //! The number of deleted rows (parallel DELETEs update it concurrently) + atomic deleted_rows; //! The optimistic row group collections associated with this table. vector> optimistic_collections; @@ -122,9 +122,9 @@ class LocalTableStorage : public enable_shared_from_this { class LocalTableManager { public: shared_ptr MoveEntry(DataTable &table); - reference_map_t> MoveEntries(); + reference_map_t> MoveEntries(); vector> GetEntries() const; - optional_ptr GetStorage(DataTable &table) const; + optional_ptr GetStorage(const DataTable &table) const; LocalTableStorage &GetOrCreateStorage(ClientContext &context, DataTable &table); idx_t EstimatedSize() const; bool IsEmpty() const; @@ -132,7 +132,7 @@ class LocalTableManager { private: mutable mutex table_storage_lock; - reference_map_t> table_storage; + reference_map_t> table_storage; }; //! The LocalStorage class holds appends that have not been committed yet @@ -214,7 +214,7 @@ class LocalStorage { //! Returns true, if the local storage contains the row id. bool CanFetch(DataTable &table, const row_t row_id); TableIndexList &GetIndexes(ClientContext &context, DataTable &table); - optional_ptr GetStorage(DataTable &table); + optional_ptr GetStorage(const DataTable &table); void VerifyNewConstraint(DataTable &parent, const BoundConstraint &constraint); diff --git a/src/duckdb/src/include/duckdb/transaction/undo_buffer.hpp b/src/duckdb/src/include/duckdb/transaction/undo_buffer.hpp index 389b8d8ab..941ebe8c6 100644 --- a/src/duckdb/src/include/duckdb/transaction/undo_buffer.hpp +++ b/src/duckdb/src/include/duckdb/transaction/undo_buffer.hpp @@ -71,12 +71,14 @@ class UndoBuffer { DuckTransaction &transaction; UndoBufferAllocator allocator; ActiveTransactionState active_transaction_state = ActiveTransactionState::UNSET; + //! Number of entries already cleaned up, so that a cleanup that threw part-way can be resumed + idx_t cleaned_up_entries = 0; private: template void IterateEntries(UndoBuffer::IteratorState &state, T &&callback); template - void IterateEntries(UndoBuffer::IteratorState &state, UndoBuffer::IteratorState &end_state, T &&callback); + void ReverseIterateEntries(UndoBuffer::IteratorState &end_state, T &&callback); template void ReverseIterateEntries(T &&callback); }; diff --git a/src/duckdb/src/include/duckdb_extension_v2.h b/src/duckdb/src/include/duckdb_extension_v2.h index 883ba6449..bc3df295e 100644 --- a/src/duckdb/src/include/duckdb_extension_v2.h +++ b/src/duckdb/src/include/duckdb_extension_v2.h @@ -80,7 +80,6 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_bignum_encode) (const uint8_t *in_data, idx_t in_length, bool is_negative, uint8_t *out_data, idx_t out_capacity, idx_t *out_length, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR (*duckdb_v2_close)(duckdb_v2_database_handle *db); DUCKDB_V2_ERROR(*duckdb_v2_column_data_collection_append) (duckdb_v2_column_data_collection_handle collection, duckdb_v2_column_data_collection_append_state_handle state, duckdb_v2_data_chunk_handle chunk, duckdb_v2_error_info_handle *err); @@ -92,11 +91,8 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_column_data_collection_combine) (duckdb_v2_column_data_collection_handle target, duckdb_v2_column_data_collection_handle *source, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_column_data_collection_create_with_connection) - (duckdb_v2_connection_handle conn, const duckdb_v2_logical_type_handle *types_array, idx_t types_count, - duckdb_v2_column_data_collection_handle *out_collection, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_column_data_collection_create_with_context) - (duckdb_v2_context_handle context, const duckdb_v2_logical_type_handle *types_array, idx_t types_count, + DUCKDB_V2_ERROR(*duckdb_v2_column_data_collection_create) + (duckdb_v2_factory_handle factory, const duckdb_v2_logical_type_handle *types_array, idx_t types_count, duckdb_v2_column_data_collection_handle *out_collection, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_column_data_collection_destroy)(duckdb_v2_column_data_collection_handle *collection); DUCKDB_V2_ERROR(*duckdb_v2_column_data_collection_reset) @@ -118,68 +114,27 @@ typedef struct { duckdb_v2_column_data_collection_worker_scan_state_handle *out_state, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_column_data_collection_worker_scan_state_destroy) (duckdb_v2_column_data_collection_worker_scan_state_handle *state); - DUCKDB_V2_ERROR(*duckdb_v2_connect) - (duckdb_v2_database_handle db, duckdb_v2_connection_handle *out_conn, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_connection_create_type_from_id) - (duckdb_v2_connection_handle conn, DUCKDB_V2_LOGICAL_TYPE_ID type_id, const duckdb_v2_identifier_t *param_names, - const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_connection_create_type_from_name) - (duckdb_v2_connection_handle conn, duckdb_v2_qname_handle name, const duckdb_v2_identifier_t *param_names, - const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, + DUCKDB_V2_ERROR(*duckdb_v2_config_get_option_by_index) + (duckdb_v2_config_handle config, idx_t index, duckdb_v2_option_handle *out_option, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_connection_create_type_from_text) - (duckdb_v2_connection_handle conn, duckdb_v2_str text, duckdb_v2_logical_type_handle *out_type, + DUCKDB_V2_ERROR(*duckdb_v2_config_get_option_by_name) + (duckdb_v2_config_handle config, const duckdb_v2_identifier_t *name, duckdb_v2_option_handle *out_option, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_connection_create_type_with_alias) - (duckdb_v2_connection_handle conn, duckdb_v2_logical_type_handle base_type, duckdb_v2_identifier_t alias_name, - duckdb_v2_logical_type_handle *out_type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_config_get_option_count) + (duckdb_v2_config_handle config, idx_t *out_count, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_create) + (duckdb_v2_instance_handle instance, duckdb_v2_connection_handle *out_conn, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_connection_destroy)(duckdb_v2_connection_handle *conn); DUCKDB_V2_ERROR(*duckdb_v2_connection_interrupt) (duckdb_v2_connection_handle conn, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_connection_option_get) - (duckdb_v2_connection_handle conn, duckdb_v2_identifier_t name, duckdb_v2_option_handle *out_option, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_connection_option_get_by_index) - (duckdb_v2_connection_handle conn, idx_t index, duckdb_v2_option_handle *out_option, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_connection_option_get_count) - (duckdb_v2_connection_handle conn, idx_t *out_count, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_connection_option_set) - (duckdb_v2_connection_handle conn, duckdb_v2_option_handle option, DUCKDB_V2_SETTING_SCOPE scope, - duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_connection_progress_get) (duckdb_v2_connection_handle conn, double *out_percentage, uint64_t *out_rows_processed, uint64_t *out_total_rows_to_process, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_context_create_type_from_id) - (duckdb_v2_context_handle ctx, DUCKDB_V2_LOGICAL_TYPE_ID type_id, const duckdb_v2_identifier_t *param_names, - const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_context_create_type_from_name) - (duckdb_v2_context_handle ctx, duckdb_v2_qname_handle name, const duckdb_v2_identifier_t *param_names, - const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_context_create_type_from_text) - (duckdb_v2_context_handle ctx, duckdb_v2_str text, duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_context_create_type_with_alias) - (duckdb_v2_context_handle ctx, duckdb_v2_logical_type_handle base_type, duckdb_v2_identifier_t alias_name, - duckdb_v2_logical_type_handle *out_type, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_create_environment) - (duckdb_v2_environment_handle *out_env, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_data_chunk_copy_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_data_chunk_handle chunk, duckdb_v2_data_chunk_handle *out_chunk, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_data_chunk_copy_with_context) - (duckdb_v2_context_handle context, duckdb_v2_data_chunk_handle chunk, duckdb_v2_data_chunk_handle *out_chunk, + DUCKDB_V2_ERROR(*duckdb_v2_data_chunk_copy) + (duckdb_v2_factory_handle factory, duckdb_v2_data_chunk_handle chunk, duckdb_v2_data_chunk_handle *out_chunk, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_data_chunk_create) - (const duckdb_v2_logical_type_handle *types, idx_t column_count, duckdb_v2_data_chunk_handle *out_chunk, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_data_chunk_create_with_connection) - (duckdb_v2_connection_handle conn, const duckdb_v2_logical_type_handle *types, idx_t column_count, - duckdb_v2_data_chunk_handle *out_chunk, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_data_chunk_create_with_context) - (duckdb_v2_context_handle context, const duckdb_v2_logical_type_handle *types, idx_t column_count, + (duckdb_v2_factory_handle factory, const duckdb_v2_logical_type_handle *types, idx_t column_count, duckdb_v2_data_chunk_handle *out_chunk, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_data_chunk_destroy)(duckdb_v2_data_chunk_handle *chunk); DUCKDB_V2_ERROR(*duckdb_v2_data_chunk_get_size) @@ -189,26 +144,43 @@ typedef struct { duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_data_chunk_get_vector_count) (duckdb_v2_data_chunk_handle chunk, idx_t *out_count, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_database_option_get) - (duckdb_v2_database_handle db, duckdb_v2_identifier_t name, duckdb_v2_option_handle *out_option, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_database_option_get_by_index) - (duckdb_v2_database_handle db, idx_t index, duckdb_v2_option_handle *out_option, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_database_option_get_count) - (duckdb_v2_database_handle db, idx_t *out_count, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_database_option_set) - (duckdb_v2_database_handle db, duckdb_v2_option_handle option, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR (*duckdb_v2_destroy_environment)(duckdb_v2_environment_handle *env); - DUCKDB_V2_ERROR (*duckdb_v2_disconnect)(duckdb_v2_connection_handle *conn); - DUCKDB_V2_ERROR(*duckdb_v2_environment_database_count) + DUCKDB_V2_ERROR(*duckdb_v2_environment_create) + (duckdb_v2_environment_handle *out_env, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_environment_destroy)(duckdb_v2_environment_handle *env); + DUCKDB_V2_ERROR(*duckdb_v2_environment_get_instance_count) (duckdb_v2_environment_handle env, idx_t *out_count, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_error_info_destroy)(duckdb_v2_error_info_handle *info); DUCKDB_V2_ERROR (*duckdb_v2_error_info_get_code)(duckdb_v2_error_info_handle info, DUCKDB_V2_ERROR *out_code); - DUCKDB_V2_ERROR(*duckdb_v2_error_info_get_raw_message) - (duckdb_v2_error_info_handle info, duckdb_v2_str *out_raw_message); + DUCKDB_V2_ERROR (*duckdb_v2_error_info_get_raw_text)(duckdb_v2_error_info_handle info, duckdb_v2_str *out_raw_text); DUCKDB_V2_ERROR (*duckdb_v2_error_info_get_text)(duckdb_v2_error_info_handle info, duckdb_v2_str *out_text); DUCKDB_V2_ERROR (*duckdb_v2_error_info_set_code)(duckdb_v2_error_info_handle info, DUCKDB_V2_ERROR code); - DUCKDB_V2_ERROR (*duckdb_v2_error_info_set_text)(duckdb_v2_error_info_handle info, duckdb_v2_str text); + DUCKDB_V2_ERROR (*duckdb_v2_error_info_set_text)(duckdb_v2_error_info_handle info, const duckdb_v2_str *text); + DUCKDB_V2_ERROR(*duckdb_v2_factory_create_type_from_id) + (duckdb_v2_factory_handle factory, DUCKDB_V2_LOGICAL_TYPE_ID type_id, const duckdb_v2_identifier_t *param_names, + const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_factory_create_type_from_name) + (duckdb_v2_factory_handle factory, duckdb_v2_qname_handle name, const duckdb_v2_identifier_t *param_names, + const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_factory_create_type_from_text) + (duckdb_v2_factory_handle factory, const duckdb_v2_str *text, duckdb_v2_logical_type_handle *out_type, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_factory_create_type_with_alias) + (duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle base_type, + const duckdb_v2_identifier_t *alias_name, duckdb_v2_logical_type_handle *out_type, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_attach) + (duckdb_v2_instance_handle instance, const duckdb_v2_identifier_t *name, const duckdb_v2_str *path, + duckdb_v2_attach_options_handle options, bool make_default, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_create) + (duckdb_v2_environment_handle env, duckdb_v2_instance_handle *out_instance, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_create_with_options) + (duckdb_v2_environment_handle env, const duckdb_v2_str *option_names, const duckdb_v2_str *option_values, + idx_t option_count, duckdb_v2_instance_handle *out_instance, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_instance_destroy)(duckdb_v2_instance_handle *instance); + DUCKDB_V2_ERROR(*duckdb_v2_instance_detach) + (duckdb_v2_instance_handle instance, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_library_version)(duckdb_v2_str *out_version, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_logical_type_copy) (duckdb_v2_logical_type_handle type, duckdb_v2_logical_type_handle *out_type, duckdb_v2_error_info_handle *err); @@ -228,27 +200,17 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_logical_type_to_text) (duckdb_v2_logical_type_handle type, char *out_text, idx_t out_capacity, idx_t *out_length, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_open) - (duckdb_v2_environment_handle env, duckdb_v2_str path, duckdb_v2_option_handle *options, idx_t option_count, - duckdb_v2_database_handle *out_db, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_option_create) - (duckdb_v2_identifier_t name, duckdb_v2_str setting, duckdb_v2_option_handle *out_option, - duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_option_destroy)(duckdb_v2_option_handle *option); DUCKDB_V2_ERROR(*duckdb_v2_option_get_alias) (duckdb_v2_option_handle option, idx_t index, duckdb_v2_identifier_t *out_alias, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_option_get_alias_count) (duckdb_v2_option_handle option, idx_t *out_count, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_option_get_default_setting) - (duckdb_v2_option_handle option, duckdb_v2_str *out_default_setting, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_option_get_default_value) + (duckdb_v2_option_handle option, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_option_get_description) (duckdb_v2_option_handle option, duckdb_v2_str *out_description, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_option_get_name) (duckdb_v2_option_handle option, duckdb_v2_identifier_t *out_name, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_option_get_setting) - (duckdb_v2_option_handle option, duckdb_v2_str *out_setting, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_option_get_target_scope) - (duckdb_v2_option_handle option, DUCKDB_V2_OPTION_TARGET_SCOPE *out_target_scope, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_parse_sql) (duckdb_v2_connection_handle conn, const char *sql, duckdb_v2_statement_iterator_handle *out_iterator, duckdb_v2_error_info_handle *err); @@ -264,8 +226,9 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_result_get_statement_type) (duckdb_v2_result_handle result, DUCKDB_V2_STATEMENT_TYPE *out_type, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_result_render_box) - (duckdb_v2_result_handle *result, idx_t max_rows, idx_t max_width, idx_t max_col_width, duckdb_v2_str null_value, - idx_t render_mode, idx_t limit, duckdb_v2_text_sink_fn sink, void *user_data, duckdb_v2_error_info_handle *err); + (duckdb_v2_result_handle *result, idx_t max_rows, idx_t max_width, idx_t max_col_width, + const duckdb_v2_str *null_value, idx_t render_mode, idx_t limit, duckdb_v2_text_sink_fn sink, void *user_data, + duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_result_step) (duckdb_v2_result_handle result, duckdb_v2_data_chunk_handle *out_chunk, DUCKDB_V2_RESULT_STEP_STATUS *out_status, duckdb_v2_error_info_handle *err); @@ -288,235 +251,120 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_statement_iterator_next) (duckdb_v2_statement_iterator_handle iterator, duckdb_v2_sql_statement_handle *out_statement, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_cast_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_value_handle value, duckdb_v2_logical_type_handle target_type, - duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_cast_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_value_handle value, duckdb_v2_logical_type_handle target_type, + DUCKDB_V2_ERROR(*duckdb_v2_value_cast) + (duckdb_v2_factory_handle factory, duckdb_v2_value_handle value, duckdb_v2_logical_type_handle target_type, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_array_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_logical_type_handle child_type, const duckdb_v2_value_handle *children, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_array) + (duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle child_type, const duckdb_v2_value_handle *children, idx_t child_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_array_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_logical_type_handle child_type, const duckdb_v2_value_handle *children, - idx_t child_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_bigint_with_connection) - (duckdb_v2_connection_handle conn, int64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_bigint_with_context) - (duckdb_v2_context_handle ctx, int64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_bignum_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_str in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_bignum_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_str in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_bit_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_str in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_bit_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_str in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_bigint) + (duckdb_v2_factory_handle factory, int64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_blob_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_str in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_bignum) + (duckdb_v2_factory_handle factory, const duckdb_v2_str *in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_blob_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_str in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_bit) + (duckdb_v2_factory_handle factory, const duckdb_v2_str *in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_bool_with_connection) - (duckdb_v2_connection_handle conn, bool in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_blob) + (duckdb_v2_factory_handle factory, const duckdb_v2_str *in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_bool_with_context) - (duckdb_v2_context_handle ctx, bool in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_date_with_connection) - (duckdb_v2_connection_handle conn, int32_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_bool) + (duckdb_v2_factory_handle factory, bool in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_date_with_context) - (duckdb_v2_context_handle ctx, int32_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_date) + (duckdb_v2_factory_handle factory, int32_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_decimal_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_hugeint_t in_value, uint8_t width, uint8_t scale, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_decimal) + (duckdb_v2_factory_handle factory, const duckdb_v2_hugeint_t *in_value, uint8_t width, uint8_t scale, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_decimal_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_hugeint_t in_value, uint8_t width, uint8_t scale, - duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_double_with_connection) - (duckdb_v2_connection_handle conn, double in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_double_with_context) - (duckdb_v2_context_handle ctx, double in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_float_with_connection) - (duckdb_v2_connection_handle conn, float in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_float_with_context) - (duckdb_v2_context_handle ctx, float in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_hugeint_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_hugeint_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_double) + (duckdb_v2_factory_handle factory, double in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_hugeint_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_hugeint_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_float) + (duckdb_v2_factory_handle factory, float in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_int_with_connection) - (duckdb_v2_connection_handle conn, int32_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_hugeint) + (duckdb_v2_factory_handle factory, const duckdb_v2_hugeint_t *in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_int_with_context) - (duckdb_v2_context_handle ctx, int32_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_int) + (duckdb_v2_factory_handle factory, int32_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_interval_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_interval_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_interval) + (duckdb_v2_factory_handle factory, const duckdb_v2_interval_t *in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_interval_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_interval_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_list_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_logical_type_handle child_type, const duckdb_v2_value_handle *children, - idx_t child_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_list_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_logical_type_handle child_type, const duckdb_v2_value_handle *children, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_list) + (duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle child_type, const duckdb_v2_value_handle *children, idx_t child_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_map_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_logical_type_handle key_type, duckdb_v2_logical_type_handle value_type, - const duckdb_v2_value_handle *keys, const duckdb_v2_value_handle *values, idx_t entry_count, - duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_map_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_logical_type_handle key_type, duckdb_v2_logical_type_handle value_type, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_map) + (duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle key_type, duckdb_v2_logical_type_handle value_type, const duckdb_v2_value_handle *keys, const duckdb_v2_value_handle *values, idx_t entry_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_value_create_null) - (duckdb_v2_logical_type_handle type, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_null_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_logical_type_handle type, duckdb_v2_value_handle *out_value, + (duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle type, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_null_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_logical_type_handle type, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_smallint) + (duckdb_v2_factory_handle factory, int16_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_smallint_with_connection) - (duckdb_v2_connection_handle conn, int16_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_smallint_with_context) - (duckdb_v2_context_handle ctx, int16_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_struct_with_connection) - (duckdb_v2_connection_handle conn, const duckdb_v2_identifier_t *names, const duckdb_v2_value_handle *children, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_struct) + (duckdb_v2_factory_handle factory, const duckdb_v2_identifier_t *names, const duckdb_v2_value_handle *children, idx_t field_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_struct_with_context) - (duckdb_v2_context_handle ctx, const duckdb_v2_identifier_t *names, const duckdb_v2_value_handle *children, - idx_t field_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_time_ns_with_connection) - (duckdb_v2_connection_handle conn, int64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_time_ns_with_context) - (duckdb_v2_context_handle ctx, int64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_time_tz_with_connection) - (duckdb_v2_connection_handle conn, uint64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_time_tz_with_context) - (duckdb_v2_context_handle ctx, uint64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_time_with_connection) - (duckdb_v2_connection_handle conn, int64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_time_with_context) - (duckdb_v2_context_handle ctx, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_time) + (duckdb_v2_factory_handle factory, int64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_ms_with_connection) - (duckdb_v2_connection_handle conn, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_time_ns) + (duckdb_v2_factory_handle factory, int64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_ms_with_context) - (duckdb_v2_context_handle ctx, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_time_tz) + (duckdb_v2_factory_handle factory, uint64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_ns_with_connection) - (duckdb_v2_connection_handle conn, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp) + (duckdb_v2_factory_handle factory, int64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_ns_with_context) - (duckdb_v2_context_handle ctx, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_ms) + (duckdb_v2_factory_handle factory, int64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_sec_with_connection) - (duckdb_v2_connection_handle conn, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_ns) + (duckdb_v2_factory_handle factory, int64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_sec_with_context) - (duckdb_v2_context_handle ctx, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_sec) + (duckdb_v2_factory_handle factory, int64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_tz_ns_with_connection) - (duckdb_v2_connection_handle conn, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_tz) + (duckdb_v2_factory_handle factory, int64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_tz_ns_with_context) - (duckdb_v2_context_handle ctx, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_tz_ns) + (duckdb_v2_factory_handle factory, int64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_tz_with_connection) - (duckdb_v2_connection_handle conn, int64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_tinyint) + (duckdb_v2_factory_handle factory, int8_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_tz_with_context) - (duckdb_v2_context_handle ctx, int64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_with_connection) - (duckdb_v2_connection_handle conn, int64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_timestamp_with_context) - (duckdb_v2_context_handle ctx, int64_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_tinyint_with_connection) - (duckdb_v2_connection_handle conn, int8_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_tinyint_with_context) - (duckdb_v2_context_handle ctx, int8_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_tuple_with_connection) - (duckdb_v2_connection_handle conn, const duckdb_v2_value_handle *children, idx_t field_count, - duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_tuple_with_context) - (duckdb_v2_context_handle ctx, const duckdb_v2_value_handle *children, idx_t field_count, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_tuple) + (duckdb_v2_factory_handle factory, const duckdb_v2_value_handle *children, idx_t field_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_type_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_logical_type_handle type, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_type_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_logical_type_handle in_type, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_ubigint_with_connection) - (duckdb_v2_connection_handle conn, uint64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_type) + (duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle in_type, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_ubigint_with_context) - (duckdb_v2_context_handle ctx, uint64_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_ubigint) + (duckdb_v2_factory_handle factory, uint64_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_uhugeint_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_uhugeint_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_uhugeint) + (duckdb_v2_factory_handle factory, const duckdb_v2_uhugeint_t *in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_uhugeint_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_uhugeint_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_uint) + (duckdb_v2_factory_handle factory, uint32_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_uint_with_connection) - (duckdb_v2_connection_handle conn, uint32_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_usmallint) + (duckdb_v2_factory_handle factory, uint16_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_uint_with_context) - (duckdb_v2_context_handle ctx, uint32_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_utinyint) + (duckdb_v2_factory_handle factory, uint8_t in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_usmallint_with_connection) - (duckdb_v2_connection_handle conn, uint16_t in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_uuid) + (duckdb_v2_factory_handle factory, const duckdb_v2_hugeint_t *in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_usmallint_with_context) - (duckdb_v2_context_handle ctx, uint16_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_utinyint_with_connection) - (duckdb_v2_connection_handle conn, uint8_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_utinyint_with_context) - (duckdb_v2_context_handle ctx, uint8_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_uuid_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_hugeint_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_uuid_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_hugeint_t in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_varchar_with_connection) - (duckdb_v2_connection_handle conn, duckdb_v2_str in_value, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_value_create_varchar_with_context) - (duckdb_v2_context_handle ctx, duckdb_v2_str in_value, duckdb_v2_value_handle *out_value, + DUCKDB_V2_ERROR(*duckdb_v2_value_create_varchar) + (duckdb_v2_factory_handle factory, const duckdb_v2_str *in_value, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_value_destroy)(duckdb_v2_value_handle *value); DUCKDB_V2_ERROR(*duckdb_v2_value_get_bigint) @@ -624,51 +472,27 @@ typedef struct { (duckdb_v2_vector_handle vector, idx_t size, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_vector_set_value) (duckdb_v2_vector_handle vector, idx_t row, duckdb_v2_value_handle value, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_bind_get_arg_count) - (duckdb_v2_aggregate_function_bind_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_bind_get_arg_type) - (duckdb_v2_aggregate_function_bind_info_handle info, idx_t index, duckdb_v2_logical_type_handle *type, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_bind_get_arg_value) - (duckdb_v2_aggregate_function_bind_info_handle info, idx_t index, duckdb_v2_value_handle *value, - duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_scalar_function) + (duckdb_v2_connection_handle conn, duckdb_v2_scalar_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_context_log) - (duckdb_v2_context_handle ctx, DUCKDB_V2_LOG_LEVEL level, duckdb_v2_str log_type, duckdb_v2_str message, - duckdb_v2_error_info_handle *err); + (duckdb_v2_context_handle ctx, DUCKDB_V2_LOG_LEVEL level, const duckdb_v2_str *log_type, + const duckdb_v2_str *message, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_scalar_function) + (duckdb_v2_extension_handle extension, duckdb_v2_scalar_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_function_signature_add_parameter) - (duckdb_v2_function_signature_handle sig, duckdb_v2_identifier_t name, duckdb_v2_logical_type_handle type, - duckdb_v2_value_handle value, duckdb_v2_error_info_handle *err); + (duckdb_v2_function_signature_handle sig, const duckdb_v2_identifier_t *name, duckdb_v2_logical_type_handle type, + duckdb_v2_value_handle value, DUCKDB_V2_FUNCTION_PARAMETER_KIND kind, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_function_signature_set_return_type) (duckdb_v2_function_signature_handle sig, duckdb_v2_logical_type_handle type, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_function_signature_set_varargs) - (duckdb_v2_function_signature_handle sig, duckdb_v2_logical_type_handle type, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_bind_get_arg_count) - (duckdb_v2_scalar_function_bind_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_bind_get_arg_type) - (duckdb_v2_scalar_function_bind_info_handle info, idx_t index, duckdb_v2_logical_type_handle *type, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_bind_get_arg_value) - (duckdb_v2_scalar_function_bind_info_handle info, idx_t index, duckdb_v2_value_handle *value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_bind_get_user_data) - (duckdb_v2_scalar_function_bind_info_handle info, void **data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_bind_set_bind_data) - (duckdb_v2_scalar_function_bind_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_bind_set_return_type) - (duckdb_v2_scalar_function_bind_info_handle info, duckdb_v2_logical_type_handle return_type, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_create_with_connection) - (duckdb_v2_connection_handle connection, duckdb_v2_scalar_function_handle *function, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_create_with_extension) - (duckdb_v2_extension_handle extension, duckdb_v2_scalar_function_handle *function, - duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_create) + (duckdb_v2_factory_handle factory, duckdb_v2_scalar_function_handle *function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_scalar_function_destroy)(duckdb_v2_scalar_function_handle *function); DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_exec_get_arg) - (duckdb_v2_scalar_function_exec_info_handle info, uint32_t index, duckdb_v2_vector_handle *vector, + (duckdb_v2_scalar_function_exec_info_handle info, idx_t index, duckdb_v2_vector_handle *vector, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_exec_get_arg_count) - (duckdb_v2_scalar_function_exec_info_handle info, uint32_t *count, duckdb_v2_error_info_handle *err); + (duckdb_v2_scalar_function_exec_info_handle info, idx_t *positional_fixed, idx_t *positional_variadic, + idx_t *named_fixed, idx_t *named_variadic, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_exec_get_bind_data) (duckdb_v2_scalar_function_exec_info_handle info, void **data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_exec_get_init_data) @@ -689,8 +513,6 @@ typedef struct { (duckdb_v2_scalar_function_init_info_handle info, void **data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_init_set_init_data) (duckdb_v2_scalar_function_init_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_register) - (duckdb_v2_scalar_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_set_bind_callback) (duckdb_v2_scalar_function_handle function, duckdb_v2_scalar_function_bind_callback_fn callback, duckdb_v2_error_info_handle *err); @@ -701,13 +523,9 @@ typedef struct { (duckdb_v2_scalar_function_handle function, duckdb_v2_scalar_function_init_callback_fn callback, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_set_name) - (duckdb_v2_scalar_function_handle function, duckdb_v2_str *name, duckdb_v2_error_info_handle *err); + (duckdb_v2_scalar_function_handle function, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_set_user_data) (duckdb_v2_scalar_function_handle function, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_bind_get_user_data) - (duckdb_v2_aggregate_function_bind_info_handle info, void **data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_bind_set_bind_data) - (duckdb_v2_aggregate_function_bind_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_bind_set_return_type) (duckdb_v2_aggregate_function_bind_info_handle info, duckdb_v2_logical_type_handle return_type, duckdb_v2_error_info_handle *err); @@ -721,12 +539,8 @@ typedef struct { (duckdb_v2_aggregate_function_combine_info_handle info, void ***states, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_combine_get_user_data) (duckdb_v2_aggregate_function_combine_info_handle info, void **data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_create_with_connection) - (duckdb_v2_connection_handle connection, duckdb_v2_aggregate_function_handle *function, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_create_with_extension) - (duckdb_v2_extension_handle extension, duckdb_v2_aggregate_function_handle *function, - duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_create) + (duckdb_v2_factory_handle factory, duckdb_v2_aggregate_function_handle *function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_aggregate_function_destroy)(duckdb_v2_aggregate_function_handle *function); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_destroy_get_bind_data) (duckdb_v2_aggregate_function_destroy_info_handle info, void **data, duckdb_v2_error_info_handle *err); @@ -760,8 +574,6 @@ typedef struct { (duckdb_v2_aggregate_function_init_info_handle info, void ***states, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_init_get_user_data) (duckdb_v2_aggregate_function_init_info_handle info, void **data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_register) - (duckdb_v2_aggregate_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_set_bind_callback) (duckdb_v2_aggregate_function_handle function, duckdb_v2_aggregate_function_bind_callback_fn callback, duckdb_v2_error_info_handle *err); @@ -778,7 +590,8 @@ typedef struct { (duckdb_v2_aggregate_function_handle function, duckdb_v2_aggregate_function_init_callback_fn callback, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_set_name) - (duckdb_v2_aggregate_function_handle function, duckdb_v2_str *name, duckdb_v2_error_info_handle *err); + (duckdb_v2_aggregate_function_handle function, const duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_set_size_callback) (duckdb_v2_aggregate_function_handle function, duckdb_v2_aggregate_function_size_callback_fn callback, duckdb_v2_error_info_handle *err); @@ -794,10 +607,11 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_size_set_state_size) (duckdb_v2_aggregate_function_size_info_handle info, idx_t size, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_update_get_arg) - (duckdb_v2_aggregate_function_update_info_handle info, uint32_t index, duckdb_v2_vector_handle *vector, + (duckdb_v2_aggregate_function_update_info_handle info, idx_t index, duckdb_v2_vector_handle *vector, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_update_get_arg_count) - (duckdb_v2_aggregate_function_update_info_handle info, uint32_t *count, duckdb_v2_error_info_handle *err); + (duckdb_v2_aggregate_function_update_info_handle info, idx_t *positional_fixed, idx_t *positional_variadic, + idx_t *named_fixed, idx_t *named_variadic, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_update_get_bind_data) (duckdb_v2_aggregate_function_update_info_handle info, void **data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_update_get_row_count) @@ -806,35 +620,29 @@ typedef struct { (duckdb_v2_aggregate_function_update_info_handle info, void ***states, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_update_get_user_data) (duckdb_v2_aggregate_function_update_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_aggregate_function) + (duckdb_v2_connection_handle conn, duckdb_v2_aggregate_function_handle function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_aggregate_function) + (duckdb_v2_extension_handle extension, duckdb_v2_aggregate_function_handle function, + duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_set_property) (duckdb_v2_aggregate_function_handle function, DUCKDB_V2_FUNCTION_PROPERTY_KEY key, DUCKDB_V2_FUNCTION_PROPERTY_VALUE value, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_set_property) (duckdb_v2_scalar_function_handle function, DUCKDB_V2_FUNCTION_PROPERTY_KEY key, DUCKDB_V2_FUNCTION_PROPERTY_VALUE value, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_table_function) + (duckdb_v2_connection_handle conn, duckdb_v2_table_function_handle function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_table_function) + (duckdb_v2_extension_handle extension, duckdb_v2_table_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_table_function_bind_add_result_column) - (duckdb_v2_table_function_bind_info_handle info, duckdb_v2_identifier_t name, duckdb_v2_logical_type_handle type, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_table_function_bind_get_arg_count) - (duckdb_v2_table_function_bind_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_table_function_bind_get_arg_type) - (duckdb_v2_table_function_bind_info_handle info, idx_t index, duckdb_v2_logical_type_handle *type, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_table_function_bind_get_arg_value) - (duckdb_v2_table_function_bind_info_handle info, idx_t index, duckdb_v2_value_handle *value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_table_function_bind_get_user_data) - (duckdb_v2_table_function_bind_info_handle info, void **data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_table_function_bind_set_bind_data) - (duckdb_v2_table_function_bind_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); + (duckdb_v2_table_function_bind_info_handle info, const duckdb_v2_identifier_t *name, + duckdb_v2_logical_type_handle type, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_table_function_bind_set_cardinality) (duckdb_v2_table_function_bind_info_handle info, idx_t cardinality, bool is_exact, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_table_function_create_with_connection) - (duckdb_v2_connection_handle connection, duckdb_v2_table_function_handle *function, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_table_function_create_with_extension) - (duckdb_v2_extension_handle extension, duckdb_v2_table_function_handle *function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_create) + (duckdb_v2_factory_handle factory, duckdb_v2_table_function_handle *function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_table_function_destroy)(duckdb_v2_table_function_handle *function); DUCKDB_V2_ERROR(*duckdb_v2_table_function_exec_get_bind_data) (duckdb_v2_table_function_exec_info_handle info, void **data, duckdb_v2_error_info_handle *err); @@ -874,8 +682,6 @@ typedef struct { (duckdb_v2_table_function_progress_info_handle info, void **data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_table_function_progress_set_progress) (duckdb_v2_table_function_progress_info_handle info, double progress, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_table_function_register) - (duckdb_v2_table_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_bind_callback) (duckdb_v2_table_function_handle function, duckdb_v2_table_function_bind_callback_fn callback, duckdb_v2_error_info_handle *err); @@ -889,17 +695,14 @@ typedef struct { (duckdb_v2_table_function_handle function, duckdb_v2_table_function_init_local_callback_fn callback, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_name) - (duckdb_v2_table_function_handle function, duckdb_v2_str *name, duckdb_v2_error_info_handle *err); + (duckdb_v2_table_function_handle function, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_progress_callback) (duckdb_v2_table_function_handle function, duckdb_v2_table_function_progress_callback_fn callback, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_user_data) (duckdb_v2_table_function_handle function, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_cast_function_create_with_connection) - (duckdb_v2_connection_handle connection, duckdb_v2_cast_function_handle *function, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_cast_function_create_with_extension) - (duckdb_v2_extension_handle extension, duckdb_v2_cast_function_handle *function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_cast_function_create) + (duckdb_v2_factory_handle factory, duckdb_v2_cast_function_handle *function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_cast_function_destroy)(duckdb_v2_cast_function_handle *function); DUCKDB_V2_ERROR(*duckdb_v2_cast_function_exec_get_input) (duckdb_v2_cast_function_exec_info_handle info, duckdb_v2_vector_handle *vector, duckdb_v2_error_info_handle *err); @@ -911,8 +714,6 @@ typedef struct { (duckdb_v2_cast_function_exec_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_cast_function_exec_get_user_data) (duckdb_v2_cast_function_exec_info_handle info, void **data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_cast_function_register) - (duckdb_v2_cast_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_cast_function_set_exec_callback) (duckdb_v2_cast_function_handle function, duckdb_v2_cast_function_exec_callback_fn callback, duckdb_v2_error_info_handle *err); @@ -928,17 +729,27 @@ typedef struct { (duckdb_v2_cast_function_handle function, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_column_data_collection_clear) (duckdb_v2_column_data_collection_handle collection, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_custom_type_create_with_connection) - (duckdb_v2_connection_handle connection, duckdb_v2_custom_type_handle *type, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_custom_type_create_with_extension) - (duckdb_v2_extension_handle extension, duckdb_v2_custom_type_handle *type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_cast_function) + (duckdb_v2_connection_handle conn, duckdb_v2_cast_function_handle function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_custom_type) + (duckdb_v2_connection_handle conn, duckdb_v2_custom_type_handle type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_replacement_scan) + (duckdb_v2_connection_handle conn, duckdb_v2_replacement_scan_handle scan, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_context_get_file_system) + (duckdb_v2_context_handle context, duckdb_v2_file_system_handle *file_system, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_type_create) + (duckdb_v2_factory_handle factory, duckdb_v2_custom_type_handle *type, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_custom_type_destroy)(duckdb_v2_custom_type_handle *type); - DUCKDB_V2_ERROR(*duckdb_v2_custom_type_register) - (duckdb_v2_custom_type_handle type, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_custom_type_set_base_type) (duckdb_v2_custom_type_handle type, duckdb_v2_logical_type_handle base_type, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_custom_type_set_name) - (duckdb_v2_custom_type_handle type, duckdb_v2_identifier_t name, duckdb_v2_error_info_handle *err); + (duckdb_v2_custom_type_handle type, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_cast_function) + (duckdb_v2_extension_handle extension, duckdb_v2_cast_function_handle function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_custom_type) + (duckdb_v2_extension_handle extension, duckdb_v2_custom_type_handle type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_replacement_scan) + (duckdb_v2_extension_handle extension, duckdb_v2_replacement_scan_handle scan, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_file_close)(duckdb_v2_file_handle file, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_file_destroy)(duckdb_v2_file_handle *file); DUCKDB_V2_ERROR(*duckdb_v2_file_open_options_create) @@ -948,7 +759,7 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_file_open_options_set_flag) (duckdb_v2_file_open_options_handle options, DUCKDB_V2_FILE_FLAG flag, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_file_open_options_set_value) - (duckdb_v2_file_open_options_handle options, duckdb_v2_str name, duckdb_v2_value_handle value, + (duckdb_v2_file_open_options_handle options, const duckdb_v2_str *name, duckdb_v2_value_handle value, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_file_read) (duckdb_v2_file_handle file, void *buffer, idx_t buffer_size, idx_t *bytes_read, duckdb_v2_error_info_handle *err); @@ -958,14 +769,9 @@ typedef struct { (duckdb_v2_file_handle file, idx_t position, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_file_size)(duckdb_v2_file_handle file, idx_t *size, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_file_sync)(duckdb_v2_file_handle file, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_file_system_get_from_connection) - (duckdb_v2_connection_handle connection, duckdb_v2_file_system_handle *file_system, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_file_system_get_from_context) - (duckdb_v2_context_handle context, duckdb_v2_file_system_handle *file_system, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_file_system_open) - (duckdb_v2_file_system_handle file_system, duckdb_v2_str file_path, duckdb_v2_file_open_options_handle options, - duckdb_v2_file_handle *file, duckdb_v2_error_info_handle *err); + (duckdb_v2_file_system_handle file_system, const duckdb_v2_str *file_path, + duckdb_v2_file_open_options_handle options, duckdb_v2_file_handle *file, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_file_tell) (duckdb_v2_file_handle file, idx_t *position, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_file_write) @@ -974,6 +780,8 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_file_write_at) (duckdb_v2_file_handle file, const void *buffer, idx_t buffer_size, idx_t location, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_register_replacement_scan) + (duckdb_v2_instance_handle instance, duckdb_v2_replacement_scan_handle scan, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_qname_create) (const duckdb_v2_identifier_t *parts, idx_t part_count, duckdb_v2_qname_handle *name, duckdb_v2_error_info_handle *err); @@ -987,30 +795,25 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_qname_hash) (duckdb_v2_qname_handle name, uint64_t *hash, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_qname_parse) - (duckdb_v2_str text, duckdb_v2_qname_handle *name, duckdb_v2_error_info_handle *err); + (const duckdb_v2_str *text, duckdb_v2_qname_handle *name, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_qname_render) (duckdb_v2_qname_handle name, char *out_text, idx_t out_capacity, idx_t *out_length, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_add_argument) (duckdb_v2_replacement_scan_info_handle info, duckdb_v2_value_handle value, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_add_named_argument) - (duckdb_v2_replacement_scan_info_handle info, duckdb_v2_identifier_t name, duckdb_v2_value_handle value, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_create_with_connection) - (duckdb_v2_connection_handle connection, duckdb_v2_replacement_scan_handle *scan, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_create_with_database) - (duckdb_v2_database_handle database, duckdb_v2_replacement_scan_handle *scan, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_create_with_extension) - (duckdb_v2_extension_handle extension, duckdb_v2_replacement_scan_handle *scan, duckdb_v2_error_info_handle *err); + (duckdb_v2_replacement_scan_info_handle info, const duckdb_v2_identifier_t *name, duckdb_v2_value_handle value, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_create) + (duckdb_v2_factory_handle factory, duckdb_v2_replacement_scan_handle *scan, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_replacement_scan_destroy)(duckdb_v2_replacement_scan_handle *scan); DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_get_name) (duckdb_v2_replacement_scan_info_handle info, duckdb_v2_qname_handle *name, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_get_user_data) (duckdb_v2_replacement_scan_info_handle info, void **data, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_register) - (duckdb_v2_replacement_scan_handle scan, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_set_alias) - (duckdb_v2_replacement_scan_info_handle info, duckdb_v2_identifier_t alias, duckdb_v2_error_info_handle *err); + (duckdb_v2_replacement_scan_info_handle info, const duckdb_v2_identifier_t *alias, + duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_set_callback) (duckdb_v2_replacement_scan_handle scan, duckdb_v2_replacement_scan_callback_fn callback, duckdb_v2_error_info_handle *err); @@ -1020,7 +823,7 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_set_function_name) (duckdb_v2_replacement_scan_info_handle info, duckdb_v2_qname_handle name, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_set_subquery) - (duckdb_v2_replacement_scan_info_handle info, duckdb_v2_str sql, duckdb_v2_error_info_handle *err); + (duckdb_v2_replacement_scan_info_handle info, const duckdb_v2_str *sql, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_replacement_scan_set_user_data) (duckdb_v2_replacement_scan_handle scan, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_arrow_exporter_append) @@ -1058,6 +861,8 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_connection_describe_table) (duckdb_v2_connection_handle conn, duckdb_v2_qname_handle name, duckdb_v2_table_description_handle *desc, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_copy_function) + (duckdb_v2_connection_handle conn, duckdb_v2_copy_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_copy_from_bind_get_column_count) (duckdb_v2_copy_from_bind_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_copy_from_bind_get_column_name) @@ -1131,16 +936,11 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_copy_from_set_progress_callback) (duckdb_v2_copy_function_handle function, duckdb_v2_copy_from_progress_callback_fn callback, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_copy_function_create_with_connection) - (duckdb_v2_connection_handle connection, duckdb_v2_copy_function_handle *function, - duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_copy_function_create_with_extension) - (duckdb_v2_extension_handle extension, duckdb_v2_copy_function_handle *function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_copy_function_create) + (duckdb_v2_factory_handle factory, duckdb_v2_copy_function_handle *function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_copy_function_destroy)(duckdb_v2_copy_function_handle *function); - DUCKDB_V2_ERROR(*duckdb_v2_copy_function_register) - (duckdb_v2_copy_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_copy_function_set_name) - (duckdb_v2_copy_function_handle function, duckdb_v2_str *name, duckdb_v2_error_info_handle *err); + (duckdb_v2_copy_function_handle function, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_copy_function_set_user_data) (duckdb_v2_copy_function_handle function, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_copy_to_batch_get_bind_data) @@ -1241,8 +1041,10 @@ typedef struct { (duckdb_v2_expression_handle expression, duckdb_v2_logical_type_handle *type, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_expression_get_type) (duckdb_v2_expression_handle expression, DUCKDB_V2_EXPRESSION_TYPE *type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_copy_function) + (duckdb_v2_extension_handle extension, duckdb_v2_copy_function_handle function, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_identifier_render_quoted) - (duckdb_v2_identifier_t name, char *out_text, idx_t out_capacity, idx_t *out_length, + (const duckdb_v2_identifier_t *name, char *out_text, idx_t out_capacity, idx_t *out_length, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_prepared_statement_create) (duckdb_v2_connection_handle conn, duckdb_v2_sql_statement_handle statement, bool require_cacheable, @@ -1254,9 +1056,6 @@ typedef struct { duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_prepared_statement_reuses_plan) (duckdb_v2_prepared_statement_handle prepared, bool *out_reuses, duckdb_v2_error_info_handle *err); - DUCKDB_V2_ERROR(*duckdb_v2_result_to_arrow_stream) - (duckdb_v2_result_handle *result, idx_t batch_size, struct ArrowArrayStream *out_stream, - duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR (*duckdb_v2_table_description_destroy)(duckdb_v2_table_description_handle *desc); DUCKDB_V2_ERROR(*duckdb_v2_table_description_get_column) (duckdb_v2_table_description_handle desc, idx_t index, duckdb_v2_column_description_handle *column, @@ -1312,6 +1111,361 @@ typedef struct { (duckdb_v2_sql_statement_handle statement, duckdb_v2_str *out_text, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_sql_statement_get_type) (duckdb_v2_sql_statement_handle statement, DUCKDB_V2_STATEMENT_TYPE *out_type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_token_iterator_destroy)(duckdb_v2_token_iterator_handle *iterator); + DUCKDB_V2_ERROR(*duckdb_v2_token_iterator_ends_unterminated) + (duckdb_v2_token_iterator_handle iterator, bool *out_ends_unterminated, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_token_iterator_next) + (duckdb_v2_token_iterator_handle iterator, DUCKDB_V2_TOKEN_TYPE *out_type, idx_t *out_start, idx_t *out_length, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_tokenize_sql) + (duckdb_v2_connection_handle conn, const duckdb_v2_str *sql, duckdb_v2_token_iterator_handle *out_iterator, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_attach_options_create) + (duckdb_v2_instance_handle instance, duckdb_v2_attach_options_handle *out_options, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_attach_options_destroy)(duckdb_v2_attach_options_handle *options); + DUCKDB_V2_ERROR(*duckdb_v2_attach_options_set) + (duckdb_v2_attach_options_handle options, const duckdb_v2_identifier_t *key, const duckdb_v2_str *setting, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_set_default) + (duckdb_v2_instance_handle instance, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_get_bind_data) + (duckdb_v2_table_function_partition_data_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_get_global_state) + (duckdb_v2_table_function_partition_data_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_get_local_state) + (duckdb_v2_table_function_partition_data_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_get_partition_column_count) + (duckdb_v2_table_function_partition_data_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_get_partition_column_index) + (duckdb_v2_table_function_partition_data_info_handle info, idx_t index, idx_t *column_index, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_get_user_data) + (duckdb_v2_table_function_partition_data_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_requires_batch_index) + (duckdb_v2_table_function_partition_data_info_handle info, bool *required, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_requires_partition_columns) + (duckdb_v2_table_function_partition_data_info_handle info, bool *required, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_set_batch_index) + (duckdb_v2_table_function_partition_data_info_handle info, idx_t batch_index, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partition_data_set_partition_value) + (duckdb_v2_table_function_partition_data_info_handle info, idx_t index, duckdb_v2_value_handle value, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partitioning_get_bind_data) + (duckdb_v2_table_function_partitioning_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partitioning_get_partition_column_count) + (duckdb_v2_table_function_partitioning_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partitioning_get_partition_column_index) + (duckdb_v2_table_function_partitioning_info_handle info, idx_t index, idx_t *column_index, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partitioning_get_user_data) + (duckdb_v2_table_function_partitioning_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_partitioning_set_partition_info) + (duckdb_v2_table_function_partitioning_info_handle info, DUCKDB_V2_TABLE_PARTITION_INFO partition_info, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_partition_data_callback) + (duckdb_v2_table_function_handle function, duckdb_v2_table_function_partition_data_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_partitioning_callback) + (duckdb_v2_table_function_handle function, duckdb_v2_table_function_partitioning_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_validate_utf8)(const duckdb_v2_str *text, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_bind_get_arg_count) + (duckdb_v2_function_bind_info_handle info, idx_t *positional_fixed, idx_t *positional_variadic, idx_t *named_fixed, + idx_t *named_variadic, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_bind_get_arg_index) + (duckdb_v2_function_bind_info_handle info, const duckdb_v2_identifier_t *name, idx_t *index, bool *found, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_bind_get_arg_name) + (duckdb_v2_function_bind_info_handle info, idx_t index, duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_bind_get_arg_type) + (duckdb_v2_function_bind_info_handle info, idx_t index, duckdb_v2_logical_type_handle *type, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_bind_get_arg_value) + (duckdb_v2_function_bind_info_handle info, idx_t index, duckdb_v2_value_handle *value, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_bind_get_user_data) + (duckdb_v2_function_bind_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_bind_set_bind_data) + (duckdb_v2_function_bind_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_bind_set_order_preservation) + (duckdb_v2_table_function_bind_info_handle info, DUCKDB_V2_ORDER_PRESERVATION order, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_arrow_result_destroy)(duckdb_v2_arrow_result_handle *result); + DUCKDB_V2_ERROR(*duckdb_v2_arrow_result_drain) + (duckdb_v2_arrow_result_handle result, idx_t *out_rows_changed, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_arrow_result_fetch_array) + (duckdb_v2_arrow_result_handle result, struct ArrowArray *out_array, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_arrow_result_get_result_type) + (duckdb_v2_arrow_result_handle result, DUCKDB_V2_RESULT_TYPE *out_type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_arrow_result_get_schema) + (duckdb_v2_arrow_result_handle result, struct ArrowSchema *out_schema, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_arrow_result_get_statement_type) + (duckdb_v2_arrow_result_handle result, DUCKDB_V2_STATEMENT_TYPE *out_type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_arrow_result_step) + (duckdb_v2_arrow_result_handle result, struct ArrowArray *out_array, DUCKDB_V2_RESULT_STEP_STATUS *out_status, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_arrow_result_to_arrow_c_stream) + (duckdb_v2_arrow_result_handle *result, struct ArrowArrayStream *out_stream, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_arrow_result_wait) + (duckdb_v2_arrow_result_handle result, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_prepared_statement_execute_arrow) + (duckdb_v2_prepared_statement_handle prepared, const duckdb_v2_identifier_t *parameter_names, + const duckdb_v2_value_handle *parameter_values, idx_t parameter_count, idx_t batch_size, + duckdb_v2_arrow_result_handle *out_result, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_statement_execute_arrow) + (duckdb_v2_connection_handle conn, duckdb_v2_sql_statement_handle statement, + const duckdb_v2_identifier_t *parameter_names, const duckdb_v2_value_handle *parameter_values, + idx_t parameter_count, idx_t batch_size, duckdb_v2_arrow_result_handle *out_result, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_resolve_types_set_return_type) + (duckdb_v2_scalar_function_resolve_types_info_handle info, duckdb_v2_logical_type_handle return_type, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_set_resolve_types_callback) + (duckdb_v2_scalar_function_handle function, duckdb_v2_scalar_function_resolve_types_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_get_factory) + (duckdb_v2_connection_handle conn, duckdb_v2_factory_handle *out_factory, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_context_get_factory) + (duckdb_v2_context_handle ctx, duckdb_v2_factory_handle *out_factory, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_get_factory) + (duckdb_v2_instance_handle instance, duckdb_v2_factory_handle *out_factory, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_bind_set_bind_data) + (duckdb_v2_scalar_function_bind_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_config_get_option_value) + (duckdb_v2_config_handle config, const duckdb_v2_identifier_t *name, duckdb_v2_value_handle *out_value, + DUCKDB_V2_SETTING_SCOPE *out_scope, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_config_set_option_text) + (duckdb_v2_config_handle config, const duckdb_v2_identifier_t *name, const duckdb_v2_str *setting, + DUCKDB_V2_SETTING_SCOPE scope, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_config_set_option_value) + (duckdb_v2_config_handle config, const duckdb_v2_identifier_t *name, duckdb_v2_value_handle value, + DUCKDB_V2_SETTING_SCOPE scope, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_get_config) + (duckdb_v2_connection_handle conn, duckdb_v2_config_handle *out_config, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_get_file_system) + (duckdb_v2_connection_handle conn, duckdb_v2_file_system_handle *file_system, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_log) + (duckdb_v2_connection_handle conn, DUCKDB_V2_LOG_LEVEL level, const duckdb_v2_str *log_type, + const duckdb_v2_str *message, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_context_get_config) + (duckdb_v2_context_handle ctx, duckdb_v2_config_handle *out_config, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_get_config) + (duckdb_v2_instance_handle instance, duckdb_v2_config_handle *out_config, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_get_file_system) + (duckdb_v2_instance_handle instance, duckdb_v2_file_system_handle *file_system, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_log) + (duckdb_v2_instance_handle instance, DUCKDB_V2_LOG_LEVEL level, const duckdb_v2_str *log_type, + const duckdb_v2_str *message, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_option_get_default_scope) + (duckdb_v2_option_handle option, DUCKDB_V2_SETTING_SCOPE *out_scope, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_option_supports_scope) + (duckdb_v2_option_handle option, DUCKDB_V2_SETTING_SCOPE scope, bool *out_supported, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_get_docs) + (duckdb_v2_aggregate_function_handle function, duckdb_v2_function_docs_handle *docs, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_pragma_function) + (duckdb_v2_connection_handle conn, duckdb_v2_pragma_function_handle function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_pragma_function) + (duckdb_v2_extension_handle extension, duckdb_v2_pragma_function_handle function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_docs_add_category) + (duckdb_v2_function_docs_handle docs, const duckdb_v2_str *category, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_docs_add_example) + (duckdb_v2_function_docs_handle docs, const duckdb_v2_str *title, const duckdb_v2_str *example, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_function_docs_set_description) + (duckdb_v2_function_docs_handle docs, const duckdb_v2_str *description, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_pragma_function_create) + (duckdb_v2_factory_handle factory, duckdb_v2_pragma_function_handle *function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_pragma_function_destroy)(duckdb_v2_pragma_function_handle *function); + DUCKDB_V2_ERROR(*duckdb_v2_pragma_function_get_docs) + (duckdb_v2_pragma_function_handle function, duckdb_v2_function_docs_handle *docs, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_pragma_function_get_signature) + (duckdb_v2_pragma_function_handle function, duckdb_v2_function_signature_handle *sig, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_pragma_function_query_set_query) + (duckdb_v2_pragma_function_query_info_handle info, const duckdb_v2_str *query, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_pragma_function_set_exec_callback) + (duckdb_v2_pragma_function_handle function, duckdb_v2_pragma_function_exec_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_pragma_function_set_name) + (duckdb_v2_pragma_function_handle function, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_pragma_function_set_query_callback) + (duckdb_v2_pragma_function_handle function, duckdb_v2_pragma_function_query_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_pragma_function_set_user_data) + (duckdb_v2_pragma_function_handle function, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_get_docs) + (duckdb_v2_scalar_function_handle function, duckdb_v2_function_docs_handle *docs, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_get_docs) + (duckdb_v2_table_function_handle function, duckdb_v2_function_docs_handle *docs, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_custom_option) + (duckdb_v2_connection_handle conn, duckdb_v2_custom_option_handle option, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_create) + (duckdb_v2_factory_handle factory, duckdb_v2_custom_option_handle *option, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_custom_option_destroy)(duckdb_v2_custom_option_handle *option); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_set_default_scope) + (duckdb_v2_custom_option_handle option, DUCKDB_V2_SETTING_SCOPE scope, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_set_default_value) + (duckdb_v2_custom_option_handle option, duckdb_v2_value_handle value, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_set_description) + (duckdb_v2_custom_option_handle option, const duckdb_v2_str *description, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_set_name) + (duckdb_v2_custom_option_handle option, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_set_supports_scope) + (duckdb_v2_custom_option_handle option, DUCKDB_V2_SETTING_SCOPE scope, bool supported, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_set_type) + (duckdb_v2_custom_option_handle option, duckdb_v2_logical_type_handle type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_set_update_callback) + (duckdb_v2_custom_option_handle option, duckdb_v2_custom_option_update_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_set_user_data) + (duckdb_v2_custom_option_handle option, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_update_get_scope) + (duckdb_v2_custom_option_update_info_handle info, DUCKDB_V2_SETTING_SCOPE *scope, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_update_get_user_data) + (duckdb_v2_custom_option_update_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_update_get_value) + (duckdb_v2_custom_option_update_info_handle info, duckdb_v2_value_handle *value, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_update_is_reset) + (duckdb_v2_custom_option_update_info_handle info, bool *is_reset, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_custom_option_update_set_value) + (duckdb_v2_custom_option_update_info_handle info, duckdb_v2_value_handle value, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_custom_option) + (duckdb_v2_extension_handle extension, duckdb_v2_custom_option_handle option, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_log_sink) + (duckdb_v2_extension_handle extension, duckdb_v2_log_sink_handle sink, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_macro_function) + (duckdb_v2_extension_handle extension, duckdb_v2_macro_function_handle function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_file_system_path_exists) + (duckdb_v2_file_system_handle file_system, const duckdb_v2_str *path, bool *out_exists, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_file_system_path_expand) + (duckdb_v2_file_system_handle file_system, const duckdb_v2_str *path, duckdb_v2_text_sink_fn sink, void *user_data, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_file_system_path_is_remote) + (duckdb_v2_file_system_handle file_system, const duckdb_v2_str *path, bool *out_is_remote, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_instance_register_log_sink) + (duckdb_v2_instance_handle instance, duckdb_v2_log_sink_handle sink, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_log_sink_create) + (duckdb_v2_factory_handle factory, duckdb_v2_log_sink_handle *sink, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_log_sink_destroy)(duckdb_v2_log_sink_handle *sink); + DUCKDB_V2_ERROR(*duckdb_v2_log_sink_set_flush_callback) + (duckdb_v2_log_sink_handle sink, duckdb_v2_log_sink_flush_callback_fn callback, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_log_sink_set_name) + (duckdb_v2_log_sink_handle sink, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_log_sink_set_user_data) + (duckdb_v2_log_sink_handle sink, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_log_sink_set_write_callback) + (duckdb_v2_log_sink_handle sink, duckdb_v2_log_sink_write_callback_fn callback, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_log_sink_write_info_get_connection_id) + (duckdb_v2_log_sink_write_info_handle info, idx_t *id, bool *present, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_log_sink_write_info_get_query_id) + (duckdb_v2_log_sink_write_info_handle info, idx_t *id, bool *present, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_log_sink_write_info_get_thread_id) + (duckdb_v2_log_sink_write_info_handle info, idx_t *id, bool *present, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_log_sink_write_info_get_transaction_id) + (duckdb_v2_log_sink_write_info_handle info, idx_t *id, bool *present, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_macro_function_create) + (duckdb_v2_factory_handle factory, DUCKDB_V2_MACRO_FUNCTION_TYPE type, duckdb_v2_macro_function_handle *function, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_macro_function_destroy)(duckdb_v2_macro_function_handle *function); + DUCKDB_V2_ERROR(*duckdb_v2_macro_function_get_docs) + (duckdb_v2_macro_function_handle function, duckdb_v2_function_docs_handle *docs, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_macro_function_get_signature) + (duckdb_v2_macro_function_handle function, duckdb_v2_function_signature_handle *sig, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_macro_function_set_body) + (duckdb_v2_macro_function_handle function, const duckdb_v2_str *body, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_macro_function_set_name) + (duckdb_v2_macro_function_handle function, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_vector_cast) + (duckdb_v2_factory_handle factory, duckdb_v2_vector_handle source, duckdb_v2_vector_handle target, + DUCKDB_V2_CAST_MODE mode, bool *out_converted, duckdb_v2_error_info_handle *cast_err, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_copy_function_set_file_extension) + (duckdb_v2_copy_function_handle function, const duckdb_v2_str *extension, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_data_chunk_clear)(duckdb_v2_data_chunk_handle chunk, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_file_system_path_glob) + (duckdb_v2_file_system_handle file_system, const duckdb_v2_str *pattern, + duckdb_v2_file_system_path_glob_callback_fn callback, void *user_data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_file_system_path_move) + (duckdb_v2_file_system_handle file_system, const duckdb_v2_str *source, const duckdb_v2_str *target, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_file_system_path_remove) + (duckdb_v2_file_system_handle file_system, const duckdb_v2_str *path, bool *out_removed, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_statement_add_collection) + (duckdb_v2_sql_statement_handle statement, const duckdb_v2_identifier_t *name, + duckdb_v2_column_data_collection_handle collection, const duckdb_v2_identifier_t *column_names, idx_t column_count, + duckdb_v2_error_info_handle *err); +#if DUCKDB_V2_API_ALLOW_UNSTABLE + DUCKDB_V2_ERROR(*duckdb_v2_connection_register_multi_file_function) + (duckdb_v2_connection_handle conn, duckdb_v2_multi_file_function_handle function, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_copy_to_set_statistics_callback) + (duckdb_v2_copy_function_handle function, duckdb_v2_copy_to_statistics_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_copy_to_statistics_get_bind_data) + (duckdb_v2_copy_to_statistics_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_copy_to_statistics_get_init_data) + (duckdb_v2_copy_to_statistics_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_copy_to_statistics_get_user_data) + (duckdb_v2_copy_to_statistics_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_copy_to_statistics_set_file_size) + (duckdb_v2_copy_to_statistics_info_handle info, idx_t file_size_bytes, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_copy_to_statistics_set_row_count) + (duckdb_v2_copy_to_statistics_info_handle info, idx_t row_count, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_data_chunk_get_capacity) + (duckdb_v2_data_chunk_handle chunk, idx_t *out_capacity, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_extension_register_multi_file_function) + (duckdb_v2_extension_handle extension, duckdb_v2_multi_file_function_handle function, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_multi_file_function_create) + (duckdb_v2_factory_handle factory, duckdb_v2_multi_file_function_handle *function, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR (*duckdb_v2_multi_file_function_destroy)(duckdb_v2_multi_file_function_handle *function); + DUCKDB_V2_ERROR(*duckdb_v2_multi_file_function_set_file_extension) + (duckdb_v2_multi_file_function_handle function, duckdb_v2_str *extension, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_multi_file_function_set_name) + (duckdb_v2_multi_file_function_handle function, duckdb_v2_str *name, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_multi_file_function_set_reader_type) + (duckdb_v2_multi_file_function_handle function, duckdb_v2_str *reader_type, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_multi_file_function_set_single_file_function) + (duckdb_v2_multi_file_function_handle function, duckdb_v2_str *name, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_claim_batch_get_bind_data) + (duckdb_v2_table_function_claim_batch_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_claim_batch_get_global_state) + (duckdb_v2_table_function_claim_batch_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_claim_batch_get_local_state) + (duckdb_v2_table_function_claim_batch_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_claim_batch_get_user_data) + (duckdb_v2_table_function_claim_batch_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_claim_batch_set_claimed) + (duckdb_v2_table_function_claim_batch_info_handle info, bool claimed, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_claim_batch_callback) + (duckdb_v2_table_function_handle function, duckdb_v2_table_function_claim_batch_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_get_bind_info_get_bind_data) + (duckdb_v2_table_function_get_bind_info_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_get_bind_info_get_user_data) + (duckdb_v2_table_function_get_bind_info_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_get_bind_info_set_column_identifier) + (duckdb_v2_table_function_get_bind_info_info_handle info, idx_t column_index, const idx_t *child_path, + idx_t child_path_length, duckdb_v2_value_handle identifier, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_get_bind_info_set_option) + (duckdb_v2_table_function_get_bind_info_info_handle info, duckdb_v2_str *key, duckdb_v2_value_handle value, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_get_bind_info_callback) + (duckdb_v2_table_function_handle function, duckdb_v2_table_function_get_bind_info_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_factory_create_common_type) + (duckdb_v2_factory_handle factory, const duckdb_v2_logical_type_handle *types, idx_t type_count, + duckdb_v2_logical_type_handle *out_type, duckdb_v2_error_info_handle *err); +#endif // capigen:end appended } duckdb_ext_api_v2; @@ -1325,17 +1479,13 @@ typedef struct { #define duckdb_v2_arena_allocate duckdb_ext_api.duckdb_v2_arena_allocate #define duckdb_v2_bignum_decode duckdb_ext_api.duckdb_v2_bignum_decode #define duckdb_v2_bignum_encode duckdb_ext_api.duckdb_v2_bignum_encode -#define duckdb_v2_close duckdb_ext_api.duckdb_v2_close #define duckdb_v2_column_data_collection_append duckdb_ext_api.duckdb_v2_column_data_collection_append #define duckdb_v2_column_data_collection_append_state_create \ duckdb_ext_api.duckdb_v2_column_data_collection_append_state_create #define duckdb_v2_column_data_collection_append_state_destroy \ duckdb_ext_api.duckdb_v2_column_data_collection_append_state_destroy -#define duckdb_v2_column_data_collection_combine duckdb_ext_api.duckdb_v2_column_data_collection_combine -#define duckdb_v2_column_data_collection_create_with_connection \ - duckdb_ext_api.duckdb_v2_column_data_collection_create_with_connection -#define duckdb_v2_column_data_collection_create_with_context \ - duckdb_ext_api.duckdb_v2_column_data_collection_create_with_context +#define duckdb_v2_column_data_collection_combine duckdb_ext_api.duckdb_v2_column_data_collection_combine +#define duckdb_v2_column_data_collection_create duckdb_ext_api.duckdb_v2_column_data_collection_create #define duckdb_v2_column_data_collection_destroy duckdb_ext_api.duckdb_v2_column_data_collection_destroy #define duckdb_v2_column_data_collection_reset duckdb_ext_api.duckdb_v2_column_data_collection_reset #define duckdb_v2_column_data_collection_row_count duckdb_ext_api.duckdb_v2_column_data_collection_row_count @@ -1348,253 +1498,184 @@ typedef struct { duckdb_ext_api.duckdb_v2_column_data_collection_worker_scan_state_create #define duckdb_v2_column_data_collection_worker_scan_state_destroy \ duckdb_ext_api.duckdb_v2_column_data_collection_worker_scan_state_destroy -#define duckdb_v2_connect duckdb_ext_api.duckdb_v2_connect -#define duckdb_v2_connection_create_type_from_id duckdb_ext_api.duckdb_v2_connection_create_type_from_id -#define duckdb_v2_connection_create_type_from_name duckdb_ext_api.duckdb_v2_connection_create_type_from_name -#define duckdb_v2_connection_create_type_from_text duckdb_ext_api.duckdb_v2_connection_create_type_from_text -#define duckdb_v2_connection_create_type_with_alias duckdb_ext_api.duckdb_v2_connection_create_type_with_alias -#define duckdb_v2_connection_interrupt duckdb_ext_api.duckdb_v2_connection_interrupt -#define duckdb_v2_connection_option_get duckdb_ext_api.duckdb_v2_connection_option_get -#define duckdb_v2_connection_option_get_by_index duckdb_ext_api.duckdb_v2_connection_option_get_by_index -#define duckdb_v2_connection_option_get_count duckdb_ext_api.duckdb_v2_connection_option_get_count -#define duckdb_v2_connection_option_set duckdb_ext_api.duckdb_v2_connection_option_set -#define duckdb_v2_connection_progress_get duckdb_ext_api.duckdb_v2_connection_progress_get -#define duckdb_v2_context_create_type_from_id duckdb_ext_api.duckdb_v2_context_create_type_from_id -#define duckdb_v2_context_create_type_from_name duckdb_ext_api.duckdb_v2_context_create_type_from_name -#define duckdb_v2_context_create_type_from_text duckdb_ext_api.duckdb_v2_context_create_type_from_text -#define duckdb_v2_context_create_type_with_alias duckdb_ext_api.duckdb_v2_context_create_type_with_alias -#define duckdb_v2_create_environment duckdb_ext_api.duckdb_v2_create_environment -#define duckdb_v2_data_chunk_copy_with_connection duckdb_ext_api.duckdb_v2_data_chunk_copy_with_connection -#define duckdb_v2_data_chunk_copy_with_context duckdb_ext_api.duckdb_v2_data_chunk_copy_with_context -#define duckdb_v2_data_chunk_create duckdb_ext_api.duckdb_v2_data_chunk_create -#define duckdb_v2_data_chunk_create_with_connection duckdb_ext_api.duckdb_v2_data_chunk_create_with_connection -#define duckdb_v2_data_chunk_create_with_context duckdb_ext_api.duckdb_v2_data_chunk_create_with_context -#define duckdb_v2_data_chunk_destroy duckdb_ext_api.duckdb_v2_data_chunk_destroy -#define duckdb_v2_data_chunk_get_size duckdb_ext_api.duckdb_v2_data_chunk_get_size -#define duckdb_v2_data_chunk_get_vector duckdb_ext_api.duckdb_v2_data_chunk_get_vector -#define duckdb_v2_data_chunk_get_vector_count duckdb_ext_api.duckdb_v2_data_chunk_get_vector_count -#define duckdb_v2_database_option_get duckdb_ext_api.duckdb_v2_database_option_get -#define duckdb_v2_database_option_get_by_index duckdb_ext_api.duckdb_v2_database_option_get_by_index -#define duckdb_v2_database_option_get_count duckdb_ext_api.duckdb_v2_database_option_get_count -#define duckdb_v2_database_option_set duckdb_ext_api.duckdb_v2_database_option_set -#define duckdb_v2_destroy_environment duckdb_ext_api.duckdb_v2_destroy_environment -#define duckdb_v2_disconnect duckdb_ext_api.duckdb_v2_disconnect -#define duckdb_v2_environment_database_count duckdb_ext_api.duckdb_v2_environment_database_count -#define duckdb_v2_error_info_destroy duckdb_ext_api.duckdb_v2_error_info_destroy -#define duckdb_v2_error_info_get_code duckdb_ext_api.duckdb_v2_error_info_get_code -#define duckdb_v2_error_info_get_raw_message duckdb_ext_api.duckdb_v2_error_info_get_raw_message -#define duckdb_v2_error_info_get_text duckdb_ext_api.duckdb_v2_error_info_get_text -#define duckdb_v2_error_info_set_code duckdb_ext_api.duckdb_v2_error_info_set_code -#define duckdb_v2_error_info_set_text duckdb_ext_api.duckdb_v2_error_info_set_text -#define duckdb_v2_library_version duckdb_ext_api.duckdb_v2_library_version -#define duckdb_v2_logical_type_copy duckdb_ext_api.duckdb_v2_logical_type_copy -#define duckdb_v2_logical_type_destroy duckdb_ext_api.duckdb_v2_logical_type_destroy -#define duckdb_v2_logical_type_get_id duckdb_ext_api.duckdb_v2_logical_type_get_id -#define duckdb_v2_logical_type_get_name duckdb_ext_api.duckdb_v2_logical_type_get_name -#define duckdb_v2_logical_type_get_param duckdb_ext_api.duckdb_v2_logical_type_get_param -#define duckdb_v2_logical_type_get_param_count duckdb_ext_api.duckdb_v2_logical_type_get_param_count -#define duckdb_v2_logical_type_is_equal duckdb_ext_api.duckdb_v2_logical_type_is_equal -#define duckdb_v2_logical_type_to_text duckdb_ext_api.duckdb_v2_logical_type_to_text -#define duckdb_v2_open duckdb_ext_api.duckdb_v2_open -#define duckdb_v2_option_create duckdb_ext_api.duckdb_v2_option_create -#define duckdb_v2_option_destroy duckdb_ext_api.duckdb_v2_option_destroy -#define duckdb_v2_option_get_alias duckdb_ext_api.duckdb_v2_option_get_alias -#define duckdb_v2_option_get_alias_count duckdb_ext_api.duckdb_v2_option_get_alias_count -#define duckdb_v2_option_get_default_setting duckdb_ext_api.duckdb_v2_option_get_default_setting -#define duckdb_v2_option_get_description duckdb_ext_api.duckdb_v2_option_get_description -#define duckdb_v2_option_get_name duckdb_ext_api.duckdb_v2_option_get_name -#define duckdb_v2_option_get_setting duckdb_ext_api.duckdb_v2_option_get_setting -#define duckdb_v2_option_get_target_scope duckdb_ext_api.duckdb_v2_option_get_target_scope -#define duckdb_v2_parse_sql duckdb_ext_api.duckdb_v2_parse_sql -#define duckdb_v2_result_destroy duckdb_ext_api.duckdb_v2_result_destroy -#define duckdb_v2_result_drain duckdb_ext_api.duckdb_v2_result_drain -#define duckdb_v2_result_fetch_chunk duckdb_ext_api.duckdb_v2_result_fetch_chunk -#define duckdb_v2_result_get_result_type duckdb_ext_api.duckdb_v2_result_get_result_type -#define duckdb_v2_result_get_schema duckdb_ext_api.duckdb_v2_result_get_schema -#define duckdb_v2_result_get_statement_type duckdb_ext_api.duckdb_v2_result_get_statement_type -#define duckdb_v2_result_render_box duckdb_ext_api.duckdb_v2_result_render_box -#define duckdb_v2_result_step duckdb_ext_api.duckdb_v2_result_step -#define duckdb_v2_result_wait duckdb_ext_api.duckdb_v2_result_wait -#define duckdb_v2_schema_destroy duckdb_ext_api.duckdb_v2_schema_destroy -#define duckdb_v2_schema_get_count duckdb_ext_api.duckdb_v2_schema_get_count -#define duckdb_v2_schema_get_field duckdb_ext_api.duckdb_v2_schema_get_field -#define duckdb_v2_sql_statement_destroy duckdb_ext_api.duckdb_v2_sql_statement_destroy -#define duckdb_v2_statement_bind duckdb_ext_api.duckdb_v2_statement_bind -#define duckdb_v2_statement_execute duckdb_ext_api.duckdb_v2_statement_execute -#define duckdb_v2_statement_iterator_destroy duckdb_ext_api.duckdb_v2_statement_iterator_destroy -#define duckdb_v2_statement_iterator_next duckdb_ext_api.duckdb_v2_statement_iterator_next -#define duckdb_v2_value_cast_with_connection duckdb_ext_api.duckdb_v2_value_cast_with_connection -#define duckdb_v2_value_cast_with_context duckdb_ext_api.duckdb_v2_value_cast_with_context -#define duckdb_v2_value_create_array_with_connection duckdb_ext_api.duckdb_v2_value_create_array_with_connection -#define duckdb_v2_value_create_array_with_context duckdb_ext_api.duckdb_v2_value_create_array_with_context -#define duckdb_v2_value_create_bigint_with_connection duckdb_ext_api.duckdb_v2_value_create_bigint_with_connection -#define duckdb_v2_value_create_bigint_with_context duckdb_ext_api.duckdb_v2_value_create_bigint_with_context -#define duckdb_v2_value_create_bignum_with_connection duckdb_ext_api.duckdb_v2_value_create_bignum_with_connection -#define duckdb_v2_value_create_bignum_with_context duckdb_ext_api.duckdb_v2_value_create_bignum_with_context -#define duckdb_v2_value_create_bit_with_connection duckdb_ext_api.duckdb_v2_value_create_bit_with_connection -#define duckdb_v2_value_create_bit_with_context duckdb_ext_api.duckdb_v2_value_create_bit_with_context -#define duckdb_v2_value_create_blob_with_connection duckdb_ext_api.duckdb_v2_value_create_blob_with_connection -#define duckdb_v2_value_create_blob_with_context duckdb_ext_api.duckdb_v2_value_create_blob_with_context -#define duckdb_v2_value_create_bool_with_connection duckdb_ext_api.duckdb_v2_value_create_bool_with_connection -#define duckdb_v2_value_create_bool_with_context duckdb_ext_api.duckdb_v2_value_create_bool_with_context -#define duckdb_v2_value_create_date_with_connection duckdb_ext_api.duckdb_v2_value_create_date_with_connection -#define duckdb_v2_value_create_date_with_context duckdb_ext_api.duckdb_v2_value_create_date_with_context -#define duckdb_v2_value_create_decimal_with_connection duckdb_ext_api.duckdb_v2_value_create_decimal_with_connection -#define duckdb_v2_value_create_decimal_with_context duckdb_ext_api.duckdb_v2_value_create_decimal_with_context -#define duckdb_v2_value_create_double_with_connection duckdb_ext_api.duckdb_v2_value_create_double_with_connection -#define duckdb_v2_value_create_double_with_context duckdb_ext_api.duckdb_v2_value_create_double_with_context -#define duckdb_v2_value_create_float_with_connection duckdb_ext_api.duckdb_v2_value_create_float_with_connection -#define duckdb_v2_value_create_float_with_context duckdb_ext_api.duckdb_v2_value_create_float_with_context -#define duckdb_v2_value_create_hugeint_with_connection duckdb_ext_api.duckdb_v2_value_create_hugeint_with_connection -#define duckdb_v2_value_create_hugeint_with_context duckdb_ext_api.duckdb_v2_value_create_hugeint_with_context -#define duckdb_v2_value_create_int_with_connection duckdb_ext_api.duckdb_v2_value_create_int_with_connection -#define duckdb_v2_value_create_int_with_context duckdb_ext_api.duckdb_v2_value_create_int_with_context -#define duckdb_v2_value_create_interval_with_connection duckdb_ext_api.duckdb_v2_value_create_interval_with_connection -#define duckdb_v2_value_create_interval_with_context duckdb_ext_api.duckdb_v2_value_create_interval_with_context -#define duckdb_v2_value_create_list_with_connection duckdb_ext_api.duckdb_v2_value_create_list_with_connection -#define duckdb_v2_value_create_list_with_context duckdb_ext_api.duckdb_v2_value_create_list_with_context -#define duckdb_v2_value_create_map_with_connection duckdb_ext_api.duckdb_v2_value_create_map_with_connection -#define duckdb_v2_value_create_map_with_context duckdb_ext_api.duckdb_v2_value_create_map_with_context -#define duckdb_v2_value_create_null duckdb_ext_api.duckdb_v2_value_create_null -#define duckdb_v2_value_create_null_with_connection duckdb_ext_api.duckdb_v2_value_create_null_with_connection -#define duckdb_v2_value_create_null_with_context duckdb_ext_api.duckdb_v2_value_create_null_with_context -#define duckdb_v2_value_create_smallint_with_connection duckdb_ext_api.duckdb_v2_value_create_smallint_with_connection -#define duckdb_v2_value_create_smallint_with_context duckdb_ext_api.duckdb_v2_value_create_smallint_with_context -#define duckdb_v2_value_create_struct_with_connection duckdb_ext_api.duckdb_v2_value_create_struct_with_connection -#define duckdb_v2_value_create_struct_with_context duckdb_ext_api.duckdb_v2_value_create_struct_with_context -#define duckdb_v2_value_create_time_ns_with_connection duckdb_ext_api.duckdb_v2_value_create_time_ns_with_connection -#define duckdb_v2_value_create_time_ns_with_context duckdb_ext_api.duckdb_v2_value_create_time_ns_with_context -#define duckdb_v2_value_create_time_tz_with_connection duckdb_ext_api.duckdb_v2_value_create_time_tz_with_connection -#define duckdb_v2_value_create_time_tz_with_context duckdb_ext_api.duckdb_v2_value_create_time_tz_with_context -#define duckdb_v2_value_create_time_with_connection duckdb_ext_api.duckdb_v2_value_create_time_with_connection -#define duckdb_v2_value_create_time_with_context duckdb_ext_api.duckdb_v2_value_create_time_with_context -#define duckdb_v2_value_create_timestamp_ms_with_connection \ - duckdb_ext_api.duckdb_v2_value_create_timestamp_ms_with_connection -#define duckdb_v2_value_create_timestamp_ms_with_context duckdb_ext_api.duckdb_v2_value_create_timestamp_ms_with_context -#define duckdb_v2_value_create_timestamp_ns_with_connection \ - duckdb_ext_api.duckdb_v2_value_create_timestamp_ns_with_connection -#define duckdb_v2_value_create_timestamp_ns_with_context duckdb_ext_api.duckdb_v2_value_create_timestamp_ns_with_context -#define duckdb_v2_value_create_timestamp_sec_with_connection \ - duckdb_ext_api.duckdb_v2_value_create_timestamp_sec_with_connection -#define duckdb_v2_value_create_timestamp_sec_with_context \ - duckdb_ext_api.duckdb_v2_value_create_timestamp_sec_with_context -#define duckdb_v2_value_create_timestamp_tz_ns_with_connection \ - duckdb_ext_api.duckdb_v2_value_create_timestamp_tz_ns_with_connection -#define duckdb_v2_value_create_timestamp_tz_ns_with_context \ - duckdb_ext_api.duckdb_v2_value_create_timestamp_tz_ns_with_context -#define duckdb_v2_value_create_timestamp_tz_with_connection \ - duckdb_ext_api.duckdb_v2_value_create_timestamp_tz_with_connection -#define duckdb_v2_value_create_timestamp_tz_with_context duckdb_ext_api.duckdb_v2_value_create_timestamp_tz_with_context -#define duckdb_v2_value_create_timestamp_with_connection duckdb_ext_api.duckdb_v2_value_create_timestamp_with_connection -#define duckdb_v2_value_create_timestamp_with_context duckdb_ext_api.duckdb_v2_value_create_timestamp_with_context -#define duckdb_v2_value_create_tinyint_with_connection duckdb_ext_api.duckdb_v2_value_create_tinyint_with_connection -#define duckdb_v2_value_create_tinyint_with_context duckdb_ext_api.duckdb_v2_value_create_tinyint_with_context -#define duckdb_v2_value_create_tuple_with_connection duckdb_ext_api.duckdb_v2_value_create_tuple_with_connection -#define duckdb_v2_value_create_tuple_with_context duckdb_ext_api.duckdb_v2_value_create_tuple_with_context -#define duckdb_v2_value_create_type_with_connection duckdb_ext_api.duckdb_v2_value_create_type_with_connection -#define duckdb_v2_value_create_type_with_context duckdb_ext_api.duckdb_v2_value_create_type_with_context -#define duckdb_v2_value_create_ubigint_with_connection duckdb_ext_api.duckdb_v2_value_create_ubigint_with_connection -#define duckdb_v2_value_create_ubigint_with_context duckdb_ext_api.duckdb_v2_value_create_ubigint_with_context -#define duckdb_v2_value_create_uhugeint_with_connection duckdb_ext_api.duckdb_v2_value_create_uhugeint_with_connection -#define duckdb_v2_value_create_uhugeint_with_context duckdb_ext_api.duckdb_v2_value_create_uhugeint_with_context -#define duckdb_v2_value_create_uint_with_connection duckdb_ext_api.duckdb_v2_value_create_uint_with_connection -#define duckdb_v2_value_create_uint_with_context duckdb_ext_api.duckdb_v2_value_create_uint_with_context -#define duckdb_v2_value_create_usmallint_with_connection duckdb_ext_api.duckdb_v2_value_create_usmallint_with_connection -#define duckdb_v2_value_create_usmallint_with_context duckdb_ext_api.duckdb_v2_value_create_usmallint_with_context -#define duckdb_v2_value_create_utinyint_with_connection duckdb_ext_api.duckdb_v2_value_create_utinyint_with_connection -#define duckdb_v2_value_create_utinyint_with_context duckdb_ext_api.duckdb_v2_value_create_utinyint_with_context -#define duckdb_v2_value_create_uuid_with_connection duckdb_ext_api.duckdb_v2_value_create_uuid_with_connection -#define duckdb_v2_value_create_uuid_with_context duckdb_ext_api.duckdb_v2_value_create_uuid_with_context -#define duckdb_v2_value_create_varchar_with_connection duckdb_ext_api.duckdb_v2_value_create_varchar_with_connection -#define duckdb_v2_value_create_varchar_with_context duckdb_ext_api.duckdb_v2_value_create_varchar_with_context -#define duckdb_v2_value_destroy duckdb_ext_api.duckdb_v2_value_destroy -#define duckdb_v2_value_get_bigint duckdb_ext_api.duckdb_v2_value_get_bigint -#define duckdb_v2_value_get_blob duckdb_ext_api.duckdb_v2_value_get_blob -#define duckdb_v2_value_get_bool duckdb_ext_api.duckdb_v2_value_get_bool -#define duckdb_v2_value_get_child duckdb_ext_api.duckdb_v2_value_get_child -#define duckdb_v2_value_get_child_count duckdb_ext_api.duckdb_v2_value_get_child_count -#define duckdb_v2_value_get_date duckdb_ext_api.duckdb_v2_value_get_date -#define duckdb_v2_value_get_decimal duckdb_ext_api.duckdb_v2_value_get_decimal -#define duckdb_v2_value_get_double duckdb_ext_api.duckdb_v2_value_get_double -#define duckdb_v2_value_get_float duckdb_ext_api.duckdb_v2_value_get_float -#define duckdb_v2_value_get_hugeint duckdb_ext_api.duckdb_v2_value_get_hugeint -#define duckdb_v2_value_get_int duckdb_ext_api.duckdb_v2_value_get_int -#define duckdb_v2_value_get_interval duckdb_ext_api.duckdb_v2_value_get_interval -#define duckdb_v2_value_get_logical_type duckdb_ext_api.duckdb_v2_value_get_logical_type -#define duckdb_v2_value_get_smallint duckdb_ext_api.duckdb_v2_value_get_smallint -#define duckdb_v2_value_get_time duckdb_ext_api.duckdb_v2_value_get_time -#define duckdb_v2_value_get_time_ns duckdb_ext_api.duckdb_v2_value_get_time_ns -#define duckdb_v2_value_get_time_tz duckdb_ext_api.duckdb_v2_value_get_time_tz -#define duckdb_v2_value_get_timestamp duckdb_ext_api.duckdb_v2_value_get_timestamp -#define duckdb_v2_value_get_timestamp_ms duckdb_ext_api.duckdb_v2_value_get_timestamp_ms -#define duckdb_v2_value_get_timestamp_ns duckdb_ext_api.duckdb_v2_value_get_timestamp_ns -#define duckdb_v2_value_get_timestamp_sec duckdb_ext_api.duckdb_v2_value_get_timestamp_sec -#define duckdb_v2_value_get_timestamp_tz duckdb_ext_api.duckdb_v2_value_get_timestamp_tz -#define duckdb_v2_value_get_timestamp_tz_ns duckdb_ext_api.duckdb_v2_value_get_timestamp_tz_ns -#define duckdb_v2_value_get_tinyint duckdb_ext_api.duckdb_v2_value_get_tinyint -#define duckdb_v2_value_get_type duckdb_ext_api.duckdb_v2_value_get_type -#define duckdb_v2_value_get_ubigint duckdb_ext_api.duckdb_v2_value_get_ubigint -#define duckdb_v2_value_get_uhugeint duckdb_ext_api.duckdb_v2_value_get_uhugeint -#define duckdb_v2_value_get_uint duckdb_ext_api.duckdb_v2_value_get_uint -#define duckdb_v2_value_get_usmallint duckdb_ext_api.duckdb_v2_value_get_usmallint -#define duckdb_v2_value_get_utinyint duckdb_ext_api.duckdb_v2_value_get_utinyint -#define duckdb_v2_value_get_uuid duckdb_ext_api.duckdb_v2_value_get_uuid -#define duckdb_v2_value_get_varchar duckdb_ext_api.duckdb_v2_value_get_varchar -#define duckdb_v2_value_is_null duckdb_ext_api.duckdb_v2_value_is_null -#define duckdb_v2_value_to_string duckdb_ext_api.duckdb_v2_value_to_string -#define duckdb_v2_vector_constant_set_valid duckdb_ext_api.duckdb_v2_vector_constant_set_valid -#define duckdb_v2_vector_flat_get_validity_mutable duckdb_ext_api.duckdb_v2_vector_flat_get_validity_mutable -#define duckdb_v2_vector_flatten duckdb_ext_api.duckdb_v2_vector_flatten -#define duckdb_v2_vector_get_arena duckdb_ext_api.duckdb_v2_vector_get_arena -#define duckdb_v2_vector_get_child duckdb_ext_api.duckdb_v2_vector_get_child -#define duckdb_v2_vector_get_child_count duckdb_ext_api.duckdb_v2_vector_get_child_count -#define duckdb_v2_vector_get_data_mutable duckdb_ext_api.duckdb_v2_vector_get_data_mutable -#define duckdb_v2_vector_get_logical_type duckdb_ext_api.duckdb_v2_vector_get_logical_type -#define duckdb_v2_vector_get_size duckdb_ext_api.duckdb_v2_vector_get_size -#define duckdb_v2_vector_get_value duckdb_ext_api.duckdb_v2_vector_get_value -#define duckdb_v2_vector_get_vector_type duckdb_ext_api.duckdb_v2_vector_get_vector_type -#define duckdb_v2_vector_get_view duckdb_ext_api.duckdb_v2_vector_get_view -#define duckdb_v2_vector_make_constant duckdb_ext_api.duckdb_v2_vector_make_constant -#define duckdb_v2_vector_make_sequence duckdb_ext_api.duckdb_v2_vector_make_sequence -#define duckdb_v2_vector_reference duckdb_ext_api.duckdb_v2_vector_reference -#define duckdb_v2_vector_set_null duckdb_ext_api.duckdb_v2_vector_set_null -#define duckdb_v2_vector_set_size duckdb_ext_api.duckdb_v2_vector_set_size -#define duckdb_v2_vector_set_value duckdb_ext_api.duckdb_v2_vector_set_value -#define duckdb_v2_aggregate_function_bind_get_arg_count duckdb_ext_api.duckdb_v2_aggregate_function_bind_get_arg_count -#define duckdb_v2_aggregate_function_bind_get_arg_type duckdb_ext_api.duckdb_v2_aggregate_function_bind_get_arg_type -#define duckdb_v2_aggregate_function_bind_get_arg_value duckdb_ext_api.duckdb_v2_aggregate_function_bind_get_arg_value -#define duckdb_v2_context_log duckdb_ext_api.duckdb_v2_context_log -#define duckdb_v2_function_signature_add_parameter duckdb_ext_api.duckdb_v2_function_signature_add_parameter -#define duckdb_v2_function_signature_set_return_type duckdb_ext_api.duckdb_v2_function_signature_set_return_type -#define duckdb_v2_function_signature_set_varargs duckdb_ext_api.duckdb_v2_function_signature_set_varargs -#define duckdb_v2_scalar_function_bind_get_arg_count duckdb_ext_api.duckdb_v2_scalar_function_bind_get_arg_count -#define duckdb_v2_scalar_function_bind_get_arg_type duckdb_ext_api.duckdb_v2_scalar_function_bind_get_arg_type -#define duckdb_v2_scalar_function_bind_get_arg_value duckdb_ext_api.duckdb_v2_scalar_function_bind_get_arg_value -#define duckdb_v2_scalar_function_bind_get_user_data duckdb_ext_api.duckdb_v2_scalar_function_bind_get_user_data -#define duckdb_v2_scalar_function_bind_set_bind_data duckdb_ext_api.duckdb_v2_scalar_function_bind_set_bind_data -#define duckdb_v2_scalar_function_bind_set_return_type duckdb_ext_api.duckdb_v2_scalar_function_bind_set_return_type -#define duckdb_v2_scalar_function_create_with_connection duckdb_ext_api.duckdb_v2_scalar_function_create_with_connection -#define duckdb_v2_scalar_function_create_with_extension duckdb_ext_api.duckdb_v2_scalar_function_create_with_extension -#define duckdb_v2_scalar_function_destroy duckdb_ext_api.duckdb_v2_scalar_function_destroy -#define duckdb_v2_scalar_function_exec_get_arg duckdb_ext_api.duckdb_v2_scalar_function_exec_get_arg -#define duckdb_v2_scalar_function_exec_get_arg_count duckdb_ext_api.duckdb_v2_scalar_function_exec_get_arg_count -#define duckdb_v2_scalar_function_exec_get_bind_data duckdb_ext_api.duckdb_v2_scalar_function_exec_get_bind_data -#define duckdb_v2_scalar_function_exec_get_init_data duckdb_ext_api.duckdb_v2_scalar_function_exec_get_init_data -#define duckdb_v2_scalar_function_exec_get_result duckdb_ext_api.duckdb_v2_scalar_function_exec_get_result -#define duckdb_v2_scalar_function_exec_get_row_count duckdb_ext_api.duckdb_v2_scalar_function_exec_get_row_count -#define duckdb_v2_scalar_function_exec_get_user_data duckdb_ext_api.duckdb_v2_scalar_function_exec_get_user_data -#define duckdb_v2_scalar_function_get_signature duckdb_ext_api.duckdb_v2_scalar_function_get_signature -#define duckdb_v2_scalar_function_init_get_bind_data duckdb_ext_api.duckdb_v2_scalar_function_init_get_bind_data -#define duckdb_v2_scalar_function_init_get_user_data duckdb_ext_api.duckdb_v2_scalar_function_init_get_user_data -#define duckdb_v2_scalar_function_init_set_init_data duckdb_ext_api.duckdb_v2_scalar_function_init_set_init_data -#define duckdb_v2_scalar_function_register duckdb_ext_api.duckdb_v2_scalar_function_register -#define duckdb_v2_scalar_function_set_bind_callback duckdb_ext_api.duckdb_v2_scalar_function_set_bind_callback -#define duckdb_v2_scalar_function_set_exec_callback duckdb_ext_api.duckdb_v2_scalar_function_set_exec_callback -#define duckdb_v2_scalar_function_set_init_callback duckdb_ext_api.duckdb_v2_scalar_function_set_init_callback -#define duckdb_v2_scalar_function_set_name duckdb_ext_api.duckdb_v2_scalar_function_set_name -#define duckdb_v2_scalar_function_set_user_data duckdb_ext_api.duckdb_v2_scalar_function_set_user_data -#define duckdb_v2_aggregate_function_bind_get_user_data duckdb_ext_api.duckdb_v2_aggregate_function_bind_get_user_data -#define duckdb_v2_aggregate_function_bind_set_bind_data duckdb_ext_api.duckdb_v2_aggregate_function_bind_set_bind_data +#define duckdb_v2_config_get_option_by_index duckdb_ext_api.duckdb_v2_config_get_option_by_index +#define duckdb_v2_config_get_option_by_name duckdb_ext_api.duckdb_v2_config_get_option_by_name +#define duckdb_v2_config_get_option_count duckdb_ext_api.duckdb_v2_config_get_option_count +#define duckdb_v2_connection_create duckdb_ext_api.duckdb_v2_connection_create +#define duckdb_v2_connection_destroy duckdb_ext_api.duckdb_v2_connection_destroy +#define duckdb_v2_connection_interrupt duckdb_ext_api.duckdb_v2_connection_interrupt +#define duckdb_v2_connection_progress_get duckdb_ext_api.duckdb_v2_connection_progress_get +#define duckdb_v2_data_chunk_copy duckdb_ext_api.duckdb_v2_data_chunk_copy +#define duckdb_v2_data_chunk_create duckdb_ext_api.duckdb_v2_data_chunk_create +#define duckdb_v2_data_chunk_destroy duckdb_ext_api.duckdb_v2_data_chunk_destroy +#define duckdb_v2_data_chunk_get_size duckdb_ext_api.duckdb_v2_data_chunk_get_size +#define duckdb_v2_data_chunk_get_vector duckdb_ext_api.duckdb_v2_data_chunk_get_vector +#define duckdb_v2_data_chunk_get_vector_count duckdb_ext_api.duckdb_v2_data_chunk_get_vector_count +#define duckdb_v2_environment_create duckdb_ext_api.duckdb_v2_environment_create +#define duckdb_v2_environment_destroy duckdb_ext_api.duckdb_v2_environment_destroy +#define duckdb_v2_environment_get_instance_count duckdb_ext_api.duckdb_v2_environment_get_instance_count +#define duckdb_v2_error_info_destroy duckdb_ext_api.duckdb_v2_error_info_destroy +#define duckdb_v2_error_info_get_code duckdb_ext_api.duckdb_v2_error_info_get_code +#define duckdb_v2_error_info_get_raw_text duckdb_ext_api.duckdb_v2_error_info_get_raw_text +#define duckdb_v2_error_info_get_text duckdb_ext_api.duckdb_v2_error_info_get_text +#define duckdb_v2_error_info_set_code duckdb_ext_api.duckdb_v2_error_info_set_code +#define duckdb_v2_error_info_set_text duckdb_ext_api.duckdb_v2_error_info_set_text +#define duckdb_v2_factory_create_type_from_id duckdb_ext_api.duckdb_v2_factory_create_type_from_id +#define duckdb_v2_factory_create_type_from_name duckdb_ext_api.duckdb_v2_factory_create_type_from_name +#define duckdb_v2_factory_create_type_from_text duckdb_ext_api.duckdb_v2_factory_create_type_from_text +#define duckdb_v2_factory_create_type_with_alias duckdb_ext_api.duckdb_v2_factory_create_type_with_alias +#define duckdb_v2_instance_attach duckdb_ext_api.duckdb_v2_instance_attach +#define duckdb_v2_instance_create duckdb_ext_api.duckdb_v2_instance_create +#define duckdb_v2_instance_create_with_options duckdb_ext_api.duckdb_v2_instance_create_with_options +#define duckdb_v2_instance_destroy duckdb_ext_api.duckdb_v2_instance_destroy +#define duckdb_v2_instance_detach duckdb_ext_api.duckdb_v2_instance_detach +#define duckdb_v2_library_version duckdb_ext_api.duckdb_v2_library_version +#define duckdb_v2_logical_type_copy duckdb_ext_api.duckdb_v2_logical_type_copy +#define duckdb_v2_logical_type_destroy duckdb_ext_api.duckdb_v2_logical_type_destroy +#define duckdb_v2_logical_type_get_id duckdb_ext_api.duckdb_v2_logical_type_get_id +#define duckdb_v2_logical_type_get_name duckdb_ext_api.duckdb_v2_logical_type_get_name +#define duckdb_v2_logical_type_get_param duckdb_ext_api.duckdb_v2_logical_type_get_param +#define duckdb_v2_logical_type_get_param_count duckdb_ext_api.duckdb_v2_logical_type_get_param_count +#define duckdb_v2_logical_type_is_equal duckdb_ext_api.duckdb_v2_logical_type_is_equal +#define duckdb_v2_logical_type_to_text duckdb_ext_api.duckdb_v2_logical_type_to_text +#define duckdb_v2_option_destroy duckdb_ext_api.duckdb_v2_option_destroy +#define duckdb_v2_option_get_alias duckdb_ext_api.duckdb_v2_option_get_alias +#define duckdb_v2_option_get_alias_count duckdb_ext_api.duckdb_v2_option_get_alias_count +#define duckdb_v2_option_get_default_value duckdb_ext_api.duckdb_v2_option_get_default_value +#define duckdb_v2_option_get_description duckdb_ext_api.duckdb_v2_option_get_description +#define duckdb_v2_option_get_name duckdb_ext_api.duckdb_v2_option_get_name +#define duckdb_v2_parse_sql duckdb_ext_api.duckdb_v2_parse_sql +#define duckdb_v2_result_destroy duckdb_ext_api.duckdb_v2_result_destroy +#define duckdb_v2_result_drain duckdb_ext_api.duckdb_v2_result_drain +#define duckdb_v2_result_fetch_chunk duckdb_ext_api.duckdb_v2_result_fetch_chunk +#define duckdb_v2_result_get_result_type duckdb_ext_api.duckdb_v2_result_get_result_type +#define duckdb_v2_result_get_schema duckdb_ext_api.duckdb_v2_result_get_schema +#define duckdb_v2_result_get_statement_type duckdb_ext_api.duckdb_v2_result_get_statement_type +#define duckdb_v2_result_render_box duckdb_ext_api.duckdb_v2_result_render_box +#define duckdb_v2_result_step duckdb_ext_api.duckdb_v2_result_step +#define duckdb_v2_result_wait duckdb_ext_api.duckdb_v2_result_wait +#define duckdb_v2_schema_destroy duckdb_ext_api.duckdb_v2_schema_destroy +#define duckdb_v2_schema_get_count duckdb_ext_api.duckdb_v2_schema_get_count +#define duckdb_v2_schema_get_field duckdb_ext_api.duckdb_v2_schema_get_field +#define duckdb_v2_sql_statement_destroy duckdb_ext_api.duckdb_v2_sql_statement_destroy +#define duckdb_v2_statement_bind duckdb_ext_api.duckdb_v2_statement_bind +#define duckdb_v2_statement_execute duckdb_ext_api.duckdb_v2_statement_execute +#define duckdb_v2_statement_iterator_destroy duckdb_ext_api.duckdb_v2_statement_iterator_destroy +#define duckdb_v2_statement_iterator_next duckdb_ext_api.duckdb_v2_statement_iterator_next +#define duckdb_v2_value_cast duckdb_ext_api.duckdb_v2_value_cast +#define duckdb_v2_value_create_array duckdb_ext_api.duckdb_v2_value_create_array +#define duckdb_v2_value_create_bigint duckdb_ext_api.duckdb_v2_value_create_bigint +#define duckdb_v2_value_create_bignum duckdb_ext_api.duckdb_v2_value_create_bignum +#define duckdb_v2_value_create_bit duckdb_ext_api.duckdb_v2_value_create_bit +#define duckdb_v2_value_create_blob duckdb_ext_api.duckdb_v2_value_create_blob +#define duckdb_v2_value_create_bool duckdb_ext_api.duckdb_v2_value_create_bool +#define duckdb_v2_value_create_date duckdb_ext_api.duckdb_v2_value_create_date +#define duckdb_v2_value_create_decimal duckdb_ext_api.duckdb_v2_value_create_decimal +#define duckdb_v2_value_create_double duckdb_ext_api.duckdb_v2_value_create_double +#define duckdb_v2_value_create_float duckdb_ext_api.duckdb_v2_value_create_float +#define duckdb_v2_value_create_hugeint duckdb_ext_api.duckdb_v2_value_create_hugeint +#define duckdb_v2_value_create_int duckdb_ext_api.duckdb_v2_value_create_int +#define duckdb_v2_value_create_interval duckdb_ext_api.duckdb_v2_value_create_interval +#define duckdb_v2_value_create_list duckdb_ext_api.duckdb_v2_value_create_list +#define duckdb_v2_value_create_map duckdb_ext_api.duckdb_v2_value_create_map +#define duckdb_v2_value_create_null duckdb_ext_api.duckdb_v2_value_create_null +#define duckdb_v2_value_create_smallint duckdb_ext_api.duckdb_v2_value_create_smallint +#define duckdb_v2_value_create_struct duckdb_ext_api.duckdb_v2_value_create_struct +#define duckdb_v2_value_create_time duckdb_ext_api.duckdb_v2_value_create_time +#define duckdb_v2_value_create_time_ns duckdb_ext_api.duckdb_v2_value_create_time_ns +#define duckdb_v2_value_create_time_tz duckdb_ext_api.duckdb_v2_value_create_time_tz +#define duckdb_v2_value_create_timestamp duckdb_ext_api.duckdb_v2_value_create_timestamp +#define duckdb_v2_value_create_timestamp_ms duckdb_ext_api.duckdb_v2_value_create_timestamp_ms +#define duckdb_v2_value_create_timestamp_ns duckdb_ext_api.duckdb_v2_value_create_timestamp_ns +#define duckdb_v2_value_create_timestamp_sec duckdb_ext_api.duckdb_v2_value_create_timestamp_sec +#define duckdb_v2_value_create_timestamp_tz duckdb_ext_api.duckdb_v2_value_create_timestamp_tz +#define duckdb_v2_value_create_timestamp_tz_ns duckdb_ext_api.duckdb_v2_value_create_timestamp_tz_ns +#define duckdb_v2_value_create_tinyint duckdb_ext_api.duckdb_v2_value_create_tinyint +#define duckdb_v2_value_create_tuple duckdb_ext_api.duckdb_v2_value_create_tuple +#define duckdb_v2_value_create_type duckdb_ext_api.duckdb_v2_value_create_type +#define duckdb_v2_value_create_ubigint duckdb_ext_api.duckdb_v2_value_create_ubigint +#define duckdb_v2_value_create_uhugeint duckdb_ext_api.duckdb_v2_value_create_uhugeint +#define duckdb_v2_value_create_uint duckdb_ext_api.duckdb_v2_value_create_uint +#define duckdb_v2_value_create_usmallint duckdb_ext_api.duckdb_v2_value_create_usmallint +#define duckdb_v2_value_create_utinyint duckdb_ext_api.duckdb_v2_value_create_utinyint +#define duckdb_v2_value_create_uuid duckdb_ext_api.duckdb_v2_value_create_uuid +#define duckdb_v2_value_create_varchar duckdb_ext_api.duckdb_v2_value_create_varchar +#define duckdb_v2_value_destroy duckdb_ext_api.duckdb_v2_value_destroy +#define duckdb_v2_value_get_bigint duckdb_ext_api.duckdb_v2_value_get_bigint +#define duckdb_v2_value_get_blob duckdb_ext_api.duckdb_v2_value_get_blob +#define duckdb_v2_value_get_bool duckdb_ext_api.duckdb_v2_value_get_bool +#define duckdb_v2_value_get_child duckdb_ext_api.duckdb_v2_value_get_child +#define duckdb_v2_value_get_child_count duckdb_ext_api.duckdb_v2_value_get_child_count +#define duckdb_v2_value_get_date duckdb_ext_api.duckdb_v2_value_get_date +#define duckdb_v2_value_get_decimal duckdb_ext_api.duckdb_v2_value_get_decimal +#define duckdb_v2_value_get_double duckdb_ext_api.duckdb_v2_value_get_double +#define duckdb_v2_value_get_float duckdb_ext_api.duckdb_v2_value_get_float +#define duckdb_v2_value_get_hugeint duckdb_ext_api.duckdb_v2_value_get_hugeint +#define duckdb_v2_value_get_int duckdb_ext_api.duckdb_v2_value_get_int +#define duckdb_v2_value_get_interval duckdb_ext_api.duckdb_v2_value_get_interval +#define duckdb_v2_value_get_logical_type duckdb_ext_api.duckdb_v2_value_get_logical_type +#define duckdb_v2_value_get_smallint duckdb_ext_api.duckdb_v2_value_get_smallint +#define duckdb_v2_value_get_time duckdb_ext_api.duckdb_v2_value_get_time +#define duckdb_v2_value_get_time_ns duckdb_ext_api.duckdb_v2_value_get_time_ns +#define duckdb_v2_value_get_time_tz duckdb_ext_api.duckdb_v2_value_get_time_tz +#define duckdb_v2_value_get_timestamp duckdb_ext_api.duckdb_v2_value_get_timestamp +#define duckdb_v2_value_get_timestamp_ms duckdb_ext_api.duckdb_v2_value_get_timestamp_ms +#define duckdb_v2_value_get_timestamp_ns duckdb_ext_api.duckdb_v2_value_get_timestamp_ns +#define duckdb_v2_value_get_timestamp_sec duckdb_ext_api.duckdb_v2_value_get_timestamp_sec +#define duckdb_v2_value_get_timestamp_tz duckdb_ext_api.duckdb_v2_value_get_timestamp_tz +#define duckdb_v2_value_get_timestamp_tz_ns duckdb_ext_api.duckdb_v2_value_get_timestamp_tz_ns +#define duckdb_v2_value_get_tinyint duckdb_ext_api.duckdb_v2_value_get_tinyint +#define duckdb_v2_value_get_type duckdb_ext_api.duckdb_v2_value_get_type +#define duckdb_v2_value_get_ubigint duckdb_ext_api.duckdb_v2_value_get_ubigint +#define duckdb_v2_value_get_uhugeint duckdb_ext_api.duckdb_v2_value_get_uhugeint +#define duckdb_v2_value_get_uint duckdb_ext_api.duckdb_v2_value_get_uint +#define duckdb_v2_value_get_usmallint duckdb_ext_api.duckdb_v2_value_get_usmallint +#define duckdb_v2_value_get_utinyint duckdb_ext_api.duckdb_v2_value_get_utinyint +#define duckdb_v2_value_get_uuid duckdb_ext_api.duckdb_v2_value_get_uuid +#define duckdb_v2_value_get_varchar duckdb_ext_api.duckdb_v2_value_get_varchar +#define duckdb_v2_value_is_null duckdb_ext_api.duckdb_v2_value_is_null +#define duckdb_v2_value_to_string duckdb_ext_api.duckdb_v2_value_to_string +#define duckdb_v2_vector_constant_set_valid duckdb_ext_api.duckdb_v2_vector_constant_set_valid +#define duckdb_v2_vector_flat_get_validity_mutable duckdb_ext_api.duckdb_v2_vector_flat_get_validity_mutable +#define duckdb_v2_vector_flatten duckdb_ext_api.duckdb_v2_vector_flatten +#define duckdb_v2_vector_get_arena duckdb_ext_api.duckdb_v2_vector_get_arena +#define duckdb_v2_vector_get_child duckdb_ext_api.duckdb_v2_vector_get_child +#define duckdb_v2_vector_get_child_count duckdb_ext_api.duckdb_v2_vector_get_child_count +#define duckdb_v2_vector_get_data_mutable duckdb_ext_api.duckdb_v2_vector_get_data_mutable +#define duckdb_v2_vector_get_logical_type duckdb_ext_api.duckdb_v2_vector_get_logical_type +#define duckdb_v2_vector_get_size duckdb_ext_api.duckdb_v2_vector_get_size +#define duckdb_v2_vector_get_value duckdb_ext_api.duckdb_v2_vector_get_value +#define duckdb_v2_vector_get_vector_type duckdb_ext_api.duckdb_v2_vector_get_vector_type +#define duckdb_v2_vector_get_view duckdb_ext_api.duckdb_v2_vector_get_view +#define duckdb_v2_vector_make_constant duckdb_ext_api.duckdb_v2_vector_make_constant +#define duckdb_v2_vector_make_sequence duckdb_ext_api.duckdb_v2_vector_make_sequence +#define duckdb_v2_vector_reference duckdb_ext_api.duckdb_v2_vector_reference +#define duckdb_v2_vector_set_null duckdb_ext_api.duckdb_v2_vector_set_null +#define duckdb_v2_vector_set_size duckdb_ext_api.duckdb_v2_vector_set_size +#define duckdb_v2_vector_set_value duckdb_ext_api.duckdb_v2_vector_set_value +#define duckdb_v2_connection_register_scalar_function duckdb_ext_api.duckdb_v2_connection_register_scalar_function +#define duckdb_v2_context_log duckdb_ext_api.duckdb_v2_context_log +#define duckdb_v2_extension_register_scalar_function duckdb_ext_api.duckdb_v2_extension_register_scalar_function +#define duckdb_v2_function_signature_add_parameter duckdb_ext_api.duckdb_v2_function_signature_add_parameter +#define duckdb_v2_function_signature_set_return_type duckdb_ext_api.duckdb_v2_function_signature_set_return_type +#define duckdb_v2_scalar_function_create duckdb_ext_api.duckdb_v2_scalar_function_create +#define duckdb_v2_scalar_function_destroy duckdb_ext_api.duckdb_v2_scalar_function_destroy +#define duckdb_v2_scalar_function_exec_get_arg duckdb_ext_api.duckdb_v2_scalar_function_exec_get_arg +#define duckdb_v2_scalar_function_exec_get_arg_count duckdb_ext_api.duckdb_v2_scalar_function_exec_get_arg_count +#define duckdb_v2_scalar_function_exec_get_bind_data duckdb_ext_api.duckdb_v2_scalar_function_exec_get_bind_data +#define duckdb_v2_scalar_function_exec_get_init_data duckdb_ext_api.duckdb_v2_scalar_function_exec_get_init_data +#define duckdb_v2_scalar_function_exec_get_result duckdb_ext_api.duckdb_v2_scalar_function_exec_get_result +#define duckdb_v2_scalar_function_exec_get_row_count duckdb_ext_api.duckdb_v2_scalar_function_exec_get_row_count +#define duckdb_v2_scalar_function_exec_get_user_data duckdb_ext_api.duckdb_v2_scalar_function_exec_get_user_data +#define duckdb_v2_scalar_function_get_signature duckdb_ext_api.duckdb_v2_scalar_function_get_signature +#define duckdb_v2_scalar_function_init_get_bind_data duckdb_ext_api.duckdb_v2_scalar_function_init_get_bind_data +#define duckdb_v2_scalar_function_init_get_user_data duckdb_ext_api.duckdb_v2_scalar_function_init_get_user_data +#define duckdb_v2_scalar_function_init_set_init_data duckdb_ext_api.duckdb_v2_scalar_function_init_set_init_data +#define duckdb_v2_scalar_function_set_bind_callback duckdb_ext_api.duckdb_v2_scalar_function_set_bind_callback +#define duckdb_v2_scalar_function_set_exec_callback duckdb_ext_api.duckdb_v2_scalar_function_set_exec_callback +#define duckdb_v2_scalar_function_set_init_callback duckdb_ext_api.duckdb_v2_scalar_function_set_init_callback +#define duckdb_v2_scalar_function_set_name duckdb_ext_api.duckdb_v2_scalar_function_set_name +#define duckdb_v2_scalar_function_set_user_data duckdb_ext_api.duckdb_v2_scalar_function_set_user_data #define duckdb_v2_aggregate_function_bind_set_return_type \ duckdb_ext_api.duckdb_v2_aggregate_function_bind_set_return_type #define duckdb_v2_aggregate_function_combine_get_bind_data \ @@ -1605,10 +1686,7 @@ typedef struct { #define duckdb_v2_aggregate_function_combine_get_targets duckdb_ext_api.duckdb_v2_aggregate_function_combine_get_targets #define duckdb_v2_aggregate_function_combine_get_user_data \ duckdb_ext_api.duckdb_v2_aggregate_function_combine_get_user_data -#define duckdb_v2_aggregate_function_create_with_connection \ - duckdb_ext_api.duckdb_v2_aggregate_function_create_with_connection -#define duckdb_v2_aggregate_function_create_with_extension \ - duckdb_ext_api.duckdb_v2_aggregate_function_create_with_extension +#define duckdb_v2_aggregate_function_create duckdb_ext_api.duckdb_v2_aggregate_function_create #define duckdb_v2_aggregate_function_destroy duckdb_ext_api.duckdb_v2_aggregate_function_destroy #define duckdb_v2_aggregate_function_destroy_get_bind_data \ duckdb_ext_api.duckdb_v2_aggregate_function_destroy_get_bind_data @@ -1633,7 +1711,6 @@ typedef struct { duckdb_ext_api.duckdb_v2_aggregate_function_init_get_state_count #define duckdb_v2_aggregate_function_init_get_states duckdb_ext_api.duckdb_v2_aggregate_function_init_get_states #define duckdb_v2_aggregate_function_init_get_user_data duckdb_ext_api.duckdb_v2_aggregate_function_init_get_user_data -#define duckdb_v2_aggregate_function_register duckdb_ext_api.duckdb_v2_aggregate_function_register #define duckdb_v2_aggregate_function_set_bind_callback duckdb_ext_api.duckdb_v2_aggregate_function_set_bind_callback #define duckdb_v2_aggregate_function_set_combine_callback \ duckdb_ext_api.duckdb_v2_aggregate_function_set_combine_callback @@ -1659,24 +1736,22 @@ typedef struct { #define duckdb_v2_aggregate_function_update_get_states duckdb_ext_api.duckdb_v2_aggregate_function_update_get_states #define duckdb_v2_aggregate_function_update_get_user_data \ duckdb_ext_api.duckdb_v2_aggregate_function_update_get_user_data -#define duckdb_v2_aggregate_function_set_property duckdb_ext_api.duckdb_v2_aggregate_function_set_property -#define duckdb_v2_scalar_function_set_property duckdb_ext_api.duckdb_v2_scalar_function_set_property -#define duckdb_v2_table_function_bind_add_result_column duckdb_ext_api.duckdb_v2_table_function_bind_add_result_column -#define duckdb_v2_table_function_bind_get_arg_count duckdb_ext_api.duckdb_v2_table_function_bind_get_arg_count -#define duckdb_v2_table_function_bind_get_arg_type duckdb_ext_api.duckdb_v2_table_function_bind_get_arg_type -#define duckdb_v2_table_function_bind_get_arg_value duckdb_ext_api.duckdb_v2_table_function_bind_get_arg_value -#define duckdb_v2_table_function_bind_get_user_data duckdb_ext_api.duckdb_v2_table_function_bind_get_user_data -#define duckdb_v2_table_function_bind_set_bind_data duckdb_ext_api.duckdb_v2_table_function_bind_set_bind_data -#define duckdb_v2_table_function_bind_set_cardinality duckdb_ext_api.duckdb_v2_table_function_bind_set_cardinality -#define duckdb_v2_table_function_create_with_connection duckdb_ext_api.duckdb_v2_table_function_create_with_connection -#define duckdb_v2_table_function_create_with_extension duckdb_ext_api.duckdb_v2_table_function_create_with_extension -#define duckdb_v2_table_function_destroy duckdb_ext_api.duckdb_v2_table_function_destroy -#define duckdb_v2_table_function_exec_get_bind_data duckdb_ext_api.duckdb_v2_table_function_exec_get_bind_data -#define duckdb_v2_table_function_exec_get_global_state duckdb_ext_api.duckdb_v2_table_function_exec_get_global_state -#define duckdb_v2_table_function_exec_get_local_state duckdb_ext_api.duckdb_v2_table_function_exec_get_local_state -#define duckdb_v2_table_function_exec_get_output_chunk duckdb_ext_api.duckdb_v2_table_function_exec_get_output_chunk -#define duckdb_v2_table_function_exec_get_user_data duckdb_ext_api.duckdb_v2_table_function_exec_get_user_data -#define duckdb_v2_table_function_get_signature duckdb_ext_api.duckdb_v2_table_function_get_signature +#define duckdb_v2_connection_register_aggregate_function duckdb_ext_api.duckdb_v2_connection_register_aggregate_function +#define duckdb_v2_extension_register_aggregate_function duckdb_ext_api.duckdb_v2_extension_register_aggregate_function +#define duckdb_v2_aggregate_function_set_property duckdb_ext_api.duckdb_v2_aggregate_function_set_property +#define duckdb_v2_scalar_function_set_property duckdb_ext_api.duckdb_v2_scalar_function_set_property +#define duckdb_v2_connection_register_table_function duckdb_ext_api.duckdb_v2_connection_register_table_function +#define duckdb_v2_extension_register_table_function duckdb_ext_api.duckdb_v2_extension_register_table_function +#define duckdb_v2_table_function_bind_add_result_column duckdb_ext_api.duckdb_v2_table_function_bind_add_result_column +#define duckdb_v2_table_function_bind_set_cardinality duckdb_ext_api.duckdb_v2_table_function_bind_set_cardinality +#define duckdb_v2_table_function_create duckdb_ext_api.duckdb_v2_table_function_create +#define duckdb_v2_table_function_destroy duckdb_ext_api.duckdb_v2_table_function_destroy +#define duckdb_v2_table_function_exec_get_bind_data duckdb_ext_api.duckdb_v2_table_function_exec_get_bind_data +#define duckdb_v2_table_function_exec_get_global_state duckdb_ext_api.duckdb_v2_table_function_exec_get_global_state +#define duckdb_v2_table_function_exec_get_local_state duckdb_ext_api.duckdb_v2_table_function_exec_get_local_state +#define duckdb_v2_table_function_exec_get_output_chunk duckdb_ext_api.duckdb_v2_table_function_exec_get_output_chunk +#define duckdb_v2_table_function_exec_get_user_data duckdb_ext_api.duckdb_v2_table_function_exec_get_user_data +#define duckdb_v2_table_function_get_signature duckdb_ext_api.duckdb_v2_table_function_get_signature #define duckdb_v2_table_function_init_global_get_bind_data \ duckdb_ext_api.duckdb_v2_table_function_init_global_get_bind_data #define duckdb_v2_table_function_init_global_get_user_data \ @@ -1698,7 +1773,6 @@ typedef struct { duckdb_ext_api.duckdb_v2_table_function_progress_get_global_state #define duckdb_v2_table_function_progress_get_user_data duckdb_ext_api.duckdb_v2_table_function_progress_get_user_data #define duckdb_v2_table_function_progress_set_progress duckdb_ext_api.duckdb_v2_table_function_progress_set_progress -#define duckdb_v2_table_function_register duckdb_ext_api.duckdb_v2_table_function_register #define duckdb_v2_table_function_set_bind_callback duckdb_ext_api.duckdb_v2_table_function_set_bind_callback #define duckdb_v2_table_function_set_exec_callback duckdb_ext_api.duckdb_v2_table_function_set_exec_callback #define duckdb_v2_table_function_set_init_global_callback \ @@ -1707,27 +1781,30 @@ typedef struct { #define duckdb_v2_table_function_set_name duckdb_ext_api.duckdb_v2_table_function_set_name #define duckdb_v2_table_function_set_progress_callback duckdb_ext_api.duckdb_v2_table_function_set_progress_callback #define duckdb_v2_table_function_set_user_data duckdb_ext_api.duckdb_v2_table_function_set_user_data -#define duckdb_v2_cast_function_create_with_connection duckdb_ext_api.duckdb_v2_cast_function_create_with_connection -#define duckdb_v2_cast_function_create_with_extension duckdb_ext_api.duckdb_v2_cast_function_create_with_extension +#define duckdb_v2_cast_function_create duckdb_ext_api.duckdb_v2_cast_function_create #define duckdb_v2_cast_function_destroy duckdb_ext_api.duckdb_v2_cast_function_destroy #define duckdb_v2_cast_function_exec_get_input duckdb_ext_api.duckdb_v2_cast_function_exec_get_input #define duckdb_v2_cast_function_exec_get_mode duckdb_ext_api.duckdb_v2_cast_function_exec_get_mode #define duckdb_v2_cast_function_exec_get_output duckdb_ext_api.duckdb_v2_cast_function_exec_get_output #define duckdb_v2_cast_function_exec_get_row_count duckdb_ext_api.duckdb_v2_cast_function_exec_get_row_count #define duckdb_v2_cast_function_exec_get_user_data duckdb_ext_api.duckdb_v2_cast_function_exec_get_user_data -#define duckdb_v2_cast_function_register duckdb_ext_api.duckdb_v2_cast_function_register #define duckdb_v2_cast_function_set_exec_callback duckdb_ext_api.duckdb_v2_cast_function_set_exec_callback #define duckdb_v2_cast_function_set_implicit_cast_cost duckdb_ext_api.duckdb_v2_cast_function_set_implicit_cast_cost #define duckdb_v2_cast_function_set_source_type duckdb_ext_api.duckdb_v2_cast_function_set_source_type #define duckdb_v2_cast_function_set_target_type duckdb_ext_api.duckdb_v2_cast_function_set_target_type #define duckdb_v2_cast_function_set_user_data duckdb_ext_api.duckdb_v2_cast_function_set_user_data #define duckdb_v2_column_data_collection_clear duckdb_ext_api.duckdb_v2_column_data_collection_clear -#define duckdb_v2_custom_type_create_with_connection duckdb_ext_api.duckdb_v2_custom_type_create_with_connection -#define duckdb_v2_custom_type_create_with_extension duckdb_ext_api.duckdb_v2_custom_type_create_with_extension +#define duckdb_v2_connection_register_cast_function duckdb_ext_api.duckdb_v2_connection_register_cast_function +#define duckdb_v2_connection_register_custom_type duckdb_ext_api.duckdb_v2_connection_register_custom_type +#define duckdb_v2_connection_register_replacement_scan duckdb_ext_api.duckdb_v2_connection_register_replacement_scan +#define duckdb_v2_context_get_file_system duckdb_ext_api.duckdb_v2_context_get_file_system +#define duckdb_v2_custom_type_create duckdb_ext_api.duckdb_v2_custom_type_create #define duckdb_v2_custom_type_destroy duckdb_ext_api.duckdb_v2_custom_type_destroy -#define duckdb_v2_custom_type_register duckdb_ext_api.duckdb_v2_custom_type_register #define duckdb_v2_custom_type_set_base_type duckdb_ext_api.duckdb_v2_custom_type_set_base_type #define duckdb_v2_custom_type_set_name duckdb_ext_api.duckdb_v2_custom_type_set_name +#define duckdb_v2_extension_register_cast_function duckdb_ext_api.duckdb_v2_extension_register_cast_function +#define duckdb_v2_extension_register_custom_type duckdb_ext_api.duckdb_v2_extension_register_custom_type +#define duckdb_v2_extension_register_replacement_scan duckdb_ext_api.duckdb_v2_extension_register_replacement_scan #define duckdb_v2_file_close duckdb_ext_api.duckdb_v2_file_close #define duckdb_v2_file_destroy duckdb_ext_api.duckdb_v2_file_destroy #define duckdb_v2_file_open_options_create duckdb_ext_api.duckdb_v2_file_open_options_create @@ -1739,12 +1816,11 @@ typedef struct { #define duckdb_v2_file_seek duckdb_ext_api.duckdb_v2_file_seek #define duckdb_v2_file_size duckdb_ext_api.duckdb_v2_file_size #define duckdb_v2_file_sync duckdb_ext_api.duckdb_v2_file_sync -#define duckdb_v2_file_system_get_from_connection duckdb_ext_api.duckdb_v2_file_system_get_from_connection -#define duckdb_v2_file_system_get_from_context duckdb_ext_api.duckdb_v2_file_system_get_from_context #define duckdb_v2_file_system_open duckdb_ext_api.duckdb_v2_file_system_open #define duckdb_v2_file_tell duckdb_ext_api.duckdb_v2_file_tell #define duckdb_v2_file_write duckdb_ext_api.duckdb_v2_file_write #define duckdb_v2_file_write_at duckdb_ext_api.duckdb_v2_file_write_at +#define duckdb_v2_instance_register_replacement_scan duckdb_ext_api.duckdb_v2_instance_register_replacement_scan #define duckdb_v2_qname_create duckdb_ext_api.duckdb_v2_qname_create #define duckdb_v2_qname_destroy duckdb_ext_api.duckdb_v2_qname_destroy #define duckdb_v2_qname_equals duckdb_ext_api.duckdb_v2_qname_equals @@ -1755,14 +1831,10 @@ typedef struct { #define duckdb_v2_qname_render duckdb_ext_api.duckdb_v2_qname_render #define duckdb_v2_replacement_scan_add_argument duckdb_ext_api.duckdb_v2_replacement_scan_add_argument #define duckdb_v2_replacement_scan_add_named_argument duckdb_ext_api.duckdb_v2_replacement_scan_add_named_argument -#define duckdb_v2_replacement_scan_create_with_connection \ - duckdb_ext_api.duckdb_v2_replacement_scan_create_with_connection -#define duckdb_v2_replacement_scan_create_with_database duckdb_ext_api.duckdb_v2_replacement_scan_create_with_database -#define duckdb_v2_replacement_scan_create_with_extension duckdb_ext_api.duckdb_v2_replacement_scan_create_with_extension +#define duckdb_v2_replacement_scan_create duckdb_ext_api.duckdb_v2_replacement_scan_create #define duckdb_v2_replacement_scan_destroy duckdb_ext_api.duckdb_v2_replacement_scan_destroy #define duckdb_v2_replacement_scan_get_name duckdb_ext_api.duckdb_v2_replacement_scan_get_name #define duckdb_v2_replacement_scan_get_user_data duckdb_ext_api.duckdb_v2_replacement_scan_get_user_data -#define duckdb_v2_replacement_scan_register duckdb_ext_api.duckdb_v2_replacement_scan_register #define duckdb_v2_replacement_scan_set_alias duckdb_ext_api.duckdb_v2_replacement_scan_set_alias #define duckdb_v2_replacement_scan_set_callback duckdb_ext_api.duckdb_v2_replacement_scan_set_callback #define duckdb_v2_replacement_scan_set_collection duckdb_ext_api.duckdb_v2_replacement_scan_set_collection @@ -1785,6 +1857,7 @@ typedef struct { #define duckdb_v2_column_description_has_default duckdb_ext_api.duckdb_v2_column_description_has_default #define duckdb_v2_column_description_has_generated duckdb_ext_api.duckdb_v2_column_description_has_generated #define duckdb_v2_connection_describe_table duckdb_ext_api.duckdb_v2_connection_describe_table +#define duckdb_v2_connection_register_copy_function duckdb_ext_api.duckdb_v2_connection_register_copy_function #define duckdb_v2_copy_from_bind_get_column_count duckdb_ext_api.duckdb_v2_copy_from_bind_get_column_count #define duckdb_v2_copy_from_bind_get_column_name duckdb_ext_api.duckdb_v2_copy_from_bind_get_column_name #define duckdb_v2_copy_from_bind_get_column_type duckdb_ext_api.duckdb_v2_copy_from_bind_get_column_type @@ -1817,10 +1890,8 @@ typedef struct { #define duckdb_v2_copy_from_set_init_global_callback duckdb_ext_api.duckdb_v2_copy_from_set_init_global_callback #define duckdb_v2_copy_from_set_init_local_callback duckdb_ext_api.duckdb_v2_copy_from_set_init_local_callback #define duckdb_v2_copy_from_set_progress_callback duckdb_ext_api.duckdb_v2_copy_from_set_progress_callback -#define duckdb_v2_copy_function_create_with_connection duckdb_ext_api.duckdb_v2_copy_function_create_with_connection -#define duckdb_v2_copy_function_create_with_extension duckdb_ext_api.duckdb_v2_copy_function_create_with_extension +#define duckdb_v2_copy_function_create duckdb_ext_api.duckdb_v2_copy_function_create #define duckdb_v2_copy_function_destroy duckdb_ext_api.duckdb_v2_copy_function_destroy -#define duckdb_v2_copy_function_register duckdb_ext_api.duckdb_v2_copy_function_register #define duckdb_v2_copy_function_set_name duckdb_ext_api.duckdb_v2_copy_function_set_name #define duckdb_v2_copy_function_set_user_data duckdb_ext_api.duckdb_v2_copy_function_set_user_data #define duckdb_v2_copy_to_batch_get_bind_data duckdb_ext_api.duckdb_v2_copy_to_batch_get_bind_data @@ -1866,12 +1937,12 @@ typedef struct { #define duckdb_v2_expression_get_child_count duckdb_ext_api.duckdb_v2_expression_get_child_count #define duckdb_v2_expression_get_return_type duckdb_ext_api.duckdb_v2_expression_get_return_type #define duckdb_v2_expression_get_type duckdb_ext_api.duckdb_v2_expression_get_type +#define duckdb_v2_extension_register_copy_function duckdb_ext_api.duckdb_v2_extension_register_copy_function #define duckdb_v2_identifier_render_quoted duckdb_ext_api.duckdb_v2_identifier_render_quoted #define duckdb_v2_prepared_statement_create duckdb_ext_api.duckdb_v2_prepared_statement_create #define duckdb_v2_prepared_statement_destroy duckdb_ext_api.duckdb_v2_prepared_statement_destroy #define duckdb_v2_prepared_statement_execute duckdb_ext_api.duckdb_v2_prepared_statement_execute #define duckdb_v2_prepared_statement_reuses_plan duckdb_ext_api.duckdb_v2_prepared_statement_reuses_plan -#define duckdb_v2_result_to_arrow_stream duckdb_ext_api.duckdb_v2_result_to_arrow_stream #define duckdb_v2_table_description_destroy duckdb_ext_api.duckdb_v2_table_description_destroy #define duckdb_v2_table_description_get_column duckdb_ext_api.duckdb_v2_table_description_get_column #define duckdb_v2_table_description_get_column_count duckdb_ext_api.duckdb_v2_table_description_get_column_count @@ -1907,6 +1978,245 @@ typedef struct { #define duckdb_v2_sql_statement_get_parameter_name duckdb_ext_api.duckdb_v2_sql_statement_get_parameter_name #define duckdb_v2_sql_statement_get_text duckdb_ext_api.duckdb_v2_sql_statement_get_text #define duckdb_v2_sql_statement_get_type duckdb_ext_api.duckdb_v2_sql_statement_get_type +#define duckdb_v2_token_iterator_destroy duckdb_ext_api.duckdb_v2_token_iterator_destroy +#define duckdb_v2_token_iterator_ends_unterminated duckdb_ext_api.duckdb_v2_token_iterator_ends_unterminated +#define duckdb_v2_token_iterator_next duckdb_ext_api.duckdb_v2_token_iterator_next +#define duckdb_v2_tokenize_sql duckdb_ext_api.duckdb_v2_tokenize_sql +#define duckdb_v2_attach_options_create duckdb_ext_api.duckdb_v2_attach_options_create +#define duckdb_v2_attach_options_destroy duckdb_ext_api.duckdb_v2_attach_options_destroy +#define duckdb_v2_attach_options_set duckdb_ext_api.duckdb_v2_attach_options_set +#define duckdb_v2_instance_set_default duckdb_ext_api.duckdb_v2_instance_set_default +#define duckdb_v2_table_function_partition_data_get_bind_data \ + duckdb_ext_api.duckdb_v2_table_function_partition_data_get_bind_data +#define duckdb_v2_table_function_partition_data_get_global_state \ + duckdb_ext_api.duckdb_v2_table_function_partition_data_get_global_state +#define duckdb_v2_table_function_partition_data_get_local_state \ + duckdb_ext_api.duckdb_v2_table_function_partition_data_get_local_state +#define duckdb_v2_table_function_partition_data_get_partition_column_count \ + duckdb_ext_api.duckdb_v2_table_function_partition_data_get_partition_column_count +#define duckdb_v2_table_function_partition_data_get_partition_column_index \ + duckdb_ext_api.duckdb_v2_table_function_partition_data_get_partition_column_index +#define duckdb_v2_table_function_partition_data_get_user_data \ + duckdb_ext_api.duckdb_v2_table_function_partition_data_get_user_data +#define duckdb_v2_table_function_partition_data_requires_batch_index \ + duckdb_ext_api.duckdb_v2_table_function_partition_data_requires_batch_index +#define duckdb_v2_table_function_partition_data_requires_partition_columns \ + duckdb_ext_api.duckdb_v2_table_function_partition_data_requires_partition_columns +#define duckdb_v2_table_function_partition_data_set_batch_index \ + duckdb_ext_api.duckdb_v2_table_function_partition_data_set_batch_index +#define duckdb_v2_table_function_partition_data_set_partition_value \ + duckdb_ext_api.duckdb_v2_table_function_partition_data_set_partition_value +#define duckdb_v2_table_function_partitioning_get_bind_data \ + duckdb_ext_api.duckdb_v2_table_function_partitioning_get_bind_data +#define duckdb_v2_table_function_partitioning_get_partition_column_count \ + duckdb_ext_api.duckdb_v2_table_function_partitioning_get_partition_column_count +#define duckdb_v2_table_function_partitioning_get_partition_column_index \ + duckdb_ext_api.duckdb_v2_table_function_partitioning_get_partition_column_index +#define duckdb_v2_table_function_partitioning_get_user_data \ + duckdb_ext_api.duckdb_v2_table_function_partitioning_get_user_data +#define duckdb_v2_table_function_partitioning_set_partition_info \ + duckdb_ext_api.duckdb_v2_table_function_partitioning_set_partition_info +#define duckdb_v2_table_function_set_partition_data_callback \ + duckdb_ext_api.duckdb_v2_table_function_set_partition_data_callback +#define duckdb_v2_table_function_set_partitioning_callback \ + duckdb_ext_api.duckdb_v2_table_function_set_partitioning_callback +#define duckdb_v2_validate_utf8 duckdb_ext_api.duckdb_v2_validate_utf8 +#define duckdb_v2_function_bind_get_arg_count duckdb_ext_api.duckdb_v2_function_bind_get_arg_count +#define duckdb_v2_function_bind_get_arg_index duckdb_ext_api.duckdb_v2_function_bind_get_arg_index +#define duckdb_v2_function_bind_get_arg_name duckdb_ext_api.duckdb_v2_function_bind_get_arg_name +#define duckdb_v2_function_bind_get_arg_type duckdb_ext_api.duckdb_v2_function_bind_get_arg_type +#define duckdb_v2_function_bind_get_arg_value duckdb_ext_api.duckdb_v2_function_bind_get_arg_value +#define duckdb_v2_function_bind_get_user_data duckdb_ext_api.duckdb_v2_function_bind_get_user_data +#define duckdb_v2_function_bind_set_bind_data duckdb_ext_api.duckdb_v2_function_bind_set_bind_data +#define duckdb_v2_table_function_bind_set_order_preservation \ + duckdb_ext_api.duckdb_v2_table_function_bind_set_order_preservation +#define duckdb_v2_arrow_result_destroy duckdb_ext_api.duckdb_v2_arrow_result_destroy +#define duckdb_v2_arrow_result_drain duckdb_ext_api.duckdb_v2_arrow_result_drain +#define duckdb_v2_arrow_result_fetch_array duckdb_ext_api.duckdb_v2_arrow_result_fetch_array +#define duckdb_v2_arrow_result_get_result_type duckdb_ext_api.duckdb_v2_arrow_result_get_result_type +#define duckdb_v2_arrow_result_get_schema duckdb_ext_api.duckdb_v2_arrow_result_get_schema +#define duckdb_v2_arrow_result_get_statement_type duckdb_ext_api.duckdb_v2_arrow_result_get_statement_type +#define duckdb_v2_arrow_result_step duckdb_ext_api.duckdb_v2_arrow_result_step +#define duckdb_v2_arrow_result_to_arrow_c_stream duckdb_ext_api.duckdb_v2_arrow_result_to_arrow_c_stream +#define duckdb_v2_arrow_result_wait duckdb_ext_api.duckdb_v2_arrow_result_wait +#define duckdb_v2_prepared_statement_execute_arrow duckdb_ext_api.duckdb_v2_prepared_statement_execute_arrow +#define duckdb_v2_statement_execute_arrow duckdb_ext_api.duckdb_v2_statement_execute_arrow +#define duckdb_v2_scalar_function_resolve_types_set_return_type \ + duckdb_ext_api.duckdb_v2_scalar_function_resolve_types_set_return_type +#define duckdb_v2_scalar_function_set_resolve_types_callback \ + duckdb_ext_api.duckdb_v2_scalar_function_set_resolve_types_callback +#define duckdb_v2_connection_get_factory duckdb_ext_api.duckdb_v2_connection_get_factory +#define duckdb_v2_context_get_factory duckdb_ext_api.duckdb_v2_context_get_factory +#define duckdb_v2_instance_get_factory duckdb_ext_api.duckdb_v2_instance_get_factory +#define duckdb_v2_scalar_function_bind_set_bind_data duckdb_ext_api.duckdb_v2_scalar_function_bind_set_bind_data +#define duckdb_v2_config_get_option_value duckdb_ext_api.duckdb_v2_config_get_option_value +#define duckdb_v2_config_set_option_text duckdb_ext_api.duckdb_v2_config_set_option_text +#define duckdb_v2_config_set_option_value duckdb_ext_api.duckdb_v2_config_set_option_value +#define duckdb_v2_connection_get_config duckdb_ext_api.duckdb_v2_connection_get_config +#define duckdb_v2_connection_get_file_system duckdb_ext_api.duckdb_v2_connection_get_file_system +#define duckdb_v2_connection_log duckdb_ext_api.duckdb_v2_connection_log +#define duckdb_v2_context_get_config duckdb_ext_api.duckdb_v2_context_get_config +#define duckdb_v2_instance_get_config duckdb_ext_api.duckdb_v2_instance_get_config +#define duckdb_v2_instance_get_file_system duckdb_ext_api.duckdb_v2_instance_get_file_system +#define duckdb_v2_instance_log duckdb_ext_api.duckdb_v2_instance_log +#define duckdb_v2_option_get_default_scope duckdb_ext_api.duckdb_v2_option_get_default_scope +#define duckdb_v2_option_supports_scope duckdb_ext_api.duckdb_v2_option_supports_scope +#define duckdb_v2_aggregate_function_get_docs duckdb_ext_api.duckdb_v2_aggregate_function_get_docs +#define duckdb_v2_connection_register_pragma_function duckdb_ext_api.duckdb_v2_connection_register_pragma_function +#define duckdb_v2_extension_register_pragma_function duckdb_ext_api.duckdb_v2_extension_register_pragma_function +#define duckdb_v2_function_docs_add_category duckdb_ext_api.duckdb_v2_function_docs_add_category +#define duckdb_v2_function_docs_add_example duckdb_ext_api.duckdb_v2_function_docs_add_example +#define duckdb_v2_function_docs_set_description duckdb_ext_api.duckdb_v2_function_docs_set_description +#define duckdb_v2_pragma_function_create duckdb_ext_api.duckdb_v2_pragma_function_create +#define duckdb_v2_pragma_function_destroy duckdb_ext_api.duckdb_v2_pragma_function_destroy +#define duckdb_v2_pragma_function_get_docs duckdb_ext_api.duckdb_v2_pragma_function_get_docs +#define duckdb_v2_pragma_function_get_signature duckdb_ext_api.duckdb_v2_pragma_function_get_signature +#define duckdb_v2_pragma_function_query_set_query duckdb_ext_api.duckdb_v2_pragma_function_query_set_query +#define duckdb_v2_pragma_function_set_exec_callback duckdb_ext_api.duckdb_v2_pragma_function_set_exec_callback +#define duckdb_v2_pragma_function_set_name duckdb_ext_api.duckdb_v2_pragma_function_set_name +#define duckdb_v2_pragma_function_set_query_callback duckdb_ext_api.duckdb_v2_pragma_function_set_query_callback +#define duckdb_v2_pragma_function_set_user_data duckdb_ext_api.duckdb_v2_pragma_function_set_user_data +#define duckdb_v2_scalar_function_get_docs duckdb_ext_api.duckdb_v2_scalar_function_get_docs +#define duckdb_v2_table_function_get_docs duckdb_ext_api.duckdb_v2_table_function_get_docs +#define duckdb_v2_connection_register_custom_option duckdb_ext_api.duckdb_v2_connection_register_custom_option +#define duckdb_v2_custom_option_create duckdb_ext_api.duckdb_v2_custom_option_create +#define duckdb_v2_custom_option_destroy duckdb_ext_api.duckdb_v2_custom_option_destroy +#define duckdb_v2_custom_option_set_default_scope duckdb_ext_api.duckdb_v2_custom_option_set_default_scope +#define duckdb_v2_custom_option_set_default_value duckdb_ext_api.duckdb_v2_custom_option_set_default_value +#define duckdb_v2_custom_option_set_description duckdb_ext_api.duckdb_v2_custom_option_set_description +#define duckdb_v2_custom_option_set_name duckdb_ext_api.duckdb_v2_custom_option_set_name +#define duckdb_v2_custom_option_set_supports_scope duckdb_ext_api.duckdb_v2_custom_option_set_supports_scope +#define duckdb_v2_custom_option_set_type duckdb_ext_api.duckdb_v2_custom_option_set_type +#define duckdb_v2_custom_option_set_update_callback duckdb_ext_api.duckdb_v2_custom_option_set_update_callback +#define duckdb_v2_custom_option_set_user_data duckdb_ext_api.duckdb_v2_custom_option_set_user_data +#define duckdb_v2_custom_option_update_get_scope duckdb_ext_api.duckdb_v2_custom_option_update_get_scope +#define duckdb_v2_custom_option_update_get_user_data duckdb_ext_api.duckdb_v2_custom_option_update_get_user_data +#define duckdb_v2_custom_option_update_get_value duckdb_ext_api.duckdb_v2_custom_option_update_get_value +#define duckdb_v2_custom_option_update_is_reset duckdb_ext_api.duckdb_v2_custom_option_update_is_reset +#define duckdb_v2_custom_option_update_set_value duckdb_ext_api.duckdb_v2_custom_option_update_set_value +#define duckdb_v2_extension_register_custom_option duckdb_ext_api.duckdb_v2_extension_register_custom_option +#define duckdb_v2_extension_register_log_sink duckdb_ext_api.duckdb_v2_extension_register_log_sink +#define duckdb_v2_extension_register_macro_function duckdb_ext_api.duckdb_v2_extension_register_macro_function +#define duckdb_v2_file_system_path_exists duckdb_ext_api.duckdb_v2_file_system_path_exists +#define duckdb_v2_file_system_path_expand duckdb_ext_api.duckdb_v2_file_system_path_expand +#define duckdb_v2_file_system_path_is_remote duckdb_ext_api.duckdb_v2_file_system_path_is_remote +#define duckdb_v2_instance_register_log_sink duckdb_ext_api.duckdb_v2_instance_register_log_sink +#define duckdb_v2_log_sink_create duckdb_ext_api.duckdb_v2_log_sink_create +#define duckdb_v2_log_sink_destroy duckdb_ext_api.duckdb_v2_log_sink_destroy +#define duckdb_v2_log_sink_set_flush_callback duckdb_ext_api.duckdb_v2_log_sink_set_flush_callback +#define duckdb_v2_log_sink_set_name duckdb_ext_api.duckdb_v2_log_sink_set_name +#define duckdb_v2_log_sink_set_user_data duckdb_ext_api.duckdb_v2_log_sink_set_user_data +#define duckdb_v2_log_sink_set_write_callback duckdb_ext_api.duckdb_v2_log_sink_set_write_callback +#define duckdb_v2_log_sink_write_info_get_connection_id duckdb_ext_api.duckdb_v2_log_sink_write_info_get_connection_id +#define duckdb_v2_log_sink_write_info_get_query_id duckdb_ext_api.duckdb_v2_log_sink_write_info_get_query_id +#define duckdb_v2_log_sink_write_info_get_thread_id duckdb_ext_api.duckdb_v2_log_sink_write_info_get_thread_id +#define duckdb_v2_log_sink_write_info_get_transaction_id duckdb_ext_api.duckdb_v2_log_sink_write_info_get_transaction_id +#define duckdb_v2_macro_function_create duckdb_ext_api.duckdb_v2_macro_function_create +#define duckdb_v2_macro_function_destroy duckdb_ext_api.duckdb_v2_macro_function_destroy +#define duckdb_v2_macro_function_get_docs duckdb_ext_api.duckdb_v2_macro_function_get_docs +#define duckdb_v2_macro_function_get_signature duckdb_ext_api.duckdb_v2_macro_function_get_signature +#define duckdb_v2_macro_function_set_body duckdb_ext_api.duckdb_v2_macro_function_set_body +#define duckdb_v2_macro_function_set_name duckdb_ext_api.duckdb_v2_macro_function_set_name +#define duckdb_v2_vector_cast duckdb_ext_api.duckdb_v2_vector_cast +#define duckdb_v2_copy_function_set_file_extension duckdb_ext_api.duckdb_v2_copy_function_set_file_extension +#define duckdb_v2_data_chunk_clear duckdb_ext_api.duckdb_v2_data_chunk_clear +#define duckdb_v2_file_system_path_glob duckdb_ext_api.duckdb_v2_file_system_path_glob +#define duckdb_v2_file_system_path_move duckdb_ext_api.duckdb_v2_file_system_path_move +#define duckdb_v2_file_system_path_remove duckdb_ext_api.duckdb_v2_file_system_path_remove +#define duckdb_v2_statement_add_collection duckdb_ext_api.duckdb_v2_statement_add_collection +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_connection_register_multi_file_function \ + duckdb_ext_api.duckdb_v2_connection_register_multi_file_function +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_copy_to_set_statistics_callback duckdb_ext_api.duckdb_v2_copy_to_set_statistics_callback +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_copy_to_statistics_get_bind_data duckdb_ext_api.duckdb_v2_copy_to_statistics_get_bind_data +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_copy_to_statistics_get_init_data duckdb_ext_api.duckdb_v2_copy_to_statistics_get_init_data +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_copy_to_statistics_get_user_data duckdb_ext_api.duckdb_v2_copy_to_statistics_get_user_data +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_copy_to_statistics_set_file_size duckdb_ext_api.duckdb_v2_copy_to_statistics_set_file_size +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_copy_to_statistics_set_row_count duckdb_ext_api.duckdb_v2_copy_to_statistics_set_row_count +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_data_chunk_get_capacity duckdb_ext_api.duckdb_v2_data_chunk_get_capacity +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_extension_register_multi_file_function duckdb_ext_api.duckdb_v2_extension_register_multi_file_function +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_multi_file_function_create duckdb_ext_api.duckdb_v2_multi_file_function_create +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_multi_file_function_destroy duckdb_ext_api.duckdb_v2_multi_file_function_destroy +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_multi_file_function_set_file_extension duckdb_ext_api.duckdb_v2_multi_file_function_set_file_extension +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_multi_file_function_set_name duckdb_ext_api.duckdb_v2_multi_file_function_set_name +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_multi_file_function_set_reader_type duckdb_ext_api.duckdb_v2_multi_file_function_set_reader_type +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_multi_file_function_set_single_file_function \ + duckdb_ext_api.duckdb_v2_multi_file_function_set_single_file_function +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_table_function_claim_batch_get_bind_data \ + duckdb_ext_api.duckdb_v2_table_function_claim_batch_get_bind_data +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_table_function_claim_batch_get_global_state \ + duckdb_ext_api.duckdb_v2_table_function_claim_batch_get_global_state +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_table_function_claim_batch_get_local_state \ + duckdb_ext_api.duckdb_v2_table_function_claim_batch_get_local_state +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_table_function_claim_batch_get_user_data \ + duckdb_ext_api.duckdb_v2_table_function_claim_batch_get_user_data +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_table_function_claim_batch_set_claimed duckdb_ext_api.duckdb_v2_table_function_claim_batch_set_claimed +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_table_function_set_claim_batch_callback \ + duckdb_ext_api.duckdb_v2_table_function_set_claim_batch_callback +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_table_function_get_bind_info_get_bind_data \ + duckdb_ext_api.duckdb_v2_table_function_get_bind_info_get_bind_data +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_table_function_get_bind_info_get_user_data \ + duckdb_ext_api.duckdb_v2_table_function_get_bind_info_get_user_data +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_table_function_get_bind_info_set_column_identifier \ + duckdb_ext_api.duckdb_v2_table_function_get_bind_info_set_column_identifier +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_table_function_get_bind_info_set_option \ + duckdb_ext_api.duckdb_v2_table_function_get_bind_info_set_option +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_table_function_set_get_bind_info_callback \ + duckdb_ext_api.duckdb_v2_table_function_set_get_bind_info_callback +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_factory_create_common_type duckdb_ext_api.duckdb_v2_factory_create_common_type +#endif // capigen:end appended #endif // DUCKDB_BUILD_STATIC_EXTENSION diff --git a/src/duckdb/src/include/duckdb_static_extension.h b/src/duckdb/src/include/duckdb_static_extension.h new file mode 100644 index 000000000..5715e8123 --- /dev/null +++ b/src/duckdb/src/include/duckdb_static_extension.h @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// +// DuckDB +// +// duckdb_static_extension.h +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include + +#ifndef DUCKDB_C_API +#ifdef _WIN32 +#ifdef DUCKDB_STATIC_BUILD +#define DUCKDB_C_API +#elif defined(DUCKDB_BUILD_LIBRARY) && !defined(DUCKDB_BUILD_LOADABLE_EXTENSION) +#define DUCKDB_C_API __declspec(dllexport) +#else +#define DUCKDB_C_API __declspec(dllimport) +#endif +#else +#if defined(__GNUC__) || defined(__clang__) +#define DUCKDB_C_API __attribute__((visibility("default"))) +#else +#define DUCKDB_C_API +#endif +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +//! Descriptor layout this header defines. +#define DUCKDB_EXTENSION_DESCRIPTOR_VERSION 2 + +typedef struct duckdb_extension_descriptor duckdb_extension_descriptor; + +//! Describes a statically linked extension, or something linked in the same way that is not an extension. DuckDB +//! allocates and zero-fills it, the describe function fills it in. +struct duckdb_extension_descriptor { + //! In: the layout DuckDB offers. Out: the layout the describe function filled, never higher than the offer. + uint32_t version; + + // Layout 1, set by DuckDB + //! Reports why the describe function refused. DuckDB copies the message. + void (*set_error)(duckdb_extension_descriptor *descriptor, const char *message); + //! DuckDB's state for set_error, opaque to the extension. + void *internal; + + // Layout 1, set by the extension; DuckDB copies the strings before the describe function's caller returns + const char *name; + const char *extension_version; + //! What the entry point was built against: the DuckDB version for entry_cpp, the C API version the extension + //! targets for entry_capi_v1 and entry_capi_v2. The same values the loadable extension metadata carries. DuckDB + //! records it and reports it; it does not refuse a mismatch on it yet. + const char *api_version; + //! void (duckdb::ExtensionLoader &) + void (*entry_cpp)(void); + //! bool (duckdb_extension_info, struct duckdb_extension_access *) + void (*entry_capi_v1)(void); + //! void (struct duckdb_v2_extension_input *) + void (*entry_capi_v2)(void); + + // Layout 2, set by the describe function + //! Set instead of an entry point by a capability, something linked like an extension that is not one (such as + //! httplib): DuckDB calls it for every database it opens, before loading extensions, and neither lists nor loads + //! it as an extension. + //! void (duckdb::DatabaseInstance &) + void (*database_callback)(void); +}; + +//! Every statically linkable extension provides duckdb_extension__describe with this signature. Returns 0 on +//! success. +typedef int32_t (*duckdb_extension_describe_t)(duckdb_extension_descriptor *descriptor); + +//! Calls describe and registers what it describes for every database opened afterwards. Returns 0 on +//! success. Registering the same describe function again is a no-op; a different one under a registered name is an +//! error. A failed registration also makes opening a database fail with the reason. +DUCKDB_C_API int32_t duckdb_register_static_extension(duckdb_extension_describe_t describe); + +#ifdef __cplusplus +} +#endif diff --git a/src/duckdb/src/include/duckdb_v2.h b/src/duckdb/src/include/duckdb_v2.h index 808a0e2eb..2cede3650 100644 --- a/src/duckdb/src/include/duckdb_v2.h +++ b/src/duckdb/src/include/duckdb_v2.h @@ -185,6 +185,31 @@ typedef struct _duckdb_extension_info *duckdb_v2_extension_handle; /* --- Enums for common --- */ +/*! + * The order guarantee a producer of rows makes about its output, which decides whether the rows it produces must be + * kept in order downstream. + */ +typedef enum DUCKDB_V2_ORDER_PRESERVATION { + /*! + * The rows have no meaningful order. The engine may reorder them freely, and consumers that otherwise keep + * insertion order, such as query results, INSERT and COPY, run in parallel without ordering them. + */ + DUCKDB_V2_ORDER_PRESERVATION_NO_ORDER = 0, + + /*! + * The rows are produced in insertion order, which the engine keeps unless the `preserve_insertion_order` setting is + * disabled. + */ + DUCKDB_V2_ORDER_PRESERVATION_INSERTION_ORDER = 1, + + /*! + * The rows are produced in an order that must be kept, as if sorted by an `ORDER BY`, even when the + * `preserve_insertion_order` setting is disabled. + */ + DUCKDB_V2_ORDER_PRESERVATION_FIXED_ORDER = 2, + DUCKDB_V2_ORDER_PRESERVATION_MAX_ENUM = 0x7FFFFFFF, +} DUCKDB_V2_ORDER_PRESERVATION; + /* --- Struct forward declarations for common --- */ typedef struct duckdb_v2_str duckdb_v2_str; @@ -196,36 +221,55 @@ typedef struct duckdb_v2_opaque duckdb_v2_opaque; /* --- Types for common --- */ /*! - * An opaque, owned handle to the V2 environment: the required root through which databases are opened. All databases - * under one environment share a cache, so file-level conflicts — the same database file opened twice — are detected - * across them. Destroying the environment refuses with ERROR_RESOURCE_IN_USE while any database opened through it is - * still alive; close those first. + * An opaque, owned handle to the V2 environment: the required root through which instance handles are created. All + * instances under one environment share an instance cache, so file-level conflicts — the same database file opened + * twice — are detected across them. Destroying the environment refuses with ERROR_RESOURCE_IN_USE while any instance + * created through it is still alive; destroy those first. */ typedef struct _duckdb_v2_environment { void *internal_ptr; } * duckdb_v2_environment_handle; -//! An opaque, owned handle to a DuckDB database instance. -typedef struct _duckdb_v2_database { +/*! + * An opaque, owned handle to a DuckDB database instance: a buffer pool, a scheduler, a set of GLOBAL settings, and the + * databases attached to it. Created empty by `duckdb_v2_instance_create()`; databases are attached with + * `duckdb_v2_instance_attach()`, made the default with `duckdb_v2_instance_set_default()`, and detached with + * `duckdb_v2_instance_detach()`. Always destroy via `duckdb_v2_instance_destroy()`. + */ +typedef struct _duckdb_v2_instance { void *internal_ptr; -} * duckdb_v2_database_handle; +} * duckdb_v2_instance_handle; -//! An opaque, owned handle to a DuckDB connection. +/*! + * An opaque, owned handle to a DuckDB connection: a session on a database instance with its own LOCAL settings and + * transaction state. Always destroy via `duckdb_v2_connection_destroy()`. + */ typedef struct _duckdb_v2_connection { void *internal_ptr; } * duckdb_v2_connection_handle; /*! - * An opaque, owned handle to a single config option: a name and a setting, plus optional metadata (description, default - * setting, target scope, aliases). You can create an with option_create and pass it when opening a database or - * connection, or you can retrieve one with metadata already filled in using option_get. The caller always destroys it - * via option_destroy, + * A borrowed handle to the scope config options are read from and written at. Taken from an instance + * (`duckdb_v2_instance_get_config()`), a connection (`duckdb_v2_connection_get_config()`) or a context + * (`duckdb_v2_context_get_config()`), and valid for as long as its source is; the caller never destroys it. An + * instance's config is the GLOBAL scope alone; a connection's or a context's config reads that client's cascade (its + * SESSION value, else the GLOBAL one, else the default) and writes at any scope the option supports. + */ +typedef struct _duckdb_v2_config { + void *internal_ptr; +} * duckdb_v2_config_handle; + +/*! + * An opaque, owned, read-only descriptor of a single config option as registered on an instance: its canonical name, + * default value, description, the scopes it may be written at, and aliases. It carries no current value: read that with + * the *_get_option_value functions, and write it with the *_set_option functions. Returned by the *_get_option_by_name + * and *_get_option_by_index functions; the caller always destroys it via `duckdb_v2_option_destroy()`. */ typedef struct _duckdb_v2_option { void *internal_ptr; } * duckdb_v2_option_handle; -//! Opaque detail for a failed call; destroy with error_info_destroy. +//! Opaque detail for a failed call; destroy with `duckdb_v2_error_info_destroy()`. typedef struct _duckdb_v2_error_info { void *internal_ptr; } * duckdb_v2_error_info_handle; @@ -234,17 +278,18 @@ typedef struct _duckdb_v2_error_info { * An opaque, owned handle to a logical type. Carries a type id plus any kind-specific metadata: decimal width and * scale, enum dictionary, list / array / struct / map / union child types, alias. A type is read-only once constructed; * instances arrive from a query result schema, or from one of the create_type_* constructors. Always destroy via - * logical_type_destroy. Borrowed strings returned by the getters are valid only until then. + * `duckdb_v2_logical_type_destroy()`. Borrowed strings returned by the getters are valid only until then. */ typedef struct _duckdb_v2_logical_type { void *internal_ptr; } * duckdb_v2_logical_type_handle; /*! - * An opaque, owned handle to a single SQL value: a logical type plus a typed payload. Always destroy via value_destroy. - * The string returned by value_get_varchar / value_get_blob is borrowed and valid only until then. That holds for - * BIGNUM too, where what is borrowed is the opaque storage form; bignum_decode translates it into a magnitude and a - * sign flag, writing into a buffer the caller supplies. + * An opaque, owned handle to a single SQL value: a logical type plus a typed payload. Always destroy via + * `duckdb_v2_value_destroy()`. The string returned by `duckdb_v2_value_get_varchar()` / `duckdb_v2_value_get_blob()` is + * borrowed and valid only until then. That holds for BIGNUM too, where what is borrowed is the opaque storage form; + * `duckdb_v2_bignum_decode()` translates it into a magnitude and a sign flag, writing into a buffer the caller + * supplies. */ typedef struct _duckdb_v2_value { void *internal_ptr; @@ -252,14 +297,15 @@ typedef struct _duckdb_v2_value { /*! * An opaque, owned handle to the streaming result of a query. Carries the schema (column names and logical types), the - * statement type, and the result type from prepare time; row data is produced incrementally by stepping (result_step) - * or draining (result_fetch_chunk). Single consumer: step from one thread at a time. + * statement type, and the result type from prepare time; row data is produced incrementally by stepping + * (`duckdb_v2_result_step()`) or draining (`duckdb_v2_result_fetch_chunk()`). Single consumer: step from one thread at + * a time. * - * A result is a cursor on the connection's execution, not a box of data. While it is live — not finished, cancelled, - * errored, or destroyed — the connection refuses new queries with ERROR_RESOURCE_IN_USE, and the query's transaction - * stays open, deferring version cleanup and checkpointing, so drain or destroy it promptly. Side-effecting statements - * (PRAGMA, ALTER, ...) take effect only once the result is drained. Always destroy via result_destroy, which is safe - * even on a partially consumed stream. + * A result is a cursor on the connection's execution, not a box of data. While its statement runs — at most until it is + * finished, cancelled, errored, or destroyed — the connection refuses new queries with ERROR_RESOURCE_IN_USE, and the + * query's transaction stays open, deferring version cleanup and checkpointing, so drain or destroy it promptly. + * Side-effecting statements (PRAGMA, ALTER, ...) take effect only once the result is drained. Always destroy via + * `duckdb_v2_result_destroy()`, which is safe even on a partially consumed stream. */ typedef struct _duckdb_v2_result { void *internal_ptr; @@ -286,9 +332,9 @@ typedef struct _duckdb_v2_vector { /*! * A borrowed handle to a "arena allocator". * - * Bytes handed out by arena_allocate share the same lifetime as the arena itself. vector_get_arena yields the arena - * backing the out-of-line bytes of non-inlined VARCHAR / BLOB / BIT / BIGNUM values, valid until the owning vector is - * flattened, reallocated, or destroyed. + * Bytes handed out by `duckdb_v2_arena_allocate()` share the same lifetime as the arena itself. + * `duckdb_v2_vector_get_arena()` yields the arena backing the out-of-line bytes of non-inlined VARCHAR / BLOB / BIT / + * BIGNUM values, valid until the owning vector is flattened, reallocated, or destroyed. */ typedef struct _duckdb_v2_arena { void *internal_ptr; @@ -297,15 +343,26 @@ typedef struct _duckdb_v2_arena { /*! * A borrowed handle to a client context: a connection seen from inside DuckDB. Handed out within DuckDB-managed scopes * — function bind / init / exec callbacks, replacement scans, and the extension entrypoint — and valid only for the - * duration of that scope; the caller never destroys it. A context is the scope for reading (settings, the file system) - * and for constructing values and types, not for registration: catalog entries and database-level hooks are installed - * through an extension or a connection. A transaction is always active while a context is handed out, which is what the - * context-scoped constructors assume. + * duration of that scope; the caller never destroys it. Within that scope a transaction is already active. A context + * reaches the same capabilities a connection does, through `duckdb_v2_context_get_factory()`, + * `duckdb_v2_context_get_config()`, `duckdb_v2_context_get_file_system()` and `duckdb_v2_context_log()`. */ typedef struct _duckdb_v2_context { void *internal_ptr; } * duckdb_v2_context_handle; +/*! + * A borrowed handle to the scope values, types, data chunks and column data collections are created in. Taken from an + * instance (`duckdb_v2_instance_get_factory()`), a connection (`duckdb_v2_connection_get_factory()`) or a context + * (`duckdb_v2_context_get_factory()`), and valid for as long as its source is; the caller never destroys it. A factory + * resolves in the scope of its source: a connection's or a context's factory reaches the catalog and the client's + * settings, while an instance's factory knows only the built-in types and casts, with default settings, and refuses + * anything that needs a catalog. + */ +typedef struct _duckdb_v2_factory { + void *internal_ptr; +} * duckdb_v2_factory_handle; + /*! * An entry in a selection-vector. * @@ -315,7 +372,7 @@ typedef struct _duckdb_v2_context { */ typedef uint32_t duckdb_v2_sel_t; -//! VARCHAR storage. Read the transparent bytes fields directly. +//! VARCHAR storage. The bytes must contain valid UTF-8. Read the transparent bytes fields directly. typedef duckdb_v2_bytes duckdb_v2_varchar_t; //! BLOB storage. Read the transparent bytes fields directly. @@ -324,7 +381,7 @@ typedef duckdb_v2_bytes duckdb_v2_blob_t; //! BIT storage. Byte 0 is the padding-bit count, bytes 1.. the data. typedef duckdb_v2_bytes duckdb_v2_bit_t; -//! BIGNUM storage. Decode via bignum_decode. +//! BIGNUM storage. Decode via `duckdb_v2_bignum_decode()`. typedef duckdb_v2_bytes duckdb_v2_bignum_t; /*! @@ -332,6 +389,8 @@ typedef duckdb_v2_bytes duckdb_v2_bignum_t; * case-insensitively. Compare two identifiers case-insensitively rather than byte for byte, and render one into SQL * through the identifier-quoting entry point rather than embedding it raw. The catalog preserves casing; some * registries (config settings) canonicalize to lowercase. + * + * An identifier passed into the API must be valid UTF-8; otherwise the call fails with `ERROR_INPUT_INVALID`. */ typedef duckdb_v2_str duckdb_v2_identifier_t; @@ -347,16 +406,16 @@ typedef duckdb_v2_str duckdb_v2_identifier_t; * * Invoked exactly once per producing call, with the complete text in a single view. * - * The view is borrowed for the duration of the call only. Copy what you need before returning, and do not retain - * `text.ptr`. The bytes are NOT guaranteed to be null-terminated. + * The view is borrowed for the duration of the call only. Copy what you need before returning, and do not retain `text` + * or `text->ptr`. The bytes are NOT guaranteed to be null-terminated. * - * `err` is a live error slot, never NULL. Populate it with error_info_set_code / error_info_set_text to signal failure - * to DuckDB. Do not destroy it yourself. + * `err` is a live error slot, never NULL. Populate it with `duckdb_v2_error_info_set_code()` / + * `duckdb_v2_error_info_set_text()` to signal failure to DuckDB. Do not destroy it yourself. * * The sink runs inside DuckDB's call frame: it must not throw or unwind across the boundary, and it must not re-enter * the API on the handle being operated on. */ -typedef void (*duckdb_v2_text_sink_fn)(duckdb_v2_str text, void *user_data, duckdb_v2_error_info_handle *err); +typedef void (*duckdb_v2_text_sink_fn)(const duckdb_v2_str *text, void *user_data, duckdb_v2_error_info_handle *err); /*! * Compares two caller-defined resources for equality. @@ -391,6 +450,12 @@ typedef void (*duckdb_v2_opaque_destroy_fn)(void *data); * "valid until the owning handle is destroyed"). `{NULL, 0}` is the canonical empty view, and `ptr` must not be * dereferenced when `len` is 0. Not to be confused with `bytes`, the transparent 16-byte *storage* format for a * variable-size value in a vector. + * + * Functions take an input view as a `const str *`, which must not be NULL; a NULL pointer, or a view whose `ptr` is + * NULL while `len` is nonzero, fails with ERROR_INPUT_INVALID. Point at a `{NULL, 0}` view to pass an empty string. + * + * Text inputs, such as VARCHAR values and names, must contain valid UTF-8. The caller is responsible for ensuring this; + * API functions do not necessarily validate the input. Binary inputs, such as BLOB values, do not require valid UTF-8. */ struct duckdb_v2_str { const char *ptr; @@ -403,7 +468,7 @@ struct duckdb_v2_str { * value.pointer.prefix repeats the first * 4. The union is transparent, so read the fields directly. VARCHAR and * BLOB carry no further encoding; BIT and BIGNUM do. For BIT, byte 0 is the padding-bit count and bytes 1.. are the - * data; BIGNUM is decoded via bignum_decode. + * data; BIGNUM is decoded via `duckdb_v2_bignum_decode()`. */ struct duckdb_v2_bytes { union { @@ -473,6 +538,12 @@ typedef enum DUCKDB_V2_ERROR { //! An object exceeded its maximum permitted size. DUCKDB_V2_ERROR_INPUT_OBJECT_SIZE = 2004, + /*! + * The metadata asked for is known only once execution binds the statement that produces it, because the statement + * expands into several. Step the result and ask again. + */ + DUCKDB_V2_ERROR_INPUT_METADATA_DEFERRED = 2005, + //! The requested resource is already in use. DUCKDB_V2_ERROR_RESOURCE_IN_USE = 3001, @@ -763,7 +834,7 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arena_allocate(duckdb_v2_arena_handle are * The mode a cast is being executed in. A normal cast must either succeed for every row or report an error, which * aborts the query. A "try" cast (SQL TRY_CAST, and implicit casts the engine probes speculatively) tolerates per-row * failures: the callback writes NULL for the rows it could not convert instead of aborting. Read with - * `duckdb_v2_cast_function_exec_get_mode()`. + * `duckdb_v2_cast_function_exec_get_mode()`, and passed to `duckdb_v2_vector_cast()`. */ typedef enum DUCKDB_V2_CAST_MODE { //! A regular cast. A conversion failure reported through the error slot aborts the query. @@ -782,11 +853,11 @@ typedef enum DUCKDB_V2_CAST_MODE { /* --- Types for cast --- */ /*! - * An owned opaque handle to a cast function being built. Created with - * `duckdb_v2_cast_function_create_with_connection()` or `duckdb_v2_cast_function_create_with_extension()`, configured + * An owned opaque handle to a cast function being built. Created with `duckdb_v2_cast_function_create()`, configured * with the setter functions (e.g. `duckdb_v2_cast_function_set_source_type()`, - * `duckdb_v2_cast_function_set_exec_callback()`, etc.), made available with `duckdb_v2_cast_function_register()`, and - * destroyed with `duckdb_v2_cast_function_destroy()`. + * `duckdb_v2_cast_function_set_exec_callback()`, etc.), made available with + * `duckdb_v2_connection_register_cast_function()` or `duckdb_v2_extension_register_cast_function()`, and destroyed with + * `duckdb_v2_cast_function_destroy()`. */ typedef struct _duckdb_v2_cast_function { void *internal_ptr; @@ -812,47 +883,28 @@ typedef void (*duckdb_v2_cast_function_exec_callback_fn)(duckdb_v2_cast_function /* --- Functions for cast --- */ /*! - * Creates a new cast function that will be registered on the connection's database. + * Creates a new cast function through a factory. * * The function starts out empty: configure it with the setter functions (e.g. * `duckdb_v2_cast_function_set_source_type()`, `duckdb_v2_cast_function_set_exec_callback()`, etc.), then make it - * available with `duckdb_v2_cast_function_register()`. The caller owns the returned handle and must destroy it with - * `duckdb_v2_cast_function_destroy()`, also after registration. - * - * history: - * - stable: v2.0.0 - * - * @param connection The connection to create the function in. - * @param function On success, receives the newly created cast function. Owned by the caller. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_cast_function_create_with_connection(duckdb_v2_connection_handle connection, - duckdb_v2_cast_function_handle *function, - duckdb_v2_error_info_handle *err); - -/*! - * Creates a new cast function that will be registered on the loading extension's database. + * available with `duckdb_v2_connection_register_cast_function()` or `duckdb_v2_extension_register_cast_function()`. The + * caller owns the returned handle and must destroy it with `duckdb_v2_cast_function_destroy()`, also after + * registration. * - * Use this from an extension load callback, where an extension handle is available. The function starts out empty: - * configure it with the setter functions (e.g. `duckdb_v2_cast_function_set_source_type()`, - * `duckdb_v2_cast_function_set_exec_callback()`, etc.), then make it available with - * `duckdb_v2_cast_function_register()`. The caller owns the returned handle and must destroy it with - * `duckdb_v2_cast_function_destroy()`, also after registration. + * The cast function can only be registered on the database the factory belongs to. * * history: * - stable: v2.0.0 * - * @param extension The extension to create the function in. + * @param factory The factory to create the cast function through. * @param function On success, receives the newly created cast function. Owned by the caller. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_cast_function_create_with_extension(duckdb_v2_extension_handle extension, - duckdb_v2_cast_function_handle *function, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_cast_function_create(duckdb_v2_factory_handle factory, + duckdb_v2_cast_function_handle *function, + duckdb_v2_error_info_handle *err); /*! * Sets the type the cast converts from. @@ -1047,22 +1099,47 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_cast_function_exec_get_mode(duckdb_v2_cas /*! * Registers the cast function, making it available to CAST and TRY_CAST. * - * The function is registered on the target given at creation: the connection's database or the loading extension. - * Registration requires a source type, a target type and an exec callback; both types must be fully defined concrete - * types. Registering a cast for a pair that already has one replaces it. The caller still owns the handle after - * registration and must destroy it with `duckdb_v2_cast_function_destroy()`, which does not affect the registered - * function. + * The cast is registered on the connection's database and takes effect immediately for every connection; it is not part + * of the connection's transaction, so a rollback does not undo it. Registration requires a source type, a target type + * and an exec callback; both types must be fully defined concrete types. Registering a cast for a pair that already has + * one replaces it. The cast function must have been created through a factory of the same database. The caller still + * owns the handle after registration and must destroy it with `duckdb_v2_cast_function_destroy()`, which does not + * affect the registered function. * * history: * - stable: v2.0.0 * - * @param function The function to register. + * @param conn The connection to register on. + * @param function The cast function to register. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_cast_function_register(duckdb_v2_cast_function_handle function, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_register_cast_function(duckdb_v2_connection_handle conn, + duckdb_v2_cast_function_handle function, + duckdb_v2_error_info_handle *err); + +/*! + * Registers the cast function, making it available to CAST and TRY_CAST. + * + * The cast is registered on the loading extension's database. Use this from the extension's load callback. Registration + * requires a source type, a target type and an exec callback; both types must be fully defined concrete types. + * Registering a cast for a pair that already has one replaces it. The cast function must have been created through a + * factory of the same database. The caller still owns the handle after registration and must destroy it with + * `duckdb_v2_cast_function_destroy()`, which does not affect the registered function. + * + * history: + * - stable: v2.0.0 + * + * @param extension The loading extension. + * @param function The cast function to register. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_extension_register_cast_function(duckdb_v2_extension_handle extension, + duckdb_v2_cast_function_handle function, + duckdb_v2_error_info_handle *err); /*! * Destroys the cast function, releasing its resources. @@ -1138,40 +1215,16 @@ typedef struct _duckdb_v2_column_data_collection_append_state { /* --- Functions for column_data_collection --- */ /*! - * Creates an empty column data collection from a connection. - * - * Allocates a new, empty column data collection, drawing its buffer allocator from the connection's context. The - * collection starts with no chunks and is ready to have chunks appended to it. A collection must have at least one - * column; an empty types array is rejected with INVALID_INPUT. - * - * history: - * - stable: v2.0.0 - * - * @param conn The connection whose context supplies the collection's allocator. - * @param types_array Pointer to an array of logical_type handles, one per column. This defines the schema for the - * chunks that will be stored in the collection. All chunks appended to this collection must have vectors that conform - * to these types. - * @param types_count Number of elements in the types_array. - * @param out_collection Receives the new collection handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_column_data_collection_create_with_connection( - duckdb_v2_connection_handle conn, const duckdb_v2_logical_type_handle *types_array, idx_t types_count, - duckdb_v2_column_data_collection_handle *out_collection, duckdb_v2_error_info_handle *err); - -/*! - * Creates an empty column data collection from a context. + * Creates an empty column data collection from a factory. * - * Allocates a new, empty column data collection, drawing its buffer allocator from the context. Use this from inside a - * callback or extension where a context is already in hand. The collection starts with no chunks and is ready to have - * chunks appended to it. A collection must have at least one column; an empty types array is rejected with - * INVALID_INPUT. + * Allocates a new, empty column data collection, drawing its buffers from the factory's database. The collection starts + * with no chunks and is ready to have chunks appended to it. A collection must have at least one column; an empty types + * array is rejected with INVALID_INPUT. * * history: * - stable: v2.0.0 * - * @param context The context whose allocator will be used for the collection. + * @param factory The factory to create with. * @param types_array Pointer to an array of logical_type handles, one per column. This defines the schema for the * chunks that will be stored in the collection. All chunks appended to this collection must have vectors that conform * to these types. @@ -1180,8 +1233,8 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_column_data_collection_create_with_connec * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_column_data_collection_create_with_context( - duckdb_v2_context_handle context, const duckdb_v2_logical_type_handle *types_array, idx_t types_count, +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_column_data_collection_create( + duckdb_v2_factory_handle factory, const duckdb_v2_logical_type_handle *types_array, idx_t types_count, duckdb_v2_column_data_collection_handle *out_collection, duckdb_v2_error_info_handle *err); /*! @@ -1239,8 +1292,8 @@ duckdb_v2_column_data_collection_destroy(duckdb_v2_column_data_collection_handle * * Transfers all chunks from the source collection to the target collection. This destroys the source collection, * setting the source handle to NULL. The two collections must have the same column types, and must have been created - * from the same context (or connections to the same database): the transferred chunks keep their original buffers, so - * combining collections from different databases would tie the target to a buffer manager it does not own. + * from factories of the same database: the transferred chunks keep their original buffers, so combining collections + * from different databases would tie the target to a buffer manager it does not own. * * history: * - stable: v2.0.0 @@ -1309,8 +1362,9 @@ duckdb_v2_column_data_collection_append_state_destroy(duckdb_v2_column_data_coll * Appends a data chunk to the collection. * * Appends a copy of the given chunk to the end of the collection. The chunk's column count and types must equal the - * collection's exactly; a mismatch is rejected with INVALID_INPUT before anything is copied. Complex-typed vectors may - * be flattened in place by the copy. + * collection's exactly; a mismatch is rejected with INVALID_INPUT before anything is copied. VARCHAR values must + * already contain valid UTF-8; this function does not validate text. Complex-typed vectors may be flattened in place by + * the copy. * * history: * - stable: v2.0.0 @@ -1432,42 +1486,21 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_column_data_collection_scan( /* --- Enums for configuration --- */ /*! - * The scope target of an option: where DuckDB permits its setting to be written. UNKNOWN is reported for an option - * whose declaration carries no explicit scope target, and for one created via option_create that has not yet been - * resolved through a database/connection get. - */ -typedef enum DUCKDB_V2_OPTION_TARGET_SCOPE { - //! Target scope is not known. - DUCKDB_V2_OPTION_TARGET_SCOPE_UNKNOWN = 0, - - //! May only be written at GLOBAL (database) scope. - DUCKDB_V2_OPTION_TARGET_SCOPE_GLOBAL_ONLY = 1, - - //! May only be written at LOCAL (session) scope. - DUCKDB_V2_OPTION_TARGET_SCOPE_LOCAL_ONLY = 2, - - //! May be written at either scope; defaults to GLOBAL when unspecified. - DUCKDB_V2_OPTION_TARGET_SCOPE_GLOBAL_DEFAULT = 3, - - //! May be written at either scope; defaults to LOCAL when unspecified. - DUCKDB_V2_OPTION_TARGET_SCOPE_LOCAL_DEFAULT = 4, - DUCKDB_V2_OPTION_TARGET_SCOPE_MAX_ENUM = 0x7FFFFFFF, -} DUCKDB_V2_OPTION_TARGET_SCOPE; - -/*! - * Destination scope for a connection-side option write, as taken by connection_option_set. AUTOMATIC defers to the - * option's target scope, like SQL `SET name = value`. GLOBAL writes through to the database, visible to all - * connections, like `SET GLOBAL`. LOCAL writes to the connection's session only, like `SET LOCAL` / `SET SESSION`. + * The scope of an option's value. Writes take DEFAULT, GLOBAL or SESSION: DEFAULT writes to the option's default scope + * (`duckdb_v2_option_get_default_scope()`), like SQL `SET name = value`; GLOBAL writes through to the instance, visible + * to all connections, like `SET GLOBAL`; SESSION writes to one connection's session only, like `SET SESSION`. Reads + * report GLOBAL or SESSION, as DuckDB attributes the value: an option no scope sets reads as GLOBAL, its default, and a + * legacy option read through a connection or context reads as SESSION. */ typedef enum DUCKDB_V2_SETTING_SCOPE { - //! Resolve from the option's target scope. - DUCKDB_V2_SETTING_SCOPE_AUTOMATIC = 0, + //! Write only: the option's default scope (`duckdb_v2_option_get_default_scope()`), not its default value. + DUCKDB_V2_SETTING_SCOPE_DEFAULT = 0, - //! Write through to the database (visible to all connections). + //! The instance's value, visible to all connections. DUCKDB_V2_SETTING_SCOPE_GLOBAL = 1, - //! Write to the connection's session only. - DUCKDB_V2_SETTING_SCOPE_LOCAL = 2, + //! A connection's session value. + DUCKDB_V2_SETTING_SCOPE_SESSION = 2, DUCKDB_V2_SETTING_SCOPE_MAX_ENUM = 0x7FFFFFFF, } DUCKDB_V2_SETTING_SCOPE; @@ -1482,647 +1515,749 @@ typedef enum DUCKDB_V2_SETTING_SCOPE { /* --- Functions for configuration --- */ /*! - * Creates an option handle carrying a name and a setting. + * Borrows the config of an instance: the GLOBAL scope. * - * The name and setting are copied in. The remaining fields — description, default setting, target scope, aliases — stay - * empty, or UNKNOWN, until the option is resolved through a database/connection get. The caller destroys it via - * option_destroy. + * Reads report GLOBAL, and writes are `SET GLOBAL`. Writing at SESSION scope returns ERROR_INPUT_INVALID: an instance + * has no session. Options that can only be chosen at startup are passed to `duckdb_v2_instance_create_with_options()`. + * The returned handle is borrowed: it is valid for as long as the instance is, and must not be destroyed. * * history: * - stable: v2.0.0 * - * @param name Option name. - * @param setting Setting (string-encoded value). - * @param out_option Receives the new option handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param instance The instance. + * @param out_config Receives the borrowed config. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_option_create(duckdb_v2_identifier_t name, duckdb_v2_str setting, - duckdb_v2_option_handle *out_option, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_instance_get_config(duckdb_v2_instance_handle instance, + duckdb_v2_config_handle *out_config, + duckdb_v2_error_info_handle *err); /*! - * Destroys an option handle. + * Borrows the config of a connection. * - * Frees the handle along with the strings it owns. On success the slot is set to null. Safe to call on an already-null - * slot. + * Reads follow the connection's cascade: its SESSION value if it set one, otherwise the GLOBAL value, otherwise the + * default. Writes accept any scope the option supports. The returned handle is borrowed: it is valid for as long as the + * connection is, and must not be destroyed. * * history: * - stable: v2.0.0 * - * @param option The option handle to destroy. + * @param conn The connection. + * @param out_config Receives the borrowed config. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_option_destroy(duckdb_v2_option_handle *option); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_get_config(duckdb_v2_connection_handle conn, + duckdb_v2_config_handle *out_config, + duckdb_v2_error_info_handle *err); /*! - * Borrows the option's canonical name. + * Borrows the config of a context. * - * When a database/connection get resolved the option from an alias, this still reports the canonical name; the alias is - * reachable via option_get_alias. + * The same scope `duckdb_v2_connection_get_config()` gives for the context's connection. The returned handle is + * borrowed: it is valid for as long as the context is, and must not be destroyed. * * history: * - stable: v2.0.0 * - * @param option The option. - * @param out_name Receives a borrowed view of the option name. Valid until the option is destroyed. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param ctx The context. + * @param out_config Receives the borrowed config. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_option_get_name(duckdb_v2_option_handle option, duckdb_v2_identifier_t *out_name, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_context_get_config(duckdb_v2_context_handle ctx, + duckdb_v2_config_handle *out_config, + duckdb_v2_error_info_handle *err); /*! - * Borrows the option's current setting (its string-encoded value). + * Reads the effective value of an option in this config, and the scope it came from. * - * For an option created via option_create this is the caller-supplied setting verbatim. For one resolved through a - * database/connection get it is the effective setting at that source's scope. + * Aliases resolve transparently, and an unknown name returns ERROR_INPUT_INVALID. The caller destroys the returned + * value. * * history: * - stable: v2.0.0 * - * @param option The option. - * @param out_setting Receives a borrowed view of the setting. Valid until the option is destroyed. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param config The config to read from. + * @param name Option name (canonical or alias). + * @param out_value Receives the value, typed as the option. Owned by the caller. + * @param out_scope Receives the scope the value came from. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_option_get_setting(duckdb_v2_option_handle option, duckdb_v2_str *out_setting, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_config_get_option_value(duckdb_v2_config_handle config, + const duckdb_v2_identifier_t *name, + duckdb_v2_value_handle *out_value, + DUCKDB_V2_SETTING_SCOPE *out_scope, + duckdb_v2_error_info_handle *err); /*! - * Borrows the option's static default setting. + * Writes an option in this config. * - * The empty view for an option created via option_create, until it has been resolved through a database/connection get. + * `scope` chooses the destination, mirroring SQL: + * - DEFAULT writes to the option's default scope, like a bare `SET name = value`. + * - GLOBAL writes through to the instance, visible to all connections, like `SET GLOBAL`. + * - SESSION writes to one connection's session only, like `SET SESSION`. + * A scope neither the option (`duckdb_v2_option_supports_scope()`) nor the config supports returns ERROR_INPUT_INVALID; + * an instance's config supports only GLOBAL. The value is cast to the option's type as SQL `SET` does. An unknown name + * is rejected unless an extension that defines it can be autoloaded. An option that can only be chosen at startup is + * rejected: pass it to `duckdb_v2_instance_create_with_options()`. * * history: * - stable: v2.0.0 * - * @param option The option. - * @param out_default_setting Receives a borrowed view of the default setting. Valid until the option is destroyed. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param config The config to write to. + * @param name Option name (canonical or alias). + * @param value The value, cast to the option's type as SQL `SET` does. Borrowed. + * @param scope Target scope: DEFAULT, GLOBAL, or SESSION. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_option_get_default_setting(duckdb_v2_option_handle option, - duckdb_v2_str *out_default_setting, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_config_set_option_value(duckdb_v2_config_handle config, + const duckdb_v2_identifier_t *name, + duckdb_v2_value_handle value, + DUCKDB_V2_SETTING_SCOPE scope, + duckdb_v2_error_info_handle *err); /*! - * Borrows the option's human-readable description. + * Writes an option in this config from text, the form SQL `SET` accepts. * - * The empty view for an option created via option_create, until it has been resolved through a database/connection get. + * `duckdb_v2_config_set_option_value()` with the text as a VARCHAR value: the engine casts it to the option's type, so + * this is the one-call form for a setting spelled the way SQL spells it, e.g. "1GB" for memory_limit. Scope handling + * and errors are `duckdb_v2_config_set_option_value()`'s. * * history: * - stable: v2.0.0 * - * @param option The option. - * @param out_description Receives a borrowed view of the description. Valid until the option is destroyed. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param config The config to write to. + * @param name Option name (canonical or alias). + * @param setting The setting, in the textual form SQL `SET` accepts. + * @param scope Target scope: DEFAULT, GLOBAL, or SESSION. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_option_get_description(duckdb_v2_option_handle option, - duckdb_v2_str *out_description, +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_config_set_option_text(duckdb_v2_config_handle config, + const duckdb_v2_identifier_t *name, + const duckdb_v2_str *setting, + DUCKDB_V2_SETTING_SCOPE scope, duckdb_v2_error_info_handle *err); /*! - * Returns the option's target scope. + * Describes a config option registered on this config's instance, by name. * - * OPTION_TARGET_SCOPE_UNKNOWN for an option created via option_create until it has been resolved through a - * database/connection get, and for one whose declaration carries no explicit scope target. + * Allocates an option descriptor: canonical name, default value, description, supported and default scopes, and + * aliases. Aliases resolve transparently — passing an alias returns the canonical option, with the alias listed in its + * alias array. An unknown name returns ERROR_INPUT_INVALID, which includes an option of an extension that has not + * loaded yet. The caller destroys the returned option. * * history: * - stable: v2.0.0 * - * @param option The option. - * @param out_target_scope Receives the target scope. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param config The config whose instance registered the options. + * @param name Option name (canonical or alias). + * @param out_option Receives the option descriptor. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_option_get_target_scope(duckdb_v2_option_handle option, - DUCKDB_V2_OPTION_TARGET_SCOPE *out_target_scope, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_config_get_option_by_name(duckdb_v2_config_handle config, + const duckdb_v2_identifier_t *name, + duckdb_v2_option_handle *out_option, + duckdb_v2_error_info_handle *err); /*! - * Returns the number of aliases registered for this option. + * Returns the number of config options registered on this config's instance. * - * Zero for an option created via option_create, until it has been resolved through a database/connection get. + * Counts core options plus the extension options registered on this instance. Aliases are NOT counted separately; each + * one is reachable through its canonical option's alias array. * * history: * - stable: v2.0.0 * - * @param option The option. - * @param out_count Receives the alias count. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param config The config whose instance registered the options. + * @param out_count Receives the option count. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_option_get_alias_count(duckdb_v2_option_handle option, idx_t *out_count, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_config_get_option_count(duckdb_v2_config_handle config, idx_t *out_count, + duckdb_v2_error_info_handle *err); /*! - * Borrows the alias name at the given index. + * Describes the config option at the given index on this config's instance. * - * An out-of-range index returns ERROR_INPUT_INVALID. + * Index space: [0, core_count) addresses core options, [core_count, total) extension options. The mapping is stable for + * as long as no extension registers new options. An out-of-range index returns ERROR_INPUT_INVALID. The caller destroys + * the returned option. * * history: * - stable: v2.0.0 * - * @param option The option. - * @param index The alias index, in [0, alias_count). - * @param out_alias Receives a borrowed view of the alias. Valid until the option is destroyed. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param config The config whose instance registered the options. + * @param index The option index, in [0, option_count). + * @param out_option Receives the option descriptor. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_option_get_alias(duckdb_v2_option_handle option, idx_t index, - duckdb_v2_identifier_t *out_alias, - duckdb_v2_error_info_handle *err); - -/* --- Struct definitions for configuration --- */ - -/* ============================================================================ - * MODULE: custom type - * ============================================================================ */ - -/* --- Enums for custom type --- */ - -/* --- Struct forward declarations for custom type --- */ - -/* --- Types for custom type --- */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_config_get_option_by_index(duckdb_v2_config_handle config, idx_t index, + duckdb_v2_option_handle *out_option, + duckdb_v2_error_info_handle *err); /*! - * An owned opaque handle to a custom type being built. Created with `duckdb_v2_custom_type_create_with_connection()` or - * `duckdb_v2_custom_type_create_with_extension()`, configured with `duckdb_v2_custom_type_set_name()` and - * `duckdb_v2_custom_type_set_base_type()`, made available with `duckdb_v2_custom_type_register()`, and destroyed with - * `duckdb_v2_custom_type_destroy()`. + * Destroys an option descriptor. + * + * Frees the handle along with the strings it owns. On success the slot is set to null. Safe to call on an already-null + * slot. + * + * history: + * - stable: v2.0.0 + * + * @param option The option handle to destroy. + * @return DUCKDB_V2_ERROR */ -typedef struct _duckdb_v2_custom_type { - void *internal_ptr; -} * duckdb_v2_custom_type_handle; - -/* --- Constants for custom type --- */ - -/* --- Function pointer typedefs for custom type --- */ - -/* --- Functions for custom type --- */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_option_destroy(duckdb_v2_option_handle *option); /*! - * Creates a new custom type that will be registered on the connection's database. + * Borrows the option's canonical name. * - * The type starts out empty: configure it with `duckdb_v2_custom_type_set_name()` and - * `duckdb_v2_custom_type_set_base_type()`, then make it available with `duckdb_v2_custom_type_register()`. The caller - * owns the returned handle and must destroy it with `duckdb_v2_custom_type_destroy()`, also after registration. + * When the option was resolved from an alias, this still reports the canonical name; the alias is reachable via + * `duckdb_v2_option_get_alias()`. * * history: * - stable: v2.0.0 * - * @param connection The connection to create the type in. - * @param type On success, receives the newly created custom type. Owned by the caller. + * @param option The option. + * @param out_name Receives a borrowed view of the option name. Valid until the option is destroyed. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_type_create_with_connection(duckdb_v2_connection_handle connection, - duckdb_v2_custom_type_handle *type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_option_get_name(duckdb_v2_option_handle option, duckdb_v2_identifier_t *out_name, + duckdb_v2_error_info_handle *err); /*! - * Creates a new custom type that will be registered on the loading extension's database. + * Returns the option's static default, as a value of the option's type. * - * Use this from an extension load callback, where an extension handle is available. The type starts out empty: - * configure it with `duckdb_v2_custom_type_set_name()` and `duckdb_v2_custom_type_set_base_type()`, then make it - * available with `duckdb_v2_custom_type_register()`. The caller owns the returned handle and must destroy it with - * `duckdb_v2_custom_type_destroy()`, also after registration. + * A NULL value for an option whose declaration has no default. The caller destroys the returned value. * * history: * - stable: v2.0.0 * - * @param extension The extension to create the type in. - * @param type On success, receives the newly created custom type. Owned by the caller. + * @param option The option. + * @param out_value Receives the default value. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_type_create_with_extension(duckdb_v2_extension_handle extension, - duckdb_v2_custom_type_handle *type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_option_get_default_value(duckdb_v2_option_handle option, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Sets the name of the custom type. - * - * This is the name the type is referred to by in SQL, and the alias carried by every logical type instance of it. The - * name is borrowed and copied. Calling this again replaces the previous name. A name must be set before registration. + * Borrows the option's human-readable description. * * history: * - stable: v2.0.0 * - * @param type The type to set the name of. - * @param name The name to set. Borrowed and copied. + * @param option The option. + * @param out_description Receives a borrowed view of the description. Valid until the option is destroyed. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_type_set_name(duckdb_v2_custom_type_handle type, - duckdb_v2_identifier_t name, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_option_get_description(duckdb_v2_option_handle option, + duckdb_v2_str *out_description, + duckdb_v2_error_info_handle *err); /*! - * Sets the base type of the custom type. + * Reports whether the option may be written at a scope. * - * The custom type shares the base type's internal representation but is logically distinct, so it can carry its own - * cast functions. The type is borrowed and copied. Calling this again replaces the previous base type. A base type must - * be set before registration, and it must be a fully defined concrete type -- ANY is a signature wildcard, not - * something a registered type can be built on. + * GLOBAL and SESSION are the scopes to ask about; DEFAULT is supported by every option that supports either. A write at + * an unsupported scope returns ERROR_INPUT_INVALID. * * history: * - stable: v2.0.0 * - * @param type The type to set the base type of. - * @param base_type The logical type to base the custom type on. Borrowed for the call only. + * @param option The option. + * @param scope The scope to ask about. + * @param out_supported Receives whether a write at the scope is accepted. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_type_set_base_type(duckdb_v2_custom_type_handle type, - duckdb_v2_logical_type_handle base_type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_option_supports_scope(duckdb_v2_option_handle option, + DUCKDB_V2_SETTING_SCOPE scope, bool *out_supported, + duckdb_v2_error_info_handle *err); /*! - * Registers the custom type, making it available for use in SQL queries. - * - * The type is registered on the target given at creation: the connection's database or the loading extension. - * Registration requires a name and a complete base type. The caller still owns the handle after registration and must - * destroy it with `duckdb_v2_custom_type_destroy()`, which does not affect the registered type. + * Returns the scope a DEFAULT write of the option goes to: GLOBAL or SESSION. * * history: * - stable: v2.0.0 * - * @param type The type to register. + * @param option The option. + * @param out_scope Receives the default scope. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_type_register(duckdb_v2_custom_type_handle type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_option_get_default_scope(duckdb_v2_option_handle option, + DUCKDB_V2_SETTING_SCOPE *out_scope, + duckdb_v2_error_info_handle *err); /*! - * Destroys the custom type, releasing its resources. + * Returns the number of aliases registered for this option. * - * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent - * double-destruction. Destroying the handle after registration does not affect the registered type. + * Zero for an extension option; extension options carry no aliases. * * history: * - stable: v2.0.0 * - * @param type The type to destroy. + * @param option The option. + * @param out_count Receives the alias count. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_type_destroy(duckdb_v2_custom_type_handle *type); - -/* --- Struct definitions for custom type --- */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_option_get_alias_count(duckdb_v2_option_handle option, idx_t *out_count, + duckdb_v2_error_info_handle *err); -/* ============================================================================ - * MODULE: data_chunk +/*! + * Borrows the alias name at the given index. + * + * An out-of-range index returns ERROR_INPUT_INVALID. + * + * history: + * - stable: v2.0.0 + * + * @param option The option. + * @param index The alias index, in [0, alias_count). + * @param out_alias Receives a borrowed view of the alias. Valid until the option is destroyed. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_option_get_alias(duckdb_v2_option_handle option, idx_t index, + duckdb_v2_identifier_t *out_alias, + duckdb_v2_error_info_handle *err); + +/* --- Struct definitions for configuration --- */ + +/* ============================================================================ + * MODULE: connection * ============================================================================ */ -/* --- Enums for data_chunk --- */ +/* --- Enums for connection --- */ -/* --- Struct forward declarations for data_chunk --- */ +/* --- Struct forward declarations for connection --- */ -/* --- Types for data_chunk --- */ +/* --- Types for connection --- */ -/* --- Constants for data_chunk --- */ +/* --- Constants for connection --- */ -/* --- Function pointer typedefs for data_chunk --- */ +/* --- Function pointer typedefs for connection --- */ -/* --- Functions for data_chunk --- */ +/* --- Functions for connection --- */ /*! - * Creates an empty data chunk with the given column types. + * Opens a connection to an instance. * - * Allocates one FLAT vector per element of the types array, each at default capacity. Every vector starts at size 0; - * set it with vector_set_size once the vector is populated. The chunk is caller-owned and must be destroyed via - * data_chunk_destroy. + * Each connection carries its own client context and session-scoped (LOCAL) settings. Connections to the same instance + * share its catalog, buffer pool, and transaction manager. A connection may be created before any database is attached + * to the instance; until one is, only the system catalog and the connection's temporary catalog are visible. The caller + * destroys it via `duckdb_v2_connection_destroy()`. * * history: * - stable: v2.0.0 * - * @param types Pointer to an array of logical_type handles, one per column. - * @param column_count Number of elements in the types array. - * @param out_chunk Receives the new chunk handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param instance The instance to connect to. + * @param out_conn Receives the new connection handle. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_data_chunk_create(const duckdb_v2_logical_type_handle *types, idx_t column_count, - duckdb_v2_data_chunk_handle *out_chunk, +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_create(duckdb_v2_instance_handle instance, + duckdb_v2_connection_handle *out_conn, duckdb_v2_error_info_handle *err); /*! - * Creates an empty data chunk with the given column types, drawing its allocator from the connection's context. + * Destroys the connection. * - * Like data_chunk_create, but the chunk's vectors are allocated through the connection's context instead of the default - * allocator, so the memory is accounted to that database. Allocates one FLAT vector per element of the types array, - * each at default capacity. Every vector starts at size 0; set it with vector_set_size once the vector is populated. - * The chunk is caller-owned and must be destroyed via data_chunk_destroy. + * Always succeeds. Releases the connection's reference on the underlying database instance, which survives for as long + * as anything else still references it. On success the handle is set to null. Safe to call on an already-null slot. * * history: * - stable: v2.0.0 * - * @param conn The connection whose context supplies the chunk's allocator. - * @param types Pointer to an array of logical_type handles, one per column. - * @param column_count Number of elements in the types array. - * @param out_chunk Receives the new chunk handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param conn The connection to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_data_chunk_create_with_connection(duckdb_v2_connection_handle conn, - const duckdb_v2_logical_type_handle *types, - idx_t column_count, - duckdb_v2_data_chunk_handle *out_chunk, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_destroy(duckdb_v2_connection_handle *conn); /*! - * Creates an empty data chunk with the given column types, drawing its allocator from a context. + * Interrupts the query currently executing on the connection. * - * Like data_chunk_create, but the chunk's vectors are allocated through the given context instead of the default - * allocator, so the memory is accounted to that database. Use this from inside a callback or extension where a context - * is already in hand. Allocates one FLAT vector per element of the types array, each at default capacity. Every vector - * starts at size 0; set it with vector_set_size once the vector is populated. The chunk is caller-owned and must be - * destroyed via data_chunk_destroy. + * The cross-thread (but not cross-connection) cancellation entry point for streaming results: safe to call from any + * thread within the execution of a query through a connection, including while another thread steps the query's result. + * A no-op when no query is active. Cancellation surfaces on the consuming side as step status CANCELLED + * (`duckdb_v2_result_step()`), or as ERROR_RUNTIME_INTERRUPT (`duckdb_v2_result_fetch_chunk()`). * * history: * - stable: v2.0.0 * - * @param context The context whose allocator will be used for the chunk. - * @param types Pointer to an array of logical_type handles, one per column. - * @param column_count Number of elements in the types array. - * @param out_chunk Receives the new chunk handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param conn The connection whose active query to interrupt. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_data_chunk_create_with_context(duckdb_v2_context_handle context, - const duckdb_v2_logical_type_handle *types, - idx_t column_count, - duckdb_v2_data_chunk_handle *out_chunk, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_interrupt(duckdb_v2_connection_handle conn, + duckdb_v2_error_info_handle *err); /*! - * Creates a deep copy of a data chunk, drawing its allocator from the connection's context. + * Captures a snapshot of the active query's execution progress. + * + * Reads the percentage and row counts from one consistent snapshot of the query currently executing on the connection. + * Safe to call from any thread, including while another thread steps the query's result. * - * Allocates a new chunk with the source chunk's column types and copies all rows into it. The copy is flattened and - * owns all its data, so it stays valid after the source chunk or whatever backs it (such as a column data collection - * scan state) is destroyed. The returned chunk is caller-owned and must be destroyed via data_chunk_destroy. + * Progress is published only when the enable_progress_bar option is set; the bridge does not enable tracking itself. + * Both row counts are 0 when no information is available. The percentage is -1 when tracking is disabled, no query is + * active, or no progress has been published yet. * * history: * - stable: v2.0.0 * - * @param conn The connection whose context supplies the copy's allocator. - * @param chunk The chunk to copy. Left unchanged. - * @param out_chunk Receives the new chunk handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param conn The connection. + * @param out_percentage Receives the percentage complete in [0, 100], -1 if tracking is disabled, no query is active, + * or no progress has been published yet. + * @param out_rows_processed Receives the number of rows processed so far. + * @param out_total_rows_to_process Receives the total number of rows the query will process. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_data_chunk_copy_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_data_chunk_handle chunk, - duckdb_v2_data_chunk_handle *out_chunk, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_progress_get(duckdb_v2_connection_handle conn, double *out_percentage, + uint64_t *out_rows_processed, + uint64_t *out_total_rows_to_process, + duckdb_v2_error_info_handle *err); + +/* --- Struct definitions for connection --- */ + +/* ============================================================================ + * MODULE: custom type + * ============================================================================ */ + +/* --- Enums for custom type --- */ + +/* --- Struct forward declarations for custom type --- */ + +/* --- Types for custom type --- */ + +/*! + * An owned opaque handle to a custom type being built. Created with `duckdb_v2_custom_type_create()`, configured with + * `duckdb_v2_custom_type_set_name()` and `duckdb_v2_custom_type_set_base_type()`, made available with + * `duckdb_v2_connection_register_custom_type()` or `duckdb_v2_extension_register_custom_type()`, and destroyed with + * `duckdb_v2_custom_type_destroy()`. + */ +typedef struct _duckdb_v2_custom_type { + void *internal_ptr; +} * duckdb_v2_custom_type_handle; + +/* --- Constants for custom type --- */ + +/* --- Function pointer typedefs for custom type --- */ + +/* --- Functions for custom type --- */ /*! - * Creates a deep copy of a data chunk, drawing its allocator from a context. + * Creates a new custom type through a factory. + * + * The type starts out empty: configure it with `duckdb_v2_custom_type_set_name()` and + * `duckdb_v2_custom_type_set_base_type()`, then make it available with `duckdb_v2_connection_register_custom_type()` or + * `duckdb_v2_extension_register_custom_type()`. The caller owns the returned handle and must destroy it with + * `duckdb_v2_custom_type_destroy()`, also after registration. * - * Allocates a new chunk with the source chunk's column types and copies all rows into it. Use this from inside a - * callback or extension where a context is already in hand. The copy is flattened and owns all its data, so it stays - * valid after the source chunk or whatever backs it (such as a column data collection scan state) is destroyed. The - * returned chunk is caller-owned and must be destroyed via data_chunk_destroy. + * The custom type can only be registered on the database the factory belongs to. * * history: * - stable: v2.0.0 * - * @param context The context whose allocator will be used for the copy. - * @param chunk The chunk to copy. Left unchanged. - * @param out_chunk Receives the new chunk handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param factory The factory to create the custom type through. + * @param type On success, receives the newly created custom type. Owned by the caller. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_data_chunk_copy_with_context(duckdb_v2_context_handle context, - duckdb_v2_data_chunk_handle chunk, - duckdb_v2_data_chunk_handle *out_chunk, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_type_create(duckdb_v2_factory_handle factory, + duckdb_v2_custom_type_handle *type, + duckdb_v2_error_info_handle *err); /*! - * Destroys a data chunk handle. + * Sets the name of the custom type. * - * Null-safe: passing nullptr or a slot already set to nullptr is a no-op. On success the slot is set to nullptr. Any - * vector previously borrowed from this chunk becomes invalid. + * This is the name the type is referred to by in SQL, and the alias carried by every logical type instance of it. The + * name is borrowed and copied. Calling this again replaces the previous name. A name must be set before registration. * * history: * - stable: v2.0.0 * - * @param chunk The chunk to destroy. + * @param type The type to set the name of. + * @param name The name to set. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_data_chunk_destroy(duckdb_v2_data_chunk_handle *chunk); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_type_set_name(duckdb_v2_custom_type_handle type, + const duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err); /*! - * Returns the row count of a data chunk. + * Sets the base type of the custom type. + * + * The custom type shares the base type's internal representation but is logically distinct, so it can carry its own + * cast functions. The type is borrowed and copied. Calling this again replaces the previous base type. A base type must + * be set before registration, and it must be a fully defined concrete type -- ANY is a signature wildcard, not + * something a registered type can be built on. * * history: * - stable: v2.0.0 * - * @param chunk The chunk. - * @param out_size Receives the row count. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param type The type to set the base type of. + * @param base_type The logical type to base the custom type on. Borrowed for the call only. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_data_chunk_get_size(duckdb_v2_data_chunk_handle chunk, idx_t *out_size, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_type_set_base_type(duckdb_v2_custom_type_handle type, + duckdb_v2_logical_type_handle base_type, + duckdb_v2_error_info_handle *err); /*! - * Returns the number of vectors in a data chunk. + * Registers the custom type, making it available for use in SQL queries. * - * Equals the column count of the producing result. + * The type is registered on the connection's database, visible to every connection, in the connection's transaction: + * inside an explicit transaction, it is invisible to other connections until the transaction commits, and a rollback + * undoes it. Registration requires a name and a complete base type. The custom type must have been created through a + * factory of the same database. The caller still owns the handle after registration and must destroy it with + * `duckdb_v2_custom_type_destroy()`, which does not affect the registered type. * * history: * - stable: v2.0.0 * - * @param chunk The chunk. - * @param out_count Receives the vector count. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param conn The connection to register on. + * @param type The custom type to register. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_data_chunk_get_vector_count(duckdb_v2_data_chunk_handle chunk, idx_t *out_count, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_register_custom_type(duckdb_v2_connection_handle conn, + duckdb_v2_custom_type_handle type, + duckdb_v2_error_info_handle *err); /*! - * Borrows the vector at the given index in a data chunk. + * Registers the custom type, making it available for use in SQL queries. * - * The returned handle is borrowed and valid until the chunk is destroyed; do not destroy it. An out-of-range index - * returns ERROR_INPUT_INVALID. + * The type is registered on the loading extension's database and attributed to the extension. Use this from the + * extension's load callback. Registration requires a name and a complete base type. The custom type must have been + * created through a factory of the same database. The caller still owns the handle after registration and must destroy + * it with `duckdb_v2_custom_type_destroy()`, which does not affect the registered type. * * history: * - stable: v2.0.0 * - * @param chunk The chunk. - * @param index The vector index, in [0, vector_count). - * @param out_vector Receives the borrowed vector handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param extension The loading extension. + * @param type The custom type to register. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_data_chunk_get_vector(duckdb_v2_data_chunk_handle chunk, idx_t index, - duckdb_v2_vector_handle *out_vector, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_extension_register_custom_type(duckdb_v2_extension_handle extension, + duckdb_v2_custom_type_handle type, + duckdb_v2_error_info_handle *err); -/* --- Struct definitions for data_chunk --- */ +/*! + * Destroys the custom type, releasing its resources. + * + * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent + * double-destruction. Destroying the handle after registration does not affect the registered type. + * + * history: + * - stable: v2.0.0 + * + * @param type The type to destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_type_destroy(duckdb_v2_custom_type_handle *type); + +/* --- Struct definitions for custom type --- */ /* ============================================================================ - * MODULE: database + * MODULE: data_chunk * ============================================================================ */ -/* --- Enums for database --- */ +/* --- Enums for data_chunk --- */ -/* --- Struct forward declarations for database --- */ +/* --- Struct forward declarations for data_chunk --- */ -/* --- Types for database --- */ +/* --- Types for data_chunk --- */ -/* --- Constants for database --- */ +/* --- Constants for data_chunk --- */ -/* --- Function pointer typedefs for database --- */ +/* --- Function pointer typedefs for data_chunk --- */ -/* --- Functions for database --- */ +/* --- Functions for data_chunk --- */ /*! - * Opens a database under the environment. - * - * `path` semantics: - * - `nullptr`, ":memory:", or any ":memory:..." path opens a fresh in-memory database. In-memory databases are never - * deduplicated across calls. - * - any other path opens that file as a database, and returns ERROR_RESOURCE_IN_USE if the file is already open under - * the same environment. + * Creates an empty data chunk with the given column types, drawing its allocator from a factory. * - * `options` is an optional array of pre-created config option handles applied at construction time. Only the name and - * setting of each handle are read; the remaining fields are ignored. Options may include pre-open database settings - * (e.g. access_mode, memory_limit) and ATTACH-style storage options (e.g. BLOCK_SIZE, ENCRYPTION_KEY). Unknown names - * are accepted and kept for a later extension to consume. Pass `options=nullptr` and `option_count=0` to open with - * defaults. - * - * LOCAL_ONLY options are rejected at this scope, as they are by database_option_set. + * The chunk's vectors are allocated through the factory's database, so the memory is accounted to that database. + * Allocates one FLAT vector per element of the types array, each at default capacity. Every vector starts at size 0; + * set it with vector_set_size once the vector is populated. The chunk is caller-owned and must be destroyed via + * data_chunk_destroy. * * history: * - stable: v2.0.0 * - * @param env The environment. - * @param path Path to the database. An empty view (`{NULL, 0}` or zero length) or any ':memory:...' path opens an - * in-memory database. - * @param options Optional array of pre-created config option handles. May be `nullptr` if option_count is 0. - * @param option_count The number of entries in the options array. - * @param out_db Receives the new database handle. + * @param factory The factory to create with. + * @param types Pointer to an array of logical_type handles, one per column. + * @param column_count Number of elements in the types array. + * @param out_chunk Receives the new chunk handle. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_open(duckdb_v2_environment_handle env, duckdb_v2_str path, - duckdb_v2_option_handle *options, idx_t option_count, - duckdb_v2_database_handle *out_db, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_data_chunk_create(duckdb_v2_factory_handle factory, + const duckdb_v2_logical_type_handle *types, idx_t column_count, + duckdb_v2_data_chunk_handle *out_chunk, + duckdb_v2_error_info_handle *err); /*! - * Closes the database. + * Creates a deep copy of a data chunk, drawing its allocator from a factory. * - * Always succeeds. The underlying database instance stays alive for as long as any connection still references it; when - * the last reference drops, the instance is destroyed and its path becomes available to reopen. On success the handle - * is set to null. + * Allocates a new chunk with the source chunk's column types and copies all rows into it, through the factory's + * database. The copy is flattened and owns all its data, so it stays valid after the source chunk or whatever backs it + * (such as a column data collection scan state) is destroyed. The returned chunk is caller-owned and must be destroyed + * via data_chunk_destroy. * * history: * - stable: v2.0.0 * - * @param db The database to close. + * @param factory The factory to create with. + * @param chunk The chunk to copy. Left unchanged. + * @param out_chunk Receives the new chunk handle. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_close(duckdb_v2_database_handle *db); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_data_chunk_copy(duckdb_v2_factory_handle factory, + duckdb_v2_data_chunk_handle chunk, + duckdb_v2_data_chunk_handle *out_chunk, + duckdb_v2_error_info_handle *err); /*! - * Sets a config option on the database (GLOBAL scope). + * Destroys a data chunk handle. * - * Only the name and setting of the option are read; the remaining fields are ignored. Equivalent to SQL `SET GLOBAL - * name = setting`. Returns ERROR_INPUT_INVALID for an option declared LOCAL_ONLY, and for a legacy option with no - * global setter. Unknown names are accepted and kept for a later extension to consume. + * Null-safe: passing nullptr or a slot already set to nullptr is a no-op. On success the slot is set to nullptr. Any + * vector previously borrowed from this chunk becomes invalid. * * history: * - stable: v2.0.0 * - * @param db The database. - * @param option The config option to apply. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param chunk The chunk to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_database_option_set(duckdb_v2_database_handle db, duckdb_v2_option_handle option, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_data_chunk_destroy(duckdb_v2_data_chunk_handle *chunk); /*! - * Reads a config option from the database (GLOBAL scope) by name. + * Empties a data chunk so it can be filled again. * - * Allocates a fully populated option: canonical name, current GLOBAL setting, default setting, description, target - * scope, and aliases. Aliases resolve transparently — passing an alias returns the canonical option, with the alias - * listed in its alias array. An unknown name returns ERROR_INPUT_INVALID. The caller destroys the returned option. + * The chunk's size becomes 0, and each of its vectors becomes a writable FLAT vector of its type again, as on creation, + * so a chunk can be reused for one batch after another instead of being created for each. Data, views and pointers + * taken from the chunk or its vectors beforehand do not survive this. Must not be called on a chunk DuckDB lends to a + * callback for reading. * * history: * - stable: v2.0.0 * - * @param db The database. - * @param name Option name (canonical or alias). - * @param out_option Receives the populated option handle. + * @param chunk The chunk to clear. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_database_option_get(duckdb_v2_database_handle db, duckdb_v2_identifier_t name, - duckdb_v2_option_handle *out_option, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_data_chunk_clear(duckdb_v2_data_chunk_handle chunk, + duckdb_v2_error_info_handle *err); /*! - * Returns the number of config options registered on the database. - * - * Counts core options plus the extension options registered on this database. Aliases are NOT counted separately; each - * one is reachable through its canonical option's alias array. + * Returns the row count of a data chunk. * * history: * - stable: v2.0.0 * - * @param db The database. - * @param out_count Receives the option count. + * @param chunk The chunk. + * @param out_size Receives the row count. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_database_option_get_count(duckdb_v2_database_handle db, idx_t *out_count, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_data_chunk_get_size(duckdb_v2_data_chunk_handle chunk, idx_t *out_size, + duckdb_v2_error_info_handle *err); +#if DUCKDB_V2_API_ALLOW_UNSTABLE /*! - * Reads the config option at the given index from the database. - * - * Index space: [0, core_count) addresses core options, [core_count, total) extension options. The mapping is stable for - * as long as no extension registers new options. An out-of-range index returns ERROR_INPUT_INVALID. The caller destroys - * the returned option. + * Returns the number of rows a data chunk can hold, e.g. how many rows the exec callback of a table function may write + * into its output chunk in one call. * * history: - * - stable: v2.0.0 + * - unstable: v2.0.0 * - * @param db The database. - * @param index The option index, in [0, option_count). - * @param out_option Receives the populated option handle. + * @param chunk The chunk. + * @param out_capacity Receives the number of rows the chunk can hold. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_database_option_get_by_index(duckdb_v2_database_handle db, idx_t index, - duckdb_v2_option_handle *out_option, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_data_chunk_get_capacity(duckdb_v2_data_chunk_handle chunk, idx_t *out_capacity, + duckdb_v2_error_info_handle *err); +#endif /*! - * Returns the version of the linked DuckDB library. + * Returns the number of vectors in a data chunk. + * + * Equals the column count of the producing result. * * history: * - stable: v2.0.0 * - * @param out_version The version string. + * @param chunk The chunk. + * @param out_count Receives the vector count. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_library_version(duckdb_v2_str *out_version, duckdb_v2_error_info_handle *err); - -/* --- Struct definitions for database --- */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_data_chunk_get_vector_count(duckdb_v2_data_chunk_handle chunk, idx_t *out_count, + duckdb_v2_error_info_handle *err); + +/*! + * Borrows the vector at the given index in a data chunk. + * + * The returned handle is borrowed and valid until the chunk is destroyed; do not destroy it. An out-of-range index + * returns ERROR_INPUT_INVALID. + * + * history: + * - stable: v2.0.0 + * + * @param chunk The chunk. + * @param index The vector index, in [0, vector_count). + * @param out_vector Receives the borrowed vector handle. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_data_chunk_get_vector(duckdb_v2_data_chunk_handle chunk, idx_t index, + duckdb_v2_vector_handle *out_vector, + duckdb_v2_error_info_handle *err); + +/* --- Struct definitions for data_chunk --- */ /* ============================================================================ * MODULE: environment @@ -2147,17 +2282,18 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_library_version(duckdb_v2_str *out_versio * - stable: v2.0.0 * * @param out_env Receives the new environment handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_create_environment(duckdb_v2_environment_handle *out_env, +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_environment_create(duckdb_v2_environment_handle *out_env, duckdb_v2_error_info_handle *err); /*! * Destroys the environment. * - * Refuses with ERROR_RESOURCE_IN_USE while any database opened through this environment is still alive; close those - * databases first, then retry. On success the handle is set to null. + * Refuses with ERROR_RESOURCE_IN_USE while any instance created through this environment is still alive; destroy those + * instances first, then retry. On success the handle is set to null. * * history: * - stable: v2.0.0 @@ -2165,24 +2301,26 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_create_environment(duckdb_v2_environment_ * @param env The environment. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_destroy_environment(duckdb_v2_environment_handle *env); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_environment_destroy(duckdb_v2_environment_handle *env); /*! - * Returns the number of databases currently open under the environment. + * Returns the number of instances currently alive under the environment. * - * A diagnostic accessor, for tracking down leaked database handles when destroy_environment returns + * A diagnostic accessor, for tracking down leaked instances when `duckdb_v2_environment_destroy()` returns * ERROR_RESOURCE_IN_USE. The count is a snapshot and may change before the next call. * * history: * - stable: v2.0.0 * * @param env The environment. - * @param out_count Receives the number of currently-open databases. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param out_count Receives the number of live instances. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_environment_database_count(duckdb_v2_environment_handle env, idx_t *out_count, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_environment_get_instance_count(duckdb_v2_environment_handle env, + idx_t *out_count, + duckdb_v2_error_info_handle *err); /* --- Struct definitions for environment --- */ @@ -2218,39 +2356,40 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_environment_database_count(duckdb_v2_envi DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_error_info_get_code(duckdb_v2_error_info_handle info, DUCKDB_V2_ERROR *out_code); /*! - * Retrieves the error message associated with an error info handle. + * Retrieves the error text associated with an error info handle. * - * Returns a borrowed pointer to the info's null-terminated error message. The pointer is owned by DuckDB and is valid - * until the info is destroyed; callers must not free it, and must not read it once the info has been destroyed. + * Returns a borrowed pointer to the info's null-terminated error text. The pointer is owned by DuckDB and is valid + * until the info is destroyed or a new text is set; callers must not free it, and must not read it once the info has + * been destroyed. * * history: * - stable: v2.0.0 * * @param info The error info handle to query. - * @param out_text Receives a borrowed view of the message. Owned by DuckDB; valid until the info handle is destroyed. + * @param out_text Receives a borrowed view of the text. Owned by DuckDB; valid until the info handle is destroyed. * @return DUCKDB_V2_ERROR */ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_error_info_get_text(duckdb_v2_error_info_handle info, duckdb_v2_str *out_text); /*! - * Retrieves the error message body without the leading category prefix. + * Retrieves the error text body without the leading category prefix. * - * Returns a borrowed view of the same message error_info_get_text reports, minus the leading " Error: " prefix. - * This is the authoritative unprefixed body: the API exposes no type name, so the prefix cannot be reconstructed and - * the body cannot be derived from error_info_get_text. Unprefixed does not mean plain — the body keeps whatever form it - * was rendered in (a LINE/caret block by default, JSON under errors_as_json). `{NULL, 0}` when there is no message. The + * Returns a borrowed view of the same text error_info_get_text reports, minus the leading " Error: " prefix. This + * is the authoritative unprefixed body: the API exposes no type name, so the prefix cannot be reconstructed and the + * body cannot be derived from error_info_get_text. Unprefixed does not mean plain — the body keeps whatever form it was + * rendered in (a LINE/caret block by default, JSON under errors_as_json). `{NULL, 0}` when there is no text. The * pointer is owned by DuckDB and valid until the info handle is destroyed. * * history: * - stable: v2.0.0 * * @param info The error info handle to query. - * @param out_raw_message Receives a borrowed view of the raw message. Owned by DuckDB; valid until the info handle is + * @param out_raw_text Receives a borrowed view of the raw text. Owned by DuckDB; valid until the info handle is * destroyed. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_error_info_get_raw_message(duckdb_v2_error_info_handle info, - duckdb_v2_str *out_raw_message); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_error_info_get_raw_text(duckdb_v2_error_info_handle info, + duckdb_v2_str *out_raw_text); /*! * Sets the error code for an error info handle. @@ -2268,20 +2407,19 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_error_info_get_raw_message(duckdb_v2_erro DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_error_info_set_code(duckdb_v2_error_info_handle info, DUCKDB_V2_ERROR code); /*! - * Sets the error message for an error info handle. + * Sets the error text for an error info handle. * - * On success, replaces the info's message with the provided one; DuckDB allocates its own copy of the string. On - * failure, nothing is changed. Accepts a `nullptr` info handle, in which case the call is a no-op and returns - * ERROR_NONE. + * On success, replaces the info's text with the provided one; DuckDB allocates its own copy of the string. On failure, + * nothing is changed. Accepts a `nullptr` info handle, in which case the call is a no-op and returns ERROR_NONE. * * history: * - stable: v2.0.0 * - * @param info The error info handle to set. On success, updated with the provided message. - * @param text The error message to set in the info. + * @param info The error info handle to set. On success, updated with the provided text. + * @param text The error text to set in the info. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_error_info_set_text(duckdb_v2_error_info_handle info, duckdb_v2_str text); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_error_info_set_text(duckdb_v2_error_info_handle info, const duckdb_v2_str *text); /*! * Destroys an error info handle and frees its resources. @@ -2355,6 +2493,84 @@ struct duckdb_v2_extension_input { duckdb_v2_error_info_handle *err; }; +/* ============================================================================ + * MODULE: factory + * ============================================================================ */ + +/* --- Enums for factory --- */ + +/* --- Struct forward declarations for factory --- */ + +/* --- Types for factory --- */ + +/* --- Constants for factory --- */ + +/* --- Function pointer typedefs for factory --- */ + +/* --- Functions for factory --- */ + +/*! + * Borrows the factory of an instance. + * + * The factory creates the built-in types and their values, casts with the built-in casts only, and allocates data + * chunks and column data collections through the instance. Anything that needs a catalog — catalog-registered types, + * qualified type names — returns ERROR_INPUT_INVALID. The returned handle is borrowed: it is valid for as long as the + * instance is, and must not be destroyed. + * + * history: + * - stable: v2.0.0 + * + * @param instance The instance. + * @param out_factory Receives the borrowed factory. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_instance_get_factory(duckdb_v2_instance_handle instance, + duckdb_v2_factory_handle *out_factory, + duckdb_v2_error_info_handle *err); + +/*! + * Borrows the factory of a connection. + * + * The factory reaches the connection's catalog and settings. Catalog lookups run in the connection's active + * transaction, or in a transaction of their own when none is open; nothing else opens a transaction. The returned + * handle is borrowed: it is valid for as long as the connection is, and must not be destroyed. + * + * history: + * - stable: v2.0.0 + * + * @param conn The connection. + * @param out_factory Receives the borrowed factory. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_get_factory(duckdb_v2_connection_handle conn, + duckdb_v2_factory_handle *out_factory, + duckdb_v2_error_info_handle *err); + +/*! + * Borrows the factory of a context. + * + * The factory reaches the context's catalog and settings, within the transaction the context already has. The returned + * handle is borrowed: it is valid for as long as the context is, and must not be destroyed. + * + * history: + * - stable: v2.0.0 + * + * @param ctx The context. + * @param out_factory Receives the borrowed factory. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_context_get_factory(duckdb_v2_context_handle ctx, + duckdb_v2_factory_handle *out_factory, + duckdb_v2_error_info_handle *err); + +/* --- Struct definitions for factory --- */ + /* ============================================================================ * MODULE: file system * ============================================================================ */ @@ -2398,6 +2614,13 @@ typedef enum DUCKDB_V2_FILE_FLAG { * otherwise assume sequential access, by caching, buffering, or keeping a single cursor, needs to know not to. */ DUCKDB_V2_FILE_FLAG_PARALLEL_ACCESS = 7, + + /*! + * Read the file through DuckDB's external file cache, which keeps the bytes that were read in memory, so that later + * reads of the same file - also by later queries - do not have to read them again. Whether a local file is cached + * is decided by the `cache_local_files` setting. Only meaningful for reading. + */ + DUCKDB_V2_FILE_FLAG_EXTERNAL_FILE_CACHE = 8, DUCKDB_V2_FILE_FLAG_MAX_ENUM = 0x7FFFFFFF, } DUCKDB_V2_FILE_FLAG; @@ -2406,10 +2629,10 @@ typedef enum DUCKDB_V2_FILE_FLAG { /* --- Types for file system --- */ /*! - * A borrowed opaque handle to a file system. Obtained from `duckdb_v2_file_system_get_from_context()` or - * `duckdb_v2_file_system_get_from_connection()`, and used to open files with `duckdb_v2_file_system_open()`. Borrowed: - * the handle belongs to the context or connection it came from, is valid only for as long as that is, and must not be - * destroyed. + * A borrowed opaque handle to a file system. Obtained from `duckdb_v2_instance_get_file_system()`, + * `duckdb_v2_connection_get_file_system()` or `duckdb_v2_context_get_file_system()`, and used to open files with + * `duckdb_v2_file_system_open()`. Borrowed: the handle belongs to the instance, connection or context it came from, is + * valid only for as long as that is, and must not be destroyed. */ typedef struct _duckdb_v2_file_system { void *internal_ptr; @@ -2438,13 +2661,22 @@ typedef struct _duckdb_v2_file { /* --- Function pointer typedefs for file system --- */ +/*! + * Receives one path matched by `duckdb_v2_file_system_path_glob()`. + * + * The view is borrowed for the duration of the call only, and is not guaranteed to be null-terminated. Report an error + * through `err`, a live slot that must not be destroyed, to stop the glob: `duckdb_v2_file_system_path_glob()` then + * fails with that error. The callback must not throw or unwind across the boundary. + */ +typedef void (*duckdb_v2_file_system_path_glob_callback_fn)(const duckdb_v2_str *path, void *user_data, + duckdb_v2_error_info_handle *err); + /* --- Functions for file system --- */ /*! * Borrows the file system of a context. * - * Use this from inside a callback, where a context is in hand. The returned handle is borrowed: it is valid only for as - * long as the context is, and must not be destroyed. + * The returned handle is borrowed: it is valid only for as long as the context is, and must not be destroyed. * * history: * - stable: v2.0.0 @@ -2455,28 +2687,50 @@ typedef struct _duckdb_v2_file { * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_file_system_get_from_context(duckdb_v2_context_handle context, - duckdb_v2_file_system_handle *file_system, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_context_get_file_system(duckdb_v2_context_handle context, + duckdb_v2_file_system_handle *file_system, + duckdb_v2_error_info_handle *err); + +/*! + * Borrows the file system of an instance. + * + * Paths are resolved with the instance's GLOBAL settings and secrets, and file I/O is not attributed to any query. + * + * The returned handle is borrowed: it is valid only for as long as the instance is, and must not be destroyed. + * + * history: + * - stable: v2.0.0 + * + * @param instance The instance to take the file system from. + * @param file_system Receives the borrowed file system. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_instance_get_file_system(duckdb_v2_instance_handle instance, + duckdb_v2_file_system_handle *file_system, + duckdb_v2_error_info_handle *err); /*! * Borrows the file system of a connection. * - * The connection form of `duckdb_v2_file_system_get_from_context()`, for callers outside a callback. The returned - * handle is borrowed: it is valid only for as long as the connection is, and must not be destroyed. + * Paths are resolved with the connection's settings and secrets, and file I/O is attributed to its running query, as + * through `duckdb_v2_context_get_file_system()` in a callback. + * + * The returned handle is borrowed: it is valid only for as long as the connection is, and must not be destroyed. * * history: * - stable: v2.0.0 * - * @param connection The connection to take the file system from. + * @param conn The connection to take the file system from. * @param file_system Receives the borrowed file system. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_file_system_get_from_connection(duckdb_v2_connection_handle connection, - duckdb_v2_file_system_handle *file_system, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_get_file_system(duckdb_v2_connection_handle conn, + duckdb_v2_file_system_handle *file_system, + duckdb_v2_error_info_handle *err); /*! * Creates a set of open options for a file system. @@ -2547,7 +2801,8 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_file_open_options_set_flag(duckdb_v2_file * @return DUCKDB_V2_ERROR */ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_file_open_options_set_value(duckdb_v2_file_open_options_handle options, - duckdb_v2_str name, duckdb_v2_value_handle value, + const duckdb_v2_str *name, + duckdb_v2_value_handle value, duckdb_v2_error_info_handle *err); /*! @@ -2593,10 +2848,138 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_file_open_options_destroy(duckdb_v2_file_ * @return DUCKDB_V2_ERROR */ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_file_system_open(duckdb_v2_file_system_handle file_system, - duckdb_v2_str file_path, + const duckdb_v2_str *file_path, duckdb_v2_file_open_options_handle options, duckdb_v2_file_handle *file, duckdb_v2_error_info_handle *err); +/*! + * Reports whether a file exists at the given path. + * + * The path is routed the way `duckdb_v2_file_system_open()` routes it, so asking about a path handled by a remote file + * system may reach out to it. A directory is not a file, and reports false. + * + * history: + * - stable: v2.0.0 + * + * @param file_system The file system to ask. + * @param path The path to check. Borrowed for the call only. + * @param out_exists Receives whether a file exists at the path. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_file_system_path_exists(duckdb_v2_file_system_handle file_system, + const duckdb_v2_str *path, bool *out_exists, + duckdb_v2_error_info_handle *err); + +/*! + * Reports whether a path names a remote file, e.g. one under `s3://`, `https://`, `gcs://` or `azure://`. + * + * Decided from the path alone: no file system is consulted, so it answers even when the extension providing the remote + * file system is not loaded. + * + * history: + * - stable: v2.0.0 + * + * @param file_system The file system the path is meant for. + * @param path The path to check. Borrowed for the call only. + * @param out_is_remote Receives whether the path names a remote file. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_file_system_path_is_remote(duckdb_v2_file_system_handle file_system, + const duckdb_v2_str *path, bool *out_is_remote, + duckdb_v2_error_info_handle *err); + +/*! + * Expands a path as the file system does when opening it: a leading `~` becomes the home directory, as the + * `home_directory` setting configures it. + * + * A path without anything to expand is handed back as it is. The expanded path is handed to `sink` in a single call. + * + * history: + * - stable: v2.0.0 + * + * @param file_system The file system whose settings expand the path. + * @param path The path to expand. Borrowed for the call only. + * @param sink Receives the expanded path in a single call. Borrowed for the duration of that call; see text_sink for + * the full contract. + * @param user_data Opaque pointer passed through to sink untouched. May be NULL. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_file_system_path_expand(duckdb_v2_file_system_handle file_system, + const duckdb_v2_str *path, duckdb_v2_text_sink_fn sink, + void *user_data, duckdb_v2_error_info_handle *err); + +/*! + * Finds the files matching a glob pattern, e.g. `*.csv` or `data_?.json` in a directory, handing each matched path to + * `callback`. + * + * The pattern is routed the way `duckdb_v2_file_system_open()` routes a path, so globbing a remote path may reach out + * to the file system handling it, and is only supported where that file system supports it. A pattern without glob + * characters matches the file it names, if there is one. Matching nothing is not an error. In which order the paths are + * handed over is up to the file system. + * + * history: + * - stable: v2.0.0 + * + * @param file_system The file system to glob in. + * @param pattern The glob pattern. Borrowed for the call only. + * @param callback Invoked once per matched path. + * @param user_data Opaque pointer passed through to `callback` untouched. May be NULL. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_file_system_path_glob(duckdb_v2_file_system_handle file_system, + const duckdb_v2_str *pattern, + duckdb_v2_file_system_path_glob_callback_fn callback, + void *user_data, duckdb_v2_error_info_handle *err); + +/*! + * Moves, or renames, a file from one path to another. + * + * The move is handled by the file system `source` routes to, the way `duckdb_v2_file_system_open()` routes a path; + * moving a file between two file systems is not supported. Whether an existing file at `target` is replaced, and + * whether the move is atomic, is up to that file system. + * + * history: + * - stable: v2.0.0 + * + * @param file_system The file system to move the file in. + * @param source The path of the file to move. Borrowed for the call only. + * @param target The path to move the file to. Borrowed for the call only. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_file_system_path_move(duckdb_v2_file_system_handle file_system, + const duckdb_v2_str *source, const duckdb_v2_str *target, + duckdb_v2_error_info_handle *err); + +/*! + * Removes the file at a path, if there is one. + * + * The path is routed the way `duckdb_v2_file_system_open()` routes it. A path with no file is not an error: + * `out_removed` then receives false. A file that exists but cannot be removed is. + * + * history: + * - stable: v2.0.0 + * + * @param file_system The file system to remove the file from. + * @param path The path of the file to remove. Borrowed for the call only. + * @param out_removed Optional. Receives whether a file was removed. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_file_system_path_remove(duckdb_v2_file_system_handle file_system, + const duckdb_v2_str *path, bool *out_removed, + duckdb_v2_error_info_handle *err); + /*! * Reads from the file into a caller-supplied buffer. * @@ -2792,310 +3175,396 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_file_destroy(duckdb_v2_file_handle *file) /* --- Struct definitions for file system --- */ /* ============================================================================ - * MODULE: function signature + * MODULE: function * ============================================================================ */ -/* --- Enums for function signature --- */ +/* --- Enums for function --- */ -/* --- Struct forward declarations for function signature --- */ +/* --- Struct forward declarations for function --- */ -/* --- Types for function signature --- */ +/* --- Types for function --- */ -//! An opaque handle to a function signature. Carries the function's argument types and return type. -typedef struct _duckdb_v2_function_signature { +/*! + * A borrowed opaque handle to the call site a bind callback is binding. Every function family's bind callback receives + * this handle next to its own bind info. It gives access to the arguments of the call, the function's user data, and + * the bind data. + * + * The arguments form one list in four parts, in this order: one argument per positional-only and standard parameter, in + * declaration order; the arguments `*args` received, in call order; one argument per named-only parameter, in + * declaration order; and the arguments `**kwargs` received, in call order. How the caller passed an argument does not + * matter: a standard parameter passed by name is at its declared position. A parameter the call omitted is present with + * its default value. See `duckdb_v2_function_bind_get_arg_count()` for the size of each part. + */ +typedef struct _duckdb_v2_function_bind_info { void *internal_ptr; -} * duckdb_v2_function_signature_handle; +} * duckdb_v2_function_bind_info_handle; -/* --- Constants for function signature --- */ +/* --- Constants for function --- */ -/* --- Function pointer typedefs for function signature --- */ +/* --- Function pointer typedefs for function --- */ -/* --- Functions for function signature --- */ +/* --- Functions for function --- */ /*! + * Retrieves the user data set on the function being bound, e.g. via `duckdb_v2_scalar_function_set_user_data()`. + * * history: * - stable: v2.0.0 * - * @param sig The signature to configure. - * @param name The parameter name. Borrowed and copied. - * @param type The parameter type. Borrowed and copied. - * @param value Optional default value, borrowed and copied. Will be cast to the parameter type. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The bind info handle. + * @param data Receives the user data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_function_signature_add_parameter(duckdb_v2_function_signature_handle sig, - duckdb_v2_identifier_t name, - duckdb_v2_logical_type_handle type, - duckdb_v2_value_handle value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_function_bind_get_user_data(duckdb_v2_function_bind_info_handle info, + void **data, duckdb_v2_error_info_handle *err); /*! - * Sets the variadic tail type of a signature. + * Sets the function's "bind data" from the bind callback. * - * Makes the signature variadic: after its fixed parameters it accepts any number of extra trailing arguments, each - * implicitly cast to type. Pass ANY for a heterogeneous tail whose arguments keep their own types. A NULL or INVALID - * type is rejected with INVALID_INPUT; ANY is accepted. Calling this again overwrites the previous variadic tail type. - * The type is borrowed and copied. + * The bind data is stored with the bound call site and retrievable from every later callback. The opaque handle bundles + * the pointer with an optional destructor, invoked when the bind data is no longer needed, and an optional equality + * callback used when comparing two bound call sites; without one, pointer equality is used. Scalar functions set their + * bind data through `duckdb_v2_scalar_function_bind_set_bind_data()` instead. * * history: * - stable: v2.0.0 * - * @param sig The signature to configure. - * @param type The type extra trailing arguments are cast to. ANY leaves them un-cast. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The bind info handle. + * @param data Opaque handle bundling the bind data pointer plus optional destructor and equality callbacks. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_function_signature_set_varargs(duckdb_v2_function_signature_handle sig, - duckdb_v2_logical_type_handle type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_function_bind_set_bind_data(duckdb_v2_function_bind_info_handle info, + duckdb_v2_opaque *data, + duckdb_v2_error_info_handle *err); /*! - * Sets the return type of a signature. + * Returns the number of arguments of the call site being bound, split into the four parts of the argument list. * - * Sets the signature's return type. The type is borrowed and copied. Calling this again overwrites the previous return - * type. An INVALID type is rejected with ERROR_INPUT_INVALID. Whether a concrete return type is required, and whether - * ANY is accepted, depends on the function family and is enforced when the signature is registered with a builder. + * The parts follow each other in this order: the positional-only and standard parameters, the arguments `*args` + * received, the named-only parameters, and the arguments `**kwargs` received. The argument at index `i` of the + * named-only part is therefore at index `positional_fixed + positional_variadic + i`. Valid indices for the other + * argument functions are [0, the sum of the four counts). Every out-parameter may be NULL, in which case nothing is + * written to it. * * history: * - stable: v2.0.0 * - * @param sig The signature to configure. - * @param type The return type. Borrowed and copied. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The bind info handle. + * @param positional_fixed Optional. Receives the number of positional-only and standard parameters. + * @param positional_variadic Optional. Receives the number of arguments `*args` received, 0 when the signature has + * none. + * @param named_fixed Optional. Receives the number of named-only parameters. + * @param named_variadic Optional. Receives the number of arguments `**kwargs` received, 0 when the signature has none. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_function_signature_set_return_type(duckdb_v2_function_signature_handle sig, - duckdb_v2_logical_type_handle type, - duckdb_v2_error_info_handle *err); - -/* --- Struct definitions for function signature --- */ - -/* ============================================================================ - * MODULE: identifier - * ============================================================================ */ - -/* --- Enums for identifier --- */ - -/* --- Struct forward declarations for identifier --- */ - -/* --- Types for identifier --- */ - -/* --- Constants for identifier --- */ - -/* --- Function pointer typedefs for identifier --- */ - -/* --- Functions for identifier --- */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_function_bind_get_arg_count(duckdb_v2_function_bind_info_handle info, + idx_t *positional_fixed, idx_t *positional_variadic, + idx_t *named_fixed, idx_t *named_variadic, + duckdb_v2_error_info_handle *err); /*! - * Renders a name as a SQL identifier, quoting and escaping only when required. - * - * Produces the SQL text for the name: the name itself when it is already a legal bare identifier, or the name - * double-quoted with interior double quotes doubled when it is a keyword or contains characters that require quoting. - * This is the engine's own identifier rendering, so the result parses back to a name equal to the input. + * Retrieves the type of the argument at the given index. * - * Writes into a caller-supplied buffer, so nothing is allocated on the caller's behalf and nothing has to be freed. - * Pass out_text = NULL to size the buffer without rendering into it: out_length then receives the length, and - * out_capacity is ignored. With out_text != NULL, out_capacity must be at least out_length + 1, or the call returns - * ERROR_INPUT_OBJECT_SIZE with out_length set to the required length and out_text left untouched. - * - * out_length never counts the terminator, but a successful write always appends one, so the buffer is usable as a C - * string. + * Fails if the index is out of bounds. The returned type is owned by the caller and must be destroyed via + * `duckdb_v2_logical_type_destroy()`. * * history: * - stable: v2.0.0 * - * @param name The name to render. Borrowed for the call only. - * @param out_text Caller-owned buffer receiving the text plus a null terminator, or NULL to only report the required - * length in out_length. - * @param out_capacity Bytes available in out_text, terminator included. Ignored when out_text is NULL. - * @param out_length Receives the text length excluding the null terminator — written on success and on - * ERROR_INPUT_OBJECT_SIZE. + * @param info The bind info handle. + * @param index The index of the argument. + * @param type Receives the argument type. Owned by the caller; destroy via `duckdb_v2_logical_type_destroy()`. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_identifier_render_quoted(duckdb_v2_identifier_t name, char *out_text, - idx_t out_capacity, idx_t *out_length, - duckdb_v2_error_info_handle *err); - -/* --- Struct definitions for identifier --- */ - -/* ============================================================================ - * MODULE: logging - * ============================================================================ */ - -/* --- Enums for logging --- */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_function_bind_get_arg_type(duckdb_v2_function_bind_info_handle info, idx_t index, + duckdb_v2_logical_type_handle *type, + duckdb_v2_error_info_handle *err); /*! - * Severity of a log message. Mirrors DuckDB's own log levels, and is compared against the configured threshold: an - * entry below it is dropped. + * Folds the argument at the given index to a constant value. + * + * Fails if the argument is not constant, e.g. a column reference, or if the index is out of bounds. The arguments of a + * table or pragma function are always constant. The resulting value may be NULL. The returned value is owned by the + * caller and must be destroyed via `duckdb_v2_value_destroy()`. + * + * history: + * - stable: v2.0.0 + * + * @param info The bind info handle. + * @param index The index of the argument. + * @param value Receives the constant value. Owned by the caller; destroy via `duckdb_v2_value_destroy()`. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR */ -typedef enum DUCKDB_V2_LOG_LEVEL { - //! Finest detail; off in any default configuration. - DUCKDB_V2_LOG_LEVEL_TRACE = 10, - - //! Diagnostic detail. - DUCKDB_V2_LOG_LEVEL_DEBUG = 20, - - //! Ordinary progress information. - DUCKDB_V2_LOG_LEVEL_INFO = 30, - - //! A recoverable problem. - DUCKDB_V2_LOG_LEVEL_WARNING = 40, - - //! An operation failed. - DUCKDB_V2_LOG_LEVEL_ERROR = 50, - - //! An unrecoverable failure. - DUCKDB_V2_LOG_LEVEL_FATAL = 60, - DUCKDB_V2_LOG_LEVEL_MAX_ENUM = 0x7FFFFFFF, -} DUCKDB_V2_LOG_LEVEL; - -/* --- Struct forward declarations for logging --- */ - -/* --- Types for logging --- */ - -/* --- Constants for logging --- */ - -/* --- Function pointer typedefs for logging --- */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_function_bind_get_arg_value(duckdb_v2_function_bind_info_handle info, + idx_t index, duckdb_v2_value_handle *value, + duckdb_v2_error_info_handle *err); -/* --- Functions for logging --- */ +/*! + * Retrieves the name of the argument at the given index. + * + * For an argument of a declared parameter, this is the parameter name. For an argument `**kwargs` received, it is the + * name the caller passed, which may be the name of a positional-only parameter. An argument `*args` received has no + * name, and yields an empty name. Fails if the index is out of bounds. The name is borrowed and valid only for the + * duration of the callback. + * + * history: + * - stable: v2.0.0 + * + * @param info The bind info handle. + * @param index The index of the argument. + * @param name Receives a borrowed view of the argument name. Valid only for the duration of the callback. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_function_bind_get_arg_name(duckdb_v2_function_bind_info_handle info, idx_t index, + duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err); /*! - * Writes a message to DuckDB's log. + * Looks up the index of an argument by name. * - * The entry is attributed to the context's connection scope, so it carries that connection's ids. Whether it is - * recorded at all is up to the database's log configuration: if logging is off, or level is below the configured - * threshold, or log_type is not among the enabled types, the call succeeds and writes nothing. An empty log_type - * selects the default type. Read entries back with `SELECT * FROM duckdb_logs`. + * Names are matched case-insensitively, against the names a caller can pass an argument by: those of the standard and + * named-only parameters, and those `**kwargs` received. Positional-only parameters are skipped, so `f(1, x := 2)` with + * a positional-only `x` finds the argument `**kwargs` received. A name the call did not pass is not an error: `found` + * receives false and `index` is left untouched. A standard or named-only parameter is always found, as the call either + * passed it or it carries its default value. * * history: * - stable: v2.0.0 * - * @param ctx The context to log through. - * @param level Severity of the message. - * @param log_type The log type to record under, matched case-sensitively. Empty selects the default type. - * @param message The message body. Borrowed for the call only. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The bind info handle. + * @param name The name to look up. Borrowed for the call only. + * @param index Receives the index of the argument, if found. + * @param found Receives whether the call has an argument of that name. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_context_log(duckdb_v2_context_handle ctx, DUCKDB_V2_LOG_LEVEL level, - duckdb_v2_str log_type, duckdb_v2_str message, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_function_bind_get_arg_index(duckdb_v2_function_bind_info_handle info, + const duckdb_v2_identifier_t *name, idx_t *index, + bool *found, duckdb_v2_error_info_handle *err); -/* --- Struct definitions for logging --- */ +/* --- Struct definitions for function --- */ /* ============================================================================ - * MODULE: qname + * MODULE: function docs * ============================================================================ */ -/* --- Enums for qname --- */ +/* --- Enums for function docs --- */ -/* --- Struct forward declarations for qname --- */ +/* --- Struct forward declarations for function docs --- */ -/* --- Types for qname --- */ +/* --- Types for function docs --- */ /*! - * An owned qualified name: an ordered path of one to three non-empty identifier parts whose last element is the object - * name. Construct with `duckdb_v2_qname_parse()` or `duckdb_v2_qname_create()`, read with - * `duckdb_v2_qname_get_part_count()` and `duckdb_v2_qname_get_part()`, render back to SQL with - * `duckdb_v2_qname_render()`, compare with `duckdb_v2_qname_equals()`, and destroy with `duckdb_v2_qname_destroy()`. - * Two handles are compared with `duckdb_v2_qname_equals()`, never by pointer. + * A borrowed opaque handle to the documentation of a function overload. Taken from the function it documents with + * `duckdb_v2_scalar_function_get_docs()`, `duckdb_v2_aggregate_function_get_docs()`, + * `duckdb_v2_table_function_get_docs()`, `duckdb_v2_pragma_function_get_docs()` or + * `duckdb_v2_macro_function_get_docs()`, and valid for as long as that function handle is; the caller never destroys + * it. */ -typedef struct _duckdb_v2_qname { +typedef struct _duckdb_v2_function_docs { void *internal_ptr; -} * duckdb_v2_qname_handle; +} * duckdb_v2_function_docs_handle; -/* --- Constants for qname --- */ +/* --- Constants for function docs --- */ -/* --- Function pointer typedefs for qname --- */ +/* --- Function pointer typedefs for function docs --- */ -/* --- Functions for qname --- */ +/* --- Functions for function docs --- */ /*! - * Parses SQL text into a qualified name. + * Sets the description of the function overload. * - * Applies the engine's qualified-name rules: dots separate parts, and a double-quoted part may contain dots and doubled - * interior quotes. More than three parts and an unterminated quote are rejected with the parser's own error; text - * without at least one non-empty part is rejected with `ERROR_INPUT_INVALID`. When the parts are already separate, - * build the name with `duckdb_v2_qname_create()` rather than joining them and parsing the result. + * The description is borrowed and copied. Calling this again replaces the previous description. * * history: * - stable: v2.0.0 * - * @param text The name text to parse. Borrowed for the call only. - * @param name On success, receives the qualified name. Owned by the caller; destroy via `duckdb_v2_qname_destroy()`. + * @param docs The documentation to set the description of. + * @param description The description to set. Borrowed and copied. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_qname_parse(duckdb_v2_str text, duckdb_v2_qname_handle *name, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_function_docs_set_description(duckdb_v2_function_docs_handle docs, + const duckdb_v2_str *description, + duckdb_v2_error_info_handle *err); /*! - * Creates a qualified name from its parts. + * Adds an example of calling the function overload. * - * The parts are ordered outermost first, so the last one is the object name. Between one and three parts are accepted - * -- the engine qualifies at most catalog.schema.name today -- and every part must be non-empty: partial qualification - * is expressed by passing fewer parts, never by empty placeholders. Zero parts, more than three, and an empty part are - * all rejected with `ERROR_INPUT_INVALID`. The parts are borrowed and copied. + * The title and the example are borrowed and copied, and examples are reported in the order they were added. * * history: * - stable: v2.0.0 * - * @param parts An array of `part_count` non-empty identifier views, outermost first. - * @param part_count The number of parts, between one and three. - * @param name On success, receives the qualified name. Owned by the caller; destroy via `duckdb_v2_qname_destroy()`. + * @param docs The documentation to add the example to. + * @param title Optional. The title of the example; NULL or an empty view for none. Borrowed and copied. + * @param example The example, e.g. a SQL expression calling the function. Borrowed and copied. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_qname_create(const duckdb_v2_identifier_t *parts, idx_t part_count, - duckdb_v2_qname_handle *name, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_function_docs_add_example(duckdb_v2_function_docs_handle docs, + const duckdb_v2_str *title, + const duckdb_v2_str *example, + duckdb_v2_error_info_handle *err); /*! - * Returns how many parts the qualified name has. + * Adds a category the function overload belongs to, e.g. "string" or "aggregate". * - * Always at least one. Valid indices for `duckdb_v2_qname_get_part()` are [0, count), and the object name is the part - * at count - 1. + * The category is borrowed and copied, and categories are reported in the order they were added. * * history: * - stable: v2.0.0 * - * @param name The qualified name. - * @param count Receives the number of parts. + * @param docs The documentation to add the category to. + * @param category The category. Borrowed and copied. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_qname_get_part_count(duckdb_v2_qname_handle name, idx_t *count, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_function_docs_add_category(duckdb_v2_function_docs_handle docs, + const duckdb_v2_str *category, + duckdb_v2_error_info_handle *err); + +/* --- Struct definitions for function docs --- */ + +/* ============================================================================ + * MODULE: function signature + * ============================================================================ */ + +/* --- Enums for function signature --- */ /*! - * Borrows one part of the qualified name. + * How a caller passes the argument for a parameter, following Python's parameter kinds. A signature orders its + * parameters by kind, in the order the values are listed here. Pass one of these to + * `duckdb_v2_function_signature_add_parameter()`. + */ +typedef enum DUCKDB_V2_FUNCTION_PARAMETER_KIND { + /*! + * Passed by position only. The name is invisible to a caller: a named argument with the same name is received by + * `**kwargs`, or rejected when the signature has none. + */ + DUCKDB_V2_FUNCTION_PARAMETER_KIND_POSITIONAL_ONLY = 0, + + //! Passed by position or by name. + DUCKDB_V2_FUNCTION_PARAMETER_KIND_STANDARD = 1, + + /*! + * `*args`: receives the positional arguments left over after the positional-only and standard parameters, each cast + * to the parameter type. ANY leaves them un-cast. + */ + DUCKDB_V2_FUNCTION_PARAMETER_KIND_POSITIONAL_VARIADIC = 2, + + //! Passed by name only. + DUCKDB_V2_FUNCTION_PARAMETER_KIND_NAMED_ONLY = 3, + + /*! + * `**kwargs`: receives the named arguments that match no other parameter, each cast to the parameter type. ANY + * leaves them un-cast. + */ + DUCKDB_V2_FUNCTION_PARAMETER_KIND_NAMED_VARIADIC = 4, + DUCKDB_V2_FUNCTION_PARAMETER_KIND_MAX_ENUM = 0x7FFFFFFF, +} DUCKDB_V2_FUNCTION_PARAMETER_KIND; + +/* --- Struct forward declarations for function signature --- */ + +/* --- Types for function signature --- */ + +//! An opaque handle to a function signature. Carries the function's parameters and return type. +typedef struct _duckdb_v2_function_signature { + void *internal_ptr; +} * duckdb_v2_function_signature_handle; + +/* --- Constants for function signature --- */ + +/* --- Function pointer typedefs for function signature --- */ + +/* --- Functions for function signature --- */ + +/*! + * Adds a parameter to a signature. * - * Parts are ordered outermost first, so the object name is the part at `duckdb_v2_qname_get_part_count()` - 1. The view - * is valid until the qualified name is destroyed. An index outside [0, count) is rejected with - * `ERROR_INPUT_OUT_OF_RANGE`. + * The kind decides how a caller passes the argument; see `DUCKDB_V2_FUNCTION_PARAMETER_KIND`. The signature keeps its + * parameters ordered by kind, and parameters of the same kind in the order they were added, so parameters of different + * kinds can be added in any order. A default value makes the parameter optional; `*args` and `**kwargs` cannot have + * one. + * + * Registration fails when two parameters share a name, when the signature has more than one `*args` or `**kwargs` + * parameter, or when a standard parameter without a default value follows one with a default value. * * history: * - stable: v2.0.0 * - * @param name The qualified name. - * @param index Zero-based part index. - * @param part Receives a borrowed view of the part, valid until the qualified name is destroyed. + * @param sig The signature to configure. + * @param name The parameter name. Borrowed and copied. + * @param type The parameter type. ANY accepts an argument of any type without casting it. Borrowed and copied. + * @param value Optional default value, borrowed and copied. Will be cast to the parameter type. + * @param kind How a caller passes the argument for the parameter. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_qname_get_part(duckdb_v2_qname_handle name, idx_t index, - duckdb_v2_identifier_t *part, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_function_signature_add_parameter( + duckdb_v2_function_signature_handle sig, const duckdb_v2_identifier_t *name, duckdb_v2_logical_type_handle type, + duckdb_v2_value_handle value, DUCKDB_V2_FUNCTION_PARAMETER_KIND kind, duckdb_v2_error_info_handle *err); /*! - * Renders the qualified name as SQL text. + * Sets the return type of a signature. * - * Joins the parts with dots, quoting and escaping each one only where the identifier requires it, so the result parses - * back through `duckdb_v2_qname_parse()` to an equal name. + * Sets the signature's return type. The type is borrowed and copied. Calling this again overwrites the previous return + * type. An INVALID type is rejected with ERROR_INPUT_INVALID. Whether a concrete return type is required, and whether + * ANY is accepted, depends on the function family and is enforced when the signature is registered with a builder. + * + * history: + * - stable: v2.0.0 + * + * @param sig The signature to configure. + * @param type The return type. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_function_signature_set_return_type(duckdb_v2_function_signature_handle sig, + duckdb_v2_logical_type_handle type, + duckdb_v2_error_info_handle *err); + +/* --- Struct definitions for function signature --- */ + +/* ============================================================================ + * MODULE: identifier + * ============================================================================ */ + +/* --- Enums for identifier --- */ + +/* --- Struct forward declarations for identifier --- */ + +/* --- Types for identifier --- */ + +/* --- Constants for identifier --- */ + +/* --- Function pointer typedefs for identifier --- */ + +/* --- Functions for identifier --- */ + +/*! + * Renders a name as a SQL identifier, quoting and escaping only when required. + * + * Produces the SQL text for the name: the name itself when it is already a legal bare identifier, or the name + * double-quoted with interior double quotes doubled when it is a keyword or contains characters that require quoting. + * This is the engine's own identifier rendering, so the result parses back to a name equal to the input. * * Writes into a caller-supplied buffer, so nothing is allocated on the caller's behalf and nothing has to be freed. * Pass out_text = NULL to size the buffer without rendering into it: out_length then receives the length, and @@ -3108,7 +3577,7 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_qname_get_part(duckdb_v2_qname_handle nam * history: * - stable: v2.0.0 * - * @param name The qualified name to render. + * @param name The name to render. Borrowed for the call only. * @param out_text Caller-owned buffer receiving the text plus a null terminator, or NULL to only report the required * length in out_length. * @param out_capacity Bytes available in out_text, terminator included. Ignored when out_text is NULL. @@ -3118,657 +3587,767 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_qname_get_part(duckdb_v2_qname_handle nam * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_qname_render(duckdb_v2_qname_handle name, char *out_text, idx_t out_capacity, - idx_t *out_length, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_identifier_render_quoted(const duckdb_v2_identifier_t *name, char *out_text, + idx_t out_capacity, idx_t *out_length, + duckdb_v2_error_info_handle *err); + +/* --- Struct definitions for identifier --- */ + +/* ============================================================================ + * MODULE: instance + * ============================================================================ */ + +/* --- Enums for instance --- */ + +/* --- Struct forward declarations for instance --- */ + +/* --- Types for instance --- */ /*! - * Compares two qualified names. + * An opaque, owned handle to the `(KEY value)` options of one `duckdb_v2_instance_attach()` call, as SQL `ATTACH` + * accepts them. Created from the instance handle it will be used with via `duckdb_v2_attach_options_create()`; destroy + * it via `duckdb_v2_attach_options_destroy()`. + */ +typedef struct _duckdb_v2_attach_options { + void *internal_ptr; +} * duckdb_v2_attach_options_handle; + +/* --- Constants for instance --- */ + +/* --- Function pointer typedefs for instance --- */ + +/* --- Functions for instance --- */ + +/*! + * Creates an instance under the environment with no startup options. * - * True when both have the same number of parts and every part matches case-insensitively, which is the engine's own - * identifier equality: casing never distinguishes two names. This is the only way to compare names; two handles holding - * equal names are still distinct pointers. + * The instance is ready on return, with no database attached: attach one with `duckdb_v2_instance_attach()` and pick + * the default with `duckdb_v2_instance_set_default()`, or connect with `duckdb_v2_connection_create()`. To choose an + * option that cannot change once the instance exists, such as access_mode, use + * `duckdb_v2_instance_create_with_options()` instead. The caller destroys the instance via + * `duckdb_v2_instance_destroy()`. * * history: * - stable: v2.0.0 * - * @param left The first qualified name. - * @param right The second qualified name. - * @param result Receives whether the two names are equal. + * @param env The environment. + * @param out_instance Receives the new instance handle. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_qname_equals(duckdb_v2_qname_handle left, duckdb_v2_qname_handle right, - bool *result, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_instance_create(duckdb_v2_environment_handle env, + duckdb_v2_instance_handle *out_instance, + duckdb_v2_error_info_handle *err); /*! - * Hashes a qualified name. + * Creates an instance under the environment, configured with startup options. * - * Consistent with `duckdb_v2_qname_equals()`: names that compare equal hash equal, casing differences included. The - * value is not stable across processes or library versions, so use it for in-process lookup tables only and never - * persist it. + * Behaves as `duckdb_v2_instance_create()`, applying the startup options first. + * + * Startup options are (name, value) pairs in two parallel arrays, the values in the textual form SQL `SET` accepts. + * They are the only way to choose an option that cannot change once the instance exists, such as access_mode or + * allow_unsigned_extensions; every other option can be written now or later through `duckdb_v2_instance_get_config()`. + * An option that needs a running instance (the logging options, allowed_directories, allowed_paths, + * disabled_filesystems) returns ERROR_INPUT_INVALID here: write it through the instance's config after creation. A name + * no core option or loaded extension defines is kept for an extension to consume while the instance starts up, and + * creation fails with the names none claimed. The caller destroys the instance via `duckdb_v2_instance_destroy()`. * * history: * - stable: v2.0.0 * - * @param name The qualified name to hash. - * @param hash Receives the hash value. + * @param env The environment. + * @param option_names Optional. An array of option_count option names (canonical or alias). Pass NULL when option_count + * is 0. + * @param option_values Optional. An array of option_count values, parallel to option_names, in the textual form SQL + * `SET` accepts. Pass NULL when option_count is 0. + * @param option_count The number of startup options. + * @param out_instance Receives the new instance handle. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_qname_hash(duckdb_v2_qname_handle name, uint64_t *hash, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_instance_create_with_options( + duckdb_v2_environment_handle env, const duckdb_v2_str *option_names, const duckdb_v2_str *option_values, + idx_t option_count, duckdb_v2_instance_handle *out_instance, duckdb_v2_error_info_handle *err); /*! - * Destroys the qualified name, releasing its resources. + * Destroys the instance handle. * - * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent - * double-destruction. Any part views borrowed from it become dangling. + * Always succeeds. Every attached database is closed with the instance. The instance itself stays alive for as long as + * any connection still references it; when the last reference drops, it is destroyed and its database files become + * available to open again. On success the handle is set to null. Safe to call on an already-null slot. * * history: * - stable: v2.0.0 * - * @param name The qualified name to destroy. + * @param instance The instance handle to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_qname_destroy(duckdb_v2_qname_handle *name); - -/* --- Struct definitions for qname --- */ - -/* ============================================================================ - * MODULE: schema - * ============================================================================ */ - -/* --- Enums for schema --- */ - -/* --- Struct forward declarations for schema --- */ - -/* --- Types for schema --- */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_instance_destroy(duckdb_v2_instance_handle *instance); /*! - * An owned, ordered list of (name, type) fields. Read with schema_get_count and schema_get_field. Destroy via - * schema_destroy. + * Attaches a database to the instance. + * + * Attaches the database at `path` exactly like `ATTACH 'path'`: + * - `:memory:` or an empty view attaches a fresh in-memory database. + * - any other path attaches that file, creating it if it does not exist. + * `name` is the name to attach under, like `ATTACH ... AS name`; it is how `duckdb_v2_instance_detach()` and + * `duckdb_v2_instance_set_default()` refer to the database. Null, or an empty view, derives the name from the path the + * way SQL `ATTACH` without `AS` does: + * - `memory` for `:memory:` or an empty view; + * - otherwise the file name up to its first `.`, without any `?query` parameters (`/data/sales.2024.db` attaches as + * `sales`), with `_db` appended when that is a reserved name such as `system` or `temp`. Naming is how two files with + * the same base name, or `:memory:` twice, attach side by side. `options` may be null; it carries the `(KEY value)` + * options of SQL `ATTACH`, for per-database options such as READ_ONLY, BLOCK_SIZE or ENCRYPTION_KEY, and must have been + * created from this instance handle. `make_default` makes the attached database the default for connections created + * afterwards, exactly as a `duckdb_v2_instance_set_default()` call right after the attach would; otherwise the default + * is left alone. A path that is already attached on this or any other instance of the environment returns + * ERROR_RESOURCE_IN_USE, and a name that is already attached fails. Options that only apply at startup are passed to + * `duckdb_v2_instance_create_with_options()`. + * + * history: + * - stable: v2.0.0 + * + * @param instance The instance handle. + * @param name Optional. The name to attach under; null or an empty view for the name derived from the path. Borrowed + * and copied. + * @param path Path to the database file, or `:memory:` / an empty view for an in-memory database. + * @param options Optional attach options created from `instance`, or null to attach with defaults under the derived + * name. + * @param make_default Whether to make the attached database the default for connections created afterwards. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR */ -typedef struct _duckdb_v2_schema { - void *internal_ptr; -} * duckdb_v2_schema_handle; - -/* --- Constants for schema --- */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_instance_attach(duckdb_v2_instance_handle instance, + const duckdb_v2_identifier_t *name, const duckdb_v2_str *path, + duckdb_v2_attach_options_handle options, bool make_default, + duckdb_v2_error_info_handle *err); -/* --- Function pointer typedefs for schema --- */ +/*! + * Detaches the database attached under `name`. + * + * Detaches it like `DETACH`, checkpointing a file database first. Unlike SQL, the default database may be detached too; + * new connections then start without a default until `duckdb_v2_instance_set_default()` names another, and existing + * connections bound to it keep it alive until their transaction on it ends and fail unqualified DDL afterwards. Returns + * ERROR_INPUT_INVALID when no database is attached under that name. Connections that still reference the database keep + * it alive until they release it. + * + * history: + * - stable: v2.0.0 + * + * @param instance The instance handle. + * @param name The name the database is attached under. Borrowed. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_instance_detach(duckdb_v2_instance_handle instance, + const duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err); -/* --- Functions for schema --- */ +/*! + * Makes the database attached under `name` the default database for connections created from now on. + * + * A connection binds to the default database when it is created, so this does not retarget existing connections: their + * default stays what it was when they connected, or what they chose with `USE`. The default is where unqualified DDL + * and unqualified table lookups that miss the temporary catalog go. It stays the default for new connections until + * another `duckdb_v2_instance_set_default()` call or until it is detached; a connection whose default has been detached + * fails unqualified DDL with a message saying so until it selects another with `USE`. Returns ERROR_INPUT_INVALID when + * no database is attached under that name. + * + * history: + * - stable: v2.0.0 + * + * @param instance The instance handle. + * @param name The name the database is attached under. Borrowed. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_instance_set_default(duckdb_v2_instance_handle instance, + const duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err); /*! - * Returns the number of fields in a schema. + * Creates an empty set of attach options for use with `duckdb_v2_instance_attach()` on `instance`. + * + * Starts empty. The caller destroys it via `duckdb_v2_attach_options_destroy()`; the options may be destroyed as soon + * as the attach call returns, and reused for further attaches before that. * * history: * - stable: v2.0.0 * - * @param schema The schema. - * @param out_count Receives the field count. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param instance The instance handle the options will be used with. + * @param out_options Receives the new options handle. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_schema_get_count(duckdb_v2_schema_handle schema, idx_t *out_count, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_attach_options_create(duckdb_v2_instance_handle instance, + duckdb_v2_attach_options_handle *out_options, + duckdb_v2_error_info_handle *err); /*! - * Borrows the name and type of the field at the given index. + * Sets one attach option, like a `(KEY value)` entry of SQL `ATTACH`. * - * out_name and out_type are valid only until the schema is destroyed, and out_type must not be destroyed. An - * out-of-range index is rejected with ERROR_INPUT_INVALID. + * The key is matched case-insensitively, as an unquoted SQL identifier is. The setting is passed on as the text a + * quoted SQL literal would produce: the engine casts the options it knows (READ_ONLY, RECOVERY_MODE, TYPE, + * DEFAULT_TABLE, VACUUM_REBUILD_INDEXES, BLOCK_SIZE, ENCRYPTION_KEY, ...) and hands the rest to the storage extension + * that ends up owning the database, which decides what they mean. Keys must be valid UTF-8; an unknown or ill-typed + * option fails the attach. Both views are borrowed and copied. Setting the same key again replaces it. * * history: * - stable: v2.0.0 * - * @param schema The schema. - * @param index Zero-based field index. - * @param out_name Receives a borrowed view of the field name. Valid until the schema is destroyed. - * @param out_type Receives a borrowed field type. Valid until the schema is destroyed; do not destroy it. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param options The attach options. + * @param key The option name, e.g. READ_ONLY. + * @param setting The option value, in the textual form a quoted SQL literal would carry. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_schema_get_field(duckdb_v2_schema_handle schema, idx_t index, - duckdb_v2_identifier_t *out_name, - duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_attach_options_set(duckdb_v2_attach_options_handle options, + const duckdb_v2_identifier_t *key, + const duckdb_v2_str *setting, + duckdb_v2_error_info_handle *err); /*! - * Destroys a schema handle. + * Destroys an attach options handle. * - * Frees the handle along with the names and types it owns. On success the slot is set to null. Safe to call on an - * already-null slot. + * On success the slot is set to null. Safe to call on an already-null slot. * * history: * - stable: v2.0.0 * - * @param schema The schema handle to destroy. + * @param options The attach options to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_schema_destroy(duckdb_v2_schema_handle *schema); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_attach_options_destroy(duckdb_v2_attach_options_handle *options); -/* --- Struct definitions for schema --- */ +/*! + * Returns the version of the linked DuckDB library. + * + * history: + * - stable: v2.0.0 + * + * @param out_version The version string. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_library_version(duckdb_v2_str *out_version, duckdb_v2_error_info_handle *err); + +/* --- Struct definitions for instance --- */ /* ============================================================================ - * MODULE: vector + * MODULE: logging * ============================================================================ */ -/* --- Enums for vector --- */ +/* --- Enums for logging --- */ /*! - * Internal representation of a vector, with FSST / SEQUENCE / SHREDDED collapsed into OTHER: the view getter rejects - * those and requires an explicit vector_flatten first. OTHER is the 0-value, so a zero-initialized out-param reads as - * "unspecified, needs flatten" rather than silently looking like FLAT. + * Severity of a log message. Mirrors DuckDB's own log levels, and is compared against the configured threshold: an + * entry below it is dropped. */ -typedef enum DUCKDB_V2_VECTOR_TYPE { - //! Default for zero-init. Covers FSST / SEQUENCE / SHREDDED — call vector_flatten first. - DUCKDB_V2_VECTOR_TYPE_OTHER = 0, - - //! Standard per-row storage. - DUCKDB_V2_VECTOR_TYPE_FLAT = 1, - - //! A single value applies to every row in the vector. - DUCKDB_V2_VECTOR_TYPE_CONSTANT = 2, - - //! Data + selection vector indirection into another vector. - DUCKDB_V2_VECTOR_TYPE_DICTIONARY = 3, - DUCKDB_V2_VECTOR_TYPE_MAX_ENUM = 0x7FFFFFFF, -} DUCKDB_V2_VECTOR_TYPE; +typedef enum DUCKDB_V2_LOG_LEVEL { + //! Finest detail; off in any default configuration. + DUCKDB_V2_LOG_LEVEL_TRACE = 10, -/* --- Struct forward declarations for vector --- */ + //! Diagnostic detail. + DUCKDB_V2_LOG_LEVEL_DEBUG = 20, -typedef struct duckdb_v2_vector_view duckdb_v2_vector_view; + //! Ordinary progress information. + DUCKDB_V2_LOG_LEVEL_INFO = 30, -typedef struct duckdb_v2_list_entry duckdb_v2_list_entry; + //! A recoverable problem. + DUCKDB_V2_LOG_LEVEL_WARNING = 40, -typedef struct duckdb_v2_hugeint_t duckdb_v2_hugeint_t; + //! An operation failed. + DUCKDB_V2_LOG_LEVEL_ERROR = 50, -typedef struct duckdb_v2_uhugeint_t duckdb_v2_uhugeint_t; + //! An unrecoverable failure. + DUCKDB_V2_LOG_LEVEL_FATAL = 60, + DUCKDB_V2_LOG_LEVEL_MAX_ENUM = 0x7FFFFFFF, +} DUCKDB_V2_LOG_LEVEL; -typedef struct duckdb_v2_interval_t duckdb_v2_interval_t; +/* --- Struct forward declarations for logging --- */ -/* --- Types for vector --- */ +/* --- Types for logging --- */ -/* --- Constants for vector --- */ +/* --- Constants for logging --- */ -/* --- Function pointer typedefs for vector --- */ +/* --- Function pointer typedefs for logging --- */ -/* --- Functions for vector --- */ +/* --- Functions for logging --- */ /*! - * Returns the internal representation kind: flat, constant, or dictionary. + * Writes a message to DuckDB's log. * - * Reports VECTOR_TYPE_OTHER for FSST / SEQUENCE / SHREDDED vectors, which must be passed through vector_flatten before - * vector_get_view will read them. + * The entry is attributed to the context's connection scope, so it carries that connection's ids. Whether it is + * recorded at all is up to the database's log configuration: if logging is off, or level is below the configured + * threshold, or log_type is not among the enabled types, the call succeeds and writes nothing. An empty log_type + * selects the default type. Read entries back with `SELECT * FROM duckdb_logs`. * * history: * - stable: v2.0.0 * - * @param vector The vector. - * @param out_type Receives the representation kind. + * @param ctx The context to log through. + * @param level Severity of the message. + * @param log_type The log type to record under, matched case-sensitively. Empty selects the default type. + * @param message The message body. Borrowed for the call only. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_get_vector_type(duckdb_v2_vector_handle vector, - DUCKDB_V2_VECTOR_TYPE *out_type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_context_log(duckdb_v2_context_handle ctx, DUCKDB_V2_LOG_LEVEL level, + const duckdb_v2_str *log_type, const duckdb_v2_str *message, + duckdb_v2_error_info_handle *err); /*! - * Returns the vector's logical type. + * Writes a message to DuckDB's log. * - * The returned logical type is the same type that was passed to vector_reference, or the type that was set on creation. - * It is caller-owned; destroy it via logical_type_destroy. + * The entry is attributed to the database scope: it carries no connection or query ids. Whether it is recorded at all + * is up to the database's log configuration: if logging is off, or level is below the configured threshold, or log_type + * is not among the enabled types, the call succeeds and writes nothing. An empty log_type selects the default type. + * Read entries back with `SELECT * FROM duckdb_logs`. * * history: * - stable: v2.0.0 * - * @param vector The vector. - * @param out_type Receives the caller-owned logical type handle. + * @param instance The instance to log through. + * @param level Severity of the message. + * @param log_type The log type to record under, matched case-sensitively. Empty selects the default type. + * @param message The message body. Borrowed for the call only. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_get_logical_type(duckdb_v2_vector_handle vector, - duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_instance_log(duckdb_v2_instance_handle instance, DUCKDB_V2_LOG_LEVEL level, + const duckdb_v2_str *log_type, const duckdb_v2_str *message, + duckdb_v2_error_info_handle *err); /*! - * Reads a vector as a unified view of data, validity, selection, and count. - * - * Populates out_view with borrowed pointers, valid until the owning chunk is destroyed. view.data addresses the - * vector's elements, to be cast to the layout its logical type implies: a typed leaf array for the primitives, - * hugeint_t / uhugeint_t / interval_t for those kinds, bytes for VARCHAR / BLOB / BIT / BIGNUM, list_entry for LIST and - * MAP, and nothing at all for ARRAY / STRUCT / UNION, whose data lives in their children. - * - * DECIMAL and ENUM are typed leaf arrays too, over a storage tier that follows from the type; both mappings are - * contract. A DECIMAL of width <= 4 stores int16, <= 9 int32, <= 18 int64, and <= 38 int128, boundaries inclusive, with - * the width and scale available from logical_type_get_param. An ENUM stores its dictionary indices as uint8 for a - * dictionary of at most 255 entries, uint16 for at most 65535, and uint32 beyond that, boundaries inclusive and an - * empty dictionary uint8; logical_type_get_param resolves an index to its entry. - * - * Rejects VECTOR_TYPE_OTHER; call vector_flatten first. + * Writes a message to DuckDB's log. * - * Reading a DICTIONARY vector has one side effect: the dictionary's underlying child is flattened in place if it is not - * already FLAT. The parent vector stays DICTIONARY, and only the child's storage shape changes, but any pointer - * previously borrowed into that child — from an earlier vector_get_view on a vector aliasing the same buffer, say — is - * invalidated. This is the only mutation vector_get_view performs; compare vector_flatten, which materializes the whole - * vector to FLAT. + * The entry is attributed to the connection scope, so it carries the connection's ids. Whether it is recorded at all is + * up to the database's log configuration: if logging is off, or level is below the configured threshold, or log_type is + * not among the enabled types, the call succeeds and writes nothing. An empty log_type selects the default type. Read + * entries back with `SELECT * FROM duckdb_logs`. * * history: * - stable: v2.0.0 * - * @param vector The vector to read. - * @param out_view Receives the view. Its pointers are borrowed and valid until the owning chunk is destroyed. + * @param conn The connection to log through. + * @param level Severity of the message. + * @param log_type The log type to record under, matched case-sensitively. Empty selects the default type. + * @param message The message body. Borrowed for the call only. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_get_view(duckdb_v2_vector_handle vector, duckdb_v2_vector_view *out_view, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_log(duckdb_v2_connection_handle conn, DUCKDB_V2_LOG_LEVEL level, + const duckdb_v2_str *log_type, const duckdb_v2_str *message, + duckdb_v2_error_info_handle *err); -/*! - * Returns the number of elements in the vector. - * - * The size is the number of logical elements the vector represents, which for a nested kind can differ from the size of - * its children. An ARRAY vector of size 10 whose array size is 3, for instance, has 10 logical elements while its - * single child vector has 30. - * - * history: - * - stable: v2.0.0 - * - * @param vector The vector. - * @param out_size Receives the number of elements. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_get_size(duckdb_v2_vector_handle vector, idx_t *out_size, - duckdb_v2_error_info_handle *err); +/* --- Struct definitions for logging --- */ + +/* ============================================================================ + * MODULE: qname + * ============================================================================ */ + +/* --- Enums for qname --- */ + +/* --- Struct forward declarations for qname --- */ + +/* --- Types for qname --- */ /*! - * Sets the number of elements in the vector. - * - * The counterpart of vector_get_size: it declares how many logical elements the vector now holds, and does not allocate - * or initialize anything. - * - * history: - * - stable: v2.0.0 - * - * @param vector The vector. - * @param size The new number of logical elements. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR + * An owned qualified name: an ordered path of one to three non-empty identifier parts whose last element is the object + * name. Construct with `duckdb_v2_qname_parse()` or `duckdb_v2_qname_create()`, read with + * `duckdb_v2_qname_get_part_count()` and `duckdb_v2_qname_get_part()`, render back to SQL with + * `duckdb_v2_qname_render()`, compare with `duckdb_v2_qname_equals()`, and destroy with `duckdb_v2_qname_destroy()`. + * Two handles are compared with `duckdb_v2_qname_equals()`, never by pointer. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_set_size(duckdb_v2_vector_handle vector, idx_t size, - duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_qname { + void *internal_ptr; +} * duckdb_v2_qname_handle; + +/* --- Constants for qname --- */ + +/* --- Function pointer typedefs for qname --- */ + +/* --- Functions for qname --- */ /*! - * Reads one cell of a vector as an owned value. - * - * The total fallback reader. It reads any logical row of any vector representation without flattening — FLAT, CONSTANT, - * DICTIONARY, and the compressed kinds the view getter rejects — and covers every type kind, including those with no - * committed view layout (today VARIANT and GEOMETRY), where it is the only way to reach a cell. + * Parses SQL text into a qualified name. * - * Each call allocates one owned value, which makes this a single-cell bridge rather than a per-row loop primitive; use - * vector_get_view on hot paths. The row index is logical and bounds-checked against vector_get_size. The returned value - * is caller-owned; destroy it via value_destroy. + * Applies the engine's qualified-name rules: dots separate parts, and a double-quoted part may contain dots and doubled + * interior quotes. More than three parts and an unterminated quote are rejected with the parser's own error. Invalid + * UTF-8 and text without at least one non-empty part are rejected with `ERROR_INPUT_INVALID`. When the parts are + * already separate, build the name with `duckdb_v2_qname_create()` rather than joining them and parsing the result. * * history: * - stable: v2.0.0 * - * @param vector The vector to read from. - * @param row The logical row index, in [0, vector_get_size). - * @param out_value Receives the owned cell value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param text The name text to parse. Borrowed for the call only. + * @param name On success, receives the qualified name. Owned by the caller; destroy via `duckdb_v2_qname_destroy()`. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_get_value(duckdb_v2_vector_handle vector, idx_t row, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_qname_parse(const duckdb_v2_str *text, duckdb_v2_qname_handle *name, + duckdb_v2_error_info_handle *err); /*! - * Writes one cell of a FLAT vector from a value. - * - * The total fallback writer. It covers every type kind, including those with no committed view layout (today VARIANT - * and GEOMETRY), where it is the only way to write a cell. The value is borrowed and copied in, then cast to the - * vector's type through the non-strict default cast; a cast failure surfaces from the call. A NULL value clears the - * row's validity. + * Creates a qualified name from its parts. * - * Requires a FLAT vector: constant, dictionary, and compressed representations are not row-addressable, so call - * vector_flatten first. Each call performs one value write, which makes this a single-cell bridge rather than a per-row - * loop primitive; use the typed mutable-data paths on hot paths. The row index is logical and bounds-checked against - * vector_get_size. + * The parts are ordered outermost first, so the last one is the object name. Between one and three parts are accepted + * -- the engine qualifies at most catalog.schema.name today -- and every part must be non-empty: partial qualification + * is expressed by passing fewer parts, never by empty placeholders. Zero parts, more than three, and an empty part are + * all rejected with `ERROR_INPUT_INVALID`. The parts are borrowed and copied. * * history: * - stable: v2.0.0 * - * @param vector The vector to write to. - * @param row The logical row index, in [0, vector_get_size). - * @param value The borrowed value to write. Copied in; cast to the vector's type. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param parts An array of `part_count` non-empty identifier views, outermost first. + * @param part_count The number of parts, between one and three. + * @param name On success, receives the qualified name. Owned by the caller; destroy via `duckdb_v2_qname_destroy()`. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_set_value(duckdb_v2_vector_handle vector, idx_t row, - duckdb_v2_value_handle value, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_qname_create(const duckdb_v2_identifier_t *parts, idx_t part_count, + duckdb_v2_qname_handle *name, duckdb_v2_error_info_handle *err); /*! - * Returns a mutable pointer to the data of a FLAT or CONSTANT vector. + * Returns how many parts the qualified name has. * - * Any other representation returns ERROR_INPUT_INVALID. The pointer is valid until the owning chunk is destroyed, and - * the vector's storage shape must not change — through a flatten, say — while it is in use. + * Always at least one. Valid indices for `duckdb_v2_qname_get_part()` are [0, count), and the object name is the part + * at count - 1. * * history: * - stable: v2.0.0 * - * @param vector The vector. - * @param out_data Receives a mutable pointer to the data. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param name The qualified name. + * @param count Receives the number of parts. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_get_data_mutable(duckdb_v2_vector_handle vector, void **out_data, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_qname_get_part_count(duckdb_v2_qname_handle name, idx_t *count, + duckdb_v2_error_info_handle *err); /*! - * Forces a vector into FLAT representation, in place. + * Borrows one part of the qualified name. * - * A no-op for a vector that is already FLAT. For a CONSTANT, DICTIONARY, FSST, SEQUENCE, or SHREDDED vector it - * materializes the per-row data and switches the representation to FLAT. Call it before vector_get_view if you would - * rather not handle CONSTANT and DICTIONARY semantics in the view; it is required for any vector reporting - * VECTOR_TYPE_OTHER. + * Parts are ordered outermost first, so the object name is the part at `duckdb_v2_qname_get_part_count()` - 1. The view + * is valid until the qualified name is destroyed. An index outside [0, count) is rejected with + * `ERROR_INPUT_OUT_OF_RANGE`. * * history: * - stable: v2.0.0 * - * @param vector The vector to flatten. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param name The qualified name. + * @param index Zero-based part index. + * @param part Receives a borrowed view of the part, valid until the qualified name is destroyed. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_flatten(duckdb_v2_vector_handle vector, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_qname_get_part(duckdb_v2_qname_handle name, idx_t index, + duckdb_v2_identifier_t *part, duckdb_v2_error_info_handle *err); /*! - * Repoints a vector at another vector's data and type, without copying. + * Renders the qualified name as SQL text. + * + * Joins the parts with dots, quoting and escaping each one only where the identifier requires it, so the result parses + * back through `duckdb_v2_qname_parse()` to an equal name. + * + * Writes into a caller-supplied buffer, so nothing is allocated on the caller's behalf and nothing has to be freed. + * Pass out_text = NULL to size the buffer without rendering into it: out_length then receives the length, and + * out_capacity is ignored. With out_text != NULL, out_capacity must be at least out_length + 1, or the call returns + * ERROR_INPUT_OBJECT_SIZE with out_length set to the required length and out_text left untouched. * - * `vector` takes on the storage and logical type of `source`: no data moves, and the two alias the same buffers until - * one of them is reset or re-referenced. Works for any type, nested included. The source's data must outlive every read - * of `vector`. Use it to hand an already-materialized vector — a chunk column produced by arrow_array_to_data_chunk, - * say — straight to an output vector without a per-row copy. + * out_length never counts the terminator, but a successful write always appends one, so the buffer is usable as a C + * string. * * history: * - stable: v2.0.0 * - * @param vector The vector to repoint at the source's data. - * @param source The vector whose data and logical type to reference. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param name The qualified name to render. + * @param out_text Caller-owned buffer receiving the text plus a null terminator, or NULL to only report the required + * length in out_length. + * @param out_capacity Bytes available in out_text, terminator included. Ignored when out_text is NULL. + * @param out_length Receives the text length excluding the null terminator — written on success and on + * ERROR_INPUT_OBJECT_SIZE. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_reference(duckdb_v2_vector_handle vector, duckdb_v2_vector_handle source, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_qname_render(duckdb_v2_qname_handle name, char *out_text, idx_t out_capacity, + idx_t *out_length, duckdb_v2_error_info_handle *err); /*! - * Turns the vector into a CONSTANT vector holding the given value. + * Compares two qualified names. * - * Afterwards the vector holds a single element that applies to every logical row; write it through - * vector_get_data_mutable, which returns a pointer to that one element. For a STRUCT vector the change propagates to - * every child. The value's logical type must equal the vector's; a mismatch returns ERROR_INPUT_INVALID. + * True when both have the same number of parts and every part matches case-insensitively, which is the engine's own + * identifier equality: casing never distinguishes two names. This is the only way to compare names; two handles holding + * equal names are still distinct pointers. * * history: * - stable: v2.0.0 * - * @param vector The vector to make constant. - * @param value The value every row takes. Its logical type must equal the vector's. - * @param count The number of elements in the vector. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param left The first qualified name. + * @param right The second qualified name. + * @param result Receives whether the two names are equal. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_make_constant(duckdb_v2_vector_handle vector, - duckdb_v2_value_handle value, idx_t count, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_qname_equals(duckdb_v2_qname_handle left, duckdb_v2_qname_handle right, + bool *result, duckdb_v2_error_info_handle *err); /*! - * Sets a vector to a SEQUENCE representation. + * Hashes a qualified name. * - * Afterwards the vector represents the arithmetic sequence start, start+increment, start+2*increment, ... over count - * elements. No data pointer is involved; the three parameters describe the sequence completely. + * Consistent with `duckdb_v2_qname_equals()`: names that compare equal hash equal, casing differences included. The + * value is not stable across processes or library versions, so use it for in-process lookup tables only and never + * persist it. * * history: * - stable: v2.0.0 * - * @param vector The vector to turn into a sequence. - * @param start The first value of the sequence. - * @param increment The step between consecutive values. - * @param count The number of elements in the sequence. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param name The qualified name to hash. + * @param hash Receives the hash value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_make_sequence(duckdb_v2_vector_handle vector, int64_t start, - int64_t increment, idx_t count, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_qname_hash(duckdb_v2_qname_handle name, uint64_t *hash, + duckdb_v2_error_info_handle *err); /*! - * Sets one row of a FLAT vector to NULL, nested children included. - * - * Clears the row's validity and broadcasts the NULL recursively to every descendant element under that row — STRUCT - * fields, and ARRAY elements strided by the array size — maintaining DuckDB's nested NULL invariant: when a STRUCT or - * ARRAY row is NULL, every descendant element under it must be NULL too. LIST children are left untouched, since their - * consumers gate on the list's own validity. Which kinds propagate is normative DuckDB behavior that evolves with the - * type system, which makes this the total NULL write path: equivalent to vector_set_value with a NULL value, without - * the per-call value allocation. + * Destroys the qualified name, releasing its resources. * - * Requires a FLAT vector: constant, dictionary, and compressed representations are not row-addressable, so call - * vector_flatten first. The row index is logical and bounds-checked against vector_get_size. + * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent + * double-destruction. Any part views borrowed from it become dangling. * * history: * - stable: v2.0.0 * - * @param vector The vector to write to. - * @param row The logical row index, in [0, vector_get_size). - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param name The qualified name to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_set_null(duckdb_v2_vector_handle vector, idx_t row, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_qname_destroy(duckdb_v2_qname_handle *name); + +/* --- Struct definitions for qname --- */ + +/* ============================================================================ + * MODULE: schema + * ============================================================================ */ + +/* --- Enums for schema --- */ + +/* --- Struct forward declarations for schema --- */ + +/* --- Types for schema --- */ /*! - * Returns a mutable pointer to the validity mask of a FLAT vector. - * - * Allocates the mask on first use, so the returned pointer is always non-null. Each uint64_t word covers 64 rows: bit N - * of word W is row W*64+N. A set bit means valid; a cleared bit means NULL. - * - * A raw mask write marks only this vector's rows. Clearing a STRUCT or ARRAY parent bit leaves the descendant elements - * untouched, which violates DuckDB's nested NULL invariant — a NULL STRUCT or ARRAY row requires NULL descendants — so - * set nested rows NULL through vector_set_null instead. When writing nested masks raw anyway, invalidate the child - * masks up front and mark elements valid as the values are written. + * An owned, ordered list of (name, type) fields. Read with schema_get_count and schema_get_field. Destroy via + * schema_destroy. + */ +typedef struct _duckdb_v2_schema { + void *internal_ptr; +} * duckdb_v2_schema_handle; + +/* --- Constants for schema --- */ + +/* --- Function pointer typedefs for schema --- */ + +/* --- Functions for schema --- */ + +/*! + * Returns the number of fields in a schema. * * history: * - stable: v2.0.0 * - * @param vector The vector. - * @param out_validity Receives a mutable pointer to the validity mask. + * @param schema The schema. + * @param out_count Receives the field count. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_flat_get_validity_mutable(duckdb_v2_vector_handle vector, - uint64_t **out_validity, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_schema_get_count(duckdb_v2_schema_handle schema, idx_t *out_count, + duckdb_v2_error_info_handle *err); /*! - * Sets the validity of a CONSTANT vector. + * Borrows the name and type of the field at the given index. * - * A constant vector holds a single element, so one flag decides whether every row reads as the value or as NULL. + * out_name and out_type are valid only until the schema is destroyed, and out_type must not be destroyed. An + * out-of-range index is rejected with ERROR_INPUT_INVALID. * * history: * - stable: v2.0.0 * - * @param vector The constant vector. - * @param validity True to mark the vector's single element valid, false to mark it NULL. + * @param schema The schema. + * @param index Zero-based field index. + * @param out_name Receives a borrowed view of the field name. Valid until the schema is destroyed. + * @param out_type Receives a borrowed field type. Valid until the schema is destroyed; do not destroy it. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_constant_set_valid(duckdb_v2_vector_handle vector, bool validity, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_schema_get_field(duckdb_v2_schema_handle schema, idx_t index, + duckdb_v2_identifier_t *out_name, + duckdb_v2_logical_type_handle *out_type, + duckdb_v2_error_info_handle *err); /*! - * Borrows a string-backed vector's arena for writing. + * Destroys a schema handle. * - * Allocates the arena on first use. Valid for VARCHAR, BLOB, BIT, and BIGNUM vectors, and returns ERROR_INPUT_INVALID - * for anything else. This call is the single string-ness check, which is why arena_allocate itself performs none. The - * arena is borrowed — never destroy it — and stays valid until the vector is flattened, reallocated, or destroyed. + * Frees the handle along with the names and types it owns. On success the slot is set to null. Safe to call on an + * already-null slot. * * history: * - stable: v2.0.0 * - * @param vector The string-backed vector whose arena to borrow. - * @param out_arena Receives the borrowed arena handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param schema The schema handle to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_get_arena(duckdb_v2_vector_handle vector, - duckdb_v2_arena_handle *out_arena, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_schema_destroy(duckdb_v2_schema_handle *schema); + +/* --- Struct definitions for schema --- */ + +/* ============================================================================ + * MODULE: tokenizer + * ============================================================================ */ + +/* --- Enums for tokenizer --- */ + +//! The lexical class of a token. +typedef enum DUCKDB_V2_TOKEN_TYPE { + //! Not an actual token class; out_type is set to this when `duckdb_v2_token_iterator_next()` fails. + DUCKDB_V2_TOKEN_TYPE_INVALID = 0, + + //! A keyword of the connection's grammar. + DUCKDB_V2_TOKEN_TYPE_KEYWORD = 1, + + //! A bare or double-quoted identifier, quotes included. + DUCKDB_V2_TOKEN_TYPE_IDENTIFIER = 2, + + //! A quoted or dollar-quoted string, delimiters included. + DUCKDB_V2_TOKEN_TYPE_STRING_LITERAL = 3, + + //! A numeric literal. + DUCKDB_V2_TOKEN_TYPE_NUMBER_LITERAL = 4, + + //! An operator or punctuation run other than the statement terminator. + DUCKDB_V2_TOKEN_TYPE_OPERATOR = 5, + + //! A line or block comment, delimiters included. + DUCKDB_V2_TOKEN_TYPE_COMMENT = 6, + + //! A statement-terminating semicolon. + DUCKDB_V2_TOKEN_TYPE_TERMINATOR = 7, + + /*! + * Not a token; reported by token_iterator_next once the input is exhausted, with start equal to the input length + * and length 0. + */ + DUCKDB_V2_TOKEN_TYPE_END_OF_INPUT = 8, + DUCKDB_V2_TOKEN_TYPE_MAX_ENUM = 0x7FFFFFFF, +} DUCKDB_V2_TOKEN_TYPE; + +/* --- Struct forward declarations for tokenizer --- */ + +/* --- Types for tokenizer --- */ + +//! An opaque, owned handle to an iterator over the tokens of a SQL string, produced by tokenize_sql. +typedef struct _duckdb_v2_token_iterator { + void *internal_ptr; +} * duckdb_v2_token_iterator_handle; + +/* --- Constants for tokenizer --- */ + +/* --- Function pointer typedefs for tokenizer --- */ + +/* --- Functions for tokenizer --- */ /*! - * Returns the number of child vectors a nested vector exposes. + * Tokenizes a SQL string into an iterator over its tokens. * - * 1 for LIST and ARRAY; 2 for MAP, the keys and the values; the field count for STRUCT; member_count + 1 for UNION, the - * tag plus the members; and 0 for any non-nested kind. vector_get_child documents what each index addresses. + * Lexical tokenization, in the context of whatever grammar extensions are loaded on the given connection. Closing the + * connection or changing settings afterwards does not affect the tokens. The SQL string is borrowed for the call only, + * the caller may free it once this call returns. Whitespace is not a token. Malformed input is not an error; + * `duckdb_v2_token_iterator_ends_unterminated()` reports whether the input ended inside an open token. + * + * *out_iterator is set to NULL on failure. * * history: * - stable: v2.0.0 * - * @param vector The vector. - * @param out_count Receives the number of child vectors. + * @param conn The connection supplying the grammar. + * @param sql The SQL text. Borrowed for the call only; may contain interior null bytes. {NULL, 0} is the empty input. + * @param out_iterator Receives the new iterator handle. Destroy via token_iterator_destroy. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_get_child_count(duckdb_v2_vector_handle vector, idx_t *out_count, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_tokenize_sql(duckdb_v2_connection_handle conn, const duckdb_v2_str *sql, + duckdb_v2_token_iterator_handle *out_iterator, + duckdb_v2_error_info_handle *err); /*! - * Borrows a child vector by index. - * - * What each index addresses, by kind: LIST and ARRAY expose [0] = the elements; MAP [0] = keys and [1] = values, hiding - * its internal LIST; STRUCT and TUPLE [i] = field i; and UNION [0] = the tag with [1..N] = the members. - * Note that value_get_child diverges on UNION, exposing only the active member. + * Yields the next token, or END_OF_INPUT once exhausted. * - * The returned child is borrowed and lives as long as the owning chunk. Returns ERROR_INPUT_INVALID if the vector has - * no children, or if the index is out of range. + * On success writes the token's class, byte offset and byte length into the out-parameters. The lexeme is the input + * bytes [start, start + length). Once the input is exhausted, every call gives TOKEN_TYPE_END_OF_INPUT with start equal + * to the input length and length 0. On failure out params are set to TOKEN_TYPE_INVALID, 0, 0. * * history: * - stable: v2.0.0 * - * @param vector The nested vector to descend into. - * @param index The child index, in [0, vector_get_child_count). - * @param out_child Receives the borrowed child vector. + * @param iterator The iterator to advance. + * @param out_type Receives the token's class, or TOKEN_TYPE_END_OF_INPUT once exhausted. + * @param out_start Receives the token's byte offset into the input; the input length for END_OF_INPUT. + * @param out_length Receives the token's byte length; 0 for END_OF_INPUT. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_get_child(duckdb_v2_vector_handle vector, idx_t index, - duckdb_v2_vector_handle *out_child, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_token_iterator_next(duckdb_v2_token_iterator_handle iterator, + DUCKDB_V2_TOKEN_TYPE *out_type, idx_t *out_start, + idx_t *out_length, duckdb_v2_error_info_handle *err); /*! - * Decodes BIGNUM storage bytes into a caller-supplied magnitude buffer + sign flag. - * - * in_data and in_length are the value's raw storage bytes, exactly as they sit in a vector's bignum payload or as - * value_get_blob reports them for a BIGNUM value. Those bytes are opaque: the storage encoding is not a committed part - * of this API, and this function is the only thing that interprets them. - * - * Writes the big-endian magnitude bytes into out_data and the sign flag into out_is_negative; reconstruct the integer - * as (-1)**out_is_negative * unsigned_big_endian(out_data[0..out_length]). Materializing the bytes is unavoidable, - * since a negative bignum stores its magnitude bit-inverted, but the buffer belongs to the caller: this function never - * allocates and hands back nothing to free. - * - * Pass out_data = NULL to size the buffer without decoding. out_length then receives the exact number of magnitude - * bytes and out_is_negative the sign, and out_capacity is ignored. The size comes straight out of the storage header, - * so the query is O(1) and exact — a size call followed by a decode call never needs a retry loop. - * - * With out_data != NULL, out_capacity must be at least out_length bytes, or the call returns ERROR_INPUT_OBJECT_SIZE - * with out_length set to the required size and out_data left untouched. On success exactly out_length bytes are - * written, and the rest of the buffer is not modified. + * Reports whether the input ended before the closing delimiter of its last token. * - * Returns ERROR_INPUT_INVALID if in_data is NULL, or if in_length is too short to hold a bignum header. + * True when the input ends inside an open string, quoted identifier, block comment or dollar-quoted string, or in a + * line comment with no trailing newline; false otherwise, including for empty or whitespace-only input. Equivalently, + * the last token `duckdb_v2_token_iterator_next()` yields before TOKEN_TYPE_END_OF_INPUT is the open one, so its class + * and lexeme say which delimiter is missing. A property of the input, not of the iterator position: the answer is the + * same before, during and after draining. The only failure is a null argument. * * history: * - stable: v2.0.0 * - * @param in_data The value's raw BIGNUM storage bytes. - * @param in_length Size of in_data in bytes. - * @param out_data Caller-owned buffer receiving the magnitude bytes, or NULL to only report the required size in - * out_length. - * @param out_capacity Bytes available in out_data. Ignored when out_data is NULL. - * @param out_length Receives the magnitude size in bytes, written on success and on ERROR_INPUT_OBJECT_SIZE. - * @param out_is_negative Receives true if the value is negative, false otherwise. + * @param iterator The iterator. + * @param out_ends_unterminated Receives whether the input ended inside an open token. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_bignum_decode(const uint8_t *in_data, idx_t in_length, uint8_t *out_data, - idx_t out_capacity, idx_t *out_length, bool *out_is_negative, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_token_iterator_ends_unterminated(duckdb_v2_token_iterator_handle iterator, + bool *out_ends_unterminated, + duckdb_v2_error_info_handle *err); /*! - * Encodes a magnitude + sign flag into BIGNUM storage bytes. - * - * The inverse of bignum_decode. in_data and in_length are the big-endian magnitude bytes and is_negative the sign; - * out_data receives the raw storage bytes for that integer, in the form a BIGNUM vector payload expects. Those bytes - * are opaque: the storage encoding is not a committed part of this API. - * - * Pass out_data = NULL to size the buffer without encoding. out_length then receives the exact number of storage bytes, - * and out_capacity is ignored. The size is a fixed function of in_length, so the query is O(1) and exact. - * - * With out_data != NULL, out_capacity must be at least out_length bytes, or the call returns ERROR_INPUT_OBJECT_SIZE - * with out_length set to the required size and out_data left untouched. On success exactly out_length bytes are - * written, and the rest of the buffer is not modified. - * - * The magnitude must be canonical, matching what bignum_decode produces: in_length >= 1 with no leading zero bytes, so - * the value zero is the single byte 0x00 with is_negative = false. A NULL in_data, an in_length of 0, a leading zero - * byte, or a negative zero returns ERROR_INPUT_INVALID; a magnitude beyond the maximum bignum width returns - * ERROR_INPUT_OUT_OF_RANGE. + * Destroys a token iterator handle. * * history: * - stable: v2.0.0 * - * @param in_data Big-endian magnitude bytes, with no leading zero bytes. - * @param in_length Size of in_data in bytes. Must be >= 1. - * @param is_negative True to encode a negative value. - * @param out_data Caller-owned buffer receiving the storage bytes, or NULL to only report the required size in - * out_length. - * @param out_capacity Bytes available in out_data. Ignored when out_data is NULL. - * @param out_length Receives the storage size in bytes — written on success and on ERROR_INPUT_OBJECT_SIZE. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param iterator The iterator to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_bignum_encode(const uint8_t *in_data, idx_t in_length, bool is_negative, - uint8_t *out_data, idx_t out_capacity, idx_t *out_length, - duckdb_v2_error_info_handle *err); - -/* --- Struct definitions for vector --- */ - -struct duckdb_v2_vector_view { - const void *data; - const uint64_t *validity; - const duckdb_v2_sel_t *sel; - idx_t count; -}; - -struct duckdb_v2_list_entry { - idx_t offset; - idx_t length; -}; - -struct duckdb_v2_hugeint_t { - uint64_t lower; - int64_t upper; -}; - -struct duckdb_v2_uhugeint_t { - uint64_t lower; - uint64_t upper; -}; +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_token_iterator_destroy(duckdb_v2_token_iterator_handle *iterator); -struct duckdb_v2_interval_t { - int32_t months; - int32_t days; - int64_t micros; -}; +/* --- Struct definitions for tokenizer --- */ /* ============================================================================ * MODULE: aggregate @@ -3782,10 +4361,10 @@ struct duckdb_v2_interval_t { /*! * An owned opaque handle to a custom aggregate function being built. Created with - * `duckdb_v2_aggregate_function_create_with_connection()` or `duckdb_v2_aggregate_function_create_with_extension()`, - * configured with the setter functions (e.g. `duckdb_v2_aggregate_function_set_name()`, - * `duckdb_v2_aggregate_function_set_update_callback()`, etc.) and the signature obtained via - * `duckdb_v2_aggregate_function_get_signature()`, made available with `duckdb_v2_aggregate_function_register()`, and + * `duckdb_v2_aggregate_function_create()`, configured with the setter functions (e.g. + * `duckdb_v2_aggregate_function_set_name()`, `duckdb_v2_aggregate_function_set_update_callback()`, etc.) and the + * signature obtained via `duckdb_v2_aggregate_function_get_signature()`, made available with + * `duckdb_v2_connection_register_aggregate_function()` or `duckdb_v2_extension_register_aggregate_function()`, and * destroyed with `duckdb_v2_aggregate_function_destroy()`. */ typedef struct _duckdb_v2_aggregate_function { @@ -3793,9 +4372,9 @@ typedef struct _duckdb_v2_aggregate_function { } * duckdb_v2_aggregate_function_handle; /*! - * A borrowed opaque handle to the arguments supplied to an aggregate function during the query preparation "bind" - * phase. The "bind" callback receives this handle and can use it to e.g. inspect the arguments given to the function, - * initialize some constant state and set the return type. + * A borrowed opaque handle to the result of an aggregate function's "bind" phase. The "bind" callback receives this + * handle next to a `duckdb_v2_function_bind_info_handle`, which gives access to the arguments, the user data and the + * bind data, and can use it to set the return type of the call site being bound. */ typedef struct _duckdb_v2_aggregate_function_bind_info { void *internal_ptr; @@ -3857,7 +4436,8 @@ typedef struct _duckdb_v2_aggregate_function_destroy_info { /* --- Function pointer typedefs for aggregate --- */ -typedef void (*duckdb_v2_aggregate_function_bind_callback_fn)(duckdb_v2_aggregate_function_bind_info_handle info, +typedef void (*duckdb_v2_aggregate_function_bind_callback_fn)(duckdb_v2_function_bind_info_handle info, + duckdb_v2_aggregate_function_bind_info_handle result, duckdb_v2_context_handle context, duckdb_v2_error_info_handle *err); @@ -3882,74 +4462,73 @@ typedef void (*duckdb_v2_aggregate_function_destroy_callback_fn)(duckdb_v2_aggre /* --- Functions for aggregate --- */ /*! - * Creates a new aggregate function that will be registered on the connection's database. + * Creates a new aggregate function through a factory. * * The function starts out empty: configure it with the setter functions (e.g. * `duckdb_v2_aggregate_function_set_name()`, `duckdb_v2_aggregate_function_set_update_callback()`, etc.) and the * signature obtained via `duckdb_v2_aggregate_function_get_signature()`, then make it available with - * `duckdb_v2_aggregate_function_register()`. The caller owns the returned handle and must destroy it with - * `duckdb_v2_aggregate_function_destroy()`, also after registration. + * `duckdb_v2_connection_register_aggregate_function()` or `duckdb_v2_extension_register_aggregate_function()`. The + * caller owns the returned handle and must destroy it with `duckdb_v2_aggregate_function_destroy()`, also after + * registration. + * + * The aggregate function can only be registered on the database the factory belongs to. * * history: * - stable: v2.0.0 * - * @param connection The connection to create the function in. + * @param factory The factory to create the aggregate function through. * @param function On success, receives the newly created aggregate function. Owned by the caller. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_create_with_connection( - duckdb_v2_connection_handle connection, duckdb_v2_aggregate_function_handle *function, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_create(duckdb_v2_factory_handle factory, + duckdb_v2_aggregate_function_handle *function, + duckdb_v2_error_info_handle *err); /*! - * Creates a new aggregate function that will be registered on the loading extension's database. + * Sets the name of the aggregate function. * - * Use this from an extension load callback, where an extension handle is available. The function starts out empty: - * configure it with the setter functions (e.g. `duckdb_v2_aggregate_function_set_name()`, - * `duckdb_v2_aggregate_function_set_update_callback()`, etc.) and the signature obtained via - * `duckdb_v2_aggregate_function_get_signature()`, then make it available with - * `duckdb_v2_aggregate_function_register()`. The caller owns the returned handle and must destroy it with - * `duckdb_v2_aggregate_function_destroy()`, also after registration. + * The name is borrowed and copied. Calling this again replaces the previous name. A name must be set before + * registration. * * history: * - stable: v2.0.0 * - * @param extension The extension to create the function in. - * @param function On success, receives the newly created aggregate function. Owned by the caller. + * @param function The function to set the name of. + * @param name The name to set. Borrowed and copied. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_create_with_extension( - duckdb_v2_extension_handle extension, duckdb_v2_aggregate_function_handle *function, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_set_name(duckdb_v2_aggregate_function_handle function, + const duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err); /*! - * Sets the name of the aggregate function. + * Returns the documentation of the function so it can be filled in. * - * The name is borrowed and copied. Calling this again replaces the previous name. A name must be set before - * registration. + * Set a description with `duckdb_v2_function_docs_set_description()`, and add examples and categories with + * `duckdb_v2_function_docs_add_example()` and `duckdb_v2_function_docs_add_category()`. The documentation is modified + * in place, describes this overload only, and is registered with the function. Optional. * * history: * - stable: v2.0.0 * - * @param function The function to set the name of. - * @param name The name to set. Borrowed and copied. + * @param function The function to get the documentation of. + * @param docs The returned documentation. Borrowed and valid for the lifetime of the function handle. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_set_name(duckdb_v2_aggregate_function_handle function, - duckdb_v2_str *name, +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_get_docs(duckdb_v2_aggregate_function_handle function, + duckdb_v2_function_docs_handle *docs, duckdb_v2_error_info_handle *err); /*! * Returns the function's signature so it can be configured. * - * Add parameters with `duckdb_v2_function_signature_add_parameter()`, set a variadic tail with - * `duckdb_v2_function_signature_set_varargs()` and set the return type with + * Add parameters with `duckdb_v2_function_signature_add_parameter()` and set the return type with * `duckdb_v2_function_signature_set_return_type()`. The signature is modified in place; the function must be given a * signature with a return type before registration. * @@ -3970,8 +4549,8 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_get_signature(duckdb_v * Sets arbitrary user data on the aggregate function. * * Associates an opaque pointer with the function, retrievable from each callback via its user data accessor (e.g. - * `duckdb_v2_aggregate_function_bind_get_user_data()`, `duckdb_v2_aggregate_function_update_get_user_data()`, etc.). - * The opaque handle bundles the pointer with an optional destructor, invoked when the data is no longer needed. + * `duckdb_v2_function_bind_get_user_data()`, `duckdb_v2_aggregate_function_update_get_user_data()`, etc.). The opaque + * handle bundles the pointer with an optional destructor, invoked when the data is no longer needed. * * history: * - stable: v2.0.0 @@ -4012,9 +4591,10 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_set_property(duckdb_v2 /*! * Sets the optional bind callback of the aggregate function. * - * The bind callback is invoked during query planning for each call site of the function. It can inspect the argument - * types and constant argument values, set a concrete return type, and set "bind data" that is shared with the other - * callbacks. + * The bind callback is invoked during query planning for each call site of the function. Through its + * `duckdb_v2_function_bind_info_handle` it can inspect the argument types and constant argument values and set "bind + * data" that is shared with the other callbacks. Through its `duckdb_v2_aggregate_function_bind_info_handle` it can set + * a concrete return type. * * history: * - stable: v2.0.0 @@ -4143,99 +4723,6 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_set_destroy_callback( duckdb_v2_aggregate_function_handle function, duckdb_v2_aggregate_function_destroy_callback_fn callback, duckdb_v2_error_info_handle *err); -/*! - * Retrieves the user data set via `duckdb_v2_aggregate_function_set_user_data()`. - * - * history: - * - stable: v2.0.0 - * - * @param info The bind info handle. - * @param data Receives the user data pointer, or null if none was set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_bind_get_user_data( - duckdb_v2_aggregate_function_bind_info_handle info, void **data, duckdb_v2_error_info_handle *err); - -/*! - * Sets the function's "bind data" from the bind callback. - * - * The bind data is stored with the bound call site and retrievable from the other callbacks. The opaque handle bundles - * the pointer with an optional destructor, invoked when the bind data is no longer needed, and an optional equality - * callback used when comparing two bound call sites; without one, pointer equality is used. - * - * history: - * - stable: v2.0.0 - * - * @param info The bind info handle. - * @param data Opaque handle bundling the bind data pointer plus optional destructor and equality callbacks. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_bind_set_bind_data( - duckdb_v2_aggregate_function_bind_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); - -/*! - * Returns the number of arguments of the call site being bound. - * - * Variadic tail arguments are included. Valid indices for `duckdb_v2_aggregate_function_bind_get_arg_type()` and - * `duckdb_v2_aggregate_function_bind_get_arg_value()` are [0, count). - * - * history: - * - stable: v2.0.0 - * - * @param info The bind info handle. - * @param count Receives the number of arguments. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_bind_get_arg_count( - duckdb_v2_aggregate_function_bind_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); - -/*! - * Retrieves the type of the argument at the given index. - * - * Fails if the index is out of bounds. The returned type is owned by the caller and must be destroyed via - * `duckdb_v2_logical_type_destroy()`. - * - * history: - * - stable: v2.0.0 - * - * @param info The bind info handle. - * @param index The index of the argument to get the type of. - * @param type Receives the argument type. Owned by the caller; destroy via `duckdb_v2_logical_type_destroy()`. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR -duckdb_v2_aggregate_function_bind_get_arg_type(duckdb_v2_aggregate_function_bind_info_handle info, idx_t index, - duckdb_v2_logical_type_handle *type, duckdb_v2_error_info_handle *err); - -/*! - * Folds the argument at the given index to a constant value. - * - * The argument must be foldable to a constant (e.g. a literal or a constant expression); otherwise the call fails with - * an error, as it does when the index is out of bounds. The resulting value may be NULL. The returned value is owned by - * the caller and must be destroyed via `duckdb_v2_value_destroy()`. - * - * history: - * - stable: v2.0.0 - * - * @param info The bind info handle. - * @param index The index of the argument to extract a constant value from. - * @param value Receives the constant value. Owned by the caller; destroy via `duckdb_v2_value_destroy()`. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR -duckdb_v2_aggregate_function_bind_get_arg_value(duckdb_v2_aggregate_function_bind_info_handle info, idx_t index, - duckdb_v2_value_handle *value, duckdb_v2_error_info_handle *err); - /*! * Sets the concrete return type of the call site being bound. * @@ -4411,40 +4898,49 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_update_get_row_count( duckdb_v2_aggregate_function_update_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); /*! - * Returns how many argument vectors this invocation carries: one per argument of the call, variadic tail arguments - * included. Valid indices for `duckdb_v2_aggregate_function_update_get_arg()` are [0, count). + * Returns how many argument vectors this invocation carries, split into the four parts of the argument list. + * + * The parts and their order are those the bind callback saw through `duckdb_v2_function_bind_get_arg_count()`, and the + * vectors are at the same indices. Valid indices for `duckdb_v2_aggregate_function_update_get_arg()` are [0, the sum of + * the four counts). Every out-parameter may be NULL, in which case nothing is written to it. * * history: * - stable: v2.0.0 * * @param info The update info handle. - * @param count Receives the number of argument vectors. + * @param positional_fixed Optional. Receives the number of positional-only and standard parameters. + * @param positional_variadic Optional. Receives the number of arguments `*args` received, 0 when the signature has + * none. + * @param named_fixed Optional. Receives the number of named-only parameters. + * @param named_variadic Optional. Receives the number of arguments `**kwargs` received, 0 when the signature has none. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_update_get_arg_count( - duckdb_v2_aggregate_function_update_info_handle info, uint32_t *count, duckdb_v2_error_info_handle *err); + duckdb_v2_aggregate_function_update_info_handle info, idx_t *positional_fixed, idx_t *positional_variadic, + idx_t *named_fixed, idx_t *named_variadic, duckdb_v2_error_info_handle *err); /*! * Retrieves the argument vector at the given index. * - * The vector holds the argument's values for the current batch; use - * `duckdb_v2_aggregate_function_update_get_row_count()` for the number of rows. Fails if the index is out of bounds. - * Borrowed; valid only for the duration of the callback. + * The index is the one the bind callback used for the argument, e.g. the index + * `duckdb_v2_function_bind_get_arg_index()` found for a name. The vector holds the argument's values for the current + * batch; use `duckdb_v2_aggregate_function_update_get_row_count()` for the number of rows. Fails if the index is out of + * bounds. Borrowed; valid only for the duration of the callback. * * history: * - stable: v2.0.0 * * @param info The update info handle. - * @param index The index of the argument vector to get. + * @param index The index of the argument. * @param vector Receives the borrowed argument vector. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ DUCKDB_C_API DUCKDB_V2_ERROR -duckdb_v2_aggregate_function_update_get_arg(duckdb_v2_aggregate_function_update_info_handle info, uint32_t index, +duckdb_v2_aggregate_function_update_get_arg(duckdb_v2_aggregate_function_update_info_handle info, idx_t index, duckdb_v2_vector_handle *vector, duckdb_v2_error_info_handle *err); /*! @@ -4703,1287 +5199,800 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_destroy_get_states( /*! * Registers the aggregate function, making it available for use in SQL queries. * - * The function is registered on the target given at creation: the connection's database or the loading extension. - * Registration requires a name, the size, init, update, combine and finalize callbacks, and a signature with a complete - * return type; an ANY return type is accepted only together with a bind callback that sets the concrete type per call - * site. The caller still owns the handle after registration and must destroy it with + * The function is registered on the connection's database, visible to every connection, in the connection's + * transaction: inside an explicit transaction, it is invisible to other connections until the transaction commits, and + * a rollback undoes it. Registration requires a name, the size, init, update, combine and finalize callbacks, and a + * signature with a complete return type; an ANY return type is accepted only together with a bind callback that sets + * the concrete type per call site. The aggregate function must have been created through a factory of the same + * database. The caller still owns the handle after registration and must destroy it with * `duckdb_v2_aggregate_function_destroy()`, which does not affect the registered function. * * history: * - stable: v2.0.0 * - * @param function The function to register. + * @param conn The connection to register on. + * @param function The aggregate function to register. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_register(duckdb_v2_aggregate_function_handle function, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_register_aggregate_function( + duckdb_v2_connection_handle conn, duckdb_v2_aggregate_function_handle function, duckdb_v2_error_info_handle *err); /*! - * Destroys the aggregate function, releasing its resources. + * Registers the aggregate function, making it available for use in SQL queries. * - * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent - * double-destruction. Destroying the handle after registration does not affect the registered function. + * The function is registered on the loading extension's database and attributed to the extension. Use this from the + * extension's load callback. Registration requires a name, the size, init, update, combine and finalize callbacks, and + * a signature with a complete return type; an ANY return type is accepted only together with a bind callback that sets + * the concrete type per call site. The aggregate function must have been created through a factory of the same + * database. The caller still owns the handle after registration and must destroy it with + * `duckdb_v2_aggregate_function_destroy()`, which does not affect the registered function. * * history: * - stable: v2.0.0 * - * @param function The function to destroy. + * @param extension The loading extension. + * @param function The aggregate function to register. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_destroy(duckdb_v2_aggregate_function_handle *function); - -/* --- Struct definitions for aggregate --- */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_extension_register_aggregate_function( + duckdb_v2_extension_handle extension, duckdb_v2_aggregate_function_handle function, + duckdb_v2_error_info_handle *err); -/* ============================================================================ - * MODULE: arrow +/*! + * Destroys the aggregate function, releasing its resources. + * + * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent + * double-destruction. Destroying the handle after registration does not affect the registered function. + * + * history: + * - stable: v2.0.0 + * + * @param function The function to destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_destroy(duckdb_v2_aggregate_function_handle *function); + +/* --- Struct definitions for aggregate --- */ + +/* ============================================================================ + * MODULE: catalog * ============================================================================ */ -/* --- Enums for arrow --- */ +/* --- Enums for catalog --- */ -/* --- Struct forward declarations for arrow --- */ +/* --- Struct forward declarations for catalog --- */ -/* --- Types for arrow --- */ +/* --- Types for catalog --- */ /*! - * Converts Arrow arrays into DuckDB data chunks, against one resolved ArrowSchema. Create it with - * `duckdb_v2_arrow_importer_create()`, which resolves every column's DuckDB type once, so the same importer serves any - * number of arrays of that shape. `duckdb_v2_arrow_importer_get_schema()` reports the resolved DuckDB schema. - * `duckdb_v2_arrow_importer_append()` takes an array, `duckdb_v2_arrow_importer_next_chunk()` produces chunks from it, - * and `duckdb_v2_arrow_importer_destroy()` frees the importer. - * - * The importer borrows the context it was created with and must not outlive it. One array is in flight at a time, and - * an importer must not be used from two threads at once. + * An owned snapshot of one base table taken at creation: where the name resolved, the table's columns, and per-column + * catalog facts. Later DDL does not update it. Create with `duckdb_v2_connection_describe_table()`, read the resolved + * location via `duckdb_v2_table_description_get_qname()`, and the columns via + * `duckdb_v2_table_description_get_column_count()` and `duckdb_v2_table_description_get_column()`. Destroy via + * `duckdb_v2_table_description_destroy()`. */ -typedef struct _duckdb_v2_arrow_importer { +typedef struct _duckdb_v2_table_description { void *internal_ptr; -} * duckdb_v2_arrow_importer_handle; +} * duckdb_v2_table_description_handle; /*! - * Converts DuckDB data chunks into Arrow arrays, for one fixed list of columns. Create it with - * `duckdb_v2_arrow_exporter_create()`, which captures the session's Arrow settings and resolves the extension types - * once. `duckdb_v2_arrow_exporter_get_schema()` reports the Arrow schema. `duckdb_v2_arrow_exporter_append()` takes a - * chunk, `duckdb_v2_arrow_exporter_next_array()` produces arrays from it, and `duckdb_v2_arrow_exporter_destroy()` - * frees the exporter. - * - * An exporter must not be used from two threads at once. + * An owned snapshot of one column of a described table: its name, type, and catalog facts. Obtain with + * `duckdb_v2_table_description_get_column()`, read with `duckdb_v2_column_description_get_name()`, + * `duckdb_v2_column_description_get_type()`, `duckdb_v2_column_description_has_default()` and + * `duckdb_v2_column_description_has_generated()`, and destroy with `duckdb_v2_column_description_destroy()`. */ -typedef struct _duckdb_v2_arrow_exporter { +typedef struct _duckdb_v2_column_description { void *internal_ptr; -} * duckdb_v2_arrow_exporter_handle; +} * duckdb_v2_column_description_handle; -/* --- Constants for arrow --- */ +/* --- Constants for catalog --- */ -/* --- Function pointer typedefs for arrow --- */ +/* --- Function pointer typedefs for catalog --- */ -/* --- Functions for arrow --- */ +/* --- Functions for catalog --- */ /*! - * Exports a result as a lazy ArrowArrayStream. Consuming. - * - * Takes ownership of the result, sets the slot to NULL, and fills the caller-allocated `out_stream`. The stream owns - * the result from then on. Releasing the stream, with `out_stream->release(out_stream)`, does not drain the result, but - * closes the query and frees the connection's live-result slot as `duckdb_v2_result_destroy()` would, so the connection - * can run its next query. - * - * The stream's `get_next` drives the result, waiting internally until a batch is ready, and gathers DuckDB chunks into - * one Arrow array of up to `batch_size` rows. The Arrow schema and the extension type map are built and cached here, - * while the query's transaction is still active, because building them can run extension populate-schema callbacks and - * read ENUM dictionaries. `get_schema` returns a copy of the cached schema and never touches the catalog. - * - * A result that has already yielded some chunks is allowed and produces a stream over the remaining rows. - * - * If the statement expanded into a group whose row-producing fragment has not started yet, this call steps the result - * far enough to cache the schema, which may block briefly. No rows are lost, since none are produced before that - * fragment is prepared. For an ordinary statement nothing executes here. + * Resolves a table name and snapshots its description. * - * The result is consumed on every path that reaches the engine, including failures. Only a null-argument rejection - * leaves it intact. `out_stream` is untouched unless the call succeeds. + * Resolves name in the connection's catalogs and returns an owned description of the table it names. name may be + * partial: an unqualified or schema-qualified name resolves through the connection's search path, exactly as the same + * name resolves in SQL. A two-part name tries the first part as a schema and as an attached database, as SQL does, and + * is rejected when both readings exist. A name that resolves to nothing is rejected with the engine's missing-table + * error; a name that resolves to a view is rejected with the engine's not-a-table error, since a description snapshots + * a base table. * * history: * - stable: v2.0.0 * - * @param result The result to export. Consumed and set to NULL, except when the call rejects a null argument. - * @param batch_size Maximum rows per Arrow array. Pass 0 for the default of 131072, which is 64 vectors in a default - * build. - * @param out_stream Caller-allocated stream the library fills. Release it with `out_stream->release(out_stream)`. + * @param conn The connection whose catalogs and search path resolve the name. + * @param name The possibly partial table name to resolve. Borrowed for the call only. + * @param desc On success, receives the owned description. Destroy via `duckdb_v2_table_description_destroy()`. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_to_arrow_stream(duckdb_v2_result_handle *result, idx_t batch_size, - struct ArrowArrayStream *out_stream, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_describe_table(duckdb_v2_connection_handle conn, + duckdb_v2_qname_handle name, + duckdb_v2_table_description_handle *desc, + duckdb_v2_error_info_handle *err); /*! - * Resolves an Arrow schema into a reusable importer. - * - * Works out every column's DuckDB logical type and the Arrow type information the conversion needs, once, so any number - * of arrays of that shape can be imported without re-reading the schema. `schema` is read, not consumed: the caller - * keeps ownership and still releases it. - * - * `batch_size` caps the rows per produced chunk. A long array is split across several chunks. Rows left over that do - * not fill a batch are held back and joined with the next array, unless the append asked to flush. Pass 0 for no - * maximum: each array becomes one chunk, however long it is. + * Returns the resolved qualified name of the described table. * - * Resolving reads the catalog for extension types, so `context` must have an active transaction. The importer keeps - * using that context for every conversion and must not outlive it. Within one connection the context is the same - * throughout, so an importer created in a bind callback is usable from the matching exec callback. `*out_importer` is - * set to NULL on failure. + * Returns an owned copy of the fully resolved name: the catalog, schema, and table the lookup landed on, with the + * casing the table was created with, never an echo of the requested name. Rendering it produces SQL that pins the + * described table regardless of search path. * * history: * - stable: v2.0.0 * - * @param context The context used to resolve the Arrow types, extension types included. - * @param schema The schema to resolve. Read, not consumed; the caller keeps ownership. - * @param batch_size Maximum rows per produced chunk, or 0 for no maximum. - * @param out_importer On success, receives the new importer. Owned by the caller; destroy via - * `duckdb_v2_arrow_importer_destroy()`. + * @param desc The description. + * @param name Receives the owned resolved name. Destroy via `duckdb_v2_qname_destroy()`. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_importer_create(duckdb_v2_context_handle context, - struct ArrowSchema *schema, idx_t batch_size, - duckdb_v2_arrow_importer_handle *out_importer, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_description_get_qname(duckdb_v2_table_description_handle desc, + duckdb_v2_qname_handle *name, + duckdb_v2_error_info_handle *err); /*! - * Returns the resolved DuckDB schema. - * - * Writes an owned schema handle with the DuckDB name and logical type of every column the importer resolved. This is - * how a caller learns the DuckDB shape of an ArrowSchema, to declare a table function's result columns for instance, - * without reimplementing the mapping from Arrow format strings to logical types. + * Returns whether the described table's catalog is read-only. * - * The fields were resolved at creation, so this reads no catalog and needs no transaction. `*out_schema` is set to NULL - * on failure. + * True when the catalog the name resolved into was attached read-only, in which case no write to the table can succeed. + * False does not by itself prove a write will succeed; it clears the catalog-level check only. * * history: * - stable: v2.0.0 * - * @param importer The importer to read. - * @param out_schema On success, receives an owned schema. Destroy via `duckdb_v2_schema_destroy()`. + * @param desc The description. + * @param readonly Receives whether the catalog is read-only. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_importer_get_schema(duckdb_v2_arrow_importer_handle importer, - duckdb_v2_schema_handle *out_schema, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_description_is_readonly(duckdb_v2_table_description_handle desc, + bool *readonly, duckdb_v2_error_info_handle *err); /*! - * Gives the importer one array to convert. - * - * Take the chunks with `duckdb_v2_arrow_importer_next_chunk()` until that returns NULL. Appending while the previous - * array still has rows left is rejected with `ERROR_INPUT_INVALID`. An array whose shape does not match the resolved - * schema -- a different child count, a child whose length differs from the array's, a null or already-released child -- - * is rejected the same way, before anything is read. - * - * `flush` marks the end of the input: rows that do not fill a batch then come out as a final short chunk instead of - * being held back for the next array. Pass NULL for `array` with `flush` set to release the held rows without supplying - * more input. - * - * `consume` decides what happens to the caller's array. - * - * When true, the importer takes over the array and sets its `release` to NULL; the caller must not release it - * afterwards. The produced chunks reference the Arrow buffers directly, without copying, and keep them alive, so the - * chunks stay valid after the importer is destroyed. Prefer this path. - * - * When false, the caller keeps the array and must keep it valid until the drain finishes. The produced chunks are - * copies, so they do not depend on the array, at the cost of one copy per chunk. - * - * Either way, a chunk that joins rows held back from the previous array is a copy, since it cannot reference two - * arrays. + * Returns the number of columns of the described table. * - * Only the default, dictionary-encoded and run-end-encoded Arrow layouts are supported. Any other layout reports - * `ERROR_QUERY_NOT_IMPLEMENTED` when the column is converted. + * Counts every column in declared order, generated columns included. Valid indices for + * `duckdb_v2_table_description_get_column()` are [0, count). * * history: * - stable: v2.0.0 * - * @param importer The importer to feed. - * @param array The array to convert. Its `release` is set to NULL when `consume` is true. - * @param consume True to hand the array over for a zero-copy import; false to keep it, in which case every produced - * chunk is a copy. - * @param flush True to mark the end of the input, releasing held rows as a final short chunk. Pass NULL for `array` - * with this set to flush without supplying more input. + * @param desc The description. + * @param count Receives the column count. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_importer_append(duckdb_v2_arrow_importer_handle importer, - struct ArrowArray *array, bool consume, bool flush, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_description_get_column_count(duckdb_v2_table_description_handle desc, + idx_t *count, + duckdb_v2_error_info_handle *err); /*! - * Produces the next chunk of the appended array, or NULL once the array is drained. - * - * Call it in a loop until `*out_chunk` is NULL. An importer with no array appended also returns NULL. Each chunk holds - * at most the importer's `batch_size` rows, or the whole array when that is 0. A chunk may start with rows held back - * from the previous array, and rows that do not fill a batch are held back in turn unless the append asked to flush. So - * NULL means the array has been read, not that all of its rows have come out. + * Returns an owned description of the column at index. * - * The conversion runs under the context the importer was created with, which must still be alive. `*out_chunk` is set - * to NULL on failure. + * Columns are numbered in declared order, generated columns included. An out-of-range index is rejected with + * `ERROR_INPUT_OUT_OF_RANGE`. * * history: * - stable: v2.0.0 * - * @param importer The importer to drain. - * @param out_chunk On success, receives the next chunk, or NULL once the array is drained. Destroy via - * `duckdb_v2_data_chunk_destroy()`. + * @param desc The description. + * @param index Zero-based column index. + * @param column Receives the owned column description. Destroy via `duckdb_v2_column_description_destroy()`. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_importer_next_chunk(duckdb_v2_arrow_importer_handle importer, - duckdb_v2_data_chunk_handle *out_chunk, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_description_get_column(duckdb_v2_table_description_handle desc, + idx_t index, + duckdb_v2_column_description_handle *column, + duckdb_v2_error_info_handle *err); /*! - * Destroys an importer. + * Destroys the table description, releasing its resources. * - * Null-safe: passing NULL, or a slot already set to NULL, is a no-op. Chunks already produced stay valid, including the - * zero-copy ones, which keep the Arrow buffers alive themselves. An array appended with `consume` true and not fully - * drained is released here. Rows held back for a next array are dropped. On success the slot is set to NULL. + * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent + * double-destruction. Column descriptions obtained from it are independent and stay valid. * * history: * - stable: v2.0.0 * - * @param importer The importer to destroy. + * @param desc The description to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_importer_destroy(duckdb_v2_arrow_importer_handle *importer); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_description_destroy(duckdb_v2_table_description_handle *desc); /*! - * Creates an exporter for one fixed list of columns. - * - * Resolves the extension types and captures the session's Arrow settings, so every array this exporter produces matches - * the schema `duckdb_v2_arrow_exporter_get_schema()` reports, even if a setting changes afterwards. - * - * `batch_size` caps the rows per produced array. A long chunk is split across several arrays. Rows left over that do - * not fill a batch are held back and joined with the next chunk, unless the append asked to flush. Pass 0 for no - * maximum: each chunk becomes one array, however long it is. + * Borrows the name of the column. * - * `types` and `names` are parallel arrays of `count` entries, borrowed and copied; they may be NULL only when `count` - * is 0. Resolving reads the catalog, so `context` must have an active transaction. `*out_exporter` is set to NULL on - * failure. + * The name carries the casing the column was declared with. * * history: * - stable: v2.0.0 * - * @param context The context whose Arrow settings are captured and whose transaction resolves the types. - * @param types An array of `count` column types. May be NULL only when `count` is 0. - * @param names An array of `count` column names, parallel to `types`. May be NULL only when `count` is 0. - * @param count The number of columns, being the length of both `types` and `names`. - * @param batch_size Maximum rows per produced array, or 0 for no maximum. - * @param out_exporter On success, receives the new exporter. Owned by the caller; destroy via - * `duckdb_v2_arrow_exporter_destroy()`. + * @param column The column description. + * @param name Receives a borrowed view of the column name. Valid until the column description is destroyed. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_exporter_create(duckdb_v2_context_handle context, - const duckdb_v2_logical_type_handle *types, - const duckdb_v2_str *names, idx_t count, idx_t batch_size, - duckdb_v2_arrow_exporter_handle *out_exporter, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_column_description_get_name(duckdb_v2_column_description_handle column, + duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err); /*! - * Returns the Arrow schema of the arrays this exporter produces. - * - * Fills the caller-allocated `out_schema`. The caller owns the result and releases it with - * `out_schema->release(out_schema)`. Callable at any point, and always returns the same schema, since it is built from - * the settings captured at creation. + * Borrows the type of the column. * * history: * - stable: v2.0.0 * - * @param exporter The exporter to read. - * @param out_schema Caller-allocated schema the library fills. Release it with `out_schema->release(out_schema)`. + * @param column The column description. + * @param type Receives the borrowed column type. Valid until the column description is destroyed; do not destroy it. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_exporter_get_schema(duckdb_v2_arrow_exporter_handle exporter, - struct ArrowSchema *out_schema, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_column_description_get_type(duckdb_v2_column_description_handle column, + duckdb_v2_logical_type_handle *type, + duckdb_v2_error_info_handle *err); /*! - * Gives the exporter one chunk to convert. + * Returns whether the column has a default value. * - * The chunk is converted in full before this returns. The conversion copies into freshly allocated Arrow buffers, so - * nothing of the caller's is retained. Every array completed by this chunk becomes available from - * `duckdb_v2_arrow_exporter_next_array()`; rows that do not complete a batch are held back and finished by the next - * chunk. Completed arrays queue up, so appending again before they are taken is allowed. - * - * `flush` marks the end of the input: the held rows are then finished as a final short array. Pass NULL for `chunk` - * with `flush` set to release the held rows without supplying more input. - * - * The chunk's types must match the ones the exporter was created with, or the call is rejected with - * `ERROR_INPUT_INVALID` before anything is read. - * - * `consume` decides only what happens to the caller's handle, since the data is copied either way. When true the chunk - * is destroyed and the slot set to NULL, saving a `duckdb_v2_data_chunk_destroy()` for a chunk the caller owns. When - * false the chunk is left untouched, which is what a chunk borrowed from a callback needs, such as the output chunk of - * a table function's exec callback, which the caller does not own and must not destroy. + * True when the column declares a default expression; the engine evaluates it for rows that omit the column. Generated + * columns report false. * * history: * - stable: v2.0.0 * - * @param exporter The exporter to feed. - * @param chunk The chunk to convert. Destroyed and set to NULL only when `consume` is true. - * @param consume True to hand the chunk over, destroying it; false to leave the caller's handle untouched. - * @param flush True to mark the end of the input, releasing the held rows as a final short array. Pass NULL for `chunk` - * with this set to flush without supplying more input. + * @param column The column description. + * @param has_default Receives whether the column has a default value. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_exporter_append(duckdb_v2_arrow_exporter_handle exporter, - duckdb_v2_data_chunk_handle *chunk, bool consume, - bool flush, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_column_description_has_default(duckdb_v2_column_description_handle column, + bool *has_default, + duckdb_v2_error_info_handle *err); /*! - * Takes the next completed array, or reports that none is ready. - * - * Call it in a loop after every `duckdb_v2_arrow_exporter_append()` until `out_array->release` is NULL, which is how - * the Arrow C Data Interface signals "no array" and what a stream's `get_next` does at end of input. Rows held back - * towards an unfinished batch are not an array yet; they come out after a further append or a flush. + * Returns whether the column is generated. * - * Each array is owned by the caller and released with `out_array->release(out_array)`, independently of the exporter - * and of every other array. + * True when the column is a generated column, computed by the engine from a generation expression and not writable. * * history: * - stable: v2.0.0 * - * @param exporter The exporter to drain. - * @param out_array Caller-allocated array the library fills. Left released -- `release` NULL -- when none is ready. - * Release a filled one with `out_array->release(out_array)`. + * @param column The column description. + * @param has_generated Receives whether the column is generated. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_exporter_next_array(duckdb_v2_arrow_exporter_handle exporter, - struct ArrowArray *out_array, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_column_description_has_generated(duckdb_v2_column_description_handle column, + bool *has_generated, + duckdb_v2_error_info_handle *err); /*! - * Destroys an exporter. + * Destroys the column description, releasing its resources. * - * Null-safe: passing NULL, or a slot already set to NULL, is a no-op. Arrays already taken stay valid. Arrays still - * queued inside are released here, and rows held back towards an unfinished batch are dropped. On success the slot is - * set to NULL. + * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent + * double-destruction. Any name or type borrowed from it becomes dangling. * * history: * - stable: v2.0.0 * - * @param exporter The exporter to destroy. + * @param column The column description to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_exporter_destroy(duckdb_v2_arrow_exporter_handle *exporter); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_column_description_destroy(duckdb_v2_column_description_handle *column); -/* --- Struct definitions for arrow --- */ +/* --- Struct definitions for catalog --- */ /* ============================================================================ - * MODULE: catalog + * MODULE: copy * ============================================================================ */ -/* --- Enums for catalog --- */ +/* --- Enums for copy --- */ -/* --- Struct forward declarations for catalog --- */ +/* --- Struct forward declarations for copy --- */ -/* --- Types for catalog --- */ +/* --- Types for copy --- */ /*! - * An owned snapshot of one base table taken at creation: where the name resolved, the table's columns, and per-column - * catalog facts. Later DDL does not update it. Create with `duckdb_v2_connection_describe_table()`, read the resolved - * location via `duckdb_v2_table_description_get_qname()`, and the columns via - * `duckdb_v2_table_description_get_column_count()` and `duckdb_v2_table_description_get_column()`. Destroy via - * `duckdb_v2_table_description_destroy()`. + * An owned opaque handle to a custom copy function being built: an output format for `COPY ... TO`, an input format for + * `COPY ... FROM`, or both. Created with `duckdb_v2_copy_function_create()`, configured with the setter functions (e.g. + * `duckdb_v2_copy_function_set_name()`, `duckdb_v2_copy_to_set_batch_callback()`, + * `duckdb_v2_copy_from_set_exec_callback()`, etc.), made available with `duckdb_v2_connection_register_copy_function()` + * or `duckdb_v2_extension_register_copy_function()`, and destroyed with `duckdb_v2_copy_function_destroy()`. */ -typedef struct _duckdb_v2_table_description { +typedef struct _duckdb_v2_copy_function { void *internal_ptr; -} * duckdb_v2_table_description_handle; +} * duckdb_v2_copy_function_handle; /*! - * An owned snapshot of one column of a described table: its name, type, and catalog facts. Obtain with - * `duckdb_v2_table_description_get_column()`, read with `duckdb_v2_column_description_get_name()`, - * `duckdb_v2_column_description_get_type()`, `duckdb_v2_column_description_has_default()` and - * `duckdb_v2_column_description_has_generated()`, and destroy with `duckdb_v2_column_description_destroy()`. + * A borrowed opaque handle to the arguments supplied to a copy function during the query preparation "bind" phase of a + * `COPY ... TO` statement. The "bind" callback receives this handle and can use it to e.g. inspect the names and types + * of the columns being written, read the statement's options and initialize some constant state. */ -typedef struct _duckdb_v2_column_description { +typedef struct _duckdb_v2_copy_to_bind_info { void *internal_ptr; -} * duckdb_v2_column_description_handle; - -/* --- Constants for catalog --- */ - -/* --- Function pointer typedefs for catalog --- */ - -/* --- Functions for catalog --- */ +} * duckdb_v2_copy_to_bind_info_handle; /*! - * Resolves a table name and snapshots its description. - * - * Resolves name in the connection's catalogs and returns an owned description of the table it names. name may be - * partial: an unqualified or schema-qualified name resolves through the connection's search path, exactly as the same - * name resolves in SQL. A two-part name tries the first part as a schema and as an attached database, as SQL does, and - * is rejected when both readings exist. A name that resolves to nothing is rejected with the engine's missing-table - * error; a name that resolves to a view is rejected with the engine's not-a-table error, since a description snapshots - * a base table. - * - * history: - * - stable: v2.0.0 - * - * @param conn The connection whose catalogs and search path resolve the name. - * @param name The possibly partial table name to resolve. Borrowed for the call only. - * @param desc On success, receives the owned description. Destroy via `duckdb_v2_table_description_destroy()`. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR + * A borrowed opaque handle to the arguments supplied to a copy function during the "batch size" phase of a `COPY ... + * TO` statement. The "batch size" callback receives this handle and must use it to report how many rows a batch should + * carry. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_describe_table(duckdb_v2_connection_handle conn, - duckdb_v2_qname_handle name, - duckdb_v2_table_description_handle *desc, - duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_copy_to_batch_size_info { + void *internal_ptr; +} * duckdb_v2_copy_to_batch_size_info_handle; /*! - * Returns the resolved qualified name of the described table. - * - * Returns an owned copy of the fully resolved name: the catalog, schema, and table the lookup landed on, with the - * casing the table was created with, never an echo of the requested name. Rendering it produces SQL that pins the - * described table regardless of search path. - * - * history: - * - stable: v2.0.0 - * - * @param desc The description. - * @param name Receives the owned resolved name. Destroy via `duckdb_v2_qname_destroy()`. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR + * A borrowed opaque handle to the arguments supplied to a copy function during the per-file "init" phase of a `COPY ... + * TO` statement. The "init" callback receives this handle and can use it to e.g. read the path of the file being + * written and set up the state shared by every batch written to that file. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_description_get_qname(duckdb_v2_table_description_handle desc, - duckdb_v2_qname_handle *name, - duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_copy_to_init_info { + void *internal_ptr; +} * duckdb_v2_copy_to_init_info_handle; /*! - * Returns whether the described table's catalog is read-only. - * - * True when the catalog the name resolved into was attached read-only, in which case no write to the table can succeed. - * False does not by itself prove a write will succeed; it clears the catalog-level check only. - * - * history: - * - stable: v2.0.0 - * - * @param desc The description. - * @param readonly Receives whether the catalog is read-only. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR + * A borrowed opaque handle to the arguments supplied to a copy function during the "batch" phase of a `COPY ... TO` + * statement. The "batch" callback receives this handle and can use it to take ownership of the rows of the batch and to + * set the prepared form of the batch handed to the "flush" callback. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_description_is_readonly(duckdb_v2_table_description_handle desc, - bool *readonly, duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_copy_to_batch_info { + void *internal_ptr; +} * duckdb_v2_copy_to_batch_info_handle; /*! - * Returns the number of columns of the described table. - * - * Counts every column in declared order, generated columns included. Valid indices for - * `duckdb_v2_table_description_get_column()` are [0, count). - * - * history: - * - stable: v2.0.0 - * - * @param desc The description. - * @param count Receives the column count. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR + * A borrowed opaque handle to the arguments supplied to a copy function during the "flush" phase of a `COPY ... TO` + * statement. The "flush" callback receives this handle and can use it to access the prepared batch and write it to the + * output. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_description_get_column_count(duckdb_v2_table_description_handle desc, - idx_t *count, - duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_copy_to_flush_info { + void *internal_ptr; +} * duckdb_v2_copy_to_flush_info_handle; /*! - * Returns an owned description of the column at index. - * - * Columns are numbered in declared order, generated columns included. An out-of-range index is rejected with - * `ERROR_INPUT_OUT_OF_RANGE`. - * - * history: - * - stable: v2.0.0 - * - * @param desc The description. - * @param index Zero-based column index. - * @param column Receives the owned column description. Destroy via `duckdb_v2_column_description_destroy()`. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR + * A borrowed opaque handle to the arguments supplied to a copy function during the per-file "finalize" phase of a `COPY + * ... TO` statement. The "finalize" callback receives this handle and can use it to e.g. close the file after every + * batch has been flushed to it. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_description_get_column(duckdb_v2_table_description_handle desc, - idx_t index, - duckdb_v2_column_description_handle *column, - duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_copy_to_finalize_info { + void *internal_ptr; +} * duckdb_v2_copy_to_finalize_info_handle; /*! - * Destroys the table description, releasing its resources. - * - * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent - * double-destruction. Column descriptions obtained from it are independent and stay valid. - * - * history: - * - stable: v2.0.0 - * - * @param desc The description to destroy. - * @return DUCKDB_V2_ERROR + * A borrowed opaque handle to the arguments supplied to a copy function when a `COPY ... TO` statement reports + * statistics about the files it wrote (e.g. with `RETURN_STATS`). The "statistics" callback receives this handle and + * uses it to report the statistics of one written file. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_description_destroy(duckdb_v2_table_description_handle *desc); +typedef struct _duckdb_v2_copy_to_statistics_info { + void *internal_ptr; +} * duckdb_v2_copy_to_statistics_info_handle; /*! - * Borrows the name of the column. - * - * The name carries the casing the column was declared with. - * - * history: - * - stable: v2.0.0 - * - * @param column The column description. - * @param name Receives a borrowed view of the column name. Valid until the column description is destroyed. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR + * A borrowed opaque handle to the arguments supplied to a copy function during the query preparation "bind" phase of a + * `COPY ... FROM` statement. The "bind" callback receives this handle and can use it to e.g. read the path of the file + * to read, inspect the names and types of the columns the target table expects, read the statement's options, hint at + * the number of rows the read will produce and initialize some constant state. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_column_description_get_name(duckdb_v2_column_description_handle column, - duckdb_v2_identifier_t *name, - duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_copy_from_bind_info { + void *internal_ptr; +} * duckdb_v2_copy_from_bind_info_handle; /*! - * Borrows the type of the column. - * - * history: - * - stable: v2.0.0 - * - * @param column The column description. - * @param type Receives the borrowed column type. Valid until the column description is destroyed; do not destroy it. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR + * A borrowed opaque handle to the arguments supplied to a copy function during the global state initialization "init + * global" phase of a `COPY ... FROM` statement. The "init global" callback receives this handle and can use it to e.g. + * set up the state shared by every thread reading the file, and to declare how many threads may read it. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_column_description_get_type(duckdb_v2_column_description_handle column, - duckdb_v2_logical_type_handle *type, - duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_copy_from_init_global_info { + void *internal_ptr; +} * duckdb_v2_copy_from_init_global_info_handle; /*! - * Returns whether the column has a default value. + * A borrowed opaque handle to the arguments supplied to a copy function during the local state initialization "init + * local" phase of a `COPY ... FROM` statement. The "init local" callback receives this handle and can use it to e.g. + * set up worker-local state, typically by claiming work from the shared global state. + */ +typedef struct _duckdb_v2_copy_from_init_local_info { + void *internal_ptr; +} * duckdb_v2_copy_from_init_local_info_handle; + +/*! + * A borrowed opaque handle to the arguments supplied to a copy function during the execution "exec" phase of a `COPY + * ... FROM` statement. The "exec" callback receives this handle and can use it to e.g. access the global and local + * state and write the next batch of rows to the output chunk. + */ +typedef struct _duckdb_v2_copy_from_exec_info { + void *internal_ptr; +} * duckdb_v2_copy_from_exec_info_handle; + +/*! + * A borrowed opaque handle to the arguments supplied to a copy function during the "progress" phase of a `COPY ... + * FROM` statement. The "progress" callback receives this handle and can use it to report how far the read has advanced. + */ +typedef struct _duckdb_v2_copy_from_progress_info { + void *internal_ptr; +} * duckdb_v2_copy_from_progress_info_handle; + +/* --- Constants for copy --- */ + +/* --- Function pointer typedefs for copy --- */ + +typedef void (*duckdb_v2_copy_to_bind_callback_fn)(duckdb_v2_copy_to_bind_info_handle info, + duckdb_v2_context_handle context, duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_copy_to_batch_size_callback_fn)(duckdb_v2_copy_to_batch_size_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_copy_to_init_callback_fn)(duckdb_v2_copy_to_init_info_handle info, + duckdb_v2_context_handle context, duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_copy_to_batch_callback_fn)(duckdb_v2_copy_to_batch_info_handle info, + duckdb_v2_context_handle context, duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_copy_to_flush_callback_fn)(duckdb_v2_copy_to_flush_info_handle info, + duckdb_v2_context_handle context, duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_copy_to_finalize_callback_fn)(duckdb_v2_copy_to_finalize_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_copy_to_statistics_callback_fn)(duckdb_v2_copy_to_statistics_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_copy_from_bind_callback_fn)(duckdb_v2_copy_from_bind_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_copy_from_init_global_callback_fn)(duckdb_v2_copy_from_init_global_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_copy_from_init_local_callback_fn)(duckdb_v2_copy_from_init_local_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_copy_from_exec_callback_fn)(duckdb_v2_copy_from_exec_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_copy_from_progress_callback_fn)(duckdb_v2_copy_from_progress_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +/* --- Functions for copy --- */ + +/*! + * Creates a new copy function through a factory. * - * True when the column declares a default expression; the engine evaluates it for rows that omit the column. Generated - * columns report false. + * A copy function implements a file format for `COPY`: once registered, SQL reaches it with `COPY ... TO 'path' (FORMAT + * name)` and `COPY table FROM 'path' (FORMAT name)`. The function starts out empty: configure it with the setter + * functions (e.g. `duckdb_v2_copy_function_set_name()`, the `copy_to_set_*` callbacks for writing, the + * `copy_from_set_*` callbacks for reading, or both), then make it available with + * `duckdb_v2_connection_register_copy_function()` or `duckdb_v2_extension_register_copy_function()`. The caller owns + * the returned handle and must destroy it with `duckdb_v2_copy_function_destroy()`, also after registration. + * + * The copy function can only be registered on the database the factory belongs to. * * history: * - stable: v2.0.0 * - * @param column The column description. - * @param has_default Receives whether the column has a default value. + * @param factory The factory to create the copy function through. + * @param function On success, receives the newly created copy function. Owned by the caller. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_column_description_has_default(duckdb_v2_column_description_handle column, - bool *has_default, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_function_create(duckdb_v2_factory_handle factory, + duckdb_v2_copy_function_handle *function, + duckdb_v2_error_info_handle *err); /*! - * Returns whether the column is generated. + * Sets the name of the copy function. * - * True when the column is a generated column, computed by the engine from a generation expression and not writable. + * The name is the format SQL selects the function with: `COPY ... TO 'path' (FORMAT name)` or `COPY table FROM 'path' + * (FORMAT name)`. It is borrowed and copied. Calling this again replaces the previous name. A name must be set before + * registration. * * history: * - stable: v2.0.0 * - * @param column The column description. - * @param has_generated Receives whether the column is generated. + * @param function The function to set the name of. + * @param name The name to set. Borrowed and copied. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_column_description_has_generated(duckdb_v2_column_description_handle column, - bool *has_generated, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_function_set_name(duckdb_v2_copy_function_handle function, + const duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err); /*! - * Destroys the column description, releasing its resources. + * Sets the file extension of the files the copy function writes, without the leading dot, e.g. `parquet`. * - * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent - * double-destruction. Any name or type borrowed from it becomes dangling. + * DuckDB uses it when it names files itself, e.g. the files of `COPY ... TO 'dir' (PARTITION_BY ...)` or + * `(PER_THREAD_OUTPUT)`. A statement can override it with the `FILE_EXTENSION` option. An extension starting with a dot + * is rejected. The extension is borrowed and copied. Calling this again replaces the previous extension. Optional, but + * without it the names DuckDB gives files end in a bare dot, e.g. `data_0.`. * * history: * - stable: v2.0.0 * - * @param column The column description to destroy. + * @param function The function to set the file extension of. + * @param extension The extension to set, without the leading dot. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_column_description_destroy(duckdb_v2_column_description_handle *column); - -/* --- Struct definitions for catalog --- */ - -/* ============================================================================ - * MODULE: connection - * ============================================================================ */ - -/* --- Enums for connection --- */ - -/* --- Struct forward declarations for connection --- */ - -/* --- Types for connection --- */ - -/* --- Constants for connection --- */ - -/* --- Function pointer typedefs for connection --- */ - -/* --- Functions for connection --- */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_function_set_file_extension(duckdb_v2_copy_function_handle function, + const duckdb_v2_str *extension, + duckdb_v2_error_info_handle *err); /*! - * Opens a connection to a database. + * Sets arbitrary user data on the copy function. * - * Each connection carries its own client context and session-scoped (LOCAL) settings. Connections to the same database - * share its catalog, buffer pool, and transaction manager. + * Associates an opaque pointer with the function, retrievable from each callback of either side via its user data + * accessor (e.g. `duckdb_v2_copy_to_bind_get_user_data()`, `duckdb_v2_copy_from_exec_get_user_data()`, etc.). The + * opaque handle bundles the pointer with an optional destructor, invoked when the data is no longer needed. * * history: * - stable: v2.0.0 * - * @param db The database to connect to. - * @param out_conn Receives the new connection handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the user data of. + * @param data Opaque handle bundling the user data pointer plus an optional destructor. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connect(duckdb_v2_database_handle db, duckdb_v2_connection_handle *out_conn, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_function_set_user_data(duckdb_v2_copy_function_handle function, + duckdb_v2_opaque *data, + duckdb_v2_error_info_handle *err); /*! - * Closes the connection. + * Sets the optional bind callback of the `COPY ... TO` side. * - * Always succeeds. Releases the connection's reference on the underlying database instance, which survives for as long - * as anything else still references it. On success the handle is set to null. + * The bind callback is invoked during query planning for each `COPY ... TO` statement that uses the function. It can + * inspect the names and types of the columns being written, read the statement's options and set "bind data" that is + * shared with the other `COPY ... TO` callbacks. * * history: * - stable: v2.0.0 * - * @param conn The connection to close. + * @param function The function to set the callback of. + * @param callback The callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_disconnect(duckdb_v2_connection_handle *conn); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_set_bind_callback(duckdb_v2_copy_function_handle function, + duckdb_v2_copy_to_bind_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Sets a config option through the connection. + * Sets the optional batch size callback of the `COPY ... TO` side. * - * Only the name and setting of the option are read; the remaining fields are ignored. `scope` chooses the destination, - * mirroring SQL: - * - AUTOMATIC resolves it from the option's target scope, like a bare `SET name = setting`. - * - GLOBAL writes through to the database, visible to all connections, like `SET GLOBAL`. - * - LOCAL writes to this connection's session only, like `SET LOCAL` / `SET SESSION`. - * A disallowed combination returns ERROR_INPUT_INVALID: GLOBAL against a LOCAL_ONLY option, LOCAL against a GLOBAL_ONLY - * one, and the legacy analogues. Unknown option names are rejected; to set an option whose defining extension has not - * loaded yet, use database scope or the startup options of open. + * The batch size callback is invoked during query planning, after the bind callback, for each `COPY ... TO` statement + * that does not set `BATCH_SIZE` itself. It should report how many rows a batch should carry via + * `duckdb_v2_copy_to_batch_size_set_target()`; the engine then cuts the rows being written into batches of that size + * and hands each to the batch callback. Without a batch size from either the statement or the callback, a batch is cut + * for every chunk of rows sunk, i.e. a vector at a time. A batch may still be smaller than the reported size (the last + * one of a file, or when `BATCH_SIZE_BYTES` cuts it first). * * history: * - stable: v2.0.0 * - * @param conn The connection. - * @param option The config option to apply. - * @param scope Target scope: AUTOMATIC, GLOBAL, or LOCAL. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the callback of. + * @param callback The callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_option_set(duckdb_v2_connection_handle conn, - duckdb_v2_option_handle option, - DUCKDB_V2_SETTING_SCOPE scope, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_set_batch_size_callback( + duckdb_v2_copy_function_handle function, duckdb_v2_copy_to_batch_size_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Reads a config option through the connection. + * Sets the optional init callback of the `COPY ... TO` side. * - * Returns the option's effective setting at the connection's scope: the LOCAL override if this connection set one, - * otherwise the GLOBAL value, otherwise the static default. The remaining fields are populated exactly as by - * database_option_get. Aliases resolve transparently, and an unknown name returns ERROR_INPUT_INVALID. The caller - * destroys the returned option. + * The init callback is invoked once per output file, before any batch destined for that file is prepared. It can read + * the path of the file via `duckdb_v2_copy_to_init_get_file_path()` and set "init data" that is shared with the batch, + * flush and finalize callbacks of that file. A statement may write several files, e.g. when its output is partitioned; + * each gets its own init data. * * history: * - stable: v2.0.0 * - * @param conn The connection. - * @param name Option name (canonical or alias). - * @param out_option Receives the populated option handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the callback of. + * @param callback The callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_option_get(duckdb_v2_connection_handle conn, - duckdb_v2_identifier_t name, - duckdb_v2_option_handle *out_option, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_set_init_callback(duckdb_v2_copy_function_handle function, + duckdb_v2_copy_to_init_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Returns the number of config options visible to this connection. + * Sets the batch callback of the `COPY ... TO` side. * - * The same count database_option_get_count reports for the underlying database: core plus extension options, aliases - * excluded. + * The batch callback is invoked during query execution with a batch of the rows being written, taken via + * `duckdb_v2_copy_to_batch_take_input()`. It prepares the batch for writing, e.g. by encoding it into the output + * format, and sets "batch data" via `duckdb_v2_copy_to_batch_set_batch_data()` that is handed to the flush callback. + * Batches may be prepared by several threads at once, so the callback must synchronize its own access to the init data. + * A batch callback must be set for the `COPY ... TO` side to be registered. * * history: * - stable: v2.0.0 * - * @param conn The connection. - * @param out_count Receives the option count. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the callback of. + * @param callback The callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_option_get_count(duckdb_v2_connection_handle conn, idx_t *out_count, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_set_batch_callback(duckdb_v2_copy_function_handle function, + duckdb_v2_copy_to_batch_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Reads the config option at the given index visible to this connection. + * Sets the flush callback of the `COPY ... TO` side. * - * Index space: [0, core_count) addresses core options, [core_count, total) the extension options visible from this - * connection's database. An out-of-range index returns ERROR_INPUT_INVALID. The caller destroys the returned option. + * The flush callback is invoked once per prepared batch to write its batch data, available via + * `duckdb_v2_copy_to_flush_get_batch_data()`, to the output. Flushes of the same file never run concurrently. A flush + * callback must be set for the `COPY ... TO` side to be registered. * * history: * - stable: v2.0.0 * - * @param conn The connection. - * @param index The option index, in [0, option_count). - * @param out_option Receives the populated option handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the callback of. + * @param callback The callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_option_get_by_index(duckdb_v2_connection_handle conn, idx_t index, - duckdb_v2_option_handle *out_option, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_set_flush_callback(duckdb_v2_copy_function_handle function, + duckdb_v2_copy_to_flush_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Interrupts the query currently executing on the connection. + * Sets the optional finalize callback of the `COPY ... TO` side. * - * The cross-thread cancellation entry point for streaming results: safe to call from any thread, including while - * another thread steps the query's result. A no-op when no query is active. Cancellation surfaces on the consuming side - * as step status CANCELLED (result_step), or as ERROR_RUNTIME_INTERRUPT (result_fetch_chunk). + * The finalize callback is invoked once per output file after the last batch destined for that file has been flushed. + * It can e.g. write a footer and close the file. * * history: * - stable: v2.0.0 * - * @param conn The connection whose active query to interrupt. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the callback of. + * @param callback The callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_interrupt(duckdb_v2_connection_handle conn, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_set_finalize_callback(duckdb_v2_copy_function_handle function, + duckdb_v2_copy_to_finalize_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Captures a snapshot of the active query's execution progress. + * Retrieves the user data set via `duckdb_v2_copy_function_set_user_data()`. * - * Reads the percentage and row counts from one consistent snapshot of the query currently executing on the connection. - * Safe to call from any thread, including while another thread steps the query's result. + * history: + * - stable: v2.0.0 * - * Progress is published only when the enable_progress_bar option is set; the bridge does not enable tracking itself. - * Both row counts are 0 when no information is available. The percentage is -1 when tracking is disabled, no query is - * active, or no progress has been published yet. - * - * history: - * - stable: v2.0.0 - * - * @param conn The connection handle. - * @param out_percentage Receives the percentage complete in [0, 100], -1 if tracking is disabled, no query is active, - * or no progress has been published yet. - * @param out_rows_processed Receives the number of rows processed so far. - * @param out_total_rows_to_process Receives the total number of rows the query will process. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The bind info handle. + * @param data Receives the user data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_progress_get(duckdb_v2_connection_handle conn, double *out_percentage, - uint64_t *out_rows_processed, - uint64_t *out_total_rows_to_process, - duckdb_v2_error_info_handle *err); - -/* --- Struct definitions for connection --- */ - -/* ============================================================================ - * MODULE: copy - * ============================================================================ */ - -/* --- Enums for copy --- */ - -/* --- Struct forward declarations for copy --- */ - -/* --- Types for copy --- */ - -/*! - * An owned opaque handle to a custom copy function being built: an output format for `COPY ... TO`, an input format for - * `COPY ... FROM`, or both. Created with `duckdb_v2_copy_function_create_with_connection()` or - * `duckdb_v2_copy_function_create_with_extension()`, configured with the setter functions (e.g. - * `duckdb_v2_copy_function_set_name()`, `duckdb_v2_copy_to_set_batch_callback()`, - * `duckdb_v2_copy_from_set_exec_callback()`, etc.), made available with `duckdb_v2_copy_function_register()`, and - * destroyed with `duckdb_v2_copy_function_destroy()`. - */ -typedef struct _duckdb_v2_copy_function { - void *internal_ptr; -} * duckdb_v2_copy_function_handle; - -/*! - * A borrowed opaque handle to the arguments supplied to a copy function during the query preparation "bind" phase of a - * `COPY ... TO` statement. The "bind" callback receives this handle and can use it to e.g. inspect the names and types - * of the columns being written, read the statement's options and initialize some constant state. - */ -typedef struct _duckdb_v2_copy_to_bind_info { - void *internal_ptr; -} * duckdb_v2_copy_to_bind_info_handle; - -/*! - * A borrowed opaque handle to the arguments supplied to a copy function during the "batch size" phase of a `COPY ... - * TO` statement. The "batch size" callback receives this handle and must use it to report how many rows a batch should - * carry. - */ -typedef struct _duckdb_v2_copy_to_batch_size_info { - void *internal_ptr; -} * duckdb_v2_copy_to_batch_size_info_handle; - -/*! - * A borrowed opaque handle to the arguments supplied to a copy function during the per-file "init" phase of a `COPY ... - * TO` statement. The "init" callback receives this handle and can use it to e.g. read the path of the file being - * written and set up the state shared by every batch written to that file. - */ -typedef struct _duckdb_v2_copy_to_init_info { - void *internal_ptr; -} * duckdb_v2_copy_to_init_info_handle; - -/*! - * A borrowed opaque handle to the arguments supplied to a copy function during the "batch" phase of a `COPY ... TO` - * statement. The "batch" callback receives this handle and can use it to take ownership of the rows of the batch and to - * set the prepared form of the batch handed to the "flush" callback. - */ -typedef struct _duckdb_v2_copy_to_batch_info { - void *internal_ptr; -} * duckdb_v2_copy_to_batch_info_handle; - -/*! - * A borrowed opaque handle to the arguments supplied to a copy function during the "flush" phase of a `COPY ... TO` - * statement. The "flush" callback receives this handle and can use it to access the prepared batch and write it to the - * output. - */ -typedef struct _duckdb_v2_copy_to_flush_info { - void *internal_ptr; -} * duckdb_v2_copy_to_flush_info_handle; - -/*! - * A borrowed opaque handle to the arguments supplied to a copy function during the per-file "finalize" phase of a `COPY - * ... TO` statement. The "finalize" callback receives this handle and can use it to e.g. close the file after every - * batch has been flushed to it. - */ -typedef struct _duckdb_v2_copy_to_finalize_info { - void *internal_ptr; -} * duckdb_v2_copy_to_finalize_info_handle; - -/*! - * A borrowed opaque handle to the arguments supplied to a copy function during the query preparation "bind" phase of a - * `COPY ... FROM` statement. The "bind" callback receives this handle and can use it to e.g. read the path of the file - * to read, inspect the names and types of the columns the target table expects, read the statement's options, hint at - * the number of rows the read will produce and initialize some constant state. - */ -typedef struct _duckdb_v2_copy_from_bind_info { - void *internal_ptr; -} * duckdb_v2_copy_from_bind_info_handle; - -/*! - * A borrowed opaque handle to the arguments supplied to a copy function during the global state initialization "init - * global" phase of a `COPY ... FROM` statement. The "init global" callback receives this handle and can use it to e.g. - * set up the state shared by every thread reading the file, and to declare how many threads may read it. - */ -typedef struct _duckdb_v2_copy_from_init_global_info { - void *internal_ptr; -} * duckdb_v2_copy_from_init_global_info_handle; - -/*! - * A borrowed opaque handle to the arguments supplied to a copy function during the local state initialization "init - * local" phase of a `COPY ... FROM` statement. The "init local" callback receives this handle and can use it to e.g. - * set up worker-local state, typically by claiming work from the shared global state. - */ -typedef struct _duckdb_v2_copy_from_init_local_info { - void *internal_ptr; -} * duckdb_v2_copy_from_init_local_info_handle; - -/*! - * A borrowed opaque handle to the arguments supplied to a copy function during the execution "exec" phase of a `COPY - * ... FROM` statement. The "exec" callback receives this handle and can use it to e.g. access the global and local - * state and write the next batch of rows to the output chunk. - */ -typedef struct _duckdb_v2_copy_from_exec_info { - void *internal_ptr; -} * duckdb_v2_copy_from_exec_info_handle; - -/*! - * A borrowed opaque handle to the arguments supplied to a copy function during the "progress" phase of a `COPY ... - * FROM` statement. The "progress" callback receives this handle and can use it to report how far the read has advanced. - */ -typedef struct _duckdb_v2_copy_from_progress_info { - void *internal_ptr; -} * duckdb_v2_copy_from_progress_info_handle; - -/* --- Constants for copy --- */ - -/* --- Function pointer typedefs for copy --- */ - -typedef void (*duckdb_v2_copy_to_bind_callback_fn)(duckdb_v2_copy_to_bind_info_handle info, - duckdb_v2_context_handle context, duckdb_v2_error_info_handle *err); - -typedef void (*duckdb_v2_copy_to_batch_size_callback_fn)(duckdb_v2_copy_to_batch_size_info_handle info, - duckdb_v2_context_handle context, - duckdb_v2_error_info_handle *err); - -typedef void (*duckdb_v2_copy_to_init_callback_fn)(duckdb_v2_copy_to_init_info_handle info, - duckdb_v2_context_handle context, duckdb_v2_error_info_handle *err); - -typedef void (*duckdb_v2_copy_to_batch_callback_fn)(duckdb_v2_copy_to_batch_info_handle info, - duckdb_v2_context_handle context, duckdb_v2_error_info_handle *err); - -typedef void (*duckdb_v2_copy_to_flush_callback_fn)(duckdb_v2_copy_to_flush_info_handle info, - duckdb_v2_context_handle context, duckdb_v2_error_info_handle *err); - -typedef void (*duckdb_v2_copy_to_finalize_callback_fn)(duckdb_v2_copy_to_finalize_info_handle info, - duckdb_v2_context_handle context, - duckdb_v2_error_info_handle *err); - -typedef void (*duckdb_v2_copy_from_bind_callback_fn)(duckdb_v2_copy_from_bind_info_handle info, - duckdb_v2_context_handle context, - duckdb_v2_error_info_handle *err); - -typedef void (*duckdb_v2_copy_from_init_global_callback_fn)(duckdb_v2_copy_from_init_global_info_handle info, - duckdb_v2_context_handle context, - duckdb_v2_error_info_handle *err); - -typedef void (*duckdb_v2_copy_from_init_local_callback_fn)(duckdb_v2_copy_from_init_local_info_handle info, - duckdb_v2_context_handle context, - duckdb_v2_error_info_handle *err); - -typedef void (*duckdb_v2_copy_from_exec_callback_fn)(duckdb_v2_copy_from_exec_info_handle info, - duckdb_v2_context_handle context, - duckdb_v2_error_info_handle *err); - -typedef void (*duckdb_v2_copy_from_progress_callback_fn)(duckdb_v2_copy_from_progress_info_handle info, - duckdb_v2_context_handle context, - duckdb_v2_error_info_handle *err); - -/* --- Functions for copy --- */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_bind_get_user_data(duckdb_v2_copy_to_bind_info_handle info, void **data, + duckdb_v2_error_info_handle *err); /*! - * Creates a new copy function that will be registered on the connection's database. + * Sets the function's "bind data" from the bind callback. * - * A copy function implements a file format for `COPY`: once registered, SQL reaches it with `COPY ... TO 'path' (FORMAT - * name)` and `COPY table FROM 'path' (FORMAT name)`. The function starts out empty: configure it with the setter - * functions (e.g. `duckdb_v2_copy_function_set_name()`, the `copy_to_set_*` callbacks for writing, the - * `copy_from_set_*` callbacks for reading, or both), then make it available with `duckdb_v2_copy_function_register()`. - * The caller owns the returned handle and must destroy it with `duckdb_v2_copy_function_destroy()`, also after - * registration. + * The bind data is stored with the bound statement and retrievable from the other callbacks of the same side. The + * opaque handle bundles the pointer with an optional destructor, invoked when the bind data is no longer needed, and an + * optional equality callback used when comparing two bound statements; without one, pointer equality is used. * * history: * - stable: v2.0.0 * - * @param connection The connection to create the function in. - * @param function On success, receives the newly created copy function. Owned by the caller. + * @param info The bind info handle. + * @param data Opaque handle bundling the bind data pointer plus optional destructor and equality callbacks. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_function_create_with_connection(duckdb_v2_connection_handle connection, - duckdb_v2_copy_function_handle *function, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_bind_set_bind_data(duckdb_v2_copy_to_bind_info_handle info, + duckdb_v2_opaque *data, + duckdb_v2_error_info_handle *err); /*! - * Creates a new copy function that will be registered on the loading extension's database. + * Retrieves the path the `COPY ... TO` statement writes to, as written in the statement. * - * A copy function implements a file format for `COPY`: once registered, SQL reaches it with `COPY ... TO 'path' (FORMAT - * name)` and `COPY table FROM 'path' (FORMAT name)`. Use this from an extension load callback, where an extension - * handle is available. The function starts out empty: configure it with the setter functions (e.g. - * `duckdb_v2_copy_function_set_name()`, the `copy_to_set_*` callbacks for writing, the `copy_from_set_*` callbacks for - * reading, or both), then make it available with `duckdb_v2_copy_function_register()`. The caller owns the returned - * handle and must destroy it with `duckdb_v2_copy_function_destroy()`, also after registration. + * This is the target before the engine's own rewrites: the path of each file actually being written, e.g. a temporary + * name or a per-partition path, is only known to the init callback via `duckdb_v2_copy_to_init_get_file_path()`. The + * path is borrowed and valid only for the duration of the callback. * * history: * - stable: v2.0.0 * - * @param extension The extension to create the function in. - * @param function On success, receives the newly created copy function. Owned by the caller. + * @param info The bind info handle. + * @param path Receives a borrowed view of the file path. Valid only for the duration of the callback. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_function_create_with_extension(duckdb_v2_extension_handle extension, - duckdb_v2_copy_function_handle *function, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_bind_get_file_path(duckdb_v2_copy_to_bind_info_handle info, + duckdb_v2_str *path, + duckdb_v2_error_info_handle *err); /*! - * Sets the name of the copy function. + * Returns the number of columns being written, i.e. the columns of the rows every batch carries. * - * The name is the format SQL selects the function with: `COPY ... TO 'path' (FORMAT name)` or `COPY table FROM 'path' - * (FORMAT name)`. It is borrowed and copied. Calling this again replaces the previous name. A name must be set before - * registration. + * Valid indices for `duckdb_v2_copy_to_bind_get_column_type()` and `duckdb_v2_copy_to_bind_get_column_name()` are [0, + * count). * * history: * - stable: v2.0.0 * - * @param function The function to set the name of. - * @param name The name to set. Borrowed and copied. + * @param info The bind info handle. + * @param count Receives the number of columns. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_function_set_name(duckdb_v2_copy_function_handle function, - duckdb_v2_str *name, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_bind_get_column_count(duckdb_v2_copy_to_bind_info_handle info, + idx_t *count, duckdb_v2_error_info_handle *err); /*! - * Sets arbitrary user data on the copy function. + * Retrieves the type of the column at the given index. * - * Associates an opaque pointer with the function, retrievable from each callback of either side via its user data - * accessor (e.g. `duckdb_v2_copy_to_bind_get_user_data()`, `duckdb_v2_copy_from_exec_get_user_data()`, etc.). The - * opaque handle bundles the pointer with an optional destructor, invoked when the data is no longer needed. + * Fails if the index is out of bounds. The returned type is owned by the caller and must be destroyed via + * `duckdb_v2_logical_type_destroy()`. * * history: * - stable: v2.0.0 * - * @param function The function to set the user data of. - * @param data Opaque handle bundling the user data pointer plus an optional destructor. + * @param info The bind info handle. + * @param index The index of the column to get the type of. + * @param type Receives the column type. Owned by the caller; destroy via `duckdb_v2_logical_type_destroy()`. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_function_set_user_data(duckdb_v2_copy_function_handle function, - duckdb_v2_opaque *data, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_bind_get_column_type(duckdb_v2_copy_to_bind_info_handle info, + idx_t index, duckdb_v2_logical_type_handle *type, + duckdb_v2_error_info_handle *err); /*! - * Sets the optional bind callback of the `COPY ... TO` side. + * Retrieves the name of the column at the given index. * - * The bind callback is invoked during query planning for each `COPY ... TO` statement that uses the function. It can - * inspect the names and types of the columns being written, read the statement's options and set "bind data" that is - * shared with the other `COPY ... TO` callbacks. + * Fails if the index is out of bounds. The name is borrowed and valid only for the duration of the callback. * * history: * - stable: v2.0.0 * - * @param function The function to set the callback of. - * @param callback The callback to set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_set_bind_callback(duckdb_v2_copy_function_handle function, - duckdb_v2_copy_to_bind_callback_fn callback, - duckdb_v2_error_info_handle *err); - -/*! - * Sets the optional batch size callback of the `COPY ... TO` side. - * - * The batch size callback is invoked during query planning, after the bind callback, for each `COPY ... TO` statement - * that does not set `BATCH_SIZE` itself. It must report how many rows a batch should carry via - * `duckdb_v2_copy_to_batch_size_set_target()`; the engine then cuts the rows being written into batches of that size - * and hands each to the batch callback. Without a batch size from either the statement or the callback, a batch is cut - * for every chunk of rows sunk, i.e. a vector at a time. A batch may still be smaller than the reported size (the last - * one of a file, or when `BATCH_SIZE_BYTES` cuts it first). - * - * history: - * - stable: v2.0.0 - * - * @param function The function to set the callback of. - * @param callback The callback to set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_set_batch_size_callback( - duckdb_v2_copy_function_handle function, duckdb_v2_copy_to_batch_size_callback_fn callback, - duckdb_v2_error_info_handle *err); - -/*! - * Sets the optional init callback of the `COPY ... TO` side. - * - * The init callback is invoked once per output file, before any batch destined for that file is prepared. It can read - * the path of the file via `duckdb_v2_copy_to_init_get_file_path()` and set "init data" that is shared with the batch, - * flush and finalize callbacks of that file. A statement may write several files, e.g. when its output is partitioned; - * each gets its own init data. - * - * history: - * - stable: v2.0.0 - * - * @param function The function to set the callback of. - * @param callback The callback to set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_set_init_callback(duckdb_v2_copy_function_handle function, - duckdb_v2_copy_to_init_callback_fn callback, - duckdb_v2_error_info_handle *err); - -/*! - * Sets the batch callback of the `COPY ... TO` side. - * - * The batch callback is invoked during query execution with a batch of the rows being written, taken via - * `duckdb_v2_copy_to_batch_take_input()`. It prepares the batch for writing, e.g. by encoding it into the output - * format, and sets "batch data" via `duckdb_v2_copy_to_batch_set_batch_data()` that is handed to the flush callback. - * Batches may be prepared by several threads at once, so the callback must synchronize its own access to the init data. - * A batch callback must be set for the `COPY ... TO` side to be registered. - * - * history: - * - stable: v2.0.0 - * - * @param function The function to set the callback of. - * @param callback The callback to set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_set_batch_callback(duckdb_v2_copy_function_handle function, - duckdb_v2_copy_to_batch_callback_fn callback, - duckdb_v2_error_info_handle *err); - -/*! - * Sets the flush callback of the `COPY ... TO` side. - * - * The flush callback is invoked once per prepared batch to write its batch data, available via - * `duckdb_v2_copy_to_flush_get_batch_data()`, to the output. Flushes of the same file never run concurrently. A flush - * callback must be set for the `COPY ... TO` side to be registered. - * - * history: - * - stable: v2.0.0 - * - * @param function The function to set the callback of. - * @param callback The callback to set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_set_flush_callback(duckdb_v2_copy_function_handle function, - duckdb_v2_copy_to_flush_callback_fn callback, - duckdb_v2_error_info_handle *err); - -/*! - * Sets the optional finalize callback of the `COPY ... TO` side. - * - * The finalize callback is invoked once per output file after the last batch destined for that file has been flushed. - * It can e.g. write a footer and close the file. - * - * history: - * - stable: v2.0.0 - * - * @param function The function to set the callback of. - * @param callback The callback to set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_set_finalize_callback(duckdb_v2_copy_function_handle function, - duckdb_v2_copy_to_finalize_callback_fn callback, - duckdb_v2_error_info_handle *err); - -/*! - * Retrieves the user data set via `duckdb_v2_copy_function_set_user_data()`. - * - * history: - * - stable: v2.0.0 - * - * @param info The bind info handle. - * @param data Receives the user data pointer, or null if none was set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_bind_get_user_data(duckdb_v2_copy_to_bind_info_handle info, void **data, - duckdb_v2_error_info_handle *err); - -/*! - * Sets the function's "bind data" from the bind callback. - * - * The bind data is stored with the bound statement and retrievable from the other callbacks of the same side. The - * opaque handle bundles the pointer with an optional destructor, invoked when the bind data is no longer needed, and an - * optional equality callback used when comparing two bound statements; without one, pointer equality is used. - * - * history: - * - stable: v2.0.0 - * - * @param info The bind info handle. - * @param data Opaque handle bundling the bind data pointer plus optional destructor and equality callbacks. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_bind_set_bind_data(duckdb_v2_copy_to_bind_info_handle info, - duckdb_v2_opaque *data, - duckdb_v2_error_info_handle *err); - -/*! - * Retrieves the path the `COPY ... TO` statement writes to, as written in the statement. - * - * This is the target before the engine's own rewrites: the path of each file actually being written, e.g. a temporary - * name or a per-partition path, is only known to the init callback via `duckdb_v2_copy_to_init_get_file_path()`. The - * path is borrowed and valid only for the duration of the callback. - * - * history: - * - stable: v2.0.0 - * - * @param info The bind info handle. - * @param path Receives a borrowed view of the file path. Valid only for the duration of the callback. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_bind_get_file_path(duckdb_v2_copy_to_bind_info_handle info, - duckdb_v2_str *path, - duckdb_v2_error_info_handle *err); - -/*! - * Returns the number of columns being written, i.e. the columns of the rows every batch carries. - * - * Valid indices for `duckdb_v2_copy_to_bind_get_column_type()` and `duckdb_v2_copy_to_bind_get_column_name()` are [0, - * count). - * - * history: - * - stable: v2.0.0 - * - * @param info The bind info handle. - * @param count Receives the number of columns. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_bind_get_column_count(duckdb_v2_copy_to_bind_info_handle info, - idx_t *count, duckdb_v2_error_info_handle *err); - -/*! - * Retrieves the type of the column at the given index. - * - * Fails if the index is out of bounds. The returned type is owned by the caller and must be destroyed via - * `duckdb_v2_logical_type_destroy()`. - * - * history: - * - stable: v2.0.0 - * - * @param info The bind info handle. - * @param index The index of the column to get the type of. - * @param type Receives the column type. Owned by the caller; destroy via `duckdb_v2_logical_type_destroy()`. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_bind_get_column_type(duckdb_v2_copy_to_bind_info_handle info, - idx_t index, duckdb_v2_logical_type_handle *type, - duckdb_v2_error_info_handle *err); - -/*! - * Retrieves the name of the column at the given index. - * - * Fails if the index is out of bounds. The name is borrowed and valid only for the duration of the callback. - * - * history: - * - stable: v2.0.0 - * - * @param info The bind info handle. - * @param index The index of the column to get the name of. - * @param name Receives a borrowed view of the column name. Valid only for the duration of the callback. + * @param info The bind info handle. + * @param index The index of the column to get the name of. + * @param name Receives a borrowed view of the column name. Valid only for the duration of the callback. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR @@ -6085,7 +6094,8 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_batch_size_get_bind_data(duckdb_v /*! * Sets the number of rows a batch should carry, as the target the engine cuts batches at. The batch size callback must - * set this to a value greater than 0; the statement fails otherwise. + * set this to a value greater than 0; the statement fails otherwise. DuckDB defines the `BATCH_SIZE` on omission of + * calling the function. * * history: * - stable: v2.0.0 @@ -6944,22 +6954,49 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_from_progress_set_progress(duckdb_v2 /*! * Registers the copy function, making it available as a `COPY` format. * - * The function is registered on the target given at creation: the connection's database or the loading extension. - * Registration requires a name and at least one configured side: a `COPY ... TO` side needs its batch and flush - * callbacks, a `COPY ... FROM` side needs its bind and exec callbacks. A statement in a direction the function does not - * implement fails with an error. The caller still owns the handle after registration and must destroy it with + * The function is registered on the connection's database, visible to every connection, in the connection's + * transaction: inside an explicit transaction, it is invisible to other connections until the transaction commits, and + * a rollback undoes it. Registration requires a name and at least one configured side: a `COPY ... TO` side needs its + * batch and flush callbacks, a `COPY ... FROM` side needs its bind and exec callbacks. A statement in a direction the + * function does not implement fails with an error. The copy function must have been created through a factory of the + * same database. The caller still owns the handle after registration and must destroy it with * `duckdb_v2_copy_function_destroy()`, which does not affect the registered function. * * history: * - stable: v2.0.0 * - * @param function The function to register. + * @param conn The connection to register on. + * @param function The copy function to register. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_function_register(duckdb_v2_copy_function_handle function, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_register_copy_function(duckdb_v2_connection_handle conn, + duckdb_v2_copy_function_handle function, + duckdb_v2_error_info_handle *err); + +/*! + * Registers the copy function, making it available as a `COPY` format. + * + * The function is registered on the loading extension's database and attributed to the extension. Use this from the + * extension's load callback. Registration requires a name and at least one configured side: a `COPY ... TO` side needs + * its batch and flush callbacks, a `COPY ... FROM` side needs its bind and exec callbacks. A statement in a direction + * the function does not implement fails with an error. The copy function must have been created through a factory of + * the same database. The caller still owns the handle after registration and must destroy it with + * `duckdb_v2_copy_function_destroy()`, which does not affect the registered function. + * + * history: + * - stable: v2.0.0 + * + * @param extension The loading extension. + * @param function The copy function to register. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_extension_register_copy_function(duckdb_v2_extension_handle extension, + duckdb_v2_copy_function_handle function, + duckdb_v2_error_info_handle *err); /*! * Destroys the copy function, releasing its resources. @@ -6975,1772 +7012,1947 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_function_register(duckdb_v2_copy_fun */ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_function_destroy(duckdb_v2_copy_function_handle *function); -/* --- Struct definitions for copy --- */ - -/* ============================================================================ - * MODULE: expression - * ============================================================================ */ - -/* --- Enums for expression --- */ - +#if DUCKDB_V2_API_ALLOW_UNSTABLE /*! - * The type of a bound expression node. The values mirror the engine's own expression types, restricted to the node - * types a filter predicate can contain; every other node type is reported as `EXPRESSION_TYPE_INVALID`. + * Sets the optional statistics callback of the `COPY ... TO` side. * - * Comparisons, `BETWEEN` and casts are regular scalar function calls once bound: they have the function's children and - * name, and only their type tells them apart from any other function. A `CAST` therefore has one child, the value being - * cast, and its target type is the node's return type. + * The statistics callback reports statistics about a written file, such as the number of rows and bytes it holds, which + * `COPY ... TO` returns when the statement asks for them with `RETURN_STATS`. It is invoked once per output file, after + * the finalize callback of that file, and only when the statement asks for the statistics. Without a statistics + * callback, `RETURN_STATS` is rejected for the function. + * + * history: + * - unstable: v2.0.0 + * + * @param function The function to set the callback of. + * @param callback The callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR */ -typedef enum DUCKDB_V2_EXPRESSION_TYPE { - //! A node type this API does not model. Its children can still be walked. - DUCKDB_V2_EXPRESSION_TYPE_INVALID = 0, - - /*! - * A cast. One child; the target type is the node's return type and `duckdb_v2_expression_cast_get_mode()` tells a - * `TRY_CAST` apart. - */ - DUCKDB_V2_EXPRESSION_TYPE_OPERATOR_CAST = 12, - - //! Logical `NOT`. One child. - DUCKDB_V2_EXPRESSION_TYPE_OPERATOR_NOT = 13, - - //! `IS NULL`. One child. - DUCKDB_V2_EXPRESSION_TYPE_OPERATOR_IS_NULL = 14, - - //! `IS NOT NULL`. One child. - DUCKDB_V2_EXPRESSION_TYPE_OPERATOR_IS_NOT_NULL = 15, - - //! `=`. Two children. - DUCKDB_V2_EXPRESSION_TYPE_COMPARE_EQUAL = 25, - - //! `<>`. Two children. - DUCKDB_V2_EXPRESSION_TYPE_COMPARE_NOTEQUAL = 26, - - //! `<`. Two children. - DUCKDB_V2_EXPRESSION_TYPE_COMPARE_LESSTHAN = 27, - - //! `>`. Two children. - DUCKDB_V2_EXPRESSION_TYPE_COMPARE_GREATERTHAN = 28, - - //! `<=`. Two children. - DUCKDB_V2_EXPRESSION_TYPE_COMPARE_LESSTHANOREQUALTO = 29, - - //! `>=`. Two children. - DUCKDB_V2_EXPRESSION_TYPE_COMPARE_GREATERTHANOREQUALTO = 30, - - //! `IN`. The first child is the value tested, the remaining children are the candidates. - DUCKDB_V2_EXPRESSION_TYPE_COMPARE_IN = 35, - - //! `NOT IN`. The first child is the value tested, the remaining children are the candidates. - DUCKDB_V2_EXPRESSION_TYPE_COMPARE_NOT_IN = 36, - - //! `IS DISTINCT FROM`. Two children. - DUCKDB_V2_EXPRESSION_TYPE_COMPARE_DISTINCT_FROM = 37, - - //! `BETWEEN`. Three children: the value tested, the lower bound and the upper bound. - DUCKDB_V2_EXPRESSION_TYPE_COMPARE_BETWEEN = 38, +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_set_statistics_callback( + duckdb_v2_copy_function_handle function, duckdb_v2_copy_to_statistics_callback_fn callback, + duckdb_v2_error_info_handle *err); +#endif - //! `IS NOT DISTINCT FROM`. Two children. - DUCKDB_V2_EXPRESSION_TYPE_COMPARE_NOT_DISTINCT_FROM = 40, +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the user data set on the copy function via `duckdb_v2_copy_function_set_user_data()`. + * + * history: + * - unstable: v2.0.0 + * + * @param info The statistics info handle. + * @param data Receives the user data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_statistics_get_user_data(duckdb_v2_copy_to_statistics_info_handle info, + void **data, duckdb_v2_error_info_handle *err); +#endif - //! Logical `AND`. Two or more children. - DUCKDB_V2_EXPRESSION_TYPE_CONJUNCTION_AND = 50, +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the bind data set by the function's `COPY ... TO` bind callback. + * + * history: + * - unstable: v2.0.0 + * + * @param info The statistics info handle. + * @param data Receives the bind data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_statistics_get_bind_data(duckdb_v2_copy_to_statistics_info_handle info, + void **data, duckdb_v2_error_info_handle *err); +#endif - //! Logical `OR`. Two or more children. - DUCKDB_V2_EXPRESSION_TYPE_CONJUNCTION_OR = 51, +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the init data set by the function's `COPY ... TO` init callback for the file being reported. + * + * history: + * - unstable: v2.0.0 + * + * @param info The statistics info handle. + * @param data Receives the init data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_statistics_get_init_data(duckdb_v2_copy_to_statistics_info_handle info, + void **data, duckdb_v2_error_info_handle *err); +#endif - //! A constant. No children; read it with `duckdb_v2_expression_constant_get_value()`. - DUCKDB_V2_EXPRESSION_TYPE_VALUE_CONSTANT = 75, +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Reports how many rows were written to the file. Reported as 0 when the callback does not set it. + * + * history: + * - unstable: v2.0.0 + * + * @param info The statistics info handle. + * @param row_count The number of rows written to the file. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_statistics_set_row_count(duckdb_v2_copy_to_statistics_info_handle info, + idx_t row_count, + duckdb_v2_error_info_handle *err); +#endif - //! A prepared statement parameter whose value is not known yet. No children. - DUCKDB_V2_EXPRESSION_TYPE_VALUE_PARAMETER = 76, +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Reports the size of the written file in bytes. Reported as 0 when the callback does not set it. + * + * history: + * - unstable: v2.0.0 + * + * @param info The statistics info handle. + * @param file_size_bytes The size of the file in bytes. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_copy_to_statistics_set_file_size(duckdb_v2_copy_to_statistics_info_handle info, + idx_t file_size_bytes, + duckdb_v2_error_info_handle *err); +#endif - /*! - * A call to a scalar function other than the ones listed above. The children are its arguments; read the name with - * `duckdb_v2_expression_function_get_name()`. - */ - DUCKDB_V2_EXPRESSION_TYPE_BOUND_FUNCTION = 141, +/* --- Struct definitions for copy --- */ - //! A `CASE` expression. The children are each `WHEN` condition followed by its `THEN` result, then the `ELSE` - //! result. - DUCKDB_V2_EXPRESSION_TYPE_CASE_EXPR = 150, +/* ============================================================================ + * MODULE: custom_option + * ============================================================================ */ - //! `COALESCE`. One or more children. - DUCKDB_V2_EXPRESSION_TYPE_OPERATOR_COALESCE = 152, +/* --- Enums for custom_option --- */ - //! A reference to a column. No children; read it with `duckdb_v2_expression_column_ref_get_index()`. - DUCKDB_V2_EXPRESSION_TYPE_BOUND_COLUMN_REF = 228, - DUCKDB_V2_EXPRESSION_TYPE_MAX_ENUM = 0x7FFFFFFF, -} DUCKDB_V2_EXPRESSION_TYPE; +/* --- Struct forward declarations for custom_option --- */ -/* --- Struct forward declarations for expression --- */ +/* --- Types for custom_option --- */ -/* --- Types for expression --- */ +/*! + * An owned opaque handle to a custom option being built. Created with `duckdb_v2_custom_option_create()`, configured + * with the setter functions (e.g. `duckdb_v2_custom_option_set_name()`, `duckdb_v2_custom_option_set_type()`, etc.), + * made available with `duckdb_v2_connection_register_custom_option()` or + * `duckdb_v2_extension_register_custom_option()`, and destroyed with `duckdb_v2_custom_option_destroy()`. + */ +typedef struct _duckdb_v2_custom_option { + void *internal_ptr; +} * duckdb_v2_custom_option_handle; /*! - * A borrowed opaque handle to a node of a bound expression tree. Read-only and owned by the engine: valid only for the - * duration of the callback that handed it out, and never destroyed by the caller. Child nodes borrowed via - * `duckdb_v2_expression_get_child()` share their parent's lifetime. + * A borrowed opaque handle to an update of a custom option, received by its update callback. It gives access to the new + * value, the scope it is written at and the user data, and can replace the value. */ -typedef struct _duckdb_v2_expression { +typedef struct _duckdb_v2_custom_option_update_info { void *internal_ptr; -} * duckdb_v2_expression_handle; +} * duckdb_v2_custom_option_update_info_handle; -/* --- Constants for expression --- */ +/* --- Constants for custom_option --- */ -/* --- Function pointer typedefs for expression --- */ +/* --- Function pointer typedefs for custom_option --- */ -/* --- Functions for expression --- */ +typedef void (*duckdb_v2_custom_option_update_callback_fn)(duckdb_v2_custom_option_update_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +/* --- Functions for custom_option --- */ /*! - * Returns the type of an expression node. + * Creates a new custom option through a factory. * - * The type decides which of the type-specific accessors apply, and what the node's children mean; see - * `EXPRESSION_TYPE`. A node of a type this API does not model reports `EXPRESSION_TYPE_INVALID`. + * The option starts out empty: configure it with the setter functions, then make it available with + * `duckdb_v2_connection_register_custom_option()` or `duckdb_v2_extension_register_custom_option()`. The caller owns + * the returned handle and must destroy it with `duckdb_v2_custom_option_destroy()`, also after registration. + * + * The custom option can only be registered on the instance the factory belongs to. * * history: * - stable: v2.0.0 * - * @param expression The expression node. - * @param type Receives the node type. + * @param factory The factory to create the custom option through. + * @param option On success, receives the newly created custom option. Owned by the caller. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_expression_get_type(duckdb_v2_expression_handle expression, - DUCKDB_V2_EXPRESSION_TYPE *type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_option_create(duckdb_v2_factory_handle factory, + duckdb_v2_custom_option_handle *option, + duckdb_v2_error_info_handle *err); /*! - * Returns the logical type the expression node evaluates to. + * Sets the name of the custom option, as `SET` and the config functions refer to it. * - * For a cast this is the target type. The returned type is owned by the caller and must be destroyed via - * `duckdb_v2_logical_type_destroy()`. + * The name is borrowed and copied. Calling this again replaces the previous name. A name must be set before + * registration. * * history: * - stable: v2.0.0 * - * @param expression The expression node. - * @param type Receives the owned logical type. + * @param option The option to set the name of. + * @param name The name to set. Borrowed and copied. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_expression_get_return_type(duckdb_v2_expression_handle expression, - duckdb_v2_logical_type_handle *type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_option_set_name(duckdb_v2_custom_option_handle option, + const duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err); /*! - * Returns the number of child nodes of an expression node. + * Sets the type of the custom option. Every value written to the option is cast to it, as SQL `SET` does. * - * Works for every node type, including `EXPRESSION_TYPE_INVALID`. Constants, parameters and column references have no - * children. + * The type is borrowed and copied. Optional when a default value is set, whose type the option then takes. * * history: * - stable: v2.0.0 * - * @param expression The expression node. - * @param count Receives the number of children. + * @param option The option to set the type of. + * @param type The type to set. Borrowed and copied. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_expression_get_child_count(duckdb_v2_expression_handle expression, idx_t *count, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_option_set_type(duckdb_v2_custom_option_handle option, + duckdb_v2_logical_type_handle type, + duckdb_v2_error_info_handle *err); /*! - * Retrieves a child node of an expression node by index. + * Sets the default value of the custom option: its value until it is set, and the value `RESET` restores. * - * Fails if the index is out of bounds. Children are ordered as the node type describes (see `EXPRESSION_TYPE`). The - * child is borrowed and shares the lifetime of its parent. + * The value is borrowed and copied, and cast to the option's type at registration, which fails when the cast does. + * Without a default value the option reads as NULL until it is set. * * history: * - stable: v2.0.0 * - * @param expression The expression node. - * @param index The index of the child to retrieve. - * @param child Receives the borrowed child node. + * @param option The option to set the default value of. + * @param value The default value. Borrowed and copied. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_expression_get_child(duckdb_v2_expression_handle expression, idx_t index, - duckdb_v2_expression_handle *child, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_option_set_default_value(duckdb_v2_custom_option_handle option, + duckdb_v2_value_handle value, + duckdb_v2_error_info_handle *err); /*! - * Returns the value of a constant node. + * Sets the description of the custom option, as `duckdb_v2_option_get_description()` and `duckdb_settings()` report it. * - * Fails if the node is not of type `EXPRESSION_TYPE_VALUE_CONSTANT`. The returned value is owned by the caller and must - * be destroyed via `duckdb_v2_value_destroy()`. + * The description is borrowed and copied. Calling this again replaces the previous description. * * history: * - stable: v2.0.0 * - * @param expression The constant node. - * @param value Receives the owned constant value. + * @param option The option to set the description of. + * @param description The description to set. Borrowed and copied. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_expression_constant_get_value(duckdb_v2_expression_handle expression, - duckdb_v2_value_handle *value, +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_option_set_description(duckdb_v2_custom_option_handle option, + const duckdb_v2_str *description, duckdb_v2_error_info_handle *err); /*! - * Returns the column a column reference node points at. + * Sets the scope a write at `SETTING_SCOPE_DEFAULT` goes to, like SQL `SET name = value`. * - * Fails if the node is not of type `EXPRESSION_TYPE_BOUND_COLUMN_REF`. The index counts the columns of the operator the - * predicate is evaluated against, which is not necessarily the full set of columns that operator can produce: a - * callback resolves it through whatever handed it the expression, e.g. - * `duckdb_v2_table_function_filter_pushdown_get_column_index()` for a filter offered to a table function. + * Must be GLOBAL or SESSION, and a scope the option supports. Defaults to SESSION, or to GLOBAL when the option does + * not support SESSION. * * history: * - stable: v2.0.0 * - * @param expression The column reference node. - * @param index Receives the column index. + * @param option The option to set the default scope of. + * @param scope The default scope, GLOBAL or SESSION. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_expression_column_ref_get_index(duckdb_v2_expression_handle expression, - idx_t *index, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_option_set_default_scope(duckdb_v2_custom_option_handle option, + DUCKDB_V2_SETTING_SCOPE scope, + duckdb_v2_error_info_handle *err); /*! - * Returns the name of the scalar function a function node calls. + * Sets whether the custom option can be written at a scope, as `duckdb_v2_option_supports_scope()` reports it. * - * Applies to `EXPRESSION_TYPE_BOUND_FUNCTION` and to the node types that are function calls underneath: the - * comparisons, `EXPRESSION_TYPE_COMPARE_BETWEEN` and `EXPRESSION_TYPE_OPERATOR_CAST`. Fails for any other node type. - * For a comparison the name is its operator, e.g. `<`; for `BETWEEN` and casts it is an internal name, so dispatch on - * the type rather than the name for those. The name is borrowed and shares the lifetime of the node. + * A custom option supports both GLOBAL and SESSION unless told otherwise. A write at a scope it does not support fails, + * from SQL as from `duckdb_v2_config_set_option_value()`. Registration fails when the option supports neither scope. * * history: * - stable: v2.0.0 * - * @param expression The function node. - * @param name Receives a borrowed view of the function name. + * @param option The option to configure. + * @param scope The scope, GLOBAL or SESSION. + * @param supported Whether the option can be written at the scope. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_expression_function_get_name(duckdb_v2_expression_handle expression, - duckdb_v2_identifier_t *name, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_option_set_supports_scope(duckdb_v2_custom_option_handle option, + DUCKDB_V2_SETTING_SCOPE scope, bool supported, + duckdb_v2_error_info_handle *err); /*! - * Returns the qualified name of the scalar function a function node calls. + * Sets arbitrary user data on the custom option. * - * Applies to the same node types as `duckdb_v2_expression_function_get_name()`, and fails for any other. The name is - * qualified with the catalog and schema the function was resolved in, where known. The returned name is owned by the - * caller and must be destroyed via `duckdb_v2_qname_destroy()`. + * Associates an opaque pointer with the option, retrievable from the update callback via + * `duckdb_v2_custom_option_update_get_user_data()`. The opaque handle bundles the pointer with an optional destructor, + * invoked when the data is no longer needed: once the custom option handle is destroyed and, if it was registered, the + * instance is too. * * history: * - stable: v2.0.0 * - * @param expression The function node. - * @param name Receives the owned qualified name. + * @param option The option to set the user data of. + * @param data Opaque handle bundling the user data pointer plus an optional destructor. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_expression_function_get_qname(duckdb_v2_expression_handle expression, - duckdb_v2_qname_handle *name, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_option_set_user_data(duckdb_v2_custom_option_handle option, + duckdb_v2_opaque *data, + duckdb_v2_error_info_handle *err); /*! - * Returns whether a cast node is a regular `CAST` or a `TRY_CAST`. + * Sets the optional update callback of the custom option. * - * Fails if the node is not of type `EXPRESSION_TYPE_OPERATOR_CAST`. A `TRY_CAST` yields NULL for a value it cannot - * convert where a regular `CAST` would fail the query. + * The callback is invoked whenever the option is set or reset, from SQL as from `duckdb_v2_config_set_option_value()`, + * after the new value is cast to the option's type and before it is stored. It may replace the value with + * `duckdb_v2_custom_option_update_set_value()`, or refuse it by reporting an error. It is not invoked for the default + * value at registration, nor for a value given to the instance at startup. The callback must not write options itself. * * history: * - stable: v2.0.0 * - * @param expression The cast node. - * @param mode Receives the cast mode. + * @param option The option to set the update callback of. + * @param callback The update callback to set. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_expression_cast_get_mode(duckdb_v2_expression_handle expression, - DUCKDB_V2_CAST_MODE *mode, - duckdb_v2_error_info_handle *err); - -/* --- Struct definitions for expression --- */ - -/* ============================================================================ - * MODULE: logical_type - * ============================================================================ */ - -/* --- Enums for logical_type --- */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_option_set_update_callback( + duckdb_v2_custom_option_handle option, duckdb_v2_custom_option_update_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Logical type identifier. The values are the same integers DuckDB uses internally, so round-tripping is lossless. The - * bind- and UDF-only ids (UNKNOWN, ANY, TEMPLATE) appear here for completeness; they do not show up in result column - * types in practice. + * Retrieves the user data set via `duckdb_v2_custom_option_set_user_data()`. + * + * history: + * - stable: v2.0.0 + * + * @param info The update info handle. + * @param data Receives the user data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR */ -typedef enum DUCKDB_V2_LOGICAL_TYPE_ID { - //! Invalid / unset. - DUCKDB_V2_LOGICAL_TYPE_ID_INVALID = 0, - - //! NULL constant type. - DUCKDB_V2_LOGICAL_TYPE_ID_SQLNULL = 1, - - //! Unknown — used for unresolved parameter expressions. - DUCKDB_V2_LOGICAL_TYPE_ID_UNKNOWN = 2, - - //! ANY — used for functions that accept any type. - DUCKDB_V2_LOGICAL_TYPE_ID_ANY = 3, - - /*! - * A type carried as a value (type parameters). Values of this type are built via value_create_type_with_context / - * _with_connection. - */ - DUCKDB_V2_LOGICAL_TYPE_ID_TYPE = 6, - DUCKDB_V2_LOGICAL_TYPE_ID_BOOLEAN = 10, - DUCKDB_V2_LOGICAL_TYPE_ID_TINYINT = 11, - DUCKDB_V2_LOGICAL_TYPE_ID_SMALLINT = 12, - DUCKDB_V2_LOGICAL_TYPE_ID_INTEGER = 13, - DUCKDB_V2_LOGICAL_TYPE_ID_BIGINT = 14, - - //! 32-bit days since epoch. - DUCKDB_V2_LOGICAL_TYPE_ID_DATE = 15, - - //! 64-bit microseconds since midnight. - DUCKDB_V2_LOGICAL_TYPE_ID_TIME = 16, - - //! 64-bit seconds since epoch. - DUCKDB_V2_LOGICAL_TYPE_ID_TIMESTAMP_SEC = 17, - - //! 64-bit milliseconds since epoch. - DUCKDB_V2_LOGICAL_TYPE_ID_TIMESTAMP_MS = 18, - - //! 64-bit microseconds since epoch. - DUCKDB_V2_LOGICAL_TYPE_ID_TIMESTAMP = 19, - - //! 64-bit nanoseconds since epoch. - DUCKDB_V2_LOGICAL_TYPE_ID_TIMESTAMP_NS = 20, - - //! Decimal with width and scale parameters. - DUCKDB_V2_LOGICAL_TYPE_ID_DECIMAL = 21, - DUCKDB_V2_LOGICAL_TYPE_ID_FLOAT = 22, - DUCKDB_V2_LOGICAL_TYPE_ID_DOUBLE = 23, - DUCKDB_V2_LOGICAL_TYPE_ID_VARCHAR = 25, - DUCKDB_V2_LOGICAL_TYPE_ID_BLOB = 26, - DUCKDB_V2_LOGICAL_TYPE_ID_INTERVAL = 27, - DUCKDB_V2_LOGICAL_TYPE_ID_UTINYINT = 28, - DUCKDB_V2_LOGICAL_TYPE_ID_USMALLINT = 29, - DUCKDB_V2_LOGICAL_TYPE_ID_UINTEGER = 30, - DUCKDB_V2_LOGICAL_TYPE_ID_UBIGINT = 31, - - //! 64-bit microseconds since epoch, timezone-aware. - DUCKDB_V2_LOGICAL_TYPE_ID_TIMESTAMP_TZ = 32, - - //! 64-bit nanoseconds since epoch, timezone-aware. - DUCKDB_V2_LOGICAL_TYPE_ID_TIMESTAMP_TZ_NS = 33, - - //! 64-bit microseconds since midnight + 32-bit offset. - DUCKDB_V2_LOGICAL_TYPE_ID_TIME_TZ = 34, - - //! 64-bit nanoseconds since midnight. - DUCKDB_V2_LOGICAL_TYPE_ID_TIME_NS = 35, - DUCKDB_V2_LOGICAL_TYPE_ID_BIT = 36, - - //! Arbitrary-precision integer (VARINT-encoded). - DUCKDB_V2_LOGICAL_TYPE_ID_BIGNUM = 39, - DUCKDB_V2_LOGICAL_TYPE_ID_UHUGEINT = 49, - DUCKDB_V2_LOGICAL_TYPE_ID_HUGEINT = 50, - DUCKDB_V2_LOGICAL_TYPE_ID_UUID = 54, - - //! Geometry (spatial extension). - DUCKDB_V2_LOGICAL_TYPE_ID_GEOMETRY = 60, - DUCKDB_V2_LOGICAL_TYPE_ID_STRUCT = 100, - DUCKDB_V2_LOGICAL_TYPE_ID_LIST = 101, - DUCKDB_V2_LOGICAL_TYPE_ID_MAP = 102, - DUCKDB_V2_LOGICAL_TYPE_ID_ENUM = 104, - DUCKDB_V2_LOGICAL_TYPE_ID_UNION = 107, - DUCKDB_V2_LOGICAL_TYPE_ID_ARRAY = 108, - DUCKDB_V2_LOGICAL_TYPE_ID_VARIANT = 109, - - //! Unnamed struct; shares the physical representation of STRUCT. - DUCKDB_V2_LOGICAL_TYPE_ID_TUPLE = 110, - DUCKDB_V2_LOGICAL_TYPE_ID_MAX_ENUM = 0x7FFFFFFF, -} DUCKDB_V2_LOGICAL_TYPE_ID; - -/* --- Struct forward declarations for logical_type --- */ - -/* --- Types for logical_type --- */ - -/* --- Constants for logical_type --- */ - -/* --- Function pointer typedefs for logical_type --- */ - -/* --- Functions for logical_type --- */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_option_update_get_user_data( + duckdb_v2_custom_option_update_info_handle info, void **data, duckdb_v2_error_info_handle *err); /*! - * Creates a logical type from a type id plus value parameters. - * - * The id-keyed twin of context_create_type_from_name: the type id names the kind, and the parameters bind it. With - * param_count 0 it instantiates a primitive directly, without touching the catalog: BOOLEAN, TINYINT..BIGINT, - * UTINYINT..UBIGINT, HUGEINT, UHUGEINT, FLOAT, DOUBLE, DATE, every TIME and TIMESTAMP variant, INTERVAL, VARCHAR, BLOB, - * BIT, BIGNUM, and UUID. ANY is accepted as well. - * - * ANY is a function-signature wildcard, constructible here so it can be passed to the function parameter and varargs - * setters, as a fixed-arity ANY parameter or an ANY varargs type. Data-creating surfaces reject it: value and data - * chunk creation, scalar and aggregate return types, table function result columns, cast source and target types, and - * custom type registration. - * - * With parameters, the id resolves to its canonical type name and binds through the same path as - * context_create_type_from_name, so the parameterized kinds construct here too: decimal(width, scale); list(T); - * array(T, size); map(K, V); struct(fields); union(members); enum(entries); and varchar with a named "collation" - * parameter. Parameters are (name, value) pairs in two parallel arrays, exactly as for context_create_type_from_name. - * - * Returns ERROR_INPUT_INVALID when param_count is 0 and the id needs parameters (DECIMAL, LIST, STRUCT, TUPLE, MAP, - * ARRAY, UNION, ENUM, VARIANT, GEOMETRY), for the bind-time-only ids (SQLNULL, UNKNOWN), for TYPE — construct that via - * context_create_type_from_text — and for INVALID. + * Retrieves the value the option is updated to, already cast to the option's type. On a reset this is the default + * value, which may be NULL. The returned value is owned by the caller and must be destroyed via + * `duckdb_v2_value_destroy()`. * * history: * - stable: v2.0.0 * - * @param ctx The context supplying the catalog and active transaction. - * @param type_id The type id to instantiate. - * @param param_names Optional. An array of param_count parameter names; a {NULL, 0} entry is positional. Pass NULL for - * all-positional parameters. - * @param param_values An array of param_count parameter values. Borrowed (copied in). Pass NULL when param_count is 0. - * @param param_count The number of parameters. 0 instantiates a parameterless primitive. - * @param out_type Receives the new logical type handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The update info handle. + * @param value Receives the new value. Owned by the caller; destroy via `duckdb_v2_value_destroy()`. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_context_create_type_from_id( - duckdb_v2_context_handle ctx, DUCKDB_V2_LOGICAL_TYPE_ID type_id, const duckdb_v2_identifier_t *param_names, - const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_option_update_get_value(duckdb_v2_custom_option_update_info_handle info, + duckdb_v2_value_handle *value, + duckdb_v2_error_info_handle *err); /*! - * Creates a logical type from a type name plus value parameters. - * - * The generic constructor: resolves the name in the context's catalog and binds it with the given parameters, exactly - * as SQL binds a type expression. Built-in parameterized kinds and registered extension types construct through this - * same call. - * - * An unqualified name is resolved along the search path first and then in the system catalog, which is where the - * built-in kinds live. A qualified name is resolved exactly as written, with no system-catalog fallback, so it names - * the type in that catalog or schema or fails. - * - * Parameters are (name, value) pairs in two parallel arrays. param_names may be NULL to make every parameter - * positional, and a {NULL, 0} entry makes that one parameter positional. Child types cross as TYPE values, built with - * value_create_type_with_context / _with_connection. The built-in shapes are: decimal(width, scale); list(T); array(T, - * size); map(K, V); struct(fields, as named or all-positional TYPE values); union(members, as named TYPE values); - * enum(entries, as VARCHAR values); and varchar with a named "collation" VARCHAR parameter. - * - * A name that resolves to a type with no bind function takes no parameters, and passing any fails. Bind errors — - * unknown name, wrong parameter count or types — surface from the call. - * - * Runs in the caller's context scope, as create_type_from_text does: reach it from a bind-phase callback or another - * context-holding scope, not from an exec-phase worker callback. External callers holding only a connection use - * connection_create_type_from_name instead. - * - * The returned logical type is caller-owned and must be destroyed via logical_type_destroy. A type resolved from the - * catalog shares database-owned storage, such as an ENUM dictionary, so destroy it before closing the database. This is - * the inverse of logical_type_get_param_count / logical_type_get_param. + * Replaces the value the option is updated to, e.g. to normalize it. The value is borrowed and copied, and cast to the + * option's type, which fails when the cast does. * * history: * - stable: v2.0.0 * - * @param ctx The context supplying the catalog and active transaction. - * @param name The type name to resolve. Parts are matched case-insensitively; qualify it to name a type in a particular - * catalog or schema. - * @param param_names Optional. An array of param_count parameter names; a {NULL, 0} entry is positional. Pass NULL for - * all-positional parameters. - * @param param_values An array of param_count parameter values. Borrowed (copied in). Pass NULL when param_count is 0. - * @param param_count The number of parameters. - * @param out_type Receives the new logical type handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The update info handle. + * @param value The value to store instead. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_context_create_type_from_name( - duckdb_v2_context_handle ctx, duckdb_v2_qname_handle name, const duckdb_v2_identifier_t *param_names, - const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_option_update_set_value(duckdb_v2_custom_option_update_info_handle info, + duckdb_v2_value_handle value, + duckdb_v2_error_info_handle *err); /*! - * Creates a logical type by parsing SQL text. - * - * Parses a SQL type expression in the given context and returns the bound logical type. It accepts primitives - * ("INTEGER"), parameterized kinds ("DECIMAL(18,3)", "INTEGER[]", "STRUCT(a INTEGER, b VARCHAR)", "MAP(VARCHAR, - * INTEGER)", "INTEGER[3]", "UNION(i INTEGER, s VARCHAR)", "ENUM('a', 'b')"), and catalog-registered type names, both - * user-defined and from extensions. A catalog type name binds to its structural type, and the name is not preserved as - * an alias. Names are case-insensitive. Parse and bind errors surface from the call. - * - * Runs in the caller's context scope: a context handle arrives with the context lock held and a transaction active, as - * in a function bind callback or custom type registration. Catalog-touching context calls belong in bind-phase - * callbacks and other context-holding scopes, not in exec-phase worker callbacks. External callers holding only a - * connection use connection_create_type_from_text instead. - * - * The returned logical type is caller-owned and must be destroyed via logical_type_destroy. A type resolved from the - * catalog shares database-owned storage, such as an ENUM dictionary, so destroy it before closing the database. This is - * the inverse of logical_type_to_text for every constructible kind. + * Retrieves the scope the value is written at: GLOBAL or SESSION, a DEFAULT write already resolved to the option's + * default scope. * * history: * - stable: v2.0.0 * - * @param ctx The context supplying the catalog and active transaction. - * @param text View of the SQL type expression to parse. - * @param out_type Receives the new logical type handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The update info handle. + * @param scope Receives the scope. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_context_create_type_from_text(duckdb_v2_context_handle ctx, duckdb_v2_str text, - duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_option_update_get_scope(duckdb_v2_custom_option_update_info_handle info, + DUCKDB_V2_SETTING_SCOPE *scope, + duckdb_v2_error_info_handle *err); /*! - * Creates a logical type from a type id plus value parameters. - * - * The id-keyed twin of connection_create_type_from_name: the type id names the kind, and the parameters bind it. With - * param_count 0 it instantiates a primitive directly, without touching the catalog: BOOLEAN, TINYINT..BIGINT, - * UTINYINT..UBIGINT, HUGEINT, UHUGEINT, FLOAT, DOUBLE, DATE, every TIME and TIMESTAMP variant, INTERVAL, VARCHAR, BLOB, - * BIT, BIGNUM, and UUID. ANY is accepted as well. - * - * ANY is a function-signature wildcard, constructible here so it can be passed to the function parameter and varargs - * setters, as a fixed-arity ANY parameter or an ANY varargs type. Data-creating surfaces reject it: value and data - * chunk creation, scalar and aggregate return types, table function result columns, cast source and target types, and - * custom type registration. - * - * With parameters, the id resolves to its canonical type name and binds through the same path as - * connection_create_type_from_name, so the parameterized kinds construct here too: decimal(width, scale); list(T); - * array(T, size); map(K, V); struct(fields); union(members); enum(entries); and varchar with a named "collation" - * parameter. Parameters are (name, value) pairs in two parallel arrays, exactly as for - * connection_create_type_from_name. - * - * Returns ERROR_INPUT_INVALID when param_count is 0 and the id needs parameters (DECIMAL, LIST, STRUCT, TUPLE, MAP, - * ARRAY, UNION, ENUM, VARIANT, GEOMETRY), for the bind-time-only ids (SQLNULL, UNKNOWN), for TYPE — construct that via - * connection_create_type_from_text — and for INVALID. + * Retrieves whether the update is a reset to the default value, like SQL `RESET`. * * history: * - stable: v2.0.0 * - * @param conn The connection supplying the catalog and active transaction. - * @param type_id The type id to instantiate. - * @param param_names Optional. An array of param_count parameter names; a {NULL, 0} entry is positional. Pass NULL for - * all-positional parameters. - * @param param_values An array of param_count parameter values. Borrowed (copied in). Pass NULL when param_count is 0. - * @param param_count The number of parameters. 0 instantiates a parameterless primitive. - * @param out_type Receives the new logical type handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The update info handle. + * @param is_reset Receives whether the update is a reset. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_create_type_from_id( - duckdb_v2_connection_handle conn, DUCKDB_V2_LOGICAL_TYPE_ID type_id, const duckdb_v2_identifier_t *param_names, - const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_option_update_is_reset(duckdb_v2_custom_option_update_info_handle info, + bool *is_reset, duckdb_v2_error_info_handle *err); /*! - * Creates a logical type from a type name plus value parameters, using a connection. + * Registers the custom option on the connection's instance, visible to every connection. * - * The same as context_create_type_from_name, except that the catalog and transaction come from a connection: the bind - * runs in its own transaction on that connection's context. Use it from outside DuckDB, where a connection — but no - * context — is in hand. - * - * Parameters are (name, value) pairs in two parallel arrays, exactly as for context_create_type_from_name. - * - * The returned logical type is caller-owned and must be destroyed via logical_type_destroy. A type resolved from the - * catalog shares database-owned storage, such as an ENUM dictionary, so destroy it before closing the database. + * Registration is not transactional: it takes effect at once, also inside an explicit transaction, and a rollback does + * not undo it. Registration requires a name and a type, or a default value to take the type from, and fails when an + * option of the same name already exists. A value given for the option at startup is picked up. The custom option must + * have been created through a factory of the same instance. The caller still owns the handle after registration and + * must destroy it with `duckdb_v2_custom_option_destroy()`, which does not affect the registered option. * * history: * - stable: v2.0.0 * - * @param conn The connection supplying the catalog and active transaction. - * @param name The type name to resolve. Parts are matched case-insensitively; qualify it to name a type in a particular - * catalog or schema. - * @param param_names Optional. An array of param_count parameter names; a {NULL, 0} entry is positional. Pass NULL for - * all-positional parameters. - * @param param_values An array of param_count parameter values. Borrowed (copied in). Pass NULL when param_count is 0. - * @param param_count The number of parameters. - * @param out_type Receives the new logical type handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param conn The connection to register on. + * @param option The custom option to register. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_create_type_from_name( - duckdb_v2_connection_handle conn, duckdb_v2_qname_handle name, const duckdb_v2_identifier_t *param_names, - const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_register_custom_option(duckdb_v2_connection_handle conn, + duckdb_v2_custom_option_handle option, + duckdb_v2_error_info_handle *err); /*! - * Creates a logical type by parsing SQL text, using a connection. + * Registers the custom option on the loading extension's instance, visible to every connection. * - * The same as context_create_type_from_text, except that the catalog and transaction come from a connection: the parse - * and bind run in their own transaction on that connection's context. Use it from outside DuckDB, where a connection — - * but no context — is in hand. - * - * The returned logical type is caller-owned and must be destroyed via logical_type_destroy. A type resolved from the - * catalog shares database-owned storage, such as an ENUM dictionary, so destroy it before closing the database. + * Use this from the extension's load callback. Registration is not transactional, requires a name and a type, or a + * default value to take the type from, and fails when an option of the same name already exists. A value given for the + * option at startup is picked up. The custom option must have been created through a factory of the same instance. The + * caller still owns the handle after registration and must destroy it with `duckdb_v2_custom_option_destroy()`, which + * does not affect the registered option. * * history: * - stable: v2.0.0 * - * @param conn The connection supplying the catalog and active transaction. - * @param text View of the SQL type expression to parse. - * @param out_type Receives the new logical type handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param extension The loading extension. + * @param option The custom option to register. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_create_type_from_text(duckdb_v2_connection_handle conn, - duckdb_v2_str text, - duckdb_v2_logical_type_handle *out_type, +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_extension_register_custom_option(duckdb_v2_extension_handle extension, + duckdb_v2_custom_option_handle option, duckdb_v2_error_info_handle *err); /*! - * Creates a copy of a logical type. + * Destroys the custom option, releasing its resources. * - * On success, writes the new caller-owned handle into *out_type; destroy it via logical_type_destroy. + * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent + * double-destruction. Destroying the handle after registration does not affect the registered option. * * history: * - stable: v2.0.0 * - * @param type The logical type to copy. - * @param out_type Receives the new logical type handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param option The option to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_logical_type_copy(duckdb_v2_logical_type_handle type, - duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_custom_option_destroy(duckdb_v2_custom_option_handle *option); + +/* --- Struct definitions for custom_option --- */ + +/* ============================================================================ + * MODULE: expression + * ============================================================================ */ + +/* --- Enums for expression --- */ /*! - * Destroys a logical type handle. - * - * Null-safe: passing nullptr or a slot already set to nullptr is a no-op. On success the slot is set to nullptr. - * - * history: - * - stable: v2.0.0 + * The type of a bound expression node. The values mirror the engine's own expression types, restricted to the node + * types a filter predicate can contain; every other node type is reported as `EXPRESSION_TYPE_INVALID`. * - * @param type The logical type to destroy. - * @return DUCKDB_V2_ERROR + * Comparisons, `BETWEEN` and casts are regular scalar function calls once bound: they have the function's children and + * name, and only their type tells them apart from any other function. A `CAST` therefore has one child, the value being + * cast, and its target type is the node's return type. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_logical_type_destroy(duckdb_v2_logical_type_handle *type); +typedef enum DUCKDB_V2_EXPRESSION_TYPE { + //! A node type this API does not model. Its children can still be walked. + DUCKDB_V2_EXPRESSION_TYPE_INVALID = 0, -/*! - * Compares two logical types for deep equality. - * - * Two types are equal when they agree in kind and in every parameter, recursively. DECIMAL(10, 2) equals DECIMAL(10, - * 2), but not DECIMAL(10, 3) and not FLOAT; two STRUCTs are equal when they have the same field names in the same order - * and equal field types. + /*! + * A cast. One child; the target type is the node's return type and `duckdb_v2_expression_cast_get_mode()` tells a + * `TRY_CAST` apart. + */ + DUCKDB_V2_EXPRESSION_TYPE_OPERATOR_CAST = 12, + + //! Logical `NOT`. One child. + DUCKDB_V2_EXPRESSION_TYPE_OPERATOR_NOT = 13, + + //! `IS NULL`. One child. + DUCKDB_V2_EXPRESSION_TYPE_OPERATOR_IS_NULL = 14, + + //! `IS NOT NULL`. One child. + DUCKDB_V2_EXPRESSION_TYPE_OPERATOR_IS_NOT_NULL = 15, + + //! `=`. Two children. + DUCKDB_V2_EXPRESSION_TYPE_COMPARE_EQUAL = 25, + + //! `<>`. Two children. + DUCKDB_V2_EXPRESSION_TYPE_COMPARE_NOTEQUAL = 26, + + //! `<`. Two children. + DUCKDB_V2_EXPRESSION_TYPE_COMPARE_LESSTHAN = 27, + + //! `>`. Two children. + DUCKDB_V2_EXPRESSION_TYPE_COMPARE_GREATERTHAN = 28, + + //! `<=`. Two children. + DUCKDB_V2_EXPRESSION_TYPE_COMPARE_LESSTHANOREQUALTO = 29, + + //! `>=`. Two children. + DUCKDB_V2_EXPRESSION_TYPE_COMPARE_GREATERTHANOREQUALTO = 30, + + //! `IN`. The first child is the value tested, the remaining children are the candidates. + DUCKDB_V2_EXPRESSION_TYPE_COMPARE_IN = 35, + + //! `NOT IN`. The first child is the value tested, the remaining children are the candidates. + DUCKDB_V2_EXPRESSION_TYPE_COMPARE_NOT_IN = 36, + + //! `IS DISTINCT FROM`. Two children. + DUCKDB_V2_EXPRESSION_TYPE_COMPARE_DISTINCT_FROM = 37, + + //! `BETWEEN`. Three children: the value tested, the lower bound and the upper bound. + DUCKDB_V2_EXPRESSION_TYPE_COMPARE_BETWEEN = 38, + + //! `IS NOT DISTINCT FROM`. Two children. + DUCKDB_V2_EXPRESSION_TYPE_COMPARE_NOT_DISTINCT_FROM = 40, + + //! Logical `AND`. Two or more children. + DUCKDB_V2_EXPRESSION_TYPE_CONJUNCTION_AND = 50, + + //! Logical `OR`. Two or more children. + DUCKDB_V2_EXPRESSION_TYPE_CONJUNCTION_OR = 51, + + //! A constant. No children; read it with `duckdb_v2_expression_constant_get_value()`. + DUCKDB_V2_EXPRESSION_TYPE_VALUE_CONSTANT = 75, + + //! A prepared statement parameter whose value is not known yet. No children. + DUCKDB_V2_EXPRESSION_TYPE_VALUE_PARAMETER = 76, + + /*! + * A call to a scalar function other than the ones listed above. The children are its arguments; read the name with + * `duckdb_v2_expression_function_get_name()`. + */ + DUCKDB_V2_EXPRESSION_TYPE_BOUND_FUNCTION = 141, + + //! A `CASE` expression. The children are each `WHEN` condition followed by its `THEN` result, then the `ELSE` + //! result. + DUCKDB_V2_EXPRESSION_TYPE_CASE_EXPR = 150, + + //! `COALESCE`. One or more children. + DUCKDB_V2_EXPRESSION_TYPE_OPERATOR_COALESCE = 152, + + //! A reference to a column. No children; read it with `duckdb_v2_expression_column_ref_get_index()`. + DUCKDB_V2_EXPRESSION_TYPE_BOUND_COLUMN_REF = 228, + DUCKDB_V2_EXPRESSION_TYPE_MAX_ENUM = 0x7FFFFFFF, +} DUCKDB_V2_EXPRESSION_TYPE; + +/* --- Struct forward declarations for expression --- */ + +/* --- Types for expression --- */ + +/*! + * A borrowed opaque handle to a node of a bound expression tree. Read-only and owned by the engine: valid only for the + * duration of the callback that handed it out, and never destroyed by the caller. Child nodes borrowed via + * `duckdb_v2_expression_get_child()` share their parent's lifetime. + */ +typedef struct _duckdb_v2_expression { + void *internal_ptr; +} * duckdb_v2_expression_handle; + +/* --- Constants for expression --- */ + +/* --- Function pointer typedefs for expression --- */ + +/* --- Functions for expression --- */ + +/*! + * Returns the type of an expression node. + * + * The type decides which of the type-specific accessors apply, and what the node's children mean; see + * `EXPRESSION_TYPE`. A node of a type this API does not model reports `EXPRESSION_TYPE_INVALID`. * * history: * - stable: v2.0.0 * - * @param left The first logical type. - * @param right The second logical type. - * @param result Receives the result of the comparison. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param expression The expression node. + * @param type Receives the node type. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_logical_type_is_equal(duckdb_v2_logical_type_handle left, - duckdb_v2_logical_type_handle right, bool *result, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_expression_get_type(duckdb_v2_expression_handle expression, + DUCKDB_V2_EXPRESSION_TYPE *type, + duckdb_v2_error_info_handle *err); /*! - * Returns the logical type id. + * Returns the logical type the expression node evaluates to. + * + * For a cast this is the target type. The returned type is owned by the caller and must be destroyed via + * `duckdb_v2_logical_type_destroy()`. * * history: * - stable: v2.0.0 * - * @param type The logical type. - * @param out_id Receives the type id. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param expression The expression node. + * @param type Receives the owned logical type. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_logical_type_get_id(duckdb_v2_logical_type_handle type, - DUCKDB_V2_LOGICAL_TYPE_ID *out_id, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_expression_get_return_type(duckdb_v2_expression_handle expression, + duckdb_v2_logical_type_handle *type, + duckdb_v2_error_info_handle *err); /*! - * Borrows the logical type's name. + * Returns the number of child nodes of an expression node. * - * The alias when one is set — an extension or user-defined name such as "POINT_2D" — otherwise the canonical name of - * the type id, such as "INTEGER", "DECIMAL", or "TIMESTAMP WITH TIME ZONE". Never the empty view. This is exactly the - * name vocabulary create_type_from_name accepts. The view is valid until the logical type is destroyed; a canonical - * name points at static storage. + * Works for every node type, including `EXPRESSION_TYPE_INVALID`. Constants, parameters and column references have no + * children. * * history: * - stable: v2.0.0 * - * @param type The logical type. - * @param out_name Receives a borrowed view of the name (alias when set, else the id's canonical name). - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param expression The expression node. + * @param count Receives the number of children. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_logical_type_get_name(duckdb_v2_logical_type_handle type, - duckdb_v2_identifier_t *out_name, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_expression_get_child_count(duckdb_v2_expression_handle expression, idx_t *count, + duckdb_v2_error_info_handle *err); /*! - * Renders a logical type as SQL text. - * - * An aliased type renders as its alias, and create_type_from_text resolves that spelling only when the name is - * registered in the connection's catalog. The text round-trips through create_type_from_text for every constructible - * kind, with one exception: ANY renders as "ANY", but create_type_from_text cannot parse it back, since ANY is a - * signature wildcard rather than a parseable SQL type. - * - * Writes into a caller-supplied buffer, so nothing is allocated on the caller's behalf and nothing has to be freed. - * Pass out_text = NULL to size the buffer without rendering into it: out_length then receives the length, and - * out_capacity is ignored. With out_text != NULL, out_capacity must be at least out_length + 1, or the call returns - * ERROR_INPUT_OBJECT_SIZE with out_length set to the required length and out_text left untouched. + * Retrieves a child node of an expression node by index. * - * out_length never counts the terminator, but a successful write always appends one, so the buffer is usable as a C - * string. + * Fails if the index is out of bounds. Children are ordered as the node type describes (see `EXPRESSION_TYPE`). The + * child is borrowed and shares the lifetime of its parent. * * history: * - stable: v2.0.0 * - * @param type The logical type. - * @param out_text Caller-owned buffer receiving the text plus a null terminator, or NULL to only report the required - * length in out_length. - * @param out_capacity Bytes available in out_text, terminator included. Ignored when out_text is NULL. - * @param out_length Receives the text length excluding the null terminator — written on success and on - * ERROR_INPUT_OBJECT_SIZE. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param expression The expression node. + * @param index The index of the child to retrieve. + * @param child Receives the borrowed child node. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_logical_type_to_text(duckdb_v2_logical_type_handle type, char *out_text, - idx_t out_capacity, idx_t *out_length, +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_expression_get_child(duckdb_v2_expression_handle expression, idx_t index, + duckdb_v2_expression_handle *child, duckdb_v2_error_info_handle *err); /*! - * Returns the number of value parameters of a logical type. + * Returns the value of a constant node. * - * The inspection dual of create_type_from_name: these are the parameters that reconstruct the type through it. Per - * kind: DECIMAL 2 (width, scale); LIST 1 (element type); ARRAY 2 (element type, size); MAP 2 (key type, value type); - * STRUCT and TUPLE one per field; UNION one per member; ENUM one per dictionary entry; VARCHAR 1 when a collation is - * set, else 0; GEOMETRY 1 when a coordinate system is set, else 0; everything else 0. A bound type reports only what it - * actually carries, so a bind-time modifier that is not retained — an ignored VARCHAR length, say — does not reappear. + * Fails if the node is not of type `EXPRESSION_TYPE_VALUE_CONSTANT`. The returned value is owned by the caller and must + * be destroyed via `duckdb_v2_value_destroy()`. * * history: * - stable: v2.0.0 * - * @param type The logical type. - * @param out_count Receives the number of parameters. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param expression The constant node. + * @param value Receives the owned constant value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_logical_type_get_param_count(duckdb_v2_logical_type_handle type, - idx_t *out_count, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_expression_constant_get_value(duckdb_v2_expression_handle expression, + duckdb_v2_value_handle *value, + duckdb_v2_error_info_handle *err); /*! - * Returns one value parameter of a logical type. + * Returns the column a column reference node points at. * - * out_name receives a borrowed view of the parameter name — a STRUCT field name, a UNION member name, "collation" — or - * the empty view {NULL, 0} for a positional parameter. A non-empty view is valid until the logical type is destroyed. - * out_value receives an owned value, destroyed via value_destroy: child types come back as TYPE values (unwrap them - * with value_get_type), DECIMAL width and scale as UTINYINT, ARRAY size as BIGINT, and ENUM dictionary entries and - * collations as VARCHAR. An out-of-range index returns ERROR_INPUT_INVALID. Each call allocates one owned value. + * Fails if the node is not of type `EXPRESSION_TYPE_BOUND_COLUMN_REF`. The index counts the columns of the operator the + * predicate is evaluated against, which is not necessarily the full set of columns that operator can produce: a + * callback resolves it through whatever handed it the expression, e.g. + * `duckdb_v2_table_function_filter_pushdown_get_column_index()` for a filter offered to a table function. * * history: * - stable: v2.0.0 * - * @param type The logical type. - * @param index The parameter index, in [0, param_count). - * @param out_name Receives a borrowed view of the parameter name, or the empty view {NULL, 0} for a positional - * parameter. - * @param out_value Receives the owned parameter value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param expression The column reference node. + * @param index Receives the column index. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_logical_type_get_param(duckdb_v2_logical_type_handle type, idx_t index, - duckdb_v2_identifier_t *out_name, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_expression_column_ref_get_index(duckdb_v2_expression_handle expression, + idx_t *index, duckdb_v2_error_info_handle *err); /*! - * Creates a logical type that is an alias of another logical type. - * - * The alias keeps the base type's internal representation, so executing against it needs no special handling, while - * remaining logically distinct from the base type. Intended for custom type bind callbacks, where both the base type - * and the name come from the bind info. + * Returns the name of the scalar function a function node calls. * - * Scoped like the rest of the create_type family: the alias is resolved against the catalog reachable from the context. - * An empty alias name returns ERROR_INPUT_INVALID. + * Applies to `EXPRESSION_TYPE_BOUND_FUNCTION` and to the node types that are function calls underneath: the + * comparisons, `EXPRESSION_TYPE_COMPARE_BETWEEN` and `EXPRESSION_TYPE_OPERATOR_CAST`. Fails for any other node type. + * For a comparison the name is its operator, e.g. `<`; for `BETWEEN` and casts it is an internal name, so dispatch on + * the type rather than the name for those. The name is borrowed and shares the lifetime of the node. * * history: * - stable: v2.0.0 * - * @param ctx The context to resolve the alias against. - * @param base_type The logical type to alias. Typically the base type supplied in the custom type bind info. - * @param alias_name The name for the resulting type. Typically the name of the custom type being constructed, also - * available from the bind info. - * @param out_type Receives the new type: the base type's internal representation under the given alias name. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param expression The function node. + * @param name Receives a borrowed view of the function name. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_context_create_type_with_alias(duckdb_v2_context_handle ctx, - duckdb_v2_logical_type_handle base_type, - duckdb_v2_identifier_t alias_name, - duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); - +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_expression_function_get_name(duckdb_v2_expression_handle expression, + duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err); + /*! - * Creates a logical type that is an alias of another logical type. + * Returns the qualified name of the scalar function a function node calls. * - * The alias keeps the base type's internal representation, so executing against it needs no special handling, while - * remaining logically distinct from the base type. Intended for custom type bind callbacks, where both the base type - * and the name come from the bind info. + * Applies to the same node types as `duckdb_v2_expression_function_get_name()`, and fails for any other. The name is + * qualified with the catalog and schema the function was resolved in, where known. The returned name is owned by the + * caller and must be destroyed via `duckdb_v2_qname_destroy()`. + * + * history: + * - stable: v2.0.0 + * + * @param expression The function node. + * @param name Receives the owned qualified name. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_expression_function_get_qname(duckdb_v2_expression_handle expression, + duckdb_v2_qname_handle *name, + duckdb_v2_error_info_handle *err); + +/*! + * Returns whether a cast node is a regular `CAST` or a `TRY_CAST`. * - * Scoped like the rest of the create_type family: the alias is resolved against the catalog reachable from the - * connection. An empty alias name returns ERROR_INPUT_INVALID. + * Fails if the node is not of type `EXPRESSION_TYPE_OPERATOR_CAST`. A `TRY_CAST` yields NULL for a value it cannot + * convert where a regular `CAST` would fail the query. * * history: * - stable: v2.0.0 * - * @param conn The connection to resolve the alias against. - * @param base_type The logical type to alias. Typically the base type supplied in the custom type bind info. - * @param alias_name The name for the resulting type. Typically the name of the custom type being constructed, also - * available from the bind info. - * @param out_type Receives the new type: the base type's internal representation under the given alias name. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param expression The cast node. + * @param mode Receives the cast mode. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_create_type_with_alias(duckdb_v2_connection_handle conn, - duckdb_v2_logical_type_handle base_type, - duckdb_v2_identifier_t alias_name, - duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_expression_cast_get_mode(duckdb_v2_expression_handle expression, + DUCKDB_V2_CAST_MODE *mode, + duckdb_v2_error_info_handle *err); -/* --- Struct definitions for logical_type --- */ +/* --- Struct definitions for expression --- */ /* ============================================================================ - * MODULE: replacement scan + * MODULE: log sink * ============================================================================ */ -/* --- Enums for replacement scan --- */ +/* --- Enums for log sink --- */ -/* --- Struct forward declarations for replacement scan --- */ +//! The scope a log entry was written in, which decides which of its ids are present. +typedef enum DUCKDB_V2_LOG_SCOPE { + //! Written for the database as a whole, carrying no connection, transaction or query id. + DUCKDB_V2_LOG_SCOPE_DATABASE = 10, -/* --- Types for replacement scan --- */ + //! Written by a connection, carrying its connection id and, inside one, the transaction and query ids. + DUCKDB_V2_LOG_SCOPE_CONNECTION = 20, + + //! Written by a thread executing a query, carrying the thread id next to the connection's ids. + DUCKDB_V2_LOG_SCOPE_THREAD = 30, + DUCKDB_V2_LOG_SCOPE_MAX_ENUM = 0x7FFFFFFF, +} DUCKDB_V2_LOG_SCOPE; + +/* --- Struct forward declarations for log sink --- */ + +/* --- Types for log sink --- */ /*! - * An owned opaque handle to a replacement scan being built. Created with - * `duckdb_v2_replacement_scan_create_with_connection()`, `duckdb_v2_replacement_scan_create_with_database()` or - * `duckdb_v2_replacement_scan_create_with_extension()`, configured with `duckdb_v2_replacement_scan_set_callback()` and - * `duckdb_v2_replacement_scan_set_user_data()`, made available with `duckdb_v2_replacement_scan_register()`, and - * destroyed with `duckdb_v2_replacement_scan_destroy()`. + * An owned opaque handle to a log sink being built. Created with `duckdb_v2_log_sink_create()`, configured with + * `duckdb_v2_log_sink_set_name()`, `duckdb_v2_log_sink_set_write_callback()` and the other setters, made available with + * `duckdb_v2_instance_register_log_sink()` or `duckdb_v2_extension_register_log_sink()`, and destroyed with + * `duckdb_v2_log_sink_destroy()`. */ -typedef struct _duckdb_v2_replacement_scan { +typedef struct _duckdb_v2_log_sink { void *internal_ptr; -} * duckdb_v2_replacement_scan_handle; +} * duckdb_v2_log_sink_handle; /*! - * A borrowed opaque handle to the arguments supplied to a replacement scan when the binder consults it. The callback - * receives this handle and can use it to inspect the unresolved name and to claim it by naming what to read instead. - * Valid only for the duration of the callback; the name it hands out is owned separately and outlives it. + * A borrowed opaque handle to the context of a log entry, received by a log sink's write callback next to the entry + * itself, and valid only for the duration of that call. Read the ids `duckdb_logs` reports for the entry through its + * getters, e.g. `duckdb_v2_log_sink_write_info_get_connection_id()`. */ -typedef struct _duckdb_v2_replacement_scan_info { +typedef struct _duckdb_v2_log_sink_write_info { void *internal_ptr; -} * duckdb_v2_replacement_scan_info_handle; +} * duckdb_v2_log_sink_write_info_handle; -/* --- Constants for replacement scan --- */ +/*! + * A borrowed opaque handle received by a log sink's flush callback, and valid only for the duration of that call. It + * carries nothing yet. + */ +typedef struct _duckdb_v2_log_sink_flush_info { + void *internal_ptr; +} * duckdb_v2_log_sink_flush_info_handle; -/* --- Function pointer typedefs for replacement scan --- */ +/* --- Constants for log sink --- */ -typedef void (*duckdb_v2_replacement_scan_callback_fn)(duckdb_v2_replacement_scan_info_handle info, - duckdb_v2_context_handle context, - duckdb_v2_error_info_handle *err); +/* --- Function pointer typedefs for log sink --- */ -/* --- Functions for replacement scan --- */ +typedef void (*duckdb_v2_log_sink_write_callback_fn)(duckdb_v2_log_sink_write_info_handle info, void *user_data, + int64_t timestamp, DUCKDB_V2_LOG_LEVEL level, + DUCKDB_V2_LOG_SCOPE scope, const duckdb_v2_str *type, + const duckdb_v2_str *message, duckdb_v2_error_info_handle *err); -/*! - * Creates a new replacement scan that will be registered on the connection. - * - * The scan is visible only to queries on this connection and is released when the connection closes. It is consulted - * before every database-wide scan, so it can claim a name that a built-in scan would otherwise take. The scan starts - * out empty: configure it with `duckdb_v2_replacement_scan_set_callback()` and optionally - * `duckdb_v2_replacement_scan_set_user_data()`, then make it available with `duckdb_v2_replacement_scan_register()`. - * The caller owns the returned handle and must destroy it with `duckdb_v2_replacement_scan_destroy()`, also after - * registration. - * - * history: - * - stable: v2.0.0 - * - * @param connection The connection to create the scan on. - * @param scan On success, receives the newly created replacement scan. Owned by the caller. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_create_with_connection(duckdb_v2_connection_handle connection, - duckdb_v2_replacement_scan_handle *scan, - duckdb_v2_error_info_handle *err); +typedef void (*duckdb_v2_log_sink_flush_callback_fn)(duckdb_v2_log_sink_flush_info_handle info, void *user_data, + duckdb_v2_error_info_handle *err); -/*! - * Creates a new replacement scan that will be registered on the database. - * - * The scan is visible to every connection to the database and lives until the database closes. The scan starts out - * empty: configure it with `duckdb_v2_replacement_scan_set_callback()` and optionally - * `duckdb_v2_replacement_scan_set_user_data()`, then make it available with `duckdb_v2_replacement_scan_register()`. - * The caller owns the returned handle and must destroy it with `duckdb_v2_replacement_scan_destroy()`, also after - * registration. - * - * history: - * - stable: v2.0.0 - * - * @param database The database to create the scan on. - * @param scan On success, receives the newly created replacement scan. Owned by the caller. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_create_with_database(duckdb_v2_database_handle database, - duckdb_v2_replacement_scan_handle *scan, - duckdb_v2_error_info_handle *err); +/* --- Functions for log sink --- */ /*! - * Creates a new replacement scan that will be registered on the loading extension's database. + * Creates a new log sink through a factory. * - * Use this from an extension load callback, where an extension handle is available. The scan is visible to every - * connection to that database and lives until the database closes. The scan starts out empty: configure it with - * `duckdb_v2_replacement_scan_set_callback()` and optionally `duckdb_v2_replacement_scan_set_user_data()`, then make it - * available with `duckdb_v2_replacement_scan_register()`. The caller owns the returned handle and must destroy it with - * `duckdb_v2_replacement_scan_destroy()`, also after registration. + * The log sink starts out empty: configure it with the setter functions, then make it available with + * `duckdb_v2_instance_register_log_sink()` or `duckdb_v2_extension_register_log_sink()`. The caller owns the returned + * handle and must destroy it with `duckdb_v2_log_sink_destroy()`, also after registration. + * + * The log sink can only be registered on the instance the factory belongs to. * * history: * - stable: v2.0.0 * - * @param extension The extension to create the scan on. - * @param scan On success, receives the newly created replacement scan. Owned by the caller. + * @param factory The factory to create the log sink through. + * @param sink On success, receives the newly created log sink. Owned by the caller. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_create_with_extension(duckdb_v2_extension_handle extension, - duckdb_v2_replacement_scan_handle *scan, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_log_sink_create(duckdb_v2_factory_handle factory, + duckdb_v2_log_sink_handle *sink, + duckdb_v2_error_info_handle *err); /*! - * Sets the callback of the replacement scan. + * Sets the name the logging configuration refers to the log sink by. * - * The callback is invoked during query planning for each table reference the catalog could not resolve. It can inspect - * the unresolved name via `duckdb_v2_replacement_scan_get_name()`, and claim the reference via - * `duckdb_v2_replacement_scan_set_function_name()`, `duckdb_v2_replacement_scan_set_collection()` or - * `duckdb_v2_replacement_scan_set_subquery()`; returning without claiming declines it. The context passed to the - * callback may be used to read settings, but not to run queries. A callback must be set before registration. + * Names are matched case-insensitively. The name is borrowed and copied. Calling this again replaces the previous name. + * A name must be set before registration. * * history: * - stable: v2.0.0 * - * @param scan The scan to set the callback of. - * @param callback The callback to set. + * @param sink The log sink to set the name of. + * @param name The name to set. Borrowed and copied. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_callback(duckdb_v2_replacement_scan_handle scan, - duckdb_v2_replacement_scan_callback_fn callback, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_log_sink_set_name(duckdb_v2_log_sink_handle sink, + const duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err); /*! - * Sets arbitrary user data on the replacement scan. + * Sets arbitrary user data on the log sink. * - * Associates an opaque pointer with the scan, retrievable from the callback via - * `duckdb_v2_replacement_scan_get_user_data()`. The opaque handle bundles the pointer with an optional destructor, - * invoked when the data is no longer needed, at the latest when the scan's scope ends. The callback may be invoked from - * several connections at once, so the data must be safe to read concurrently. + * Associates an opaque pointer with the sink, handed to its write and flush callbacks. The opaque handle bundles the + * pointer with an optional destructor, invoked when the data is no longer needed: when the instance is destroyed if the + * sink was registered, else when the log sink handle is. * * history: * - stable: v2.0.0 * - * @param scan The scan to set the user data of. + * @param sink The log sink to set the user data of. * @param data Opaque handle bundling the user data pointer plus an optional destructor. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_user_data(duckdb_v2_replacement_scan_handle scan, - duckdb_v2_opaque *data, - duckdb_v2_error_info_handle *err); - -/*! - * Retrieves the user data set via `duckdb_v2_replacement_scan_set_user_data()`. - * - * history: - * - stable: v2.0.0 - * - * @param info The info handle. - * @param data Receives the user data pointer, or null if none was set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_get_user_data(duckdb_v2_replacement_scan_info_handle info, - void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_log_sink_set_user_data(duckdb_v2_log_sink_handle sink, duckdb_v2_opaque *data, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the name the catalog could not resolve. + * Sets the write callback of the log sink, invoked for every entry the logging configuration directs to the sink. * - * The name as written in the query, as a path: an unqualified reference has a single part, and a qualified one carries - * its catalog and schema before it -- see `duckdb_v2_qname_get_part()`. For a file-backed reference the single part is - * the path with the quotes stripped. The returned name is owned by the caller and must be destroyed via - * `duckdb_v2_qname_destroy()`; being owned, it may outlive the callback. + * The callback must return quickly, must be safe to run concurrently and on any thread, and must not log or change the + * logging configuration. A write callback must be set before registration. * * history: * - stable: v2.0.0 * - * @param info The info handle. - * @param name Receives the unresolved name. Owned by the caller; destroy via `duckdb_v2_qname_destroy()`. + * @param sink The log sink to set the write callback of. + * @param callback The write callback to set. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_get_name(duckdb_v2_replacement_scan_info_handle info, - duckdb_v2_qname_handle *name, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_log_sink_set_write_callback(duckdb_v2_log_sink_handle sink, + duckdb_v2_log_sink_write_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Claims the reference by naming a table function to read instead. - * - * Arguments to the function are added with `duckdb_v2_replacement_scan_add_argument()` and - * `duckdb_v2_replacement_scan_add_named_argument()`. The name is borrowed and copied, and its parts are matched - * case-insensitively. A qualified name targets a function in a particular schema or catalog, exactly as writing it out - * in SQL would. The name is not resolved here: an unknown function fails later, when the replacement is bound. Calling - * this again replaces the previous name and keeps the arguments added so far. + * Sets the optional flush callback of the log sink, invoked when DuckDB flushes its log, so entries the sink holds on + * to can be written out. When DuckDB flushes is up to DuckDB; entries still held when the instance is destroyed are for + * the user data's destructor to write out. * - * The three claim forms, `duckdb_v2_replacement_scan_set_function_name()`, - * `duckdb_v2_replacement_scan_set_collection()` and `duckdb_v2_replacement_scan_set_subquery()`, are mutually - * exclusive: claiming the reference through a second, different form results in an error. + * Like the write callback, it must be safe to run on any thread, and must not log or change the logging configuration. * * history: * - stable: v2.0.0 * - * @param info The info handle. - * @param name The table function to read instead. Borrowed and copied. + * @param sink The log sink to set the flush callback of. + * @param callback The flush callback to set. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_function_name(duckdb_v2_replacement_scan_info_handle info, - duckdb_v2_qname_handle name, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_log_sink_set_flush_callback(duckdb_v2_log_sink_handle sink, + duckdb_v2_log_sink_flush_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Appends a positional argument to the claimed table function. - * - * Positional arguments are passed in the order they are added, before any named ones. The value is borrowed and copied, - * so the caller may destroy it after the call. Requires `duckdb_v2_replacement_scan_set_function_name()` to have been - * called first; otherwise the call results in an error. + * Retrieves the id of the connection that wrote the entry, as `duckdb_logs` reports it. An entry written for the + * database as a whole has none: `present` then receives false and `id` is left untouched. * * history: * - stable: v2.0.0 * - * @param info The info handle. - * @param value The argument value. Borrowed and copied. + * @param info The write info handle of the entry. + * @param id Receives the connection id, if present. + * @param present Receives whether the entry has a connection id. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_add_argument(duckdb_v2_replacement_scan_info_handle info, - duckdb_v2_value_handle value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_log_sink_write_info_get_connection_id(duckdb_v2_log_sink_write_info_handle info, + idx_t *id, bool *present, + duckdb_v2_error_info_handle *err); /*! - * Appends a named argument to the claimed table function. - * - * Equivalent to writing `name := value` at the call site. The name and the value are borrowed and copied, so the caller - * may destroy the value after the call. Requires `duckdb_v2_replacement_scan_set_function_name()` to have been called - * first; otherwise the call results in an error. + * Retrieves the id of the transaction the entry was written in, as `duckdb_logs` reports it. An entry written outside a + * transaction has none: `present` then receives false and `id` is left untouched. * * history: * - stable: v2.0.0 * - * @param info The info handle. - * @param name The name of the parameter to bind the value to. Borrowed and copied. - * @param value The argument value. Borrowed and copied. + * @param info The write info handle of the entry. + * @param id Receives the transaction id, if present. + * @param present Receives whether the entry has a transaction id. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_add_named_argument(duckdb_v2_replacement_scan_info_handle info, - duckdb_v2_identifier_t name, - duckdb_v2_value_handle value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_log_sink_write_info_get_transaction_id(duckdb_v2_log_sink_write_info_handle info, + idx_t *id, bool *present, + duckdb_v2_error_info_handle *err); /*! - * Claims the reference by naming a column data collection to read instead. - * - * The collection is borrowed, not copied: the caller keeps ownership and must keep it alive, and must not clear, reset - * or destroy it, for as long as any result reading it is live, since the result scans its buffers directly. - * - * A prepared statement extends that lifetime well beyond its own results. Preparing a statement over a claimed name - * captures the borrow in the plan, and since a collection claim reads no database there is nothing to invalidate that - * plan: `duckdb_v2_prepared_statement_reuses_plan()` reports true, and every later execution reuses the captured borrow - * without consulting the callback again. So dropping the name from whatever the callback resolves against does not - * release the collection, and releasing it while such a statement is live leaves the next execution reading freed - * memory. Destroy every prepared statement over a claimed name before clearing, resetting or destroying the collection - * behind it. - * - * By default the collection's columns are named col1..colN. Pass `column_names` to name them instead. When supplied, - * `column_count` must equal the collection's column count and no name may be empty; pass NULL and 0 for the default - * names. - * - * The three claim forms, `duckdb_v2_replacement_scan_set_function_name()`, - * `duckdb_v2_replacement_scan_set_collection()` and `duckdb_v2_replacement_scan_set_subquery()`, are mutually - * exclusive: claiming the reference through a second, different form results in an error. + * Retrieves the id of the query the entry was written for, as `duckdb_logs` reports it. An entry written outside a + * query has none: `present` then receives false and `id` is left untouched. * * history: * - stable: v2.0.0 * - * @param info The info handle. - * @param collection The collection to read instead. Borrowed; the caller keeps ownership and must keep it alive. - * @param column_names Optional. An array of `column_count` column names, in order. Pass NULL for the default names - * col1..colN. - * @param column_count The number of names in `column_names`. Must equal the collection's column count, or 0 for the - * default names. + * @param info The write info handle of the entry. + * @param id Receives the query id, if present. + * @param present Receives whether the entry has a query id. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_collection( - duckdb_v2_replacement_scan_info_handle info, duckdb_v2_column_data_collection_handle collection, - const duckdb_v2_identifier_t *column_names, idx_t column_count, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_log_sink_write_info_get_query_id(duckdb_v2_log_sink_write_info_handle info, + idx_t *id, bool *present, + duckdb_v2_error_info_handle *err); /*! - * Claims the reference by naming a query to read instead. - * - * The text is parsed at the call and must contain exactly one SELECT statement: a syntax error, several statements or a - * statement of another kind results in an error. The text is borrowed for the call only. Prefer - * `duckdb_v2_replacement_scan_set_function_name()` when a single table function call suffices, as it avoids the parse - * and keeps the plan flatter. - * - * The three claim forms, `duckdb_v2_replacement_scan_set_function_name()`, - * `duckdb_v2_replacement_scan_set_collection()` and `duckdb_v2_replacement_scan_set_subquery()`, are mutually - * exclusive: claiming the reference through a second, different form results in an error. + * Retrieves the id of the thread that wrote the entry, as `duckdb_logs` reports it. Only an entry written in the thread + * scope has one: otherwise `present` receives false and `id` is left untouched. The id identifies the thread that + * logged, which need not be the thread the write callback runs on. * * history: * - stable: v2.0.0 * - * @param info The info handle. - * @param sql The SELECT statement to read instead. Borrowed for the call only. + * @param info The write info handle of the entry. + * @param id Receives the thread id, if present. + * @param present Receives whether the entry has a thread id. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_subquery(duckdb_v2_replacement_scan_info_handle info, - duckdb_v2_str sql, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_log_sink_write_info_get_thread_id(duckdb_v2_log_sink_write_info_handle info, + idx_t *id, bool *present, + duckdb_v2_error_info_handle *err); /*! - * Sets the alias the claimed replacement is bound under. + * Registers the log sink on the instance, making it available to the logging configuration under its name. * - * Optional, and independent of the claim form. An alias written in the query takes precedence over this one; without - * either, the table name of the reference is used, which for a file-backed reference is the path. The alias is borrowed - * and copied. + * Registering does not direct any entries to the sink by itself; the logging configuration does. Registration is not + * transactional, requires a name and a write callback, and fails when the name is taken: by a sink DuckDB provides + * itself, such as `memory`, `stdout` or `file`, or by a sink registered before. The log sink must have been created + * through a factory of the same instance. Its user data passes to the registered sink. The caller still owns the handle + * after registration and must destroy it with `duckdb_v2_log_sink_destroy()`, which does not affect the registered + * sink. * * history: * - stable: v2.0.0 * - * @param info The info handle. - * @param alias The alias to bind the replacement under. Borrowed and copied. + * @param instance The instance to register on. + * @param sink The log sink to register. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_alias(duckdb_v2_replacement_scan_info_handle info, - duckdb_v2_identifier_t alias, +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_instance_register_log_sink(duckdb_v2_instance_handle instance, + duckdb_v2_log_sink_handle sink, duckdb_v2_error_info_handle *err); /*! - * Registers the replacement scan, making it consulted for names the catalog cannot resolve. + * Registers the log sink on the loading extension's instance, making it available to the logging configuration under + * its name. * - * The scan is registered on the target given at creation: the connection, the database or the loading extension's - * database. Registration requires a callback. Scans are consulted in registration order within their scope, - * connection-scoped ones before database-wide ones, and the first to claim a name wins. A scan cannot be registered - * twice, and a registered scan cannot be unregistered: it lives until its scope ends. Registering a database-wide scan - * while queries are binding on other connections is not thread-safe; register from an extension load callback or before - * issuing queries. The caller still owns the handle after registration and must destroy it with - * `duckdb_v2_replacement_scan_destroy()`, which does not affect the registered scan. + * Use this from the extension's load callback. Registration follows the rules of + * `duckdb_v2_instance_register_log_sink()`. * * history: * - stable: v2.0.0 * - * @param scan The scan to register. + * @param extension The loading extension. + * @param sink The log sink to register. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_register(duckdb_v2_replacement_scan_handle scan, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_extension_register_log_sink(duckdb_v2_extension_handle extension, + duckdb_v2_log_sink_handle sink, + duckdb_v2_error_info_handle *err); /*! - * Destroys the replacement scan, releasing its resources. + * Destroys the log sink, releasing its resources. * * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent - * double-destruction. Destroying the handle after registration does not affect the registered scan. + * double-destruction. Destroying the handle after registration does not affect the registered sink. * * history: * - stable: v2.0.0 * - * @param scan The scan to destroy. + * @param sink The log sink to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_destroy(duckdb_v2_replacement_scan_handle *scan); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_log_sink_destroy(duckdb_v2_log_sink_handle *sink); -/* --- Struct definitions for replacement scan --- */ +/* --- Struct definitions for log sink --- */ /* ============================================================================ - * MODULE: scalar + * MODULE: logical_type * ============================================================================ */ -/* --- Enums for scalar --- */ - -/* --- Struct forward declarations for scalar --- */ - -/* --- Types for scalar --- */ +/* --- Enums for logical_type --- */ /*! - * An owned opaque handle to a custom scalar function being built. Created with - * `duckdb_v2_scalar_function_create_with_connection()` or `duckdb_v2_scalar_function_create_with_extension()`, - * configured with the setter functions (e.g. `duckdb_v2_scalar_function_set_name()`, - * `duckdb_v2_scalar_function_set_exec_callback()`, etc.) and the signature obtained via - * `duckdb_v2_scalar_function_get_signature()`, made available with `duckdb_v2_scalar_function_register()`, and - * destroyed with `duckdb_v2_scalar_function_destroy()`. + * Logical type identifier. The values are the same integers DuckDB uses internally, so round-tripping is lossless. The + * bind- and UDF-only ids (UNKNOWN, ANY, TEMPLATE) appear here for completeness; they do not show up in result column + * types in practice. */ -typedef struct _duckdb_v2_scalar_function { - void *internal_ptr; -} * duckdb_v2_scalar_function_handle; +typedef enum DUCKDB_V2_LOGICAL_TYPE_ID { + //! Invalid / unset. + DUCKDB_V2_LOGICAL_TYPE_ID_INVALID = 0, -/*! - * A borrowed opaque handle to the arguments supplied to a scalar function during the query preparation "bind" phase. - * The "bind" callback receives this handle and can use it to e.g. inspect the arguments given to the function, - * initialize some constant state and set the return type. - */ -typedef struct _duckdb_v2_scalar_function_bind_info { - void *internal_ptr; -} * duckdb_v2_scalar_function_bind_info_handle; + //! NULL constant type. + DUCKDB_V2_LOGICAL_TYPE_ID_SQLNULL = 1, -/*! - * A borrowed opaque handle to the arguments supplied to a scalar function during the local state initialization "init" - * phase. The "init" callback receives this handle and can use it to e.g. set up local state reusable across invocations - * of the "exec" execution callback. - */ -typedef struct _duckdb_v2_scalar_function_init_info { - void *internal_ptr; -} * duckdb_v2_scalar_function_init_info_handle; + //! Unknown — used for unresolved parameter expressions. + DUCKDB_V2_LOGICAL_TYPE_ID_UNKNOWN = 2, -/*! - * A borrowed opaque handle to the arguments supplied to a scalar function during the execution "exec" phase. The "exec" - * callback receives this handle and can use it to e.g. access the input argument vectors, retrieve the number of rows - * to process, and write results to the output vector. - */ -typedef struct _duckdb_v2_scalar_function_exec_info { - void *internal_ptr; -} * duckdb_v2_scalar_function_exec_info_handle; + //! ANY — used for functions that accept any type. + DUCKDB_V2_LOGICAL_TYPE_ID_ANY = 3, -/* --- Constants for scalar --- */ + //! A type carried as a value (type parameters). Values of this type are built via value_create_type. + DUCKDB_V2_LOGICAL_TYPE_ID_TYPE = 6, + DUCKDB_V2_LOGICAL_TYPE_ID_BOOLEAN = 10, + DUCKDB_V2_LOGICAL_TYPE_ID_TINYINT = 11, + DUCKDB_V2_LOGICAL_TYPE_ID_SMALLINT = 12, + DUCKDB_V2_LOGICAL_TYPE_ID_INTEGER = 13, + DUCKDB_V2_LOGICAL_TYPE_ID_BIGINT = 14, -/* --- Function pointer typedefs for scalar --- */ + //! 32-bit days since epoch. + DUCKDB_V2_LOGICAL_TYPE_ID_DATE = 15, -typedef void (*duckdb_v2_scalar_function_bind_callback_fn)(duckdb_v2_scalar_function_bind_info_handle info, - duckdb_v2_context_handle context, - duckdb_v2_error_info_handle *err); + //! 64-bit microseconds since midnight. + DUCKDB_V2_LOGICAL_TYPE_ID_TIME = 16, -typedef void (*duckdb_v2_scalar_function_init_callback_fn)(duckdb_v2_scalar_function_init_info_handle info, - duckdb_v2_context_handle context, - duckdb_v2_error_info_handle *err); + //! 64-bit seconds since epoch. + DUCKDB_V2_LOGICAL_TYPE_ID_TIMESTAMP_SEC = 17, -typedef void (*duckdb_v2_scalar_function_exec_callback_fn)(duckdb_v2_scalar_function_exec_info_handle info, - duckdb_v2_context_handle context, - duckdb_v2_error_info_handle *err); + //! 64-bit milliseconds since epoch. + DUCKDB_V2_LOGICAL_TYPE_ID_TIMESTAMP_MS = 18, -/* --- Functions for scalar --- */ + //! 64-bit microseconds since epoch. + DUCKDB_V2_LOGICAL_TYPE_ID_TIMESTAMP = 19, + + //! 64-bit nanoseconds since epoch. + DUCKDB_V2_LOGICAL_TYPE_ID_TIMESTAMP_NS = 20, + + //! Decimal with width and scale parameters. + DUCKDB_V2_LOGICAL_TYPE_ID_DECIMAL = 21, + DUCKDB_V2_LOGICAL_TYPE_ID_FLOAT = 22, + DUCKDB_V2_LOGICAL_TYPE_ID_DOUBLE = 23, + DUCKDB_V2_LOGICAL_TYPE_ID_VARCHAR = 25, + DUCKDB_V2_LOGICAL_TYPE_ID_BLOB = 26, + DUCKDB_V2_LOGICAL_TYPE_ID_INTERVAL = 27, + DUCKDB_V2_LOGICAL_TYPE_ID_UTINYINT = 28, + DUCKDB_V2_LOGICAL_TYPE_ID_USMALLINT = 29, + DUCKDB_V2_LOGICAL_TYPE_ID_UINTEGER = 30, + DUCKDB_V2_LOGICAL_TYPE_ID_UBIGINT = 31, + + //! 64-bit microseconds since epoch, timezone-aware. + DUCKDB_V2_LOGICAL_TYPE_ID_TIMESTAMP_TZ = 32, + + //! 64-bit nanoseconds since epoch, timezone-aware. + DUCKDB_V2_LOGICAL_TYPE_ID_TIMESTAMP_TZ_NS = 33, + + //! 64-bit microseconds since midnight + 32-bit offset. + DUCKDB_V2_LOGICAL_TYPE_ID_TIME_TZ = 34, + + //! 64-bit nanoseconds since midnight. + DUCKDB_V2_LOGICAL_TYPE_ID_TIME_NS = 35, + DUCKDB_V2_LOGICAL_TYPE_ID_BIT = 36, + + //! Arbitrary-precision integer (VARINT-encoded). + DUCKDB_V2_LOGICAL_TYPE_ID_BIGNUM = 39, + DUCKDB_V2_LOGICAL_TYPE_ID_UHUGEINT = 49, + DUCKDB_V2_LOGICAL_TYPE_ID_HUGEINT = 50, + DUCKDB_V2_LOGICAL_TYPE_ID_UUID = 54, + + //! Geometry (spatial extension). + DUCKDB_V2_LOGICAL_TYPE_ID_GEOMETRY = 60, + DUCKDB_V2_LOGICAL_TYPE_ID_STRUCT = 100, + DUCKDB_V2_LOGICAL_TYPE_ID_LIST = 101, + DUCKDB_V2_LOGICAL_TYPE_ID_MAP = 102, + DUCKDB_V2_LOGICAL_TYPE_ID_ENUM = 104, + DUCKDB_V2_LOGICAL_TYPE_ID_UNION = 107, + DUCKDB_V2_LOGICAL_TYPE_ID_ARRAY = 108, + DUCKDB_V2_LOGICAL_TYPE_ID_VARIANT = 109, + + //! Unnamed struct; shares the physical representation of STRUCT. + DUCKDB_V2_LOGICAL_TYPE_ID_TUPLE = 110, + DUCKDB_V2_LOGICAL_TYPE_ID_MAX_ENUM = 0x7FFFFFFF, +} DUCKDB_V2_LOGICAL_TYPE_ID; + +/* --- Struct forward declarations for logical_type --- */ + +/* --- Types for logical_type --- */ + +/* --- Constants for logical_type --- */ + +/* --- Function pointer typedefs for logical_type --- */ + +/* --- Functions for logical_type --- */ /*! - * Creates a new scalar function that will be registered on the connection's database. + * Creates a logical type from a type id plus value parameters. * - * The function starts out empty: configure it with the setter functions (e.g. `duckdb_v2_scalar_function_set_name()`, - * `duckdb_v2_scalar_function_set_exec_callback()`, etc.) and the signature obtained via - * `duckdb_v2_scalar_function_get_signature()`, then make it available with `duckdb_v2_scalar_function_register()`. The - * caller owns the returned handle and must destroy it with `duckdb_v2_scalar_function_destroy()`, also after - * registration. + * The id-keyed twin of factory_create_type_from_name: the type id names the kind, and the parameters bind it. With + * param_count 0 it instantiates a primitive directly, without touching the catalog: BOOLEAN, TINYINT..BIGINT, + * UTINYINT..UBIGINT, HUGEINT, UHUGEINT, FLOAT, DOUBLE, DATE, every TIME and TIMESTAMP variant, INTERVAL, VARCHAR, BLOB, + * BIT, BIGNUM, and UUID. ANY is accepted as well. + * + * ANY is a function-signature wildcard, constructible here so it can be passed to the function parameter and varargs + * setters, as a fixed-arity ANY parameter or an ANY varargs type. Data-creating surfaces reject it: value and data + * chunk creation, scalar and aggregate return types, table function result columns, cast source and target types, and + * custom type registration. + * + * With parameters, the id resolves to its canonical built-in type and binds through the same constructors as + * factory_create_type_from_name, so the parameterized kinds construct here too: decimal(width, scale); list(T); + * array(T, size); map(K, V); struct(fields); union(members); enum(entries); and varchar with a named "collation" + * parameter. Parameters are (name, value) pairs in two parallel arrays, exactly as for factory_create_type_from_name. + * + * Returns ERROR_INPUT_INVALID when param_count is 0 and the id needs parameters (DECIMAL, LIST, STRUCT, TUPLE, MAP, + * ARRAY, UNION, ENUM, VARIANT, GEOMETRY), for the bind-time-only ids (SQLNULL, UNKNOWN), for TYPE — construct that via + * factory_create_type_from_text — and for INVALID. * * history: * - stable: v2.0.0 * - * @param connection The connection to create the function in. - * @param function On success, receives the newly created scalar function. Owned by the caller. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to resolve the type in. + * @param type_id The type id to instantiate. + * @param param_names Optional. An array of param_count parameter names; a {NULL, 0} entry is positional. Pass NULL for + * all-positional parameters. + * @param param_values An array of param_count parameter values. Borrowed (copied in). Pass NULL when param_count is 0. + * @param param_count The number of parameters. 0 instantiates a parameterless primitive. + * @param out_type Receives the new logical type handle. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_create_with_connection( - duckdb_v2_connection_handle connection, duckdb_v2_scalar_function_handle *function, +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_factory_create_type_from_id( + duckdb_v2_factory_handle factory, DUCKDB_V2_LOGICAL_TYPE_ID type_id, const duckdb_v2_identifier_t *param_names, + const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, duckdb_v2_error_info_handle *err); /*! - * Creates a new scalar function that will be registered on the loading extension's database. + * Creates a logical type from a type name plus value parameters. * - * Use this from an extension load callback, where an extension handle is available. The function starts out empty: - * configure it with the setter functions (e.g. `duckdb_v2_scalar_function_set_name()`, - * `duckdb_v2_scalar_function_set_exec_callback()`, etc.) and the signature obtained via - * `duckdb_v2_scalar_function_get_signature()`, then make it available with `duckdb_v2_scalar_function_register()`. The - * caller owns the returned handle and must destroy it with `duckdb_v2_scalar_function_destroy()`, also after - * registration. + * The generic constructor: resolves the name in the factory's catalog and binds it with the given parameters, exactly + * as SQL binds a type expression. Built-in parameterized kinds and registered extension types construct through this + * same call. + * + * An unqualified name is resolved along the search path first and then in the system catalog, which is where the + * built-in kinds live. A qualified name is resolved exactly as written, with no system-catalog fallback, so it names + * the type in that catalog or schema or fails. + * + * Parameters are (name, value) pairs in two parallel arrays. param_names may be NULL to make every parameter + * positional, and a {NULL, 0} entry makes that one parameter positional. Child types cross as TYPE values, built with + * value_create_type. The built-in shapes are: decimal(width, scale); list(T); array(T, size); map(K, V); struct(fields, + * as named or all-positional TYPE values); union(members, as named TYPE values); enum(entries, as VARCHAR values); and + * varchar with a named "collation" VARCHAR parameter. + * + * A name that resolves to a type with no bind function takes no parameters, and passing any fails. Bind errors — + * unknown name, wrong parameter count or types — surface from the call. + * + * Resolves in the scope of the factory: a connection's or callback's factory reaches the catalog, an instance's factory + * knows only the built-in types. A connection's factory looks up catalog types in the connection's transaction, or in + * one of its own when none is open. Through an instance's factory, only an unqualified built-in name resolves. + * + * The returned logical type is caller-owned and must be destroyed via logical_type_destroy. A type resolved from the + * catalog shares database-owned storage, such as an ENUM dictionary, so destroy it before closing the database. This is + * the inverse of logical_type_get_param_count / logical_type_get_param. * * history: * - stable: v2.0.0 * - * @param extension The extension to create the function in. - * @param function On success, receives the newly created scalar function. Owned by the caller. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to resolve the type in. + * @param name The type name to resolve. Parts are matched case-insensitively; qualify it to name a type in a particular + * catalog or schema. + * @param param_names Optional. An array of param_count parameter names; a {NULL, 0} entry is positional. Pass NULL for + * all-positional parameters. + * @param param_values An array of param_count parameter values. Borrowed (copied in). Pass NULL when param_count is 0. + * @param param_count The number of parameters. + * @param out_type Receives the new logical type handle. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_create_with_extension(duckdb_v2_extension_handle extension, - duckdb_v2_scalar_function_handle *function, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_factory_create_type_from_name( + duckdb_v2_factory_handle factory, duckdb_v2_qname_handle name, const duckdb_v2_identifier_t *param_names, + const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, + duckdb_v2_error_info_handle *err); /*! - * Sets the name of the scalar function. + * Creates a logical type by parsing SQL text. * - * The name is borrowed and copied. Calling this again replaces the previous name. A name must be set before - * registration. + * Parses a SQL type expression and returns the bound logical type. It accepts primitives ("INTEGER"), parameterized + * kinds ("DECIMAL(18,3)", "INTEGER[]", "STRUCT(a INTEGER, b VARCHAR)", "MAP(VARCHAR, INTEGER)", "INTEGER[3]", "UNION(i + * INTEGER, s VARCHAR)", "ENUM('a', 'b')"), and catalog-registered type names, both user-defined and from extensions. A + * catalog type name binds to its structural type, and the name is not preserved as an alias. Names are + * case-insensitive. Parse and bind errors surface from the call. + * + * Resolves in the scope of the factory: a connection's or callback's factory reaches the catalog, an instance's factory + * knows only the built-in types. A connection's factory looks up catalog types in the connection's transaction, or in + * one of its own when none is open. Text naming only built-in types never touches the catalog. + * + * The returned logical type is caller-owned and must be destroyed via logical_type_destroy. A type resolved from the + * catalog shares database-owned storage, such as an ENUM dictionary, so destroy it before closing the database. This is + * the inverse of logical_type_to_text for every constructible kind. * * history: * - stable: v2.0.0 * - * @param function The function to set the name of. - * @param name The name to set. Borrowed and copied. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to resolve the type in. + * @param text View of the SQL type expression to parse. + * @param out_type Receives the new logical type handle. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_name(duckdb_v2_scalar_function_handle function, - duckdb_v2_str *name, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_factory_create_type_from_text(duckdb_v2_factory_handle factory, + const duckdb_v2_str *text, + duckdb_v2_logical_type_handle *out_type, + duckdb_v2_error_info_handle *err); /*! - * Returns the function's signature so it can be configured. + * Creates a copy of a logical type. * - * Add parameters with `duckdb_v2_function_signature_add_parameter()`, set a variadic tail with - * `duckdb_v2_function_signature_set_varargs()` and set the return type with - * `duckdb_v2_function_signature_set_return_type()`. The signature is modified in place; the function must be given a - * signature with a return type before registration. + * On success, writes the new caller-owned handle into *out_type; destroy it via logical_type_destroy. * * history: * - stable: v2.0.0 * - * @param function The function to get the signature of. - * @param sig The returned signature. Borrowed and valid for the lifetime of the function handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param type The logical type to copy. + * @param out_type Receives the new logical type handle. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_get_signature(duckdb_v2_scalar_function_handle function, - duckdb_v2_function_signature_handle *sig, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_logical_type_copy(duckdb_v2_logical_type_handle type, + duckdb_v2_logical_type_handle *out_type, + duckdb_v2_error_info_handle *err); /*! - * Sets arbitrary user data on the scalar function. + * Destroys a logical type handle. * - * Associates an opaque pointer with the function, retrievable from the callbacks via - * `duckdb_v2_scalar_function_bind_get_user_data()`, `duckdb_v2_scalar_function_init_get_user_data()` and - * `duckdb_v2_scalar_function_exec_get_user_data()`. The opaque handle bundles the pointer with an optional destructor, - * invoked when the data is no longer needed. + * Null-safe: passing nullptr or a slot already set to nullptr is a no-op. On success the slot is set to nullptr. * * history: * - stable: v2.0.0 * - * @param function The function to set the user data of. - * @param data Opaque handle bundling the user data pointer plus an optional destructor. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param type The logical type to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_user_data(duckdb_v2_scalar_function_handle function, - duckdb_v2_opaque *data, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_logical_type_destroy(duckdb_v2_logical_type_handle *type); /*! - * Sets a property on the scalar function. + * Compares two logical types for deep equality. * - * Configures a function property that influences planning and execution, such as stability, NULL handling, fallibility - * or collation handling. The value must be one of the `DUCKDB_V2_FUNCTION_PROPERTY_VALUE` entries belonging to `key`; - * passing a value that does not belong to the key, or a key that is not valid for scalar functions, results in an - * error. + * Two types are equal when they agree in kind and in every parameter, recursively. DECIMAL(10, 2) equals DECIMAL(10, + * 2), but not DECIMAL(10, 3) and not FLOAT; two STRUCTs are equal when they have the same field names in the same order + * and equal field types. * * history: * - stable: v2.0.0 * - * @param function The function to set the property of. - * @param key The property to set. - * @param value The value to set for the property. Must belong to `key`. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param left The first logical type. + * @param right The second logical type. + * @param result Receives the result of the comparison. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_property(duckdb_v2_scalar_function_handle function, - DUCKDB_V2_FUNCTION_PROPERTY_KEY key, - DUCKDB_V2_FUNCTION_PROPERTY_VALUE value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_logical_type_is_equal(duckdb_v2_logical_type_handle left, + duckdb_v2_logical_type_handle right, bool *result, + duckdb_v2_error_info_handle *err); /*! - * Sets the optional bind callback of the scalar function. - * - * The bind callback is invoked during query planning for each call site of the function. It can inspect the argument - * types and constant argument values, set a concrete return type, and set "bind data" that is shared with the init and - * exec callbacks. + * Returns the logical type id. * * history: * - stable: v2.0.0 * - * @param function The function to set the bind callback of. - * @param callback The bind callback to set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param type The logical type. + * @param out_id Receives the type id. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_bind_callback( - duckdb_v2_scalar_function_handle function, duckdb_v2_scalar_function_bind_callback_fn callback, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_logical_type_get_id(duckdb_v2_logical_type_handle type, + DUCKDB_V2_LOGICAL_TYPE_ID *out_id, + duckdb_v2_error_info_handle *err); /*! - * Sets the optional init callback of the scalar function. + * Borrows the logical type's name. * - * The init callback is invoked at the start of execution for each thread that will execute the function. It can set - * worker-local "init data", retrievable from the exec callback, to keep mutable state across invocations of the - * function. + * The alias when one is set — an extension or user-defined name such as "POINT_2D" — otherwise the canonical name of + * the type id, such as "INTEGER", "DECIMAL", or "TIMESTAMP WITH TIME ZONE". Never the empty view. This is exactly the + * name vocabulary create_type_from_name accepts. The view is valid until the logical type is destroyed; a canonical + * name points at static storage. * * history: * - stable: v2.0.0 * - * @param function The function to set the init callback of. - * @param callback The init callback to set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param type The logical type. + * @param out_name Receives a borrowed view of the name (alias when set, else the id's canonical name). + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_init_callback( - duckdb_v2_scalar_function_handle function, duckdb_v2_scalar_function_init_callback_fn callback, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_logical_type_get_name(duckdb_v2_logical_type_handle type, + duckdb_v2_identifier_t *out_name, + duckdb_v2_error_info_handle *err); /*! - * Sets the exec callback of the scalar function. + * Renders a logical type as SQL text. * - * The exec callback implements the function's logic: it is invoked during query execution with a batch of input rows - * and must fill the result vector. An exec callback must be set before registration. + * An aliased type renders as its alias, and create_type_from_text resolves that spelling only when the name is + * registered in the factory's catalog. The text round-trips through create_type_from_text for every constructible kind, + * with one exception: ANY renders as "ANY", but create_type_from_text cannot parse it back, since ANY is a signature + * wildcard rather than a parseable SQL type. + * + * Writes into a caller-supplied buffer, so nothing is allocated on the caller's behalf and nothing has to be freed. + * Pass out_text = NULL to size the buffer without rendering into it: out_length then receives the length, and + * out_capacity is ignored. With out_text != NULL, out_capacity must be at least out_length + 1, or the call returns + * ERROR_INPUT_OBJECT_SIZE with out_length set to the required length and out_text left untouched. + * + * out_length never counts the terminator, but a successful write always appends one, so the buffer is usable as a C + * string. * * history: * - stable: v2.0.0 * - * @param function The function to set the exec callback of. - * @param callback The exec callback to set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param type The logical type. + * @param out_text Caller-owned buffer receiving the text plus a null terminator, or NULL to only report the required + * length in out_length. + * @param out_capacity Bytes available in out_text, terminator included. Ignored when out_text is NULL. + * @param out_length Receives the text length excluding the null terminator — written on success and on + * ERROR_INPUT_OBJECT_SIZE. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_exec_callback( - duckdb_v2_scalar_function_handle function, duckdb_v2_scalar_function_exec_callback_fn callback, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_logical_type_to_text(duckdb_v2_logical_type_handle type, char *out_text, + idx_t out_capacity, idx_t *out_length, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the user data set via `duckdb_v2_scalar_function_set_user_data()`. + * Returns the number of value parameters of a logical type. + * + * The inspection dual of create_type_from_name: these are the parameters that reconstruct the type through it. Per + * kind: DECIMAL 2 (width, scale); LIST 1 (element type); ARRAY 2 (element type, size); MAP 2 (key type, value type); + * STRUCT and TUPLE one per field; UNION one per member; ENUM one per dictionary entry; VARCHAR 1 when a collation is + * set, else 0; GEOMETRY 1 when a coordinate system is set, else 0; everything else 0. A bound type reports only what it + * actually carries, so a bind-time modifier that is not retained — an ignored VARCHAR length, say — does not reappear. * * history: * - stable: v2.0.0 * - * @param info The bind info handle. - * @param data Receives the user data pointer, or null if none was set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param type The logical type. + * @param out_count Receives the number of parameters. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_bind_get_user_data( - duckdb_v2_scalar_function_bind_info_handle info, void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_logical_type_get_param_count(duckdb_v2_logical_type_handle type, + idx_t *out_count, duckdb_v2_error_info_handle *err); /*! - * Sets the function's "bind data" from the bind callback. + * Returns one value parameter of a logical type. * - * The bind data is stored with the bound call site and retrievable from the init and exec callbacks. The opaque handle - * bundles the pointer with an optional destructor, invoked when the bind data is no longer needed, and an optional - * equality callback used when comparing two bound call sites; without one, pointer equality is used. + * out_name receives a borrowed view of the parameter name — a STRUCT field name, a UNION member name, "collation" — or + * the empty view {NULL, 0} for a positional parameter. A non-empty view is valid until the logical type is destroyed. + * out_value receives an owned value, destroyed via value_destroy: child types come back as TYPE values (unwrap them + * with value_get_type), DECIMAL width and scale as UTINYINT, ARRAY size as BIGINT, and ENUM dictionary entries and + * collations as VARCHAR. An out-of-range index returns ERROR_INPUT_INVALID. Each call allocates one owned value. * * history: * - stable: v2.0.0 * - * @param info The bind info handle. - * @param data Opaque handle bundling the bind data pointer plus optional destructor and equality callbacks. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param type The logical type. + * @param index The parameter index, in [0, param_count). + * @param out_name Receives a borrowed view of the parameter name, or the empty view {NULL, 0} for a positional + * parameter. + * @param out_value Receives the owned parameter value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_bind_set_bind_data( - duckdb_v2_scalar_function_bind_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_logical_type_get_param(duckdb_v2_logical_type_handle type, idx_t index, + duckdb_v2_identifier_t *out_name, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Returns the number of arguments of the call site being bound. + * Creates a logical type that is an alias of another logical type. + * + * The alias keeps the base type's internal representation, so executing against it needs no special handling, while + * remaining logically distinct from the base type. Intended for custom type bind callbacks, where both the base type + * and the name come from the bind info. * - * Variadic tail arguments are included. Valid indices for `duckdb_v2_scalar_function_bind_get_arg_type()` and - * `duckdb_v2_scalar_function_bind_get_arg_value()` are [0, count). + * The alias is not registered anywhere, so this works through any factory. An empty alias name returns + * ERROR_INPUT_INVALID. * * history: * - stable: v2.0.0 * - * @param info The bind info handle. - * @param count Receives the number of arguments. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to resolve the type in. + * @param base_type The logical type to alias. Typically the base type supplied in the custom type bind info. + * @param alias_name The name for the resulting type. Typically the name of the custom type being constructed, also + * available from the bind info. + * @param out_type Receives the new type: the base type's internal representation under the given alias name. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_bind_get_arg_count( - duckdb_v2_scalar_function_bind_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_factory_create_type_with_alias(duckdb_v2_factory_handle factory, + duckdb_v2_logical_type_handle base_type, + const duckdb_v2_identifier_t *alias_name, + duckdb_v2_logical_type_handle *out_type, + duckdb_v2_error_info_handle *err); +#if DUCKDB_V2_API_ALLOW_UNSTABLE /*! - * Retrieves the type of the argument at the given index. + * Creates the common type of a set of logical types. * - * Fails if the index is out of bounds. The returned type is owned by the caller and must be destroyed via - * `duckdb_v2_logical_type_destroy()`. + * The common type is the type every input implicitly casts to, resolved by the same rule a SQL list literal and + * value_create_list follow: INTEGER and BIGINT resolve to BIGINT, and DECIMAL(4,1) and DECIMAL(5,4) to DECIMAL(7,4). + * The types are combined pairwise from left to right, and the result is not guaranteed to be independent of their + * order. + * + * A connection's or a callback's factory also applies the casts that extensions registered, while an instance's factory + * knows only the built-in ones, so the two can resolve the same set differently. A single type resolves to a copy of + * itself. + * + * Returns ERROR_INPUT_INVALID for an empty set, for a NULL entry and for ANY, and ERROR_QUERY_NOT_IMPLEMENTED when the + * types have no common type. * * history: - * - stable: v2.0.0 + * - unstable: v2.0.0 * - * @param info The bind info handle. - * @param index The index of the argument to get the type of. - * @param type Receives the argument type. Owned by the caller; destroy via `duckdb_v2_logical_type_destroy()`. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to resolve the type in. + * @param types An array of type_count logical types. Borrowed. + * @param type_count The number of types. Must be at least 1. + * @param out_type Receives the new logical type handle. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR -duckdb_v2_scalar_function_bind_get_arg_type(duckdb_v2_scalar_function_bind_info_handle info, idx_t index, - duckdb_v2_logical_type_handle *type, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_factory_create_common_type(duckdb_v2_factory_handle factory, + const duckdb_v2_logical_type_handle *types, + idx_t type_count, + duckdb_v2_logical_type_handle *out_type, + duckdb_v2_error_info_handle *err); +#endif + +/* --- Struct definitions for logical_type --- */ + +/* ============================================================================ + * MODULE: macro + * ============================================================================ */ + +/* --- Enums for macro --- */ + +//! The kind of a macro function, deciding how its body is read. Pass one to `duckdb_v2_macro_function_create()`. +typedef enum DUCKDB_V2_MACRO_FUNCTION_TYPE { + //! A scalar macro. The body is a single expression, and the macro is called like a scalar function. + DUCKDB_V2_MACRO_FUNCTION_TYPE_SCALAR = 0, + + //! A table macro. The body is a single SELECT query, and the macro is called like a table function. + DUCKDB_V2_MACRO_FUNCTION_TYPE_TABLE = 1, + DUCKDB_V2_MACRO_FUNCTION_TYPE_MAX_ENUM = 0x7FFFFFFF, +} DUCKDB_V2_MACRO_FUNCTION_TYPE; + +/* --- Struct forward declarations for macro --- */ + +/* --- Types for macro --- */ /*! - * Folds the argument at the given index to a constant value. + * An owned opaque handle to a macro function being built. Created with `duckdb_v2_macro_function_create()`, configured + * with `duckdb_v2_macro_function_set_name()`, `duckdb_v2_macro_function_set_body()` and the signature obtained via + * `duckdb_v2_macro_function_get_signature()`, made available with `duckdb_v2_extension_register_macro_function()`, and + * destroyed with `duckdb_v2_macro_function_destroy()`. + */ +typedef struct _duckdb_v2_macro_function { + void *internal_ptr; +} * duckdb_v2_macro_function_handle; + +/* --- Constants for macro --- */ + +/* --- Function pointer typedefs for macro --- */ + +/* --- Functions for macro --- */ + +/*! + * Creates a new macro function through a factory. + * + * The macro function starts out empty: configure it with the setter functions and the signature obtained via + * `duckdb_v2_macro_function_get_signature()`, then make it available with + * `duckdb_v2_extension_register_macro_function()`. The caller owns the returned handle and must destroy it with + * `duckdb_v2_macro_function_destroy()`, also after registration. * - * The argument must be foldable to a constant (e.g. a literal or a constant expression); otherwise the call fails with - * an error, as it does when the index is out of bounds. The resulting value may be NULL. The returned value is owned by - * the caller and must be destroyed via `duckdb_v2_value_destroy()`. + * The macro function can only be registered on the database the factory belongs to. * * history: * - stable: v2.0.0 * - * @param info The bind info handle. - * @param index The index of the argument to extract a constant value from. - * @param value Receives the constant value. Owned by the caller; destroy via `duckdb_v2_value_destroy()`. + * @param factory The factory to create the macro function through. + * @param type Whether the macro is a scalar or a table macro. + * @param function On success, receives the newly created macro function. Owned by the caller. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR -duckdb_v2_scalar_function_bind_get_arg_value(duckdb_v2_scalar_function_bind_info_handle info, idx_t index, - duckdb_v2_value_handle *value, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_macro_function_create(duckdb_v2_factory_handle factory, + DUCKDB_V2_MACRO_FUNCTION_TYPE type, + duckdb_v2_macro_function_handle *function, + duckdb_v2_error_info_handle *err); /*! - * Sets the concrete return type of the call site being bound. + * Sets the name of the macro function, as SQL calls it. * - * Overrides the return type declared in the signature. Register the function with an ANY return type and set the - * concrete type here, derived from the argument types, to build a function whose result type depends on its input. The - * type is borrowed and copied. + * The name is borrowed and copied. Calling this again replaces the previous name. A name must be set before + * registration. * * history: * - stable: v2.0.0 * - * @param info The bind info handle. - * @param return_type The return type to set. Borrowed for the call only. + * @param function The macro function to set the name of. + * @param name The name to set. Borrowed and copied. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_bind_set_return_type( - duckdb_v2_scalar_function_bind_info_handle info, duckdb_v2_logical_type_handle return_type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_macro_function_set_name(duckdb_v2_macro_function_handle function, + const duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the user data set via `duckdb_v2_scalar_function_set_user_data()`. + * Sets the body of the macro function, as SQL text. + * + * For a scalar macro the body is a single expression, for a table macro a single SELECT query. It refers to the + * parameters by name. The body is parsed at registration, which fails on a syntax error; names in it are resolved only + * when the macro is called, as with SQL `CREATE MACRO`. The body is borrowed and copied. Calling this again replaces + * the previous body. A body must be set before registration. * * history: * - stable: v2.0.0 * - * @param info The init info handle. - * @param data Receives the user data pointer, or null if none was set. + * @param function The macro function to set the body of. + * @param body The SQL text of the body. Borrowed and copied. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_init_get_user_data( - duckdb_v2_scalar_function_init_info_handle info, void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_macro_function_set_body(duckdb_v2_macro_function_handle function, + const duckdb_v2_str *body, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the bind data set by the function's bind callback. + * Returns the macro function's signature so its parameters can be declared. + * + * Add parameters with `duckdb_v2_function_signature_add_parameter()`. A macro's parameters can always be passed by + * position or by name, so only `FUNCTION_PARAMETER_KIND_STANDARD` parameters are accepted; a macro has no variadic + * parameters. A parameter of type ANY is untyped, and a parameter with a default value can be omitted by a caller. A + * macro's result is decided by its body, so the signature cannot set a return type. Registration fails on a signature + * that does not follow these rules. The signature is modified in place. * * history: * - stable: v2.0.0 * - * @param info The init info handle. - * @param data Receives the bind data pointer, or null if none was set. + * @param function The macro function to get the signature of. + * @param sig The returned signature. Borrowed and valid for the lifetime of the macro function handle. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_init_get_bind_data( - duckdb_v2_scalar_function_init_info_handle info, void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_macro_function_get_signature(duckdb_v2_macro_function_handle function, + duckdb_v2_function_signature_handle *sig, + duckdb_v2_error_info_handle *err); /*! - * Sets the function's worker-local "init data" from the init callback. + * Returns the documentation of the macro function so it can be filled in. * - * The init data is associated with the executing thread for the duration of the query and retrievable from the exec - * callback via `duckdb_v2_scalar_function_exec_get_init_data()`. The opaque handle bundles the pointer with an optional - * destructor, invoked when the init data is no longer needed. + * Set a description with `duckdb_v2_function_docs_set_description()`, and add examples and categories with + * `duckdb_v2_function_docs_add_example()` and `duckdb_v2_function_docs_add_category()`. The documentation is modified + * in place and registered with the macro function. Optional. * * history: * - stable: v2.0.0 * - * @param info The init info handle. - * @param data Opaque handle bundling the init data pointer plus an optional destructor. + * @param function The macro function to get the documentation of. + * @param docs The returned documentation. Borrowed and valid for the lifetime of the macro function handle. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_init_set_init_data( - duckdb_v2_scalar_function_init_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_macro_function_get_docs(duckdb_v2_macro_function_handle function, + duckdb_v2_function_docs_handle *docs, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the user data set via `duckdb_v2_scalar_function_set_user_data()`. + * Registers the macro function, making it available for use in SQL queries. + * + * The macro function is registered on the loading extension's database and attributed to the extension. Use this from + * the extension's load callback. Registration requires a name and a body that parses, and a signature that follows the + * rules of `duckdb_v2_macro_function_get_signature()`. It fails when a function of the same name already exists: a + * macro function has a single overload. The macro function must have been created through a factory of the same + * database. The caller still owns the handle after registration and must destroy it with + * `duckdb_v2_macro_function_destroy()`, which does not affect the registered macro function. * * history: * - stable: v2.0.0 * - * @param info The exec info handle. - * @param data Receives the user data pointer, or null if none was set. + * @param extension The loading extension. + * @param function The macro function to register. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_user_data( - duckdb_v2_scalar_function_exec_info_handle info, void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_extension_register_macro_function(duckdb_v2_extension_handle extension, + duckdb_v2_macro_function_handle function, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the bind data set by the function's bind callback. + * Destroys the macro function, releasing its resources. + * + * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent + * double-destruction. Destroying the handle after registration does not affect the registered macro function. * * history: * - stable: v2.0.0 * - * @param info The exec info handle. - * @param data Receives the bind data pointer, or null if none was set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param function The macro function to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_bind_data( - duckdb_v2_scalar_function_exec_info_handle info, void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_macro_function_destroy(duckdb_v2_macro_function_handle *function); + +/* --- Struct definitions for macro --- */ + +/* ============================================================================ + * MODULE: pragma + * ============================================================================ */ + +/* --- Enums for pragma --- */ + +/* --- Struct forward declarations for pragma --- */ + +/* --- Types for pragma --- */ /*! - * Retrieves the worker-local init data set by the function's init callback. + * An owned opaque handle to a custom pragma function being built. Created with `duckdb_v2_pragma_function_create()`, + * configured with the setter functions (e.g. `duckdb_v2_pragma_function_set_name()`, + * `duckdb_v2_pragma_function_set_query_callback()`, etc.) and the signature obtained via + * `duckdb_v2_pragma_function_get_signature()`, made available with `duckdb_v2_connection_register_pragma_function()` or + * `duckdb_v2_extension_register_pragma_function()`, and destroyed with `duckdb_v2_pragma_function_destroy()`. + */ +typedef struct _duckdb_v2_pragma_function { + void *internal_ptr; +} * duckdb_v2_pragma_function_handle; + +/*! + * A borrowed opaque handle to the result of a pragma function's "query" callback. The query callback receives this + * handle next to a `duckdb_v2_function_bind_info_handle`, which gives access to the arguments and the user data, and + * uses it to set the query to run in place of the `PRAGMA` statement. + */ +typedef struct _duckdb_v2_pragma_function_query_info { + void *internal_ptr; +} * duckdb_v2_pragma_function_query_info_handle; + +/* --- Constants for pragma --- */ + +/* --- Function pointer typedefs for pragma --- */ + +typedef void (*duckdb_v2_pragma_function_query_callback_fn)(duckdb_v2_function_bind_info_handle info, + duckdb_v2_pragma_function_query_info_handle result, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_pragma_function_exec_callback_fn)(duckdb_v2_function_bind_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +/* --- Functions for pragma --- */ + +/*! + * Creates a new pragma function through a factory. + * + * The function starts out empty: configure it with the setter functions (e.g. `duckdb_v2_pragma_function_set_name()`, + * `duckdb_v2_pragma_function_set_query_callback()`, etc.) and the signature obtained via + * `duckdb_v2_pragma_function_get_signature()`, then make it available with + * `duckdb_v2_connection_register_pragma_function()` or `duckdb_v2_extension_register_pragma_function()`. The caller + * owns the returned handle and must destroy it with `duckdb_v2_pragma_function_destroy()`, also after registration. + * + * The pragma function can only be registered on the database the factory belongs to. * * history: * - stable: v2.0.0 * - * @param info The exec info handle. - * @param data Receives the init data pointer for the executing thread, or null if none was set. + * @param factory The factory to create the pragma function through. + * @param function On success, receives the newly created pragma function. Owned by the caller. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_init_data( - duckdb_v2_scalar_function_exec_info_handle info, void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_pragma_function_create(duckdb_v2_factory_handle factory, + duckdb_v2_pragma_function_handle *function, + duckdb_v2_error_info_handle *err); /*! - * Returns how many rows this execution must produce. The exec callback must write exactly this many rows to the result - * vector. Note that this may be less than a full vector: with all-constant arguments the function is invoked for a - * single row and the result is expanded by the engine. + * Sets the name of the pragma function, as the `PRAGMA` statement calls it. + * + * The name is borrowed and copied. Calling this again replaces the previous name. A name must be set before + * registration. * * history: * - stable: v2.0.0 * - * @param info The exec info handle. - * @param count Receives the number of rows. + * @param function The function to set the name of. + * @param name The name to set. Borrowed and copied. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_row_count( - duckdb_v2_scalar_function_exec_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_pragma_function_set_name(duckdb_v2_pragma_function_handle function, + const duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err); /*! - * Returns how many argument vectors this execution carries: one per argument of the call, variadic tail arguments - * included. Valid indices for `duckdb_v2_scalar_function_exec_get_arg()` are [0, count). + * Returns the documentation of the function so it can be filled in. + * + * Set a description with `duckdb_v2_function_docs_set_description()`, and add examples and categories with + * `duckdb_v2_function_docs_add_example()` and `duckdb_v2_function_docs_add_category()`. The documentation is modified + * in place and registered with the function. Optional. * * history: * - stable: v2.0.0 * - * @param info The exec info handle. - * @param count Receives the number of argument vectors. + * @param function The function to get the documentation of. + * @param docs The returned documentation. Borrowed and valid for the lifetime of the function handle. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_arg_count( - duckdb_v2_scalar_function_exec_info_handle info, uint32_t *count, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_pragma_function_get_docs(duckdb_v2_pragma_function_handle function, + duckdb_v2_function_docs_handle *docs, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the argument vector at the given index. + * Returns the function's signature so it can be configured. * - * The vector holds the argument's values for the current batch; use `duckdb_v2_scalar_function_exec_get_row_count()` - * for the number of rows. Fails if the index is out of bounds. Borrowed; valid only for the duration of the callback. + * Add parameters with `duckdb_v2_function_signature_add_parameter()`. A pragma produces no value, so its signature + * cannot set a return type. A signature without parameters makes a pragma called as `PRAGMA name`. The signature is + * modified in place. * * history: * - stable: v2.0.0 * - * @param info The exec info handle. - * @param index The index of the argument vector to get. - * @param vector Receives the borrowed argument vector. + * @param function The function to get the signature of. + * @param sig The returned signature. Borrowed and valid for the lifetime of the function handle. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_arg(duckdb_v2_scalar_function_exec_info_handle info, - uint32_t index, duckdb_v2_vector_handle *vector, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_pragma_function_get_signature(duckdb_v2_pragma_function_handle function, + duckdb_v2_function_signature_handle *sig, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the result vector the exec callback must write into. + * Sets arbitrary user data on the pragma function. * - * The callback must write one entry per input row; use `duckdb_v2_scalar_function_exec_get_row_count()` for the number - * of rows. Borrowed; valid only for the duration of the callback. + * Associates an opaque pointer with the function, retrievable from the callbacks via + * `duckdb_v2_function_bind_get_user_data()`. The opaque handle bundles the pointer with an optional destructor, invoked + * when the data is no longer needed. * * history: * - stable: v2.0.0 * - * @param info The exec info handle. - * @param vector Receives the borrowed result vector to write into. + * @param function The function to set the user data of. + * @param data Opaque handle bundling the user data pointer plus an optional destructor. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_result(duckdb_v2_scalar_function_exec_info_handle info, - duckdb_v2_vector_handle *vector, +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_pragma_function_set_user_data(duckdb_v2_pragma_function_handle function, + duckdb_v2_opaque *data, + duckdb_v2_error_info_handle *err); + +/*! + * Sets the query callback of the pragma function, making it a query pragma. + * + * The query callback is invoked before the `PRAGMA` statement is bound, and must set a SQL string through + * `duckdb_v2_pragma_function_query_set_query()`. The string is parsed and its statements run in place of the `PRAGMA` + * statement. A pragma function has either a query callback or an exec callback; registration fails when both or neither + * are set. + * + * history: + * - stable: v2.0.0 + * + * @param function The function to set the query callback of. + * @param callback The query callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_pragma_function_set_query_callback( + duckdb_v2_pragma_function_handle function, duckdb_v2_pragma_function_query_callback_fn callback, + duckdb_v2_error_info_handle *err); + +/*! + * Sets the exec callback of the pragma function, making it an exec pragma. + * + * The exec callback is invoked when the `PRAGMA` statement executes, and runs for its side effects: the statement + * produces no rows. A pragma function has either a query callback or an exec callback; registration fails when both or + * neither are set. + * + * history: + * - stable: v2.0.0 + * + * @param function The function to set the exec callback of. + * @param callback The exec callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_pragma_function_set_exec_callback( + duckdb_v2_pragma_function_handle function, duckdb_v2_pragma_function_exec_callback_fn callback, + duckdb_v2_error_info_handle *err); + +/*! + * Sets the SQL to run in place of the `PRAGMA` statement. + * + * The query may hold several statements, separated by semicolons. It is borrowed and copied. Calling this again + * replaces the previous query. The query callback must set a query. + * + * history: + * - stable: v2.0.0 + * + * @param info The query info handle. + * @param query The SQL to run. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_pragma_function_query_set_query(duckdb_v2_pragma_function_query_info_handle info, + const duckdb_v2_str *query, duckdb_v2_error_info_handle *err); /*! - * Registers the scalar function, making it available for use in SQL queries. + * Registers the pragma function, making it available to the `PRAGMA` statement. * - * The function is registered on the target given at creation: the connection's database or the loading extension. - * Registration requires a name, an exec callback, and a signature with a complete return type; an ANY return type is - * accepted only together with a bind callback that sets the concrete type per call site. The caller still owns the - * handle after registration and must destroy it with `duckdb_v2_scalar_function_destroy()`, which does not affect the - * registered function. + * The function is registered on the connection's database, visible to every connection, in the connection's + * transaction: inside an explicit transaction, it is invisible to other connections until the transaction commits, and + * a rollback undoes it. Registration requires a name and exactly one of a query callback or an exec callback, and fails + * when a pragma of the same name already exists. The pragma function must have been created through a factory of the + * same database. The caller still owns the handle after registration and must destroy it with + * `duckdb_v2_pragma_function_destroy()`, which does not affect the registered function. * * history: * - stable: v2.0.0 * - * @param function The function to register. + * @param conn The connection to register on. + * @param function The pragma function to register. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_register(duckdb_v2_scalar_function_handle function, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_register_pragma_function(duckdb_v2_connection_handle conn, + duckdb_v2_pragma_function_handle function, + duckdb_v2_error_info_handle *err); /*! - * Destroys the scalar function, releasing its resources. + * Registers the pragma function, making it available to the `PRAGMA` statement. + * + * The function is registered on the loading extension's database and attributed to the extension. Use this from the + * extension's load callback. Registration requires a name and exactly one of a query callback or an exec callback, and + * fails when a pragma of the same name already exists. The pragma function must have been created through a factory of + * the same database. The caller still owns the handle after registration and must destroy it with + * `duckdb_v2_pragma_function_destroy()`, which does not affect the registered function. + * + * history: + * - stable: v2.0.0 + * + * @param extension The loading extension. + * @param function The pragma function to register. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_extension_register_pragma_function(duckdb_v2_extension_handle extension, + duckdb_v2_pragma_function_handle function, + duckdb_v2_error_info_handle *err); + +/*! + * Destroys the pragma function, releasing its resources. * * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent * double-destruction. Destroying the handle after registration does not affect the registered function. @@ -8751,4074 +8963,6149 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_register(duckdb_v2_scalar * @param function The function to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_destroy(duckdb_v2_scalar_function_handle *function); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_pragma_function_destroy(duckdb_v2_pragma_function_handle *function); -/* --- Struct definitions for scalar --- */ +/* --- Struct definitions for pragma --- */ /* ============================================================================ - * MODULE: sql_statement + * MODULE: replacement scan * ============================================================================ */ -/* --- Enums for sql_statement --- */ - -//! SQL statement type of a parsed statement or an executed query. -typedef enum DUCKDB_V2_STATEMENT_TYPE { - DUCKDB_V2_STATEMENT_TYPE_INVALID = 0, - DUCKDB_V2_STATEMENT_TYPE_SELECT = 1, - DUCKDB_V2_STATEMENT_TYPE_INSERT = 2, - DUCKDB_V2_STATEMENT_TYPE_UPDATE = 3, - DUCKDB_V2_STATEMENT_TYPE_CREATE = 4, - DUCKDB_V2_STATEMENT_TYPE_DELETE = 5, - DUCKDB_V2_STATEMENT_TYPE_PREPARE = 6, - DUCKDB_V2_STATEMENT_TYPE_EXECUTE = 7, - DUCKDB_V2_STATEMENT_TYPE_ALTER = 8, - DUCKDB_V2_STATEMENT_TYPE_TRANSACTION = 9, - DUCKDB_V2_STATEMENT_TYPE_COPY = 10, - DUCKDB_V2_STATEMENT_TYPE_ANALYZE = 11, - DUCKDB_V2_STATEMENT_TYPE_VARIABLE_SET = 12, - DUCKDB_V2_STATEMENT_TYPE_CREATE_FUNC = 13, - DUCKDB_V2_STATEMENT_TYPE_EXPLAIN = 14, - DUCKDB_V2_STATEMENT_TYPE_DROP = 15, - DUCKDB_V2_STATEMENT_TYPE_EXPORT = 16, - DUCKDB_V2_STATEMENT_TYPE_PRAGMA = 17, - DUCKDB_V2_STATEMENT_TYPE_VACUUM = 18, - DUCKDB_V2_STATEMENT_TYPE_CALL = 19, - DUCKDB_V2_STATEMENT_TYPE_SET = 20, - DUCKDB_V2_STATEMENT_TYPE_LOAD = 21, - DUCKDB_V2_STATEMENT_TYPE_RELATION = 22, - DUCKDB_V2_STATEMENT_TYPE_EXTENSION = 23, - DUCKDB_V2_STATEMENT_TYPE_LOGICAL_PLAN = 24, - DUCKDB_V2_STATEMENT_TYPE_ATTACH = 25, - DUCKDB_V2_STATEMENT_TYPE_DETACH = 26, - DUCKDB_V2_STATEMENT_TYPE_MULTI = 27, - DUCKDB_V2_STATEMENT_TYPE_COPY_DATABASE = 28, - DUCKDB_V2_STATEMENT_TYPE_UPDATE_EXTENSIONS = 29, - DUCKDB_V2_STATEMENT_TYPE_MERGE_INTO = 30, - DUCKDB_V2_STATEMENT_TYPE_CONNECT = 31, - DUCKDB_V2_STATEMENT_TYPE_DISCONNECT = 32, - DUCKDB_V2_STATEMENT_TYPE_EXTERNAL_RESOURCE = 33, - DUCKDB_V2_STATEMENT_TYPE_MAX_ENUM = 0x7FFFFFFF, -} DUCKDB_V2_STATEMENT_TYPE; +/* --- Enums for replacement scan --- */ -/* --- Struct forward declarations for sql_statement --- */ +/* --- Struct forward declarations for replacement scan --- */ -/* --- Types for sql_statement --- */ +/* --- Types for replacement scan --- */ /*! - * An opaque, owned handle to a single parsed SQL statement, produced by statement_iterator_next. statement_execute runs - * it without consuming it; the caller always destroys it via sql_statement_destroy. + * An owned opaque handle to a replacement scan being built. Created with `duckdb_v2_replacement_scan_create()`, + * configured with `duckdb_v2_replacement_scan_set_callback()` and `duckdb_v2_replacement_scan_set_user_data()`, made + * available with `duckdb_v2_connection_register_replacement_scan()`, `duckdb_v2_instance_register_replacement_scan()` + * or `duckdb_v2_extension_register_replacement_scan()`, and destroyed with `duckdb_v2_replacement_scan_destroy()`. */ -typedef struct _duckdb_v2_sql_statement { +typedef struct _duckdb_v2_replacement_scan { void *internal_ptr; -} * duckdb_v2_sql_statement_handle; +} * duckdb_v2_replacement_scan_handle; /*! - * An opaque, owned handle to an iterator over the statements of a SQL string, produced by parse_sql. Destroy it via - * statement_iterator_destroy; statements it already yielded are independently owned and unaffected. + * A borrowed opaque handle to the arguments supplied to a replacement scan when the binder consults it. The callback + * receives this handle and can use it to inspect the unresolved name and to claim it by naming what to read instead. + * Valid only for the duration of the callback; the name it hands out is owned separately and outlives it. */ -typedef struct _duckdb_v2_statement_iterator { +typedef struct _duckdb_v2_replacement_scan_info { void *internal_ptr; -} * duckdb_v2_statement_iterator_handle; +} * duckdb_v2_replacement_scan_info_handle; -/* --- Constants for sql_statement --- */ +/* --- Constants for replacement scan --- */ -/* --- Function pointer typedefs for sql_statement --- */ +/* --- Function pointer typedefs for replacement scan --- */ -/* --- Functions for sql_statement --- */ +typedef void (*duckdb_v2_replacement_scan_callback_fn)(duckdb_v2_replacement_scan_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +/* --- Functions for replacement scan --- */ /*! - * Parses a SQL string into an iterator over its statements. + * Creates a new replacement scan through a factory. * - * Parses and nothing more: no binding, no catalog access, no transaction. The connection supplies the parser options - * and parser extensions, and is not otherwise touched. Statements are raw parser output; statement-level rewrites - * happen inside statement_execute. The SQL string is copied, so the caller may free it once this call returns. An input - * with no statements — empty, whitespace, or separators only — yields an iterator that is immediately exhausted. + * The scan starts out empty: configure it with `duckdb_v2_replacement_scan_set_callback()` and optionally + * `duckdb_v2_replacement_scan_set_user_data()`, then make it available with + * `duckdb_v2_connection_register_replacement_scan()`, `duckdb_v2_instance_register_replacement_scan()` or + * `duckdb_v2_extension_register_replacement_scan()`. The caller owns the returned handle and must destroy it with + * `duckdb_v2_replacement_scan_destroy()`, also after registration. + * + * The scan can only be registered on the database the factory belongs to. * * history: * - stable: v2.0.0 * - * @param conn The connection supplying the parser state. - * @param sql Null-terminated SQL string; may contain any number of statements. - * @param out_iterator Receives the new iterator handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param factory The factory to create the scan through. + * @param scan On success, receives the newly created replacement scan. Owned by the caller. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_parse_sql(duckdb_v2_connection_handle conn, const char *sql, - duckdb_v2_statement_iterator_handle *out_iterator, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_create(duckdb_v2_factory_handle factory, + duckdb_v2_replacement_scan_handle *scan, + duckdb_v2_error_info_handle *err); /*! - * Yields the next statement, or NULL when exhausted. + * Sets the callback of the replacement scan. * - * On success *out_statement receives the next owned statement, or NULL once the iterator is exhausted — repeatedly, so - * calling again is harmless. A parse error within the input surfaces no later than the call that reaches the failing - * statement: an implementation that parses eagerly reports it from parse_sql instead and yields no statements at all, - * while an incremental one yields the statements ahead of the failure first. On failure *out_statement is set to NULL. + * The callback is invoked during query planning for each table reference the catalog could not resolve. It can inspect + * the unresolved name via `duckdb_v2_replacement_scan_get_name()`, and claim the reference via + * `duckdb_v2_replacement_scan_set_function_name()`, `duckdb_v2_replacement_scan_set_collection()` or + * `duckdb_v2_replacement_scan_set_subquery()`; returning without claiming declines it. The context passed to the + * callback may be used to read settings, but not to run queries. A callback must be set before registration. * * history: * - stable: v2.0.0 * - * @param iterator The iterator to advance. - * @param out_statement Receives an owned statement, or NULL when the iterator is exhausted. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param scan The scan to set the callback of. + * @param callback The callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_statement_iterator_next(duckdb_v2_statement_iterator_handle iterator, - duckdb_v2_sql_statement_handle *out_statement, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_callback(duckdb_v2_replacement_scan_handle scan, + duckdb_v2_replacement_scan_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Binds a parsed statement without executing, yielding its schema signature. - * - * Preprocesses and binds the statement exactly as execution would, but runs nothing: the result is the statement's - * signature as two schemas, not a query result. The statement is borrowed, not consumed, so it can be bound as often as - * you like and executed later. out_schema receives the output schema (result columns) and is never empty, since a - * non-SELECT reports a single status column: a BIGINT changed-rows count, or a BOOLEAN success. out_parameters, when - * non-NULL, receives the input schema (parameter types, ordered by binding index). Both are owned; destroy them via - * schema_destroy. - * - * Binding is read-only and does not disturb a live result: it begins no query, claims no cursor, reuses an active - * transaction read-only, and runs alongside a paused stream. It is single-consumer like the stepping functions, so bind - * concurrently only on a second connection. Prepare-time errors — binder, catalog, preprocessing — surface here. A - * statement that preprocessing expands into a group (a dynamic PIVOT, or statement-expanding DDL such as ALTER ADD - * COLUMN with a non-constant DEFAULT) cannot be bound and is rejected with ERROR_INPUT_INVALID; execute it instead. + * Sets arbitrary user data on the replacement scan. * - * *out_schema and *out_parameters are set to NULL on failure. + * Associates an opaque pointer with the scan, retrievable from the callback via + * `duckdb_v2_replacement_scan_get_user_data()`. The opaque handle bundles the pointer with an optional destructor, + * invoked when the data is no longer needed, at the latest when the scan's scope ends. The callback may be invoked from + * several connections at once, so the data must be safe to read concurrently. * * history: * - stable: v2.0.0 * - * @param conn The connection supplying the catalog, transaction, and parser state. - * @param statement The statement to bind. Borrowed; not consumed. - * @param out_schema Receives the owned output schema (result columns). Destroy via schema_destroy. - * @param out_parameters Optional. When non-NULL, receives the owned input schema (parameter types, ordered by binding - * index). Destroy via schema_destroy. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param scan The scan to set the user data of. + * @param data Opaque handle bundling the user data pointer plus an optional destructor. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_statement_bind(duckdb_v2_connection_handle conn, - duckdb_v2_sql_statement_handle statement, - duckdb_v2_schema_handle *out_schema, - duckdb_v2_schema_handle *out_parameters, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_user_data(duckdb_v2_replacement_scan_handle scan, + duckdb_v2_opaque *data, + duckdb_v2_error_info_handle *err); /*! - * Returns the statement's type as classified by the parser. - * - * This gives the type before the statement-level rewrites that `duckdb_v2_statement_execute()` applies, if any. So a - * PRAGMA reports PRAGMA even where execution rewrites it into a SELECT or a CALL. - * `duckdb_v2_result_get_statement_type()` on the executed result reports the rewritten type. A statement the parser - * expands into a group reports MULTI. Its parts are not visible here, and statement_bind rejects it. + * Retrieves the user data set via `duckdb_v2_replacement_scan_set_user_data()`. * * history: * - stable: v2.0.0 * - * @param statement The statement. - * @param out_type Receives the statement type. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The info handle. + * @param data Receives the user data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_sql_statement_get_type(duckdb_v2_sql_statement_handle statement, - DUCKDB_V2_STATEMENT_TYPE *out_type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_get_user_data(duckdb_v2_replacement_scan_info_handle info, + void **data, duckdb_v2_error_info_handle *err); /*! - * Borrows the statement's own SQL text. + * Retrieves the name the catalog could not resolve. * - * The slice of the parsed string that belongs to this statement. A trailing terminator and the whitespace after it are - * included, whitespace and comments before the first token are not. The statement holds its own copy, so the view - * outlives the SQL string passed to parse_sql and the iterator, and stays valid until the statement is destroyed. A - * statement produced by a parser extension that overrides parsing carries whatever text the extension recorded. + * The name as written in the query, as a path: an unqualified reference has a single part, and a qualified one carries + * its catalog and schema before it -- see `duckdb_v2_qname_get_part()`. For a file-backed reference the single part is + * the path with the quotes stripped. The returned name is owned by the caller and must be destroyed via + * `duckdb_v2_qname_destroy()`; being owned, it may outlive the callback. * * history: * - stable: v2.0.0 * - * @param statement The statement. - * @param out_text Receives a borrowed view of the statement text, valid until the statement is destroyed. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The info handle. + * @param name Receives the unresolved name. Owned by the caller; destroy via `duckdb_v2_qname_destroy()`. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_sql_statement_get_text(duckdb_v2_sql_statement_handle statement, - duckdb_v2_str *out_text, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_get_name(duckdb_v2_replacement_scan_info_handle info, + duckdb_v2_qname_handle *name, + duckdb_v2_error_info_handle *err); /*! - * Returns the number of distinct parameters the statement declares. + * Claims the reference by naming a table function to read instead. * - * This counts the parameters the parser found ($1, ?, $name, ...), so it needs no catalog and no binding; only the - * parameter types wait for `duckdb_v2_statement_bind()`. Repeated uses of one parameter count once. + * Arguments to the function are added with `duckdb_v2_replacement_scan_add_argument()` and + * `duckdb_v2_replacement_scan_add_named_argument()`. The name is borrowed and copied, and its parts are matched + * case-insensitively. A qualified name targets a function in a particular schema or catalog, exactly as writing it out + * in SQL would. The name is not resolved here: an unknown function fails later, when the replacement is bound. Calling + * this again replaces the previous name and keeps the arguments added so far. + * + * The three claim forms, `duckdb_v2_replacement_scan_set_function_name()`, + * `duckdb_v2_replacement_scan_set_collection()` and `duckdb_v2_replacement_scan_set_subquery()`, are mutually + * exclusive: claiming the reference through a second, different form results in an error. * * history: * - stable: v2.0.0 * - * @param statement The statement. - * @param out_count Receives the number of distinct parameters. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The info handle. + * @param name The table function to read instead. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_sql_statement_get_parameter_count(duckdb_v2_sql_statement_handle statement, - idx_t *out_count, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_function_name(duckdb_v2_replacement_scan_info_handle info, + duckdb_v2_qname_handle name, + duckdb_v2_error_info_handle *err); /*! - * Borrows the name of one parameter, in binding order. + * Appends a positional argument to the claimed table function. * - * Parse-time metadata. Positions follow the parameters' binding indices, the order of `duckdb_v2_statement_bind()`'s - * parameter schema, so position i here names field i there. The name is the binding key that - * `duckdb_v2_statement_execute()` accepts: "1", "2", ... for a positional parameter ($1 or ?), the identifier for a - * named one ($name). Positional indices may be gapped ($1 and $3 without $2), in which case the names are "1" and "3" - * at positions 0 and 1. The view is valid until the statement is destroyed. An index outside [0, count) is rejected - * with ERROR_INPUT_OUT_OF_RANGE. + * Positional arguments are passed in the order they are added, before any named ones. The value is borrowed and copied, + * so the caller may destroy it after the call. Requires `duckdb_v2_replacement_scan_set_function_name()` to have been + * called first; otherwise the call results in an error. * * history: * - stable: v2.0.0 * - * @param statement The statement. - * @param index Zero-based position in binding order. - * @param out_name Receives a borrowed view of the parameter name, valid until the statement is destroyed. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The info handle. + * @param value The argument value. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_sql_statement_get_parameter_name(duckdb_v2_sql_statement_handle statement, - idx_t index, duckdb_v2_identifier_t *out_name, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_add_argument(duckdb_v2_replacement_scan_info_handle info, + duckdb_v2_value_handle value, + duckdb_v2_error_info_handle *err); /*! - * Destroys a statement handle. + * Appends a named argument to the claimed table function. * - * Null-safe: passing nullptr or a slot already set to nullptr is a no-op. statement_execute does not consume a - * statement, so every statement is destroyed here once it is no longer needed. On success the slot is set to nullptr. + * Equivalent to writing `name := value` at the call site. The name and the value are borrowed and copied, so the caller + * may destroy the value after the call. Requires `duckdb_v2_replacement_scan_set_function_name()` to have been called + * first; otherwise the call results in an error. * * history: * - stable: v2.0.0 * - * @param statement The statement to destroy. + * @param info The info handle. + * @param name The name of the parameter to bind the value to. Borrowed and copied. + * @param value The argument value. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_sql_statement_destroy(duckdb_v2_sql_statement_handle *statement); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_add_named_argument(duckdb_v2_replacement_scan_info_handle info, + const duckdb_v2_identifier_t *name, + duckdb_v2_value_handle value, + duckdb_v2_error_info_handle *err); /*! - * Destroys a statement iterator handle. + * Claims the reference by naming a column data collection to read instead. * - * Null-safe: passing nullptr or a slot already set to nullptr is a no-op. Statements already yielded are independently - * owned and stay valid. On success the slot is set to nullptr. + * The collection is borrowed, not copied: the caller keeps ownership and must keep it alive, and must not clear, reset + * or destroy it, for as long as any result reading it is live, since the result scans its buffers directly. * - * history: - * - stable: v2.0.0 + * A prepared statement extends that lifetime well beyond its own results. Preparing a statement over a claimed name + * captures the borrow in the plan, and since a collection claim reads no database there is nothing to invalidate that + * plan: `duckdb_v2_prepared_statement_reuses_plan()` reports true, and every later execution reuses the captured borrow + * without consulting the callback again. So dropping the name from whatever the callback resolves against does not + * release the collection, and releasing it while such a statement is live leaves the next execution reading freed + * memory. Destroy every prepared statement over a claimed name before clearing, resetting or destroying the collection + * behind it. * - * @param iterator The iterator to destroy. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_statement_iterator_destroy(duckdb_v2_statement_iterator_handle *iterator); - -/* --- Struct definitions for sql_statement --- */ - -/* ============================================================================ - * MODULE: value - * ============================================================================ */ - -/* --- Enums for value --- */ - -/* --- Struct forward declarations for value --- */ - -/* --- Types for value --- */ - -/* --- Constants for value --- */ - -/* --- Function pointer typedefs for value --- */ - -/* --- Functions for value --- */ - -/*! - * Destroys a value handle. + * By default the collection's columns are named col1..colN. Pass `column_names` to name them instead. When supplied, + * `column_count` must equal the collection's column count and no name may be empty; pass NULL and 0 for the default + * names. * - * Null-safe: passing nullptr or a slot already set to nullptr is a no-op. On success the slot is set to nullptr. + * The three claim forms, `duckdb_v2_replacement_scan_set_function_name()`, + * `duckdb_v2_replacement_scan_set_collection()` and `duckdb_v2_replacement_scan_set_subquery()`, are mutually + * exclusive: claiming the reference through a second, different form results in an error. * * history: * - stable: v2.0.0 * - * @param value The value to destroy. + * @param info The info handle. + * @param collection The collection to read instead. Borrowed; the caller keeps ownership and must keep it alive. + * @param column_names Optional. An array of `column_count` column names, in order. Pass NULL for the default names + * col1..colN. + * @param column_count The number of names in `column_names`. Must equal the collection's column count, or 0 for the + * default names. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_destroy(duckdb_v2_value_handle *value); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_collection( + duckdb_v2_replacement_scan_info_handle info, duckdb_v2_column_data_collection_handle collection, + const duckdb_v2_identifier_t *column_names, idx_t column_count, duckdb_v2_error_info_handle *err); /*! - * Creates a NULL value of the given logical type. + * Claims the reference by naming a query to read instead. * - * The logical type is borrowed and copied into the value, so the caller can destroy it independently. + * The text is parsed at the call and must contain exactly one SELECT statement: a syntax error, several statements or a + * statement of another kind results in an error. The text is borrowed for the call only. Prefer + * `duckdb_v2_replacement_scan_set_function_name()` when a single table function call suffices, as it avoids the parse + * and keeps the plan flatter. + * + * The three claim forms, `duckdb_v2_replacement_scan_set_function_name()`, + * `duckdb_v2_replacement_scan_set_collection()` and `duckdb_v2_replacement_scan_set_subquery()`, are mutually + * exclusive: claiming the reference through a second, different form results in an error. * * history: * - stable: v2.0.0 * - * @param type The borrowed logical type to attach to the NULL value. - * @param out_value Receives the new NULL value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The info handle. + * @param sql The SELECT statement to read instead. Borrowed for the call only. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_null(duckdb_v2_logical_type_handle type, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_subquery(duckdb_v2_replacement_scan_info_handle info, + const duckdb_v2_str *sql, + duckdb_v2_error_info_handle *err); /*! - * Returns the value as a BOOLEAN. + * Sets the alias the claimed replacement is bound under. * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * Optional, and independent of the claim form. An alias written in the query takes precedence over this one; without + * either, the table name of the reference is used, which for a file-backed reference is the path. The alias is borrowed + * and copied. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload as a bool. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The info handle. + * @param alias The alias to bind the replacement under. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_bool(duckdb_v2_value_handle value, bool *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_alias(duckdb_v2_replacement_scan_info_handle info, + const duckdb_v2_identifier_t *alias, + duckdb_v2_error_info_handle *err); /*! - * Returns the value as a UTINYINT. + * Registers the replacement scan, making it consulted for names the catalog cannot resolve. * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * The scan is visible only to queries on this connection and is released when the connection is destroyed. It is + * consulted before every instance-wide scan, so it can claim a name that a built-in scan would otherwise take. + * Registration requires a callback. Scans are consulted in registration order within their scope, connection-scoped + * ones before instance-wide ones, and the first to claim a name wins. A scan cannot be registered twice, and a + * registered scan cannot be unregistered: it lives until its scope ends. The scan must have been created through a + * factory of the same database. The caller still owns the handle after registration and must destroy it with + * `duckdb_v2_replacement_scan_destroy()`, which does not affect the registered scan. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload as a uint8_t. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param conn The connection to register on. + * @param scan The replacement scan to register. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_utinyint(duckdb_v2_value_handle value, uint8_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_register_replacement_scan(duckdb_v2_connection_handle conn, + duckdb_v2_replacement_scan_handle scan, + duckdb_v2_error_info_handle *err); /*! - * Returns the value as a USMALLINT. + * Registers the replacement scan, making it consulted for names the catalog cannot resolve. * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * The scan is visible to every connection to the instance and lives until the instance is destroyed. Registering it + * while queries are binding on other connections is not thread-safe: register before issuing queries. Registration + * requires a callback. Scans are consulted in registration order within their scope, connection-scoped ones before + * instance-wide ones, and the first to claim a name wins. A scan cannot be registered twice, and a registered scan + * cannot be unregistered: it lives until its scope ends. The scan must have been created through a factory of the same + * database. The caller still owns the handle after registration and must destroy it with + * `duckdb_v2_replacement_scan_destroy()`, which does not affect the registered scan. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload as a uint16_t. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param instance The instance to register on. + * @param scan The replacement scan to register. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_usmallint(duckdb_v2_value_handle value, uint16_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_instance_register_replacement_scan(duckdb_v2_instance_handle instance, + duckdb_v2_replacement_scan_handle scan, + duckdb_v2_error_info_handle *err); /*! - * Returns the value as a UINTEGER. + * Registers the replacement scan, making it consulted for names the catalog cannot resolve. * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * The scan is visible to every connection to the loading extension's instance and lives until the instance is + * destroyed. Use this from the extension's load callback. Registration requires a callback. Scans are consulted in + * registration order within their scope, connection-scoped ones before instance-wide ones, and the first to claim a + * name wins. A scan cannot be registered twice, and a registered scan cannot be unregistered: it lives until its scope + * ends. The scan must have been created through a factory of the same database. The caller still owns the handle after + * registration and must destroy it with `duckdb_v2_replacement_scan_destroy()`, which does not affect the registered + * scan. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload as a uint32_t. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param extension The loading extension. + * @param scan The replacement scan to register. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_uint(duckdb_v2_value_handle value, uint32_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_extension_register_replacement_scan(duckdb_v2_extension_handle extension, + duckdb_v2_replacement_scan_handle scan, + duckdb_v2_error_info_handle *err); /*! - * Returns the value as a UBIGINT. + * Destroys the replacement scan, releasing its resources. * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent + * double-destruction. Destroying the handle after registration does not affect the registered scan. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload as a uint64_t. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param scan The scan to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_ubigint(duckdb_v2_value_handle value, uint64_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_replacement_scan_destroy(duckdb_v2_replacement_scan_handle *scan); -/*! - * Returns the value as a UHUGEINT. - * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. - * - * history: - * - stable: v2.0.0 - * - * @param value The value to read. - * @param out Receives the payload as a uint128_t. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR +/* --- Struct definitions for replacement scan --- */ + +/* ============================================================================ + * MODULE: scalar + * ============================================================================ */ + +/* --- Enums for scalar --- */ + +/* --- Struct forward declarations for scalar --- */ + +/* --- Types for scalar --- */ + +/*! + * An owned opaque handle to a custom scalar function being built. Created with `duckdb_v2_scalar_function_create()`, + * configured with the setter functions (e.g. `duckdb_v2_scalar_function_set_name()`, + * `duckdb_v2_scalar_function_set_exec_callback()`, etc.) and the signature obtained via + * `duckdb_v2_scalar_function_get_signature()`, made available with `duckdb_v2_connection_register_scalar_function()` or + * `duckdb_v2_extension_register_scalar_function()`, and destroyed with `duckdb_v2_scalar_function_destroy()`. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_uhugeint(duckdb_v2_value_handle value, duckdb_v2_uhugeint_t *out, - duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_scalar_function { + void *internal_ptr; +} * duckdb_v2_scalar_function_handle; /*! - * Returns the value as a TINYINT. + * A borrowed opaque handle to the result of a scalar function's "resolve types" phase. The "resolve types" callback + * receives this handle next to a `duckdb_v2_function_bind_info_handle`, which gives access to the arguments and the + * user data, and can use it to set the return type of the call site being bound. + */ +typedef struct _duckdb_v2_scalar_function_resolve_types_info { + void *internal_ptr; +} * duckdb_v2_scalar_function_resolve_types_info_handle; + +/*! + * A borrowed opaque handle to the result of a scalar function's "bind" phase. The "bind" callback receives this handle + * next to a `duckdb_v2_function_bind_info_handle`, which gives access to the arguments and the user data, and can use + * it to set the bind data of the call site being bound. + */ +typedef struct _duckdb_v2_scalar_function_bind_info { + void *internal_ptr; +} * duckdb_v2_scalar_function_bind_info_handle; + +/*! + * A borrowed opaque handle to the arguments supplied to a scalar function during the local state initialization "init" + * phase. The "init" callback receives this handle and can use it to e.g. set up local state reusable across invocations + * of the "exec" execution callback. + */ +typedef struct _duckdb_v2_scalar_function_init_info { + void *internal_ptr; +} * duckdb_v2_scalar_function_init_info_handle; + +/*! + * A borrowed opaque handle to the arguments supplied to a scalar function during the execution "exec" phase. The "exec" + * callback receives this handle and can use it to e.g. access the input argument vectors, retrieve the number of rows + * to process, and write results to the output vector. + */ +typedef struct _duckdb_v2_scalar_function_exec_info { + void *internal_ptr; +} * duckdb_v2_scalar_function_exec_info_handle; + +/* --- Constants for scalar --- */ + +/* --- Function pointer typedefs for scalar --- */ + +typedef void (*duckdb_v2_scalar_function_resolve_types_callback_fn)( + duckdb_v2_function_bind_info_handle info, duckdb_v2_scalar_function_resolve_types_info_handle result, + duckdb_v2_context_handle context, duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_scalar_function_bind_callback_fn)(duckdb_v2_function_bind_info_handle info, + duckdb_v2_scalar_function_bind_info_handle result, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_scalar_function_init_callback_fn)(duckdb_v2_scalar_function_init_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_scalar_function_exec_callback_fn)(duckdb_v2_scalar_function_exec_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +/* --- Functions for scalar --- */ + +/*! + * Creates a new scalar function through a factory. * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * The function starts out empty: configure it with the setter functions (e.g. `duckdb_v2_scalar_function_set_name()`, + * `duckdb_v2_scalar_function_set_exec_callback()`, etc.) and the signature obtained via + * `duckdb_v2_scalar_function_get_signature()`, then make it available with + * `duckdb_v2_connection_register_scalar_function()` or `duckdb_v2_extension_register_scalar_function()`. The caller + * owns the returned handle and must destroy it with `duckdb_v2_scalar_function_destroy()`, also after registration. + * + * The scalar function can only be registered on the database the factory belongs to. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload as an int8_t. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param factory The factory to create the scalar function through. + * @param function On success, receives the newly created scalar function. Owned by the caller. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_tinyint(duckdb_v2_value_handle value, int8_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_create(duckdb_v2_factory_handle factory, + duckdb_v2_scalar_function_handle *function, + duckdb_v2_error_info_handle *err); /*! - * Returns the value as a SMALLINT. + * Sets the name of the scalar function. * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * The name is borrowed and copied. Calling this again replaces the previous name. A name must be set before + * registration. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload as an int16_t. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the name of. + * @param name The name to set. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_smallint(duckdb_v2_value_handle value, int16_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_name(duckdb_v2_scalar_function_handle function, + const duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err); /*! - * Returns the value as an INTEGER. + * Returns the documentation of the function so it can be filled in. * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * Set a description with `duckdb_v2_function_docs_set_description()`, and add examples and categories with + * `duckdb_v2_function_docs_add_example()` and `duckdb_v2_function_docs_add_category()`. The documentation is modified + * in place, describes this overload only, and is registered with the function. Optional. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload as an int32_t. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to get the documentation of. + * @param docs The returned documentation. Borrowed and valid for the lifetime of the function handle. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_int(duckdb_v2_value_handle value, int32_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_get_docs(duckdb_v2_scalar_function_handle function, + duckdb_v2_function_docs_handle *docs, + duckdb_v2_error_info_handle *err); /*! - * Returns the value as a BIGINT. + * Returns the function's signature so it can be configured. * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * Add parameters with `duckdb_v2_function_signature_add_parameter()` and set the return type with + * `duckdb_v2_function_signature_set_return_type()`. The signature is modified in place; the function must be given a + * signature with a return type before registration. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload as an int64_t. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to get the signature of. + * @param sig The returned signature. Borrowed and valid for the lifetime of the function handle. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_bigint(duckdb_v2_value_handle value, int64_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_get_signature(duckdb_v2_scalar_function_handle function, + duckdb_v2_function_signature_handle *sig, + duckdb_v2_error_info_handle *err); /*! - * Returns the value as a HUGEINT. + * Sets arbitrary user data on the scalar function. * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * Associates an opaque pointer with the function, retrievable from the callbacks via + * `duckdb_v2_function_bind_get_user_data()`, `duckdb_v2_scalar_function_init_get_user_data()` and + * `duckdb_v2_scalar_function_exec_get_user_data()`. The opaque handle bundles the pointer with an optional destructor, + * invoked when the data is no longer needed. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload as an int128_t. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the user data of. + * @param data Opaque handle bundling the user data pointer plus an optional destructor. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_hugeint(duckdb_v2_value_handle value, duckdb_v2_hugeint_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_user_data(duckdb_v2_scalar_function_handle function, + duckdb_v2_opaque *data, + duckdb_v2_error_info_handle *err); /*! - * Returns the value as a VARCHAR. + * Sets a property on the scalar function. * - * The string is borrowed from the value and stays valid until the value is destroyed. That borrow is why this getter - * does not convert: a converted copy would not outlive the call. A value of any other type id, and a NULL value, return - * ERROR_INPUT_INVALID. + * Configures a function property that influences planning and execution, such as stability, NULL handling, fallibility + * or collation handling. The value must be one of the `DUCKDB_V2_FUNCTION_PROPERTY_VALUE` entries belonging to `key`; + * passing a value that does not belong to the key, or a key that is not valid for scalar functions, results in an + * error. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload as a UTF-8 string. The returned string is borrowed and valid until the value handle - * is destroyed. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the property of. + * @param key The property to set. + * @param value The value to set for the property. Must belong to `key`. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_varchar(duckdb_v2_value_handle value, duckdb_v2_str *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_property(duckdb_v2_scalar_function_handle function, + DUCKDB_V2_FUNCTION_PROPERTY_KEY key, + DUCKDB_V2_FUNCTION_PROPERTY_VALUE value, + duckdb_v2_error_info_handle *err); /*! - * Returns the value as a byte string: a BLOB, or the storage bytes of a BIT or BIGNUM. + * Sets the optional resolve types callback of the scalar function. * - * The string is borrowed from the value and stays valid until the value is destroyed. That borrow is why this getter - * does not convert: a converted copy would not outlive the call. A value of any other type id, and a NULL value, return - * ERROR_INPUT_INVALID. + * The resolve types callback is invoked during query planning for each call site of the function, before the arguments + * are cast to the parameter types of the signature. Through its `duckdb_v2_function_bind_info_handle` it can inspect + * the argument types as they were passed and constant argument values. Through its + * `duckdb_v2_scalar_function_resolve_types_info_handle` it can set a concrete return type. It cannot set "bind data", + * use the bind callback for that. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload as a byte string. The returned string is borrowed and valid until the value handle is - * destroyed. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the resolve types callback of. + * @param callback The resolve types callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_blob(duckdb_v2_value_handle value, duckdb_v2_str *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_resolve_types_callback( + duckdb_v2_scalar_function_handle function, duckdb_v2_scalar_function_resolve_types_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Returns the value as a FLOAT. + * Sets the optional bind callback of the scalar function. * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * The bind callback is invoked during query planning for each call site of the function, after the resolve types + * callback and after the arguments have been cast to the parameter types of the signature. Through its + * `duckdb_v2_function_bind_info_handle` it can inspect the argument types and constant argument values. Through its + * `duckdb_v2_scalar_function_bind_info_handle` it can set "bind data" that is shared with the init and exec callbacks. + * It cannot change the types of the call site, use the resolve types callback for that. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload as a float. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the bind callback of. + * @param callback The bind callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_float(duckdb_v2_value_handle value, float *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_bind_callback( + duckdb_v2_scalar_function_handle function, duckdb_v2_scalar_function_bind_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Returns the value as a DOUBLE. + * Sets the optional init callback of the scalar function. * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * The init callback is invoked at the start of execution for each thread that will execute the function. It can set + * worker-local "init data", retrievable from the exec callback, to keep mutable state across invocations of the + * function. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload as a double. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the init callback of. + * @param callback The init callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_double(duckdb_v2_value_handle value, double *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_init_callback( + duckdb_v2_scalar_function_handle function, duckdb_v2_scalar_function_init_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Returns the value as a DATE. - * - * The payload is days since 1970-01-01, the same unit a vector of this type exposes. + * Sets the exec callback of the scalar function. * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * The exec callback implements the function's logic: it is invoked during query execution with a batch of input rows + * and must fill the result vector. An exec callback must be set before registration. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the exec callback of. + * @param callback The exec callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_date(duckdb_v2_value_handle value, int32_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_exec_callback( + duckdb_v2_scalar_function_handle function, duckdb_v2_scalar_function_exec_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Returns the value as a TIME. - * - * The payload is microseconds since midnight, the same unit a vector of this type exposes. + * Sets the concrete return type of the call site being bound. * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * Overrides the return type declared in the signature. Register the function with an ANY return type and set the + * concrete type here, derived from the argument types, to build a function whose result type depends on its input. The + * type is borrowed and copied. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The resolve types info handle. + * @param return_type The return type to set. Borrowed for the call only. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_time(duckdb_v2_value_handle value, int64_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_resolve_types_set_return_type( + duckdb_v2_scalar_function_resolve_types_info_handle info, duckdb_v2_logical_type_handle return_type, + duckdb_v2_error_info_handle *err); /*! - * Returns the value as a TIME_NS. - * - * The payload is nanoseconds since midnight, the same unit a vector of this type exposes. + * Sets the function's "bind data" from the bind callback. * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * The bind data is stored with the bound call site and retrievable from the init and exec callbacks. The opaque handle + * bundles the pointer with an optional destructor, invoked when the bind data is no longer needed, and an optional + * equality callback used when comparing two bound call sites; without one, pointer equality is used. Scalar functions + * set their bind data through this function, `duckdb_v2_function_bind_set_bind_data()` is not available to them. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The bind info handle. + * @param data Opaque handle bundling the bind data pointer plus optional destructor and equality callbacks. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_time_ns(duckdb_v2_value_handle value, int64_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_bind_set_bind_data( + duckdb_v2_scalar_function_bind_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); /*! - * Returns the value as a TIME_TZ. - * - * The payload packs the time of day and the UTC offset into one integer, in the committed layout: micros in the high 40 - * bits, a biased offset in the low 24. The same form a vector of this type exposes. - * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * Retrieves the user data set via `duckdb_v2_scalar_function_set_user_data()`. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The init info handle. + * @param data Receives the user data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_time_tz(duckdb_v2_value_handle value, uint64_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_init_get_user_data( + duckdb_v2_scalar_function_init_info_handle info, void **data, duckdb_v2_error_info_handle *err); /*! - * Returns the value as a TIMESTAMP. - * - * The payload is microseconds since 1970-01-01, the same unit a vector of this type exposes. - * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * Retrieves the bind data set by the function's bind callback. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The init info handle. + * @param data Receives the bind data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_timestamp(duckdb_v2_value_handle value, int64_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_init_get_bind_data( + duckdb_v2_scalar_function_init_info_handle info, void **data, duckdb_v2_error_info_handle *err); /*! - * Returns the value as a TIMESTAMP_SEC. - * - * The payload is seconds since 1970-01-01, the same unit a vector of this type exposes. + * Sets the function's worker-local "init data" from the init callback. * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * The init data is associated with the executing thread for the duration of the query and retrievable from the exec + * callback via `duckdb_v2_scalar_function_exec_get_init_data()`. The opaque handle bundles the pointer with an optional + * destructor, invoked when the init data is no longer needed. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The init info handle. + * @param data Opaque handle bundling the init data pointer plus an optional destructor. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_timestamp_sec(duckdb_v2_value_handle value, int64_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_init_set_init_data( + duckdb_v2_scalar_function_init_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); /*! - * Returns the value as a TIMESTAMP_MS. - * - * The payload is milliseconds since 1970-01-01, the same unit a vector of this type exposes. - * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * Retrieves the user data set via `duckdb_v2_scalar_function_set_user_data()`. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The exec info handle. + * @param data Receives the user data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_timestamp_ms(duckdb_v2_value_handle value, int64_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_user_data( + duckdb_v2_scalar_function_exec_info_handle info, void **data, duckdb_v2_error_info_handle *err); /*! - * Returns the value as a TIMESTAMP_NS. - * - * The payload is nanoseconds since 1970-01-01, the same unit a vector of this type exposes. - * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * Retrieves the bind data set by the function's bind callback. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The exec info handle. + * @param data Receives the bind data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_timestamp_ns(duckdb_v2_value_handle value, int64_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_bind_data( + duckdb_v2_scalar_function_exec_info_handle info, void **data, duckdb_v2_error_info_handle *err); /*! - * Returns the value as a TIMESTAMP_TZ. - * - * The payload is microseconds since 1970-01-01, in UTC, the same unit a vector of this type exposes. - * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * Retrieves the worker-local init data set by the function's init callback. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The exec info handle. + * @param data Receives the init data pointer for the executing thread, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_timestamp_tz(duckdb_v2_value_handle value, int64_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_init_data( + duckdb_v2_scalar_function_exec_info_handle info, void **data, duckdb_v2_error_info_handle *err); /*! - * Returns the value as a TIMESTAMP_TZ_NS. - * - * The payload is nanoseconds since 1970-01-01, in UTC, the same unit a vector of this type exposes. - * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * Returns how many rows this execution must produce. The exec callback must write exactly this many rows to the result + * vector. Note that this may be less than a full vector: with all-constant arguments the function is invoked for a + * single row and the result is expanded by the engine. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The exec info handle. + * @param count Receives the number of rows. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_timestamp_tz_ns(duckdb_v2_value_handle value, int64_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_row_count( + duckdb_v2_scalar_function_exec_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); /*! - * Returns the value as an INTERVAL. - * - * The payload is the (months, days, micros) triple, the same unit a vector of this type exposes. + * Returns how many argument vectors this invocation carries, split into the four parts of the argument list. * - * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. - * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * The parts and their order are those the bind callback saw through `duckdb_v2_function_bind_get_arg_count()`, and the + * vectors are at the same indices. Valid indices for `duckdb_v2_scalar_function_exec_get_arg()` are [0, the sum of the + * four counts). Every out-parameter may be NULL, in which case nothing is written to it. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the payload. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The exec info handle. + * @param positional_fixed Optional. Receives the number of positional-only and standard parameters. + * @param positional_variadic Optional. Receives the number of arguments `*args` received, 0 when the signature has + * none. + * @param named_fixed Optional. Receives the number of named-only parameters. + * @param named_variadic Optional. Receives the number of arguments `**kwargs` received, 0 when the signature has none. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_interval(duckdb_v2_value_handle value, duckdb_v2_interval_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_arg_count( + duckdb_v2_scalar_function_exec_info_handle info, idx_t *positional_fixed, idx_t *positional_variadic, + idx_t *named_fixed, idx_t *named_variadic, duckdb_v2_error_info_handle *err); /*! - * Returns the value as a UUID, in its internal 128-bit form. + * Retrieves the argument vector at the given index. * - * The dual of value_create_uuid: the storage form a vector element holds, with the high bit flipped so the integer - * sorts, rather than the canonical byte order. A value of any other type id, and a NULL value, return - * ERROR_INPUT_INVALID. + * The index is the one the bind callback used for the argument, e.g. the index + * `duckdb_v2_function_bind_get_arg_index()` found for a name. The vector holds the argument's values for the current + * batch; use `duckdb_v2_scalar_function_exec_get_row_count()` for the number of rows. Fails if the index is out of + * bounds. Borrowed; valid only for the duration of the callback. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the internal 128-bit storage form. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The exec info handle. + * @param index The index of the argument. + * @param vector Receives the borrowed argument vector. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_uuid(duckdb_v2_value_handle value, duckdb_v2_hugeint_t *out, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_arg(duckdb_v2_scalar_function_exec_info_handle info, + idx_t index, duckdb_v2_vector_handle *vector, + duckdb_v2_error_info_handle *err); /*! - * Returns the value as a DECIMAL: its backing integer plus the width and scale of its type. + * Retrieves the result vector the exec callback must write into. * - * The integer is the value scaled by 10^scale — the dual of value_create_decimal — widened to 128 bits from whatever - * storage tier the width selects. The integer getters convert, so a DECIMAL read through value_get_bigint is its - * numeric value with the fraction dropped; this one always reports storage instead, together with the scale needed to - * interpret it. A value of any other type id, and a NULL value, return ERROR_INPUT_INVALID. + * The callback must write one entry per input row; use `duckdb_v2_scalar_function_exec_get_row_count()` for the number + * of rows. Borrowed; valid only for the duration of the callback. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out Receives the backing integer, scaled by 10^scale. - * @param out_width Receives the total digit count of the value's type. - * @param out_scale Receives the digits after the decimal point. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The exec info handle. + * @param vector Receives the borrowed result vector to write into. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_decimal(duckdb_v2_value_handle value, duckdb_v2_hugeint_t *out, - uint8_t *out_width, uint8_t *out_scale, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_result(duckdb_v2_scalar_function_exec_info_handle info, + duckdb_v2_vector_handle *vector, + duckdb_v2_error_info_handle *err); /*! - * Unwraps the logical type carried by a TYPE value. + * Registers the scalar function, making it available for use in SQL queries. * - * Returns ERROR_INPUT_INVALID unless the value is a non-NULL TYPE value. The returned logical type is caller-owned and - * must be destroyed via logical_type_destroy. + * The function is registered on the connection's database, visible to every connection, in the connection's + * transaction: inside an explicit transaction, it is invisible to other connections until the transaction commits, and + * a rollback undoes it. Registration requires a name, an exec callback, and a signature with a complete return type; an + * ANY return type is accepted only together with a resolve types callback that sets the concrete type per call site. + * The scalar function must have been created through a factory of the same database. The caller still owns the handle + * after registration and must destroy it with `duckdb_v2_scalar_function_destroy()`, which does not affect the + * registered function. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out_type Receives the owned logical type. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param conn The connection to register on. + * @param function The scalar function to register. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_type(duckdb_v2_value_handle value, - duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_register_scalar_function(duckdb_v2_connection_handle conn, + duckdb_v2_scalar_function_handle function, + duckdb_v2_error_info_handle *err); /*! - * Creates a NULL value of the given logical type. + * Registers the scalar function, making it available for use in SQL queries. * - * The logical type is borrowed and copied into the value, so the caller can destroy it independently. + * The function is registered on the loading extension's database and attributed to the extension. Use this from the + * extension's load callback. Registration requires a name, an exec callback, and a signature with a complete return + * type; an ANY return type is accepted only together with a resolve types callback that sets the concrete type per call + * site. The scalar function must have been created through a factory of the same database. The caller still owns the + * handle after registration and must destroy it with `duckdb_v2_scalar_function_destroy()`, which does not affect the + * registered function. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param type The borrowed logical type to attach to the NULL value. - * @param out_value Receives the new NULL value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param extension The loading extension. + * @param function The scalar function to register. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_null_with_context(duckdb_v2_context_handle ctx, - duckdb_v2_logical_type_handle type, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_extension_register_scalar_function(duckdb_v2_extension_handle extension, + duckdb_v2_scalar_function_handle function, + duckdb_v2_error_info_handle *err); /*! - * Creates a BOOLEAN value from a bool. + * Destroys the scalar function, releasing its resources. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent + * double-destruction. Destroying the handle after registration does not affect the registered function. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The bool to wrap. - * @param out_value Receives the new BOOLEAN value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_bool_with_context(duckdb_v2_context_handle ctx, bool in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_destroy(duckdb_v2_scalar_function_handle *function); -/*! - * Creates a UTINYINT value from a uint8_t. - * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. - * - * history: - * - stable: v2.0.0 - * - * @param ctx The context to use for the construction. - * @param in_value The uint8_t to wrap. - * @param out_value Receives the new UTINYINT value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_utinyint_with_context(duckdb_v2_context_handle ctx, - uint8_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +/* --- Struct definitions for scalar --- */ + +/* ============================================================================ + * MODULE: sql_statement + * ============================================================================ */ + +/* --- Enums for sql_statement --- */ + +//! SQL statement type of a parsed statement or an executed query. +typedef enum DUCKDB_V2_STATEMENT_TYPE { + DUCKDB_V2_STATEMENT_TYPE_INVALID = 0, + DUCKDB_V2_STATEMENT_TYPE_SELECT = 1, + DUCKDB_V2_STATEMENT_TYPE_INSERT = 2, + DUCKDB_V2_STATEMENT_TYPE_UPDATE = 3, + DUCKDB_V2_STATEMENT_TYPE_CREATE = 4, + DUCKDB_V2_STATEMENT_TYPE_DELETE = 5, + DUCKDB_V2_STATEMENT_TYPE_PREPARE = 6, + DUCKDB_V2_STATEMENT_TYPE_EXECUTE = 7, + DUCKDB_V2_STATEMENT_TYPE_ALTER = 8, + DUCKDB_V2_STATEMENT_TYPE_TRANSACTION = 9, + DUCKDB_V2_STATEMENT_TYPE_COPY = 10, + DUCKDB_V2_STATEMENT_TYPE_ANALYZE = 11, + DUCKDB_V2_STATEMENT_TYPE_VARIABLE_SET = 12, + DUCKDB_V2_STATEMENT_TYPE_CREATE_FUNC = 13, + DUCKDB_V2_STATEMENT_TYPE_EXPLAIN = 14, + DUCKDB_V2_STATEMENT_TYPE_DROP = 15, + DUCKDB_V2_STATEMENT_TYPE_EXPORT = 16, + DUCKDB_V2_STATEMENT_TYPE_PRAGMA = 17, + DUCKDB_V2_STATEMENT_TYPE_VACUUM = 18, + DUCKDB_V2_STATEMENT_TYPE_CALL = 19, + DUCKDB_V2_STATEMENT_TYPE_SET = 20, + DUCKDB_V2_STATEMENT_TYPE_LOAD = 21, + DUCKDB_V2_STATEMENT_TYPE_RELATION = 22, + DUCKDB_V2_STATEMENT_TYPE_EXTENSION = 23, + DUCKDB_V2_STATEMENT_TYPE_LOGICAL_PLAN = 24, + DUCKDB_V2_STATEMENT_TYPE_ATTACH = 25, + DUCKDB_V2_STATEMENT_TYPE_DETACH = 26, + DUCKDB_V2_STATEMENT_TYPE_MULTI = 27, + DUCKDB_V2_STATEMENT_TYPE_COPY_DATABASE = 28, + DUCKDB_V2_STATEMENT_TYPE_UPDATE_EXTENSIONS = 29, + DUCKDB_V2_STATEMENT_TYPE_MERGE_INTO = 30, + DUCKDB_V2_STATEMENT_TYPE_CONNECT = 31, + DUCKDB_V2_STATEMENT_TYPE_DISCONNECT = 32, + DUCKDB_V2_STATEMENT_TYPE_EXTERNAL_RESOURCE = 33, + DUCKDB_V2_STATEMENT_TYPE_PASSTHROUGH = 34, + DUCKDB_V2_STATEMENT_TYPE_MAX_ENUM = 0x7FFFFFFF, +} DUCKDB_V2_STATEMENT_TYPE; + +/* --- Struct forward declarations for sql_statement --- */ + +/* --- Types for sql_statement --- */ /*! - * Creates a USMALLINT value from a uint16_t. - * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. - * - * history: - * - stable: v2.0.0 - * - * @param ctx The context to use for the construction. - * @param in_value The uint16_t to wrap. - * @param out_value Receives the new USMALLINT value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR + * An opaque, owned handle to a single parsed SQL statement, produced by statement_iterator_next. statement_execute runs + * it without consuming it; the caller always destroys it via sql_statement_destroy. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_usmallint_with_context(duckdb_v2_context_handle ctx, - uint16_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_sql_statement { + void *internal_ptr; +} * duckdb_v2_sql_statement_handle; /*! - * Creates a UINTEGER value from a uint32_t. - * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. - * - * history: - * - stable: v2.0.0 - * - * @param ctx The context to use for the construction. - * @param in_value The uint32_t to wrap. - * @param out_value Receives the new UINTEGER value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR + * An opaque, owned handle to an iterator over the statements of a SQL string, produced by parse_sql. Destroy it via + * statement_iterator_destroy; statements it already yielded are independently owned and unaffected. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_uint_with_context(duckdb_v2_context_handle ctx, uint32_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_statement_iterator { + void *internal_ptr; +} * duckdb_v2_statement_iterator_handle; + +/* --- Constants for sql_statement --- */ + +/* --- Function pointer typedefs for sql_statement --- */ + +/* --- Functions for sql_statement --- */ /*! - * Creates a UBIGINT value from a uint64_t. + * Parses a SQL string into an iterator over its statements. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * Parses and nothing more: no binding, no catalog access, no transaction. The connection supplies the parser options + * and parser extensions, and is not otherwise touched. Statements are raw parser output; statement-level rewrites + * happen inside statement_execute. The SQL string is copied, so the caller may free it once this call returns. An input + * with no statements — empty, whitespace, or separators only — yields an iterator that is immediately exhausted. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The uint64_t to wrap. - * @param out_value Receives the new UBIGINT value. + * @param conn The connection supplying the parser configuration. + * @param sql Null-terminated SQL string; may contain any number of statements. + * @param out_iterator Receives the new iterator handle. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_ubigint_with_context(duckdb_v2_context_handle ctx, - uint64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_parse_sql(duckdb_v2_connection_handle conn, const char *sql, + duckdb_v2_statement_iterator_handle *out_iterator, + duckdb_v2_error_info_handle *err); /*! - * Creates a UHUGEINT value from a uint128_t. + * Yields the next statement, or NULL when exhausted. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * On success *out_statement receives the next owned statement, or NULL once the iterator is exhausted — repeatedly, so + * calling again is harmless. A parse error within the input surfaces no later than the call that reaches the failing + * statement: an implementation that parses eagerly reports it from parse_sql instead and yields no statements at all, + * while an incremental one yields the statements ahead of the failure first. On failure *out_statement is set to NULL. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The uint128_t to wrap. - * @param out_value Receives the new UHUGEINT value. + * @param iterator The iterator to advance. + * @param out_statement Receives an owned statement, or NULL when the iterator is exhausted. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_uhugeint_with_context(duckdb_v2_context_handle ctx, - duckdb_v2_uhugeint_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_statement_iterator_next(duckdb_v2_statement_iterator_handle iterator, + duckdb_v2_sql_statement_handle *out_statement, + duckdb_v2_error_info_handle *err); /*! - * Creates a TINYINT value from an int8_t. + * Binds a parsed statement without executing, yielding its schema signature. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * Preprocesses and binds the statement exactly as execution would, but runs nothing: the result is the statement's + * signature as two schemas, not a query result. The statement is borrowed, not consumed, so it can be bound as often as + * you like and executed later. out_schema receives the output schema (result columns) and is never empty, since a + * non-SELECT reports a single status column: a BIGINT changed-rows count, or a BOOLEAN success. out_parameters, when + * non-NULL, receives the input schema (parameter types, ordered by binding index). Both are owned; destroy them via + * schema_destroy. + * + * Binding is read-only and does not disturb a live result: it begins no query, claims no cursor, reuses an active + * transaction read-only, and runs alongside a paused stream. It is single-consumer like the stepping functions, so bind + * concurrently only on a second connection. Prepare-time errors — binder, catalog, preprocessing — surface here. A + * statement that expands into a group (a dynamic PIVOT, or statement-expanding DDL such as ALTER ADD COLUMN with a + * non-constant DEFAULT) has a schema known only at execution, and is reported with ERROR_INPUT_METADATA_DEFERRED; + * execute it instead. + * + * *out_schema and *out_parameters are set to NULL on failure. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The int8_t to wrap. - * @param out_value Receives the new TINYINT value. + * @param conn The connection supplying the catalog, transaction, and parser configuration. + * @param statement The statement to bind. Borrowed; not consumed. + * @param out_schema Receives the owned output schema (result columns). Destroy via schema_destroy. + * @param out_parameters Optional. When non-NULL, receives the owned input schema (parameter types, ordered by binding + * index). Destroy via schema_destroy. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_tinyint_with_context(duckdb_v2_context_handle ctx, int8_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_statement_bind(duckdb_v2_connection_handle conn, + duckdb_v2_sql_statement_handle statement, + duckdb_v2_schema_handle *out_schema, + duckdb_v2_schema_handle *out_parameters, + duckdb_v2_error_info_handle *err); /*! - * Creates a SMALLINT value from an int16_t. + * Binds a column data collection to the statement under a name. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * Makes the collection visible to the statement as a table named `name`, by adding a common table expression to the + * parsed statement. The statement can then be bound, prepared or executed as usual; each execution reads the + * collection's contents at that moment, so a prepared statement can be executed again after the collection was cleared + * and refilled. Supported statement types are SELECT, INSERT, UPDATE, DELETE and MERGE INTO; any other type is rejected + * with `ERROR_INPUT_INVALID`. * - * history: - * - stable: v2.0.0 + * A name the statement already defines as a common table expression, in its SQL text or by a prior call, is rejected + * with `ERROR_INPUT_INVALID`. The binding shadows any catalog table named `name`, as a common table expression does. * - * @param ctx The context to use for the construction. - * @param in_value The int16_t to wrap. - * @param out_value Receives the new SMALLINT value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_smallint_with_context(duckdb_v2_context_handle ctx, - int16_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - -/*! - * Creates an INTEGER value from an int32_t. + * By default the collection's columns are named col1..colN. Pass `column_names` to name them instead; when supplied, + * `column_count` must equal the collection's column count. Pass NULL and 0 for the default names. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * The collection is borrowed: the caller keeps ownership and must keep it alive, and must not clear, reset or destroy + * it, for as long as the statement, any statement copied or prepared from it, or any result executing it is live. An + * empty collection is a valid binding. Several collections can be bound under distinct names. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The int32_t to wrap. - * @param out_value Receives the new INTEGER value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param statement The statement to bind the collection to. Modified in place. + * @param name The non-empty name the statement refers to the collection by. Borrowed and copied. + * @param collection The collection to bind. Borrowed; the caller keeps ownership and must keep it alive. + * @param column_names Optional. An array of `column_count` names for the collection's columns, in order. Pass NULL for + * col1..colN. + * @param column_count The number of names in `column_names`. 0 for the default names; otherwise the collection's column + * count. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_int_with_context(duckdb_v2_context_handle ctx, int32_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_statement_add_collection(duckdb_v2_sql_statement_handle statement, + const duckdb_v2_identifier_t *name, + duckdb_v2_column_data_collection_handle collection, + const duckdb_v2_identifier_t *column_names, + idx_t column_count, duckdb_v2_error_info_handle *err); /*! - * Creates a BIGINT value from an int64_t. + * Returns the statement's type as classified by the parser. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * This gives the type before the statement-level rewrites that `duckdb_v2_statement_execute()` applies, if any. So a + * PRAGMA reports PRAGMA even where execution rewrites it into a SELECT or a CALL. + * `duckdb_v2_result_get_statement_type()` on the executed result reports the rewritten type of a statement that + * rewrites into one. A statement the parser expands into a group reports MULTI, here and on its result. Its parts are + * not visible here, and statement_bind reports ERROR_INPUT_METADATA_DEFERRED for it. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The int64_t to wrap. - * @param out_value Receives the new BIGINT value. + * @param statement The statement. + * @param out_type Receives the statement type. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_bigint_with_context(duckdb_v2_context_handle ctx, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_sql_statement_get_type(duckdb_v2_sql_statement_handle statement, + DUCKDB_V2_STATEMENT_TYPE *out_type, + duckdb_v2_error_info_handle *err); /*! - * Creates a HUGEINT value from an int128_t. + * Borrows the statement's own SQL text. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * The slice of the parsed string that belongs to this statement. A trailing terminator and the whitespace after it are + * included, whitespace and comments before the first token are not. The statement holds its own copy, so the view + * outlives the SQL string passed to parse_sql and the iterator, and stays valid until the statement is destroyed. A + * statement produced by a parser extension that overrides parsing carries whatever text the extension recorded. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The int128_t to wrap. - * @param out_value Receives the new HUGEINT value. + * @param statement The statement. + * @param out_text Receives a borrowed view of the statement text, valid until the statement is destroyed. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_hugeint_with_context(duckdb_v2_context_handle ctx, - duckdb_v2_hugeint_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_sql_statement_get_text(duckdb_v2_sql_statement_handle statement, + duckdb_v2_str *out_text, + duckdb_v2_error_info_handle *err); /*! - * Creates a VARCHAR value from a UTF-8 string. + * Returns the number of distinct parameters the statement declares. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * This counts the parameters the parser found ($1, ?, $name, ...), so it needs no catalog and no binding; only the + * parameter types wait for `duckdb_v2_statement_bind()`. Repeated uses of one parameter count once. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The UTF-8 string to wrap. May be null only when len is 0 (empty string). - * @param out_value Receives the new VARCHAR value. + * @param statement The statement. + * @param out_count Receives the number of distinct parameters. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_varchar_with_context(duckdb_v2_context_handle ctx, - duckdb_v2_str in_value, - duckdb_v2_value_handle *out_value, +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_sql_statement_get_parameter_count(duckdb_v2_sql_statement_handle statement, + idx_t *out_count, duckdb_v2_error_info_handle *err); /*! - * Creates a BLOB value from a byte string. + * Borrows the name of one parameter, in binding order. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * Parse-time metadata. Positions follow the parameters' binding indices, the order of `duckdb_v2_statement_bind()`'s + * parameter schema, so position i here names field i there. The name is the binding key that + * `duckdb_v2_statement_execute()` accepts: "1", "2", ... for a positional parameter ($1 or ?), the identifier for a + * named one ($name). Positional indices may be gapped ($1 and $3 without $2), in which case the names are "1" and "3" + * at positions 0 and 1. The view is valid until the statement is destroyed. An index outside [0, count) is rejected + * with ERROR_INPUT_OUT_OF_RANGE. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The byte string to wrap. May be null only when len is 0 (empty string). - * @param out_value Receives the new BLOB value. + * @param statement The statement. + * @param index Zero-based position in binding order. + * @param out_name Receives a borrowed view of the parameter name, valid until the statement is destroyed. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_blob_with_context(duckdb_v2_context_handle ctx, - duckdb_v2_str in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_sql_statement_get_parameter_name(duckdb_v2_sql_statement_handle statement, + idx_t index, duckdb_v2_identifier_t *out_name, + duckdb_v2_error_info_handle *err); /*! - * Creates a FLOAT value from a float. + * Destroys a statement handle. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * Null-safe: passing nullptr or a slot already set to nullptr is a no-op. statement_execute does not consume a + * statement, so every statement is destroyed here once it is no longer needed. On success the slot is set to nullptr. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The float to wrap. - * @param out_value Receives the new FLOAT value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param statement The statement to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_float_with_context(duckdb_v2_context_handle ctx, float in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_sql_statement_destroy(duckdb_v2_sql_statement_handle *statement); /*! - * Creates a DOUBLE value from a double. + * Destroys a statement iterator handle. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * Null-safe: passing nullptr or a slot already set to nullptr is a no-op. Statements already yielded are independently + * owned and stay valid. On success the slot is set to nullptr. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The double to wrap. - * @param out_value Receives the new DOUBLE value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param iterator The iterator to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_double_with_context(duckdb_v2_context_handle ctx, double in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_statement_iterator_destroy(duckdb_v2_statement_iterator_handle *iterator); -/*! - * Creates a TYPE value from a logical type. - * - * The logical type is borrowed and copied into the value, so the caller can destroy it independently. - * - * history: - * - stable: v2.0.0 - * - * @param ctx The context to use for the construction. - * @param in_type The borrowed logical type to wrap. - * @param out_value Receives the new TYPE value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR +/* --- Struct definitions for sql_statement --- */ + +/* ============================================================================ + * MODULE: vector + * ============================================================================ */ + +/* --- Enums for vector --- */ + +/*! + * Internal representation of a vector, with FSST / SEQUENCE / SHREDDED collapsed into OTHER: the view getter rejects + * those and requires an explicit vector_flatten first. OTHER is the 0-value, so a zero-initialized out-param reads as + * "unspecified, needs flatten" rather than silently looking like FLAT. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_type_with_context(duckdb_v2_context_handle ctx, - duckdb_v2_logical_type_handle in_type, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +typedef enum DUCKDB_V2_VECTOR_TYPE { + //! Default for zero-init. Covers FSST / SEQUENCE / SHREDDED — call vector_flatten first. + DUCKDB_V2_VECTOR_TYPE_OTHER = 0, + + //! Standard per-row storage. + DUCKDB_V2_VECTOR_TYPE_FLAT = 1, + + //! A single value applies to every row in the vector. + DUCKDB_V2_VECTOR_TYPE_CONSTANT = 2, + + //! Data + selection vector indirection into another vector. + DUCKDB_V2_VECTOR_TYPE_DICTIONARY = 3, + DUCKDB_V2_VECTOR_TYPE_MAX_ENUM = 0x7FFFFFFF, +} DUCKDB_V2_VECTOR_TYPE; + +/* --- Struct forward declarations for vector --- */ + +typedef struct duckdb_v2_vector_view duckdb_v2_vector_view; + +typedef struct duckdb_v2_list_entry duckdb_v2_list_entry; + +typedef struct duckdb_v2_hugeint_t duckdb_v2_hugeint_t; + +typedef struct duckdb_v2_uhugeint_t duckdb_v2_uhugeint_t; + +typedef struct duckdb_v2_interval_t duckdb_v2_interval_t; + +/* --- Types for vector --- */ + +/* --- Constants for vector --- */ + +/* --- Function pointer typedefs for vector --- */ + +/* --- Functions for vector --- */ /*! - * Creates a NULL value of the given logical type. + * Returns the internal representation kind: flat, constant, or dictionary. * - * The logical type is borrowed and copied into the value, so the caller can destroy it independently. + * Reports VECTOR_TYPE_OTHER for FSST / SEQUENCE / SHREDDED vectors, which must be passed through vector_flatten before + * vector_get_view will read them. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param type The borrowed logical type to attach to the NULL value. - * @param out_value Receives the new NULL value. + * @param vector The vector. + * @param out_type Receives the representation kind. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_null_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_logical_type_handle type, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_get_vector_type(duckdb_v2_vector_handle vector, + DUCKDB_V2_VECTOR_TYPE *out_type, + duckdb_v2_error_info_handle *err); /*! - * Creates a BOOLEAN value from a bool. + * Returns the vector's logical type. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * The returned logical type is the same type that was passed to vector_reference, or the type that was set on creation. + * It is caller-owned; destroy it via logical_type_destroy. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The bool to wrap. - * @param out_value Receives the new BOOLEAN value. + * @param vector The vector. + * @param out_type Receives the caller-owned logical type handle. Destroy via logical_type_destroy. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_bool_with_connection(duckdb_v2_connection_handle conn, - bool in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_get_logical_type(duckdb_v2_vector_handle vector, + duckdb_v2_logical_type_handle *out_type, + duckdb_v2_error_info_handle *err); /*! - * Creates a UTINYINT value from a uint8_t. + * Reads a vector as a unified view of data, validity, selection, and count. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * Populates out_view with borrowed pointers, valid until the owning chunk is destroyed. view.data addresses the + * vector's elements, to be cast to the layout its logical type implies: a typed leaf array for the primitives, + * hugeint_t / uhugeint_t / interval_t for those kinds, bytes for VARCHAR / BLOB / BIT / BIGNUM, list_entry for LIST and + * MAP, and nothing at all for ARRAY / STRUCT / UNION, whose data lives in their children. + * + * DECIMAL and ENUM are typed leaf arrays too, over a storage tier that follows from the type; both mappings are + * contract. A DECIMAL of width <= 4 stores int16, <= 9 int32, <= 18 int64, and <= 38 int128, boundaries inclusive, with + * the width and scale available from logical_type_get_param. An ENUM stores its dictionary indices as uint8 for a + * dictionary of at most 255 entries, uint16 for at most 65535, and uint32 beyond that, boundaries inclusive and an + * empty dictionary uint8; logical_type_get_param resolves an index to its entry. + * + * Rejects VECTOR_TYPE_OTHER; call vector_flatten first. + * + * Reading a DICTIONARY vector has one side effect: the dictionary's underlying child is flattened in place if it is not + * already FLAT. The parent vector stays DICTIONARY, and only the child's storage shape changes, but any pointer + * previously borrowed into that child — from an earlier vector_get_view on a vector aliasing the same buffer, say — is + * invalidated. This is the only mutation vector_get_view performs; compare vector_flatten, which materializes the whole + * vector to FLAT. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The uint8_t to wrap. - * @param out_value Receives the new UTINYINT value. + * @param vector The vector to read. + * @param out_view Receives the view. Its pointers are borrowed and valid until the owning chunk is destroyed. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_utinyint_with_connection(duckdb_v2_connection_handle conn, - uint8_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_get_view(duckdb_v2_vector_handle vector, duckdb_v2_vector_view *out_view, + duckdb_v2_error_info_handle *err); /*! - * Creates a USMALLINT value from a uint16_t. + * Returns the number of elements in the vector. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * The size is the number of logical elements the vector represents, which for a nested kind can differ from the size of + * its children. An ARRAY vector of size 10 whose array size is 3, for instance, has 10 logical elements while its + * single child vector has 30. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The uint16_t to wrap. - * @param out_value Receives the new USMALLINT value. + * @param vector The vector. + * @param out_size Receives the number of elements. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_usmallint_with_connection(duckdb_v2_connection_handle conn, - uint16_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_get_size(duckdb_v2_vector_handle vector, idx_t *out_size, + duckdb_v2_error_info_handle *err); /*! - * Creates a UINTEGER value from a uint32_t. + * Sets the number of elements in the vector. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * The counterpart of vector_get_size: it declares how many logical elements the vector now holds. It reserves enough + * space for size logical elements. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The uint32_t to wrap. - * @param out_value Receives the new UINTEGER value. + * @param vector The vector. + * @param size The new number of logical elements. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_uint_with_connection(duckdb_v2_connection_handle conn, - uint32_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_set_size(duckdb_v2_vector_handle vector, idx_t size, + duckdb_v2_error_info_handle *err); /*! - * Creates a UBIGINT value from a uint64_t. + * Reads one cell of a vector as an owned value. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * The total fallback reader. It reads any logical row of any vector representation without flattening — FLAT, CONSTANT, + * DICTIONARY, and the compressed kinds the view getter rejects — and covers every type kind, including those with no + * committed view layout (today VARIANT and GEOMETRY), where it is the only way to reach a cell. + * + * Each call allocates one owned value, which makes this a single-cell bridge rather than a per-row loop primitive; use + * vector_get_view on hot paths. The row index is logical and bounds-checked against vector_get_size. The returned value + * is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The uint64_t to wrap. - * @param out_value Receives the new UBIGINT value. + * @param vector The vector to read from. + * @param row The logical row index, in [0, vector_get_size). + * @param out_value Receives the owned cell value. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_ubigint_with_connection(duckdb_v2_connection_handle conn, - uint64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_get_value(duckdb_v2_vector_handle vector, idx_t row, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Creates a UHUGEINT value from a uint128_t. + * Writes one cell of a FLAT vector from a value. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * The total fallback writer. It covers every type kind, including those with no committed view layout (today VARIANT + * and GEOMETRY), where it is the only way to write a cell. The value is borrowed and copied in, then cast to the + * vector's type through the non-strict default cast; a cast failure surfaces from the call. A NULL value clears the + * row's validity. + * + * Requires a FLAT vector: constant, dictionary, and compressed representations are not row-addressable, so call + * vector_flatten first. Each call performs one value write, which makes this a single-cell bridge rather than a per-row + * loop primitive; use the typed mutable-data paths on hot paths. The row index is logical and bounds-checked against + * vector_get_size. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The uint128_t to wrap. - * @param out_value Receives the new UHUGEINT value. + * @param vector The vector to write to. + * @param row The logical row index, in [0, vector_get_size). + * @param value The borrowed value to write. Copied in; cast to the vector's type. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_uhugeint_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_uhugeint_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_set_value(duckdb_v2_vector_handle vector, idx_t row, + duckdb_v2_value_handle value, duckdb_v2_error_info_handle *err); /*! - * Creates a TINYINT value from an int8_t. + * Returns a mutable pointer to the data of a FLAT or CONSTANT vector. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * Any other representation returns ERROR_INPUT_INVALID. The pointer is valid until the owning chunk is destroyed, and + * the vector's storage shape must not change — through a flatten, say — while it is in use. + * + * When writing VARCHAR values, the caller must ensure that the bytes contain valid UTF-8. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The int8_t to wrap. - * @param out_value Receives the new TINYINT value. + * @param vector The vector. + * @param out_data Receives a mutable pointer to the data. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_tinyint_with_connection(duckdb_v2_connection_handle conn, - int8_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_get_data_mutable(duckdb_v2_vector_handle vector, void **out_data, + duckdb_v2_error_info_handle *err); /*! - * Creates a SMALLINT value from an int16_t. + * Forces a vector into FLAT representation, in place. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * A no-op for a vector that is already FLAT. For a CONSTANT, DICTIONARY, FSST, SEQUENCE, or SHREDDED vector it + * materializes the per-row data and switches the representation to FLAT. Call it before vector_get_view if you would + * rather not handle CONSTANT and DICTIONARY semantics in the view; it is required for any vector reporting + * VECTOR_TYPE_OTHER. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The int16_t to wrap. - * @param out_value Receives the new SMALLINT value. + * @param vector The vector to flatten. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_smallint_with_connection(duckdb_v2_connection_handle conn, - int16_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_flatten(duckdb_v2_vector_handle vector, duckdb_v2_error_info_handle *err); /*! - * Creates an INTEGER value from an int32_t. + * Casts every row of a vector into another vector, using a factory. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * The conversion is the one SQL `CAST` / `TRY_CAST` performs, registered custom casts included, from the type of + * `source` to the type of `target`. It covers as many rows as `source` holds (`duckdb_v2_vector_get_size()`), of any + * representation. `target` is replaced by the result, as `duckdb_v2_vector_reference()` would, taking its size: data + * and views taken from it beforehand do not survive the call, and nothing it held before carries over. In + * CAST_MODE_NORMAL a row that does not convert fails the call with the engine's conversion error, and the contents of + * `target` are unspecified. In CAST_MODE_TRY such rows are NULL in `target` instead and the call succeeds; compare the + * validity of `source` and `target` to find them. `cast_err` then receives the conversion error of one of them, e.g. to + * report why the cast did not convert every row. + * + * A connection's or callback's factory casts with that client's cast functions and settings (e.g. TimeZone). An + * instance's factory supports only the built-in casts, with default settings. `source` and `target` must be different + * vectors; `target` is typically a vector of a data chunk created by the caller, or the result vector of a callback. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The int32_t to wrap. - * @param out_value Receives the new INTEGER value. + * @param factory The factory supplying the cast functions. + * @param source The vector to cast. Borrowed for the call only. + * @param target The vector to write the cast rows into. Its logical type is the target type. + * @param mode Whether a row that does not convert fails the call, or becomes NULL. + * @param out_converted Optional. Receives whether every row converted. Always true when a CAST_MODE_NORMAL cast + * succeeds. + * @param cast_err Optional. When a CAST_MODE_TRY cast succeeds without converting every row, receives an opaque info + * handle with the conversion error of one row that did not convert, which the caller must destroy via + * error_info_destroy. Otherwise receives NULL. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_int_with_connection(duckdb_v2_connection_handle conn, - int32_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_cast(duckdb_v2_factory_handle factory, duckdb_v2_vector_handle source, + duckdb_v2_vector_handle target, DUCKDB_V2_CAST_MODE mode, + bool *out_converted, duckdb_v2_error_info_handle *cast_err, + duckdb_v2_error_info_handle *err); /*! - * Creates a BIGINT value from an int64_t. + * Repoints a vector at another vector's data, without copying. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * `vector` takes on the storage of `source`: no data moves, and the two alias the same buffers until one of them is + * reset or re-referenced. Works for any type, including nested types. The source's logical type must equal the + * vector's; a mismatch returns ERROR_INPUT_INVALID. The source's data must outlive every read of `vector`. Use it to + * hand an already-materialized vector straight to an output vector without a per-row copy. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The int64_t to wrap. - * @param out_value Receives the new BIGINT value. + * @param vector The vector to repoint at the source's data. + * @param source The vector whose data to reference. Its logical type must equal the vector's. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_bigint_with_connection(duckdb_v2_connection_handle conn, - int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_reference(duckdb_v2_vector_handle vector, duckdb_v2_vector_handle source, + duckdb_v2_error_info_handle *err); /*! - * Creates a HUGEINT value from an int128_t. + * Turns the vector into a CONSTANT vector holding the given value. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * Afterwards the vector holds a single element that applies to every logical row; write it through + * vector_get_data_mutable, which returns a pointer to that one element. For a STRUCT vector the change propagates to + * every child. The value's logical type must equal the vector's; a mismatch returns ERROR_INPUT_INVALID. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The int128_t to wrap. - * @param out_value Receives the new HUGEINT value. + * @param vector The vector to make constant. + * @param value The value every row takes. Its logical type must equal the vector's. + * @param count The number of elements in the vector. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_hugeint_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_hugeint_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_make_constant(duckdb_v2_vector_handle vector, + duckdb_v2_value_handle value, idx_t count, + duckdb_v2_error_info_handle *err); /*! - * Creates a VARCHAR value from a UTF-8 string. + * Sets a vector to a SEQUENCE representation. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * Afterwards the vector represents the arithmetic sequence start, start+increment, start+2*increment, ... over count + * elements. No data pointer is involved; the three parameters describe the sequence completely. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The UTF-8 string to wrap. May be null only when len is 0 (empty string). - * @param out_value Receives the new VARCHAR value. + * @param vector The vector to turn into a sequence. + * @param start The first value of the sequence. + * @param increment The step between consecutive values. + * @param count The number of elements in the sequence. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_varchar_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_str in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_make_sequence(duckdb_v2_vector_handle vector, int64_t start, + int64_t increment, idx_t count, + duckdb_v2_error_info_handle *err); /*! - * Creates a BLOB value from a byte string. + * Sets one row of a FLAT vector to NULL, nested children included. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * Clears the row's validity and broadcasts the NULL recursively to every descendant element under that row — STRUCT + * fields, and ARRAY elements strided by the array size — maintaining DuckDB's nested NULL invariant: when a STRUCT or + * ARRAY row is NULL, every descendant element under it must be NULL too. LIST children are left untouched, since their + * consumers gate on the list's own validity. Which kinds propagate is normative DuckDB behavior that evolves with the + * type system, which makes this the total NULL write path: equivalent to vector_set_value with a NULL value, without + * the per-call value allocation. + * + * Requires a FLAT vector: constant, dictionary, and compressed representations are not row-addressable, so call + * vector_flatten first. The row index is logical and bounds-checked against vector_get_size. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The byte string to wrap. May be null only when len is 0 (empty string). - * @param out_value Receives the new BLOB value. + * @param vector The vector to write to. + * @param row The logical row index, in [0, vector_get_size). * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_blob_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_str in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_set_null(duckdb_v2_vector_handle vector, idx_t row, + duckdb_v2_error_info_handle *err); /*! - * Creates a FLOAT value from a float. + * Returns a mutable pointer to the validity mask of a FLAT vector. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * Allocates the mask on first use, so the returned pointer is always non-null. Each uint64_t word covers 64 rows: bit N + * of word W is row W*64+N. A set bit means valid; a cleared bit means NULL. + * + * A raw mask write marks only this vector's rows. Clearing a STRUCT or ARRAY parent bit leaves the descendant elements + * untouched, which violates DuckDB's nested NULL invariant — a NULL STRUCT or ARRAY row requires NULL descendants — so + * set nested rows NULL through vector_set_null instead. When writing nested masks raw anyway, invalidate the child + * masks up front and mark elements valid as the values are written. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The float to wrap. - * @param out_value Receives the new FLOAT value. + * @param vector The vector. + * @param out_validity Receives a mutable pointer to the validity mask. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_float_with_connection(duckdb_v2_connection_handle conn, - float in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_flat_get_validity_mutable(duckdb_v2_vector_handle vector, + uint64_t **out_validity, + duckdb_v2_error_info_handle *err); /*! - * Creates a DOUBLE value from a double. + * Sets the validity of a CONSTANT vector. * - * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * A constant vector holds a single element, so one flag decides whether every row reads as the value or as NULL. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The double to wrap. - * @param out_value Receives the new DOUBLE value. + * @param vector The constant vector. + * @param validity True to mark the vector's single element valid, false to mark it NULL. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_double_with_connection(duckdb_v2_connection_handle conn, - double in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_constant_set_valid(duckdb_v2_vector_handle vector, bool validity, + duckdb_v2_error_info_handle *err); /*! - * Creates a TYPE value wrapping the given logical type. + * Borrows a string-backed vector's arena for writing. * - * The input logical type is borrowed; the value internally copies the type so the caller can destroy the logical type - * independently. + * Allocates the arena on first use. Valid for VARCHAR, BLOB, BIT, and BIGNUM vectors, and returns ERROR_INPUT_INVALID + * for anything else. This call is the single string-ness check, which is why arena_allocate itself performs none. The + * arena is borrowed — never destroy it — and stays valid until the vector is flattened, reallocated, or destroyed. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param type The borrowed logical type to wrap. - * @param out_value Receives the new value. + * @param vector The string-backed vector whose arena to borrow. + * @param out_arena Receives the borrowed arena handle. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_type_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_logical_type_handle type, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_get_arena(duckdb_v2_vector_handle vector, + duckdb_v2_arena_handle *out_arena, + duckdb_v2_error_info_handle *err); /*! - * Creates a DATE value. + * Returns the number of child vectors a nested vector exposes. * - * The payload is days since 1970-01-01, the same unit value_get_date reports. It is not range-checked here; value_cast - * is the validating path. The returned value is caller-owned; destroy it via value_destroy. + * 1 for LIST, MAP and ARRAY; the field count for STRUCT; member_count + 1 for UNION, the tag plus the members; and 0 + * for any non-nested kind. vector_get_child documents what each index addresses. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new DATE value. + * @param vector The vector. + * @param out_count Receives the number of child vectors. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_date_with_context(duckdb_v2_context_handle ctx, int32_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_get_child_count(duckdb_v2_vector_handle vector, idx_t *out_count, + duckdb_v2_error_info_handle *err); /*! - * Creates a DATE value. + * Borrows a child vector by index. * - * The payload is days since 1970-01-01, the same unit value_get_date reports. It is not range-checked here; value_cast - * is the validating path. The returned value is caller-owned; destroy it via value_destroy. + * What each index addresses, by kind: LIST and ARRAY expose [0] = the elements; MAP [0] = the entries, a STRUCT(key, + * value) whose children are the keys and the values; STRUCT and TUPLE [i] = field i; and UNION [0] = the tag with + * [1..N] = the members. Note that value_get_child diverges on UNION, exposing only the active member. + * + * The returned child is borrowed and lives as long as the owning chunk. Returns ERROR_INPUT_INVALID if the vector has + * no children, or if the index is out of range. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new DATE value. + * @param vector The nested vector to descend into. + * @param index The child index, in [0, vector_get_child_count). + * @param out_child Receives the borrowed child vector. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_date_with_connection(duckdb_v2_connection_handle conn, - int32_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_vector_get_child(duckdb_v2_vector_handle vector, idx_t index, + duckdb_v2_vector_handle *out_child, + duckdb_v2_error_info_handle *err); /*! - * Creates a TIME value. + * Decodes BIGNUM storage bytes into a caller-supplied magnitude buffer + sign flag. * - * The payload is microseconds since midnight, the same unit value_get_time reports. It is not range-checked here; - * value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. + * in_data and in_length are the value's raw storage bytes, exactly as they sit in a vector's bignum payload or as + * value_get_blob reports them for a BIGNUM value. Those bytes are opaque: the storage encoding is not a committed part + * of this API, and this function is the only thing that interprets them. * - * history: - * - stable: v2.0.0 + * Writes the big-endian magnitude bytes into out_data and the sign flag into out_is_negative; reconstruct the integer + * as (-1)**out_is_negative * unsigned_big_endian(out_data[0..out_length]). Materializing the bytes is unavoidable, + * since a negative bignum stores its magnitude bit-inverted, but the buffer belongs to the caller: this function never + * allocates and hands back nothing to free. * - * @param ctx The context to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new TIME value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_time_with_context(duckdb_v2_context_handle ctx, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - -/*! - * Creates a TIME value. + * Pass out_data = NULL to size the buffer without decoding. out_length then receives the exact number of magnitude + * bytes and out_is_negative the sign, and out_capacity is ignored. The size comes straight out of the storage header, + * so the query is O(1) and exact — a size call followed by a decode call never needs a retry loop. * - * The payload is microseconds since midnight, the same unit value_get_time reports. It is not range-checked here; - * value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. + * With out_data != NULL, out_capacity must be at least out_length bytes, or the call returns ERROR_INPUT_OBJECT_SIZE + * with out_length set to the required size and out_data left untouched. On success exactly out_length bytes are + * written, and the rest of the buffer is not modified. + * + * Returns ERROR_INPUT_INVALID if in_data is NULL, or if in_length is too short to hold a bignum header. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new TIME value. + * @param in_data The value's raw BIGNUM storage bytes. + * @param in_length Size of in_data in bytes. + * @param out_data Caller-owned buffer receiving the magnitude bytes, or NULL to only report the required size in + * out_length. + * @param out_capacity Bytes available in out_data. Ignored when out_data is NULL. + * @param out_length Receives the magnitude size in bytes, written on success and on ERROR_INPUT_OBJECT_SIZE. + * @param out_is_negative Receives true if the value is negative, false otherwise. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_time_with_connection(duckdb_v2_connection_handle conn, - int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_bignum_decode(const uint8_t *in_data, idx_t in_length, uint8_t *out_data, + idx_t out_capacity, idx_t *out_length, bool *out_is_negative, + duckdb_v2_error_info_handle *err); /*! - * Creates a TIME_NS value. + * Encodes a magnitude + sign flag into BIGNUM storage bytes. * - * The payload is nanoseconds since midnight, the same unit value_get_time_ns reports. It is not range-checked here; - * value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. + * The inverse of bignum_decode. in_data and in_length are the big-endian magnitude bytes and is_negative the sign; + * out_data receives the raw storage bytes for that integer, in the form a BIGNUM vector payload expects. Those bytes + * are opaque: the storage encoding is not a committed part of this API. * - * history: - * - stable: v2.0.0 + * Pass out_data = NULL to size the buffer without encoding. out_length then receives the exact number of storage bytes, + * and out_capacity is ignored. The size is a fixed function of in_length, so the query is O(1) and exact. * - * @param ctx The context to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new TIME_NS value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_time_ns_with_context(duckdb_v2_context_handle ctx, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - -/*! - * Creates a TIME_NS value. + * With out_data != NULL, out_capacity must be at least out_length bytes, or the call returns ERROR_INPUT_OBJECT_SIZE + * with out_length set to the required size and out_data left untouched. On success exactly out_length bytes are + * written, and the rest of the buffer is not modified. * - * The payload is nanoseconds since midnight, the same unit value_get_time_ns reports. It is not range-checked here; - * value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. + * The magnitude must be canonical, matching what bignum_decode produces: in_length >= 1 with no leading zero bytes, so + * the value zero is the single byte 0x00 with is_negative = false. A NULL in_data, an in_length of 0, a leading zero + * byte, or a negative zero returns ERROR_INPUT_INVALID; a magnitude beyond the maximum bignum width returns + * ERROR_INPUT_OUT_OF_RANGE. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new TIME_NS value. + * @param in_data Big-endian magnitude bytes, with no leading zero bytes. + * @param in_length Size of in_data in bytes. Must be >= 1. + * @param is_negative True to encode a negative value. + * @param out_data Caller-owned buffer receiving the storage bytes, or NULL to only report the required size in + * out_length. + * @param out_capacity Bytes available in out_data. Ignored when out_data is NULL. + * @param out_length Receives the storage size in bytes — written on success and on ERROR_INPUT_OBJECT_SIZE. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_time_ns_with_connection(duckdb_v2_connection_handle conn, - int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_bignum_encode(const uint8_t *in_data, idx_t in_length, bool is_negative, + uint8_t *out_data, idx_t out_capacity, idx_t *out_length, + duckdb_v2_error_info_handle *err); /*! - * Creates a TIME_TZ value. + * Validates all text->len bytes as UTF-8, including bytes after embedded NUL characters. * - * The payload packs the time of day and the UTC offset into one integer, in the committed layout: micros in the high 40 - * bits, a biased offset in the low 24. The same form value_get_time_tz reports. It is not range-checked here; - * value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. + * Returns ERROR_INPUT_INVALID if any of: + * - text is NULL. + * - text->ptr is NULL and text->len is nonzero. + * - The input contains malformed UTF-8. + * + * A view with a NULL ptr and zero length is valid. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new TIME_TZ value. + * @param text The bytes to validate. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_time_tz_with_context(duckdb_v2_context_handle ctx, - uint64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_validate_utf8(const duckdb_v2_str *text, duckdb_v2_error_info_handle *err); + +/* --- Struct definitions for vector --- */ + +struct duckdb_v2_vector_view { + const void *data; + const uint64_t *validity; + const duckdb_v2_sel_t *sel; + idx_t count; +}; + +struct duckdb_v2_list_entry { + idx_t offset; + idx_t length; +}; + +struct duckdb_v2_hugeint_t { + uint64_t lower; + int64_t upper; +}; + +struct duckdb_v2_uhugeint_t { + uint64_t lower; + uint64_t upper; +}; + +struct duckdb_v2_interval_t { + int32_t months; + int32_t days; + int64_t micros; +}; + +/* ============================================================================ + * MODULE: prepared_statement + * ============================================================================ */ + +/* --- Enums for prepared_statement --- */ + +/* --- Struct forward declarations for prepared_statement --- */ + +/* --- Types for prepared_statement --- */ /*! - * Creates a TIME_TZ value. - * - * The payload packs the time of day and the UTC offset into one integer, in the committed layout: micros in the high 40 - * bits, a biased offset in the low 24. The same form value_get_time_tz reports. It is not range-checked here; - * value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. - * - * history: - * - stable: v2.0.0 - * - * @param conn The connection to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new TIME_TZ value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR + * An owned handle to a statement bound and planned once, executable repeatedly via + * `duckdb_v2_prepared_statement_execute()`. Construct with `duckdb_v2_prepared_statement_create()` and destroy with + * `duckdb_v2_prepared_statement_destroy()`. It keeps its connection's session alive, so it stays usable across + * executions and even after the connection is disconnected. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_time_tz_with_connection(duckdb_v2_connection_handle conn, - uint64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_prepared_statement { + void *internal_ptr; +} * duckdb_v2_prepared_statement_handle; + +/* --- Constants for prepared_statement --- */ + +/* --- Function pointer typedefs for prepared_statement --- */ + +/* --- Functions for prepared_statement --- */ /*! - * Creates a TIMESTAMP value. + * Prepares a parsed statement into a reusable handle. Non-consuming. * - * The payload is microseconds since 1970-01-01, the same unit value_get_timestamp reports. It is not range-checked - * here; value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. + * Copies the statement's AST, then binds and plans it once. Binder and catalog errors surface here, exactly as they + * would from `duckdb_v2_statement_execute()`. The statement is borrowed rather than consumed, since a copy is what gets + * prepared, so it can be prepared again or executed directly; the caller destroys it with + * `duckdb_v2_sql_statement_destroy()`. * - * history: - * - stable: v2.0.0 + * By default this succeeds for any preparable statement, whether or not its plan will be reused; ask + * `duckdb_v2_prepared_statement_reuses_plan()` which one you got. Setting `require_cacheable` instead fails with + * `ERROR_INPUT_INVALID` when the plan would not be reused, so a caller who wants the handle only for the speedup finds + * out here rather than after silently taking the slow path. * - * @param ctx The context to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new TIMESTAMP value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_with_context(duckdb_v2_context_handle ctx, - int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - -/*! - * Creates a TIMESTAMP value. + * A statement that DuckDB expands into several, such as a dynamic PIVOT, is kept as written and expanded again at each + * execution, in the transaction state of that execution. It runs nothing here, its plan is never reused, and its + * result's metadata is deferred as for `duckdb_v2_statement_execute()`. Parameters on such a statement are refused with + * `ERROR_INPUT_INVALID`. * - * The payload is microseconds since 1970-01-01, the same unit value_get_timestamp reports. It is not range-checked - * here; value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. + * Refuses with `ERROR_RESOURCE_IN_USE` while a statement on the connection is still running. Drain, destroy, or + * interrupt that result first, or prepare on another connection. `*out_prepared` is set to NULL on failure. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new TIMESTAMP value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param conn The connection supplying the catalog, transaction, and parser state. The prepared statement belongs to + * it. + * @param statement The statement to prepare. Borrowed and copied, not consumed; destroy it with + * `duckdb_v2_sql_statement_destroy()`. + * @param require_cacheable When true, fail with `ERROR_INPUT_INVALID` unless the prepared plan will be reused across + * executions, as `duckdb_v2_prepared_statement_reuses_plan()` would report it. + * @param out_prepared On success, receives the new prepared statement. Owned by the caller; destroy via + * `duckdb_v2_prepared_statement_destroy()`. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_with_connection(duckdb_v2_connection_handle conn, - int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_prepared_statement_create(duckdb_v2_connection_handle conn, + duckdb_v2_sql_statement_handle statement, + bool require_cacheable, + duckdb_v2_prepared_statement_handle *out_prepared, + duckdb_v2_error_info_handle *err); /*! - * Creates a TIMESTAMP_SEC value. + * Executes a prepared statement, streaming its result. Non-consuming. * - * The payload is seconds since 1970-01-01, the same unit value_get_timestamp_sec reports. It is not range-checked here; - * value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. + * Returns a result without executing anything: execution happens incrementally as the result is stepped or drained, + * exactly as with `duckdb_v2_statement_execute()`. The handle returned is an ordinary result, with identical behaviour + * throughout -- streaming, draining, the changed-row count of a DML statement, the output schema, the statement type, + * and the result type. + * + * `parameter_values` binds the statement's parameters as constants for this execution. Binding is positional by default + * ($1 = element 0); supply `parameter_names` to bind by name instead, where a non-empty entry binds its value to that + * named parameter ($name, matched case-insensitively) and a {NULL, 0} entry stays positional. Pass NULL for both + * arrays, or a count of 0, for a statement without parameters. Both arrays are borrowed and copied in, so the caller + * still owns and destroys them. A key set that does not match the statement's parameters is rejected with + * `ERROR_INPUT_INVALID`, with one exception: a named parameter left without a value reads the session variable of the + * same name (`SET VARIABLE`) when one exists. + * + * Not consumed: execute the same handle again, with the same values or different ones, as often as you like. Values are + * bound per execution and nothing carries over between them. A catalog change since the statement was prepared, or a + * parameter type that differs from the one the cached plan assumed, triggers a re-bind that is invisible apart from its + * cost. + * + * Refuses with `ERROR_RESOURCE_IN_USE` while a statement on the connection is still running; drain, destroy, or + * interrupt that result first, or execute on another connection. A failed execution, at any stage, leaves the prepared + * statement usable. `*out_result` is set to NULL on failure. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new TIMESTAMP_SEC value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param prepared The prepared statement to execute. Borrowed; not consumed. + * @param parameter_names Optional. An array of `parameter_count` parameter names; a non-empty entry binds its value to + * the named parameter ($name, case-insensitive), a {NULL, 0} entry keeps it positional ($1 = element 0). Pass NULL to + * bind everything positionally. + * @param parameter_values Optional. An array of `parameter_count` values. Each binds by name when `parameter_names` + * supplies one, and positionally ($1 = element 0) otherwise. Borrowed and copied in. Pass NULL for a statement without + * parameters. + * @param parameter_count The number of entries in `parameter_names` and `parameter_values`. Pass 0 for a statement + * without parameters. + * @param out_result On success, receives the new result. Owned by the caller; destroy via `duckdb_v2_result_destroy()`. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_sec_with_context(duckdb_v2_context_handle ctx, - int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_prepared_statement_execute(duckdb_v2_prepared_statement_handle prepared, + const duckdb_v2_identifier_t *parameter_names, + const duckdb_v2_value_handle *parameter_values, + idx_t parameter_count, + duckdb_v2_result_handle *out_result, + duckdb_v2_error_info_handle *err); /*! - * Creates a TIMESTAMP_SEC value. + * Reports whether the prepared statement reuses its compiled plan across executions. * - * The payload is seconds since 1970-01-01, the same unit value_get_timestamp_sec reports. It is not range-checked here; - * value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. + * True when executions reuse the plan built at prepare time, provided the supplied values match the planned parameter + * types and nothing the plan depends on has changed; false when the statement re-binds on every execution, making it no + * faster than `duckdb_v2_statement_execute()`. A plan is reused only when all parameter types were resolved at prepare + * time and the plan is cacheable: `SELECT 42` reuses, `SELECT $1::INTEGER + 1` reuses, a statement reading a base table + * does not (it re-binds so a catalog change is picked up), and `SELECT $1 + $2` does not (the types are unknown until + * values arrive). + * + * A static property of the built plan, fixed when the statement was prepared and independent of the values later passed + * to `duckdb_v2_prepared_statement_execute()`. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new TIMESTAMP_SEC value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param prepared The prepared statement to inspect. + * @param out_reuses Receives true when the compiled plan is reused across executions, false when the statement re-binds + * each time. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_sec_with_connection(duckdb_v2_connection_handle conn, - int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_prepared_statement_reuses_plan(duckdb_v2_prepared_statement_handle prepared, + bool *out_reuses, + duckdb_v2_error_info_handle *err); /*! - * Creates a TIMESTAMP_MS value. + * Destroys a prepared statement. * - * The payload is milliseconds since 1970-01-01, the same unit value_get_timestamp_ms reports. It is not range-checked - * here; value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. + * Null-safe: passing NULL, or a slot already set to NULL, is a no-op. A result produced by + * `duckdb_v2_prepared_statement_execute()` is independently owned and keeps the session alive itself, so the prepared + * statement may be destroyed while results made from it are still live. On success the slot is set to NULL. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new TIMESTAMP_MS value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param prepared The prepared statement to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_ms_with_context(duckdb_v2_context_handle ctx, - int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_prepared_statement_destroy(duckdb_v2_prepared_statement_handle *prepared); + +/* --- Struct definitions for prepared_statement --- */ + +/* ============================================================================ + * MODULE: query_result + * ============================================================================ */ + +/* --- Enums for query_result --- */ /*! - * Creates a TIMESTAMP_MS value. + * Shape of a query result. QUERY_RESULT carries rows and columns; CHANGED_ROWS carries an affected row count, as an + * INSERT, UPDATE, or DELETE without RETURNING produces; NOTHING covers DDL and other statements with no row output. + */ +typedef enum DUCKDB_V2_RESULT_TYPE { + DUCKDB_V2_RESULT_TYPE_QUERY_RESULT = 0, + DUCKDB_V2_RESULT_TYPE_CHANGED_ROWS = 1, + DUCKDB_V2_RESULT_TYPE_NOTHING = 2, + DUCKDB_V2_RESULT_TYPE_MAX_ENUM = 0x7FFFFFFF, +} DUCKDB_V2_RESULT_TYPE; + +/*! + * Outcome of a result_step call. WAITING is the 0-value, so a zero-initialized out-param reads as "no work product yet" + * rather than CHUNK, the same convention VECTOR_TYPE_OTHER follows. The four states are the ones a consumer acts on; + * they are deliberately not a projection of any internal enum. + */ +typedef enum DUCKDB_V2_RESULT_STEP_STATUS { + //! No chunk yet; step again, or block in result_wait. + DUCKDB_V2_RESULT_STEP_STATUS_WAITING = 0, + + //! A caller-owned chunk was written to *out_chunk, or an array to *out_array. + DUCKDB_V2_RESULT_STEP_STATUS_CHUNK = 1, + + //! Stream exhausted. Sticky. + DUCKDB_V2_RESULT_STEP_STATUS_FINISHED = 2, + + //! Query was interrupted. Sticky. + DUCKDB_V2_RESULT_STEP_STATUS_CANCELLED = 3, + DUCKDB_V2_RESULT_STEP_STATUS_MAX_ENUM = 0x7FFFFFFF, +} DUCKDB_V2_RESULT_STEP_STATUS; + +/* --- Struct forward declarations for query_result --- */ + +/* --- Types for query_result --- */ + +/* --- Constants for query_result --- */ + +/* --- Function pointer typedefs for query_result --- */ + +/* --- Functions for query_result --- */ + +/*! + * Executes a parsed statement on the connection, streaming its result. Non-consuming. * - * The payload is milliseconds since 1970-01-01, the same unit value_get_timestamp_ms reports. It is not range-checked - * here; value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. + * Takes a statement produced by the sql_statement module (parse_sql / statement_iterator_next), preprocesses and + * prepares it, and returns a result handle without executing anything: execution happens incrementally as the result is + * stepped (result_step) or drained (result_fetch_chunk). This call reports only the errors detectable at prepare time — + * binder, catalog, pragma preprocessing — while errors raised during execution surface from the stepping functions. + * + * The statement is borrowed, not consumed, since a copy is what executes. It can be executed again, for example with a + * different set of values, and the caller destroys it with sql_statement_destroy. + * + * parameter_values binds the statement's parameters as constants for this execution. Binding is positional by default: + * the i-th value binds $(i+1), matching SQL's own convention, so dense $1..$N and ? placeholders work directly. Supply + * parameter_names to bind by name instead — a non-empty name binds its value to that named parameter ($name, matched + * case-insensitively), while a {NULL, 0} entry leaves that value positional. The parameter schema from statement_bind + * lists the names to use. Both arrays are borrowed and copied in; the caller still owns and destroys them. Pass NULL + * for both, or a count of 0, for an unparameterized statement. A key set that does not match the statement's parameters + * — names for a positional statement, or the reverse — is rejected with ERROR_INPUT_INVALID, and named and positional + * parameters cannot be mixed within one statement. Parameters and statement expansion are mutually exclusive: passing + * values for a statement that preprocesses into a group is rejected with ERROR_INPUT_INVALID. + * + * DuckDB can expand one statement into a group: a dynamic PIVOT, or statement-expanding DDL such as ALTER ... ADD + * COLUMN with a non-constant DEFAULT. The group executes as one result through the same steps, and the stream surfaces + * the rows of the first row-producing statement of the group. A row-producing statement followed by another cannot be + * served as a single result and reports ERROR_QUERY_NOT_IMPLEMENTED; no known expansion produces one. + * + * One running statement per connection: this refuses with ERROR_RESOURCE_IN_USE while a statement on the connection is + * still running. Drain, destroy, or interrupt that result first, or open another connection. + * + * Schema metadata — result type, statement type, column count, names, logical types — is available on the returned + * handle immediately, before the first step. + * + * *out_result is set to nullptr on failure. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new TIMESTAMP_MS value. + * @param conn The connection on which to execute the statement. + * @param statement The statement to execute. Borrowed and copied; not consumed. Destroy it with sql_statement_destroy. + * @param parameter_names Optional. An array of parameter_count parameter names; a non-empty entry binds its value to + * the named parameter ($name, case-insensitive), a {NULL, 0} entry keeps it positional ($1 = element 0). Pass NULL for + * all-positional binding. Named and positional parameters cannot be mixed within one statement. + * @param parameter_values Optional. An array of parameter_count value handles. Each binds by name when parameter_names + * supplies one, otherwise positionally ($1 = element 0). Borrowed (copied in). Pass NULL for an unparameterized + * statement. + * @param parameter_count The number of parameters in parameter_names and parameter_values. Pass 0 for an + * unparameterized statement. + * @param out_result Receives the new result handle. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_ms_with_connection(duckdb_v2_connection_handle conn, - int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_statement_execute(duckdb_v2_connection_handle conn, + duckdb_v2_sql_statement_handle statement, + const duckdb_v2_identifier_t *parameter_names, + const duckdb_v2_value_handle *parameter_values, + idx_t parameter_count, duckdb_v2_result_handle *out_result, + duckdb_v2_error_info_handle *err); /*! - * Creates a TIMESTAMP_NS value. + * Destroys a result handle. * - * The payload is nanoseconds since 1970-01-01, the same unit value_get_timestamp_ns reports. It is not range-checked - * here; value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. + * Null-safe: passing nullptr or a slot already set to nullptr is a no-op. Frees the memory the result owns and releases + * the connection for its next query. Safe at any point in the stream's life. Destroying a result before its statement + * ended (drained, or stepped to FINISHED) aborts the statement. On autocommit, its writes are rolled back. Inside a + * transaction, a statement that may write invalidates the transaction, and a read-only statement just stops. Chunks + * already fetched are caller-owned and stay valid. On success the slot is set to nullptr. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new TIMESTAMP_NS value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param result The result to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_ns_with_context(duckdb_v2_context_handle ctx, - int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_destroy(duckdb_v2_result_handle *result); /*! - * Creates a TIMESTAMP_NS value. + * Runs one bounded unit of query execution and returns without blocking. * - * The payload is nanoseconds since 1970-01-01, the same unit value_get_timestamp_ns reports. It is not range-checked - * here; value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. + * The streaming primitive: a step does a bounded amount of execution work, mostly without blocking, and returns control + * to the caller, so an event loop stays responsive and can interleave other work between steps. The conveniences — + * result_wait, result_fetch_chunk, result_drain — block, and are for synchronous callers. out_status reports the + * outcome: + * + * - CHUNK: *out_chunk receives a caller-owned chunk (destroy via data_chunk_destroy). Written only for this status; + * nullptr otherwise. + * - WAITING: no chunk yet, but work was done. Transient: keep stepping and it resolves to CHUNK, FINISHED, CANCELLED, + * or an error. Block in result_wait rather than busy-stepping. + * - FINISHED: stream exhausted. Sticky. + * - CANCELLED: query interrupted via connection_interrupt. Sticky. Cancellation is a status here, not an error; + * result_fetch_chunk, which has no status out-param, reports it as ERROR_RUNTIME_INTERRUPT. + * + * Execution errors come back as the return code plus err, never as a status; out_status is then unspecified and + * *out_chunk is nullptr. Errors are sticky, so later steps report the same code. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new TIMESTAMP_NS value. + * @param result The result to step. + * @param out_chunk Receives an owned chunk if *out_status is CHUNK; set to nullptr otherwise. + * @param out_status Receives the step status. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_ns_with_connection(duckdb_v2_connection_handle conn, - int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_step(duckdb_v2_result_handle result, + duckdb_v2_data_chunk_handle *out_chunk, + DUCKDB_V2_RESULT_STEP_STATUS *out_status, + duckdb_v2_error_info_handle *err); /*! - * Creates a TIMESTAMP_TZ value. + * Blocks until the next chunk is available and returns it. * - * The payload is microseconds since 1970-01-01, in UTC, the same unit value_get_timestamp_tz reports. It is not - * range-checked here; value_cast is the validating path. The returned value is caller-owned; destroy it via - * value_destroy. + * A convenience over result_step: blocks until a chunk is produced or the stream ends. On success *out_chunk receives a + * caller-owned chunk (destroy via data_chunk_destroy), or nullptr at end-of-stream. End-of-stream is sticky, so later + * calls keep succeeding with *out_chunk set to nullptr. + * + * An interrupted query returns ERROR_RUNTIME_INTERRUPT — the same event result_step reports as status CANCELLED, + * carried on the error channel because this function has no status out-param. + * + * On failure *out_chunk is set to nullptr. Errors are sticky, so later calls report the same code. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new TIMESTAMP_TZ value. + * @param result The result to fetch from. + * @param out_chunk Receives an owned chunk, or nullptr at end-of-stream. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_tz_with_context(duckdb_v2_context_handle ctx, - int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_fetch_chunk(duckdb_v2_result_handle result, + duckdb_v2_data_chunk_handle *out_chunk, + duckdb_v2_error_info_handle *err); /*! - * Creates a TIMESTAMP_TZ value. - * - * The payload is microseconds since 1970-01-01, in UTC, the same unit value_get_timestamp_tz reports. It is not - * range-checked here; value_cast is the validating path. The returned value is caller-owned; destroy it via - * value_destroy. + * Blocks until result_step can make progress. + * + * A convenience over result_step: blocks until a step is worth issuing again, that is, until a unit of execution work + * can run on the calling thread. Never produces or consumes chunks. Waiting on a terminal result — FINISHED, CANCELLED, + * or a sticky error — returns immediately; it is a no-op, never an error. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new TIMESTAMP_TZ value. + * @param result The result to wait on. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_tz_with_connection(duckdb_v2_connection_handle conn, - int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_wait(duckdb_v2_result_handle result, duckdb_v2_error_info_handle *err); /*! - * Creates a TIMESTAMP_TZ_NS value. + * Renders the result as a box table, consuming it. * - * The payload is nanoseconds since 1970-01-01, in UTC, the same unit value_get_timestamp_tz_ns reports. It is not - * range-checked here; value_cast is the validating path. The returned value is caller-owned; destroy it via - * value_destroy. + * Drains the result into a column data collection and renders it with the same renderer the DuckDB CLI uses, so every + * client displays results identically without reimplementing table formatting. The result is consumed by transfer: the + * slot is set to NULL on success and on failure alike. A partially consumed result is accepted, and the remainder is + * what gets rendered. * - * history: - * - stable: v2.0.0 + * The whole remaining result materializes in memory before rendering. max_rows bounds what is DISPLAYED, not what is + * read, so with limit 0 the footer's row count is exact. A caller who cannot afford full materialization should bound + * the query itself (e.g. LIMIT n) and pass n as limit; the footer then renders "? rows" whenever the result fills that + * bound, since the true total is unknown at that point. * - * @param ctx The context to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new TIMESTAMP_TZ_NS value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR - */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_tz_ns_with_context(duckdb_v2_context_handle ctx, - int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); - -/*! - * Creates a TIMESTAMP_TZ_NS value. + * Zero selects the renderer default for each sizing knob: max_rows 20, max_width the probed terminal width or 80 when + * that is unavailable, max_col_width 20. An empty null_value renders NULL cells as the default "NULL" text. * - * The payload is nanoseconds since 1970-01-01, in UTC, the same unit value_get_timestamp_tz_ns reports. It is not - * range-checked here; value_cast is the validating path. The returned value is caller-owned; destroy it via - * value_destroy. + * The rendered text goes to sink rather than being returned: a box can be large, and this way nothing allocates a + * buffer the caller has to free. The sink is called exactly once with the whole box, so its view carries the full + * length — write it straight to a stream, or copy it into your own string. sink must not be NULL. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new TIMESTAMP_TZ_NS value. + * @param result The result to render; consumed and set to NULL. Left intact only when the call rejects null arguments. + * @param max_rows Maximum rows displayed; 0 selects the renderer default (20). + * @param max_width Maximum total width in characters; 0 selects the renderer default. + * @param max_col_width Maximum width of one column; 0 selects the renderer default (20). + * @param null_value Text rendered for NULL cells; empty selects "NULL". + * @param render_mode 0 renders rows (records down the page); 1 renders columns; other values are rejected with + * INVALID_INPUT. + * @param limit The row limit the caller applied to the query before rendering; 0 means none. When the materialized + * result holds exactly this many rows the true total is unknown, so the footer renders "? rows" instead of an exact + * count. + * @param sink Receives the whole rendered box in a single call. Borrowed for the duration of that call; see text_sink + * for the full contract. + * @param user_data Opaque pointer passed through to sink untouched. May be NULL. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_tz_ns_with_connection(duckdb_v2_connection_handle conn, - int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_render_box(duckdb_v2_result_handle *result, idx_t max_rows, + idx_t max_width, idx_t max_col_width, + const duckdb_v2_str *null_value, idx_t render_mode, + idx_t limit, duckdb_v2_text_sink_fn sink, void *user_data, + duckdb_v2_error_info_handle *err); /*! - * Creates a INTERVAL value. + * Runs the result to completion and reports the changed-row count. * - * The payload is the (months, days, micros) triple, the same unit value_get_interval reports. It is not range-checked - * here; value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. + * A convenience over result_step: blocks until the stream is fully consumed, so every side effect is applied. Rows of a + * row-producing result are discarded. For a CHANGED_ROWS result *out_rows_changed receives the affected row count; for + * every other result type, and for a stream whose Count chunk was already consumed, it receives 0. + * + * The result type (result_get_result_type) is prepare-time metadata, so a caller can choose between consuming rows and + * draining without inspecting the SQL. Draining an already FINISHED result succeeds. Cancellation surfaces as + * ERROR_RUNTIME_INTERRUPT; errors are sticky, and on failure *out_rows_changed is unspecified. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new INTERVAL value. + * @param result The result to drain. + * @param out_rows_changed Receives the affected row count for CHANGED_ROWS results, 0 otherwise. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_interval_with_context(duckdb_v2_context_handle ctx, - duckdb_v2_interval_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_drain(duckdb_v2_result_handle result, idx_t *out_rows_changed, + duckdb_v2_error_info_handle *err); /*! - * Creates a INTERVAL value. + * Returns the shape of the result: query, changed rows, or nothing. * - * The payload is the (months, days, micros) triple, the same unit value_get_interval reports. It is not range-checked - * here; value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. + * QUERY_RESULT for statements that produce rows (SELECT, RETURNING, EXPLAIN), CHANGED_ROWS for an INSERT, UPDATE, or + * DELETE without RETURNING, NOTHING for DDL and other statements with no row output. + * + * Prepare-time metadata: available from statement_execute on, except for a statement that expands into a group, where + * it fails with ERROR_INPUT_METADATA_DEFERRED until stepping has bound the row-producing statement, or the group ended + * without one. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The payload to wrap. - * @param out_value Receives the new INTERVAL value. + * @param result The result. + * @param out_type Receives the result shape. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_interval_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_interval_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_get_result_type(duckdb_v2_result_handle result, + DUCKDB_V2_RESULT_TYPE *out_type, + duckdb_v2_error_info_handle *err); /*! - * Creates a DECIMAL value from its backing integer. + * Returns the SQL statement type that produced the result. * - * The value is scaled by 10^scale, so (18500, 18, 3) is 18.500. width is the total digit count and must be 1..38; scale - * is the number of digits after the point and must not exceed width. Violating either returns ERROR_INPUT_INVALID, as - * does a value too wide for the storage tier the width selects. The value itself is not range-checked against the - * width; value_cast is the validating path. + * Prepare-time metadata: available from statement_execute on. A statement that expands into a group reports its type as + * written: MULTI, or PRAGMA for a pragma such as IMPORT DATABASE. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param in_value The backing integer, scaled by 10^scale. - * @param width Total digit count, 1..38. - * @param scale Digits after the decimal point; must not exceed width. - * @param out_value Receives the new DECIMAL value. + * @param result The result. + * @param out_type Receives the statement type. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_decimal_with_context(duckdb_v2_context_handle ctx, - duckdb_v2_hugeint_t in_value, uint8_t width, - uint8_t scale, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_get_statement_type(duckdb_v2_result_handle result, + DUCKDB_V2_STATEMENT_TYPE *out_type, + duckdb_v2_error_info_handle *err); /*! - * Creates a DECIMAL value from its backing integer. + * Returns the result's output schema as a single schema handle. * - * The value is scaled by 10^scale, so (18500, 18, 3) is 18.500. width is the total digit count and must be 1..38; scale - * is the number of digits after the point and must not exceed width. Violating either returns ERROR_INPUT_INVALID, as - * does a value too wide for the storage tier the width selects. The value itself is not range-checked against the - * width; value_cast is the validating path. + * Builds an owned schema of the result's column names and types into *out_schema; destroy it via schema_destroy. + * Prepare-time metadata, available before the first step, or for an expanding statement once stepping has bound the + * row-producing statement (before that it fails with ERROR_INPUT_METADATA_DEFERRED). Never empty: a non-SELECT reports + * a single status column, either a BIGINT changed-rows count or a BOOLEAN success. + * + * This schema is the authoritative source for the column types of the chunks the result produces; vectors do not carry + * their own type. + * + * *out_schema is set to NULL on failure. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param in_value The backing integer, scaled by 10^scale. - * @param width Total digit count, 1..38. - * @param scale Digits after the decimal point; must not exceed width. - * @param out_value Receives the new DECIMAL value. + * @param result The result. + * @param out_schema Receives the owned output schema. Destroy via schema_destroy. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_decimal_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_hugeint_t in_value, uint8_t width, - uint8_t scale, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_get_schema(duckdb_v2_result_handle result, + duckdb_v2_schema_handle *out_schema, + duckdb_v2_error_info_handle *err); + +/* --- Struct definitions for query_result --- */ + +/* ============================================================================ + * MODULE: table + * ============================================================================ */ + +/* --- Enums for table --- */ /*! - * Creates a UUID value from its internal 128-bit form. - * - * The payload is the storage form a vector element holds, not the canonical byte order: the high bit is flipped so the - * integer sorts. To build one from canonical text, cast a VARCHAR with value_cast instead, which is also how a UUID - * renders back. - * - * history: - * - stable: v2.0.0 - * - * @param ctx The context to use for the construction. - * @param in_value The internal 128-bit storage form. - * @param out_value Receives the new UUID value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR + * Whether a table function's scan is partitioned by a given set of columns, and how. Reported by + * `duckdb_v2_table_function_set_partitioning_callback()` via + * `duckdb_v2_table_function_partitioning_set_partition_info()`. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_uuid_with_context(duckdb_v2_context_handle ctx, - duckdb_v2_hugeint_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +typedef enum DUCKDB_V2_TABLE_PARTITION_INFO { + //! The scan is not known to be partitioned by the requested columns. + DUCKDB_V2_TABLE_PARTITION_INFO_NOT_PARTITIONED = 0, + + //! Each partition the scan produces carries exactly one distinct value for the requested columns. + DUCKDB_V2_TABLE_PARTITION_INFO_SINGLE_VALUE_PARTITIONS = 1, + + //! The partitions the scan produces overlap only at their boundaries. + DUCKDB_V2_TABLE_PARTITION_INFO_OVERLAPPING_PARTITIONS = 2, + + //! The partitions the scan produces are disjoint ranges. + DUCKDB_V2_TABLE_PARTITION_INFO_DISJOINT_PARTITIONS = 3, + DUCKDB_V2_TABLE_PARTITION_INFO_MAX_ENUM = 0x7FFFFFFF, +} DUCKDB_V2_TABLE_PARTITION_INFO; + +/* --- Struct forward declarations for table --- */ + +/* --- Types for table --- */ /*! - * Creates a UUID value from its internal 128-bit form. - * - * The payload is the storage form a vector element holds, not the canonical byte order: the high bit is flipped so the - * integer sorts. To build one from canonical text, cast a VARCHAR with value_cast instead, which is also how a UUID - * renders back. - * - * history: - * - stable: v2.0.0 - * - * @param conn The connection to use for the construction. - * @param in_value The internal 128-bit storage form. - * @param out_value Receives the new UUID value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR + * An owned opaque handle to a custom table function being built. Created with `duckdb_v2_table_function_create()`, + * configured with the setter functions (e.g. `duckdb_v2_table_function_set_name()`, + * `duckdb_v2_table_function_set_exec_callback()`, etc.) and the signature obtained via + * `duckdb_v2_table_function_get_signature()`, made available with `duckdb_v2_connection_register_table_function()` or + * `duckdb_v2_extension_register_table_function()`, and destroyed with `duckdb_v2_table_function_destroy()`. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_uuid_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_hugeint_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_table_function { + void *internal_ptr; +} * duckdb_v2_table_function_handle; /*! - * Creates a BIT value from its wire bytes. - * - * The wire form is a mandatory padding-header byte — the count of leading bits in the first data byte that are not part - * of the bit string — followed by the data bytes, so the input must be at least 1 byte. These are the same bytes - * value_get_blob reports back. - * - * history: - * - stable: v2.0.0 - * - * @param ctx The context to use for the construction. - * @param in_value The wire bytes, a padding header byte followed by data. Must be at least 1 byte. - * @param out_value Receives the new BIT value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR + * A borrowed opaque handle to the result of a table function's "bind" phase. The "bind" callback receives this handle + * next to a `duckdb_v2_function_bind_info_handle`, which gives access to the arguments, the user data and the bind + * data. It must use this handle to declare the columns the function returns, and can use it to hint at the number of + * rows the scan will produce. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_bit_with_context(duckdb_v2_context_handle ctx, - duckdb_v2_str in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_table_function_bind_info { + void *internal_ptr; +} * duckdb_v2_table_function_bind_info_handle; /*! - * Creates a BIT value from its wire bytes. - * - * The wire form is a mandatory padding-header byte — the count of leading bits in the first data byte that are not part - * of the bit string — followed by the data bytes, so the input must be at least 1 byte. These are the same bytes - * value_get_blob reports back. - * - * history: - * - stable: v2.0.0 - * - * @param conn The connection to use for the construction. - * @param in_value The wire bytes, a padding header byte followed by data. Must be at least 1 byte. - * @param out_value Receives the new BIT value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR + * A borrowed opaque handle to the arguments supplied to a table function during the global state initialization "init + * global" phase. The "init global" callback receives this handle and can use it to e.g. set up the state shared by + * every thread scanning the function, and to declare how many threads may scan it. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_bit_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_str in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_table_function_init_global_info { + void *internal_ptr; +} * duckdb_v2_table_function_init_global_info_handle; /*! - * Creates a BIGNUM value from its storage bytes. - * - * The bytes are opaque storage, as produced by bignum_encode from a magnitude and a sign flag; a negative is stored - * bit-inverted behind a header, so these are not the magnitude bytes. The header plus at least one magnitude byte means - * the input must exceed 3 bytes. These are the same bytes value_get_blob reports back, for bignum_decode to translate. - * - * history: - * - stable: v2.0.0 - * - * @param ctx The context to use for the construction. - * @param in_value The opaque storage bytes from bignum_encode. Must exceed 3 bytes. - * @param out_value Receives the new BIGNUM value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR + * A borrowed opaque handle to the arguments supplied to a table function during the local state initialization "init + * local" phase. The "init local" callback receives this handle and can use it to e.g. set up worker-local state, + * typically by claiming work from the shared global state. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_bignum_with_context(duckdb_v2_context_handle ctx, - duckdb_v2_str in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_table_function_init_local_info { + void *internal_ptr; +} * duckdb_v2_table_function_init_local_info_handle; /*! - * Creates a BIGNUM value from its storage bytes. - * - * The bytes are opaque storage, as produced by bignum_encode from a magnitude and a sign flag; a negative is stored - * bit-inverted behind a header, so these are not the magnitude bytes. The header plus at least one magnitude byte means - * the input must exceed 3 bytes. These are the same bytes value_get_blob reports back, for bignum_decode to translate. - * - * history: - * - stable: v2.0.0 - * - * @param conn The connection to use for the construction. - * @param in_value The opaque storage bytes from bignum_encode. Must exceed 3 bytes. - * @param out_value Receives the new BIGNUM value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR + * A borrowed opaque handle to the arguments supplied to a table function during the execution "exec" phase. The "exec" + * callback receives this handle and can use it to e.g. access the global and local state and write the next batch of + * rows to the output chunk. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_bignum_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_str in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_table_function_exec_info { + void *internal_ptr; +} * duckdb_v2_table_function_exec_info_handle; /*! - * Creates a LIST value from its elements. - * - * The child type is the common type of the elements, resolved by the same rule a SQL list literal follows, and every - * element is cast to it; a set with no common type surfaces the cast error. Pass child_type to name the type instead, - * which is also how an empty list is built: with child_type NULL, an empty element array has no type to resolve and - * returns ERROR_INPUT_INVALID. - * - * The elements are borrowed and copied in, and a NULL element becomes a typed NULL. The type is rebuilt from its child, - * so an alias on the outer LIST type is not preserved; value_cast is the alias-preserving path. - * - * history: - * - stable: v2.0.0 - * - * @param ctx The context to use for the construction. - * @param child_type Optional. The element type. Pass NULL to resolve it from the elements. - * @param children An array of child_count elements. Borrowed (copied in). Pass NULL when child_count is 0. - * @param child_count The number of elements. - * @param out_value Receives the new composite value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. - * @return DUCKDB_V2_ERROR + * A borrowed opaque handle to the arguments supplied to a table function during the "progress" phase. The "progress" + * callback receives this handle and can use it to report how far the scan has advanced. + */ +typedef struct _duckdb_v2_table_function_progress_info { + void *internal_ptr; +} * duckdb_v2_table_function_progress_info_handle; + +/*! + * A borrowed opaque handle to the arguments supplied to a table function during the "filter pushdown" phase of query + * optimization. The "filter pushdown" callback receives this handle and can use it to inspect the filter predicates the + * query applies to the function's rows, and to accept the ones it will apply itself. + */ +typedef struct _duckdb_v2_table_function_filter_pushdown_info { + void *internal_ptr; +} * duckdb_v2_table_function_filter_pushdown_info_handle; + +/*! + * A borrowed opaque handle to the arguments supplied to a table function during the "partition data" phase. The engine + * invokes the callback on the worker thread that just produced a batch of rows, once per batch, only when a downstream + * operator needs to know the batch's ordering position, the values of a set of partitioning columns, or both; the + * handle reports which of those were actually requested and receives the answer. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_list_with_context( - duckdb_v2_context_handle ctx, duckdb_v2_logical_type_handle child_type, const duckdb_v2_value_handle *children, - idx_t child_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_table_function_partition_data_info { + void *internal_ptr; +} * duckdb_v2_table_function_partition_data_info_handle; /*! - * Creates a LIST value from its elements. + * A borrowed opaque handle to the arguments supplied to a table function during the "partitioning" phase of query + * optimization. The engine invokes the callback once per candidate `GROUP BY` column set, before execution starts, to + * decide whether the scan can feed a partitioned aggregate directly instead of hashing. + */ +typedef struct _duckdb_v2_table_function_partitioning_info { + void *internal_ptr; +} * duckdb_v2_table_function_partitioning_info_handle; + +/*! + * A borrowed opaque handle to the arguments supplied to a table function when a scanning thread claims its next batch + * of work. The "claim batch" callback receives this handle and can use it to claim the next part of the scan from the + * shared global state for the thread's local state, and to report whether it claimed one. + */ +typedef struct _duckdb_v2_table_function_claim_batch_info { + void *internal_ptr; +} * duckdb_v2_table_function_claim_batch_info_handle; + +/*! + * A borrowed opaque handle to the arguments supplied to a table function when the engine asks it to describe a bound + * call. The "get bind info" callback receives this handle, and can use it to describe the call in more detail than its + * columns: with identifiers of its columns (e.g. field ids), and with options - key-value facts about what the call + * reads. + */ +typedef struct _duckdb_v2_table_function_get_bind_info_info { + void *internal_ptr; +} * duckdb_v2_table_function_get_bind_info_info_handle; + +/*! + * An owned opaque handle to a multi-file table function being built. A multi-file function wraps an already registered + * table function that reads a single file, and adds everything that is needed to read many files at once on top of it: + * globbing, lists of files, hive partitioning, the `filename` column, `union_by_name` and the like. Created with + * `duckdb_v2_multi_file_function_create()`, configured with the setter functions (e.g. + * `duckdb_v2_multi_file_function_set_name()`, `duckdb_v2_multi_file_function_set_single_file_function()`), made + * available with `duckdb_v2_connection_register_multi_file_function()` or + * `duckdb_v2_extension_register_multi_file_function()`, and destroyed with `duckdb_v2_multi_file_function_destroy()`. + */ +typedef struct _duckdb_v2_multi_file_function { + void *internal_ptr; +} * duckdb_v2_multi_file_function_handle; + +/* --- Constants for table --- */ + +/* --- Function pointer typedefs for table --- */ + +typedef void (*duckdb_v2_table_function_bind_callback_fn)(duckdb_v2_function_bind_info_handle info, + duckdb_v2_table_function_bind_info_handle result, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_table_function_init_global_callback_fn)(duckdb_v2_table_function_init_global_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_table_function_init_local_callback_fn)(duckdb_v2_table_function_init_local_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_table_function_exec_callback_fn)(duckdb_v2_table_function_exec_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_table_function_progress_callback_fn)(duckdb_v2_table_function_progress_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_table_function_filter_pushdown_callback_fn)( + duckdb_v2_table_function_filter_pushdown_info_handle info, duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_table_function_partition_data_callback_fn)( + duckdb_v2_table_function_partition_data_info_handle info, duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_table_function_partitioning_callback_fn)( + duckdb_v2_table_function_partitioning_info_handle info, duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_table_function_claim_batch_callback_fn)(duckdb_v2_table_function_claim_batch_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +typedef void (*duckdb_v2_table_function_get_bind_info_callback_fn)( + duckdb_v2_table_function_get_bind_info_info_handle info, duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + +/* --- Functions for table --- */ + +/*! + * Creates a new table function through a factory. * - * The child type is the common type of the elements, resolved by the same rule a SQL list literal follows, and every - * element is cast to it; a set with no common type surfaces the cast error. Pass child_type to name the type instead, - * which is also how an empty list is built: with child_type NULL, an empty element array has no type to resolve and - * returns ERROR_INPUT_INVALID. + * The function starts out empty: configure it with the setter functions (e.g. `duckdb_v2_table_function_set_name()`, + * `duckdb_v2_table_function_set_exec_callback()`, etc.) and the signature obtained via + * `duckdb_v2_table_function_get_signature()`, then make it available with + * `duckdb_v2_connection_register_table_function()` or `duckdb_v2_extension_register_table_function()`. The caller owns + * the returned handle and must destroy it with `duckdb_v2_table_function_destroy()`, also after registration. * - * The elements are borrowed and copied in, and a NULL element becomes a typed NULL. The type is rebuilt from its child, - * so an alias on the outer LIST type is not preserved; value_cast is the alias-preserving path. + * The table function can only be registered on the database the factory belongs to. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param child_type Optional. The element type. Pass NULL to resolve it from the elements. - * @param children An array of child_count elements. Borrowed (copied in). Pass NULL when child_count is 0. - * @param child_count The number of elements. - * @param out_value Receives the new composite value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param factory The factory to create the table function through. + * @param function On success, receives the newly created table function. Owned by the caller. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_list_with_connection( - duckdb_v2_connection_handle conn, duckdb_v2_logical_type_handle child_type, const duckdb_v2_value_handle *children, - idx_t child_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_create(duckdb_v2_factory_handle factory, + duckdb_v2_table_function_handle *function, + duckdb_v2_error_info_handle *err); /*! - * Creates an ARRAY value from its elements, sized by their count. - * - * The child type is resolved exactly as for value_create_list, and child_type names it explicitly the same way. The - * minimum array size is 1, so an empty element array returns ERROR_INPUT_INVALID whether or not child_type is given. + * Sets the name of the table function. * - * The elements are borrowed and copied in, and a NULL element becomes a typed NULL. + * The name is borrowed and copied. Calling this again replaces the previous name. A name must be set before + * registration. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param child_type Optional. The element type. Pass NULL to resolve it from the elements. - * @param children An array of child_count elements. Borrowed (copied in). Pass NULL when child_count is 0. - * @param child_count The number of elements. - * @param out_value Receives the new composite value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the name of. + * @param name The name to set. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_array_with_context( - duckdb_v2_context_handle ctx, duckdb_v2_logical_type_handle child_type, const duckdb_v2_value_handle *children, - idx_t child_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_name(duckdb_v2_table_function_handle function, + const duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err); /*! - * Creates an ARRAY value from its elements, sized by their count. - * - * The child type is resolved exactly as for value_create_list, and child_type names it explicitly the same way. The - * minimum array size is 1, so an empty element array returns ERROR_INPUT_INVALID whether or not child_type is given. + * Returns the documentation of the function so it can be filled in. * - * The elements are borrowed and copied in, and a NULL element becomes a typed NULL. + * Set a description with `duckdb_v2_function_docs_set_description()`, and add examples and categories with + * `duckdb_v2_function_docs_add_example()` and `duckdb_v2_function_docs_add_category()`. The documentation is modified + * in place, describes this overload only, and is registered with the function. Optional. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param child_type Optional. The element type. Pass NULL to resolve it from the elements. - * @param children An array of child_count elements. Borrowed (copied in). Pass NULL when child_count is 0. - * @param child_count The number of elements. - * @param out_value Receives the new composite value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to get the documentation of. + * @param docs The returned documentation. Borrowed and valid for the lifetime of the function handle. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_array_with_connection( - duckdb_v2_connection_handle conn, duckdb_v2_logical_type_handle child_type, const duckdb_v2_value_handle *children, - idx_t child_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_get_docs(duckdb_v2_table_function_handle function, + duckdb_v2_function_docs_handle *docs, + duckdb_v2_error_info_handle *err); /*! - * Creates a STRUCT value from named fields, in order. - * - * Each field keeps its own child's type, so there is nothing to resolve across them; names and children are parallel - * arrays of field_count entries. An empty field list builds the empty struct, which is a real type. + * Returns the function's signature so it can be configured. * - * The names and children are borrowed and copied in, and a NULL child becomes a typed NULL. Duplicate field names are - * rejected. + * Add parameters with `duckdb_v2_function_signature_add_parameter()`. The signature is modified in place. A table + * function declares the columns it returns from its bind callback instead of through a return type, so registration + * rejects a signature whose return type was set with `duckdb_v2_function_signature_set_return_type()`. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param names An array of field_count field names. Pass NULL when field_count is 0. - * @param children An array of field_count field values. Borrowed (copied in). Pass NULL when field_count is 0. - * @param field_count The number of fields. - * @param out_value Receives the new composite value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to get the signature of. + * @param sig The returned signature. Borrowed and valid for the lifetime of the function handle. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_struct_with_context( - duckdb_v2_context_handle ctx, const duckdb_v2_identifier_t *names, const duckdb_v2_value_handle *children, - idx_t field_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_get_signature(duckdb_v2_table_function_handle function, + duckdb_v2_function_signature_handle *sig, + duckdb_v2_error_info_handle *err); /*! - * Creates a STRUCT value from named fields, in order. - * - * Each field keeps its own child's type, so there is nothing to resolve across them; names and children are parallel - * arrays of field_count entries. An empty field list builds the empty struct, which is a real type. + * Sets arbitrary user data on the table function. * - * The names and children are borrowed and copied in, and a NULL child becomes a typed NULL. Duplicate field names are - * rejected. + * Associates an opaque pointer with the function, retrievable from the callbacks via + * `duckdb_v2_function_bind_get_user_data()`, `duckdb_v2_table_function_init_global_get_user_data()`, + * `duckdb_v2_table_function_exec_get_user_data()` and their counterparts on the other phases. The opaque handle bundles + * the pointer with an optional destructor, invoked when the data is no longer needed. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param names An array of field_count field names. Pass NULL when field_count is 0. - * @param children An array of field_count field values. Borrowed (copied in). Pass NULL when field_count is 0. - * @param field_count The number of fields. - * @param out_value Receives the new composite value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the user data of. + * @param data Opaque handle bundling the user data pointer plus an optional destructor. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_struct_with_connection( - duckdb_v2_connection_handle conn, const duckdb_v2_identifier_t *names, const duckdb_v2_value_handle *children, - idx_t field_count, duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_user_data(duckdb_v2_table_function_handle function, + duckdb_v2_opaque *data, + duckdb_v2_error_info_handle *err); /*! - * Creates a TUPLE value from positional fields. - * - * TUPLE is the unnamed struct: the same positional children as value_create_struct, under a distinct type id and with - * no names. Each field keeps its own child's type. An empty field list builds the empty tuple, which is a real type. + * Sets the bind callback of the table function. * - * The children are borrowed and copied in, and a NULL child becomes a typed NULL. + * The bind callback is invoked during query planning for each call site of the function. It must declare the columns + * the function returns via `duckdb_v2_table_function_bind_add_result_column()`. Through its + * `duckdb_v2_function_bind_info_handle` it can also inspect the constant argument values and set "bind data" that is + * shared with all later callbacks. A bind callback must be set before registration. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param children An array of field_count field values. Borrowed (copied in). Pass NULL when field_count is 0. - * @param field_count The number of fields. - * @param out_value Receives the new composite value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the bind callback of. + * @param callback The bind callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_tuple_with_context(duckdb_v2_context_handle ctx, - const duckdb_v2_value_handle *children, - idx_t field_count, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_bind_callback( + duckdb_v2_table_function_handle function, duckdb_v2_table_function_bind_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Creates a TUPLE value from positional fields. - * - * TUPLE is the unnamed struct: the same positional children as value_create_struct, under a distinct type id and with - * no names. Each field keeps its own child's type. An empty field list builds the empty tuple, which is a real type. + * Sets the optional global init callback of the table function. * - * The children are borrowed and copied in, and a NULL child becomes a typed NULL. + * The global init callback is invoked once per scan, at the start of execution. It can set "global state" shared by + * every thread scanning the function, and declare how many threads may scan it in parallel via + * `duckdb_v2_table_function_init_global_set_max_threads()`. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param children An array of field_count field values. Borrowed (copied in). Pass NULL when field_count is 0. - * @param field_count The number of fields. - * @param out_value Receives the new composite value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the global init callback of. + * @param callback The global init callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_tuple_with_connection(duckdb_v2_connection_handle conn, - const duckdb_v2_value_handle *children, - idx_t field_count, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_init_global_callback( + duckdb_v2_table_function_handle function, duckdb_v2_table_function_init_global_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Creates a MAP value from parallel key and value arrays. - * - * The key and value types are the common types of the keys and of the values, resolved as for value_create_list, and - * every entry is cast to them. Pass key_type and value_type to name them instead, which is also how an empty map is - * built: with both NULL, empty key and value arrays have no types to resolve and return ERROR_INPUT_INVALID. + * Sets the optional local init callback of the table function. * - * Keys and values are borrowed and copied in, and the two arrays must be the same length. Keys must be non-NULL and - * unique; both are enforced. + * The local init callback is invoked once per thread that will scan the function. It can set worker-local "local + * state", retrievable from the exec callback, typically derived from the shared global state. * * history: * - stable: v2.0.0 * - * @param ctx The context to use for the construction. - * @param key_type Optional. The key type. Pass NULL to resolve it from the keys. - * @param value_type Optional. The value type. Pass NULL to resolve it from the values. - * @param keys An array of entry_count keys. Borrowed (copied in). Pass NULL when entry_count is 0. - * @param values An array of entry_count values, parallel to keys. Borrowed (copied in). Pass NULL when entry_count is - * 0. - * @param entry_count The number of entries. - * @param out_value Receives the new composite value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the local init callback of. + * @param callback The local init callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_map_with_context( - duckdb_v2_context_handle ctx, duckdb_v2_logical_type_handle key_type, duckdb_v2_logical_type_handle value_type, - const duckdb_v2_value_handle *keys, const duckdb_v2_value_handle *values, idx_t entry_count, - duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_init_local_callback( + duckdb_v2_table_function_handle function, duckdb_v2_table_function_init_local_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Creates a MAP value from parallel key and value arrays. - * - * The key and value types are the common types of the keys and of the values, resolved as for value_create_list, and - * every entry is cast to them. Pass key_type and value_type to name them instead, which is also how an empty map is - * built: with both NULL, empty key and value arrays have no types to resolve and return ERROR_INPUT_INVALID. + * Sets the exec callback of the table function. * - * Keys and values are borrowed and copied in, and the two arrays must be the same length. Keys must be non-NULL and - * unique; both are enforced. + * The exec callback implements the function's logic: it is invoked repeatedly during query execution and writes the + * next batch of rows to the output chunk, until it produces an empty batch to signal the end of the scan. An exec + * callback must be set before registration. * * history: * - stable: v2.0.0 * - * @param conn The connection to use for the construction. - * @param key_type Optional. The key type. Pass NULL to resolve it from the keys. - * @param value_type Optional. The value type. Pass NULL to resolve it from the values. - * @param keys An array of entry_count keys. Borrowed (copied in). Pass NULL when entry_count is 0. - * @param values An array of entry_count values, parallel to keys. Borrowed (copied in). Pass NULL when entry_count is - * 0. - * @param entry_count The number of entries. - * @param out_value Receives the new composite value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the exec callback of. + * @param callback The exec callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_map_with_connection( - duckdb_v2_connection_handle conn, duckdb_v2_logical_type_handle key_type, duckdb_v2_logical_type_handle value_type, - const duckdb_v2_value_handle *keys, const duckdb_v2_value_handle *values, idx_t entry_count, - duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_exec_callback( + duckdb_v2_table_function_handle function, duckdb_v2_table_function_exec_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Returns the number of child values of a value. + * Sets the optional progress callback of the table function. * - * Per type kind: the element count for LIST and ARRAY; the field count for STRUCT and TUPLE; twice the entry count for - * MAP, whose children alternate key, value; and 2 for UNION, the tag plus the active member. Everything else reports 0, - * NULL values of any nested type included. + * The progress callback is invoked on demand during execution to report how far the scan has advanced, which the engine + * surfaces as the query's progress. It is invoked concurrently with the exec callback, so it must read the global state + * in a thread-safe way. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out_count Receives the number of children. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the progress callback of. + * @param callback The progress callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_child_count(duckdb_v2_value_handle value, idx_t *out_count, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_progress_callback( + duckdb_v2_table_function_handle function, duckdb_v2_table_function_progress_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Returns one child of a composite value as an owned copy. - * - * LIST and ARRAY children are the elements. STRUCT and TUPLE children are the fields in declared order, and a STRUCT's - * field names come from its type, via logical_type_get_param. MAP children alternate key, value, symmetric with - * value_create_map. UNION children are [0] the tag, as a UTINYINT value, and [1] the active member, because a union - * value holds only that one member. - * - * Note the divergence from vector_get_child, which descends structurally and so exposes [0] = tag and [1..N] = ALL - * members. Code written to descend generically over both must account for that difference. + * Sets whether the table function supports projection pushdown. Defaults to false. * - * An out-of-range index returns ERROR_INPUT_INVALID, as does any index on a non-composite or NULL value. The returned - * value is caller-owned; destroy it via value_destroy. + * With projection pushdown, the engine asks the function for only the columns a query actually uses: the exec + * callback's output chunk holds one vector per requested column rather than one per column declared in bind, and the + * global init, local init and exec callbacks can look up which declared column each vector stands for via e.g. + * `duckdb_v2_table_function_exec_get_column_index()`. Without it the output chunk always holds every declared column, + * and the engine drops the unused ones itself. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param index The child index, in [0, child_count). - * @param out_child Receives the owned child value. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to configure. + * @param enable Whether the function supports projection pushdown. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_child(duckdb_v2_value_handle value, idx_t index, - duckdb_v2_value_handle *out_child, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_projection_pushdown(duckdb_v2_table_function_handle function, + bool enable, + duckdb_v2_error_info_handle *err); /*! - * Casts a value to a target type, using a connection. - * - * The same as value_cast_with_context, except that the cast function set comes from a connection: the cast runs in its - * own transaction on that connection's context. Use it from outside DuckDB, where a connection — but no context — is in - * hand. - * - * The conversion is the SQL-faithful, non-strict one, registered custom casts included, and a cast failure surfaces - * from the call. Together with a VARCHAR built through value_create_varchar_with_context / _with_connection, this - * constructs any value from text, extension values included; casting a member value to a union type, or a VARCHAR to an - * enum type, is the sanctioned way to build UNION and ENUM values. + * Sets the optional filter pushdown callback of the table function. * - * The input value and target type are borrowed. The returned value is caller-owned; destroy it via value_destroy. + * The filter pushdown callback is invoked while the query is optimized, after the bind callback and before any init + * callback, with the filter predicates the query applies to the function's rows. Each predicate is a bound expression + * the callback can inspect with the `expression` functions; the callback accepts the predicates it will apply itself + * via `duckdb_v2_table_function_filter_pushdown_accept()`, typically after recording what they select in the bind data. + * The engine then stops applying the accepted predicates and keeps applying the rest above the scan, so accepting a + * predicate is a promise to filter the rows exactly as it would have. The optimizer may invoke the callback more than + * once for the same query, each time with the predicates not yet accepted; it is not invoked when there are none. * * history: * - stable: v2.0.0 * - * @param conn The connection whose context supplies the cast function set. - * @param value The borrowed value to cast. - * @param target_type The borrowed target logical type. - * @param out_value Receives the owned cast result. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the filter pushdown callback of. + * @param callback The filter pushdown callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_cast_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_value_handle value, - duckdb_v2_logical_type_handle target_type, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_filter_pushdown_callback( + duckdb_v2_table_function_handle function, duckdb_v2_table_function_filter_pushdown_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Casts a value to a target type, using a context. - * - * The conversion is the SQL-faithful, non-strict one, registered custom casts included, and a cast failure surfaces - * from the call. Together with a VARCHAR built through value_create_varchar_with_context / _with_connection, this - * constructs any value from text, extension values included; casting a member value to a union type, or a VARCHAR to an - * enum type, is the sanctioned way to build UNION and ENUM values. + * Sets the optional "partition data" callback of the table function. * - * Runs in the caller's context scope, as create_type_from_text does: reach it from a bind-phase callback or another - * context-holding scope, not from an exec-phase worker callback. From outside DuckDB use value_cast_with_connection. - * - * The input value and target type are borrowed. The returned value is caller-owned; destroy it via value_destroy. + * The callback reports, for the batch the exec callback just produced, an ordering batch index, the values of a set of + * partitioning columns, or both, depending on what a downstream operator requires; it runs on the worker thread that + * produced the batch. Set `duckdb_v2_table_function_set_partitioning_callback()` too when the function can answer + * partitioning-column requests, since the engine only asks a scan for those when that callback reports + * `TABLE_PARTITION_INFO_SINGLE_VALUE_PARTITIONS`. * * history: * - stable: v2.0.0 * - * @param ctx The context supplying the cast function set. - * @param value The borrowed value to cast. - * @param target_type The borrowed target logical type. - * @param out_value Receives the owned cast result. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the partition data callback of. + * @param callback The partition data callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_cast_with_context(duckdb_v2_context_handle ctx, - duckdb_v2_value_handle value, - duckdb_v2_logical_type_handle target_type, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_partition_data_callback( + duckdb_v2_table_function_handle function, duckdb_v2_table_function_partition_data_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Returns whether the value is NULL. + * Sets the optional "partitioning" callback of the table function. + * + * The callback tells the query optimizer, for a candidate `GROUP BY` column set, whether every partition the scan + * produces carries exactly one distinct value for those columns; only `TABLE_PARTITION_INFO_SINGLE_VALUE_PARTITIONS` + * unlocks the partitioned aggregate optimization; any other result, or leaving this callback unset, keeps the regular + * hash aggregate. It runs on the planning thread, before execution starts, and must be deterministic for a given column + * set since it may be called on a plan the optimizer later discards. Registration fails unless + * `duckdb_v2_table_function_set_partition_data_callback()` is set too, since the engine asks that callback for the + * partitioning column values once this one claims single-value partitions. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out_is_null Receives true if the value is NULL, false otherwise. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param function The function to set the partitioning callback of. + * @param callback The partitioning callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_is_null(duckdb_v2_value_handle value, bool *out_is_null, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_partitioning_callback( + duckdb_v2_table_function_handle function, duckdb_v2_table_function_partitioning_callback_fn callback, + duckdb_v2_error_info_handle *err); /*! - * Returns the logical type of the value. + * Declares one of the columns the function returns. * - * The returned logical type is caller-owned; destroy it via logical_type_destroy. + * Call this once per column, in order: the columns declared here are the columns of the table the function produces, + * and the vectors of the output chunk the exec callback fills follow the same order. At least one column must be + * declared. The type must be a fully defined concrete type; ANY is rejected, as a result column carries data. The name + * and type are borrowed and copied. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out_type Receives the owned logical type. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The bind info handle. + * @param name The name of the column. Borrowed and copied. + * @param type The type of the column. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_logical_type(duckdb_v2_value_handle value, - duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_bind_add_result_column( + duckdb_v2_table_function_bind_info_handle info, const duckdb_v2_identifier_t *name, + duckdb_v2_logical_type_handle type, duckdb_v2_error_info_handle *err); /*! - * Renders the value as a human-readable string. Diagnostic only. - * - * Writes into a caller-supplied buffer, so nothing is allocated on the caller's behalf and nothing has to be freed. - * - * Pass out_string = NULL to size the buffer without rendering into it: out_length then receives the length, and - * out_capacity is ignored. With out_string != NULL, out_capacity must be at least out_length + 1, or the call returns - * ERROR_INPUT_OBJECT_SIZE with out_length set to the required length and out_string left untouched. + * Sets the estimated number of rows the scan will produce. * - * out_length never counts the terminator, but a successful write always appends one, so the buffer is usable as a C - * string. + * The estimate is a hint for the optimizer, not a limit: producing a different number of rows is not an error. Without + * it, the optimizer falls back on its own defaults. * * history: * - stable: v2.0.0 * - * @param value The value to read. - * @param out_string Caller-owned buffer receiving the text plus a null terminator, or NULL to only report the required - * length in out_length. - * @param out_capacity Bytes available in out_string, terminator included. Ignored when out_string is NULL. - * @param out_length Receives the text length excluding the null terminator — written on success and on - * ERROR_INPUT_OBJECT_SIZE. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @param info The bind info handle. + * @param cardinality The estimated number of rows. + * @param is_exact Whether the estimate is exact, which also makes it an upper bound on the row count. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_to_string(duckdb_v2_value_handle value, char *out_string, - idx_t out_capacity, idx_t *out_length, - duckdb_v2_error_info_handle *err); - -/* --- Struct definitions for value --- */ - -/* ============================================================================ - * MODULE: prepared_statement - * ============================================================================ */ - -/* --- Enums for prepared_statement --- */ - -/* --- Struct forward declarations for prepared_statement --- */ - -/* --- Types for prepared_statement --- */ - -/*! - * An owned handle to a statement bound and planned once, executable repeatedly via - * `duckdb_v2_prepared_statement_execute()`. Construct with `duckdb_v2_prepared_statement_create()` and destroy with - * `duckdb_v2_prepared_statement_destroy()`. It keeps its connection's session alive, so it stays usable across - * executions and even after the connection is disconnected. - */ -typedef struct _duckdb_v2_prepared_statement { - void *internal_ptr; -} * duckdb_v2_prepared_statement_handle; - -/* --- Constants for prepared_statement --- */ - -/* --- Function pointer typedefs for prepared_statement --- */ - -/* --- Functions for prepared_statement --- */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_bind_set_cardinality( + duckdb_v2_table_function_bind_info_handle info, idx_t cardinality, bool is_exact, duckdb_v2_error_info_handle *err); /*! - * Prepares a parsed statement into a reusable handle. Non-consuming. - * - * Copies the statement's AST, then binds and plans it once. Binder and catalog errors surface here, exactly as they - * would from `duckdb_v2_statement_execute()`. The statement is borrowed rather than consumed, since a copy is what gets - * prepared, so it can be prepared again or executed directly; the caller destroys it with - * `duckdb_v2_sql_statement_destroy()`. - * - * By default this succeeds for any preparable statement, whether or not its plan will be reused; ask - * `duckdb_v2_prepared_statement_reuses_plan()` which one you got. Setting `require_cacheable` instead fails with - * `ERROR_INPUT_INVALID` when the plan would not be reused, so a caller who wants the handle only for the speedup finds - * out here rather than after silently taking the slow path. + * Sets the order guarantee of the rows this call of the function produces. Defaults to + * `ORDER_PRESERVATION_INSERTION_ORDER`. * - * Refuses with `ERROR_RESOURCE_IN_USE` while the connection has a live result. Drain, destroy, or interrupt that result - * first, or prepare on another connection. `*out_prepared` is set to NULL on failure. + * With `ORDER_PRESERVATION_NO_ORDER`, a scan that reports more than one thread via + * `duckdb_v2_table_function_init_global_set_max_threads()` runs in parallel into query results, INSERT and COPY without + * a `duckdb_v2_table_function_set_partition_data_callback()`, and the rows arrive in no particular order. With + * insertion order kept, those consumers need the partition data callback to run in parallel. Fails with + * `ERROR_INPUT_INVALID` when order is not one of the enum's declared values. * * history: * - stable: v2.0.0 * - * @param conn The connection supplying the catalog, transaction, and parser state. The prepared statement belongs to - * it. - * @param statement The statement to prepare. Borrowed and copied, not consumed; destroy it with - * `duckdb_v2_sql_statement_destroy()`. - * @param require_cacheable When true, fail with `ERROR_INPUT_INVALID` unless the prepared plan will be reused across - * executions, as `duckdb_v2_prepared_statement_reuses_plan()` would report it. - * @param out_prepared On success, receives the new prepared statement. Owned by the caller; destroy via - * `duckdb_v2_prepared_statement_destroy()`. + * @param info The bind info handle. + * @param order The order guarantee of the produced rows. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_prepared_statement_create(duckdb_v2_connection_handle conn, - duckdb_v2_sql_statement_handle statement, - bool require_cacheable, - duckdb_v2_prepared_statement_handle *out_prepared, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_bind_set_order_preservation( + duckdb_v2_table_function_bind_info_handle info, DUCKDB_V2_ORDER_PRESERVATION order, + duckdb_v2_error_info_handle *err); /*! - * Executes a prepared statement, streaming its result. Non-consuming. - * - * Returns a result without executing anything: execution happens incrementally as the result is stepped or drained, - * exactly as with `duckdb_v2_statement_execute()`. The handle returned is an ordinary result, with identical behaviour - * throughout -- streaming, draining, the changed-row count of a DML statement, the output schema, the statement type, - * and the result type. - * - * `parameter_values` binds the statement's parameters as constants for this execution. Binding is positional by default - * ($1 = element 0); supply `parameter_names` to bind by name instead, where a non-empty entry binds its value to that - * named parameter ($name, matched case-insensitively) and a {NULL, 0} entry stays positional. Pass NULL for both - * arrays, or a count of 0, for a statement without parameters. Both arrays are borrowed and copied in, so the caller - * still owns and destroys them. A key set that does not match the statement's parameters is rejected with - * `ERROR_INPUT_INVALID`, with one exception: a named parameter left without a value reads the session variable of the - * same name (`SET VARIABLE`) when one exists. - * - * Not consumed: execute the same handle again, with the same values or different ones, as often as you like. Values are - * bound per execution and nothing carries over between them. A catalog change since the statement was prepared, or a - * parameter type that differs from the one the cached plan assumed, triggers a re-bind that is invisible apart from its - * cost. - * - * Refuses with `ERROR_RESOURCE_IN_USE` while the connection has a live result; drain, destroy, or interrupt it first, - * or execute on another connection. A failed execution, at any stage, leaves the prepared statement usable. - * `*out_result` is set to NULL on failure. + * Retrieves the user data set via `duckdb_v2_table_function_set_user_data()`. * * history: * - stable: v2.0.0 * - * @param prepared The prepared statement to execute. Borrowed; not consumed. - * @param parameter_names Optional. An array of `parameter_count` parameter names; a non-empty entry binds its value to - * the named parameter ($name, case-insensitive), a {NULL, 0} entry keeps it positional ($1 = element 0). Pass NULL to - * bind everything positionally. - * @param parameter_values Optional. An array of `parameter_count` values. Each binds by name when `parameter_names` - * supplies one, and positionally ($1 = element 0) otherwise. Borrowed and copied in. Pass NULL for a statement without - * parameters. - * @param parameter_count The number of entries in `parameter_names` and `parameter_values`. Pass 0 for a statement - * without parameters. - * @param out_result On success, receives the new result. Owned by the caller; destroy via `duckdb_v2_result_destroy()`. + * @param info The global init info handle. + * @param data Receives the user data pointer, or null if none was set. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_prepared_statement_execute(duckdb_v2_prepared_statement_handle prepared, - const duckdb_v2_identifier_t *parameter_names, - const duckdb_v2_value_handle *parameter_values, - idx_t parameter_count, - duckdb_v2_result_handle *out_result, - duckdb_v2_error_info_handle *err); - +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_global_get_user_data( + duckdb_v2_table_function_init_global_info_handle info, void **data, duckdb_v2_error_info_handle *err); + /*! - * Reports whether the prepared statement reuses its compiled plan across executions. + * Retrieves the bind data set by the function's bind callback. * - * True when executions reuse the plan built at prepare time, provided the supplied values match the planned parameter - * types and nothing the plan depends on has changed; false when the statement re-binds on every execution, making it no - * faster than `duckdb_v2_statement_execute()`. A plan is reused only when all parameter types were resolved at prepare - * time and the plan is cacheable: `SELECT 42` reuses, `SELECT $1::INTEGER + 1` reuses, a statement reading a base table - * does not (it re-binds so a catalog change is picked up), and `SELECT $1 + $2` does not (the types are unknown until - * values arrive). + * history: + * - stable: v2.0.0 * - * A static property of the built plan, fixed when the statement was prepared and independent of the values later passed - * to `duckdb_v2_prepared_statement_execute()`. + * @param info The global init info handle. + * @param data Receives the bind data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_global_get_bind_data( + duckdb_v2_table_function_init_global_info_handle info, void **data, duckdb_v2_error_info_handle *err); + +/*! + * Sets the function's "global state" from the global init callback. + * + * The global state lives for the duration of the scan and is retrievable from the local init, exec and progress + * callbacks. Every thread scanning the function shares it, so the function must synchronize its own access to it. The + * opaque handle bundles the pointer with an optional destructor, invoked when the scan is done with the state. * * history: * - stable: v2.0.0 * - * @param prepared The prepared statement to inspect. - * @param out_reuses Receives true when the compiled plan is reused across executions, false when the statement re-binds - * each time. + * @param info The global init info handle. + * @param data Opaque handle bundling the global state pointer plus an optional destructor. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_prepared_statement_reuses_plan(duckdb_v2_prepared_statement_handle prepared, - bool *out_reuses, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_global_set_global_state( + duckdb_v2_table_function_init_global_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); /*! - * Destroys a prepared statement. + * Sets how many threads may scan the function in parallel. * - * Null-safe: passing NULL, or a slot already set to NULL, is a no-op. A result produced by - * `duckdb_v2_prepared_statement_execute()` is independently owned and keeps the session alive itself, so the prepared - * statement may be destroyed while results made from it are still live. On success the slot is set to NULL. + * Defaults to 1, a single-threaded scan. The engine creates at most this many local states, and therefore runs at most + * this many exec callbacks concurrently. It is an upper bound, not a request: the engine may use fewer threads. * * history: * - stable: v2.0.0 * - * @param prepared The prepared statement to destroy. + * @param info The global init info handle. + * @param max_threads The maximum number of threads. Must be at least 1. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_prepared_statement_destroy(duckdb_v2_prepared_statement_handle *prepared); - -/* --- Struct definitions for prepared_statement --- */ - -/* ============================================================================ - * MODULE: query_result - * ============================================================================ */ - -/* --- Enums for query_result --- */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_global_set_max_threads( + duckdb_v2_table_function_init_global_info_handle info, idx_t max_threads, duckdb_v2_error_info_handle *err); /*! - * Shape of a query result. QUERY_RESULT carries rows and columns; CHANGED_ROWS carries an affected row count, as an - * INSERT, UPDATE, or DELETE without RETURNING produces; NOTHING covers DDL and other statements with no row output. + * Returns the number of columns the scan produces. + * + * With projection pushdown (see `duckdb_v2_table_function_set_projection_pushdown()`) this is the number of columns the + * query uses, which is the number of vectors in the exec callback's output chunk; without it, it is the number of + * columns declared in bind. Valid indices for `duckdb_v2_table_function_init_global_get_column_index()` are `0` up to + * (but excluding) this count. + * + * history: + * - stable: v2.0.0 + * + * @param info The global init info handle. + * @param count Receives the number of columns the scan produces. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR */ -typedef enum DUCKDB_V2_RESULT_TYPE { - DUCKDB_V2_RESULT_TYPE_QUERY_RESULT = 0, - DUCKDB_V2_RESULT_TYPE_CHANGED_ROWS = 1, - DUCKDB_V2_RESULT_TYPE_NOTHING = 2, - DUCKDB_V2_RESULT_TYPE_MAX_ENUM = 0x7FFFFFFF, -} DUCKDB_V2_RESULT_TYPE; +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_global_get_column_count( + duckdb_v2_table_function_init_global_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); /*! - * Outcome of a result_step call. WAITING is the 0-value, so a zero-initialized out-param reads as "no work product yet" - * rather than CHUNK, the same convention VECTOR_TYPE_OTHER follows. The four states are the ones a consumer acts on; - * they are deliberately not a projection of any internal enum. + * Returns which declared column the scan's column at the given index stands for. + * + * The result indexes the columns declared with `duckdb_v2_table_function_bind_add_result_column()`, in declaration + * order: the exec callback fills the output chunk's vector at `index` with that column's data. Without projection + * pushdown the mapping is the identity. Fails if the index is out of bounds. + * + * history: + * - stable: v2.0.0 + * + * @param info The global init info handle. + * @param index The index of the column in the scan's output. + * @param column_index Receives the index of the declared column. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR */ -typedef enum DUCKDB_V2_RESULT_STEP_STATUS { - //! No chunk yet; step again, or block in result_wait. - DUCKDB_V2_RESULT_STEP_STATUS_WAITING = 0, - - //! A caller-owned chunk was written to *out_chunk. - DUCKDB_V2_RESULT_STEP_STATUS_CHUNK = 1, - - //! Stream exhausted. Sticky. - DUCKDB_V2_RESULT_STEP_STATUS_FINISHED = 2, - - //! Query was interrupted. Sticky. - DUCKDB_V2_RESULT_STEP_STATUS_CANCELLED = 3, - DUCKDB_V2_RESULT_STEP_STATUS_MAX_ENUM = 0x7FFFFFFF, -} DUCKDB_V2_RESULT_STEP_STATUS; - -/* --- Struct forward declarations for query_result --- */ - -/* --- Types for query_result --- */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_global_get_column_index( + duckdb_v2_table_function_init_global_info_handle info, idx_t index, idx_t *column_index, + duckdb_v2_error_info_handle *err); -/* --- Constants for query_result --- */ +/*! + * Retrieves the user data set via `duckdb_v2_table_function_set_user_data()`. + * + * history: + * - stable: v2.0.0 + * + * @param info The local init info handle. + * @param data Receives the user data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_local_get_user_data( + duckdb_v2_table_function_init_local_info_handle info, void **data, duckdb_v2_error_info_handle *err); -/* --- Function pointer typedefs for query_result --- */ +/*! + * Retrieves the bind data set by the function's bind callback. + * + * history: + * - stable: v2.0.0 + * + * @param info The local init info handle. + * @param data Receives the bind data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_local_get_bind_data( + duckdb_v2_table_function_init_local_info_handle info, void **data, duckdb_v2_error_info_handle *err); -/* --- Functions for query_result --- */ +/*! + * Retrieves the global state set by the function's global init callback. + * + * Shared with every other thread scanning the function; access to it must be synchronized by the function. + * + * history: + * - stable: v2.0.0 + * + * @param info The local init info handle. + * @param data Receives the global state pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_local_get_global_state( + duckdb_v2_table_function_init_local_info_handle info, void **data, duckdb_v2_error_info_handle *err); /*! - * Executes a parsed statement on the connection, streaming its result. Non-consuming. + * Sets the function's worker-local "local state" from the local init callback. * - * Takes a statement produced by the sql_statement module (parse_sql / statement_iterator_next), preprocesses and - * prepares it, and returns a result handle without executing anything: execution happens incrementally as the result is - * stepped (result_step) or drained (result_fetch_chunk). This call reports only the errors detectable at prepare time — - * binder, catalog, pragma preprocessing — while errors raised during execution surface from the stepping functions. + * The local state is associated with the executing thread for the duration of the scan and retrievable from the exec + * callback via `duckdb_v2_table_function_exec_get_local_state()`. No other thread observes it, so it needs no + * synchronization. The opaque handle bundles the pointer with an optional destructor, invoked when the local state is + * no longer needed. * - * The statement is borrowed, not consumed, since a copy is what executes. It can be executed again, for example with a - * different set of values, and the caller destroys it with sql_statement_destroy. + * history: + * - stable: v2.0.0 * - * parameter_values binds the statement's parameters as constants for this execution. Binding is positional by default: - * the i-th value binds $(i+1), matching SQL's own convention, so dense $1..$N and ? placeholders work directly. Supply - * parameter_names to bind by name instead — a non-empty name binds its value to that named parameter ($name, matched - * case-insensitively), while a {NULL, 0} entry leaves that value positional. The parameter schema from statement_bind - * lists the names to use. Both arrays are borrowed and copied in; the caller still owns and destroys them. Pass NULL - * for both, or a count of 0, for an unparameterized statement. A key set that does not match the statement's parameters - * — names for a positional statement, or the reverse — is rejected with ERROR_INPUT_INVALID, and named and positional - * parameters cannot be mixed within one statement. Parameters and statement expansion are mutually exclusive: passing - * values for a statement that preprocesses into a group is rejected with ERROR_INPUT_INVALID. + * @param info The local init info handle. + * @param data Opaque handle bundling the local state pointer plus an optional destructor. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_local_set_local_state( + duckdb_v2_table_function_init_local_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); + +/*! + * Returns the number of columns the scan produces. * - * Preprocessing can expand one statement into a group — a dynamic PIVOT, or statement-expanding DDL such as ALTER ... - * ADD COLUMN with a non-constant DEFAULT. The group executes as one result through the same steps, and the stream - * surfaces the first row-producing statement of the group, or the last statement when none produces rows. An expansion - * with more than one row-producing statement cannot be streamed as a single result and reports - * ERROR_QUERY_NOT_IMPLEMENTED; no known expansion produces one. + * With projection pushdown (see `duckdb_v2_table_function_set_projection_pushdown()`) this is the number of columns the + * query uses, which is the number of vectors in the exec callback's output chunk; without it, it is the number of + * columns declared in bind. Valid indices for `duckdb_v2_table_function_init_local_get_column_index()` are `0` up to + * (but excluding) this count. * - * One live result per connection: this refuses with ERROR_RESOURCE_IN_USE while the connection already has a live - * result. Drain, destroy, or interrupt that one first, or open another connection. + * history: + * - stable: v2.0.0 * - * Schema metadata — result type, statement type, column count, names, logical types — is available on the returned - * handle immediately, before the first step. + * @param info The local init info handle. + * @param count Receives the number of columns the scan produces. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_local_get_column_count( + duckdb_v2_table_function_init_local_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); + +/*! + * Returns which declared column the scan's column at the given index stands for. * - * *out_result is set to nullptr on failure. + * The result indexes the columns declared with `duckdb_v2_table_function_bind_add_result_column()`, in declaration + * order: the exec callback fills the output chunk's vector at `index` with that column's data. Without projection + * pushdown the mapping is the identity. Fails if the index is out of bounds. * * history: * - stable: v2.0.0 * - * @param conn The connection on which to execute the statement. - * @param statement The statement to execute. Borrowed and copied; not consumed. Destroy it with sql_statement_destroy. - * @param parameter_names Optional. An array of parameter_count parameter names; a non-empty entry binds its value to - * the named parameter ($name, case-insensitive), a {NULL, 0} entry keeps it positional ($1 = element 0). Pass NULL for - * all-positional binding. Named and positional parameters cannot be mixed within one statement. - * @param parameter_values Optional. An array of parameter_count value handles. Each binds by name when parameter_names - * supplies one, otherwise positionally ($1 = element 0). Borrowed (copied in). Pass NULL for an unparameterized - * statement. - * @param parameter_count The number of parameters in parameter_names and parameter_values. Pass 0 for an - * unparameterized statement. - * @param out_result Receives the new result handle. + * @param info The local init info handle. + * @param index The index of the column in the scan's output. + * @param column_index Receives the index of the declared column. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR +duckdb_v2_table_function_init_local_get_column_index(duckdb_v2_table_function_init_local_info_handle info, idx_t index, + idx_t *column_index, duckdb_v2_error_info_handle *err); + +/*! + * Retrieves the user data set via `duckdb_v2_table_function_set_user_data()`. + * + * history: + * - stable: v2.0.0 + * + * @param info The exec info handle. + * @param data Receives the user data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_exec_get_user_data(duckdb_v2_table_function_exec_info_handle info, + void **data, duckdb_v2_error_info_handle *err); + +/*! + * Retrieves the bind data set by the function's bind callback. + * + * history: + * - stable: v2.0.0 + * + * @param info The exec info handle. + * @param data Receives the bind data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_exec_get_bind_data(duckdb_v2_table_function_exec_info_handle info, + void **data, duckdb_v2_error_info_handle *err); + +/*! + * Retrieves the global state set by the function's global init callback. + * + * Shared with every other thread scanning the function; access to it must be synchronized by the function. + * + * history: + * - stable: v2.0.0 + * + * @param info The exec info handle. + * @param data Receives the global state pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_exec_get_global_state( + duckdb_v2_table_function_exec_info_handle info, void **data, duckdb_v2_error_info_handle *err); + +/*! + * Retrieves the worker-local local state set by the function's local init callback. + * + * history: + * - stable: v2.0.0 + * + * @param info The exec info handle. + * @param data Receives the local state pointer for the executing thread, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_exec_get_local_state( + duckdb_v2_table_function_exec_info_handle info, void **data, duckdb_v2_error_info_handle *err); + +/*! + * Retrieves the output chunk the exec callback must write the next batch of rows into. + * + * The chunk holds one vector per column declared with `duckdb_v2_table_function_bind_add_result_column()`, in the same + * order; reach them with `duckdb_v2_data_chunk_get_vector()`. The chunk starts out empty on every invocation: write the + * rows, then declare how many there are with `duckdb_v2_vector_set_size()` on the first vector, which the engine takes + * as the batch's row count and propagates to the other vectors. Producing an empty batch signals the end of the scan, + * after which the callback is not invoked again on that thread. Borrowed; valid only for the duration of the callback. + * + * history: + * - stable: v2.0.0 + * + * @param info The exec info handle. + * @param chunk Receives the borrowed output chunk to write into. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR +duckdb_v2_table_function_exec_get_output_chunk(duckdb_v2_table_function_exec_info_handle info, + duckdb_v2_data_chunk_handle *chunk, duckdb_v2_error_info_handle *err); + +/*! + * Returns the number of columns the scan produces. + * + * With projection pushdown (see `duckdb_v2_table_function_set_projection_pushdown()`) this is the number of columns the + * query uses, which is the number of vectors in the exec callback's output chunk; without it, it is the number of + * columns declared in bind. Valid indices for `duckdb_v2_table_function_exec_get_column_index()` are `0` up to (but + * excluding) this count. + * + * history: + * - stable: v2.0.0 + * + * @param info The exec info handle. + * @param count Receives the number of columns the scan produces. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_exec_get_column_count( + duckdb_v2_table_function_exec_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); + +/*! + * Returns which declared column the scan's column at the given index stands for. + * + * The result indexes the columns declared with `duckdb_v2_table_function_bind_add_result_column()`, in declaration + * order: the exec callback fills the output chunk's vector at `index` with that column's data. Without projection + * pushdown the mapping is the identity. Fails if the index is out of bounds. + * + * history: + * - stable: v2.0.0 + * + * @param info The exec info handle. + * @param index The index of the column in the scan's output. + * @param column_index Receives the index of the declared column. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_exec_get_column_index( + duckdb_v2_table_function_exec_info_handle info, idx_t index, idx_t *column_index, duckdb_v2_error_info_handle *err); + +/*! + * Retrieves the user data set via `duckdb_v2_table_function_set_user_data()`. + * + * history: + * - stable: v2.0.0 + * + * @param info The progress info handle. + * @param data Receives the user data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_progress_get_user_data( + duckdb_v2_table_function_progress_info_handle info, void **data, duckdb_v2_error_info_handle *err); + +/*! + * Retrieves the bind data set by the function's bind callback. + * + * history: + * - stable: v2.0.0 + * + * @param info The progress info handle. + * @param data Receives the bind data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_progress_get_bind_data( + duckdb_v2_table_function_progress_info_handle info, void **data, duckdb_v2_error_info_handle *err); + +/*! + * Retrieves the global state set by the function's global init callback. + * + * The progress callback runs concurrently with the exec callbacks scanning the function, so it must read the global + * state in a thread-safe way. + * + * history: + * - stable: v2.0.0 + * + * @param info The progress info handle. + * @param data Receives the global state pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_progress_get_global_state( + duckdb_v2_table_function_progress_info_handle info, void **data, duckdb_v2_error_info_handle *err); + +/*! + * Reports how far the scan has advanced. + * + * A fraction between 0.0 (nothing scanned yet) and 1.0 (done); values outside that range are clamped. A progress + * callback that returns without calling this reports no progress. + * + * history: + * - stable: v2.0.0 + * + * @param info The progress info handle. + * @param progress The fraction of the scan that is complete, in [0.0, 1.0]. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_progress_set_progress( + duckdb_v2_table_function_progress_info_handle info, double progress, duckdb_v2_error_info_handle *err); + +/*! + * Retrieves the user data set via `duckdb_v2_table_function_set_user_data()`, or null if none was set. + * + * history: + * - stable: v2.0.0 + * + * @param info The filter pushdown info handle. + * @param data Receives the user data pointer. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_filter_pushdown_get_user_data( + duckdb_v2_table_function_filter_pushdown_info_handle info, void **data, duckdb_v2_error_info_handle *err); + +/*! + * Retrieves the bind data set via `duckdb_v2_function_bind_set_bind_data()`, or null if none was set. + * + * This is the same object the init and exec callbacks later receive, so a predicate the callback accepts can be + * recorded in it for the scan to apply. + * + * history: + * - stable: v2.0.0 + * + * @param info The filter pushdown info handle. + * @param data Receives the bind data pointer. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_filter_pushdown_get_bind_data( + duckdb_v2_table_function_filter_pushdown_info_handle info, void **data, duckdb_v2_error_info_handle *err); + +/*! + * Returns the number of filter predicates offered to the function. + * + * The predicates are combined with `AND`: every row the scan produces must satisfy all of them. Valid indices for + * `duckdb_v2_table_function_filter_pushdown_get_filter()` and `duckdb_v2_table_function_filter_pushdown_accept()` are + * `0` up to (but excluding) this count. + * + * history: + * - stable: v2.0.0 + * + * @param info The filter pushdown info handle. + * @param count Receives the number of predicates. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_filter_pushdown_get_filter_count( + duckdb_v2_table_function_filter_pushdown_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); + +/*! + * Retrieves the filter predicate at the given index. + * + * Fails if the index is out of bounds. The predicate is a bound expression evaluating to `BOOLEAN`; inspect it with + * `duckdb_v2_expression_get_type()` and the other `expression` functions, and resolve the column references it contains + * via `duckdb_v2_table_function_filter_pushdown_get_column_index()`. Borrowed; valid only for the duration of the + * callback. + * + * history: + * - stable: v2.0.0 + * + * @param info The filter pushdown info handle. + * @param index The index of the predicate to retrieve. + * @param filter Receives the borrowed predicate. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_filter_pushdown_get_filter( + duckdb_v2_table_function_filter_pushdown_info_handle info, idx_t index, duckdb_v2_expression_handle *filter, + duckdb_v2_error_info_handle *err); + +/*! + * Accepts the filter predicate at the given index: the function will apply it itself. + * + * The engine stops applying an accepted predicate, so the scan must produce only rows that satisfy it, or the query + * returns rows it should not. Predicates left unaccepted are applied by the engine as usual, so a callback that + * recognizes nothing can simply return. Fails if the index is out of bounds. + * + * history: + * - stable: v2.0.0 + * + * @param info The filter pushdown info handle. + * @param index The index of the predicate to accept. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_filter_pushdown_accept( + duckdb_v2_table_function_filter_pushdown_info_handle info, idx_t index, duckdb_v2_error_info_handle *err); + +/*! + * Returns the number of columns the predicates can refer to. + * + * A column reference inside a predicate (`duckdb_v2_expression_column_ref_get_index()`) indexes the columns the query + * reads from the function, not the columns declared in bind. Valid indices for + * `duckdb_v2_table_function_filter_pushdown_get_column_index()` are `0` up to (but excluding) this count. + * + * history: + * - stable: v2.0.0 + * + * @param info The filter pushdown info handle. + * @param count Receives the number of referable columns. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_filter_pushdown_get_column_count( + duckdb_v2_table_function_filter_pushdown_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); + +/*! + * Resolves a column reference found in a predicate to the declared column it refers to. + * + * Takes the index a `EXPRESSION_TYPE_BOUND_COLUMN_REF` node reports and returns the index of the column among the ones + * declared with `duckdb_v2_table_function_bind_add_result_column()`, in declaration order. Fails if the index is out of + * bounds. + * + * history: + * - stable: v2.0.0 + * + * @param info The filter pushdown info handle. + * @param index The column index reported by a column reference node. + * @param column_index Receives the index of the declared column. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_filter_pushdown_get_column_index( + duckdb_v2_table_function_filter_pushdown_info_handle info, idx_t index, idx_t *column_index, + duckdb_v2_error_info_handle *err); + +/*! + * Retrieves the user data set via `duckdb_v2_table_function_set_user_data()`. + * + * history: + * - stable: v2.0.0 + * + * @param info The partition data info handle. + * @param data Receives the user data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_partition_data_get_user_data( + duckdb_v2_table_function_partition_data_info_handle info, void **data, duckdb_v2_error_info_handle *err); + +/*! + * Retrieves the bind data set by the function's bind callback. + * + * history: + * - stable: v2.0.0 + * + * @param info The partition data info handle. + * @param data Receives the bind data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_partition_data_get_bind_data( + duckdb_v2_table_function_partition_data_info_handle info, void **data, duckdb_v2_error_info_handle *err); + +/*! + * Retrieves the global state set by the function's global init callback. + * + * Shared with every other thread scanning the function; access to it must be synchronized by the function. + * + * history: + * - stable: v2.0.0 + * + * @param info The partition data info handle. + * @param data Receives the global state pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_partition_data_get_global_state( + duckdb_v2_table_function_partition_data_info_handle info, void **data, duckdb_v2_error_info_handle *err); + +/*! + * Retrieves the worker-local local state set by the function's local init callback, for the thread that produced the + * batch this call reports on. No other thread observes it, so it needs no synchronization. + * + * history: + * - stable: v2.0.0 + * + * @param info The partition data info handle. + * @param data Receives the local state pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_partition_data_get_local_state( + duckdb_v2_table_function_partition_data_info_handle info, void **data, duckdb_v2_error_info_handle *err); + +/*! + * Returns whether a downstream operator needs this batch's ordering position. + * + * `duckdb_v2_table_function_partition_data_set_batch_index()` must still be called even when this is false: the engine + * validates the reported value regardless of whether it is actually used for ordering, so `0` is always a safe answer + * when this reports false. + * + * history: + * - stable: v2.0.0 + * + * @param info The partition data info handle. + * @param required Receives whether a batch index is required. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_partition_data_requires_batch_index( + duckdb_v2_table_function_partition_data_info_handle info, bool *required, duckdb_v2_error_info_handle *err); + +/*! + * Returns whether a downstream operator needs this batch's values for a set of partitioning columns. + * + * When true, call `duckdb_v2_table_function_partition_data_set_partition_value()` once for every index in `[0, count)`, + * where `count` comes from `duckdb_v2_table_function_partition_data_get_partition_column_count()`. + * + * history: + * - stable: v2.0.0 + * + * @param info The partition data info handle. + * @param required Receives whether partitioning column values are required. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_partition_data_requires_partition_columns( + duckdb_v2_table_function_partition_data_info_handle info, bool *required, duckdb_v2_error_info_handle *err); + +/*! + * Returns the number of partitioning columns a downstream operator is requesting values for. + * + * `0` when `duckdb_v2_table_function_partition_data_requires_partition_columns()` is false. Valid indices for + * `duckdb_v2_table_function_partition_data_get_partition_column_index()` and + * `duckdb_v2_table_function_partition_data_set_partition_value()` are `0` up to (but excluding) this count. + * + * history: + * - stable: v2.0.0 + * + * @param info The partition data info handle. + * @param count Receives the number of requested partitioning columns. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_partition_data_get_partition_column_count( + duckdb_v2_table_function_partition_data_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); + +/*! + * Returns which declared column the requested partitioning column at the given index stands for. + * + * The result indexes the columns declared with `duckdb_v2_table_function_bind_add_result_column()`, in declaration + * order. Fails if the index is out of bounds. + * + * history: + * - stable: v2.0.0 + * + * @param info The partition data info handle. + * @param index The index of the requested partitioning column. + * @param column_index Receives the index of the declared column. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_partition_data_get_partition_column_index( + duckdb_v2_table_function_partition_data_info_handle info, idx_t index, idx_t *column_index, + duckdb_v2_error_info_handle *err); + +/*! + * Reports the ordering position of the batch the exec callback just produced. + * + * Required on every call, whether or not `duckdb_v2_table_function_partition_data_requires_batch_index()` is true: the + * engine validates the reported value regardless. Not calling this before the callback returns fails the query; `0` is + * always a safe answer when the batch index is not actually required. Must not decrease across successive calls on the + * same thread, must be unique across threads for the ordering to be meaningful, must be less than roughly `10^13`, and, + * when partitioning column values are also being reported, must change whenever those values change: the engine only + * re-reads them when the batch index changes, so reporting a new value under an unchanged batch index is treated as a + * caller error rather than applied. Fails with `ERROR_INPUT_INVALID` for an out-of-range value; a decreasing value or + * an unchanged value paired with changed partitioning columns fails the query once the callback returns. + * + * history: + * - stable: v2.0.0 + * + * @param info The partition data info handle. + * @param batch_index The batch's ordering position. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_partition_data_set_batch_index( + duckdb_v2_table_function_partition_data_info_handle info, idx_t batch_index, duckdb_v2_error_info_handle *err); + +/*! + * Reports the single value of the partitioning column at the given index, for the batch the exec callback just + * produced. + * + * Every partition reported this way carries exactly one distinct value for the column, so the value serves as both the + * minimum and maximum of the partition's range. The value is borrowed and copied, and must be of the declared type of + * the corresponding result column. Required once for every index in `[0, + * `duckdb_v2_table_function_partition_data_get_partition_column_count()`)` before the callback returns; calling it + * again for the same index overwrites the previous value. The reported value only takes effect together with a changed + * `duckdb_v2_table_function_partition_data_set_batch_index()`: reporting a different value under an unchanged batch + * index fails the query once the callback returns. Fails with `ERROR_INPUT_INVALID` when the index is out of bounds or + * the value's type does not match. + * + * history: + * - stable: v2.0.0 + * + * @param info The partition data info handle. + * @param index The index of the partitioning column being reported. + * @param value The single value of the partitioning column. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_partition_data_set_partition_value( + duckdb_v2_table_function_partition_data_info_handle info, idx_t index, duckdb_v2_value_handle value, + duckdb_v2_error_info_handle *err); + +/*! + * Retrieves the user data set via `duckdb_v2_table_function_set_user_data()`. + * + * history: + * - stable: v2.0.0 + * + * @param info The partitioning info handle. + * @param data Receives the user data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_partitioning_get_user_data( + duckdb_v2_table_function_partitioning_info_handle info, void **data, duckdb_v2_error_info_handle *err); + +/*! + * Retrieves the bind data set by the function's bind callback. + * + * history: + * - stable: v2.0.0 + * + * @param info The partitioning info handle. + * @param data Receives the bind data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_partitioning_get_bind_data( + duckdb_v2_table_function_partitioning_info_handle info, void **data, duckdb_v2_error_info_handle *err); + +/*! + * Returns the number of columns the optimizer is asking about: the candidate `GROUP BY` column set. + * + * Valid indices for `duckdb_v2_table_function_partitioning_get_partition_column_index()` are `0` up to (but excluding) + * this count. + * + * history: + * - stable: v2.0.0 + * + * @param info The partitioning info handle. + * @param count Receives the number of columns in the candidate set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_partitioning_get_partition_column_count( + duckdb_v2_table_function_partitioning_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); + +/*! + * Returns which declared column the candidate `GROUP BY` column at the given index stands for. + * + * The result indexes the columns declared with `duckdb_v2_table_function_bind_add_result_column()`, in declaration + * order. Fails if the index is out of bounds. + * + * history: + * - stable: v2.0.0 + * + * @param info The partitioning info handle. + * @param index The index of the candidate column. + * @param column_index Receives the index of the declared column. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_partitioning_get_partition_column_index( + duckdb_v2_table_function_partitioning_info_handle info, idx_t index, idx_t *column_index, + duckdb_v2_error_info_handle *err); + +/*! + * Reports whether every partition the scan produces carries exactly one distinct value for the candidate `GROUP BY` + * column set. + * + * Only `TABLE_PARTITION_INFO_SINGLE_VALUE_PARTITIONS` unlocks the partitioned aggregate optimization; any other value + * keeps the regular hash aggregate. A callback that never calls this is treated as reporting + * `TABLE_PARTITION_INFO_NOT_PARTITIONED`. Fails with `ERROR_INPUT_INVALID` when partition_info is not one of the enum's + * declared values. + * + * history: + * - stable: v2.0.0 + * + * @param info The partitioning info handle. + * @param partition_info Whether, and how, the scan is partitioned by the candidate column set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_partitioning_set_partition_info( + duckdb_v2_table_function_partitioning_info_handle info, DUCKDB_V2_TABLE_PARTITION_INFO partition_info, + duckdb_v2_error_info_handle *err); + +/*! + * Registers the table function, making it available for use in SQL queries. + * + * The function is registered on the connection's database, visible to every connection, in the connection's + * transaction: inside an explicit transaction, it is invisible to other connections until the transaction commits, and + * a rollback undoes it. Registration requires a name, a bind callback and an exec callback, and rejects a signature + * that declares a return type, since a table function declares the columns it returns from its bind callback. The table + * function must have been created through a factory of the same database. The caller still owns the handle after + * registration and must destroy it with `duckdb_v2_table_function_destroy()`, which does not affect the registered + * function. + * + * history: + * - stable: v2.0.0 + * + * @param conn The connection to register on. + * @param function The table function to register. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_register_table_function(duckdb_v2_connection_handle conn, + duckdb_v2_table_function_handle function, + duckdb_v2_error_info_handle *err); + +/*! + * Registers the table function, making it available for use in SQL queries. + * + * The function is registered on the loading extension's database and attributed to the extension. Use this from the + * extension's load callback. Registration requires a name, a bind callback and an exec callback, and rejects a + * signature that declares a return type, since a table function declares the columns it returns from its bind callback. + * The table function must have been created through a factory of the same database. The caller still owns the handle + * after registration and must destroy it with `duckdb_v2_table_function_destroy()`, which does not affect the + * registered function. + * + * history: + * - stable: v2.0.0 + * + * @param extension The loading extension. + * @param function The table function to register. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_extension_register_table_function(duckdb_v2_extension_handle extension, + duckdb_v2_table_function_handle function, + duckdb_v2_error_info_handle *err); + +/*! + * Destroys the table function, releasing its resources. + * + * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent + * double-destruction. Destroying the handle after registration does not affect the registered function. + * + * history: + * - stable: v2.0.0 + * + * @param function The function to destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_destroy(duckdb_v2_table_function_handle *function); + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Sets the optional claim batch callback of the table function. + * + * With a claim batch callback, a scanning thread works through the scan one batch at a time: the callback claims the + * next batch of work (e.g. the next block of a file) for the thread's local state, typically from the shared global + * state, and the exec callback then produces the rows of that batch only, leaving the output chunk empty once the batch + * is exhausted. A batch is claimed before the exec callback first runs on a thread, and again whenever the exec + * callback leaves the output chunk empty; the scan ends for a thread once the claim batch callback does not claim a + * batch. + * + * Telling the batches apart lets a caller that scans several batches in parallel put their rows back in the order of + * the batches, e.g. a multi-file function registered with `duckdb_v2_connection_register_multi_file_function()` or + * `duckdb_v2_extension_register_multi_file_function()` claims the batches of the function itself, so that the rows of a + * file keep their order also when several threads scan it. + * + * The callback runs while the work of the scan is handed out to the threads, possibly while every other scanning thread + * waits for it. It should therefore only claim the work - e.g. reserve the number of the next block - and leave reading + * it to the exec callback. + * + * history: + * - unstable: v2.0.0 + * + * @param function The function to set the claim batch callback of. + * @param callback The claim batch callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_claim_batch_callback( + duckdb_v2_table_function_handle function, duckdb_v2_table_function_claim_batch_callback_fn callback, + duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the user data set on the table function via `duckdb_v2_table_function_set_user_data()`. + * + * history: + * - unstable: v2.0.0 + * + * @param info The claim batch info handle. + * @param data Receives the user data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_claim_batch_get_user_data( + duckdb_v2_table_function_claim_batch_info_handle info, void **data, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the bind data set by the function's bind callback. + * + * history: + * - unstable: v2.0.0 + * + * @param info The claim batch info handle. + * @param data Receives the bind data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_claim_batch_get_bind_data( + duckdb_v2_table_function_claim_batch_info_handle info, void **data, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the global state set by the function's global init callback. + * + * Shared with every other thread scanning the function; access to it must be synchronized by the function. + * + * history: + * - unstable: v2.0.0 + * + * @param info The claim batch info handle. + * @param data Receives the global state pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_claim_batch_get_global_state( + duckdb_v2_table_function_claim_batch_info_handle info, void **data, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the local state set by the function's local init callback, for the thread claiming a batch. + * + * history: + * - unstable: v2.0.0 + * + * @param info The claim batch info handle. + * @param data Receives the local state pointer for the claiming thread, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_claim_batch_get_local_state( + duckdb_v2_table_function_claim_batch_info_handle info, void **data, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Reports whether the callback claimed a batch for the thread. + * + * A callback that returns without calling this claimed nothing, which ends the scan for the thread. + * + * history: + * - unstable: v2.0.0 + * + * @param info The claim batch info handle. + * @param claimed Whether a batch was claimed. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_claim_batch_set_claimed( + duckdb_v2_table_function_claim_batch_info_handle info, bool claimed, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Sets the optional "get bind info" callback of the table function. + * + * The engine invokes the callback with the bind data of a bound call when it needs to know more about the call than its + * columns. E.g. a multi-file function registered with `duckdb_v2_connection_register_multi_file_function()` or + * `duckdb_v2_extension_register_multi_file_function()` asks the function it wraps for every file it binds: it maps the + * columns of the files onto the columns of its scan by the identifiers the callback reports, and exposes the options it + * reports as the metadata of the reader of the file. The callback may be invoked more than once for the same bind data, + * so it should only report what the bind already determined. + * + * history: + * - unstable: v2.0.0 + * + * @param function The function to set the callback of. + * @param callback The "get bind info" callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_get_bind_info_callback( + duckdb_v2_table_function_handle function, duckdb_v2_table_function_get_bind_info_callback_fn callback, + duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the user data set via `duckdb_v2_table_function_set_user_data()`. + * + * history: + * - unstable: v2.0.0 + * + * @param info The get bind info handle. + * @param data Receives the user data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_get_bind_info_get_user_data( + duckdb_v2_table_function_get_bind_info_info_handle info, void **data, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the bind data the function's bind callback set for the bound call being described. + * + * history: + * - unstable: v2.0.0 + * + * @param info The get bind info handle. + * @param data Receives the bind data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_get_bind_info_get_bind_data( + duckdb_v2_table_function_get_bind_info_info_handle info, void **data, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Attaches an identifier to a result column of the bound call, or to a field nested inside one. + * + * A multi-file reader can then map the columns of every file onto the columns of its scan by identifier rather than by + * name - e.g. by the field ids of a file format that carries them, so that renamed columns and fields are still found. + * An identifier is an INTEGER field id or a VARCHAR name. + * + * The nested field is addressed by a path of child indexes, starting at the result column at `column_index`: a STRUCT + * field by its index, the elements of a LIST or ARRAY by `0`, the keys of a MAP by `0` and its values by `1`, and a + * UNION member by its index. An empty path addresses the result column itself. Setting an identifier again for the same + * column or field replaces it. Fails with `ERROR_INPUT_INVALID` when the column index is out of range, or the path does + * not address a nested field of the column's type. + * + * history: + * - unstable: v2.0.0 + * + * @param info The get bind info handle. + * @param column_index The index of the result column, in the order the bind callback declared the columns. + * @param child_path The path of child indexes addressing the nested field, or null for the column itself. + * @param child_path_length The number of entries in the path, 0 for the column itself. + * @param identifier The identifier, an INTEGER or VARCHAR value. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_get_bind_info_set_column_identifier( + duckdb_v2_table_function_get_bind_info_info_handle info, idx_t column_index, const idx_t *child_path, + idx_t child_path_length, duckdb_v2_value_handle identifier, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Sets an option describing the bound call: a key-value fact about what the call reads, e.g. a property of the file it + * reads. Setting a key again replaces its value. + * + * A multi-file function registered with `duckdb_v2_connection_register_multi_file_function()` or + * `duckdb_v2_extension_register_multi_file_function()` exposes the options of the function it wraps as the metadata of + * the reader of each file, e.g. to a custom multi-file reader of another extension. + * + * history: + * - unstable: v2.0.0 + * + * @param info The get bind info handle. + * @param key The key of the option. Borrowed and copied. + * @param value The value of the option. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_get_bind_info_set_option( + duckdb_v2_table_function_get_bind_info_info_handle info, duckdb_v2_str *key, duckdb_v2_value_handle value, + duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Creates a new multi-file function through a factory. + * + * The function starts out empty: give it a name with `duckdb_v2_multi_file_function_set_name()` and the single-file + * function it wraps with `duckdb_v2_multi_file_function_set_single_file_function()`, then make it available with + * `duckdb_v2_connection_register_multi_file_function()` or `duckdb_v2_extension_register_multi_file_function()`. The + * caller owns the returned handle and must destroy it with `duckdb_v2_multi_file_function_destroy()`, also after + * registration. + * + * The multi-file function can only be registered on the database the factory belongs to. + * + * history: + * - unstable: v2.0.0 + * + * @param factory The factory to create the multi-file function through. + * @param function On success, receives the newly created multi-file function. Owned by the caller. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_multi_file_function_create(duckdb_v2_factory_handle factory, + duckdb_v2_multi_file_function_handle *function, + duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Sets the name of the multi-file function, as SQL will call it. + * + * The name is borrowed and copied. Calling this again replaces the previous name. A name must be set before + * registration. + * + * history: + * - unstable: v2.0.0 + * + * @param function The function to set the name of. + * @param name The name to set. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_multi_file_function_set_name(duckdb_v2_multi_file_function_handle function, + duckdb_v2_str *name, + duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Sets the name of the table function that reads a single file, which the multi-file function wraps. + * + * The single-file function must be registered before the multi-file function is, and must take the file to read as its + * only positional parameter, of type ANY. It is bound once for every file that is read, and receives the file the way a + * multi-file function takes one: as the VARCHAR path of the file when there is nothing more to know about it, and + * otherwise as a STRUCT holding the path in its `filename` field and the options to open the file with in its other + * fields - e.g. the size of the file, as a file system reported it while globbing, or the options a caller passed along + * with the file (`read_x([{'filename': 'f.x', 'file_size': 42}])`). Opening the file with those options, set with + * `duckdb_v2_file_open_options_set_value()`, lets the file system skip work it would otherwise repeat, such as asking a + * remote store for the size of the file. A single-file function that takes a VARCHAR parameter instead is still + * accepted, and only receives the path. Every named parameter the single-file function declares is also a named + * parameter of the multi-file function, forwarded to it as given. Everything that involves several files - globbing, + * lists of files, hive partitioning, the `filename` column, `union_by_name` and the like - is provided by the + * multi-file function on top of it. The name is borrowed and copied. A single-file function must be set before + * registration. + * + * history: + * - unstable: v2.0.0 + * + * @param function The function to set the single-file function of. + * @param name The name of the registered single-file table function. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_multi_file_function_set_single_file_function( + duckdb_v2_multi_file_function_handle function, duckdb_v2_str *name, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Sets how the files the function reads are referred to in messages, e.g. "Avro" or "JSON". + * + * Optional: without it, the files are referred to by the name of the multi-file function. The name is borrowed and + * copied. + * + * history: + * - unstable: v2.0.0 + * + * @param function The function to set the reader type of. + * @param reader_type The reader type. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_multi_file_function_set_reader_type( + duckdb_v2_multi_file_function_handle function, duckdb_v2_str *reader_type, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Sets the file extension of the files the function reads, e.g. "avro". + * + * Optional. With a file extension, a path that names a directory rather than a file reads the files with that extension + * inside it. Without one, the paths passed to the function must match at least one file. The extension is given without + * a leading dot, and is borrowed and copied. + * + * history: + * - unstable: v2.0.0 + * + * @param function The function to set the file extension of. + * @param extension The file extension, without a leading dot. Borrowed and copied. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_multi_file_function_set_file_extension( + duckdb_v2_multi_file_function_handle function, duckdb_v2_str *extension, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Registers the multi-file function, making it available for use in SQL queries. + * + * The function is registered on the connection's database, visible to every connection, in the connection's + * transaction: inside an explicit transaction, it is invisible to other connections until the transaction commits, and + * a rollback undoes it. It can then be called with the path of a single file, a glob pattern, or a list of either, and + * accepts the options every multi-file function accepts (e.g. `filename`, `hive_partitioning`, `union_by_name`) next to + * the named parameters of the single-file function. Registration requires a name and a single-file function, and fails + * when no table function by that name is registered or it does not take the path of the file to read as its only + * positional parameter (see `duckdb_v2_multi_file_function_set_single_file_function()`). The multi-file function must + * have been created through a factory of the same database. The caller still owns the handle after registration and + * must destroy it with `duckdb_v2_multi_file_function_destroy()`, which does not affect the registered function. + * + * history: + * - unstable: v2.0.0 + * + * @param conn The connection to register on. + * @param function The multi-file function to register. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_connection_register_multi_file_function( + duckdb_v2_connection_handle conn, duckdb_v2_multi_file_function_handle function, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Registers the multi-file function, making it available for use in SQL queries. + * + * The function is registered on the loading extension's database and attributed to the extension. Use this from the + * extension's load callback. It can then be called with the path of a single file, a glob pattern, or a list of either, + * and accepts the options every multi-file function accepts (e.g. `filename`, `hive_partitioning`, `union_by_name`) + * next to the named parameters of the single-file function. Registration requires a name and a single-file function, + * and fails when no table function by that name is registered or it does not take the path of the file to read as its + * only positional parameter (see `duckdb_v2_multi_file_function_set_single_file_function()`). The multi-file function + * must have been created through a factory of the same database. The caller still owns the handle after registration + * and must destroy it with `duckdb_v2_multi_file_function_destroy()`, which does not affect the registered function. + * + * history: + * - unstable: v2.0.0 + * + * @param extension The loading extension. + * @param function The multi-file function to register. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_extension_register_multi_file_function( + duckdb_v2_extension_handle extension, duckdb_v2_multi_file_function_handle function, + duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Destroys the multi-file function, releasing its resources. + * + * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent + * double-destruction. Destroying the handle after registration does not affect the registered function. + * + * history: + * - unstable: v2.0.0 + * + * @param function The function to destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_multi_file_function_destroy(duckdb_v2_multi_file_function_handle *function); +#endif + +/* --- Struct definitions for table --- */ + +/* ============================================================================ + * MODULE: value + * ============================================================================ */ + +/* --- Enums for value --- */ + +/* --- Struct forward declarations for value --- */ + +/* --- Types for value --- */ + +/* --- Constants for value --- */ + +/* --- Function pointer typedefs for value --- */ + +/* --- Functions for value --- */ + +/*! + * Destroys a value handle. + * + * Null-safe: passing nullptr or a slot already set to nullptr is a no-op. On success the slot is set to nullptr. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_destroy(duckdb_v2_value_handle *value); + +/*! + * Returns the value as a BOOLEAN. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload as a bool. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_bool(duckdb_v2_value_handle value, bool *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a UTINYINT. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload as a uint8_t. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_utinyint(duckdb_v2_value_handle value, uint8_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a USMALLINT. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload as a uint16_t. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_usmallint(duckdb_v2_value_handle value, uint16_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a UINTEGER. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload as a uint32_t. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_uint(duckdb_v2_value_handle value, uint32_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a UBIGINT. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload as a uint64_t. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_ubigint(duckdb_v2_value_handle value, uint64_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a UHUGEINT. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload as a uint128_t. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_uhugeint(duckdb_v2_value_handle value, duckdb_v2_uhugeint_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a TINYINT. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload as an int8_t. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_tinyint(duckdb_v2_value_handle value, int8_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a SMALLINT. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload as an int16_t. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_smallint(duckdb_v2_value_handle value, int16_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as an INTEGER. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload as an int32_t. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_int(duckdb_v2_value_handle value, int32_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a BIGINT. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload as an int64_t. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_bigint(duckdb_v2_value_handle value, int64_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a HUGEINT. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload as an int128_t. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_hugeint(duckdb_v2_value_handle value, duckdb_v2_hugeint_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a VARCHAR. + * + * The string is borrowed from the value and stays valid until the value is destroyed. That borrow is why this getter + * does not convert: a converted copy would not outlive the call. A value of any other type id, and a NULL value, return + * ERROR_INPUT_INVALID. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload as a UTF-8 string. The returned string is borrowed and valid until the value handle + * is destroyed. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_varchar(duckdb_v2_value_handle value, duckdb_v2_str *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a byte string: a BLOB, or the storage bytes of a BIT or BIGNUM. + * + * The string is borrowed from the value and stays valid until the value is destroyed. That borrow is why this getter + * does not convert: a converted copy would not outlive the call. A value of any other type id, and a NULL value, return + * ERROR_INPUT_INVALID. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload as a byte string. The returned string is borrowed and valid until the value handle is + * destroyed. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_blob(duckdb_v2_value_handle value, duckdb_v2_str *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a FLOAT. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload as a float. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_float(duckdb_v2_value_handle value, float *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a DOUBLE. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload as a double. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_double(duckdb_v2_value_handle value, double *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a DATE. + * + * The payload is days since 1970-01-01, the same unit a vector of this type exposes. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_date(duckdb_v2_value_handle value, int32_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a TIME. + * + * The payload is microseconds since midnight, the same unit a vector of this type exposes. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_time(duckdb_v2_value_handle value, int64_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a TIME_NS. + * + * The payload is nanoseconds since midnight, the same unit a vector of this type exposes. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_time_ns(duckdb_v2_value_handle value, int64_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a TIME_TZ. + * + * The payload packs the time of day and the UTC offset into one integer, in the committed layout: micros in the high 40 + * bits, a biased offset in the low 24. The same form a vector of this type exposes. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_time_tz(duckdb_v2_value_handle value, uint64_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a TIMESTAMP. + * + * The payload is microseconds since 1970-01-01, the same unit a vector of this type exposes. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_timestamp(duckdb_v2_value_handle value, int64_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a TIMESTAMP_SEC. + * + * The payload is seconds since 1970-01-01, the same unit a vector of this type exposes. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_timestamp_sec(duckdb_v2_value_handle value, int64_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a TIMESTAMP_MS. + * + * The payload is milliseconds since 1970-01-01, the same unit a vector of this type exposes. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_timestamp_ms(duckdb_v2_value_handle value, int64_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a TIMESTAMP_NS. + * + * The payload is nanoseconds since 1970-01-01, the same unit a vector of this type exposes. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_timestamp_ns(duckdb_v2_value_handle value, int64_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a TIMESTAMP_TZ. + * + * The payload is microseconds since 1970-01-01, in UTC, the same unit a vector of this type exposes. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_timestamp_tz(duckdb_v2_value_handle value, int64_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a TIMESTAMP_TZ_NS. + * + * The payload is nanoseconds since 1970-01-01, in UTC, the same unit a vector of this type exposes. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_timestamp_tz_ns(duckdb_v2_value_handle value, int64_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as an INTERVAL. + * + * The payload is the (months, days, micros) triple, the same unit a vector of this type exposes. + * + * A value of a different type is converted through the default cast set, on a copy, so reading never alters the value. + * An unsupported conversion returns ERROR_INPUT_INVALID, as does a NULL value. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the payload. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_interval(duckdb_v2_value_handle value, duckdb_v2_interval_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a UUID, in its internal 128-bit form. + * + * The dual of value_create_uuid: the storage form a vector element holds, with the high bit flipped so the integer + * sorts, rather than the canonical byte order. A value of any other type id, and a NULL value, return + * ERROR_INPUT_INVALID. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the internal 128-bit storage form. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_uuid(duckdb_v2_value_handle value, duckdb_v2_hugeint_t *out, + duckdb_v2_error_info_handle *err); + +/*! + * Returns the value as a DECIMAL: its backing integer plus the width and scale of its type. + * + * The integer is the value scaled by 10^scale — the dual of value_create_decimal — widened to 128 bits from whatever + * storage tier the width selects. The integer getters convert, so a DECIMAL read through value_get_bigint is its + * numeric value with the fraction dropped; this one always reports storage instead, together with the scale needed to + * interpret it. A value of any other type id, and a NULL value, return ERROR_INPUT_INVALID. + * + * history: + * - stable: v2.0.0 + * + * @param value The value to read. + * @param out Receives the backing integer, scaled by 10^scale. + * @param out_width Receives the total digit count of the value's type. + * @param out_scale Receives the digits after the decimal point. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_statement_execute(duckdb_v2_connection_handle conn, - duckdb_v2_sql_statement_handle statement, - const duckdb_v2_identifier_t *parameter_names, - const duckdb_v2_value_handle *parameter_values, - idx_t parameter_count, duckdb_v2_result_handle *out_result, +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_decimal(duckdb_v2_value_handle value, duckdb_v2_hugeint_t *out, + uint8_t *out_width, uint8_t *out_scale, duckdb_v2_error_info_handle *err); /*! - * Destroys a result handle. + * Unwraps the logical type carried by a TYPE value. * - * Null-safe: passing nullptr or a slot already set to nullptr is a no-op. Frees the memory the result owns and releases - * the connection for its next query. Safe at any point in the stream's life, though destroying a partially consumed - * result abandons the remaining execution, including side effects not yet applied. Chunks already fetched are - * caller-owned and stay valid. On success the slot is set to nullptr. + * Returns ERROR_INPUT_INVALID unless the value is a non-NULL TYPE value. The returned logical type is caller-owned and + * must be destroyed via logical_type_destroy. * * history: * - stable: v2.0.0 * - * @param result The result to destroy. + * @param value The value to read. + * @param out_type Receives the owned logical type. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_destroy(duckdb_v2_result_handle *result); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_type(duckdb_v2_value_handle value, + duckdb_v2_logical_type_handle *out_type, + duckdb_v2_error_info_handle *err); /*! - * Runs one bounded unit of query execution and returns without blocking. - * - * The streaming primitive: a step does a bounded amount of execution work, mostly without blocking, and returns control - * to the caller, so an event loop stays responsive and can interleave other work between steps. The conveniences — - * result_wait, result_fetch_chunk, result_drain — block, and are for synchronous callers. out_status reports the - * outcome: - * - * - CHUNK: *out_chunk receives a caller-owned chunk (destroy via data_chunk_destroy). Written only for this status; - * nullptr otherwise. - * - WAITING: no chunk yet, but work was done. Transient: keep stepping and it resolves to CHUNK, FINISHED, CANCELLED, - * or an error. Block in result_wait rather than busy-stepping. - * - FINISHED: stream exhausted. Sticky. - * - CANCELLED: query interrupted via connection_interrupt. Sticky. Cancellation is a status here, not an error; - * result_fetch_chunk, which has no status out-param, reports it as ERROR_RUNTIME_INTERRUPT. + * Creates a NULL value of the given logical type. * - * Execution errors come back as the return code plus err, never as a status; out_status is then unspecified and - * *out_chunk is nullptr. Errors are sticky, so later steps report the same code. + * The logical type is borrowed and copied into the value, so the caller can destroy it independently. * * history: * - stable: v2.0.0 * - * @param result The result to step. - * @param out_chunk Receives an owned chunk iff *out_status is CHUNK; set to nullptr otherwise. - * @param out_status Receives the step status. + * @param factory The factory to create with. + * @param type The borrowed logical type to attach to the NULL value. + * @param out_value Receives the new NULL value. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_step(duckdb_v2_result_handle result, - duckdb_v2_data_chunk_handle *out_chunk, - DUCKDB_V2_RESULT_STEP_STATUS *out_status, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_null(duckdb_v2_factory_handle factory, + duckdb_v2_logical_type_handle type, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Blocks until the next chunk is available and returns it. - * - * A convenience over result_step: blocks until a chunk is produced or the stream ends. On success *out_chunk receives a - * caller-owned chunk (destroy via data_chunk_destroy), or nullptr at end-of-stream. End-of-stream is sticky, so later - * calls keep succeeding with *out_chunk set to nullptr. - * - * An interrupted query returns ERROR_RUNTIME_INTERRUPT — the same event result_step reports as status CANCELLED, - * carried on the error channel because this function has no status out-param. + * Creates a BOOLEAN value from a bool. * - * On failure *out_chunk is set to nullptr. Errors are sticky, so later calls report the same code. + * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param result The result to fetch from. - * @param out_chunk Receives an owned chunk, or nullptr at end-of-stream. + * @param factory The factory to create with. + * @param in_value The bool to wrap. + * @param out_value Receives the new BOOLEAN value. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_fetch_chunk(duckdb_v2_result_handle result, - duckdb_v2_data_chunk_handle *out_chunk, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_bool(duckdb_v2_factory_handle factory, bool in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Blocks until result_step can make progress. + * Creates a UTINYINT value from a uint8_t. * - * A convenience over result_step: blocks until a step is worth issuing again, that is, until a unit of execution work - * can run on the calling thread. Never produces or consumes chunks. Waiting on a terminal result — FINISHED, CANCELLED, - * or a sticky error — returns immediately; it is a no-op, never an error. + * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param result The result to wait on. + * @param factory The factory to create with. + * @param in_value The uint8_t to wrap. + * @param out_value Receives the new UTINYINT value. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_wait(duckdb_v2_result_handle result, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_utinyint(duckdb_v2_factory_handle factory, uint8_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Renders the result as a box table, consuming it. - * - * Drains the result into a column data collection and renders it with the same renderer the DuckDB CLI uses, so every - * client displays results identically without reimplementing table formatting. The result is consumed by transfer: the - * slot is set to NULL on success and on failure alike, as with result_to_arrow_stream. A partially consumed result is - * accepted, and the remainder is what gets rendered. - * - * The whole remaining result materializes in memory before rendering. max_rows bounds what is DISPLAYED, not what is - * read, so with limit 0 the footer's row count is exact. A caller who cannot afford full materialization should bound - * the query itself (e.g. LIMIT n) and pass n as limit; the footer then renders "? rows" whenever the result fills that - * bound, since the true total is unknown at that point. - * - * Zero selects the renderer default for each sizing knob: max_rows 20, max_width the probed terminal width or 80 when - * that is unavailable, max_col_width 20. An empty null_value renders NULL cells as the default "NULL" text. + * Creates a USMALLINT value from a uint16_t. * - * The rendered text goes to sink rather than being returned: a box can be large, and this way nothing allocates a - * buffer the caller has to free. The sink is called exactly once with the whole box, so its view carries the full - * length — write it straight to a stream, or copy it into your own string. sink must not be NULL. + * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param result The result to render; consumed and set to NULL. Left intact only when the call rejects null arguments. - * @param max_rows Maximum rows displayed; 0 selects the renderer default (20). - * @param max_width Maximum total width in characters; 0 selects the renderer default. - * @param max_col_width Maximum width of one column; 0 selects the renderer default (20). - * @param null_value Text rendered for NULL cells; empty selects "NULL". - * @param render_mode 0 renders rows (records down the page); 1 renders columns; other values are rejected with - * INVALID_INPUT. - * @param limit The row limit the caller applied to the query before rendering; 0 means none. When the materialized - * result holds exactly this many rows the true total is unknown, so the footer renders "? rows" instead of an exact - * count. - * @param sink Receives the whole rendered box in a single call. Borrowed for the duration of that call; see text_sink - * for the full contract. - * @param user_data Opaque pointer passed through to sink untouched. May be NULL. + * @param factory The factory to create with. + * @param in_value The uint16_t to wrap. + * @param out_value Receives the new USMALLINT value. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_render_box(duckdb_v2_result_handle *result, idx_t max_rows, - idx_t max_width, idx_t max_col_width, duckdb_v2_str null_value, - idx_t render_mode, idx_t limit, duckdb_v2_text_sink_fn sink, - void *user_data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_usmallint(duckdb_v2_factory_handle factory, uint16_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Runs the result to completion and reports the changed-row count. - * - * A convenience over result_step: blocks until the stream is fully consumed, so every side effect is applied. Rows of a - * row-producing result are discarded. For a CHANGED_ROWS result *out_rows_changed receives the affected row count; for - * every other result type, and for a stream whose Count chunk was already consumed, it receives 0. + * Creates a UINTEGER value from a uint32_t. * - * The result type (result_get_result_type) is prepare-time metadata, so a caller can choose between consuming rows and - * draining without inspecting the SQL. Draining an already FINISHED result succeeds. Cancellation surfaces as - * ERROR_RUNTIME_INTERRUPT; errors are sticky, and on failure *out_rows_changed is unspecified. + * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param result The result to drain. - * @param out_rows_changed Receives the affected row count for CHANGED_ROWS results, 0 otherwise. + * @param factory The factory to create with. + * @param in_value The uint32_t to wrap. + * @param out_value Receives the new UINTEGER value. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_drain(duckdb_v2_result_handle result, idx_t *out_rows_changed, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_uint(duckdb_v2_factory_handle factory, uint32_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Returns the shape of the result: query, changed rows, or nothing. - * - * QUERY_RESULT for statements that produce rows (SELECT, RETURNING, EXPLAIN), CHANGED_ROWS for an INSERT, UPDATE, or - * DELETE without RETURNING, NOTHING for DDL and other statements with no row output. + * Creates a UBIGINT value from a uint64_t. * - * Prepare-time metadata: available from statement_execute on, except for a statement that preprocessing expands into a - * group, where it fails with ERROR_INPUT_INVALID until stepping has prepared the row-producing fragment. + * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param result The result. - * @param out_type Receives the result shape. + * @param factory The factory to create with. + * @param in_value The uint64_t to wrap. + * @param out_value Receives the new UBIGINT value. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_get_result_type(duckdb_v2_result_handle result, - DUCKDB_V2_RESULT_TYPE *out_type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_ubigint(duckdb_v2_factory_handle factory, uint64_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Returns the SQL statement type that produced the result. + * Creates a UHUGEINT value from a uint128_t. * - * Prepare-time metadata: available from statement_execute on, except for a statement that preprocessing expands into a - * group, where it fails with ERROR_INPUT_INVALID until stepping has prepared the row-producing fragment. + * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param result The result. - * @param out_type Receives the statement type. + * @param factory The factory to create with. + * @param in_value The uint128_t to wrap. + * @param out_value Receives the new UHUGEINT value. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_get_statement_type(duckdb_v2_result_handle result, - DUCKDB_V2_STATEMENT_TYPE *out_type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_uhugeint(duckdb_v2_factory_handle factory, + const duckdb_v2_uhugeint_t *in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Returns the result's output schema as a single schema handle. - * - * Builds an owned schema of the result's column names and types into *out_schema; destroy it via schema_destroy. - * Prepare-time metadata, available before the first step, or for an expanding statement once stepping has prepared the - * row-producing fragment. Never empty: a non-SELECT reports a single status column, either a BIGINT changed-rows count - * or a BOOLEAN success. - * - * This schema is the authoritative source for the column types of the chunks the result produces; vectors do not carry - * their own type. + * Creates a TINYINT value from an int8_t. * - * *out_schema is set to NULL on failure. + * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param result The result. - * @param out_schema Receives the owned output schema. Destroy via schema_destroy. + * @param factory The factory to create with. + * @param in_value The int8_t to wrap. + * @param out_value Receives the new TINYINT value. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_result_get_schema(duckdb_v2_result_handle result, - duckdb_v2_schema_handle *out_schema, - duckdb_v2_error_info_handle *err); - -/* --- Struct definitions for query_result --- */ - -/* ============================================================================ - * MODULE: table - * ============================================================================ */ - -/* --- Enums for table --- */ - -/* --- Struct forward declarations for table --- */ - -/* --- Types for table --- */ - -/*! - * An owned opaque handle to a custom table function being built. Created with - * `duckdb_v2_table_function_create_with_connection()` or `duckdb_v2_table_function_create_with_extension()`, configured - * with the setter functions (e.g. `duckdb_v2_table_function_set_name()`, - * `duckdb_v2_table_function_set_exec_callback()`, etc.) and the signature obtained via - * `duckdb_v2_table_function_get_signature()`, made available with `duckdb_v2_table_function_register()`, and destroyed - * with `duckdb_v2_table_function_destroy()`. - */ -typedef struct _duckdb_v2_table_function { - void *internal_ptr; -} * duckdb_v2_table_function_handle; +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_tinyint(duckdb_v2_factory_handle factory, int8_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * A borrowed opaque handle to the arguments supplied to a table function during the query preparation "bind" phase. The - * "bind" callback receives this handle and must use it to declare the columns the function returns; it can also inspect - * the arguments given to the function, initialize some constant state and hint at the number of rows the scan will - * produce. + * Creates a SMALLINT value from an int16_t. + * + * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * + * history: + * - stable: v2.0.0 + * + * @param factory The factory to create with. + * @param in_value The int16_t to wrap. + * @param out_value Receives the new SMALLINT value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR */ -typedef struct _duckdb_v2_table_function_bind_info { - void *internal_ptr; -} * duckdb_v2_table_function_bind_info_handle; +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_smallint(duckdb_v2_factory_handle factory, int16_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * A borrowed opaque handle to the arguments supplied to a table function during the global state initialization "init - * global" phase. The "init global" callback receives this handle and can use it to e.g. set up the state shared by - * every thread scanning the function, and to declare how many threads may scan it. + * Creates an INTEGER value from an int32_t. + * + * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * + * history: + * - stable: v2.0.0 + * + * @param factory The factory to create with. + * @param in_value The int32_t to wrap. + * @param out_value Receives the new INTEGER value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR */ -typedef struct _duckdb_v2_table_function_init_global_info { - void *internal_ptr; -} * duckdb_v2_table_function_init_global_info_handle; +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_int(duckdb_v2_factory_handle factory, int32_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * A borrowed opaque handle to the arguments supplied to a table function during the local state initialization "init - * local" phase. The "init local" callback receives this handle and can use it to e.g. set up worker-local state, - * typically by claiming work from the shared global state. + * Creates a BIGINT value from an int64_t. + * + * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * + * history: + * - stable: v2.0.0 + * + * @param factory The factory to create with. + * @param in_value The int64_t to wrap. + * @param out_value Receives the new BIGINT value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR */ -typedef struct _duckdb_v2_table_function_init_local_info { - void *internal_ptr; -} * duckdb_v2_table_function_init_local_info_handle; +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_bigint(duckdb_v2_factory_handle factory, int64_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * A borrowed opaque handle to the arguments supplied to a table function during the execution "exec" phase. The "exec" - * callback receives this handle and can use it to e.g. access the global and local state and write the next batch of - * rows to the output chunk. + * Creates a HUGEINT value from an int128_t. + * + * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * + * history: + * - stable: v2.0.0 + * + * @param factory The factory to create with. + * @param in_value The int128_t to wrap. + * @param out_value Receives the new HUGEINT value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR */ -typedef struct _duckdb_v2_table_function_exec_info { - void *internal_ptr; -} * duckdb_v2_table_function_exec_info_handle; +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_hugeint(duckdb_v2_factory_handle factory, + const duckdb_v2_hugeint_t *in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * A borrowed opaque handle to the arguments supplied to a table function during the "progress" phase. The "progress" - * callback receives this handle and can use it to report how far the scan has advanced. + * Creates a VARCHAR value from a UTF-8 string. + * + * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * + * history: + * - stable: v2.0.0 + * + * @param factory The factory to create with. + * @param in_value The UTF-8 string to wrap. May be null only when len is 0 (empty string). + * @param out_value Receives the new VARCHAR value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR */ -typedef struct _duckdb_v2_table_function_progress_info { - void *internal_ptr; -} * duckdb_v2_table_function_progress_info_handle; +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_varchar(duckdb_v2_factory_handle factory, + const duckdb_v2_str *in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * A borrowed opaque handle to the arguments supplied to a table function during the "filter pushdown" phase of query - * optimization. The "filter pushdown" callback receives this handle and can use it to inspect the filter predicates the - * query applies to the function's rows, and to accept the ones it will apply itself. + * Creates a BLOB value from a byte string. + * + * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. + * + * history: + * - stable: v2.0.0 + * + * @param factory The factory to create with. + * @param in_value The byte string to wrap. May be null only when len is 0 (empty string). + * @param out_value Receives the new BLOB value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. + * @return DUCKDB_V2_ERROR */ -typedef struct _duckdb_v2_table_function_filter_pushdown_info { - void *internal_ptr; -} * duckdb_v2_table_function_filter_pushdown_info_handle; - -/* --- Constants for table --- */ - -/* --- Function pointer typedefs for table --- */ - -typedef void (*duckdb_v2_table_function_bind_callback_fn)(duckdb_v2_table_function_bind_info_handle info, - duckdb_v2_context_handle context, - duckdb_v2_error_info_handle *err); - -typedef void (*duckdb_v2_table_function_init_global_callback_fn)(duckdb_v2_table_function_init_global_info_handle info, - duckdb_v2_context_handle context, - duckdb_v2_error_info_handle *err); - -typedef void (*duckdb_v2_table_function_init_local_callback_fn)(duckdb_v2_table_function_init_local_info_handle info, - duckdb_v2_context_handle context, - duckdb_v2_error_info_handle *err); - -typedef void (*duckdb_v2_table_function_exec_callback_fn)(duckdb_v2_table_function_exec_info_handle info, - duckdb_v2_context_handle context, - duckdb_v2_error_info_handle *err); - -typedef void (*duckdb_v2_table_function_progress_callback_fn)(duckdb_v2_table_function_progress_info_handle info, - duckdb_v2_context_handle context, - duckdb_v2_error_info_handle *err); - -typedef void (*duckdb_v2_table_function_filter_pushdown_callback_fn)( - duckdb_v2_table_function_filter_pushdown_info_handle info, duckdb_v2_context_handle context, - duckdb_v2_error_info_handle *err); - -/* --- Functions for table --- */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_blob(duckdb_v2_factory_handle factory, + const duckdb_v2_str *in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Creates a new table function that will be registered on the connection's database. + * Creates a FLOAT value from a float. * - * The function starts out empty: configure it with the setter functions (e.g. `duckdb_v2_table_function_set_name()`, - * `duckdb_v2_table_function_set_exec_callback()`, etc.) and the signature obtained via - * `duckdb_v2_table_function_get_signature()`, then make it available with `duckdb_v2_table_function_register()`. The - * caller owns the returned handle and must destroy it with `duckdb_v2_table_function_destroy()`, also after - * registration. + * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param connection The connection to create the function in. - * @param function On success, receives the newly created table function. Owned by the caller. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param in_value The float to wrap. + * @param out_value Receives the new FLOAT value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_create_with_connection(duckdb_v2_connection_handle connection, - duckdb_v2_table_function_handle *function, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_float(duckdb_v2_factory_handle factory, float in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Creates a new table function that will be registered on the loading extension's database. + * Creates a DOUBLE value from a double. * - * Use this from an extension load callback, where an extension handle is available. The function starts out empty: - * configure it with the setter functions (e.g. `duckdb_v2_table_function_set_name()`, - * `duckdb_v2_table_function_set_exec_callback()`, etc.) and the signature obtained via - * `duckdb_v2_table_function_get_signature()`, then make it available with `duckdb_v2_table_function_register()`. The - * caller owns the returned handle and must destroy it with `duckdb_v2_table_function_destroy()`, also after - * registration. + * The input is copied in. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param extension The extension to create the function in. - * @param function On success, receives the newly created table function. Owned by the caller. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param in_value The double to wrap. + * @param out_value Receives the new DOUBLE value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_create_with_extension(duckdb_v2_extension_handle extension, - duckdb_v2_table_function_handle *function, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_double(duckdb_v2_factory_handle factory, double in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Sets the name of the table function. + * Creates a TYPE value from a logical type. * - * The name is borrowed and copied. Calling this again replaces the previous name. A name must be set before - * registration. + * The logical type is borrowed and copied into the value, so the caller can destroy it independently. * * history: * - stable: v2.0.0 * - * @param function The function to set the name of. - * @param name The name to set. Borrowed and copied. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param in_type The borrowed logical type to wrap. + * @param out_value Receives the new TYPE value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_name(duckdb_v2_table_function_handle function, - duckdb_v2_str *name, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_type(duckdb_v2_factory_handle factory, + duckdb_v2_logical_type_handle in_type, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Returns the function's signature so it can be configured. + * Creates a DATE value. * - * Add parameters with `duckdb_v2_function_signature_add_parameter()` and set a variadic tail with - * `duckdb_v2_function_signature_set_varargs()`. The signature is modified in place. A table function maps the signature - * onto the two ways SQL passes arguments to a table function: a parameter without a default value becomes a required - * positional argument, a parameter with a default value becomes a named argument the caller may omit. The variadic tail - * extends the positional arguments. A table function declares the columns it returns from its bind callback instead of - * through a return type, so registration rejects a signature whose return type was set with - * `duckdb_v2_function_signature_set_return_type()`. + * The payload is days since 1970-01-01, the same unit value_get_date reports. It is not range-checked here; value_cast + * is the validating path. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param function The function to get the signature of. - * @param sig The returned signature. Borrowed and valid for the lifetime of the function handle. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param in_value The payload to wrap. + * @param out_value Receives the new DATE value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_get_signature(duckdb_v2_table_function_handle function, - duckdb_v2_function_signature_handle *sig, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_date(duckdb_v2_factory_handle factory, int32_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Sets arbitrary user data on the table function. + * Creates a TIME value. * - * Associates an opaque pointer with the function, retrievable from the callbacks via - * `duckdb_v2_table_function_bind_get_user_data()`, `duckdb_v2_table_function_init_global_get_user_data()`, - * `duckdb_v2_table_function_exec_get_user_data()` and their counterparts on the other phases. The opaque handle bundles - * the pointer with an optional destructor, invoked when the data is no longer needed. + * The payload is microseconds since midnight, the same unit value_get_time reports. It is not range-checked here; + * value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param function The function to set the user data of. - * @param data Opaque handle bundling the user data pointer plus an optional destructor. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param in_value The payload to wrap. + * @param out_value Receives the new TIME value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_user_data(duckdb_v2_table_function_handle function, - duckdb_v2_opaque *data, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_time(duckdb_v2_factory_handle factory, int64_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Sets the bind callback of the table function. + * Creates a TIME_NS value. * - * The bind callback is invoked during query planning for each call site of the function. It must declare the columns - * the function returns via `duckdb_v2_table_function_bind_add_result_column()`. It can also inspect the constant - * argument values and set "bind data" that is shared with all later callbacks. A bind callback must be set before - * registration. + * The payload is nanoseconds since midnight, the same unit value_get_time_ns reports. It is not range-checked here; + * value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param function The function to set the bind callback of. - * @param callback The bind callback to set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param in_value The payload to wrap. + * @param out_value Receives the new TIME_NS value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_bind_callback( - duckdb_v2_table_function_handle function, duckdb_v2_table_function_bind_callback_fn callback, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_time_ns(duckdb_v2_factory_handle factory, int64_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Sets the optional global init callback of the table function. + * Creates a TIME_TZ value. * - * The global init callback is invoked once per scan, at the start of execution. It can set "global state" shared by - * every thread scanning the function, and declare how many threads may scan it in parallel via - * `duckdb_v2_table_function_init_global_set_max_threads()`. + * The payload packs the time of day and the UTC offset into one integer, in the committed layout: micros in the high 40 + * bits, a biased offset in the low 24. The same form value_get_time_tz reports. It is not range-checked here; + * value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param function The function to set the global init callback of. - * @param callback The global init callback to set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param in_value The payload to wrap. + * @param out_value Receives the new TIME_TZ value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_init_global_callback( - duckdb_v2_table_function_handle function, duckdb_v2_table_function_init_global_callback_fn callback, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_time_tz(duckdb_v2_factory_handle factory, uint64_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Sets the optional local init callback of the table function. + * Creates a TIMESTAMP value. * - * The local init callback is invoked once per thread that will scan the function. It can set worker-local "local - * state", retrievable from the exec callback, typically derived from the shared global state. + * The payload is microseconds since 1970-01-01, the same unit value_get_timestamp reports. It is not range-checked + * here; value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param function The function to set the local init callback of. - * @param callback The local init callback to set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param in_value The payload to wrap. + * @param out_value Receives the new TIMESTAMP value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_init_local_callback( - duckdb_v2_table_function_handle function, duckdb_v2_table_function_init_local_callback_fn callback, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp(duckdb_v2_factory_handle factory, int64_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Sets the exec callback of the table function. + * Creates a TIMESTAMP_SEC value. * - * The exec callback implements the function's logic: it is invoked repeatedly during query execution and writes the - * next batch of rows to the output chunk, until it produces an empty batch to signal the end of the scan. An exec - * callback must be set before registration. + * The payload is seconds since 1970-01-01, the same unit value_get_timestamp_sec reports. It is not range-checked here; + * value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param function The function to set the exec callback of. - * @param callback The exec callback to set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param in_value The payload to wrap. + * @param out_value Receives the new TIMESTAMP_SEC value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_exec_callback( - duckdb_v2_table_function_handle function, duckdb_v2_table_function_exec_callback_fn callback, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_sec(duckdb_v2_factory_handle factory, int64_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Sets the optional progress callback of the table function. + * Creates a TIMESTAMP_MS value. * - * The progress callback is invoked on demand during execution to report how far the scan has advanced, which the engine - * surfaces as the query's progress. It is invoked concurrently with the exec callback, so it must read the global state - * in a thread-safe way. + * The payload is milliseconds since 1970-01-01, the same unit value_get_timestamp_ms reports. It is not range-checked + * here; value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param function The function to set the progress callback of. - * @param callback The progress callback to set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param in_value The payload to wrap. + * @param out_value Receives the new TIMESTAMP_MS value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_progress_callback( - duckdb_v2_table_function_handle function, duckdb_v2_table_function_progress_callback_fn callback, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_ms(duckdb_v2_factory_handle factory, int64_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Sets whether the table function supports projection pushdown. Defaults to false. + * Creates a TIMESTAMP_NS value. * - * With projection pushdown, the engine asks the function for only the columns a query actually uses: the exec - * callback's output chunk holds one vector per requested column rather than one per column declared in bind, and the - * global init, local init and exec callbacks can look up which declared column each vector stands for via e.g. - * `duckdb_v2_table_function_exec_get_column_index()`. Without it the output chunk always holds every declared column, - * and the engine drops the unused ones itself. + * The payload is nanoseconds since 1970-01-01, the same unit value_get_timestamp_ns reports. It is not range-checked + * here; value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param function The function to configure. - * @param enable Whether the function supports projection pushdown. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param in_value The payload to wrap. + * @param out_value Receives the new TIMESTAMP_NS value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_projection_pushdown(duckdb_v2_table_function_handle function, - bool enable, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_ns(duckdb_v2_factory_handle factory, int64_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Sets the optional filter pushdown callback of the table function. + * Creates a TIMESTAMP_TZ value. * - * The filter pushdown callback is invoked while the query is optimized, after the bind callback and before any init - * callback, with the filter predicates the query applies to the function's rows. Each predicate is a bound expression - * the callback can inspect with the `expression` functions; the callback accepts the predicates it will apply itself - * via `duckdb_v2_table_function_filter_pushdown_accept()`, typically after recording what they select in the bind data. - * The engine then stops applying the accepted predicates and keeps applying the rest above the scan, so accepting a - * predicate is a promise to filter the rows exactly as it would have. The optimizer may invoke the callback more than - * once for the same query, each time with the predicates not yet accepted; it is not invoked when there are none. + * The payload is microseconds since 1970-01-01, in UTC, the same unit value_get_timestamp_tz reports. It is not + * range-checked here; value_cast is the validating path. The returned value is caller-owned; destroy it via + * value_destroy. * * history: * - stable: v2.0.0 * - * @param function The function to set the filter pushdown callback of. - * @param callback The filter pushdown callback to set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param in_value The payload to wrap. + * @param out_value Receives the new TIMESTAMP_TZ value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_filter_pushdown_callback( - duckdb_v2_table_function_handle function, duckdb_v2_table_function_filter_pushdown_callback_fn callback, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_tz(duckdb_v2_factory_handle factory, int64_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the user data set via `duckdb_v2_table_function_set_user_data()`. + * Creates a TIMESTAMP_TZ_NS value. + * + * The payload is nanoseconds since 1970-01-01, in UTC, the same unit value_get_timestamp_tz_ns reports. It is not + * range-checked here; value_cast is the validating path. The returned value is caller-owned; destroy it via + * value_destroy. * * history: * - stable: v2.0.0 * - * @param info The bind info handle. - * @param data Receives the user data pointer, or null if none was set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param in_value The payload to wrap. + * @param out_value Receives the new TIMESTAMP_TZ_NS value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_bind_get_user_data(duckdb_v2_table_function_bind_info_handle info, - void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_tz_ns(duckdb_v2_factory_handle factory, int64_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Sets the function's "bind data" from the bind callback. + * Creates a INTERVAL value. * - * The bind data is stored with the bound call site and retrievable from every later callback. The opaque handle bundles - * the pointer with an optional destructor, invoked when the bind data is no longer needed, and an optional equality - * callback used when comparing two bound call sites; without one, pointer equality is used. + * The payload is the (months, days, micros) triple, the same unit value_get_interval reports. It is not range-checked + * here; value_cast is the validating path. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param info The bind info handle. - * @param data Opaque handle bundling the bind data pointer plus optional destructor and equality callbacks. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param in_value The payload to wrap. + * @param out_value Receives the new INTERVAL value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_bind_set_bind_data(duckdb_v2_table_function_bind_info_handle info, - duckdb_v2_opaque *data, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_interval(duckdb_v2_factory_handle factory, + const duckdb_v2_interval_t *in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Returns the number of arguments of the call site being bound. + * Creates a DECIMAL value from its backing integer. * - * The arguments are presented in signature order: one for every parameter declared with - * `duckdb_v2_function_signature_add_parameter()`, followed by any variadic tail arguments. A parameter the call site - * omitted is still present, carrying the default value declared for it, so the count only varies with the length of the - * variadic tail. Valid indices for `duckdb_v2_table_function_bind_get_arg_type()` and - * `duckdb_v2_table_function_bind_get_arg_value()` are [0, count). + * The value is scaled by 10^scale, so (18500, 18, 3) is 18.500. width is the total digit count and must be 1..38; scale + * is the number of digits after the point and must not exceed width. Violating either returns ERROR_INPUT_INVALID, as + * does a value too wide for the storage tier the width selects. The value itself is not range-checked against the + * width; value_cast is the validating path. * * history: * - stable: v2.0.0 * - * @param info The bind info handle. - * @param count Receives the number of arguments. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param in_value The backing integer, scaled by 10^scale. + * @param width Total digit count, 1..38. + * @param scale Digits after the decimal point; must not exceed width. + * @param out_value Receives the new DECIMAL value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_bind_get_arg_count(duckdb_v2_table_function_bind_info_handle info, - idx_t *count, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_decimal(duckdb_v2_factory_handle factory, + const duckdb_v2_hugeint_t *in_value, uint8_t width, + uint8_t scale, duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the type of the argument at the given index. + * Creates a UUID value from its internal 128-bit form. * - * Fails if the index is out of bounds. The returned type is owned by the caller and must be destroyed via - * `duckdb_v2_logical_type_destroy()`. + * The payload is the storage form a vector element holds, not the canonical byte order: the high bit is flipped so the + * integer sorts. To build one from canonical text, cast a VARCHAR with value_cast instead, which is also how a UUID + * renders back. * * history: * - stable: v2.0.0 * - * @param info The bind info handle. - * @param index The index of the argument to get the type of. - * @param type Receives the argument type. Owned by the caller; destroy via `duckdb_v2_logical_type_destroy()`. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param in_value The internal 128-bit storage form. + * @param out_value Receives the new UUID value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_bind_get_arg_type(duckdb_v2_table_function_bind_info_handle info, - idx_t index, - duckdb_v2_logical_type_handle *type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_uuid(duckdb_v2_factory_handle factory, + const duckdb_v2_hugeint_t *in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the constant value of the argument at the given index. + * Creates a BIT value from its wire bytes. * - * The arguments of a table function are always constants, folded before the bind callback runs, so this never fails for - * an index in bounds; it does fail when the index is out of bounds. The value may be NULL. The returned value is owned - * by the caller and must be destroyed via `duckdb_v2_value_destroy()`. + * The wire form is a mandatory padding-header byte — the count of leading bits in the first data byte that are not part + * of the bit string — followed by the data bytes, so the input must be at least 1 byte. These are the same bytes + * value_get_blob reports back. * * history: * - stable: v2.0.0 * - * @param info The bind info handle. - * @param index The index of the argument to get the value of. - * @param value Receives the constant value. Owned by the caller; destroy via `duckdb_v2_value_destroy()`. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param in_value The wire bytes, a padding header byte followed by data. Must be at least 1 byte. + * @param out_value Receives the new BIT value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_bind_get_arg_value(duckdb_v2_table_function_bind_info_handle info, - idx_t index, duckdb_v2_value_handle *value, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_bit(duckdb_v2_factory_handle factory, const duckdb_v2_str *in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Declares one of the columns the function returns. + * Creates a BIGNUM value from its storage bytes. * - * Call this once per column, in order: the columns declared here are the columns of the table the function produces, - * and the vectors of the output chunk the exec callback fills follow the same order. At least one column must be - * declared. The type must be a fully defined concrete type; ANY is rejected, as a result column carries data. The name - * and type are borrowed and copied. + * The bytes are opaque storage, as produced by bignum_encode from a magnitude and a sign flag; a negative is stored + * bit-inverted behind a header, so these are not the magnitude bytes. The header plus at least one magnitude byte means + * the input must exceed 3 bytes. These are the same bytes value_get_blob reports back, for bignum_decode to translate. * * history: * - stable: v2.0.0 * - * @param info The bind info handle. - * @param name The name of the column. Borrowed and copied. - * @param type The type of the column. Borrowed and copied. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param in_value The opaque storage bytes from bignum_encode. Must exceed 3 bytes. + * @param out_value Receives the new BIGNUM value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_bind_add_result_column( - duckdb_v2_table_function_bind_info_handle info, duckdb_v2_identifier_t name, duckdb_v2_logical_type_handle type, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_bignum(duckdb_v2_factory_handle factory, + const duckdb_v2_str *in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Sets the estimated number of rows the scan will produce. + * Creates a LIST value from its elements. * - * The estimate is a hint for the optimizer, not a limit: producing a different number of rows is not an error. Without - * it, the optimizer falls back on its own defaults. + * The child type is the common type of the elements, resolved by the same rule a SQL list literal follows, and every + * element is cast to it; a set with no common type surfaces the cast error. Pass child_type to name the type instead, + * which is also how an empty list is built: with child_type NULL, an empty element array has no type to resolve and + * returns ERROR_INPUT_INVALID. + * + * The elements are borrowed and copied in, and a NULL element becomes a typed NULL. The type is rebuilt from its child, + * so an alias on the outer LIST type is not preserved; value_cast is the alias-preserving path. * * history: * - stable: v2.0.0 * - * @param info The bind info handle. - * @param cardinality The estimated number of rows. - * @param is_exact Whether the estimate is exact, which also makes it an upper bound on the row count. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param child_type Optional. The element type. Pass NULL to resolve it from the elements. + * @param children An array of child_count elements. Borrowed (copied in). Pass NULL when child_count is 0. + * @param child_count The number of elements. + * @param out_value Receives the new composite value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_bind_set_cardinality( - duckdb_v2_table_function_bind_info_handle info, idx_t cardinality, bool is_exact, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_list(duckdb_v2_factory_handle factory, + duckdb_v2_logical_type_handle child_type, + const duckdb_v2_value_handle *children, idx_t child_count, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the user data set via `duckdb_v2_table_function_set_user_data()`. + * Creates an ARRAY value from its elements, sized by their count. + * + * The child type is resolved exactly as for value_create_list, and child_type names it explicitly the same way. The + * minimum array size is 1, so an empty element array returns ERROR_INPUT_INVALID whether or not child_type is given. + * + * The elements are borrowed and copied in, and a NULL element becomes a typed NULL. * * history: * - stable: v2.0.0 * - * @param info The global init info handle. - * @param data Receives the user data pointer, or null if none was set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param child_type Optional. The element type. Pass NULL to resolve it from the elements. + * @param children An array of child_count elements. Borrowed (copied in). Pass NULL when child_count is 0. + * @param child_count The number of elements. + * @param out_value Receives the new composite value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_global_get_user_data( - duckdb_v2_table_function_init_global_info_handle info, void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_array(duckdb_v2_factory_handle factory, + duckdb_v2_logical_type_handle child_type, + const duckdb_v2_value_handle *children, idx_t child_count, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the bind data set by the function's bind callback. + * Creates a STRUCT value from named fields, in order. + * + * Each field keeps its own child's type, so there is nothing to resolve across them; names and children are parallel + * arrays of field_count entries. An empty field list builds the empty struct, which is a real type. + * + * The names and children are borrowed and copied in, and a NULL child becomes a typed NULL. Duplicate field names are + * rejected. * * history: * - stable: v2.0.0 * - * @param info The global init info handle. - * @param data Receives the bind data pointer, or null if none was set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param names An array of field_count field names. Pass NULL when field_count is 0. + * @param children An array of field_count field values. Borrowed (copied in). Pass NULL when field_count is 0. + * @param field_count The number of fields. + * @param out_value Receives the new composite value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_global_get_bind_data( - duckdb_v2_table_function_init_global_info_handle info, void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_struct(duckdb_v2_factory_handle factory, + const duckdb_v2_identifier_t *names, + const duckdb_v2_value_handle *children, idx_t field_count, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Sets the function's "global state" from the global init callback. + * Creates a TUPLE value from positional fields. * - * The global state lives for the duration of the scan and is retrievable from the local init, exec and progress - * callbacks. Every thread scanning the function shares it, so the function must synchronize its own access to it. The - * opaque handle bundles the pointer with an optional destructor, invoked when the scan is done with the state. + * TUPLE is the unnamed struct: the same positional children as value_create_struct, under a distinct type id and with + * no names. Each field keeps its own child's type. An empty field list builds the empty tuple, which is a real type. + * + * The children are borrowed and copied in, and a NULL child becomes a typed NULL. * * history: * - stable: v2.0.0 * - * @param info The global init info handle. - * @param data Opaque handle bundling the global state pointer plus an optional destructor. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param children An array of field_count field values. Borrowed (copied in). Pass NULL when field_count is 0. + * @param field_count The number of fields. + * @param out_value Receives the new composite value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_global_set_global_state( - duckdb_v2_table_function_init_global_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_tuple(duckdb_v2_factory_handle factory, + const duckdb_v2_value_handle *children, idx_t field_count, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err); /*! - * Sets how many threads may scan the function in parallel. + * Creates a MAP value from parallel key and value arrays. * - * Defaults to 1, a single-threaded scan. The engine creates at most this many local states, and therefore runs at most - * this many exec callbacks concurrently. It is an upper bound, not a request: the engine may use fewer threads. + * The key and value types are the common types of the keys and of the values, resolved as for value_create_list, and + * every entry is cast to them. Pass key_type and value_type to name them instead, which is also how an empty map is + * built: with both NULL, empty key and value arrays have no types to resolve and return ERROR_INPUT_INVALID. + * + * Keys and values are borrowed and copied in, and the two arrays must be the same length. Keys must be non-NULL and + * unique; both are enforced. * * history: * - stable: v2.0.0 * - * @param info The global init info handle. - * @param max_threads The maximum number of threads. Must be at least 1. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory to create with. + * @param key_type Optional. The key type. Pass NULL to resolve it from the keys. + * @param value_type Optional. The value type. Pass NULL to resolve it from the values. + * @param keys An array of entry_count keys. Borrowed (copied in). Pass NULL when entry_count is 0. + * @param values An array of entry_count values, parallel to keys. Borrowed (copied in). Pass NULL when entry_count is + * 0. + * @param entry_count The number of entries. + * @param out_value Receives the new composite value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_global_set_max_threads( - duckdb_v2_table_function_init_global_info_handle info, idx_t max_threads, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_create_map( + duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle key_type, duckdb_v2_logical_type_handle value_type, + const duckdb_v2_value_handle *keys, const duckdb_v2_value_handle *values, idx_t entry_count, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); /*! - * Returns the number of columns the scan produces. + * Returns the number of child values of a value. * - * With projection pushdown (see `duckdb_v2_table_function_set_projection_pushdown()`) this is the number of columns the - * query uses, which is the number of vectors in the exec callback's output chunk; without it, it is the number of - * columns declared in bind. Valid indices for `duckdb_v2_table_function_init_global_get_column_index()` are `0` up to - * (but excluding) this count. + * Per type kind: the element count for LIST and ARRAY; the field count for STRUCT and TUPLE; twice the entry count for + * MAP, whose children alternate key, value; and 2 for UNION, the tag plus the active member. Everything else reports 0, + * NULL values of any nested type included. * * history: * - stable: v2.0.0 * - * @param info The global init info handle. - * @param count Receives the number of columns the scan produces. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param value The value to read. + * @param out_count Receives the number of children. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_global_get_column_count( - duckdb_v2_table_function_init_global_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_child_count(duckdb_v2_value_handle value, idx_t *out_count, + duckdb_v2_error_info_handle *err); /*! - * Returns which declared column the scan's column at the given index stands for. + * Returns one child of a composite value as an owned copy. + * + * LIST and ARRAY children are the elements. STRUCT and TUPLE children are the fields in declared order, and a STRUCT's + * field names come from its type, via logical_type_get_param. MAP children alternate key, value, symmetric with + * value_create_map. UNION children are [0] the tag, as a UTINYINT value, and [1] the active member, because a union + * value holds only that one member. * - * The result indexes the columns declared with `duckdb_v2_table_function_bind_add_result_column()`, in declaration - * order: the exec callback fills the output chunk's vector at `index` with that column's data. Without projection - * pushdown the mapping is the identity. Fails if the index is out of bounds. + * Note the divergence from vector_get_child, which descends structurally and so exposes [0] = tag and [1..N] = ALL + * members. Code written to descend generically over both must account for that difference. + * + * An out-of-range index returns ERROR_INPUT_INVALID, as does any index on a non-composite or NULL value. The returned + * value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param info The global init info handle. - * @param index The index of the column in the scan's output. - * @param column_index Receives the index of the declared column. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param value The value to read. + * @param index The child index, in [0, child_count). + * @param out_child Receives the owned child value. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_global_get_column_index( - duckdb_v2_table_function_init_global_info_handle info, idx_t index, idx_t *column_index, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_child(duckdb_v2_value_handle value, idx_t index, + duckdb_v2_value_handle *out_child, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the user data set via `duckdb_v2_table_function_set_user_data()`. + * Casts a value to a target type, using a factory. + * + * The conversion is the SQL-faithful, non-strict one, registered custom casts included, and a cast failure surfaces + * from the call. Together with a VARCHAR built through value_create_varchar, this constructs any value from text, + * extension values included; casting a member value to a union type, or a VARCHAR to an enum type, is the sanctioned + * way to build UNION and ENUM values. + * + * A connection's or callback's factory casts with that client's cast functions and settings (e.g. TimeZone). An + * instance's factory supports only the built-in casts, with default settings. + * + * The input value and target type are borrowed. The returned value is caller-owned; destroy it via value_destroy. * * history: * - stable: v2.0.0 * - * @param info The local init info handle. - * @param data Receives the user data pointer, or null if none was set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param factory The factory supplying the cast functions. + * @param value The borrowed value to cast. + * @param target_type The borrowed target logical type. + * @param out_value Receives the owned cast result. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_local_get_user_data( - duckdb_v2_table_function_init_local_info_handle info, void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_cast(duckdb_v2_factory_handle factory, duckdb_v2_value_handle value, + duckdb_v2_logical_type_handle target_type, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err); /*! - * Retrieves the bind data set by the function's bind callback. + * Returns whether the value is NULL. * * history: * - stable: v2.0.0 * - * @param info The local init info handle. - * @param data Receives the bind data pointer, or null if none was set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param value The value to read. + * @param out_is_null Receives true if the value is NULL, false otherwise. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_local_get_bind_data( - duckdb_v2_table_function_init_local_info_handle info, void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_is_null(duckdb_v2_value_handle value, bool *out_is_null, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the global state set by the function's global init callback. + * Returns the logical type of the value. * - * Shared with every other thread scanning the function; access to it must be synchronized by the function. + * The returned logical type is caller-owned; destroy it via logical_type_destroy. * * history: * - stable: v2.0.0 * - * @param info The local init info handle. - * @param data Receives the global state pointer, or null if none was set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param value The value to read. + * @param out_type Receives the owned logical type. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_local_get_global_state( - duckdb_v2_table_function_init_local_info_handle info, void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_get_logical_type(duckdb_v2_value_handle value, + duckdb_v2_logical_type_handle *out_type, + duckdb_v2_error_info_handle *err); /*! - * Sets the function's worker-local "local state" from the local init callback. + * Renders the value as a human-readable string. Diagnostic only. * - * The local state is associated with the executing thread for the duration of the scan and retrievable from the exec - * callback via `duckdb_v2_table_function_exec_get_local_state()`. No other thread observes it, so it needs no - * synchronization. The opaque handle bundles the pointer with an optional destructor, invoked when the local state is - * no longer needed. + * Writes into a caller-supplied buffer, so nothing is allocated on the caller's behalf and nothing has to be freed. + * + * Pass out_string = NULL to size the buffer without rendering into it: out_length then receives the length, and + * out_capacity is ignored. With out_string != NULL, out_capacity must be at least out_length + 1, or the call returns + * ERROR_INPUT_OBJECT_SIZE with out_length set to the required length and out_string left untouched. + * + * out_length never counts the terminator, but a successful write always appends one, so the buffer is usable as a C + * string. * * history: * - stable: v2.0.0 * - * @param info The local init info handle. - * @param data Opaque handle bundling the local state pointer plus an optional destructor. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param value The value to read. + * @param out_string Caller-owned buffer receiving the text plus a null terminator, or NULL to only report the required + * length in out_length. + * @param out_capacity Bytes available in out_string, terminator included. Ignored when out_string is NULL. + * @param out_length Receives the text length excluding the null terminator — written on success and on + * ERROR_INPUT_OBJECT_SIZE. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via error_info_destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_local_set_local_state( - duckdb_v2_table_function_init_local_info_handle info, duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_value_to_string(duckdb_v2_value_handle value, char *out_string, + idx_t out_capacity, idx_t *out_length, + duckdb_v2_error_info_handle *err); + +/* --- Struct definitions for value --- */ + +/* ============================================================================ + * MODULE: arrow + * ============================================================================ */ + +/* --- Enums for arrow --- */ + +/* --- Struct forward declarations for arrow --- */ + +/* --- Types for arrow --- */ /*! - * Returns the number of columns the scan produces. + * Converts Arrow arrays into DuckDB data chunks, against one resolved ArrowSchema. Create it with + * `duckdb_v2_arrow_importer_create()`, which resolves every column's DuckDB type once, so the same importer serves any + * number of arrays of that shape. `duckdb_v2_arrow_importer_get_schema()` reports the resolved DuckDB schema. + * `duckdb_v2_arrow_importer_append()` takes an array, `duckdb_v2_arrow_importer_next_chunk()` produces chunks from it, + * and `duckdb_v2_arrow_importer_destroy()` frees the importer. * - * With projection pushdown (see `duckdb_v2_table_function_set_projection_pushdown()`) this is the number of columns the - * query uses, which is the number of vectors in the exec callback's output chunk; without it, it is the number of - * columns declared in bind. Valid indices for `duckdb_v2_table_function_init_local_get_column_index()` are `0` up to - * (but excluding) this count. + * The importer borrows the context it was created with and must not outlive it. One array is in flight at a time, and + * an importer must not be used from two threads at once. + */ +typedef struct _duckdb_v2_arrow_importer { + void *internal_ptr; +} * duckdb_v2_arrow_importer_handle; + +/*! + * Converts DuckDB data chunks into Arrow arrays, for one fixed list of columns. Create it with + * `duckdb_v2_arrow_exporter_create()`, which captures the session's Arrow settings and resolves the extension types + * once. `duckdb_v2_arrow_exporter_get_schema()` reports the Arrow schema. `duckdb_v2_arrow_exporter_append()` takes a + * chunk, `duckdb_v2_arrow_exporter_next_array()` produces arrays from it, and `duckdb_v2_arrow_exporter_destroy()` + * frees the exporter. * - * history: - * - stable: v2.0.0 + * An exporter must not be used from two threads at once. + */ +typedef struct _duckdb_v2_arrow_exporter { + void *internal_ptr; +} * duckdb_v2_arrow_exporter_handle; + +/*! + * A query result that produces Arrow arrays. Works like a result, which produces data chunks. Create it with + * `duckdb_v2_statement_execute_arrow()` or `duckdb_v2_prepared_statement_execute_arrow()`. * - * @param info The local init info handle. - * @param count Receives the number of columns the scan produces. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. - * @return DUCKDB_V2_ERROR + * - Use it from one thread at a time. + * - `duckdb_v2_connection_interrupt()` cancels it: `duckdb_v2_arrow_result_step()` reports `CANCELLED`, and + * `duckdb_v2_arrow_result_fetch_array()` and a stream's `get_next` fail. + * - Arrays and schemas it hands out belong to the caller and stay valid after the result is destroyed. */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_init_local_get_column_count( - duckdb_v2_table_function_init_local_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); +typedef struct _duckdb_v2_arrow_result { + void *internal_ptr; +} * duckdb_v2_arrow_result_handle; + +/* --- Constants for arrow --- */ + +/* --- Function pointer typedefs for arrow --- */ + +/* --- Functions for arrow --- */ /*! - * Returns which declared column the scan's column at the given index stands for. + * Resolves an Arrow schema into a reusable importer. * - * The result indexes the columns declared with `duckdb_v2_table_function_bind_add_result_column()`, in declaration - * order: the exec callback fills the output chunk's vector at `index` with that column's data. Without projection - * pushdown the mapping is the identity. Fails if the index is out of bounds. + * Works out every column's DuckDB logical type and the Arrow type information the conversion needs, once, so any number + * of arrays of that shape can be imported without re-reading the schema. `schema` is read, not consumed: the caller + * keeps ownership and still releases it. + * + * `batch_size` caps the rows per produced chunk. A long array is split across several chunks. Rows left over that do + * not fill a batch are held back and joined with the next array, unless the append asked to flush. Pass 0 for no + * maximum: each array becomes one chunk, however long it is. + * + * Resolving reads the catalog for extension types, so `context` must have an active transaction. The importer keeps + * using that context for every conversion and must not outlive it. Within one connection the context is the same + * throughout, so an importer created in a bind callback is usable from the matching exec callback. `*out_importer` is + * set to NULL on failure. * * history: * - stable: v2.0.0 * - * @param info The local init info handle. - * @param index The index of the column in the scan's output. - * @param column_index Receives the index of the declared column. + * @param context The context used to resolve the Arrow types, extension types included. + * @param schema The schema to resolve. Read, not consumed; the caller keeps ownership. + * @param batch_size Maximum rows per produced chunk, or 0 for no maximum. + * @param out_importer On success, receives the new importer. Owned by the caller; destroy via + * `duckdb_v2_arrow_importer_destroy()`. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR -duckdb_v2_table_function_init_local_get_column_index(duckdb_v2_table_function_init_local_info_handle info, idx_t index, - idx_t *column_index, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_importer_create(duckdb_v2_context_handle context, + struct ArrowSchema *schema, idx_t batch_size, + duckdb_v2_arrow_importer_handle *out_importer, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the user data set via `duckdb_v2_table_function_set_user_data()`. + * Returns the resolved DuckDB schema. + * + * Writes an owned schema handle with the DuckDB name and logical type of every column the importer resolved. This is + * how a caller learns the DuckDB shape of an ArrowSchema, to declare a table function's result columns for instance, + * without reimplementing the mapping from Arrow format strings to logical types. + * + * The fields were resolved at creation, so this reads no catalog and needs no transaction. `*out_schema` is set to NULL + * on failure. * * history: * - stable: v2.0.0 * - * @param info The exec info handle. - * @param data Receives the user data pointer, or null if none was set. + * @param importer The importer to read. + * @param out_schema On success, receives an owned schema. Destroy via `duckdb_v2_schema_destroy()`. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_exec_get_user_data(duckdb_v2_table_function_exec_info_handle info, - void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_importer_get_schema(duckdb_v2_arrow_importer_handle importer, + duckdb_v2_schema_handle *out_schema, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the bind data set by the function's bind callback. + * Gives the importer one array to convert. + * + * Take the chunks with `duckdb_v2_arrow_importer_next_chunk()` until that returns NULL. Appending while the previous + * array still has rows left is rejected with `ERROR_INPUT_INVALID`. An array whose shape does not match the resolved + * schema -- a different child count, a child whose length differs from the array's, a null or already-released child -- + * is rejected the same way, before anything is read. + * + * `flush` marks the end of the input: rows that do not fill a batch then come out as a final short chunk instead of + * being held back for the next array. Pass NULL for `array` with `flush` set to release the held rows without supplying + * more input. + * + * `consume` decides what happens to the caller's array. + * + * When true, the importer takes over the array and sets its `release` to NULL; the caller must not release it + * afterwards. The produced chunks reference the Arrow buffers directly, without copying, and keep them alive, so the + * chunks stay valid after the importer is destroyed. Prefer this path. + * + * When false, the caller keeps the array and must keep it valid until the drain finishes. The produced chunks are + * copies, so they do not depend on the array, at the cost of one copy per chunk. + * + * Either way, a chunk that joins rows held back from the previous array is a copy, since it cannot reference two + * arrays. + * + * Only the default, dictionary-encoded and run-end-encoded Arrow layouts are supported. Any other layout reports + * `ERROR_QUERY_NOT_IMPLEMENTED` when the column is converted. * * history: * - stable: v2.0.0 * - * @param info The exec info handle. - * @param data Receives the bind data pointer, or null if none was set. + * @param importer The importer to feed. + * @param array The array to convert. Its `release` is set to NULL when `consume` is true. + * @param consume True to hand the array over for a zero-copy import; false to keep it, in which case every produced + * chunk is a copy. + * @param flush True to mark the end of the input, releasing held rows as a final short chunk. Pass NULL for `array` + * with this set to flush without supplying more input. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_exec_get_bind_data(duckdb_v2_table_function_exec_info_handle info, - void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_importer_append(duckdb_v2_arrow_importer_handle importer, + struct ArrowArray *array, bool consume, bool flush, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the global state set by the function's global init callback. + * Produces the next chunk of the appended array, or NULL once the array is drained. + * + * Call it in a loop until `*out_chunk` is NULL. An importer with no array appended also returns NULL. Each chunk holds + * at most the importer's `batch_size` rows, or the whole array when that is 0. A chunk may start with rows held back + * from the previous array, and rows that do not fill a batch are held back in turn unless the append asked to flush. So + * NULL means the array has been read, not that all of its rows have come out. * - * Shared with every other thread scanning the function; access to it must be synchronized by the function. + * The conversion runs under the context the importer was created with, which must still be alive. `*out_chunk` is set + * to NULL on failure. * * history: * - stable: v2.0.0 * - * @param info The exec info handle. - * @param data Receives the global state pointer, or null if none was set. + * @param importer The importer to drain. + * @param out_chunk On success, receives the next chunk, or NULL once the array is drained. Destroy via + * `duckdb_v2_data_chunk_destroy()`. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_exec_get_global_state( - duckdb_v2_table_function_exec_info_handle info, void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_importer_next_chunk(duckdb_v2_arrow_importer_handle importer, + duckdb_v2_data_chunk_handle *out_chunk, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the worker-local local state set by the function's local init callback. + * Destroys an importer. + * + * Null-safe: passing NULL, or a slot already set to NULL, is a no-op. Chunks already produced stay valid, including the + * zero-copy ones, which keep the Arrow buffers alive themselves. An array appended with `consume` true and not fully + * drained is released here. Rows held back for a next array are dropped. On success the slot is set to NULL. * * history: * - stable: v2.0.0 * - * @param info The exec info handle. - * @param data Receives the local state pointer for the executing thread, or null if none was set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param importer The importer to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_exec_get_local_state( - duckdb_v2_table_function_exec_info_handle info, void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_importer_destroy(duckdb_v2_arrow_importer_handle *importer); /*! - * Retrieves the output chunk the exec callback must write the next batch of rows into. + * Creates an exporter for one fixed list of columns. * - * The chunk holds one vector per column declared with `duckdb_v2_table_function_bind_add_result_column()`, in the same - * order; reach them with `duckdb_v2_data_chunk_get_vector()`. The chunk starts out empty on every invocation: write the - * rows, then declare how many there are with `duckdb_v2_vector_set_size()` on the first vector, which the engine takes - * as the batch's row count and propagates to the other vectors. Producing an empty batch signals the end of the scan, - * after which the callback is not invoked again on that thread. Borrowed; valid only for the duration of the callback. + * Resolves the extension types and captures the session's Arrow settings, so every array this exporter produces matches + * the schema `duckdb_v2_arrow_exporter_get_schema()` reports, even if a setting changes afterwards. + * + * `batch_size` caps the rows per produced array. A long chunk is split across several arrays. Rows left over that do + * not fill a batch are held back and joined with the next chunk, unless the append asked to flush. Pass 0 for no + * maximum: each chunk becomes one array, however long it is. + * + * `types` and `names` are parallel arrays of `count` entries, borrowed and copied; they may be NULL only when `count` + * is 0. Resolving reads the catalog, so `context` must have an active transaction. `*out_exporter` is set to NULL on + * failure. * * history: * - stable: v2.0.0 * - * @param info The exec info handle. - * @param chunk Receives the borrowed output chunk to write into. + * @param context The context whose Arrow settings are captured and whose transaction resolves the types. + * @param types An array of `count` column types. May be NULL only when `count` is 0. + * @param names An array of `count` column names, parallel to `types`. Each name must be valid UTF-8. May be NULL only + * when `count` is 0. + * @param count The number of columns, being the length of both `types` and `names`. + * @param batch_size Maximum rows per produced array, or 0 for no maximum. + * @param out_exporter On success, receives the new exporter. Owned by the caller; destroy via + * `duckdb_v2_arrow_exporter_destroy()`. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR -duckdb_v2_table_function_exec_get_output_chunk(duckdb_v2_table_function_exec_info_handle info, - duckdb_v2_data_chunk_handle *chunk, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_exporter_create(duckdb_v2_context_handle context, + const duckdb_v2_logical_type_handle *types, + const duckdb_v2_str *names, idx_t count, idx_t batch_size, + duckdb_v2_arrow_exporter_handle *out_exporter, + duckdb_v2_error_info_handle *err); /*! - * Returns the number of columns the scan produces. + * Returns the Arrow schema of the arrays this exporter produces. * - * With projection pushdown (see `duckdb_v2_table_function_set_projection_pushdown()`) this is the number of columns the - * query uses, which is the number of vectors in the exec callback's output chunk; without it, it is the number of - * columns declared in bind. Valid indices for `duckdb_v2_table_function_exec_get_column_index()` are `0` up to (but - * excluding) this count. + * Fills the caller-allocated `out_schema`. The caller owns the result and releases it with + * `out_schema->release(out_schema)`. Callable at any point, and always returns the same schema, since it is built from + * the settings captured at creation. * * history: * - stable: v2.0.0 * - * @param info The exec info handle. - * @param count Receives the number of columns the scan produces. + * @param exporter The exporter to read. + * @param out_schema Caller-allocated schema the library fills. Release it with `out_schema->release(out_schema)`. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_exec_get_column_count( - duckdb_v2_table_function_exec_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_exporter_get_schema(duckdb_v2_arrow_exporter_handle exporter, + struct ArrowSchema *out_schema, + duckdb_v2_error_info_handle *err); /*! - * Returns which declared column the scan's column at the given index stands for. + * Gives the exporter one chunk to convert. * - * The result indexes the columns declared with `duckdb_v2_table_function_bind_add_result_column()`, in declaration - * order: the exec callback fills the output chunk's vector at `index` with that column's data. Without projection - * pushdown the mapping is the identity. Fails if the index is out of bounds. + * The chunk is converted in full before this returns. The conversion copies into freshly allocated Arrow buffers, so + * nothing of the caller's is retained. Every array completed by this chunk becomes available from + * `duckdb_v2_arrow_exporter_next_array()`; rows that do not complete a batch are held back and finished by the next + * chunk. Completed arrays queue up, so appending again before they are taken is allowed. + * + * `flush` marks the end of the input: the held rows are then finished as a final short array. Pass NULL for `chunk` + * with `flush` set to release the held rows without supplying more input. + * + * The chunk's types must match the ones the exporter was created with, or the call is rejected with + * `ERROR_INPUT_INVALID` before anything is read. + * + * `consume` decides only what happens to the caller's handle, since the data is copied either way. When true the chunk + * is destroyed and the slot set to NULL, saving a `duckdb_v2_data_chunk_destroy()` for a chunk the caller owns. When + * false the chunk is left untouched, which is what a chunk borrowed from a callback needs, such as the output chunk of + * a table function's exec callback, which the caller does not own and must not destroy. * * history: * - stable: v2.0.0 * - * @param info The exec info handle. - * @param index The index of the column in the scan's output. - * @param column_index Receives the index of the declared column. + * @param exporter The exporter to feed. + * @param chunk The chunk to convert. Destroyed and set to NULL only when `consume` is true. + * @param consume True to hand the chunk over, destroying it; false to leave the caller's handle untouched. + * @param flush True to mark the end of the input, releasing the held rows as a final short array. Pass NULL for `chunk` + * with this set to flush without supplying more input. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_exec_get_column_index( - duckdb_v2_table_function_exec_info_handle info, idx_t index, idx_t *column_index, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_exporter_append(duckdb_v2_arrow_exporter_handle exporter, + duckdb_v2_data_chunk_handle *chunk, bool consume, + bool flush, duckdb_v2_error_info_handle *err); /*! - * Retrieves the user data set via `duckdb_v2_table_function_set_user_data()`. + * Takes the next completed array, or reports that none is ready. + * + * Call it in a loop after every `duckdb_v2_arrow_exporter_append()` until `out_array->release` is NULL, which is how + * the Arrow C Data Interface signals "no array" and what a stream's `get_next` does at end of input. Rows held back + * towards an unfinished batch are not an array yet; they come out after a further append or a flush. + * + * Each array is owned by the caller and released with `out_array->release(out_array)`, independently of the exporter + * and of every other array. * * history: * - stable: v2.0.0 * - * @param info The progress info handle. - * @param data Receives the user data pointer, or null if none was set. + * @param exporter The exporter to drain. + * @param out_array Caller-allocated array the library fills. Left released -- `release` NULL -- when none is ready. + * Release a filled one with `out_array->release(out_array)`. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_progress_get_user_data( - duckdb_v2_table_function_progress_info_handle info, void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_exporter_next_array(duckdb_v2_arrow_exporter_handle exporter, + struct ArrowArray *out_array, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the bind data set by the function's bind callback. + * Destroys an exporter. + * + * Null-safe: passing NULL, or a slot already set to NULL, is a no-op. Arrays already taken stay valid. Arrays still + * queued inside are released here, and rows held back towards an unfinished batch are dropped. On success the slot is + * set to NULL. * * history: * - stable: v2.0.0 * - * @param info The progress info handle. - * @param data Receives the bind data pointer, or null if none was set. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param exporter The exporter to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_progress_get_bind_data( - duckdb_v2_table_function_progress_info_handle info, void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_exporter_destroy(duckdb_v2_arrow_exporter_handle *exporter); /*! - * Retrieves the global state set by the function's global init callback. + * Executes a parsed statement. The result produces Arrow arrays. * - * The progress callback runs concurrently with the exec callbacks scanning the function, so it must read the global - * state in a thread-safe way. + * Works like `duckdb_v2_statement_execute()`. `*out_result` is set to NULL on failure. * * history: * - stable: v2.0.0 * - * @param info The progress info handle. - * @param data Receives the global state pointer, or null if none was set. + * @param conn The connection to run the statement on. + * @param statement The statement to execute. Not consumed. + * @param parameter_names Optional. As in `duckdb_v2_statement_execute()`. + * @param parameter_values Optional. As in `duckdb_v2_statement_execute()`. + * @param parameter_count The number of parameters. 0 for none. + * @param batch_size Maximum rows per array. Arrays can be shorter. 0 means 131072. + * @param out_result Receives the new result. Destroy via `duckdb_v2_arrow_result_destroy()`. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_progress_get_global_state( - duckdb_v2_table_function_progress_info_handle info, void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_statement_execute_arrow(duckdb_v2_connection_handle conn, + duckdb_v2_sql_statement_handle statement, + const duckdb_v2_identifier_t *parameter_names, + const duckdb_v2_value_handle *parameter_values, + idx_t parameter_count, idx_t batch_size, + duckdb_v2_arrow_result_handle *out_result, + duckdb_v2_error_info_handle *err); /*! - * Reports how far the scan has advanced. + * Executes a prepared statement. The result produces Arrow arrays. * - * A fraction between 0.0 (nothing scanned yet) and 1.0 (done); values outside that range are clamped. A progress - * callback that returns without calling this reports no progress. + * Works like `duckdb_v2_prepared_statement_execute()`. `*out_result` is set to NULL on failure. * * history: * - stable: v2.0.0 * - * @param info The progress info handle. - * @param progress The fraction of the scan that is complete, in [0.0, 1.0]. + * @param prepared The prepared statement to execute. Not consumed. + * @param parameter_names Optional. As in `duckdb_v2_prepared_statement_execute()`. + * @param parameter_values Optional. As in `duckdb_v2_prepared_statement_execute()`. + * @param parameter_count The number of parameters. 0 for none. + * @param batch_size Maximum rows per array. Arrays can be shorter. 0 means 131072. + * @param out_result Receives the new result. Destroy via `duckdb_v2_arrow_result_destroy()`. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_progress_set_progress( - duckdb_v2_table_function_progress_info_handle info, double progress, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_prepared_statement_execute_arrow(duckdb_v2_prepared_statement_handle prepared, + const duckdb_v2_identifier_t *parameter_names, + const duckdb_v2_value_handle *parameter_values, + idx_t parameter_count, idx_t batch_size, + duckdb_v2_arrow_result_handle *out_result, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the user data set via `duckdb_v2_table_function_set_user_data()`, or null if none was set. + * Destroys the result. Works like `duckdb_v2_result_destroy()`. * * history: * - stable: v2.0.0 * - * @param info The filter pushdown info handle. - * @param data Receives the user data pointer. - * @param err Optional. On failure, receives an opaque info handle the caller must destroy via - * `duckdb_v2_error_info_destroy()`. + * @param result The result to destroy. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_filter_pushdown_get_user_data( - duckdb_v2_table_function_filter_pushdown_info_handle info, void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_result_destroy(duckdb_v2_arrow_result_handle *result); /*! - * Retrieves the bind data set via `duckdb_v2_table_function_bind_set_bind_data()`, or null if none was set. + * Runs a bounded amount of the query and returns without blocking. * - * This is the same object the init and exec callbacks later receive, so a predicate the callback accepts can be - * recorded in it for the scan to apply. + * Works like `duckdb_v2_result_step()`. On status `CHUNK`, `out_array` holds the next array. Otherwise its `release` is + * NULL. * * history: * - stable: v2.0.0 * - * @param info The filter pushdown info handle. - * @param data Receives the bind data pointer. + * @param result The result to step. + * @param out_array Caller-allocated array the library fills. Release it with `out_array->release(out_array)`. + * @param out_status Receives the step status. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_filter_pushdown_get_bind_data( - duckdb_v2_table_function_filter_pushdown_info_handle info, void **data, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_result_step(duckdb_v2_arrow_result_handle result, + struct ArrowArray *out_array, + DUCKDB_V2_RESULT_STEP_STATUS *out_status, + duckdb_v2_error_info_handle *err); /*! - * Returns the number of filter predicates offered to the function. + * Blocks until the next array is ready. * - * The predicates are combined with `AND`: every row the scan produces must satisfy all of them. Valid indices for - * `duckdb_v2_table_function_filter_pushdown_get_filter()` and `duckdb_v2_table_function_filter_pushdown_accept()` are - * `0` up to (but excluding) this count. + * Works like `duckdb_v2_result_fetch_chunk()`. At the end of the result, `out_array->release` is NULL. * * history: * - stable: v2.0.0 * - * @param info The filter pushdown info handle. - * @param count Receives the number of predicates. + * @param result The result to fetch from. + * @param out_array Caller-allocated array the library fills. Release it with `out_array->release(out_array)`. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_filter_pushdown_get_filter_count( - duckdb_v2_table_function_filter_pushdown_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_result_fetch_array(duckdb_v2_arrow_result_handle result, + struct ArrowArray *out_array, + duckdb_v2_error_info_handle *err); /*! - * Retrieves the filter predicate at the given index. - * - * Fails if the index is out of bounds. The predicate is a bound expression evaluating to `BOOLEAN`; inspect it with - * `duckdb_v2_expression_get_type()` and the other `expression` functions, and resolve the column references it contains - * via `duckdb_v2_table_function_filter_pushdown_get_column_index()`. Borrowed; valid only for the duration of the - * callback. + * Blocks until `duckdb_v2_arrow_result_step()` can make progress. Works like `duckdb_v2_result_wait()`. * * history: * - stable: v2.0.0 * - * @param info The filter pushdown info handle. - * @param index The index of the predicate to retrieve. - * @param filter Receives the borrowed predicate. + * @param result The result to wait on. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_filter_pushdown_get_filter( - duckdb_v2_table_function_filter_pushdown_info_handle info, idx_t index, duckdb_v2_expression_handle *filter, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_result_wait(duckdb_v2_arrow_result_handle result, + duckdb_v2_error_info_handle *err); /*! - * Accepts the filter predicate at the given index: the function will apply it itself. - * - * The engine stops applying an accepted predicate, so the scan must produce only rows that satisfy it, or the query - * returns rows it should not. Predicates left unaccepted are applied by the engine as usual, so a callback that - * recognizes nothing can simply return. Fails if the index is out of bounds. + * Runs the query to the end and reports the changed-row count. Works like `duckdb_v2_result_drain()`. * * history: * - stable: v2.0.0 * - * @param info The filter pushdown info handle. - * @param index The index of the predicate to accept. + * @param result The result to drain. + * @param out_rows_changed Receives the changed-row count for an INSERT, UPDATE or DELETE, 0 otherwise. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_filter_pushdown_accept( - duckdb_v2_table_function_filter_pushdown_info_handle info, idx_t index, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_result_drain(duckdb_v2_arrow_result_handle result, idx_t *out_rows_changed, + duckdb_v2_error_info_handle *err); /*! - * Returns the number of columns the predicates can refer to. - * - * A column reference inside a predicate (`duckdb_v2_expression_column_ref_get_index()`) indexes the columns the query - * reads from the function, not the columns declared in bind. Valid indices for - * `duckdb_v2_table_function_filter_pushdown_get_column_index()` are `0` up to (but excluding) this count. + * Returns the shape of the result. Works like `duckdb_v2_result_get_result_type()`. * * history: * - stable: v2.0.0 * - * @param info The filter pushdown info handle. - * @param count Receives the number of referable columns. + * @param result The result. + * @param out_type Receives the result shape. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_filter_pushdown_get_column_count( - duckdb_v2_table_function_filter_pushdown_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_result_get_result_type(duckdb_v2_arrow_result_handle result, + DUCKDB_V2_RESULT_TYPE *out_type, + duckdb_v2_error_info_handle *err); /*! - * Resolves a column reference found in a predicate to the declared column it refers to. - * - * Takes the index a `EXPRESSION_TYPE_BOUND_COLUMN_REF` node reports and returns the index of the column among the ones - * declared with `duckdb_v2_table_function_bind_add_result_column()`, in declaration order. Fails if the index is out of - * bounds. + * Returns the type of statement that produced the result. Works like `duckdb_v2_result_get_statement_type()`. * * history: * - stable: v2.0.0 * - * @param info The filter pushdown info handle. - * @param index The column index reported by a column reference node. - * @param column_index Receives the index of the declared column. + * @param result The result. + * @param out_type Receives the statement type. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_filter_pushdown_get_column_index( - duckdb_v2_table_function_filter_pushdown_info_handle info, idx_t index, idx_t *column_index, - duckdb_v2_error_info_handle *err); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_result_get_statement_type(duckdb_v2_arrow_result_handle result, + DUCKDB_V2_STATEMENT_TYPE *out_type, + duckdb_v2_error_info_handle *err); /*! - * Registers the table function, making it available for use in SQL queries. + * Returns the Arrow schema of the result's arrays. * - * The function is registered on the target given at creation: the connection's database or the loading extension. - * Registration requires a name, a bind callback and an exec callback, and rejects a signature that declares a return - * type, since a table function declares the columns it returns from its bind callback. The caller still owns the handle - * after registration and must destroy it with `duckdb_v2_table_function_destroy()`, which does not affect the - * registered function. + * Available whenever `duckdb_v2_result_get_schema()` would be. * * history: * - stable: v2.0.0 * - * @param function The function to register. + * @param result The result. + * @param out_schema Caller-allocated schema the library fills. Release it with `out_schema->release(out_schema)`. * @param err Optional. On failure, receives an opaque info handle the caller must destroy via * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_register(duckdb_v2_table_function_handle function, +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_result_get_schema(duckdb_v2_arrow_result_handle result, + struct ArrowSchema *out_schema, duckdb_v2_error_info_handle *err); /*! - * Destroys the table function, releasing its resources. + * Turns the result into an ArrowArrayStream. * - * Null-safe: passing a null pointer or null handle is a no-op. The handle is set to null on return to prevent - * double-destruction. Destroying the handle after registration does not affect the registered function. + * On success the stream takes over the result and `*result` is set to NULL. Each `get_next` returns the next array. + * Arrays already fetched are not repeated. Releasing the stream destroys the result. + * + * Converting runs no part of the query. The stream's `get_schema` can: for a statement that expands into several + * statements, such as PIVOT, it runs the ones before the statement that returns rows. A failing `get_next` or + * `get_schema` returns `EIO`; `get_last_error` has the message. * * history: * - stable: v2.0.0 * - * @param function The function to destroy. + * @param result The result to take over. Set to NULL on success. + * @param out_stream Caller-allocated stream the library fills. Release it with `out_stream->release(out_stream)`. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. * @return DUCKDB_V2_ERROR */ -DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_destroy(duckdb_v2_table_function_handle *function); +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_arrow_result_to_arrow_c_stream(duckdb_v2_arrow_result_handle *result, + struct ArrowArrayStream *out_stream, + duckdb_v2_error_info_handle *err); -/* --- Struct definitions for table --- */ +/* --- Struct definitions for arrow --- */ #ifdef __cplusplus } diff --git a/src/duckdb/src/logging/log_manager.cpp b/src/duckdb/src/logging/log_manager.cpp index 5d80e08bc..e1b3b174d 100644 --- a/src/duckdb/src/logging/log_manager.cpp +++ b/src/duckdb/src/logging/log_manager.cpp @@ -34,6 +34,7 @@ RegisteredLoggingContext LogManager::RegisterLoggingContext(LoggingContext &cont } bool LogManager::RegisterLogStorage(const string &name, shared_ptr &storage) { + unique_lock lck(lock); if (registered_log_storages.find(name) != registered_log_storages.end()) { return false; } @@ -285,6 +286,7 @@ void LogManager::RegisterDefaultLogTypes() { RegisterLogType(make_uniq()); RegisterLogType(make_uniq()); RegisterLogType(make_uniq()); + RegisterLogType(make_uniq()); } } // namespace duckdb diff --git a/src/duckdb/src/logging/log_storage.cpp b/src/duckdb/src/logging/log_storage.cpp index 815ebc53a..71225f51a 100644 --- a/src/duckdb/src/logging/log_storage.cpp +++ b/src/duckdb/src/logging/log_storage.cpp @@ -79,6 +79,9 @@ bool LogStorage::Scan(LogStorageScanState &state, DataChunk &result) const { void LogStorage::InitializeScan(LogStorageScanState &state) const { throw NotImplementedException("Not implemented for this LogStorage: InitializeScanEntries"); } +optional_idx LogStorage::GetScanRowCount(LoggingTargetTable table) const { + return optional_idx(); +} void LogStorage::Truncate() { throw NotImplementedException("Not implemented for this LogStorage: TruncateLogStorage"); } @@ -482,7 +485,7 @@ unique_ptr FileLogStorage::BindReplaceInternal(ClientContext &context, string sub_query_string = StringUtil::Format("%s FROM read_csv_auto(%s, columns={%s})", select_clause, SQLString(path), csv_columns); - Parser parser(context.GetParserOptions()); + Parser parser(context); parser.ParseQuery(sub_query_string); auto select_stmt = unique_ptr_cast(std::move(parser.statements[0])); @@ -770,6 +773,11 @@ bool InMemoryLogStorage::Scan(LogStorageScanState &state, DataChunk &result) con return GetBuffer(in_mem_scan_state.table).Scan(in_mem_scan_state.scan_state, result); } +optional_idx InMemoryLogStorage::GetScanRowCount(LoggingTargetTable table) const { + unique_lock lck(lock); + return GetBuffer(table).Count(); +} + void InMemoryLogStorage::InitializeScan(LogStorageScanState &state) const { unique_lock lck(lock); auto &in_mem_scan_state = state.Cast(); diff --git a/src/duckdb/src/logging/log_types.cpp b/src/duckdb/src/logging/log_types.cpp index b8512505d..bb25617f0 100644 --- a/src/duckdb/src/logging/log_types.cpp +++ b/src/duckdb/src/logging/log_types.cpp @@ -3,6 +3,7 @@ #include "duckdb/common/exception.hpp" #include "duckdb/common/file_opener.hpp" #include "duckdb/main/http/http_util.hpp" +#include "duckdb/main/profiler/profiling_utils.hpp" #include "duckdb/execution/physical_operator.hpp" #include "duckdb/logging/file_system_logger.hpp" #include "duckdb/main/attached_database.hpp" @@ -23,6 +24,7 @@ constexpr LogLevel CheckpointLogType::LEVEL; constexpr LogLevel AdaptiveFilterLogType::LEVEL; constexpr LogLevel ParquetPrefetchLogType::LEVEL; constexpr LogLevel AsyncTaskScheduleLogType::LEVEL; +constexpr LogLevel ProgressVerificationLogType::LEVEL; //===--------------------------------------------------------------------===// // QueryLogType @@ -59,6 +61,130 @@ LogicalType FileSystemLogType::GetLogType() { //===--------------------------------------------------------------------===// // HTTPLogType //===--------------------------------------------------------------------===// +static const case_insensitive_set_t &HTTPRequestHeaderAllowList() { + //! For extra info, the advise from OpenTelemetry: + //! https://opentelemetry.io/docs/specs/semconv/registry/attributes/http/#http-request-header + static const case_insensitive_set_t allow_list = { + // HTTP semantics (https://www.rfc-editor.org/rfc/rfc9110.html). + "accept", // Accepted media types and parameters. + "accept-charset", // Accepted character sets. + "accept-encoding", // Accepted content codings. + "accept-language", // Preferred response languages. + "connection", // Connection handling options. + "content-encoding", // Applied content codings. + "content-language", // Languages of the intended audience. + "content-length", // Content size in bytes. + "content-type", // Content media type and parameters. + "date", // Message timestamp. + "expect", // Expected server behavior before sending content. + "if-modified-since", // Modification timestamp used for conditional requests. + "if-unmodified-since", // Modification timestamp used for conditional requests. + "max-forwards", // Remaining forwarding limit. + "range", // Requested ranges. + "te", // Accepted transfer codings and trailer support. + "trailer", // Names of fields sent in trailers. + "upgrade", // Proposed or selected protocols. + "user-agent", // Client product and version information. + // "via", // Intermediary protocols, hosts and software comments. + // "authorization", // Contains credentials and signatures. + // "content-location", // URI can contain sensitive paths or query parameters. + // "from", // Contains the user's email address. + // "host", // Identifies the endpoint, including bucket or tenant names. + // "if-match", // Contains opaque resource validators. + // "if-none-match", // Contains opaque resource validators. + // "if-range", // Can contain an opaque resource validator instead of a date. + // "proxy-authorization", // Contains proxy credentials. + // "referer", // URI can contain sensitive paths or query parameters. + + // Headers defined outside RFC 9110. + "cache-control", // Caching directives. + "transfer-encoding", // Applied transfer codings. + // "cookie", // Can contain session credentials and user data. + + // S3 common headers (https://docs.aws.amazon.com/AmazonS3/latest/developerguide/RESTCommonRequestHeaders.html). + "x-amz-date", // Request signing timestamp. + // "content-md5", // Fingerprints the request content. + // "x-amz-content-sha256", // Can fingerprint the payload, not just contain a fixed signing marker. + // "x-amz-security-token", // Contains session credentials. + + // S3 PutObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html). + "x-amz-request-payer", // Requester-pays billing mode. + "x-amz-server-side-encryption", // Server-side encryption algorithm. + // "x-amz-expected-bucket-owner", // Identifies an AWS account. + // "x-amz-server-side-encryption-aws-kms-key-id", // Identifies an encryption key and possibly its account. + // "x-amz-server-side-encryption-context", // Contains caller-provided encryption context. + // "x-amz-server-side-encryption-customer-key", // Contains the customer encryption key. + // "x-amz-server-side-encryption-customer-key-md5", // Fingerprints the customer encryption key. + // "x-amz-tagging", // Contains caller-provided object tags. + // "x-amz-website-redirect-location", // URI can contain sensitive paths or query parameters. + + // S3 CopyObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html). + // "x-amz-copy-source", // Contains a bucket, object path and possibly a version ID. + // "x-amz-copy-source-if-match", // Contains opaque resource validators. + // "x-amz-copy-source-if-none-match", // Contains opaque resource validators. + // "x-amz-copy-source-server-side-encryption-customer-key", // Contains the source encryption key. + // "x-amz-copy-source-server-side-encryption-customer-key-md5", // Fingerprints the source encryption key. + // "x-amz-source-expected-bucket-owner", // Identifies an AWS account. + + // S3 session authorization (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateSession.html). + // "x-amz-s3session-token", // Contains S3 Express session credentials. + }; + return allow_list; +} + +static const case_insensitive_set_t &HTTPResponseHeaderAllowList() { + static const case_insensitive_set_t allow_list = { + // HTTP semantics (https://www.rfc-editor.org/rfc/rfc9110.html). + "accept", // Accepted media types and parameters. + "accept-encoding", // Accepted content codings. + "accept-ranges", // Supported range units. + "allow", // Supported HTTP methods. + "connection", // Connection handling options. + "content-encoding", // Applied content codings. + "content-language", // Languages of the intended audience. + "content-length", // Content size in bytes. + "content-range", // Transferred range and total size. + "content-type", // Content media type and parameters. + "date", // Message timestamp. + "last-modified", // Resource modification timestamp. + "retry-after", // Retry delay or timestamp. + "server", // Server product and version information. + "trailer", // Names of fields sent in trailers. + "upgrade", // Proposed or selected protocols. + "vary", // Request field names used for response selection. + // "via", // Intermediary protocols, hosts and software comments. + // "authentication-info", // Can contain authentication tokens and parameters. + // "content-location", // URI can contain sensitive paths or query parameters. + // "etag", // Contains an opaque resource validator. + // "location", // Redirect URI can contain credentials or signed query parameters. + // "proxy-authenticate", // Authentication challenges can contain tokens and realm information. + // "proxy-authentication-info", // Can contain authentication tokens and parameters. + // "www-authenticate", // Authentication challenges can contain tokens and realm information. + + // HTTP caching (https://www.rfc-editor.org/rfc/rfc9111.html). + "age", // Time spent in caches. + "cache-control", // Caching directives. + "expires", // Cache expiration timestamp. + + // Other HTTP headers. + "transfer-encoding", // Applied transfer codings. + // "set-cookie", // Can contain session credentials and user data. + + // S3 (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html). + "x-amz-bucket-region", // Bucket region. + "x-amz-server-side-encryption", // Server-side encryption algorithm. + // "x-amz-abort-rule-id", // Identifies a lifecycle rule. + // "x-amz-copy-source-version-id", // Identifies a source object version. + // "x-amz-expiration", // Includes a lifecycle rule ID as well as a date. + // "x-amz-id-2", // Request correlation ID, enabling it exposes request identity. + // "x-amz-request-id", // Request correlation ID, enabling it exposes request identity. + // "x-amz-server-side-encryption-aws-kms-key-id", // Identifies an encryption key and possibly its account. + // "x-amz-server-side-encryption-customer-key-md5", // Fingerprints the customer encryption key. + // "x-amz-version-id", // Identifies an object version. + }; + return allow_list; +} + HTTPLogType::HTTPLogType() : LogType(NAME, LEVEL, GetLogType()) { } @@ -85,16 +211,56 @@ LogicalType HTTPLogType::GetLogType() { return LogicalType::STRUCT(child_list); } -static Value CreateHTTPHeadersValue(const HTTPHeaders &headers) { +static Value CreateHTTPHeadersValue(const HTTPHeaders &headers, const case_insensitive_set_t &allow_list, + bool redact_http_logs) { vector keys; vector values; for (const auto &header : headers) { keys.emplace_back(header.first); - values.emplace_back(header.second); + const bool allow_value = !redact_http_logs || allow_list.find(header.first) != allow_list.end(); + values.emplace_back(allow_value ? header.second : HTTPLogType::REDACTED_VALUE); } return Value::MAP(LogicalType::VARCHAR, LogicalType::VARCHAR, keys, values); } +static string BuildLogURL(const BaseRequest &request, bool redact_http_logs) { + string url; + url.reserve(request.proto_host_port.size() + request.path.size()); + + if (!redact_http_logs) { + url.append(request.proto_host_port); + url.append(request.path); + return url; + } + + // Redact user info, e.g. https://user:pass@duckdb.org -> https://redacted@duckdb.org + const auto pos_at = request.proto_host_port.rfind('@'); + if (pos_at != string::npos) { + auto pos_proto_end = request.proto_host_port.find("://"); + if (pos_proto_end == string::npos) { + // Without a scheme separator, redact from the start. + pos_proto_end = 0; + } else { + pos_proto_end += 3; + } + url.append(request.proto_host_port, 0, pos_proto_end); + url.append(HTTPLogType::REDACTED_VALUE); + url.append(request.proto_host_port, pos_at, string::npos); + } else { + url += request.proto_host_port; + } + + // Redact the query parameters or fragment + const auto pos_end_of_path = request.path.find_first_of("?#"); + if (pos_end_of_path == string::npos) { + url += request.path; + } else { + url.append(request.path, 0, pos_end_of_path + 1); + url.append(HTTPLogType::REDACTED_VALUE); + } + return url; +} + static string HTTPStatusToLogString(HTTPStatusCode status) { try { return EnumUtil::ToString(status); @@ -103,11 +269,12 @@ static string HTTPStatusToLogString(HTTPStatusCode status) { } } -string HTTPLogType::ConstructLogMessage(BaseRequest &request, optional_ptr response) { +string HTTPLogType::ConstructLogMessage(BaseRequest &request, optional_ptr response, + bool redact_http_logs) { child_list_t request_child_list = { {"type", Value(EnumUtil::ToString(request.type))}, - {"url", Value(request.url)}, - {"headers", CreateHTTPHeadersValue(request.headers)}, + {"url", Value(BuildLogURL(request, redact_http_logs))}, + {"headers", CreateHTTPHeadersValue(request.headers, HTTPRequestHeaderAllowList(), redact_http_logs)}, {"start_time", request.have_request_timing ? Value::TIMESTAMP(request.request_system_start) : Value()}, {"duration_ms", request.have_request_timing @@ -120,7 +287,7 @@ string HTTPLogType::ConstructLogMessage(BaseRequest &request, optional_ptr response_child_list = { {"status", Value(HTTPStatusToLogString(response->status))}, {"reason", Value(response->reason.empty() ? response->GetRequestError() : response->reason)}, - {"headers", CreateHTTPHeadersValue(response->headers)}, + {"headers", CreateHTTPHeadersValue(response->headers, HTTPResponseHeaderAllowList(), redact_http_logs)}, }; response_value = Value::STRUCT(response_child_list); } @@ -196,14 +363,19 @@ LogicalType MetricsLogType::GetLogType() { child_list_t child_list = { {"metric", LogicalType::VARCHAR}, {"value", LogicalType::VARCHAR}, + {"start_time", LogicalType::TIMESTAMP_TZ}, + {"end_time", LogicalType::TIMESTAMP_TZ}, }; return LogicalType::STRUCT(child_list); } -string MetricsLogType::ConstructLogMessage(const string &metric, const Value &value) { +string MetricsLogType::ConstructLogMessage(const string &metric, const Value &value, + optional_ptr timing) { child_list_t child_list = { {"metric", metric}, {"value", value.ToString()}, + {"start_time", timing && timing->has_bounds ? Value::TIMESTAMP(timing->start_time) : Value()}, + {"end_time", timing && timing->has_bounds ? Value::TIMESTAMP(timing->end_time) : Value()}, }; return Value::STRUCT(std::move(child_list)).ToString(); } @@ -384,6 +556,33 @@ string AsyncTaskScheduleLogType::ConstructLogMessage(const string &pool, idx_t t return Value::STRUCT(std::move(child_list)).ToString(); } +//===--------------------------------------------------------------------===// +// ProgressVerificationLogType +//===--------------------------------------------------------------------===// +ProgressVerificationLogType::ProgressVerificationLogType() : LogType(NAME, LEVEL, GetLogType()) { +} + +LogicalType ProgressVerificationLogType::GetLogType() { + child_list_t child_list = { + {"invariant", LogicalType::VARCHAR}, + {"operator", LogicalType::VARCHAR}, + {"pipeline", LogicalType::VARCHAR}, + {"detail", LogicalType::VARCHAR}, + }; + return LogicalType::STRUCT(child_list); +} + +string ProgressVerificationLogType::ConstructLogMessage(const string &invariant, const string &operator_name, + const string &pipeline, const string &detail) { + child_list_t child_list = { + {"invariant", Value(invariant)}, + {"operator", Value(operator_name)}, + {"pipeline", Value(pipeline)}, + {"detail", Value(detail)}, + }; + return Value::STRUCT(std::move(child_list)).ToString(); +} + //===--------------------------------------------------------------------===// // ExternalResourceLogType //===--------------------------------------------------------------------===// diff --git a/src/duckdb/src/main/appender.cpp b/src/duckdb/src/main/appender.cpp index f2d16561b..2a55ebd32 100644 --- a/src/duckdb/src/main/appender.cpp +++ b/src/duckdb/src/main/appender.cpp @@ -467,10 +467,10 @@ CommonTableExpressionMap &GetCTEMap(SQLStatement &statement) { } } -unique_ptr BaseAppender::ParseStatement(unique_ptr table_ref, const string &query, - const string &table_name) { +unique_ptr BaseAppender::ParseStatement(ClientContext &context, unique_ptr table_ref, + const string &query, const string &table_name) { // Parse the query. - Parser parser; + Parser parser(context); parser.ParseQuery(query); // Must be a single statement. @@ -623,7 +623,7 @@ void Appender::FlushInternal(ColumnDataCollection &collection) { auto query = ConstructQuery(*description, table_name, expected_names); auto table_ref = GetColumnDataTableRef(collection, table_name, expected_names); - auto stmt = ParseStatement(std::move(table_ref), query, table_name.GetIdentifierName()); + auto stmt = ParseStatement(*context_ref, std::move(table_ref), query, table_name.GetIdentifierName()); context_ref->Append(std::move(stmt)); } @@ -724,7 +724,7 @@ void QueryAppender::FlushInternal(ColumnDataCollection &collection) { throw InvalidInputException("Attempting to flush query appender data on a closed connection"); } auto table_ref = GetColumnDataTableRef(collection, table_name, names); - auto parsed_statement = ParseStatement(std::move(table_ref), query, table_name.GetIdentifierName()); + auto parsed_statement = ParseStatement(*context_ref, std::move(table_ref), query, table_name.GetIdentifierName()); context_ref->Append(std::move(parsed_statement)); } diff --git a/src/duckdb/src/main/attached_database.cpp b/src/duckdb/src/main/attached_database.cpp index d67f6cd7b..a6253e5f8 100644 --- a/src/duckdb/src/main/attached_database.cpp +++ b/src/duckdb/src/main/attached_database.cpp @@ -7,8 +7,10 @@ #include "duckdb/common/file_system.hpp" #include "duckdb/main/database.hpp" #include "duckdb/main/database_manager.hpp" +#include "duckdb/main/external_resources_manager.hpp" #include "duckdb/main/settings.hpp" #include "duckdb/parser/parsed_data/attach_info.hpp" +#include "duckdb/parser/peg/compiled_grammar.hpp" #include "duckdb/parser/qualified_name.hpp" #include "duckdb/storage/storage_extension.hpp" #include "duckdb/storage/storage_manager.hpp" @@ -42,17 +44,26 @@ AttachOptions::AttachOptions(const DBConfigOptions &options) : access_mode(options.access_mode), db_type(options.database_type) { } +//! The spellings of the access mode option, and whether `true` means read-only for each. +static const unordered_map ACCESS_MODE_OPTIONS = { + {"readonly", true}, {"read_only", true}, {"readwrite", false}, {"read_write", false}}; + +string AttachOptions::OptionSetting(const string &name) { + auto lower = StringUtil::Lower(name); + if (ACCESS_MODE_OPTIONS.find(lower) != ACCESS_MODE_OPTIONS.end()) { + return "access_mode"; + } + return lower; +} + AttachOptions::AttachOptions(const unordered_map &attach_options, const AccessMode default_access_mode) : access_mode(default_access_mode) { for (auto &entry : attach_options) { - if (entry.first == "readonly" || entry.first == "read_only") { - // Extract the read access mode. - auto read_only = BooleanValue::Get(entry.second.DefaultCastAs(LogicalType::BOOLEAN)); - if (read_only) { - access_mode = AccessMode::READ_ONLY; - } else { - access_mode = AccessMode::READ_WRITE; - } + auto access_mode_option = ACCESS_MODE_OPTIONS.find(entry.first); + if (access_mode_option != ACCESS_MODE_OPTIONS.end()) { + // Extract the access mode; the option reads inverted for the readwrite spellings. + auto value = BooleanValue::Get(entry.second.DefaultCastAs(LogicalType::BOOLEAN)); + access_mode = value == access_mode_option->second ? AccessMode::READ_ONLY : AccessMode::READ_WRITE; continue; } @@ -63,17 +74,6 @@ AttachOptions::AttachOptions(const unordered_map &attach_options, continue; } - if (entry.first == "readwrite" || entry.first == "read_write") { - // Extract the write access mode. - auto read_write = BooleanValue::Get(entry.second.DefaultCastAs(LogicalType::BOOLEAN)); - if (!read_write) { - access_mode = AccessMode::READ_ONLY; - } else { - access_mode = AccessMode::READ_WRITE; - } - continue; - } - if (entry.first == "type") { // Extract the database type. Normalize case so that // `TYPE sqlite` and `TYPE 'SQLite'` are equivalent. @@ -149,6 +149,11 @@ AttachedDatabase::AttachedDatabase(DatabaseInstance &db, Catalog &catalog_p, Ide ephemeral = options.ephemeral; vacuum_rebuild_threshold = options.vacuum_rebuild_indexes_threshold; original_path = options.original_path; + deleter_function = options.deleter_function; + deleter_payload = options.deleter_payload; + deleter_resource_type = options.deleter_resource_type; + deleter_resource_name = options.deleter_resource_name; + borrowed_resource_name = options.borrowed_resource_name; // We create the storage after the catalog to guarantee we allow extensions to instantiate the DuckCatalog. catalog = make_uniq(*this); @@ -173,6 +178,11 @@ AttachedDatabase::AttachedDatabase(DatabaseInstance &db, Catalog &catalog_p, Sto ephemeral = options.ephemeral; vacuum_rebuild_threshold = options.vacuum_rebuild_indexes_threshold; original_path = options.original_path; + deleter_function = options.deleter_function; + deleter_payload = options.deleter_payload; + deleter_resource_type = options.deleter_resource_type; + deleter_resource_name = options.deleter_resource_name; + borrowed_resource_name = options.borrowed_resource_name; optional_ptr storage_info = storage_extension->storage_info.get(); catalog = storage_extension->attach(storage_info, context, *this, name.GetIdentifierName(), info, options); @@ -223,6 +233,13 @@ idx_t AttachedDatabase::GetVacuumRebuildIndexThreshold() const { return Settings::Get(db); } +shared_ptr AttachedDatabase::GetConnectedGrammar(const ClientContext &context) { + if (connected_grammar) { + return connected_grammar; + } + return db.GetParserCache().GetPassthroughMatcher(context); +} + string AttachedDatabase::StoredPath() const { if (stored_database_path) { return stored_database_path->path; @@ -363,6 +380,16 @@ void AttachedDatabase::OnDetach(ClientContext &context) { } } +unique_ptr AttachedDatabase::ExtractDeleter() { + if (deleter_function.empty()) { + return nullptr; + } + auto result = make_uniq(db, std::move(deleter_function), std::move(deleter_payload), + std::move(deleter_resource_type), std::move(deleter_resource_name)); + deleter_function.clear(); + return result; +} + void AttachedDatabase::Close(const DatabaseCloseAction action) { if (is_closed) { return; diff --git a/src/duckdb/src/main/buffered_data/batched_buffered_data.cpp b/src/duckdb/src/main/buffered_data/batched_buffered_data.cpp index 4800b30b8..be94e3c01 100644 --- a/src/duckdb/src/main/buffered_data/batched_buffered_data.cpp +++ b/src/duckdb/src/main/buffered_data/batched_buffered_data.cpp @@ -1,27 +1,30 @@ #include "duckdb/main/buffered_data/batched_buffered_data.hpp" #include "duckdb/execution/executor.hpp" #include "duckdb/main/client_context.hpp" -#include "duckdb/main/stream_query_result.hpp" +#include "duckdb/main/query_result.hpp" #include "duckdb/common/helper.hpp" #include "duckdb/common/stack.hpp" namespace duckdb { -BatchedBufferedData::BatchedBufferedData(ClientContext &context, ResultLifetime lifetime) - : BufferedData(BufferedData::Type::BATCHED, context, lifetime), buffer_byte_count(0), read_queue_byte_count(0), - min_batch(0) { +BatchedBufferedData::BatchedBufferedData(ClientContext &context, ResultLifetime lifetime, + ResultFormatContext format_context, shared_ptr format) + : BufferedData(BufferedData::Type::BATCHED, context, lifetime, std::move(format_context), std::move(format)), + buffer_byte_count(0), read_queue_byte_count(0), min_batch(0) { } -bool BatchedBufferedData::AppendOrBlock(DataChunk &to_append, idx_t batch, const InterruptState &blocked_sink) { - // Copied outside the lock: both outcomes need the copy, and parallel producers copy concurrently - auto copy = CopyForBuffering(to_append); - const idx_t chunk_data_size = copy->GetDataSize(); +bool BatchedBufferedData::AppendOrBlock(unique_ptr unit, idx_t batch, const InterruptState &blocked_sink) { + const idx_t unit_data_size = unit->byte_size; annotated_lock_guard lock(glock); + D_ASSERT(batch != DConstants::INVALID_INDEX); D_ASSERT(batch >= min_batch); - max_seen_chunk_bytes = MaxValue(max_seen_chunk_bytes, chunk_data_size); - if (ShouldBlockBatch(lock, batch, chunk_data_size)) { - // Park holding the finished copy. Restart selection deposits it at wake time - auto entry = blocked_sinks.emplace(batch, BlockedSink {blocked_sink, chunk_data_size, std::move(copy)}); + max_seen_unit_bytes = MaxValue(max_seen_unit_bytes, unit_data_size); + if (ShouldBlockBatch(lock, batch, unit_data_size)) { + // Park holding the finished unit. Restart selection deposits it at wake time + parked_bytes += unit_data_size; + peak_streaming_bytes = + MaxValue(peak_streaming_bytes, read_queue_byte_count + buffer_byte_count + parked_bytes); + auto entry = blocked_sinks.emplace(batch, BlockedSink {blocked_sink, std::move(unit)}); (void)entry; D_ASSERT(entry.second); return true; @@ -36,15 +39,17 @@ bool BatchedBufferedData::AppendOrBlock(DataChunk &to_append, idx_t batch, const // There should not be any batches in the buffer that are lower or equal to the minimum batch index throw InternalException("Batches remaining in buffer"); } - read_queue.push(BufferedChunk {std::move(copy), chunk_data_size}); - read_queue_byte_count += chunk_data_size; + read_queue.push(std::move(unit)); + read_queue_byte_count += unit_data_size; } else { auto &in_progress_batch = buffer[batch]; in_progress_batch.completed = false; - in_progress_batch.chunk_refs.push_back(BufferedChunk {std::move(copy), chunk_data_size}); - buffer_byte_count += chunk_data_size; + in_progress_batch.units.push_back(std::move(unit)); + buffer_byte_count += unit_data_size; } peak_buffered_bytes = MaxValue(peak_buffered_bytes, read_queue_byte_count + buffer_byte_count); + peak_streaming_bytes = + MaxValue(peak_streaming_bytes, read_queue_byte_count + buffer_byte_count + parked_bytes); return false; } @@ -52,17 +57,17 @@ bool BatchedBufferedData::ShouldBlockBatch(annotated_lock_guard idx_t incoming_bytes) { const idx_t total = read_queue_byte_count + buffer_byte_count; if (IsMinimumBatchIndex(lock, batch)) { - // Only block the minimum batch producer while the consumer has chunks to pop. + // Only block the minimum batch producer while the consumer has units to pop. // Every pop lowers the total, so consumption always wakes it again return total + incoming_bytes > total_buffer_size && !read_queue.empty(); } if (total == 0) { - // An empty pool always admits one chunk, so every producer keeps progressing + // An empty pool always admits one unit, so every producer keeps progressing return false; } - // Read-ahead batches leave the minimum batch a reserve: the largest chunk seen, capped at half the - // budget so one oversized chunk cannot serialize read-ahead for the rest of the query - const idx_t reserve = MaxValue(MinValue(max_seen_chunk_bytes, total_buffer_size / 2), + // Read-ahead batches leave the minimum batch a reserve: the largest unit seen, capped at half the + // budget so one oversized unit cannot serialize read-ahead for the rest of the query + const idx_t reserve = MaxValue(MinValue(max_seen_unit_bytes, total_buffer_size / 2), MaxValue(total_buffer_size / 8, 1)); const idx_t threshold = MaxValue(total_buffer_size - reserve, 1); return total + incoming_bytes > threshold; @@ -83,7 +88,7 @@ void BatchedBufferedData::CollectRestartableSinks(annotated_lock_guardfirst, it->second.pending_bytes)) { + if (ShouldBlockBatch(lock, it->first, it->second.PendingBytes())) { it++; continue; } @@ -94,22 +99,25 @@ void BatchedBufferedData::CollectRestartableSinks(annotated_lock_guard &lock, idx_t batch, BlockedSink &sink) { - // Deposit before the wake, so the chunk is visible when the producer resumes; the batch may have - // become the minimum meanwhile. The null guard keeps a park without a copy out of the queue - if (!sink.pending_chunk) { + // Deposit before the wake, so the unit is visible when the producer resumes; the batch may have + // become the minimum meanwhile. The null guard keeps a park without a unit out of the queue + if (!sink.pending_unit) { return; } + const idx_t pending_bytes = sink.pending_unit->byte_size; + parked_bytes -= pending_bytes; if (IsMinimumBatchIndex(lock, batch)) { - read_queue.push(BufferedChunk {std::move(sink.pending_chunk), sink.pending_bytes}); - read_queue_byte_count += sink.pending_bytes; + read_queue.push(std::move(sink.pending_unit)); + read_queue_byte_count += pending_bytes; } else { auto &in_progress_batch = buffer[batch]; in_progress_batch.completed = false; - in_progress_batch.chunk_refs.push_back(BufferedChunk {std::move(sink.pending_chunk), sink.pending_bytes}); - buffer_byte_count += sink.pending_bytes; + in_progress_batch.units.push_back(std::move(sink.pending_unit)); + buffer_byte_count += pending_bytes; } peak_buffered_bytes = MaxValue(peak_buffered_bytes, read_queue_byte_count + buffer_byte_count); - sink.pending_bytes = 0; + peak_streaming_bytes = + MaxValue(peak_streaming_bytes, read_queue_byte_count + buffer_byte_count + parked_bytes); } void BatchedBufferedData::InvokeUnblocks(const vector> &to_unblock) { @@ -146,17 +154,17 @@ void BatchedBufferedData::MoveCompletedBatches(annotated_lock_guardbyte_size; + read_queue.push(std::move(unit)); } // Verification to make sure we're not breaking the order by moving batches before the previous ones have // finished if (lowest_moved_batch > batch_index) { throw InternalException("Lowest moved batch is %d, attempted to move %d afterwards\nAttempted to move %d " - "chunks, of %d bytes in total\nmin_batch is %d", - lowest_moved_batch, batch_index, in_progress_batch.chunk_refs.size(), - batch_data_size, min_batch); + "units, of %d bytes in total\nmin_batch is %d", + lowest_moved_batch, batch_index, in_progress_batch.units.size(), batch_data_size, + min_batch); } D_ASSERT(lowest_moved_batch <= batch_index); lowest_moved_batch = batch_index; @@ -188,7 +196,7 @@ void BatchedBufferedData::UpdateMinBatchIndex(idx_t min_batch_index) { // The move conserves the byte total, so only the newly minimum batch's sink can // have a changed block decision: it now falls under the read queue rule auto entry = blocked_sinks.find(min_batch); - if (entry != blocked_sinks.end() && !ShouldBlockBatch(lock, entry->first, entry->second.pending_bytes)) { + if (entry != blocked_sinks.end() && !ShouldBlockBatch(lock, entry->first, entry->second.PendingBytes())) { DepositParked(lock, entry->first, entry->second); to_unblock.emplace_back(entry->first, std::move(entry->second)); blocked_sinks.erase(entry); @@ -215,28 +223,27 @@ void BatchedBufferedData::AssertNoBlockedSinks() { #endif } -unique_ptr BatchedBufferedData::Scan() { - unique_ptr chunk; +unique_ptr BatchedBufferedData::Scan() { + unique_ptr unit; vector> to_unblock; { annotated_lock_guard lock(glock); if (read_queue.empty()) { - context.reset(); + Close(); D_ASSERT(blocked_sinks.empty()); D_ASSERT(buffer.empty()); return nullptr; } - auto ref = std::move(read_queue.front()); + unit = std::move(read_queue.front()); read_queue.pop(); - chunk = std::move(ref.chunk); - read_queue_byte_count -= ref.data_size; + read_queue_byte_count -= unit->byte_size; // The walk is O(blocked sinks), so only run it when a sink exists if (!blocked_sinks.empty()) { CollectRestartableSinks(lock, to_unblock); } } InvokeUnblocks(to_unblock); - return chunk; + return unit; } } // namespace duckdb diff --git a/src/duckdb/src/main/buffered_data/buffered_data.cpp b/src/duckdb/src/main/buffered_data/buffered_data.cpp index 3d19ac7a5..a988b9b03 100644 --- a/src/duckdb/src/main/buffered_data/buffered_data.cpp +++ b/src/duckdb/src/main/buffered_data/buffered_data.cpp @@ -2,18 +2,27 @@ #include "duckdb/common/exception.hpp" #include "duckdb/common/helper.hpp" #include "duckdb/execution/executor.hpp" -#include "duckdb/main/stream_query_result.hpp" +#include "duckdb/main/query_result.hpp" #include "duckdb/main/client_config.hpp" #include "duckdb/main/client_context.hpp" namespace duckdb { -BufferedData::BufferedData(Type type, ClientContext &context_p, ResultLifetime lifetime) +static shared_ptr FormatOrChunk(shared_ptr format) { + if (!format) { + return ChunkFormat::InMemory(); + } + return format; +} + +BufferedData::BufferedData(Type type, ClientContext &context_p, ResultLifetime lifetime, + ResultFormatContext format_context_p, shared_ptr format_p) : type(type), context(context_p.shared_from_this()), // The setting has no lower bound. A buffer that can never admit a chunk blocks // every sink while empty, and the stream silently ends with zero rows total_buffer_size(MaxValue(ClientConfig::GetConfig(context_p).max_streaming_buffer_size, 1)), - lifetime(lifetime) { + lifetime(lifetime), format_context(std::move(format_context_p)), format(FormatOrChunk(std::move(format_p))), + format_state(format->InitGlobal(format_context)) { } BufferedData::~BufferedData() { @@ -56,7 +65,7 @@ bool BufferedData::ParkUndecided(const InterruptState &blocked_sink) { return true; } -bool BufferedData::HasParkedProducer() { +bool BufferedData::WaitsOnConsumer() { // The undecided list is empty once the retention is settled: ParkUndecided re-checks under glock if (lifetime == ResultLifetime::UNDECIDED) { annotated_lock_guard lock(glock); @@ -64,25 +73,13 @@ bool BufferedData::HasParkedProducer() { return true; } } - return HasBlockedSink(); + // A space park is only the consumer's to release when a pop is possible: the batched buffer also + // parks read-ahead batches while the read queue is empty, and the minimum batch releases those + return HasBlockedSink() && HasObservableUnit(); } -StreamExecutionResult BufferedData::MapExecutionResult(PendingExecutionResult execution_result) { - switch (execution_result) { - case PendingExecutionResult::BLOCKED: - case PendingExecutionResult::RESULT_READY: - return StreamExecutionResult::BLOCKED; - case PendingExecutionResult::NO_TASKS_AVAILABLE: - case PendingExecutionResult::RESULT_NOT_READY: - return StreamExecutionResult::CHUNK_NOT_READY; - case PendingExecutionResult::EXECUTION_FINISHED: - return StreamExecutionResult::EXECUTION_FINISHED; - case PendingExecutionResult::EXECUTION_ERROR: - return StreamExecutionResult::EXECUTION_ERROR; - default: - throw InternalException("No conversion from PendingExecutionResult (%s) -> StreamExecutionResult", - EnumUtil::ToString(execution_result)); - } +QueryResultState BufferedData::Cancelled(QueryResult &result) { + return result.Cancelled(); } unique_ptr BufferedData::CopyForBuffering(DataChunk &chunk) { @@ -96,51 +93,79 @@ idx_t BufferedData::LowWaterMark(idx_t capacity) { return MaxValue(capacity / 2, 1); } -StreamExecutionResult BufferedData::ExecuteTaskInternal(StreamQueryResult &result, ClientContextLock &context_lock) { +QueryResultState BufferedData::Participate(ClientContextLock &context_lock, QueryResult &result) { auto cc = context.lock(); if (!cc) { - return StreamExecutionResult::EXECUTION_CANCELLED; + return Cancelled(result); } if (!cc->IsActiveResult(context_lock, result)) { - return StreamExecutionResult::EXECUTION_CANCELLED; + return Cancelled(result); } DecideDraining(); - // Checked with chunks poppable too, so a cancel ends the drain early. A worker error also raises + // Checked with units poppable too, so a cancel ends the drain early. A worker error also raises // the flag, so only a flag without an executor error is a cancel; both loads are seq_cst const bool interrupted = cc->interrupt_state.load() == ClientInterruptState::INTERRUPTED; if (interrupted && !Executor::Get(*cc).HasError()) { throw InterruptException(); } if (!interrupted && ReplenishSatisfied()) { - return StreamExecutionResult::CHUNK_READY; + return QueryResultState::READY; } UnblockSinks(); // Let the executor run until the buffer is no longer empty auto execution_result = cc->ExecuteTaskInternal(context_lock, result); - if (execution_result == PendingExecutionResult::EXECUTION_ERROR) { - // The query has ended, so a still-buffered chunk must not be reported as poppable + if (execution_result == QueryResultState::EXECUTION_ERROR) { + // The query has ended, so a still-buffered unit must not be reported as poppable Close(); - return StreamExecutionResult::EXECUTION_ERROR; + return QueryResultState::EXECUTION_ERROR; } if (ReplenishSatisfied()) { - return StreamExecutionResult::CHUNK_READY; + return QueryResultState::READY; + } + // Engine READY means a parked producer with a unit to pop, which satisfies the replenish above + D_ASSERT(execution_result != QueryResultState::READY); + return execution_result; +} + +QueryResultState BufferedData::Poll(ClientContextLock &context_lock, QueryResult &result) { + auto cc = context.lock(); + if (!cc) { + return Cancelled(result); } - if (execution_result == PendingExecutionResult::BLOCKED || - execution_result == PendingExecutionResult::RESULT_READY) { - return StreamExecutionResult::BLOCKED; + if (!cc->IsActiveResult(context_lock, result)) { + return Cancelled(result); + } + // Checked before the buffer, so a cancel is seen even with units poppable. A worker error also + // raises the flag; only a flag without an executor error is a real cancel + const bool interrupted = cc->interrupt_state.load() == ClientInterruptState::INTERRUPTED; + if (interrupted && !Executor::Get(*cc).HasError()) { + throw InterruptException(); } - return MapExecutionResult(execution_result); + if (!interrupted && HasObservableUnit()) { + return QueryResultState::READY; + } + auto execution_result = cc->PollInternal(context_lock, result); + if (execution_result == QueryResultState::EXECUTION_ERROR) { + Close(); + return QueryResultState::EXECUTION_ERROR; + } + if (HasObservableUnit()) { + return QueryResultState::READY; + } + // Engine READY means a parked producer with a unit to pop, which the check above would have seen + D_ASSERT(execution_result != QueryResultState::READY); + return execution_result; } -StreamExecutionResult BufferedData::ReplenishBuffer(StreamQueryResult &result, ClientContextLock &context_lock) { +QueryResultState BufferedData::ReplenishBuffer(ClientContextLock &context_lock, QueryResult &result) { auto cc = context.lock(); if (!cc) { - return StreamExecutionResult::EXECUTION_CANCELLED; + return Cancelled(result); } - StreamExecutionResult execution_result; - while (!StreamQueryResult::IsChunkReady(execution_result = ExecuteTaskInternal(result, context_lock))) { - if (execution_result == StreamExecutionResult::BLOCKED) { + QueryResultState execution_result; + while (!IsObservable(execution_result = Participate(context_lock, result))) { + if (execution_result == QueryResultState::BLOCKED) { UnblockSinks(); cc->WaitForTask(context_lock, result); } diff --git a/src/duckdb/src/main/buffered_data/simple_buffered_data.cpp b/src/duckdb/src/main/buffered_data/simple_buffered_data.cpp index 534284168..36984e63b 100644 --- a/src/duckdb/src/main/buffered_data/simple_buffered_data.cpp +++ b/src/duckdb/src/main/buffered_data/simple_buffered_data.cpp @@ -2,13 +2,15 @@ #include "duckdb/common/vector.hpp" #include "duckdb/execution/executor.hpp" #include "duckdb/main/client_context.hpp" -#include "duckdb/main/stream_query_result.hpp" +#include "duckdb/main/query_result.hpp" #include "duckdb/common/helper.hpp" namespace duckdb { -SimpleBufferedData::SimpleBufferedData(ClientContext &context, ResultLifetime lifetime) - : BufferedData(BufferedData::Type::SIMPLE, context, lifetime), buffered_count(0), buffer_size(total_buffer_size) { +SimpleBufferedData::SimpleBufferedData(ClientContext &context, ResultLifetime lifetime, + ResultFormatContext format_context, shared_ptr format) + : BufferedData(BufferedData::Type::SIMPLE, context, lifetime, std::move(format_context), std::move(format)), + buffered_count(0), buffer_size(total_buffer_size) { } SimpleBufferedData::~SimpleBufferedData() { @@ -24,11 +26,22 @@ idx_t SimpleBufferedData::PeakBufferedBytes() { return peak_buffered_bytes; } +idx_t SimpleBufferedData::PeakStreamingBytes() { + annotated_lock_guard lock(glock); + return peak_streaming_bytes; +} + bool SimpleBufferedData::HasBlockedSink() { annotated_lock_guard lock(glock); return !blocked_sinks.empty(); } +bool SimpleBufferedData::HasObservableUnit() { + annotated_lock_guard lock(glock); + // Readiness is the unit queue, never the byte count: units with rows but zero data bytes exist + return !unread_units.empty(); +} + void SimpleBufferedData::CollectRestartableSinks(annotated_lock_guard &lock, vector &to_unblock) { D_ASSERT(to_unblock.empty()); @@ -36,17 +49,19 @@ void SimpleBufferedData::CollectRestartableSinks(annotated_lock_guard 0 && buffered_count + front.pending_bytes > BufferSize()) { + // Sinks restart in FIFO order. Stop at the first unit that does not fit yet + if (buffered_count > 0 && buffered_count + front.PendingBytes() > BufferSize()) { break; } - // Deposit the parked copy, so the chunk is visible before the producer wakes. - // Parks always carry their copy; the guard keeps a null from corrupting the queue - if (front.pending_chunk) { - buffered_count += front.pending_bytes; - unread_chunks.push(BufferedChunk {std::move(front.pending_chunk), front.pending_bytes}); + // Deposit the parked unit, so it is visible before the producer wakes. + // Parks always carry their unit; the guard keeps a null from corrupting the queue + if (front.pending_unit) { + const idx_t pending_bytes = front.pending_unit->byte_size; + parked_bytes -= pending_bytes; + buffered_count += pending_bytes; + unread_units.push(std::move(front.pending_unit)); peak_buffered_bytes = MaxValue(peak_buffered_bytes, buffered_count); - front.pending_bytes = 0; + peak_streaming_bytes = MaxValue(peak_streaming_bytes, buffered_count + parked_bytes); } to_unblock.push_back(std::move(front)); blocked_sinks.pop(); @@ -87,46 +102,46 @@ void SimpleBufferedData::AssertNoBlockedSinks() { #endif } -unique_ptr SimpleBufferedData::Scan() { +unique_ptr SimpleBufferedData::Scan() { if (Closed()) { return nullptr; } - unique_ptr chunk; + unique_ptr unit; vector to_unblock; { annotated_lock_guard lock(glock); - if (unread_chunks.empty()) { + if (unread_units.empty()) { Close(); return nullptr; } - auto ref = std::move(unread_chunks.front()); - unread_chunks.pop(); - chunk = std::move(ref.chunk); - buffered_count -= ref.data_size; + unit = std::move(unread_units.front()); + unread_units.pop(); + buffered_count -= unit->byte_size; // The pop restarts blocked producers below the low-water mark if (buffered_count < LowWaterMark(BufferSize())) { CollectRestartableSinks(lock, to_unblock); } } InvokeUnblocks(to_unblock); - return chunk; + return unit; } -bool SimpleBufferedData::AppendOrBlock(DataChunk &to_append, const InterruptState &blocked_sink) { - // Copied outside the lock: both outcomes need the copy, and parallel producers copy concurrently - auto copy = CopyForBuffering(to_append); - const idx_t chunk_data_size = copy->GetDataSize(); +bool SimpleBufferedData::AppendOrBlock(unique_ptr unit, const InterruptState &blocked_sink) { + const idx_t unit_data_size = unit->byte_size; annotated_lock_guard lock(glock); - // The buffer admits a chunk that fits, and always one chunk when empty - if (buffered_count > 0 && buffered_count + chunk_data_size > BufferSize()) { - // Park holding the finished copy. Restart selection deposits it at wake time - blocked_sinks.push(BlockedSink {blocked_sink, chunk_data_size, std::move(copy)}); + // The buffer admits a unit that fits, and always one unit when empty + if (buffered_count > 0 && buffered_count + unit_data_size > BufferSize()) { + // Park holding the finished unit. Restart selection deposits it at wake time + parked_bytes += unit_data_size; + peak_streaming_bytes = MaxValue(peak_streaming_bytes, buffered_count + parked_bytes); + blocked_sinks.push(BlockedSink {blocked_sink, std::move(unit)}); return true; } - unread_chunks.push(BufferedChunk {std::move(copy), chunk_data_size}); - buffered_count += chunk_data_size; + unread_units.push(std::move(unit)); + buffered_count += unit_data_size; peak_buffered_bytes = MaxValue(peak_buffered_bytes, buffered_count); + peak_streaming_bytes = MaxValue(peak_streaming_bytes, buffered_count + parked_bytes); return false; } diff --git a/src/duckdb/src/main/capi/v1/aggregate_function-c.cpp b/src/duckdb/src/main/capi/v1/aggregate_function-c.cpp index 7cebd2005..26c7de981 100644 --- a/src/duckdb/src/main/capi/v1/aggregate_function-c.cpp +++ b/src/duckdb/src/main/capi/v1/aggregate_function-c.cpp @@ -1,4 +1,5 @@ #include "duckdb/catalog/catalog.hpp" +#include "duckdb/main/capi/capi_function_signature.hpp" #include "duckdb/common/type_visitor.hpp" #include "duckdb/common/types.hpp" #include "duckdb/function/function.hpp" @@ -100,8 +101,14 @@ void CAPIAggregateUpdate(Vector inputs[], AggregateInputData &aggr_input_data, i chunk.data.emplace_back(Vector::Ref(inputs[c])); } + // the callback expects one state per row - flatten a reference, as callers may hold on to the state buffer + auto states = Vector::Ref(state); + if (states.GetVectorType() != VectorType::FLAT_VECTOR) { + states.Flatten(*FlatVector::IncrementalSelectionVector(), count); + } + auto &bind_data = aggr_input_data.bind_data->Cast(); - auto state_data = FlatVector::GetDataMutableUnsafe(state); + auto state_data = FlatVector::GetDataMutableUnsafe(states); auto c_input_chunk = reinterpret_cast(&chunk); CAggregateExecuteInfo exec_info(bind_data.info); @@ -186,7 +193,8 @@ void duckdb_aggregate_function_add_parameter(duckdb_aggregate_function function, } auto &aggregate_function = GetCAggregateFunction(function); auto logical_type = reinterpret_cast(type); - aggregate_function.GetSignature().AddParameter(*logical_type); + // v1 declares no parameter name, so the parameter is positional-only - see duckdb_scalar_function_add_parameter + duckdb::CAPIFunctionSignature::AddPositionalOnly(aggregate_function.GetSignature(), *logical_type); } void duckdb_aggregate_function_set_return_type(duckdb_aggregate_function function, duckdb_logical_type type) { diff --git a/src/duckdb/src/main/capi/v1/arrow-c.cpp b/src/duckdb/src/main/capi/v1/arrow-c.cpp index aa57f4591..d2432e60c 100644 --- a/src/duckdb/src/main/capi/v1/arrow-c.cpp +++ b/src/duckdb/src/main/capi/v1/arrow-c.cpp @@ -1,19 +1,51 @@ #include "duckdb/common/arrow/arrow.hpp" #include "duckdb/common/arrow/arrow_converter.hpp" +#include "duckdb/common/arrow/arrow_format.hpp" #include "duckdb/function/table/arrow.hpp" +#include "duckdb/common/types/column/column_data_collection.hpp" #include "duckdb/main/capi/capi_internal.hpp" +#include "duckdb/main/query_parameters.hpp" #include "fmt/format.h" using duckdb::ArrowConverter; +using duckdb::ArrowFormat; using duckdb::ArrowResultWrapper; using duckdb::CClientArrowOptionsWrapper; using duckdb::Connection; using duckdb::DataChunk; using duckdb::LogicalType; -using duckdb::MaterializedQueryResult; using duckdb::PreparedStatementWrapper; +using duckdb::QueryParameters; using duckdb::QueryResult; -using duckdb::QueryResultType; + +namespace { + +//! Every duckdb_arrow result is produced in the Arrow format, so the engine's worker threads build +//! the Arrow arrays and duckdb_query_arrow_array only hands them out +QueryParameters ArrowParameters() { + return QueryParameters(duckdb::make_shared_ptr(STANDARD_VECTOR_SIZE)); +} + +//! The rows a CHANGED_ROWS statement affected +idx_t ChangedRows(QueryResult &result) { + if (result.HasError()) { + return 0; + } + if (result.GetStatementProperties().return_type != duckdb::StatementReturnType::CHANGED_ROWS) { + return 0; + } + auto &types = result.GetTypes(); + if (types.size() != 1 || types[0].id() != duckdb::LogicalTypeId::BIGINT) { + return 0; + } + auto &arrays = result.Collection(); + if (arrays.empty()) { + return 0; + } + return ArrowFormat::ChangedRows(arrays.front()->arrow_array); +} + +} // namespace duckdb_error_data duckdb_to_arrow_schema(duckdb_arrow_options arrow_options, duckdb_logical_type *types, const char **names, idx_t column_count, struct ArrowSchema *out_schema) { @@ -106,17 +138,18 @@ duckdb_error_data duckdb_data_chunk_from_arrow(duckdb_connection connection, str auto &arrow_types = arrow_table->GetColumns(); dchunk->SetChildCardinality(duckdb::NumericCast(arrow_array->length)); + // All columns share ownership of the array, so it is only released once no column references it anymore + auto owned_data = duckdb::make_shared_ptr(); + owned_data->arrow_array = *arrow_array; + // We set it to nullptr to effectively transfer the ownership + arrow_array->release = nullptr; for (idx_t i = 0; i < dchunk->ColumnCount(); i++) { auto &parent_array = *arrow_array; auto &array = parent_array.children[i]; auto arrow_type = arrow_types.at(i); auto array_physical_type = arrow_type->GetPhysicalType(); auto array_state = duckdb::make_uniq(*conn->context); - // We need to make sure that our chunk will hold the ownership - array_state->owned_data = duckdb::make_shared_ptr(); - array_state->owned_data->arrow_array = *arrow_array; - // We set it to nullptr to effectively transfer the ownership - arrow_array->release = nullptr; + array_state->owned_data = owned_data; try { switch (array_physical_type) { case duckdb::ArrowArrayPhysicalType::DICTIONARY_ENCODED: @@ -162,13 +195,13 @@ duckdb_state duckdb_query_arrow(duckdb_connection connection, const char *query, Connection *conn = (Connection *)connection; auto wrapper = new ArrowResultWrapper(); try { - wrapper->result = conn->Query(query); - *out_result = (duckdb_arrow)wrapper; - return !wrapper->result->HasError() ? DuckDBSuccess : DuckDBError; + wrapper->result = conn->context->Query(query, ArrowParameters()); } catch (...) { delete wrapper; return DuckDBError; } + *out_result = (duckdb_arrow)wrapper; + return !wrapper->result->HasError() ? DuckDBSuccess : DuckDBError; } duckdb_state duckdb_query_arrow_schema(duckdb_arrow result, duckdb_arrow_schema *out_schema) { @@ -234,17 +267,20 @@ duckdb_state duckdb_query_arrow_array(duckdb_arrow result, duckdb_arrow_array *o return DuckDBSuccess; } auto wrapper = reinterpret_cast(result); - auto success = wrapper->result->TryFetch(wrapper->current_chunk, wrapper->result->GetErrorObject()); - if (!success) { // LCOV_EXCL_START + duckdb::unique_ptr array; + try { + array = wrapper->result->Fetch(); + } catch (std::exception &ex) { // LCOV_EXCL_START + wrapper->result->SetError(duckdb::ErrorData(ex)); + return DuckDBError; + } catch (...) { + wrapper->result->SetError(duckdb::ErrorData("Unknown error in Fetch")); return DuckDBError; } // LCOV_EXCL_STOP - if (!wrapper->current_chunk || wrapper->current_chunk->size() == 0) { + if (!array) { return DuckDBSuccess; } - auto extension_type_cast = duckdb::ArrowTypeExtensionData::GetExtensionTypes( - *wrapper->result->client_properties.client_context, wrapper->result->GetTypes()); - ArrowConverter::ToArrowArray(*wrapper->current_chunk, reinterpret_cast(*out_array), - wrapper->result->client_properties, extension_type_cast); + array->MoveTo(*reinterpret_cast(*out_array)); return DuckDBSuccess; } @@ -254,11 +290,12 @@ void duckdb_result_arrow_array(duckdb_result result, duckdb_data_chunk chunk, du } auto dchunk = reinterpret_cast(chunk); auto &result_data = *(reinterpret_cast(result.internal_data)); - auto extension_type_cast = duckdb::ArrowTypeExtensionData::GetExtensionTypes( - *result_data.result->client_properties.client_context, result_data.result->GetTypes()); + auto &client_properties = result_data.GetClientProperties(); + auto extension_type_cast = + duckdb::ArrowTypeExtensionData::GetExtensionTypes(*client_properties.client_context, result_data.GetTypes()); - ArrowConverter::ToArrowArray(*dchunk, reinterpret_cast(*out_array), - result_data.result->client_properties, extension_type_cast); + ArrowConverter::ToArrowArray(*dchunk, reinterpret_cast(*out_array), client_properties, + extension_type_cast); } idx_t duckdb_arrow_row_count(duckdb_arrow result) { @@ -276,20 +313,11 @@ idx_t duckdb_arrow_column_count(duckdb_arrow result) { idx_t duckdb_arrow_rows_changed(duckdb_arrow result) { auto wrapper = reinterpret_cast(result); - if (wrapper->result->HasError()) { + try { + return ChangedRows(*wrapper->result); + } catch (...) { // LCOV_EXCL_START return 0; - } - idx_t rows_changed = 0; - auto &collection = wrapper->result->Collection(); - idx_t row_count = collection.Count(); - if (row_count > 0 && - wrapper->result->GetStatementProperties().return_type == duckdb::StatementReturnType::CHANGED_ROWS) { - auto rows = collection.GetRows(); - D_ASSERT(row_count == 1); - D_ASSERT(rows.size() == 1); - rows_changed = duckdb::NumericCast(rows[0].GetValue(0).GetValue()); - } - return rows_changed; + } // LCOV_EXCL_STOP } const char *duckdb_query_arrow_error(duckdb_arrow result) { @@ -326,15 +354,13 @@ duckdb_state duckdb_execute_prepared_arrow(duckdb_prepared_statement prepared_st } auto arrow_wrapper = new ArrowResultWrapper(); try { - auto result = wrapper->statement->Execute(wrapper->values, false); - D_ASSERT(result->GetResultType() == QueryResultType::MATERIALIZED_RESULT); - arrow_wrapper->result = duckdb::unique_ptr_cast(std::move(result)); - *out_result = reinterpret_cast(arrow_wrapper); - return !arrow_wrapper->result->HasError() ? DuckDBSuccess : DuckDBError; + arrow_wrapper->result = wrapper->statement->Execute(wrapper->values, ArrowParameters()); } catch (...) { delete arrow_wrapper; return DuckDBError; } + *out_result = reinterpret_cast(arrow_wrapper); + return !arrow_wrapper->result->HasError() ? DuckDBSuccess : DuckDBError; } namespace arrow_array_stream_wrapper { @@ -394,14 +420,27 @@ int GetNext(struct ArrowArrayStream *stream, struct ArrowArray *out) { return DuckDBSuccess; } -duckdb::unique_ptr FactoryGetNext(uintptr_t stream_factory_ptr, - duckdb::ArrowStreamParameters ¶meters) { - auto stream = reinterpret_cast(stream_factory_ptr); - auto ret = duckdb::make_uniq(); - ret->arrow_array_stream = *stream; - ret->arrow_array_stream.release = EmptyStreamRelease; - return ret; -} +class CArrowScanFactory : public duckdb::ArrowScanFactory { +public: + explicit CArrowScanFactory(ArrowArrayStream &stream_p) : stream(stream_p) { + } + + void GetSchema(ArrowSchema &schema) override { + FactoryGetSchema(&stream.get(), schema); + } + + duckdb::unique_ptr + ProduceStream(duckdb::ArrowStreamParameters ¶meters) override { + auto result = duckdb::make_uniq(); + result->arrow_array_stream = stream.get(); + result->arrow_array_stream.release = EmptyStreamRelease; + return result; + } + +private: + //! The caller retains ownership of the stream and its arrays. + duckdb::reference stream; +}; // LCOV_EXCL_START // This function is never be called, because it's used to construct a stream wrapping around a caller-supplied @@ -423,10 +462,11 @@ void Release(struct ArrowArrayStream *stream) { duckdb_state Ingest(duckdb_connection connection, const char *table_name, struct ArrowArrayStream *input) { try { auto cconn = reinterpret_cast(connection); - cconn - ->TableFunction("arrow_scan", {duckdb::Value::POINTER((uintptr_t)input), - duckdb::Value::POINTER((uintptr_t)FactoryGetNext), - duckdb::Value::POINTER((uintptr_t)FactoryGetSchema)}) + if (!input) { + return DuckDBError; + } + auto factory = duckdb::make_shared_ptr(*input); + cconn->TableFunction("arrow_scan", {}, {}, std::move(factory)) ->CreateView(duckdb::Identifier(table_name), true, false); } catch (...) { // LCOV_EXCL_START // Tried covering this in tests, but it proved harder than expected. At the time of writing: diff --git a/src/duckdb/src/main/capi/v1/copy_function-c.cpp b/src/duckdb/src/main/capi/v1/copy_function-c.cpp index c4f45391c..5e1bc17bc 100644 --- a/src/duckdb/src/main/capi/v1/copy_function-c.cpp +++ b/src/duckdb/src/main/capi/v1/copy_function-c.cpp @@ -650,12 +650,12 @@ unique_ptr CCopyFromBind(ClientContext &context, CopyFromFunctionB auto &tf_info = info.tf.function_info->Cast(); auto result = make_uniq(tf_info); - named_parameter_map_t named_parameters; + named_argument_map_t named_parameters; // Turn all options into named parameters for (auto opt : info.info.options) { - auto param_it = info.tf.named_parameters.find(Identifier(opt.first)); - if (param_it == info.tf.named_parameters.end()) { + auto option_schema = info.tf.GetSignature().GetTypedKwargs(); + if (!option_schema || !option_schema->Find(Identifier(opt.first))) { // Option not found in the table function's named parameters throw BinderException("'%s' is not a supported option for copy function '%s'", opt.first.c_str(), info.tf.name.c_str()); @@ -724,14 +724,21 @@ void duckdb_copy_function_set_copy_from_function(duckdb_copy_function copy_funct if (!tf_info.bind || !tf_info.init || !tf_info.function) { return; } - for (auto it = tf.named_parameters.begin(); it != tf.named_parameters.end(); it++) { - if (duckdb::TypeVisitor::Contains(it->second, duckdb::LogicalTypeId::INVALID)) { + for (auto ¶m : tf.GetSignature().GetParameters()) { + // a variadic parameter carries no type of its own + if (param.IsVariadic()) { + continue; + } + if (duckdb::TypeVisitor::Contains(param.GetType(), duckdb::LogicalTypeId::INVALID)) { return; } } - for (const auto &argument : tf.GetArguments()) { - if (duckdb::TypeVisitor::Contains(argument, duckdb::LogicalTypeId::INVALID)) { - return; + auto option_schema = tf.GetSignature().GetTypedKwargs(); + if (option_schema) { + for (auto &option : option_schema->GetOptions()) { + if (duckdb::TypeVisitor::Contains(option.type, duckdb::LogicalTypeId::INVALID)) { + return; + } } } diff --git a/src/duckdb/src/main/capi/v1/helper-c.cpp b/src/duckdb/src/main/capi/v1/helper-c.cpp index 7fb8ff627..9b9a7a655 100644 --- a/src/duckdb/src/main/capi/v1/helper-c.cpp +++ b/src/duckdb/src/main/capi/v1/helper-c.cpp @@ -411,6 +411,8 @@ duckdb_error_type ErrorTypeToC(const ExceptionType type) { return DUCKDB_ERROR_DATA_CORRUPTION; case ExceptionType::INVALID_CONFIGURATION: return DUCKDB_INVALID_CONFIGURATION; + case ExceptionType::RESOURCE_IN_USE: + return DUCKDB_ERROR_RESOURCE_IN_USE; default: return DUCKDB_ERROR_INVALID; } @@ -506,6 +508,8 @@ ExceptionType ErrorTypeFromC(const duckdb_error_type type) { return ExceptionType::DATA_CORRUPTION; case DUCKDB_INVALID_CONFIGURATION: return ExceptionType::INVALID_CONFIGURATION; + case DUCKDB_ERROR_RESOURCE_IN_USE: + return ExceptionType::RESOURCE_IN_USE; default: return ExceptionType::INVALID; } diff --git a/src/duckdb/src/main/capi/v1/pending-c.cpp b/src/duckdb/src/main/capi/v1/pending-c.cpp index 1d4fa4dcb..3b2d4d2d7 100644 --- a/src/duckdb/src/main/capi/v1/pending-c.cpp +++ b/src/duckdb/src/main/capi/v1/pending-c.cpp @@ -1,17 +1,17 @@ #include "duckdb/main/capi/capi_internal.hpp" #include "duckdb/main/query_result.hpp" -#include "duckdb/main/pending_query_result.hpp" #include "duckdb/common/error_data.hpp" #include "duckdb/common/case_insensitive_map.hpp" #include "duckdb/common/optional_ptr.hpp" using duckdb::case_insensitive_map_t; +using duckdb::IsObservable; using duckdb::make_uniq; using duckdb::optional_ptr; -using duckdb::PendingExecutionResult; -using duckdb::PendingQueryResult; using duckdb::PendingStatementWrapper; using duckdb::PreparedStatementWrapper; +using duckdb::QueryResult; +using duckdb::QueryResultState; using duckdb::Value; duckdb_state duckdb_pending_prepared_internal(duckdb_prepared_statement prepared_statement, @@ -21,12 +21,18 @@ duckdb_state duckdb_pending_prepared_internal(duckdb_prepared_statement prepared } auto wrapper = reinterpret_cast(prepared_statement); auto result = new PendingStatementWrapper(); - result->allow_streaming = allow_streaming; + result->allow_streaming = false; try { - result->statement = wrapper->statement->PendingQuery(wrapper->values, allow_streaming); + result->statement = wrapper->statement->Submit(wrapper->values); + result->allow_streaming = + allow_streaming && duckdb::StreamsByDefault(wrapper->statement->GetStatementType(), *result->statement); + if (!result->allow_streaming) { + // Settled before the caller drives it, so each execute_task call stays one unit of work + result->statement->Materialize(); + } } catch (std::exception &ex) { - result->statement = make_uniq(duckdb::ErrorData(ex)); + result->statement = make_uniq(duckdb::ErrorData(ex)); } duckdb_state return_value = !result->statement->HasError() ? DuckDBSuccess : DuckDBError; *out_result = reinterpret_cast(result); @@ -77,20 +83,21 @@ duckdb_pending_state duckdb_pending_execute_check_state(duckdb_pending_result pe if (wrapper->statement->HasError()) { return DUCKDB_PENDING_ERROR; } - PendingExecutionResult return_value; + QueryResultState return_value; try { - return_value = wrapper->statement->CheckPulse(); + return_value = wrapper->statement->Poll(); } catch (std::exception &ex) { wrapper->statement->SetError(duckdb::ErrorData(ex)); return DUCKDB_PENDING_ERROR; } switch (return_value) { - case PendingExecutionResult::BLOCKED: - case PendingExecutionResult::RESULT_READY: + case QueryResultState::BLOCKED: + case QueryResultState::READY: + case QueryResultState::FINISHED: return DUCKDB_PENDING_RESULT_READY; - case PendingExecutionResult::NO_TASKS_AVAILABLE: + case QueryResultState::NO_TASKS_AVAILABLE: return DUCKDB_PENDING_NO_TASKS_AVAILABLE; - case PendingExecutionResult::RESULT_NOT_READY: + case QueryResultState::NOT_READY: return DUCKDB_PENDING_RESULT_NOT_READY; default: return DUCKDB_PENDING_ERROR; @@ -108,7 +115,7 @@ duckdb_pending_state duckdb_pending_execute_task(duckdb_pending_result pending_r if (wrapper->statement->HasError()) { return DUCKDB_PENDING_ERROR; } - PendingExecutionResult return_value; + QueryResultState return_value; try { return_value = wrapper->statement->ExecuteTask(); } catch (std::exception &ex) { @@ -116,13 +123,13 @@ duckdb_pending_state duckdb_pending_execute_task(duckdb_pending_result pending_r return DUCKDB_PENDING_ERROR; } switch (return_value) { - case PendingExecutionResult::EXECUTION_FINISHED: - case PendingExecutionResult::RESULT_READY: + case QueryResultState::FINISHED: + case QueryResultState::READY: return DUCKDB_PENDING_RESULT_READY; - case PendingExecutionResult::BLOCKED: - case PendingExecutionResult::NO_TASKS_AVAILABLE: + case QueryResultState::BLOCKED: + case QueryResultState::NO_TASKS_AVAILABLE: return DUCKDB_PENDING_NO_TASKS_AVAILABLE; - case PendingExecutionResult::RESULT_NOT_READY: + case QueryResultState::NOT_READY: return DUCKDB_PENDING_RESULT_NOT_READY; default: return DUCKDB_PENDING_ERROR; @@ -132,15 +139,15 @@ duckdb_pending_state duckdb_pending_execute_task(duckdb_pending_result pending_r bool duckdb_pending_execution_is_finished(duckdb_pending_state pending_state) { switch (pending_state) { case DUCKDB_PENDING_RESULT_READY: - return PendingQueryResult::IsResultReady(PendingExecutionResult::RESULT_READY); + return IsObservable(QueryResultState::READY); case DUCKDB_PENDING_NO_TASKS_AVAILABLE: - return PendingQueryResult::IsResultReady(PendingExecutionResult::NO_TASKS_AVAILABLE); + return IsObservable(QueryResultState::NO_TASKS_AVAILABLE); case DUCKDB_PENDING_RESULT_NOT_READY: - return PendingQueryResult::IsResultReady(PendingExecutionResult::RESULT_NOT_READY); + return IsObservable(QueryResultState::NOT_READY); case DUCKDB_PENDING_ERROR: - return PendingQueryResult::IsResultReady(PendingExecutionResult::EXECUTION_ERROR); + return IsObservable(QueryResultState::EXECUTION_ERROR); default: - return PendingQueryResult::IsResultReady(PendingExecutionResult::EXECUTION_ERROR); + return IsObservable(QueryResultState::EXECUTION_ERROR); } } @@ -154,14 +161,17 @@ duckdb_state duckdb_execute_pending(duckdb_pending_result pending_result, duckdb return DuckDBError; } - duckdb::unique_ptr result; + auto result = std::move(wrapper->statement); + const bool stream_result = wrapper->allow_streaming && !result->HasError(); try { - result = wrapper->statement->Execute(); + if (stream_result) { + return DuckDBTranslateStreamResult(duckdb::make_uniq>(std::move(result)), + out_result); + } + result->Complete(); } catch (std::exception &ex) { duckdb::ErrorData error(ex); - result = duckdb::make_uniq(std::move(error)); + result = duckdb::make_uniq(std::move(error)); } - - wrapper->statement.reset(); return DuckDBTranslateResult(std::move(result), out_result); } diff --git a/src/duckdb/src/main/capi/v1/prepared-c.cpp b/src/duckdb/src/main/capi/v1/prepared-c.cpp index 9f80bd7ac..20b7fd984 100644 --- a/src/duckdb/src/main/capi/v1/prepared-c.cpp +++ b/src/duckdb/src/main/capi/v1/prepared-c.cpp @@ -13,10 +13,8 @@ using duckdb::ErrorData; using duckdb::ExtractStatementsWrapper; using duckdb::hugeint_t; using duckdb::LogicalType; -using duckdb::MaterializedQueryResult; using duckdb::optional_ptr; using duckdb::PreparedStatementWrapper; -using duckdb::QueryResultType; using duckdb::StringUtil; using duckdb::timestamp_t; using duckdb::uhugeint_t; @@ -432,7 +430,7 @@ duckdb_state duckdb_execute_prepared(duckdb_prepared_statement prepared_statemen duckdb::unique_ptr result; try { - result = wrapper->statement->Execute(wrapper->values, false); + result = wrapper->statement->Execute(wrapper->values); } catch (...) { return DuckDBError; } @@ -447,8 +445,15 @@ duckdb_state duckdb_execute_prepared_streaming(duckdb_prepared_statement prepare } try { - auto result = wrapper->statement->Execute(wrapper->values, true); - return DuckDBTranslateResult(std::move(result), out_result); + auto result = wrapper->statement->Submit(wrapper->values); + // Without an out_result the stream is dropped unread, which rolls back what the statement wrote + if (result->HasError() || + !duckdb::StreamsByDefault(wrapper->statement->GetStatementType(), *result, out_result != nullptr)) { + result->Complete(); + return DuckDBTranslateResult(std::move(result), out_result); + } + return DuckDBTranslateStreamResult(duckdb::make_uniq>(std::move(result)), + out_result); } catch (...) { return DuckDBError; } diff --git a/src/duckdb/src/main/capi/v1/result-c.cpp b/src/duckdb/src/main/capi/v1/result-c.cpp index 479042187..2d8c7ec89 100644 --- a/src/duckdb/src/main/capi/v1/result-c.cpp +++ b/src/duckdb/src/main/capi/v1/result-c.cpp @@ -147,7 +147,7 @@ void WriteData(duckdb_column *column, ColumnDataCollection &source, const vector } } -duckdb_state deprecated_duckdb_translate_column(MaterializedQueryResult &result, duckdb_column *column, idx_t col) { +duckdb_state deprecated_duckdb_translate_column(QueryResult &result, duckdb_column *column, idx_t col) { D_ASSERT(!result.HasError()); auto &collection = result.Collection(); idx_t row_count = collection.Count(); @@ -296,6 +296,29 @@ duckdb_state deprecated_duckdb_translate_column(MaterializedQueryResult &result, return DuckDBSuccess; } +duckdb_state DuckDBTranslateStreamResult(unique_ptr> stream_p, duckdb_result *out) { + D_ASSERT(stream_p); + auto &stream = *stream_p; + if (!out) { + // no result to write to, only return the status + return !stream.HasError() ? DuckDBSuccess : DuckDBError; + } + memset(out, 0, sizeof(duckdb_result)); + + auto result_data = new DuckDBResultData(); + result_data->stream = std::move(stream_p); + result_data->result_set_type = CAPIResultSetType::CAPI_RESULT_TYPE_NONE; + out->internal_data = result_data; + + if (stream.HasError()) { + out->deprecated_error_message = (char *)stream.GetError().c_str(); // NOLINT + return DuckDBError; + } + out->deprecated_column_count = stream.ColumnCount(); + out->deprecated_rows_changed = 0; + return DuckDBSuccess; +} + duckdb_state DuckDBTranslateResult(unique_ptr result_p, duckdb_result *out) { auto &result = *result_p; D_ASSERT(result_p); @@ -324,12 +347,30 @@ duckdb_state DuckDBTranslateResult(unique_ptr result_p, duckdb_resu return DuckDBSuccess; } +//! Drain a stream into a retained result, for the random-access deprecated result set +static unique_ptr DrainStreamIntoResult(QueryResultStream<> &stream) { + auto collection = make_uniq(Allocator::DefaultAllocator(), stream.GetTypes()); + ColumnDataAppendState append_state; + collection->InitializeAppend(append_state); + while (auto chunk = stream.Fetch()) { + if (chunk->size() == 0) { + break; + } + collection->Append(append_state, *chunk); + } + if (stream.HasError()) { + return make_uniq(stream.GetErrorObject()); + } + return make_uniq(stream.GetStatementType(), stream.GetStatementProperties(), stream.GetNames(), + std::move(collection), stream.GetClientProperties()); +} + bool DeprecatedMaterializeResult(duckdb_result *result) { if (!result) { return false; } auto result_data = reinterpret_cast(result->internal_data); - if (result_data->result->HasError()) { + if (result_data->HasError()) { return false; } if (result_data->result_set_type == CAPIResultSetType::CAPI_RESULT_TYPE_DEPRECATED) { @@ -346,34 +387,36 @@ bool DeprecatedMaterializeResult(duckdb_result *result) { } // materialize as deprecated result set result_data->result_set_type = CAPIResultSetType::CAPI_RESULT_TYPE_DEPRECATED; - auto column_count = result_data->result->ColumnCount(); + auto column_count = result_data->ColumnCount(); result->deprecated_columns = (duckdb_column *)duckdb_malloc(sizeof(duckdb_column) * column_count); if (!result->deprecated_columns) { // LCOV_EXCL_START // malloc failure return DuckDBError; } // LCOV_EXCL_STOP - if (result_data->result->GetResultType() == QueryResultType::STREAM_RESULT) { - // if we are dealing with a stream result, convert it to a materialized result first - auto &stream_result = (StreamQueryResult &)*result_data->result; - result_data->result = stream_result.Materialize(); + if (result_data->IsStreaming()) { + // The deprecated result set is random-access: drain the stream into a collection + result_data->result = DrainStreamIntoResult(*result_data->stream); + result_data->stream.reset(); + if (result_data->result->HasError()) { + return false; + } } - D_ASSERT(result_data->result->GetResultType() == QueryResultType::MATERIALIZED_RESULT); - auto &materialized = reinterpret_cast(*result_data->result); + auto &materialized = *result_data->result; // convert the result to a materialized result // zero initialize the columns (so we can cleanly delete it in case a malloc fails) memset(result->deprecated_columns, 0, sizeof(duckdb_column) * column_count); for (idx_t i = 0; i < column_count; i++) { - result->deprecated_columns[i].deprecated_type = LogicalTypeIdToC(result_data->result->GetTypes()[i].id()); - result->deprecated_columns[i].deprecated_name = (char *)result_data->result->ColumnName(i).c_str(); // NOLINT + result->deprecated_columns[i].deprecated_type = LogicalTypeIdToC(materialized.GetTypes()[i].id()); + result->deprecated_columns[i].deprecated_name = (char *)materialized.ColumnName(i).c_str(); // NOLINT } result->deprecated_row_count = materialized.RowCount(); if (result->deprecated_row_count > 0 && materialized.GetStatementProperties().return_type == StatementReturnType::CHANGED_ROWS) { // update total changes - auto row_changes = materialized.GetValue(0, 0); + auto row_changes = materialized.Collection().GetValue(0, 0); if (!row_changes.IsNull()) { auto cast_row_changes = row_changes.DefaultTryCastAs(LogicalType::BIGINT); if (cast_row_changes) { @@ -439,7 +482,7 @@ const char *duckdb_column_name(duckdb_result *result, idx_t col) { return nullptr; } auto &result_data = *(reinterpret_cast(result->internal_data)); - return result_data.result->ColumnName(col).c_str(); + return result_data.ColumnName(col).c_str(); } duckdb_type duckdb_column_type(duckdb_result *result, idx_t col) { @@ -447,7 +490,7 @@ duckdb_type duckdb_column_type(duckdb_result *result, idx_t col) { return DUCKDB_TYPE_INVALID; } auto &result_data = *(reinterpret_cast(result->internal_data)); - return duckdb::LogicalTypeIdToC(result_data.result->GetTypes()[col].id()); + return duckdb::LogicalTypeIdToC(result_data.GetTypes()[col].id()); } duckdb_logical_type duckdb_column_logical_type(duckdb_result *result, idx_t col) { @@ -455,7 +498,7 @@ duckdb_logical_type duckdb_column_logical_type(duckdb_result *result, idx_t col) return nullptr; } auto &result_data = *(reinterpret_cast(result->internal_data)); - return reinterpret_cast(new duckdb::LogicalType(result_data.result->GetTypes()[col])); + return reinterpret_cast(new duckdb::LogicalType(result_data.GetTypes()[col])); } duckdb_arrow_options duckdb_result_get_arrow_options(duckdb_result *result) { @@ -463,10 +506,10 @@ duckdb_arrow_options duckdb_result_get_arrow_options(duckdb_result *result) { return nullptr; } auto &result_data = *(reinterpret_cast(result->internal_data)); - if (!result_data.result) { + if (!result_data.result && !result_data.stream) { return nullptr; } - auto arrow_options_wrapper = new duckdb::CClientArrowOptionsWrapper(result_data.result->client_properties); + auto arrow_options_wrapper = new duckdb::CClientArrowOptionsWrapper(result_data.GetClientProperties()); return reinterpret_cast(arrow_options_wrapper); } @@ -478,7 +521,7 @@ idx_t duckdb_column_count(duckdb_result *result) { return 0; } auto &result_data = *(reinterpret_cast(result->internal_data)); - return result_data.result->ColumnCount(); + return result_data.ColumnCount(); } idx_t duckdb_row_count(duckdb_result *result) { @@ -486,12 +529,11 @@ idx_t duckdb_row_count(duckdb_result *result) { return 0; } auto &result_data = *(reinterpret_cast(result->internal_data)); - if (result_data.result->GetResultType() == duckdb::QueryResultType::STREAM_RESULT) { + if (result_data.IsStreaming()) { // We can't know the row count beforehand return 0; } - auto &materialized = reinterpret_cast(*result_data.result); - return materialized.RowCount(); + return result_data.Retained().RowCount(); } idx_t duckdb_rows_changed(duckdb_result *result) { @@ -503,7 +545,10 @@ idx_t duckdb_rows_changed(duckdb_result *result) { // not a materialized result return result->deprecated_rows_changed; } - auto &materialized = reinterpret_cast(*result_data.result); + if (result_data.IsStreaming()) { + return 0; + } + auto &materialized = result_data.Retained(); if (materialized.GetStatementProperties().return_type != duckdb::StatementReturnType::CHANGED_ROWS) { // we can only use this function for CHANGED_ROWS result types return 0; @@ -512,7 +557,7 @@ idx_t duckdb_rows_changed(duckdb_result *result) { // CHANGED_ROWS should return exactly one row return 0; } - return materialized.GetValue(0, 0).GetValue(); + return materialized.Collection().GetValue(0, 0).GetValue(); } void *duckdb_column_data(duckdb_result *result, idx_t col) { @@ -540,7 +585,7 @@ const char *duckdb_result_error(duckdb_result *result) { return nullptr; } auto &result_data = *(reinterpret_cast(result->internal_data)); - return !result_data.result->HasError() ? nullptr : result_data.result->GetError().c_str(); + return !result_data.HasError() ? nullptr : result_data.GetError().c_str(); } duckdb_error_type duckdb_result_error_type(duckdb_result *result) { @@ -548,10 +593,10 @@ duckdb_error_type duckdb_result_error_type(duckdb_result *result) { return DUCKDB_ERROR_INVALID; } auto &result_data = *(reinterpret_cast(result->internal_data)); - if (!result_data.result->HasError()) { + if (!result_data.HasError()) { return DUCKDB_ERROR_INVALID; } - return duckdb::ErrorTypeToC(result_data.result->GetErrorType()); + return duckdb::ErrorTypeToC(result_data.GetErrorType()); } idx_t duckdb_result_chunk_count(duckdb_result result) { @@ -562,12 +607,11 @@ idx_t duckdb_result_chunk_count(duckdb_result result) { if (result_data.result_set_type == duckdb::CAPIResultSetType::CAPI_RESULT_TYPE_DEPRECATED) { return 0; } - if (result_data.result->GetResultType() != duckdb::QueryResultType::MATERIALIZED_RESULT) { + if (result_data.IsStreaming()) { // Can't know beforehand how many chunks are returned. return 0; } - auto &materialized = reinterpret_cast(*result_data.result); - return materialized.Collection().ChunkCount(); + return result_data.Retained().Collection().ChunkCount(); } duckdb_data_chunk duckdb_result_get_chunk(duckdb_result result, idx_t chunk_idx) { @@ -578,13 +622,12 @@ duckdb_data_chunk duckdb_result_get_chunk(duckdb_result result, idx_t chunk_idx) if (result_data.result_set_type == duckdb::CAPIResultSetType::CAPI_RESULT_TYPE_DEPRECATED) { return nullptr; } - if (result_data.result->GetResultType() != duckdb::QueryResultType::MATERIALIZED_RESULT) { - // This API is only supported for materialized query results + if (result_data.IsStreaming()) { + // This API is only supported for retained query results return nullptr; } result_data.result_set_type = duckdb::CAPIResultSetType::CAPI_RESULT_TYPE_MATERIALIZED; - auto &materialized = reinterpret_cast(*result_data.result); - auto &collection = materialized.Collection(); + auto &collection = result_data.Retained().Collection(); if (chunk_idx >= collection.ChunkCount()) { return nullptr; } @@ -602,7 +645,7 @@ bool duckdb_result_is_streaming(duckdb_result result) { return false; } auto &result_data = *(reinterpret_cast(result.internal_data)); - return result_data.result->GetResultType() == duckdb::QueryResultType::STREAM_RESULT; + return result_data.IsStreaming(); } duckdb_result_type duckdb_result_return_type(duckdb_result result) { @@ -610,7 +653,7 @@ duckdb_result_type duckdb_result_return_type(duckdb_result result) { return DUCKDB_RESULT_TYPE_INVALID; } auto &result_data = *(reinterpret_cast(result.internal_data)); - switch (result_data.result->GetStatementProperties().return_type) { + switch (result_data.GetStatementProperties().return_type) { case duckdb::StatementReturnType::CHANGED_ROWS: return DUCKDB_RESULT_TYPE_CHANGED_ROWS; case duckdb::StatementReturnType::NOTHING: @@ -628,5 +671,5 @@ duckdb_statement_type duckdb_result_statement_type(duckdb_result result) { } auto &pres = *(reinterpret_cast(result.internal_data)); - return StatementTypeToC(pres.result->GetStatementType()); + return StatementTypeToC(pres.GetStatementType()); } diff --git a/src/duckdb/src/main/capi/v1/scalar_function-c.cpp b/src/duckdb/src/main/capi/v1/scalar_function-c.cpp index d272d9346..5140b8806 100644 --- a/src/duckdb/src/main/capi/v1/scalar_function-c.cpp +++ b/src/duckdb/src/main/capi/v1/scalar_function-c.cpp @@ -1,4 +1,5 @@ #include "duckdb/catalog/catalog.hpp" +#include "duckdb/main/capi/capi_function_signature.hpp" #include "duckdb/common/type_visitor.hpp" #include "duckdb/common/types.hpp" #include "duckdb/function/function.hpp" @@ -251,7 +252,7 @@ void duckdb_scalar_function_set_varargs(duckdb_scalar_function function, duckdb_ } auto &scalar_function = GetCScalarFunction(function); auto logical_type = reinterpret_cast(type); - scalar_function.SetVarArgs(*logical_type); + duckdb::CAPIFunctionSignature::SetVarArgs(scalar_function.GetSignature(), *logical_type); } void duckdb_scalar_function_set_special_handling(duckdb_scalar_function function) { @@ -276,7 +277,9 @@ void duckdb_scalar_function_add_parameter(duckdb_scalar_function function, duckd } auto &scalar_function = GetCScalarFunction(function); auto logical_type = reinterpret_cast(type); - scalar_function.GetSignature().AddParameter(*logical_type); + // v1 declares no parameter name, so the caller cannot mean one: the parameter is positional-only, and the + // synthetic "colN" it is given stays invisible. It goes ahead of any varargs set beforehand. + duckdb::CAPIFunctionSignature::AddPositionalOnly(scalar_function.GetSignature(), *logical_type); } void duckdb_scalar_function_set_return_type(duckdb_scalar_function function, duckdb_logical_type type) { diff --git a/src/duckdb/src/main/capi/v1/stream-c.cpp b/src/duckdb/src/main/capi/v1/stream-c.cpp index 5f3d0e012..f5a39f5f4 100644 --- a/src/duckdb/src/main/capi/v1/stream-c.cpp +++ b/src/duckdb/src/main/capi/v1/stream-c.cpp @@ -6,11 +6,8 @@ duckdb_data_chunk duckdb_stream_fetch_chunk(duckdb_result result) { if (!result.internal_data) { return nullptr; } - auto &result_data = *((duckdb::DuckDBResultData *)result.internal_data); - if (result_data.result->GetResultType() != duckdb::QueryResultType::STREAM_RESULT) { - // We can only fetch from a StreamQueryResult - return nullptr; - } + // A streaming entry point hands out a retained result for a statement it does not stream, and its rows are read + // here too, since this is the reader its caller was told to use return duckdb_fetch_chunk(result); } @@ -23,15 +20,17 @@ duckdb_data_chunk duckdb_fetch_chunk(duckdb_result result) { return nullptr; } result_data.result_set_type = duckdb::CAPIResultSetType::CAPI_RESULT_TYPE_STREAMING; - auto &result_instance = (duckdb::QueryResult &)*result_data.result; - // FetchRaw ? Do we care about flattening them? try { - auto chunk = result_instance.Fetch(); + auto chunk = result_data.Fetch(); return reinterpret_cast(chunk.release()); } catch (std::exception &e) { // Set the error on the result so duckdb_result_error can retrieve it duckdb::ErrorData error(e); - result_instance.SetError(error); + if (result_data.stream) { + result_data.stream->SetError(std::move(error)); + } else { + result_data.result->SetError(std::move(error)); + } return nullptr; } } diff --git a/src/duckdb/src/main/capi/v1/table_function-c.cpp b/src/duckdb/src/main/capi/v1/table_function-c.cpp index 7fd048c33..88b2a5c84 100644 --- a/src/duckdb/src/main/capi/v1/table_function-c.cpp +++ b/src/duckdb/src/main/capi/v1/table_function-c.cpp @@ -1,4 +1,5 @@ #include "duckdb/catalog/catalog.hpp" +#include "duckdb/main/capi/capi_function_signature.hpp" #include "duckdb/common/type_visitor.hpp" #include "duckdb/common/types.hpp" #include "duckdb/function/table_function.hpp" @@ -9,6 +10,7 @@ #include "duckdb/storage/statistics/node_statistics.hpp" namespace duckdb { + namespace { //===--------------------------------------------------------------------===// // Structures @@ -111,6 +113,7 @@ unique_ptr CTableFunctionBind(ClientContext &context, TableFunctio D_ASSERT(info.bind && info.function && info.init); auto result = make_uniq(info); + // the cast values are kept, so that a plan that repeats this bind needs no literal types CTableInternalBindInfo bind_info(context, input.inputs, input.named_parameters, return_types, names, *result, info); info.bind(ToCTableFunctionBindInfo(bind_info)); if (!bind_info.success) { @@ -205,7 +208,9 @@ void duckdb_table_function_add_parameter(duckdb_table_function function, duckdb_ } auto &tf = GetCTableFunction(function); auto logical_type = reinterpret_cast(type); - tf.GetArguments().push_back(*logical_type); + // v1 declares no parameter name, so the parameter is positional-only - see duckdb_scalar_function_add_parameter. + // duckdb_table_function_add_named_parameter is the one that declares a name a caller can use + duckdb::CAPIFunctionSignature::AddPositionalOnly(tf.GetSignature(), *logical_type); } void duckdb_table_function_add_named_parameter(duckdb_table_function function, const char *name, @@ -215,7 +220,19 @@ void duckdb_table_function_add_named_parameter(duckdb_table_function function, c } auto &tf = GetCTableFunction(function); auto logical_type = reinterpret_cast(type); - tf.named_parameters.insert({name, *logical_type}); + // an option the call leaves out does not reach the bind, where duckdb_bind_get_named_parameter reports it absent + auto add_option = [&](duckdb::TypedKwargs &options) { + auto option_name = duckdb::Identifier(name); + if (!options.Find(option_name)) { + options.Add(std::move(option_name), *logical_type); + } + }; + auto &signature = tf.GetSignature(); + if (signature.GetTypedKwargs()) { + signature.ExtendTypedKwargs(add_option); + } else { + signature.WithTypedKwargs("options", add_option); + } } void duckdb_table_function_set_extra_info(duckdb_table_function function, void *extra_info, @@ -284,14 +301,20 @@ duckdb_state duckdb_register_table_function(duckdb_connection connection, duckdb if (tf.name.empty() || !info.bind || !info.init || !info.function) { return DuckDBError; } - for (auto it = tf.named_parameters.begin(); it != tf.named_parameters.end(); it++) { - if (duckdb::TypeVisitor::Contains(it->second, duckdb::LogicalTypeId::INVALID)) { + for (auto ¶m : tf.GetSignature().GetParameters()) { + // a variadic parameter carries no type of its own + if (param.IsVariadic()) { + continue; + } + if (duckdb::TypeVisitor::Contains(param.GetType(), duckdb::LogicalTypeId::INVALID)) { return DuckDBError; } } - for (const auto &argument : tf.GetArguments()) { - if (duckdb::TypeVisitor::Contains(argument, duckdb::LogicalTypeId::INVALID)) { - return DuckDBError; + if (auto option_schema = tf.GetSignature().GetTypedKwargs()) { + for (auto &option : option_schema->GetOptions()) { + if (duckdb::TypeVisitor::Contains(option.type, duckdb::LogicalTypeId::INVALID)) { + return DuckDBError; + } } } diff --git a/src/duckdb/src/main/capi/v1/value-c.cpp b/src/duckdb/src/main/capi/v1/value-c.cpp index ec2cb5410..27aebfb67 100644 --- a/src/duckdb/src/main/capi/v1/value-c.cpp +++ b/src/duckdb/src/main/capi/v1/value-c.cpp @@ -49,8 +49,7 @@ static bool ResultIsDecimal(duckdb_result *result, idx_t col) { return false; } auto result_data = (duckdb::DuckDBResultData *)result->internal_data; - auto &query_result = result_data->result; - auto &source_type = query_result->GetTypes()[col]; + auto &source_type = result_data->GetTypes()[col]; return source_type.id() == duckdb::LogicalTypeId::DECIMAL; } diff --git a/src/duckdb/src/main/capi/v2/capi_v2.cpp b/src/duckdb/src/main/capi/v2/capi_v2.cpp index 520e6dec7..eeee4e7f4 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2.cpp @@ -248,13 +248,18 @@ auto TryGetExceptionTypeFromErrorCode(DUCKDB_V2_ERROR code) -> optional void { +auto RenderCaughtError(DUCKDB_V2_ERROR &code, string &text, optional &raw_message) noexcept -> void { // Set the fallback code first (non-throwing), then render the detail. code = DUCKDB_V2_ERROR_API; try { // The bare throw re-raises the exception currently being handled so the catch clauses can dispatch on its type. try { throw; + } catch (const MetadataDeferredException &ex) { + ErrorData error_data(ex); + code = DUCKDB_V2_ERROR_INPUT_METADATA_DEFERRED; + text = error_data.Message(); + raw_message = error_data.RawMessage(); } catch (const duckdb::Exception &ex) { ErrorData error_data(ex); code = GetErrorCodeFromExceptionType(error_data.Type()); @@ -272,11 +277,11 @@ auto RenderCaughtError(DUCKDB_V2_ERROR &code, string &text, string &raw_message) // Rendering the detail exhausted memory: that supersedes the original report. code = DUCKDB_V2_ERROR_RESOURCE_OUT_OF_MEMORY; text.clear(); - raw_message.clear(); + raw_message.reset(); } catch (...) { // Rendering the detail failed: keep the code produced so far with no detail. text.clear(); - raw_message.clear(); + raw_message.reset(); } } @@ -302,7 +307,7 @@ auto NullArgumentError(duckdb_v2_error_info_handle *err, const char *function, c auto &out = *Convert(*err); out.code = code; out.message.clear(); - out.raw_message.clear(); + out.raw_message.reset(); try { // Render through ErrorData so message/raw_message match what WithErrorHandler // produces for a thrown InvalidInputException. @@ -328,18 +333,34 @@ auto NullArgumentError(duckdb_v2_error_info_handle *err, const char *function, c // Map DuckDB's SettingScopeTarget to the V2 enum. // Legacy options (declared via DUCKDB_GLOBAL / DUCKDB_LOCAL / DUCKDB_GLOBAL_LOCAL) carry SettingScopeTarget::INVALID; // we surface that as UNKNOWN so V2 callers can distinguish "unconstrained legacy" from a declared scope. -static DUCKDB_V2_OPTION_TARGET_SCOPE MapScopeTarget(SettingScopeTarget s) { - switch (s) { +// The scopes a core option may be written at, and where an AUTOMATIC write goes; mirrors PhysicalSet::GetSettingScope. +static void PopulateOptionScopes(const unique_ptr &out, const ConfigurationOption &option) { + if (option.set_local || option.set_global) { + // legacy option: the setters it has decide + out->supports_session = option.set_local != nullptr; + out->supports_global = option.set_global != nullptr; + out->default_scope = option.set_local ? DUCKDB_V2_SETTING_SCOPE_SESSION : DUCKDB_V2_SETTING_SCOPE_GLOBAL; + return; + } + switch (option.scope) { case SettingScopeTarget::GLOBAL_ONLY: - return DUCKDB_V2_OPTION_TARGET_SCOPE_GLOBAL_ONLY; + out->supports_global = true; + out->default_scope = DUCKDB_V2_SETTING_SCOPE_GLOBAL; + break; case SettingScopeTarget::LOCAL_ONLY: - return DUCKDB_V2_OPTION_TARGET_SCOPE_LOCAL_ONLY; + out->supports_session = true; + out->default_scope = DUCKDB_V2_SETTING_SCOPE_SESSION; + break; case SettingScopeTarget::GLOBAL_DEFAULT: - return DUCKDB_V2_OPTION_TARGET_SCOPE_GLOBAL_DEFAULT; + out->supports_global = out->supports_session = true; + out->default_scope = DUCKDB_V2_SETTING_SCOPE_GLOBAL; + break; case SettingScopeTarget::LOCAL_DEFAULT: - return DUCKDB_V2_OPTION_TARGET_SCOPE_LOCAL_DEFAULT; + out->supports_global = out->supports_session = true; + out->default_scope = DUCKDB_V2_SETTING_SCOPE_SESSION; + break; default: - return DUCKDB_V2_OPTION_TARGET_SCOPE_UNKNOWN; + break; } } @@ -358,59 +379,72 @@ static void PopulateOptionAliases(const unique_ptr &out, const Identi } } -// Read the effective setting for `name` through `client`'s setting cascade. -// For a databases internal connection (no LOCAL overrides) this returns GLOBAL -> static default; -// For a client connection it returns LOCAL -> GLOBAL -> static default. -// Falls back to `fallback_default` if the cascade returned NULL. -static std::string ReadEffectiveSetting(ClientContext &client, const Identifier &name, - const std::string &fallback_default) { - if (Value result; client.TryGetCurrentSetting(name, result) && !result.IsNull()) { - return result.ToString(); +DUCKDB_V2_SETTING_SCOPE CV2OptionSource::ReadValue(std::string_view name, Value &result) const { + Identifier name_id(name); + auto option = DBConfig::GetOptionByName(name_id); + ExtensionOption extension_option; + if (!option && !config.TryGetExtensionOption(name_id, extension_option)) { + throw InvalidInputException("unknown configuration option: %s", name_id.GetIdentifierName()); } - return fallback_default; + auto canonical_name = option ? Identifier(option->name) : name_id; + auto lookup = context ? context->TryGetCurrentSetting(canonical_name, result) + : config.TryGetCurrentSetting(canonical_name, result); + if (!lookup) { + // neither set nor defaulted + result = Value(); + return DUCKDB_V2_SETTING_SCOPE_GLOBAL; + } + return lookup.GetScope() == SettingScope::LOCAL ? DUCKDB_V2_SETTING_SCOPE_SESSION : DUCKDB_V2_SETTING_SCOPE_GLOBAL; } -static unique_ptr PopulateOptionFromCore(const ConfigurationOption &option, ClientContext &client) { +static unique_ptr PopulateOptionFromCore(const ConfigurationOption &option, const CV2OptionSource &source) { auto out = make_uniq(); out->name = option.name ? option.name : ""; out->description = option.description ? option.description : ""; - out->target_scope = MapScopeTarget(option.scope); - out->default_setting = option.default_value ? option.default_value : ""; + PopulateOptionScopes(out, option); + if (!DBConfig::TryGetDefaultValue(&option, out->default_value)) { + out->default_value = Value(); + } out->aliases.clear(); PopulateOptionAliases(out, out->name); - out->setting = ReadEffectiveSetting(client, out->name, out->default_setting); return out; } -// Populate `out` from an extension option. Extension options carry no -// SettingScopeTarget (the V2 enum reports UNKNOWN) and no aliases. +// Populate `out` from an extension option. Extension options carry no aliases. static unique_ptr PopulateOptionFromExtension(const Identifier &name, const ExtensionOption &ext_option, - ClientContext &client) { + const CV2OptionSource &source) { auto out = make_uniq(); out->name = name; out->description = ext_option.description; - out->target_scope = DUCKDB_V2_OPTION_TARGET_SCOPE_UNKNOWN; - out->default_setting = ext_option.default_value.IsNull() ? std::string() : ext_option.default_value.ToString(); + // AUTOMATIC writes the session unless the option declares GLOBAL + out->supports_global = ext_option.supports_global; + out->supports_session = ext_option.supports_session; + out->default_scope = + ext_option.default_scope == SetScope::GLOBAL ? DUCKDB_V2_SETTING_SCOPE_GLOBAL : DUCKDB_V2_SETTING_SCOPE_SESSION; + out->default_value = ext_option.default_value; out->aliases.clear(); - out->setting = ReadEffectiveSetting(client, name, out->default_setting); return out; } -unique_ptr CV2Option::FromIndex(ClientContext &context, DBConfig &config, idx_t index) { +idx_t CV2Option::Count(const CV2OptionSource &source) { + return DBConfig::GetOptionCount() + source.GetConfig().GetExtensionSettings().size(); +} + +unique_ptr CV2Option::FromIndex(const CV2OptionSource &source, idx_t index) { const auto core_count = DBConfig::GetOptionCount(); if (index < core_count) { auto option = DBConfig::GetOptionByIndex(index); if (!option) { throw InvalidInputException("core option not found at given index"); } - return PopulateOptionFromCore(*option, context); + return PopulateOptionFromCore(*option, source); } const idx_t ext_rel = index - core_count; - auto ext_settings = config.GetExtensionSettings(); + auto ext_settings = source.GetConfig().GetExtensionSettings(); if (ext_rel >= ext_settings.size()) { throw InvalidInputException("option index out of range"); } @@ -418,20 +452,20 @@ unique_ptr CV2Option::FromIndex(ClientContext &context, DBConfig &con for (const auto &[name, option] : ext_settings) { if (i == ext_rel) { - return PopulateOptionFromExtension(name, option, context); + return PopulateOptionFromExtension(name, option, source); } ++i; } throw InvalidInputException("option index out of range"); } -unique_ptr CV2Option::FromName(ClientContext &context, DBConfig &config, std::string_view name) { +unique_ptr CV2Option::FromName(const CV2OptionSource &source, std::string_view name) { Identifier name_id(name); if (auto option = DBConfig::GetOptionByName(name_id)) { - return PopulateOptionFromCore(*option, context); + return PopulateOptionFromCore(*option, source); } - if (ExtensionOption ext_option; config.TryGetExtensionOption(name_id, ext_option)) { - return PopulateOptionFromExtension(name_id, ext_option, context); + if (ExtensionOption ext_option; source.GetConfig().TryGetExtensionOption(name_id, ext_option)) { + return PopulateOptionFromExtension(name_id, ext_option, source); } throw InvalidInputException("unknown configuration option: %s", name_id.GetIdentifierName()); } diff --git a/src/duckdb/src/main/capi/v2/capi_v2_arrow.cpp b/src/duckdb/src/main/capi/v2/capi_v2_arrow.cpp index e677598b1..ba944aa9d 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_arrow.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_arrow.cpp @@ -2,24 +2,15 @@ #include "duckdb/common/arrow/arrow_appender.hpp" #include "duckdb/common/arrow/arrow_converter.hpp" -#include "duckdb/common/arrow/arrow_util.hpp" +#include "duckdb/common/arrow/arrow_format.hpp" #include "duckdb/common/arrow/arrow_wrapper.hpp" -#include "duckdb/common/arrow/nanoarrow/nanoarrow.hpp" #include "duckdb/function/table/arrow.hpp" #include "duckdb/function/table/arrow/arrow_duck_schema.hpp" -#include "duckdb/main/chunk_scan_state.hpp" #include #include namespace duckdb::capiv2 { -namespace { - -//! What batch_size 0 selects for a result stream: 64 vectors in a default build, so one Arrow array gathers a clean 64 -//! engine chunks. -constexpr idx_t CV2_DEFAULT_ARROW_BATCH_SIZE = 131072; - -} // namespace //---------------------------------------------------------------------------------------------------------------------- // arrow_importer @@ -138,173 +129,39 @@ auto Convert(CV2ArrowExporter *exporter) -> duckdb_v2_arrow_exporter_handle { namespace { //---------------------------------------------------------------------------------------------------------------------- -// result_to_arrow_stream +// Import conversion //---------------------------------------------------------------------------------------------------------------------- -//! Drives a V2 result through the engine's chunk-cursor interface, so ArrowUtil::TryFetchChunk (offset tracking plus -//! appender coalescing) can pull from it. End of stream is signalled the way QueryResultChunkScanState signals it: a -//! null current chunk. -class CV2ArrowScanState : public ChunkScanState { -public: - explicit CV2ArrowScanState(ResultWrapperV2 &wrapper) : wrapper(wrapper) { - } - - bool LoadNextChunk(ErrorData &error) override { - if (finished) { - current_chunk = nullptr; - return true; - } - try { - current_chunk = wrapper.FetchChunkBlocking(); - } catch (std::exception &ex) { - scan_error = ErrorData(ex); - has_scan_error = true; - finished = true; - current_chunk = nullptr; - error = scan_error; - return false; - } - offset = 0; - if (!current_chunk) { - finished = true; - } - return true; - } - bool HasError() const override { - return has_scan_error; - } - ErrorData &GetError() override { - return scan_error; - } - const vector &Types() const override { - return wrapper.types; - } - const vector &Names() const override { - return wrapper.names; +//! The record-batch struct may carry a validity bitmap of its own: a clear bit marks the whole row null, so +//! it has to apply to every column alike. Returns null when the array carries no bitmap, or no row is null. +auto CV2TopLevelValidity(const ArrowArray &array, idx_t from, idx_t rows) -> unique_ptr { + if (array.null_count == 0 || array.n_buffers == 0 || !array.buffers || !array.buffers[0]) { + return nullptr; } - -private: - ResultWrapperV2 &wrapper; - ErrorData scan_error; - bool has_scan_error = false; -}; - -//! The stream's private_data. Owns the result state machine -- and through it the query's transaction and the -//! connection's live-result slot -- the cursor driving it, and the schema cached while the producing transaction was -//! still live. -struct CV2ArrowStream { - unique_ptr wrapper; - unique_ptr scan_state; - ArrowSchema cached_schema {}; - unordered_map> extension_types; - ClientProperties client_properties; - idx_t batch_size = CV2_DEFAULT_ARROW_BATCH_SIZE; - ErrorData last_error; - - ~CV2ArrowStream() { - if (cached_schema.release) { - cached_schema.release(&cached_schema); + auto bits = static_cast(array.buffers[0]); + auto mask = make_uniq(rows); + for (idx_t row = 0; row < rows; row++) { + auto index = NumericCast(array.offset) + from + row; + if (!(bits[index / 8] & (1 << (index % 8)))) { + mask->SetInvalid(row); } - // The scan state holds a reference into *wrapper, so drop it first. - scan_state.reset(); - } -}; - -//! An Arrow callback must not let an exception cross the C ABI, so each one is wrapped whole and reports through the -//! errno-style return code the interface specifies. -int CV2ArrowStreamGetSchema(ArrowArrayStream *stream, ArrowSchema *out) { - if (!stream->release || !stream->private_data) { - return EINVAL; } - auto &self = *static_cast(stream->private_data); - try { - if (!self.cached_schema.release) { - self.last_error = ErrorData("arrow stream: the schema is unavailable"); - return EINVAL; - } - // The consumer owns what get_schema returns and releases it independently of the stream, so hand out a deep - // copy. Copying the cached schema is pure: it never re-reads the catalog, which is the point of having cached - // it. - if (duckdb_nanoarrow::ArrowSchemaDeepCopy(&self.cached_schema, out) != NANOARROW_OK) { - self.last_error = ErrorData("arrow stream: failed to copy the schema"); - return ENOMEM; - } - return 0; - } catch (std::exception &ex) { - // Recording the message is itself best-effort under memory pressure; the return code is what the consumer must - // rely on. - try { - self.last_error = ErrorData(ex); - } catch (...) { // NOLINT: best-effort - } - return EIO; - } catch (...) { - return EIO; + if (!mask->CanHaveNull()) { + return nullptr; } + return mask; } -int CV2ArrowStreamGetNext(ArrowArrayStream *stream, ArrowArray *out) { - if (!stream->release || !stream->private_data) { - return EINVAL; - } - auto &self = *static_cast(stream->private_data); - out->release = nullptr; - try { - idx_t result_count = 0; - ErrorData error; - if (!ArrowUtil::TryFetchChunk(*self.scan_state, self.client_properties, self.batch_size, out, result_count, - error, self.extension_types)) { - self.last_error = error; - return EIO; - } - if (result_count == 0) { - // End of stream, which the interface spells as a released array. - out->release = nullptr; +//! Marks the rows the record batch calls null as null in this vector too. +void CV2ApplyTopLevelMask(Vector &vector, const ValidityMask &top_level_mask, idx_t rows) { + auto &mask = FlatVector::ValidityMutable(vector); + for (idx_t row = 0; row < rows; row++) { + if (!top_level_mask.RowIsValid(row)) { + mask.SetInvalid(row); } - } catch (std::exception &ex) { - self.last_error = ErrorData(ex); - return EIO; - } catch (...) { - return EIO; } - return 0; } -const char *CV2ArrowStreamGetLastError(ArrowArrayStream *stream) { - if (!stream->release || !stream->private_data) { - return "arrow stream was released"; - } - auto &self = *static_cast(stream->private_data); - return self.last_error.Message().c_str(); -} - -void CV2ArrowStreamRelease(ArrowArrayStream *stream) { - if (!stream || !stream->release) { - return; - } - stream->release = nullptr; - auto self = static_cast(stream->private_data); - stream->private_data = nullptr; - if (!self) { - return; - } - // Mirror result_destroy: close the engine result and roll back any transaction the bridge injected, before freeing. - // A release callback must not throw across the C ABI. - self->scan_state.reset(); - try { - if (self->wrapper) { - self->wrapper->Finalize(); - } - } catch (...) { // NOLINT: best-effort cleanup - } - // Frees the cached schema, and the wrapper, whose destructor releases the busy slot. - delete self; -} - -//---------------------------------------------------------------------------------------------------------------------- -// Import conversion -//---------------------------------------------------------------------------------------------------------------------- - //! Converts `rows` rows starting at `from` of `array` into a fresh chunk, through the resolved per-column Arrow types. //! `owner` is the shared owner the chunk's zero-copy vectors keep alive; it is null when the caller kept the array, in //! which case the result is materialized instead. @@ -316,29 +173,37 @@ auto CV2ConvertArrowSlice(ClientContext &context, CV2ArrowImporter &importer, Ar auto chunk = make_uniq(); chunk->Initialize(Allocator::DefaultAllocator(), types, MaxValue(rows, 1)); chunk->SetChildCardinality(rows); + auto top_level_mask = CV2TopLevelValidity(array, from, rows); for (idx_t i = 0; i < chunk->ColumnCount(); i++) { auto *child_array = array.children[i]; auto arrow_type = arrow_types.at(i); // A fresh scan state per slice, so nothing cached for one array leaks into the next. The cost is re-decoding a // dictionary per chunk. auto array_state = make_uniq(context); - array_state->owned_data = owner; + // The dictionary conversion requires a non-null owned_data to attach to its vectors. When + // there is no owner the chunk is materialized below, so an inert wrapper is enough. + array_state->owned_data = owner ? owner : duckdb::make_shared_ptr(); switch (arrow_type->GetPhysicalType()) { case ArrowArrayPhysicalType::DICTIONARY_ENCODED: if (!child_array->dictionary) { throw InvalidInputException("Dictionary-encoded Arrow array has no dictionary"); } + // the dictionary conversion folds the mask into its selection vector itself ArrowToDuckDBConversion::ColumnArrowToDuckDBDictionary(chunk->data[i], *child_array, from, *array_state, - rows, *arrow_type); + rows, *arrow_type, -1, top_level_mask.get()); break; case ArrowArrayPhysicalType::RUN_END_ENCODED: ArrowToDuckDBConversion::ColumnArrowToDuckDBRunEndEncoded(chunk->data[i], *child_array, from, *array_state, - rows, *arrow_type); + rows, *arrow_type, -1, top_level_mask.get()); break; case ArrowArrayPhysicalType::DEFAULT: ArrowToDuckDBConversion::SetValidityMask(chunk->data[i], *child_array, from, rows, array.offset, -1); + // Merging before the conversion lets a struct or union column propagate the nulls to its children. + if (top_level_mask) { + CV2ApplyTopLevelMask(chunk->data[i], *top_level_mask, rows); + } ArrowToDuckDBConversion::ColumnArrowToDuckDB(chunk->data[i], *child_array, from, *array_state, rows, - *arrow_type); + *arrow_type, -1, top_level_mask.get()); break; default: throw NotImplementedException("Only default Arrow physical types are currently supported"); @@ -346,6 +211,10 @@ auto CV2ConvertArrowSlice(ClientContext &context, CV2ArrowImporter &importer, Ar // Re-assert the size after the conversion, mirroring the engine's own scan loop: a dictionary or run-end // conversion replaces the vector rather than filling it. FlatVector::SetSize(chunk->data[i], count_t(rows)); + // A conversion may rebuild the validity mask from its own buffers, so the top-level nulls go in again. + if (top_level_mask && chunk->data[i].GetVectorType() == VectorType::FLAT_VECTOR) { + CV2ApplyTopLevelMask(chunk->data[i], *top_level_mask, rows); + } } chunk->CheckCardinality(rows); if (owner) { @@ -378,82 +247,114 @@ void CV2ValidateArrowArray(CV2ArrowImporter &importer, ArrowArray &array) { throw InvalidInputException("Arrow array child length does not match the array length"); } } + // A positive null count is only meaningful with the validity bitmap to read it from. + if (array.null_count > 0 && (array.n_buffers == 0 || !array.buffers || !array.buffers[0])) { + throw InvalidInputException("Arrow array has a non-zero null count but no validity bitmap"); + } } -} // namespace -} // namespace duckdb::capiv2 - //---------------------------------------------------------------------------------------------------------------------- -// Public API +// arrow_result //---------------------------------------------------------------------------------------------------------------------- -using namespace duckdb::capiv2; +//! 64 vectors in a default build. +constexpr idx_t DEFAULT_ARROW_RESULT_BATCH_SIZE = 131072; -DUCKDB_V2_ERROR duckdb_v2_result_to_arrow_stream(duckdb_v2_result_handle *result, idx_t batch_size, - struct ArrowArrayStream *out_stream, - duckdb_v2_error_info_handle *err) { - // Validate before taking ownership, so a rejection leaves the caller's result intact. - DUCKDB_CHECK_ARG(result); - DUCKDB_CHECK_ARG(*result); - DUCKDB_CHECK_ARG(out_stream); - return WithErrorHandler(err, [&]() { - // Adopt by transfer; consumed on success and failure alike. - auto wrapper = duckdb::unique_ptr(Convert(*result)); - *result = nullptr; +//! The wrapper behind an Arrow result, as the chunk functions that do not depend on the format take it. +auto AsResult(duckdb_v2_arrow_result_handle result) -> duckdb_v2_result_handle { + return Convert(Convert(result)); +} + +void FetchArrowArray(ResultWrapperV2 &wrapper, ArrowArray &out) { + out.release = nullptr; + auto array = wrapper.FetchBlocking(); + if (array) { + array->MoveTo(out); + } +} + +void CopyResultSchema(ResultWrapperV2 &wrapper, ArrowSchema &out) { + out.release = nullptr; + wrapper.RequireMetadata(); + CopyArrowSchema(wrapper.arrow_schema.arrow_schema, out); +} + +//! The private_data of an ArrowArrayStream made from an Arrow result. +struct CV2ArrowResultStream { + unique_ptr wrapper; + //! What get_last_error returns, kept until the next call. + string last_error; +}; + +//! No exception may cross the C ABI, so a failure becomes an errno code and the message waits for get_last_error. +template +int CallArrowResultStream(ArrowArrayStream *stream, FUNC callback) { + if (!stream || !stream->release || !stream->private_data) { + return EINVAL; + } + auto &self = *static_cast(stream->private_data); + try { + callback(*self.wrapper); + self.last_error.clear(); + return 0; + } catch (std::exception &ex) { try { - // The schema must be built while the query's transaction is live, so advance to the principal fragment if - // its metadata is not available yet. No rows can be produced before that fragment is prepared, so stepping - // here never drops data. - while (!wrapper->metadata_available) { - duckdb::unique_ptr discard; - auto status = wrapper->Step(discard); - if (status == DUCKDB_V2_RESULT_STEP_STATUS_WAITING) { - wrapper->Wait(); - continue; - } - if (status == DUCKDB_V2_RESULT_STEP_STATUS_CHUNK) { - throw duckdb::InternalException( - "arrow stream: a row was produced before result metadata was available"); - } - break; // FINISHED / CANCELLED: no row-producing fragment. - } - if (!wrapper->context) { - throw duckdb::InvalidInputException("result is not associated with an active context"); - } - auto &context = *wrapper->context; - - auto self = duckdb::make_uniq(); - self->batch_size = batch_size == 0 ? CV2_DEFAULT_ARROW_BATCH_SIZE : batch_size; - self->client_properties = context.GetClientProperties(); - // Cache the schema and the extension type map now, under the live transaction: populate-schema callbacks - // and ENUM dictionaries read the catalog, which get_schema cannot do once the transaction is gone. - self->extension_types = duckdb::ArrowTypeExtensionData::GetExtensionTypes(context, wrapper->types); - duckdb::ArrowConverter::ToArrowSchema(&self->cached_schema, wrapper->types, - duckdb::IdentifiersToStrings(wrapper->names), - self->client_properties); - self->scan_state = duckdb::make_uniq(*wrapper); - self->wrapper = std::move(wrapper); - - out_stream->get_schema = CV2ArrowStreamGetSchema; - out_stream->get_next = CV2ArrowStreamGetNext; - out_stream->get_last_error = CV2ArrowStreamGetLastError; - out_stream->release = CV2ArrowStreamRelease; - out_stream->private_data = self.release(); - } catch (...) { - // A throw before ownership moved into the stream leaves the result with us. Finalize it so a failed export - // cleans up exactly as result_destroy would, rather than leaving the query open until the local pointer - // goes out of scope. - if (wrapper) { - try { - wrapper->Finalize(); - } catch (...) { // NOLINT: never mask the original error - } - } - throw; + self.last_error = ErrorData(ex).Message(); + } catch (...) { // NOLINT: the code is what the consumer relies on + self.last_error.clear(); } + } catch (...) { // NOLINT: the code is what the consumer relies on + self.last_error.clear(); + } + return EIO; +} + +int ArrowResultStreamGetSchema(ArrowArrayStream *stream, ArrowSchema *out) { + return CallArrowResultStream(stream, [&](ResultWrapperV2 &wrapper) { + wrapper.AdvanceToMetadata(); + CopyResultSchema(wrapper, *out); }); } +int ArrowResultStreamGetNext(ArrowArrayStream *stream, ArrowArray *out) { + return CallArrowResultStream(stream, [&](ResultWrapperV2 &wrapper) { FetchArrowArray(wrapper, *out); }); +} + +const char *ArrowResultStreamGetLastError(ArrowArrayStream *stream) { + if (!stream || !stream->release || !stream->private_data) { + return nullptr; + } + auto &self = *static_cast(stream->private_data); + return self.last_error.empty() ? nullptr : self.last_error.c_str(); +} + +void ArrowResultStreamRelease(ArrowArrayStream *stream) { + if (!stream || !stream->release) { + return; + } + unique_ptr self(static_cast(stream->private_data)); + stream->release = nullptr; + stream->private_data = nullptr; + try { + self->wrapper->Finalize(); + } catch (...) { // NOLINT: release has no error channel + } +} + +} // namespace + +auto ArrowResultFormat(idx_t batch_size) -> shared_ptr { + return make_shared_ptr(batch_size == 0 ? DEFAULT_ARROW_RESULT_BATCH_SIZE : batch_size); +} + +} // namespace duckdb::capiv2 + +//---------------------------------------------------------------------------------------------------------------------- +// Public API +//---------------------------------------------------------------------------------------------------------------------- + +using namespace duckdb::capiv2; + //---------------------------------------------------------------------------------------------------------------------- // arrow_importer //---------------------------------------------------------------------------------------------------------------------- @@ -467,9 +368,9 @@ DUCKDB_V2_ERROR duckdb_v2_arrow_importer_create(duckdb_v2_context_handle context *out_importer = nullptr; return WithErrorHandler(err, [&]() { auto importer = duckdb::make_uniq(); - importer->context = Convert(context); + importer->context = &Convert(context)->context; importer->batch_size = batch_size; - duckdb::ArrowTableFunction::PopulateArrowTableSchema(*Convert(context), importer->table, *schema); + duckdb::ArrowTableFunction::PopulateArrowTableSchema(Convert(context)->context, importer->table, *schema); *out_importer = Convert(importer.release()); }); } @@ -608,7 +509,7 @@ DUCKDB_V2_ERROR duckdb_v2_arrow_exporter_create(duckdb_v2_context_handle context return NullArgumentError(err, __func__, "names"); } return WithErrorHandler(err, [&]() { - auto &ctx = *Convert(context); + auto &ctx = Convert(context)->context; auto exporter = duckdb::make_uniq(); exporter->batch_size = batch_size; exporter->types.reserve(count); @@ -617,11 +518,12 @@ DUCKDB_V2_ERROR duckdb_v2_arrow_exporter_create(duckdb_v2_context_handle context if (!types[i]) { throw duckdb::InvalidInputException("null logical type at index %llu", i); } - if (IsNullArgument(names[i])) { + if (IsNullArgument(&names[i])) { throw duckdb::InvalidInputException("malformed column name at index %llu", i); } - exporter->types.push_back(*Convert(types[i])); - exporter->names.emplace_back(Convert(names[i])); + auto type = Convert(types[i]); + exporter->types.push_back(*type); + exporter->names.emplace_back(ConvertIdentifierName(names[i])); } // Pin the settings and the extension map now: the schema below and every array this exporter produces are built // from them, which is what keeps the two consistent. @@ -708,3 +610,118 @@ DUCKDB_V2_ERROR duckdb_v2_arrow_exporter_destroy(duckdb_v2_arrow_exporter_handle } }); } + +//---------------------------------------------------------------------------------------------------------------------- +// arrow_result +//---------------------------------------------------------------------------------------------------------------------- + +DUCKDB_V2_ERROR duckdb_v2_statement_execute_arrow(duckdb_v2_connection_handle conn, + duckdb_v2_sql_statement_handle statement, + const duckdb_v2_identifier_t *parameter_names, + const duckdb_v2_value_handle *parameter_values, idx_t parameter_count, + idx_t batch_size, duckdb_v2_arrow_result_handle *out_result, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(out_result); + *out_result = nullptr; + DUCKDB_CHECK_ARG(conn); + DUCKDB_CHECK_ARG(statement); + if (parameter_count > 0 && !parameter_values) { + return NullArgumentError(err, __func__, "parameter_values"); + } + return WithErrorHandler(err, [&]() { + *out_result = ConvertArrowResult(ExecuteStatementV2(Convert(conn)->context, *Convert(statement), + parameter_names, parameter_values, parameter_count, + __func__, ArrowResultFormat(batch_size)) + .release()); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_arrow_result_destroy(duckdb_v2_arrow_result_handle *result) { + return WithErrorHandler(nullptr, [&]() { + if (!result || !*result) { + return; + } + duckdb::unique_ptr wrapper(Convert(*result)); + *result = nullptr; + wrapper->Finalize(); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_arrow_result_step(duckdb_v2_arrow_result_handle result, struct ArrowArray *out_array, + DUCKDB_V2_RESULT_STEP_STATUS *out_status, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(result); + DUCKDB_CHECK_ARG(out_array); + DUCKDB_CHECK_ARG(out_status); + out_array->release = nullptr; + return WithErrorHandler(err, [&]() { + duckdb::unique_ptr array; + *out_status = Convert(result)->Step(array); + if (array) { + array->MoveTo(*out_array); + } + }); +} + +DUCKDB_V2_ERROR duckdb_v2_arrow_result_fetch_array(duckdb_v2_arrow_result_handle result, struct ArrowArray *out_array, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(result); + DUCKDB_CHECK_ARG(out_array); + out_array->release = nullptr; + return WithErrorHandler(err, [&]() { FetchArrowArray(*Convert(result), *out_array); }); +} + +DUCKDB_V2_ERROR duckdb_v2_arrow_result_wait(duckdb_v2_arrow_result_handle result, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(result); + return duckdb_v2_result_wait(AsResult(result), err); +} + +DUCKDB_V2_ERROR duckdb_v2_arrow_result_drain(duckdb_v2_arrow_result_handle result, idx_t *out_rows_changed, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(result); + DUCKDB_CHECK_ARG(out_rows_changed); + return WithErrorHandler(err, [&]() { *out_rows_changed = Convert(result)->Drain(); }); +} + +DUCKDB_V2_ERROR duckdb_v2_arrow_result_get_result_type(duckdb_v2_arrow_result_handle result, + DUCKDB_V2_RESULT_TYPE *out_type, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(result); + DUCKDB_CHECK_ARG(out_type); + return duckdb_v2_result_get_result_type(AsResult(result), out_type, err); +} + +DUCKDB_V2_ERROR duckdb_v2_arrow_result_get_statement_type(duckdb_v2_arrow_result_handle result, + DUCKDB_V2_STATEMENT_TYPE *out_type, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(result); + DUCKDB_CHECK_ARG(out_type); + return duckdb_v2_result_get_statement_type(AsResult(result), out_type, err); +} + +DUCKDB_V2_ERROR duckdb_v2_arrow_result_get_schema(duckdb_v2_arrow_result_handle result, struct ArrowSchema *out_schema, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(result); + DUCKDB_CHECK_ARG(out_schema); + out_schema->release = nullptr; + return WithErrorHandler(err, [&]() { CopyResultSchema(*Convert(result), *out_schema); }); +} + +DUCKDB_V2_ERROR duckdb_v2_arrow_result_to_arrow_c_stream(duckdb_v2_arrow_result_handle *result, + struct ArrowArrayStream *out_stream, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(out_stream); + out_stream->release = nullptr; + DUCKDB_CHECK_ARG(result); + DUCKDB_CHECK_ARG(*result); + return WithErrorHandler(err, [&]() { + auto self = duckdb::make_uniq(); + self->wrapper = duckdb::unique_ptr(Convert(*result)); + *result = nullptr; + out_stream->get_schema = ArrowResultStreamGetSchema; + out_stream->get_next = ArrowResultStreamGetNext; + out_stream->get_last_error = ArrowResultStreamGetLastError; + out_stream->release = ArrowResultStreamRelease; + out_stream->private_data = self.release(); + }); +} diff --git a/src/duckdb/src/main/capi/v2/capi_v2_catalog.cpp b/src/duckdb/src/main/capi/v2/capi_v2_catalog.cpp index 056320703..badd2e281 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_catalog.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_catalog.cpp @@ -113,7 +113,7 @@ DUCKDB_V2_ERROR duckdb_v2_column_description_get_type(duckdb_v2_column_descripti DUCKDB_CHECK_ARG(type); *type = nullptr; // Borrowed: the column definition owns the type for as long as the description lives. - return WithErrorHandler(err, [&]() { *type = Convert(&Convert(column)->TypeMutable()); }); + return WithErrorHandler(err, [&]() { *type = ConvertBorrowed(Convert(column)->Type()); }); } DUCKDB_V2_ERROR duckdb_v2_column_description_has_default(duckdb_v2_column_description_handle column, bool *has_default, diff --git a/src/duckdb/src/main/capi/v2/capi_v2_column_data_collection.cpp b/src/duckdb/src/main/capi/v2/capi_v2_column_data_collection.cpp index efe6f9d52..c4c520b3f 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_column_data_collection.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_column_data_collection.cpp @@ -1,19 +1,12 @@ #include "duckdb/main/capi_v2/capi_v2_internal.hpp" +#include "duckdb/storage/buffer_manager.hpp" #include "duckdb/common/type_visitor.hpp" #include "duckdb/common/types/column/column_data_collection.hpp" namespace duckdb { namespace capiv2 { -static auto Convert(ColumnDataCollection *cdc) -> duckdb_v2_column_data_collection_handle { - return reinterpret_cast(cdc); -} - -static auto Convert(duckdb_v2_column_data_collection_handle cdc) -> ColumnDataCollection * { - return reinterpret_cast(cdc); -} - static auto Convert(ColumnDataAppendState *state) -> duckdb_v2_column_data_collection_append_state_handle { return reinterpret_cast(state); } @@ -38,7 +31,7 @@ static auto Convert(duckdb_v2_column_data_collection_worker_scan_state_handle st return reinterpret_cast(state); } -static void CreateColumnDataCollection(ClientContext &context, const duckdb_v2_logical_type_handle *types_array, +static void CreateColumnDataCollection(DatabaseInstance &db, const duckdb_v2_logical_type_handle *types_array, idx_t types_count, duckdb_v2_column_data_collection_handle *out_collection, const char *function_name) { *out_collection = nullptr; @@ -51,7 +44,8 @@ static void CreateColumnDataCollection(ClientContext &context, const duckdb_v2_l if (!types_array[i]) { throw InvalidInputException("null logical type at index %llu", i); } - const auto &type = *Convert(types_array[i]); + auto type_ref = Convert(types_array[i]); + const auto &type = *type_ref; // ANY is a signature wildcard with no physical layout; a collection // allocates storage, so reject it (mirrors data_chunk_create). if (TypeVisitor::Contains(type, LogicalTypeId::ANY)) { @@ -59,7 +53,7 @@ static void CreateColumnDataCollection(ClientContext &context, const duckdb_v2_l } types.push_back(type); } - auto collection = make_uniq(context, std::move(types)); + auto collection = make_uniq(BufferManager::GetBufferManager(db), std::move(types)); *out_collection = Convert(collection.release()); } @@ -85,27 +79,17 @@ static void VerifyChunkTypes(const ColumnDataCollection &collection, const DataC using namespace duckdb::capiv2; -DUCKDB_V2_ERROR duckdb_v2_column_data_collection_create_with_connection( - duckdb_v2_connection_handle conn, const duckdb_v2_logical_type_handle *types_array, idx_t types_count, - duckdb_v2_column_data_collection_handle *out_collection, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(types_array); - DUCKDB_CHECK_ARG(out_collection); - return WithErrorHandler(err, [&]() { - CreateColumnDataCollection(*Convert(conn)->context, types_array, types_count, out_collection, - "duckdb_v2_column_data_collection_create_with_connection"); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_column_data_collection_create_with_context( - duckdb_v2_context_handle context, const duckdb_v2_logical_type_handle *types_array, idx_t types_count, - duckdb_v2_column_data_collection_handle *out_collection, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(context); +DUCKDB_V2_ERROR duckdb_v2_column_data_collection_create(duckdb_v2_factory_handle factory, + const duckdb_v2_logical_type_handle *types_array, + idx_t types_count, + duckdb_v2_column_data_collection_handle *out_collection, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(types_array); DUCKDB_CHECK_ARG(out_collection); return WithErrorHandler(err, [&]() { - CreateColumnDataCollection(*Convert(context), types_array, types_count, out_collection, - "duckdb_v2_column_data_collection_create_with_context"); + CreateColumnDataCollection(Convert(factory)->GetDatabase(), types_array, types_count, out_collection, + "duckdb_v2_column_data_collection_create"); }); } diff --git a/src/duckdb/src/main/capi/v2/capi_v2_config.cpp b/src/duckdb/src/main/capi/v2/capi_v2_config.cpp new file mode 100644 index 000000000..3c8a6c748 --- /dev/null +++ b/src/duckdb/src/main/capi/v2/capi_v2_config.cpp @@ -0,0 +1,153 @@ +#include "duckdb/main/capi_v2/capi_v2_internal.hpp" + +namespace duckdb { +namespace capiv2 { + +//---------------------------------------------------------------------------------------------------------------------- +// Instance Config +//---------------------------------------------------------------------------------------------------------------------- + +DUCKDB_V2_SETTING_SCOPE CV2InstanceConfig::ReadValue(std::string_view name, Value &result) { + // Legacy options are read through a client context; a fresh one has no SESSION settings to shadow GLOBAL. + auto connection = Connection::CreateInternal(*instance.GetDatabase().instance); + CV2OptionSource(*connection.context).ReadValue(name, result); + return DUCKDB_V2_SETTING_SCOPE_GLOBAL; +} + +void CV2InstanceConfig::Write(const Identifier &name, const Value &value, DUCKDB_V2_SETTING_SCOPE scope) { + if (scope == DUCKDB_V2_SETTING_SCOPE_SESSION) { + throw InvalidInputException("an instance has no SESSION scope: write the option through a connection"); + } + auto connection = Connection::CreateInternal(*instance.GetDatabase().instance); + PhysicalSet::SetVariable(*connection.context, name, SetScope::GLOBAL, value); +} + +unique_ptr CV2InstanceConfig::GetOption(std::string_view name) { + return CV2Option::FromName(CV2OptionSource(*instance.GetDatabase().instance), name); +} + +unique_ptr CV2InstanceConfig::GetOptionByIndex(idx_t index) { + return CV2Option::FromIndex(CV2OptionSource(*instance.GetDatabase().instance), index); +} + +idx_t CV2InstanceConfig::GetOptionCount() { + return CV2Option::Count(CV2OptionSource(*instance.GetDatabase().instance)); +} + +//---------------------------------------------------------------------------------------------------------------------- +// Client Config +//---------------------------------------------------------------------------------------------------------------------- + +DUCKDB_V2_SETTING_SCOPE CV2ClientConfig::ReadValue(std::string_view name, Value &result) { + return CV2OptionSource(context).ReadValue(name, result); +} + +void CV2ClientConfig::Write(const Identifier &name, const Value &value, DUCKDB_V2_SETTING_SCOPE scope) { + PhysicalSet::SetVariable(context, name, ConvertSetScope(scope), value); +} + +unique_ptr CV2ClientConfig::GetOption(std::string_view name) { + return CV2Option::FromName(CV2OptionSource(context), name); +} + +unique_ptr CV2ClientConfig::GetOptionByIndex(idx_t index) { + return CV2Option::FromIndex(CV2OptionSource(context), index); +} + +idx_t CV2ClientConfig::GetOptionCount() { + return CV2Option::Count(CV2OptionSource(context)); +} + +} // namespace capiv2 +} // namespace duckdb + +using namespace duckdb::capiv2; + +DUCKDB_V2_ERROR duckdb_v2_instance_get_config(duckdb_v2_instance_handle instance, duckdb_v2_config_handle *out_config, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(instance); + DUCKDB_CHECK_ARG(out_config); + *out_config = nullptr; + return WithErrorHandler(err, [&]() { *out_config = Convert(&Convert(instance)->config_handle); }); +} + +DUCKDB_V2_ERROR duckdb_v2_connection_get_config(duckdb_v2_connection_handle conn, duckdb_v2_config_handle *out_config, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(conn); + DUCKDB_CHECK_ARG(out_config); + *out_config = nullptr; + return WithErrorHandler(err, [&]() { *out_config = Convert(&Convert(conn)->context_handle.config_handle); }); +} + +DUCKDB_V2_ERROR duckdb_v2_context_get_config(duckdb_v2_context_handle ctx, duckdb_v2_config_handle *out_config, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(ctx); + DUCKDB_CHECK_ARG(out_config); + *out_config = nullptr; + return WithErrorHandler(err, [&]() { *out_config = Convert(&Convert(ctx)->config_handle); }); +} + +DUCKDB_V2_ERROR duckdb_v2_config_get_option_value(duckdb_v2_config_handle config, const duckdb_v2_identifier_t *name, + duckdb_v2_value_handle *out_value, DUCKDB_V2_SETTING_SCOPE *out_scope, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(config); + DUCKDB_CHECK_ARG(name); + DUCKDB_CHECK_ARG(out_value); + DUCKDB_CHECK_ARG(out_scope); + *out_value = nullptr; + return WithErrorHandler(err, [&]() { + duckdb::Value value; + *out_scope = Convert(config)->ReadValue(ConvertIdentifierName(name), value); + *out_value = Convert(new duckdb::Value(std::move(value))); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_config_set_option_value(duckdb_v2_config_handle config, const duckdb_v2_identifier_t *name, + duckdb_v2_value_handle value, DUCKDB_V2_SETTING_SCOPE scope, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(config); + DUCKDB_CHECK_ARG(name); + DUCKDB_CHECK_ARG(value); + return WithErrorHandler(err, [&]() { + Convert(config)->Write(duckdb::Identifier(ConvertIdentifierName(name)), *Convert(value), scope); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_config_set_option_text(duckdb_v2_config_handle config, const duckdb_v2_identifier_t *name, + const duckdb_v2_str *setting, DUCKDB_V2_SETTING_SCOPE scope, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(config); + DUCKDB_CHECK_ARG(name); + DUCKDB_CHECK_ARG(setting); + return WithErrorHandler(err, [&]() { + Convert(config)->Write(duckdb::Identifier(ConvertIdentifierName(name)), + duckdb::Value(duckdb::string(Convert(setting))), scope); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_config_get_option_by_name(duckdb_v2_config_handle config, const duckdb_v2_identifier_t *name, + duckdb_v2_option_handle *out_option, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(config); + DUCKDB_CHECK_ARG(name); + DUCKDB_CHECK_ARG(out_option); + *out_option = nullptr; + return WithErrorHandler( + err, [&]() { *out_option = Convert(Convert(config)->GetOption(ConvertIdentifierName(name)).release()); }); +} + +DUCKDB_V2_ERROR duckdb_v2_config_get_option_count(duckdb_v2_config_handle config, idx_t *out_count, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(config); + DUCKDB_CHECK_ARG(out_count); + return WithErrorHandler(err, [&]() { *out_count = Convert(config)->GetOptionCount(); }); +} + +DUCKDB_V2_ERROR duckdb_v2_config_get_option_by_index(duckdb_v2_config_handle config, idx_t index, + duckdb_v2_option_handle *out_option, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(config); + DUCKDB_CHECK_ARG(out_option); + *out_option = nullptr; + return WithErrorHandler(err, [&]() { *out_option = Convert(Convert(config)->GetOptionByIndex(index).release()); }); +} diff --git a/src/duckdb/src/main/capi/v2/capi_v2_connection.cpp b/src/duckdb/src/main/capi/v2/capi_v2_connection.cpp index 7d0fda671..2d88d63cb 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_connection.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_connection.cpp @@ -1,41 +1,20 @@ #include "duckdb/main/capi_v2/capi_v2_internal.hpp" -namespace duckdb { -namespace capiv2 { -namespace { - -// Map V2's user-facing scope choice to DuckDB's SetScope. -SetScope MapSettingScope(DUCKDB_V2_SETTING_SCOPE s) { - switch (s) { - case DUCKDB_V2_SETTING_SCOPE_GLOBAL: - return SetScope::GLOBAL; - case DUCKDB_V2_SETTING_SCOPE_LOCAL: - return SetScope::SESSION; - default: - return SetScope::AUTOMATIC; - } -} - -} // namespace - -} // namespace capiv2 -} // namespace duckdb - using namespace duckdb::capiv2; -DUCKDB_V2_ERROR duckdb_v2_connect(duckdb_v2_database_handle db, duckdb_v2_connection_handle *out_conn, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(db); +DUCKDB_V2_ERROR duckdb_v2_connection_create(duckdb_v2_instance_handle instance, duckdb_v2_connection_handle *out_conn, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(instance); DUCKDB_CHECK_ARG(out_conn); *out_conn = nullptr; return WithErrorHandler(err, [&]() { - auto *db_wrapper = Convert(db); - auto connection = duckdb::make_uniq(*db_wrapper->database); + auto &instance_wrapper = *Convert(instance); + auto connection = duckdb::make_uniq(instance_wrapper.GetDatabase()); *out_conn = Convert(connection.release()); }); } -DUCKDB_V2_ERROR duckdb_v2_disconnect(duckdb_v2_connection_handle *conn) { +DUCKDB_V2_ERROR duckdb_v2_connection_destroy(duckdb_v2_connection_handle *conn) { return WithErrorHandler(nullptr, [&]() { if (!conn) { return; @@ -47,56 +26,6 @@ DUCKDB_V2_ERROR duckdb_v2_disconnect(duckdb_v2_connection_handle *conn) { }); } -DUCKDB_V2_ERROR duckdb_v2_connection_option_set(duckdb_v2_connection_handle conn, duckdb_v2_option_handle option, - DUCKDB_V2_SETTING_SCOPE scope, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(option); - return WithErrorHandler(err, [&]() { - auto *opt = Convert(option); - auto &client = *Convert(conn)->context; - duckdb::PhysicalSet::SetVariable(client, opt->name, MapSettingScope(scope), duckdb::Value(opt->setting)); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_connection_option_get(duckdb_v2_connection_handle conn, duckdb_v2_identifier_t name, - duckdb_v2_option_handle *out_option, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(name); - DUCKDB_CHECK_ARG(out_option); - *out_option = nullptr; - return WithErrorHandler(err, [&]() { - auto &client = *Convert(conn)->context; - auto &config = duckdb::DBConfig::GetConfig(client); - auto wrapper = CV2Option::FromName(client, config, Convert(name)); - *out_option = Convert(wrapper.release()); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_connection_option_get_count(duckdb_v2_connection_handle conn, idx_t *out_count, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(out_count); - return WithErrorHandler(err, [&]() { - auto &client = *Convert(conn)->context; - auto &config = duckdb::DBConfig::GetConfig(client); - *out_count = duckdb::DBConfig::GetOptionCount() + config.GetExtensionSettings().size(); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_connection_option_get_by_index(duckdb_v2_connection_handle conn, idx_t index, - duckdb_v2_option_handle *out_option, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(out_option); - *out_option = nullptr; - return WithErrorHandler(err, [&]() { - auto &client = *Convert(conn)->context; - auto &config = duckdb::DBConfig::GetConfig(client); - auto wrapper = CV2Option::FromIndex(client, config, index); - *out_option = Convert(wrapper.release()); - }); -} - // --------------------------------------------------------------------------- // Query process management // --------------------------------------------------------------------------- @@ -106,7 +35,7 @@ DUCKDB_V2_ERROR duckdb_v2_connection_interrupt(duckdb_v2_connection_handle conn, return WithErrorHandler(err, [&]() { // Record that the cancellation was user-initiated. auto &context = *Convert(conn)->context; - GetBusySlot(context)->cancel_requested.store(true); + GetBusySlot(context)->cancel_requests.fetch_add(1); // ClientContext::Interrupt is an atomic store; safe to call from any thread context.Interrupt(); diff --git a/src/duckdb/src/main/capi/v2/capi_v2_custom_option.cpp b/src/duckdb/src/main/capi/v2/capi_v2_custom_option.cpp new file mode 100644 index 000000000..beeb84e3b --- /dev/null +++ b/src/duckdb/src/main/capi/v2/capi_v2_custom_option.cpp @@ -0,0 +1,303 @@ +#include "duckdb/main/capi_v2/capi_v2_internal.hpp" +#include "duckdb/main/config.hpp" + +namespace duckdb::capiv2 { + +class CV2CustomOptionUpdateInfo { +public: + CV2CustomOptionUpdateInfo(ClientContext &context, const LogicalType &type, Value &value, SetScope scope, + bool is_reset, void *user_data) + : context(context), type(type), value(value), scope(scope), is_reset(is_reset), in_user_data(user_data) { + } + + ClientContext &context; + const LogicalType &type; + //! The value the option is updated to, replaceable by the callback + Value &value; + SetScope scope; + bool is_reset; + void *in_user_data; +}; + +static auto Convert(duckdb_v2_custom_option_update_info_handle info) -> CV2CustomOptionUpdateInfo * { + return reinterpret_cast(info); +} +static auto Convert(CV2CustomOptionUpdateInfo *info) -> duckdb_v2_custom_option_update_info_handle { + return reinterpret_cast(info); +} + +class CV2CustomOptionInfo final : public ExtensionOptionInfo { +public: + void OnSet(ClientContext &context, SetScope scope, Value ¶meter, bool is_reset) const override { + if (!update_cb) { + return; + } + CV2CustomOptionUpdateInfo info(context, type, parameter, scope, is_reset, + user_data ? user_data->GetData() : nullptr); + + CV2ErrorInfo err = {}; + auto err_ptr = Convert(&err); + CV2CallbackContext callback_context(context); + update_cb(Convert(&info), Convert(&callback_context), &err_ptr); + + if (err.HasError()) { + err.ThrowAsException(); + } + } + +public: + duckdb_v2_custom_option_update_callback_fn update_cb = nullptr; + shared_ptr user_data = nullptr; + LogicalType type; +}; + +class CV2CustomOption { +public: + explicit CV2CustomOption(DatabaseInstance &db) : db(db) { + } + + //! Validates the configuration and registers the option on `target`. + void Register(DatabaseInstance &target) { + CheckRegistrationTarget(db, target, "custom option"); + if (name.empty()) { + throw InvalidInputException("Option name cannot be empty."); + } + auto option_type = type; + if (option_type.id() == LogicalTypeId::INVALID) { + option_type = default_value.type(); + } + if (option_type.id() == LogicalTypeId::INVALID || option_type.id() == LogicalTypeId::SQLNULL) { + throw InvalidInputException( + "A type, or a default value to take the type from, must be set for the option."); + } + if (!option_type.IsComplete()) { + throw InvalidInputException("The type of the option must be a fully defined concrete type."); + } + if (!supports_global && !supports_session) { + throw InvalidInputException("The option must support at least one of the GLOBAL and SESSION scopes."); + } + auto scope = default_scope; + if (scope == SetScope::AUTOMATIC) { + scope = supports_session ? SetScope::SESSION : SetScope::GLOBAL; + } + if ((scope == SetScope::GLOBAL && !supports_global) || (scope == SetScope::SESSION && !supports_session)) { + throw InvalidInputException("The default scope of the option must be a scope it supports."); + } + + auto &config = DBConfig::GetConfig(target); + if (DBConfig::GetOptionByName(name) || config.HasExtensionOption(name)) { + throw InvalidInputException("An option named \"%s\" already exists.", name); + } + + ExtensionOption option; + option.description = description; + option.type = option_type; + option.default_value = default_value.IsNull() ? Value(option_type) : default_value.DefaultCastAs(option_type); + option.default_scope = scope; + option.supports_global = supports_global; + option.supports_session = supports_session; + auto option_info = make_shared_ptr(); + option_info->update_cb = update_cb; + option_info->user_data = user_data; + option_info->type = option_type; + option.info = std::move(option_info); + config.AddExtensionOption(name, std::move(option)); + } + +public: + //! The database it was created for: the only one it can be registered on. + DatabaseInstance &db; + Identifier name; + LogicalType type; + Value default_value; + string description; + //! AUTOMATIC until set: resolved at registration from the supported scopes + SetScope default_scope = SetScope::AUTOMATIC; + bool supports_global = true; + bool supports_session = true; + duckdb_v2_custom_option_update_callback_fn update_cb = nullptr; + shared_ptr user_data = nullptr; +}; + +static auto Convert(duckdb_v2_custom_option_handle option) -> CV2CustomOption * { + return reinterpret_cast(option); +} +static auto Convert(CV2CustomOption *option) -> duckdb_v2_custom_option_handle { + return reinterpret_cast(option); +} + +static auto ConvertWriteScope(DUCKDB_V2_SETTING_SCOPE scope) -> SetScope { + switch (scope) { + case DUCKDB_V2_SETTING_SCOPE_GLOBAL: + return SetScope::GLOBAL; + case DUCKDB_V2_SETTING_SCOPE_SESSION: + return SetScope::SESSION; + default: + throw InvalidInputException("The scope must be GLOBAL or SESSION."); + } +} + +} // namespace duckdb::capiv2 + +//---------------------------------------------------------------------------------------------------------------------- +// Public Functions +//---------------------------------------------------------------------------------------------------------------------- + +using namespace duckdb::capiv2; + +DUCKDB_V2_ERROR duckdb_v2_custom_option_create(duckdb_v2_factory_handle factory, duckdb_v2_custom_option_handle *option, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); + DUCKDB_CHECK_ARG(option); + *option = nullptr; + return WithErrorHandler(err, [&]() { + auto result = duckdb::make_uniq(Convert(factory)->GetDatabase()); + *option = Convert(result.release()); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_custom_option_set_name(duckdb_v2_custom_option_handle option, + const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(option); + DUCKDB_CHECK_ARG(name); + return WithErrorHandler(err, [&]() { Convert(option)->name = duckdb::Identifier(ConvertIdentifierName(name)); }); +} + +DUCKDB_V2_ERROR duckdb_v2_custom_option_set_type(duckdb_v2_custom_option_handle option, + duckdb_v2_logical_type_handle type, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(option); + DUCKDB_CHECK_ARG(type); + return WithErrorHandler(err, [&]() { + auto option_type = Convert(type); + Convert(option)->type = *option_type; + }); +} + +DUCKDB_V2_ERROR duckdb_v2_custom_option_set_default_value(duckdb_v2_custom_option_handle option, + duckdb_v2_value_handle value, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(option); + DUCKDB_CHECK_ARG(value); + return WithErrorHandler(err, [&]() { Convert(option)->default_value = *Convert(value); }); +} + +DUCKDB_V2_ERROR duckdb_v2_custom_option_set_description(duckdb_v2_custom_option_handle option, + const duckdb_v2_str *description, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(option); + DUCKDB_CHECK_ARG(description); + return WithErrorHandler(err, [&]() { Convert(option)->description = duckdb::string(Convert(description)); }); +} + +DUCKDB_V2_ERROR duckdb_v2_custom_option_set_default_scope(duckdb_v2_custom_option_handle option, + DUCKDB_V2_SETTING_SCOPE scope, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(option); + return WithErrorHandler(err, [&]() { Convert(option)->default_scope = ConvertWriteScope(scope); }); +} + +DUCKDB_V2_ERROR duckdb_v2_custom_option_set_supports_scope(duckdb_v2_custom_option_handle option, + DUCKDB_V2_SETTING_SCOPE scope, bool supported, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(option); + return WithErrorHandler(err, [&]() { + auto &self = *Convert(option); + if (ConvertWriteScope(scope) == duckdb::SetScope::GLOBAL) { + self.supports_global = supported; + } else { + self.supports_session = supported; + } + }); +} + +DUCKDB_V2_ERROR duckdb_v2_custom_option_set_user_data(duckdb_v2_custom_option_handle option, duckdb_v2_opaque *data, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(option); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { + Convert(option)->user_data = duckdb::make_shared_ptr(data->ptr, data->destroy, data->equals); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_custom_option_set_update_callback(duckdb_v2_custom_option_handle option, + duckdb_v2_custom_option_update_callback_fn callback, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(option); + return WithErrorHandler(err, [&]() { Convert(option)->update_cb = callback; }); +} + +DUCKDB_V2_ERROR duckdb_v2_custom_option_update_get_user_data(duckdb_v2_custom_option_update_info_handle info, + void **data, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_user_data; }); +} + +DUCKDB_V2_ERROR duckdb_v2_custom_option_update_get_value(duckdb_v2_custom_option_update_info_handle info, + duckdb_v2_value_handle *value, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(value); + *value = nullptr; + return WithErrorHandler(err, [&]() { *value = Convert(new duckdb::Value(Convert(info)->value)); }); +} + +DUCKDB_V2_ERROR duckdb_v2_custom_option_update_set_value(duckdb_v2_custom_option_update_info_handle info, + duckdb_v2_value_handle value, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(value); + return WithErrorHandler(err, [&]() { + auto &update = *Convert(info); + update.value = Convert(value)->CastAs(update.context, update.type); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_custom_option_update_get_scope(duckdb_v2_custom_option_update_info_handle info, + DUCKDB_V2_SETTING_SCOPE *scope, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(scope); + return WithErrorHandler(err, [&]() { + *scope = Convert(info)->scope == duckdb::SetScope::GLOBAL ? DUCKDB_V2_SETTING_SCOPE_GLOBAL + : DUCKDB_V2_SETTING_SCOPE_SESSION; + }); +} + +DUCKDB_V2_ERROR duckdb_v2_custom_option_update_is_reset(duckdb_v2_custom_option_update_info_handle info, bool *is_reset, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(is_reset); + return WithErrorHandler(err, [&]() { *is_reset = Convert(info)->is_reset; }); +} + +DUCKDB_V2_ERROR duckdb_v2_connection_register_custom_option(duckdb_v2_connection_handle conn, + duckdb_v2_custom_option_handle option, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(conn); + DUCKDB_CHECK_ARG(option); + return WithErrorHandler(err, [&]() { Convert(option)->Register(*Convert(conn)->context->db); }); +} + +DUCKDB_V2_ERROR duckdb_v2_extension_register_custom_option(duckdb_v2_extension_handle extension, + duckdb_v2_custom_option_handle option, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(extension); + DUCKDB_CHECK_ARG(option); + return WithErrorHandler(err, [&]() { + auto &loader = GetExtensionLoader(extension); + Convert(option)->Register(loader.GetDatabaseInstance()); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_custom_option_destroy(duckdb_v2_custom_option_handle *option) { + return WithErrorHandler(nullptr, [&]() { + if (!option) { + return; + } + if (*option) { + delete Convert(*option); + *option = nullptr; + } + }); +} diff --git a/src/duckdb/src/main/capi/v2/capi_v2_custom_type.cpp b/src/duckdb/src/main/capi/v2/capi_v2_custom_type.cpp index 57aec59a4..74ba3a4fd 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_custom_type.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_custom_type.cpp @@ -6,9 +6,13 @@ namespace duckdb::capiv2 { class CV2CustomType { public: + explicit CV2CustomType(DatabaseInstance &db) : db(db) { + } + // Validates the configuration and returns the type to install: the base type carrying the custom type's name as // its alias, which is what makes it logically distinct from the base type. - LogicalType Build() { + LogicalType Build(DatabaseInstance &target) { + CheckRegistrationTarget(db, target, "custom type"); if (name.empty()) { throw InvalidInputException("Type name cannot be empty."); } @@ -21,56 +25,13 @@ class CV2CustomType { return base_type.WithAlias(name.GetIdentifierName()); } - void Register() { - RegisterToCatalog(Build()); - } - - virtual ~CV2CustomType() = default; - virtual void RegisterToCatalog(LogicalType type) = 0; - public: + //! The database it was created for: the only one it can be registered on. + DatabaseInstance &db; Identifier name; LogicalType base_type; }; -class CV2ConnectionCustomType : public CV2CustomType { -public: - explicit CV2ConnectionCustomType(Connection &connection) : connection(connection) { - } - - void RegisterToCatalog(LogicalType type) override { - auto &context = *connection.context; - - context.RunFunctionInTransaction([&]() { - auto &catalog = Catalog::GetSystemCatalog(context); - // Read the name before the type is moved out: sibling arguments have no evaluation order. - auto name = type.GetAlias(); - CreateTypeInfo type_info(std::move(name), std::move(type)); - type_info.temporary = true; - type_info.internal = true; - type_info.on_conflict = OnCreateConflict::ALTER_ON_CONFLICT; - catalog.CreateType(context, type_info); - }); - } - -private: - Connection &connection; -}; - -class CV2ExtensionCustomType : public CV2CustomType { -public: - explicit CV2ExtensionCustomType(ExtensionLoader &loader) : loader(loader) { - } - - void RegisterToCatalog(LogicalType type) override { - auto name = type.GetAlias(); - loader.RegisterType(std::move(name), std::move(type)); - } - -private: - ExtensionLoader &loader; -}; - static auto Convert(duckdb_v2_custom_type_handle type) -> CV2CustomType * { return reinterpret_cast(type); } @@ -86,37 +47,22 @@ static auto Convert(CV2CustomType *type) -> duckdb_v2_custom_type_handle { using namespace duckdb::capiv2; -DUCKDB_V2_ERROR duckdb_v2_custom_type_create_with_connection(duckdb_v2_connection_handle connection, - duckdb_v2_custom_type_handle *out_type, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(connection); - DUCKDB_CHECK_ARG(out_type); - *out_type = nullptr; - return WithErrorHandler(err, [&]() { - auto &conn = *Convert(connection); - auto type = duckdb::make_uniq(conn); - *out_type = Convert(type.release()); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_custom_type_create_with_extension(duckdb_v2_extension_handle extension, - duckdb_v2_custom_type_handle *out_type, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(extension); - DUCKDB_CHECK_ARG(out_type); - *out_type = nullptr; +DUCKDB_V2_ERROR duckdb_v2_custom_type_create(duckdb_v2_factory_handle factory, duckdb_v2_custom_type_handle *type, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); + DUCKDB_CHECK_ARG(type); + *type = nullptr; return WithErrorHandler(err, [&]() { - auto &loader = GetExtensionLoader(extension); - auto type = duckdb::make_uniq(loader); - *out_type = Convert(type.release()); + auto result = duckdb::make_uniq(Convert(factory)->GetDatabase()); + *type = Convert(result.release()); }); } -DUCKDB_V2_ERROR duckdb_v2_custom_type_set_name(duckdb_v2_custom_type_handle type, duckdb_v2_identifier_t name, +DUCKDB_V2_ERROR duckdb_v2_custom_type_set_name(duckdb_v2_custom_type_handle type, const duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(type); DUCKDB_CHECK_ARG(name); - return WithErrorHandler(err, [&]() { Convert(type)->name = duckdb::Identifier(Convert(name)); }); + return WithErrorHandler(err, [&]() { Convert(type)->name = duckdb::Identifier(ConvertIdentifierName(name)); }); } DUCKDB_V2_ERROR duckdb_v2_custom_type_set_base_type(duckdb_v2_custom_type_handle type, @@ -124,12 +70,43 @@ DUCKDB_V2_ERROR duckdb_v2_custom_type_set_base_type(duckdb_v2_custom_type_handle duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(type); DUCKDB_CHECK_ARG(base_type); - return WithErrorHandler(err, [&]() { Convert(type)->base_type = *Convert(base_type); }); + return WithErrorHandler(err, [&]() { + auto base = Convert(base_type); + Convert(type)->base_type = *base; + }); +} + +DUCKDB_V2_ERROR duckdb_v2_connection_register_custom_type(duckdb_v2_connection_handle conn, + duckdb_v2_custom_type_handle type, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(conn); + DUCKDB_CHECK_ARG(type); + return WithErrorHandler(err, [&]() { + auto &context = *Convert(conn)->context; + context.RunFunctionInTransaction([&]() { + auto type_value = Convert(type)->Build(*context.db); + // Read the name before the type is moved out: sibling arguments have no evaluation order. + auto name = type_value.GetAlias(); + duckdb::CreateTypeInfo info(std::move(name), std::move(type_value)); + info.temporary = true; + info.internal = true; + info.on_conflict = duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + duckdb::Catalog::GetSystemCatalog(context).CreateType(context, info); + }); + }); } -DUCKDB_V2_ERROR duckdb_v2_custom_type_register(duckdb_v2_custom_type_handle type, duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR duckdb_v2_extension_register_custom_type(duckdb_v2_extension_handle extension, + duckdb_v2_custom_type_handle type, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(extension); DUCKDB_CHECK_ARG(type); - return WithErrorHandler(err, [&]() { Convert(type)->Register(); }); + return WithErrorHandler(err, [&]() { + auto &loader = GetExtensionLoader(extension); + auto type_value = Convert(type)->Build(loader.GetDatabaseInstance()); + auto name = type_value.GetAlias(); + loader.RegisterType(std::move(name), std::move(type_value)); + }); } DUCKDB_V2_ERROR duckdb_v2_custom_type_destroy(duckdb_v2_custom_type_handle *type) { diff --git a/src/duckdb/src/main/capi/v2/capi_v2_data_chunk.cpp b/src/duckdb/src/main/capi/v2/capi_v2_data_chunk.cpp index 84eb72dac..b4da83873 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_data_chunk.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_data_chunk.cpp @@ -15,7 +15,8 @@ static void CreateDataChunk(Allocator &allocator, const duckdb_v2_logical_type_h if (!types[i]) { throw InvalidInputException("null logical type at index %llu", i); } - const auto <ype = *Convert(types[i]); + auto ltype_ref = Convert(types[i]); + const auto <ype = *ltype_ref; // ANY is a signature wildcard with no physical layout; a chunk allocates // storage, so reject it (an ANY vector throws InternalException). if (TypeVisitor::Contains(ltype, LogicalTypeId::ANY)) { @@ -28,12 +29,12 @@ static void CreateDataChunk(Allocator &allocator, const duckdb_v2_logical_type_h *out_chunk = Convert(chunk.release()); } -static void CopyDataChunk(ClientContext &context, duckdb_v2_data_chunk_handle chunk, +static void CopyDataChunk(DatabaseInstance &db, duckdb_v2_data_chunk_handle chunk, duckdb_v2_data_chunk_handle *out_chunk) { *out_chunk = nullptr; auto &source = *Convert(chunk); auto copy = make_uniq(); - copy->Initialize(Allocator::Get(context), source.GetTypes(), MaxValue(source.size(), STANDARD_VECTOR_SIZE)); + copy->Initialize(Allocator::Get(db), source.GetTypes(), MaxValue(source.size(), STANDARD_VECTOR_SIZE)); source.Copy(*copy); copy->SetCardinalityUnsafe(source.size()); *out_chunk = Convert(copy.release()); @@ -45,55 +46,28 @@ static void CopyDataChunk(ClientContext &context, duckdb_v2_data_chunk_handle ch using namespace duckdb::capiv2; // TODO: this should be removed. -DUCKDB_V2_ERROR duckdb_v2_data_chunk_create(const duckdb_v2_logical_type_handle *types, idx_t column_count, +DUCKDB_V2_ERROR duckdb_v2_data_chunk_create(duckdb_v2_factory_handle factory, + const duckdb_v2_logical_type_handle *types, idx_t column_count, duckdb_v2_data_chunk_handle *out_chunk, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(types); - DUCKDB_CHECK_ARG(out_chunk); - return WithErrorHandler( - err, [&]() { CreateDataChunk(duckdb::Allocator::DefaultAllocator(), types, column_count, out_chunk); }); -} - -DUCKDB_V2_ERROR duckdb_v2_data_chunk_create_with_connection(duckdb_v2_connection_handle conn, - const duckdb_v2_logical_type_handle *types, - idx_t column_count, duckdb_v2_data_chunk_handle *out_chunk, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(types); DUCKDB_CHECK_ARG(out_chunk); return WithErrorHandler(err, [&]() { - CreateDataChunk(duckdb::Allocator::Get(*Convert(conn)->context), types, column_count, out_chunk); + CreateDataChunk(duckdb::Allocator::Get(Convert(factory)->GetDatabase()), types, column_count, out_chunk); }); } -DUCKDB_V2_ERROR duckdb_v2_data_chunk_create_with_context(duckdb_v2_context_handle context, - const duckdb_v2_logical_type_handle *types, idx_t column_count, - duckdb_v2_data_chunk_handle *out_chunk, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(context); - DUCKDB_CHECK_ARG(types); - DUCKDB_CHECK_ARG(out_chunk); - return WithErrorHandler( - err, [&]() { CreateDataChunk(duckdb::Allocator::Get(*Convert(context)), types, column_count, out_chunk); }); -} - -DUCKDB_V2_ERROR duckdb_v2_data_chunk_copy_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_data_chunk_handle chunk, - duckdb_v2_data_chunk_handle *out_chunk, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_data_chunk_copy(duckdb_v2_factory_handle factory, duckdb_v2_data_chunk_handle chunk, + duckdb_v2_data_chunk_handle *out_chunk, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(chunk); DUCKDB_CHECK_ARG(out_chunk); - return WithErrorHandler(err, [&]() { CopyDataChunk(*Convert(conn)->context, chunk, out_chunk); }); + return WithErrorHandler(err, [&]() { CopyDataChunk(Convert(factory)->GetDatabase(), chunk, out_chunk); }); } -DUCKDB_V2_ERROR duckdb_v2_data_chunk_copy_with_context(duckdb_v2_context_handle context, - duckdb_v2_data_chunk_handle chunk, - duckdb_v2_data_chunk_handle *out_chunk, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(context); +DUCKDB_V2_ERROR duckdb_v2_data_chunk_clear(duckdb_v2_data_chunk_handle chunk, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(chunk); - DUCKDB_CHECK_ARG(out_chunk); - return WithErrorHandler(err, [&]() { CopyDataChunk(*Convert(context), chunk, out_chunk); }); + return WithErrorHandler(err, [&]() { Convert(chunk)->Reset(); }); } DUCKDB_V2_ERROR duckdb_v2_data_chunk_destroy(duckdb_v2_data_chunk_handle *chunk) { @@ -115,6 +89,13 @@ DUCKDB_V2_ERROR duckdb_v2_data_chunk_get_size(duckdb_v2_data_chunk_handle chunk, return WithErrorHandler(err, [&]() { *out_size = Convert(chunk)->size(); }); } +DUCKDB_V2_ERROR duckdb_v2_data_chunk_get_capacity(duckdb_v2_data_chunk_handle chunk, idx_t *out_capacity, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(chunk); + DUCKDB_CHECK_ARG(out_capacity); + return WithErrorHandler(err, [&]() { *out_capacity = Convert(chunk)->GetCapacity(); }); +} + DUCKDB_V2_ERROR duckdb_v2_data_chunk_get_vector_count(duckdb_v2_data_chunk_handle chunk, idx_t *out_count, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(chunk); diff --git a/src/duckdb/src/main/capi/v2/capi_v2_database.cpp b/src/duckdb/src/main/capi/v2/capi_v2_database.cpp deleted file mode 100644 index 823e686e7..000000000 --- a/src/duckdb/src/main/capi/v2/capi_v2_database.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include "duckdb/main/capi_v2/capi_v2_internal.hpp" - -#include "duckdb/common/types/value.hpp" - -using namespace duckdb::capiv2; - -DUCKDB_V2_ERROR duckdb_v2_open(duckdb_v2_environment_handle env, duckdb_v2_str path, duckdb_v2_option_handle *options, - idx_t option_count, duckdb_v2_database_handle *out_db, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(env); - DUCKDB_CHECK_ARG(out_db); - DUCKDB_CHECK_ARG(path); - - return WithErrorHandler(err, [&]() { - if (option_count > 0 && !options) { - throw duckdb::InvalidInputException("option_count > 0 but options is null in duckdb_v2_open"); - } - - *out_db = nullptr; - - const auto config = duckdb::make_uniq(); - for (idx_t i = 0; i < option_count; i++) { - const auto *opt = Convert(options[i]); - if (!opt) { - throw duckdb::InvalidInputException("null option handle in options array passed to duckdb_v2_open"); - } - config->SetOptionByName(opt->name, duckdb::Value(opt->setting)); - } - - // Route through the env's DBInstanceCache with NEVER_CACHE so the path manager is shared across all opens. - // File conflicts get detected, but no instance is memoized. Every open produces a fresh DatabaseInstance. - - auto *env_wrapper = Convert(env); - const auto path_str = duckdb::string(Convert(path)); - const auto db = env_wrapper->cache->GetOrCreateInstance(path_str, *config, duckdb::CacheBehavior::NEVER_CACHE); - auto wrapper = duckdb::make_uniq(*env_wrapper, db); - env_wrapper->open_database_count.fetch_add(1, std::memory_order_release); - *out_db = Convert(wrapper.release()); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_close(duckdb_v2_database_handle *db) { - return WithErrorHandler(nullptr, [&]() { - if (!db) { - return; - } - if (*db) { - const auto *wrapper = Convert(*db); - auto &env = wrapper->env; - delete wrapper; - env.open_database_count.fetch_sub(1, std::memory_order_release); - *db = nullptr; - } - }); -} - -DUCKDB_V2_ERROR duckdb_v2_database_option_set(duckdb_v2_database_handle db, duckdb_v2_option_handle option, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(db); - DUCKDB_CHECK_ARG(option); - return WithErrorHandler(err, [&]() { - const auto *db_wrapper = Convert(db); - const auto *opt = Convert(option); - // Use the database's internal ClientContext as the bridge into PhysicalSet::ApplyVariable. - // Force GLOBAL scope: the internal context has no LOCAL settings of its own, and database-scoped settings only - // make sense as GLOBAL anyway. - auto &client = *db_wrapper->internal_connection->context; - duckdb::PhysicalSet::SetVariable(client, opt->name, duckdb::SetScope::GLOBAL, duckdb::Value(opt->setting)); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_database_option_get(duckdb_v2_database_handle db, duckdb_v2_identifier_t name, - duckdb_v2_option_handle *out_option, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(db); - DUCKDB_CHECK_ARG(name); - DUCKDB_CHECK_ARG(out_option); - *out_option = nullptr; - return WithErrorHandler(err, [&]() { - const auto *db_wrapper = Convert(db); - auto &client = *db_wrapper->internal_connection->context; - auto &config = db_wrapper->database->instance->config; - auto wrapper = CV2Option::FromName(client, config, Convert(name)); - *out_option = Convert(wrapper.release()); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_database_option_get_count(duckdb_v2_database_handle db, idx_t *out_count, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(db); - DUCKDB_CHECK_ARG(out_count); - return WithErrorHandler(err, [&]() { - const auto *db_wrapper = Convert(db); - const auto &config = db_wrapper->database->instance->config; - *out_count = duckdb::DBConfig::GetOptionCount() + config.GetExtensionSettings().size(); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_database_option_get_by_index(duckdb_v2_database_handle db, idx_t index, - duckdb_v2_option_handle *out_option, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(db); - DUCKDB_CHECK_ARG(out_option); - *out_option = nullptr; - return WithErrorHandler(err, [&]() { - const auto *db_wrapper = Convert(db); - auto &client = *db_wrapper->internal_connection->context; - auto &config = db_wrapper->database->instance->config; - - auto wrapper = CV2Option::FromIndex(client, config, index); - *out_option = Convert(wrapper.release()); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_library_version(duckdb_v2_str *out_version, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(out_version); - return WithErrorHandler(err, [&]() { - const auto version = duckdb::DuckDB::LibraryVersion(); - *out_version = duckdb_v2_str {version, std::strlen(version)}; - }); -} diff --git a/src/duckdb/src/main/capi/v2/capi_v2_environment.cpp b/src/duckdb/src/main/capi/v2/capi_v2_environment.cpp index 49c65685a..96fe072e9 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_environment.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_environment.cpp @@ -2,7 +2,7 @@ using namespace duckdb::capiv2; -DUCKDB_V2_ERROR duckdb_v2_create_environment(duckdb_v2_environment_handle *out_env, duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR duckdb_v2_environment_create(duckdb_v2_environment_handle *out_env, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(out_env); *out_env = nullptr; return WithErrorHandler(err, [&]() { @@ -12,15 +12,15 @@ DUCKDB_V2_ERROR duckdb_v2_create_environment(duckdb_v2_environment_handle *out_e }); } -// destroy_environment keeps a manual return path so the open-databases case +// environment_destroy keeps a manual return path so the live-instances case // can surface as RESOURCE_IN_USE — there is no ExceptionType that maps to // that V2 code, so routing it through WithErrorHandler would degrade it. -DUCKDB_V2_ERROR duckdb_v2_destroy_environment(duckdb_v2_environment_handle *env) { +DUCKDB_V2_ERROR duckdb_v2_environment_destroy(duckdb_v2_environment_handle *env) { if (!env || !*env) { return DUCKDB_V2_ERROR_NONE; } const auto *wrapper = Convert(*env); - auto count = wrapper->open_database_count.load(std::memory_order_acquire); + auto count = wrapper->instance_count.load(std::memory_order_acquire); if (count != 0) { return DUCKDB_V2_ERROR_RESOURCE_IN_USE; } @@ -29,9 +29,9 @@ DUCKDB_V2_ERROR duckdb_v2_destroy_environment(duckdb_v2_environment_handle *env) return DUCKDB_V2_ERROR_NONE; } -DUCKDB_V2_ERROR duckdb_v2_environment_database_count(duckdb_v2_environment_handle env, idx_t *out_count, - duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR duckdb_v2_environment_get_instance_count(duckdb_v2_environment_handle env, idx_t *out_count, + duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(env); DUCKDB_CHECK_ARG(out_count); - return WithErrorHandler(err, [&]() { *out_count = Convert(env)->open_database_count.load(); }); + return WithErrorHandler(err, [&]() { *out_count = Convert(env)->instance_count.load(); }); } diff --git a/src/duckdb/src/main/capi/v2/capi_v2_error.cpp b/src/duckdb/src/main/capi/v2/capi_v2_error.cpp index 54eb556c3..792d688cb 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_error.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_error.cpp @@ -11,22 +11,22 @@ DUCKDB_V2_ERROR duckdb_v2_error_info_get_text(duckdb_v2_error_info_handle info, return DUCKDB_V2_ERROR_NONE; } -DUCKDB_V2_ERROR duckdb_v2_error_info_get_raw_message(duckdb_v2_error_info_handle info, duckdb_v2_str *out_raw_message) { - if (!info || !out_raw_message) { +DUCKDB_V2_ERROR duckdb_v2_error_info_get_raw_text(duckdb_v2_error_info_handle info, duckdb_v2_str *out_raw_text) { + if (!info || !out_raw_text) { return DUCKDB_V2_ERROR_INPUT_INVALID; } const auto *ei = Convert(info); - *out_raw_message = ei->raw_message.empty() ? duckdb_v2_str {nullptr, 0} : Convert(ei->raw_message); + *out_raw_text = ei->raw_message ? Convert(ei->raw_message.value()) : duckdb_v2_str {nullptr, 0}; return DUCKDB_V2_ERROR_NONE; } -DUCKDB_V2_ERROR duckdb_v2_error_info_set_text(duckdb_v2_error_info_handle info, duckdb_v2_str text) { - if (!info || (!text.ptr && text.len > 0)) { +DUCKDB_V2_ERROR duckdb_v2_error_info_set_text(duckdb_v2_error_info_handle info, const duckdb_v2_str *text) { + if (!info || IsNullArgument(text)) { return DUCKDB_V2_ERROR_INPUT_INVALID; } auto *ei = Convert(info); ei->message = Convert(text); - ei->raw_message.clear(); // Directly-set message has no raw body. + ei->raw_message.reset(); // Directly-set message has no raw body. return DUCKDB_V2_ERROR_NONE; } diff --git a/src/duckdb/src/main/capi/v2/capi_v2_expression.cpp b/src/duckdb/src/main/capi/v2/capi_v2_expression.cpp index 50a3e02b3..c9cef4d26 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_expression.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_expression.cpp @@ -130,8 +130,7 @@ DUCKDB_V2_ERROR duckdb_v2_expression_get_return_type(duckdb_v2_expression_handle DUCKDB_CHECK_ARG(expression); DUCKDB_CHECK_ARG(type); *type = nullptr; - return WithErrorHandler(err, - [&]() { *type = Convert(new duckdb::LogicalType(Convert(expression)->GetReturnType())); }); + return WithErrorHandler(err, [&]() { *type = Convert(Convert(expression)->GetReturnType()); }); } DUCKDB_V2_ERROR duckdb_v2_expression_get_child_count(duckdb_v2_expression_handle expression, idx_t *count, diff --git a/src/duckdb/src/main/capi/v2/capi_v2_extension.cpp b/src/duckdb/src/main/capi/v2/capi_v2_extension.cpp index 805cbc090..b1145dcc9 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_extension.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_extension.cpp @@ -50,7 +50,7 @@ namespace { //! context to lend (startup, static linking, autoloading). Rolls back unless the load finished. class CAPIV2LoadScope { public: - explicit CAPIV2LoadScope(DatabaseInstance &db) : con(db) { + explicit CAPIV2LoadScope(DatabaseInstance &db) : con(Connection::CreateInternal(db)) { con.BeginTransaction(); } @@ -130,7 +130,8 @@ void CallEntrypoint(DuckDBExtensionLoadStateV2 &load_state, const string &extens ::duckdb_v2_extension_input input; input.get_api = statically_linked ? nullptr : ExtensionGetAPIV2; input.extension = load_state.ToCStruct(); - input.context = capiv2::Convert(&context); + capiv2::CV2CallbackContext callback_context(context); + input.context = capiv2::Convert(&callback_context); input.err = &err_handle; (*init_fun)(&input); diff --git a/src/duckdb/src/main/capi/v2/capi_v2_factory.cpp b/src/duckdb/src/main/capi/v2/capi_v2_factory.cpp new file mode 100644 index 000000000..6084f4d08 --- /dev/null +++ b/src/duckdb/src/main/capi/v2/capi_v2_factory.cpp @@ -0,0 +1,29 @@ +#include "duckdb/main/capi_v2/capi_v2_internal.hpp" + +using namespace duckdb::capiv2; + +DUCKDB_V2_ERROR duckdb_v2_instance_get_factory(duckdb_v2_instance_handle instance, + duckdb_v2_factory_handle *out_factory, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(instance); + DUCKDB_CHECK_ARG(out_factory); + *out_factory = nullptr; + return WithErrorHandler(err, [&]() { *out_factory = Convert(&Convert(instance)->factory); }); +} + +DUCKDB_V2_ERROR duckdb_v2_connection_get_factory(duckdb_v2_connection_handle conn, + duckdb_v2_factory_handle *out_factory, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(conn); + DUCKDB_CHECK_ARG(out_factory); + *out_factory = nullptr; + return WithErrorHandler(err, [&]() { *out_factory = Convert(&Convert(conn)->context_handle.factory); }); +} + +DUCKDB_V2_ERROR duckdb_v2_context_get_factory(duckdb_v2_context_handle ctx, duckdb_v2_factory_handle *out_factory, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(ctx); + DUCKDB_CHECK_ARG(out_factory); + *out_factory = nullptr; + return WithErrorHandler(err, [&]() { *out_factory = Convert(&Convert(ctx)->factory); }); +} diff --git a/src/duckdb/src/main/capi/v2/capi_v2_file_system.cpp b/src/duckdb/src/main/capi/v2/capi_v2_file_system.cpp index bef1a730d..e0704579a 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_file_system.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_file_system.cpp @@ -7,24 +7,6 @@ namespace duckdb::capiv2 { -// The file system handle, borrowed. It carries the context it was taken from alongside the file system itself, so -// that reads and writes can hand the engine a QueryContext and have their bytes attributed to the query. Kept on the -// context's own state, so the handle stays borrowed -- one per context, alive exactly as long as the context is. -class CV2FileSystem : public ClientContextState { -public: - optional_ptr fs; - //! The context the file system was taken from, so reads and writes can be attributed to the query. - QueryContext query; -}; - -inline auto GetFileSystemSlot(ClientContext &context) -> shared_ptr { - constexpr auto FILE_SYSTEM_SLOT_KEY = "c_api_v2_file_system"; - auto slot = context.registered_state->GetOrCreate(FILE_SYSTEM_SLOT_KEY); - slot->fs = &FileSystem::GetFileSystem(context); - slot->query = context; - return slot; -} - // How a file is opened, owned. Holds the flag word as given rather than the engine's FileOpenFlags, so that // "no flags set yet" stays distinguishable and is reported when the options are actually used. class CV2FileOpenOptions { @@ -100,6 +82,9 @@ static void ApplyFileFlag(CV2FileOpenOptions &options, DUCKDB_V2_FILE_FLAG flag) case DUCKDB_V2_FILE_FLAG_PARALLEL_ACCESS: options.flags |= FileOpenFlags::FILE_FLAGS_PARALLEL_ACCESS; break; + case DUCKDB_V2_FILE_FLAG_EXTERNAL_FILE_CACHE: + options.flags.SetCachingMode(CachingMode::ALWAYS_CACHE); + break; default: // Includes FILE_FLAG_INVALID, which names no behaviour. throw InvalidInputException("'%d' is not a file flag.", static_cast(flag)); @@ -115,25 +100,31 @@ static void ApplyFileFlag(CV2FileOpenOptions &options, DUCKDB_V2_FILE_FLAG flag) using namespace duckdb::capiv2; -DUCKDB_V2_ERROR duckdb_v2_file_system_get_from_context(duckdb_v2_context_handle context, - duckdb_v2_file_system_handle *out_file_system, - duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR duckdb_v2_context_get_file_system(duckdb_v2_context_handle context, + duckdb_v2_file_system_handle *out_file_system, + duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(context); DUCKDB_CHECK_ARG(out_file_system); *out_file_system = nullptr; - return WithErrorHandler(err, [&]() { *out_file_system = Convert(GetFileSystemSlot(*Convert(context)).get()); }); + return WithErrorHandler(err, [&]() { *out_file_system = Convert(&Convert(context)->file_system); }); } -DUCKDB_V2_ERROR duckdb_v2_file_system_get_from_connection(duckdb_v2_connection_handle connection, - duckdb_v2_file_system_handle *out_file_system, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(connection); +DUCKDB_V2_ERROR duckdb_v2_instance_get_file_system(duckdb_v2_instance_handle instance, + duckdb_v2_file_system_handle *out_file_system, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(instance); DUCKDB_CHECK_ARG(out_file_system); *out_file_system = nullptr; - return WithErrorHandler(err, [&]() { - auto &context = *Convert(connection)->context; - *out_file_system = Convert(GetFileSystemSlot(context).get()); - }); + return WithErrorHandler(err, [&]() { *out_file_system = Convert(&Convert(instance)->file_system); }); +} + +DUCKDB_V2_ERROR duckdb_v2_connection_get_file_system(duckdb_v2_connection_handle conn, + duckdb_v2_file_system_handle *out_file_system, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(conn); + DUCKDB_CHECK_ARG(out_file_system); + *out_file_system = nullptr; + return WithErrorHandler(err, [&]() { *out_file_system = Convert(&Convert(conn)->context_handle.file_system); }); } DUCKDB_V2_ERROR duckdb_v2_file_open_options_create(duckdb_v2_file_system_handle file_system, @@ -151,8 +142,9 @@ DUCKDB_V2_ERROR duckdb_v2_file_open_options_set_flag(duckdb_v2_file_open_options return WithErrorHandler(err, [&]() { ApplyFileFlag(*Convert(options), flag); }); } -DUCKDB_V2_ERROR duckdb_v2_file_open_options_set_value(duckdb_v2_file_open_options_handle options, duckdb_v2_str name, - duckdb_v2_value_handle value, duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR duckdb_v2_file_open_options_set_value(duckdb_v2_file_open_options_handle options, + const duckdb_v2_str *name, duckdb_v2_value_handle value, + duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(options); DUCKDB_CHECK_ARG(name); DUCKDB_CHECK_ARG(value); @@ -177,7 +169,7 @@ DUCKDB_V2_ERROR duckdb_v2_file_open_options_destroy(duckdb_v2_file_open_options_ }); } -DUCKDB_V2_ERROR duckdb_v2_file_system_open(duckdb_v2_file_system_handle file_system, duckdb_v2_str file_path, +DUCKDB_V2_ERROR duckdb_v2_file_system_open(duckdb_v2_file_system_handle file_system, const duckdb_v2_str *file_path, duckdb_v2_file_open_options_handle options, duckdb_v2_file_handle *out_file_handle, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(file_system); @@ -192,6 +184,11 @@ DUCKDB_V2_ERROR duckdb_v2_file_system_open(duckdb_v2_file_system_handle file_sys throw duckdb::InvalidInputException( "The open options carry no flags, so they cannot say whether the file is being read or written."); } + if (opts.flags.GetCachingMode() != duckdb::CachingMode::NO_CACHING && + (opts.flags.OpenForWriting() || opts.flags.OpenForAppending())) { + throw duckdb::InvalidInputException("Only a file opened for reading can be read through the external file " + "cache."); + } duckdb::OpenFileInfo info(duckdb::string(Convert(file_path))); info.extended_info = opts.extended_info; @@ -199,7 +196,7 @@ DUCKDB_V2_ERROR duckdb_v2_file_system_open(duckdb_v2_file_system_handle file_sys // No opener is passed: FileSystem::GetFileSystem hands back the context's own OpenerFileSystem, which // pushes the opener itself -- which is how a remote file system reaches settings and secrets. Supplying one // here is rejected outright ("the opener is pushed automatically"). - auto handle = slot.fs->OpenFile(info, opts.flags); + auto handle = slot.fs.OpenFile(info, opts.flags); if (!handle) { throw duckdb::IOException("Failed to open file: %s", info.path); } @@ -210,6 +207,79 @@ DUCKDB_V2_ERROR duckdb_v2_file_system_open(duckdb_v2_file_system_handle file_sys }); } +DUCKDB_V2_ERROR duckdb_v2_file_system_path_exists(duckdb_v2_file_system_handle file_system, const duckdb_v2_str *path, + bool *out_exists, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(file_system); + DUCKDB_CHECK_ARG(path); + DUCKDB_CHECK_ARG(out_exists); + return WithErrorHandler( + err, [&]() { *out_exists = Convert(file_system)->fs.FileExists(duckdb::string(Convert(path))); }); +} + +DUCKDB_V2_ERROR duckdb_v2_file_system_path_is_remote(duckdb_v2_file_system_handle file_system, + const duckdb_v2_str *path, bool *out_is_remote, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(file_system); + DUCKDB_CHECK_ARG(path); + DUCKDB_CHECK_ARG(out_is_remote); + return WithErrorHandler( + err, [&]() { *out_is_remote = duckdb::FileSystem::IsRemoteFile(duckdb::string(Convert(path))); }); +} + +DUCKDB_V2_ERROR duckdb_v2_file_system_path_expand(duckdb_v2_file_system_handle file_system, const duckdb_v2_str *path, + duckdb_v2_text_sink_fn sink, void *user_data, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(file_system); + DUCKDB_CHECK_ARG(path); + DUCKDB_CHECK_ARG(sink); + return WithErrorHandler(err, [&]() { + auto expanded = Convert(file_system)->fs.ExpandPath(duckdb::string(Convert(path))); + InvokeTextSink(sink, duckdb_v2_str {expanded.data(), expanded.size()}, user_data); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_file_system_path_glob(duckdb_v2_file_system_handle file_system, const duckdb_v2_str *pattern, + duckdb_v2_file_system_path_glob_callback_fn callback, void *user_data, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(file_system); + DUCKDB_CHECK_ARG(pattern); + DUCKDB_CHECK_ARG(callback); + return WithErrorHandler(err, [&]() { + auto matches = Convert(file_system)->fs.Glob(duckdb::string(Convert(pattern))); + for (auto &match : matches) { + CV2ErrorInfo info; + auto handle = Convert(&info); + duckdb_v2_str path {match.path.data(), match.path.size()}; + callback(&path, user_data, &handle); + if (info.HasError()) { + info.ThrowAsException(); + } + } + }); +} + +DUCKDB_V2_ERROR duckdb_v2_file_system_path_move(duckdb_v2_file_system_handle file_system, const duckdb_v2_str *source, + const duckdb_v2_str *target, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(file_system); + DUCKDB_CHECK_ARG(source); + DUCKDB_CHECK_ARG(target); + return WithErrorHandler(err, [&]() { + Convert(file_system)->fs.MoveFile(duckdb::string(Convert(source)), duckdb::string(Convert(target))); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_file_system_path_remove(duckdb_v2_file_system_handle file_system, const duckdb_v2_str *path, + bool *out_removed, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(file_system); + DUCKDB_CHECK_ARG(path); + return WithErrorHandler(err, [&]() { + auto removed = Convert(file_system)->fs.TryRemoveFile(duckdb::string(Convert(path))); + if (out_removed) { + *out_removed = removed; + } + }); +} + DUCKDB_V2_ERROR duckdb_v2_file_read(duckdb_v2_file_handle file, void *buffer, idx_t buffer_size, idx_t *bytes_read, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(file); diff --git a/src/duckdb/src/main/capi/v2/capi_v2_func.cpp b/src/duckdb/src/main/capi/v2/capi_v2_func.cpp new file mode 100644 index 000000000..b1db328e5 --- /dev/null +++ b/src/duckdb/src/main/capi/v2/capi_v2_func.cpp @@ -0,0 +1,169 @@ +#include "duckdb/main/capi_v2/capi_v2_function_internal.hpp" + +namespace duckdb::capiv2 { + +const Identifier &CV2FunctionBindInfo::GetArgName(idx_t index) const { + static const Identifier variadic_name; + const auto positional_count = function.GetPositionalArgumentCount(); + if (index >= positional_count) { + return function.GetNamedArguments()[index - positional_count]; + } + if (index < signature.GetPositionalParameterCount()) { + return signature.GetParameter(index).GetName(); + } + return variadic_name; +} + +optional_idx CV2FunctionBindInfo::FindArg(const Identifier &name) const { + for (idx_t i = signature.GetPositionalOnlyParameterCount(); i < GetArgCount(); i++) { + const auto &arg_name = GetArgName(i); + if (!arg_name.empty() && arg_name == name) { + return optional_idx(i); + } + } + return optional_idx(); +} + +LogicalType CV2ExpressionBindInfo::GetArgType(idx_t index) const { + return input.GetArguments()[index]->GetReturnType(); +} + +Value CV2ExpressionBindInfo::GetArgValue(idx_t index) const { + return input.GetConstant(index); +} + +CV2ConstantBindInfo::CV2ConstantBindInfo(const BoundSimpleFunction &function, const FunctionSignature &signature, + void *user_data, const vector &positional, + const named_argument_map_t &named) + : CV2FunctionBindInfo(function, signature, user_data), positional(positional), named(named) { + D_ASSERT(function.GetPositionalArgumentCount() == positional.size()); +} + +const Value &CV2ConstantBindInfo::GetArg(idx_t index) const { + if (index < positional.size()) { + return positional[index]; + } + const auto &name = function.GetNamedArguments()[index - positional.size()]; + auto entry = named.find(name); + if (entry == named.end()) { + throw InternalException("Named argument \"%s\" of a function call has no value", name); + } + return entry->second; +} + +LogicalType CV2ConstantBindInfo::GetArgType(idx_t index) const { + return GetArg(index).type(); +} + +Value CV2ConstantBindInfo::GetArgValue(idx_t index) const { + return GetArg(index); +} + +static void CheckArgIndex(const CV2FunctionBindInfo &info, idx_t index, const char *function_name) { + if (index >= info.GetArgCount()) { + throw InvalidInputException("Index out of bounds in %s", function_name); + } +} + +} // namespace duckdb::capiv2 + +//---------------------------------------------------------------------------------------------------------------------- +// Public Functions +//---------------------------------------------------------------------------------------------------------------------- + +using namespace duckdb::capiv2; + +DUCKDB_V2_ERROR duckdb_v2_function_bind_get_user_data(duckdb_v2_function_bind_info_handle info, void **data, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_user_data; }); +} + +DUCKDB_V2_ERROR duckdb_v2_function_bind_set_bind_data(duckdb_v2_function_bind_info_handle info, duckdb_v2_opaque *data, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { + auto &bind_data_error = Convert(info)->bind_data_error; + if (!bind_data_error.empty()) { + throw duckdb::InvalidInputException(bind_data_error); + } + Convert(info)->out_bind_data = + data->ptr ? duckdb::make_shared_ptr(data->ptr, data->destroy, data->equals) : nullptr; + }); +} + +DUCKDB_V2_ERROR duckdb_v2_function_bind_get_arg_count(duckdb_v2_function_bind_info_handle info, idx_t *positional_fixed, + idx_t *positional_variadic, idx_t *named_fixed, + idx_t *named_variadic, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + return WithErrorHandler(err, [&]() { + auto &function = Convert(info)->function; + auto &signature = Convert(info)->signature; + if (positional_fixed) { + *positional_fixed = function.GetStandardArgumentCount(signature); + } + if (positional_variadic) { + *positional_variadic = function.GetVarArgsCount(signature); + } + if (named_fixed) { + *named_fixed = function.GetKeywordOnlyArgumentCount(signature); + } + if (named_variadic) { + *named_variadic = function.GetKwargsCount(signature); + } + }); +} + +DUCKDB_V2_ERROR duckdb_v2_function_bind_get_arg_type(duckdb_v2_function_bind_info_handle info, idx_t index, + duckdb_v2_logical_type_handle *type, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(type); + *type = nullptr; + return WithErrorHandler(err, [&]() { + auto &bind_info = *Convert(info); + CheckArgIndex(bind_info, index, "duckdb_v2_function_bind_get_arg_type"); + *type = Convert(bind_info.GetArgType(index)); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_function_bind_get_arg_value(duckdb_v2_function_bind_info_handle info, idx_t index, + duckdb_v2_value_handle *value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(value); + *value = nullptr; + return WithErrorHandler(err, [&]() { + auto &bind_info = *Convert(info); + CheckArgIndex(bind_info, index, "duckdb_v2_function_bind_get_arg_value"); + *value = Convert(new duckdb::Value(bind_info.GetArgValue(index))); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_function_bind_get_arg_name(duckdb_v2_function_bind_info_handle info, idx_t index, + duckdb_v2_identifier_t *name, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(name); + return WithErrorHandler(err, [&]() { + auto &bind_info = *Convert(info); + CheckArgIndex(bind_info, index, "duckdb_v2_function_bind_get_arg_name"); + *name = Convert(bind_info.GetArgName(index)); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_function_bind_get_arg_index(duckdb_v2_function_bind_info_handle info, + const duckdb_v2_identifier_t *name, idx_t *index, bool *found, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(name); + DUCKDB_CHECK_ARG(index); + DUCKDB_CHECK_ARG(found); + return WithErrorHandler(err, [&]() { + auto entry = Convert(info)->FindArg(duckdb::Identifier(ConvertIdentifierName(name))); + *found = entry.IsValid(); + if (entry.IsValid()) { + *index = entry.GetIndex(); + } + }); +} diff --git a/src/duckdb/src/main/capi/v2/capi_v2_func_aggregate.cpp b/src/duckdb/src/main/capi/v2/capi_v2_func_aggregate.cpp index 3a4180071..d3d64d9da 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_func_aggregate.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_func_aggregate.cpp @@ -1,4 +1,5 @@ #include "duckdb/main/capi_v2/capi_v2_internal.hpp" +#include "duckdb/main/capi_v2/capi_v2_function_internal.hpp" #include "duckdb/catalog/catalog.hpp" #include "duckdb/function/aggregate_function.hpp" #include "duckdb/parser/parsed_data/create_aggregate_function_info.hpp" @@ -24,10 +25,7 @@ class CV2AggregateFunctionData final : public FunctionData { class CV2AggregateBindInfo { public: - void *in_user_data = nullptr; BindAggregateFunctionInput *in_input = nullptr; - - duckdb_v2_opaque out_bind_data = {}; }; static auto Convert(duckdb_v2_aggregate_function_bind_info_handle info) -> CV2AggregateBindInfo * { @@ -75,6 +73,8 @@ class CV2AggregateUpdateInfo { Vector *inputs = nullptr; idx_t input_count = 0; + //! Tells the four parts of the argument list apart + const BoundAggregateFunction *function = nullptr; idx_t row_count = 0; void **states = nullptr; }; @@ -160,19 +160,20 @@ static auto GetUserBindData(const FunctionData *bind_data) -> void * { static auto CV2AggregateBind(BindAggregateFunctionInput &input) -> unique_ptr { const auto &info = input.GetBoundFunction().GetExtraFunctionInfo().Cast(); - CV2AggregateBindInfo args = {}; - args.in_user_data = info.user_data ? info.user_data->GetData() : nullptr; - args.in_input = &input; + auto &bound_function = input.GetBoundFunction(); + CV2ExpressionBindInfo bind_info(bound_function, info.user_data ? info.user_data->GetData() : nullptr, input); + CV2AggregateBindInfo result_info = {}; + result_info.in_input = &input; CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.bind_cb(Convert(&args), Convert(&input.GetClientContext()), &err_ptr); + CV2CallbackContext callback_context(input.GetClientContext()); + info.bind_cb(Convert(&bind_info), Convert(&result_info), Convert(&callback_context), &err_ptr); unique_ptr result = nullptr; - if (args.out_bind_data.ptr) { + if (bind_info.out_bind_data) { auto set_result = make_uniq(); - set_result->handle = - make_shared_ptr(args.out_bind_data.ptr, args.out_bind_data.destroy, args.out_bind_data.equals); + set_result->handle = std::move(bind_info.out_bind_data); result = std::move(set_result); } @@ -223,13 +224,20 @@ static auto CV2AggregateUpdate(Vector inputs[], AggregateInputData &aggr_input_d idx_t count) -> void { const auto &info = aggr_input_data.function.GetExtraFunctionInfo().Cast(); + // the callback expects one state per row - flatten a reference, as callers may hold on to the state buffer + auto states = Vector::Ref(state); + if (states.GetVectorType() != VectorType::FLAT_VECTOR) { + states.Flatten(*FlatVector::IncrementalSelectionVector(), count); + } + CV2AggregateUpdateInfo args = {}; args.in_user_data = info.user_data ? info.user_data->GetData() : nullptr; args.in_bind_data = GetUserBindData(aggr_input_data.bind_data.get()); args.inputs = inputs; args.input_count = input_count; + args.function = &aggr_input_data.function; args.row_count = count; - args.states = FlatVector::GetDataMutableUnsafe(state); + args.states = FlatVector::GetDataMutableUnsafe(states); CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); @@ -305,13 +313,15 @@ static auto CV2AggregateDestroy(Vector &state, AggregateInputData &aggr_input_da class CV2AggregateFunction { public: - CV2AggregateFunction() { + explicit CV2AggregateFunction(DatabaseInstance &db) : db(db) { // The callbacks' error slots make failure part of the API contract, so the function defaults to fallible: // a non-fallible function turns any execution error reported through the slot into an internal error. properties.SetFallible(); } - void Register() { + //! Validates the configuration and builds the aggregate function to register on `target`. + AggregateFunction Build(DatabaseInstance &target) { + CheckRegistrationTarget(db, target, "aggregate function"); if (name.empty()) { throw InvalidInputException("Function name cannot be empty."); } @@ -364,53 +374,19 @@ class CV2AggregateFunction { } function.SetExtraFunctionInfo(std::move(info)); - // Call the implementation to register - RegisterToCatalog(std::move(function)); + return function; } - virtual ~CV2AggregateFunction() = default; - virtual void RegisterToCatalog(AggregateFunction function) = 0; - public: + //! The database it was created for: the only one it can be registered on. + DatabaseInstance &db; FunctionSignature signature; + CV2FunctionDocs docs; CV2AggregateFunctionInfo info; Identifier name; AggregateFunctionProperties properties; }; -class CV2ConnectionAggregateFunction : public CV2AggregateFunction { -public: - explicit CV2ConnectionAggregateFunction(Connection &connection) : connection(connection) { - } - - void RegisterToCatalog(AggregateFunction function) override { - auto &context = *connection.context; - - context.RunFunctionInTransaction([&]() { - auto &catalog = Catalog::GetSystemCatalog(context); - CreateAggregateFunctionInfo af_info(std::move(function)); - af_info.on_conflict = OnCreateConflict::ALTER_ON_CONFLICT; - catalog.CreateFunction(context, af_info); - }); - } - -private: - Connection &connection; -}; - -class CV2ExtensionAggregateFunction : public CV2AggregateFunction { -public: - explicit CV2ExtensionAggregateFunction(ExtensionLoader &loader) : loader(loader) { - } - - void RegisterToCatalog(AggregateFunction function) override { - loader.RegisterFunction(std::move(function)); - } - -private: - ExtensionLoader &loader; -}; - static auto Convert(duckdb_v2_aggregate_function_handle func) -> CV2AggregateFunction * { return reinterpret_cast(func); } @@ -426,38 +402,31 @@ static auto Convert(CV2AggregateFunction *func) -> duckdb_v2_aggregate_function_ using namespace duckdb::capiv2; -DUCKDB_V2_ERROR duckdb_v2_aggregate_function_create_with_connection(duckdb_v2_connection_handle connection, - duckdb_v2_aggregate_function_handle *out_function, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(connection); - DUCKDB_CHECK_ARG(out_function); - *out_function = nullptr; +DUCKDB_V2_ERROR duckdb_v2_aggregate_function_create(duckdb_v2_factory_handle factory, + duckdb_v2_aggregate_function_handle *function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); + DUCKDB_CHECK_ARG(function); + *function = nullptr; return WithErrorHandler(err, [&]() { - auto &conn = *Convert(connection); - auto function = duckdb::make_uniq(conn); - *out_function = Convert(function.release()); + auto result = duckdb::make_uniq(Convert(factory)->GetDatabase()); + *function = Convert(result.release()); }); } -DUCKDB_V2_ERROR duckdb_v2_aggregate_function_create_with_extension(duckdb_v2_extension_handle extension, - duckdb_v2_aggregate_function_handle *out_function, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(extension); - DUCKDB_CHECK_ARG(out_function); - *out_function = nullptr; - return WithErrorHandler(err, [&]() { - auto &loader = GetExtensionLoader(extension); - auto function = duckdb::make_uniq(loader); - *out_function = Convert(function.release()); - }); +DUCKDB_V2_ERROR duckdb_v2_aggregate_function_set_name(duckdb_v2_aggregate_function_handle function, + const duckdb_v2_str *name, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + DUCKDB_CHECK_ARG(name); + return WithErrorHandler(err, [&]() { Convert(function)->name = duckdb::Identifier(ConvertIdentifierName(name)); }); } -DUCKDB_V2_ERROR duckdb_v2_aggregate_function_set_name(duckdb_v2_aggregate_function_handle function, duckdb_v2_str *name, +DUCKDB_V2_ERROR duckdb_v2_aggregate_function_get_docs(duckdb_v2_aggregate_function_handle function, + duckdb_v2_function_docs_handle *docs, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(function); - DUCKDB_CHECK_ARG(name); - DUCKDB_CHECK_ARG(*name); - return WithErrorHandler(err, [&]() { Convert(function)->name = duckdb::Identifier(Convert(*name)); }); + DUCKDB_CHECK_ARG(docs); + return WithErrorHandler(err, [&]() { *docs = Convert(&Convert(function)->docs); }); } DUCKDB_V2_ERROR duckdb_v2_aggregate_function_get_signature(duckdb_v2_aggregate_function_handle function, @@ -540,67 +509,15 @@ duckdb_v2_aggregate_function_set_destroy_callback(duckdb_v2_aggregate_function_h return WithErrorHandler(err, [&]() { Convert(function)->info.destroy_cb = callback; }); } -DUCKDB_V2_ERROR duckdb_v2_aggregate_function_bind_get_user_data(duckdb_v2_aggregate_function_bind_info_handle info, - void **data, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(info); - DUCKDB_CHECK_ARG(data); - return WithErrorHandler(err, [&]() { *data = Convert(info)->in_user_data; }); -} - -DUCKDB_V2_ERROR duckdb_v2_aggregate_function_bind_set_bind_data(duckdb_v2_aggregate_function_bind_info_handle info, - duckdb_v2_opaque *data, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(info); - return WithErrorHandler(err, [&]() { Convert(info)->out_bind_data = *data; }); -} - -DUCKDB_V2_ERROR duckdb_v2_aggregate_function_bind_get_arg_count(duckdb_v2_aggregate_function_bind_info_handle info, - idx_t *count, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(info); - DUCKDB_CHECK_ARG(count); - return WithErrorHandler(err, [&]() { *count = Convert(info)->in_input->GetArguments().size(); }); -} - -DUCKDB_V2_ERROR duckdb_v2_aggregate_function_bind_get_arg_type(duckdb_v2_aggregate_function_bind_info_handle info, - idx_t index, duckdb_v2_logical_type_handle *type, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(info); - DUCKDB_CHECK_ARG(type); - *type = nullptr; - return WithErrorHandler(err, [&]() { - const auto &input = *Convert(info)->in_input; - const auto &arguments = input.GetArguments(); - if (index >= arguments.size()) { - throw duckdb::InvalidInputException( - "Index out of bounds in duckdb_v2_aggregate_function_bind_get_arg_type"); - } - *type = Convert(new duckdb::LogicalType(arguments[index]->GetReturnType())); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_aggregate_function_bind_get_arg_value(duckdb_v2_aggregate_function_bind_info_handle info, - idx_t index, duckdb_v2_value_handle *value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(info); - DUCKDB_CHECK_ARG(value); - *value = nullptr; - return WithErrorHandler(err, [&]() { - const auto &input = *Convert(info)->in_input; - if (index >= input.GetArguments().size()) { - throw duckdb::InvalidInputException( - "Index out of bounds in duckdb_v2_aggregate_function_bind_get_arg_value"); - } - *value = Convert(new duckdb::Value(input.GetConstant(index))); - }); -} - DUCKDB_V2_ERROR duckdb_v2_aggregate_function_bind_set_return_type(duckdb_v2_aggregate_function_bind_info_handle info, duckdb_v2_logical_type_handle return_type, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(info); DUCKDB_CHECK_ARG(return_type); - return WithErrorHandler( - err, [&]() { Convert(info)->in_input->GetBoundFunction().SetReturnType(*Convert(return_type)); }); + return WithErrorHandler(err, [&]() { + auto type = Convert(return_type); + Convert(info)->in_input->GetBoundFunction().SetReturnType(*type); + }); } DUCKDB_V2_ERROR duckdb_v2_aggregate_function_size_get_user_data(duckdb_v2_aggregate_function_size_info_handle info, @@ -673,14 +590,29 @@ DUCKDB_V2_ERROR duckdb_v2_aggregate_function_update_get_row_count(duckdb_v2_aggr } DUCKDB_V2_ERROR duckdb_v2_aggregate_function_update_get_arg_count(duckdb_v2_aggregate_function_update_info_handle info, - uint32_t *count, duckdb_v2_error_info_handle *err) { + idx_t *positional_fixed, idx_t *positional_variadic, + idx_t *named_fixed, idx_t *named_variadic, + duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(info); - DUCKDB_CHECK_ARG(count); - return WithErrorHandler(err, [&]() { *count = duckdb::NumericCast(Convert(info)->input_count); }); + return WithErrorHandler(err, [&]() { + auto &function = *Convert(info)->function; + if (positional_fixed) { + *positional_fixed = function.GetStandardArgumentCount(); + } + if (positional_variadic) { + *positional_variadic = function.GetVarArgsCount(); + } + if (named_fixed) { + *named_fixed = function.GetKeywordOnlyArgumentCount(); + } + if (named_variadic) { + *named_variadic = function.GetKwargsCount(); + } + }); } DUCKDB_V2_ERROR duckdb_v2_aggregate_function_update_get_arg(duckdb_v2_aggregate_function_update_info_handle info, - uint32_t index, duckdb_v2_vector_handle *vector, + idx_t index, duckdb_v2_vector_handle *vector, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(info); DUCKDB_CHECK_ARG(vector); @@ -817,10 +749,37 @@ DUCKDB_V2_ERROR duckdb_v2_aggregate_function_destroy_get_states(duckdb_v2_aggreg return WithErrorHandler(err, [&]() { *states = Convert(info)->states; }); } -DUCKDB_V2_ERROR duckdb_v2_aggregate_function_register(duckdb_v2_aggregate_function_handle function, - duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR duckdb_v2_connection_register_aggregate_function(duckdb_v2_connection_handle conn, + duckdb_v2_aggregate_function_handle function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(conn); DUCKDB_CHECK_ARG(function); - return WithErrorHandler(err, [&]() { Convert(function)->Register(); }); + return WithErrorHandler(err, [&]() { + auto &context = *Convert(conn)->context; + context.RunFunctionInTransaction([&]() { + auto &self = *Convert(function); + auto info = duckdb::CreateAggregateFunctionInfo(self.Build(*context.db)); + self.docs.AddTo(self.signature, info); + info.on_conflict = duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + auto &catalog = duckdb::Catalog::GetSystemCatalog(context); + catalog.CreateFunction(context, info); + }); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_extension_register_aggregate_function(duckdb_v2_extension_handle extension, + duckdb_v2_aggregate_function_handle function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(extension); + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { + auto &self = *Convert(function); + auto &loader = GetExtensionLoader(extension); + duckdb::CreateAggregateFunctionInfo info(self.Build(loader.GetDatabaseInstance())); + self.docs.AddTo(self.signature, info); + info.on_conflict = duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + loader.RegisterFunction(std::move(info)); + }); } DUCKDB_V2_ERROR duckdb_v2_aggregate_function_destroy(duckdb_v2_aggregate_function_handle *function) { diff --git a/src/duckdb/src/main/capi/v2/capi_v2_func_cast.cpp b/src/duckdb/src/main/capi/v2/capi_v2_func_cast.cpp index be0173551..60dc596a4 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_func_cast.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_func_cast.cpp @@ -23,7 +23,7 @@ class CV2CastBoundData final : public BoundCastData { // one. The state exists purely to forward it to the exec callback. class CV2CastLocalState final : public FunctionLocalState { public: - optional_ptr context; + unique_ptr context; }; class CV2CastExecInfo { @@ -50,7 +50,7 @@ static auto CV2CastInitLocalState(CastLocalStateParameters ¶meters) -> uniqu throw InvalidInputException("Cannot initialize local state for extension cast function without a context"); } - state->context = parameters.context; + state->context = make_uniq(*parameters.context); return std::move(state); } @@ -67,9 +67,9 @@ static auto CV2CastExec(Vector &input, Vector &output, idx_t count, CastParamete args.count = count; args.mode = is_try_cast ? DUCKDB_V2_CAST_MODE_TRY : DUCKDB_V2_CAST_MODE_NORMAL; - optional_ptr context = nullptr; + optional_ptr context = nullptr; if (parameters.local_state) { - context = parameters.local_state->Cast().context; + context = parameters.local_state->Cast().context.get(); } CV2ErrorInfo err = {}; @@ -89,7 +89,12 @@ static auto CV2CastExec(Vector &input, Vector &output, idx_t count, CastParamete class CV2CastFunction { public: - void Register() { + explicit CV2CastFunction(DatabaseInstance &db) : db(db) { + } + + //! Validates the configuration and builds the cast to register on `target`. + BoundCastInfo Build(DatabaseInstance &target) { + CheckRegistrationTarget(db, target, "cast function"); if (!source_type.IsComplete()) { throw InvalidInputException("Source type must be set to a fully defined concrete type"); } @@ -101,13 +106,12 @@ class CV2CastFunction { } BoundCastInfo cast_info(CV2CastExec, make_uniq(exec_cb, user_data), CV2CastInitLocalState); - RegisterToCatalog(std::move(cast_info)); + return cast_info; } - virtual ~CV2CastFunction() = default; - virtual void RegisterToCatalog(BoundCastInfo cast_info) = 0; - public: + //! The database it was created for: the only one it can be registered on. + DatabaseInstance &db; LogicalType source_type; LogicalType target_type; int64_t implicit_cast_cost = -1; @@ -115,37 +119,6 @@ class CV2CastFunction { shared_ptr user_data = nullptr; }; -class CV2ConnectionCastFunction : public CV2CastFunction { -public: - explicit CV2ConnectionCastFunction(Connection &connection) : connection(connection) { - } - - void RegisterToCatalog(BoundCastInfo cast_info) override { - auto &context = *connection.context; - - context.RunFunctionInTransaction([&]() { - auto &casts = CastFunctionSet::Get(context); - casts.RegisterCastFunction(source_type, target_type, std::move(cast_info), implicit_cast_cost); - }); - } - -private: - Connection &connection; -}; - -class CV2ExtensionCastFunction : public CV2CastFunction { -public: - explicit CV2ExtensionCastFunction(ExtensionLoader &loader) : loader(loader) { - } - - void RegisterToCatalog(BoundCastInfo cast_info) override { - loader.RegisterCastFunction(source_type, target_type, std::move(cast_info), implicit_cast_cost); - } - -private: - ExtensionLoader &loader; -}; - static auto Convert(duckdb_v2_cast_function_handle func) -> CV2CastFunction * { return reinterpret_cast(func); } @@ -161,29 +134,15 @@ static auto Convert(CV2CastFunction *func) -> duckdb_v2_cast_function_handle { using namespace duckdb::capiv2; -DUCKDB_V2_ERROR duckdb_v2_cast_function_create_with_connection(duckdb_v2_connection_handle connection, - duckdb_v2_cast_function_handle *out_function, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(connection); - DUCKDB_CHECK_ARG(out_function); - *out_function = nullptr; - return WithErrorHandler(err, [&]() { - auto &conn = *Convert(connection); - auto function = duckdb::make_uniq(conn); - *out_function = Convert(function.release()); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_cast_function_create_with_extension(duckdb_v2_extension_handle extension, - duckdb_v2_cast_function_handle *out_function, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(extension); - DUCKDB_CHECK_ARG(out_function); - *out_function = nullptr; +DUCKDB_V2_ERROR duckdb_v2_cast_function_create(duckdb_v2_factory_handle factory, + duckdb_v2_cast_function_handle *function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); + DUCKDB_CHECK_ARG(function); + *function = nullptr; return WithErrorHandler(err, [&]() { - auto &loader = GetExtensionLoader(extension); - auto function = duckdb::make_uniq(loader); - *out_function = Convert(function.release()); + auto result = duckdb::make_uniq(Convert(factory)->GetDatabase()); + *function = Convert(result.release()); }); } @@ -192,7 +151,10 @@ DUCKDB_V2_ERROR duckdb_v2_cast_function_set_source_type(duckdb_v2_cast_function_ duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(function); DUCKDB_CHECK_ARG(source_type); - return WithErrorHandler(err, [&]() { Convert(function)->source_type = *Convert(source_type); }); + return WithErrorHandler(err, [&]() { + auto type = Convert(source_type); + Convert(function)->source_type = *type; + }); } DUCKDB_V2_ERROR duckdb_v2_cast_function_set_target_type(duckdb_v2_cast_function_handle function, @@ -200,7 +162,10 @@ DUCKDB_V2_ERROR duckdb_v2_cast_function_set_target_type(duckdb_v2_cast_function_ duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(function); DUCKDB_CHECK_ARG(target_type); - return WithErrorHandler(err, [&]() { Convert(function)->target_type = *Convert(target_type); }); + return WithErrorHandler(err, [&]() { + auto type = Convert(target_type); + Convert(function)->target_type = *type; + }); } DUCKDB_V2_ERROR duckdb_v2_cast_function_set_implicit_cast_cost(duckdb_v2_cast_function_handle function, int64_t cost, @@ -263,10 +228,31 @@ DUCKDB_V2_ERROR duckdb_v2_cast_function_exec_get_mode(duckdb_v2_cast_function_ex return WithErrorHandler(err, [&]() { *mode = Convert(info)->mode; }); } -DUCKDB_V2_ERROR duckdb_v2_cast_function_register(duckdb_v2_cast_function_handle function, - duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR duckdb_v2_connection_register_cast_function(duckdb_v2_connection_handle conn, + duckdb_v2_cast_function_handle function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(conn); DUCKDB_CHECK_ARG(function); - return WithErrorHandler(err, [&]() { Convert(function)->Register(); }); + return WithErrorHandler(err, [&]() { + auto &self = *Convert(function); + auto &db = *Convert(conn)->context->db; + auto cast_info = self.Build(db); + duckdb::DBConfig::GetConfig(db).GetCastFunctions().RegisterCastFunction( + self.source_type, self.target_type, std::move(cast_info), self.implicit_cast_cost); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_extension_register_cast_function(duckdb_v2_extension_handle extension, + duckdb_v2_cast_function_handle function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(extension); + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { + auto &self = *Convert(function); + auto &loader = GetExtensionLoader(extension); + auto cast_info = self.Build(loader.GetDatabaseInstance()); + loader.RegisterCastFunction(self.source_type, self.target_type, std::move(cast_info), self.implicit_cast_cost); + }); } DUCKDB_V2_ERROR duckdb_v2_cast_function_destroy(duckdb_v2_cast_function_handle *function) { diff --git a/src/duckdb/src/main/capi/v2/capi_v2_func_copy.cpp b/src/duckdb/src/main/capi/v2/capi_v2_func_copy.cpp index f92c578ea..9f02cd3ec 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_func_copy.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_func_copy.cpp @@ -20,6 +20,7 @@ class CV2CopyFunctionInfo final : public CopyFunctionInfo { duckdb_v2_copy_to_batch_callback_fn to_batch_cb = nullptr; duckdb_v2_copy_to_flush_callback_fn to_flush_cb = nullptr; duckdb_v2_copy_to_finalize_callback_fn to_finalize_cb = nullptr; + duckdb_v2_copy_to_statistics_callback_fn to_statistics_cb = nullptr; // COPY ... FROM duckdb_v2_copy_from_bind_callback_fn from_bind_cb = nullptr; duckdb_v2_copy_from_init_global_callback_fn from_init_global_cb = nullptr; @@ -30,7 +31,8 @@ class CV2CopyFunctionInfo final : public CopyFunctionInfo { shared_ptr user_data = nullptr; bool HasCopyTo() const { - return to_bind_cb || to_batch_size_cb || to_init_cb || to_batch_cb || to_flush_cb || to_finalize_cb; + return to_bind_cb || to_batch_size_cb || to_init_cb || to_batch_cb || to_flush_cb || to_finalize_cb || + to_statistics_cb; } bool HasCopyFrom() const { return from_bind_cb || from_init_global_cb || from_init_local_cb || from_exec_cb || from_progress_cb; @@ -78,8 +80,15 @@ class CV2CopyFunctionData final : public FunctionData { class CV2CopyToGlobalState final : public GlobalFunctionData { public: CV2UserData handle; + //! Where the statistics of the file are reported, when the statement asks for them + optional_ptr written_statistics; + //! Serializes the flushes of callers that sink into the file directly (see CV2CopyToSink) + mutex flush_lock; }; +//! The local state of callers that sink into a file directly rather than through the batch interface +class CV2CopyToLocalState final : public LocalFunctionData {}; + class CV2CopyToBatchData final : public PreparedBatchData { public: CV2UserData handle; @@ -129,7 +138,7 @@ class CV2CopyToBindInfo { const vector *in_types = nullptr; CV2CopyOptionList in_options; - duckdb_v2_opaque out_bind_data = {}; + shared_ptr out_bind_data; }; static auto Convert(duckdb_v2_copy_to_bind_info_handle info) -> CV2CopyToBindInfo * { @@ -217,6 +226,23 @@ static auto Convert(CV2CopyToFinalizeInfo *info) -> duckdb_v2_copy_to_finalize_i return reinterpret_cast(info); } +class CV2CopyToStatisticsInfo { +public: + void *in_user_data = nullptr; + void *in_bind_data = nullptr; + void *in_init_data = nullptr; + + idx_t out_row_count = 0; + idx_t out_file_size_bytes = 0; +}; + +static auto Convert(duckdb_v2_copy_to_statistics_info_handle info) -> CV2CopyToStatisticsInfo * { + return reinterpret_cast(info); +} +static auto Convert(CV2CopyToStatisticsInfo *info) -> duckdb_v2_copy_to_statistics_info_handle { + return reinterpret_cast(info); +} + //---------------------------------------------------------------------------------------------------------------------- // COPY FROM callback info //---------------------------------------------------------------------------------------------------------------------- @@ -229,7 +255,7 @@ class CV2CopyFromBindInfo { const vector *in_types = nullptr; CV2CopyOptionList in_options; - duckdb_v2_opaque out_bind_data = {}; + shared_ptr out_bind_data; idx_t out_cardinality = 0; bool out_cardinality_is_exact = false; bool out_cardinality_set = false; @@ -324,14 +350,11 @@ static auto GetUserBindData(const FunctionData &bind_data) -> void * { return handle ? handle->GetData() : nullptr; } -//! Takes ownership of whatever bind data the callback set, so it is destroyed even when the callback failed. -static auto MakeBindData(const shared_ptr &info, const duckdb_v2_opaque &out_bind_data) +static auto MakeBindData(const shared_ptr &info, shared_ptr handle) -> unique_ptr { auto result = make_uniq(); result->info = info; - if (out_bind_data.ptr) { - result->handle = make_shared_ptr(out_bind_data.ptr, out_bind_data.destroy, out_bind_data.equals); - } + result->handle = std::move(handle); return result; } @@ -341,6 +364,11 @@ static auto MakeBindData(const shared_ptr &info, const duckdb_ static auto CV2CopyToBind(ClientContext &context, CopyFunctionBindInput &input, const vector &names, const vector &sql_types) -> unique_ptr { + if (!input.function_info) { + // the bind reaches the callbacks through the function info, which a caller that binds the copy function + // itself has to pass along + throw InvalidInputException("The COPY TO bind of a copy function was called without its function info"); + } const auto &info = input.function_info->Cast(); CV2CopyToBindInfo args = {}; @@ -354,10 +382,11 @@ static auto CV2CopyToBind(ClientContext &context, CopyFunctionBindInput &input, CV2ErrorInfo err = {}; if (info.to_bind_cb) { auto err_ptr = Convert(&err); - info.to_bind_cb(Convert(&args), Convert(&context), &err_ptr); + CV2CallbackContext callback_context(context); + info.to_bind_cb(Convert(&args), Convert(&callback_context), &err_ptr); } - auto result = MakeBindData(input.function_info, args.out_bind_data); + auto result = MakeBindData(input.function_info, std::move(args.out_bind_data)); if (err.HasError()) { err.ThrowAsException(); } @@ -373,7 +402,8 @@ static auto CV2CopyToBatchSize(ClientContext &context, FunctionData &bind_data) CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.to_batch_size_cb(Convert(&args), Convert(&context), &err_ptr); + CV2CallbackContext callback_context(context); + info.to_batch_size_cb(Convert(&args), Convert(&callback_context), &err_ptr); if (err.HasError()) { err.ThrowAsException(); @@ -397,7 +427,8 @@ static auto CV2CopyToInitGlobal(ClientContext &context, FunctionData &bind_data, CV2ErrorInfo err = {}; if (info.to_init_cb) { auto err_ptr = Convert(&err); - info.to_init_cb(Convert(&args), Convert(&context), &err_ptr); + CV2CallbackContext callback_context(context); + info.to_init_cb(Convert(&args), Convert(&callback_context), &err_ptr); } auto result = make_uniq(); @@ -424,7 +455,8 @@ static auto CV2CopyToPrepareBatch(ClientContext &context, FunctionData &bind_dat CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.to_batch_cb(Convert(&args), Convert(&context), &err_ptr); + CV2CallbackContext callback_context(context); + info.to_batch_cb(Convert(&args), Convert(&callback_context), &err_ptr); auto result = make_uniq(); if (args.out_batch_data.ptr) { @@ -450,33 +482,92 @@ static auto CV2CopyToFlushBatch(ClientContext &context, FunctionData &bind_data, CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.to_flush_cb(Convert(&args), Convert(&context), &err_ptr); + CV2CallbackContext callback_context(context); + info.to_flush_cb(Convert(&args), Convert(&callback_context), &err_ptr); if (err.HasError()) { err.ThrowAsException(); } } -static auto CV2CopyToFinalize(ClientContext &context, FunctionData &bind_data, GlobalFunctionData &gstate) -> void { +static auto CV2CopyToReportStatistics(ClientContext &context, FunctionData &bind_data, CV2CopyToGlobalState &gstate) + -> void { const auto &info = GetFunctionInfo(bind_data); - // Always wired, as the engine requires a finalize hook; the user's callback is optional. - if (!info.to_finalize_cb) { - return; - } - - CV2CopyToFinalizeInfo args = {}; + CV2CopyToStatisticsInfo args = {}; args.in_user_data = GetUserData(info); args.in_bind_data = GetUserBindData(bind_data); - args.in_init_data = gstate.Cast().handle.GetData(); + args.in_init_data = gstate.handle.GetData(); CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.to_finalize_cb(Convert(&args), Convert(&context), &err_ptr); + CV2CallbackContext callback_context(context); + info.to_statistics_cb(Convert(&args), Convert(&callback_context), &err_ptr); if (err.HasError()) { err.ThrowAsException(); } + gstate.written_statistics->row_count = args.out_row_count; + gstate.written_statistics->file_size_bytes = args.out_file_size_bytes; +} + +static auto CV2CopyToFinalize(ClientContext &context, FunctionData &bind_data, GlobalFunctionData &gstate_p) -> void { + const auto &info = GetFunctionInfo(bind_data); + auto &gstate = gstate_p.Cast(); + + // Always wired, as the engine requires a finalize hook; the user's callback is optional. + if (info.to_finalize_cb) { + CV2CopyToFinalizeInfo args = {}; + args.in_user_data = GetUserData(info); + args.in_bind_data = GetUserBindData(bind_data); + args.in_init_data = gstate.handle.GetData(); + + CV2ErrorInfo err = {}; + auto err_ptr = Convert(&err); + CV2CallbackContext callback_context(context); + info.to_finalize_cb(Convert(&args), Convert(&callback_context), &err_ptr); + + if (err.HasError()) { + err.ThrowAsException(); + } + } + // the statistics describe the finished file, so they are reported once it is finalized + if (gstate.written_statistics) { + CV2CopyToReportStatistics(context, bind_data, gstate); + } +} + +//! The engine asks where to report the statistics of a file right after creating it, before anything is written to it +static auto CV2CopyToGetWrittenStatistics(ClientContext &context, FunctionData &bind_data, GlobalFunctionData &gstate, + CopyFunctionFileStatistics &statistics) -> void { + gstate.Cast().written_statistics = &statistics; +} + +//---------------------------------------------------------------------------------------------------------------------- +// COPY TO sink interface +//---------------------------------------------------------------------------------------------------------------------- +// COPY TO itself drives the function through its batch interface. These hooks serve callers that write a file through +// the copy function directly, sinking chunks into it one at a time - every chunk is written as a batch of its own. + +static auto CV2CopyToInitLocal(ExecutionContext &context, FunctionData &bind_data) -> unique_ptr { + return make_uniq(); +} + +static auto CV2CopyToSink(ExecutionContext &context, FunctionData &bind_data, GlobalFunctionData &gstate, + LocalFunctionData &lstate, DataChunk &input) -> void { + if (input.size() == 0) { + return; + } + auto batch = make_uniq(context.client, input.GetTypes()); + batch->Append(input); + auto prepared = CV2CopyToPrepareBatch(context.client, bind_data, gstate, std::move(batch)); + // batches are never flushed concurrently for the same file + lock_guard guard(gstate.Cast().flush_lock); + CV2CopyToFlushBatch(context.client, bind_data, gstate, *prepared); +} + +static auto CV2CopyToCombine(ExecutionContext &context, FunctionData &bind_data, GlobalFunctionData &gstate, + LocalFunctionData &lstate) -> void { } //---------------------------------------------------------------------------------------------------------------------- @@ -498,9 +589,10 @@ static auto CV2CopyFromBind(ClientContext &context, CopyFromFunctionBindInput &i CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.from_bind_cb(Convert(&args), Convert(&context), &err_ptr); + CV2CallbackContext callback_context(context); + info.from_bind_cb(Convert(&args), Convert(&callback_context), &err_ptr); - auto result = MakeBindData(function_info, args.out_bind_data); + auto result = MakeBindData(function_info, std::move(args.out_bind_data)); result->cardinality = args.out_cardinality; result->cardinality_is_exact = args.out_cardinality_is_exact; result->cardinality_set = args.out_cardinality_set; @@ -527,7 +619,8 @@ static auto CV2CopyFromInitGlobal(ClientContext &context, TableFunctionInitInput CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.from_init_global_cb(Convert(&args), Convert(&context), &err_ptr); + CV2CallbackContext callback_context(context); + info.from_init_global_cb(Convert(&args), Convert(&callback_context), &err_ptr); if (args.out_global_state.ptr) { result->handle = @@ -561,7 +654,8 @@ static auto CV2CopyFromInitLocal(ExecutionContext &context, TableFunctionInitInp CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.from_init_local_cb(Convert(&args), Convert(&context.client), &err_ptr); + CV2CallbackContext callback_context(context.client); + info.from_init_local_cb(Convert(&args), Convert(&callback_context), &err_ptr); unique_ptr result = nullptr; if (args.out_local_state.ptr) { @@ -596,7 +690,8 @@ static auto CV2CopyFromExec(ClientContext &context, TableFunctionInput &input, D CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.from_exec_cb(Convert(&args), Convert(&context), &err_ptr); + CV2CallbackContext callback_context(context); + info.from_exec_cb(Convert(&args), Convert(&callback_context), &err_ptr); if (err.HasError()) { err.ThrowAsException(); @@ -636,7 +731,8 @@ static auto CV2CopyFromProgress(ClientContext &context, const FunctionData *bind CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.from_progress_cb(Convert(&args), Convert(&context), &err_ptr); + CV2CallbackContext callback_context(context); + info.from_progress_cb(Convert(&args), Convert(&callback_context), &err_ptr); if (err.HasError()) { err.ThrowAsException(); @@ -652,7 +748,12 @@ static auto CV2CopyFromProgress(ClientContext &context, const FunctionData *bind class CV2CopyFunction { public: - void Register() { + explicit CV2CopyFunction(DatabaseInstance &db) : db(db) { + } + + //! Validates the configuration and builds the copy function to register on `target`. + CopyFunction Build(DatabaseInstance &target) { + CheckRegistrationTarget(db, target, "copy function"); if (name.empty()) { throw InvalidInputException("Function name cannot be empty."); } @@ -688,9 +789,15 @@ class CV2CopyFunction { function.prepare_batch = CV2CopyToPrepareBatch; function.flush_batch = CV2CopyToFlushBatch; function.copy_to_finalize = CV2CopyToFinalize; + function.copy_to_initialize_local = CV2CopyToInitLocal; + function.copy_to_sink = CV2CopyToSink; + function.copy_to_combine = CV2CopyToCombine; if (function_info->to_batch_size_cb) { function.desired_batch_size = CV2CopyToBatchSize; } + if (function_info->to_statistics_cb) { + function.copy_to_get_written_statistics = CV2CopyToGetWrittenStatistics; + } } if (has_copy_from) { // The engine reads through a table function whose own bind is skipped in favour of copy_from_bind. @@ -705,50 +812,17 @@ class CV2CopyFunction { function.copy_from_function = std::move(reader); } function.function_info = std::move(function_info); + function.extension = extension; - // Call the implementation to register - RegisterToCatalog(std::move(function)); + return function; } - virtual ~CV2CopyFunction() = default; - virtual void RegisterToCatalog(CopyFunction function) = 0; - public: + //! The database it was created for: the only one it can be registered on. + DatabaseInstance &db; CV2CopyFunctionInfo info; Identifier name; -}; - -class CV2ConnectionCopyFunction : public CV2CopyFunction { -public: - explicit CV2ConnectionCopyFunction(Connection &connection) : connection(connection) { - } - - void RegisterToCatalog(CopyFunction function) override { - auto &context = *connection.context; - - context.RunFunctionInTransaction([&]() { - auto &catalog = Catalog::GetSystemCatalog(context); - CreateCopyFunctionInfo cf_info(std::move(function)); - cf_info.on_conflict = OnCreateConflict::ALTER_ON_CONFLICT; - catalog.CreateCopyFunction(context, cf_info); - }); - } - -private: - Connection &connection; -}; - -class CV2ExtensionCopyFunction : public CV2CopyFunction { -public: - explicit CV2ExtensionCopyFunction(ExtensionLoader &loader) : loader(loader) { - } - - void RegisterToCatalog(CopyFunction function) override { - loader.RegisterFunction(std::move(function)); - } - -private: - ExtensionLoader &loader; + string extension; }; static auto Convert(duckdb_v2_copy_function_handle func) -> CV2CopyFunction * { @@ -765,7 +839,7 @@ static auto GetColumnType(INFO &args, idx_t index, const char *function) -> duck if (index >= types.size()) { throw InvalidInputException("Index out of bounds in %s", function); } - return Convert(new LogicalType(types[index])); + return Convert(types[index]); } template @@ -811,38 +885,38 @@ static auto GetOptionValue(INFO &args, idx_t index, const char *function) -> duc using namespace duckdb::capiv2; -DUCKDB_V2_ERROR duckdb_v2_copy_function_create_with_connection(duckdb_v2_connection_handle connection, - duckdb_v2_copy_function_handle *function, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(connection); +DUCKDB_V2_ERROR duckdb_v2_copy_function_create(duckdb_v2_factory_handle factory, + duckdb_v2_copy_function_handle *function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(function); *function = nullptr; return WithErrorHandler(err, [&]() { - auto &conn = *Convert(connection); - auto result = duckdb::make_uniq(conn); + auto result = duckdb::make_uniq(Convert(factory)->GetDatabase()); *function = Convert(result.release()); }); } -DUCKDB_V2_ERROR duckdb_v2_copy_function_create_with_extension(duckdb_v2_extension_handle extension, - duckdb_v2_copy_function_handle *function, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(extension); +DUCKDB_V2_ERROR duckdb_v2_copy_function_set_name(duckdb_v2_copy_function_handle function, const duckdb_v2_str *name, + duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(function); - *function = nullptr; - return WithErrorHandler(err, [&]() { - auto &loader = GetExtensionLoader(extension); - auto result = duckdb::make_uniq(loader); - *function = Convert(result.release()); - }); + DUCKDB_CHECK_ARG(name); + return WithErrorHandler(err, [&]() { Convert(function)->name = duckdb::Identifier(ConvertIdentifierName(name)); }); } -DUCKDB_V2_ERROR duckdb_v2_copy_function_set_name(duckdb_v2_copy_function_handle function, duckdb_v2_str *name, - duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR duckdb_v2_copy_function_set_file_extension(duckdb_v2_copy_function_handle function, + const duckdb_v2_str *extension, + duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(function); - DUCKDB_CHECK_ARG(name); - DUCKDB_CHECK_ARG(*name); - return WithErrorHandler(err, [&]() { Convert(function)->name = duckdb::Identifier(Convert(*name)); }); + DUCKDB_CHECK_ARG(extension); + return WithErrorHandler(err, [&]() { + duckdb::string value(extension->ptr, extension->len); + if (!value.empty() && value[0] == '.') { + throw duckdb::InvalidInputException("The file extension of a copy function is set without its leading dot, " + "e.g. \"parquet\" rather than \".parquet\"."); + } + Convert(function)->extension = std::move(value); + }); } DUCKDB_V2_ERROR duckdb_v2_copy_function_set_user_data(duckdb_v2_copy_function_handle function, duckdb_v2_opaque *data, @@ -916,7 +990,10 @@ DUCKDB_V2_ERROR duckdb_v2_copy_to_bind_set_bind_data(duckdb_v2_copy_to_bind_info duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(info); DUCKDB_CHECK_ARG(data); - return WithErrorHandler(err, [&]() { Convert(info)->out_bind_data = *data; }); + return WithErrorHandler(err, [&]() { + Convert(info)->out_bind_data = + data->ptr ? duckdb::make_shared_ptr(data->ptr, data->destroy, data->equals) : nullptr; + }); } DUCKDB_V2_ERROR duckdb_v2_copy_to_bind_get_file_path(duckdb_v2_copy_to_bind_info_handle info, duckdb_v2_str *path, @@ -1191,7 +1268,10 @@ DUCKDB_V2_ERROR duckdb_v2_copy_from_bind_set_bind_data(duckdb_v2_copy_from_bind_ duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(info); DUCKDB_CHECK_ARG(data); - return WithErrorHandler(err, [&]() { Convert(info)->out_bind_data = *data; }); + return WithErrorHandler(err, [&]() { + Convert(info)->out_bind_data = + data->ptr ? duckdb::make_shared_ptr(data->ptr, data->destroy, data->equals) : nullptr; + }); } DUCKDB_V2_ERROR duckdb_v2_copy_from_bind_get_file_path(duckdb_v2_copy_from_bind_info_handle info, duckdb_v2_str *path, @@ -1410,10 +1490,31 @@ DUCKDB_V2_ERROR duckdb_v2_copy_from_progress_set_progress(duckdb_v2_copy_from_pr // Register / destroy //---------------------------------------------------------------------------------------------------------------------- -DUCKDB_V2_ERROR duckdb_v2_copy_function_register(duckdb_v2_copy_function_handle function, - duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR duckdb_v2_connection_register_copy_function(duckdb_v2_connection_handle conn, + duckdb_v2_copy_function_handle function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(conn); + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { + auto &context = *Convert(conn)->context; + context.RunFunctionInTransaction([&]() { + auto info = duckdb::CreateCopyFunctionInfo(Convert(function)->Build(*context.db)); + info.on_conflict = duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + auto &catalog = duckdb::Catalog::GetSystemCatalog(context); + catalog.CreateCopyFunction(context, info); + }); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_extension_register_copy_function(duckdb_v2_extension_handle extension, + duckdb_v2_copy_function_handle function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(extension); DUCKDB_CHECK_ARG(function); - return WithErrorHandler(err, [&]() { Convert(function)->Register(); }); + return WithErrorHandler(err, [&]() { + auto &loader = GetExtensionLoader(extension); + loader.RegisterFunction(Convert(function)->Build(loader.GetDatabaseInstance())); + }); } DUCKDB_V2_ERROR duckdb_v2_copy_function_destroy(duckdb_v2_copy_function_handle *function) { @@ -1427,3 +1528,47 @@ DUCKDB_V2_ERROR duckdb_v2_copy_function_destroy(duckdb_v2_copy_function_handle * } }); } + +//---------------------------------------------------------------------------------------------------------------------- +// COPY TO Statistics +//---------------------------------------------------------------------------------------------------------------------- + +DUCKDB_V2_ERROR duckdb_v2_copy_to_set_statistics_callback(duckdb_v2_copy_function_handle function, + duckdb_v2_copy_to_statistics_callback_fn callback, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { Convert(function)->info.to_statistics_cb = callback; }); +} + +DUCKDB_V2_ERROR duckdb_v2_copy_to_statistics_get_user_data(duckdb_v2_copy_to_statistics_info_handle info, void **data, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_user_data; }); +} + +DUCKDB_V2_ERROR duckdb_v2_copy_to_statistics_get_bind_data(duckdb_v2_copy_to_statistics_info_handle info, void **data, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_bind_data; }); +} + +DUCKDB_V2_ERROR duckdb_v2_copy_to_statistics_get_init_data(duckdb_v2_copy_to_statistics_info_handle info, void **data, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_init_data; }); +} + +DUCKDB_V2_ERROR duckdb_v2_copy_to_statistics_set_row_count(duckdb_v2_copy_to_statistics_info_handle info, + idx_t row_count, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + return WithErrorHandler(err, [&]() { Convert(info)->out_row_count = row_count; }); +} + +DUCKDB_V2_ERROR duckdb_v2_copy_to_statistics_set_file_size(duckdb_v2_copy_to_statistics_info_handle info, + idx_t file_size_bytes, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + return WithErrorHandler(err, [&]() { Convert(info)->out_file_size_bytes = file_size_bytes; }); +} diff --git a/src/duckdb/src/main/capi/v2/capi_v2_func_docs.cpp b/src/duckdb/src/main/capi/v2/capi_v2_func_docs.cpp new file mode 100644 index 000000000..cc3f07cfa --- /dev/null +++ b/src/duckdb/src/main/capi/v2/capi_v2_func_docs.cpp @@ -0,0 +1,35 @@ +#include "duckdb/main/capi_v2/capi_v2_function_internal.hpp" + +//---------------------------------------------------------------------------------------------------------------------- +// Public Functions +//---------------------------------------------------------------------------------------------------------------------- + +using namespace duckdb::capiv2; + +DUCKDB_V2_ERROR duckdb_v2_function_docs_set_description(duckdb_v2_function_docs_handle docs, + const duckdb_v2_str *description, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(docs); + DUCKDB_CHECK_ARG(description); + return WithErrorHandler(err, [&]() { Convert(docs)->description = duckdb::string(Convert(description)); }); +} + +DUCKDB_V2_ERROR duckdb_v2_function_docs_add_example(duckdb_v2_function_docs_handle docs, const duckdb_v2_str *title, + const duckdb_v2_str *example, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(docs); + if (title) { + DUCKDB_CHECK_ARG(title); + } + DUCKDB_CHECK_ARG(example); + return WithErrorHandler(err, [&]() { + duckdb::string title_text = title ? duckdb::string(Convert(title)) : duckdb::string(); + Convert(docs)->examples.push_back({std::move(title_text), duckdb::string(Convert(example))}); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_function_docs_add_category(duckdb_v2_function_docs_handle docs, const duckdb_v2_str *category, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(docs); + DUCKDB_CHECK_ARG(category); + return WithErrorHandler(err, [&]() { Convert(docs)->categories.emplace_back(Convert(category)); }); +} diff --git a/src/duckdb/src/main/capi/v2/capi_v2_func_macro.cpp b/src/duckdb/src/main/capi/v2/capi_v2_func_macro.cpp new file mode 100644 index 000000000..098cdef4b --- /dev/null +++ b/src/duckdb/src/main/capi/v2/capi_v2_func_macro.cpp @@ -0,0 +1,198 @@ +#include "duckdb/main/capi_v2/capi_v2_internal.hpp" +#include "duckdb/main/capi_v2/capi_v2_function_internal.hpp" +#include "duckdb/function/scalar_macro_function.hpp" +#include "duckdb/function/table_macro_function.hpp" +#include "duckdb/parser/expression/columnref_expression.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/parser/parsed_data/create_macro_info.hpp" +#include "duckdb/parser/parser.hpp" +#include "duckdb/parser/statement/select_statement.hpp" + +namespace duckdb::capiv2 { + +class CV2MacroFunction { +public: + CV2MacroFunction(DatabaseInstance &db, MacroType type) : db(db), type(type) { + } + + //! Validates the configuration and builds the macro to register on `target`. + unique_ptr Build(DatabaseInstance &target) { + CheckRegistrationTarget(db, target, "macro function"); + if (name.empty()) { + throw InvalidInputException("Function name cannot be empty."); + } + if (body.empty()) { + throw InvalidInputException("A body must be set for the macro function."); + } + if (signature.GetReturnType().id() != LogicalTypeId::INVALID) { + throw InvalidInputException( + "A macro function signature cannot set a return type: the result follows from the body."); + } + if (signature.GetTypedKwargs()) { + throw InvalidInputException("A macro function cannot have variadic parameters."); + } + signature.Verify(); + + auto macro = ParseBody(); + for (idx_t i = 0; i < signature.GetParameterCount(); i++) { + auto ¶meter = signature.GetParameter(i); + switch (parameter.GetKind()) { + case FunctionParameterKind::STANDARD: + break; + case FunctionParameterKind::VAR_POSITIONAL: + case FunctionParameterKind::VAR_KEYWORD: + throw InvalidInputException("A macro function cannot have variadic parameters, but \"%s\" is one.", + parameter.GetName().GetIdentifierName()); + default: + throw InvalidInputException( + "The parameters of a macro function can always be passed by position or by name, so \"%s\" must " + "be a standard parameter.", + parameter.GetName().GetIdentifierName()); + } + auto ¶meter_name = parameter.GetName(); + // ANY leaves the parameter untyped, as a parameter declared without a type + auto parameter_type = + parameter.GetType().id() == LogicalTypeId::ANY ? LogicalType::UNKNOWN : parameter.GetType(); + if (parameter.HasDefaultValue()) { + auto default_value = *parameter.GetDefaultValue(); + if (parameter_type.id() != LogicalTypeId::UNKNOWN) { + default_value = default_value.DefaultCastAs(parameter_type); + } + auto default_expr = ConstantExpression::FromValue(default_value); + default_expr->SetAlias(parameter_name); + macro->default_parameters[parameter_name] = std::move(default_expr); + } + macro->parameters.push_back(make_uniq(parameter_name)); + macro->types.push_back(std::move(parameter_type)); + } + + auto catalog_type = type == MacroType::TABLE_MACRO ? CatalogType::TABLE_MACRO_ENTRY : CatalogType::MACRO_ENTRY; + auto parameter_types = macro->types; + auto info = make_uniq(catalog_type); + info->macros.push_back(std::move(macro)); + info->SetQualifiedName(QualifiedName(Identifier::InvalidCatalog(), Identifier::DefaultSchema(), name)); + info->internal = true; + docs.AddTo(std::move(parameter_types), *info); + return info; + } + +public: + //! The database it was created for: the only one it can be registered on. + DatabaseInstance &db; + MacroType type; + Identifier name; + string body; + FunctionSignature signature; + CV2FunctionDocs docs; + +private: + unique_ptr ParseBody() const { + auto parser = Parser::GetBuiltinParser(); + if (type == MacroType::SCALAR_MACRO) { + auto expressions = parser.ParseExpressionList(body); + if (expressions.size() != 1) { + throw InvalidInputException("The body of a scalar macro function must be a single expression."); + } + return make_uniq(std::move(expressions[0])); + } + parser.ParseQuery(body); + if (parser.statements.size() != 1 || parser.statements[0]->type != StatementType::SELECT_STATEMENT) { + throw InvalidInputException("The body of a table macro function must be a single SELECT query."); + } + auto &select = parser.statements[0]->Cast(); + return make_uniq(std::move(select.node)); + } +}; + +static auto Convert(duckdb_v2_macro_function_handle function) -> CV2MacroFunction * { + return reinterpret_cast(function); +} +static auto Convert(CV2MacroFunction *function) -> duckdb_v2_macro_function_handle { + return reinterpret_cast(function); +} + +} // namespace duckdb::capiv2 + +//---------------------------------------------------------------------------------------------------------------------- +// Public Functions +//---------------------------------------------------------------------------------------------------------------------- + +using namespace duckdb::capiv2; + +DUCKDB_V2_ERROR duckdb_v2_macro_function_create(duckdb_v2_factory_handle factory, DUCKDB_V2_MACRO_FUNCTION_TYPE type, + duckdb_v2_macro_function_handle *function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); + DUCKDB_CHECK_ARG(function); + *function = nullptr; + return WithErrorHandler(err, [&]() { + duckdb::MacroType macro_type; + switch (type) { + case DUCKDB_V2_MACRO_FUNCTION_TYPE_SCALAR: + macro_type = duckdb::MacroType::SCALAR_MACRO; + break; + case DUCKDB_V2_MACRO_FUNCTION_TYPE_TABLE: + macro_type = duckdb::MacroType::TABLE_MACRO; + break; + default: + throw duckdb::InvalidInputException("Invalid macro function type %d", static_cast(type)); + } + auto result = duckdb::make_uniq(Convert(factory)->GetDatabase(), macro_type); + *function = Convert(result.release()); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_macro_function_set_name(duckdb_v2_macro_function_handle function, + const duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + DUCKDB_CHECK_ARG(name); + return WithErrorHandler(err, [&]() { Convert(function)->name = duckdb::Identifier(ConvertIdentifierName(name)); }); +} + +DUCKDB_V2_ERROR duckdb_v2_macro_function_set_body(duckdb_v2_macro_function_handle function, const duckdb_v2_str *body, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + DUCKDB_CHECK_ARG(body); + return WithErrorHandler(err, [&]() { Convert(function)->body = duckdb::string(Convert(body)); }); +} + +DUCKDB_V2_ERROR duckdb_v2_macro_function_get_signature(duckdb_v2_macro_function_handle function, + duckdb_v2_function_signature_handle *sig, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + DUCKDB_CHECK_ARG(sig); + return WithErrorHandler(err, [&]() { *sig = Convert(&Convert(function)->signature); }); +} + +DUCKDB_V2_ERROR duckdb_v2_macro_function_get_docs(duckdb_v2_macro_function_handle function, + duckdb_v2_function_docs_handle *docs, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + DUCKDB_CHECK_ARG(docs); + return WithErrorHandler(err, [&]() { *docs = Convert(&Convert(function)->docs); }); +} + +DUCKDB_V2_ERROR duckdb_v2_extension_register_macro_function(duckdb_v2_extension_handle extension, + duckdb_v2_macro_function_handle function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(extension); + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { + auto &loader = GetExtensionLoader(extension); + auto info = Convert(function)->Build(loader.GetDatabaseInstance()); + loader.RegisterFunction(*info); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_macro_function_destroy(duckdb_v2_macro_function_handle *function) { + return WithErrorHandler(nullptr, [&]() { + if (!function) { + return; + } + if (*function) { + delete Convert(*function); + *function = nullptr; + } + }); +} diff --git a/src/duckdb/src/main/capi/v2/capi_v2_func_pragma.cpp b/src/duckdb/src/main/capi/v2/capi_v2_func_pragma.cpp new file mode 100644 index 000000000..444c0ddd6 --- /dev/null +++ b/src/duckdb/src/main/capi/v2/capi_v2_func_pragma.cpp @@ -0,0 +1,240 @@ +#include "duckdb/main/capi_v2/capi_v2_internal.hpp" +#include "duckdb/main/capi_v2/capi_v2_function_internal.hpp" +#include "duckdb/catalog/catalog.hpp" +#include "duckdb/function/pragma_function.hpp" +#include "duckdb/parser/parsed_data/create_pragma_function_info.hpp" + +namespace duckdb::capiv2 { + +class CV2PragmaFunctionInfo final : public PragmaFunctionInfo { +public: + duckdb_v2_pragma_function_query_callback_fn query_cb = nullptr; + duckdb_v2_pragma_function_exec_callback_fn exec_cb = nullptr; + shared_ptr user_data = nullptr; +}; + +class CV2PragmaQueryInfo { +public: + //! The query the callback set, if any + optional out_query; +}; + +static auto Convert(duckdb_v2_pragma_function_query_info_handle info) -> CV2PragmaQueryInfo * { + return reinterpret_cast(info); +} +static auto Convert(CV2PragmaQueryInfo *info) -> duckdb_v2_pragma_function_query_info_handle { + return reinterpret_cast(info); +} + +//! Hands the arguments of the PRAGMA statement to `callback`, and throws the error it reported +template +static auto CV2PragmaInvoke(ClientContext &context, const FunctionParameters ¶meters, FUNC &&callback) -> void { + if (!parameters.function) { + throw InternalException("A pragma function was called without its function"); + } + auto &function = *parameters.function; + auto &info = function.function_info->Cast(); + auto &signature = function.GetSignature(); + + // the binder places the arguments as it does for a table function, so the call is laid out the same way + BoundSimpleFunction bound_function; + bound_function.SetName(function.name); + bound_function.SetCallArguments(signature, parameters.values, parameters.named_parameters); + CV2ConstantBindInfo bind_info(bound_function, signature, info.user_data ? info.user_data->GetData() : nullptr, + parameters.values, parameters.named_parameters); + bind_info.bind_data_error = "Pragma functions have no bind data"; + + CV2ErrorInfo err = {}; + auto err_ptr = Convert(&err); + CV2CallbackContext callback_context(context); + callback(info, Convert(&bind_info), Convert(&callback_context), &err_ptr); + + if (err.HasError()) { + err.ThrowAsException(); + } +} + +static auto CV2PragmaQuery(ClientContext &context, const FunctionParameters ¶meters) -> string { + CV2PragmaQueryInfo result_info; + CV2PragmaInvoke(context, parameters, [&](const CV2PragmaFunctionInfo &info, auto bind_info, auto ctx, auto err) { + info.query_cb(bind_info, Convert(&result_info), ctx, err); + }); + if (!result_info.out_query) { + throw InvalidInputException("The query callback of pragma function \"%s\" did not set a query.", + parameters.function->name); + } + return std::move(*result_info.out_query); +} + +static auto CV2PragmaExec(ClientContext &context, const FunctionParameters ¶meters) -> void { + CV2PragmaInvoke(context, parameters, [&](const CV2PragmaFunctionInfo &info, auto bind_info, auto ctx, auto err) { + info.exec_cb(bind_info, ctx, err); + }); +} + +class CV2PragmaFunction { +public: + explicit CV2PragmaFunction(DatabaseInstance &db) : db(db) { + } + + //! Validates the configuration and builds the pragma function to register on `target`. + PragmaFunction Build(DatabaseInstance &target) { + CheckRegistrationTarget(db, target, "pragma function"); + if (name.empty()) { + throw InvalidInputException("Function name cannot be empty."); + } + if (info.query_cb && info.exec_cb) { + throw InvalidInputException("A pragma function cannot have both a query callback and an exec callback."); + } + if (!info.query_cb && !info.exec_cb) { + throw InvalidInputException("Either a query callback or an exec callback must be set for the function."); + } + if (signature.GetReturnType().id() != LogicalTypeId::INVALID) { + throw InvalidInputException("A pragma function signature cannot set a return type."); + } + + signature.Verify(); + + auto function = info.query_cb ? PragmaFunction::PragmaCall(name, CV2PragmaQuery, signature) + : PragmaFunction::PragmaCall(name, CV2PragmaExec, signature); + if (signature.GetParameterCount() == 0) { + function.type = PragmaType::PRAGMA_STATEMENT; + } + function.function_info = make_shared_ptr(std::move(info)); + return function; + } + +public: + //! The database it was created for: the only one it can be registered on. + DatabaseInstance &db; + FunctionSignature signature; + CV2FunctionDocs docs; + CV2PragmaFunctionInfo info; + Identifier name; +}; + +static auto Convert(duckdb_v2_pragma_function_handle func) -> CV2PragmaFunction * { + return reinterpret_cast(func); +} +static auto Convert(CV2PragmaFunction *func) -> duckdb_v2_pragma_function_handle { + return reinterpret_cast(func); +} + +} // namespace duckdb::capiv2 + +//---------------------------------------------------------------------------------------------------------------------- +// Public Functions +//---------------------------------------------------------------------------------------------------------------------- + +using namespace duckdb::capiv2; + +DUCKDB_V2_ERROR duckdb_v2_pragma_function_create(duckdb_v2_factory_handle factory, + duckdb_v2_pragma_function_handle *function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); + DUCKDB_CHECK_ARG(function); + *function = nullptr; + return WithErrorHandler(err, [&]() { + auto result = duckdb::make_uniq(Convert(factory)->GetDatabase()); + *function = Convert(result.release()); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_pragma_function_set_name(duckdb_v2_pragma_function_handle function, + const duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + DUCKDB_CHECK_ARG(name); + return WithErrorHandler(err, [&]() { Convert(function)->name = duckdb::Identifier(ConvertIdentifierName(name)); }); +} + +DUCKDB_V2_ERROR duckdb_v2_pragma_function_get_docs(duckdb_v2_pragma_function_handle function, + duckdb_v2_function_docs_handle *docs, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + DUCKDB_CHECK_ARG(docs); + return WithErrorHandler(err, [&]() { *docs = Convert(&Convert(function)->docs); }); +} + +DUCKDB_V2_ERROR duckdb_v2_pragma_function_get_signature(duckdb_v2_pragma_function_handle function, + duckdb_v2_function_signature_handle *sig, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + DUCKDB_CHECK_ARG(sig); + return WithErrorHandler(err, [&]() { *sig = Convert(&Convert(function)->signature); }); +} + +DUCKDB_V2_ERROR duckdb_v2_pragma_function_set_user_data(duckdb_v2_pragma_function_handle function, + duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { + Convert(function)->info.user_data = + duckdb::make_shared_ptr(data->ptr, data->destroy, data->equals); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_pragma_function_set_query_callback(duckdb_v2_pragma_function_handle function, + duckdb_v2_pragma_function_query_callback_fn callback, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { Convert(function)->info.query_cb = callback; }); +} + +DUCKDB_V2_ERROR duckdb_v2_pragma_function_set_exec_callback(duckdb_v2_pragma_function_handle function, + duckdb_v2_pragma_function_exec_callback_fn callback, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { Convert(function)->info.exec_cb = callback; }); +} + +DUCKDB_V2_ERROR duckdb_v2_pragma_function_query_set_query(duckdb_v2_pragma_function_query_info_handle info, + const duckdb_v2_str *query, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(query); + return WithErrorHandler(err, [&]() { Convert(info)->out_query = duckdb::string(Convert(query)); }); +} + +DUCKDB_V2_ERROR duckdb_v2_connection_register_pragma_function(duckdb_v2_connection_handle conn, + duckdb_v2_pragma_function_handle function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(conn); + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { + auto &context = *Convert(conn)->context; + context.RunFunctionInTransaction([&]() { + auto &self = *Convert(function); + duckdb::CreatePragmaFunctionInfo info(self.Build(*context.db)); + self.docs.AddTo(self.signature, info); + auto &catalog = duckdb::Catalog::GetSystemCatalog(context); + catalog.CreatePragmaFunction(context, info); + }); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_extension_register_pragma_function(duckdb_v2_extension_handle extension, + duckdb_v2_pragma_function_handle function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(extension); + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { + auto &self = *Convert(function); + auto &loader = GetExtensionLoader(extension); + duckdb::CreatePragmaFunctionInfo info(self.Build(loader.GetDatabaseInstance())); + self.docs.AddTo(self.signature, info); + loader.RegisterFunction(std::move(info)); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_pragma_function_destroy(duckdb_v2_pragma_function_handle *function) { + return WithErrorHandler(nullptr, [&]() { + if (!function) { + return; + } + if (*function) { + delete Convert(*function); + *function = nullptr; + } + }); +} diff --git a/src/duckdb/src/main/capi/v2/capi_v2_func_scalar.cpp b/src/duckdb/src/main/capi/v2/capi_v2_func_scalar.cpp index 6f14ed015..5e5b1ff85 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_func_scalar.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_func_scalar.cpp @@ -1,4 +1,5 @@ #include "duckdb/main/capi_v2/capi_v2_internal.hpp" +#include "duckdb/main/capi_v2/capi_v2_function_internal.hpp" #include "duckdb/catalog/catalog.hpp" #include "duckdb/parser/parsed_data/create_scalar_function_info.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" @@ -27,12 +28,34 @@ class CV2FunctionLocalState final : public FunctionLocalState { CV2UserData handle; }; -class CV2BindInfo { +static constexpr const char *SCALAR_BIND_DATA_ERROR = + "Scalar functions set the bind data through duckdb_v2_scalar_function_bind_set_bind_data"; + +//! The arguments of a scalar call before they are cast to the parameter types - the bind data cannot be set +class CV2ResolveTypesBindInfo final : public CV2FunctionBindInfo { public: - void *in_user_data = nullptr; - BindScalarFunctionInput *in_input = nullptr; + CV2ResolveTypesBindInfo(const BoundScalarFunction &function, void *user_data, + const ResolveScalarFunctionTypesInput &input) + : CV2FunctionBindInfo(function, user_data), input(input) { + D_ASSERT(GetArgCount() == input.GetArgumentCount()); + bind_data_error = SCALAR_BIND_DATA_ERROR; + } - duckdb_v2_opaque out_bind_data = {}; + LogicalType GetArgType(idx_t index) const override { + return input.GetArgumentType(index); + } + Value GetArgValue(idx_t index) const override { + return input.GetConstant(index); + } + +private: + const ResolveScalarFunctionTypesInput &input; +}; + +class CV2BindInfo { +public: + //! The bind data the callback set, or nullptr + shared_ptr out_bind_data; }; static auto Convert(duckdb_v2_scalar_function_bind_info_handle info) -> CV2BindInfo * { @@ -42,6 +65,21 @@ static auto Convert(CV2BindInfo *info) -> duckdb_v2_scalar_function_bind_info_ha return reinterpret_cast(info); } +class CV2ResolveTypesInfo { +public: + explicit CV2ResolveTypesInfo(BoundScalarFunction &bound_function) : bound_function(bound_function) { + } + + BoundScalarFunction &bound_function; +}; + +static auto Convert(duckdb_v2_scalar_function_resolve_types_info_handle info) -> CV2ResolveTypesInfo * { + return reinterpret_cast(info); +} +static auto Convert(CV2ResolveTypesInfo *info) -> duckdb_v2_scalar_function_resolve_types_info_handle { + return reinterpret_cast(info); +} + class CV2InitInfo { public: void *in_user_data = nullptr; @@ -64,6 +102,8 @@ class CV2ExecInfo { void *in_init_data = nullptr; DataChunk *input; + //! Tells the four parts of the argument list apart + const BoundScalarFunction *function = nullptr; Vector *result; }; @@ -76,28 +116,47 @@ static auto Convert(CV2ExecInfo *info) -> duckdb_v2_scalar_function_exec_info_ha class CV2ScalarFunctionInfo : public ScalarFunctionInfo { public: + duckdb_v2_scalar_function_resolve_types_callback_fn resolve_types_cb = nullptr; duckdb_v2_scalar_function_bind_callback_fn bind_cb = nullptr; duckdb_v2_scalar_function_init_callback_fn init_cb = nullptr; duckdb_v2_scalar_function_exec_callback_fn exec_cb = nullptr; shared_ptr user_data = nullptr; }; +static auto CV2ScalarResolveTypes(ResolveScalarFunctionTypesInput &input) -> void { + auto &bound_function = input.GetBoundFunction(); + const auto &info = bound_function.GetExtraFunctionInfo().Cast(); + + CV2ResolveTypesBindInfo bind_info(bound_function, info.user_data ? info.user_data->GetData() : nullptr, input); + CV2ResolveTypesInfo result_info(bound_function); + + CV2ErrorInfo err = {}; + auto err_ptr = Convert(&err); + CV2CallbackContext callback_context(input.GetClientContext()); + info.resolve_types_cb(Convert(&bind_info), Convert(&result_info), Convert(&callback_context), &err_ptr); + + if (err.HasError()) { + err.ThrowAsException(); + } +} + static auto CV2ScalarBind(BindScalarFunctionInput &input) -> unique_ptr { const auto &info = input.GetBoundFunction().GetExtraFunctionInfo().Cast(); - CV2BindInfo args = {}; - args.in_user_data = info.user_data ? info.user_data->GetData() : nullptr; - args.in_input = &input; + auto &bound_function = input.GetBoundFunction(); + CV2ExpressionBindInfo bind_info(bound_function, info.user_data ? info.user_data->GetData() : nullptr, input); + bind_info.bind_data_error = SCALAR_BIND_DATA_ERROR; + CV2BindInfo result_info; CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.bind_cb(Convert(&args), Convert(&input.GetClientContext()), &err_ptr); + CV2CallbackContext callback_context(input.GetClientContext()); + info.bind_cb(Convert(&bind_info), Convert(&result_info), Convert(&callback_context), &err_ptr); unique_ptr result = nullptr; - if (args.out_bind_data.ptr) { + if (result_info.out_bind_data) { auto set_result = make_uniq(); - set_result->handle = - make_shared_ptr(args.out_bind_data.ptr, args.out_bind_data.destroy, args.out_bind_data.equals); + set_result->handle = std::move(result_info.out_bind_data); result = std::move(set_result); } @@ -120,7 +179,8 @@ static auto CV2ScalarInit(ExpressionState &state, const BoundFunctionExpression CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.init_cb(Convert(&args), Convert(&state.GetContext()), &err_ptr); + CV2CallbackContext callback_context(state.GetContext()); + info.init_cb(Convert(&args), Convert(&callback_context), &err_ptr); unique_ptr result = nullptr; if (args.out_init_data.ptr) { @@ -145,6 +205,7 @@ static auto CV2ScalarExec(DataChunk &input, ExpressionState &state, Vector &resu CV2ExecInfo args; args.in_user_data = info.user_data ? info.user_data->GetData() : nullptr; args.input = &input; + args.function = &expr.Function(); args.result = &result; // Setup bind data (if provided) @@ -162,7 +223,8 @@ static auto CV2ScalarExec(DataChunk &input, ExpressionState &state, Vector &resu CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.exec_cb(Convert(&args), Convert(&state.GetContext()), &err_ptr); + CV2CallbackContext callback_context(state.GetContext()); + info.exec_cb(Convert(&args), Convert(&callback_context), &err_ptr); if (err.HasError()) { err.ThrowAsException(); @@ -171,13 +233,15 @@ static auto CV2ScalarExec(DataChunk &input, ExpressionState &state, Vector &resu class CV2ScalarFunction { public: - CV2ScalarFunction() { + explicit CV2ScalarFunction(DatabaseInstance &db) : db(db) { // The exec callback's error slot makes failure part of the API contract, so the function defaults to fallible: // a non-fallible function turns any execution error reported through the slot into an internal error. properties.SetFallible(); } - void Register() { + //! Validates the configuration and builds the scalar function to register on `target`. + ScalarFunction Build(DatabaseInstance &target) { + CheckRegistrationTarget(db, target, "scalar function"); if (name.empty()) { throw InvalidInputException("Function name cannot be empty."); } @@ -187,11 +251,12 @@ class CV2ScalarFunction { const auto &return_type = signature.GetReturnType(); - // ANY is allowed as a placeholder return type only when a bind callback is present to resolve the actual type. + // ANY is allowed as a placeholder return type only when a resolve types callback is present to resolve the + // actual type. if (return_type.id() == LogicalTypeId::ANY) { - if (info.bind_cb == nullptr) { + if (info.resolve_types_cb == nullptr) { throw InvalidInputException( - "An ANY return type requires a bind callback to set the concrete return type."); + "An ANY return type requires a resolve types callback to set the concrete return type."); } } else { if (return_type.id() == LogicalTypeId::INVALID) { @@ -208,6 +273,9 @@ class CV2ScalarFunction { function.SetProperties(properties); function.GetSignature() = signature; + if (info.resolve_types_cb) { + function.SetResolveTypesCallback(CV2ScalarResolveTypes); + } if (info.bind_cb) { function.SetBindCallback(CV2ScalarBind); } @@ -216,53 +284,19 @@ class CV2ScalarFunction { } function.SetExtraFunctionInfo(std::move(info)); - // Call the implementation to register - RegisterToCatalog(std::move(function)); + return function; } - virtual ~CV2ScalarFunction() = default; - virtual void RegisterToCatalog(ScalarFunction function) = 0; - public: + //! The database it was created for: the only one it can be registered on. + DatabaseInstance &db; FunctionSignature signature; + CV2FunctionDocs docs; CV2ScalarFunctionInfo info; Identifier name; FunctionProperties properties; }; -class CV2ConnectionScalarFunction : public CV2ScalarFunction { -public: - explicit CV2ConnectionScalarFunction(Connection &connection) : connection(connection) { - } - - void RegisterToCatalog(ScalarFunction function) override { - auto &context = *connection.context; - - context.RunFunctionInTransaction([&]() { - auto &catalog = Catalog::GetSystemCatalog(context); - CreateScalarFunctionInfo sf_info(std::move(function)); - sf_info.on_conflict = OnCreateConflict::ALTER_ON_CONFLICT; - catalog.CreateFunction(context, sf_info); - }); - } - -private: - Connection &connection; -}; - -class CV2ExtensionScalarFunction : public CV2ScalarFunction { -public: - explicit CV2ExtensionScalarFunction(ExtensionLoader &loader) : loader(loader) { - } - - void RegisterToCatalog(ScalarFunction function) override { - loader.RegisterFunction(std::move(function)); - } - -private: - ExtensionLoader &loader; -}; - static auto Convert(duckdb_v2_scalar_function_handle func) -> CV2ScalarFunction * { return reinterpret_cast(func); } @@ -278,38 +312,31 @@ static auto Convert(CV2ScalarFunction *func) -> duckdb_v2_scalar_function_handle using namespace duckdb::capiv2; -DUCKDB_V2_ERROR duckdb_v2_scalar_function_create_with_connection(duckdb_v2_connection_handle connection, - duckdb_v2_scalar_function_handle *out_function, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(connection); - DUCKDB_CHECK_ARG(out_function); - *out_function = nullptr; +DUCKDB_V2_ERROR duckdb_v2_scalar_function_create(duckdb_v2_factory_handle factory, + duckdb_v2_scalar_function_handle *function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); + DUCKDB_CHECK_ARG(function); + *function = nullptr; return WithErrorHandler(err, [&]() { - auto &conn = *Convert(connection); - auto function = duckdb::make_uniq(conn); - *out_function = Convert(function.release()); + auto result = duckdb::make_uniq(Convert(factory)->GetDatabase()); + *function = Convert(result.release()); }); } -DUCKDB_V2_ERROR duckdb_v2_scalar_function_create_with_extension(duckdb_v2_extension_handle extension, - duckdb_v2_scalar_function_handle *out_function, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(extension); - DUCKDB_CHECK_ARG(out_function); - *out_function = nullptr; - return WithErrorHandler(err, [&]() { - auto &loader = GetExtensionLoader(extension); - auto function = duckdb::make_uniq(loader); - *out_function = Convert(function.release()); - }); +DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_name(duckdb_v2_scalar_function_handle function, const duckdb_v2_str *name, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + DUCKDB_CHECK_ARG(name); + return WithErrorHandler(err, [&]() { Convert(function)->name = duckdb::Identifier(ConvertIdentifierName(name)); }); } -DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_name(duckdb_v2_scalar_function_handle function, duckdb_v2_str *name, +DUCKDB_V2_ERROR duckdb_v2_scalar_function_get_docs(duckdb_v2_scalar_function_handle function, + duckdb_v2_function_docs_handle *docs, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(function); - DUCKDB_CHECK_ARG(name); - DUCKDB_CHECK_ARG(*name); - return WithErrorHandler(err, [&]() { Convert(function)->name = duckdb::Identifier(Convert(*name)); }); + DUCKDB_CHECK_ARG(docs); + return WithErrorHandler(err, [&]() { *docs = Convert(&Convert(function)->docs); }); } DUCKDB_V2_ERROR duckdb_v2_scalar_function_get_signature(duckdb_v2_scalar_function_handle function, @@ -338,6 +365,14 @@ DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_property(duckdb_v2_scalar_function return WithErrorHandler(err, [&]() { SetScalarFunctionProperty(Convert(function)->properties, key, value); }); } +DUCKDB_V2_ERROR +duckdb_v2_scalar_function_set_resolve_types_callback(duckdb_v2_scalar_function_handle function, + duckdb_v2_scalar_function_resolve_types_callback_fn callback, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { Convert(function)->info.resolve_types_cb = callback; }); +} + DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_bind_callback(duckdb_v2_scalar_function_handle function, duckdb_v2_scalar_function_bind_callback_fn callback, duckdb_v2_error_info_handle *err) { @@ -359,66 +394,28 @@ DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_exec_callback(duckdb_v2_scalar_fun return WithErrorHandler(err, [&]() { Convert(function)->info.exec_cb = callback; }); } -DUCKDB_V2_ERROR duckdb_v2_scalar_function_bind_get_user_data(duckdb_v2_scalar_function_bind_info_handle info, - void **data, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(info); - DUCKDB_CHECK_ARG(data); - return WithErrorHandler(err, [&]() { *data = Convert(info)->in_user_data; }); -} - -DUCKDB_V2_ERROR duckdb_v2_scalar_function_bind_set_bind_data(duckdb_v2_scalar_function_bind_info_handle info, - duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(info); - return WithErrorHandler(err, [&]() { Convert(info)->out_bind_data = *data; }); -} - -DUCKDB_V2_ERROR duckdb_v2_scalar_function_bind_get_arg_count(duckdb_v2_scalar_function_bind_info_handle info, - idx_t *count, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(info); - DUCKDB_CHECK_ARG(count); - return WithErrorHandler(err, [&]() { *count = Convert(info)->in_input->GetArguments().size(); }); -} - -DUCKDB_V2_ERROR duckdb_v2_scalar_function_bind_get_arg_type(duckdb_v2_scalar_function_bind_info_handle info, - idx_t index, duckdb_v2_logical_type_handle *type, - duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR +duckdb_v2_scalar_function_resolve_types_set_return_type(duckdb_v2_scalar_function_resolve_types_info_handle info, + duckdb_v2_logical_type_handle return_type, + duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(info); - DUCKDB_CHECK_ARG(type); - *type = nullptr; + DUCKDB_CHECK_ARG(return_type); return WithErrorHandler(err, [&]() { - const auto &input = *Convert(info)->in_input; - const auto &arguments = input.GetArguments(); - if (index >= arguments.size()) { - throw duckdb::InvalidInputException("Index out of bounds in duckdb_v2_scalar_function_bind_get_arg_type"); - } - *type = Convert(new duckdb::LogicalType(arguments[index]->GetReturnType())); + auto type = Convert(return_type); + Convert(info)->bound_function.SetReturnType(*type); }); } -DUCKDB_V2_ERROR duckdb_v2_scalar_function_bind_get_arg_value(duckdb_v2_scalar_function_bind_info_handle info, - idx_t index, duckdb_v2_value_handle *value, - duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR duckdb_v2_scalar_function_bind_set_bind_data(duckdb_v2_scalar_function_bind_info_handle info, + duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(info); - DUCKDB_CHECK_ARG(value); - *value = nullptr; + DUCKDB_CHECK_ARG(data); return WithErrorHandler(err, [&]() { - const auto &input = *Convert(info)->in_input; - if (index >= input.GetArguments().size()) { - throw duckdb::InvalidInputException("Index out of bounds in duckdb_v2_scalar_function_bind_get_arg_value"); - } - *value = Convert(new duckdb::Value(input.GetConstant(index))); + Convert(info)->out_bind_data = + data->ptr ? duckdb::make_shared_ptr(data->ptr, data->destroy, data->equals) : nullptr; }); } -DUCKDB_V2_ERROR duckdb_v2_scalar_function_bind_set_return_type(duckdb_v2_scalar_function_bind_info_handle info, - duckdb_v2_logical_type_handle return_type, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(info); - DUCKDB_CHECK_ARG(return_type); - return WithErrorHandler( - err, [&]() { Convert(info)->in_input->GetBoundFunction().SetReturnType(*Convert(return_type)); }); -} - DUCKDB_V2_ERROR duckdb_v2_scalar_function_init_get_user_data(duckdb_v2_scalar_function_init_info_handle info, void **data, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(info); @@ -473,20 +470,32 @@ DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_row_count(duckdb_v2_scalar_fu } DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_arg_count(duckdb_v2_scalar_function_exec_info_handle info, - uint32_t *count, duckdb_v2_error_info_handle *err) { + idx_t *positional_fixed, idx_t *positional_variadic, + idx_t *named_fixed, idx_t *named_variadic, + duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(info); - DUCKDB_CHECK_ARG(count); - - return WithErrorHandler(err, - [&]() { *count = duckdb::NumericCast(Convert(info)->input->ColumnCount()); }); + return WithErrorHandler(err, [&]() { + auto &function = *Convert(info)->function; + if (positional_fixed) { + *positional_fixed = function.GetStandardArgumentCount(); + } + if (positional_variadic) { + *positional_variadic = function.GetVarArgsCount(); + } + if (named_fixed) { + *named_fixed = function.GetKeywordOnlyArgumentCount(); + } + if (named_variadic) { + *named_variadic = function.GetKwargsCount(); + } + }); } -DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_arg(duckdb_v2_scalar_function_exec_info_handle info, uint32_t index, +DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_arg(duckdb_v2_scalar_function_exec_info_handle info, idx_t index, duckdb_v2_vector_handle *vector, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(info); DUCKDB_CHECK_ARG(vector); - return WithErrorHandler(err, [&]() { auto &exec_info = *Convert(info); if (index >= exec_info.input->ColumnCount()) { @@ -508,10 +517,37 @@ DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_result(duckdb_v2_scalar_funct }); } -DUCKDB_V2_ERROR duckdb_v2_scalar_function_register(duckdb_v2_scalar_function_handle function, - duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR duckdb_v2_connection_register_scalar_function(duckdb_v2_connection_handle conn, + duckdb_v2_scalar_function_handle function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(conn); + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { + auto &context = *Convert(conn)->context; + context.RunFunctionInTransaction([&]() { + auto &self = *Convert(function); + auto info = duckdb::CreateScalarFunctionInfo(self.Build(*context.db)); + self.docs.AddTo(self.signature, info); + info.on_conflict = duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + auto &catalog = duckdb::Catalog::GetSystemCatalog(context); + catalog.CreateFunction(context, info); + }); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_extension_register_scalar_function(duckdb_v2_extension_handle extension, + duckdb_v2_scalar_function_handle function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(extension); DUCKDB_CHECK_ARG(function); - return WithErrorHandler(err, [&]() { Convert(function)->Register(); }); + return WithErrorHandler(err, [&]() { + auto &self = *Convert(function); + auto &loader = GetExtensionLoader(extension); + duckdb::CreateScalarFunctionInfo info(self.Build(loader.GetDatabaseInstance())); + self.docs.AddTo(self.signature, info); + info.on_conflict = duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + loader.RegisterFunction(std::move(info)); + }); } DUCKDB_V2_ERROR duckdb_v2_scalar_function_destroy(duckdb_v2_scalar_function_handle *function) { diff --git a/src/duckdb/src/main/capi/v2/capi_v2_func_signature.cpp b/src/duckdb/src/main/capi/v2/capi_v2_func_signature.cpp index 7f6fd71a3..4c0942ad8 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_func_signature.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_func_signature.cpp @@ -1,34 +1,45 @@ #include "duckdb/main/capi_v2/capi_v2_internal.hpp" +#include "duckdb/main/capi/capi_function_signature.hpp" using namespace duckdb::capiv2; -DUCKDB_V2_ERROR duckdb_v2_function_signature_add_parameter(duckdb_v2_function_signature_handle sig, - duckdb_v2_identifier_t name, - duckdb_v2_logical_type_handle type, - duckdb_v2_value_handle value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(sig); - DUCKDB_CHECK_ARG(type); - - return WithErrorHandler(err, [&]() { - auto &signature = *Convert(sig); - if (value) { - signature.AddParameter(duckdb::Identifier(Convert(name)), *Convert(type), *Convert(value)); - } else { - signature.AddParameter(duckdb::Identifier(Convert(name)), *Convert(type)); - } - }); +static auto ConvertParameterKind(DUCKDB_V2_FUNCTION_PARAMETER_KIND kind) -> duckdb::FunctionParameterKind { + switch (kind) { + case DUCKDB_V2_FUNCTION_PARAMETER_KIND_POSITIONAL_ONLY: + return duckdb::FunctionParameterKind::POSITIONAL_ONLY; + case DUCKDB_V2_FUNCTION_PARAMETER_KIND_STANDARD: + return duckdb::FunctionParameterKind::STANDARD; + case DUCKDB_V2_FUNCTION_PARAMETER_KIND_POSITIONAL_VARIADIC: + return duckdb::FunctionParameterKind::VAR_POSITIONAL; + case DUCKDB_V2_FUNCTION_PARAMETER_KIND_NAMED_ONLY: + return duckdb::FunctionParameterKind::KEYWORD_ONLY; + case DUCKDB_V2_FUNCTION_PARAMETER_KIND_NAMED_VARIADIC: + return duckdb::FunctionParameterKind::VAR_KEYWORD; + default: + throw duckdb::InvalidInputException("Invalid function parameter kind %d", static_cast(kind)); + } } -DUCKDB_V2_ERROR duckdb_v2_function_signature_set_varargs(duckdb_v2_function_signature_handle sig, - duckdb_v2_logical_type_handle type, - duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR +duckdb_v2_function_signature_add_parameter(duckdb_v2_function_signature_handle sig, const duckdb_v2_identifier_t *name, + duckdb_v2_logical_type_handle type, duckdb_v2_value_handle value, + DUCKDB_V2_FUNCTION_PARAMETER_KIND kind, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(sig); DUCKDB_CHECK_ARG(type); return WithErrorHandler(err, [&]() { auto &signature = *Convert(sig); - signature.SetVarArgs(*Convert(type)); + auto param_kind = ConvertParameterKind(kind); + auto param_name = duckdb::Identifier(ConvertIdentifierName(name)); + duckdb::optional default_value; + if (value) { + default_value = *Convert(value); + } + auto param_type = Convert(type); + // parameters of different kinds can be added in any order, the signature orders them by kind + duckdb::CAPIFunctionSignature::AddParameter( + signature, + duckdb::FunctionParameter(std::move(param_name), *param_type, std::move(default_value), param_kind)); }); } @@ -40,6 +51,7 @@ DUCKDB_V2_ERROR duckdb_v2_function_signature_set_return_type(duckdb_v2_function_ return WithErrorHandler(err, [&]() { auto &signature = *Convert(sig); - signature.SetReturnType(*Convert(type)); + auto return_type = Convert(type); + signature.SetReturnType(*return_type); }); } diff --git a/src/duckdb/src/main/capi/v2/capi_v2_func_table.cpp b/src/duckdb/src/main/capi/v2/capi_v2_func_table.cpp index e94d6dcb0..31deaee5f 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_func_table.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_func_table.cpp @@ -1,5 +1,12 @@ #include "duckdb/main/capi_v2/capi_v2_internal.hpp" +#include "duckdb/main/capi_v2/capi_v2_function_internal.hpp" #include "duckdb/catalog/catalog.hpp" +#include "duckdb/catalog/catalog_entry/table_function_catalog_entry.hpp" +#include "duckdb/common/multi_file/multi_file_data.hpp" +#include "duckdb/common/multi_file/table_function_multi_file.hpp" +#include "duckdb/execution/partition_info.hpp" +#include "duckdb/function/partition_stats.hpp" +#include "duckdb/parallel/pipeline.hpp" #include "duckdb/parser/parsed_data/create_table_function_info.hpp" #include "duckdb/planner/operator/logical_get.hpp" #include "duckdb/storage/statistics/node_statistics.hpp" @@ -21,8 +28,8 @@ class CV2TableFunctionData final : public FunctionData { bool cardinality_is_exact = false; bool cardinality_set = false; - // How many result columns the bind callback declared. - idx_t column_count = 0; + vector column_types; + vector column_names; auto Copy() const -> unique_ptr override { auto copy = make_uniq(); @@ -31,7 +38,8 @@ class CV2TableFunctionData final : public FunctionData { copy->cardinality = cardinality; copy->cardinality_is_exact = cardinality_is_exact; copy->cardinality_set = cardinality_set; - copy->column_count = column_count; + copy->column_types = column_types; + copy->column_names = column_names; return std::move(copy); } @@ -57,19 +65,75 @@ class CV2TableGlobalState final : public GlobalTableFunctionState { class CV2TableLocalState final : public LocalTableFunctionState { public: CV2UserData handle; + //! Whether the caller claims the batches of this thread (see TableFunction::claim_batch) + bool claimed_externally = false; + //! Whether this thread has claimed a batch it has not exhausted yet + bool batch_claimed = false; + //! Written into by the exec callback when the scan requests no columns, as the function produces its rows through + //! its first column + unique_ptr row_chunk; + + // Requested partition columns as declared columns, fixed for the lifetime of the local state's pipeline. + const vector &PartitionColumns(const OperatorPartitionInfo &partition_info, + const vector &scan_columns) { + if (!partition_columns_resolved) { + for (auto scan_position : partition_info.partition_columns) { + D_ASSERT(scan_position < scan_columns.size()); + partition_columns.push_back(scan_columns[scan_position]); + } + partition_columns_resolved = true; + } + D_ASSERT(partition_columns.size() == partition_info.partition_columns.size()); + return partition_columns; + } + + void CheckPartitionData(const Identifier &function_name, const OperatorPartitionData &data) { + if (last_batch_index.IsValid()) { + auto last_index = last_batch_index.GetIndex(); + // The engine would otherwise raise an InternalException, which invalidates the database. + if (data.batch_index < last_index) { + throw InvalidInputException( + "The partition data callback of table function \"%s\" reported batch index %llu after " + "previously reporting %llu on the same thread; the batch index must not decrease.", + function_name, data.batch_index, last_index); + } + // The engine only reads the values when the batch index changes; a changed value would silently be ignored. + if (data.batch_index == last_index) { + D_ASSERT(data.partition_data.size() == last_partition_values.size()); + for (idx_t i = 0; i < last_partition_values.size(); i++) { + if (Value::NotDistinctFrom(data.partition_data[i].min_val, last_partition_values[i])) { + continue; + } + throw InvalidInputException( + "The partition data callback of table function \"%s\" reported a different partitioning " + "column value at index %llu without changing the batch index.", + function_name, i); + } + return; + } + } + last_batch_index = data.batch_index; + last_partition_values.clear(); + for (auto &entry : data.partition_data) { + last_partition_values.push_back(entry.min_val); + } + } + +private: + bool partition_columns_resolved = false; + vector partition_columns; + optional_idx last_batch_index; + vector last_partition_values; }; class CV2TableBindInfo { public: - void *in_user_data = nullptr; - const vector *in_args = nullptr; - vector out_column_types; vector out_column_names; - duckdb_v2_opaque out_bind_data = {}; idx_t out_cardinality = 0; bool out_cardinality_is_exact = false; bool out_cardinality_set = false; + OrderPreservationType out_order_preservation = OrderPreservationType::INSERTION_ORDER; }; static auto Convert(duckdb_v2_table_function_bind_info_handle info) -> CV2TableBindInfo * { @@ -147,6 +211,23 @@ static auto Convert(CV2TableProgressInfo *info) -> duckdb_v2_table_function_prog return reinterpret_cast(info); } +class CV2TableClaimBatchInfo { +public: + void *in_user_data = nullptr; + void *in_bind_data = nullptr; + void *in_global_state = nullptr; + void *in_local_state = nullptr; + + bool out_claimed = false; +}; + +static auto Convert(duckdb_v2_table_function_claim_batch_info_handle info) -> CV2TableClaimBatchInfo * { + return reinterpret_cast(info); +} +static auto Convert(CV2TableClaimBatchInfo *info) -> duckdb_v2_table_function_claim_batch_info_handle { + return reinterpret_cast(info); +} + class CV2TableFilterPushdownInfo { public: void *in_user_data = nullptr; @@ -166,6 +247,79 @@ static auto Convert(CV2TableFilterPushdownInfo *info) -> duckdb_v2_table_functio return reinterpret_cast(info); } +class CV2TablePartitionDataInfo { +public: + void *in_user_data = nullptr; + void *in_bind_data = nullptr; + void *in_global_state = nullptr; + void *in_local_state = nullptr; + const OperatorPartitionInfo *in_partition_info = nullptr; + const vector *in_partition_columns = nullptr; + const vector *in_column_types = nullptr; + + optional_idx out_batch_index; + vector out_partition_value_set; + vector out_partition_values; +}; + +static auto Convert(duckdb_v2_table_function_partition_data_info_handle info) -> CV2TablePartitionDataInfo * { + return reinterpret_cast(info); +} +static auto Convert(CV2TablePartitionDataInfo *info) -> duckdb_v2_table_function_partition_data_info_handle { + return reinterpret_cast(info); +} + +class CV2TablePartitioningInfo { +public: + void *in_user_data = nullptr; + void *in_bind_data = nullptr; + const vector *in_partition_ids = nullptr; + + // Not calling the setter is not a caller error: it means "not partitioned", the conservative default. + TablePartitionInfo out_partition_info = TablePartitionInfo::NOT_PARTITIONED; +}; + +static auto Convert(duckdb_v2_table_function_partitioning_info_handle info) -> CV2TablePartitioningInfo * { + return reinterpret_cast(info); +} +static auto Convert(CV2TablePartitioningInfo *info) -> duckdb_v2_table_function_partitioning_info_handle { + return reinterpret_cast(info); +} + +static_assert(static_cast(TablePartitionInfo::NOT_PARTITIONED) == DUCKDB_V2_TABLE_PARTITION_INFO_NOT_PARTITIONED, + "TablePartitionInfo::NOT_PARTITIONED mismatch"); +static_assert(static_cast(TablePartitionInfo::SINGLE_VALUE_PARTITIONS) == + DUCKDB_V2_TABLE_PARTITION_INFO_SINGLE_VALUE_PARTITIONS, + "TablePartitionInfo::SINGLE_VALUE_PARTITIONS mismatch"); +static_assert(static_cast(TablePartitionInfo::OVERLAPPING_PARTITIONS) == + DUCKDB_V2_TABLE_PARTITION_INFO_OVERLAPPING_PARTITIONS, + "TablePartitionInfo::OVERLAPPING_PARTITIONS mismatch"); +static_assert(static_cast(TablePartitionInfo::DISJOINT_PARTITIONS) == + DUCKDB_V2_TABLE_PARTITION_INFO_DISJOINT_PARTITIONS, + "TablePartitionInfo::DISJOINT_PARTITIONS mismatch"); + +static auto CV2ConvertPartitionInfo(DUCKDB_V2_TABLE_PARTITION_INFO value) -> TablePartitionInfo { + if (static_cast(value) > DUCKDB_V2_TABLE_PARTITION_INFO_DISJOINT_PARTITIONS) { + throw duckdb::InvalidInputException( + "Invalid value in duckdb_v2_table_function_partitioning_set_partition_info"); + } + return static_cast(value); +} + +static_assert(static_cast(OrderPreservationType::NO_ORDER) == DUCKDB_V2_ORDER_PRESERVATION_NO_ORDER, + "OrderPreservationType::NO_ORDER mismatch"); +static_assert(static_cast(OrderPreservationType::INSERTION_ORDER) == DUCKDB_V2_ORDER_PRESERVATION_INSERTION_ORDER, + "OrderPreservationType::INSERTION_ORDER mismatch"); +static_assert(static_cast(OrderPreservationType::FIXED_ORDER) == DUCKDB_V2_ORDER_PRESERVATION_FIXED_ORDER, + "OrderPreservationType::FIXED_ORDER mismatch"); + +static auto CV2ConvertOrderPreservation(DUCKDB_V2_ORDER_PRESERVATION value) -> OrderPreservationType { + if (static_cast(value) > DUCKDB_V2_ORDER_PRESERVATION_FIXED_ORDER) { + throw duckdb::InvalidInputException("Invalid value in duckdb_v2_table_function_bind_set_order_preservation"); + } + return static_cast(value); +} + class CV2TableFunctionInfo : public TableFunctionInfo { public: duckdb_v2_table_function_bind_callback_fn bind_cb = nullptr; @@ -174,64 +328,81 @@ class CV2TableFunctionInfo : public TableFunctionInfo { duckdb_v2_table_function_exec_callback_fn exec_cb = nullptr; duckdb_v2_table_function_progress_callback_fn progress_cb = nullptr; duckdb_v2_table_function_filter_pushdown_callback_fn filter_pushdown_cb = nullptr; + duckdb_v2_table_function_partition_data_callback_fn partition_data_cb = nullptr; + duckdb_v2_table_function_partitioning_callback_fn partitioning_cb = nullptr; + duckdb_v2_table_function_claim_batch_callback_fn claim_batch_cb = nullptr; + duckdb_v2_table_function_get_bind_info_callback_fn get_bind_info_cb = nullptr; shared_ptr user_data = nullptr; bool projection_pushdown = false; - // The signature's slot plan, captured at registration: every parameter name in signature order, how many of them - // lead the positional prefix (the ones without a default), and the default of each remaining parameter. The bind - // wrapper assembles the argument list from it, injecting the default for a parameter the call site omitted, so - // the bind callback observes a value for every parameter. - vector parameter_names; - idx_t positional_count = 0; - identifier_map_t named_parameter_defaults; + Identifier name; }; -//! Assembles the call's arguments in signature-slot order: first the parameters without a default, taken from the -//! positional arguments, then the parameters with one, taken from the named arguments or the declared default when -//! the call site omitted them, then the variadic tail. -static auto CV2TableCollectArguments(const CV2TableFunctionInfo &info, TableFunctionBindInput &input) -> vector { - vector arguments; - const auto positional_count = MinValue(info.positional_count, input.inputs.size()); - - for (idx_t i = 0; i < positional_count; i++) { - arguments.push_back(input.inputs[i]); +//! What the "get bind info" callback describes a bound call with +class CV2TableGetBindInfoInfo { +public: + explicit CV2TableGetBindInfoInfo(const CV2TableFunctionData &bind_data_p) : bind_data(bind_data_p) { } - for (idx_t i = info.positional_count; i < info.parameter_names.size(); i++) { - const auto &name = info.parameter_names[i]; - auto provided = input.named_parameters.find(name); - if (provided != input.named_parameters.end()) { - arguments.push_back(provided->second); - } else { - arguments.push_back(info.named_parameter_defaults.at(name)); - } + + void *in_user_data = nullptr; + void *in_bind_data = nullptr; + //! The bound call - its result columns are the columns identifiers are attached to + const CV2TableFunctionData &bind_data; + + //! What the callback reports + BindInfo out_bind_info {ScanType::EXTERNAL}; +}; + +static auto Convert(duckdb_v2_table_function_get_bind_info_info_handle info) -> CV2TableGetBindInfoInfo * { + return reinterpret_cast(info); +} +static auto Convert(CV2TableGetBindInfoInfo *info) -> duckdb_v2_table_function_get_bind_info_info_handle { + return reinterpret_cast(info); +} + +//! Asks the function to describe a bound call: e.g. the identifiers of its columns +static auto CV2TableGetBindInfo(TableFunctionGetBindInfoInput &input) -> BindInfo { + if (!input.bind_data) { + return BindInfo(ScanType::EXTERNAL); } - for (idx_t i = positional_count; i < input.inputs.size(); i++) { - arguments.push_back(input.inputs[i]); + const auto &bind_data = input.bind_data->Cast(); + const auto &info = *bind_data.info; + + CV2TableGetBindInfoInfo args(bind_data); + args.in_user_data = info.user_data ? info.user_data->GetData() : nullptr; + args.in_bind_data = bind_data.handle ? bind_data.handle->GetData() : nullptr; + + CV2ErrorInfo err = {}; + auto err_ptr = Convert(&err); + CV2CallbackContext callback_context(input.context); + info.get_bind_info_cb(Convert(&args), Convert(&callback_context), &err_ptr); + + if (err.HasError()) { + err.ThrowAsException(); } - return arguments; + + return std::move(args.out_bind_info); } static auto CV2TableBind(ClientContext &context, TableFunctionBindInput &input, vector &return_types, vector &names) -> unique_ptr { const auto &info = input.info->Cast(); - auto arguments = CV2TableCollectArguments(info, input); - + // the binder places every argument in its signature slot, defaults included, so the call reads them as-is + CV2ConstantBindInfo bind_info(input.table_function, input.table_function.GetSignature(), + info.user_data ? info.user_data->GetData() : nullptr, input.inputs, + input.named_parameters); CV2TableBindInfo args = {}; - args.in_user_data = info.user_data ? info.user_data->GetData() : nullptr; - args.in_args = &arguments; CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.bind_cb(Convert(&args), Convert(&context), &err_ptr); + CV2CallbackContext callback_context(context); + info.bind_cb(Convert(&bind_info), Convert(&args), Convert(&callback_context), &err_ptr); // Take ownership of whatever the callback set before reporting an error, so it is destroyed either way. auto result = make_uniq(); result->info = &info; - if (args.out_bind_data.ptr) { - result->handle = - make_shared_ptr(args.out_bind_data.ptr, args.out_bind_data.destroy, args.out_bind_data.equals); - } + result->handle = std::move(bind_info.out_bind_data); result->cardinality = args.out_cardinality; result->cardinality_is_exact = args.out_cardinality_is_exact; result->cardinality_set = args.out_cardinality_set; @@ -242,10 +413,14 @@ static auto CV2TableBind(ClientContext &context, TableFunctionBindInput &input, if (args.out_column_types.empty()) { throw InvalidInputException("The bind callback of table function \"%s\" did not declare any result columns.", - input.table_function.name); + input.table_function.GetName()); } - result->column_count = args.out_column_types.size(); + // The binder hands each call site its own copy of the function, so this does not leak into other calls. + input.table_function.order_preservation_type = args.out_order_preservation; + + result->column_types = args.out_column_types; + result->column_names = args.out_column_names; return_types = std::move(args.out_column_types); names = std::move(args.out_column_names); return std::move(result); @@ -256,10 +431,14 @@ static auto CV2TableBind(ClientContext &context, TableFunctionBindInput &input, static auto CV2TableScanColumns(const CV2TableFunctionData &bind_data, const TableFunctionInitInput &input) -> vector { if (bind_data.info->projection_pushdown) { + if (input.column_ids.empty()) { + // the scan only produces rows (e.g. for count(*)) - the function produces them through its first column + return {0}; + } return input.column_ids; } vector columns; - for (idx_t i = 0; i < bind_data.column_count; i++) { + for (idx_t i = 0; i < bind_data.column_types.size(); i++) { columns.push_back(i); } return columns; @@ -284,7 +463,8 @@ static auto CV2TableInitGlobal(ClientContext &context, TableFunctionInitInput &i CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.init_global_cb(Convert(&args), Convert(&context), &err_ptr); + CV2CallbackContext callback_context(context); + info.init_global_cb(Convert(&args), Convert(&callback_context), &err_ptr); if (args.out_global_state.ptr) { result->handle = @@ -305,8 +485,10 @@ static auto CV2TableInitLocal(ExecutionContext &context, TableFunctionInitInput const auto &bind_data = input.bind_data->Cast(); const auto &info = *bind_data.info; + // Always produced, even without a callback: get_partition_data keeps its per-thread guard state here. + auto result = make_uniq(); if (!info.init_local_cb) { - return nullptr; + return std::move(result); } auto scan_columns = CV2TableScanColumns(bind_data, input); @@ -321,14 +503,12 @@ static auto CV2TableInitLocal(ExecutionContext &context, TableFunctionInitInput CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.init_local_cb(Convert(&args), Convert(&context.client), &err_ptr); + CV2CallbackContext callback_context(context.client); + info.init_local_cb(Convert(&args), Convert(&callback_context), &err_ptr); - unique_ptr result = nullptr; if (args.out_local_state.ptr) { - auto set_result = make_uniq(); - set_result->handle = + result->handle = CV2UserData(args.out_local_state.ptr, args.out_local_state.destroy, args.out_local_state.equals); - result = std::move(set_result); } // Throw after taking ownership of the state, so that it is destroyed even if we error. @@ -336,10 +516,69 @@ static auto CV2TableInitLocal(ExecutionContext &context, TableFunctionInitInput err.ThrowAsException(); } - return result; + return std::move(result); +} + +//! Calls the claim batch callback for the local state of the given input - returns whether a batch was claimed +static auto CV2TableClaimNextBatch(ClientContext &context, TableFunctionInput &input) -> bool { + const auto &bind_data = input.bind_data->Cast(); + const auto &info = *bind_data.info; + auto &local_state = input.local_state->Cast(); + + CV2TableClaimBatchInfo args = {}; + args.in_user_data = info.user_data ? info.user_data->GetData() : nullptr; + args.in_bind_data = bind_data.handle ? bind_data.handle->GetData() : nullptr; + if (input.global_state) { + args.in_global_state = input.global_state->Cast().handle.GetData(); + } + args.in_local_state = local_state.handle.GetData(); + + CV2ErrorInfo err = {}; + auto err_ptr = Convert(&err); + CV2CallbackContext callback_context(context); + info.claim_batch_cb(Convert(&args), Convert(&callback_context), &err_ptr); + + if (err.HasError()) { + err.ThrowAsException(); + } + local_state.batch_claimed = args.out_claimed; + return args.out_claimed; +} + +//! The caller claims the batches of this local state itself - every exec then scans the claimed batch only +static auto CV2TableClaimBatch(ClientContext &context, TableFunctionInput &input) -> bool { + input.local_state->Cast().claimed_externally = true; + return CV2TableClaimNextBatch(context, input); } +static auto CV2TableExecBatch(ClientContext &context, TableFunctionInput &input, DataChunk &output) -> void; + static auto CV2TableExec(ClientContext &context, TableFunctionInput &input, DataChunk &output) -> void { + const auto &info = *input.bind_data->Cast().info; + if (!info.claim_batch_cb) { + CV2TableExecBatch(context, input, output); + return; + } + auto &local_state = input.local_state->Cast(); + if (local_state.claimed_externally) { + // an empty chunk ends the batch, after which the caller claims the next one + CV2TableExecBatch(context, input, output); + return; + } + // nobody claims the batches for us - move on to the next batch whenever the current one is exhausted + while (true) { + if (!local_state.batch_claimed && !CV2TableClaimNextBatch(context, input)) { + return; + } + CV2TableExecBatch(context, input, output); + if (output.size() > 0) { + return; + } + local_state.batch_claimed = false; + } +} + +static auto CV2TableExecBatch(ClientContext &context, TableFunctionInput &input, DataChunk &output) -> void { const auto &bind_data = input.bind_data->Cast(); const auto &info = *bind_data.info; @@ -351,14 +590,24 @@ static auto CV2TableExec(ClientContext &context, TableFunctionInput &input, Data args.in_global_state = global_state.handle.GetData(); args.in_scan_columns = &global_state.scan_columns; } - if (input.local_state) { - args.in_local_state = input.local_state->Cast().handle.GetData(); + auto &local_state = input.local_state->Cast(); + args.in_local_state = local_state.handle.GetData(); + reference target(output); + if (output.ColumnCount() == 0) { + // the scan requests no columns (e.g. for count(*)), only rows - the function writes them into its first column + if (!local_state.row_chunk) { + local_state.row_chunk = make_uniq(); + local_state.row_chunk->Initialize(context, {bind_data.column_types[0]}); + } + local_state.row_chunk->Reset(); + target = *local_state.row_chunk; } - args.output = &output; + args.output = &target.get(); CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.exec_cb(Convert(&args), Convert(&context), &err_ptr); + CV2CallbackContext callback_context(context); + info.exec_cb(Convert(&args), Convert(&callback_context), &err_ptr); if (err.HasError()) { err.ThrowAsException(); @@ -367,7 +616,12 @@ static auto CV2TableExec(ClientContext &context, TableFunctionInput &input, Data // The vector write API sizes per vector, while the engine reads the chunk's cardinality. Callers typically size // only the first vector and write the rest through direct buffer writes, so take the first vector's size as the // batch's row count and propagate it to the others. A table function always has at least one result column. - output.SetChildCardinality(output.data[0].size()); + auto row_count = target.get().data[0].size(); + if (output.ColumnCount() == 0) { + output.SetCardinalityUnsafe(row_count); + return; + } + output.SetChildCardinality(row_count); } //! An exact estimate also pins the maximum; otherwise only the estimate is known. @@ -402,7 +656,8 @@ static auto CV2TableProgress(ClientContext &context, const FunctionData *bind_da CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.progress_cb(Convert(&args), Convert(&context), &err_ptr); + CV2CallbackContext callback_context(context); + info.progress_cb(Convert(&args), Convert(&callback_context), &err_ptr); if (err.HasError()) { err.ThrowAsException(); @@ -431,7 +686,8 @@ static auto CV2TableFilterPushdown(ClientContext &context, LogicalGet &get, Func CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - info.filter_pushdown_cb(Convert(&args), Convert(&context), &err_ptr); + CV2CallbackContext callback_context(context); + info.filter_pushdown_cb(Convert(&args), Convert(&callback_context), &err_ptr); if (err.HasError()) { err.ThrowAsException(); @@ -446,9 +702,91 @@ static auto CV2TableFilterPushdown(ClientContext &context, LogicalGet &get, Func filters = std::move(remaining); } +//! Batch index is required on every call, even when unrequested: the pipeline range-checks it regardless. +static auto CV2TakePartitionData(CV2TablePartitionDataInfo &args, const Identifier &function_name) + -> OperatorPartitionData { + if (!args.out_batch_index.IsValid()) { + throw InvalidInputException("The partition data callback of table function \"%s\" did not set a batch index.", + function_name); + } + OperatorPartitionData result(args.out_batch_index.GetIndex()); + result.partition_data.reserve(args.out_partition_values.size()); + for (idx_t i = 0; i < args.out_partition_values.size(); i++) { + if (!args.out_partition_value_set[i]) { + throw InvalidInputException("The partition data callback of table function \"%s\" did not set the " + "partitioning column value at index %llu.", + function_name, i); + } + result.partition_data.emplace_back(std::move(args.out_partition_values[i])); + } + return result; +} + +static auto CV2TableGetPartitionData(ClientContext &context, TableFunctionGetPartitionInput &input) + -> OperatorPartitionData { + const auto &bind_data = input.bind_data->Cast(); + const auto &info = *bind_data.info; + + auto &global_state = input.global_state->Cast(); + auto &local_state = input.local_state->Cast(); + + CV2TablePartitionDataInfo args = {}; + args.in_user_data = info.user_data ? info.user_data->GetData() : nullptr; + args.in_bind_data = bind_data.handle ? bind_data.handle->GetData() : nullptr; + args.in_global_state = global_state.handle.GetData(); + args.in_local_state = local_state.handle.GetData(); + args.in_partition_info = &input.partition_info; + // The planner resolves these to scan positions, unlike get_partition_info's, which it resolves to declared + // columns; the callback is promised declared columns in both. + args.in_partition_columns = &local_state.PartitionColumns(input.partition_info, global_state.scan_columns); + args.in_column_types = &bind_data.column_types; + args.out_partition_value_set.resize(args.in_partition_columns->size(), false); + args.out_partition_values.resize(args.in_partition_columns->size()); + + CV2ErrorInfo err = {}; + auto err_ptr = Convert(&err); + CV2CallbackContext callback_context(context); + info.partition_data_cb(Convert(&args), Convert(&callback_context), &err_ptr); + + if (err.HasError()) { + err.ThrowAsException(); + } + + auto result = CV2TakePartitionData(args, info.name); + local_state.CheckPartitionData(info.name, result); + return result; +} + +//! Runs at plan time and must be deterministic: the optimizer may call it for a plan shape it later discards. +static auto CV2TableGetPartitionInfo(ClientContext &context, TableFunctionPartitionInput &input) -> TablePartitionInfo { + const auto &bind_data = input.bind_data->Cast(); + const auto &info = *bind_data.info; + + CV2TablePartitioningInfo args = {}; + args.in_user_data = info.user_data ? info.user_data->GetData() : nullptr; + args.in_bind_data = bind_data.handle ? bind_data.handle->GetData() : nullptr; + args.in_partition_ids = &input.partition_ids; + + CV2ErrorInfo err = {}; + auto err_ptr = Convert(&err); + CV2CallbackContext callback_context(context); + info.partitioning_cb(Convert(&args), Convert(&callback_context), &err_ptr); + + if (err.HasError()) { + err.ThrowAsException(); + } + + return args.out_partition_info; +} + class CV2TableFunction { public: - void Register() { + explicit CV2TableFunction(DatabaseInstance &db) : db(db) { + } + + //! Validates the configuration and builds the table function to register on `target`. + TableFunction Build(DatabaseInstance &target) { + CheckRegistrationTarget(db, target, "table function"); if (name.empty()) { throw InvalidInputException("Function name cannot be empty."); } @@ -458,6 +796,10 @@ class CV2TableFunction { if (!info.exec_cb) { throw InvalidInputException("Exec callback must be set for the function."); } + // The engine calls get_partition_data without a null check once get_partition_info claims partitions. + if (info.partitioning_cb && !info.partition_data_cb) { + throw InvalidInputException("Partition data callback must be set when the partitioning callback is set."); + } // A table function declares the columns it returns from its bind callback, not through a return type. if (signature.GetReturnType().id() != LogicalTypeId::INVALID) { throw InvalidInputException("A table function signature cannot set a return type."); @@ -465,20 +807,9 @@ class CV2TableFunction { signature.Verify(); - // Route the signature onto the two ways SQL passes arguments to a table function: a parameter without a - // default value is a required positional argument, a parameter with one is an optional named argument. - vector positional; - for (idx_t i = 0; i < signature.GetParameterCount(); i++) { - const auto ¶m = signature.GetParameter(i); - if (!param.HasDefaultValue()) { - positional.push_back(param.GetType()); - } - } - - TableFunction function(name, positional, CV2TableExec, CV2TableBind, CV2TableInitGlobal, CV2TableInitLocal); - if (signature.HasVarArgs()) { - function.SetVarArgs(signature.GetVarArgs()); - } + // A table function holds a signature of its own, so the declared one is handed over as-is + TableFunction function(name, {}, CV2TableExec, CV2TableBind, CV2TableInitGlobal, CV2TableInitLocal); + function.GetSignature() = signature; // Always wired: it serves the estimate a bind callback may set, which is not known at registration. It // reports no estimate when the bind callback sets none. @@ -489,71 +820,118 @@ class CV2TableFunction { if (info.filter_pushdown_cb) { function.pushdown_complex_filter = CV2TableFilterPushdown; } + if (info.partition_data_cb) { + function.get_partition_data = CV2TableGetPartitionData; + } + if (info.partitioning_cb) { + function.get_partition_info = CV2TableGetPartitionInfo; + } function.projection_pushdown = info.projection_pushdown; - - auto function_info = make_shared_ptr(std::move(info)); - for (idx_t i = 0; i < signature.GetParameterCount(); i++) { - const auto ¶m = signature.GetParameter(i); - if (param.HasDefaultValue()) { - function.named_parameters[param.GetName()] = param.GetType(); - function_info->named_parameter_defaults[param.GetName()] = *param.GetDefaultValue(); - } - function_info->parameter_names.push_back(param.GetName()); + if (info.get_bind_info_cb) { + function.get_bind_info = CV2TableGetBindInfo; } - function_info->positional_count = positional.size(); - function.function_info = std::move(function_info); - // Call the implementation to register - RegisterToCatalog(std::move(function)); - } + info.name = name; + function.function_info = make_shared_ptr(std::move(info)); - virtual ~CV2TableFunction() = default; - virtual void RegisterToCatalog(TableFunction function) = 0; + return function; + } public: + //! The database it was created for: the only one it can be registered on. + DatabaseInstance &db; FunctionSignature signature; + CV2FunctionDocs docs; CV2TableFunctionInfo info; Identifier name; }; -class CV2ConnectionTableFunction : public CV2TableFunction { -public: - explicit CV2ConnectionTableFunction(Connection &connection) : connection(connection) { - } - - void RegisterToCatalog(TableFunction function) override { - auto &context = *connection.context; +static auto Convert(duckdb_v2_table_function_handle func) -> CV2TableFunction * { + return reinterpret_cast(func); +} +static auto Convert(CV2TableFunction *func) -> duckdb_v2_table_function_handle { + return reinterpret_cast(func); +} - context.RunFunctionInTransaction([&]() { - auto &catalog = Catalog::GetSystemCatalog(context); - CreateTableFunctionInfo tf_info(std::move(function)); - tf_info.on_conflict = OnCreateConflict::ALTER_ON_CONFLICT; - catalog.CreateTableFunction(context, tf_info); - }); +//---------------------------------------------------------------------------------------------------------------------- +// Multi-file functions +//---------------------------------------------------------------------------------------------------------------------- +//! The overloads of a table function catalog entry, copied so they outlive the transaction that read them. +static auto CopyTableFunctions(optional_ptr entry) -> unique_ptr { + if (!entry) { + return nullptr; } + return make_uniq(entry->Cast().functions); +} -private: - Connection &connection; -}; - -class CV2ExtensionTableFunction : public CV2TableFunction { +class CV2MultiFileFunction { public: - explicit CV2ExtensionTableFunction(ExtensionLoader &loader) : loader(loader) { + explicit CV2MultiFileFunction(DatabaseInstance &db) : db(db) { } - void RegisterToCatalog(TableFunction function) override { - loader.RegisterFunction(std::move(function)); + //! Validates the configuration and builds the function set to register on `target`, wrapping the overloads of + //! the single-file function registered there. + TableFunctionSet Build(DatabaseInstance &target, unique_ptr single_file_overloads) { + CheckRegistrationTarget(db, target, "multi-file function"); + if (name.empty()) { + throw InvalidInputException("Function name cannot be empty."); + } + if (single_file_function.empty()) { + throw InvalidInputException("The single-file function must be set for the multi-file function."); + } + TableFunctionMultiFileSettings settings; + settings.reader_type = reader_type; + if (!file_extension.empty()) { + settings.glob_input = FileGlobInput(FileGlobOptions::FALLBACK_GLOB, file_extension); + } + auto single_file = SelectSingleFileFunction(std::move(single_file_overloads)); + if (single_file.function == CV2TableExec && single_file.function_info) { + // the multi-file scan claims the batches of a C API function that scans one batch at a time + if (single_file.function_info->Cast().claim_batch_cb) { + settings.claim_batch = CV2TableClaimBatch; + } + } + auto function = TableFunctionMultiFileWrapper::CreateFunctionSet(std::move(single_file), name, settings); + return function; } private: - ExtensionLoader &loader; + //! The overload of the single-file function that takes the path of the file to read + TableFunction SelectSingleFileFunction(unique_ptr overloads) const { + if (!overloads) { + throw InvalidInputException("Table function \"%s\" to read single files with does not exist.", + single_file_function); + } + for (auto &function : overloads->functions) { + auto &signature = function->GetSignature(); + if (signature.GetRequiredParameterCount() != 1 || !signature.GetParameter(0).AcceptsPosition()) { + continue; + } + auto &type = signature.GetParameter(0).GetType(); + if (type == LogicalType::VARCHAR || type.id() == LogicalTypeId::ANY) { + return *function; + } + } + throw InvalidInputException( + "Table function \"%s\" cannot read single files: it must take the file to read as its only positional " + "parameter, of type ANY (or VARCHAR).", + single_file_function); + } + +public: + //! The database it was created for: the only one it can be registered on. + DatabaseInstance &db; + Identifier name; + Identifier single_file_function; + string reader_type; + string file_extension; }; -static auto Convert(duckdb_v2_table_function_handle func) -> CV2TableFunction * { - return reinterpret_cast(func); +static auto Convert(duckdb_v2_multi_file_function_handle func) -> CV2MultiFileFunction * { + return reinterpret_cast(func); } -static auto Convert(CV2TableFunction *func) -> duckdb_v2_table_function_handle { - return reinterpret_cast(func); +static auto Convert(CV2MultiFileFunction *func) -> duckdb_v2_multi_file_function_handle { + return reinterpret_cast(func); } } // namespace duckdb::capiv2 @@ -564,38 +942,31 @@ static auto Convert(CV2TableFunction *func) -> duckdb_v2_table_function_handle { using namespace duckdb::capiv2; -DUCKDB_V2_ERROR duckdb_v2_table_function_create_with_connection(duckdb_v2_connection_handle connection, - duckdb_v2_table_function_handle *function, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(connection); +DUCKDB_V2_ERROR duckdb_v2_table_function_create(duckdb_v2_factory_handle factory, + duckdb_v2_table_function_handle *function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(function); *function = nullptr; return WithErrorHandler(err, [&]() { - auto &conn = *Convert(connection); - auto result = duckdb::make_uniq(conn); + auto result = duckdb::make_uniq(Convert(factory)->GetDatabase()); *function = Convert(result.release()); }); } -DUCKDB_V2_ERROR duckdb_v2_table_function_create_with_extension(duckdb_v2_extension_handle extension, - duckdb_v2_table_function_handle *function, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(extension); +DUCKDB_V2_ERROR duckdb_v2_table_function_set_name(duckdb_v2_table_function_handle function, const duckdb_v2_str *name, + duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(function); - *function = nullptr; - return WithErrorHandler(err, [&]() { - auto &loader = GetExtensionLoader(extension); - auto result = duckdb::make_uniq(loader); - *function = Convert(result.release()); - }); + DUCKDB_CHECK_ARG(name); + return WithErrorHandler(err, [&]() { Convert(function)->name = duckdb::Identifier(ConvertIdentifierName(name)); }); } -DUCKDB_V2_ERROR duckdb_v2_table_function_set_name(duckdb_v2_table_function_handle function, duckdb_v2_str *name, +DUCKDB_V2_ERROR duckdb_v2_table_function_get_docs(duckdb_v2_table_function_handle function, + duckdb_v2_function_docs_handle *docs, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(function); - DUCKDB_CHECK_ARG(name); - DUCKDB_CHECK_ARG(*name); - return WithErrorHandler(err, [&]() { Convert(function)->name = duckdb::Identifier(Convert(*name)); }); + DUCKDB_CHECK_ARG(docs); + return WithErrorHandler(err, [&]() { *docs = Convert(&Convert(function)->docs); }); } DUCKDB_V2_ERROR duckdb_v2_table_function_get_signature(duckdb_v2_table_function_handle function, @@ -667,72 +1038,38 @@ duckdb_v2_table_function_set_filter_pushdown_callback(duckdb_v2_table_function_h return WithErrorHandler(err, [&]() { Convert(function)->info.filter_pushdown_cb = callback; }); } -DUCKDB_V2_ERROR duckdb_v2_table_function_bind_get_user_data(duckdb_v2_table_function_bind_info_handle info, void **data, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(info); - DUCKDB_CHECK_ARG(data); - return WithErrorHandler(err, [&]() { *data = Convert(info)->in_user_data; }); -} - -DUCKDB_V2_ERROR duckdb_v2_table_function_bind_set_bind_data(duckdb_v2_table_function_bind_info_handle info, - duckdb_v2_opaque *data, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(info); - DUCKDB_CHECK_ARG(data); - return WithErrorHandler(err, [&]() { Convert(info)->out_bind_data = *data; }); -} - -DUCKDB_V2_ERROR duckdb_v2_table_function_bind_get_arg_count(duckdb_v2_table_function_bind_info_handle info, - idx_t *count, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(info); - DUCKDB_CHECK_ARG(count); - return WithErrorHandler(err, [&]() { *count = Convert(info)->in_args->size(); }); -} - -DUCKDB_V2_ERROR duckdb_v2_table_function_bind_get_arg_type(duckdb_v2_table_function_bind_info_handle info, idx_t index, - duckdb_v2_logical_type_handle *type, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(info); - DUCKDB_CHECK_ARG(type); - *type = nullptr; - return WithErrorHandler(err, [&]() { - const auto &arguments = *Convert(info)->in_args; - if (index >= arguments.size()) { - throw duckdb::InvalidInputException("Index out of bounds in duckdb_v2_table_function_bind_get_arg_type"); - } - *type = Convert(new duckdb::LogicalType(arguments[index].type())); - }); +DUCKDB_V2_ERROR +duckdb_v2_table_function_set_partition_data_callback(duckdb_v2_table_function_handle function, + duckdb_v2_table_function_partition_data_callback_fn callback, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { Convert(function)->info.partition_data_cb = callback; }); } -DUCKDB_V2_ERROR duckdb_v2_table_function_bind_get_arg_value(duckdb_v2_table_function_bind_info_handle info, idx_t index, - duckdb_v2_value_handle *value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(info); - DUCKDB_CHECK_ARG(value); - *value = nullptr; - return WithErrorHandler(err, [&]() { - const auto &arguments = *Convert(info)->in_args; - if (index >= arguments.size()) { - throw duckdb::InvalidInputException("Index out of bounds in duckdb_v2_table_function_bind_get_arg_value"); - } - *value = Convert(new duckdb::Value(arguments[index])); - }); +DUCKDB_V2_ERROR +duckdb_v2_table_function_set_partitioning_callback(duckdb_v2_table_function_handle function, + duckdb_v2_table_function_partitioning_callback_fn callback, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { Convert(function)->info.partitioning_cb = callback; }); } DUCKDB_V2_ERROR duckdb_v2_table_function_bind_add_result_column(duckdb_v2_table_function_bind_info_handle info, - duckdb_v2_identifier_t name, + const duckdb_v2_identifier_t *name, duckdb_v2_logical_type_handle type, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(info); DUCKDB_CHECK_ARG(name); DUCKDB_CHECK_ARG(type); return WithErrorHandler(err, [&]() { - const auto &column_type = *Convert(type); + auto column_type_ref = Convert(type); + const auto &column_type = *column_type_ref; // A result column carries data, so a wildcard or otherwise incomplete type has no meaning here. if (!column_type.IsComplete()) { throw duckdb::InvalidInputException("Result column type must be a fully defined concrete type"); } auto &bind_info = *Convert(info); - bind_info.out_column_names.emplace_back(Convert(name)); + bind_info.out_column_names.emplace_back(ConvertIdentifierName(name)); bind_info.out_column_types.push_back(column_type); }); } @@ -749,6 +1086,13 @@ DUCKDB_V2_ERROR duckdb_v2_table_function_bind_set_cardinality(duckdb_v2_table_fu }); } +DUCKDB_V2_ERROR duckdb_v2_table_function_bind_set_order_preservation(duckdb_v2_table_function_bind_info_handle info, + DUCKDB_V2_ORDER_PRESERVATION order, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + return WithErrorHandler(err, [&]() { Convert(info)->out_order_preservation = CV2ConvertOrderPreservation(order); }); +} + DUCKDB_V2_ERROR duckdb_v2_table_function_init_global_get_user_data(duckdb_v2_table_function_init_global_info_handle info, void **data, duckdb_v2_error_info_handle *err) { @@ -1027,10 +1371,197 @@ duckdb_v2_table_function_filter_pushdown_get_column_index(duckdb_v2_table_functi }); } -DUCKDB_V2_ERROR duckdb_v2_table_function_register(duckdb_v2_table_function_handle function, - duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR +duckdb_v2_table_function_partition_data_get_user_data(duckdb_v2_table_function_partition_data_info_handle info, + void **data, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_user_data; }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_partition_data_get_bind_data(duckdb_v2_table_function_partition_data_info_handle info, + void **data, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_bind_data; }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_partition_data_get_global_state(duckdb_v2_table_function_partition_data_info_handle info, + void **data, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_global_state; }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_partition_data_get_local_state(duckdb_v2_table_function_partition_data_info_handle info, + void **data, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_local_state; }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_partition_data_requires_batch_index(duckdb_v2_table_function_partition_data_info_handle info, + bool *required, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(required); + return WithErrorHandler(err, [&]() { *required = Convert(info)->in_partition_info->RequiresBatchIndex(); }); +} + +DUCKDB_V2_ERROR duckdb_v2_table_function_partition_data_requires_partition_columns( + duckdb_v2_table_function_partition_data_info_handle info, bool *required, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(required); + return WithErrorHandler(err, [&]() { *required = Convert(info)->in_partition_info->RequiresPartitionColumns(); }); +} + +DUCKDB_V2_ERROR duckdb_v2_table_function_partition_data_get_partition_column_count( + duckdb_v2_table_function_partition_data_info_handle info, idx_t *count, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(count); + return WithErrorHandler(err, [&]() { *count = Convert(info)->in_partition_columns->size(); }); +} + +DUCKDB_V2_ERROR duckdb_v2_table_function_partition_data_get_partition_column_index( + duckdb_v2_table_function_partition_data_info_handle info, idx_t index, idx_t *column_index, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(column_index); + return WithErrorHandler(err, [&]() { + const auto &columns = *Convert(info)->in_partition_columns; + if (index >= columns.size()) { + throw duckdb::InvalidInputException( + "Index out of bounds in duckdb_v2_table_function_partition_data_get_partition_column_index"); + } + *column_index = columns[index]; + }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_partition_data_set_batch_index(duckdb_v2_table_function_partition_data_info_handle info, + idx_t batch_index, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + return WithErrorHandler(err, [&]() { + // ToPipelinePartitionData adds base + 1 and throws an InternalException from BATCH_INCREMENT - 1 on. + if (batch_index >= duckdb::PipelineBuildState::BATCH_INCREMENT - 2) { + throw duckdb::InvalidInputException( + "duckdb_v2_table_function_partition_data_set_batch_index: batch_index %llu must be less than " + "%llu", + batch_index, duckdb::PipelineBuildState::BATCH_INCREMENT - 2); + } + Convert(info)->out_batch_index = batch_index; + }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_partition_data_set_partition_value(duckdb_v2_table_function_partition_data_info_handle info, + idx_t index, duckdb_v2_value_handle value, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(value); + return WithErrorHandler(err, [&]() { + auto &args = *Convert(info); + if (index >= args.out_partition_values.size()) { + throw duckdb::InvalidInputException( + "Index out of bounds in duckdb_v2_table_function_partition_data_set_partition_value"); + } + const auto &expected_type = (*args.in_column_types)[(*args.in_partition_columns)[index]]; + const auto &actual = *Convert(value); + if (actual.type() != expected_type) { + throw duckdb::InvalidInputException( + "duckdb_v2_table_function_partition_data_set_partition_value: expected type %s for the " + "partitioning column at index %llu, got %s", + expected_type.ToString(), index, actual.type().ToString()); + } + args.out_partition_values[index] = actual; + args.out_partition_value_set[index] = true; + }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_partitioning_get_user_data(duckdb_v2_table_function_partitioning_info_handle info, void **data, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_user_data; }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_partitioning_get_bind_data(duckdb_v2_table_function_partitioning_info_handle info, void **data, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_bind_data; }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_partitioning_get_partition_column_count(duckdb_v2_table_function_partitioning_info_handle info, + idx_t *count, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(count); + return WithErrorHandler(err, [&]() { *count = Convert(info)->in_partition_ids->size(); }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_partitioning_get_partition_column_index(duckdb_v2_table_function_partitioning_info_handle info, + idx_t index, idx_t *column_index, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(column_index); + return WithErrorHandler(err, [&]() { + const auto &columns = *Convert(info)->in_partition_ids; + if (index >= columns.size()) { + throw duckdb::InvalidInputException( + "Index out of bounds in duckdb_v2_table_function_partitioning_get_partition_column_index"); + } + *column_index = columns[index]; + }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_partitioning_set_partition_info(duckdb_v2_table_function_partitioning_info_handle info, + DUCKDB_V2_TABLE_PARTITION_INFO partition_info, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + return WithErrorHandler(err, + [&]() { Convert(info)->out_partition_info = CV2ConvertPartitionInfo(partition_info); }); +} + +DUCKDB_V2_ERROR duckdb_v2_connection_register_table_function(duckdb_v2_connection_handle conn, + duckdb_v2_table_function_handle function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(conn); DUCKDB_CHECK_ARG(function); - return WithErrorHandler(err, [&]() { Convert(function)->Register(); }); + return WithErrorHandler(err, [&]() { + auto &context = *Convert(conn)->context; + context.RunFunctionInTransaction([&]() { + auto &self = *Convert(function); + auto info = duckdb::CreateTableFunctionInfo(self.Build(*context.db)); + self.docs.AddTo(self.signature, info); + info.on_conflict = duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + auto &catalog = duckdb::Catalog::GetSystemCatalog(context); + // CreateFunction, unlike CreateTableFunction, adds the overloads to an existing function. + catalog.CreateFunction(context, info); + }); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_extension_register_table_function(duckdb_v2_extension_handle extension, + duckdb_v2_table_function_handle function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(extension); + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { + auto &self = *Convert(function); + auto &loader = GetExtensionLoader(extension); + duckdb::CreateTableFunctionInfo info(self.Build(loader.GetDatabaseInstance())); + self.docs.AddTo(self.signature, info); + info.on_conflict = duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + loader.RegisterFunction(std::move(info)); + }); } DUCKDB_V2_ERROR duckdb_v2_table_function_destroy(duckdb_v2_table_function_handle *function) { @@ -1044,3 +1575,238 @@ DUCKDB_V2_ERROR duckdb_v2_table_function_destroy(duckdb_v2_table_function_handle } }); } + +//---------------------------------------------------------------------------------------------------------------------- +// Batch claiming +//---------------------------------------------------------------------------------------------------------------------- + +DUCKDB_V2_ERROR +duckdb_v2_table_function_set_claim_batch_callback(duckdb_v2_table_function_handle function, + duckdb_v2_table_function_claim_batch_callback_fn callback, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { Convert(function)->info.claim_batch_cb = callback; }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_claim_batch_get_user_data(duckdb_v2_table_function_claim_batch_info_handle info, void **data, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_user_data; }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_claim_batch_get_bind_data(duckdb_v2_table_function_claim_batch_info_handle info, void **data, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_bind_data; }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_claim_batch_get_global_state(duckdb_v2_table_function_claim_batch_info_handle info, + void **data, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_global_state; }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_claim_batch_get_local_state(duckdb_v2_table_function_claim_batch_info_handle info, void **data, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_local_state; }); +} + +DUCKDB_V2_ERROR duckdb_v2_table_function_claim_batch_set_claimed(duckdb_v2_table_function_claim_batch_info_handle info, + bool claimed, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + return WithErrorHandler(err, [&]() { Convert(info)->out_claimed = claimed; }); +} + +//---------------------------------------------------------------------------------------------------------------------- +// File readers +//---------------------------------------------------------------------------------------------------------------------- + +DUCKDB_V2_ERROR +duckdb_v2_table_function_set_get_bind_info_callback(duckdb_v2_table_function_handle function, + duckdb_v2_table_function_get_bind_info_callback_fn callback, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { Convert(function)->info.get_bind_info_cb = callback; }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_get_bind_info_get_user_data(duckdb_v2_table_function_get_bind_info_info_handle info, + void **data, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_user_data; }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_get_bind_info_get_bind_data(duckdb_v2_table_function_get_bind_info_info_handle info, + void **data, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_bind_data; }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_get_bind_info_set_column_identifier(duckdb_v2_table_function_get_bind_info_info_handle info, + idx_t column_index, const idx_t *child_path, + idx_t child_path_length, duckdb_v2_value_handle identifier, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(identifier); + return WithErrorHandler(err, [&]() { + auto &args = *Convert(info); + auto &bind_data = args.bind_data; + if (column_index >= bind_data.column_types.size()) { + throw duckdb::InvalidInputException( + "Index out of bounds in duckdb_v2_table_function_get_bind_info_set_column_identifier: result column " + "%llu has not been declared", + column_index); + } + if (child_path_length > 0 && !child_path) { + throw duckdb::InvalidInputException("duckdb_v2_table_function_get_bind_info_set_column_identifier: the " + "child path cannot be null unless it is empty"); + } + const auto &value = *Convert(identifier); + const auto identifier_type = value.type().id(); + if (value.IsNull() || + (identifier_type != duckdb::LogicalTypeId::INTEGER && identifier_type != duckdb::LogicalTypeId::VARCHAR)) { + throw duckdb::InvalidInputException("duckdb_v2_table_function_get_bind_info_set_column_identifier: an " + "identifier must be a non-NULL INTEGER or VARCHAR, got %s", + value.ToString()); + } + duckdb::vector path(child_path, child_path + child_path_length); + // verify the path addresses a nested field of the column, so a mistake is reported where it is made + auto definition = duckdb::MultiFileColumnDefinition::CreateNested(bind_data.column_names[column_index], + bind_data.column_types[column_index]); + definition.ResolveChildPath(path); + + auto &identifiers = args.out_bind_info.column_identifiers; + for (auto &entry : identifiers) { + if (entry.column_index == column_index && entry.child_path == path) { + entry.identifier = value; + return; + } + } + identifiers.push_back({column_index, std::move(path), value}); + }); +} + +DUCKDB_V2_ERROR +duckdb_v2_table_function_get_bind_info_set_option(duckdb_v2_table_function_get_bind_info_info_handle info, + duckdb_v2_str *key, duckdb_v2_value_handle value, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(key); + DUCKDB_CHECK_ARG(value); + return WithErrorHandler( + err, [&]() { Convert(info)->out_bind_info.options[duckdb::string(Convert(*key))] = *Convert(value); }); +} + +//---------------------------------------------------------------------------------------------------------------------- +// Multi-file functions +//---------------------------------------------------------------------------------------------------------------------- + +DUCKDB_V2_ERROR duckdb_v2_multi_file_function_create(duckdb_v2_factory_handle factory, + duckdb_v2_multi_file_function_handle *function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); + DUCKDB_CHECK_ARG(function); + *function = nullptr; + return WithErrorHandler(err, [&]() { + auto result = duckdb::make_uniq(Convert(factory)->GetDatabase()); + *function = Convert(result.release()); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_multi_file_function_set_name(duckdb_v2_multi_file_function_handle function, + duckdb_v2_str *name, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + DUCKDB_CHECK_ARG(name); + return WithErrorHandler(err, [&]() { Convert(function)->name = duckdb::Identifier(Convert(*name)); }); +} + +DUCKDB_V2_ERROR duckdb_v2_multi_file_function_set_single_file_function(duckdb_v2_multi_file_function_handle function, + duckdb_v2_str *name, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + DUCKDB_CHECK_ARG(name); + return WithErrorHandler(err, + [&]() { Convert(function)->single_file_function = duckdb::Identifier(Convert(*name)); }); +} + +DUCKDB_V2_ERROR duckdb_v2_multi_file_function_set_reader_type(duckdb_v2_multi_file_function_handle function, + duckdb_v2_str *reader_type, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + DUCKDB_CHECK_ARG(reader_type); + return WithErrorHandler(err, [&]() { Convert(function)->reader_type = duckdb::string(Convert(*reader_type)); }); +} + +DUCKDB_V2_ERROR duckdb_v2_multi_file_function_set_file_extension(duckdb_v2_multi_file_function_handle function, + duckdb_v2_str *extension, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + DUCKDB_CHECK_ARG(extension); + return WithErrorHandler(err, [&]() { + auto file_extension = duckdb::string(Convert(*extension)); + if (duckdb::StringUtil::StartsWith(file_extension, ".")) { + throw duckdb::InvalidInputException("The file extension \"%s\" must be given without a leading dot", + file_extension); + } + Convert(function)->file_extension = std::move(file_extension); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_connection_register_multi_file_function(duckdb_v2_connection_handle conn, + duckdb_v2_multi_file_function_handle function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(conn); + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { + auto &self = *Convert(function); + auto &context = *Convert(conn)->context; + context.RunFunctionInTransaction([&]() { + auto &catalog = duckdb::Catalog::GetSystemCatalog(context); + auto single_file = catalog.GetEntry(context, duckdb::CatalogType::TABLE_FUNCTION_ENTRY, + duckdb::Identifier::DefaultSchema(), self.single_file_function, + duckdb::OnEntryNotFound::RETURN_NULL); + duckdb::CreateTableFunctionInfo info(self.Build(*context.db, CopyTableFunctions(single_file))); + info.on_conflict = duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + // CreateFunction, unlike CreateTableFunction, adds the overloads to an existing function. + catalog.CreateFunction(context, info); + }); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_extension_register_multi_file_function(duckdb_v2_extension_handle extension, + duckdb_v2_multi_file_function_handle function, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(extension); + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { + auto &self = *Convert(function); + auto &loader = GetExtensionLoader(extension); + auto single_file = CopyTableFunctions(loader.TryGetTableFunction(self.single_file_function)); + loader.RegisterFunction(self.Build(loader.GetDatabaseInstance(), std::move(single_file))); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_multi_file_function_destroy(duckdb_v2_multi_file_function_handle *function) { + return WithErrorHandler(nullptr, [&]() { + if (!function) { + return; + } + if (*function) { + delete Convert(*function); + *function = nullptr; + } + }); +} diff --git a/src/duckdb/src/main/capi/v2/capi_v2_identifier.cpp b/src/duckdb/src/main/capi/v2/capi_v2_identifier.cpp index ddaf54350..9af919b2b 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_identifier.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_identifier.cpp @@ -3,13 +3,14 @@ using namespace duckdb::capiv2; -DUCKDB_V2_ERROR duckdb_v2_identifier_render_quoted(duckdb_v2_identifier_t name, char *out_text, idx_t out_capacity, - idx_t *out_length, duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR duckdb_v2_identifier_render_quoted(const duckdb_v2_identifier_t *name, char *out_text, + idx_t out_capacity, idx_t *out_length, + duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(name); DUCKDB_CHECK_ARG(out_length); return WithErrorHandler(err, [&]() { *out_length = 0; - auto rendered = duckdb::SQLIdentifier::ToString(duckdb::string(Convert(name))); + auto rendered = duckdb::SQLIdentifier::ToString(duckdb::string(ConvertIdentifierName(name))); FillCallerText(out_text, out_capacity, out_length, rendered, "duckdb_v2_identifier_render_quoted"); }); } diff --git a/src/duckdb/src/main/capi/v2/capi_v2_instance.cpp b/src/duckdb/src/main/capi/v2/capi_v2_instance.cpp new file mode 100644 index 000000000..8225a6e9f --- /dev/null +++ b/src/duckdb/src/main/capi/v2/capi_v2_instance.cpp @@ -0,0 +1,234 @@ +#include "duckdb/main/capi_v2/capi_v2_internal.hpp" + +#include "duckdb/catalog/catalog.hpp" +#include "duckdb/common/file_system.hpp" +#include "duckdb/common/types/value.hpp" +#include "duckdb/main/attached_database.hpp" +#include "duckdb/main/database_manager.hpp" +#include "duckdb/main/database_path_and_type.hpp" +#include "duckdb/main/settings.hpp" +#include "duckdb/parser/parsed_data/attach_info.hpp" + +namespace duckdb { +namespace capiv2 { + +// Routed through the env's DBInstanceCache so every instance shares its path manager: opening one file twice is +// detected across instances, while no instance is memoized. +CV2Instance::CV2Instance(CV2Environment &env, DBConfig &startup_config) + : database(env.cache->CreateEmptyInstance(startup_config)), env(env), factory(*this), config_handle(*this), + file_system(FileSystem::GetFileSystem(*database->instance), QueryContext()) { +} + +DatabaseInstance &CV2InstanceFactory::GetDatabase() { + return *instance.GetDatabase().instance; +} + +void CV2InstanceFactory::WithTransaction(const std::function &fn) { + throw InvalidInputException("this requires a catalog: use the factory of a connection or a context"); +} + +//! Runs `action` inside a transaction on a short-lived connection, the way the ATTACH / DETACH operators run inside +//! their statement's transaction. +template +static void WithTransaction(DuckDB &database, T action) { + auto connection = Connection::CreateInternal(*database.instance); + connection.BeginTransaction(); + try { + action(*connection.context); + } catch (...) { + connection.Rollback(); + throw; + } + connection.Commit(); +} + +void CV2Instance::Attach(const string &path, const Identifier &name, + optional_ptr attach_options, bool make_default) { + if (attach_options && &attach_options->instance != this) { + throw InvalidInputException("the attach options were created from a different instance handle"); + } + WithTransaction(*database, [&](ClientContext &context) { + // Mirrors PhysicalAttach for `ATTACH 'path' AS name (options)`. + auto &instance = *database->instance; + AttachInfo info; + info.path = path; + info.name = name; + if (attach_options) { + info.options = attach_options->options; + } + AttachOptions options(info.options, instance.config.options.access_mode); + options.original_path = path; + if (options.db_type.empty()) { + DBPathAndType::ExtractExtensionPrefix(info.path, options.db_type); + } + if (info.name.empty()) { + info.name = AttachedDatabase::ExtractDatabaseName(info.path, FileSystem::GetFileSystem(instance)); + } + // The host opening a file is not external access: allow it the way the main database path is allowed. + if (options.db_type.empty() && !DBConfig::IsInMemoryDatabase(info.path.c_str()) && + !FileSystem::IsRemoteFile(info.path) && !Settings::Get(instance)) { + instance.config.AddAllowedDatabasePath(info.path); + } + auto &db_manager = DatabaseManager::Get(instance); + auto attached = db_manager.AttachDatabase(context, info, options); + if (make_default) { + db_manager.SetDefaultDatabase(attached->GetName()); + } + }); +} + +//! Throws unless a user database is attached under `name`. +static void CheckAttached(DatabaseInstance &instance, const Identifier &name) { + auto attached = DatabaseManager::Get(instance).GetDatabase(name); + if (!attached || attached->IsSystem()) { + throw InvalidInputException("no database is attached under the name '%s'", name.GetIdentifierName()); + } +} + +void CV2Instance::Detach(const Identifier &name) { + auto &instance = *database->instance; + CheckAttached(instance, name); + WithTransaction(*database, [&](ClientContext &context) { + DatabaseManager::Get(instance).DetachDatabase(context, name, OnEntryNotFound::THROW_EXCEPTION, true); + }); +} + +void CV2Instance::SetDefault(const Identifier &name) { + auto &instance = *database->instance; + CheckAttached(instance, name); + DatabaseManager::Get(instance).SetDefaultDatabase(name); +} + +} // namespace capiv2 +} // namespace duckdb + +using namespace duckdb::capiv2; + +DUCKDB_V2_ERROR duckdb_v2_instance_create(duckdb_v2_environment_handle env, duckdb_v2_instance_handle *out_instance, + duckdb_v2_error_info_handle *err) { + return duckdb_v2_instance_create_with_options(env, nullptr, nullptr, 0, out_instance, err); +} + +DUCKDB_V2_ERROR duckdb_v2_instance_create_with_options(duckdb_v2_environment_handle env, + const duckdb_v2_str *option_names, + const duckdb_v2_str *option_values, idx_t option_count, + duckdb_v2_instance_handle *out_instance, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(env); + DUCKDB_CHECK_ARG(out_instance); + *out_instance = nullptr; + if (option_count > 0 && !option_names) { + return NullArgumentError(err, __func__, "option_names"); + } + if (option_count > 0 && !option_values) { + return NullArgumentError(err, __func__, "option_values"); + } + return WithErrorHandler(err, [&]() { + duckdb::DBConfig startup_config; + for (idx_t i = 0; i < option_count; i++) { + if (IsNullArgument(&option_names[i]) || IsNullArgument(&option_values[i])) { + throw duckdb::InvalidInputException("malformed startup option at index %llu", i); + } + startup_config.SetOptionByName(duckdb::Identifier(duckdb::string(Convert(&option_names[i]))), + duckdb::Value(duckdb::string(Convert(&option_values[i])))); + } + auto *env_wrapper = Convert(env); + auto wrapper = duckdb::make_uniq(*env_wrapper, startup_config); + env_wrapper->instance_count.fetch_add(1, std::memory_order_release); + *out_instance = Convert(wrapper.release()); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_instance_destroy(duckdb_v2_instance_handle *instance) { + return WithErrorHandler(nullptr, [&]() { + if (!instance) { + return; + } + if (*instance) { + const auto *wrapper = Convert(*instance); + auto &env = wrapper->env; + delete wrapper; + env.instance_count.fetch_sub(1, std::memory_order_release); + *instance = nullptr; + } + }); +} + +DUCKDB_V2_ERROR duckdb_v2_instance_attach(duckdb_v2_instance_handle instance, const duckdb_v2_identifier_t *name, + const duckdb_v2_str *path, duckdb_v2_attach_options_handle options, + bool make_default, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(instance); + DUCKDB_CHECK_ARG(path); + if (name) { + DUCKDB_CHECK_ARG(name); + } + return WithErrorHandler(err, [&]() { + auto &wrapper = *Convert(instance); + duckdb::Identifier attach_name = name ? duckdb::Identifier(ConvertIdentifierName(name)) : duckdb::Identifier(); + wrapper.Attach(duckdb::string(Convert(path)), attach_name, Convert(options), make_default); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_attach_options_create(duckdb_v2_instance_handle instance, + duckdb_v2_attach_options_handle *out_options, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(instance); + DUCKDB_CHECK_ARG(out_options); + *out_options = nullptr; + return WithErrorHandler(err, [&]() { + auto options = duckdb::make_uniq(*Convert(instance)); + *out_options = Convert(options.release()); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_attach_options_set(duckdb_v2_attach_options_handle options, const duckdb_v2_identifier_t *key, + const duckdb_v2_str *setting, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(options); + DUCKDB_CHECK_ARG(key); + DUCKDB_CHECK_ARG(setting); + return WithErrorHandler(err, [&]() { + // Keys are unquoted SQL identifiers, which the parser lowercases. + auto lowered = duckdb::StringUtil::Lower(duckdb::string(ConvertIdentifierName(key))); + Convert(options)->options[lowered] = duckdb::Value(duckdb::string(Convert(setting))); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_attach_options_destroy(duckdb_v2_attach_options_handle *options) { + return WithErrorHandler(nullptr, [&]() { + if (!options) { + return; + } + if (*options) { + delete Convert(*options); + *options = nullptr; + } + }); +} + +DUCKDB_V2_ERROR duckdb_v2_instance_detach(duckdb_v2_instance_handle instance, const duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(instance); + DUCKDB_CHECK_ARG(name); + return WithErrorHandler(err, [&]() { + auto &wrapper = *Convert(instance); + wrapper.Detach(duckdb::Identifier(ConvertIdentifierName(name))); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_instance_set_default(duckdb_v2_instance_handle instance, const duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(instance); + DUCKDB_CHECK_ARG(name); + return WithErrorHandler(err, [&]() { + auto &wrapper = *Convert(instance); + wrapper.SetDefault(duckdb::Identifier(ConvertIdentifierName(name))); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_library_version(duckdb_v2_str *out_version, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(out_version); + return WithErrorHandler(err, [&]() { + const auto version = duckdb::DuckDB::LibraryVersion(); + *out_version = duckdb_v2_str {version, std::strlen(version)}; + }); +} diff --git a/src/duckdb/src/main/capi/v2/capi_v2_log_sink.cpp b/src/duckdb/src/main/capi/v2/capi_v2_log_sink.cpp new file mode 100644 index 000000000..760324d21 --- /dev/null +++ b/src/duckdb/src/main/capi/v2/capi_v2_log_sink.cpp @@ -0,0 +1,243 @@ +#include "duckdb/main/capi_v2/capi_v2_internal.hpp" + +#include "duckdb/logging/log_manager.hpp" +#include "duckdb/logging/log_storage.hpp" + +namespace duckdb::capiv2 { + +//! The context of a log entry, beyond what the write callback receives directly +class CV2LogSinkWriteInfo { +public: + explicit CV2LogSinkWriteInfo(const LoggingContext &context) : context(context) { + } + + const LoggingContext &context; +}; + +static auto Convert(duckdb_v2_log_sink_write_info_handle info) -> CV2LogSinkWriteInfo * { + return reinterpret_cast(info); +} +static auto Convert(CV2LogSinkWriteInfo *info) -> duckdb_v2_log_sink_write_info_handle { + return reinterpret_cast(info); +} + +//! Carries nothing yet +class CV2LogSinkFlushInfo {}; + +static auto Convert(CV2LogSinkFlushInfo *info) -> duckdb_v2_log_sink_flush_info_handle { + return reinterpret_cast(info); +} + +//! A log sink that hands every entry to the caller's write callback. Errors the callbacks report are dropped: +//! logging happens inside arbitrary operations, which a sink must not fail. +class CV2CallbackLogSink final : public LogStorage { +public: + CV2CallbackLogSink(string name, duckdb_v2_log_sink_write_callback_fn write_cb, + duckdb_v2_log_sink_flush_callback_fn flush_cb, shared_ptr user_data) + : name(std::move(name)), write_cb(write_cb), flush_cb(flush_cb), user_data(std::move(user_data)) { + } + + const string GetStorageName() override { + return name; + } + + void WriteLogEntry(timestamp_t timestamp, LogLevel level, const string &log_type, const string &log_message, + const RegisteredLoggingContext &context) override { + CV2LogSinkWriteInfo info(context.context); + duckdb_v2_str type {log_type.data(), log_type.size()}; + duckdb_v2_str message {log_message.data(), log_message.size()}; + CV2ErrorInfo err = {}; + auto err_ptr = Convert(&err); + // the values of the level and scope enums match duckdb::LogLevel and duckdb::LogContextScope + write_cb(Convert(&info), GetUserData(), timestamp.value, static_cast(level), + static_cast(context.context.scope), &type, &message, &err_ptr); + } + + void WriteLogEntries(DataChunk &chunk, const RegisteredLoggingContext &context) override { + } + + void FlushAll() override { + if (!flush_cb) { + return; + } + CV2LogSinkFlushInfo info; + CV2ErrorInfo err = {}; + auto err_ptr = Convert(&err); + flush_cb(Convert(&info), GetUserData(), &err_ptr); + } + + void Flush(LoggingTargetTable table) override { + } + + bool IsEnabled(LoggingTargetTable table) override { + return true; + } + +private: + void *GetUserData() const { + return user_data ? user_data->GetData() : nullptr; + } + + const string name; + duckdb_v2_log_sink_write_callback_fn write_cb; + duckdb_v2_log_sink_flush_callback_fn flush_cb; + shared_ptr user_data; +}; + +class CV2LogSink { +public: + explicit CV2LogSink(DatabaseInstance &db) : db(db) { + } + + //! Validates the configuration and registers the sink on `target`. + void Register(DatabaseInstance &target) { + CheckRegistrationTarget(db, target, "log sink"); + if (name.empty()) { + throw InvalidInputException("Log sink name cannot be empty."); + } + if (!write_cb) { + throw InvalidInputException("A write callback must be set for the log sink."); + } + // the log manager looks registered sinks up by their lowercased name + auto sink_name = StringUtil::Lower(name); + if (sink_name == LogConfig::IN_MEMORY_STORAGE_NAME || sink_name == LogConfig::STDOUT_STORAGE_NAME || + sink_name == LogConfig::FILE_STORAGE_NAME) { + throw InvalidInputException("\"%s\" is the name of a log sink DuckDB provides.", name); + } + shared_ptr sink = make_shared_ptr(sink_name, write_cb, flush_cb, user_data); + if (!target.GetLogManager().RegisterLogStorage(sink_name, sink)) { + throw InvalidInputException("A log sink named \"%s\" is already registered.", name); + } + } + +public: + //! The database it was created for: the only one it can be registered on. + DatabaseInstance &db; + string name; + duckdb_v2_log_sink_write_callback_fn write_cb = nullptr; + duckdb_v2_log_sink_flush_callback_fn flush_cb = nullptr; + shared_ptr user_data = nullptr; +}; + +static auto Convert(duckdb_v2_log_sink_handle sink) -> CV2LogSink * { + return reinterpret_cast(sink); +} +static auto Convert(CV2LogSink *sink) -> duckdb_v2_log_sink_handle { + return reinterpret_cast(sink); +} + +static void ReadId(const optional_idx &source, idx_t *id, bool *present) { + *present = source.IsValid(); + if (source.IsValid()) { + *id = source.GetIndex(); + } +} + +} // namespace duckdb::capiv2 + +//---------------------------------------------------------------------------------------------------------------------- +// Public Functions +//---------------------------------------------------------------------------------------------------------------------- + +using namespace duckdb::capiv2; + +DUCKDB_V2_ERROR duckdb_v2_log_sink_create(duckdb_v2_factory_handle factory, duckdb_v2_log_sink_handle *sink, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); + DUCKDB_CHECK_ARG(sink); + *sink = nullptr; + return WithErrorHandler(err, [&]() { + auto result = duckdb::make_uniq(Convert(factory)->GetDatabase()); + *sink = Convert(result.release()); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_log_sink_set_name(duckdb_v2_log_sink_handle sink, const duckdb_v2_identifier_t *name, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(sink); + DUCKDB_CHECK_ARG(name); + return WithErrorHandler(err, [&]() { Convert(sink)->name = duckdb::string(ConvertIdentifierName(name)); }); +} + +DUCKDB_V2_ERROR duckdb_v2_log_sink_set_user_data(duckdb_v2_log_sink_handle sink, duckdb_v2_opaque *data, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(sink); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { + Convert(sink)->user_data = duckdb::make_shared_ptr(data->ptr, data->destroy, data->equals); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_log_sink_set_write_callback(duckdb_v2_log_sink_handle sink, + duckdb_v2_log_sink_write_callback_fn callback, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(sink); + return WithErrorHandler(err, [&]() { Convert(sink)->write_cb = callback; }); +} + +DUCKDB_V2_ERROR duckdb_v2_log_sink_set_flush_callback(duckdb_v2_log_sink_handle sink, + duckdb_v2_log_sink_flush_callback_fn callback, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(sink); + return WithErrorHandler(err, [&]() { Convert(sink)->flush_cb = callback; }); +} + +DUCKDB_V2_ERROR duckdb_v2_log_sink_write_info_get_connection_id(duckdb_v2_log_sink_write_info_handle info, idx_t *id, + bool *present, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(id); + DUCKDB_CHECK_ARG(present); + return WithErrorHandler(err, [&]() { ReadId(Convert(info)->context.connection_id, id, present); }); +} + +DUCKDB_V2_ERROR duckdb_v2_log_sink_write_info_get_transaction_id(duckdb_v2_log_sink_write_info_handle info, idx_t *id, + bool *present, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(id); + DUCKDB_CHECK_ARG(present); + return WithErrorHandler(err, [&]() { ReadId(Convert(info)->context.transaction_id, id, present); }); +} + +DUCKDB_V2_ERROR duckdb_v2_log_sink_write_info_get_query_id(duckdb_v2_log_sink_write_info_handle info, idx_t *id, + bool *present, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(id); + DUCKDB_CHECK_ARG(present); + return WithErrorHandler(err, [&]() { ReadId(Convert(info)->context.query_id, id, present); }); +} + +DUCKDB_V2_ERROR duckdb_v2_log_sink_write_info_get_thread_id(duckdb_v2_log_sink_write_info_handle info, idx_t *id, + bool *present, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(id); + DUCKDB_CHECK_ARG(present); + return WithErrorHandler(err, [&]() { ReadId(Convert(info)->context.thread_id, id, present); }); +} + +DUCKDB_V2_ERROR duckdb_v2_instance_register_log_sink(duckdb_v2_instance_handle instance, duckdb_v2_log_sink_handle sink, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(instance); + DUCKDB_CHECK_ARG(sink); + return WithErrorHandler(err, [&]() { Convert(sink)->Register(*Convert(instance)->GetDatabase().instance); }); +} + +DUCKDB_V2_ERROR duckdb_v2_extension_register_log_sink(duckdb_v2_extension_handle extension, + duckdb_v2_log_sink_handle sink, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(extension); + DUCKDB_CHECK_ARG(sink); + return WithErrorHandler(err, + [&]() { Convert(sink)->Register(GetExtensionLoader(extension).GetDatabaseInstance()); }); +} + +DUCKDB_V2_ERROR duckdb_v2_log_sink_destroy(duckdb_v2_log_sink_handle *sink) { + return WithErrorHandler(nullptr, [&]() { + if (!sink) { + return; + } + if (*sink) { + delete Convert(*sink); + *sink = nullptr; + } + }); +} diff --git a/src/duckdb/src/main/capi/v2/capi_v2_logging.cpp b/src/duckdb/src/main/capi/v2/capi_v2_logging.cpp index 56a4ec522..35287218f 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_logging.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_logging.cpp @@ -21,7 +21,18 @@ static LogLevel ConvertLogLevel(DUCKDB_V2_LOG_LEVEL level) { case DUCKDB_V2_LOG_LEVEL_FATAL: return LogLevel::LOG_FATAL; default: - throw InvalidInputException("unknown log level passed to duckdb_v2_context_log"); + throw InvalidInputException("'%d' is not a log level", static_cast(level)); + } +} + +static void WriteLog(Logger &logger, DUCKDB_V2_LOG_LEVEL level, const duckdb_v2_str *log_type, + const duckdb_v2_str *message) { + const auto log_level = ConvertLogLevel(level); + // ShouldLog/WriteLog take a C string, so the borrowed view has to be materialized. + const string type(Convert(log_type)); + const auto *type_name = type.empty() ? DefaultLogType::NAME : type.c_str(); + if (logger.ShouldLog(type_name, log_level)) { + logger.WriteLog(type_name, log_level, string(Convert(message))); } } @@ -34,18 +45,27 @@ static LogLevel ConvertLogLevel(DUCKDB_V2_LOG_LEVEL level) { using namespace duckdb::capiv2; -DUCKDB_V2_ERROR duckdb_v2_context_log(duckdb_v2_context_handle ctx, DUCKDB_V2_LOG_LEVEL level, duckdb_v2_str log_type, - duckdb_v2_str message, duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR duckdb_v2_context_log(duckdb_v2_context_handle ctx, DUCKDB_V2_LOG_LEVEL level, + const duckdb_v2_str *log_type, const duckdb_v2_str *message, + duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(ctx); + return WithErrorHandler(err, + [&]() { WriteLog(duckdb::Logger::Get(Convert(ctx)->context), level, log_type, message); }); +} + +DUCKDB_V2_ERROR duckdb_v2_instance_log(duckdb_v2_instance_handle instance, DUCKDB_V2_LOG_LEVEL level, + const duckdb_v2_str *log_type, const duckdb_v2_str *message, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(instance); return WithErrorHandler(err, [&]() { - const auto log_level = ConvertLogLevel(level); - // ShouldLog/WriteLog take a C string, so the borrowed view has to be materialized. - const duckdb::string type(Convert(log_type)); - const auto *type_name = type.empty() ? duckdb::DefaultLogType::NAME : type.c_str(); - - auto &logger = duckdb::Logger::Get(*Convert(ctx)); - if (logger.ShouldLog(type_name, log_level)) { - logger.WriteLog(type_name, log_level, duckdb::string(Convert(message))); - } + WriteLog(duckdb::Logger::Get(*Convert(instance)->GetDatabase().instance), level, log_type, message); }); } + +DUCKDB_V2_ERROR duckdb_v2_connection_log(duckdb_v2_connection_handle conn, DUCKDB_V2_LOG_LEVEL level, + const duckdb_v2_str *log_type, const duckdb_v2_str *message, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(conn); + return WithErrorHandler( + err, [&]() { WriteLog(duckdb::Logger::Get(*Convert(conn)->context), level, log_type, message); }); +} diff --git a/src/duckdb/src/main/capi/v2/capi_v2_logical_type.cpp b/src/duckdb/src/main/capi/v2/capi_v2_logical_type.cpp index a8a5ab120..78def864a 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_logical_type.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_logical_type.cpp @@ -1,9 +1,10 @@ #include "duckdb/main/capi_v2/capi_v2_internal.hpp" #include "duckdb/catalog/catalog_entry/type_catalog_entry.hpp" +#include "duckdb/catalog/default/default_types.hpp" #include "duckdb/catalog/catalog_entry_retriever.hpp" #include "duckdb/common/enum_util.hpp" -#include "duckdb/common/extra_type_info.hpp" +#include "duckdb/common/logical_type_info.hpp" #include "duckdb/common/types/geometry_crs.hpp" #include "duckdb/common/types/string_type.hpp" #include "duckdb/common/types/vector.hpp" @@ -118,6 +119,35 @@ LogicalType BindTypeByNameV2(ClientContext &context, const QualifiedName &name, return type_entry.constructors.Bind(context, type_entry.user_type, args); } +// Binds a built-in type by name, without a client: constructors that consult the catalog (collations, coordinate +// systems) skip that check or refuse. +LogicalType BindBuiltinTypeV2(const QualifiedName &name, const vector &args) { + if (name.Path().size() > 1) { + throw InvalidInputException("a qualified type name requires a catalog: use the factory of a connection or a " + "context"); + } + vector> params; + params.reserve(args.size()); + for (auto &arg : args) { + params.emplace_back(arg.GetName(), arg.GetValue()); + } + auto type = DefaultTypeGenerator::TryDefaultBind(name.Name().GetIdentifierName(), params); + if (type.id() == LogicalTypeId::INVALID) { + throw InvalidInputException("type \"%s\" is not a built-in type", name.Name().GetIdentifierName()); + } + return type; +} + +// Binds a type by name in the factory's scope: the client's catalog, or the built-in types only. +LogicalType BindTypeV2(CV2Factory &factory, const QualifiedName &name, const vector &args) { + if (!factory.TryGetClientContext()) { + return BindBuiltinTypeV2(name, args); + } + LogicalType result; + factory.WithTransaction([&](ClientContext &context) { result = BindTypeByNameV2(context, name, args); }); + return result; +} + // The value-parameter view of a bound type: the exact dual of // logical_type_create. Kinds without retained parameters report 0. idx_t TypeParamCount(const LogicalType &type) { @@ -181,6 +211,27 @@ Value TypeParamValue(const LogicalType &type, idx_t index, duckdb_v2_identifier_ } } // anonymous namespace + +auto ResolveCommonType(CV2Factory &factory, const vector &types) -> LogicalType { + D_ASSERT(!types.empty()); + auto context = factory.TryGetClientContext(); + auto result = types[0]; + for (idx_t i = 1; i < types.size(); i++) { + auto &type = types[i]; + if (context) { + result = LogicalType::MaxLogicalType(*context, result, type); + continue; + } + LogicalType max_type; + if (!LogicalType::DefaultTryGetMaxLogicalTypeUnchecked(result, type, max_type)) { + throw NotImplementedException("Cannot combine types %s and %s - an explicit cast is required", + result.ToString(), type.ToString()); + } + result = std::move(max_type); + } + return result; +} + } // namespace duckdb::capiv2 // --------------------------------------------------------------------------- @@ -204,7 +255,7 @@ static duckdb::vector CollectTypeArgsV2(const duckdb_v2_id if (!param_values[i]) { throw duckdb::InvalidInputException("null parameter value at index %llu", i); } - args.emplace_back(duckdb::string(Convert(param_name)), *Convert(param_values[i])); + args.emplace_back(duckdb::string(ConvertIdentifierName(param_name)), *Convert(param_values[i])); } return args; } @@ -212,7 +263,7 @@ static duckdb::vector CollectTypeArgsV2(const duckdb_v2_id // A parameterless call instantiates the primitive directly; with parameters // the id resolves to its canonical name and binds through the same path as // create_type_from_name. -static void CreateLogicalTypeFromIdV2(duckdb::ClientContext &context, DUCKDB_V2_LOGICAL_TYPE_ID type_id, +static void CreateLogicalTypeFromIdV2(CV2Factory &factory, DUCKDB_V2_LOGICAL_TYPE_ID type_id, const duckdb_v2_identifier_t *param_names, const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, const char *fn) { @@ -228,81 +279,54 @@ static void CreateLogicalTypeFromIdV2(duckdb::ClientContext &context, DUCKDB_V2_ if (!IsPrimitiveCreatable(id)) { throw duckdb::InvalidInputException("%s requires type parameters for this type id", fn); } - *out_type = Convert(new duckdb::LogicalType(id)); + *out_type = Convert(duckdb::LogicalType(id)); return; } // Bind errors propagate. auto args = CollectTypeArgsV2(param_names, param_values, param_count); - auto bound = - BindTypeByNameV2(context, duckdb::QualifiedName(duckdb::Identifier(duckdb::EnumUtil::ToString(id))), args); - *out_type = Convert(new duckdb::LogicalType(std::move(bound))); + auto bound = BindTypeV2(factory, duckdb::QualifiedName(duckdb::Identifier(duckdb::EnumUtil::ToString(id))), args); + *out_type = Convert(std::move(bound)); } -DUCKDB_V2_ERROR duckdb_v2_context_create_type_from_id(duckdb_v2_context_handle ctx, DUCKDB_V2_LOGICAL_TYPE_ID type_id, +DUCKDB_V2_ERROR duckdb_v2_factory_create_type_from_id(duckdb_v2_factory_handle factory, + DUCKDB_V2_LOGICAL_TYPE_ID type_id, const duckdb_v2_identifier_t *param_names, const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, duckdb_v2_error_info_handle *err) { - static const char *fn = "duckdb_v2_context_create_type_from_id"; - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_type); - return WithErrorHandler(err, [&]() { - // A context arrives with the lock held and a transaction active. - CreateLogicalTypeFromIdV2(*Convert(ctx), type_id, param_names, param_values, param_count, out_type, fn); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_connection_create_type_from_id(duckdb_v2_connection_handle conn, - DUCKDB_V2_LOGICAL_TYPE_ID type_id, - const duckdb_v2_identifier_t *param_names, - const duckdb_v2_value_handle *param_values, idx_t param_count, - duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err) { - static const char *fn = "duckdb_v2_connection_create_type_from_id"; - DUCKDB_CHECK_ARG(conn); + static const char *fn = "duckdb_v2_factory_create_type_from_id"; + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_type); return WithErrorHandler(err, [&]() { - auto &context = *Convert(conn)->context; - context.RunFunctionInTransaction([&]() { - CreateLogicalTypeFromIdV2(context, type_id, param_names, param_values, param_count, out_type, fn); - }); + CreateLogicalTypeFromIdV2(*Convert(factory), type_id, param_names, param_values, param_count, out_type, fn); }); } -static void CreateLogicalTypeFromTextV2(duckdb::ClientContext &context, duckdb_v2_str text, +static void CreateLogicalTypeFromTextV2(CV2Factory &factory, const duckdb_v2_str *text, duckdb_v2_logical_type_handle *out_type) { *out_type = nullptr; // Parse and bind errors propagate. auto string = duckdb::string(Convert(text)); - auto parsed = duckdb::TransformStringToLogicalType(string, context); - *out_type = Convert(new duckdb::LogicalType(std::move(parsed))); + if (!factory.TryGetClientContext()) { + *out_type = Convert(duckdb::TypeManager::Get(factory.GetDatabase()).ParseLogicalType(string)); + return; + } + duckdb::LogicalType parsed; + factory.WithTransaction( + [&](duckdb::ClientContext &context) { parsed = duckdb::TransformStringToLogicalType(string, context); }); + *out_type = Convert(std::move(parsed)); } -DUCKDB_V2_ERROR duckdb_v2_context_create_type_from_text(duckdb_v2_context_handle ctx, duckdb_v2_str text, +DUCKDB_V2_ERROR duckdb_v2_factory_create_type_from_text(duckdb_v2_factory_handle factory, const duckdb_v2_str *text, duckdb_v2_logical_type_handle *out_type, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_type); DUCKDB_CHECK_ARG(text); - return WithErrorHandler(err, [&]() { - // A context arrives with the lock held and a transaction active. - CreateLogicalTypeFromTextV2(*Convert(ctx), text, out_type); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_connection_create_type_from_text(duckdb_v2_connection_handle conn, duckdb_v2_str text, - duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(out_type); - DUCKDB_CHECK_ARG(text); - return WithErrorHandler(err, [&]() { - auto &context = *Convert(conn)->context; - context.RunFunctionInTransaction([&]() { CreateLogicalTypeFromTextV2(context, text, out_type); }); - }); + return WithErrorHandler(err, [&]() { CreateLogicalTypeFromTextV2(*Convert(factory), text, out_type); }); } -static void CreateLogicalTypeFromArgsV2(duckdb::ClientContext &context, duckdb_v2_qname_handle name, +static void CreateLogicalTypeFromArgsV2(CV2Factory &factory, duckdb_v2_qname_handle name, const duckdb_v2_identifier_t *param_names, const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type) { @@ -312,37 +336,20 @@ static void CreateLogicalTypeFromArgsV2(duckdb::ClientContext &context, duckdb_v *out_type = nullptr; auto args = CollectTypeArgsV2(param_names, param_values, param_count); // Bind errors propagate. - auto bound = BindTypeByNameV2(context, *Convert(name), args); - *out_type = Convert(new duckdb::LogicalType(std::move(bound))); + auto bound = BindTypeV2(factory, *Convert(name), args); + *out_type = Convert(std::move(bound)); } -DUCKDB_V2_ERROR duckdb_v2_context_create_type_from_name(duckdb_v2_context_handle ctx, duckdb_v2_qname_handle name, +DUCKDB_V2_ERROR duckdb_v2_factory_create_type_from_name(duckdb_v2_factory_handle factory, duckdb_v2_qname_handle name, const duckdb_v2_identifier_t *param_names, const duckdb_v2_value_handle *param_values, idx_t param_count, duckdb_v2_logical_type_handle *out_type, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_type); DUCKDB_CHECK_ARG(name); return WithErrorHandler(err, [&]() { - // A context arrives with the lock held and a transaction active. - CreateLogicalTypeFromArgsV2(*Convert(ctx), name, param_names, param_values, param_count, out_type); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_connection_create_type_from_name(duckdb_v2_connection_handle conn, - duckdb_v2_qname_handle name, - const duckdb_v2_identifier_t *param_names, - const duckdb_v2_value_handle *param_values, - idx_t param_count, duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(out_type); - DUCKDB_CHECK_ARG(name); - return WithErrorHandler(err, [&]() { - auto &context = *Convert(conn)->context; - context.RunFunctionInTransaction( - [&]() { CreateLogicalTypeFromArgsV2(context, name, param_names, param_values, param_count, out_type); }); + CreateLogicalTypeFromArgsV2(*Convert(factory), name, param_names, param_values, param_count, out_type); }); } @@ -351,9 +358,8 @@ DUCKDB_V2_ERROR duckdb_v2_logical_type_copy(duckdb_v2_logical_type_handle type, DUCKDB_CHECK_ARG(type); DUCKDB_CHECK_ARG(out_type); return WithErrorHandler(err, [&]() { - auto *lt = Convert(type); - auto *copy = new duckdb::LogicalType(*lt); - *out_type = Convert(copy); + auto lt = Convert(type); + *out_type = Convert(*lt); }); } @@ -363,7 +369,7 @@ DUCKDB_V2_ERROR duckdb_v2_logical_type_destroy(duckdb_v2_logical_type_handle *ty return; } if (*type) { - delete Convert(*type); + TakeOwnership(*type); *type = nullptr; } }); @@ -379,10 +385,10 @@ DUCKDB_V2_ERROR duckdb_v2_logical_type_is_equal(duckdb_v2_logical_type_handle le DUCKDB_CHECK_ARG(right); DUCKDB_CHECK_ARG(result); return WithErrorHandler(err, [&]() { - const auto &left_lt = *Convert(left); - const auto &right_lt = *Convert(right); + auto left_lt = Convert(left); + auto right_lt = Convert(right); - *result = left_lt == right_lt; + *result = *left_lt == *right_lt; }); } @@ -398,10 +404,10 @@ DUCKDB_V2_ERROR duckdb_v2_logical_type_get_name(duckdb_v2_logical_type_handle ty DUCKDB_CHECK_ARG(type); DUCKDB_CHECK_ARG(out_name); return WithErrorHandler(err, [&]() { - auto *lt = Convert(type); - auto info = lt->AuxInfo(); - if (info && !info->alias.empty()) { - *out_name = Convert(info->alias); + auto lt = Convert(type); + auto &info = lt->GetTypeInfo(); + if (!info.alias.empty()) { + *out_name = Convert(info.alias); return; } // Canonical fixed name of the id: static storage, so the borrowed @@ -429,7 +435,10 @@ DUCKDB_V2_ERROR duckdb_v2_logical_type_get_param_count(duckdb_v2_logical_type_ha duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(type); DUCKDB_CHECK_ARG(out_count); - return WithErrorHandler(err, [&]() { *out_count = TypeParamCount(*Convert(type)); }); + return WithErrorHandler(err, [&]() { + auto lt = Convert(type); + *out_count = TypeParamCount(*lt); + }); } DUCKDB_V2_ERROR duckdb_v2_logical_type_get_param(duckdb_v2_logical_type_handle type, idx_t index, @@ -441,7 +450,8 @@ DUCKDB_V2_ERROR duckdb_v2_logical_type_get_param(duckdb_v2_logical_type_handle t return WithErrorHandler(err, [&]() { *out_name = duckdb_v2_identifier_t {nullptr, 0}; *out_value = nullptr; - auto < = *Convert(type); + auto lt_ref = Convert(type); + auto < = *lt_ref; if (index >= TypeParamCount(lt)) { throw duckdb::InvalidInputException("parameter index out of range in duckdb_v2_logical_type_get_param"); } @@ -456,39 +466,53 @@ namespace { // The alias keeps the base type's internal representation; only the name and // its catalog identity change. -duckdb::LogicalType AliasOf(duckdb_v2_logical_type_handle base_type, duckdb_v2_identifier_t alias_name, +duckdb::LogicalType AliasOf(duckdb_v2_logical_type_handle base_type, const duckdb_v2_identifier_t *alias_name, duckdb_v2_logical_type_handle *out_type) { *out_type = nullptr; - if (alias_name.len == 0) { + auto name = ConvertIdentifierName(alias_name); + if (name.empty()) { throw duckdb::InvalidInputException("alias name cannot be empty"); } - return Convert(base_type)->WithAlias(std::string(Convert(alias_name))); + return Convert(base_type)->WithAlias(std::string(name)); } } // namespace -DUCKDB_V2_ERROR duckdb_v2_context_create_type_with_alias(duckdb_v2_context_handle ctx, +DUCKDB_V2_ERROR duckdb_v2_factory_create_type_with_alias(duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle base_type, - duckdb_v2_identifier_t alias_name, + const duckdb_v2_identifier_t *alias_name, duckdb_v2_logical_type_handle *out_type, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(base_type); DUCKDB_CHECK_ARG(alias_name); DUCKDB_CHECK_ARG(out_type); - return WithErrorHandler( - err, [&]() { *out_type = Convert(new duckdb::LogicalType(AliasOf(base_type, alias_name, out_type))); }); + return WithErrorHandler(err, [&]() { *out_type = Convert(AliasOf(base_type, alias_name, out_type)); }); } -DUCKDB_V2_ERROR duckdb_v2_connection_create_type_with_alias(duckdb_v2_connection_handle conn, - duckdb_v2_logical_type_handle base_type, - duckdb_v2_identifier_t alias_name, - duckdb_v2_logical_type_handle *out_type, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(base_type); - DUCKDB_CHECK_ARG(alias_name); +DUCKDB_V2_ERROR duckdb_v2_factory_create_common_type(duckdb_v2_factory_handle factory, + const duckdb_v2_logical_type_handle *types, idx_t type_count, + duckdb_v2_logical_type_handle *out_type, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_type); - return WithErrorHandler( - err, [&]() { *out_type = Convert(new duckdb::LogicalType(AliasOf(base_type, alias_name, out_type))); }); + *out_type = nullptr; + return WithErrorHandler(err, [&]() { + if (type_count == 0 || !types) { + throw duckdb::InvalidInputException("cannot resolve the common type of an empty set"); + } + duckdb::vector input_types; + input_types.reserve(type_count); + for (idx_t i = 0; i < type_count; i++) { + if (!types[i]) { + throw duckdb::InvalidInputException("type %llu is null", i); + } + auto type = Convert(types[i]); + if (type->id() == duckdb::LogicalTypeId::ANY) { + throw duckdb::InvalidInputException("cannot resolve the common type of ANY"); + } + input_types.push_back(*type); + } + *out_type = Convert(ResolveCommonType(*Convert(factory), input_types)); + }); } diff --git a/src/duckdb/src/main/capi/v2/capi_v2_option.cpp b/src/duckdb/src/main/capi/v2/capi_v2_option.cpp index ffc3c1367..cf1215c79 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_option.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_option.cpp @@ -2,20 +2,6 @@ using namespace duckdb::capiv2; -DUCKDB_V2_ERROR duckdb_v2_option_create(duckdb_v2_identifier_t name, duckdb_v2_str setting, - duckdb_v2_option_handle *out_option, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(name); - DUCKDB_CHECK_ARG(setting); - DUCKDB_CHECK_ARG(out_option); - *out_option = nullptr; - return WithErrorHandler(err, [&]() { - auto wrapper = duckdb::make_uniq(); - wrapper->name = duckdb::Identifier(Convert(name)); - wrapper->setting = Convert(setting); - *out_option = Convert(wrapper.release()); - }); -} - DUCKDB_V2_ERROR duckdb_v2_option_destroy(duckdb_v2_option_handle *option) { return WithErrorHandler(nullptr, [&]() { if (!option) { @@ -35,18 +21,12 @@ DUCKDB_V2_ERROR duckdb_v2_option_get_name(duckdb_v2_option_handle option, duckdb return WithErrorHandler(err, [&]() { *out_name = Convert(Convert(option)->name); }); } -DUCKDB_V2_ERROR duckdb_v2_option_get_setting(duckdb_v2_option_handle option, duckdb_v2_str *out_setting, - duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR duckdb_v2_option_get_default_value(duckdb_v2_option_handle option, duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(option); - DUCKDB_CHECK_ARG(out_setting); - return WithErrorHandler(err, [&]() { *out_setting = Convert(Convert(option)->setting); }); -} - -DUCKDB_V2_ERROR duckdb_v2_option_get_default_setting(duckdb_v2_option_handle option, duckdb_v2_str *out_default_setting, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(option); - DUCKDB_CHECK_ARG(out_default_setting); - return WithErrorHandler(err, [&]() { *out_default_setting = Convert(Convert(option)->default_setting); }); + DUCKDB_CHECK_ARG(out_value); + *out_value = nullptr; + return WithErrorHandler(err, [&]() { *out_value = Convert(new duckdb::Value(Convert(option)->default_value)); }); } DUCKDB_V2_ERROR duckdb_v2_option_get_description(duckdb_v2_option_handle option, duckdb_v2_str *out_description, @@ -56,12 +36,31 @@ DUCKDB_V2_ERROR duckdb_v2_option_get_description(duckdb_v2_option_handle option, return WithErrorHandler(err, [&]() { *out_description = Convert(Convert(option)->description); }); } -DUCKDB_V2_ERROR duckdb_v2_option_get_target_scope(duckdb_v2_option_handle option, - DUCKDB_V2_OPTION_TARGET_SCOPE *out_target_scope, - duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR duckdb_v2_option_supports_scope(duckdb_v2_option_handle option, DUCKDB_V2_SETTING_SCOPE scope, + bool *out_supported, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(option); - DUCKDB_CHECK_ARG(out_target_scope); - return WithErrorHandler(err, [&]() { *out_target_scope = Convert(option)->target_scope; }); + DUCKDB_CHECK_ARG(out_supported); + return WithErrorHandler(err, [&]() { + const auto &wrapper = *Convert(option); + switch (scope) { + case DUCKDB_V2_SETTING_SCOPE_GLOBAL: + *out_supported = wrapper.supports_global; + break; + case DUCKDB_V2_SETTING_SCOPE_SESSION: + *out_supported = wrapper.supports_session; + break; + default: + *out_supported = wrapper.supports_global || wrapper.supports_session; + break; + } + }); +} + +DUCKDB_V2_ERROR duckdb_v2_option_get_default_scope(duckdb_v2_option_handle option, DUCKDB_V2_SETTING_SCOPE *out_scope, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(option); + DUCKDB_CHECK_ARG(out_scope); + return WithErrorHandler(err, [&]() { *out_scope = Convert(option)->default_scope; }); } DUCKDB_V2_ERROR duckdb_v2_option_get_alias_count(duckdb_v2_option_handle option, idx_t *out_count, @@ -84,3 +83,7 @@ DUCKDB_V2_ERROR duckdb_v2_option_get_alias(duckdb_v2_option_handle option, idx_t *out_alias = Convert(wrapper.aliases[index]); }); } + +// --------------------------------------------------------------------------- +// Context options: the connection's cascade, from inside DuckDB +// --------------------------------------------------------------------------- diff --git a/src/duckdb/src/main/capi/v2/capi_v2_prepared_statement.cpp b/src/duckdb/src/main/capi/v2/capi_v2_prepared_statement.cpp index 11fc467ac..d36b021ce 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_prepared_statement.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_prepared_statement.cpp @@ -48,15 +48,8 @@ DUCKDB_V2_ERROR duckdb_v2_prepared_statement_create(duckdb_v2_connection_handle DUCKDB_CHECK_ARG(statement); return WithErrorHandler(err, [&]() { auto *connection = Convert(conn); - // Preparing runs the engine's query cleanup, which would cancel a live stream, so - // refuse first. Unlike execute this only checks the slot is free: preparing - // produces no result, so it never claims it. - if (GetBusySlot(*connection->context)->owner.load() != nullptr) { - throw duckdb::ResourceInUseException( - "connection has a live result; drain, destroy, or interrupt it before preparing a statement " - "(or open another connection)"); - } - // Borrowed, not consumed: prepare a copy so the caller keeps the original. + // Borrowed, not consumed: prepare a copy so the caller keeps the original. The engine refuses a prepare while + // another statement on the connection is running. auto prepared = connection->context->Prepare(Convert(statement)->Copy()); if (prepared->HasError()) { // Prepare reports failure on the returned object rather than throwing; re-throw @@ -67,8 +60,8 @@ DUCKDB_V2_ERROR duckdb_v2_prepared_statement_create(duckdb_v2_connection_handle if (require_cacheable && !PreparedReusesPlanV2(prepared->GetStatementProperties())) { throw duckdb::InvalidInputException( "prepared_statement_create(require_cacheable): this plan is re-bound on every execution (an unresolved " - "parameter type, or a table scan), so executing it is no faster than statement_execute; prepare " - "without require_cacheable to accept that"); + "parameter type, a table scan, or a statement that expands into several), so executing it is no faster " + "than statement_execute; prepare without require_cacheable to accept that"); } auto wrapper = duckdb::make_uniq(); wrapper->context = connection->context; @@ -95,7 +88,30 @@ DUCKDB_V2_ERROR duckdb_v2_prepared_statement_execute(duckdb_v2_prepared_statemen // retained on the handle between calls. duckdb::identifier_map_t values; BuildParameterMap(parameter_names, parameter_values, parameter_count, __func__, values); - *out_result = ExecutePreparedStatementV2(wrapper->context, *wrapper->prepared, values); + *out_result = + Convert(ExecutePreparedStatementV2(wrapper->context, *wrapper->prepared, values, nullptr).release()); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_prepared_statement_execute_arrow(duckdb_v2_prepared_statement_handle prepared, + const duckdb_v2_identifier_t *parameter_names, + const duckdb_v2_value_handle *parameter_values, + idx_t parameter_count, idx_t batch_size, + duckdb_v2_arrow_result_handle *out_result, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(out_result); + *out_result = nullptr; + DUCKDB_CHECK_ARG(prepared); + if (parameter_count > 0 && !parameter_values) { + return NullArgumentError(err, __func__, "parameter_values"); + } + return WithErrorHandler(err, [&]() { + auto *wrapper = Convert(prepared); + duckdb::identifier_map_t values; + BuildParameterMap(parameter_names, parameter_values, parameter_count, __func__, values); + *out_result = ConvertArrowResult( + ExecutePreparedStatementV2(wrapper->context, *wrapper->prepared, values, ArrowResultFormat(batch_size)) + .release()); }); } diff --git a/src/duckdb/src/main/capi/v2/capi_v2_qname.cpp b/src/duckdb/src/main/capi/v2/capi_v2_qname.cpp index a5333ac0c..03392a84d 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_qname.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_qname.cpp @@ -26,13 +26,17 @@ static void CheckQNameParts(const QualifiedName &name, const char *function_name using namespace duckdb::capiv2; -DUCKDB_V2_ERROR duckdb_v2_qname_parse(duckdb_v2_str text, duckdb_v2_qname_handle *out_name, +DUCKDB_V2_ERROR duckdb_v2_qname_parse(const duckdb_v2_str *text, duckdb_v2_qname_handle *out_name, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(text); DUCKDB_CHECK_ARG(out_name); *out_name = nullptr; return WithErrorHandler(err, [&]() { - auto parsed = duckdb::QualifiedName::Parse(duckdb::string(Convert(text))); + duckdb::QualifiedName parsed; + duckdb::string error; + if (!duckdb::QualifiedName::TryParse(duckdb::string(ConvertIdentifierName(text)), parsed, error)) { + throw duckdb::InvalidInputException("%s in duckdb_v2_qname_parse.", error); + } CheckQNameParts(parsed, "duckdb_v2_qname_parse"); *out_name = Convert(new duckdb::QualifiedName(std::move(parsed))); }); @@ -54,9 +58,10 @@ DUCKDB_V2_ERROR duckdb_v2_qname_create(const duckdb_v2_identifier_t *parts, idx_ // The last part is the object name; everything before it is the qualification. duckdb::vector qualification; for (idx_t i = 0; i + 1 < part_count; i++) { - qualification.push_back(duckdb::Identifier(Convert(parts[i]))); + qualification.push_back(duckdb::Identifier(ConvertIdentifierName(parts[i]))); } - auto name = duckdb::QualifiedName(std::move(qualification), duckdb::Identifier(Convert(parts[part_count - 1]))); + auto name = duckdb::QualifiedName(std::move(qualification), + duckdb::Identifier(ConvertIdentifierName(parts[part_count - 1]))); CheckQNameParts(name, "duckdb_v2_qname_create"); *out_name = Convert(new duckdb::QualifiedName(std::move(name))); }); diff --git a/src/duckdb/src/main/capi/v2/capi_v2_replacement_scan.cpp b/src/duckdb/src/main/capi/v2/capi_v2_replacement_scan.cpp index ed5100be3..7b37ba0d7 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_replacement_scan.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_replacement_scan.cpp @@ -114,7 +114,8 @@ static auto CV2ReplacementScanTrampoline(ClientContext &context, ReplacementScan CV2ErrorInfo err = {}; auto err_ptr = Convert(&err); - scan_data.callback(Convert(&args), Convert(&context), &err_ptr); + CV2CallbackContext callback_context(context); + scan_data.callback(Convert(&args), Convert(&callback_context), &err_ptr); if (err.HasError()) { err.ThrowAsException(); @@ -124,7 +125,12 @@ static auto CV2ReplacementScanTrampoline(ClientContext &context, ReplacementScan class CV2ReplacementScan { public: - void Register() { + explicit CV2ReplacementScan(DatabaseInstance &db) : db(db) { + } + + //! Validates the configuration and builds the scan to register on `target`. + ReplacementScan Build(DatabaseInstance &target) { + CheckRegistrationTarget(db, target, "replacement scan"); if (!info.callback) { throw InvalidInputException("Callback must be set for the replacement scan."); } @@ -136,44 +142,16 @@ class CV2ReplacementScan { auto data = make_uniq(); data->callback = info.callback; data->user_data = info.user_data; - RegisterScan(ReplacementScan(CV2ReplacementScanTrampoline, std::move(data))); + return ReplacementScan(CV2ReplacementScanTrampoline, std::move(data)); } - virtual ~CV2ReplacementScan() = default; - virtual void RegisterScan(ReplacementScan scan) = 0; - public: + //! The database it was created for: the only one it can be registered on. + DatabaseInstance &db; CV2ReplacementScanData info; bool registered = false; }; -class CV2ConnectionReplacementScan : public CV2ReplacementScan { -public: - explicit CV2ConnectionReplacementScan(Connection &connection) : connection(connection) { - } - - void RegisterScan(ReplacementScan scan) override { - // Connection-scoped: this touches only the connection's own state, never the shared database config. - connection.context->config.replacement_scans.push_back(make_shared_ptr(std::move(scan))); - } - -private: - Connection &connection; -}; - -class CV2DatabaseReplacementScan : public CV2ReplacementScan { -public: - explicit CV2DatabaseReplacementScan(DatabaseInstance &db) : db(db) { - } - - void RegisterScan(ReplacementScan scan) override { - DBConfig::GetConfig(db).replacement_scans.push_back(std::move(scan)); - } - -private: - DatabaseInstance &db; -}; - static auto Convert(duckdb_v2_replacement_scan_handle scan) -> CV2ReplacementScan * { return reinterpret_cast(scan); } @@ -189,42 +167,15 @@ static auto Convert(CV2ReplacementScan *scan) -> duckdb_v2_replacement_scan_hand using namespace duckdb::capiv2; -DUCKDB_V2_ERROR duckdb_v2_replacement_scan_create_with_connection(duckdb_v2_connection_handle connection, - duckdb_v2_replacement_scan_handle *out_scan, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(connection); - DUCKDB_CHECK_ARG(out_scan); - *out_scan = nullptr; - return WithErrorHandler(err, [&]() { - auto &conn = *Convert(connection); - auto scan = duckdb::make_uniq(conn); - *out_scan = Convert(scan.release()); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_replacement_scan_create_with_database(duckdb_v2_database_handle database, - duckdb_v2_replacement_scan_handle *out_scan, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(database); - DUCKDB_CHECK_ARG(out_scan); - *out_scan = nullptr; - return WithErrorHandler(err, [&]() { - auto &db = *Convert(database)->database->instance; - auto scan = duckdb::make_uniq(db); - *out_scan = Convert(scan.release()); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_replacement_scan_create_with_extension(duckdb_v2_extension_handle extension, - duckdb_v2_replacement_scan_handle *out_scan, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(extension); - DUCKDB_CHECK_ARG(out_scan); - *out_scan = nullptr; +DUCKDB_V2_ERROR duckdb_v2_replacement_scan_create(duckdb_v2_factory_handle factory, + duckdb_v2_replacement_scan_handle *scan, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); + DUCKDB_CHECK_ARG(scan); + *scan = nullptr; return WithErrorHandler(err, [&]() { - auto &db = GetExtensionLoader(extension).GetDatabaseInstance(); - auto scan = duckdb::make_uniq(db); - *out_scan = Convert(scan.release()); + auto result = duckdb::make_uniq(Convert(factory)->GetDatabase()); + *scan = Convert(result.release()); }); } @@ -290,7 +241,8 @@ DUCKDB_V2_ERROR duckdb_v2_replacement_scan_add_argument(duckdb_v2_replacement_sc } DUCKDB_V2_ERROR duckdb_v2_replacement_scan_add_named_argument(duckdb_v2_replacement_scan_info_handle info, - duckdb_v2_identifier_t name, duckdb_v2_value_handle value, + const duckdb_v2_identifier_t *name, + duckdb_v2_value_handle value, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(info); DUCKDB_CHECK_ARG(name); @@ -298,7 +250,7 @@ DUCKDB_V2_ERROR duckdb_v2_replacement_scan_add_named_argument(duckdb_v2_replacem return WithErrorHandler(err, [&]() { auto &args = *Convert(info); args.RequireFunctionClaim("duckdb_v2_replacement_scan_add_named_argument"); - args.out_named_arguments.emplace_back(duckdb::Identifier(Convert(name)), *Convert(value)); + args.out_named_arguments.emplace_back(duckdb::Identifier(ConvertIdentifierName(name)), *Convert(value)); }); } @@ -324,7 +276,7 @@ DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_collection(duckdb_v2_replacement_ } duckdb::vector names; for (idx_t i = 0; i < column_count; i++) { - auto name = duckdb::Identifier(Convert(column_names[i])); + auto name = duckdb::Identifier(ConvertIdentifierName(column_names[i])); if (name.empty()) { throw duckdb::InvalidInputException("Column names cannot be empty."); } @@ -337,8 +289,8 @@ DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_collection(duckdb_v2_replacement_ }); } -DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_subquery(duckdb_v2_replacement_scan_info_handle info, duckdb_v2_str sql, - duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_subquery(duckdb_v2_replacement_scan_info_handle info, + const duckdb_v2_str *sql, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(info); DUCKDB_CHECK_ARG(sql); return WithErrorHandler(err, [&]() { @@ -346,7 +298,7 @@ DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_subquery(duckdb_v2_replacement_sc // Parsed here rather than when the claim is materialized, so a bad query fails this call instead of // surfacing later as an opaque binding error. - duckdb::Parser parser(args.in_context->GetParserOptions()); + duckdb::Parser parser(*args.in_context); parser.ParseQuery(duckdb::string(Convert(sql))); if (parser.statements.size() != 1) { throw duckdb::InvalidInputException("The replacement subquery must be exactly one SELECT statement."); @@ -362,16 +314,47 @@ DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_subquery(duckdb_v2_replacement_sc } DUCKDB_V2_ERROR duckdb_v2_replacement_scan_set_alias(duckdb_v2_replacement_scan_info_handle info, - duckdb_v2_identifier_t alias, duckdb_v2_error_info_handle *err) { + const duckdb_v2_identifier_t *alias, + duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(info); DUCKDB_CHECK_ARG(alias); - return WithErrorHandler(err, [&]() { Convert(info)->out_alias = duckdb::Identifier(Convert(alias)); }); + return WithErrorHandler(err, + [&]() { Convert(info)->out_alias = duckdb::Identifier(ConvertIdentifierName(alias)); }); } -DUCKDB_V2_ERROR duckdb_v2_replacement_scan_register(duckdb_v2_replacement_scan_handle scan, - duckdb_v2_error_info_handle *err) { +DUCKDB_V2_ERROR duckdb_v2_connection_register_replacement_scan(duckdb_v2_connection_handle conn, + duckdb_v2_replacement_scan_handle scan, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(conn); + DUCKDB_CHECK_ARG(scan); + return WithErrorHandler(err, [&]() { + auto &context = *Convert(conn)->context; + auto built = Convert(scan)->Build(*context.db); + // Connection-scoped: this touches only the client's own state, never the shared database config. + context.config.replacement_scans.push_back(duckdb::make_shared_ptr(std::move(built))); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_instance_register_replacement_scan(duckdb_v2_instance_handle instance, + duckdb_v2_replacement_scan_handle scan, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(instance); + DUCKDB_CHECK_ARG(scan); + return WithErrorHandler(err, [&]() { + auto &db = *Convert(instance)->GetDatabase().instance; + duckdb::DBConfig::GetConfig(db).replacement_scans.push_back(Convert(scan)->Build(db)); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_extension_register_replacement_scan(duckdb_v2_extension_handle extension, + duckdb_v2_replacement_scan_handle scan, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(extension); DUCKDB_CHECK_ARG(scan); - return WithErrorHandler(err, [&]() { Convert(scan)->Register(); }); + return WithErrorHandler(err, [&]() { + auto &db = GetExtensionLoader(extension).GetDatabaseInstance(); + duckdb::DBConfig::GetConfig(db).replacement_scans.push_back(Convert(scan)->Build(db)); + }); } DUCKDB_V2_ERROR duckdb_v2_replacement_scan_destroy(duckdb_v2_replacement_scan_handle *scan) { diff --git a/src/duckdb/src/main/capi/v2/capi_v2_result.cpp b/src/duckdb/src/main/capi/v2/capi_v2_result.cpp index 9d5cf3048..c7b7391cc 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_result.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_result.cpp @@ -1,13 +1,13 @@ #include "duckdb/main/capi_v2/capi_v2_result_internal.hpp" +#include "duckdb/common/arrow/arrow_format.hpp" +#include "duckdb/common/arrow/nanoarrow/nanoarrow.hpp" #include "duckdb/common/box_renderer.hpp" #include "duckdb/common/box_renderer_context.hpp" #include "duckdb/common/column_data_collection_render_interface.hpp" #include "duckdb/common/types/column/column_data_collection.hpp" -#include "duckdb/common/enums/pending_execution_result.hpp" -#include "duckdb/common/enums/stream_execution_result.hpp" -#include "duckdb/parser/statement/transaction_statement.hpp" +#include "duckdb/common/enums/query_result_state.hpp" namespace duckdb::capiv2 { namespace { @@ -30,77 +30,85 @@ DUCKDB_V2_RESULT_TYPE MapResultType(StatementReturnType t) { return DUCKDB_V2_RESULT_TYPE_QUERY_RESULT; } +//! The count is the first row of the single BIGINT column. +void ReadChangedRows(const DataChunk &chunk, idx_t &rows_changed) { + if (chunk.size() > 0) { + rows_changed = static_cast(chunk.GetValue(0, 0).GetValue()); + } +} + +void ReadChangedRows(const ArrowArrayWrapper &unit, idx_t &rows_changed) { + if (unit.arrow_array.length > 0) { + rows_changed = ArrowFormat::ChangedRows(unit.arrow_array); + } +} + } // anonymous namespace // --------------------------------------------------------------------------- // ResultWrapperV2 state machine // --------------------------------------------------------------------------- -void ResultWrapperV2::BeginPending(unique_ptr next_pending, bool is_principal) { - if (next_pending->HasError()) { - // Re-throw the typed ErrorData so the exception's ExceptionType is - // preserved and routed through GetErrorCodeFromExceptionType. - next_pending->GetErrorObject().Throw(); - } - principal_active = is_principal; - if (is_principal) { - types = next_pending->GetTypes(); - names = next_pending->GetNames(); - statement_type = next_pending->GetStatementType(); - properties = next_pending->GetStatementProperties(); - metadata_available = true; +void ResultWrapperV2::BeginPending(unique_ptr next_handle, StatementType written_type) { + next_handle->ThrowIfError(); + stream_when_ready = StreamsByDefault(written_type, *next_handle); + if (!stream_when_ready) { + // Settled before the caller steps it, so every step stays one bounded unit of work + next_handle->Materialize(); } - pending = std::move(next_pending); + statement_type = next_handle->GetStatementType(); + handle = std::move(next_handle); state = State::PENDING; + CaptureMetadata(); } -void ResultWrapperV2::StartNextFragment() { - D_ASSERT(fragment_index < fragments.size()); - if (fragment_index > 0 && context->IsInterrupted()) { - // PendingQuery's InitialCleanup clears a pending interrupt flag; - // without this check an interrupt landing exactly at a fragment - // boundary would be silently swallowed and the group would keep - // running. (At fragment 0 the clearing is desired: a stale - // interrupt must not poison a new query.) - throw InterruptException(); - } - idx_t this_index = fragment_index; - auto stmt = std::move(fragments[fragment_index++]); - bool is_last = fragment_index == fragments.size(); - // Parameters bind only to the first fragment (statement_execute rejects them on - // a statement that expands, so that fragment is the user's statement). Later - // fragments take the no-values path. - auto next_pending = - (this_index == 0 && !param_values.empty()) - ? context->PendingQuery(std::move(stmt), param_values, QueryResultOutputType::ALLOW_STREAMING) - : context->PendingQuery(std::move(stmt), QueryResultOutputType::ALLOW_STREAMING); - // Principal selection is a property of the fragment group; compute it here, then - // hand the pending to the shared BeginPending seam. A HasError() pending is left - // for BeginPending to raise (return_type is meaningless on it). - bool has_result = !next_pending->HasError() && - next_pending->GetStatementProperties().return_type == StatementReturnType::QUERY_RESULT; - if (principal_seen && has_result) { - // ClientContext::Query would chain these as separate results with - // separate schemas; a single stream cannot. No known expansion - // produces two row-producing fragments. - throw NotImplementedException( - "statement expands to multiple row-producing statements, which cannot be streamed as a single result"); +void ResultWrapperV2::CaptureMetadata() { + if (metadata_available) { + return; } - // Result selection mirrors ClientContext::Query: the caller sees the - // first row-producing fragment, or the last fragment when none - // produces rows. - bool is_principal = has_result || (is_last && !principal_seen); - if (has_result) { - principal_seen = true; + const bool arrow = format && format->Is(); + if (stream) { + // A failed result reports empty metadata, which is not the result's + if (stream->HasError() || !stream->MetadataAvailable()) { + return; + } + types = stream->GetTypes(); + names = stream->GetNames(); + properties = stream->GetStatementProperties(); + if (arrow) { + auto &arrow_stream = static_cast &>(*stream); + CopyArrowSchema(arrow_stream.FormatState().Schema(), arrow_schema.arrow_schema); + } + } else if (handle) { + if (handle->HasError() || !handle->MetadataAvailable()) { + return; + } + types = handle->GetTypes(); + names = handle->GetNames(); + properties = handle->GetStatementProperties(); + if (arrow) { + CopyArrowSchema(handle->FormatState().Schema(), arrow_schema.arrow_schema); + } + } else { + return; } - BeginPending(std::move(next_pending), is_principal); + metadata_available = true; } -void ResultWrapperV2::RequireMetadata() const { - if (!metadata_available) { - throw InvalidInputException("result metadata is not yet available: the statement expands to a group of " - "statements and the result-producing one has not been prepared; step the result"); +void ResultWrapperV2::RequireMetadata() { + CaptureMetadata(); + if (metadata_available) { + return; } + // A result that ended before its metadata was known never gets it + if (state == State::ERRORED) { + error.Throw(); + } + if (state == State::CANCELLED) { + throw InterruptException(); + } + throw MetadataDeferredException("result metadata is not yet available: the statement expands into several, and " + "the one that produces the result has not been bound; step the result"); } DUCKDB_V2_RESULT_STEP_STATUS ResultWrapperV2::HandleExecutionError(ErrorData error_data) { @@ -109,16 +117,9 @@ DUCKDB_V2_RESULT_STEP_STATUS ResultWrapperV2::HandleExecutionError(ErrorData err // (e.g. a max_execution_time timeout) must surface as an error carrying the // engine's message, mirroring the eager ClientContext::Query path. bool user_cancelled = error_data.Type() == ExceptionType::INTERRUPT && busy_slot && - busy_slot->cancel_requested.load(std::memory_order_relaxed); - pending.reset(); - result.reset(); - fragments.clear(); - try { - RollbackIncompleteGroup(); - } catch (...) { - // Best effort; never mask the original error. - } - ReleaseBusySlot(); + busy_slot->cancel_requests.load() != cancel_requests_at_start; + stream.reset(); + handle.reset(); if (user_cancelled) { // In the step channel, cancellation is a status, not an error, // regardless of which phase the interrupt landed in. The status @@ -132,8 +133,10 @@ DUCKDB_V2_RESULT_STEP_STATUS ResultWrapperV2::HandleExecutionError(ErrorData err error.Throw(); } -DUCKDB_V2_RESULT_STEP_STATUS ResultWrapperV2::Step(unique_ptr &out_chunk) { - out_chunk.reset(); +template +DUCKDB_V2_RESULT_STEP_STATUS ResultWrapperV2::Step(unique_ptr &out_unit) { + D_ASSERT((format ? format->Is() : std::is_same::value)); + out_unit.reset(); switch (state) { case State::FINISHED: return DUCKDB_V2_RESULT_STEP_STATUS_FINISHED; @@ -144,126 +147,97 @@ DUCKDB_V2_RESULT_STEP_STATUS ResultWrapperV2::Step(unique_ptr &out_ch // internal results. error.Throw(); case State::PENDING: { - PendingExecutionResult exec; + QueryResultState exec; try { - exec = pending->ExecuteTask(); + exec = handle->ExecuteTask(); + CaptureMetadata(); } catch (std::exception &ex) { return HandleExecutionError(ErrorData(ex)); } switch (exec) { - case PendingExecutionResult::RESULT_NOT_READY: - case PendingExecutionResult::BLOCKED: - case PendingExecutionResult::NO_TASKS_AVAILABLE: + case QueryResultState::NOT_READY: + case QueryResultState::BLOCKED: + case QueryResultState::NO_TASKS_AVAILABLE: return DUCKDB_V2_RESULT_STEP_STATUS_WAITING; - case PendingExecutionResult::EXECUTION_ERROR: - return HandleExecutionError(pending->GetErrorObject()); - case PendingExecutionResult::RESULT_READY: - case PendingExecutionResult::EXECUTION_FINISHED: { - // Transition to the streaming phase. Execute() is (mostly) - // instant once the pending result is ready. Reporting WAITING - // after the transition keeps the contract simple: one unit of - // work per step; the next step hits the stream. - unique_ptr res; + case QueryResultState::EXECUTION_ERROR: + return HandleExecutionError(handle->GetErrorObject()); + case QueryResultState::READY: + case QueryResultState::FINISHED: { + // Transition to the streaming phase. Reporting WAITING after the + // transition keeps the contract simple: one unit of work per + // step; the next step hits the stream. try { - res = pending->Execute(); + if (!stream_when_ready) { + handle->Complete(); + if (handle->HasError()) { + return HandleExecutionError(handle->GetErrorObject()); + } + } else { + stream = make_uniq>(std::move(handle)); + } + CaptureMetadata(); } catch (std::exception &ex) { return HandleExecutionError(ErrorData(ex)); } - pending.reset(); - if (res->HasError()) { - return HandleExecutionError(res->GetErrorObject()); - } - result = std::move(res); state = State::STREAMING; return DUCKDB_V2_RESULT_STEP_STATUS_WAITING; } } - D_ASSERT(false); // unmapped PendingExecutionResult variant + D_ASSERT(false); // unmapped QueryResultState variant return DUCKDB_V2_RESULT_STEP_STATUS_WAITING; } case State::STREAMING: { - if (result->GetResultType() == QueryResultType::STREAM_RESULT) { - auto &stream = result->Cast(); - StreamExecutionResult exec; + if (stream) { + QueryResultState exec; try { - exec = stream.ExecuteTask(); + exec = stream->ExecuteTask(); + CaptureMetadata(); } catch (std::exception &ex) { - // SimpleBufferedData surfaces a pending interrupt by - // throwing InterruptException from ExecuteTask; route it - // through the same sink as EXECUTION_CANCELLED. + // The stream records an interrupt as an error rather than + // throwing; this is a guard for anything that escapes. return HandleExecutionError(ErrorData(ex)); } switch (exec) { - case StreamExecutionResult::CHUNK_NOT_READY: - case StreamExecutionResult::BLOCKED: - case StreamExecutionResult::NO_TASKS_AVAILABLE: + case QueryResultState::NOT_READY: + case QueryResultState::BLOCKED: + case QueryResultState::NO_TASKS_AVAILABLE: return DUCKDB_V2_RESULT_STEP_STATUS_WAITING; - case StreamExecutionResult::EXECUTION_CANCELLED: - pending.reset(); - result.reset(); - fragments.clear(); - try { - RollbackIncompleteGroup(); - } catch (...) { - // Best effort; cancellation still wins. - } - ReleaseBusySlot(); - state = State::CANCELLED; - return DUCKDB_V2_RESULT_STEP_STATUS_CANCELLED; - case StreamExecutionResult::EXECUTION_ERROR: - return HandleExecutionError(stream.GetErrorObject()); - case StreamExecutionResult::CHUNK_READY: - case StreamExecutionResult::EXECUTION_FINISHED: + case QueryResultState::EXECUTION_ERROR: + return HandleExecutionError(stream->GetErrorObject()); + case QueryResultState::READY: + case QueryResultState::FINISHED: // Both states mean "Fetch without doing more execution - // work": CHUNK_READY has a buffered chunk; after - // EXECUTION_FINISHED the buffer may still hold trailing - // chunks and Fetch drains them until it reports - // end-of-stream. + // work": READY has a buffered chunk; after FINISHED the + // buffer may still hold trailing chunks and Fetch drains + // them until it reports end-of-stream. break; } } - // Fetch the next buffered chunk. For the materialized fallback - // (non-row statements can come back materialized even with - // ALLOW_STREAMING) the data is fully available and every step - // lands here directly. - unique_ptr chunk; + // A retained statement has all its data, so every step lands here directly + unique_ptr unit; try { - chunk = result->Fetch(); + unit = + stream ? static_cast &>(*stream).Fetch() : handle->template Fetch(); } catch (std::exception &ex) { return HandleExecutionError(ErrorData(ex)); } - if (result->HasError()) { - // StreamQueryResult::Fetch reports late execution errors by - // setting the error and returning null. - return HandleExecutionError(result->GetErrorObject()); + if (stream ? stream->HasError() : handle->HasError()) { + // The stream reports late execution errors by setting the error + // and returning null. + return HandleExecutionError(stream ? stream->GetErrorObject() : handle->GetErrorObject()); } - if (!chunk || chunk->size() == 0) { - // Stream results already normalize end-of-stream to null (and - // close) in FetchInternal; the size() == 0 arm is needed for the - // materialized fallback, whose Fetch does not normalize. - result.reset(); - if (fragment_index < fragments.size()) { - // More fragments in the group: start the next one and keep - // reporting WAITING. FINISHED only after the whole group - // (including a trailing injected COMMIT) has executed. - try { - StartNextFragment(); - } catch (std::exception &ex) { - return HandleExecutionError(ErrorData(ex)); - } - return DUCKDB_V2_RESULT_STEP_STATUS_WAITING; - } + try { + CaptureMetadata(); + } catch (std::exception &ex) { + return HandleExecutionError(ErrorData(ex)); + } + if (!unit) { + stream.reset(); + handle.reset(); state = State::FINISHED; - ReleaseBusySlot(); return DUCKDB_V2_RESULT_STEP_STATUS_FINISHED; } - if (!principal_active) { - // Output of a non-principal fragment (e.g. the internal UPDATE of - // an expanded ALTER): discard it, exactly as ClientContext::Query - // drops these results from its chain. - return DUCKDB_V2_RESULT_STEP_STATUS_WAITING; - } - out_chunk = std::move(chunk); + out_unit = std::move(unit); return DUCKDB_V2_RESULT_STEP_STATUS_CHUNK; } } @@ -278,44 +252,43 @@ void ResultWrapperV2::Wait() { // executability first, the same way the steps do. switch (state) { case State::PENDING: { - // CheckPulse re-validates executability (throwing a clean error when - // the result is closed) and reports the engine state without running - // work. Its outcome decides whether blocking is safe: the engine may - // already have processed an execution error and ended the query - // internally (waiting then dereferences the closed query), and a - // ready or finished query has no task to wake on. - switch (pending->CheckPulse()) { - case PendingExecutionResult::BLOCKED: - case PendingExecutionResult::NO_TASKS_AVAILABLE: + // Poll re-validates executability and reports the engine state + // without running work. Its outcome decides whether blocking is + // safe: the engine may already have processed an execution error and + // ended the query internally (waiting then dereferences the closed + // query), and a ready or finished query has no task to wake on. + auto polled = handle->Poll(); + CaptureMetadata(); + switch (polled) { + case QueryResultState::BLOCKED: + case QueryResultState::NO_TASKS_AVAILABLE: // No progress possible right now; this is the one case where // blocking is meaningful. - pending->WaitForTask(); + handle->WaitForTask(); return; - case PendingExecutionResult::EXECUTION_ERROR: + case QueryResultState::EXECUTION_ERROR: // The error (interrupts included) is already recorded on the - // pending result and the engine closed the query. Transition the - // state machine now: a later ExecuteTask would trip the engine's + // handle and the engine closed the query. Transition the state + // machine now: a later ExecuteTask would trip the engine's // closed-result check and misreport the error as INVALID_INPUT. - HandleExecutionError(pending->GetErrorObject()); + HandleExecutionError(handle->GetErrorObject()); return; default: - // RESULT_NOT_READY / RESULT_READY / EXECUTION_FINISHED: the next - // step makes progress without blocking. + // NOT_READY / READY / FINISHED: the next step makes progress + // without blocking. return; } } case State::STREAMING: - if (result->GetResultType() == QueryResultType::STREAM_RESULT) { - auto &stream = result->Cast(); - if (!stream.IsOpen()) { + if (stream) { + if (!stream->IsOpen()) { // Closed or invalidated: the next step returns without // blocking, so there is nothing to wait for. return; } - stream.WaitForTask(); + stream->WaitForTask(); } - // Materialized fallback: every step makes progress; nothing to - // wait for. + // A retained result makes progress on every step; nothing to wait for. return; default: // Terminal states: waiting is a no-op, never an error. @@ -323,12 +296,13 @@ void ResultWrapperV2::Wait() { } } -unique_ptr ResultWrapperV2::FetchChunkBlocking() { +template +unique_ptr ResultWrapperV2::FetchBlocking() { while (true) { - unique_ptr chunk; - switch (Step(chunk)) { + unique_ptr unit; + switch (Step(unit)) { case DUCKDB_V2_RESULT_STEP_STATUS_CHUNK: - return chunk; + return unit; case DUCKDB_V2_RESULT_STEP_STATUS_FINISHED: return nullptr; case DUCKDB_V2_RESULT_STEP_STATUS_CANCELLED: @@ -348,38 +322,104 @@ unique_ptr ResultWrapperV2::FetchChunkBlocking() { } } +template +void ResultWrapperV2::AdvanceToMetadata() { + while (!metadata_available) { + unique_ptr discard; + auto status = Step(discard); + if (status == DUCKDB_V2_RESULT_STEP_STATUS_WAITING) { + Wait(); + continue; + } + if (status == DUCKDB_V2_RESULT_STEP_STATUS_CHUNK) { + throw InternalException("a row was produced before result metadata was available"); + } + break; // FINISHED / CANCELLED + } +} + +template +idx_t ResultWrapperV2::Drain() { + // CHANGED_ROWS results stream a single-row BIGINT Count unit. The metadata of a statement that expands into several + // may only become available mid-drain, so check per unit. + idx_t rows_changed = 0; + while (auto unit = FetchBlocking()) { + if (metadata_available && properties.return_type == StatementReturnType::CHANGED_ROWS) { + ReadChangedRows(*unit, rows_changed); + } + } + return rows_changed; +} + +// Put def's of the arrow specializations here so they are instantiated +template DUCKDB_V2_RESULT_STEP_STATUS ResultWrapperV2::Step(unique_ptr &); +template unique_ptr ResultWrapperV2::FetchBlocking(); +template void ResultWrapperV2::AdvanceToMetadata(); +template idx_t ResultWrapperV2::Drain(); + +void CopyArrowSchema(const ArrowSchema &source, ArrowSchema &out) { + out.release = nullptr; + if (duckdb_nanoarrow::ArrowSchemaDeepCopy(&source, &out) != NANOARROW_OK) { + throw OutOfMemoryException("failed to copy the Arrow schema"); + } +} + auto Convert(ResultWrapperV2 *wrapper) -> duckdb_v2_result_handle { return reinterpret_cast(wrapper); } auto Convert(duckdb_v2_result_handle handle) -> ResultWrapperV2 * { return reinterpret_cast(handle); } +auto ConvertArrowResult(ResultWrapperV2 *wrapper) -> duckdb_v2_arrow_result_handle { + return reinterpret_cast(wrapper); +} +auto Convert(duckdb_v2_arrow_result_handle handle) -> ResultWrapperV2 * { + return reinterpret_cast(handle); +} auto ExecutePreparedStatementV2(const shared_ptr &context, PreparedStatement &prepared, - identifier_map_t &values) -> duckdb_v2_result_handle { + identifier_map_t &values, shared_ptr format) + -> unique_ptr { auto wrapper = make_uniq(); - // One live result per connection, claimed the way statement_execute claims it and - // before PendingQuery runs, which would otherwise cancel the live stream. - auto busy_slot = GetBusySlot(*context); - void *expected = nullptr; - if (!busy_slot->owner.compare_exchange_strong(expected, wrapper.get())) { - throw ResourceInUseException("connection has a live result; drain, destroy, or interrupt it before starting " - "a new query (or open another connection)"); - } - // On any failure below, the wrapper's destructor releases the slot. - wrapper->busy_slot = std::move(busy_slot); - wrapper->busy_slot->cancel_requested.store(false, std::memory_order_relaxed); + wrapper->busy_slot = GetBusySlot(*context); + wrapper->cancel_requests_at_start = wrapper->busy_slot->cancel_requests.load(); wrapper->context = context; - // A prepared statement is always one engine statement: preprocessing, expansion and - // the wrapping transaction all happened at prepare time, so this bypasses the fragment - // machinery and is always principal. fragment_count is 1 for metadata symmetry only. - wrapper->fragment_count = 1; - wrapper->BeginPending(prepared.PendingQuery(values, /*allow_stream_result=*/true), true); + wrapper->format = std::move(format); + // The engine refuses the submission while another statement on the connection is running + wrapper->BeginPending(prepared.Submit(values, QueryParameters(wrapper->format)), prepared.GetStatementType()); // The engine runs a prepared statement through an internal EXECUTE, whose statement type // would otherwise be what the result reports. Report the type of the statement that was // prepared instead, so a prepared result is indistinguishable from a stateless one. wrapper->statement_type = prepared.GetStatementType(); - return Convert(wrapper.release()); + return wrapper; +} + +auto ExecuteStatementV2(const shared_ptr &context, const SQLStatement &statement, + const duckdb_v2_identifier_t *parameter_names, const duckdb_v2_value_handle *parameter_values, + idx_t parameter_count, const char *function_name, shared_ptr format) + -> unique_ptr { + auto wrapper = make_uniq(); + // The busy slot lives in the context's registered-state map, so the connection handle stays a bare Connection * + wrapper->busy_slot = GetBusySlot(*context); + wrapper->cancel_requests_at_start = wrapper->busy_slot->cancel_requests.load(); + + // Borrowed, not consumed: execute a copy so the caller keeps the original. + auto stmt = statement.Copy(); + // Fold parameter values in as constants, keyed by name when parameter_names + // supplies one and positionally ("1".."N") otherwise. + identifier_map_t values; + BuildParameterMap(parameter_names, parameter_values, parameter_count, function_name, values); + wrapper->context = context; + wrapper->format = std::move(format); + // Lazy streaming execution: nothing executes until the result is stepped. The engine expands the statement, + // running what it expands into as one query, and refuses the submission while another statement on the + // connection is running. + auto handle = values.empty() ? context->Submit(std::move(stmt), QueryParameters(wrapper->format)) + : context->Submit(std::move(stmt), values, QueryParameters(wrapper->format)); + // The result's type, so that a PRAGMA the engine reparses into a SELECT streams as that SELECT + const auto result_type = handle->GetStatementType(); + wrapper->BeginPending(std::move(handle), result_type); + return wrapper; } } // namespace duckdb::capiv2 @@ -404,72 +444,9 @@ DUCKDB_V2_ERROR duckdb_v2_statement_execute(duckdb_v2_connection_handle conn, du return NullArgumentError(err, __func__, "parameter_values"); } return WithErrorHandler(err, [&]() { - auto *connection = Convert(conn); - auto wrapper = duckdb::make_uniq(); - // One live result per connection. The busy slot lives in the context's - // registered-state map (so the connection handle stays a bare Connection *), - // shared with this result. The busy check is a manual return path: no - // ExceptionType maps to RESOURCE_IN_USE, so routing it through - // WithErrorHandler would degrade the code. It must run before PendingQuery, - // which would otherwise silently cancel the live stream. - auto busy_slot = GetBusySlot(*connection->context); - void *expected = nullptr; - if (!busy_slot->owner.compare_exchange_strong(expected, wrapper.get())) { - throw duckdb::ResourceInUseException( - "connection has a live result; drain, destroy, or interrupt it before starting " - "a new query (or open another connection)"); - } - // On any failure below, the wrapper's destructor releases the slot. - wrapper->busy_slot = std::move(busy_slot); - // A fresh query starts uncancelled: clear any consumer-cancellation request - // left over from before this result claimed the slot (mirrors the engine - // clearing interrupt_state at query begin). - wrapper->busy_slot->cancel_requested.store(false, std::memory_order_relaxed); - - // Borrowed, not consumed: execute a copy so the caller keeps the original. - auto stmt = Convert(statement)->Copy(); - // Fold parameter values in as constants, keyed by name when parameter_names - // supplies one and positionally ("1".."N") otherwise. - BuildParameterMap(parameter_names, parameter_values, parameter_count, "duckdb_v2_statement_execute", - wrapper->param_values); - // Statement-level preprocessing (pragma reparsing, expansion - // unpacking, transaction wrapping): one user statement can expand - // into a group of engine statements that the wrapper executes in - // order. parse_sql deliberately leaves this to statement_execute so - // parsing stays binder-free and a group is never split across the - // API boundary. - wrapper->fragments.push_back(std::move(stmt)); - connection->context->PreprocessStatements(wrapper->fragments); - if (wrapper->fragments.empty()) { - throw duckdb::InvalidInputException("statement preprocessing yielded no executable statements"); - } - wrapper->fragment_count = wrapper->fragments.size(); - // Reject parameters on a statement that expands into a group: the values would - // bind to the first fragment, an injected BEGIN, not the user's statement. (A - // statement can carry a parameter and still expand, e.g. a volatile DEFAULT.) - if (!wrapper->param_values.empty() && wrapper->fragments.size() > 1) { - throw duckdb::InvalidInputException( - "parameters are not supported for a statement that expands into multiple engine statements"); - } - // Detect whether preprocessing wrapped this group in its own - // transaction (autocommit input expanded to BEGIN ... COMMIT). - // Preprocessing only injects the wrap for a multi-fragment group, as a - // leading BEGIN paired with a trailing COMMIT; a lone user-issued BEGIN - // is a single fragment the user owns and the bridge must not roll back. - if (wrapper->fragments.size() > 1 && - wrapper->fragments.front()->type == duckdb::StatementType::TRANSACTION_STATEMENT && - wrapper->fragments.back()->type == duckdb::StatementType::TRANSACTION_STATEMENT) { - auto &front_stmt = wrapper->fragments.front()->Cast(); - auto &back_stmt = wrapper->fragments.back()->Cast(); - wrapper->owns_wrapping_transaction = front_stmt.info->type == duckdb::TransactionType::BEGIN_TRANSACTION && - back_stmt.info->type == duckdb::TransactionType::COMMIT; - } - wrapper->context = connection->context; - // Prepare the first fragment. Lazy streaming execution: nothing - // executes until the result is stepped; for non-expanding statements - // (the common case) this also captures the metadata immediately. - wrapper->StartNextFragment(); - *out_result = Convert(wrapper.release()); + *out_result = Convert(ExecuteStatementV2(Convert(conn)->context, *Convert(statement), parameter_names, + parameter_values, parameter_count, __func__, nullptr) + .release()); }); } @@ -477,34 +454,17 @@ DUCKDB_V2_ERROR duckdb_v2_result_drain(duckdb_v2_result_handle result, idx_t *ou duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(result); DUCKDB_CHECK_ARG(out_rows_changed); - return WithErrorHandler(err, [&]() { - auto *wrapper = Convert(result); - // Drain to completion: side effects are applied, rows of a - // row-producing result are discarded. CHANGED_ROWS results stream a - // single-row BIGINT Count chunk carrying the affected row count. - // The principal fragment's metadata may only become available - // mid-drain (expanding statements), so check per chunk: surfaced - // chunks always belong to the principal fragment. - idx_t rows_changed = 0; - while (auto chunk = wrapper->FetchChunkBlocking()) { - bool changed_rows = wrapper->metadata_available && - wrapper->properties.return_type == duckdb::StatementReturnType::CHANGED_ROWS; - if (changed_rows && chunk->size() > 0) { - rows_changed = static_cast(chunk->GetValue(0, 0).GetValue()); - } - } - *out_rows_changed = rows_changed; - }); + return WithErrorHandler(err, [&]() { *out_rows_changed = Convert(result)->Drain(); }); } DUCKDB_V2_ERROR duckdb_v2_result_render_box(duckdb_v2_result_handle *result, idx_t max_rows, idx_t max_width, - idx_t max_col_width, duckdb_v2_str null_value, idx_t render_mode, + idx_t max_col_width, const duckdb_v2_str *null_value, idx_t render_mode, idx_t limit, duckdb_v2_text_sink_fn sink, void *user_data, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(result); DUCKDB_CHECK_ARG(*result); DUCKDB_CHECK_ARG(sink); - // Validate the by-value arguments before consuming the result, so an + // Validate the view arguments before consuming the result, so an // argument rejection leaves the caller's result intact (as the null-arg // rejections above do). DUCKDB_CHECK_ARG(null_value); @@ -515,20 +475,8 @@ DUCKDB_V2_ERROR duckdb_v2_result_render_box(duckdb_v2_result_handle *result, idx // Adopt by transfer; consumed on success and failure alike. auto wrapper = duckdb::unique_ptr(Convert(*result)); *result = nullptr; - // Names and types must be available before building the collection; - // expanding statements may need stepping to the principal fragment. - while (!wrapper->metadata_available) { - duckdb::unique_ptr discard; - auto status = wrapper->Step(discard); - if (status == DUCKDB_V2_RESULT_STEP_STATUS_WAITING) { - wrapper->Wait(); - continue; - } - if (status == DUCKDB_V2_RESULT_STEP_STATUS_CHUNK) { - throw duckdb::InternalException("render box: a row was produced before result metadata was available"); - } - break; // FINISHED / CANCELLED: no row-producing fragment. - } + // Names and types must be available before building the collection. + wrapper->AdvanceToMetadata(); if (!wrapper->context) { throw duckdb::InvalidInputException("result is not associated with an active context"); } @@ -536,7 +484,7 @@ DUCKDB_V2_ERROR duckdb_v2_result_render_box(duckdb_v2_result_handle *result, idx // Materialize the remainder; max_rows bounds display, not the read. duckdb::ColumnDataCollection collection(duckdb::Allocator::DefaultAllocator(), wrapper->types); - while (auto chunk = wrapper->FetchChunkBlocking()) { + while (auto chunk = wrapper->FetchBlocking()) { collection.Append(*chunk); } @@ -550,8 +498,8 @@ DUCKDB_V2_ERROR duckdb_v2_result_render_box(duckdb_v2_result_handle *result, idx if (max_col_width != 0) { config.max_col_width = max_col_width; } - if (null_value.len) { - config.null_value = std::string(null_value.ptr, null_value.len); + if (null_value->len) { + config.null_value = std::string(null_value->ptr, null_value->len); } config.render_mode = render_mode == 1 ? duckdb::RenderMode::COLUMNS : duckdb::RenderMode::ROWS; // The caller's query-side LIMIT: when the materialized result fills it, the @@ -597,7 +545,7 @@ DUCKDB_V2_ERROR duckdb_v2_result_step(duckdb_v2_result_handle result, duckdb_v2_ return WithErrorHandler(err, [&]() { auto *wrapper = Convert(result); duckdb::unique_ptr chunk; - auto status = wrapper->Step(chunk); + auto status = wrapper->Step(chunk); *out_status = status; if (status == DUCKDB_V2_RESULT_STEP_STATUS_CHUNK) { *out_chunk = Convert(chunk.release()); @@ -612,7 +560,7 @@ DUCKDB_V2_ERROR duckdb_v2_result_fetch_chunk(duckdb_v2_result_handle result, duc *out_chunk = nullptr; return WithErrorHandler(err, [&]() { auto *wrapper = Convert(result); - auto chunk = wrapper->FetchChunkBlocking(); + auto chunk = wrapper->FetchBlocking(); if (chunk) { *out_chunk = Convert(chunk.release()); } @@ -645,11 +593,7 @@ DUCKDB_V2_ERROR duckdb_v2_result_get_statement_type(duckdb_v2_result_handle resu DUCKDB_CHECK_ARG(result); DUCKDB_CHECK_ARG(out_type); - return WithErrorHandler(err, [&]() { - auto *r = Convert(result); - r->RequireMetadata(); - *out_type = Convert(r->statement_type); - }); + return WithErrorHandler(err, [&]() { *out_type = Convert(Convert(result)->statement_type); }); } DUCKDB_V2_ERROR duckdb_v2_result_get_schema(duckdb_v2_result_handle result, duckdb_v2_schema_handle *out_schema, diff --git a/src/duckdb/src/main/capi/v2/capi_v2_schema.cpp b/src/duckdb/src/main/capi/v2/capi_v2_schema.cpp index c22d6883b..c7d79d554 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_schema.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_schema.cpp @@ -26,7 +26,7 @@ DUCKDB_V2_ERROR duckdb_v2_schema_get_field(duckdb_v2_schema_handle schema, idx_t // Both borrowed, valid until the schema is destroyed; out_type aliases the // wrapper-owned LogicalType and must not be destroyed by the caller. *out_name = Convert(field.name); - *out_type = Convert(&field.type); + *out_type = ConvertBorrowed(field.type); }); } diff --git a/src/duckdb/src/main/capi/v2/capi_v2_statement.cpp b/src/duckdb/src/main/capi/v2/capi_v2_statement.cpp index f2a718dbc..8665c867c 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_statement.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_statement.cpp @@ -1,6 +1,18 @@ #include "duckdb/main/capi_v2/capi_v2_internal.hpp" +#include "duckdb/parser/expression/star_expression.hpp" +#include "duckdb/parser/query_node/delete_query_node.hpp" +#include "duckdb/parser/query_node/insert_query_node.hpp" +#include "duckdb/parser/query_node/merge_query_node.hpp" +#include "duckdb/parser/query_node/select_node.hpp" +#include "duckdb/parser/query_node/update_query_node.hpp" #include "duckdb/parser/sql_statement.hpp" +#include "duckdb/parser/statement/delete_statement.hpp" +#include "duckdb/parser/statement/insert_statement.hpp" +#include "duckdb/parser/statement/merge_into_statement.hpp" +#include "duckdb/parser/statement/select_statement.hpp" +#include "duckdb/parser/statement/update_statement.hpp" +#include "duckdb/parser/tableref/column_data_ref.hpp" #include #include @@ -36,6 +48,26 @@ auto ParameterNamesInBindingOrder(const duckdb::SQLStatement &statement) return names; } +//! The common table expressions of a statement a collection can be bound to +auto GetCollectionCTEMap(duckdb::SQLStatement &statement) -> CommonTableExpressionMap & { + switch (statement.type) { + case StatementType::SELECT_STATEMENT: + return statement.Cast().node->cte_map; + case StatementType::INSERT_STATEMENT: + return statement.Cast().node->cte_map; + case StatementType::DELETE_STATEMENT: + return statement.Cast().node->cte_map; + case StatementType::UPDATE_STATEMENT: + return statement.Cast().node->cte_map; + case StatementType::MERGE_INTO_STATEMENT: + return statement.Cast().node->cte_map; + default: + throw InvalidInputException("A collection can only be bound to a SELECT, INSERT, UPDATE, DELETE or MERGE INTO " + "statement, not to a %s statement.", + StatementTypeToString(statement.type)); + } +} + } // namespace auto Convert(duckdb_v2_statement_iterator_handle ptr) -> StatementIteratorWrapperV2 * { @@ -168,6 +200,56 @@ DUCKDB_V2_ERROR duckdb_v2_sql_statement_get_parameter_name(duckdb_v2_sql_stateme }); } +DUCKDB_V2_ERROR duckdb_v2_statement_add_collection(duckdb_v2_sql_statement_handle statement, + const duckdb_v2_identifier_t *name, + duckdb_v2_column_data_collection_handle collection, + const duckdb_v2_identifier_t *column_names, idx_t column_count, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(statement); + DUCKDB_CHECK_ARG(name); + DUCKDB_CHECK_ARG(collection); + return WithErrorHandler(err, [&]() { + auto &stmt = *Convert(statement); + auto &cdc = *Convert(collection); + duckdb::Identifier alias(ConvertIdentifierName(name)); + if (alias.empty()) { + throw duckdb::InvalidInputException("The name to bind a collection under cannot be empty."); + } + if (column_count > 0 && !column_names) { + throw duckdb::InvalidInputException("column_names cannot be null when column_count is not 0."); + } + if (column_count > 0 && column_count != cdc.ColumnCount()) { + throw duckdb::InvalidInputException( + "column_count (%llu) does not match the collection's column count (%llu).", + static_cast(column_count), static_cast(cdc.ColumnCount())); + } + duckdb::vector expected_names; + for (idx_t i = 0; i < column_count; i++) { + expected_names.emplace_back(ConvertIdentifierName(column_names[i])); + if (expected_names.back().empty()) { + throw duckdb::InvalidInputException("Column name %llu of the collection cannot be empty.", + static_cast(i)); + } + } + + auto &cte_map = GetCollectionCTEMap(stmt); + if (cte_map.map.find(alias) != cte_map.map.end()) { + throw duckdb::InvalidInputException("The statement already has a common table expression named %s.", + duckdb::SQLIdentifier(alias)); + } + // SELECT * over a borrowed reference to the collection, never materialized + auto ref = duckdb::make_uniq(cdc, std::move(expected_names)); + ref->alias = alias; + auto select = duckdb::make_uniq(); + select->select_list.push_back(duckdb::make_uniq()); + select->from_table = std::move(ref); + auto cte_info = duckdb::make_uniq(); + cte_info->query_node = std::move(select); + cte_info->materialized = duckdb::CTEMaterialize::CTE_MATERIALIZE_NEVER; + cte_map.map.insert(alias, std::move(cte_info)); + }); +} + DUCKDB_V2_ERROR duckdb_v2_sql_statement_destroy(duckdb_v2_sql_statement_handle *statement) { return WithErrorHandler(nullptr, [&]() { if (!statement) { @@ -205,21 +287,15 @@ DUCKDB_V2_ERROR duckdb_v2_statement_bind(duckdb_v2_connection_handle conn, duckd } auto *connection = Convert(conn); - // Borrowed, not consumed: bind a copy. Preprocess it so the schema matches - // what execution would bind (pragma reparse, expansion). - duckdb::vector> fragments; - fragments.push_back(Convert(statement)->Copy()); - connection->context->PreprocessStatements(fragments); - if (fragments.size() != 1) { - // A statement that expands into a group (a dynamic PIVOT, or - // statement-expanding DDL) is not bindable: bind binds one engine statement. - throw duckdb::InvalidInputException( - "statement expands into multiple engine statements; bind is not supported, execute it instead"); + // Borrowed, not consumed: bind a copy. BindStatement, not Prepare: bind phase only (read-only, no optimize / + // physical plan) and allowed while a result is live (Prepare is refused). It expands the statement as + // execution would, and throws on a bind error, routed through WithErrorHandler. + auto signature = connection->context->BindStatement(Convert(statement)->Copy()); + if (signature.metadata_deferred) { + throw MetadataDeferredException( + "the statement expands into several, so its schema is known only once execution binds the one that " + "produces the result; execute it instead"); } - // BindStatement, not Prepare: bind phase only (read-only, no optimize / physical - // plan) and must not cancel a live result (Prepare's InitialCleanup would). It - // throws on a bind error, routed through WithErrorHandler. - auto signature = connection->context->BindStatement(std::move(fragments[0])); // Assemble into locals, publish to the out-params only at the end once nothing // can throw, so a failure leaves them NULL. auto output = duckdb::make_uniq(); diff --git a/src/duckdb/src/main/capi/v2/capi_v2_tokenizer.cpp b/src/duckdb/src/main/capi/v2/capi_v2_tokenizer.cpp new file mode 100644 index 000000000..9049b7b96 --- /dev/null +++ b/src/duckdb/src/main/capi/v2/capi_v2_tokenizer.cpp @@ -0,0 +1,148 @@ +#include "duckdb/main/capi_v2/capi_v2_internal.hpp" + +#include "duckdb/parser/peg/compiled_grammar.hpp" +#include "duckdb/parser/peg/tokenizer/highlight_tokenizer.hpp" + +namespace duckdb::capiv2 { +namespace { + +struct TokenV2 { + DUCKDB_V2_TOKEN_TYPE type; + idx_t start; + idx_t length; +}; + +struct TokenIteratorWrapperV2 { + vector tokens; + idx_t input_length = 0; + idx_t position = 0; + bool ends_unterminated = false; +}; + +// With this terminator, TokenizeInput returns true exactly on a clean exit; both sentinels are dropped anyway. +class CleanExitTokenizerBehavior : public HighlightTokenizerBehavior { +public: + using HighlightTokenizerBehavior::HighlightTokenizerBehavior; + + TokenType GetTerminator() const override { + return TokenType::END_OF_INPUT_AUTOCOMPLETE; + } +}; + +DUCKDB_V2_TOKEN_TYPE ConvertTokenType(TokenType type) { + switch (type) { + case TokenType::KEYWORD: + return DUCKDB_V2_TOKEN_TYPE_KEYWORD; + case TokenType::IDENTIFIER: + return DUCKDB_V2_TOKEN_TYPE_IDENTIFIER; + case TokenType::STRING_LITERAL: + return DUCKDB_V2_TOKEN_TYPE_STRING_LITERAL; + case TokenType::NUMBER_LITERAL: + return DUCKDB_V2_TOKEN_TYPE_NUMBER_LITERAL; + case TokenType::OPERATOR: + return DUCKDB_V2_TOKEN_TYPE_OPERATOR; + case TokenType::COMMENT: + return DUCKDB_V2_TOKEN_TYPE_COMMENT; + case TokenType::TERMINATOR: + return DUCKDB_V2_TOKEN_TYPE_TERMINATOR; + default: + // The raw tokenizer only assigns lexical classes; the semantic ones need the matcher pass. + throw InternalException("tokenizer emitted token type %d, which is not a lexical class", + static_cast(type)); + } +} + +} // namespace + +auto Convert(duckdb_v2_token_iterator_handle ptr) -> TokenIteratorWrapperV2 * { + return reinterpret_cast(ptr); +} +auto Convert(TokenIteratorWrapperV2 *ptr) -> duckdb_v2_token_iterator_handle { + return reinterpret_cast(ptr); +} + +} // namespace duckdb::capiv2 + +//---------------------------------------------------------------------------------------------------------------------- +// Public API +//---------------------------------------------------------------------------------------------------------------------- + +using namespace duckdb::capiv2; + +DUCKDB_V2_ERROR duckdb_v2_tokenize_sql(duckdb_v2_connection_handle conn, const duckdb_v2_str *sql, + duckdb_v2_token_iterator_handle *out_iterator, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(out_iterator); + *out_iterator = nullptr; + DUCKDB_CHECK_ARG(conn); + DUCKDB_CHECK_ARG(sql); + return WithErrorHandler(err, [&]() { + auto *connection = Convert(conn); + duckdb::string input(Convert(sql)); + duckdb::vector raw_tokens; + CleanExitTokenizerBehavior behavior(input, raw_tokens); + auto grammar = duckdb::CompiledGrammar::Get(*connection->context); + const bool clean_exit = grammar->GetTokenizer().TokenizeInput(behavior); + + auto wrapper = duckdb::make_uniq(); + wrapper->input_length = input.size(); + wrapper->tokens.reserve(raw_tokens.size()); + bool last_unterminated = false; + for (auto &token : raw_tokens) { + if (token.type == duckdb::TokenType::END_OF_INPUT || + token.type == duckdb::TokenType::END_OF_INPUT_AUTOCOMPLETE) { + continue; + } + wrapper->tokens.push_back({ConvertTokenType(token.type), token.offset, token.length}); + last_unterminated = token.unterminated; + } + // A trailing line comment is a dirty exit without the flag; an open string or quoted identifier is a clean + // exit with it. + wrapper->ends_unterminated = !clean_exit || last_unterminated; + *out_iterator = Convert(wrapper.release()); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_token_iterator_next(duckdb_v2_token_iterator_handle iterator, DUCKDB_V2_TOKEN_TYPE *out_type, + idx_t *out_start, idx_t *out_length, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(out_type); + DUCKDB_CHECK_ARG(out_start); + DUCKDB_CHECK_ARG(out_length); + *out_type = DUCKDB_V2_TOKEN_TYPE_INVALID; + *out_start = 0; + *out_length = 0; + DUCKDB_CHECK_ARG(iterator); + return WithErrorHandler(err, [&]() { + auto &wrapper = *Convert(iterator); + if (wrapper.position >= wrapper.tokens.size()) { + *out_type = DUCKDB_V2_TOKEN_TYPE_END_OF_INPUT; + *out_start = wrapper.input_length; + return; + } + auto &token = wrapper.tokens[wrapper.position++]; + *out_type = token.type; + *out_start = token.start; + *out_length = token.length; + }); +} + +DUCKDB_V2_ERROR duckdb_v2_token_iterator_ends_unterminated(duckdb_v2_token_iterator_handle iterator, + bool *out_ends_unterminated, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(out_ends_unterminated); + *out_ends_unterminated = false; + DUCKDB_CHECK_ARG(iterator); + return WithErrorHandler(err, [&]() { *out_ends_unterminated = Convert(iterator)->ends_unterminated; }); +} + +DUCKDB_V2_ERROR duckdb_v2_token_iterator_destroy(duckdb_v2_token_iterator_handle *iterator) { + return WithErrorHandler(nullptr, [&]() { + if (!iterator) { + return; + } + if (*iterator) { + delete Convert(*iterator); + *iterator = nullptr; + } + }); +} diff --git a/src/duckdb/src/main/capi/v2/capi_v2_utf8.cpp b/src/duckdb/src/main/capi/v2/capi_v2_utf8.cpp new file mode 100644 index 000000000..49bacb166 --- /dev/null +++ b/src/duckdb/src/main/capi/v2/capi_v2_utf8.cpp @@ -0,0 +1,26 @@ +#include "duckdb/main/capi_v2/capi_v2_internal.hpp" + +#include "utf8proc_wrapper.hpp" + +namespace duckdb::capiv2 { + +auto ConvertIdentifierName(duckdb_v2_identifier_t name) -> std::string_view { + auto bytes = Convert(name); + if (!Utf8Proc::IsValid(bytes.data(), bytes.size())) { + throw InvalidInputException("Identifier is not valid UTF-8"); + } + return bytes; +} + +} // namespace duckdb::capiv2 + +using namespace duckdb::capiv2; + +DUCKDB_V2_ERROR duckdb_v2_validate_utf8(const duckdb_v2_str *text, duckdb_v2_error_info_handle *err) { + return WithErrorHandler(err, [&]() { + auto bytes = Convert(text); + if (!duckdb::Utf8Proc::IsValid(bytes.data(), bytes.size())) { + throw duckdb::InvalidInputException("Input is not valid UTF-8"); + } + }); +} diff --git a/src/duckdb/src/main/capi/v2/capi_v2_value.cpp b/src/duckdb/src/main/capi/v2/capi_v2_value.cpp index 7ad86a037..1f746dc96 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_value.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_value.cpp @@ -90,11 +90,12 @@ duckdb_v2_value_handle Emit(Value value) { // Gate for the constructors that take a type rather than a payload. ANY is a // signature wildcard; a value carries data, so reject it. -const LogicalType &RequireValueType(duckdb_v2_logical_type_handle type) { +LogicalType RequireValueType(duckdb_v2_logical_type_handle type) { if (!type) { throw InvalidInputException("logical type handle cannot be null"); } - auto < = *Convert(type); + auto lt_ref = Convert(type); + auto < = *lt_ref; if (lt.id() == LogicalTypeId::ANY) { throw InvalidInputException("type cannot be ANY"); } @@ -105,7 +106,7 @@ const LogicalType &RequireValueType(duckdb_v2_logical_type_handle type) { // constructor covers only the widest one, so the narrower tiers go through the // int64 form. Width and scale are gated here: an out-of-range pair would build // a broken type rather than a bad value. -Value BuildDecimal(duckdb_v2_hugeint_t in_value, uint8_t width, uint8_t scale) { +Value BuildDecimal(const duckdb_v2_hugeint_t *in_value, uint8_t width, uint8_t scale) { if (width < 1 || width > Decimal::MAX_WIDTH_DECIMAL) { throw InvalidInputException(": DECIMAL width must be between 1 and " + std::to_string(Decimal::MAX_WIDTH_DECIMAL)); @@ -127,7 +128,7 @@ Value BuildDecimal(duckdb_v2_hugeint_t in_value, uint8_t width, uint8_t scale) { // BIT carries a mandatory padding-header byte, BIGNUM a header plus at least // one magnitude byte; anything shorter is not addressable storage. -Value BuildBit(duckdb_v2_str in_value) { +Value BuildBit(const duckdb_v2_str *in_value) { auto bytes = Convert(in_value); if (bytes.empty()) { throw InvalidInputException("the BIT wire form carries a mandatory padding header byte"); @@ -135,7 +136,7 @@ Value BuildBit(duckdb_v2_str in_value) { return Value::BIT(const_data_ptr_cast(bytes.data()), bytes.size()); } -Value BuildBignum(duckdb_v2_str in_value) { +Value BuildBignum(const duckdb_v2_str *in_value) { auto bytes = Convert(in_value); if (bytes.size() <= Bignum::BIGNUM_HEADER_SIZE) { throw InvalidInputException("the BIGNUM storage form requires more than " + @@ -165,22 +166,6 @@ DUCKDB_V2_ERROR duckdb_v2_value_destroy(duckdb_v2_value_handle *value) { }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_null(duckdb_v2_logical_type_handle type, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(type); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { - // ANY is a signature wildcard; a value carries data, so reject it. - if (Convert(type)->id() == duckdb::LogicalTypeId::ANY) { - throw duckdb::InvalidInputException("duckdb_v2_value_create_null: type cannot be ANY"); - } - // Value(LogicalType) constructs a typed NULL — exactly what we want. - auto *v = new duckdb::Value(*Convert(type)); - *out_value = Convert(v); - }); -} - DUCKDB_V2_ERROR duckdb_v2_value_is_null(duckdb_v2_value_handle value, bool *out_is_null, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(value); @@ -193,10 +178,7 @@ DUCKDB_V2_ERROR duckdb_v2_value_get_logical_type(duckdb_v2_value_handle value, d DUCKDB_CHECK_ARG(value); DUCKDB_CHECK_ARG(out_type); *out_type = nullptr; - return WithErrorHandler(err, [&]() { - auto *lt = new duckdb::LogicalType(Convert(value)->type()); - *out_type = Convert(lt); - }); + return WithErrorHandler(err, [&]() { *out_type = Convert(Convert(value)->type()); }); } DUCKDB_V2_ERROR duckdb_v2_value_to_string(duckdb_v2_value_handle value, char *out_string, idx_t out_capacity, @@ -335,8 +317,7 @@ DUCKDB_V2_ERROR duckdb_v2_value_get_type(duckdb_v2_value_handle value, duckdb_v2 return WithErrorHandler(err, [&]() { RequireTypedValue(value, duckdb::LogicalTypeId::TYPE); // TypeValue::GetType deserializes the stored type into a fresh copy. - auto *lt = new duckdb::LogicalType(duckdb::TypeValue::GetType(*Convert(value))); - *out_type = Convert(lt); + *out_type = Convert(duckdb::TypeValue::GetType(*Convert(value))); }); } @@ -464,319 +445,141 @@ DUCKDB_V2_ERROR duckdb_v2_value_get_interval(duckdb_v2_value_handle value, duckd // --------------------------------------------------------------------------- // Typed constructors // -// Two forms per type: the context form for a live bind / execution context, -// the connection form for outside one. Both are gated on their handle, which -// is what makes a value's construction scoped to a catalog. +// Gated on the context handle, which is what makes a value's construction +// scoped to a catalog. // --------------------------------------------------------------------------- -DUCKDB_V2_ERROR duckdb_v2_value_create_bool_with_context(duckdb_v2_context_handle ctx, bool in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::BOOLEAN(in_value)); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_bool_with_connection(duckdb_v2_connection_handle conn, bool in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_bool(duckdb_v2_factory_handle factory, bool in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::BOOLEAN(in_value)); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_tinyint_with_context(duckdb_v2_context_handle ctx, int8_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); +DUCKDB_V2_ERROR duckdb_v2_value_create_tinyint(duckdb_v2_factory_handle factory, int8_t in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::TINYINT(in_value)); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_tinyint_with_connection(duckdb_v2_connection_handle conn, int8_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::TINYINT(in_value)); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_smallint_with_context(duckdb_v2_context_handle ctx, int16_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::SMALLINT(in_value)); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_smallint_with_connection(duckdb_v2_connection_handle conn, int16_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_smallint(duckdb_v2_factory_handle factory, int16_t in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::SMALLINT(in_value)); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_int_with_context(duckdb_v2_context_handle ctx, int32_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::INTEGER(in_value)); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_int_with_connection(duckdb_v2_connection_handle conn, int32_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_int(duckdb_v2_factory_handle factory, int32_t in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::INTEGER(in_value)); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_bigint_with_context(duckdb_v2_context_handle ctx, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::BIGINT(in_value)); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_bigint_with_connection(duckdb_v2_connection_handle conn, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_bigint(duckdb_v2_factory_handle factory, int64_t in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::BIGINT(in_value)); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_hugeint_with_context(duckdb_v2_context_handle ctx, duckdb_v2_hugeint_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); +DUCKDB_V2_ERROR duckdb_v2_value_create_hugeint(duckdb_v2_factory_handle factory, const duckdb_v2_hugeint_t *in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::HUGEINT(Convert(in_value))); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_hugeint_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_hugeint_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::HUGEINT(Convert(in_value))); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_utinyint_with_context(duckdb_v2_context_handle ctx, uint8_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::UTINYINT(in_value)); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_utinyint_with_connection(duckdb_v2_connection_handle conn, uint8_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_utinyint(duckdb_v2_factory_handle factory, uint8_t in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::UTINYINT(in_value)); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_usmallint_with_context(duckdb_v2_context_handle ctx, uint16_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::USMALLINT(in_value)); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_usmallint_with_connection(duckdb_v2_connection_handle conn, uint16_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_usmallint(duckdb_v2_factory_handle factory, uint16_t in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::USMALLINT(in_value)); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_uint_with_context(duckdb_v2_context_handle ctx, uint32_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); +DUCKDB_V2_ERROR duckdb_v2_value_create_uint(duckdb_v2_factory_handle factory, uint32_t in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::UINTEGER(in_value)); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_uint_with_connection(duckdb_v2_connection_handle conn, uint32_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::UINTEGER(in_value)); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_ubigint_with_context(duckdb_v2_context_handle ctx, uint64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); +DUCKDB_V2_ERROR duckdb_v2_value_create_ubigint(duckdb_v2_factory_handle factory, uint64_t in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::UBIGINT(in_value)); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_ubigint_with_connection(duckdb_v2_connection_handle conn, uint64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::UBIGINT(in_value)); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_uhugeint_with_context(duckdb_v2_context_handle ctx, - duckdb_v2_uhugeint_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::UHUGEINT(Convert(in_value))); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_uhugeint_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_uhugeint_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_uhugeint(duckdb_v2_factory_handle factory, const duckdb_v2_uhugeint_t *in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::UHUGEINT(Convert(in_value))); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_float_with_context(duckdb_v2_context_handle ctx, float in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::FLOAT(in_value)); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_float_with_connection(duckdb_v2_connection_handle conn, float in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_float(duckdb_v2_factory_handle factory, float in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::FLOAT(in_value)); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_double_with_context(duckdb_v2_context_handle ctx, double in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); +DUCKDB_V2_ERROR duckdb_v2_value_create_double(duckdb_v2_factory_handle factory, double in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::DOUBLE(in_value)); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_double_with_connection(duckdb_v2_connection_handle conn, double in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::DOUBLE(in_value)); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_varchar_with_context(duckdb_v2_context_handle ctx, duckdb_v2_str in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value(Convert(in_value))); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_varchar_with_connection(duckdb_v2_connection_handle conn, duckdb_v2_str in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_varchar(duckdb_v2_factory_handle factory, const duckdb_v2_str *in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value(Convert(in_value))); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_blob_with_context(duckdb_v2_context_handle ctx, duckdb_v2_str in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::BLOB_RAW(std::string(Convert(in_value)))); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_blob_with_connection(duckdb_v2_connection_handle conn, duckdb_v2_str in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_blob(duckdb_v2_factory_handle factory, const duckdb_v2_str *in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::BLOB_RAW(std::string(Convert(in_value)))); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_null_with_context(duckdb_v2_context_handle ctx, - duckdb_v2_logical_type_handle type, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); +DUCKDB_V2_ERROR duckdb_v2_value_create_null(duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle type, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value(RequireValueType(type))); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_null_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_logical_type_handle type, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value(RequireValueType(type))); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_type_with_context(duckdb_v2_context_handle ctx, - duckdb_v2_logical_type_handle in_type, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::TYPE(RequireValueType(in_type))); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_type_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_logical_type_handle in_type, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_type(duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle in_type, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::TYPE(RequireValueType(in_type))); }); @@ -788,212 +591,99 @@ DUCKDB_V2_ERROR duckdb_v2_value_create_type_with_connection(duckdb_v2_connection // The duals of the temporal accessors: same units, no range checking. // --------------------------------------------------------------------------- -DUCKDB_V2_ERROR duckdb_v2_value_create_date_with_context(duckdb_v2_context_handle ctx, int32_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::DATE(duckdb::date_t(in_value))); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_date_with_connection(duckdb_v2_connection_handle conn, int32_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_date(duckdb_v2_factory_handle factory, int32_t in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::DATE(duckdb::date_t(in_value))); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_time_with_context(duckdb_v2_context_handle ctx, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::TIME(duckdb::dtime_t(in_value))); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_time_with_connection(duckdb_v2_connection_handle conn, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_time(duckdb_v2_factory_handle factory, int64_t in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::TIME(duckdb::dtime_t(in_value))); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_time_ns_with_context(duckdb_v2_context_handle ctx, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); +DUCKDB_V2_ERROR duckdb_v2_value_create_time_ns(duckdb_v2_factory_handle factory, int64_t in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::TIME_NS(duckdb::dtime_ns_t(in_value))); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_time_ns_with_connection(duckdb_v2_connection_handle conn, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::TIME_NS(duckdb::dtime_ns_t(in_value))); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_time_tz_with_context(duckdb_v2_context_handle ctx, uint64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::TIMETZ(duckdb::dtime_tz_t(in_value))); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_time_tz_with_connection(duckdb_v2_connection_handle conn, uint64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_time_tz(duckdb_v2_factory_handle factory, uint64_t in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::TIMETZ(duckdb::dtime_tz_t(in_value))); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_with_context(duckdb_v2_context_handle ctx, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::TIMESTAMP(duckdb::timestamp_t(in_value))); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_with_connection(duckdb_v2_connection_handle conn, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp(duckdb_v2_factory_handle factory, int64_t in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::TIMESTAMP(duckdb::timestamp_t(in_value))); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_sec_with_context(duckdb_v2_context_handle ctx, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler( - err, [&]() { *out_value = Emit(duckdb::Value::TIMESTAMPSEC(duckdb::timestamp_sec_t(in_value))); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_sec_with_connection(duckdb_v2_connection_handle conn, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_sec(duckdb_v2_factory_handle factory, int64_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler( err, [&]() { *out_value = Emit(duckdb::Value::TIMESTAMPSEC(duckdb::timestamp_sec_t(in_value))); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_ms_with_context(duckdb_v2_context_handle ctx, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, - [&]() { *out_value = Emit(duckdb::Value::TIMESTAMPMS(duckdb::timestamp_ms_t(in_value))); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_ms_with_connection(duckdb_v2_connection_handle conn, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_ms(duckdb_v2_factory_handle factory, int64_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::TIMESTAMPMS(duckdb::timestamp_ms_t(in_value))); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_ns_with_context(duckdb_v2_context_handle ctx, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, - [&]() { *out_value = Emit(duckdb::Value::TIMESTAMPNS(duckdb::timestamp_ns_t(in_value))); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_ns_with_connection(duckdb_v2_connection_handle conn, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_ns(duckdb_v2_factory_handle factory, int64_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::TIMESTAMPNS(duckdb::timestamp_ns_t(in_value))); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_tz_with_context(duckdb_v2_context_handle ctx, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, - [&]() { *out_value = Emit(duckdb::Value::TIMESTAMPTZ(duckdb::timestamp_tz_t(in_value))); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_tz_with_connection(duckdb_v2_connection_handle conn, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_tz(duckdb_v2_factory_handle factory, int64_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::TIMESTAMPTZ(duckdb::timestamp_tz_t(in_value))); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_tz_ns_with_context(duckdb_v2_context_handle ctx, int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler( - err, [&]() { *out_value = Emit(duckdb::Value::TIMESTAMPTZNS(duckdb::timestamp_tz_ns_t(in_value))); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_tz_ns_with_connection(duckdb_v2_connection_handle conn, - int64_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_timestamp_tz_ns(duckdb_v2_factory_handle factory, int64_t in_value, + duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler( err, [&]() { *out_value = Emit(duckdb::Value::TIMESTAMPTZNS(duckdb::timestamp_tz_ns_t(in_value))); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_interval_with_context(duckdb_v2_context_handle ctx, - duckdb_v2_interval_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::INTERVAL(Convert(in_value))); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_interval_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_interval_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_interval(duckdb_v2_factory_handle factory, const duckdb_v2_interval_t *in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::INTERVAL(Convert(in_value))); }); @@ -1006,76 +696,34 @@ DUCKDB_V2_ERROR duckdb_v2_value_create_interval_with_connection(duckdb_v2_connec // plus its width and scale, an internal 128-bit form, or opaque storage bytes. // --------------------------------------------------------------------------- -DUCKDB_V2_ERROR duckdb_v2_value_create_decimal_with_context(duckdb_v2_context_handle ctx, duckdb_v2_hugeint_t in_value, - uint8_t width, uint8_t scale, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); +DUCKDB_V2_ERROR duckdb_v2_value_create_decimal(duckdb_v2_factory_handle factory, const duckdb_v2_hugeint_t *in_value, + uint8_t width, uint8_t scale, duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(BuildDecimal(in_value, width, scale)); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_uuid_with_context(duckdb_v2_context_handle ctx, duckdb_v2_hugeint_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); +DUCKDB_V2_ERROR duckdb_v2_value_create_uuid(duckdb_v2_factory_handle factory, const duckdb_v2_hugeint_t *in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::UUID(Convert(in_value))); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_bit_with_context(duckdb_v2_context_handle ctx, duckdb_v2_str in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(BuildBit(in_value)); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_bignum_with_context(duckdb_v2_context_handle ctx, duckdb_v2_str in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(BuildBignum(in_value)); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_decimal_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_hugeint_t in_value, uint8_t width, - uint8_t scale, duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(BuildDecimal(in_value, width, scale)); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_uuid_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_hugeint_t in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(duckdb::Value::UUID(Convert(in_value))); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_bit_with_connection(duckdb_v2_connection_handle conn, duckdb_v2_str in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_bit(duckdb_v2_factory_handle factory, const duckdb_v2_str *in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(BuildBit(in_value)); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_bignum_with_connection(duckdb_v2_connection_handle conn, duckdb_v2_str in_value, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_bignum(duckdb_v2_factory_handle factory, const duckdb_v2_str *in_value, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(BuildBignum(in_value)); }); @@ -1106,30 +754,32 @@ duckdb::vector CollectChildren(const duckdb_v2_value_handle *chil // The common type of a set of children, the same rule a list literal follows. // An explicit type wins outright; without one an empty set has nothing to // resolve, which is what makes the empty forms take their type. -duckdb::LogicalType ResolveChildType(duckdb::ClientContext &ctx, duckdb_v2_logical_type_handle declared, +duckdb::LogicalType ResolveChildType(CV2Factory &factory, duckdb_v2_logical_type_handle declared, const duckdb::vector &children, const char *what) { if (declared) { - return *Convert(declared); + auto declared_type = Convert(declared); + return *declared_type; } if (children.empty()) { throw duckdb::InvalidInputException(std::string("cannot resolve the ") + what + " of an empty set; pass it explicitly"); } - auto type = children[0].type(); - for (idx_t i = 1; i < children.size(); i++) { - type = duckdb::LogicalType::MaxLogicalType(ctx, type, children[i].type()); + duckdb::vector types; + types.reserve(children.size()); + for (auto &child : children) { + types.push_back(child.type()); } - return type; + return ResolveCommonType(factory, types); } -duckdb::Value BuildList(duckdb::ClientContext &ctx, duckdb_v2_logical_type_handle child_type, +duckdb::Value BuildList(CV2Factory &factory, duckdb_v2_logical_type_handle child_type, const duckdb_v2_value_handle *children, idx_t child_count) { auto values = CollectChildren(children, child_count); - auto type = ResolveChildType(ctx, child_type, values, "element type"); + auto type = ResolveChildType(factory, child_type, values, "element type"); return duckdb::Value::LIST(type, std::move(values)); } -duckdb::Value BuildArray(duckdb::ClientContext &ctx, duckdb_v2_logical_type_handle child_type, +duckdb::Value BuildArray(CV2Factory &factory, duckdb_v2_logical_type_handle child_type, const duckdb_v2_value_handle *children, idx_t child_count) { auto values = CollectChildren(children, child_count); // The engine's minimum array size is 1, so there is no empty ARRAY, with or @@ -1137,7 +787,7 @@ duckdb::Value BuildArray(duckdb::ClientContext &ctx, duckdb_v2_logical_type_hand if (values.empty()) { throw duckdb::InvalidInputException("an ARRAY must have at least one element"); } - auto type = ResolveChildType(ctx, child_type, values, "element type"); + auto type = ResolveChildType(factory, child_type, values, "element type"); return duckdb::Value::ARRAY(type, std::move(values)); } @@ -1155,7 +805,7 @@ duckdb::Value BuildStruct(const duckdb_v2_identifier_t *names, const duckdb_v2_v if (!names[i].ptr && names[i].len > 0) { throw duckdb::InvalidInputException("field names cannot be null"); } - fields.emplace_back(std::string(names[i].ptr ? names[i].ptr : "", names[i].len), std::move(values[i])); + fields.emplace_back(std::string(ConvertIdentifierName(names[i])), std::move(values[i])); } return duckdb::Value::STRUCT(std::move(fields)); } @@ -1170,136 +820,66 @@ duckdb::Value BuildTuple(const duckdb_v2_value_handle *children, idx_t field_cou return duckdb::Value::STRUCT(duckdb::LogicalType::TUPLE(std::move(types)), std::move(values)); } -duckdb::Value BuildMap(duckdb::ClientContext &ctx, duckdb_v2_logical_type_handle key_type, +duckdb::Value BuildMap(CV2Factory &factory, duckdb_v2_logical_type_handle key_type, duckdb_v2_logical_type_handle value_type, const duckdb_v2_value_handle *keys, const duckdb_v2_value_handle *values, idx_t entry_count) { auto key_values = CollectChildren(keys, entry_count); auto value_values = CollectChildren(values, entry_count); - auto resolved_key = ResolveChildType(ctx, key_type, key_values, "key type"); - auto resolved_value = ResolveChildType(ctx, value_type, value_values, "value type"); + auto resolved_key = ResolveChildType(factory, key_type, key_values, "key type"); + auto resolved_value = ResolveChildType(factory, value_type, value_values, "value type"); return duckdb::Value::MAP(resolved_key, resolved_value, std::move(key_values), std::move(value_values)); } } // namespace -DUCKDB_V2_ERROR duckdb_v2_value_create_list_with_context(duckdb_v2_context_handle ctx, - duckdb_v2_logical_type_handle child_type, - const duckdb_v2_value_handle *children, idx_t child_count, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, - [&]() { *out_value = Emit(BuildList(*Convert(ctx), child_type, children, child_count)); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_list_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_logical_type_handle child_type, - const duckdb_v2_value_handle *children, idx_t child_count, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { - auto &ctx = *Convert(conn)->context; - ctx.RunFunctionInTransaction([&]() { *out_value = Emit(BuildList(ctx, child_type, children, child_count)); }); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_array_with_context(duckdb_v2_context_handle ctx, - duckdb_v2_logical_type_handle child_type, - const duckdb_v2_value_handle *children, idx_t child_count, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, - [&]() { *out_value = Emit(BuildArray(*Convert(ctx), child_type, children, child_count)); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_array_with_connection(duckdb_v2_connection_handle conn, - duckdb_v2_logical_type_handle child_type, - const duckdb_v2_value_handle *children, idx_t child_count, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_list(duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle child_type, + const duckdb_v2_value_handle *children, idx_t child_count, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; - return WithErrorHandler(err, [&]() { - auto &ctx = *Convert(conn)->context; - ctx.RunFunctionInTransaction([&]() { *out_value = Emit(BuildArray(ctx, child_type, children, child_count)); }); - }); + return WithErrorHandler( + err, [&]() { *out_value = Emit(BuildList(*Convert(factory), child_type, children, child_count)); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_struct_with_context(duckdb_v2_context_handle ctx, - const duckdb_v2_identifier_t *names, - const duckdb_v2_value_handle *children, idx_t field_count, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); +DUCKDB_V2_ERROR duckdb_v2_value_create_array(duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle child_type, + const duckdb_v2_value_handle *children, idx_t child_count, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(BuildStruct(names, children, field_count)); }); + return WithErrorHandler( + err, [&]() { *out_value = Emit(BuildArray(*Convert(factory), child_type, children, child_count)); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_struct_with_connection(duckdb_v2_connection_handle conn, - const duckdb_v2_identifier_t *names, - const duckdb_v2_value_handle *children, idx_t field_count, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_struct(duckdb_v2_factory_handle factory, const duckdb_v2_identifier_t *names, + const duckdb_v2_value_handle *children, idx_t field_count, + duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(BuildStruct(names, children, field_count)); }); } -DUCKDB_V2_ERROR duckdb_v2_value_create_tuple_with_context(duckdb_v2_context_handle ctx, - const duckdb_v2_value_handle *children, idx_t field_count, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler(err, [&]() { *out_value = Emit(BuildTuple(children, field_count)); }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_create_tuple_with_connection(duckdb_v2_connection_handle conn, - const duckdb_v2_value_handle *children, idx_t field_count, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +DUCKDB_V2_ERROR duckdb_v2_value_create_tuple(duckdb_v2_factory_handle factory, const duckdb_v2_value_handle *children, + idx_t field_count, duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { *out_value = Emit(BuildTuple(children, field_count)); }); } DUCKDB_V2_ERROR -duckdb_v2_value_create_map_with_context(duckdb_v2_context_handle ctx, duckdb_v2_logical_type_handle key_type, - duckdb_v2_logical_type_handle value_type, const duckdb_v2_value_handle *keys, - const duckdb_v2_value_handle *values, idx_t entry_count, - duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); - DUCKDB_CHECK_ARG(out_value); - *out_value = nullptr; - return WithErrorHandler( - err, [&]() { *out_value = Emit(BuildMap(*Convert(ctx), key_type, value_type, keys, values, entry_count)); }); -} - -DUCKDB_V2_ERROR -duckdb_v2_value_create_map_with_connection(duckdb_v2_connection_handle conn, duckdb_v2_logical_type_handle key_type, - duckdb_v2_logical_type_handle value_type, const duckdb_v2_value_handle *keys, - const duckdb_v2_value_handle *values, idx_t entry_count, - duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); +duckdb_v2_value_create_map(duckdb_v2_factory_handle factory, duckdb_v2_logical_type_handle key_type, + duckdb_v2_logical_type_handle value_type, const duckdb_v2_value_handle *keys, + const duckdb_v2_value_handle *values, idx_t entry_count, duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(out_value); *out_value = nullptr; return WithErrorHandler(err, [&]() { - auto &ctx = *Convert(conn)->context; - ctx.RunFunctionInTransaction( - [&]() { *out_value = Emit(BuildMap(ctx, key_type, value_type, keys, values, entry_count)); }); + *out_value = Emit(BuildMap(*Convert(factory), key_type, value_type, keys, values, entry_count)); }); } @@ -1350,35 +930,23 @@ DUCKDB_V2_ERROR duckdb_v2_value_get_child(duckdb_v2_value_handle value, idx_t in }); } -static void CastValueV2(duckdb::ClientContext &ctx, duckdb_v2_value_handle value, - duckdb_v2_logical_type_handle target_type, duckdb_v2_value_handle *out_value) { +static void CastValueV2(CV2Factory &factory, duckdb_v2_value_handle value, duckdb_v2_logical_type_handle target_type, + duckdb_v2_value_handle *out_value) { *out_value = nullptr; - // Non-strict, through the context's cast function set (registered - // custom casts included). Cast failures propagate. - auto casted = Convert(value)->CastAs(ctx, *Convert(target_type)); + // Non-strict, through the client's cast function set (registered custom + // casts included), or the built-in casts only. Cast failures propagate. + auto target = Convert(target_type); + auto context = factory.TryGetClientContext(); + auto casted = context ? Convert(value)->CastAs(*context, *target) : Convert(value)->DefaultCastAs(*target); *out_value = Convert(new duckdb::Value(std::move(casted))); } -DUCKDB_V2_ERROR duckdb_v2_value_cast_with_connection(duckdb_v2_connection_handle conn, duckdb_v2_value_handle value, - duckdb_v2_logical_type_handle target_type, - duckdb_v2_value_handle *out_value, - duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(conn); - DUCKDB_CHECK_ARG(value); - DUCKDB_CHECK_ARG(target_type); - DUCKDB_CHECK_ARG(out_value); - return WithErrorHandler(err, [&]() { - auto &ctx = *Convert(conn)->context; - ctx.RunFunctionInTransaction([&]() { CastValueV2(ctx, value, target_type, out_value); }); - }); -} - -DUCKDB_V2_ERROR duckdb_v2_value_cast_with_context(duckdb_v2_context_handle ctx, duckdb_v2_value_handle value, - duckdb_v2_logical_type_handle target_type, - duckdb_v2_value_handle *out_value, duckdb_v2_error_info_handle *err) { - DUCKDB_CHECK_ARG(ctx); +DUCKDB_V2_ERROR duckdb_v2_value_cast(duckdb_v2_factory_handle factory, duckdb_v2_value_handle value, + duckdb_v2_logical_type_handle target_type, duckdb_v2_value_handle *out_value, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(factory); DUCKDB_CHECK_ARG(value); DUCKDB_CHECK_ARG(target_type); DUCKDB_CHECK_ARG(out_value); - return WithErrorHandler(err, [&]() { CastValueV2(*Convert(ctx), value, target_type, out_value); }); + return WithErrorHandler(err, [&]() { CastValueV2(*Convert(factory), value, target_type, out_value); }); } diff --git a/src/duckdb/src/main/capi/v2/capi_v2_vector.cpp b/src/duckdb/src/main/capi/v2/capi_v2_vector.cpp index 47f9bb17f..74f634bb9 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_vector.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_vector.cpp @@ -1,5 +1,6 @@ #include "duckdb/main/capi_v2/capi_v2_internal.hpp" +#include "duckdb/common/exception/conversion_exception.hpp" #include "duckdb/common/types/vector.hpp" #include "duckdb/common/vector/array_vector.hpp" #include "duckdb/common/vector/constant_vector.hpp" @@ -9,6 +10,7 @@ #include "duckdb/common/vector/map_vector.hpp" #include "duckdb/common/vector/struct_vector.hpp" #include "duckdb/common/vector/union_vector.hpp" +#include "duckdb/common/vector_operations/vector_operations.hpp" // Out-param zeroing on failure: // - Pointer-bearing out-params (out_view, out_child, out_data) are @@ -66,8 +68,7 @@ DUCKDB_V2_ERROR duckdb_v2_vector_get_logical_type(duckdb_v2_vector_handle vector } *out_type = nullptr; auto *vec = Convert(vector); - auto *lt = new duckdb::LogicalType(vec->GetType()); - *out_type = reinterpret_cast<_duckdb_v2_logical_type *>(lt); + *out_type = Convert(vec->GetType()); }); } @@ -76,11 +77,64 @@ DUCKDB_V2_ERROR duckdb_v2_vector_flatten(duckdb_v2_vector_handle vector, duckdb_ return WithErrorHandler(err, [&]() { Convert(vector)->Flatten(); }); } +DUCKDB_V2_ERROR duckdb_v2_vector_cast(duckdb_v2_factory_handle factory, duckdb_v2_vector_handle source, + duckdb_v2_vector_handle target, DUCKDB_V2_CAST_MODE mode, bool *out_converted, + duckdb_v2_error_info_handle *cast_err, duckdb_v2_error_info_handle *err) { + if (cast_err) { + *cast_err = nullptr; + } + DUCKDB_CHECK_ARG(factory); + DUCKDB_CHECK_ARG(source); + DUCKDB_CHECK_ARG(target); + return WithErrorHandler(err, [&]() { + auto *src = Convert(source); + auto *dst = Convert(target); + if (src == dst) { + throw duckdb::InvalidInputException("duckdb_v2_vector_cast: source and target must be different vectors"); + } + if (mode != DUCKDB_V2_CAST_MODE_NORMAL && mode != DUCKDB_V2_CAST_MODE_TRY) { + throw duckdb::InvalidInputException("duckdb_v2_vector_cast: invalid cast mode %d", static_cast(mode)); + } + const auto count = src->size(); + // a try cast collects the error instead of throwing, and NULLs the rows that failed + duckdb::string error; + auto error_ptr = mode == DUCKDB_V2_CAST_MODE_TRY ? &error : nullptr; + auto context = Convert(factory)->TryGetClientContext(); + // the casts expect a fresh result, so stale validity or data in the target cannot leak into it + duckdb::Vector result(dst->GetType(), count); + auto converted = context ? duckdb::VectorOperations::TryCast(*context, *src, result, count, error_ptr) + : duckdb::VectorOperations::DefaultTryCast(*src, result, count, error_ptr); + dst->Reference(result); + if (out_converted) { + *out_converted = converted; + } + if (cast_err && !converted) { + // rendered like any error the API reports, as a conversion error + auto info = duckdb::make_uniq(); + try { + throw duckdb::ConversionException(error); + } catch (...) { + RenderCaughtError(info->code, info->message, info->raw_message); + } + *cast_err = Convert(info.release()); + } + }); +} + DUCKDB_V2_ERROR duckdb_v2_vector_reference(duckdb_v2_vector_handle vector, duckdb_v2_vector_handle source, duckdb_v2_error_info_handle *err) { DUCKDB_CHECK_ARG(vector); DUCKDB_CHECK_ARG(source); - return WithErrorHandler(err, [&]() { Convert(vector)->Reference(*Convert(source)); }); + return WithErrorHandler(err, [&]() { + auto *vec = Convert(vector); + auto *src = Convert(source); + // The engine reports a mismatch as an INTERNAL error, and only by type id. + if (vec->GetType() != src->GetType()) { + throw duckdb::InvalidInputException( + "duckdb_v2_vector_reference: source type does not match the vector's logical type"); + } + vec->Reference(*src); + }); } // --------------------------------------------------------------------------- @@ -238,8 +292,7 @@ DUCKDB_V2_ERROR duckdb_v2_vector_constant_set_valid(duckdb_v2_vector_handle vect // // Per-kind child counts: // LIST → 1 child ([0] = elements) -// MAP → 2 children ([0] = keys, [1] = values; V2 hides MAP's -// internal LIST) +// MAP → 1 child ([0] = entries, a STRUCT(key, value)) // ARRAY → 1 child ([0] = elements) // STRUCT → N children ([i] = field i) // UNION → N+1 children ([0] = tag, [1..N] = members) @@ -254,12 +307,10 @@ DUCKDB_V2_ERROR duckdb_v2_vector_get_child_count(duckdb_v2_vector_handle vector, auto *vec = Convert(vector); switch (vec->GetType().id()) { case duckdb::LogicalTypeId::LIST: + case duckdb::LogicalTypeId::MAP: case duckdb::LogicalTypeId::ARRAY: *out_count = 1; return; - case duckdb::LogicalTypeId::MAP: - *out_count = 2; - return; case duckdb::LogicalTypeId::STRUCT: case duckdb::LogicalTypeId::TUPLE: *out_count = duckdb::StructType::GetChildCount(vec->GetType()); @@ -290,6 +341,15 @@ DUCKDB_V2_ERROR duckdb_v2_vector_get_child(duckdb_v2_vector_handle vector, idx_t *out_child = Convert(&child); return; } + case duckdb::LogicalTypeId::MAP: { + if (index != 0) { + throw duckdb::InvalidInputException( + "duckdb_v2_vector_get_child: MAP has only child [0] (entries, a STRUCT(key, value))"); + } + auto &entries = duckdb::ListVector::GetChildMutable(*vec); + *out_child = Convert(&entries); + return; + } case duckdb::LogicalTypeId::ARRAY: { if (index != 0) { throw duckdb::InvalidInputException("duckdb_v2_vector_get_child: ARRAY has only child [0] (elements)"); @@ -298,21 +358,6 @@ DUCKDB_V2_ERROR duckdb_v2_vector_get_child(duckdb_v2_vector_handle vector, idx_t *out_child = Convert(&child); return; } - case duckdb::LogicalTypeId::MAP: { - // V2 hides MAP's internal LIST: child [0] is the - // key vector, child [1] is the value vector. - if (index == 0) { - auto &keys = duckdb::MapVector::GetKeys(*vec); - *out_child = Convert(&keys); - return; - } - if (index == 1) { - auto &values = duckdb::MapVector::GetValues(*vec); - *out_child = Convert(&values); - return; - } - throw duckdb::InvalidInputException("duckdb_v2_vector_get_child: MAP children are [0]=keys, [1]=values"); - } case duckdb::LogicalTypeId::STRUCT: case duckdb::LogicalTypeId::TUPLE: { auto &entries = duckdb::StructVector::GetEntries(*vec); diff --git a/src/duckdb/src/main/chunk_scan_state.cpp b/src/duckdb/src/main/chunk_scan_state.cpp deleted file mode 100644 index aab054c99..000000000 --- a/src/duckdb/src/main/chunk_scan_state.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "duckdb/common/types/data_chunk.hpp" -#include "duckdb/main/chunk_scan_state.hpp" - -namespace duckdb { - -ChunkScanState::ChunkScanState() { -} - -ChunkScanState::~ChunkScanState() { -} - -idx_t ChunkScanState::CurrentOffset() const { - return offset; -} - -void ChunkScanState::IncreaseOffset(idx_t increment, bool unsafe) { - D_ASSERT(unsafe || increment <= RemainingInChunk()); - offset += increment; -} - -bool ChunkScanState::ChunkIsEmpty() const { - return !current_chunk || current_chunk->size() == 0; -} - -bool ChunkScanState::Finished() const { - return finished; -} - -bool ChunkScanState::ScanStarted() const { - return !ChunkIsEmpty(); -} - -DataChunk &ChunkScanState::CurrentChunk() { - // Scan must already be started - D_ASSERT(current_chunk); - return *current_chunk; -} - -idx_t ChunkScanState::RemainingInChunk() const { - if (ChunkIsEmpty()) { - return 0; - } - D_ASSERT(current_chunk); - D_ASSERT(offset <= current_chunk->size()); - return current_chunk->size() - offset; -} - -} // namespace duckdb diff --git a/src/duckdb/src/main/chunk_scan_state/batched_data_collection.cpp b/src/duckdb/src/main/chunk_scan_state/batched_data_collection.cpp deleted file mode 100644 index 965158338..000000000 --- a/src/duckdb/src/main/chunk_scan_state/batched_data_collection.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "duckdb/main/chunk_scan_state/batched_data_collection.hpp" -#include "duckdb/storage/buffer_manager.hpp" - -namespace duckdb { - -BatchCollectionChunkScanState::BatchCollectionChunkScanState(BatchedDataCollection &collection, - BatchedChunkIteratorRange &range, ClientContext &context) - : ChunkScanState(), collection(collection) { - collection.InitializeScan(state, range); - current_chunk = make_uniq(); - auto &allocator = BufferManager::GetBufferManager(context).GetBufferAllocator(); - current_chunk->Initialize(allocator, collection.Types()); -} - -BatchCollectionChunkScanState::~BatchCollectionChunkScanState() { -} - -void BatchCollectionChunkScanState::InternalLoad(ErrorData &error) { - if (state.range.begin == state.range.end) { - // Signal empty chunk to break out of the loop - current_chunk->SetChildCardinality(0); - return; - } - offset = 0; - current_chunk->Reset(); - collection.Scan(state, *current_chunk); - return; -} - -bool BatchCollectionChunkScanState::HasError() const { - return false; -} - -ErrorData &BatchCollectionChunkScanState::GetError() { - throw NotImplementedException("BatchDataCollections don't have an internal error object"); -} - -const vector &BatchCollectionChunkScanState::Types() const { - return collection.Types(); -} - -const vector &BatchCollectionChunkScanState::Names() const { - throw NotImplementedException("BatchDataCollections don't have names"); -} - -bool BatchCollectionChunkScanState::LoadNextChunk(ErrorData &error) { - if (finished) { - return false; - } - InternalLoad(error); - if (ChunkIsEmpty()) { - finished = true; - } - return true; -} - -} // namespace duckdb diff --git a/src/duckdb/src/main/chunk_scan_state/query_result.cpp b/src/duckdb/src/main/chunk_scan_state/query_result.cpp deleted file mode 100644 index 76a172dd2..000000000 --- a/src/duckdb/src/main/chunk_scan_state/query_result.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "duckdb/main/query_result.hpp" -#include "duckdb/main/stream_query_result.hpp" -#include "duckdb/main/chunk_scan_state/query_result.hpp" - -namespace duckdb { - -QueryResultChunkScanState::QueryResultChunkScanState(QueryResult &result) : ChunkScanState(), result(result) { -} - -QueryResultChunkScanState::~QueryResultChunkScanState() { -} - -bool QueryResultChunkScanState::InternalLoad(ErrorData &error) { - D_ASSERT(!finished); - if (result.GetResultType() == QueryResultType::STREAM_RESULT) { - auto &stream_result = result.Cast(); - if (!stream_result.IsOpen()) { - return true; - } - } - return result.TryFetch(current_chunk, error); -} - -bool QueryResultChunkScanState::HasError() const { - return result.HasError(); -} - -ErrorData &QueryResultChunkScanState::GetError() { - D_ASSERT(result.HasError()); - return result.GetErrorObject(); -} - -const vector &QueryResultChunkScanState::Types() const { - return result.GetTypes(); -} - -const vector &QueryResultChunkScanState::Names() const { - return result.GetNames(); -} - -bool QueryResultChunkScanState::LoadNextChunk(ErrorData &error) { - if (finished) { - return !finished; - } - auto load_result = InternalLoad(error); - if (!load_result) { - finished = true; - } - offset = 0; - return !finished; -} - -} // namespace duckdb diff --git a/src/duckdb/src/main/client_context.cpp b/src/duckdb/src/main/client_context.cpp index 1fafe97dd..62ae13551 100644 --- a/src/duckdb/src/main/client_context.cpp +++ b/src/duckdb/src/main/client_context.cpp @@ -11,11 +11,11 @@ #include "duckdb/common/types/column/column_data_collection.hpp" #include "duckdb/common/types/uuid.hpp" #include "duckdb/execution/column_binding_resolver.hpp" -#include "duckdb/execution/operator/helper/physical_result_collector.hpp" #include "duckdb/execution/physical_plan_generator.hpp" #include "duckdb/main/appender.hpp" #include "duckdb/main/attached_database.hpp" #include "duckdb/main/client_context_file_opener.hpp" +#include "duckdb/main/active_query_context.hpp" #include "duckdb/main/client_context_state.hpp" #include "duckdb/main/client_data.hpp" #include "duckdb/main/database.hpp" @@ -23,38 +23,25 @@ #include "duckdb/main/statement_iterator.hpp" #include "duckdb/main/error_manager.hpp" #include "duckdb/main/parse_iterator.hpp" -#include "duckdb/main/materialized_query_result.hpp" +#include "duckdb/main/prepared_statement_data.hpp" #include "duckdb/main/query_profiler.hpp" #include "duckdb/main/query_result.hpp" #include "duckdb/main/relation.hpp" -#include "duckdb/main/stream_query_result.hpp" #include "duckdb/optimizer/optimizer.hpp" #include "duckdb/parser/expression/constant_expression.hpp" -#include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/parser/expression/star_expression.hpp" -#include "duckdb/parser/tableref/table_function_ref.hpp" -#include "duckdb/parser/expression/parameter_expression.hpp" #include "duckdb/parser/parsed_data/create_function_info.hpp" #include "duckdb/parser/parser.hpp" #include "duckdb/parser/peg/compiled_grammar.hpp" #include "duckdb/parser/query_node/select_node.hpp" -#include "duckdb/parser/statement/drop_statement.hpp" #include "duckdb/parser/statement/execute_statement.hpp" #include "duckdb/parser/statement/explain_statement.hpp" -#include "duckdb/parser/statement/delete_statement.hpp" -#include "duckdb/parser/statement/insert_statement.hpp" -#include "duckdb/parser/statement/merge_into_statement.hpp" -#include "duckdb/parser/statement/update_statement.hpp" -#include "duckdb/parser/query_node/update_query_node.hpp" #include "duckdb/parser/statement/prepare_statement.hpp" #include "duckdb/parser/statement/relation_statement.hpp" #include "duckdb/parser/statement/select_statement.hpp" -#include "duckdb/parser/tableref/column_data_ref.hpp" #include "duckdb/planner/binder.hpp" #include "duckdb/planner/logical_plan_verifier.hpp" -#include "duckdb/planner/operator/logical_execute.hpp" #include "duckdb/planner/planner.hpp" -#include "duckdb/common/enums/current_transaction_state.hpp" #include "duckdb/planner/statement_preprocessor.hpp" #include "duckdb/storage/data_table.hpp" #include "duckdb/transaction/meta_transaction.hpp" @@ -63,6 +50,7 @@ #include "duckdb/logging/log_type.hpp" #include "duckdb/logging/log_manager.hpp" #include "duckdb/main/settings.hpp" +#include "duckdb/main/result_format.hpp" #include "duckdb/main/result_set_manager.hpp" #include "duckdb/parser/statement/transaction_statement.hpp" #include "duckdb/main/prepared_statement.hpp" @@ -85,53 +73,6 @@ static bool OsxRosettaIsActive() { namespace duckdb { -struct ActiveQueryContext { -public: - //! The query that is currently being executed - string query; - //! Prepared statement data - shared_ptr prepared; - //! The query executor - unique_ptr executor; - //! The progress bar - unique_ptr progress_bar; - -public: - void SetOpenResult(BaseQueryResult &result) { - open_result = &result; - } - bool IsOpenResult(BaseQueryResult &result) { - return open_result == &result; - } - bool HasOpenResult() const { - return open_result != nullptr; - } - -private: - //! The currently open result - BaseQueryResult *open_result = nullptr; -}; - -//! RAII wrapper that ensures the active query is reset if an exception occurs during preparation -struct ActiveQueryGuard { - unique_ptr &active_query; - bool set_active_query; - - ActiveQueryGuard(unique_ptr &active_query_p, const string &query) - : active_query(active_query_p), set_active_query(false) { - if (!active_query) { - active_query = make_uniq(); - set_active_query = true; - active_query->query = query; - } - } - ~ActiveQueryGuard() { - if (set_active_query) { - active_query.reset(); - } - } -}; - #ifdef DEBUG struct DebugClientContextState : public ClientContextState { ~DebugClientContextState() override { @@ -239,11 +180,13 @@ void ClientContext::ConnectToCatalog(const shared_ptr &target) } connected_to_database = target; is_connected = true; + ClientConfig::GetConfig(*this).connected_grammar = target->GetConnectedGrammar(*this); } void ClientContext::DisconnectFromCatalog() { connected_to_database.reset(); is_connected = false; + ClientConfig::GetConfig(*this).connected_grammar.reset(); } shared_ptr ClientContext::TryGetConnectedCatalog() const { @@ -275,7 +218,11 @@ void ClientContext::Destroy() { if (transaction.HasActiveTransaction()) { transaction.ResetActiveQuery(); if (!transaction.IsAutoCommit()) { - transaction.Rollback(nullptr); + // a destructor cannot throw: once the database is invalidated, the rollback can fail as well + try { + transaction.Rollback(nullptr); + } catch (...) { // NOLINT + } } } CleanupInternal(*lock); @@ -292,184 +239,68 @@ void ClientContext::ProcessError(ErrorData &error, const string &query) const { template unique_ptr ClientContext::ErrorResult(ErrorData error, const string &query) { - ProcessError(error, query); - return make_uniq(std::move(error)); -} - -void ClientContext::BeginQueryInternal(ClientContextLock &lock, const SQLStatement &statement) { - // check if we are on AutoCommit. In this case we should start a transaction - D_ASSERT(!active_query); - auto &db_inst = DatabaseInstance::GetDatabase(*this); - if (ValidChecker::IsInvalidated(db_inst)) { - throw ErrorManager::InvalidatedDatabase(*this, ValidChecker::InvalidatedMessage(db_inst)); + bool invalidates_transaction = true; + if (!ErrorInvalidatesTransaction(error.Type())) { + // standard exceptions don't invalidate the transaction + invalidates_transaction = false; + } else if (Exception::InvalidatesDatabase(error.Type())) { + auto &db_instance = DatabaseInstance::GetDatabase(*this); + ValidChecker::Invalidate(db_instance, error.RawMessage()); } - active_query = make_uniq(); - if (transaction.IsAutoCommit()) { - transaction.BeginTransaction(); + if (invalidates_transaction && HasActiveTransaction()) { + ValidChecker::Invalidate(ActiveTransaction(), error.RawMessage()); } - transaction.SetActiveQuery(db->GetDatabaseManager().GetNewQueryNumber()); - auto &query = statement.query; - LogQueryInternal(lock, query); - active_query->query = query; - - query_progress.Initialize(); - // Set query deadline if max_execution_time is configured - auto max_execution_time = Settings::Get(*this); - if (max_execution_time > 0) { - auto now = steady_clock::now(); - auto deadline_tp = now + milliseconds(max_execution_time); - query_deadline = NumericCast(duration_cast(deadline_tp.time_since_epoch()).count()); - } else { - query_deadline.SetInvalid(); - } - // Notify any registered state of query begin - for (auto &state : registered_state->States()) { - state->QueryBegin(*this); - } - - // Flush the old logger. - logger->Flush(); - - // Refresh the logger to ensure we are in sync with the global log settings. - LoggingContext logging_context(LogContextScope::CONNECTION); - logging_context.connection_id = connection_id; - logging_context.transaction_id = transaction.ActiveTransaction().global_transaction_id; - logging_context.query_id = transaction.GetActiveQuery(); - logger = db->GetLogManager().CreateLogger(logging_context, true); - DUCKDB_LOG(*this, QueryLogType, query); + ProcessError(error, query); + return make_uniq(std::move(error)); } -ErrorData ClientContext::EndQueryInternal(ClientContextLock &lock, bool success, bool invalidate_transaction, - optional_ptr previous_error) { - if (active_query->executor) { - active_query->executor->CancelTasks(); - } - active_query->progress_bar.reset(); - D_ASSERT(active_query.get()); - active_query.reset(); - query_deadline.SetInvalid(); - query_progress.Initialize(); - ErrorData error; - try { - if (transaction.HasActiveTransaction()) { - transaction.ResetActiveQuery(); - if (transaction.IsAutoCommit()) { - if (success) { - transaction.Commit(); - } else { - transaction.Rollback(previous_error); - } - } else if (invalidate_transaction) { - D_ASSERT(!success); - ValidChecker::Invalidate(ActiveTransaction(), "Failed to commit"); - } - } - } catch (std::exception &ex) { - error = ErrorData(ex); - if (Exception::InvalidatesDatabase(error.Type()) || error.Type() == ExceptionType::INTERNAL) { - auto &db_inst = DatabaseInstance::GetDatabase(*this); - ValidChecker::Invalidate(db_inst, error.RawMessage()); - } - } catch (...) { // LCOV_EXCL_START - error = ErrorData("Unhandled exception!"); - } // LCOV_EXCL_STOP - - client_data->profiler->EndQuery(); - - // Refresh the logger - logger->Flush(); - LoggingContext context(LogContextScope::CONNECTION); - context.connection_id = connection_id; - logger = db->GetLogManager().CreateLogger(context, true); - - // Notify any registered state of query end - for (auto const &s : registered_state->States()) { - if (error.HasError()) { - s->QueryEnd(*this, &error); - } else { - s->QueryEnd(*this, previous_error); - } - } - return error; +bool ClientContext::OpenResultHoldsConnection(ClientContextLock &) { + // A query no result holds (one whose begin failed), or whose result a collector built so that nothing can end + // it, is abandoned instead + return active_query && active_query->HasOpenResult() && !active_query->collector_built_result; } -void ClientContext::CleanupInternal(ClientContextLock &lock, BaseQueryResult *result, bool invalidate_transaction) { - if (!active_query) { - // no query currently active - return; - } - if (active_query->executor) { - active_query->executor->CancelTasks(); - } - active_query->progress_bar.reset(); - - // Relaunch the threads if a SET THREADS command was issued - auto &scheduler = TaskScheduler::GetScheduler(*this); - scheduler.RelaunchThreads(); - - if (result && result->GetResultType() == QueryResultType::STREAM_RESULT) { - // Record the streaming buffer peak while the profiler is still running - auto &stream_result = static_cast(*result); - if (stream_result.HasBufferedData()) { - QueryProfiler::Get(*this).SetStreamingPeakBufferSize(stream_result.GetBufferedData().PeakBufferedBytes()); - } - } - - optional_ptr passed_error = nullptr; - if (result && result->HasError()) { - passed_error = result->GetErrorObject(); - } - auto error = EndQueryInternal(lock, result ? !result->HasError() : false, invalidate_transaction, passed_error); - if (result && !result->HasError()) { - // if an error occurred while committing report it in the result - result->SetError(error); +template +unique_ptr ClientContext::InitialCleanup(ClientContextLock &lock) { + if (OpenResultHoldsConnection(lock)) { + // The statement never ran, so no invalidation policy applies to the open result's transaction + auto error = ErrorData(ResourceInUseException(OPEN_RESULT_ERROR)); + ProcessError(error, string()); + return make_uniq(std::move(error)); } - D_ASSERT(!active_query); + AbandonActiveQuery(lock); + return nullptr; } -Executor &ClientContext::GetExecutor() { - D_ASSERT(active_query); - D_ASSERT(active_query->executor); - return *active_query->executor; +void ClientContext::VerifyNoOpenResult() { + auto lock = LockContext(); + if (OpenResultHoldsConnection(*lock)) { + throw ResourceInUseException(OPEN_RESULT_ERROR); + } } Logger &ClientContext::GetLogger() const { return *logger; } -const string &ClientContext::GetCurrentQuery() { - D_ASSERT(active_query); - return active_query->query; -} - connection_t ClientContext::GetConnectionId() const { return connection_id; } -unique_ptr ClientContext::FetchResultInternal(ClientContextLock &lock, PendingQueryResult &pending) { - D_ASSERT(active_query); - D_ASSERT(active_query->IsOpenResult(pending)); - D_ASSERT(active_query->prepared); - auto &executor = GetExecutor(); - // A streaming request always plans a streaming sink, so the collector alone decides - const bool keep_result_open = executor.HasStreamingResultCollector(); - unique_ptr result; - D_ASSERT(executor.HasResultCollector()); - // we have a result collector - fetch the result directly from the result collector - result = executor.GetResult(); - if (!keep_result_open) { - CleanupInternal(lock, result.get(), false); - } else { - active_query->SetOpenResult(*result); - } - return result; -} - -static bool IsExplainAnalyze(SQLStatement *statement) { +static bool IsExplainAnalyze(ClientContext &context, SQLStatement *statement) { if (!statement) { return false; } + if (statement->type == StatementType::EXECUTE_STATEMENT) { + // the profiler state set up when preparing is gone by the time the statement is executed + auto &prepared_statements = ClientData::Get(context).prepared_statements; + auto entry = prepared_statements.find(statement->Cast().name); + if (entry == prepared_statements.end()) { + return false; + } + return IsExplainAnalyze(context, entry->second->unbound_statement.get()); + } if (statement->type != StatementType::EXPLAIN_STATEMENT) { return false; } @@ -479,15 +310,15 @@ static bool IsExplainAnalyze(SQLStatement *statement) { shared_ptr ClientContext::CreatePreparedStatementInternal(ClientContextLock &lock, unique_ptr statement, - PendingQueryParameters parameters) { + const QueryParameters ¶meters) { StatementType statement_type = statement->type; auto result = make_shared_ptr(statement_type); auto &profiler = QueryProfiler::Get(*this); - profiler.StartQuery(statement->query, IsExplainAnalyze(statement.get())); + profiler.StartQuery(statement->query, IsExplainAnalyze(*this, statement.get())); Planner logical_planner(*this); - if (parameters.parameters) { - auto ¶meter_values = *parameters.parameters; + if (parameters.statement_args) { + auto ¶meter_values = *parameters.statement_args; for (auto &value : parameter_values) { logical_planner.parameter_data.emplace(value.first, BoundParameterData(value.second)); } @@ -499,6 +330,9 @@ shared_ptr ClientContext::CreatePreparedStatementInternal D_ASSERT(logical_planner.plan || !logical_planner.properties.bound_all_parameters); } + if (logical_planner.properties.bound_all_parameters) { + logical_planner.Optimize(); + } auto logical_plan = std::move(logical_planner.plan); // extract the result column names from the plan result->properties = logical_planner.properties; @@ -509,31 +343,6 @@ shared_ptr ClientContext::CreatePreparedStatementInternal // not all parameters were bound - return return result; } -#ifdef DEBUG - logical_plan->Verify(*this); -#endif - bool optimize = Settings::Get(*this); - if (Settings::Get(*this)) { - // verify disable optimizer - disable EXCEPT for explain, otherwise every single EXPLAIN query breaks - if (logical_plan->type != LogicalOperatorType::LOGICAL_EXPLAIN) { - optimize = false; - } - } - if (logical_plan->RequireOptimizer()) { - { - auto optimizer_timer = profiler.StartTimer(); - Optimizer optimizer(*logical_planner.binder, *this); - if (optimize) { - logical_plan = optimizer.Optimize(std::move(logical_plan)); - } else { - logical_plan = optimizer.LowerMandatoryAggregateRewrites(std::move(logical_plan)); - } - D_ASSERT(logical_plan); - } -#ifdef DEBUG - logical_plan->Verify(*this); -#endif - } // Convert the logical query plan into a physical query plan. { @@ -547,7 +356,7 @@ shared_ptr ClientContext::CreatePreparedStatementInternal shared_ptr ClientContext::CreatePreparedStatement(ClientContextLock &lock, unique_ptr statement, - PendingQueryParameters parameters) { + const QueryParameters ¶meters) { // check if any client context state could request a rebind bool can_request_rebind = false; for (auto &state : registered_state->States()) { @@ -596,18 +405,6 @@ QueryProgress ClientContext::GetQueryProgress() { return query_progress; } -void BindPreparedStatementParameters(ClientContext &context, PreparedStatementData &statement, - const PendingQueryParameters ¶meters) { - identifier_map_t owned_values; - if (parameters.parameters) { - auto ¶ms = *parameters.parameters; - for (auto &val : params) { - owned_values.emplace(val); - } - } - statement.Bind(context, owned_values); -} - void ClientContext::CheckIfPreparedStatementIsExecutable(PreparedStatementData &statement) { if (ValidChecker::IsInvalidated(ActiveTransaction()) && statement.properties.requires_valid_transaction) { throw ErrorManager::InvalidatedTransaction(*this); @@ -631,67 +428,13 @@ void ClientContext::CheckIfPreparedStatementIsExecutable(PreparedStatementData & } } -unique_ptr -ClientContext::PendingPreparedStatementInternal(ClientContextLock &lock, - shared_ptr statement_data_p, - const PendingQueryParameters ¶meters) { - D_ASSERT(active_query); - auto &statement_data = *statement_data_p; - BindPreparedStatementParameters(*this, statement_data, parameters); - - // Create the query executor. - active_query->executor = make_uniq(*this); - auto &executor = *active_query->executor; - - if (config.enable_progress_bar) { - progress_bar_display_create_func_t display_create_func = nullptr; - if (config.print_progress_bar) { - // Use either a custom display function, or the default. - display_create_func = - config.display_create_func ? config.display_create_func : ProgressBar::DefaultProgressBarDisplay; - } - active_query->progress_bar = - make_uniq(executor, NumericCast(config.wait_time), display_create_func); - active_query->progress_bar->Start(); - query_progress.Restart(); - } - - const auto stream_result = parameters.query_parameters.output_type == QueryResultOutputType::ALLOW_STREAMING && - statement_data.properties.output_type == QueryResultOutputType::ALLOW_STREAMING; - - // Decide how to get the result collector. - get_result_collector_t get_collector = PhysicalResultCollector::GetResultCollector; - auto &client_config = ClientConfig::GetConfig(*this); - if (!stream_result && client_config.get_result_collector) { - get_collector = client_config.get_result_collector; - } - statement_data.output_type = - stream_result ? QueryResultOutputType::ALLOW_STREAMING : QueryResultOutputType::FORCE_MATERIALIZED; - statement_data.memory_type = parameters.query_parameters.memory_type; - - // Get the result collector and initialize the executor. - auto collector = get_collector(*this, statement_data); - D_ASSERT(collector->type == PhysicalOperatorType::RESULT_COLLECTOR); - executor.Initialize(std::move(collector)); - - auto types = executor.GetTypes(); - D_ASSERT(types == statement_data.types); - D_ASSERT(!active_query->HasOpenResult()); - - auto pending_result = - make_uniq(shared_from_this(), *statement_data_p, std::move(types), stream_result); - active_query->prepared = std::move(statement_data_p); - active_query->SetOpenResult(*pending_result); - return pending_result; -} - -void ClientContext::WaitForTask(ClientContextLock &lock, BaseQueryResult &result) { - auto &executor = *active_query->executor; - if (executor.HasTaskInProgress()) { - // This thread is holding a partially processed task, the next step resumes it without waiting. - return; +bool ClientContext::ErrorInvalidatesQueryTransaction(ExceptionType type) { + // A statement that expands into several and fails after one of its statements wrote has applied part of its + // effect, which only ending the transaction undoes + if (active_query && active_query->ExpandsIntoSeveral() && active_query->Wrote()) { + return true; } - executor.WaitForTask(); + return ErrorInvalidatesTransaction(type); } bool ClientContext::ErrorInvalidatesTransaction(ExceptionType type) { @@ -704,83 +447,10 @@ bool ClientContext::ErrorInvalidatesTransaction(ExceptionType type) { } } -PendingExecutionResult ClientContext::ExecuteTaskInternal(ClientContextLock &lock, BaseQueryResult &result, - bool dry_run) { - D_ASSERT(active_query); - D_ASSERT(active_query->IsOpenResult(result)); - bool invalidate_transaction = true; - try { - // Surface a pending interrupt even when this thread runs no task that reaches InterruptCheck. - // IsInterrupted() rather than InterruptCheck(): we must not enforce query_deadline here. - if (!dry_run && IsInterrupted()) { - throw InterruptException(); - } - auto query_result = active_query->executor->ExecuteTask(dry_run); - if (active_query->progress_bar) { - auto is_finished = PendingQueryResult::IsResultReady(query_result); - active_query->progress_bar->Update(is_finished); - query_progress = active_query->progress_bar->GetDetailedQueryProgress(); - } - return query_result; - } catch (std::exception &ex) { - auto error = ErrorData(ex); - if (error.Type() == ExceptionType::INTERRUPT) { - auto &executor = *active_query->executor; - if (!executor.HasError()) { - // Interrupted by the user - result.SetError(ex); - invalidate_transaction = true; - } else { - // Interrupted by an exception caused in a worker thread - error = executor.GetError(); - invalidate_transaction = ErrorInvalidatesTransaction(error.Type()); - result.SetError(error); - } - } else if (!ErrorInvalidatesTransaction(error.Type())) { - invalidate_transaction = false; - } else if (Exception::InvalidatesDatabase(error.Type()) || error.Type() == ExceptionType::INTERNAL) { - // fatal exceptions invalidate the entire database - auto &db_instance = DatabaseInstance::GetDatabase(*this); - ValidChecker::Invalidate(db_instance, error.RawMessage()); - } - ProcessError(error, active_query->query); - result.SetError(std::move(error)); - } catch (...) { // LCOV_EXCL_START - result.SetError(ErrorData("Unhandled exception in ExecuteTaskInternal")); - } // LCOV_EXCL_STOP - EndQueryInternal(lock, false, invalidate_transaction, result.GetErrorObject()); - return PendingExecutionResult::EXECUTION_ERROR; -} - -void ClientContext::InitialCleanup(ClientContextLock &lock) { - //! Cleanup any open results and reset the interrupted flag - CleanupInternal(lock); - interrupt_state = ClientInterruptState::NOT_INTERRUPTED; -} - StatementIterator ClientContext::IterateStatements(const string &query) { - // The iterator yields ready-to-execute (engine-facing) statements: PRAGMA reparse, - // MULTI_STATEMENT unpack and transaction wrapping per peel — matches the eager API users expect. - // Callers that want raw parse-facing statements and drive their own preprocessing construct a - // ParseIterator directly (e.g. Query / ParseStatementsInternal below, which hold the lock). return StatementIterator(ParseIterator(*this, query)); } -void ClientContext::PreprocessStatements(vector> &buffer, - optional_ptr lock) { - // Acquire our own lock if the caller doesn't hold one (e.g. the shell); own_lock keeps it alive - // for the duration of the preprocess pass. - unique_ptr own_lock; - if (!lock) { - own_lock = LockContext(); - lock = own_lock.get(); - } - StatementPreprocessor preprocessor(*this); - const CurrentTransactionState transaction_state = - transaction.HasActiveTransaction() ? IN_ACTIVE_TRANSACTION : NOT_IN_ACTIVE_TRANSACTION; - preprocessor.Preprocess(*lock, buffer, transaction_state); -} - vector> ClientContext::ParseStatementsInternal(ClientContextLock &lock, const string &query) { try { QueryProfiler::Get(*this).StartQuery(query); @@ -789,11 +459,7 @@ vector> ClientContext::ParseStatementsInternal(ClientCo StatementIterator iterator {ParseIterator(*this, query)}; vector> result; while (iterator.Peek()) { - auto stmt = iterator.GetStatementForExecutionWithLock(lock); - if (!stmt) { - continue; // a peel that preprocessing swallowed - } - result.push_back(std::move(stmt)); + result.push_back(iterator.GetStatementForExecution()); } return result; } catch (std::exception &ex) { @@ -810,6 +476,8 @@ unique_ptr ClientContext::ExtractPlan(const string &query) { if (statements.size() != 1) { throw InvalidInputException("ExtractPlan can only prepare a single statement"); } + statements[0] = ExpandSingleStatementInternal(*lock, std::move(statements[0]), + "ExtractPlan can only prepare a single statement"); unique_ptr plan; RunFunctionInTransactionInternal(*lock, [&]() { @@ -852,6 +520,22 @@ static PreparedStatementInfo GetPreparedStatementInfo(PreparedStatementData &dat unique_ptr ClientContext::PrepareInternal(ClientContextLock &lock, unique_ptr statement) { + // Only a pragma or a multi-statement can expand, and only one that does not expand into exactly one statement + // keeps the statement as written, to expand it again at each execution + unique_ptr written_statement; + if (statement->type == StatementType::PRAGMA_STATEMENT || statement->type == StatementType::MULTI_STATEMENT) { + written_statement = statement->Copy(); + } + auto expanded = PreprocessStatementInternal(lock, std::move(statement)); + if (expanded.statements.size() != 1) { + D_ASSERT(written_statement); + if (!written_statement->named_param_map.empty()) { + throw InvalidInputException(EXPANDING_PARAMETERS_ERROR); + } + return make_uniq(shared_from_this(), std::move(written_statement)); + } + statement = std::move(expanded.statements[0]); + auto statement_query = statement->query; // prepare the statement under a generated name - the returned PreparedStatement only refers to that name auto name = "duckdb_prepare_internal_" + UUID::ToString(UUID::GenerateRandomUUID()); @@ -861,12 +545,9 @@ unique_ptr ClientContext::PrepareInternal(ClientContextLock & prepare->stmt_location = statement->stmt_location; prepare->statement = std::move(statement); - PendingQueryParameters parameters; - parameters.query_parameters.output_type = QueryResultOutputType::FORCE_MATERIALIZED; + QueryParameters parameters; auto result = RunStatementInternal(lock, std::move(prepare), parameters, false); - if (result->HasError()) { - result->ThrowError(); - } + result->ThrowIfError(); auto entry = client_data->prepared_statements.find(Identifier(name)); if (entry == client_data->prepared_statements.end()) { throw InternalException("PREPARE succeeded but the prepared statement was not registered"); @@ -887,7 +568,9 @@ unique_ptr ClientContext::Prepare(unique_ptr st // Try to prepare. try { - InitialCleanup(*lock); + if (auto refusal = InitialCleanup(*lock)) { + return refusal; + } return PrepareInternal(*lock, std::move(statement)); } catch (std::exception &ex) { return ErrorResult(ErrorData(ex), query); @@ -896,15 +579,34 @@ unique_ptr ClientContext::Prepare(unique_ptr st StatementSignature ClientContext::BindStatement(unique_ptr statement) { auto lock = LockContext(); - auto named_param_map = statement->named_param_map; StatementSignature signature; ErrorData bind_error; RunFunctionInTransactionInternal( *lock, [&]() { try { + // Expanded inside this transaction, so an expansion error leaves the caller's transaction as a bind + // error does + const bool has_parameters = !statement->named_param_map.empty(); + auto expanded = StatementPreprocessor(*this).ExpandStatement(std::move(statement)); + if (expanded.statements.empty()) { + signature.properties.return_type = StatementReturnType::NOTHING; + return; + } + if (expanded.statements.size() > 1) { + if (has_parameters) { + throw InvalidInputException(EXPANDING_PARAMETERS_ERROR); + } + // The placeholder the planner reports for a result known only at execution + signature.names = {"unknown"}; + signature.types = {LogicalTypeId::UNKNOWN}; + signature.metadata_deferred = true; + return; + } + auto bound = std::move(expanded.statements[0]); + auto named_param_map = bound->named_param_map; Planner planner(*this); - planner.CreatePlan(std::move(statement)); + planner.CreatePlan(std::move(bound)); signature.names = planner.names; signature.types = planner.types; signature.properties = std::move(planner.properties); @@ -942,7 +644,9 @@ unique_ptr ClientContext::Prepare(const string &query) { auto lock = LockContext(); // prepare the query try { - InitialCleanup(*lock); + if (auto refusal = InitialCleanup(*lock)) { + return refusal; + } // first parse the query auto statements = ParseStatementsInternal(*lock, query); @@ -958,12 +662,12 @@ unique_ptr ClientContext::Prepare(const string &query) { } } -unique_ptr ClientContext::PendingStatementInternal(ClientContextLock &lock, - unique_ptr statement, - const PendingQueryParameters ¶meters) { +unique_ptr ClientContext::SubmitStatementInternal(ClientContextLock &lock, + unique_ptr statement, + const QueryParameters ¶meters) { // prepare the query for execution - if (!statement->named_param_map.empty() && parameters.parameters) { - PreparedStatement::VerifyParameters(*parameters.parameters, statement->named_param_map, this); + if (!statement->named_param_map.empty() && parameters.statement_args) { + PreparedStatement::VerifyParameters(*parameters.statement_args, statement->named_param_map, this); } else if (!statement->named_param_map.empty()) { identifier_map_t empty_parameters; PreparedStatement::VerifyParameters(empty_parameters, statement->named_param_map, this); @@ -972,27 +676,36 @@ unique_ptr ClientContext::PendingStatementInternal(ClientCon auto prepared = CreatePreparedStatement(lock, std::move(statement), parameters); if (!prepared->properties.bound_all_parameters) { - return ErrorResult(InvalidInputException("Not all parameters were bound")); + return ErrorResult(InvalidInputException("Not all parameters were bound")); } // execute the prepared statement CheckIfPreparedStatementIsExecutable(*prepared); - return PendingPreparedStatementInternal(lock, std::move(prepared), parameters); + return SubmitPreparedStatementInternal(lock, std::move(prepared), parameters); } -unique_ptr ClientContext::RunStatementInternal(ClientContextLock &lock, unique_ptr statement, - const PendingQueryParameters ¶meters, bool verify) { - auto pending = PendingQueryInternal(lock, std::move(statement), parameters, verify); - if (pending->HasError()) { - return ErrorResult(pending->GetErrorObject()); +unique_ptr ClientContext::ExpandSingleStatementInternal(ClientContextLock &lock, + unique_ptr statement, + const char *refusal) { + auto expanded = PreprocessStatementInternal(lock, std::move(statement)); + if (expanded.statements.size() != 1) { + throw InvalidInputException(refusal); } - return ExecutePendingQueryInternal(lock, *pending); + return std::move(expanded.statements[0]); } -bool ClientContext::IsActiveResult(ClientContextLock &lock, BaseQueryResult &result) { - if (!active_query) { - return false; +PreprocessedStatement ClientContext::PreprocessStatementInternal(ClientContextLock &lock, + unique_ptr statement) { + StatementPreprocessor preprocessor(*this); + return preprocessor.PreprocessStatement(lock, std::move(statement)); +} + +unique_ptr ClientContext::RunStatementInternal(ClientContextLock &lock, unique_ptr statement, + const QueryParameters ¶meters, bool verify) { + auto result = SubmitInternal(lock, std::move(statement), parameters, verify, Retention::RETAINED_AT_SUBMISSION); + if (result->HasError()) { + return result; } - return active_query->IsOpenResult(result); + return CompleteInternal(lock, std::move(result)); } //! Whether this statement executes a prepared statement with parameter values supplied through the C/C++ API @@ -1003,17 +716,17 @@ static bool HasBoundParameterValues(const SQLStatement &statement) { return !statement.Cast().bound_values.empty(); } -unique_ptr ClientContext::PendingStatement(ClientContextLock &lock, - unique_ptr statement, - const PendingQueryParameters ¶meters) { +unique_ptr ClientContext::SubmitStatement(ClientContextLock &lock, unique_ptr statement, + const QueryParameters ¶meters, bool verify, + Retention retention) { // CONNECT chokepoint: when connected, non-control SQL is rewritten in place and falls through to - // the normal pipeline. No recursion — the rewrite goes through PendingStatementInternal, not back here. + // the normal pipeline. No recursion — the rewrite goes through SubmitStatementInternal, not back here. if (is_connected) { if (!IsConnectControlStatement(statement->type)) { // Parameterized prepared statements would need parameter substitution we don't do in v0 — reject. // No-param prepared statements have a fully-resolved `query` already, route them. if (HasBoundParameterValues(*statement)) { - return ErrorResult( + return ErrorResult( ErrorData(InvalidInputException("Parameterized prepared statements cannot be executed while " "CONNECT-ed; DISCONNECT first, or run the SQL as a fresh " "statement to route through the CONNECT binding")), @@ -1022,7 +735,7 @@ unique_ptr ClientContext::PendingStatement(ClientContextLock auto live = TryGetConnectedCatalog(); if (!live) { // Target was detached elsewhere; user must explicitly DISCONNECT to clear is_connected. - return ErrorResult( + return ErrorResult( ErrorData(InvalidInputException( "The connected database has been detached out from under this connection. Issue " "DISCONNECT to clear the connection before running further SQL.")), @@ -1039,11 +752,37 @@ unique_ptr ClientContext::PendingStatement(ClientContextLock // statement is now SELECT * FROM ; fall through. } } - // the statement is moved into PendingStatementInternal - keep the source text for error reporting + // the statement is moved into the query - keep the source text for error reporting auto query = statement->query; - unique_ptr pending; + const auto statement_type = statement->type; + const bool has_parameters = + !statement->named_param_map.empty() || (parameters.statement_args && !parameters.statement_args->empty()); + PreprocessedStatement preprocessed; try { - BeginQueryInternal(lock, *statement); + preprocessed = PreprocessStatementInternal(lock, std::move(statement)); + auto &statements = preprocessed.statements; + if (statements.size() > 1 && has_parameters) { + throw InvalidInputException(EXPANDING_PARAMETERS_ERROR); + } + if (verify) { + // A later statement binds only once the earlier ones ran, so a statement that expands is verified without + // running copies of it + for (auto &internal_statement : statements) { + StatementVerification(lock, internal_statement, parameters, statements.size() == 1); + } + } + } catch (std::exception &ex) { + // preserve extra error data (like query location) + return ErrorResult(ErrorData(ex), query); + } + if (preprocessed.statements.empty()) { + StatementProperties properties; + properties.return_type = StatementReturnType::NOTHING; + return EmptyResultInternal(statement_type, std::move(properties), {}, {}, parameters.format); + } + const bool expands = preprocessed.statements.size() > 1; + try { + BeginQueryInternal(lock, std::move(preprocessed), parameters, retention); } catch (std::exception &ex) { ErrorData error(ex); if (Exception::InvalidatesDatabase(error.Type())) { @@ -1051,15 +790,29 @@ unique_ptr ClientContext::PendingStatement(ClientContextLock auto &db_instance = DatabaseInstance::GetDatabase(*this); ValidChecker::Invalidate(db_instance, error.RawMessage()); } - return ErrorResult(std::move(error), query); + return ErrorResult(std::move(error), query); } + unique_ptr handle; + unique_ptr result; bool invalidate_query = true; try { - pending = PendingStatementInternal(lock, std::move(statement), parameters); + if (expands) { + handle = + make_uniq(shared_from_this(), statement_type, parameters.format, GetClientProperties()); + active_query->handle = handle.get(); + active_query->SetOpenResult(*handle); + } + result = SubmitStatementInternal(lock, active_query->TakeNextStatement(), parameters); + if (!result && ClientConfig::GetConfig(*this).get_result_collector) { + result = RunToResultStatementInternal(lock, *handle); + } + if (!result) { + result = std::move(handle); + } } catch (std::exception &ex) { ErrorData error(ex); - if (!ErrorInvalidatesTransaction(error.Type())) { + if (!ErrorInvalidatesQueryTransaction(error.Type())) { // standard exceptions do not invalidate the current transaction invalidate_query = false; } else if (Exception::InvalidatesDatabase(error.Type())) { @@ -1068,15 +821,23 @@ unique_ptr ClientContext::PendingStatement(ClientContextLock ValidChecker::Invalidate(db_instance, error.RawMessage()); } // other types of exceptions do invalidate the current transaction - pending = ErrorResult(std::move(error), query); + result = ErrorResult(std::move(error), query); } - if (pending->HasError()) { - // query failed: abort now - EndQueryInternal(lock, false, invalidate_query, pending->GetErrorObject()); - return pending; + if (handle) { + // A handle that is not handed out is released without the context lock its Close would take + handle->context.reset(); } - D_ASSERT(active_query->IsOpenResult(*pending)); - return pending; + if (result->HasError()) { + // query failed: abort now, unless a delegated collector's execution already ended it + if (active_query) { + EndQueryInternal(lock, false, invalidate_query, result->GetErrorObject()); + } + return result; + } + // A collector that builds its own result object finishes the query inside the submission, so + // the query it belonged to may already be gone + D_ASSERT(!active_query || active_query->IsOpenResult(*result)); + return result; } void ClientContext::LogQueryInternal(ClientContextLock &, const string &query) { @@ -1100,22 +861,31 @@ void ClientContext::LogQueryInternal(ClientContextLock &, const string &query) { client_data->log_query_writer->Sync(); } -unique_ptr ClientContext::Query(unique_ptr statement, QueryParameters parameters) { - auto pending_query = PendingQuery(std::move(statement), parameters); - if (pending_query->HasError()) { +unique_ptr ClientContext::Query(unique_ptr statement, const QueryParameters ¶meters) { + auto lock = LockContext(); + try { + if (auto refusal = InitialCleanup(*lock)) { + return refusal; + } + } catch (std::exception &ex) { + return ErrorResult(ErrorData(ex), statement->query); + } + auto result = SubmitInternal(*lock, std::move(statement), parameters, true, Retention::RETAINED_AT_SUBMISSION); + if (result->HasError()) { if (transaction.HasActiveTransaction() && transaction.GetAutoRollback()) { - transaction.Rollback(pending_query->GetErrorObject()); + transaction.Rollback(result->GetErrorObject()); } - return ErrorResult(pending_query->GetErrorObject()); + return result; } - return pending_query->Execute(); + return CompleteInternal(*lock, std::move(result)); } unique_ptr ClientContext::Query(const string &query, QueryParameters query_parameters) { auto lock = LockContext(); - // The lazy path bypasses ParseStatementsInternal → InitialCleanup, so clear leftover query state - // (interrupt flag, etc.) ourselves. - InitialCleanup(*lock); + // The lazy path drives its own parse iterator, so it clears leftover query state (interrupt flag, etc.) itself + if (auto refusal = InitialCleanup(*lock)) { + return refusal; + } auto &profiler = QueryProfiler::Get(*this); profiler.StartQuery(query); // ParseIterator's constructor runs UTF-8 validation / Unicode-space strip and can throw — route @@ -1126,7 +896,7 @@ unique_ptr ClientContext::Query(const string &query, QueryParameter iterator_storage = make_uniq(ParseIterator(*this, query)); iterator_ptr = *iterator_storage; } catch (const std::exception &ex) { - return ErrorResult(ErrorData(ex), query); + return ErrorResult(ErrorData(ex), query); } auto &iterator = *iterator_ptr; @@ -1135,7 +905,7 @@ unique_ptr ClientContext::Query(const string &query, QueryParameter has_now = iterator.Peek(); return nullptr; } catch (const std::exception &ex) { - return ErrorResult(ErrorData(ex), query); + return ErrorResult(ErrorData(ex), query); } }; @@ -1148,45 +918,40 @@ unique_ptr ClientContext::Query(const string &query, QueryParameter StatementProperties properties; vector names; auto collection = make_uniq(Allocator::DefaultAllocator()); - return make_uniq(StatementType::INVALID_STATEMENT, properties, std::move(names), - std::move(collection), GetClientProperties()); + return make_uniq(StatementType::INVALID_STATEMENT, properties, std::move(names), + std::move(collection), GetClientProperties()); } unique_ptr result; optional_ptr last_result; bool last_had_result = false; while (has_current) { - // Get + preprocess the next engine-facing statement, reusing the lock we already hold. PRAGMA - // reparse / MULTI_STATEMENT unpacking happen inside GetStatementForExecutionWithLock, which sees the - // transaction state left by the previously executed statement. A peel can preprocess to - // nothing, in which case statement is null and there is nothing to execute. + // The next user statement. Its submission preprocesses it, seeing the transaction state the previous + // statement left, and runs what it expands into as one query unique_ptr statement; try { - statement = iterator.GetStatementForExecutionWithLock(*lock); + statement = iterator.GetStatementForExecution(); } catch (const std::exception &ex) { - return ErrorResult(ErrorData(ex), query); + return ErrorResult(ErrorData(ex), query); } // Look ahead WITHOUT parsing: HasMore() only walks the token cursor, so it never parses (and // never throws) the next statement here. The next statement is parsed later, in this loop's - // next GetStatementForExecutionWithLock — after the current statement has executed. This lets a statement + // next GetStatementForExecution, after the current statement has executed. This lets a statement // register grammar (e.g. LOAD an extension) that a following statement then uses. bool has_next = iterator.HasMore(); + if (has_next && query_parameters.statement_args && !query_parameters.statement_args->empty()) { + return ErrorResult( + ErrorData(InvalidInputException("Cannot prepare multiple statements at once!")), query); + } if (statement) { - bool is_last_overall = !has_next; - PendingQueryParameters parameters; - parameters.query_parameters = query_parameters; - if (!is_last_overall) { - parameters.query_parameters.output_type = QueryResultOutputType::FORCE_MATERIALIZED; - } - auto pending_query = PendingQueryInternal(*lock, std::move(statement), parameters); - auto has_result = pending_query->GetStatementProperties().return_type == StatementReturnType::QUERY_RESULT; - unique_ptr current_result; - if (pending_query->HasError()) { - current_result = ErrorResult(pending_query->GetErrorObject()); - } else { - current_result = ExecutePendingQueryInternal(*lock, *pending_query); + // Every statement completes before the call returns, so none of them leaves a producer parked for the + // consumer's choice + auto current_result = + SubmitInternal(*lock, std::move(statement), query_parameters, true, Retention::RETAINED_AT_SUBMISSION); + if (!current_result->HasError()) { + current_result = CompleteInternal(*lock, std::move(current_result)); } if (current_result->HasError()) { if (transaction.HasActiveTransaction() && transaction.GetAutoRollback()) { @@ -1197,7 +962,8 @@ unique_ptr ClientContext::Query(const string &query, QueryParameter interrupt_state = ClientInterruptState::NOT_INTERRUPTED; return current_result; } - // now append the result to the list of results + // now append the result to the list of results; a completed result knows its metadata + auto has_result = current_result->GetStatementProperties().return_type == StatementReturnType::QUERY_RESULT; if (!last_result || !last_had_result) { // first result of the query result = std::move(current_result); @@ -1216,31 +982,22 @@ unique_ptr ClientContext::Query(const string &query, QueryParameter return result; } -unique_ptr ClientContext::PendingQuery(const string &query, QueryParameters parameters) { - identifier_map_t empty_param_list; - return PendingQuery(query, empty_param_list, parameters); -} - -unique_ptr ClientContext::PendingQuery(unique_ptr statement, - QueryParameters parameters) { - identifier_map_t empty_param_list; - return PendingQuery(std::move(statement), empty_param_list, parameters); +unique_ptr ClientContext::Query(const string &query, shared_ptr format) { + return Query(query, QueryParameters(std::move(format))); } -unique_ptr ClientContext::PendingQuery(const string &query, - identifier_map_t &values, - QueryParameters parameters) { - PendingQueryParameters params; - params.parameters = values; - params.query_parameters = parameters; - return PendingQuery(query, params); +unique_ptr ClientContext::Query(unique_ptr statement, shared_ptr format) { + return Query(std::move(statement), QueryParameters(std::move(format))); } -unique_ptr ClientContext::PendingQuery(const string &query, PendingQueryParameters parameters) { +unique_ptr ClientContext::Submit(const string &query, const QueryParameters ¶meters) { auto lock = LockContext(); try { - InitialCleanup(*lock); + if (auto refusal = InitialCleanup(*lock)) { + return refusal; + } + // The submission preprocesses the statement, which may expand into several that run as one query auto statements = ParseStatementsInternal(*lock, query); if (statements.empty()) { throw InvalidInputException("No statement to prepare!"); @@ -1249,74 +1006,78 @@ unique_ptr ClientContext::PendingQuery(const string &query, throw InvalidInputException("Cannot prepare multiple statements at once!"); } - return PendingQueryInternal(*lock, std::move(statements[0]), parameters, true); + return SubmitInternal(*lock, std::move(statements[0]), parameters, true); } catch (std::exception &ex) { ErrorData error(ex); ProcessError(error, query); - return make_uniq(std::move(error)); + return make_uniq(std::move(error)); } } -unique_ptr ClientContext::PendingQuery(unique_ptr statement, - identifier_map_t &values, - QueryParameters parameters) { +unique_ptr ClientContext::Submit(unique_ptr statement, const QueryParameters ¶meters) { auto lock = LockContext(); try { - InitialCleanup(*lock); - - PendingQueryParameters params; - params.query_parameters = parameters; - params.parameters = values; + if (auto refusal = InitialCleanup(*lock)) { + return refusal; + } - return PendingQueryInternal(*lock, std::move(statement), params, true); + return SubmitInternal(*lock, std::move(statement), parameters, true); } catch (std::exception &ex) { - return make_uniq(ErrorData(ex)); + return make_uniq(ErrorData(ex)); } } +unique_ptr ClientContext::Submit(const string &query, shared_ptr format) { + return Submit(query, QueryParameters(std::move(format))); +} + +unique_ptr ClientContext::Submit(unique_ptr statement, shared_ptr format) { + return Submit(std::move(statement), QueryParameters(std::move(format))); +} + +unique_ptr ClientContext::Submit(const string &query, identifier_map_t &values, + QueryParameters parameters) { + parameters.statement_args = values; + return Submit(query, parameters); +} + +unique_ptr ClientContext::Submit(unique_ptr statement, + identifier_map_t &values, + QueryParameters parameters) { + parameters.statement_args = values; + return Submit(std::move(statement), parameters); +} + unique_ptr ClientContext::RunInternalStatement(unique_ptr statement, - const PendingQueryParameters ¶meters) { + const QueryParameters ¶meters) { auto lock = LockContext(); try { - InitialCleanup(*lock); + if (auto refusal = InitialCleanup(*lock)) { + return refusal; + } } catch (std::exception &ex) { - return ErrorResult(ErrorData(ex), statement->query); + return ErrorResult(ErrorData(ex), statement->query); } - auto pending = PendingQueryInternal(*lock, std::move(statement), parameters, false); - if (pending->HasError()) { - return ErrorResult(pending->GetErrorObject()); - } - return pending->ExecuteInternal(*lock); + return RunStatementInternal(*lock, std::move(statement), parameters, false); } -unique_ptr ClientContext::PendingInternalStatement(unique_ptr statement, - const PendingQueryParameters ¶meters) { +unique_ptr ClientContext::SubmitInternalStatement(unique_ptr statement, + const QueryParameters ¶meters) { auto lock = LockContext(); try { - InitialCleanup(*lock); - } catch (std::exception &ex) { - return ErrorResult(ErrorData(ex), statement->query); - } - return PendingQueryInternal(*lock, std::move(statement), parameters, false); -} - -unique_ptr ClientContext::PendingQueryInternal(ClientContextLock &lock, - unique_ptr statement, - const PendingQueryParameters ¶meters, - bool verify) { - if (verify) { - try { - StatementVerification(lock, statement, parameters); - } catch (std::exception &ex) { - // preserve extra error data (like query location) - return ErrorResult(ErrorData(ex), statement->query); + if (auto refusal = InitialCleanup(*lock)) { + return refusal; } + } catch (std::exception &ex) { + return ErrorResult(ErrorData(ex), statement->query); } - return PendingStatement(lock, std::move(statement), parameters); + return SubmitInternal(*lock, std::move(statement), parameters, false); } -unique_ptr ClientContext::ExecutePendingQueryInternal(ClientContextLock &lock, PendingQueryResult &query) { - return query.ExecuteInternal(lock); +unique_ptr ClientContext::SubmitInternal(ClientContextLock &lock, unique_ptr statement, + const QueryParameters ¶meters, bool verify, + Retention retention) { + return SubmitStatement(lock, std::move(statement), parameters, verify, retention); } void ClientContext::Interrupt() { @@ -1346,16 +1107,30 @@ void ClientContext::InterruptCheck() const { } // Only check timeout every N calls to avoid expensive steady_clock::now() syscall if (query_deadline.IsValid() && ++timeout_check_counter % TIMEOUT_CHECK_INTERVAL == 0) { - auto now = NumericCast(duration_cast(steady_clock::now().time_since_epoch()).count()); - if (now >= query_deadline.GetIndex()) { - throw InterruptException("Query exceeded maximum execution time"); - } + BlockingInterruptCheck(); + } +} + +void ClientContext::BlockingInterruptCheck() const { + if (IsInterrupted()) { + throw InterruptException(); + } + if (QueryDeadlinePassed()) { + throw InterruptException(QUERY_DEADLINE_ERROR); + } +} + +bool ClientContext::QueryDeadlinePassed() const { + if (!query_deadline.IsValid()) { + return false; } + auto now = NumericCast(duration_cast(steady_clock::now().time_since_epoch()).count()); + return now >= query_deadline.GetIndex(); } void ClientContext::CancelTransaction() { auto lock = LockContext(); - InitialCleanup(*lock); + AbandonActiveQuery(*lock); } void ClientContext::EnableProfiling() { @@ -1389,6 +1164,87 @@ void ClientContext::RegisterFunction(CreateFunctionInfo &info) { }); } +void ClientContext::RunTransactionStatementInternal(const TransactionInfo &info) { + auto type = info.type; + if (type == TransactionType::COMMIT && transaction.HasActiveTransaction() && + ValidChecker::IsInvalidated(ActiveTransaction())) { + // transaction is invalidated - turn COMMIT into ROLLBACK + type = TransactionType::ROLLBACK; + } + switch (type) { + case TransactionType::BEGIN_TRANSACTION: { + if (!transaction.IsAutoCommit()) { + throw TransactionException("cannot start a transaction within a transaction"); + } + transaction.SetAutoCommit(false); + if (info.modifier == TransactionModifierType::TRANSACTION_READ_ONLY) { + transaction.SetReadOnly(); + } + transaction.SetInvalidationPolicy(info.invalidation_policy); + transaction.SetAutoRollback(info.auto_rollback); + if (Settings::Get(*this)) { + auto databases = DatabaseManager::Get(*this).GetDatabases(*this); + for (auto &attached : databases) { + if (ValidChecker::IsInvalidated(*attached)) { + continue; + } + transaction.ActiveTransaction().GetTransaction(*attached); + } + } + break; + } + case TransactionType::COMMIT: + if (transaction.IsAutoCommit()) { + throw TransactionException("cannot commit - no transaction is active"); + } + transaction.Commit(); + // The commit is irreversible, so ignore interrupts until the next query. + SuppressInterrupts(); + break; + case TransactionType::ROLLBACK: { + if (transaction.IsAutoCommit()) { + throw TransactionException("cannot rollback - no transaction is active"); + } + auto &valid_checker = ValidChecker::Get(transaction.ActiveTransaction()); + if (valid_checker.IsInvalidated()) { + ErrorData error(ExceptionType::TRANSACTION, valid_checker.InvalidatedMessage()); + transaction.Rollback(error); + } else { + transaction.Rollback(nullptr); + } + break; + } + default: + throw NotImplementedException("Unrecognized transaction type!"); + } +} + +void ClientContext::RunTransactionStatement(const TransactionInfo &info) { + auto lock = LockContext(); + if (auto refusal = InitialCleanup(*lock)) { + refusal->ThrowError(); + } + if (is_connected) { + auto statement = make_uniq(info.Copy()); + statement->query = info.ToString(); + QueryParameters parameters; + auto result = RunStatementInternal(*lock, std::move(statement), parameters); + if (result->HasError()) { + if (transaction.HasActiveTransaction() && transaction.GetAutoRollback()) { + transaction.Rollback(result->GetErrorObject()); + } + ClearInterrupt(); + result->ThrowError(); + } + return; + } + auto &db_instance = DatabaseInstance::GetDatabase(*this); + if (ValidChecker::IsInvalidated(db_instance)) { + throw ErrorManager::InvalidatedDatabase(*this, ValidChecker::InvalidatedMessage(db_instance)); + } + RunTransactionStatementInternal(info); +} + void ClientContext::RunFunctionInTransactionInternal(ClientContextLock &lock, const std::function &fun, bool requires_valid_transaction) { if (requires_valid_transaction && transaction.HasActiveTransaction() && @@ -1458,10 +1314,8 @@ unique_ptr ClientContext::TableInfo(const Identifier &schema_n } void ClientContext::Append(unique_ptr stmt) { - auto result = Query(std::move(stmt), false); - if (result->HasError()) { - result->GetErrorObject().Throw("Failed to append: "); - } + auto result = Query(std::move(stmt), QueryParameters()); + result->ThrowIfError("Failed to append: "); } void ClientContext::Append(TableDescription &description, ColumnDataCollection &collection) { @@ -1469,7 +1323,7 @@ void ClientContext::Append(TableDescription &description, ColumnDataCollection & vector expected_names; auto query = Appender::ConstructQuery(description, table_name, expected_names); auto table_ref = BaseAppender::GetColumnDataTableRef(collection, table_name, expected_names); - auto stmt = BaseAppender::ParseStatement(std::move(table_ref), query, table_name.GetIdentifierName()); + auto stmt = BaseAppender::ParseStatement(*this, std::move(table_ref), query, table_name.GetIdentifierName()); Append(std::move(stmt)); } @@ -1503,6 +1357,7 @@ unordered_set ClientContext::GetTableNames(const string &query, const bo if (statements.size() != 1) { throw InvalidInputException("Expected a single statement"); } + statements[0] = ExpandSingleStatementInternal(*lock, std::move(statements[0]), "Expected a single statement"); unordered_set result; RunFunctionInTransactionInternal(*lock, [&]() { @@ -1516,10 +1371,11 @@ unordered_set ClientContext::GetTableNames(const string &query, const bo return result; } -unique_ptr ClientContext::PendingQueryInternal(ClientContextLock &lock, - const shared_ptr &relation, - QueryParameters query_parameters) { - InitialCleanup(lock); +unique_ptr ClientContext::SubmitInternal(ClientContextLock &lock, const shared_ptr &relation, + const QueryParameters &query_parameters) { + if (auto refusal = InitialCleanup(lock)) { + return refusal; + } #ifdef DEBUG // run the ToString method of any relation we run, mostly to ensure it doesn't crash @@ -1528,27 +1384,29 @@ unique_ptr ClientContext::PendingQueryInternal(ClientContext #endif auto relation_stmt = make_uniq(relation); - PendingQueryParameters parameters; - parameters.query_parameters = query_parameters; - return PendingQueryInternal(lock, std::move(relation_stmt), parameters); + return SubmitInternal(lock, std::move(relation_stmt), query_parameters); } -unique_ptr ClientContext::PendingQuery(const shared_ptr &relation, - QueryParameters query_parameters) { +unique_ptr ClientContext::Submit(const shared_ptr &relation, + const QueryParameters &query_parameters) { auto lock = LockContext(); - return PendingQueryInternal(*lock, relation, query_parameters); + return SubmitInternal(*lock, relation, query_parameters); } unique_ptr ClientContext::Execute(const shared_ptr &relation) { auto lock = LockContext(); auto &expected_columns = relation->Columns(); - auto pending = PendingQueryInternal(*lock, relation, false); - if (pending->HasError()) { - return ErrorResult(pending->GetErrorObject()); + if (auto refusal = InitialCleanup(*lock)) { + return refusal; } - unique_ptr result; - result = ExecutePendingQueryInternal(*lock, *pending); + auto relation_stmt = make_uniq(relation); + QueryParameters parameters; + auto result = SubmitInternal(*lock, std::move(relation_stmt), parameters, true, Retention::RETAINED_AT_SUBMISSION); + if (result->HasError()) { + return result; + } + result = CompleteInternal(*lock, std::move(result)); if (result->HasError()) { return result; } @@ -1581,7 +1439,7 @@ unique_ptr ClientContext::Execute(const shared_ptr &relat err_str += result->ColumnName(i) + " " + result->GetTypes()[i].ToString(); } err_str += "]"; - return ErrorResult(ErrorData(err_str)); + return ErrorResult(ErrorData(err_str)); } SettingLookupResult ClientContext::TryGetCurrentSetting(const Identifier &key, Value &result) const { @@ -1615,7 +1473,6 @@ ParserOptions ClientContext::GetParserOptions() { ParserOptions options; options.identifier_case_mode = Settings::Get(*this); options.integer_division = Settings::Get(*this); - options.heap_based_parser = Settings::Get(*this); options.regex_match_operator_semantics = Settings::Get(*this); options.max_expression_depth = Settings::Get(*this); options.extensions = DBConfig::GetConfig(*this).GetCallbackManager(); @@ -1626,11 +1483,7 @@ ParserOptions ClientContext::GetParserOptions() { ClientProperties ClientContext::GetClientProperties() { string timezone = "UTC"; - Value result; - - if (TryGetCurrentSetting("TimeZone", result)) { - timezone = result.ToString(); - } + TryGetCurrentSetting("TimeZone", timezone); ArrowOffsetSize arrow_offset_size = ArrowOffsetSize::REGULAR; if (Settings::Get(*this)) { arrow_offset_size = ArrowOffsetSize::LARGE; @@ -1648,13 +1501,6 @@ ClientProperties ClientContext::GetClientProperties() { this}; } -bool ClientContext::ExecutionIsFinished() { - if (!active_query || !active_query->executor) { - return false; - } - return active_query->executor->ExecutionIsFinished(); -} - LogicalType ClientContext::ParseLogicalType(const string &type) { auto lock = LockContext(); LogicalType logical_type; diff --git a/src/duckdb/src/main/client_context_execution.cpp b/src/duckdb/src/main/client_context_execution.cpp new file mode 100644 index 000000000..2b204776e --- /dev/null +++ b/src/duckdb/src/main/client_context_execution.cpp @@ -0,0 +1,587 @@ +#include "duckdb/main/client_context.hpp" +#include "duckdb/common/operator/add.hpp" + +#include "duckdb/common/chrono.hpp" +#include "duckdb/common/error_data.hpp" +#include "duckdb/common/progress_bar/progress_bar.hpp" +#include "duckdb/execution/executor.hpp" +#include "duckdb/execution/operator/helper/physical_result_collector.hpp" +#include "duckdb/execution/operator/helper/physical_result_sink.hpp" +#include "duckdb/logging/log_manager.hpp" +#include "duckdb/logging/log_type.hpp" +#include "duckdb/main/active_query_context.hpp" +#include "duckdb/main/buffered_data/batched_buffered_data.hpp" +#include "duckdb/main/buffered_data/simple_buffered_data.hpp" +#include "duckdb/main/client_config.hpp" +#include "duckdb/main/client_context_state.hpp" +#include "duckdb/main/client_data.hpp" +#include "duckdb/main/database.hpp" +#include "duckdb/main/database_manager.hpp" +#include "duckdb/main/error_manager.hpp" +#include "duckdb/main/prepared_statement_data.hpp" +#include "duckdb/main/query_profiler.hpp" +#include "duckdb/main/query_result.hpp" +#include "duckdb/main/result_format.hpp" +#include "duckdb/main/settings.hpp" +#include "duckdb/main/valid_checker.hpp" +#include "duckdb/parallel/task_scheduler.hpp" +#include "duckdb/transaction/meta_transaction.hpp" +#include "duckdb/transaction/transaction_context.hpp" + +namespace duckdb { + +void ClientContext::BeginQueryInternal(ClientContextLock &lock, PreprocessedStatement statements, + const QueryParameters ¶meters, Retention retention) { + // check if we are on AutoCommit. In this case we should start a transaction + D_ASSERT(!active_query); + D_ASSERT(!statements.statements.empty()); + auto &db_inst = DatabaseInstance::GetDatabase(*this); + if (ValidChecker::IsInvalidated(db_inst)) { + throw ErrorManager::InvalidatedDatabase(*this, ValidChecker::InvalidatedMessage(db_inst)); + } + auto query = statements.statements[0]->query; + active_query = make_uniq(std::move(statements)); + active_query->format = parameters.format; + if (retention == Retention::RETAINED_AT_SUBMISSION) { + active_query->retention = ResultLifetime::RETAINED; + } + if (transaction.IsAutoCommit() && !transaction.HasActiveTransaction()) { + transaction.BeginTransaction(); + } + BeginStatementInternal(lock, query); + + query_progress.Initialize(); + // Set query deadline if max_execution_time is configured + auto max_execution_time = Settings::Get(*this); + if (max_execution_time > 0) { + auto now_ms = NumericCast(duration_cast(steady_clock::now().time_since_epoch()).count()); + idx_t deadline; + if (TryAddOperator::Operation(now_ms, NumericCast(max_execution_time), deadline)) { + query_deadline = deadline; + } else { + // the deadline is too far in the future to be represented - there is effectively no deadline + query_deadline.SetInvalid(); + } + } else { + query_deadline.SetInvalid(); + } + // Notify any registered state of query begin + for (auto &state : registered_state->States()) { + state->QueryBegin(*this); + } + RefreshLoggerInternal(); +} + +void ClientContext::BeginStatementInternal(ClientContextLock &lock, const string &query) { + transaction.SetActiveQuery(db->GetDatabaseManager().GetNewQueryNumber()); + LogQueryInternal(lock, query); + active_query->query = query; +} + +void ClientContext::RefreshLoggerInternal() { + // Flush the old logger. + logger->Flush(); + + // Refresh the logger to ensure we are in sync with the global log settings. + LoggingContext logging_context(LogContextScope::CONNECTION); + logging_context.connection_id = connection_id; + logging_context.transaction_id = transaction.ActiveTransaction().global_transaction_id; + logging_context.query_id = transaction.GetActiveQuery(); + logger = db->GetLogManager().CreateLogger(logging_context, true); + DUCKDB_LOG(*this, QueryLogType, active_query->query); +} + +ErrorData ClientContext::EndQueryInternal(ClientContextLock &lock, bool success, bool invalidate_transaction, + optional_ptr previous_error) { + if (active_query->executor) { + active_query->executor->CancelTasks(); + } + active_query->progress_bar.reset(); + D_ASSERT(active_query.get()); + active_query.reset(); + query_deadline.SetInvalid(); + query_progress.Initialize(); + ErrorData error; + try { + if (transaction.HasActiveTransaction()) { + transaction.ResetActiveQuery(); + if (transaction.IsAutoCommit()) { + if (success) { + transaction.Commit(); + } else { + transaction.Rollback(previous_error); + } + } else if (invalidate_transaction) { + D_ASSERT(!success); + if (transaction.GetAutoRollback()) { + transaction.Rollback(previous_error); + } else { + ValidChecker::Invalidate(ActiveTransaction(), "Failed to commit"); + } + } + } + } catch (std::exception &ex) { + error = ErrorData(ex); + if (Exception::InvalidatesDatabase(error.Type()) || error.Type() == ExceptionType::INTERNAL) { + auto &db_inst = DatabaseInstance::GetDatabase(*this); + ValidChecker::Invalidate(db_inst, error.RawMessage()); + } + } catch (...) { // LCOV_EXCL_START + error = ErrorData("Unhandled exception!"); + } // LCOV_EXCL_STOP + + client_data->profiler->EndQuery(); + + // Refresh the logger + logger->Flush(); + LoggingContext context(LogContextScope::CONNECTION); + context.connection_id = connection_id; + logger = db->GetLogManager().CreateLogger(context, true); + + // Notify any registered state of query end + for (auto const &s : registered_state->States()) { + if (error.HasError()) { + s->QueryEnd(*this, &error); + } else { + s->QueryEnd(*this, previous_error); + } + } + return error; +} + +void ClientContext::CleanupInternal(ClientContextLock &lock, BaseQueryResult *result, bool invalidate_transaction) { + if (!active_query) { + // no query currently active + return; + } + if (active_query->executor) { + // Read before CancelTasks clears the slot, and while the profiler is still running + auto buffer = active_query->executor->GetResultBuffer(); + if (buffer) { + QueryProfiler::Get(*this).SetStreamingPeakBufferSize(buffer->PeakStreamingBytes()); + } + active_query->executor->CancelTasks(); + } + active_query->progress_bar.reset(); + + // Relaunch the threads if a SET THREADS command was issued + auto &scheduler = TaskScheduler::GetScheduler(*this); + scheduler.RelaunchThreads(); + + optional_ptr passed_error = nullptr; + if (result && result->HasError()) { + passed_error = result->GetErrorObject(); + } + auto error = EndQueryInternal(lock, result ? !result->HasError() : false, invalidate_transaction, passed_error); + if (result && !result->HasError()) { + // if an error occurred while committing report it in the result + result->SetError(error); + } + D_ASSERT(!active_query); +} + +void ClientContext::AbortInternal(ClientContextLock &lock) { + D_ASSERT(active_query); + auto &prepared = active_query->prepared; + // No savepoint isolates a single statement, so partial writes can only be dropped with the whole transaction. A + // statement that expands into several and is abandoned with statements left would leave them unapplied, and those + // are not bound, so they count as writing + const bool may_write = !prepared || !prepared->properties.IsReadOnly() || active_query->HasNextStatement(); + CleanupInternal(lock, nullptr, may_write); +} + +Executor &ClientContext::GetExecutor() { + D_ASSERT(active_query); + D_ASSERT(active_query->executor); + return *active_query->executor; +} + +const string &ClientContext::GetCurrentQuery() { + D_ASSERT(active_query); + return active_query->query; +} + +void BindPreparedStatementParameters(ClientContext &context, PreparedStatementData &statement, + const QueryParameters ¶meters) { + identifier_map_t owned_values; + if (parameters.statement_args) { + auto ¶ms = *parameters.statement_args; + for (auto &val : params) { + owned_values.emplace(val); + } + } + statement.Bind(context, owned_values); +} + +unique_ptr ClientContext::SubmitPreparedStatementInternal( + ClientContextLock &lock, shared_ptr statement_data_p, const QueryParameters ¶meters) { + D_ASSERT(active_query); + auto &statement_data = *statement_data_p; + BindPreparedStatementParameters(*this, statement_data, parameters); + // The plan must outlive the executor, also when Initialize throws + active_query->prepared = std::move(statement_data_p); + + // Create the query executor. + active_query->executor = make_uniq(*this); + auto &executor = *active_query->executor; + + if (config.enable_progress_bar) { + progress_bar_display_create_func_t display_create_func = nullptr; + if (config.print_progress_bar) { + // Use either a custom display function, or the default. + display_create_func = + config.display_create_func ? config.display_create_func : ProgressBar::DefaultProgressBarDisplay; + } + active_query->progress_bar = + make_uniq(executor, NumericCast(config.wait_time), display_create_func); + active_query->progress_bar->Start(); + query_progress.Restart(); + } + + // The result statement of a query that expands into several is the one that returns rows; the others only run + const bool expands = active_query->ExpandsIntoSeveral(); + const bool result_statement = + !expands || statement_data.properties.return_type == StatementReturnType::QUERY_RESULT; + if (expands && result_statement && active_query->HasNextStatement()) { + throw NotImplementedException("A statement that expands into a row-returning statement followed by others " + "cannot be served as one result"); + } + + // Decide how to get the result collector. + get_result_collector_t get_collector = PhysicalResultCollector::GetResultCollector; + auto &client_config = ClientConfig::GetConfig(*this); + if (client_config.get_result_collector && result_statement) { + get_collector = client_config.get_result_collector; + } + + // Get the result collector and initialize the executor. + auto collector = get_collector(*this, statement_data); + D_ASSERT(collector->type == PhysicalOperatorType::RESULT_COLLECTOR); + // A custom hook can hand back the default sink, which is then served like any other query + const bool delegating = collector->Cast().BuildsOwnResult(); + if (delegating && expands && !active_query->inside_submission) { + throw NotImplementedException("A result collector that builds its own result object can only serve a " + "statement that expands into several when it is set before the submission"); + } + if (delegating && parameters.format) { + if (!parameters.format->Is()) { + // The collector builds its own result, which the format would never reach + throw InvalidInputException("A result format cannot be combined with a custom result collector"); + } + if (parameters.format->Cast().MemoryType() == QueryResultMemoryType::BUFFER_MANAGED) { + // The collector chooses its own store, so the request would be silently downgraded + throw InvalidInputException("A buffer-managed result cannot be combined with a custom result collector"); + } + } + + // Read before Initialize starts the workers: a SET statement writes the settings from a task + auto client_properties = GetClientProperties(); + auto types = statement_data.types; + + // The buffer is created here, on the client thread, and handed to the sink, the executor and the + // handle. It runs the format's InitGlobal before any worker starts, so workers only read the format state + shared_ptr buffer; + if (!delegating) { + auto &sink = collector->Cast(); + ResultFormatContext format_context {statement_data.types, statement_data.names, client_properties, + sink.ordering}; + // The output of a statement the result does not carry is discarded, so it is kept in chunks + auto format = result_statement ? parameters.format : nullptr; + if (sink.ordering == ResultOrdering::BATCH_INDEX_ORDERED) { + buffer = make_shared_ptr(*this, ResultLifetime::UNDECIDED, std::move(format_context), + std::move(format)); + } else { + buffer = make_shared_ptr(*this, ResultLifetime::UNDECIDED, std::move(format_context), + std::move(format)); + } + // Settled before execution starts, so no producer parks for a decision that is already made, or that no + // consumer makes for a statement whose output is discarded + if (!result_statement) { + buffer->Decide(ResultLifetime::RETAINED); + } else if (active_query->retention != ResultLifetime::UNDECIDED) { + buffer->Decide(active_query->retention); + } + sink.SetResultBuffer(buffer); + } + executor.SetResultBuffer(buffer); + + if (expands) { + // Recorded before Initialize schedules the statement's tasks, which may write before it returns + active_query->RecordDatabases(statement_data.properties); + } + executor.Initialize(std::move(collector)); + + D_ASSERT(executor.GetTypes() == statement_data.types); + if (expands) { + if (!result_statement) { + return nullptr; + } + active_query->result_statement_started = true; + auto &handle = *active_query->handle; + if (!delegating) { + handle.AdoptResultStatement(active_query->QueryProperties(statement_data.properties), statement_data, + std::move(buffer)); + return nullptr; + } + // The handle is released first, so destroying it never takes the context lock held here + handle.context.reset(); + return CompleteDelegatedInternal(lock, handle); + } + D_ASSERT(!active_query->HasOpenResult()); + + auto result = make_uniq(shared_from_this(), statement_data, std::move(types), + std::move(client_properties), std::move(buffer)); + active_query->SetOpenResult(*result); + if (delegating) { + // The collector builds its own result object: run the query and hand that object out. The + // handle is released first, so destroying it never takes the context lock held here + result->context.reset(); + auto produced = CompleteDelegatedInternal(lock, *result); + if (produced) { + return produced; + } + } + return result; +} + +unique_ptr ClientContext::CompleteDelegatedInternal(ClientContextLock &lock, QueryResult &result) { + QueryResultState state; + while (!IsObservable(state = ExecuteTaskInternal(lock, result))) { + if (state == QueryResultState::BLOCKED) { + WaitForTask(lock, result); + } + } + if (result.HasError()) { + // The error is on the handle, which the caller hands out instead + return nullptr; + } + auto &executor = GetExecutor(); + auto produced = executor.GetResult(); + if (executor.HasStreamingResultCollector()) { + active_query->SetOpenResult(*produced); + active_query->collector_built_result = true; + } else { + CleanupInternal(lock, produced.get(), false); + } + return produced; +} + +void ClientContext::WaitForTask(ClientContextLock &lock, BaseQueryResult &result) { + auto &executor = *active_query->executor; + if (executor.HasTaskInProgress()) { + // This thread is holding a partially processed task, the next step resumes it without waiting. + return; + } + executor.WaitForTask(); +} + +QueryResultState ClientContext::ExecuteTaskInternal(ClientContextLock &lock, BaseQueryResult &result) { + D_ASSERT(active_query); + D_ASSERT(active_query->IsOpenResult(result)); + try { + // Surface a pending interrupt even when this thread runs no task that reaches InterruptCheck. + // IsInterrupted() rather than InterruptCheck(): we must not enforce query_deadline here. + // Skip when the executor already has an error: ExecuteTask rethrows that with its original type. + if (IsInterrupted() && !active_query->executor->HasError()) { + throw InterruptException(); + } + auto state = active_query->executor->ExecuteTask(); + UpdateProgressInternal(state); + if (state == QueryResultState::FINISHED && active_query->HasNextStatement()) { + return StartNextStatementInternal(lock); + } + return state; + } catch (std::exception &ex) { + return FailQueryInternal(lock, result, ErrorData(ex)); + } catch (...) { // LCOV_EXCL_START + return FailQueryInternal(lock, result, ErrorData("Unhandled exception in ExecuteTaskInternal")); + } // LCOV_EXCL_STOP +} + +QueryResultState ClientContext::PollInternal(ClientContextLock &lock, BaseQueryResult &result) { + D_ASSERT(active_query); + D_ASSERT(active_query->IsOpenResult(result)); + try { + auto state = active_query->executor->Poll(); + UpdateProgressInternal(state); + if (state == QueryResultState::FINISHED && active_query->HasNextStatement()) { + return StartNextStatementInternal(lock); + } + return state; + } catch (std::exception &ex) { + return FailQueryInternal(lock, result, ErrorData(ex)); + } catch (...) { // LCOV_EXCL_START + return FailQueryInternal(lock, result, ErrorData("Unhandled exception in PollInternal")); + } // LCOV_EXCL_STOP +} + +void ClientContext::UpdateProgressInternal(QueryResultState state) { + if (!active_query->progress_bar) { + return; + } + // todo: this is not correct for streaming results + active_query->progress_bar->Update(IsObservable(state)); + query_progress = active_query->progress_bar->GetDetailedQueryProgress(); +} + +QueryResultState ClientContext::FailQueryInternal(ClientContextLock &lock, BaseQueryResult &result, ErrorData error) { + bool invalidate_transaction = true; + if (error.Type() == ExceptionType::INTERRUPT) { + // No executor when the failure comes between two statements of a query that expands into several + auto &executor = active_query->executor; + if (executor && executor->HasError()) { + // Interrupted by an exception caused in a worker thread + error = executor->GetError(); + invalidate_transaction = ErrorInvalidatesQueryTransaction(error.Type()); + } + } else if (!ErrorInvalidatesQueryTransaction(error.Type())) { + invalidate_transaction = false; + } else if (Exception::InvalidatesDatabase(error.Type()) || error.Type() == ExceptionType::INTERNAL) { + // fatal exceptions invalidate the entire database + auto &db_instance = DatabaseInstance::GetDatabase(*this); + ValidChecker::Invalidate(db_instance, error.RawMessage()); + } + ProcessError(error, active_query->query); + result.SetError(std::move(error)); + EndQueryInternal(lock, false, invalidate_transaction, result.GetErrorObject()); + return QueryResultState::EXECUTION_ERROR; +} + +void ClientContext::AbandonActiveQuery(ClientContextLock &lock) { + if (active_query) { + AbortInternal(lock); + } + interrupt_state = ClientInterruptState::NOT_INTERRUPTED; +} + +unique_ptr ClientContext::CompleteInternal(ClientContextLock &lock, unique_ptr result) { + result->CompleteInternal(lock); + return result; +} + +bool ClientContext::IsActiveResult(ClientContextLock &lock, BaseQueryResult &result) { + if (!active_query) { + return false; + } + return active_query->IsOpenResult(result); +} + +bool ClientContext::ExecutionIsFinished() { + lock_guard guard(context_lock); + if (!active_query || !active_query->executor || active_query->HasNextStatement()) { + return false; + } + return active_query->executor->ExecutionIsFinished(); +} + +QueryResultState ClientContext::StartNextStatementInternal(ClientContextLock &lock) { + QueryResultState state; + do { + auto produced = AdvanceStatementInternal(lock); + // Only a submission meets a collector that builds its own result object + D_ASSERT(!produced); + state = active_query->executor->Poll(); + } while (state == QueryResultState::FINISHED && active_query->HasNextStatement()); + return state; +} + +unique_ptr ClientContext::AdvanceStatementInternal(ClientContextLock &lock) { + // Checked before the finished statement is torn down, so a failure is reported against it + if (IsInterrupted()) { + throw InterruptException(); + } + if (QueryDeadlinePassed()) { + throw InterruptException(QUERY_DEADLINE_ERROR); + } + EndStatementInternal(lock); + auto statement = active_query->TakeNextStatement(); + BeginStatementInternal(lock, statement->query); + RefreshLoggerInternal(); + QueryParameters parameters(active_query->format); + auto produced = SubmitStatementInternal(lock, std::move(statement), parameters); + // A statement that could not start fails the query; the result object of a collector already ended it + if (produced && produced->HasError() && active_query && !active_query->IsOpenResult(*produced)) { + produced->GetErrorObject().Throw(); + } + return produced; +} + +unique_ptr ClientContext::RunToResultStatementInternal(ClientContextLock &lock, QueryResult &handle) { + active_query->inside_submission = true; + try { + // A collector that builds its own result object and fails ends the query + while (active_query && !active_query->result_statement_started && active_query->HasNextStatement()) { + auto state = active_query->executor->ExecuteTask(); + UpdateProgressInternal(state); + if (state == QueryResultState::BLOCKED || state == QueryResultState::NO_TASKS_AVAILABLE) { + WaitForTask(lock, handle); + } else if (state == QueryResultState::FINISHED) { + auto produced = AdvanceStatementInternal(lock); + if (produced) { + return produced; + } + } + } + } catch (std::exception &ex) { + if (active_query) { + FailQueryInternal(lock, handle, ErrorData(ex)); + } else { + handle.SetError(ErrorData(ex)); + } + return nullptr; + } + if (active_query) { + active_query->inside_submission = false; + } + return nullptr; +} + +void ClientContext::EndStatementInternal(ClientContextLock &lock) { + active_query->executor->CancelTasks(); + active_query->executor.reset(); + active_query->progress_bar.reset(); + // The plan outlives the executor + active_query->prepared.reset(); + transaction.ResetActiveQuery(); + client_data->profiler->EndQuery(); +} + +unique_ptr ClientContext::TakeFinishedResultInternal(ClientContextLock &lock) { + if (!active_query->ExpandsIntoSeveral() || active_query->result_statement_started) { + return GetExecutor().GetResult(); + } + // No statement returned rows: the result reports nothing, the way a single statement that returns nothing does + StatementProperties properties; + properties.return_type = StatementReturnType::NOTHING; + return EmptyResultInternal(active_query->handle->GetStatementType(), + active_query->QueryProperties(std::move(properties)), {LogicalType::BOOLEAN}, + {Identifier("Success")}, active_query->format); +} + +unique_ptr ClientContext::EmptyResultInternal(StatementType statement_type, StatementProperties properties, + vector types, vector names, + shared_ptr format) { + if (!format) { + format = ChunkFormat::InMemory(); + } + auto client_properties = GetClientProperties(); + ResultFormatContext format_context {types, names, client_properties, ResultOrdering::UNORDERED}; + shared_ptr format_state = format->InitGlobal(format_context); + auto collection = format->CreateCollection(*this, *format_state, format_context); + // A retained collection is read only once finalized + collection->Finalize(); + return make_uniq(statement_type, std::move(properties), std::move(types), std::move(names), + std::move(collection), std::move(format), std::move(format_state), + std::move(client_properties)); +} + +ResultLifetime ClientContext::DecideResultLifetimeInternal(ClientContextLock &lock, ResultLifetime decision) { + D_ASSERT(active_query && !active_query->result_statement_started); + D_ASSERT(decision != ResultLifetime::UNDECIDED); + if (active_query->retention == ResultLifetime::UNDECIDED) { + active_query->retention = decision; + } + return active_query->retention; +} + +} // namespace duckdb diff --git a/src/duckdb/src/main/client_data.cpp b/src/duckdb/src/main/client_data.cpp index cde8488f9..9dd8937a6 100644 --- a/src/duckdb/src/main/client_data.cpp +++ b/src/duckdb/src/main/client_data.cpp @@ -234,6 +234,7 @@ class ClientBufferManager : public BufferManager { ClientData::ClientData(ClientContext &context) : catalog_search_path(make_uniq(context)) { auto &db = DatabaseInstance::GetDatabase(context); + default_database = DatabaseManager::Get(db).GetDefaultDatabase(); profiler = make_shared_ptr(context); temporary_objects = make_shared_ptr(db, AttachedDatabaseType::TEMP_DATABASE); temporary_objects->oid = DatabaseManager::Get(db).NextOid(); diff --git a/src/duckdb/src/main/client_verify.cpp b/src/duckdb/src/main/client_verify.cpp index 92741cd25..5242d836f 100644 --- a/src/duckdb/src/main/client_verify.cpp +++ b/src/duckdb/src/main/client_verify.cpp @@ -74,7 +74,7 @@ static void ReplaceStatement(unique_ptr &statement, unique_ptr &statement, - PendingQueryParameters query_parameters) { + QueryParameters query_parameters, bool allow_nested_statements) { auto verification = Settings::Get(*this); if (verification == DebugStatementVerification::COPY_STATEMENT) { if (statement->type == StatementType::LOGICAL_PLAN_STATEMENT) { @@ -87,11 +87,9 @@ void ClientContext::StatementVerification(ClientContextLock &lock, unique_ptrToString()); // FIXME: these properties don't round-trip in ToString(), so we overwrite them manually @@ -180,7 +178,7 @@ void ClientContext::StatementVerification(ClientContextLock &lock, unique_ptrtype != StatementType::SELECT_STATEMENT) { + if (!allow_nested_statements || statement->type != StatementType::SELECT_STATEMENT) { // only supported for SELECT (for now?) return; } @@ -188,7 +186,7 @@ void ClientContext::StatementVerification(ClientContextLock &lock, unique_ptrempty()) { + if (query_parameters.statement_args && !query_parameters.statement_args->empty()) { // not supported for statements that already have parameters return; } @@ -239,7 +237,13 @@ void ClientContext::StatementVerification(ClientContextLock &lock, unique_ptrnamed_values = std::move(prep_verifier.values); ReplaceStatement(statement, std::move(execute)); - } else if (verification == DebugStatementVerification::EXPLAIN_STATEMENT) { + } else if (verification == DebugStatementVerification::EXPLAIN_STATEMENT || + verification == DebugStatementVerification::EXPLAIN_SQL || + verification == DebugStatementVerification::EXPLAIN_SQL_STRICT) { + const bool export_sql = verification != DebugStatementVerification::EXPLAIN_STATEMENT; + if (!allow_nested_statements || (export_sql && statement->type != StatementType::SELECT_STATEMENT)) { + return; + } if (statement->type == StatementType::EXPLAIN_STATEMENT) { // don't explain explain... return; @@ -248,24 +252,66 @@ void ClientContext::StatementVerification(ClientContextLock &lock, unique_ptrempty()) { + if (query_parameters.statement_args && !query_parameters.statement_args->empty()) { // not supported for statements that already have parameters return; } - // deliberately left without source text: the EXPLAIN runs as a nested statement, and giving it a - // query would let it consume the error location that belongs to the statement we are verifying - auto explain_stmt = make_uniq(statement->Copy()); - // Disable the profiler during the verification EXPLAIN to prevent it from consuming the profiler context - // (which would lose parser timing captured before StatementVerification was called) and from - // overwriting the profiling output file with the EXPLAIN's profiling data. - auto &client_config = ClientConfig::GetConfig(*this); - bool saved_profiler = client_config.enable_profiler; - ScopedConfigSetting suppress_profiling( - client_config, [](ClientConfig &config) { config.enable_profiler = false; }, - [saved_profiler](ClientConfig &config) { config.enable_profiler = saved_profiler; }); - auto explain_result = RunStatementInternal(lock, std::move(explain_stmt), query_parameters); - if (explain_result->HasError()) { - explain_result->ThrowError(); + auto explain_and_replace = [&]() { + // deliberately left without source text: the EXPLAIN runs as a nested statement, and giving it a + // query would let it consume the error location that belongs to the statement we are verifying + auto explain_stmt = make_uniq( + statement->Copy(), export_sql ? ExplainType::EXPLAIN_SQL : ExplainType::EXPLAIN_STANDARD); + explain_stmt->allow_unsupported_sql = verification == DebugStatementVerification::EXPLAIN_SQL; + // Disable the profiler during the verification EXPLAIN to prevent it from consuming the profiler context + // (which would lose parser timing captured before StatementVerification was called) and from + // overwriting the profiling output file with the EXPLAIN's profiling data. + auto &client_config = ClientConfig::GetConfig(*this); + bool saved_profiler = client_config.enable_profiler; + ScopedConfigSetting suppress_profiling( + client_config, [](ClientConfig &config) { config.enable_profiler = false; }, + [saved_profiler](ClientConfig &config) { config.enable_profiler = saved_profiler; }); + auto explain_result = RunStatementInternal(lock, std::move(explain_stmt), query_parameters, false); + explain_result->ThrowIfError(); + if (!export_sql) { + return; + } + auto chunk = explain_result->Fetch(); + if (!chunk || chunk->size() == 0) { + D_ASSERT(verification == DebugStatementVerification::EXPLAIN_SQL); + return; + } + D_ASSERT(chunk && chunk->size() == 1 && chunk->ColumnCount() == 2); + auto sql = chunk->GetValue(1, 0).GetValue(); + auto parser_options = GetParserOptions(); + parser_options.identifier_case_mode = IdentifierCaseMode::PRESERVE_CASE; + Parser parser(parser_options); + parser.ParseQuery(sql); + if (parser.statements.size() != 1 || parser.statements[0]->type != StatementType::SELECT_STATEMENT || + !parser.statements[0]->named_param_map.empty()) { + throw InternalException("SQL export verification did not produce one parameter-free SELECT"); + } + ReplaceStatement(statement, std::move(parser.statements[0])); + }; + // The generated SQL encodes what the optimizer derived under the planning snapshot (statistics, exact + // cardinalities), so it must execute in the transaction the EXPLAIN planned it in + const bool shared_transaction = export_sql && transaction.IsAutoCommit(); + if (shared_transaction) { + transaction.SetAutoCommit(false); + } + try { + explain_and_replace(); + } catch (...) { + if (shared_transaction) { + if (transaction.HasActiveTransaction()) { + transaction.Rollback(nullptr); + } + transaction.SetAutoCommit(true); + } + throw; + } + if (shared_transaction) { + // the transaction stays open and the verified statement commits it + transaction.SetAutoCommit(true); } } } diff --git a/src/duckdb/src/main/config.cpp b/src/duckdb/src/main/config.cpp index 8f48c7456..9917653b6 100644 --- a/src/duckdb/src/main/config.cpp +++ b/src/duckdb/src/main/config.cpp @@ -3,6 +3,7 @@ #include "duckdb/common/cgroups.hpp" #include "duckdb/common/file_system.hpp" #include "duckdb/main/http/http_transport_manager.hpp" +#include "duckdb/main/extension/external_extension_provider.hpp" #include "duckdb/common/operator/cast_operators.hpp" #include "duckdb/common/operator/multiply.hpp" #include "duckdb/common/string_util.hpp" @@ -70,7 +71,6 @@ DebugVerificationMode DBConfigOptions::global_verification_mode = DebugVerificat static const ConfigurationOption internal_options[] = { - DUCKDB_GLOBAL(DeltaOnlyVariantEncodingEnabledSetting), DUCKDB_GLOBAL(AccessModeSetting), DUCKDB_LOCAL(ActiveGrammarExtensionsSetting), DUCKDB_SETTING_CALLBACK(AllocatorBackgroundThreadsSetting), @@ -102,14 +102,16 @@ static const ConfigurationOption internal_options[] = { DUCKDB_SETTING(CatalogErrorMaxSchemasSetting), DUCKDB_SETTING_CALLBACK(CheckpointOnDetachSetting), DUCKDB_GLOBAL(CheckpointThresholdSetting), - DUCKDB_SETTING_CALLBACK(CurrentDialectSetting), + DUCKDB_LOCAL(CurrentDialectSetting), DUCKDB_SETTING_CALLBACK(CurrentTransactionInvalidationPolicySetting), DUCKDB_SETTING(CustomExtensionRepositorySetting), DUCKDB_GLOBAL(CustomUserAgentSetting), + DUCKDB_GLOBAL(DebugAbortOnWalFailureSetting), DUCKDB_SETTING(DebugAsofIejoinSetting), DUCKDB_SETTING_CALLBACK(DebugCheckpointAbortSetting), DUCKDB_SETTING(DebugCheckpointScanSleepMsSetting), DUCKDB_SETTING(DebugCheckpointSleepMsSetting), + DUCKDB_GLOBAL(DebugDeltaOnlyVariantEncodingEnabledSetting), DUCKDB_SETTING(DebugDisableOptimizerSetting), DUCKDB_SETTING(EnableCachingOperatorsSetting), DUCKDB_SETTING(DebugEvictionQueueSleepMicroSecondsSetting), @@ -127,12 +129,13 @@ static const ConfigurationOption internal_options[] = { DUCKDB_GLOBAL(DebugOrderVerificationSetting), DUCKDB_SETTING_CALLBACK(DebugPhysicalTableScanExecutionStrategySetting), DUCKDB_SETTING(DebugSkipCheckpointOnCommitSetting), - DUCKDB_SETTING(DebugTransformerTrampolineStyleSetting), DUCKDB_GLOBAL(DebugVerificationModeSetting), DUCKDB_SETTING(DebugVerificationProjectionSetting), DUCKDB_SETTING(DebugVerifyAggregateStateExportSetting), DUCKDB_SETTING(DebugVerifyBlocksSetting), DUCKDB_SETTING(DebugVerifyColumnBindingsSetting), + DUCKDB_SETTING_CALLBACK(DebugVerifyProgressSetting), + DUCKDB_SETTING(DebugVerifyProgressIgnoreSetting), DUCKDB_SETTING(DebugVerifySerializerSetting), DUCKDB_SETTING_CALLBACK(DebugVerifyStatementSetting), DUCKDB_SETTING(DebugVerifyStatsSetting), @@ -169,22 +172,26 @@ static const ConfigurationOption internal_options[] = { DUCKDB_LOCAL(EnableProgressBarPrintSetting), DUCKDB_SETTING(EnableViewDependenciesSetting), DUCKDB_GLOBAL(EnabledLogTypes), + DUCKDB_SETTING_CALLBACK(ErrorOnDivisionByZeroSetting), DUCKDB_SETTING(ErrorsAsJSONSetting), DUCKDB_SETTING_CALLBACK(ExperimentalMetadataReuseSetting), DUCKDB_SETTING_CALLBACK(ExplainOutputSetting), DUCKDB_GLOBAL(ExtensionDirectoriesSetting), DUCKDB_SETTING_CALLBACK(ExtensionDirectorySetting), DUCKDB_SETTING_CALLBACK(ExtensionRepositoryDirectorySetting), - DUCKDB_SETTING_CALLBACK(ExternalFileCacheLocalBlockSizeSetting), - DUCKDB_SETTING_CALLBACK(ExternalFileCacheRemoteBlockSizeSetting), + DUCKDB_SETTING_CALLBACK(ExternalFileCacheLocalMaxBlockSizeSetting), + DUCKDB_SETTING_CALLBACK(ExternalFileCacheRemoteMaxBlockSizeSetting), + DUCKDB_SETTING_CALLBACK(ExternalFileCacheRemoteMinBlockSizeSetting), + DUCKDB_SETTING_CALLBACK(ExternalFileCacheRequestSizingSetting), DUCKDB_SETTING(ExternalFileCacheSpillSetting), DUCKDB_SETTING_CALLBACK(ExternalThreadsSetting), DUCKDB_SETTING(FileSearchPathSetting), DUCKDB_SETTING_CALLBACK(ForceColumnMetadataReuseSetting), DUCKDB_SETTING_CALLBACK(ForceCompressionSetting), + DUCKDB_SETTING_CALLBACK(FsyncModeSetting), DUCKDB_SETTING(GeometryMinimumShreddingSize), - DUCKDB_SETTING(HeapBasedParserSetting), DUCKDB_SETTING_CALLBACK(HomeDirectorySetting), + DUCKDB_GLOBAL(HTTPClientPoolCapacitySetting), DUCKDB_GLOBAL(HTTPProxySetting), DUCKDB_SETTING(HTTPProxyPasswordSetting), DUCKDB_SETTING(HTTPProxyUsernameSetting), @@ -213,7 +220,6 @@ static const ConfigurationOption internal_options[] = { DUCKDB_SETTING(MaxVacuumTasksSetting), DUCKDB_SETTING(MergeJoinThresholdSetting), DUCKDB_SETTING(NestedLoopJoinThresholdSetting), - DUCKDB_SETTING_CALLBACK(NullOnDivisionByZeroSetting), DUCKDB_SETTING_CALLBACK(OldImplicitCastingSetting), DUCKDB_LOCAL(OperatorMemoryLimitSetting), DUCKDB_SETTING(OrderByNonIntegerLiteralSetting), @@ -235,6 +241,7 @@ static const ConfigurationOption internal_options[] = { DUCKDB_LOCAL(ProfilingRendererSettingsSetting), DUCKDB_LOCAL(ProgressBarTimeSetting), DUCKDB_SETTING_CALLBACK(ReadAheadDepthSetting), + DUCKDB_SETTING(RedactHttpLogsSetting), DUCKDB_SETTING_CALLBACK(RegexMatchOperatorSemanticsSetting), DUCKDB_SETTING(ScalarSubqueryErrorOnMultipleRowsSetting), DUCKDB_SETTING(SchedulerProcessPartialSetting), @@ -262,6 +269,7 @@ static const ConfigurationOption internal_options[] = { FINAL_SETTING}; static const ConfigurationAlias setting_aliases[] = { + DUCKDB_SETTING_ALIAS("__delta_only_variant_encoding_enabled", DebugDeltaOnlyVariantEncodingEnabledSetting), DUCKDB_SETTING_ALIAS("enable_caching_operators", EnableCachingOperatorsSetting), DUCKDB_SETTING_ALIAS("force_bitpacking_mode", ForceBitpackingModeSetting), DUCKDB_SETTING_ALIAS("force_mbedtls_unsafe", ForceMbedtlsUnsafeSetting), @@ -544,6 +552,24 @@ LogicalType DBConfig::ParseLogicalType(const string &type) { return type_id; } +ExtensionOptionInfo::~ExtensionOptionInfo() { +} + +SetScope ExtensionOption::ResolveScope(const Identifier &name, SetScope scope) const { + if (scope == SetScope::AUTOMATIC) { + scope = default_scope; + } + if (scope == SetScope::GLOBAL && !supports_global) { + throw InvalidInputException( + "Setting \"%s\" cannot be set as a global variable - it can only be set per session", name); + } + if (scope == SetScope::SESSION && !supports_session) { + throw InvalidInputException("Setting \"%s\" cannot be set as a session variable - it can only be set globally", + name); + } + return scope; +} + bool DBConfig::HasExtensionOption(const Identifier &name) const { return user_settings.HasExtensionOption(name); } @@ -553,9 +579,14 @@ bool DBConfig::TryGetExtensionOption(const Identifier &name, ExtensionOption &re } void DBConfig::AddExtensionOption(const Identifier &name, string description, LogicalType parameter, - const Value &default_value, set_option_callback_t function, SetScope default_scope) { - ExtensionOption extension_option(std::move(description), std::move(parameter), function, default_value, - default_scope); + const Value &default_value, set_option_callback_t function, SetScope default_scope, + bool is_debug, bool is_deprecated) { + AddExtensionOption(name, ExtensionOption(std::move(description), std::move(parameter), function, default_value, + default_scope, is_debug, is_deprecated)); +} + +void DBConfig::AddExtensionOption(const Identifier &name, ExtensionOption extension_option) { + auto default_value = extension_option.default_value; auto setting_index = user_settings.AddExtensionOption(name, std::move(extension_option)); // copy over unrecognized options, if they match the new extension option auto iter = options.unrecognized_options.find(name); @@ -592,6 +623,11 @@ CastFunctionSet &DBConfig::GetCastFunctions() { return type_manager->GetCastFunctions(); } +const CastFunctionSet &DBConfig::GetCastFunctions() const { + const auto &manager = *type_manager; + return manager.GetCastFunctions(); +} + TypeManager &DBConfig::GetTypeManager() { return *type_manager; } @@ -708,10 +744,10 @@ idx_t DBConfig::GetSystemAvailableMemory(FileSystem &fs) { return available_memory; } -idx_t DBConfig::ParseMemoryLimit(const string &arg) { +optional_idx DBConfig::ParseMemoryLimit(const string &arg) { if (arg[0] == '-' || arg == "null" || arg == "none") { - // infinite - return NumericLimits::Maximum(); + // unlimited + return optional_idx(); } idx_t result; @@ -719,15 +755,21 @@ idx_t DBConfig::ParseMemoryLimit(const string &arg) { if (!error.empty()) { if (error == "Memory cannot be negative") { - result = DConstants::INVALID_INDEX; - } else { - throw ParserException(error); + return optional_idx(); } + throw ParserException(error); + } + if (result == DConstants::INVALID_INDEX) { + return optional_idx(); } - return result; } +idx_t DBConfig::ParseMemoryLimitOrMaximum(const string &arg) { + auto limit = ParseMemoryLimit(arg); + return limit.IsValid() ? limit.GetIndex() : NumericLimits::Maximum(); +} + optional_idx DBConfig::ParseMemoryLimitSlurm(const string &arg) { if (arg.empty()) { return optional_idx(); @@ -883,8 +925,8 @@ const ExtensionCallbackManager &DBConfig::GetCallbackManager() const { return *callback_manager; } -string DBConfig::SanitizeAllowedPath(const string &path_p) const { - auto result = file_system->CanonicalizePath(path_p); +string DBConfig::SanitizeAllowedPath(const string &path_p, optional_ptr opener) const { + auto result = file_system->CanonicalizePath(FileSystem::ExpandPath(path_p, opener)); // allowed_directories/allowed_path always uses forward slashes regardless of the OS auto path_sep = file_system->PathSeparator(path_p); if (path_sep != "/") { @@ -933,21 +975,56 @@ void DBConfig::AddAllowedDirectory(const string &path) { if (!StringUtil::EndsWith(allowed_directory, "/")) { allowed_directory += "/"; } + lock_guard guard(allowed_paths_lock); options.allowed_directories.insert(allowed_directory); } +bool DBConfig::IsOptimizerDisabled(OptimizerType type) const { + lock_guard guard(disabled_optimizers_lock); + return options.disabled_optimizers.find(type) != options.disabled_optimizers.end(); +} + +set DBConfig::GetDisabledOptimizers() const { + lock_guard guard(disabled_optimizers_lock); + return options.disabled_optimizers; +} + +void DBConfig::SetDisabledOptimizers(set disabled_optimizers) { + lock_guard guard(disabled_optimizers_lock); + options.disabled_optimizers = std::move(disabled_optimizers); +} + void DBConfig::AddAllowedPath(const string &path) { auto allowed_path = SanitizeAllowedPath(path); + lock_guard guard(allowed_paths_lock); options.allowed_paths.insert(allowed_path); } -bool DBConfig::CanAccessFile(const string &input_path, FileType type) { +void DBConfig::AddAllowedDatabasePath(const string &database_path) { + AddAllowedPath(database_path); + AddAllowedPath(database_path + ".wal"); + AddAllowedPath(database_path + ".wal.checkpoint"); + AddAllowedPath(database_path + ".wal.recovery"); +} + +vector DBConfig::GetAllowedDirectories() const { + lock_guard guard(allowed_paths_lock); + return vector(options.allowed_directories.begin(), options.allowed_directories.end()); +} + +vector DBConfig::GetAllowedPaths() const { + lock_guard guard(allowed_paths_lock); + return vector(options.allowed_paths.begin(), options.allowed_paths.end()); +} + +bool DBConfig::CanAccessFile(const string &input_path, FileType type, optional_ptr opener) { if (Settings::Get(*this)) { // all external access is allowed return true; } - string path = SanitizeAllowedPath(input_path); + string path = SanitizeAllowedPath(input_path, opener); + lock_guard guard(allowed_paths_lock); if (options.allowed_paths.count(path) > 0) { // path is explicitly allowed return true; @@ -988,6 +1065,17 @@ void DBConfig::SetHTTPUtil(const shared_ptr &new_http_util) { http_transport_manager->SetHTTPUtil(new_http_util); } +void DBConfig::SetExternalExtensionProvider(const shared_ptr &new_provider) { + if (!new_provider) { + throw InvalidInputException("External extension provider cannot be null"); + } + external_extension_provider = new_provider; +} + +ExternalExtensionProvider &DBConfig::GetExternalExtensionProvider() const { + return *external_extension_provider; +} + HTTPUtil &DBConfig::GetHTTPUtil() const { return http_transport_manager->GetHTTPUtil(); } @@ -996,4 +1084,8 @@ HTTPTransportManager &DBConfig::GetHTTPTransportManager() { return *http_transport_manager; } +const HTTPTransportManager &DBConfig::GetHTTPTransportManager() const { + return *http_transport_manager; +} + } // namespace duckdb diff --git a/src/duckdb/src/main/connection.cpp b/src/duckdb/src/main/connection.cpp index 166b9bff8..9b70cdb3e 100644 --- a/src/duckdb/src/main/connection.cpp +++ b/src/duckdb/src/main/connection.cpp @@ -13,14 +13,19 @@ #include "duckdb/main/relation/table_relation.hpp" #include "duckdb/main/relation/value_relation.hpp" #include "duckdb/main/relation/view_relation.hpp" +#include "duckdb/parser/parsed_data/transaction_info.hpp" #include "duckdb/parser/parser.hpp" #include "duckdb/planner/logical_operator.hpp" #include "duckdb/main/statement_iterator.hpp" namespace duckdb { -Connection::Connection(DatabaseInstance &database) +Connection::Connection(DatabaseInstance &database) : Connection(database, ConnectionType::USER) { +} + +Connection::Connection(DatabaseInstance &database, ConnectionType connection_type) : context(make_shared_ptr(database.shared_from_this())) { + context->connection_type = connection_type; auto &connection_manager = ConnectionManager::Get(database); connection_manager.AssignConnectionId(*this); connection_manager.AddConnection(*context); @@ -29,6 +34,10 @@ Connection::Connection(DatabaseInstance &database) Connection::Connection(DuckDB &database) : Connection(*database.instance) { } +Connection Connection::CreateInternal(DatabaseInstance &database) { + return Connection(database, ConnectionType::INTERNAL); +} + Connection::Connection(Connection &&other) noexcept { std::swap(context, other.context); std::swap(connection_id, other.connection_id); @@ -77,51 +86,44 @@ void Connection::ForceParallelism() { ClientConfig::GetConfig(*context).verify_parallelism = true; } -unique_ptr Connection::SendQuery(const string &query, QueryParameters query_parameters) { - return context->Query(query, query_parameters); +unique_ptr Connection::Query(const string &query) { + return context->Query(query, QueryParameters()); } -unique_ptr Connection::SendQuery(unique_ptr statement, QueryParameters query_parameters) { - return context->Query(std::move(statement), query_parameters); +unique_ptr Connection::Query(unique_ptr statement, shared_ptr format) { + return context->Query(std::move(statement), std::move(format)); } -unique_ptr Connection::Query(const string &query) { - QueryParameters query_parameters; - query_parameters.output_type = QueryResultOutputType::FORCE_MATERIALIZED; - auto result = context->Query(query, query_parameters); - D_ASSERT(result->GetResultType() == QueryResultType::MATERIALIZED_RESULT); - return unique_ptr_cast(std::move(result)); +unique_ptr Connection::Query(const string &query, shared_ptr format) { + return context->Query(query, std::move(format)); } -unique_ptr Connection::Query(unique_ptr statement, - QueryResultMemoryType memory_type) { - QueryParameters query_parameters; - query_parameters.output_type = QueryResultOutputType::FORCE_MATERIALIZED; - query_parameters.memory_type = memory_type; - auto result = context->Query(std::move(statement), query_parameters); - D_ASSERT(result->GetResultType() == QueryResultType::MATERIALIZED_RESULT); - return unique_ptr_cast(std::move(result)); +unique_ptr Connection::Submit(const string &query, const QueryParameters &query_parameters) { + return context->Submit(query, query_parameters); } -unique_ptr Connection::PendingQuery(const string &query, QueryParameters query_parameters) { - return context->PendingQuery(query, query_parameters); +unique_ptr Connection::Submit(unique_ptr statement, + const QueryParameters &query_parameters) { + return context->Submit(std::move(statement), query_parameters); } -unique_ptr Connection::PendingQuery(unique_ptr statement, - QueryParameters query_parameters) { - return context->PendingQuery(std::move(statement), query_parameters); +unique_ptr Connection::Submit(const string &query, shared_ptr format) { + return context->Submit(query, std::move(format)); } -unique_ptr Connection::PendingQuery(const string &query, - identifier_map_t &named_values, - QueryParameters query_parameters) { - return context->PendingQuery(query, named_values, query_parameters); +unique_ptr Connection::Submit(unique_ptr statement, shared_ptr format) { + return context->Submit(std::move(statement), std::move(format)); } -unique_ptr Connection::PendingQuery(unique_ptr statement, - identifier_map_t &named_values, - QueryParameters query_parameters) { - return context->PendingQuery(std::move(statement), named_values, query_parameters); +unique_ptr Connection::Submit(const string &query, identifier_map_t &named_values, + const QueryParameters &query_parameters) { + return context->Submit(query, named_values, query_parameters); +} + +unique_ptr Connection::Submit(unique_ptr statement, + identifier_map_t &named_values, + const QueryParameters &query_parameters) { + return context->Submit(std::move(statement), named_values, query_parameters); } static identifier_map_t ConvertParamListToMap(vector ¶m_list) { @@ -133,20 +135,16 @@ static identifier_map_t ConvertParamListToMap(vector return named_values; } -unique_ptr Connection::PendingQuery(const string &query, vector &values, - QueryParameters query_parameters) { +unique_ptr Connection::Submit(const string &query, vector &values, + const QueryParameters &query_parameters) { auto named_params = ConvertParamListToMap(values); - return context->PendingQuery(query, named_params, query_parameters); + return context->Submit(query, named_params, query_parameters); } -unique_ptr Connection::PendingQuery(unique_ptr statement, vector &values, - QueryParameters query_parameters) { +unique_ptr Connection::Submit(unique_ptr statement, vector &values, + const QueryParameters &query_parameters) { auto named_params = ConvertParamListToMap(values); - return context->PendingQuery(std::move(statement), named_params, query_parameters); -} - -unique_ptr Connection::PendingQuery(const string &query, PendingQueryParameters parameters) { - return context->PendingQuery(query, parameters); + return context->Submit(std::move(statement), named_params, query_parameters); } unique_ptr Connection::Prepare(const string &query) { @@ -159,15 +157,9 @@ unique_ptr Connection::Prepare(unique_ptr state unique_ptr Connection::QueryParamsRecursive(const string &query, vector &values) { auto named_params = ConvertParamListToMap(values); - PendingQueryParameters parameters; - parameters.parameters = &named_params; - parameters.query_parameters.output_type = QueryResultOutputType::FORCE_MATERIALIZED; - parameters.query_parameters.memory_type = QueryResultMemoryType::BUFFER_MANAGED; - auto pending = PendingQuery(query, parameters); - if (pending->HasError()) { - return make_uniq(pending->GetErrorObject()); - } - return pending->Execute(); + QueryParameters parameters; + parameters.statement_args = named_params; + return context->Query(query, parameters); } unique_ptr Connection::TableInfo(const Identifier &database_name, const Identifier &schema_name, @@ -184,15 +176,17 @@ unique_ptr Connection::TableInfo(const Identifier &table_name) } vector> Connection::ExtractStatements(const string &query) { - // Eager convenience over the lazy ClientContext::ExtractStatements iterator: drain the - // engine-facing statements into a vector. auto &client_context = *context; - auto iterator = client_context.IterateStatements(query); vector> result; - while (iterator.Peek()) { - if (auto statement = iterator.GetStatement()) { - result.push_back(std::move(statement)); + try { + auto iterator = client_context.IterateStatements(query); + while (iterator.Peek()) { + result.push_back(iterator.GetStatement()); } + } catch (std::exception &ex) { + ErrorData error(ex); + client_context.ProcessError(error, query); + error.Throw(); } return result; } @@ -260,6 +254,13 @@ shared_ptr Connection::TableFunction(const string &fname, const vector return make_shared_ptr(context, fname, values, named_parameters); } +shared_ptr Connection::TableFunction(const string &fname, const vector &values, + const named_parameter_map_t &named_parameters, + shared_ptr bind_info) { + return make_shared_ptr(context, fname, values, named_parameters, nullptr, true, + std::move(bind_info)); +} + shared_ptr Connection::TableFunction(const string &fname, const vector &values) { return make_shared_ptr(context, fname, values); } @@ -307,7 +308,7 @@ shared_ptr Connection::ReadCSV(const string &csv_file, const vector column_list; for (auto &column : columns) { - auto col_list = Parser::ParseColumnList(column, context->GetParserOptions()); + auto col_list = Parser(*context).ParseColumnList(column); if (col_list.LogicalColumnCount() != 1) { throw ParserException("Expected a single column definition"); } @@ -339,24 +340,18 @@ shared_ptr Connection::RelationFromQuery(unique_ptr s } void Connection::BeginTransaction() { - auto result = Query("BEGIN TRANSACTION"); - if (result->HasError()) { - result->ThrowError(); - } + TransactionInfo info(TransactionType::BEGIN_TRANSACTION); + context->RunTransactionStatement(info); } void Connection::Commit() { - auto result = Query("COMMIT"); - if (result->HasError()) { - result->ThrowError(); - } + TransactionInfo info(TransactionType::COMMIT); + context->RunTransactionStatement(info); } void Connection::Rollback() { - auto result = Query("ROLLBACK"); - if (result->HasError()) { - result->ThrowError(); - } + TransactionInfo info(TransactionType::ROLLBACK); + context->RunTransactionStatement(info); } void Connection::SetAutoCommit(bool auto_commit) { diff --git a/src/duckdb/src/main/database.cpp b/src/duckdb/src/main/database.cpp index f18b0d8f6..6817c5f23 100644 --- a/src/duckdb/src/main/database.cpp +++ b/src/duckdb/src/main/database.cpp @@ -7,6 +7,7 @@ #include "duckdb/catalog/catalog.hpp" #include "duckdb/main/http/http_util.hpp" #include "duckdb/main/http/http_transport_manager.hpp" +#include "duckdb/main/extension/external_extension_provider.hpp" #include "duckdb/common/virtual_file_system.hpp" #include "duckdb/common/local_file_system.hpp" #include "duckdb/execution/index/index_type_set.hpp" @@ -63,6 +64,7 @@ DBConfig::DBConfig() { error_manager = make_uniq(); secret_manager = make_uniq(); http_transport_manager = HTTPTransportManager::Create(make_shared_ptr()); + external_extension_provider = make_shared_ptr(); callback_manager = make_uniq(); } @@ -285,7 +287,7 @@ void DatabaseInstance::LoadExtensionSettings() { // if the extension provided the option, it should no longer be unrecognized. D_ASSERT(config.options.unrecognized_options.find(name) == config.options.unrecognized_options.end()); auto &context = *con.context; - PhysicalSet::SetExtensionVariable(context, extension_option, SetScope::GLOBAL, value); + PhysicalSet::SetExtensionVariable(context, name, extension_option, SetScope::GLOBAL, value); } con.Commit(); @@ -300,6 +302,36 @@ static duckdb_ext_api_v1 CreateAPIv1Wrapper() { } void DatabaseInstance::Initialize(const char *database_path, DBConfig *user_config) { + InitializeInstance(database_path, user_config); + if (!db_manager->HasAttachedDatabase()) { + CreateMainDatabase(); + } + // The main database is the default; a storage extension may have attached it during startup instead. + optional_ptr main_database; + for (auto &attached : db_manager->GetDatabases()) { + if (attached->IsSystem()) { + continue; + } + if (!main_database || attached->oid < main_database->oid) { + main_database = attached.get(); + } + } + db_manager->SetDefaultDatabase(main_database->GetName()); + StartScheduler(); +} + +void DatabaseInstance::InitializeEmpty(DBConfig *user_config) { + InitializeInstance(nullptr, user_config); + StartScheduler(); +} + +void DatabaseInstance::StartScheduler() { + scheduler->SetThreads(config.options.maximum_threads, Settings::Get(config)); + scheduler->SetAsyncThreads(config.options.async_threads); + scheduler->RelaunchThreads(); +} + +void DatabaseInstance::InitializeInstance(const char *database_path, DBConfig *user_config) { DBConfig default_config; DBConfig *config_ptr = &default_config; if (user_config) { @@ -309,6 +341,11 @@ void DatabaseInstance::Initialize(const char *database_path, DBConfig *user_conf Configure(*config_ptr, database_path); // publish what this binary links, unless the config already carries a set handed to us ExtensionHelper::RegisterLinkedExtensions(config); + for (auto &linked : config.linked_extensions) { + if (linked.database_callback) { + linked.database_callback(*this); + } + } create_api_v1 = CreateAPIv1Wrapper; invoke_capi_v2 = InvokeCAPIV2Entrypoint; @@ -361,15 +398,6 @@ void DatabaseInstance::Initialize(const char *database_path, DBConfig *user_conf } LoadExtensionSettings(); - - if (!db_manager->HasAttachedDatabase()) { - CreateMainDatabase(); - } - - // only increase thread count after storage init because we get races on catalog otherwise - scheduler->SetThreads(config.options.maximum_threads, Settings::Get(config)); - scheduler->SetAsyncThreads(config.options.async_threads); - scheduler->RelaunchThreads(); } DuckDB::DuckDB(const char *path, DBConfig *new_config) : instance(make_shared_ptr()) { @@ -383,6 +411,16 @@ DuckDB::DuckDB(const char *path, DBConfig *new_config) : instance(make_shared_pt DuckDB::DuckDB(const string &path, DBConfig *config) : DuckDB(path.c_str(), config) { } +shared_ptr DuckDB::CreateEmpty(DBConfig *config) { + auto instance = make_shared_ptr(); + instance->InitializeEmpty(config); + auto db = make_shared_ptr(*instance); + if (instance->config.options.load_extensions) { + ExtensionHelper::LoadAllExtensions(*db); + } + return db; +} + DuckDB::DuckDB(DatabaseInstance &instance_p) : instance(instance_p.shared_from_this()) { } @@ -515,12 +553,9 @@ void DatabaseInstance::Configure(DBConfig &new_config, const char *database_path } else { config.file_system = make_uniq(FileSystem::CreateLocal()); } - config.http_transport_manager->Initialize(DBConfig::GetSystemMaxThreads(*config.file_system)); + config.http_transport_manager->Initialize(config); if (database_path && !Settings::Get(*this)) { - config.AddAllowedPath(database_path); - config.AddAllowedPath(database_path + string(".wal")); - config.AddAllowedPath(database_path + string(".wal.checkpoint")); - config.AddAllowedPath(database_path + string(".wal.recovery")); + config.AddAllowedDatabasePath(database_path); if (!config.options.temporary_directory.empty()) { config.AddAllowedDirectory(config.options.temporary_directory); } diff --git a/src/duckdb/src/main/database_manager.cpp b/src/duckdb/src/main/database_manager.cpp index 300c2449a..86bf27271 100644 --- a/src/duckdb/src/main/database_manager.cpp +++ b/src/duckdb/src/main/database_manager.cpp @@ -3,6 +3,7 @@ #include "duckdb/catalog/catalog.hpp" #include "duckdb/catalog/catalog_search_path.hpp" #include "duckdb/main/attached_database.hpp" +#include "duckdb/main/external_resources_manager.hpp" #include "duckdb/main/client_data.hpp" #include "duckdb/main/query_profiler.hpp" #include "duckdb/main/database.hpp" @@ -105,7 +106,10 @@ bool RequiresTrackingAttaches(const string &path, const string &db_type) { shared_ptr DatabaseManager::AttachDatabase(ClientContext &context, AttachInfo &info, AttachOptions &options) { string extension = ""; - if (FileSystem::IsRemoteFile(info.path, extension)) { + // a storage extension named by an explicit TYPE interprets the path itself (e.g. as a connection URI) + // so a remote-looking path only implies a remote file (read-only, requiring e.g. httpfs) otherwise + bool explicit_storage_type = !options.db_type.empty() && !StringUtil::CIEquals(options.db_type, "duckdb"); + if (!explicit_storage_type && FileSystem::IsRemoteFile(info.path, extension)) { if (options.access_mode == AccessMode::AUTOMATIC) { // Attaching of remote files gets bumped to READ_ONLY // This is due to the fact that on most (all?) remote files writes to DB are not available @@ -131,7 +135,7 @@ shared_ptr DatabaseManager::AttachDatabase(ClientContext &cont auto existing_mode = existing_db->IsReadOnly() ? AccessMode::READ_ONLY : AccessMode::READ_WRITE; auto existing_mode_str = EnumUtil::ToString(existing_mode); auto attached_mode = EnumUtil::ToString(options.access_mode); - throw BinderException("Database \"%s\" is already attached in %s mode, cannot re-attach in %s mode", + throw BinderException("Database %s is already attached in %s mode, cannot re-attach in %s mode", info.name, existing_mode_str, attached_mode); } if (!options.default_table.Name().empty()) { @@ -235,7 +239,7 @@ optional_ptr DatabaseManager::FinalizeAttach(ClientContext &co detached_db = std::move(entry.first->second); databases[name] = attached_db; } else { - throw BinderException("Failed to attach database: database with name \"%s\" already exists", name); + throw BinderException("Failed to attach database: database with name %s already exists", name); } } } @@ -244,9 +248,15 @@ optional_ptr DatabaseManager::FinalizeAttach(ClientContext &co if (detached_db->GetCatalog().Supports(RemoteCapability::IS_REMOTE)) { --remote_catalog_count; } + auto deleter = detached_db->ExtractDeleter(); meta_transaction.DetachDatabase(*detached_db); detached_db->OnDetach(context); detached_db.reset(); + // Best-effort teardown: the replacement is already in effect, so a deleter failure must not + // abort the statement mid-swap; TryDelete logs it as a warning instead. + if (deleter) { + deleter->TryDelete(); + } } if (attached_db->GetCatalog().Supports(RemoteCapability::IS_REMOTE)) { ++remote_catalog_count; @@ -258,8 +268,9 @@ optional_ptr DatabaseManager::FinalizeAttach(ClientContext &co return db_ref; } -void DatabaseManager::DetachDatabase(ClientContext &context, const Identifier &name, OnEntryNotFound if_not_found) { - if (GetDefaultDatabase(context) == name) { +void DatabaseManager::DetachDatabase(ClientContext &context, const Identifier &name, OnEntryNotFound if_not_found, + bool allow_default_database) { + if (!allow_default_database && TryGetDefaultDatabase(context) == name) { throw BinderException("Cannot detach database %s because it is the default database. Select a different " "database using `USE` to allow detaching this database", name); @@ -273,10 +284,59 @@ void DatabaseManager::DetachDatabase(ClientContext &context, const Identifier &n return; } + auto deleter = attached_db->ExtractDeleter(); attached_db->OnDetach(context); // DetachInternal removes the AttachedDatabase from the list of databases that can be referenced. AttachedDatabase::InvokeCloseIfLastReference(attached_db, context); + + // Tear down the owned external resource last (runs SQL): the detach itself is complete either way, + // and a teardown failure surfaces with instructions to retry it manually. + if (deleter) { + deleter->Delete(); + } +} + +void DatabaseManager::AddPendingTeardown(unique_ptr deleter) { + lock_guard guard(pending_teardowns_lock); + pending_teardowns.push_back(std::move(deleter)); +} + +void DatabaseManager::DrainPendingTeardowns() { + vector> to_run; + { + lock_guard guard(pending_teardowns_lock); + to_run = std::move(pending_teardowns); + pending_teardowns.clear(); + } + // Best-effort: the statement that owned these resources is gone, so a failure can only be logged. + for (auto &deleter : to_run) { + deleter->TryDelete(); + } +} + +void DatabaseManager::DetachResourceBorrowers(ClientContext &context, const string &resource_name) { + vector to_detach; + { + lock_guard guard(databases_lock); + for (auto &entry : databases) { + // Both sides come from ColId -> Identifier::GetIdentifierName(), matching how the manager keys names. + if (entry.second->GetBorrowedResourceName() == resource_name) { + to_detach.push_back(entry.second->GetName()); + } + } + } + // Detach outside the lock: DetachDatabase takes databases_lock itself. A borrowed attachment binds no + // deleter, so this runs no teardown -- the resource is already gone. + for (auto &name : to_detach) { + try { + DetachDatabase(context, name, OnEntryNotFound::RETURN_NULL); + } catch (std::exception &) { + // Best-effort: DETACH refuses a connection's default database, and DESTROY goes through anyway. + // Deliberately not marked invalid -- ValidChecker would then throw for every statement + // resolving through it, including the USE and DETACH needed to recover. + } + } } void DatabaseManager::Alter(ClientContext &context, AlterInfo &info) { @@ -320,6 +380,9 @@ void DatabaseManager::RenameDatabase(ClientContext &context, const Identifier &o databases.erase(old_entry); attached_db->SetName(new_name); databases[new_name] = attached_db; + if (default_database == old_name) { + default_database = new_name; + } } } @@ -333,6 +396,21 @@ shared_ptr DatabaseManager::DetachInternal(const Identifier &n } attached_db = std::move(entry->second); databases.erase(entry); + if (default_database == name) { + // Fall back to the oldest remaining database (OIDs are assigned in attach order) + // Arguably, this should not be required - we should just leave the default database unset. + // The default used to be defined as the oldest attached database, so detaching the current default changed + // the new default to "unset". Keep the previous observable behavior rather than leave new connections + // without a default db, since SQL has no way to set the default database at the instance level. + default_database = Identifier(); + idx_t oldest_oid = DConstants::INVALID_INDEX; + for (auto &other : databases) { + if (other.second->oid < oldest_oid) { + oldest_oid = other.second->oid; + default_database = other.first; + } + } + } } if (attached_db && attached_db->GetCatalog().Supports(RemoteCapability::IS_REMOTE)) { --remote_catalog_count; @@ -407,28 +485,58 @@ void DatabaseManager::GetDatabaseType(ClientContext &context, AttachInfo &info, } Identifier DatabaseManager::GetDefaultDatabase(ClientContext &context) { - auto &config = ClientData::Get(context); - auto &default_entry = config.catalog_search_path->GetDefault(); - if (IsInvalidCatalog(default_entry.GetCatalog())) { - auto &manager = DatabaseManager::Get(context); - lock_guard guard(manager.databases_lock); - if (manager.databases.empty()) { - auto modified_database = MetaTransaction::Get(context).ModifiedDatabase(); - if (modified_database) { - return modified_database->GetName(); - } - throw InternalException("Calling DatabaseManager::GetDefaultDatabase with no database attached"); + auto result = TryGetDefaultDatabase(context); + if (!IsInvalidCatalog(result)) { + return result; + } + auto &connected_with = ClientData::Get(context).default_database; + if (!IsInvalidCatalog(connected_with)) { + throw CatalogException("The default database %s has been detached: select another database using `USE`", + connected_with); + } + // Reachable on an instance created without a database (DuckDB::CreateEmpty). + throw CatalogException("No database is attached: attach one with ATTACH before referring to a database"); +} + +Identifier DatabaseManager::TryGetDefaultDatabase(ClientContext &context) { + auto &client_data = ClientData::Get(context); + auto &default_entry = client_data.catalog_search_path->GetDefault(); + if (!IsInvalidCatalog(default_entry.GetCatalog())) { + return default_entry.GetCatalog(); + } + auto &manager = DatabaseManager::Get(context); + // Callable outside a transaction: only consult the transaction's references when one is active. + const bool in_transaction = context.transaction.HasActiveTransaction(); + if (!IsInvalidCatalog(client_data.default_database)) { + // still the default while attached, or while this transaction still references it + if (manager.GetDatabase(client_data.default_database)) { + return client_data.default_database; } - // OIDs are assigned in attach order, so the oldest attached database is the default. - auto default_database = manager.databases.begin(); - for (auto entry = manager.databases.begin(); entry != manager.databases.end(); entry++) { - if (entry->second->oid < default_database->second->oid) { - default_database = entry; - } + if (in_transaction && MetaTransaction::Get(context).GetReferencedDatabase(client_data.default_database)) { + return client_data.default_database; } - return default_database->first; + return Identifier(); + } + if (in_transaction && !manager.HasAttachedDatabase()) { + auto modified_database = MetaTransaction::Get(context).ModifiedDatabase(); + if (modified_database) { + return modified_database->GetName(); + } + } + return Identifier(); +} + +Identifier DatabaseManager::GetDefaultDatabase() { + lock_guard guard(databases_lock); + return default_database; +} + +void DatabaseManager::SetDefaultDatabase(const Identifier &name) { + lock_guard guard(databases_lock); + if (!IsInvalidCatalog(name) && databases.find(name) == databases.end()) { + throw BinderException("Cannot make database %s the default database: it is not attached", name); } - return default_entry.GetCatalog(); + default_database = name; } vector> DatabaseManager::GetDatabases(ClientContext &context, diff --git a/src/duckdb/src/main/db_instance_cache.cpp b/src/duckdb/src/main/db_instance_cache.cpp index c774ba86e..c1920a30e 100644 --- a/src/duckdb/src/main/db_instance_cache.cpp +++ b/src/duckdb/src/main/db_instance_cache.cpp @@ -149,6 +149,11 @@ shared_ptr DBInstanceCache::CreateInstance(const string &database, DBCon return CreateInstanceInternal(database, config, cache_instance, unique_lock(cache_lock), on_create); } +shared_ptr DBInstanceCache::CreateEmptyInstance(DBConfig &config) { + config.path_manager = path_manager; + return DuckDB::CreateEmpty(&config); +} + shared_ptr DBInstanceCache::GetOrCreateInstance(const string &database, DBConfig &config_dict, bool cache_instance, const std::function &on_create) { diff --git a/src/duckdb/src/main/extension.cpp b/src/duckdb/src/main/extension.cpp index 30cdd6188..ece345775 100644 --- a/src/duckdb/src/main/extension.cpp +++ b/src/duckdb/src/main/extension.cpp @@ -1,3 +1,7 @@ +// The engine implements the whole V2 C API, including the unstable surface +#ifndef DUCKDB_V2_API_ALLOW_UNSTABLE +#define DUCKDB_V2_API_ALLOW_UNSTABLE 1 +#endif #include "duckdb/main/extension.hpp" #include "duckdb/common/operator/cast_operators.hpp" diff --git a/src/duckdb/src/main/extension/extension_helper.cpp b/src/duckdb/src/main/extension/extension_helper.cpp index 67db84fc3..2835029e8 100644 --- a/src/duckdb/src/main/extension/extension_helper.cpp +++ b/src/duckdb/src/main/extension/extension_helper.cpp @@ -1,5 +1,7 @@ #include "duckdb/main/extension_helper.hpp" #include "duckdb/common/multi_file/multi_file_list.hpp" +#include "duckdb/main/extension/external_extension_provider.hpp" +#include "duckdb/main/extension/linked_extension_registry.hpp" #include "duckdb/common/file_system.hpp" #include "duckdb/common/local_file_system.hpp" @@ -16,89 +18,30 @@ #include "duckdb/main/extension_repository_manager.hpp" #include "duckdb/main/settings.hpp" -// Note that c++ preprocessor doesn't have a nice way to clean this up so we need to set the defines we use to false -// explicitly when they are undefined -#ifndef DUCKDB_EXTENSION_CORE_FUNCTIONS_LINKED -#define DUCKDB_EXTENSION_CORE_FUNCTIONS_LINKED false -#endif - -#ifndef DUCKDB_EXTENSION_ICU_LINKED -#define DUCKDB_EXTENSION_ICU_LINKED false -#endif - -#ifndef DUCKDB_EXTENSION_EXCEL_LINKED -#define DUCKDB_EXTENSION_EXCEL_LINKED false -#endif - -#ifndef DUCKDB_EXTENSION_PARQUET_LINKED -#define DUCKDB_EXTENSION_PARQUET_LINKED false -#endif - -#ifndef DUCKDB_EXTENSION_TPCH_LINKED -#define DUCKDB_EXTENSION_TPCH_LINKED false -#endif - -#ifndef DUCKDB_EXTENSION_TPCDS_LINKED -#define DUCKDB_EXTENSION_TPCDS_LINKED false -#endif - -#ifndef DUCKDB_EXTENSION_HTTPFS_LINKED -#define DUCKDB_EXTENSION_HTTPFS_LINKED false -#endif - -#ifndef DUCKDB_EXTENSION_JSON_LINKED -#define DUCKDB_EXTENSION_JSON_LINKED false -#endif - -#ifndef DUCKDB_EXTENSION_AUTOCOMPLETE_LINKED -#define DUCKDB_EXTENSION_AUTOCOMPLETE_LINKED false -#endif - -// Load the generated header file containing our list of extension headers -#if defined(GENERATED_EXTENSION_HEADERS) && GENERATED_EXTENSION_HEADERS -#include "duckdb/main/extension/generated_extension_loader.hpp" -#else -// TODO: rewrite package_build.py to allow also loading out-of-tree extensions in non-cmake builds, after that -// these can be removed -#if DUCKDB_EXTENSION_CORE_FUNCTIONS_LINKED -#include "core_functions_extension.hpp" -#endif - -#if DUCKDB_EXTENSION_ICU_LINKED -#include "icu_extension.hpp" -#endif - -#if DUCKDB_EXTENSION_PARQUET_LINKED -#include "parquet_extension.hpp" -#endif - -#if DUCKDB_EXTENSION_TPCH_LINKED -#include "tpch_extension.hpp" -#endif - -#if DUCKDB_EXTENSION_TPCDS_LINKED -#include "tpcds_extension.hpp" -#endif - -#if DUCKDB_EXTENSION_JSON_LINKED -#include "json_extension.hpp" -#endif - -#if DUCKDB_EXTENSION_AUTOCOMPLETE_LINKED -#include "autocomplete_extension.hpp" -#endif - -#endif - namespace duckdb { +void ExtensionHelper::RegisterLinkedExtensions(DBConfig &config) { + for (auto &linked : LinkedExtensionRegistry::Get()) { + bool present = false; + for (auto &existing : config.linked_extensions) { + if (StringUtil::CIEquals(existing.name, linked.name)) { + present = true; + break; + } + } + if (!present) { + config.linked_extensions.push_back(linked); + } + } +} + //! Loads an extension that was linked into the binary, via the registry the config carries. This is //! deliberately not generated code: an extension with its own copy of DuckDB links no generated //! loader, so a compile-time list would be invisible to it, while the config is data it can read. ExtensionLoadResult ExtensionHelper::LoadExtension(DuckDB &db, const std::string &extension) { auto &config = DBConfig::GetConfig(*db.instance); for (auto &linked : config.linked_extensions) { - if (StringUtil::CIEquals(linked.name, extension)) { + if (linked.load && StringUtil::CIEquals(linked.name, extension)) { linked.load(db); return ExtensionLoadResult::LOADED_EXTENSION; } @@ -111,7 +54,9 @@ void ExtensionHelper::LoadAllExtensions(DuckDB &db) { auto &config = DBConfig::GetConfig(*db.instance); auto linked = config.linked_extensions; for (auto &entry : linked) { - entry.load(db); + if (entry.load) { + entry.load(db); + } } } @@ -119,37 +64,37 @@ void ExtensionHelper::LoadAllExtensions(DuckDB &db) { // Default Extensions //===--------------------------------------------------------------------===// static const DefaultExtension internal_extensions[] = { - {"core_functions", "Core function library", DUCKDB_EXTENSION_CORE_FUNCTIONS_LINKED}, - {"icu", "Adds support for time zones and collations using the ICU library", DUCKDB_EXTENSION_ICU_LINKED}, - {"excel", "Adds support for Excel-like format strings", DUCKDB_EXTENSION_EXCEL_LINKED}, - {"parquet", "Adds support for reading and writing parquet files", DUCKDB_EXTENSION_PARQUET_LINKED}, - {"tpch", "Adds TPC-H data generation and query support", DUCKDB_EXTENSION_TPCH_LINKED}, - {"tpcds", "Adds TPC-DS data generation and query support", DUCKDB_EXTENSION_TPCDS_LINKED}, - {"httpfs", "Adds support for reading and writing files over a HTTP(S) connection", DUCKDB_EXTENSION_HTTPFS_LINKED}, - {"json", "Adds support for JSON operations", DUCKDB_EXTENSION_JSON_LINKED}, - {"autocomplete", "Adds support for autocomplete in the shell", DUCKDB_EXTENSION_AUTOCOMPLETE_LINKED}, - {"motherduck", "Enables motherduck integration with the system", false}, - {"mysql_scanner", "Adds support for connecting to a MySQL database", false}, - {"odbc_scanner", "Adds support for connecting to remote databases over ODBC", false}, - {"sqlite_scanner", "Adds support for reading and writing SQLite database files", false}, - {"postgres_scanner", "Adds support for connecting to a Postgres database", false}, - {"inet", "Adds support for IP-related data types and functions", false}, - {"spatial", "Geospatial extension that adds support for working with spatial data and functions", false}, - {"aws", "Provides features that depend on the AWS SDK", false}, - {"azure", "Adds a filesystem abstraction for Azure blob storage to DuckDB", false}, - {"encodings", "All unicode encodings to UTF-8", false}, - {"iceberg", "Adds support for Apache Iceberg", false}, - {"vss", "Adds indexing support to accelerate Vector Similarity Search", false}, - {"delta", "Adds support for Delta Lake", false}, - {"fts", "Adds support for Full-Text Search Indexes", false}, - {"ui", "Adds local UI for DuckDB", false}, - {"ducklake", "Adds support for DuckLake, SQL as a Lakehouse Format", false}, - {"quack", "The DuckDB 'Quack' Client/Server Protocol", false}, - {"vortex", "Adds support for reading and writing files using the Vortex file format", false}, - {"lance", "Adds support for querying Lance datasets", false}, - {"avro", "Adds support for reading Avro files", false}, - {"unity_catalog", "Adds support for connecting to Unity Catalog", false}, - {nullptr, nullptr, false}}; + {"core_functions", "Core function library"}, + {"icu", "Adds support for time zones and collations using the ICU library"}, + {"excel", "Adds support for Excel-like format strings"}, + {"parquet", "Adds support for reading and writing parquet files"}, + {"tpch", "Adds TPC-H data generation and query support"}, + {"tpcds", "Adds TPC-DS data generation and query support"}, + {"httpfs", "Adds support for reading and writing files over a HTTP(S) connection"}, + {"json", "Adds support for JSON operations"}, + {"autocomplete", "Adds support for autocomplete in the shell"}, + {"motherduck", "Enables motherduck integration with the system"}, + {"mysql_scanner", "Adds support for connecting to a MySQL database"}, + {"odbc_scanner", "Adds support for connecting to remote databases over ODBC"}, + {"sqlite_scanner", "Adds support for reading and writing SQLite database files"}, + {"postgres_scanner", "Adds support for connecting to a Postgres database"}, + {"inet", "Adds support for IP-related data types and functions"}, + {"spatial", "Geospatial extension that adds support for working with spatial data and functions"}, + {"aws", "Provides features that depend on the AWS SDK"}, + {"azure", "Adds a filesystem abstraction for Azure blob storage to DuckDB"}, + {"encodings", "All unicode encodings to UTF-8"}, + {"iceberg", "Adds support for Apache Iceberg"}, + {"vss", "Adds indexing support to accelerate Vector Similarity Search"}, + {"delta", "Adds support for Delta Lake"}, + {"fts", "Adds support for Full-Text Search Indexes"}, + {"ui", "Adds local UI for DuckDB"}, + {"ducklake", "Adds support for DuckLake, SQL as a Lakehouse Format"}, + {"quack", "The DuckDB 'Quack' Client/Server Protocol"}, + {"vortex", "Adds support for reading and writing files using the Vortex file format"}, + {"lance", "Adds support for querying Lance datasets"}, + {"avro", "Adds support for reading Avro files"}, + {"unity_catalog", "Adds support for connecting to Unity Catalog"}, + {nullptr, nullptr}}; idx_t ExtensionHelper::DefaultExtensionCount() { idx_t index; @@ -182,10 +127,10 @@ bool ExtensionHelper::AllowAutoInstall(const string &extension) { return false; } -bool ExtensionHelper::CanAutoloadExtension(const string &ext_name) { -#ifdef DUCKDB_DISABLE_EXTENSION_LOAD - return false; -#endif +bool ExtensionHelper::CanAutoloadExtension(DatabaseInstance &db, const string &ext_name) { + if (!DBConfig::GetConfig(db).GetExternalExtensionProvider().SupportsExternalExtensions()) { + return false; + } if (ext_name.empty()) { return false; @@ -206,7 +151,7 @@ string ExtensionHelper::AddExtensionInstallHintToErrorMsg(DatabaseInstance &db, const string &extension_name) { string install_hint; - if (!ExtensionHelper::CanAutoloadExtension(extension_name)) { + if (!ExtensionHelper::CanAutoloadExtension(db, extension_name)) { install_hint = "Please try installing and loading the " + extension_name + " extension:\nINSTALL " + extension_name + ";\nLOAD " + extension_name + ";\n\n"; } else if (!Settings::Get(db)) { @@ -241,8 +186,7 @@ bool ExtensionHelper::TryAutoLoadExtension(ClientContext &context, const string } try { if (Settings::Get(context)) { - auto autoinstall_repo_setting = Settings::Get(context); - auto autoinstall_repo = ExtensionRepository::GetRepositoryByUrl(autoinstall_repo_setting); + auto autoinstall_repo = GetAutoinstallRepository(*context.db); ExtensionInstallOptions options; options.repository = autoinstall_repo; ExtensionHelper::InstallExtension(context, extension_name, options); @@ -254,24 +198,26 @@ bool ExtensionHelper::TryAutoLoadExtension(ClientContext &context, const string } } -static string GetAutoInstallExtensionsRepository(const DBConfig &config) { +ExtensionRepository ExtensionHelper::GetAutoinstallRepository(DatabaseInstance &db) { + auto &config = DBConfig::GetConfig(db); string repository_url = Settings::Get(config); if (repository_url.empty()) { repository_url = Settings::Get(config); } - return repository_url; + if (repository_url.empty()) { + repository_url = config.options.default_autoinstall_repository; + } + return ExtensionRepository::GetRepositoryByUrl(repository_url); } bool ExtensionHelper::TryAutoLoadExtension(DatabaseInstance &instance, const string &extension_name) noexcept { if (instance.ExtensionIsLoaded(extension_name)) { return true; } - auto &dbconfig = DBConfig::GetConfig(instance); try { auto &fs = FileSystem::GetFileSystem(instance); if (Settings::Get(instance)) { - auto repository_url = GetAutoInstallExtensionsRepository(dbconfig); - auto autoinstall_repo = ExtensionRepository::GetRepositoryByUrl(repository_url); + auto autoinstall_repo = GetAutoinstallRepository(instance); ExtensionInstallOptions options; options.repository = autoinstall_repo; ExtensionHelper::InstallExtension(instance, fs, extension_name, options); @@ -291,6 +237,9 @@ bool ExtensionHelper::TryAutoLoadAvailableExtension(DatabaseInstance &instance, return true; } try { + if (!Settings::Get(instance)) { + return false; + } auto &fs = FileSystem::GetFileSystem(instance); ExtensionHelper::LoadExternalExtension(instance, fs, AutoLoadOptions(extension_name)); return true; @@ -434,13 +383,11 @@ void ExtensionHelper::AutoLoadExtension(DatabaseInstance &db, const string &exte // Avoid downloading again return; } - auto &dbconfig = DBConfig::GetConfig(db); try { auto &fs = FileSystem::GetLocal(db); #ifndef DUCKDB_WASM if (Settings::Get(db)) { - auto repository_url = GetAutoInstallExtensionsRepository(dbconfig); - auto autoinstall_repo = ExtensionRepository::GetRepositoryByUrl(repository_url); + auto autoinstall_repo = GetAutoinstallRepository(db); ExtensionInstallOptions options; options.repository = autoinstall_repo; ExtensionHelper::InstallExtension(db, fs, extension_name, options); @@ -454,6 +401,18 @@ void ExtensionHelper::AutoLoadExtension(DatabaseInstance &db, const string &exte } } +void ExtensionHelper::AutoLoadExtensionForPath(DatabaseInstance &db, const string &path, const string &path_kind) { + string extension_name; + if (!FileSystem::IsRemoteFile(path, extension_name) || db.ExtensionIsLoaded(extension_name)) { + return; + } + if (!CanAutoloadExtension(db, extension_name) || !Settings::Get(db)) { + auto error_message = path_kind + " " + path + " requires the extension " + extension_name + " to be loaded"; + throw MissingExtensionException(AddExtensionInstallHintToErrorMsg(db, error_message, extension_name)); + } + AutoLoadExtension(db, extension_name); +} + // typos:off static const char *const public_keys[] = { R"( diff --git a/src/duckdb/src/main/extension/extension_install.cpp b/src/duckdb/src/main/extension/extension_install.cpp index 05dce47d5..e07474b21 100644 --- a/src/duckdb/src/main/extension/extension_install.cpp +++ b/src/duckdb/src/main/extension/extension_install.cpp @@ -9,6 +9,7 @@ #include "duckdb/common/string_util.hpp" #include "duckdb/common/types/uuid.hpp" #include "duckdb/main/client_data.hpp" +#include "duckdb/main/extension/external_extension_provider.hpp" #include "duckdb/main/extension_helper.hpp" #include "duckdb/main/extension_install_info.hpp" #include "duckdb/main/extension_repository_manager.hpp" @@ -214,53 +215,6 @@ unique_ptr ExtensionHelper::InstallExtension(ClientContext return InstallExtensionInternal(db, fs, local_path, extension, options, context); } -static unsafe_unique_array ReadExtensionFileFromDisk(FileSystem &fs, const string &path, idx_t &file_size) { - auto source_file = fs.OpenFile(path, FileFlags::FILE_FLAGS_READ); - file_size = source_file->GetFileSize(); - auto in_buffer = make_unsafe_uniq_array(file_size); - source_file->Read(QueryContext(), in_buffer.get(), file_size); - source_file->Close(); - return in_buffer; -} - -static void WriteExtensionFileToDisk(QueryContext &query_context, FileSystem &fs, const string &path, void *data, - idx_t data_size, DatabaseInstance &db, ExtensionInstallInfo &info) { - if (!Settings::Get(db)) { - string signature_key_fingerprint; - const bool signature_valid = ExtensionHelper::CheckExtensionBufferSignature( - db, static_cast(data), data_size, info.repository_type, info.repository_name, - &signature_key_fingerprint); - if (!signature_valid) { - throw IOException("Attempting to install an extension file that doesn't have a valid signature, see " - "https://duckdb.org/docs/current/operations_manual/securing_duckdb/securing_extensions"); - } - // record which trusted key signed the extension, so it can be surfaced through duckdb_extensions() - info.signature_key_fingerprint = signature_key_fingerprint; - } - - // Now signature has been checked (if signature checking is enabled) - - // Open target_file, at this points ending with '.duckdb_extension' - auto target_file = - fs.OpenFile(path, FileFlags::FILE_FLAGS_WRITE | FileFlags::FILE_FLAGS_READ | FileFlags::FILE_FLAGS_APPEND | - FileFlags::FILE_FLAGS_FILE_CREATE_NEW | FileFlags::FILE_FLAGS_ENABLE_EXTENSION_INSTALL); - // Write content to the file - target_file->Write(query_context, data, data_size); - - target_file->Close(); - target_file.reset(); -} - -static void WriteExtensionMetadataFileToDisk(FileSystem &fs, const string &path, ExtensionInstallInfo &metadata) { - // the metadata file records which repository the extension came from, and thereby which keys may have signed it, - // so it is part of the extension trust domain and reserved from other writers - auto file_writer = BufferedFileWriter(fs, path, - FileFlags::FILE_FLAGS_WRITE | FileFlags::FILE_FLAGS_FILE_CREATE_NEW | - FileFlags::FILE_FLAGS_ENABLE_EXTENSION_INSTALL); - BinarySerializer::Serialize(metadata, file_writer); - file_writer.Sync(); -} - string ExtensionHelper::ExtensionUrlTemplate(optional_ptr db, const ExtensionRepository &repository, const string &version) { string versioned_path; @@ -285,375 +239,13 @@ string ExtensionHelper::ExtensionFinalizeUrlTemplate(const string &url_template, return url; } -static void CheckExtensionMetadataOnInstall(DatabaseInstance &db, void *in_buffer, idx_t file_size, - ExtensionInstallInfo &info, const string &extension_name) { - if (file_size < ParsedExtensionMetaData::FOOTER_SIZE) { - throw IOException("Failed to install '%s', file too small to be a valid DuckDB extension!", extension_name); - } - - auto parsed_metadata = ExtensionHelper::ParseExtensionMetaData(static_cast(in_buffer) + - (file_size - ParsedExtensionMetaData::FOOTER_SIZE)); - - auto metadata_mismatch_error = parsed_metadata.GetInvalidMetadataError(); - - if (!metadata_mismatch_error.empty() && !Settings::Get(db)) { - throw IOException("Failed to install '%s'\n%s", extension_name, metadata_mismatch_error); - } - - info.version = parsed_metadata.extension_version; -} - -// Note: since this method is not atomic, this can fail in different ways, that should all be handled properly by -// DuckDB: -// 1. Crash after extension removal: extension is now uninstalled, metadata file still present -// 2. Crash after metadata removal: extension is now uninstalled, extension dir is clean -// 3. Crash after extension move: extension is now uninstalled, new metadata file present -static void WriteExtensionFiles(QueryContext &query_context, FileSystem &fs, const string &temp_path, - const string &local_extension_path, void *in_buffer, idx_t file_size, - ExtensionInstallInfo &info, DatabaseInstance &db) { - // temp_path ends with '.duckdb_extension' - if (!StringUtil::EndsWith(temp_path, ".duckdb_extension")) { - throw InternalException("Extension install temp_path of '%s' is not valid, should end in '.duckdb_extension'", - temp_path); - } - // local_extension_path ends with '.duckdb_extension', and given it will be written only after signature checks, - // it's now loadable - if (!StringUtil::EndsWith(local_extension_path, ".duckdb_extension")) { - throw InternalException("Extension install local_extension_path of '%s' is not valid, should end in " - "'.duckdb_extension'", - temp_path); - } - - // Write extension to tmp file - the repository the extension comes from determines which keys are trusted to - // sign it. This also records the fingerprint of the key that signed the extension in the install info - WriteExtensionFileToDisk(query_context, fs, temp_path, in_buffer, file_size, db, info); - // When this exit, signature has already being checked (if enabled by config) - - // Write metadata to tmp file - auto metadata_tmp_path = temp_path + ".info"; - auto metadata_file_path = local_extension_path + ".info"; - WriteExtensionMetadataFileToDisk(fs, metadata_tmp_path, info); - - fs.MoveFile(metadata_tmp_path, metadata_file_path); - fs.MoveFile(temp_path, local_extension_path); -} - -// Install an extension using a filesystem -static unique_ptr DirectInstallExtension(DatabaseInstance &db, FileSystem &fs, const string &path, - const string &temp_path, const string &extension_name, - const string &local_extension_path, - ExtensionInstallOptions &options, - optional_ptr context) { - string extension; - string file; - if (fs.IsRemoteFile(path, extension)) { - file = path; - // Try autoloading httpfs for loading extensions over https - if (context) { - auto &db = DatabaseInstance::GetDatabase(*context); - if (extension == "httpfs" && !db.ExtensionIsLoaded("httpfs") && - Settings::Get(*context)) { - ExtensionHelper::AutoLoadExtension(*context, "httpfs"); - } - } - } else { - file = fs.ConvertSeparators(path); - } - - // Check if file exists - bool exists = fs.FileExists(file); - - // Recheck without .gz - if (!exists && StringUtil::EndsWith(file, CompressionExtensionFromType(FileCompressionType::GZIP))) { - file = file.substr(0, file.size() - 3); - exists = fs.FileExists(file); - } - - // Throw error on failure - if (!exists) { - if (!fs.IsRemoteFile(file)) { - throw IOException("Failed to install local extension \"%s\", no access to the file at PATH \"%s\"\n", - extension_name, file); - } - if (StringUtil::StartsWith(file, "https://")) { - throw IOException("Failed to install remote extension \"%s\" from url \"%s\"", extension_name, file); - } - } - - idx_t file_size; - auto in_buffer = ReadExtensionFileFromDisk(fs, file, file_size); - - ExtensionInstallInfo info; - - string decompressed_data; - void *extension_decompressed; - idx_t extension_decompressed_size; - - if (GZipFileSystem::CheckIsZip(const_char_ptr_cast(in_buffer.get()), file_size)) { - decompressed_data = GZipFileSystem::UncompressGZIPString(const_char_ptr_cast(in_buffer.get()), file_size); - extension_decompressed = (void *)decompressed_data.data(); - extension_decompressed_size = decompressed_data.size(); - } else { - extension_decompressed = (void *)in_buffer.get(); - extension_decompressed_size = file_size; - } - - CheckExtensionMetadataOnInstall(db, extension_decompressed, extension_decompressed_size, info, extension_name); - - if (!options.repository) { - info.mode = ExtensionInstallMode::CUSTOM_PATH; - info.full_path = file; - } else { - info.mode = ExtensionInstallMode::REPOSITORY; - info.full_path = file; - info.repository_url = options.repository->path; - info.repository_type = options.repository->type; - info.repository_name = options.repository->name; - } - - QueryContext query_context(context); - WriteExtensionFiles(query_context, fs, temp_path, local_extension_path, extension_decompressed, - extension_decompressed_size, info, db); - - return make_uniq(info); -} - -#ifndef DUCKDB_DISABLE_EXTENSION_LOAD -static unique_ptr InstallFromHttpUrl(DatabaseInstance &db, const string &url, - const string &extension_name, const string &temp_path, - const string &local_extension_path, - ExtensionInstallOptions &options, - optional_ptr context) { - unique_ptr install_info; - { - auto &fs = FileSystem::GetLocal(db); - if (fs.FileExists(local_extension_path + ".info")) { - try { - install_info = - ExtensionInstallInfo::TryReadInfoFile(fs, local_extension_path + ".info", extension_name); - } catch (...) { - if (!options.force_install) { - // We are going to rewrite the file anyhow, so this is fine - throw; - } - } - } - } - - HTTPHeaders headers(db); - if (options.use_etags && install_info && !install_info->etag.empty()) { - headers.Insert("If-None-Match", StringUtil::Format("%s", install_info->etag)); - } - - auto &manager = db.config.GetHTTPTransportManager(); - auto session = context ? manager.CreateSession(*context, url) : manager.CreateSession(db, url); - auto ¶ms = session.Parameters(); - - // Unclear what's peculiar about extension install flow, but those two parameters are needed - // to avoid lengthy retry on 304 - params.follow_location = false; - params.keep_alive = false; - - GetRequestInfo get_request(url, headers, params, nullptr, nullptr); - get_request.try_request = true; - - auto response = session.Request(get_request); - if (!response->Success()) { - // if we should not retry or exceeded the number of retries - bubble up the error - string message; - ExtensionHelper::CreateSuggestions(extension_name, message); - - auto documentation_link = ExtensionHelper::ExtensionInstallDocumentationLink(extension_name); - if (!documentation_link.empty()) { - message += "\nFor more info, visit " + documentation_link; - } - if (response->HasRequestError()) { - // request error - this means something went wrong performing the request - throw IOException("Failed to download extension \"%s\" at URL \"%s\"\n%s (ERROR %s)", extension_name, url, - message, response->GetRequestError()); - } - // if this was not a request error this means the server responded - report the response status and response - throw HTTPException(*response, "Failed to download extension \"%s\" at URL \"%s\" (HTTP %n)\n%s", - extension_name, url, int(response->status), message); - } - if (response->status == HTTPStatusCode::NotModified_304 && install_info) { - return install_info; - } - - string decompressed_body; - void *extension_data; - idx_t extension_size; - - if (GZipFileSystem::CheckIsZip(response->body.data(), response->body.size())) { - decompressed_body = GZipFileSystem::UncompressGZIPString(response->body); - extension_data = (void *)decompressed_body.data(); - extension_size = decompressed_body.size(); - } else { - extension_data = (void *)response->body.data(); - extension_size = response->body.size(); - } - - ExtensionInstallInfo info; - CheckExtensionMetadataOnInstall(db, extension_data, extension_size, info, extension_name); - if (response->HasHeader("ETag")) { - info.etag = response->GetHeaderValue("ETag"); - } - - if (options.repository) { - info.mode = ExtensionInstallMode::REPOSITORY; - info.full_path = url; - info.repository_url = options.repository->path; - info.repository_type = options.repository->type; - info.repository_name = options.repository->name; - } else { - info.mode = ExtensionInstallMode::CUSTOM_PATH; - info.full_path = url; - } - - QueryContext query_context(context); - auto fs = FileSystem::CreateLocal(); - WriteExtensionFiles(query_context, *fs, temp_path, local_extension_path, extension_data, extension_size, info, db); - - return make_uniq(info); -} - -// Install an extension using a hand-rolled http request -static unique_ptr InstallFromRepository(DatabaseInstance &db, FileSystem &fs, const string &url, - const string &extension_name, const string &temp_path, - const string &local_extension_path, - ExtensionInstallOptions &options, - optional_ptr context) { - string url_template = ExtensionHelper::ExtensionUrlTemplate(db, *options.repository, options.version); - string generated_url = ExtensionHelper::ExtensionFinalizeUrlTemplate(url_template, extension_name); - - // Special handling for http repository: avoid using regular filesystem (note: the filesystem is not used here) - if (HTTPUtil::IsHTTPProtocol(options.repository->path)) { - if (db.ExtensionIsLoaded("httpfs")) { - HTTPUtil::BumpToSecureProtocol(generated_url); - } - return InstallFromHttpUrl(db, generated_url, extension_name, temp_path, local_extension_path, options, context); - } - - // Default case, let the FileSystem figure it out - return DirectInstallExtension(db, fs, generated_url, temp_path, extension_name, local_extension_path, options, - context); -} - -static void ThrowErrorOnMismatchingExtensionOrigin(FileSystem &fs, const string &local_extension_path, - const string &extension_name, const string &extension, - optional_ptr repository) { - auto install_info = ExtensionInstallInfo::TryReadInfoFile(fs, local_extension_path + ".info", extension_name); - - string format_string = "Installing extension '%s' failed. The extension is already installed " - "but the origin is different.\n" - "Currently installed extension is from %s '%s', while the extension to be " - "installed is from %s '%s'.\n" - "To solve this rerun this command with `FORCE INSTALL`"; - string repo = "repository"; - string custom_path = "custom_path"; - - if (install_info) { - if (install_info->mode == ExtensionInstallMode::REPOSITORY && repository && - install_info->repository_url != repository->path) { - throw InvalidInputException(format_string, extension_name, repo, install_info->repository_url, repo, - repository->path); - } - if (install_info->mode == ExtensionInstallMode::REPOSITORY && ExtensionHelper::IsFullPath(extension)) { - throw InvalidInputException(format_string, extension_name, repo, install_info->repository_url, custom_path, - extension); - } - } -} -#endif // DUCKDB_DISABLE_EXTENSION_LOAD - unique_ptr ExtensionHelper::InstallExtensionInternal(DatabaseInstance &db, FileSystem &fs, const string &local_path, const string &extension, ExtensionInstallOptions &options, optional_ptr context) { -#ifdef DUCKDB_DISABLE_EXTENSION_LOAD - throw PermissionException("Installing external extensions is disabled through a compile time flag"); -#else - - auto extension_name = ApplyExtensionAlias(fs.ExtractBaseName(extension)); - - if (ExtensionHelper::IsFullPath(extension) && options.repository) { - throw InvalidInputException("Cannot pass both a repository and a full path url"); - } - - // Resolve default repository if there is none set - ExtensionRepository resolved_repository; - if (!ExtensionHelper::IsFullPath(extension) && !options.repository) { - resolved_repository = ExtensionRepository::GetDefaultRepository(db.config); - options.repository = resolved_repository; - } - - // User-provided repositories install into a per-repository subfolder to avoid on-disk name collisions between - // repositories. Core and community extensions keep the flat top-level layout for backwards compatibility - string install_path = local_path; - if (options.repository && options.repository->type == ExtensionRepositoryType::USER_PROVIDED) { - // installing native code from a user-provided repository trusts that repository's signing keys, so it requires - // the same explicit opt-in as adding one. Without this, pointing extension_repository_directory at an - // attacker-controlled directory and installing from it would bypass the opt-in entirely - if (ExtensionRepositoryManager::GetAccess(db) != ExtensionRepositoryAccess::ALLOWED) { - throw PermissionException("Installing extensions from a user-provided repository requires " - "allow_extension_repositories='allowed'"); - } - install_path = fs.JoinPath(fs.JoinPath(local_path, "repositories"), options.repository->name); - FileSystem::GetLocal(db).CreateDirectoriesRecursive(install_path); - } - - string local_extension_path = fs.JoinPath(install_path, extension_name + ".duckdb_extension"); - string temp_path = - local_extension_path + ".tmp-" + UUID::ToString(UUID::GenerateRandomUUID()) + ".duckdb_extension"; - - if (fs.FileExists(local_extension_path) && !options.force_install) { - // File exists: throw error if origin mismatches - if (options.throw_on_origin_mismatch && !Settings::Get(db) && - fs.FileExists(local_extension_path + ".info")) { - ThrowErrorOnMismatchingExtensionOrigin(fs, local_extension_path, extension_name, extension, - options.repository); - } - - // File exists, but that's okay, install is now a NOP - return nullptr; - } - - fs.TryRemoveFile(temp_path); - - // Install extension from local, direct url - if (ExtensionHelper::IsFullPath(extension) && !FileSystem::IsRemoteFile(extension)) { - auto &local_fs = FileSystem::GetLocal(db); - return DirectInstallExtension(db, local_fs, extension, temp_path, extension, local_extension_path, options, - context); - } - - // Install extension from local url based on a repository (Note that this will install it as a local file) - if (options.repository && !FileSystem::IsRemoteFile(options.repository->path)) { - auto &local_fs = FileSystem::GetLocal(db); - return InstallFromRepository(db, local_fs, extension, extension_name, temp_path, local_extension_path, options, - context); - } - -#ifdef DISABLE_DUCKDB_REMOTE_INSTALL - throw BinderException("Remote extension installation is disabled through configuration"); -#else - - // Full path direct installation - if (IsFullPath(extension)) { - if (StringUtil::StartsWith(extension, "http://")) { - // HTTP takes separate path to avoid dependency on httpfs extension - return InstallFromHttpUrl(db, extension, extension_name, temp_path, local_extension_path, options, context); - } - - // Direct installation from local or remote path - return DirectInstallExtension(db, fs, extension, temp_path, extension, local_extension_path, options, context); - } - - // Repository installation - return InstallFromRepository(db, fs, extension, extension_name, temp_path, local_extension_path, options, context); -#endif -#endif + return DBConfig::GetConfig(db).GetExternalExtensionProvider().Install(db, fs, local_path, extension, options, + context); } } // namespace duckdb diff --git a/src/duckdb/src/main/extension/extension_install_dynamic.cpp b/src/duckdb/src/main/extension/extension_install_dynamic.cpp new file mode 100644 index 000000000..f5192d2b2 --- /dev/null +++ b/src/duckdb/src/main/extension/extension_install_dynamic.cpp @@ -0,0 +1,502 @@ +#include "duckdb/common/exception/http_exception.hpp" +#include "duckdb/common/multi_file/multi_file_list.hpp" +#include "duckdb/common/gzip_file_system.hpp" +#include "duckdb/main/http/http_transport_manager.hpp" +#include "duckdb/main/http/http_util.hpp" +#include "duckdb/common/local_file_system.hpp" +#include "duckdb/main/database_file_opener.hpp" +#include "duckdb/common/serializer/binary_serializer.hpp" +#include "duckdb/common/string_util.hpp" +#include "duckdb/common/types/uuid.hpp" +#include "duckdb/main/client_data.hpp" +#include "duckdb/main/extension_helper.hpp" +#include "duckdb/main/extension_install_info.hpp" +#include "duckdb/main/extension_repository_manager.hpp" +#include "duckdb/main/secret/secret.hpp" +#include "duckdb/main/secret/secret_manager.hpp" +#include "duckdb/main/settings.hpp" +#include "duckdb/common/windows_undefs.hpp" + +#include + +namespace duckdb { + +static unsafe_unique_array ReadExtensionFileFromDisk(FileSystem &fs, const string &path, idx_t &file_size) { + auto source_file = fs.OpenFile(path, FileFlags::FILE_FLAGS_READ); + file_size = source_file->GetFileSize(); + auto in_buffer = make_unsafe_uniq_array(file_size); + source_file->Read(QueryContext(), in_buffer.get(), file_size); + source_file->Close(); + return in_buffer; +} + +static void WriteExtensionFileToDisk(QueryContext &query_context, FileSystem &fs, const string &path, void *data, + idx_t data_size, DatabaseInstance &db, ExtensionInstallInfo &info) { + if (!Settings::Get(db)) { + string signature_key_fingerprint; + const bool signature_valid = ExtensionHelper::CheckExtensionBufferSignature( + db, static_cast(data), data_size, info.repository_type, info.repository_name, + &signature_key_fingerprint); + if (!signature_valid) { + throw IOException("Attempting to install an extension file that doesn't have a valid signature, see " + "https://duckdb.org/docs/current/operations_manual/securing_duckdb/securing_extensions"); + } + // record which trusted key signed the extension, so it can be surfaced through duckdb_extensions() + info.signature_key_fingerprint = signature_key_fingerprint; + } + + // Now signature has been checked (if signature checking is enabled) + + // Open target_file, at this points ending with '.duckdb_extension' + auto target_file = + fs.OpenFile(path, FileFlags::FILE_FLAGS_WRITE | FileFlags::FILE_FLAGS_READ | FileFlags::FILE_FLAGS_APPEND | + FileFlags::FILE_FLAGS_FILE_CREATE_NEW | FileFlags::FILE_FLAGS_ENABLE_EXTENSION_INSTALL); + // Write content to the file + target_file->Write(query_context, data, data_size); + + target_file->Close(); + target_file.reset(); +} + +static void WriteExtensionMetadataFileToDisk(FileSystem &fs, const string &path, ExtensionInstallInfo &metadata) { + // the metadata file records which repository the extension came from, and thereby which keys may have signed it, + // so it is part of the extension trust domain and reserved from other writers + auto file_writer = BufferedFileWriter(fs, path, + FileFlags::FILE_FLAGS_WRITE | FileFlags::FILE_FLAGS_FILE_CREATE_NEW | + FileFlags::FILE_FLAGS_ENABLE_EXTENSION_INSTALL); + BinarySerializer::Serialize(metadata, file_writer); + file_writer.Sync(); +} + +static void CheckExtensionMetadataOnInstall(DatabaseInstance &db, void *in_buffer, idx_t file_size, + ExtensionInstallInfo &info, const string &extension_name) { + if (file_size < ParsedExtensionMetaData::FOOTER_SIZE) { + throw IOException("Failed to install '%s', file too small to be a valid DuckDB extension!", extension_name); + } + + auto parsed_metadata = ExtensionHelper::ParseExtensionMetaData(static_cast(in_buffer) + + (file_size - ParsedExtensionMetaData::FOOTER_SIZE)); + + auto metadata_mismatch_error = parsed_metadata.GetInvalidMetadataError(); + + if (!metadata_mismatch_error.empty() && !Settings::Get(db)) { + throw IOException("Failed to install '%s'\n%s", extension_name, metadata_mismatch_error); + } + + info.version = parsed_metadata.extension_version; +} + +// Note: since this method is not atomic, this can fail in different ways, that should all be handled properly by +// DuckDB: +// 1. Crash after extension removal: extension is now uninstalled, metadata file still present +// 2. Crash after metadata removal: extension is now uninstalled, extension dir is clean +// 3. Crash after extension move: extension is now uninstalled, new metadata file present +static void WriteExtensionFiles(QueryContext &query_context, FileSystem &fs, const string &temp_path, + const string &local_extension_path, void *in_buffer, idx_t file_size, + ExtensionInstallInfo &info, DatabaseInstance &db) { + // temp_path ends with '.duckdb_extension' + if (!StringUtil::EndsWith(temp_path, ".duckdb_extension")) { + throw InternalException("Extension install temp_path of '%s' is not valid, should end in '.duckdb_extension'", + temp_path); + } + // local_extension_path ends with '.duckdb_extension', and given it will be written only after signature checks, + // it's now loadable + if (!StringUtil::EndsWith(local_extension_path, ".duckdb_extension")) { + throw InternalException("Extension install local_extension_path of '%s' is not valid, should end in " + "'.duckdb_extension'", + temp_path); + } + + // Write extension to tmp file - the repository the extension comes from determines which keys are trusted to + // sign it. This also records the fingerprint of the key that signed the extension in the install info + WriteExtensionFileToDisk(query_context, fs, temp_path, in_buffer, file_size, db, info); + // When this exit, signature has already being checked (if enabled by config) + + // Write metadata to tmp file + auto metadata_tmp_path = temp_path + ".info"; + auto metadata_file_path = local_extension_path + ".info"; + WriteExtensionMetadataFileToDisk(fs, metadata_tmp_path, info); + + fs.MoveFile(metadata_tmp_path, metadata_file_path); + fs.MoveFile(temp_path, local_extension_path); +} + +// Install an extension using a filesystem +static unique_ptr DirectInstallExtension(DatabaseInstance &db, FileSystem &fs, const string &path, + const string &temp_path, const string &extension_name, + const string &local_extension_path, + ExtensionInstallOptions &options, + optional_ptr context) { + string extension; + string file; + if (fs.IsRemoteFile(path, extension)) { + file = path; + // Try autoloading httpfs for loading extensions over https + if (context) { + auto &db = DatabaseInstance::GetDatabase(*context); + if (extension == "httpfs" && !db.ExtensionIsLoaded("httpfs") && + Settings::Get(*context)) { + ExtensionHelper::AutoLoadExtension(*context, "httpfs"); + } + } + } else { + file = fs.ConvertSeparators(path); + } + + // Check if file exists + bool exists = fs.FileExists(file); + + // Recheck without .gz + if (!exists && StringUtil::EndsWith(file, CompressionExtensionFromType(FileCompressionType::GZIP))) { + file = file.substr(0, file.size() - 3); + exists = fs.FileExists(file); + } + + // Throw error on failure + if (!exists) { + if (!fs.IsRemoteFile(file)) { + throw IOException("Failed to install local extension \"%s\", no access to the file at PATH \"%s\"\n", + extension_name, file); + } + if (StringUtil::StartsWith(file, "https://")) { + throw IOException("Failed to install remote extension \"%s\" from url \"%s\"", extension_name, file); + } + } + + idx_t file_size; + auto in_buffer = ReadExtensionFileFromDisk(fs, file, file_size); + + ExtensionInstallInfo info; + + string decompressed_data; + void *extension_decompressed; + idx_t extension_decompressed_size; + + if (GZipFileSystem::CheckIsZip(const_char_ptr_cast(in_buffer.get()), file_size)) { + decompressed_data = GZipFileSystem::UncompressGZIPString(const_char_ptr_cast(in_buffer.get()), file_size); + extension_decompressed = (void *)decompressed_data.data(); + extension_decompressed_size = decompressed_data.size(); + } else { + extension_decompressed = (void *)in_buffer.get(); + extension_decompressed_size = file_size; + } + + CheckExtensionMetadataOnInstall(db, extension_decompressed, extension_decompressed_size, info, extension_name); + + if (!options.repository) { + info.mode = ExtensionInstallMode::CUSTOM_PATH; + info.full_path = file; + } else { + info.mode = ExtensionInstallMode::REPOSITORY; + info.full_path = file; + info.repository_url = options.repository->path; + info.repository_type = options.repository->type; + info.repository_name = options.repository->name; + } + + QueryContext query_context(context); + WriteExtensionFiles(query_context, fs, temp_path, local_extension_path, extension_decompressed, + extension_decompressed_size, info, db); + + return make_uniq(info); +} + +//! Throws if the url of an extension download is not allowed by the file access settings +static void VerifyCanDownloadExtension(DatabaseInstance &db, const string &url) { + if (Settings::Get(db) || ExtensionRepository::IsOfficialRepositoryUrl(url)) { + return; + } + // urls are matched against allowed_directories by prefix without being normalized, so reject dot segments + // (also percent-encoded) that the server could resolve to a location outside of the allowed directory + auto lower_url = StringUtil::Lower(url); + for (auto &segment : StringUtil::Split(lower_url, '/')) { + if (segment == "." || segment == ".." || StringUtil::Contains(segment, "%2e")) { + throw PermissionException("Cannot download extension from \"%s\" - urls with dot segments are not " + "allowed when external access is disabled", + url); + } + } + if (db.config.CanAccessFile(url, FileType::FILE_TYPE_REGULAR)) { + return; + } + // with httpfs loaded http repositories are bumped to https: allow them if the http url is allowed + if (StringUtil::StartsWith(url, "https://") && + db.config.CanAccessFile("http://" + url.substr(8), FileType::FILE_TYPE_REGULAR)) { + return; + } + throw PermissionException("Cannot download extension from \"%s\" - external access is disabled by " + "configuration. Add the repository to allowed_directories to allow it", + url); +} + +//! Perform the GET request for an extension against a single url +static unique_ptr RequestExtension(DatabaseInstance &db, optional_ptr context, + const string &url, const HTTPHeaders &headers) { + // the official repositories are reachable even when external access is disabled, any other url must be allowed + // through allowed_directories or allowed_paths + VerifyCanDownloadExtension(db, url); + auto &manager = db.config.GetHTTPTransportManager(); + auto session = context ? manager.CreateSession(*context, url) : manager.CreateSession(db, url); + auto ¶ms = session.Parameters(); + + // Unclear what's peculiar about extension install flow, but those two parameters are needed + // to avoid lengthy retry on 304 + params.follow_location = false; + params.keep_alive = false; + // the url was checked above, which also allows the official repositories, so skip the central check + params.skip_external_access_check = true; + + GetRequestInfo get_request(url, headers, params, nullptr, nullptr); + get_request.try_request = true; + + return session.Request(get_request); +} + +//! Whether a failed response means the server itself is unavailable, as opposed to e.g. the extension not existing +static bool ServerUnavailable(const HTTPResponse &response) { + if (response.HasRequestError()) { + // the request never completed: DNS failure, connection refused, timeout, ... + return true; + } + return response.status >= HTTPStatusCode::InternalServerError_500; +} + +//! Install an extension from a http url. If a backup url is given it is tried when the server at url is unavailable +static unique_ptr +InstallFromHttpUrl(DatabaseInstance &db, const string &url, const string &extension_name, const string &temp_path, + const string &local_extension_path, ExtensionInstallOptions &options, + optional_ptr context, const string &backup_url = string()) { + unique_ptr install_info; + { + auto &fs = FileSystem::GetLocal(db); + if (fs.FileExists(local_extension_path + ".info")) { + try { + install_info = + ExtensionInstallInfo::TryReadInfoFile(fs, local_extension_path + ".info", extension_name); + } catch (...) { + if (!options.force_install) { + // We are going to rewrite the file anyhow, so this is fine + throw; + } + } + } + } + + HTTPHeaders headers(db); + if (options.use_etags && install_info && !install_info->etag.empty()) { + headers.Insert("If-None-Match", StringUtil::Format("%s", install_info->etag)); + } + + string download_url = url; + auto response = RequestExtension(db, context, url, headers); + if (!response->Success() && !backup_url.empty() && ServerUnavailable(*response)) { + // the server is unavailable - retry against the backup server. If that fails as well we report the error of + // the primary server, as that is the one the user asked for + auto backup_response = RequestExtension(db, context, backup_url, headers); + if (backup_response->Success()) { + download_url = backup_url; + response = std::move(backup_response); + } + } + if (!response->Success()) { + // if we should not retry or exceeded the number of retries - bubble up the error + string message; + ExtensionHelper::CreateSuggestions(extension_name, message); + + auto documentation_link = ExtensionHelper::ExtensionInstallDocumentationLink(extension_name); + if (!documentation_link.empty()) { + message += "\nFor more info, visit " + documentation_link; + } + if (response->HasRequestError()) { + // request error - this means something went wrong performing the request + throw IOException("Failed to download extension \"%s\" at URL \"%s\"\n%s (ERROR %s)", extension_name, url, + message, response->GetRequestError()); + } + // if this was not a request error this means the server responded - report the response status and response + throw HTTPException(*response, "Failed to download extension \"%s\" at URL \"%s\" (HTTP %n)\n%s", + extension_name, url, int(response->status), message); + } + if (response->status == HTTPStatusCode::NotModified_304 && install_info) { + return install_info; + } + + string decompressed_body; + void *extension_data; + idx_t extension_size; + + if (GZipFileSystem::CheckIsZip(response->body.data(), response->body.size())) { + decompressed_body = GZipFileSystem::UncompressGZIPString(response->body); + extension_data = (void *)decompressed_body.data(); + extension_size = decompressed_body.size(); + } else { + extension_data = (void *)response->body.data(); + extension_size = response->body.size(); + } + + ExtensionInstallInfo info; + CheckExtensionMetadataOnInstall(db, extension_data, extension_size, info, extension_name); + if (response->HasHeader("ETag")) { + info.etag = response->GetHeaderValue("ETag"); + } + + if (options.repository) { + info.mode = ExtensionInstallMode::REPOSITORY; + info.full_path = download_url; + info.repository_url = options.repository->path; + info.repository_type = options.repository->type; + info.repository_name = options.repository->name; + } else { + info.mode = ExtensionInstallMode::CUSTOM_PATH; + info.full_path = download_url; + } + + QueryContext query_context(context); + auto fs = FileSystem::CreateLocal(); + WriteExtensionFiles(query_context, *fs, temp_path, local_extension_path, extension_data, extension_size, info, db); + + return make_uniq(info); +} + +// Install an extension using a hand-rolled http request +static unique_ptr InstallFromRepository(DatabaseInstance &db, FileSystem &fs, const string &url, + const string &extension_name, const string &temp_path, + const string &local_extension_path, + ExtensionInstallOptions &options, + optional_ptr context) { + string url_template = ExtensionHelper::ExtensionUrlTemplate(db, *options.repository, options.version); + string generated_url = ExtensionHelper::ExtensionFinalizeUrlTemplate(url_template, extension_name); + + // Special handling for http repository: avoid using regular filesystem (note: the filesystem is not used here) + if (HTTPUtil::IsHTTPProtocol(options.repository->path)) { + // The core and community repositories have backup servers that serve the same binaries - fall back to those + // if the primary server is unavailable. Custom repositories have no backup, so there the error surfaces + // directly + string backup_url; + auto backup_repository_url = ExtensionRepository::TryGetBackupRepositoryUrl(options.repository->path); + if (!backup_repository_url.empty()) { + backup_url = StringUtil::Replace(generated_url, options.repository->path, backup_repository_url); + } + if (db.ExtensionIsLoaded("httpfs")) { + HTTPUtil::BumpToSecureProtocol(generated_url); + HTTPUtil::BumpToSecureProtocol(backup_url); + } + return InstallFromHttpUrl(db, generated_url, extension_name, temp_path, local_extension_path, options, context, + backup_url); + } + + // Default case, let the FileSystem figure it out + return DirectInstallExtension(db, fs, generated_url, temp_path, extension_name, local_extension_path, options, + context); +} + +static void ThrowErrorOnMismatchingExtensionOrigin(FileSystem &fs, const string &local_extension_path, + const string &extension_name, const string &extension, + optional_ptr repository) { + auto install_info = ExtensionInstallInfo::TryReadInfoFile(fs, local_extension_path + ".info", extension_name); + + string format_string = "Installing extension '%s' failed. The extension is already installed " + "but the origin is different.\n" + "Currently installed extension is from %s '%s', while the extension to be " + "installed is from %s '%s'.\n" + "To solve this rerun this command with `FORCE INSTALL`"; + string repo = "repository"; + string custom_path = "custom_path"; + + if (install_info) { + if (install_info->mode == ExtensionInstallMode::REPOSITORY && repository && + install_info->repository_url != repository->path) { + throw InvalidInputException(format_string, extension_name, repo, install_info->repository_url, repo, + repository->path); + } + if (install_info->mode == ExtensionInstallMode::REPOSITORY && ExtensionHelper::IsFullPath(extension)) { + throw InvalidInputException(format_string, extension_name, repo, install_info->repository_url, custom_path, + extension); + } + } +} + +unique_ptr InstallExternalExtension(DatabaseInstance &db, FileSystem &fs, + const string &local_path, const string &extension, + ExtensionInstallOptions &options, + optional_ptr context) { + auto extension_name = ExtensionHelper::ApplyExtensionAlias(fs.ExtractBaseName(extension)); + + if (ExtensionHelper::IsFullPath(extension) && options.repository) { + throw InvalidInputException("Cannot pass both a repository and a full path url"); + } + + // Resolve default repository if there is none set + ExtensionRepository resolved_repository; + if (!ExtensionHelper::IsFullPath(extension) && !options.repository) { + resolved_repository = ExtensionRepository::GetDefaultRepository(db.config); + options.repository = resolved_repository; + } + + // User-provided repositories install into a per-repository subfolder to avoid on-disk name collisions between + // repositories. Core and community extensions keep the flat top-level layout for backwards compatibility + string install_path = local_path; + if (options.repository && options.repository->type == ExtensionRepositoryType::USER_PROVIDED) { + // installing native code from a user-provided repository trusts that repository's signing keys, so it requires + // the same explicit opt-in as adding one. Without this, pointing extension_repository_directory at an + // attacker-controlled directory and installing from it would bypass the opt-in entirely + if (ExtensionRepositoryManager::GetAccess(db) != ExtensionRepositoryAccess::ALLOWED) { + throw PermissionException("Installing extensions from a user-provided repository requires " + "allow_extension_repositories='allowed'"); + } + install_path = fs.JoinPath(fs.JoinPath(local_path, "repositories"), options.repository->name); + FileSystem::GetLocal(db).CreateDirectoriesRecursive(install_path); + } + + string local_extension_path = fs.JoinPath(install_path, extension_name + ".duckdb_extension"); + string temp_path = + local_extension_path + ".tmp-" + UUID::ToString(UUID::GenerateRandomUUID()) + ".duckdb_extension"; + + if (fs.FileExists(local_extension_path) && !options.force_install) { + // File exists: throw error if origin mismatches + if (options.throw_on_origin_mismatch && !Settings::Get(db) && + fs.FileExists(local_extension_path + ".info")) { + ThrowErrorOnMismatchingExtensionOrigin(fs, local_extension_path, extension_name, extension, + options.repository); + } + + // File exists, but that's okay, install is now a NOP + return nullptr; + } + + fs.TryRemoveFile(temp_path); + + // Install extension from local, direct url + if (ExtensionHelper::IsFullPath(extension) && !FileSystem::IsRemoteFile(extension)) { + auto &local_fs = FileSystem::GetLocal(db); + return DirectInstallExtension(db, local_fs, extension, temp_path, extension, local_extension_path, options, + context); + } + + // Install extension from local url based on a repository (Note that this will install it as a local file) + if (options.repository && !FileSystem::IsRemoteFile(options.repository->path)) { + auto &local_fs = FileSystem::GetLocal(db); + return InstallFromRepository(db, local_fs, extension, extension_name, temp_path, local_extension_path, options, + context); + } + +#ifdef DISABLE_DUCKDB_REMOTE_INSTALL + throw BinderException("Remote extension installation is disabled through configuration"); +#else + + // Full path direct installation + if (ExtensionHelper::IsFullPath(extension)) { + if (StringUtil::StartsWith(extension, "http://")) { + // HTTP takes separate path to avoid dependency on httpfs extension + return InstallFromHttpUrl(db, extension, extension_name, temp_path, local_extension_path, options, context); + } + + // Direct installation from local or remote path + return DirectInstallExtension(db, fs, extension, temp_path, extension, local_extension_path, options, context); + } + + // Repository installation + return InstallFromRepository(db, fs, extension, extension_name, temp_path, local_extension_path, options, context); +#endif +} + +} // namespace duckdb diff --git a/src/duckdb/src/main/extension/extension_load.cpp b/src/duckdb/src/main/extension/extension_load.cpp index 14f9af038..e951fce32 100644 --- a/src/duckdb/src/main/extension/extension_load.cpp +++ b/src/duckdb/src/main/extension/extension_load.cpp @@ -1,5 +1,4 @@ #include "duckdb.h" -#include "duckdb/common/dl.hpp" #include "duckdb/common/operator/cast_operators.hpp" #include "duckdb/common/string_util.hpp" #include "duckdb/common/virtual_file_system.hpp" @@ -7,6 +6,8 @@ #include "duckdb/main/capi/extension_api.hpp" #include "duckdb/main/capi_v2/extension_load_v2.hpp" #include "duckdb/main/error_manager.hpp" +#include "duckdb/main/extension/external_extension_provider.hpp" +#include "duckdb/main/extension/linked_extension_registry.hpp" #include "duckdb/main/extension_helper.hpp" #include "duckdb/main/extension_manager.hpp" #include "duckdb/main/extension_repository_manager.hpp" @@ -17,10 +18,6 @@ #include #endif // DUCKDB_NO_THREADS -#ifdef WASM_LOADABLE_EXTENSIONS -#include -#endif - namespace duckdb { //===--------------------------------------------------------------------===// @@ -152,6 +149,44 @@ struct ExtensionAccess { //===--------------------------------------------------------------------===// // Static C API Extension Loading //===--------------------------------------------------------------------===// +void DuckDB::LoadStaticExtension(duckdb_extension_describe_t describe) { + StaticExtensionDescription description; + auto error = LinkedExtensionRegistry::Describe(describe, description); + if (!error.empty()) { + throw InvalidInputException("Failed to load statically linked extension: %s", error); + } + if (description.descriptor.database_callback) { + reinterpret_cast(description.descriptor.database_callback)(*instance); + return; + } + // C++ first, then C API v2, then C API v1 + auto &descriptor = description.descriptor; + if (!descriptor.entry_cpp) { + if (descriptor.entry_capi_v2) { + LoadStaticCAPIExtensionV2(description.name, + reinterpret_cast(descriptor.entry_capi_v2)); + } else { + LoadStaticCAPIExtension(description.name, reinterpret_cast(descriptor.entry_capi_v1)); + } + return; + } + auto &manager = ExtensionManager::Get(*instance); + auto load_info = manager.BeginLoad({description.name}); + if (!load_info) { + // already loaded + return; + } + + ExtensionLoader loader(*load_info); + (*reinterpret_cast(descriptor.entry_cpp))(loader); + loader.FinalizeLoad(); + + ExtensionInstallInfo install_info; + install_info.mode = ExtensionInstallMode::STATICALLY_LINKED; + install_info.version = description.version; + load_info->FinishLoad(install_info); +} + void DuckDB::LoadStaticCAPIExtension(const string &name, ext_init_c_api_fun_t init_fun) { auto &manager = ExtensionManager::Get(*instance); auto load_info = manager.BeginLoad({name}); @@ -219,31 +254,11 @@ void DuckDB::LoadStaticCAPIExtensionV2(const string &name, ext_init_c_api_v2_fun //===--------------------------------------------------------------------===// // Load External Extension //===--------------------------------------------------------------------===// -#ifndef DUCKDB_DISABLE_EXTENSION_LOAD // The C++ init function typedef void (*ext_init_fun_t)(ExtensionLoader &); // The C init function typedef bool (*ext_init_c_api_fun_t)(duckdb_extension_info info, duckdb_extension_access *access); -template -static T LoadFunctionFromDLL(void *dll, const string &function_name, const string &filename) { - auto function = dlsym(dll, function_name.c_str()); - if (!function) { - throw IOException("File \"%s\" did not contain function \"%s\": %s", filename, function_name, GetDLError()); - } - return (T)function; -} -#endif - -template -static T TryLoadFunctionFromDLL(void *dll, const string &function_name, const string &filename) { - auto function = dlsym(dll, function_name.c_str()); - if (!function) { - return nullptr; - } - return (T)function; -} - static void ComputeSHA256Buffer(const char *buffer, const idx_t start, const idx_t end, string *res) { // Invoke MbedTls function to actually compute sha256 char hash[duckdb_mbedtls::MbedTlsWrapper::SHA256_HASH_LENGTH_BYTES]; @@ -455,9 +470,11 @@ bool ExtensionHelper::CheckExtensionBufferSignature(DatabaseInstance &db, const bool ExtensionHelper::TryInitialLoad(DatabaseInstance &db, FileSystem &fs, const string &extension, const string &repository_name, bool core_only, ExtensionInitResult &result, string &error) { -#ifdef DUCKDB_DISABLE_EXTENSION_LOAD - throw PermissionException("Loading external extensions is disabled through a compile time flag"); -#else + auto &provider = db.config.GetExternalExtensionProvider(); + if (!provider.SupportsExternalExtensions()) { + throw PermissionException("Loading external extensions is not supported: this build does not link the " + "loadable_extensions library"); + } if (!Settings::Get(db)) { throw PermissionException("Loading external extensions is disabled through configuration"); } @@ -669,32 +686,7 @@ bool ExtensionHelper::TryInitialLoad(DatabaseInstance &db, FileSystem &fs, const } } -#ifdef WASM_LOADABLE_EXTENSIONS - EM_ASM( - { - // Next few lines should arguably in separate JavaScript-land function call - // TODO: move them out / have them configurable - const xhr = new XMLHttpRequest(); - xhr.open("GET", UTF8ToString($0), false); - xhr.responseType = "arraybuffer"; - xhr.send(null); - var uInt8Array = xhr.response; - WebAssembly.validate(uInt8Array); - console.log('Loading extension ', UTF8ToString($1)); - - // Here we add the uInt8Array to Emscripten's filesystem, for it to be found by dlopen - FS.writeFile(UTF8ToString($1), new Uint8Array(uInt8Array)); - }, - filename.c_str(), filebase.c_str()); - auto dopen_from = filebase; -#else - auto dopen_from = filename; -#endif - - auto lib_hdl = dlopen(dopen_from.c_str(), RTLD_NOW | RTLD_LOCAL); - if (!lib_hdl) { - throw IOException("Extension \"%s\" could not be loaded: %s", filename, GetDLError()); - } + auto lib_hdl = provider.OpenLibrary(filename, filebase); // Initialize the ExtensionInitResult result.filebase = lowercase_extension_name; @@ -724,7 +716,6 @@ bool ExtensionHelper::TryInitialLoad(DatabaseInstance &db, FileSystem &fs, const } return true; -#endif } ExtensionInitResult ExtensionHelper::InitialLoad(DatabaseInstance &db, FileSystem &fs, const string &extension, @@ -738,11 +729,12 @@ ExtensionInitResult ExtensionHelper::InitialLoad(DatabaseInstance &db, FileSyste // the extension load failed - try installing the extension, from the requested repository if one was given ExtensionInstallOptions options; ExtensionRepository repository; - if (!repository_name.empty() && - (ExtensionRepositoryManager::TryGetRepository(db, fs, repository_name, repository) || - ExtensionRepository::TryGetKnownRepository(repository_name, repository))) { - options.repository = repository; + if (repository_name.empty() || + !(ExtensionRepositoryManager::TryGetRepository(db, fs, repository_name, repository) || + ExtensionRepository::TryGetKnownRepository(repository_name, repository))) { + repository = GetAutoinstallRepository(db); } + options.repository = repository; ExtensionHelper::InstallExtension(db, fs, extension, options); // try loading again if (!TryInitialLoad(db, fs, extension, repository_name, core_only, result, error)) { @@ -777,8 +769,7 @@ string ExtensionHelper::GetExtensionName(const string &original_name) { void ExtensionHelper::LoadExternalExtension(DatabaseInstance &db, FileSystem &fs, const ExtensionLoadOptions &options, optional_ptr context) { // Loading a second copy of an extension that is already linked into this binary is an ODR - // violation. The default extension table cannot detect that for out-of-tree extensions, which - // are never marked statically_loaded, so ask the CMake-generated loader instead. + // violation, so ask the registry of extensions linked into this binary first. // Statically linked extensions are inherently core-trusted, so only take this shortcut for a bare // load or an explicit core namespace - never let community/x or myrepo/x resolve to a linked core extension. bool allow_static_shortcut = options.repository.empty() || StringUtil::Lower(options.repository) == "core"; @@ -808,16 +799,13 @@ void ExtensionHelper::LoadExternalExtension(DatabaseInstance &db, FileSystem &fs void ExtensionHelper::LoadExternalExtensionInternal(DatabaseInstance &db, FileSystem &fs, const string &extension, const string &repository_name, bool core_only, ExtensionActiveLoad &info, optional_ptr context) { -#ifdef DUCKDB_DISABLE_EXTENSION_LOAD - throw PermissionException("Loading external extensions is disabled through a compile time flag"); -#else auto extension_init_result = InitialLoad(db, fs, extension, repository_name, core_only); // C++ ABI if (extension_init_result.abi_type == ExtensionABIType::CPP) { auto init_fun_name = extension_init_result.filebase + "_duckdb_cpp_init"; - ext_init_fun_t init_fun = TryLoadFunctionFromDLL(extension_init_result.lib_hdl, init_fun_name, - extension_init_result.filename); + ext_init_fun_t init_fun = reinterpret_cast( + db.config.GetExternalExtensionProvider().TryLoadFunction(extension_init_result.lib_hdl, init_fun_name)); if (!init_fun) { throw IOException("Extension '%s' did not contain the expected entrypoint function '%s'", extension, init_fun_name); @@ -842,8 +830,8 @@ void ExtensionHelper::LoadExternalExtensionInternal(DatabaseInstance &db, FileSy // C ABI, V2 if (UsesCAPIV2(extension_init_result)) { auto init_fun_name = extension_init_result.filebase + "_init_c_api_v2"; - auto init_fun_capi_v2 = TryLoadFunctionFromDLL( - extension_init_result.lib_hdl, init_fun_name, extension_init_result.filename); + auto init_fun_capi_v2 = reinterpret_cast( + db.config.GetExternalExtensionProvider().TryLoadFunction(extension_init_result.lib_hdl, init_fun_name)); if (!init_fun_capi_v2) { throw IOException( @@ -866,12 +854,12 @@ void ExtensionHelper::LoadExternalExtensionInternal(DatabaseInstance &db, FileSy // C ABI, V1 if (extension_init_result.abi_type == ExtensionABIType::C_STRUCT) { auto init_fun_name = extension_init_result.filebase + "_init_c_api"; - ext_init_c_api_fun_t init_fun_capi = TryLoadFunctionFromDLL( - extension_init_result.lib_hdl, init_fun_name, extension_init_result.filename); + ext_init_c_api_fun_t init_fun_capi = reinterpret_cast( + db.config.GetExternalExtensionProvider().TryLoadFunction(extension_init_result.lib_hdl, init_fun_name)); if (!init_fun_capi) { throw IOException("File \"%s\" did not contain function \"%s\": %s", extension_init_result.filename, - init_fun_name, GetDLError()); + init_fun_name, db.config.GetExternalExtensionProvider().GetLibraryError()); } // Create the load state DuckDBExtensionLoadState load_state(db, extension_init_result); @@ -904,7 +892,6 @@ void ExtensionHelper::LoadExternalExtensionInternal(DatabaseInstance &db, FileSy throw IOException("Unknown ABI type of value '%s' for extension '%s'", static_cast(extension_init_result.abi_type), extension); -#endif } void ExtensionHelper::LoadExternalExtension(ClientContext &context, const ExtensionLoadOptions &options) { diff --git a/src/duckdb/src/main/extension/extension_load_dynamic.cpp b/src/duckdb/src/main/extension/extension_load_dynamic.cpp new file mode 100644 index 000000000..5653a3d42 --- /dev/null +++ b/src/duckdb/src/main/extension/extension_load_dynamic.cpp @@ -0,0 +1,105 @@ +#include "duckdb/common/dl.hpp" +#include "duckdb/common/exception.hpp" +#include "duckdb/main/database.hpp" +#include "duckdb/main/extension/external_extension_provider.hpp" +#include "duckdb/main/extension_helper.hpp" +#include "duckdb/main/extension_install_info.hpp" +#include "duckdb_static_extension.h" + +#ifdef WASM_LOADABLE_EXTENSIONS +#include +#endif + +#if defined(DUCKDB_DISABLE_EXTENSION_LOAD) +#define DUCKDB_EXTENSION_LOAD_DEFINE_WARNING \ + "DUCKDB_DISABLE_EXTENSION_LOAD no longer disables loading and installing extensions: leave " \ + "duckdb_loadable_extensions out of the link instead (ENABLE_EXTENSION_LOAD=OFF, or package_build.py with " \ + "extension_load=False)" +#if defined(_MSC_VER) +#pragma message("warning: " DUCKDB_EXTENSION_LOAD_DEFINE_WARNING) +#else +#pragma GCC warning DUCKDB_EXTENSION_LOAD_DEFINE_WARNING +#endif +#endif + +namespace duckdb { + +unique_ptr InstallExternalExtension(DatabaseInstance &db, FileSystem &fs, + const string &local_path, const string &extension, + ExtensionInstallOptions &options, + optional_ptr context); + +//! Installs extensions from repositories and opens their libraries with dlopen +class DynamicExtensionProvider : public ExternalExtensionProvider { +public: + string GetName() const override { + return "dynamic"; + } + + bool SupportsExternalExtensions() const override { + return true; + } + + unique_ptr Install(DatabaseInstance &db, FileSystem &fs, const string &local_path, + const string &extension, ExtensionInstallOptions &options, + optional_ptr context) override { + return InstallExternalExtension(db, fs, local_path, extension, options, context); + } + + void *OpenLibrary(const string &filename, const string &filebase) override { +#ifdef WASM_LOADABLE_EXTENSIONS + EM_ASM( + { + // Next few lines should arguably in separate JavaScript-land function call + // TODO: move them out / have them configurable + const xhr = new XMLHttpRequest(); + xhr.open("GET", UTF8ToString($0), false); + xhr.responseType = "arraybuffer"; + xhr.send(null); + var uInt8Array = xhr.response; + WebAssembly.validate(uInt8Array); + console.log('Loading extension ', UTF8ToString($1)); + + // Here we add the uInt8Array to Emscripten's filesystem, for it to be found by dlopen + FS.writeFile(UTF8ToString($1), new Uint8Array(uInt8Array)); + }, + filename.c_str(), filebase.c_str()); + const auto &dopen_from = filebase; +#else + const auto &dopen_from = filename; +#endif + + auto lib_hdl = dlopen(dopen_from.c_str(), RTLD_NOW | RTLD_LOCAL); + if (!lib_hdl) { + throw IOException("Extension \"%s\" could not be loaded: %s", filename, GetDLError()); + } + return lib_hdl; + } + + void *TryLoadFunction(void *library, const string &function_name) override { + return dlsym(library, function_name.c_str()); + } + + string GetLibraryError() override { + return GetDLError(); + } +}; + +static void RegisterDynamicExtensionProvider(DatabaseInstance &db) { + db.config.SetExternalExtensionProvider(make_shared_ptr()); +} + +} // namespace duckdb + +//! Lets every database opened afterwards install and load external extensions, through +//! duckdb_register_static_extension. It is not an extension, so it asks for a database callback. +extern "C" int32_t duckdb_extension_loadable_extensions_describe(duckdb_extension_descriptor *descriptor) { + if (descriptor->version < 2) { + descriptor->set_error(descriptor, "loadable_extensions needs descriptor layout 2"); + return 1; + } + descriptor->version = 2; + descriptor->name = "loadable_extensions"; + descriptor->database_callback = reinterpret_cast(duckdb::RegisterDynamicExtensionProvider); + return 0; +} diff --git a/src/duckdb/src/main/extension/extension_loader.cpp b/src/duckdb/src/main/extension/extension_loader.cpp index 5c0d76384..44593e888 100644 --- a/src/duckdb/src/main/extension/extension_loader.cpp +++ b/src/duckdb/src/main/extension/extension_loader.cpp @@ -239,10 +239,16 @@ void ExtensionLoader::RegisterFunction(PragmaFunction function) { void ExtensionLoader::RegisterFunction(PragmaFunctionSet function) { D_ASSERT(!function.name.empty()); - CreatePragmaFunctionInfo info(std::move(function)); + RegisterFunction(CreatePragmaFunctionInfo(std::move(function))); +} + +void ExtensionLoader::RegisterFunction(CreatePragmaFunctionInfo info) { + D_ASSERT(!info.functions.name.empty()); info.extension_name = GetRegisteredExtensionName(); - info.SetQualifiedName( - QualifiedName(info.GetQualifiedName().Catalog(), loader_info.extension_schema, info.GetQualifiedName().Name())); + if (info.GetQualifiedName().Schema() == DEFAULT_SCHEMA) { + info.SetQualifiedName(QualifiedName(info.GetQualifiedName().Catalog(), loader_info.extension_schema, + info.GetQualifiedName().Name())); + } auto &system_catalog = Catalog::GetSystemCatalog(db); auto data = CatalogTransaction::GetSystemTransaction(db); system_catalog.CreatePragmaFunction(data, info); @@ -313,7 +319,7 @@ void ExtensionLoader::AddFunctionOverload(ScalarFunctionSet functions) { // NOLI void ExtensionLoader::AddFunctionOverload(TableFunctionSet functions) { // NOLINT auto &table_function = GetTableFunction(functions.name); - functions.ApplyToFunctions([&](TableFunction &function) { function.name = functions.name; }); + functions.ApplyToFunctions([&](TableFunction &function) { table_function.FinalizeFunction(function); }); for (auto &function : functions.functions) { table_function.functions.AddFunction(std::move(function)); } diff --git a/src/duckdb/src/main/extension/external_extension_provider.cpp b/src/duckdb/src/main/extension/external_extension_provider.cpp new file mode 100644 index 000000000..280791fa7 --- /dev/null +++ b/src/duckdb/src/main/extension/external_extension_provider.cpp @@ -0,0 +1,37 @@ +#include "duckdb/main/extension/external_extension_provider.hpp" + +#include "duckdb/common/exception.hpp" +#include "duckdb/main/extension_install_info.hpp" + +namespace duckdb { + +static constexpr const char *NO_LOADER_HINT = "this build does not link the loadable_extensions library"; + +string ExternalExtensionProvider::GetName() const { + return "none"; +} + +bool ExternalExtensionProvider::SupportsExternalExtensions() const { + return false; +} + +unique_ptr ExternalExtensionProvider::Install(DatabaseInstance &db, FileSystem &fs, + const string &local_path, const string &extension, + ExtensionInstallOptions &options, + optional_ptr context) { + throw PermissionException("Installing external extensions is not supported: %s", NO_LOADER_HINT); +} + +void *ExternalExtensionProvider::OpenLibrary(const string &filename, const string &filebase) { + throw PermissionException("Loading external extensions is not supported: %s", NO_LOADER_HINT); +} + +void *ExternalExtensionProvider::TryLoadFunction(void *library, const string &function_name) { + throw PermissionException("Loading external extensions is not supported: %s", NO_LOADER_HINT); +} + +string ExternalExtensionProvider::GetLibraryError() { + return string(); +} + +} // namespace duckdb diff --git a/src/duckdb/src/main/extension/linked_extension_registry.cpp b/src/duckdb/src/main/extension/linked_extension_registry.cpp new file mode 100644 index 000000000..1f769e5c3 --- /dev/null +++ b/src/duckdb/src/main/extension/linked_extension_registry.cpp @@ -0,0 +1,147 @@ +#include "duckdb/main/extension/linked_extension_registry.hpp" + +#include "duckdb/common/exception.hpp" +#include "duckdb/common/string_util.hpp" +#include "duckdb/main/database.hpp" +#include "duckdb_static_extension.h" + +#include +#include + +namespace duckdb { + +namespace { + +struct RegisteredExtension { + string name; + duckdb_extension_describe_t describe; +}; + +struct RegistryState { + std::mutex lock; + vector extensions; + vector registered; + vector errors; + //! set when a registration failed without recording why - recording it is what may have failed + std::atomic unknown_error {false}; +}; + +// function-local so that registrations during static initialization always find it constructed +RegistryState &GetRegistryState() { + static RegistryState state; + return state; +} + +void SetDescriptorError(duckdb_extension_descriptor *descriptor, const char *message) { + auto &error = *static_cast(descriptor->internal); + error = message ? message : ""; +} + +//! Returns an empty string on success, otherwise the reason the registration failed. +string Register(duckdb_extension_describe_t describe) { + StaticExtensionDescription description; + auto error = LinkedExtensionRegistry::Describe(describe, description); + if (!error.empty()) { + return error; + } + auto &state = GetRegistryState(); + std::lock_guard guard(state.lock); + for (auto &entry : state.registered) { + if (!StringUtil::CIEquals(entry.name, description.name)) { + continue; + } + if (entry.describe == describe) { + return string(); + } + return "extension '" + description.name + "' is registered by two different describe functions"; + } + state.registered.push_back({description.name, describe}); + LinkedExtension linked; + linked.name = description.name; + if (description.descriptor.database_callback) { + linked.database_callback = + reinterpret_cast(description.descriptor.database_callback); + } else { + linked.load = [describe](DuckDB &db) { + db.LoadStaticExtension(describe); + }; + } + state.extensions.push_back(std::move(linked)); + return string(); +} + +} // namespace + +string LinkedExtensionRegistry::Describe(duckdb_extension_describe_t describe, StaticExtensionDescription &result) { + if (!describe) { + return "no describe function was given"; + } + string error; + auto &descriptor = result.descriptor; + descriptor = duckdb_extension_descriptor(); + descriptor.version = DUCKDB_EXTENSION_DESCRIPTOR_VERSION; + descriptor.set_error = SetDescriptorError; + descriptor.internal = &error; + + auto status = describe(&descriptor); + descriptor.internal = nullptr; + result.name = descriptor.name ? descriptor.name : ""; + result.version = descriptor.extension_version ? descriptor.extension_version : ""; + result.api_version = descriptor.api_version ? descriptor.api_version : ""; + const string subject = result.name.empty() ? string("an extension") : "extension '" + result.name + "'"; + if (status != 0) { + return subject + " refused to register: " + (error.empty() ? string("no reason given") : error); + } + if (!error.empty()) { + return subject + " reported an error but returned success: " + error; + } + if (descriptor.version < 1 || descriptor.version > DUCKDB_EXTENSION_DESCRIPTOR_VERSION) { + return StringUtil::Format("%s filled descriptor layout %d, but layout %d was offered", subject, + descriptor.version, DUCKDB_EXTENSION_DESCRIPTOR_VERSION); + } + if (result.name.empty()) { + return "an extension describe function did not set a name"; + } + if (descriptor.version < 2) { + descriptor.database_callback = nullptr; + } + auto has_entry = descriptor.entry_cpp || descriptor.entry_capi_v1 || descriptor.entry_capi_v2; + if (descriptor.database_callback && has_entry) { + return subject + " set both an entry point and a database callback"; + } + if (!has_entry && !descriptor.database_callback) { + return subject + " did not set an entry point"; + } + return string(); +} + +vector LinkedExtensionRegistry::Get() { + auto &state = GetRegistryState(); + std::lock_guard guard(state.lock); + if (state.errors.empty() && !state.unknown_error) { + return state.extensions; + } + auto errors = state.errors; + if (state.unknown_error) { + errors.push_back("an extension registration failed with an unknown error"); + } + throw InvalidConfigurationException("Failed to register statically linked extensions: %s", + StringUtil::Join(errors, "; ")); +} + +} // namespace duckdb + +int32_t duckdb_register_static_extension(duckdb_extension_describe_t describe) { + auto &state = duckdb::GetRegistryState(); + try { + auto error = duckdb::Register(describe); + if (error.empty()) { + return 0; + } + std::lock_guard guard(state.lock); + state.errors.push_back(std::move(error)); + } catch (...) { // NOLINT: never throw across the C API + state.unknown_error = true; + } + return 1; +} diff --git a/src/duckdb/src/main/extension_callback_manager.cpp b/src/duckdb/src/main/extension_callback_manager.cpp index d1cb0f9c3..a59a69d65 100644 --- a/src/duckdb/src/main/extension_callback_manager.cpp +++ b/src/duckdb/src/main/extension_callback_manager.cpp @@ -1,7 +1,8 @@ #include "duckdb/main/extension_callback_manager.hpp" #include "duckdb/parser/parser_extension.hpp" #include "duckdb/parser/grammar_extension.hpp" -#include "duckdb/parser/dialect_extension.hpp" +#include "duckdb/parser/peg/compiled_grammar.hpp" +#include "duckdb/parser/peg/dialect_extension.hpp" #include "duckdb/optimizer/optimizer_extension.hpp" #include "duckdb/planner/operator_extension.hpp" #include "duckdb/planner/planner_extension.hpp" @@ -15,7 +16,7 @@ namespace duckdb { struct ExtensionCallbackRegistry { //! SQL dialects made available to the PEG parser - vector dialect_extensions; + vector> dialect_extensions; //! Extensions made to the parser vector parser_extensions; //! Extensions made to the grammar of the main (PEG) parser @@ -47,7 +48,6 @@ ExtensionCallbackManager &ExtensionCallbackManager::Get(DatabaseInstance &db) { } ExtensionCallbackManager::ExtensionCallbackManager() : callback_registry(make_shared_ptr()) { - callback_registry->dialect_extensions.emplace_back("duckdb"); } ExtensionCallbackManager::~ExtensionCallbackManager() { } @@ -83,15 +83,18 @@ void ExtensionCallbackManager::Register(shared_ptr extension) callback_registry.atomic_store(new_registry); } -void ExtensionCallbackManager::Register(DialectExtension extension) { - if (extension.name.empty()) { +void ExtensionCallbackManager::Register(shared_ptr extension) { + if (!extension) { + throw InvalidInputException("Cannot register a NULL dialect extension"); + } + if (extension->Name().empty()) { throw InvalidInputException("Dialect name cannot be empty"); } lock_guard guard(registry_lock); auto new_registry = make_shared_ptr(*callback_registry); for (auto &existing : new_registry->dialect_extensions) { - if (StringUtil::CIEquals(existing.name, extension.name)) { - throw InvalidInputException("Dialect \"%s\" is already registered", extension.name); + if (StringUtil::CIEquals(existing->Name(), extension->Name())) { + throw InvalidInputException("Dialect \"%s\" is already registered", extension->Name()); } } new_registry->dialect_extensions.push_back(std::move(extension)); @@ -173,10 +176,10 @@ ExtensionCallbackIteratorHelper ExtensionCallbackManager::Parse return ExtensionCallbackIteratorHelper(parser_extensions, std::move(registry)); } -ExtensionCallbackIteratorHelper ExtensionCallbackManager::DialectExtensions() const { +ExtensionCallbackIteratorHelper> ExtensionCallbackManager::DialectExtensions() const { auto registry = callback_registry.atomic_load(); auto &dialect_extensions = registry->dialect_extensions; - return ExtensionCallbackIteratorHelper(dialect_extensions, std::move(registry)); + return ExtensionCallbackIteratorHelper>(dialect_extensions, std::move(registry)); } ExtensionCallbackIteratorHelper ExtensionCallbackManager::PlannerExtensions() const { @@ -223,14 +226,14 @@ bool ExtensionCallbackManager::HasParserExtensions() const { return !registry->parser_extensions.empty(); } -bool ExtensionCallbackManager::HasDialectExtension(const string &name) const { +optional_ptr ExtensionCallbackManager::GetDialectExtension(const string &name) const { auto registry = callback_registry.atomic_load(); for (auto &dialect : registry->dialect_extensions) { - if (StringUtil::CIEquals(dialect.name, name)) { - return true; + if (StringUtil::CIEquals(dialect->Name(), name)) { + return dialect.get(); } } - return false; + return nullptr; } void OptimizerExtension::Register(DBConfig &config, OptimizerExtension extension) { @@ -245,7 +248,7 @@ void GrammarExtension::Register(DatabaseInstance &db, shared_ptr extension) { config.GetCallbackManager().Register(std::move(extension)); } @@ -283,7 +286,7 @@ template class ExtensionCallbackIteratorHelper>; template class ExtensionCallbackIteratorHelper; template class ExtensionCallbackIteratorHelper; template class ExtensionCallbackIteratorHelper>; -template class ExtensionCallbackIteratorHelper; +template class ExtensionCallbackIteratorHelper>; template class ExtensionCallbackIteratorHelper; } // namespace duckdb diff --git a/src/duckdb/src/main/extension_install_info.cpp b/src/duckdb/src/main/extension_install_info.cpp index 35143e0c3..62a1a26c7 100644 --- a/src/duckdb/src/main/extension_install_info.cpp +++ b/src/duckdb/src/main/extension_install_info.cpp @@ -1,5 +1,6 @@ #include "duckdb/main/extension_install_info.hpp" #include "duckdb/common/string.hpp" +#include "duckdb/common/string_util.hpp" #include "duckdb/common/file_system.hpp" #include "duckdb/main/settings.hpp" #include "duckdb/common/serializer/buffered_file_reader.hpp" @@ -68,6 +69,39 @@ string ExtensionRepository::TryConvertUrlToKnownRepository(const string &url) { return ""; } +string ExtensionRepository::TryGetBackupRepositoryUrl(const string &url) { + if (url == CORE_REPOSITORY_URL) { + return CORE_BACKUP_REPOSITORY_URL; + } + if (url == COMMUNITY_REPOSITORY_URL) { + return COMMUNITY_BACKUP_REPOSITORY_URL; + } + return ""; +} + +bool ExtensionRepository::IsOfficialRepositoryUrl(const string &url) { + static constexpr const char *OFFICIAL_REPOSITORY_URLS[] = {CORE_REPOSITORY_URL, CORE_NIGHTLY_REPOSITORY_URL, + COMMUNITY_REPOSITORY_URL, CORE_BACKUP_REPOSITORY_URL, + COMMUNITY_BACKUP_REPOSITORY_URL}; + // strip the scheme: the official repositories are served over both http and https + string host_and_path; + if (StringUtil::StartsWith(url, "http://")) { + host_and_path = url.substr(7); + } else if (StringUtil::StartsWith(url, "https://")) { + host_and_path = url.substr(8); + } else { + return false; + } + for (auto repository_url : OFFICIAL_REPOSITORY_URLS) { + string repository_host = string(repository_url).substr(7); + // the host must match exactly, so that e.g. "extensions.duckdb.org.example.com" does not match + if (host_and_path == repository_host || StringUtil::StartsWith(host_and_path, repository_host + "/")) { + return true; + } + } + return false; +} + ExtensionRepository ExtensionRepository::GetDefaultRepository(optional_ptr config) { if (config) { auto custom_extension_repo = Settings::Get(*config); diff --git a/src/duckdb/src/main/extension_manager.cpp b/src/duckdb/src/main/extension_manager.cpp index c888a2405..ee186a7fa 100644 --- a/src/duckdb/src/main/extension_manager.cpp +++ b/src/duckdb/src/main/extension_manager.cpp @@ -56,7 +56,7 @@ void ExtensionManager::RemoveExtensionInfo(const string &name) { loaded_extensions_info.erase(Identifier(name)); } -optional_ptr ExtensionManager::GetExtensionInfo(const string &name) { +shared_ptr ExtensionManager::GetExtensionInfo(const string &name) { auto extension_name = ExtensionHelper::GetExtensionName(name); lock_guard guard(lock); @@ -64,7 +64,7 @@ optional_ptr ExtensionManager::GetExtensionInfo(const string &nam if (entry == loaded_extensions_info.end()) { return nullptr; } - return entry->second.get(); + return entry->second; } vector ExtensionManager::GetExtensions() { @@ -101,7 +101,7 @@ unique_ptr ExtensionManager::BeginLoad(const ExtensionLoadO extension_name = ExtensionHelper::GetExtensionName(options.extension_name); string original_extension_name = extension_name; - optional_ptr info; + shared_ptr info; auto entry = loaded_extensions_info.end(); if (options.alias.empty()) { @@ -113,9 +113,9 @@ unique_ptr ExtensionManager::BeginLoad(const ExtensionLoadO // check if the extension is not already registered under an alias // we don't have an entry yet - create one - auto extension_info = make_uniq(); + auto extension_info = make_shared_ptr(); extension_info->orig_ext_name = original_extension_name; - info = extension_info.get(); + info = extension_info; if (!options.alias.empty()) { // if aliasing is used, we need to register the alias instead of extension name @@ -142,13 +142,13 @@ unique_ptr ExtensionManager::BeginLoad(const ExtensionLoadO return nullptr; } // it is not loaded yet - try to load it - info = entry->second.get(); + info = entry->second; } extension_list_lock.unlock(); // we have an extension and we want to try to load it - instantiate the load // we instantiate the ExtensionActiveLoad which also grabs the lock for loading the specific extension - auto result = make_uniq(db, *info, Identifier(original_extension_name), options.alias); + auto result = make_uniq(db, info, Identifier(original_extension_name), options.alias); // we now have a lock for loading the extension // HOWEVER - another thread might have finished loading in the meantime - double check to avoid a double load diff --git a/src/duckdb/src/main/external_resource_type_registry.cpp b/src/duckdb/src/main/external_resource_type_registry.cpp index 03c18d94f..c4175f712 100644 --- a/src/duckdb/src/main/external_resource_type_registry.cpp +++ b/src/duckdb/src/main/external_resource_type_registry.cpp @@ -1,6 +1,5 @@ #include "duckdb/main/external_resource_type_registry.hpp" -#include "duckdb/common/error_data.hpp" #include "duckdb/common/exception.hpp" #include "duckdb/main/client_context.hpp" #include "duckdb/main/database.hpp" @@ -20,23 +19,10 @@ ExternalResourceTypeRegistry &ExternalResourceTypeRegistry::Get(ClientContext &c //! name here - the registry is append-only, and a bad entry would squat its name until restart. static void ValidateFunctionName(const ExternalResourceType &type, const string &key, const string &value) { QualifiedName qname; - try { - qname = QualifiedName::Parse(value); - } catch (const std::exception &ex) { - ErrorData error(ex); + string error; + if (!QualifiedName::TryParse(value, qname, error)) { throw InvalidInputException("external resource type \"%s\": '%s' is not a valid function name: %s", type.name, - key, error.RawMessage()); - } - if (qname.Name().empty()) { - throw InvalidInputException("external resource type \"%s\": '%s' is not a valid function name: \"%s\"", - type.name, key, value); - } - for (auto &component : qname.Path()) { - if (component.empty()) { - throw InvalidInputException( - "external resource type \"%s\": '%s' has an empty catalog or schema component: \"%s\"", type.name, key, - value); - } + key, error); } } diff --git a/src/duckdb/src/main/http/http_client_httplib.cpp b/src/duckdb/src/main/http/http_client_httplib.cpp new file mode 100644 index 000000000..55f69bf18 --- /dev/null +++ b/src/duckdb/src/main/http/http_client_httplib.cpp @@ -0,0 +1,145 @@ +#include "duckdb/main/http/http_util.hpp" +#include "duckdb/common/exception.hpp" +#include "duckdb/main/database.hpp" +#include "duckdb_static_extension.h" + +#include "httplib.hpp" + +#if defined(DUCKDB_DISABLE_BUILTIN_HTTPLIB) || defined(DISABLE_DUCKDB_REMOTE_INSTALL) || \ + defined(DUCKDB_DISABLE_EXTENSION_LOAD) +#define DUCKDB_HTTPLIB_DEFINE_WARNING \ + "DUCKDB_DISABLE_BUILTIN_HTTPLIB, DISABLE_DUCKDB_REMOTE_INSTALL and DUCKDB_DISABLE_EXTENSION_LOAD no longer " \ + "disable the built-in httplib client: leave duckdb_httplib out of the link instead (ENABLE_BUILTIN_HTTPLIB=OFF, " \ + "or package_build.py with builtin_httplib=False)" +#if defined(_MSC_VER) +#pragma message("warning: " DUCKDB_HTTPLIB_DEFINE_WARNING) +#else +#pragma GCC warning DUCKDB_HTTPLIB_DEFINE_WARNING +#endif +#endif + +namespace duckdb { + +class HTTPLibClient : public HTTPClient { +public: + HTTPLibClient(HTTPParams &http_params, const string &proto_host_port) : HTTPClient(proto_host_port) { + client = make_uniq(proto_host_port); + Initialize(http_params); + } + void Initialize(HTTPParams &http_params) override { + auto sec = static_cast(http_params.timeout); + auto usec = static_cast(http_params.timeout_usec); + client->set_follow_location(http_params.follow_location); + client->set_keep_alive(http_params.keep_alive); + client->set_write_timeout(sec, usec); + client->set_read_timeout(sec, usec); + client->set_connection_timeout(sec, usec); + client->set_decompress(false); + + if (!http_params.http_proxy.empty()) { + client->set_proxy(http_params.http_proxy, static_cast(http_params.http_proxy_port)); + + if (!http_params.http_proxy_username.empty()) { + client->set_proxy_basic_auth(http_params.http_proxy_username, http_params.http_proxy_password); + } + } + } + unique_ptr Get(GetRequestInfo &info) override { + auto headers = TransformHeaders(info.headers, info.params); + if (!info.response_handler && !info.content_handler) { + return TransformResult(client->Get(info.path, headers)); + } else { + return TransformResult(client->Get( + info.path, headers, + [&](const duckdb_httplib::Response &response) { + auto http_response = TransformResponse(response); + return info.response_handler(*http_response); + }, + [&](const char *data, size_t data_length) { + return info.content_handler(const_data_ptr_cast(data), data_length); + })); + } + } + unique_ptr Put(PutRequestInfo &info) override { + throw NotImplementedException("PUT request not implemented"); + } + + unique_ptr Head(HeadRequestInfo &info) override { + throw NotImplementedException("HEAD request not implemented"); + } + + unique_ptr Delete(DeleteRequestInfo &info) override { + throw NotImplementedException("DELETE request not implemented"); + } + + unique_ptr Post(PostRequestInfo &info) override { + throw NotImplementedException("POST request not implemented"); + } + + unique_ptr Options(OptionsRequestInfo &info) override { + throw NotImplementedException("OPTIONS request not implemented"); + } + + unique_ptr client; + +private: + duckdb_httplib::Headers TransformHeaders(const HTTPHeaders &header_map, const HTTPParams ¶ms) { + duckdb_httplib::Headers headers; + for (auto &entry : header_map) { + headers.insert(entry); + } + return headers; + } + + unique_ptr TransformResponse(const duckdb_httplib::Response &response) { + auto status_code = HTTPUtil::ToStatusCode(response.status); + auto result = make_uniq(status_code); + result->body = response.body; + result->reason = response.reason; + for (auto &entry : response.headers) { + result->headers.Append(entry.first, entry.second); + } + return result; + } + + unique_ptr TransformResult(const duckdb_httplib::Result &res) { + if (res.error() == duckdb_httplib::Error::Success) { + auto &response = res.value(); + return TransformResponse(response); + } else { + auto result = make_uniq(HTTPStatusCode::INVALID); + result->request_error = to_string(res.error()); + return result; + } + } +}; + +class HTTPLibHTTPUtil : public HTTPUtil { +public: + string GetName() const override { + return "Built-In"; + } + + unique_ptr InitializeClient(HTTPParams &http_params, const string &proto_host_port) override { + return make_uniq(http_params, proto_host_port); + } +}; + +static void RegisterHTTPLibClient(DatabaseInstance &db) { + db.config.SetHTTPUtil(make_shared_ptr()); +} + +} // namespace duckdb + +//! Registers the built-in httplib client for every database opened afterwards, through +//! duckdb_register_static_extension. It is not an extension, so it asks for a database callback. +extern "C" int32_t duckdb_extension_httplib_describe(duckdb_extension_descriptor *descriptor) { + if (descriptor->version < 2) { + descriptor->set_error(descriptor, "httplib needs descriptor layout 2"); + return 1; + } + descriptor->version = 2; + descriptor->name = "httplib"; + descriptor->database_callback = reinterpret_cast(duckdb::RegisterHTTPLibClient); + return 0; +} diff --git a/src/duckdb/src/main/http/http_client_pool.cpp b/src/duckdb/src/main/http/http_client_pool.cpp index 576c06624..7fcfe5d23 100644 --- a/src/duckdb/src/main/http/http_client_pool.cpp +++ b/src/duckdb/src/main/http/http_client_pool.cpp @@ -40,8 +40,25 @@ HTTPClientPool::HTTPClientPool(idx_t capacity_p) : capacity(capacity_p) { non_empty_buckets.reserve(capacity); } +void HTTPClientPool::SetCapacity(idx_t new_capacity) { + D_ASSERT(new_capacity > 0); + if (new_capacity > capacity) { + client_buckets.reserve(new_capacity); + non_empty_buckets.reserve(new_capacity); + for (auto &entry : client_buckets) { + entry.second.idle_clients.reserve(new_capacity); + } + } + capacity = new_capacity; + WakeNextAdmission(); +} + +idx_t HTTPClientPool::GetCapacity() const { + return capacity; +} + bool HTTPClientPool::HasAdmissionResource() const { - return reserved_clients < capacity || !non_empty_buckets.empty(); + return reserved_clients < capacity || (reserved_clients == capacity && !non_empty_buckets.empty()); } void HTTPClientPool::WaitForAdmission(annotated_unique_lock &guard, bool owns_capacity) { @@ -179,6 +196,7 @@ void HTTPClientPool::AdoptPreparedBucket(Reservation &reservation) { reservation.bucket = BucketHandle(exact->second); return; } + prepared.idle_clients.reserve(capacity); auto inserted = client_buckets.insert(std::move(reservation.bucket_node.node)); reservation.bucket = BucketHandle(inserted->second); } @@ -198,6 +216,9 @@ void HTTPClientPool::Return(BucketHandle handle, unique_ptr client) HTTPClientPool::Reservation HTTPClientPool::TakeIdleForDisposal(IdleFilter filter, idx_t first, uint64_t second) { Reservation result; + if (filter == IdleFilter::EXCESS && reserved_clients <= capacity) { + return result; + } for (auto &entry : client_buckets) { if (!entry.second.idle_clients.empty() && MatchesFilter(entry.first, filter, first, second)) { result.bucket = BucketHandle(entry.second); @@ -311,6 +332,7 @@ bool HTTPClientPool::MatchesFilter(const ClientKey &key, IdleFilter filter, idx_ return key.provider_epoch == first && key.connection_epoch < second; case IdleFilter::SESSION: return key.session_id == first; + case IdleFilter::EXCESS: case IdleFilter::ALL: return true; } diff --git a/src/duckdb/src/main/http/http_retry_budget.cpp b/src/duckdb/src/main/http/http_retry_budget.cpp index 8010996df..7ba67f2a8 100644 --- a/src/duckdb/src/main/http/http_retry_budget.cpp +++ b/src/duckdb/src/main/http/http_retry_budget.cpp @@ -3,11 +3,14 @@ #include "duckdb/common/limits.hpp" #include "duckdb/common/operator/cast_operators.hpp" #include "duckdb/common/random_engine.hpp" +#include "duckdb/common/string_util.hpp" +#include "duckdb/common/types/timestamp.hpp" +#include "duckdb/function/scalar/strftime_format.hpp" #include "duckdb/main/http/http_util.hpp" +#include #ifndef DUCKDB_NO_THREADS #include -#include #include #endif @@ -17,16 +20,224 @@ HTTPRetryDecision HTTPRetryDecision::Finish() { return HTTPRetryDecision(Type::FINISH); } +HTTPRetryDecision HTTPRetryDecision::Success() { + return HTTPRetryDecision(Type::SUCCESS); +} + +HTTPRetryDecision HTTPRetryDecision::Failure() { + return HTTPRetryDecision(Type::FAILURE); +} + +HTTPRetryDecision HTTPRetryDecision::Correction() { + return HTTPRetryDecision(Type::CORRECTION); +} + HTTPRetryDecision HTTPRetryDecision::Retry() { return HTTPRetryDecision(Type::RETRY); } +HTTPRetryDecision HTTPRetryDecision::Transient() { + return HTTPRetryDecision(Type::TRANSIENT); +} + HTTPRetryDecision HTTPRetryDecision::Throttled(const string &retry_after) { return HTTPRetryDecision(Type::THROTTLED, retry_after); } -HTTPRetryBudget::HTTPRetryBudget(const HTTPParams ¶ms) - : retries(params.retries), retry_wait_ms(params.retry_wait_ms), retry_backoff(params.retry_backoff) { +void HTTPRetryQuota::Touch(int64_t now_ms) { + if (now_ms - last_used_ms >= IDLE_RESET_MS) { + capacity = INITIAL_CAPACITY; + } + last_used_ms = now_ms; +} + +bool HTTPRetryQuota::TryAcquire(int64_t cost, int64_t now_ms) { + lock_guard guard(lock); + Touch(now_ms); + if (cost > capacity) { + return false; + } + capacity -= cost; + return true; +} + +void HTTPRetryQuota::Release(int64_t amount, int64_t now_ms) { + lock_guard guard(lock); + Touch(now_ms); + capacity = MinValue(capacity + amount, INITIAL_CAPACITY); +} + +int64_t HTTPRetryQuota::Available() const { + lock_guard guard(lock); + return capacity; +} + +int64_t HTTPRetryQuota::IdleMs(int64_t now_ms) const { + lock_guard guard(lock); + return now_ms - last_used_ms; +} + +HTTPRetryBudget::HTTPRetryBudget(const HTTPParams ¶ms, HTTPRetryOutcomeOwner owner_p) + : retries(params.retries), retry_wait_ms(params.retry_wait_ms), retry_backoff(params.retry_backoff), + owner(owner_p) { +#ifdef DUCKDB_NO_THREADS + // Without threads there is no back-off sleep, so the schedule settings go unused. + (void)retry_wait_ms; + (void)retry_backoff; +#endif +} + +uint64_t HTTPRetryBudget::ThrottledRetries() const { + return throttled_retries; +} + +uint64_t HTTPRetryBudget::RetriesUsed() const { + return retries_used; +} + +uint64_t HTTPRetryBudget::BeginAttempt() { + return ++attempts; +} + +uint64_t HTTPRetryBudget::ThrottleExtraRetries() const { +#ifndef DUCKDB_NO_THREADS + // http_retries=0 switches retries off entirely, throttled or not. + return retries > 0 ? THROTTLE_EXTRA_RETRIES : 0; +#else + // Without threads we cannot sleep between retries, so do not add zero-delay retries. + return 0; +#endif +} + +uint64_t HTTPRetryBudget::Corrections() const { + // The first send and every admitted retry are accounted for; any other send was a corrected re-send. + const auto accounted = 1 + retries_used; + return attempts > accounted ? attempts - accounted : 0; +} + +uint64_t HTTPRetryBudget::MaxAttempts() const { + return 1 + retries + ThrottleExtraRetries() + Corrections(); +} + +bool HTTPRetryBudget::InRun() const { + return run_depth > 0; +} + +bool HTTPRetryBudget::QuotaExhausted() const { + return quota_exhausted; +} + +bool HTTPRetryBudget::CorrectionsExhausted() const { + return corrections_exhausted; +} + +void HTTPRetryBudget::BindQuota(shared_ptr quota_p) { + quota = std::move(quota_p); +} + +HTTPRetryOutcomeOwner HTTPRetryBudget::Owner() const { + return owner; +} + +void HTTPRetryBudget::RequireCoordinator(const char *what) const { + if (owner != HTTPRetryOutcomeOwner::COORDINATOR) { + throw InternalException( + "HTTP retry budget: %s is the coordinator's decision, but this operation's outcome is " + "owned by the HTTP status; construct the budget with HTTPRetryOutcomeOwner::COORDINATOR", + what); + } +} + +void HTTPRetryBudget::ReportSuccess() { + RequireCoordinator("ReportSuccess"); + Settle(true); +} + +void HTTPRetryBudget::ReportFailure() { + RequireCoordinator("ReportFailure"); + Settle(false); +} + +void HTTPRetryBudget::RecordStatusOutcome(bool success) { + if (owner != HTTPRetryOutcomeOwner::STATUS) { + // The coordinator validates the response and reports; what the status says is never final here. + return; + } + status_outcome = success ? RecordedOutcome::SUCCESS : RecordedOutcome::FAILURE; + if (!InRun()) { + // Nobody else is going to judge this response. + Settle(success); + } +} + +void HTTPRetryBudget::Settle(bool success) { + status_outcome = RecordedOutcome::NONE; + if (outcome_settled) { + // Refunded or charged already: the operation has one outcome. + return; + } + outcome_settled = true; + if (success) { + if (last_retry_quota) { + last_retry_quota->Release(last_retry_cost); + } else if (quota) { + quota->Release(HTTPRetryQuota::NO_RETRY_REFUND); + } + } + ForgetLastRetry(); +} + +void HTTPRetryBudget::ForgetLastRetry() { + last_retry_quota.reset(); + last_retry_cost = 0; +} + +uint64_t HTTPRetryBudget::BackoffMs(uint64_t retry_wait_ms, float retry_backoff, uint64_t retry, uint64_t floor_ms, + uint64_t random_value, bool throttled) { + D_ASSERT(retry > 0); + const auto exponent = static_cast(retry - 1); + // Cap in the double domain so the exponential cannot overflow the cast. + const auto base_ms = + MinValue(static_cast(retry_wait_ms) * pow(static_cast(retry_backoff), exponent), + static_cast(MAX_BACKOFF_MS)); + const auto base = static_cast(base_ms); + // Throttled retries never collapse onto the moment the server asked us to slow down. + const auto lower = throttled ? base / 2 : 0; + const auto jittered = lower + random_value % (base - lower + 1); + return MaxValue(jittered, MinValue(floor_ms, MAX_BACKOFF_MS)); +} + +static const StrpTimeFormat &RetryAfterDateFormat() { + // RFC 7231 IMF-fixdate, e.g. "Sun, 06 Nov 1994 08:49:37 GMT". + static const StrpTimeFormat format = []() { + StrpTimeFormat result; + auto error = StrTimeFormat::ParseFormatSpecifier("%a, %d %b %Y %H:%M:%S GMT", result); + D_ASSERT(error.empty()); + (void)error; + return result; + }(); + return format; +} + +bool HTTPRetryBudget::TryParseRetryAfterMs(const string &retry_after, uint64_t &delay_ms) { + string value = retry_after; + StringUtil::Trim(value); + if (value.empty()) { + return false; + } + uint64_t seconds = 0; + if (TryCast::Operation(string_t(value), seconds)) { + delay_ms = seconds > MAX_BACKOFF_MS / 1000 ? MAX_BACKOFF_MS : seconds * 1000; + return true; + } + timestamp_t target; + if (!RetryAfterDateFormat().TryParseTimestamp(value.c_str(), value.size(), target)) { + return false; + } + const auto now_ms = Timestamp::GetEpochMs(Timestamp::GetCurrentTimestamp()); + const auto target_ms = Timestamp::GetEpochMs(target); + delay_ms = target_ms > now_ms ? static_cast(target_ms - now_ms) : 0; + return true; } void HTTPRetryBudget::Run(const std::function &attempt) { @@ -38,57 +249,110 @@ void HTTPRetryBudget::Run(const std::function &attempt, if (!attempt) { throw InternalException("HTTP retry loop requires an attempt callback"); } + // A refusal belongs to the run that hit it, not to later requests sharing the budget. + quota_exhausted = false; + corrections_exhausted = false; + const bool outermost = !InRun(); + if (outermost) { + // A new operation leg: whatever an earlier leg settled is history. + status_outcome = RecordedOutcome::NONE; + outcome_settled = false; + } + struct RunDepthGuard { + explicit RunDepthGuard(uint64_t &depth_p) : depth(depth_p) { + depth++; + } + ~RunDepthGuard() { + depth--; + } + uint64_t &depth; + } depth_guard(run_depth); for (;;) { auto decision = attempt(); - if (decision.type == HTTPRetryDecision::Type::FINISH || !ConsumeAndWait(decision)) { + switch (decision.type) { + case HTTPRetryDecision::Type::SUCCESS: + // The coordinator validated the response: the explicit outcome is final wherever it is decided. + RequireCoordinator("a Success decision"); + Settle(true); return; - } - if (before_retry) { - before_retry(); + case HTTPRetryDecision::Type::FAILURE: + RequireCoordinator("a Failure decision"); + Settle(false); + return; + case HTTPRetryDecision::Type::FINISH: + if (outermost && status_outcome != RecordedOutcome::NONE) { + // A status-owned operation is over and nobody judges it: the outcome core recorded from the status + // is settled now. Without one the caller records the outcome itself after the run. A + // coordinator-owned budget never records a status and waits for the coordinator's report. + Settle(status_outcome == RecordedOutcome::SUCCESS); + } + return; + case HTTPRetryDecision::Type::CORRECTION: + if (!AdmitCorrection()) { + return; + } + continue; + default: + if (!ConsumeAndWait(decision)) { + return; + } + if (before_retry) { + before_retry(); + } } } } +bool HTTPRetryBudget::AdmitCorrection() { + // The corrected request is a new send of the same operation: the last response does not count. + status_outcome = RecordedOutcome::NONE; + if (corrections_admitted >= MAX_CORRECTIONS) { + // The operation ends with the response it has, and its retries did not get it through. + corrections_exhausted = true; + ForgetLastRetry(); + return false; + } + corrections_admitted++; + return true; +} + bool HTTPRetryBudget::ConsumeAndWait(const HTTPRetryDecision &decision) { D_ASSERT(decision.type != HTTPRetryDecision::Type::FINISH); const bool throttled = decision.type == HTTPRetryDecision::Type::THROTTLED; -#ifndef DUCKDB_NO_THREADS - static constexpr uint64_t THROTTLE_EXTRA_RETRIES = 5; -#else - // Without threads we cannot sleep between retries, so do not add zero-delay retries. - static constexpr uint64_t THROTTLE_EXTRA_RETRIES = 0; -#endif - const auto extra_retries = throttled ? THROTTLE_EXTRA_RETRIES : 0; + const auto extra_retries = throttled ? ThrottleExtraRetries() : 0; + // Asking for a retry means the last attempt did not get the operation through, whatever a nested loop recorded. + status_outcome = RecordedOutcome::NONE; if (retries_used >= retries && retries_used - retries >= extra_retries) { + // Nothing left to refund: the retries already spent did not get this request through. + ForgetLastRetry(); return false; } + const auto cost = decision.type == HTTPRetryDecision::Type::TRANSIENT ? HTTPRetryQuota::TRANSIENT_RETRY_COST + : HTTPRetryQuota::RETRY_COST; + if (quota && !quota->TryAcquire(cost)) { + quota_exhausted = true; + ForgetLastRetry(); + return false; + } + // The admitted retry is the one a later success refunds; its outcome is still open. + last_retry_quota = quota; + last_retry_cost = cost; + outcome_settled = false; retries_used++; + if (throttled) { + throttled_retries++; + } #ifndef DUCKDB_NO_THREADS - if (retries_used > 1 || throttled) { - static constexpr uint64_t THROTTLE_MAX_BACKOFF_MS = 10000; - const auto backoff_exp = static_cast(throttled ? retries_used - 1 : retries_used - 2); - const auto backoff_ms = (double)retry_wait_ms * pow(retry_backoff, backoff_exp); - // Cap in the double domain to avoid overflow in the cast. - uint64_t sleep_amount = (uint64_t)MinValue(backoff_ms, (double)NumericLimits::Maximum()); - if (throttled) { - sleep_amount = MinValue(sleep_amount, THROTTLE_MAX_BACKOFF_MS); - if (!decision.retry_after.empty()) { - // Honor a numeric Retry-After (seconds), capped like the backoff. - uint64_t retry_after_s = 0; - if (TryCast::Operation(string_t(decision.retry_after), retry_after_s)) { - retry_after_s = MinValue(retry_after_s, THROTTLE_MAX_BACKOFF_MS / 1000); - sleep_amount = MaxValue(sleep_amount, retry_after_s * 1000); - } - } - // Subtractive jitter ([base/2, base]) de-synchronizes retry bursts while honoring the cap. - RandomEngine random; - sleep_amount -= random.NextRandomInteger64() % (sleep_amount / 2 + 1); - } - std::this_thread::sleep_for(std::chrono::milliseconds(sleep_amount)); + uint64_t floor_ms = 0; + if (throttled && !decision.retry_after.empty()) { + TryParseRetryAfterMs(decision.retry_after, floor_ms); + } + RandomEngine random; + const auto sleep_ms = + BackoffMs(retry_wait_ms, retry_backoff, retries_used, floor_ms, random.NextRandomInteger64(), throttled); + if (sleep_ms > 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms)); } -#else - (void)retry_wait_ms; - (void)retry_backoff; #endif return true; } diff --git a/src/duckdb/src/main/http/http_transport_manager.cpp b/src/duckdb/src/main/http/http_transport_manager.cpp index c61daaa6e..ac7658fc5 100644 --- a/src/duckdb/src/main/http/http_transport_manager.cpp +++ b/src/duckdb/src/main/http/http_transport_manager.cpp @@ -204,7 +204,7 @@ HTTPTransportManager::HTTPTransportManager(const shared_ptr &initial_h } auto reuse_policy = initial_http_util->GetTransportReusePolicy(); ValidateReusePolicy(reuse_policy); - providers.push_back(make_uniq(initial_http_util, reuse_policy, 0)); + providers.push_back(make_uniq(initial_http_util, reuse_policy, idx_t(0))); } HTTPTransportManager::~HTTPTransportManager() { @@ -213,7 +213,8 @@ HTTPTransportManager::~HTTPTransportManager() { D_ASSERT(clients.IsEmpty()); } -idx_t HTTPTransportManager::CalculateCapacity(idx_t system_concurrency, optional_idx file_descriptor_limit) { +idx_t HTTPTransportManager::CalculateCapacity(idx_t system_concurrency, optional_idx file_descriptor_limit, + idx_t io_concurrency) { idx_t cpu_target; if (system_concurrency >= 128) { cpu_target = HTTP_TRANSPORT_MAX_CAPACITY; @@ -221,6 +222,8 @@ idx_t HTTPTransportManager::CalculateCapacity(idx_t system_concurrency, optional cpu_target = system_concurrency * 2; cpu_target = MaxValue(cpu_target, 16); } + // every async I/O thread can hold a client at once, so do not let the pool be the smaller of the two + cpu_target = MaxValue(cpu_target, io_concurrency); if (!file_descriptor_limit.IsValid()) { return cpu_target; } @@ -255,8 +258,13 @@ bool HTTPTransportManager::AdvanceConnectionEpoch(uint64_t &connection_epoch, bo return false; } -void HTTPTransportManager::Initialize(idx_t system_concurrency) { - auto new_capacity = CalculateCapacity(system_concurrency, GetFileDescriptorLimit()); +void HTTPTransportManager::Initialize(const DBConfig &config) { + auto new_capacity = config.options.http_client_pool_capacity == DConstants::INVALID_INDEX + ? AutomaticCapacity(config) + : config.options.http_client_pool_capacity; + if (new_capacity == 0) { + throw InvalidInputException("The HTTP client pool capacity must be at least 1"); + } HTTPClientPool new_clients(new_capacity); annotated_lock_guard guard(lock); @@ -270,6 +278,36 @@ void HTTPTransportManager::Initialize(idx_t system_concurrency) { initialized = true; } +idx_t HTTPTransportManager::AutomaticCapacity(const DBConfig &config) { + auto &fs = *config.file_system; + auto io_concurrency = config.options.async_threads == DConstants::INVALID_INDEX + ? DBConfig::GetSystemMaxAsyncThreads(fs) + : config.options.async_threads; + return CalculateCapacity(DBConfig::GetSystemMaxThreads(fs), GetFileDescriptorLimit(), io_concurrency); +} + +idx_t HTTPTransportManager::GetCapacity() const { + annotated_lock_guard guard(lock); + return clients.GetCapacity(); +} + +void HTTPTransportManager::SetCapacity(idx_t capacity) { + if (capacity == 0) { + throw InvalidInputException("The HTTP client pool capacity must be at least 1"); + } + { + annotated_lock_guard guard(lock); + if (!initialized) { + throw InternalException("HTTP transport manager capacity was set before initialization"); + } + if (clients.IsClosed()) { + return; + } + clients.SetCapacity(capacity); + } + DisposeIdle(IdleFilter::EXCESS); +} + HTTPTransportManager::SessionReservation HTTPTransportManager::ReserveSession() { annotated_lock_guard guard(lock); if (!initialized) { @@ -469,8 +507,9 @@ unique_ptr HTTPTransportManager::PerformRequest(Session &session, } bool HTTPTransportManager::CanReturnClientLocked(const Lease &lease, bool cleanup_succeeded) const { - if (!cleanup_succeeded || !lease.reusable || !lease.client || clients.IsClosed() || !lease.state || - !lease.bucket.IsValid() || !IsStateValidLocked(*lease.state)) { + if (!cleanup_succeeded || !lease.reusable || !lease.client || clients.IsClosed() || + clients.ReservedClients() > clients.GetCapacity() || !lease.state || !lease.bucket.IsValid() || + !IsStateValidLocked(*lease.state)) { return false; } auto &state = *lease.state; diff --git a/src/duckdb/src/main/http/http_util.cpp b/src/duckdb/src/main/http/http_util.cpp index d8cb72b4e..435977999 100644 --- a/src/duckdb/src/main/http/http_util.cpp +++ b/src/duckdb/src/main/http/http_util.cpp @@ -13,22 +13,15 @@ #include "duckdb/main/http/http_retry_budget.hpp" #include "duckdb/main/settings.hpp" -#ifdef DISABLE_DUCKDB_REMOTE_INSTALL -#define DUCKDB_DISABLE_BUILTIN_HTTPLIB -#endif -#ifdef DUCKDB_DISABLE_EXTENSION_LOAD -#define DUCKDB_DISABLE_BUILTIN_HTTPLIB -#endif - -#ifndef DUCKDB_DISABLE_BUILTIN_HTTPLIB -#include "httplib.hpp" -#endif - namespace duckdb { HTTPParams::~HTTPParams() { } +bool HTTPParams::ShouldRedactLogs() const { + return !db || Settings::Get(*db); +} + idx_t HTTPParams::GetTransportReuseDomain() const { return transport_reuse_domain; } @@ -81,24 +74,6 @@ HTTPHeaders::header_values_t HTTPHeaders::GetHeaderValues(const string &key) con return {GetHeaderValue(key)}; } -#ifndef DUCKDB_DISABLE_BUILTIN_HTTPLIB -unique_ptr TransformResponse(duckdb_httplib::Result &res) { - auto status_code = HTTPUtil::ToStatusCode(res ? res->status : 0); - auto result = make_uniq(status_code); - if (res.error() == duckdb_httplib::Error::Success) { - auto &response = res.value(); - result->body = response.body; - result->reason = response.reason; - for (auto &entry : response.headers) { - result->headers.Append(entry.first, entry.second); - } - } else { - result->request_error = to_string(res.error()); - } - return result; -} -#endif - HTTPResponse::HTTPResponse(HTTPStatusCode code) : status(code) { } @@ -136,7 +111,7 @@ HTTPUtil &HTTPUtil::Get(DatabaseInstance &db) { } string HTTPUtil::GetName() const { - return "Built-In"; + return "none"; } HTTPTransportReusePolicy HTTPUtil::GetTransportReusePolicy() const { @@ -148,17 +123,26 @@ bool HTTPResponse::ShouldRetry() const { // request errors are eligible for retry return true; } + // Core judges by status only; what a service says in the body is the provider's call (HTTPUtil::ShouldRetry). + return HTTPUtil::IsRetryableStatus(status); +} + +bool HTTPUtil::IsRetryableStatus(HTTPStatusCode status) { switch (status) { case HTTPStatusCode::RequestTimeout_408: case HTTPStatusCode::ImATeapot_418: case HTTPStatusCode::TooManyRequests_429: - case HTTPStatusCode::InternalServerError_500: - case HTTPStatusCode::BadGateway_502: - case HTTPStatusCode::ServiceUnavailable_503: - case HTTPStatusCode::GatewayTimeout_504: return true; - default: + // Server errors that describe the request or the server's abilities, not a passing condition. + case HTTPStatusCode::NotImplemented_501: + case HTTPStatusCode::HttpVersionNotSupported_505: + case HTTPStatusCode::NetworkAuthenticationRequired_511: return false; + default: { + // Any other server error is eligible, as in the AWS SDKs. + const auto code = static_cast(status); + return code >= 500 && code < 600; + } } } @@ -170,12 +154,47 @@ bool HTTPUtil::ShouldRetry(const BaseRequest &request, const HTTPResponse &respo return response.ShouldRetry(); } +void HTTPUtil::PrepareAttempt(BaseRequest &request, idx_t attempt, idx_t max_attempts) { + // default: requests are sent as-is +} + +shared_ptr HTTPUtil::GetRetryQuota(const string &proto_host_port) { + lock_guard guard(retry_quota_lock); + auto entry = retry_quotas.find(proto_host_port); + if (entry != retry_quotas.end()) { + return entry->second; + } + if (retry_quotas.size() >= MAX_RETRY_QUOTAS) { + // Forget the host that has been quiet for longest; an idle quota would have been reset anyway. + const auto now_ms = TimePoint::GetTickMs(); + auto victim = retry_quotas.begin(); + for (auto it = retry_quotas.begin(); it != retry_quotas.end(); ++it) { + if (it->second->IdleMs(now_ms) > victim->second->IdleMs(now_ms)) { + victim = it; + } + } + retry_quotas.erase(victim); + } + auto quota = make_shared_ptr(); + retry_quotas.emplace(proto_host_port, quota); + return quota; +} + unique_ptr HTTPUtil::Request(BaseRequest &request) { unique_ptr client; return Request(request, client); } unique_ptr HTTPUtil::Request(BaseRequest &request, unique_ptr &client) { + if (request.params.db && !request.params.skip_external_access_check) { + // HTTP requests access an external resource - check this is allowed by the file access settings. This is the + // central check for requests that do not go through the file system (e.g. the REST catalog calls of iceberg). + auto &config = request.params.db->config; + if (!config.CanAccessFile(request.url, FileType::FILE_TYPE_REGULAR)) { + throw PermissionException("Cannot access \"%s\" - file system operations are disabled by configuration", + request.url); + } + } auto response = SendRequest(request, client); if (!response) { auto method = EnumUtil::ToString(request.type); @@ -198,108 +217,8 @@ HeadRequestInfo::~HeadRequestInfo() = default; DeleteRequestInfo::~DeleteRequestInfo() = default; PostRequestInfo::~PostRequestInfo() = default; -#ifndef DUCKDB_DISABLE_BUILTIN_HTTPLIB -class HTTPLibClient : public HTTPClient { -public: - HTTPLibClient(HTTPParams &http_params, const string &proto_host_port) : HTTPClient(proto_host_port) { - client = make_uniq(proto_host_port); - Initialize(http_params); - } - void Initialize(HTTPParams &http_params) override { - auto sec = static_cast(http_params.timeout); - auto usec = static_cast(http_params.timeout_usec); - client->set_follow_location(http_params.follow_location); - client->set_keep_alive(http_params.keep_alive); - client->set_write_timeout(sec, usec); - client->set_read_timeout(sec, usec); - client->set_connection_timeout(sec, usec); - client->set_decompress(false); - - if (!http_params.http_proxy.empty()) { - client->set_proxy(http_params.http_proxy, static_cast(http_params.http_proxy_port)); - - if (!http_params.http_proxy_username.empty()) { - client->set_proxy_basic_auth(http_params.http_proxy_username, http_params.http_proxy_password); - } - } - } - unique_ptr Get(GetRequestInfo &info) override { - auto headers = TransformHeaders(info.headers, info.params); - if (!info.response_handler && !info.content_handler) { - return TransformResult(client->Get(info.path, headers)); - } else { - return TransformResult(client->Get( - info.path, headers, - [&](const duckdb_httplib::Response &response) { - auto http_response = TransformResponse(response); - return info.response_handler(*http_response); - }, - [&](const char *data, size_t data_length) { - return info.content_handler(const_data_ptr_cast(data), data_length); - })); - } - } - unique_ptr Put(PutRequestInfo &info) override { - throw NotImplementedException("PUT request not implemented"); - } - - unique_ptr Head(HeadRequestInfo &info) override { - throw NotImplementedException("HEAD request not implemented"); - } - - unique_ptr Delete(DeleteRequestInfo &info) override { - throw NotImplementedException("DELETE request not implemented"); - } - - unique_ptr Post(PostRequestInfo &info) override { - throw NotImplementedException("POST request not implemented"); - } - - unique_ptr Options(OptionsRequestInfo &info) override { - throw NotImplementedException("OPTIONS request not implemented"); - } - - unique_ptr client; - -private: - duckdb_httplib::Headers TransformHeaders(const HTTPHeaders &header_map, const HTTPParams ¶ms) { - duckdb_httplib::Headers headers; - for (auto &entry : header_map) { - headers.insert(entry); - } - return headers; - } - - unique_ptr TransformResponse(const duckdb_httplib::Response &response) { - auto status_code = HTTPUtil::ToStatusCode(response.status); - auto result = make_uniq(status_code); - result->body = response.body; - result->reason = response.reason; - for (auto &entry : response.headers) { - result->headers.Append(entry.first, entry.second); - } - return result; - } - - unique_ptr TransformResult(const duckdb_httplib::Result &res) { - if (res.error() == duckdb_httplib::Error::Success) { - auto &response = res.value(); - return TransformResponse(response); - } else { - auto result = make_uniq(HTTPStatusCode::INVALID); - result->request_error = to_string(res.error()); - return result; - } - } -}; -#endif - unique_ptr HTTPUtil::InitializeClient(HTTPParams &http_params, const string &proto_host_port) { -#ifndef DUCKDB_DISABLE_BUILTIN_HTTPLIB - return make_uniq(http_params, proto_host_port); -#else return nullptr; -#endif } unique_ptr HTTPUtil::InitializeClientExtended(HTTPParams &http_params, const string &proto_host_port, @@ -357,7 +276,9 @@ unique_ptr HTTPUtil::SendRequest(BaseRequest &request, unique_ptr< } if (!client) { throw InvalidConfigurationException( - "HTTPClient is not been setup yet (possibly due to configuration), no HTTP request can be performed"); + "HTTP provider '%s' has no HTTP client for '%s', no HTTP request can be performed. Load an extension " + "that provides one (such as httpfs), or build with ENABLE_BUILTIN_HTTPLIB=ON", + GetName(), request.url); } } @@ -377,7 +298,7 @@ void HTTPUtil::LogRequest(BaseRequest &request, optional_ptr respo if (!request.params.logger || !request.params.logger->ShouldLog(HTTPLogType::NAME, HTTPLogType::LEVEL)) { return; } - auto log_string = HTTPLogType::ConstructLogMessage(request, response); + auto log_string = HTTPLogType::ConstructLogMessage(request, response, request.params.ShouldRedactLogs()); request.params.logger->WriteLog(HTTPLogType::NAME, HTTPLogType::LEVEL, log_string); } @@ -482,28 +403,61 @@ void HTTPUtil::DecomposeURL(const string &input, string &path_out, string &proto } } -// Retry eligible requests using an operation-local budget and the existing transport retry hook. +// Handlers turn error statuses into exceptions; rebuild the response they saw so it can be judged like a received one. +static unique_ptr ResponseFromError(const ErrorData &error) { + auto &extra_info = error.ExtraInfo(); + auto status_entry = extra_info.find("status_code"); + int32_t status_code = 0; + if (status_entry == extra_info.end() || + !TryCast::Operation(string_t(status_entry->second), status_code)) { + return nullptr; + } + auto response = make_uniq(HTTPUtil::ToStatusCode(status_code)); + for (auto &entry : extra_info) { + if (entry.first == "response_body") { + response->body = entry.second; + } else if (entry.first == "reason") { + response->reason = entry.second; + } else if (StringUtil::StartsWith(entry.first, "header_")) { + response->headers.Insert(entry.first.substr(7), entry.second); + } + } + return response; +} + +// Retry eligible requests using an operation-local budget, the host's retry quota and the transport retry hook. +// This loop is not the operation's coordinator: it judges statuses (with the provider's ShouldRetry), returns Finish() +// once it stops and records what the status says. A coordinator-owned budget shared by a provider loop +// (request.retry_budget) ignores that record and settles the quota from the provider's validated outcome; the local +// status-owned budget settles it from the status. duckdb::unique_ptr -HTTPUtil::RunRequestWithRetry(const std::function(void)> &on_request, - const BaseRequest &request, const std::function &retry_cb) { +HTTPUtil::RunRequestWithRetry(const std::function(void)> &on_request, BaseRequest &request, + const std::function &retry_cb) { auto ¶ms = request.params; HTTPRetryBudget local_retry_budget(params); auto retry_budget = request.retry_budget; if (!retry_budget) { retry_budget = local_retry_budget; } + // Charge this request's host, even when a shared budget was bound to another host before. + retry_budget->BindQuota(params.http_util.GetRetryQuota(request.proto_host_port)); unique_ptr response; std::exception_ptr caught_e; + ErrorData caught_error; string exception_error; bool should_retry = false; retry_budget->Run( [&]() { response.reset(); caught_e = nullptr; + caught_error = ErrorData(); exception_error.clear(); - string caught_status; - string caught_retry_after; + unique_ptr caught_response; + // Attempts are numbered per operation, so a re-send after a correction counts as the attempt it is; the + // bound grows with such re-sends, which do not consume retries. + const auto attempt = retry_budget->BeginAttempt(); + params.http_util.PrepareAttempt(request, attempt, retry_budget->MaxAttempts()); try { response = on_request(); if (response) { @@ -512,70 +466,101 @@ HTTPUtil::RunRequestWithRetry(const std::function(void) } catch (IOException &e) { exception_error = e.what(); caught_e = std::current_exception(); + caught_error = ErrorData(e); } catch (HTTPException &e) { exception_error = e.what(); caught_e = std::current_exception(); - // Handlers turn error statuses into exceptions; recover the throttle information. - ErrorData error_data(e); - auto entry = error_data.ExtraInfo().find("status_code"); - if (entry != error_data.ExtraInfo().end()) { - caught_status = entry->second; - } - auto retry_entry = error_data.ExtraInfo().find("header_Retry-After"); - if (retry_entry != error_data.ExtraInfo().end()) { - caught_retry_after = retry_entry->second; - } + caught_error = ErrorData(e); + caught_response = ResponseFromError(caught_error); } - // Request errors and caught exceptions are eligible without a response status. - should_retry = !response || params.http_util.ShouldRetry(request, *response); + if (response) { + should_retry = params.http_util.ShouldRetry(request, *response); + } else if (caught_response) { + // A thrown status is judged like a received one, provider rules included. + should_retry = params.http_util.ShouldRetry(request, *caught_response); + } else { + // Without a status the transport failed and nothing is known about the server. + should_retry = true; + } if (!should_retry) { - auto response_code = static_cast(response->status); - response->success = (response_code >= 200 && response_code < 300) || - response->status == HTTPStatusCode::NotModified_304; + if (response) { + auto response_code = static_cast(response->status); + response->success = (response_code >= 200 && response_code < 300) || + response->status == HTTPStatusCode::NotModified_304; + } return HTTPRetryDecision::Finish(); } if (!HTTPUtil::IsIdempotent(request.type)) { return HTTPRetryDecision::Finish(); } - const bool throttled = (response && (response->status == HTTPStatusCode::TooManyRequests_429 || - response->status == HTTPStatusCode::ServiceUnavailable_503)) || - caught_status == "429" || caught_status == "503"; - if (throttled) { - if (response && response->headers.HasHeader("Retry-After")) { - caught_retry_after = response->headers.GetHeaderValue("Retry-After"); - } - return HTTPRetryDecision::Throttled(caught_retry_after); + if (!response && !caught_response) { + // The transport failed before any status arrived: nothing is known about the server. + return HTTPRetryDecision::Transient(); + } + auto &judged = response ? *response : *caught_response; + if (judged.HasRequestError()) { + return HTTPRetryDecision::Transient(); + } + if (judged.status == HTTPStatusCode::TooManyRequests_429 || + judged.status == HTTPStatusCode::ServiceUnavailable_503) { + return HTTPRetryDecision::Throttled( + judged.headers.HasHeader("Retry-After") ? judged.headers.GetHeaderValue("Retry-After") : string()); } return HTTPRetryDecision::Retry(); }, retry_cb); - if (!should_retry) { + // With a coordinator-owned budget the outcome is settled by the coordinator once it has validated the response: + // an error document behind HTTP 200 is a failure or a retry there and must not refund the retry it consumed. + if (!should_retry && response) { + retry_budget->RecordStatusOutcome(response->success); return response; } + // The request failed: it threw, could not be retried any further, or is not replay-safe. + retry_budget->RecordStatusOutcome(false); + string quota_note; + if (retry_budget->QuotaExhausted()) { + quota_note = StringUtil::Format(" (retry quota for '%s' exhausted: requests to this host keep failing)", + request.proto_host_port); + } if (request.try_request) { if (!response) { response = make_uniq(HTTPStatusCode::INVALID); - response->request_error = exception_error.empty() ? "Unknown error" : std::move(exception_error); + response->request_error = + (exception_error.empty() ? "Unknown error" : std::move(exception_error)) + quota_note; + } else if (!quota_note.empty()) { + // A received error keeps its status; the note goes to the field GetError() reports. + if (response->HasRequestError()) { + response->request_error += quota_note; + } else { + response->reason += quota_note; + } } response->success = false; return response; } auto method = EnumUtil::ToString(request.type); if (caught_e) { - std::rethrow_exception(caught_e); + if (quota_note.empty()) { + std::rethrow_exception(caught_e); + } + // Same type and extra info as the caught exception, with the quota note appended. + if (caught_error.Type() == ExceptionType::HTTP) { + throw HTTPException(caught_error.ExtraInfo(), caught_error.RawMessage() + quota_note); + } + throw IOException(caught_error.ExtraInfo(), caught_error.RawMessage() + quota_note); } if (response && !response->HasRequestError()) { - throw HTTPException(*response, "Request returned HTTP %d for HTTP %s to '%s'", - static_cast(response->status), method, request.url); + throw HTTPException(*response, "Request returned HTTP %d for HTTP %s to '%s'%s", + static_cast(response->status), method, request.url, quota_note); } string error = response ? response->GetError() : "Unknown error"; - throw IOException("%s error for HTTP %s to '%s'", error, method, request.url); + throw IOException("%s error for HTTP %s to '%s'%s", error, method, request.url, quota_note); } void HTTPParams::Initialize(optional_ptr opener) { - auto db = FileOpener::TryGetDatabase(opener); + db = FileOpener::TryGetDatabase(opener); if (db) { auto &http_proxy_setting = db->config.options.http_proxy; if (!http_proxy_setting.empty()) { diff --git a/src/duckdb/src/main/materialized_query_result.cpp b/src/duckdb/src/main/materialized_query_result.cpp deleted file mode 100644 index 110c605ae..000000000 --- a/src/duckdb/src/main/materialized_query_result.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "duckdb/main/materialized_query_result.hpp" -#include "duckdb/common/to_string.hpp" -#include "duckdb/main/client_context.hpp" -#include "duckdb/common/box_renderer.hpp" - -namespace duckdb { - -MaterializedQueryResult::MaterializedQueryResult(StatementType statement_type, StatementProperties properties, - vector names_p, - unique_ptr collection_p, - ClientProperties client_properties) - : QueryResult(QueryResultType::MATERIALIZED_RESULT, statement_type, std::move(properties), collection_p->Types(), - std::move(names_p), std::move(client_properties)), - collection(std::move(collection_p)), scan_initialized(false) { -} - -MaterializedQueryResult::MaterializedQueryResult(ErrorData error) - : QueryResult(QueryResultType::MATERIALIZED_RESULT, std::move(error)), scan_initialized(false) { -} - -string MaterializedQueryResult::ToString() { - string result; - if (!HasError()) { - result = HeaderToString(); - result += "[ Rows: " + to_string(collection->Count()) + "]\n"; - auto &coll = Collection(); - for (auto &row : coll.Rows()) { - for (idx_t col_idx = 0; col_idx < coll.ColumnCount(); col_idx++) { - if (col_idx > 0) { - result += "\t"; - } - auto val = row.GetValue(col_idx); - result += val.IsNull() ? "NULL" : StringUtil::Replace(val.ToString(), string("\0", 1), "\\0"); - } - result += "\n"; - } - result += "\n"; - } else { - result = GetError() + "\n"; - } - return result; -} - -string MaterializedQueryResult::ToBox(BoxRendererContext &context, const BoxRendererConfig &config) { - if (HasError()) { - return GetError() + "\n"; - } - if (!collection) { - return "Internal error - result was successful but there was no collection"; - } - BoxRenderer renderer(config); - ColumnDataCollectionWrapper wrapper(Collection()); - return renderer.ToString(context, IdentifiersToStrings(GetNames()), wrapper); -} - -Value MaterializedQueryResult::GetValue(idx_t column, idx_t index) { - if (!row_collection) { - row_collection = make_uniq(collection->GetRows()); - } - return row_collection->GetValue(column, index); -} - -idx_t MaterializedQueryResult::RowCount() const { - return collection ? collection->Count() : 0; -} - -ColumnDataCollection &MaterializedQueryResult::Collection() { - if (HasError()) { - throw InvalidInputException("Attempting to get collection from an unsuccessful query result\n: Error %s", - GetError()); - } - if (!collection) { - throw InternalException("Missing collection from materialized query result"); - } - return *collection; -} - -unique_ptr MaterializedQueryResult::TakeCollection() { - if (HasError()) { - throw InvalidInputException("Attempting to get collection from an unsuccessful query result\n: Error %s", - GetError()); - } - if (!collection) { - throw InternalException("Missing collection from materialized query result"); - } - return std::move(collection); -} - -unique_ptr MaterializedQueryResult::FetchInternal() { - if (HasError()) { - throw InvalidInputException("Attempting to fetch from an unsuccessful query result\nError: %s", GetError()); - } - auto result = make_uniq(); - collection->InitializeScanChunk(*result); - if (!scan_initialized) { - // we disallow zero copy so the chunk is independently usable even after the result is destroyed - collection->InitializeScan(scan_state, ColumnDataScanProperties::DISALLOW_ZERO_COPY); - scan_initialized = true; - } - collection->Scan(scan_state, *result); - if (result->size() == 0) { - return nullptr; - } - return result; -} - -} // namespace duckdb diff --git a/src/duckdb/src/main/parser_context.cpp b/src/duckdb/src/main/parser_context.cpp new file mode 100644 index 000000000..ea064a47c --- /dev/null +++ b/src/duckdb/src/main/parser_context.cpp @@ -0,0 +1,110 @@ +#include "duckdb/parser/parser.hpp" +#include "duckdb/parser/peg/compiled_grammar.hpp" +#include "duckdb/parser/peg/dialect_extension.hpp" +#include "duckdb/main/client_context.hpp" +#include "duckdb/main/client_config.hpp" +#include "duckdb/main/database.hpp" +#include "duckdb/main/extension_callback_manager.hpp" +#include "duckdb/parser/grammar_extension.hpp" +#include "duckdb/parser/peg/passthrough_dialect.hpp" + +namespace duckdb { + +Parser::Parser(ClientContext &context) : Parser(context.GetParserOptions()) { +} + +Parser::Parser(ClientContext &context, IdentifierCaseMode identifier_case_mode) : Parser(context) { + options.identifier_case_mode = identifier_case_mode; +} + +shared_ptr CompiledGrammar::Get(ClientContext &context) { + auto &client_config = ClientConfig::GetConfig(context); + auto &callback_manager = ExtensionCallbackManager::Get(context); + if (client_config.connected_grammar) { + // while CONNECT-ed, the database being talked to decides how its statements are parsed + return client_config.connected_grammar; + } + if (client_config.current_dialect) { + auto dialect_extension = callback_manager.GetDialectExtension(*client_config.current_dialect); + if (!dialect_extension) { + throw InternalException("Dialect extension set to '%s' but couldn't be located in the registry", + *client_config.current_dialect); + } + return dialect_extension->GetCompiledGrammar(context); + } + if (client_config.cached_grammar) { + return client_config.cached_grammar; + } + return DatabaseInstance::GetDatabase(context).GetParserCache().GetMatcher(); +} + +shared_ptr CompiledGrammar::Create(const ClientContext &context, + const vector &active_extensions) { + return context.db->GetParserCache().GetMatcher(context, active_extensions); +} + +ParserCache::ParserCache() { +} + +ParserCache::~ParserCache() { +} + +shared_ptr ParserCache::GetMatcher() { + { + std::unique_lock lock(mutex); + if (matcher) { + return matcher; + } + } + auto new_matcher = CompiledGrammar::DefaultGrammar(); + + std::unique_lock lock(mutex); + if (!matcher) { + matcher = std::move(new_matcher); + } + return matcher; +} + +shared_ptr ParserCache::GetMatcher(const ClientContext &context, + const vector &active_extensions) { + vector> selected_extensions; + vector extension_names; + auto &callback_manager = ExtensionCallbackManager::Get(context); + for (auto &name : active_extensions) { + auto grammar_extension = callback_manager.FindGrammarExtension(name); + if (grammar_extension) { + selected_extensions.emplace_back(*grammar_extension); + extension_names.push_back(grammar_extension->Name()); + } + } + // compile while holding the lock, so concurrent first uses of the same extensions only compile once + lock_guard lock(extension_mutex); + for (idx_t i = 0; i < extension_matchers.size(); i++) { + if (extension_matchers[i].first == extension_names) { + // move the entry to the back, which holds the most recently used grammar + auto entry = std::move(extension_matchers[i]); + extension_matchers.erase(extension_matchers.begin() + NumericCast(i)); + extension_matchers.push_back(std::move(entry)); + return extension_matchers.back().second; + } + } + // compile before evicting, so a grammar that fails to compile does not evict a valid one + auto new_matcher = CompiledGrammar::Create(selected_extensions); + if (extension_matchers.size() >= MAX_CACHED_EXTENSION_MATCHERS) { + // connections that still use the evicted grammar keep their own reference to it + extension_matchers.erase(extension_matchers.begin()); + } + extension_matchers.emplace_back(std::move(extension_names), std::move(new_matcher)); + return extension_matchers.back().second; +} + +shared_ptr ParserCache::GetPassthroughMatcher(const ClientContext &context) { + lock_guard lock(passthrough_mutex); + if (!passthrough_dialect) { + passthrough_dialect = make_uniq(); + } + // the dialect caches the compiled grammar itself + return passthrough_dialect->GetCompiledGrammar(context); +} + +} // namespace duckdb diff --git a/src/duckdb/src/main/pending_query_result.cpp b/src/duckdb/src/main/pending_query_result.cpp deleted file mode 100644 index 2ad1df1f7..000000000 --- a/src/duckdb/src/main/pending_query_result.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "duckdb/main/pending_query_result.hpp" -#include "duckdb/main/client_context.hpp" -#include "duckdb/main/prepared_statement_data.hpp" -#include "duckdb/main/materialized_query_result.hpp" -#include "duckdb/main/stream_query_result.hpp" - -namespace duckdb { - -PendingQueryResult::PendingQueryResult(shared_ptr context_p, PreparedStatementData &statement, - vector types_p, bool allow_stream_result) - : BaseQueryResult(QueryResultType::PENDING_RESULT, statement.statement_type, statement.properties, - std::move(types_p), statement.names), - context(std::move(context_p)), allow_stream_result(allow_stream_result) { -} - -PendingQueryResult::PendingQueryResult(ErrorData error) - : BaseQueryResult(QueryResultType::PENDING_RESULT, std::move(error)) { -} - -PendingQueryResult::~PendingQueryResult() { -} - -unique_ptr PendingQueryResult::LockContext() { - if (!context) { - if (HasError()) { - throw InvalidInputException( - "Attempting to execute an unsuccessful or closed pending query result\nError: %s", GetError()); - } - throw InvalidInputException("Attempting to execute an unsuccessful or closed pending query result"); - } - return context->LockContext(); -} - -void PendingQueryResult::CheckExecutableInternal(ClientContextLock &lock) { - bool invalidated = HasError() || !context; - if (!invalidated) { - invalidated = !context->IsActiveResult(lock, *this); - } - if (invalidated) { - if (HasError()) { - throw InvalidInputException( - "Attempting to execute an unsuccessful or closed pending query result\nError: %s", GetError()); - } - throw InvalidInputException("Attempting to execute an unsuccessful or closed pending query result"); - } -} - -void PendingQueryResult::WaitForTask() { - auto lock = LockContext(); - context->WaitForTask(*lock, *this); -} - -PendingExecutionResult PendingQueryResult::ExecuteTask() { - auto lock = LockContext(); - return ExecuteTaskInternal(*lock); -} - -PendingExecutionResult PendingQueryResult::CheckPulse() { - auto lock = LockContext(); - CheckExecutableInternal(*lock); - return context->ExecuteTaskInternal(*lock, *this, true); -} - -bool PendingQueryResult::AllowStreamResult() const { - return allow_stream_result; -} - -PendingExecutionResult PendingQueryResult::ExecuteTaskInternal(ClientContextLock &lock) { - CheckExecutableInternal(lock); - return context->ExecuteTaskInternal(lock, *this, false); -} - -unique_ptr PendingQueryResult::ExecuteInternal(ClientContextLock &lock) { - CheckExecutableInternal(lock); - - PendingExecutionResult execution_result; - while (!IsResultReady(execution_result = ExecuteTaskInternal(lock))) { - if (execution_result == PendingExecutionResult::BLOCKED) { - CheckExecutableInternal(lock); - context->WaitForTask(lock, *this); - } - } - if (HasError()) { - if (allow_stream_result) { - return make_uniq(GetErrorObject()); - } else { - return make_uniq(GetErrorObject()); - } - } - auto result = context->FetchResultInternal(lock, *this); - // release our context reference (cannot use Close(): the context lock is already held here) - context.reset(); - return result; -} - -unique_ptr PendingQueryResult::Execute() { - auto lock = LockContext(); - return ExecuteInternal(*lock); -} - -void PendingQueryResult::Close() { - if (context) { - auto lock = LockContext(); - if (context->IsActiveResult(*lock, *this)) { - // Abandoned before execution finished: release the active-query state now (matching - // InitialCleanup) instead of leaking it until the next query or context teardown. - context->CleanupInternal(*lock, this, false); - } - } - context.reset(); -} - -bool PendingQueryResult::IsResultReady(PendingExecutionResult result) { - return (IsExecutionFinished(result) || result == PendingExecutionResult::RESULT_READY); -} - -bool PendingQueryResult::IsExecutionFinished(PendingExecutionResult result) { - return (result == PendingExecutionResult::EXECUTION_FINISHED || result == PendingExecutionResult::EXECUTION_ERROR); -} - -} // namespace duckdb diff --git a/src/duckdb/src/main/prepared_statement.cpp b/src/duckdb/src/main/prepared_statement.cpp index 52966aa54..4851d03c4 100644 --- a/src/duckdb/src/main/prepared_statement.cpp +++ b/src/duckdb/src/main/prepared_statement.cpp @@ -1,6 +1,7 @@ #include "duckdb/main/prepared_statement.hpp" #include "duckdb/common/exception.hpp" #include "duckdb/main/client_context.hpp" +#include "duckdb/parser/sql_statement.hpp" #include "duckdb/parser/statement/execute_statement.hpp" namespace duckdb { @@ -11,10 +12,24 @@ PreparedStatement::PreparedStatement(const shared_ptr &context_p, D_ASSERT(!context.expired()); } +PreparedStatement::PreparedStatement(const shared_ptr &context_p, unique_ptr statement) + : context(context_p), query(statement->query), success(true), expanding_statement(std::move(statement)) { + D_ASSERT(!context.expired()); + // The placeholder the planner reports for a result known only at execution. The properties describe nothing + info.names = {Identifier("unknown")}; + info.types = {LogicalTypeId::UNKNOWN}; + info.statement_type = expanding_statement->type; + info.properties.always_require_rebind = true; +} + PreparedStatement::PreparedStatement(ErrorData error) : success(false), error(std::move(error)) { } PreparedStatement::~PreparedStatement() { + if (expanding_statement) { + // nothing was prepared in the client context + return; + } auto client_context = context.lock(); if (!client_context) { // the client context is gone - the prepared statement has been deallocated together with it @@ -93,8 +108,18 @@ case_insensitive_map_t PreparedStatement::GetExpectedParameterTypes return expected_types; } -unique_ptr -PreparedStatement::CreateExecuteStatement(const identifier_map_t &named_values) const { +bool PreparedStatement::MetadataAvailable() const { + return !expanding_statement && info.properties.bound_all_parameters; +} + +unique_ptr PreparedStatement::CreateStatement(identifier_map_t &named_values, + QueryParameters ¶meters) const { + if (expanding_statement) { + if (!named_values.empty()) { + parameters.statement_args = named_values; + } + return expanding_statement->Copy(); + } auto execute = make_uniq(); execute->name = Identifier(name); // report the query that was prepared - not the generated EXECUTE - in errors and profiling output @@ -106,55 +131,53 @@ PreparedStatement::CreateExecuteStatement(const identifier_map_t PreparedStatement::Execute(identifier_map_t &named_values, - bool allow_stream_result) { + const QueryParameters &query_parameters) { if (!success) { - return make_uniq( + return make_uniq( ErrorData(InvalidInputException("Attempting to execute an unsuccessfully prepared statement!"))); } auto client_context = context.lock(); if (!client_context) { - return make_uniq(ErrorData( + return make_uniq(ErrorData( InvalidInputException("Attempting to execute a prepared statement after its connection was closed!"))); } - PendingQueryParameters parameters; - parameters.query_parameters.output_type = - allow_stream_result ? QueryResultOutputType::ALLOW_STREAMING : QueryResultOutputType::FORCE_MATERIALIZED; - return client_context->RunInternalStatement(CreateExecuteStatement(named_values), parameters); + auto parameters = query_parameters; + auto statement = CreateStatement(named_values, parameters); + return client_context->RunInternalStatement(std::move(statement), parameters); } -unique_ptr PreparedStatement::Execute(vector &values, bool allow_stream_result) { +unique_ptr PreparedStatement::Execute(vector &values, const QueryParameters &query_parameters) { identifier_map_t named_values; for (idx_t i = 0; i < values.size(); i++) { named_values[Identifier(std::to_string(i + 1))] = BoundParameterData(values[i]); } - return Execute(named_values, allow_stream_result); + return Execute(named_values, query_parameters); } -unique_ptr PreparedStatement::PendingQuery(vector &values, bool allow_stream_result) { +unique_ptr PreparedStatement::Submit(vector &values, const QueryParameters &query_parameters) { identifier_map_t named_values; for (idx_t i = 0; i < values.size(); i++) { auto &val = values[i]; named_values[Identifier(std::to_string(i + 1))] = BoundParameterData(val); } - return PendingQuery(named_values, allow_stream_result); + return Submit(named_values, query_parameters); } -unique_ptr PreparedStatement::PendingQuery(identifier_map_t &named_values, - bool allow_stream_result) { +unique_ptr PreparedStatement::Submit(identifier_map_t &named_values, + const QueryParameters &query_parameters) { if (!success) { auto exception = InvalidInputException("Attempting to execute an unsuccessfully prepared statement!"); - return make_uniq(ErrorData(exception)); + return make_uniq(ErrorData(exception)); } auto client_context = context.lock(); if (!client_context) { auto exception = InvalidInputException("Attempting to execute a prepared statement after its connection was closed!"); - return make_uniq(ErrorData(exception)); + return make_uniq(ErrorData(exception)); } - PendingQueryParameters parameters; - parameters.query_parameters.output_type = - allow_stream_result ? QueryResultOutputType::ALLOW_STREAMING : QueryResultOutputType::FORCE_MATERIALIZED; - return client_context->PendingInternalStatement(CreateExecuteStatement(named_values), parameters); + auto parameters = query_parameters; + auto statement = CreateStatement(named_values, parameters); + return client_context->SubmitInternalStatement(std::move(statement), parameters); } } // namespace duckdb diff --git a/src/duckdb/src/main/profiler/gathered_metrics.cpp b/src/duckdb/src/main/profiler/gathered_metrics.cpp index dc8f5f98f..e58f4e541 100644 --- a/src/duckdb/src/main/profiler/gathered_metrics.cpp +++ b/src/duckdb/src/main/profiler/gathered_metrics.cpp @@ -105,12 +105,15 @@ void GatheredMetrics::SetMetric(const string &key, const string &value) { metrics[key] = Value(value); } -void GatheredMetrics::WriteMetricsToLog(ClientContext &context) const { +void GatheredMetrics::WriteMetricsToLog(ClientContext &context, + const unordered_map &timings) const { auto &logger = Logger::Get(context); if (logger.ShouldLog(MetricsLogType::NAME, MetricsLogType::LEVEL)) { for (const auto &entry : metrics) { + auto timing = timings.find(entry.first); logger.WriteLog(MetricsLogType::NAME, MetricsLogType::LEVEL, - MetricsLogType::ConstructLogMessage(entry.first, entry.second)); + MetricsLogType::ConstructLogMessage(entry.first, entry.second, + timing == timings.end() ? nullptr : &timing->second)); } } } diff --git a/src/duckdb/src/main/profiler/profiling_utils.cpp b/src/duckdb/src/main/profiler/profiling_utils.cpp index 15a460c13..af3e532a5 100644 --- a/src/duckdb/src/main/profiler/profiling_utils.cpp +++ b/src/duckdb/src/main/profiler/profiling_utils.cpp @@ -5,8 +5,8 @@ namespace duckdb { void QueryMetrics::FinalizeMetrics(GatheredMetrics &info) { info.SetMetric(query_sql); - for (const auto &[key, ns] : string_timings) { - info.SetMetric(key, static_cast(ns) / 1e9); + for (const auto &[key, timing] : string_timings) { + info.SetMetric(key, static_cast(timing.duration) / 1e9); } for (const auto &[key, count] : string_counters) { info.SetMetric(key, count); diff --git a/src/duckdb/src/main/query_profiler.cpp b/src/duckdb/src/main/query_profiler.cpp index c3d1ea27e..4036cad2a 100644 --- a/src/duckdb/src/main/query_profiler.cpp +++ b/src/duckdb/src/main/query_profiler.cpp @@ -160,7 +160,7 @@ void QueryProfiler::StartQuery(const string &query, bool is_explain_analyze_p, b } Start(query); } -void QueryProfiler::AddParserTime(const Profiler &parser_timer) { +void QueryProfiler::AddParserTime(const Profiler &parser_timer, timestamp_t start_time, timestamp_t end_time) { if (!running || !IsEnabled()) { return; } @@ -168,7 +168,7 @@ void QueryProfiler::AddParserTime(const Profiler &parser_timer) { if (!parser_time_ns) { return; } - query_metrics.UpdateMetric(MetricParserTotalTime::Name, parser_time_ns); + query_metrics.UpdateMetric(MetricParserTotalTime::Name, parser_time_ns, start_time, end_time); } bool QueryProfiler::OperatorRequiresProfiling(const PhysicalOperatorType op_type) { @@ -547,6 +547,9 @@ void OperatorProfiler::Flush(const PhysicalOperator &phys_op) { } void QueryProfiler::Flush(OperatorProfiler &profiler) { + if (!running.load(std::memory_order_relaxed)) { + return; + } lock_guard guard(lock); if (!IsEnabled() || !running) { return; @@ -579,6 +582,9 @@ void QueryProfiler::Flush(OperatorProfiler &profiler) { } void QueryProfiler::SetBlockedTime(const double &blocked_thread_time) { + if (!running.load(std::memory_order_relaxed)) { + return; + } lock_guard guard(lock); if (!IsEnabled() || !running) { return; @@ -842,7 +848,7 @@ void QueryProfiler::ToLogInternal() const { if (!root) { return; } - metrics->WriteMetricsToLog(context); + metrics->WriteMetricsToLog(context, query_metrics.GetMetricTimings()); } void QueryProfiler::ToLog() const { diff --git a/src/duckdb/src/main/query_result.cpp b/src/duckdb/src/main/query_result.cpp index e6a4b2137..e98b39e9a 100644 --- a/src/duckdb/src/main/query_result.cpp +++ b/src/duckdb/src/main/query_result.cpp @@ -1,24 +1,59 @@ #include "duckdb/main/query_result.hpp" #include "duckdb/common/box_renderer.hpp" +#include "duckdb/common/column_data_collection_render_interface.hpp" #include "duckdb/common/printer.hpp" +#include "duckdb/common/string_util.hpp" +#include "duckdb/common/to_string.hpp" +#include "duckdb/common/types/column/column_data_collection.hpp" #include "duckdb/common/vector.hpp" +#include "duckdb/execution/executor.hpp" +#include "duckdb/main/buffered_data/buffered_data.hpp" #include "duckdb/main/client_context.hpp" +#include "duckdb/main/database.hpp" +#include "duckdb/main/prepared_statement_data.hpp" +#include "duckdb/main/retained_result_collection.hpp" + namespace duckdb { -BaseQueryResult::BaseQueryResult(QueryResultType type, StatementType statement_type, StatementProperties properties_p, +BaseQueryResult::BaseQueryResult(StatementType statement_type, StatementProperties properties_p, vector types_p, vector names_p) - : type(type), statement_type(statement_type), properties(std::move(properties_p)), types(std::move(types_p)), + : statement_type(statement_type), properties(std::move(properties_p)), types(std::move(types_p)), names(std::move(names_p)), success(true) { D_ASSERT(types.size() == names.size()); } -BaseQueryResult::BaseQueryResult(QueryResultType type, ErrorData error) - : type(type), success(false), error(std::move(error)) { +BaseQueryResult::BaseQueryResult(ErrorData error) : success(false), error(std::move(error)) { // Assert that the error object is initialized D_ASSERT(this->error.HasError()); } +BaseQueryResult::BaseQueryResult(StatementType statement_type) + : statement_type(statement_type), metadata_available(false), success(true) { +} + +void BaseQueryResult::SetMetadata(StatementProperties properties_p, vector types_p, + vector names_p) { + D_ASSERT(!metadata_available); + D_ASSERT(types_p.size() == names_p.size()); + properties = std::move(properties_p); + types = std::move(types_p); + names = std::move(names_p); + metadata_available = true; +} + +void BaseQueryResult::RequireMetadata() const { + // A result that failed reports the empty metadata of every failed result + if (!MetadataAvailable()) { + throw InvalidInputException("The metadata of this result is not available yet: the statement expands into " + "several, and the one that produces the result has not been bound"); + } +} + +bool BaseQueryResult::MetadataAvailable() const { + return metadata_available || !success; +} + BaseQueryResult::~BaseQueryResult() { } @@ -27,6 +62,12 @@ void BaseQueryResult::ThrowError(const string &prepended_message) const { error.Throw(prepended_message); } +void BaseQueryResult::ThrowIfError(const string &prepended_message) const { + if (HasError()) { + error.Throw(prepended_message); + } +} + void BaseQueryResult::SetError(ErrorData error) { success = !error.HasError(); this->error = std::move(error); @@ -55,41 +96,102 @@ const ErrorData &BaseQueryResult::GetErrorObject() const { } idx_t BaseQueryResult::ColumnCount() const { + RequireMetadata(); return types.size(); } -QueryResultType BaseQueryResult::GetResultType() const { - return type; -} - StatementType BaseQueryResult::GetStatementType() const { return statement_type; } const StatementProperties &BaseQueryResult::GetStatementProperties() const { + RequireMetadata(); return properties; } const vector &BaseQueryResult::GetTypes() const { + RequireMetadata(); return types; } const vector &BaseQueryResult::GetNames() const { + RequireMetadata(); return names; } -QueryResult::QueryResult(QueryResultType type, StatementType statement_type, StatementProperties properties, - vector types_p, vector names_p, ClientProperties client_properties_p) - : BaseQueryResult(type, statement_type, std::move(properties), std::move(types_p), std::move(names_p)), - client_properties(std::move(client_properties_p)) { +//===--------------------------------------------------------------------===// +// Construction +//===--------------------------------------------------------------------===// +QueryResult::QueryResult(shared_ptr context_p, PreparedStatementData &statement, + vector types_p, ClientProperties client_properties_p, + shared_ptr buffer_p) + : BaseQueryResult(statement.statement_type, statement.properties, std::move(types_p), statement.names), + client_properties(std::move(client_properties_p)), context(std::move(context_p)), buffer(std::move(buffer_p)) { + if (buffer) { + format = buffer->SharedFormat(); + format_state = buffer->SharedFormatState(); + } else { + format = ChunkFormat::InMemory(); + InitializeChunkFormatState(); + } } -QueryResult::QueryResult(QueryResultType type, ErrorData error) - : BaseQueryResult(type, std::move(error)), - client_properties("UTC", ArrowOffsetSize::REGULAR, false, false, false, ArrowFormatVersion::V1_0, nullptr) { +QueryResult::QueryResult(shared_ptr context_p, StatementType statement_type, + shared_ptr format_p, ClientProperties client_properties_p) + : BaseQueryResult(statement_type), client_properties(std::move(client_properties_p)), context(std::move(context_p)), + format(std::move(format_p)) { + if (!format) { + format = ChunkFormat::InMemory(); + } + // The state of a result without columns, which a failure leaves in place; the statement producing the result + // replaces it + ResultFormatContext format_context {vector(), vector(), client_properties, + ResultOrdering::UNORDERED}; + format_state = format->InitGlobal(format_context); +} + +void QueryResult::AdoptResultStatement(StatementProperties properties, PreparedStatementData &statement, + shared_ptr buffer_p) { + D_ASSERT(!buffer && buffer_p); + SetMetadata(std::move(properties), statement.types, statement.names); + buffer = std::move(buffer_p); + format = buffer->SharedFormat(); + format_state = buffer->SharedFormatState(); +} + +void QueryResult::AdoptRowlessResult(QueryResult &produced) { + SetMetadata(produced.GetStatementProperties(), produced.GetTypes(), produced.GetNames()); + format_state = produced.format_state; + AdoptCollected(produced); +} + +QueryResult::QueryResult(StatementType statement_type, StatementProperties properties, vector names_p, + unique_ptr collection_p, ClientProperties client_properties_p) + : BaseQueryResult(statement_type, std::move(properties), collection_p->Types(), std::move(names_p)), + client_properties(std::move(client_properties_p)), format(ChunkFormat::InMemory()), + collection(make_uniq(std::move(collection_p))) { + InitializeChunkFormatState(); +} + +QueryResult::QueryResult(StatementType statement_type, StatementProperties properties, vector types_p, + vector names_p, unique_ptr collection_p, + shared_ptr format_p, shared_ptr format_state_p, + ClientProperties client_properties_p) + : BaseQueryResult(statement_type, std::move(properties), std::move(types_p), std::move(names_p)), + client_properties(std::move(client_properties_p)), format(std::move(format_p)), + format_state(std::move(format_state_p)), collection(std::move(collection_p)) { + D_ASSERT(format && format_state && collection); +} + +QueryResult::QueryResult(ErrorData error) + : BaseQueryResult(std::move(error)), + client_properties("UTC", ArrowOffsetSize::REGULAR, false, false, false, ArrowFormatVersion::V1_0, nullptr), + format(ChunkFormat::InMemory()) { + InitializeChunkFormatState(); } QueryResult::~QueryResult() { + Close(); } void QueryResult::DeduplicateColumns(vector &names) { @@ -124,24 +226,355 @@ const Identifier &QueryResult::ColumnName(idx_t index) const { return names[index]; } -string QueryResult::ToBox(BoxRendererContext &context, const BoxRendererConfig &config) { - return ToString(); +//===--------------------------------------------------------------------===// +// Execution +//===--------------------------------------------------------------------===// +unique_ptr QueryResult::LockContext() { + if (!context) { + string error_str = "Attempting to execute an unsuccessful or closed query result"; + if (HasError()) { + error_str += StringUtil::Format("\nError: %s", GetError()); + } + throw InvalidInputException(error_str); + } + return context->LockContext(); +} + +bool QueryResult::IsOpenInternal(ClientContextLock &lock) { + if (HasError() || !context) { + return false; + } + return context->IsActiveResult(lock, *this); +} + +void QueryResult::CheckExecutableInternal(ClientContextLock &lock) { + if (!IsOpenInternal(lock)) { + string error_str = "Attempting to execute an unsuccessful or closed query result"; + if (HasError()) { + error_str += StringUtil::Format("\nError: %s", GetError()); + } + throw InvalidInputException(error_str); + } +} + +bool QueryResult::IsOpen() { + if (HasError() || !context) { + return false; + } + auto lock = LockContext(); + return IsOpenInternal(*lock); +} + +QueryResultState QueryResult::Cancelled() { + if (!HasError()) { + SetError( + ErrorData(ExceptionType::INTERRUPT, "The execution of the query was cancelled before it could finish")); + } + return QueryResultState::EXECUTION_ERROR; } -unique_ptr QueryResult::Fetch() { - auto chunk = FetchRaw(); - if (!chunk) { - return nullptr; +QueryResultState QueryResult::Poll() { + if (HasError()) { + return QueryResultState::EXECUTION_ERROR; + } + if (IsCollected() || !context) { + // The result was collected, or the query already ended: keep reporting the terminal state + return QueryResultState::FINISHED; } - chunk->Flatten(); - return chunk; + auto lock = LockContext(); + if (!IsOpenInternal(*lock)) { + return Cancelled(); + } + return context->PollInternal(*lock, *this); +} + +QueryResultState QueryResult::ExecuteTask() { + if (IsCollected()) { + // An earlier call ended the query: keep reporting the terminal state + return QueryResultState::FINISHED; + } + // Ending the query drops the handle's reference to the context, which the lock below outlives + auto keep_alive = context; + auto lock = LockContext(); + CheckExecutableInternal(*lock); + auto state = context->ExecuteTaskInternal(*lock, *this); + // Only a statement that sank no row finishes undecided, since producers park until the decision. A statement + // that expands into several without returning rows never has a buffer + if (state == QueryResultState::FINISHED && (!buffer || buffer->Lifetime() != ResultLifetime::DRAINING)) { + return EndFinishedInternal(*lock); + } + return state; +} + +void QueryResult::WaitForTask() { + if (!context) { + return; + } + auto lock = LockContext(); + if (!IsOpenInternal(*lock)) { + return; + } + context->WaitForTask(*lock, *this); +} + +void QueryResult::Close() { + if (buffer) { + buffer->Close(); + } + if (context) { + auto lock = context->LockContext(); + if (context->IsActiveResult(*lock, *this)) { + // No call ended the query, so it was abandoned and nothing it ran may be committed + context->AbortInternal(*lock); + } + } + context.reset(); +} + +//===--------------------------------------------------------------------===// +// Format +//===--------------------------------------------------------------------===// +void QueryResult::InitializeChunkFormatState() { + ResultFormatContext format_context {GetTypes(), GetNames(), client_properties, ResultOrdering::UNORDERED}; + format_state = format->InitGlobal(format_context); +} + +const ResultFormat &QueryResult::Format() const { + D_ASSERT(format); + return *format; +} + +void QueryResult::AdoptCollected(QueryResult &produced) { + collection = std::move(produced.collection); +} + +void QueryResult::ThrowFormatMismatch(const char *expected) const { + throw InvalidInputException("This query result is in the \"%s\" format, but it was asked for the \"%s\" format", + Format().Name(), expected); +} + +const ResultFormatGlobalState &QueryResult::CheckedFormatState(const char *expected) const { + if (!StringUtil::Equals(Format().Name(), expected)) { + ThrowFormatMismatch(expected); + } + RequireMetadata(); + D_ASSERT(format_state); + return *format_state; +} + +void QueryResult::PrepareCollected(const char *expected) { + Complete(); + if (HasError()) { + throw InvalidInputException("Attempting to get collection from an unsuccessful query result\n: Error %s", + GetError()); + } + if (!StringUtil::Equals(Format().Name(), expected)) { + ThrowFormatMismatch(expected); + } + if (!IsCollected()) { + ThrowNoCollection(); + } +} + +//===--------------------------------------------------------------------===// +// Retention +//===--------------------------------------------------------------------===// +void QueryResult::Materialize() { + if (IsCollected() || HasError() || !context) { + return; + } + auto lock = LockContext(); + if (!IsOpenInternal(*lock)) { + Cancelled(); + context.reset(); + return; + } + SettleRetainedInternal(*lock); +} + +void QueryResult::SettleRetainedInternal(ClientContextLock &lock) { + if (buffer) { + buffer->Decide(ResultLifetime::RETAINED); + return; + } + // The statement that produces the result has not started: its buffer takes the decision when it is created + context->DecideResultLifetimeInternal(lock, ResultLifetime::RETAINED); +} + +void QueryResult::Complete() { + if (IsCollected() || HasError() || !context) { + return; + } + // The handle may hold the last reference to the context, which the lock below outlives + auto keep_alive = context; + auto lock = keep_alive->LockContext(); + CompleteInternal(*lock); +} + +void QueryResult::CompleteInternal(ClientContextLock &lock) { + if (IsCollected() || HasError() || !context) { + return; + } + if (!IsOpenInternal(lock)) { + Cancelled(); + context.reset(); + return; + } + SettleRetainedInternal(lock); + try { + QueryResultState state; + while (!IsTerminal(state = context->ExecuteTaskInternal(lock, *this))) { + if (state == QueryResultState::BLOCKED || state == QueryResultState::READY) { + context->WaitForTask(lock, *this); + } + } + if (state == QueryResultState::FINISHED) { + EndFinishedInternal(lock); + } + } catch (std::exception &ex) { + AbandonAfterThrowInternal(lock, ErrorData(ex)); + throw; + } catch (...) { // LCOV_EXCL_START + AbandonAfterThrowInternal(lock, ErrorData("Unhandled exception while completing the query")); + throw; + } // LCOV_EXCL_STOP + context.reset(); +} + +void QueryResult::AbandonAfterThrowInternal(ClientContextLock &lock, ErrorData error) { + // the caller holds the context lock - clean up with it here, as closing this result would lock it again + try { + if (context->IsActiveResult(lock, *this)) { + if (!HasError()) { + // Ended as failed, so nothing the query ran is committed + SetError(std::move(error)); + } + context->CleanupInternal(lock, this, true); + } + } catch (...) { // NOLINT + } + context.reset(); +} + +QueryResultState QueryResult::EndFinishedInternal(ClientContextLock &lock) { + auto produced = context->TakeFinishedResultInternal(lock); + // Cleanup can fail on an autocommit commit; it records the error on this result + context->CleanupInternal(lock, this, false); + if (!HasError()) { + if (MetadataAvailable()) { + AdoptCollected(*produced); + } else { + AdoptRowlessResult(*produced); + } + } + context.reset(); + return HasError() ? QueryResultState::EXECUTION_ERROR : QueryResultState::FINISHED; +} + +void QueryResult::ThrowNoCollection() const { + throw InvalidInputException("This query result no longer holds a collection: it was taken with TakeCollection, or " + "the result was closed before it was collected"); +} + +idx_t QueryResult::RowCount() { + Complete(); + if (HasError()) { + return 0; + } + if (!IsCollected()) { + ThrowNoCollection(); + } + return collection->Count(); +} + +//===--------------------------------------------------------------------===// +// Fetch +//===--------------------------------------------------------------------===// +void QueryResult::EndQuery(ClientContextLock &lock, bool invalidate_transaction) { + context->CleanupInternal(lock, this, invalidate_transaction); +} + +void QueryResult::HandleFetchFailure(ClientContextLock &lock, ErrorData error) { + if (!context || !context->IsActiveResult(lock, *this)) { + // The query already ended, so the failure is only recorded, unless an earlier one is + if (!HasError()) { + SetError(std::move(error)); + } + return; + } + bool invalidate_query = true; + if (!context->ErrorInvalidatesQueryTransaction(error.Type())) { + // standard exceptions do not invalidate the current transaction + invalidate_query = false; + } else if (Exception::InvalidatesDatabase(error.Type())) { + // fatal exceptions invalidate the entire database + auto &db_instance = DatabaseInstance::GetDatabase(*context); + ValidChecker::Invalidate(db_instance, error.RawMessage()); + } + context->ProcessError(error, context->GetCurrentQuery()); + SetError(std::move(error)); + context->CleanupInternal(lock, this, invalidate_query); } unique_ptr QueryResult::FetchRaw() { - return FetchInternal(); + Complete(); + if (HasError()) { + throw InvalidInputException("Attempting to fetch from an unsuccessful query result\nError: %s", GetError()); + } + if (!Format().Is()) { + ThrowFormatMismatch(ChunkFormat::NAME); + } + if (!collection) { + ThrowNoCollection(); + } + return collection->Cast().FetchRaw(); +} + +//===--------------------------------------------------------------------===// +// Rendering +//===--------------------------------------------------------------------===// +string QueryResult::ToString() { + // The metadata of a statement that expands into several is known once it runs + Complete(); + if (HasError()) { + return GetError() + "\n"; + } + string result = HeaderToString(); + if (!Format().Is()) { + return result + "[ Rows: " + to_string(RowCount()) + "]\n\n"; + } + auto &coll = Collection(); + result += "[ Rows: " + to_string(coll.Count()) + "]\n"; + for (auto &row : coll.Rows()) { + for (idx_t col_idx = 0; col_idx < coll.ColumnCount(); col_idx++) { + if (col_idx > 0) { + result += "\t"; + } + auto val = row.GetValue(col_idx); + result += val.IsNull() ? "NULL" : StringUtil::Replace(val.ToString(), string("\0", 1), "\\0"); + } + result += "\n"; + } + result += "\n"; + return result; +} + +string QueryResult::ToBox(BoxRendererContext &context_p, const BoxRendererConfig &config) { + Complete(); + if (HasError()) { + return GetError() + "\n"; + } + if (!Format().Is()) { + return HeaderToString() + "[ Rows: " + to_string(RowCount()) + "]\n\n"; + } + BoxRenderer renderer(config); + ColumnDataCollectionWrapper wrapper(Collection()); + return renderer.ToString(context_p, IdentifiersToStrings(GetNames()), wrapper); } bool QueryResult::Equals(QueryResult &other, bool compare_names) { // LCOV_EXCL_START + Complete(); + other.Complete(); // first compare the success state of the results if (HasError() != other.HasError()) { return false; @@ -149,6 +582,9 @@ bool QueryResult::Equals(QueryResult &other, bool compare_names) { // LCOV_EXCL_ if (HasError()) { return GetErrorObject() == other.GetErrorObject(); } + if (!Format().Is() || !other.Format().Is()) { + throw InvalidInputException("Query results can only be compared in the chunk format"); + } // compare names if (compare_names && GetNames() != other.GetNames()) { return false; diff --git a/src/duckdb/src/main/query_result_stream.cpp b/src/duckdb/src/main/query_result_stream.cpp new file mode 100644 index 000000000..5770b2fca --- /dev/null +++ b/src/duckdb/src/main/query_result_stream.cpp @@ -0,0 +1,307 @@ +#include "duckdb/main/query_result_stream.hpp" + +#include "duckdb/common/string_util.hpp" +#include "duckdb/execution/executor.hpp" +#include "duckdb/main/buffered_data/buffered_data.hpp" +#include "duckdb/main/client_context.hpp" +#include "duckdb/main/result_unit.hpp" + +namespace duckdb { + +bool StreamsByDefault(StatementType written_type, const QueryResult &result, bool reads_to_the_end) { + // Checked first: a statement that expands into several is no SELECT, and its properties may not be known yet + if (written_type != StatementType::SELECT_STATEMENT) { + return false; + } + auto &properties = result.GetStatementProperties(); + if (properties.return_type != StatementReturnType::QUERY_RESULT) { + return false; + } + return reads_to_the_end || properties.IsReadOnly(); +} + +ResultStreamBase::ResultStreamBase(unique_ptr result, const char *expected_format) + : handle(std::move(result)) { + if (!handle) { + throw InvalidInputException("Attempting to open a stream on a query result that does not exist"); + } + if (handle->HasError()) { + throw InvalidInputException("Attempting to open a stream on an unsuccessful query result\nError: %s", + handle->GetError()); + } + if (handle->HasBufferedData()) { + if (handle->GetBufferedData().Decide(ResultLifetime::DRAINING) != ResultLifetime::DRAINING) { + throw InvalidInputException("Attempting to open a stream on a query result that is being retained"); + } + } else if (handle->context && !handle->MetadataAvailable()) { + // A statement that expands into several: the statement producing the result takes the decision when it + // starts + auto keep_alive = handle->context; + auto lock = keep_alive->LockContext(); + handle->CheckExecutableInternal(*lock); + if (keep_alive->DecideResultLifetimeInternal(*lock, ResultLifetime::DRAINING) != ResultLifetime::DRAINING) { + throw InvalidInputException("Attempting to open a stream on a query result that is being retained"); + } + } else { + throw InvalidInputException("Attempting to open a stream on a query result that has no streaming buffer"); + } + if (!StringUtil::Equals(handle->Format().Name(), expected_format)) { + throw InvalidInputException("Attempting to open a \"%s\" stream on a query result in the \"%s\" format", + expected_format, handle->Format().Name()); + } +} + +ResultStreamBase::~ResultStreamBase() { + Close(); +} + +void ResultStreamBase::Close() { + handle->Close(); +} + +bool ResultStreamBase::IsOpen() { + return handle->IsOpen(); +} + +bool ResultStreamBase::MetadataAvailable() const { + return handle->MetadataAvailable(); +} + +QueryResultState +ResultStreamBase::GuardedInternal(const char *name, + const std::function &call) { + if (!handle->context) { + // The stream already ended. Keep reporting the terminal state + return handle->HasError() ? QueryResultState::EXECUTION_ERROR : QueryResultState::FINISHED; + } + // Ending the query drops the handle's reference to the context, which the lock below outlives + auto keep_alive = handle->context; + auto lock = keep_alive->LockContext(); + try { + return call(*lock); + } catch (std::exception &ex) { + // A pending interrupt reaches the consumer as an error on the stream, never as a throw + handle->HandleFetchFailure(*lock, ErrorData(ex)); + } catch (...) { // LCOV_EXCL_START + handle->SetError(ErrorData(StringUtil::Format("Unhandled exception in %s", name))); + handle->EndQuery(*lock, true); + } // LCOV_EXCL_STOP + return QueryResultState::EXECUTION_ERROR; +} + +QueryResultState ResultStreamBase::DriveWithoutBufferInternal(ClientContextLock &lock, bool execute_task) { + D_ASSERT(!handle->buffer); + if (!handle->IsOpenInternal(lock)) { + return handle->Cancelled(); + } + auto &context = *handle->context; + // Checked as the buffer's own Poll checks it, so a cancel is seen before a finished execution can be ended. A + // worker error also raises the flag; both loads are seq_cst + if (context.interrupt_state.load() == ClientInterruptState::INTERRUPTED && !Executor::Get(context).HasError()) { + throw InterruptException(); + } + return execute_task ? context.ExecuteTaskInternal(lock, *handle) : context.PollInternal(lock, *handle); +} + +QueryResultState ResultStreamBase::Poll() { + auto state = GuardedInternal("Poll", [&](ClientContextLock &lock) { + if (!handle->buffer) { + auto state = DriveWithoutBufferInternal(lock, false); + if (!handle->buffer) { + return state; + } + } + return handle->buffer->Poll(lock, *handle); + }); + if (state == QueryResultState::EXECUTION_ERROR) { + Close(); + } + return state; +} + +QueryResultState ResultStreamBase::ExecuteTask() { + auto state = GuardedInternal("ExecuteTask", [&](ClientContextLock &lock) { + if (!handle->buffer) { + auto state = DriveWithoutBufferInternal(lock, true); + // When the statement producing the result started, the buffer reports what is poppable + return handle->buffer ? handle->buffer->Poll(lock, *handle) : state; + } + return handle->buffer->Participate(lock, *handle); + }); + if (state == QueryResultState::EXECUTION_ERROR) { + // A finished execution can still hold trailing units, so only an error ends the stream here + Close(); + } + return state; +} + +void ResultStreamBase::WaitForTask() { + if (!handle->context) { + return; + } + if (handle->buffer) { + handle->buffer->UnblockSinks(); + } + handle->WaitForTask(); +} + +QueryResultState ResultStreamBase::TryFetchUnit(unique_ptr &out_unit) { + out_unit.reset(); + auto state = GuardedInternal("TryFetch", [&](ClientContextLock &lock) { + if (!handle->buffer) { + auto state = DriveWithoutBufferInternal(lock, false); + if (!handle->buffer) { + if (state == QueryResultState::FINISHED) { + // A statement that expands into several and returns no rows ends without a unit + return handle->EndFinishedInternal(lock); + } + return state; + } + } + auto &buffer = *handle->buffer; + auto state = buffer.Poll(lock, *handle); + if (state == QueryResultState::EXECUTION_ERROR) { + return state; + } + if (state == QueryResultState::READY) { + out_unit = buffer.Scan(); + // READY is only reported with a unit queued + D_ASSERT(out_unit); + return QueryResultState::READY; + } + if (state == QueryResultState::FINISHED) { + // The buffer is drained and execution is done: this is the end of the stream + buffer.AssertNoBlockedSinks(); + handle->EndQuery(lock); + // Cleanup can fail on an autocommit commit. It records the error without throwing + return handle->HasError() ? QueryResultState::EXECUTION_ERROR : QueryResultState::FINISHED; + } + return state; + }); + if (IsTerminal(state)) { + Close(); + } + return state; +} + +unique_ptr ResultStreamBase::FetchUnitInternal(ClientContextLock &lock) { + try { + // The statements before the one producing the result of a statement that expands run on this thread + while (!handle->buffer) { + auto state = handle->context->ExecuteTaskInternal(lock, *handle); + if (state == QueryResultState::EXECUTION_ERROR) { + return nullptr; + } + if (state == QueryResultState::FINISHED && !handle->buffer) { + handle->EndFinishedInternal(lock); + return nullptr; + } + if (state == QueryResultState::BLOCKED || state == QueryResultState::NO_TASKS_AVAILABLE) { + handle->context->WaitForTask(lock, *handle); + } + } + auto &buffer = *handle->buffer; + auto state = buffer.ReplenishBuffer(lock, *handle); + if (state == QueryResultState::EXECUTION_ERROR) { + return nullptr; + } + auto unit = buffer.Scan(); + if (!unit) { + // READY is only reported with a unit queued, so an empty scan means execution finished + D_ASSERT(state == QueryResultState::FINISHED); + handle->EndQuery(lock); + return nullptr; + } + return unit; + } catch (std::exception &ex) { + handle->HandleFetchFailure(lock, ErrorData(ex)); + } catch (...) { // LCOV_EXCL_START + handle->SetError(ErrorData("Unhandled exception in Fetch")); + handle->EndQuery(lock, true); + } // LCOV_EXCL_STOP + return nullptr; +} + +unique_ptr ResultStreamBase::FetchUnit() { + if (!handle->context && !handle->HasError()) { + // The stream ended cleanly. Keep reporting the end, the way TryFetch and Poll do + return nullptr; + } + unique_ptr unit; + { + // Ending the query drops the handle's reference to the context, which the lock below outlives + auto keep_alive = handle->context; + auto lock = handle->LockContext(); + handle->CheckExecutableInternal(*lock); + unit = FetchUnitInternal(*lock); + } + if (!unit) { + if (!HasError() && handle->buffer) { + handle->buffer->AssertNoBlockedSinks(); + } + Close(); + return nullptr; + } + return unit; +} + +const ResultFormatGlobalState &ResultStreamBase::FormatStateInternal() const { + if (!handle->buffer) { + return handle->CheckedFormatState(handle->Format().Name()); + } + return handle->GetBufferedData().FormatState(); +} + +void ResultStreamBase::SetError(ErrorData error) { + handle->SetError(std::move(error)); +} + +bool ResultStreamBase::HasError() const { + return handle->HasError(); +} + +const string &ResultStreamBase::GetError() const { + return handle->GetError(); +} + +const ErrorData &ResultStreamBase::GetErrorObject() const { + return handle->GetErrorObject(); +} + +const ExceptionType &ResultStreamBase::GetErrorType() const { + return handle->GetErrorType(); +} + +const vector &ResultStreamBase::GetTypes() const { + return handle->GetTypes(); +} + +const vector &ResultStreamBase::GetNames() const { + return handle->GetNames(); +} + +const Identifier &ResultStreamBase::ColumnName(idx_t index) const { + return handle->ColumnName(index); +} + +idx_t ResultStreamBase::ColumnCount() const { + return handle->ColumnCount(); +} + +StatementType ResultStreamBase::GetStatementType() const { + return handle->GetStatementType(); +} + +const StatementProperties &ResultStreamBase::GetStatementProperties() const { + return handle->GetStatementProperties(); +} + +const ClientProperties &ResultStreamBase::GetClientProperties() const { + return handle->client_properties; +} + +ClientProperties &ResultStreamBase::GetClientProperties() { + return handle->client_properties; +} + +} // namespace duckdb diff --git a/src/duckdb/src/main/relation.cpp b/src/duckdb/src/main/relation.cpp index f496b7636..ba58e2ce5 100644 --- a/src/duckdb/src/main/relation.cpp +++ b/src/duckdb/src/main/relation.cpp @@ -38,7 +38,7 @@ shared_ptr Relation::Project(const string &expression, const string &a } shared_ptr Relation::Project(const string &select_list, const vector &aliases) { - auto expressions = Parser::ParseExpressionList(select_list, context->GetContext()->GetParserOptions()); + auto expressions = Parser(*context->GetContext()).ParseExpressionList(select_list); return make_shared_ptr(shared_from_this(), std::move(expressions), StringsToIdentifiers(aliases)); } @@ -61,7 +61,7 @@ static vector> StringListToExpressionList(ClientCon } vector> result_list; for (auto &expr : expressions) { - auto expression_list = Parser::ParseExpressionList(expr, context.GetParserOptions()); + auto expression_list = Parser(context).ParseExpressionList(expr); if (expression_list.size() != 1) { throw ParserException("Expected a single expression in the expression list"); } @@ -77,7 +77,7 @@ shared_ptr Relation::Project(const vector &expressions, const } shared_ptr Relation::Filter(const string &expression) { - auto expression_list = Parser::ParseExpressionList(expression, context->GetContext()->GetParserOptions()); + auto expression_list = Parser(*context->GetContext()).ParseExpressionList(expression); if (expression_list.size() != 1) { throw ParserException("Expected a single expression as filter condition"); } @@ -106,7 +106,7 @@ shared_ptr Relation::Limit(int64_t limit, int64_t offset) { } shared_ptr Relation::Order(const string &expression) { - auto order_list = Parser::ParseOrderList(expression, context->GetContext()->GetParserOptions()); + auto order_list = Parser(*context->GetContext()).ParseOrderList(expression); return Order(std::move(order_list)); } @@ -120,7 +120,7 @@ shared_ptr Relation::Order(const vector &expressions) { } vector order_list; for (auto &expression : expressions) { - auto inner_list = Parser::ParseOrderList(expression, context->GetContext()->GetParserOptions()); + auto inner_list = Parser(*context->GetContext()).ParseOrderList(expression); if (inner_list.size() != 1) { throw ParserException("Expected a single ORDER BY expression in the expression list"); } @@ -131,7 +131,7 @@ shared_ptr Relation::Order(const vector &expressions) { shared_ptr Relation::Join(const shared_ptr &other, const string &condition, JoinType type, JoinRefType ref_type) { - auto expression_list = Parser::ParseExpressionList(condition, context->GetContext()->GetParserOptions()); + auto expression_list = Parser(*context->GetContext()).ParseExpressionList(condition); D_ASSERT(!expression_list.empty()); return Join(other, std::move(expression_list), type, ref_type); } @@ -184,7 +184,7 @@ shared_ptr Relation::Alias(const string &alias) { } shared_ptr Relation::Aggregate(const string &aggregate_list) { - auto expression_list = Parser::ParseExpressionList(aggregate_list, context->GetContext()->GetParserOptions()); + auto expression_list = Parser(*context->GetContext()).ParseExpressionList(aggregate_list); return make_shared_ptr(shared_from_this(), std::move(expression_list)); } @@ -193,8 +193,8 @@ shared_ptr Relation::Aggregate(vector> ex } shared_ptr Relation::Aggregate(const string &aggregate_list, const string &group_list) { - auto expression_list = Parser::ParseExpressionList(aggregate_list, context->GetContext()->GetParserOptions()); - auto groups = Parser::ParseGroupByList(group_list, context->GetContext()->GetParserOptions()); + auto expression_list = Parser(*context->GetContext()).ParseExpressionList(aggregate_list); + auto groups = Parser(*context->GetContext()).ParseGroupByList(group_list); return make_shared_ptr(shared_from_this(), std::move(expression_list), std::move(groups)); } @@ -210,7 +210,7 @@ shared_ptr Relation::Aggregate(const vector &aggregates, const } shared_ptr Relation::Aggregate(vector> expressions, const string &group_list) { - auto groups = Parser::ParseGroupByList(group_list, context->GetContext()->GetParserOptions()); + auto groups = Parser(*context->GetContext()).ParseGroupByList(group_list); return make_shared_ptr(shared_from_this(), std::move(expressions), std::move(groups)); } @@ -231,9 +231,7 @@ unique_ptr Relation::Execute() { unique_ptr Relation::ExecuteOrThrow() { auto res = Execute(); D_ASSERT(res); - if (res->HasError()) { - res->ThrowError(); - } + res->ThrowIfError(); return res; } @@ -263,10 +261,7 @@ void Relation::Insert(const Identifier &schema_name, const Identifier &table_nam void Relation::Insert(const Identifier &catalog_name, const Identifier &schema_name, const Identifier &table_name) { auto insert = InsertRel(catalog_name, schema_name, table_name); auto res = insert->Execute(); - if (res->HasError()) { - const string prepended_message = "Failed to insert into table '" + table_name + "': "; - res->ThrowError(prepended_message); - } + res->ThrowIfError("Failed to insert into table '" + table_name + "': "); } void Relation::Insert(const vector> &values) { @@ -305,10 +300,7 @@ void Relation::Create(const Identifier &catalog_name, const Identifier &schema_n } auto create = CreateRel(catalog_name, schema_name, table_name, temporary, on_conflict); auto res = create->Execute(); - if (res->HasError()) { - const string prepended_message = "Failed to create table '" + table_name + "': "; - res->ThrowError(prepended_message); - } + res->ThrowIfError("Failed to create table '" + table_name + "': "); } shared_ptr Relation::WriteCSVRel(const string &csv_file, identifier_map_t> options) { @@ -318,10 +310,7 @@ shared_ptr Relation::WriteCSVRel(const string &csv_file, identifier_ma void Relation::WriteCSV(const string &csv_file, identifier_map_t> options) { auto write_csv = WriteCSVRel(csv_file, std::move(options)); auto res = write_csv->Execute(); - if (res->HasError()) { - const string prepended_message = "Failed to write '" + csv_file + "': "; - res->ThrowError(prepended_message); - } + res->ThrowIfError("Failed to write '" + csv_file + "': "); } shared_ptr Relation::WriteParquetRel(const string &parquet_file, identifier_map_t> options) { @@ -333,10 +322,7 @@ shared_ptr Relation::WriteParquetRel(const string &parquet_file, ident void Relation::WriteParquet(const string &parquet_file, identifier_map_t> options) { auto write_parquet = WriteParquetRel(parquet_file, std::move(options)); auto res = write_parquet->Execute(); - if (res->HasError()) { - const string prepended_message = "Failed to write '" + parquet_file + "': "; - res->ThrowError(prepended_message); - } + res->ThrowIfError("Failed to write '" + parquet_file + "': "); } shared_ptr Relation::CreateView(const Identifier &name, bool replace, bool temporary) { @@ -347,15 +333,12 @@ shared_ptr Relation::CreateView(const Identifier &schema_name, const I bool temporary) { auto view = make_shared_ptr(shared_from_this(), schema_name, name, replace, temporary); auto res = view->Execute(); - if (res->HasError()) { - const string prepended_message = "Failed to create view '" + name + "': "; - res->ThrowError(prepended_message); - } + res->ThrowIfError("Failed to create view '" + name + "': "); return shared_from_this(); } unique_ptr Relation::Query(const string &sql) const { - return context->GetContext()->Query(sql, false); + return context->GetContext()->Query(sql, QueryParameters()); } unique_ptr Relation::Query(const Identifier &name, const string &sql) { diff --git a/src/duckdb/src/main/relation/query_relation.cpp b/src/duckdb/src/main/relation/query_relation.cpp index bcdd55dcf..ca415cd70 100644 --- a/src/duckdb/src/main/relation/query_relation.cpp +++ b/src/duckdb/src/main/relation/query_relation.cpp @@ -29,7 +29,7 @@ QueryRelation::~QueryRelation() { unique_ptr QueryRelation::ParseStatement(ClientContext &context, const string &query, const string &error) { - Parser parser(context.GetParserOptions()); + Parser parser(context); parser.ParseQuery(query); if (parser.statements.size() != 1) { throw ParserException(error); diff --git a/src/duckdb/src/main/relation/read_csv_relation.cpp b/src/duckdb/src/main/relation/read_csv_relation.cpp index e6dfecff6..11d6c649d 100644 --- a/src/duckdb/src/main/relation/read_csv_relation.cpp +++ b/src/duckdb/src/main/relation/read_csv_relation.cpp @@ -15,7 +15,9 @@ #include "duckdb/parser/tableref/table_function_ref.hpp" #include "duckdb/function/table/read_csv.hpp" #include "duckdb/common/multi_file/multi_file_function.hpp" -#include "duckdb/execution/operator/csv_scanner/csv_multi_file_info.hpp" +#include "duckdb/execution/operator/csv_scanner/csv_file_scanner.hpp" +#include "duckdb/execution/operator/csv_scanner/csv_schema_discovery.hpp" +#include "duckdb/common/multi_file/union_by_name.hpp" namespace duckdb { @@ -37,11 +39,14 @@ CSVReaderOptions ReadCSVRelationBind(const shared_ptr &context, c D_ASSERT(!files.empty()); auto &file_name = files[0]; - CSVFileReaderOptions csv_file_options; - auto &csv_options = csv_file_options.options; + CSVReaderOptions csv_options; csv_options.file_path = file_name.path; vector empty; - csv_options.FromNamedParameters(options, *context, file_options); + named_argument_map_t named_arguments; + for (auto &option : options) { + named_arguments.insert(option.first, option.second); + } + csv_options.FromNamedParameters(named_arguments, *context, file_options); // Run the auto-detect, populating the options with the detected settings SimpleMultiFileList multi_file_list(files); @@ -49,12 +54,14 @@ CSVReaderOptions ReadCSVRelationBind(const shared_ptr &context, c if (file_options.union_by_name) { vector types; vector names; - auto result = make_uniq(); - auto csv_data = make_uniq(); - result->interface = make_uniq(); - - multi_file_reader->BindUnionReader(*context, types, names, multi_file_list, *result, csv_file_options, - file_options); + // sniff every file and unify the columns of the files by name + identifier_map_t union_names_map; + for (auto &file : files) { + auto file_csv_options = csv_options; + file_csv_options.file_path = file.path; + CSVFileScan file_scan(*context, file, file_csv_options, file_options); + UnionByName::CombineUnionTypes(file_scan.GetNames(), file_scan.GetTypes(), types, names, union_names_map); + } if (!csv_options.sql_types_per_column.empty()) { const auto exception = CSVError::ColumnTypesError(csv_options.sql_types_per_column, names); if (!exception.error_message.empty()) { diff --git a/src/duckdb/src/main/relation/table_function_relation.cpp b/src/duckdb/src/main/relation/table_function_relation.cpp index c73cb0218..51ef3ef7b 100644 --- a/src/duckdb/src/main/relation/table_function_relation.cpp +++ b/src/duckdb/src/main/relation/table_function_relation.cpp @@ -30,27 +30,30 @@ void TableFunctionRelation::SetNamedParameters(named_parameter_map_t &&options) TableFunctionRelation::TableFunctionRelation(const shared_ptr &context, string name_p, vector parameters_p, named_parameter_map_t named_parameters, - shared_ptr input_relation_p, bool auto_init) + shared_ptr input_relation_p, bool auto_init, + shared_ptr bind_info_p) : Relation(context, RelationType::TABLE_FUNCTION_RELATION), name(std::move(name_p)), parameters(std::move(parameters_p)), named_parameters(std::move(named_parameters)), - input_relation(std::move(input_relation_p)), auto_initialize(auto_init) { + input_relation(std::move(input_relation_p)), bind_info(std::move(bind_info_p)), auto_initialize(auto_init) { InitializeColumns(); } TableFunctionRelation::TableFunctionRelation(const shared_ptr &context, string name_p, vector parameters_p, named_parameter_map_t named_parameters, - shared_ptr input_relation_p, bool auto_init) + shared_ptr input_relation_p, bool auto_init, + shared_ptr bind_info_p) : Relation(context, RelationType::TABLE_FUNCTION_RELATION), name(std::move(name_p)), parameters(std::move(parameters_p)), named_parameters(std::move(named_parameters)), - input_relation(std::move(input_relation_p)), auto_initialize(auto_init) { + input_relation(std::move(input_relation_p)), bind_info(std::move(bind_info_p)), auto_initialize(auto_init) { InitializeColumns(); } TableFunctionRelation::TableFunctionRelation(const shared_ptr &context, string name_p, vector parameters_p, shared_ptr input_relation_p, - bool auto_init) + bool auto_init, shared_ptr bind_info_p) : Relation(context, RelationType::TABLE_FUNCTION_RELATION), name(std::move(name_p)), - parameters(std::move(parameters_p)), input_relation(std::move(input_relation_p)), auto_initialize(auto_init) { + parameters(std::move(parameters_p)), input_relation(std::move(input_relation_p)), + bind_info(std::move(bind_info_p)), auto_initialize(auto_init) { InitializeColumns(); } @@ -95,6 +98,7 @@ unique_ptr TableFunctionRelation::GetTableRef() { auto table_function = make_uniq(); auto function = make_uniq(name, std::move(children)); table_function->function = std::move(function); + table_function->bind_info = bind_info; return std::move(table_function); } diff --git a/src/duckdb/src/main/relation/table_relation.cpp b/src/duckdb/src/main/relation/table_relation.cpp index 4587f42af..c9d30981c 100644 --- a/src/duckdb/src/main/relation/table_relation.cpp +++ b/src/duckdb/src/main/relation/table_relation.cpp @@ -47,7 +47,7 @@ string TableRelation::ToString(idx_t depth) { static unique_ptr ParseCondition(ClientContext &context, const string &condition) { if (!condition.empty()) { - auto expression_list = Parser::ParseExpressionList(condition, context.GetParserOptions()); + auto expression_list = Parser(context).ParseExpressionList(condition); if (expression_list.size() != 1) { throw ParserException("Expected a single expression as filter condition"); } @@ -72,7 +72,7 @@ void TableRelation::Update(const string &update_list, const string &condition) { vector update_columns; vector> expressions; auto cond = ParseCondition(*context->GetContext(), condition); - Parser::ParseUpdateList(update_list, update_columns, expressions, context->GetContext()->GetParserOptions()); + Parser(*context->GetContext()).ParseUpdateList(update_list, update_columns, expressions); auto update = make_shared_ptr( context, std::move(cond), description->qualified_name.Catalog(), description->qualified_name.Schema(), description->qualified_name.Name(), std::move(update_columns), std::move(expressions)); diff --git a/src/duckdb/src/main/relation/value_relation.cpp b/src/duckdb/src/main/relation/value_relation.cpp index 33ba718dc..1340132ca 100644 --- a/src/duckdb/src/main/relation/value_relation.cpp +++ b/src/duckdb/src/main/relation/value_relation.cpp @@ -34,7 +34,7 @@ ValueRelation::ValueRelation(const shared_ptr &context, ValueRelation::ValueRelation(const shared_ptr &context, const string &values_list, vector names_p, string alias_p) : Relation(context, RelationType::VALUE_LIST_RELATION), names(std::move(names_p)), alias(std::move(alias_p)) { - this->expressions = Parser::ParseValuesList(values_list, context->GetParserOptions()); + this->expressions = Parser(*context).ParseValuesList(values_list); QueryResult::DeduplicateColumns(names); TryBindRelation(columns); } diff --git a/src/duckdb/src/main/result_format.cpp b/src/duckdb/src/main/result_format.cpp new file mode 100644 index 000000000..7b056287a --- /dev/null +++ b/src/duckdb/src/main/result_format.cpp @@ -0,0 +1,95 @@ +#include "duckdb/main/result_format.hpp" + +#include "duckdb/common/string_util.hpp" +#include "duckdb/main/buffered_data/buffered_data.hpp" +#include "duckdb/main/retained_result_collection.hpp" + +namespace duckdb { + +ResultFormatGlobalState::~ResultFormatGlobalState() { +} + +ResultFormatLocalState::~ResultFormatLocalState() { +} + +ResultFormat::~ResultFormat() { +} + +bool ResultFormat::NameEquals(const char *name) const { + return StringUtil::Equals(Name(), name); +} + +//===--------------------------------------------------------------------===// +// ChunkFormat +//===--------------------------------------------------------------------===// +class ChunkFormatLocalState : public ResultFormatLocalState { +public: + unique_ptr unit; +}; + +ChunkFormat::ChunkFormat(QueryResultMemoryType memory_type_p) : memory_type(memory_type_p) { +} + +const shared_ptr &ChunkFormat::InMemory() { + static const shared_ptr format = make_shared_ptr(QueryResultMemoryType::IN_MEMORY); + return format; +} + +const shared_ptr &ChunkFormat::BufferManaged() { + static const shared_ptr format = make_shared_ptr(QueryResultMemoryType::BUFFER_MANAGED); + return format; +} + +QueryResultMemoryType ChunkFormat::MemoryType() const { + return memory_type; +} + +const char *ChunkFormat::Name() const { + return NAME; +} + +unique_ptr ChunkFormat::InitGlobal(const ResultFormatContext &context) { + return make_uniq(); +} + +unique_ptr ChunkFormat::InitLocal(ResultFormatGlobalState &gstate) { + return make_uniq(); +} + +void ChunkFormat::AppendToUnit(ResultFormatGlobalState &gstate, ResultFormatLocalState &lstate_p, DataChunk &chunk) { + auto &lstate = lstate_p.Cast(); + D_ASSERT(!lstate.unit); + // Copied outside the buffer's lock, so parallel producers copy concurrently + lstate.unit = make_uniq(BufferedData::CopyForBuffering(chunk)); +} + +bool ChunkFormat::IsUnitFinished(ResultFormatLocalState &lstate_p) { + auto &lstate = lstate_p.Cast(); + return lstate.unit != nullptr; +} + +unique_ptr ChunkFormat::FinishUnit(ResultFormatGlobalState &gstate, ResultFormatLocalState &lstate_p) { + auto &lstate = lstate_p.Cast(); + return std::move(lstate.unit); +} + +unique_ptr ChunkFormat::CreateCollection(ClientContext &context, + ResultFormatGlobalState &gstate, + const ResultFormatContext &format_context) { + bool batch_ordered = format_context.ordering == ResultOrdering::BATCH_INDEX_ORDERED; + return make_uniq(context, format_context.types, memory_type, batch_ordered); +} + +unique_ptr ChunkFormat::UnpackUnit(unique_ptr unit) { + if (!unit) { + return nullptr; + } + auto &chunk_unit = unit->Cast(); + for (idx_t i = 0; i < chunk_unit.chunk->ColumnCount(); i++) { + // CopyForBuffering copies into a freshly initialized chunk, so every column is already flat + D_ASSERT(chunk_unit.chunk->data[i].GetVectorType() == VectorType::FLAT_VECTOR); + } + return std::move(chunk_unit.chunk); +} + +} // namespace duckdb diff --git a/src/duckdb/src/main/result_unit.cpp b/src/duckdb/src/main/result_unit.cpp new file mode 100644 index 000000000..78720be87 --- /dev/null +++ b/src/duckdb/src/main/result_unit.cpp @@ -0,0 +1,15 @@ +#include "duckdb/main/result_unit.hpp" + +namespace duckdb { + +ResultUnit::ResultUnit(idx_t row_count_p, idx_t byte_size_p) : row_count(row_count_p), byte_size(byte_size_p) { +} + +ResultUnit::~ResultUnit() { +} + +ChunkUnit::ChunkUnit(unique_ptr chunk_p) + : ResultUnit(chunk_p->size(), chunk_p->GetDataSize()), chunk(std::move(chunk_p)) { +} + +} // namespace duckdb diff --git a/src/duckdb/src/main/retained_result_collection.cpp b/src/duckdb/src/main/retained_result_collection.cpp new file mode 100644 index 000000000..f3c2b8b90 --- /dev/null +++ b/src/duckdb/src/main/retained_result_collection.cpp @@ -0,0 +1,118 @@ +#include "duckdb/main/retained_result_collection.hpp" + +#include "duckdb/common/enum_util.hpp" +#include "duckdb/common/types/batched_data_collection.hpp" +#include "duckdb/common/types/column/column_data_collection.hpp" +#include "duckdb/main/client_context.hpp" +#include "duckdb/storage/buffer_manager.hpp" + +namespace duckdb { + +RetainedResultCollection::~RetainedResultCollection() { +} + +//===--------------------------------------------------------------------===// +// ChunkRetainedCollection +//===--------------------------------------------------------------------===// +ChunkRetainedCollection::ChunkRetainedCollection(ClientContext &context, const vector &types, + QueryResultMemoryType memory_type, bool batch_ordered_p) + : batch_ordered(batch_ordered_p) { + switch (memory_type) { + case QueryResultMemoryType::IN_MEMORY: + if (batch_ordered) { + batched = make_uniq(context, types); + } else { + collection = make_uniq(Allocator::DefaultAllocator(), types); + } + break; + case QueryResultMemoryType::BUFFER_MANAGED: + // The database's buffer manager, because the result can outlive the ClientContext + if (batch_ordered) { + batched = + make_uniq(context, types, ColumnDataAllocatorType::BUFFER_MANAGER_ALLOCATOR, + ColumnDataCollectionLifetime::THROW_ERROR_AFTER_DATABASE_CLOSES); + } else { + collection = + make_uniq(BufferManager::GetBufferManager(*context.db), types, + ColumnDataCollectionLifetime::THROW_ERROR_AFTER_DATABASE_CLOSES); + } + break; + default: + throw NotImplementedException("ChunkRetainedCollection for %s", EnumUtil::ToString(memory_type)); + } +} + +ChunkRetainedCollection::ChunkRetainedCollection(unique_ptr collection_p) + : batch_ordered(false), collection(std::move(collection_p)) { +} + +ChunkRetainedCollection::~ChunkRetainedCollection() { +} + +void ChunkRetainedCollection::Append(DataChunk &chunk, idx_t batch_index) { + if (batch_ordered) { + batched->Append(chunk, batch_index); + return; + } + if (!append_initialized) { + // InitializeAppend adds an empty chunk, which a scan of a combined-only collection would read as its end + collection->InitializeAppend(append_state); + append_initialized = true; + } + collection->Append(append_state, chunk); +} + +void ChunkRetainedCollection::Combine(RetainedResultCollection &local_p) { + auto &local = local_p.Cast(); + if (batch_ordered) { + batched->Merge(*local.batched); + return; + } + collection->Combine(*local.collection); +} + +void ChunkRetainedCollection::Finalize() { + if (batch_ordered) { + collection = batched->FetchCollection(); + batched.reset(); + } +} + +idx_t ChunkRetainedCollection::Count() const { + if (batch_ordered) { + return batched ? batched->Count() : collection->Count(); + } + return collection->Count(); +} + +ColumnDataCollection &ChunkRetainedCollection::Get() { + D_ASSERT(collection); + return *collection; +} + +unique_ptr ChunkRetainedCollection::Take() { + D_ASSERT(collection); + return std::move(collection); +} + +unique_ptr ChunkRetainedCollection::FetchRaw() { + D_ASSERT(collection); + if (scan_exhausted) { + return nullptr; + } + auto result = make_uniq(); + collection->InitializeScanChunk(*result); + if (!scan_initialized) { + // Disallow zero copy so the chunk is independently usable even after the collection is destroyed + collection->InitializeScan(scan_state, ColumnDataScanProperties::DISALLOW_ZERO_COPY); + scan_initialized = true; + } + collection->Scan(scan_state, *result); + if (result->size() == 0) { + scan_exhausted = true; + return nullptr; + } + return result; +} + +} // namespace duckdb diff --git a/src/duckdb/src/main/secret/secret_manager.cpp b/src/duckdb/src/main/secret/secret_manager.cpp index 5eb49e3b8..39ff78bd9 100644 --- a/src/duckdb/src/main/secret/secret_manager.cpp +++ b/src/duckdb/src/main/secret/secret_manager.cpp @@ -542,8 +542,8 @@ vector SecretManager::AllSecrets(CatalogTransaction transaction) { } // Add results from all backends to the result set - for (const auto &backend : secret_storages) { - auto backend_result = backend.second->AllSecrets(&transaction); + for (const auto &backend : GetSecretStorages()) { + auto backend_result = backend.get().AllSecrets(&transaction); for (const auto &it : backend_result) { result.push_back(it); } diff --git a/src/duckdb/src/main/settings/autogenerated_settings.cpp b/src/duckdb/src/main/settings/autogenerated_settings.cpp index 0eefdef63..e44dca611 100644 --- a/src/duckdb/src/main/settings/autogenerated_settings.cpp +++ b/src/duckdb/src/main/settings/autogenerated_settings.cpp @@ -79,6 +79,22 @@ Value CustomUserAgentSetting::GetSetting(const ClientContext &context) { return Value(config.options.custom_user_agent); } +//===----------------------------------------------------------------------===// +// Debug Abort On Wal Failure +//===----------------------------------------------------------------------===// +void DebugAbortOnWalFailureSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) { + config.options.abort_on_wal_failure = input.GetValue(); +} + +void DebugAbortOnWalFailureSetting::ResetGlobal(DatabaseInstance *db, DBConfig &config) { + config.options.abort_on_wal_failure = DBConfigOptions().abort_on_wal_failure; +} + +Value DebugAbortOnWalFailureSetting::GetSetting(const ClientContext &context) { + auto &config = DBConfig::GetConfig(context); + return Value::BOOLEAN(config.options.abort_on_wal_failure); +} + //===----------------------------------------------------------------------===// // Debug Checkpoint Abort //===----------------------------------------------------------------------===// @@ -126,6 +142,16 @@ void DebugPhysicalTableScanExecutionStrategySetting::OnSet(SettingCallbackInfo & EnumUtil::FromString(StringValue::Get(parameter)); } +//===----------------------------------------------------------------------===// +// Debug Verify Progress +//===----------------------------------------------------------------------===// +void DebugVerifyProgressSetting::OnSet(SettingCallbackInfo &info, Value ¶meter) { + if (parameter.IsNull()) { + throw InvalidInputException("debug_verify_progress setting cannot be NULL"); + } + EnumUtil::FromString(StringValue::Get(parameter)); +} + //===----------------------------------------------------------------------===// // Debug Verify Statement //===----------------------------------------------------------------------===// @@ -209,6 +235,26 @@ void ExplainOutputSetting::OnSet(SettingCallbackInfo &info, Value ¶meter) { EnumUtil::FromString(StringValue::Get(parameter)); } +//===----------------------------------------------------------------------===// +// External File Cache Request Sizing +//===----------------------------------------------------------------------===// +void ExternalFileCacheRequestSizingSetting::OnSet(SettingCallbackInfo &info, Value ¶meter) { + if (parameter.IsNull()) { + throw InvalidInputException("external_file_cache_request_sizing setting cannot be NULL"); + } + EnumUtil::FromString(StringValue::Get(parameter)); +} + +//===----------------------------------------------------------------------===// +// Fsync Mode +//===----------------------------------------------------------------------===// +void FsyncModeSetting::OnSet(SettingCallbackInfo &info, Value ¶meter) { + if (parameter.IsNull()) { + throw InvalidInputException("fsync_mode setting cannot be NULL"); + } + EnumUtil::FromString(StringValue::Get(parameter)); +} + //===----------------------------------------------------------------------===// // H T T P Proxy //===----------------------------------------------------------------------===// diff --git a/src/duckdb/src/main/settings/custom_settings.cpp b/src/duckdb/src/main/settings/custom_settings.cpp index 7cf8182c5..3b63cefb3 100644 --- a/src/duckdb/src/main/settings/custom_settings.cpp +++ b/src/duckdb/src/main/settings/custom_settings.cpp @@ -15,6 +15,7 @@ #include "duckdb/common/constants.hpp" #include "duckdb/common/enums/access_mode.hpp" #include "duckdb/common/enum_util.hpp" +#include "duckdb/common/limits.hpp" #include "duckdb/catalog/catalog_search_path.hpp" #include "duckdb/common/string_util.hpp" #include "duckdb/common/file_system.hpp" @@ -26,6 +27,7 @@ #include "duckdb/main/config.hpp" #include "duckdb/main/database.hpp" #include "duckdb/main/database_manager.hpp" +#include "duckdb/main/http/http_transport_manager.hpp" #include "duckdb/common/tree_renderer.hpp" #include "duckdb/main/extension_helper.hpp" #include "duckdb/main/extension_callback_manager.hpp" @@ -44,7 +46,9 @@ #include "duckdb/logging/log_manager.hpp" #include "duckdb/common/type_visitor.hpp" #include "duckdb/function/variant/variant_shredding.hpp" +#include "duckdb/storage/statistics/variant_stats.hpp" #include "duckdb/storage/block_allocator.hpp" +#include "duckdb/parser/peg/dialect_extension.hpp" #include "duckdb/parser/grammar_extension.hpp" #include "mbedtls_wrapper.hpp" @@ -74,13 +78,17 @@ static DatabaseInstance &GetDB(DatabaseInstance *db) { template static idx_t ParseMemoryLimitOrPercentage(const string &input, BASE &&get_base) { if (input.empty() || input.back() != '%') { - return DBConfig::ParseMemoryLimit(input); + return DBConfig::ParseMemoryLimitOrMaximum(input); } double percentage; if (!TryDoubleCast(input.c_str(), input.size() - 1, percentage, false) || percentage < 0 || percentage > 100) { throw InvalidInputException("Unable to parse valid percentage (input: %s)", input); } - return LossyNumericCast(percentage) * get_base() / 100; + auto result = percentage * static_cast(get_base()) / 100.0; + if (result >= static_cast(NumericLimits::Maximum())) { + return NumericLimits::Maximum(); + } + return LossyNumericCast(result); } //! The available system memory. The config's filesystem is not set until the database starts, but @@ -110,6 +118,9 @@ bool AccessModeSetting::OnGlobalSet(DatabaseInstance *db, DBConfig &config, cons // Allocator Background Threads //===----------------------------------------------------------------------===// void AllocatorBackgroundThreadsSetting::OnSet(SettingCallbackInfo &info, Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", AllocatorBackgroundThreadsSetting::Name); + } Allocator::SetBackgroundThreads(input.GetValue()); } @@ -118,7 +129,7 @@ void AllocatorBackgroundThreadsSetting::OnSet(SettingCallbackInfo &info, Value & //===----------------------------------------------------------------------===// void AllocatorBulkDeallocationFlushThresholdSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) { - config.options.allocator_bulk_deallocation_flush_threshold = DBConfig::ParseMemoryLimit(input.ToString()); + config.options.allocator_bulk_deallocation_flush_threshold = DBConfig::ParseMemoryLimitOrMaximum(input.ToString()); if (db) { BufferManager::GetBufferManager(*db).GetBufferPool().SetAllocatorBulkDeallocationFlushThreshold( config.options.allocator_bulk_deallocation_flush_threshold); @@ -142,15 +153,16 @@ Value AllocatorBulkDeallocationFlushThresholdSetting::GetSetting(const ClientCon //===----------------------------------------------------------------------===// // Delta Only Variant Legacy Encoding //===----------------------------------------------------------------------===// -void DeltaOnlyVariantEncodingEnabledSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) { +void DebugDeltaOnlyVariantEncodingEnabledSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, + const Value &input) { throw InvalidInputException("This setting is not adjustable by a user"); } -void DeltaOnlyVariantEncodingEnabledSetting::ResetGlobal(DatabaseInstance *db, DBConfig &config) { +void DebugDeltaOnlyVariantEncodingEnabledSetting::ResetGlobal(DatabaseInstance *db, DBConfig &config) { throw InvalidInputException("This setting is not adjustable by a user"); } -Value DeltaOnlyVariantEncodingEnabledSetting::GetSetting(const ClientContext &context) { +Value DebugDeltaOnlyVariantEncodingEnabledSetting::GetSetting(const ClientContext &context) { auto &config = DBConfig::GetConfig(context); return Value::BOOLEAN(config.options.variant_legacy_encoding); } @@ -166,6 +178,9 @@ void AllocatorFlushThresholdSetting::OnSet(SettingCallbackInfo &info, Value &inp // Allow Community Extensions //===----------------------------------------------------------------------===// void AllowCommunityExtensionsSetting::OnSet(SettingCallbackInfo &info, Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", AllowCommunityExtensionsSetting::Name); + } if (info.db && input.GetValue()) { throw InvalidInputException("Cannot change allow_community_extensions setting while database is running"); } @@ -188,6 +203,9 @@ static int ExtensionRepositoryAccessLevel(ExtensionRepositoryAccess access) { } void AllowExtensionRepositoriesSetting::OnSet(SettingCallbackInfo &info, Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", AllowExtensionRepositoriesSetting::Name); + } // validate the value auto new_access = ExtensionRepositoryManager::ParseAccess(StringValue::Get(input)); if (!info.db) { @@ -224,6 +242,9 @@ void ExtensionRepositoryDirectorySetting::OnSet(SettingCallbackInfo &info, Value // Allow Persistent Secrets //===----------------------------------------------------------------------===// void AllowPersistentSecretsSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", AllowPersistentSecretsSetting::Name); + } auto value = input.DefaultCastAs(LogicalType::BOOLEAN); config.secret_manager->SetEnablePersistentSecrets(value.GetValue()); } @@ -241,6 +262,9 @@ Value AllowPersistentSecretsSetting::GetSetting(const ClientContext &context) { // Allow Unredacted Secrets //===----------------------------------------------------------------------===// void AllowUnredactedSecretsSetting::OnSet(SettingCallbackInfo &info, Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", AllowUnredactedSecretsSetting::Name); + } if ((info.db || info.context) && input.GetValue()) { throw InvalidInputException("Cannot change allow_unredacted_secrets setting while database is running"); } @@ -259,6 +283,9 @@ void DisableDatabaseInvalidationSetting::OnSet(SettingCallbackInfo &info, Value // Allow Unsigned Extensions //===----------------------------------------------------------------------===// void AllowUnsignedExtensionsSetting::OnSet(SettingCallbackInfo &info, Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", AllowUnsignedExtensionsSetting::Name); + } if (info.db && input.GetValue()) { throw InvalidInputException("Cannot change allow_unsigned_extensions setting while database is running"); } @@ -268,6 +295,9 @@ void AllowUnsignedExtensionsSetting::OnSet(SettingCallbackInfo &info, Value &inp // Allowed Configs //===----------------------------------------------------------------------===// void AllowedConfigsSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", AllowedConfigsSetting::Name); + } config.options.allowed_configs.clear(); auto &list = ListValue::GetChildren(input); for (auto &val : list) { @@ -292,6 +322,9 @@ Value AllowedConfigsSetting::GetSetting(const ClientContext &context) { // Allowed Directories //===----------------------------------------------------------------------===// void AllowedDirectoriesSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", AllowedDirectoriesSetting::Name); + } if (!Settings::Get(config)) { throw InvalidInputException("Cannot change allowed_directories when enable_external_access is disabled"); } @@ -315,7 +348,7 @@ void AllowedDirectoriesSetting::ResetGlobal(DatabaseInstance *db, DBConfig &conf Value AllowedDirectoriesSetting::GetSetting(const ClientContext &context) { auto &config = DBConfig::GetConfig(context); vector allowed_directories; - for (auto &dir : config.options.allowed_directories) { + for (auto &dir : config.GetAllowedDirectories()) { allowed_directories.emplace_back(dir); } return Value::LIST(LogicalType::VARCHAR, std::move(allowed_directories)); @@ -325,6 +358,9 @@ Value AllowedDirectoriesSetting::GetSetting(const ClientContext &context) { // Allowed Paths //===----------------------------------------------------------------------===//void void AllowedPathsSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", AllowedPathsSetting::Name); + } if (!Settings::Get(config)) { throw InvalidInputException("Cannot change allowed_paths when enable_external_access is disabled"); } @@ -349,7 +385,7 @@ void AllowedPathsSetting::ResetGlobal(DatabaseInstance *db, DBConfig &config) { Value AllowedPathsSetting::GetSetting(const ClientContext &context) { auto &config = DBConfig::GetConfig(context); vector allowed_paths; - for (auto &dir : config.options.allowed_paths) { + for (auto &dir : config.GetAllowedPaths()) { allowed_paths.emplace_back(dir); } return Value::LIST(LogicalType::VARCHAR, std::move(allowed_paths)); @@ -383,7 +419,7 @@ Value BlockAllocatorMemorySetting::GetSetting(const ClientContext &context) { // Checkpoint Threshold //===----------------------------------------------------------------------===// void CheckpointThresholdSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) { - idx_t new_limit = DBConfig::ParseMemoryLimit(input.ToString()); + idx_t new_limit = DBConfig::ParseMemoryLimitOrMaximum(input.ToString()); config.options.checkpoint_wal_size = new_limit; } @@ -431,6 +467,9 @@ Value DebugVerificationModeSetting::GetSetting(const ClientContext &context) { // Default Block Size //===----------------------------------------------------------------------===// void DefaultBlockSizeSetting::OnSet(SettingCallbackInfo &, Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", DefaultBlockSizeSetting::Name); + } auto block_alloc_size = input.GetValue(); Storage::VerifyBlockAllocSize(block_alloc_size); } @@ -519,6 +558,7 @@ void DisabledCompressionMethodsSetting::SetGlobal(DatabaseInstance *db, DBConfig case CompressionType::COMPRESSION_CONSTANT: case CompressionType::COMPRESSION_EMPTY: case CompressionType::COMPRESSION_UNCOMPRESSED: + case CompressionType::ENUM_SIZE: throw InvalidInputException("Compression method %s cannot be disabled", param); default: break; @@ -579,17 +619,17 @@ void DisabledOptimizersSetting::SetGlobal(DatabaseInstance *db, DBConfig &config } disabled_optimizers.insert(OptimizerTypeFromString(param)); } - config.options.disabled_optimizers = std::move(disabled_optimizers); + config.SetDisabledOptimizers(std::move(disabled_optimizers)); } void DisabledOptimizersSetting::ResetGlobal(DatabaseInstance *db, DBConfig &config) { - config.options.disabled_optimizers = DBConfigOptions().disabled_optimizers; + config.SetDisabledOptimizers(DBConfigOptions().disabled_optimizers); } Value DisabledOptimizersSetting::GetSetting(const ClientContext &context) { auto &config = DBConfig::GetConfig(context); string result; - for (auto &optimizer : config.options.disabled_optimizers) { + for (auto &optimizer : config.GetDisabledOptimizers()) { if (!result.empty()) { result += ","; } @@ -620,6 +660,9 @@ void VacuumRebuildIndexesSetting::OnSet(SettingCallbackInfo &info, Value &input) // Enable External Access //===----------------------------------------------------------------------===// void EnableExternalAccessSetting::OnSet(SettingCallbackInfo &info, Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", EnableExternalAccessSetting::Name); + } if (!info.db) { return; } @@ -648,6 +691,9 @@ void EnableExternalAccessSetting::OnSet(SettingCallbackInfo &info, Value &input) // Enable External File Cache //===----------------------------------------------------------------------===// void EnableExternalFileCacheSetting::OnSet(SettingCallbackInfo &info, Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", EnableExternalFileCacheSetting::Name); + } if (info.db) { ExternalFileCache::Get(*info.db).SetEnabled(input.GetValue()); } @@ -656,26 +702,36 @@ void EnableExternalFileCacheSetting::OnSet(SettingCallbackInfo &info, Value &inp //===----------------------------------------------------------------------===// // External File Cache Block Sizes //===----------------------------------------------------------------------===// -void ExternalFileCacheLocalBlockSizeSetting::OnSet(SettingCallbackInfo &info, Value &input) { +static void ValidateExternalFileCacheBlockSize(const char *name, const Value &input) { const auto bytes = input.GetValue(); if (bytes == 0) { - throw InvalidInputException("Invalid option for %s: value must be positive", string(Name)); + throw InvalidInputException("Invalid option for %s: value must be positive", string(name)); } if (!IsPowerOfTwo(bytes)) { - throw InvalidInputException("Invalid option for %s: block size must be a power of two, got %llu", string(Name), + throw InvalidInputException("Invalid option for %s: block size must be a power of two, got %llu", string(name), bytes); } } -void ExternalFileCacheRemoteBlockSizeSetting::OnSet(SettingCallbackInfo &info, Value &input) { - const auto bytes = input.GetValue(); - if (bytes == 0) { - throw InvalidInputException("Invalid option for %s: value must be positive", string(Name)); +void ExternalFileCacheLocalMaxBlockSizeSetting::OnSet(SettingCallbackInfo &info, Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", ExternalFileCacheLocalMaxBlockSizeSetting::Name); } - if (!IsPowerOfTwo(bytes)) { - throw InvalidInputException("Invalid option for %s: block size must be a power of two, got %llu", string(Name), - bytes); + ValidateExternalFileCacheBlockSize(Name, input); +} + +void ExternalFileCacheRemoteMaxBlockSizeSetting::OnSet(SettingCallbackInfo &info, Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", ExternalFileCacheRemoteMaxBlockSizeSetting::Name); } + ValidateExternalFileCacheBlockSize(Name, input); +} + +void ExternalFileCacheRemoteMinBlockSizeSetting::OnSet(SettingCallbackInfo &info, Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", ExternalFileCacheRemoteMinBlockSizeSetting::Name); + } + ValidateExternalFileCacheBlockSize(Name, input); } //===----------------------------------------------------------------------===// @@ -685,6 +741,9 @@ Value EnableLogging::GetSetting(const ClientContext &context) { return context.db->GetLogManager().GetConfig().enabled; } void EnableLogging::SetGlobal(DatabaseInstance *db_p, DBConfig &config, const Value ¶meter) { + if (parameter.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", EnableLogging::Name); + } auto &db = GetDB(db_p); db.GetLogManager().SetEnableLogging(parameter.GetValue()); } @@ -719,9 +778,6 @@ void ForceVariantShredding::SetGlobal(DatabaseInstance *_, DBConfig &config, con "or STRUCT (for OBJECT Variant values), not %s", type.ToString()); } - if (type.id() == LogicalTypeId::STRUCT && StructType::IsUnnamed(type)) { - throw InvalidInputException("STRUCT types in the shredding can not be empty"); - } return false; } switch (type.id()) { @@ -763,9 +819,7 @@ void ForceVariantShredding::SetGlobal(DatabaseInstance *_, DBConfig &config, con return false; }); - auto shredding_type = TypeVisitor::VisitReplace(logical_type, [](const LogicalType &type) { - return LogicalType::STRUCT({{"typed_value", type}, {"untyped_value_index", LogicalType::UINTEGER}}); - }); + auto shredding_type = VariantStats::GetShreddingType(logical_type); force_variant_shredding = LogicalType::STRUCT({{"unshredded", VariantShredding::GetUnshreddedType()}, {"shredded", shredding_type}}); } @@ -783,6 +837,9 @@ Value ForceVariantShredding::GetSetting(const ClientContext &context) { // Extension Directory //===----------------------------------------------------------------------===// void ExtensionDirectoriesSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", ExtensionDirectoriesSetting::Name); + } config.options.extension_directories.clear(); auto &list = ListValue::GetChildren(input); @@ -1106,6 +1163,9 @@ void HTTPProxySetting::ResetGlobal(DatabaseInstance *, DBConfig &config) { // Index Scan Percentage //===----------------------------------------------------------------------===// void IndexScanPercentageSetting::OnSet(SettingCallbackInfo &, Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", IndexScanPercentageSetting::Name); + } auto index_scan_percentage = input.GetValue(); if (index_scan_percentage < 0 || index_scan_percentage > 1.0) { throw InvalidInputException("the index scan percentage must be within [0, 1]"); @@ -1116,6 +1176,9 @@ void IndexScanPercentageSetting::OnSet(SettingCallbackInfo &, Value &input) { // Initial Column Segment Size //===----------------------------------------------------------------------===// void InitialColumnSegmentSizeSetting::OnSet(SettingCallbackInfo &, Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", InitialColumnSegmentSizeSetting::Name); + } auto initial_column_segment_size = input.GetValue(); if (initial_column_segment_size < DEFAULT_BLOCK_HEADER_STORAGE_SIZE) { throw InvalidInputException( @@ -1149,18 +1212,23 @@ void LogQueryPathSetting::OnSet(SettingCallbackInfo &info, Value &input) { //===----------------------------------------------------------------------===// void MaxMemorySetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) { // a percentage is relative to the system memory, since resolving it against maximum_memory would be circular - config.options.maximum_memory = + auto maximum_memory = ParseMemoryLimitOrPercentage(input.ToString(), [&]() { return GetAvailableSystemMemory(config); }); if (db) { - BufferManager::GetBufferManager(*db).SetMemoryLimit(config.options.maximum_memory); + BufferManager::GetBufferManager(*db).SetMemoryLimit(maximum_memory); } + config.options.maximum_memory = maximum_memory; } void MaxMemorySetting::ResetGlobal(DatabaseInstance *db, DBConfig &config) { + auto old_memory = config.options.maximum_memory; config.SetDefaultMaxMemory(); + auto new_memory = config.options.maximum_memory; + config.options.maximum_memory = old_memory; if (db) { - BufferManager::GetBufferManager(*db).SetMemoryLimit(config.options.maximum_memory); + BufferManager::GetBufferManager(*db).SetMemoryLimit(new_memory); } + config.options.maximum_memory = new_memory; } Value MaxMemorySetting::GetSetting(const ClientContext &context) { @@ -1172,11 +1240,14 @@ Value MaxMemorySetting::GetSetting(const ClientContext &context) { // Max Temp Directory Size //===----------------------------------------------------------------------===// void MaxTempDirectorySizeSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", MaxTempDirectorySizeSetting::Name); + } if (input == "90% of available disk space") { ResetGlobal(db, config); return; } - auto maximum_swap_space = DBConfig::ParseMemoryLimit(input.ToString()); + auto maximum_swap_space = DBConfig::ParseMemoryLimitOrMaximum(input.ToString()); if (maximum_swap_space == DConstants::INVALID_INDEX) { // We use INVALID_INDEX to indicate that the value is not set by the user // use one lower to indicate 'unlimited' @@ -1225,6 +1296,7 @@ void OperatorMemoryLimitSetting::SetLocal(ClientContext &context, const Value &i if (input.IsNull()) { config.operator_memory_limit.SetInvalid(); } else { + // an unlimited size is the same as no operator memory limit config.operator_memory_limit = DBConfig::ParseMemoryLimit(input.ToString()); } } @@ -1345,6 +1417,10 @@ void ProfilingRendererSettingsSetting::SetLocal(ClientContext &context, const Va unordered_map new_settings; for (auto &entry : MapValue::GetChildren(input)) { auto &key_value = StructValue::GetChildren(entry); + if (key_value[1].IsNull()) { + throw InvalidInputException("profiling_renderer_settings: the value of \"%s\" cannot be NULL", + key_value[0].ToString()); + } new_settings[StringUtil::Lower(key_value[0].ToString())] = key_value[1]; } // eagerly configure the renderer of the current profiler format to validate the setting values @@ -1430,6 +1506,9 @@ Value ProfilingCoverageSetting::GetSetting(const ClientContext &context) { // Progress Bar Time //===----------------------------------------------------------------------===// void ProgressBarTimeSetting::SetLocal(ClientContext &context, const Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", ProgressBarTimeSetting::Name); + } auto &config = ClientConfig::GetConfig(context); ProgressBar::SystemOverrideCheck(config); config.wait_time = input.GetValue(); @@ -1556,7 +1635,7 @@ Value StandardVectorSizeSetting::GetSetting(const ClientContext &) { //===----------------------------------------------------------------------===// void MaxStreamingBufferSizeSetting::SetLocal(ClientContext &context, const Value &input) { auto &config = ClientConfig::GetConfig(context); - config.max_streaming_buffer_size = DBConfig::ParseMemoryLimit(input.ToString()); + config.max_streaming_buffer_size = DBConfig::ParseMemoryLimitOrMaximum(input.ToString()); } void MaxStreamingBufferSizeSetting::ResetLocal(ClientContext &context) { @@ -1619,6 +1698,9 @@ void TempFileEncryptionSetting::OnSet(SettingCallbackInfo &info, Value &input) { // Tracked Metrics //===----------------------------------------------------------------------===// void TrackedMetricsSetting::SetLocal(ClientContext &context, const Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", TrackedMetricsSetting::Name); + } auto &config = ClientConfig::GetConfig(context); config.tracked_metrics.clear(); if (input.type() == LogicalType::LIST(LogicalType::VARCHAR)) { @@ -1675,6 +1757,13 @@ Value ThreadsSetting::GetSetting(const ClientContext &context) { return Value::BIGINT(NumericCast(config.options.maximum_threads)); } +static void ResizeAutomaticHTTPClientPool(optional_ptr db, DBConfig &config) { + if (!db || config.options.http_client_pool_capacity != DConstants::INVALID_INDEX) { + return; + } + config.GetHTTPTransportManager().SetCapacity(HTTPTransportManager::AutomaticCapacity(config)); +} + //===----------------------------------------------------------------------===// // Async Threads //===----------------------------------------------------------------------===// @@ -1691,6 +1780,7 @@ void AsyncThreadsSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, cons TaskScheduler::GetScheduler(*db).SetAsyncThreads(new_async_threads); } config.options.async_threads = new_async_threads; + ResizeAutomaticHTTPClientPool(db, config); } void AsyncThreadsSetting::ResetGlobal(DatabaseInstance *db, DBConfig &config) { @@ -1699,6 +1789,7 @@ void AsyncThreadsSetting::ResetGlobal(DatabaseInstance *db, DBConfig &config) { TaskScheduler::GetScheduler(*db).SetAsyncThreads(new_async_threads); } config.options.async_threads = new_async_threads; + ResizeAutomaticHTTPClientPool(db, config); } Value AsyncThreadsSetting::GetSetting(const ClientContext &context) { @@ -1706,11 +1797,42 @@ Value AsyncThreadsSetting::GetSetting(const ClientContext &context) { return Value::BIGINT(NumericCast(config.options.async_threads)); } +void HTTPClientPoolCapacitySetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) { + if (input.IsNull()) { + throw InvalidInputException("http_client_pool_capacity must be a positive integer"); + } + auto new_val = input.GetValue(); + if (new_val <= 0) { + throw InvalidInputException( + "http_client_pool_capacity must be a positive integer, RESET it to return to the automatic value"); + } + auto new_capacity = NumericCast(new_val); + if (db) { + config.GetHTTPTransportManager().SetCapacity(new_capacity); + } + config.options.http_client_pool_capacity = new_capacity; +} + +void HTTPClientPoolCapacitySetting::ResetGlobal(DatabaseInstance *db, DBConfig &config) { + if (db) { + config.GetHTTPTransportManager().SetCapacity(HTTPTransportManager::AutomaticCapacity(config)); + } + config.options.http_client_pool_capacity = DConstants::INVALID_INDEX; +} + +Value HTTPClientPoolCapacitySetting::GetSetting(const ClientContext &context) { + auto &config = DBConfig::GetConfig(context); + return Value::BIGINT(NumericCast(config.GetHTTPTransportManager().GetCapacity())); +} + //===----------------------------------------------------------------------===// // Warnings As Errors //===----------------------------------------------------------------------===// void WarningsAsErrorsSetting::OnSet(SettingCallbackInfo &info, Value &input) { + if (input.IsNull()) { + throw InvalidInputException("%s setting cannot be NULL", WarningsAsErrorsSetting::Name); + } auto &log_manager = LogManager::Get(*info.context); if (input == Value(true) && !log_manager.GetConfig().enabled) { throw Exception( @@ -1726,6 +1848,7 @@ void WriteBufferRowGroupMemoryLimitSetting::SetGlobal(DatabaseInstance *db, DBCo if (input.IsNull() || input.ToString().empty()) { config.options.write_buffer_row_group_memory_limit = optional_idx(); } else { + // an unlimited size means that there is no limit config.options.write_buffer_row_group_memory_limit = DBConfig::ParseMemoryLimit(input.ToString()); } } @@ -1748,20 +1871,61 @@ Value WriteBufferRowGroupMemoryLimitSetting::GetSetting(const ClientContext &con void CurrentTransactionInvalidationPolicySetting::OnSet(SettingCallbackInfo &info, Value &input) { if (!info.context) { throw InvalidInputException( - "current_transaction_invalidaton_policy can only be set when there is an active client context"); + "current_transaction_invalidation_policy can only be set when there is an active client context"); } info.context->transaction.SetInvalidationPolicy( EnumUtil::FromString(input.GetValue())); } -void CurrentDialectSetting::OnSet(SettingCallbackInfo &info, Value &input) { +void CurrentDialectSetting::SetLocal(ClientContext &context, const Value &input) { + if (!OnLocalSet(context, input)) { + return; + } + auto &client_config = ClientConfig::GetConfig(context); + auto &config = DatabaseInstance::GetDatabase(context).config; + if (input.IsNull()) { - throw InvalidInputException("current_dialect setting cannot be NULL"); + client_config.current_dialect = std::nullopt; + return; } auto dialect_name = input.GetValue(); - if (!info.config.GetCallbackManager().HasDialectExtension(dialect_name)) { + + auto dialect_extension_p = config.GetCallbackManager().GetDialectExtension(dialect_name); + if (!dialect_extension_p) { throw InvalidInputException("Dialect \"%s\" is not installed", dialect_name); } + auto &dialect_extension = *dialect_extension_p; + //! The grammar gets lazily compiled, load it if it wasn't compiled yet + (void)dialect_extension.GetCompiledGrammar(context); + client_config.current_dialect = dialect_name; + auto &compatibility_mode = dialect_extension.GetCompatibilityMode(); + if (compatibility_mode) { + Settings::Set(context, SetScope::LOCAL, + Value(EnumUtil::ToString(*compatibility_mode))); + } +} + +void CurrentDialectSetting::ResetLocal(ClientContext &context) { + if (!OnLocalReset(context)) { + return; + } + ClientConfig::GetConfig(context).current_dialect = std::nullopt; +} + +bool CurrentDialectSetting::OnLocalSet(ClientContext &context, const Value &input) { + return true; +} + +bool CurrentDialectSetting::OnLocalReset(ClientContext &context) { + return true; +} + +Value CurrentDialectSetting::GetSetting(const ClientContext &context) { + auto &client_config = ClientConfig::GetConfig(context); + if (client_config.current_dialect) { + return Value(*client_config.current_dialect); + } + return Value(); } void ActiveGrammarExtensionsSetting::SetLocal(ClientContext &context, const Value &input) { @@ -1778,7 +1942,8 @@ void ActiveGrammarExtensionsSetting::SetLocal(ClientContext &context, const Valu auto &config = DatabaseInstance::GetDatabase(context).config; auto &callback_manager = config.GetCallbackManager(); - case_insensitive_set_t selected_extensions; + case_insensitive_set_t distinct_names; + vector selected_extensions; if (input.type().id() != LogicalTypeId::LIST) { throw InvalidInputException("'active_grammar_extensions' setting value should be of type VARCHAR[], not %s", input.type().ToString()); @@ -1790,9 +1955,10 @@ void ActiveGrammarExtensionsSetting::SetLocal(ClientContext &context, const Valu val.type().ToString()); } auto val_str = val.GetValue(); - if (!selected_extensions.insert(val_str).second) { + if (!distinct_names.insert(val_str).second) { throw InvalidInputException("'active_grammar_extensions' list contains duplicate value '%s'", val_str); } + selected_extensions.emplace_back(std::move(val_str)); } vector missing; @@ -1813,7 +1979,8 @@ void ActiveGrammarExtensionsSetting::SetLocal(ClientContext &context, const Valu return; } - auto compiled_grammar = CompiledGrammar::Create(context, selected_extensions); + auto compiled_grammar = + DatabaseInstance::GetDatabase(context).GetParserCache().GetMatcher(context, selected_extensions); client_config.active_grammar_extensions = std::move(selected_extensions); client_config.cached_grammar = std::move(compiled_grammar); } @@ -1870,6 +2037,10 @@ void EnableObjectCacheSetting::OnSet(SettingCallbackInfo &info, Value &) { WarnDeprecatedSetting(info, EnableObjectCacheSetting::Name); } +void ErrorOnDivisionByZeroSetting::OnSet(SettingCallbackInfo &info, Value &) { + WarnDeprecatedSetting(info, ErrorOnDivisionByZeroSetting::Name); +} + void ExperimentalMetadataReuseSetting::OnSet(SettingCallbackInfo &info, Value &) { WarnDeprecatedSetting(info, ExperimentalMetadataReuseSetting::Name); } @@ -1886,10 +2057,6 @@ void LegacyMetricsFormatSetting::OnSet(SettingCallbackInfo &info, Value &) { WarnDeprecatedSetting(info, LegacyMetricsFormatSetting::Name); } -void NullOnDivisionByZeroSetting::OnSet(SettingCallbackInfo &info, Value &) { - WarnDeprecatedSetting(info, NullOnDivisionByZeroSetting::Name); -} - void ProduceArrowStringViewSetting::OnSet(SettingCallbackInfo &info, Value &) { WarnDeprecatedSetting(info, ProduceArrowStringViewSetting::Name); } diff --git a/src/duckdb/src/main/statement_iterator.cpp b/src/duckdb/src/main/statement_iterator.cpp index 36cf603da..5a49b4d2c 100644 --- a/src/duckdb/src/main/statement_iterator.cpp +++ b/src/duckdb/src/main/statement_iterator.cpp @@ -23,21 +23,17 @@ StatementIterator::~StatementIterator() = default; StatementIterator::StatementIterator(StatementIterator &&) noexcept = default; bool StatementIterator::Peek() { - // More buffered engine statements from the current peel's expansion? - if (buffer_cursor < buffer.size()) { - return true; - } if (pending_statement) { return true; } - // Otherwise, is there another parse-facing statement to pull? Parses ahead, does NOT preprocess - // — safe to use as a lookahead. + parser_start_time = Timestamp::GetCurrentTimestamp(); parser_timer.Start(); if (!source.Peek()) { parser_timer.Reset(); return false; } parser_timer.End(); + parser_end_time = Timestamp::GetCurrentTimestamp(); pending_statement = source.GetStatement(); if (!pending_statement) { parser_timer.Reset(); @@ -47,71 +43,32 @@ bool StatementIterator::Peek() { } bool StatementIterator::HasMore() { - // Buffered engine statements from the current peel still remain? - if (buffer_cursor < buffer.size()) { - return true; - } if (pending_statement) { return true; } - // Otherwise defer to the parse-facing source's grammar-free existence check. return source.HasMore(); } -unique_ptr StatementIterator::GetStatementInternal(optional_ptr lock, - bool profile_statement) { - // Drain the current peel's expansion first. - if (buffer_cursor < buffer.size()) { - auto statement = std::move(buffer[buffer_cursor++]); - if (profile_statement) { - auto &profiler = QueryProfiler::Get(context); - profiler.StartQuery(statement->query, StatementIteratorIsExplainAnalyze(statement.get())); - profiler.AddParserTime(parser_timer); - } - parser_timer.Reset(); - return statement; - } - // Pull the next parse-facing statement. +unique_ptr StatementIterator::GetStatementInternal(bool profile_statement) { if (!pending_statement && !Peek()) { - return nullptr; // exhausted - } - auto stmt = std::move(pending_statement); - buffer.clear(); - buffer_cursor = 0; - buffer.push_back(std::move(stmt)); - // Preprocess the peel into one-or-more engine-facing statements. This runs in Get (not Peek) so it - // sees the transaction state left by the previously executed statement. - context.PreprocessStatements(buffer, lock); - if (buffer.empty()) { - parser_timer.Reset(); - // Preprocessing swallowed the peel — caller skips with `continue`; the next Get pulls on. return nullptr; } - buffer_cursor = 1; - auto statement = std::move(buffer[0]); + auto statement = std::move(pending_statement); if (profile_statement) { auto &profiler = QueryProfiler::Get(context); profiler.StartQuery(statement->query, StatementIteratorIsExplainAnalyze(statement.get())); - profiler.AddParserTime(parser_timer); + profiler.AddParserTime(parser_timer, parser_start_time, parser_end_time); } parser_timer.Reset(); return statement; } unique_ptr StatementIterator::GetStatement() { - return GetStatementInternal(nullptr, false); -} - -unique_ptr StatementIterator::GetStatementWithLock(ClientContextLock &lock) { - return GetStatementInternal(&lock, false); + return GetStatementInternal(false); } unique_ptr StatementIterator::GetStatementForExecution() { - return GetStatementInternal(nullptr, true); -} - -unique_ptr StatementIterator::GetStatementForExecutionWithLock(ClientContextLock &lock) { - return GetStatementInternal(&lock, true); + return GetStatementInternal(true); } } // namespace duckdb diff --git a/src/duckdb/src/main/stream_query_result.cpp b/src/duckdb/src/main/stream_query_result.cpp deleted file mode 100644 index c97335b1e..000000000 --- a/src/duckdb/src/main/stream_query_result.cpp +++ /dev/null @@ -1,272 +0,0 @@ -#include "duckdb/main/stream_query_result.hpp" - -#include "duckdb/main/client_context.hpp" -#include "duckdb/main/pending_query_result.hpp" -#include "duckdb/execution/executor.hpp" -#include "duckdb/main/materialized_query_result.hpp" -#include "duckdb/common/box_renderer.hpp" -#include "duckdb/main/database.hpp" - -namespace duckdb { - -StreamQueryResult::StreamQueryResult(StatementType statement_type, StatementProperties properties, - vector types, vector names, - ClientProperties client_properties, shared_ptr data) - : QueryResult(QueryResultType::STREAM_RESULT, statement_type, std::move(properties), std::move(types), - std::move(names), std::move(client_properties)), - buffered_data(std::move(data)) { - context = buffered_data->GetContext(); -} - -StreamQueryResult::StreamQueryResult(ErrorData error) : QueryResult(QueryResultType::STREAM_RESULT, std::move(error)) { -} - -StreamQueryResult::~StreamQueryResult() { -} - -string StreamQueryResult::ToString() { - string result; - if (!HasError()) { - result = HeaderToString(); - result += "[[STREAM RESULT]]"; - } else { - result = GetError() + "\n"; - } - return result; -} - -unique_ptr StreamQueryResult::LockContext() { - if (!context) { - string error_str = "Attempting to execute an unsuccessful or closed pending query result"; - if (HasError()) { - error_str += StringUtil::Format("\nError: %s", GetError()); - } - throw InvalidInputException(error_str); - } - return context->LockContext(); -} - -StreamExecutionResult StreamQueryResult::ExecuteTaskInternal(ClientContextLock &lock) { - return buffered_data->ExecuteTaskInternal(*this, lock); -} - -StreamExecutionResult StreamQueryResult::ExecuteTask() { - auto lock = LockContext(); - return ExecuteTaskInternal(*lock); -} - -void StreamQueryResult::WaitForTask() { - auto lock = LockContext(); - // Nothing to wait for on a result being materialized; ExecuteTask reports it - if (buffered_data->Decide(ResultLifetime::DRAINING) != ResultLifetime::DRAINING) { - return; - } - buffered_data->UnblockSinks(); - context->WaitForTask(*lock, *this); -} - -static bool ExecutionErrorOccurred(StreamExecutionResult result) { - if (result == StreamExecutionResult::EXECUTION_CANCELLED) { - return true; - } - if (result == StreamExecutionResult::EXECUTION_ERROR) { - return true; - } - return false; -} - -unique_ptr StreamQueryResult::FetchNextInternal(ClientContextLock &lock) { - bool invalidate_query = true; - unique_ptr chunk; - try { - // fetch the chunk and return it - auto stream_execution_result = buffered_data->ReplenishBuffer(*this, lock); - if (ExecutionErrorOccurred(stream_execution_result)) { - return chunk; - } - chunk = buffered_data->Scan(); - if (!chunk || chunk->ColumnCount() == 0 || chunk->size() == 0) { - context->CleanupInternal(lock, this); - chunk = nullptr; - } - return chunk; - } catch (std::exception &ex) { - ErrorData error(ex); - if (!Exception::InvalidatesTransaction(error.Type())) { - // standard exceptions do not invalidate the current transaction - invalidate_query = false; - } else if (Exception::InvalidatesDatabase(error.Type())) { - // fatal exceptions invalidate the entire database - auto &db_instance = DatabaseInstance::GetDatabase(*context); - ValidChecker::Invalidate(db_instance, error.RawMessage()); - } - context->ProcessError(error, context->GetCurrentQuery()); - SetError(std::move(error)); - } catch (...) { // LCOV_EXCL_START - SetError(ErrorData("Unhandled exception in FetchInternal")); - } // LCOV_EXCL_STOP - context->CleanupInternal(lock, this, invalidate_query); - return nullptr; -} - -unique_ptr StreamQueryResult::FetchInternal() { - unique_ptr chunk; - { - auto lock = LockContext(); - CheckExecutableInternal(*lock); - chunk = FetchNextInternal(*lock); - } - if (!chunk || chunk->ColumnCount() == 0 || chunk->size() == 0) { - if (!HasError()) { - buffered_data->AssertNoBlockedSinks(); - } - Close(); - return nullptr; - } - return chunk; -} - -#ifdef DUCKDB_ALTERNATIVE_VERIFY -static unique_ptr AlternativeFetch(StreamQueryResult &stream_result) { - // We first use StreamQueryResult::ExecuteTask until IsChunkReady becomes true - // then call Fetch - StreamExecutionResult execution_result; - while (!StreamQueryResult::IsChunkReady(execution_result = stream_result.ExecuteTask())) { - if (execution_result == StreamExecutionResult::BLOCKED) { - stream_result.WaitForTask(); - } - } - if (execution_result == StreamExecutionResult::EXECUTION_CANCELLED) { - throw InvalidInputException("The execution of the query was cancelled before it could finish, likely " - "caused by executing a different query"); - } - if (execution_result == StreamExecutionResult::EXECUTION_ERROR) { - // Mirror Fetch: the error is already on the result, so the drain ends instead of throwing - stream_result.Close(); - return nullptr; - } - return stream_result.Fetch(); -} -#endif - -unique_ptr StreamQueryResult::MaterializeByDraining() { - auto collection = make_uniq(Allocator::DefaultAllocator(), GetTypes()); - - ColumnDataAppendState append_state; - collection->InitializeAppend(append_state); - while (true) { -#ifdef DUCKDB_ALTERNATIVE_VERIFY - auto chunk = AlternativeFetch(*this); -#else - auto chunk = Fetch(); -#endif - if (!chunk || chunk->size() == 0) { - break; - } - collection->Append(append_state, *chunk); - } - if (HasError()) { - return make_uniq(GetErrorObject()); - } - return make_uniq(GetStatementType(), GetStatementProperties(), GetNames(), - std::move(collection), client_properties); -} - -unique_ptr StreamQueryResult::Materialize() { - if (HasError() || !context) { - return make_uniq(GetErrorObject()); - } - bool retained; - unique_ptr result; - { - auto lock = LockContext(); - CheckExecutableInternal(*lock); - retained = buffered_data->Decide(ResultLifetime::RETAINED) == ResultLifetime::RETAINED; - if (retained) { - // Producers append into the sink's collection from here on, so the query runs to - // completion and the collection is taken from the sink instead of copied out of the buffer - PendingExecutionResult execution_result; - while (!PendingQueryResult::IsExecutionFinished(execution_result = - context->ExecuteTaskInternal(*lock, *this))) { - if (execution_result == PendingExecutionResult::BLOCKED || - execution_result == PendingExecutionResult::RESULT_READY) { - context->WaitForTask(*lock, *this); - } - } - if (execution_result == PendingExecutionResult::EXECUTION_FINISHED) { - result = context->GetExecutor().GetResult(); - context->CleanupInternal(*lock, result.get(), false); - } - } - } - if (!retained) { - return MaterializeByDraining(); - } - Close(); - if (HasError()) { - return make_uniq(GetErrorObject()); - } - D_ASSERT(result && result->GetResultType() == QueryResultType::MATERIALIZED_RESULT); - return unique_ptr_cast(std::move(result)); -} - -bool StreamQueryResult::IsOpenInternal(ClientContextLock &lock) { - bool invalidated = HasError() || !context; - if (!invalidated) { - invalidated = !context->IsActiveResult(lock, *this); - } - return !invalidated; -} - -void StreamQueryResult::CheckExecutableInternal(ClientContextLock &lock) { - if (!IsOpenInternal(lock)) { - string error_str = "Attempting to execute an unsuccessful or closed pending query result"; - if (HasError()) { - error_str += StringUtil::Format("\nError: %s", GetError()); - } - throw InvalidInputException(error_str); - } -} - -bool StreamQueryResult::IsOpen() { - if (HasError() || !context) { - return false; - } - auto lock = LockContext(); - return IsOpenInternal(*lock); -} - -void StreamQueryResult::Close() { - buffered_data->Close(); - if (context) { - auto lock = LockContext(); - if (context->IsActiveResult(*lock, *this)) { - // Abandoned before the stream was fully drained: release the active-query state now - // (matching InitialCleanup) instead of leaking it until the next query or context teardown. - context->CleanupInternal(*lock, this, false); - } - } - context.reset(); -} - -bool StreamQueryResult::IsChunkReady(StreamExecutionResult result) { - if (result == StreamExecutionResult::CHUNK_READY) { - // A chunk is ready to be fetched with Fetch() - return true; - } - if (result == StreamExecutionResult::EXECUTION_CANCELLED) { - // Another query execution was started that cancelled this one - return true; - } - if (result == StreamExecutionResult::EXECUTION_ERROR) { - // An error was encountered while executing the final pipeline - return true; - } - if (result == StreamExecutionResult::EXECUTION_FINISHED) { - // The final pipeline completed successfully - return true; - } - return false; -} - -} // namespace duckdb diff --git a/src/duckdb/src/optimizer/aggregate_function_rewriter.cpp b/src/duckdb/src/optimizer/aggregate_function_rewriter.cpp index 5b4e4251a..93b24c9b3 100644 --- a/src/duckdb/src/optimizer/aggregate_function_rewriter.cpp +++ b/src/duckdb/src/optimizer/aggregate_function_rewriter.cpp @@ -5,7 +5,7 @@ #include "duckdb/function/aggregate/distributive_functions.hpp" #include "duckdb/optimizer/matcher/expression_matcher.hpp" #include "duckdb/optimizer/aggregate_rewrite.hpp" -#include "duckdb/optimizer/builtin_function_lookup.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/optimizer/optimizer.hpp" #include "duckdb/planner/binder.hpp" #include "duckdb/planner/column_binding_map.hpp" @@ -14,6 +14,7 @@ #include "duckdb/planner/expression/bound_columnref_expression.hpp" #include "duckdb/planner/expression/bound_constant_expression.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" +#include "duckdb/planner/expression_nullability.hpp" #include "duckdb/planner/operator/logical_aggregate.hpp" #include "duckdb/planner/operator/logical_projection.hpp" @@ -45,6 +46,9 @@ class AggregateRewriteRule { public: virtual bool ShouldSkip(const LogicalAggregate &aggr) const = 0; + virtual bool ShouldRewrite(const LogicalAggregate &aggr, Expression &expr) { + return true; + } virtual unique_ptr Rewrite(unique_ptr &expr, vector> &bindings, vector> &additional_expressions) = 0; virtual unique_ptr @@ -66,7 +70,8 @@ static unique_ptr GetSmallIntegerTypesMatcher() { //! AVG(x) -> SUM(x) / COUNT(x) class AvgRewriteRule : public AggregateRewriteRule { public: - explicit AvgRewriteRule(Optimizer &optimizer) : AggregateRewriteRule(optimizer) { + AvgRewriteRule(Optimizer &optimizer, LogicalOperator &root) + : AggregateRewriteRule(optimizer), nullability(optimizer.context, root) { auto op = make_uniq(); op->function = make_uniq("avg"); op->type = make_uniq(); @@ -75,20 +80,68 @@ class AvgRewriteRule : public AggregateRewriteRule { child_matcher->type = make_uniq(); op->matchers.push_back(std::move(child_matcher)); matcher = std::move(op); + + auto count = make_uniq(); + count->function = make_uniq(identifier_set_t {"count", "count_star"}); + count->policy = SetMatcher::Policy::SOME; + count_matcher = std::move(count); } public: bool ShouldSkip(const LogicalAggregate &aggr) const override { - // AVG -> SUM/COUNT is correct under any grouping (both ignore NULLs - // identically), so ROLLUP/CUBE/GROUPING SETS are safe to rewrite. - // Decomposing here is also the prerequisite for partial-aggregate - // pushdown to fire on AVG queries. The remaining guard is for - // grouping_functions: their per-row grouping-set bitmasks reference - // the original AVG column directly, and the rewrite has no way to - // translate those references. return !aggr.grouping_functions.empty(); } + bool ShouldRewrite(const LogicalAggregate &aggr, Expression &expr) override { + if (aggr.groups.empty()) { + return true; + } + if (optimizer.OptimizerDisabled(OptimizerType::COMMON_AGGREGATE)) { + return false; + } + auto &child = *expr.Cast().GetChildren()[0]; + FunctionBinder binder(optimizer.context); + vector> sum_args; + sum_args.push_back(child.Copy()); + auto sum = binder.BindAggregateFunction( + GetBuiltinAggregateFunction(optimizer.context, "sum", {child.GetReturnType()}), std::move(sum_args)); + vector> count_args; + count_args.push_back(child.Copy()); + auto count = binder.BindAggregateFunction( + GetBuiltinAggregateFunction(optimizer.context, "count", {child.GetReturnType()}), std::move(count_args)); + + D_ASSERT(aggr.children.size() == 1); + auto is_not_null = [&](const Expression &input) { + if (input.GetExpressionClass() == ExpressionClass::BOUND_CONSTANT) { + return !input.Cast().GetValue().IsNull(); + } + return nullability.IsNotNull(*aggr.children[0], input); + }; + const auto can_share_count = + !optimizer.OptimizerDisabled(OptimizerType::STATISTICS_PROPAGATION) && is_not_null(child); + for (auto &other : aggr.expressions) { + // Identical AVGs already share their native state. + if (expr.Equals(*other)) { + continue; + } + if (sum->Equals(*other) || count->Equals(*other)) { + return true; + } + if (!can_share_count || other->IsVolatile()) { + continue; + } + vector> bindings; + if (!count_matcher->Match(*other, bindings) && !matcher->Match(*other, bindings)) { + continue; + } + auto &children = other->Cast().GetChildren(); + if (children.empty() || (children.size() == 1 && is_not_null(*children[0]))) { + return true; + } + } + return false; + } + unique_ptr Rewrite(unique_ptr &expr, vector> &bindings, vector> &additional_expressions) override { FunctionBinder function_binder(optimizer.context); @@ -111,6 +164,10 @@ class AvgRewriteRule : public AggregateRewriteRule { // SUM(x) / COUNT(x): additional_expressions[0] is the COUNT(x) ref return optimizer.BindScalarFunction("/", std::move(aggr_ref), std::move(additional_expressions[0])); } + +private: + NotNullExpressionAnalyzer nullability; + unique_ptr count_matcher; }; //! SUM(x + C) -> SUM(x) + C * COUNT(x) @@ -346,6 +403,9 @@ class AggregateFunctionRewriterInternal : public LogicalOperatorVisitor { if (!rule.matcher->Match(*expr, bindings)) { continue; } + if (!rule.ShouldRewrite(aggr, *expr)) { + continue; + } RewriteInfo rewrite_info; auto count_arg = rule.Rewrite(expr, bindings, rewrite_info.additional_expressions); @@ -434,9 +494,6 @@ class AggregateFunctionRewriterInternal : public LogicalOperatorVisitor { }; AggregateFunctionRewriter::AggregateFunctionRewriter(Optimizer &optimizer) : optimizer(optimizer) { - rules.push_back(make_uniq(optimizer)); - rules.push_back(make_uniq(optimizer)); - rules.push_back(make_uniq(optimizer)); } AggregateFunctionRewriter::~AggregateFunctionRewriter() { @@ -444,6 +501,10 @@ AggregateFunctionRewriter::~AggregateFunctionRewriter() { void AggregateFunctionRewriter::Optimize(unique_ptr &op) { RewriteAggregateCallbacks(optimizer, op); + vector> rules; + rules.push_back(make_uniq(optimizer, *op)); + rules.push_back(make_uniq(optimizer)); + rules.push_back(make_uniq(optimizer)); // Run each rule as an independent pass so that transformations by an earlier rule affect later rules for (auto &rule : rules) { AggregateFunctionRewriterInternal rewriter(optimizer, *rule); diff --git a/src/duckdb/src/optimizer/aggregate_reuse.cpp b/src/duckdb/src/optimizer/aggregate_reuse.cpp index 6bf05294a..cb66fc78b 100644 --- a/src/duckdb/src/optimizer/aggregate_reuse.cpp +++ b/src/duckdb/src/optimizer/aggregate_reuse.cpp @@ -2,7 +2,7 @@ #include "duckdb/optimizer/aggregate_rewrite_helper.hpp" #include "duckdb/optimizer/aggregate_reuse_internal.hpp" -#include "duckdb/optimizer/builtin_function_lookup.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/optimizer/optimizer.hpp" #include "duckdb/function/aggregate/distributive_functions.hpp" #include "duckdb/function/function_binder.hpp" diff --git a/src/duckdb/src/optimizer/aggregate_rewrite.cpp b/src/duckdb/src/optimizer/aggregate_rewrite.cpp index 701811f08..d8bd10ec4 100644 --- a/src/duckdb/src/optimizer/aggregate_rewrite.cpp +++ b/src/duckdb/src/optimizer/aggregate_rewrite.cpp @@ -2,7 +2,7 @@ #include "duckdb/function/aggregate/distributive_functions.hpp" #include "duckdb/function/function_binder.hpp" -#include "duckdb/optimizer/builtin_function_lookup.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/optimizer/optimizer.hpp" #include "duckdb/planner/binder.hpp" #include "duckdb/planner/expression/bound_aggregate_expression.hpp" @@ -85,13 +85,7 @@ static unique_ptr CreateAggregateSortKey(ClientContext &context, con sort_children.push_back(make_uniq(Value(order.GetOrderModifier()))); } FunctionBinder function_binder(context); - ErrorData error; - auto sort_key = function_binder.BindScalarFunction(Identifier::DefaultSchema(), "create_sort_key", - std::move(sort_children), error); - if (!sort_key) { - error.Throw(); - } - return sort_key; + return function_binder.BindScalarFunction(Identifier::DefaultSchema(), "create_sort_key", std::move(sort_children)); } unique_ptr FrequencyAggregateRewrite::Create(AggregateRewriteInput &input, bool ignore_nulls, diff --git a/src/duckdb/src/optimizer/aggregate_rewrite_helper.cpp b/src/duckdb/src/optimizer/aggregate_rewrite_helper.cpp index 02c75446c..a3c0da1b4 100644 --- a/src/duckdb/src/optimizer/aggregate_rewrite_helper.cpp +++ b/src/duckdb/src/optimizer/aggregate_rewrite_helper.cpp @@ -87,33 +87,29 @@ void AggregateRewriteHelper::StageVolatileAggregateInputs(Optimizer &optimizer, return; } - child->ResolveOperatorTypes(); auto child_bindings = child->GetColumnBindings(); auto projection_index = optimizer.binder.GenerateTableIndex(); + auto projection = LogicalProjection::CreateIdentity(projection_index, std::move(child)); column_binding_map_t projection_replacements; - vector> projection_expressions; - projection_expressions.reserve(child_bindings.size()); for (idx_t col_idx = 0; col_idx < child_bindings.size(); col_idx++) { projection_replacements[child_bindings[col_idx]] = ColumnBinding(projection_index, ProjectionIndex(col_idx)); - projection_expressions.push_back( - make_uniq(child->types[col_idx], child_bindings[col_idx])); } for (auto &group : aggr.groups) { - StageVolatileExpression(group, projection_index, projection_expressions); + StageVolatileExpression(group, projection_index, projection->expressions); } for (auto &expr : aggr.expressions) { auto &aggregate = expr->Cast(); for (auto &child_expr : aggregate.GetChildrenMutable()) { - StageVolatileExpression(child_expr, projection_index, projection_expressions); + StageVolatileExpression(child_expr, projection_index, projection->expressions); } if (aggregate.GetOrderBys()) { for (auto &order : aggregate.GetOrderBysMutable()->orders) { - StageVolatileExpression(order.expression, projection_index, projection_expressions); + StageVolatileExpression(order.expression, projection_index, projection->expressions); } } - StageVolatileExpression(aggregate.GetFilterMutable(), projection_index, projection_expressions); + StageVolatileExpression(aggregate.GetFilterMutable(), projection_index, projection->expressions); } for (auto &group : aggr.groups) { @@ -123,8 +119,6 @@ void AggregateRewriteHelper::StageVolatileAggregateInputs(Optimizer &optimizer, RebindExpression(expr, projection_replacements); } - auto projection = make_uniq(projection_index, std::move(projection_expressions)); - projection->children.push_back(std::move(child)); child = std::move(projection); } diff --git a/src/duckdb/src/optimizer/build_probe_side_optimizer.cpp b/src/duckdb/src/optimizer/build_probe_side_optimizer.cpp index 8a3f1b6da..cc6a55166 100644 --- a/src/duckdb/src/optimizer/build_probe_side_optimizer.cpp +++ b/src/duckdb/src/optimizer/build_probe_side_optimizer.cpp @@ -218,7 +218,7 @@ double BuildProbeSideOptimizer::GetBuildSize(vector types, const id // This is a not a smooth cost function, so instead we do the average, which is ~3 * sizeof(ht_entry_t) row_width += 3 * sizeof(ht_entry_t); - return static_cast(row_width * cardinality); + return MaxValue(static_cast(row_width) * static_cast(cardinality), sizeof(ht_entry_t)); } idx_t BuildProbeSideOptimizer::ChildHasJoins(LogicalOperator &op) { diff --git a/src/duckdb/src/optimizer/column_binding_replacer.cpp b/src/duckdb/src/optimizer/column_binding_replacer.cpp index 9b462d8e6..bab6b3010 100644 --- a/src/duckdb/src/optimizer/column_binding_replacer.cpp +++ b/src/duckdb/src/optimizer/column_binding_replacer.cpp @@ -4,6 +4,7 @@ #include "duckdb/planner/expression/bound_subquery_expression.hpp" #include "duckdb/planner/operator/logical_cte.hpp" #include "duckdb/planner/operator/logical_dependent_join.hpp" +#include "duckdb/planner/operator/logical_secure_view.hpp" #include "duckdb/planner/column_binding_map.hpp" namespace duckdb { @@ -137,11 +138,24 @@ void ColumnBindingReplacer::VisitOperator(LogicalOperator &op) { if (stop_operator && stop_operator.get() == &op) { return; } - VisitOperatorChildren(op); + // Binding replacement keeps child layouts intact, so projection maps are left untouched + for (auto &child : op.children) { + VisitOperator(*child); + } VisitOperatorBindings(op); } void ColumnBindingReplacer::VisitOperatorBindings(LogicalOperator &op) { + if (op.type == LogicalOperatorType::LOGICAL_SECURE_VIEW) { + auto &view = op.Cast(); + for (auto &binding : view.output_bindings) { + for (auto &replacement : replacement_bindings) { + if (binding == replacement.old_binding) { + binding = replacement.new_binding; + } + } + } + } VisitOperatorExpressions(op); } @@ -301,6 +315,31 @@ static ReplacementBinding ResolveBoundaryReplacement(ColumnBinding binding, return ReplacementBinding(binding, binding); } +void ColumnBindingRewrite::RewriteChild( + unique_ptr &op, idx_t child_index, + const std::function &, BindingReplacementGraph &)> &rewrite) { + if (child_index >= op->children.size()) { + throw InternalException("Binding rewrite child index %llu out of range", child_index); + } + auto old_child_bindings = op->children[child_index]->GetColumnBindings(); + auto projection_map = LogicalOperatorVisitor::GetProjectionMap(*op, child_index); + if (projection_map && projection_map->empty()) { + // Freeze the implicit selection before the callback can change the child's output layout. + for (idx_t i = 0; i < old_child_bindings.size(); i++) { + projection_map->emplace_back(i); + } + } + BindingReplacementGraph replacements; + rewrite(op->children[child_index], replacements); + if (!op->children[child_index]) { + throw InternalException("Child rewrite requires a non-null child"); + } + if (projection_map && old_child_bindings.empty() && !op->children[child_index]->GetColumnBindings().empty()) { + throw InternalException("Child rewrite cannot preserve an empty implicit projection"); + } + ApplyToChild(op, child_index, std::move(old_child_bindings), replacements); +} + void ColumnBindingRewrite::ApplyToChild(unique_ptr &op, idx_t child_index, vector old_child_bindings, const BindingReplacementGraph &replacements) { diff --git a/src/duckdb/src/optimizer/common_subplan_optimizer.cpp b/src/duckdb/src/optimizer/common_subplan_optimizer.cpp index 6fdbfe263..b8b82fe8f 100644 --- a/src/duckdb/src/optimizer/common_subplan_optimizer.cpp +++ b/src/duckdb/src/optimizer/common_subplan_optimizer.cpp @@ -19,6 +19,54 @@ namespace duckdb { //===--------------------------------------------------------------------===// // Subplan Signature/Info //===--------------------------------------------------------------------===// +static vector GetExpandedColumnBindings(LogicalOperator &op) { + switch (op.type) { + case LogicalOperatorType::LOGICAL_FILTER: + case LogicalOperatorType::LOGICAL_ORDER_BY: + case LogicalOperatorType::LOGICAL_LIMIT: + case LogicalOperatorType::LOGICAL_TOP_N: + case LogicalOperatorType::LOGICAL_DISTINCT: + return GetExpandedColumnBindings(*op.children[0]); + case LogicalOperatorType::LOGICAL_WINDOW: + case LogicalOperatorType::LOGICAL_UNNEST: { + auto bindings = GetExpandedColumnBindings(*op.children[0]); + const auto table_index = op.GetTableIndex()[0]; + for (auto col_idx : ProjectionIndex::GetIndexes(op.expressions.size())) { + bindings.emplace_back(table_index, col_idx); + } + return bindings; + } + case LogicalOperatorType::LOGICAL_ANY_JOIN: + case LogicalOperatorType::LOGICAL_ASOF_JOIN: + case LogicalOperatorType::LOGICAL_COMPARISON_JOIN: { + auto &join = op.Cast(); + auto left_bindings = GetExpandedColumnBindings(*op.children[0]); + if (join.join_type == JoinType::SEMI || join.join_type == JoinType::ANTI) { + return left_bindings; + } + if (join.join_type == JoinType::MARK) { + left_bindings.emplace_back(join.mark_index, ProjectionIndex(0)); + return left_bindings; + } + auto right_bindings = GetExpandedColumnBindings(*op.children[1]); + if (join.join_type == JoinType::RIGHT_SEMI || join.join_type == JoinType::RIGHT_ANTI) { + return right_bindings; + } + left_bindings.insert(left_bindings.end(), right_bindings.begin(), right_bindings.end()); + return left_bindings; + } + case LogicalOperatorType::LOGICAL_CROSS_PRODUCT: + case LogicalOperatorType::LOGICAL_POSITIONAL_JOIN: { + auto left_bindings = GetExpandedColumnBindings(*op.children[0]); + auto right_bindings = GetExpandedColumnBindings(*op.children[1]); + left_bindings.insert(left_bindings.end(), right_bindings.begin(), right_bindings.end()); + return left_bindings; + } + default: + return op.GetColumnBindings(); + } +} + enum class ConversionType { TO_CANONICAL, RESTORE_ORIGINAL, @@ -100,7 +148,7 @@ class PlanSignatureTableIndexMap { // Store temporary mapping for (const auto &child_op : op.children) { - for (const auto &child_cb : child_op->GetColumnBindings()) { + for (const auto &child_cb : GetExpandedColumnBindings(*child_op)) { const auto &original = child_cb.table_index; auto it = to_canonical_table_index.find(original); if (it != to_canonical_table_index.end()) { @@ -250,6 +298,9 @@ class PlanSignatureTableIndexMap { auto &get = op.Cast(); switch (TYPE) { case ConversionType::TO_CANONICAL: { + // Source ordinality is represented by the bound operators. + source_ordinality = get.source_ordinality; + get.source_ordinality = OrdinalityType::WITHOUT_ORDINALITY; D_ASSERT(column_ids.empty()); // Grab selected GET columns and populate with all possible columns column_ids = std::move(get.GetMutableColumnIds()); @@ -327,6 +378,7 @@ class PlanSignatureTableIndexMap { break; } case ConversionType::RESTORE_ORIGINAL: + get.source_ordinality = source_ordinality; D_ASSERT(!column_ids.empty()); get.GetMutableColumnIds() = std::move(column_ids); D_ASSERT(get.projection_ids.empty()); @@ -480,6 +532,7 @@ class PlanSignatureTableIndexMap { vector> projection_maps; //! Utility to temporarily store column ids, projection_ids, table indices, expression info and children + OrdinalityType source_ordinality = OrdinalityType::WITHOUT_ORDINALITY; vector column_ids; vector chunk_column_ids; vector projection_ids; @@ -612,12 +665,12 @@ class PlanSignature { return true; case LogicalOperatorType::LOGICAL_GET: { auto &get = op.Cast(); - if (get.bind_data && !get.function.HasSerializationCallbacks() && get.parameters.empty() && - get.named_parameters.empty()) { + if (get.bind_data && !get.function.HasSerializationCallbacks() && get.parameters.empty()) { // Without serialization callbacks, the serialized form carries only the call parameters - // (see LogicalGet::Serialize). A parameter-less scan - e.g., one created through an - // attached catalog - keeps its identity solely in the bind data, so equal serialized - // bytes cannot prove that two scans read the same table. + // (see LogicalGet::Serialize). A scan without positional parameters - e.g., one created through + // an attached catalog - keeps its identity solely in the bind data, and its options do not + // identify what it reads, so equal serialized bytes cannot prove that two scans read the same + // table. return false; } return true; @@ -835,7 +888,6 @@ class CommonSubplanFinder { // We can bail on a subplan for various reasons (some of which could potentially be fixed) bool bail = false; - column_binding_set_t required_bindings; for (auto &subplan : subplan_info.subplans) { column_binding_set_t subplan_bindings; for (auto &cb : subplan.canonical_bindings) { @@ -845,46 +897,16 @@ class CommonSubplanFinder { break; } subplan_bindings.insert(cb); - required_bindings.insert(cb); } if (bail) { break; } } - idx_t primary_subplan_idx = subplan_info.subplans.size(); - idx_t primary_subplan_binding_count = 0; - for (idx_t subplan_idx = 0; subplan_idx < subplan_info.subplans.size() && !bail; subplan_idx++) { - const auto expanded_bindings = - GetExpandedCanonicalBindings(*subplan_info.subplans[subplan_idx].op.get()); - bool contains_required_bindings = true; - for (auto &cb : required_bindings) { - if (std::find(expanded_bindings.begin(), expanded_bindings.end(), cb) == expanded_bindings.end()) { - contains_required_bindings = false; - break; - } - } - if (!contains_required_bindings) { - continue; - } - auto &subplan = subplan_info.subplans[subplan_idx]; - if (primary_subplan_idx == subplan_info.subplans.size() || - subplan.canonical_bindings.size() > primary_subplan_binding_count) { - primary_subplan_idx = subplan_idx; - primary_subplan_binding_count = subplan.canonical_bindings.size(); - } - } - if (primary_subplan_idx == subplan_info.subplans.size()) { - bail = true; // None of the subplans can expose every binding required by the duplicate occurrences - } - - if (bail) { + if (bail || !SelectPrimarySubplan(subplan_info)) { to_remove.push_back(signature); continue; } - - // Move the primary subplan to the front - std::swap(subplan_info.subplans[0], subplan_info.subplans[primary_subplan_idx]); } // Only remove them all at the end so the logic above doesn't get affected @@ -902,6 +924,9 @@ class CommonSubplanFinder { if (!ShouldMaterialize(subplan_info)) { continue; // No longer worth materializing due to other materializations } + if (!SelectPrimarySubplan(subplan_info)) { + continue; // Nested subplans were replaced by CTE refs that no longer expose the required bindings + } const auto cte_index = optimizer.binder.GenerateTableIndex(); const auto cte_name = StringUtil::Format("__common_subplan_%llu", index++); @@ -1131,42 +1156,6 @@ class CommonSubplanFinder { return a.get(); } - vector GetExpandedColumnBindings(LogicalOperator &op) { - switch (op.type) { - case LogicalOperatorType::LOGICAL_FILTER: - case LogicalOperatorType::LOGICAL_ORDER_BY: - return GetExpandedColumnBindings(*op.children[0]); - case LogicalOperatorType::LOGICAL_ANY_JOIN: - case LogicalOperatorType::LOGICAL_ASOF_JOIN: - case LogicalOperatorType::LOGICAL_COMPARISON_JOIN: { - auto &join = op.Cast(); - auto left_bindings = GetExpandedColumnBindings(*op.children[0]); - if (join.join_type == JoinType::SEMI || join.join_type == JoinType::ANTI) { - return left_bindings; - } - if (join.join_type == JoinType::MARK) { - left_bindings.emplace_back(join.mark_index, ProjectionIndex(0)); - return left_bindings; - } - auto right_bindings = GetExpandedColumnBindings(*op.children[1]); - if (join.join_type == JoinType::RIGHT_SEMI || join.join_type == JoinType::RIGHT_ANTI) { - return right_bindings; - } - left_bindings.insert(left_bindings.end(), right_bindings.begin(), right_bindings.end()); - return left_bindings; - } - case LogicalOperatorType::LOGICAL_CROSS_PRODUCT: - case LogicalOperatorType::LOGICAL_POSITIONAL_JOIN: { - auto left_bindings = GetExpandedColumnBindings(*op.children[0]); - auto right_bindings = GetExpandedColumnBindings(*op.children[1]); - left_bindings.insert(left_bindings.end(), right_bindings.begin(), right_bindings.end()); - return left_bindings; - } - default: - return op.GetColumnBindings(); - } - } - arena_vector GetCanonicalBindings(const vector &original_bindings) { const auto &table_index_map = state.table_index_map.GetMap(); arena_vector canonical_bindings(state.allocator); @@ -1192,6 +1181,40 @@ class CommonSubplanFinder { return GetCanonicalBindings(GetExpandedColumnBindings(op)); } + //! Moves a subplan that can expose every binding required by the duplicate occurrences to the front + bool SelectPrimarySubplan(SubplanInfo &subplan_info) { + column_binding_set_t required_bindings; + for (auto &subplan : subplan_info.subplans) { + required_bindings.insert(subplan.canonical_bindings.begin(), subplan.canonical_bindings.end()); + } + + optional_idx primary_subplan_idx; + idx_t primary_subplan_binding_count = 0; + for (idx_t subplan_idx = 0; subplan_idx < subplan_info.subplans.size(); subplan_idx++) { + auto &subplan = subplan_info.subplans[subplan_idx]; + const auto expanded_bindings = GetExpandedCanonicalBindings(*subplan.op.get()); + bool contains_required_bindings = true; + for (auto &cb : required_bindings) { + if (std::find(expanded_bindings.begin(), expanded_bindings.end(), cb) == expanded_bindings.end()) { + contains_required_bindings = false; + break; + } + } + if (!contains_required_bindings) { + continue; + } + if (!primary_subplan_idx.IsValid() || subplan.canonical_bindings.size() > primary_subplan_binding_count) { + primary_subplan_idx = subplan_idx; + primary_subplan_binding_count = subplan.canonical_bindings.size(); + } + } + if (!primary_subplan_idx.IsValid()) { + return false; + } + std::swap(subplan_info.subplans[0], subplan_info.subplans[primary_subplan_idx.GetIndex()]); + return true; + } + static void ClearProjectionMaps(LogicalOperator &op) { switch (op.type) { case LogicalOperatorType::LOGICAL_ANY_JOIN: diff --git a/src/duckdb/src/optimizer/compressed_materialization.cpp b/src/duckdb/src/optimizer/compressed_materialization.cpp index 1dbe0b7aa..6a2a772c4 100644 --- a/src/duckdb/src/optimizer/compressed_materialization.cpp +++ b/src/duckdb/src/optimizer/compressed_materialization.cpp @@ -7,7 +7,7 @@ #include "duckdb/function/scalar/nested_functions.hpp" #include "duckdb/function/scalar/operators.hpp" #include "duckdb/function/scalar/variant_functions.hpp" -#include "duckdb/optimizer/builtin_function_lookup.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/optimizer/column_binding_replacer.hpp" #include "duckdb/optimizer/optimizer.hpp" #include "duckdb/optimizer/topn_optimizer.hpp" @@ -138,6 +138,7 @@ unique_ptr CMHelper::CreateDefaultStatsAwareCast(ClientContext &cont throw InternalException("Expected a cast in CMHelper::CreateDefaultStatsAwareCast"); } // LCOV_EXCL_STOP auto &cast = result->Cast(); + CMUtils::MarkCast(cast); result_stats = BoundCastExpression::PropagateStatistics(cast, input_stats, context); if (!result_stats) { // LCOV_EXCL_START throw InternalException("Could not propagate cast statistics in compressed materialization"); @@ -355,8 +356,30 @@ unique_ptr CMHelper::CreateIntegralFunctionCompress(unique_p // CompressedMaterialization //===--------------------------------------------------------------------===// CompressedMaterialization::CompressedMaterialization(Optimizer &optimizer_p, LogicalOperator &root_p, + const vector>> &ancestors_p, statistics_map_t &statistics_map_p) - : optimizer(optimizer_p), context(optimizer.context), root(&root_p), statistics_map(statistics_map_p) { + : optimizer(optimizer_p), context(optimizer.context), root(&root_p), ancestors(ancestors_p), + statistics_map(statistics_map_p) { +} + +void CompressedMaterialization::RebindAncestors(ColumnBindingReplacer &replacer) { + // Only the materializing operator consumes compressed values. Its ancestors see the + // restored output and must retain their expression types, including explicit collations. + ColumnBindingReplacer parent_replacer; + for (auto &replacement : replacer.replacement_bindings) { + parent_replacer.replacement_bindings.emplace_back(replacement.old_binding, replacement.new_binding); + } + for (auto &ancestor : ancestors) { + auto &ancestor_op = ancestor.get(); + if (replacer.stop_operator && replacer.stop_operator.get() == ancestor_op.get()) { + continue; + } + if (ancestor_op.get() == ancestors.back().get().get()) { + replacer.VisitOperatorBindings(*ancestor_op); + } else { + parent_replacer.VisitOperatorBindings(*ancestor_op); + } + } } void CompressedMaterialization::GetReferencedBindings(const Expression &root_expr, @@ -432,7 +455,7 @@ void CompressedMaterialization::Compress(unique_ptr &op) { return; } - root->ResolveOperatorTypes(); + op->ResolveOperatorTypes(); switch (op->type) { case LogicalOperatorType::LOGICAL_AGGREGATE_AND_GROUP_BY: @@ -530,7 +553,11 @@ void CompressedMaterialization::CreateCompressProjection(unique_ptr CompressedMaterialization::GetCompressExpression(const ColumnBinding &binding, @@ -992,7 +1019,8 @@ unique_ptr CompressedMaterialization::GetGeometryDecompress(unique_p BoundScalarFunction bound_function(decompress_function); bound_function.SetReturnType(result_type); - return make_uniq(std::move(bound_function), std::move(arguments), nullptr); + auto result = make_uniq(std::move(bound_function), std::move(arguments), nullptr); + return std::move(result); } unique_ptr CompressedMaterialization::GetIntegralDecompress(unique_ptr input, @@ -1008,7 +1036,8 @@ unique_ptr CompressedMaterialization::GetIntegralDecompress(unique_p BoundScalarFunction bound_function(decompress_function); bound_function.SetReturnType(result_type); - return make_uniq(std::move(bound_function), std::move(arguments), nullptr); + auto result = make_uniq(std::move(bound_function), std::move(arguments), nullptr); + return std::move(result); } unique_ptr CompressedMaterialization::GetStringDecompress(unique_ptr input, @@ -1022,7 +1051,8 @@ unique_ptr CompressedMaterialization::GetStringDecompress(unique_ptr BoundScalarFunction bound_function(decompress_function); bound_function.SetReturnType(result_type); - return make_uniq(std::move(bound_function), std::move(arguments), nullptr); + auto result = make_uniq(std::move(bound_function), std::move(arguments), nullptr); + return std::move(result); } unique_ptr CompressedMaterialization::GetVariantDecompress(unique_ptr input, diff --git a/src/duckdb/src/optimizer/compressed_materialization/compress_aggregate.cpp b/src/duckdb/src/optimizer/compressed_materialization/compress_aggregate.cpp index 6671dbda8..abecfeef4 100644 --- a/src/duckdb/src/optimizer/compressed_materialization/compress_aggregate.cpp +++ b/src/duckdb/src/optimizer/compressed_materialization/compress_aggregate.cpp @@ -91,6 +91,21 @@ void CompressedMaterialization::CompressAggregate(unique_ptr &o // Create info for compression CompressedMaterializationInfo info(*op, {0}, referenced_bindings); + // Columns that the aggregate does not read cannot be referenced above it, so leave them uncompressed + column_binding_set_t used_bindings; + for (const auto &group : groups) { + GetReferencedBindings(*group, used_bindings); + } + for (const auto &expr : aggregate.expressions) { + GetReferencedBindings(*expr, used_bindings); + } + auto &child_info = info.child_info[0]; + for (idx_t binding_idx = 0; binding_idx < child_info.bindings_before.size(); binding_idx++) { + if (used_bindings.find(child_info.bindings_before[binding_idx]) == used_bindings.end()) { + child_info.can_compress[binding_idx] = false; + } + } + // Create binding mapping const auto bindings_out = aggregate.GetColumnBindings(); const auto &types = aggregate.types; diff --git a/src/duckdb/src/optimizer/cte_filter_pusher.cpp b/src/duckdb/src/optimizer/cte_filter_pusher.cpp index f1ddf640e..73b820273 100644 --- a/src/duckdb/src/optimizer/cte_filter_pusher.cpp +++ b/src/duckdb/src/optimizer/cte_filter_pusher.cpp @@ -1,9 +1,11 @@ #include "duckdb/optimizer/cte_filter_pusher.hpp" +#include "duckdb/optimizer/cte_join_filter_pusher.hpp" -#include "duckdb/optimizer/optimizer.hpp" #include "duckdb/optimizer/column_binding_replacer.hpp" #include "duckdb/optimizer/filter_pushdown.hpp" +#include "duckdb/optimizer/optimizer.hpp" #include "duckdb/planner/expression/bound_conjunction_expression.hpp" +#include "duckdb/planner/expression/expression_barrier.hpp" #include "duckdb/planner/operator/logical_cteref.hpp" #include "duckdb/planner/operator/logical_filter.hpp" #include "duckdb/planner/operator/logical_materialized_cte.hpp" @@ -11,7 +13,8 @@ namespace duckdb { CTEFilterPusher::MaterializedCTEInfo::MaterializedCTEInfo(LogicalOperator &materialized_cte_p) - : materialized_cte(materialized_cte_p), all_cte_refs_are_filtered(true) { + : materialized_cte(materialized_cte_p), all_cte_refs_are_filtered(true), + has_filter_dependency(materialized_cte_p.Cast().filter_dependency != nullptr) { } CTEFilterPusher::CTEFilterPusher(Optimizer &optimizer_p) : optimizer(optimizer_p) { @@ -19,11 +22,19 @@ CTEFilterPusher::CTEFilterPusher(Optimizer &optimizer_p) : optimizer(optimizer_p unique_ptr CTEFilterPusher::Optimize(unique_ptr op) { FindCandidates(*op); + if (cte_info_map.empty()) { + return op; + } + // Scalar producer pushdown must preserve markers referenced by CTE consumers. + FilterPushdown pushdown(optimizer); + unordered_set referenced_bindings; + pushdown.CheckMarkToSemi(*op, referenced_bindings); auto ctes = std::move(cte_info_map); // Iterate once over all materialized CTEs for (auto it = ctes.rbegin(); it != ctes.rend(); it++) { - if (!it->second->all_cte_refs_are_filtered) { + if (it->second->filters.empty() || + (!it->second->all_cte_refs_are_filtered && !it->second->has_filter_dependency)) { continue; } @@ -32,11 +43,43 @@ unique_ptr CTEFilterPusher::Optimize(unique_ptr>(); FindCandidates(*op); - PushFilterIntoCTE(*cte_info_map[it->first]); + auto &info = *cte_info_map[it->first]; + if (CanPushFilter(info)) { + PushFilterIntoCTE(info); + } } + CTEJoinFilterPusher join_pusher(optimizer); + join_pusher.Optimize(*op); return op; } +void CTEFilterPusher::ClearDependencies(LogicalOperator &op) { + if (op.type == LogicalOperatorType::LOGICAL_MATERIALIZED_CTE) { + op.Cast().filter_dependency.reset(); + } + for (auto &child : op.children) { + ClearDependencies(*child); + } +} + +bool CTEFilterPusher::CanPushFilter(const MaterializedCTEInfo &info) { + if (info.all_cte_refs_are_filtered) { + return true; + } + if (info.filters.size() != 1 || !HasValidDependency(info)) { + return false; + } + auto &cte = info.materialized_cte.Cast(); + D_ASSERT(info.filters[0].get().children[0]->type == LogicalOperatorType::LOGICAL_CTE_REF); + auto &filtered_ref = info.filters[0].get().children[0]->Cast(); + return filtered_ref.table_index == cte.filter_dependency->row_scan; +} + +bool CTEFilterPusher::HasValidDependency(const MaterializedCTEInfo &info) { + auto &cte = info.materialized_cte.Cast(); + return cte.filter_dependency && cte.filter_dependency->MatchesConsumers(info.references); +} + void CTEFilterPusher::FindCandidates(LogicalOperator &op) { if (op.type == LogicalOperatorType::LOGICAL_MATERIALIZED_CTE) { // We encountered a new CTE, add it to the map @@ -51,6 +94,7 @@ void CTEFilterPusher::FindCandidates(LogicalOperator &op) { auto it = cte_info_map.find(to_string(cte_ref.cte_index.index)); if (it != cte_info_map.end()) { it->second->filters.push_back(op); + it->second->references.push_back(cte_ref); } return; } else if (op.type == LogicalOperatorType::LOGICAL_CTE_REF) { @@ -59,6 +103,7 @@ void CTEFilterPusher::FindCandidates(LogicalOperator &op) { auto it = cte_info_map.find(to_string(cte_ref.cte_index.index)); if (it != cte_info_map.end()) { it->second->all_cte_refs_are_filtered = false; + it->second->references.push_back(cte_ref); } return; } @@ -88,9 +133,19 @@ void CTEFilterPusher::PushFilterIntoCTE(MaterializedCTEInfo &info) { replacer.replacement_bindings.emplace_back(old_bindings[i], new_bindings[i]); } + bool all_conjuncts_repeatable = true; + for (auto &expr : filter.get().expressions) { + all_conjuncts_repeatable &= !expr->IsVolatile() && !ExpressionBarrier::Contains(*expr); + } + // We copy the filters and replace the CTE reference bindings with the bindings in the CTE definition unique_ptr inner_expr; for (auto &filter_expr : filter.get().expressions) { + // Dropping a conjunct must not expose errors it previously short-circuited. + if (filter_expr->IsVolatile() || ExpressionBarrier::Contains(*filter_expr) || + (!all_conjuncts_repeatable && filter_expr->CanThrow())) { + continue; + } auto filter_expr_copy = filter_expr->Copy(); replacer.VisitExpression(&filter_expr_copy); if (inner_expr) { @@ -101,6 +156,11 @@ void CTEFilterPusher::PushFilterIntoCTE(MaterializedCTEInfo &info) { } } + // An unrestricted consumer makes the disjunction true. + if (!inner_expr) { + return; + } + if (outer_expr) { outer_expr = make_uniq(ExpressionType::CONJUNCTION_OR, std::move(outer_expr), std::move(inner_expr)); diff --git a/src/duckdb/src/optimizer/cte_inlining.cpp b/src/duckdb/src/optimizer/cte_inlining.cpp index b2322ff50..f0f98f973 100644 --- a/src/duckdb/src/optimizer/cte_inlining.cpp +++ b/src/duckdb/src/optimizer/cte_inlining.cpp @@ -251,25 +251,8 @@ bool CTEInlining::Inline(unique_ptr &op, LogicalOperator &mater return false; } } - vector> proj_expressions; - definition->ResolveOperatorTypes(); - vector types = definition->types; - vector bindings = - requires_copy ? copy->GetColumnBindings() : definition->GetColumnBindings(); - - idx_t col_idx = 0; - for (auto &col : bindings) { - proj_expressions.push_back(make_uniq(types[col_idx], col)); - col_idx++; - } - auto proj = make_uniq(cteref.table_index, std::move(proj_expressions)); - - if (requires_copy) { - proj->children.push_back(std::move(copy)); - } else { - proj->children.push_back(std::move(definition)); - } - op = std::move(proj); + op = LogicalProjection::CreateIdentity(cteref.table_index, + requires_copy ? std::move(copy) : std::move(definition)); return true; } return true; diff --git a/src/duckdb/src/optimizer/cte_join_filter_collector.cpp b/src/duckdb/src/optimizer/cte_join_filter_collector.cpp new file mode 100644 index 000000000..75810abe3 --- /dev/null +++ b/src/duckdb/src/optimizer/cte_join_filter_collector.cpp @@ -0,0 +1,228 @@ +#include "duckdb/optimizer/cte_join_filter_collector.hpp" +#include "duckdb/common/numeric_utils.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/expression/bound_function_expression.hpp" +#include "duckdb/planner/expression/expression_barrier.hpp" +#include "duckdb/planner/expression_iterator.hpp" +#include "duckdb/planner/logical_operator_visitor.hpp" +#include "duckdb/planner/operator/logical_aggregate.hpp" +#include "duckdb/planner/operator/logical_comparison_join.hpp" +#include "duckdb/planner/operator/logical_cteref.hpp" +#include "duckdb/planner/operator/logical_distinct.hpp" +#include "duckdb/planner/operator/logical_projection.hpp" + +namespace duckdb { + +CTEJoinFilterCollector::CTEJoinFilterCollector(const unordered_map &targets) + : join_targets(targets) { +} + +vector CTEJoinFilterCollector::Collect(LogicalOperator &op, + const unordered_map &targets) { + CTEJoinFilterCollector collector(targets); + collector.VisitOperator(op); + return std::move(collector.join_filters); +} + +static bool CanPassCTEJoinFilter(LogicalOperator &op) { + bool safe = true; + LogicalOperatorVisitor::EnumerateExpressions(op, [&](unique_ptr *expr) { + safe &= !(*expr)->IsVolatile() && !(*expr)->CanThrow() && !ExpressionBarrier::Contains(**expr); + ExpressionIterator::VisitExpression( + **expr, [&](const BoundFunctionExpression &func) { safe &= !func.Function().RequiresOrderedExecution(); }); + }); + return safe; +} + +static bool ContainsCTEJoinKeys(LogicalOperator &op, const vector &keys) { + auto bindings = op.GetColumnBindings(); + for (auto &key : keys) { + if (std::find(bindings.begin(), bindings.end(), key) == bindings.end()) { + return false; + } + } + return true; +} + +static bool MapCTEJoinProjection(LogicalProjection &projection, vector &keys) { + for (auto &key : keys) { + if (key.table_index != projection.table_index) { + return false; + } + auto &expr = projection.GetExpression(key); + if (expr.GetExpressionClass() != ExpressionClass::BOUND_COLUMN_REF) { + return false; + } + key = expr.Cast().Binding(); + } + return true; +} + +static optional_ptr FindCTEJoinSource(LogicalOperator &op, vector &keys) { + if (!ContainsCTEJoinKeys(op, keys)) { + return nullptr; + } + switch (op.type) { + case LogicalOperatorType::LOGICAL_CTE_REF: { + auto &ref = op.Cast(); + return ref.is_recurring ? nullptr : &ref; + } + case LogicalOperatorType::LOGICAL_PROJECTION: + if (!MapCTEJoinProjection(op.Cast(), keys)) { + return nullptr; + } + break; + case LogicalOperatorType::LOGICAL_FILTER: + break; + default: + return nullptr; + } + return FindCTEJoinSource(*op.children[0], keys); +} + +void CTEJoinFilterCollector::VisitOperator(LogicalOperator &op) { + for (auto &child : op.children) { + VisitOperator(*child); + } + if (op.type != LogicalOperatorType::LOGICAL_COMPARISON_JOIN || + op.Cast().join_type != JoinType::INNER || !CanPassCTEJoinFilter(op)) { + return; + } + auto &join = op.Cast(); + vector left_keys, right_keys; + vector comparisons; + for (auto &condition : join.conditions) { + if (!condition.IsComparison() || + (condition.GetComparisonType() != ExpressionType::COMPARE_EQUAL && + condition.GetComparisonType() != ExpressionType::COMPARE_NOT_DISTINCT_FROM) || + condition.GetLHS().GetExpressionClass() != ExpressionClass::BOUND_COLUMN_REF || + condition.GetRHS().GetExpressionClass() != ExpressionClass::BOUND_COLUMN_REF) { + continue; + } + left_keys.push_back(condition.GetLHS().Cast().Binding()); + right_keys.push_back(condition.GetRHS().Cast().Binding()); + comparisons.push_back(condition.GetComparisonType()); + } + if (comparisons.empty()) { + return; + } + for (idx_t side = 0; side < 2; side++) { + auto source_keys = side == 0 ? left_keys : right_keys; + auto source = FindCTEJoinSource(*op.children[side], source_keys); + if (source) { + PushFilter(*op.children[1 - side], *source, source_keys, side == 0 ? right_keys : left_keys, comparisons); + } + } +} + +void CTEJoinFilterCollector::PushFilter(LogicalOperator &op, const LogicalCTERef &source, + const vector &source_keys, vector target_keys, + const vector &comparisons) { + if (!ContainsCTEJoinKeys(op, target_keys) || !CanPassCTEJoinFilter(op)) { + return; + } + switch (op.type) { + case LogicalOperatorType::LOGICAL_CTE_REF: + AddFilter(source, op.Cast(), source_keys, target_keys, comparisons); + return; + case LogicalOperatorType::LOGICAL_MATERIALIZED_CTE: + PushFilter(*op.children[1], source, source_keys, std::move(target_keys), comparisons); + return; + case LogicalOperatorType::LOGICAL_PROJECTION: + if (!MapCTEJoinProjection(op.Cast(), target_keys)) { + return; + } + break; + case LogicalOperatorType::LOGICAL_FILTER: + case LogicalOperatorType::LOGICAL_ORDER_BY: + break; + case LogicalOperatorType::LOGICAL_DISTINCT: + if (op.Cast().distinct_type != DistinctType::DISTINCT) { + return; + } + break; + case LogicalOperatorType::LOGICAL_AGGREGATE_AND_GROUP_BY: { + auto &aggregate = op.Cast(); + for (auto &key : target_keys) { + if (key.table_index != aggregate.group_index) { + return; + } + for (auto &grouping_set : aggregate.grouping_sets) { + if (!grouping_set.count(key.column_index)) { + return; + } + } + auto &group = aggregate.GetGroupExpression(key.column_index); + if (group.GetExpressionClass() != ExpressionClass::BOUND_COLUMN_REF) { + return; + } + key = group.Cast().Binding(); + } + break; + } + case LogicalOperatorType::LOGICAL_UNION: { + auto bindings = op.GetColumnBindings(); + for (auto &child : op.children) { + auto child_bindings = child->GetColumnBindings(); + auto child_keys = target_keys; + for (auto &key : child_keys) { + auto position = NumericCast(std::find(bindings.begin(), bindings.end(), key) - bindings.begin()); + key = child_bindings[position]; + } + PushFilter(*child, source, source_keys, std::move(child_keys), comparisons); + } + return; + } + case LogicalOperatorType::LOGICAL_COMPARISON_JOIN: { + auto &join = op.Cast(); + if (join.join_type != JoinType::INNER && join.join_type != JoinType::LEFT && join.join_type != JoinType::SEMI && + join.join_type != JoinType::ANTI && join.join_type != JoinType::MARK) { + return; + } + if (ContainsCTEJoinKeys(*op.children[0], target_keys)) { + PushFilter(*op.children[0], source, source_keys, target_keys, comparisons); + } + if (join.join_type == JoinType::INNER && ContainsCTEJoinKeys(*op.children[1], target_keys)) { + PushFilter(*op.children[1], source, source_keys, std::move(target_keys), comparisons); + } + return; + } + default: + return; + } + PushFilter(*op.children[0], source, source_keys, std::move(target_keys), comparisons); +} + +void CTEJoinFilterCollector::AddFilter(const LogicalCTERef &source, const LogicalCTERef &target, + const vector &source_keys, + const vector &target_keys, + const vector &comparisons) { + D_ASSERT(!comparisons.empty() && source_keys.size() == comparisons.size() && + target_keys.size() == comparisons.size()); + auto target_entry = join_targets.find(target.cte_index); + if (source.is_recurring || target.is_recurring || source.cte_index == target.cte_index || + target_entry == join_targets.end() || target_entry->second != target.table_index) { + return; + } + CTEJoinFilter filter; + filter.source = source.cte_index; + filter.target = target.cte_index; + filter.target_scan = target.table_index; + filter.comparisons = comparisons; + for (idx_t i = 0; i < comparisons.size(); i++) { + D_ASSERT(source_keys[i].table_index == source.table_index); + D_ASSERT(target_keys[i].table_index == target.table_index); + filter.source_columns.push_back(source_keys[i].column_index); + filter.target_columns.push_back(target_keys[i].column_index); + } + for (auto &existing : join_filters) { + if (existing.source == filter.source && existing.target == filter.target && + existing.target_scan == filter.target_scan && existing.source_columns == filter.source_columns && + existing.target_columns == filter.target_columns && existing.comparisons == filter.comparisons) { + return; + } + } + join_filters.push_back(std::move(filter)); +} + +} // namespace duckdb diff --git a/src/duckdb/src/optimizer/cte_join_filter_pusher.cpp b/src/duckdb/src/optimizer/cte_join_filter_pusher.cpp new file mode 100644 index 000000000..a6677bd2b --- /dev/null +++ b/src/duckdb/src/optimizer/cte_join_filter_pusher.cpp @@ -0,0 +1,178 @@ +#include "duckdb/optimizer/cte_join_filter_pusher.hpp" +#include "duckdb/optimizer/cte_join_filter_collector.hpp" +#include "duckdb/optimizer/optimizer.hpp" +#include "duckdb/optimizer/relation_statistics/relation_statistics_extractor.hpp" +#include "duckdb/planner/binder.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/operator/logical_comparison_join.hpp" +#include "duckdb/planner/operator/logical_cteref.hpp" +#include "duckdb/planner/operator/logical_materialized_cte.hpp" + +#include + +namespace duckdb { + +CTEJoinFilterPusher::CTEJoinFilterPusher(Optimizer &optimizer) : optimizer(optimizer) { +} + +bool CTEJoinFilterPusher::HasValidDependency(const MaterializedCTEInfo &info) { + auto &cte = info.materialized_cte.Cast(); + return cte.filter_dependency && cte.filter_dependency->MatchesConsumers(info.references); +} + +void CTEJoinFilterPusher::FindCandidates(LogicalOperator &op) { + if (op.type == LogicalOperatorType::LOGICAL_MATERIALIZED_CTE) { + auto &cte = op.Cast(); + auto info = make_uniq(op); + info->ancestors = available_ctes; + cte_info_map.insert(to_string(cte.table_index.index), std::move(info)); + FindCandidates(*op.children[0]); + available_ctes.push_back(cte.table_index); + FindCandidates(*op.children[1]); + available_ctes.pop_back(); + return; + } + if (op.type == LogicalOperatorType::LOGICAL_CTE_REF) { + auto &ref = op.Cast(); + auto entry = cte_info_map.find(to_string(ref.cte_index.index)); + if (entry != cte_info_map.end()) { + entry->second->references.push_back(ref); + } + return; + } + for (auto &child : op.children) { + FindCandidates(*child); + } +} + +void CTEJoinFilterPusher::Optimize(LogicalOperator &op) { + // Bound the extra scan/hash-build cost and require a substantial reduction in retained keys. + static constexpr double MAX_SOURCE_RATIO = 0.25; + static constexpr double MAX_KEY_RATIO = 0.5; + unordered_map join_targets; + cte_info_map.clear(); + FindCandidates(op); + for (auto &entry : cte_info_map) { + if (HasValidDependency(*entry.second)) { + auto &cte = entry.second->materialized_cte.Cast(); + join_targets.emplace(cte.table_index, cte.filter_dependency->row_scan); + } + } + if (join_targets.empty()) { + return; + } + auto join_filters = CTEJoinFilterCollector::Collect(op, join_targets); + if (join_filters.empty()) { + return; + } + + cte_info_map.clear(); + FindCandidates(op); + vector targets; + for (auto &entry : cte_info_map) { + targets.push_back(entry.second->materialized_cte.Cast().table_index); + } + unordered_map restricted_stats; + for (auto target : targets) { + // Producer rewrites invalidate operator references and cached statistics. + cte_info_map.clear(); + FindCandidates(op); + auto target_entry = cte_info_map.find(to_string(target.index)); + if (target_entry == cte_info_map.end() || !HasValidDependency(*target_entry->second)) { + continue; + } + auto &info = *target_entry->second; + auto &cte = info.materialized_cte.Cast(); + RelationStatsExtractor extractor(optimizer.context, [&](TableIndex index) -> optional_ptr { + auto entry = cte_info_map.find(to_string(index.index)); + return entry == cte_info_map.end() ? nullptr : entry->second->materialized_cte.children[0].get(); + }); + auto target_stats = extractor.Extract(*cte.children[0]); + if (!target_stats || target_stats->cardinality == 0) { + continue; + } + optional_ptr best; + double best_fraction = 1; + idx_t best_source_count = 0; + for (auto &filter : join_filters) { + if (filter.target != target || filter.target_scan != cte.filter_dependency->row_scan || + std::find(info.ancestors.begin(), info.ancestors.end(), filter.source) == info.ancestors.end()) { + continue; + } + auto source_entry = cte_info_map.find(to_string(filter.source.index)); + if (source_entry == cte_info_map.end()) { + continue; + } + auto &source_cte = source_entry->second->materialized_cte; + auto cached = restricted_stats.find(filter.source); + optional_ptr source_stats = + cached == restricted_stats.end() ? extractor.Extract(*source_cte.children[0]) : &cached->second; + // An extra scan and hash build should be small relative to the input being restricted. + if (!source_stats || static_cast(source_stats->cardinality) > + static_cast(target_stats->cardinality) * MAX_SOURCE_RATIO) { + continue; + } + double fraction = 1; + bool valid_columns = true; + for (idx_t i = 0; i < filter.comparisons.size(); i++) { + auto source_col = filter.source_columns[i].GetIndex(); + auto target_col = filter.target_columns[i].GetIndex(); + if (source_col >= source_stats->columns.size() || target_col >= target_stats->columns.size()) { + valid_columns = false; + break; + } + auto &source_distinct = source_stats->columns[source_col].distinct_count; + auto &target_distinct = target_stats->columns[target_col].distinct_count; + if (target_distinct.source == DistinctCountSource::CARDINALITY || target_distinct.distinct_count == 0) { + continue; + } + auto source_count = MinValue(source_distinct.distinct_count, source_stats->cardinality); + auto target_count = MinValue(target_distinct.distinct_count, target_stats->cardinality); + fraction = MinValue(fraction, static_cast(source_count) / static_cast(target_count)); + } + if (valid_columns && fraction <= MAX_KEY_RATIO && + (!best || fraction < best_fraction || + (fraction == best_fraction && source_stats->cardinality < best_source_count))) { + best = filter; + best_fraction = fraction; + best_source_count = source_stats->cardinality; + } + } + if (!best) { + continue; + } + auto &source_cte = cte_info_map[to_string(best->source.index)]->materialized_cte; + source_cte.children[0]->ResolveOperatorTypes(); + auto &source_types = source_cte.children[0]->types; + vector names; + for (idx_t i = 0; i < source_types.size(); i++) { + names.emplace_back("key_" + to_string(i)); + } + auto source_ref = make_uniq(optimizer.binder.GenerateTableIndex(), best->source, source_types, + std::move(names)); + auto source_bindings = source_ref->GetColumnBindings(); + auto target_bindings = cte.children[0]->GetColumnBindings(); + cte.children[0]->ResolveOperatorTypes(); + auto join = make_uniq(JoinType::SEMI); + for (idx_t i = 0; i < best->comparisons.size(); i++) { + auto source_col = best->source_columns[i].GetIndex(); + auto target_col = best->target_columns[i].GetIndex(); + join->conditions.emplace_back( + make_uniq(cte.children[0]->types[target_col], target_bindings[target_col]), + make_uniq(source_types[source_col], source_bindings[source_col]), + best->comparisons[i]); + } + auto stats = *target_stats; + stats.cardinality = LossyNumericCast(std::ceil(static_cast(stats.cardinality) * best_fraction)); + for (auto &column : stats.columns) { + column.distinct_count.distinct_count = MinValue(column.distinct_count.distinct_count, stats.cardinality); + } + restricted_stats.emplace(target, std::move(stats)); + join->children.push_back(std::move(cte.children[0])); + join->children.push_back(std::move(source_ref)); + cte.children[0] = std::move(join); + } + return; +} + +} // namespace duckdb diff --git a/src/duckdb/src/optimizer/expression_heuristics.cpp b/src/duckdb/src/optimizer/expression_heuristics.cpp index f9b1cdb76..9cfad2969 100644 --- a/src/duckdb/src/optimizer/expression_heuristics.cpp +++ b/src/duckdb/src/optimizer/expression_heuristics.cpp @@ -243,8 +243,8 @@ idx_t ExpressionHeuristics::Cost(const TableFilter &filter) { return 0; } if (func.Function().GetName() == SelectivityOptionalFilterScalarFun::NAME && func.BindInfo()) { - auto &data = func.BindInfo()->Cast(); - return data.child_filter_expr ? Cost(*data.child_filter_expr) : 0; + auto optional_child = ExpressionFilter::GetOptionalFilterChild(func); + return optional_child ? Cost(*optional_child) : 0; } } if (ExpressionFilter::IsOptionalExpression(expr)) { diff --git a/src/duckdb/src/optimizer/expression_rewriter.cpp b/src/duckdb/src/optimizer/expression_rewriter.cpp index b27c6ed90..2b20327ff 100644 --- a/src/duckdb/src/optimizer/expression_rewriter.cpp +++ b/src/duckdb/src/optimizer/expression_rewriter.cpp @@ -1,7 +1,7 @@ #include "duckdb/optimizer/expression_rewriter.hpp" #include "duckdb/common/exception.hpp" -#include "duckdb/optimizer/builtin_function_lookup.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/planner/expression_iterator.hpp" #include "duckdb/planner/operator/logical_filter.hpp" #include "duckdb/function/scalar/generic_functions.hpp" @@ -25,8 +25,13 @@ static unique_ptr ApplyRule(LogicalOperator &op, const vectorGetAlias(); +#ifdef D_ASSERT_IS_ENABLED + auto return_type = expr->GetReturnType(); +#endif auto result = rule.get().Apply(op, bindings, rule_made_change, is_root); if (result) { + D_ASSERT(result->GetReturnType() != return_type || + result->GetReturnType().EqualsIncludingCollation(return_type)); changes_made = true; // the base node changed: the rule applied changes if (!alias.empty()) { diff --git a/src/duckdb/src/optimizer/filter_combiner.cpp b/src/duckdb/src/optimizer/filter_combiner.cpp index e40bcd9b9..e79d1ff66 100644 --- a/src/duckdb/src/optimizer/filter_combiner.cpp +++ b/src/duckdb/src/optimizer/filter_combiner.cpp @@ -25,6 +25,7 @@ #include "duckdb/common/types/interval.hpp" #include "duckdb/optimizer/column_lifetime_analyzer.hpp" #include "duckdb/planner/expression_iterator.hpp" +#include "duckdb/planner/operator/logical_filter.hpp" #include "duckdb/planner/operator/logical_get.hpp" #include "utf8proc_wrapper.hpp" #include "duckdb/optimizer/in_clause_rewriter.hpp" @@ -114,6 +115,18 @@ FilterResult FilterCombiner::AddFilter(unique_ptr expr) { return result; } +FilterResult FilterCombiner::AddConjuncts(unique_ptr expr) { + vector> conjuncts; + conjuncts.push_back(std::move(expr)); + LogicalFilter::SplitPredicates(conjuncts); + for (auto &conjunct : conjuncts) { + if (AddFilter(std::move(conjunct)) == FilterResult::UNSATISFIABLE) { + return FilterResult::UNSATISFIABLE; + } + } + return FilterResult::SUCCESS; +} + void FilterCombiner::GenerateEquivalentFilters(const Expression &filter, const std::function filter)> &callback) { if (filter.IsVolatile()) { @@ -138,14 +151,7 @@ void FilterCombiner::GenerateEquivalentFilters(const Expression &filter, auto &col = col_ref.get(); auto set_id = equivalence_set_map.find(col)->second; for (auto &item : equivalence_map[set_id]) { - auto copy = filter.Copy(); - ExpressionIterator::VisitExpressionMutable( - copy, [&](BoundColumnRefExpression &cref, unique_ptr &child) { - if (cref.Equals(col)) { - child = item.get().Copy(); - } - }); - callback(std::move(copy)); + callback(ExpressionIterator::ReplaceExpression(filter, col, item.get())); } } } @@ -498,6 +504,10 @@ FilterPushdownResult FilterCombiner::TryPushdownPrefixFilter(TableFilterSet &tab } auto &column_ref = func.GetChildren()[0]->Cast(); auto &constant_value_expr = func.GetChildren()[1]->Cast(); + if (constant_value_expr.GetValue().IsNull()) { + // prefix(col, NULL) is NULL and filters out every row - leave the filter in place, do not push it down + return FilterPushdownResult::NO_PUSHDOWN; + } auto prefix_string = StringValue::Get(constant_value_expr.GetValue()); if (prefix_string.empty()) { // empty prefix - skip @@ -565,13 +575,8 @@ FilterPushdownResult FilterCombiner::TryPushdownLikeFilter(TableFilterSet &table auto &constant_value_expr = func.GetChildren()[1]->Cast(); auto proj_index = column_ref.Binding().column_index; - // constant value expr can sometimes be null. if so, push is not null filter, which will - // make the filter unsatisfiable and return no results. if (constant_value_expr.GetValue().IsNull()) { - auto is_not_null = ExpressionFilter::CreateNullCheckExpression( - CreateFilterTargetExpression(*func.GetChildren()[0]), ExpressionType::OPERATOR_IS_NOT_NULL); - table_filters.PushFilter(proj_index, make_uniq(std::move(is_not_null))); - return FilterPushdownResult::PUSHED_DOWN_FULLY; + return FilterPushdownResult::NO_PUSHDOWN; } auto &like_string = StringValue::Get(constant_value_expr.GetValue()); if (like_string[0] == '%' || like_string[0] == '_') { @@ -622,6 +627,20 @@ FilterPushdownResult FilterCombiner::TryPushdownLikeFilter(TableFilterSet &table return FilterPushdownResult::PUSHED_DOWN_PARTIALLY; } +static bool TryPushdownDenseInFilter(TableFilterSet &table_filters, const BoundColumnRefExpression &column, + vector &values) { + if (values.empty() || !FilterCombiner::IsDenseRange(values)) { + return false; + } + // IsDenseRange sorts the values and only accepts consecutive integers. + auto lower_bound = CreateComparisonExpression(column, ExpressionType::COMPARE_GREATERTHANOREQUALTO, values.front()); + auto upper_bound = + CreateComparisonExpression(column, ExpressionType::COMPARE_LESSTHANOREQUALTO, std::move(values.back())); + table_filters.PushFilter(column.Binding().column_index, make_uniq(std::move(lower_bound))); + table_filters.PushFilter(column.Binding().column_index, make_uniq(std::move(upper_bound))); + return true; +} + FilterPushdownResult FilterCombiner::TryPushdownInFilter(TableFilterSet &table_filters, const vector &column_ids, Expression &expr) { if (expr.GetExpressionType() != ExpressionType::COMPARE_IN) { @@ -673,15 +692,7 @@ FilterPushdownResult FilterCombiner::TryPushdownInFilter(TableFilterSet &table_f D_ASSERT(!const_value_expr.GetValue().IsNull()); in_list.push_back(const_value_expr.GetValue()); } - if (type.IsIntegral() && IsDenseRange(in_list)) { - // dense range! turn this into x >= min AND x <= max - // IsDenseRange sorts in_list, so the front element is the min and the back element is the max - auto lower_bound = CreateComparisonExpression( - *func.GetChildren()[0], ExpressionType::COMPARE_GREATERTHANOREQUALTO, std::move(in_list.front())); - auto upper_bound = CreateComparisonExpression(*func.GetChildren()[0], ExpressionType::COMPARE_LESSTHANOREQUALTO, - std::move(in_list.back())); - table_filters.PushFilter(proj_index, make_uniq(std::move(lower_bound))); - table_filters.PushFilter(proj_index, make_uniq(std::move(upper_bound))); + if (TryPushdownDenseInFilter(table_filters, column_ref, in_list)) { return FilterPushdownResult::PUSHED_DOWN_FULLY; } // if this is not a dense range we can push an optional filter for zone-map pruning @@ -706,6 +717,9 @@ FilterPushdownResult FilterCombiner::TryPushdownOrClause(TableFilterSet &table_f } ProjectionIndex proj_id; LogicalType col_type = LogicalType::INVALID; + optional_ptr equality_column; + vector equality_values; + bool only_equalities = true; for (idx_t i = 0; i < conj.GetChildren().size(); i++) { auto &child = conj.GetChildren()[i]; if (!BoundComparisonExpression::IsComparison(*child)) { @@ -738,6 +752,11 @@ FilterPushdownResult FilterCombiner::TryPushdownOrClause(TableFilterSet &table_f } auto comparison_type = invert ? FlipComparisonExpression(comp.GetExpressionType()) : comp.GetExpressionType(); + only_equalities &= comparison_type == ExpressionType::COMPARE_EQUAL; + if (comparison_type == ExpressionType::COMPARE_EQUAL && !const_val->GetValue().IsNull()) { + equality_column = column_ref; + equality_values.push_back(const_val->GetValue()); + } if (const_val->GetValue().IsNull()) { switch (comparison_type) { case ExpressionType::COMPARE_DISTINCT_FROM: { @@ -762,6 +781,10 @@ FilterPushdownResult FilterCombiner::TryPushdownOrClause(TableFilterSet &table_f CreateComparisonExpression(*column_ref, comparison_type, const_val->GetValue())); } } + if (only_equalities && equality_column && + TryPushdownDenseInFilter(table_filters, *equality_column, equality_values)) { + return FilterPushdownResult::PUSHED_DOWN_FULLY; + } table_filters.PushFilter(proj_id, CreateOptionalExpressionFilter(std::move(conj_filter), col_type)); return FilterPushdownResult::PUSHED_DOWN_PARTIALLY; } @@ -779,7 +802,19 @@ static bool IsLessThan(ExpressionType type) { //! Margins: 2h for DST ambiguity (TIMESTAMP<->TIMESTAMPTZ), 24h for time-of-day loss, 1s for rounding loss. static int64_t GetTemporalCastMargin(LogicalTypeId source, LogicalTypeId target) { if (source == target) { - return 0; + switch (source) { + case LogicalTypeId::DATE: + case LogicalTypeId::TIMESTAMP: + case LogicalTypeId::TIMESTAMP_TZ: + case LogicalTypeId::TIMESTAMP_NS: + case LogicalTypeId::TIMESTAMP_TZ_NS: + case LogicalTypeId::TIMESTAMP_MS: + case LogicalTypeId::TIMESTAMP_SEC: + return 0; + default: + // non-temporal types with the same id can differ (e.g. UNION or DECIMAL types) + return -1; + } } switch (source) { case LogicalTypeId::TIMESTAMP: @@ -1115,7 +1150,6 @@ FilterResult FilterCombiner::AddBoundComparisonFilter(Expression &expr) { return ret; } else { // comparison between two non-scalars - // only handle comparisons for now if (expr.GetExpressionType() != ExpressionType::COMPARE_EQUAL) { return FilterResult::UNSUPPORTED; } @@ -1270,47 +1304,18 @@ FilterResult FilterCombiner::AddFilter(Expression &expr) { return FilterResult::UNSUPPORTED; } -/* - * Create and add new transitive filters from a two non-scalar filter such as j > i, j >= i, j < i, and j <= i - * It's missing to create another method to add transitive filters from scalar filters, e.g, i > 10 - */ +// Infer constant bounds from comparisons between non-scalar expressions. FilterResult FilterCombiner::AddTransitiveFilters(BoundFunctionExpression &comparison, bool is_root) { if (!IsGreaterThan(comparison.GetExpressionType()) && !IsLessThan(comparison.GetExpressionType())) { return FilterResult::UNSUPPORTED; } + // Derived bounds must not duplicate volatile or throwing expressions. + if (comparison.IsVolatile() || comparison.CanThrow()) { + return FilterResult::UNSUPPORTED; + } // get the LHS and RHS nodes auto &left_node = GetNode(*BoundComparisonExpression::LeftMutable(comparison)); - reference right_node = GetNode(*BoundComparisonExpression::RightMutable(comparison)); - // In case with filters like CAST(i) = j and i = 5 we replace the COLUMN_REF i with the constant 5 - do { - if (right_node.get().GetExpressionType() != ExpressionType::OPERATOR_CAST) { - break; - } - auto &bound_cast_expr = right_node.get().Cast(); - if (BoundCastExpression::Child(bound_cast_expr).GetExpressionType() != ExpressionType::BOUND_COLUMN_REF) { - break; - } - auto &col_ref = BoundCastExpression::Child(bound_cast_expr).Cast(); - for (auto &stored_exp : stored_expressions) { - const_reference expr = stored_exp.first; - if (expr.get().GetExpressionType() == ExpressionType::OPERATOR_CAST) { - expr = BoundCastExpression::Child(right_node.get().Cast()); - } - if (expr.get().GetExpressionType() != ExpressionType::BOUND_COLUMN_REF) { - continue; - } - auto &st_col_ref = expr.get().Cast(); - if (st_col_ref.Binding() != col_ref.Binding()) { - continue; - } - if (bound_cast_expr.GetReturnType() != stored_exp.second->GetReturnType()) { - continue; - } - BoundCastExpression::ChildMutable(bound_cast_expr) = stored_exp.second->Copy(); - right_node = GetNode(*BoundCastExpression::ChildMutable(bound_cast_expr)); - break; - } - } while (false); + auto &right_node = GetNode(*BoundComparisonExpression::RightMutable(comparison)); if (left_node.Equals(right_node)) { return FilterResult::UNSUPPORTED; diff --git a/src/duckdb/src/optimizer/filter_pushdown.cpp b/src/duckdb/src/optimizer/filter_pushdown.cpp index 470cc9298..d262b32a9 100644 --- a/src/duckdb/src/optimizer/filter_pushdown.cpp +++ b/src/duckdb/src/optimizer/filter_pushdown.cpp @@ -172,62 +172,34 @@ unique_ptr FilterPushdown::PushdownJoin(unique_ptrtype == LogicalOperatorType::LOGICAL_DELIM_JOIN); auto &join = op->Cast(); - const auto restore_projection_maps = join.HasProjectionMap(); - auto left_projection_map = join.left_projection_map; - auto right_projection_map = join.right_projection_map; - unordered_set left_bindings, right_bindings; LogicalJoin::GetTableReferences(*op->children[0], left_bindings); LogicalJoin::GetTableReferences(*op->children[1], right_bindings); - unique_ptr result; + // every branch keeps the join's projection maps on the operator it returns: the join itself when it survives, + // the re-created join in PushdownInnerJoin, or a filter carrying the map when a side replaces the join switch (join.join_type) { case JoinType::OUTER: - result = PushdownOuterJoin(std::move(op), left_bindings, right_bindings); - break; + return PushdownOuterJoin(std::move(op), left_bindings, right_bindings); case JoinType::INNER: // AsOf joins can't push anything into the RHS, so treat it as a left join if (op->type == LogicalOperatorType::LOGICAL_ASOF_JOIN) { - result = PushdownLeftJoin(std::move(op), left_bindings, right_bindings); - break; + return PushdownLeftJoin(std::move(op), left_bindings, right_bindings); } - result = PushdownInnerJoin(std::move(op), left_bindings, right_bindings); - break; + return PushdownInnerJoin(std::move(op), left_bindings, right_bindings); case JoinType::LEFT: - result = PushdownLeftJoin(std::move(op), left_bindings, right_bindings); - break; + return PushdownLeftJoin(std::move(op), left_bindings, right_bindings); case JoinType::MARK: - result = PushdownMarkJoin(std::move(op), left_bindings, right_bindings); - break; + return PushdownMarkJoin(std::move(op), left_bindings, right_bindings); case JoinType::SINGLE: - result = PushdownSingleJoin(std::move(op), left_bindings, right_bindings); - break; + return PushdownSingleJoin(std::move(op), left_bindings, right_bindings); case JoinType::SEMI: case JoinType::ANTI: - result = PushdownSemiAntiJoin(std::move(op)); - break; + return PushdownSemiAntiJoin(std::move(op)); default: // unsupported join type: stop pushing down return FinishPushdown(std::move(op)); } - - if (restore_projection_maps) { - switch (result->type) { - case LogicalOperatorType::LOGICAL_COMPARISON_JOIN: - case LogicalOperatorType::LOGICAL_ASOF_JOIN: - case LogicalOperatorType::LOGICAL_ANY_JOIN: - case LogicalOperatorType::LOGICAL_DELIM_JOIN: { - // Pushing down filter through join didn't change operator type (e.g., LogicalEmptyResult), restore maps - auto &result_join = result->Cast(); - result_join.left_projection_map = std::move(left_projection_map); - result_join.right_projection_map = std::move(right_projection_map); - break; - } - default: - break; - } - } - return result; } FilterResult FilterPushdown::PushFilters() { for (auto &f : filters) { @@ -246,17 +218,7 @@ FilterResult FilterPushdown::AddFilter(unique_ptr expr) { if (PushFilters() == FilterResult::UNSATISFIABLE) { return FilterResult::UNSATISFIABLE; } - // split up the filters by AND predicate - vector> expressions; - expressions.push_back(std::move(expr)); - LogicalFilter::SplitPredicates(expressions); - // push the filters into the combiner - for (auto &child_expr : expressions) { - if (combiner.AddFilter(std::move(child_expr)) == FilterResult::UNSATISFIABLE) { - return FilterResult::UNSATISFIABLE; - } - } - return FilterResult::SUCCESS; + return combiner.AddConjuncts(std::move(expr)); } void FilterPushdown::GenerateFilters() { diff --git a/src/duckdb/src/optimizer/grouping_sets_optimizer.cpp b/src/duckdb/src/optimizer/grouping_sets_optimizer.cpp index ef30ec7fa..51f2247d7 100644 --- a/src/duckdb/src/optimizer/grouping_sets_optimizer.cpp +++ b/src/duckdb/src/optimizer/grouping_sets_optimizer.cpp @@ -6,7 +6,7 @@ #include "duckdb/function/function_binder.hpp" #include "duckdb/function/scalar/generic_common.hpp" #include "duckdb/optimizer/aggregate_rewrite_helper.hpp" -#include "duckdb/optimizer/builtin_function_lookup.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/optimizer/optimizer.hpp" #include "duckdb/planner/binder.hpp" #include "duckdb/planner/expression/bound_aggregate_expression.hpp" @@ -50,6 +50,9 @@ struct GroupingSetLevel { } // namespace +//! The maximum number of grouping sets that are rewritten into separate aggregates +static constexpr idx_t MAX_REWRITE_GROUPING_SETS = 1024; + static bool CanRewriteAggregate(const BoundAggregateExpression &aggregate) { if (aggregate.IsDistinct() || aggregate.GetOrderBys()) { // DISTINCT / ORDER BY aggregates cannot be computed by combining states of a finer aggregation @@ -192,9 +195,9 @@ bool GroupingSetsOptimizer::TryExpandGroupingSets(unique_ptr &o } auto cte_name = Identifier(StringUtil::Format("__grouping_sets_input_cte_%llu", cte_index.index)); - result = make_uniq(std::move(cte_name), cte_index, input_types.size(), - std::move(op->children[0]), std::move(result), - CTEMaterialize::CTE_MATERIALIZE_DEFAULT); + auto input = LogicalProjection::CreateIdentity(optimizer.binder.GenerateTableIndex(), std::move(op->children[0])); + result = make_uniq(std::move(cte_name), cte_index, input_types.size(), std::move(input), + std::move(result), CTEMaterialize::CTE_MATERIALIZE_DEFAULT); if (aggr.has_estimated_cardinality) { result->SetEstimatedCardinality(aggr.estimated_cardinality); } @@ -249,6 +252,10 @@ bool GroupingSetsOptimizer::TryRewriteGroupingSets(unique_ptr & can_cascade = false; } } + if (aggr.grouping_sets.size() > MAX_REWRITE_GROUPING_SETS) { + // the rewritten plan nests a materialized CTE per grouping set - leave large grouping sets to the aggregate + return false; + } vector levels; if (!can_cascade || !FindCascade(aggr.grouping_sets, levels)) { // Explicit branches support arbitrary aggregates and expose single grouping sets to subsequent rewrites. diff --git a/src/duckdb/src/optimizer/join_filter_pushdown_optimizer.cpp b/src/duckdb/src/optimizer/join_filter_pushdown_optimizer.cpp index 1a9c4578d..b9c150533 100644 --- a/src/duckdb/src/optimizer/join_filter_pushdown_optimizer.cpp +++ b/src/duckdb/src/optimizer/join_filter_pushdown_optimizer.cpp @@ -4,7 +4,7 @@ #include "duckdb/execution/operator/join/physical_comparison_join.hpp" #include "duckdb/function/aggregate/distributive_functions.hpp" #include "duckdb/function/function_binder.hpp" -#include "duckdb/optimizer/builtin_function_lookup.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/optimizer/optimizer.hpp" #include "duckdb/planner/expression/bound_aggregate_expression.hpp" #include "duckdb/planner/expression/bound_columnref_expression.hpp" @@ -368,6 +368,9 @@ void JoinFilterPushdownOptimizer::GenerateJoinFilters(LogicalComparisonJoin &joi JoinFilterPushdownFilter get_filter; get_filter.dynamic_filters = get.dynamic_filters; get_filter.columns = std::move(target.columns); + if (get.has_estimated_cardinality) { + get_filter.estimated_scan_cardinality = get.estimated_cardinality; + } pushdown_info->probe_info.push_back(std::move(get_filter)); } diff --git a/src/duckdb/src/optimizer/join_order/cardinality_estimator.cpp b/src/duckdb/src/optimizer/join_order/cardinality_estimator.cpp index 4d48fd3d2..9c28fd7b5 100644 --- a/src/duckdb/src/optimizer/join_order/cardinality_estimator.cpp +++ b/src/duckdb/src/optimizer/join_order/cardinality_estimator.cpp @@ -487,6 +487,15 @@ LeftJoinDenomInfo CardinalityEstimator::CalculateLeftJoinDenomInfo(Subgraph2Deno auto &inner_numerator = set_manager.Union(*left.numerator_relations, *right.numerator_relations); auto inner_denom = CalculateInnerJoinDenom(GetEffectiveDenom(left.denom) * GetEffectiveDenom(right.denom), filter); + auto composite_pair = predicate.GetCompositeJoinPair(); + if (composite_pair) { + auto cap = GetJoinPairCap(*composite_pair); + CompositeJoinPairStats stats; + stats.RegisterDistinctCount(filter.GetDistinctCount()); + if (stats.CanApplyCap(cap) && filter.GetDistinctCount() > 0 && cap > filter.GetDistinctCount()) { + inner_denom = inner_denom / filter.GetDistinctCount() * cap; + } + } if (inner_denom <= 0) { return LeftJoinDenomInfo(preserved_numerator, preserved_denom); } diff --git a/src/duckdb/src/optimizer/join_order/join_order_operator.cpp b/src/duckdb/src/optimizer/join_order/join_order_operator.cpp index bdcdb6b91..2816fa664 100644 --- a/src/duckdb/src/optimizer/join_order/join_order_operator.cpp +++ b/src/duckdb/src/optimizer/join_order/join_order_operator.cpp @@ -1,13 +1,12 @@ #include "duckdb/optimizer/join_order/join_order_operator.hpp" #include "duckdb/common/assert.hpp" -#include "duckdb/function/function.hpp" #include "duckdb/planner/expression/bound_columnref_expression.hpp" #include "duckdb/planner/expression/bound_comparison_expression.hpp" #include "duckdb/planner/expression/bound_conjunction_expression.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" #include "duckdb/planner/expression/bound_operator_expression.hpp" -#include "duckdb/planner/expression_iterator.hpp" +#include "duckdb/planner/expression_nullability.hpp" namespace duckdb { @@ -86,29 +85,8 @@ static bool ColumnBecomesNull(const BoundColumnRefExpression &column, const Join return entry != relation_mapping.end() && ContainsRelation(null_relations, entry->second); } -static bool ExpressionBecomesNull(const Expression &expression, const JoinRelationSet &null_relations, - const unordered_map &relation_mapping) { - if (expression.GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF) { - return ColumnBecomesNull(expression.Cast(), null_relations, relation_mapping); - } - if (expression.GetExpressionClass() == ExpressionClass::BOUND_FUNCTION) { - auto &function = expression.Cast(); - if (function.Function().GetNullHandling() == FunctionNullHandling::SPECIAL_HANDLING) { - return false; - } - } - if (!expression.PropagatesNullValues()) { - return false; - } - bool child_becomes_null = false; - ExpressionIterator::EnumerateChildren(expression, [&](const Expression &child) { - child_becomes_null |= ExpressionBecomesNull(child, null_relations, relation_mapping); - }); - return child_becomes_null; -} - -static bool ExpressionRejectsNulls(const Expression &expression, const JoinRelationSet &null_relations, - const unordered_map &relation_mapping) { +static bool ExpressionRejectsNulls(const Expression &expression, + const std::function &column_becomes_null) { if (expression.GetExpressionType() == ExpressionType::CONJUNCTION_AND || expression.GetExpressionType() == ExpressionType::CONJUNCTION_OR) { auto &conjunction = expression.Cast(); @@ -118,17 +96,16 @@ static bool ExpressionRejectsNulls(const Expression &expression, const JoinRelat auto rejects = expression.GetExpressionType() == ExpressionType::CONJUNCTION_OR; for (auto &child : conjunction.GetChildren()) { if (expression.GetExpressionType() == ExpressionType::CONJUNCTION_AND) { - rejects |= ExpressionRejectsNulls(*child, null_relations, relation_mapping); + rejects |= ExpressionRejectsNulls(*child, column_becomes_null); } else { - rejects &= ExpressionRejectsNulls(*child, null_relations, relation_mapping); + rejects &= ExpressionRejectsNulls(*child, column_becomes_null); } } return rejects; } if (expression.GetExpressionType() == ExpressionType::OPERATOR_IS_NOT_NULL) { auto &op = expression.Cast(); - return !op.GetChildren().empty() && - ExpressionBecomesNull(*op.GetChildren()[0], null_relations, relation_mapping); + return !op.GetChildren().empty() && ExpressionBecomesNull(*op.GetChildren()[0], column_becomes_null); } if (BoundComparisonExpression::IsComparison(expression)) { if (expression.GetExpressionType() == ExpressionType::COMPARE_DISTINCT_FROM || @@ -136,26 +113,29 @@ static bool ExpressionRejectsNulls(const Expression &expression, const JoinRelat return false; } auto &comparison = expression.Cast(); - return ExpressionBecomesNull(BoundComparisonExpression::Left(comparison), null_relations, relation_mapping) || - ExpressionBecomesNull(BoundComparisonExpression::Right(comparison), null_relations, relation_mapping); + return ExpressionBecomesNull(BoundComparisonExpression::Left(comparison), column_becomes_null) || + ExpressionBecomesNull(BoundComparisonExpression::Right(comparison), column_becomes_null); } return expression.GetReturnType().id() == LogicalTypeId::BOOLEAN && - ExpressionBecomesNull(expression, null_relations, relation_mapping); + ExpressionBecomesNull(expression, column_becomes_null); } static bool PredicateRejectsNulls(const vector &conditions, const JoinRelationSet &null_relations, const unordered_map &relation_mapping) { + auto column_becomes_null = [&](const BoundColumnRefExpression &column) { + return ColumnBecomesNull(column, null_relations, relation_mapping); + }; for (auto &condition : conditions) { if (condition.IsComparison()) { if (condition.GetComparisonType() == ExpressionType::COMPARE_DISTINCT_FROM || condition.GetComparisonType() == ExpressionType::COMPARE_NOT_DISTINCT_FROM) { continue; } - if (ExpressionBecomesNull(condition.GetLHS(), null_relations, relation_mapping) || - ExpressionBecomesNull(condition.GetRHS(), null_relations, relation_mapping)) { + if (ExpressionBecomesNull(condition.GetLHS(), column_becomes_null) || + ExpressionBecomesNull(condition.GetRHS(), column_becomes_null)) { return true; } - } else if (ExpressionRejectsNulls(condition.GetJoinExpression(), null_relations, relation_mapping)) { + } else if (ExpressionRejectsNulls(condition.GetJoinExpression(), column_becomes_null)) { return true; } } diff --git a/src/duckdb/src/optimizer/join_order/join_order_optimizer.cpp b/src/duckdb/src/optimizer/join_order/join_order_optimizer.cpp index 98a8f2b33..1ed6428e9 100644 --- a/src/duckdb/src/optimizer/join_order/join_order_optimizer.cpp +++ b/src/duckdb/src/optimizer/join_order/join_order_optimizer.cpp @@ -8,7 +8,6 @@ #include "duckdb/optimizer/join_order/plan_enumerator.hpp" #include "duckdb/planner/expression/list.hpp" #include "duckdb/planner/operator/list.hpp" -#include "duckdb/main/settings.hpp" namespace duckdb { @@ -27,14 +26,12 @@ static optional CombineReorderableStats(const vector source; - for (auto &stats : relation_stats) { - source = stats.GetColumnStats(binding); - if (source) { - break; - } - } + auto source = lookup.Find(binding); if (!source) { return {}; } @@ -59,8 +56,7 @@ JoinOrderOptimizer JoinOrderOptimizer::CreateChildOptimizer() { unique_ptr JoinOrderOptimizer::Optimize(unique_ptr plan, optional_ptr stats) { - auto max_expression_depth = Settings::Get(query_graph_manager.context); - if (depth > max_expression_depth) { + if (depth > MAX_RECURSION_DEPTH) { // Very deep plans will eventually consume quite some stack space // Returning the current plan is always a valid choice return plan; diff --git a/src/duckdb/src/optimizer/join_order/join_predicate.cpp b/src/duckdb/src/optimizer/join_order/join_predicate.cpp index 71c1ed2e1..f832c7b6c 100644 --- a/src/duckdb/src/optimizer/join_order/join_predicate.cpp +++ b/src/duckdb/src/optimizer/join_order/join_predicate.cpp @@ -164,6 +164,14 @@ optional_idx JoinPredicate::GetEqualityClassIndex() const { return equality_class_index; } +void JoinPredicate::SetCompositeJoinPair(JoinRelationSet &pair) { + composite_join_pair = pair; +} + +optional_ptr JoinPredicate::GetCompositeJoinPair() const { + return composite_join_pair; +} + JoinEqualityPredicateEdge::JoinEqualityPredicateEdge(JoinPredicate &predicate, RelationIndex left_relation, RelationIndex right_relation, ColumnBinding left_binding, ColumnBinding right_binding) @@ -171,6 +179,46 @@ JoinEqualityPredicateEdge::JoinEqualityPredicateEdge(JoinPredicate &predicate, R right_binding(right_binding) { } +idx_t JoinEqualityGroups::GetOrCreateGroup(const ColumnBinding &binding) { + auto entry = binding_to_group.emplace(binding, parents.size()); + if (entry.second) { + parents.push_back(entry.first->second); + } + return entry.first->second; +} + +idx_t JoinEqualityGroups::FindRoot(idx_t group) { + while (parents[group] != group) { + parents[group] = parents[parents[group]]; + group = parents[group]; + } + return group; +} + +void JoinEqualityGroups::AddEquality(const ColumnBinding &left, const ColumnBinding &right) { + auto left_root = FindRoot(GetOrCreateGroup(left)); + auto right_root = FindRoot(GetOrCreateGroup(right)); + if (left_root != right_root) { + parents[MaxValue(left_root, right_root)] = MinValue(left_root, right_root); + } +} + +idx_t JoinEqualityGroups::GetGroup(const ColumnBinding &binding) { + auto entry = binding_to_group.find(binding); + D_ASSERT(entry != binding_to_group.end()); + return FindRoot(entry->second); +} + +void RelationPairEqualitySummary::AddEquality(idx_t equality_class_index, ColumnBinding first_binding, + ColumnBinding second_binding) { + if (std::find(direct_equality_class_indices.begin(), direct_equality_class_indices.end(), equality_class_index) == + direct_equality_class_indices.end()) { + direct_equality_class_indices.push_back(equality_class_index); + } + first_relation_bindings.insert(first_binding); + second_relation_bindings.insert(second_binding); +} + bool RelationPairEqualitySummary::HasDirectCompositeEquality() const { return direct_equality_class_indices.size() >= 2 && first_relation_bindings.size() >= 2 && second_relation_bindings.size() >= 2; @@ -215,18 +263,9 @@ void JoinPredicateModel::AddEqualityClass(JoinEqualityClass equality_class) { equality_classes.push_back(std::move(equality_class)); } -bool JoinPredicateModel::ContainsClassIndex(const vector &class_indices, idx_t equality_class_index) { - return std::find(class_indices.begin(), class_indices.end(), equality_class_index) != class_indices.end(); -} - void JoinPredicateModel::AddDirectEqualityPairClass(JoinRelationSet &pair, idx_t equality_class_index, ColumnBinding first_binding, ColumnBinding second_binding) { - auto &summary = equality_pairs[pair]; - if (!ContainsClassIndex(summary.direct_equality_class_indices, equality_class_index)) { - summary.direct_equality_class_indices.push_back(equality_class_index); - } - summary.first_relation_bindings.insert(first_binding); - summary.second_relation_bindings.insert(second_binding); + equality_pairs[pair].AddEquality(equality_class_index, first_binding, second_binding); } const vector> &JoinPredicateModel::GetPredicates() const { diff --git a/src/duckdb/src/optimizer/join_order/join_relation_set.cpp b/src/duckdb/src/optimizer/join_order/join_relation_set.cpp index 10e319dee..7794c423f 100644 --- a/src/duckdb/src/optimizer/join_order/join_relation_set.cpp +++ b/src/duckdb/src/optimizer/join_order/join_relation_set.cpp @@ -9,8 +9,18 @@ namespace duckdb { using JoinRelationTreeNode = JoinRelationSetManager::JoinRelationTreeNode; +static constexpr idx_t BITMASK_WORD_SIZE = sizeof(idx_t) * 8; + JoinRelationSet::JoinRelationSet(unsafe_unique_array relations, idx_t count) : relations(std::move(relations)), count(count) { + if (count != 0) { + // the relations are sorted, so the last one determines the number of words + bitmask.resize(this->relations[count - 1].index / BITMASK_WORD_SIZE + 1, 0); + for (idx_t i = 0; i < count; i++) { + auto index = this->relations[i].index; + bitmask[index / BITMASK_WORD_SIZE] |= idx_t(1) << (index % BITMASK_WORD_SIZE); + } + } } // LCOV_EXCL_START @@ -30,33 +40,23 @@ bool JoinRelationSet::Empty() const { //! Returns true if sub is a subset of super bool JoinRelationSet::IsSubset(const JoinRelationSet &super, const JoinRelationSet &sub) { D_ASSERT(sub.count > 0); - if (sub.count > super.count) { + if (sub.count > super.count || sub.bitmask.size() > super.bitmask.size()) { return false; } - idx_t j = 0; - for (idx_t i = 0; i < super.count; i++) { - if (sub.relations[j] == super.relations[i]) { - j++; - if (j == sub.count) { - return true; - } + for (idx_t i = 0; i < sub.bitmask.size(); i++) { + if (sub.bitmask[i] & ~super.bitmask[i]) { + return false; } } - return false; + return true; } bool JoinRelationSet::Intersects(const JoinRelationSet &left, const JoinRelationSet &right) { - idx_t left_idx = 0; - idx_t right_idx = 0; - while (left_idx < left.count && right_idx < right.count) { - if (left.relations[left_idx] == right.relations[right_idx]) { + const auto words = MinValue(left.bitmask.size(), right.bitmask.size()); + for (idx_t i = 0; i < words; i++) { + if (left.bitmask[i] & right.bitmask[i]) { return true; } - if (left.relations[left_idx] < right.relations[right_idx]) { - left_idx++; - } else { - right_idx++; - } } return false; } diff --git a/src/duckdb/src/optimizer/join_order/plan_enumerator.cpp b/src/duckdb/src/optimizer/join_order/plan_enumerator.cpp index f88abebee..5c4a415fa 100644 --- a/src/duckdb/src/optimizer/join_order/plan_enumerator.cpp +++ b/src/duckdb/src/optimizer/join_order/plan_enumerator.cpp @@ -413,45 +413,51 @@ bool PlanEnumerator::SolveJoinOrderApproximately() { for (idx_t i = 0; i < query_graph_manager.relation_manager.NumRelations(); i++) { join_relations.push_back(query_graph_manager.set_manager.GetJoinRelation(RelationIndex(i))); } - while (join_relations.size() > 1) { - // now in every step of the algorithm, we greedily pick the join between the to-be-joined relations that has the - // smallest cost. This is O(r^2) per step, and every step will reduce the total amount of relations to-be-joined - // by 1, so the total cost is O(r^3) in the amount of relations - // long is needed to prevent clang-tidy complaints. (idx_t) cannot be added to an iterator position because it - // is unsigned. - idx_t best_left = 0, best_right = 0; - bool found_connection = false; - double best_cost = NumericLimits::Maximum(); + + // in every step of the algorithm, we greedily pick the join between the to-be-joined relations that has the + // smallest cost. Pair costs are deterministic while the query graph is unchanged, so we evaluate each pair once + // and keep the viable candidates in a list, instead of re-emitting every pair in every step + struct GreedyCandidate { + optional_ptr left; + optional_ptr right; + double cost; + }; + vector candidates; + auto EmitCandidate = [&](JoinRelationSet &left, JoinRelationSet &right) { + auto &connection = GetConnections(left, right); + if (connection.empty()) { + return; + } + auto node = EmitPair(left, right, connection); + if (node) { + candidates.push_back({&left, &right, node->cost}); + } + }; + auto InitializeCandidates = [&]() { + candidates.clear(); for (idx_t i = 0; i < join_relations.size(); i++) { - auto left = join_relations[i]; for (idx_t j = i + 1; j < join_relations.size(); j++) { - auto right = join_relations[j]; - // check if we can connect these two relations - auto &connection = GetConnections(left, right); - if (!connection.empty()) { - // we can check the cost of this connection - auto node = EmitPair(left, right, connection); - if (!node) { - continue; - } + EmitCandidate(join_relations[i], join_relations[j]); + } + } + }; + InitializeCandidates(); - // update the DP tree in case a plan created by the DP algorithm uses the node - // that was potentially just updated by EmitPair. You will get a use-after-free - // error if future plans rely on the old node that was just replaced. - // if node in FullPath, then updateDP tree. - - if (!found_connection || node->cost < best_cost) { - // best pair found so far - found_connection = true; - best_cost = node->cost; - best_left = i; - best_right = j; - } - } + while (join_relations.size() > 1) { + optional_ptr best_left; + optional_ptr best_right; + double best_cost = NumericLimits::Maximum(); + for (auto &candidate : candidates) { + if (!best_left || candidate.cost < best_cost) { + best_cost = candidate.cost; + best_left = candidate.left; + best_right = candidate.right; } } - if (!found_connection) { + if (!best_left) { if (ActivateRequiredCrossProducts()) { + // the query graph gained edges, which invalidates the evaluated candidates + InitializeCandidates(); continue; } // Optimizer-introduced cross products are valid only inside an inner-join companion set (Section 6.2). @@ -466,41 +472,50 @@ bool PlanEnumerator::SolveJoinOrderApproximately() { auto right_plan = plans.find(right); D_ASSERT(left_plan != plans.end() && right_plan != plans.end()); auto cost = left_plan->second->cost + right_plan->second->cost; - if (!found_connection || cost < best_cost) { - found_connection = true; + if (!best_left || cost < best_cost) { best_cost = cost; - best_left = i; - best_right = j; + best_left = left; + best_right = right; } } } - if (!found_connection) { + if (!best_left) { return false; } - auto &left = join_relations[best_left].get(); - auto &right = join_relations[best_right].get(); // create a cross product edge (i.e. edge with empty filter) between these two sets in the query graph - query_graph_manager.CreateQueryGraphCrossProduct(left, right); + query_graph_manager.CreateQueryGraphCrossProduct(*best_left, *best_right); + // the new edge only connects this pair, which is merged below, so the other candidates remain valid connection_cache.clear(); // now emit the pair and continue with the algorithm - auto &connections = GetConnections(left, right); + auto &connections = GetConnections(*best_left, *best_right); D_ASSERT(!connections.empty()); - auto cross_product = EmitPair(left, right, connections); + auto cross_product = EmitPair(*best_left, *best_right, connections); if (!cross_product) { return false; } } - // now update the to-be-checked pairs - // remove left and right, and add the combination - - // important to erase the biggest element first - // if we erase the smallest element first the index of the biggest element changes - auto &new_set = query_graph_manager.set_manager.Union(join_relations.at(best_left).get(), - join_relations.at(best_right).get()); - D_ASSERT(best_right > best_left); - join_relations.erase(join_relations.begin() + (int64_t)best_right); - join_relations.erase(join_relations.begin() + (int64_t)best_left); + // now update the to-be-checked pairs: remove left and right, and add the combination + auto &new_set = query_graph_manager.set_manager.Union(*best_left, *best_right); + for (idx_t i = join_relations.size(); i > 0; i--) { + auto &relation = join_relations[i - 1].get(); + if (&relation == best_left.get() || &relation == best_right.get()) { + join_relations.erase(join_relations.begin() + NumericCast(i - 1)); + } + } + idx_t kept_candidates = 0; + for (idx_t candidate_idx = 0; candidate_idx < candidates.size(); candidate_idx++) { + auto &candidate = candidates[candidate_idx]; + if (candidate.left == best_left || candidate.left == best_right || candidate.right == best_left || + candidate.right == best_right) { + continue; + } + candidates[kept_candidates++] = candidate; + } + candidates.resize(kept_candidates); + for (auto &relation : join_relations) { + EmitCandidate(relation, new_set); + } join_relations.push_back(new_set); } return true; diff --git a/src/duckdb/src/optimizer/join_order/query_graph_manager.cpp b/src/duckdb/src/optimizer/join_order/query_graph_manager.cpp index 149d4b127..7f91cf700 100644 --- a/src/duckdb/src/optimizer/join_order/query_graph_manager.cpp +++ b/src/duckdb/src/optimizer/join_order/query_graph_manager.cpp @@ -26,27 +26,7 @@ QueryGraphManager::QueryGraphManager(ClientContext &context) : context(context), void QueryGraphManager::BuildPredicateModel() { predicate_model.Clear(); - column_binding_map_t binding_to_component; - vector parents; - - auto get_component = [&](const ColumnBinding &binding) -> idx_t { - auto entry = binding_to_component.find(binding); - if (entry != binding_to_component.end()) { - return entry->second; - } - auto component = parents.size(); - parents.push_back(component); - binding_to_component[binding] = component; - return component; - }; - - auto find_root = [&](idx_t component) -> idx_t { - while (parents[component] != component) { - parents[component] = parents[parents[component]]; - component = parents[component]; - } - return component; - }; + JoinEqualityGroups equality_groups; // First union all normalized equality predicates. Do not assign edge ids in this pass, // because a later predicate can merge two previously separate components. @@ -66,11 +46,7 @@ void QueryGraphManager::BuildPredicateModel() { continue; } - auto left_root = find_root(get_component(predicate.GetEqualityBinding(true))); - auto right_root = find_root(get_component(predicate.GetEqualityBinding(false))); - if (left_root != right_root) { - parents[MaxValue(left_root, right_root)] = MinValue(left_root, right_root); - } + equality_groups.AddEquality(predicate.GetEqualityBinding(true), predicate.GetEqualityBinding(false)); } // Then assign stable final ids to every equality edge using the final roots. @@ -81,7 +57,7 @@ void QueryGraphManager::BuildPredicateModel() { if (!predicate.CanBuildEqualityClosure()) { continue; } - auto root = find_root(binding_to_component[predicate.GetEqualityBinding(true)]); + auto root = equality_groups.GetGroup(predicate.GetEqualityBinding(true)); auto entry = root_to_equivalence_id.find(root); if (entry == root_to_equivalence_id.end()) { entry = root_to_equivalence_id.insert(make_pair(root, root_to_equivalence_id.size())).first; @@ -125,6 +101,79 @@ void QueryGraphManager::BuildPredicateModel() { predicate_model.AddDirectEqualityPairClass(pair, equality_class.index, first_binding, second_binding); } } + BuildLeftEqualityPairs(); +} + +void QueryGraphManager::BuildLeftEqualityPairs() { + for (auto &op : join_operators) { + if (op->type != JoinOrderOperatorType::LEFT) { + continue; + } + JoinEqualityGroups groups; + auto add_equality = [&](ExpressionType type, const Expression &left, const Expression &right) { + if (type != ExpressionType::COMPARE_EQUAL && type != ExpressionType::COMPARE_NOT_DISTINCT_FROM) { + return; + } + ColumnBinding left_binding; + ColumnBinding right_binding; + GetEquivalenceBinding(left, left_binding); + GetEquivalenceBinding(right, right_binding); + if (left_binding.table_index.IsValid() && right_binding.table_index.IsValid()) { + groups.AddEquality(left_binding, right_binding); + } + }; + vector> residuals; + for (auto &condition : op->conditions) { + if (condition.IsComparison()) { + add_equality(condition.GetComparisonType(), condition.GetLHS(), condition.GetRHS()); + } else { + residuals.push_back(condition.GetJoinExpression()); + } + } + // Same-side ON equalities can connect keys even though they are not costing predicates. + while (!residuals.empty()) { + auto &expression = residuals.back().get(); + residuals.pop_back(); + if (expression.GetExpressionType() == ExpressionType::CONJUNCTION_AND) { + for (auto &child : expression.Cast().GetChildren()) { + residuals.push_back(*child); + } + } else if (BoundComparisonExpression::IsComparison(expression)) { + auto &comparison = expression.Cast(); + add_equality(expression.GetExpressionType(), BoundComparisonExpression::Left(comparison), + BoundComparisonExpression::Right(comparison)); + } + } + reference_map_t summaries; + vector, reference>> pairs; + for (auto predicate_index : op->costing_predicate_indices) { + auto &predicate = predicate_model.GetPredicates()[predicate_index].get(); + auto type = predicate.GetComparisonType(); + if (!predicate.CanBuildSelectivityDomain() || !predicate.HasValidEqualityBindings() || + (type != ExpressionType::COMPARE_EQUAL && type != ExpressionType::COMPARE_NOT_DISTINCT_FROM)) { + continue; + } + auto first_binding = predicate.GetEqualityBinding(true); + auto second_binding = predicate.GetEqualityBinding(false); + auto first_relation = JoinOrderUtil::GetBindingRelation(first_binding); + auto second_relation = JoinOrderUtil::GetBindingRelation(second_binding); + if (first_relation == second_relation) { + continue; + } + auto &pair = set_manager.Union(set_manager.GetJoinRelation(first_relation), + set_manager.GetJoinRelation(second_relation)); + if (pair.relations[0] != first_relation) { + std::swap(first_binding, second_binding); + } + summaries[pair].AddEquality(groups.GetGroup(first_binding), first_binding, second_binding); + pairs.emplace_back(predicate, pair); + } + for (auto &entry : pairs) { + if (summaries.at(entry.second).HasDirectCompositeEquality()) { + entry.first.get().SetCompositeJoinPair(entry.second); + } + } + } } void QueryGraphManager::BuildInnerCompanionSets() { diff --git a/src/duckdb/src/optimizer/join_order/relation_manager.cpp b/src/duckdb/src/optimizer/join_order/relation_manager.cpp index 7d83d9cef..8b6139cae 100644 --- a/src/duckdb/src/optimizer/join_order/relation_manager.cpp +++ b/src/duckdb/src/optimizer/join_order/relation_manager.cpp @@ -264,6 +264,22 @@ static bool JoinIsReorderable(LogicalOperator &op) { return false; } +static bool IsProjectedRecursiveCTERef(const LogicalOperator &op, const JoinOrderOptimizer &optimizer) { + optional_ptr source = op; + while (source->children.size() == 1) { + if (OperatorIsNonReorderable(source->type) || + (OperatorNeedsRelation(source->type) && source->type != LogicalOperatorType::LOGICAL_PROJECTION)) { + return false; + } + source = source->children[0].get(); + } + if (source->type != LogicalOperatorType::LOGICAL_CTE_REF) { + return false; + } + auto &cte_ref = source->Cast(); + return optimizer.recursive_cte_indexes.find(cte_ref.cte_index) != optimizer.recursive_cte_indexes.end(); +} + static bool RecursiveCTERefCanReorder(optional_ptr parent) { if (!parent || parent->type != LogicalOperatorType::LOGICAL_COMPARISON_JOIN) { return false; @@ -346,14 +362,12 @@ static optional CombineNonReorderableStats(LogicalOperator &op, c auto ordinal_set_output = op.type == LogicalOperatorType::LOGICAL_UNION || op.type == LogicalOperatorType::LOGICAL_INTERSECT || op.type == LogicalOperatorType::LOGICAL_EXCEPT; + RelationColumnStatsLookup lookup; + for (auto &stats : child_stats) { + lookup.AddRelation(stats); + } for (idx_t column_idx = 0; column_idx < bindings.size(); column_idx++) { - optional_ptr source; - for (auto &stats : child_stats) { - source = stats.GetColumnStats(bindings[column_idx]); - if (source) { - break; - } - } + auto source = lookup.Find(bindings[column_idx]); if (!source && ordinal_set_output && !child_stats.empty() && column_idx < child_stats[0].columns.size()) { source = child_stats[0].columns[column_idx]; } @@ -584,7 +598,11 @@ bool RelationManager::ExtractJoinRelations(JoinOrderOptimizer &optimizer, Logica } proj.SetEstimatedCardinality(proj_stats->cardinality); ModifyStatsIfLimit(limit_op.get(), *proj_stats); - return AddRelation(input_op, parent, *proj_stats); + if (!AddRelation(input_op, parent, *proj_stats)) { + return false; + } + // A projection must preserve the recursive reference's join-order restriction. + return !IsProjectedRecursiveCTERef(proj, optimizer) || RecursiveCTERefCanReorder(parent); } case LogicalOperatorType::LOGICAL_EMPTY_RESULT: { // optimize the child and copy the stats diff --git a/src/duckdb/src/optimizer/multi_stage_aggregate_rewriter.cpp b/src/duckdb/src/optimizer/multi_stage_aggregate_rewriter.cpp index bd4eac573..ff8524f89 100644 --- a/src/duckdb/src/optimizer/multi_stage_aggregate_rewriter.cpp +++ b/src/duckdb/src/optimizer/multi_stage_aggregate_rewriter.cpp @@ -1,6 +1,8 @@ #include "duckdb/optimizer/multi_stage_aggregate_rewriter.hpp" #include "duckdb/common/string_util.hpp" +#include "duckdb/function/aggregate/distributive_function_utils.hpp" +#include "duckdb/function/function_binder.hpp" #include "duckdb/optimizer/aggregate_rewrite_helper.hpp" #include "duckdb/optimizer/aggregate_rewrite.hpp" #include "duckdb/optimizer/optimizer.hpp" @@ -9,6 +11,7 @@ #include "duckdb/planner/expression/bound_aggregate_expression.hpp" #include "duckdb/planner/expression/bound_columnref_expression.hpp" #include "duckdb/planner/expression/bound_constant_expression.hpp" +#include "duckdb/planner/expression_binder.hpp" #include "duckdb/planner/expression_iterator.hpp" #include "duckdb/planner/operator/logical_aggregate.hpp" #include "duckdb/planner/operator/logical_comparison_join.hpp" @@ -27,12 +30,15 @@ MultiStageAggregateRewriter::MultiStageAggregateRewriter( namespace { struct DistinctAggregateSet { - explicit DistinctAggregateSet(idx_t source_index) : source_index(source_index) { + DistinctAggregateSet(idx_t source_index, vector> keys_p) + : source_index(source_index), keys(std::move(keys_p)) { aggregate_indices.push_back(source_index); } idx_t source_index; vector aggregate_indices; + //! Actual deduplication keys, including collations unless the binder already applied them. + vector> keys; vector> order_expressions; }; @@ -62,6 +68,24 @@ static optional_idx FindExpression(const vector> &express return optional_idx(); } +//! Expression equality ignores the return type, but a collation lives in the return type and decides which +//! argument values are duplicates of each other. Two distinct aggregates can only share a deduplication branch +//! if their arguments agree on that as well. +static bool ArgumentsEqualIncludingCollation(const BoundAggregateExpression &lhs, const BoundAggregateExpression &rhs) { + auto &left = lhs.GetChildren(); + auto &right = rhs.GetChildren(); + if (left.size() != right.size()) { + return false; + } + for (idx_t i = 0; i < left.size(); i++) { + if (!Expression::Equals(*left[i], *right[i]) || + !left[i]->GetReturnType().EqualsIncludingCollation(right[i]->GetReturnType())) { + return false; + } + } + return true; +} + static void AddOrderExpressions(DistinctAggregateSet &set, const BoundAggregateExpression &aggregate) { if (!aggregate.GetOrderBys()) { return; @@ -78,33 +102,34 @@ static void AddOrderExpressions(DistinctAggregateSet &set, const BoundAggregateE static unique_ptr CreateFinalAggregate(const BoundAggregateExpression &source, const DistinctAggregateSet &set, - TableIndex input_table, idx_t input_column_offset, - idx_t order_column_offset, + TableIndex input_table, idx_t order_column_offset, + const vector &value_columns, optional_idx filter_column_offset = optional_idx()) { auto result = unique_ptr_cast(source.Copy()); result->GetAggregateTypeMutable() = AggregateType::NON_DISTINCT; result->GetChildrenMutable().clear(); for (idx_t child_idx = 0; child_idx < source.GetChildren().size(); child_idx++) { auto &child = source.GetChildren()[child_idx]; - result->GetChildrenMutable().push_back(make_uniq( - child->GetReturnType(), ColumnBinding(input_table, ProjectionIndex(input_column_offset + child_idx)))); + auto &value_column = value_columns[child_idx]; + result->GetChildrenMutable().push_back( + make_uniq(child->GetReturnType(), value_column)); } if (source.GetOrderBys()) { result->GetOrderBysMutable() = make_uniq(); for (auto &order : source.GetOrderBys()->orders) { auto order_idx = FindExpression(source.GetChildren(), *order.expression); - idx_t column_offset; + ColumnBinding order_binding; if (order_idx.IsValid()) { - column_offset = input_column_offset + order_idx.GetIndex(); + // the ORDER BY is one of the arguments - sort on the value of that argument + order_binding = value_columns[order_idx.GetIndex()]; } else { order_idx = FindExpression(set.order_expressions, *order.expression); D_ASSERT(order_idx.IsValid()); - column_offset = order_column_offset + order_idx.GetIndex(); + order_binding = ColumnBinding(input_table, ProjectionIndex(order_column_offset + order_idx.GetIndex())); } result->GetOrderBysMutable()->orders.emplace_back( order.type, order.null_order, - make_uniq(order.expression->GetReturnType(), - ColumnBinding(input_table, ProjectionIndex(column_offset)))); + make_uniq(order.expression->GetReturnType(), order_binding)); } } else { result->GetOrderBysMutable().reset(); @@ -153,8 +178,30 @@ static BranchResult CreateDistinctBranch(Optimizer &optimizer, LogicalAggregate for (auto &group : aggr.groups) { distinct_groups.push_back(AggregateRewriteHelper::CopyAndRebind(*group, input_replacements)); } - for (auto &child : source_aggregate.GetChildren()) { - distinct_groups.push_back(AggregateRewriteHelper::CopyAndRebind(*child, input_replacements)); + auto distinct_group_index = optimizer.binder.GenerateTableIndex(); + auto distinct_aggregate_index = optimizer.binder.GenerateTableIndex(); + + // Group on comparison keys, but pass original values to the final aggregate. When these differ, + // FIRST carries a representative, just as for collated GROUP BY. + vector> distinct_aggregates; + vector value_columns; + value_columns.reserve(source_aggregate.GetChildren().size()); + FunctionBinder function_binder(optimizer.GetContext()); + for (idx_t child_idx = 0; child_idx < source_aggregate.GetChildren().size(); child_idx++) { + auto &child = source_aggregate.GetChildren()[child_idx]; + auto &key = set.keys[child_idx]; + auto value_binding = ColumnBinding(distinct_group_index, ProjectionIndex(distinct_groups.size())); + if (!Expression::Equals(*child, *key)) { + vector> first_children; + first_children.push_back(AggregateRewriteHelper::CopyAndRebind(*child, input_replacements)); + auto first_aggregate = + function_binder.BindAggregateFunction(FirstFunctionGetter::GetFunction(child->GetReturnType()), + std::move(first_children), nullptr, AggregateType::NON_DISTINCT); + value_binding = ColumnBinding(distinct_aggregate_index, ProjectionIndex(distinct_aggregates.size())); + distinct_aggregates.push_back(std::move(first_aggregate)); + } + value_columns.push_back(value_binding); + distinct_groups.push_back(AggregateRewriteHelper::CopyAndRebind(*key, input_replacements)); } for (auto &order_expr : set.order_expressions) { distinct_groups.push_back(AggregateRewriteHelper::CopyAndRebind(*order_expr, input_replacements)); @@ -167,10 +214,8 @@ static BranchResult CreateDistinctBranch(Optimizer &optimizer, LogicalAggregate AggregateRewriteHelper::CopyAndRebind(*source_aggregate.GetFilter(), input_replacements)); } - auto distinct_group_index = optimizer.binder.GenerateTableIndex(); - auto distinct_aggregate_index = optimizer.binder.GenerateTableIndex(); auto distinct = - make_uniq(distinct_group_index, distinct_aggregate_index, vector>()); + make_uniq(distinct_group_index, distinct_aggregate_index, std::move(distinct_aggregates)); distinct->groups = std::move(distinct_groups); distinct->children.push_back(std::move(input)); @@ -186,8 +231,8 @@ static BranchResult CreateDistinctBranch(Optimizer &optimizer, LogicalAggregate const auto order_column_offset = group_count + source_aggregate.GetChildren().size(); for (auto aggregate_idx : set.aggregate_indices) { auto &aggregate = aggr.expressions[aggregate_idx]->Cast(); - final_aggregates.push_back(CreateFinalAggregate(aggregate, set, distinct_group_index, group_count, - order_column_offset, filter_column_offset)); + final_aggregates.push_back(CreateFinalAggregate(aggregate, set, distinct_group_index, order_column_offset, + value_columns, filter_column_offset)); } auto final_group_index = optimizer.binder.GenerateTableIndex(); @@ -346,6 +391,7 @@ static BranchResult CreateRewriteBranch(Optimizer &optimizer, LogicalAggregate & optional_idx input_cte_index; if (input_consumers > 1) { input_cte_index = optimizer.binder.GenerateTableIndex().index; + input = LogicalProjection::CreateIdentity(optimizer.binder.GenerateTableIndex(), std::move(input)); } struct StageOutput { @@ -369,7 +415,8 @@ static BranchResult CreateRewriteBranch(Optimizer &optimizer, LogicalAggregate & AggregateRewriteHelper::CreateCTERef(optimizer, TableIndex(input_cte_index.GetIndex()), input_types, input_names, input_bindings, replacements)); } else { - sources.push_back(std::move(input)); + // The validation above guarantees that the input is moved exactly once when no CTE is needed. + sources.push_back(std::move(input)); // NOLINT(bugprone-use-after-move) } } else { auto &source_stage = set.plan->stages[source.stage_index]; @@ -686,10 +733,20 @@ bool MultiStageAggregateRewriter::TryRewrite(unique_ptr &op) { regular_aggregates.push_back(aggregate_idx); continue; } + // Resolve keys before sharing branches: COUNT may already consume keys from the binder, while a + // value-returning aggregate with the same argument still needs its collation applied. + vector> keys; + for (auto &child : aggregate.GetChildren()) { + auto key = child->Copy(); + if (aggregate.Function().GetCollationHandling() != FunctionCollationHandling::PUSH_DISTINCT_COLLATIONS) { + ExpressionBinder::PushCollation(optimizer.GetContext(), key, key->GetReturnType()); + } + keys.push_back(std::move(key)); + } bool found_match = false; for (auto &set : distinct_sets) { auto &other = aggr.expressions[set.source_index]->Cast(); - if (Expression::ListEquals(aggregate.GetChildren(), other.GetChildren()) && + if (ArgumentsEqualIncludingCollation(aggregate, other) && Expression::ListEquals(keys, set.keys) && Expression::Equals(aggregate.GetFilter(), other.GetFilter())) { set.aggregate_indices.push_back(aggregate_idx); AddOrderExpressions(set, aggregate); @@ -698,7 +755,7 @@ bool MultiStageAggregateRewriter::TryRewrite(unique_ptr &op) { } } if (!found_match) { - distinct_sets.emplace_back(aggregate_idx); + distinct_sets.emplace_back(aggregate_idx, std::move(keys)); AddOrderExpressions(distinct_sets.back(), aggregate); } } @@ -717,6 +774,8 @@ bool MultiStageAggregateRewriter::TryRewrite(unique_ptr &op) { input_names = AggregateRewriteHelper::GenerateColumnNames("__aggregate_input", input_types.size()); input_bindings = op->children[0]->GetColumnBindings(); cte_index = optimizer.binder.GenerateTableIndex(); + op->children[0] = + LogicalProjection::CreateIdentity(optimizer.binder.GenerateTableIndex(), std::move(op->children[0])); } vector branches; diff --git a/src/duckdb/src/optimizer/optimizer.cpp b/src/duckdb/src/optimizer/optimizer.cpp index edc5d2078..e9ef3cb82 100644 --- a/src/duckdb/src/optimizer/optimizer.cpp +++ b/src/duckdb/src/optimizer/optimizer.cpp @@ -62,6 +62,7 @@ #include "duckdb/planner/logical_plan_verifier.hpp" #include "duckdb/planner/planner.hpp" #include "duckdb/planner/operator/logical_prepare.hpp" +#include "duckdb/planner/operator/logical_secure_view.hpp" #include "duckdb/optimizer/remote_pushdown_optimizer.hpp" #include "duckdb/main/database_manager.hpp" #include "duckdb/main/settings.hpp" @@ -129,13 +130,11 @@ bool Optimizer::OptimizerDisabled(ClientContext &context_p, OptimizerType type) return true; } auto &config = DBConfig::GetConfig(context_p); - return config.options.disabled_optimizers.find(type) != config.options.disabled_optimizers.end(); + return config.IsOptimizerDisabled(type); } void Optimizer::RunOptimizer(OptimizerType type, const std::function &callback) { - if (context.IsInterrupted()) { - throw InterruptException(); - } + context.InterruptCheck(); if (OptimizerDisabled(type)) { // optimizer is marked as disabled: skip @@ -244,6 +243,10 @@ void Optimizer::RunBuiltInOptimizers() { default: break; } + // determine which secure views may expose the statistics of their contents - this runs before any rewrites so + // that the filters the optimizer pushes into a view are not mistaken for the view restricting its own rows + LogicalSecureView::AnalyzeStatistics(*plan); + // first we perform expression rewrites using the ExpressionRewriter // this does not change the logical plan structure, but only simplifies the expression trees RunOptimizer(OptimizerType::EXPRESSION_REWRITER, [&]() { @@ -288,6 +291,7 @@ void Optimizer::RunBuiltInOptimizers() { CTEFilterPusher cte_filter_pusher(*this); plan = cte_filter_pusher.Optimize(std::move(plan)); }); + CTEFilterPusher::ClearDependencies(*plan); RunOptimizer(OptimizerType::REGEX_RANGE, [&]() { RegexRangeFilter regex_opt; @@ -380,7 +384,7 @@ void Optimizer::RunBuiltInOptimizers() { // rewrites UNNESTs in DelimJoins by moving them to the projection RunOptimizer(OptimizerType::UNNEST_REWRITER, [&]() { - UnnestRewriter unnest_rewriter; + UnnestRewriter unnest_rewriter(context); plan = unnest_rewriter.Optimize(std::move(plan)); }); @@ -561,6 +565,7 @@ unique_ptr Optimizer::LowerMandatoryAggregateRewrites(unique_pt unique_ptr Optimizer::Optimize(unique_ptr plan_p) { plan_p = LowerMandatoryAggregateRewrites(std::move(plan_p)); if (!Settings::Get(context)) { + CTEFilterPusher::ClearDependencies(*plan_p); return plan_p; } Verify(*plan_p); @@ -578,6 +583,7 @@ unique_ptr Optimizer::Optimize(unique_ptr plan RunOptimizer(OptimizerType::EXTENSION, [&]() { OptimizerExtensionInput input {GetContext(), *this, pre_optimizer_extension.optimizer_info.get()}; if (pre_optimizer_extension.pre_optimize_function) { + CTEFilterPusher::ClearDependencies(*plan); pre_optimizer_extension.pre_optimize_function(input, plan); } }); diff --git a/src/duckdb/src/optimizer/outer_join_simplification.cpp b/src/duckdb/src/optimizer/outer_join_simplification.cpp index 35a06a3ba..e989c8dd9 100644 --- a/src/duckdb/src/optimizer/outer_join_simplification.cpp +++ b/src/duckdb/src/optimizer/outer_join_simplification.cpp @@ -391,7 +391,8 @@ void OuterJoinSimplification::VisitTopN(LogicalTopN &top_n, LogicalOperator &op) for (const auto &order_node : top_n.orders) { AddRequiredColumns(*order_node.expression); } - VisitOperatorChildren(op); + OuterJoinSimplification child_simplification(required_columns); + child_simplification.VisitOperator(*op.children[0]); } void OuterJoinSimplification::VisitAggregate(LogicalAggregate &aggregate, LogicalOperator &op) { @@ -446,9 +447,11 @@ void OuterJoinSimplification::VisitOperator(LogicalOperator &op) { VisitTopN(top_n, op); return; } - case LogicalOperatorType::LOGICAL_LIMIT: - VisitOperatorChildren(op); + case LogicalOperatorType::LOGICAL_LIMIT: { + OuterJoinSimplification child_simplification(required_columns); + child_simplification.VisitOperator(*op.children[0]); return; + } case LogicalOperatorType::LOGICAL_AGGREGATE_AND_GROUP_BY: { auto &aggregate = op.Cast(); VisitAggregate(aggregate, op); diff --git a/src/duckdb/src/optimizer/partial_aggregate_pushdown.cpp b/src/duckdb/src/optimizer/partial_aggregate_pushdown.cpp index cdabba289..e0ec4d75b 100644 --- a/src/duckdb/src/optimizer/partial_aggregate_pushdown.cpp +++ b/src/duckdb/src/optimizer/partial_aggregate_pushdown.cpp @@ -5,7 +5,7 @@ #include "duckdb/function/aggregate/distributive_functions.hpp" #include "duckdb/function/function_binder.hpp" #include "duckdb/function/scalar/generic_common.hpp" -#include "duckdb/optimizer/builtin_function_lookup.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/optimizer/optimizer.hpp" #include "duckdb/optimizer/relation_statistics/relation_statistics_extractor.hpp" #include "duckdb/planner/binder.hpp" diff --git a/src/duckdb/src/optimizer/projection_placement.cpp b/src/duckdb/src/optimizer/projection_placement.cpp index f754878fb..2329feb2c 100644 --- a/src/duckdb/src/optimizer/projection_placement.cpp +++ b/src/duckdb/src/optimizer/projection_placement.cpp @@ -481,7 +481,8 @@ static ProjectionRewrite RebuildProjection(Optimizer &optimizer, unique_ptr &root, LogicalProjection &projection, const column_binding_set_t &removed_bindings, vector &dependencies, - vector> &binding_replacements) { + vector> &binding_replacements, + optional_ptr &lowest_rebuilt) { auto projection_ptr = FindOperator(root, projection); if (!projection_ptr) { throw InternalException("Projection placement lost a projection while lifting an expression"); @@ -501,9 +502,12 @@ static ProjectionRewrite RebuildProjection(Optimizer &optimizer, if (expression->GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF) { auto &colref = expression->Cast(); if (colref.Depth() == 0) { + // the expressions still refer to the bindings of projections rebuilt below us: the binding replacer + // only runs once the whole placement is in place (see ApplyLiftPlacement) + auto current_binding = ResolveBinding(colref.Binding(), binding_replacements); for (idx_t dependency_idx = 0; dependency_idx < dependencies.size(); dependency_idx++) { if (!dependency_indexes[dependency_idx].IsValid() && - colref.Binding() == dependencies[dependency_idx].current_binding && + current_binding == dependencies[dependency_idx].current_binding && colref.GetReturnType() == dependencies[dependency_idx].type) { dependency_indexes[dependency_idx] = optional_idx(new_index); } @@ -532,10 +536,8 @@ static ProjectionRewrite RebuildProjection(Optimizer &optimizer, optional_ptr replacement_ptr = *replacement; *projection_ptr = std::move(replacement); - ColumnBindingReplacer replacer; for (auto &retained_binding : retained_bindings) { auto new_binding = ColumnBinding(table_index, ProjectionIndex(retained_binding.second)); - replacer.replacement_bindings.emplace_back(retained_binding.first, new_binding); binding_replacements.emplace_back(retained_binding.first, new_binding); CopyStatistics(statistics_map, retained_binding.first, new_binding); } @@ -545,9 +547,13 @@ static ProjectionRewrite RebuildProjection(Optimizer &optimizer, CopyStatistics(statistics_map, old_binding, new_binding); dependencies[dependency_idx].current_binding = new_binding; } - replacer.stop_operator = replacement_ptr; - replacer.VisitOperator(*root); - + if (!lowest_rebuilt) { + lowest_rebuilt = replacement_ptr; + } + // The references to the old bindings are replaced later, in one go, once the lifted expression has been placed: + // the projection maps of the operators above us are only made consistent with the new column layout step by + // step (RemapParentProjectionMap, ExposeBinding, and finally the restore projection), and walking the plan with + // a ColumnBindingReplacer before that resolves the column bindings of every operator through these stale maps. return {std::move(old_bindings), replacement_ptr->GetColumnBindings()}; } @@ -580,8 +586,9 @@ static void ApplyLiftPlacement(Optimizer &optimizer, column_binding_map_t lowest_rebuilt; + auto source_rewrite = RebuildProjection(optimizer, statistics_map, root, source, source_binding, dependencies, + binding_replacements, lowest_rebuilt); RemapParentProjectionMap(placement.path[0].op.get(), placement.path[0].child_index, source_rewrite.old_bindings, source_rewrite.new_bindings); @@ -590,7 +597,7 @@ static void ApplyLiftPlacement(Optimizer &optimizer, column_binding_map_t(), - step.result_bindings, dependencies, binding_replacements); + step.result_bindings, dependencies, binding_replacements, lowest_rebuilt); } else { for (auto &dependency : dependencies) { ExposeBinding(step.op.get(), step.child_index, dependency.current_binding); @@ -676,6 +683,19 @@ static void ApplyLiftPlacement(Optimizer &optimizer, column_binding_map_tResolveOperatorTypes(); auto old_bindings = target->GetColumnBindings(); - auto old_types = target->types; - - vector> lower_expressions; - lower_expressions.reserve(old_bindings.size() + group.placements.size()); - for (idx_t i = 0; i < old_bindings.size(); i++) { - lower_expressions.push_back(make_uniq(old_types[i], old_bindings[i])); - } + auto lower_index = optimizer.binder.GenerateTableIndex(); + auto lower_projection = LogicalProjection::CreateIdentity(lower_index, std::move(target)); for (auto &placement : group.placements) { - lower_expressions.push_back(std::move(placement.expression)); + lower_projection->expressions.push_back(std::move(placement.expression)); } - - auto lower_index = optimizer.binder.GenerateTableIndex(); - auto lower_projection = make_uniq(lower_index, std::move(lower_expressions)); - if (target->has_estimated_cardinality) { - lower_projection->SetEstimatedCardinality(target->estimated_cardinality); + if (lower_projection->children[0]->has_estimated_cardinality) { + lower_projection->SetEstimatedCardinality(lower_projection->children[0]->estimated_cardinality); } - lower_projection->children.push_back(std::move(target)); target = std::move(lower_projection); BindingReplacementGraph replacements; diff --git a/src/duckdb/src/optimizer/projection_pullup.cpp b/src/duckdb/src/optimizer/projection_pullup.cpp index 26152f5f5..4c2d01615 100644 --- a/src/duckdb/src/optimizer/projection_pullup.cpp +++ b/src/duckdb/src/optimizer/projection_pullup.cpp @@ -41,31 +41,20 @@ optional_ptr ProjectionPullup::FindParent(LogicalOperator &targ void ProjectionPullup::InsertProjectionBelowOp(unique_ptr &op, unique_ptr &child, bool stop_at_op) { if (child->type != LogicalOperatorType::LOGICAL_PROJECTION) { - child->ResolveOperatorTypes(); auto proj_index = optimizer.binder.GenerateTableIndex(); auto child_bindings = child->GetColumnBindings(); - const auto child_types = child->types; - const auto column_count = child_bindings.size(); - - vector> expressions; - expressions.reserve(column_count); - for (idx_t i = 0; i < column_count; i++) { - expressions.push_back(make_uniq(child_types[i], child_bindings[i])); - } ColumnBindingReplacer replacer; - for (idx_t col_idx = 0; col_idx < column_count; col_idx++) { + for (idx_t col_idx = 0; col_idx < child_bindings.size(); col_idx++) { const auto &old_binding = child_bindings[col_idx]; replacer.replacement_bindings.emplace_back(old_binding, ColumnBinding(proj_index, ProjectionIndex(col_idx))); } - auto new_projection = make_uniq(proj_index, std::move(expressions)); - if (child->has_estimated_cardinality) { - new_projection->SetEstimatedCardinality(child->estimated_cardinality); + auto new_projection = LogicalProjection::CreateIdentity(proj_index, std::move(child)); + if (new_projection->children[0]->has_estimated_cardinality) { + new_projection->SetEstimatedCardinality(new_projection->children[0]->estimated_cardinality); } - - new_projection->children.emplace_back(std::move(child)); child = std::move(new_projection); if (stop_at_op) { diff --git a/src/duckdb/src/optimizer/pullup/pullup_projection.cpp b/src/duckdb/src/optimizer/pullup/pullup_projection.cpp index c69e5535d..c9c940e3f 100644 --- a/src/duckdb/src/optimizer/pullup/pullup_projection.cpp +++ b/src/duckdb/src/optimizer/pullup/pullup_projection.cpp @@ -80,6 +80,13 @@ unique_ptr FilterPullup::PullupProjection(unique_ptrchildren[0] = Rewrite(std::move(op->children[0])); if (!filters_expr_pullup.empty()) { auto &proj = op->Cast(); + for (auto &expr : filters_expr_pullup) { + if (expr->CanThrow()) { + // Keep fallible predicates and their guards below the projection. + RevertFilterPullup(proj, filters_expr_pullup); + return op; + } + } // INTERSECT, EXCEPT, and DISTINCT if (!can_add_column) { // special treatment for operators that cannot add columns, e.g., INTERSECT, EXCEPT, and DISTINCT diff --git a/src/duckdb/src/optimizer/pushdown/pushdown_cross_product.cpp b/src/duckdb/src/optimizer/pushdown/pushdown_cross_product.cpp index b8a626121..84893ecf6 100644 --- a/src/duckdb/src/optimizer/pushdown/pushdown_cross_product.cpp +++ b/src/duckdb/src/optimizer/pushdown/pushdown_cross_product.cpp @@ -1,13 +1,60 @@ #include "duckdb/optimizer/filter_pushdown.hpp" #include "duckdb/optimizer/in_clause_rewriter.hpp" +#include "duckdb/planner/expression/bound_between_expression.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/expression/bound_comparison_expression.hpp" #include "duckdb/planner/operator/logical_comparison_join.hpp" #include "duckdb/planner/operator/logical_cross_product.hpp" #include "duckdb/planner/operator/logical_filter.hpp" +#include "duckdb/planner/operator/logical_empty_result.hpp" namespace duckdb { using Filter = FilterPushdown::Filter; +static bool HasTransitiveJoinBounds(const vector> &filters) { + unordered_set bound_tables; + for (auto &filter : filters) { + if (filter->bindings.size() != 1) { + continue; + } + auto &expr = *filter->filter; + bool has_bound = false; + if (BoundComparisonExpression::IsComparison(expr)) { + auto &comparison = expr.Cast(); + has_bound = BoundComparisonExpression::Left(comparison).IsFoldable() || + BoundComparisonExpression::Right(comparison).IsFoldable(); + } else if (expr.GetExpressionType() == ExpressionType::COMPARE_BETWEEN) { + auto &between = expr.Cast(); + has_bound = BoundBetweenExpression::LowerBound(between).IsFoldable() || + BoundBetweenExpression::UpperBound(between).IsFoldable(); + } + if (has_bound) { + bound_tables.insert(*filter->bindings.begin()); + } + } + if (bound_tables.empty()) { + return false; + } + for (auto &filter : filters) { + if (filter->bindings.size() < 2 || !BoundComparisonExpression::IsComparison(*filter->filter)) { + continue; + } + auto &comparison = filter->filter->Cast(); + auto type = comparison.GetExpressionType(); + if (type != ExpressionType::COMPARE_GREATERTHAN && type != ExpressionType::COMPARE_GREATERTHANOREQUALTO && + type != ExpressionType::COMPARE_LESSTHAN && type != ExpressionType::COMPARE_LESSTHANOREQUALTO) { + continue; + } + auto &right = BoundComparisonExpression::Right(comparison); + if (right.GetExpressionType() == ExpressionType::BOUND_COLUMN_REF && + bound_tables.count(right.Cast().Binding().table_index)) { + return true; + } + } + return false; +} + unique_ptr FilterPushdown::PushdownCrossProduct(unique_ptr op) { D_ASSERT(op->children.size() > 1); FilterPushdown left_pushdown(optimizer, convert_mark_joins, projection_mode); @@ -23,6 +70,13 @@ unique_ptr FilterPushdown::PushdownCrossProduct(unique_ptr left_bindings, right_bindings; if (!filters.empty()) { + // Recombine useful join bounds before distributing filters to the inputs. + if (HasTransitiveJoinBounds(filters)) { + if (PushFilters() == FilterResult::UNSATISFIABLE) { + return make_uniq(std::move(op)); + } + GenerateFilters(); + } // check to see into which side we should push the filters // first get the LHS and RHS bindings LogicalJoin::GetTableReferences(*op->children[0], left_bindings); diff --git a/src/duckdb/src/optimizer/pushdown/pushdown_get.cpp b/src/duckdb/src/optimizer/pushdown/pushdown_get.cpp index 9ec626d21..3109bd72f 100644 --- a/src/duckdb/src/optimizer/pushdown/pushdown_get.cpp +++ b/src/duckdb/src/optimizer/pushdown/pushdown_get.cpp @@ -121,7 +121,8 @@ unique_ptr FilterPushdown::PushdownGet(unique_ptr> expressions; expressions.reserve(filters.size()); @@ -144,9 +145,13 @@ unique_ptr FilterPushdown::PushdownGet(unique_ptr FilterPushdown::PushdownInnerJoin(unique_ptrtype) { + case LogicalOperatorType::LOGICAL_COMPARISON_JOIN: + case LogicalOperatorType::LOGICAL_ANY_JOIN: { + auto &result_join = result->Cast(); + result_join.left_projection_map = std::move(join.left_projection_map); + result_join.right_projection_map = std::move(join.right_projection_map); + break; + } + default: + break; + } + return result; } } // namespace duckdb diff --git a/src/duckdb/src/optimizer/pushdown/pushdown_secure_view.cpp b/src/duckdb/src/optimizer/pushdown/pushdown_secure_view.cpp index 301c05da7..e2f971af6 100644 --- a/src/duckdb/src/optimizer/pushdown/pushdown_secure_view.cpp +++ b/src/duckdb/src/optimizer/pushdown/pushdown_secure_view.cpp @@ -1,28 +1,57 @@ #include "duckdb/optimizer/filter_pushdown.hpp" #include "duckdb/optimizer/optimizer.hpp" #include "duckdb/planner/expression/expression_barrier.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/expression_iterator.hpp" #include "duckdb/planner/operator/logical_empty_result.hpp" #include "duckdb/planner/operator/logical_secure_view.hpp" namespace duckdb { +static bool RetainSecureViewFilter(unique_ptr &expression, const LogicalSecureView &view) { + if (expression->GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF) { + auto &column = expression->Cast(); + if (column.Depth() != 0 || view.output_bindings.size() != view.output_expressions.size()) { + return false; + } + for (idx_t i = 0; i < view.output_bindings.size(); i++) { + if (column.Binding() == view.output_bindings[i]) { + expression = view.output_expressions[i]->Copy(); + return true; + } + } + return false; + } + bool valid = true; + ExpressionIterator::EnumerateChildren( + *expression, [&](unique_ptr &child) { valid = RetainSecureViewFilter(child, view) && valid; }); + return valid; +} + unique_ptr FilterPushdown::PushdownSecureView(unique_ptr op) { D_ASSERT(op->type == LogicalOperatorType::LOGICAL_SECURE_VIEW); // filters can be pushed into a secure view, but only if evaluating them on rows that the view does not emit // cannot be observed from the outside. Expressions that can throw an error or have side effects are wrapped in // a barrier: those are never pushed past an operator inside the view that removes rows, and are always // evaluated after the filters they end up next to. - // + auto &secure_view = op->Cast(); // freeze the cardinality estimate of the view before anything is pushed into it - otherwise the estimate of the - // boundary node reports what the statistics of the view contents say about the caller's predicate - if (!filters.empty() && !op->has_estimated_cardinality) { + // boundary node reports what the statistics of the view contents say about the caller's predicate. Views whose + // statistics are exposed anyway do not need this - the caller can derive the same estimate above the boundary + if (!filters.empty() && !secure_view.propagate_statistics && !op->has_estimated_cardinality) { op->SetEstimatedCardinality(op->children[0]->EstimateCardinality(optimizer.GetContext())); } - auto &secure_view = op->Cast(); FilterPushdown child_pushdown(optimizer, convert_mark_joins, projection_mode); for (auto &f : filters) { auto expr = std::move(f->filter); + if (secure_view.has_source) { + auto source_filter = expr->Copy(); + if (!RetainSecureViewFilter(source_filter, secure_view)) { + source_filter.reset(); + } + secure_view.source_filters.push_back(std::move(source_filter)); + } // the operators inside the view are never shown - report the filter as part of the boundary node instead secure_view.pushed_filters.push_back(expr->ToString()); if (ExpressionBarrier::Required(*expr) && !ExpressionBarrier::Contains(*expr)) { diff --git a/src/duckdb/src/optimizer/pushdown/pushdown_semi_anti_join.cpp b/src/duckdb/src/optimizer/pushdown/pushdown_semi_anti_join.cpp index 9d4ba02b7..21009431e 100644 --- a/src/duckdb/src/optimizer/pushdown/pushdown_semi_anti_join.cpp +++ b/src/duckdb/src/optimizer/pushdown/pushdown_semi_anti_join.cpp @@ -39,8 +39,8 @@ unique_ptr FilterPushdown::PushdownSemiAntiJoin(unique_ptrchildren[0]); + // nothing to match against: every left row passes, keep the join's output layout + return LogicalJoin::PromoteJoinSide(std::move(op->children[0]), std::move(join.left_projection_map)); case JoinType::SEMI: return make_uniq(std::move(op)); default: diff --git a/src/duckdb/src/optimizer/relation_statistics/relation_statistics.cpp b/src/duckdb/src/optimizer/relation_statistics/relation_statistics.cpp index 7426e56e5..fbc8738a7 100644 --- a/src/duckdb/src/optimizer/relation_statistics/relation_statistics.cpp +++ b/src/duckdb/src/optimizer/relation_statistics/relation_statistics.cpp @@ -42,6 +42,24 @@ bool RelationStats::MatchesBindings(const vector &bindings) const return true; } +RelationColumnStatsLookup::RelationColumnStatsLookup(const RelationStats &stats) { + AddRelation(stats); +} + +void RelationColumnStatsLookup::AddRelation(const RelationStats &stats) { + for (auto &column : stats.columns) { + columns.emplace(column.binding, column); + } +} + +optional_ptr RelationColumnStatsLookup::Find(ColumnBinding binding) const { + auto entry = columns.find(binding); + if (entry == columns.end()) { + return nullptr; + } + return entry->second.get(); +} + void RelationStats::Verify(const vector &bindings) const { D_ASSERT(!stats_initialized || MatchesBindings(bindings)); } diff --git a/src/duckdb/src/optimizer/relation_statistics/relation_statistics_helper.cpp b/src/duckdb/src/optimizer/relation_statistics/relation_statistics_helper.cpp index f32bab0e8..b4a983207 100644 --- a/src/duckdb/src/optimizer/relation_statistics/relation_statistics_helper.cpp +++ b/src/duckdb/src/optimizer/relation_statistics/relation_statistics_helper.cpp @@ -71,6 +71,7 @@ optional RelationStatisticsHelper::ExtractProjectionStats(Logical result.stats_initialized = true; auto bindings = projection.GetColumnBindings(); D_ASSERT(bindings.size() == projection.expressions.size()); + RelationColumnStatsLookup child_columns(child_stats); for (idx_t expression_idx = 0; expression_idx < projection.expressions.size(); expression_idx++) { auto &expression = *projection.expressions[expression_idx]; auto expression_binding = GetChildColumnBinding(expression); @@ -78,7 +79,7 @@ optional RelationStatisticsHelper::ExtractProjectionStats(Logical if (expression_binding.expression_is_constant) { distinct_count = DistinctCount(MinValue(result.cardinality, 1), DistinctCountSource::EXACT); } else if (expression_binding.FoundColumnRef()) { - auto child_column = child_stats.GetColumnStats(expression_binding.child_binding); + auto child_column = child_columns.Find(expression_binding.child_binding); if (!child_column) { return {}; } @@ -108,6 +109,7 @@ idx_t RelationStatisticsHelper::EstimateDistinctCardinality(const vector RelationStatisticsHelper::ExtractAggregationStats(LogicalAggregate &aggregate, const RelationStats &child_stats) { + RelationColumnStatsLookup child_columns(child_stats); vector cardinality_counts; for (auto &grouping_set : aggregate.grouping_sets) { vector set_counts; @@ -116,7 +118,7 @@ optional RelationStatisticsHelper::ExtractAggregationStats(Logica if (group.GetExpressionClass() != ExpressionClass::BOUND_COLUMN_REF) { continue; } - auto column = child_stats.GetColumnStats(group.Cast().Binding()); + auto column = child_columns.Find(group.Cast().Binding()); if (!column) { return {}; } @@ -139,7 +141,7 @@ optional RelationStatisticsHelper::ExtractAggregationStats(Logica auto &group = *aggregate.groups[group_idx]; DistinctCount distinct_count(result.cardinality, DistinctCountSource::CARDINALITY); if (group.GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF) { - auto child_column = child_stats.GetColumnStats(group.Cast().Binding()); + auto child_column = child_columns.Find(group.Cast().Binding()); if (!child_column) { return {}; } @@ -168,8 +170,9 @@ optional RelationStatisticsHelper::ExtractWindowStats(LogicalWind result.cardinality = child_stats.cardinality; result.table_name = Identifier(window.GetName()); result.stats_initialized = true; + RelationColumnStatsLookup child_columns(child_stats); for (auto &binding : window.GetColumnBindings()) { - auto child_column = child_stats.GetColumnStats(binding); + auto child_column = child_columns.Find(binding); if (child_column) { result.columns.emplace_back(binding, child_column->distinct_count, child_column->name); } else if (binding.table_index == window.window_index) { @@ -183,10 +186,11 @@ optional RelationStatisticsHelper::ExtractWindowStats(LogicalWind return result; } -static optional GetDistinctTargetCount(Expression &target, const RelationStats &child_stats) { +static optional GetDistinctTargetCount(Expression &target, const RelationStats &child_stats, + const RelationColumnStatsLookup &child_columns) { switch (target.GetExpressionClass()) { case ExpressionClass::BOUND_COLUMN_REF: { - auto column = child_stats.GetColumnStats(target.Cast().Binding()); + auto column = child_columns.Find(target.Cast().Binding()); return column ? optional(column->distinct_count) : optional(); } case ExpressionClass::BOUND_REF: { @@ -213,8 +217,9 @@ optional RelationStatisticsHelper::ExtractDistinctStats(LogicalDi distinct_counts.push_back(column.distinct_count); } } else { + RelationColumnStatsLookup child_columns(child_stats); for (auto &target : distinct.distinct_targets) { - auto count = GetDistinctTargetCount(*target, child_stats); + auto count = GetDistinctTargetCount(*target, child_stats, child_columns); if (!count) { return result; } @@ -250,14 +255,16 @@ optional RelationStatisticsHelper::ProjectOutputStats(const Relat result.filter_strength = stats.filter_strength; result.stats_initialized = true; result.table_name = stats.table_name; - for (auto &binding : op.GetColumnBindings()) { - auto column = stats.GetColumnStats(binding); + RelationColumnStatsLookup lookup(stats); + auto bindings = op.GetColumnBindings(); + for (auto &binding : bindings) { + auto column = lookup.Find(binding); if (!column) { return {}; } result.columns.push_back(*column); } - result.Verify(op.GetColumnBindings()); + result.Verify(bindings); return result; } diff --git a/src/duckdb/src/optimizer/relation_statistics/relation_statistics_operator.cpp b/src/duckdb/src/optimizer/relation_statistics/relation_statistics_operator.cpp index f1ba72352..4ac81b70b 100644 --- a/src/duckdb/src/optimizer/relation_statistics/relation_statistics_operator.cpp +++ b/src/duckdb/src/optimizer/relation_statistics/relation_statistics_operator.cpp @@ -11,14 +11,12 @@ ProjectChildStats(LogicalOperator &op, const vector source; - for (auto &child : children) { - source = child.get().GetColumnStats(binding); - if (source) { - break; - } - } + auto source = child_columns.Find(binding); if (!source) { return {}; } @@ -47,11 +45,12 @@ static optional ExtractGetWithChildStats(LogicalGet &get, ClientC const RelationStats &child_stats) { auto result = RelationStatisticsHelper::ExtractGetStats(get, context); result.cardinality = child_stats.cardinality; + RelationColumnStatsLookup child_columns(child_stats); for (auto &binding : get.GetColumnBindings()) { if (binding.table_index == get.table_index) { continue; } - auto child_column = child_stats.GetColumnStats(binding); + auto child_column = child_columns.Find(binding); if (!child_column) { return {}; } @@ -67,8 +66,9 @@ static optional ExtractUnnestStats(LogicalOperator &op, const Rel result.cardinality = child_stats.cardinality; result.stats_initialized = true; result.table_name = Identifier(op.GetName()); + RelationColumnStatsLookup child_columns(child_stats); for (auto &binding : op.GetColumnBindings()) { - auto child_column = child_stats.GetColumnStats(binding); + auto child_column = child_columns.Find(binding); if (child_column) { result.columns.emplace_back(binding, child_column->distinct_count, child_column->name); } else if (binding.table_index == unnest.unnest_index) { @@ -97,8 +97,9 @@ static optional ExtractComparisonJoinStats(LogicalComparisonJoin mark_result.cardinality = cardinality; mark_result.stats_initialized = true; mark_result.table_name = Identifier(join.GetName()); + RelationColumnStatsLookup left_columns(child_stats[0].get()); for (auto &binding : join.GetColumnBindings()) { - auto column = child_stats[0].get().GetColumnStats(binding); + auto column = left_columns.Find(binding); if (column) { mark_result.columns.emplace_back(binding, column->distinct_count, column->name); } else if (binding.table_index == join.mark_index) { diff --git a/src/duckdb/src/optimizer/relation_statistics/relation_statistics_scan.cpp b/src/duckdb/src/optimizer/relation_statistics/relation_statistics_scan.cpp index 049f9a713..566a2400e 100644 --- a/src/duckdb/src/optimizer/relation_statistics/relation_statistics_scan.cpp +++ b/src/duckdb/src/optimizer/relation_statistics/relation_statistics_scan.cpp @@ -133,7 +133,7 @@ DistinctCount RelationStatisticsHelper::GetDistinctCount(LogicalGet &get, Client RelationStats RelationStatisticsHelper::ExtractGetStats(LogicalGet &get, ClientContext &context) { RelationStats result; - auto base_table_cardinality = get.EstimateCardinality(context); + auto base_table_cardinality = get.EstimateSourceCardinality(context); auto cardinality_after_filters = base_table_cardinality; result.table_name = get.GetTable() ? get.GetTable()->name : Identifier(get.GetName()); @@ -143,6 +143,11 @@ RelationStats RelationStatisticsHelper::ExtractGetStats(LogicalGet &get, ClientC auto &column_index = get.GetColumnIndex(entry.GetIndex()); auto column_statistics = GetColumnStatistics(get, context, column_index); if (column_statistics) { + auto &filter = ExpressionFilter::GetExpressionFilter(entry.Filter(), "ExtractGetStats"); + if (filter.column_indexes.size() <= 1 && + filter.CheckStatistics(context, *column_statistics) == FilterPropagateResult::FILTER_ALWAYS_TRUE) { + continue; + } cardinality_after_filters = MinValue(cardinality_after_filters, InspectTableFilter(base_table_cardinality, entry.Filter(), *column_statistics)); diff --git a/src/duckdb/src/optimizer/remote_pushdown_optimizer.cpp b/src/duckdb/src/optimizer/remote_pushdown_optimizer.cpp index 287b4f1ea..969389add 100644 --- a/src/duckdb/src/optimizer/remote_pushdown_optimizer.cpp +++ b/src/duckdb/src/optimizer/remote_pushdown_optimizer.cpp @@ -49,7 +49,7 @@ #include "duckdb/parser/expression/type_expression.hpp" #include "duckdb/parser/expression/window_expression.hpp" #include "duckdb/parser/tableref/table_function_ref.hpp" -#include "duckdb/common/extra_type_info.hpp" +#include "duckdb/common/logical_type_info.hpp" #include "duckdb/function/table_function.hpp" #include "duckdb/parser/query_node/recursive_cte_node.hpp" #include "duckdb/planner/expression_binder/constant_binder.hpp" @@ -1360,15 +1360,16 @@ void RemotePushdownOptimizer::StripCatalogName(TableRef &ref, const Identifier & void RemotePushdownOptimizer::StripCatalogName(ParsedExpression &expr, const Identifier &catalog_name) { if (expr.GetExpressionClass() == ExpressionClass::COLUMN_REF) { auto &col_ref = expr.Cast(); - // Strip catalog prefix from qualified column references, normalising to exactly table.col (2 parts). + // Strip the catalog prefix from qualified column references, keeping everything after it. // Require at least 3 names: a 2-part ref like "rpc.field" is either table.col or struct-column.field — // not catalog-qualified — so stripping would be wrong. - // For 3-part catalog.table.col → table.col (one level stripped) - // For 4-part catalog.schema.table.col → table.col (catalog + schema stripped) + // For 3-part catalog.table.col → table.col + // For 4-part catalog.schema.table.col → schema.table.col + // Only the catalog is dropped: the parser keeps arbitrarily deep refs in a single ColumnRef, so trailing + // names may be struct fields (catalog.table.s.a.b → table.s.a.b) and must be preserved. if (col_ref.ColumnNames().size() >= 3 && col_ref.ColumnNames()[0] == catalog_name) { - Identifier table_name = col_ref.ColumnNames()[col_ref.ColumnNames().size() - 2]; - Identifier col_name = col_ref.ColumnNames()[col_ref.ColumnNames().size() - 1]; - col_ref.ColumnNamesMutable() = {std::move(table_name), std::move(col_name)}; + auto &names = col_ref.ColumnNamesMutable(); + names.erase(names.begin()); } return; } diff --git a/src/duckdb/src/optimizer/remove_duplicate_groups.cpp b/src/duckdb/src/optimizer/remove_duplicate_groups.cpp index c0ab697ba..ef7363cf5 100644 --- a/src/duckdb/src/optimizer/remove_duplicate_groups.cpp +++ b/src/duckdb/src/optimizer/remove_duplicate_groups.cpp @@ -246,9 +246,9 @@ void RemoveDuplicateGroups::VisitAggregate(LogicalAggregate &aggr) { return; } - // This optimizer should run before statistics propagation, so this should be empty - // If it runs after, then group_stats should be updated too - D_ASSERT(aggr.group_stats.empty()); + // Earlier statistics (e.g. from the filter simplification) no longer match the groups - the statistics + // propagation that runs after this optimizer computes them again + aggr.group_stats.clear(); const idx_t num_original_groups = aggr.groups.size(); const idx_t num_aggregate_outputs = aggr.expressions.size(); diff --git a/src/duckdb/src/optimizer/remove_redundant_order_keys.cpp b/src/duckdb/src/optimizer/remove_redundant_order_keys.cpp index 7f5c65f98..733b53233 100644 --- a/src/duckdb/src/optimizer/remove_redundant_order_keys.cpp +++ b/src/duckdb/src/optimizer/remove_redundant_order_keys.cpp @@ -1,6 +1,7 @@ #include "duckdb/optimizer/remove_redundant_order_keys.hpp" #include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp" +#include "duckdb/common/algorithm.hpp" #include "duckdb/parser/constraints/not_null_constraint.hpp" #include "duckdb/parser/constraints/unique_constraint.hpp" #include "duckdb/planner/column_binding_map.hpp" diff --git a/src/duckdb/src/optimizer/remove_unused_columns.cpp b/src/duckdb/src/optimizer/remove_unused_columns.cpp index bea859ff6..5e1c46045 100644 --- a/src/duckdb/src/optimizer/remove_unused_columns.cpp +++ b/src/duckdb/src/optimizer/remove_unused_columns.cpp @@ -5,7 +5,7 @@ #include "duckdb/function/aggregate/distributive_functions.hpp" #include "duckdb/function/function_binder.hpp" #include "duckdb/function/scalar/struct_functions.hpp" -#include "duckdb/optimizer/builtin_function_lookup.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/parser/parsed_data/vacuum_info.hpp" #include "duckdb/planner/binder.hpp" #include "duckdb/planner/column_binding_map.hpp" @@ -25,8 +25,10 @@ #include "duckdb/planner/operator/logical_projection.hpp" #include "duckdb/planner/operator/logical_recursive_cte.hpp" #include "duckdb/planner/operator/logical_set_operation.hpp" +#include "duckdb/planner/operator/logical_secure_view.hpp" #include "duckdb/planner/operator/logical_cte.hpp" #include "duckdb/planner/operator/logical_cteref.hpp" +#include "duckdb/planner/operator/logical_window.hpp" #include "duckdb/function/scalar/struct_utils.hpp" #include "duckdb/function/scalar/variant_utils.hpp" #include "duckdb/function/scalar/nested_functions.hpp" @@ -34,6 +36,7 @@ #include "duckdb/optimizer/optimizer.hpp" #include "duckdb/planner/logical_operator_repeatability.hpp" #include "duckdb/planner/subquery/column_binding_layout.hpp" +#include "duckdb/planner/expression_iterator.hpp" #include namespace duckdb { @@ -135,6 +138,7 @@ void RemoveUnusedColumns::ApplyRecursiveProjections(LogicalRecursiveCTE &rec, entries.push_back(i); } ClearUnusedExpressions(entries, rec.table_index); + ApplySecureViewReplacements(); for (auto &child : rec.children) { child->ResolveOperatorTypes(); @@ -192,7 +196,9 @@ bool RemoveUnusedColumns::TryPruneRecursiveCTE(LogicalRecursiveCTE &rec) { } for (auto &replacement : reference_replacements) { root.projection_map_replacements[replacement.old_binding] = {replacement.new_binding}; + RecordSecureViewReplacement(replacement.old_binding, replacement.new_binding); } + ApplySecureViewReplacements(); return true; } @@ -224,26 +230,31 @@ idx_t BaseColumnPruner::ReplaceBinding(ColumnBinding current_binding, ColumnBind template void RemoveUnusedColumns::ClearUnusedExpressions(vector &list, TableIndex table_idx, bool replace) { D_ASSERT(mode == RemoveUnusedColumnsMode::APPLY); - idx_t offset = 0; idx_t new_col_idx = 0; + // entries that are referred to are moved to the front of the list, the rest is erased at the end + idx_t kept_count = 0; for (idx_t col_idx = 0; col_idx < list.size(); col_idx++) { - auto current_binding = ColumnBinding(table_idx, ProjectionIndex(col_idx + offset)); + auto current_binding = ColumnBinding(table_idx, ProjectionIndex(col_idx)); + if (replace && !root.active_secure_views.empty()) { + root.secure_view_replacements[current_binding].clear(); + } auto entry = column_references.find(current_binding); if (entry == column_references.end()) { if (replace) { root.projection_map_replacements[current_binding].clear(); } // this entry is not referred to, erase it from the set of expressions - list.erase_at(col_idx); - offset++; - col_idx--; continue; } + if (kept_count != col_idx) { + list[kept_count] = std::move(list[col_idx]); + } + kept_count++; if (!replace) { continue; } bool should_replace = false; - if (col_idx + offset != new_col_idx) { + if (col_idx != new_col_idx) { should_replace = true; } if (!entry->second.child_columns.empty() && @@ -260,11 +271,17 @@ void RemoveUnusedColumns::ClearUnusedExpressions(vector &list, TableIndex tab for (idx_t binding_idx = 0; binding_idx < created_bindings; binding_idx++) { map_replacements.emplace_back(table_idx, ProjectionIndex(new_col_idx + binding_idx)); } + if (entry->second.child_columns.empty() || + entry->second.supports_pushdown_extract != PushdownExtractSupport::ENABLED) { + RecordSecureViewReplacement(current_binding, new_binding); + } new_col_idx += created_bindings; } else { + RecordSecureViewReplacement(current_binding, current_binding); new_col_idx++; } } + list.erase(list.begin() + NumericCast(kept_count), list.end()); } enum class ProjectionBindingResolution : uint8_t { SELECTED, REPLACEMENT }; @@ -349,10 +366,182 @@ void RemoveUnusedColumns::VisitPrunableChildren(LogicalOperator &op) { } } +static idx_t ReplaceSecureViewSource(unique_ptr &expression, const ColumnBinding &binding, + const Expression &source_expression, bool &has_other_binding) { + if (expression->GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF) { + auto &column = expression->Cast(); + if (column.Binding() != binding || column.Depth() != 0) { + has_other_binding = true; + return 0; + } + expression = source_expression.Copy(); + return 1; + } + idx_t replacements = 0; + ExpressionIterator::EnumerateChildren(*expression, [&](unique_ptr &child) { + replacements += ReplaceSecureViewSource(child, binding, source_expression, has_other_binding); + }); + return replacements; +} + +static unique_ptr ComposeSecureViewSource(const Expression &expression, const ColumnBinding &binding, + const Expression &source_expression) { + auto result = expression.Copy(); + bool has_other_binding = false; + auto replacements = ReplaceSecureViewSource(result, binding, source_expression, has_other_binding); + if (has_other_binding || + (replacements != 1 && expression.GetExpressionClass() != ExpressionClass::BOUND_CONSTANT)) { + return nullptr; + } + return result; +} + +void RemoveUnusedColumns::RecordSecureViewReplacement(ColumnBinding old_binding, ColumnBinding new_binding, + unique_ptr expression) { + if (root.active_secure_views.empty()) { + return; + } + auto &columns = root.secure_view_replacements[old_binding]; + for (auto &column : columns) { + if (column.binding == new_binding && + ((!column.expression && !expression) || + (column.expression && expression && column.expression->Equals(*expression)))) { + return; + } + } + columns.push_back({new_binding, std::move(expression)}); +} + +void RemoveUnusedColumns::ApplySecureViewReplacements() { + if (root.secure_view_replacements.empty()) { + return; + } + for (auto &view_ref : root.active_secure_views) { + auto &view = view_ref.get(); + vector bindings; + vector> expressions; + bool valid = true; + for (idx_t i = 0; valid && i < view.output_bindings.size(); i++) { + auto binding = view.output_bindings[i]; + auto replacement = root.secure_view_replacements.find(binding); + if (replacement == root.secure_view_replacements.end()) { + bindings.push_back(binding); + expressions.push_back(std::move(view.output_expressions[i])); + continue; + } + for (auto &column : replacement->second) { + auto source = column.expression + ? ComposeSecureViewSource(*column.expression, binding, *view.output_expressions[i]) + : view.output_expressions[i]->Copy(); + if (!source) { + valid = false; + break; + } + bindings.push_back(column.binding); + expressions.push_back(std::move(source)); + } + } + if (!valid) { + bindings.clear(); + expressions.clear(); + } + view.output_bindings = std::move(bindings); + view.output_expressions = std::move(expressions); + } + root.secure_view_replacements.clear(); +} + +void RemoveUnusedColumns::VisitSecureView(LogicalSecureView &view) { + D_ASSERT(mode == RemoveUnusedColumnsMode::APPLY); + D_ASSERT(!everything_referenced); + D_ASSERT(view.children.size() == 1); + D_ASSERT(view.output_bindings.size() == view.output_expressions.size()); + + root.active_secure_views.push_back(view); + VisitPrunableChildren(view); + root.active_secure_views.pop_back(); + view.ResolveOperatorTypes(); + + auto new_bindings = view.children[0]->GetColumnBindings(); + auto &new_types = view.children[0]->types; + vector> new_expressions; + new_expressions.reserve(new_bindings.size()); + for (idx_t output_idx = 0; output_idx < new_bindings.size(); output_idx++) { + unique_ptr source_expression; + for (idx_t i = 0; i < view.output_bindings.size(); i++) { + if (view.output_bindings[i] != new_bindings[output_idx] || + !view.output_expressions[i]->GetReturnType().EqualsIncludingCollation(new_types[output_idx])) { + continue; + } + if (source_expression && !source_expression->Equals(*view.output_expressions[i])) { + view.output_bindings.clear(); + view.output_expressions.clear(); + return; + } + source_expression = view.output_expressions[i]->Copy(); + } + if (!source_expression) { + view.output_bindings.clear(); + view.output_expressions.clear(); + return; + } + new_expressions.push_back(std::move(source_expression)); + } + view.output_bindings = std::move(new_bindings); + view.output_expressions = std::move(new_expressions); +} + void RemoveUnusedColumns::VisitOperator(unique_ptr &op_ref) { const bool analyze = mode == RemoveUnusedColumnsMode::ANALYZE; auto &op = *op_ref; switch (op.type) { + case LogicalOperatorType::LOGICAL_WINDOW: { + auto &window = op.Cast(); + // Preserve volatile evaluation within the window expression, even when its output is unused. + // Column references do not propagate volatility from their producers. + if (!everything_referenced) { + for (idx_t i = 0; i < window.expressions.size(); i++) { + if (window.expressions[i]->IsVolatile()) { + column_references.emplace(ColumnBinding(window.window_index, ProjectionIndex(i)), + ReferencedColumn()); + } + } + } + if (analyze) { + // Windows also pass through child columns, so retain the existing references. + for (idx_t i = 0; i < window.expressions.size(); i++) { + auto binding = ColumnBinding(window.window_index, ProjectionIndex(i)); + if (everything_referenced || column_references.find(binding) != column_references.end()) { + VisitExpression(&window.expressions[i]); + } + } + VisitOperator(window.children[0]); + return; + } + if (!everything_referenced) { + ClearUnusedExpressions(window.expressions, window.window_index); + ApplySecureViewReplacements(); + } + if (window.expressions.empty()) { + // A window with no remaining outputs preserves its child's rows and bindings. + auto child = std::move(window.children[0]); + op_ref = std::move(child); + VisitOperator(op_ref); + return; + } + VisitOperatorExpressions(window); + VisitPrunableChildren(window); + return; + } + case LogicalOperatorType::LOGICAL_SECURE_VIEW: { + auto &view = op.Cast(); + if (!analyze && !everything_referenced && view.has_source && + view.output_bindings.size() == view.output_expressions.size()) { + VisitSecureView(view); + return; + } + break; + } case LogicalOperatorType::LOGICAL_AGGREGATE_AND_GROUP_BY: { // aggregate auto &aggr = op.Cast(); @@ -373,6 +562,7 @@ void RemoveUnusedColumns::VisitOperator(unique_ptr &op_ref) { if (!everything_referenced) { // FIXME: groups that are not referenced need to stay -> but they don't need to be scanned and output! ClearUnusedExpressions(aggr.expressions, aggr.aggregate_index); + ApplySecureViewReplacements(); if (aggr.expressions.empty() && aggr.groups.empty()) { // removed all expressions from the aggregate: push a COUNT(*) auto count_star_fun = GetBuiltinAggregateFunction(context, CountStarFun::Name, {}); @@ -486,6 +676,7 @@ void RemoveUnusedColumns::VisitOperator(unique_ptr &op_ref) { } ClearUnusedExpressions(entries, setop.table_index); if (entries.size() >= setop.column_count) { + ApplySecureViewReplacements(); // We still need to recurse into the children to populate CTE info, etc. for (auto &child : op.children) { RemoveUnusedColumns remove(*this, true); @@ -498,7 +689,10 @@ void RemoveUnusedColumns::VisitOperator(unique_ptr &op_ref) { // no columns referenced: this happens in the case of a COUNT(*) // extract the first column entries.push_back(0); + auto binding = ColumnBinding(setop.table_index, ProjectionIndex(0)); + RecordSecureViewReplacement(binding, binding); } + ApplySecureViewReplacements(); for (idx_t child_idx = 0; child_idx < op.children.size(); child_idx++) { RemoveUnusedColumns remove(*this, true); auto &child = op.children[child_idx]; @@ -568,7 +762,10 @@ void RemoveUnusedColumns::VisitOperator(unique_ptr &op_ref) { // this happens in the case of e.g. EXISTS(SELECT * FROM ...) // in this case we only need to project a single constant proj.expressions.push_back(make_uniq(Value::INTEGER(42))); + auto binding = ColumnBinding(proj.table_index, ProjectionIndex(0)); + RecordSecureViewReplacement(binding, binding, proj.expressions[0]->Copy()); } + ApplySecureViewReplacements(); } // then recurse into the children of this projection RemoveUnusedColumns remove(*this, false); @@ -619,11 +816,13 @@ void RemoveUnusedColumns::VisitOperator(unique_ptr &op_ref) { // need to implicitly reference everything. break; } - // distinct, all projected columns are used for the DISTINCT computation - // mark all columns as used and continue to the children - // FIXME: DISTINCT with expression list does not implicitly reference everything + // Preserve all DISTINCT inputs without affecting sibling branches. + const auto previous_everything_referenced = everything_referenced; everything_referenced = true; - break; + LogicalOperatorVisitor::VisitOperatorExpressions(op); + VisitPrunableChildren(op); + everything_referenced = previous_everything_referenced; + return; } case LogicalOperatorType::LOGICAL_RECURSIVE_CTE: { if (analyze) { @@ -746,8 +945,11 @@ void RemoveUnusedColumns::VisitOperator(unique_ptr &op_ref) { cte_ref_pruner.VisitOperator(*cte.children[1]); for (auto &replacement : cte_ref_pruner.binding_replacements) { root.projection_map_replacements[replacement.old_binding] = {replacement.new_binding}; + RecordSecureViewReplacement(replacement.old_binding, replacement.new_binding); } + ApplySecureViewReplacements(); + // We also need to rewrite the column bindings in the right-hand side of the CTE to account for the removed // columns on the left-hand side. Conveniently, the CTERefPruner already has the information about which // columns were removed, so we can reuse it for the column binding replacement. @@ -896,6 +1098,8 @@ void RemoveUnusedColumns::WritePushdownExtractColumns( auto return_type = expr->GetReturnType(); auto &colref = col.bindings[struct_extract.bindings_idx]; + auto old_binding = colref.get().Binding(); + auto source = root.active_secure_views.empty() ? nullptr : expr->Copy(); auto colref_copy = colref.get().Copy(); expr = std::move(colref_copy); auto &new_expr = expr->Cast(); @@ -903,6 +1107,7 @@ void RemoveUnusedColumns::WritePushdownExtractColumns( auto column_index = callback(*entry, component.cast ? &(*component.cast)->GetReturnType() : nullptr); new_expr.BindingMutable().column_index = column_index; + RecordSecureViewReplacement(old_binding, new_expr.Binding(), std::move(source)); } } @@ -1081,6 +1286,7 @@ void RemoveUnusedColumns::RemoveColumnsFromLogicalColumnDataGet(LogicalColumnDat auto column_ids = get.GetColumnIds(); ClearUnusedExpressions(column_ids, get.table_index); + ApplySecureViewReplacements(); get.SetColumnIds(std::move(column_ids)); } @@ -1135,20 +1341,25 @@ void RemoveUnusedColumns::RemoveColumnsFromLogicalGet(LogicalGet &get, unique_pt filter_expr = std::move(column_ref); } filter_expressions.push_back(std::move(filter_expr)); + } + for (auto &filter_expression : filter_expressions) { //! Now visit the filter to add to the 'column_references' - VisitExpression(&filter_expressions.back()); + VisitExpression(&filter_expression); } + //! The 'column_references' hold references to these expressions, so they have to stay alive + vector> multi_filter_columns; for (const auto &filter : get.table_filters.GetMultiColumnFilters()) { const auto &expression_filter = ExpressionFilter::GetExpressionFilter(*filter, "RemoveUnusedColumns::VisitGet"); for (const auto &filter_idx : expression_filter.column_indexes) { const auto &col_id = get.GetColumnIndex(filter_idx); auto column_type = get.GetColumnType(col_id); ColumnBinding filter_binding(get.table_index, filter_idx); - unique_ptr column_ref = - make_uniq(std::move(column_type), filter_binding); - VisitExpression(&column_ref); + multi_filter_columns.push_back(make_uniq(std::move(column_type), filter_binding)); } } + for (auto &column_ref : multi_filter_columns) { + VisitExpression(&column_ref); + } //! Check with the LogicalGet whether pushdown-extract is supported CheckPushdownExtract(get); @@ -1211,6 +1422,7 @@ void RemoveUnusedColumns::RemoveColumnsFromLogicalGet(LogicalGet &get, unique_pt original_ids.emplace_back(any_column); new_column_ids.emplace_back(any_column); } + ApplySecureViewReplacements(); get.SetColumnIds(std::move(new_column_ids)); // remap table filters so they point towards the new set of ids @@ -1452,6 +1664,15 @@ bool BaseColumnPruner::HandleExtractRecursive(unique_ptr &expr_p, } } +//! A cast pushed into the scan of a shredded VARIANT is applied to the shredded value instead of the VARIANT. For a +//! JSON target the two differ: a VARIANT serializes to JSON, a shredded VARCHAR would be parsed as JSON. +static bool ExtractCastCanBePushedDown(const BoundColumnRefExpression &colref, const Expression &cast) { + if (colref.GetReturnType().id() != LogicalTypeId::VARIANT) { + return true; + } + return !cast.GetReturnType().IsJSONType(); +} + bool BaseColumnPruner::HandleExtractExpression(unique_ptr *expression, optional_ptr> cast_expression) { optional_ptr colref; @@ -1462,6 +1683,10 @@ bool BaseColumnPruner::HandleExtractExpression(unique_ptr *expressio if (!HandleExtractRecursive(*expression, colref, path_ref, expressions)) { return false; } + if (cast_expression && !ExtractCastCanBePushedDown(*colref, **cast_expression)) { + // the cast stays on top of the pushed-down extract + cast_expression = nullptr; + } if (cast_expression) { auto &top_level = expressions.back(); top_level.cast = cast_expression; @@ -1603,15 +1828,25 @@ static bool TryGetCastChild(unique_ptr &expr, optional_ptr *expression) { - //! Check if this is a struct extract wrapped in a cast + if ((*expression)->GetExpressionType() == ExpressionType::OPERATOR_TRY) { + const auto was_inside_try = inside_try; + inside_try = true; + LogicalOperatorVisitor::VisitExpression(expression); + inside_try = was_inside_try; + return; + } + + //! Check if this is a struct extract wrapped in a cast. TRY_CAST is already + //! skipped by TryGetCastChild. A throwing CAST under TRY must also stay put: + //! unused-columns would otherwise emit CAST(struct_extract) in a child + //! projection and leave TRY wrapping only the already-cast column (#25236). optional_ptr> cast_child; - if (TryGetCastChild(*expression, cast_child)) { + if (!inside_try && TryGetCastChild(*expression, cast_child)) { if (HandleExtractExpression(cast_child.get(), expression)) { // already handled return; diff --git a/src/duckdb/src/optimizer/row_group_pruner.cpp b/src/duckdb/src/optimizer/row_group_pruner.cpp index 9488dbd21..a93b90e9d 100644 --- a/src/duckdb/src/optimizer/row_group_pruner.cpp +++ b/src/duckdb/src/optimizer/row_group_pruner.cpp @@ -188,6 +188,9 @@ RowGroupPruner::CreateRowGroupReordererOptions(const optional_idx row_limit, con order_by, column_type, order_type, null_order, storage_index, row_offset.GetIndex(), partition_stats); if (offset_puning_result.pruned_row_group_count > 0 || offset_puning_result.leading_null_group_offset > 0) { // We can prune row groups and/or reduce the offset by consuming definite NULL-only groups + if (!logical_limit.unpruned_offset.IsValid()) { + logical_limit.unpruned_offset = row_offset; + } logical_limit.offset_val = BoundLimitNode::ConstantValue(NumericCast(offset_puning_result.offset_remainder)); diff --git a/src/duckdb/src/optimizer/rule/arithmetic_simplification.cpp b/src/duckdb/src/optimizer/rule/arithmetic_simplification.cpp index 64e59b445..6baf2f443 100644 --- a/src/duckdb/src/optimizer/rule/arithmetic_simplification.cpp +++ b/src/duckdb/src/optimizer/rule/arithmetic_simplification.cpp @@ -15,12 +15,11 @@ ArithmeticSimplificationRule::ArithmeticSimplificationRule(ExpressionRewriter &r op->matchers.push_back(make_uniq()); op->matchers.push_back(make_uniq()); op->policy = SetMatcher::Policy::SOME; - // we only match on simple arithmetic expressions (+, -, *, /) - op->function = make_uniq(identifier_set_t {"+", "-", "*", "//"}); + op->function = make_uniq(identifier_set_t {"+", "-", "*", "/", "//"}); // and only with numeric results - op->type = make_uniq(); - op->matchers[0]->type = make_uniq(); - op->matchers[1]->type = make_uniq(); + op->type = make_uniq(); + op->matchers[0]->type = make_uniq(); + op->matchers[1]->type = make_uniq(); root = std::move(op); } @@ -36,22 +35,36 @@ unique_ptr ArithmeticSimplificationRule::Apply(LogicalOperator &op, return make_uniq(Value(root.GetReturnType())); } auto &func_name = root.Function().GetName(); + if (func_name == "/") { + if (constant_child == 1 && constant.GetValue() == 1 && + (root.GetChildren()[0]->GetReturnType().id() == LogicalTypeId::FLOAT || + root.GetChildren()[0]->GetReturnType().id() == LogicalTypeId::DOUBLE)) { + return std::move(root.GetChildrenMutable()[0]); + } + return nullptr; + } + if (!root.GetReturnType().IsIntegral()) { + return nullptr; + } if (func_name == "+") { if (constant.GetValue() == 0) { // addition with 0 // we can remove the entire operator and replace it with the non-constant child - return std::move(root.GetChildrenMutable()[1 - constant_child]); + return Expression::PreserveReturnType(root.GetReturnType(), + std::move(root.GetChildrenMutable()[1 - constant_child])); } } else if (func_name == "-") { if (constant_child == 1 && constant.GetValue() == 0) { // subtraction by 0 // we can remove the entire operator and replace it with the non-constant child - return std::move(root.GetChildrenMutable()[1 - constant_child]); + return Expression::PreserveReturnType(root.GetReturnType(), + std::move(root.GetChildrenMutable()[1 - constant_child])); } } else if (func_name == "*") { if (constant.GetValue() == 1) { // multiply with 1, replace with non-constant child - return std::move(root.GetChildrenMutable()[1 - constant_child]); + return Expression::PreserveReturnType(root.GetReturnType(), + std::move(root.GetChildrenMutable()[1 - constant_child])); } else if (constant.GetValue() == 0) { // multiply by zero: replace with constant or null return ExpressionRewriter::ConstantOrNull(GetContext(), @@ -62,8 +75,9 @@ unique_ptr ArithmeticSimplificationRule::Apply(LogicalOperator &op, if (constant_child == 1) { if (constant.GetValue() == 1) { // divide by 1, replace with non-constant child - return std::move(root.GetChildrenMutable()[1 - constant_child]); - } else if (constant.GetValue() == 0 && Settings::Get(rewriter.context)) { + return Expression::PreserveReturnType(root.GetReturnType(), + std::move(root.GetChildrenMutable()[1 - constant_child])); + } else if (constant.GetValue() == 0 && !Settings::Get(rewriter.context)) { // divide by 0, replace with NULL return make_uniq(Value(root.GetReturnType())); } diff --git a/src/duckdb/src/optimizer/rule/case_simplification.cpp b/src/duckdb/src/optimizer/rule/case_simplification.cpp index 4e2568a3c..290e97a76 100644 --- a/src/duckdb/src/optimizer/rule/case_simplification.cpp +++ b/src/duckdb/src/optimizer/rule/case_simplification.cpp @@ -19,7 +19,10 @@ unique_ptr CaseSimplificationRule::Apply(LogicalOperator &op, vector if (case_check.when_expr->IsFoldable()) { // the WHEN check is a foldable expression // use an ExpressionExecutor to execute the expression - auto constant_value = ExpressionExecutor::EvaluateScalar(GetContext(), *case_check.when_expr); + Value constant_value; + if (!ExpressionExecutor::TryEvaluateScalar(GetContext(), *case_check.when_expr, constant_value)) { + continue; + } // fold based on the constant condition auto condition = constant_value.DefaultCastAs(LogicalType::BOOLEAN); @@ -40,7 +43,7 @@ unique_ptr CaseSimplificationRule::Apply(LogicalOperator &op, vector } if (root.CaseChecksMutable().empty()) { // no case checks left: return the ELSE expression - return std::move(root.ElseMutable()); + return Expression::PreserveReturnType(root.GetReturnType(), std::move(root.ElseMutable())); } return nullptr; } diff --git a/src/duckdb/src/optimizer/rule/comparison_simplification.cpp b/src/duckdb/src/optimizer/rule/comparison_simplification.cpp index 5e68afab0..53de7616b 100644 --- a/src/duckdb/src/optimizer/rule/comparison_simplification.cpp +++ b/src/duckdb/src/optimizer/rule/comparison_simplification.cpp @@ -16,7 +16,11 @@ static bool DateTimestampComparisonIsInvertible(ClientContext &context, BoundFun BoundFunctionExpression &cast_expression, const Value &constant_value, Value &cast_constant, bool column_ref_left, unique_ptr &replacement) { - if (Timestamp::GetTime(constant_value.GetValue()) == dtime_t(0)) { + auto timestamp = constant_value.GetValue(); + if (!timestamp.IsFinite()) { + return false; + } + if (Timestamp::GetTime(timestamp) == dtime_t(0)) { return true; // it's midnight: no replacement needed } auto op = expr.GetExpressionType(); @@ -73,6 +77,34 @@ static bool DateTimestampComparisonIsInvertible(ClientContext &context, BoundFun return true; } +static bool IsLosslessIntegralToFloatingCast(const LogicalType &source_type, const LogicalType &target_type) { + if (target_type.id() == LogicalTypeId::DOUBLE) { + switch (source_type.id()) { + case LogicalTypeId::TINYINT: + case LogicalTypeId::SMALLINT: + case LogicalTypeId::INTEGER: + case LogicalTypeId::UTINYINT: + case LogicalTypeId::USMALLINT: + case LogicalTypeId::UINTEGER: + return true; + default: + return false; + } + } + if (target_type.id() == LogicalTypeId::FLOAT) { + switch (source_type.id()) { + case LogicalTypeId::TINYINT: + case LogicalTypeId::SMALLINT: + case LogicalTypeId::UTINYINT: + case LogicalTypeId::USMALLINT: + return true; + default: + return false; + } + } + return false; +} + static bool ConstantCastIsInvertible(ClientContext &context, BoundFunctionExpression &expr, BoundFunctionExpression &cast_expression, const Value &constant_value, Value &cast_constant, const LogicalType &target_type, bool column_ref_left, @@ -80,12 +112,21 @@ static bool ConstantCastIsInvertible(ClientContext &context, BoundFunctionExpres if (cast_constant.IsNull() || BoundCastExpression::CastIsInvertible(cast_expression.GetReturnType(), target_type)) { return true; } - // This asks about the constant, not the column, and the constant was cast strictly just above - for - // integers that already proves it is exactly representable, so no type-level guarantee is needed. The - // column side is checked separately by the caller. if (cast_expression.GetReturnType().IsIntegral() && target_type.IsIntegral()) { return true; } + // The constant must survive the integral round-trip exactly; the column side is checked by the caller. + if (IsLosslessIntegralToFloatingCast(target_type, cast_expression.GetReturnType())) { + string error_message; + auto roundtrip = cast_constant.TryCastAs(context, cast_expression.GetReturnType(), &error_message, true); + return roundtrip && *roundtrip == constant_value; + } + if (target_type.id() == LogicalTypeId::DECIMAL && cast_expression.GetReturnType().id() == LogicalTypeId::DECIMAL) { + // a narrowing decimal cast of the constant is fine if the constant round-trips exactly + string error_message; + auto roundtrip = cast_constant.TryCastAs(context, cast_expression.GetReturnType(), &error_message, true); + return roundtrip && *roundtrip == constant_value; + } if (target_type.id() != LogicalTypeId::DATE || cast_expression.GetReturnType().id() != LogicalTypeId::TIMESTAMP) { return false; } @@ -201,7 +242,8 @@ unique_ptr ComparisonSimplificationRule::Apply(LogicalOperator &op, //! invertible in practice. auto &cast_expression = column_ref_expr.Cast(); auto target_type = BoundCastExpression::SourceType(cast_expression); - if (!BoundCastExpression::CastIsInvertible(target_type, cast_expression.GetReturnType())) { + if (!BoundCastExpression::CastIsInvertible(target_type, cast_expression.GetReturnType()) && + !IsLosslessIntegralToFloatingCast(target_type, cast_expression.GetReturnType())) { return nullptr; } diff --git a/src/duckdb/src/optimizer/rule/constant_order_normalization.cpp b/src/duckdb/src/optimizer/rule/constant_order_normalization.cpp index 4768b8a3b..6f636d679 100644 --- a/src/duckdb/src/optimizer/rule/constant_order_normalization.cpp +++ b/src/duckdb/src/optimizer/rule/constant_order_normalization.cpp @@ -121,7 +121,6 @@ unique_ptr ConstantOrderNormalizationRule::Apply(LogicalOperator &op // Reconstruct the expression. FunctionBinder binder(rewriter.context); - ErrorData error; unique_ptr new_root = ordered_bindings[0].get().Copy(); vector> children; children.push_back(std::move(new_root)); @@ -129,10 +128,7 @@ unique_ptr ConstantOrderNormalizationRule::Apply(LogicalOperator &op // Right child. children.push_back(ordered_bindings[i].get().Copy()); new_root = binder.BindScalarFunction(Identifier::DefaultSchema(), root.Function().GetName(), - std::move(children), error, root.IsOperator()); - if (!new_root) { - error.Throw(); - } + std::move(children), root.IsOperator()); children.clear(); // Left child. children.push_back(std::move(new_root)); @@ -141,7 +137,7 @@ unique_ptr ConstantOrderNormalizationRule::Apply(LogicalOperator &op D_ASSERT(children.size() == 1); D_ASSERT(children[0]->GetReturnType() == root.GetReturnType()); - return std::move(children[0]); + return Expression::PreserveReturnType(root.GetReturnType(), std::move(children[0])); } } // namespace duckdb diff --git a/src/duckdb/src/optimizer/rule/date_part_simplification.cpp b/src/duckdb/src/optimizer/rule/date_part_simplification.cpp index c5225b7fe..5b02f5122 100644 --- a/src/duckdb/src/optimizer/rule/date_part_simplification.cpp +++ b/src/duckdb/src/optimizer/rule/date_part_simplification.cpp @@ -96,14 +96,8 @@ unique_ptr DatePartSimplificationRule::Apply(LogicalOperator &op, ve vector> children; children.push_back(std::move(date_part.GetChildrenMutable()[1])); - ErrorData error; FunctionBinder binder(rewriter.context); - auto function = - binder.BindScalarFunction(Identifier::DefaultSchema(), new_function_name, std::move(children), error, false); - if (!function) { - error.Throw(); - } - return function; + return binder.BindScalarFunction(Identifier::DefaultSchema(), new_function_name, std::move(children)); } } // namespace duckdb diff --git a/src/duckdb/src/optimizer/rule/date_trunc_simplification.cpp b/src/duckdb/src/optimizer/rule/date_trunc_simplification.cpp index 87f0b2fc3..787a721ba 100644 --- a/src/duckdb/src/optimizer/rule/date_trunc_simplification.cpp +++ b/src/duckdb/src/optimizer/rule/date_trunc_simplification.cpp @@ -13,6 +13,7 @@ #include "duckdb/optimizer/matcher/expression_matcher.hpp" #include "duckdb/optimizer/expression_rewriter.hpp" #include "duckdb/common/enums/date_part_specifier.hpp" +#include "duckdb/common/types/date.hpp" #include "duckdb/function/function.hpp" #include "duckdb/function/function_binder.hpp" @@ -37,6 +38,27 @@ DateTruncSimplificationRule::DateTruncSimplificationRule(ExpressionRewriter &rew root = std::move(op); } +static bool TruncatesYearsTowardsZero(const BoundConstantExpression &date_part) { + DatePartSpecifier part; + if (!TryGetDatePartSpecifier(StringValue::Get(date_part.GetValue()), part)) { + return true; + } + return part == DatePartSpecifier::DECADE || part == DatePartSpecifier::CENTURY || + part == DatePartSpecifier::MILLENNIUM; +} + +static bool IsAfterFirstYear(const Value &value) { + if (value.IsNull()) { + return true; + } + auto date_value = value.DefaultTryCastAs(LogicalType::DATE); + if (!date_value || date_value->IsNull()) { + return false; + } + // leave a margin for time zones + return Date::ExtractYear(date_value->GetValue()) > 1; +} + unique_ptr DateTruncSimplificationRule::Apply(LogicalOperator &op, vector> &bindings, bool &changes_made, bool is_root) { auto &expr = bindings[0].get().Cast(); @@ -73,6 +95,12 @@ unique_ptr DateTruncSimplificationRule::Apply(LogicalOperator &op, v return nullptr; } + // Truncating to decades, centuries or millennia does not round years before the common era towards the past + // (e.g. 150 BC truncates to 100 BC), so the rewrite below is only valid for constants after that + if (TruncatesYearsTowardsZero(date_part) && !IsAfterFirstYear(rhs.GetValue())) { + return nullptr; + } + // Check whether trunc(date_part, constant_rhs) = constant_rhs. const bool is_truncated = DateIsTruncated(date_part, rhs); diff --git a/src/duckdb/src/optimizer/rule/empty_needle_removal.cpp b/src/duckdb/src/optimizer/rule/empty_needle_removal.cpp index ae6732eb2..12ab18a4d 100644 --- a/src/duckdb/src/optimizer/rule/empty_needle_removal.cpp +++ b/src/duckdb/src/optimizer/rule/empty_needle_removal.cpp @@ -23,7 +23,7 @@ static unique_ptr TryRemoveEmptyStringConcat(ClientContext &context, if (value.IsNull() || value.type().id() != LogicalTypeId::VARCHAR || !StringValue::Get(value).empty()) { continue; } - return std::move(children[1 - child_idx]); + return Expression::PreserveReturnType(root.GetReturnType(), std::move(children[1 - child_idx])); } return nullptr; } @@ -110,7 +110,7 @@ unique_ptr NoopReplaceRemovalRule::Apply(LogicalOperator &op, vector if (StringValue::Get(needle) != StringValue::Get(replacement)) { return nullptr; } - return std::move(root.GetChildrenMutable()[0]); + return Expression::PreserveReturnType(root.GetReturnType(), std::move(root.GetChildrenMutable()[0])); } } // namespace duckdb diff --git a/src/duckdb/src/optimizer/rule/least_greatest_simplification.cpp b/src/duckdb/src/optimizer/rule/least_greatest_simplification.cpp index 7f7595808..26b82cab9 100644 --- a/src/duckdb/src/optimizer/rule/least_greatest_simplification.cpp +++ b/src/duckdb/src/optimizer/rule/least_greatest_simplification.cpp @@ -23,7 +23,7 @@ unique_ptr LeastGreatestSimplificationRule::Apply(LogicalOperator &o if (children[0]->IsVolatile() || !children[0]->Equals(*children[1])) { return nullptr; } - return std::move(children[0]); + return Expression::PreserveReturnType(root.GetReturnType(), std::move(children[0])); } } // namespace duckdb diff --git a/src/duckdb/src/optimizer/rule/like_optimizations.cpp b/src/duckdb/src/optimizer/rule/like_optimizations.cpp index 29d531a7f..fdf8c596d 100644 --- a/src/duckdb/src/optimizer/rule/like_optimizations.cpp +++ b/src/duckdb/src/optimizer/rule/like_optimizations.cpp @@ -1,7 +1,7 @@ #include "duckdb/optimizer/rule/like_optimizations.hpp" #include "duckdb/execution/expression_executor.hpp" -#include "duckdb/optimizer/builtin_function_lookup.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/function/scalar/string_functions.hpp" #include "duckdb/function/scalar/string_common.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" @@ -17,9 +17,10 @@ LikeOptimizationRule::LikeOptimizationRule(ExpressionRewriter &rewriter) : Rule( func->matchers.push_back(make_uniq()); func->matchers.push_back(make_uniq()); func->policy = SetMatcher::Policy::ORDERED; - // we match on LIKE ("~~"), NOT LIKE ("!~~"), GLOB ("~~~"), and NOT GLOB ("!~~~") - func->function = make_uniq( - identifier_set_t {Identifier("!~~"), Identifier("~~"), Identifier("!~~~"), Identifier("~~~")}); + // we match on LIKE ("~~"), NOT LIKE ("!~~"), GLOB ("~~~"), NOT GLOB ("!~~~"), ILIKE ("~~*"), and NOT ILIKE ("!~~*") + func->function = + make_uniq(identifier_set_t {Identifier("!~~"), Identifier("~~"), Identifier("!~~~"), + Identifier("~~~"), Identifier("~~*"), Identifier("!~~*")}); root = std::move(func); } @@ -139,6 +140,11 @@ unique_ptr LikeOptimizationRule::Apply(LogicalOperator &op, vector(Value(root.GetReturnType())); } + bool is_ilike = root.Function().GetName() == "~~*" || root.Function().GetName() == "!~~*"; + if (is_ilike) { + return nullptr; + } + // the constant_expr is a scalar expression that we have to fold if (!constant_expr.IsFoldable()) { return nullptr; diff --git a/src/duckdb/src/optimizer/rule/monotone_preimage.cpp b/src/duckdb/src/optimizer/rule/monotone_preimage.cpp index e8c97f9bc..11825d66e 100644 --- a/src/duckdb/src/optimizer/rule/monotone_preimage.cpp +++ b/src/duckdb/src/optimizer/rule/monotone_preimage.cpp @@ -51,8 +51,10 @@ static bool GetFiniteDomain(const LogicalType &type, int64_t &lo, int64_t &hi, b return true; case LogicalTypeId::TIMESTAMP: case LogicalTypeId::TIMESTAMP_TZ: - // the ±infinity sentinels keep the finite domain bounded away from INT64_MIN/MAX, so the - // never-true sentinels (hi+1 / lo-1) in the bisection can't overflow + // every non-sentinel int64 is a valid, comparable timestamp (make_timestamp accepts all of them), so the + // finite domain is everything except the ±infinity sentinels; that also keeps the never-true sentinels + // (hi+1 / lo-1) of the bisection away from INT64_MIN/MAX. Not all of these values render, though: see + // IsRenderableBound. lo = timestamp_t::ninfinity().value + 1; hi = timestamp_t::infinity().value - 1; has_infinity = true; @@ -145,10 +147,37 @@ static int64_t LastTruePrefix(PRED pred, int64_t lo, int64_t hi, bool &ok) { return l; } -static unique_ptr MakeColComparison(ExpressionType type, const BoundColumnRefExpression &col, - const LogicalType &col_type, int64_t bound) { - return BoundComparisonExpression::Create(type, col.Copy(), - make_uniq(Value::Numeric(col_type, bound))); +//! Whether a bound with internal representation `bound` can be rendered (Expression::ToString, EXPLAIN). +//! TIMESTAMP values between INT64_MIN and the first midnight of Timestamp::MIN_YEAR are valid and comparable +//! (make_timestamp accepts them) but have no date/time form: Timestamp::Convert overflows on them. +static bool IsRenderableBound(const LogicalType &col_type, int64_t bound) { + switch (col_type.id()) { + case LogicalTypeId::TIMESTAMP: + case LogicalTypeId::TIMESTAMP_TZ: + return bound >= Value::MinimumValue(LogicalType::TIMESTAMP).GetValue().value; + default: + return true; + } +} + +//! Emit `col >= bound` (lower) or `col <= bound` (upper). On types with ±infinity a bound on the edge of the +//! finite domain is written as a strict comparison against the adjacent sentinel instead (`col > -infinity`, +//! `col < infinity`): nothing lies between the two, and the sentinel always renders while the edge of the +//! TIMESTAMP domain does not. Returns nullptr if the bound cannot be rendered - the caller then keeps f(col). +static unique_ptr MakeColBound(bool lower, const BoundColumnRefExpression &col, const LogicalType &col_type, + int64_t bound, int64_t dom_edge, bool has_infinity) { + if (has_infinity && bound == dom_edge) { + auto sentinel = lower ? Value::NegativeInfinity(col_type) : Value::Infinity(col_type); + return BoundComparisonExpression::Create(lower ? ExpressionType::COMPARE_GREATERTHAN + : ExpressionType::COMPARE_LESSTHAN, + col.Copy(), make_uniq(std::move(sentinel))); + } + if (!IsRenderableBound(col_type, bound)) { + return nullptr; + } + return BoundComparisonExpression::Create( + lower ? ExpressionType::COMPARE_GREATERTHANOREQUALTO : ExpressionType::COMPARE_LESSTHANOREQUALTO, col.Copy(), + make_uniq(Value::Numeric(col_type, bound))); } unique_ptr MonotonePreimageRule::Apply(LogicalOperator &op, vector> &bindings, @@ -323,10 +352,16 @@ unique_ptr MonotonePreimageRule::Apply(LogicalOperator &op, vector lower; unique_ptr upper; if (need_lower) { - lower = MakeColComparison(ExpressionType::COMPARE_GREATERTHANOREQUALTO, col, col_type, a); + lower = MakeColBound(true, col, col_type, a, dom_lo, has_infinity); + if (!lower) { + return nullptr; + } } if (need_upper) { - upper = MakeColComparison(ExpressionType::COMPARE_LESSTHANOREQUALTO, col, col_type, b); + upper = MakeColBound(false, col, col_type, b, dom_hi, has_infinity); + if (!upper) { + return nullptr; + } } if (lower && upper) { return make_uniq(ExpressionType::CONJUNCTION_AND, std::move(lower), diff --git a/src/duckdb/src/optimizer/rule/move_constants.cpp b/src/duckdb/src/optimizer/rule/move_constants.cpp index fc5cac60c..194e4e2a5 100644 --- a/src/duckdb/src/optimizer/rule/move_constants.cpp +++ b/src/duckdb/src/optimizer/rule/move_constants.cpp @@ -91,7 +91,7 @@ unique_ptr MoveConstantsRule::Apply(LogicalOperator &op, vector MoveConstantsRule::Apply(LogicalOperator &op, vector MoveConstantsRule::Apply(LogicalOperator &op, vector 2] BoundComparisonExpression::FlipType(comparison); @@ -180,7 +180,7 @@ unique_ptr MoveConstantsRule::Apply(LogicalOperator &op, vector CreateIsNanCall(ClientContext &context, const Expr vector> children; children.push_back(expr.Copy()); - ErrorData error; FunctionBinder binder(context); - auto isnan = binder.BindScalarFunction(Identifier::DefaultSchema(), "isnan", std::move(children), error); - if (!isnan) { - error.Throw(); - } - return isnan; + return binder.BindScalarFunction(Identifier::DefaultSchema(), "isnan", std::move(children)); } static unique_ptr CreateNotIsNanGuard(ClientContext &context, const Expression &expr) { @@ -304,7 +299,7 @@ unique_ptr MoveUnaryMinusRule::Apply(LogicalOperator &op, vector MoveUnaryMinusRule::Apply(LogicalOperator &op, vector(negated_value), width, scale); + outer_constant.SetValue(Value::DECIMAL(Cast::Operation(negated_value), width, scale)); break; case PhysicalType::INT32: - outer_constant.GetValueMutable() = - Value::DECIMAL(Cast::Operation(negated_value), width, scale); + outer_constant.SetValue(Value::DECIMAL(Cast::Operation(negated_value), width, scale)); break; case PhysicalType::INT64: - outer_constant.GetValueMutable() = - Value::DECIMAL(Cast::Operation(negated_value), width, scale); + outer_constant.SetValue(Value::DECIMAL(Cast::Operation(negated_value), width, scale)); break; case PhysicalType::INT128: - outer_constant.GetValueMutable() = Value::DECIMAL(negated_value, width, scale); + outer_constant.SetValue(Value::DECIMAL(negated_value, width, scale)); break; default: throw InternalException("Unknown DECIMAL physical type"); @@ -347,7 +339,7 @@ unique_ptr MoveUnaryMinusRule::Apply(LogicalOperator &op, vector [x FLIPPED_COMP -c] auto inner_expr = std::move(negation.GetChildrenMutable()[0]); diff --git a/src/duckdb/src/optimizer/rule/regex_optimizations.cpp b/src/duckdb/src/optimizer/rule/regex_optimizations.cpp index 9a7dd0433..1c3b0152d 100644 --- a/src/duckdb/src/optimizer/rule/regex_optimizations.cpp +++ b/src/duckdb/src/optimizer/rule/regex_optimizations.cpp @@ -2,7 +2,7 @@ #include "duckdb/execution/expression_executor.hpp" #include "duckdb/function/function_binder.hpp" -#include "duckdb/optimizer/builtin_function_lookup.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/optimizer/expression_rewriter.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" #include "duckdb/planner/expression/bound_constant_expression.hpp" @@ -134,11 +134,21 @@ static LikeString LikeMatchFromRegex(duckdb_re2::RE2 &pattern) { } break; } + case duckdb_re2::kRegexpBeginText: + // anchors are represented by the absence of a leading / trailing '%', so they must be at the edges + if (cur_sub_index != 0) { + ret.exists = false; + return ret; + } + break; case duckdb_re2::kRegexpEndText: + if (cur_sub_index + 1 != num_subs) { + ret.exists = false; + return ret; + } + break; case duckdb_re2::kRegexpEmptyMatch: - case duckdb_re2::kRegexpBeginText: { break; - } default: // some other regexp op that doesn't have an equivalent to a like string // return false; @@ -287,6 +297,10 @@ unique_ptr RegexpReplaceExtractRule::Apply(LogicalOperator &op, vect if (!compiled.ok() || !RegexpHasWholeTextAnchors(compiled.Regexp())) { return nullptr; } + if (group_index > compiled.NumberOfCapturingGroups()) { + // leave reporting the invalid back-reference to regexp_replace + return nullptr; + } string options_str = "k"; if (root.GetChildrenMutable().size() == 4) { diff --git a/src/duckdb/src/optimizer/rule/string_prefix.cpp b/src/duckdb/src/optimizer/rule/string_prefix.cpp index 566285339..a2a85f962 100644 --- a/src/duckdb/src/optimizer/rule/string_prefix.cpp +++ b/src/duckdb/src/optimizer/rule/string_prefix.cpp @@ -3,7 +3,7 @@ #include "duckdb/common/limits.hpp" #include "duckdb/function/scalar/string_common.hpp" #include "duckdb/function/scalar/string_functions.hpp" -#include "duckdb/optimizer/builtin_function_lookup.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/optimizer/expression_rewriter.hpp" #include "duckdb/optimizer/matcher/expression_matcher.hpp" #include "duckdb/optimizer/matcher/type_matcher_id.hpp" diff --git a/src/duckdb/src/optimizer/scalar_fn_pushdown.cpp b/src/duckdb/src/optimizer/scalar_fn_pushdown.cpp index 9cb58267c..ea8b12da7 100644 --- a/src/duckdb/src/optimizer/scalar_fn_pushdown.cpp +++ b/src/duckdb/src/optimizer/scalar_fn_pushdown.cpp @@ -71,12 +71,12 @@ unique_ptr ScalarFnReplace::VisitReplace(BoundColumnRefExpression &e return std::move(*ptr); } - const auto &[analysis, column_index, projection] = *binding; + const auto &[analysis, column_index, projection, projection_column_index] = *binding; if (CanPushdownColumn(analysis, column_index)) { const LogicalType return_type = analysis.get.returned_types[analysis.StorageIndex(column_index)]; expr.SetReturnType(return_type); if (projection != nullptr && !projection->types.empty()) { - projection->types[column_index] = return_type; + projection->types[projection_column_index] = return_type; } } @@ -99,7 +99,7 @@ unique_ptr ScalarFnReplace::VisitReplace(BoundFunctionExpression &ex return nullptr; } - const auto &[analysis, column_index, projection] = *binding; + const auto &[analysis, column_index, projection, projection_column_index] = *binding; if (!CanPushdownColumn(analysis, column_index)) { return std::move(*ptr); } @@ -107,7 +107,7 @@ unique_ptr ScalarFnReplace::VisitReplace(BoundFunctionExpression &ex const LogicalType return_type = analysis.get.returned_types[analysis.StorageIndex(column_index)]; bound_col_base->SetReturnType(return_type); if (projection != nullptr && !projection->types.empty()) { - projection->types[column_index] = return_type; + projection->types[projection_column_index] = return_type; } return std::move(bound_col_base); } diff --git a/src/duckdb/src/optimizer/statistics/expression/propagate_conjunction.cpp b/src/duckdb/src/optimizer/statistics/expression/propagate_conjunction.cpp index 8e56fee73..debc0f07b 100644 --- a/src/duckdb/src/optimizer/statistics/expression/propagate_conjunction.cpp +++ b/src/duckdb/src/optimizer/statistics/expression/propagate_conjunction.cpp @@ -2,11 +2,68 @@ #include "duckdb/optimizer/statistics_propagator.hpp" #include "duckdb/planner/expression/bound_conjunction_expression.hpp" #include "duckdb/planner/expression/bound_constant_expression.hpp" +#include "duckdb/planner/expression/bound_function_expression.hpp" +#include "duckdb/planner/expression/expression_barrier.hpp" +#include "duckdb/planner/expression_nullability.hpp" #include "duckdb/optimizer/expression_rewriter.hpp" #include "duckdb/execution/expression_executor.hpp" namespace duckdb { +void StatisticsPropagator::SimplifyConstantOrNull(vector> &expressions) { + if (expressions.size() < 2) { + return; + } + bool has_candidate = false; + for (auto &expr : expressions) { + has_candidate |= ExpressionIsConstantOrNull(*expr, Value::BOOLEAN(true)); + } + if (!has_candidate) { + return; + } + for (auto &expr : expressions) { + // Removing a NULL check can expose more rows to the remaining predicates. + if (expr->CanThrow() || expr->IsVolatile() || ExpressionBarrier::Contains(*expr)) { + return; + } + } + for (idx_t expr_idx = 0; expr_idx < expressions.size(); expr_idx++) { + auto &expr = *expressions[expr_idx]; + if (!ExpressionIsConstantOrNull(expr, Value::BOOLEAN(true))) { + continue; + } + auto &children = expr.Cast().GetChildren(); + D_ASSERT(children.size() >= 2); + bool redundant = true; + for (idx_t child_idx = 1; child_idx < children.size(); child_idx++) { + auto &child = *children[child_idx]; + if (child.GetExpressionClass() == ExpressionClass::BOUND_CONSTANT) { + if (child.Cast().GetValue().IsNull()) { + redundant = false; + break; + } + continue; + } + bool rejects_null = false; + for (idx_t other_idx = 0; other_idx < expressions.size(); other_idx++) { + if (other_idx != expr_idx && FilterRejectsNull(*expressions[other_idx], child)) { + rejects_null = true; + break; + } + } + if (!rejects_null) { + redundant = false; + break; + } + } + if (redundant) { + expressions.erase_at(expr_idx); + expr_idx--; + removed_expressions = true; + } + } +} + unique_ptr StatisticsPropagator::PropagateExpression(BoundConjunctionExpression &expr, unique_ptr &expr_ptr) { auto is_and = expr.GetExpressionType() == ExpressionType::CONJUNCTION_AND; @@ -54,6 +111,9 @@ unique_ptr StatisticsPropagator::PropagateExpression(BoundConjun expr_ptr = make_uniq(Value::BOOLEAN(constant_value)); return PropagateExpression(expr_ptr); } + if (is_and) { + SimplifyConstantOrNull(expr.GetChildrenMutable()); + } if (expr.GetChildrenMutable().empty()) { // if there are no children left, replace the conjunction with TRUE (for AND) or FALSE (for OR) expr_ptr = make_uniq(Value::BOOLEAN(is_and)); diff --git a/src/duckdb/src/optimizer/statistics/expression/propagate_function.cpp b/src/duckdb/src/optimizer/statistics/expression/propagate_function.cpp index 6aadff67a..caa7948e3 100644 --- a/src/duckdb/src/optimizer/statistics/expression/propagate_function.cpp +++ b/src/duckdb/src/optimizer/statistics/expression/propagate_function.cpp @@ -40,7 +40,12 @@ bool CanInferConstantFromNumericBounds(const Value &value) { } bool TryInferConstantBounds(const BaseStatistics &stats, Value &constant) { - if (stats.CanHaveNull()) { + if (stats.CanHaveNull() && !stats.CanHaveNoNull()) { + constant = Value(stats.GetType()); + return true; + } + // bounds of empty stats do not describe any actual value + if (stats.CanHaveNull() || !stats.CanHaveNoNull()) { return false; } if (stats.GetStatsType() == StatisticsType::NUMERIC_STATS && NumericStats::HasMinMax(stats)) { diff --git a/src/duckdb/src/optimizer/statistics/expression/propagate_operator.cpp b/src/duckdb/src/optimizer/statistics/expression/propagate_operator.cpp index cccf58edb..6eba5f59c 100644 --- a/src/duckdb/src/optimizer/statistics/expression/propagate_operator.cpp +++ b/src/duckdb/src/optimizer/statistics/expression/propagate_operator.cpp @@ -1,9 +1,33 @@ #include "duckdb/optimizer/statistics_propagator.hpp" +#include "duckdb/planner/expression/bound_cast_expression.hpp" #include "duckdb/planner/expression/bound_constant_expression.hpp" #include "duckdb/planner/expression/bound_operator_expression.hpp" +#include "duckdb/planner/expression_iterator.hpp" namespace duckdb { +//! Casting a variable-size value to VARIANT throws if the encoded row exceeds the maximum size, even for TRY_CAST +static bool ContainsThrowingVariantCast(const Expression &expr) { + if (BoundCastExpression::IsCast(expr)) { + auto &cast = expr.Cast(); + auto source_type = BoundCastExpression::SourceType(cast); + if (BoundCastExpression::TargetType(cast).id() == LogicalTypeId::VARIANT && + source_type.id() != LogicalTypeId::VARIANT && !TypeIsConstantSize(source_type.InternalType())) { + return true; + } + } + bool result = false; + ExpressionIterator::EnumerateChildren( + expr, [&](const Expression &child) { result = result || ContainsThrowingVariantCast(child); }); + return result; +} + +//! Folding a NULL check removes its child from the plan +static bool CanFoldNullCheck(const BoundOperatorExpression &expr) { + auto &child = *expr.GetChildren()[0]; + return !child.IsVolatile() && !ContainsThrowingVariantCast(child); +} + unique_ptr StatisticsPropagator::PropagateExpression(BoundOperatorExpression &expr, unique_ptr &expr_ptr) { bool all_have_stats = true; @@ -50,7 +74,7 @@ unique_ptr StatisticsPropagator::PropagateExpression(BoundOperat D_ASSERT(expr.GetChildrenMutable().size() == child_stats.size()); if (expr.GetChildrenMutable().size() == 1) { // coalesce of one entry: simply return that entry - expr_ptr = std::move(expr.GetChildrenMutable()[0]); + expr_ptr = Expression::PreserveReturnType(expr.GetReturnType(), std::move(expr.GetChildrenMutable()[0])); } else { // coalesce of multiple entries // merge the stats @@ -60,6 +84,9 @@ unique_ptr StatisticsPropagator::PropagateExpression(BoundOperat } return std::move(child_stats[0]); case ExpressionType::OPERATOR_IS_NULL: + if (!CanFoldNullCheck(expr)) { + return nullptr; + } if (!child_stats[0]->CanHaveNull()) { // child has no null values: x IS NULL will always be false expr_ptr = make_uniq(Value::BOOLEAN(false)); @@ -72,6 +99,9 @@ unique_ptr StatisticsPropagator::PropagateExpression(BoundOperat } return nullptr; case ExpressionType::OPERATOR_IS_NOT_NULL: + if (!CanFoldNullCheck(expr)) { + return nullptr; + } if (!child_stats[0]->CanHaveNull()) { // child has no null values: x IS NOT NULL will always be true expr_ptr = make_uniq(Value::BOOLEAN(true)); diff --git a/src/duckdb/src/optimizer/statistics/operator/propagate_filter.cpp b/src/duckdb/src/optimizer/statistics/operator/propagate_filter.cpp index 8a39bf26f..6e5b05758 100644 --- a/src/duckdb/src/optimizer/statistics/operator/propagate_filter.cpp +++ b/src/duckdb/src/optimizer/statistics/operator/propagate_filter.cpp @@ -338,7 +338,7 @@ unique_ptr StatisticsPropagator::PropagateStatistics(LogicalFilt if (filter.projection_map.empty()) { node_ptr = std::move(filter.children[0]); } - break; + return std::move(node_stats); } } else if (prune_result == FilterPropagateResult::FILTER_ALWAYS_FALSE || prune_result == FilterPropagateResult::FILTER_FALSE_OR_NULL) { @@ -347,6 +347,7 @@ unique_ptr StatisticsPropagator::PropagateStatistics(LogicalFilt return make_uniq(0U, 0U); } } + SimplifyConstantOrNull(filter.expressions); // the max cardinality of a filter is the cardinality of the input (i.e. no tuples get filtered) return std::move(node_stats); } diff --git a/src/duckdb/src/optimizer/statistics/operator/propagate_get.cpp b/src/duckdb/src/optimizer/statistics/operator/propagate_get.cpp index 505504b9d..d001d0964 100644 --- a/src/duckdb/src/optimizer/statistics/operator/propagate_get.cpp +++ b/src/duckdb/src/optimizer/statistics/operator/propagate_get.cpp @@ -14,11 +14,6 @@ namespace duckdb { -static bool IsDirectFilterColumnRef(const Expression &expr) { - return expr.GetExpressionType() == ExpressionType::BOUND_REF || - expr.GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF; -} - static void GetColumnIndex(const unique_ptr &expr, idx_t &index, Identifier &alias) { if (expr->GetExpressionType() == ExpressionType::BOUND_REF) { auto &bound_ref = expr->Cast(); @@ -76,26 +71,18 @@ void StatisticsPropagator::UpdateExpressionFilterStatistics(BaseStatistics &inpu if (!BoundComparisonExpression::IsComparison(expr)) { break; } - auto &comp = expr.Cast(); - auto compare_type = comp.GetExpressionType(); + ExpressionType compare_type; + auto constant = + ExpressionFilter::TryGetColumnConstantComparison(expr.Cast(), compare_type); + if (!constant) { + break; + } auto is_compare_distinct = compare_type == ExpressionType::COMPARE_DISTINCT_FROM || compare_type == ExpressionType::COMPARE_NOT_DISTINCT_FROM; - auto &left = BoundComparisonExpression::Left(comp); - auto &right = BoundComparisonExpression::Right(comp); - if (IsDirectFilterColumnRef(left) && right.GetExpressionType() == ExpressionType::VALUE_CONSTANT) { - auto &constant = right.Cast(); - if (constant.GetValue().type().InternalType() == input.GetType().InternalType()) { - UpdateFilterStatistics(input, comp.GetExpressionType(), constant.GetValue()); - } else if (!is_compare_distinct) { - input.Set(StatsInfo::CANNOT_HAVE_NULL_VALUES); - } - } else if (left.GetExpressionType() == ExpressionType::VALUE_CONSTANT && IsDirectFilterColumnRef(right)) { - auto &constant = left.Cast(); - if (constant.GetValue().type().InternalType() == input.GetType().InternalType()) { - UpdateFilterStatistics(input, FlipComparisonExpression(comp.GetExpressionType()), constant.GetValue()); - } else if (!is_compare_distinct) { - input.Set(StatsInfo::CANNOT_HAVE_NULL_VALUES); - } + if (constant->GetValue().type().InternalType() == input.GetType().InternalType()) { + UpdateFilterStatistics(input, compare_type, constant->GetValue()); + } else if (!is_compare_distinct) { + input.Set(StatsInfo::CANNOT_HAVE_NULL_VALUES); } break; } @@ -111,7 +98,7 @@ void StatisticsPropagator::UpdateExpressionFilterStatistics(BaseStatistics &inpu case ExpressionClass::BOUND_OPERATOR: { auto &op = expr.Cast(); if (expr.GetExpressionType() == ExpressionType::OPERATOR_IS_NOT_NULL && !op.GetChildren().empty() && - IsDirectFilterColumnRef(*op.GetChildren()[0])) { + ExpressionFilter::IsSimpleFilterColumnRef(*op.GetChildren()[0])) { input.Set(StatsInfo::CANNOT_HAVE_NULL_VALUES); } break; diff --git a/src/duckdb/src/optimizer/statistics/operator/propagate_join.cpp b/src/duckdb/src/optimizer/statistics/operator/propagate_join.cpp index d68863d5c..d937e40e9 100644 --- a/src/duckdb/src/optimizer/statistics/operator/propagate_join.cpp +++ b/src/duckdb/src/optimizer/statistics/operator/propagate_join.cpp @@ -21,15 +21,15 @@ bool StatisticsPropagator::HandleJoinNeverMatches(LogicalJoin &join, unique_ptr< // semi or inner join on false; entire node can be pruned ReplaceWithEmptyResult(node_ptr); return true; + + // If the filter is always false or Null, the join output is exactly one of its sides: + // promote that side together with its projection map so the output layout stays unchanged. case JoinType::RIGHT_ANTI: - case JoinType::ANTI: { - if (join.join_type == JoinType::RIGHT_ANTI) { - std::swap(join.children[0], join.children[1]); - } - // If the filter is always false or Null, just return the left child. - node_ptr = std::move(join.children[0]); + node_ptr = LogicalJoin::PromoteJoinSide(std::move(join.children[1]), std::move(join.right_projection_map)); + return true; + case JoinType::ANTI: + node_ptr = LogicalJoin::PromoteJoinSide(std::move(join.children[0]), std::move(join.left_projection_map)); return true; - } case JoinType::LEFT: // anti/left outer join: replace right side with empty node ReplaceWithEmptyResult(join.children[1]); @@ -46,13 +46,32 @@ bool StatisticsPropagator::HandleJoinNeverMatches(LogicalJoin &join, unique_ptr< } } +//! Appends the columns of a join side that the join emits to the projection map of its replacement +static void AddProjectedColumns(vector &result, const vector &projection_map, + idx_t column_count, idx_t offset) { + if (projection_map.empty()) { + for (idx_t column_idx = 0; column_idx < column_count; column_idx++) { + result.emplace_back(offset + column_idx); + } + return; + } + for (auto &column_idx : projection_map) { + result.emplace_back(offset + column_idx.GetIndex()); + } +} + bool StatisticsPropagator::HandleJoinAlwaysMatches(LogicalJoin &join, unique_ptr &node_ptr) { switch (join.join_type) { case JoinType::RIGHT_SEMI: case JoinType::SEMI: { + auto &emitted_projection_map = + join.join_type == JoinType::RIGHT_SEMI ? join.right_projection_map : join.left_projection_map; if (join.join_type == JoinType::RIGHT_SEMI) { std::swap(join.children[0], join.children[1]); } + // the cross product also emits the columns of the other side: project them out again + vector projection_map; + AddProjectedColumns(projection_map, emitted_projection_map, join.children[0]->GetColumnBindings().size(), 0); // when the right child has no data, return an empty set // cannot just return the left child because if the right child has no cardinality // then the whole result should be empty. @@ -63,14 +82,21 @@ bool StatisticsPropagator::HandleJoinAlwaysMatches(LogicalJoin &join, unique_ptr limit->AddChild(std::move(join.children[1])); auto cross_product = LogicalCrossProduct::Create(std::move(join.children[0]), std::move(limit)); cross_product->SetEstimatedCardinality(join.estimated_cardinality); - node_ptr = std::move(cross_product); + node_ptr = LogicalJoin::PromoteJoinSide(std::move(cross_product), std::move(projection_map)); return true; } case JoinType::INNER: { - // inner, replace with cross product + // inner, replace with cross product - keeping the columns that the join emits + vector projection_map; + if (!join.left_projection_map.empty() || !join.right_projection_map.empty()) { + auto left_count = join.children[0]->GetColumnBindings().size(); + AddProjectedColumns(projection_map, join.left_projection_map, left_count, 0); + AddProjectedColumns(projection_map, join.right_projection_map, join.children[1]->GetColumnBindings().size(), + left_count); + } auto cross_product = LogicalCrossProduct::Create(std::move(join.children[0]), std::move(join.children[1])); cross_product->SetEstimatedCardinality(join.estimated_cardinality); - node_ptr = std::move(cross_product); + node_ptr = LogicalJoin::PromoteJoinSide(std::move(cross_product), std::move(projection_map)); return true; } default: @@ -213,8 +239,12 @@ void StatisticsPropagator::MultiplyCardinalities(unique_ptr &sta return; } stats->estimated_cardinality = MaxValue(stats->estimated_cardinality, new_stats.estimated_cardinality); - auto new_max = Hugeint::Multiply(NumericCast(stats->max_cardinality), - NumericCast(new_stats.max_cardinality)); + hugeint_t new_max; + if (!Hugeint::TryMultiply(Hugeint::Convert(stats->max_cardinality), Hugeint::Convert(new_stats.max_cardinality), + new_max)) { + stats = nullptr; + return; + } if (new_max < NumericLimits::Maximum()) { int64_t result; if (!Hugeint::TryCast(new_max, result)) { @@ -348,6 +378,10 @@ unique_ptr StatisticsPropagator::PropagateStatistics(LogicalPosi void StatisticsPropagator::CreateFilterFromJoinStats(unique_ptr &child, unique_ptr &expr, const BaseStatistics &stats_before, const BaseStatistics &stats_after) { + // Defer join-derived filters until after join ordering. + if (mode != StatisticsPropagationMode::FULL) { + return; + } // Only do this for integral colref's that have stats if (expr->GetExpressionType() != ExpressionType::BOUND_COLUMN_REF || !expr->GetReturnType().IsIntegral() || !NumericStats::HasMinMax(stats_before) || !NumericStats::HasMinMax(stats_after)) { diff --git a/src/duckdb/src/optimizer/statistics/operator/propagate_secure_view.cpp b/src/duckdb/src/optimizer/statistics/operator/propagate_secure_view.cpp index 18da8cf93..b33985f10 100644 --- a/src/duckdb/src/optimizer/statistics/operator/propagate_secure_view.cpp +++ b/src/duckdb/src/optimizer/statistics/operator/propagate_secure_view.cpp @@ -11,24 +11,37 @@ unique_ptr StatisticsPropagator::PropagateStatistics(LogicalSecu bindings_before.insert(entry.first); } - // propagate into the view - the view definition itself is still optimized using its own statistics - PropagateChildren(op, node_ptr); + // propagate into the view - the view definition itself is always optimized using its own statistics + auto child_stats = PropagateStatistics(op.children[0]); - // discard every statistic that was derived from the contents of the view. Statistics describe the data itself - // (min/max, null counts, distinct counts) - leaking them past a secure view would expose values from rows that - // the view does not return, both through stats() and by allowing filters on top of the view to be pruned. + // statistics describe the data itself (min/max, null counts, distinct counts). Only the statistics of the columns + // that the view emits may cross the boundary, and only if the view emits every row it reads - otherwise they would + // expose values from rows that the view does not return, both through stats() and by allowing filters on top of + // the view to be pruned. + column_binding_set_t exposed_bindings; + if (op.propagate_statistics) { + for (auto &binding : op.GetColumnBindings()) { + exposed_bindings.insert(binding); + } + } vector leaked_bindings; for (auto &entry : statistics_map) { - if (bindings_before.find(entry.first) == bindings_before.end()) { - leaked_bindings.push_back(entry.first); + if (bindings_before.find(entry.first) != bindings_before.end()) { + continue; + } + if (exposed_bindings.find(entry.first) != exposed_bindings.end()) { + continue; } + leaked_bindings.push_back(entry.first); } for (auto &binding : leaked_bindings) { statistics_map.erase(binding); } - - // no cardinality information escapes the view either - return nullptr; + if (!op.propagate_statistics) { + // no cardinality information escapes the view either + return nullptr; + } + return child_stats; } } // namespace duckdb diff --git a/src/duckdb/src/optimizer/statistics_propagator.cpp b/src/duckdb/src/optimizer/statistics_propagator.cpp index db3d72baa..fc928ae1f 100644 --- a/src/duckdb/src/optimizer/statistics_propagator.cpp +++ b/src/duckdb/src/optimizer/statistics_propagator.cpp @@ -46,6 +46,7 @@ unique_ptr StatisticsPropagator::PropagateChildren(LogicalOperat unique_ptr StatisticsPropagator::PropagateStatistics(LogicalOperator &node, unique_ptr &node_ptr) { + propagation_path.emplace_back(node_ptr); unique_ptr result; switch (node.type) { case LogicalOperatorType::LOGICAL_AGGREGATE_AND_GROUP_BY: @@ -106,10 +107,11 @@ unique_ptr StatisticsPropagator::PropagateStatistics(LogicalOper if (mode == StatisticsPropagationMode::FULL && !optimizer.OptimizerDisabled(OptimizerType::COMPRESSED_MATERIALIZATION)) { // compress data based on statistics for materializing operators - CompressedMaterialization compressed_materialization(optimizer, *root, statistics_map); + CompressedMaterialization compressed_materialization(optimizer, *root, propagation_path, statistics_map); compressed_materialization.Compress(node_ptr); } + propagation_path.pop_back(); return result; } @@ -161,7 +163,11 @@ unique_ptr StatisticsPropagator::PropagateExpression(Expression } unique_ptr StatisticsPropagator::PropagateExpression(unique_ptr &expr) { +#ifdef D_ASSERT_IS_ENABLED + auto return_type = expr->GetReturnType(); +#endif auto stats = PropagateExpression(*expr, expr); + D_ASSERT(expr->GetReturnType() != return_type || expr->GetReturnType().EqualsIncludingCollation(return_type)); if (Settings::Get(context) && stats) { expr->SetVerificationStats(stats->ToUnique()); } diff --git a/src/duckdb/src/optimizer/topn_optimizer.cpp b/src/duckdb/src/optimizer/topn_optimizer.cpp index 6816757dc..a2c9f805b 100644 --- a/src/duckdb/src/optimizer/topn_optimizer.cpp +++ b/src/duckdb/src/optimizer/topn_optimizer.cpp @@ -184,6 +184,7 @@ unique_ptr TopN::Optimize(unique_ptr op) { offset_val = limit.offset_val.GetConstantValue(); } auto topn = make_uniq(std::move(order_by.orders), limit_val, offset_val); + topn->unpruned_offset = limit.unpruned_offset; topn->AddChild(std::move(order_by.children[0])); auto cardinality = limit_val; if (topn->children[0]->has_estimated_cardinality && topn->children[0]->estimated_cardinality < limit_val) { diff --git a/src/duckdb/src/optimizer/topn_window_elimination.cpp b/src/duckdb/src/optimizer/topn_window_elimination.cpp index 12fbe65c0..586787929 100644 --- a/src/duckdb/src/optimizer/topn_window_elimination.cpp +++ b/src/duckdb/src/optimizer/topn_window_elimination.cpp @@ -5,7 +5,7 @@ #include "duckdb/common/helper.hpp" #include "duckdb/common/unordered_set.hpp" #include "duckdb/common/unique_ptr.hpp" -#include "duckdb/optimizer/builtin_function_lookup.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/optimizer/late_materialization_helper.hpp" #include "duckdb/planner/binder.hpp" #include "duckdb/planner/expression_nullability.hpp" @@ -275,6 +275,8 @@ unique_ptr TopNWindowElimination::OptimizeInternal(unique_ptr new_bindings; @@ -898,24 +900,10 @@ TopNWindowElimination::ExtractOptimizerParameters(const LogicalWindow &window, c params.partition_count = window_expr.Partitions().size(); params.order_type = window_expr.OrderBy()[0].type; - VisitExpression(&window_expr.OrderByMutable()[0].expression); - if (params.payload_type == TopNPayloadType::SINGLE_COLUMN && !aggregate_payload.empty()) { - VisitExpression(&aggregate_payload[0]); - } - for (const auto &column_ref : column_references) { - const auto &column_stats = stats->find(column_ref.first); - if (column_stats == stats->end() || column_stats->second->CanHaveNull()) { - params.can_be_null = true; - break; - } - } - // A compound ORDER BY expression (NULLIF, arithmetic, cast, ...) can produce NULLs even when its input - // columns are non-null. Only a direct column reference inherits the column's known nullability, so treat a - // non-column-reference as potentially null to keep the nulls-last aggregate variant. - if (window_expr.OrderBy()[0].expression->GetExpressionType() != ExpressionType::BOUND_COLUMN_REF) { - params.can_be_null = true; - } - column_references.clear(); + // The statistics of a column binding are those at the top of the plan: a filter above the window (e.g. on the + // result of a decorrelated LATERAL join) can have narrowed them to non-NULL although the input of the window + // contains NULLs. They cannot tell whether the window input can be NULL, so keep the NULL-preserving variants. + params.can_be_null = true; return params; } diff --git a/src/duckdb/src/optimizer/type_pushdown.cpp b/src/duckdb/src/optimizer/type_pushdown.cpp index 2e36ffe2b..491f4402e 100644 --- a/src/duckdb/src/optimizer/type_pushdown.cpp +++ b/src/duckdb/src/optimizer/type_pushdown.cpp @@ -87,12 +87,20 @@ void FindGetsAndProjections(LogicalOperator &op, Analyses &analyses, Projections } } +// A column binding into a GET is an index into its column ids, not a column id +// itself. Virtual columns (e.g. "filename") have no entry in returned_types, so +// nothing can be pushed into them. +static bool IsRealGetColumn(const LogicalGet &get, ProjectionIndex column_index) { + const auto &column_ids = get.GetColumnIds(); + return column_index < column_ids.size() && !column_ids[column_index].IsVirtualColumn(); +} + optional Resolve(ColumnBinding binding, Analyses &analyses, const Projections &projections) { - if (IsVirtualColumn(binding.column_index)) { - return nullopt; - } if (const auto it = analyses.find(binding.table_index); it != analyses.end()) { - return {{it->second, binding.column_index, nullptr}}; + if (!IsRealGetColumn(it->second.get, binding.column_index)) { + return nullopt; + } + return {{it->second, binding.column_index, nullptr, binding.column_index}}; } const auto projection_it = projections.find(binding.table_index); @@ -106,11 +114,11 @@ optional Resolve(ColumnBinding binding, Analyses &analyses, const Pr return nullopt; } const ColumnBinding get_binding = inner->Cast().Binding(); - if (IsVirtualColumn(get_binding.column_index)) { - return nullopt; - } if (const auto it = analyses.find(get_binding.table_index); it != analyses.end()) { - return {{it->second, get_binding.column_index, &projection}}; + if (!IsRealGetColumn(it->second.get, get_binding.column_index)) { + return nullopt; + } + return {{it->second, get_binding.column_index, &projection, binding.column_index}}; } return nullopt; } @@ -236,7 +244,7 @@ unique_ptr CastReplace::VisitReplace(BoundColumnRefExpression &expr, return std::move(*ptr); } - const auto &[analysis, column_index, projection] = *binding; + const auto &[analysis, column_index, projection, projection_column_index] = *binding; if (CanPushdownColumn(analysis, column_index)) { const LogicalType return_type = analysis.get.returned_types[analysis.StorageIndex(column_index)]; expr.SetReturnType(return_type); @@ -244,7 +252,7 @@ unique_ptr CastReplace::VisitReplace(BoundColumnRefExpression &expr, // LogicalProjection::ResolveTypes, so we need to check whether types in // projection have been resolved, and updated them only if needed. if (projection != nullptr && !projection->types.empty()) { - projection->types[column_index] = return_type; + projection->types[projection_column_index] = return_type; } } @@ -265,7 +273,7 @@ unique_ptr CastReplace::VisitReplace(BoundFunctionExpression &expr, return nullptr; } - const auto &[analysis, column_index, projection] = *binding; + const auto &[analysis, column_index, projection, projection_column_index] = *binding; if (!CanPushdownColumn(analysis, column_index)) { return std::move(*ptr); } @@ -274,7 +282,7 @@ unique_ptr CastReplace::VisitReplace(BoundFunctionExpression &expr, bound_col_base->SetReturnType(return_type); // Same as in CastReplace::VisitReplace(BoundColumnRefExpression) if (projection != nullptr && !projection->types.empty()) { - projection->types[column_index] = return_type; + projection->types[projection_column_index] = return_type; } return std::move(bound_col_base); } diff --git a/src/duckdb/src/optimizer/unnest_rewriter.cpp b/src/duckdb/src/optimizer/unnest_rewriter.cpp index 1414f571f..827158c14 100644 --- a/src/duckdb/src/optimizer/unnest_rewriter.cpp +++ b/src/duckdb/src/optimizer/unnest_rewriter.cpp @@ -1,5 +1,7 @@ #include "duckdb/optimizer/unnest_rewriter.hpp" +#include "duckdb/planner/expression/bound_cast_expression.hpp" + #include "duckdb/common/assert.hpp" #include "duckdb/common/enums/expression_type.hpp" #include "duckdb/common/helper.hpp" @@ -168,7 +170,7 @@ void UnnestRewriter::FindCandidates(unique_ptr &root, unique_pt curr_op = &delim_join.children[other_idx]; if (curr_op->get()->type == LogicalOperatorType::LOGICAL_GET) { auto &get = curr_op->get()->Cast(); - if (!ExpressionBinder::IsUnnestFunction(get.function.name)) { + if (!ExpressionBinder::IsUnnestFunction(get.function.GetName())) { return; } // pattern2: delim_get -> projection -> table_in_out(unnest) @@ -217,7 +219,8 @@ void UnnestRewriter::FindCandidates(unique_ptr &root, unique_pt } auto &bind_col = proj.expressions[col_bind.column_index]->Cast(); auto unnest_expr = make_uniq(unnest_get->types[i]); - unnest_expr->ChildMutable() = proj.expressions[col_bind.column_index]->Copy(); + unnest_expr->ChildMutable() = + BoundCastExpression::AddArrayCastToList(context, proj.expressions[col_bind.column_index]->Copy()); bind_col.BindingMutable() = ColumnBinding(unnest_get_index, bind_col.Binding().column_index); auto unnest_proj_idx = ColumnBinding::PushExpression(unnest->expressions, std::move(unnest_expr)); ColumnBinding new_column_ref(bind_col.Binding().table_index, unnest_proj_idx); @@ -236,13 +239,14 @@ void UnnestRewriter::FindCandidates(unique_ptr &root, unique_pt } } -static bool ConvertCTETableInOutUnnest(unique_ptr &root, unique_ptr &op, - TableIndex input_cte_index, bool require_input_cte_ref = true) { +static bool ConvertCTETableInOutUnnest(ClientContext &context, unique_ptr &root, + unique_ptr &op, TableIndex input_cte_index, + bool require_input_cte_ref = true) { if (op->type != LogicalOperatorType::LOGICAL_GET) { return false; } auto &get = op->Cast(); - if (!ExpressionBinder::IsUnnestFunction(get.function.name) || get.ordinality_idx.IsValid()) { + if (!ExpressionBinder::IsUnnestFunction(get.function.GetName()) || get.ordinality_idx.IsValid()) { return false; } if (op->children.size() != 1 || op->children[0]->type != LogicalOperatorType::LOGICAL_PROJECTION) { @@ -284,7 +288,8 @@ static bool ConvertCTETableInOutUnnest(unique_ptr &root, unique } auto &bind_col = proj.expressions[col_bind.column_index]->Cast(); auto unnest_expr = make_uniq(unnest_get->types[i]); - unnest_expr->ChildMutable() = proj.expressions[col_bind.column_index]->Copy(); + unnest_expr->ChildMutable() = + BoundCastExpression::AddArrayCastToList(context, proj.expressions[col_bind.column_index]->Copy()); bind_col.BindingMutable() = ColumnBinding(unnest_get_index, bind_col.Binding().column_index); auto unnest_proj_idx = ColumnBinding::PushExpression(unnest->expressions, std::move(unnest_expr)); ColumnBinding new_column_ref(bind_col.Binding().table_index, unnest_proj_idx); @@ -416,7 +421,7 @@ bool UnnestRewriter::RewriteInlineCTEDedupCandidate(unique_ptr idx_t other_side = 1 - domain_side.GetIndex(); auto domain_ref_bindings = join.children[domain_side.GetIndex()]->GetColumnBindings(); if (join.children[other_side]->type == LogicalOperatorType::LOGICAL_GET && - !ConvertCTETableInOutUnnest(root, join.children[other_side], domain_cte.table_index, false)) { + !ConvertCTETableInOutUnnest(context, root, join.children[other_side], domain_cte.table_index, false)) { return false; } @@ -580,7 +585,7 @@ bool UnnestRewriter::RewriteCTECandidate(unique_ptr &root, uniq idx_t other_side = 1 - domain_side.GetIndex(); if (join.children[other_side]->type == LogicalOperatorType::LOGICAL_GET && - !ConvertCTETableInOutUnnest(root, join.children[other_side], dedup_cte.table_index)) { + !ConvertCTETableInOutUnnest(context, root, join.children[other_side], dedup_cte.table_index)) { return false; } diff --git a/src/duckdb/src/parallel/executor.cpp b/src/duckdb/src/parallel/executor.cpp index 03953951e..8beb6d811 100644 --- a/src/duckdb/src/parallel/executor.cpp +++ b/src/duckdb/src/parallel/executor.cpp @@ -7,6 +7,7 @@ #include "duckdb/execution/operator/set/physical_cte.hpp" #include "duckdb/execution/operator/set/physical_recursive_cte.hpp" #include "duckdb/execution/physical_operator.hpp" +#include "duckdb/main/buffered_data/buffered_data.hpp" #include "duckdb/main/client_context.hpp" #include "duckdb/main/client_data.hpp" #include "duckdb/main/settings.hpp" @@ -18,6 +19,7 @@ #include "duckdb/parallel/pipeline_initialize_event.hpp" #include "duckdb/parallel/pipeline_prepare_finish_event.hpp" #include "duckdb/parallel/pipeline_schedule.hpp" +#include "duckdb/parallel/progress_verifier.hpp" #include "duckdb/parallel/task_scheduler.hpp" #include "duckdb/parallel/thread_context.hpp" @@ -240,6 +242,12 @@ void Executor::InitializeInternal(PhysicalOperator &plan) { this->profiler = ClientData::Get(context).profiler; this->producer = scheduler.CreateProducer(); + auto verify_progress = Settings::Get(context); + if (verify_progress != DebugProgressVerification::NONE) { + progress_verifier = make_uniq(context, verify_progress, + Settings::Get(context)); + } + // build and ready the pipelines PipelineBuildState state; auto root_pipeline = make_shared_ptr(*this, state, nullptr); @@ -277,6 +285,10 @@ void Executor::InitializeInternal(PhysicalOperator &plan) { void Executor::CancelTasks() { task.reset(); + { + lock_guard guard(result_buffer_lock); + result_buffer.reset(); + } reference_map_t> to_destroy; { lock_guard elock(executor_lock); @@ -372,7 +384,7 @@ void Executor::WaitForTask() { return; } if (ResultCollectorIsBlocked()) { - // If the result collector is blocked, it won't get unblocked until the connection calls Fetch + // Only the consumer's decision or pop lets the query progress, so waiting here is pointless blocked_thread_time += blocked_micros; return; } @@ -419,8 +431,8 @@ void Executor::AddToBeRescheduled(shared_ptr &task_p) { // Save the reference before move — evaluation order of operator[] key and assignment value is unspecified pre-C++17 auto &task_ref = *task_p; to_be_rescheduled_tasks[task_ref] = std::move(task_p); - // Only a result-sink park needs the consumer, so only a streaming plan wakes the waiting consumer - if (HasStreamingResultCollector()) { + // Only a result-sink park needs the consumer, so only a store that can park wakes it + if (ResultStoreCanPark()) { task_reschedule.notify_all(); } } @@ -429,48 +441,26 @@ bool Executor::ExecutionIsFinished() { return completed_pipelines >= total_pipelines || HasError(); } -PendingExecutionResult Executor::ExecuteTask(bool dry_run) { +QueryResultState Executor::ExecuteTask() { // Only executor should return NO_TASKS_AVAILABLE - D_ASSERT(execution_result != PendingExecutionResult::NO_TASKS_AVAILABLE); - if (execution_result != PendingExecutionResult::RESULT_NOT_READY && ExecutionIsFinished()) { + D_ASSERT(execution_result != QueryResultState::NO_TASKS_AVAILABLE); + if (execution_result != QueryResultState::NOT_READY && ExecutionIsFinished()) { return execution_result; } - // check if there are any incomplete pipelines - auto &scheduler = TaskScheduler::GetScheduler(context); if (completed_pipelines < total_pipelines) { - // there are! if we don't already have a task, fetch one - auto current_task = task.get(); - if (dry_run) { - // Pretend we have no task, we don't want to execute anything - current_task = nullptr; - } else { - if (!task) { - scheduler.GetTaskFromProducer(*producer, task); - } - current_task = task.get(); + if (!task) { + TaskScheduler::GetScheduler(context).GetTaskFromProducer(*producer, task); } - - if (!current_task && !HasError()) { - // there are no tasks to be scheduled and there are tasks blocked - lock_guard l(executor_lock); - if (to_be_rescheduled_tasks.empty()) { - return PendingExecutionResult::NO_TASKS_AVAILABLE; - } - // At least one task is blocked - if (ResultCollectorIsBlocked()) { - return PendingExecutionResult::RESULT_READY; - } - return PendingExecutionResult::BLOCKED; + if (!task && !HasError()) { + return IdleState(); } - - if (current_task) { - // if we have a task, partially process it + if (task) { + // partially process the task auto result = task->Execute(TaskExecutionMode::PROCESS_PARTIAL); if (result == TaskExecutionResult::TASK_BLOCKED) { task->Deschedule(); task.reset(); } else if (result == TaskExecutionResult::TASK_FINISHED) { - // if the task is finished, clean it up task.reset(); } else if (result == TaskExecutionResult::TASK_ERROR) { if (!HasError()) { @@ -488,26 +478,64 @@ PendingExecutionResult Executor::ExecuteTask(bool dry_run) { TaskScheduler::GetScheduler(context).ScheduleTask(token, task); task.reset(); } - return PendingExecutionResult::RESULT_NOT_READY; + return QueryResultState::NOT_READY; } - execution_result = PendingExecutionResult::EXECUTION_ERROR; + FailExecution(); + } + return FinishExecution(); +} - // an exception has occurred executing one of the pipelines - // we need to cancel all tasks associated with this executor - CancelTasks(); - ThrowException(); +QueryResultState Executor::Poll() { + D_ASSERT(execution_result != QueryResultState::NO_TASKS_AVAILABLE); + if (execution_result != QueryResultState::NOT_READY && ExecutionIsFinished()) { + return execution_result; } - D_ASSERT(!task); + if (completed_pipelines < total_pipelines) { + if (!HasError()) { + return IdleState(); + } + FailExecution(); + } + return FinishExecution(); +} +QueryResultState Executor::IdleState() { + lock_guard l(executor_lock); + if (to_be_rescheduled_tasks.empty()) { + return QueryResultState::NO_TASKS_AVAILABLE; + } + // At least one task is blocked + if (ResultCollectorIsBlocked()) { + return QueryResultState::READY; + } + return QueryResultState::BLOCKED; +} + +void Executor::FailExecution() { + execution_result = QueryResultState::EXECUTION_ERROR; + // an exception has occurred executing one of the pipelines + // we need to cancel all tasks associated with this executor + CancelTasks(); + ThrowException(); +} + +QueryResultState Executor::FinishExecution() { + D_ASSERT(!task); lock_guard elock(executor_lock); + if (progress_verifier && !HasError()) { + auto progress_error = progress_verifier->Finalize(pipelines); + if (!progress_error.empty()) { + error_manager.PushError(ErrorData(ExceptionType::INVALID_INPUT, progress_error)); + } + } pipelines.clear(); NextExecutor(); if (HasError()) { // LCOV_EXCL_START // an exception has occurred executing one of the pipelines - execution_result = PendingExecutionResult::EXECUTION_ERROR; + execution_result = QueryResultState::EXECUTION_ERROR; ThrowException(); } // LCOV_EXCL_STOP - execution_result = PendingExecutionResult::EXECUTION_FINISHED; + execution_result = QueryResultState::FINISHED; return execution_result; } @@ -521,10 +549,11 @@ void Executor::Reset() { completed_pipelines = 0; total_pipelines = 0; error_manager.Reset(); + progress_verifier.reset(); pipelines.clear(); events.clear(); to_be_rescheduled_tasks.clear(); - execution_result = PendingExecutionResult::RESULT_NOT_READY; + execution_result = QueryResultState::NOT_READY; } shared_ptr Executor::CreateChildPipeline(Pipeline ¤t, PhysicalOperator &op) { @@ -576,12 +605,11 @@ void Executor::ThrowException() { } void Executor::Flush(ThreadContext &thread_context) { - auto global_profiler = profiler; - if (global_profiler) { - global_profiler->Flush(thread_context.profiler); + if (profiler) { + profiler->Flush(thread_context.profiler); auto blocked_time = blocked_thread_time.load(); - global_profiler->SetBlockedTime(double(blocked_time) / 1000.0 / 1000.0); + profiler->SetBlockedTime(double(blocked_time) / 1000.0 / 1000.0); } } @@ -614,20 +642,44 @@ bool Executor::HasStreamingResultCollector() { return result_collector.IsStreaming(); } -bool Executor::ResultCollectorIsBlocked() { - // A sink retained by the plan never parks, so the materialized hot path skips the lock +void Executor::SetResultBuffer(shared_ptr result_buffer_p) { + lock_guard guard(result_buffer_lock); + result_buffer = std::move(result_buffer_p); +} + +shared_ptr Executor::GetResultBuffer() { + lock_guard guard(result_buffer_lock); + return result_buffer; +} + +bool Executor::ResultStoreCanPark() { if (!HasStreamingResultCollector()) { return false; } + auto buffer = GetResultBuffer(); + // The store was settled on retained at submission: producers append and never park + return !buffer || buffer->Lifetime() != ResultLifetime::RETAINED; +} + +bool Executor::ResultCollectorIsBlocked() { + // A store that cannot park never waits on the consumer, so the retained hot path skips the rest + if (!ResultStoreCanPark()) { + return false; + } + auto buffer = GetResultBuffer(); + if (buffer) { + return buffer->WaitsOnConsumer(); + } auto &result_collector = physical_plan->Cast(); // The sink state is published by a pipeline initialize task on a worker, under the - // operator lock. Read it under the same lock, or the client can observe the pointer - // before the pointee is visible + // operator lock. Read it under the same lock, or readiness is reported before GetResult + // has a state to fetch from lock_guard guard(result_collector.lock); if (!result_collector.sink_state) { return false; } - return result_collector.HasBlockedResultProducer(*result_collector.sink_state); + // A custom streaming collector has no parked-producer notion, and must never be waited on forever + return result_collector.IsStreaming(); } unique_ptr Executor::GetResult() { diff --git a/src/duckdb/src/parallel/pipeline.cpp b/src/duckdb/src/parallel/pipeline.cpp index cc6d0d44f..40232b81d 100644 --- a/src/duckdb/src/parallel/pipeline.cpp +++ b/src/duckdb/src/parallel/pipeline.cpp @@ -17,6 +17,7 @@ #include "duckdb/parallel/pipeline_event.hpp" #include "duckdb/parallel/pipeline_executor.hpp" #include "duckdb/parallel/pipeline_schedule.hpp" +#include "duckdb/parallel/progress_verifier.hpp" #include "duckdb/parallel/task_scheduler.hpp" #include "duckdb/main/settings.hpp" #include "duckdb/storage/buffer_manager.hpp" @@ -80,33 +81,41 @@ ClientContext &Pipeline::GetClientContext() { } bool Pipeline::GetProgress(ProgressData &progress) { + PipelineProgress detailed_progress; + GetDetailedProgress(detailed_progress); + progress = detailed_progress.pipeline; + return progress.IsValid(); +} + +void Pipeline::GetDetailedProgress(PipelineProgress &progress) { D_ASSERT(source); idx_t source_cardinality = MinValue(source->estimated_cardinality, 1ULL << 48ULL); if (source_cardinality < 1) { source_cardinality = 1; } if (!initialized) { - progress.done = 0; - progress.total = double(source_cardinality); - return true; + progress.source.done = 0; + progress.source.total = double(source_cardinality); + progress.pipeline = progress.source; + return; } auto &client = executor.context; auto state = GetSourceState(); if (state) { - progress = source->GetProgress(client, *state); + progress.source = source->GetProgress(client, *state); } else { - progress.done = 0; - progress.total = double(source_cardinality); + progress.source.done = 0; + progress.source.total = double(source_cardinality); } - progress.Normalize(double(source_cardinality)); + progress.pipeline = progress.source; + progress.pipeline.Normalize(double(source_cardinality)); if (sink) { lock_guard guard(sink->lock); if (sink->sink_state) { - progress = sink->GetSinkProgress(client, *sink->sink_state, progress); + progress.pipeline = sink->GetSinkProgress(client, *sink->sink_state, progress.pipeline); } } - return progress.IsValid(); } void Pipeline::ScheduleSequentialTask(shared_ptr &event) { @@ -353,6 +362,7 @@ void Pipeline::ResetSinkForReschedule() { if (!sink->IsSink()) { throw InternalException("Sink of pipeline does not have IsSink set"); } + NotifyProgressReset(); lock_guard guard(sink->lock); auto &client = GetClientContext(); auto allow_reuse = Settings::Get(client); @@ -394,6 +404,7 @@ void Pipeline::Reset() { } void Pipeline::ResetForReschedule(bool reset_sink) { + NotifyProgressReset(); if (reset_sink) { ResetSinkForReschedule(); } @@ -423,12 +434,20 @@ void Pipeline::ResetForReschedule(bool reset_sink) { initialized = true; } +void Pipeline::NotifyProgressReset() { + auto progress_verifier = executor.GetProgressVerifier(); + if (progress_verifier) { + progress_verifier->OnReset(*this); + } +} + void Pipeline::ResetSource(bool force) { if (source && !source->IsSource()) { throw InternalException("Source of pipeline does not have IsSource set"); } auto source_state = GetSourceState(); if (force || !source_state) { + NotifyProgressReset(); auto partition_info = sink ? sink->RequiredPartitionInfo() : OperatorPartitionInfo::NoPartitionInfo(); SetSourceState(ToSharedSourceState(source->GetGlobalSourceState(GetClientContext(), partition_info))); } diff --git a/src/duckdb/src/parallel/pipeline_broadcast_exchange.cpp b/src/duckdb/src/parallel/pipeline_broadcast_exchange.cpp index c455bf010..84db381bd 100644 --- a/src/duckdb/src/parallel/pipeline_broadcast_exchange.cpp +++ b/src/duckdb/src/parallel/pipeline_broadcast_exchange.cpp @@ -120,7 +120,7 @@ struct PipelineBroadcastExchange::BroadcastSpool { collection.InitializeAppend(append_state); } - void Append(DataChunk &chunk, idx_t batch_index) { + void Append(DataChunk &chunk, idx_t batch_index) DUCKDB_EXCLUDES(lock) { annotated_lock_guard guard(lock); D_ASSERT(chunk.size() > 0); auto row_offset = collection.Count(); @@ -130,18 +130,18 @@ struct PipelineBroadcastExchange::BroadcastSpool { append_generation++; } - void Read(idx_t position, DataChunk &result, BroadcastSpoolReader &reader) { - annotated_lock_guard guard(lock); - if (position < base_position || position >= next_position) { - throw InternalException("Attempted to read retired pipeline broadcast spool chunk"); + void Read(idx_t position, const ChunkEntry &entry, DataChunk &result, BroadcastSpoolReader &reader, + bool producer_finished) DUCKDB_EXCLUDES(lock) { + // Completion is observed under the exchange mutex; collection metadata and append_generation are then + // immutable. + annotated_unique_lock guard(lock, std::defer_lock); + if (!producer_finished) { + guard.lock(); } - auto chunk_idx = position - base_position; - D_ASSERT(chunk_idx < chunks.size()); - auto &entry = chunks[chunk_idx]; result.Reset(); auto row_idx = entry.row_offset; - auto offset = SeekLocked(position, entry, row_idx, reader); + auto offset = Seek(position, entry, row_idx, reader); auto available = reader.scan_chunk.size() - offset; if (available >= entry.row_count) { if (offset == 0 && entry.row_count == reader.scan_chunk.size()) { @@ -155,7 +155,7 @@ struct PipelineBroadcastExchange::BroadcastSpool { idx_t rows_read = 0; while (rows_read < entry.row_count) { row_idx = entry.row_offset + rows_read; - offset = SeekLocked(position, entry, row_idx, reader); + offset = Seek(position, entry, row_idx, reader); auto append_count = MinValue(reader.scan_chunk.size() - offset, entry.row_count - rows_read); auto sel = SelectionVector::Incremental(offset, append_count); result.Append(reader.scan_chunk, sel, append_count); @@ -163,7 +163,7 @@ struct PipelineBroadcastExchange::BroadcastSpool { } } - idx_t RetireBefore(idx_t position) { + idx_t RetireBefore(idx_t position) DUCKDB_EXCLUDES(lock) { annotated_lock_guard guard(lock); idx_t retired_chunks = 0; while (!chunks.empty() && base_position < position) { @@ -174,41 +174,35 @@ struct PipelineBroadcastExchange::BroadcastSpool { return retired_chunks; } - bool HasPosition(idx_t position) { - annotated_lock_guard guard(lock); - return position >= base_position && position < next_position; - } - - idx_t BatchIndex(idx_t position) { + ChunkEntry GetChunkEntry(idx_t position) DUCKDB_EXCLUDES(lock) { annotated_lock_guard guard(lock); if (position < base_position || position >= next_position) { throw InternalException("Attempted to inspect retired pipeline broadcast spool chunk"); } - return chunks[position - base_position].batch_index; + return chunks[position - base_position]; } - void InitializeReader(BroadcastSpoolReader &reader) { + void InitializeReader(BroadcastSpoolReader &reader) DUCKDB_EXCLUDES(lock) { annotated_lock_guard guard(lock); - ResetReaderLocked(reader); + ResetReader(reader); collection.InitializeScanChunk(reader.scan_state, reader.scan_chunk); } - void ResetReaderLocked(BroadcastSpoolReader &reader) { + void ResetReader(BroadcastSpoolReader &reader) { collection.InitializeScan(reader.scan_state, ColumnDataScanProperties::ALLOW_ZERO_COPY); reader.observed_generation = append_generation; } - idx_t SeekLocked(idx_t position, const ChunkEntry &entry, idx_t row_idx, BroadcastSpoolReader &reader) { + idx_t Seek(idx_t position, const ChunkEntry &entry, idx_t row_idx, BroadcastSpoolReader &reader) { if (reader.observed_generation != append_generation && !reader.Contains(row_idx)) { - ResetReaderLocked(reader); + ResetReader(reader); } if (!collection.Seek(row_idx, reader.scan_state, reader.scan_chunk)) { throw InternalException( - "Failed to read pipeline broadcast spool chunk (position=%llu, base=%llu, next=%llu, row=%llu, " + "Failed to read pipeline broadcast spool chunk (position=%llu, row=%llu, " "row_offset=%llu, row_count=%llu, current=%llu, next_row=%llu, generation=%llu, observed=%llu)", - position, base_position, next_position, row_idx, entry.row_offset, entry.row_count, - reader.scan_state.current_row_index, reader.scan_state.next_row_index, append_generation, - reader.observed_generation); + position, row_idx, entry.row_offset, entry.row_count, reader.scan_state.current_row_index, + reader.scan_state.next_row_index, append_generation, reader.observed_generation); } D_ASSERT(reader.scan_state.current_row_index <= row_idx); auto offset = row_idx - reader.scan_state.current_row_index; @@ -218,10 +212,10 @@ struct PipelineBroadcastExchange::BroadcastSpool { annotated_mutex lock; ColumnDataCollection collection; - ColumnDataAppendState append_state; - deque chunks; - idx_t base_position; - idx_t next_position; + ColumnDataAppendState append_state DUCKDB_GUARDED_BY(lock); + deque chunks DUCKDB_GUARDED_BY(lock); + idx_t base_position DUCKDB_GUARDED_BY(lock); + idx_t next_position DUCKDB_GUARDED_BY(lock); idx_t append_generation = 0; }; @@ -240,6 +234,7 @@ struct PipelineBroadcastExchange::AppendReservation { struct PipelineBroadcastExchange::SpoolReadReservation { shared_ptr spool; shared_ptr reader; + BroadcastSpool::ChunkEntry entry; idx_t position = 0; optional_idx batch_sequence; @@ -489,16 +484,14 @@ struct PipelineBroadcastExchange::BufferState { optional_idx &batch_index, SpoolReadReservation &spool_read) const { D_ASSERT(position < next_position); if (shared_spool) { - if (!shared_spool->HasPosition(position)) { - throw InternalException("Pipeline broadcast shared spool chunk was retired before it was read"); - } + spool_read.entry = shared_spool->GetChunkEntry(position); if (!reader) { reader = make_shared_ptr(*shared_spool); } spool_read.spool = shared_spool; spool_read.reader = reader; spool_read.position = position; - auto stored_batch_index = shared_spool->BatchIndex(position); + auto stored_batch_index = spool_read.entry.batch_index; if (stored_batch_index != DConstants::INVALID_INDEX) { batch_index = stored_batch_index; } @@ -1386,6 +1379,7 @@ void PipelineBroadcastExchange::Finish() { } buffer->FinishBatches(); if (producer_state == ProducerState::ACTIVE) { + D_ASSERT(append_reservation_state == AppendReservationState::IDLE); producer_state = ProducerState::FINISHED; } TryReleaseBufferedStorageLocked(); @@ -1436,6 +1430,7 @@ SourceResultType PipelineBroadcastExchange::Scan(idx_t consumer_idx, DataChunk & vector log_entries; shared_ptr next_chunk; SpoolReadReservation spool_read; + bool producer_finished; SourceResultType result; batch_index = optional_idx(); batch_index_state = SourceBatchIndexState::UNCHANGED; @@ -1443,11 +1438,12 @@ SourceResultType PipelineBroadcastExchange::Scan(idx_t consumer_idx, DataChunk & annotated_lock_guard guard(lock); result = ReserveScanLocked(consumer_idx, interrupt_state, scan_state, next_chunk, batch_index, batch_index_state, spool_read, readers, writers, log_entries); + producer_finished = producer_state == ProducerState::FINISHED; } if (spool_read.IsSet()) { try { - spool_read.spool->Read(spool_read.position, chunk, *spool_read.reader); + spool_read.spool->Read(spool_read.position, spool_read.entry, chunk, *spool_read.reader, producer_finished); } catch (...) { vector failed_readers; vector failed_writers; @@ -1767,24 +1763,23 @@ ProgressData PipelineBroadcastExchange::SinkProgress(const ProgressData &source_ idx_t estimated_cardinality) const { annotated_lock_guard guard(lock); ProgressData progress; - auto produced_count = produced_rows.load(std::memory_order_relaxed); - auto produced = double(produced_count); if (producer_state != ProducerState::ACTIVE) { - auto total = MaxValue(produced, 1.0); - progress.done = total; - progress.total = total; + progress.done = 1.0; + progress.total = 1.0; return progress; } - progress.done = produced; if (source_progress.IsValid()) { - progress.total = produced + MaxValue(source_progress.total - source_progress.done, 1.0); + // the producing pipeline is done once its source is exhausted + return source_progress; + } + auto produced_count = produced_rows.load(std::memory_order_relaxed); + auto produced = double(produced_count); + progress.done = produced; + static constexpr const idx_t MAX_PROGRESS_CARDINALITY = 1ULL << 48ULL; + if (estimated_cardinality > 0 && estimated_cardinality < MAX_PROGRESS_CARDINALITY) { + progress.total = double(MaxValue(estimated_cardinality, produced_count + 1)); } else { - static constexpr const idx_t MAX_PROGRESS_CARDINALITY = 1ULL << 48ULL; - if (estimated_cardinality > 0 && estimated_cardinality < MAX_PROGRESS_CARDINALITY) { - progress.total = double(MaxValue(estimated_cardinality, produced_count + 1)); - } else { - progress.total = produced + 1.0; - } + progress.total = produced + 1.0; } if (progress.done > progress.total) { progress.total = progress.done; diff --git a/src/duckdb/src/parallel/pipeline_executor.cpp b/src/duckdb/src/parallel/pipeline_executor.cpp index b73e3b689..e61574823 100644 --- a/src/duckdb/src/parallel/pipeline_executor.cpp +++ b/src/duckdb/src/parallel/pipeline_executor.cpp @@ -5,6 +5,7 @@ #include "duckdb/main/client_context.hpp" #include "duckdb/main/settings.hpp" +#include "duckdb/parallel/progress_verifier.hpp" #ifdef DUCKDB_DEBUG_ASYNC_SINK_SOURCE #include @@ -61,7 +62,7 @@ PipelineExecutor::PipelineExecutor(ClientContext &context_p, Pipeline &pipeline_ auto ¤t_operator = pipeline.operators[i].get(); auto chunk = make_uniq(); - chunk->Initialize(BufferAllocator::Get(context.client), prev_operator.GetTypes()); + prev_operator.InitializeOutputChunk(BufferAllocator::Get(context.client), *chunk); intermediate_chunks.push_back(std::move(chunk)); auto op_state = current_operator.GetOperatorState(context); @@ -92,6 +93,7 @@ void PipelineExecutor::Reset() { source_profiling_finalized = false; source_finish_notification_state = SourceFinishNotificationState::PENDING; should_flush_current_idx = true; + unsampled_progress_rows = 0; while (!in_process_operators.empty()) { in_process_operators.pop(); } @@ -706,6 +708,12 @@ PipelineExecuteResult PipelineExecutor::PushFinalize() { finalized = true; NotifySourceFinished(); + SampleProgress(); + auto progress_verifier = pipeline.executor.GetProgressVerifier(); + if (progress_verifier && !exhausted_source && !pipeline.IsExternalInput()) { + progress_verifier->OnEarlyExit(pipeline); + } + // If source was not exhausted (e.g. LIMIT stopped the pipeline), collect exact metrics now if (!source_profiling_finalized && local_source_state && global_source_state) { context.thread.profiler.FinishSource(*pipeline.source, *global_source_state, *local_source_state); @@ -851,6 +859,8 @@ PipelineExecutor::SourceFetchResult PipelineExecutor::FetchFromSource(DataChunk D_ASSERT(!pipeline.IsExternalInput()); D_ASSERT(global_source_state); D_ASSERT(local_source_state); + // the previous chunk has been pushed through the pipeline + SampleProgress(); StartOperator(*pipeline.source); OperatorSourceInput source_input = {*global_source_state, *local_source_state, interrupt_state}; @@ -869,12 +879,24 @@ PipelineExecutor::SourceFetchResult PipelineExecutor::FetchFromSource(DataChunk } EndOperator(*pipeline.source, &result); + if (pipeline.executor.GetProgressVerifier()) { + unsampled_progress_rows = result.size(); + } return fetch_result; } +void PipelineExecutor::SampleProgress() { + auto progress_verifier = pipeline.executor.GetProgressVerifier(); + if (!progress_verifier || unsampled_progress_rows == 0) { + return; + } + progress_verifier->OnSourceChunk(pipeline, unsampled_progress_rows); + unsampled_progress_rows = 0; +} + void PipelineExecutor::InitializeChunk(DataChunk &chunk) { auto &last_op = pipeline.operators.empty() ? *pipeline.source : pipeline.operators.back().get(); - chunk.Initialize(BufferAllocator::Get(context.client), last_op.GetTypes()); + last_op.InitializeOutputChunk(BufferAllocator::Get(context.client), chunk); } void PipelineExecutor::StartOperator(PhysicalOperator &op) { diff --git a/src/duckdb/src/parallel/progress_verifier.cpp b/src/duckdb/src/parallel/progress_verifier.cpp new file mode 100644 index 000000000..4b0b02ee8 --- /dev/null +++ b/src/duckdb/src/parallel/progress_verifier.cpp @@ -0,0 +1,374 @@ +#include "duckdb/parallel/progress_verifier.hpp" + +#include "duckdb/common/enum_util.hpp" +#include "duckdb/common/string_util.hpp" +#include "duckdb/execution/operator/scan/physical_table_scan.hpp" +#include "duckdb/logging/logger.hpp" +#include "duckdb/main/client_context.hpp" +#include "duckdb/parallel/pipeline.hpp" + +#include + +namespace duckdb { + +//! Tolerance used when comparing progress fractions +static constexpr double PROGRESS_EPSILON = 1e-6; +//! Tolerance for a completed pipeline to count as 100% done +static constexpr double COMPLETION_EPSILON = 1e-3; +//! Minimum number of chunks before invalid progress or a missing 100% is flagged +static constexpr idx_t MIN_CHUNKS_SUPPORTED = 2; +//! Minimum number of rows before non-moving or inaccurate progress is flagged +static constexpr idx_t MIN_ROWS_GRANULARITY = 16384; +//! Maximum deviation between the source progress and the fraction of rows produced +static constexpr double MAX_INACCURACY = 0.5; + +ProgressVerifier::ProgressVerifier(ClientContext &context, DebugProgressVerification mode, const string &ignore_str) + : context(context), mode(mode) { + for (auto &entry : StringUtil::Split(ignore_str, ',')) { + auto trimmed = entry; + StringUtil::Trim(trimmed); + if (!trimmed.empty()) { + ignore_list.insert(StringUtil::Upper(trimmed)); + } + } +} + +string ProgressVerifier::OperatorName(const PhysicalOperator &op) { + if (op.type == PhysicalOperatorType::TABLE_SCAN) { + // table functions implement progress individually - attribute to the function + return StringUtil::Upper(op.Cast().function.GetName().GetIdentifierName()); + } + return PhysicalOperatorToString(op.type); +} + +bool ProgressVerifier::FailsQuery(ProgressInvariant invariant) { + // selective filters and skewed data legitimately make source progress deviate from the rows produced + return invariant != ProgressInvariant::INACCURATE; +} + +static bool IsWellFormed(const ProgressData &progress) { + return std::isfinite(progress.done) && std::isfinite(progress.total) && progress.done >= 0 && progress.total >= 0 && + progress.done <= progress.total * (1 + PROGRESS_EPSILON) + PROGRESS_EPSILON; +} + +static double ProgressFraction(const ProgressData &progress) { + if (progress.total <= 0) { + return 0; + } + return MinValue(progress.done / progress.total, 1.0); +} + +static string FormatPercentage(double fraction) { + return StringUtil::Format("%.1f%%", fraction * 100); +} + +static string FormatProgress(const ProgressData &progress) { + return StringUtil::Format("done=%g, total=%g", progress.done, progress.total); +} + +ProgressVerifier::PipelineTrace &ProgressVerifier::GetTrace(Pipeline &pipeline) { + lock_guard guard(lock); + auto entry = traces.find(&pipeline); + if (entry != traces.end()) { + return *entry->second; + } + auto trace = make_uniq(); + trace->source_name = OperatorName(*pipeline.GetSource()); + trace->sink_name = pipeline.GetSink() ? OperatorName(*pipeline.GetSink()) : "NONE"; + auto &result = *trace; + traces[&pipeline] = std::move(trace); + return result; +} + +void ProgressVerifier::Sample(Pipeline &pipeline, PipelineTrace &trace, bool is_final) { + PipelineProgress progress; + pipeline.GetDetailedProgress(progress); + + auto &run = trace.run; + const auto sample_idx = run.samples.size(); + ProgressSample sample; + sample.source_fraction = std::nan(""); + sample.fraction = std::nan(""); + sample.rows = run.rows; + + // invalid progress after completion never reaches the user - only flag it while the pipeline is running + if (progress.source.invalid) { + if (!is_final && !run.first_unsupported_source.IsValid()) { + run.first_unsupported_source = sample_idx; + } + } else if (!IsWellFormed(progress.source)) { + AddViolation(trace, ProgressInvariant::MALFORMED_SOURCE, trace.source_name, FormatProgress(progress.source)); + } else { + sample.source_fraction = ProgressFraction(progress.source); + if (progress.pipeline.invalid) { + if (!is_final && !run.first_unsupported_sink.IsValid()) { + run.first_unsupported_sink = sample_idx; + } + } else if (!IsWellFormed(progress.pipeline)) { + AddViolation(trace, ProgressInvariant::MALFORMED_SINK, trace.sink_name, + StringUtil::Format("%s (source %s)", FormatProgress(progress.pipeline), + FormatProgress(progress.source))); + } else { + sample.fraction = ProgressFraction(progress.pipeline); + } + } + run.samples.push_back(sample); + run.has_final_sample = is_final; +} + +void ProgressVerifier::OnSourceChunk(Pipeline &pipeline, idx_t count) { + auto &trace = GetTrace(pipeline); + lock_guard guard(trace.lock); + trace.run.rows += count; + trace.run.chunks++; + Sample(pipeline, trace, false); +} + +void ProgressVerifier::OnEarlyExit(Pipeline &pipeline) { + auto &trace = GetTrace(pipeline); + lock_guard guard(trace.lock); + trace.run.early_exit = true; +} + +void ProgressVerifier::OnReset(Pipeline &pipeline) { + optional_ptr trace; + { + lock_guard guard(lock); + auto entry = traces.find(&pipeline); + if (entry == traces.end()) { + return; + } + trace = entry->second.get(); + } + lock_guard guard(trace->lock); + if (trace->run.samples.empty() && !trace->run.early_exit) { + return; + } + VerifyRun(*trace); + trace->run = PipelineRun(); +} + +static optional_idx FindDecrease(const vector &fractions) { + double previous = -1; + for (idx_t i = 0; i < fractions.size(); i++) { + if (std::isnan(fractions[i])) { + continue; + } + if (previous >= 0 && fractions[i] < previous - PROGRESS_EPSILON) { + return i; + } + previous = fractions[i]; + } + return optional_idx(); +} + +static double PreviousValid(const vector &fractions, idx_t idx) { + for (idx_t i = idx; i > 0; i--) { + if (!std::isnan(fractions[i - 1])) { + return fractions[i - 1]; + } + } + return 0; +} + +//! Returns the number of valid fractions, and whether they all have the same value +static idx_t CountConstant(const vector &fractions, bool &constant, double &value) { + idx_t count = 0; + double min_value = 2; + double max_value = -1; + for (auto fraction : fractions) { + if (std::isnan(fraction)) { + continue; + } + count++; + min_value = MinValue(min_value, fraction); + max_value = MaxValue(max_value, fraction); + } + constant = count > 0 && max_value - min_value < PROGRESS_EPSILON; + value = min_value; + return count; +} + +void ProgressVerifier::VerifyRun(PipelineTrace &trace) { + auto &run = trace.run; + if (run.samples.empty()) { + return; + } + vector source_fractions; + vector fractions; + for (auto &sample : run.samples) { + source_fractions.push_back(sample.source_fraction); + fractions.push_back(sample.fraction); + } + + // progress must be reported while the pipeline is producing data + if (run.chunks >= MIN_CHUNKS_SUPPORTED) { + if (run.first_unsupported_source.IsValid()) { + AddViolation(trace, ProgressInvariant::UNSUPPORTED_SOURCE, trace.source_name, + StringUtil::Format("invalid progress after %llu of %llu chunks", + run.first_unsupported_source.GetIndex() + 1, run.chunks)); + } + if (run.first_unsupported_sink.IsValid()) { + AddViolation(trace, ProgressInvariant::UNSUPPORTED_SINK, trace.sink_name, + StringUtil::Format("invalid progress after %llu of %llu chunks", + run.first_unsupported_sink.GetIndex() + 1, run.chunks)); + } + } + + // progress must never decrease - attribute to the source if its own progress decreased + auto source_decrease = FindDecrease(source_fractions); + if (source_decrease.IsValid()) { + auto idx = source_decrease.GetIndex(); + AddViolation(trace, ProgressInvariant::NON_MONOTONIC, trace.source_name, + StringUtil::Format("source progress decreased from %s to %s at sample %llu", + FormatPercentage(PreviousValid(source_fractions, idx)), + FormatPercentage(source_fractions[idx]), idx + 1)); + } else { + auto decrease = FindDecrease(fractions); + if (decrease.IsValid()) { + auto idx = decrease.GetIndex(); + AddViolation(trace, ProgressInvariant::NON_MONOTONIC, trace.sink_name, + StringUtil::Format("pipeline progress decreased from %s to %s at sample %llu", + FormatPercentage(PreviousValid(fractions, idx)), + FormatPercentage(fractions[idx]), idx + 1)); + } + } + + auto ran_to_completion = run.has_final_sample && !run.early_exit; + // a pipeline that ran to completion must report 100% + if (ran_to_completion && run.chunks >= MIN_CHUNKS_SUPPORTED) { + auto &final_sample = run.samples.back(); + if (!std::isnan(final_sample.fraction) && final_sample.fraction < 1 - COMPLETION_EPSILON) { + auto source_incomplete = final_sample.source_fraction < 1 - COMPLETION_EPSILON; + AddViolation(trace, ProgressInvariant::INCOMPLETE, source_incomplete ? trace.source_name : trace.sink_name, + StringUtil::Format("finished at %s (source %s) after %llu chunks", + FormatPercentage(final_sample.fraction), + FormatPercentage(final_sample.source_fraction), run.chunks)); + } + } + + if (run.rows < MIN_ROWS_GRANULARITY) { + return; + } + // progress must move while the source produces data + auto chunk_sample_count = run.has_final_sample ? run.samples.size() - 1 : run.samples.size(); + source_fractions.resize(chunk_sample_count); + fractions.resize(chunk_sample_count); + bool source_constant, constant; + double source_value, value; + auto source_count = CountConstant(source_fractions, source_constant, source_value); + auto count = CountConstant(fractions, constant, value); + // progress that reaches 100% too early is not stalled, but inaccurate + source_constant = source_constant && source_value < 1 - COMPLETION_EPSILON; + constant = constant && value < 1 - COMPLETION_EPSILON; + if (source_count > 1 && source_constant) { + AddViolation(trace, ProgressInvariant::STALLED, trace.source_name, + StringUtil::Format("source progress stuck at %s for %llu chunks", FormatPercentage(source_value), + source_count)); + } else if (count > 1 && constant) { + AddViolation( + trace, ProgressInvariant::STALLED, trace.sink_name, + StringUtil::Format("pipeline progress stuck at %s for %llu chunks", FormatPercentage(value), count)); + } + + // source progress should roughly follow the fraction of rows produced + if (ran_to_completion && run.rows > 0) { + double max_error = 0; + idx_t max_idx = 0; + for (idx_t i = 0; i < chunk_sample_count; i++) { + auto &sample = run.samples[i]; + if (std::isnan(sample.source_fraction)) { + continue; + } + auto actual = static_cast(sample.rows) / static_cast(run.rows); + auto error = std::fabs(sample.source_fraction - actual); + if (error > max_error) { + max_error = error; + max_idx = i; + } + } + if (max_error > MAX_INACCURACY) { + auto &sample = run.samples[max_idx]; + AddViolation( + trace, ProgressInvariant::INACCURATE, trace.source_name, + StringUtil::Format("source reported %s after %s of the rows (%llu of %llu)", + FormatPercentage(sample.source_fraction), + FormatPercentage(static_cast(sample.rows) / static_cast(run.rows)), + sample.rows, run.rows)); + } + } +} + +void ProgressVerifier::AddViolation(const PipelineTrace &trace, ProgressInvariant invariant, + const string &operator_name, const string &detail) { + lock_guard guard(lock); + auto key = EnumUtil::ToString(invariant) + ":" + operator_name; + if (reported.find(key) != reported.end()) { + return; + } + reported.insert(key); + Violation violation; + violation.invariant = invariant; + violation.operator_name = operator_name; + violation.pipeline = trace.source_name + " -> " + trace.sink_name; + violation.detail = detail; + violations.push_back(std::move(violation)); +} + +bool ProgressVerifier::IsIgnored(const Violation &violation) const { + auto invariant = EnumUtil::ToString(violation.invariant); + return ignore_list.count(invariant) > 0 || ignore_list.count(invariant + ":" + violation.operator_name) > 0 || + ignore_list.count("*:" + violation.operator_name) > 0; +} + +string ProgressVerifier::Finalize(const vector> &pipelines) { + for (auto &pipeline : pipelines) { + optional_ptr trace; + { + lock_guard guard(lock); + auto entry = traces.find(pipeline.get()); + if (entry == traces.end()) { + // the source of this pipeline never produced any data + continue; + } + trace = entry->second.get(); + } + lock_guard guard(trace->lock); + Sample(*pipeline, *trace, true); + } + vector> all_traces; + { + lock_guard guard(lock); + for (auto &entry : traces) { + all_traces.push_back(*entry.second); + } + } + for (auto &trace : all_traces) { + lock_guard guard(trace.get().lock); + VerifyRun(trace.get()); + trace.get().run = PipelineRun(); + } + + lock_guard guard(lock); + string error; + vector failing_keys; + for (auto &violation : violations) { + auto invariant = EnumUtil::ToString(violation.invariant); + if (mode != DebugProgressVerification::ERROR || !FailsQuery(violation.invariant) || IsIgnored(violation)) { + DUCKDB_LOG(context, ProgressVerificationLogType, invariant, violation.operator_name, violation.pipeline, + violation.detail); + continue; + } + error += StringUtil::Format("\n- %s [%s] in pipeline %s: %s", invariant, violation.operator_name, + violation.pipeline, violation.detail); + failing_keys.push_back(invariant + ":" + violation.operator_name); + } + violations.clear(); + if (error.empty()) { + return error; + } + return "Progress verification failed:" + error + "\nIgnore with SET debug_verify_progress_ignore='" + + StringUtil::Join(failing_keys, ",") + "'"; +} + +} // namespace duckdb diff --git a/src/duckdb/src/parallel/scan_read_ahead.cpp b/src/duckdb/src/parallel/scan_read_ahead.cpp index d650fc93a..a789974ba 100644 --- a/src/duckdb/src/parallel/scan_read_ahead.cpp +++ b/src/duckdb/src/parallel/scan_read_ahead.cpp @@ -46,12 +46,6 @@ bool ReadAheadJobCompletion::TryPark(TableFunctionInput &data_p) { return true; } -//! Throw when the query was interrupted, otherwise yield the thread -static void ScanReadAheadYield(ClientContext &context) { - context.InterruptCheck(); - TaskScheduler::YieldThread(); -} - void ReadAheadJobCompletion::WaitForIO() { shared_ptr task; while (pending_io_tasks.load() > 0) { @@ -70,8 +64,9 @@ void ReadAheadJobCompletion::WaitForIO() { class ReadAheadIOTask : public BaseExecutorTask { public: ReadAheadIOTask(TaskExecutor &executor, unique_ptr task_p, - shared_ptr completion_p) - : BaseExecutorTask(executor), task(std::move(task_p)), completion(std::move(completion_p)) { + shared_ptr completion_p, ScanReadAhead &read_ahead_p) + : BaseExecutorTask(executor), task(std::move(task_p)), completion(std::move(completion_p)), + read_ahead(read_ahead_p) { completion->AddIOTask(); } ~ReadAheadIOTask() override { @@ -80,20 +75,29 @@ class ReadAheadIOTask : public BaseExecutorTask { } void ExecuteTask() override { - // Does the actual IO - task->Execute(); + try { + // Does the actual IO + task->Execute(); + } catch (std::exception &ex) { + // recording the error through the read-ahead also wakes the threads waiting for progress + read_ahead.PushError(ErrorData(ex)); + } } private: unique_ptr task; shared_ptr completion; + //! Outlives the task, the read-ahead drains its executor before it is destroyed + ScanReadAhead &read_ahead; }; //! Async task that opens one file ahead of decoding and releases the pending-open count when destroyed class FileOpenTask : public BaseExecutorTask { public: - FileOpenTask(TaskExecutor &executor, shared_ptr> pending_opens_p, std::function open_fn_p) - : BaseExecutorTask(executor), pending_opens(std::move(pending_opens_p)), open_fn(std::move(open_fn_p)) { + FileOpenTask(TaskExecutor &executor, shared_ptr> pending_opens_p, std::function open_fn_p, + std::function cancel_fn_p) + : BaseExecutorTask(executor), pending_opens(std::move(pending_opens_p)), open_fn(std::move(open_fn_p)), + cancel_fn(std::move(cancel_fn_p)) { ++*pending_opens; } ~FileOpenTask() override { @@ -105,9 +109,15 @@ class FileOpenTask : public BaseExecutorTask { open_fn(); } + //! The open never runs, so settle the state the scan waits on - it only ever leaves OPENING in one of these two + void Cancel() override { + cancel_fn(); + } + private: shared_ptr> pending_opens; std::function open_fn; + std::function cancel_fn; }; ScanReadAheadJob::~ScanReadAheadJob() = default; @@ -124,6 +134,10 @@ ScanReadAhead::ScanReadAhead(ClientContext &context, idx_t read_ahead_depth_p, } ScanReadAhead::~ScanReadAhead() { + CancelAndDrain(); +} + +void ScanReadAhead::CancelAndDrain() { executor->CancelAndDrain(); } @@ -187,13 +201,19 @@ unique_ptr ScanReadAhead::ClaimJob() { auto job = std::move(ready_queue.front()); ready_queue.pop_front(); ReleaseSlot(); + NotifyProgress(false); return job; } void ScanReadAhead::WaitForJob(ScanReadAheadJob &job) { job.SettleIO(); // the job's I/O has completed, release its budget charge - pending_io_bytes -= job.io_bytes; + const auto budget = backlog_budget.load(); + const auto previous = pending_io_bytes.fetch_sub(job.io_bytes); + if (previous >= budget && previous - job.io_bytes < budget) { + // the scan is no longer over budget, a waiting thread can produce + SignalProgress(false); + } job.io_bytes = 0; ThrowIfError(); } @@ -202,7 +222,9 @@ ScanReadAheadAcquire ScanReadAhead::AcquireJob(ClientContext &context, TableFunc const ProduceJobCallback &claim_and_schedule, unique_ptr &job) { D_ASSERT(!job); + optional yield_deadline; while (true) { + const auto epoch = ProgressEpoch(); // keep the queue full before claiming while (TryProduceJob(claim_and_schedule)) { } @@ -220,8 +242,18 @@ ScanReadAheadAcquire ScanReadAhead::AcquireJob(ClientContext &context, TableFunc } } if (!job) { - // another thread is between claiming an assignment and pushing its job, wait for it - ScanReadAheadYield(context); + // another thread is between claiming an assignment and pushing its job, a short claim is caught by + // yielding and a long one is waited for + const auto now = std::chrono::steady_clock::now(); + if (!yield_deadline) { + yield_deadline = now + std::chrono::microseconds(PRODUCER_YIELD_MICROS); + } + if (now < *yield_deadline) { + context.InterruptCheck(); + TaskScheduler::YieldThread(); + } else { + WaitForProgress(context, epoch); + } continue; } // park until the job's scheduled I/O completes, the last I/O task to finish wakes the scan task @@ -238,11 +270,22 @@ void ScanReadAhead::SetDone() { done = true; } +idx_t ScanReadAhead::ReservationDepth() const { + // over budget one job stays queued, claimed jobs hold the budget until their task resumes + return pending_io_bytes.load() >= backlog_budget.load() ? 1 : read_ahead_depth; +} + bool ScanReadAhead::TryReserveSlot() { - const bool over_budget = pending_io_bytes.load() >= backlog_budget.load(); + // take the minimum job charge up front so concurrent producers cannot all pass the budget check together + const bool over_budget = pending_io_bytes.fetch_add(MINIMUM_JOB_IO_CHARGE) >= backlog_budget.load(); const idx_t depth = over_budget ? 1 : read_ahead_depth; if (active_jobs.fetch_add(1) >= depth) { --active_jobs; + pending_io_bytes -= MINIMUM_JOB_IO_CHARGE; + if (active_jobs.load() < ReservationDepth()) { + // a concurrent attempt failed on this attempt's charge or slot, let it retry + SignalProgress(false); + } return false; } ++active_producers; @@ -250,49 +293,51 @@ bool ScanReadAhead::TryReserveSlot() { } void ScanReadAhead::PushJob(unique_ptr job, vector> io_tasks) { - // beyond its scheduled I/O a job carries scan-state overhead (row-group sized decode buffers) - static constexpr idx_t MINIMUM_JOB_IO_CHARGE = 16ULL * 1024 * 1024; auto completion = make_shared_ptr(executor); job->io_completion = completion; // wrap all reads before scheduling any, a wrapped task settles the completion even when scheduling throws - vector> read_tasks; + vector> read_tasks; read_tasks.reserve(io_tasks.size()); for (auto &task : io_tasks) { job->io_bytes += task->GetIOSize(); - read_tasks.push_back(make_uniq(*executor, std::move(task), completion)); + read_tasks.push_back(make_uniq(*executor, std::move(task), completion, *this)); } job->io_bytes = MaxValue(job->io_bytes, MINIMUM_JOB_IO_CHARGE); - pending_io_bytes += job->io_bytes; + // the minimum charge was taken by TryReserveSlot, charge the remainder + pending_io_bytes += job->io_bytes - MINIMUM_JOB_IO_CHARGE; // schedule the reads detached on the async pool right away, logged under their own label if (!read_tasks.empty()) { DUCKDB_LOG(context, AsyncTaskScheduleLogType, "READ_AHEAD", read_tasks.size()); } - for (auto &task : read_tasks) { - executor->ScheduleTask(std::move(task)); - } + executor->ScheduleTasks(std::move(read_tasks)); lock_guard guard(lock); if (memory_governor) { - memory_governor->UpdateReservation(pending_io_bytes.load()); + // Reserve room for the next job before admission checks the budget. + memory_governor->UpdateReservation(pending_io_bytes.load() + MINIMUM_JOB_IO_CHARGE); backlog_budget = memory_governor->BackpressureBudget(); } // producers push concurrently, so admit jobs to the queue in batch-index order if (job->batch_index < next_batch_index || !pending_jobs.emplace(job->batch_index, std::move(job)).second) { throw InternalException("Read-ahead jobs must have unique batch indexes, assigned densely from 0"); } + idx_t admitted = 0; while (!pending_jobs.empty() && pending_jobs.begin()->first == next_batch_index) { ready_queue.push_back(std::move(pending_jobs.begin()->second)); pending_jobs.erase(pending_jobs.begin()); next_batch_index++; + admitted++; } + NotifyProgress(admitted > 1); } void ScanReadAhead::PushError(ErrorData error) { executor->PushError(std::move(error)); + SignalProgress(true); } -void ScanReadAhead::ScheduleFileOpen(std::function open_fn) { +void ScanReadAhead::ScheduleFileOpen(std::function open_fn, std::function cancel_fn) { // the task holds the count from construction to destruction, so it stays balanced even if scheduling throws - executor->ScheduleTask(make_uniq(*executor, pending_opens, std::move(open_fn))); + executor->ScheduleTask(make_uniq(*executor, pending_opens, std::move(open_fn), std::move(cancel_fn))); } bool ScanReadAhead::CanScheduleOpen() const { @@ -300,13 +345,11 @@ bool ScanReadAhead::CanScheduleOpen() const { } bool ScanReadAhead::TryRunPendingTask() { - ThrowIfError(); shared_ptr task; if (!executor->GetTask(task)) { return false; } task->Execute(TaskExecutionMode::PROCESS_ALL); - ThrowIfError(); return true; } @@ -321,4 +364,47 @@ void ScanReadAhead::ReleaseSlot() { active_jobs--; } +void ScanReadAhead::WakeWaiters(bool wake_all) { + if (wake_all) { + progress.notify_all(); + } else { + progress.notify_one(); + } +} + +void ScanReadAhead::NotifyProgress(bool wake_all) { + ++progress_epoch; + if (waiting_threads.load() > 0) { + WakeWaiters(wake_all); + } +} + +void ScanReadAhead::SignalProgress(bool wake_all) { + ++progress_epoch; + if (waiting_threads.load() == 0) { + return; + } + { + // a waiter that saw the old epoch is blocked in the wait once the lock is free + lock_guard guard(lock); + } + WakeWaiters(wake_all); +} + +void ScanReadAhead::WaitForProgress(ClientContext &context, idx_t epoch) { + context.BlockingInterruptCheck(); + { + unique_lock guard(lock); + ++waiting_threads; + // the timeout bounds the interrupt latency + progress.wait_for(guard, std::chrono::milliseconds(10), [&]() { return progress_epoch.load() != epoch; }); + --waiting_threads; + } + context.BlockingInterruptCheck(); +} + +idx_t ScanReadAhead::ProgressEpoch() const { + return progress_epoch.load(); +} + } // namespace duckdb diff --git a/src/duckdb/src/parallel/task_executor.cpp b/src/duckdb/src/parallel/task_executor.cpp index 01657ad8e..c5be0f60f 100644 --- a/src/duckdb/src/parallel/task_executor.cpp +++ b/src/duckdb/src/parallel/task_executor.cpp @@ -4,6 +4,106 @@ namespace duckdb { +//! The wrapper the executor puts around every scheduled task +//! It owns the cancel check, the task notifier, the error handling and the task accounting, so that none of those +//! depend on what the task itself does +class TaskExecutorTask : public Task { +public: + TaskExecutorTask(TaskExecutor &executor, unique_ptr task_p) + : executor(executor), task(std::move(task_p)) { + } + +public: + TaskExecutionResult Execute(TaskExecutionMode mode) override { + FinishGuard guard(executor); + if (executor.HasError() || executor.IsCancelled()) { + // another task encountered an error, or the executor was cancelled - retire without doing the work + return RunGuarded([&]() { task->Cancel(); }, "Unknown exception while cancelling a task"); + } + TaskExecutionResult result; + try { + TaskNotifier task_notifier {executor.context}; + // PROCESS_ALL must run to completion, so loop the steps; PROCESS_PARTIAL does one and yields + do { + result = task->ExecuteTaskStep(); + } while (result == TaskExecutionResult::TASK_NOT_FINISHED && mode == TaskExecutionMode::PROCESS_ALL); + } catch (std::exception &ex) { + executor.PushError(ErrorData(ex)); + return TaskExecutionResult::TASK_ERROR; + } catch (...) { // LCOV_EXCL_START + executor.PushError(ErrorData("Unknown exception during task execution")); + return TaskExecutionResult::TASK_ERROR; + } // LCOV_EXCL_STOP + if (result == TaskExecutionResult::TASK_NOT_FINISHED) { + // yielded under PROCESS_PARTIAL: the scheduler re-enqueues this wrapper, keep the task's slot open + guard.Dismiss(); + return TaskExecutionResult::TASK_NOT_FINISHED; + } + D_ASSERT(result == TaskExecutionResult::TASK_FINISHED); + return TaskExecutionResult::TASK_FINISHED; + } + + void Deschedule() override { + throw InternalException("Tasks scheduled on a TaskExecutor cannot be descheduled"); + } + + void Reschedule() override { + throw InternalException("Tasks scheduled on a TaskExecutor cannot be rescheduled"); + } + + string TaskType() const override { + return task->TaskType(); + } + + //! Retire a task that never made it into the queue, without touching the executor's accounting + void Retire() noexcept { + try { + RunGuarded([&]() { task->Cancel(); }, "Unknown exception while cancelling a task"); + } catch (...) { + // Preserve the submission exception if reporting a cancellation error fails. + } + } + +private: + //! Settles the executor's task counter on every exit path, so that a drain always terminates + class FinishGuard { + public: + explicit FinishGuard(TaskExecutor &executor) : executor(executor) { + } + ~FinishGuard() { + if (!dismissed) { + executor.FinishTask(); + } + } + //! Keep the task's slot open across a yield - the wrapper will run again and finish it later + void Dismiss() { + dismissed = true; + } + + private: + TaskExecutor &executor; + bool dismissed = false; + }; + + template + TaskExecutionResult RunGuarded(FUNC &&callback, const char *unknown_error) { + try { + callback(); + } catch (std::exception &ex) { + executor.PushError(ErrorData(ex)); + return TaskExecutionResult::TASK_ERROR; + } catch (...) { // LCOV_EXCL_START + executor.PushError(ErrorData(unknown_error)); + return TaskExecutionResult::TASK_ERROR; + } // LCOV_EXCL_STOP + return TaskExecutionResult::TASK_FINISHED; + } + +private: + TaskExecutor &executor; + unique_ptr task; +}; + TaskExecutor::TaskExecutor(TaskScheduler &scheduler, TaskSchedulerType type_p) : scheduler(scheduler), type(type_p), token(scheduler.CreateProducer()) { } @@ -34,22 +134,94 @@ void TaskExecutor::ThrowError() { error_manager.ThrowException(); } -void TaskExecutor::ScheduleTask(unique_ptr task) { +ErrorData TaskExecutor::GetError() { + return error_manager.GetError(); +} + +bool TaskExecutor::IsCancelled() const { + return cancelled; +} + +void TaskExecutor::ScheduleTask(unique_ptr task) { + // wrap before taking ownership of a slot, so that a failure to allocate the wrapper needs no rollback + shared_ptr scheduled_task; + try { + scheduled_task = make_shared_ptr(*this, std::move(task)); + } catch (...) { + if (task) { + TaskExecutorTask unscheduled_task(*this, std::move(task)); + unscheduled_task.Retire(); + } + throw; + } { const annotated_lock_guard lock(token->producer_lock); ++total_tasks; } try { - scheduler.ScheduleTask(*token, std::move(task), type); + // hold on to our own reference, so that a task that fails to queue can still be retired + scheduler.ScheduleTask(*token, scheduled_task, type); } catch (...) { - const annotated_lock_guard lock(token->producer_lock); - // We failed to schedule the task, so we decrement the total number of tasks, instead of incrementing completed - // tasks count. - --total_tasks; - token->producer_cv.notify_one(); + { + const annotated_lock_guard lock(token->producer_lock); + // We failed to schedule the task, so we decrement the total number of tasks, instead of incrementing + // completed tasks count. + --total_tasks; + token->producer_cv.notify_one(); + } + scheduled_task->Retire(); throw; } } + +void TaskExecutor::ScheduleTasks(vector> tasks) { + if (tasks.empty()) { + return; + } + if (tasks.size() == 1) { + ScheduleTask(std::move(tasks.front())); + return; + } + vector> scheduled_tasks; + bool accounted = false; + try { +#ifdef DUCKDB_NO_THREADS + // The no-threads bulk queue can publish a prefix before an allocation fails. + for (auto &task : tasks) { + ScheduleTask(std::move(task)); + } +#else + scheduled_tasks.reserve(tasks.size()); + for (auto &task : tasks) { + scheduled_tasks.push_back(make_shared_ptr(*this, std::move(task))); + } + { + const annotated_lock_guard lock(token->producer_lock); + total_tasks += tasks.size(); + } + accounted = true; + // The threaded bulk queue publishes all tasks or none, moving ownership on success. + scheduler.ScheduleTasks(*token, scheduled_tasks, type); +#endif + } catch (...) { + if (accounted) { + const annotated_lock_guard lock(token->producer_lock); + total_tasks -= tasks.size(); + token->producer_cv.notify_one(); + } + for (auto &task : scheduled_tasks) { + static_cast(*task).Retire(); + } + for (auto &task : tasks) { + if (task) { + TaskExecutorTask unscheduled_task(*this, std::move(task)); + unscheduled_task.Retire(); + } + } + throw; + } +} + void TaskExecutor::FinishTask() { const annotated_lock_guard lk(token->producer_lock); ++completed_tasks; @@ -73,7 +245,8 @@ void TaskExecutor::DrainTasks() { const auto res = task_from_producer->Execute(TaskExecutionMode::PROCESS_ALL); std::ignore = res; - D_ASSERT(res != TaskExecutionResult::TASK_BLOCKED); + // PROCESS_ALL runs a task to completion, so a drain only ever sees a finished or errored task + D_ASSERT(res == TaskExecutionResult::TASK_FINISHED || res == TaskExecutionResult::TASK_ERROR); task_from_producer.reset(); } } @@ -101,26 +274,14 @@ bool TaskExecutor::GetTask(shared_ptr &task) { BaseExecutorTask::BaseExecutorTask(TaskExecutor &executor) : executor(executor) { } -TaskExecutionResult BaseExecutorTask::Execute(TaskExecutionMode mode) { - if (executor.HasError() || executor.cancelled) { - // another task encountered an error or the executor was cancelled - bailout - executor.FinishTask(); - return TaskExecutionResult::TASK_FINISHED; - } +TaskExecutor::JoinGuard::JoinGuard(TaskExecutor &executor) : executor(executor) { +} + +TaskExecutor::JoinGuard::~JoinGuard() { try { - { - TaskNotifier task_notifier {executor.context}; - ExecuteTask(); - } - executor.FinishTask(); - return TaskExecutionResult::TASK_FINISHED; - } catch (std::exception &ex) { - executor.PushError(ErrorData(ex)); - } catch (...) { // LCOV_EXCL_START - executor.PushError(ErrorData("Unknown exception during Checkpoint!")); - } // LCOV_EXCL_STOP - executor.FinishTask(); - return TaskExecutionResult::TASK_ERROR; + executor.WorkOnTasks(); + } catch (...) { // NOLINT + } } } // namespace duckdb diff --git a/src/duckdb/src/parallel/task_notifier.cpp b/src/duckdb/src/parallel/task_notifier.cpp index 012afb548..4b1eb34ed 100644 --- a/src/duckdb/src/parallel/task_notifier.cpp +++ b/src/duckdb/src/parallel/task_notifier.cpp @@ -6,17 +6,13 @@ namespace duckdb { TaskNotifier::TaskNotifier(optional_ptr context_p) : context(context_p) { if (context) { - for (auto &state : context->registered_state->States()) { - state->OnTaskStart(*context); - } + context->registered_state->ForEachState([&](ClientContextState &state) { state.OnTaskStart(*context); }); } } TaskNotifier::~TaskNotifier() { if (context) { - for (auto &state : context->registered_state->States()) { - state->OnTaskStop(*context); - } + context->registered_state->ForEachState([&](ClientContextState &state) { state.OnTaskStop(*context); }); } } diff --git a/src/duckdb/src/parallel/task_scheduler.cpp b/src/duckdb/src/parallel/task_scheduler.cpp index 6adc36a03..a4106a020 100644 --- a/src/duckdb/src/parallel/task_scheduler.cpp +++ b/src/duckdb/src/parallel/task_scheduler.cpp @@ -104,26 +104,22 @@ void TaskScheduler::ScheduleTasks(ProducerToken &producer, vector &task) { +bool TaskScheduler::GetTaskInternal(ConsumerToken &consumer, shared_ptr &task) { for (auto &queue : queues) { - if (queue->Dequeue(task)) { + if (queue->Dequeue(consumer, task)) { return true; } } return false; } -bool TaskScheduler::GetTaskInternal(shared_ptr &task, TaskSchedulerType pool_type) { - return GetQueue(pool_type).Dequeue(task); -} - void TaskScheduler::ExecuteForever(atomic *marker) { ExecuteForever(marker, TaskSchedulerType::REGULAR); } -bool TaskScheduler::TryDequeueAndProcessTask(const DBConfig &config, TaskSchedulerQueue &queue, +bool TaskScheduler::TryDequeueAndProcessTask(const DBConfig &config, TaskSchedulerQueue &queue, ConsumerToken &consumer, shared_ptr &task) { - if (queue.Dequeue(task)) { + if (queue.Dequeue(consumer, task)) { auto process_mode = TaskExecutionMode::PROCESS_ALL; if (Settings::Get(config)) { process_mode = TaskExecutionMode::PROCESS_PARTIAL; @@ -164,6 +160,7 @@ void TaskScheduler::ExecuteForever(atomic *marker, const TaskSchedulerType const auto &block_allocator = BlockAllocator::Get(db); const auto &config = DBConfig::GetConfig(db); auto &pool = GetPool(pool_type); + ConsumerToken consumer(queues); shared_ptr task; // loop until the marker is set to false @@ -194,12 +191,12 @@ void TaskScheduler::ExecuteForever(atomic *marker, const TaskSchedulerType if (pool_type == TaskSchedulerType::REGULAR) { // Regular thread pool picks up tasks from all pools for (auto &queue : queues) { - if (TryDequeueAndProcessTask(config, *queue, task)) { + if (TryDequeueAndProcessTask(config, *queue, consumer, task)) { break; } } } else { - TryDequeueAndProcessTask(config, GetQueue(pool_type), task); + TryDequeueAndProcessTask(config, GetQueue(pool_type), consumer, task); } } // this thread will exit, flush all of its outstanding allocations @@ -215,11 +212,12 @@ void TaskScheduler::ExecuteForever(atomic *marker, const TaskSchedulerType idx_t TaskScheduler::ExecuteTasks(atomic *marker, idx_t max_tasks) { #ifndef DUCKDB_NO_THREADS + ConsumerToken consumer(queues); idx_t completed_tasks = 0; // loop until the marker is set to false while (*marker && completed_tasks < max_tasks) { shared_ptr task; - if (!GetTaskInternal(task)) { + if (!GetTaskInternal(consumer, task)) { return completed_tasks; } auto execute_result = task->Execute(TaskExecutionMode::PROCESS_ALL); @@ -246,10 +244,11 @@ idx_t TaskScheduler::ExecuteTasks(atomic *marker, idx_t max_tasks) { void TaskScheduler::ExecuteTasks(idx_t max_tasks) { #ifndef DUCKDB_NO_THREADS + ConsumerToken consumer(queues); shared_ptr task; for (idx_t i = 0; i < max_tasks; i++) { GetPool(TaskSchedulerType::REGULAR).Wait(TASK_TIMEOUT_USECS); - if (!GetTaskInternal(task)) { + if (!GetTaskInternal(consumer, task)) { return; } try { diff --git a/src/duckdb/src/parallel/task_scheduler_queue.cpp b/src/duckdb/src/parallel/task_scheduler_queue.cpp index 36839f328..ef1d15112 100644 --- a/src/duckdb/src/parallel/task_scheduler_queue.cpp +++ b/src/duckdb/src/parallel/task_scheduler_queue.cpp @@ -13,7 +13,11 @@ TaskSchedulerType TaskSchedulerQueue::GetPoolType() { } #ifndef DUCKDB_NO_THREADS -typedef duckdb_moodycamel::ConcurrentQueue> concurrent_queue_t; +struct TaskQueueTraits : public duckdb_moodycamel::ConcurrentQueueDefaultTraits { + //! Consumers move on to the next producer after this many tasks (default: 256) + static constexpr uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = 16; +}; +typedef duckdb_moodycamel::ConcurrentQueue, TaskQueueTraits> concurrent_queue_t; struct ConcurrentQueueWrapper { concurrent_queue_t q; @@ -28,6 +32,15 @@ struct QueueProducerToken { duckdb_moodycamel::ProducerToken token; }; +struct QueueConsumerToken { +public: + explicit QueueConsumerToken(TaskSchedulerQueue &queue) : token(queue.GetQueue().q) { + } + +public: + duckdb_moodycamel::ConsumerToken token; +}; + TaskSchedulerQueue::TaskSchedulerQueue(TaskSchedulerType pool_type_p) : pool_type(pool_type_p), queue(make_uniq()) { } @@ -70,8 +83,8 @@ bool TaskSchedulerQueue::DequeueFromProducerLocked(ProducerToken &token, shared_ return true; } -bool TaskSchedulerQueue::Dequeue(shared_ptr &task) { - if (!queue->q.try_dequeue(task)) { +bool TaskSchedulerQueue::Dequeue(ConsumerToken &token, shared_ptr &task) { + if (!queue->q.try_dequeue(token.GetQueueConsumerToken(pool_type).token, task)) { return false; } --tasks_in_queue; @@ -112,6 +125,12 @@ struct QueueProducerToken { TaskSchedulerQueue *queue; }; +struct QueueConsumerToken { +public: + explicit QueueConsumerToken(TaskSchedulerQueue &) { + } +}; + TaskSchedulerQueue::TaskSchedulerQueue(TaskSchedulerType pool_type_p) : pool_type(pool_type_p) { } @@ -152,7 +171,7 @@ bool TaskSchedulerQueue::DequeueFromProducerLocked(ProducerToken &token, shared_ return true; } -bool TaskSchedulerQueue::Dequeue(shared_ptr &task) { +bool TaskSchedulerQueue::Dequeue(ConsumerToken &token, shared_ptr &task) { throw InternalException("Global dequeue not supported for no threads queue"); } @@ -206,4 +225,17 @@ QueueProducerToken &ProducerToken::GetQueueProducerToken(TaskSchedulerType pool_ return *tokens[static_cast(pool_type)]; } +ConsumerToken::ConsumerToken(array, TASK_SCHEDULER_TYPE_COUNT> &queues) { + for (uint8_t i = 0; i < TASK_SCHEDULER_TYPE_COUNT; i++) { + tokens[i] = make_uniq(*queues[i]); + } +} + +ConsumerToken::~ConsumerToken() { +} + +QueueConsumerToken &ConsumerToken::GetQueueConsumerToken(TaskSchedulerType pool_type) { + return *tokens[static_cast(pool_type)]; +} + } // namespace duckdb diff --git a/src/duckdb/src/parser/column_definition.cpp b/src/duckdb/src/parser/column_definition.cpp index 0c92d0cb2..eb46806c6 100644 --- a/src/duckdb/src/parser/column_definition.cpp +++ b/src/duckdb/src/parser/column_definition.cpp @@ -3,7 +3,7 @@ #include "duckdb/parser/expression/columnref_expression.hpp" #include "duckdb/parser/expression/cast_expression.hpp" #include "duckdb/common/exception/parser_exception.hpp" -#include "duckdb/common/extra_type_info.hpp" +#include "duckdb/common/logical_type_info.hpp" namespace duckdb { @@ -133,20 +133,11 @@ string ColumnDefinition::ToSQLString() const { if (column_type.id() != LogicalTypeId::ANY) { result += Type().ToString(); } - auto extra_type_info = column_type.AuxInfo(); - if (extra_type_info) { - if (extra_type_info->type == ExtraTypeInfoType::STRING_TYPE_INFO) { - auto &string_info = extra_type_info->Cast(); - if (!string_info.collation.empty()) { - result += " COLLATE " + string_info.collation; - } - } - if (extra_type_info->type == ExtraTypeInfoType::UNBOUND_TYPE_INFO) { - // TODO - // auto &colllation = UnboundType::GetCollation(column_type); - // if (!colllation.empty()) { - // ss << " COLLATE " + colllation; - //} + auto &type_info = column_type.GetTypeInfo(); + if (type_info.type == LogicalTypeInfoType::STRING_TYPE_INFO) { + auto &string_info = type_info.Cast(); + if (!string_info.collation.empty()) { + result += " COLLATE " + string_info.collation; } } if (Generated()) { @@ -212,7 +203,7 @@ void ColumnDefinition::SetGeneratedExpression(unique_ptr new_e category = TableColumnType::GENERATED; if (new_expr->HasSubquery()) { - throw ParserException("Expression of generated column \"%s\" contains a subquery, which isn't allowed", name); + throw ParserException("Expression of generated column %s contains a subquery, which isn't allowed", name); } VerifyColumnRefs(*new_expr); diff --git a/src/duckdb/src/parser/constraint.cpp b/src/duckdb/src/parser/constraint.cpp index 67aaec651..6e23d5057 100644 --- a/src/duckdb/src/parser/constraint.cpp +++ b/src/duckdb/src/parser/constraint.cpp @@ -1,6 +1,7 @@ #include "duckdb/parser/constraint.hpp" #include "duckdb/common/printer.hpp" +#include "duckdb/parser/constraints/foreign_key_constraint.hpp" namespace duckdb { @@ -14,4 +15,26 @@ void Constraint::Print() const { Printer::Print(ToString()); } +bool Constraint::NeedsBackingIndex() const { + switch (type) { + case ConstraintType::UNIQUE: + return true; + case ConstraintType::FOREIGN_KEY: + // Reverse foreign key constraints on the referenced table do not own a backing index. + return Cast().info.IsAppendConstraint(); + default: + return false; + } +} + +idx_t Constraint::GetBackingIndexOid() const { + return backing_index_oid.GetIndex(); +} + +void Constraint::SetBackingIndexOid(idx_t oid) { + D_ASSERT(NeedsBackingIndex()); + D_ASSERT(oid != DConstants::INVALID_INDEX); + backing_index_oid = oid; +} + } // namespace duckdb diff --git a/src/duckdb/src/parser/constraints/foreign_key_constraint.cpp b/src/duckdb/src/parser/constraints/foreign_key_constraint.cpp index 141932850..282204ea3 100644 --- a/src/duckdb/src/parser/constraints/foreign_key_constraint.cpp +++ b/src/duckdb/src/parser/constraints/foreign_key_constraint.cpp @@ -50,7 +50,7 @@ string ForeignKeyConstraint::ToString() const { } unique_ptr ForeignKeyConstraint::Copy() const { - return make_uniq(pk_columns, fk_columns, info); + return make_uniq(*this); } } // namespace duckdb diff --git a/src/duckdb/src/parser/constraints/unique_constraint.cpp b/src/duckdb/src/parser/constraints/unique_constraint.cpp index 63b48c5c0..c90247752 100644 --- a/src/duckdb/src/parser/constraints/unique_constraint.cpp +++ b/src/duckdb/src/parser/constraints/unique_constraint.cpp @@ -42,11 +42,7 @@ string UniqueConstraint::ToString() const { } unique_ptr UniqueConstraint::Copy() const { - if (!HasIndex()) { - return make_uniq(columns, is_primary_key, check_mode); - } - - return make_uniq(index, columns.empty() ? Identifier() : columns[0], is_primary_key, check_mode); + return make_uniq(*this); } bool UniqueConstraint::IsPrimaryKey() const { diff --git a/src/duckdb/src/parser/expression/cast_expression.cpp b/src/duckdb/src/parser/expression/cast_expression.cpp index f0a54714d..fb93183c7 100644 --- a/src/duckdb/src/parser/expression/cast_expression.cpp +++ b/src/duckdb/src/parser/expression/cast_expression.cpp @@ -56,9 +56,15 @@ unique_ptr CastExpression::Deserialize(Deserializer &deseriali deserializer.ReadPropertyWithExplicitDefault>(203, "type_expr", nullptr); if (type_expression) { + if (type_expression->GetExpressionClass() != ExpressionClass::TYPE) { + throw SerializationException("Failed to deserialize CAST expression: the target is not a type expression"); + } result->cast_type = unique_ptr_cast(std::move(type_expression)); } else { // written by a version that stored the target as a LogicalType + if (cast_type.id() == LogicalTypeId::INVALID) { + throw SerializationException("Failed to deserialize CAST expression: missing target type"); + } result->cast_type = TypeExpression::FromLogicalType(cast_type); } return std::move(result); diff --git a/src/duckdb/src/parser/expression/function_expression.cpp b/src/duckdb/src/parser/expression/function_expression.cpp index 7ff0b9373..bd15e9955 100644 --- a/src/duckdb/src/parser/expression/function_expression.cpp +++ b/src/duckdb/src/parser/expression/function_expression.cpp @@ -66,7 +66,7 @@ string FunctionExpression::ToString() const { return "((" + arguments[0].ToString() + ")" + StringUtil::Replace(qualified_name.Name().GetIdentifierName(), "__postfix", "") + ")"; } - return qualified_name.Name().GetIdentifierName() + "(" + arguments[0].ToString() + ")"; + return "(" + qualified_name.Name().GetIdentifierName() + "(" + arguments[0].ToString() + "))"; } if (arguments.size() == 2) { return StringUtil::Format("(%s %s %s)", arguments[0].ToString(), qualified_name.Name().GetIdentifierName(), @@ -82,9 +82,9 @@ string FunctionExpression::ToString() const { } result += StringUtil::Join(arguments, arguments.size(), ", ", [&](const FunctionArgument &child) { return child.ToString(); }); - // ordered aggregate + // ordered aggregate - WITHIN GROUP is only accepted for mode, other aggregates take the ORDER BY as an argument if (order_bys && !order_bys->orders.empty()) { - if (arguments.empty()) { + if (arguments.empty() && qualified_name.Name() == Identifier("mode")) { result += ") WITHIN GROUP ("; } result += " ORDER BY "; diff --git a/src/duckdb/src/parser/expression/star_expression.cpp b/src/duckdb/src/parser/expression/star_expression.cpp index 30848415f..8603ae4bc 100644 --- a/src/duckdb/src/parser/expression/star_expression.cpp +++ b/src/duckdb/src/parser/expression/star_expression.cpp @@ -26,42 +26,21 @@ string StarExpression::ToString() const { result += relation_name.empty() ? "*" : SQLIdentifier(relation_name) + ".*"; if (!exclude_list.empty()) { result += " EXCLUDE ("; - bool first_entry = true; - for (auto &entry : exclude_list) { - if (!first_entry) { - result += ", "; - } - result += entry.ToString(); - first_entry = false; - } + result += StringUtil::Join(exclude_list, ", ", [](const auto &entry) { return entry.ToString(); }); result += ")"; } if (!replace_list.empty()) { result += " REPLACE ("; - bool first_entry = true; - for (auto &entry : replace_list) { - if (!first_entry) { - result += ", "; - } - result += entry.second->ToString(); - result += " AS "; - result += SQLIdentifier(entry.first); - first_entry = false; - } + result += StringUtil::Join(replace_list, ", ", [](const auto &entry) { + return entry.second->ToString() + " AS " + SQLIdentifier(entry.first); + }); result += ")"; } if (!rename_list.empty()) { result += " RENAME ("; - bool first_entry = true; - for (auto &entry : rename_list) { - if (!first_entry) { - result += ", "; - } - result += entry.first.ToString(); - result += " AS "; - result += SQLIdentifier(entry.second); - first_entry = false; - } + result += StringUtil::Join(rename_list, ", ", [](const auto &entry) { + return entry.first.ToString() + " AS " + SQLIdentifier(entry.second); + }); result += ")"; } if (columns) { diff --git a/src/duckdb/src/parser/expression/type_expression.cpp b/src/duckdb/src/parser/expression/type_expression.cpp index 1844ec6e8..4d88b98bc 100644 --- a/src/duckdb/src/parser/expression/type_expression.cpp +++ b/src/duckdb/src/parser/expression/type_expression.cpp @@ -7,8 +7,30 @@ #include "duckdb/common/string_util.hpp" #include "duckdb/parser/expression/constant_expression.hpp" #include "duckdb/common/types/hash.hpp" +#include "duckdb/common/type_visitor.hpp" +#include "duckdb/common/unordered_set.hpp" namespace duckdb { +bool TypeExpression::IsSQLType(LogicalTypeId id) { + static const auto admitted_ids = [] { + // TYPE has SQL syntax but is not included in AllTypes. + unordered_set ids {LogicalTypeId::SQLNULL, LogicalTypeId::TYPE}; + for (auto &type : LogicalType::AllTypes()) { + ids.insert(type.id()); + } + return ids; + }(); + return admitted_ids.count(id) != 0; +} + +bool TypeExpression::CanRepresent(const LogicalType &type) { + return type.IsComplete() && !TypeVisitor::Contains(type, [](const LogicalType &child) { + const bool empty_tuple = child.id() == LogicalTypeId::TUPLE && StructType::GetChildCount(child) == 0; + const bool empty_enum = child.id() == LogicalTypeId::ENUM && EnumType::GetSize(child) == 0; + return !IsSQLType(child.id()) || empty_tuple || empty_enum; + }); +} + TypeExpression::TypeExpression(QualifiedName qualified_name_p, vector> children_p) : ParsedExpression(ExpressionType::TYPE, ExpressionClass::TYPE), qualified_name(std::move(qualified_name_p)), children(std::move(children_p)) { @@ -26,89 +48,103 @@ TypeExpression::TypeExpression(const string &type_name, vector node; + string text; +}; - // LIST and ARRAY have special syntax - if (result.empty() && type_name == "LIST" && params.size() == 1) { - return params[0]->ToString() + "[]"; - } - if (result.empty() && type_name == "ARRAY" && params.size() == 2) { - auto &type_param = params[0]; - auto &size_param = params[1]; - return type_param->ToString() + "[" + size_param->ToString() + "]"; - } - // So does STRUCT, MAP and UNION - if (result.empty() && type_name == "STRUCT") { - if (params.empty()) { - return "STRUCT"; - } - string struct_result = "STRUCT("; - for (idx_t i = 0; i < params.size(); i++) { - struct_result += SQLIdentifier(params[i]->GetAlias()) + " " + params[i]->ToString(); - if (i < params.size() - 1) { - struct_result += ", "; - } - } - struct_result += ")"; - return struct_result; - } - if (result.empty() && type_name == "UNION") { - if (params.empty()) { - return "UNION"; - } - string union_result = "UNION("; - for (idx_t i = 0; i < params.size(); i++) { - union_result += SQLIdentifier(params[i]->GetAlias()) + " " + params[i]->ToString(); - if (i < params.size() - 1) { - union_result += ", "; - } - } - union_result += ")"; - return union_result; - } - - if (result.empty() && type_name == "MAP" && params.size() == 2) { - return "MAP(" + params[0]->ToString() + ", " + params[1]->ToString() + ")"; +static void PushChild(vector &stack, const ParsedExpression &child) { + if (child.GetExpressionClass() == ExpressionClass::TYPE) { + stack.push_back(StackItem {&child.Cast(), ""}); + } else { + stack.push_back(StackItem {nullptr, child.ToString()}); } +} - if (result.empty() && type_name == "VARCHAR" && !params.empty()) { - if (params.back()->HasAlias() && params.back()->GetAlias() == "collation") { - // Special case for VARCHAR with collation - auto &collate_expr = params.back()->Cast(); - return StringUtil::Format("VARCHAR COLLATE %s", - SQLIdentifier(collate_expr.GetLiteral().ToValue().ToString())); - } - } +string TypeExpression::ToString() const { + string output; + vector stack; + stack.push_back(StackItem {this, ""}); - if (result.empty() && type_name == "INTERVAL" && !params.empty()) { - // We ignore interval types parameters. - return "INTERVAL"; - } + while (!stack.empty()) { + auto item = std::move(stack.back()); + stack.pop_back(); - auto type_id = TransformStringToLogicalTypeId(type_name.GetIdentifierName()); - if (type_id != LogicalTypeId::UNBOUND && type_id != LogicalTypeId::SQLNULL) { - // Built-in type name - result += type_name.GetIdentifierName(); - } else { - result += SQLIdentifier(type_name); - } + if (!item.node) { + output += item.text; + } else { + auto &node = *item.node; + auto &type_name = node.qualified_name.Name(); + auto qual = node.qualified_name.QualificationToString(); + auto ¶ms = node.children; - if (!params.empty()) { - result += "("; - for (idx_t i = 0; i < params.size(); i++) { - result += params[i]->ToString(); - if (i < params.size() - 1) { - result += ", "; + // LIST and ARRAY have special syntax + if (qual.empty() && type_name == "LIST" && params.size() == 1) { + stack.push_back(StackItem {nullptr, "[]"}); + PushChild(stack, *params[0]); + } else if (qual.empty() && type_name == "ARRAY" && params.size() == 2) { + stack.push_back(StackItem {nullptr, "]"}); + PushChild(stack, *params[1]); + stack.push_back(StackItem {nullptr, "["}); + PushChild(stack, *params[0]); + } else if (qual.empty() && (type_name == "STRUCT" || type_name == "UNION")) { + // STRUCT and UNION share the NAME(alias type, ...) syntax + string head = type_name == "STRUCT" ? "STRUCT" : "UNION"; + if (params.empty()) { + stack.push_back(StackItem {nullptr, head}); + continue; + } + stack.push_back(StackItem {nullptr, ")"}); + for (idx_t i = params.size(); i > 0; i--) { + auto &curr_param = *params[i - 1]; + PushChild(stack, curr_param); + stack.push_back(StackItem {nullptr, SQLIdentifier(curr_param.GetAlias()) + " "}); + if (i > 1) { + stack.push_back(StackItem {nullptr, ", "}); + } + } + stack.push_back(StackItem {nullptr, head + "("}); + } else if (qual.empty() && type_name == "MAP" && params.size() == 2) { + stack.push_back(StackItem {nullptr, ")"}); + PushChild(stack, *params[1]); + stack.push_back(StackItem {nullptr, ", "}); + PushChild(stack, *params[0]); + stack.push_back(StackItem {nullptr, "MAP("}); + } else if (qual.empty() && type_name == "VARCHAR" && !params.empty() && params.back()->HasAlias() && + params.back()->GetAlias() == "collation") { + // Special case for VARCHAR with collation + auto &collate_expr = params.back()->Cast(); + stack.push_back(StackItem { + nullptr, StringUtil::Format("VARCHAR COLLATE %s", + SQLIdentifier(collate_expr.GetLiteral().ToValue().ToString()))}); + } else if (qual.empty() && type_name == "INTERVAL" && !params.empty()) { + // We ignore interval types parameters. + stack.push_back(StackItem {nullptr, "INTERVAL"}); + } else { + string result = qual; + auto type_id = TransformStringToLogicalTypeId(type_name.GetIdentifierName()); + if (type_id != LogicalTypeId::UNBOUND && type_id != LogicalTypeId::SQLNULL) { + // Built-in type name + result += type_name.GetIdentifierName(); + } else { + result += SQLIdentifier(type_name); + } + if (params.empty()) { + stack.push_back(StackItem {nullptr, result}); + continue; + } + stack.push_back(StackItem {nullptr, ")"}); + for (idx_t i = params.size(); i > 0; i--) { + PushChild(stack, *params[i - 1]); + if (i > 1) { + stack.push_back(StackItem {nullptr, ", "}); + } + } + stack.push_back(StackItem {nullptr, result + "("}); } } - result += ")"; } - return result; + return output; } //===--------------------------------------------------------------------===// diff --git a/src/duckdb/src/parser/expression/value_expression.cpp b/src/duckdb/src/parser/expression/value_expression.cpp index bb1f9bc39..a845e06ce 100644 --- a/src/duckdb/src/parser/expression/value_expression.cpp +++ b/src/duckdb/src/parser/expression/value_expression.cpp @@ -1,4 +1,7 @@ #include "duckdb/common/string_util.hpp" +#include "duckdb/common/extension_type_info.hpp" +#include "duckdb/common/types/geometry_crs.hpp" +#include "duckdb/parser/expression/case_expression.hpp" #include "duckdb/common/types/value.hpp" #include "duckdb/common/value_operations/value_operations.hpp" #include "duckdb/parser/expression/cast_expression.hpp" @@ -6,6 +9,12 @@ #include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/parser/expression/type_expression.hpp" +#include "duckdb/common/type_visitor.hpp" +#include "duckdb/common/types/variant_iterator.hpp" +#include "duckdb/common/types/vector.hpp" +#include "duckdb/function/scalar/generic_common.hpp" +#include "duckdb/parser/expression/collate_expression.hpp" + #include namespace duckdb { @@ -64,7 +73,7 @@ static unique_ptr StringCast(const Value &value) { } static unique_ptr ListValueExpression(vector> children) { - return make_uniq("list_value", std::move(children)); + return make_uniq(QualifiedName("system", "main", "list_value"), std::move(children)); } static vector> ChildExpressions(const vector &values) { @@ -83,14 +92,15 @@ static unique_ptr NamedArgument(const Identifier &name, const child->SetAlias(name); vector arguments; arguments.emplace_back(name, std::move(child)); - return make_uniq(Identifier(function_name), std::move(arguments)); + return make_uniq(QualifiedName("system", "main", Identifier(function_name)), + std::move(arguments)); } static unique_ptr StructExpression(const Value &value) { auto &type = value.type(); auto &children = StructValue::GetChildren(value); if (StructType::IsUnnamed(type)) { - return make_uniq("row", ChildExpressions(children)); + return make_uniq(QualifiedName("system", "main", "row"), ChildExpressions(children)); } vector arguments; for (idx_t i = 0; i < children.size(); i++) { @@ -99,7 +109,7 @@ static unique_ptr StructExpression(const Value &value) { child->SetAlias(name); arguments.emplace_back(name, std::move(child)); } - return make_uniq("struct_pack", std::move(arguments)); + return make_uniq(QualifiedName("system", "main", "struct_pack"), std::move(arguments)); } static unique_ptr MapExpression(const Value &value) { @@ -114,11 +124,213 @@ static unique_ptr MapExpression(const Value &value) { arguments.push_back(ListValueExpression(std::move(keys))); arguments.push_back(ListValueExpression(std::move(values))); // the map constructor cannot reproduce empty or NULL-only key/value types - return CastTo(value.type(), make_uniq("map", std::move(arguments))); + return CastTo(value.type(), + make_uniq(QualifiedName("system", "main", "map"), std::move(arguments))); +} + +static unique_ptr ValueFunction(const string &name, vector> arguments) { + return make_uniq(QualifiedName("system", "main", Identifier(name)), std::move(arguments)); +} + +static unique_ptr IntervalExpression(const interval_t &value) { + vector> parts; + for (auto &part : vector> {{"to_months", Value::INTEGER(value.months)}, + {"to_days", Value::INTEGER(value.days)}, + {"to_microseconds", Value::BIGINT(value.micros)}}) { + vector> arguments; + arguments.push_back(ConstantExpression::FromValue(part.second)); + parts.push_back(ValueFunction(part.first, std::move(arguments))); + } + vector> sum; + sum.push_back(std::move(parts[0])); + sum.push_back(std::move(parts[1])); + auto months_and_days = ValueFunction("add", std::move(sum)); + vector> result; + result.push_back(std::move(months_and_days)); + result.push_back(std::move(parts[2])); + return ValueFunction("add", std::move(result)); +} + +static unique_ptr GeometryExpression(const Value &value) { + auto &type = value.type(); + unique_ptr result; + if (value.IsNull()) { + auto geometry = GeoType::HasCRS(type) ? Value("GEOMETRYCOLLECTION EMPTY") : Value(); + result = CastTo(LogicalType::GEOMETRY(), ConstantExpression::FromValue(geometry)); + } else { + vector> arguments; + arguments.push_back(ConstantExpression::FromValue(Value::BLOB_RAW(StringValue::Get(value)))); + result = ValueFunction("st_geomfromwkb", std::move(arguments)); + } + if (!GeoType::HasCRS(type)) { + return result; + } + vector> arguments; + arguments.push_back(std::move(result)); + arguments.push_back(ConstantExpression::String(GeoType::GetCRS(type).GetDefinition())); + result = ValueFunction("st_setcrs", std::move(arguments)); + if (value.IsNull()) { + auto typed_null = make_uniq(); + typed_null->CaseChecksMutable().push_back({ConstantExpression::Boolean(false), std::move(result)}); + typed_null->ElseMutable() = ConstantExpression::Null(); + result = std::move(typed_null); + } + return result; +} + +static bool HasUnsupportedVariantKeys(const VariantNode &node) { + // Struct literals require nonempty, case-insensitively unique keys; inspect before the lossy STRUCT conversion. + if (node.GetTypeId() == VariantLogicalType::OBJECT) { + identifier_set_t names; + for (auto &child : node.GetObjectChildren()) { + if (child.key.GetSize() == 0 || !names.insert(Identifier(child.key.GetString())).second || + HasUnsupportedVariantKeys(child.value)) { + return true; + } + } + } else if (node.GetTypeId() == VariantLogicalType::ARRAY) { + for (auto child : node.GetArrayChildren()) { + if (HasUnsupportedVariantKeys(child)) { + return true; + } + } + } + return false; +} + +bool ConstantExpression::RequiresTypeWitness(const LogicalType &type) { + return TypeVisitor::Contains(type, [&](const LogicalType &child) { + return child.IsAggregateState() || child.id() == LogicalTypeId::TYPE || + (child.id() == LogicalTypeId::GEOMETRY && GeoType::HasCRS(child)) || + (type.id() != LogicalTypeId::VARCHAR && child.id() == LogicalTypeId::VARCHAR && + !StringType::GetCollation(child).empty()); + }); +} + +static unique_ptr NestedValueExpression(const LogicalType &type, optional_ptr value) { + if (type.IsAggregateState() || type.id() == LogicalTypeId::GEOMETRY || type.id() == LogicalTypeId::TYPE || + (type.id() != LogicalTypeId::TUPLE && TypeExpression::CanRepresent(type) && + !ConstantExpression::RequiresTypeWitness(type))) { + return ConstantExpression::FromValue(value ? value->WithType(type) : Value(type)); + } + const bool empty_list = + value && !value->IsNull() && type.id() == LogicalTypeId::LIST && ListValue::GetChildren(*value).empty(); + if (value && (value->IsNull() || empty_list)) { + auto result = make_uniq(); + result->CaseChecksMutable().push_back( + {ConstantExpression::Boolean(false), NestedValueExpression(type, nullptr)}); + result->ElseMutable() = empty_list ? ListValueExpression({}) : ConstantExpression::Null(); + return std::move(result); + } + if (type.id() == LogicalTypeId::MAP) { + vector keys, values; + if (value) { + for (auto &entry : MapValue::GetChildren(*value)) { + auto &children = StructValue::GetChildren(entry); + keys.push_back(children[0]); + values.push_back(children[1]); + } + } + vector> arguments; + arguments.push_back(ConstantExpression::FromValue(Value::LIST(MapType::KeyType(type), std::move(keys)))); + arguments.push_back(ConstantExpression::FromValue(Value::LIST(MapType::ValueType(type), std::move(values)))); + auto result = ValueFunction("map", std::move(arguments)); + return type.HasAlias() ? CastTo(type, std::move(result)) : std::move(result); + } + if (type.id() == LogicalTypeId::UNION && UnionType::GetMemberCount(type) == 1) { + auto &member_type = UnionType::GetMemberType(type, 0); + auto child = NestedValueExpression(member_type, value ? &UnionValue::GetValue(*value) : nullptr); + vector arguments; + arguments.emplace_back(UnionType::GetMemberName(type, 0), std::move(child)); + unique_ptr result = + make_uniq(QualifiedName("system", "main", "union_value"), std::move(arguments)); + return type.HasAlias() ? CastTo(type, std::move(result)) : std::move(result); + } + vector arguments; + child_list_t child_types; + optional_ptr> children; + string function; + switch (type.id()) { + case LogicalTypeId::TUPLE: + case LogicalTypeId::STRUCT: + function = type.id() == LogicalTypeId::TUPLE ? "row" : "struct_pack"; + child_types = StructType::GetChildTypes(type); + if (value) { + children = StructValue::GetChildren(*value); + } + break; + case LogicalTypeId::LIST: + function = "list_value"; + if (value) { + children = ListValue::GetChildren(*value); + } + child_types.resize(children ? children->size() : 1, {Identifier(), ListType::GetChildType(type)}); + break; + case LogicalTypeId::ARRAY: + function = "array_value"; + if (value) { + children = ArrayValue::GetChildren(*value); + } + child_types.resize(ArrayType::GetSize(type), {Identifier(), ArrayType::GetChildType(type)}); + break; + default: + throw NotImplementedException("The nested value type has no SQL constructor"); + } + for (idx_t i = 0; i < child_types.size(); i++) { + auto child = NestedValueExpression(child_types[i].second, children ? &(*children)[i] : nullptr); + auto name = function == "struct_pack" ? child_types[i].first : Identifier(); + child->SetAlias(name); + arguments.emplace_back(name, std::move(child)); + } + unique_ptr result = + make_uniq(QualifiedName("system", "main", Identifier(function)), std::move(arguments)); + return type.HasAlias() ? CastTo(type, std::move(result)) : std::move(result); } unique_ptr ConstantExpression::FromValue(const Value &value) { auto &type = value.type(); + if (type.IsAggregateState()) { + auto storage_type = type.WithAlias("").WithExtensionInfo(nullptr); + Vector source(value, count_t(1)); + Vector storage(storage_type, 1); + storage.Reinterpret(source); + auto result = ExportAggregateFunction::StateToSQL(type, FromValue(storage.GetValue(0))); + if (!result) { + throw NotImplementedException("Aggregate state SQL parameters are not representable"); + } + return result; + } + if (type.id() == LogicalTypeId::VARCHAR && !StringType::GetCollation(type).empty()) { + auto result = FromValue(value.WithType(LogicalType::VARCHAR)); + if (type.HasAlias()) { + result = CastTo(type, std::move(result)); + } + return make_uniq(StringType::GetCollation(type), std::move(result)); + } + if (type.id() == LogicalTypeId::TYPE) { + if (value.IsNull()) { + return CastTo(type, Null()); + } + vector> arguments; + arguments.push_back(FromValue(Value(TypeValue::GetType(value)))); + return ValueFunction("get_type", std::move(arguments)); + } + if (type.id() == LogicalTypeId::GEOMETRY) { + auto result = GeometryExpression(value); + return type.HasAlias() ? CastTo(type, std::move(result)) : std::move(result); + } + if (TypeVisitor::Contains(type, [](const LogicalType &child) { + return child.id() == LogicalTypeId::ENUM && EnumType::GetSize(child) == 0; + })) { + throw NotImplementedException("The nested value type has no SQL constructor"); + } + if (RequiresTypeWitness(type) || type.id() == LogicalTypeId::TUPLE) { + return NestedValueExpression(type, value); + } + if (!value.IsNull() && type.id() == LogicalTypeId::INTERVAL) { + auto result = IntervalExpression(IntervalValue::Get(value)); + return type.HasAlias() ? CastTo(type, std::move(result)) : std::move(result); + } if (value.IsNull()) { if (type.id() == LogicalTypeId::SQLNULL) { return Null(); @@ -172,18 +384,12 @@ unique_ptr ConstantExpression::FromValue(const Value &value) { case LogicalTypeId::STRUCT: return StructExpression(value); case LogicalTypeId::VARIANT: { - auto payload = VariantValue::GetValue(value); - if (payload.type().id() == LogicalTypeId::STRUCT) { - child_list_t children; - auto &values = StructValue::GetChildren(payload); - for (idx_t i = 0; i < values.size(); i++) { - children.emplace_back(StructType::GetChildName(payload.type(), i), - values[i].DefaultCastAs(LogicalType::VARIANT())); - } - payload = Value::STRUCT(std::move(children)); - } else if (payload.type().id() == LogicalTypeId::LIST) { - payload = Value::LIST(LogicalType::VARIANT(), ListValue::GetChildren(payload)); + Vector vector(value, count_t(1)); + VariantIterator iterator(vector); + if (HasUnsupportedVariantKeys(iterator.Root(0))) { + throw NotImplementedException("VARIANT object keys cannot be represented by a struct literal"); } + auto payload = VariantValue::GetValue(value); return CastTo(type, CastTo(payload.type(), FromValue(payload))); } case LogicalTypeId::LIST: { @@ -198,12 +404,11 @@ unique_ptr ConstantExpression::FromValue(const Value &value) { if (children.empty()) { return CastTo(type, ListValueExpression({})); } - return CastTo(type, make_uniq("array_value", ChildExpressions(children))); + return CastTo(type, make_uniq(QualifiedName("system", "main", "array_value"), + ChildExpressions(children))); } case LogicalTypeId::MAP: return MapExpression(value); - case LogicalTypeId::TYPE: - return TypeExpression::FromLogicalType(TypeValue::GetType(value)); case LogicalTypeId::POINTER: return FromLiteral(Literal::Pointer(value.GetPointer())); case LogicalTypeId::UNION: { diff --git a/src/duckdb/src/parser/keyword_helper.cpp b/src/duckdb/src/parser/keyword_helper.cpp index b5aefd415..f77a21235 100644 --- a/src/duckdb/src/parser/keyword_helper.cpp +++ b/src/duckdb/src/parser/keyword_helper.cpp @@ -5,29 +5,12 @@ namespace duckdb { -static KeywordCategory GetPEGKeywordCategory(const string &text) { - auto &helper = DuckDBKeywordHelper::Instance(); - if (helper.KeywordCategoryType(text, PEGKeywordCategory::KEYWORD_RESERVED)) { - return KeywordCategory::KEYWORD_RESERVED; - } - if (helper.KeywordCategoryType(text, PEGKeywordCategory::KEYWORD_UNRESERVED)) { - return KeywordCategory::KEYWORD_UNRESERVED; - } - if (helper.KeywordCategoryType(text, PEGKeywordCategory::KEYWORD_TYPE_FUNC)) { - return KeywordCategory::KEYWORD_TYPE_FUNC; - } - if (helper.KeywordCategoryType(text, PEGKeywordCategory::KEYWORD_COL_NAME)) { - return KeywordCategory::KEYWORD_COL_NAME; - } - return KeywordCategory::KEYWORD_NONE; -} - bool KeywordHelper::IsKeyword(const string &text, KeywordCategory category) { - return GetPEGKeywordCategory(text) != category; + return DuckDBKeywordHelper::Instance().GetKeywordCategory(text) != category; } KeywordCategory KeywordHelper::KeywordCategoryType(const string &text) { - return GetPEGKeywordCategory(text); + return DuckDBKeywordHelper::Instance().GetKeywordCategory(text); } bool KeywordHelper::RequiresQuotes(const string &text, bool allow_caps) { @@ -76,8 +59,14 @@ string KeywordHelper::WriteQuoted(const string &text, char quote) { return WriteQuotedAndEscaped(text, quote); } +static bool RequiresQuotesForPrinting(const string &text, bool allow_caps) { + // After a qualification dot, an underscore followed by a digit can begin a decimal literal. + const bool ambiguous_after_dot = text.size() > 1 && text[0] == '_' && StringUtil::CharacterIsDigit(text[1]); + return text.empty() || ambiguous_after_dot || KeywordHelper::RequiresQuotes(text, allow_caps); +} + string KeywordHelper::WriteOptionallyQuoted(const string &text, char quote, bool allow_caps) { - if (!RequiresQuotes(text, allow_caps)) { + if (!RequiresQuotesForPrinting(text, allow_caps)) { return text; } return WriteQuotedAndEscaped(text, quote); @@ -87,7 +76,7 @@ SQLIdentifier::SQLIdentifier(const Identifier &id) : raw_string(id.GetIdentifier } string SQLIdentifier::ToString(const string &identifier) { - if (!KeywordHelper::RequiresQuotes(identifier)) { + if (!RequiresQuotesForPrinting(identifier, true)) { return identifier; } return SQLQuotedIdentifier::ToString(identifier); diff --git a/src/duckdb/src/parser/literal.cpp b/src/duckdb/src/parser/literal.cpp index 0e553627b..6f68c67c1 100644 --- a/src/duckdb/src/parser/literal.cpp +++ b/src/duckdb/src/parser/literal.cpp @@ -145,6 +145,11 @@ static Value NumberToValue(const string &val) { str_val.GetSize() - num_underscores < Decimal::MAX_WIDTH_DECIMAL + decimal_offset) { // figure out the width/scale based on the decimal position auto width = NumericCast(str_val.GetSize() - 1 - num_underscores); + // a well-formed decimal has a single '.', so its position never sits past the width; a second (e.g. trailing) + // dot pushes decimal_position beyond width and would underflow the scale computation below + if (decimal_position.GetIndex() > NumericCast(width) + num_integer_underscores) { + throw ParserException("Could not convert string \"%s\" to a decimal value", val); + } auto scale = NumericCast(width - decimal_position.GetIndex() + num_integer_underscores); if (val[0] == '-') { width--; diff --git a/src/duckdb/src/parser/parsed_data/alter_schema_info.cpp b/src/duckdb/src/parser/parsed_data/alter_schema_info.cpp new file mode 100644 index 000000000..fc93e8009 --- /dev/null +++ b/src/duckdb/src/parser/parsed_data/alter_schema_info.cpp @@ -0,0 +1,129 @@ +#include "duckdb/parser/parsed_data/alter_schema_info.hpp" + +#include "duckdb/common/sql_identifier.hpp" +#include "duckdb/parser/keyword_helper.hpp" + +namespace duckdb { + +//===--------------------------------------------------------------------===// +// AlterSchemaInfo +//===--------------------------------------------------------------------===// +AlterSchemaInfo::AlterSchemaInfo(AlterSchemaType alter_schema_type) + : AlterInfo(AlterType::ALTER_SCHEMA), alter_schema_type(alter_schema_type) { +} + +AlterSchemaInfo::AlterSchemaInfo(AlterSchemaType alter_schema_type, const AlterEntryData &data) + : AlterInfo(AlterType::ALTER_SCHEMA, data.qualified_name, data.if_not_found), alter_schema_type(alter_schema_type) { +} + +AlterSchemaInfo::~AlterSchemaInfo() { +} + +CatalogType AlterSchemaInfo::GetCatalogType() const { + return CatalogType::SCHEMA_ENTRY; +} + +const Identifier &AlterSchemaInfo::SchemaName() const { + return GetQualifiedName().Schema(); +} + +const Identifier &AlterSchemaInfo::SchemaCatalog() const { + return GetQualifiedName().Catalog(); +} + +vector AlterSchemaInfo::SchemaPath() const { + auto &path = GetQualifiedName().Path(); + // skip the catalog (if any) and the empty trailing name + idx_t start = path.size() >= 3 ? 1 : 0; + vector result; + for (idx_t i = start; i + 1 < path.size(); i++) { + result.push_back(path[i]); + } + return result; +} + +string AlterSchemaInfo::QualifiedSchemaToString() const { + string result = "ALTER SCHEMA "; + if (if_not_found == OnEntryNotFound::RETURN_NULL) { + result += "IF EXISTS "; + } + auto &path = GetQualifiedName().Path(); + for (idx_t i = 0; i + 1 < path.size(); i++) { + if (i > 0) { + result += "."; + } + result += SQLIdentifier(path[i]); + } + return result; +} + +//===--------------------------------------------------------------------===// +// SetSchemaOptionsInfo +//===--------------------------------------------------------------------===// +SetSchemaOptionsInfo::SetSchemaOptionsInfo() : AlterSchemaInfo(AlterSchemaType::SET_SCHEMA_OPTIONS) { +} + +SetSchemaOptionsInfo::SetSchemaOptionsInfo(const AlterEntryData &data, + case_insensitive_map_t> options) + : AlterSchemaInfo(AlterSchemaType::SET_SCHEMA_OPTIONS, data), options(std::move(options)) { +} + +SetSchemaOptionsInfo::~SetSchemaOptionsInfo() { +} + +unique_ptr SetSchemaOptionsInfo::Copy() const { + case_insensitive_map_t> options_copy; + for (auto &option : options) { + options_copy.emplace(option.first, option.second->Copy()); + } + return make_uniq(GetAlterEntryData(), std::move(options_copy)); +} + +string SetSchemaOptionsInfo::ToString() const { + string result = QualifiedSchemaToString(); + result += " SET ("; + idx_t i = 0; + for (auto &entry : options) { + if (i > 0) { + result += ", "; + } + result += SQLString(entry.first) + "=" + entry.second->ToString(); + i++; + } + result += ")"; + return result; +} + +//===--------------------------------------------------------------------===// +// ResetSchemaOptionsInfo +//===--------------------------------------------------------------------===// +ResetSchemaOptionsInfo::ResetSchemaOptionsInfo() : AlterSchemaInfo(AlterSchemaType::RESET_SCHEMA_OPTIONS) { +} + +ResetSchemaOptionsInfo::ResetSchemaOptionsInfo(const AlterEntryData &data, identifier_set_t options) + : AlterSchemaInfo(AlterSchemaType::RESET_SCHEMA_OPTIONS, data), options(std::move(options)) { +} + +ResetSchemaOptionsInfo::~ResetSchemaOptionsInfo() { +} + +unique_ptr ResetSchemaOptionsInfo::Copy() const { + return make_uniq(GetAlterEntryData(), options); +} + +string ResetSchemaOptionsInfo::ToString() const { + string result = QualifiedSchemaToString(); + result += " RESET ("; + idx_t i = 0; + for (auto &entry : options) { + if (i > 0) { + result += ", "; + } + result += SQLString(entry.GetIdentifierName()); + i++; + } + result += ")"; + return result; +} + +} // namespace duckdb diff --git a/src/duckdb/src/parser/parsed_data/alter_table_function_info.cpp b/src/duckdb/src/parser/parsed_data/alter_table_function_info.cpp index e0ed3608e..5b89fa358 100644 --- a/src/duckdb/src/parser/parsed_data/alter_table_function_info.cpp +++ b/src/duckdb/src/parser/parsed_data/alter_table_function_info.cpp @@ -19,9 +19,10 @@ CatalogType AlterTableFunctionInfo::GetCatalogType() const { //===--------------------------------------------------------------------===// // AddTableFunctionOverloadInfo //===--------------------------------------------------------------------===// -AddTableFunctionOverloadInfo::AddTableFunctionOverloadInfo(const AlterEntryData &data, TableFunctionSet new_overloads_p) +AddTableFunctionOverloadInfo::AddTableFunctionOverloadInfo(const AlterEntryData &data, TableFunctionSet new_overloads_p, + vector new_descriptions_p) : AlterTableFunctionInfo(AlterTableFunctionType::ADD_FUNCTION_OVERLOADS, data), - new_overloads(std::move(new_overloads_p)) { + new_overloads(std::move(new_overloads_p)), new_descriptions(std::move(new_descriptions_p)) { this->allow_internal = true; } @@ -29,7 +30,8 @@ AddTableFunctionOverloadInfo::~AddTableFunctionOverloadInfo() { } unique_ptr AddTableFunctionOverloadInfo::Copy() const { - return make_uniq_base(GetAlterEntryData(), new_overloads); + return make_uniq_base(GetAlterEntryData(), new_overloads, + new_descriptions); } string AddTableFunctionOverloadInfo::ToString() const { diff --git a/src/duckdb/src/parser/parsed_data/alter_table_info.cpp b/src/duckdb/src/parser/parsed_data/alter_table_info.cpp index 54f814cb2..b236366a4 100644 --- a/src/duckdb/src/parser/parsed_data/alter_table_info.cpp +++ b/src/duckdb/src/parser/parsed_data/alter_table_info.cpp @@ -1,7 +1,7 @@ #include "duckdb/parser/parsed_data/alter_table_info.hpp" #include "duckdb/common/sql_identifier.hpp" -#include "duckdb/common/extra_type_info.hpp" +#include "duckdb/common/logical_type_info.hpp" #include "duckdb/parser/constraint.hpp" #include "duckdb/parser/keyword_helper.hpp" @@ -211,10 +211,10 @@ AddColumnInfo::AddColumnInfo(ColumnDefinition new_column_p) : AlterTableInfo(AlterTableType::ADD_COLUMN), new_column(std::move(new_column_p)) { } -AddColumnInfo::AddColumnInfo(const AlterEntryData &data, ColumnDefinition new_column, bool if_column_not_exists, - bool add_not_null) - : AlterTableInfo(AlterTableType::ADD_COLUMN, data), new_column(std::move(new_column)), - if_column_not_exists(if_column_not_exists), add_not_null(add_not_null) { +AddColumnInfo::AddColumnInfo(const AlterEntryData &data, ColumnDefinition new_column_p, bool if_column_not_exists_p, + AddColumnConstraints add_column_constraints_p) + : AlterTableInfo(AlterTableType::ADD_COLUMN, data), new_column(std::move(new_column_p)), + if_column_not_exists(if_column_not_exists_p), add_column_constraints(add_column_constraints_p) { } AddColumnInfo::~AddColumnInfo() { @@ -222,7 +222,7 @@ AddColumnInfo::~AddColumnInfo() { unique_ptr AddColumnInfo::Copy() const { return make_uniq_base(GetAlterEntryData(), new_column.Copy(), if_column_not_exists, - add_not_null); + add_column_constraints); } string AddColumnInfo::ToString() const { @@ -238,13 +238,19 @@ string AddColumnInfo::ToString() const { } result += " " + SQLIdentifier(this->new_column.GetName()); result += " " + this->new_column.GetType().ToString(); - if (add_not_null) { + if (add_column_constraints.add_not_null) { result += " NOT NULL"; } + if (add_column_constraints.add_unique) { + result += " UNIQUE"; + } if (this->new_column.HasDefaultValue()) { result += " DEFAULT "; result += this->new_column.DefaultValue().ToString(); } + if (this->new_column.CompressionType() != CompressionType::COMPRESSION_AUTO) { + result += " USING COMPRESSION " + CompressionTypeToString(this->new_column.CompressionType()); + } result += ";"; return result; } @@ -375,17 +381,17 @@ string RemoveFieldInfo::ToString() const { ChangeColumnTypeInfo::ChangeColumnTypeInfo() : AlterTableInfo(AlterTableType::ALTER_COLUMN_TYPE) { } -ChangeColumnTypeInfo::ChangeColumnTypeInfo(const AlterEntryData &data, Identifier column_name, LogicalType target_type, - unique_ptr expression) - : AlterTableInfo(AlterTableType::ALTER_COLUMN_TYPE, data), column_name(std::move(column_name)), +ChangeColumnTypeInfo::ChangeColumnTypeInfo(const AlterEntryData &data, vector column_path, + LogicalType target_type, unique_ptr expression) + : AlterTableInfo(AlterTableType::ALTER_COLUMN_TYPE, data), column_path(std::move(column_path)), target_type(std::move(target_type)), expression(std::move(expression)) { } ChangeColumnTypeInfo::~ChangeColumnTypeInfo() { } unique_ptr ChangeColumnTypeInfo::Copy() const { - return make_uniq_base(GetAlterEntryData(), column_name, target_type, - expression->Copy()); + return make_uniq_base(GetAlterEntryData(), column_path, target_type, + expression ? expression->Copy() : nullptr); } string ChangeColumnTypeInfo::ToString() const { @@ -396,14 +402,19 @@ string ChangeColumnTypeInfo::ToString() const { } result += GetQualifiedName().ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); result += " ALTER COLUMN "; - result += SQLIdentifier(column_name); + for (idx_t i = 0; i < column_path.size(); i++) { + if (i > 0) { + result += "."; + } + result += SQLIdentifier(column_path[i]); + } result += " TYPE "; if (target_type.IsValid()) { result += target_type.ToString(); } - auto extra_type_info = target_type.AuxInfo(); - if (extra_type_info && extra_type_info->type == ExtraTypeInfoType::STRING_TYPE_INFO) { - auto &string_info = extra_type_info->Cast(); + auto &type_info = target_type.GetTypeInfo(); + if (type_info.type == LogicalTypeInfoType::STRING_TYPE_INFO) { + auto &string_info = type_info.Cast(); if (!string_info.collation.empty()) { result += " COLLATE " + string_info.collation; } @@ -422,16 +433,16 @@ string ChangeColumnTypeInfo::ToString() const { SetDefaultInfo::SetDefaultInfo() : AlterTableInfo(AlterTableType::SET_DEFAULT) { } -SetDefaultInfo::SetDefaultInfo(const AlterEntryData &data, Identifier column_name_p, +SetDefaultInfo::SetDefaultInfo(const AlterEntryData &data, vector column_path, unique_ptr new_default) - : AlterTableInfo(AlterTableType::SET_DEFAULT, data), column_name(std::move(column_name_p)), + : AlterTableInfo(AlterTableType::SET_DEFAULT, data), column_path(std::move(column_path)), expression(std::move(new_default)) { } SetDefaultInfo::~SetDefaultInfo() { } unique_ptr SetDefaultInfo::Copy() const { - return make_uniq_base(GetAlterEntryData(), column_name, + return make_uniq_base(GetAlterEntryData(), column_path, expression ? expression->Copy() : nullptr); } @@ -443,7 +454,12 @@ string SetDefaultInfo::ToString() const { } result += GetQualifiedName().ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); result += " ALTER COLUMN "; - result += SQLIdentifier(column_name); + for (idx_t i = 0; i < column_path.size(); i++) { + if (i > 0) { + result += "."; + } + result += SQLIdentifier(column_path[i]); + } if (expression) { result += " SET DEFAULT "; result += expression->ToString(); @@ -460,14 +476,14 @@ string SetDefaultInfo::ToString() const { SetNotNullInfo::SetNotNullInfo() : AlterTableInfo(AlterTableType::SET_NOT_NULL) { } -SetNotNullInfo::SetNotNullInfo(const AlterEntryData &data, Identifier column_name_p) - : AlterTableInfo(AlterTableType::SET_NOT_NULL, data), column_name(std::move(column_name_p)) { +SetNotNullInfo::SetNotNullInfo(const AlterEntryData &data, vector column_path) + : AlterTableInfo(AlterTableType::SET_NOT_NULL, data), column_path(std::move(column_path)) { } SetNotNullInfo::~SetNotNullInfo() { } unique_ptr SetNotNullInfo::Copy() const { - return make_uniq_base(GetAlterEntryData(), column_name); + return make_uniq_base(GetAlterEntryData(), column_path); } string SetNotNullInfo::ToString() const { @@ -478,7 +494,12 @@ string SetNotNullInfo::ToString() const { } result += GetQualifiedName().ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); result += " ALTER COLUMN "; - result += SQLIdentifier(column_name); + for (idx_t i = 0; i < column_path.size(); i++) { + if (i > 0) { + result += "."; + } + result += SQLIdentifier(column_path[i]); + } result += " SET NOT NULL"; result += ";"; return result; @@ -490,14 +511,14 @@ string SetNotNullInfo::ToString() const { DropNotNullInfo::DropNotNullInfo() : AlterTableInfo(AlterTableType::DROP_NOT_NULL) { } -DropNotNullInfo::DropNotNullInfo(const AlterEntryData &data, Identifier column_name_p) - : AlterTableInfo(AlterTableType::DROP_NOT_NULL, data), column_name(std::move(column_name_p)) { +DropNotNullInfo::DropNotNullInfo(const AlterEntryData &data, vector column_path) + : AlterTableInfo(AlterTableType::DROP_NOT_NULL, data), column_path(std::move(column_path)) { } DropNotNullInfo::~DropNotNullInfo() { } unique_ptr DropNotNullInfo::Copy() const { - return make_uniq_base(GetAlterEntryData(), column_name); + return make_uniq_base(GetAlterEntryData(), column_path); } string DropNotNullInfo::ToString() const { @@ -508,7 +529,12 @@ string DropNotNullInfo::ToString() const { } result += GetQualifiedName().ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); result += " ALTER COLUMN "; - result += SQLIdentifier(column_name); + for (idx_t i = 0; i < column_path.size(); i++) { + if (i > 0) { + result += "."; + } + result += SQLIdentifier(column_path[i]); + } result += " DROP NOT NULL"; result += ";"; return result; diff --git a/src/duckdb/src/parser/parsed_data/attach_info.cpp b/src/duckdb/src/parser/parsed_data/attach_info.cpp index c4a0251ce..0fd12023c 100644 --- a/src/duckdb/src/parser/parsed_data/attach_info.cpp +++ b/src/duckdb/src/parser/parsed_data/attach_info.cpp @@ -18,11 +18,26 @@ unique_ptr AttachInfo::Copy() const { result->parsed_options[entry.first] = entry.second->Copy(); } result->on_conflict = on_conflict; + if (external_resource) { + result->external_resource = external_resource->Copy(); + } return result; } string AttachInfo::ToString() const { string result = ""; + // `ATTACH TO [NEW TEMPORARY] EXTERNAL RESOURCE [(create opts)] AS name [(attach opts)]` + if (external_resource) { + // No IF NOT EXISTS / OR REPLACE: AttachToExternalResource has no slot for either, so rendering + // one would produce SQL that cannot be parsed back. + result += "ATTACH TO " + external_resource->ToString(); + if (!name.empty()) { + result += " AS " + SQLIdentifier(name); + } + result += RenderOptionList(parsed_options, options); + result += ";"; + return result; + } result += "ATTACH"; if (on_conflict == OnCreateConflict::IGNORE_ON_CONFLICT) { result += " IF NOT EXISTS"; @@ -38,18 +53,7 @@ string AttachInfo::ToString() const { if (!name.empty()) { result += " AS " + SQLIdentifier(name); } - if (!parsed_options.empty() || !options.empty()) { - vector stringified; - for (auto &opt : parsed_options) { - stringified.push_back( - StringUtil::Format("%s %s", SQLIdentifier::ToString(opt.first), opt.second->ToString())); - } - for (auto &opt : options) { - stringified.push_back( - StringUtil::Format("%s %s", SQLIdentifier::ToString(opt.first), opt.second.ToSQLString())); - } - result += " (" + StringUtil::Join(stringified, ", ") + ")"; - } + result += RenderOptionList(parsed_options, options); result += ";"; return result; } diff --git a/src/duckdb/src/parser/parsed_data/connect_info.cpp b/src/duckdb/src/parser/parsed_data/connect_info.cpp index 153e13166..5bea79484 100644 --- a/src/duckdb/src/parser/parsed_data/connect_info.cpp +++ b/src/duckdb/src/parser/parsed_data/connect_info.cpp @@ -15,10 +15,17 @@ unique_ptr ConnectInfo::Copy() const { result->parsed_options[entry.first] = entry.second->Copy(); } result->options = options; + if (external_resource) { + result->external_resource = external_resource->Copy(); + } return result; } string ConnectInfo::ToString() const { + if (external_resource) { + // Only the borrow form can carry connect options; provisioning gives its one list to the recipe. + return "CONNECT TO " + external_resource->ToString() + RenderOptionList(parsed_options, options) + ";"; + } if (target_is_local) { return "CONNECT LOCAL;"; } @@ -31,18 +38,7 @@ string ConnectInfo::ToString() const { } else { result += SQLIdentifier(name); } - if (!parsed_options.empty() || !options.empty()) { - vector stringified; - for (auto &opt : parsed_options) { - stringified.push_back( - StringUtil::Format("%s %s", SQLIdentifier::ToString(opt.first), opt.second->ToString())); - } - for (auto &opt : options) { - stringified.push_back( - StringUtil::Format("%s %s", SQLIdentifier::ToString(opt.first), opt.second.ToSQLString())); - } - result += " (" + StringUtil::Join(stringified, ", ") + ")"; - } + result += RenderOptionList(parsed_options, options); result += ";"; return result; } diff --git a/src/duckdb/src/parser/parsed_data/copy_info.cpp b/src/duckdb/src/parser/parsed_data/copy_info.cpp index d0fe2bac4..939d54333 100644 --- a/src/duckdb/src/parser/parsed_data/copy_info.cpp +++ b/src/duckdb/src/parser/parsed_data/copy_info.cpp @@ -1,5 +1,7 @@ #include "duckdb/parser/parsed_data/copy_info.hpp" +#include "duckdb/common/algorithm.hpp" + #include "duckdb/common/sql_identifier.hpp" #include "duckdb/parser/query_node.hpp" @@ -70,18 +72,27 @@ string CopyInfo::CopyOptionsToString() const { if (!format.empty() && !is_format_auto_detected) { stringified.push_back(StringUtil::Format(" FORMAT %s", SQLString(format))); } + // render the options in sorted order so the generated text is deterministic across platforms + vector parsed_option_names; for (auto &opt : parsed_options) { - auto &name = opt.first; - auto &expr = opt.second; + parsed_option_names.push_back(opt.first); + } + std::sort(parsed_option_names.begin(), parsed_option_names.end()); + for (auto &name : parsed_option_names) { + auto &expr = parsed_options.find(name)->second; string option_string = StringUtil::Format("%s", name); if (expr) { option_string += " " + expr->ToString(); } stringified.push_back(option_string); } + vector option_names; for (auto &opt : options) { - auto &name = opt.first; - auto &values = opt.second; + option_names.push_back(opt.first); + } + std::sort(option_names.begin(), option_names.end()); + for (auto &name : option_names) { + auto &values = options.find(name)->second; auto option = StringUtil::Format("%s ", name); if (values.empty()) { diff --git a/src/duckdb/src/parser/parsed_data/create_aggregate_function_info.cpp b/src/duckdb/src/parser/parsed_data/create_aggregate_function_info.cpp index b00a7e4ee..9d6ebf245 100644 --- a/src/duckdb/src/parser/parsed_data/create_aggregate_function_info.cpp +++ b/src/duckdb/src/parser/parsed_data/create_aggregate_function_info.cpp @@ -5,6 +5,7 @@ namespace duckdb { CreateAggregateFunctionInfo::CreateAggregateFunctionInfo(AggregateFunction function) : CreateFunctionInfo(CatalogType::AGGREGATE_FUNCTION_ENTRY), functions(function.name) { SetFunctionName(function.name); + function.GetSignature().Verify(); functions.AddFunction(std::move(function)); internal = true; } @@ -12,6 +13,9 @@ CreateAggregateFunctionInfo::CreateAggregateFunctionInfo(AggregateFunction funct CreateAggregateFunctionInfo::CreateAggregateFunctionInfo(AggregateFunctionSet set) : CreateFunctionInfo(CatalogType::AGGREGATE_FUNCTION_ENTRY), functions(std::move(set)) { SetFunctionName(functions.name); + for (auto &func : functions.functions) { + func->GetSignature().Verify(); + } functions.ApplyToFunctions([&](AggregateFunction &func) { func.name = functions.name; }); internal = true; } diff --git a/src/duckdb/src/parser/parsed_data/create_index_info.cpp b/src/duckdb/src/parser/parsed_data/create_index_info.cpp index 9e3c4593e..82cffa43f 100644 --- a/src/duckdb/src/parser/parsed_data/create_index_info.cpp +++ b/src/duckdb/src/parser/parsed_data/create_index_info.cpp @@ -94,10 +94,9 @@ string CreateIndexInfo::ToString() const { if (i > 0) { result += ", "; } - if (opt.second.IsNull()) { - result += opt.first; - } else { - result += StringUtil::Format("%s = %s", opt.first, opt.second.ToString()); + result += SQLIdentifier(opt.first); + if (!opt.second.IsNull()) { + result += " = " + opt.second.ToSQLString(); } i++; } diff --git a/src/duckdb/src/parser/parsed_data/create_pragma_function_info.cpp b/src/duckdb/src/parser/parsed_data/create_pragma_function_info.cpp index 5448e0fb3..569ee84ef 100644 --- a/src/duckdb/src/parser/parsed_data/create_pragma_function_info.cpp +++ b/src/duckdb/src/parser/parsed_data/create_pragma_function_info.cpp @@ -5,12 +5,16 @@ namespace duckdb { CreatePragmaFunctionInfo::CreatePragmaFunctionInfo(PragmaFunction function) : CreateFunctionInfo(CatalogType::PRAGMA_FUNCTION_ENTRY), functions(function.name) { SetFunctionName(function.name); + function.GetSignature().Verify(); functions.AddFunction(std::move(function)); internal = true; } CreatePragmaFunctionInfo::CreatePragmaFunctionInfo(PragmaFunctionSet functions_p) : CreateFunctionInfo(CatalogType::PRAGMA_FUNCTION_ENTRY), functions(std::move(functions_p)) { SetFunctionName(functions.name); + for (auto &func : functions.functions) { + func->GetSignature().Verify(); + } internal = true; } CreatePragmaFunctionInfo::CreatePragmaFunctionInfo(Identifier name, PragmaFunctionSet functions_p) diff --git a/src/duckdb/src/parser/parsed_data/create_scalar_function_info.cpp b/src/duckdb/src/parser/parsed_data/create_scalar_function_info.cpp index 7df28cbd1..76b2c1ec1 100644 --- a/src/duckdb/src/parser/parsed_data/create_scalar_function_info.cpp +++ b/src/duckdb/src/parser/parsed_data/create_scalar_function_info.cpp @@ -6,12 +6,16 @@ namespace duckdb { CreateScalarFunctionInfo::CreateScalarFunctionInfo(ScalarFunction function) : CreateFunctionInfo(CatalogType::SCALAR_FUNCTION_ENTRY), functions(function.name) { SetFunctionName(function.name); + function.GetSignature().Verify(); functions.AddFunction(std::move(function)); internal = true; } CreateScalarFunctionInfo::CreateScalarFunctionInfo(ScalarFunctionSet set) : CreateFunctionInfo(CatalogType::SCALAR_FUNCTION_ENTRY), functions(std::move(set)) { SetFunctionName(functions.name); + for (auto &func : functions.functions) { + func->GetSignature().Verify(); + } functions.ApplyToFunctions([&](ScalarFunction &func) { func.name = functions.name; }); internal = true; } diff --git a/src/duckdb/src/parser/parsed_data/create_schema_info.cpp b/src/duckdb/src/parser/parsed_data/create_schema_info.cpp index 4a7c984fb..adc02ec29 100644 --- a/src/duckdb/src/parser/parsed_data/create_schema_info.cpp +++ b/src/duckdb/src/parser/parsed_data/create_schema_info.cpp @@ -1,5 +1,5 @@ #include "duckdb/parser/parsed_data/create_schema_info.hpp" -#include "duckdb/common/sql_identifier.hpp" +#include "duckdb/common/exception/catalog_exception.hpp" namespace duckdb { @@ -12,10 +12,8 @@ const Identifier &CreateSchemaInfo::SchemaName() const { } const Identifier &CreateSchemaInfo::SchemaCatalog() const { - static const Identifier EMPTY; - auto &path = GetQualifiedName().Path(); // the catalog is the leading component once the path carries [catalog, schema, ] - return path.size() >= 3 ? path[0] : EMPTY; + return GetQualifiedName().Catalog(); } vector CreateSchemaInfo::ParentSchemas() const { @@ -33,24 +31,44 @@ bool CreateSchemaInfo::IsNested() const { return GetQualifiedName().Path().size() > 3; } +bool CreateSchemaInfo::ShouldReplaceOnConflict() const { + switch (on_conflict) { + case OnCreateConflict::ERROR_ON_CONFLICT: + throw CatalogException::EntryAlreadyExists(CatalogType::SCHEMA_ENTRY, SchemaName()); + case OnCreateConflict::IGNORE_ON_CONFLICT: + return false; + case OnCreateConflict::REPLACE_ON_CONFLICT: + return true; + default: + throw InternalException("Unsupported OnCreateConflict for CreateSchema"); + } +} + unique_ptr CreateSchemaInfo::Copy() const { auto result = make_uniq(); CopyProperties(*result); + for (auto &option : options) { + result->options.emplace(option.first, option.second->Copy()); + } return std::move(result); } string CreateSchemaInfo::ToString() const { - string qualified; - auto &path = GetQualifiedName().Path(); - // the last element is the (empty) trailing name slot - the schema itself is the element before it - for (idx_t i = 0; i + 1 < path.size(); i++) { - if (!qualified.empty()) { - qualified += "."; - } - qualified += SQLIdentifier(path[i]); - } + auto qualified = GetQualifiedName().Parent().ToString(); string temp = temporary ? "TEMPORARY " : ""; + if (!options.empty()) { + qualified += " WITH ("; + idx_t i = 0; + for (auto &entry : options) { + if (i > 0) { + qualified += ", "; + } + qualified += SQLString(entry.first) + "=" + entry.second->ToString(); + i++; + } + qualified += ")"; + } string ret = ""; switch (on_conflict) { diff --git a/src/duckdb/src/parser/parsed_data/create_table_function_info.cpp b/src/duckdb/src/parser/parsed_data/create_table_function_info.cpp index fd5b4bb3d..3a44eb103 100644 --- a/src/duckdb/src/parser/parsed_data/create_table_function_info.cpp +++ b/src/duckdb/src/parser/parsed_data/create_table_function_info.cpp @@ -6,12 +6,16 @@ namespace duckdb { CreateTableFunctionInfo::CreateTableFunctionInfo(TableFunction function) : CreateFunctionInfo(CatalogType::TABLE_FUNCTION_ENTRY), functions(function.name) { SetFunctionName(function.name); + function.GetSignature().Verify(); functions.AddFunction(std::move(function)); internal = true; } CreateTableFunctionInfo::CreateTableFunctionInfo(TableFunctionSet set) : CreateFunctionInfo(CatalogType::TABLE_FUNCTION_ENTRY), functions(std::move(set)) { SetFunctionName(functions.name); + for (auto &func : functions.functions) { + func->GetSignature().Verify(); + } functions.ApplyToFunctions([&](TableFunction &func) { func.name = functions.name; }); internal = true; } @@ -26,7 +30,7 @@ unique_ptr CreateTableFunctionInfo::Copy() const { unique_ptr CreateTableFunctionInfo::GetAlterInfo() const { return make_uniq_base( - AlterEntryData(GetQualifiedName(), OnEntryNotFound::RETURN_NULL), functions); + AlterEntryData(GetQualifiedName(), OnEntryNotFound::RETURN_NULL), functions, descriptions); } } // namespace duckdb diff --git a/src/duckdb/src/parser/parsed_data/create_table_info.cpp b/src/duckdb/src/parser/parsed_data/create_table_info.cpp index d27c0f179..e1a77e46f 100644 --- a/src/duckdb/src/parser/parsed_data/create_table_info.cpp +++ b/src/duckdb/src/parser/parsed_data/create_table_info.cpp @@ -2,6 +2,7 @@ #include "duckdb/catalog/catalog_entry/schema_catalog_entry.hpp" #include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp" #include "duckdb/catalog/catalog.hpp" +#include "duckdb/parser/constraints/not_null_constraint.hpp" namespace duckdb { @@ -38,6 +39,16 @@ unique_ptr CreateTableInfo::Copy() const { return std::move(result); } +optional_idx CreateTableInfo::FindNotNullConstraint(LogicalIndex column) const { + for (idx_t constraint_idx = 0; constraint_idx < constraints.size(); constraint_idx++) { + auto &constraint = *constraints[constraint_idx]; + if (constraint.type == ConstraintType::NOT_NULL && constraint.Cast().index == column) { + return constraint_idx; + } + } + return optional_idx(); +} + string CreateTableInfo::ExtraOptionsToString() const { string ret; if (!partition_keys.empty()) { diff --git a/src/duckdb/src/parser/parsed_data/create_type_info.cpp b/src/duckdb/src/parser/parsed_data/create_type_info.cpp index d3c1103fe..9397141c8 100644 --- a/src/duckdb/src/parser/parsed_data/create_type_info.cpp +++ b/src/duckdb/src/parser/parsed_data/create_type_info.cpp @@ -1,7 +1,7 @@ #include "duckdb/parser/parsed_data/create_type_info.hpp" #include "duckdb/common/sql_identifier.hpp" -#include "duckdb/common/extra_type_info.hpp" +#include "duckdb/common/logical_type_info.hpp" #include "duckdb/common/sql_identifier.hpp" namespace duckdb { diff --git a/src/duckdb/src/parser/parsed_data/create_view_info.cpp b/src/duckdb/src/parser/parsed_data/create_view_info.cpp index d4c5cdafa..4e06ca1f0 100644 --- a/src/duckdb/src/parser/parsed_data/create_view_info.cpp +++ b/src/duckdb/src/parser/parsed_data/create_view_info.cpp @@ -50,8 +50,7 @@ unique_ptr CreateViewInfo::Copy() const { return std::move(result); } -unique_ptr CreateViewInfo::ParseSelect(const string &sql) { - Parser parser; +unique_ptr CreateViewInfo::ParseSelect(Parser &parser, const string &sql) { parser.ParseQuery(sql); if (parser.statements.size() != 1 || parser.statements[0]->type != StatementType::SELECT_STATEMENT) { throw BinderException( @@ -62,13 +61,13 @@ unique_ptr CreateViewInfo::ParseSelect(const string &sql) { return unique_ptr_cast(std::move(parser.statements[0])); } -unique_ptr CreateViewInfo::FromSelect(ClientContext &context, unique_ptr info) { +unique_ptr CreateViewInfo::FromSelect(Parser &parser, unique_ptr info) { D_ASSERT(info); D_ASSERT(!info->GetViewName().empty()); D_ASSERT(!info->sql.empty()); D_ASSERT(!info->query); - info->query = ParseSelect(info->sql); + info->query = ParseSelect(parser, info->sql); return info; } @@ -77,7 +76,7 @@ unique_ptr CreateViewInfo::FromCreateView(ClientContext &context D_ASSERT(!sql.empty()); // parse the SQL statement - Parser parser; + Parser parser(context); parser.ParseQuery(sql); if (parser.statements.size() != 1 || parser.statements[0]->type != StatementType::CREATE_STATEMENT) { diff --git a/src/duckdb/src/parser/parsed_data/create_window_function_info.cpp b/src/duckdb/src/parser/parsed_data/create_window_function_info.cpp index 545848358..0801d76b9 100644 --- a/src/duckdb/src/parser/parsed_data/create_window_function_info.cpp +++ b/src/duckdb/src/parser/parsed_data/create_window_function_info.cpp @@ -5,6 +5,7 @@ namespace duckdb { CreateWindowFunctionInfo::CreateWindowFunctionInfo(WindowFunction function) : CreateFunctionInfo(CatalogType::WINDOW_FUNCTION_ENTRY), functions(function.name) { SetFunctionName(function.name); + function.GetSignature().Verify(); functions.AddFunction(std::move(function)); internal = true; } @@ -12,6 +13,9 @@ CreateWindowFunctionInfo::CreateWindowFunctionInfo(WindowFunction function) CreateWindowFunctionInfo::CreateWindowFunctionInfo(WindowFunctionSet set) : CreateFunctionInfo(CatalogType::WINDOW_FUNCTION_ENTRY), functions(std::move(set)) { SetFunctionName(functions.name); + for (auto &func : functions.functions) { + func->GetSignature().Verify(); + } functions.ApplyToFunctions([&](WindowFunction &func) { func.name = functions.name; }); internal = true; } diff --git a/src/duckdb/src/parser/parsed_data/external_resource_options.cpp b/src/duckdb/src/parser/parsed_data/external_resource_options.cpp new file mode 100644 index 000000000..28699c725 --- /dev/null +++ b/src/duckdb/src/parser/parsed_data/external_resource_options.cpp @@ -0,0 +1,49 @@ +#include "duckdb/parser/parsed_data/external_resource_options.hpp" + +#include "duckdb/parser/parsed_data/parse_info.hpp" + +#include "duckdb/common/serializer/deserializer.hpp" +#include "duckdb/common/sql_identifier.hpp" +#include "duckdb/common/serializer/serializer.hpp" +#include "duckdb/common/string_util.hpp" +#include "duckdb/parser/keyword_helper.hpp" + +namespace duckdb { + +unique_ptr ExternalResourceOptions::Copy() const { + auto result = make_uniq(); + for (auto &entry : parsed_params) { + result->parsed_params[entry.first] = entry.second->Copy(); + } + result->provider = provider; + result->params = params; + result->reference_name = reference_name; + return result; +} + +string ExternalResourceOptions::ToString() const { + // Reference form: a bare identifier naming an already-registered resource. + if (!reference_name.empty()) { + return "EXTERNAL RESOURCE " + SQLIdentifier(reference_name); + } + // Create form: NEW TEMPORARY EXTERNAL RESOURCE '' [(create opts)]. + string result = "NEW TEMPORARY EXTERNAL RESOURCE " + SQLString(provider); + result += RenderOptionList(parsed_params, params); + return result; +} + +void ExternalResourceOptions::Serialize(Serializer &serializer) const { + serializer.WritePropertyWithDefault(101, "provider", provider); + serializer.WritePropertyWithDefault>(102, "params", params); + serializer.WritePropertyWithDefault(103, "reference_name", reference_name); +} + +unique_ptr ExternalResourceOptions::Deserialize(Deserializer &deserializer) { + auto result = make_uniq(); + deserializer.ReadPropertyWithDefault(101, "provider", result->provider); + deserializer.ReadPropertyWithDefault>(102, "params", result->params); + deserializer.ReadPropertyWithDefault(103, "reference_name", result->reference_name); + return result; +} + +} // namespace duckdb diff --git a/src/duckdb/src/parser/parsed_data/extra_drop_info.cpp b/src/duckdb/src/parser/parsed_data/extra_drop_info.cpp index 681842f08..cc0681f40 100644 --- a/src/duckdb/src/parser/parsed_data/extra_drop_info.cpp +++ b/src/duckdb/src/parser/parsed_data/extra_drop_info.cpp @@ -13,7 +13,8 @@ unique_ptr ExtraDropTriggerInfo::Copy() const { return make_uniq(*this); } -ExtraDropSecretInfo::ExtraDropSecretInfo() : ExtraDropInfo(ExtraDropInfoType::SECRET_INFO) { +ExtraDropSecretInfo::ExtraDropSecretInfo() + : ExtraDropInfo(ExtraDropInfoType::SECRET_INFO), persist_mode(SecretPersistType::DEFAULT) { } ExtraDropSecretInfo::ExtraDropSecretInfo(const ExtraDropSecretInfo &info) diff --git a/src/duckdb/src/parser/parsed_data/parse_info.cpp b/src/duckdb/src/parser/parsed_data/parse_info.cpp index 4976b9b1c..69b02fe34 100644 --- a/src/duckdb/src/parser/parsed_data/parse_info.cpp +++ b/src/duckdb/src/parser/parsed_data/parse_info.cpp @@ -1,4 +1,7 @@ #include "duckdb/parser/parsed_data/parse_info.hpp" +#include "duckdb/parser/parsed_expression.hpp" +#include "duckdb/common/sql_identifier.hpp" +#include "duckdb/common/string_util.hpp" #include "duckdb/common/enums/catalog_type.hpp" #include "duckdb/common/enum_util.hpp" #include "duckdb/parser/keyword_helper.hpp" @@ -35,4 +38,20 @@ string ParseInfo::TypeToString(CatalogType type) { } } +string RenderOptionList(const case_insensitive_map_t> &parsed_options, + const unordered_map &options) { + if (parsed_options.empty() && options.empty()) { + return string(); + } + vector stringified; + for (auto &opt : parsed_options) { + stringified.push_back(StringUtil::Format("%s %s", SQLIdentifier::ToString(opt.first), opt.second->ToString())); + } + for (auto &opt : options) { + stringified.push_back( + StringUtil::Format("%s %s", SQLIdentifier::ToString(opt.first), opt.second.ToSQLString())); + } + return " (" + StringUtil::Join(stringified, ", ") + ")"; +} + } // namespace duckdb diff --git a/src/duckdb/src/parser/parsed_expression.cpp b/src/duckdb/src/parser/parsed_expression.cpp index e6ff5f1f4..0d06b8e57 100644 --- a/src/duckdb/src/parser/parsed_expression.cpp +++ b/src/duckdb/src/parser/parsed_expression.cpp @@ -62,4 +62,114 @@ bool ParsedExpression::ListEquals(const vector> &le return ExpressionUtil::ListEquals(left, right); } +static bool IsComparisonType(ExpressionType type) { + switch (type) { + case ExpressionType::COMPARE_EQUAL: + case ExpressionType::COMPARE_NOTEQUAL: + case ExpressionType::COMPARE_LESSTHAN: + case ExpressionType::COMPARE_GREATERTHAN: + case ExpressionType::COMPARE_LESSTHANOREQUALTO: + case ExpressionType::COMPARE_GREATERTHANOREQUALTO: + case ExpressionType::COMPARE_DISTINCT_FROM: + case ExpressionType::COMPARE_NOT_DISTINCT_FROM: + return true; + default: + return false; + } +} + +//! Whether an OperatorExpression of the given type can have the given number of children +static bool IsValidOperator(ExpressionType type, idx_t child_count) { + switch (type) { + case ExpressionType::COMPARE_IN: + case ExpressionType::COMPARE_NOT_IN: + return child_count >= 2; + case ExpressionType::OPERATOR_UNPACK: + case ExpressionType::OPERATOR_TRY: + case ExpressionType::OPERATOR_NOT: + case ExpressionType::OPERATOR_IS_NULL: + case ExpressionType::OPERATOR_IS_NOT_NULL: + return child_count == 1; + case ExpressionType::GROUPING_FUNCTION: + return true; + case ExpressionType::OPERATOR_COALESCE: + return child_count >= 1; + case ExpressionType::ARRAY_EXTRACT: + case ExpressionType::STRUCT_EXTRACT: + return child_count == 2; + case ExpressionType::ARRAY_SLICE: + return child_count == 3 || child_count == 4; + case ExpressionType::ARRAY_CONSTRUCTOR: + return true; + default: + return IsComparisonType(type) && child_count == 2; + } +} + +void ParsedExpression::VerifyDeserialized() { + auto invalid = [&](const string &reason) { + return SerializationException("Failed to deserialize %s expression: %s", + EnumUtil::ToString(GetExpressionClass()), reason); + }; + for (auto &child : ChildrenMutable()) { + if (!child) { + throw invalid("missing child expression"); + } + } + switch (GetExpressionClass()) { + case ExpressionClass::COLUMN_REF: + if (Cast().ColumnNames().empty()) { + throw invalid("no column names"); + } + break; + case ExpressionClass::COMPARISON: + if (!IsComparisonType(GetExpressionType())) { + throw invalid("invalid comparison type " + EnumUtil::ToString(GetExpressionType())); + } + break; + case ExpressionClass::CONJUNCTION: + if (GetExpressionType() != ExpressionType::CONJUNCTION_AND && + GetExpressionType() != ExpressionType::CONJUNCTION_OR) { + throw invalid("invalid conjunction type " + EnumUtil::ToString(GetExpressionType())); + } + if (Cast().GetChildren().empty()) { + throw invalid("no children"); + } + break; + case ExpressionClass::OPERATOR: + if (!IsValidOperator(GetExpressionType(), Cast().GetChildren().size())) { + throw invalid("invalid operator type " + EnumUtil::ToString(GetExpressionType()) + " for " + + to_string(Cast().GetChildren().size()) + " children"); + } + break; + case ExpressionClass::CASE: + if (Cast().CaseChecks().empty()) { + throw invalid("no WHEN clauses"); + } + break; + case ExpressionClass::FUNCTION: + if (!Cast().OrderBy()) { + throw invalid("missing ORDER BY modifier"); + } + break; + case ExpressionClass::SUBQUERY: { + auto &subquery = Cast(); + if (!subquery.Subquery() || !subquery.Subquery()->node) { + throw invalid("missing subquery"); + } + if ((subquery.GetSubqueryType() == SubqueryType::ANY) != (subquery.GetChild() != nullptr)) { + throw invalid("a child is required for, and only for, ANY subqueries"); + } + if (subquery.GetSubqueryType() == SubqueryType::INVALID) { + throw invalid("invalid subquery type"); + } + break; + } + case ExpressionClass::LAMBDA_REF: + throw invalid("lambda references cannot be deserialized"); + default: + break; + } +} + } // namespace duckdb diff --git a/src/duckdb/src/parser/parsed_expression_iterator.cpp b/src/duckdb/src/parser/parsed_expression_iterator.cpp index f8f1c2da1..0a7195b10 100644 --- a/src/duckdb/src/parser/parsed_expression_iterator.cpp +++ b/src/duckdb/src/parser/parsed_expression_iterator.cpp @@ -136,9 +136,15 @@ void ParsedExpressionIterator::EnumerateTableRefChildren( } break; } + case TableReferenceType::SHOW_REF: { + auto &show_ref = ref.Cast(); + if (show_ref.query) { + EnumerateQueryNodeChildren(*show_ref.query, expr_callback, ref_callback); + } + break; + } case TableReferenceType::BASE_TABLE: case TableReferenceType::EMPTY_FROM: - case TableReferenceType::SHOW_REF: case TableReferenceType::COLUMN_DATA: case TableReferenceType::DELIM_GET: // these TableRefs do not need to be unfolded @@ -179,7 +185,9 @@ void ParsedExpressionIterator::EnumerateQueryNodeChildren( expr_callback(sel_node.qualify); } - EnumerateTableRefChildren(*sel_node.from_table.get(), expr_callback, ref_callback); + if (sel_node.from_table) { + EnumerateTableRefChildren(*sel_node.from_table, expr_callback, ref_callback); + } break; } case QueryNodeType::SET_OPERATION_NODE: { diff --git a/src/duckdb/src/parser/parser.cpp b/src/duckdb/src/parser/parser.cpp index 26b798a51..9ed616981 100644 --- a/src/duckdb/src/parser/parser.cpp +++ b/src/duckdb/src/parser/parser.cpp @@ -25,13 +25,24 @@ Parser::Parser(const ParserOptions &options_p) : options(options_p) { } Parser::~Parser() = default; +Parser::Parser(Parser &&other) noexcept = default; + +ParserOptions ParserOptions::Builtin() { + ParserOptions options; + options.compiled_grammar = CompiledGrammar::DefaultGrammar(); + return options; +} + +Parser Parser::GetBuiltinParser() { + return Parser(ParserOptions::Builtin()); +} CompiledGrammar &Parser::GetGrammar() { if (!compiled_grammar) { if (options.compiled_grammar) { compiled_grammar = options.compiled_grammar; } else { - compiled_grammar = CompiledGrammar::Create(); + throw InternalException("ParserOptions requires a compiled grammar"); } } return *compiled_grammar; @@ -369,6 +380,9 @@ vector Parser::Tokenize(const string &query) { vector result; result.reserve(tokens.size()); for (auto &token : tokens) { + if (token.type == TokenType::END_OF_INPUT || token.type == TokenType::END_OF_INPUT_AUTOCOMPLETE) { + continue; + } SimplifiedToken simplified; simplified.start = token.offset; switch (token.type) { @@ -540,21 +554,7 @@ vector Parser::TokenizeError(const string &error_msg) { } KeywordCategory Parser::ToKeywordCategory(const string &text) { - auto &helper = DuckDBKeywordHelper::Instance(); - - if (helper.KeywordCategoryType(text, PEGKeywordCategory::KEYWORD_RESERVED)) { - return KeywordCategory::KEYWORD_RESERVED; - } - if (helper.KeywordCategoryType(text, PEGKeywordCategory::KEYWORD_UNRESERVED)) { - return KeywordCategory::KEYWORD_UNRESERVED; - } - if (helper.KeywordCategoryType(text, PEGKeywordCategory::KEYWORD_TYPE_FUNC)) { - return KeywordCategory::KEYWORD_TYPE_FUNC; - } - if (helper.KeywordCategoryType(text, PEGKeywordCategory::KEYWORD_COL_NAME)) { - return KeywordCategory::KEYWORD_COL_NAME; - } - return KeywordCategory::KEYWORD_NONE; + return DuckDBKeywordHelper::Instance().GetKeywordCategory(text); } KeywordCategory Parser::IsKeyword(const string &text) { @@ -566,8 +566,16 @@ vector Parser::KeywordList() { return keyword_helper.KeywordList(); } -vector> Parser::ParseExpressionList(const string &select_list, - const ParserOptions &options) { +unique_ptr Parser::ParseSelectNode(const string &query) { + Parser parser(options); + parser.ParseQuery(query); + if (parser.statements.size() != 1 || parser.statements[0]->type != StatementType::SELECT_STATEMENT) { + throw InternalException("Expected a single select statement"); + } + return std::move(parser.statements[0]->Cast().node); +} + +vector> Parser::ParseExpressionList(const string &select_list) { // construct a mock query prefixed with SELECT string mock_query = "SELECT " + select_list; // parse the query @@ -603,7 +611,15 @@ vector> Parser::ParseExpressionList(const string &s return std::move(select_node.select_list); } -GroupByNode Parser::ParseGroupByList(const string &group_by, const ParserOptions &options) { +unique_ptr Parser::ParseSingleExpression(const string &expression) { + auto expressions = ParseExpressionList(expression); + if (expressions.size() != 1) { + throw InternalException("Expected a single expression"); + } + return std::move(expressions[0]); +} + +GroupByNode Parser::ParseGroupByList(const string &group_by) { // construct a mock SELECT query with our group_by expressions string mock_query = StringUtil::Format("SELECT 42 GROUP BY %s", group_by); // parse the query @@ -619,7 +635,7 @@ GroupByNode Parser::ParseGroupByList(const string &group_by, const ParserOptions return std::move(select_node.groups); } -vector Parser::ParseOrderList(const string &select_list, const ParserOptions &options) { +vector Parser::ParseOrderList(const string &select_list) { // construct a mock query string mock_query = "SELECT * FROM tbl ORDER BY " + select_list; // parse the query @@ -641,7 +657,7 @@ vector Parser::ParseOrderList(const string &select_list, const Pars } void Parser::ParseUpdateList(const string &update_list, vector &update_columns, - vector> &expressions, const ParserOptions &options) { + vector> &expressions) { // construct a mock query string mock_query = "UPDATE tbl SET " + update_list; // parse the query @@ -656,8 +672,7 @@ void Parser::ParseUpdateList(const string &update_list, vector &upda expressions = std::move(update.node->set_info->expressions); } -vector>> Parser::ParseValuesList(const string &value_list, - const ParserOptions &options) { +vector>> Parser::ParseValuesList(const string &value_list) { // construct a mock query string mock_query = "VALUES " + value_list; // parse the query @@ -679,7 +694,7 @@ vector>> Parser::ParseValuesList(const strin return std::move(values_list.values); } -ColumnList Parser::ParseColumnList(const string &column_list, const ParserOptions &options) { +ColumnList Parser::ParseColumnList(const string &column_list) { string mock_query = "CREATE TABLE tbl (" + column_list + ")"; Parser parser(options); parser.ParseQuery(mock_query); @@ -694,8 +709,8 @@ ColumnList Parser::ParseColumnList(const string &column_list, const ParserOption return std::move(info.columns); } -ColumnDefinition Parser::ParseColumnDefinition(const string &column_definition, const ParserOptions &options) { - auto column_list = ParseColumnList(column_definition, options); +ColumnDefinition Parser::ParseColumnDefinition(const string &column_definition) { + auto column_list = ParseColumnList(column_definition); return column_list.GetColumn(LogicalIndex(0)).Copy(); } diff --git a/src/duckdb/src/parser/peg/autocomplete_core.cpp b/src/duckdb/src/parser/peg/autocomplete_core.cpp index e0c16bafe..a637e7ba1 100644 --- a/src/duckdb/src/parser/peg/autocomplete_core.cpp +++ b/src/duckdb/src/parser/peg/autocomplete_core.cpp @@ -215,8 +215,7 @@ vector GenerateAutoCompleteSuggestions(AutoCompleteCatal TokenIterator token_iterator(tokens); ArenaAllocator process_allocator(Allocator::DefaultAllocator()); MatchContext match_context(suggestions, parse_allocator, process_allocator, max_token_index, - MatchMode::RECOGNIZE_ONLY, IdentifierCaseMode::PRESERVE_CASE, - parameters.use_heap_based_parser); + MatchMode::RECOGNIZE_ONLY, IdentifierCaseMode::PRESERVE_CASE); MatchState state(token_iterator, match_context); compiled_grammar->ProgramMatcher().MatchParseResult(state); } @@ -230,6 +229,7 @@ vector GenerateAutoCompleteSuggestions(AutoCompleteCatal vector new_suggestions; switch (suggestion.type) { case SuggestionState::SUGGEST_VARIABLE: + case SuggestionState::SUGGEST_BARE_LABEL: // variables have no suggestions available break; case SuggestionState::SUGGEST_KEYWORD: diff --git a/src/duckdb/src/parser/peg/compiled_grammar.cpp b/src/duckdb/src/parser/peg/compiled_grammar.cpp index faf56e8c4..3cf3f2878 100644 --- a/src/duckdb/src/parser/peg/compiled_grammar.cpp +++ b/src/duckdb/src/parser/peg/compiled_grammar.cpp @@ -2,25 +2,16 @@ #include "duckdb/parser/peg/matcher_factory.hpp" #include "duckdb/parser/peg/keyword_helper/parsed_grammar_keyword_helper.hpp" #include "duckdb/common/string_util.hpp" -#include "duckdb/main/database.hpp" -#include "duckdb/main/client_config.hpp" -#include "duckdb/main/client_context.hpp" -#include "duckdb/main/extension_callback_manager.hpp" #include "duckdb/parser/grammar_extension.hpp" namespace duckdb { -CompiledGrammar::CompiledGrammar(const ParsedGrammar &grammar, bool has_grammar_changes_p) - : owned_keyword_helper(make_uniq(grammar)), keyword_helper(*owned_keyword_helper), - tokenizer(keyword_helper), has_grammar_changes(has_grammar_changes_p) { -} - -shared_ptr CompiledGrammar::Get(ClientContext &context) { - auto &client_config = ClientConfig::GetConfig(context); - if (client_config.cached_grammar) { - return client_config.cached_grammar; - } - return DatabaseInstance::GetDatabase(context).GetParserCache().GetMatcher(); +CompiledGrammar::CompiledGrammar(MatcherAllocator &&allocator_p, unique_ptr &&keyword_helper_p, + unique_ptr &&tokenizer_p, compiled_rules_map_t &&rules_p, + const Matcher &program_matcher, const Matcher &top_level_statement_matcher) + : allocator(std::move(allocator_p)), keyword_helper(std::move(keyword_helper_p)), tokenizer(std::move(tokenizer_p)), + rules(std::move(rules_p)), program_matcher(program_matcher), + top_level_statement_matcher(top_level_statement_matcher) { } static void ValidateParsedGrammarRoots(const ParsedGrammar &grammar) { @@ -64,6 +55,8 @@ terminal_rule_overrides_t ParsedGrammar::BuildTerminalRuleOverrides(const PEGKey make_uniq(SuggestionState::SUGGEST_VARIABLE, keyword_helper)); AddTerminalRuleOverride(overrides, "ReservedIdentifier", make_uniq(SuggestionState::SUGGEST_VARIABLE, keyword_helper)); + AddTerminalRuleOverride(overrides, "BareColLabel", + make_uniq(SuggestionState::SUGGEST_BARE_LABEL, keyword_helper)); AddTerminalRuleOverride(overrides, "CatalogName", make_uniq(SuggestionState::SUGGEST_CATALOG_NAME, keyword_helper)); AddTerminalRuleOverride(overrides, "SchemaName", @@ -107,6 +100,8 @@ terminal_rule_overrides_t ParsedGrammar::BuildTerminalRuleOverrides(const PEGKey AddTerminalRuleOverride(overrides, "NumberLiteral", make_uniq()); AddTerminalRuleOverride(overrides, "StringLiteral", make_uniq()); AddTerminalRuleOverride(overrides, "OperatorLiteral", make_uniq()); + AddTerminalRuleOverride(overrides, "AnyOperatorLiteral", + make_uniq(OperatorMatcherMode::ALL_OPERATORS)); //===--------------------------------------------------------------------===// // END GENERATED RULE OVERRIDES //===--------------------------------------------------------------------===// @@ -118,10 +113,9 @@ terminal_rule_overrides_t ParsedGrammar::BuildTerminalRuleOverrides(const PEGKey return overrides; } -shared_ptr -CompiledGrammar::Create(const case_insensitive_map_t> &grammar_extensions) { +shared_ptr CompiledGrammar::Create(const vector> &grammar_extensions) { auto grammar = ParsedGrammar::CreateDefault(); - for (auto &[_, extension] : grammar_extensions) { + for (auto &extension : grammar_extensions) { auto changes = extension.get().GetChanges(); for (auto &change : changes) { change.Apply(grammar); @@ -134,49 +128,36 @@ CompiledGrammar::Create(const case_insensitive_map_t CheckReference(grammar, parsed_rule, expression); } - auto new_matcher = shared_ptr(new CompiledGrammar(grammar, !grammar_extensions.empty())); + auto keyword_helper = make_uniq(grammar); + auto tokenizer = make_uniq(*keyword_helper); + compiled_rules_map_t rules; for (auto &entry : grammar.rules) { auto &rule = *entry.second; - new_matcher->rules.emplace(rule.name, make_uniq(rule.name, rule.transform_process)); + rules.emplace(rule.name, make_uniq(rule.name, rule.transform_process, rule.collapsible, + rule.expression_depth, rule.suppress_suggestions)); } - auto terminal_rule_overrides = grammar.BuildTerminalRuleOverrides(new_matcher->GetKeywordHelper()); - MatcherFactory factory(new_matcher->allocator, grammar, *new_matcher, std::move(terminal_rule_overrides)); - new_matcher->program_matcher = factory.CreateRootMatcher("Program"); - new_matcher->top_level_statement_matcher = factory.GetMatcher("TopLevelStatement"); - return new_matcher; -} -shared_ptr CompiledGrammar::Create() { - return Create({}); -} + MatcherAllocator allocator; + auto terminal_rule_overrides = grammar.BuildTerminalRuleOverrides(*keyword_helper); + MatcherFactory factory(allocator, grammar, rules, *keyword_helper, std::move(terminal_rule_overrides)); -shared_ptr CompiledGrammar::Create(const ClientContext &context, - const case_insensitive_set_t &active_extensions) { - case_insensitive_map_t> selected_extensions; - auto &callback_manager = ExtensionCallbackManager::Get(context); - for (auto &name : active_extensions) { - auto grammar_extension = callback_manager.FindGrammarExtension(name); - if (grammar_extension) { - selected_extensions.emplace(name, *grammar_extension); - } - } - return Create(selected_extensions); + auto &program_matcher = factory.CreateRootMatcher("Program"); + auto &top_level_statement_matcher = factory.GetMatcher("TopLevelStatement"); + ComputeFirstSets(program_matcher, keyword_helper->GetLiteralTable()); + + auto new_matcher = shared_ptr(new CompiledGrammar(std::move(allocator), std::move(keyword_helper), + std::move(tokenizer), std::move(rules), + program_matcher, top_level_statement_matcher)); + return new_matcher; } -shared_ptr ParserCache::GetMatcher() { - { - std::unique_lock lock(mutex); - if (matcher) { - return matcher; - } - } - auto new_matcher = CompiledGrammar::Create(); +shared_ptr CompiledGrammar::DefaultGrammar() { + static auto grammar = Create(); + return grammar; +} - std::unique_lock lock(mutex); - if (!matcher) { - matcher = std::move(new_matcher); - } - return matcher; +shared_ptr CompiledGrammar::Create() { + return Create({}); } optional_ptr CompiledGrammar::GetRule(const string &rule_name) const { diff --git a/src/duckdb/src/parser/peg/dialect_extension.cpp b/src/duckdb/src/parser/peg/dialect_extension.cpp new file mode 100644 index 000000000..76756d34a --- /dev/null +++ b/src/duckdb/src/parser/peg/dialect_extension.cpp @@ -0,0 +1,84 @@ +#include "duckdb/parser/peg/dialect_extension.hpp" +#include "duckdb/parser/peg/keyword_helper/parsed_grammar_keyword_helper.hpp" +#include "duckdb/parser/peg/compiled_grammar.hpp" +#include "duckdb/parser/peg/tokenizer/tokenizer.hpp" +#include "duckdb/parser/peg/matcher_factory.hpp" + +namespace duckdb { + +const string &DialectExtension::Name() const { + return name; +} + +const optional &DialectExtension::GetCompatibilityMode() const { + return compatibility_mode; +} + +shared_ptr DialectExtension::GetCompiledGrammar(const ClientContext &context) { + { + lock_guard guard(lock); + if (cache) { + return cache; + } + } + + //! Grammar changes + auto parsed_grammar = ParsedGrammar::CreateDefault(); + GrammarChangesInput grammar_change_input {parsed_grammar, context}; + ApplyGrammarChanges(grammar_change_input); + + //! Keyword Helper + CreateKeywordHelperInput keyword_helper_input {parsed_grammar}; + auto keyword_helper = CreateKeywordHelper(keyword_helper_input); + + //! Tokenizer + CreateTokenizerInput tokenizer_input {*keyword_helper}; + auto tokenizer = CreateTokenizer(tokenizer_input); + + //! MatcherFactory + MatcherAllocator allocator; + compiled_rules_map_t rules; + for (auto &entry : parsed_grammar.rules) { + auto &rule = *entry.second; + rules.emplace(rule.name, make_uniq(rule.name, rule.transform_process, rule.collapsible, + rule.expression_depth, rule.suppress_suggestions)); + } + auto terminal_rule_overrides = parsed_grammar.BuildTerminalRuleOverrides(*keyword_helper); + CreateMatcherFactoryInput matcher_factory_input {allocator, parsed_grammar, rules, *keyword_helper, + std::move(terminal_rule_overrides)}; + auto matcher_factory = CreateMatcherFactory(matcher_factory_input); + + auto &program_matcher = matcher_factory->CreateRootMatcher("Program"); + auto &top_level_statement_matcher = matcher_factory->GetMatcher("TopLevelStatement"); + + auto result = shared_ptr(new CompiledGrammar(std::move(allocator), std::move(keyword_helper), + std::move(tokenizer), std::move(rules), + program_matcher, top_level_statement_matcher)); + + lock_guard guard(lock); + if (!cache) { + cache = result; + } + return cache; +} + +void DialectExtension::ApplyGrammarChanges(GrammarChangesInput &) { + return; +} + +unique_ptr DialectExtension::CreateMatcherFactory(CreateMatcherFactoryInput &input) { + return make_uniq(input.allocator, input.parsed_grammar, input.rules, input.keyword_helper, + std::move(input.terminal_rules)); +} + +unique_ptr DialectExtension::CreateTokenizer(CreateTokenizerInput &input) { + auto &keyword_helper = input.keyword_helper; + return make_uniq(keyword_helper); +} + +unique_ptr DialectExtension::CreateKeywordHelper(CreateKeywordHelperInput &input) { + auto &parsed_grammar = input.grammar; + return make_uniq(parsed_grammar); +} + +} // namespace duckdb diff --git a/src/duckdb/src/parser/peg/grammar_change.cpp b/src/duckdb/src/parser/peg/grammar_change.cpp index 0c383ac08..40b2e4361 100644 --- a/src/duckdb/src/parser/peg/grammar_change.cpp +++ b/src/duckdb/src/parser/peg/grammar_change.cpp @@ -5,15 +5,18 @@ namespace duckdb { GrammarChange GrammarChange::Create(GrammarChangeType type, string rule_name, string definition, grammar_transform_process_function_t transform_process, grammar_cursor_function_t find_cursor, - terminal_rule_matcher_factory_t matcher_factory) { + terminal_rule_matcher_factory_t matcher_factory, bool collapsible, + bool suppress_suggestions) { return GrammarChange(type, std::move(rule_name), std::move(definition), std::move(transform_process), - std::move(find_cursor), std::move(matcher_factory)); + std::move(find_cursor), std::move(matcher_factory), collapsible, suppress_suggestions); } GrammarChange GrammarChange::AddRule(const string &rule_definition, - grammar_transform_process_function_t transform_process) { + grammar_transform_process_function_t transform_process, + bool suppress_suggestions) { auto rule = ParsedGrammar::ParseSingleRule(rule_definition); - return Create(GrammarChangeType::ADD_RULE, std::move(rule.name), rule_definition, std::move(transform_process)); + return Create(GrammarChangeType::ADD_RULE, std::move(rule.name), rule_definition, std::move(transform_process), + nullptr, nullptr, false, suppress_suggestions); } GrammarChange GrammarChange::AddChoice(const string &rule_name, const string &choice, @@ -42,8 +45,10 @@ GrammarChange GrammarChange::ReplaceRule(const string &rule_definition, } GrammarChange GrammarChange::SetTransformProcess(const string &rule_name, - grammar_transform_process_function_t transform_process) { - return Create(GrammarChangeType::SET_TRANSFORM, rule_name, string(), std::move(transform_process)); + grammar_transform_process_function_t transform_process, + bool collapsible, bool suppress_suggestions) { + return Create(GrammarChangeType::SET_TRANSFORM, rule_name, string(), std::move(transform_process), nullptr, nullptr, + collapsible, suppress_suggestions); } GrammarChange GrammarChange::AddTerminalRuleOverride(const string &rule_name, @@ -55,7 +60,7 @@ GrammarChange GrammarChange::AddTerminalRuleOverride(const string &rule_name, void GrammarChange::Apply(ParsedGrammar &grammar) const { switch (type) { case GrammarChangeType::ADD_RULE: - grammar.AddRule(definition, transform_process); + grammar.AddRule(definition, transform_process, suppress_suggestions); break; case GrammarChangeType::ADD_CHOICE: grammar.AddChoice(rule_name, definition, find_cursor); @@ -73,7 +78,7 @@ void GrammarChange::Apply(ParsedGrammar &grammar) const { grammar.ReplaceRule(definition, transform_process); break; case GrammarChangeType::SET_TRANSFORM: - grammar.SetTransformProcess(rule_name, transform_process); + grammar.SetTransformProcess(rule_name, transform_process, collapsible, suppress_suggestions); break; case GrammarChangeType::ADD_TERMINAL_RULE_OVERRIDE: grammar.AddTerminalRuleOverride(rule_name, matcher_factory); diff --git a/src/duckdb/src/parser/peg/keyword_helper/default_keyword_maps.cpp b/src/duckdb/src/parser/peg/keyword_helper/default_keyword_maps.cpp index 20ec6622a..3517431b4 100644 --- a/src/duckdb/src/parser/peg/keyword_helper/default_keyword_maps.cpp +++ b/src/duckdb/src/parser/peg/keyword_helper/default_keyword_maps.cpp @@ -1,43 +1,108 @@ #include "duckdb/parser/peg/keyword_helper/default_keyword_maps.hpp" +#include "duckdb/parser/peg/matcher.hpp" namespace duckdb { -bool DefaultKeywordMaps::IsKeywordOfCategory(const string &text, PEGKeywordCategory category) const { - switch (category) { - case PEGKeywordCategory::KEYWORD_RESERVED: - return reserved_keyword_map.count(text) != 0; - case PEGKeywordCategory::KEYWORD_UNRESERVED: - return unreserved_keyword_map.count(text) != 0; - case PEGKeywordCategory::KEYWORD_TYPE_FUNC: - return typefunc_keyword_map.count(text) != 0; - case PEGKeywordCategory::KEYWORD_COL_NAME: - return colname_keyword_map.count(text) != 0; - case PEGKeywordCategory::KEYWORD_TYPE_NAME: - return typename_keyword_map.count(text) != 0; +static constexpr keyword_categories_t KEYWORD_UNRESERVED = keyword_categories_t::CreateCategory(0); +static constexpr keyword_categories_t KEYWORD_RESERVED = keyword_categories_t::CreateCategory(1); +static constexpr keyword_categories_t KEYWORD_TYPE_FUNC = keyword_categories_t::CreateCategory(2); +static constexpr keyword_categories_t KEYWORD_COL_NAME = keyword_categories_t::CreateCategory(3); +static constexpr keyword_categories_t KEYWORD_TYPE_NAME = keyword_categories_t::CreateCategory(4); +static constexpr keyword_categories_t KEYWORD_BARE_LABEL = keyword_categories_t::CreateCategory(5); + +LiteralInfo DefaultKeywordMaps::LookupKeyword(const string &text, uint16_t literal_id) const { + keyword_categories_t flags; + if (unreserved_keyword_map.count(text)) { + flags |= KEYWORD_UNRESERVED; + } + if (reserved_keyword_map.count(text)) { + flags |= KEYWORD_RESERVED; + } + if (typefunc_keyword_map.count(text)) { + flags |= KEYWORD_TYPE_FUNC; + } + if (colname_keyword_map.count(text)) { + flags |= KEYWORD_COL_NAME; + } + if (typename_keyword_map.count(text)) { + flags |= KEYWORD_TYPE_NAME; + } + if (bare_label_keyword_map.count(text)) { + flags |= KEYWORD_BARE_LABEL; + } + return LiteralInfo(literal_id, flags); +} + +keyword_categories_t DefaultKeywordMaps::GetIdentifierMask(SuggestionState type) { + switch (type) { + case SuggestionState::SUGGEST_BARE_LABEL: + return KEYWORD_BARE_LABEL; + case SuggestionState::SUGGEST_TYPE_NAME: + return KEYWORD_UNRESERVED | KEYWORD_TYPE_NAME; + case SuggestionState::SUGGEST_SCALAR_FUNCTION_NAME: + case SuggestionState::SUGGEST_TABLE_FUNCTION_NAME: + return KEYWORD_UNRESERVED | KEYWORD_TYPE_FUNC; default: - return false; + return KEYWORD_UNRESERVED | KEYWORD_COL_NAME; } } -bool DefaultKeywordMaps::IsKeyword(const string &text) const { - return reserved_keyword_map.count(text) != 0 || unreserved_keyword_map.count(text) != 0 || - colname_keyword_map.count(text) != 0 || typefunc_keyword_map.count(text) != 0 || - typename_keyword_map.count(text) != 0; +KeywordCategory DefaultKeywordMaps::GetKeywordCategory(LiteralInfo info) { + auto categories = GetKeywordCategories(info); + if (categories.empty()) { + return KeywordCategory::KEYWORD_NONE; + } + return categories[0]; +} + +vector DefaultKeywordMaps::GetKeywordCategories(LiteralInfo info) { + vector result; + if (info.HasAnyFlags(KEYWORD_RESERVED)) { + result.push_back(KeywordCategory::KEYWORD_RESERVED); + } + if (info.HasAnyFlags(KEYWORD_UNRESERVED)) { + result.push_back(KeywordCategory::KEYWORD_UNRESERVED); + } + if (info.HasAnyFlags(KEYWORD_TYPE_FUNC)) { + result.push_back(KeywordCategory::KEYWORD_TYPE_FUNC); + } + if (info.HasAnyFlags(KEYWORD_COL_NAME)) { + result.push_back(KeywordCategory::KEYWORD_COL_NAME); + } + if (info.HasAnyFlags(KEYWORD_TYPE_NAME)) { + result.push_back(KeywordCategory::KEYWORD_TYPE_NAME); + } + return result; +} + +case_insensitive_map_t DefaultKeywordMaps::ToLiteralMap() const { + case_insensitive_map_t result; + auto add_keywords = [&](const case_insensitive_set_t &words) { + for (auto &word : words) { + result.emplace(word, LookupKeyword(word)); + } + }; + add_keywords(reserved_keyword_map); + add_keywords(unreserved_keyword_map); + add_keywords(colname_keyword_map); + add_keywords(typefunc_keyword_map); + add_keywords(typename_keyword_map); + return result; } vector DefaultKeywordMaps::ToList() const { vector result; for (auto &kw : reserved_keyword_map) { - result.push_back({kw, KeywordCategory::KEYWORD_RESERVED}); + result.push_back({kw, "reserved"}); } for (auto &kw : unreserved_keyword_map) { - result.push_back({kw, KeywordCategory::KEYWORD_UNRESERVED}); + result.push_back({kw, "unreserved"}); } for (auto &kw : typefunc_keyword_map) { - result.push_back({kw, KeywordCategory::KEYWORD_TYPE_FUNC}); + result.push_back({kw, "type_function"}); } for (auto &kw : colname_keyword_map) { - result.push_back({kw, KeywordCategory::KEYWORD_COL_NAME}); + result.push_back({kw, "column_name"}); } return result; } diff --git a/src/duckdb/src/parser/peg/keyword_helper/duckdb_keyword_helper.cpp b/src/duckdb/src/parser/peg/keyword_helper/duckdb_keyword_helper.cpp index 6421801fb..45f57c8cb 100644 --- a/src/duckdb/src/parser/peg/keyword_helper/duckdb_keyword_helper.cpp +++ b/src/duckdb/src/parser/peg/keyword_helper/duckdb_keyword_helper.cpp @@ -1,9 +1,11 @@ #include "duckdb/parser/peg/keyword_helper/duckdb_keyword_helper.hpp" +#include "duckdb/parser/peg/parsed_grammar.hpp" namespace duckdb { -DuckDBKeywordHelper::DuckDBKeywordHelper() : initialized(false) { - InitializeKeywordMaps(); +DuckDBKeywordHelper::DuckDBKeywordHelper() + : keyword_maps(InitializeKeywordMaps()), + literal_table(ParsedGrammar::CreateDefault(), keyword_maps.ToLiteralMap()) { } const DuckDBKeywordHelper &DuckDBKeywordHelper::Instance() { @@ -11,13 +13,13 @@ const DuckDBKeywordHelper &DuckDBKeywordHelper::Instance() { return instance; } -bool DuckDBKeywordHelper::KeywordCategoryType(const std::string &text, const PEGKeywordCategory category) const { - return keyword_maps.IsKeywordOfCategory(text, category); +keyword_categories_t DuckDBKeywordHelper::GetIdentifierMask(SuggestionState type) const { + return DefaultKeywordMaps::GetIdentifierMask(type); } -bool DuckDBKeywordHelper::IsKeyword(const string &text) const { - return keyword_maps.IsKeyword(text); -}; +KeywordCategory DuckDBKeywordHelper::GetKeywordCategory(const string &text) const { + return DefaultKeywordMaps::GetKeywordCategory(LookupKeyword(text)); +} vector DuckDBKeywordHelper::KeywordList() const { return keyword_maps.ToList(); diff --git a/src/duckdb/src/parser/peg/keyword_helper/grammar_literal_table.cpp b/src/duckdb/src/parser/peg/keyword_helper/grammar_literal_table.cpp new file mode 100644 index 000000000..dfec0c26a --- /dev/null +++ b/src/duckdb/src/parser/peg/keyword_helper/grammar_literal_table.cpp @@ -0,0 +1,50 @@ +#include "duckdb/parser/peg/grammar_literal_table.hpp" + +#include "duckdb/common/atomic.hpp" +#include "duckdb/common/exception.hpp" +#include "duckdb/parser/peg/parsed_grammar.hpp" + +namespace duckdb { + +static uint64_t NextLiteralTableId() { + static atomic next_id {1}; + auto result = next_id.fetch_add(1, std::memory_order_relaxed); + D_ASSERT(result != 0); + return result; +} + +GrammarLiteralTable::GrammarLiteralTable(const ParsedGrammar &grammar, + const case_insensitive_map_t &keywords) + : cache_id(NextLiteralTableId()) { + vector> pending; + for (auto &entry : grammar.rules) { + pending.push_back(entry.second->recipe.expression); + } + while (!pending.empty()) { + auto &expression = pending.back().get(); + pending.pop_back(); + if (expression.type == PEGExpression::Type::LITERAL) { + Register(expression.text.GetString()); + } + for (auto &child : expression.children) { + pending.push_back(child); + } + } + for (auto &entry : keywords) { + Register(entry.first, entry.second.CategoryFlags()); + } +} + +void GrammarLiteralTable::Register(const string &text, keyword_categories_t categories) { + auto entry = literals.find(text); + if (entry == literals.end()) { + if (literals.size() >= LiteralInfo::MAX_LITERAL_ID) { + throw InvalidInputException("Grammar has too many distinct literals"); + } + auto id = static_cast(literals.size() + 1); + entry = literals.emplace(text, LiteralInfo(id)).first; + } + entry->second.AddCategories(categories); +} + +} // namespace duckdb diff --git a/src/duckdb/src/parser/peg/keyword_helper/parsed_grammar_keyword_helper.cpp b/src/duckdb/src/parser/peg/keyword_helper/parsed_grammar_keyword_helper.cpp index 5d75b5bab..f1c75cd79 100644 --- a/src/duckdb/src/parser/peg/keyword_helper/parsed_grammar_keyword_helper.cpp +++ b/src/duckdb/src/parser/peg/keyword_helper/parsed_grammar_keyword_helper.cpp @@ -46,26 +46,44 @@ static void PopulateKeywordMap(const ParsedGrammar &grammar, const string &root_ active_rules.erase(rule_name); } -ParsedGrammarKeywordHelper::ParsedGrammarKeywordHelper(const ParsedGrammar &grammar) { - unordered_map> mapping { - {"ReservedKeyword", keyword_maps.reserved_keyword_map}, - {"UnreservedKeyword", keyword_maps.unreserved_keyword_map}, - {"ColumnNameKeyword", keyword_maps.colname_keyword_map}, - {"TypeFuncKeyword", keyword_maps.typefunc_keyword_map}, - {"TypeNameKeyword", keyword_maps.typename_keyword_map}, +static DefaultKeywordMaps BuildKeywordMaps(const ParsedGrammar &grammar) { + DefaultKeywordMaps keyword_maps; + struct KeywordRuleMapping { + const char *rule_name; + reference keyword_map; + bool optional; + }; + // grammars without a bare-label rule only accept non-keywords as bare labels + const KeywordRuleMapping mapping[] = { + {"ReservedKeyword", keyword_maps.reserved_keyword_map, false}, + {"UnreservedKeyword", keyword_maps.unreserved_keyword_map, false}, + {"ColumnNameKeyword", keyword_maps.colname_keyword_map, false}, + {"TypeFuncKeyword", keyword_maps.typefunc_keyword_map, false}, + {"TypeNameKeyword", keyword_maps.typename_keyword_map, false}, + {"BareLabelKeyword", keyword_maps.bare_label_keyword_map, true}, }; for (auto &entry : mapping) { + if (entry.optional && !grammar.GetRule(entry.rule_name)) { + continue; + } case_insensitive_set_t active_rules; - PopulateKeywordMap(grammar, entry.first, entry.first, entry.second.get(), active_rules); + PopulateKeywordMap(grammar, entry.rule_name, entry.rule_name, entry.keyword_map.get(), active_rules); + } + // the bare-label classification is orthogonal: every entry must also be an unreserved or column-name keyword + for (auto &keyword : keyword_maps.bare_label_keyword_map) { + if (!keyword_maps.unreserved_keyword_map.count(keyword) && !keyword_maps.colname_keyword_map.count(keyword)) { + throw InvalidInputException("Bare label keyword '%s' is not an unreserved or column-name keyword", keyword); + } } + return keyword_maps; } -bool ParsedGrammarKeywordHelper::KeywordCategoryType(const string &text, PEGKeywordCategory category) const { - return keyword_maps.IsKeywordOfCategory(text, category); +ParsedGrammarKeywordHelper::ParsedGrammarKeywordHelper(const ParsedGrammar &grammar) + : keyword_maps(BuildKeywordMaps(grammar)), literal_table(grammar, keyword_maps.ToLiteralMap()) { } -bool ParsedGrammarKeywordHelper::IsKeyword(const string &text) const { - return keyword_maps.IsKeyword(text); +keyword_categories_t ParsedGrammarKeywordHelper::GetIdentifierMask(SuggestionState type) const { + return DefaultKeywordMaps::GetIdentifierMask(type); } vector ParsedGrammarKeywordHelper::KeywordList() const { diff --git a/src/duckdb/src/parser/peg/keyword_map.cpp b/src/duckdb/src/parser/peg/keyword_map.cpp index dcd747db9..6cb1df163 100644 --- a/src/duckdb/src/parser/peg/keyword_map.cpp +++ b/src/duckdb/src/parser/peg/keyword_map.cpp @@ -2,17 +2,15 @@ #include "duckdb/parser/peg/keyword_helper/duckdb_keyword_helper.hpp" namespace duckdb { -void DuckDBKeywordHelper::InitializeKeywordMaps() { // Renamed for clarity - if (initialized) { - return; - }; - initialized = true; +DefaultKeywordMaps DuckDBKeywordHelper::InitializeKeywordMaps() { + DefaultKeywordMaps keyword_maps; auto &reserved_keyword_map = keyword_maps.reserved_keyword_map; auto &unreserved_keyword_map = keyword_maps.unreserved_keyword_map; auto &colname_keyword_map = keyword_maps.colname_keyword_map; auto &typefunc_keyword_map = keyword_maps.typefunc_keyword_map; auto &typename_keyword_map = keyword_maps.typename_keyword_map; + auto &bare_label_keyword_map = keyword_maps.bare_label_keyword_map; // Populating reserved_keyword_map reserved_keyword_map.insert("all"); @@ -163,6 +161,7 @@ void DuckDBKeywordHelper::InitializeKeywordMaps() { // Renamed for clarity unreserved_keyword_map.insert("delimiter"); unreserved_keyword_map.insert("delimiters"); unreserved_keyword_map.insert("depends"); + unreserved_keyword_map.insert("destroy"); unreserved_keyword_map.insert("detach"); unreserved_keyword_map.insert("dictionary"); unreserved_keyword_map.insert("disable"); @@ -324,6 +323,7 @@ void DuckDBKeywordHelper::InitializeKeywordMaps() { // Renamed for clarity unreserved_keyword_map.insert("ref"); unreserved_keyword_map.insert("referencing"); unreserved_keyword_map.insert("refresh"); + unreserved_keyword_map.insert("register"); unreserved_keyword_map.insert("reindex"); unreserved_keyword_map.insert("relative"); unreserved_keyword_map.insert("release"); @@ -333,6 +333,7 @@ void DuckDBKeywordHelper::InitializeKeywordMaps() { // Renamed for clarity unreserved_keyword_map.insert("replica"); unreserved_keyword_map.insert("reset"); unreserved_keyword_map.insert("resource"); + unreserved_keyword_map.insert("resources"); unreserved_keyword_map.insert("respect"); unreserved_keyword_map.insert("restart"); unreserved_keyword_map.insert("restrict"); @@ -564,5 +565,366 @@ void DuckDBKeywordHelper::InitializeKeywordMaps() { // Renamed for clarity typename_keyword_map.insert("try_cast"); typename_keyword_map.insert("unpack"); typename_keyword_map.insert("verbose"); + + // Populating bare_label_keyword_map + bare_label_keyword_map.insert("abort"); + bare_label_keyword_map.insert("absolute"); + bare_label_keyword_map.insert("access"); + bare_label_keyword_map.insert("action"); + bare_label_keyword_map.insert("add"); + bare_label_keyword_map.insert("admin"); + bare_label_keyword_map.insert("after"); + bare_label_keyword_map.insert("aggregate"); + bare_label_keyword_map.insert("also"); + bare_label_keyword_map.insert("alter"); + bare_label_keyword_map.insert("always"); + bare_label_keyword_map.insert("approx"); + bare_label_keyword_map.insert("assertion"); + bare_label_keyword_map.insert("assignment"); + bare_label_keyword_map.insert("attach"); + bare_label_keyword_map.insert("attribute"); + bare_label_keyword_map.insert("backward"); + bare_label_keyword_map.insert("before"); + bare_label_keyword_map.insert("begin"); + bare_label_keyword_map.insert("between"); + bare_label_keyword_map.insert("bigint"); + bare_label_keyword_map.insert("bit"); + bare_label_keyword_map.insert("boolean"); + bare_label_keyword_map.insert("cache"); + bare_label_keyword_map.insert("call"); + bare_label_keyword_map.insert("called"); + bare_label_keyword_map.insert("cascade"); + bare_label_keyword_map.insert("cascaded"); + bare_label_keyword_map.insert("catalog"); + bare_label_keyword_map.insert("chain"); + bare_label_keyword_map.insert("char"); + bare_label_keyword_map.insert("character"); + bare_label_keyword_map.insert("characteristics"); + bare_label_keyword_map.insert("checkpoint"); + bare_label_keyword_map.insert("class"); + bare_label_keyword_map.insert("close"); + bare_label_keyword_map.insert("cluster"); + bare_label_keyword_map.insert("coalesce"); + bare_label_keyword_map.insert("columns"); + bare_label_keyword_map.insert("comment"); + bare_label_keyword_map.insert("comments"); + bare_label_keyword_map.insert("commit"); + bare_label_keyword_map.insert("committed"); + bare_label_keyword_map.insert("compression"); + bare_label_keyword_map.insert("configuration"); + bare_label_keyword_map.insert("conflict"); + bare_label_keyword_map.insert("connect"); + bare_label_keyword_map.insert("connection"); + bare_label_keyword_map.insert("constraints"); + bare_label_keyword_map.insert("content"); + bare_label_keyword_map.insert("continue"); + bare_label_keyword_map.insert("conversion"); + bare_label_keyword_map.insert("copy"); + bare_label_keyword_map.insert("cost"); + bare_label_keyword_map.insert("csv"); + bare_label_keyword_map.insert("cube"); + bare_label_keyword_map.insert("current"); + bare_label_keyword_map.insert("cursor"); + bare_label_keyword_map.insert("cycle"); + bare_label_keyword_map.insert("data"); + bare_label_keyword_map.insert("database"); + bare_label_keyword_map.insert("deallocate"); + bare_label_keyword_map.insert("dec"); + bare_label_keyword_map.insert("decimal"); + bare_label_keyword_map.insert("declare"); + bare_label_keyword_map.insert("defaults"); + bare_label_keyword_map.insert("deferred"); + bare_label_keyword_map.insert("definer"); + bare_label_keyword_map.insert("delete"); + bare_label_keyword_map.insert("delimiter"); + bare_label_keyword_map.insert("delimiters"); + bare_label_keyword_map.insert("depends"); + bare_label_keyword_map.insert("destroy"); + bare_label_keyword_map.insert("detach"); + bare_label_keyword_map.insert("dictionary"); + bare_label_keyword_map.insert("disable"); + bare_label_keyword_map.insert("discard"); + bare_label_keyword_map.insert("disconnect"); + bare_label_keyword_map.insert("distance"); + bare_label_keyword_map.insert("document"); + bare_label_keyword_map.insert("domain"); + bare_label_keyword_map.insert("double"); + bare_label_keyword_map.insert("drop"); + bare_label_keyword_map.insert("each"); + bare_label_keyword_map.insert("enable"); + bare_label_keyword_map.insert("encoding"); + bare_label_keyword_map.insert("encrypted"); + bare_label_keyword_map.insert("enum"); + bare_label_keyword_map.insert("error"); + bare_label_keyword_map.insert("event"); + bare_label_keyword_map.insert("exact"); + bare_label_keyword_map.insert("excluding"); + bare_label_keyword_map.insert("exclusive"); + bare_label_keyword_map.insert("execute"); + bare_label_keyword_map.insert("exists"); + bare_label_keyword_map.insert("explain"); + bare_label_keyword_map.insert("export"); + bare_label_keyword_map.insert("extension"); + bare_label_keyword_map.insert("extensions"); + bare_label_keyword_map.insert("external"); + bare_label_keyword_map.insert("extract"); + bare_label_keyword_map.insert("family"); + bare_label_keyword_map.insert("first"); + bare_label_keyword_map.insert("float"); + bare_label_keyword_map.insert("following"); + bare_label_keyword_map.insert("force"); + bare_label_keyword_map.insert("forward"); + bare_label_keyword_map.insert("function"); + bare_label_keyword_map.insert("functions"); + bare_label_keyword_map.insert("generated"); + bare_label_keyword_map.insert("global"); + bare_label_keyword_map.insert("grant"); + bare_label_keyword_map.insert("granted"); + bare_label_keyword_map.insert("grouping"); + bare_label_keyword_map.insert("grouping_id"); + bare_label_keyword_map.insert("groups"); + bare_label_keyword_map.insert("handler"); + bare_label_keyword_map.insert("header"); + bare_label_keyword_map.insert("hold"); + bare_label_keyword_map.insert("identity"); + bare_label_keyword_map.insert("if"); + bare_label_keyword_map.insert("immediate"); + bare_label_keyword_map.insert("immutable"); + bare_label_keyword_map.insert("implicit"); + bare_label_keyword_map.insert("import"); + bare_label_keyword_map.insert("include"); + bare_label_keyword_map.insert("including"); + bare_label_keyword_map.insert("increment"); + bare_label_keyword_map.insert("index"); + bare_label_keyword_map.insert("indexes"); + bare_label_keyword_map.insert("inherit"); + bare_label_keyword_map.insert("inherits"); + bare_label_keyword_map.insert("inline"); + bare_label_keyword_map.insert("inout"); + bare_label_keyword_map.insert("input"); + bare_label_keyword_map.insert("insensitive"); + bare_label_keyword_map.insert("insert"); + bare_label_keyword_map.insert("install"); + bare_label_keyword_map.insert("instead"); + bare_label_keyword_map.insert("int"); + bare_label_keyword_map.insert("integer"); + bare_label_keyword_map.insert("interval"); + bare_label_keyword_map.insert("invoker"); + bare_label_keyword_map.insert("isolation"); + bare_label_keyword_map.insert("json"); + bare_label_keyword_map.insert("key"); + bare_label_keyword_map.insert("label"); + bare_label_keyword_map.insert("language"); + bare_label_keyword_map.insert("large"); + bare_label_keyword_map.insert("last"); + bare_label_keyword_map.insert("leakproof"); + bare_label_keyword_map.insert("level"); + bare_label_keyword_map.insert("listen"); + bare_label_keyword_map.insert("load"); + bare_label_keyword_map.insert("local"); + bare_label_keyword_map.insert("location"); + bare_label_keyword_map.insert("lock"); + bare_label_keyword_map.insert("locked"); + bare_label_keyword_map.insert("logged"); + bare_label_keyword_map.insert("macro"); + bare_label_keyword_map.insert("map"); + bare_label_keyword_map.insert("mapping"); + bare_label_keyword_map.insert("match"); + bare_label_keyword_map.insert("matched"); + bare_label_keyword_map.insert("materialized"); + bare_label_keyword_map.insert("maxvalue"); + bare_label_keyword_map.insert("merge"); + bare_label_keyword_map.insert("method"); + bare_label_keyword_map.insert("minvalue"); + bare_label_keyword_map.insert("mode"); + bare_label_keyword_map.insert("move"); + bare_label_keyword_map.insert("name"); + bare_label_keyword_map.insert("names"); + bare_label_keyword_map.insert("national"); + bare_label_keyword_map.insert("nchar"); + bare_label_keyword_map.insert("nearest"); + bare_label_keyword_map.insert("new"); + bare_label_keyword_map.insert("next"); + bare_label_keyword_map.insert("no"); + bare_label_keyword_map.insert("none"); + bare_label_keyword_map.insert("nothing"); + bare_label_keyword_map.insert("notify"); + bare_label_keyword_map.insert("nowait"); + bare_label_keyword_map.insert("nullif"); + bare_label_keyword_map.insert("nulls"); + bare_label_keyword_map.insert("numeric"); + bare_label_keyword_map.insert("object"); + bare_label_keyword_map.insert("of"); + bare_label_keyword_map.insert("off"); + bare_label_keyword_map.insert("oids"); + bare_label_keyword_map.insert("old"); + bare_label_keyword_map.insert("operator"); + bare_label_keyword_map.insert("option"); + bare_label_keyword_map.insert("options"); + bare_label_keyword_map.insert("ordinality"); + bare_label_keyword_map.insert("others"); + bare_label_keyword_map.insert("out"); + bare_label_keyword_map.insert("overlay"); + bare_label_keyword_map.insert("overriding"); + bare_label_keyword_map.insert("owned"); + bare_label_keyword_map.insert("owner"); + bare_label_keyword_map.insert("parallel"); + bare_label_keyword_map.insert("parser"); + bare_label_keyword_map.insert("partial"); + bare_label_keyword_map.insert("partition"); + bare_label_keyword_map.insert("partitioned"); + bare_label_keyword_map.insert("passing"); + bare_label_keyword_map.insert("password"); + bare_label_keyword_map.insert("percent"); + bare_label_keyword_map.insert("persistent"); + bare_label_keyword_map.insert("plans"); + bare_label_keyword_map.insert("policy"); + bare_label_keyword_map.insert("position"); + bare_label_keyword_map.insert("pragma"); + bare_label_keyword_map.insert("preceding"); + bare_label_keyword_map.insert("prepare"); + bare_label_keyword_map.insert("prepared"); + bare_label_keyword_map.insert("preserve"); + bare_label_keyword_map.insert("prior"); + bare_label_keyword_map.insert("privileges"); + bare_label_keyword_map.insert("procedural"); + bare_label_keyword_map.insert("procedure"); + bare_label_keyword_map.insert("program"); + bare_label_keyword_map.insert("publication"); + bare_label_keyword_map.insert("quote"); + bare_label_keyword_map.insert("range"); + bare_label_keyword_map.insert("read"); + bare_label_keyword_map.insert("real"); + bare_label_keyword_map.insert("reassign"); + bare_label_keyword_map.insert("recheck"); + bare_label_keyword_map.insert("recursive"); + bare_label_keyword_map.insert("ref"); + bare_label_keyword_map.insert("referencing"); + bare_label_keyword_map.insert("refresh"); + bare_label_keyword_map.insert("register"); + bare_label_keyword_map.insert("reindex"); + bare_label_keyword_map.insert("relative"); + bare_label_keyword_map.insert("release"); + bare_label_keyword_map.insert("repeatable"); + bare_label_keyword_map.insert("replica"); + bare_label_keyword_map.insert("reset"); + bare_label_keyword_map.insert("resource"); + bare_label_keyword_map.insert("resources"); + bare_label_keyword_map.insert("restart"); + bare_label_keyword_map.insert("restrict"); + bare_label_keyword_map.insert("returns"); + bare_label_keyword_map.insert("revoke"); + bare_label_keyword_map.insert("role"); + bare_label_keyword_map.insert("rollback"); + bare_label_keyword_map.insert("rollup"); + bare_label_keyword_map.insert("row"); + bare_label_keyword_map.insert("rows"); + bare_label_keyword_map.insert("rule"); + bare_label_keyword_map.insert("sample"); + bare_label_keyword_map.insert("savepoint"); + bare_label_keyword_map.insert("schema"); + bare_label_keyword_map.insert("schemas"); + bare_label_keyword_map.insert("scope"); + bare_label_keyword_map.insert("scroll"); + bare_label_keyword_map.insert("search"); + bare_label_keyword_map.insert("secret"); + bare_label_keyword_map.insert("secure"); + bare_label_keyword_map.insert("security"); + bare_label_keyword_map.insert("sequence"); + bare_label_keyword_map.insert("sequences"); + bare_label_keyword_map.insert("serializable"); + bare_label_keyword_map.insert("server"); + bare_label_keyword_map.insert("session"); + bare_label_keyword_map.insert("set"); + bare_label_keyword_map.insert("setof"); + bare_label_keyword_map.insert("sets"); + bare_label_keyword_map.insert("share"); + bare_label_keyword_map.insert("similarity"); + bare_label_keyword_map.insert("simple"); + bare_label_keyword_map.insert("skip"); + bare_label_keyword_map.insert("smallint"); + bare_label_keyword_map.insert("snapshot"); + bare_label_keyword_map.insert("sorted"); + bare_label_keyword_map.insert("source"); + bare_label_keyword_map.insert("sql"); + bare_label_keyword_map.insert("stable"); + bare_label_keyword_map.insert("standalone"); + bare_label_keyword_map.insert("start"); + bare_label_keyword_map.insert("statement"); + bare_label_keyword_map.insert("statistics"); + bare_label_keyword_map.insert("stdin"); + bare_label_keyword_map.insert("stdout"); + bare_label_keyword_map.insert("storage"); + bare_label_keyword_map.insert("stored"); + bare_label_keyword_map.insert("strict"); + bare_label_keyword_map.insert("strip"); + bare_label_keyword_map.insert("struct"); + bare_label_keyword_map.insert("subscription"); + bare_label_keyword_map.insert("substring"); + bare_label_keyword_map.insert("sysid"); + bare_label_keyword_map.insert("system"); + bare_label_keyword_map.insert("tables"); + bare_label_keyword_map.insert("tablespace"); + bare_label_keyword_map.insert("target"); + bare_label_keyword_map.insert("temp"); + bare_label_keyword_map.insert("template"); + bare_label_keyword_map.insert("temporary"); + bare_label_keyword_map.insert("text"); + bare_label_keyword_map.insert("ties"); + bare_label_keyword_map.insert("time"); + bare_label_keyword_map.insert("timestamp"); + bare_label_keyword_map.insert("transaction"); + bare_label_keyword_map.insert("transform"); + bare_label_keyword_map.insert("treat"); + bare_label_keyword_map.insert("trigger"); + bare_label_keyword_map.insert("trim"); + bare_label_keyword_map.insert("truncate"); + bare_label_keyword_map.insert("trusted"); + bare_label_keyword_map.insert("try_cast"); + bare_label_keyword_map.insert("tuple"); + bare_label_keyword_map.insert("type"); + bare_label_keyword_map.insert("types"); + bare_label_keyword_map.insert("unbounded"); + bare_label_keyword_map.insert("uncommitted"); + bare_label_keyword_map.insert("unencrypted"); + bare_label_keyword_map.insert("unknown"); + bare_label_keyword_map.insert("unlisten"); + bare_label_keyword_map.insert("unlogged"); + bare_label_keyword_map.insert("until"); + bare_label_keyword_map.insert("update"); + bare_label_keyword_map.insert("use"); + bare_label_keyword_map.insert("user"); + bare_label_keyword_map.insert("vacuum"); + bare_label_keyword_map.insert("valid"); + bare_label_keyword_map.insert("validate"); + bare_label_keyword_map.insert("validator"); + bare_label_keyword_map.insert("value"); + bare_label_keyword_map.insert("values"); + bare_label_keyword_map.insert("varchar"); + bare_label_keyword_map.insert("variable"); + bare_label_keyword_map.insert("version"); + bare_label_keyword_map.insert("view"); + bare_label_keyword_map.insert("views"); + bare_label_keyword_map.insert("virtual"); + bare_label_keyword_map.insert("volatile"); + bare_label_keyword_map.insert("whitespace"); + bare_label_keyword_map.insert("work"); + bare_label_keyword_map.insert("wrapper"); + bare_label_keyword_map.insert("write"); + bare_label_keyword_map.insert("xml"); + bare_label_keyword_map.insert("xmlattributes"); + bare_label_keyword_map.insert("xmlconcat"); + bare_label_keyword_map.insert("xmlelement"); + bare_label_keyword_map.insert("xmlexists"); + bare_label_keyword_map.insert("xmlforest"); + bare_label_keyword_map.insert("xmlnamespaces"); + bare_label_keyword_map.insert("xmlparse"); + bare_label_keyword_map.insert("xmlpi"); + bare_label_keyword_map.insert("xmlroot"); + bare_label_keyword_map.insert("xmlserialize"); + bare_label_keyword_map.insert("xmltable"); + bare_label_keyword_map.insert("yes"); + bare_label_keyword_map.insert("zone"); + return keyword_maps; } } // namespace duckdb diff --git a/src/duckdb/src/parser/peg/matcher.cpp b/src/duckdb/src/parser/peg/matcher.cpp index 46e0b2407..8874b7e17 100644 --- a/src/duckdb/src/parser/peg/matcher.cpp +++ b/src/duckdb/src/parser/peg/matcher.cpp @@ -18,40 +18,10 @@ namespace duckdb { -static MatcherResult ExecuteRecursive(MatchInput input) { - auto &matcher = input.matcher; - auto &state = input.state; - state.rule = matcher.GetRule(); - PackratMatchState packrat_state; - if (PackratMatchState::IsEnabled(matcher, state)) { - auto cached_result = packrat_state.TryLoadCachedResult(matcher, state); - if (cached_result) { - return *cached_result; - } - } - - auto process = matcher.StartMatch(state); - optional child_result; - while (true) { - auto step = process->Resume(child_result); - child_result.reset(); - auto child = step.GetChild(); - if (!child) { - auto result = step.GetResult(); - packrat_state.StoreResult(matcher, state, result); - return result; - } - child_result = ExecuteRecursive(*child); - } -} - MatcherResult Matcher::MatchParseResult(MatchState &state) const { MatchInput input {*this, state}; - if (state.context.use_heap_based_parser) { - MatchStack stack; - return stack.Execute(input); - } - return ExecuteRecursive(input); + MatchStack stack; + return stack.Execute(input); } SuggestionType Matcher::AddSuggestion(MatchState &state) const { @@ -67,7 +37,7 @@ SuggestionType Matcher::AddSuggestion(MatchState &state) const { return AddSuggestionInternal(state); } -string Matcher::GetName() const { +string Matcher::GetPrintName() const { if (name.empty()) { return ToString(); } @@ -89,10 +59,9 @@ Matcher &MatcherAllocator::Allocate(unique_ptr matcher) { return result; } -optional_ptr ParseResultAllocator::Allocate(unique_ptr parse_result) { - auto result_ptr = parse_result.get(); - parse_results.push_back(std::move(parse_result)); - return optional_ptr(result_ptr); +ParseResultAllocator::ParseResultAllocator() : arena(Allocator::DefaultAllocator()), pending_destructors(arena) { } +ParseResultAllocator::~ParseResultAllocator() = default; + } // namespace duckdb diff --git a/src/duckdb/src/parser/peg/matcher_factory.cpp b/src/duckdb/src/parser/peg/matcher_factory.cpp index 8186ccbeb..84f9b1d2d 100644 --- a/src/duckdb/src/parser/peg/matcher_factory.cpp +++ b/src/duckdb/src/parser/peg/matcher_factory.cpp @@ -2,9 +2,24 @@ #include "duckdb/parser/peg/peg_parser.hpp" #include "duckdb/parser/peg/matcher/list.hpp" #include "duckdb/parser/peg/compiled_grammar.hpp" +#include "duckdb/parser/peg/matcher/literal_choice_matcher.hpp" namespace duckdb { +class CompiledKeywordMatcher final : public KeywordMatcher { +public: + CompiledKeywordMatcher(const string &keyword, const KeywordInfo &info, const PEGKeywordHelper &helper) + : KeywordMatcher(keyword, info, helper) { + } + + optional_idx GetDispatchLiteral(const GrammarLiteralTable &table) const override { + if (literal_table.get() != &table || !literal_info.LiteralId()) { + return optional_idx(); + } + return optional_idx(literal_info.LiteralId()); + } +}; + void MatcherFactory::MatcherConstructionState::Register(string_t rule_name) { unconstructed.insert(rule_name); } @@ -92,6 +107,14 @@ Matcher &MatcherFactory::CreateMatcher(const PEGExpression &expression, const st } } +optional_ptr MatcherFactory::GetRule(const string &rule_name) const { + auto entry = rules.find(rule_name); + if (entry == rules.end()) { + return nullptr; + } + return *entry->second; +} + Matcher &MatcherFactory::CreateMatcher(string_t rule_name, vector> ¶meters) { bool is_function_call = !parameters.empty(); auto matcher_entry = matchers.find(rule_name); @@ -133,7 +156,7 @@ Matcher &MatcherFactory::CreateMatcher(string_t rule_name, vector().suppress_suggestions = true; } return matcher; @@ -159,7 +185,7 @@ void MatcherFactory::AddRuleOverride(const char *name, unique_ptr &&mat matcher.SetPackratMemoized(); } if (grammar.GetRule(name)) { - auto rule_p = compiled.GetRule(name); + auto rule_p = GetRule(name); if (!rule_p) { throw InvalidInputException("No registered data exists for rule '%s', failed to set RuleOverride", name); } @@ -177,9 +203,10 @@ void MatcherFactory::SuppressSuggestions(const char *name) { no_suggestion_rules.insert(name); } -MatcherFactory::MatcherFactory(MatcherAllocator &allocator, const ParsedGrammar &grammar_p, CompiledGrammar &compiled_p, +MatcherFactory::MatcherFactory(MatcherAllocator &allocator, const ParsedGrammar &grammar_p, + const compiled_rules_map_t &rules, const PEGKeywordHelper &keyword_helper_p, terminal_rule_overrides_t terminal_rule_overrides_p) - : allocator(allocator), grammar(grammar_p), compiled(compiled_p), + : allocator(allocator), grammar(grammar_p), rules(rules), keyword_helper(keyword_helper_p), terminal_rule_overrides(std::move(terminal_rule_overrides_p)) { } @@ -193,27 +220,12 @@ Matcher &MatcherFactory::CreateRootMatcher(const string &root_rule) { // START GENERATED PACKRAT MEMOIZED RULES //===--------------------------------------------------------------------===// AddPackratMemoizedRule("Expression"); - AddPackratMemoizedRule("LambdaArrowExpression"); - AddPackratMemoizedRule("LogicalOrExpression"); - AddPackratMemoizedRule("LogicalAndExpression"); - AddPackratMemoizedRule("LogicalNotExpression"); - AddPackratMemoizedRule("IsExpression"); - AddPackratMemoizedRule("ComparisonExpression"); - AddPackratMemoizedRule("BitwiseExpression"); - AddPackratMemoizedRule("AdditiveExpression"); - AddPackratMemoizedRule("MultiplicativeExpression"); - AddPackratMemoizedRule("ExponentiationExpression"); - AddPackratMemoizedRule("PrefixExpression"); - AddPackratMemoizedRule("CollateExpression"); - AddPackratMemoizedRule("AtTimeZoneExpression"); - AddPackratMemoizedRule("SingleExpression"); - AddPackratMemoizedRule("BaseExpression"); - AddPackratMemoizedRule("ParensExpression"); - AddPackratMemoizedRule("ParenthesisExpression"); + AddPackratMemoizedRule("TableRef"); AddPackratMemoizedRule("Identifier"); AddPackratMemoizedRule("ColId"); - AddPackratMemoizedRule("ColumnReference"); - AddPackratMemoizedRule("FunctionExpression"); + AddPackratMemoizedRule("CatalogQualification"); + AddPackratMemoizedRule("SchemaQualification"); + AddPackratMemoizedRule("ReservedSchemaQualification"); //===--------------------------------------------------------------------===// // END GENERATED PACKRAT MEMOIZED RULES //===--------------------------------------------------------------------===// @@ -222,8 +234,6 @@ Matcher &MatcherFactory::CreateRootMatcher(const string &root_rule) { AddRuleOverride(entry.first.c_str(), std::move(entry.second)); } - // suppress suggestions for catch-all rules that would pollute statement-level autocomplete - SuppressSuggestions("ExpressionStatement"); // Using SHOW to describe a table/query is deprecated - parse these forms but do not autocomplete them after SHOW // (only setting names and the special SHOW forms are offered). DESCRIBE/SUMMARIZE still complete tables and // queries. @@ -257,23 +267,47 @@ Matcher &MatcherFactory::CreateRootMatcher(const string &root_rule) { } unique_ptr MatcherFactory::CreateKeyword(const string &keyword, const KeywordInfo &info) const { - return make_uniq(keyword, info); + return make_uniq(keyword, info, keyword_helper); +} + +template +static unique_ptr MakeStructural(ARGS &&... args) { + auto result = make_uniq(std::forward(args)...); + result->SetStructural(); + return result; } unique_ptr MatcherFactory::CreateList() const { - return make_uniq(); + return MakeStructural(); } unique_ptr MatcherFactory::CreateChoice(vector> &&matchers) const { - return make_uniq(std::move(matchers)); + auto &table = keyword_helper.GetLiteralTable(); + if (matchers.size() > 1) { + unordered_map literal_children; + for (idx_t i = 0; i < matchers.size(); i++) { + auto &matcher = matchers[i].get(); + if (matcher.Type() != MatcherType::KEYWORD) { + return MakeStructural(std::move(matchers)); + } + auto literal = matcher.Cast().GetDispatchLiteral(table); + if (!literal.IsValid()) { + return MakeStructural(std::move(matchers)); + } + // Preserve the first alternative when spellings share an ID. + literal_children.emplace(static_cast(literal.GetIndex()), i); + } + return MakeStructural(std::move(matchers), table, std::move(literal_children)); + } + return MakeStructural(std::move(matchers)); } unique_ptr MatcherFactory::CreateOptional(Matcher &matcher) const { - return make_uniq(matcher); + return MakeStructural(matcher); } unique_ptr MatcherFactory::CreateRepeat(Matcher &matcher) const { - return make_uniq(matcher); + return MakeStructural(matcher); } KeywordMatcher &MatcherFactory::Keyword(const string &keyword) const { diff --git a/src/duckdb/src/parser/peg/matcher_first_set.cpp b/src/duckdb/src/parser/peg/matcher_first_set.cpp new file mode 100644 index 000000000..e11efc38b --- /dev/null +++ b/src/duckdb/src/parser/peg/matcher_first_set.cpp @@ -0,0 +1,196 @@ +#include "duckdb/parser/peg/matcher.hpp" +#include "duckdb/parser/peg/matcher/list.hpp" +#include "duckdb/common/reference_map.hpp" + +namespace duckdb { + +//===--------------------------------------------------------------------===// +// FIRST sets +//===--------------------------------------------------------------------===// +bool MatcherFirstSet::Merge(const MatcherFirstSet &other) { + bool changed = false; + if (other.any && !any) { + any = changed = true; + } + if ((class_mask | other.class_mask) != class_mask) { + class_mask |= other.class_mask; + changed = true; + } + if (other.literals.size() > literals.size()) { + literals.resize(other.literals.size(), 0); + } + for (idx_t i = 0; i < other.literals.size(); i++) { + auto merged = literals[i] | other.literals[i]; + if (merged != literals[i]) { + literals[i] = merged; + changed = true; + } + } + return changed; +} + +void MatcherFirstSet::AddLiteral(idx_t literal_id) { + auto word = literal_id / 64; + if (word >= literals.size()) { + literals.resize(word + 1, 0); + } + literals[word] |= uint64_t(1) << (literal_id % 64); +} + +//! Calls fun(child) for every child of a built-in composite matcher +template +static void ForEachChild(Matcher &matcher, FUN &&fun) { + switch (matcher.Type()) { + case MatcherType::LIST: + for (auto &child : matcher.Cast().matchers) { + fun(child.get()); + } + break; + case MatcherType::CHOICE: + for (auto &child : matcher.Cast().matchers) { + fun(child.get()); + } + break; + case MatcherType::OPTIONAL: + fun(matcher.Cast().GetChildMatcher()); + break; + case MatcherType::REPEAT: + fun(matcher.Cast().GetChildMatcher()); + break; + default: + break; + } +} + +//! Derived matchers can override StartMatch, so only matchers marked as structural follow the semantics of their type +static bool IsComposite(const Matcher &matcher) { + if (!matcher.IsStructural()) { + return false; + } + auto type = matcher.Type(); + return type == MatcherType::LIST || type == MatcherType::CHOICE || type == MatcherType::OPTIONAL || + type == MatcherType::REPEAT; +} + +//! Recomputes the FIRST set of a composite matcher from its children, returns whether anything changed +static bool UpdateFirstSet(Matcher &matcher) { + auto &first = matcher.first_set; + bool changed = false; + bool nullable; + switch (matcher.Type()) { + case MatcherType::LIST: + // the first non-nullable element ends the FIRST set of a sequence + nullable = true; + for (auto &child : matcher.Cast().matchers) { + auto &child_first = child.get().first_set; + changed |= first.Merge(child_first); + if (!child_first.nullable) { + nullable = false; + break; + } + } + break; + case MatcherType::CHOICE: + nullable = false; + for (auto &child : matcher.Cast().matchers) { + auto &child_first = child.get().first_set; + changed |= first.Merge(child_first); + nullable = nullable || child_first.nullable; + } + break; + case MatcherType::OPTIONAL: + changed |= first.Merge(matcher.Cast().GetChildMatcher().first_set); + nullable = true; + break; + case MatcherType::REPEAT: { + auto &child_first = matcher.Cast().GetChildMatcher().first_set; + changed |= first.Merge(child_first); + nullable = child_first.nullable; + break; + } + default: + throw InternalException("UpdateFirstSet called on a matcher that is not composite"); + } + if (nullable && !first.nullable) { + first.nullable = true; + changed = true; + } + return changed; +} + +struct FirstSetTraversalEntry { + explicit FirstSetTraversalEntry(Matcher &matcher) : matcher(matcher) { + } + + reference matcher; + bool expanded = false; +}; + +void ComputeFirstSets(Matcher &root, const GrammarLiteralTable &table) { + // collect the reachable matchers, children before their parents (except for recursive references) + vector> matchers; + vector> composites; + reference_set_t seen; + vector pending; + pending.emplace_back(root); + seen.insert(root); + while (!pending.empty()) { + auto &entry = pending.back(); + auto &matcher = entry.matcher.get(); + if (entry.expanded) { + pending.pop_back(); + matchers.push_back(matcher); + continue; + } + entry.expanded = true; + // entry is invalidated by the pushes below + ForEachChild(matcher, [&](Matcher &child) { + if (seen.insert(child).second) { + pending.emplace_back(child); + } + }); + } + for (auto &entry : matchers) { + auto &matcher = entry.get(); + auto &first = matcher.first_set; + first = MatcherFirstSet(); + first.table = &table; + if (IsComposite(matcher)) { + composites.push_back(matcher); + } else if (matcher.IsAtomic()) { + static_cast(matcher).InitializeFirstSet(first, table); + } else { + // custom and derived matchers: no information + first.any = true; + } + } + // composite matchers: least fixed point, the grammar is recursive + // children are visited before their parents, so only recursive references need another pass + bool changed = true; + while (changed) { + changed = false; + for (auto &matcher : composites) { + changed |= UpdateFirstSet(matcher.get()); + } + } + for (auto &entry : matchers) { + entry.get().first_set.computed = true; + } +} + +bool MatcherFirstSet::MightMatch(MatchState &state) const { + if (!computed || any || nullable) { + return true; + } + auto token = state.token_iterator.Current(); + if (!token) { + return false; + } + if (token->token_class & class_mask) { + return true; + } + auto literal_id = state.token_iterator.CurrentLiteralInfo(*table).LiteralId(); + return literal_id && HasLiteral(literal_id); +} + +} // namespace duckdb diff --git a/src/duckdb/src/parser/peg/matcher_process.cpp b/src/duckdb/src/parser/peg/matcher_process.cpp index b1511f31f..da631f6ce 100644 --- a/src/duckdb/src/parser/peg/matcher_process.cpp +++ b/src/duckdb/src/parser/peg/matcher_process.cpp @@ -1,5 +1,6 @@ #include "duckdb/parser/peg/matcher.hpp" #include "duckdb/parser/peg/matcher/choice_matcher.hpp" +#include "duckdb/parser/peg/matcher/literal_choice_matcher.hpp" #include "duckdb/parser/peg/matcher/list_matcher.hpp" #include "duckdb/parser/peg/matcher/optional_matcher.hpp" #include "duckdb/parser/peg/matcher/repeat_matcher.hpp" @@ -20,7 +21,9 @@ optional MatchStep::GetChild() { MatcherResult MatchStep::GetResult() const { D_ASSERT(!child); - D_ASSERT(result); + if (!result) { + throw InternalException("Completed match step has no result"); + } return *result; } @@ -49,7 +52,10 @@ arena_ptr AtomicMatcher::StartMatch(MatchState &state) const { class ListMatchProcess : public MatchProcess { public: ListMatchProcess(const ListMatcher &matcher_p, MatchState &state_p) - : matcher(matcher_p), state(state_p), list_state(state_p) { + : matcher(matcher_p), state(state_p), list_state(state_p), results(state_p.context.process_allocator) { + if (state_p.BuildParseResult()) { + results.reserve(matcher_p.matchers.size()); + } saved_suggestion_size = matcher.suppress_suggestions ? list_state.context.suggestions.size() : 0; if (auto current = list_state.token_iterator.Current()) { start_offset = optional_idx(current->offset); @@ -65,22 +71,23 @@ class ListMatchProcess : public MatchProcess { return MatchStep::Complete(MatcherResult::Failure()); } if (child_result->HasParseResult()) { - results.push_back(*child_result->GetParseResult()); + results.emplace_back(*child_result->GetParseResult()); } child_index++; } while (child_index < matcher.matchers.size()) { + auto &child_matcher = matcher.matchers[child_index].get(); auto current = list_state.token_iterator.Current(); bool at_autocomplete_cursor = current && current->type == TokenType::END_OF_INPUT_AUTOCOMPLETE; if (!at_autocomplete_cursor) { awaiting_child = true; - return MatchStep::Child({matcher.matchers[child_index].get(), list_state}); + return MatchStep::Child({child_matcher, list_state}); } if (matcher.suppress_suggestions) { DiscardSuggestions(); return MatchStep::Complete(MatcherResult::Failure()); } - if (matcher.matchers[child_index].get().AddSuggestion(list_state) == SuggestionType::OPTIONAL) { + if (child_matcher.AddSuggestion(list_state) == SuggestionType::OPTIONAL) { child_index++; continue; } @@ -89,12 +96,48 @@ class ListMatchProcess : public MatchProcess { } state.token_iterator.SetPosition(list_state.token_iterator); DiscardSuggestions(); - auto list_name = matcher.HasName() ? matcher.GetName() : string(); - return MatchStep::Complete( - state.AllocateParseResult(std::move(results), std::move(list_name), start_offset)); + if (matcher.IsCollapsible()) { + auto collapsible = FindCollapsibleResult(); + if (collapsible) { + collapsible->collapsed = true; + return MatchStep::Complete(MatcherResult::Success(collapsible)); + } + } + auto named_matcher = matcher.HasName() ? &matcher : nullptr; + return MatchStep::Complete(state.AllocateParseResult( + state.context.allocator.MakeChildren(results), named_matcher, start_offset)); } private: + //! The child that can stand in for this rule's own result, or nullptr when the rule has to build one + optional_ptr FindCollapsibleResult() const { + optional_ptr collapsible; + for (auto &child : results) { + auto &child_result = child.get(); + // an optional that matched nothing carries no value, so it does not stop the rule from collapsing + if (child_result.type == ParseResultType::OPTIONAL && + !child_result.Cast().HasResult()) { + continue; + } + + // a second child with a result means the rule combines them rather than forwarding one of them + if (collapsible) { + return nullptr; + } + + collapsible = child_result; + } + + // only results that carry a rule of their own are collapsible into this one, since the result is + // transformed by that rule + if (collapsible && !collapsible->GetRule()) { + return nullptr; + } + + // null when no child produced a result, so a rule that matched empty still gets a result of its own + return collapsible; + } + void DiscardSuggestions() { if (!matcher.suppress_suggestions) { return; @@ -108,7 +151,7 @@ class ListMatchProcess : public MatchProcess { const ListMatcher &matcher; MatchState &state; MatchState list_state; - vector> results; + arena_vector> results; idx_t child_index = 0; idx_t saved_suggestion_size = 0; optional_idx start_offset; @@ -119,9 +162,11 @@ arena_ptr ListMatcher::StartMatch(MatchState &state) const { return state.Make(*this, state); } +template class ChoiceMatchProcess : public MatchProcess { public: - ChoiceMatchProcess(const ChoiceMatcher &matcher_p, MatchState &state_p) : matcher(matcher_p), state(state_p) { + ChoiceMatchProcess(const ChoiceMatcher &matcher_p, MatchState &state_p, idx_t child_index_p = 0) + : matcher(matcher_p), state(state_p), child_index(child_index_p) { if (auto current = state.token_iterator.Current()) { start_offset = optional_idx(current->offset); } @@ -131,7 +176,9 @@ class ChoiceMatchProcess : public MatchProcess { D_ASSERT(awaiting_child == child_result.has_value()); if (child_result) { awaiting_child = false; - D_ASSERT(child_state); + if (!child_state) { + throw InternalException("Choice matcher child state is missing"); + } if (child_result->IsSuccess()) { state.token_iterator.SetPosition(child_state->token_iterator); if (!child_result->HasParseResult()) { @@ -140,6 +187,9 @@ class ChoiceMatchProcess : public MatchProcess { return MatchStep::Complete(state.AllocateParseResult(*child_result->GetParseResult(), child_index, start_offset)); } + if (SINGLE_CHILD) { + return MatchStep::Complete(MatcherResult::Failure()); + } child_index++; child_state.reset(); } @@ -161,7 +211,14 @@ class ChoiceMatchProcess : public MatchProcess { }; arena_ptr ChoiceMatcher::StartMatch(MatchState &state) const { - return state.Make(*this, state); + return state.Make>(*this, state); +} + +arena_ptr LiteralChoiceMatcher::StartMatch(MatchState &state) const { + auto literal = state.token_iterator.CurrentLiteralInfo(table); + auto entry = literal_children.find(literal.LiteralId()); + auto child_index = entry == literal_children.end() ? matchers.size() : entry->second; + return state.Make>(*this, state, child_index); } class OptionalMatchProcess : public MatchProcess { @@ -206,7 +263,7 @@ arena_ptr OptionalMatcher::StartMatch(MatchState &state) const { class RepeatMatchProcess : public MatchProcess { public: RepeatMatchProcess(const RepeatMatcher &matcher_p, MatchState &state_p) - : matcher(matcher_p), state(state_p), repeat_state(state_p) { + : matcher(matcher_p), state(state_p), repeat_state(state_p), results(state_p.context.process_allocator) { if (auto current = repeat_state.token_iterator.Current()) { start_offset = optional_idx(current->offset); } @@ -224,7 +281,7 @@ class RepeatMatchProcess : public MatchProcess { } matched_once = true; if (child_result->HasParseResult()) { - results.push_back(*child_result->GetParseResult()); + results.emplace_back(*child_result->GetParseResult()); } state.token_iterator.SetPosition(repeat_state.token_iterator); auto current = repeat_state.token_iterator.Current(); @@ -239,14 +296,15 @@ class RepeatMatchProcess : public MatchProcess { private: MatcherResult CreateResult() { - return state.AllocateParseResult(std::move(results), start_offset); + return state.AllocateParseResult(state.context.allocator.MakeChildren(results), + start_offset); } private: const RepeatMatcher &matcher; MatchState &state; MatchState repeat_state; - vector> results; + arena_vector> results; bool matched_once = false; optional_idx start_offset; bool awaiting_child = false; diff --git a/src/duckdb/src/parser/peg/matcher_stack.cpp b/src/duckdb/src/parser/peg/matcher_stack.cpp index bd434f2e9..16653c385 100644 --- a/src/duckdb/src/parser/peg/matcher_stack.cpp +++ b/src/duckdb/src/parser/peg/matcher_stack.cpp @@ -1,5 +1,6 @@ #include "duckdb/parser/peg/matcher_stack.hpp" #include "duckdb/common/optional.hpp" +#include "duckdb/parser/peg/matcher/optional_matcher.hpp" namespace duckdb { @@ -16,7 +17,13 @@ MatchStack::~MatchStack() { void MatchStack::DestroyTopFrame() { D_ASSERT(!frames.empty()); + auto &frame = frames.back(); + auto &process_allocator = frame.match_state.context.process_allocator; + const auto process_arena_position = frame.process_arena_position; frames.pop_back(); + // the processes of a frame and everything they allocated are dead once the frame is popped - release them so the + // process arena only ever holds the live frames instead of every process the whole statement ever created + process_allocator.Rewind(process_arena_position); } optional PackratMatchState::TryLoadCachedResult(const Matcher &matcher, MatchState &state) { @@ -78,6 +85,7 @@ MatcherResult MatchStack::ExecuteAtomicMatcher(MatchInput input) { void MatchStack::PushFrame(MatchInput input) { input.state.rule = input.matcher.GetRule(); frames.emplace_back(input); + frames.back().process_arena_position = input.state.context.process_allocator.GetPosition(); } void MatchStack::InitializeFrame(MatchStackFrame &frame) { @@ -113,6 +121,21 @@ bool MatchStack::ExecuteFrame(MatchStackFrame &frame) { frame.child_result = ExecuteAtomicMatcher(*child); return false; } + if (child->state.BuildParseResult()) { + // skip sub-matchers that certainly cannot match at the current token (see MatcherFirstSet); such an attempt + // would fail without consuming a token, so skipping it changes neither the result nor the error position + if (child->matcher.Type() == MatcherType::OPTIONAL && child->matcher.IsStructural() && + !child->matcher.Cast().GetChildMatcher().first_set.MightMatch(child->state)) { + // what the optional's own process returns when its element fails + child->state.rule = child->matcher.GetRule(); + frame.child_result = child->state.AllocateParseResult(); + return false; + } + if (!child->matcher.first_set.MightMatch(child->state)) { + frame.child_result = MatcherResult::Failure(); + return false; + } + } PushFrame(*child); return false; } diff --git a/src/duckdb/src/parser/peg/operator_matcher.cpp b/src/duckdb/src/parser/peg/operator_matcher.cpp new file mode 100644 index 000000000..d6fe001ee --- /dev/null +++ b/src/duckdb/src/parser/peg/operator_matcher.cpp @@ -0,0 +1,30 @@ +#include "duckdb/parser/peg/matcher/operator_matcher.hpp" + +namespace duckdb { + +bool OperatorMatcher::HasSpecialPrecedence(const string &operator_name) { + switch (operator_name.size()) { + case 1: + switch (operator_name[0]) { + case '=': + case '<': + case '>': + case '~': + return true; + default: + return false; + } + case 2: + return operator_name == "->" || operator_name == "<=" || operator_name == ">=" || operator_name == "!=" || + operator_name == "==" || operator_name == "<>" || operator_name == "~~" || operator_name == "~*" || + operator_name == "!~"; + case 3: + return operator_name == "~~*" || operator_name == "~~~" || operator_name == "!~~" || operator_name == "!~*"; + case 4: + return operator_name == "!~~*"; + default: + return false; + } +} + +} // namespace duckdb diff --git a/src/duckdb/src/parser/peg/parsed_grammar.cpp b/src/duckdb/src/parser/peg/parsed_grammar.cpp index a09bfc8a8..48628a0dc 100644 --- a/src/duckdb/src/parser/peg/parsed_grammar.cpp +++ b/src/duckdb/src/parser/peg/parsed_grammar.cpp @@ -104,9 +104,11 @@ void ParsedGrammar::AddParsedRule(ParsedGrammarRule rule) { rules.emplace(std::move(name), make_uniq(std::move(rule))); } -void ParsedGrammar::AddRule(const string &rule_definition, grammar_transform_process_function_t transform_process) { +void ParsedGrammar::AddRule(const string &rule_definition, grammar_transform_process_function_t transform_process, + bool suppress_suggestions) { auto rule = ParseSingleRule(rule_definition); rule.transform_process = std::move(transform_process); + rule.suppress_suggestions = suppress_suggestions; AddParsedRule(std::move(rule)); } @@ -217,10 +219,16 @@ void ParsedGrammar::ReplaceRule(const string &rule_definition, grammar_transform entry->second = make_uniq(std::move(rule)); } -void ParsedGrammar::SetTransformProcess(const string &rule_name, - grammar_transform_process_function_t transform_process) { +void ParsedGrammar::SetTransformProcess(const string &rule_name, grammar_transform_process_function_t transform_process, + bool collapsible, bool suppress_suggestions) { auto &rule = GetMutableRule(rule_name); rule.transform_process = std::move(transform_process); + rule.collapsible = collapsible; + rule.suppress_suggestions = suppress_suggestions; +} + +void ParsedGrammar::SetExpressionDepth(const string &rule_name, ExpressionDepthKind kind) { + GetMutableRule(rule_name).expression_depth = kind; } void ParsedGrammar::AddTerminalRuleOverride(const string &rule_name, terminal_rule_matcher_factory_t matcher_factory) { diff --git a/src/duckdb/src/parser/peg/parser_packrat.cpp b/src/duckdb/src/parser/peg/parser_packrat.cpp index d8651b00d..4d8c33572 100644 --- a/src/duckdb/src/parser/peg/parser_packrat.cpp +++ b/src/duckdb/src/parser/peg/parser_packrat.cpp @@ -8,7 +8,8 @@ size_t ParserPackratKeyHash::operator()(const ParserPackratKey &key) const { return std::hash()(key.matcher_id) ^ (std::hash()(key.token_index) << 1); } -ParserPackratCache::ParserPackratCache() = default; +ParserPackratCache::ParserPackratCache(ArenaAllocator &arena) : entries(arena) { +} ParserPackratCache::~ParserPackratCache() = default; diff --git a/src/duckdb/src/parser/peg/passthrough_dialect.cpp b/src/duckdb/src/parser/peg/passthrough_dialect.cpp new file mode 100644 index 000000000..c160cc601 --- /dev/null +++ b/src/duckdb/src/parser/peg/passthrough_dialect.cpp @@ -0,0 +1,49 @@ +#include "duckdb/parser/peg/passthrough_dialect.hpp" + +#include "duckdb/parser/peg/parsed_grammar.hpp" +#include "duckdb/parser/peg/transformer/peg_transformer.hpp" +#include "duckdb/parser/statement/passthrough_statement.hpp" + +namespace duckdb { + +namespace { + +//! Yields the statement without looking at its tokens - the text is filled in from the parse result's extent +class PassthroughTransformProcess final : public TransformProcess { +public: + explicit PassthroughTransformProcess(PEGTransformer &transformer_p) : transformer(transformer_p) { + } + + TransformStep Resume(arena_ptr child_result) override { + D_ASSERT(!child_result); + unique_ptr statement = make_uniq(); + return TransformStep::Complete(transformer.MakeResult>(std::move(statement))); + } + +private: + PEGTransformer &transformer; +}; + +arena_ptr StartPassthroughTransform(PEGTransformer &transformer, ParseResult &) { + return transformer.MakeProcess(transformer); +} + +} // namespace + +void PassthroughDialect::ApplyGrammarChanges(GrammarChangesInput &input) { + auto &grammar = input.parsed_grammar; + // the body is a placeholder: the terminal override below is what matches a token + grammar.AddRule("PassthroughToken <- Identifier"); + grammar.AddTerminalRuleOverride("PassthroughToken", [](const PEGKeywordHelper &) -> unique_ptr { + return make_uniq(); + }); + grammar.AddRule("PassthroughStatement <- PassthroughToken+", StartPassthroughTransform); + // keep Statement's dispatch transform - it forwards to whichever alternative matched + auto statement_rule = grammar.GetRule("Statement"); + if (!statement_rule) { + throw InternalException("The passthrough dialect requires a Statement rule"); + } + grammar.ReplaceRule("Statement <- DisconnectStatement / PassthroughStatement", statement_rule->transform_process); +} + +} // namespace duckdb diff --git a/src/duckdb/src/parser/peg/peg_parser.cpp b/src/duckdb/src/parser/peg/peg_parser.cpp index 0d4d95bcd..4731b732b 100644 --- a/src/duckdb/src/parser/peg/peg_parser.cpp +++ b/src/duckdb/src/parser/peg/peg_parser.cpp @@ -199,6 +199,7 @@ void PEGParser::ParseRules(const char *grammar) { // if we see a newline while we are parsing a rule definition we can complete the rule auto new_rule = PEGRule(std::move(parameters), BuildExpression(tokens)); AddRule(rule_name, std::move(new_rule)); + parameters = string_map_t(); rule_name = string_t(); // look for the subsequent rule parse_state = PEGParseState::RULE_NAME; diff --git a/src/duckdb/src/parser/peg/sql_formatter.cpp b/src/duckdb/src/parser/peg/sql_formatter.cpp index 0e3df4d07..af672832b 100644 --- a/src/duckdb/src/parser/peg/sql_formatter.cpp +++ b/src/duckdb/src/parser/peg/sql_formatter.cpp @@ -36,6 +36,10 @@ class SQLFormatter { static bool IsClauseKeywordLine(const string &trimmed); static bool ShouldUppercase(const string &kw); + // Token helpers + static vector Tokenize(const string &sql); + static bool SkipNonCodeToken(const vector &tokens, idx_t &token_idx, idx_t &pos); + // Token lookahead helpers static string PeekKeyword(const vector &tokens, idx_t i, idx_t offset); static idx_t DetectCompoundClause(const vector &tokens, idx_t i, string &compound_text, @@ -87,7 +91,7 @@ class SQLFormatter { SQLFormatter::SQLFormatter(const FormatterConfig &config) : config(config) { } -string SQLFormatter::Format(const string &sql) { +vector SQLFormatter::Tokenize(const string &sql) { auto &keyword_helper = DuckDBKeywordHelper::Instance(); vector tokens; HighlightTokenizerBehavior behavior(sql, tokens); @@ -100,6 +104,42 @@ string SQLFormatter::Format(const string &sql) { } } + return tokens; +} + +bool SQLFormatter::SkipNonCodeToken(const vector &tokens, idx_t &token_idx, idx_t &pos) { + while (token_idx < tokens.size() && tokens[token_idx].offset + tokens[token_idx].length <= pos) { + token_idx++; + } + if (token_idx == tokens.size()) { + return false; + } + auto &token = tokens[token_idx]; + if (token.offset > pos) { + return false; + } + bool quoted_identifier = + token.type == TokenType::IDENTIFIER && !token.text.empty() && (token.text[0] == '"' || token.text[0] == '`'); + if (token.type != TokenType::STRING_LITERAL && token.type != TokenType::COMMENT && !quoted_identifier) { + return false; + } + auto length = token.length; + // Keep the newline ending a line comment as a formatting boundary. + if (token.type == TokenType::COMMENT && StringUtil::StartsWith(token.text, "--")) { + while (length > 0 && (token.text[length - 1] == '\n' || token.text[length - 1] == '\r')) { + length--; + } + } + if (token.offset + length <= pos) { + return false; + } + pos = token.offset + length - 1; + return true; +} + +string SQLFormatter::Format(const string &sql) { + auto tokens = Tokenize(sql); + if (tokens.empty()) { return sql; } @@ -327,9 +367,14 @@ string SQLFormatter::TrimLeft(const string &line) { } vector SQLFormatter::SplitLines(const string &formatted) { + auto tokens = Tokenize(formatted); + idx_t token_idx = 0; vector lines; idx_t start = 0; for (idx_t k = 0; k <= formatted.size(); k++) { + if (SkipNonCodeToken(tokens, token_idx, k)) { + continue; + } if (k == formatted.size() || formatted[k] == '\n') { lines.push_back(formatted.substr(start, k - start)); start = k + 1; @@ -866,6 +911,9 @@ string SQLFormatter::MergeShortClauses(const string &formatted) const { break; } content_end = j + 1; + if (cline.find('\n') != string::npos) { + break; + } j++; } @@ -1028,19 +1076,15 @@ string SQLFormatter::CollapseFirstCondition(const string &formatted) const { vector SQLFormatter::SplitTopLevelCommas(const string &s) { vector parts; int32_t depth = 0; - bool in_str = false; - char str_char = '\0'; + auto tokens = Tokenize(s); + idx_t token_idx = 0; idx_t start = 0; for (idx_t k = 0; k < s.size(); k++) { const char c = s[k]; - if (in_str) { - if (c == str_char) { - in_str = false; - } - } else if (c == '\'' || c == '"' || c == '`') { - in_str = true; - str_char = c; - } else if (c == '(') { + if (SkipNonCodeToken(tokens, token_idx, k)) { + continue; + } + if (c == '(') { depth++; } else if (c == ')') { depth--; @@ -1081,18 +1125,14 @@ vector SQLFormatter::SplitTopLevelCommas(const string &s) { //! Returns string::npos if not found. idx_t SQLFormatter::FindMatchingClose(const string &s, idx_t open_pos) { int32_t depth = 0; - bool in_str = false; - char str_char = '\0'; + auto tokens = Tokenize(s); + idx_t token_idx = 0; for (idx_t k = open_pos; k < s.size(); k++) { const char c = s[k]; - if (in_str) { - if (c == str_char) { - in_str = false; - } - } else if (c == '\'' || c == '"' || c == '`') { - in_str = true; - str_char = c; - } else if (c == '(') { + if (SkipNonCodeToken(tokens, token_idx, k)) { + continue; + } + if (c == '(') { depth++; } else if (c == ')') { depth--; @@ -1165,7 +1205,12 @@ string SQLFormatter::ExpandTableDefinition(const string &formatted) const { } idx_t paren_pos = string::npos; + auto tokens = Tokenize(line); + idx_t token_idx = 0; for (idx_t k = indent + prefix_len; k < line.size(); k++) { + if (SkipNonCodeToken(tokens, token_idx, k)) { + continue; + } if (line[k] == '(') { paren_pos = k; break; @@ -1192,7 +1237,12 @@ string SQLFormatter::ExpandTableDefinition(const string &formatted) const { // Case A: keyword on line i, "tablename(cols)" on line i+1. const string &next_line = lines[i + 1]; idx_t next_paren = string::npos; + auto next_tokens = Tokenize(next_line); + idx_t next_token_idx = 0; for (idx_t k = 0; k < next_line.size(); k++) { + if (SkipNonCodeToken(next_tokens, next_token_idx, k)) { + continue; + } if (next_line[k] == '(') { next_paren = k; break; @@ -1278,19 +1328,10 @@ bool SQLFormatter::MatchKeywordAt(const string &s, idx_t pos, const char *keywor //! skipping string literals. Does NOT skip paren-enclosed content //! so that CASE inside function calls (e.g. sum(CASE...)) is found. idx_t SQLFormatter::FindKeywordAny(const string &s, idx_t from_pos, const char *keyword) { - bool in_str = false; - char str_char = '\0'; + auto tokens = Tokenize(s); + idx_t token_idx = 0; for (idx_t k = from_pos; k < s.size(); k++) { - const char c = s[k]; - if (in_str) { - if (c == str_char) { - in_str = false; - } - continue; - } - if (c == '\'' || c == '"' || c == '`') { - in_str = true; - str_char = c; + if (SkipNonCodeToken(tokens, token_idx, k)) { continue; } if (MatchKeywordAt(s, k, keyword)) { @@ -1304,21 +1345,11 @@ idx_t SQLFormatter::FindKeywordAny(const string &s, idx_t from_pos, const char * //! CASE...END pairs. Returns string::npos if no matching END is found. idx_t SQLFormatter::FindCaseEnd(const string &s, idx_t case_pos) { int32_t depth = 0; - bool in_str = false; - char str_char = '\0'; + auto tokens = Tokenize(s); + idx_t token_idx = 0; idx_t k = case_pos; while (k < s.size()) { - const char c = s[k]; - if (in_str) { - if (c == str_char) { - in_str = false; - } - k++; - continue; - } - if (c == '\'' || c == '"' || c == '`') { - in_str = true; - str_char = c; + if (SkipNonCodeToken(tokens, token_idx, k)) { k++; continue; } @@ -1347,8 +1378,8 @@ vector SQLFormatter::SplitCaseBranches(const string &content) { vector branches; int32_t paren_depth = 0; int32_t case_depth = 0; - bool in_str = false; - char str_char = '\0'; + auto tokens = Tokenize(content); + idx_t token_idx = 0; idx_t start = 0; while (start < content.size() && content[start] == ' ') { @@ -1358,16 +1389,7 @@ vector SQLFormatter::SplitCaseBranches(const string &content) { while (k < content.size()) { const char c = content[k]; - if (in_str) { - if (c == str_char) { - in_str = false; - } - k++; - continue; - } - if (c == '\'' || c == '"' || c == '`') { - in_str = true; - str_char = c; + if (SkipNonCodeToken(tokens, token_idx, k)) { k++; continue; } @@ -1528,20 +1550,12 @@ string SQLFormatter::ExpandCaseExpressions(const string &formatted) const { idx_t SQLFormatter::FindLongestParenList(const string &line, idx_t from_pos) { idx_t best_pos = string::npos; idx_t best_len = 0; - bool in_str = false; - char str_char = '\0'; + auto tokens = Tokenize(line); + idx_t token_idx = 0; int32_t depth = 0; for (idx_t k = from_pos; k < line.size(); k++) { char c = line[k]; - if (in_str) { - if (c == str_char) { - in_str = false; - } - continue; - } - if (c == '\'' || c == '"' || c == '`') { - in_str = true; - str_char = c; + if (SkipNonCodeToken(tokens, token_idx, k)) { continue; } if (c == '(' && depth == 0) { diff --git a/src/duckdb/src/parser/peg/tokenizer/base_tokenizer.cpp b/src/duckdb/src/parser/peg/tokenizer/base_tokenizer.cpp index 916a3a06e..c02196b46 100644 --- a/src/duckdb/src/parser/peg/tokenizer/base_tokenizer.cpp +++ b/src/duckdb/src/parser/peg/tokenizer/base_tokenizer.cpp @@ -1,52 +1,62 @@ #include "duckdb/common/string_util.hpp" #include "duckdb/parser/peg/tokenizer/tokenizer.hpp" #include "duckdb/parser/peg/keyword_helper.hpp" +#include "duckdb/parser/peg/matcher/operator_matcher.hpp" namespace duckdb { +uint8_t ComputeMatcherTokenClass(TokenType type, const string &text) { + switch (type) { + case TokenType::KEYWORD: + case TokenType::IDENTIFIER: + return MatcherTokenClass::WORD; + case TokenType::STRING_LITERAL: + // string literals are also accepted in identifier positions (e.g. table and file names) + return MatcherTokenClass::STRING | MatcherTokenClass::WORD; + case TokenType::NUMBER_LITERAL: + return MatcherTokenClass::NUMBER; + case TokenType::OPERATOR: + if (OperatorMatcher::HasSpecialPrecedence(text)) { + return MatcherTokenClass::OPERATOR; + } + return MatcherTokenClass::OPERATOR | MatcherTokenClass::GENERIC_OPERATOR; + default: + // terminators and end-of-input are only matched as literals or by matchers without a FIRST set + return 0; + } +} + TokenizerBehavior::TokenizerBehavior(const string &sql, vector &tokens) : sql(sql), tokens(tokens) { } Tokenizer::Tokenizer(const PEGKeywordHelper &keyword_helper_p) : keyword_helper(keyword_helper_p) { } -static bool OperatorEquals(const char *str, const char *op, idx_t len, idx_t &op_len) { - for (idx_t i = 0; i < len; i++) { - if (str[i] != op[i]) { - return false; - } - } - op_len = len; - return true; +bool Tokenizer::BackslashEscapesStringLiterals() const { + return false; } -bool Tokenizer::IsSpecialOperator(const string &sql, idx_t pos, idx_t &op_len) const { - const char *op_start = sql.c_str() + pos; - if (pos + 2 < sql.size()) { - if (OperatorEquals(op_start, "->>", 3, op_len)) { - return true; - } - } - if (pos + 1 >= sql.size()) { - // 2-byte operators are out-of-bounds +bool Tokenizer::IsQuotedIdentifierDelimiter(char character) const { + // double quotes are the SQL standard delimiter; backticks are accepted as an alternative + // (as in several other SQL dialects), with the same doubling rule for escaping the delimiter + return character == '"' || character == '`'; +} + +void Tokenizer::HandleLastToken(TokenizerBehavior &behavior, TokenizeState state, const string &sql, + idx_t last_pos) const { + string last_word = sql.substr(last_pos, sql.size() - last_pos); + behavior.OnLastToken(*this, state, last_word, last_pos); +} + +bool Tokenizer::IsCompoundColonToken(const string &sql, idx_t pos, idx_t &token_length) const { + if (pos + 1 >= sql.size() || sql[pos] != ':') { return false; } - if (OperatorEquals(op_start, "::", 2, op_len)) { - return true; - } - if (OperatorEquals(op_start, ":=", 2, op_len)) { - return true; - } - if (OperatorEquals(op_start, "->", 2, op_len)) { - return true; - } - if (OperatorEquals(op_start, "**", 2, op_len)) { - return true; - } - if (OperatorEquals(op_start, "//", 2, op_len)) { - return true; + if (sql[pos + 1] != ':' && sql[pos + 1] != '=') { + return false; } - return false; + token_length = 2; + return true; } bool Tokenizer::IsSingleByteOperator(char c) { @@ -58,9 +68,9 @@ bool Tokenizer::IsSingleByteOperator(char c) { case '[': case ']': case ',': + case ':': case '?': case '$': - case '-': case '#': return true; default: @@ -75,6 +85,15 @@ bool Tokenizer::CharacterIsInitialNumber(char c) { return c == '.'; } +static bool NumberHasDotOrExponent(const string &sql, idx_t start, idx_t end) { + for (idx_t i = start; i < end; i++) { + if (sql[i] == '.' || sql[i] == 'e' || sql[i] == 'E') { + return true; + } + } + return false; +} + bool Tokenizer::CharacterIsSpecialStringCharacter(char c) { if (c == 'N' || c == 'n') { return true; @@ -118,7 +137,6 @@ bool Tokenizer::CharacterIsScientific(char c) { bool Tokenizer::CharacterIsControlFlow(char c) { switch (c) { case '\'': - case '-': case ';': case '"': case '.': @@ -145,13 +163,25 @@ bool Tokenizer::CharacterIsKeyword(char c) { } bool Tokenizer::CharacterIsOperator(char c) { - if (IsSingleByteOperator(c)) { - return false; - } - if (CharacterIsControlFlow(c)) { + switch (c) { + case '+': + case '-': + case '*': + case '/': + case '%': + case '^': + case '<': + case '>': + case '=': + case '~': + case '!': + case '@': + case '&': + case '|': + return true; + default: return false; } - return StringUtil::CharacterIsOperator(c); } TokenType Tokenizer::TokenizeStateToType(TokenizeState state) { @@ -245,17 +275,21 @@ bool Tokenizer::TokenizeInput(TokenizerBehavior &behavior) const { auto &sql = behavior.sql; auto &tokens = behavior.tokens; if (TokenizeInputInternal(behavior)) { - tokens.emplace_back("", sql.size(), behavior.GetTerminator()); + auto terminator = behavior.GetTerminator(); + tokens.emplace_back("", sql.size(), terminator); + if (terminator == TokenType::END_OF_INPUT_AUTOCOMPLETE) { + return true; + } } else { tokens.emplace_back("", sql.size(), TokenType::END_OF_INPUT); } - return !tokens.empty() && tokens.back().type == TokenType::END_OF_INPUT_AUTOCOMPLETE; + return false; } -void Tokenizer::PushOperatorToken(TokenizerBehavior &behavior, idx_t start, idx_t end) { +void Tokenizer::PushOperatorToken(TokenizerBehavior &behavior, idx_t start, idx_t end) const { auto &sql = behavior.sql; auto &tokens = behavior.tokens; - // Apply PostgreSQL trimming rule: an operator cannot end in '+' unless + // Apply PostgreSQL trimming rule: an operator cannot end in '+' or '-' unless // it contains at least one of: ~ ! @ # % ^ & | ` ? idx_t end_pos = end; bool has_special = false; @@ -269,12 +303,12 @@ void Tokenizer::PushOperatorToken(TokenizerBehavior &behavior, idx_t start, idx_ } } if (!has_special) { - while (end_pos > start && sql[end_pos - 1] == '+') { + while (end_pos > start + 1 && (sql[end_pos - 1] == '+' || sql[end_pos - 1] == '-')) { end_pos--; } } behavior.PushToken(start, end_pos, TokenType::OPERATOR); - // Push any trimmed '+' characters as individual tokens + // Push any trimmed '+' or '-' characters as individual tokens for (idx_t pos = end_pos; pos < end; pos++) { tokens.emplace_back(string(1, sql[pos]), pos, TokenType::OPERATOR); } @@ -286,6 +320,7 @@ bool Tokenizer::TokenizeInputInternal(TokenizerBehavior &behavior) const { auto state = TokenizeState::STANDARD; idx_t last_pos = 0; bool escape_string = false; + char quoted_identifier_delimiter = '"'; string dollar_quote_marker; idx_t dollar_marker_start = 0; idx_t multi_line_comment_depth = 0; @@ -299,8 +334,9 @@ bool Tokenizer::TokenizeInputInternal(TokenizerBehavior &behavior) const { escape_string = false; break; } - if (c == '"') { + if (IsQuotedIdentifierDelimiter(c)) { state = TokenizeState::QUOTED_IDENTIFIER; + quoted_identifier_delimiter = c; last_pos = i; break; } @@ -365,16 +401,16 @@ bool Tokenizer::TokenizeInputInternal(TokenizerBehavior &behavior) const { last_pos = i + 1; break; } - idx_t op_len; - if (IsSpecialOperator(sql, i, op_len)) { - if (i + op_len < sql.size() && CharacterIsOperator(sql[i + op_len])) { + idx_t token_length; + if (IsCompoundColonToken(sql, i, token_length)) { + if (i + token_length < sql.size() && CharacterIsOperator(sql[i + token_length])) { state = TokenizeState::OPERATOR; last_pos = i; break; } - // special operator - push the special operator - tokens.emplace_back(sql.substr(i, op_len), last_pos, TokenType::OPERATOR); - i += op_len - 1; + // Push the compound colon token + tokens.emplace_back(sql.substr(i, token_length), last_pos, TokenType::OPERATOR); + i += token_length - 1; last_pos = i + 1; break; } @@ -411,8 +447,8 @@ bool Tokenizer::TokenizeInputInternal(TokenizerBehavior &behavior) const { last_pos = i; break; case TokenizeState::NUMERIC: - // Check for "always allowed" numeric characters - if (CharacterIsInitialNumber(c)) { + // Check for "always allowed" numeric characters - a number has at most one dot, and none in its exponent + if (CharacterIsInitialNumber(c) && (c != '.' || !NumberHasDotOrExponent(sql, last_pos, i))) { break; // Continue tokenizing } // Allow underscore only when immediately followed by a digit (no consecutive underscores) @@ -454,6 +490,13 @@ bool Tokenizer::TokenizeInputInternal(TokenizerBehavior &behavior) const { i--; break; case TokenizeState::OPERATOR: + if (c == '-' && i + 1 < sql.size() && sql[i + 1] == '-') { + PushOperatorToken(behavior, last_pos, i); + state = TokenizeState::STANDARD; + last_pos = i; + i--; + break; + } if (c == '/' && i + 1 < sql.size() && sql[i + 1] == '*') { PushOperatorToken(behavior, last_pos, i); // Go back to STANDARD state so it tokenizes as a block comment @@ -484,7 +527,7 @@ bool Tokenizer::TokenizeInputInternal(TokenizerBehavior &behavior) const { } break; case TokenizeState::STRING_LITERAL: - if (escape_string && c == '\\' && i + 1 < sql.size()) { + if ((escape_string || BackslashEscapesStringLiterals()) && c == '\\' && i + 1 < sql.size()) { i++; break; } @@ -501,8 +544,8 @@ bool Tokenizer::TokenizeInputInternal(TokenizerBehavior &behavior) const { } break; case TokenizeState::QUOTED_IDENTIFIER: - if (c == '"') { - if (i + 1 < sql.size() && sql[i + 1] == '"') { + if (c == quoted_identifier_delimiter) { + if (i + 1 < sql.size() && sql[i + 1] == quoted_identifier_delimiter) { // escaped - skip escape i++; } else { @@ -589,8 +632,7 @@ bool Tokenizer::TokenizeInputInternal(TokenizerBehavior &behavior) const { default: break; } - string last_word = sql.substr(last_pos, sql.size() - last_pos); - behavior.OnLastToken(*this, state, std::move(last_word), last_pos); + HandleLastToken(behavior, state, sql, last_pos); return true; } @@ -602,8 +644,8 @@ void TokenizerBehavior::OnLastToken(const Tokenizer &tokenizer, TokenizeState st if (last_word.empty()) { return; } - if (state == TokenizeState::KEYWORD) { - state = tokenizer.keyword_helper.IsKeyword(last_word) ? TokenizeState::KEYWORD : TokenizeState::STANDARD; + if (state == TokenizeState::KEYWORD && !tokenizer.keyword_helper.IsKeyword(last_word)) { + state = TokenizeState::STANDARD; } bool is_unterminated = Tokenizer::IsUnterminatedState(state); diff --git a/src/duckdb/src/parser/peg/tokenizer/parser_tokenizer.cpp b/src/duckdb/src/parser/peg/tokenizer/parser_tokenizer.cpp index 608a45e51..bdf8fcc50 100644 --- a/src/duckdb/src/parser/peg/tokenizer/parser_tokenizer.cpp +++ b/src/duckdb/src/parser/peg/tokenizer/parser_tokenizer.cpp @@ -4,7 +4,10 @@ namespace duckdb { static bool IsEmptyQuotedIdentifier(const string &sql, idx_t start, idx_t end, TokenType type) { - return type == TokenType::IDENTIFIER && end == start + 2 && sql.substr(start, 2) == "\"\""; + if (type != TokenType::IDENTIFIER || end != start + 2) { + return false; + } + return (sql[start] == '"' && sql[start + 1] == '"') || (sql[start] == '`' && sql[start + 1] == '`'); } ParserTokenizerBehavior::ParserTokenizerBehavior(const string &sql, vector &tokens) diff --git a/src/duckdb/src/parser/peg/transformer/peg_transformer.cpp b/src/duckdb/src/parser/peg/transformer/peg_transformer.cpp index 7f594a110..f2330f956 100644 --- a/src/duckdb/src/parser/peg/transformer/peg_transformer.cpp +++ b/src/duckdb/src/parser/peg/transformer/peg_transformer.cpp @@ -12,7 +12,7 @@ TransformStep TransformStep::Child(TransformInput input) { return TransformStep(input, nullptr); } -TransformStep TransformStep::Complete(unique_ptr result) { +TransformStep TransformStep::Complete(arena_ptr result) { D_ASSERT(result); return TransformStep(nullopt, std::move(result)); } @@ -21,7 +21,7 @@ optional TransformStep::GetChild() { return child; } -unique_ptr TransformStep::TakeResult() { +arena_ptr TransformStep::TakeResult() { D_ASSERT(!child); D_ASSERT(result); return std::move(result); @@ -29,7 +29,8 @@ unique_ptr TransformStep::TakeResult() { GeneratedTransformProcess::GeneratedTransformProcess(PEGTransformer &transformer_p, TransformInput input, const TransformFrameOps &info_p) - : parse_result(input.parse_result), info(info_p), transformer(transformer_p) { + : parse_result(input.parse_result), info(info_p), child_results(transformer_p.GetAllocator()), + transformer(transformer_p), pending_children(transformer_p.GetAllocator()) { if (!info.initialize || !info.finalize) { throw InternalException("Incomplete transformer process for rule '%s'", info.name); } @@ -38,9 +39,11 @@ GeneratedTransformProcess::GeneratedTransformProcess(PEGTransformer &transformer void GeneratedTransformProcess::ReserveChildSlots(idx_t count) { child_results.resize(count); + // a rule pushes at most one child per slot, so the pending list never has to grow either + pending_children.reserve(count); } -void GeneratedTransformProcess::SetChildResult(idx_t slot, unique_ptr result) { +void GeneratedTransformProcess::SetChildResult(idx_t slot, arena_ptr result) { if (slot >= child_results.size()) { throw InternalException("Invalid transformer result slot %llu for rule '%s'", slot, info.name); } @@ -84,30 +87,30 @@ FinalizeTransformProcess::FinalizeTransformProcess(PEGTransformer &transformer_p : transformer(transformer_p), parse_result(parse_result_p), finalize(std::move(finalize_p)) { } -TransformStep FinalizeTransformProcess::Resume(unique_ptr child_result) { +TransformStep FinalizeTransformProcess::Resume(arena_ptr child_result) { D_ASSERT(!completed); D_ASSERT(!child_result); auto result = finalize(transformer, parse_result); if (!result) { - throw InternalException("Transformer for rule '%s' returned a nullptr", parse_result.name); + throw InternalException("Transformer for rule '%s' returned a nullptr", parse_result.Name()); } completed = true; return TransformStep::Complete(std::move(result)); } -unique_ptr CompiledGrammarRule::StartTransform(PEGTransformer &transformer, - ParseResult &parse_result) const { +arena_ptr CompiledGrammarRule::StartTransform(PEGTransformer &transformer, + ParseResult &parse_result) const { if (!transform_process) { - throw NotImplementedException("No transform process found for rule '%s'", parse_result.name); + throw NotImplementedException("No transform process found for rule '%s'", parse_result.Name()); } auto result = transform_process(transformer, parse_result); if (!result) { - throw InternalException("Transform process factory for rule '%s' returned a nullptr", parse_result.name); + throw InternalException("Transform process factory for rule '%s' returned a nullptr", parse_result.Name()); } return result; } -TransformStep GeneratedTransformProcess::Resume(unique_ptr child_result) { +TransformStep GeneratedTransformProcess::Resume(arena_ptr child_result) { D_ASSERT(!completed); D_ASSERT(child_result_slot.IsValid() == bool(child_result)); if (child_result) { @@ -124,18 +127,39 @@ TransformStackFrame::TransformStackFrame(TransformInput input) TransformStack::TransformStack(PEGTransformer &transformer_p) : transformer(transformer_p) { } +TransformStack::~TransformStack() { + // release the expression depth of frames that remain after an exception + while (!frames.empty()) { + PopFrame(); + } +} + void TransformStack::PushFrame(TransformInput input) { frames.emplace(input); + auto &frame = frames.top(); + auto weight = transformer.ExpressionDepthWeight(frame.rule, frame.parse_result); + if (weight) { + // recorded first: the frame is released (and the weight subtracted) when entering throws + frame.expression_depth_weight = weight; + transformer.EnterExpression(weight); + } +} + +void TransformStack::PopFrame() { + if (frames.top().expression_depth_weight) { + transformer.ExitExpression(frames.top().expression_depth_weight); + } + frames.pop(); } void TransformStack::InitializeFrame(TransformStackFrame &frame) { if (!frame.rule) { - throw InternalException("No registered data exists for rule '%s'", frame.parse_result.name); + throw InternalException("No registered data exists for rule '%s'", frame.parse_result.Name()); } frame.process = frame.rule->StartTransform(transformer, frame.parse_result); } -unique_ptr TransformStack::ExecuteFrame(TransformStackFrame &frame) { +arena_ptr TransformStack::ExecuteFrame(TransformStackFrame &frame) { if (!frame.process) { InitializeFrame(frame); } @@ -149,10 +173,10 @@ unique_ptr TransformStack::ExecuteFrame(TransformStackFram return nullptr; } -unique_ptr TransformStack::Execute(TransformInput input) { +arena_ptr TransformStack::Execute(TransformInput input) { D_ASSERT(frames.empty()); if (!input.GetRule()) { - throw InternalException("No registered data exists for rule '%s'", input.parse_result.name); + throw InternalException("No registered data exists for rule '%s'", input.parse_result.Name()); } PushFrame(input); while (!frames.empty()) { @@ -162,7 +186,7 @@ unique_ptr TransformStack::Execute(TransformInput input) { continue; } transformer.SetResultLocation(frame.parse_result, *result); - frames.pop(); + PopFrame(); if (frames.empty()) { return result; } @@ -181,7 +205,7 @@ string TransformStack::FormatStack() const { result << "\n"; } auto &parse_result = frames[i].parse_result; - result << "#" << i << " " << parse_result.name; + result << "#" << i << " " << parse_result.Name(); if (parse_result.offset.IsValid()) { result << " offset=" << parse_result.offset.GetIndex(); } @@ -190,36 +214,67 @@ string TransformStack::FormatStack() const { } #endif -unique_ptr PEGTransformer::ExecuteRecursive(TransformInput input) { - auto rule = input.GetRule(); +arena_ptr PEGTransformer::TransformInternal(ParseResult &parse_result) { + auto rule = parse_result.GetRule(); if (!rule) { - throw InternalException("No registered data exists for rule '%s'", input.parse_result.name); - } - auto process = rule->StartTransform(*this, input.parse_result); - unique_ptr child_result; - while (true) { - auto step = process->Resume(std::move(child_result)); - auto child = step.GetChild(); - if (!child) { - auto result = step.TakeResult(); - SetResultLocation(input.parse_result, *result); - return result; + throw InternalException("No registered data exists for rule '%s'", parse_result.Name()); + } + TransformInput input {*rule, parse_result}; + TransformStack stack(*this); + return stack.Execute(input); +} + +//! The number of repeated elements directly in a rule's match: the links of an operator chain +static idx_t CountChainLinks(ParseResult &parse_result) { + switch (parse_result.type) { + case ParseResultType::REPEAT: + return parse_result.Cast().GetChildren().size(); + case ParseResultType::OPTIONAL: { + auto &optional = parse_result.Cast(); + return optional.HasResult() ? CountChainLinks(optional.GetResultUnsafe()) : 0; + } + case ParseResultType::LIST: { + idx_t links = 0; + for (auto &child : parse_result.Cast().GetChildren()) { + auto type = child.get().type; + if (type == ParseResultType::REPEAT || type == ParseResultType::OPTIONAL) { + links += CountChainLinks(child.get()); + } } - child_result = ExecuteRecursive(*child); + return links; + } + default: + return 0; } } -unique_ptr PEGTransformer::TransformInternal(ParseResult &parse_result) { - auto rule = parse_result.GetRule(); +idx_t PEGTransformer::ExpressionDepthWeight(optional_ptr rule, ParseResult &parse_result) { if (!rule) { - throw InternalException("No registered data exists for rule '%s'", parse_result.name); + return 0; } - TransformInput input {*rule, parse_result}; - if (options.heap_based_parser) { - TransformStack stack(*this); - return stack.Execute(input); + switch (rule->expression_depth) { + case ExpressionDepthKind::NESTING: + return 1; + case ExpressionDepthKind::CHAIN: + return CountChainLinks(parse_result); + default: + return 0; } - return ExecuteRecursive(input); +} + +void PEGTransformer::EnterExpression(idx_t weight) { + // the transformer does not recurse, but binding the resulting expressions does - reject them early + expression_depth += weight; + if (expression_depth > options.max_expression_depth) { + throw ParserException("Max expression depth limit of %lld exceeded. Use \"SET max_expression_depth TO x\" to " + "increase the maximum expression depth.", + options.max_expression_depth); + } +} + +void PEGTransformer::ExitExpression(idx_t weight) { + D_ASSERT(expression_depth >= weight); + expression_depth -= weight; } const CompiledGrammarRule &PEGTransformer::GetRule(const string &rule_name) const { @@ -290,6 +345,7 @@ void PEGTransformer::Clear() { ClearParameters(); pivot_entries.clear(); stored_cte_map.clear(); + window_clauses.clear(); } idx_t PEGTransformer::ParamCount() const { @@ -390,9 +446,13 @@ bool PEGTransformer::IsWindowFrameDefault(WindowBoundary start, WindowBoundary e } unique_ptr PEGTransformer::GetWindowClause(const Identifier &window_name) { - auto it = window_clauses.find(window_name); - if (it == window_clauses.end()) { - throw ParserException("window \"%s\" does not exist", window_name); + if (window_clauses.empty()) { + throw ParserException("window %s does not exist", window_name); + } + auto ¤t_windows = window_clauses.back(); + auto it = current_windows.find(window_name); + if (it == current_windows.end()) { + throw ParserException("window %s does not exist", window_name); } return unique_ptr_cast(it->second->Copy()); } diff --git a/src/duckdb/src/parser/peg/transformer/peg_transformer_factory.cpp b/src/duckdb/src/parser/peg/transformer/peg_transformer_factory.cpp index 4ffb71a24..cc0ac5f8f 100644 --- a/src/duckdb/src/parser/peg/transformer/peg_transformer_factory.cpp +++ b/src/duckdb/src/parser/peg/transformer/peg_transformer_factory.cpp @@ -14,25 +14,14 @@ #include "duckdb/common/enums/subquery_type.hpp" #include "duckdb/common/exception/conversion_exception.hpp" #include "duckdb/parser/expression/cast_expression.hpp" +#include "duckdb/catalog/default/default_types.hpp" +#include "duckdb/parser/expression/type_expression.hpp" #include "duckdb/parser/query_node/set_operation_node.hpp" #include "duckdb/parser/statement/merge_into_statement.hpp" #include "duckdb/parser/constraints/foreign_key_constraint.hpp" namespace duckdb { -unique_ptr PEGTransformerFactory::TransformStatement(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - if (!transformer.named_parameter_map.empty()) { - // Avoid overriding a previous move with nothing - result->named_param_map = transformer.named_parameter_map; - } - result->has_anonymous_parameters = transformer.has_anonymous_parameters; - return result; -} - static unique_ptr ExtractAndTransformStatement(PEGTransformer &transformer, const TokenIterator &token_iterator, ParseResult &stmt_pr, optional_idx terminator_offset) { @@ -66,12 +55,13 @@ unique_ptr PEGTransformerFactory::TransformTopLevelStatement(Token } vector suggestions; ParseResultAllocator parse_result_allocator; - ParserPackratCache packrat_cache; idx_t max_token_index = token_iterator.Position(); + // the packrat cache outlives the match processes, so they need separate arenas ArenaAllocator process_allocator(Allocator::DefaultAllocator()); + ArenaAllocator packrat_allocator(Allocator::DefaultAllocator()); + ParserPackratCache packrat_cache(packrat_allocator); MatchContext match_context(suggestions, parse_result_allocator, process_allocator, max_token_index, - MatchMode::BUILD_PARSE_RESULT, options.identifier_case_mode, options.heap_based_parser, - &packrat_cache); + MatchMode::BUILD_PARSE_RESULT, options.identifier_case_mode, &packrat_cache); MatchState state(token_iterator, match_context); auto match_result = grammar.TopLevelStatementMatcher().MatchParseResult(state); process_allocator.FreeAll(); @@ -123,72 +113,77 @@ unique_ptr PEGTransformerFactory::TransformTopLevelStatement(Token return ExtractAndTransformStatement(transformer, token_iterator, stmt_opt.GetResult(), terminator_offset); } -#define REGISTER_TRANSFORM(FUNCTION) Register(string(#FUNCTION).substr(9), &FUNCTION) - -void PEGTransformerFactory::RegisterCommon() { - // common.gram - REGISTER_TRANSFORM(TransformNumberLiteral); - REGISTER_TRANSFORM(TransformStringLiteral); - REGISTER_TRANSFORM(TransformIntervalToIntervalAsType); -} - -void PEGTransformerFactory::RegisterCreateTable() { - // create_table.gram - REGISTER_TRANSFORM(TransformIdentifier); -} - -void PEGTransformerFactory::RegisterExpression() { - // expression.gram - REGISTER_TRANSFORM(TransformPrefixExpression); - REGISTER_TRANSFORM(TransformOverClause); -} - -void PEGTransformerFactory::RegisterPivot() { - // PivotStatement and UnpivotStatement measure parameter usage while transforming - // the source table, so their top-level wrappers remain manual. - REGISTER_TRANSFORM(TransformPivotStatement); - REGISTER_TRANSFORM(TransformUnpivotStatement); -} - -void PEGTransformerFactory::RegisterSelect() { - // select.gram rules that remain manual after generated wrappers are registered. - Register("SelectStatementInternal", &TransformSelectStatementInternalRule); - REGISTER_TRANSFORM(TransformSimpleSelect); - REGISTER_TRANSFORM(TransformTableRef); - REGISTER_TRANSFORM(TransformWithClause); - REGISTER_TRANSFORM(TransformWindowDefinition); -} - -void PEGTransformerFactory::RegisterKeywordsAndIdentifiers() { - Register("PragmaName", &TransformIdentifierOrKeyword); - Register("TypeName", &TransformIdentifierOrKeyword); - Register("PlainIdentifier", &TransformIdentifierOrKeyword); - Register("QuotedIdentifier", &TransformIdentifierOrKeyword); - Register("ReservedKeyword", &TransformIdentifierOrKeyword); - Register("UnreservedKeyword", &TransformIdentifierOrKeyword); - Register("ColumnNameKeyword", &TransformIdentifierOrKeyword); - Register("FuncNameKeyword", &TransformIdentifierOrKeyword); - Register("TypeNameKeyword", &TransformIdentifierOrKeyword); - Register("SettingName", &TransformIdentifierOrKeyword); -} - PEGTransformerFactory::PEGTransformerFactory(ParsedGrammar &grammar_p) : grammar(grammar_p) { - RegisterGenerated(); - REGISTER_TRANSFORM(TransformStatement); - RegisterCommon(); - RegisterCreateTable(); - RegisterExpression(); - RegisterPivot(); - RegisterSelect(); - RegisterKeywordsAndIdentifiers(); + case_insensitive_set_t collapsible_rules; + //===--------------------------------------------------------------------===// + // START GENERATED COLLAPSIBLE RULES + //===--------------------------------------------------------------------===// + collapsible_rules.insert("Expression"); + collapsible_rules.insert("ColumnDefaultExpr"); + collapsible_rules.insert("LambdaArrowExpression"); + collapsible_rules.insert("LogicalOrExpression"); + collapsible_rules.insert("ColDefOrExpr"); + collapsible_rules.insert("LogicalAndExpression"); + collapsible_rules.insert("ColDefAndExpr"); + collapsible_rules.insert("LogicalNotExpression"); + collapsible_rules.insert("IsExpression"); + collapsible_rules.insert("IsDistinctFromExpression"); + collapsible_rules.insert("ComparisonExpression"); + collapsible_rules.insert("BetweenInLikeExpression"); + collapsible_rules.insert("OtherOperatorExpression"); + collapsible_rules.insert("InfixOtherOperatorExpression"); + collapsible_rules.insert("BitwiseExpression"); + collapsible_rules.insert("AdditiveExpression"); + collapsible_rules.insert("MultiplicativeExpression"); + collapsible_rules.insert("ExponentiationExpression"); + collapsible_rules.insert("CollateExpression"); + collapsible_rules.insert("AtTimeZoneExpression"); + collapsible_rules.insert("PrefixExpression"); + collapsible_rules.insert("BaseExpression"); + collapsible_rules.insert("SelectSetOpChain"); + collapsible_rules.insert("IntersectChain"); + collapsible_rules.insert("TableRef"); + //===--------------------------------------------------------------------===// + // END GENERATED COLLAPSIBLE RULES + //===--------------------------------------------------------------------===// + for (auto &entry : GeneratedTransformFrameOps()) { auto process_info = entry.second; grammar.SetTransformProcess( entry.first, - [process_info](PEGTransformer &transformer, ParseResult &parse_result) -> unique_ptr { - return make_uniq(transformer, TransformInput {parse_result}, *process_info); - }); + [process_info](PEGTransformer &transformer, ParseResult &parse_result) -> arena_ptr { + return transformer.MakeProcess(transformer, TransformInput {parse_result}, + *process_info); + }, + collapsible_rules.count(entry.first) > 0); } + //===--------------------------------------------------------------------===// + // START GENERATED EXPRESSION DEPTH RULES + //===--------------------------------------------------------------------===// + grammar.SetExpressionDepth("SingleExpression", ExpressionDepthKind::NESTING); + grammar.SetExpressionDepth("Type", ExpressionDepthKind::NESTING); + grammar.SetExpressionDepth("NestedType", ExpressionDepthKind::NESTING); + grammar.SetExpressionDepth("LambdaArrowExpression", ExpressionDepthKind::CHAIN); + grammar.SetExpressionDepth("LogicalOrExpression", ExpressionDepthKind::CHAIN); + grammar.SetExpressionDepth("LogicalAndExpression", ExpressionDepthKind::CHAIN); + grammar.SetExpressionDepth("NotExpression", ExpressionDepthKind::CHAIN); + grammar.SetExpressionDepth("IsExpressionContinuation", ExpressionDepthKind::CHAIN); + grammar.SetExpressionDepth("IsDistinctFromExpression", ExpressionDepthKind::CHAIN); + grammar.SetExpressionDepth("ComparisonExpression", ExpressionDepthKind::CHAIN); + grammar.SetExpressionDepth("BetweenInLikeExpression", ExpressionDepthKind::CHAIN); + grammar.SetExpressionDepth("InfixOtherOperatorExpression", ExpressionDepthKind::CHAIN); + grammar.SetExpressionDepth("BitwiseExpression", ExpressionDepthKind::CHAIN); + grammar.SetExpressionDepth("TildeExpression", ExpressionDepthKind::CHAIN); + grammar.SetExpressionDepth("AdditiveExpression", ExpressionDepthKind::CHAIN); + grammar.SetExpressionDepth("MultiplicativeExpression", ExpressionDepthKind::CHAIN); + grammar.SetExpressionDepth("ExponentiationExpression", ExpressionDepthKind::CHAIN); + grammar.SetExpressionDepth("CollateExpression", ExpressionDepthKind::CHAIN); + grammar.SetExpressionDepth("AtTimeZoneExpression", ExpressionDepthKind::CHAIN); + grammar.SetExpressionDepth("PrefixExpression", ExpressionDepthKind::CHAIN); + grammar.SetExpressionDepth("IndirectionList", ExpressionDepthKind::CHAIN); + //===--------------------------------------------------------------------===// + // END GENERATED EXPRESSION DEPTH RULES + //===--------------------------------------------------------------------===// } void PEGTransformerFactory::RegisterDefaultTransforms(ParsedGrammar &grammar) { @@ -222,10 +217,19 @@ bool PEGTransformerFactory::ExpressionIsEmptyStar(const ParsedExpression &expr) return false; } auto &star = expr.Cast(); - if (!star.IsColumns() && star.ExcludeList().empty() && star.ReplaceList().empty()) { - return true; + if (star.IsColumns()) { + return false; + } + if (!star.ExcludeList().empty()) { + return false; + } + if (!star.ReplaceList().empty()) { + return false; } - return false; + if (!star.RenameList().empty()) { + return false; + } + return true; } QualifiedName PEGTransformerFactory::StringToQualifiedName(vector input) { @@ -293,4 +297,25 @@ LogicalType PEGTransformerFactory::GetIntervalTargetType(DatePartSpecifier date_ } } +LogicalType PEGTransformerFactory::ApplyColumnCollation(const optional &type, + unique_ptr collation) { + if (!type) { + throw ParserException("Specify the VARCHAR type to set a collation"); + } + if (!type->IsUnbound()) { + throw InternalException("Expected only unbound types here"); + } + auto &expr = UnboundType::GetTypeExpression(*type); + if (expr->GetExpressionClass() != ExpressionClass::TYPE) { + throw InternalException("Expected a type expression"); + } + auto &type_expr = expr->Cast(); + if (DefaultTypeGenerator::GetDefaultType(type_expr.GetTypeName()) != LogicalTypeId::VARCHAR) { + throw ParserException("Only VARCHAR columns can have collations!"); + } + vector> type_children; + type_children.push_back(std::move(collation)); + return LogicalType::UNBOUND(make_uniq(Identifier("VARCHAR"), std::move(type_children))); +} + } // namespace duckdb diff --git a/src/duckdb/src/parser/peg/transformer/transform_alter.cpp b/src/duckdb/src/parser/peg/transformer/transform_alter.cpp index 022f370d7..347ba2b24 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_alter.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_alter.cpp @@ -1,3 +1,4 @@ +#include "duckdb/parser/constraints/unique_constraint.hpp" #include "duckdb/parser/peg/ast/add_column_entry.hpp" #include "duckdb/parser/expression/columnref_expression.hpp" #include "duckdb/parser/peg/ast/column_constraint_entry.hpp" @@ -7,6 +8,7 @@ #include "duckdb/parser/parsed_data/alter_table_info.hpp" #include "duckdb/parser/expression/cast_expression.hpp" #include "duckdb/parser/parsed_data/alter_database_info.hpp" +#include "duckdb/parser/parsed_data/alter_schema_info.hpp" #include "duckdb/parser/statement/multi_statement.hpp" #include "duckdb/parser/statement/update_statement.hpp" #include "duckdb/parser/query_node/update_query_node.hpp" @@ -40,17 +42,20 @@ unique_ptr PEGTransformerFactory::TransformAlterStatement(PEGTrans } auto &add_column = alter_table.Cast(); auto &column_entry = add_column.new_column; - const bool add_not_null = add_column.add_not_null; + const auto follow_ups = add_column.add_column_constraints; const bool materialize_default = column_entry.HasDefaultValue() && !IsSimpleDefaultValue(column_entry.DefaultValue()); - if (!add_not_null && !materialize_default) { + if (!follow_ups.add_not_null && !follow_ups.add_unique && !materialize_default) { return std::move(result); } if (add_column.if_column_not_exists) { - if (add_not_null) { + if (follow_ups.add_not_null) { throw NotImplementedException("Adding a NOT NULL column with IF NOT EXISTS is not supported"); } + if (follow_ups.add_unique) { + throw NotImplementedException("Adding a UNIQUE column with IF NOT EXISTS is not supported"); + } // IF NOT EXISTS is not supported by the multi-statement rewrite - keep the plain ALTER return std::move(result); } @@ -64,15 +69,23 @@ unique_ptr PEGTransformerFactory::TransformAlterStatement(PEGTrans multi_statement = TransformAndMaterializeAlter( alter_entry_data, make_uniq(add_column.GetAlterEntryData(), std::move(null_column), - add_column.if_column_not_exists, /*add_not_null=*/false), + add_column.if_column_not_exists, AddColumnConstraints()), column_name.GetIdentifierName(), column_entry.DefaultValue().Copy()); } else { multi_statement = make_uniq(); - add_column.add_not_null = false; + add_column.add_column_constraints = AddColumnConstraints(); AddToMultiStatement(multi_statement, std::move(result->info)); } - if (add_not_null) { - AddToMultiStatement(multi_statement, make_uniq(alter_entry_data, column_name)); + if (follow_ups.add_not_null) { + AddToMultiStatement(multi_statement, + make_uniq(alter_entry_data, vector {column_name})); + } + if (follow_ups.add_unique) { + vector unique_columns; + unique_columns.push_back(column_name); + auto unique_constraint = make_uniq(std::move(unique_columns), /*is_primary_key=*/false); + AddToMultiStatement(multi_statement, + make_uniq(alter_entry_data, std::move(unique_constraint))); } return std::move(multi_statement); } @@ -116,8 +129,22 @@ unique_ptr PEGTransformerFactory::TransformAlterViewStmt(PEGTransform unique_ptr PEGTransformerFactory::TransformAlterSchemaStmt(PEGTransformer &transformer, const optional &if_exists, const QualifiedName &qualified_name, - unique_ptr rename_alter) { - throw NotImplementedException("Altering schemas is not yet supported"); + unique_ptr alter_schema_options) { + // the new schema is stored in the Schema() slot, matching the layout used by CreateSchemaInfo + auto if_not_found = if_exists ? OnEntryNotFound::RETURN_NULL : OnEntryNotFound::THROW_EXCEPTION; + AlterEntryData data(QualifiedName(qualified_name.Path(), Identifier()), if_not_found); + switch (alter_schema_options->alter_table_type) { + case AlterTableType::SET_TABLE_OPTIONS: { + auto &set_options = alter_schema_options->Cast(); + return make_uniq(data, std::move(set_options.table_options)); + } + case AlterTableType::RESET_TABLE_OPTIONS: { + auto &reset_options = alter_schema_options->Cast(); + return make_uniq(data, std::move(reset_options.table_options)); + } + default: + throw NotImplementedException("Altering schemas is not yet supported"); + } } unique_ptr PEGTransformerFactory::TransformAlterSequenceStmt(PEGTransformer &transformer, @@ -149,7 +176,7 @@ QualifiedName PEGTransformerFactory::TransformQualifiedSequenceName(PEGTransform unique_ptr PEGTransformerFactory::TransformRenameAlterSequenceOptions(PEGTransformer &transformer, unique_ptr rename_alter) { - return std::move(rename_alter); + throw NotImplementedException("Renaming sequences is not yet supported"); } unique_ptr @@ -230,8 +257,8 @@ unique_ptr PEGTransformerFactory::TransformAndMaterializeAlter( // 3. `ALTER TABLE t ALTER u SET DEFAULT ;` // Reinstate the original default expression. - AddToMultiStatement(multi_statement, - make_uniq(data, Identifier(column_name), std::move(expression))); + AddToMultiStatement(multi_statement, make_uniq(data, vector {Identifier(column_name)}, + std::move(expression))); return multi_statement; } @@ -244,17 +271,24 @@ unique_ptr PEGTransformerFactory::TransformAddColumn(PEGTransfor if (add_column_entry.default_value) { column_definition.SetDefaultValue(std::move(add_column_entry.default_value)); } + column_definition.SetCompressionType(add_column_entry.compression_type); unique_ptr result; auto if_not_exists_value = if_not_exists.has_value(); if (add_column_entry.column_path.size() == 1) { result = make_uniq(AlterEntryData(), std::move(column_definition), if_not_exists_value, - add_column_entry.is_not_null); + add_column_entry.add_column_constraints); } else { - if (add_column_entry.is_not_null) { + if (add_column_entry.add_column_constraints.add_not_null) { throw NotImplementedException("Adding NOT NULL constraints to nested fields is not supported"); } + if (add_column_entry.add_column_constraints.add_unique) { + throw NotImplementedException("Adding UNIQUE constraints to nested fields is not supported"); + } + if (add_column_entry.compression_type != CompressionType::COMPRESSION_AUTO) { + throw NotImplementedException("Adding compression to nested fields is not supported"); + } const auto parent_path = vector(add_column_entry.column_path.begin(), add_column_entry.column_path.end() - 1); result = @@ -290,14 +324,25 @@ AddColumnEntry PEGTransformerFactory::TransformAddColumnEntry( } new_column.default_value = std::move(constraint.expression); } else if (constraint_type == ConstraintType::NOT_NULL) { - new_column.is_not_null = true; + new_column.add_column_constraints.add_not_null = true; + } else if (constraint_type == ConstraintType::UNIQUE && constraint.constraint_type_info.is_primary_key) { + throw ParserException("Adding columns with PRIMARY KEY constraints is not supported yet"); + } else if (constraint_type == ConstraintType::UNIQUE && + constraint.constraint_type_info.check_mode != ConstraintCheckMode::DEFAULT) { + throw ParserException("Adding columns with UNIQUE constraints with a check mode is not supported yet"); } else if (constraint_type == ConstraintType::UNIQUE) { - throw ParserException("Adding columns with %s constraints is not supported yet", - constraint.constraint_type_info.is_primary_key ? "PRIMARY KEY" : "UNIQUE"); + new_column.add_column_constraints.add_unique = true; } else if (constraint_type == ConstraintType::CHECK) { throw ParserException("Adding columns with CHECK constraints is not supported yet"); } else if (constraint_type == ConstraintType::FOREIGN_KEY) { throw ParserException("Adding columns with FOREIGN KEY constraints is not supported yet"); + } else if (constraint.constraint_name == "ColumnCompression") { + new_column.compression_type = constraint.compression_type; + if (new_column.compression_type == CompressionType::COMPRESSION_AUTO) { + throw ParserException("Unrecognized option for column compression"); + } + } else if (constraint.constraint_name == "ColumnCollation") { + new_column.type = ApplyColumnCollation(new_column.type, std::move(constraint.expression)); } } } @@ -331,26 +376,23 @@ unique_ptr PEGTransformerFactory::TransformAlterColumn(PEGTransformer &transformer, const bool &has_result, unique_ptr nested_column_name, unique_ptr alter_column_entry) { + //! Preserved so DuckTableEntry can detect a nested-field target; not interpreted here. + auto column_path = nested_column_name->ColumnNames(); if (alter_column_entry->alter_table_type == AlterTableType::SET_DEFAULT) { auto set_default_entry = unique_ptr_cast(std::move(alter_column_entry)); - // TODO(Dtenwolde) Figure out with nested names; - set_default_entry->column_name = nested_column_name->ColumnNames()[0]; + set_default_entry->column_path = column_path; return std::move(set_default_entry); } else if (alter_column_entry->alter_table_type == AlterTableType::DROP_NOT_NULL) { auto drop_not_null = unique_ptr_cast(std::move(alter_column_entry)); - drop_not_null->column_name = nested_column_name->ColumnNames()[0]; + drop_not_null->column_path = column_path; return std::move(drop_not_null); } else if (alter_column_entry->alter_table_type == AlterTableType::SET_NOT_NULL) { auto set_not_null = unique_ptr_cast(std::move(alter_column_entry)); - set_not_null->column_name = nested_column_name->ColumnNames()[0]; + set_not_null->column_path = column_path; return std::move(set_not_null); } else if (alter_column_entry->alter_table_type == AlterTableType::ALTER_COLUMN_TYPE) { auto change_column_type = unique_ptr_cast(std::move(alter_column_entry)); - change_column_type->column_name = nested_column_name->ColumnNames()[0]; - if (!change_column_type->expression) { - change_column_type->expression = - make_uniq(change_column_type->target_type, std::move(nested_column_name)); - } + change_column_type->column_path = column_path; return std::move(change_column_type); } else { throw NotImplementedException("Unrecognized type for alter column encountered"); @@ -358,31 +400,33 @@ PEGTransformerFactory::TransformAlterColumn(PEGTransformer &transformer, const b } unique_ptr PEGTransformerFactory::TransformDropDefault(PEGTransformer &transformer) { - return make_uniq(AlterEntryData(), "", nullptr); + return make_uniq(AlterEntryData(), vector {}, nullptr); } unique_ptr PEGTransformerFactory::TransformChangeNullability(PEGTransformer &transformer, const string &drop_or_set) { if (StringUtil::CIEquals(drop_or_set, "drop")) { - return make_uniq(AlterEntryData(), ""); + return make_uniq(AlterEntryData(), vector {}); } else { - return make_uniq(AlterEntryData(), ""); + return make_uniq(AlterEntryData(), vector {}); } } -unique_ptr -PEGTransformerFactory::TransformAlterType(PEGTransformer &transformer, const bool &has_result, - const optional &type, - optional> using_expression) { +unique_ptr PEGTransformerFactory::TransformAlterType( + PEGTransformer &transformer, const bool &has_result, const optional &type, + optional column_collation, optional> using_expression) { if (!type && !using_expression) { throw ParserException("Omitting the type is only possible in combination with USING"); } auto alter_type = type ? *type : LogicalType::UNKNOWN; + if (column_collation) { + alter_type = ApplyColumnCollation(type, std::move(column_collation->expression)); + } unique_ptr expression; if (using_expression) { expression = std::move(*using_expression); } - return make_uniq(AlterEntryData(), "", alter_type, std::move(expression)); + return make_uniq(AlterEntryData(), vector {}, alter_type, std::move(expression)); } unique_ptr PEGTransformerFactory::TransformUsingExpression(PEGTransformer &transformer, @@ -392,7 +436,7 @@ unique_ptr PEGTransformerFactory::TransformUsingExpression(PEG unique_ptr PEGTransformerFactory::TransformAddDefault(PEGTransformer &transformer, unique_ptr expression) { - return make_uniq(AlterEntryData(), "", std::move(expression)); + return make_uniq(AlterEntryData(), vector {}, std::move(expression)); } unique_ptr diff --git a/src/duckdb/src/parser/peg/transformer/transform_comment.cpp b/src/duckdb/src/parser/peg/transformer/transform_comment.cpp index 5dbafc09c..f5cbe424b 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_comment.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_comment.cpp @@ -4,9 +4,15 @@ namespace duckdb { +vector +PEGTransformerFactory::TransformCommentStringLiteralIdentifier(PEGTransformer &transformer, + const Identifier &string_literal_identifier) { + return {string_literal_identifier.GetIdentifierName()}; +} + unique_ptr PEGTransformerFactory::TransformCommentStatement(PEGTransformer &transformer, const CatalogType &comment_on_type, - const vector &dotted_identifier, + const vector &comment_target, const Value &comment_value) { auto result = make_uniq(); unique_ptr info; @@ -14,7 +20,7 @@ unique_ptr PEGTransformerFactory::TransformCommentStatement(PEGTra Identifier column_name; if (comment_on_type == CatalogType::INVALID) { // Column type returned - auto identifier = dotted_identifier; + auto identifier = comment_target; column_name = Identifier(identifier.back()); identifier.pop_back(); if (identifier.empty()) { @@ -28,7 +34,7 @@ unique_ptr PEGTransformerFactory::TransformCommentStatement(PEGTra } else if (comment_on_type == CatalogType::SCHEMA_ENTRY) { throw NotImplementedException("Adding comments to schemas is not implemented"); } else { - auto qualified_name = StringToQualifiedName(dotted_identifier); + auto qualified_name = StringToQualifiedName(comment_target); info = make_uniq(comment_on_type, qualified_name.Catalog(), qualified_name.Schema(), qualified_name.Name(), comment_value, OnEntryNotFound::THROW_EXCEPTION); } diff --git a/src/duckdb/src/parser/peg/transformer/transform_common.cpp b/src/duckdb/src/parser/peg/transformer/transform_common.cpp index bfb6439f8..b466696ab 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_common.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_common.cpp @@ -1,4 +1,4 @@ -#include "duckdb/common/extra_type_info.hpp" +#include "duckdb/common/logical_type_info.hpp" #include "duckdb/common/enums/date_part_specifier.hpp" #include "duckdb/common/operator/cast_operators.hpp" #include "duckdb/parser/peg/transformer/peg_transformer.hpp" @@ -53,6 +53,12 @@ string PEGTransformerFactory::TransformIdentifierOrKeyword(PEGTransformer &trans LogicalType PEGTransformerFactory::TransformType(PEGTransformer &transformer, unique_ptr type_variations, const optional> &array_bounds) { + return LogicalType::UNBOUND(TransformNestedType(transformer, std::move(type_variations), array_bounds)); +} + +unique_ptr PEGTransformerFactory::TransformNestedType(PEGTransformer &transformer, + unique_ptr type_variations, + const optional> &array_bounds) { auto array_depth_guard = transformer.StackCheck(array_bounds ? array_bounds->size() : 0); auto type = std::move(type_variations); if (array_bounds) { @@ -68,7 +74,7 @@ LogicalType PEGTransformerFactory::TransformType(PEGTransformer &transformer, } } } - return LogicalType::UNBOUND(std::move(type)); + return type; } int64_t PEGTransformerFactory::TransformArrayKeyword(PEGTransformer &transformer) { @@ -132,6 +138,9 @@ PEGTransformerFactory::TransformTimeType(PEGTransformer &transformer, const Logi return make_uniq(Identifier(EnumUtil::ToString(LogicalType::TIMESTAMP)), vector> {}); } + if (with_timezone) { + throw ParserException("TIMESTAMP WITH TIME ZONE does not take a precision"); + } if (modifiers.size() > 1) { throw ParserException("TIMESTAMP only supports a single modifier"); } @@ -316,36 +325,29 @@ QualifiedName PEGTransformerFactory::TransformCatalogReservedSchemaTypeName( return QualifiedName(std::move(qualification), reserved_type_name); } -unique_ptr PEGTransformerFactory::TransformMapType(PEGTransformer &transformer, - const optional> &type) { +unique_ptr +PEGTransformerFactory::TransformMapType(PEGTransformer &transformer, + optional>> nested_type) { vector> map_children; - if (type) { - for (auto &child_type : *type) { - map_children.push_back(UnboundType::GetTypeExpression(child_type)->Copy()); - } + if (nested_type) { + map_children = std::move(*nested_type); } return make_uniq(Identifier("MAP"), std::move(map_children)); } -unique_ptr PEGTransformerFactory::TransformTupleType(PEGTransformer &transformer, - const vector &type) { - vector> tuple_children; - for (auto &child : type) { - tuple_children.push_back(UnboundType::GetTypeExpression(child)->Copy()); - } - return make_uniq(Identifier("TUPLE"), std::move(tuple_children)); +unique_ptr +PEGTransformerFactory::TransformTupleType(PEGTransformer &transformer, + vector> nested_type) { + return make_uniq(Identifier("TUPLE"), std::move(nested_type)); } -unique_ptr -PEGTransformerFactory::TransformRowType(PEGTransformer &transformer, - const optional> &col_id_type_list) { +unique_ptr PEGTransformerFactory::TransformRowType( + PEGTransformer &transformer, optional>>> col_id_type_list) { vector> struct_children; if (col_id_type_list) { for (auto &child : *col_id_type_list) { - auto &type_expr = UnboundType::GetTypeExpression(child.second); - auto new_type_expr = type_expr->Copy(); - new_type_expr->SetAlias(child.first); - struct_children.push_back(std::move(new_type_expr)); + child.second->SetAlias(child.first); + struct_children.push_back(std::move(child.second)); } } return make_uniq(Identifier("STRUCT"), std::move(struct_children)); @@ -372,29 +374,25 @@ unique_ptr PEGTransformerFactory::TransformVariantType(PEGTran unique_ptr PEGTransformerFactory::TransformUnionType(PEGTransformer &transformer, - const child_list_t &col_id_type_list) { - identifier_set_t union_names; + vector>> col_id_type_list) { vector> union_children; for (auto &colid : col_id_type_list) { - union_names.insert(colid.first); - auto &type_expr = UnboundType::GetTypeExpression(colid.second); - auto new_type_expr = type_expr->Copy(); - new_type_expr->SetAlias(colid.first); - union_children.push_back(std::move(new_type_expr)); + colid.second->SetAlias(colid.first); + union_children.push_back(std::move(colid.second)); } return make_uniq(Identifier("UNION"), std::move(union_children)); } -child_list_t +vector>> PEGTransformerFactory::TransformColIdTypeList(PEGTransformer &transformer, - const vector> &col_id_type) { + vector>> col_id_type) { return col_id_type; } -pair PEGTransformerFactory::TransformColIdType(PEGTransformer &transformer, - const Identifier &col_id, - const LogicalType &type) { - return make_pair(Identifier(col_id), type); +pair> +PEGTransformerFactory::TransformColIdType(PEGTransformer &transformer, const Identifier &col_id, + unique_ptr nested_type) { + return make_pair(Identifier(col_id), std::move(nested_type)); } unique_ptr PEGTransformerFactory::TransformBitType( @@ -531,8 +529,8 @@ unique_ptr PEGTransformerFactory::TransformNumberLiteral(PEGTr } unique_ptr PEGTransformerFactory::TransformSetofType(PEGTransformer &transformer, - const LogicalType &type) { - return UnboundType::GetTypeExpression(type)->Copy(); + unique_ptr nested_type) { + return nested_type; } // StringLiteral <- '\'' [^\']* '\'' diff --git a/src/duckdb/src/parser/peg/transformer/transform_copy.cpp b/src/duckdb/src/parser/peg/transformer/transform_copy.cpp index 93107fa0f..cbae0a00c 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_copy.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_copy.cpp @@ -132,6 +132,7 @@ unique_ptr PEGTransformerFactory::TransformCopyTable(PEGTransformer &transformer, unique_ptr base_table_name, const optional> &insert_column_list, const bool &from_or_to, unique_ptr copy_file_name, + const optional &legacy_option, const optional> ©_options) { auto result = make_uniq(); auto info = make_uniq(); @@ -149,8 +150,17 @@ PEGTransformerFactory::TransformCopyTable(PEGTransformer &transformer, unique_pt } info->format = ExtractFormat(info->file_path); + vector generic_options; + + if (legacy_option) { + generic_options.push_back(*legacy_option); + } + if (copy_options) { - auto generic_options = *copy_options; + generic_options.insert(generic_options.end(), copy_options->begin(), copy_options->end()); + } + + if (!generic_options.empty()) { SetCopyOptions(info, generic_options); } @@ -184,14 +194,20 @@ PEGTransformerFactory::TransformCopyFileNameIdentifierColId(PEGTransformer &tran } Identifier PEGTransformerFactory::TransformIdentifierColId(PEGTransformer &transformer, const Identifier &identifier, - const Identifier &col_id) { - string result; - result += identifier.GetIdentifierName(); - result += "."; - result += col_id.GetIdentifierName(); + const vector ©_file_name_suffix) { + string result = identifier.GetIdentifierName(); + for (const auto &suffix : copy_file_name_suffix) { + result += "."; + result += suffix.GetIdentifierName(); + } return Identifier(result); } +GenericCopyOption PEGTransformerFactory::TransformCopyLegacyOption(PEGTransformer &transformer, const bool &has_result, + const string &delimiter) { + return GenericCopyOption("delimiter", delimiter); +} + vector PEGTransformerFactory::TransformCopyOptions(PEGTransformer &transformer, const bool &has_result, const vector ©_option_list) { diff --git a/src/duckdb/src/parser/peg/transformer/transform_create_index.cpp b/src/duckdb/src/parser/peg/transformer/transform_create_index.cpp index eaf93d095..07ffd9bb4 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_create_index.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_create_index.cpp @@ -7,8 +7,7 @@ namespace duckdb { unique_ptr PEGTransformerFactory::TransformCreateIndexStmt( PEGTransformer &transformer, const optional &unique_index, const optional &if_not_exists, const optional &index_name, unique_ptr base_table_name, - const optional> &insert_column_list, const optional &index_type, - optional>> index_element, + const optional &index_type, vector> index_element, optional>> with_list, optional> where_clause) { auto result = make_uniq(); @@ -23,22 +22,19 @@ unique_ptr PEGTransformerFactory::TransformCreateIndexStmt( // the index lives in the same (possibly nested) schema as the table it is created on index_info->SetQualifiedName(base_table_name->GetQualifiedName().WithName(*index_name)); index_info->index_type = index_type ? index_type->GetIdentifierName() : "ART"; - if (insert_column_list) { - for (auto &column : *insert_column_list) { - index_info->expressions.push_back( - make_uniq(Identifier(column), base_table_name->Table())); - index_info->parsed_expressions.push_back( - make_uniq(Identifier(column), base_table_name->Table())); - } - } - if (index_element) { - for (auto &expr : *index_element) { - if (expr->GetExpressionType() == ExpressionType::COLLATE) { - throw NotImplementedException("Index with collation not supported yet!"); + for (auto &expr : index_element) { + if (!index_type && expr->GetExpressionClass() == ExpressionClass::COLUMN_REF) { + auto &column_ref = expr->Cast(); + if (!column_ref.IsQualified()) { + auto column_name = column_ref.GetColumnName(); + expr = make_uniq(std::move(column_name), base_table_name->Table()); } - index_info->expressions.push_back(expr->Copy()); - index_info->parsed_expressions.push_back(std::move(expr)); } + if (expr->GetExpressionType() == ExpressionType::COLLATE) { + throw NotImplementedException("Index with collation not supported yet!"); + } + index_info->expressions.push_back(expr->Copy()); + index_info->parsed_expressions.push_back(std::move(expr)); } if (where_clause) { throw NotImplementedException("Creating partial indexes is not supported currently"); @@ -56,9 +52,13 @@ unique_ptr PEGTransformerFactory::TransformCreateIndexStmt( return result; } -string PEGTransformerFactory::TransformDottedIdentifierString(PEGTransformer &transformer, - const vector &dotted_identifier) { - return StringUtil::Join(dotted_identifier, "."); +string PEGTransformerFactory::TransformDottedColLabel(PEGTransformer &transformer, const string &col_label, + const optional> &dot_col_label) { + vector parts {col_label}; + if (dot_col_label) { + parts.insert(parts.end(), dot_col_label->begin(), dot_col_label->end()); + } + return StringUtil::Join(parts, "."); } Identifier PEGTransformerFactory::TransformIndexType(PEGTransformer &transformer, const Identifier &identifier) { @@ -88,7 +88,11 @@ PEGTransformerFactory::TransformRelOptionList(PEGTransformer &transformer, vector>> rel_option) { case_insensitive_map_t> result; for (auto &option : rel_option) { - result.insert({option.first.GetIdentifierName(), std::move(option.second)}); + auto &name = option.first.GetIdentifierName(); + if (result.find(name) != result.end()) { + throw ParserException("Duplicate option \"%s\" in WITH clause", name); + } + result.insert({name, std::move(option.second)}); } return result; } diff --git a/src/duckdb/src/parser/peg/transformer/transform_create_schema.cpp b/src/duckdb/src/parser/peg/transformer/transform_create_schema.cpp index 5950a7ad3..36428b00d 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_create_schema.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_create_schema.cpp @@ -2,9 +2,9 @@ #include "duckdb/parser/peg/transformer/peg_transformer.hpp" namespace duckdb { -unique_ptr PEGTransformerFactory::TransformCreateSchemaStmt(PEGTransformer &transformer, - const optional &if_not_exists, - const QualifiedName &qualified_name) { +unique_ptr PEGTransformerFactory::TransformCreateSchemaStmt( + PEGTransformer &transformer, const optional &if_not_exists, const QualifiedName &qualified_name, + optional>> with_option_list) { auto result = make_uniq(); auto info = make_uniq(); info->on_conflict = if_not_exists ? OnCreateConflict::IGNORE_ON_CONFLICT : OnCreateConflict::ERROR_ON_CONFLICT; @@ -12,9 +12,18 @@ unique_ptr PEGTransformerFactory::TransformCreateSchemaStmt(PEG // catalog/schema serialization correct). The leading components are resolved into a catalog + parent-schema chain // during binding (see Binder::BindCreateSchema). info->SetQualifiedName(QualifiedName(qualified_name.Path(), Identifier())); + if (with_option_list) { + info->options = std::move(*with_option_list); + } result->info = std::move(info); return result; } +case_insensitive_map_t> +PEGTransformerFactory::TransformWithOptionList(PEGTransformer &transformer, + case_insensitive_map_t> rel_option_list) { + return rel_option_list; +} + } // namespace duckdb diff --git a/src/duckdb/src/parser/peg/transformer/transform_create_sequence.cpp b/src/duckdb/src/parser/peg/transformer/transform_create_sequence.cpp index 5a76f6ac1..d8169e9d9 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_create_sequence.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_create_sequence.cpp @@ -130,6 +130,13 @@ pair> PEGTransformerFactory::TransformSeqNoCy return make_pair("cycle", make_uniq(SequenceInfo::SEQ_CYCLE, Value(false))); } +//! Sequence options take integer constants; anything else used to be rounded or cast silently +static void CheckIntegerSequenceOption(const char *option, const Literal &literal) { + if (!literal.IsNull() && literal.kind != LiteralKind::INTEGER) { + throw ParserException("Expected an integer argument for %s, got %s", option, literal.ToString()); + } +} + pair> PEGTransformerFactory::TransformSeqSetIncrement(PEGTransformer &transformer, const bool &has_result, unique_ptr expression) { @@ -152,6 +159,7 @@ PEGTransformerFactory::TransformSeqSetIncrement(PEGTransformer &transformer, con throw ParserException("Expected constant expression."); } auto &const_expr = expression->Cast(); + CheckIntegerSequenceOption("INCREMENT", const_expr.GetLiteral()); return make_pair("increment", make_uniq(SequenceInfo::SEQ_INC, const_expr.GetLiteral().ToValue())); } @@ -179,6 +187,7 @@ PEGTransformerFactory::TransformSeqSetMinMax(PEGTransformer &transformer, const throw ParserException("Expected constant expression."); } auto &const_expr = expression->Cast(); + CheckIntegerSequenceOption(seq_min_or_max == "minvalue" ? "MINVALUE" : "MAXVALUE", const_expr.GetLiteral()); auto seq_info = seq_min_or_max == "minvalue" ? SequenceInfo::SEQ_MIN : SequenceInfo::SEQ_MAX; return make_pair(seq_min_or_max, make_uniq(seq_info, const_expr.GetLiteral().ToValue())); } @@ -196,6 +205,7 @@ PEGTransformerFactory::TransformSeqStartWith(PEGTransformer &transformer, const throw ParserException("Expected constant expression."); } auto &const_expr = expression->Cast(); + CheckIntegerSequenceOption("START", const_expr.GetLiteral()); return make_pair("start", make_uniq(SequenceInfo::SEQ_START, const_expr.GetLiteral().ToValue())); } diff --git a/src/duckdb/src/parser/peg/transformer/transform_create_table.cpp b/src/duckdb/src/parser/peg/transformer/transform_create_table.cpp index 1164222b8..8930b1324 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_create_table.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_create_table.cpp @@ -18,8 +18,6 @@ #include "duckdb/parser/parsed_data/create_secret_info.hpp" #include "duckdb/parser/constraints/not_null_constraint.hpp" #include "duckdb/parser/expression/cast_expression.hpp" -#include "duckdb/parser/expression/type_expression.hpp" -#include "duckdb/catalog/default/default_types.hpp" namespace duckdb { @@ -35,6 +33,9 @@ PEGTransformerFactory::TransformCreateStatement(PEGTransformer &transformer, con } conflict_policy = OnCreateConflict::REPLACE_ON_CONFLICT; } + if (temporary && *temporary == SecretPersistType::PERSISTENT && result->info->type != CatalogType::SECRET_ENTRY) { + throw ParserException("PERSISTENT is only supported for CREATE SECRET"); + } if (result->info->type == CatalogType::SECRET_ENTRY) { auto &secret_info = result->info->Cast(); secret_info.persist_type = temporary ? *temporary : SecretPersistType::DEFAULT; @@ -157,19 +158,19 @@ ColumnElements PEGTransformerFactory::TransformCreateTableColumnList(PEGTransformer &transformer, vector create_table_column_element) { ColumnElements result; - for (idx_t col_idx = 0; col_idx < create_table_column_element.size(); ++col_idx) { - auto &column_element = create_table_column_element[col_idx]; + for (auto &column_element : create_table_column_element) { if (column_element.column_definition) { auto &column_result = *column_element.column_definition; + auto column_index = LogicalIndex(result.columns.LogicalColumnCount()); for (auto &constraint : column_result.constraints) { result.constraints.push_back(std::move(constraint)); } for (auto constraint_type : column_result.constraint_types) { if (constraint_type.type == ConstraintType::NOT_NULL) { - result.constraints.push_back(make_uniq(LogicalIndex(col_idx))); + result.constraints.push_back(make_uniq(column_index)); } else if (constraint_type.type == ConstraintType::UNIQUE) { result.constraints.push_back( - make_uniq(LogicalIndex(col_idx), column_result.column_definition.GetName(), + make_uniq(column_index, column_result.column_definition.GetName(), constraint_type.is_primary_key, constraint_type.check_mode)); } } @@ -211,11 +212,6 @@ Identifier PEGTransformerFactory::TransformStringLiteralIdentifier(PEGTransforme return Identifier(string_literal); } -string PEGTransformerFactory::TransformIdentifier(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - return list_pr.Child(0).identifier.GetIdentifierName(); -} - vector PEGTransformerFactory::TransformDottedIdentifier(PEGTransformer &transformer, const Identifier &identifier, const optional> &dot_col_label) { @@ -231,10 +227,9 @@ string PEGTransformerFactory::TransformDotColLabel(PEGTransformer &transformer, } ConstraintColumnDefinition PEGTransformerFactory::TransformColumnDefinition( - PEGTransformer &transformer, const vector &dotted_identifier, const optional &type, - optional generated_column, const bool &has_result, - optional> column_constraint) { - auto qualified_name = StringToQualifiedName(dotted_identifier); + PEGTransformer &transformer, const Identifier &identifier, const optional &type, + optional generated_column, optional> column_constraint) { + auto qualified_name = QualifiedName(identifier); bool has_type = type.has_value(); bool has_generated = generated_column && generated_column->expr != nullptr; if (!has_type && !has_generated) { @@ -243,6 +238,7 @@ ConstraintColumnDefinition PEGTransformerFactory::TransformColumnDefinition( } auto column_type = has_type ? *type : LogicalType::ANY; CompressionType compression_type = CompressionType::COMPRESSION_AUTO; + bool has_collation = false; ColumnConstraint accumulated_constraints; if (column_constraint) { for (auto &cc_entry : *column_constraint) { @@ -271,28 +267,14 @@ ConstraintColumnDefinition PEGTransformerFactory::TransformColumnDefinition( } accumulated_constraints.constraints.push_back(std::move(cc_entry.constraint)); } else if (cc_entry.constraint_name == "ColumnCollation") { + if (has_collation) { + throw ParserException("multiple COLLATE clauses not allowed"); + } + has_collation = true; if (has_generated) { throw ParserException("Collations are not supported on generated columns"); } - if (column_type.id() == LogicalTypeId::ANY) { - throw ParserException("Specify the VARCHAR type for column \"%s\" with collation.", - qualified_name.ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA)); - } else if (column_type.IsUnbound()) { - auto &expr = UnboundType::GetTypeExpression(column_type); - if (expr->GetExpressionClass() != ExpressionClass::TYPE) { - throw InternalException("Expected a type expression"); - } - auto &type_expr = expr->Cast(); - if (DefaultTypeGenerator::GetDefaultType(type_expr.GetTypeName()) != LogicalTypeId::VARCHAR) { - throw ParserException("Only VARCHAR columns can have collations!"); - } - } else { - throw InternalException("Expected only unbound types here"); - } - vector> type_children; - type_children.push_back(std::move(cc_entry.expression)); - column_type = - LogicalType::UNBOUND(make_uniq(Identifier("VARCHAR"), std::move(type_children))); + column_type = ApplyColumnCollation(column_type, std::move(cc_entry.expression)); } else { accumulated_constraints.constraints.push_back(std::move(cc_entry.constraint)); } @@ -301,14 +283,14 @@ ConstraintColumnDefinition PEGTransformerFactory::TransformColumnDefinition( if (has_generated) { auto generated = std::move(*generated_column); if (generated.expr->HasSubquery()) { - throw ParserException("Expression of generated column \"%s\" contains a subquery, which isn't allowed", + throw ParserException("Expression of generated column %s contains a subquery, which isn't allowed", qualified_name.Name()); } if (column_type != LogicalType::ANY) { generated.expr = make_uniq(column_type, std::move(generated.expr)); } if (generated.expr->HasSubquery()) { - throw ParserException("Expression of generated column \"%s\" contains a subquery, which isn't allowed", + throw ParserException("Expression of generated column %s contains a subquery, which isn't allowed", qualified_name.Name()); } @@ -334,9 +316,12 @@ ConstraintColumnDefinition PEGTransformerFactory::TransformColumnDefinition( } GeneratedColumnDefinition PEGTransformerFactory::TransformGeneratedColumn(PEGTransformer &transformer, - const bool &has_result, + const optional &generated_always, unique_ptr expression, const optional &generated_column_type) { + if (generated_always && !*generated_always) { + throw ParserException("for a generated column, GENERATED ALWAYS must be specified"); + } GeneratedColumnDefinition generated; generated.expr = std::move(expression); VerifyColumnRefs(*generated.expr); @@ -415,7 +400,7 @@ ColumnConstraintEntry PEGTransformerFactory::TransformColumnCompression(PEGTrans ColumnConstraintEntry PEGTransformerFactory::TransformForeignKeyConstraint(PEGTransformer &transformer, unique_ptr base_table_name, const optional> &column_list, - const KeyActions &key_actions) { + const optional &key_actions) { ForeignKeyInfo fk_info; fk_info.schema = base_table_name->GetQualifiedName().Schema(); fk_info.table = base_table_name->Table(); @@ -431,17 +416,26 @@ ColumnConstraintEntry PEGTransformerFactory::TransformForeignKeyConstraint(PEGTr return entry; } -KeyActions PEGTransformerFactory::TransformKeyActions(PEGTransformer &transformer, - const optional &update_action, - const optional &delete_action) { - KeyActions results; - if (update_action) { - results.update_action = *update_action; - } +KeyActions PEGTransformerFactory::TransformUpdateFirstKeyActions(PEGTransformer &transformer, + const string &update_action, + const optional &delete_action) { + KeyActions result; + result.update_action = update_action; if (delete_action) { - results.delete_action = *delete_action; + result.delete_action = *delete_action; } - return results; + return result; +} + +KeyActions PEGTransformerFactory::TransformDeleteFirstKeyActions(PEGTransformer &transformer, + const string &delete_action, + const optional &update_action) { + KeyActions result; + result.delete_action = delete_action; + if (update_action) { + result.update_action = *update_action; + } + return result; } string PEGTransformerFactory::TransformUpdateAction(PEGTransformer &transformer, const string &key_action) { @@ -500,6 +494,13 @@ ConstraintCheckMode PEGTransformerFactory::TransformDeferredConstraint(PEGTransf return ConstraintCheckMode::DEFERRED; } +// Column constraint names are parsed but not stored +ColumnConstraintEntry PEGTransformerFactory::TransformNameableColumnConstraint(PEGTransformer &transformer, + const bool &has_result, + ColumnConstraintEntry child) { + return child; +} + bool PEGTransformerFactory::TransformNullConstraint(PEGTransformer &transformer) { return false; } @@ -548,6 +549,14 @@ bool PEGTransformerFactory::TransformDeleteRows(PEGTransformer &transformer) { throw NotImplementedException("Only ON COMMIT PRESERVE ROWS is supported"); } +bool PEGTransformerFactory::TransformGeneratedAlways(PEGTransformer &transformer) { + return true; +} + +bool PEGTransformerFactory::TransformGeneratedByDefault(PEGTransformer &transformer) { + throw ParserException("for a generated column, GENERATED ALWAYS must be specified"); +} + bool PEGTransformerFactory::TransformVirtualGeneratedColumn(PEGTransformer &transformer) { return true; } diff --git a/src/duckdb/src/parser/peg/transformer/transform_create_type.cpp b/src/duckdb/src/parser/peg/transformer/transform_create_type.cpp index 991d7ade2..9c870fb7f 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_create_type.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_create_type.cpp @@ -1,5 +1,6 @@ #include "duckdb/common/vector/flat_vector.hpp" #include "duckdb/common/vector/string_vector.hpp" +#include "duckdb/parser/expression/type_expression.hpp" #include "duckdb/parser/peg/transformer/peg_transformer.hpp" #include "duckdb/parser/parsed_data/create_type_info.hpp" @@ -35,7 +36,8 @@ PEGTransformerFactory::TransformEnumSelectType(PEGTransformer &transformer, unique_ptr PEGTransformerFactory::TransformEnumStringLiteralList(PEGTransformer &transformer, - const optional> &string_literal) { + const optional> &string_literal, + const optional> &array_bounds) { auto result = make_uniq(); idx_t enum_count = string_literal ? string_literal->size() : 0; Vector enum_vector(LogicalType::VARCHAR, enum_count); @@ -46,6 +48,9 @@ PEGTransformerFactory::TransformEnumStringLiteralList(PEGTransformer &transforme } } result->type = LogicalType::ENUM(enum_vector, enum_count); + if (array_bounds && !array_bounds->empty()) { + result->type = TransformType(transformer, TypeExpression::FromLogicalType(result->type), array_bounds); + } return result; } diff --git a/src/duckdb/src/parser/peg/transformer/transform_create_view.cpp b/src/duckdb/src/parser/peg/transformer/transform_create_view.cpp index 2c634282e..f342bf280 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_create_view.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_create_view.cpp @@ -102,6 +102,9 @@ PEGTransformerFactory::TransformCreateViewStmt(PEGTransformer &transformer, cons if (create_secure) { info->security_type = ViewSecurityType::SECURE_VIEW; } + if (create_recursive && !insert_column_list) { + throw ParserException("CREATE RECURSIVE VIEW requires a column list"); + } if (insert_column_list) { info->aliases = StringsToIdentifiers(*insert_column_list); } diff --git a/src/duckdb/src/parser/peg/transformer/transform_delete.cpp b/src/duckdb/src/parser/peg/transformer/transform_delete.cpp index 577dfca05..372c91309 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_delete.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_delete.cpp @@ -29,14 +29,18 @@ unique_ptr PEGTransformerFactory::TransformDeleteStatement( unique_ptr PEGTransformerFactory::TransformTargetOptAlias(PEGTransformer &transformer, unique_ptr base_table_name, - const bool &has_result, - const optional &col_id) { - if (col_id && !col_id->empty()) { - base_table_name->alias = Identifier(*col_id); + const optional &target_alias) { + if (target_alias && !target_alias->empty()) { + base_table_name->alias = Identifier(*target_alias); } return base_table_name; } +Identifier PEGTransformerFactory::TransformTargetAlias(PEGTransformer &transformer, const bool &has_result, + const Identifier &col_id) { + return col_id; +} + vector> PEGTransformerFactory::TransformDeleteUsingClause(PEGTransformer &transformer, vector> table_ref) { return table_ref; @@ -44,10 +48,24 @@ vector> PEGTransformerFactory::TransformDeleteUsingClause(P unique_ptr PEGTransformerFactory::TransformTruncateStatement(PEGTransformer &transformer, const bool &has_result, - unique_ptr base_table_name) { + unique_ptr base_table_name, + optional truncate_options) { + if (truncate_options && *truncate_options) { + throw NotImplementedException("TRUNCATE ... CASCADE is not implemented"); + } auto result = make_uniq(); result->node->table = std::move(base_table_name); return std::move(result); } +bool PEGTransformerFactory::TransformTruncateAliasOptions(PEGTransformer &transformer, const Identifier &target_alias, + optional truncate_drop_behavior) { + // the alias is ignored, the result is whether CASCADE was given + return truncate_drop_behavior && *truncate_drop_behavior; +} + +bool PEGTransformerFactory::TransformTruncateDropBehavior(PEGTransformer &transformer, const bool &drop_behavior) { + return drop_behavior; +} + } // namespace duckdb diff --git a/src/duckdb/src/parser/peg/transformer/transform_describe.cpp b/src/duckdb/src/parser/peg/transformer/transform_describe.cpp index 5fe0ec5c7..5ff313174 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_describe.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_describe.cpp @@ -213,6 +213,9 @@ DescribeTarget PEGTransformerFactory::TransformDescribeBaseTableName(PEGTransfor DescribeTarget PEGTransformerFactory::TransformDescribeStringLiteral(PEGTransformer &transformer, const string &string_literal) { + if (string_literal.empty()) { + throw ParserException("Expected a table name, but got an empty string"); + } DescribeTarget result; result.is_table_name = true; result.table_name = Identifier(string_literal); diff --git a/src/duckdb/src/parser/peg/transformer/transform_drop.cpp b/src/duckdb/src/parser/peg/transformer/transform_drop.cpp index 0c8854dd4..c0473c3f3 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_drop.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_drop.cpp @@ -8,6 +8,9 @@ unique_ptr PEGTransformerFactory::TransformDropStatement(PEGTransf unique_ptr drop_entries, const optional &drop_behavior) { if (drop_behavior) { + if (drop_entries->info->type == CatalogType::SECRET_ENTRY) { + throw ParserException("CASCADE and RESTRICT are not supported for DROP SECRET"); + } drop_entries->info->cascade = *drop_behavior; } return std::move(drop_entries); diff --git a/src/duckdb/src/parser/peg/transformer/transform_explain.cpp b/src/duckdb/src/parser/peg/transformer/transform_explain.cpp index ab65b9d7a..37224d83c 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_explain.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_explain.cpp @@ -18,6 +18,7 @@ unique_ptr PEGTransformerFactory::TransformExplainStatement( const optional> &explain_option_list, unique_ptr explainable_statements) { auto explain_type = analyze_keyword ? ExplainType::EXPLAIN_ANALYZE : ExplainType::EXPLAIN_STANDARD; bool format_is_set = false; + bool sql_is_set = false; auto format = ProfilerPrintFormat::Default(); if (explain_option_list) { for (auto option : *explain_option_list) { @@ -38,6 +39,11 @@ unique_ptr PEGTransformerFactory::TransformExplainStatement( format = ParseProfilerPrintFormat(option.children[0]); } format_is_set = true; + } else if (option_name == "sql") { + if (sql_is_set || !option.children.empty() || option.expression) { + throw InvalidInputException("SQL must be provided once without arguments"); + } + sql_is_set = true; } else if (option_name == "analyze") { explain_type = ExplainType::EXPLAIN_ANALYZE; } else { @@ -45,6 +51,13 @@ unique_ptr PEGTransformerFactory::TransformExplainStatement( } } } + if (sql_is_set) { + if (format_is_set || explain_type == ExplainType::EXPLAIN_ANALYZE) { + throw InvalidInputException("EXPLAIN (SQL) cannot be combined with ANALYZE or FORMAT"); + } + transformer.PivotEntryCheck("EXPLAIN (SQL) statement"); + explain_type = ExplainType::EXPLAIN_SQL; + } auto statement = std::move(explainable_statements); return make_uniq(std::move(statement), explain_type, format); } diff --git a/src/duckdb/src/parser/peg/transformer/transform_expression.cpp b/src/duckdb/src/parser/peg/transformer/transform_expression.cpp index 9c1562f42..1a2a75c7b 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_expression.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_expression.cpp @@ -18,62 +18,96 @@ #include "duckdb/parser/tableref/subqueryref.hpp" #include "duckdb/parser/tableref/emptytableref.hpp" #include "duckdb/parser/parsed_expression_iterator.hpp" +#include "duckdb/parser/expression/star_expression.hpp" #include "duckdb/common/string_util.hpp" namespace duckdb { -unique_ptr -PEGTransformerFactory::TransformExpressionStatement(PEGTransformer &transformer, - vector> expression_alias) { - auto expressions = std::move(expression_alias); - auto select_statement = make_uniq(); - auto select_node = make_uniq(); +//! Only an unqualified star (e.g. COUNT(*)) refers to the rows themselves - a qualified star (e.g. COUNT(tbl.*)) +//! refers to the columns of that relation and is expanded by the binder +static bool ExpressionIsUnqualifiedStar(const ParsedExpression &expr) { + return PEGTransformerFactory::ExpressionIsEmptyStar(expr) && expr.Cast().RelationName().empty(); +} - bool any_column_ref = false; - for (auto &expr : expressions) { - if (expr->GetExpressionClass() == ExpressionClass::COLUMN_REF) { - any_column_ref = true; - break; +//! Which syntactic form named the function: f(x) or x.f(y). +enum class ParserRewriteCallForm : uint8_t { PLAIN, METHOD_CHAIN }; + +//! Names that the parser rewrites into a syntax node instead of a catalog function call. +//! Every path that can name a function must go through this, or the paths disagree. +//! Returns nullptr when the name is an ordinary function. +static unique_ptr TransformParserFunctionRewrite(const string &lowercase_name, + vector &function_children, + ParserRewriteCallForm call_form) { + auto reject_named_arguments = [&function_children](const char *context) { + for (auto &arg : function_children) { + if (arg.HasName()) { + throw ParserException("Named arguments are not supported in %s", context); + } } - } + }; - if (any_column_ref && expressions.size() > 1) { - throw ParserException("Mix of table names and expressions is not supported. " - "Use SELECT to explicitly specify what you want to query."); + if (lowercase_name == "unpack") { + if (function_children.size() != 1) { + throw ParserException("Wrong number of arguments to the UNPACK operator"); + } + reject_named_arguments("UNPACK operator"); + auto expr = make_uniq(ExpressionType::OPERATOR_UNPACK); + for (auto &arg : function_children) { + expr->GetChildrenMutable().push_back(std::move(arg.GetExpressionMutable())); + } + return std::move(expr); } - - if (any_column_ref) { - // Single COLUMN_REF: treat as table scan - auto &col_expr = expressions[0]->Cast(); - if (col_expr.IsQualified()) { - if (col_expr.ColumnNames().size() >= 4) { - throw ParserException( - "Too many qualifiers encountered. Expected \"catalog.schema.table\" or \"schema.table\""); - } - if (col_expr.ColumnNames().size() == 3) { - auto table_description = TableDescription( - QualifiedName(col_expr.ColumnNames()[0], col_expr.ColumnNames()[1], col_expr.ColumnNames()[2])); - select_node->from_table = make_uniq(table_description); - } else if (col_expr.ColumnNames().size() == 2) { - auto table_description = - TableDescription(QualifiedName({col_expr.ColumnNames()[0]}, col_expr.ColumnNames()[1])); - select_node->from_table = make_uniq(table_description); - } - } else { - auto base_table = make_uniq(); - base_table->SetTable(col_expr.GetColumnName()); - select_node->from_table = std::move(base_table); + if (lowercase_name == "try") { + if (function_children.size() != 1) { + throw ParserException("Wrong number of arguments provided to TRY expression"); } - select_node->select_list.push_back(make_uniq()); - } else { - for (auto &expr : expressions) { - select_node->select_list.push_back(std::move(expr)); + reject_named_arguments("TRY expression"); + auto try_expression = make_uniq(ExpressionType::OPERATOR_TRY); + for (auto &arg : function_children) { + try_expression->GetChildrenMutable().push_back(std::move(arg.GetExpressionMutable())); } - select_node->from_table = make_uniq(); + return std::move(try_expression); } + if (lowercase_name == "construct_array") { + reject_named_arguments("array constructors"); + auto construct_array = make_uniq(ExpressionType::ARRAY_CONSTRUCTOR); + for (auto &arg : function_children) { + construct_array->GetChildrenMutable().push_back(std::move(arg.GetExpressionMutable())); + } + return std::move(construct_array); + } + if (lowercase_name == "ifnull") { + if (function_children.size() != 2) { + throw ParserException("Wrong number of arguments to IFNULL."); + } + reject_named_arguments("IFNULL expressions"); - select_statement->node = std::move(select_node); - return std::move(select_statement); + // Two-argument COALESCE + auto coalesce_op = make_uniq(ExpressionType::OPERATOR_COALESCE); + coalesce_op->GetChildrenMutable().push_back(std::move(function_children[0].GetExpressionMutable())); + coalesce_op->GetChildrenMutable().push_back(std::move(function_children[1].GetExpressionMutable())); + return std::move(coalesce_op); + } + // COALESCE has a grammar rule of its own (CoalesceExpression), which is how the plain path spells + // it. Reaching the plain path under this name therefore means the caller wrote it as a function + // call - quoted, or schema-qualified - which resolves in the catalog and lets a user macro named + // coalesce win. The method chain has no grammar form, so the rewrite is its only route. + if (lowercase_name == "coalesce" && call_form == ParserRewriteCallForm::METHOD_CHAIN) { + reject_named_arguments("COALESCE expressions"); + auto coalesce_op = make_uniq(ExpressionType::OPERATOR_COALESCE); + for (auto &arg : function_children) { + coalesce_op->GetChildrenMutable().push_back(std::move(arg.GetExpressionMutable())); + } + return std::move(coalesce_op); + } + if (lowercase_name == "date") { + if (function_children.size() != 1) { + throw ParserException("Wrong number of arguments provided to DATE function"); + } + return std::move( + make_uniq(LogicalType::DATE, std::move(function_children[0].GetExpressionMutable()))); + } + return nullptr; } unique_ptr @@ -105,7 +139,25 @@ PEGTransformerFactory::TransformBaseExpression(PEGTransformer &transformer, } else if (indirection_expr->GetExpressionClass() == ExpressionClass::FUNCTION) { auto function_expr = unique_ptr_cast(std::move(indirection_expr)); function_expr->GetArgumentsMutable().insert(function_expr->GetArgumentsMutable().begin(), std::move(expr)); - expr = std::move(function_expr); + // A method-chained call names a function too, so it takes the same rewrites. + auto lowercase_name = StringUtil::Lower(function_expr->FunctionName().GetIdentifierName()); + const bool distinct = function_expr->Distinct(); + const bool has_order_by = function_expr->OrderBy() && !function_expr->OrderBy()->orders.empty(); + if (auto rewritten = TransformParserFunctionRewrite(lowercase_name, function_expr->GetArgumentsMutable(), + ParserRewriteCallForm::METHOD_CHAIN)) { + // The method grammar accepts DISTINCT and ORDER BY for every call, but a rewrite yields a + // syntax node with nowhere to put them. Reject rather than drop them silently. + auto upper_name = StringUtil::Upper(lowercase_name); + if (distinct) { + throw ParserException("DISTINCT is not supported in %s expressions", upper_name); + } + if (has_order_by) { + throw ParserException("ORDER BY is not supported in %s expressions", upper_name); + } + expr = std::move(rewritten); + } else { + expr = std::move(function_expr); + } prev_indirection_was_cast = false; } else if (indirection_expr->GetExpressionClass() == ExpressionClass::CONSTANT) { vector> struct_children; @@ -187,7 +239,7 @@ unique_ptr PEGTransformerFactory::TransformFunctionExpression( if (filter_clause) { filter_expr = std::move(*filter_clause); } - if (function_children.size() == 1 && ExpressionIsEmptyStar(*function_children[0].GetExpressionMutable()) && + if (function_children.size() == 1 && ExpressionIsUnqualifiedStar(function_children[0].GetExpression()) && !distinct && order_modifier->orders.empty()) { // COUNT(*) gets converted into COUNT() function_children.clear(); @@ -229,64 +281,9 @@ unique_ptr PEGTransformerFactory::TransformFunctionExpression( lowercase_name = "count_star"; } - if (lowercase_name == "unpack") { - if (function_children.size() != 1) { - throw ParserException("Wrong number of arguments to the UNPACK operator"); - } - auto expr = make_uniq(ExpressionType::OPERATOR_UNPACK); - for (auto &arg : function_children) { - if (arg.HasName()) { - throw ParserException("Named arguments are not supported in UNPACK operator"); - } - expr->GetChildrenMutable().push_back(std::move(arg.GetExpressionMutable())); - } - return std::move(expr); - } - if (lowercase_name == "try") { - if (function_children.size() != 1) { - throw ParserException("Wrong number of arguments provided to TRY expression"); - } - auto try_expression = make_uniq(ExpressionType::OPERATOR_TRY); - for (auto &arg : function_children) { - if (arg.HasName()) { - throw ParserException("Named arguments are not supported in TRY expression"); - } - try_expression->GetChildrenMutable().push_back(std::move(arg.GetExpressionMutable())); - } - return std::move(try_expression); - } - if (lowercase_name == "construct_array") { - auto construct_array = make_uniq(ExpressionType::ARRAY_CONSTRUCTOR); - for (auto &arg : function_children) { - if (arg.HasName()) { - throw ParserException("Named arguments are not supported in array constructors"); - } - construct_array->GetChildrenMutable().push_back(std::move(arg.GetExpressionMutable())); - } - return std::move(construct_array); - } - if (lowercase_name == "ifnull") { - if (function_children.size() != 2) { - throw ParserException("Wrong number of arguments to IFNULL."); - } - for (auto &arg : function_children) { - if (arg.HasName()) { - throw ParserException("Named arguments are not supported in IFNULL expressions"); - } - } - - // Two-argument COALESCE - auto coalesce_op = make_uniq(ExpressionType::OPERATOR_COALESCE); - coalesce_op->GetChildrenMutable().push_back(std::move(function_children[0].GetExpressionMutable())); - coalesce_op->GetChildrenMutable().push_back(std::move(function_children[1].GetExpressionMutable())); - return std::move(coalesce_op); - } - if (lowercase_name == "date") { - if (function_children.size() != 1) { - throw ParserException("Wrong number of arguments provided to DATE function"); - } - return std::move( - make_uniq(LogicalType::DATE, std::move(function_children[0].GetExpressionMutable()))); + if (auto rewritten = + TransformParserFunctionRewrite(lowercase_name, function_children, ParserRewriteCallForm::PLAIN)) { + return rewritten; } if (function_expression_arguments.has_ignore_nulls) { throw ParserException("RESPECT/IGNORE NULLS is not supported for non-window functions"); @@ -336,6 +333,9 @@ MethodArguments PEGTransformerFactory::TransformFunctionExpressionArgumentList( PEGTransformer &transformer, const optional &distinct_or_all, optional> function_argument_list, optional> order_by_clause, const optional &ignore_or_respect_nulls) { + if (distinct_or_all && !function_argument_list) { + throw ParserException("%s requires at least one argument", *distinct_or_all ? "DISTINCT" : "ALL"); + } MethodArguments result; if (distinct_or_all) { result.distinct = *distinct_or_all; @@ -570,20 +570,6 @@ PEGTransformerFactory::TransformBoundedListExpression(PEGTransformer &transforme return vector>(); } -// Expression <- LambdaArrowExpression -unique_ptr PEGTransformerFactory::TransformExpression(PEGTransformer &transformer, - ParseResult &parse_result) { - auto stack_check = transformer.StackCheck(); - auto &list_pr = parse_result.Cast(); - return transformer.Transform>(list_pr.Child(0)); -} - -unique_ptr -PEGTransformerFactory::TransformExpression(PEGTransformer &transformer, - unique_ptr lambda_arrow_expression) { - return lambda_arrow_expression; -} - unique_ptr PEGTransformerFactory::TransformColumnDefaultExpr(PEGTransformer &transformer, unique_ptr col_def_or_expr) { @@ -683,6 +669,7 @@ PEGTransformerFactory::TransformLogicalNotExpression(PEGTransformer &transformer if (!not_expression) { return expr; } + auto not_depth_guard = transformer.StackCheck(not_expression->size()); for (idx_t i = 0; i < not_expression->size(); i++) { vector> inner_list_children; inner_list_children.push_back(std::move(expr)); @@ -696,31 +683,119 @@ vector PEGTransformerFactory::TransformNotExpression(PEGTransformer &trans return not_keyword; } +static unique_ptr ApplyIsTest(unique_ptr expr, + unique_ptr is_expr) { + if (is_expr->GetExpressionClass() == ExpressionClass::COMPARISON) { + auto compare_expr = unique_ptr_cast(std::move(is_expr)); + compare_expr->LeftMutable() = make_uniq(LogicalType::BOOLEAN, std::move(expr)); + return std::move(compare_expr); + } + if (is_expr->GetExpressionClass() == ExpressionClass::OPERATOR) { + auto operator_expr = unique_ptr_cast(std::move(is_expr)); + operator_expr->GetChildrenMutable().insert(operator_expr->GetChildrenMutable().begin(), std::move(expr)); + return std::move(operator_expr); + } + throw InternalException("Unexpected expression encountered in IsExpression: %s", + ExpressionClassToString(is_expr->GetExpressionClass())); +} + +static unique_ptr ApplyIsDistinctFromTail(unique_ptr expr, + IsDistinctFromTail tail) { + return make_uniq(tail.comparison_type, std::move(expr), std::move(tail.expression)); +} + +static unique_ptr ApplyComparisonTail(unique_ptr expr, + ComparisonExpressionTail tail) { + auto right_expr = std::move(tail.expression); + for (idx_t i = 0; i < tail.not_keywords.size(); i++) { + vector> inner_list_children; + inner_list_children.push_back(std::move(right_expr)); + right_expr = make_uniq(ExpressionType::OPERATOR_NOT, std::move(inner_list_children)); + } + return make_uniq(tail.comparison_type, std::move(expr), std::move(right_expr)); +} + unique_ptr PEGTransformerFactory::TransformIsExpression(PEGTransformer &transformer, unique_ptr is_distinct_from_expression, - optional>> is_test) { + optional> is_expression_continuation) { auto expr = std::move(is_distinct_from_expression); - if (!is_test) { + if (!is_expression_continuation) { return expr; } - for (auto &is_expr : *is_test) { - if (is_expr->GetExpressionClass() == ExpressionClass::COMPARISON) { - auto compare_expr = unique_ptr_cast(std::move(is_expr)); - compare_expr->LeftMutable() = make_uniq(LogicalType::BOOLEAN, std::move(expr)); - expr = std::move(compare_expr); - } else if (is_expr->GetExpressionClass() == ExpressionClass::OPERATOR) { - auto operator_expr = unique_ptr_cast(std::move(is_expr)); - operator_expr->GetChildrenMutable().insert(operator_expr->GetChildrenMutable().begin(), std::move(expr)); - expr = std::move(operator_expr); - } else { - throw InternalException("Unexpected expression encountered in IsExpression: %s", - ExpressionClassToString(is_expr->GetExpressionClass())); + auto previous_type = ExpressionTailType::IS_TEST; + for (auto &tail : *is_expression_continuation) { + if (tail.type == previous_type && + (tail.type == ExpressionTailType::DISTINCT || tail.type == ExpressionTailType::COMPARISON)) { + throw ParserException("Chained comparisons are not supported, use AND to combine comparisons"); } + switch (tail.type) { + case ExpressionTailType::IS_TEST: + expr = ApplyIsTest(std::move(expr), std::move(tail.test)); + break; + case ExpressionTailType::DISTINCT: + expr = ApplyIsDistinctFromTail(std::move(expr), std::move(tail.distinct)); + break; + case ExpressionTailType::COMPARISON: + expr = ApplyComparisonTail(std::move(expr), std::move(tail.comparison)); + break; + case ExpressionTailType::OTHER_OPERATOR: { + vector other_tail; + other_tail.push_back(std::move(tail.other)); + expr = TransformInfixOtherOperatorExpression(transformer, std::move(expr), std::move(other_tail)); + break; + } + } + previous_type = tail.type; } return expr; } +vector +PEGTransformerFactory::TransformIsExpressionContinuation(PEGTransformer &transformer, + unique_ptr is_test, + optional> is_expression_tail) { + vector result; + result.push_back(TransformIsTestTail(transformer, std::move(is_test))); + if (is_expression_tail) { + for (auto &tail : *is_expression_tail) { + result.push_back(std::move(tail)); + } + } + return result; +} + +IsExpressionTail PEGTransformerFactory::TransformIsTestTail(PEGTransformer &transformer, + unique_ptr is_test) { + IsExpressionTail result; + result.test = std::move(is_test); + return result; +} + +IsExpressionTail PEGTransformerFactory::TransformIsDistinctTail(PEGTransformer &transformer, + IsDistinctFromTail is_distinct_from_tail) { + IsExpressionTail result; + result.type = ExpressionTailType::DISTINCT; + result.distinct = std::move(is_distinct_from_tail); + return result; +} + +IsExpressionTail PEGTransformerFactory::TransformIsComparisonTail(PEGTransformer &transformer, + ComparisonExpressionTail comparison_expression_tail) { + IsExpressionTail result; + result.type = ExpressionTailType::COMPARISON; + result.comparison = std::move(comparison_expression_tail); + return result; +} + +IsExpressionTail PEGTransformerFactory::TransformIsOtherOperatorTail(PEGTransformer &transformer, + OtherOperatorTail other_operator_tail) { + IsExpressionTail result; + result.type = ExpressionTailType::OTHER_OPERATOR; + result.other = std::move(other_operator_tail); + return result; +} + unique_ptr PEGTransformerFactory::TransformIsLiteral(PEGTransformer &transformer, const bool &has_result, const Value &is_literal_value) { @@ -765,9 +840,7 @@ PEGTransformerFactory::TransformIsDistinctFromExpression(PEGTransformer &transfo throw ParserException("Chained comparisons are not supported, use AND to combine comparisons"); } for (auto &is_distinct : *is_distinct_from_tail) { - auto distinct_operator = make_uniq(is_distinct.comparison_type, std::move(expr), - std::move(is_distinct.expression)); - expr = std::move(distinct_operator); + expr = ApplyIsDistinctFromTail(std::move(expr), std::move(is_distinct)); } return expr; } @@ -784,13 +857,7 @@ unique_ptr PEGTransformerFactory::TransformComparisonExpressio } auto cmp_depth_guard = transformer.StackCheck(comparison_expression_tail->size()); for (auto &comparison_expr : *comparison_expression_tail) { - auto right_expr = std::move(comparison_expr.expression); - for (idx_t i = 0; i < comparison_expr.not_keywords.size(); i++) { - vector> inner_list_children; - inner_list_children.push_back(std::move(right_expr)); - right_expr = make_uniq(ExpressionType::OPERATOR_NOT, std::move(inner_list_children)); - } - expr = make_uniq(comparison_expr.comparison_type, std::move(expr), std::move(right_expr)); + expr = ApplyComparisonTail(std::move(expr), std::move(comparison_expr)); } return expr; } @@ -991,16 +1058,10 @@ static unique_ptr TransformRegexAnyAllList(unique_ptr -PEGTransformerFactory::TransformBetweenInLikeExpression(PEGTransformer &transformer, - unique_ptr other_operator_expression, - optional between_in_like_op) { - auto expr = std::move(other_operator_expression); - if (!between_in_like_op) { - return expr; - } - auto between_in_like_expr = std::move(between_in_like_op->expression); - bool has_not = between_in_like_op->has_not; +static unique_ptr ApplyBetweenInLikeOperator(unique_ptr expr, + BetweenInLikeOperator &predicate) { + auto between_in_like_expr = std::move(predicate.expression); + bool has_not = predicate.has_not; if (between_in_like_expr->GetExpressionClass() == ExpressionClass::BETWEEN) { auto between_expr = unique_ptr_cast(std::move(between_in_like_expr)); between_expr->InputMutable() = std::move(expr); @@ -1054,13 +1115,44 @@ PEGTransformerFactory::TransformBetweenInLikeExpression(PEGTransformer &transfor return expr; } +unique_ptr PEGTransformerFactory::TransformBetweenInLikeExpression( + PEGTransformer &transformer, unique_ptr other_operator_expression, + optional> in_predicate, optional between_like_op) { + auto expr = std::move(other_operator_expression); + if (!in_predicate && !between_like_op) { + return expr; + } + auto predicate_count = in_predicate ? in_predicate->size() : 0; + if (between_like_op) { + predicate_count++; + } + auto depth_guard = transformer.StackCheck(predicate_count); + if (in_predicate) { + for (auto &predicate : *in_predicate) { + expr = ApplyBetweenInLikeOperator(std::move(expr), predicate); + } + } + if (between_like_op) { + expr = ApplyBetweenInLikeOperator(std::move(expr), *between_like_op); + } + return expr; +} + +BetweenInLikeOperator PEGTransformerFactory::TransformInPredicate(PEGTransformer &transformer, const bool &has_result, + unique_ptr in_clause) { + return {has_result, std::move(in_clause)}; +} + BetweenInLikeOperator -PEGTransformerFactory::TransformBetweenInLikeOp(PEGTransformer &transformer, const bool &has_result, - unique_ptr between_in_like_op_expression) { - BetweenInLikeOperator result; - result.has_not = has_result; - result.expression = std::move(between_in_like_op_expression); - return result; +PEGTransformerFactory::TransformBetweenLikeOp(PEGTransformer &transformer, const bool &has_result, + unique_ptr between_like_op_expression) { + if (has_result && between_like_op_expression->GetExpressionClass() == ExpressionClass::FUNCTION) { + auto &function_name = between_like_op_expression->Cast().FunctionName(); + if (StringUtil::StartsWith(function_name.GetIdentifierName(), "!")) { + throw ParserException("NOT cannot precede an operator that is already negated (!~~, !~~*, !~ or !~*)"); + } + } + return {has_result, std::move(between_like_op_expression)}; } unique_ptr PEGTransformerFactory::TransformInClause(PEGTransformer &transformer, @@ -1103,7 +1195,7 @@ PEGTransformerFactory::TransformInSelectStatement(PEGTransformer &transformer, } unique_ptr -PEGTransformerFactory::TransformBetweenClause(PEGTransformer &transformer, +PEGTransformerFactory::TransformBetweenClause(PEGTransformer &transformer, const bool &, unique_ptr other_operator_expression, unique_ptr other_operator_expression_1) { auto result = make_uniq(nullptr, std::move(other_operator_expression), @@ -1144,8 +1236,8 @@ PEGTransformerFactory::TransformLikeClause(PEGTransformer &transformer, const st unique_ptr PEGTransformerFactory::TransformEscapeClause(PEGTransformer &transformer, - unique_ptr comparison_expression) { - return comparison_expression; + unique_ptr other_operator_expression) { + return other_operator_expression; } string PEGTransformerFactory::TransformLikeToken(PEGTransformer &transformer) { @@ -1188,10 +1280,31 @@ string PEGTransformerFactory::TransformNotSimilarToOp(PEGTransformer &transforme return "!" + RegexMatchOperatorFunctionName(transformer); } +static unique_ptr TransformOperatorFunction(const string &operator_name, + vector> children) { + vector split_operator = StringUtil::Split(operator_name, "."); + string schema_name; + string function_name; + if (split_operator.size() == 1) { + function_name = std::move(split_operator[0]); + } else if (split_operator.size() == 2) { + schema_name = std::move(split_operator[0]); + function_name = std::move(split_operator[1]); + } else { + throw ParserException("Too many identifiers found, expected schema.operator or operator"); + } + + auto result = make_uniq( + QualifiedName(Identifier(), Identifier(std::move(schema_name)), Identifier(std::move(function_name))), + std::move(children)); + result->IsOperatorMutable() = true; + return std::move(result); +} + unique_ptr -PEGTransformerFactory::TransformOtherOperatorExpression(PEGTransformer &transformer, - unique_ptr bitwise_expression, - optional> other_operator_tail) { +PEGTransformerFactory::TransformInfixOtherOperatorExpression(PEGTransformer &transformer, + unique_ptr bitwise_expression, + optional> other_operator_tail) { auto expr = std::move(bitwise_expression); if (!other_operator_tail) { return expr; @@ -1266,28 +1379,20 @@ PEGTransformerFactory::TransformOtherOperatorExpression(PEGTransformer &transfor vector> children_function; children_function.push_back(std::move(expr)); children_function.push_back(std::move(right_expr)); - vector split_operator = StringUtil::Split(other_operator, "."); - string schema_name; - string func_name = ""; - if (split_operator.size() == 1) { - func_name = split_operator[0]; - } else if (split_operator.size() == 2) { - schema_name = split_operator[0]; - func_name = split_operator[1]; - } else { - throw ParserException("Too many identifiers found, expected schema.operator or operator"); - } - - auto func_expr = make_uniq( - QualifiedName(Identifier(), Identifier(std::move(schema_name)), Identifier(std::move(func_name))), - std::move(children_function)); - func_expr->IsOperatorMutable() = true; - expr = std::move(func_expr); + expr = TransformOperatorFunction(other_operator, std::move(children_function)); } } return expr; } +unique_ptr +PEGTransformerFactory::TransformCustomPrefixExpression(PEGTransformer &transformer, const string &operator_literal, + unique_ptr other_operator_expression) { + vector> children; + children.push_back(std::move(other_operator_expression)); + return TransformOperatorFunction(operator_literal, std::move(children)); +} + OtherOperatorTail PEGTransformerFactory::TransformOtherOperatorTail(PEGTransformer &transformer, ParsedOperator other_operator, unique_ptr bitwise_expression) { @@ -1319,18 +1424,19 @@ string PEGTransformerFactory::TransformQualifiedOperator(PEGTransformer &transfo string PEGTransformerFactory::TransformQualifiedOperatorContents(PEGTransformer &transformer, const optional> &col_id_dot, - const string &any_op) { + const string &any_operator_literal) { vector result; if (col_id_dot) { result = *col_id_dot; } - result.push_back(any_op); + result.push_back(any_operator_literal); return StringUtil::Join(result, "."); } -pair PEGTransformerFactory::TransformAnyAllOperator(PEGTransformer &transformer, const string &any_op, +pair PEGTransformerFactory::TransformAnyAllOperator(PEGTransformer &transformer, + const string &any_operator_literal, const bool &any_or_all) { - return make_pair(any_op, any_or_all); + return make_pair(any_operator_literal, any_or_all); } bool PEGTransformerFactory::TransformSubqueryAny(PEGTransformer &transformer) { @@ -1343,9 +1449,9 @@ bool PEGTransformerFactory::TransformSubqueryAll(PEGTransformer &transformer) { unique_ptr PEGTransformerFactory::TransformBitwiseExpression(PEGTransformer &transformer, - unique_ptr additive_expression, + unique_ptr tilde_expression, optional> bitwise_expression_tail) { - auto expr = std::move(additive_expression); + auto expr = std::move(tilde_expression); if (!bitwise_expression_tail) { return expr; } @@ -1428,8 +1534,25 @@ unique_ptr PEGTransformerFactory::TransformExponentiationExpre BinaryExpressionTail PEGTransformerFactory::TransformBitwiseExpressionTail(PEGTransformer &transformer, const string &bit_operator, - unique_ptr additive_expression) { - return {bit_operator, std::move(additive_expression), optional_idx()}; + unique_ptr tilde_expression) { + return {bit_operator, std::move(tilde_expression), optional_idx()}; +} + +unique_ptr +PEGTransformerFactory::TransformTildeExpression(PEGTransformer &transformer, + optional> tilde_prefix_operator, + unique_ptr additive_expression) { + auto expr = std::move(additive_expression); + if (!tilde_prefix_operator) { + return expr; + } + auto tilde_depth_guard = transformer.StackCheck(tilde_prefix_operator->size()); + for (const auto &prefix : *tilde_prefix_operator) { + vector> children; + children.push_back(std::move(expr)); + expr = TransformOperatorFunction(prefix, std::move(children)); + } + return expr; } BinaryExpressionTail @@ -1504,46 +1627,6 @@ PEGTransformerFactory::TransformAtTimeZoneExpressionTail(PEGTransformer &transfo return prefix_expression; } -// PrefixExpression <- PrefixOperator* BaseExpression -unique_ptr PEGTransformerFactory::TransformPrefixExpression(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &prefix_opt = list_pr.Child(0); - auto &base_expr_pr = list_pr.Child(1); - - if (!prefix_opt.HasResult()) { - return transformer.Transform>(base_expr_pr); - } - - auto &prefix_repeat = prefix_opt.GetResult().Cast(); - auto expr = transformer.Transform>(base_expr_pr); - - vector prefixes; - for (auto &child_ref : prefix_repeat.GetChildren()) { - prefixes.push_back(transformer.Transform(child_ref)); - } - - for (auto it = prefixes.rbegin(); it != prefixes.rend(); ++it) { - const string &prefix = *it; - - if (prefix == "-" && expr->GetExpressionType() == ExpressionType::VALUE_CONSTANT) { - // fold the sign into the number text so -9223372036854775808 stays exact - auto &literal = expr->Cast().GetLiteral(); - if (literal.IsNumeric()) { - expr = make_uniq(literal.Negate()); - continue; - } - } - - vector> children; - children.push_back(std::move(expr)); - auto func_expr = make_uniq(Identifier(prefix), std::move(children)); - func_expr->IsOperatorMutable() = true; - expr = std::move(func_expr); - } - return expr; -} - void PEGTransformerFactory::InitializePrefixExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -1564,7 +1647,7 @@ void PEGTransformerFactory::InitializePrefixExpressionTrampoline(PEGTransformer process.PushChild({list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePrefixExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -1572,7 +1655,7 @@ PEGTransformerFactory::FinalizePrefixExpressionTrampoline(PEGTransformer &transf auto expr = process.TakeResult>(0); if (!prefix_opt.HasResult()) { - return make_uniq>>(std::move(expr)); + return transformer.MakeResult>(std::move(expr)); } auto &prefix_repeat = prefix_opt.GetResult().Cast(); @@ -1598,7 +1681,7 @@ PEGTransformerFactory::FinalizePrefixExpressionTrampoline(PEGTransformer &transf func_expr->IsOperatorMutable() = true; expr = std::move(func_expr); } - return make_uniq>>(std::move(expr)); + return transformer.MakeResult>(std::move(expr)); } unique_ptr PEGTransformerFactory::TransformAnonymousParameter(PEGTransformer &transformer) { // AnonymousParameter <- '?' @@ -1705,7 +1788,7 @@ PEGTransformerFactory::TransformPositionalExpression(PEGTransformer &transformer // LiteralExpression <- StringLiteral / NumberLiteral / 'NULL' / 'TRUE' / 'FALSE' unique_ptr PEGTransformerFactory::TransformLiteralExpression(PEGTransformer &transformer, ParseResult &choice_result) { - if (choice_result.name == "StringLiteral") { + if (choice_result.Name() == "StringLiteral") { auto &string_literal = choice_result.Cast(); return string_literal.ToExpression(); } @@ -1717,35 +1800,35 @@ void PEGTransformerFactory::InitializeLiteralExpressionTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLiteralExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto &choice_pr = list_pr.Child(0); auto &choice_result = choice_pr.GetResult(); unique_ptr result; - if (choice_result.name == "StringLiteral") { + if (choice_result.Name() == "StringLiteral") { auto &string_literal = choice_result.Cast(); result = string_literal.ToExpression(); - } else if (choice_result.name == "NumberLiteral") { + } else if (choice_result.Name() == "NumberLiteral") { result = TransformNumberLiteral(transformer, choice_result); } else { auto &constant_list_pr = choice_result.Cast(); auto &constant_choice_pr = constant_list_pr.Child(0); auto &constant_result = constant_choice_pr.GetResult(); Value value; - if (constant_result.name == "NullLiteral") { + if (constant_result.Name() == "NullLiteral") { value = TransformNullLiteral(transformer); - } else if (constant_result.name == "TrueLiteral") { + } else if (constant_result.Name() == "TrueLiteral") { value = TransformTrueLiteral(transformer); - } else if (constant_result.name == "FalseLiteral") { + } else if (constant_result.Name() == "FalseLiteral") { value = TransformFalseLiteral(transformer); } else { - throw InternalException("Unexpected literal expression process child '%s'", constant_result.name); + throw InternalException("Unexpected literal expression process child '%s'", constant_result.Name()); } result = TransformConstantLiteral(transformer, value); } - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } unique_ptr PEGTransformerFactory::TransformParensExpression(PEGTransformer &transformer, @@ -1807,7 +1890,7 @@ unique_ptr PEGTransformerFactory::TransformMethodExpression(PEGTransformer &transformer, const string &col_label, MethodArguments method_expression_arguments) { if (method_expression_arguments.arguments.size() == 1 && - ExpressionIsEmptyStar(method_expression_arguments.arguments[0].GetExpression())) { + ExpressionIsUnqualifiedStar(method_expression_arguments.arguments[0].GetExpression())) { // COUNT(*) gets converted into COUNT() method_expression_arguments.arguments.clear(); } @@ -1870,11 +1953,12 @@ PEGTransformerFactory::TransformSliceExpression(PEGTransformer &transformer, return make_uniq(ExpressionType::ARRAY_SLICE, std::move(slice_bound)); } -vector> PEGTransformerFactory::TransformSliceBound( - PEGTransformer &transformer, optional> expression, - optional> end_slice_bound, optional> step_slice_bound) { +vector> +PEGTransformerFactory::TransformSliceBound(PEGTransformer &transformer, + optional> expression, + optional>> slice_upper_bound) { vector> slice_bounds; - if (!end_slice_bound && !step_slice_bound) { + if (!slice_upper_bound) { if (expression && *expression) { slice_bounds.push_back(std::move(*expression)); } @@ -1885,11 +1969,27 @@ vector> PEGTransformerFactory::TransformSliceBound( } else { slice_bounds.push_back(OperatorExpression::EmptySliceBound()); } - if (end_slice_bound && *end_slice_bound) { - slice_bounds.push_back(std::move(*end_slice_bound)); - } else { - slice_bounds.push_back(OperatorExpression::EmptySliceBound()); + for (auto &bound : *slice_upper_bound) { + slice_bounds.push_back(std::move(bound)); } + return slice_bounds; +} + +vector> +PEGTransformerFactory::TransformMinusStepSliceBound(PEGTransformer &transformer, + unique_ptr step_slice_bound) { + vector> slice_bounds; + slice_bounds.push_back(OperatorExpression::EmptySliceBound()); + slice_bounds.push_back(std::move(step_slice_bound)); + return slice_bounds; +} + +vector> +PEGTransformerFactory::TransformEndStepSliceBound(PEGTransformer &transformer, + unique_ptr end_slice_bound, + optional> step_slice_bound) { + vector> slice_bounds; + slice_bounds.push_back(std::move(end_slice_bound)); if (step_slice_bound && *step_slice_bound) { slice_bounds.push_back(std::move(*step_slice_bound)); } @@ -1898,19 +1998,15 @@ vector> PEGTransformerFactory::TransformSliceBound( unique_ptr PEGTransformerFactory::TransformEndSliceBound(PEGTransformer &transformer, - optional> end_slice_value) { + optional> expression) { // If either the lower or upper bound is not specified, we use an empty constant LIST, // which we handle in the execution. - if (end_slice_value && *end_slice_value) { - return std::move(*end_slice_value); + if (expression && *expression) { + return std::move(*expression); } return OperatorExpression::EmptySliceBound(); } -unique_ptr PEGTransformerFactory::TransformEndSliceMinus(PEGTransformer &transformer) { - return OperatorExpression::EmptySliceBound(); -} - unique_ptr PEGTransformerFactory::TransformStepSliceBound(PEGTransformer &transformer, optional> expression) { @@ -2018,18 +2114,6 @@ QualifiedColumnName PEGTransformerFactory::TransformExcludeColumnName(PEGTransfo return QualifiedColumnName(col_id_or_string); } -unique_ptr PEGTransformerFactory::TransformOverClause(PEGTransformer &transformer, - ParseResult &parse_result) { - if (transformer.in_window_definition) { - throw ParserException("window functions are not allowed in window definitions"); - } - auto &list_pr = parse_result.Cast(); - transformer.in_window_definition = true; - auto window_frame = transformer.Transform>(list_pr.GetChild(1)); - transformer.in_window_definition = false; - return window_frame; -} - void PEGTransformerFactory::InitializeOverClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { if (transformer.in_window_definition) { @@ -2041,11 +2125,11 @@ void PEGTransformerFactory::InitializeOverClauseTrampoline(PEGTransformer &trans process.PushChild({list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOverClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); transformer.in_window_definition = false; - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } unique_ptr PEGTransformerFactory::TransformIdentifierWindowFrame(PEGTransformer &transformer, @@ -2154,6 +2238,15 @@ WindowFrame PEGTransformerFactory::TransformFrameClause(PEGTransformer &transfor vector frame_extent, const optional &window_exclude_clause) { WindowFrame result; + if (frame_extent[0].boundary == WindowBoundary::CURRENT_ROW_RANGE && + frame_extent[1].boundary == WindowBoundary::EXPR_PRECEDING_RANGE) { + throw ParserException("Frame starting from current row cannot have preceding rows"); + } + if (frame_extent[0].boundary == WindowBoundary::EXPR_FOLLOWING_RANGE && + (frame_extent[1].boundary == WindowBoundary::CURRENT_ROW_RANGE || + frame_extent[1].boundary == WindowBoundary::EXPR_PRECEDING_RANGE)) { + throw ParserException("Frame starting from following row cannot have preceding rows"); + } for (auto &frame : frame_extent) { if (StringUtil::CIEquals(framing, "rows")) { if (frame.boundary == WindowBoundary::CURRENT_ROW_RANGE) { @@ -2216,6 +2309,9 @@ PEGTransformerFactory::TransformBetweenFrameExtent(PEGTransformer &transformer, vector PEGTransformerFactory::TransformSingleFrameExtent(PEGTransformer &transformer, WindowBoundaryExpression frame_bound) { + if (frame_bound.boundary == WindowBoundary::EXPR_FOLLOWING_RANGE) { + throw ParserException("Frame starting from following row cannot end with current row"); + } vector result; result.push_back(std::move(frame_bound)); WindowBoundaryExpression end_current_row; diff --git a/src/duckdb/src/parser/peg/transformer/transform_external_resource.cpp b/src/duckdb/src/parser/peg/transformer/transform_external_resource.cpp index b31fa23b1..08a011847 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_external_resource.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_external_resource.cpp @@ -5,19 +5,12 @@ namespace duckdb { -//! Fold `(k v, ...)` create params onto the statement. A bare flag binds to boolean true, mirroring -//! ATTACH/CONNECT TO EXTERNAL RESOURCE option handling. +//! Fold `(k v, ...)` create params onto the statement; they are resolved at bind time. static void ApplyOptions(const optional> &options, ExternalResourceStatement &stmt) { if (!options) { return; } - for (const auto &opt : *options) { - if (!opt.expression && opt.children.empty()) { - stmt.options[opt.name.GetIdentifierName()] = ConstantExpression::Boolean(true); - } else { - stmt.options[opt.name.GetIdentifierName()] = opt.GetFirstChildOrExpression(); - } - } + PEGTransformerFactory::CollectGenericOptions(*options, stmt.options, "EXTERNAL RESOURCE"); } unique_ptr PEGTransformerFactory::TransformCreateExternalResourceStmt( diff --git a/src/duckdb/src/parser/peg/transformer/transform_external_resource_source.cpp b/src/duckdb/src/parser/peg/transformer/transform_external_resource_source.cpp new file mode 100644 index 000000000..a42b643a9 --- /dev/null +++ b/src/duckdb/src/parser/peg/transformer/transform_external_resource_source.cpp @@ -0,0 +1,70 @@ +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/parser/peg/ast/generic_copy_option.hpp" +#include "duckdb/parser/peg/transformer/peg_transformer.hpp" +#include "duckdb/parser/parsed_data/external_resource_options.hpp" +#include "duckdb/parser/statement/attach_statement.hpp" +#include "duckdb/parser/statement/connect_statement.hpp" + +namespace duckdb { + +// Both statements produce a normal ATTACH/CONNECT carrying an ExternalResourceOptions. `NEW TEMPORARY` +// provisions a resource the statement owns; a bare `` borrows a registered one. + +//! NEW TEMPORARY EXTERNAL RESOURCE '' [(create opts)] — provision a fresh resource. +unique_ptr PEGTransformerFactory::TransformExternalResourceCreateClause( + PEGTransformer &transformer, const string &string_literal, + const optional> &external_resource_creation_options) { + auto result = make_uniq(); + result->provider = string_literal; + if (external_resource_creation_options) { + CollectGenericOptions(*external_resource_creation_options, result->parsed_params, "EXTERNAL RESOURCE"); + } + return result; +} + +//! EXTERNAL RESOURCE — reference an already-registered resource. +unique_ptr +PEGTransformerFactory::TransformExternalResourceReferenceClause(PEGTransformer &transformer, const Identifier &col_id) { + auto result = make_uniq(); + result->reference_name = col_id.GetIdentifierName(); + return result; +} + +unique_ptr PEGTransformerFactory::TransformAttachToExternalResource( + PEGTransformer &transformer, unique_ptr external_resource_source, + const Identifier &attach_alias, const optional> &attach_options) { + auto result = make_uniq(); + auto info = make_uniq(); + info->on_conflict = OnCreateConflict::ERROR_ON_CONFLICT; + // The alias is mandatory in the grammar: it separates the create params from the attach options. + info->name = Identifier(attach_alias); + info->external_resource = std::move(external_resource_source); + if (attach_options) { + SplitGenericOptions(*attach_options, info->parsed_options, info->options, "ATTACH"); + } + result->info = std::move(info); + return std::move(result); +} + +//! CONNECT has no alias separating its options from the recipe's. Borrowing takes no create params, so +//! a list there is the connection's; provisioning claims the first, and a second is refused. +unique_ptr +PEGTransformerFactory::TransformConnectToExternalResource(PEGTransformer &transformer, + unique_ptr external_resource_source, + const optional> &attach_options) { + auto result = make_uniq(); + auto info = make_uniq(); + if (attach_options && external_resource_source->reference_name.empty()) { + throw ParserException("CONNECT TO NEW TEMPORARY EXTERNAL RESOURCE: the option list belongs to the resource, so " + "the connection cannot take one of its own. Use `ATTACH TO NEW TEMPORARY EXTERNAL " + "RESOURCE ... AS (options); CONNECT ;` instead"); + } + info->external_resource = std::move(external_resource_source); + if (attach_options) { + SplitGenericOptions(*attach_options, info->parsed_options, info->options, "CONNECT"); + } + result->info = std::move(info); + return std::move(result); +} + +} // namespace duckdb diff --git a/src/duckdb/src/parser/peg/transformer/transform_generated.cpp b/src/duckdb/src/parser/peg/transformer/transform_generated.cpp deleted file mode 100644 index f9efedb65..000000000 --- a/src/duckdb/src/parser/peg/transformer/transform_generated.cpp +++ /dev/null @@ -1,12706 +0,0 @@ -// AUTO-GENERATED by scripts/parser/generate_transformer.py -- DO NOT EDIT -#include "duckdb/parser/peg/transformer/peg_transformer.hpp" -#include "duckdb/parser/expression/star_expression.hpp" -#include "duckdb/parser/expression/columnref_expression.hpp" - -namespace duckdb { - -unique_ptr PEGTransformerFactory::TransformAlterStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto alter_options = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformAlterStatement(transformer, std::move(alter_options)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAlterOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAlterTableStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(1).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - auto base_table_name = transformer.Transform>(list_pr.GetChild(2)); - vector> alter_table_options; - auto alter_table_options_items = ExtractParseResultsFromList(list_pr.GetChild(3)); - for (auto &alter_table_options_item : alter_table_options_items) { - auto alter_table_options_value = - transformer.Transform>(alter_table_options_item.get()); - alter_table_options.push_back(std::move(alter_table_options_value)); - } - auto result = - TransformAlterTableStmt(transformer, if_exists, std::move(base_table_name), std::move(alter_table_options)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAlterSchemaStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(1).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - auto qualified_name = transformer.Transform(list_pr.GetChild(2)); - auto rename_alter = transformer.Transform>(list_pr.GetChild(3)); - auto result = TransformAlterSchemaStmt(transformer, if_exists, qualified_name, std::move(rename_alter)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAlterTableOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAddConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto top_level_constraint = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformAddConstraint(transformer, std::move(top_level_constraint)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDropConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(2).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - auto identifier = list_pr.GetChild(3).Cast().identifier; - optional drop_behavior {}; - auto &drop_behavior_opt = list_pr.GetChild(4).Cast(); - if (drop_behavior_opt.HasResult()) { - auto drop_behavior_value = transformer.Transform(drop_behavior_opt.GetResult()); - drop_behavior = drop_behavior_value; - } - auto result = TransformDropConstraint(transformer, if_exists, identifier, drop_behavior); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAddColumnInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - optional if_not_exists {}; - auto &if_not_exists_opt = list_pr.GetChild(2).Cast(); - if (if_not_exists_opt.HasResult()) { - auto if_not_exists_value = transformer.Transform(if_not_exists_opt.GetResult()); - if_not_exists = if_not_exists_value; - } - auto add_column_entry = transformer.Transform(list_pr.GetChild(3)); - auto result = TransformAddColumn(transformer, has_result, if_not_exists, std::move(add_column_entry)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAddColumnEntryInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto dotted_identifier = transformer.Transform>(list_pr.GetChild(0)); - optional type {}; - auto &type_opt = list_pr.GetChild(1).Cast(); - if (type_opt.HasResult()) { - auto type_value = transformer.Transform(type_opt.GetResult()); - type = type_value; - } - optional generated_column {}; - auto &generated_column_opt = list_pr.GetChild(2).Cast(); - if (generated_column_opt.HasResult()) { - auto generated_column_value = - transformer.Transform(generated_column_opt.GetResult()); - generated_column = std::move(generated_column_value); - } - optional> column_constraint {}; - auto &column_constraint_opt = list_pr.GetChild(3).Cast(); - if (column_constraint_opt.HasResult()) { - vector column_constraint_value; - auto &column_constraint_value_repeat_1 = column_constraint_opt.GetResult().Cast(); - for (auto &column_constraint_value_item_1 : column_constraint_value_repeat_1.GetChildren()) { - auto column_constraint_value_value_1 = - transformer.Transform(column_constraint_value_item_1.get()); - column_constraint_value.push_back(std::move(column_constraint_value_value_1)); - } - column_constraint = std::move(column_constraint_value); - } - auto result = TransformAddColumnEntry(transformer, dotted_identifier, type, std::move(generated_column), - std::move(column_constraint)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDropColumnInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(2).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - auto nested_column_name = transformer.Transform>(list_pr.GetChild(3)); - optional drop_behavior {}; - auto &drop_behavior_opt = list_pr.GetChild(4).Cast(); - if (drop_behavior_opt.HasResult()) { - auto drop_behavior_value = transformer.Transform(drop_behavior_opt.GetResult()); - drop_behavior = drop_behavior_value; - } - auto result = TransformDropColumn(transformer, has_result, if_exists, std::move(nested_column_name), drop_behavior); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAlterColumnInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto nested_column_name = transformer.Transform>(list_pr.GetChild(2)); - auto alter_column_entry = transformer.Transform>(list_pr.GetChild(3)); - auto result = - TransformAlterColumn(transformer, has_result, std::move(nested_column_name), std::move(alter_column_entry)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformRenameColumnInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto nested_column_name = transformer.Transform>(list_pr.GetChild(2)); - auto identifier = list_pr.GetChild(4).Cast().identifier; - auto result = TransformRenameColumn(transformer, has_result, std::move(nested_column_name), identifier); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNestedColumnNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> identifier_dot {}; - auto &identifier_dot_opt = list_pr.GetChild(0).Cast(); - if (identifier_dot_opt.HasResult()) { - vector identifier_dot_value; - auto &identifier_dot_value_repeat_1 = identifier_dot_opt.GetResult().Cast(); - for (auto &identifier_dot_value_item_1 : identifier_dot_value_repeat_1.GetChildren()) { - auto identifier_dot_value_value_1 = transformer.Transform(identifier_dot_value_item_1.get()); - identifier_dot_value.push_back(identifier_dot_value_value_1); - } - identifier_dot = identifier_dot_value; - } - auto column_name = list_pr.GetChild(1).Cast().identifier; - auto result = TransformNestedColumnName(transformer, identifier_dot, column_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIdentifierDotInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(0).Cast().identifier; - auto result = TransformIdentifierDot(transformer, identifier); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformRenameAlterInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(2).Cast().identifier; - auto result = TransformRenameAlter(transformer, identifier); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSetPartitionedByInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> expression; - auto expression_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(3))); - for (auto &expression_item : expression_items) { - auto expression_value = transformer.Transform>(expression_item.get()); - expression.push_back(std::move(expression_value)); - } - auto result = TransformSetPartitionedBy(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformResetPartitionedByInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformResetPartitionedBy(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSetSortedByInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto order_by_expressions = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(3))); - auto result = TransformSetSortedBy(transformer, std::move(order_by_expressions)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformResetSortedByInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformResetSortedBy(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSetOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto rel_option_list = - transformer.Transform>>(list_pr.GetChild(1)); - auto result = TransformSetOptions(transformer, std::move(rel_option_list)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformResetOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto rel_option_list = - transformer.Transform>>(list_pr.GetChild(1)); - auto result = TransformResetOptions(transformer, std::move(rel_option_list)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAlterColumnEntryInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAddOrDropDefaultInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAddDefaultInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformAddDefault(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDropDefaultInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformDropDefault(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformChangeNullabilityInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto drop_or_set = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformChangeNullability(transformer, drop_or_set); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDropOrSetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - string result; - if (choice_pr.GetResult().type == ParseResultType::IDENTIFIER) { - result = choice_pr.GetResult().Cast().identifier.GetIdentifierName(); - } else if (choice_pr.GetResult().type == ParseResultType::KEYWORD) { - result = choice_pr.GetResult().Cast().keyword; - } else if (choice_pr.GetResult().type == ParseResultType::STRING) { - result = choice_pr.GetResult().Cast().result; - } else if (choice_pr.GetResult().type == ParseResultType::OPERATOR) { - result = choice_pr.GetResult().Cast().operator_token; - } else { - result = transformer.Transform(choice_pr.GetResult()); - } - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDropNullabilityInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformDropNullability(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSetNullabilityInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSetNullability(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformAlterTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(0).Cast(); - has_result = has_result_opt.HasResult(); - optional type {}; - auto &type_opt = list_pr.GetChild(2).Cast(); - if (type_opt.HasResult()) { - auto type_value = transformer.Transform(type_opt.GetResult()); - type = type_value; - } - optional> using_expression {}; - auto &using_expression_opt = list_pr.GetChild(3).Cast(); - if (using_expression_opt.HasResult()) { - auto using_expression_value = - transformer.Transform>(using_expression_opt.GetResult()); - using_expression = std::move(using_expression_value); - } - auto result = TransformAlterType(transformer, has_result, type, std::move(using_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformUsingExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformUsingExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAlterViewStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(1).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - auto base_table_name = transformer.Transform>(list_pr.GetChild(2)); - auto rename_alter = transformer.Transform>(list_pr.GetChild(3)); - auto result = TransformAlterViewStmt(transformer, if_exists, std::move(base_table_name), std::move(rename_alter)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAlterSequenceStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(1).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - auto qualified_sequence_name = transformer.Transform(list_pr.GetChild(2)); - auto alter_sequence_options = transformer.Transform>(list_pr.GetChild(3)); - auto result = - TransformAlterSequenceStmt(transformer, if_exists, qualified_sequence_name, std::move(alter_sequence_options)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformQualifiedSequenceNameInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional catalog_qualification {}; - auto &catalog_qualification_opt = list_pr.GetChild(0).Cast(); - if (catalog_qualification_opt.HasResult()) { - auto catalog_qualification_value = transformer.Transform(catalog_qualification_opt.GetResult()); - catalog_qualification = catalog_qualification_value; - } - optional schema_qualification {}; - auto &schema_qualification_opt = list_pr.GetChild(1).Cast(); - if (schema_qualification_opt.HasResult()) { - auto schema_qualification_value = transformer.Transform(schema_qualification_opt.GetResult()); - schema_qualification = schema_qualification_value; - } - auto sequence_name = list_pr.GetChild(2).Cast().identifier; - auto result = - TransformQualifiedSequenceName(transformer, catalog_qualification, schema_qualification, sequence_name); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformAlterSequenceOptionsInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformRenameAlterSequenceOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto rename_alter = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformRenameAlterSequenceOptions(transformer, std::move(rename_alter)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSetSequenceOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector>> sequence_option; - auto &sequence_option_repeat = list_pr.GetChild(0).Cast(); - for (auto &sequence_option_item : sequence_option_repeat.GetChildren()) { - auto sequence_option_value = - transformer.Transform>>(sequence_option_item.get()); - sequence_option.push_back(std::move(sequence_option_value)); - } - auto result = TransformSetSequenceOption(transformer, std::move(sequence_option)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAlterDatabaseStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(1).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - auto identifier = list_pr.GetChild(2).Cast().identifier; - auto identifier_1 = list_pr.GetChild(6).Cast().identifier; - auto result = TransformAlterDatabaseStmt(transformer, if_exists, identifier, identifier_1); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAnalyzeStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto analyze_keyword = transformer.Transform(list_pr.GetChild(0)); - optional analyze_verbose {}; - auto &analyze_verbose_opt = list_pr.GetChild(1).Cast(); - if (analyze_verbose_opt.HasResult()) { - auto analyze_verbose_value = transformer.Transform(analyze_verbose_opt.GetResult()); - analyze_verbose = analyze_verbose_value; - } - optional analyze_target {}; - auto &analyze_target_opt = list_pr.GetChild(2).Cast(); - if (analyze_target_opt.HasResult()) { - auto analyze_target_value = transformer.Transform(analyze_target_opt.GetResult()); - analyze_target = std::move(analyze_target_value); - } - auto result = TransformAnalyzeStatement(transformer, analyze_keyword, analyze_verbose, std::move(analyze_target)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAnalyzeTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto base_table_name = transformer.Transform>(list_pr.GetChild(0)); - optional> name_list {}; - auto &name_list_opt = list_pr.GetChild(1).Cast(); - if (name_list_opt.HasResult()) { - auto name_list_value = transformer.Transform>(name_list_opt.GetResult()); - name_list = name_list_value; - } - auto result = TransformAnalyzeTarget(transformer, std::move(base_table_name), name_list); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAnalyzeVerboseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformAnalyzeVerbose(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformAttachStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional or_replace {}; - auto &or_replace_opt = list_pr.GetChild(1).Cast(); - if (or_replace_opt.HasResult()) { - auto or_replace_value = transformer.Transform(or_replace_opt.GetResult()); - or_replace = or_replace_value; - } - optional if_not_exists {}; - auto &if_not_exists_opt = list_pr.GetChild(2).Cast(); - if (if_not_exists_opt.HasResult()) { - auto if_not_exists_value = transformer.Transform(if_not_exists_opt.GetResult()); - if_not_exists = if_not_exists_value; - } - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(3).Cast(); - has_result = has_result_opt.HasResult(); - auto database_path = transformer.Transform>(list_pr.GetChild(4)); - optional attach_alias {}; - auto &attach_alias_opt = list_pr.GetChild(5).Cast(); - if (attach_alias_opt.HasResult()) { - auto attach_alias_value = transformer.Transform(attach_alias_opt.GetResult()); - attach_alias = attach_alias_value; - } - optional> attach_options {}; - auto &attach_options_opt = list_pr.GetChild(6).Cast(); - if (attach_options_opt.HasResult()) { - auto attach_options_value = transformer.Transform>(attach_options_opt.GetResult()); - attach_options = attach_options_value; - } - auto result = TransformAttachStatement(transformer, or_replace, if_not_exists, has_result, std::move(database_path), - attach_alias, attach_options); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDatabasePathInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformDatabasePath(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAttachAliasInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformAttachAlias(transformer, col_id); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformAttachOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto generic_copy_option_list = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformAttachOptions(transformer, generic_copy_option_list); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformCallStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto qualified_table_function = transformer.Transform(list_pr.GetChild(1)); - auto table_function_arguments = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformCallStatement(transformer, qualified_table_function, std::move(table_function_arguments)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCheckpointStatementInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional checkpoint_force {}; - auto &checkpoint_force_opt = list_pr.GetChild(0).Cast(); - if (checkpoint_force_opt.HasResult()) { - auto checkpoint_force_value = transformer.Transform(checkpoint_force_opt.GetResult()); - checkpoint_force = checkpoint_force_value; - } - optional catalog_name {}; - auto &catalog_name_opt = list_pr.GetChild(2).Cast(); - if (catalog_name_opt.HasResult()) { - auto catalog_name_value = catalog_name_opt.GetResult().Cast().identifier; - catalog_name = catalog_name_value; - } - auto result = TransformCheckpointStatement(transformer, checkpoint_force, catalog_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCheckpointForceInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCheckpointForce(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCommentStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto comment_on_type = transformer.Transform(list_pr.GetChild(2)); - auto dotted_identifier = transformer.Transform>(list_pr.GetChild(3)); - auto comment_value = transformer.Transform(list_pr.GetChild(5)); - auto result = TransformCommentStatement(transformer, comment_on_type, dotted_identifier, comment_value); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCommentOnTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCommentTableInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCommentTable(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCommentSequenceInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCommentSequence(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCommentFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCommentFunction(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCommentMacroTableInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCommentMacroTable(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCommentMacroInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCommentMacro(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCommentViewInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCommentView(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCommentDatabaseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCommentDatabase(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCommentIndexInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCommentIndex(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCommentSchemaInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCommentSchema(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCommentTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCommentType(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCommentColumnInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCommentColumn(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCommentValueInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformStringLiteralValueInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto string_literal = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformStringLiteralValue(transformer, string_literal); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformAnalyzeKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformAnalyzeKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformExpressionStatementInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> expression_alias; - auto expression_alias_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - for (auto &expression_alias_item : expression_alias_items) { - auto expression_alias_value = transformer.Transform>(expression_alias_item.get()); - expression_alias.push_back(std::move(expression_alias_value)); - } - auto result = TransformExpressionStatement(transformer, std::move(expression_alias)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformExpressionAliasInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformConstraintNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id_or_string = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformConstraintName(transformer, col_id_or_string); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCollationNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(0).Cast().identifier; - auto result = TransformCollationName(transformer, identifier); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto type_variations = transformer.Transform>(list_pr.GetChild(0)); - optional> array_bounds {}; - auto &array_bounds_opt = list_pr.GetChild(1).Cast(); - if (array_bounds_opt.HasResult()) { - vector array_bounds_value; - auto &array_bounds_value_repeat_1 = array_bounds_opt.GetResult().Cast(); - for (auto &array_bounds_value_item_1 : array_bounds_value_repeat_1.GetChildren()) { - auto array_bounds_value_value_1 = transformer.Transform(array_bounds_value_item_1.get()); - array_bounds_value.push_back(array_bounds_value_value_1); - } - array_bounds = array_bounds_value; - } - auto result = TransformType(transformer, std::move(type_variations), array_bounds); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTypeVariationsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSimpleTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCharacterSimpleTypeInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional>> type_modifiers {}; - auto &type_modifiers_opt = list_pr.GetChild(1).Cast(); - if (type_modifiers_opt.HasResult()) { - auto type_modifiers_value = - transformer.Transform>>(type_modifiers_opt.GetResult()); - type_modifiers = std::move(type_modifiers_value); - } - auto result = TransformCharacterSimpleType(transformer, std::move(type_modifiers)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformQualifiedSimpleTypeInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto qualified_type_name = transformer.Transform(list_pr.GetChild(0)); - optional>> type_modifiers {}; - auto &type_modifiers_opt = list_pr.GetChild(1).Cast(); - if (type_modifiers_opt.HasResult()) { - auto type_modifiers_value = - transformer.Transform>>(type_modifiers_opt.GetResult()); - type_modifiers = std::move(type_modifiers_value); - } - auto result = TransformQualifiedSimpleType(transformer, qualified_type_name, std::move(type_modifiers)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIntervalTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIntervalIntervalInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformIntervalWithSpecifierInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformIntervalWithRangeSpecifierInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto interval_to_interval_as_type = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformIntervalWithRangeSpecifier(transformer, interval_to_interval_as_type); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformIntervalWithSimpleSpecifierInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto interval = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformIntervalWithSimpleSpecifier(transformer, interval); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformIntervalWithoutSpecifierInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformIntervalWithoutSpecifier(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformYearKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformYearKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformMonthKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformMonthKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDayKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformDayKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformHourKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformHourKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformMinuteKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformMinuteKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSecondKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSecondKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformMillisecondKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformMillisecondKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformMicrosecondKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformMicrosecondKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformWeekKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformWeekKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformQuarterKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformQuarterKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDecadeKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformDecadeKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCenturyKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCenturyKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformMillenniumKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformMillenniumKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformIntervalInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformIntervalToIntervalInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformYearToMonthInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto year_keyword = transformer.Transform(list_pr.GetChild(0)); - auto month_keyword = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformYearToMonth(transformer, year_keyword, month_keyword); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDayToHourInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto day_keyword = transformer.Transform(list_pr.GetChild(0)); - auto hour_keyword = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformDayToHour(transformer, day_keyword, hour_keyword); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDayToMinuteInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto day_keyword = transformer.Transform(list_pr.GetChild(0)); - auto minute_keyword = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformDayToMinute(transformer, day_keyword, minute_keyword); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDayToSecondInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto day_keyword = transformer.Transform(list_pr.GetChild(0)); - auto second_keyword = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformDayToSecond(transformer, day_keyword, second_keyword); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformHourToMinuteInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto hour_keyword = transformer.Transform(list_pr.GetChild(0)); - auto minute_keyword = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformHourToMinute(transformer, hour_keyword, minute_keyword); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformHourToSecondInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto hour_keyword = transformer.Transform(list_pr.GetChild(0)); - auto second_keyword = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformHourToSecond(transformer, hour_keyword, second_keyword); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformMinuteToSecondInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto minute_keyword = transformer.Transform(list_pr.GetChild(0)); - auto second_keyword = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformMinuteToSecond(transformer, minute_keyword, second_keyword); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformBitTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - optional>> expression {}; - auto &expression_opt = list_pr.GetChild(2).Cast(); - if (expression_opt.HasResult()) { - vector> expression_value; - auto expression_value_items_1 = - ExtractParseResultsFromList(ExtractResultFromParens(expression_opt.GetResult())); - for (auto &expression_value_item_1 : expression_value_items_1) { - auto expression_value_value_1 = - transformer.Transform>(expression_value_item_1.get()); - expression_value.push_back(std::move(expression_value_value_1)); - } - expression = std::move(expression_value); - } - auto result = TransformBitType(transformer, has_result, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformGeometryTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> expression {}; - auto &expression_opt = list_pr.GetChild(1).Cast(); - if (expression_opt.HasResult()) { - auto expression_value = - transformer.Transform>(ExtractResultFromParens(expression_opt.GetResult())); - expression = std::move(expression_value); - } - auto result = TransformGeometryType(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformVariantTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformVariantType(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNumericTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSimpleNumericTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto &choice_result = choice_pr.GetResult(); - string child; - if (choice_result.type == ParseResultType::IDENTIFIER) { - child = choice_result.Cast().identifier.GetIdentifierName(); - } else if (choice_result.type == ParseResultType::KEYWORD) { - child = choice_result.Cast().keyword; - } else if (choice_result.type == ParseResultType::STRING) { - child = choice_result.Cast().result; - } else if (choice_result.type == ParseResultType::OPERATOR) { - child = choice_result.Cast().operator_token; - } else { - child = transformer.Transform(choice_result); - } - auto result = TransformSimpleNumericType(transformer, child); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDecimalNumericTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIntTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformIntType(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformIntegerTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformIntegerType(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSmallintTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSmallintType(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformBigintTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformBigintType(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformRealTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformRealType(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformBooleanTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformBooleanType(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDoubleTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformDoubleType(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformFloatTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> number_literal {}; - auto &number_literal_opt = list_pr.GetChild(1).Cast(); - if (number_literal_opt.HasResult()) { - auto number_literal_value = transformer.Transform>( - ExtractResultFromParens(number_literal_opt.GetResult())); - number_literal = std::move(number_literal_value); - } - auto result = TransformFloatType(transformer, std::move(number_literal)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDecimalTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional>> type_modifiers {}; - auto &type_modifiers_opt = list_pr.GetChild(1).Cast(); - if (type_modifiers_opt.HasResult()) { - auto type_modifiers_value = - transformer.Transform>>(type_modifiers_opt.GetResult()); - type_modifiers = std::move(type_modifiers_value); - } - auto result = TransformDecimalType(transformer, std::move(type_modifiers)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDecTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional>> type_modifiers {}; - auto &type_modifiers_opt = list_pr.GetChild(1).Cast(); - if (type_modifiers_opt.HasResult()) { - auto type_modifiers_value = - transformer.Transform>>(type_modifiers_opt.GetResult()); - type_modifiers = std::move(type_modifiers_value); - } - auto result = TransformDecType(transformer, std::move(type_modifiers)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNumericModTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional>> type_modifiers {}; - auto &type_modifiers_opt = list_pr.GetChild(1).Cast(); - if (type_modifiers_opt.HasResult()) { - auto type_modifiers_value = - transformer.Transform>>(type_modifiers_opt.GetResult()); - type_modifiers = std::move(type_modifiers_value); - } - auto result = TransformNumericModType(transformer, std::move(type_modifiers)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformQualifiedTypeNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformTypeNameAsQualifiedNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto type_name = list_pr.GetChild(0).Cast().identifier; - auto result = TransformTypeNameAsQualifiedName(transformer, type_name); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformCatalogReservedSchemaTypeNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto catalog_qualification = transformer.Transform(list_pr.GetChild(0)); - vector reserved_schema_qualification; - auto &reserved_schema_qualification_repeat = list_pr.GetChild(1).Cast(); - for (auto &reserved_schema_qualification_item : reserved_schema_qualification_repeat.GetChildren()) { - auto reserved_schema_qualification_value = - transformer.Transform(reserved_schema_qualification_item.get()); - reserved_schema_qualification.push_back(reserved_schema_qualification_value); - } - auto reserved_type_name = list_pr.GetChild(2).Cast().identifier; - auto result = TransformCatalogReservedSchemaTypeName(transformer, catalog_qualification, - reserved_schema_qualification, reserved_type_name); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformSchemaReservedTypeNameInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto schema_qualification = transformer.Transform(list_pr.GetChild(0)); - auto reserved_type_name = list_pr.GetChild(1).Cast().identifier; - auto result = TransformSchemaReservedTypeName(transformer, schema_qualification, reserved_type_name); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTypeModifiersInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional>> expression {}; - auto &expression_opt = ExtractResultFromParens(list_pr.GetChild(0)).Cast(); - if (expression_opt.HasResult()) { - vector> expression_value; - auto expression_value_items_1 = ExtractParseResultsFromList(expression_opt.GetResult()); - for (auto &expression_value_item_1 : expression_value_items_1) { - auto expression_value_value_1 = - transformer.Transform>(expression_value_item_1.get()); - expression_value.push_back(std::move(expression_value_value_1)); - } - expression = std::move(expression_value); - } - auto result = TransformTypeModifiers(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformRowTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> col_id_type_list {}; - auto &col_id_type_list_opt = list_pr.GetChild(1).Cast(); - if (col_id_type_list_opt.HasResult()) { - auto col_id_type_list_value = - transformer.Transform>(col_id_type_list_opt.GetResult()); - col_id_type_list = col_id_type_list_value; - } - auto result = TransformRowType(transformer, col_id_type_list); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSetofTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto type = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformSetofType(transformer, type); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformUnionTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id_type_list = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformUnionType(transformer, col_id_type_list); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformColIdTypeListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> col_id_type; - auto col_id_type_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &col_id_type_item : col_id_type_items) { - auto col_id_type_value = transformer.Transform>(col_id_type_item.get()); - col_id_type.push_back(col_id_type_value); - } - auto result = TransformColIdTypeList(transformer, col_id_type); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformMapTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> type {}; - auto &type_opt = list_pr.GetChild(1).Cast(); - if (type_opt.HasResult()) { - vector type_value; - auto type_value_items_1 = ExtractParseResultsFromList(ExtractResultFromParens(type_opt.GetResult())); - for (auto &type_value_item_1 : type_value_items_1) { - auto type_value_value_1 = transformer.Transform(type_value_item_1.get()); - type_value.push_back(type_value_value_1); - } - type = type_value; - } - auto result = TransformMapType(transformer, type); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTupleTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector type; - auto type_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(1))); - for (auto &type_item : type_items) { - auto type_value = transformer.Transform(type_item.get()); - type.push_back(type_value); - } - auto result = TransformTupleType(transformer, type); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformColIdTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id = transformer.Transform(list_pr.GetChild(0)); - auto type = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformColIdType(transformer, col_id, type); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformArrayBoundsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformArrayKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformArrayKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformArrayKeywordWithBoundsInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto square_brackets_array = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformArrayKeywordWithBounds(transformer, square_brackets_array); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformSquareBracketsArrayInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> expression {}; - auto &expression_opt = list_pr.GetChild(1).Cast(); - if (expression_opt.HasResult()) { - auto expression_value = transformer.Transform>(expression_opt.GetResult()); - expression = std::move(expression_value); - } - auto result = TransformSquareBracketsArray(transformer, std::move(expression)); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTimeTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto time_or_timestamp = transformer.Transform(list_pr.GetChild(0)); - optional>> type_modifiers {}; - auto &type_modifiers_opt = list_pr.GetChild(1).Cast(); - if (type_modifiers_opt.HasResult()) { - auto type_modifiers_value = - transformer.Transform>>(type_modifiers_opt.GetResult()); - type_modifiers = std::move(type_modifiers_value); - } - optional time_zone {}; - auto &time_zone_opt = list_pr.GetChild(2).Cast(); - if (time_zone_opt.HasResult()) { - auto time_zone_value = transformer.Transform(time_zone_opt.GetResult()); - time_zone = time_zone_value; - } - auto result = TransformTimeType(transformer, time_or_timestamp, std::move(type_modifiers), time_zone); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTimeOrTimestampInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTimeTypeIdInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformTimeTypeId(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTimestampTypeIdInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformTimestampTypeId(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTimeZoneInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto with_or_without = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformTimeZone(transformer, with_or_without); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformWithOrWithoutInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformWithRuleInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformWithRule(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformWithoutRuleInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformWithoutRule(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformConnectStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> session_target {}; - auto &session_target_opt = list_pr.GetChild(1).Cast(); - if (session_target_opt.HasResult()) { - auto session_target_value = transformer.Transform>(session_target_opt.GetResult()); - session_target = std::move(session_target_value); - } - auto result = TransformConnectStatement(transformer, std::move(session_target)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformDisconnectStatementInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto result = TransformDisconnectStatement(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSessionTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformLocalSessionTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformLocalSessionTarget(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformStringSessionTargetInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto string_literal = transformer.Transform(list_pr.GetChild(0)); - optional> generic_copy_option_list {}; - auto &generic_copy_option_list_opt = list_pr.GetChild(1).Cast(); - if (generic_copy_option_list_opt.HasResult()) { - auto generic_copy_option_list_value = - transformer.Transform>(generic_copy_option_list_opt.GetResult()); - generic_copy_option_list = generic_copy_option_list_value; - } - auto result = TransformStringSessionTarget(transformer, string_literal, generic_copy_option_list); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCatalogSessionTargetInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto catalog_name = list_pr.GetChild(0).Cast().identifier; - auto result = TransformCatalogSessionTarget(transformer, catalog_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCopyStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto copy_variations = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformCopyStatement(transformer, std::move(copy_variations)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCopyVariationsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCopyTableInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto base_table_name = transformer.Transform>(list_pr.GetChild(0)); - optional> insert_column_list {}; - auto &insert_column_list_opt = list_pr.GetChild(1).Cast(); - if (insert_column_list_opt.HasResult()) { - auto insert_column_list_value = transformer.Transform>(insert_column_list_opt.GetResult()); - insert_column_list = insert_column_list_value; - } - auto from_or_to = transformer.Transform(list_pr.GetChild(2)); - auto copy_file_name = transformer.Transform>(list_pr.GetChild(3)); - optional> copy_options {}; - auto ©_options_opt = list_pr.GetChild(4).Cast(); - if (copy_options_opt.HasResult()) { - auto copy_options_value = transformer.Transform>(copy_options_opt.GetResult()); - copy_options = copy_options_value; - } - auto result = TransformCopyTable(transformer, std::move(base_table_name), insert_column_list, from_or_to, - std::move(copy_file_name), copy_options); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformFromOrToInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCopyFromInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCopyFrom(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCopyToInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCopyTo(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCopySelectInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto select_statement_internal = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(0))); - auto copy_file_name = transformer.Transform>(list_pr.GetChild(2)); - optional> copy_options {}; - auto ©_options_opt = list_pr.GetChild(3).Cast(); - if (copy_options_opt.HasResult()) { - auto copy_options_value = transformer.Transform>(copy_options_opt.GetResult()); - copy_options = copy_options_value; - } - auto result = - TransformCopySelect(transformer, std::move(select_statement_internal), std::move(copy_file_name), copy_options); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCopyFileNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCopyFileNameExpressionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCopyFileNameStringLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto string_literal = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformCopyFileNameStringLiteral(transformer, string_literal); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCopyFileNameIdentifierInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(0).Cast().identifier; - auto result = TransformCopyFileNameIdentifier(transformer, identifier); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCopyFileNameIdentifierColIdInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier_col_id = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformCopyFileNameIdentifierColId(transformer, identifier_col_id); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIdentifierColIdInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(0).Cast().identifier; - auto col_id = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformIdentifierColId(transformer, identifier, col_id); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCopyOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(0).Cast(); - has_result = has_result_opt.HasResult(); - auto copy_option_list = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformCopyOptions(transformer, has_result, copy_option_list); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformCopyOptionListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(result); -} - -unique_ptr -PEGTransformerFactory::TransformSpecializedOptionListInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto specialized_option = transformer.Transform(list_pr.GetChild(0)); - optional> specialized_option_tail {}; - auto &specialized_option_tail_opt = list_pr.GetChild(1).Cast(); - if (specialized_option_tail_opt.HasResult()) { - vector specialized_option_tail_value; - auto &specialized_option_tail_value_repeat_1 = - specialized_option_tail_opt.GetResult().Cast(); - for (auto &specialized_option_tail_value_item_1 : specialized_option_tail_value_repeat_1.GetChildren()) { - auto specialized_option_tail_value_value_1 = - transformer.Transform(specialized_option_tail_value_item_1.get()); - specialized_option_tail_value.push_back(specialized_option_tail_value_value_1); - } - specialized_option_tail = specialized_option_tail_value; - } - auto result = TransformSpecializedOptionList(transformer, specialized_option, specialized_option_tail); - return make_uniq>>(result); -} - -unique_ptr -PEGTransformerFactory::TransformSpecializedOptionTailInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(0).Cast(); - has_result = has_result_opt.HasResult(); - auto specialized_option = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformSpecializedOptionTail(transformer, has_result, specialized_option); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSpecializedOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSingleOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformBinaryOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformBinaryOption(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformFreezeOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformFreezeOption(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformOidsOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformOidsOption(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCsvOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCsvOption(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformHeaderOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformHeaderOption(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformNullAsOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto string_literal = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformNullAsOption(transformer, has_result, string_literal); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDelimiterAsOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto string_literal = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformDelimiterAsOption(transformer, has_result, string_literal); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformQuoteAsOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto string_literal = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformQuoteAsOption(transformer, has_result, string_literal); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformEscapeAsOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto string_literal = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformEscapeAsOption(transformer, has_result, string_literal); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformEncodingOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto string_literal = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformEncodingOption(transformer, string_literal); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformForceQuoteOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional force_quote {}; - auto &force_quote_opt = list_pr.GetChild(0).Cast(); - if (force_quote_opt.HasResult()) { - auto force_quote_value = transformer.Transform(force_quote_opt.GetResult()); - force_quote = force_quote_value; - } - auto star_symbol_column_list = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformForceQuoteOption(transformer, force_quote, star_symbol_column_list); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformStarSymbolColumnListInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto &choice_result = choice_pr.GetResult(); - vector result {}; - if (!(choice_result.name == "StarSymbol")) { - result = transformer.Transform>(choice_result); - } - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformForceQuoteInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformForceQuote(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformPartitionByOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto partition_by_column_list = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformPartitionByOption(transformer, partition_by_column_list); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformPartitionByColumnListInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(result); -} - -unique_ptr -PEGTransformerFactory::TransformStarPartitionByColumnListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformStarPartitionByColumnList(transformer); - return make_uniq>>(result); -} - -unique_ptr -PEGTransformerFactory::TransformParenthesizedPartitionByColumnListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto column_list = transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = TransformParenthesizedPartitionByColumnList(transformer, column_list); - return make_uniq>>(result); -} - -unique_ptr -PEGTransformerFactory::TransformSinglePartitionByColumnListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformSinglePartitionByColumnList(transformer, col_id); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformForceNullOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional force_not_null {}; - auto &force_not_null_opt = list_pr.GetChild(1).Cast(); - if (force_not_null_opt.HasResult()) { - auto force_not_null_value = transformer.Transform(force_not_null_opt.GetResult()); - force_not_null = force_not_null_value; - } - auto column_list = transformer.Transform>(list_pr.GetChild(3)); - auto result = TransformForceNullOption(transformer, force_not_null, column_list); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformForceNotNullInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformForceNotNull(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformCopyGenericOptionListInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector copy_generic_option; - auto copy_generic_option_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto ©_generic_option_item : copy_generic_option_items) { - auto copy_generic_option_value = transformer.Transform(copy_generic_option_item.get()); - copy_generic_option.push_back(copy_generic_option_value); - } - auto result = TransformCopyGenericOptionList(transformer, copy_generic_option); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformCopyGenericOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformOrderByCopyOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional generic_copy_option_value {}; - auto &generic_copy_option_value_opt = list_pr.GetChild(2).Cast(); - if (generic_copy_option_value_opt.HasResult()) { - auto generic_copy_option_value_value = - transformer.Transform(generic_copy_option_value_opt.GetResult()); - generic_copy_option_value = std::move(generic_copy_option_value_value); - } - auto result = TransformOrderByCopyOption(transformer, std::move(generic_copy_option_value)); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformPartitionedByCopyOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional generic_copy_option_value {}; - auto &generic_copy_option_value_opt = list_pr.GetChild(2).Cast(); - if (generic_copy_option_value_opt.HasResult()) { - auto generic_copy_option_value_value = - transformer.Transform(generic_copy_option_value_opt.GetResult()); - generic_copy_option_value = std::move(generic_copy_option_value_value); - } - auto result = TransformPartitionedByCopyOption(transformer, std::move(generic_copy_option_value)); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformGenericCopyOptionListInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector generic_copy_option; - auto generic_copy_option_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &generic_copy_option_item : generic_copy_option_items) { - auto generic_copy_option_value = transformer.Transform(generic_copy_option_item.get()); - generic_copy_option.push_back(generic_copy_option_value); - } - auto result = TransformGenericCopyOptionList(transformer, generic_copy_option); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformGenericCopyOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto copy_option_name = list_pr.GetChild(0).Cast().identifier; - optional generic_copy_option_value {}; - auto &generic_copy_option_value_opt = list_pr.GetChild(1).Cast(); - if (generic_copy_option_value_opt.HasResult()) { - auto generic_copy_option_value_value = - transformer.Transform(generic_copy_option_value_opt.GetResult()); - generic_copy_option_value = std::move(generic_copy_option_value_value); - } - auto result = TransformGenericCopyOption(transformer, copy_option_name, std::move(generic_copy_option_value)); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformGenericCopyOptionValueInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformGenericCopyOptionOrderListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto generic_copy_option_parenthesized_expression_list = - transformer.Transform>(list_pr.GetChild(0)); - auto result = - TransformGenericCopyOptionOrderList(transformer, std::move(generic_copy_option_parenthesized_expression_list)); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformGenericCopyOptionExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformGenericCopyOptionExpression(transformer, std::move(expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformGenericCopyOptionParenthesizedExpressionListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto order_by_expression_list = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = - TransformGenericCopyOptionParenthesizedExpressionList(transformer, std::move(order_by_expression_list)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCopyFromDatabaseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCopyFromDatabaseWithFlagInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id = transformer.Transform(list_pr.GetChild(2)); - auto col_id_1 = transformer.Transform(list_pr.GetChild(4)); - auto copy_database_flag = transformer.Transform(list_pr.GetChild(5)); - auto result = TransformCopyFromDatabaseWithFlag(transformer, col_id, col_id_1, copy_database_flag); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCopyFromDatabaseWithoutFlagInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id = transformer.Transform(list_pr.GetChild(2)); - auto col_id_1 = transformer.Transform(list_pr.GetChild(4)); - auto result = TransformCopyFromDatabaseWithoutFlag(transformer, col_id, col_id_1); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCopyDatabaseFlagInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto schema_or_data = transformer.Transform(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = TransformCopyDatabaseFlag(transformer, schema_or_data); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSchemaOrDataInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCopySchemaInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCopySchema(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCopyDataInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCopyData(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCreateIndexStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional unique_index {}; - auto &unique_index_opt = list_pr.GetChild(0).Cast(); - if (unique_index_opt.HasResult()) { - auto unique_index_value = transformer.Transform(unique_index_opt.GetResult()); - unique_index = unique_index_value; - } - optional if_not_exists {}; - auto &if_not_exists_opt = list_pr.GetChild(2).Cast(); - if (if_not_exists_opt.HasResult()) { - auto if_not_exists_value = transformer.Transform(if_not_exists_opt.GetResult()); - if_not_exists = if_not_exists_value; - } - optional index_name {}; - auto &index_name_opt = list_pr.GetChild(3).Cast(); - if (index_name_opt.HasResult()) { - auto index_name_value = index_name_opt.GetResult().Cast().identifier; - index_name = index_name_value; - } - auto base_table_name = transformer.Transform>(list_pr.GetChild(5)); - optional> insert_column_list {}; - auto &insert_column_list_opt = list_pr.GetChild(6).Cast(); - if (insert_column_list_opt.HasResult()) { - auto insert_column_list_value = transformer.Transform>(insert_column_list_opt.GetResult()); - insert_column_list = insert_column_list_value; - } - optional index_type {}; - auto &index_type_opt = list_pr.GetChild(7).Cast(); - if (index_type_opt.HasResult()) { - auto index_type_value = transformer.Transform(index_type_opt.GetResult()); - index_type = index_type_value; - } - optional>> index_element {}; - auto &index_element_opt = list_pr.GetChild(8).Cast(); - if (index_element_opt.HasResult()) { - vector> index_element_value; - auto index_element_value_items_1 = - ExtractParseResultsFromList(ExtractResultFromParens(index_element_opt.GetResult())); - for (auto &index_element_value_item_1 : index_element_value_items_1) { - auto index_element_value_value_1 = - transformer.Transform>(index_element_value_item_1.get()); - index_element_value.push_back(std::move(index_element_value_value_1)); - } - index_element = std::move(index_element_value); - } - optional>> with_list {}; - auto &with_list_opt = list_pr.GetChild(9).Cast(); - if (with_list_opt.HasResult()) { - auto with_list_value = - transformer.Transform>>(with_list_opt.GetResult()); - with_list = std::move(with_list_value); - } - optional> where_clause {}; - auto &where_clause_opt = list_pr.GetChild(10).Cast(); - if (where_clause_opt.HasResult()) { - auto where_clause_value = transformer.Transform>(where_clause_opt.GetResult()); - where_clause = std::move(where_clause_value); - } - auto result = TransformCreateIndexStmt(transformer, unique_index, if_not_exists, index_name, - std::move(base_table_name), insert_column_list, index_type, - std::move(index_element), std::move(with_list), std::move(where_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformWithListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto rel_option_or_oids = - transformer.Transform>>(list_pr.GetChild(1)); - auto result = TransformWithList(transformer, std::move(rel_option_or_oids)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformRelOptionOrOidsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>>(choice_pr.GetResult()); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformRelOptionListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector>> rel_option; - auto rel_option_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &rel_option_item : rel_option_items) { - auto rel_option_value = - transformer.Transform>>(rel_option_item.get()); - rel_option.push_back(std::move(rel_option_value)); - } - auto result = TransformRelOptionList(transformer, std::move(rel_option)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOidsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto with_or_without_oids = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformOids(transformer, with_or_without_oids); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformWithOrWithoutOidsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformWithOidsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformWithOids(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformWithoutOidsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformWithoutOids(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformIndexElementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - optional desc_or_asc {}; - auto &desc_or_asc_opt = list_pr.GetChild(1).Cast(); - if (desc_or_asc_opt.HasResult()) { - auto desc_or_asc_value = transformer.Transform(desc_or_asc_opt.GetResult()); - desc_or_asc = desc_or_asc_value; - } - optional nulls_first_or_last {}; - auto &nulls_first_or_last_opt = list_pr.GetChild(2).Cast(); - if (nulls_first_or_last_opt.HasResult()) { - auto nulls_first_or_last_value = transformer.Transform(nulls_first_or_last_opt.GetResult()); - nulls_first_or_last = nulls_first_or_last_value; - } - auto result = TransformIndexElement(transformer, std::move(expression), desc_or_asc, nulls_first_or_last); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformUniqueIndexInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformUniqueIndex(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformIndexTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(1).Cast().identifier; - auto result = TransformIndexType(transformer, identifier); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformRelOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto rel_option_name = transformer.Transform(list_pr.GetChild(0)); - optional> rel_option_argument_opt {}; - auto &rel_option_argument_opt_opt = list_pr.GetChild(1).Cast(); - if (rel_option_argument_opt_opt.HasResult()) { - auto rel_option_argument_opt_value = - transformer.Transform>(rel_option_argument_opt_opt.GetResult()); - rel_option_argument_opt = std::move(rel_option_argument_opt_value); - } - auto result = TransformRelOption(transformer, rel_option_name, std::move(rel_option_argument_opt)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformRelOptionNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto &choice_result = choice_pr.GetResult(); - string child; - if (choice_result.type == ParseResultType::IDENTIFIER) { - child = choice_result.Cast().identifier.GetIdentifierName(); - } else if (choice_result.type == ParseResultType::KEYWORD) { - child = choice_result.Cast().keyword; - } else if (choice_result.type == ParseResultType::STRING) { - child = choice_result.Cast().result; - } else if (choice_result.type == ParseResultType::OPERATOR) { - child = choice_result.Cast().operator_token; - } else { - child = transformer.Transform(choice_result); - } - auto result = TransformRelOptionName(transformer, child); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformDottedIdentifierStringInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto dotted_identifier = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformDottedIdentifierString(transformer, dotted_identifier); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformRelOptionArgumentOptInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto def_arg = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformRelOptionArgumentOpt(transformer, std::move(def_arg)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDefArgInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDefArgNullInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto null_literal = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformDefArgNull(transformer, null_literal); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDefArgKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto reserved_keyword = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformDefArgKeyword(transformer, reserved_keyword); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformDefArgStringLiteralInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto string_literal = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformDefArgStringLiteral(transformer, string_literal); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNoneLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformNoneLiteral(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCreateMacroStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto macro_or_function = transformer.Transform(list_pr.GetChild(0)); - optional if_not_exists {}; - auto &if_not_exists_opt = list_pr.GetChild(1).Cast(); - if (if_not_exists_opt.HasResult()) { - auto if_not_exists_value = transformer.Transform(if_not_exists_opt.GetResult()); - if_not_exists = if_not_exists_value; - } - auto qualified_name = transformer.Transform(list_pr.GetChild(2)); - vector> macro_definition; - auto macro_definition_items = ExtractParseResultsFromList(list_pr.GetChild(3)); - for (auto ¯o_definition_item : macro_definition_items) { - auto macro_definition_value = transformer.Transform>(macro_definition_item.get()); - macro_definition.push_back(std::move(macro_definition_value)); - } - auto result = TransformCreateMacroStmt(transformer, macro_or_function, if_not_exists, qualified_name, - std::move(macro_definition)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMacroOrFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformMacroKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformMacroKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformFunctionKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformFunctionKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformMacroDefinitionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> macro_parameters {}; - auto ¯o_parameters_opt = ExtractResultFromParens(list_pr.GetChild(0)).Cast(); - if (macro_parameters_opt.HasResult()) { - auto macro_parameters_value = transformer.Transform>(macro_parameters_opt.GetResult()); - macro_parameters = std::move(macro_parameters_value); - } - auto macro_definition_body = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformMacroDefinition(transformer, std::move(macro_parameters), std::move(macro_definition_body)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformMacroDefinitionBodyInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMacroParametersInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector macro_parameter; - auto macro_parameter_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - for (auto ¯o_parameter_item : macro_parameter_items) { - auto macro_parameter_value = transformer.Transform(macro_parameter_item.get()); - macro_parameter.push_back(std::move(macro_parameter_value)); - } - auto result = TransformMacroParameters(transformer, std::move(macro_parameter)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMacroParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSimpleParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto type_func_name = transformer.Transform(list_pr.GetChild(0)); - optional type {}; - auto &type_opt = list_pr.GetChild(1).Cast(); - if (type_opt.HasResult()) { - auto type_value = transformer.Transform(type_opt.GetResult()); - type = type_value; - } - auto result = TransformSimpleParameter(transformer, type_func_name, type); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformScalarMacroDefinitionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformScalarMacroDefinition(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformTableMacroDefinitionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto select_statement_internal = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformTableMacroDefinition(transformer, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCreateSchemaStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_not_exists {}; - auto &if_not_exists_opt = list_pr.GetChild(1).Cast(); - if (if_not_exists_opt.HasResult()) { - auto if_not_exists_value = transformer.Transform(if_not_exists_opt.GetResult()); - if_not_exists = if_not_exists_value; - } - auto qualified_name = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformCreateSchemaStmt(transformer, if_not_exists, qualified_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCreateSecretStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_not_exists {}; - auto &if_not_exists_opt = list_pr.GetChild(1).Cast(); - if (if_not_exists_opt.HasResult()) { - auto if_not_exists_value = transformer.Transform(if_not_exists_opt.GetResult()); - if_not_exists = if_not_exists_value; - } - optional secret_name {}; - auto &secret_name_opt = list_pr.GetChild(2).Cast(); - if (secret_name_opt.HasResult()) { - auto secret_name_value = transformer.Transform(secret_name_opt.GetResult()); - secret_name = secret_name_value; - } - optional secret_storage_specifier {}; - auto &secret_storage_specifier_opt = list_pr.GetChild(3).Cast(); - if (secret_storage_specifier_opt.HasResult()) { - auto secret_storage_specifier_value = - transformer.Transform(secret_storage_specifier_opt.GetResult()); - secret_storage_specifier = secret_storage_specifier_value; - } - auto generic_copy_option_list = transformer.Transform>(list_pr.GetChild(4)); - auto result = TransformCreateSecretStmt(transformer, if_not_exists, secret_name, secret_storage_specifier, - generic_copy_option_list); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformSecretStorageSpecifierInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(1).Cast().identifier; - auto result = TransformSecretStorageSpecifier(transformer, identifier); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSecretNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformSecretName(transformer, col_id); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCreateSequenceStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_not_exists {}; - auto &if_not_exists_opt = list_pr.GetChild(1).Cast(); - if (if_not_exists_opt.HasResult()) { - auto if_not_exists_value = transformer.Transform(if_not_exists_opt.GetResult()); - if_not_exists = if_not_exists_value; - } - auto qualified_name = transformer.Transform(list_pr.GetChild(2)); - optional>>> sequence_option {}; - auto &sequence_option_opt = list_pr.GetChild(3).Cast(); - if (sequence_option_opt.HasResult()) { - vector>> sequence_option_value; - auto &sequence_option_value_repeat_1 = sequence_option_opt.GetResult().Cast(); - for (auto &sequence_option_value_item_1 : sequence_option_value_repeat_1.GetChildren()) { - auto sequence_option_value_value_1 = - transformer.Transform>>(sequence_option_value_item_1.get()); - sequence_option_value.push_back(std::move(sequence_option_value_value_1)); - } - sequence_option = std::move(sequence_option_value); - } - auto result = TransformCreateSequenceStmt(transformer, if_not_exists, qualified_name, std::move(sequence_option)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSequenceOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>>(choice_pr.GetResult()); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSeqSetCycleInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>>(choice_pr.GetResult()); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSeqCycleInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSeqCycle(transformer); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSeqNoCycleInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSeqNoCycle(transformer); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSeqSetIncrementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto expression = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformSeqSetIncrement(transformer, has_result, std::move(expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSeqSetMinMaxInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto seq_min_or_max = transformer.Transform(list_pr.GetChild(0)); - auto expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformSeqSetMinMax(transformer, seq_min_or_max, std::move(expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSeqNoMinMaxInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto seq_min_or_max = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformSeqNoMinMax(transformer, seq_min_or_max); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSeqStartWithInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto expression = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformSeqStartWith(transformer, has_result, std::move(expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSeqOwnedByInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto qualified_name = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformSeqOwnedBy(transformer, qualified_name); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSeqMinOrMaxInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - string result; - if (choice_pr.GetResult().type == ParseResultType::IDENTIFIER) { - result = choice_pr.GetResult().Cast().identifier.GetIdentifierName(); - } else if (choice_pr.GetResult().type == ParseResultType::KEYWORD) { - result = choice_pr.GetResult().Cast().keyword; - } else if (choice_pr.GetResult().type == ParseResultType::STRING) { - result = choice_pr.GetResult().Cast().result; - } else if (choice_pr.GetResult().type == ParseResultType::OPERATOR) { - result = choice_pr.GetResult().Cast().operator_token; - } else { - result = transformer.Transform(choice_pr.GetResult()); - } - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformMinValueInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformMinValue(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformMaxValueInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformMaxValue(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCreateStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional or_replace {}; - auto &or_replace_opt = list_pr.GetChild(1).Cast(); - if (or_replace_opt.HasResult()) { - auto or_replace_value = transformer.Transform(or_replace_opt.GetResult()); - or_replace = or_replace_value; - } - optional temporary {}; - auto &temporary_opt = list_pr.GetChild(2).Cast(); - if (temporary_opt.HasResult()) { - auto temporary_value = transformer.Transform(temporary_opt.GetResult()); - temporary = temporary_value; - } - auto create_statement_variation = transformer.Transform>(list_pr.GetChild(3)); - auto result = TransformCreateStatement(transformer, or_replace, temporary, std::move(create_statement_variation)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCreateStatementVariationInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOrReplaceInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformOrReplace(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTemporaryInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformPersistentInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformPersistent(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTempPersistentInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformTempPersistent(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformTemporaryPersistentInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto result = TransformTemporaryPersistent(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCreateTableStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_not_exists {}; - auto &if_not_exists_opt = list_pr.GetChild(1).Cast(); - if (if_not_exists_opt.HasResult()) { - auto if_not_exists_value = transformer.Transform(if_not_exists_opt.GetResult()); - if_not_exists = if_not_exists_value; - } - auto qualified_name = transformer.Transform(list_pr.GetChild(2)); - auto create_table_definition = transformer.Transform(list_pr.GetChild(3)); - optional commit_action {}; - auto &commit_action_opt = list_pr.GetChild(4).Cast(); - if (commit_action_opt.HasResult()) { - auto commit_action_value = transformer.Transform(commit_action_opt.GetResult()); - commit_action = commit_action_value; - } - auto result = TransformCreateTableStmt(transformer, if_not_exists, qualified_name, - std::move(create_table_definition), commit_action); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCreateTableDefinitionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCreateTableAsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional identifier_list {}; - auto &identifier_list_opt = list_pr.GetChild(0).Cast(); - if (identifier_list_opt.HasResult()) { - auto identifier_list_value = transformer.Transform(identifier_list_opt.GetResult()); - identifier_list = std::move(identifier_list_value); - } - optional partition_sorted_options {}; - auto &partition_sorted_options_opt = list_pr.GetChild(1).Cast(); - if (partition_sorted_options_opt.HasResult()) { - auto partition_sorted_options_value = - transformer.Transform(partition_sorted_options_opt.GetResult()); - partition_sorted_options = std::move(partition_sorted_options_value); - } - optional>> with_list {}; - auto &with_list_opt = list_pr.GetChild(2).Cast(); - if (with_list_opt.HasResult()) { - auto with_list_value = - transformer.Transform>>(with_list_opt.GetResult()); - with_list = std::move(with_list_value); - } - auto statement = transformer.Transform>(list_pr.GetChild(4)); - optional with_data {}; - auto &with_data_opt = list_pr.GetChild(5).Cast(); - if (with_data_opt.HasResult()) { - auto with_data_value = transformer.Transform(with_data_opt.GetResult()); - with_data = with_data_value; - } - auto result = TransformCreateTableAs(transformer, std::move(identifier_list), std::move(partition_sorted_options), - std::move(with_list), std::move(statement), with_data); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformPartitionSortedOptionsInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformPartitionOptSortedOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto partition_options = transformer.Transform>>(list_pr.GetChild(0)); - optional>> sorted_options {}; - auto &sorted_options_opt = list_pr.GetChild(1).Cast(); - if (sorted_options_opt.HasResult()) { - auto sorted_options_value = - transformer.Transform>>(sorted_options_opt.GetResult()); - sorted_options = std::move(sorted_options_value); - } - auto result = - TransformPartitionOptSortedOptions(transformer, std::move(partition_options), std::move(sorted_options)); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformSortedOptPartitionOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto sorted_options = transformer.Transform>>(list_pr.GetChild(0)); - optional>> partition_options {}; - auto &partition_options_opt = list_pr.GetChild(1).Cast(); - if (partition_options_opt.HasResult()) { - auto partition_options_value = - transformer.Transform>>(partition_options_opt.GetResult()); - partition_options = std::move(partition_options_value); - } - auto result = - TransformSortedOptPartitionOptions(transformer, std::move(sorted_options), std::move(partition_options)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPartitionOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> expression; - auto expression_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(2))); - for (auto &expression_item : expression_items) { - auto expression_value = transformer.Transform>(expression_item.get()); - expression.push_back(std::move(expression_value)); - } - auto result = TransformPartitionOptions(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSortedOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> expression; - auto expression_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(2))); - for (auto &expression_item : expression_items) { - auto expression_value = transformer.Transform>(expression_item.get()); - expression.push_back(std::move(expression_value)); - } - auto result = TransformSortedOptions(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformWithDataInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformWithDataOnlyInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformWithDataOnly(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformWithNoDataInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformWithNoData(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformIdentifierListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector identifier; - auto identifier_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &identifier_item : identifier_items) { - auto identifier_value = identifier_item.get().Cast().identifier; - identifier.push_back(identifier_value); - } - auto result = TransformIdentifierList(transformer, identifier); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCreateColumnListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional create_table_column_list {}; - auto &create_table_column_list_opt = ExtractResultFromParens(list_pr.GetChild(0)).Cast(); - if (create_table_column_list_opt.HasResult()) { - auto create_table_column_list_value = - transformer.Transform(create_table_column_list_opt.GetResult()); - create_table_column_list = std::move(create_table_column_list_value); - } - optional partition_sorted_options {}; - auto &partition_sorted_options_opt = list_pr.GetChild(1).Cast(); - if (partition_sorted_options_opt.HasResult()) { - auto partition_sorted_options_value = - transformer.Transform(partition_sorted_options_opt.GetResult()); - partition_sorted_options = std::move(partition_sorted_options_value); - } - optional>> with_list {}; - auto &with_list_opt = list_pr.GetChild(2).Cast(); - if (with_list_opt.HasResult()) { - auto with_list_value = - transformer.Transform>>(with_list_opt.GetResult()); - with_list = std::move(with_list_value); - } - auto result = TransformCreateColumnList(transformer, std::move(create_table_column_list), - std::move(partition_sorted_options), std::move(with_list)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIfNotExistsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformIfNotExists(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformQualifiedNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformSchemaReservedIdentifierOrStringLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto schema_qualification = transformer.Transform(list_pr.GetChild(0)); - auto reserved_identifier_or_string_literal = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformSchemaReservedIdentifierOrStringLiteral(transformer, schema_qualification, - reserved_identifier_or_string_literal); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformCatalogReservedSchemaIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto catalog_qualification = transformer.Transform(list_pr.GetChild(0)); - vector reserved_schema_qualification; - auto &reserved_schema_qualification_repeat = list_pr.GetChild(1).Cast(); - for (auto &reserved_schema_qualification_item : reserved_schema_qualification_repeat.GetChildren()) { - auto reserved_schema_qualification_value = - transformer.Transform(reserved_schema_qualification_item.get()); - reserved_schema_qualification.push_back(reserved_schema_qualification_value); - } - auto reserved_identifier_or_string_literal = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformCatalogReservedSchemaIdentifier( - transformer, catalog_qualification, reserved_schema_qualification, reserved_identifier_or_string_literal); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformIdentifierOrStringLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto &choice_result = choice_pr.GetResult(); - string child; - if (choice_result.type == ParseResultType::IDENTIFIER) { - child = choice_result.Cast().identifier.GetIdentifierName(); - } else if (choice_result.type == ParseResultType::KEYWORD) { - child = choice_result.Cast().keyword; - } else if (choice_result.type == ParseResultType::STRING) { - child = choice_result.Cast().result; - } else if (choice_result.type == ParseResultType::OPERATOR) { - child = choice_result.Cast().operator_token; - } else { - child = transformer.Transform(choice_result); - } - auto result = TransformIdentifierOrStringLiteral(transformer, child); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformReservedIdentifierOrStringLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - Identifier result; - if (choice_pr.GetResult().type == ParseResultType::IDENTIFIER) { - result = choice_pr.GetResult().Cast().identifier; - } else if (choice_pr.GetResult().type == ParseResultType::KEYWORD) { - result = Identifier(choice_pr.GetResult().Cast().keyword); - } else if (choice_pr.GetResult().type == ParseResultType::STRING) { - result = Identifier(choice_pr.GetResult().Cast().result); - } else { - result = transformer.Transform(choice_pr.GetResult()); - } - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformCatalogQualificationInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto catalog_name = list_pr.GetChild(0).Cast().identifier; - auto result = TransformCatalogQualification(transformer, catalog_name); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformSchemaQualificationInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto schema_name = list_pr.GetChild(0).Cast().identifier; - auto result = TransformSchemaQualification(transformer, schema_name); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformReservedSchemaQualificationInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto reserved_schema_name = list_pr.GetChild(0).Cast().identifier; - auto result = TransformReservedSchemaQualification(transformer, reserved_schema_name); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTableQualificationInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto table_name = list_pr.GetChild(0).Cast().identifier; - auto result = TransformTableQualification(transformer, table_name); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformReservedTableQualificationInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto reserved_table_name = list_pr.GetChild(0).Cast().identifier; - auto result = TransformReservedTableQualification(transformer, reserved_table_name); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformCreateTableColumnListInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector create_table_column_element; - auto create_table_column_element_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - for (auto &create_table_column_element_item : create_table_column_element_items) { - auto create_table_column_element_value = - transformer.Transform(create_table_column_element_item.get()); - create_table_column_element.push_back(std::move(create_table_column_element_value)); - } - auto result = TransformCreateTableColumnList(transformer, std::move(create_table_column_element)); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCreateTableColumnElementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCreateTableColumnDefinitionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto column_definition = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformCreateTableColumnDefinition(transformer, std::move(column_definition)); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCreateTableConstraintInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto top_level_constraint = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformCreateTableConstraint(transformer, std::move(top_level_constraint)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformColumnDefinitionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto dotted_identifier = transformer.Transform>(list_pr.GetChild(0)); - optional type {}; - auto &type_opt = list_pr.GetChild(1).Cast(); - if (type_opt.HasResult()) { - auto type_value = transformer.Transform(type_opt.GetResult()); - type = type_value; - } - optional generated_column {}; - auto &generated_column_opt = list_pr.GetChild(2).Cast(); - if (generated_column_opt.HasResult()) { - auto generated_column_value = - transformer.Transform(generated_column_opt.GetResult()); - generated_column = std::move(generated_column_value); - } - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(3).Cast(); - has_result = has_result_opt.HasResult(); - optional> column_constraint {}; - auto &column_constraint_opt = list_pr.GetChild(4).Cast(); - if (column_constraint_opt.HasResult()) { - vector column_constraint_value; - auto &column_constraint_value_repeat_1 = column_constraint_opt.GetResult().Cast(); - for (auto &column_constraint_value_item_1 : column_constraint_value_repeat_1.GetChildren()) { - auto column_constraint_value_value_1 = - transformer.Transform(column_constraint_value_item_1.get()); - column_constraint_value.push_back(std::move(column_constraint_value_value_1)); - } - column_constraint = std::move(column_constraint_value); - } - auto result = TransformColumnDefinition(transformer, dotted_identifier, type, std::move(generated_column), - has_result, std::move(column_constraint)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformColumnConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNotNullConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto child = transformer.Transform(choice_pr.GetResult()); - auto result = TransformNotNullConstraint(transformer, child); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNullConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformNullConstraint(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformNotNullColumnConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformNotNullColumnConstraint(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformUniqueConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional constraint_timing {}; - auto &constraint_timing_opt = list_pr.GetChild(1).Cast(); - if (constraint_timing_opt.HasResult()) { - auto constraint_timing_value = transformer.Transform(constraint_timing_opt.GetResult()); - constraint_timing = constraint_timing_value; - } - auto result = TransformUniqueConstraint(transformer, constraint_timing); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformPrimaryKeyConstraintInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional constraint_timing {}; - auto &constraint_timing_opt = list_pr.GetChild(2).Cast(); - if (constraint_timing_opt.HasResult()) { - auto constraint_timing_value = transformer.Transform(constraint_timing_opt.GetResult()); - constraint_timing = constraint_timing_value; - } - auto result = TransformPrimaryKeyConstraint(transformer, constraint_timing); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformConstraintTimingInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformImmediateConstraintInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto result = TransformImmediateConstraint(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDeferredConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformDeferredConstraint(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDefaultValueInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto column_default_expr = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformDefaultValue(transformer, std::move(column_default_expr)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCheckConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(1))); - auto result = TransformCheckConstraint(transformer, std::move(expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformForeignKeyConstraintInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto base_table_name = transformer.Transform>(list_pr.GetChild(1)); - optional> column_list {}; - auto &column_list_opt = list_pr.GetChild(2).Cast(); - if (column_list_opt.HasResult()) { - auto column_list_value = - transformer.Transform>(ExtractResultFromParens(column_list_opt.GetResult())); - column_list = column_list_value; - } - auto key_actions = transformer.Transform(list_pr.GetChild(3)); - auto result = TransformForeignKeyConstraint(transformer, std::move(base_table_name), column_list, key_actions); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformColumnCollationInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto dotted_identifier = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformColumnCollation(transformer, dotted_identifier); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformColumnCompressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id_or_string = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformColumnCompression(transformer, col_id_or_string); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformKeyActionsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional update_action {}; - auto &update_action_opt = list_pr.GetChild(0).Cast(); - if (update_action_opt.HasResult()) { - auto update_action_value = transformer.Transform(update_action_opt.GetResult()); - update_action = update_action_value; - } - optional delete_action {}; - auto &delete_action_opt = list_pr.GetChild(1).Cast(); - if (delete_action_opt.HasResult()) { - auto delete_action_value = transformer.Transform(delete_action_opt.GetResult()); - delete_action = delete_action_value; - } - auto result = TransformKeyActions(transformer, update_action, delete_action); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformUpdateActionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto key_action = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformUpdateAction(transformer, key_action); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDeleteActionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto key_action = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformDeleteAction(transformer, key_action); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformKeyActionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - string result; - if (choice_pr.GetResult().type == ParseResultType::IDENTIFIER) { - result = choice_pr.GetResult().Cast().identifier.GetIdentifierName(); - } else if (choice_pr.GetResult().type == ParseResultType::KEYWORD) { - result = choice_pr.GetResult().Cast().keyword; - } else if (choice_pr.GetResult().type == ParseResultType::STRING) { - result = choice_pr.GetResult().Cast().result; - } else if (choice_pr.GetResult().type == ParseResultType::OPERATOR) { - result = choice_pr.GetResult().Cast().operator_token; - } else { - result = transformer.Transform(choice_pr.GetResult()); - } - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformNoKeyActionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformNoKeyAction(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformRestrictKeyActionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformRestrictKeyAction(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCascadeKeyActionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCascadeKeyAction(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSetNullKeyActionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSetNullKeyAction(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformSetDefaultKeyActionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto result = TransformSetDefaultKeyAction(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTopLevelConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(0).Cast(); - has_result = has_result_opt.HasResult(); - auto top_level_constraint_list = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformTopLevelConstraint(transformer, has_result, std::move(top_level_constraint_list)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformTopLevelConstraintListInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTopCheckConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto check_constraint = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformTopCheckConstraint(transformer, std::move(check_constraint)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformTopPrimaryKeyConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto column_id_list = transformer.Transform>(list_pr.GetChild(2)); - optional constraint_timing {}; - auto &constraint_timing_opt = list_pr.GetChild(3).Cast(); - if (constraint_timing_opt.HasResult()) { - auto constraint_timing_value = transformer.Transform(constraint_timing_opt.GetResult()); - constraint_timing = constraint_timing_value; - } - auto result = TransformTopPrimaryKeyConstraint(transformer, column_id_list, constraint_timing); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformTopUniqueConstraintInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto column_id_list = transformer.Transform>(list_pr.GetChild(1)); - optional constraint_timing {}; - auto &constraint_timing_opt = list_pr.GetChild(2).Cast(); - if (constraint_timing_opt.HasResult()) { - auto constraint_timing_value = transformer.Transform(constraint_timing_opt.GetResult()); - constraint_timing = constraint_timing_value; - } - auto result = TransformTopUniqueConstraint(transformer, column_id_list, constraint_timing); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformTopForeignKeyConstraintInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto column_id_list = transformer.Transform>(list_pr.GetChild(2)); - auto foreign_key_constraint = transformer.Transform(list_pr.GetChild(3)); - auto result = TransformTopForeignKeyConstraint(transformer, column_id_list, std::move(foreign_key_constraint)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformColumnIdListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector col_id; - auto col_id_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &col_id_item : col_id_items) { - auto col_id_value = transformer.Transform(col_id_item.get()); - col_id.push_back(col_id_value); - } - auto result = TransformColumnIdList(transformer, col_id); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformDottedIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(0).Cast().identifier; - optional> dot_col_label {}; - auto &dot_col_label_opt = list_pr.GetChild(1).Cast(); - if (dot_col_label_opt.HasResult()) { - vector dot_col_label_value; - auto &dot_col_label_value_repeat_1 = dot_col_label_opt.GetResult().Cast(); - for (auto &dot_col_label_value_item_1 : dot_col_label_value_repeat_1.GetChildren()) { - auto dot_col_label_value_value_1 = transformer.Transform(dot_col_label_value_item_1.get()); - dot_col_label_value.push_back(dot_col_label_value_value_1); - } - dot_col_label = dot_col_label_value; - } - auto result = TransformDottedIdentifier(transformer, identifier, dot_col_label); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformDotColLabelInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_label = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformDotColLabel(transformer, col_label); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformColIdInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - Identifier result; - if (choice_pr.GetResult().type == ParseResultType::IDENTIFIER) { - result = choice_pr.GetResult().Cast().identifier; - } else if (choice_pr.GetResult().type == ParseResultType::KEYWORD) { - result = Identifier(choice_pr.GetResult().Cast().keyword); - } else if (choice_pr.GetResult().type == ParseResultType::STRING) { - result = Identifier(choice_pr.GetResult().Cast().result); - } else { - result = transformer.Transform(choice_pr.GetResult()); - } - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformColIdOrStringInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTypeFuncNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - Identifier result; - if (choice_pr.GetResult().type == ParseResultType::IDENTIFIER) { - result = choice_pr.GetResult().Cast().identifier; - } else if (choice_pr.GetResult().type == ParseResultType::KEYWORD) { - result = Identifier(choice_pr.GetResult().Cast().keyword); - } else if (choice_pr.GetResult().type == ParseResultType::STRING) { - result = Identifier(choice_pr.GetResult().Cast().result); - } else { - result = transformer.Transform(choice_pr.GetResult()); - } - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTypeFuncKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - string result; - if (choice_pr.GetResult().type == ParseResultType::IDENTIFIER) { - result = choice_pr.GetResult().Cast().identifier.GetIdentifierName(); - } else if (choice_pr.GetResult().type == ParseResultType::KEYWORD) { - result = choice_pr.GetResult().Cast().keyword; - } else if (choice_pr.GetResult().type == ParseResultType::STRING) { - result = choice_pr.GetResult().Cast().result; - } else if (choice_pr.GetResult().type == ParseResultType::OPERATOR) { - result = choice_pr.GetResult().Cast().operator_token; - } else { - result = transformer.Transform(choice_pr.GetResult()); - } - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformColLabelInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - string result; - if (choice_pr.GetResult().type == ParseResultType::IDENTIFIER) { - result = choice_pr.GetResult().Cast().identifier.GetIdentifierName(); - } else if (choice_pr.GetResult().type == ParseResultType::KEYWORD) { - result = choice_pr.GetResult().Cast().keyword; - } else if (choice_pr.GetResult().type == ParseResultType::STRING) { - result = choice_pr.GetResult().Cast().result; - } else if (choice_pr.GetResult().type == ParseResultType::OPERATOR) { - result = choice_pr.GetResult().Cast().operator_token; - } else { - result = transformer.Transform(choice_pr.GetResult()); - } - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformColLabelOrStringInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformColLabelIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_label = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformColLabelIdentifier(transformer, col_label); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformStringLiteralIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto string_literal = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformStringLiteralIdentifier(transformer, string_literal); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformGeneratedColumnInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(0).Cast(); - has_result = has_result_opt.HasResult(); - auto expression = transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(2))); - optional generated_column_type {}; - auto &generated_column_type_opt = list_pr.GetChild(3).Cast(); - if (generated_column_type_opt.HasResult()) { - auto generated_column_type_value = transformer.Transform(generated_column_type_opt.GetResult()); - generated_column_type = generated_column_type_value; - } - auto result = TransformGeneratedColumn(transformer, has_result, std::move(expression), generated_column_type); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformGeneratedColumnTypeInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCommitActionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto preserve_or_delete = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformCommitAction(transformer, preserve_or_delete); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformPreserveOrDeleteInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformPreserveRowsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformPreserveRows(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDeleteRowsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformDeleteRows(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformVirtualGeneratedColumnInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto result = TransformVirtualGeneratedColumn(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformStoredGeneratedColumnInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto result = TransformStoredGeneratedColumn(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCreateTriggerStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_not_exists {}; - auto &if_not_exists_opt = list_pr.GetChild(1).Cast(); - if (if_not_exists_opt.HasResult()) { - auto if_not_exists_value = transformer.Transform(if_not_exists_opt.GetResult()); - if_not_exists = if_not_exists_value; - } - auto trigger_name = transformer.Transform(list_pr.GetChild(2)); - auto trigger_timing = transformer.Transform(list_pr.GetChild(3)); - auto trigger_event = transformer.Transform(list_pr.GetChild(4)); - auto base_table_name = transformer.Transform>(list_pr.GetChild(6)); - optional referencing_clause {}; - auto &referencing_clause_opt = list_pr.GetChild(7).Cast(); - if (referencing_clause_opt.HasResult()) { - auto referencing_clause_value = - transformer.Transform(referencing_clause_opt.GetResult()); - referencing_clause = referencing_clause_value; - } - optional for_each_clause {}; - auto &for_each_clause_opt = list_pr.GetChild(8).Cast(); - if (for_each_clause_opt.HasResult()) { - auto for_each_clause_value = transformer.Transform(for_each_clause_opt.GetResult()); - for_each_clause = for_each_clause_value; - } - auto trigger_body = transformer.Transform>(list_pr.GetChild(9)); - auto result = TransformCreateTriggerStmt(transformer, if_not_exists, trigger_name, trigger_timing, trigger_event, - std::move(base_table_name), referencing_clause, for_each_clause, - std::move(trigger_body)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTriggerBodyInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTriggerNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(0).Cast().identifier; - auto result = TransformTriggerName(transformer, identifier); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformReferencingClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto referencing_item = transformer.Transform(list_pr.GetChild(1)); - optional referencing_item_1 {}; - auto &referencing_item_1_opt = list_pr.GetChild(2).Cast(); - if (referencing_item_1_opt.HasResult()) { - auto referencing_item_1_value = - transformer.Transform(referencing_item_1_opt.GetResult()); - referencing_item_1 = referencing_item_1_value; - } - auto result = TransformReferencingClause(transformer, referencing_item, referencing_item_1); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformReferencingItemInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformReferencingNewTableAsInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id = transformer.Transform(list_pr.GetChild(3)); - auto result = TransformReferencingNewTableAs(transformer, col_id); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformReferencingOldTableAsInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id = transformer.Transform(list_pr.GetChild(3)); - auto result = TransformReferencingOldTableAs(transformer, col_id); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTriggerTimingInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTriggerBeforeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformTriggerBefore(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTriggerAfterInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformTriggerAfter(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTriggerInsteadOfInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformTriggerInsteadOf(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTriggerEventInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTriggerEventInsertInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformTriggerEventInsert(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTriggerEventDeleteInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformTriggerEventDelete(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTriggerEventUpdateInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformTriggerEventUpdate(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformTriggerEventUpdateOfInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto trigger_column_list = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformTriggerEventUpdateOf(transformer, trigger_column_list); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTriggerColumnListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector col_id; - auto col_id_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - for (auto &col_id_item : col_id_items) { - auto col_id_value = transformer.Transform(col_id_item.get()); - col_id.push_back(col_id_value); - } - auto result = TransformTriggerColumnList(transformer, col_id); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformForEachClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformForEachRowInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformForEachRow(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformForEachStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformForEachStatement(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCreateTypeStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_not_exists {}; - auto &if_not_exists_opt = list_pr.GetChild(1).Cast(); - if (if_not_exists_opt.HasResult()) { - auto if_not_exists_value = transformer.Transform(if_not_exists_opt.GetResult()); - if_not_exists = if_not_exists_value; - } - auto qualified_name = transformer.Transform(list_pr.GetChild(2)); - auto create_type = transformer.Transform>(list_pr.GetChild(4)); - auto result = TransformCreateTypeStmt(transformer, if_not_exists, qualified_name, std::move(create_type)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCreateTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCreateTypeFromTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto type = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformCreateTypeFromType(transformer, type); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformEnumSelectTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto select_statement_internal = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(1))); - auto result = TransformEnumSelectType(transformer, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformEnumStringLiteralListInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> string_literal {}; - auto &string_literal_opt = ExtractResultFromParens(list_pr.GetChild(1)).Cast(); - if (string_literal_opt.HasResult()) { - vector string_literal_value; - auto string_literal_value_items_1 = ExtractParseResultsFromList(string_literal_opt.GetResult()); - for (auto &string_literal_value_item_1 : string_literal_value_items_1) { - auto string_literal_value_value_1 = transformer.Transform(string_literal_value_item_1.get()); - string_literal_value.push_back(string_literal_value_value_1); - } - string_literal = string_literal_value; - } - auto result = TransformEnumStringLiteralList(transformer, string_literal); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCreateViewStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional create_secure {}; - auto &create_secure_opt = list_pr.GetChild(0).Cast(); - if (create_secure_opt.HasResult()) { - auto create_secure_value = transformer.Transform(create_secure_opt.GetResult()); - create_secure = create_secure_value; - } - optional create_recursive {}; - auto &create_recursive_opt = list_pr.GetChild(1).Cast(); - if (create_recursive_opt.HasResult()) { - auto create_recursive_value = transformer.Transform(create_recursive_opt.GetResult()); - create_recursive = create_recursive_value; - } - optional if_not_exists {}; - auto &if_not_exists_opt = list_pr.GetChild(3).Cast(); - if (if_not_exists_opt.HasResult()) { - auto if_not_exists_value = transformer.Transform(if_not_exists_opt.GetResult()); - if_not_exists = if_not_exists_value; - } - auto qualified_name = transformer.Transform(list_pr.GetChild(4)); - optional> insert_column_list {}; - auto &insert_column_list_opt = list_pr.GetChild(5).Cast(); - if (insert_column_list_opt.HasResult()) { - auto insert_column_list_value = transformer.Transform>(insert_column_list_opt.GetResult()); - insert_column_list = insert_column_list_value; - } - optional>> with_list {}; - auto &with_list_opt = list_pr.GetChild(6).Cast(); - if (with_list_opt.HasResult()) { - auto with_list_value = - transformer.Transform>>(with_list_opt.GetResult()); - with_list = std::move(with_list_value); - } - auto select_statement_internal = transformer.Transform>(list_pr.GetChild(8)); - auto result = - TransformCreateViewStmt(transformer, create_secure, create_recursive, if_not_exists, qualified_name, - insert_column_list, std::move(with_list), std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCreateRecursiveInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCreateRecursive(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCreateSecureInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCreateSecure(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformDeallocateStatementInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional deallocate_prepare {}; - auto &deallocate_prepare_opt = list_pr.GetChild(1).Cast(); - if (deallocate_prepare_opt.HasResult()) { - auto deallocate_prepare_value = transformer.Transform(deallocate_prepare_opt.GetResult()); - deallocate_prepare = deallocate_prepare_value; - } - auto identifier = list_pr.GetChild(2).Cast().identifier; - auto result = TransformDeallocateStatement(transformer, deallocate_prepare, identifier); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDeallocatePrepareInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformDeallocatePrepare(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDeleteStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional with_clause {}; - auto &with_clause_opt = list_pr.GetChild(0).Cast(); - if (with_clause_opt.HasResult()) { - auto with_clause_value = transformer.Transform(with_clause_opt.GetResult()); - with_clause = std::move(with_clause_value); - } - auto target_opt_alias = transformer.Transform>(list_pr.GetChild(3)); - optional>> delete_using_clause {}; - auto &delete_using_clause_opt = list_pr.GetChild(4).Cast(); - if (delete_using_clause_opt.HasResult()) { - auto delete_using_clause_value = - transformer.Transform>>(delete_using_clause_opt.GetResult()); - delete_using_clause = std::move(delete_using_clause_value); - } - optional> where_clause {}; - auto &where_clause_opt = list_pr.GetChild(5).Cast(); - if (where_clause_opt.HasResult()) { - auto where_clause_value = transformer.Transform>(where_clause_opt.GetResult()); - where_clause = std::move(where_clause_value); - } - optional>> returning_clause {}; - auto &returning_clause_opt = list_pr.GetChild(6).Cast(); - if (returning_clause_opt.HasResult()) { - auto returning_clause_value = - transformer.Transform>>(returning_clause_opt.GetResult()); - returning_clause = std::move(returning_clause_value); - } - auto result = - TransformDeleteStatement(transformer, std::move(with_clause), std::move(target_opt_alias), - std::move(delete_using_clause), std::move(where_clause), std::move(returning_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTruncateStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto base_table_name = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformTruncateStatement(transformer, has_result, std::move(base_table_name)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTargetOptAliasInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto base_table_name = transformer.Transform>(list_pr.GetChild(0)); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - optional col_id {}; - auto &col_id_opt = list_pr.GetChild(2).Cast(); - if (col_id_opt.HasResult()) { - auto col_id_value = transformer.Transform(col_id_opt.GetResult()); - col_id = col_id_value; - } - auto result = TransformTargetOptAlias(transformer, std::move(base_table_name), has_result, col_id); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDeleteUsingClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> table_ref; - auto table_ref_items = ExtractParseResultsFromList(list_pr.GetChild(1)); - for (auto &table_ref_item : table_ref_items) { - auto table_ref_value = transformer.Transform>(table_ref_item.get()); - table_ref.push_back(std::move(table_ref_value)); - } - auto result = TransformDeleteUsingClause(transformer, std::move(table_ref)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDescribeStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto child = transformer.Transform>(choice_pr.GetResult()); - auto result = TransformDescribeStatement(transformer, std::move(child)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformShowDeprecatedSelectInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto show_rule = transformer.Transform(list_pr.GetChild(0)); - auto select_statement_internal = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformShowDeprecatedSelect(transformer, show_rule, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDescribeSelectInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto describe_or_summarize = transformer.Transform(list_pr.GetChild(0)); - auto select_statement_internal = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformDescribeSelect(transformer, describe_or_summarize, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformShowAllTablesInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto show_or_describe = transformer.Transform(list_pr.GetChild(0)); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(2).Cast(); - has_result = has_result_opt.HasResult(); - auto result = TransformShowAllTables(transformer, show_or_describe, has_result); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformShowTablesInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto show_or_describe = transformer.Transform(list_pr.GetChild(0)); - auto qualified_name = transformer.Transform(list_pr.GetChild(3)); - auto result = TransformShowTables(transformer, show_or_describe, qualified_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformShowByNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto show_rule = transformer.Transform(list_pr.GetChild(0)); - optional show_target {}; - auto &show_target_opt = list_pr.GetChild(1).Cast(); - if (show_target_opt.HasResult()) { - auto show_target_value = transformer.Transform(show_target_opt.GetResult()); - show_target = std::move(show_target_value); - } - auto result = TransformShowByName(transformer, show_rule, std::move(show_target)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDescribeByNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto describe_or_summarize = transformer.Transform(list_pr.GetChild(0)); - optional describe_target {}; - auto &describe_target_opt = list_pr.GetChild(1).Cast(); - if (describe_target_opt.HasResult()) { - auto describe_target_value = transformer.Transform(describe_target_opt.GetResult()); - describe_target = std::move(describe_target_value); - } - auto result = TransformDescribeByName(transformer, describe_or_summarize, std::move(describe_target)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformDescribeOrSummarizeInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformShowTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto &choice_result = choice_pr.GetResult(); - DescribeTarget result {}; - if (!(choice_result.name == "ShowSpecialForm")) { - result = transformer.Transform(choice_result); - } - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformShowDeprecatedQualifiedTableNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto qualified_table_name = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformShowDeprecatedQualifiedTableName(transformer, std::move(qualified_table_name)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformShowSettingNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto setting_name = list_pr.GetChild(0).Cast().identifier; - auto result = TransformShowSettingName(transformer, setting_name); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDescribeTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformDescribeBaseTableNameInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto base_table_name = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformDescribeBaseTableName(transformer, std::move(base_table_name)); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformDescribeStringLiteralInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto string_literal = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformDescribeStringLiteral(transformer, string_literal); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSummarizeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto summarize_rule = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformSummarize(transformer, summarize_rule); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSummarizeRuleInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSummarizeRule(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformShowOrDescribeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformShowRuleInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformShowRule(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDescribeRuleInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDescribeLongRuleInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformDescribeLongRule(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDescRuleInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformDescRule(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDetachStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(2).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - auto catalog_name = list_pr.GetChild(3).Cast().identifier; - auto result = TransformDetachStatement(transformer, has_result, if_exists, catalog_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDropStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto drop_entries = transformer.Transform>(list_pr.GetChild(1)); - optional drop_behavior {}; - auto &drop_behavior_opt = list_pr.GetChild(2).Cast(); - if (drop_behavior_opt.HasResult()) { - auto drop_behavior_value = transformer.Transform(drop_behavior_opt.GetResult()); - drop_behavior = drop_behavior_value; - } - auto result = TransformDropStatement(transformer, std::move(drop_entries), drop_behavior); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDropEntriesInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDropTriggerInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(1).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - auto trigger_name = transformer.Transform(list_pr.GetChild(2)); - auto base_table_name = transformer.Transform>(list_pr.GetChild(4)); - auto result = TransformDropTrigger(transformer, if_exists, trigger_name, std::move(base_table_name)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDropTableInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto table_or_view = transformer.Transform(list_pr.GetChild(0)); - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(1).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - vector> base_table_name; - auto base_table_name_items = ExtractParseResultsFromList(list_pr.GetChild(2)); - for (auto &base_table_name_item : base_table_name_items) { - auto base_table_name_value = transformer.Transform>(base_table_name_item.get()); - base_table_name.push_back(std::move(base_table_name_value)); - } - auto result = TransformDropTable(transformer, table_or_view, if_exists, std::move(base_table_name)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDropTableFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto comment_macro_table = transformer.Transform(list_pr.GetChild(0)); - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(1).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - vector table_function_name; - auto table_function_name_items = ExtractParseResultsFromList(list_pr.GetChild(2)); - for (auto &table_function_name_item : table_function_name_items) { - auto table_function_name_value = table_function_name_item.get().Cast().identifier; - table_function_name.push_back(table_function_name_value); - } - auto result = TransformDropTableFunction(transformer, comment_macro_table, if_exists, table_function_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDropFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto function_type_macro = transformer.Transform(list_pr.GetChild(0)); - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(1).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - vector function_identifier; - auto function_identifier_items = ExtractParseResultsFromList(list_pr.GetChild(2)); - for (auto &function_identifier_item : function_identifier_items) { - auto function_identifier_value = transformer.Transform(function_identifier_item.get()); - function_identifier.push_back(function_identifier_value); - } - auto result = TransformDropFunction(transformer, function_type_macro, if_exists, function_identifier); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDropSchemaInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(1).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - vector qualified_name; - auto qualified_name_items = ExtractParseResultsFromList(list_pr.GetChild(2)); - for (auto &qualified_name_item : qualified_name_items) { - auto qualified_name_value = transformer.Transform(qualified_name_item.get()); - qualified_name.push_back(qualified_name_value); - } - auto result = TransformDropSchema(transformer, if_exists, qualified_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDropIndexInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(1).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - vector qualified_index_name; - auto qualified_index_name_items = ExtractParseResultsFromList(list_pr.GetChild(2)); - for (auto &qualified_index_name_item : qualified_index_name_items) { - auto qualified_index_name_value = transformer.Transform(qualified_index_name_item.get()); - qualified_index_name.push_back(qualified_index_name_value); - } - auto result = TransformDropIndex(transformer, if_exists, qualified_index_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformQualifiedIndexNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformQualifiedIndexNameStringInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto index_name = list_pr.GetChild(0).Cast().identifier; - auto result = TransformQualifiedIndexNameString(transformer, index_name); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformSchemaReservedIndexInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto schema_qualification = transformer.Transform(list_pr.GetChild(0)); - auto reserved_index_name = list_pr.GetChild(1).Cast().identifier; - auto result = TransformSchemaReservedIndex(transformer, schema_qualification, reserved_index_name); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformCatalogReservedSchemaIndexInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto catalog_qualification = transformer.Transform(list_pr.GetChild(0)); - auto reserved_schema_qualification = transformer.Transform(list_pr.GetChild(1)); - auto reserved_index_name = list_pr.GetChild(2).Cast().identifier; - auto result = TransformCatalogReservedSchemaIndex(transformer, catalog_qualification, reserved_schema_qualification, - reserved_index_name); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDropSequenceInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(1).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - vector qualified_sequence_name; - auto qualified_sequence_name_items = ExtractParseResultsFromList(list_pr.GetChild(2)); - for (auto &qualified_sequence_name_item : qualified_sequence_name_items) { - auto qualified_sequence_name_value = transformer.Transform(qualified_sequence_name_item.get()); - qualified_sequence_name.push_back(qualified_sequence_name_value); - } - auto result = TransformDropSequence(transformer, if_exists, qualified_sequence_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDropCollationInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(1).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - vector collation_name; - auto collation_name_items = ExtractParseResultsFromList(list_pr.GetChild(2)); - for (auto &collation_name_item : collation_name_items) { - auto collation_name_value = transformer.Transform(collation_name_item.get()); - collation_name.push_back(collation_name_value); - } - auto result = TransformDropCollation(transformer, if_exists, collation_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDropTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(1).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - vector qualified_type_name; - auto qualified_type_name_items = ExtractParseResultsFromList(list_pr.GetChild(2)); - for (auto &qualified_type_name_item : qualified_type_name_items) { - auto qualified_type_name_value = transformer.Transform(qualified_type_name_item.get()); - qualified_type_name.push_back(qualified_type_name_value); - } - auto result = TransformDropType(transformer, if_exists, qualified_type_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDropSecretInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional temporary {}; - auto &temporary_opt = list_pr.GetChild(0).Cast(); - if (temporary_opt.HasResult()) { - auto temporary_value = transformer.Transform(temporary_opt.GetResult()); - temporary = temporary_value; - } - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(2).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - auto secret_name = transformer.Transform(list_pr.GetChild(3)); - optional drop_secret_storage {}; - auto &drop_secret_storage_opt = list_pr.GetChild(4).Cast(); - if (drop_secret_storage_opt.HasResult()) { - auto drop_secret_storage_value = transformer.Transform(drop_secret_storage_opt.GetResult()); - drop_secret_storage = drop_secret_storage_value; - } - auto result = TransformDropSecret(transformer, temporary, if_exists, secret_name, drop_secret_storage); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTableOrViewInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformMaterializedViewEntryInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto result = TransformMaterializedViewEntry(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformFunctionTypeMacroInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformFunctionTypeMacroKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformFunctionTypeMacroKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformFunctionTypeFunctionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto result = TransformFunctionTypeFunction(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDropBehaviorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformCascadeDropBehaviorInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto result = TransformCascadeDropBehavior(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformRestrictDropBehaviorInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto result = TransformRestrictDropBehavior(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformIfExistsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformIfExists(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDropSecretStorageInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(1).Cast().identifier; - auto result = TransformDropSecretStorage(transformer, identifier); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformExecuteStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(1).Cast().identifier; - optional> table_function_arguments {}; - auto &table_function_arguments_opt = list_pr.GetChild(2).Cast(); - if (table_function_arguments_opt.HasResult()) { - auto table_function_arguments_value = - transformer.Transform>(table_function_arguments_opt.GetResult()); - table_function_arguments = std::move(table_function_arguments_value); - } - auto result = TransformExecuteStatement(transformer, identifier, std::move(table_function_arguments)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformExplainStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional analyze_keyword {}; - auto &analyze_keyword_opt = list_pr.GetChild(1).Cast(); - if (analyze_keyword_opt.HasResult()) { - auto analyze_keyword_value = transformer.Transform(analyze_keyword_opt.GetResult()); - analyze_keyword = analyze_keyword_value; - } - optional> explain_option_list {}; - auto &explain_option_list_opt = list_pr.GetChild(2).Cast(); - if (explain_option_list_opt.HasResult()) { - auto explain_option_list_value = - transformer.Transform>(explain_option_list_opt.GetResult()); - explain_option_list = explain_option_list_value; - } - auto explainable_statements = transformer.Transform>(list_pr.GetChild(3)); - auto result = - TransformExplainStatement(transformer, analyze_keyword, explain_option_list, std::move(explainable_statements)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformExplainOptionListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector explain_option; - auto explain_option_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &explain_option_item : explain_option_items) { - auto explain_option_value = transformer.Transform(explain_option_item.get()); - explain_option.push_back(explain_option_value); - } - auto result = TransformExplainOptionList(transformer, explain_option); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformExplainOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto explain_option_name = transformer.Transform(list_pr.GetChild(0)); - optional> expression {}; - auto &expression_opt = list_pr.GetChild(1).Cast(); - if (expression_opt.HasResult()) { - auto expression_value = transformer.Transform>(expression_opt.GetResult()); - expression = std::move(expression_value); - } - auto result = TransformExplainOption(transformer, explain_option_name, std::move(expression)); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformExplainOptionNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = TransformExplainOptionName(transformer, choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformExplainSelectStatementInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto select_statement_internal = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformExplainSelectStatement(transformer, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformExplainableStatementsInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformExportStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional export_source {}; - auto &export_source_opt = list_pr.GetChild(2).Cast(); - if (export_source_opt.HasResult()) { - auto export_source_value = transformer.Transform(export_source_opt.GetResult()); - export_source = export_source_value; - } - auto string_literal = transformer.Transform(list_pr.GetChild(3)); - optional> generic_copy_option_list {}; - auto &generic_copy_option_list_opt = list_pr.GetChild(4).Cast(); - if (generic_copy_option_list_opt.HasResult()) { - auto generic_copy_option_list_value = - transformer.Transform>(generic_copy_option_list_opt.GetResult()); - generic_copy_option_list = generic_copy_option_list_value; - } - auto result = TransformExportStatement(transformer, export_source, string_literal, generic_copy_option_list); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformExportSourceInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto catalog_name = list_pr.GetChild(0).Cast().identifier; - auto result = TransformExportSource(transformer, catalog_name); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformImportStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto string_literal = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformImportStatement(transformer, string_literal); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformColumnReferenceInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto child = transformer.Transform>(choice_pr.GetResult()); - auto result = TransformColumnReference(transformer, std::move(child)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformNestedSchemaTableColumnNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto catalog_qualification = transformer.Transform(list_pr.GetChild(0)); - auto reserved_schema_qualification = transformer.Transform(list_pr.GetChild(1)); - auto reserved_schema_qualification_1 = transformer.Transform(list_pr.GetChild(2)); - vector reserved_schema_qualification_2; - auto &reserved_schema_qualification_2_repeat = list_pr.GetChild(3).Cast(); - for (auto &reserved_schema_qualification_2_item : reserved_schema_qualification_2_repeat.GetChildren()) { - auto reserved_schema_qualification_2_value = - transformer.Transform(reserved_schema_qualification_2_item.get()); - reserved_schema_qualification_2.push_back(reserved_schema_qualification_2_value); - } - auto reserved_column_name = list_pr.GetChild(4).Cast().identifier; - auto result = TransformNestedSchemaTableColumnName(transformer, catalog_qualification, - reserved_schema_qualification, reserved_schema_qualification_1, - reserved_schema_qualification_2, reserved_column_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCatalogReservedSchemaTableColumnNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto catalog_qualification = transformer.Transform(list_pr.GetChild(0)); - auto reserved_schema_qualification = transformer.Transform(list_pr.GetChild(1)); - auto reserved_table_qualification = transformer.Transform(list_pr.GetChild(2)); - auto reserved_column_name = list_pr.GetChild(3).Cast().identifier; - auto result = - TransformCatalogReservedSchemaTableColumnName(transformer, catalog_qualification, reserved_schema_qualification, - reserved_table_qualification, reserved_column_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformSchemaReservedTableColumnNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto schema_qualification = transformer.Transform(list_pr.GetChild(0)); - auto reserved_table_qualification = transformer.Transform(list_pr.GetChild(1)); - auto reserved_column_name = list_pr.GetChild(2).Cast().identifier; - auto result = TransformSchemaReservedTableColumnName(transformer, schema_qualification, - reserved_table_qualification, reserved_column_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformTableReservedColumnNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto table_qualification = transformer.Transform(list_pr.GetChild(0)); - auto reserved_column_name = list_pr.GetChild(1).Cast().identifier; - auto result = TransformTableReservedColumnName(transformer, table_qualification, reserved_column_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformFunctionExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto function_identifier = transformer.Transform(list_pr.GetChild(0)); - auto function_expression_arguments = transformer.Transform(list_pr.GetChild(1)); - optional> within_group_clause {}; - auto &within_group_clause_opt = list_pr.GetChild(2).Cast(); - if (within_group_clause_opt.HasResult()) { - auto within_group_clause_value = - transformer.Transform>(within_group_clause_opt.GetResult()); - within_group_clause = std::move(within_group_clause_value); - } - optional> filter_clause {}; - auto &filter_clause_opt = list_pr.GetChild(3).Cast(); - if (filter_clause_opt.HasResult()) { - auto filter_clause_value = transformer.Transform>(filter_clause_opt.GetResult()); - filter_clause = std::move(filter_clause_value); - } - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(4).Cast(); - has_result = has_result_opt.HasResult(); - optional> over_clause {}; - auto &over_clause_opt = list_pr.GetChild(5).Cast(); - if (over_clause_opt.HasResult()) { - auto over_clause_value = transformer.Transform>(over_clause_opt.GetResult()); - over_clause = std::move(over_clause_value); - } - auto result = TransformFunctionExpression(transformer, function_identifier, - std::move(function_expression_arguments), std::move(within_group_clause), - std::move(filter_clause), has_result, std::move(over_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformFunctionExpressionArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto function_expression_argument_list = - transformer.Transform(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = TransformFunctionExpressionArguments(transformer, std::move(function_expression_argument_list)); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformFunctionExpressionArgumentListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional distinct_or_all {}; - auto &distinct_or_all_opt = list_pr.GetChild(0).Cast(); - if (distinct_or_all_opt.HasResult()) { - auto distinct_or_all_value = transformer.Transform(distinct_or_all_opt.GetResult()); - distinct_or_all = distinct_or_all_value; - } - optional> function_argument_list {}; - auto &function_argument_list_opt = list_pr.GetChild(1).Cast(); - if (function_argument_list_opt.HasResult()) { - auto function_argument_list_value = - transformer.Transform>(function_argument_list_opt.GetResult()); - function_argument_list = std::move(function_argument_list_value); - } - optional> order_by_clause {}; - auto &order_by_clause_opt = list_pr.GetChild(2).Cast(); - if (order_by_clause_opt.HasResult()) { - auto order_by_clause_value = transformer.Transform>(order_by_clause_opt.GetResult()); - order_by_clause = std::move(order_by_clause_value); - } - optional ignore_or_respect_nulls {}; - auto &ignore_or_respect_nulls_opt = list_pr.GetChild(3).Cast(); - if (ignore_or_respect_nulls_opt.HasResult()) { - auto ignore_or_respect_nulls_value = transformer.Transform(ignore_or_respect_nulls_opt.GetResult()); - ignore_or_respect_nulls = ignore_or_respect_nulls_value; - } - auto result = - TransformFunctionExpressionArgumentList(transformer, distinct_or_all, std::move(function_argument_list), - std::move(order_by_clause), ignore_or_respect_nulls); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformFunctionArgumentListInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector function_argument; - auto function_argument_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - for (auto &function_argument_item : function_argument_items) { - auto function_argument_value = transformer.Transform(function_argument_item.get()); - function_argument.push_back(std::move(function_argument_value)); - } - auto result = TransformFunctionArgumentList(transformer, std::move(function_argument)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformFunctionIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformFunctionNameAsQualifiedNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto function_name = list_pr.GetChild(0).Cast().identifier; - auto result = TransformFunctionNameAsQualifiedName(transformer, function_name); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformCatalogReservedSchemaFunctionNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto catalog_qualification = transformer.Transform(list_pr.GetChild(0)); - optional> reserved_schema_qualification {}; - auto &reserved_schema_qualification_opt = list_pr.GetChild(1).Cast(); - if (reserved_schema_qualification_opt.HasResult()) { - vector reserved_schema_qualification_value; - auto &reserved_schema_qualification_value_repeat_1 = - reserved_schema_qualification_opt.GetResult().Cast(); - for (auto &reserved_schema_qualification_value_item_1 : - reserved_schema_qualification_value_repeat_1.GetChildren()) { - auto reserved_schema_qualification_value_value_1 = - transformer.Transform(reserved_schema_qualification_value_item_1.get()); - reserved_schema_qualification_value.push_back(reserved_schema_qualification_value_value_1); - } - reserved_schema_qualification = reserved_schema_qualification_value; - } - auto reserved_function_name = list_pr.GetChild(2).Cast().identifier; - auto result = TransformCatalogReservedSchemaFunctionName(transformer, catalog_qualification, - reserved_schema_qualification, reserved_function_name); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformSchemaReservedFunctionNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto schema_qualification = transformer.Transform(list_pr.GetChild(0)); - auto reserved_function_name = list_pr.GetChild(1).Cast().identifier; - auto result = TransformSchemaReservedFunctionName(transformer, schema_qualification, reserved_function_name); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDistinctOrAllInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDistinctKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformDistinctKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformAllKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformAllKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformWithinGroupClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto order_by_clause = transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(2))); - auto result = TransformWithinGroupClause(transformer, std::move(order_by_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformFilterClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto filter_clause_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformFilterClause(transformer, std::move(filter_clause_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformFilterClauseExpressionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto filter_clause_contents = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = TransformFilterClauseExpression(transformer, std::move(filter_clause_contents)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformFilterClauseContentsInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(0).Cast(); - has_result = has_result_opt.HasResult(); - auto expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformFilterClauseContents(transformer, has_result, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformIgnoreOrRespectNullsInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformIgnoreNullsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformIgnoreNulls(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformRespectNullsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformRespectNulls(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformParenthesisExpressionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional>> expression {}; - auto &expression_opt = ExtractResultFromParens(list_pr.GetChild(0)).Cast(); - if (expression_opt.HasResult()) { - vector> expression_value; - auto expression_value_items_1 = ExtractParseResultsFromList(expression_opt.GetResult()); - for (auto &expression_value_item_1 : expression_value_items_1) { - auto expression_value_value_1 = - transformer.Transform>(expression_value_item_1.get()); - expression_value.push_back(std::move(expression_value_value_1)); - } - expression = std::move(expression_value); - } - auto result = TransformParenthesisExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformLiteralExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = TransformLiteralExpression(transformer, choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformConstantLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto child = transformer.Transform(choice_pr.GetResult()); - auto result = TransformConstantLiteral(transformer, child); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNullLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformNullLiteral(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTrueLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformTrueLiteral(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformFalseLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformFalseLiteral(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCastExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto cast_or_try_cast = transformer.Transform(list_pr.GetChild(0)); - auto cast_arguments = transformer.Transform(ExtractResultFromParens(list_pr.GetChild(1))); - auto result = TransformCastExpression(transformer, cast_or_try_cast, std::move(cast_arguments)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCastArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - auto type = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformCastArguments(transformer, std::move(expression), type); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCastOrTryCastInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCastKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCastKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTryCastKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformTryCastKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformColIdDotInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformColIdDot(transformer, col_id); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformStarExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> star_qualifier_list {}; - auto &star_qualifier_list_opt = list_pr.GetChild(0).Cast(); - if (star_qualifier_list_opt.HasResult()) { - auto star_qualifier_list_value = transformer.Transform>(star_qualifier_list_opt.GetResult()); - star_qualifier_list = star_qualifier_list_value; - } - optional exclude_list {}; - auto &exclude_list_opt = list_pr.GetChild(2).Cast(); - if (exclude_list_opt.HasResult()) { - auto exclude_list_value = transformer.Transform(exclude_list_opt.GetResult()); - exclude_list = exclude_list_value; - } - optional>> replace_list {}; - auto &replace_list_opt = list_pr.GetChild(3).Cast(); - if (replace_list_opt.HasResult()) { - auto replace_list_value = - transformer.Transform>>(replace_list_opt.GetResult()); - replace_list = std::move(replace_list_value); - } - optional> rename_list {}; - auto &rename_list_opt = list_pr.GetChild(4).Cast(); - if (rename_list_opt.HasResult()) { - auto rename_list_value = transformer.Transform>(rename_list_opt.GetResult()); - rename_list = rename_list_value; - } - auto result = - TransformStarExpression(transformer, star_qualifier_list, exclude_list, std::move(replace_list), rename_list); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformStarQualifierListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector col_id_dot; - auto &col_id_dot_repeat = list_pr.GetChild(0).Cast(); - for (auto &col_id_dot_item : col_id_dot_repeat.GetChildren()) { - auto col_id_dot_value = transformer.Transform(col_id_dot_item.get()); - col_id_dot.push_back(col_id_dot_value); - } - auto result = TransformStarQualifierList(transformer, col_id_dot); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformExcludeListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto exclude_names = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformExcludeList(transformer, exclude_names); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformExcludeNamesInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformExcludeNameListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector exclude_name; - auto exclude_name_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &exclude_name_item : exclude_name_items) { - auto exclude_name_value = transformer.Transform(exclude_name_item.get()); - exclude_name.push_back(exclude_name_value); - } - auto result = TransformExcludeNameList(transformer, exclude_name); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformExcludeNameSingleInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto exclude_name = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformExcludeNameSingle(transformer, exclude_name); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformExcludeNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformExcludeDottedNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto dotted_identifier = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformExcludeDottedName(transformer, dotted_identifier); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformExcludeColumnNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id_or_string = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformExcludeColumnName(transformer, col_id_or_string); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformReplaceListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto replace_entries = - transformer.Transform>>(list_pr.GetChild(1)); - auto result = TransformReplaceList(transformer, std::move(replace_entries)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformReplaceEntriesInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>>(choice_pr.GetResult()); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformReplaceEntrySingleInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto replace_entry = transformer.Transform>>(list_pr.GetChild(0)); - auto result = TransformReplaceEntrySingle(transformer, std::move(replace_entry)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformReplaceEntryListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector>> replace_entry; - auto replace_entry_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &replace_entry_item : replace_entry_items) { - auto replace_entry_value = - transformer.Transform>>(replace_entry_item.get()); - replace_entry.push_back(std::move(replace_entry_value)); - } - auto result = TransformReplaceEntryList(transformer, std::move(replace_entry)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformReplaceEntryInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - auto column_reference = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformReplaceEntry(transformer, std::move(expression), std::move(column_reference)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformRenameListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto rename_entries = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformRenameList(transformer, rename_entries); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformRenameEntriesInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformRenameEntryListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> rename_entry; - auto rename_entry_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &rename_entry_item : rename_entry_items) { - auto rename_entry_value = transformer.Transform>(rename_entry_item.get()); - rename_entry.push_back(rename_entry_value); - } - auto result = TransformRenameEntryList(transformer, rename_entry); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformSingleRenameEntryInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto rename_entry = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformSingleRenameEntry(transformer, rename_entry); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformRenameEntryInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto exclude_name = transformer.Transform(list_pr.GetChild(0)); - auto identifier = list_pr.GetChild(2).Cast().identifier; - auto result = TransformRenameEntry(transformer, exclude_name, identifier); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformSubqueryExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional subquery_not {}; - auto &subquery_not_opt = list_pr.GetChild(0).Cast(); - if (subquery_not_opt.HasResult()) { - auto subquery_not_value = transformer.Transform(subquery_not_opt.GetResult()); - subquery_not = subquery_not_value; - } - optional subquery_exists {}; - auto &subquery_exists_opt = list_pr.GetChild(1).Cast(); - if (subquery_exists_opt.HasResult()) { - auto subquery_exists_value = transformer.Transform(subquery_exists_opt.GetResult()); - subquery_exists = subquery_exists_value; - } - auto subquery_reference = transformer.Transform>(list_pr.GetChild(2)); - auto result = - TransformSubqueryExpression(transformer, subquery_not, subquery_exists, std::move(subquery_reference)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSubqueryNotInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSubqueryNot(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSubqueryExistsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSubqueryExists(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCaseExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> expression {}; - auto &expression_opt = list_pr.GetChild(1).Cast(); - if (expression_opt.HasResult()) { - auto expression_value = transformer.Transform>(expression_opt.GetResult()); - expression = std::move(expression_value); - } - vector case_when_then; - auto &case_when_then_repeat = list_pr.GetChild(2).Cast(); - for (auto &case_when_then_item : case_when_then_repeat.GetChildren()) { - auto case_when_then_value = transformer.Transform(case_when_then_item.get()); - case_when_then.push_back(std::move(case_when_then_value)); - } - optional> case_else {}; - auto &case_else_opt = list_pr.GetChild(3).Cast(); - if (case_else_opt.HasResult()) { - auto case_else_value = transformer.Transform>(case_else_opt.GetResult()); - case_else = std::move(case_else_value); - } - auto result = - TransformCaseExpression(transformer, std::move(expression), std::move(case_when_then), std::move(case_else)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCaseWhenThenInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(1)); - auto expression_1 = transformer.Transform>(list_pr.GetChild(3)); - auto result = TransformCaseWhenThen(transformer, std::move(expression), std::move(expression_1)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCaseElseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformCaseElse(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTypeLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto type = transformer.Transform(list_pr.GetChild(0)); - auto string_literal = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformTypeLiteral(transformer, type, string_literal); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIntervalLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto interval_parameter = transformer.Transform>(list_pr.GetChild(1)); - optional interval {}; - auto &interval_opt = list_pr.GetChild(2).Cast(); - if (interval_opt.HasResult()) { - auto interval_value = transformer.Transform(interval_opt.GetResult()); - interval = interval_value; - } - auto result = TransformIntervalLiteral(transformer, std::move(interval_parameter), interval); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIntervalParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformIntervalStringParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto string_literal = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformIntervalStringParameter(transformer, string_literal); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformFrameClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto framing = transformer.Transform(list_pr.GetChild(0)); - auto frame_extent = transformer.Transform>(list_pr.GetChild(1)); - optional window_exclude_clause {}; - auto &window_exclude_clause_opt = list_pr.GetChild(2).Cast(); - if (window_exclude_clause_opt.HasResult()) { - auto window_exclude_clause_value = - transformer.Transform(window_exclude_clause_opt.GetResult()); - window_exclude_clause = window_exclude_clause_value; - } - auto result = TransformFrameClause(transformer, framing, std::move(frame_extent), window_exclude_clause); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformFramingInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - string result; - if (choice_pr.GetResult().type == ParseResultType::IDENTIFIER) { - result = choice_pr.GetResult().Cast().identifier.GetIdentifierName(); - } else if (choice_pr.GetResult().type == ParseResultType::KEYWORD) { - result = choice_pr.GetResult().Cast().keyword; - } else if (choice_pr.GetResult().type == ParseResultType::STRING) { - result = choice_pr.GetResult().Cast().result; - } else if (choice_pr.GetResult().type == ParseResultType::OPERATOR) { - result = choice_pr.GetResult().Cast().operator_token; - } else { - result = transformer.Transform(choice_pr.GetResult()); - } - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformRowsFramingInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformRowsFraming(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformRangeFramingInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformRangeFraming(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformGroupsFramingInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformGroupsFraming(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformFrameExtentInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSingleFrameExtentInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto frame_bound = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformSingleFrameExtent(transformer, std::move(frame_bound)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformBetweenFrameExtentInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto frame_bound = transformer.Transform(list_pr.GetChild(1)); - auto frame_bound_1 = transformer.Transform(list_pr.GetChild(3)); - auto result = TransformBetweenFrameExtent(transformer, std::move(frame_bound), std::move(frame_bound_1)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformFrameBoundInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformFrameUnboundedInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto preceding_or_following = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformFrameUnbounded(transformer, preceding_or_following); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformFrameExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - auto preceding_or_following = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformFrameExpression(transformer, std::move(expression), preceding_or_following); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformFrameCurrentRowInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformFrameCurrentRow(transformer); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformPrecedingOrFollowingInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformPrecedingFrameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformPrecedingFrame(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformFollowingFrameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformFollowingFrame(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformWindowExcludeClauseInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto window_exclude_element = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformWindowExcludeClause(transformer, window_exclude_element); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformWindowExcludeElementInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformExcludeCurrentRowInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformExcludeCurrentRow(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformExcludeGroupInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformExcludeGroup(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformExcludeTiesInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformExcludeTies(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformExcludeNoOthersInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformExcludeNoOthers(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformWindowFrameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformIdentifierWindowFrameInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(0).Cast().identifier; - auto result = TransformIdentifierWindowFrame(transformer, identifier); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformParensIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = ExtractResultFromParens(list_pr.GetChild(0)).Cast().identifier; - auto result = TransformParensIdentifier(transformer, identifier); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformWindowFrameDefinitionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformWindowFrameNameContentsParensInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto window_frame_name_contents = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = TransformWindowFrameNameContentsParens(transformer, std::move(window_frame_name_contents)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformWindowFrameNameContentsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional base_window_name {}; - auto &base_window_name_opt = list_pr.GetChild(0).Cast(); - if (base_window_name_opt.HasResult()) { - auto base_window_name_value = transformer.Transform(base_window_name_opt.GetResult()); - base_window_name = base_window_name_value; - } - auto window_frame_contents = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformWindowFrameNameContents(transformer, base_window_name, std::move(window_frame_contents)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformWindowFrameContentsParensInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto window_frame_contents = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = TransformWindowFrameContentsParens(transformer, std::move(window_frame_contents)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformWindowFrameContentsInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional>> window_partition {}; - auto &window_partition_opt = list_pr.GetChild(0).Cast(); - if (window_partition_opt.HasResult()) { - auto window_partition_value = - transformer.Transform>>(window_partition_opt.GetResult()); - window_partition = std::move(window_partition_value); - } - optional> order_by_clause {}; - auto &order_by_clause_opt = list_pr.GetChild(1).Cast(); - if (order_by_clause_opt.HasResult()) { - auto order_by_clause_value = transformer.Transform>(order_by_clause_opt.GetResult()); - order_by_clause = std::move(order_by_clause_value); - } - optional frame_clause {}; - auto &frame_clause_opt = list_pr.GetChild(2).Cast(); - if (frame_clause_opt.HasResult()) { - auto frame_clause_value = transformer.Transform(frame_clause_opt.GetResult()); - frame_clause = std::move(frame_clause_value); - } - auto result = TransformWindowFrameContents(transformer, std::move(window_partition), std::move(order_by_clause), - std::move(frame_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformBaseWindowNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(0).Cast().identifier; - auto result = TransformBaseWindowName(transformer, identifier); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformWindowPartitionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> expression; - auto expression_items = ExtractParseResultsFromList(list_pr.GetChild(2)); - for (auto &expression_item : expression_items) { - auto expression_value = transformer.Transform>(expression_item.get()); - expression.push_back(std::move(expression_value)); - } - auto result = TransformWindowPartition(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformListExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformArrayBoundedListExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(0).Cast(); - has_result = has_result_opt.HasResult(); - auto bounded_list_expression = transformer.Transform>>(list_pr.GetChild(1)); - auto result = TransformArrayBoundedListExpression(transformer, has_result, std::move(bounded_list_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformArrayParensSelectInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto select_statement_internal = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(1))); - auto result = TransformArrayParensSelect(transformer, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformBoundedListExpressionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional>> expression {}; - auto &expression_opt = list_pr.GetChild(1).Cast(); - if (expression_opt.HasResult()) { - vector> expression_value; - auto expression_value_items_1 = ExtractParseResultsFromList(expression_opt.GetResult()); - for (auto &expression_value_item_1 : expression_value_items_1) { - auto expression_value_value_1 = - transformer.Transform>(expression_value_item_1.get()); - expression_value.push_back(std::move(expression_value_value_1)); - } - expression = std::move(expression_value); - } - auto result = TransformBoundedListExpression(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformStructExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> struct_field {}; - auto &struct_field_opt = list_pr.GetChild(1).Cast(); - if (struct_field_opt.HasResult()) { - vector struct_field_value; - auto struct_field_value_items_1 = ExtractParseResultsFromList(struct_field_opt.GetResult()); - for (auto &struct_field_value_item_1 : struct_field_value_items_1) { - auto struct_field_value_value_1 = transformer.Transform(struct_field_value_item_1.get()); - struct_field_value.push_back(std::move(struct_field_value_value_1)); - } - struct_field = std::move(struct_field_value); - } - auto result = TransformStructExpression(transformer, std::move(struct_field)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformStructFieldInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id_or_string = transformer.Transform(list_pr.GetChild(0)); - auto expression = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformStructField(transformer, col_id_or_string, std::move(expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMapExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto map_struct_expression = transformer.Transform>>(list_pr.GetChild(1)); - auto result = TransformMapExpression(transformer, std::move(map_struct_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformMapStructExpressionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional>>> map_struct_field {}; - auto &map_struct_field_opt = list_pr.GetChild(1).Cast(); - if (map_struct_field_opt.HasResult()) { - vector>> map_struct_field_value; - auto map_struct_field_value_items_1 = ExtractParseResultsFromList(map_struct_field_opt.GetResult()); - for (auto &map_struct_field_value_item_1 : map_struct_field_value_items_1) { - auto map_struct_field_value_value_1 = - transformer.Transform>>(map_struct_field_value_item_1.get()); - map_struct_field_value.push_back(std::move(map_struct_field_value_value_1)); - } - map_struct_field = std::move(map_struct_field_value); - } - auto result = TransformMapStructExpression(transformer, std::move(map_struct_field)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMapStructFieldInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - auto expression_1 = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformMapStructField(transformer, std::move(expression), std::move(expression_1)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformGroupingExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto grouping_or_grouping_id = transformer.Transform(list_pr.GetChild(0)); - optional>> expression {}; - auto &expression_opt = ExtractResultFromParens(list_pr.GetChild(1)).Cast(); - if (expression_opt.HasResult()) { - vector> expression_value; - auto expression_value_items_1 = ExtractParseResultsFromList(expression_opt.GetResult()); - for (auto &expression_value_item_1 : expression_value_items_1) { - auto expression_value_value_1 = - transformer.Transform>(expression_value_item_1.get()); - expression_value.push_back(std::move(expression_value_value_1)); - } - expression = std::move(expression_value); - } - auto result = TransformGroupingExpression(transformer, grouping_or_grouping_id, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformGroupingOrGroupingIdInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformGroupingKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformGroupingKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformGroupingIdKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformGroupingIdKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformQuestionMarkNumberedParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto number_literal = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformQuestionMarkNumberedParameter(transformer, std::move(number_literal)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAnonymousParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformAnonymousParameter(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNumberedParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto number_literal = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformNumberedParameter(transformer, std::move(number_literal)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformColLabelParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_label = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformColLabelParameter(transformer, col_label); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformPositionalExpressionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto number_literal = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformPositionalExpression(transformer, std::move(number_literal)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDefaultExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformDefaultExpression(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformListComprehensionExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(1)); - vector col_id_or_string; - auto col_id_or_string_items = ExtractParseResultsFromList(list_pr.GetChild(3)); - for (auto &col_id_or_string_item : col_id_or_string_items) { - auto col_id_or_string_value = transformer.Transform(col_id_or_string_item.get()); - col_id_or_string.push_back(col_id_or_string_value); - } - auto expression_1 = transformer.Transform>(list_pr.GetChild(5)); - optional> list_comprehension_filter {}; - auto &list_comprehension_filter_opt = list_pr.GetChild(6).Cast(); - if (list_comprehension_filter_opt.HasResult()) { - auto list_comprehension_filter_value = - transformer.Transform>(list_comprehension_filter_opt.GetResult()); - list_comprehension_filter = std::move(list_comprehension_filter_value); - } - auto result = TransformListComprehensionExpression(transformer, std::move(expression), col_id_or_string, - std::move(expression_1), std::move(list_comprehension_filter)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformListComprehensionFilterInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformListComprehensionFilter(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformParensExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = TransformParensExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformMeasureSemanticsExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto measure_semantics = transformer.Transform(list_pr.GetChild(0)); - auto function_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformMeasureSemanticsExpression(transformer, measure_semantics, std::move(function_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSingleExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto lambda_arrow_expression = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformExpression(transformer, std::move(lambda_arrow_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformColumnDefaultExprInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_def_or_expr = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformColumnDefaultExpr(transformer, std::move(col_def_or_expr)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformLambdaArrowExpressionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto logical_or_expression = transformer.Transform>(list_pr.GetChild(0)); - optional>> single_arrow_pair {}; - auto &single_arrow_pair_opt = list_pr.GetChild(1).Cast(); - if (single_arrow_pair_opt.HasResult()) { - vector> single_arrow_pair_value; - auto &single_arrow_pair_value_repeat_1 = single_arrow_pair_opt.GetResult().Cast(); - for (auto &single_arrow_pair_value_item_1 : single_arrow_pair_value_repeat_1.GetChildren()) { - auto single_arrow_pair_value_value_1 = - transformer.Transform>(single_arrow_pair_value_item_1.get()); - single_arrow_pair_value.push_back(std::move(single_arrow_pair_value_value_1)); - } - single_arrow_pair = std::move(single_arrow_pair_value); - } - auto result = - TransformLambdaArrowExpression(transformer, std::move(logical_or_expression), std::move(single_arrow_pair)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSingleArrowPairInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto logical_or_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformSingleArrowPair(transformer, std::move(logical_or_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformLogicalOrExpressionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto logical_and_expression = transformer.Transform>(list_pr.GetChild(0)); - optional>> logical_or_expression_tail {}; - auto &logical_or_expression_tail_opt = list_pr.GetChild(1).Cast(); - if (logical_or_expression_tail_opt.HasResult()) { - vector> logical_or_expression_tail_value; - auto &logical_or_expression_tail_value_repeat_1 = - logical_or_expression_tail_opt.GetResult().Cast(); - for (auto &logical_or_expression_tail_value_item_1 : logical_or_expression_tail_value_repeat_1.GetChildren()) { - auto logical_or_expression_tail_value_value_1 = - transformer.Transform>(logical_or_expression_tail_value_item_1.get()); - logical_or_expression_tail_value.push_back(std::move(logical_or_expression_tail_value_value_1)); - } - logical_or_expression_tail = std::move(logical_or_expression_tail_value); - } - auto result = TransformLogicalOrExpression(transformer, std::move(logical_and_expression), - std::move(logical_or_expression_tail)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformLogicalOrExpressionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto logical_and_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformLogicalOrExpressionTail(transformer, std::move(logical_and_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformColDefOrExprInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_def_and_expr = transformer.Transform>(list_pr.GetChild(0)); - optional>> col_def_or_expression_tail {}; - auto &col_def_or_expression_tail_opt = list_pr.GetChild(1).Cast(); - if (col_def_or_expression_tail_opt.HasResult()) { - vector> col_def_or_expression_tail_value; - auto &col_def_or_expression_tail_value_repeat_1 = - col_def_or_expression_tail_opt.GetResult().Cast(); - for (auto &col_def_or_expression_tail_value_item_1 : col_def_or_expression_tail_value_repeat_1.GetChildren()) { - auto col_def_or_expression_tail_value_value_1 = - transformer.Transform>(col_def_or_expression_tail_value_item_1.get()); - col_def_or_expression_tail_value.push_back(std::move(col_def_or_expression_tail_value_value_1)); - } - col_def_or_expression_tail = std::move(col_def_or_expression_tail_value); - } - auto result = - TransformColDefOrExpr(transformer, std::move(col_def_and_expr), std::move(col_def_or_expression_tail)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformColDefOrExpressionTailInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_def_and_expr = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformColDefOrExpressionTail(transformer, std::move(col_def_and_expr)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformLogicalAndExpressionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto logical_not_expression = transformer.Transform>(list_pr.GetChild(0)); - optional>> logical_and_expression_tail {}; - auto &logical_and_expression_tail_opt = list_pr.GetChild(1).Cast(); - if (logical_and_expression_tail_opt.HasResult()) { - vector> logical_and_expression_tail_value; - auto &logical_and_expression_tail_value_repeat_1 = - logical_and_expression_tail_opt.GetResult().Cast(); - for (auto &logical_and_expression_tail_value_item_1 : - logical_and_expression_tail_value_repeat_1.GetChildren()) { - auto logical_and_expression_tail_value_value_1 = - transformer.Transform>(logical_and_expression_tail_value_item_1.get()); - logical_and_expression_tail_value.push_back(std::move(logical_and_expression_tail_value_value_1)); - } - logical_and_expression_tail = std::move(logical_and_expression_tail_value); - } - auto result = TransformLogicalAndExpression(transformer, std::move(logical_not_expression), - std::move(logical_and_expression_tail)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformLogicalAndExpressionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto logical_not_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformLogicalAndExpressionTail(transformer, std::move(logical_not_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformColDefAndExprInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto is_distinct_from_expression = transformer.Transform>(list_pr.GetChild(0)); - optional>> col_def_and_expression_tail {}; - auto &col_def_and_expression_tail_opt = list_pr.GetChild(1).Cast(); - if (col_def_and_expression_tail_opt.HasResult()) { - vector> col_def_and_expression_tail_value; - auto &col_def_and_expression_tail_value_repeat_1 = - col_def_and_expression_tail_opt.GetResult().Cast(); - for (auto &col_def_and_expression_tail_value_item_1 : - col_def_and_expression_tail_value_repeat_1.GetChildren()) { - auto col_def_and_expression_tail_value_value_1 = - transformer.Transform>(col_def_and_expression_tail_value_item_1.get()); - col_def_and_expression_tail_value.push_back(std::move(col_def_and_expression_tail_value_value_1)); - } - col_def_and_expression_tail = std::move(col_def_and_expression_tail_value); - } - auto result = TransformColDefAndExpr(transformer, std::move(is_distinct_from_expression), - std::move(col_def_and_expression_tail)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformColDefAndExpressionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto is_distinct_from_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformColDefAndExpressionTail(transformer, std::move(is_distinct_from_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformLogicalNotExpressionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> not_expression {}; - auto ¬_expression_opt = list_pr.GetChild(0).Cast(); - if (not_expression_opt.HasResult()) { - auto not_expression_value = transformer.Transform>(not_expression_opt.GetResult()); - not_expression = std::move(not_expression_value); - } - auto is_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformLogicalNotExpression(transformer, std::move(not_expression), std::move(is_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNotExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector not_keyword; - auto ¬_keyword_repeat = list_pr.GetChild(0).Cast(); - for (auto ¬_keyword_item : not_keyword_repeat.GetChildren()) { - auto not_keyword_value = transformer.Transform(not_keyword_item.get()); - not_keyword.push_back(not_keyword_value); - } - auto result = TransformNotExpression(transformer, not_keyword); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNotKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformNotKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformIsExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto is_distinct_from_expression = transformer.Transform>(list_pr.GetChild(0)); - optional>> is_test {}; - auto &is_test_opt = list_pr.GetChild(1).Cast(); - if (is_test_opt.HasResult()) { - vector> is_test_value; - auto &is_test_value_repeat_1 = is_test_opt.GetResult().Cast(); - for (auto &is_test_value_item_1 : is_test_value_repeat_1.GetChildren()) { - auto is_test_value_value_1 = - transformer.Transform>(is_test_value_item_1.get()); - is_test_value.push_back(std::move(is_test_value_value_1)); - } - is_test = std::move(is_test_value); - } - auto result = TransformIsExpression(transformer, std::move(is_distinct_from_expression), std::move(is_test)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIsTestInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIsLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto is_literal_value = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformIsLiteral(transformer, has_result, is_literal_value); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIsLiteralValueInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformUnknownLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformUnknownLiteral(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformNotNullInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNotNullKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformNotNullKeyword(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNotNullOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformNotNullOperator(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIsNullInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto is_null_operator = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformIsNull(transformer, std::move(is_null_operator)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIsNullOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformIsNullOperator(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformIsDistinctFromExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto comparison_expression = transformer.Transform>(list_pr.GetChild(0)); - optional> is_distinct_from_tail {}; - auto &is_distinct_from_tail_opt = list_pr.GetChild(1).Cast(); - if (is_distinct_from_tail_opt.HasResult()) { - vector is_distinct_from_tail_value; - auto &is_distinct_from_tail_value_repeat_1 = is_distinct_from_tail_opt.GetResult().Cast(); - for (auto &is_distinct_from_tail_value_item_1 : is_distinct_from_tail_value_repeat_1.GetChildren()) { - auto is_distinct_from_tail_value_value_1 = - transformer.Transform(is_distinct_from_tail_value_item_1.get()); - is_distinct_from_tail_value.push_back(std::move(is_distinct_from_tail_value_value_1)); - } - is_distinct_from_tail = std::move(is_distinct_from_tail_value); - } - auto result = TransformIsDistinctFromExpression(transformer, std::move(comparison_expression), - std::move(is_distinct_from_tail)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIsDistinctFromTailInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto is_distinct_from_op = transformer.Transform(list_pr.GetChild(0)); - auto comparison_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformIsDistinctFromTail(transformer, is_distinct_from_op, std::move(comparison_expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIsDistinctFromOpInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto result = TransformIsDistinctFromOp(transformer, has_result); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformComparisonExpressionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto between_in_like_expression = transformer.Transform>(list_pr.GetChild(0)); - optional> comparison_expression_tail {}; - auto &comparison_expression_tail_opt = list_pr.GetChild(1).Cast(); - if (comparison_expression_tail_opt.HasResult()) { - vector comparison_expression_tail_value; - auto &comparison_expression_tail_value_repeat_1 = - comparison_expression_tail_opt.GetResult().Cast(); - for (auto &comparison_expression_tail_value_item_1 : comparison_expression_tail_value_repeat_1.GetChildren()) { - auto comparison_expression_tail_value_value_1 = - transformer.Transform(comparison_expression_tail_value_item_1.get()); - comparison_expression_tail_value.push_back(std::move(comparison_expression_tail_value_value_1)); - } - comparison_expression_tail = std::move(comparison_expression_tail_value); - } - auto result = TransformComparisonExpression(transformer, std::move(between_in_like_expression), - std::move(comparison_expression_tail)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformComparisonExpressionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto comparison_operator = transformer.Transform(list_pr.GetChild(0)); - optional> not_expression {}; - auto ¬_expression_opt = list_pr.GetChild(1).Cast(); - if (not_expression_opt.HasResult()) { - auto not_expression_value = transformer.Transform>(not_expression_opt.GetResult()); - not_expression = std::move(not_expression_value); - } - auto between_in_like_expression = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformComparisonExpressionTail(transformer, comparison_operator, std::move(not_expression), - std::move(between_in_like_expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformComparisonOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformOperatorEqualInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformOperatorEqual(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformOperatorNotEqualInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformOperatorNotEqual(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformOperatorLessThanInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformOperatorLessThan(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformOperatorGreaterThanInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto result = TransformOperatorGreaterThan(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformOperatorLessThanEqualsInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto result = TransformOperatorLessThanEquals(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformOperatorGreaterThanEqualsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformOperatorGreaterThanEquals(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformBetweenInLikeExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto other_operator_expression = transformer.Transform>(list_pr.GetChild(0)); - optional between_in_like_op {}; - auto &between_in_like_op_opt = list_pr.GetChild(1).Cast(); - if (between_in_like_op_opt.HasResult()) { - auto between_in_like_op_value = - transformer.Transform(between_in_like_op_opt.GetResult()); - between_in_like_op = std::move(between_in_like_op_value); - } - auto result = TransformBetweenInLikeExpression(transformer, std::move(other_operator_expression), - std::move(between_in_like_op)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformBetweenInLikeOpInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(0).Cast(); - has_result = has_result_opt.HasResult(); - auto between_in_like_op_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformBetweenInLikeOp(transformer, has_result, std::move(between_in_like_op_expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformBetweenInLikeOpExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformLikeClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto like_variations = transformer.Transform(list_pr.GetChild(0)); - auto other_operator_expression = transformer.Transform>(list_pr.GetChild(1)); - optional> escape_clause {}; - auto &escape_clause_opt = list_pr.GetChild(2).Cast(); - if (escape_clause_opt.HasResult()) { - auto escape_clause_value = transformer.Transform>(escape_clause_opt.GetResult()); - escape_clause = std::move(escape_clause_value); - } - auto result = TransformLikeClause(transformer, like_variations, std::move(other_operator_expression), - std::move(escape_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformEscapeClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto comparison_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformEscapeClause(transformer, std::move(comparison_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformLikeVariationsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - string result; - if (choice_pr.GetResult().type == ParseResultType::IDENTIFIER) { - result = choice_pr.GetResult().Cast().identifier.GetIdentifierName(); - } else if (choice_pr.GetResult().type == ParseResultType::KEYWORD) { - result = choice_pr.GetResult().Cast().keyword; - } else if (choice_pr.GetResult().type == ParseResultType::STRING) { - result = choice_pr.GetResult().Cast().result; - } else if (choice_pr.GetResult().type == ParseResultType::OPERATOR) { - result = choice_pr.GetResult().Cast().operator_token; - } else { - result = transformer.Transform(choice_pr.GetResult()); - } - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformLikeTokenInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformLikeToken(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformILikeTokenInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformILikeToken(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformGlobTokenInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformGlobToken(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSimilarToTokenInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSimilarToToken(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformRegexMatchTokenInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformRegexMatchToken(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformRegexInsensitiveMatchTokenInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformRegexInsensitiveMatchToken(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformNotILikeOpInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformNotILikeOp(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformNotLikeOpInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformNotLikeOp(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformNotRegexInsensitiveMatchOpInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformNotRegexInsensitiveMatchOp(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformNotSimilarToOpInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformNotSimilarToOp(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformInClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto in_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformInClause(transformer, std::move(in_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformInExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformInContainsExpressionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto other_operator_expression = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformInContainsExpression(transformer, std::move(other_operator_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformInExpressionListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> expression; - auto expression_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &expression_item : expression_items) { - auto expression_value = transformer.Transform>(expression_item.get()); - expression.push_back(std::move(expression_value)); - } - auto result = TransformInExpressionList(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformInSelectStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto select_statement_internal = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = TransformInSelectStatement(transformer, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformBetweenClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto other_operator_expression = transformer.Transform>(list_pr.GetChild(1)); - auto other_operator_expression_1 = transformer.Transform>(list_pr.GetChild(3)); - auto result = TransformBetweenClause(transformer, std::move(other_operator_expression), - std::move(other_operator_expression_1)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformOtherOperatorExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto bitwise_expression = transformer.Transform>(list_pr.GetChild(0)); - optional> other_operator_tail {}; - auto &other_operator_tail_opt = list_pr.GetChild(1).Cast(); - if (other_operator_tail_opt.HasResult()) { - vector other_operator_tail_value; - auto &other_operator_tail_value_repeat_1 = other_operator_tail_opt.GetResult().Cast(); - for (auto &other_operator_tail_value_item_1 : other_operator_tail_value_repeat_1.GetChildren()) { - auto other_operator_tail_value_value_1 = - transformer.Transform(other_operator_tail_value_item_1.get()); - other_operator_tail_value.push_back(std::move(other_operator_tail_value_value_1)); - } - other_operator_tail = std::move(other_operator_tail_value); - } - auto result = - TransformOtherOperatorExpression(transformer, std::move(bitwise_expression), std::move(other_operator_tail)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOtherOperatorTailInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto other_operator = transformer.Transform(list_pr.GetChild(0)); - auto bitwise_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformOtherOperatorTail(transformer, std::move(other_operator), std::move(bitwise_expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOtherOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformAnyAllParsedOperatorInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto any_all_operator = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformAnyAllParsedOperator(transformer, any_all_operator); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNamedOtherOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto &choice_result = choice_pr.GetResult(); - string child; - if (choice_result.type == ParseResultType::IDENTIFIER) { - child = choice_result.Cast().identifier.GetIdentifierName(); - } else if (choice_result.type == ParseResultType::KEYWORD) { - child = choice_result.Cast().keyword; - } else if (choice_result.type == ParseResultType::STRING) { - child = choice_result.Cast().result; - } else if (choice_result.type == ParseResultType::OPERATOR) { - child = choice_result.Cast().operator_token; - } else { - child = transformer.Transform(choice_result); - } - auto result = TransformNamedOtherOperator(transformer, child); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAnyAllOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto any_op = transformer.Transform(list_pr.GetChild(0)); - auto any_or_all = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformAnyAllOperator(transformer, any_op, any_or_all); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformAnyOrAllInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSubqueryAnyInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSubqueryAny(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSubqueryAllInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSubqueryAll(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformInetOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = choice_pr.GetResult().Cast().keyword; - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformJsonOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - string result = "->>"; - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformListOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = choice_pr.GetResult().Cast().keyword; - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformStringOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = choice_pr.GetResult().Cast().keyword; - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformQualifiedOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto qualified_operator_contents = transformer.Transform(ExtractResultFromParens(list_pr.GetChild(1))); - auto result = TransformQualifiedOperator(transformer, qualified_operator_contents); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformQualifiedOperatorContentsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> col_id_dot {}; - auto &col_id_dot_opt = list_pr.GetChild(0).Cast(); - if (col_id_dot_opt.HasResult()) { - vector col_id_dot_value; - auto &col_id_dot_value_repeat_1 = col_id_dot_opt.GetResult().Cast(); - for (auto &col_id_dot_value_item_1 : col_id_dot_value_repeat_1.GetChildren()) { - auto col_id_dot_value_value_1 = transformer.Transform(col_id_dot_value_item_1.get()); - col_id_dot_value.push_back(col_id_dot_value_value_1); - } - col_id_dot = col_id_dot_value; - } - auto any_op = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformQualifiedOperatorContents(transformer, col_id_dot, any_op); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformAnyOpInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = choice_pr.GetResult().Cast().keyword; - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformBitwiseExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto additive_expression = transformer.Transform>(list_pr.GetChild(0)); - optional> bitwise_expression_tail {}; - auto &bitwise_expression_tail_opt = list_pr.GetChild(1).Cast(); - if (bitwise_expression_tail_opt.HasResult()) { - vector bitwise_expression_tail_value; - auto &bitwise_expression_tail_value_repeat_1 = - bitwise_expression_tail_opt.GetResult().Cast(); - for (auto &bitwise_expression_tail_value_item_1 : bitwise_expression_tail_value_repeat_1.GetChildren()) { - auto bitwise_expression_tail_value_value_1 = - transformer.Transform(bitwise_expression_tail_value_item_1.get()); - bitwise_expression_tail_value.push_back(std::move(bitwise_expression_tail_value_value_1)); - } - bitwise_expression_tail = std::move(bitwise_expression_tail_value); - } - auto result = - TransformBitwiseExpression(transformer, std::move(additive_expression), std::move(bitwise_expression_tail)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformBitwiseExpressionTailInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto bit_operator = transformer.Transform(list_pr.GetChild(0)); - auto additive_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformBitwiseExpressionTail(transformer, bit_operator, std::move(additive_expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformBitOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = choice_pr.GetResult().Cast().keyword; - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformAdditiveExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto multiplicative_expression = transformer.Transform>(list_pr.GetChild(0)); - optional> additive_expression_tail {}; - auto &additive_expression_tail_opt = list_pr.GetChild(1).Cast(); - if (additive_expression_tail_opt.HasResult()) { - vector additive_expression_tail_value; - auto &additive_expression_tail_value_repeat_1 = - additive_expression_tail_opt.GetResult().Cast(); - for (auto &additive_expression_tail_value_item_1 : additive_expression_tail_value_repeat_1.GetChildren()) { - auto additive_expression_tail_value_value_1 = - transformer.Transform(additive_expression_tail_value_item_1.get()); - additive_expression_tail_value.push_back(std::move(additive_expression_tail_value_value_1)); - } - additive_expression_tail = std::move(additive_expression_tail_value); - } - auto result = TransformAdditiveExpression(transformer, std::move(multiplicative_expression), - std::move(additive_expression_tail)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformAdditiveExpressionTailInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto term = transformer.Transform(list_pr.GetChild(0)); - auto multiplicative_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = - TransformAdditiveExpressionTail(transformer, term, std::move(multiplicative_expression), parse_result.offset); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTermInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = choice_pr.GetResult().Cast().keyword; - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformMultiplicativeExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto exponentiation_expression = transformer.Transform>(list_pr.GetChild(0)); - optional> multiplicative_expression_tail {}; - auto &multiplicative_expression_tail_opt = list_pr.GetChild(1).Cast(); - if (multiplicative_expression_tail_opt.HasResult()) { - vector multiplicative_expression_tail_value; - auto &multiplicative_expression_tail_value_repeat_1 = - multiplicative_expression_tail_opt.GetResult().Cast(); - for (auto &multiplicative_expression_tail_value_item_1 : - multiplicative_expression_tail_value_repeat_1.GetChildren()) { - auto multiplicative_expression_tail_value_value_1 = - transformer.Transform(multiplicative_expression_tail_value_item_1.get()); - multiplicative_expression_tail_value.push_back(std::move(multiplicative_expression_tail_value_value_1)); - } - multiplicative_expression_tail = std::move(multiplicative_expression_tail_value); - } - auto result = TransformMultiplicativeExpression(transformer, std::move(exponentiation_expression), - std::move(multiplicative_expression_tail)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformMultiplicativeExpressionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto factor = transformer.Transform(list_pr.GetChild(0)); - auto exponentiation_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformMultiplicativeExpressionTail(transformer, factor, std::move(exponentiation_expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformFactorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = choice_pr.GetResult().Cast().keyword; - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformExponentiationExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto collate_expression = transformer.Transform>(list_pr.GetChild(0)); - optional> exponentiation_expression_tail {}; - auto &exponentiation_expression_tail_opt = list_pr.GetChild(1).Cast(); - if (exponentiation_expression_tail_opt.HasResult()) { - vector exponentiation_expression_tail_value; - auto &exponentiation_expression_tail_value_repeat_1 = - exponentiation_expression_tail_opt.GetResult().Cast(); - for (auto &exponentiation_expression_tail_value_item_1 : - exponentiation_expression_tail_value_repeat_1.GetChildren()) { - auto exponentiation_expression_tail_value_value_1 = - transformer.Transform(exponentiation_expression_tail_value_item_1.get()); - exponentiation_expression_tail_value.push_back(std::move(exponentiation_expression_tail_value_value_1)); - } - exponentiation_expression_tail = std::move(exponentiation_expression_tail_value); - } - auto result = TransformExponentiationExpression(transformer, std::move(collate_expression), - std::move(exponentiation_expression_tail)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformExponentiationExpressionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto exponent_operator = transformer.Transform(list_pr.GetChild(0)); - auto collate_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformExponentiationExpressionTail(transformer, exponent_operator, std::move(collate_expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformExponentOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = choice_pr.GetResult().Cast().keyword; - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCollateExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto at_time_zone_expression = transformer.Transform>(list_pr.GetChild(0)); - optional>> collate_expression_tail {}; - auto &collate_expression_tail_opt = list_pr.GetChild(1).Cast(); - if (collate_expression_tail_opt.HasResult()) { - vector> collate_expression_tail_value; - auto &collate_expression_tail_value_repeat_1 = - collate_expression_tail_opt.GetResult().Cast(); - for (auto &collate_expression_tail_value_item_1 : collate_expression_tail_value_repeat_1.GetChildren()) { - auto collate_expression_tail_value_value_1 = - transformer.Transform>(collate_expression_tail_value_item_1.get()); - collate_expression_tail_value.push_back(std::move(collate_expression_tail_value_value_1)); - } - collate_expression_tail = std::move(collate_expression_tail_value); - } - auto result = - TransformCollateExpression(transformer, std::move(at_time_zone_expression), std::move(collate_expression_tail)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCollateExpressionTailInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto at_time_zone_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformCollateExpressionTail(transformer, std::move(at_time_zone_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformAtTimeZoneExpressionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto prefix_expression = transformer.Transform>(list_pr.GetChild(0)); - optional>> at_time_zone_expression_tail {}; - auto &at_time_zone_expression_tail_opt = list_pr.GetChild(1).Cast(); - if (at_time_zone_expression_tail_opt.HasResult()) { - vector> at_time_zone_expression_tail_value; - auto &at_time_zone_expression_tail_value_repeat_1 = - at_time_zone_expression_tail_opt.GetResult().Cast(); - for (auto &at_time_zone_expression_tail_value_item_1 : - at_time_zone_expression_tail_value_repeat_1.GetChildren()) { - auto at_time_zone_expression_tail_value_value_1 = - transformer.Transform>(at_time_zone_expression_tail_value_item_1.get()); - at_time_zone_expression_tail_value.push_back(std::move(at_time_zone_expression_tail_value_value_1)); - } - at_time_zone_expression_tail = std::move(at_time_zone_expression_tail_value); - } - auto result = TransformAtTimeZoneExpression(transformer, std::move(prefix_expression), - std::move(at_time_zone_expression_tail)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformAtTimeZoneExpressionTailInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto prefix_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformAtTimeZoneExpressionTail(transformer, std::move(prefix_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPrefixOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - string result; - if (choice_pr.GetResult().type == ParseResultType::IDENTIFIER) { - result = choice_pr.GetResult().Cast().identifier.GetIdentifierName(); - } else if (choice_pr.GetResult().type == ParseResultType::KEYWORD) { - result = choice_pr.GetResult().Cast().keyword; - } else if (choice_pr.GetResult().type == ParseResultType::STRING) { - result = choice_pr.GetResult().Cast().result; - } else if (choice_pr.GetResult().type == ParseResultType::OPERATOR) { - result = choice_pr.GetResult().Cast().operator_token; - } else { - result = transformer.Transform(choice_pr.GetResult()); - } - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformMinusPrefixOperatorInternal(PEGTransformer &transformer, ParseResult &parse_result) { - string result = "-"; - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformPlusPrefixOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - string result = "+"; - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformTildePrefixOperatorInternal(PEGTransformer &transformer, ParseResult &parse_result) { - string result = "~"; - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformBaseExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto single_expression = transformer.Transform>(list_pr.GetChild(0)); - optional>> indirection_list {}; - auto &indirection_list_opt = list_pr.GetChild(1).Cast(); - if (indirection_list_opt.HasResult()) { - auto indirection_list_value = - transformer.Transform>>(indirection_list_opt.GetResult()); - indirection_list = std::move(indirection_list_value); - } - auto result = TransformBaseExpression(transformer, std::move(single_expression), std::move(indirection_list)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIndirectionListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> indirection; - auto &indirection_repeat = list_pr.GetChild(0).Cast(); - for (auto &indirection_item : indirection_repeat.GetChildren()) { - auto indirection_value = transformer.Transform>(indirection_item.get()); - indirection.push_back(std::move(indirection_value)); - } - auto result = TransformIndirectionList(transformer, std::move(indirection)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIndirectionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCastOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto type = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformCastOperator(transformer, type); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDotOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDotMethodOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto method_expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformDotMethodOperator(transformer, std::move(method_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDotColumnOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_label = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformDotColumnOperator(transformer, col_label); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMethodExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_label = transformer.Transform(list_pr.GetChild(0)); - auto method_expression_arguments = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformMethodExpression(transformer, col_label, std::move(method_expression_arguments)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformMethodExpressionArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto method_expression_argument_list = - transformer.Transform(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = TransformMethodExpressionArguments(transformer, std::move(method_expression_argument_list)); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformMethodExpressionArgumentListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional distinct_or_all {}; - auto &distinct_or_all_opt = list_pr.GetChild(0).Cast(); - if (distinct_or_all_opt.HasResult()) { - auto distinct_or_all_value = transformer.Transform(distinct_or_all_opt.GetResult()); - distinct_or_all = distinct_or_all_value; - } - optional> method_function_arguments {}; - auto &method_function_arguments_opt = list_pr.GetChild(1).Cast(); - if (method_function_arguments_opt.HasResult()) { - auto method_function_arguments_value = - transformer.Transform>(method_function_arguments_opt.GetResult()); - method_function_arguments = std::move(method_function_arguments_value); - } - optional> order_by_clause {}; - auto &order_by_clause_opt = list_pr.GetChild(2).Cast(); - if (order_by_clause_opt.HasResult()) { - auto order_by_clause_value = transformer.Transform>(order_by_clause_opt.GetResult()); - order_by_clause = std::move(order_by_clause_value); - } - optional ignore_or_respect_nulls {}; - auto &ignore_or_respect_nulls_opt = list_pr.GetChild(3).Cast(); - if (ignore_or_respect_nulls_opt.HasResult()) { - auto ignore_or_respect_nulls_value = transformer.Transform(ignore_or_respect_nulls_opt.GetResult()); - ignore_or_respect_nulls = ignore_or_respect_nulls_value; - } - auto result = - TransformMethodExpressionArgumentList(transformer, distinct_or_all, std::move(method_function_arguments), - std::move(order_by_clause), ignore_or_respect_nulls); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformMethodFunctionArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector function_argument; - auto function_argument_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - for (auto &function_argument_item : function_argument_items) { - auto function_argument_value = transformer.Transform(function_argument_item.get()); - function_argument.push_back(std::move(function_argument_value)); - } - auto result = TransformMethodFunctionArguments(transformer, std::move(function_argument)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSliceExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto slice_bound = transformer.Transform>>(list_pr.GetChild(1)); - auto result = TransformSliceExpression(transformer, std::move(slice_bound)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSliceBoundInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> expression {}; - auto &expression_opt = list_pr.GetChild(0).Cast(); - if (expression_opt.HasResult()) { - auto expression_value = transformer.Transform>(expression_opt.GetResult()); - expression = std::move(expression_value); - } - optional> end_slice_bound {}; - auto &end_slice_bound_opt = list_pr.GetChild(1).Cast(); - if (end_slice_bound_opt.HasResult()) { - auto end_slice_bound_value = - transformer.Transform>(end_slice_bound_opt.GetResult()); - end_slice_bound = std::move(end_slice_bound_value); - } - optional> step_slice_bound {}; - auto &step_slice_bound_opt = list_pr.GetChild(2).Cast(); - if (step_slice_bound_opt.HasResult()) { - auto step_slice_bound_value = - transformer.Transform>(step_slice_bound_opt.GetResult()); - step_slice_bound = std::move(step_slice_bound_value); - } - auto result = TransformSliceBound(transformer, std::move(expression), std::move(end_slice_bound), - std::move(step_slice_bound)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformEndSliceBoundInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> end_slice_value {}; - auto &end_slice_value_opt = list_pr.GetChild(1).Cast(); - if (end_slice_value_opt.HasResult()) { - auto end_slice_value_value = - transformer.Transform>(end_slice_value_opt.GetResult()); - end_slice_value = std::move(end_slice_value_value); - } - auto result = TransformEndSliceBound(transformer, std::move(end_slice_value)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformEndSliceValueInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformEndSliceMinusInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformEndSliceMinus(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformStepSliceBoundInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> expression {}; - auto &expression_opt = list_pr.GetChild(1).Cast(); - if (expression_opt.HasResult()) { - auto expression_value = transformer.Transform>(expression_opt.GetResult()); - expression = std::move(expression_value); - } - auto result = TransformStepSliceBound(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPostfixOperatorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformPostfixOperator(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformSpecialFunctionExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCoalesceExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> expression; - auto expression_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(1))); - for (auto &expression_item : expression_items) { - auto expression_value = transformer.Transform>(expression_item.get()); - expression.push_back(std::move(expression_value)); - } - auto result = TransformCoalesceExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformUnpackExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(1))); - auto result = TransformUnpackExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTryExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(1))); - auto result = TransformTryExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformColumnsExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(0).Cast(); - has_result = has_result_opt.HasResult(); - auto expression = transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(2))); - auto result = TransformColumnsExpression(transformer, has_result, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformExtractExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto extract_arguments = - transformer.Transform>>(ExtractResultFromParens(list_pr.GetChild(1))); - auto result = TransformExtractExpression(transformer, std::move(extract_arguments)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformExtractArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto extract_argument = transformer.Transform>(list_pr.GetChild(0)); - auto expression = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformExtractArguments(transformer, std::move(extract_argument), std::move(expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformLambdaExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector col_id_or_string; - auto col_id_or_string_items = ExtractParseResultsFromList(list_pr.GetChild(1)); - for (auto &col_id_or_string_item : col_id_or_string_items) { - auto col_id_or_string_value = transformer.Transform(col_id_or_string_item.get()); - col_id_or_string.push_back(col_id_or_string_value); - } - auto expression = transformer.Transform>(list_pr.GetChild(3)); - auto result = TransformLambdaExpression(transformer, col_id_or_string, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNullIfExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto null_if_arguments = - transformer.Transform>>(ExtractResultFromParens(list_pr.GetChild(1))); - auto result = TransformNullIfExpression(transformer, std::move(null_if_arguments)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNullIfArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - auto expression_1 = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformNullIfArguments(transformer, std::move(expression), std::move(expression_1)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPositionExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto position_arguments = - transformer.Transform>>(ExtractResultFromParens(list_pr.GetChild(1))); - auto result = TransformPositionExpression(transformer, std::move(position_arguments)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPositionArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto other_operator_expression = transformer.Transform>(list_pr.GetChild(0)); - auto expression = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformPositionArguments(transformer, std::move(other_operator_expression), std::move(expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformRowExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional>> expression {}; - auto &expression_opt = ExtractResultFromParens(list_pr.GetChild(1)).Cast(); - if (expression_opt.HasResult()) { - vector> expression_value; - auto expression_value_items_1 = ExtractParseResultsFromList(expression_opt.GetResult()); - for (auto &expression_value_item_1 : expression_value_items_1) { - auto expression_value_value_1 = - transformer.Transform>(expression_value_item_1.get()); - expression_value.push_back(std::move(expression_value_value_1)); - } - expression = std::move(expression_value); - } - auto result = TransformRowExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformSubstringExpressionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto substring_arguments = - transformer.Transform>>(ExtractResultFromParens(list_pr.GetChild(1))); - auto result = TransformSubstringExpression(transformer, std::move(substring_arguments)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSubstringArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>>(choice_pr.GetResult()); - return make_uniq>>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformSubstringExpressionListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> expression; - auto expression_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - for (auto &expression_item : expression_items) { - auto expression_value = transformer.Transform>(expression_item.get()); - expression.push_back(std::move(expression_value)); - } - auto result = TransformSubstringExpressionList(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformSubstringParametersInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - auto substring_from_for = transformer.Transform>>(list_pr.GetChild(1)); - auto result = TransformSubstringParameters(transformer, std::move(expression), std::move(substring_from_for)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSubstringFromForInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>>(choice_pr.GetResult()); - return make_uniq>>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformSubstringFromOptionalForInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto from_expression = transformer.Transform>(list_pr.GetChild(0)); - optional> for_expression {}; - auto &for_expression_opt = list_pr.GetChild(1).Cast(); - if (for_expression_opt.HasResult()) { - auto for_expression_value = transformer.Transform>(for_expression_opt.GetResult()); - for_expression = std::move(for_expression_value); - } - auto result = TransformSubstringFromOptionalFor(transformer, std::move(from_expression), std::move(for_expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSubstringForInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto for_expression = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformSubstringFor(transformer, std::move(for_expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTrimExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto trim_arguments = transformer.Transform(ExtractResultFromParens(list_pr.GetChild(1))); - auto result = TransformTrimExpression(transformer, std::move(trim_arguments)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTrimArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional trim_direction {}; - auto &trim_direction_opt = list_pr.GetChild(0).Cast(); - if (trim_direction_opt.HasResult()) { - auto trim_direction_value = transformer.Transform(trim_direction_opt.GetResult()); - trim_direction = trim_direction_value; - } - optional> trim_source {}; - auto &trim_source_opt = list_pr.GetChild(1).Cast(); - if (trim_source_opt.HasResult()) { - auto trim_source_value = transformer.Transform>(trim_source_opt.GetResult()); - trim_source = std::move(trim_source_value); - } - vector> expression; - auto expression_items = ExtractParseResultsFromList(list_pr.GetChild(2)); - for (auto &expression_item : expression_items) { - auto expression_value = transformer.Transform>(expression_item.get()); - expression.push_back(std::move(expression_value)); - } - auto result = TransformTrimArguments(transformer, trim_direction, std::move(trim_source), std::move(expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTrimDirectionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - string result; - if (choice_pr.GetResult().type == ParseResultType::IDENTIFIER) { - result = choice_pr.GetResult().Cast().identifier.GetIdentifierName(); - } else if (choice_pr.GetResult().type == ParseResultType::KEYWORD) { - result = choice_pr.GetResult().Cast().keyword; - } else if (choice_pr.GetResult().type == ParseResultType::STRING) { - result = choice_pr.GetResult().Cast().result; - } else if (choice_pr.GetResult().type == ParseResultType::OPERATOR) { - result = choice_pr.GetResult().Cast().operator_token; - } else { - result = transformer.Transform(choice_pr.GetResult()); - } - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTrimBothInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformTrimBoth(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTrimLeadingInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformTrimLeading(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTrimTrailingInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformTrimTrailing(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTrimSourceInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> expression {}; - auto &expression_opt = list_pr.GetChild(0).Cast(); - if (expression_opt.HasResult()) { - auto expression_value = transformer.Transform>(expression_opt.GetResult()); - expression = std::move(expression_value); - } - auto result = TransformTrimSource(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOverlayExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto overlay_arguments = - transformer.Transform>>(ExtractResultFromParens(list_pr.GetChild(1))); - auto result = TransformOverlayExpression(transformer, std::move(overlay_arguments)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOverlayArgumentsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>>(choice_pr.GetResult()); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOverlayParametersInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - auto expression_1 = transformer.Transform>(list_pr.GetChild(2)); - auto from_expression = transformer.Transform>(list_pr.GetChild(3)); - optional> for_expression {}; - auto &for_expression_opt = list_pr.GetChild(4).Cast(); - if (for_expression_opt.HasResult()) { - auto for_expression_value = transformer.Transform>(for_expression_opt.GetResult()); - for_expression = std::move(for_expression_value); - } - auto result = TransformOverlayParameters(transformer, std::move(expression), std::move(expression_1), - std::move(from_expression), std::move(for_expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformFromExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformFromExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformForExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformForExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformOverlayExpressionListInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> expression; - auto expression_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - for (auto &expression_item : expression_items) { - auto expression_value = transformer.Transform>(expression_item.get()); - expression.push_back(std::move(expression_value)); - } - auto result = TransformOverlayExpressionList(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformExtractArgumentInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformExtractDatePartArgumentInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto extract_date_part = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformExtractDatePartArgument(transformer, extract_date_part); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformExtractIdentifierArgumentInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(0).Cast().identifier; - auto result = TransformExtractIdentifierArgument(transformer, identifier); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformExtractStringArgumentInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto string_literal = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformExtractStringArgument(transformer, string_literal); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformExtractDatePartInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformExternalResourceStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCreateExternalResourceStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto string_literal = transformer.Transform(list_pr.GetChild(3)); - optional attach_alias {}; - auto &attach_alias_opt = list_pr.GetChild(4).Cast(); - if (attach_alias_opt.HasResult()) { - auto attach_alias_value = transformer.Transform(attach_alias_opt.GetResult()); - attach_alias = attach_alias_value; - } - optional> external_resource_creation_options {}; - auto &external_resource_creation_options_opt = list_pr.GetChild(5).Cast(); - if (external_resource_creation_options_opt.HasResult()) { - auto external_resource_creation_options_value = - transformer.Transform>(external_resource_creation_options_opt.GetResult()); - external_resource_creation_options = external_resource_creation_options_value; - } - auto result = TransformCreateExternalResourceStmt(transformer, string_literal, attach_alias, - external_resource_creation_options); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformRegisterExternalResourceStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto string_literal = transformer.Transform(list_pr.GetChild(3)); - optional attach_alias {}; - auto &attach_alias_opt = list_pr.GetChild(4).Cast(); - if (attach_alias_opt.HasResult()) { - auto attach_alias_value = transformer.Transform(attach_alias_opt.GetResult()); - attach_alias = attach_alias_value; - } - auto expression = transformer.Transform>(list_pr.GetChild(6)); - auto result = - TransformRegisterExternalResourceStmt(transformer, string_literal, attach_alias, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformDestroyExternalResourceStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id = transformer.Transform(list_pr.GetChild(3)); - auto result = TransformDestroyExternalResourceStmt(transformer, col_id); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformShowExternalResourcesStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional show_all_modifier {}; - auto &show_all_modifier_opt = list_pr.GetChild(1).Cast(); - if (show_all_modifier_opt.HasResult()) { - auto show_all_modifier_value = transformer.Transform(show_all_modifier_opt.GetResult()); - show_all_modifier = show_all_modifier_value; - } - auto result = TransformShowExternalResourcesStmt(transformer, show_all_modifier); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformShowAllModifierInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformShowAllModifier(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformExternalResourceCreationOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto generic_copy_option_list = transformer.Transform>(list_pr.GetChild(0)); - auto result = generic_copy_option_list; - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformInsertStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional with_clause {}; - auto &with_clause_opt = list_pr.GetChild(0).Cast(); - if (with_clause_opt.HasResult()) { - auto with_clause_value = transformer.Transform(with_clause_opt.GetResult()); - with_clause = std::move(with_clause_value); - } - optional or_action {}; - auto &or_action_opt = list_pr.GetChild(2).Cast(); - if (or_action_opt.HasResult()) { - auto or_action_value = transformer.Transform(or_action_opt.GetResult()); - or_action = or_action_value; - } - auto insert_target = transformer.Transform>(list_pr.GetChild(4)); - optional by_name_or_position {}; - auto &by_name_or_position_opt = list_pr.GetChild(5).Cast(); - if (by_name_or_position_opt.HasResult()) { - auto by_name_or_position_value = transformer.Transform(by_name_or_position_opt.GetResult()); - by_name_or_position = by_name_or_position_value; - } - optional> insert_column_list {}; - auto &insert_column_list_opt = list_pr.GetChild(6).Cast(); - if (insert_column_list_opt.HasResult()) { - auto insert_column_list_value = transformer.Transform>(insert_column_list_opt.GetResult()); - insert_column_list = insert_column_list_value; - } - auto insert_values = transformer.Transform(list_pr.GetChild(7)); - optional> on_conflict_clause {}; - auto &on_conflict_clause_opt = list_pr.GetChild(8).Cast(); - if (on_conflict_clause_opt.HasResult()) { - auto on_conflict_clause_value = - transformer.Transform>(on_conflict_clause_opt.GetResult()); - on_conflict_clause = std::move(on_conflict_clause_value); - } - optional>> returning_clause {}; - auto &returning_clause_opt = list_pr.GetChild(9).Cast(); - if (returning_clause_opt.HasResult()) { - auto returning_clause_value = - transformer.Transform>>(returning_clause_opt.GetResult()); - returning_clause = std::move(returning_clause_value); - } - auto result = TransformInsertStatement(transformer, std::move(with_clause), or_action, std::move(insert_target), - by_name_or_position, insert_column_list, std::move(insert_values), - std::move(on_conflict_clause), std::move(returning_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOrActionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformInsertOrReplaceInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformInsertOrReplace(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformInsertOrIgnoreInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformInsertOrIgnore(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformByNameOrPositionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformInsertByNameOrderInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto insert_by_name = transformer.Transform(list_pr.GetChild(1)); - auto result = insert_by_name; - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformInsertByPositionOrderInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto insert_by_position = transformer.Transform(list_pr.GetChild(1)); - auto result = insert_by_position; - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformInsertByNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformInsertByName(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformInsertByPositionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformInsertByPosition(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformInsertTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto base_table_name = transformer.Transform>(list_pr.GetChild(0)); - optional insert_alias {}; - auto &insert_alias_opt = list_pr.GetChild(1).Cast(); - if (insert_alias_opt.HasResult()) { - auto insert_alias_value = transformer.Transform(insert_alias_opt.GetResult()); - insert_alias = insert_alias_value; - } - auto result = TransformInsertTarget(transformer, std::move(base_table_name), insert_alias); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformInsertAliasInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(1).Cast().identifier; - auto result = TransformInsertAlias(transformer, identifier); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformColumnListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector col_id; - auto col_id_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - for (auto &col_id_item : col_id_items) { - auto col_id_value = transformer.Transform(col_id_item.get()); - col_id.push_back(col_id_value); - } - auto result = TransformColumnList(transformer, col_id); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformInsertColumnListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto column_list = transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = TransformInsertColumnList(transformer, column_list); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformInsertValuesInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSelectInsertValuesInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto select_statement_internal = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformSelectInsertValues(transformer, std::move(select_statement_internal)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDefaultValuesInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformDefaultValues(transformer); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOnConflictClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional on_conflict_target {}; - auto &on_conflict_target_opt = list_pr.GetChild(2).Cast(); - if (on_conflict_target_opt.HasResult()) { - auto on_conflict_target_value = - transformer.Transform(on_conflict_target_opt.GetResult()); - on_conflict_target = std::move(on_conflict_target_value); - } - auto on_conflict_action = transformer.Transform>(list_pr.GetChild(3)); - auto result = TransformOnConflictClause(transformer, std::move(on_conflict_target), std::move(on_conflict_action)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOnConflictTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformOnConflictExpressionTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto column_id_list = transformer.Transform>(list_pr.GetChild(0)); - optional> where_clause {}; - auto &where_clause_opt = list_pr.GetChild(1).Cast(); - if (where_clause_opt.HasResult()) { - auto where_clause_value = transformer.Transform>(where_clause_opt.GetResult()); - where_clause = std::move(where_clause_value); - } - auto result = TransformOnConflictExpressionTarget(transformer, column_id_list, std::move(where_clause)); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformOnConflictIndexTargetInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto constraint_name = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformOnConflictIndexTarget(transformer, constraint_name); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOnConflictActionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOnConflictUpdateInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto update_set_clause = transformer.Transform>(list_pr.GetChild(3)); - optional> where_clause {}; - auto &where_clause_opt = list_pr.GetChild(4).Cast(); - if (where_clause_opt.HasResult()) { - auto where_clause_value = transformer.Transform>(where_clause_opt.GetResult()); - where_clause = std::move(where_clause_value); - } - auto result = TransformOnConflictUpdate(transformer, std::move(update_set_clause), std::move(where_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOnConflictNothingInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformOnConflictNothing(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformReturningClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto target_list = transformer.Transform>>(list_pr.GetChild(1)); - auto result = TransformReturningClause(transformer, std::move(target_list)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformLoadStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id_or_string = transformer.Transform(list_pr.GetChild(1)); - optional from_source {}; - auto &from_source_opt = list_pr.GetChild(2).Cast(); - if (from_source_opt.HasResult()) { - auto from_source_value = transformer.Transform(from_source_opt.GetResult()); - from_source = from_source_value; - } - optional extension_alias {}; - auto &extension_alias_opt = list_pr.GetChild(3).Cast(); - if (extension_alias_opt.HasResult()) { - auto extension_alias_value = transformer.Transform(extension_alias_opt.GetResult()); - extension_alias = extension_alias_value; - } - auto result = TransformLoadStatement(transformer, col_id_or_string, from_source, extension_alias); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformExtensionAliasInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(1).Cast().identifier; - auto result = TransformExtensionAlias(transformer, identifier); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformInstallStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(0).Cast(); - has_result = has_result_opt.HasResult(); - optional install_and_load {}; - auto &install_and_load_opt = list_pr.GetChild(2).Cast(); - if (install_and_load_opt.HasResult()) { - auto install_and_load_value = transformer.Transform(install_and_load_opt.GetResult()); - install_and_load = install_and_load_value; - } - auto identifier_or_string_literal = transformer.Transform(list_pr.GetChild(3)); - optional from_source {}; - auto &from_source_opt = list_pr.GetChild(4).Cast(); - if (from_source_opt.HasResult()) { - auto from_source_value = transformer.Transform(from_source_opt.GetResult()); - from_source = from_source_value; - } - optional version_number {}; - auto &version_number_opt = list_pr.GetChild(5).Cast(); - if (version_number_opt.HasResult()) { - auto version_number_value = transformer.Transform(version_number_opt.GetResult()); - version_number = version_number_value; - } - auto result = TransformInstallStatement(transformer, has_result, install_and_load, identifier_or_string_literal, - from_source, version_number); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformInstallAndLoadInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformInstallAndLoad(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformUpdateExtensionsStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> identifier {}; - auto &identifier_opt = list_pr.GetChild(2).Cast(); - if (identifier_opt.HasResult()) { - vector identifier_value; - auto identifier_value_items_1 = - ExtractParseResultsFromList(ExtractResultFromParens(identifier_opt.GetResult())); - for (auto &identifier_value_item_1 : identifier_value_items_1) { - auto identifier_value_value_1 = identifier_value_item_1.get().Cast().identifier; - identifier_value.push_back(identifier_value_value_1); - } - identifier = identifier_value; - } - auto result = TransformUpdateExtensionsStatement(transformer, identifier); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformFromSourceInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformFromSourceIdentifierInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(1).Cast().identifier; - auto result = TransformFromSourceIdentifier(transformer, identifier); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformFromSourceStringInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto string_literal = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformFromSourceString(transformer, string_literal); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformVersionNumberInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier_or_string_literal = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformVersionNumber(transformer, identifier_or_string_literal); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformExtensionRepositoryStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCreateExtensionRepositoryStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional or_replace {}; - auto &or_replace_opt = list_pr.GetChild(1).Cast(); - if (or_replace_opt.HasResult()) { - auto or_replace_value = transformer.Transform(or_replace_opt.GetResult()); - or_replace = or_replace_value; - } - optional if_not_exists {}; - auto &if_not_exists_opt = list_pr.GetChild(4).Cast(); - if (if_not_exists_opt.HasResult()) { - auto if_not_exists_value = transformer.Transform(if_not_exists_opt.GetResult()); - if_not_exists = if_not_exists_value; - } - auto col_id_or_string = transformer.Transform(list_pr.GetChild(5)); - auto repository_prefix = transformer.Transform(list_pr.GetChild(6)); - optional> repository_public_key {}; - auto &repository_public_key_opt = list_pr.GetChild(7).Cast(); - if (repository_public_key_opt.HasResult()) { - auto repository_public_key_value = transformer.Transform>(repository_public_key_opt.GetResult()); - repository_public_key = repository_public_key_value; - } - auto result = TransformCreateExtensionRepositoryStmt(transformer, or_replace, if_not_exists, col_id_or_string, - repository_prefix, repository_public_key); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformRepositoryPrefixInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(0).Cast(); - has_result = has_result_opt.HasResult(); - auto string_literal = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformRepositoryPrefix(transformer, has_result, string_literal); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformRepositoryPublicKeyInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(0).Cast(); - has_result = has_result_opt.HasResult(); - vector string_literal; - auto string_literal_items = ExtractParseResultsFromList(list_pr.GetChild(3)); - for (auto &string_literal_item : string_literal_items) { - auto string_literal_value = transformer.Transform(string_literal_item.get()); - string_literal.push_back(string_literal_value); - } - auto result = TransformRepositoryPublicKey(transformer, has_result, string_literal); - return make_uniq>>(result); -} - -unique_ptr -PEGTransformerFactory::TransformDropExtensionRepositoryStmtInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional if_exists {}; - auto &if_exists_opt = list_pr.GetChild(3).Cast(); - if (if_exists_opt.HasResult()) { - auto if_exists_value = transformer.Transform(if_exists_opt.GetResult()); - if_exists = if_exists_value; - } - auto col_id_or_string = transformer.Transform(list_pr.GetChild(4)); - auto result = TransformDropExtensionRepositoryStmt(transformer, if_exists, col_id_or_string); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformTableMatchRecognizeClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto match_recognize_body = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(1))); - optional table_alias {}; - auto &table_alias_opt = list_pr.GetChild(2).Cast(); - if (table_alias_opt.HasResult()) { - auto table_alias_value = transformer.Transform(table_alias_opt.GetResult()); - table_alias = table_alias_value; - } - auto result = TransformTableMatchRecognizeClause(transformer, std::move(match_recognize_body), table_alias); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMatchRecognizeBodyInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector match_recognize_clause; - auto &match_recognize_clause_repeat = list_pr.GetChild(0).Cast(); - for (auto &match_recognize_clause_item : match_recognize_clause_repeat.GetChildren()) { - auto match_recognize_clause_value = - transformer.Transform(match_recognize_clause_item.get()); - match_recognize_clause.push_back(std::move(match_recognize_clause_value)); - } - auto result = TransformMatchRecognizeBody(transformer, std::move(match_recognize_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformMatchRecognizeClauseInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMRPartitionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto window_partition = transformer.Transform>>(list_pr.GetChild(0)); - auto result = TransformMRPartition(transformer, std::move(window_partition)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMROrderByInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto order_by_clause = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformMROrderBy(transformer, std::move(order_by_clause)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMRMeasuresInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto measures_clause = transformer.Transform>>(list_pr.GetChild(0)); - auto result = TransformMRMeasures(transformer, std::move(measures_clause)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMRRowsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto rows_per_match = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformMRRows(transformer, rows_per_match); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMRSkipInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto after_match_skip = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformMRSkip(transformer, std::move(after_match_skip)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMRPatternInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto pattern_clause = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformMRPattern(transformer, std::move(pattern_clause)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMRSubsetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto subset_clause = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformMRSubset(transformer, std::move(subset_clause)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMRDefineAutoInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformMRDefineAuto(transformer); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMRDefineInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto define_clause = transformer.Transform>>(list_pr.GetChild(0)); - auto result = TransformMRDefine(transformer, std::move(define_clause)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMeasuresClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> measures_element; - auto measures_element_items = ExtractParseResultsFromList(list_pr.GetChild(1)); - for (auto &measures_element_item : measures_element_items) { - auto measures_element_value = transformer.Transform>(measures_element_item.get()); - measures_element.push_back(std::move(measures_element_value)); - } - auto result = TransformMeasuresClause(transformer, std::move(measures_element)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMeasuresElementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - auto col_label_or_string = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformMeasuresElement(transformer, std::move(expression), col_label_or_string); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMeasureSemanticsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformRunningSemanticsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformRunningSemantics(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformFinalSemanticsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformFinalSemantics(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformRowsPerMatchInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformOneRowPerMatchInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformOneRowPerMatch(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformAllRowsPerMatchInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional all_rows_empty_matches {}; - auto &all_rows_empty_matches_opt = list_pr.GetChild(4).Cast(); - if (all_rows_empty_matches_opt.HasResult()) { - auto all_rows_empty_matches_value = - transformer.Transform(all_rows_empty_matches_opt.GetResult()); - all_rows_empty_matches = all_rows_empty_matches_value; - } - auto result = TransformAllRowsPerMatch(transformer, all_rows_empty_matches); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformAllRowsEmptyMatchesInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformShowEmptyMatchesInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformShowEmptyMatches(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformOmitEmptyMatchesInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformOmitEmptyMatches(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformWithUnmatchedRowsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformWithUnmatchedRows(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformAfterMatchSkipInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto after_match_skip_target = transformer.Transform(list_pr.GetChild(3)); - auto result = std::move(after_match_skip_target); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformAfterMatchSkipTargetInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSkipToNextRowInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSkipToNextRow(transformer); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSkipPastLastRowInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSkipPastLastRow(transformer); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSkipToFirstVarInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_label_or_string = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformSkipToFirstVar(transformer, col_label_or_string); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSkipToLastVarInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_label_or_string = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformSkipToLastVar(transformer, col_label_or_string); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSkipToVarInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_label_or_string = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformSkipToVar(transformer, col_label_or_string); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPatternClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto row_pattern = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(1))); - auto result = std::move(row_pattern); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformRowPatternInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto row_pattern_term = transformer.Transform>(list_pr.GetChild(0)); - optional>> row_pattern_alternative {}; - auto &row_pattern_alternative_opt = list_pr.GetChild(1).Cast(); - if (row_pattern_alternative_opt.HasResult()) { - vector> row_pattern_alternative_value; - auto &row_pattern_alternative_value_repeat_1 = - row_pattern_alternative_opt.GetResult().Cast(); - for (auto &row_pattern_alternative_value_item_1 : row_pattern_alternative_value_repeat_1.GetChildren()) { - auto row_pattern_alternative_value_value_1 = - transformer.Transform>(row_pattern_alternative_value_item_1.get()); - row_pattern_alternative_value.push_back(std::move(row_pattern_alternative_value_value_1)); - } - row_pattern_alternative = std::move(row_pattern_alternative_value); - } - auto result = TransformRowPattern(transformer, std::move(row_pattern_term), std::move(row_pattern_alternative)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformRowPatternAlternativeInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto row_pattern_term = transformer.Transform>(list_pr.GetChild(1)); - auto result = std::move(row_pattern_term); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformRowPatternTermInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> row_pattern_factor; - auto &row_pattern_factor_repeat = list_pr.GetChild(0).Cast(); - for (auto &row_pattern_factor_item : row_pattern_factor_repeat.GetChildren()) { - auto row_pattern_factor_value = - transformer.Transform>(row_pattern_factor_item.get()); - row_pattern_factor.push_back(std::move(row_pattern_factor_value)); - } - auto result = TransformRowPatternTerm(transformer, std::move(row_pattern_factor)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformRowPatternFactorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto row_pattern_primary = transformer.Transform>(list_pr.GetChild(0)); - optional row_pattern_quantifier {}; - auto &row_pattern_quantifier_opt = list_pr.GetChild(1).Cast(); - if (row_pattern_quantifier_opt.HasResult()) { - auto row_pattern_quantifier_value = - transformer.Transform(row_pattern_quantifier_opt.GetResult()); - row_pattern_quantifier = row_pattern_quantifier_value; - } - auto result = TransformRowPatternFactor(transformer, std::move(row_pattern_primary), row_pattern_quantifier); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformRowPatternPrimaryInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformRowPatternPermuteInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> row_pattern; - auto row_pattern_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(1))); - for (auto &row_pattern_item : row_pattern_items) { - auto row_pattern_value = transformer.Transform>(row_pattern_item.get()); - row_pattern.push_back(std::move(row_pattern_value)); - } - auto result = TransformRowPatternPermute(transformer, std::move(row_pattern)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformRowPatternAnchorInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPatternStartInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformPatternStart(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPatternEndInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformPatternEnd(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformRowPatternGroupInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto row_pattern = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = std::move(row_pattern); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformRowPatternEmptyInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformRowPatternEmpty(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformRowPatternExclusionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto row_pattern = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformRowPatternExclusion(transformer, std::move(row_pattern)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformRowPatternLabelInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_label_or_string = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformRowPatternLabel(transformer, col_label_or_string); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformRowPatternQuantifierInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto row_pattern_quantifier_kind = transformer.Transform(list_pr.GetChild(0)); - optional quantifier_reluctant {}; - auto &quantifier_reluctant_opt = list_pr.GetChild(1).Cast(); - if (quantifier_reluctant_opt.HasResult()) { - auto quantifier_reluctant_value = transformer.Transform(quantifier_reluctant_opt.GetResult()); - quantifier_reluctant = quantifier_reluctant_value; - } - auto result = TransformRowPatternQuantifier(transformer, row_pattern_quantifier_kind, quantifier_reluctant); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformRowPatternQuantifierKindInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformQuantifierReluctantInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto result = TransformQuantifierReluctant(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformQuantifierStarInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformQuantifierStar(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformQuantifierPlusInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformQuantifierPlus(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformQuantifierOptionalInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformQuantifierOptional(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformQuantifierRangeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto quantifier_bounds = transformer.Transform(list_pr.GetChild(1)); - auto result = quantifier_bounds; - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformQuantifierBoundsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformQuantifierMinMaxInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto number_literal = transformer.Transform>(list_pr.GetChild(0)); - auto number_literal_1 = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformQuantifierMinMax(transformer, std::move(number_literal), std::move(number_literal_1)); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformQuantifierMinInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto number_literal = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformQuantifierMin(transformer, std::move(number_literal)); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformQuantifierMaxInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto number_literal = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformQuantifierMax(transformer, std::move(number_literal)); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformQuantifierExactInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto number_literal = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformQuantifierExact(transformer, std::move(number_literal)); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSubsetClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector subset_element; - auto subset_element_items = ExtractParseResultsFromList(list_pr.GetChild(1)); - for (auto &subset_element_item : subset_element_items) { - auto subset_element_value = transformer.Transform(subset_element_item.get()); - subset_element.push_back(std::move(subset_element_value)); - } - auto result = TransformSubsetClause(transformer, std::move(subset_element)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSubsetElementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_label_or_string = transformer.Transform(list_pr.GetChild(0)); - vector col_label_or_string_1; - auto col_label_or_string_1_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(2))); - for (auto &col_label_or_string_1_item : col_label_or_string_1_items) { - auto col_label_or_string_1_value = transformer.Transform(col_label_or_string_1_item.get()); - col_label_or_string_1.push_back(col_label_or_string_1_value); - } - auto result = TransformSubsetElement(transformer, col_label_or_string, col_label_or_string_1); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDefineClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> define_element; - auto define_element_items = ExtractParseResultsFromList(list_pr.GetChild(1)); - for (auto &define_element_item : define_element_items) { - auto define_element_value = transformer.Transform>(define_element_item.get()); - define_element.push_back(std::move(define_element_value)); - } - auto result = TransformDefineClause(transformer, std::move(define_element)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDefineElementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_label_or_string = transformer.Transform(list_pr.GetChild(0)); - auto expression = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformDefineElement(transformer, col_label_or_string, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMergeIntoStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional with_clause {}; - auto &with_clause_opt = list_pr.GetChild(0).Cast(); - if (with_clause_opt.HasResult()) { - auto with_clause_value = transformer.Transform(with_clause_opt.GetResult()); - with_clause = std::move(with_clause_value); - } - auto target_opt_alias = transformer.Transform>(list_pr.GetChild(3)); - auto merge_into_using_clause = transformer.Transform>(list_pr.GetChild(4)); - auto join_qualifier = transformer.Transform(list_pr.GetChild(5)); - vector>> merge_match; - auto &merge_match_repeat = list_pr.GetChild(6).Cast(); - for (auto &merge_match_item : merge_match_repeat.GetChildren()) { - auto merge_match_value = - transformer.Transform>>(merge_match_item.get()); - merge_match.push_back(std::move(merge_match_value)); - } - optional>> returning_clause {}; - auto &returning_clause_opt = list_pr.GetChild(7).Cast(); - if (returning_clause_opt.HasResult()) { - auto returning_clause_value = - transformer.Transform>>(returning_clause_opt.GetResult()); - returning_clause = std::move(returning_clause_value); - } - auto result = TransformMergeIntoStatement(transformer, std::move(with_clause), std::move(target_opt_alias), - std::move(merge_into_using_clause), std::move(join_qualifier), - std::move(merge_match), std::move(returning_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformMergeIntoUsingClauseInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto table_ref = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformMergeIntoUsingClause(transformer, std::move(table_ref)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMergeMatchInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>>(choice_pr.GetResult()); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMatchedClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> and_expression {}; - auto &and_expression_opt = list_pr.GetChild(2).Cast(); - if (and_expression_opt.HasResult()) { - auto and_expression_value = transformer.Transform>(and_expression_opt.GetResult()); - and_expression = std::move(and_expression_value); - } - auto matched_clause_action = transformer.Transform>(list_pr.GetChild(4)); - auto result = TransformMatchedClause(transformer, std::move(and_expression), std::move(matched_clause_action)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformMatchedClauseActionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformUpdateMatchClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> update_match_info {}; - auto &update_match_info_opt = list_pr.GetChild(1).Cast(); - if (update_match_info_opt.HasResult()) { - auto update_match_info_value = - transformer.Transform>(update_match_info_opt.GetResult()); - update_match_info = std::move(update_match_info_value); - } - auto result = TransformUpdateMatchClause(transformer, std::move(update_match_info)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformUpdateMatchInfoInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformUpdateMatchSetActionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto update_match_set_clause = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformUpdateMatchSetAction(transformer, std::move(update_match_set_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformUpdateByNameOrPositionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto by_name_or_position = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformUpdateByNameOrPosition(transformer, by_name_or_position); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDeleteMatchClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformDeleteMatchClause(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformInsertMatchClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> insert_match_info {}; - auto &insert_match_info_opt = list_pr.GetChild(1).Cast(); - if (insert_match_info_opt.HasResult()) { - auto insert_match_info_value = - transformer.Transform>(insert_match_info_opt.GetResult()); - insert_match_info = std::move(insert_match_info_value); - } - auto result = TransformInsertMatchClause(transformer, std::move(insert_match_info)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformInsertMatchInfoInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformInsertDefaultValuesInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto result = TransformInsertDefaultValues(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformInsertByNameOrPositionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional by_name_or_position {}; - auto &by_name_or_position_opt = list_pr.GetChild(0).Cast(); - if (by_name_or_position_opt.HasResult()) { - auto by_name_or_position_value = transformer.Transform(by_name_or_position_opt.GetResult()); - by_name_or_position = by_name_or_position_value; - } - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto result = TransformInsertByNameOrPosition(transformer, by_name_or_position, has_result); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformInsertValuesListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> insert_column_list {}; - auto &insert_column_list_opt = list_pr.GetChild(0).Cast(); - if (insert_column_list_opt.HasResult()) { - auto insert_column_list_value = transformer.Transform>(insert_column_list_opt.GetResult()); - insert_column_list = insert_column_list_value; - } - vector> expression; - auto expression_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(2))); - for (auto &expression_item : expression_items) { - auto expression_value = transformer.Transform>(expression_item.get()); - expression.push_back(std::move(expression_value)); - } - auto result = TransformInsertValuesList(transformer, insert_column_list, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformDoNothingMatchClauseInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto result = TransformDoNothingMatchClause(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformErrorMatchClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> expression {}; - auto &expression_opt = list_pr.GetChild(1).Cast(); - if (expression_opt.HasResult()) { - auto expression_value = transformer.Transform>(expression_opt.GetResult()); - expression = std::move(expression_value); - } - auto result = TransformErrorMatchClause(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformUpdateMatchSetClauseInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto update_match_set_info = transformer.Transform>(list_pr.GetChild(1)); - auto result = std::move(update_match_set_info); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformUpdateMatchSetInfoInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto &choice_result = choice_pr.GetResult(); - unique_ptr result {}; - if (!(choice_result.name == "StarSymbol")) { - result = transformer.Transform>(choice_result); - } - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAndExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformAndExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNotMatchedClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional by_source_or_target {}; - auto &by_source_or_target_opt = list_pr.GetChild(3).Cast(); - if (by_source_or_target_opt.HasResult()) { - auto by_source_or_target_value = - transformer.Transform(by_source_or_target_opt.GetResult()); - by_source_or_target = by_source_or_target_value; - } - optional> and_expression {}; - auto &and_expression_opt = list_pr.GetChild(4).Cast(); - if (and_expression_opt.HasResult()) { - auto and_expression_value = transformer.Transform>(and_expression_opt.GetResult()); - and_expression = std::move(and_expression_value); - } - auto matched_clause_action = transformer.Transform>(list_pr.GetChild(6)); - auto result = TransformNotMatchedClause(transformer, by_source_or_target, std::move(and_expression), - std::move(matched_clause_action)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformBySourceOrTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformBySourceInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformBySource(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformByTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformByTarget(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformPivotOnInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto pivot_column_list = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformPivotOn(transformer, std::move(pivot_column_list)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPivotUsingInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto target_list = transformer.Transform>>(list_pr.GetChild(1)); - auto result = TransformPivotUsing(transformer, std::move(target_list)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPivotColumnListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector pivot_column_entry; - auto pivot_column_entry_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - for (auto &pivot_column_entry_item : pivot_column_entry_items) { - auto pivot_column_entry_value = transformer.Transform(pivot_column_entry_item.get()); - pivot_column_entry.push_back(std::move(pivot_column_entry_value)); - } - auto result = TransformPivotColumnList(transformer, std::move(pivot_column_entry)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPivotColumnEntryInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformPivotColumnExpressionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformPivotColumnExpression(transformer, std::move(expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformPivotColumnSubqueryInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto base_expression = transformer.Transform>(list_pr.GetChild(0)); - auto select_statement_internal = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(2))); - auto result = - TransformPivotColumnSubquery(transformer, std::move(base_expression), std::move(select_statement_internal)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIntoNameValuesInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id_or_string = transformer.Transform(list_pr.GetChild(2)); - auto optional_parens_name_list = transformer.Transform>(list_pr.GetChild(4)); - auto result = TransformIntoNameValues(transformer, col_id_or_string, optional_parens_name_list); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformOptionalParensNameListInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(result); -} - -unique_ptr -PEGTransformerFactory::TransformParenthesizedNameListInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector col_id_or_string; - auto col_id_or_string_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &col_id_or_string_item : col_id_or_string_items) { - auto col_id_or_string_value = transformer.Transform(col_id_or_string_item.get()); - col_id_or_string.push_back(col_id_or_string_value); - } - auto result = TransformParenthesizedNameList(transformer, col_id_or_string); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformBareNameListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector col_id_or_string; - auto col_id_or_string_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - for (auto &col_id_or_string_item : col_id_or_string_items) { - auto col_id_or_string_value = transformer.Transform(col_id_or_string_item.get()); - col_id_or_string.push_back(col_id_or_string_value); - } - auto result = TransformBareNameList(transformer, col_id_or_string); - return make_uniq>>(result); -} - -unique_ptr -PEGTransformerFactory::TransformIncludeOrExcludeNullsInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformIncludeNullsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformIncludeNulls(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformExcludeNullsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformExcludeNulls(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformUnpivotHeaderInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(result); -} - -unique_ptr -PEGTransformerFactory::TransformUnpivotHeaderSingleInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id_or_string = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformUnpivotHeaderSingle(transformer, col_id_or_string); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformUnpivotHeaderListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector col_id_or_string; - auto col_id_or_string_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &col_id_or_string_item : col_id_or_string_items) { - auto col_id_or_string_value = transformer.Transform(col_id_or_string_item.get()); - col_id_or_string.push_back(col_id_or_string_value); - } - auto result = TransformUnpivotHeaderList(transformer, col_id_or_string); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformPragmaStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto pragma_assign_or_function = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformPragmaStatement(transformer, std::move(pragma_assign_or_function)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformPragmaAssignOrFunctionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPragmaAssignInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto setting_name = list_pr.GetChild(0).Cast().identifier; - auto variable_list = transformer.Transform>>(list_pr.GetChild(2)); - auto result = TransformPragmaAssign(transformer, setting_name, std::move(variable_list)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPragmaFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto pragma_name = list_pr.GetChild(0).Cast().identifier; - optional>> pragma_parameters {}; - auto &pragma_parameters_opt = list_pr.GetChild(1).Cast(); - if (pragma_parameters_opt.HasResult()) { - auto pragma_parameters_value = - transformer.Transform>>(pragma_parameters_opt.GetResult()); - pragma_parameters = std::move(pragma_parameters_value); - } - auto result = TransformPragmaFunction(transformer, pragma_name, std::move(pragma_parameters)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPragmaParametersInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> expression; - auto expression_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &expression_item : expression_items) { - auto expression_value = transformer.Transform>(expression_item.get()); - expression.push_back(std::move(expression_value)); - } - auto result = TransformPragmaParameters(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPrepareStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(1).Cast().identifier; - optional> type_list {}; - auto &type_list_opt = list_pr.GetChild(2).Cast(); - if (type_list_opt.HasResult()) { - auto type_list_value = transformer.Transform>(type_list_opt.GetResult()); - type_list = type_list_value; - } - auto statement = transformer.Transform>(list_pr.GetChild(4)); - auto result = TransformPrepareStatement(transformer, identifier, type_list, std::move(statement)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTypeListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector type; - auto type_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &type_item : type_items) { - auto type_value = transformer.Transform(type_item.get()); - type.push_back(type_value); - } - auto result = TransformTypeList(transformer, type); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformSelectStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto select_statement_internal = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformSelectStatement(transformer, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSelectSetOpChainInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto intersect_chain = transformer.Transform>(list_pr.GetChild(0)); - optional, unique_ptr>>> select_set_op_chain_tail {}; - auto &select_set_op_chain_tail_opt = list_pr.GetChild(1).Cast(); - if (select_set_op_chain_tail_opt.HasResult()) { - vector, unique_ptr>> select_set_op_chain_tail_value; - auto &select_set_op_chain_tail_value_repeat_1 = - select_set_op_chain_tail_opt.GetResult().Cast(); - for (auto &select_set_op_chain_tail_value_item_1 : select_set_op_chain_tail_value_repeat_1.GetChildren()) { - auto select_set_op_chain_tail_value_value_1 = - transformer.Transform, unique_ptr>>( - select_set_op_chain_tail_value_item_1.get()); - select_set_op_chain_tail_value.push_back(std::move(select_set_op_chain_tail_value_value_1)); - } - select_set_op_chain_tail = std::move(select_set_op_chain_tail_value); - } - auto result = - TransformSelectSetOpChain(transformer, std::move(intersect_chain), std::move(select_set_op_chain_tail)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformSelectSetOpChainTailInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto setop_clause = transformer.Transform>(list_pr.GetChild(0)); - auto intersect_chain = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformSelectSetOpChainTail(transformer, std::move(setop_clause), std::move(intersect_chain)); - return make_uniq, unique_ptr>>>( - std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIntersectChainInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto select_atom = transformer.Transform>(list_pr.GetChild(0)); - optional, unique_ptr>>> intersect_chain_tail {}; - auto &intersect_chain_tail_opt = list_pr.GetChild(1).Cast(); - if (intersect_chain_tail_opt.HasResult()) { - vector, unique_ptr>> intersect_chain_tail_value; - auto &intersect_chain_tail_value_repeat_1 = intersect_chain_tail_opt.GetResult().Cast(); - for (auto &intersect_chain_tail_value_item_1 : intersect_chain_tail_value_repeat_1.GetChildren()) { - auto intersect_chain_tail_value_value_1 = - transformer.Transform, unique_ptr>>( - intersect_chain_tail_value_item_1.get()); - intersect_chain_tail_value.push_back(std::move(intersect_chain_tail_value_value_1)); - } - intersect_chain_tail = std::move(intersect_chain_tail_value); - } - auto result = TransformIntersectChain(transformer, std::move(select_atom), std::move(intersect_chain_tail)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformIntersectChainTailInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto set_intersect_clause = transformer.Transform>(list_pr.GetChild(0)); - auto select_atom = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformIntersectChainTail(transformer, std::move(set_intersect_clause), std::move(select_atom)); - return make_uniq, unique_ptr>>>( - std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSetIntersectClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional distinct_or_all {}; - auto &distinct_or_all_opt = list_pr.GetChild(1).Cast(); - if (distinct_or_all_opt.HasResult()) { - auto distinct_or_all_value = transformer.Transform(distinct_or_all_opt.GetResult()); - distinct_or_all = distinct_or_all_value; - } - auto result = TransformSetIntersectClause(transformer, distinct_or_all); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSelectAtomInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSelectParensInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto select_statement_internal = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = TransformSelectParens(transformer, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSetopClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto setop_type = transformer.Transform(list_pr.GetChild(0)); - optional distinct_or_all {}; - auto &distinct_or_all_opt = list_pr.GetChild(1).Cast(); - if (distinct_or_all_opt.HasResult()) { - auto distinct_or_all_value = transformer.Transform(distinct_or_all_opt.GetResult()); - distinct_or_all = distinct_or_all_value; - } - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(2).Cast(); - has_result = has_result_opt.HasResult(); - auto result = TransformSetopClause(transformer, setop_type, distinct_or_all, has_result); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSetopTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSetopUnionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSetopUnion(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSetopExceptInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSetopExcept(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformSelectStatementTypeInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformResultModifiersInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> order_by_clause {}; - auto &order_by_clause_opt = list_pr.GetChild(0).Cast(); - if (order_by_clause_opt.HasResult()) { - auto order_by_clause_value = transformer.Transform>(order_by_clause_opt.GetResult()); - order_by_clause = std::move(order_by_clause_value); - } - optional> limit_offset {}; - auto &limit_offset_opt = list_pr.GetChild(1).Cast(); - if (limit_offset_opt.HasResult()) { - auto limit_offset_value = transformer.Transform>(limit_offset_opt.GetResult()); - limit_offset = std::move(limit_offset_value); - } - auto result = TransformResultModifiers(transformer, std::move(order_by_clause), std::move(limit_offset)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformLimitOffsetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformLimitOffsetClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto limit_clause = transformer.Transform(list_pr.GetChild(0)); - optional offset_clause {}; - auto &offset_clause_opt = list_pr.GetChild(1).Cast(); - if (offset_clause_opt.HasResult()) { - auto offset_clause_value = transformer.Transform(offset_clause_opt.GetResult()); - offset_clause = std::move(offset_clause_value); - } - auto result = TransformLimitOffsetClause(transformer, std::move(limit_clause), std::move(offset_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOffsetLimitClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto offset_clause = transformer.Transform(list_pr.GetChild(0)); - optional limit_clause {}; - auto &limit_clause_opt = list_pr.GetChild(1).Cast(); - if (limit_clause_opt.HasResult()) { - auto limit_clause_value = transformer.Transform(limit_clause_opt.GetResult()); - limit_clause = std::move(limit_clause_value); - } - auto result = TransformOffsetLimitClause(transformer, std::move(offset_clause), std::move(limit_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOffsetFetchClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto offset_clause = transformer.Transform(list_pr.GetChild(0)); - auto fetch_clause = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformOffsetFetchClause(transformer, std::move(offset_clause), std::move(fetch_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformFetchOnlyClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto fetch_clause = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformFetchOnlyClause(transformer, std::move(fetch_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTableStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto base_table_name = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformTableStatement(transformer, std::move(base_table_name)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformOptionalParensSimpleSelectInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSimpleSelectParensInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto simple_select = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = TransformSimpleSelectParens(transformer, std::move(simple_select)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSelectFromInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSelectFromClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto select_clause = transformer.Transform>(list_pr.GetChild(0)); - optional> from_clause {}; - auto &from_clause_opt = list_pr.GetChild(1).Cast(); - if (from_clause_opt.HasResult()) { - auto from_clause_value = transformer.Transform>(from_clause_opt.GetResult()); - from_clause = std::move(from_clause_value); - } - auto result = TransformSelectFromClause(transformer, std::move(select_clause), std::move(from_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformFromSelectClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto from_clause = transformer.Transform>(list_pr.GetChild(0)); - optional> select_clause {}; - auto &select_clause_opt = list_pr.GetChild(1).Cast(); - if (select_clause_opt.HasResult()) { - auto select_clause_value = transformer.Transform>(select_clause_opt.GetResult()); - select_clause = std::move(select_clause_value); - } - auto result = TransformFromSelectClause(transformer, std::move(from_clause), std::move(select_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformWithStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id_or_string = transformer.Transform(list_pr.GetChild(0)); - optional> insert_column_list {}; - auto &insert_column_list_opt = list_pr.GetChild(1).Cast(); - if (insert_column_list_opt.HasResult()) { - auto insert_column_list_value = transformer.Transform>(insert_column_list_opt.GetResult()); - insert_column_list = insert_column_list_value; - } - optional>> using_key {}; - auto &using_key_opt = list_pr.GetChild(2).Cast(); - if (using_key_opt.HasResult()) { - auto using_key_value = transformer.Transform>>(using_key_opt.GetResult()); - using_key = std::move(using_key_value); - } - optional materialized {}; - auto &materialized_opt = list_pr.GetChild(4).Cast(); - if (materialized_opt.HasResult()) { - auto materialized_value = transformer.Transform(materialized_opt.GetResult()); - materialized = materialized_value; - } - auto cte_body = transformer.Transform>(list_pr.GetChild(5)); - auto result = TransformWithStatement(transformer, col_id_or_string, insert_column_list, std::move(using_key), - materialized, std::move(cte_body)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCTEBodyInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCTESelectBodyInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto select_statement_internal = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = TransformCTESelectBody(transformer, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCTEDMLBodyInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto statement = transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = TransformCTEDMLBody(transformer, std::move(statement)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformUsingKeyInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto target_list = - transformer.Transform>>(ExtractResultFromParens(list_pr.GetChild(2))); - auto result = TransformUsingKey(transformer, std::move(target_list)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformMaterializedInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(0).Cast(); - has_result = has_result_opt.HasResult(); - auto result = TransformMaterialized(transformer, has_result); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSelectClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional distinct_clause {}; - auto &distinct_clause_opt = list_pr.GetChild(1).Cast(); - if (distinct_clause_opt.HasResult()) { - auto distinct_clause_value = transformer.Transform(distinct_clause_opt.GetResult()); - distinct_clause = std::move(distinct_clause_value); - } - optional>> target_list {}; - auto &target_list_opt = list_pr.GetChild(2).Cast(); - if (target_list_opt.HasResult()) { - auto target_list_value = - transformer.Transform>>(target_list_opt.GetResult()); - target_list = std::move(target_list_value); - } - auto result = TransformSelectClause(transformer, std::move(distinct_clause), std::move(target_list)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTargetListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> aliased_expression; - auto aliased_expression_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - for (auto &aliased_expression_item : aliased_expression_items) { - auto aliased_expression_value = - transformer.Transform>(aliased_expression_item.get()); - aliased_expression.push_back(std::move(aliased_expression_value)); - } - auto result = TransformTargetList(transformer, std::move(aliased_expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformColumnAliasesInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector col_id_or_string; - auto col_id_or_string_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &col_id_or_string_item : col_id_or_string_items) { - auto col_id_or_string_value = transformer.Transform(col_id_or_string_item.get()); - col_id_or_string.push_back(col_id_or_string_value); - } - auto result = TransformColumnAliases(transformer, col_id_or_string); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformDistinctClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDistinctAllInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformDistinctAll(transformer); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDistinctOnInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional>> distinct_on_targets {}; - auto &distinct_on_targets_opt = list_pr.GetChild(1).Cast(); - if (distinct_on_targets_opt.HasResult()) { - auto distinct_on_targets_value = - transformer.Transform>>(distinct_on_targets_opt.GetResult()); - distinct_on_targets = std::move(distinct_on_targets_value); - } - auto result = TransformDistinctOn(transformer, std::move(distinct_on_targets)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDistinctOnTargetsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> expression; - auto expression_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(1))); - for (auto &expression_item : expression_items) { - auto expression_value = transformer.Transform>(expression_item.get()); - expression.push_back(std::move(expression_value)); - } - auto result = TransformDistinctOnTargets(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformInnerTableRefInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTableSubqueryInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional table_alias_colon {}; - auto &table_alias_colon_opt = list_pr.GetChild(0).Cast(); - if (table_alias_colon_opt.HasResult()) { - auto table_alias_colon_value = transformer.Transform(table_alias_colon_opt.GetResult()); - table_alias_colon = table_alias_colon_value; - } - optional lateral {}; - auto &lateral_opt = list_pr.GetChild(1).Cast(); - if (lateral_opt.HasResult()) { - auto lateral_value = transformer.Transform(lateral_opt.GetResult()); - lateral = lateral_value; - } - auto subquery_reference = transformer.Transform>(list_pr.GetChild(2)); - optional table_alias {}; - auto &table_alias_opt = list_pr.GetChild(3).Cast(); - if (table_alias_opt.HasResult()) { - auto table_alias_value = transformer.Transform(table_alias_opt.GetResult()); - table_alias = table_alias_value; - } - auto result = - TransformTableSubquery(transformer, table_alias_colon, lateral, std::move(subquery_reference), table_alias); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformBaseTableRefInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional table_alias_colon {}; - auto &table_alias_colon_opt = list_pr.GetChild(0).Cast(); - if (table_alias_colon_opt.HasResult()) { - auto table_alias_colon_value = transformer.Transform(table_alias_colon_opt.GetResult()); - table_alias_colon = table_alias_colon_value; - } - auto base_table_name = transformer.Transform>(list_pr.GetChild(1)); - optional table_alias {}; - auto &table_alias_opt = list_pr.GetChild(2).Cast(); - if (table_alias_opt.HasResult()) { - auto table_alias_value = transformer.Transform(table_alias_opt.GetResult()); - table_alias = table_alias_value; - } - optional> at_clause {}; - auto &at_clause_opt = list_pr.GetChild(3).Cast(); - if (at_clause_opt.HasResult()) { - auto at_clause_value = transformer.Transform>(at_clause_opt.GetResult()); - at_clause = std::move(at_clause_value); - } - optional> sample_clause {}; - auto &sample_clause_opt = list_pr.GetChild(4).Cast(); - if (sample_clause_opt.HasResult()) { - auto sample_clause_value = transformer.Transform>(sample_clause_opt.GetResult()); - sample_clause = std::move(sample_clause_value); - } - auto result = TransformBaseTableRef(transformer, table_alias_colon, std::move(base_table_name), table_alias, - std::move(at_clause), std::move(sample_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTableAliasColonInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id_or_string = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformTableAliasColon(transformer, col_id_or_string); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformValuesRefInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional table_alias_colon {}; - auto &table_alias_colon_opt = list_pr.GetChild(0).Cast(); - if (table_alias_colon_opt.HasResult()) { - auto table_alias_colon_value = transformer.Transform(table_alias_colon_opt.GetResult()); - table_alias_colon = table_alias_colon_value; - } - auto values_clause = transformer.Transform>(list_pr.GetChild(1)); - optional table_alias {}; - auto &table_alias_opt = list_pr.GetChild(2).Cast(); - if (table_alias_opt.HasResult()) { - auto table_alias_value = transformer.Transform(table_alias_opt.GetResult()); - table_alias = table_alias_value; - } - auto result = TransformValuesRef(transformer, table_alias_colon, std::move(values_clause), table_alias); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformParensTableRefInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional table_alias_colon {}; - auto &table_alias_colon_opt = list_pr.GetChild(0).Cast(); - if (table_alias_colon_opt.HasResult()) { - auto table_alias_colon_value = transformer.Transform(table_alias_colon_opt.GetResult()); - table_alias_colon = table_alias_colon_value; - } - auto table_ref = transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(1))); - optional table_alias {}; - auto &table_alias_opt = list_pr.GetChild(2).Cast(); - if (table_alias_opt.HasResult()) { - auto table_alias_value = transformer.Transform(table_alias_opt.GetResult()); - table_alias = table_alias_value; - } - optional> sample_clause {}; - auto &sample_clause_opt = list_pr.GetChild(3).Cast(); - if (sample_clause_opt.HasResult()) { - auto sample_clause_value = transformer.Transform>(sample_clause_opt.GetResult()); - sample_clause = std::move(sample_clause_value); - } - auto result = TransformParensTableRef(transformer, table_alias_colon, std::move(table_ref), table_alias, - std::move(sample_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformJoinOrPivotInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTablePivotClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto table_pivot_clause_body = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(1))); - optional table_alias {}; - auto &table_alias_opt = list_pr.GetChild(2).Cast(); - if (table_alias_opt.HasResult()) { - auto table_alias_value = transformer.Transform(table_alias_opt.GetResult()); - table_alias = table_alias_value; - } - auto result = TransformTablePivotClause(transformer, std::move(table_pivot_clause_body), table_alias); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformTablePivotClauseBodyInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto target_list = transformer.Transform>>(list_pr.GetChild(0)); - vector pivot_value_list; - auto &pivot_value_list_repeat = list_pr.GetChild(2).Cast(); - for (auto &pivot_value_list_item : pivot_value_list_repeat.GetChildren()) { - auto pivot_value_list_value = transformer.Transform(pivot_value_list_item.get()); - pivot_value_list.push_back(std::move(pivot_value_list_value)); - } - optional> pivot_group_by_list {}; - auto &pivot_group_by_list_opt = list_pr.GetChild(3).Cast(); - if (pivot_group_by_list_opt.HasResult()) { - auto pivot_group_by_list_value = transformer.Transform>(pivot_group_by_list_opt.GetResult()); - pivot_group_by_list = pivot_group_by_list_value; - } - auto result = TransformTablePivotClauseBody(transformer, std::move(target_list), std::move(pivot_value_list), - pivot_group_by_list); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPivotGroupByListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto optional_parens_name_list = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformPivotGroupByList(transformer, optional_parens_name_list); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformTableUnpivotClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional include_or_exclude_nulls {}; - auto &include_or_exclude_nulls_opt = list_pr.GetChild(1).Cast(); - if (include_or_exclude_nulls_opt.HasResult()) { - auto include_or_exclude_nulls_value = transformer.Transform(include_or_exclude_nulls_opt.GetResult()); - include_or_exclude_nulls = include_or_exclude_nulls_value; - } - auto table_unpivot_clause_body = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(2))); - optional table_alias {}; - auto &table_alias_opt = list_pr.GetChild(3).Cast(); - if (table_alias_opt.HasResult()) { - auto table_alias_value = transformer.Transform(table_alias_opt.GetResult()); - table_alias = table_alias_value; - } - auto result = TransformTableUnpivotClause(transformer, include_or_exclude_nulls, - std::move(table_unpivot_clause_body), table_alias); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformTableUnpivotClauseBodyInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto unpivot_header = transformer.Transform>(list_pr.GetChild(0)); - vector unpivot_value_list; - auto &unpivot_value_list_repeat = list_pr.GetChild(2).Cast(); - for (auto &unpivot_value_list_item : unpivot_value_list_repeat.GetChildren()) { - auto unpivot_value_list_value = transformer.Transform(unpivot_value_list_item.get()); - unpivot_value_list.push_back(std::move(unpivot_value_list_value)); - } - auto result = TransformTableUnpivotClauseBody(transformer, unpivot_header, std::move(unpivot_value_list)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPivotHeaderInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto base_expression = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformPivotHeader(transformer, std::move(base_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPivotValueListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto pivot_header = transformer.Transform>(list_pr.GetChild(0)); - auto pivot_value_target = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformPivotValueList(transformer, std::move(pivot_header), std::move(pivot_value_target)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPivotValueTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPivotEnumTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(0).Cast().identifier; - auto result = TransformPivotEnumTarget(transformer, identifier); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPivotListTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto pivot_target_list = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformPivotListTarget(transformer, std::move(pivot_target_list)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformUnpivotValueListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto unpivot_header = transformer.Transform>(list_pr.GetChild(0)); - auto unpivot_target_list = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformUnpivotValueList(transformer, unpivot_header, std::move(unpivot_target_list)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformPivotTargetListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto target_list = - transformer.Transform>>(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = TransformPivotTargetList(transformer, std::move(target_list)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformUnpivotTargetListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto target_list = - transformer.Transform>>(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = TransformUnpivotTargetList(transformer, std::move(target_list)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformLateralInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformLateral(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformBaseTableNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformUnqualifiedBaseTableNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto table_name = list_pr.GetChild(0).Cast().identifier; - auto result = TransformUnqualifiedBaseTableName(transformer, table_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformQualifiedTableNameInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformSchemaReservedTableInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto schema_qualification = transformer.Transform(list_pr.GetChild(0)); - auto reserved_table_name = list_pr.GetChild(1).Cast().identifier; - auto result = TransformSchemaReservedTable(transformer, schema_qualification, reserved_table_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformCatalogReservedSchemaTableInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto catalog_qualification = transformer.Transform(list_pr.GetChild(0)); - vector reserved_schema_qualification; - auto &reserved_schema_qualification_repeat = list_pr.GetChild(1).Cast(); - for (auto &reserved_schema_qualification_item : reserved_schema_qualification_repeat.GetChildren()) { - auto reserved_schema_qualification_value = - transformer.Transform(reserved_schema_qualification_item.get()); - reserved_schema_qualification.push_back(reserved_schema_qualification_value); - } - auto reserved_table_name = list_pr.GetChild(2).Cast().identifier; - auto result = TransformCatalogReservedSchemaTable(transformer, catalog_qualification, reserved_schema_qualification, - reserved_table_name); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTableFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformTableFunctionLateralOptInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional lateral {}; - auto &lateral_opt = list_pr.GetChild(0).Cast(); - if (lateral_opt.HasResult()) { - auto lateral_value = transformer.Transform(lateral_opt.GetResult()); - lateral = lateral_value; - } - auto qualified_table_function = transformer.Transform(list_pr.GetChild(1)); - auto table_function_arguments = transformer.Transform>(list_pr.GetChild(2)); - optional with_ordinality {}; - auto &with_ordinality_opt = list_pr.GetChild(3).Cast(); - if (with_ordinality_opt.HasResult()) { - auto with_ordinality_value = transformer.Transform(with_ordinality_opt.GetResult()); - with_ordinality = with_ordinality_value; - } - optional table_alias {}; - auto &table_alias_opt = list_pr.GetChild(4).Cast(); - if (table_alias_opt.HasResult()) { - auto table_alias_value = transformer.Transform(table_alias_opt.GetResult()); - table_alias = table_alias_value; - } - auto result = TransformTableFunctionLateralOpt(transformer, lateral, qualified_table_function, - std::move(table_function_arguments), with_ordinality, table_alias); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformTableFunctionAliasColonInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto table_alias_colon = transformer.Transform(list_pr.GetChild(0)); - auto qualified_table_function = transformer.Transform(list_pr.GetChild(1)); - auto table_function_arguments = transformer.Transform>(list_pr.GetChild(2)); - optional with_ordinality {}; - auto &with_ordinality_opt = list_pr.GetChild(3).Cast(); - if (with_ordinality_opt.HasResult()) { - auto with_ordinality_value = transformer.Transform(with_ordinality_opt.GetResult()); - with_ordinality = with_ordinality_value; - } - optional> sample_clause {}; - auto &sample_clause_opt = list_pr.GetChild(4).Cast(); - if (sample_clause_opt.HasResult()) { - auto sample_clause_value = transformer.Transform>(sample_clause_opt.GetResult()); - sample_clause = std::move(sample_clause_value); - } - auto result = TransformTableFunctionAliasColon(transformer, table_alias_colon, qualified_table_function, - std::move(table_function_arguments), with_ordinality, - std::move(sample_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformWithOrdinalityInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformWithOrdinality(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformQualifiedTableFunctionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional catalog_qualification {}; - auto &catalog_qualification_opt = list_pr.GetChild(0).Cast(); - if (catalog_qualification_opt.HasResult()) { - auto catalog_qualification_value = transformer.Transform(catalog_qualification_opt.GetResult()); - catalog_qualification = catalog_qualification_value; - } - optional> schema_qualification {}; - auto &schema_qualification_opt = list_pr.GetChild(1).Cast(); - if (schema_qualification_opt.HasResult()) { - vector schema_qualification_value; - auto &schema_qualification_value_repeat_1 = schema_qualification_opt.GetResult().Cast(); - for (auto &schema_qualification_value_item_1 : schema_qualification_value_repeat_1.GetChildren()) { - auto schema_qualification_value_value_1 = - transformer.Transform(schema_qualification_value_item_1.get()); - schema_qualification_value.push_back(schema_qualification_value_value_1); - } - schema_qualification = schema_qualification_value; - } - auto table_function_name = list_pr.GetChild(2).Cast().identifier; - auto result = - TransformQualifiedTableFunction(transformer, catalog_qualification, schema_qualification, table_function_name); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformTableFunctionArgumentsInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional> function_argument {}; - auto &function_argument_opt = ExtractResultFromParens(list_pr.GetChild(0)).Cast(); - if (function_argument_opt.HasResult()) { - vector function_argument_value; - auto function_argument_value_items_1 = ExtractParseResultsFromList(function_argument_opt.GetResult()); - for (auto &function_argument_value_item_1 : function_argument_value_items_1) { - auto function_argument_value_value_1 = - transformer.Transform(function_argument_value_item_1.get()); - function_argument_value.push_back(std::move(function_argument_value_value_1)); - } - function_argument = std::move(function_argument_value); - } - auto result = TransformTableFunctionArguments(transformer, std::move(function_argument)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformFunctionArgumentInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformNamedFunctionArgumentInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto named_parameter = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformNamedFunctionArgument(transformer, std::move(named_parameter)); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformPositionalFunctionArgumentInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformPositionalFunctionArgument(transformer, std::move(expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNamedParameterInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto type_func_name = transformer.Transform(list_pr.GetChild(0)); - optional type {}; - auto &type_opt = list_pr.GetChild(1).Cast(); - if (type_opt.HasResult()) { - auto type_value = transformer.Transform(type_opt.GetResult()); - type = type_value; - } - auto expression = transformer.Transform>(list_pr.GetChild(3)); - auto result = TransformNamedParameter(transformer, type_func_name, type, std::move(expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformTableAliasInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTableAliasAsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier_or_string_literal = transformer.Transform(list_pr.GetChild(1)); - optional> column_aliases {}; - auto &column_aliases_opt = list_pr.GetChild(2).Cast(); - if (column_aliases_opt.HasResult()) { - auto column_aliases_value = transformer.Transform>(column_aliases_opt.GetResult()); - column_aliases = column_aliases_value; - } - auto result = TransformTableAliasAs(transformer, identifier_or_string_literal, column_aliases); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformTableAliasWithoutAsInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(0).Cast().identifier; - optional> column_aliases {}; - auto &column_aliases_opt = list_pr.GetChild(1).Cast(); - if (column_aliases_opt.HasResult()) { - auto column_aliases_value = transformer.Transform>(column_aliases_opt.GetResult()); - column_aliases = column_aliases_value; - } - auto result = TransformTableAliasWithoutAs(transformer, identifier, column_aliases); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformAtClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto at_specifier = transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(1))); - auto result = TransformAtClause(transformer, std::move(at_specifier)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAtSpecifierInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto at_unit = transformer.Transform(list_pr.GetChild(0)); - auto expression = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformAtSpecifier(transformer, at_unit, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAtUnitInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - string result; - if (choice_pr.GetResult().type == ParseResultType::IDENTIFIER) { - result = choice_pr.GetResult().Cast().identifier.GetIdentifierName(); - } else if (choice_pr.GetResult().type == ParseResultType::KEYWORD) { - result = choice_pr.GetResult().Cast().keyword; - } else if (choice_pr.GetResult().type == ParseResultType::STRING) { - result = choice_pr.GetResult().Cast().result; - } else if (choice_pr.GetResult().type == ParseResultType::OPERATOR) { - result = choice_pr.GetResult().Cast().operator_token; - } else { - result = transformer.Transform(choice_pr.GetResult()); - } - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformVersionAtUnitInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformVersionAtUnit(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformTimestampAtUnitInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformTimestampAtUnit(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformJoinClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNearestJoinClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNearestJoinAliasedInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional join_type {}; - auto &join_type_opt = list_pr.GetChild(0).Cast(); - if (join_type_opt.HasResult()) { - auto join_type_value = transformer.Transform(join_type_opt.GetResult()); - join_type = join_type_value; - } - auto table_ref = transformer.Transform>(list_pr.GetChild(2)); - optional approx_or_exact {}; - auto &approx_or_exact_opt = list_pr.GetChild(3).Cast(); - if (approx_or_exact_opt.HasResult()) { - auto approx_or_exact_value = transformer.Transform(approx_or_exact_opt.GetResult()); - approx_or_exact = approx_or_exact_value; - } - optional> number_literal {}; - auto &number_literal_opt = list_pr.GetChild(5).Cast(); - if (number_literal_opt.HasResult()) { - auto number_literal_value = transformer.Transform>(number_literal_opt.GetResult()); - number_literal = std::move(number_literal_value); - } - auto distance_or_similarity = transformer.Transform(list_pr.GetChild(7)); - auto expression = transformer.Transform>(list_pr.GetChild(8)); - auto result = TransformNearestJoinAliased(transformer, join_type, std::move(table_ref), approx_or_exact, - std::move(number_literal), distance_or_similarity, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNearestJoinBareInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional join_type {}; - auto &join_type_opt = list_pr.GetChild(0).Cast(); - if (join_type_opt.HasResult()) { - auto join_type_value = transformer.Transform(join_type_opt.GetResult()); - join_type = join_type_value; - } - auto nearest_bare_table_ref = transformer.Transform>(list_pr.GetChild(2)); - optional approx_or_exact {}; - auto &approx_or_exact_opt = list_pr.GetChild(3).Cast(); - if (approx_or_exact_opt.HasResult()) { - auto approx_or_exact_value = transformer.Transform(approx_or_exact_opt.GetResult()); - approx_or_exact = approx_or_exact_value; - } - optional> number_literal {}; - auto &number_literal_opt = list_pr.GetChild(5).Cast(); - if (number_literal_opt.HasResult()) { - auto number_literal_value = transformer.Transform>(number_literal_opt.GetResult()); - number_literal = std::move(number_literal_value); - } - auto distance_or_similarity = transformer.Transform(list_pr.GetChild(7)); - auto expression = transformer.Transform>(list_pr.GetChild(8)); - auto result = TransformNearestJoinBare(transformer, join_type, std::move(nearest_bare_table_ref), approx_or_exact, - std::move(number_literal), distance_or_similarity, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformNearestBareTableRefInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformNearestValuesRefInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto values_clause = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformNearestValuesRef(transformer, std::move(values_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformNearestTableFunctionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional lateral {}; - auto &lateral_opt = list_pr.GetChild(0).Cast(); - if (lateral_opt.HasResult()) { - auto lateral_value = transformer.Transform(lateral_opt.GetResult()); - lateral = lateral_value; - } - auto qualified_table_function = transformer.Transform(list_pr.GetChild(1)); - auto table_function_arguments = transformer.Transform>(list_pr.GetChild(2)); - optional with_ordinality {}; - auto &with_ordinality_opt = list_pr.GetChild(3).Cast(); - if (with_ordinality_opt.HasResult()) { - auto with_ordinality_value = transformer.Transform(with_ordinality_opt.GetResult()); - with_ordinality = with_ordinality_value; - } - auto result = TransformNearestTableFunction(transformer, lateral, qualified_table_function, - std::move(table_function_arguments), with_ordinality); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformNearestTableSubqueryInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional lateral {}; - auto &lateral_opt = list_pr.GetChild(0).Cast(); - if (lateral_opt.HasResult()) { - auto lateral_value = transformer.Transform(lateral_opt.GetResult()); - lateral = lateral_value; - } - auto subquery_reference = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformNearestTableSubquery(transformer, lateral, std::move(subquery_reference)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformNearestBaseTableRefInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto base_table_name = transformer.Transform>(list_pr.GetChild(0)); - optional> at_clause {}; - auto &at_clause_opt = list_pr.GetChild(1).Cast(); - if (at_clause_opt.HasResult()) { - auto at_clause_value = transformer.Transform>(at_clause_opt.GetResult()); - at_clause = std::move(at_clause_value); - } - optional> sample_clause {}; - auto &sample_clause_opt = list_pr.GetChild(2).Cast(); - if (sample_clause_opt.HasResult()) { - auto sample_clause_value = transformer.Transform>(sample_clause_opt.GetResult()); - sample_clause = std::move(sample_clause_value); - } - auto result = TransformNearestBaseTableRef(transformer, std::move(base_table_name), std::move(at_clause), - std::move(sample_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformNearestParensTableRefInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto table_ref = transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(0))); - optional> sample_clause {}; - auto &sample_clause_opt = list_pr.GetChild(1).Cast(); - if (sample_clause_opt.HasResult()) { - auto sample_clause_value = transformer.Transform>(sample_clause_opt.GetResult()); - sample_clause = std::move(sample_clause_value); - } - auto result = TransformNearestParensTableRef(transformer, std::move(table_ref), std::move(sample_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformApproxOrExactInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformNearestApproxInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformNearestApprox(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformNearestExactInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformNearestExact(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformDistanceOrSimilarityInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformNearestDistanceInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformNearestDistance(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformNearestSimilarityInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformNearestSimilarity(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformRegularJoinClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional asof {}; - auto &asof_opt = list_pr.GetChild(0).Cast(); - if (asof_opt.HasResult()) { - auto asof_value = transformer.Transform(asof_opt.GetResult()); - asof = asof_value; - } - optional join_type {}; - auto &join_type_opt = list_pr.GetChild(1).Cast(); - if (join_type_opt.HasResult()) { - auto join_type_value = transformer.Transform(join_type_opt.GetResult()); - join_type = join_type_value; - } - auto table_ref = transformer.Transform>(list_pr.GetChild(3)); - auto join_qualifier = transformer.Transform(list_pr.GetChild(4)); - auto result = - TransformRegularJoinClause(transformer, asof, join_type, std::move(table_ref), std::move(join_qualifier)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformJoinByClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_label = - transformer.Transform(ExtractResultFromParens(list_pr.GetChild(2)).Cast().GetChild(1)); - auto table_ref = transformer.Transform>(list_pr.GetChild(3)); - auto join_qualifier = transformer.Transform(list_pr.GetChild(4)); - auto result = TransformJoinByClause(transformer, col_label, std::move(table_ref), std::move(join_qualifier)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAsofInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformAsof(transformer); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformJoinWithoutOnClauseInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto join_prefix = transformer.Transform(list_pr.GetChild(0)); - auto inner_table_ref = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformJoinWithoutOnClause(transformer, join_prefix, std::move(inner_table_ref)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformJoinQualifierInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOnClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformOnClause(transformer, std::move(expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformUsingClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector column_name; - auto column_name_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(1))); - for (auto &column_name_item : column_name_items) { - auto column_name_value = column_name_item.get().Cast().identifier; - column_name.push_back(column_name_value); - } - auto result = TransformUsingClause(transformer, column_name); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformJoinTypeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformJoinPrefixInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCrossJoinPrefixInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCrossJoinPrefix(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformNaturalJoinPrefixInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional join_type {}; - auto &join_type_opt = list_pr.GetChild(1).Cast(); - if (join_type_opt.HasResult()) { - auto join_type_value = transformer.Transform(join_type_opt.GetResult()); - join_type = join_type_value; - } - auto result = TransformNaturalJoinPrefix(transformer, join_type); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformPositionalJoinPrefixInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto result = TransformPositionalJoinPrefix(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformFullJoinInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto result = TransformFullJoin(transformer, has_result); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformLeftJoinInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto result = TransformLeftJoin(transformer, has_result); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformRightJoinInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto result = TransformRightJoin(transformer, has_result); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSemiJoinInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSemiJoin(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformAntiJoinInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformAntiJoin(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformInnerJoinInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformInnerJoin(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformFromClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> table_ref; - auto table_ref_items = ExtractParseResultsFromList(list_pr.GetChild(1)); - for (auto &table_ref_item : table_ref_items) { - auto table_ref_value = transformer.Transform>(table_ref_item.get()); - table_ref.push_back(std::move(table_ref_value)); - } - auto result = TransformFromClause(transformer, std::move(table_ref)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformWhereClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformWhereClause(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformGroupByClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto group_by_expressions = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformGroupByClause(transformer, std::move(group_by_expressions)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformHavingClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformHavingClause(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformQualifyClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformQualifyClause(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSampleClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto sample_entry = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformSampleClause(transformer, std::move(sample_entry)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformWindowClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> window_definition; - auto window_definition_items = ExtractParseResultsFromList(list_pr.GetChild(1)); - for (auto &window_definition_item : window_definition_items) { - auto window_definition_value = - transformer.Transform>(window_definition_item.get()); - window_definition.push_back(std::move(window_definition_value)); - } - auto result = TransformWindowClause(transformer, std::move(window_definition)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSampleEntryInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSampleEntryCountInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto sample_count = transformer.Transform>(list_pr.GetChild(0)); - optional> sample_properties {}; - auto &sample_properties_opt = list_pr.GetChild(1).Cast(); - if (sample_properties_opt.HasResult()) { - auto sample_properties_value = transformer.Transform>( - ExtractResultFromParens(sample_properties_opt.GetResult())); - sample_properties = sample_properties_value; - } - auto result = TransformSampleEntryCount(transformer, std::move(sample_count), sample_properties); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformSampleEntryFunctionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional sample_function {}; - auto &sample_function_opt = list_pr.GetChild(0).Cast(); - if (sample_function_opt.HasResult()) { - auto sample_function_value = transformer.Transform(sample_function_opt.GetResult()); - sample_function = sample_function_value; - } - auto sample_count = transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(1))); - optional repeatable_sample {}; - auto &repeatable_sample_opt = list_pr.GetChild(2).Cast(); - if (repeatable_sample_opt.HasResult()) { - auto repeatable_sample_value = transformer.Transform(repeatable_sample_opt.GetResult()); - repeatable_sample = repeatable_sample_value; - } - auto result = - TransformSampleEntryFunction(transformer, sample_function, std::move(sample_count), repeatable_sample); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSampleFunctionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformSampleFunction(transformer, col_id); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSamplePropertiesInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id = transformer.Transform(list_pr.GetChild(0)); - optional sample_seed {}; - auto &sample_seed_opt = list_pr.GetChild(1).Cast(); - if (sample_seed_opt.HasResult()) { - auto sample_seed_value = - transformer.Transform(sample_seed_opt.GetResult().Cast().GetChild(1)); - sample_seed = sample_seed_value; - } - auto result = TransformSampleProperties(transformer, col_id, sample_seed); - return make_uniq>>(result); -} - -unique_ptr PEGTransformerFactory::TransformRepeatableSampleInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto sample_seed = transformer.Transform(ExtractResultFromParens(list_pr.GetChild(1))); - auto result = TransformRepeatableSample(transformer, sample_seed); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSampleSeedInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto number_literal = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformSampleSeed(transformer, std::move(number_literal)); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSampleCountInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto sample_value = transformer.Transform>(list_pr.GetChild(0)); - optional sample_unit {}; - auto &sample_unit_opt = list_pr.GetChild(1).Cast(); - if (sample_unit_opt.HasResult()) { - auto sample_unit_value = transformer.Transform(sample_unit_opt.GetResult()); - sample_unit = sample_unit_value; - } - auto result = TransformSampleCount(transformer, std::move(sample_value), sample_unit); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSampleValueInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSampleUnitInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSamplePercentageInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSamplePercentage(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSampleRowsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSampleRows(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformGroupByExpressionsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformGroupByAllInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformGroupByAll(transformer); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformGroupByListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector group_by_expression; - auto group_by_expression_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - for (auto &group_by_expression_item : group_by_expression_items) { - auto group_by_expression_value = transformer.Transform(group_by_expression_item.get()); - group_by_expression.push_back(std::move(group_by_expression_value)); - } - auto result = TransformGroupByList(transformer, std::move(group_by_expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformGroupByExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformGroupByBaseExpressionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformGroupByBaseExpression(transformer, std::move(expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformEmptyGroupingItemInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformEmptyGroupingItem(transformer); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCubeOrRollupClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto cube_or_rollup = transformer.Transform(list_pr.GetChild(0)); - optional>> expression {}; - auto &expression_opt = ExtractResultFromParens(list_pr.GetChild(1)).Cast(); - if (expression_opt.HasResult()) { - vector> expression_value; - auto expression_value_items_1 = ExtractParseResultsFromList(expression_opt.GetResult()); - for (auto &expression_value_item_1 : expression_value_items_1) { - auto expression_value_value_1 = - transformer.Transform>(expression_value_item_1.get()); - expression_value.push_back(std::move(expression_value_value_1)); - } - expression = std::move(expression_value); - } - auto result = TransformCubeOrRollupClause(transformer, cube_or_rollup, std::move(expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCubeOrRollupInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - string result; - if (choice_pr.GetResult().type == ParseResultType::IDENTIFIER) { - result = choice_pr.GetResult().Cast().identifier.GetIdentifierName(); - } else if (choice_pr.GetResult().type == ParseResultType::KEYWORD) { - result = choice_pr.GetResult().Cast().keyword; - } else if (choice_pr.GetResult().type == ParseResultType::STRING) { - result = choice_pr.GetResult().Cast().result; - } else if (choice_pr.GetResult().type == ParseResultType::OPERATOR) { - result = choice_pr.GetResult().Cast().operator_token; - } else { - result = transformer.Transform(choice_pr.GetResult()); - } - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformCubeKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformCubeKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformRollupKeywordInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformRollupKeyword(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformGroupingSetsClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector group_by_expression; - auto group_by_expression_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(2))); - for (auto &group_by_expression_item : group_by_expression_items) { - auto group_by_expression_value = transformer.Transform(group_by_expression_item.get()); - group_by_expression.push_back(std::move(group_by_expression_value)); - } - auto result = TransformGroupingSetsClause(transformer, std::move(group_by_expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSubqueryReferenceInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto select_statement_internal = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(0))); - auto result = TransformSubqueryReference(transformer, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOrderByExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - optional desc_or_asc {}; - auto &desc_or_asc_opt = list_pr.GetChild(1).Cast(); - if (desc_or_asc_opt.HasResult()) { - auto desc_or_asc_value = transformer.Transform(desc_or_asc_opt.GetResult()); - desc_or_asc = desc_or_asc_value; - } - optional nulls_first_or_last {}; - auto &nulls_first_or_last_opt = list_pr.GetChild(2).Cast(); - if (nulls_first_or_last_opt.HasResult()) { - auto nulls_first_or_last_value = transformer.Transform(nulls_first_or_last_opt.GetResult()); - nulls_first_or_last = nulls_first_or_last_value; - } - auto result = TransformOrderByExpression(transformer, std::move(expression), desc_or_asc, nulls_first_or_last); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformDescOrAscInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDescendingOrderInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformDescendingOrder(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformAscendingOrderInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformAscendingOrder(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformNullsFirstOrLastInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformNullsFirstInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformNullsFirst(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformNullsLastInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformNullsLast(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformOrderByClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto order_by_expressions = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformOrderByClause(transformer, std::move(order_by_expressions)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOrderByExpressionsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformOrderByExpressionListInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector order_by_expression; - auto order_by_expression_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - for (auto &order_by_expression_item : order_by_expression_items) { - auto order_by_expression_value = transformer.Transform(order_by_expression_item.get()); - order_by_expression.push_back(std::move(order_by_expression_value)); - } - auto result = TransformOrderByExpressionList(transformer, std::move(order_by_expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOrderByAllInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional desc_or_asc {}; - auto &desc_or_asc_opt = list_pr.GetChild(1).Cast(); - if (desc_or_asc_opt.HasResult()) { - auto desc_or_asc_value = transformer.Transform(desc_or_asc_opt.GetResult()); - desc_or_asc = desc_or_asc_value; - } - optional nulls_first_or_last {}; - auto &nulls_first_or_last_opt = list_pr.GetChild(2).Cast(); - if (nulls_first_or_last_opt.HasResult()) { - auto nulls_first_or_last_value = transformer.Transform(nulls_first_or_last_opt.GetResult()); - nulls_first_or_last = nulls_first_or_last_value; - } - auto result = TransformOrderByAll(transformer, desc_or_asc, nulls_first_or_last); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformLimitClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto limit_value = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformLimitClause(transformer, std::move(limit_value)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOffsetClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto offset_value = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformOffsetClause(transformer, std::move(offset_value)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformOffsetValueInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto result = TransformOffsetValue(transformer, std::move(expression), has_result); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformLimitValueInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformLimitAllInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformLimitAll(transformer); - return make_uniq>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformLimitLiteralPercentInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto number_literal = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformLimitLiteralPercent(transformer, std::move(number_literal)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformLimitExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto result = TransformLimitExpression(transformer, std::move(expression), has_result); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformFetchClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto fetch_value = transformer.Transform(list_pr.GetChild(2)); - auto result = std::move(fetch_value); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformFetchValueInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformFetchValue(transformer, std::move(expression)); - return make_uniq>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformAliasedExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformColIdExpressionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto col_id = transformer.Transform(list_pr.GetChild(0)); - auto expression = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformColIdExpression(transformer, col_id, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformExpressionAsCollabelInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - auto col_label_or_string = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformExpressionAsCollabel(transformer, std::move(expression), col_label_or_string); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformExpressionOptIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto expression = transformer.Transform>(list_pr.GetChild(0)); - optional identifier {}; - auto &identifier_opt = list_pr.GetChild(1).Cast(); - if (identifier_opt.HasResult()) { - auto identifier_value = identifier_opt.GetResult().Cast().identifier; - identifier = identifier_value; - } - auto result = TransformExpressionOptIdentifier(transformer, std::move(expression), identifier); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformValuesClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector>> values_expressions; - auto values_expressions_items = ExtractParseResultsFromList(list_pr.GetChild(1)); - for (auto &values_expressions_item : values_expressions_items) { - auto values_expressions_value = - transformer.Transform>>(values_expressions_item.get()); - values_expressions.push_back(std::move(values_expressions_value)); - } - auto result = TransformValuesClause(transformer, std::move(values_expressions)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformValuesExpressionsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> expression; - auto expression_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &expression_item : expression_items) { - auto expression_value = transformer.Transform>(expression_item.get()); - expression.push_back(std::move(expression_value)); - } - auto result = TransformValuesExpressions(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSetStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto set_assignment_or_time_zone = transformer.Transform>(list_pr.GetChild(1)); - auto result = TransformSetStatement(transformer, std::move(set_assignment_or_time_zone)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformSetAssignmentOrTimeZoneInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformResetStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto set_variable_or_setting = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformResetStatement(transformer, set_variable_or_setting); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSetSchemaInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto string_literal = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformSetSchema(transformer, string_literal); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformStandardAssignmentInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto set_variable_or_setting = transformer.Transform(list_pr.GetChild(0)); - auto set_assignment = transformer.Transform>>(list_pr.GetChild(1)); - auto result = TransformStandardAssignment(transformer, set_variable_or_setting, std::move(set_assignment)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformSetVariableOrSettingInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSetTimeZoneInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto zone_value = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformSetTimeZone(transformer, std::move(zone_value)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformZoneValueInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformZoneLocalInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformZoneLocal(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformZoneDefaultInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformZoneDefault(transformer); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformZoneStringLiteralInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto string_literal = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformZoneStringLiteral(transformer, string_literal); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformZoneIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(0).Cast().identifier; - auto result = TransformZoneIdentifier(transformer, identifier); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformZoneIntervalWithIntervalInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto string_literal = transformer.Transform(list_pr.GetChild(1)); - optional interval {}; - auto &interval_opt = list_pr.GetChild(2).Cast(); - if (interval_opt.HasResult()) { - auto interval_value = transformer.Transform(interval_opt.GetResult()); - interval = interval_value; - } - auto result = TransformZoneIntervalWithInterval(transformer, string_literal, interval); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformZoneIntervalWithPrecisionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto number_literal = - transformer.Transform>(ExtractResultFromParens(list_pr.GetChild(1))); - auto string_literal = transformer.Transform(list_pr.GetChild(2)); - auto result = TransformZoneIntervalWithPrecision(transformer, std::move(number_literal), string_literal); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformSetSettingInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional setting_scope {}; - auto &setting_scope_opt = list_pr.GetChild(0).Cast(); - if (setting_scope_opt.HasResult()) { - auto setting_scope_value = transformer.Transform(setting_scope_opt.GetResult()); - setting_scope = setting_scope_value; - } - auto setting_name = list_pr.GetChild(1).Cast().identifier; - auto result = TransformSetSetting(transformer, setting_scope, setting_name); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSetVariableInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto variable_scope = transformer.Transform(list_pr.GetChild(0)); - auto identifier = list_pr.GetChild(1).Cast().identifier; - auto result = TransformSetVariable(transformer, variable_scope, identifier); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformVariableScopeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformVariableScope(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSettingScopeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformLocalScopeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformLocalScope(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSessionScopeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformSessionScope(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformGlobalScopeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformGlobalScope(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformSetAssignmentInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto variable_list = transformer.Transform>>(list_pr.GetChild(1)); - auto result = TransformSetAssignment(transformer, std::move(variable_list)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformVariableListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector> expression; - auto expression_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - for (auto &expression_item : expression_items) { - auto expression_value = transformer.Transform>(expression_item.get()); - expression.push_back(std::move(expression_value)); - } - auto result = TransformVariableList(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformTransactionStatementInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformBeginTransactionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - optional read_or_write {}; - auto &read_or_write_opt = list_pr.GetChild(2).Cast(); - if (read_or_write_opt.HasResult()) { - auto read_or_write_value = transformer.Transform(read_or_write_opt.GetResult()); - read_or_write = read_or_write_value; - } - auto result = TransformBeginTransaction(transformer, has_result, read_or_write); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformRollbackTransactionInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto result = TransformRollbackTransaction(transformer, has_result); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformCommitTransactionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); - has_result = has_result_opt.HasResult(); - auto result = TransformCommitTransaction(transformer, has_result); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformReadOrWriteInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto read_only_or_read_write = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformReadOrWrite(transformer, read_only_or_read_write); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformReadOnlyOrReadWriteInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformReadOnlyInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformReadOnly(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformReadWriteInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformReadWrite(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformUpdateStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional with_clause {}; - auto &with_clause_opt = list_pr.GetChild(0).Cast(); - if (with_clause_opt.HasResult()) { - auto with_clause_value = transformer.Transform(with_clause_opt.GetResult()); - with_clause = std::move(with_clause_value); - } - auto update_target = transformer.Transform>(list_pr.GetChild(2)); - auto update_set_clause = transformer.Transform>(list_pr.GetChild(3)); - optional> from_clause {}; - auto &from_clause_opt = list_pr.GetChild(4).Cast(); - if (from_clause_opt.HasResult()) { - auto from_clause_value = transformer.Transform>(from_clause_opt.GetResult()); - from_clause = std::move(from_clause_value); - } - optional> where_clause {}; - auto &where_clause_opt = list_pr.GetChild(5).Cast(); - if (where_clause_opt.HasResult()) { - auto where_clause_value = transformer.Transform>(where_clause_opt.GetResult()); - where_clause = std::move(where_clause_value); - } - optional>> returning_clause {}; - auto &returning_clause_opt = list_pr.GetChild(6).Cast(); - if (returning_clause_opt.HasResult()) { - auto returning_clause_value = - transformer.Transform>>(returning_clause_opt.GetResult()); - returning_clause = std::move(returning_clause_value); - } - auto result = TransformUpdateStatement(transformer, std::move(with_clause), std::move(update_target), - std::move(update_set_clause), std::move(from_clause), - std::move(where_clause), std::move(returning_clause)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformUpdateTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformBaseTableSetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto base_table_name = transformer.Transform>(list_pr.GetChild(0)); - auto result = TransformBaseTableSet(transformer, std::move(base_table_name)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformBaseTableAliasSetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto base_table_name = transformer.Transform>(list_pr.GetChild(0)); - optional update_alias {}; - auto &update_alias_opt = list_pr.GetChild(1).Cast(); - if (update_alias_opt.HasResult()) { - auto update_alias_value = transformer.Transform(update_alias_opt.GetResult()); - update_alias = update_alias_value; - } - auto result = TransformBaseTableAliasSet(transformer, std::move(base_table_name), update_alias); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformUpdateAliasInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(0).Cast(); - has_result = has_result_opt.HasResult(); - auto col_id = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformUpdateAlias(transformer, has_result, col_id); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformUpdateSetClauseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform>(choice_pr.GetResult()); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformUpdateSetTupleInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector column_name; - auto column_name_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &column_name_item : column_name_items) { - auto column_name_value = column_name_item.get().Cast().identifier; - column_name.push_back(column_name_value); - } - auto expression = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformUpdateSetTuple(transformer, column_name, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformUpdateSetElementListInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector>> update_set_element; - auto update_set_element_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - for (auto &update_set_element_item : update_set_element_items) { - auto update_set_element_value = - transformer.Transform>>(update_set_element_item.get()); - update_set_element.push_back(std::move(update_set_element_value)); - } - auto result = TransformUpdateSetElementList(transformer, std::move(update_set_element)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformUpdateSetElementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto update_set_column_target = transformer.Transform(list_pr.GetChild(0)); - auto expression = transformer.Transform>(list_pr.GetChild(2)); - auto result = TransformUpdateSetElement(transformer, update_set_column_target, std::move(expression)); - return make_uniq>>>(std::move(result)); -} - -unique_ptr -PEGTransformerFactory::TransformUpdateSetColumnTargetInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto column_name = list_pr.GetChild(0).Cast().identifier; - optional> dot_identifier {}; - auto &dot_identifier_opt = list_pr.GetChild(1).Cast(); - if (dot_identifier_opt.HasResult()) { - vector dot_identifier_value; - auto &dot_identifier_value_repeat_1 = dot_identifier_opt.GetResult().Cast(); - for (auto &dot_identifier_value_item_1 : dot_identifier_value_repeat_1.GetChildren()) { - auto dot_identifier_value_value_1 = transformer.Transform(dot_identifier_value_item_1.get()); - dot_identifier_value.push_back(dot_identifier_value_value_1); - } - dot_identifier = dot_identifier_value; - } - auto result = TransformUpdateSetColumnTarget(transformer, column_name, dot_identifier); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformUseStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto use_target = transformer.Transform(list_pr.GetChild(1)); - auto result = TransformUseStatement(transformer, use_target); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformUseTargetInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformSchemaNameAsUseTargetInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto schema_name = list_pr.GetChild(0).Cast().identifier; - auto result = TransformSchemaNameAsUseTarget(transformer, schema_name); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformCatalogNameAsUseTargetInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto catalog_name = list_pr.GetChild(0).Cast().identifier; - auto result = TransformCatalogNameAsUseTarget(transformer, catalog_name); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformUseTargetCatalogSchemaInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto catalog_name = list_pr.GetChild(0).Cast().identifier; - auto reserved_schema_name = list_pr.GetChild(2).Cast().identifier; - optional> dot_identifier {}; - auto &dot_identifier_opt = list_pr.GetChild(3).Cast(); - if (dot_identifier_opt.HasResult()) { - vector dot_identifier_value; - auto &dot_identifier_value_repeat_1 = dot_identifier_opt.GetResult().Cast(); - for (auto &dot_identifier_value_item_1 : dot_identifier_value_repeat_1.GetChildren()) { - auto dot_identifier_value_value_1 = transformer.Transform(dot_identifier_value_item_1.get()); - dot_identifier_value.push_back(dot_identifier_value_value_1); - } - dot_identifier = dot_identifier_value; - } - auto result = TransformUseTargetCatalogSchema(transformer, catalog_name, reserved_schema_name, dot_identifier); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformDotIdentifierInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto identifier = list_pr.GetChild(1).Cast().identifier; - auto result = TransformDotIdentifier(transformer, identifier); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformVacuumStatementInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional vacuum_options {}; - auto &vacuum_options_opt = list_pr.GetChild(1).Cast(); - if (vacuum_options_opt.HasResult()) { - auto vacuum_options_value = transformer.Transform(vacuum_options_opt.GetResult()); - vacuum_options = vacuum_options_value; - } - optional analyze_target {}; - auto &analyze_target_opt = list_pr.GetChild(2).Cast(); - if (analyze_target_opt.HasResult()) { - auto analyze_target_value = transformer.Transform(analyze_target_opt.GetResult()); - analyze_target = std::move(analyze_target_value); - } - auto result = TransformVacuumStatement(transformer, vacuum_options, std::move(analyze_target)); - return make_uniq>>(std::move(result)); -} - -unique_ptr PEGTransformerFactory::TransformVacuumOptionsInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = transformer.Transform(choice_pr.GetResult()); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformVacuumParensOptionsInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector vacuum_option; - auto vacuum_option_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &vacuum_option_item : vacuum_option_items) { - auto vacuum_option_value = transformer.Transform(vacuum_option_item.get()); - vacuum_option.push_back(vacuum_option_value); - } - auto result = TransformVacuumParensOptions(transformer, vacuum_option); - return make_uniq>(result); -} - -unique_ptr -PEGTransformerFactory::TransformVacuumLegacyOptionsInternal(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - optional opt_full {}; - auto &opt_full_opt = list_pr.GetChild(0).Cast(); - if (opt_full_opt.HasResult()) { - auto opt_full_value = transformer.Transform(opt_full_opt.GetResult()); - opt_full = opt_full_value; - } - optional opt_freeze {}; - auto &opt_freeze_opt = list_pr.GetChild(1).Cast(); - if (opt_freeze_opt.HasResult()) { - auto opt_freeze_value = transformer.Transform(opt_freeze_opt.GetResult()); - opt_freeze = opt_freeze_value; - } - optional opt_verbose {}; - auto &opt_verbose_opt = list_pr.GetChild(2).Cast(); - if (opt_verbose_opt.HasResult()) { - auto opt_verbose_value = transformer.Transform(opt_verbose_opt.GetResult()); - opt_verbose = opt_verbose_value; - } - optional opt_analyze {}; - auto &opt_analyze_opt = list_pr.GetChild(3).Cast(); - if (opt_analyze_opt.HasResult()) { - auto opt_analyze_value = transformer.Transform(opt_analyze_opt.GetResult()); - opt_analyze = opt_analyze_value; - } - auto result = TransformVacuumLegacyOptions(transformer, opt_full, opt_freeze, opt_verbose, opt_analyze); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformVacuumOptionInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - string result; - if (choice_pr.GetResult().type == ParseResultType::IDENTIFIER) { - result = choice_pr.GetResult().Cast().identifier.GetIdentifierName(); - } else if (choice_pr.GetResult().type == ParseResultType::KEYWORD) { - result = choice_pr.GetResult().Cast().keyword; - } else if (choice_pr.GetResult().type == ParseResultType::STRING) { - result = choice_pr.GetResult().Cast().result; - } else if (choice_pr.GetResult().type == ParseResultType::OPERATOR) { - result = choice_pr.GetResult().Cast().operator_token; - } else { - result = transformer.Transform(choice_pr.GetResult()); - } - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformOptAnalyzeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto analyze_keyword = transformer.Transform(list_pr.GetChild(0)); - auto result = TransformOptAnalyze(transformer, analyze_keyword); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformOptFullInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformOptFull(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformOptFreezeInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformOptFreeze(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformOptVerboseInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto result = TransformOptVerbose(transformer); - return make_uniq>(result); -} - -unique_ptr PEGTransformerFactory::TransformNameListInternal(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - vector col_id; - auto col_id_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); - for (auto &col_id_item : col_id_items) { - auto col_id_value = transformer.Transform(col_id_item.get()); - col_id.push_back(col_id_value); - } - auto result = TransformNameList(transformer, col_id); - return make_uniq>>(result); -} - -void PEGTransformerFactory::RegisterGenerated() { - static const TransformFinalizeRule builtin_transform_rules[] = { - {"AlterStatement", &PEGTransformerFactory::TransformAlterStatementInternal}, - {"AlterOptions", &PEGTransformerFactory::TransformAlterOptionsInternal}, - {"AlterTableStmt", &PEGTransformerFactory::TransformAlterTableStmtInternal}, - {"AlterSchemaStmt", &PEGTransformerFactory::TransformAlterSchemaStmtInternal}, - {"AlterTableOptions", &PEGTransformerFactory::TransformAlterTableOptionsInternal}, - {"AddConstraint", &PEGTransformerFactory::TransformAddConstraintInternal}, - {"DropConstraint", &PEGTransformerFactory::TransformDropConstraintInternal}, - {"AddColumn", &PEGTransformerFactory::TransformAddColumnInternal}, - {"AddColumnEntry", &PEGTransformerFactory::TransformAddColumnEntryInternal}, - {"DropColumn", &PEGTransformerFactory::TransformDropColumnInternal}, - {"AlterColumn", &PEGTransformerFactory::TransformAlterColumnInternal}, - {"RenameColumn", &PEGTransformerFactory::TransformRenameColumnInternal}, - {"NestedColumnName", &PEGTransformerFactory::TransformNestedColumnNameInternal}, - {"IdentifierDot", &PEGTransformerFactory::TransformIdentifierDotInternal}, - {"RenameAlter", &PEGTransformerFactory::TransformRenameAlterInternal}, - {"SetPartitionedBy", &PEGTransformerFactory::TransformSetPartitionedByInternal}, - {"ResetPartitionedBy", &PEGTransformerFactory::TransformResetPartitionedByInternal}, - {"SetSortedBy", &PEGTransformerFactory::TransformSetSortedByInternal}, - {"ResetSortedBy", &PEGTransformerFactory::TransformResetSortedByInternal}, - {"SetOptions", &PEGTransformerFactory::TransformSetOptionsInternal}, - {"ResetOptions", &PEGTransformerFactory::TransformResetOptionsInternal}, - {"AlterColumnEntry", &PEGTransformerFactory::TransformAlterColumnEntryInternal}, - {"AddOrDropDefault", &PEGTransformerFactory::TransformAddOrDropDefaultInternal}, - {"AddDefault", &PEGTransformerFactory::TransformAddDefaultInternal}, - {"DropDefault", &PEGTransformerFactory::TransformDropDefaultInternal}, - {"ChangeNullability", &PEGTransformerFactory::TransformChangeNullabilityInternal}, - {"DropOrSet", &PEGTransformerFactory::TransformDropOrSetInternal}, - {"DropNullability", &PEGTransformerFactory::TransformDropNullabilityInternal}, - {"SetNullability", &PEGTransformerFactory::TransformSetNullabilityInternal}, - {"AlterType", &PEGTransformerFactory::TransformAlterTypeInternal}, - {"UsingExpression", &PEGTransformerFactory::TransformUsingExpressionInternal}, - {"AlterViewStmt", &PEGTransformerFactory::TransformAlterViewStmtInternal}, - {"AlterSequenceStmt", &PEGTransformerFactory::TransformAlterSequenceStmtInternal}, - {"QualifiedSequenceName", &PEGTransformerFactory::TransformQualifiedSequenceNameInternal}, - {"AlterSequenceOptions", &PEGTransformerFactory::TransformAlterSequenceOptionsInternal}, - {"RenameAlterSequenceOptions", &PEGTransformerFactory::TransformRenameAlterSequenceOptionsInternal}, - {"SetSequenceOption", &PEGTransformerFactory::TransformSetSequenceOptionInternal}, - {"AlterDatabaseStmt", &PEGTransformerFactory::TransformAlterDatabaseStmtInternal}, - {"AnalyzeStatement", &PEGTransformerFactory::TransformAnalyzeStatementInternal}, - {"AnalyzeTarget", &PEGTransformerFactory::TransformAnalyzeTargetInternal}, - {"AnalyzeVerbose", &PEGTransformerFactory::TransformAnalyzeVerboseInternal}, - {"AttachStatement", &PEGTransformerFactory::TransformAttachStatementInternal}, - {"DatabasePath", &PEGTransformerFactory::TransformDatabasePathInternal}, - {"AttachAlias", &PEGTransformerFactory::TransformAttachAliasInternal}, - {"AttachOptions", &PEGTransformerFactory::TransformAttachOptionsInternal}, - {"CallStatement", &PEGTransformerFactory::TransformCallStatementInternal}, - {"CheckpointStatement", &PEGTransformerFactory::TransformCheckpointStatementInternal}, - {"CheckpointForce", &PEGTransformerFactory::TransformCheckpointForceInternal}, - {"CommentStatement", &PEGTransformerFactory::TransformCommentStatementInternal}, - {"CommentOnType", &PEGTransformerFactory::TransformCommentOnTypeInternal}, - {"CommentTable", &PEGTransformerFactory::TransformCommentTableInternal}, - {"CommentSequence", &PEGTransformerFactory::TransformCommentSequenceInternal}, - {"CommentFunction", &PEGTransformerFactory::TransformCommentFunctionInternal}, - {"CommentMacroTable", &PEGTransformerFactory::TransformCommentMacroTableInternal}, - {"CommentMacro", &PEGTransformerFactory::TransformCommentMacroInternal}, - {"CommentView", &PEGTransformerFactory::TransformCommentViewInternal}, - {"CommentDatabase", &PEGTransformerFactory::TransformCommentDatabaseInternal}, - {"CommentIndex", &PEGTransformerFactory::TransformCommentIndexInternal}, - {"CommentSchema", &PEGTransformerFactory::TransformCommentSchemaInternal}, - {"CommentType", &PEGTransformerFactory::TransformCommentTypeInternal}, - {"CommentColumn", &PEGTransformerFactory::TransformCommentColumnInternal}, - {"CommentValue", &PEGTransformerFactory::TransformCommentValueInternal}, - {"StringLiteralValue", &PEGTransformerFactory::TransformStringLiteralValueInternal}, - {"AnalyzeKeyword", &PEGTransformerFactory::TransformAnalyzeKeywordInternal}, - {"ExpressionStatement", &PEGTransformerFactory::TransformExpressionStatementInternal}, - {"ExpressionAlias", &PEGTransformerFactory::TransformExpressionAliasInternal}, - {"ConstraintName", &PEGTransformerFactory::TransformConstraintNameInternal}, - {"CollationName", &PEGTransformerFactory::TransformCollationNameInternal}, - {"Type", &PEGTransformerFactory::TransformTypeInternal}, - {"TypeVariations", &PEGTransformerFactory::TransformTypeVariationsInternal}, - {"SimpleType", &PEGTransformerFactory::TransformSimpleTypeInternal}, - {"CharacterSimpleType", &PEGTransformerFactory::TransformCharacterSimpleTypeInternal}, - {"QualifiedSimpleType", &PEGTransformerFactory::TransformQualifiedSimpleTypeInternal}, - {"IntervalType", &PEGTransformerFactory::TransformIntervalTypeInternal}, - {"IntervalInterval", &PEGTransformerFactory::TransformIntervalIntervalInternal}, - {"IntervalWithSpecifier", &PEGTransformerFactory::TransformIntervalWithSpecifierInternal}, - {"IntervalWithRangeSpecifier", &PEGTransformerFactory::TransformIntervalWithRangeSpecifierInternal}, - {"IntervalWithSimpleSpecifier", &PEGTransformerFactory::TransformIntervalWithSimpleSpecifierInternal}, - {"IntervalWithoutSpecifier", &PEGTransformerFactory::TransformIntervalWithoutSpecifierInternal}, - {"YearKeyword", &PEGTransformerFactory::TransformYearKeywordInternal}, - {"MonthKeyword", &PEGTransformerFactory::TransformMonthKeywordInternal}, - {"DayKeyword", &PEGTransformerFactory::TransformDayKeywordInternal}, - {"HourKeyword", &PEGTransformerFactory::TransformHourKeywordInternal}, - {"MinuteKeyword", &PEGTransformerFactory::TransformMinuteKeywordInternal}, - {"SecondKeyword", &PEGTransformerFactory::TransformSecondKeywordInternal}, - {"MillisecondKeyword", &PEGTransformerFactory::TransformMillisecondKeywordInternal}, - {"MicrosecondKeyword", &PEGTransformerFactory::TransformMicrosecondKeywordInternal}, - {"WeekKeyword", &PEGTransformerFactory::TransformWeekKeywordInternal}, - {"QuarterKeyword", &PEGTransformerFactory::TransformQuarterKeywordInternal}, - {"DecadeKeyword", &PEGTransformerFactory::TransformDecadeKeywordInternal}, - {"CenturyKeyword", &PEGTransformerFactory::TransformCenturyKeywordInternal}, - {"MillenniumKeyword", &PEGTransformerFactory::TransformMillenniumKeywordInternal}, - {"Interval", &PEGTransformerFactory::TransformIntervalInternal}, - {"IntervalToInterval", &PEGTransformerFactory::TransformIntervalToIntervalInternal}, - {"YearToMonth", &PEGTransformerFactory::TransformYearToMonthInternal}, - {"DayToHour", &PEGTransformerFactory::TransformDayToHourInternal}, - {"DayToMinute", &PEGTransformerFactory::TransformDayToMinuteInternal}, - {"DayToSecond", &PEGTransformerFactory::TransformDayToSecondInternal}, - {"HourToMinute", &PEGTransformerFactory::TransformHourToMinuteInternal}, - {"HourToSecond", &PEGTransformerFactory::TransformHourToSecondInternal}, - {"MinuteToSecond", &PEGTransformerFactory::TransformMinuteToSecondInternal}, - {"BitType", &PEGTransformerFactory::TransformBitTypeInternal}, - {"GeometryType", &PEGTransformerFactory::TransformGeometryTypeInternal}, - {"VariantType", &PEGTransformerFactory::TransformVariantTypeInternal}, - {"NumericType", &PEGTransformerFactory::TransformNumericTypeInternal}, - {"SimpleNumericType", &PEGTransformerFactory::TransformSimpleNumericTypeInternal}, - {"DecimalNumericType", &PEGTransformerFactory::TransformDecimalNumericTypeInternal}, - {"IntType", &PEGTransformerFactory::TransformIntTypeInternal}, - {"IntegerType", &PEGTransformerFactory::TransformIntegerTypeInternal}, - {"SmallintType", &PEGTransformerFactory::TransformSmallintTypeInternal}, - {"BigintType", &PEGTransformerFactory::TransformBigintTypeInternal}, - {"RealType", &PEGTransformerFactory::TransformRealTypeInternal}, - {"BooleanType", &PEGTransformerFactory::TransformBooleanTypeInternal}, - {"DoubleType", &PEGTransformerFactory::TransformDoubleTypeInternal}, - {"FloatType", &PEGTransformerFactory::TransformFloatTypeInternal}, - {"DecimalType", &PEGTransformerFactory::TransformDecimalTypeInternal}, - {"DecType", &PEGTransformerFactory::TransformDecTypeInternal}, - {"NumericModType", &PEGTransformerFactory::TransformNumericModTypeInternal}, - {"QualifiedTypeName", &PEGTransformerFactory::TransformQualifiedTypeNameInternal}, - {"TypeNameAsQualifiedName", &PEGTransformerFactory::TransformTypeNameAsQualifiedNameInternal}, - {"CatalogReservedSchemaTypeName", &PEGTransformerFactory::TransformCatalogReservedSchemaTypeNameInternal}, - {"SchemaReservedTypeName", &PEGTransformerFactory::TransformSchemaReservedTypeNameInternal}, - {"TypeModifiers", &PEGTransformerFactory::TransformTypeModifiersInternal}, - {"RowType", &PEGTransformerFactory::TransformRowTypeInternal}, - {"SetofType", &PEGTransformerFactory::TransformSetofTypeInternal}, - {"UnionType", &PEGTransformerFactory::TransformUnionTypeInternal}, - {"ColIdTypeList", &PEGTransformerFactory::TransformColIdTypeListInternal}, - {"MapType", &PEGTransformerFactory::TransformMapTypeInternal}, - {"TupleType", &PEGTransformerFactory::TransformTupleTypeInternal}, - {"ColIdType", &PEGTransformerFactory::TransformColIdTypeInternal}, - {"ArrayBounds", &PEGTransformerFactory::TransformArrayBoundsInternal}, - {"ArrayKeyword", &PEGTransformerFactory::TransformArrayKeywordInternal}, - {"ArrayKeywordWithBounds", &PEGTransformerFactory::TransformArrayKeywordWithBoundsInternal}, - {"SquareBracketsArray", &PEGTransformerFactory::TransformSquareBracketsArrayInternal}, - {"TimeType", &PEGTransformerFactory::TransformTimeTypeInternal}, - {"TimeOrTimestamp", &PEGTransformerFactory::TransformTimeOrTimestampInternal}, - {"TimeTypeId", &PEGTransformerFactory::TransformTimeTypeIdInternal}, - {"TimestampTypeId", &PEGTransformerFactory::TransformTimestampTypeIdInternal}, - {"TimeZone", &PEGTransformerFactory::TransformTimeZoneInternal}, - {"WithOrWithout", &PEGTransformerFactory::TransformWithOrWithoutInternal}, - {"WithRule", &PEGTransformerFactory::TransformWithRuleInternal}, - {"WithoutRule", &PEGTransformerFactory::TransformWithoutRuleInternal}, - {"ConnectStatement", &PEGTransformerFactory::TransformConnectStatementInternal}, - {"DisconnectStatement", &PEGTransformerFactory::TransformDisconnectStatementInternal}, - {"SessionTarget", &PEGTransformerFactory::TransformSessionTargetInternal}, - {"LocalSessionTarget", &PEGTransformerFactory::TransformLocalSessionTargetInternal}, - {"StringSessionTarget", &PEGTransformerFactory::TransformStringSessionTargetInternal}, - {"CatalogSessionTarget", &PEGTransformerFactory::TransformCatalogSessionTargetInternal}, - {"CopyStatement", &PEGTransformerFactory::TransformCopyStatementInternal}, - {"CopyVariations", &PEGTransformerFactory::TransformCopyVariationsInternal}, - {"CopyTable", &PEGTransformerFactory::TransformCopyTableInternal}, - {"FromOrTo", &PEGTransformerFactory::TransformFromOrToInternal}, - {"CopyFrom", &PEGTransformerFactory::TransformCopyFromInternal}, - {"CopyTo", &PEGTransformerFactory::TransformCopyToInternal}, - {"CopySelect", &PEGTransformerFactory::TransformCopySelectInternal}, - {"CopyFileName", &PEGTransformerFactory::TransformCopyFileNameInternal}, - {"CopyFileNameExpression", &PEGTransformerFactory::TransformCopyFileNameExpressionInternal}, - {"CopyFileNameStringLiteral", &PEGTransformerFactory::TransformCopyFileNameStringLiteralInternal}, - {"CopyFileNameIdentifier", &PEGTransformerFactory::TransformCopyFileNameIdentifierInternal}, - {"CopyFileNameIdentifierColId", &PEGTransformerFactory::TransformCopyFileNameIdentifierColIdInternal}, - {"IdentifierColId", &PEGTransformerFactory::TransformIdentifierColIdInternal}, - {"CopyOptions", &PEGTransformerFactory::TransformCopyOptionsInternal}, - {"CopyOptionList", &PEGTransformerFactory::TransformCopyOptionListInternal}, - {"SpecializedOptionList", &PEGTransformerFactory::TransformSpecializedOptionListInternal}, - {"SpecializedOptionTail", &PEGTransformerFactory::TransformSpecializedOptionTailInternal}, - {"SpecializedOption", &PEGTransformerFactory::TransformSpecializedOptionInternal}, - {"SingleOption", &PEGTransformerFactory::TransformSingleOptionInternal}, - {"BinaryOption", &PEGTransformerFactory::TransformBinaryOptionInternal}, - {"FreezeOption", &PEGTransformerFactory::TransformFreezeOptionInternal}, - {"OidsOption", &PEGTransformerFactory::TransformOidsOptionInternal}, - {"CsvOption", &PEGTransformerFactory::TransformCsvOptionInternal}, - {"HeaderOption", &PEGTransformerFactory::TransformHeaderOptionInternal}, - {"NullAsOption", &PEGTransformerFactory::TransformNullAsOptionInternal}, - {"DelimiterAsOption", &PEGTransformerFactory::TransformDelimiterAsOptionInternal}, - {"QuoteAsOption", &PEGTransformerFactory::TransformQuoteAsOptionInternal}, - {"EscapeAsOption", &PEGTransformerFactory::TransformEscapeAsOptionInternal}, - {"EncodingOption", &PEGTransformerFactory::TransformEncodingOptionInternal}, - {"ForceQuoteOption", &PEGTransformerFactory::TransformForceQuoteOptionInternal}, - {"StarSymbolColumnList", &PEGTransformerFactory::TransformStarSymbolColumnListInternal}, - {"ForceQuote", &PEGTransformerFactory::TransformForceQuoteInternal}, - {"PartitionByOption", &PEGTransformerFactory::TransformPartitionByOptionInternal}, - {"PartitionByColumnList", &PEGTransformerFactory::TransformPartitionByColumnListInternal}, - {"StarPartitionByColumnList", &PEGTransformerFactory::TransformStarPartitionByColumnListInternal}, - {"ParenthesizedPartitionByColumnList", - &PEGTransformerFactory::TransformParenthesizedPartitionByColumnListInternal}, - {"SinglePartitionByColumnList", &PEGTransformerFactory::TransformSinglePartitionByColumnListInternal}, - {"ForceNullOption", &PEGTransformerFactory::TransformForceNullOptionInternal}, - {"ForceNotNull", &PEGTransformerFactory::TransformForceNotNullInternal}, - {"CopyGenericOptionList", &PEGTransformerFactory::TransformCopyGenericOptionListInternal}, - {"CopyGenericOption", &PEGTransformerFactory::TransformCopyGenericOptionInternal}, - {"OrderByCopyOption", &PEGTransformerFactory::TransformOrderByCopyOptionInternal}, - {"PartitionedByCopyOption", &PEGTransformerFactory::TransformPartitionedByCopyOptionInternal}, - {"GenericCopyOptionList", &PEGTransformerFactory::TransformGenericCopyOptionListInternal}, - {"GenericCopyOption", &PEGTransformerFactory::TransformGenericCopyOptionInternal}, - {"GenericCopyOptionValue", &PEGTransformerFactory::TransformGenericCopyOptionValueInternal}, - {"GenericCopyOptionOrderList", &PEGTransformerFactory::TransformGenericCopyOptionOrderListInternal}, - {"GenericCopyOptionExpression", &PEGTransformerFactory::TransformGenericCopyOptionExpressionInternal}, - {"GenericCopyOptionParenthesizedExpressionList", - &PEGTransformerFactory::TransformGenericCopyOptionParenthesizedExpressionListInternal}, - {"CopyFromDatabase", &PEGTransformerFactory::TransformCopyFromDatabaseInternal}, - {"CopyFromDatabaseWithFlag", &PEGTransformerFactory::TransformCopyFromDatabaseWithFlagInternal}, - {"CopyFromDatabaseWithoutFlag", &PEGTransformerFactory::TransformCopyFromDatabaseWithoutFlagInternal}, - {"CopyDatabaseFlag", &PEGTransformerFactory::TransformCopyDatabaseFlagInternal}, - {"SchemaOrData", &PEGTransformerFactory::TransformSchemaOrDataInternal}, - {"CopySchema", &PEGTransformerFactory::TransformCopySchemaInternal}, - {"CopyData", &PEGTransformerFactory::TransformCopyDataInternal}, - {"CreateIndexStmt", &PEGTransformerFactory::TransformCreateIndexStmtInternal}, - {"WithList", &PEGTransformerFactory::TransformWithListInternal}, - {"RelOptionOrOids", &PEGTransformerFactory::TransformRelOptionOrOidsInternal}, - {"RelOptionList", &PEGTransformerFactory::TransformRelOptionListInternal}, - {"Oids", &PEGTransformerFactory::TransformOidsInternal}, - {"WithOrWithoutOids", &PEGTransformerFactory::TransformWithOrWithoutOidsInternal}, - {"WithOids", &PEGTransformerFactory::TransformWithOidsInternal}, - {"WithoutOids", &PEGTransformerFactory::TransformWithoutOidsInternal}, - {"IndexElement", &PEGTransformerFactory::TransformIndexElementInternal}, - {"UniqueIndex", &PEGTransformerFactory::TransformUniqueIndexInternal}, - {"IndexType", &PEGTransformerFactory::TransformIndexTypeInternal}, - {"RelOption", &PEGTransformerFactory::TransformRelOptionInternal}, - {"RelOptionName", &PEGTransformerFactory::TransformRelOptionNameInternal}, - {"DottedIdentifierString", &PEGTransformerFactory::TransformDottedIdentifierStringInternal}, - {"RelOptionArgumentOpt", &PEGTransformerFactory::TransformRelOptionArgumentOptInternal}, - {"DefArg", &PEGTransformerFactory::TransformDefArgInternal}, - {"DefArgNull", &PEGTransformerFactory::TransformDefArgNullInternal}, - {"DefArgKeyword", &PEGTransformerFactory::TransformDefArgKeywordInternal}, - {"DefArgStringLiteral", &PEGTransformerFactory::TransformDefArgStringLiteralInternal}, - {"NoneLiteral", &PEGTransformerFactory::TransformNoneLiteralInternal}, - {"CreateMacroStmt", &PEGTransformerFactory::TransformCreateMacroStmtInternal}, - {"MacroOrFunction", &PEGTransformerFactory::TransformMacroOrFunctionInternal}, - {"MacroKeyword", &PEGTransformerFactory::TransformMacroKeywordInternal}, - {"FunctionKeyword", &PEGTransformerFactory::TransformFunctionKeywordInternal}, - {"MacroDefinition", &PEGTransformerFactory::TransformMacroDefinitionInternal}, - {"MacroDefinitionBody", &PEGTransformerFactory::TransformMacroDefinitionBodyInternal}, - {"MacroParameters", &PEGTransformerFactory::TransformMacroParametersInternal}, - {"MacroParameter", &PEGTransformerFactory::TransformMacroParameterInternal}, - {"SimpleParameter", &PEGTransformerFactory::TransformSimpleParameterInternal}, - {"ScalarMacroDefinition", &PEGTransformerFactory::TransformScalarMacroDefinitionInternal}, - {"TableMacroDefinition", &PEGTransformerFactory::TransformTableMacroDefinitionInternal}, - {"CreateSchemaStmt", &PEGTransformerFactory::TransformCreateSchemaStmtInternal}, - {"CreateSecretStmt", &PEGTransformerFactory::TransformCreateSecretStmtInternal}, - {"SecretStorageSpecifier", &PEGTransformerFactory::TransformSecretStorageSpecifierInternal}, - {"SecretName", &PEGTransformerFactory::TransformSecretNameInternal}, - {"CreateSequenceStmt", &PEGTransformerFactory::TransformCreateSequenceStmtInternal}, - {"SequenceOption", &PEGTransformerFactory::TransformSequenceOptionInternal}, - {"SeqSetCycle", &PEGTransformerFactory::TransformSeqSetCycleInternal}, - {"SeqCycle", &PEGTransformerFactory::TransformSeqCycleInternal}, - {"SeqNoCycle", &PEGTransformerFactory::TransformSeqNoCycleInternal}, - {"SeqSetIncrement", &PEGTransformerFactory::TransformSeqSetIncrementInternal}, - {"SeqSetMinMax", &PEGTransformerFactory::TransformSeqSetMinMaxInternal}, - {"SeqNoMinMax", &PEGTransformerFactory::TransformSeqNoMinMaxInternal}, - {"SeqStartWith", &PEGTransformerFactory::TransformSeqStartWithInternal}, - {"SeqOwnedBy", &PEGTransformerFactory::TransformSeqOwnedByInternal}, - {"SeqMinOrMax", &PEGTransformerFactory::TransformSeqMinOrMaxInternal}, - {"MinValue", &PEGTransformerFactory::TransformMinValueInternal}, - {"MaxValue", &PEGTransformerFactory::TransformMaxValueInternal}, - {"CreateStatement", &PEGTransformerFactory::TransformCreateStatementInternal}, - {"CreateStatementVariation", &PEGTransformerFactory::TransformCreateStatementVariationInternal}, - {"OrReplace", &PEGTransformerFactory::TransformOrReplaceInternal}, - {"Temporary", &PEGTransformerFactory::TransformTemporaryInternal}, - {"Persistent", &PEGTransformerFactory::TransformPersistentInternal}, - {"TempPersistent", &PEGTransformerFactory::TransformTempPersistentInternal}, - {"TemporaryPersistent", &PEGTransformerFactory::TransformTemporaryPersistentInternal}, - {"CreateTableStmt", &PEGTransformerFactory::TransformCreateTableStmtInternal}, - {"CreateTableDefinition", &PEGTransformerFactory::TransformCreateTableDefinitionInternal}, - {"CreateTableAs", &PEGTransformerFactory::TransformCreateTableAsInternal}, - {"PartitionSortedOptions", &PEGTransformerFactory::TransformPartitionSortedOptionsInternal}, - {"PartitionOptSortedOptions", &PEGTransformerFactory::TransformPartitionOptSortedOptionsInternal}, - {"SortedOptPartitionOptions", &PEGTransformerFactory::TransformSortedOptPartitionOptionsInternal}, - {"PartitionOptions", &PEGTransformerFactory::TransformPartitionOptionsInternal}, - {"SortedOptions", &PEGTransformerFactory::TransformSortedOptionsInternal}, - {"WithData", &PEGTransformerFactory::TransformWithDataInternal}, - {"WithDataOnly", &PEGTransformerFactory::TransformWithDataOnlyInternal}, - {"WithNoData", &PEGTransformerFactory::TransformWithNoDataInternal}, - {"IdentifierList", &PEGTransformerFactory::TransformIdentifierListInternal}, - {"CreateColumnList", &PEGTransformerFactory::TransformCreateColumnListInternal}, - {"IfNotExists", &PEGTransformerFactory::TransformIfNotExistsInternal}, - {"QualifiedName", &PEGTransformerFactory::TransformQualifiedNameInternal}, - {"SchemaReservedIdentifierOrStringLiteral", - &PEGTransformerFactory::TransformSchemaReservedIdentifierOrStringLiteralInternal}, - {"CatalogReservedSchemaIdentifier", &PEGTransformerFactory::TransformCatalogReservedSchemaIdentifierInternal}, - {"IdentifierOrStringLiteral", &PEGTransformerFactory::TransformIdentifierOrStringLiteralInternal}, - {"ReservedIdentifierOrStringLiteral", - &PEGTransformerFactory::TransformReservedIdentifierOrStringLiteralInternal}, - {"CatalogQualification", &PEGTransformerFactory::TransformCatalogQualificationInternal}, - {"SchemaQualification", &PEGTransformerFactory::TransformSchemaQualificationInternal}, - {"ReservedSchemaQualification", &PEGTransformerFactory::TransformReservedSchemaQualificationInternal}, - {"TableQualification", &PEGTransformerFactory::TransformTableQualificationInternal}, - {"ReservedTableQualification", &PEGTransformerFactory::TransformReservedTableQualificationInternal}, - {"CreateTableColumnList", &PEGTransformerFactory::TransformCreateTableColumnListInternal}, - {"CreateTableColumnElement", &PEGTransformerFactory::TransformCreateTableColumnElementInternal}, - {"CreateTableColumnDefinition", &PEGTransformerFactory::TransformCreateTableColumnDefinitionInternal}, - {"CreateTableConstraint", &PEGTransformerFactory::TransformCreateTableConstraintInternal}, - {"ColumnDefinition", &PEGTransformerFactory::TransformColumnDefinitionInternal}, - {"ColumnConstraint", &PEGTransformerFactory::TransformColumnConstraintInternal}, - {"NotNullConstraint", &PEGTransformerFactory::TransformNotNullConstraintInternal}, - {"NullConstraint", &PEGTransformerFactory::TransformNullConstraintInternal}, - {"NotNullColumnConstraint", &PEGTransformerFactory::TransformNotNullColumnConstraintInternal}, - {"UniqueConstraint", &PEGTransformerFactory::TransformUniqueConstraintInternal}, - {"PrimaryKeyConstraint", &PEGTransformerFactory::TransformPrimaryKeyConstraintInternal}, - {"ConstraintTiming", &PEGTransformerFactory::TransformConstraintTimingInternal}, - {"ImmediateConstraint", &PEGTransformerFactory::TransformImmediateConstraintInternal}, - {"DeferredConstraint", &PEGTransformerFactory::TransformDeferredConstraintInternal}, - {"DefaultValue", &PEGTransformerFactory::TransformDefaultValueInternal}, - {"CheckConstraint", &PEGTransformerFactory::TransformCheckConstraintInternal}, - {"ForeignKeyConstraint", &PEGTransformerFactory::TransformForeignKeyConstraintInternal}, - {"ColumnCollation", &PEGTransformerFactory::TransformColumnCollationInternal}, - {"ColumnCompression", &PEGTransformerFactory::TransformColumnCompressionInternal}, - {"KeyActions", &PEGTransformerFactory::TransformKeyActionsInternal}, - {"UpdateAction", &PEGTransformerFactory::TransformUpdateActionInternal}, - {"DeleteAction", &PEGTransformerFactory::TransformDeleteActionInternal}, - {"KeyAction", &PEGTransformerFactory::TransformKeyActionInternal}, - {"NoKeyAction", &PEGTransformerFactory::TransformNoKeyActionInternal}, - {"RestrictKeyAction", &PEGTransformerFactory::TransformRestrictKeyActionInternal}, - {"CascadeKeyAction", &PEGTransformerFactory::TransformCascadeKeyActionInternal}, - {"SetNullKeyAction", &PEGTransformerFactory::TransformSetNullKeyActionInternal}, - {"SetDefaultKeyAction", &PEGTransformerFactory::TransformSetDefaultKeyActionInternal}, - {"TopLevelConstraint", &PEGTransformerFactory::TransformTopLevelConstraintInternal}, - {"TopLevelConstraintList", &PEGTransformerFactory::TransformTopLevelConstraintListInternal}, - {"TopCheckConstraint", &PEGTransformerFactory::TransformTopCheckConstraintInternal}, - {"TopPrimaryKeyConstraint", &PEGTransformerFactory::TransformTopPrimaryKeyConstraintInternal}, - {"TopUniqueConstraint", &PEGTransformerFactory::TransformTopUniqueConstraintInternal}, - {"TopForeignKeyConstraint", &PEGTransformerFactory::TransformTopForeignKeyConstraintInternal}, - {"ColumnIdList", &PEGTransformerFactory::TransformColumnIdListInternal}, - {"DottedIdentifier", &PEGTransformerFactory::TransformDottedIdentifierInternal}, - {"DotColLabel", &PEGTransformerFactory::TransformDotColLabelInternal}, - {"ColId", &PEGTransformerFactory::TransformColIdInternal}, - {"ColIdOrString", &PEGTransformerFactory::TransformColIdOrStringInternal}, - {"TypeFuncName", &PEGTransformerFactory::TransformTypeFuncNameInternal}, - {"TypeFuncKeyword", &PEGTransformerFactory::TransformTypeFuncKeywordInternal}, - {"ColLabel", &PEGTransformerFactory::TransformColLabelInternal}, - {"ColLabelOrString", &PEGTransformerFactory::TransformColLabelOrStringInternal}, - {"ColLabelIdentifier", &PEGTransformerFactory::TransformColLabelIdentifierInternal}, - {"StringLiteralIdentifier", &PEGTransformerFactory::TransformStringLiteralIdentifierInternal}, - {"GeneratedColumn", &PEGTransformerFactory::TransformGeneratedColumnInternal}, - {"GeneratedColumnType", &PEGTransformerFactory::TransformGeneratedColumnTypeInternal}, - {"CommitAction", &PEGTransformerFactory::TransformCommitActionInternal}, - {"PreserveOrDelete", &PEGTransformerFactory::TransformPreserveOrDeleteInternal}, - {"PreserveRows", &PEGTransformerFactory::TransformPreserveRowsInternal}, - {"DeleteRows", &PEGTransformerFactory::TransformDeleteRowsInternal}, - {"VirtualGeneratedColumn", &PEGTransformerFactory::TransformVirtualGeneratedColumnInternal}, - {"StoredGeneratedColumn", &PEGTransformerFactory::TransformStoredGeneratedColumnInternal}, - {"CreateTriggerStmt", &PEGTransformerFactory::TransformCreateTriggerStmtInternal}, - {"TriggerBody", &PEGTransformerFactory::TransformTriggerBodyInternal}, - {"TriggerName", &PEGTransformerFactory::TransformTriggerNameInternal}, - {"ReferencingClause", &PEGTransformerFactory::TransformReferencingClauseInternal}, - {"ReferencingItem", &PEGTransformerFactory::TransformReferencingItemInternal}, - {"ReferencingNewTableAs", &PEGTransformerFactory::TransformReferencingNewTableAsInternal}, - {"ReferencingOldTableAs", &PEGTransformerFactory::TransformReferencingOldTableAsInternal}, - {"TriggerTiming", &PEGTransformerFactory::TransformTriggerTimingInternal}, - {"TriggerBefore", &PEGTransformerFactory::TransformTriggerBeforeInternal}, - {"TriggerAfter", &PEGTransformerFactory::TransformTriggerAfterInternal}, - {"TriggerInsteadOf", &PEGTransformerFactory::TransformTriggerInsteadOfInternal}, - {"TriggerEvent", &PEGTransformerFactory::TransformTriggerEventInternal}, - {"TriggerEventInsert", &PEGTransformerFactory::TransformTriggerEventInsertInternal}, - {"TriggerEventDelete", &PEGTransformerFactory::TransformTriggerEventDeleteInternal}, - {"TriggerEventUpdate", &PEGTransformerFactory::TransformTriggerEventUpdateInternal}, - {"TriggerEventUpdateOf", &PEGTransformerFactory::TransformTriggerEventUpdateOfInternal}, - {"TriggerColumnList", &PEGTransformerFactory::TransformTriggerColumnListInternal}, - {"ForEachClause", &PEGTransformerFactory::TransformForEachClauseInternal}, - {"ForEachRow", &PEGTransformerFactory::TransformForEachRowInternal}, - {"ForEachStatement", &PEGTransformerFactory::TransformForEachStatementInternal}, - {"CreateTypeStmt", &PEGTransformerFactory::TransformCreateTypeStmtInternal}, - {"CreateType", &PEGTransformerFactory::TransformCreateTypeInternal}, - {"CreateTypeFromType", &PEGTransformerFactory::TransformCreateTypeFromTypeInternal}, - {"EnumSelectType", &PEGTransformerFactory::TransformEnumSelectTypeInternal}, - {"EnumStringLiteralList", &PEGTransformerFactory::TransformEnumStringLiteralListInternal}, - {"CreateViewStmt", &PEGTransformerFactory::TransformCreateViewStmtInternal}, - {"CreateRecursive", &PEGTransformerFactory::TransformCreateRecursiveInternal}, - {"CreateSecure", &PEGTransformerFactory::TransformCreateSecureInternal}, - {"DeallocateStatement", &PEGTransformerFactory::TransformDeallocateStatementInternal}, - {"DeallocatePrepare", &PEGTransformerFactory::TransformDeallocatePrepareInternal}, - {"DeleteStatement", &PEGTransformerFactory::TransformDeleteStatementInternal}, - {"TruncateStatement", &PEGTransformerFactory::TransformTruncateStatementInternal}, - {"TargetOptAlias", &PEGTransformerFactory::TransformTargetOptAliasInternal}, - {"DeleteUsingClause", &PEGTransformerFactory::TransformDeleteUsingClauseInternal}, - {"DescribeStatement", &PEGTransformerFactory::TransformDescribeStatementInternal}, - {"ShowDeprecatedSelect", &PEGTransformerFactory::TransformShowDeprecatedSelectInternal}, - {"DescribeSelect", &PEGTransformerFactory::TransformDescribeSelectInternal}, - {"ShowAllTables", &PEGTransformerFactory::TransformShowAllTablesInternal}, - {"ShowTables", &PEGTransformerFactory::TransformShowTablesInternal}, - {"ShowByName", &PEGTransformerFactory::TransformShowByNameInternal}, - {"DescribeByName", &PEGTransformerFactory::TransformDescribeByNameInternal}, - {"DescribeOrSummarize", &PEGTransformerFactory::TransformDescribeOrSummarizeInternal}, - {"ShowTarget", &PEGTransformerFactory::TransformShowTargetInternal}, - {"ShowDeprecatedQualifiedTableName", &PEGTransformerFactory::TransformShowDeprecatedQualifiedTableNameInternal}, - {"ShowSettingName", &PEGTransformerFactory::TransformShowSettingNameInternal}, - {"DescribeTarget", &PEGTransformerFactory::TransformDescribeTargetInternal}, - {"DescribeBaseTableName", &PEGTransformerFactory::TransformDescribeBaseTableNameInternal}, - {"DescribeStringLiteral", &PEGTransformerFactory::TransformDescribeStringLiteralInternal}, - {"Summarize", &PEGTransformerFactory::TransformSummarizeInternal}, - {"SummarizeRule", &PEGTransformerFactory::TransformSummarizeRuleInternal}, - {"ShowOrDescribe", &PEGTransformerFactory::TransformShowOrDescribeInternal}, - {"ShowRule", &PEGTransformerFactory::TransformShowRuleInternal}, - {"DescribeRule", &PEGTransformerFactory::TransformDescribeRuleInternal}, - {"DescribeLongRule", &PEGTransformerFactory::TransformDescribeLongRuleInternal}, - {"DescRule", &PEGTransformerFactory::TransformDescRuleInternal}, - {"DetachStatement", &PEGTransformerFactory::TransformDetachStatementInternal}, - {"DropStatement", &PEGTransformerFactory::TransformDropStatementInternal}, - {"DropEntries", &PEGTransformerFactory::TransformDropEntriesInternal}, - {"DropTrigger", &PEGTransformerFactory::TransformDropTriggerInternal}, - {"DropTable", &PEGTransformerFactory::TransformDropTableInternal}, - {"DropTableFunction", &PEGTransformerFactory::TransformDropTableFunctionInternal}, - {"DropFunction", &PEGTransformerFactory::TransformDropFunctionInternal}, - {"DropSchema", &PEGTransformerFactory::TransformDropSchemaInternal}, - {"DropIndex", &PEGTransformerFactory::TransformDropIndexInternal}, - {"QualifiedIndexName", &PEGTransformerFactory::TransformQualifiedIndexNameInternal}, - {"QualifiedIndexNameString", &PEGTransformerFactory::TransformQualifiedIndexNameStringInternal}, - {"SchemaReservedIndex", &PEGTransformerFactory::TransformSchemaReservedIndexInternal}, - {"CatalogReservedSchemaIndex", &PEGTransformerFactory::TransformCatalogReservedSchemaIndexInternal}, - {"DropSequence", &PEGTransformerFactory::TransformDropSequenceInternal}, - {"DropCollation", &PEGTransformerFactory::TransformDropCollationInternal}, - {"DropType", &PEGTransformerFactory::TransformDropTypeInternal}, - {"DropSecret", &PEGTransformerFactory::TransformDropSecretInternal}, - {"TableOrView", &PEGTransformerFactory::TransformTableOrViewInternal}, - {"MaterializedViewEntry", &PEGTransformerFactory::TransformMaterializedViewEntryInternal}, - {"FunctionTypeMacro", &PEGTransformerFactory::TransformFunctionTypeMacroInternal}, - {"FunctionTypeMacroKeyword", &PEGTransformerFactory::TransformFunctionTypeMacroKeywordInternal}, - {"FunctionTypeFunction", &PEGTransformerFactory::TransformFunctionTypeFunctionInternal}, - {"DropBehavior", &PEGTransformerFactory::TransformDropBehaviorInternal}, - {"CascadeDropBehavior", &PEGTransformerFactory::TransformCascadeDropBehaviorInternal}, - {"RestrictDropBehavior", &PEGTransformerFactory::TransformRestrictDropBehaviorInternal}, - {"IfExists", &PEGTransformerFactory::TransformIfExistsInternal}, - {"DropSecretStorage", &PEGTransformerFactory::TransformDropSecretStorageInternal}, - {"ExecuteStatement", &PEGTransformerFactory::TransformExecuteStatementInternal}, - {"ExplainStatement", &PEGTransformerFactory::TransformExplainStatementInternal}, - {"ExplainOptionList", &PEGTransformerFactory::TransformExplainOptionListInternal}, - {"ExplainOption", &PEGTransformerFactory::TransformExplainOptionInternal}, - {"ExplainOptionName", &PEGTransformerFactory::TransformExplainOptionNameInternal}, - {"ExplainSelectStatement", &PEGTransformerFactory::TransformExplainSelectStatementInternal}, - {"ExplainableStatements", &PEGTransformerFactory::TransformExplainableStatementsInternal}, - {"ExportStatement", &PEGTransformerFactory::TransformExportStatementInternal}, - {"ExportSource", &PEGTransformerFactory::TransformExportSourceInternal}, - {"ImportStatement", &PEGTransformerFactory::TransformImportStatementInternal}, - {"ColumnReference", &PEGTransformerFactory::TransformColumnReferenceInternal}, - {"NestedSchemaTableColumnName", &PEGTransformerFactory::TransformNestedSchemaTableColumnNameInternal}, - {"CatalogReservedSchemaTableColumnName", - &PEGTransformerFactory::TransformCatalogReservedSchemaTableColumnNameInternal}, - {"SchemaReservedTableColumnName", &PEGTransformerFactory::TransformSchemaReservedTableColumnNameInternal}, - {"TableReservedColumnName", &PEGTransformerFactory::TransformTableReservedColumnNameInternal}, - {"FunctionExpression", &PEGTransformerFactory::TransformFunctionExpressionInternal}, - {"FunctionExpressionArguments", &PEGTransformerFactory::TransformFunctionExpressionArgumentsInternal}, - {"FunctionExpressionArgumentList", &PEGTransformerFactory::TransformFunctionExpressionArgumentListInternal}, - {"FunctionArgumentList", &PEGTransformerFactory::TransformFunctionArgumentListInternal}, - {"FunctionIdentifier", &PEGTransformerFactory::TransformFunctionIdentifierInternal}, - {"FunctionNameAsQualifiedName", &PEGTransformerFactory::TransformFunctionNameAsQualifiedNameInternal}, - {"CatalogReservedSchemaFunctionName", - &PEGTransformerFactory::TransformCatalogReservedSchemaFunctionNameInternal}, - {"SchemaReservedFunctionName", &PEGTransformerFactory::TransformSchemaReservedFunctionNameInternal}, - {"DistinctOrAll", &PEGTransformerFactory::TransformDistinctOrAllInternal}, - {"DistinctKeyword", &PEGTransformerFactory::TransformDistinctKeywordInternal}, - {"AllKeyword", &PEGTransformerFactory::TransformAllKeywordInternal}, - {"WithinGroupClause", &PEGTransformerFactory::TransformWithinGroupClauseInternal}, - {"FilterClause", &PEGTransformerFactory::TransformFilterClauseInternal}, - {"FilterClauseExpression", &PEGTransformerFactory::TransformFilterClauseExpressionInternal}, - {"FilterClauseContents", &PEGTransformerFactory::TransformFilterClauseContentsInternal}, - {"IgnoreOrRespectNulls", &PEGTransformerFactory::TransformIgnoreOrRespectNullsInternal}, - {"IgnoreNulls", &PEGTransformerFactory::TransformIgnoreNullsInternal}, - {"RespectNulls", &PEGTransformerFactory::TransformRespectNullsInternal}, - {"ParenthesisExpression", &PEGTransformerFactory::TransformParenthesisExpressionInternal}, - {"LiteralExpression", &PEGTransformerFactory::TransformLiteralExpressionInternal}, - {"ConstantLiteral", &PEGTransformerFactory::TransformConstantLiteralInternal}, - {"NullLiteral", &PEGTransformerFactory::TransformNullLiteralInternal}, - {"TrueLiteral", &PEGTransformerFactory::TransformTrueLiteralInternal}, - {"FalseLiteral", &PEGTransformerFactory::TransformFalseLiteralInternal}, - {"CastExpression", &PEGTransformerFactory::TransformCastExpressionInternal}, - {"CastArguments", &PEGTransformerFactory::TransformCastArgumentsInternal}, - {"CastOrTryCast", &PEGTransformerFactory::TransformCastOrTryCastInternal}, - {"CastKeyword", &PEGTransformerFactory::TransformCastKeywordInternal}, - {"TryCastKeyword", &PEGTransformerFactory::TransformTryCastKeywordInternal}, - {"ColIdDot", &PEGTransformerFactory::TransformColIdDotInternal}, - {"StarExpression", &PEGTransformerFactory::TransformStarExpressionInternal}, - {"StarQualifierList", &PEGTransformerFactory::TransformStarQualifierListInternal}, - {"ExcludeList", &PEGTransformerFactory::TransformExcludeListInternal}, - {"ExcludeNames", &PEGTransformerFactory::TransformExcludeNamesInternal}, - {"ExcludeNameList", &PEGTransformerFactory::TransformExcludeNameListInternal}, - {"ExcludeNameSingle", &PEGTransformerFactory::TransformExcludeNameSingleInternal}, - {"ExcludeName", &PEGTransformerFactory::TransformExcludeNameInternal}, - {"ExcludeDottedName", &PEGTransformerFactory::TransformExcludeDottedNameInternal}, - {"ExcludeColumnName", &PEGTransformerFactory::TransformExcludeColumnNameInternal}, - {"ReplaceList", &PEGTransformerFactory::TransformReplaceListInternal}, - {"ReplaceEntries", &PEGTransformerFactory::TransformReplaceEntriesInternal}, - {"ReplaceEntrySingle", &PEGTransformerFactory::TransformReplaceEntrySingleInternal}, - {"ReplaceEntryList", &PEGTransformerFactory::TransformReplaceEntryListInternal}, - {"ReplaceEntry", &PEGTransformerFactory::TransformReplaceEntryInternal}, - {"RenameList", &PEGTransformerFactory::TransformRenameListInternal}, - {"RenameEntries", &PEGTransformerFactory::TransformRenameEntriesInternal}, - {"RenameEntryList", &PEGTransformerFactory::TransformRenameEntryListInternal}, - {"SingleRenameEntry", &PEGTransformerFactory::TransformSingleRenameEntryInternal}, - {"RenameEntry", &PEGTransformerFactory::TransformRenameEntryInternal}, - {"SubqueryExpression", &PEGTransformerFactory::TransformSubqueryExpressionInternal}, - {"SubqueryNot", &PEGTransformerFactory::TransformSubqueryNotInternal}, - {"SubqueryExists", &PEGTransformerFactory::TransformSubqueryExistsInternal}, - {"CaseExpression", &PEGTransformerFactory::TransformCaseExpressionInternal}, - {"CaseWhenThen", &PEGTransformerFactory::TransformCaseWhenThenInternal}, - {"CaseElse", &PEGTransformerFactory::TransformCaseElseInternal}, - {"TypeLiteral", &PEGTransformerFactory::TransformTypeLiteralInternal}, - {"IntervalLiteral", &PEGTransformerFactory::TransformIntervalLiteralInternal}, - {"IntervalParameter", &PEGTransformerFactory::TransformIntervalParameterInternal}, - {"IntervalStringParameter", &PEGTransformerFactory::TransformIntervalStringParameterInternal}, - {"FrameClause", &PEGTransformerFactory::TransformFrameClauseInternal}, - {"Framing", &PEGTransformerFactory::TransformFramingInternal}, - {"RowsFraming", &PEGTransformerFactory::TransformRowsFramingInternal}, - {"RangeFraming", &PEGTransformerFactory::TransformRangeFramingInternal}, - {"GroupsFraming", &PEGTransformerFactory::TransformGroupsFramingInternal}, - {"FrameExtent", &PEGTransformerFactory::TransformFrameExtentInternal}, - {"SingleFrameExtent", &PEGTransformerFactory::TransformSingleFrameExtentInternal}, - {"BetweenFrameExtent", &PEGTransformerFactory::TransformBetweenFrameExtentInternal}, - {"FrameBound", &PEGTransformerFactory::TransformFrameBoundInternal}, - {"FrameUnbounded", &PEGTransformerFactory::TransformFrameUnboundedInternal}, - {"FrameExpression", &PEGTransformerFactory::TransformFrameExpressionInternal}, - {"FrameCurrentRow", &PEGTransformerFactory::TransformFrameCurrentRowInternal}, - {"PrecedingOrFollowing", &PEGTransformerFactory::TransformPrecedingOrFollowingInternal}, - {"PrecedingFrame", &PEGTransformerFactory::TransformPrecedingFrameInternal}, - {"FollowingFrame", &PEGTransformerFactory::TransformFollowingFrameInternal}, - {"WindowExcludeClause", &PEGTransformerFactory::TransformWindowExcludeClauseInternal}, - {"WindowExcludeElement", &PEGTransformerFactory::TransformWindowExcludeElementInternal}, - {"ExcludeCurrentRow", &PEGTransformerFactory::TransformExcludeCurrentRowInternal}, - {"ExcludeGroup", &PEGTransformerFactory::TransformExcludeGroupInternal}, - {"ExcludeTies", &PEGTransformerFactory::TransformExcludeTiesInternal}, - {"ExcludeNoOthers", &PEGTransformerFactory::TransformExcludeNoOthersInternal}, - {"WindowFrame", &PEGTransformerFactory::TransformWindowFrameInternal}, - {"IdentifierWindowFrame", &PEGTransformerFactory::TransformIdentifierWindowFrameInternal}, - {"ParensIdentifier", &PEGTransformerFactory::TransformParensIdentifierInternal}, - {"WindowFrameDefinition", &PEGTransformerFactory::TransformWindowFrameDefinitionInternal}, - {"WindowFrameNameContentsParens", &PEGTransformerFactory::TransformWindowFrameNameContentsParensInternal}, - {"WindowFrameNameContents", &PEGTransformerFactory::TransformWindowFrameNameContentsInternal}, - {"WindowFrameContentsParens", &PEGTransformerFactory::TransformWindowFrameContentsParensInternal}, - {"WindowFrameContents", &PEGTransformerFactory::TransformWindowFrameContentsInternal}, - {"BaseWindowName", &PEGTransformerFactory::TransformBaseWindowNameInternal}, - {"WindowPartition", &PEGTransformerFactory::TransformWindowPartitionInternal}, - {"ListExpression", &PEGTransformerFactory::TransformListExpressionInternal}, - {"ArrayBoundedListExpression", &PEGTransformerFactory::TransformArrayBoundedListExpressionInternal}, - {"ArrayParensSelect", &PEGTransformerFactory::TransformArrayParensSelectInternal}, - {"BoundedListExpression", &PEGTransformerFactory::TransformBoundedListExpressionInternal}, - {"StructExpression", &PEGTransformerFactory::TransformStructExpressionInternal}, - {"StructField", &PEGTransformerFactory::TransformStructFieldInternal}, - {"MapExpression", &PEGTransformerFactory::TransformMapExpressionInternal}, - {"MapStructExpression", &PEGTransformerFactory::TransformMapStructExpressionInternal}, - {"MapStructField", &PEGTransformerFactory::TransformMapStructFieldInternal}, - {"GroupingExpression", &PEGTransformerFactory::TransformGroupingExpressionInternal}, - {"GroupingOrGroupingId", &PEGTransformerFactory::TransformGroupingOrGroupingIdInternal}, - {"GroupingKeyword", &PEGTransformerFactory::TransformGroupingKeywordInternal}, - {"GroupingIdKeyword", &PEGTransformerFactory::TransformGroupingIdKeywordInternal}, - {"Parameter", &PEGTransformerFactory::TransformParameterInternal}, - {"QuestionMarkNumberedParameter", &PEGTransformerFactory::TransformQuestionMarkNumberedParameterInternal}, - {"AnonymousParameter", &PEGTransformerFactory::TransformAnonymousParameterInternal}, - {"NumberedParameter", &PEGTransformerFactory::TransformNumberedParameterInternal}, - {"ColLabelParameter", &PEGTransformerFactory::TransformColLabelParameterInternal}, - {"PositionalExpression", &PEGTransformerFactory::TransformPositionalExpressionInternal}, - {"DefaultExpression", &PEGTransformerFactory::TransformDefaultExpressionInternal}, - {"ListComprehensionExpression", &PEGTransformerFactory::TransformListComprehensionExpressionInternal}, - {"ListComprehensionFilter", &PEGTransformerFactory::TransformListComprehensionFilterInternal}, - {"ParensExpression", &PEGTransformerFactory::TransformParensExpressionInternal}, - {"MeasureSemanticsExpression", &PEGTransformerFactory::TransformMeasureSemanticsExpressionInternal}, - {"SingleExpression", &PEGTransformerFactory::TransformSingleExpressionInternal}, - {"Expression", &PEGTransformerFactory::TransformExpressionInternal}, - {"ColumnDefaultExpr", &PEGTransformerFactory::TransformColumnDefaultExprInternal}, - {"LambdaArrowExpression", &PEGTransformerFactory::TransformLambdaArrowExpressionInternal}, - {"SingleArrowPair", &PEGTransformerFactory::TransformSingleArrowPairInternal}, - {"LogicalOrExpression", &PEGTransformerFactory::TransformLogicalOrExpressionInternal}, - {"LogicalOrExpressionTail", &PEGTransformerFactory::TransformLogicalOrExpressionTailInternal}, - {"ColDefOrExpr", &PEGTransformerFactory::TransformColDefOrExprInternal}, - {"ColDefOrExpressionTail", &PEGTransformerFactory::TransformColDefOrExpressionTailInternal}, - {"LogicalAndExpression", &PEGTransformerFactory::TransformLogicalAndExpressionInternal}, - {"LogicalAndExpressionTail", &PEGTransformerFactory::TransformLogicalAndExpressionTailInternal}, - {"ColDefAndExpr", &PEGTransformerFactory::TransformColDefAndExprInternal}, - {"ColDefAndExpressionTail", &PEGTransformerFactory::TransformColDefAndExpressionTailInternal}, - {"LogicalNotExpression", &PEGTransformerFactory::TransformLogicalNotExpressionInternal}, - {"NotExpression", &PEGTransformerFactory::TransformNotExpressionInternal}, - {"NotKeyword", &PEGTransformerFactory::TransformNotKeywordInternal}, - {"IsExpression", &PEGTransformerFactory::TransformIsExpressionInternal}, - {"IsTest", &PEGTransformerFactory::TransformIsTestInternal}, - {"IsLiteral", &PEGTransformerFactory::TransformIsLiteralInternal}, - {"IsLiteralValue", &PEGTransformerFactory::TransformIsLiteralValueInternal}, - {"UnknownLiteral", &PEGTransformerFactory::TransformUnknownLiteralInternal}, - {"NotNull", &PEGTransformerFactory::TransformNotNullInternal}, - {"NotNullKeyword", &PEGTransformerFactory::TransformNotNullKeywordInternal}, - {"NotNullOperator", &PEGTransformerFactory::TransformNotNullOperatorInternal}, - {"IsNull", &PEGTransformerFactory::TransformIsNullInternal}, - {"IsNullOperator", &PEGTransformerFactory::TransformIsNullOperatorInternal}, - {"IsDistinctFromExpression", &PEGTransformerFactory::TransformIsDistinctFromExpressionInternal}, - {"IsDistinctFromTail", &PEGTransformerFactory::TransformIsDistinctFromTailInternal}, - {"IsDistinctFromOp", &PEGTransformerFactory::TransformIsDistinctFromOpInternal}, - {"ComparisonExpression", &PEGTransformerFactory::TransformComparisonExpressionInternal}, - {"ComparisonExpressionTail", &PEGTransformerFactory::TransformComparisonExpressionTailInternal}, - {"ComparisonOperator", &PEGTransformerFactory::TransformComparisonOperatorInternal}, - {"OperatorEqual", &PEGTransformerFactory::TransformOperatorEqualInternal}, - {"OperatorNotEqual", &PEGTransformerFactory::TransformOperatorNotEqualInternal}, - {"OperatorLessThan", &PEGTransformerFactory::TransformOperatorLessThanInternal}, - {"OperatorGreaterThan", &PEGTransformerFactory::TransformOperatorGreaterThanInternal}, - {"OperatorLessThanEquals", &PEGTransformerFactory::TransformOperatorLessThanEqualsInternal}, - {"OperatorGreaterThanEquals", &PEGTransformerFactory::TransformOperatorGreaterThanEqualsInternal}, - {"BetweenInLikeExpression", &PEGTransformerFactory::TransformBetweenInLikeExpressionInternal}, - {"BetweenInLikeOp", &PEGTransformerFactory::TransformBetweenInLikeOpInternal}, - {"BetweenInLikeOpExpression", &PEGTransformerFactory::TransformBetweenInLikeOpExpressionInternal}, - {"LikeClause", &PEGTransformerFactory::TransformLikeClauseInternal}, - {"EscapeClause", &PEGTransformerFactory::TransformEscapeClauseInternal}, - {"LikeVariations", &PEGTransformerFactory::TransformLikeVariationsInternal}, - {"LikeToken", &PEGTransformerFactory::TransformLikeTokenInternal}, - {"ILikeToken", &PEGTransformerFactory::TransformILikeTokenInternal}, - {"GlobToken", &PEGTransformerFactory::TransformGlobTokenInternal}, - {"SimilarToToken", &PEGTransformerFactory::TransformSimilarToTokenInternal}, - {"RegexMatchToken", &PEGTransformerFactory::TransformRegexMatchTokenInternal}, - {"RegexInsensitiveMatchToken", &PEGTransformerFactory::TransformRegexInsensitiveMatchTokenInternal}, - {"NotILikeOp", &PEGTransformerFactory::TransformNotILikeOpInternal}, - {"NotLikeOp", &PEGTransformerFactory::TransformNotLikeOpInternal}, - {"NotRegexInsensitiveMatchOp", &PEGTransformerFactory::TransformNotRegexInsensitiveMatchOpInternal}, - {"NotSimilarToOp", &PEGTransformerFactory::TransformNotSimilarToOpInternal}, - {"InClause", &PEGTransformerFactory::TransformInClauseInternal}, - {"InExpression", &PEGTransformerFactory::TransformInExpressionInternal}, - {"InContainsExpression", &PEGTransformerFactory::TransformInContainsExpressionInternal}, - {"InExpressionList", &PEGTransformerFactory::TransformInExpressionListInternal}, - {"InSelectStatement", &PEGTransformerFactory::TransformInSelectStatementInternal}, - {"BetweenClause", &PEGTransformerFactory::TransformBetweenClauseInternal}, - {"OtherOperatorExpression", &PEGTransformerFactory::TransformOtherOperatorExpressionInternal}, - {"OtherOperatorTail", &PEGTransformerFactory::TransformOtherOperatorTailInternal}, - {"OtherOperator", &PEGTransformerFactory::TransformOtherOperatorInternal}, - {"AnyAllParsedOperator", &PEGTransformerFactory::TransformAnyAllParsedOperatorInternal}, - {"NamedOtherOperator", &PEGTransformerFactory::TransformNamedOtherOperatorInternal}, - {"AnyAllOperator", &PEGTransformerFactory::TransformAnyAllOperatorInternal}, - {"AnyOrAll", &PEGTransformerFactory::TransformAnyOrAllInternal}, - {"SubqueryAny", &PEGTransformerFactory::TransformSubqueryAnyInternal}, - {"SubqueryAll", &PEGTransformerFactory::TransformSubqueryAllInternal}, - {"InetOperator", &PEGTransformerFactory::TransformInetOperatorInternal}, - {"JsonOperator", &PEGTransformerFactory::TransformJsonOperatorInternal}, - {"ListOperator", &PEGTransformerFactory::TransformListOperatorInternal}, - {"StringOperator", &PEGTransformerFactory::TransformStringOperatorInternal}, - {"QualifiedOperator", &PEGTransformerFactory::TransformQualifiedOperatorInternal}, - {"QualifiedOperatorContents", &PEGTransformerFactory::TransformQualifiedOperatorContentsInternal}, - {"AnyOp", &PEGTransformerFactory::TransformAnyOpInternal}, - {"BitwiseExpression", &PEGTransformerFactory::TransformBitwiseExpressionInternal}, - {"BitwiseExpressionTail", &PEGTransformerFactory::TransformBitwiseExpressionTailInternal}, - {"BitOperator", &PEGTransformerFactory::TransformBitOperatorInternal}, - {"AdditiveExpression", &PEGTransformerFactory::TransformAdditiveExpressionInternal}, - {"AdditiveExpressionTail", &PEGTransformerFactory::TransformAdditiveExpressionTailInternal}, - {"Term", &PEGTransformerFactory::TransformTermInternal}, - {"MultiplicativeExpression", &PEGTransformerFactory::TransformMultiplicativeExpressionInternal}, - {"MultiplicativeExpressionTail", &PEGTransformerFactory::TransformMultiplicativeExpressionTailInternal}, - {"Factor", &PEGTransformerFactory::TransformFactorInternal}, - {"ExponentiationExpression", &PEGTransformerFactory::TransformExponentiationExpressionInternal}, - {"ExponentiationExpressionTail", &PEGTransformerFactory::TransformExponentiationExpressionTailInternal}, - {"ExponentOperator", &PEGTransformerFactory::TransformExponentOperatorInternal}, - {"CollateExpression", &PEGTransformerFactory::TransformCollateExpressionInternal}, - {"CollateExpressionTail", &PEGTransformerFactory::TransformCollateExpressionTailInternal}, - {"AtTimeZoneExpression", &PEGTransformerFactory::TransformAtTimeZoneExpressionInternal}, - {"AtTimeZoneExpressionTail", &PEGTransformerFactory::TransformAtTimeZoneExpressionTailInternal}, - {"PrefixOperator", &PEGTransformerFactory::TransformPrefixOperatorInternal}, - {"MinusPrefixOperator", &PEGTransformerFactory::TransformMinusPrefixOperatorInternal}, - {"PlusPrefixOperator", &PEGTransformerFactory::TransformPlusPrefixOperatorInternal}, - {"TildePrefixOperator", &PEGTransformerFactory::TransformTildePrefixOperatorInternal}, - {"BaseExpression", &PEGTransformerFactory::TransformBaseExpressionInternal}, - {"IndirectionList", &PEGTransformerFactory::TransformIndirectionListInternal}, - {"Indirection", &PEGTransformerFactory::TransformIndirectionInternal}, - {"CastOperator", &PEGTransformerFactory::TransformCastOperatorInternal}, - {"DotOperator", &PEGTransformerFactory::TransformDotOperatorInternal}, - {"DotMethodOperator", &PEGTransformerFactory::TransformDotMethodOperatorInternal}, - {"DotColumnOperator", &PEGTransformerFactory::TransformDotColumnOperatorInternal}, - {"MethodExpression", &PEGTransformerFactory::TransformMethodExpressionInternal}, - {"MethodExpressionArguments", &PEGTransformerFactory::TransformMethodExpressionArgumentsInternal}, - {"MethodExpressionArgumentList", &PEGTransformerFactory::TransformMethodExpressionArgumentListInternal}, - {"MethodFunctionArguments", &PEGTransformerFactory::TransformMethodFunctionArgumentsInternal}, - {"SliceExpression", &PEGTransformerFactory::TransformSliceExpressionInternal}, - {"SliceBound", &PEGTransformerFactory::TransformSliceBoundInternal}, - {"EndSliceBound", &PEGTransformerFactory::TransformEndSliceBoundInternal}, - {"EndSliceValue", &PEGTransformerFactory::TransformEndSliceValueInternal}, - {"EndSliceMinus", &PEGTransformerFactory::TransformEndSliceMinusInternal}, - {"StepSliceBound", &PEGTransformerFactory::TransformStepSliceBoundInternal}, - {"PostfixOperator", &PEGTransformerFactory::TransformPostfixOperatorInternal}, - {"SpecialFunctionExpression", &PEGTransformerFactory::TransformSpecialFunctionExpressionInternal}, - {"CoalesceExpression", &PEGTransformerFactory::TransformCoalesceExpressionInternal}, - {"UnpackExpression", &PEGTransformerFactory::TransformUnpackExpressionInternal}, - {"TryExpression", &PEGTransformerFactory::TransformTryExpressionInternal}, - {"ColumnsExpression", &PEGTransformerFactory::TransformColumnsExpressionInternal}, - {"ExtractExpression", &PEGTransformerFactory::TransformExtractExpressionInternal}, - {"ExtractArguments", &PEGTransformerFactory::TransformExtractArgumentsInternal}, - {"LambdaExpression", &PEGTransformerFactory::TransformLambdaExpressionInternal}, - {"NullIfExpression", &PEGTransformerFactory::TransformNullIfExpressionInternal}, - {"NullIfArguments", &PEGTransformerFactory::TransformNullIfArgumentsInternal}, - {"PositionExpression", &PEGTransformerFactory::TransformPositionExpressionInternal}, - {"PositionArguments", &PEGTransformerFactory::TransformPositionArgumentsInternal}, - {"RowExpression", &PEGTransformerFactory::TransformRowExpressionInternal}, - {"SubstringExpression", &PEGTransformerFactory::TransformSubstringExpressionInternal}, - {"SubstringArguments", &PEGTransformerFactory::TransformSubstringArgumentsInternal}, - {"SubstringExpressionList", &PEGTransformerFactory::TransformSubstringExpressionListInternal}, - {"SubstringParameters", &PEGTransformerFactory::TransformSubstringParametersInternal}, - {"SubstringFromFor", &PEGTransformerFactory::TransformSubstringFromForInternal}, - {"SubstringFromOptionalFor", &PEGTransformerFactory::TransformSubstringFromOptionalForInternal}, - {"SubstringFor", &PEGTransformerFactory::TransformSubstringForInternal}, - {"TrimExpression", &PEGTransformerFactory::TransformTrimExpressionInternal}, - {"TrimArguments", &PEGTransformerFactory::TransformTrimArgumentsInternal}, - {"TrimDirection", &PEGTransformerFactory::TransformTrimDirectionInternal}, - {"TrimBoth", &PEGTransformerFactory::TransformTrimBothInternal}, - {"TrimLeading", &PEGTransformerFactory::TransformTrimLeadingInternal}, - {"TrimTrailing", &PEGTransformerFactory::TransformTrimTrailingInternal}, - {"TrimSource", &PEGTransformerFactory::TransformTrimSourceInternal}, - {"OverlayExpression", &PEGTransformerFactory::TransformOverlayExpressionInternal}, - {"OverlayArguments", &PEGTransformerFactory::TransformOverlayArgumentsInternal}, - {"OverlayParameters", &PEGTransformerFactory::TransformOverlayParametersInternal}, - {"FromExpression", &PEGTransformerFactory::TransformFromExpressionInternal}, - {"ForExpression", &PEGTransformerFactory::TransformForExpressionInternal}, - {"OverlayExpressionList", &PEGTransformerFactory::TransformOverlayExpressionListInternal}, - {"ExtractArgument", &PEGTransformerFactory::TransformExtractArgumentInternal}, - {"ExtractDatePartArgument", &PEGTransformerFactory::TransformExtractDatePartArgumentInternal}, - {"ExtractIdentifierArgument", &PEGTransformerFactory::TransformExtractIdentifierArgumentInternal}, - {"ExtractStringArgument", &PEGTransformerFactory::TransformExtractStringArgumentInternal}, - {"ExtractDatePart", &PEGTransformerFactory::TransformExtractDatePartInternal}, - {"ExternalResourceStatement", &PEGTransformerFactory::TransformExternalResourceStatementInternal}, - {"CreateExternalResourceStmt", &PEGTransformerFactory::TransformCreateExternalResourceStmtInternal}, - {"RegisterExternalResourceStmt", &PEGTransformerFactory::TransformRegisterExternalResourceStmtInternal}, - {"DestroyExternalResourceStmt", &PEGTransformerFactory::TransformDestroyExternalResourceStmtInternal}, - {"ShowExternalResourcesStmt", &PEGTransformerFactory::TransformShowExternalResourcesStmtInternal}, - {"ShowAllModifier", &PEGTransformerFactory::TransformShowAllModifierInternal}, - {"ExternalResourceCreationOptions", &PEGTransformerFactory::TransformExternalResourceCreationOptionsInternal}, - {"InsertStatement", &PEGTransformerFactory::TransformInsertStatementInternal}, - {"OrAction", &PEGTransformerFactory::TransformOrActionInternal}, - {"InsertOrReplace", &PEGTransformerFactory::TransformInsertOrReplaceInternal}, - {"InsertOrIgnore", &PEGTransformerFactory::TransformInsertOrIgnoreInternal}, - {"ByNameOrPosition", &PEGTransformerFactory::TransformByNameOrPositionInternal}, - {"InsertByNameOrder", &PEGTransformerFactory::TransformInsertByNameOrderInternal}, - {"InsertByPositionOrder", &PEGTransformerFactory::TransformInsertByPositionOrderInternal}, - {"InsertByName", &PEGTransformerFactory::TransformInsertByNameInternal}, - {"InsertByPosition", &PEGTransformerFactory::TransformInsertByPositionInternal}, - {"InsertTarget", &PEGTransformerFactory::TransformInsertTargetInternal}, - {"InsertAlias", &PEGTransformerFactory::TransformInsertAliasInternal}, - {"ColumnList", &PEGTransformerFactory::TransformColumnListInternal}, - {"InsertColumnList", &PEGTransformerFactory::TransformInsertColumnListInternal}, - {"InsertValues", &PEGTransformerFactory::TransformInsertValuesInternal}, - {"SelectInsertValues", &PEGTransformerFactory::TransformSelectInsertValuesInternal}, - {"DefaultValues", &PEGTransformerFactory::TransformDefaultValuesInternal}, - {"OnConflictClause", &PEGTransformerFactory::TransformOnConflictClauseInternal}, - {"OnConflictTarget", &PEGTransformerFactory::TransformOnConflictTargetInternal}, - {"OnConflictExpressionTarget", &PEGTransformerFactory::TransformOnConflictExpressionTargetInternal}, - {"OnConflictIndexTarget", &PEGTransformerFactory::TransformOnConflictIndexTargetInternal}, - {"OnConflictAction", &PEGTransformerFactory::TransformOnConflictActionInternal}, - {"OnConflictUpdate", &PEGTransformerFactory::TransformOnConflictUpdateInternal}, - {"OnConflictNothing", &PEGTransformerFactory::TransformOnConflictNothingInternal}, - {"ReturningClause", &PEGTransformerFactory::TransformReturningClauseInternal}, - {"LoadStatement", &PEGTransformerFactory::TransformLoadStatementInternal}, - {"ExtensionAlias", &PEGTransformerFactory::TransformExtensionAliasInternal}, - {"InstallStatement", &PEGTransformerFactory::TransformInstallStatementInternal}, - {"InstallAndLoad", &PEGTransformerFactory::TransformInstallAndLoadInternal}, - {"UpdateExtensionsStatement", &PEGTransformerFactory::TransformUpdateExtensionsStatementInternal}, - {"FromSource", &PEGTransformerFactory::TransformFromSourceInternal}, - {"FromSourceIdentifier", &PEGTransformerFactory::TransformFromSourceIdentifierInternal}, - {"FromSourceString", &PEGTransformerFactory::TransformFromSourceStringInternal}, - {"VersionNumber", &PEGTransformerFactory::TransformVersionNumberInternal}, - {"ExtensionRepositoryStatement", &PEGTransformerFactory::TransformExtensionRepositoryStatementInternal}, - {"CreateExtensionRepositoryStmt", &PEGTransformerFactory::TransformCreateExtensionRepositoryStmtInternal}, - {"RepositoryPrefix", &PEGTransformerFactory::TransformRepositoryPrefixInternal}, - {"RepositoryPublicKey", &PEGTransformerFactory::TransformRepositoryPublicKeyInternal}, - {"DropExtensionRepositoryStmt", &PEGTransformerFactory::TransformDropExtensionRepositoryStmtInternal}, - {"TableMatchRecognizeClause", &PEGTransformerFactory::TransformTableMatchRecognizeClauseInternal}, - {"MatchRecognizeBody", &PEGTransformerFactory::TransformMatchRecognizeBodyInternal}, - {"MatchRecognizeClause", &PEGTransformerFactory::TransformMatchRecognizeClauseInternal}, - {"MRPartition", &PEGTransformerFactory::TransformMRPartitionInternal}, - {"MROrderBy", &PEGTransformerFactory::TransformMROrderByInternal}, - {"MRMeasures", &PEGTransformerFactory::TransformMRMeasuresInternal}, - {"MRRows", &PEGTransformerFactory::TransformMRRowsInternal}, - {"MRSkip", &PEGTransformerFactory::TransformMRSkipInternal}, - {"MRPattern", &PEGTransformerFactory::TransformMRPatternInternal}, - {"MRSubset", &PEGTransformerFactory::TransformMRSubsetInternal}, - {"MRDefineAuto", &PEGTransformerFactory::TransformMRDefineAutoInternal}, - {"MRDefine", &PEGTransformerFactory::TransformMRDefineInternal}, - {"MeasuresClause", &PEGTransformerFactory::TransformMeasuresClauseInternal}, - {"MeasuresElement", &PEGTransformerFactory::TransformMeasuresElementInternal}, - {"MeasureSemantics", &PEGTransformerFactory::TransformMeasureSemanticsInternal}, - {"RunningSemantics", &PEGTransformerFactory::TransformRunningSemanticsInternal}, - {"FinalSemantics", &PEGTransformerFactory::TransformFinalSemanticsInternal}, - {"RowsPerMatch", &PEGTransformerFactory::TransformRowsPerMatchInternal}, - {"OneRowPerMatch", &PEGTransformerFactory::TransformOneRowPerMatchInternal}, - {"AllRowsPerMatch", &PEGTransformerFactory::TransformAllRowsPerMatchInternal}, - {"AllRowsEmptyMatches", &PEGTransformerFactory::TransformAllRowsEmptyMatchesInternal}, - {"ShowEmptyMatches", &PEGTransformerFactory::TransformShowEmptyMatchesInternal}, - {"OmitEmptyMatches", &PEGTransformerFactory::TransformOmitEmptyMatchesInternal}, - {"WithUnmatchedRows", &PEGTransformerFactory::TransformWithUnmatchedRowsInternal}, - {"AfterMatchSkip", &PEGTransformerFactory::TransformAfterMatchSkipInternal}, - {"AfterMatchSkipTarget", &PEGTransformerFactory::TransformAfterMatchSkipTargetInternal}, - {"SkipToNextRow", &PEGTransformerFactory::TransformSkipToNextRowInternal}, - {"SkipPastLastRow", &PEGTransformerFactory::TransformSkipPastLastRowInternal}, - {"SkipToFirstVar", &PEGTransformerFactory::TransformSkipToFirstVarInternal}, - {"SkipToLastVar", &PEGTransformerFactory::TransformSkipToLastVarInternal}, - {"SkipToVar", &PEGTransformerFactory::TransformSkipToVarInternal}, - {"PatternClause", &PEGTransformerFactory::TransformPatternClauseInternal}, - {"RowPattern", &PEGTransformerFactory::TransformRowPatternInternal}, - {"RowPatternAlternative", &PEGTransformerFactory::TransformRowPatternAlternativeInternal}, - {"RowPatternTerm", &PEGTransformerFactory::TransformRowPatternTermInternal}, - {"RowPatternFactor", &PEGTransformerFactory::TransformRowPatternFactorInternal}, - {"RowPatternPrimary", &PEGTransformerFactory::TransformRowPatternPrimaryInternal}, - {"RowPatternPermute", &PEGTransformerFactory::TransformRowPatternPermuteInternal}, - {"RowPatternAnchor", &PEGTransformerFactory::TransformRowPatternAnchorInternal}, - {"PatternStart", &PEGTransformerFactory::TransformPatternStartInternal}, - {"PatternEnd", &PEGTransformerFactory::TransformPatternEndInternal}, - {"RowPatternGroup", &PEGTransformerFactory::TransformRowPatternGroupInternal}, - {"RowPatternEmpty", &PEGTransformerFactory::TransformRowPatternEmptyInternal}, - {"RowPatternExclusion", &PEGTransformerFactory::TransformRowPatternExclusionInternal}, - {"RowPatternLabel", &PEGTransformerFactory::TransformRowPatternLabelInternal}, - {"RowPatternQuantifier", &PEGTransformerFactory::TransformRowPatternQuantifierInternal}, - {"RowPatternQuantifierKind", &PEGTransformerFactory::TransformRowPatternQuantifierKindInternal}, - {"QuantifierReluctant", &PEGTransformerFactory::TransformQuantifierReluctantInternal}, - {"QuantifierStar", &PEGTransformerFactory::TransformQuantifierStarInternal}, - {"QuantifierPlus", &PEGTransformerFactory::TransformQuantifierPlusInternal}, - {"QuantifierOptional", &PEGTransformerFactory::TransformQuantifierOptionalInternal}, - {"QuantifierRange", &PEGTransformerFactory::TransformQuantifierRangeInternal}, - {"QuantifierBounds", &PEGTransformerFactory::TransformQuantifierBoundsInternal}, - {"QuantifierMinMax", &PEGTransformerFactory::TransformQuantifierMinMaxInternal}, - {"QuantifierMin", &PEGTransformerFactory::TransformQuantifierMinInternal}, - {"QuantifierMax", &PEGTransformerFactory::TransformQuantifierMaxInternal}, - {"QuantifierExact", &PEGTransformerFactory::TransformQuantifierExactInternal}, - {"SubsetClause", &PEGTransformerFactory::TransformSubsetClauseInternal}, - {"SubsetElement", &PEGTransformerFactory::TransformSubsetElementInternal}, - {"DefineClause", &PEGTransformerFactory::TransformDefineClauseInternal}, - {"DefineElement", &PEGTransformerFactory::TransformDefineElementInternal}, - {"MergeIntoStatement", &PEGTransformerFactory::TransformMergeIntoStatementInternal}, - {"MergeIntoUsingClause", &PEGTransformerFactory::TransformMergeIntoUsingClauseInternal}, - {"MergeMatch", &PEGTransformerFactory::TransformMergeMatchInternal}, - {"MatchedClause", &PEGTransformerFactory::TransformMatchedClauseInternal}, - {"MatchedClauseAction", &PEGTransformerFactory::TransformMatchedClauseActionInternal}, - {"UpdateMatchClause", &PEGTransformerFactory::TransformUpdateMatchClauseInternal}, - {"UpdateMatchInfo", &PEGTransformerFactory::TransformUpdateMatchInfoInternal}, - {"UpdateMatchSetAction", &PEGTransformerFactory::TransformUpdateMatchSetActionInternal}, - {"UpdateByNameOrPosition", &PEGTransformerFactory::TransformUpdateByNameOrPositionInternal}, - {"DeleteMatchClause", &PEGTransformerFactory::TransformDeleteMatchClauseInternal}, - {"InsertMatchClause", &PEGTransformerFactory::TransformInsertMatchClauseInternal}, - {"InsertMatchInfo", &PEGTransformerFactory::TransformInsertMatchInfoInternal}, - {"InsertDefaultValues", &PEGTransformerFactory::TransformInsertDefaultValuesInternal}, - {"InsertByNameOrPosition", &PEGTransformerFactory::TransformInsertByNameOrPositionInternal}, - {"InsertValuesList", &PEGTransformerFactory::TransformInsertValuesListInternal}, - {"DoNothingMatchClause", &PEGTransformerFactory::TransformDoNothingMatchClauseInternal}, - {"ErrorMatchClause", &PEGTransformerFactory::TransformErrorMatchClauseInternal}, - {"UpdateMatchSetClause", &PEGTransformerFactory::TransformUpdateMatchSetClauseInternal}, - {"UpdateMatchSetInfo", &PEGTransformerFactory::TransformUpdateMatchSetInfoInternal}, - {"AndExpression", &PEGTransformerFactory::TransformAndExpressionInternal}, - {"NotMatchedClause", &PEGTransformerFactory::TransformNotMatchedClauseInternal}, - {"BySourceOrTarget", &PEGTransformerFactory::TransformBySourceOrTargetInternal}, - {"BySource", &PEGTransformerFactory::TransformBySourceInternal}, - {"ByTarget", &PEGTransformerFactory::TransformByTargetInternal}, - {"PivotOn", &PEGTransformerFactory::TransformPivotOnInternal}, - {"PivotUsing", &PEGTransformerFactory::TransformPivotUsingInternal}, - {"PivotColumnList", &PEGTransformerFactory::TransformPivotColumnListInternal}, - {"PivotColumnEntry", &PEGTransformerFactory::TransformPivotColumnEntryInternal}, - {"PivotColumnExpression", &PEGTransformerFactory::TransformPivotColumnExpressionInternal}, - {"PivotColumnSubquery", &PEGTransformerFactory::TransformPivotColumnSubqueryInternal}, - {"IntoNameValues", &PEGTransformerFactory::TransformIntoNameValuesInternal}, - {"OptionalParensNameList", &PEGTransformerFactory::TransformOptionalParensNameListInternal}, - {"ParenthesizedNameList", &PEGTransformerFactory::TransformParenthesizedNameListInternal}, - {"BareNameList", &PEGTransformerFactory::TransformBareNameListInternal}, - {"IncludeOrExcludeNulls", &PEGTransformerFactory::TransformIncludeOrExcludeNullsInternal}, - {"IncludeNulls", &PEGTransformerFactory::TransformIncludeNullsInternal}, - {"ExcludeNulls", &PEGTransformerFactory::TransformExcludeNullsInternal}, - {"UnpivotHeader", &PEGTransformerFactory::TransformUnpivotHeaderInternal}, - {"UnpivotHeaderSingle", &PEGTransformerFactory::TransformUnpivotHeaderSingleInternal}, - {"UnpivotHeaderList", &PEGTransformerFactory::TransformUnpivotHeaderListInternal}, - {"PragmaStatement", &PEGTransformerFactory::TransformPragmaStatementInternal}, - {"PragmaAssignOrFunction", &PEGTransformerFactory::TransformPragmaAssignOrFunctionInternal}, - {"PragmaAssign", &PEGTransformerFactory::TransformPragmaAssignInternal}, - {"PragmaFunction", &PEGTransformerFactory::TransformPragmaFunctionInternal}, - {"PragmaParameters", &PEGTransformerFactory::TransformPragmaParametersInternal}, - {"PrepareStatement", &PEGTransformerFactory::TransformPrepareStatementInternal}, - {"TypeList", &PEGTransformerFactory::TransformTypeListInternal}, - {"SelectStatement", &PEGTransformerFactory::TransformSelectStatementInternal}, - {"SelectSetOpChain", &PEGTransformerFactory::TransformSelectSetOpChainInternal}, - {"SelectSetOpChainTail", &PEGTransformerFactory::TransformSelectSetOpChainTailInternal}, - {"IntersectChain", &PEGTransformerFactory::TransformIntersectChainInternal}, - {"IntersectChainTail", &PEGTransformerFactory::TransformIntersectChainTailInternal}, - {"SetIntersectClause", &PEGTransformerFactory::TransformSetIntersectClauseInternal}, - {"SelectAtom", &PEGTransformerFactory::TransformSelectAtomInternal}, - {"SelectParens", &PEGTransformerFactory::TransformSelectParensInternal}, - {"SetopClause", &PEGTransformerFactory::TransformSetopClauseInternal}, - {"SetopType", &PEGTransformerFactory::TransformSetopTypeInternal}, - {"SetopUnion", &PEGTransformerFactory::TransformSetopUnionInternal}, - {"SetopExcept", &PEGTransformerFactory::TransformSetopExceptInternal}, - {"SelectStatementType", &PEGTransformerFactory::TransformSelectStatementTypeInternal}, - {"ResultModifiers", &PEGTransformerFactory::TransformResultModifiersInternal}, - {"LimitOffset", &PEGTransformerFactory::TransformLimitOffsetInternal}, - {"LimitOffsetClause", &PEGTransformerFactory::TransformLimitOffsetClauseInternal}, - {"OffsetLimitClause", &PEGTransformerFactory::TransformOffsetLimitClauseInternal}, - {"OffsetFetchClause", &PEGTransformerFactory::TransformOffsetFetchClauseInternal}, - {"FetchOnlyClause", &PEGTransformerFactory::TransformFetchOnlyClauseInternal}, - {"TableStatement", &PEGTransformerFactory::TransformTableStatementInternal}, - {"OptionalParensSimpleSelect", &PEGTransformerFactory::TransformOptionalParensSimpleSelectInternal}, - {"SimpleSelectParens", &PEGTransformerFactory::TransformSimpleSelectParensInternal}, - {"SelectFrom", &PEGTransformerFactory::TransformSelectFromInternal}, - {"SelectFromClause", &PEGTransformerFactory::TransformSelectFromClauseInternal}, - {"FromSelectClause", &PEGTransformerFactory::TransformFromSelectClauseInternal}, - {"WithStatement", &PEGTransformerFactory::TransformWithStatementInternal}, - {"CTEBody", &PEGTransformerFactory::TransformCTEBodyInternal}, - {"CTESelectBody", &PEGTransformerFactory::TransformCTESelectBodyInternal}, - {"CTEDMLBody", &PEGTransformerFactory::TransformCTEDMLBodyInternal}, - {"UsingKey", &PEGTransformerFactory::TransformUsingKeyInternal}, - {"Materialized", &PEGTransformerFactory::TransformMaterializedInternal}, - {"SelectClause", &PEGTransformerFactory::TransformSelectClauseInternal}, - {"TargetList", &PEGTransformerFactory::TransformTargetListInternal}, - {"ColumnAliases", &PEGTransformerFactory::TransformColumnAliasesInternal}, - {"DistinctClause", &PEGTransformerFactory::TransformDistinctClauseInternal}, - {"DistinctAll", &PEGTransformerFactory::TransformDistinctAllInternal}, - {"DistinctOn", &PEGTransformerFactory::TransformDistinctOnInternal}, - {"DistinctOnTargets", &PEGTransformerFactory::TransformDistinctOnTargetsInternal}, - {"InnerTableRef", &PEGTransformerFactory::TransformInnerTableRefInternal}, - {"TableSubquery", &PEGTransformerFactory::TransformTableSubqueryInternal}, - {"BaseTableRef", &PEGTransformerFactory::TransformBaseTableRefInternal}, - {"TableAliasColon", &PEGTransformerFactory::TransformTableAliasColonInternal}, - {"ValuesRef", &PEGTransformerFactory::TransformValuesRefInternal}, - {"ParensTableRef", &PEGTransformerFactory::TransformParensTableRefInternal}, - {"JoinOrPivot", &PEGTransformerFactory::TransformJoinOrPivotInternal}, - {"TablePivotClause", &PEGTransformerFactory::TransformTablePivotClauseInternal}, - {"TablePivotClauseBody", &PEGTransformerFactory::TransformTablePivotClauseBodyInternal}, - {"PivotGroupByList", &PEGTransformerFactory::TransformPivotGroupByListInternal}, - {"TableUnpivotClause", &PEGTransformerFactory::TransformTableUnpivotClauseInternal}, - {"TableUnpivotClauseBody", &PEGTransformerFactory::TransformTableUnpivotClauseBodyInternal}, - {"PivotHeader", &PEGTransformerFactory::TransformPivotHeaderInternal}, - {"PivotValueList", &PEGTransformerFactory::TransformPivotValueListInternal}, - {"PivotValueTarget", &PEGTransformerFactory::TransformPivotValueTargetInternal}, - {"PivotEnumTarget", &PEGTransformerFactory::TransformPivotEnumTargetInternal}, - {"PivotListTarget", &PEGTransformerFactory::TransformPivotListTargetInternal}, - {"UnpivotValueList", &PEGTransformerFactory::TransformUnpivotValueListInternal}, - {"PivotTargetList", &PEGTransformerFactory::TransformPivotTargetListInternal}, - {"UnpivotTargetList", &PEGTransformerFactory::TransformUnpivotTargetListInternal}, - {"Lateral", &PEGTransformerFactory::TransformLateralInternal}, - {"BaseTableName", &PEGTransformerFactory::TransformBaseTableNameInternal}, - {"UnqualifiedBaseTableName", &PEGTransformerFactory::TransformUnqualifiedBaseTableNameInternal}, - {"QualifiedTableName", &PEGTransformerFactory::TransformQualifiedTableNameInternal}, - {"SchemaReservedTable", &PEGTransformerFactory::TransformSchemaReservedTableInternal}, - {"CatalogReservedSchemaTable", &PEGTransformerFactory::TransformCatalogReservedSchemaTableInternal}, - {"TableFunction", &PEGTransformerFactory::TransformTableFunctionInternal}, - {"TableFunctionLateralOpt", &PEGTransformerFactory::TransformTableFunctionLateralOptInternal}, - {"TableFunctionAliasColon", &PEGTransformerFactory::TransformTableFunctionAliasColonInternal}, - {"WithOrdinality", &PEGTransformerFactory::TransformWithOrdinalityInternal}, - {"QualifiedTableFunction", &PEGTransformerFactory::TransformQualifiedTableFunctionInternal}, - {"TableFunctionArguments", &PEGTransformerFactory::TransformTableFunctionArgumentsInternal}, - {"FunctionArgument", &PEGTransformerFactory::TransformFunctionArgumentInternal}, - {"NamedFunctionArgument", &PEGTransformerFactory::TransformNamedFunctionArgumentInternal}, - {"PositionalFunctionArgument", &PEGTransformerFactory::TransformPositionalFunctionArgumentInternal}, - {"NamedParameter", &PEGTransformerFactory::TransformNamedParameterInternal}, - {"TableAlias", &PEGTransformerFactory::TransformTableAliasInternal}, - {"TableAliasAs", &PEGTransformerFactory::TransformTableAliasAsInternal}, - {"TableAliasWithoutAs", &PEGTransformerFactory::TransformTableAliasWithoutAsInternal}, - {"AtClause", &PEGTransformerFactory::TransformAtClauseInternal}, - {"AtSpecifier", &PEGTransformerFactory::TransformAtSpecifierInternal}, - {"AtUnit", &PEGTransformerFactory::TransformAtUnitInternal}, - {"VersionAtUnit", &PEGTransformerFactory::TransformVersionAtUnitInternal}, - {"TimestampAtUnit", &PEGTransformerFactory::TransformTimestampAtUnitInternal}, - {"JoinClause", &PEGTransformerFactory::TransformJoinClauseInternal}, - {"NearestJoinClause", &PEGTransformerFactory::TransformNearestJoinClauseInternal}, - {"NearestJoinAliased", &PEGTransformerFactory::TransformNearestJoinAliasedInternal}, - {"NearestJoinBare", &PEGTransformerFactory::TransformNearestJoinBareInternal}, - {"NearestBareTableRef", &PEGTransformerFactory::TransformNearestBareTableRefInternal}, - {"NearestValuesRef", &PEGTransformerFactory::TransformNearestValuesRefInternal}, - {"NearestTableFunction", &PEGTransformerFactory::TransformNearestTableFunctionInternal}, - {"NearestTableSubquery", &PEGTransformerFactory::TransformNearestTableSubqueryInternal}, - {"NearestBaseTableRef", &PEGTransformerFactory::TransformNearestBaseTableRefInternal}, - {"NearestParensTableRef", &PEGTransformerFactory::TransformNearestParensTableRefInternal}, - {"ApproxOrExact", &PEGTransformerFactory::TransformApproxOrExactInternal}, - {"NearestApprox", &PEGTransformerFactory::TransformNearestApproxInternal}, - {"NearestExact", &PEGTransformerFactory::TransformNearestExactInternal}, - {"DistanceOrSimilarity", &PEGTransformerFactory::TransformDistanceOrSimilarityInternal}, - {"NearestDistance", &PEGTransformerFactory::TransformNearestDistanceInternal}, - {"NearestSimilarity", &PEGTransformerFactory::TransformNearestSimilarityInternal}, - {"RegularJoinClause", &PEGTransformerFactory::TransformRegularJoinClauseInternal}, - {"JoinByClause", &PEGTransformerFactory::TransformJoinByClauseInternal}, - {"Asof", &PEGTransformerFactory::TransformAsofInternal}, - {"JoinWithoutOnClause", &PEGTransformerFactory::TransformJoinWithoutOnClauseInternal}, - {"JoinQualifier", &PEGTransformerFactory::TransformJoinQualifierInternal}, - {"OnClause", &PEGTransformerFactory::TransformOnClauseInternal}, - {"UsingClause", &PEGTransformerFactory::TransformUsingClauseInternal}, - {"JoinType", &PEGTransformerFactory::TransformJoinTypeInternal}, - {"JoinPrefix", &PEGTransformerFactory::TransformJoinPrefixInternal}, - {"CrossJoinPrefix", &PEGTransformerFactory::TransformCrossJoinPrefixInternal}, - {"NaturalJoinPrefix", &PEGTransformerFactory::TransformNaturalJoinPrefixInternal}, - {"PositionalJoinPrefix", &PEGTransformerFactory::TransformPositionalJoinPrefixInternal}, - {"FullJoin", &PEGTransformerFactory::TransformFullJoinInternal}, - {"LeftJoin", &PEGTransformerFactory::TransformLeftJoinInternal}, - {"RightJoin", &PEGTransformerFactory::TransformRightJoinInternal}, - {"SemiJoin", &PEGTransformerFactory::TransformSemiJoinInternal}, - {"AntiJoin", &PEGTransformerFactory::TransformAntiJoinInternal}, - {"InnerJoin", &PEGTransformerFactory::TransformInnerJoinInternal}, - {"FromClause", &PEGTransformerFactory::TransformFromClauseInternal}, - {"WhereClause", &PEGTransformerFactory::TransformWhereClauseInternal}, - {"GroupByClause", &PEGTransformerFactory::TransformGroupByClauseInternal}, - {"HavingClause", &PEGTransformerFactory::TransformHavingClauseInternal}, - {"QualifyClause", &PEGTransformerFactory::TransformQualifyClauseInternal}, - {"SampleClause", &PEGTransformerFactory::TransformSampleClauseInternal}, - {"WindowClause", &PEGTransformerFactory::TransformWindowClauseInternal}, - {"SampleEntry", &PEGTransformerFactory::TransformSampleEntryInternal}, - {"SampleEntryCount", &PEGTransformerFactory::TransformSampleEntryCountInternal}, - {"SampleEntryFunction", &PEGTransformerFactory::TransformSampleEntryFunctionInternal}, - {"SampleFunction", &PEGTransformerFactory::TransformSampleFunctionInternal}, - {"SampleProperties", &PEGTransformerFactory::TransformSamplePropertiesInternal}, - {"RepeatableSample", &PEGTransformerFactory::TransformRepeatableSampleInternal}, - {"SampleSeed", &PEGTransformerFactory::TransformSampleSeedInternal}, - {"SampleCount", &PEGTransformerFactory::TransformSampleCountInternal}, - {"SampleValue", &PEGTransformerFactory::TransformSampleValueInternal}, - {"SampleUnit", &PEGTransformerFactory::TransformSampleUnitInternal}, - {"SamplePercentage", &PEGTransformerFactory::TransformSamplePercentageInternal}, - {"SampleRows", &PEGTransformerFactory::TransformSampleRowsInternal}, - {"GroupByExpressions", &PEGTransformerFactory::TransformGroupByExpressionsInternal}, - {"GroupByAll", &PEGTransformerFactory::TransformGroupByAllInternal}, - {"GroupByList", &PEGTransformerFactory::TransformGroupByListInternal}, - {"GroupByExpression", &PEGTransformerFactory::TransformGroupByExpressionInternal}, - {"GroupByBaseExpression", &PEGTransformerFactory::TransformGroupByBaseExpressionInternal}, - {"EmptyGroupingItem", &PEGTransformerFactory::TransformEmptyGroupingItemInternal}, - {"CubeOrRollupClause", &PEGTransformerFactory::TransformCubeOrRollupClauseInternal}, - {"CubeOrRollup", &PEGTransformerFactory::TransformCubeOrRollupInternal}, - {"CubeKeyword", &PEGTransformerFactory::TransformCubeKeywordInternal}, - {"RollupKeyword", &PEGTransformerFactory::TransformRollupKeywordInternal}, - {"GroupingSetsClause", &PEGTransformerFactory::TransformGroupingSetsClauseInternal}, - {"SubqueryReference", &PEGTransformerFactory::TransformSubqueryReferenceInternal}, - {"OrderByExpression", &PEGTransformerFactory::TransformOrderByExpressionInternal}, - {"DescOrAsc", &PEGTransformerFactory::TransformDescOrAscInternal}, - {"DescendingOrder", &PEGTransformerFactory::TransformDescendingOrderInternal}, - {"AscendingOrder", &PEGTransformerFactory::TransformAscendingOrderInternal}, - {"NullsFirstOrLast", &PEGTransformerFactory::TransformNullsFirstOrLastInternal}, - {"NullsFirst", &PEGTransformerFactory::TransformNullsFirstInternal}, - {"NullsLast", &PEGTransformerFactory::TransformNullsLastInternal}, - {"OrderByClause", &PEGTransformerFactory::TransformOrderByClauseInternal}, - {"OrderByExpressions", &PEGTransformerFactory::TransformOrderByExpressionsInternal}, - {"OrderByExpressionList", &PEGTransformerFactory::TransformOrderByExpressionListInternal}, - {"OrderByAll", &PEGTransformerFactory::TransformOrderByAllInternal}, - {"LimitClause", &PEGTransformerFactory::TransformLimitClauseInternal}, - {"OffsetClause", &PEGTransformerFactory::TransformOffsetClauseInternal}, - {"OffsetValue", &PEGTransformerFactory::TransformOffsetValueInternal}, - {"LimitValue", &PEGTransformerFactory::TransformLimitValueInternal}, - {"LimitAll", &PEGTransformerFactory::TransformLimitAllInternal}, - {"LimitLiteralPercent", &PEGTransformerFactory::TransformLimitLiteralPercentInternal}, - {"LimitExpression", &PEGTransformerFactory::TransformLimitExpressionInternal}, - {"FetchClause", &PEGTransformerFactory::TransformFetchClauseInternal}, - {"FetchValue", &PEGTransformerFactory::TransformFetchValueInternal}, - {"AliasedExpression", &PEGTransformerFactory::TransformAliasedExpressionInternal}, - {"ColIdExpression", &PEGTransformerFactory::TransformColIdExpressionInternal}, - {"ExpressionAsCollabel", &PEGTransformerFactory::TransformExpressionAsCollabelInternal}, - {"ExpressionOptIdentifier", &PEGTransformerFactory::TransformExpressionOptIdentifierInternal}, - {"ValuesClause", &PEGTransformerFactory::TransformValuesClauseInternal}, - {"ValuesExpressions", &PEGTransformerFactory::TransformValuesExpressionsInternal}, - {"SetStatement", &PEGTransformerFactory::TransformSetStatementInternal}, - {"SetAssignmentOrTimeZone", &PEGTransformerFactory::TransformSetAssignmentOrTimeZoneInternal}, - {"ResetStatement", &PEGTransformerFactory::TransformResetStatementInternal}, - {"SetSchema", &PEGTransformerFactory::TransformSetSchemaInternal}, - {"StandardAssignment", &PEGTransformerFactory::TransformStandardAssignmentInternal}, - {"SetVariableOrSetting", &PEGTransformerFactory::TransformSetVariableOrSettingInternal}, - {"SetTimeZone", &PEGTransformerFactory::TransformSetTimeZoneInternal}, - {"ZoneValue", &PEGTransformerFactory::TransformZoneValueInternal}, - {"ZoneLocal", &PEGTransformerFactory::TransformZoneLocalInternal}, - {"ZoneDefault", &PEGTransformerFactory::TransformZoneDefaultInternal}, - {"ZoneStringLiteral", &PEGTransformerFactory::TransformZoneStringLiteralInternal}, - {"ZoneIdentifier", &PEGTransformerFactory::TransformZoneIdentifierInternal}, - {"ZoneIntervalWithInterval", &PEGTransformerFactory::TransformZoneIntervalWithIntervalInternal}, - {"ZoneIntervalWithPrecision", &PEGTransformerFactory::TransformZoneIntervalWithPrecisionInternal}, - {"SetSetting", &PEGTransformerFactory::TransformSetSettingInternal}, - {"SetVariable", &PEGTransformerFactory::TransformSetVariableInternal}, - {"VariableScope", &PEGTransformerFactory::TransformVariableScopeInternal}, - {"SettingScope", &PEGTransformerFactory::TransformSettingScopeInternal}, - {"LocalScope", &PEGTransformerFactory::TransformLocalScopeInternal}, - {"SessionScope", &PEGTransformerFactory::TransformSessionScopeInternal}, - {"GlobalScope", &PEGTransformerFactory::TransformGlobalScopeInternal}, - {"SetAssignment", &PEGTransformerFactory::TransformSetAssignmentInternal}, - {"VariableList", &PEGTransformerFactory::TransformVariableListInternal}, - {"TransactionStatement", &PEGTransformerFactory::TransformTransactionStatementInternal}, - {"BeginTransaction", &PEGTransformerFactory::TransformBeginTransactionInternal}, - {"RollbackTransaction", &PEGTransformerFactory::TransformRollbackTransactionInternal}, - {"CommitTransaction", &PEGTransformerFactory::TransformCommitTransactionInternal}, - {"ReadOrWrite", &PEGTransformerFactory::TransformReadOrWriteInternal}, - {"ReadOnlyOrReadWrite", &PEGTransformerFactory::TransformReadOnlyOrReadWriteInternal}, - {"ReadOnly", &PEGTransformerFactory::TransformReadOnlyInternal}, - {"ReadWrite", &PEGTransformerFactory::TransformReadWriteInternal}, - {"UpdateStatement", &PEGTransformerFactory::TransformUpdateStatementInternal}, - {"UpdateTarget", &PEGTransformerFactory::TransformUpdateTargetInternal}, - {"BaseTableSet", &PEGTransformerFactory::TransformBaseTableSetInternal}, - {"BaseTableAliasSet", &PEGTransformerFactory::TransformBaseTableAliasSetInternal}, - {"UpdateAlias", &PEGTransformerFactory::TransformUpdateAliasInternal}, - {"UpdateSetClause", &PEGTransformerFactory::TransformUpdateSetClauseInternal}, - {"UpdateSetTuple", &PEGTransformerFactory::TransformUpdateSetTupleInternal}, - {"UpdateSetElementList", &PEGTransformerFactory::TransformUpdateSetElementListInternal}, - {"UpdateSetElement", &PEGTransformerFactory::TransformUpdateSetElementInternal}, - {"UpdateSetColumnTarget", &PEGTransformerFactory::TransformUpdateSetColumnTargetInternal}, - {"UseStatement", &PEGTransformerFactory::TransformUseStatementInternal}, - {"UseTarget", &PEGTransformerFactory::TransformUseTargetInternal}, - {"SchemaNameAsUseTarget", &PEGTransformerFactory::TransformSchemaNameAsUseTargetInternal}, - {"CatalogNameAsUseTarget", &PEGTransformerFactory::TransformCatalogNameAsUseTargetInternal}, - {"UseTargetCatalogSchema", &PEGTransformerFactory::TransformUseTargetCatalogSchemaInternal}, - {"DotIdentifier", &PEGTransformerFactory::TransformDotIdentifierInternal}, - {"VacuumStatement", &PEGTransformerFactory::TransformVacuumStatementInternal}, - {"VacuumOptions", &PEGTransformerFactory::TransformVacuumOptionsInternal}, - {"VacuumParensOptions", &PEGTransformerFactory::TransformVacuumParensOptionsInternal}, - {"VacuumLegacyOptions", &PEGTransformerFactory::TransformVacuumLegacyOptionsInternal}, - {"VacuumOption", &PEGTransformerFactory::TransformVacuumOptionInternal}, - {"OptAnalyze", &PEGTransformerFactory::TransformOptAnalyzeInternal}, - {"OptFull", &PEGTransformerFactory::TransformOptFullInternal}, - {"OptFreeze", &PEGTransformerFactory::TransformOptFreezeInternal}, - {"OptVerbose", &PEGTransformerFactory::TransformOptVerboseInternal}, - {"NameList", &PEGTransformerFactory::TransformNameListInternal}, - }; - for (const auto &rule : builtin_transform_rules) { - Register(rule.name, rule.finalize); - } -} - -} // namespace duckdb diff --git a/src/duckdb/src/parser/peg/transformer/transform_generated_trampoline.cpp b/src/duckdb/src/parser/peg/transformer/transform_generated_trampoline.cpp index 67dcd838b..26b598280 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_generated_trampoline.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_generated_trampoline.cpp @@ -8,6 +8,39 @@ namespace duckdb { static const TransformFrameOps STATEMENT_OPS = {"Statement", &PEGTransformerFactory::InitializeStatementTrampoline, &PEGTransformerFactory::FinalizeStatementTrampoline}; +static const TransformFrameOps PRAGMA_NAME_OPS = {"PragmaName", + &PEGTransformerFactory::InitializeIdentifierOrKeywordTrampoline, + &PEGTransformerFactory::FinalizeIdentifierOrKeywordTrampoline}; +static const TransformFrameOps TYPE_NAME_OPS = {"TypeName", + &PEGTransformerFactory::InitializeIdentifierOrKeywordTrampoline, + &PEGTransformerFactory::FinalizeIdentifierOrKeywordTrampoline}; +static const TransformFrameOps PLAIN_IDENTIFIER_OPS = {"PlainIdentifier", + &PEGTransformerFactory::InitializeIdentifierOrKeywordTrampoline, + &PEGTransformerFactory::FinalizeIdentifierOrKeywordTrampoline}; +static const TransformFrameOps QUOTED_IDENTIFIER_OPS = {"QuotedIdentifier", + &PEGTransformerFactory::InitializeIdentifierOrKeywordTrampoline, + &PEGTransformerFactory::FinalizeIdentifierOrKeywordTrampoline}; +static const TransformFrameOps RESERVED_KEYWORD_OPS = {"ReservedKeyword", + &PEGTransformerFactory::InitializeIdentifierOrKeywordTrampoline, + &PEGTransformerFactory::FinalizeIdentifierOrKeywordTrampoline}; +static const TransformFrameOps UNRESERVED_KEYWORD_OPS = { + "UnreservedKeyword", &PEGTransformerFactory::InitializeIdentifierOrKeywordTrampoline, + &PEGTransformerFactory::FinalizeIdentifierOrKeywordTrampoline}; +static const TransformFrameOps COLUMN_NAME_KEYWORD_OPS = { + "ColumnNameKeyword", &PEGTransformerFactory::InitializeIdentifierOrKeywordTrampoline, + &PEGTransformerFactory::FinalizeIdentifierOrKeywordTrampoline}; +static const TransformFrameOps FUNC_NAME_KEYWORD_OPS = {"FuncNameKeyword", + &PEGTransformerFactory::InitializeIdentifierOrKeywordTrampoline, + &PEGTransformerFactory::FinalizeIdentifierOrKeywordTrampoline}; +static const TransformFrameOps TYPE_NAME_KEYWORD_OPS = {"TypeNameKeyword", + &PEGTransformerFactory::InitializeIdentifierOrKeywordTrampoline, + &PEGTransformerFactory::FinalizeIdentifierOrKeywordTrampoline}; +static const TransformFrameOps BARE_LABEL_KEYWORD_OPS = { + "BareLabelKeyword", &PEGTransformerFactory::InitializeIdentifierOrKeywordTrampoline, + &PEGTransformerFactory::FinalizeIdentifierOrKeywordTrampoline}; +static const TransformFrameOps SETTING_NAME_OPS = {"SettingName", + &PEGTransformerFactory::InitializeIdentifierOrKeywordTrampoline, + &PEGTransformerFactory::FinalizeIdentifierOrKeywordTrampoline}; static const TransformFrameOps ALTER_STATEMENT_OPS = {"AlterStatement", &PEGTransformerFactory::InitializeAlterStatementTrampoline, &PEGTransformerFactory::FinalizeAlterStatementTrampoline}; @@ -20,6 +53,9 @@ static const TransformFrameOps ALTER_TABLE_STMT_OPS = {"AlterTableStmt", static const TransformFrameOps ALTER_SCHEMA_STMT_OPS = {"AlterSchemaStmt", &PEGTransformerFactory::InitializeAlterSchemaStmtTrampoline, &PEGTransformerFactory::FinalizeAlterSchemaStmtTrampoline}; +static const TransformFrameOps ALTER_SCHEMA_OPTIONS_OPS = { + "AlterSchemaOptions", &PEGTransformerFactory::InitializeAlterSchemaOptionsTrampoline, + &PEGTransformerFactory::FinalizeAlterSchemaOptionsTrampoline}; static const TransformFrameOps ALTER_TABLE_OPTIONS_OPS = {"AlterTableOptions", &PEGTransformerFactory::InitializeAlterTableOptionsTrampoline, &PEGTransformerFactory::FinalizeAlterTableOptionsTrampoline}; @@ -149,6 +185,12 @@ static const TransformFrameOps CHECKPOINT_FORCE_OPS = {"CheckpointForce", static const TransformFrameOps COMMENT_STATEMENT_OPS = {"CommentStatement", &PEGTransformerFactory::InitializeCommentStatementTrampoline, &PEGTransformerFactory::FinalizeCommentStatementTrampoline}; +static const TransformFrameOps COMMENT_TARGET_OPS = {"CommentTarget", + &PEGTransformerFactory::InitializeCommentTargetTrampoline, + &PEGTransformerFactory::FinalizeCommentTargetTrampoline}; +static const TransformFrameOps COMMENT_STRING_LITERAL_IDENTIFIER_OPS = { + "CommentStringLiteralIdentifier", &PEGTransformerFactory::InitializeCommentStringLiteralIdentifierTrampoline, + &PEGTransformerFactory::FinalizeCommentStringLiteralIdentifierTrampoline}; static const TransformFrameOps COMMENT_ON_TYPE_OPS = {"CommentOnType", &PEGTransformerFactory::InitializeCommentOnTypeTrampoline, &PEGTransformerFactory::FinalizeCommentOnTypeTrampoline}; @@ -194,12 +236,6 @@ static const TransformFrameOps STRING_LITERAL_VALUE_OPS = { static const TransformFrameOps ANALYZE_KEYWORD_OPS = {"AnalyzeKeyword", &PEGTransformerFactory::InitializeAnalyzeKeywordTrampoline, &PEGTransformerFactory::FinalizeAnalyzeKeywordTrampoline}; -static const TransformFrameOps EXPRESSION_STATEMENT_OPS = { - "ExpressionStatement", &PEGTransformerFactory::InitializeExpressionStatementTrampoline, - &PEGTransformerFactory::FinalizeExpressionStatementTrampoline}; -static const TransformFrameOps EXPRESSION_ALIAS_OPS = {"ExpressionAlias", - &PEGTransformerFactory::InitializeExpressionAliasTrampoline, - &PEGTransformerFactory::FinalizeExpressionAliasTrampoline}; static const TransformFrameOps INDEX_NAME_OPS = {"IndexName", &PEGTransformerFactory::InitializeIndexNameTrampoline, &PEGTransformerFactory::FinalizeIndexNameTrampoline}; static const TransformFrameOps CONSTRAINT_NAME_OPS = {"ConstraintName", @@ -373,6 +409,8 @@ static const TransformFrameOps SCHEMA_RESERVED_TYPE_NAME_OPS = { static const TransformFrameOps TYPE_MODIFIERS_OPS = {"TypeModifiers", &PEGTransformerFactory::InitializeTypeModifiersTrampoline, &PEGTransformerFactory::FinalizeTypeModifiersTrampoline}; +static const TransformFrameOps NESTED_TYPE_OPS = {"NestedType", &PEGTransformerFactory::InitializeNestedTypeTrampoline, + &PEGTransformerFactory::FinalizeNestedTypeTrampoline}; static const TransformFrameOps ROW_TYPE_OPS = {"RowType", &PEGTransformerFactory::InitializeRowTypeTrampoline, &PEGTransformerFactory::FinalizeRowTypeTrampoline}; static const TransformFrameOps SETOF_TYPE_OPS = {"SetofType", &PEGTransformerFactory::InitializeSetofTypeTrampoline, @@ -472,6 +510,12 @@ static const TransformFrameOps COPY_FILE_NAME_IDENTIFIER_COL_ID_OPS = { static const TransformFrameOps IDENTIFIER_COL_ID_OPS = {"IdentifierColId", &PEGTransformerFactory::InitializeIdentifierColIdTrampoline, &PEGTransformerFactory::FinalizeIdentifierColIdTrampoline}; +static const TransformFrameOps COPY_FILE_NAME_SUFFIX_OPS = { + "CopyFileNameSuffix", &PEGTransformerFactory::InitializeCopyFileNameSuffixTrampoline, + &PEGTransformerFactory::FinalizeCopyFileNameSuffixTrampoline}; +static const TransformFrameOps COPY_LEGACY_OPTION_OPS = {"CopyLegacyOption", + &PEGTransformerFactory::InitializeCopyLegacyOptionTrampoline, + &PEGTransformerFactory::FinalizeCopyLegacyOptionTrampoline}; static const TransformFrameOps COPY_OPTIONS_OPS = {"CopyOptions", &PEGTransformerFactory::InitializeCopyOptionsTrampoline, &PEGTransformerFactory::FinalizeCopyOptionsTrampoline}; @@ -632,9 +676,9 @@ static const TransformFrameOps REL_OPTION_OPS = {"RelOption", &PEGTransformerFac static const TransformFrameOps REL_OPTION_NAME_OPS = {"RelOptionName", &PEGTransformerFactory::InitializeRelOptionNameTrampoline, &PEGTransformerFactory::FinalizeRelOptionNameTrampoline}; -static const TransformFrameOps DOTTED_IDENTIFIER_STRING_OPS = { - "DottedIdentifierString", &PEGTransformerFactory::InitializeDottedIdentifierStringTrampoline, - &PEGTransformerFactory::FinalizeDottedIdentifierStringTrampoline}; +static const TransformFrameOps DOTTED_COL_LABEL_OPS = {"DottedColLabel", + &PEGTransformerFactory::InitializeDottedColLabelTrampoline, + &PEGTransformerFactory::FinalizeDottedColLabelTrampoline}; static const TransformFrameOps REL_OPTION_ARGUMENT_OPT_OPS = { "RelOptionArgumentOpt", &PEGTransformerFactory::InitializeRelOptionArgumentOptTrampoline, &PEGTransformerFactory::FinalizeRelOptionArgumentOptTrampoline}; @@ -687,6 +731,9 @@ static const TransformFrameOps TABLE_MACRO_DEFINITION_OPS = { static const TransformFrameOps CREATE_SCHEMA_STMT_OPS = {"CreateSchemaStmt", &PEGTransformerFactory::InitializeCreateSchemaStmtTrampoline, &PEGTransformerFactory::FinalizeCreateSchemaStmtTrampoline}; +static const TransformFrameOps WITH_OPTION_LIST_OPS = {"WithOptionList", + &PEGTransformerFactory::InitializeWithOptionListTrampoline, + &PEGTransformerFactory::FinalizeWithOptionListTrampoline}; static const TransformFrameOps CREATE_SECRET_STMT_OPS = {"CreateSecretStmt", &PEGTransformerFactory::InitializeCreateSecretStmtTrampoline, &PEGTransformerFactory::FinalizeCreateSecretStmtTrampoline}; @@ -836,6 +883,12 @@ static const TransformFrameOps COLUMN_DEFINITION_OPS = {"ColumnDefinition", static const TransformFrameOps COLUMN_CONSTRAINT_OPS = {"ColumnConstraint", &PEGTransformerFactory::InitializeColumnConstraintTrampoline, &PEGTransformerFactory::FinalizeColumnConstraintTrampoline}; +static const TransformFrameOps NAMEABLE_COLUMN_CONSTRAINT_OPS = { + "NameableColumnConstraint", &PEGTransformerFactory::InitializeNameableColumnConstraintTrampoline, + &PEGTransformerFactory::FinalizeNameableColumnConstraintTrampoline}; +static const TransformFrameOps COLUMN_CONSTRAINT_ELEMENT_OPS = { + "ColumnConstraintElement", &PEGTransformerFactory::InitializeColumnConstraintElementTrampoline, + &PEGTransformerFactory::FinalizeColumnConstraintElementTrampoline}; static const TransformFrameOps NOT_NULL_CONSTRAINT_OPS = {"NotNullConstraint", &PEGTransformerFactory::InitializeNotNullConstraintTrampoline, &PEGTransformerFactory::FinalizeNotNullConstraintTrampoline}; @@ -877,6 +930,12 @@ static const TransformFrameOps COLUMN_COMPRESSION_OPS = {"ColumnCompression", &PEGTransformerFactory::FinalizeColumnCompressionTrampoline}; static const TransformFrameOps KEY_ACTIONS_OPS = {"KeyActions", &PEGTransformerFactory::InitializeKeyActionsTrampoline, &PEGTransformerFactory::FinalizeKeyActionsTrampoline}; +static const TransformFrameOps UPDATE_FIRST_KEY_ACTIONS_OPS = { + "UpdateFirstKeyActions", &PEGTransformerFactory::InitializeUpdateFirstKeyActionsTrampoline, + &PEGTransformerFactory::FinalizeUpdateFirstKeyActionsTrampoline}; +static const TransformFrameOps DELETE_FIRST_KEY_ACTIONS_OPS = { + "DeleteFirstKeyActions", &PEGTransformerFactory::InitializeDeleteFirstKeyActionsTrampoline, + &PEGTransformerFactory::FinalizeDeleteFirstKeyActionsTrampoline}; static const TransformFrameOps UPDATE_ACTION_OPS = {"UpdateAction", &PEGTransformerFactory::InitializeUpdateActionTrampoline, &PEGTransformerFactory::FinalizeUpdateActionTrampoline}; @@ -954,6 +1013,14 @@ static const TransformFrameOps STRING_LITERAL_IDENTIFIER_OPS = { static const TransformFrameOps GENERATED_COLUMN_OPS = {"GeneratedColumn", &PEGTransformerFactory::InitializeGeneratedColumnTrampoline, &PEGTransformerFactory::FinalizeGeneratedColumnTrampoline}; +static const TransformFrameOps GENERATED_OPS = {"Generated", &PEGTransformerFactory::InitializeGeneratedTrampoline, + &PEGTransformerFactory::FinalizeGeneratedTrampoline}; +static const TransformFrameOps GENERATED_ALWAYS_OPS = {"GeneratedAlways", + &PEGTransformerFactory::InitializeGeneratedAlwaysTrampoline, + &PEGTransformerFactory::FinalizeGeneratedAlwaysTrampoline}; +static const TransformFrameOps GENERATED_BY_DEFAULT_OPS = { + "GeneratedByDefault", &PEGTransformerFactory::InitializeGeneratedByDefaultTrampoline, + &PEGTransformerFactory::FinalizeGeneratedByDefaultTrampoline}; static const TransformFrameOps GENERATED_COLUMN_TYPE_OPS = { "GeneratedColumnType", &PEGTransformerFactory::InitializeGeneratedColumnTypeTrampoline, &PEGTransformerFactory::FinalizeGeneratedColumnTypeTrampoline}; @@ -1068,9 +1135,21 @@ static const TransformFrameOps DELETE_STATEMENT_OPS = {"DeleteStatement", static const TransformFrameOps TRUNCATE_STATEMENT_OPS = {"TruncateStatement", &PEGTransformerFactory::InitializeTruncateStatementTrampoline, &PEGTransformerFactory::FinalizeTruncateStatementTrampoline}; +static const TransformFrameOps TRUNCATE_OPTIONS_OPS = {"TruncateOptions", + &PEGTransformerFactory::InitializeTruncateOptionsTrampoline, + &PEGTransformerFactory::FinalizeTruncateOptionsTrampoline}; +static const TransformFrameOps TRUNCATE_ALIAS_OPTIONS_OPS = { + "TruncateAliasOptions", &PEGTransformerFactory::InitializeTruncateAliasOptionsTrampoline, + &PEGTransformerFactory::FinalizeTruncateAliasOptionsTrampoline}; +static const TransformFrameOps TRUNCATE_DROP_BEHAVIOR_OPS = { + "TruncateDropBehavior", &PEGTransformerFactory::InitializeTruncateDropBehaviorTrampoline, + &PEGTransformerFactory::FinalizeTruncateDropBehaviorTrampoline}; static const TransformFrameOps TARGET_OPT_ALIAS_OPS = {"TargetOptAlias", &PEGTransformerFactory::InitializeTargetOptAliasTrampoline, &PEGTransformerFactory::FinalizeTargetOptAliasTrampoline}; +static const TransformFrameOps TARGET_ALIAS_OPS = {"TargetAlias", + &PEGTransformerFactory::InitializeTargetAliasTrampoline, + &PEGTransformerFactory::FinalizeTargetAliasTrampoline}; static const TransformFrameOps DELETE_USING_CLAUSE_OPS = {"DeleteUsingClause", &PEGTransformerFactory::InitializeDeleteUsingClauseTrampoline, &PEGTransformerFactory::FinalizeDeleteUsingClauseTrampoline}; @@ -1638,6 +1717,23 @@ static const TransformFrameOps NOT_KEYWORD_OPS = {"NotKeyword", &PEGTransformerF static const TransformFrameOps IS_EXPRESSION_OPS = {"IsExpression", &PEGTransformerFactory::InitializeIsExpressionTrampoline, &PEGTransformerFactory::FinalizeIsExpressionTrampoline}; +static const TransformFrameOps IS_EXPRESSION_CONTINUATION_OPS = { + "IsExpressionContinuation", &PEGTransformerFactory::InitializeIsExpressionContinuationTrampoline, + &PEGTransformerFactory::FinalizeIsExpressionContinuationTrampoline}; +static const TransformFrameOps IS_EXPRESSION_TAIL_OPS = {"IsExpressionTail", + &PEGTransformerFactory::InitializeIsExpressionTailTrampoline, + &PEGTransformerFactory::FinalizeIsExpressionTailTrampoline}; +static const TransformFrameOps IS_TEST_TAIL_OPS = {"IsTestTail", &PEGTransformerFactory::InitializeIsTestTailTrampoline, + &PEGTransformerFactory::FinalizeIsTestTailTrampoline}; +static const TransformFrameOps IS_DISTINCT_TAIL_OPS = {"IsDistinctTail", + &PEGTransformerFactory::InitializeIsDistinctTailTrampoline, + &PEGTransformerFactory::FinalizeIsDistinctTailTrampoline}; +static const TransformFrameOps IS_COMPARISON_TAIL_OPS = {"IsComparisonTail", + &PEGTransformerFactory::InitializeIsComparisonTailTrampoline, + &PEGTransformerFactory::FinalizeIsComparisonTailTrampoline}; +static const TransformFrameOps IS_OTHER_OPERATOR_TAIL_OPS = { + "IsOtherOperatorTail", &PEGTransformerFactory::InitializeIsOtherOperatorTailTrampoline, + &PEGTransformerFactory::FinalizeIsOtherOperatorTailTrampoline}; static const TransformFrameOps IS_TEST_OPS = {"IsTest", &PEGTransformerFactory::InitializeIsTestTrampoline, &PEGTransformerFactory::FinalizeIsTestTrampoline}; static const TransformFrameOps IS_LITERAL_OPS = {"IsLiteral", &PEGTransformerFactory::InitializeIsLiteralTrampoline, @@ -1700,12 +1796,15 @@ static const TransformFrameOps OPERATOR_GREATER_THAN_EQUALS_OPS = { static const TransformFrameOps BETWEEN_IN_LIKE_EXPRESSION_OPS = { "BetweenInLikeExpression", &PEGTransformerFactory::InitializeBetweenInLikeExpressionTrampoline, &PEGTransformerFactory::FinalizeBetweenInLikeExpressionTrampoline}; -static const TransformFrameOps BETWEEN_IN_LIKE_OP_OPS = {"BetweenInLikeOp", - &PEGTransformerFactory::InitializeBetweenInLikeOpTrampoline, - &PEGTransformerFactory::FinalizeBetweenInLikeOpTrampoline}; -static const TransformFrameOps BETWEEN_IN_LIKE_OP_EXPRESSION_OPS = { - "BetweenInLikeOpExpression", &PEGTransformerFactory::InitializeBetweenInLikeOpExpressionTrampoline, - &PEGTransformerFactory::FinalizeBetweenInLikeOpExpressionTrampoline}; +static const TransformFrameOps IN_PREDICATE_OPS = {"InPredicate", + &PEGTransformerFactory::InitializeInPredicateTrampoline, + &PEGTransformerFactory::FinalizeInPredicateTrampoline}; +static const TransformFrameOps BETWEEN_LIKE_OP_OPS = {"BetweenLikeOp", + &PEGTransformerFactory::InitializeBetweenLikeOpTrampoline, + &PEGTransformerFactory::FinalizeBetweenLikeOpTrampoline}; +static const TransformFrameOps BETWEEN_LIKE_OP_EXPRESSION_OPS = { + "BetweenLikeOpExpression", &PEGTransformerFactory::InitializeBetweenLikeOpExpressionTrampoline, + &PEGTransformerFactory::FinalizeBetweenLikeOpExpressionTrampoline}; static const TransformFrameOps LIKE_CLAUSE_OPS = {"LikeClause", &PEGTransformerFactory::InitializeLikeClauseTrampoline, &PEGTransformerFactory::FinalizeLikeClauseTrampoline}; static const TransformFrameOps ESCAPE_CLAUSE_OPS = {"EscapeClause", @@ -1759,6 +1858,12 @@ static const TransformFrameOps BETWEEN_CLAUSE_OPS = {"BetweenClause", static const TransformFrameOps OTHER_OPERATOR_EXPRESSION_OPS = { "OtherOperatorExpression", &PEGTransformerFactory::InitializeOtherOperatorExpressionTrampoline, &PEGTransformerFactory::FinalizeOtherOperatorExpressionTrampoline}; +static const TransformFrameOps INFIX_OTHER_OPERATOR_EXPRESSION_OPS = { + "InfixOtherOperatorExpression", &PEGTransformerFactory::InitializeInfixOtherOperatorExpressionTrampoline, + &PEGTransformerFactory::FinalizeInfixOtherOperatorExpressionTrampoline}; +static const TransformFrameOps CUSTOM_PREFIX_EXPRESSION_OPS = { + "CustomPrefixExpression", &PEGTransformerFactory::InitializeCustomPrefixExpressionTrampoline, + &PEGTransformerFactory::FinalizeCustomPrefixExpressionTrampoline}; static const TransformFrameOps OTHER_OPERATOR_TAIL_OPS = {"OtherOperatorTail", &PEGTransformerFactory::InitializeOtherOperatorTailTrampoline, &PEGTransformerFactory::FinalizeOtherOperatorTailTrampoline}; @@ -1774,6 +1879,9 @@ static const TransformFrameOps NAMED_OTHER_OPERATOR_OPS = { static const TransformFrameOps OPERATOR_LITERAL_OPS = {"OperatorLiteral", &PEGTransformerFactory::InitializeOperatorLiteralTrampoline, &PEGTransformerFactory::FinalizeOperatorLiteralTrampoline}; +static const TransformFrameOps ANY_OPERATOR_LITERAL_OPS = { + "AnyOperatorLiteral", &PEGTransformerFactory::InitializeAnyOperatorLiteralTrampoline, + &PEGTransformerFactory::FinalizeAnyOperatorLiteralTrampoline}; static const TransformFrameOps ANY_ALL_OPERATOR_OPS = {"AnyAllOperator", &PEGTransformerFactory::InitializeAnyAllOperatorTrampoline, &PEGTransformerFactory::FinalizeAnyAllOperatorTrampoline}; @@ -1785,26 +1893,12 @@ static const TransformFrameOps SUBQUERY_ANY_OPS = {"SubqueryAny", static const TransformFrameOps SUBQUERY_ALL_OPS = {"SubqueryAll", &PEGTransformerFactory::InitializeSubqueryAllTrampoline, &PEGTransformerFactory::FinalizeSubqueryAllTrampoline}; -static const TransformFrameOps INET_OPERATOR_OPS = {"InetOperator", - &PEGTransformerFactory::InitializeInetOperatorTrampoline, - &PEGTransformerFactory::FinalizeInetOperatorTrampoline}; -static const TransformFrameOps JSON_OPERATOR_OPS = {"JsonOperator", - &PEGTransformerFactory::InitializeJsonOperatorTrampoline, - &PEGTransformerFactory::FinalizeJsonOperatorTrampoline}; -static const TransformFrameOps LIST_OPERATOR_OPS = {"ListOperator", - &PEGTransformerFactory::InitializeListOperatorTrampoline, - &PEGTransformerFactory::FinalizeListOperatorTrampoline}; -static const TransformFrameOps STRING_OPERATOR_OPS = {"StringOperator", - &PEGTransformerFactory::InitializeStringOperatorTrampoline, - &PEGTransformerFactory::FinalizeStringOperatorTrampoline}; static const TransformFrameOps QUALIFIED_OPERATOR_OPS = {"QualifiedOperator", &PEGTransformerFactory::InitializeQualifiedOperatorTrampoline, &PEGTransformerFactory::FinalizeQualifiedOperatorTrampoline}; static const TransformFrameOps QUALIFIED_OPERATOR_CONTENTS_OPS = { "QualifiedOperatorContents", &PEGTransformerFactory::InitializeQualifiedOperatorContentsTrampoline, &PEGTransformerFactory::FinalizeQualifiedOperatorContentsTrampoline}; -static const TransformFrameOps ANY_OP_OPS = {"AnyOp", &PEGTransformerFactory::InitializeAnyOpTrampoline, - &PEGTransformerFactory::FinalizeAnyOpTrampoline}; static const TransformFrameOps BITWISE_EXPRESSION_OPS = {"BitwiseExpression", &PEGTransformerFactory::InitializeBitwiseExpressionTrampoline, &PEGTransformerFactory::FinalizeBitwiseExpressionTrampoline}; @@ -1814,6 +1908,9 @@ static const TransformFrameOps BITWISE_EXPRESSION_TAIL_OPS = { static const TransformFrameOps BIT_OPERATOR_OPS = {"BitOperator", &PEGTransformerFactory::InitializeBitOperatorTrampoline, &PEGTransformerFactory::FinalizeBitOperatorTrampoline}; +static const TransformFrameOps TILDE_EXPRESSION_OPS = {"TildeExpression", + &PEGTransformerFactory::InitializeTildeExpressionTrampoline, + &PEGTransformerFactory::FinalizeTildeExpressionTrampoline}; static const TransformFrameOps ADDITIVE_EXPRESSION_OPS = { "AdditiveExpression", &PEGTransformerFactory::InitializeAdditiveExpressionTrampoline, &PEGTransformerFactory::FinalizeAdditiveExpressionTrampoline}; @@ -1904,15 +2001,18 @@ static const TransformFrameOps SLICE_EXPRESSION_OPS = {"SliceExpression", &PEGTransformerFactory::FinalizeSliceExpressionTrampoline}; static const TransformFrameOps SLICE_BOUND_OPS = {"SliceBound", &PEGTransformerFactory::InitializeSliceBoundTrampoline, &PEGTransformerFactory::FinalizeSliceBoundTrampoline}; +static const TransformFrameOps SLICE_UPPER_BOUND_OPS = {"SliceUpperBound", + &PEGTransformerFactory::InitializeSliceUpperBoundTrampoline, + &PEGTransformerFactory::FinalizeSliceUpperBoundTrampoline}; +static const TransformFrameOps MINUS_STEP_SLICE_BOUND_OPS = { + "MinusStepSliceBound", &PEGTransformerFactory::InitializeMinusStepSliceBoundTrampoline, + &PEGTransformerFactory::FinalizeMinusStepSliceBoundTrampoline}; +static const TransformFrameOps END_STEP_SLICE_BOUND_OPS = { + "EndStepSliceBound", &PEGTransformerFactory::InitializeEndStepSliceBoundTrampoline, + &PEGTransformerFactory::FinalizeEndStepSliceBoundTrampoline}; static const TransformFrameOps END_SLICE_BOUND_OPS = {"EndSliceBound", &PEGTransformerFactory::InitializeEndSliceBoundTrampoline, &PEGTransformerFactory::FinalizeEndSliceBoundTrampoline}; -static const TransformFrameOps END_SLICE_VALUE_OPS = {"EndSliceValue", - &PEGTransformerFactory::InitializeEndSliceValueTrampoline, - &PEGTransformerFactory::FinalizeEndSliceValueTrampoline}; -static const TransformFrameOps END_SLICE_MINUS_OPS = {"EndSliceMinus", - &PEGTransformerFactory::InitializeEndSliceMinusTrampoline, - &PEGTransformerFactory::FinalizeEndSliceMinusTrampoline}; static const TransformFrameOps STEP_SLICE_BOUND_OPS = {"StepSliceBound", &PEGTransformerFactory::InitializeStepSliceBoundTrampoline, &PEGTransformerFactory::FinalizeStepSliceBoundTrampoline}; @@ -2052,6 +2152,21 @@ static const TransformFrameOps SHOW_ALL_MODIFIER_OPS = {"ShowAllModifier", static const TransformFrameOps EXTERNAL_RESOURCE_CREATION_OPTIONS_OPS = { "ExternalResourceCreationOptions", &PEGTransformerFactory::InitializeExternalResourceCreationOptionsTrampoline, &PEGTransformerFactory::FinalizeExternalResourceCreationOptionsTrampoline}; +static const TransformFrameOps ATTACH_TO_EXTERNAL_RESOURCE_OPS = { + "AttachToExternalResource", &PEGTransformerFactory::InitializeAttachToExternalResourceTrampoline, + &PEGTransformerFactory::FinalizeAttachToExternalResourceTrampoline}; +static const TransformFrameOps CONNECT_TO_EXTERNAL_RESOURCE_OPS = { + "ConnectToExternalResource", &PEGTransformerFactory::InitializeConnectToExternalResourceTrampoline, + &PEGTransformerFactory::FinalizeConnectToExternalResourceTrampoline}; +static const TransformFrameOps EXTERNAL_RESOURCE_SOURCE_OPS = { + "ExternalResourceSource", &PEGTransformerFactory::InitializeExternalResourceSourceTrampoline, + &PEGTransformerFactory::FinalizeExternalResourceSourceTrampoline}; +static const TransformFrameOps EXTERNAL_RESOURCE_CREATE_CLAUSE_OPS = { + "ExternalResourceCreateClause", &PEGTransformerFactory::InitializeExternalResourceCreateClauseTrampoline, + &PEGTransformerFactory::FinalizeExternalResourceCreateClauseTrampoline}; +static const TransformFrameOps EXTERNAL_RESOURCE_REFERENCE_CLAUSE_OPS = { + "ExternalResourceReferenceClause", &PEGTransformerFactory::InitializeExternalResourceReferenceClauseTrampoline, + &PEGTransformerFactory::FinalizeExternalResourceReferenceClauseTrampoline}; static const TransformFrameOps INSERT_STATEMENT_OPS = {"InsertStatement", &PEGTransformerFactory::InitializeInsertStatementTrampoline, &PEGTransformerFactory::FinalizeInsertStatementTrampoline}; @@ -2531,6 +2646,9 @@ static const TransformFrameOps OFFSET_LIMIT_CLAUSE_OPS = {"OffsetLimitClause", static const TransformFrameOps OFFSET_FETCH_CLAUSE_OPS = {"OffsetFetchClause", &PEGTransformerFactory::InitializeOffsetFetchClauseTrampoline, &PEGTransformerFactory::FinalizeOffsetFetchClauseTrampoline}; +static const TransformFrameOps FETCH_OFFSET_CLAUSE_OPS = {"FetchOffsetClause", + &PEGTransformerFactory::InitializeFetchOffsetClauseTrampoline, + &PEGTransformerFactory::FinalizeFetchOffsetClauseTrampoline}; static const TransformFrameOps FETCH_ONLY_CLAUSE_OPS = {"FetchOnlyClause", &PEGTransformerFactory::InitializeFetchOnlyClauseTrampoline, &PEGTransformerFactory::FinalizeFetchOnlyClauseTrampoline}; @@ -2785,6 +2903,9 @@ static const TransformFrameOps ON_CLAUSE_OPS = {"OnClause", &PEGTransformerFacto static const TransformFrameOps USING_CLAUSE_OPS = {"UsingClause", &PEGTransformerFactory::InitializeUsingClauseTrampoline, &PEGTransformerFactory::FinalizeUsingClauseTrampoline}; +static const TransformFrameOps USING_COLUMN_NAME_OPS = {"UsingColumnName", + &PEGTransformerFactory::InitializeUsingColumnNameTrampoline, + &PEGTransformerFactory::FinalizeUsingColumnNameTrampoline}; static const TransformFrameOps JOIN_TYPE_OPS = {"JoinType", &PEGTransformerFactory::InitializeJoinTypeTrampoline, &PEGTransformerFactory::FinalizeJoinTypeTrampoline}; static const TransformFrameOps JOIN_PREFIX_OPS = {"JoinPrefix", &PEGTransformerFactory::InitializeJoinPrefixTrampoline, @@ -2827,6 +2948,9 @@ static const TransformFrameOps QUALIFY_CLAUSE_OPS = {"QualifyClause", static const TransformFrameOps SAMPLE_CLAUSE_OPS = {"SampleClause", &PEGTransformerFactory::InitializeSampleClauseTrampoline, &PEGTransformerFactory::FinalizeSampleClauseTrampoline}; +static const TransformFrameOps SELECT_SAMPLE_CLAUSE_OPS = { + "SelectSampleClause", &PEGTransformerFactory::InitializeSelectSampleClauseTrampoline, + &PEGTransformerFactory::FinalizeSelectSampleClauseTrampoline}; static const TransformFrameOps WINDOW_CLAUSE_OPS = {"WindowClause", &PEGTransformerFactory::InitializeWindowClauseTrampoline, &PEGTransformerFactory::FinalizeWindowClauseTrampoline}; @@ -2952,6 +3076,12 @@ static const TransformFrameOps LIMIT_EXPRESSION_OPS = {"LimitExpression", static const TransformFrameOps FETCH_CLAUSE_OPS = {"FetchClause", &PEGTransformerFactory::InitializeFetchClauseTrampoline, &PEGTransformerFactory::FinalizeFetchClauseTrampoline}; +static const TransformFrameOps FETCH_CLAUSE_WITHOUT_VALUE_OPS = { + "FetchClauseWithoutValue", &PEGTransformerFactory::InitializeFetchClauseWithoutValueTrampoline, + &PEGTransformerFactory::FinalizeFetchClauseWithoutValueTrampoline}; +static const TransformFrameOps FETCH_CLAUSE_WITH_VALUE_OPS = { + "FetchClauseWithValue", &PEGTransformerFactory::InitializeFetchClauseWithValueTrampoline, + &PEGTransformerFactory::FinalizeFetchClauseWithValueTrampoline}; static const TransformFrameOps FETCH_VALUE_OPS = {"FetchValue", &PEGTransformerFactory::InitializeFetchValueTrampoline, &PEGTransformerFactory::FinalizeFetchValueTrampoline}; static const TransformFrameOps ALIASED_EXPRESSION_OPS = {"AliasedExpression", @@ -3073,15 +3203,15 @@ static const TransformFrameOps BASE_TABLE_ALIAS_SET_OPS = { static const TransformFrameOps UPDATE_ALIAS_OPS = {"UpdateAlias", &PEGTransformerFactory::InitializeUpdateAliasTrampoline, &PEGTransformerFactory::FinalizeUpdateAliasTrampoline}; +static const TransformFrameOps UPDATE_SET_CLAUSE_LIST_OPS = { + "UpdateSetClauseList", &PEGTransformerFactory::InitializeUpdateSetClauseListTrampoline, + &PEGTransformerFactory::FinalizeUpdateSetClauseListTrampoline}; static const TransformFrameOps UPDATE_SET_CLAUSE_OPS = {"UpdateSetClause", &PEGTransformerFactory::InitializeUpdateSetClauseTrampoline, &PEGTransformerFactory::FinalizeUpdateSetClauseTrampoline}; static const TransformFrameOps UPDATE_SET_TUPLE_OPS = {"UpdateSetTuple", &PEGTransformerFactory::InitializeUpdateSetTupleTrampoline, &PEGTransformerFactory::FinalizeUpdateSetTupleTrampoline}; -static const TransformFrameOps UPDATE_SET_ELEMENT_LIST_OPS = { - "UpdateSetElementList", &PEGTransformerFactory::InitializeUpdateSetElementListTrampoline, - &PEGTransformerFactory::FinalizeUpdateSetElementListTrampoline}; static const TransformFrameOps UPDATE_SET_ELEMENT_OPS = {"UpdateSetElement", &PEGTransformerFactory::InitializeUpdateSetElementTrampoline, &PEGTransformerFactory::FinalizeUpdateSetElementTrampoline}; @@ -3134,10 +3264,22 @@ static const TransformFrameOps NAME_LIST_OPS = {"NameList", &PEGTransformerFacto const case_insensitive_map_t &PEGTransformerFactory::GeneratedTransformFrameOps() { static const case_insensitive_map_t result = { {"Statement", &STATEMENT_OPS}, + {"PragmaName", &PRAGMA_NAME_OPS}, + {"TypeName", &TYPE_NAME_OPS}, + {"PlainIdentifier", &PLAIN_IDENTIFIER_OPS}, + {"QuotedIdentifier", "ED_IDENTIFIER_OPS}, + {"ReservedKeyword", &RESERVED_KEYWORD_OPS}, + {"UnreservedKeyword", &UNRESERVED_KEYWORD_OPS}, + {"ColumnNameKeyword", &COLUMN_NAME_KEYWORD_OPS}, + {"FuncNameKeyword", &FUNC_NAME_KEYWORD_OPS}, + {"TypeNameKeyword", &TYPE_NAME_KEYWORD_OPS}, + {"BareLabelKeyword", &BARE_LABEL_KEYWORD_OPS}, + {"SettingName", &SETTING_NAME_OPS}, {"AlterStatement", &ALTER_STATEMENT_OPS}, {"AlterOptions", &ALTER_OPTIONS_OPS}, {"AlterTableStmt", &ALTER_TABLE_STMT_OPS}, {"AlterSchemaStmt", &ALTER_SCHEMA_STMT_OPS}, + {"AlterSchemaOptions", &ALTER_SCHEMA_OPTIONS_OPS}, {"AlterTableOptions", &ALTER_TABLE_OPTIONS_OPS}, {"AddConstraint", &ADD_CONSTRAINT_OPS}, {"DropConstraint", &DROP_CONSTRAINT_OPS}, @@ -3183,6 +3325,8 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"CheckpointStatement", &CHECKPOINT_STATEMENT_OPS}, {"CheckpointForce", &CHECKPOINT_FORCE_OPS}, {"CommentStatement", &COMMENT_STATEMENT_OPS}, + {"CommentTarget", &COMMENT_TARGET_OPS}, + {"CommentStringLiteralIdentifier", &COMMENT_STRING_LITERAL_IDENTIFIER_OPS}, {"CommentOnType", &COMMENT_ON_TYPE_OPS}, {"CommentTable", &COMMENT_TABLE_OPS}, {"CommentSequence", &COMMENT_SEQUENCE_OPS}, @@ -3198,8 +3342,6 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"CommentValue", &COMMENT_VALUE_OPS}, {"StringLiteralValue", &STRING_LITERAL_VALUE_OPS}, {"AnalyzeKeyword", &ANALYZE_KEYWORD_OPS}, - {"ExpressionStatement", &EXPRESSION_STATEMENT_OPS}, - {"ExpressionAlias", &EXPRESSION_ALIAS_OPS}, {"IndexName", &INDEX_NAME_OPS}, {"ConstraintName", &CONSTRAINT_NAME_OPS}, {"SequenceName", &SEQUENCE_NAME_OPS}, @@ -3262,6 +3404,7 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"CatalogReservedSchemaTypeName", &CATALOG_RESERVED_SCHEMA_TYPE_NAME_OPS}, {"SchemaReservedTypeName", &SCHEMA_RESERVED_TYPE_NAME_OPS}, {"TypeModifiers", &TYPE_MODIFIERS_OPS}, + {"NestedType", &NESTED_TYPE_OPS}, {"RowType", &ROW_TYPE_OPS}, {"SetofType", &SETOF_TYPE_OPS}, {"UnionType", &UNION_TYPE_OPS}, @@ -3300,6 +3443,8 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"CopyFileNameIdentifier", ©_FILE_NAME_IDENTIFIER_OPS}, {"CopyFileNameIdentifierColId", ©_FILE_NAME_IDENTIFIER_COL_ID_OPS}, {"IdentifierColId", &IDENTIFIER_COL_ID_OPS}, + {"CopyFileNameSuffix", ©_FILE_NAME_SUFFIX_OPS}, + {"CopyLegacyOption", ©_LEGACY_OPTION_OPS}, {"CopyOptions", ©_OPTIONS_OPS}, {"CopyOptionList", ©_OPTION_LIST_OPS}, {"SpecializedOptionList", &SPECIALIZED_OPTION_LIST_OPS}, @@ -3356,7 +3501,7 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"IndexType", &INDEX_TYPE_OPS}, {"RelOption", &REL_OPTION_OPS}, {"RelOptionName", &REL_OPTION_NAME_OPS}, - {"DottedIdentifierString", &DOTTED_IDENTIFIER_STRING_OPS}, + {"DottedColLabel", &DOTTED_COL_LABEL_OPS}, {"RelOptionArgumentOpt", &REL_OPTION_ARGUMENT_OPT_OPS}, {"DefArg", &DEF_ARG_OPS}, {"DefArgNull", &DEF_ARG_NULL_OPS}, @@ -3375,6 +3520,7 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"ScalarMacroDefinition", &SCALAR_MACRO_DEFINITION_OPS}, {"TableMacroDefinition", &TABLE_MACRO_DEFINITION_OPS}, {"CreateSchemaStmt", &CREATE_SCHEMA_STMT_OPS}, + {"WithOptionList", &WITH_OPTION_LIST_OPS}, {"CreateSecretStmt", &CREATE_SECRET_STMT_OPS}, {"SecretStorageSpecifier", &SECRET_STORAGE_SPECIFIER_OPS}, {"SecretName", &SECRET_NAME_OPS}, @@ -3428,6 +3574,8 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"CreateTableConstraint", &CREATE_TABLE_CONSTRAINT_OPS}, {"ColumnDefinition", &COLUMN_DEFINITION_OPS}, {"ColumnConstraint", &COLUMN_CONSTRAINT_OPS}, + {"NameableColumnConstraint", &NAMEABLE_COLUMN_CONSTRAINT_OPS}, + {"ColumnConstraintElement", &COLUMN_CONSTRAINT_ELEMENT_OPS}, {"NotNullConstraint", &NOT_NULL_CONSTRAINT_OPS}, {"NullConstraint", &NULL_CONSTRAINT_OPS}, {"NotNullColumnConstraint", &NOT_NULL_COLUMN_CONSTRAINT_OPS}, @@ -3442,6 +3590,8 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"ColumnCollation", &COLUMN_COLLATION_OPS}, {"ColumnCompression", &COLUMN_COMPRESSION_OPS}, {"KeyActions", &KEY_ACTIONS_OPS}, + {"UpdateFirstKeyActions", &UPDATE_FIRST_KEY_ACTIONS_OPS}, + {"DeleteFirstKeyActions", &DELETE_FIRST_KEY_ACTIONS_OPS}, {"UpdateAction", &UPDATE_ACTION_OPS}, {"DeleteAction", &DELETE_ACTION_OPS}, {"KeyAction", &KEY_ACTION_OPS}, @@ -3469,6 +3619,9 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"ColLabelIdentifier", &COL_LABEL_IDENTIFIER_OPS}, {"StringLiteralIdentifier", &STRING_LITERAL_IDENTIFIER_OPS}, {"GeneratedColumn", &GENERATED_COLUMN_OPS}, + {"Generated", &GENERATED_OPS}, + {"GeneratedAlways", &GENERATED_ALWAYS_OPS}, + {"GeneratedByDefault", &GENERATED_BY_DEFAULT_OPS}, {"GeneratedColumnType", &GENERATED_COLUMN_TYPE_OPS}, {"CommitAction", &COMMIT_ACTION_OPS}, {"PreserveOrDelete", &PRESERVE_OR_DELETE_OPS}, @@ -3508,7 +3661,11 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"DeallocatePrepare", &DEALLOCATE_PREPARE_OPS}, {"DeleteStatement", &DELETE_STATEMENT_OPS}, {"TruncateStatement", &TRUNCATE_STATEMENT_OPS}, + {"TruncateOptions", &TRUNCATE_OPTIONS_OPS}, + {"TruncateAliasOptions", &TRUNCATE_ALIAS_OPTIONS_OPS}, + {"TruncateDropBehavior", &TRUNCATE_DROP_BEHAVIOR_OPS}, {"TargetOptAlias", &TARGET_OPT_ALIAS_OPS}, + {"TargetAlias", &TARGET_ALIAS_OPS}, {"DeleteUsingClause", &DELETE_USING_CLAUSE_OPS}, {"DescribeStatement", &DESCRIBE_STATEMENT_OPS}, {"ShowDeprecatedSelect", &SHOW_DEPRECATED_SELECT_OPS}, @@ -3705,6 +3862,12 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"NotExpression", &NOT_EXPRESSION_OPS}, {"NotKeyword", &NOT_KEYWORD_OPS}, {"IsExpression", &IS_EXPRESSION_OPS}, + {"IsExpressionContinuation", &IS_EXPRESSION_CONTINUATION_OPS}, + {"IsExpressionTail", &IS_EXPRESSION_TAIL_OPS}, + {"IsTestTail", &IS_TEST_TAIL_OPS}, + {"IsDistinctTail", &IS_DISTINCT_TAIL_OPS}, + {"IsComparisonTail", &IS_COMPARISON_TAIL_OPS}, + {"IsOtherOperatorTail", &IS_OTHER_OPERATOR_TAIL_OPS}, {"IsTest", &IS_TEST_OPS}, {"IsLiteral", &IS_LITERAL_OPS}, {"IsLiteralValue", &IS_LITERAL_VALUE_OPS}, @@ -3727,8 +3890,9 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"OperatorLessThanEquals", &OPERATOR_LESS_THAN_EQUALS_OPS}, {"OperatorGreaterThanEquals", &OPERATOR_GREATER_THAN_EQUALS_OPS}, {"BetweenInLikeExpression", &BETWEEN_IN_LIKE_EXPRESSION_OPS}, - {"BetweenInLikeOp", &BETWEEN_IN_LIKE_OP_OPS}, - {"BetweenInLikeOpExpression", &BETWEEN_IN_LIKE_OP_EXPRESSION_OPS}, + {"InPredicate", &IN_PREDICATE_OPS}, + {"BetweenLikeOp", &BETWEEN_LIKE_OP_OPS}, + {"BetweenLikeOpExpression", &BETWEEN_LIKE_OP_EXPRESSION_OPS}, {"LikeClause", &LIKE_CLAUSE_OPS}, {"EscapeClause", &ESCAPE_CLAUSE_OPS}, {"LikeVariations", &LIKE_VARIATIONS_OPS}, @@ -3749,25 +3913,24 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"InSelectStatement", &IN_SELECT_STATEMENT_OPS}, {"BetweenClause", &BETWEEN_CLAUSE_OPS}, {"OtherOperatorExpression", &OTHER_OPERATOR_EXPRESSION_OPS}, + {"InfixOtherOperatorExpression", &INFIX_OTHER_OPERATOR_EXPRESSION_OPS}, + {"CustomPrefixExpression", &CUSTOM_PREFIX_EXPRESSION_OPS}, {"OtherOperatorTail", &OTHER_OPERATOR_TAIL_OPS}, {"OtherOperator", &OTHER_OPERATOR_OPS}, {"AnyAllParsedOperator", &ANY_ALL_PARSED_OPERATOR_OPS}, {"NamedOtherOperator", &NAMED_OTHER_OPERATOR_OPS}, {"OperatorLiteral", &OPERATOR_LITERAL_OPS}, + {"AnyOperatorLiteral", &ANY_OPERATOR_LITERAL_OPS}, {"AnyAllOperator", &ANY_ALL_OPERATOR_OPS}, {"AnyOrAll", &ANY_OR_ALL_OPS}, {"SubqueryAny", &SUBQUERY_ANY_OPS}, {"SubqueryAll", &SUBQUERY_ALL_OPS}, - {"InetOperator", &INET_OPERATOR_OPS}, - {"JsonOperator", &JSON_OPERATOR_OPS}, - {"ListOperator", &LIST_OPERATOR_OPS}, - {"StringOperator", &STRING_OPERATOR_OPS}, {"QualifiedOperator", &QUALIFIED_OPERATOR_OPS}, {"QualifiedOperatorContents", &QUALIFIED_OPERATOR_CONTENTS_OPS}, - {"AnyOp", &ANY_OP_OPS}, {"BitwiseExpression", &BITWISE_EXPRESSION_OPS}, {"BitwiseExpressionTail", &BITWISE_EXPRESSION_TAIL_OPS}, {"BitOperator", &BIT_OPERATOR_OPS}, + {"TildeExpression", &TILDE_EXPRESSION_OPS}, {"AdditiveExpression", &ADDITIVE_EXPRESSION_OPS}, {"AdditiveExpressionTail", &ADDITIVE_EXPRESSION_TAIL_OPS}, {"Term", &TERM_OPS}, @@ -3799,9 +3962,10 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"MethodFunctionArguments", &METHOD_FUNCTION_ARGUMENTS_OPS}, {"SliceExpression", &SLICE_EXPRESSION_OPS}, {"SliceBound", &SLICE_BOUND_OPS}, + {"SliceUpperBound", &SLICE_UPPER_BOUND_OPS}, + {"MinusStepSliceBound", &MINUS_STEP_SLICE_BOUND_OPS}, + {"EndStepSliceBound", &END_STEP_SLICE_BOUND_OPS}, {"EndSliceBound", &END_SLICE_BOUND_OPS}, - {"EndSliceValue", &END_SLICE_VALUE_OPS}, - {"EndSliceMinus", &END_SLICE_MINUS_OPS}, {"StepSliceBound", &STEP_SLICE_BOUND_OPS}, {"PostfixOperator", &POSTFIX_OPERATOR_OPS}, {"SpecialFunctionExpression", &SPECIAL_FUNCTION_EXPRESSION_OPS}, @@ -3849,6 +4013,11 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"ShowExternalResourcesStmt", &SHOW_EXTERNAL_RESOURCES_STMT_OPS}, {"ShowAllModifier", &SHOW_ALL_MODIFIER_OPS}, {"ExternalResourceCreationOptions", &EXTERNAL_RESOURCE_CREATION_OPTIONS_OPS}, + {"AttachToExternalResource", &ATTACH_TO_EXTERNAL_RESOURCE_OPS}, + {"ConnectToExternalResource", &CONNECT_TO_EXTERNAL_RESOURCE_OPS}, + {"ExternalResourceSource", &EXTERNAL_RESOURCE_SOURCE_OPS}, + {"ExternalResourceCreateClause", &EXTERNAL_RESOURCE_CREATE_CLAUSE_OPS}, + {"ExternalResourceReferenceClause", &EXTERNAL_RESOURCE_REFERENCE_CLAUSE_OPS}, {"InsertStatement", &INSERT_STATEMENT_OPS}, {"OrAction", &OR_ACTION_OPS}, {"InsertOrReplace", &INSERT_OR_REPLACE_OPS}, @@ -4016,6 +4185,7 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"LimitOffsetClause", &LIMIT_OFFSET_CLAUSE_OPS}, {"OffsetLimitClause", &OFFSET_LIMIT_CLAUSE_OPS}, {"OffsetFetchClause", &OFFSET_FETCH_CLAUSE_OPS}, + {"FetchOffsetClause", &FETCH_OFFSET_CLAUSE_OPS}, {"FetchOnlyClause", &FETCH_ONLY_CLAUSE_OPS}, {"TableStatement", &TABLE_STATEMENT_OPS}, {"OptionalParensSimpleSelect", &OPTIONAL_PARENS_SIMPLE_SELECT_OPS}, @@ -4106,6 +4276,7 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"JoinQualifier", &JOIN_QUALIFIER_OPS}, {"OnClause", &ON_CLAUSE_OPS}, {"UsingClause", &USING_CLAUSE_OPS}, + {"UsingColumnName", &USING_COLUMN_NAME_OPS}, {"JoinType", &JOIN_TYPE_OPS}, {"JoinPrefix", &JOIN_PREFIX_OPS}, {"CrossJoinPrefix", &CROSS_JOIN_PREFIX_OPS}, @@ -4123,6 +4294,7 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"HavingClause", &HAVING_CLAUSE_OPS}, {"QualifyClause", &QUALIFY_CLAUSE_OPS}, {"SampleClause", &SAMPLE_CLAUSE_OPS}, + {"SelectSampleClause", &SELECT_SAMPLE_CLAUSE_OPS}, {"WindowClause", &WINDOW_CLAUSE_OPS}, {"WindowDefinition", &WINDOW_DEFINITION_OPS}, {"SampleEntry", &SAMPLE_ENTRY_OPS}, @@ -4168,6 +4340,8 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"LimitLiteralPercent", &LIMIT_LITERAL_PERCENT_OPS}, {"LimitExpression", &LIMIT_EXPRESSION_OPS}, {"FetchClause", &FETCH_CLAUSE_OPS}, + {"FetchClauseWithoutValue", &FETCH_CLAUSE_WITHOUT_VALUE_OPS}, + {"FetchClauseWithValue", &FETCH_CLAUSE_WITH_VALUE_OPS}, {"FetchValue", &FETCH_VALUE_OPS}, {"AliasedExpression", &ALIASED_EXPRESSION_OPS}, {"ColIdExpression", &COL_ID_EXPRESSION_OPS}, @@ -4211,9 +4385,9 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"BaseTableSet", &BASE_TABLE_SET_OPS}, {"BaseTableAliasSet", &BASE_TABLE_ALIAS_SET_OPS}, {"UpdateAlias", &UPDATE_ALIAS_OPS}, + {"UpdateSetClauseList", &UPDATE_SET_CLAUSE_LIST_OPS}, {"UpdateSetClause", &UPDATE_SET_CLAUSE_OPS}, {"UpdateSetTuple", &UPDATE_SET_TUPLE_OPS}, - {"UpdateSetElementList", &UPDATE_SET_ELEMENT_LIST_OPS}, {"UpdateSetElement", &UPDATE_SET_ELEMENT_OPS}, {"UpdateSetColumnTarget", &UPDATE_SET_COLUMN_TARGET_OPS}, {"UseStatement", &USE_STATEMENT_OPS}, @@ -4244,19 +4418,31 @@ void PEGTransformerFactory::InitializeStatementTrampoline(PEGTransformer &transf process.ReserveChildSlots(1); auto child_rule = choice_result.GetRule(); if (!child_rule) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeStatementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); if (!transformer.named_parameter_map.empty()) { result->named_param_map = transformer.named_parameter_map; } result->has_anonymous_parameters = transformer.has_anonymous_parameters; - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); +} + +void PEGTransformerFactory::InitializeIdentifierOrKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + process.ReserveChildSlots(0); +} + +arena_ptr +PEGTransformerFactory::FinalizeIdentifierOrKeywordTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto result = TransformIdentifierOrKeyword(transformer, process.parse_result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeAlterStatementTrampoline(PEGTransformer &transformer, @@ -4266,12 +4452,12 @@ void PEGTransformerFactory::InitializeAlterStatementTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("AlterOptions"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAlterStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto alter_options = process.TakeResult>(0); auto result = TransformAlterStatement(transformer, std::move(alter_options)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAlterOptionsTrampoline(PEGTransformer &transformer, @@ -4283,15 +4469,15 @@ void PEGTransformerFactory::InitializeAlterOptionsTrampoline(PEGTransformer &tra auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAlterOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAlterTableStmtTrampoline(PEGTransformer &transformer, @@ -4311,7 +4497,7 @@ void PEGTransformerFactory::InitializeAlterTableStmtTrampoline(PEGTransformer &t } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAlterTableStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -4328,14 +4514,14 @@ PEGTransformerFactory::FinalizeAlterTableStmtTrampoline(PEGTransformer &transfor } auto result = TransformAlterTableStmt(transformer, if_exists, std::move(base_table_name), std::move(alter_table_options)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAlterSchemaStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(3); - process.PushChild({transformer.GetRule("RenameAlter"), list_pr.GetChild(3)}, 2); + process.PushChild({transformer.GetRule("AlterSchemaOptions"), list_pr.GetChild(3)}, 2); process.PushChild({transformer.GetRule("QualifiedName"), list_pr.GetChild(2)}, 1); auto &if_exists_opt = list_pr.GetChild(1).Cast(); if (if_exists_opt.HasResult()) { @@ -4343,7 +4529,7 @@ void PEGTransformerFactory::InitializeAlterSchemaStmtTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAlterSchemaStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional if_exists {}; @@ -4351,9 +4537,30 @@ PEGTransformerFactory::FinalizeAlterSchemaStmtTrampoline(PEGTransformer &transfo if_exists = process.TakeResult(0); } auto qualified_name = process.TakeResult(1); - auto rename_alter = process.TakeResult>(2); - auto result = TransformAlterSchemaStmt(transformer, if_exists, qualified_name, std::move(rename_alter)); - return make_uniq>>(std::move(result)); + auto alter_schema_options = process.TakeResult>(2); + auto result = TransformAlterSchemaStmt(transformer, if_exists, qualified_name, std::move(alter_schema_options)); + return transformer.MakeResult>(std::move(result)); +} + +void PEGTransformerFactory::InitializeAlterSchemaOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + auto &choice_pr = list_pr.Child(0); + auto &choice_result = choice_pr.GetResult(); + process.ReserveChildSlots(1); + auto child_rule = choice_result.GetRule(); + auto has_transform_process = child_rule && child_rule->transform_process; + if (!has_transform_process) { + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); + } + process.PushChild({*child_rule, choice_result}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeAlterSchemaOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto result = process.TakeResult>(0); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAlterTableOptionsTrampoline(PEGTransformer &transformer, @@ -4365,16 +4572,16 @@ void PEGTransformerFactory::InitializeAlterTableOptionsTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAlterTableOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAddConstraintTrampoline(PEGTransformer &transformer, @@ -4384,43 +4591,43 @@ void PEGTransformerFactory::InitializeAddConstraintTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("TopLevelConstraint"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAddConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto top_level_constraint = process.TakeResult>(0); auto result = TransformAddConstraint(transformer, std::move(top_level_constraint)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDropConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(2); + process.ReserveChildSlots(3); auto &drop_behavior_opt = list_pr.GetChild(4).Cast(); if (drop_behavior_opt.HasResult()) { - process.PushChild({transformer.GetRule("DropBehavior"), drop_behavior_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule("DropBehavior"), drop_behavior_opt.GetResult()}, 2); } + process.PushChild({transformer.GetRule("ConstraintName"), list_pr.GetChild(3)}, 1); auto &if_exists_opt = list_pr.GetChild(2).Cast(); if (if_exists_opt.HasResult()) { process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 0); } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDropConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto &list_pr = process.parse_result.Cast(); optional if_exists {}; if (process.child_results[0]) { if_exists = process.TakeResult(0); } - auto identifier = list_pr.GetChild(3).Cast().identifier; + auto constraint_name = process.TakeResult(1); optional drop_behavior {}; - if (process.child_results[1]) { - drop_behavior = process.TakeResult(1); + if (process.child_results[2]) { + drop_behavior = process.TakeResult(2); } - auto result = TransformDropConstraint(transformer, if_exists, identifier, drop_behavior); - return make_uniq>>(std::move(result)); + auto result = TransformDropConstraint(transformer, if_exists, constraint_name, drop_behavior); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAddColumnTrampoline(PEGTransformer &transformer, @@ -4434,8 +4641,8 @@ void PEGTransformerFactory::InitializeAddColumnTrampoline(PEGTransformer &transf } } -unique_ptr -PEGTransformerFactory::FinalizeAddColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeAddColumnTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); bool has_result {}; auto &has_result_opt = list_pr.GetChild(1).Cast(); @@ -4446,7 +4653,7 @@ PEGTransformerFactory::FinalizeAddColumnTrampoline(PEGTransformer &transformer, } auto add_column_entry = process.TakeResult(1); auto result = TransformAddColumn(transformer, has_result, if_not_exists, std::move(add_column_entry)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAddColumnEntryTrampoline(PEGTransformer &transformer, @@ -4478,7 +4685,7 @@ void PEGTransformerFactory::InitializeAddColumnEntryTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("DottedIdentifier"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAddColumnEntryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -4508,7 +4715,7 @@ PEGTransformerFactory::FinalizeAddColumnEntryTrampoline(PEGTransformer &transfor } auto result = TransformAddColumnEntry(transformer, dotted_identifier, type, std::move(generated_column), std::move(column_constraint)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeDropColumnTrampoline(PEGTransformer &transformer, @@ -4526,7 +4733,7 @@ void PEGTransformerFactory::InitializeDropColumnTrampoline(PEGTransformer &trans } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDropColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); bool has_result {}; @@ -4542,7 +4749,7 @@ PEGTransformerFactory::FinalizeDropColumnTrampoline(PEGTransformer &transformer, drop_behavior = process.TakeResult(2); } auto result = TransformDropColumn(transformer, has_result, if_exists, std::move(nested_column_name), drop_behavior); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAlterColumnTrampoline(PEGTransformer &transformer, @@ -4553,7 +4760,7 @@ void PEGTransformerFactory::InitializeAlterColumnTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("NestedColumnName"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAlterColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); bool has_result {}; @@ -4563,26 +4770,27 @@ PEGTransformerFactory::FinalizeAlterColumnTrampoline(PEGTransformer &transformer auto alter_column_entry = process.TakeResult>(1); auto result = TransformAlterColumn(transformer, has_result, std::move(nested_column_name), std::move(alter_column_entry)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeRenameColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(1); + process.ReserveChildSlots(2); + process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(4)}, 1); process.PushChild({transformer.GetRule("NestedColumnName"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRenameColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); bool has_result {}; auto &has_result_opt = list_pr.GetChild(1).Cast(); has_result = has_result_opt.HasResult(); auto nested_column_name = process.TakeResult>(0); - auto identifier = list_pr.GetChild(4).Cast().identifier; - auto result = TransformRenameColumn(transformer, has_result, std::move(nested_column_name), identifier); - return make_uniq>>(std::move(result)); + auto col_id_or_string = process.TakeResult(1); + auto result = TransformRenameColumn(transformer, has_result, std::move(nested_column_name), col_id_or_string); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNestedColumnNameTrampoline(PEGTransformer &transformer, @@ -4604,7 +4812,7 @@ void PEGTransformerFactory::InitializeNestedColumnNameTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNestedColumnNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -4625,7 +4833,7 @@ PEGTransformerFactory::FinalizeNestedColumnNameTrampoline(PEGTransformer &transf } auto column_name = list_pr.GetChild(1).Cast().identifier; auto result = TransformNestedColumnName(transformer, identifier_dot, column_name); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIdentifierDotTrampoline(PEGTransformer &transformer, @@ -4633,26 +4841,27 @@ void PEGTransformerFactory::InitializeIdentifierDotTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIdentifierDotTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto identifier = list_pr.GetChild(0).Cast().identifier; auto result = TransformIdentifierDot(transformer, identifier); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeRenameAlterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - process.ReserveChildSlots(0); + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRenameAlterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto &list_pr = process.parse_result.Cast(); - auto identifier = list_pr.GetChild(2).Cast().identifier; - auto result = TransformRenameAlter(transformer, identifier); - return make_uniq>>(std::move(result)); + auto col_id_or_string = process.TakeResult(0); + auto result = TransformRenameAlter(transformer, col_id_or_string); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSetPartitionedByTrampoline(PEGTransformer &transformer, @@ -4667,7 +4876,7 @@ void PEGTransformerFactory::InitializeSetPartitionedByTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSetPartitionedByTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -4678,7 +4887,7 @@ PEGTransformerFactory::FinalizeSetPartitionedByTrampoline(PEGTransformer &transf expression.push_back(process.TakeResult>(i)); } auto result = TransformSetPartitionedBy(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeResetPartitionedByTrampoline(PEGTransformer &transformer, @@ -4686,11 +4895,11 @@ void PEGTransformerFactory::InitializeResetPartitionedByTrampoline(PEGTransforme process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeResetPartitionedByTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformResetPartitionedBy(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSetSortedByTrampoline(PEGTransformer &transformer, @@ -4700,11 +4909,11 @@ void PEGTransformerFactory::InitializeSetSortedByTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("OrderByExpressions"), ExtractResultFromParens(list_pr.GetChild(3))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSetSortedByTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto order_by_expressions = process.TakeResult>(0); auto result = TransformSetSortedBy(transformer, std::move(order_by_expressions)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeResetSortedByTrampoline(PEGTransformer &transformer, @@ -4712,11 +4921,11 @@ void PEGTransformerFactory::InitializeResetSortedByTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeResetSortedByTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformResetSortedBy(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSetOptionsTrampoline(PEGTransformer &transformer, @@ -4726,11 +4935,11 @@ void PEGTransformerFactory::InitializeSetOptionsTrampoline(PEGTransformer &trans process.PushChild({transformer.GetRule("RelOptionList"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSetOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto rel_option_list = process.TakeResult>>(0); auto result = TransformSetOptions(transformer, std::move(rel_option_list)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeResetOptionsTrampoline(PEGTransformer &transformer, @@ -4740,11 +4949,11 @@ void PEGTransformerFactory::InitializeResetOptionsTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("RelOptionList"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeResetOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto rel_option_list = process.TakeResult>>(0); auto result = TransformResetOptions(transformer, std::move(rel_option_list)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAlterColumnEntryTrampoline(PEGTransformer &transformer, @@ -4756,16 +4965,16 @@ void PEGTransformerFactory::InitializeAlterColumnEntryTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAlterColumnEntryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAddOrDropDefaultTrampoline(PEGTransformer &transformer, @@ -4777,16 +4986,16 @@ void PEGTransformerFactory::InitializeAddOrDropDefaultTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAddOrDropDefaultTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAddDefaultTrampoline(PEGTransformer &transformer, @@ -4796,11 +5005,11 @@ void PEGTransformerFactory::InitializeAddDefaultTrampoline(PEGTransformer &trans process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAddDefaultTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformAddDefault(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDropDefaultTrampoline(PEGTransformer &transformer, @@ -4808,10 +5017,10 @@ void PEGTransformerFactory::InitializeDropDefaultTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDropDefaultTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformDropDefault(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeChangeNullabilityTrampoline(PEGTransformer &transformer, @@ -4821,12 +5030,12 @@ void PEGTransformerFactory::InitializeChangeNullabilityTrampoline(PEGTransformer process.PushChild({transformer.GetRule("DropOrSet"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeChangeNullabilityTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto drop_or_set = process.TakeResult(0); auto result = TransformChangeNullability(transformer, drop_or_set); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDropOrSetTrampoline(PEGTransformer &transformer, @@ -4842,13 +5051,13 @@ void PEGTransformerFactory::InitializeDropOrSetTrampoline(PEGTransformer &transf return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeDropOrSetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeDropOrSetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { string result; if (process.child_results[0]) { result = process.TakeResult(0); @@ -4867,7 +5076,7 @@ PEGTransformerFactory::FinalizeDropOrSetTrampoline(PEGTransformer &transformer, result = transformer.Transform(choice_result); } } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDropNullabilityTrampoline(PEGTransformer &transformer, @@ -4875,11 +5084,11 @@ void PEGTransformerFactory::InitializeDropNullabilityTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDropNullabilityTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformDropNullability(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSetNullabilityTrampoline(PEGTransformer &transformer, @@ -4887,20 +5096,24 @@ void PEGTransformerFactory::InitializeSetNullabilityTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSetNullabilityTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformSetNullability(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeAlterTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(2); - auto &using_expression_opt = list_pr.GetChild(3).Cast(); + process.ReserveChildSlots(3); + auto &using_expression_opt = list_pr.GetChild(4).Cast(); if (using_expression_opt.HasResult()) { - process.PushChild({transformer.GetRule("UsingExpression"), using_expression_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule("UsingExpression"), using_expression_opt.GetResult()}, 2); + } + auto &column_collation_opt = list_pr.GetChild(3).Cast(); + if (column_collation_opt.HasResult()) { + process.PushChild({transformer.GetRule("ColumnCollation"), column_collation_opt.GetResult()}, 1); } auto &type_opt = list_pr.GetChild(2).Cast(); if (type_opt.HasResult()) { @@ -4908,8 +5121,8 @@ void PEGTransformerFactory::InitializeAlterTypeTrampoline(PEGTransformer &transf } } -unique_ptr -PEGTransformerFactory::FinalizeAlterTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeAlterTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); bool has_result {}; auto &has_result_opt = list_pr.GetChild(0).Cast(); @@ -4918,12 +5131,17 @@ PEGTransformerFactory::FinalizeAlterTypeTrampoline(PEGTransformer &transformer, if (process.child_results[0]) { type = process.TakeResult(0); } - optional> using_expression {}; + optional column_collation {}; if (process.child_results[1]) { - using_expression = process.TakeResult>(1); + column_collation = process.TakeResult(1); + } + optional> using_expression {}; + if (process.child_results[2]) { + using_expression = process.TakeResult>(2); } - auto result = TransformAlterType(transformer, has_result, type, std::move(using_expression)); - return make_uniq>>(std::move(result)); + auto result = + TransformAlterType(transformer, has_result, type, std::move(column_collation), std::move(using_expression)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUsingExpressionTrampoline(PEGTransformer &transformer, @@ -4933,12 +5151,12 @@ void PEGTransformerFactory::InitializeUsingExpressionTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUsingExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformUsingExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAlterViewStmtTrampoline(PEGTransformer &transformer, @@ -4953,7 +5171,7 @@ void PEGTransformerFactory::InitializeAlterViewStmtTrampoline(PEGTransformer &tr } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAlterViewStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional if_exists {}; @@ -4963,7 +5181,7 @@ PEGTransformerFactory::FinalizeAlterViewStmtTrampoline(PEGTransformer &transform auto base_table_name = process.TakeResult>(1); auto rename_alter = process.TakeResult>(2); auto result = TransformAlterViewStmt(transformer, if_exists, std::move(base_table_name), std::move(rename_alter)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAlterSequenceStmtTrampoline(PEGTransformer &transformer, @@ -4978,7 +5196,7 @@ void PEGTransformerFactory::InitializeAlterSequenceStmtTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAlterSequenceStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional if_exists {}; @@ -4989,7 +5207,7 @@ PEGTransformerFactory::FinalizeAlterSequenceStmtTrampoline(PEGTransformer &trans auto alter_sequence_options = process.TakeResult>(2); auto result = TransformAlterSequenceStmt(transformer, if_exists, qualified_sequence_name, std::move(alter_sequence_options)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeQualifiedSequenceNameTrampoline(PEGTransformer &transformer, @@ -5006,7 +5224,7 @@ void PEGTransformerFactory::InitializeQualifiedSequenceNameTrampoline(PEGTransfo } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQualifiedSequenceNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -5021,7 +5239,7 @@ PEGTransformerFactory::FinalizeQualifiedSequenceNameTrampoline(PEGTransformer &t auto sequence_name = list_pr.GetChild(2).Cast().identifier; auto result = TransformQualifiedSequenceName(transformer, catalog_qualification, schema_qualification, sequence_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeAlterSequenceOptionsTrampoline(PEGTransformer &transformer, @@ -5033,16 +5251,16 @@ void PEGTransformerFactory::InitializeAlterSequenceOptionsTrampoline(PEGTransfor auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAlterSequenceOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeRenameAlterSequenceOptionsTrampoline(PEGTransformer &transformer, @@ -5052,12 +5270,12 @@ void PEGTransformerFactory::InitializeRenameAlterSequenceOptionsTrampoline(PEGTr process.PushChild({transformer.GetRule("RenameAlter"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRenameAlterSequenceOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto rename_alter = process.TakeResult>(0); auto result = TransformRenameAlterSequenceOptions(transformer, std::move(rename_alter)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSetSequenceOptionTrampoline(PEGTransformer &transformer, @@ -5073,7 +5291,7 @@ void PEGTransformerFactory::InitializeSetSequenceOptionTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSetSequenceOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -5085,7 +5303,7 @@ PEGTransformerFactory::FinalizeSetSequenceOptionTrampoline(PEGTransformer &trans sequence_option.push_back(process.TakeResult>>(i)); } auto result = TransformSetSequenceOption(transformer, std::move(sequence_option)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAlterDatabaseStmtTrampoline(PEGTransformer &transformer, @@ -5098,7 +5316,7 @@ void PEGTransformerFactory::InitializeAlterDatabaseStmtTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAlterDatabaseStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -5109,7 +5327,7 @@ PEGTransformerFactory::FinalizeAlterDatabaseStmtTrampoline(PEGTransformer &trans auto identifier = list_pr.GetChild(2).Cast().identifier; auto identifier_1 = list_pr.GetChild(6).Cast().identifier; auto result = TransformAlterDatabaseStmt(transformer, if_exists, identifier, identifier_1); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAnalyzeStatementTrampoline(PEGTransformer &transformer, @@ -5127,7 +5345,7 @@ void PEGTransformerFactory::InitializeAnalyzeStatementTrampoline(PEGTransformer process.PushChild({transformer.GetRule("AnalyzeKeyword"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAnalyzeStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto analyze_keyword = process.TakeResult(0); @@ -5140,7 +5358,7 @@ PEGTransformerFactory::FinalizeAnalyzeStatementTrampoline(PEGTransformer &transf analyze_target = process.TakeResult(2); } auto result = TransformAnalyzeStatement(transformer, analyze_keyword, analyze_verbose, std::move(analyze_target)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAnalyzeTargetTrampoline(PEGTransformer &transformer, @@ -5154,7 +5372,7 @@ void PEGTransformerFactory::InitializeAnalyzeTargetTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAnalyzeTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto base_table_name = process.TakeResult>(0); @@ -5163,7 +5381,7 @@ PEGTransformerFactory::FinalizeAnalyzeTargetTrampoline(PEGTransformer &transform name_list = process.TakeResult>(1); } auto result = TransformAnalyzeTarget(transformer, std::move(base_table_name), name_list); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeAnalyzeVerboseTrampoline(PEGTransformer &transformer, @@ -5171,11 +5389,11 @@ void PEGTransformerFactory::InitializeAnalyzeVerboseTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAnalyzeVerboseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformAnalyzeVerbose(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeAttachStatementTrampoline(PEGTransformer &transformer, @@ -5201,7 +5419,7 @@ void PEGTransformerFactory::InitializeAttachStatementTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAttachStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -5227,7 +5445,7 @@ PEGTransformerFactory::FinalizeAttachStatementTrampoline(PEGTransformer &transfo } auto result = TransformAttachStatement(transformer, or_replace, if_not_exists, has_result, std::move(database_path), attach_alias, attach_options); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDatabasePathTrampoline(PEGTransformer &transformer, @@ -5237,11 +5455,11 @@ void PEGTransformerFactory::InitializeDatabasePathTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDatabasePathTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformDatabasePath(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAttachAliasTrampoline(PEGTransformer &transformer, @@ -5251,11 +5469,11 @@ void PEGTransformerFactory::InitializeAttachAliasTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAttachAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id = process.TakeResult(0); auto result = TransformAttachAlias(transformer, col_id); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeAttachOptionsTrampoline(PEGTransformer &transformer, @@ -5265,12 +5483,12 @@ void PEGTransformerFactory::InitializeAttachOptionsTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("GenericCopyOptionList"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAttachOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto generic_copy_option_list = process.TakeResult>(0); auto result = TransformAttachOptions(transformer, generic_copy_option_list); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeCallStatementTrampoline(PEGTransformer &transformer, @@ -5281,13 +5499,13 @@ void PEGTransformerFactory::InitializeCallStatementTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("QualifiedTableFunction"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCallStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto qualified_table_function = process.TakeResult(0); auto table_function_arguments = process.TakeResult>(1); auto result = TransformCallStatement(transformer, qualified_table_function, std::move(table_function_arguments)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCheckpointStatementTrampoline(PEGTransformer &transformer, @@ -5300,7 +5518,7 @@ void PEGTransformerFactory::InitializeCheckpointStatementTrampoline(PEGTransform } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCheckpointStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -5314,7 +5532,7 @@ PEGTransformerFactory::FinalizeCheckpointStatementTrampoline(PEGTransformer &tra catalog_name = catalog_name_opt.GetResult().Cast().identifier; } auto result = TransformCheckpointStatement(transformer, checkpoint_force, catalog_name); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCheckpointForceTrampoline(PEGTransformer &transformer, @@ -5322,11 +5540,11 @@ void PEGTransformerFactory::InitializeCheckpointForceTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCheckpointForceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCheckpointForce(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCommentStatementTrampoline(PEGTransformer &transformer, @@ -5334,18 +5552,54 @@ void PEGTransformerFactory::InitializeCommentStatementTrampoline(PEGTransformer auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(3); process.PushChild({transformer.GetRule("CommentValue"), list_pr.GetChild(5)}, 2); - process.PushChild({transformer.GetRule("DottedIdentifier"), list_pr.GetChild(3)}, 1); + process.PushChild({transformer.GetRule("CommentTarget"), list_pr.GetChild(3)}, 1); process.PushChild({transformer.GetRule("CommentOnType"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCommentStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto comment_on_type = process.TakeResult(0); - auto dotted_identifier = process.TakeResult>(1); + auto comment_target = process.TakeResult>(1); auto comment_value = process.TakeResult(2); - auto result = TransformCommentStatement(transformer, comment_on_type, dotted_identifier, comment_value); - return make_uniq>>(std::move(result)); + auto result = TransformCommentStatement(transformer, comment_on_type, comment_target, comment_value); + return transformer.MakeResult>(std::move(result)); +} + +void PEGTransformerFactory::InitializeCommentTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + auto &choice_pr = list_pr.Child(0); + auto &choice_result = choice_pr.GetResult(); + process.ReserveChildSlots(1); + auto child_rule = choice_result.GetRule(); + auto has_transform_process = child_rule && child_rule->transform_process; + if (!has_transform_process) { + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); + } + process.PushChild({*child_rule, choice_result}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeCommentTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto result = process.TakeResult>(0); + return transformer.MakeResult>(result); +} + +void PEGTransformerFactory::InitializeCommentStringLiteralIdentifierTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + process.PushChild({transformer.GetRule("StringLiteralIdentifier"), list_pr.GetChild(0)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeCommentStringLiteralIdentifierTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto string_literal_identifier = process.TakeResult(0); + auto result = TransformCommentStringLiteralIdentifier(transformer, string_literal_identifier); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeCommentOnTypeTrampoline(PEGTransformer &transformer, @@ -5357,16 +5611,16 @@ void PEGTransformerFactory::InitializeCommentOnTypeTrampoline(PEGTransformer &tr auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCommentOnTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCommentTableTrampoline(PEGTransformer &transformer, @@ -5374,10 +5628,10 @@ void PEGTransformerFactory::InitializeCommentTableTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCommentTableTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCommentTable(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCommentSequenceTrampoline(PEGTransformer &transformer, @@ -5385,11 +5639,11 @@ void PEGTransformerFactory::InitializeCommentSequenceTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCommentSequenceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCommentSequence(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCommentFunctionTrampoline(PEGTransformer &transformer, @@ -5397,11 +5651,11 @@ void PEGTransformerFactory::InitializeCommentFunctionTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCommentFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCommentFunction(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCommentMacroTableTrampoline(PEGTransformer &transformer, @@ -5409,11 +5663,11 @@ void PEGTransformerFactory::InitializeCommentMacroTableTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCommentMacroTableTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCommentMacroTable(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCommentMacroTrampoline(PEGTransformer &transformer, @@ -5421,10 +5675,10 @@ void PEGTransformerFactory::InitializeCommentMacroTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCommentMacroTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCommentMacro(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCommentViewTrampoline(PEGTransformer &transformer, @@ -5432,10 +5686,10 @@ void PEGTransformerFactory::InitializeCommentViewTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCommentViewTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCommentView(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCommentDatabaseTrampoline(PEGTransformer &transformer, @@ -5443,11 +5697,11 @@ void PEGTransformerFactory::InitializeCommentDatabaseTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCommentDatabaseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCommentDatabase(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCommentIndexTrampoline(PEGTransformer &transformer, @@ -5455,10 +5709,10 @@ void PEGTransformerFactory::InitializeCommentIndexTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCommentIndexTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCommentIndex(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCommentSchemaTrampoline(PEGTransformer &transformer, @@ -5466,11 +5720,11 @@ void PEGTransformerFactory::InitializeCommentSchemaTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCommentSchemaTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCommentSchema(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCommentTypeTrampoline(PEGTransformer &transformer, @@ -5478,10 +5732,10 @@ void PEGTransformerFactory::InitializeCommentTypeTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCommentTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCommentType(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCommentColumnTrampoline(PEGTransformer &transformer, @@ -5489,11 +5743,11 @@ void PEGTransformerFactory::InitializeCommentColumnTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCommentColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCommentColumn(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCommentValueTrampoline(PEGTransformer &transformer, @@ -5505,15 +5759,15 @@ void PEGTransformerFactory::InitializeCommentValueTrampoline(PEGTransformer &tra auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCommentValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeStringLiteralValueTrampoline(PEGTransformer &transformer, @@ -5521,13 +5775,13 @@ void PEGTransformerFactory::InitializeStringLiteralValueTrampoline(PEGTransforme process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeStringLiteralValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(0)); auto result = TransformStringLiteralValue(transformer, string_literal); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeAnalyzeKeywordTrampoline(PEGTransformer &transformer, @@ -5535,58 +5789,11 @@ void PEGTransformerFactory::InitializeAnalyzeKeywordTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAnalyzeKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformAnalyzeKeyword(transformer); - return make_uniq>(result); -} - -void PEGTransformerFactory::InitializeExpressionStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { - auto &list_pr = process.parse_result.Cast(); - auto list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - auto dynamic_child_count = list_items.size(); - process.ReserveChildSlots(1 + dynamic_child_count - 1); - for (idx_t i = list_items.size(); i > 0; i--) { - auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ExpressionAlias"), list_items[child_idx].get()}, 0 + child_idx); - } -} - -unique_ptr -PEGTransformerFactory::FinalizeExpressionStatementTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { - auto &list_pr = process.parse_result.Cast(); - auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - auto dynamic_child_count = dynamic_list_items.size(); - vector> expression_alias; - for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { - expression_alias.push_back(process.TakeResult>(i)); - } - auto result = TransformExpressionStatement(transformer, std::move(expression_alias)); - return make_uniq>>(std::move(result)); -} - -void PEGTransformerFactory::InitializeExpressionAliasTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { - auto &list_pr = process.parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto &choice_result = choice_pr.GetResult(); - process.ReserveChildSlots(1); - auto child_rule = choice_result.GetRule(); - auto has_transform_process = child_rule && child_rule->transform_process; - if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); - } - process.PushChild({*child_rule, choice_result}, 0); -} - -unique_ptr -PEGTransformerFactory::FinalizeExpressionAliasTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { - auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeIndexNameTrampoline(PEGTransformer &transformer, @@ -5594,10 +5801,10 @@ void PEGTransformerFactory::InitializeIndexNameTrampoline(PEGTransformer &transf process.ReserveChildSlots(0); } -unique_ptr -PEGTransformerFactory::FinalizeIndexNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeIndexNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.parse_result.Cast().identifier.GetIdentifierName(); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeConstraintNameTrampoline(PEGTransformer &transformer, @@ -5607,12 +5814,12 @@ void PEGTransformerFactory::InitializeConstraintNameTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeConstraintNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id_or_string = process.TakeResult(0); auto result = TransformConstraintName(transformer, col_id_or_string); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSequenceNameTrampoline(PEGTransformer &transformer, @@ -5620,10 +5827,10 @@ void PEGTransformerFactory::InitializeSequenceNameTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSequenceNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.parse_result.Cast().identifier.GetIdentifierName(); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCollationNameTrampoline(PEGTransformer &transformer, @@ -5631,13 +5838,13 @@ void PEGTransformerFactory::InitializeCollationNameTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCollationNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto identifier = list_pr.GetChild(0).Cast().identifier; auto result = TransformCollationName(transformer, identifier); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeNumberLiteralTrampoline(PEGTransformer &transformer, @@ -5645,11 +5852,11 @@ void PEGTransformerFactory::InitializeNumberLiteralTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNumberLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformNumberLiteral(transformer, process.parse_result); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeStringLiteralTrampoline(PEGTransformer &transformer, @@ -5657,11 +5864,11 @@ void PEGTransformerFactory::InitializeStringLiteralTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformStringLiteral(transformer, process.parse_result); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { @@ -5683,8 +5890,8 @@ void PEGTransformerFactory::InitializeTypeTrampoline(PEGTransformer &transformer process.PushChild({transformer.GetRule("TypeVariations"), list_pr.GetChild(0)}, 0); } -unique_ptr PEGTransformerFactory::FinalizeTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); idx_t dynamic_child_count = 0; auto &dynamic_repeat_opt = list_pr.GetChild(1).Cast(); @@ -5703,7 +5910,7 @@ unique_ptr PEGTransformerFactory::FinalizeTypeTrampoline(P array_bounds = std::move(array_bounds_value); } auto result = TransformType(transformer, std::move(type_variations), array_bounds); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTypeVariationsTrampoline(PEGTransformer &transformer, @@ -5715,16 +5922,16 @@ void PEGTransformerFactory::InitializeTypeVariationsTrampoline(PEGTransformer &t auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTypeVariationsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSimpleTypeTrampoline(PEGTransformer &transformer, @@ -5736,15 +5943,15 @@ void PEGTransformerFactory::InitializeSimpleTypeTrampoline(PEGTransformer &trans auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSimpleTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCharacterSimpleTypeTrampoline(PEGTransformer &transformer, @@ -5757,7 +5964,7 @@ void PEGTransformerFactory::InitializeCharacterSimpleTypeTrampoline(PEGTransform } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCharacterSimpleTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional>> type_modifiers {}; @@ -5765,7 +5972,7 @@ PEGTransformerFactory::FinalizeCharacterSimpleTypeTrampoline(PEGTransformer &tra type_modifiers = process.TakeResult>>(0); } auto result = TransformCharacterSimpleType(transformer, std::move(type_modifiers)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeQualifiedSimpleTypeTrampoline(PEGTransformer &transformer, @@ -5779,7 +5986,7 @@ void PEGTransformerFactory::InitializeQualifiedSimpleTypeTrampoline(PEGTransform process.PushChild({transformer.GetRule("QualifiedTypeName"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQualifiedSimpleTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto qualified_type_name = process.TakeResult(0); @@ -5788,7 +5995,7 @@ PEGTransformerFactory::FinalizeQualifiedSimpleTypeTrampoline(PEGTransformer &tra type_modifiers = process.TakeResult>>(1); } auto result = TransformQualifiedSimpleType(transformer, qualified_type_name, std::move(type_modifiers)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIntervalTypeTrampoline(PEGTransformer &transformer, @@ -5800,15 +6007,15 @@ void PEGTransformerFactory::InitializeIntervalTypeTrampoline(PEGTransformer &tra auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIntervalTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIntervalIntervalTrampoline(PEGTransformer &transformer, @@ -5820,16 +6027,16 @@ void PEGTransformerFactory::InitializeIntervalIntervalTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIntervalIntervalTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIntervalWithSpecifierTrampoline(PEGTransformer &transformer, @@ -5841,16 +6048,16 @@ void PEGTransformerFactory::InitializeIntervalWithSpecifierTrampoline(PEGTransfo auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIntervalWithSpecifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIntervalWithRangeSpecifierTrampoline(PEGTransformer &transformer, @@ -5860,12 +6067,12 @@ void PEGTransformerFactory::InitializeIntervalWithRangeSpecifierTrampoline(PEGTr process.PushChild({transformer.GetRule("IntervalToIntervalAsType"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIntervalWithRangeSpecifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto interval_to_interval_as_type = process.TakeResult(0); auto result = TransformIntervalWithRangeSpecifier(transformer, interval_to_interval_as_type); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIntervalWithSimpleSpecifierTrampoline(PEGTransformer &transformer, @@ -5875,12 +6082,12 @@ void PEGTransformerFactory::InitializeIntervalWithSimpleSpecifierTrampoline(PEGT process.PushChild({transformer.GetRule("Interval"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIntervalWithSimpleSpecifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto interval = process.TakeResult(0); auto result = TransformIntervalWithSimpleSpecifier(transformer, interval); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIntervalWithoutSpecifierTrampoline(PEGTransformer &transformer, @@ -5888,11 +6095,11 @@ void PEGTransformerFactory::InitializeIntervalWithoutSpecifierTrampoline(PEGTran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIntervalWithoutSpecifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformIntervalWithoutSpecifier(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIntervalToIntervalAsTypeTrampoline(PEGTransformer &transformer, @@ -5909,7 +6116,7 @@ void PEGTransformerFactory::InitializeIntervalToIntervalAsTypeTrampoline(PEGTran process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIntervalToIntervalAsTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -5920,7 +6127,7 @@ PEGTransformerFactory::FinalizeIntervalToIntervalAsTypeTrampoline(PEGTransformer } else { result = TransformIntervalToIntervalAsType(transformer, choice_result); } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeYearKeywordTrampoline(PEGTransformer &transformer, @@ -5928,10 +6135,10 @@ void PEGTransformerFactory::InitializeYearKeywordTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeYearKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformYearKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeMonthKeywordTrampoline(PEGTransformer &transformer, @@ -5939,10 +6146,10 @@ void PEGTransformerFactory::InitializeMonthKeywordTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMonthKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformMonthKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDayKeywordTrampoline(PEGTransformer &transformer, @@ -5950,10 +6157,10 @@ void PEGTransformerFactory::InitializeDayKeywordTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDayKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformDayKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeHourKeywordTrampoline(PEGTransformer &transformer, @@ -5961,10 +6168,10 @@ void PEGTransformerFactory::InitializeHourKeywordTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeHourKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformHourKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeMinuteKeywordTrampoline(PEGTransformer &transformer, @@ -5972,11 +6179,11 @@ void PEGTransformerFactory::InitializeMinuteKeywordTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMinuteKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformMinuteKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSecondKeywordTrampoline(PEGTransformer &transformer, @@ -5984,11 +6191,11 @@ void PEGTransformerFactory::InitializeSecondKeywordTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSecondKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformSecondKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeMillisecondKeywordTrampoline(PEGTransformer &transformer, @@ -5996,11 +6203,11 @@ void PEGTransformerFactory::InitializeMillisecondKeywordTrampoline(PEGTransforme process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMillisecondKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformMillisecondKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeMicrosecondKeywordTrampoline(PEGTransformer &transformer, @@ -6008,11 +6215,11 @@ void PEGTransformerFactory::InitializeMicrosecondKeywordTrampoline(PEGTransforme process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMicrosecondKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformMicrosecondKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeWeekKeywordTrampoline(PEGTransformer &transformer, @@ -6020,10 +6227,10 @@ void PEGTransformerFactory::InitializeWeekKeywordTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWeekKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformWeekKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeQuarterKeywordTrampoline(PEGTransformer &transformer, @@ -6031,11 +6238,11 @@ void PEGTransformerFactory::InitializeQuarterKeywordTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQuarterKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformQuarterKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDecadeKeywordTrampoline(PEGTransformer &transformer, @@ -6043,11 +6250,11 @@ void PEGTransformerFactory::InitializeDecadeKeywordTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDecadeKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformDecadeKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCenturyKeywordTrampoline(PEGTransformer &transformer, @@ -6055,11 +6262,11 @@ void PEGTransformerFactory::InitializeCenturyKeywordTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCenturyKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCenturyKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeMillenniumKeywordTrampoline(PEGTransformer &transformer, @@ -6067,11 +6274,11 @@ void PEGTransformerFactory::InitializeMillenniumKeywordTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMillenniumKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformMillenniumKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeIntervalTrampoline(PEGTransformer &transformer, @@ -6083,15 +6290,15 @@ void PEGTransformerFactory::InitializeIntervalTrampoline(PEGTransformer &transfo auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr PEGTransformerFactory::FinalizeIntervalTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeIntervalTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeIntervalToIntervalTrampoline(PEGTransformer &transformer, @@ -6103,16 +6310,16 @@ void PEGTransformerFactory::InitializeIntervalToIntervalTrampoline(PEGTransforme auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIntervalToIntervalTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeYearToMonthTrampoline(PEGTransformer &transformer, @@ -6123,12 +6330,12 @@ void PEGTransformerFactory::InitializeYearToMonthTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("YearKeyword"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeYearToMonthTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto year_keyword = process.TakeResult(0); auto month_keyword = process.TakeResult(1); auto result = TransformYearToMonth(transformer, year_keyword, month_keyword); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDayToHourTrampoline(PEGTransformer &transformer, @@ -6139,12 +6346,12 @@ void PEGTransformerFactory::InitializeDayToHourTrampoline(PEGTransformer &transf process.PushChild({transformer.GetRule("DayKeyword"), list_pr.GetChild(0)}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeDayToHourTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeDayToHourTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto day_keyword = process.TakeResult(0); auto hour_keyword = process.TakeResult(1); auto result = TransformDayToHour(transformer, day_keyword, hour_keyword); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDayToMinuteTrampoline(PEGTransformer &transformer, @@ -6155,12 +6362,12 @@ void PEGTransformerFactory::InitializeDayToMinuteTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("DayKeyword"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDayToMinuteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto day_keyword = process.TakeResult(0); auto minute_keyword = process.TakeResult(1); auto result = TransformDayToMinute(transformer, day_keyword, minute_keyword); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDayToSecondTrampoline(PEGTransformer &transformer, @@ -6171,12 +6378,12 @@ void PEGTransformerFactory::InitializeDayToSecondTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("DayKeyword"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDayToSecondTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto day_keyword = process.TakeResult(0); auto second_keyword = process.TakeResult(1); auto result = TransformDayToSecond(transformer, day_keyword, second_keyword); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeHourToMinuteTrampoline(PEGTransformer &transformer, @@ -6187,12 +6394,12 @@ void PEGTransformerFactory::InitializeHourToMinuteTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("HourKeyword"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeHourToMinuteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto hour_keyword = process.TakeResult(0); auto minute_keyword = process.TakeResult(1); auto result = TransformHourToMinute(transformer, hour_keyword, minute_keyword); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeHourToSecondTrampoline(PEGTransformer &transformer, @@ -6203,12 +6410,12 @@ void PEGTransformerFactory::InitializeHourToSecondTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("HourKeyword"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeHourToSecondTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto hour_keyword = process.TakeResult(0); auto second_keyword = process.TakeResult(1); auto result = TransformHourToSecond(transformer, hour_keyword, second_keyword); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeMinuteToSecondTrampoline(PEGTransformer &transformer, @@ -6219,13 +6426,13 @@ void PEGTransformerFactory::InitializeMinuteToSecondTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("MinuteKeyword"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMinuteToSecondTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto minute_keyword = process.TakeResult(0); auto second_keyword = process.TakeResult(1); auto result = TransformMinuteToSecond(transformer, minute_keyword, second_keyword); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeBitTypeTrampoline(PEGTransformer &transformer, @@ -6246,8 +6453,8 @@ void PEGTransformerFactory::InitializeBitTypeTrampoline(PEGTransformer &transfor } } -unique_ptr PEGTransformerFactory::FinalizeBitTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeBitTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); idx_t dynamic_child_count = 0; auto &dynamic_list_opt = list_pr.GetChild(2).Cast(); @@ -6268,7 +6475,7 @@ unique_ptr PEGTransformerFactory::FinalizeBitTypeTrampolin expression = std::move(expression_value); } auto result = TransformBitType(transformer, has_result, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeGeometryTypeTrampoline(PEGTransformer &transformer, @@ -6281,14 +6488,14 @@ void PEGTransformerFactory::InitializeGeometryTypeTrampoline(PEGTransformer &tra } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGeometryTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional> expression {}; if (process.child_results[0]) { expression = process.TakeResult>(0); } auto result = TransformGeometryType(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeVariantTypeTrampoline(PEGTransformer &transformer, @@ -6296,10 +6503,10 @@ void PEGTransformerFactory::InitializeVariantTypeTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeVariantTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformVariantType(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNumericTypeTrampoline(PEGTransformer &transformer, @@ -6311,15 +6518,15 @@ void PEGTransformerFactory::InitializeNumericTypeTrampoline(PEGTransformer &tran auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNumericTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSimpleNumericTypeTrampoline(PEGTransformer &transformer, @@ -6337,12 +6544,12 @@ void PEGTransformerFactory::InitializeSimpleNumericTypeTrampoline(PEGTransformer return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSimpleNumericTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { string child; @@ -6364,7 +6571,7 @@ PEGTransformerFactory::FinalizeSimpleNumericTypeTrampoline(PEGTransformer &trans } } auto result = TransformSimpleNumericType(transformer, child); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDecimalNumericTypeTrampoline(PEGTransformer &transformer, @@ -6376,16 +6583,16 @@ void PEGTransformerFactory::InitializeDecimalNumericTypeTrampoline(PEGTransforme auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDecimalNumericTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIntTypeTrampoline(PEGTransformer &transformer, @@ -6393,10 +6600,10 @@ void PEGTransformerFactory::InitializeIntTypeTrampoline(PEGTransformer &transfor process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeIntTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeIntTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformIntType(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeIntegerTypeTrampoline(PEGTransformer &transformer, @@ -6404,10 +6611,10 @@ void PEGTransformerFactory::InitializeIntegerTypeTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIntegerTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformIntegerType(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSmallintTypeTrampoline(PEGTransformer &transformer, @@ -6415,10 +6622,10 @@ void PEGTransformerFactory::InitializeSmallintTypeTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSmallintTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformSmallintType(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeBigintTypeTrampoline(PEGTransformer &transformer, @@ -6426,10 +6633,10 @@ void PEGTransformerFactory::InitializeBigintTypeTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBigintTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformBigintType(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeRealTypeTrampoline(PEGTransformer &transformer, @@ -6437,10 +6644,10 @@ void PEGTransformerFactory::InitializeRealTypeTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeRealTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeRealTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformRealType(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeBooleanTypeTrampoline(PEGTransformer &transformer, @@ -6448,10 +6655,10 @@ void PEGTransformerFactory::InitializeBooleanTypeTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBooleanTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformBooleanType(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDoubleTypeTrampoline(PEGTransformer &transformer, @@ -6459,10 +6666,10 @@ void PEGTransformerFactory::InitializeDoubleTypeTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDoubleTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformDoubleType(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeFloatTypeTrampoline(PEGTransformer &transformer, @@ -6470,8 +6677,8 @@ void PEGTransformerFactory::InitializeFloatTypeTrampoline(PEGTransformer &transf process.ReserveChildSlots(0); } -unique_ptr -PEGTransformerFactory::FinalizeFloatTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeFloatTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); optional> number_literal {}; auto &number_literal_opt = list_pr.GetChild(1).Cast(); @@ -6479,7 +6686,7 @@ PEGTransformerFactory::FinalizeFloatTypeTrampoline(PEGTransformer &transformer, number_literal = TransformNumberLiteral(transformer, ExtractResultFromParens(number_literal_opt.GetResult())); } auto result = TransformFloatType(transformer, std::move(number_literal)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDecimalTypeTrampoline(PEGTransformer &transformer, @@ -6492,14 +6699,14 @@ void PEGTransformerFactory::InitializeDecimalTypeTrampoline(PEGTransformer &tran } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDecimalTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional>> type_modifiers {}; if (process.child_results[0]) { type_modifiers = process.TakeResult>>(0); } auto result = TransformDecimalType(transformer, std::move(type_modifiers)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDecTypeTrampoline(PEGTransformer &transformer, @@ -6512,14 +6719,14 @@ void PEGTransformerFactory::InitializeDecTypeTrampoline(PEGTransformer &transfor } } -unique_ptr PEGTransformerFactory::FinalizeDecTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeDecTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { optional>> type_modifiers {}; if (process.child_results[0]) { type_modifiers = process.TakeResult>>(0); } auto result = TransformDecType(transformer, std::move(type_modifiers)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNumericModTypeTrampoline(PEGTransformer &transformer, @@ -6532,7 +6739,7 @@ void PEGTransformerFactory::InitializeNumericModTypeTrampoline(PEGTransformer &t } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNumericModTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional>> type_modifiers {}; @@ -6540,7 +6747,7 @@ PEGTransformerFactory::FinalizeNumericModTypeTrampoline(PEGTransformer &transfor type_modifiers = process.TakeResult>>(0); } auto result = TransformNumericModType(transformer, std::move(type_modifiers)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeQualifiedTypeNameTrampoline(PEGTransformer &transformer, @@ -6552,16 +6759,16 @@ void PEGTransformerFactory::InitializeQualifiedTypeNameTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQualifiedTypeNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTypeNameAsQualifiedNameTrampoline(PEGTransformer &transformer, @@ -6569,13 +6776,13 @@ void PEGTransformerFactory::InitializeTypeNameAsQualifiedNameTrampoline(PEGTrans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTypeNameAsQualifiedNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto type_name = list_pr.GetChild(0).Cast().identifier; auto result = TransformTypeNameAsQualifiedName(transformer, type_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCatalogReservedSchemaTypeNameTrampoline(PEGTransformer &transformer, @@ -6593,7 +6800,7 @@ void PEGTransformerFactory::InitializeCatalogReservedSchemaTypeNameTrampoline(PE process.PushChild({transformer.GetRule("CatalogQualification"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCatalogReservedSchemaTypeNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -6608,7 +6815,7 @@ PEGTransformerFactory::FinalizeCatalogReservedSchemaTypeNameTrampoline(PEGTransf auto reserved_type_name = list_pr.GetChild(2).Cast().identifier; auto result = TransformCatalogReservedSchemaTypeName(transformer, catalog_qualification, reserved_schema_qualification, reserved_type_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSchemaReservedTypeNameTrampoline(PEGTransformer &transformer, @@ -6618,14 +6825,14 @@ void PEGTransformerFactory::InitializeSchemaReservedTypeNameTrampoline(PEGTransf process.PushChild({transformer.GetRule("SchemaQualification"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSchemaReservedTypeNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto schema_qualification = process.TakeResult(0); auto reserved_type_name = list_pr.GetChild(1).Cast().identifier; auto result = TransformSchemaReservedTypeName(transformer, schema_qualification, reserved_type_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTypeModifiersTrampoline(PEGTransformer &transformer, @@ -6646,7 +6853,7 @@ void PEGTransformerFactory::InitializeTypeModifiersTrampoline(PEGTransformer &tr } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTypeModifiersTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -6666,7 +6873,50 @@ PEGTransformerFactory::FinalizeTypeModifiersTrampoline(PEGTransformer &transform expression = std::move(expression_value); } auto result = TransformTypeModifiers(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); +} + +void PEGTransformerFactory::InitializeNestedTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + auto &repeat_opt = list_pr.GetChild(1).Cast(); + idx_t dynamic_child_count = 0; + if (repeat_opt.HasResult()) { + auto &repeat_pr = repeat_opt.GetResult().Cast(); + auto repeat_children = repeat_pr.GetChildren(); + dynamic_child_count = repeat_children.size(); + process.ReserveChildSlots(2 + dynamic_child_count - 1); + for (idx_t i = repeat_children.size(); i > 0; i--) { + auto child_idx = i - 1; + process.PushChild({transformer.GetRule("ArrayBounds"), repeat_children[child_idx].get()}, 1 + child_idx); + } + } else { + process.ReserveChildSlots(2 - 1); + } + process.PushChild({transformer.GetRule("TypeVariations"), list_pr.GetChild(0)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeNestedTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + idx_t dynamic_child_count = 0; + auto &dynamic_repeat_opt = list_pr.GetChild(1).Cast(); + if (dynamic_repeat_opt.HasResult()) { + auto &dynamic_repeat_pr = dynamic_repeat_opt.GetResult().Cast(); + auto dynamic_repeat_children = dynamic_repeat_pr.GetChildren(); + dynamic_child_count = dynamic_repeat_children.size(); + } + auto type_variations = process.TakeResult>(0); + optional> array_bounds {}; + if (dynamic_child_count > 0) { + vector array_bounds_value; + for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { + array_bounds_value.push_back(process.TakeResult(i)); + } + array_bounds = std::move(array_bounds_value); + } + auto result = TransformNestedType(transformer, std::move(type_variations), array_bounds); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeRowTypeTrampoline(PEGTransformer &transformer, @@ -6679,28 +6929,28 @@ void PEGTransformerFactory::InitializeRowTypeTrampoline(PEGTransformer &transfor } } -unique_ptr PEGTransformerFactory::FinalizeRowTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { - optional> col_id_type_list {}; +arena_ptr PEGTransformerFactory::FinalizeRowTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + optional>>> col_id_type_list {}; if (process.child_results[0]) { - col_id_type_list = process.TakeResult>(0); + col_id_type_list = process.TakeResult>>>(0); } - auto result = TransformRowType(transformer, col_id_type_list); - return make_uniq>>(std::move(result)); + auto result = TransformRowType(transformer, std::move(col_id_type_list)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSetofTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Type"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule("NestedType"), list_pr.GetChild(1)}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeSetofTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto type = process.TakeResult(0); - auto result = TransformSetofType(transformer, type); - return make_uniq>>(std::move(result)); +arena_ptr PEGTransformerFactory::FinalizeSetofTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto nested_type = process.TakeResult>(0); + auto result = TransformSetofType(transformer, std::move(nested_type)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUnionTypeTrampoline(PEGTransformer &transformer, @@ -6710,11 +6960,11 @@ void PEGTransformerFactory::InitializeUnionTypeTrampoline(PEGTransformer &transf process.PushChild({transformer.GetRule("ColIdTypeList"), list_pr.GetChild(1)}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeUnionTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto col_id_type_list = process.TakeResult>(0); - auto result = TransformUnionType(transformer, col_id_type_list); - return make_uniq>>(std::move(result)); +arena_ptr PEGTransformerFactory::FinalizeUnionTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto col_id_type_list = process.TakeResult>>>(0); + auto result = TransformUnionType(transformer, std::move(col_id_type_list)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeColIdTypeListTrampoline(PEGTransformer &transformer, @@ -6729,18 +6979,18 @@ void PEGTransformerFactory::InitializeColIdTypeListTrampoline(PEGTransformer &tr } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColIdTypeListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); - vector> col_id_type; + vector>> col_id_type; for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { - col_id_type.push_back(process.TakeResult>(i)); + col_id_type.push_back(process.TakeResult>>(i)); } - auto result = TransformColIdTypeList(transformer, col_id_type); - return make_uniq>>(result); + auto result = TransformColIdTypeList(transformer, std::move(col_id_type)); + return transformer.MakeResult>>>(std::move(result)); } void PEGTransformerFactory::InitializeMapTypeTrampoline(PEGTransformer &transformer, @@ -6754,15 +7004,15 @@ void PEGTransformerFactory::InitializeMapTypeTrampoline(PEGTransformer &transfor process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Type"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule("NestedType"), list_items[child_idx].get()}, 0 + child_idx); } } else { process.ReserveChildSlots(1 - 1); } } -unique_ptr PEGTransformerFactory::FinalizeMapTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeMapTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); idx_t dynamic_child_count = 0; auto &dynamic_list_opt = list_pr.GetChild(1).Cast(); @@ -6770,17 +7020,17 @@ unique_ptr PEGTransformerFactory::FinalizeMapTypeTrampolin auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(dynamic_list_opt.GetResult())); dynamic_child_count = dynamic_list_items.size(); } - optional> type {}; - auto &type_opt = list_pr.GetChild(1).Cast(); - if (type_opt.HasResult()) { - vector type_value; + optional>> nested_type {}; + auto &nested_type_opt = list_pr.GetChild(1).Cast(); + if (nested_type_opt.HasResult()) { + vector> nested_type_value; for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { - type_value.push_back(process.TakeResult(i)); + nested_type_value.push_back(process.TakeResult>(i)); } - type = std::move(type_value); + nested_type = std::move(nested_type_value); } - auto result = TransformMapType(transformer, type); - return make_uniq>>(std::move(result)); + auto result = TransformMapType(transformer, std::move(nested_type)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTupleTypeTrampoline(PEGTransformer &transformer, @@ -6791,37 +7041,37 @@ void PEGTransformerFactory::InitializeTupleTypeTrampoline(PEGTransformer &transf process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Type"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule("NestedType"), list_items[child_idx].get()}, 0 + child_idx); } } -unique_ptr -PEGTransformerFactory::FinalizeTupleTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeTupleTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(1))); auto dynamic_child_count = dynamic_list_items.size(); - vector type; + vector> nested_type; for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { - type.push_back(process.TakeResult(i)); + nested_type.push_back(process.TakeResult>(i)); } - auto result = TransformTupleType(transformer, type); - return make_uniq>>(std::move(result)); + auto result = TransformTupleType(transformer, std::move(nested_type)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeColIdTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("Type"), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule("NestedType"), list_pr.GetChild(1)}, 1); process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(0)}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeColIdTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeColIdTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto col_id = process.TakeResult(0); - auto type = process.TakeResult(1); - auto result = TransformColIdType(transformer, col_id, type); - return make_uniq>>(result); + auto nested_type = process.TakeResult>(1); + auto result = TransformColIdType(transformer, col_id, std::move(nested_type)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeArrayBoundsTrampoline(PEGTransformer &transformer, @@ -6833,15 +7083,15 @@ void PEGTransformerFactory::InitializeArrayBoundsTrampoline(PEGTransformer &tran auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeArrayBoundsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeArrayKeywordTrampoline(PEGTransformer &transformer, @@ -6849,10 +7099,10 @@ void PEGTransformerFactory::InitializeArrayKeywordTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeArrayKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformArrayKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeArrayKeywordWithBoundsTrampoline(PEGTransformer &transformer, @@ -6862,12 +7112,12 @@ void PEGTransformerFactory::InitializeArrayKeywordWithBoundsTrampoline(PEGTransf process.PushChild({transformer.GetRule("SquareBracketsArray"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeArrayKeywordWithBoundsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto square_brackets_array = process.TakeResult(0); auto result = TransformArrayKeywordWithBounds(transformer, square_brackets_array); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSquareBracketsArrayTrampoline(PEGTransformer &transformer, @@ -6880,7 +7130,7 @@ void PEGTransformerFactory::InitializeSquareBracketsArrayTrampoline(PEGTransform } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSquareBracketsArrayTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional> expression {}; @@ -6888,7 +7138,7 @@ PEGTransformerFactory::FinalizeSquareBracketsArrayTrampoline(PEGTransformer &tra expression = process.TakeResult>(0); } auto result = TransformSquareBracketsArray(transformer, std::move(expression)); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTimeTypeTrampoline(PEGTransformer &transformer, @@ -6906,8 +7156,8 @@ void PEGTransformerFactory::InitializeTimeTypeTrampoline(PEGTransformer &transfo process.PushChild({transformer.GetRule("TimeOrTimestamp"), list_pr.GetChild(0)}, 0); } -unique_ptr PEGTransformerFactory::FinalizeTimeTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeTimeTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto time_or_timestamp = process.TakeResult(0); optional>> type_modifiers {}; if (process.child_results[1]) { @@ -6918,7 +7168,7 @@ unique_ptr PEGTransformerFactory::FinalizeTimeTypeTrampoli time_zone = process.TakeResult(2); } auto result = TransformTimeType(transformer, time_or_timestamp, std::move(type_modifiers), time_zone); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTimeOrTimestampTrampoline(PEGTransformer &transformer, @@ -6930,16 +7180,16 @@ void PEGTransformerFactory::InitializeTimeOrTimestampTrampoline(PEGTransformer & auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTimeOrTimestampTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTimeTypeIdTrampoline(PEGTransformer &transformer, @@ -6947,10 +7197,10 @@ void PEGTransformerFactory::InitializeTimeTypeIdTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTimeTypeIdTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformTimeTypeId(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTimestampTypeIdTrampoline(PEGTransformer &transformer, @@ -6958,11 +7208,11 @@ void PEGTransformerFactory::InitializeTimestampTypeIdTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTimestampTypeIdTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformTimestampTypeId(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTimeZoneTrampoline(PEGTransformer &transformer, @@ -6972,11 +7222,11 @@ void PEGTransformerFactory::InitializeTimeZoneTrampoline(PEGTransformer &transfo process.PushChild({transformer.GetRule("WithOrWithout"), list_pr.GetChild(0)}, 0); } -unique_ptr PEGTransformerFactory::FinalizeTimeZoneTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeTimeZoneTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto with_or_without = process.TakeResult(0); auto result = TransformTimeZone(transformer, with_or_without); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeWithOrWithoutTrampoline(PEGTransformer &transformer, @@ -6988,16 +7238,16 @@ void PEGTransformerFactory::InitializeWithOrWithoutTrampoline(PEGTransformer &tr auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWithOrWithoutTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeWithRuleTrampoline(PEGTransformer &transformer, @@ -7005,10 +7255,10 @@ void PEGTransformerFactory::InitializeWithRuleTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeWithRuleTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeWithRuleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformWithRule(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeWithoutRuleTrampoline(PEGTransformer &transformer, @@ -7016,10 +7266,10 @@ void PEGTransformerFactory::InitializeWithoutRuleTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWithoutRuleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformWithoutRule(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeConnectStatementTrampoline(PEGTransformer &transformer, @@ -7032,7 +7282,7 @@ void PEGTransformerFactory::InitializeConnectStatementTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeConnectStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional> session_target {}; @@ -7040,7 +7290,7 @@ PEGTransformerFactory::FinalizeConnectStatementTrampoline(PEGTransformer &transf session_target = process.TakeResult>(0); } auto result = TransformConnectStatement(transformer, std::move(session_target)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDisconnectStatementTrampoline(PEGTransformer &transformer, @@ -7048,11 +7298,11 @@ void PEGTransformerFactory::InitializeDisconnectStatementTrampoline(PEGTransform process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDisconnectStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformDisconnectStatement(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSessionTargetTrampoline(PEGTransformer &transformer, @@ -7064,16 +7314,16 @@ void PEGTransformerFactory::InitializeSessionTargetTrampoline(PEGTransformer &tr auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSessionTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeLocalSessionTargetTrampoline(PEGTransformer &transformer, @@ -7081,11 +7331,11 @@ void PEGTransformerFactory::InitializeLocalSessionTargetTrampoline(PEGTransforme process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLocalSessionTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformLocalSessionTarget(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeStringSessionTargetTrampoline(PEGTransformer &transformer, @@ -7098,7 +7348,7 @@ void PEGTransformerFactory::InitializeStringSessionTargetTrampoline(PEGTransform } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeStringSessionTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -7108,7 +7358,7 @@ PEGTransformerFactory::FinalizeStringSessionTargetTrampoline(PEGTransformer &tra generic_copy_option_list = process.TakeResult>(0); } auto result = TransformStringSessionTarget(transformer, string_literal, generic_copy_option_list); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCatalogSessionTargetTrampoline(PEGTransformer &transformer, @@ -7116,13 +7366,13 @@ void PEGTransformerFactory::InitializeCatalogSessionTargetTrampoline(PEGTransfor process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCatalogSessionTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto catalog_name = list_pr.GetChild(0).Cast().identifier; auto result = TransformCatalogSessionTarget(transformer, catalog_name); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCopyStatementTrampoline(PEGTransformer &transformer, @@ -7132,12 +7382,12 @@ void PEGTransformerFactory::InitializeCopyStatementTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("CopyVariations"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCopyStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto copy_variations = process.TakeResult>(0); auto result = TransformCopyStatement(transformer, std::move(copy_variations)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCopyVariationsTrampoline(PEGTransformer &transformer, @@ -7149,25 +7399,29 @@ void PEGTransformerFactory::InitializeCopyVariationsTrampoline(PEGTransformer &t auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCopyVariationsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCopyTableTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(5); - auto ©_options_opt = list_pr.GetChild(4).Cast(); + process.ReserveChildSlots(6); + auto ©_options_opt = list_pr.GetChild(5).Cast(); if (copy_options_opt.HasResult()) { - process.PushChild({transformer.GetRule("CopyOptions"), copy_options_opt.GetResult()}, 4); + process.PushChild({transformer.GetRule("CopyOptions"), copy_options_opt.GetResult()}, 5); + } + auto ©_legacy_option_opt = list_pr.GetChild(4).Cast(); + if (copy_legacy_option_opt.HasResult()) { + process.PushChild({transformer.GetRule("CopyLegacyOption"), copy_legacy_option_opt.GetResult()}, 4); } process.PushChild({transformer.GetRule("CopyFileName"), list_pr.GetChild(3)}, 3); process.PushChild({transformer.GetRule("FromOrTo"), list_pr.GetChild(2)}, 2); @@ -7178,8 +7432,8 @@ void PEGTransformerFactory::InitializeCopyTableTrampoline(PEGTransformer &transf process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(0)}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeCopyTableTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeCopyTableTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto base_table_name = process.TakeResult>(0); optional> insert_column_list {}; if (process.child_results[1]) { @@ -7187,13 +7441,17 @@ PEGTransformerFactory::FinalizeCopyTableTrampoline(PEGTransformer &transformer, } auto from_or_to = process.TakeResult(2); auto copy_file_name = process.TakeResult>(3); - optional> copy_options {}; + optional copy_legacy_option {}; if (process.child_results[4]) { - copy_options = process.TakeResult>(4); + copy_legacy_option = process.TakeResult(4); + } + optional> copy_options {}; + if (process.child_results[5]) { + copy_options = process.TakeResult>(5); } auto result = TransformCopyTable(transformer, std::move(base_table_name), insert_column_list, from_or_to, - std::move(copy_file_name), copy_options); - return make_uniq>>(std::move(result)); + std::move(copy_file_name), copy_legacy_option, copy_options); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeFromOrToTrampoline(PEGTransformer &transformer, @@ -7205,15 +7463,15 @@ void PEGTransformerFactory::InitializeFromOrToTrampoline(PEGTransformer &transfo auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr PEGTransformerFactory::FinalizeFromOrToTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeFromOrToTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCopyFromTrampoline(PEGTransformer &transformer, @@ -7221,10 +7479,10 @@ void PEGTransformerFactory::InitializeCopyFromTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeCopyFromTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeCopyFromTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformCopyFrom(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCopyToTrampoline(PEGTransformer &transformer, @@ -7232,10 +7490,10 @@ void PEGTransformerFactory::InitializeCopyToTrampoline(PEGTransformer &transform process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeCopyToTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeCopyToTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformCopyTo(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCopySelectTrampoline(PEGTransformer &transformer, @@ -7251,7 +7509,7 @@ void PEGTransformerFactory::InitializeCopySelectTrampoline(PEGTransformer &trans 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCopySelectTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto select_statement_internal = process.TakeResult>(0); auto copy_file_name = process.TakeResult>(1); @@ -7261,7 +7519,7 @@ PEGTransformerFactory::FinalizeCopySelectTrampoline(PEGTransformer &transformer, } auto result = TransformCopySelect(transformer, std::move(select_statement_internal), std::move(copy_file_name), copy_options); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCopyFileNameTrampoline(PEGTransformer &transformer, @@ -7273,15 +7531,15 @@ void PEGTransformerFactory::InitializeCopyFileNameTrampoline(PEGTransformer &tra auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCopyFileNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCopyFileNameExpressionTrampoline(PEGTransformer &transformer, @@ -7293,16 +7551,16 @@ void PEGTransformerFactory::InitializeCopyFileNameExpressionTrampoline(PEGTransf auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCopyFileNameExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCopyFileNameStringLiteralTrampoline(PEGTransformer &transformer, @@ -7310,13 +7568,13 @@ void PEGTransformerFactory::InitializeCopyFileNameStringLiteralTrampoline(PEGTra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCopyFileNameStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(0)); auto result = TransformCopyFileNameStringLiteral(transformer, string_literal); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCopyFileNameIdentifierTrampoline(PEGTransformer &transformer, @@ -7324,13 +7582,13 @@ void PEGTransformerFactory::InitializeCopyFileNameIdentifierTrampoline(PEGTransf process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCopyFileNameIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto identifier = list_pr.GetChild(0).Cast().identifier; auto result = TransformCopyFileNameIdentifier(transformer, identifier); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCopyFileNameIdentifierColIdTrampoline(PEGTransformer &transformer, @@ -7340,29 +7598,72 @@ void PEGTransformerFactory::InitializeCopyFileNameIdentifierColIdTrampoline(PEGT process.PushChild({transformer.GetRule("IdentifierColId"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCopyFileNameIdentifierColIdTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto identifier_col_id = process.TakeResult(0); auto result = TransformCopyFileNameIdentifierColId(transformer, identifier_col_id); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIdentifierColIdTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(2)}, 0); + auto &repeat_pr = list_pr.GetChild(1).Cast(); + auto repeat_children = repeat_pr.GetChildren(); + auto dynamic_child_count = repeat_children.size(); + process.ReserveChildSlots(1 + dynamic_child_count - 1); + for (idx_t i = repeat_children.size(); i > 0; i--) { + auto child_idx = i - 1; + process.PushChild({transformer.GetRule("CopyFileNameSuffix"), repeat_children[child_idx].get()}, 0 + child_idx); + } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIdentifierColIdTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); + auto &dynamic_repeat_pr = list_pr.GetChild(1).Cast(); + auto dynamic_repeat_children = dynamic_repeat_pr.GetChildren(); + auto dynamic_child_count = dynamic_repeat_children.size(); auto identifier = list_pr.GetChild(0).Cast().identifier; - auto col_id = process.TakeResult(0); - auto result = TransformIdentifierColId(transformer, identifier, col_id); - return make_uniq>(result); + vector copy_file_name_suffix; + for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { + copy_file_name_suffix.push_back(process.TakeResult(i)); + } + auto result = TransformIdentifierColId(transformer, identifier, copy_file_name_suffix); + return transformer.MakeResult(result); +} + +void PEGTransformerFactory::InitializeCopyFileNameSuffixTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(1)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeCopyFileNameSuffixTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto result = process.TakeResult(0); + return transformer.MakeResult(result); +} + +void PEGTransformerFactory::InitializeCopyLegacyOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + process.ReserveChildSlots(0); +} + +arena_ptr +PEGTransformerFactory::FinalizeCopyLegacyOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + bool has_result {}; + auto &has_result_opt = list_pr.GetChild(0).Cast(); + has_result = has_result_opt.HasResult(); + auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(2)); + auto result = TransformCopyLegacyOption(transformer, has_result, string_literal); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCopyOptionsTrampoline(PEGTransformer &transformer, @@ -7372,7 +7673,7 @@ void PEGTransformerFactory::InitializeCopyOptionsTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("CopyOptionList"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCopyOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); bool has_result {}; @@ -7380,7 +7681,7 @@ PEGTransformerFactory::FinalizeCopyOptionsTrampoline(PEGTransformer &transformer has_result = has_result_opt.HasResult(); auto copy_option_list = process.TakeResult>(0); auto result = TransformCopyOptions(transformer, has_result, copy_option_list); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeCopyOptionListTrampoline(PEGTransformer &transformer, @@ -7392,16 +7693,16 @@ void PEGTransformerFactory::InitializeCopyOptionListTrampoline(PEGTransformer &t auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCopyOptionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeSpecializedOptionListTrampoline(PEGTransformer &transformer, @@ -7425,7 +7726,7 @@ void PEGTransformerFactory::InitializeSpecializedOptionListTrampoline(PEGTransfo process.PushChild({transformer.GetRule("SpecializedOption"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSpecializedOptionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -7446,7 +7747,7 @@ PEGTransformerFactory::FinalizeSpecializedOptionListTrampoline(PEGTransformer &t specialized_option_tail = std::move(specialized_option_tail_value); } auto result = TransformSpecializedOptionList(transformer, specialized_option, specialized_option_tail); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeSpecializedOptionTailTrampoline(PEGTransformer &transformer, @@ -7456,7 +7757,7 @@ void PEGTransformerFactory::InitializeSpecializedOptionTailTrampoline(PEGTransfo process.PushChild({transformer.GetRule("SpecializedOption"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSpecializedOptionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -7465,7 +7766,7 @@ PEGTransformerFactory::FinalizeSpecializedOptionTailTrampoline(PEGTransformer &t has_result = has_result_opt.HasResult(); auto specialized_option = process.TakeResult(0); auto result = TransformSpecializedOptionTail(transformer, has_result, specialized_option); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSpecializedOptionTrampoline(PEGTransformer &transformer, @@ -7477,16 +7778,16 @@ void PEGTransformerFactory::InitializeSpecializedOptionTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSpecializedOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSingleOptionTrampoline(PEGTransformer &transformer, @@ -7498,15 +7799,15 @@ void PEGTransformerFactory::InitializeSingleOptionTrampoline(PEGTransformer &tra auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSingleOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeBinaryOptionTrampoline(PEGTransformer &transformer, @@ -7514,10 +7815,10 @@ void PEGTransformerFactory::InitializeBinaryOptionTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBinaryOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformBinaryOption(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeFreezeOptionTrampoline(PEGTransformer &transformer, @@ -7525,10 +7826,10 @@ void PEGTransformerFactory::InitializeFreezeOptionTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFreezeOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformFreezeOption(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeOidsOptionTrampoline(PEGTransformer &transformer, @@ -7536,10 +7837,10 @@ void PEGTransformerFactory::InitializeOidsOptionTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOidsOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformOidsOption(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCsvOptionTrampoline(PEGTransformer &transformer, @@ -7547,10 +7848,10 @@ void PEGTransformerFactory::InitializeCsvOptionTrampoline(PEGTransformer &transf process.ReserveChildSlots(0); } -unique_ptr -PEGTransformerFactory::FinalizeCsvOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeCsvOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformCsvOption(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeHeaderOptionTrampoline(PEGTransformer &transformer, @@ -7558,10 +7859,10 @@ void PEGTransformerFactory::InitializeHeaderOptionTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeHeaderOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformHeaderOption(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeNullAsOptionTrampoline(PEGTransformer &transformer, @@ -7569,7 +7870,7 @@ void PEGTransformerFactory::InitializeNullAsOptionTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNullAsOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); bool has_result {}; @@ -7577,7 +7878,7 @@ PEGTransformerFactory::FinalizeNullAsOptionTrampoline(PEGTransformer &transforme has_result = has_result_opt.HasResult(); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(2)); auto result = TransformNullAsOption(transformer, has_result, string_literal); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDelimiterAsOptionTrampoline(PEGTransformer &transformer, @@ -7585,7 +7886,7 @@ void PEGTransformerFactory::InitializeDelimiterAsOptionTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDelimiterAsOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -7594,7 +7895,7 @@ PEGTransformerFactory::FinalizeDelimiterAsOptionTrampoline(PEGTransformer &trans has_result = has_result_opt.HasResult(); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(2)); auto result = TransformDelimiterAsOption(transformer, has_result, string_literal); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeQuoteAsOptionTrampoline(PEGTransformer &transformer, @@ -7602,7 +7903,7 @@ void PEGTransformerFactory::InitializeQuoteAsOptionTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQuoteAsOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -7611,7 +7912,7 @@ PEGTransformerFactory::FinalizeQuoteAsOptionTrampoline(PEGTransformer &transform has_result = has_result_opt.HasResult(); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(2)); auto result = TransformQuoteAsOption(transformer, has_result, string_literal); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeEscapeAsOptionTrampoline(PEGTransformer &transformer, @@ -7619,7 +7920,7 @@ void PEGTransformerFactory::InitializeEscapeAsOptionTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeEscapeAsOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -7628,7 +7929,7 @@ PEGTransformerFactory::FinalizeEscapeAsOptionTrampoline(PEGTransformer &transfor has_result = has_result_opt.HasResult(); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(2)); auto result = TransformEscapeAsOption(transformer, has_result, string_literal); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeEncodingOptionTrampoline(PEGTransformer &transformer, @@ -7636,13 +7937,13 @@ void PEGTransformerFactory::InitializeEncodingOptionTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeEncodingOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(1)); auto result = TransformEncodingOption(transformer, string_literal); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeForceQuoteOptionTrampoline(PEGTransformer &transformer, @@ -7656,7 +7957,7 @@ void PEGTransformerFactory::InitializeForceQuoteOptionTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeForceQuoteOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional force_quote {}; @@ -7665,7 +7966,7 @@ PEGTransformerFactory::FinalizeForceQuoteOptionTrampoline(PEGTransformer &transf } auto star_symbol_column_list = process.TakeResult>(1); auto result = TransformForceQuoteOption(transformer, force_quote, star_symbol_column_list); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeStarSymbolColumnListTrampoline(PEGTransformer &transformer, @@ -7676,23 +7977,23 @@ void PEGTransformerFactory::InitializeStarSymbolColumnListTrampoline(PEGTransfor process.ReserveChildSlots(1); auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; - if (!has_transform_process && (choice_result.name == "StarSymbol")) { + if (!has_transform_process && (choice_result.Name() == "StarSymbol")) { return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeStarSymbolColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { vector result {}; if (process.child_results[0]) { result = process.TakeResult>(0); } - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeForceQuoteTrampoline(PEGTransformer &transformer, @@ -7700,10 +8001,10 @@ void PEGTransformerFactory::InitializeForceQuoteTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeForceQuoteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformForceQuote(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializePartitionByOptionTrampoline(PEGTransformer &transformer, @@ -7713,12 +8014,12 @@ void PEGTransformerFactory::InitializePartitionByOptionTrampoline(PEGTransformer process.PushChild({transformer.GetRule("PartitionByColumnList"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePartitionByOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto partition_by_column_list = process.TakeResult>(0); auto result = TransformPartitionByOption(transformer, partition_by_column_list); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializePartitionByColumnListTrampoline(PEGTransformer &transformer, @@ -7730,16 +8031,16 @@ void PEGTransformerFactory::InitializePartitionByColumnListTrampoline(PEGTransfo auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePartitionByColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeStarPartitionByColumnListTrampoline(PEGTransformer &transformer, @@ -7747,11 +8048,11 @@ void PEGTransformerFactory::InitializeStarPartitionByColumnListTrampoline(PEGTra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeStarPartitionByColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformStarPartitionByColumnList(transformer); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeParenthesizedPartitionByColumnListTrampoline(PEGTransformer &transformer, @@ -7761,12 +8062,12 @@ void PEGTransformerFactory::InitializeParenthesizedPartitionByColumnListTrampoli process.PushChild({transformer.GetRule("ColumnList"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeParenthesizedPartitionByColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto column_list = process.TakeResult>(0); auto result = TransformParenthesizedPartitionByColumnList(transformer, column_list); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeSinglePartitionByColumnListTrampoline(PEGTransformer &transformer, @@ -7776,12 +8077,12 @@ void PEGTransformerFactory::InitializeSinglePartitionByColumnListTrampoline(PEGT process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSinglePartitionByColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id = process.TakeResult(0); auto result = TransformSinglePartitionByColumnList(transformer, col_id); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeForceNullOptionTrampoline(PEGTransformer &transformer, @@ -7795,7 +8096,7 @@ void PEGTransformerFactory::InitializeForceNullOptionTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeForceNullOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional force_not_null {}; @@ -7804,7 +8105,7 @@ PEGTransformerFactory::FinalizeForceNullOptionTrampoline(PEGTransformer &transfo } auto column_list = process.TakeResult>(1); auto result = TransformForceNullOption(transformer, force_not_null, column_list); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeForceNotNullTrampoline(PEGTransformer &transformer, @@ -7812,10 +8113,10 @@ void PEGTransformerFactory::InitializeForceNotNullTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeForceNotNullTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformForceNotNull(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCopyGenericOptionListTrampoline(PEGTransformer &transformer, @@ -7830,7 +8131,7 @@ void PEGTransformerFactory::InitializeCopyGenericOptionListTrampoline(PEGTransfo } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCopyGenericOptionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -7841,7 +8142,7 @@ PEGTransformerFactory::FinalizeCopyGenericOptionListTrampoline(PEGTransformer &t copy_generic_option.push_back(process.TakeResult(i)); } auto result = TransformCopyGenericOptionList(transformer, copy_generic_option); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeCopyGenericOptionTrampoline(PEGTransformer &transformer, @@ -7853,16 +8154,16 @@ void PEGTransformerFactory::InitializeCopyGenericOptionTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCopyGenericOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeOrderByCopyOptionTrampoline(PEGTransformer &transformer, @@ -7876,7 +8177,7 @@ void PEGTransformerFactory::InitializeOrderByCopyOptionTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOrderByCopyOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional generic_copy_option_value {}; @@ -7884,7 +8185,7 @@ PEGTransformerFactory::FinalizeOrderByCopyOptionTrampoline(PEGTransformer &trans generic_copy_option_value = process.TakeResult(0); } auto result = TransformOrderByCopyOption(transformer, std::move(generic_copy_option_value)); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializePartitionedByCopyOptionTrampoline(PEGTransformer &transformer, @@ -7898,7 +8199,7 @@ void PEGTransformerFactory::InitializePartitionedByCopyOptionTrampoline(PEGTrans } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePartitionedByCopyOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional generic_copy_option_value {}; @@ -7906,7 +8207,7 @@ PEGTransformerFactory::FinalizePartitionedByCopyOptionTrampoline(PEGTransformer generic_copy_option_value = process.TakeResult(0); } auto result = TransformPartitionedByCopyOption(transformer, std::move(generic_copy_option_value)); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeGenericCopyOptionListTrampoline(PEGTransformer &transformer, @@ -7921,7 +8222,7 @@ void PEGTransformerFactory::InitializeGenericCopyOptionListTrampoline(PEGTransfo } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGenericCopyOptionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -7932,7 +8233,7 @@ PEGTransformerFactory::FinalizeGenericCopyOptionListTrampoline(PEGTransformer &t generic_copy_option.push_back(process.TakeResult(i)); } auto result = TransformGenericCopyOptionList(transformer, generic_copy_option); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeGenericCopyOptionTrampoline(PEGTransformer &transformer, @@ -7946,7 +8247,7 @@ void PEGTransformerFactory::InitializeGenericCopyOptionTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGenericCopyOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -7956,7 +8257,7 @@ PEGTransformerFactory::FinalizeGenericCopyOptionTrampoline(PEGTransformer &trans generic_copy_option_value = process.TakeResult(0); } auto result = TransformGenericCopyOption(transformer, copy_option_name, std::move(generic_copy_option_value)); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeGenericCopyOptionValueTrampoline(PEGTransformer &transformer, @@ -7968,16 +8269,16 @@ void PEGTransformerFactory::InitializeGenericCopyOptionValueTrampoline(PEGTransf auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGenericCopyOptionValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeGenericCopyOptionOrderListTrampoline(PEGTransformer &transformer, @@ -7987,13 +8288,13 @@ void PEGTransformerFactory::InitializeGenericCopyOptionOrderListTrampoline(PEGTr process.PushChild({transformer.GetRule("GenericCopyOptionParenthesizedExpressionList"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGenericCopyOptionOrderListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto generic_copy_option_parenthesized_expression_list = process.TakeResult>(0); auto result = TransformGenericCopyOptionOrderList(transformer, std::move(generic_copy_option_parenthesized_expression_list)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeGenericCopyOptionExpressionTrampoline(PEGTransformer &transformer, @@ -8003,12 +8304,12 @@ void PEGTransformerFactory::InitializeGenericCopyOptionExpressionTrampoline(PEGT process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGenericCopyOptionExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformGenericCopyOptionExpression(transformer, std::move(expression)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeGenericCopyOptionParenthesizedExpressionListTrampoline( @@ -8018,12 +8319,12 @@ void PEGTransformerFactory::InitializeGenericCopyOptionParenthesizedExpressionLi process.PushChild({transformer.GetRule("OrderByExpressionList"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } -unique_ptr PEGTransformerFactory::FinalizeGenericCopyOptionParenthesizedExpressionListTrampoline( +arena_ptr PEGTransformerFactory::FinalizeGenericCopyOptionParenthesizedExpressionListTrampoline( PEGTransformer &transformer, GeneratedTransformProcess &process) { auto order_by_expression_list = process.TakeResult>(0); auto result = TransformGenericCopyOptionParenthesizedExpressionList(transformer, std::move(order_by_expression_list)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCopyFromDatabaseTrampoline(PEGTransformer &transformer, @@ -8035,16 +8336,16 @@ void PEGTransformerFactory::InitializeCopyFromDatabaseTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCopyFromDatabaseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCopyFromDatabaseWithFlagTrampoline(PEGTransformer &transformer, @@ -8056,14 +8357,14 @@ void PEGTransformerFactory::InitializeCopyFromDatabaseWithFlagTrampoline(PEGTran process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCopyFromDatabaseWithFlagTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id = process.TakeResult(0); auto col_id_1 = process.TakeResult(1); auto copy_database_flag = process.TakeResult(2); auto result = TransformCopyFromDatabaseWithFlag(transformer, col_id, col_id_1, copy_database_flag); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCopyFromDatabaseWithoutFlagTrampoline(PEGTransformer &transformer, @@ -8074,13 +8375,13 @@ void PEGTransformerFactory::InitializeCopyFromDatabaseWithoutFlagTrampoline(PEGT process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCopyFromDatabaseWithoutFlagTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id = process.TakeResult(0); auto col_id_1 = process.TakeResult(1); auto result = TransformCopyFromDatabaseWithoutFlag(transformer, col_id, col_id_1); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCopyDatabaseFlagTrampoline(PEGTransformer &transformer, @@ -8090,12 +8391,12 @@ void PEGTransformerFactory::InitializeCopyDatabaseFlagTrampoline(PEGTransformer process.PushChild({transformer.GetRule("SchemaOrData"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCopyDatabaseFlagTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto schema_or_data = process.TakeResult(0); auto result = TransformCopyDatabaseFlag(transformer, schema_or_data); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSchemaOrDataTrampoline(PEGTransformer &transformer, @@ -8107,15 +8408,15 @@ void PEGTransformerFactory::InitializeSchemaOrDataTrampoline(PEGTransformer &tra auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSchemaOrDataTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCopySchemaTrampoline(PEGTransformer &transformer, @@ -8123,10 +8424,10 @@ void PEGTransformerFactory::InitializeCopySchemaTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCopySchemaTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCopySchema(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCopyDataTrampoline(PEGTransformer &transformer, @@ -8134,55 +8435,34 @@ void PEGTransformerFactory::InitializeCopyDataTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeCopyDataTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeCopyDataTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformCopyData(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCreateIndexStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - auto &list_opt = list_pr.GetChild(8).Cast(); - idx_t dynamic_child_count = 0; - if (list_opt.HasResult()) { - auto list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_opt.GetResult())); - dynamic_child_count = list_items.size(); - process.ReserveChildSlots(8 + dynamic_child_count - 1); - auto &where_clause_opt = list_pr.GetChild(10).Cast(); - if (where_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("WhereClause"), where_clause_opt.GetResult()}, - 7 + dynamic_child_count - 1); - } - auto &with_list_opt = list_pr.GetChild(9).Cast(); - if (with_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("WithList"), with_list_opt.GetResult()}, - 6 + dynamic_child_count - 1); - } - for (idx_t i = list_items.size(); i > 0; i--) { - auto child_idx = i - 1; - process.PushChild({transformer.GetRule("IndexElement"), list_items[child_idx].get()}, 5 + child_idx); - } - } else { - process.ReserveChildSlots(8 - 1); - auto &where_clause_opt = list_pr.GetChild(10).Cast(); - if (where_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("WhereClause"), where_clause_opt.GetResult()}, - 7 + dynamic_child_count - 1); - } - auto &with_list_opt = list_pr.GetChild(9).Cast(); - if (with_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("WithList"), with_list_opt.GetResult()}, - 6 + dynamic_child_count - 1); - } - } - auto &index_type_opt = list_pr.GetChild(7).Cast(); - if (index_type_opt.HasResult()) { - process.PushChild({transformer.GetRule("IndexType"), index_type_opt.GetResult()}, 4); + auto list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(7))); + auto dynamic_child_count = list_items.size(); + process.ReserveChildSlots(7 + dynamic_child_count - 1); + auto &where_clause_opt = list_pr.GetChild(9).Cast(); + if (where_clause_opt.HasResult()) { + process.PushChild({transformer.GetRule("WhereClause"), where_clause_opt.GetResult()}, + 6 + dynamic_child_count - 1); } - auto &insert_column_list_opt = list_pr.GetChild(6).Cast(); - if (insert_column_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("InsertColumnList"), insert_column_list_opt.GetResult()}, 3); + auto &with_list_opt = list_pr.GetChild(8).Cast(); + if (with_list_opt.HasResult()) { + process.PushChild({transformer.GetRule("WithList"), with_list_opt.GetResult()}, 5 + dynamic_child_count - 1); + } + for (idx_t i = list_items.size(); i > 0; i--) { + auto child_idx = i - 1; + process.PushChild({transformer.GetRule("IndexElement"), list_items[child_idx].get()}, 4 + child_idx); + } + auto &index_type_opt = list_pr.GetChild(6).Cast(); + if (index_type_opt.HasResult()) { + process.PushChild({transformer.GetRule("IndexType"), index_type_opt.GetResult()}, 3); } process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(5)}, 2); auto &if_not_exists_opt = list_pr.GetChild(2).Cast(); @@ -8195,16 +8475,12 @@ void PEGTransformerFactory::InitializeCreateIndexStmtTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateIndexStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - idx_t dynamic_child_count = 0; - auto &dynamic_list_opt = list_pr.GetChild(8).Cast(); - if (dynamic_list_opt.HasResult()) { - auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(dynamic_list_opt.GetResult())); - dynamic_child_count = dynamic_list_items.size(); - } + auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(7))); + auto dynamic_child_count = dynamic_list_items.size(); optional unique_index {}; if (process.child_results[0]) { unique_index = process.TakeResult(0); @@ -8219,36 +8495,27 @@ PEGTransformerFactory::FinalizeCreateIndexStmtTrampoline(PEGTransformer &transfo index_name = index_name_opt.GetResult().Cast().identifier; } auto base_table_name = process.TakeResult>(2); - optional> insert_column_list {}; + optional index_type {}; if (process.child_results[3]) { - insert_column_list = process.TakeResult>(3); + index_type = process.TakeResult(3); } - optional index_type {}; - if (process.child_results[4]) { - index_type = process.TakeResult(4); - } - optional>> index_element {}; - auto &index_element_opt = list_pr.GetChild(8).Cast(); - if (index_element_opt.HasResult()) { - vector> index_element_value; - for (idx_t i = 5; i < 5 + dynamic_child_count; i++) { - index_element_value.push_back(process.TakeResult>(i)); - } - index_element = std::move(index_element_value); + vector> index_element; + for (idx_t i = 4; i < 4 + dynamic_child_count; i++) { + index_element.push_back(process.TakeResult>(i)); } optional>> with_list {}; - if (process.child_results[6 + dynamic_child_count - 1]) { + if (process.child_results[5 + dynamic_child_count - 1]) { with_list = - process.TakeResult>>(6 + dynamic_child_count - 1); + process.TakeResult>>(5 + dynamic_child_count - 1); } optional> where_clause {}; - if (process.child_results[7 + dynamic_child_count - 1]) { - where_clause = process.TakeResult>(7 + dynamic_child_count - 1); + if (process.child_results[6 + dynamic_child_count - 1]) { + where_clause = process.TakeResult>(6 + dynamic_child_count - 1); } - auto result = TransformCreateIndexStmt(transformer, unique_index, if_not_exists, index_name, - std::move(base_table_name), insert_column_list, index_type, - std::move(index_element), std::move(with_list), std::move(where_clause)); - return make_uniq>>(std::move(result)); + auto result = + TransformCreateIndexStmt(transformer, unique_index, if_not_exists, index_name, std::move(base_table_name), + index_type, std::move(index_element), std::move(with_list), std::move(where_clause)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeWithListTrampoline(PEGTransformer &transformer, @@ -8258,11 +8525,11 @@ void PEGTransformerFactory::InitializeWithListTrampoline(PEGTransformer &transfo process.PushChild({transformer.GetRule("RelOptionOrOids"), list_pr.GetChild(1)}, 0); } -unique_ptr PEGTransformerFactory::FinalizeWithListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeWithListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto rel_option_or_oids = process.TakeResult>>(0); auto result = TransformWithList(transformer, std::move(rel_option_or_oids)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeRelOptionOrOidsTrampoline(PEGTransformer &transformer, @@ -8274,16 +8541,16 @@ void PEGTransformerFactory::InitializeRelOptionOrOidsTrampoline(PEGTransformer & auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRelOptionOrOidsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>>(0); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeRelOptionListTrampoline(PEGTransformer &transformer, @@ -8298,7 +8565,7 @@ void PEGTransformerFactory::InitializeRelOptionListTrampoline(PEGTransformer &tr } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRelOptionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -8309,7 +8576,7 @@ PEGTransformerFactory::FinalizeRelOptionListTrampoline(PEGTransformer &transform rel_option.push_back(process.TakeResult>>(i)); } auto result = TransformRelOptionList(transformer, std::move(rel_option)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeOidsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { @@ -8318,11 +8585,11 @@ void PEGTransformerFactory::InitializeOidsTrampoline(PEGTransformer &transformer process.PushChild({transformer.GetRule("WithOrWithoutOids"), list_pr.GetChild(0)}, 0); } -unique_ptr PEGTransformerFactory::FinalizeOidsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeOidsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto with_or_without_oids = process.TakeResult(0); auto result = TransformOids(transformer, with_or_without_oids); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeWithOrWithoutOidsTrampoline(PEGTransformer &transformer, @@ -8334,16 +8601,16 @@ void PEGTransformerFactory::InitializeWithOrWithoutOidsTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWithOrWithoutOidsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeWithOidsTrampoline(PEGTransformer &transformer, @@ -8351,10 +8618,10 @@ void PEGTransformerFactory::InitializeWithOidsTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeWithOidsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeWithOidsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformWithOids(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeWithoutOidsTrampoline(PEGTransformer &transformer, @@ -8362,10 +8629,10 @@ void PEGTransformerFactory::InitializeWithoutOidsTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWithoutOidsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformWithoutOids(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeIndexElementTrampoline(PEGTransformer &transformer, @@ -8383,7 +8650,7 @@ void PEGTransformerFactory::InitializeIndexElementTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIndexElementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); optional desc_or_asc {}; @@ -8395,7 +8662,7 @@ PEGTransformerFactory::FinalizeIndexElementTrampoline(PEGTransformer &transforme nulls_first_or_last = process.TakeResult(2); } auto result = TransformIndexElement(transformer, std::move(expression), desc_or_asc, nulls_first_or_last); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUniqueIndexTrampoline(PEGTransformer &transformer, @@ -8403,10 +8670,10 @@ void PEGTransformerFactory::InitializeUniqueIndexTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUniqueIndexTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformUniqueIndex(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeIndexTypeTrampoline(PEGTransformer &transformer, @@ -8414,12 +8681,12 @@ void PEGTransformerFactory::InitializeIndexTypeTrampoline(PEGTransformer &transf process.ReserveChildSlots(0); } -unique_ptr -PEGTransformerFactory::FinalizeIndexTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeIndexTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto identifier = list_pr.GetChild(1).Cast().identifier; auto result = TransformIndexType(transformer, identifier); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeRelOptionTrampoline(PEGTransformer &transformer, @@ -8433,15 +8700,15 @@ void PEGTransformerFactory::InitializeRelOptionTrampoline(PEGTransformer &transf process.PushChild({transformer.GetRule("RelOptionName"), list_pr.GetChild(0)}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeRelOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeRelOptionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto rel_option_name = process.TakeResult(0); optional> rel_option_argument_opt {}; if (process.child_results[1]) { rel_option_argument_opt = process.TakeResult>(1); } auto result = TransformRelOption(transformer, rel_option_name, std::move(rel_option_argument_opt)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeRelOptionNameTrampoline(PEGTransformer &transformer, @@ -8452,12 +8719,12 @@ void PEGTransformerFactory::InitializeRelOptionNameTrampoline(PEGTransformer &tr process.ReserveChildSlots(1); auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; - if (choice_result.name.empty() || choice_result.type == ParseResultType::IDENTIFIER || + if (choice_result.Name().empty() || choice_result.type == ParseResultType::IDENTIFIER || choice_result.type == ParseResultType::KEYWORD || choice_result.type == ParseResultType::STRING) { return; } if (!has_transform_process && - (choice_result.name == "DottedIdentifierString" || choice_result.name == "StringLiteral")) { + (choice_result.Name() == "DottedColLabel" || choice_result.Name() == "StringLiteral")) { return; } if (!has_transform_process && @@ -8467,12 +8734,12 @@ void PEGTransformerFactory::InitializeRelOptionNameTrampoline(PEGTransformer &tr return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRelOptionNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { string child; @@ -8494,22 +8761,51 @@ PEGTransformerFactory::FinalizeRelOptionNameTrampoline(PEGTransformer &transform } } auto result = TransformRelOptionName(transformer, child); - return make_uniq>(result); + return transformer.MakeResult(result); } -void PEGTransformerFactory::InitializeDottedIdentifierStringTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +void PEGTransformerFactory::InitializeDottedColLabelTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("DottedIdentifier"), list_pr.GetChild(0)}, 0); + auto &repeat_opt = list_pr.GetChild(1).Cast(); + idx_t dynamic_child_count = 0; + if (repeat_opt.HasResult()) { + auto &repeat_pr = repeat_opt.GetResult().Cast(); + auto repeat_children = repeat_pr.GetChildren(); + dynamic_child_count = repeat_children.size(); + process.ReserveChildSlots(2 + dynamic_child_count - 1); + for (idx_t i = repeat_children.size(); i > 0; i--) { + auto child_idx = i - 1; + process.PushChild({transformer.GetRule("DotColLabel"), repeat_children[child_idx].get()}, 1 + child_idx); + } + } else { + process.ReserveChildSlots(2 - 1); + } + process.PushChild({transformer.GetRule("ColLabel"), list_pr.GetChild(0)}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeDottedIdentifierStringTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { - auto dotted_identifier = process.TakeResult>(0); - auto result = TransformDottedIdentifierString(transformer, dotted_identifier); - return make_uniq>(result); +arena_ptr +PEGTransformerFactory::FinalizeDottedColLabelTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + idx_t dynamic_child_count = 0; + auto &dynamic_repeat_opt = list_pr.GetChild(1).Cast(); + if (dynamic_repeat_opt.HasResult()) { + auto &dynamic_repeat_pr = dynamic_repeat_opt.GetResult().Cast(); + auto dynamic_repeat_children = dynamic_repeat_pr.GetChildren(); + dynamic_child_count = dynamic_repeat_children.size(); + } + auto col_label = process.TakeResult(0); + optional> dot_col_label {}; + if (dynamic_child_count > 0) { + vector dot_col_label_value; + for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { + dot_col_label_value.push_back(process.TakeResult(i)); + } + dot_col_label = std::move(dot_col_label_value); + } + auto result = TransformDottedColLabel(transformer, col_label, dot_col_label); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeRelOptionArgumentOptTrampoline(PEGTransformer &transformer, @@ -8519,12 +8815,12 @@ void PEGTransformerFactory::InitializeRelOptionArgumentOptTrampoline(PEGTransfor process.PushChild({transformer.GetRule("DefArg"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRelOptionArgumentOptTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto def_arg = process.TakeResult>(0); auto result = TransformRelOptionArgumentOpt(transformer, std::move(def_arg)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDefArgTrampoline(PEGTransformer &transformer, @@ -8536,15 +8832,15 @@ void PEGTransformerFactory::InitializeDefArgTrampoline(PEGTransformer &transform auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr PEGTransformerFactory::FinalizeDefArgTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeDefArgTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDefArgNullTrampoline(PEGTransformer &transformer, @@ -8554,11 +8850,11 @@ void PEGTransformerFactory::InitializeDefArgNullTrampoline(PEGTransformer &trans process.PushChild({transformer.GetRule("NullLiteral"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDefArgNullTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto null_literal = process.TakeResult(0); auto result = TransformDefArgNull(transformer, null_literal); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDefArgKeywordTrampoline(PEGTransformer &transformer, @@ -8566,13 +8862,13 @@ void PEGTransformerFactory::InitializeDefArgKeywordTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDefArgKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto reserved_keyword = list_pr.GetChild(0).Cast().identifier.GetIdentifierName(); auto result = TransformDefArgKeyword(transformer, reserved_keyword); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDefArgStringLiteralTrampoline(PEGTransformer &transformer, @@ -8580,13 +8876,13 @@ void PEGTransformerFactory::InitializeDefArgStringLiteralTrampoline(PEGTransform process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDefArgStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(0)); auto result = TransformDefArgStringLiteral(transformer, string_literal); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNoneLiteralTrampoline(PEGTransformer &transformer, @@ -8594,10 +8890,10 @@ void PEGTransformerFactory::InitializeNoneLiteralTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNoneLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformNoneLiteral(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCreateMacroStmtTrampoline(PEGTransformer &transformer, @@ -8618,7 +8914,7 @@ void PEGTransformerFactory::InitializeCreateMacroStmtTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("MacroOrFunction"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateMacroStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -8636,7 +8932,7 @@ PEGTransformerFactory::FinalizeCreateMacroStmtTrampoline(PEGTransformer &transfo } auto result = TransformCreateMacroStmt(transformer, macro_or_function, if_not_exists, qualified_name, std::move(macro_definition)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeMacroOrFunctionTrampoline(PEGTransformer &transformer, @@ -8648,16 +8944,16 @@ void PEGTransformerFactory::InitializeMacroOrFunctionTrampoline(PEGTransformer & auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMacroOrFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeMacroKeywordTrampoline(PEGTransformer &transformer, @@ -8665,10 +8961,10 @@ void PEGTransformerFactory::InitializeMacroKeywordTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMacroKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformMacroKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeFunctionKeywordTrampoline(PEGTransformer &transformer, @@ -8676,11 +8972,11 @@ void PEGTransformerFactory::InitializeFunctionKeywordTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFunctionKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformFunctionKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeMacroDefinitionTrampoline(PEGTransformer &transformer, @@ -8694,7 +8990,7 @@ void PEGTransformerFactory::InitializeMacroDefinitionTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMacroDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional> macro_parameters {}; @@ -8703,7 +8999,7 @@ PEGTransformerFactory::FinalizeMacroDefinitionTrampoline(PEGTransformer &transfo } auto macro_definition_body = process.TakeResult>(1); auto result = TransformMacroDefinition(transformer, std::move(macro_parameters), std::move(macro_definition_body)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeMacroDefinitionBodyTrampoline(PEGTransformer &transformer, @@ -8715,16 +9011,16 @@ void PEGTransformerFactory::InitializeMacroDefinitionBodyTrampoline(PEGTransform auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMacroDefinitionBodyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeMacroParametersTrampoline(PEGTransformer &transformer, @@ -8739,7 +9035,7 @@ void PEGTransformerFactory::InitializeMacroParametersTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMacroParametersTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -8750,7 +9046,7 @@ PEGTransformerFactory::FinalizeMacroParametersTrampoline(PEGTransformer &transfo macro_parameter.push_back(process.TakeResult(i)); } auto result = TransformMacroParameters(transformer, std::move(macro_parameter)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeMacroParameterTrampoline(PEGTransformer &transformer, @@ -8762,16 +9058,16 @@ void PEGTransformerFactory::InitializeMacroParameterTrampoline(PEGTransformer &t auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMacroParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeSimpleParameterTrampoline(PEGTransformer &transformer, @@ -8785,7 +9081,7 @@ void PEGTransformerFactory::InitializeSimpleParameterTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("TypeFuncName"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSimpleParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto type_func_name = process.TakeResult(0); @@ -8794,7 +9090,7 @@ PEGTransformerFactory::FinalizeSimpleParameterTrampoline(PEGTransformer &transfo type = process.TakeResult(1); } auto result = TransformSimpleParameter(transformer, type_func_name, type); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeScalarMacroDefinitionTrampoline(PEGTransformer &transformer, @@ -8804,12 +9100,12 @@ void PEGTransformerFactory::InitializeScalarMacroDefinitionTrampoline(PEGTransfo process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeScalarMacroDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformScalarMacroDefinition(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTableMacroDefinitionTrampoline(PEGTransformer &transformer, @@ -8819,18 +9115,22 @@ void PEGTransformerFactory::InitializeTableMacroDefinitionTrampoline(PEGTransfor process.PushChild({transformer.GetRule("SelectStatementInternal"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTableMacroDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto select_statement_internal = process.TakeResult>(0); auto result = TransformTableMacroDefinition(transformer, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCreateSchemaStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(2); + process.ReserveChildSlots(3); + auto &with_option_list_opt = list_pr.GetChild(3).Cast(); + if (with_option_list_opt.HasResult()) { + process.PushChild({transformer.GetRule("WithOptionList"), with_option_list_opt.GetResult()}, 2); + } process.PushChild({transformer.GetRule("QualifiedName"), list_pr.GetChild(2)}, 1); auto &if_not_exists_opt = list_pr.GetChild(1).Cast(); if (if_not_exists_opt.HasResult()) { @@ -8838,7 +9138,7 @@ void PEGTransformerFactory::InitializeCreateSchemaStmtTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateSchemaStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional if_not_exists {}; @@ -8846,8 +9146,27 @@ PEGTransformerFactory::FinalizeCreateSchemaStmtTrampoline(PEGTransformer &transf if_not_exists = process.TakeResult(0); } auto qualified_name = process.TakeResult(1); - auto result = TransformCreateSchemaStmt(transformer, if_not_exists, qualified_name); - return make_uniq>>(std::move(result)); + optional>> with_option_list {}; + if (process.child_results[2]) { + with_option_list = process.TakeResult>>(2); + } + auto result = TransformCreateSchemaStmt(transformer, if_not_exists, qualified_name, std::move(with_option_list)); + return transformer.MakeResult>(std::move(result)); +} + +void PEGTransformerFactory::InitializeWithOptionListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + process.PushChild({transformer.GetRule("RelOptionList"), list_pr.GetChild(1)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeWithOptionListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto rel_option_list = process.TakeResult>>(0); + auto result = TransformWithOptionList(transformer, std::move(rel_option_list)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeCreateSecretStmtTrampoline(PEGTransformer &transformer, @@ -8869,7 +9188,7 @@ void PEGTransformerFactory::InitializeCreateSecretStmtTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateSecretStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional if_not_exists {}; @@ -8887,7 +9206,7 @@ PEGTransformerFactory::FinalizeCreateSecretStmtTrampoline(PEGTransformer &transf auto generic_copy_option_list = process.TakeResult>(3); auto result = TransformCreateSecretStmt(transformer, if_not_exists, secret_name, secret_storage_specifier, generic_copy_option_list); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSecretStorageSpecifierTrampoline(PEGTransformer &transformer, @@ -8895,13 +9214,13 @@ void PEGTransformerFactory::InitializeSecretStorageSpecifierTrampoline(PEGTransf process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSecretStorageSpecifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto identifier = list_pr.GetChild(1).Cast().identifier; auto result = TransformSecretStorageSpecifier(transformer, identifier); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSecretNameTrampoline(PEGTransformer &transformer, @@ -8911,11 +9230,11 @@ void PEGTransformerFactory::InitializeSecretNameTrampoline(PEGTransformer &trans process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSecretNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id = process.TakeResult(0); auto result = TransformSecretName(transformer, col_id); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCreateSequenceStmtTrampoline(PEGTransformer &transformer, @@ -8942,7 +9261,7 @@ void PEGTransformerFactory::InitializeCreateSequenceStmtTrampoline(PEGTransforme } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateSequenceStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -8967,7 +9286,7 @@ PEGTransformerFactory::FinalizeCreateSequenceStmtTrampoline(PEGTransformer &tran sequence_option = std::move(sequence_option_value); } auto result = TransformCreateSequenceStmt(transformer, if_not_exists, qualified_name, std::move(sequence_option)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSequenceOptionTrampoline(PEGTransformer &transformer, @@ -8979,16 +9298,16 @@ void PEGTransformerFactory::InitializeSequenceOptionTrampoline(PEGTransformer &t auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSequenceOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>>(0); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeSeqSetCycleTrampoline(PEGTransformer &transformer, @@ -9000,15 +9319,15 @@ void PEGTransformerFactory::InitializeSeqSetCycleTrampoline(PEGTransformer &tran auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSeqSetCycleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>>(0); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeSeqCycleTrampoline(PEGTransformer &transformer, @@ -9016,10 +9335,10 @@ void PEGTransformerFactory::InitializeSeqCycleTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeSeqCycleTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeSeqCycleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformSeqCycle(transformer); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeSeqNoCycleTrampoline(PEGTransformer &transformer, @@ -9027,10 +9346,10 @@ void PEGTransformerFactory::InitializeSeqNoCycleTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSeqNoCycleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformSeqNoCycle(transformer); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeSeqSetIncrementTrampoline(PEGTransformer &transformer, @@ -9040,7 +9359,7 @@ void PEGTransformerFactory::InitializeSeqSetIncrementTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSeqSetIncrementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -9049,7 +9368,7 @@ PEGTransformerFactory::FinalizeSeqSetIncrementTrampoline(PEGTransformer &transfo has_result = has_result_opt.HasResult(); auto expression = process.TakeResult>(0); auto result = TransformSeqSetIncrement(transformer, has_result, std::move(expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeSeqSetMinMaxTrampoline(PEGTransformer &transformer, @@ -9060,12 +9379,12 @@ void PEGTransformerFactory::InitializeSeqSetMinMaxTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("SeqMinOrMax"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSeqSetMinMaxTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto seq_min_or_max = process.TakeResult(0); auto expression = process.TakeResult>(1); auto result = TransformSeqSetMinMax(transformer, seq_min_or_max, std::move(expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeSeqNoMinMaxTrampoline(PEGTransformer &transformer, @@ -9075,11 +9394,11 @@ void PEGTransformerFactory::InitializeSeqNoMinMaxTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("SeqMinOrMax"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSeqNoMinMaxTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto seq_min_or_max = process.TakeResult(0); auto result = TransformSeqNoMinMax(transformer, seq_min_or_max); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeSeqStartWithTrampoline(PEGTransformer &transformer, @@ -9089,7 +9408,7 @@ void PEGTransformerFactory::InitializeSeqStartWithTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSeqStartWithTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); bool has_result {}; @@ -9097,7 +9416,7 @@ PEGTransformerFactory::FinalizeSeqStartWithTrampoline(PEGTransformer &transforme has_result = has_result_opt.HasResult(); auto expression = process.TakeResult>(0); auto result = TransformSeqStartWith(transformer, has_result, std::move(expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeSeqOwnedByTrampoline(PEGTransformer &transformer, @@ -9107,11 +9426,11 @@ void PEGTransformerFactory::InitializeSeqOwnedByTrampoline(PEGTransformer &trans process.PushChild({transformer.GetRule("QualifiedName"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSeqOwnedByTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto qualified_name = process.TakeResult(0); auto result = TransformSeqOwnedBy(transformer, qualified_name); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeSeqMinOrMaxTrampoline(PEGTransformer &transformer, @@ -9127,12 +9446,12 @@ void PEGTransformerFactory::InitializeSeqMinOrMaxTrampoline(PEGTransformer &tran return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSeqMinOrMaxTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { string result; if (process.child_results[0]) { @@ -9152,7 +9471,7 @@ PEGTransformerFactory::FinalizeSeqMinOrMaxTrampoline(PEGTransformer &transformer result = transformer.Transform(choice_result); } } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeMinValueTrampoline(PEGTransformer &transformer, @@ -9160,10 +9479,10 @@ void PEGTransformerFactory::InitializeMinValueTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeMinValueTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeMinValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformMinValue(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeMaxValueTrampoline(PEGTransformer &transformer, @@ -9171,10 +9490,10 @@ void PEGTransformerFactory::InitializeMaxValueTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeMaxValueTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeMaxValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformMaxValue(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCreateStatementTrampoline(PEGTransformer &transformer, @@ -9192,7 +9511,7 @@ void PEGTransformerFactory::InitializeCreateStatementTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional or_replace {}; @@ -9205,7 +9524,7 @@ PEGTransformerFactory::FinalizeCreateStatementTrampoline(PEGTransformer &transfo } auto create_statement_variation = process.TakeResult>(2); auto result = TransformCreateStatement(transformer, or_replace, temporary, std::move(create_statement_variation)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCreateStatementVariationTrampoline(PEGTransformer &transformer, @@ -9217,16 +9536,16 @@ void PEGTransformerFactory::InitializeCreateStatementVariationTrampoline(PEGTran auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateStatementVariationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeOrReplaceTrampoline(PEGTransformer &transformer, @@ -9234,10 +9553,10 @@ void PEGTransformerFactory::InitializeOrReplaceTrampoline(PEGTransformer &transf process.ReserveChildSlots(0); } -unique_ptr -PEGTransformerFactory::FinalizeOrReplaceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeOrReplaceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformOrReplace(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTemporaryTrampoline(PEGTransformer &transformer, @@ -9249,15 +9568,15 @@ void PEGTransformerFactory::InitializeTemporaryTrampoline(PEGTransformer &transf auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeTemporaryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeTemporaryTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializePersistentTrampoline(PEGTransformer &transformer, @@ -9265,10 +9584,10 @@ void PEGTransformerFactory::InitializePersistentTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePersistentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformPersistent(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTempPersistentTrampoline(PEGTransformer &transformer, @@ -9276,11 +9595,11 @@ void PEGTransformerFactory::InitializeTempPersistentTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTempPersistentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformTempPersistent(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTemporaryPersistentTrampoline(PEGTransformer &transformer, @@ -9288,11 +9607,11 @@ void PEGTransformerFactory::InitializeTemporaryPersistentTrampoline(PEGTransform process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTemporaryPersistentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformTemporaryPersistent(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCreateTableStmtTrampoline(PEGTransformer &transformer, @@ -9311,7 +9630,7 @@ void PEGTransformerFactory::InitializeCreateTableStmtTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateTableStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional if_not_exists {}; @@ -9326,7 +9645,7 @@ PEGTransformerFactory::FinalizeCreateTableStmtTrampoline(PEGTransformer &transfo } auto result = TransformCreateTableStmt(transformer, if_not_exists, qualified_name, std::move(create_table_definition), commit_action); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCreateTableDefinitionTrampoline(PEGTransformer &transformer, @@ -9338,16 +9657,16 @@ void PEGTransformerFactory::InitializeCreateTableDefinitionTrampoline(PEGTransfo auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateTableDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeCreateTableAsTrampoline(PEGTransformer &transformer, @@ -9373,7 +9692,7 @@ void PEGTransformerFactory::InitializeCreateTableAsTrampoline(PEGTransformer &tr } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateTableAsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional identifier_list {}; @@ -9395,7 +9714,7 @@ PEGTransformerFactory::FinalizeCreateTableAsTrampoline(PEGTransformer &transform } auto result = TransformCreateTableAs(transformer, std::move(identifier_list), std::move(partition_sorted_options), std::move(with_list), std::move(statement), with_data); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializePartitionSortedOptionsTrampoline(PEGTransformer &transformer, @@ -9407,16 +9726,16 @@ void PEGTransformerFactory::InitializePartitionSortedOptionsTrampoline(PEGTransf auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePartitionSortedOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializePartitionOptSortedOptionsTrampoline(PEGTransformer &transformer, @@ -9430,7 +9749,7 @@ void PEGTransformerFactory::InitializePartitionOptSortedOptionsTrampoline(PEGTra process.PushChild({transformer.GetRule("PartitionOptions"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePartitionOptSortedOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto partition_options = process.TakeResult>>(0); @@ -9440,7 +9759,7 @@ PEGTransformerFactory::FinalizePartitionOptSortedOptionsTrampoline(PEGTransforme } auto result = TransformPartitionOptSortedOptions(transformer, std::move(partition_options), std::move(sorted_options)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeSortedOptPartitionOptionsTrampoline(PEGTransformer &transformer, @@ -9454,7 +9773,7 @@ void PEGTransformerFactory::InitializeSortedOptPartitionOptionsTrampoline(PEGTra process.PushChild({transformer.GetRule("SortedOptions"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSortedOptPartitionOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto sorted_options = process.TakeResult>>(0); @@ -9464,7 +9783,7 @@ PEGTransformerFactory::FinalizeSortedOptPartitionOptionsTrampoline(PEGTransforme } auto result = TransformSortedOptPartitionOptions(transformer, std::move(sorted_options), std::move(partition_options)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializePartitionOptionsTrampoline(PEGTransformer &transformer, @@ -9479,7 +9798,7 @@ void PEGTransformerFactory::InitializePartitionOptionsTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePartitionOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -9490,7 +9809,7 @@ PEGTransformerFactory::FinalizePartitionOptionsTrampoline(PEGTransformer &transf expression.push_back(process.TakeResult>(i)); } auto result = TransformPartitionOptions(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeSortedOptionsTrampoline(PEGTransformer &transformer, @@ -9505,7 +9824,7 @@ void PEGTransformerFactory::InitializeSortedOptionsTrampoline(PEGTransformer &tr } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSortedOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -9516,7 +9835,7 @@ PEGTransformerFactory::FinalizeSortedOptionsTrampoline(PEGTransformer &transform expression.push_back(process.TakeResult>(i)); } auto result = TransformSortedOptions(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeWithDataTrampoline(PEGTransformer &transformer, @@ -9528,15 +9847,15 @@ void PEGTransformerFactory::InitializeWithDataTrampoline(PEGTransformer &transfo auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr PEGTransformerFactory::FinalizeWithDataTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeWithDataTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeWithDataOnlyTrampoline(PEGTransformer &transformer, @@ -9544,10 +9863,10 @@ void PEGTransformerFactory::InitializeWithDataOnlyTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWithDataOnlyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformWithDataOnly(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeWithNoDataTrampoline(PEGTransformer &transformer, @@ -9555,10 +9874,10 @@ void PEGTransformerFactory::InitializeWithNoDataTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWithNoDataTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformWithNoData(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeIdentifierListTrampoline(PEGTransformer &transformer, @@ -9566,7 +9885,7 @@ void PEGTransformerFactory::InitializeIdentifierListTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIdentifierListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -9576,7 +9895,7 @@ PEGTransformerFactory::FinalizeIdentifierListTrampoline(PEGTransformer &transfor identifier.push_back(identifier_item.get().Cast().identifier); } auto result = TransformIdentifierList(transformer, identifier); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeCreateColumnListTrampoline(PEGTransformer &transformer, @@ -9597,7 +9916,7 @@ void PEGTransformerFactory::InitializeCreateColumnListTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional create_table_column_list {}; @@ -9614,7 +9933,7 @@ PEGTransformerFactory::FinalizeCreateColumnListTrampoline(PEGTransformer &transf } auto result = TransformCreateColumnList(transformer, std::move(create_table_column_list), std::move(partition_sorted_options), std::move(with_list)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeIfNotExistsTrampoline(PEGTransformer &transformer, @@ -9622,10 +9941,10 @@ void PEGTransformerFactory::InitializeIfNotExistsTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIfNotExistsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformIfNotExists(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeQualifiedNameTrampoline(PEGTransformer &transformer, @@ -9637,16 +9956,16 @@ void PEGTransformerFactory::InitializeQualifiedNameTrampoline(PEGTransformer &tr auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQualifiedNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSchemaReservedIdentifierOrStringLiteralTrampoline( @@ -9657,14 +9976,14 @@ void PEGTransformerFactory::InitializeSchemaReservedIdentifierOrStringLiteralTra process.PushChild({transformer.GetRule("SchemaQualification"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSchemaReservedIdentifierOrStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto schema_qualification = process.TakeResult(0); auto reserved_identifier_or_string_literal = process.TakeResult(1); auto result = TransformSchemaReservedIdentifierOrStringLiteral(transformer, schema_qualification, reserved_identifier_or_string_literal); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCatalogReservedSchemaIdentifierTrampoline(PEGTransformer &transformer, @@ -9684,7 +10003,7 @@ void PEGTransformerFactory::InitializeCatalogReservedSchemaIdentifierTrampoline( process.PushChild({transformer.GetRule("CatalogQualification"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCatalogReservedSchemaIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -9699,7 +10018,7 @@ PEGTransformerFactory::FinalizeCatalogReservedSchemaIdentifierTrampoline(PEGTran auto reserved_identifier_or_string_literal = process.TakeResult(2 + dynamic_child_count - 1); auto result = TransformCatalogReservedSchemaIdentifier( transformer, catalog_qualification, reserved_schema_qualification, reserved_identifier_or_string_literal); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeIdentifierOrStringLiteralTrampoline(PEGTransformer &transformer, @@ -9717,12 +10036,12 @@ void PEGTransformerFactory::InitializeIdentifierOrStringLiteralTrampoline(PEGTra return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIdentifierOrStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { string child; @@ -9744,7 +10063,7 @@ PEGTransformerFactory::FinalizeIdentifierOrStringLiteralTrampoline(PEGTransforme } } auto result = TransformIdentifierOrStringLiteral(transformer, child); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeReservedIdentifierOrStringLiteralTrampoline(PEGTransformer &transformer, @@ -9755,20 +10074,20 @@ void PEGTransformerFactory::InitializeReservedIdentifierOrStringLiteralTrampolin process.ReserveChildSlots(1); auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; - if (choice_result.name.empty() || choice_result.type == ParseResultType::IDENTIFIER || + if (choice_result.Name().empty() || choice_result.type == ParseResultType::IDENTIFIER || choice_result.type == ParseResultType::KEYWORD || choice_result.type == ParseResultType::STRING) { return; } - if (!has_transform_process && (choice_result.name == "StringLiteral")) { + if (!has_transform_process && (choice_result.Name() == "StringLiteral")) { return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeReservedIdentifierOrStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { Identifier result; @@ -9787,7 +10106,7 @@ PEGTransformerFactory::FinalizeReservedIdentifierOrStringLiteralTrampoline(PEGTr result = Identifier(TransformIdentifierOrKeyword(transformer, choice_result)); } } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCatalogQualificationTrampoline(PEGTransformer &transformer, @@ -9795,13 +10114,13 @@ void PEGTransformerFactory::InitializeCatalogQualificationTrampoline(PEGTransfor process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCatalogQualificationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto catalog_name = list_pr.GetChild(0).Cast().identifier; auto result = TransformCatalogQualification(transformer, catalog_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSchemaQualificationTrampoline(PEGTransformer &transformer, @@ -9809,13 +10128,13 @@ void PEGTransformerFactory::InitializeSchemaQualificationTrampoline(PEGTransform process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSchemaQualificationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto schema_name = list_pr.GetChild(0).Cast().identifier; auto result = TransformSchemaQualification(transformer, schema_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeReservedSchemaQualificationTrampoline(PEGTransformer &transformer, @@ -9823,13 +10142,13 @@ void PEGTransformerFactory::InitializeReservedSchemaQualificationTrampoline(PEGT process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeReservedSchemaQualificationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto reserved_schema_name = list_pr.GetChild(0).Cast().identifier; auto result = TransformReservedSchemaQualification(transformer, reserved_schema_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTableQualificationTrampoline(PEGTransformer &transformer, @@ -9837,13 +10156,13 @@ void PEGTransformerFactory::InitializeTableQualificationTrampoline(PEGTransforme process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTableQualificationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto table_name = list_pr.GetChild(0).Cast().identifier; auto result = TransformTableQualification(transformer, table_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeReservedTableQualificationTrampoline(PEGTransformer &transformer, @@ -9851,13 +10170,13 @@ void PEGTransformerFactory::InitializeReservedTableQualificationTrampoline(PEGTr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeReservedTableQualificationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto reserved_table_name = list_pr.GetChild(0).Cast().identifier; auto result = TransformReservedTableQualification(transformer, reserved_table_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCreateTableColumnListTrampoline(PEGTransformer &transformer, @@ -9873,7 +10192,7 @@ void PEGTransformerFactory::InitializeCreateTableColumnListTrampoline(PEGTransfo } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateTableColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -9884,7 +10203,7 @@ PEGTransformerFactory::FinalizeCreateTableColumnListTrampoline(PEGTransformer &t create_table_column_element.push_back(process.TakeResult(i)); } auto result = TransformCreateTableColumnList(transformer, std::move(create_table_column_element)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeCreateTableColumnElementTrampoline(PEGTransformer &transformer, @@ -9896,16 +10215,16 @@ void PEGTransformerFactory::InitializeCreateTableColumnElementTrampoline(PEGTran auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateTableColumnElementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeCreateTableColumnDefinitionTrampoline(PEGTransformer &transformer, @@ -9915,12 +10234,12 @@ void PEGTransformerFactory::InitializeCreateTableColumnDefinitionTrampoline(PEGT process.PushChild({transformer.GetRule("ColumnDefinition"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateTableColumnDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto column_definition = process.TakeResult(0); auto result = TransformCreateTableColumnDefinition(transformer, std::move(column_definition)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeCreateTableConstraintTrampoline(PEGTransformer &transformer, @@ -9930,77 +10249,73 @@ void PEGTransformerFactory::InitializeCreateTableConstraintTrampoline(PEGTransfo process.PushChild({transformer.GetRule("TopLevelConstraint"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateTableConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto top_level_constraint = process.TakeResult>(0); auto result = TransformCreateTableConstraint(transformer, std::move(top_level_constraint)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeColumnDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - auto &repeat_opt = list_pr.GetChild(4).Cast(); + auto &repeat_opt = list_pr.GetChild(3).Cast(); idx_t dynamic_child_count = 0; if (repeat_opt.HasResult()) { auto &repeat_pr = repeat_opt.GetResult().Cast(); auto repeat_children = repeat_pr.GetChildren(); dynamic_child_count = repeat_children.size(); - process.ReserveChildSlots(4 + dynamic_child_count - 1); + process.ReserveChildSlots(3 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; process.PushChild({transformer.GetRule("ColumnConstraint"), repeat_children[child_idx].get()}, - 3 + child_idx); + 2 + child_idx); } } else { - process.ReserveChildSlots(4 - 1); + process.ReserveChildSlots(3 - 1); } auto &generated_column_opt = list_pr.GetChild(2).Cast(); if (generated_column_opt.HasResult()) { - process.PushChild({transformer.GetRule("GeneratedColumn"), generated_column_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule("GeneratedColumn"), generated_column_opt.GetResult()}, 1); } auto &type_opt = list_pr.GetChild(1).Cast(); if (type_opt.HasResult()) { - process.PushChild({transformer.GetRule("Type"), type_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule("Type"), type_opt.GetResult()}, 0); } - process.PushChild({transformer.GetRule("DottedIdentifier"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColumnDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); idx_t dynamic_child_count = 0; - auto &dynamic_repeat_opt = list_pr.GetChild(4).Cast(); + auto &dynamic_repeat_opt = list_pr.GetChild(3).Cast(); if (dynamic_repeat_opt.HasResult()) { auto &dynamic_repeat_pr = dynamic_repeat_opt.GetResult().Cast(); auto dynamic_repeat_children = dynamic_repeat_pr.GetChildren(); dynamic_child_count = dynamic_repeat_children.size(); } - auto dotted_identifier = process.TakeResult>(0); + auto identifier = list_pr.GetChild(0).Cast().identifier; optional type {}; - if (process.child_results[1]) { - type = process.TakeResult(1); + if (process.child_results[0]) { + type = process.TakeResult(0); } optional generated_column {}; - if (process.child_results[2]) { - generated_column = process.TakeResult(2); + if (process.child_results[1]) { + generated_column = process.TakeResult(1); } - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(3).Cast(); - has_result = has_result_opt.HasResult(); optional> column_constraint {}; if (dynamic_child_count > 0) { vector column_constraint_value; - for (idx_t i = 3; i < 3 + dynamic_child_count; i++) { + for (idx_t i = 2; i < 2 + dynamic_child_count; i++) { column_constraint_value.push_back(process.TakeResult(i)); } column_constraint = std::move(column_constraint_value); } - auto result = TransformColumnDefinition(transformer, dotted_identifier, type, std::move(generated_column), - has_result, std::move(column_constraint)); - return make_uniq>(std::move(result)); + auto result = TransformColumnDefinition(transformer, identifier, type, std::move(generated_column), + std::move(column_constraint)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeColumnConstraintTrampoline(PEGTransformer &transformer, @@ -10012,16 +10327,56 @@ void PEGTransformerFactory::InitializeColumnConstraintTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColumnConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); +} + +void PEGTransformerFactory::InitializeNameableColumnConstraintTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + process.PushChild({transformer.GetRule("ColumnConstraintElement"), list_pr.GetChild(1)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeNameableColumnConstraintTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + bool has_result {}; + auto &has_result_opt = list_pr.GetChild(0).Cast(); + has_result = has_result_opt.HasResult(); + auto column_constraint_element = process.TakeResult(0); + auto result = TransformNameableColumnConstraint(transformer, has_result, std::move(column_constraint_element)); + return transformer.MakeResult(std::move(result)); +} + +void PEGTransformerFactory::InitializeColumnConstraintElementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + auto &choice_pr = list_pr.Child(0); + auto &choice_result = choice_pr.GetResult(); + process.ReserveChildSlots(1); + auto child_rule = choice_result.GetRule(); + auto has_transform_process = child_rule && child_rule->transform_process; + if (!has_transform_process) { + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); + } + process.PushChild({*child_rule, choice_result}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeColumnConstraintElementTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto result = process.TakeResult(0); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeNotNullConstraintTrampoline(PEGTransformer &transformer, @@ -10033,17 +10388,17 @@ void PEGTransformerFactory::InitializeNotNullConstraintTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNotNullConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto child = process.TakeResult(0); auto result = TransformNotNullConstraint(transformer, child); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeNullConstraintTrampoline(PEGTransformer &transformer, @@ -10051,11 +10406,11 @@ void PEGTransformerFactory::InitializeNullConstraintTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNullConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformNullConstraint(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeNotNullColumnConstraintTrampoline(PEGTransformer &transformer, @@ -10063,11 +10418,11 @@ void PEGTransformerFactory::InitializeNotNullColumnConstraintTrampoline(PEGTrans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNotNullColumnConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformNotNullColumnConstraint(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeUniqueConstraintTrampoline(PEGTransformer &transformer, @@ -10080,7 +10435,7 @@ void PEGTransformerFactory::InitializeUniqueConstraintTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUniqueConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional constraint_timing {}; @@ -10088,7 +10443,7 @@ PEGTransformerFactory::FinalizeUniqueConstraintTrampoline(PEGTransformer &transf constraint_timing = process.TakeResult(0); } auto result = TransformUniqueConstraint(transformer, constraint_timing); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializePrimaryKeyConstraintTrampoline(PEGTransformer &transformer, @@ -10101,7 +10456,7 @@ void PEGTransformerFactory::InitializePrimaryKeyConstraintTrampoline(PEGTransfor } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePrimaryKeyConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional constraint_timing {}; @@ -10109,7 +10464,7 @@ PEGTransformerFactory::FinalizePrimaryKeyConstraintTrampoline(PEGTransformer &tr constraint_timing = process.TakeResult(0); } auto result = TransformPrimaryKeyConstraint(transformer, constraint_timing); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeConstraintTimingTrampoline(PEGTransformer &transformer, @@ -10121,16 +10476,16 @@ void PEGTransformerFactory::InitializeConstraintTimingTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeConstraintTimingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeImmediateConstraintTrampoline(PEGTransformer &transformer, @@ -10138,11 +10493,11 @@ void PEGTransformerFactory::InitializeImmediateConstraintTrampoline(PEGTransform process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeImmediateConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformImmediateConstraint(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDeferredConstraintTrampoline(PEGTransformer &transformer, @@ -10150,11 +10505,11 @@ void PEGTransformerFactory::InitializeDeferredConstraintTrampoline(PEGTransforme process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDeferredConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformDeferredConstraint(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDefaultValueTrampoline(PEGTransformer &transformer, @@ -10164,11 +10519,11 @@ void PEGTransformerFactory::InitializeDefaultValueTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("ColumnDefaultExpr"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDefaultValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto column_default_expr = process.TakeResult>(0); auto result = TransformDefaultValue(transformer, std::move(column_default_expr)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeCheckConstraintTrampoline(PEGTransformer &transformer, @@ -10178,19 +10533,22 @@ void PEGTransformerFactory::InitializeCheckConstraintTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("Expression"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCheckConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformCheckConstraint(transformer, std::move(expression)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeForeignKeyConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(3); - process.PushChild({transformer.GetRule("KeyActions"), list_pr.GetChild(3)}, 2); + auto &key_actions_opt = list_pr.GetChild(3).Cast(); + if (key_actions_opt.HasResult()) { + process.PushChild({transformer.GetRule("KeyActions"), key_actions_opt.GetResult()}, 2); + } auto &column_list_opt = list_pr.GetChild(2).Cast(); if (column_list_opt.HasResult()) { process.PushChild({transformer.GetRule("ColumnList"), ExtractResultFromParens(column_list_opt.GetResult())}, 1); @@ -10198,7 +10556,7 @@ void PEGTransformerFactory::InitializeForeignKeyConstraintTrampoline(PEGTransfor process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeForeignKeyConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto base_table_name = process.TakeResult>(0); @@ -10206,9 +10564,12 @@ PEGTransformerFactory::FinalizeForeignKeyConstraintTrampoline(PEGTransformer &tr if (process.child_results[1]) { column_list = process.TakeResult>(1); } - auto key_actions = process.TakeResult(2); + optional key_actions {}; + if (process.child_results[2]) { + key_actions = process.TakeResult(2); + } auto result = TransformForeignKeyConstraint(transformer, std::move(base_table_name), column_list, key_actions); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeColumnCollationTrampoline(PEGTransformer &transformer, @@ -10218,12 +10579,12 @@ void PEGTransformerFactory::InitializeColumnCollationTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("DottedIdentifier"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColumnCollationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto dotted_identifier = process.TakeResult>(0); auto result = TransformColumnCollation(transformer, dotted_identifier); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeColumnCompressionTrampoline(PEGTransformer &transformer, @@ -10233,40 +10594,78 @@ void PEGTransformerFactory::InitializeColumnCompressionTrampoline(PEGTransformer process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColumnCompressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id_or_string = process.TakeResult(0); auto result = TransformColumnCompression(transformer, col_id_or_string); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeKeyActionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); + auto &choice_pr = list_pr.Child(0); + auto &choice_result = choice_pr.GetResult(); + process.ReserveChildSlots(1); + auto child_rule = choice_result.GetRule(); + auto has_transform_process = child_rule && child_rule->transform_process; + if (!has_transform_process) { + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); + } + process.PushChild({*child_rule, choice_result}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeKeyActionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { + auto result = process.TakeResult(0); + return transformer.MakeResult(result); +} + +void PEGTransformerFactory::InitializeUpdateFirstKeyActionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); auto &delete_action_opt = list_pr.GetChild(1).Cast(); if (delete_action_opt.HasResult()) { process.PushChild({transformer.GetRule("DeleteAction"), delete_action_opt.GetResult()}, 1); } - auto &update_action_opt = list_pr.GetChild(0).Cast(); + process.PushChild({transformer.GetRule("UpdateAction"), list_pr.GetChild(0)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeUpdateFirstKeyActionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto update_action = process.TakeResult(0); + optional delete_action {}; + if (process.child_results[1]) { + delete_action = process.TakeResult(1); + } + auto result = TransformUpdateFirstKeyActions(transformer, update_action, delete_action); + return transformer.MakeResult(result); +} + +void PEGTransformerFactory::InitializeDeleteFirstKeyActionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(2); + auto &update_action_opt = list_pr.GetChild(1).Cast(); if (update_action_opt.HasResult()) { - process.PushChild({transformer.GetRule("UpdateAction"), update_action_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule("UpdateAction"), update_action_opt.GetResult()}, 1); } + process.PushChild({transformer.GetRule("DeleteAction"), list_pr.GetChild(0)}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeKeyActionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr +PEGTransformerFactory::FinalizeDeleteFirstKeyActionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto delete_action = process.TakeResult(0); optional update_action {}; - if (process.child_results[0]) { - update_action = process.TakeResult(0); - } - optional delete_action {}; if (process.child_results[1]) { - delete_action = process.TakeResult(1); + update_action = process.TakeResult(1); } - auto result = TransformKeyActions(transformer, update_action, delete_action); - return make_uniq>(result); + auto result = TransformDeleteFirstKeyActions(transformer, delete_action, update_action); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeUpdateActionTrampoline(PEGTransformer &transformer, @@ -10276,11 +10675,11 @@ void PEGTransformerFactory::InitializeUpdateActionTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("KeyAction"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUpdateActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto key_action = process.TakeResult(0); auto result = TransformUpdateAction(transformer, key_action); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDeleteActionTrampoline(PEGTransformer &transformer, @@ -10290,11 +10689,11 @@ void PEGTransformerFactory::InitializeDeleteActionTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("KeyAction"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDeleteActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto key_action = process.TakeResult(0); auto result = TransformDeleteAction(transformer, key_action); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeKeyActionTrampoline(PEGTransformer &transformer, @@ -10310,13 +10709,13 @@ void PEGTransformerFactory::InitializeKeyActionTrampoline(PEGTransformer &transf return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeKeyActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeKeyActionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { string result; if (process.child_results[0]) { result = process.TakeResult(0); @@ -10335,7 +10734,7 @@ PEGTransformerFactory::FinalizeKeyActionTrampoline(PEGTransformer &transformer, result = transformer.Transform(choice_result); } } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeNoKeyActionTrampoline(PEGTransformer &transformer, @@ -10343,10 +10742,10 @@ void PEGTransformerFactory::InitializeNoKeyActionTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNoKeyActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformNoKeyAction(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeRestrictKeyActionTrampoline(PEGTransformer &transformer, @@ -10354,11 +10753,11 @@ void PEGTransformerFactory::InitializeRestrictKeyActionTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRestrictKeyActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformRestrictKeyAction(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCascadeKeyActionTrampoline(PEGTransformer &transformer, @@ -10366,11 +10765,11 @@ void PEGTransformerFactory::InitializeCascadeKeyActionTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCascadeKeyActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCascadeKeyAction(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSetNullKeyActionTrampoline(PEGTransformer &transformer, @@ -10378,11 +10777,11 @@ void PEGTransformerFactory::InitializeSetNullKeyActionTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSetNullKeyActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformSetNullKeyAction(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSetDefaultKeyActionTrampoline(PEGTransformer &transformer, @@ -10390,11 +10789,11 @@ void PEGTransformerFactory::InitializeSetDefaultKeyActionTrampoline(PEGTransform process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSetDefaultKeyActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformSetDefaultKeyAction(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTopLevelConstraintTrampoline(PEGTransformer &transformer, @@ -10404,7 +10803,7 @@ void PEGTransformerFactory::InitializeTopLevelConstraintTrampoline(PEGTransforme process.PushChild({transformer.GetRule("TopLevelConstraintList"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTopLevelConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -10413,7 +10812,7 @@ PEGTransformerFactory::FinalizeTopLevelConstraintTrampoline(PEGTransformer &tran has_result = has_result_opt.HasResult(); auto top_level_constraint_list = process.TakeResult>(0); auto result = TransformTopLevelConstraint(transformer, has_result, std::move(top_level_constraint_list)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTopLevelConstraintListTrampoline(PEGTransformer &transformer, @@ -10425,16 +10824,16 @@ void PEGTransformerFactory::InitializeTopLevelConstraintListTrampoline(PEGTransf auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTopLevelConstraintListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTopCheckConstraintTrampoline(PEGTransformer &transformer, @@ -10444,12 +10843,12 @@ void PEGTransformerFactory::InitializeTopCheckConstraintTrampoline(PEGTransforme process.PushChild({transformer.GetRule("CheckConstraint"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTopCheckConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto check_constraint = process.TakeResult(0); auto result = TransformTopCheckConstraint(transformer, std::move(check_constraint)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTopPrimaryKeyConstraintTrampoline(PEGTransformer &transformer, @@ -10463,7 +10862,7 @@ void PEGTransformerFactory::InitializeTopPrimaryKeyConstraintTrampoline(PEGTrans process.PushChild({transformer.GetRule("ColumnIdList"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTopPrimaryKeyConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto column_id_list = process.TakeResult>(0); @@ -10472,7 +10871,7 @@ PEGTransformerFactory::FinalizeTopPrimaryKeyConstraintTrampoline(PEGTransformer constraint_timing = process.TakeResult(1); } auto result = TransformTopPrimaryKeyConstraint(transformer, column_id_list, constraint_timing); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTopUniqueConstraintTrampoline(PEGTransformer &transformer, @@ -10486,7 +10885,7 @@ void PEGTransformerFactory::InitializeTopUniqueConstraintTrampoline(PEGTransform process.PushChild({transformer.GetRule("ColumnIdList"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTopUniqueConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto column_id_list = process.TakeResult>(0); @@ -10495,7 +10894,7 @@ PEGTransformerFactory::FinalizeTopUniqueConstraintTrampoline(PEGTransformer &tra constraint_timing = process.TakeResult(1); } auto result = TransformTopUniqueConstraint(transformer, column_id_list, constraint_timing); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTopForeignKeyConstraintTrampoline(PEGTransformer &transformer, @@ -10506,13 +10905,13 @@ void PEGTransformerFactory::InitializeTopForeignKeyConstraintTrampoline(PEGTrans process.PushChild({transformer.GetRule("ColumnIdList"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTopForeignKeyConstraintTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto column_id_list = process.TakeResult>(0); auto foreign_key_constraint = process.TakeResult(1); auto result = TransformTopForeignKeyConstraint(transformer, column_id_list, std::move(foreign_key_constraint)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeColumnIdListTrampoline(PEGTransformer &transformer, @@ -10527,7 +10926,7 @@ void PEGTransformerFactory::InitializeColumnIdListTrampoline(PEGTransformer &tra } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColumnIdListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); @@ -10537,7 +10936,7 @@ PEGTransformerFactory::FinalizeColumnIdListTrampoline(PEGTransformer &transforme col_id.push_back(process.TakeResult(i)); } auto result = TransformColumnIdList(transformer, col_id); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeDottedIdentifierTrampoline(PEGTransformer &transformer, @@ -10559,7 +10958,7 @@ void PEGTransformerFactory::InitializeDottedIdentifierTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDottedIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -10580,7 +10979,7 @@ PEGTransformerFactory::FinalizeDottedIdentifierTrampoline(PEGTransformer &transf dot_col_label = std::move(dot_col_label_value); } auto result = TransformDottedIdentifier(transformer, identifier, dot_col_label); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeDotColLabelTrampoline(PEGTransformer &transformer, @@ -10590,11 +10989,11 @@ void PEGTransformerFactory::InitializeDotColLabelTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("ColLabel"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDotColLabelTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_label = process.TakeResult(0); auto result = TransformDotColLabel(transformer, col_label); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeIdentifierTrampoline(PEGTransformer &transformer, @@ -10602,10 +11001,10 @@ void PEGTransformerFactory::InitializeIdentifierTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.parse_result.Cast().identifier.GetIdentifierName(); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeColIdTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { @@ -10615,22 +11014,23 @@ void PEGTransformerFactory::InitializeColIdTrampoline(PEGTransformer &transforme process.ReserveChildSlots(1); auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; - if (choice_result.name.empty() || choice_result.type == ParseResultType::IDENTIFIER || + if (choice_result.Name().empty() || choice_result.type == ParseResultType::IDENTIFIER || choice_result.type == ParseResultType::KEYWORD || choice_result.type == ParseResultType::STRING) { return; } - if (!has_transform_process && (choice_result.name == "UnreservedKeyword" || - choice_result.name == "ColumnNameKeyword" || choice_result.name == "Identifier")) { + if (!has_transform_process && + (choice_result.Name() == "UnreservedKeyword" || choice_result.Name() == "ColumnNameKeyword" || + choice_result.Name() == "Identifier")) { return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr PEGTransformerFactory::FinalizeColIdTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeColIdTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { Identifier result; if (process.child_results[0]) { result = process.TakeResult(0); @@ -10647,7 +11047,7 @@ unique_ptr PEGTransformerFactory::FinalizeColIdTrampoline( result = Identifier(TransformIdentifierOrKeyword(transformer, choice_result)); } } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeColIdOrStringTrampoline(PEGTransformer &transformer, @@ -10658,17 +11058,17 @@ void PEGTransformerFactory::InitializeColIdOrStringTrampoline(PEGTransformer &tr process.ReserveChildSlots(1); auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; - if (choice_result.name.empty() || choice_result.type == ParseResultType::IDENTIFIER || + if (choice_result.Name().empty() || choice_result.type == ParseResultType::IDENTIFIER || choice_result.type == ParseResultType::KEYWORD || choice_result.type == ParseResultType::STRING) { return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColIdOrStringTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { Identifier result; @@ -10687,7 +11087,7 @@ PEGTransformerFactory::FinalizeColIdOrStringTrampoline(PEGTransformer &transform result = Identifier(TransformIdentifierOrKeyword(transformer, choice_result)); } } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTypeFuncNameTrampoline(PEGTransformer &transformer, @@ -10698,21 +11098,21 @@ void PEGTransformerFactory::InitializeTypeFuncNameTrampoline(PEGTransformer &tra process.ReserveChildSlots(1); auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; - if (choice_result.name.empty() || choice_result.type == ParseResultType::IDENTIFIER || + if (choice_result.Name().empty() || choice_result.type == ParseResultType::IDENTIFIER || choice_result.type == ParseResultType::KEYWORD || choice_result.type == ParseResultType::STRING) { return; } - if (!has_transform_process && (choice_result.name == "UnreservedKeyword" || - choice_result.name == "TypeFuncKeyword" || choice_result.name == "Identifier")) { + if (!has_transform_process && (choice_result.Name() == "UnreservedKeyword" || + choice_result.Name() == "TypeFuncKeyword" || choice_result.Name() == "Identifier")) { return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTypeFuncNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { Identifier result; if (process.child_results[0]) { @@ -10730,7 +11130,7 @@ PEGTransformerFactory::FinalizeTypeFuncNameTrampoline(PEGTransformer &transforme result = Identifier(TransformIdentifierOrKeyword(transformer, choice_result)); } } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTypeFuncKeywordTrampoline(PEGTransformer &transformer, @@ -10746,16 +11146,16 @@ void PEGTransformerFactory::InitializeTypeFuncKeywordTrampoline(PEGTransformer & return; } if (!has_transform_process && - (choice_result.name == "TypeNameKeyword" || choice_result.name == "FuncNameKeyword")) { + (choice_result.Name() == "TypeNameKeyword" || choice_result.Name() == "FuncNameKeyword")) { return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTypeFuncKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { string result; @@ -10776,7 +11176,7 @@ PEGTransformerFactory::FinalizeTypeFuncKeywordTrampoline(PEGTransformer &transfo result = transformer.Transform(choice_result); } } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeColLabelTrampoline(PEGTransformer &transformer, @@ -10792,19 +11192,19 @@ void PEGTransformerFactory::InitializeColLabelTrampoline(PEGTransformer &transfo return; } if (!has_transform_process && - (choice_result.name == "ReservedKeyword" || choice_result.name == "UnreservedKeyword" || - choice_result.name == "ColumnNameKeyword" || choice_result.name == "FuncNameKeyword" || - choice_result.name == "TypeNameKeyword")) { + (choice_result.Name() == "ReservedKeyword" || choice_result.Name() == "UnreservedKeyword" || + choice_result.Name() == "ColumnNameKeyword" || choice_result.Name() == "FuncNameKeyword" || + choice_result.Name() == "TypeNameKeyword")) { return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr PEGTransformerFactory::FinalizeColLabelTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeColLabelTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { string result; if (process.child_results[0]) { result = process.TakeResult(0); @@ -10823,7 +11223,7 @@ unique_ptr PEGTransformerFactory::FinalizeColLabelTrampoli result = transformer.Transform(choice_result); } } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeColLabelOrStringTrampoline(PEGTransformer &transformer, @@ -10834,17 +11234,17 @@ void PEGTransformerFactory::InitializeColLabelOrStringTrampoline(PEGTransformer process.ReserveChildSlots(1); auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; - if (choice_result.name.empty() || choice_result.type == ParseResultType::IDENTIFIER || + if (choice_result.Name().empty() || choice_result.type == ParseResultType::IDENTIFIER || choice_result.type == ParseResultType::KEYWORD || choice_result.type == ParseResultType::STRING) { return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColLabelOrStringTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { Identifier result; @@ -10863,7 +11263,7 @@ PEGTransformerFactory::FinalizeColLabelOrStringTrampoline(PEGTransformer &transf result = Identifier(TransformIdentifierOrKeyword(transformer, choice_result)); } } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeColLabelIdentifierTrampoline(PEGTransformer &transformer, @@ -10873,12 +11273,12 @@ void PEGTransformerFactory::InitializeColLabelIdentifierTrampoline(PEGTransforme process.PushChild({transformer.GetRule("ColLabel"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColLabelIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_label = process.TakeResult(0); auto result = TransformColLabelIdentifier(transformer, col_label); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeStringLiteralIdentifierTrampoline(PEGTransformer &transformer, @@ -10886,40 +11286,88 @@ void PEGTransformerFactory::InitializeStringLiteralIdentifierTrampoline(PEGTrans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeStringLiteralIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(0)); auto result = TransformStringLiteralIdentifier(transformer, string_literal); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeGeneratedColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(2); + process.ReserveChildSlots(3); auto &generated_column_type_opt = list_pr.GetChild(3).Cast(); if (generated_column_type_opt.HasResult()) { - process.PushChild({transformer.GetRule("GeneratedColumnType"), generated_column_type_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule("GeneratedColumnType"), generated_column_type_opt.GetResult()}, 2); + } + process.PushChild({transformer.GetRule("Expression"), ExtractResultFromParens(list_pr.GetChild(2))}, 1); + auto &generated_opt = list_pr.GetChild(0).Cast(); + if (generated_opt.HasResult()) { + process.PushChild({transformer.GetRule("Generated"), generated_opt.GetResult()}, 0); } - process.PushChild({transformer.GetRule("Expression"), ExtractResultFromParens(list_pr.GetChild(2))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGeneratedColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto &list_pr = process.parse_result.Cast(); - bool has_result {}; - auto &has_result_opt = list_pr.GetChild(0).Cast(); - has_result = has_result_opt.HasResult(); - auto expression = process.TakeResult>(0); + optional generated {}; + if (process.child_results[0]) { + generated = process.TakeResult(0); + } + auto expression = process.TakeResult>(1); optional generated_column_type {}; - if (process.child_results[1]) { - generated_column_type = process.TakeResult(1); + if (process.child_results[2]) { + generated_column_type = process.TakeResult(2); + } + auto result = TransformGeneratedColumn(transformer, generated, std::move(expression), generated_column_type); + return transformer.MakeResult(std::move(result)); +} + +void PEGTransformerFactory::InitializeGeneratedTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + auto &choice_pr = list_pr.Child(0); + auto &choice_result = choice_pr.GetResult(); + process.ReserveChildSlots(1); + auto child_rule = choice_result.GetRule(); + auto has_transform_process = child_rule && child_rule->transform_process; + if (!has_transform_process) { + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } - auto result = TransformGeneratedColumn(transformer, has_result, std::move(expression), generated_column_type); - return make_uniq>(std::move(result)); + process.PushChild({*child_rule, choice_result}, 0); +} + +arena_ptr PEGTransformerFactory::FinalizeGeneratedTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto result = process.TakeResult(0); + return transformer.MakeResult(result); +} + +void PEGTransformerFactory::InitializeGeneratedAlwaysTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + process.ReserveChildSlots(0); +} + +arena_ptr +PEGTransformerFactory::FinalizeGeneratedAlwaysTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto result = TransformGeneratedAlways(transformer); + return transformer.MakeResult(result); +} + +void PEGTransformerFactory::InitializeGeneratedByDefaultTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + process.ReserveChildSlots(0); +} + +arena_ptr +PEGTransformerFactory::FinalizeGeneratedByDefaultTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto result = TransformGeneratedByDefault(transformer); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeGeneratedColumnTypeTrampoline(PEGTransformer &transformer, @@ -10931,16 +11379,16 @@ void PEGTransformerFactory::InitializeGeneratedColumnTypeTrampoline(PEGTransform auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGeneratedColumnTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCommitActionTrampoline(PEGTransformer &transformer, @@ -10950,11 +11398,11 @@ void PEGTransformerFactory::InitializeCommitActionTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("PreserveOrDelete"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCommitActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto preserve_or_delete = process.TakeResult(0); auto result = TransformCommitAction(transformer, preserve_or_delete); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializePreserveOrDeleteTrampoline(PEGTransformer &transformer, @@ -10966,16 +11414,16 @@ void PEGTransformerFactory::InitializePreserveOrDeleteTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePreserveOrDeleteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializePreserveRowsTrampoline(PEGTransformer &transformer, @@ -10983,10 +11431,10 @@ void PEGTransformerFactory::InitializePreserveRowsTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePreserveRowsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformPreserveRows(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDeleteRowsTrampoline(PEGTransformer &transformer, @@ -10994,10 +11442,10 @@ void PEGTransformerFactory::InitializeDeleteRowsTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDeleteRowsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformDeleteRows(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeVirtualGeneratedColumnTrampoline(PEGTransformer &transformer, @@ -11005,11 +11453,11 @@ void PEGTransformerFactory::InitializeVirtualGeneratedColumnTrampoline(PEGTransf process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeVirtualGeneratedColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformVirtualGeneratedColumn(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeStoredGeneratedColumnTrampoline(PEGTransformer &transformer, @@ -11017,11 +11465,11 @@ void PEGTransformerFactory::InitializeStoredGeneratedColumnTrampoline(PEGTransfo process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeStoredGeneratedColumnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformStoredGeneratedColumn(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCreateTriggerStmtTrampoline(PEGTransformer &transformer, @@ -11047,7 +11495,7 @@ void PEGTransformerFactory::InitializeCreateTriggerStmtTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateTriggerStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional if_not_exists {}; @@ -11070,7 +11518,7 @@ PEGTransformerFactory::FinalizeCreateTriggerStmtTrampoline(PEGTransformer &trans auto result = TransformCreateTriggerStmt(transformer, if_not_exists, trigger_name, trigger_timing, trigger_event, std::move(base_table_name), referencing_clause, for_each_clause, std::move(trigger_body)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTriggerBodyTrampoline(PEGTransformer &transformer, @@ -11082,15 +11530,15 @@ void PEGTransformerFactory::InitializeTriggerBodyTrampoline(PEGTransformer &tran auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTriggerBodyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTriggerNameTrampoline(PEGTransformer &transformer, @@ -11098,12 +11546,12 @@ void PEGTransformerFactory::InitializeTriggerNameTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTriggerNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto identifier = list_pr.GetChild(0).Cast().identifier; auto result = TransformTriggerName(transformer, identifier); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeReferencingClauseTrampoline(PEGTransformer &transformer, @@ -11117,7 +11565,7 @@ void PEGTransformerFactory::InitializeReferencingClauseTrampoline(PEGTransformer process.PushChild({transformer.GetRule("ReferencingItem"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeReferencingClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto referencing_item = process.TakeResult(0); @@ -11126,7 +11574,7 @@ PEGTransformerFactory::FinalizeReferencingClauseTrampoline(PEGTransformer &trans referencing_item_1 = process.TakeResult(1); } auto result = TransformReferencingClause(transformer, referencing_item, referencing_item_1); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeReferencingItemTrampoline(PEGTransformer &transformer, @@ -11138,16 +11586,16 @@ void PEGTransformerFactory::InitializeReferencingItemTrampoline(PEGTransformer & auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeReferencingItemTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeReferencingNewTableAsTrampoline(PEGTransformer &transformer, @@ -11157,12 +11605,12 @@ void PEGTransformerFactory::InitializeReferencingNewTableAsTrampoline(PEGTransfo process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(3)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeReferencingNewTableAsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id = process.TakeResult(0); auto result = TransformReferencingNewTableAs(transformer, col_id); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeReferencingOldTableAsTrampoline(PEGTransformer &transformer, @@ -11172,12 +11620,12 @@ void PEGTransformerFactory::InitializeReferencingOldTableAsTrampoline(PEGTransfo process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(3)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeReferencingOldTableAsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id = process.TakeResult(0); auto result = TransformReferencingOldTableAs(transformer, col_id); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTriggerTimingTrampoline(PEGTransformer &transformer, @@ -11189,16 +11637,16 @@ void PEGTransformerFactory::InitializeTriggerTimingTrampoline(PEGTransformer &tr auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTriggerTimingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTriggerBeforeTrampoline(PEGTransformer &transformer, @@ -11206,11 +11654,11 @@ void PEGTransformerFactory::InitializeTriggerBeforeTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTriggerBeforeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformTriggerBefore(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTriggerAfterTrampoline(PEGTransformer &transformer, @@ -11218,10 +11666,10 @@ void PEGTransformerFactory::InitializeTriggerAfterTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTriggerAfterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformTriggerAfter(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTriggerInsteadOfTrampoline(PEGTransformer &transformer, @@ -11229,11 +11677,11 @@ void PEGTransformerFactory::InitializeTriggerInsteadOfTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTriggerInsteadOfTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformTriggerInsteadOf(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTriggerEventTrampoline(PEGTransformer &transformer, @@ -11245,15 +11693,15 @@ void PEGTransformerFactory::InitializeTriggerEventTrampoline(PEGTransformer &tra auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTriggerEventTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTriggerEventInsertTrampoline(PEGTransformer &transformer, @@ -11261,11 +11709,11 @@ void PEGTransformerFactory::InitializeTriggerEventInsertTrampoline(PEGTransforme process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTriggerEventInsertTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformTriggerEventInsert(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTriggerEventDeleteTrampoline(PEGTransformer &transformer, @@ -11273,11 +11721,11 @@ void PEGTransformerFactory::InitializeTriggerEventDeleteTrampoline(PEGTransforme process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTriggerEventDeleteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformTriggerEventDelete(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTriggerEventUpdateTrampoline(PEGTransformer &transformer, @@ -11285,11 +11733,11 @@ void PEGTransformerFactory::InitializeTriggerEventUpdateTrampoline(PEGTransforme process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTriggerEventUpdateTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformTriggerEventUpdate(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTriggerEventUpdateOfTrampoline(PEGTransformer &transformer, @@ -11299,12 +11747,12 @@ void PEGTransformerFactory::InitializeTriggerEventUpdateOfTrampoline(PEGTransfor process.PushChild({transformer.GetRule("TriggerColumnList"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTriggerEventUpdateOfTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto trigger_column_list = process.TakeResult>(0); auto result = TransformTriggerEventUpdateOf(transformer, trigger_column_list); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTriggerColumnListTrampoline(PEGTransformer &transformer, @@ -11319,7 +11767,7 @@ void PEGTransformerFactory::InitializeTriggerColumnListTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTriggerColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -11330,7 +11778,7 @@ PEGTransformerFactory::FinalizeTriggerColumnListTrampoline(PEGTransformer &trans col_id.push_back(process.TakeResult(i)); } auto result = TransformTriggerColumnList(transformer, col_id); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeForEachClauseTrampoline(PEGTransformer &transformer, @@ -11342,16 +11790,16 @@ void PEGTransformerFactory::InitializeForEachClauseTrampoline(PEGTransformer &tr auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeForEachClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeForEachRowTrampoline(PEGTransformer &transformer, @@ -11359,10 +11807,10 @@ void PEGTransformerFactory::InitializeForEachRowTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeForEachRowTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformForEachRow(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeForEachStatementTrampoline(PEGTransformer &transformer, @@ -11370,11 +11818,11 @@ void PEGTransformerFactory::InitializeForEachStatementTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeForEachStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformForEachStatement(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCreateTypeStmtTrampoline(PEGTransformer &transformer, @@ -11389,7 +11837,7 @@ void PEGTransformerFactory::InitializeCreateTypeStmtTrampoline(PEGTransformer &t } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateTypeStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional if_not_exists {}; @@ -11399,7 +11847,7 @@ PEGTransformerFactory::FinalizeCreateTypeStmtTrampoline(PEGTransformer &transfor auto qualified_name = process.TakeResult(1); auto create_type = process.TakeResult>(2); auto result = TransformCreateTypeStmt(transformer, if_not_exists, qualified_name, std::move(create_type)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCreateTypeTrampoline(PEGTransformer &transformer, @@ -11411,15 +11859,15 @@ void PEGTransformerFactory::InitializeCreateTypeTrampoline(PEGTransformer &trans auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCreateTypeFromTypeTrampoline(PEGTransformer &transformer, @@ -11429,12 +11877,12 @@ void PEGTransformerFactory::InitializeCreateTypeFromTypeTrampoline(PEGTransforme process.PushChild({transformer.GetRule("Type"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateTypeFromTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto type = process.TakeResult(0); auto result = TransformCreateTypeFromType(transformer, type); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeEnumSelectTypeTrampoline(PEGTransformer &transformer, @@ -11445,23 +11893,44 @@ void PEGTransformerFactory::InitializeEnumSelectTypeTrampoline(PEGTransformer &t 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeEnumSelectTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto select_statement_internal = process.TakeResult>(0); auto result = TransformEnumSelectType(transformer, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeEnumStringLiteralListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - process.ReserveChildSlots(0); + auto &list_pr = process.parse_result.Cast(); + auto &repeat_opt = list_pr.GetChild(2).Cast(); + idx_t dynamic_child_count = 0; + if (repeat_opt.HasResult()) { + auto &repeat_pr = repeat_opt.GetResult().Cast(); + auto repeat_children = repeat_pr.GetChildren(); + dynamic_child_count = repeat_children.size(); + process.ReserveChildSlots(1 + dynamic_child_count - 1); + for (idx_t i = repeat_children.size(); i > 0; i--) { + auto child_idx = i - 1; + process.PushChild({transformer.GetRule("ArrayBounds"), repeat_children[child_idx].get()}, 0 + child_idx); + } + } else { + process.ReserveChildSlots(1 - 1); + } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeEnumStringLiteralListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); + idx_t dynamic_child_count = 0; + auto &dynamic_repeat_opt = list_pr.GetChild(2).Cast(); + if (dynamic_repeat_opt.HasResult()) { + auto &dynamic_repeat_pr = dynamic_repeat_opt.GetResult().Cast(); + auto dynamic_repeat_children = dynamic_repeat_pr.GetChildren(); + dynamic_child_count = dynamic_repeat_children.size(); + } optional> string_literal {}; auto &string_literal_opt = ExtractResultFromParens(list_pr.GetChild(1)).Cast(); if (string_literal_opt.HasResult()) { @@ -11473,8 +11942,16 @@ PEGTransformerFactory::FinalizeEnumStringLiteralListTrampoline(PEGTransformer &t } string_literal = std::move(string_literal_value); } - auto result = TransformEnumStringLiteralList(transformer, string_literal); - return make_uniq>>(std::move(result)); + optional> array_bounds {}; + if (dynamic_child_count > 0) { + vector array_bounds_value; + for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { + array_bounds_value.push_back(process.TakeResult(i)); + } + array_bounds = std::move(array_bounds_value); + } + auto result = TransformEnumStringLiteralList(transformer, string_literal, array_bounds); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCreateViewStmtTrampoline(PEGTransformer &transformer, @@ -11505,7 +11982,7 @@ void PEGTransformerFactory::InitializeCreateViewStmtTrampoline(PEGTransformer &t } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateViewStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional create_secure {}; @@ -11533,7 +12010,7 @@ PEGTransformerFactory::FinalizeCreateViewStmtTrampoline(PEGTransformer &transfor auto result = TransformCreateViewStmt(transformer, create_secure, create_recursive, if_not_exists, qualified_name, insert_column_list, std::move(with_list), std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCreateRecursiveTrampoline(PEGTransformer &transformer, @@ -11541,11 +12018,11 @@ void PEGTransformerFactory::InitializeCreateRecursiveTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateRecursiveTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCreateRecursive(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCreateSecureTrampoline(PEGTransformer &transformer, @@ -11553,33 +12030,33 @@ void PEGTransformerFactory::InitializeCreateSecureTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateSecureTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCreateSecure(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDeallocateStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(1); + process.ReserveChildSlots(2); + process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(2)}, 1); auto &deallocate_prepare_opt = list_pr.GetChild(1).Cast(); if (deallocate_prepare_opt.HasResult()) { process.PushChild({transformer.GetRule("DeallocatePrepare"), deallocate_prepare_opt.GetResult()}, 0); } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDeallocateStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto &list_pr = process.parse_result.Cast(); optional deallocate_prepare {}; if (process.child_results[0]) { deallocate_prepare = process.TakeResult(0); } - auto identifier = list_pr.GetChild(2).Cast().identifier; - auto result = TransformDeallocateStatement(transformer, deallocate_prepare, identifier); - return make_uniq>>(std::move(result)); + auto col_id_or_string = process.TakeResult(1); + auto result = TransformDeallocateStatement(transformer, deallocate_prepare, col_id_or_string); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDeallocatePrepareTrampoline(PEGTransformer &transformer, @@ -11587,11 +12064,11 @@ void PEGTransformerFactory::InitializeDeallocatePrepareTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDeallocatePrepareTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformDeallocatePrepare(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDeleteStatementTrampoline(PEGTransformer &transformer, @@ -11617,7 +12094,7 @@ void PEGTransformerFactory::InitializeDeleteStatementTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDeleteStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional with_clause {}; @@ -11640,17 +12117,21 @@ PEGTransformerFactory::FinalizeDeleteStatementTrampoline(PEGTransformer &transfo auto result = TransformDeleteStatement(transformer, std::move(with_clause), std::move(target_opt_alias), std::move(delete_using_clause), std::move(where_clause), std::move(returning_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTruncateStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(1); + process.ReserveChildSlots(2); + auto &truncate_options_opt = list_pr.GetChild(3).Cast(); + if (truncate_options_opt.HasResult()) { + process.PushChild({transformer.GetRule("TruncateOptions"), truncate_options_opt.GetResult()}, 1); + } process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTruncateStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -11658,35 +12139,112 @@ PEGTransformerFactory::FinalizeTruncateStatementTrampoline(PEGTransformer &trans auto &has_result_opt = list_pr.GetChild(1).Cast(); has_result = has_result_opt.HasResult(); auto base_table_name = process.TakeResult>(0); - auto result = TransformTruncateStatement(transformer, has_result, std::move(base_table_name)); - return make_uniq>>(std::move(result)); + optional truncate_options {}; + if (process.child_results[1]) { + truncate_options = process.TakeResult(1); + } + auto result = TransformTruncateStatement(transformer, has_result, std::move(base_table_name), truncate_options); + return transformer.MakeResult>(std::move(result)); +} + +void PEGTransformerFactory::InitializeTruncateOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + auto &choice_pr = list_pr.Child(0); + auto &choice_result = choice_pr.GetResult(); + process.ReserveChildSlots(1); + auto child_rule = choice_result.GetRule(); + auto has_transform_process = child_rule && child_rule->transform_process; + if (!has_transform_process) { + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); + } + process.PushChild({*child_rule, choice_result}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeTruncateOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto result = process.TakeResult(0); + return transformer.MakeResult(result); +} + +void PEGTransformerFactory::InitializeTruncateAliasOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(2); + auto &truncate_drop_behavior_opt = list_pr.GetChild(1).Cast(); + if (truncate_drop_behavior_opt.HasResult()) { + process.PushChild({transformer.GetRule("TruncateDropBehavior"), truncate_drop_behavior_opt.GetResult()}, 1); + } + process.PushChild({transformer.GetRule("TargetAlias"), list_pr.GetChild(0)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeTruncateAliasOptionsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto target_alias = process.TakeResult(0); + optional truncate_drop_behavior {}; + if (process.child_results[1]) { + truncate_drop_behavior = process.TakeResult(1); + } + auto result = TransformTruncateAliasOptions(transformer, target_alias, truncate_drop_behavior); + return transformer.MakeResult(result); +} + +void PEGTransformerFactory::InitializeTruncateDropBehaviorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + process.PushChild({transformer.GetRule("DropBehavior"), list_pr.GetChild(0)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeTruncateDropBehaviorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto drop_behavior = process.TakeResult(0); + auto result = TransformTruncateDropBehavior(transformer, drop_behavior); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTargetOptAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - auto &col_id_opt = list_pr.GetChild(2).Cast(); - if (col_id_opt.HasResult()) { - process.PushChild({transformer.GetRule("ColId"), col_id_opt.GetResult()}, 1); + auto &target_alias_opt = list_pr.GetChild(1).Cast(); + if (target_alias_opt.HasResult()) { + process.PushChild({transformer.GetRule("TargetAlias"), target_alias_opt.GetResult()}, 1); + } + process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(0)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeTargetOptAliasTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto base_table_name = process.TakeResult>(0); + optional target_alias {}; + if (process.child_results[1]) { + target_alias = process.TakeResult(1); } - process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(0)}, 0); + auto result = TransformTargetOptAlias(transformer, std::move(base_table_name), target_alias); + return transformer.MakeResult>(std::move(result)); } -unique_ptr -PEGTransformerFactory::FinalizeTargetOptAliasTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +void PEGTransformerFactory::InitializeTargetAliasTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(1)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeTargetAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - auto base_table_name = process.TakeResult>(0); bool has_result {}; - auto &has_result_opt = list_pr.GetChild(1).Cast(); + auto &has_result_opt = list_pr.GetChild(0).Cast(); has_result = has_result_opt.HasResult(); - optional col_id {}; - if (process.child_results[1]) { - col_id = process.TakeResult(1); - } - auto result = TransformTargetOptAlias(transformer, std::move(base_table_name), has_result, col_id); - return make_uniq>>(std::move(result)); + auto col_id = process.TakeResult(0); + auto result = TransformTargetAlias(transformer, has_result, col_id); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDeleteUsingClauseTrampoline(PEGTransformer &transformer, @@ -11701,7 +12259,7 @@ void PEGTransformerFactory::InitializeDeleteUsingClauseTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDeleteUsingClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -11712,7 +12270,7 @@ PEGTransformerFactory::FinalizeDeleteUsingClauseTrampoline(PEGTransformer &trans table_ref.push_back(process.TakeResult>(i)); } auto result = TransformDeleteUsingClause(transformer, std::move(table_ref)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeDescribeStatementTrampoline(PEGTransformer &transformer, @@ -11724,17 +12282,17 @@ void PEGTransformerFactory::InitializeDescribeStatementTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDescribeStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto child = process.TakeResult>(0); auto result = TransformDescribeStatement(transformer, std::move(child)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeShowDeprecatedSelectTrampoline(PEGTransformer &transformer, @@ -11745,13 +12303,13 @@ void PEGTransformerFactory::InitializeShowDeprecatedSelectTrampoline(PEGTransfor process.PushChild({transformer.GetRule("ShowRule"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeShowDeprecatedSelectTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto show_rule = process.TakeResult(0); auto select_statement_internal = process.TakeResult>(1); auto result = TransformShowDeprecatedSelect(transformer, show_rule, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDescribeSelectTrampoline(PEGTransformer &transformer, @@ -11762,13 +12320,13 @@ void PEGTransformerFactory::InitializeDescribeSelectTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("DescribeOrSummarize"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDescribeSelectTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto describe_or_summarize = process.TakeResult(0); auto select_statement_internal = process.TakeResult>(1); auto result = TransformDescribeSelect(transformer, describe_or_summarize, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeShowAllTablesTrampoline(PEGTransformer &transformer, @@ -11778,7 +12336,7 @@ void PEGTransformerFactory::InitializeShowAllTablesTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("ShowOrDescribe"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeShowAllTablesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -11787,7 +12345,7 @@ PEGTransformerFactory::FinalizeShowAllTablesTrampoline(PEGTransformer &transform auto &has_result_opt = list_pr.GetChild(2).Cast(); has_result = has_result_opt.HasResult(); auto result = TransformShowAllTables(transformer, show_or_describe, has_result); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeShowTablesTrampoline(PEGTransformer &transformer, @@ -11798,12 +12356,12 @@ void PEGTransformerFactory::InitializeShowTablesTrampoline(PEGTransformer &trans process.PushChild({transformer.GetRule("ShowOrDescribe"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeShowTablesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto show_or_describe = process.TakeResult(0); auto qualified_name = process.TakeResult(1); auto result = TransformShowTables(transformer, show_or_describe, qualified_name); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeShowByNameTrampoline(PEGTransformer &transformer, @@ -11817,7 +12375,7 @@ void PEGTransformerFactory::InitializeShowByNameTrampoline(PEGTransformer &trans process.PushChild({transformer.GetRule("ShowRule"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeShowByNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto show_rule = process.TakeResult(0); optional show_target {}; @@ -11825,7 +12383,7 @@ PEGTransformerFactory::FinalizeShowByNameTrampoline(PEGTransformer &transformer, show_target = process.TakeResult(1); } auto result = TransformShowByName(transformer, show_rule, std::move(show_target)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDescribeByNameTrampoline(PEGTransformer &transformer, @@ -11839,7 +12397,7 @@ void PEGTransformerFactory::InitializeDescribeByNameTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("DescribeOrSummarize"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDescribeByNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto describe_or_summarize = process.TakeResult(0); @@ -11848,7 +12406,7 @@ PEGTransformerFactory::FinalizeDescribeByNameTrampoline(PEGTransformer &transfor describe_target = process.TakeResult(1); } auto result = TransformDescribeByName(transformer, describe_or_summarize, std::move(describe_target)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDescribeOrSummarizeTrampoline(PEGTransformer &transformer, @@ -11860,16 +12418,16 @@ void PEGTransformerFactory::InitializeDescribeOrSummarizeTrampoline(PEGTransform auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDescribeOrSummarizeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeShowTargetTrampoline(PEGTransformer &transformer, @@ -11880,22 +12438,22 @@ void PEGTransformerFactory::InitializeShowTargetTrampoline(PEGTransformer &trans process.ReserveChildSlots(1); auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; - if (!has_transform_process && (choice_result.name == "ShowSpecialForm")) { + if (!has_transform_process && (choice_result.Name() == "ShowSpecialForm")) { return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeShowTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { DescribeTarget result {}; if (process.child_results[0]) { result = process.TakeResult(0); } - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeShowDeprecatedQualifiedTableNameTrampoline(PEGTransformer &transformer, @@ -11905,12 +12463,12 @@ void PEGTransformerFactory::InitializeShowDeprecatedQualifiedTableNameTrampoline process.PushChild({transformer.GetRule("QualifiedTableName"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeShowDeprecatedQualifiedTableNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto qualified_table_name = process.TakeResult>(0); auto result = TransformShowDeprecatedQualifiedTableName(transformer, std::move(qualified_table_name)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeShowSettingNameTrampoline(PEGTransformer &transformer, @@ -11918,13 +12476,13 @@ void PEGTransformerFactory::InitializeShowSettingNameTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeShowSettingNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto setting_name = list_pr.GetChild(0).Cast().identifier; auto result = TransformShowSettingName(transformer, setting_name); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeDescribeTargetTrampoline(PEGTransformer &transformer, @@ -11936,16 +12494,16 @@ void PEGTransformerFactory::InitializeDescribeTargetTrampoline(PEGTransformer &t auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDescribeTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeDescribeBaseTableNameTrampoline(PEGTransformer &transformer, @@ -11955,12 +12513,12 @@ void PEGTransformerFactory::InitializeDescribeBaseTableNameTrampoline(PEGTransfo process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDescribeBaseTableNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto base_table_name = process.TakeResult>(0); auto result = TransformDescribeBaseTableName(transformer, std::move(base_table_name)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeDescribeStringLiteralTrampoline(PEGTransformer &transformer, @@ -11968,13 +12526,13 @@ void PEGTransformerFactory::InitializeDescribeStringLiteralTrampoline(PEGTransfo process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDescribeStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(0)); auto result = TransformDescribeStringLiteral(transformer, string_literal); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeSummarizeTrampoline(PEGTransformer &transformer, @@ -11984,11 +12542,11 @@ void PEGTransformerFactory::InitializeSummarizeTrampoline(PEGTransformer &transf process.PushChild({transformer.GetRule("SummarizeRule"), list_pr.GetChild(0)}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeSummarizeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeSummarizeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto summarize_rule = process.TakeResult(0); auto result = TransformSummarize(transformer, summarize_rule); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSummarizeRuleTrampoline(PEGTransformer &transformer, @@ -11996,11 +12554,11 @@ void PEGTransformerFactory::InitializeSummarizeRuleTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSummarizeRuleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformSummarizeRule(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeShowOrDescribeTrampoline(PEGTransformer &transformer, @@ -12012,16 +12570,16 @@ void PEGTransformerFactory::InitializeShowOrDescribeTrampoline(PEGTransformer &t auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeShowOrDescribeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeShowRuleTrampoline(PEGTransformer &transformer, @@ -12029,10 +12587,10 @@ void PEGTransformerFactory::InitializeShowRuleTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeShowRuleTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeShowRuleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformShowRule(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDescribeRuleTrampoline(PEGTransformer &transformer, @@ -12044,15 +12602,15 @@ void PEGTransformerFactory::InitializeDescribeRuleTrampoline(PEGTransformer &tra auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDescribeRuleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDescribeLongRuleTrampoline(PEGTransformer &transformer, @@ -12060,11 +12618,11 @@ void PEGTransformerFactory::InitializeDescribeLongRuleTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDescribeLongRuleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformDescribeLongRule(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDescRuleTrampoline(PEGTransformer &transformer, @@ -12072,10 +12630,10 @@ void PEGTransformerFactory::InitializeDescRuleTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeDescRuleTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeDescRuleTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformDescRule(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDetachStatementTrampoline(PEGTransformer &transformer, @@ -12088,7 +12646,7 @@ void PEGTransformerFactory::InitializeDetachStatementTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDetachStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -12101,7 +12659,7 @@ PEGTransformerFactory::FinalizeDetachStatementTrampoline(PEGTransformer &transfo } auto catalog_name = list_pr.GetChild(3).Cast().identifier; auto result = TransformDetachStatement(transformer, has_result, if_exists, catalog_name); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDropStatementTrampoline(PEGTransformer &transformer, @@ -12115,7 +12673,7 @@ void PEGTransformerFactory::InitializeDropStatementTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("DropEntries"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDropStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto drop_entries = process.TakeResult>(0); @@ -12124,7 +12682,7 @@ PEGTransformerFactory::FinalizeDropStatementTrampoline(PEGTransformer &transform drop_behavior = process.TakeResult(1); } auto result = TransformDropStatement(transformer, std::move(drop_entries), drop_behavior); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDropEntriesTrampoline(PEGTransformer &transformer, @@ -12136,15 +12694,15 @@ void PEGTransformerFactory::InitializeDropEntriesTrampoline(PEGTransformer &tran auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDropEntriesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDropTriggerTrampoline(PEGTransformer &transformer, @@ -12159,7 +12717,7 @@ void PEGTransformerFactory::InitializeDropTriggerTrampoline(PEGTransformer &tran } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDropTriggerTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional if_exists {}; if (process.child_results[0]) { @@ -12168,7 +12726,7 @@ PEGTransformerFactory::FinalizeDropTriggerTrampoline(PEGTransformer &transformer auto trigger_name = process.TakeResult(1); auto base_table_name = process.TakeResult>(2); auto result = TransformDropTrigger(transformer, if_exists, trigger_name, std::move(base_table_name)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDropTableTrampoline(PEGTransformer &transformer, @@ -12188,8 +12746,8 @@ void PEGTransformerFactory::InitializeDropTableTrampoline(PEGTransformer &transf process.PushChild({transformer.GetRule("TableOrView"), list_pr.GetChild(0)}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeDropTableTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeDropTableTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(2)); auto dynamic_child_count = dynamic_list_items.size(); @@ -12203,7 +12761,7 @@ PEGTransformerFactory::FinalizeDropTableTrampoline(PEGTransformer &transformer, base_table_name.push_back(process.TakeResult>(i)); } auto result = TransformDropTable(transformer, table_or_view, if_exists, std::move(base_table_name)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDropTableFunctionTrampoline(PEGTransformer &transformer, @@ -12217,7 +12775,7 @@ void PEGTransformerFactory::InitializeDropTableFunctionTrampoline(PEGTransformer process.PushChild({transformer.GetRule("CommentMacroTable"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDropTableFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -12232,7 +12790,7 @@ PEGTransformerFactory::FinalizeDropTableFunctionTrampoline(PEGTransformer &trans table_function_name.push_back(table_function_name_item.get().Cast().identifier); } auto result = TransformDropTableFunction(transformer, comment_macro_table, if_exists, table_function_name); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDropFunctionTrampoline(PEGTransformer &transformer, @@ -12252,7 +12810,7 @@ void PEGTransformerFactory::InitializeDropFunctionTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("FunctionTypeMacro"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDropFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(2)); @@ -12267,7 +12825,7 @@ PEGTransformerFactory::FinalizeDropFunctionTrampoline(PEGTransformer &transforme function_identifier.push_back(process.TakeResult(i)); } auto result = TransformDropFunction(transformer, function_type_macro, if_exists, function_identifier); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDropSchemaTrampoline(PEGTransformer &transformer, @@ -12286,7 +12844,7 @@ void PEGTransformerFactory::InitializeDropSchemaTrampoline(PEGTransformer &trans } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDropSchemaTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(2)); @@ -12300,7 +12858,7 @@ PEGTransformerFactory::FinalizeDropSchemaTrampoline(PEGTransformer &transformer, qualified_name.push_back(process.TakeResult(i)); } auto result = TransformDropSchema(transformer, if_exists, qualified_name); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDropIndexTrampoline(PEGTransformer &transformer, @@ -12319,8 +12877,8 @@ void PEGTransformerFactory::InitializeDropIndexTrampoline(PEGTransformer &transf } } -unique_ptr -PEGTransformerFactory::FinalizeDropIndexTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeDropIndexTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(2)); auto dynamic_child_count = dynamic_list_items.size(); @@ -12333,7 +12891,7 @@ PEGTransformerFactory::FinalizeDropIndexTrampoline(PEGTransformer &transformer, qualified_index_name.push_back(process.TakeResult(i)); } auto result = TransformDropIndex(transformer, if_exists, qualified_index_name); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeQualifiedIndexNameTrampoline(PEGTransformer &transformer, @@ -12345,16 +12903,16 @@ void PEGTransformerFactory::InitializeQualifiedIndexNameTrampoline(PEGTransforme auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQualifiedIndexNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeQualifiedIndexNameStringTrampoline(PEGTransformer &transformer, @@ -12362,13 +12920,13 @@ void PEGTransformerFactory::InitializeQualifiedIndexNameStringTrampoline(PEGTran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQualifiedIndexNameStringTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto index_name = list_pr.GetChild(0).Cast().identifier; auto result = TransformQualifiedIndexNameString(transformer, index_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSchemaReservedIndexTrampoline(PEGTransformer &transformer, @@ -12378,14 +12936,14 @@ void PEGTransformerFactory::InitializeSchemaReservedIndexTrampoline(PEGTransform process.PushChild({transformer.GetRule("SchemaQualification"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSchemaReservedIndexTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto schema_qualification = process.TakeResult(0); auto reserved_index_name = list_pr.GetChild(1).Cast().identifier; auto result = TransformSchemaReservedIndex(transformer, schema_qualification, reserved_index_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCatalogReservedSchemaIndexTrampoline(PEGTransformer &transformer, @@ -12396,7 +12954,7 @@ void PEGTransformerFactory::InitializeCatalogReservedSchemaIndexTrampoline(PEGTr process.PushChild({transformer.GetRule("CatalogQualification"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCatalogReservedSchemaIndexTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -12405,7 +12963,7 @@ PEGTransformerFactory::FinalizeCatalogReservedSchemaIndexTrampoline(PEGTransform auto reserved_index_name = list_pr.GetChild(2).Cast().identifier; auto result = TransformCatalogReservedSchemaIndex(transformer, catalog_qualification, reserved_schema_qualification, reserved_index_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDropSequenceTrampoline(PEGTransformer &transformer, @@ -12424,7 +12982,7 @@ void PEGTransformerFactory::InitializeDropSequenceTrampoline(PEGTransformer &tra } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDropSequenceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(2)); @@ -12438,7 +12996,7 @@ PEGTransformerFactory::FinalizeDropSequenceTrampoline(PEGTransformer &transforme qualified_sequence_name.push_back(process.TakeResult(i)); } auto result = TransformDropSequence(transformer, if_exists, qualified_sequence_name); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDropCollationTrampoline(PEGTransformer &transformer, @@ -12457,7 +13015,7 @@ void PEGTransformerFactory::InitializeDropCollationTrampoline(PEGTransformer &tr } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDropCollationTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -12472,7 +13030,7 @@ PEGTransformerFactory::FinalizeDropCollationTrampoline(PEGTransformer &transform collation_name.push_back(process.TakeResult(i)); } auto result = TransformDropCollation(transformer, if_exists, collation_name); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDropTypeTrampoline(PEGTransformer &transformer, @@ -12491,8 +13049,8 @@ void PEGTransformerFactory::InitializeDropTypeTrampoline(PEGTransformer &transfo } } -unique_ptr PEGTransformerFactory::FinalizeDropTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeDropTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(2)); auto dynamic_child_count = dynamic_list_items.size(); @@ -12505,7 +13063,7 @@ unique_ptr PEGTransformerFactory::FinalizeDropTypeTrampoli qualified_type_name.push_back(process.TakeResult(i)); } auto result = TransformDropType(transformer, if_exists, qualified_type_name); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDropSecretTrampoline(PEGTransformer &transformer, @@ -12527,7 +13085,7 @@ void PEGTransformerFactory::InitializeDropSecretTrampoline(PEGTransformer &trans } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDropSecretTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional temporary {}; if (process.child_results[0]) { @@ -12543,7 +13101,7 @@ PEGTransformerFactory::FinalizeDropSecretTrampoline(PEGTransformer &transformer, drop_secret_storage = process.TakeResult(3); } auto result = TransformDropSecret(transformer, temporary, if_exists, secret_name, drop_secret_storage); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTableOrViewTrampoline(PEGTransformer &transformer, @@ -12555,15 +13113,15 @@ void PEGTransformerFactory::InitializeTableOrViewTrampoline(PEGTransformer &tran auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTableOrViewTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeMaterializedViewEntryTrampoline(PEGTransformer &transformer, @@ -12571,11 +13129,11 @@ void PEGTransformerFactory::InitializeMaterializedViewEntryTrampoline(PEGTransfo process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMaterializedViewEntryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformMaterializedViewEntry(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeFunctionTypeMacroTrampoline(PEGTransformer &transformer, @@ -12587,16 +13145,16 @@ void PEGTransformerFactory::InitializeFunctionTypeMacroTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFunctionTypeMacroTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeFunctionTypeMacroKeywordTrampoline(PEGTransformer &transformer, @@ -12604,11 +13162,11 @@ void PEGTransformerFactory::InitializeFunctionTypeMacroKeywordTrampoline(PEGTran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFunctionTypeMacroKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformFunctionTypeMacroKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeFunctionTypeFunctionTrampoline(PEGTransformer &transformer, @@ -12616,11 +13174,11 @@ void PEGTransformerFactory::InitializeFunctionTypeFunctionTrampoline(PEGTransfor process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFunctionTypeFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformFunctionTypeFunction(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDropBehaviorTrampoline(PEGTransformer &transformer, @@ -12632,15 +13190,15 @@ void PEGTransformerFactory::InitializeDropBehaviorTrampoline(PEGTransformer &tra auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDropBehaviorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCascadeDropBehaviorTrampoline(PEGTransformer &transformer, @@ -12648,11 +13206,11 @@ void PEGTransformerFactory::InitializeCascadeDropBehaviorTrampoline(PEGTransform process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCascadeDropBehaviorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCascadeDropBehavior(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeRestrictDropBehaviorTrampoline(PEGTransformer &transformer, @@ -12660,11 +13218,11 @@ void PEGTransformerFactory::InitializeRestrictDropBehaviorTrampoline(PEGTransfor process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRestrictDropBehaviorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformRestrictDropBehavior(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeIfExistsTrampoline(PEGTransformer &transformer, @@ -12672,10 +13230,10 @@ void PEGTransformerFactory::InitializeIfExistsTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeIfExistsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeIfExistsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformIfExists(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDropSecretStorageTrampoline(PEGTransformer &transformer, @@ -12683,36 +13241,36 @@ void PEGTransformerFactory::InitializeDropSecretStorageTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDropSecretStorageTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto identifier = list_pr.GetChild(1).Cast().identifier; auto result = TransformDropSecretStorage(transformer, identifier); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeExecuteStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(1); + process.ReserveChildSlots(2); auto &table_function_arguments_opt = list_pr.GetChild(2).Cast(); if (table_function_arguments_opt.HasResult()) { - process.PushChild({transformer.GetRule("TableFunctionArguments"), table_function_arguments_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule("TableFunctionArguments"), table_function_arguments_opt.GetResult()}, 1); } + process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExecuteStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto &list_pr = process.parse_result.Cast(); - auto identifier = list_pr.GetChild(1).Cast().identifier; + auto col_id_or_string = process.TakeResult(0); optional> table_function_arguments {}; - if (process.child_results[0]) { - table_function_arguments = process.TakeResult>(0); + if (process.child_results[1]) { + table_function_arguments = process.TakeResult>(1); } - auto result = TransformExecuteStatement(transformer, identifier, std::move(table_function_arguments)); - return make_uniq>>(std::move(result)); + auto result = TransformExecuteStatement(transformer, col_id_or_string, std::move(table_function_arguments)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeExplainStatementTrampoline(PEGTransformer &transformer, @@ -12730,7 +13288,7 @@ void PEGTransformerFactory::InitializeExplainStatementTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExplainStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional analyze_keyword {}; @@ -12744,7 +13302,7 @@ PEGTransformerFactory::FinalizeExplainStatementTrampoline(PEGTransformer &transf auto explainable_statements = process.TakeResult>(2); auto result = TransformExplainStatement(transformer, analyze_keyword, explain_option_list, std::move(explainable_statements)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeExplainOptionListTrampoline(PEGTransformer &transformer, @@ -12759,7 +13317,7 @@ void PEGTransformerFactory::InitializeExplainOptionListTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExplainOptionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -12770,7 +13328,7 @@ PEGTransformerFactory::FinalizeExplainOptionListTrampoline(PEGTransformer &trans explain_option.push_back(process.TakeResult(i)); } auto result = TransformExplainOptionList(transformer, explain_option); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeExplainOptionTrampoline(PEGTransformer &transformer, @@ -12784,7 +13342,7 @@ void PEGTransformerFactory::InitializeExplainOptionTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("ExplainOptionName"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExplainOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto explain_option_name = process.TakeResult(0); @@ -12793,7 +13351,7 @@ PEGTransformerFactory::FinalizeExplainOptionTrampoline(PEGTransformer &transform expression = process.TakeResult>(1); } auto result = TransformExplainOption(transformer, explain_option_name, std::move(expression)); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeExplainOptionNameTrampoline(PEGTransformer &transformer, @@ -12804,12 +13362,12 @@ void PEGTransformerFactory::InitializeExplainOptionNameTrampoline(PEGTransformer process.ReserveChildSlots(1); auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; - if (choice_result.name.empty() || choice_result.type == ParseResultType::IDENTIFIER || + if (choice_result.Name().empty() || choice_result.type == ParseResultType::IDENTIFIER || choice_result.type == ParseResultType::KEYWORD || choice_result.type == ParseResultType::STRING) { return; } if (!has_transform_process && - (choice_result.name == "FuncNameKeyword" || choice_result.name == "TypeNameKeyword")) { + (choice_result.Name() == "FuncNameKeyword" || choice_result.Name() == "TypeNameKeyword")) { return; } if (!has_transform_process) { @@ -12818,7 +13376,7 @@ void PEGTransformerFactory::InitializeExplainOptionNameTrampoline(PEGTransformer process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExplainOptionNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -12829,7 +13387,7 @@ PEGTransformerFactory::FinalizeExplainOptionNameTrampoline(PEGTransformer &trans } else { result = TransformExplainOptionName(transformer, choice_result); } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeExplainSelectStatementTrampoline(PEGTransformer &transformer, @@ -12839,12 +13397,12 @@ void PEGTransformerFactory::InitializeExplainSelectStatementTrampoline(PEGTransf process.PushChild({transformer.GetRule("SelectStatementInternal"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExplainSelectStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto select_statement_internal = process.TakeResult>(0); auto result = TransformExplainSelectStatement(transformer, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeExplainableStatementsTrampoline(PEGTransformer &transformer, @@ -12856,16 +13414,16 @@ void PEGTransformerFactory::InitializeExplainableStatementsTrampoline(PEGTransfo auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExplainableStatementsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeExportStatementTrampoline(PEGTransformer &transformer, @@ -12882,7 +13440,7 @@ void PEGTransformerFactory::InitializeExportStatementTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExportStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -12896,7 +13454,7 @@ PEGTransformerFactory::FinalizeExportStatementTrampoline(PEGTransformer &transfo generic_copy_option_list = process.TakeResult>(1); } auto result = TransformExportStatement(transformer, export_source, string_literal, generic_copy_option_list); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeExportSourceTrampoline(PEGTransformer &transformer, @@ -12904,12 +13462,12 @@ void PEGTransformerFactory::InitializeExportSourceTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExportSourceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto catalog_name = list_pr.GetChild(0).Cast().identifier; auto result = TransformExportSource(transformer, catalog_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeImportStatementTrampoline(PEGTransformer &transformer, @@ -12917,13 +13475,13 @@ void PEGTransformerFactory::InitializeImportStatementTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeImportStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(2)); auto result = TransformImportStatement(transformer, string_literal); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeColumnReferenceTrampoline(PEGTransformer &transformer, @@ -12935,17 +13493,17 @@ void PEGTransformerFactory::InitializeColumnReferenceTrampoline(PEGTransformer & auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColumnReferenceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto child = process.TakeResult>(0); auto result = TransformColumnReference(transformer, std::move(child)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNestedSchemaTableColumnNameTrampoline(PEGTransformer &transformer, @@ -12965,7 +13523,7 @@ void PEGTransformerFactory::InitializeNestedSchemaTableColumnNameTrampoline(PEGT process.PushChild({transformer.GetRule("CatalogQualification"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNestedSchemaTableColumnNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -12983,7 +13541,7 @@ PEGTransformerFactory::FinalizeNestedSchemaTableColumnNameTrampoline(PEGTransfor auto result = TransformNestedSchemaTableColumnName(transformer, catalog_qualification, reserved_schema_qualification, reserved_schema_qualification_1, reserved_schema_qualification_2, reserved_column_name); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCatalogReservedSchemaTableColumnNameTrampoline( @@ -12995,7 +13553,7 @@ void PEGTransformerFactory::InitializeCatalogReservedSchemaTableColumnNameTrampo process.PushChild({transformer.GetRule("CatalogQualification"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCatalogReservedSchemaTableColumnNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -13006,7 +13564,7 @@ PEGTransformerFactory::FinalizeCatalogReservedSchemaTableColumnNameTrampoline(PE auto result = TransformCatalogReservedSchemaTableColumnName(transformer, catalog_qualification, reserved_schema_qualification, reserved_table_qualification, reserved_column_name); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSchemaReservedTableColumnNameTrampoline(PEGTransformer &transformer, @@ -13017,7 +13575,7 @@ void PEGTransformerFactory::InitializeSchemaReservedTableColumnNameTrampoline(PE process.PushChild({transformer.GetRule("SchemaQualification"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSchemaReservedTableColumnNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -13026,7 +13584,7 @@ PEGTransformerFactory::FinalizeSchemaReservedTableColumnNameTrampoline(PEGTransf auto reserved_column_name = list_pr.GetChild(2).Cast().identifier; auto result = TransformSchemaReservedTableColumnName(transformer, schema_qualification, reserved_table_qualification, reserved_column_name); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTableReservedColumnNameTrampoline(PEGTransformer &transformer, @@ -13036,14 +13594,14 @@ void PEGTransformerFactory::InitializeTableReservedColumnNameTrampoline(PEGTrans process.PushChild({transformer.GetRule("TableQualification"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTableReservedColumnNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto table_qualification = process.TakeResult(0); auto reserved_column_name = list_pr.GetChild(1).Cast().identifier; auto result = TransformTableReservedColumnName(transformer, table_qualification, reserved_column_name); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeFunctionExpressionTrampoline(PEGTransformer &transformer, @@ -13066,7 +13624,7 @@ void PEGTransformerFactory::InitializeFunctionExpressionTrampoline(PEGTransforme process.PushChild({transformer.GetRule("FunctionIdentifier"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFunctionExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -13090,7 +13648,7 @@ PEGTransformerFactory::FinalizeFunctionExpressionTrampoline(PEGTransformer &tran auto result = TransformFunctionExpression(transformer, function_identifier, std::move(function_expression_arguments), std::move(within_group_clause), std::move(filter_clause), has_result, std::move(over_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeFunctionExpressionArgumentsTrampoline(PEGTransformer &transformer, @@ -13101,12 +13659,12 @@ void PEGTransformerFactory::InitializeFunctionExpressionArgumentsTrampoline(PEGT {transformer.GetRule("FunctionExpressionArgumentList"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFunctionExpressionArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto function_expression_argument_list = process.TakeResult(0); auto result = TransformFunctionExpressionArguments(transformer, std::move(function_expression_argument_list)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeFunctionExpressionArgumentListTrampoline(PEGTransformer &transformer, @@ -13131,7 +13689,7 @@ void PEGTransformerFactory::InitializeFunctionExpressionArgumentListTrampoline(P } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFunctionExpressionArgumentListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional distinct_or_all {}; @@ -13153,7 +13711,7 @@ PEGTransformerFactory::FinalizeFunctionExpressionArgumentListTrampoline(PEGTrans auto result = TransformFunctionExpressionArgumentList(transformer, distinct_or_all, std::move(function_argument_list), std::move(order_by_clause), ignore_or_respect_nulls); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeFunctionArgumentListTrampoline(PEGTransformer &transformer, @@ -13168,7 +13726,7 @@ void PEGTransformerFactory::InitializeFunctionArgumentListTrampoline(PEGTransfor } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFunctionArgumentListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -13179,7 +13737,7 @@ PEGTransformerFactory::FinalizeFunctionArgumentListTrampoline(PEGTransformer &tr function_argument.push_back(process.TakeResult(i)); } auto result = TransformFunctionArgumentList(transformer, std::move(function_argument)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeFunctionIdentifierTrampoline(PEGTransformer &transformer, @@ -13191,16 +13749,16 @@ void PEGTransformerFactory::InitializeFunctionIdentifierTrampoline(PEGTransforme auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFunctionIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeFunctionNameAsQualifiedNameTrampoline(PEGTransformer &transformer, @@ -13208,13 +13766,13 @@ void PEGTransformerFactory::InitializeFunctionNameAsQualifiedNameTrampoline(PEGT process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFunctionNameAsQualifiedNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto function_name = list_pr.GetChild(0).Cast().identifier; auto result = TransformFunctionNameAsQualifiedName(transformer, function_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCatalogReservedSchemaFunctionNameTrampoline(PEGTransformer &transformer, @@ -13238,7 +13796,7 @@ void PEGTransformerFactory::InitializeCatalogReservedSchemaFunctionNameTrampolin process.PushChild({transformer.GetRule("CatalogQualification"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCatalogReservedSchemaFunctionNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -13261,7 +13819,7 @@ PEGTransformerFactory::FinalizeCatalogReservedSchemaFunctionNameTrampoline(PEGTr auto reserved_function_name = list_pr.GetChild(2).Cast().identifier; auto result = TransformCatalogReservedSchemaFunctionName(transformer, catalog_qualification, reserved_schema_qualification, reserved_function_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSchemaReservedFunctionNameTrampoline(PEGTransformer &transformer, @@ -13271,14 +13829,14 @@ void PEGTransformerFactory::InitializeSchemaReservedFunctionNameTrampoline(PEGTr process.PushChild({transformer.GetRule("SchemaQualification"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSchemaReservedFunctionNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto schema_qualification = process.TakeResult(0); auto reserved_function_name = list_pr.GetChild(1).Cast().identifier; auto result = TransformSchemaReservedFunctionName(transformer, schema_qualification, reserved_function_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDistinctOrAllTrampoline(PEGTransformer &transformer, @@ -13290,16 +13848,16 @@ void PEGTransformerFactory::InitializeDistinctOrAllTrampoline(PEGTransformer &tr auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDistinctOrAllTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDistinctKeywordTrampoline(PEGTransformer &transformer, @@ -13307,11 +13865,11 @@ void PEGTransformerFactory::InitializeDistinctKeywordTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDistinctKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformDistinctKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeAllKeywordTrampoline(PEGTransformer &transformer, @@ -13319,10 +13877,10 @@ void PEGTransformerFactory::InitializeAllKeywordTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAllKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformAllKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeWithinGroupClauseTrampoline(PEGTransformer &transformer, @@ -13332,12 +13890,12 @@ void PEGTransformerFactory::InitializeWithinGroupClauseTrampoline(PEGTransformer process.PushChild({transformer.GetRule("OrderByClause"), ExtractResultFromParens(list_pr.GetChild(2))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWithinGroupClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto order_by_clause = process.TakeResult>(0); auto result = TransformWithinGroupClause(transformer, std::move(order_by_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeFilterClauseTrampoline(PEGTransformer &transformer, @@ -13347,11 +13905,11 @@ void PEGTransformerFactory::InitializeFilterClauseTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("FilterClauseExpression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFilterClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto filter_clause_expression = process.TakeResult>(0); auto result = TransformFilterClause(transformer, std::move(filter_clause_expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeFilterClauseExpressionTrampoline(PEGTransformer &transformer, @@ -13361,12 +13919,12 @@ void PEGTransformerFactory::InitializeFilterClauseExpressionTrampoline(PEGTransf process.PushChild({transformer.GetRule("FilterClauseContents"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFilterClauseExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto filter_clause_contents = process.TakeResult>(0); auto result = TransformFilterClauseExpression(transformer, std::move(filter_clause_contents)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeFilterClauseContentsTrampoline(PEGTransformer &transformer, @@ -13376,7 +13934,7 @@ void PEGTransformerFactory::InitializeFilterClauseContentsTrampoline(PEGTransfor process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFilterClauseContentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -13385,7 +13943,7 @@ PEGTransformerFactory::FinalizeFilterClauseContentsTrampoline(PEGTransformer &tr has_result = has_result_opt.HasResult(); auto expression = process.TakeResult>(0); auto result = TransformFilterClauseContents(transformer, has_result, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIgnoreOrRespectNullsTrampoline(PEGTransformer &transformer, @@ -13397,16 +13955,16 @@ void PEGTransformerFactory::InitializeIgnoreOrRespectNullsTrampoline(PEGTransfor auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIgnoreOrRespectNullsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeIgnoreNullsTrampoline(PEGTransformer &transformer, @@ -13414,10 +13972,10 @@ void PEGTransformerFactory::InitializeIgnoreNullsTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIgnoreNullsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformIgnoreNulls(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeRespectNullsTrampoline(PEGTransformer &transformer, @@ -13425,10 +13983,10 @@ void PEGTransformerFactory::InitializeRespectNullsTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRespectNullsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformRespectNulls(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeParenthesisExpressionTrampoline(PEGTransformer &transformer, @@ -13449,7 +14007,7 @@ void PEGTransformerFactory::InitializeParenthesisExpressionTrampoline(PEGTransfo } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeParenthesisExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -13469,7 +14027,7 @@ PEGTransformerFactory::FinalizeParenthesisExpressionTrampoline(PEGTransformer &t expression = std::move(expression_value); } auto result = TransformParenthesisExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeConstantLiteralTrampoline(PEGTransformer &transformer, @@ -13481,17 +14039,17 @@ void PEGTransformerFactory::InitializeConstantLiteralTrampoline(PEGTransformer & auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeConstantLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto child = process.TakeResult(0); auto result = TransformConstantLiteral(transformer, child); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNullLiteralTrampoline(PEGTransformer &transformer, @@ -13499,10 +14057,10 @@ void PEGTransformerFactory::InitializeNullLiteralTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNullLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformNullLiteral(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTrueLiteralTrampoline(PEGTransformer &transformer, @@ -13510,10 +14068,10 @@ void PEGTransformerFactory::InitializeTrueLiteralTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTrueLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformTrueLiteral(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeFalseLiteralTrampoline(PEGTransformer &transformer, @@ -13521,10 +14079,10 @@ void PEGTransformerFactory::InitializeFalseLiteralTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFalseLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformFalseLiteral(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCastExpressionTrampoline(PEGTransformer &transformer, @@ -13535,13 +14093,13 @@ void PEGTransformerFactory::InitializeCastExpressionTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("CastOrTryCast"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCastExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto cast_or_try_cast = process.TakeResult(0); auto cast_arguments = process.TakeResult(1); auto result = TransformCastExpression(transformer, cast_or_try_cast, std::move(cast_arguments)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCastArgumentsTrampoline(PEGTransformer &transformer, @@ -13552,13 +14110,13 @@ void PEGTransformerFactory::InitializeCastArgumentsTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCastArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto type = process.TakeResult(1); auto result = TransformCastArguments(transformer, std::move(expression), type); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeCastOrTryCastTrampoline(PEGTransformer &transformer, @@ -13570,16 +14128,16 @@ void PEGTransformerFactory::InitializeCastOrTryCastTrampoline(PEGTransformer &tr auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCastOrTryCastTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCastKeywordTrampoline(PEGTransformer &transformer, @@ -13587,10 +14145,10 @@ void PEGTransformerFactory::InitializeCastKeywordTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCastKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCastKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTryCastKeywordTrampoline(PEGTransformer &transformer, @@ -13598,11 +14156,11 @@ void PEGTransformerFactory::InitializeTryCastKeywordTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTryCastKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformTryCastKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeColIdDotTrampoline(PEGTransformer &transformer, @@ -13612,11 +14170,11 @@ void PEGTransformerFactory::InitializeColIdDotTrampoline(PEGTransformer &transfo process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(0)}, 0); } -unique_ptr PEGTransformerFactory::FinalizeColIdDotTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeColIdDotTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto col_id = process.TakeResult(0); auto result = TransformColIdDot(transformer, col_id); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeStarExpressionTrampoline(PEGTransformer &transformer, @@ -13641,7 +14199,7 @@ void PEGTransformerFactory::InitializeStarExpressionTrampoline(PEGTransformer &t } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeStarExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional> star_qualifier_list {}; @@ -13662,7 +14220,7 @@ PEGTransformerFactory::FinalizeStarExpressionTrampoline(PEGTransformer &transfor } auto result = TransformStarExpression(transformer, star_qualifier_list, exclude_list, std::move(replace_list), rename_list); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeStarQualifierListTrampoline(PEGTransformer &transformer, @@ -13678,7 +14236,7 @@ void PEGTransformerFactory::InitializeStarQualifierListTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeStarQualifierListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -13690,7 +14248,7 @@ PEGTransformerFactory::FinalizeStarQualifierListTrampoline(PEGTransformer &trans col_id_dot.push_back(process.TakeResult(i)); } auto result = TransformStarQualifierList(transformer, col_id_dot); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeExcludeListTrampoline(PEGTransformer &transformer, @@ -13700,11 +14258,11 @@ void PEGTransformerFactory::InitializeExcludeListTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("ExcludeNames"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExcludeListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto exclude_names = process.TakeResult(0); auto result = TransformExcludeList(transformer, exclude_names); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeExcludeNamesTrampoline(PEGTransformer &transformer, @@ -13716,15 +14274,15 @@ void PEGTransformerFactory::InitializeExcludeNamesTrampoline(PEGTransformer &tra auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExcludeNamesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeExcludeNameListTrampoline(PEGTransformer &transformer, @@ -13739,7 +14297,7 @@ void PEGTransformerFactory::InitializeExcludeNameListTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExcludeNameListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -13750,7 +14308,7 @@ PEGTransformerFactory::FinalizeExcludeNameListTrampoline(PEGTransformer &transfo exclude_name.push_back(process.TakeResult(i)); } auto result = TransformExcludeNameList(transformer, exclude_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeExcludeNameSingleTrampoline(PEGTransformer &transformer, @@ -13760,12 +14318,12 @@ void PEGTransformerFactory::InitializeExcludeNameSingleTrampoline(PEGTransformer process.PushChild({transformer.GetRule("ExcludeName"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExcludeNameSingleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto exclude_name = process.TakeResult(0); auto result = TransformExcludeNameSingle(transformer, exclude_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeExcludeNameTrampoline(PEGTransformer &transformer, @@ -13777,15 +14335,15 @@ void PEGTransformerFactory::InitializeExcludeNameTrampoline(PEGTransformer &tran auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExcludeNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeExcludeDottedNameTrampoline(PEGTransformer &transformer, @@ -13795,12 +14353,12 @@ void PEGTransformerFactory::InitializeExcludeDottedNameTrampoline(PEGTransformer process.PushChild({transformer.GetRule("DottedIdentifier"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExcludeDottedNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto dotted_identifier = process.TakeResult>(0); auto result = TransformExcludeDottedName(transformer, dotted_identifier); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeExcludeColumnNameTrampoline(PEGTransformer &transformer, @@ -13810,12 +14368,12 @@ void PEGTransformerFactory::InitializeExcludeColumnNameTrampoline(PEGTransformer process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExcludeColumnNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id_or_string = process.TakeResult(0); auto result = TransformExcludeColumnName(transformer, col_id_or_string); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeReplaceListTrampoline(PEGTransformer &transformer, @@ -13825,11 +14383,11 @@ void PEGTransformerFactory::InitializeReplaceListTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("ReplaceEntries"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeReplaceListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto replace_entries = process.TakeResult>>(0); auto result = TransformReplaceList(transformer, std::move(replace_entries)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeReplaceEntriesTrampoline(PEGTransformer &transformer, @@ -13841,16 +14399,16 @@ void PEGTransformerFactory::InitializeReplaceEntriesTrampoline(PEGTransformer &t auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeReplaceEntriesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>>(0); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeReplaceEntrySingleTrampoline(PEGTransformer &transformer, @@ -13860,12 +14418,12 @@ void PEGTransformerFactory::InitializeReplaceEntrySingleTrampoline(PEGTransforme process.PushChild({transformer.GetRule("ReplaceEntry"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeReplaceEntrySingleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto replace_entry = process.TakeResult>>(0); auto result = TransformReplaceEntrySingle(transformer, std::move(replace_entry)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeReplaceEntryListTrampoline(PEGTransformer &transformer, @@ -13880,7 +14438,7 @@ void PEGTransformerFactory::InitializeReplaceEntryListTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeReplaceEntryListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -13891,7 +14449,7 @@ PEGTransformerFactory::FinalizeReplaceEntryListTrampoline(PEGTransformer &transf replace_entry.push_back(process.TakeResult>>(i)); } auto result = TransformReplaceEntryList(transformer, std::move(replace_entry)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeReplaceEntryTrampoline(PEGTransformer &transformer, @@ -13902,12 +14460,12 @@ void PEGTransformerFactory::InitializeReplaceEntryTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeReplaceEntryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto column_reference = process.TakeResult>(1); auto result = TransformReplaceEntry(transformer, std::move(expression), std::move(column_reference)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeRenameListTrampoline(PEGTransformer &transformer, @@ -13917,11 +14475,11 @@ void PEGTransformerFactory::InitializeRenameListTrampoline(PEGTransformer &trans process.PushChild({transformer.GetRule("RenameEntries"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRenameListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto rename_entries = process.TakeResult>(0); auto result = TransformRenameList(transformer, rename_entries); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeRenameEntriesTrampoline(PEGTransformer &transformer, @@ -13933,16 +14491,16 @@ void PEGTransformerFactory::InitializeRenameEntriesTrampoline(PEGTransformer &tr auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRenameEntriesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeRenameEntryListTrampoline(PEGTransformer &transformer, @@ -13957,7 +14515,7 @@ void PEGTransformerFactory::InitializeRenameEntryListTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRenameEntryListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -13968,7 +14526,7 @@ PEGTransformerFactory::FinalizeRenameEntryListTrampoline(PEGTransformer &transfo rename_entry.push_back(process.TakeResult>(i)); } auto result = TransformRenameEntryList(transformer, rename_entry); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeSingleRenameEntryTrampoline(PEGTransformer &transformer, @@ -13978,12 +14536,12 @@ void PEGTransformerFactory::InitializeSingleRenameEntryTrampoline(PEGTransformer process.PushChild({transformer.GetRule("RenameEntry"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSingleRenameEntryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto rename_entry = process.TakeResult>(0); auto result = TransformSingleRenameEntry(transformer, rename_entry); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeRenameEntryTrampoline(PEGTransformer &transformer, @@ -13993,13 +14551,13 @@ void PEGTransformerFactory::InitializeRenameEntryTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("ExcludeName"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRenameEntryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto exclude_name = process.TakeResult(0); auto identifier = list_pr.GetChild(2).Cast().identifier; auto result = TransformRenameEntry(transformer, exclude_name, identifier); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeSubqueryExpressionTrampoline(PEGTransformer &transformer, @@ -14017,7 +14575,7 @@ void PEGTransformerFactory::InitializeSubqueryExpressionTrampoline(PEGTransforme } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSubqueryExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional subquery_not {}; @@ -14031,7 +14589,7 @@ PEGTransformerFactory::FinalizeSubqueryExpressionTrampoline(PEGTransformer &tran auto subquery_reference = process.TakeResult>(2); auto result = TransformSubqueryExpression(transformer, subquery_not, subquery_exists, std::move(subquery_reference)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSubqueryNotTrampoline(PEGTransformer &transformer, @@ -14039,10 +14597,10 @@ void PEGTransformerFactory::InitializeSubqueryNotTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSubqueryNotTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformSubqueryNot(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSubqueryExistsTrampoline(PEGTransformer &transformer, @@ -14050,11 +14608,11 @@ void PEGTransformerFactory::InitializeSubqueryExistsTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSubqueryExistsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformSubqueryExists(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCaseExpressionTrampoline(PEGTransformer &transformer, @@ -14078,7 +14636,7 @@ void PEGTransformerFactory::InitializeCaseExpressionTrampoline(PEGTransformer &t } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCaseExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -14099,7 +14657,7 @@ PEGTransformerFactory::FinalizeCaseExpressionTrampoline(PEGTransformer &transfor } auto result = TransformCaseExpression(transformer, std::move(expression), std::move(case_when_then), std::move(case_else)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCaseWhenThenTrampoline(PEGTransformer &transformer, @@ -14110,12 +14668,12 @@ void PEGTransformerFactory::InitializeCaseWhenThenTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCaseWhenThenTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto expression_1 = process.TakeResult>(1); auto result = TransformCaseWhenThen(transformer, std::move(expression), std::move(expression_1)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeCaseElseTrampoline(PEGTransformer &transformer, @@ -14125,11 +14683,11 @@ void PEGTransformerFactory::InitializeCaseElseTrampoline(PEGTransformer &transfo process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); } -unique_ptr PEGTransformerFactory::FinalizeCaseElseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeCaseElseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformCaseElse(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTypeLiteralTrampoline(PEGTransformer &transformer, @@ -14139,13 +14697,13 @@ void PEGTransformerFactory::InitializeTypeLiteralTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("Type"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTypeLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto type = process.TakeResult(0); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(1)); auto result = TransformTypeLiteral(transformer, type, string_literal); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIntervalLiteralTrampoline(PEGTransformer &transformer, @@ -14159,7 +14717,7 @@ void PEGTransformerFactory::InitializeIntervalLiteralTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("IntervalParameter"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIntervalLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto interval_parameter = process.TakeResult>(0); @@ -14168,7 +14726,7 @@ PEGTransformerFactory::FinalizeIntervalLiteralTrampoline(PEGTransformer &transfo interval = process.TakeResult(1); } auto result = TransformIntervalLiteral(transformer, std::move(interval_parameter), interval); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIntervalParameterTrampoline(PEGTransformer &transformer, @@ -14180,16 +14738,16 @@ void PEGTransformerFactory::InitializeIntervalParameterTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIntervalParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIntervalStringParameterTrampoline(PEGTransformer &transformer, @@ -14197,13 +14755,13 @@ void PEGTransformerFactory::InitializeIntervalStringParameterTrampoline(PEGTrans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIntervalStringParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(0)); auto result = TransformIntervalStringParameter(transformer, string_literal); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeFrameClauseTrampoline(PEGTransformer &transformer, @@ -14218,7 +14776,7 @@ void PEGTransformerFactory::InitializeFrameClauseTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("Framing"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFrameClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto framing = process.TakeResult(0); auto frame_extent = process.TakeResult>(1); @@ -14227,7 +14785,7 @@ PEGTransformerFactory::FinalizeFrameClauseTrampoline(PEGTransformer &transformer window_exclude_clause = process.TakeResult(2); } auto result = TransformFrameClause(transformer, framing, std::move(frame_extent), window_exclude_clause); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeFramingTrampoline(PEGTransformer &transformer, @@ -14243,13 +14801,13 @@ void PEGTransformerFactory::InitializeFramingTrampoline(PEGTransformer &transfor return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr PEGTransformerFactory::FinalizeFramingTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeFramingTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { string result; if (process.child_results[0]) { result = process.TakeResult(0); @@ -14268,7 +14826,7 @@ unique_ptr PEGTransformerFactory::FinalizeFramingTrampolin result = transformer.Transform(choice_result); } } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeRowsFramingTrampoline(PEGTransformer &transformer, @@ -14276,10 +14834,10 @@ void PEGTransformerFactory::InitializeRowsFramingTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRowsFramingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformRowsFraming(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeRangeFramingTrampoline(PEGTransformer &transformer, @@ -14287,10 +14845,10 @@ void PEGTransformerFactory::InitializeRangeFramingTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRangeFramingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformRangeFraming(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeGroupsFramingTrampoline(PEGTransformer &transformer, @@ -14298,11 +14856,11 @@ void PEGTransformerFactory::InitializeGroupsFramingTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGroupsFramingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformGroupsFraming(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeFrameExtentTrampoline(PEGTransformer &transformer, @@ -14314,15 +14872,15 @@ void PEGTransformerFactory::InitializeFrameExtentTrampoline(PEGTransformer &tran auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFrameExtentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSingleFrameExtentTrampoline(PEGTransformer &transformer, @@ -14332,12 +14890,12 @@ void PEGTransformerFactory::InitializeSingleFrameExtentTrampoline(PEGTransformer process.PushChild({transformer.GetRule("FrameBound"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSingleFrameExtentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto frame_bound = process.TakeResult(0); auto result = TransformSingleFrameExtent(transformer, std::move(frame_bound)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeBetweenFrameExtentTrampoline(PEGTransformer &transformer, @@ -14348,13 +14906,13 @@ void PEGTransformerFactory::InitializeBetweenFrameExtentTrampoline(PEGTransforme process.PushChild({transformer.GetRule("FrameBound"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBetweenFrameExtentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto frame_bound = process.TakeResult(0); auto frame_bound_1 = process.TakeResult(1); auto result = TransformBetweenFrameExtent(transformer, std::move(frame_bound), std::move(frame_bound_1)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeFrameBoundTrampoline(PEGTransformer &transformer, @@ -14366,15 +14924,15 @@ void PEGTransformerFactory::InitializeFrameBoundTrampoline(PEGTransformer &trans auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFrameBoundTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeFrameUnboundedTrampoline(PEGTransformer &transformer, @@ -14384,12 +14942,12 @@ void PEGTransformerFactory::InitializeFrameUnboundedTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("PrecedingOrFollowing"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFrameUnboundedTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto preceding_or_following = process.TakeResult(0); auto result = TransformFrameUnbounded(transformer, preceding_or_following); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeFrameExpressionTrampoline(PEGTransformer &transformer, @@ -14400,13 +14958,13 @@ void PEGTransformerFactory::InitializeFrameExpressionTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFrameExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto preceding_or_following = process.TakeResult(1); auto result = TransformFrameExpression(transformer, std::move(expression), preceding_or_following); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeFrameCurrentRowTrampoline(PEGTransformer &transformer, @@ -14414,11 +14972,11 @@ void PEGTransformerFactory::InitializeFrameCurrentRowTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFrameCurrentRowTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformFrameCurrentRow(transformer); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializePrecedingOrFollowingTrampoline(PEGTransformer &transformer, @@ -14430,16 +14988,16 @@ void PEGTransformerFactory::InitializePrecedingOrFollowingTrampoline(PEGTransfor auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePrecedingOrFollowingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializePrecedingFrameTrampoline(PEGTransformer &transformer, @@ -14447,11 +15005,11 @@ void PEGTransformerFactory::InitializePrecedingFrameTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePrecedingFrameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformPrecedingFrame(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeFollowingFrameTrampoline(PEGTransformer &transformer, @@ -14459,11 +15017,11 @@ void PEGTransformerFactory::InitializeFollowingFrameTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFollowingFrameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformFollowingFrame(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeWindowExcludeClauseTrampoline(PEGTransformer &transformer, @@ -14473,12 +15031,12 @@ void PEGTransformerFactory::InitializeWindowExcludeClauseTrampoline(PEGTransform process.PushChild({transformer.GetRule("WindowExcludeElement"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWindowExcludeClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto window_exclude_element = process.TakeResult(0); auto result = TransformWindowExcludeClause(transformer, window_exclude_element); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeWindowExcludeElementTrampoline(PEGTransformer &transformer, @@ -14490,16 +15048,16 @@ void PEGTransformerFactory::InitializeWindowExcludeElementTrampoline(PEGTransfor auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWindowExcludeElementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeExcludeCurrentRowTrampoline(PEGTransformer &transformer, @@ -14507,11 +15065,11 @@ void PEGTransformerFactory::InitializeExcludeCurrentRowTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExcludeCurrentRowTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformExcludeCurrentRow(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeExcludeGroupTrampoline(PEGTransformer &transformer, @@ -14519,10 +15077,10 @@ void PEGTransformerFactory::InitializeExcludeGroupTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExcludeGroupTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformExcludeGroup(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeExcludeTiesTrampoline(PEGTransformer &transformer, @@ -14530,10 +15088,10 @@ void PEGTransformerFactory::InitializeExcludeTiesTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExcludeTiesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformExcludeTies(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeExcludeNoOthersTrampoline(PEGTransformer &transformer, @@ -14541,11 +15099,11 @@ void PEGTransformerFactory::InitializeExcludeNoOthersTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExcludeNoOthersTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformExcludeNoOthers(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeWindowFrameTrampoline(PEGTransformer &transformer, @@ -14557,15 +15115,15 @@ void PEGTransformerFactory::InitializeWindowFrameTrampoline(PEGTransformer &tran auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWindowFrameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIdentifierWindowFrameTrampoline(PEGTransformer &transformer, @@ -14573,13 +15131,13 @@ void PEGTransformerFactory::InitializeIdentifierWindowFrameTrampoline(PEGTransfo process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIdentifierWindowFrameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto identifier = list_pr.GetChild(0).Cast().identifier; auto result = TransformIdentifierWindowFrame(transformer, identifier); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeParensIdentifierTrampoline(PEGTransformer &transformer, @@ -14587,13 +15145,13 @@ void PEGTransformerFactory::InitializeParensIdentifierTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeParensIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto identifier = ExtractResultFromParens(list_pr.GetChild(0)).Cast().identifier; auto result = TransformParensIdentifier(transformer, identifier); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeWindowFrameDefinitionTrampoline(PEGTransformer &transformer, @@ -14605,16 +15163,16 @@ void PEGTransformerFactory::InitializeWindowFrameDefinitionTrampoline(PEGTransfo auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWindowFrameDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeWindowFrameNameContentsParensTrampoline(PEGTransformer &transformer, @@ -14625,12 +15183,12 @@ void PEGTransformerFactory::InitializeWindowFrameNameContentsParensTrampoline(PE 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWindowFrameNameContentsParensTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto window_frame_name_contents = process.TakeResult>(0); auto result = TransformWindowFrameNameContentsParens(transformer, std::move(window_frame_name_contents)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeWindowFrameNameContentsTrampoline(PEGTransformer &transformer, @@ -14644,7 +15202,7 @@ void PEGTransformerFactory::InitializeWindowFrameNameContentsTrampoline(PEGTrans } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWindowFrameNameContentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional base_window_name {}; @@ -14653,7 +15211,7 @@ PEGTransformerFactory::FinalizeWindowFrameNameContentsTrampoline(PEGTransformer } auto window_frame_contents = process.TakeResult>(1); auto result = TransformWindowFrameNameContents(transformer, base_window_name, std::move(window_frame_contents)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeWindowFrameContentsParensTrampoline(PEGTransformer &transformer, @@ -14663,12 +15221,12 @@ void PEGTransformerFactory::InitializeWindowFrameContentsParensTrampoline(PEGTra process.PushChild({transformer.GetRule("WindowFrameContents"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWindowFrameContentsParensTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto window_frame_contents = process.TakeResult>(0); auto result = TransformWindowFrameContentsParens(transformer, std::move(window_frame_contents)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeWindowFrameContentsTrampoline(PEGTransformer &transformer, @@ -14689,7 +15247,7 @@ void PEGTransformerFactory::InitializeWindowFrameContentsTrampoline(PEGTransform } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWindowFrameContentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional>> window_partition {}; @@ -14706,7 +15264,7 @@ PEGTransformerFactory::FinalizeWindowFrameContentsTrampoline(PEGTransformer &tra } auto result = TransformWindowFrameContents(transformer, std::move(window_partition), std::move(order_by_clause), std::move(frame_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeBaseWindowNameTrampoline(PEGTransformer &transformer, @@ -14714,13 +15272,13 @@ void PEGTransformerFactory::InitializeBaseWindowNameTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBaseWindowNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto identifier = list_pr.GetChild(0).Cast().identifier; auto result = TransformBaseWindowName(transformer, identifier); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeWindowPartitionTrampoline(PEGTransformer &transformer, @@ -14735,7 +15293,7 @@ void PEGTransformerFactory::InitializeWindowPartitionTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWindowPartitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -14746,7 +15304,7 @@ PEGTransformerFactory::FinalizeWindowPartitionTrampoline(PEGTransformer &transfo expression.push_back(process.TakeResult>(i)); } auto result = TransformWindowPartition(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeListExpressionTrampoline(PEGTransformer &transformer, @@ -14758,16 +15316,16 @@ void PEGTransformerFactory::InitializeListExpressionTrampoline(PEGTransformer &t auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeListExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeArrayBoundedListExpressionTrampoline(PEGTransformer &transformer, @@ -14777,7 +15335,7 @@ void PEGTransformerFactory::InitializeArrayBoundedListExpressionTrampoline(PEGTr process.PushChild({transformer.GetRule("BoundedListExpression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeArrayBoundedListExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -14786,7 +15344,7 @@ PEGTransformerFactory::FinalizeArrayBoundedListExpressionTrampoline(PEGTransform has_result = has_result_opt.HasResult(); auto bounded_list_expression = process.TakeResult>>(0); auto result = TransformArrayBoundedListExpression(transformer, has_result, std::move(bounded_list_expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeArrayParensSelectTrampoline(PEGTransformer &transformer, @@ -14797,12 +15355,12 @@ void PEGTransformerFactory::InitializeArrayParensSelectTrampoline(PEGTransformer 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeArrayParensSelectTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto select_statement_internal = process.TakeResult>(0); auto result = TransformArrayParensSelect(transformer, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeBoundedListExpressionTrampoline(PEGTransformer &transformer, @@ -14823,7 +15381,7 @@ void PEGTransformerFactory::InitializeBoundedListExpressionTrampoline(PEGTransfo } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBoundedListExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -14843,7 +15401,7 @@ PEGTransformerFactory::FinalizeBoundedListExpressionTrampoline(PEGTransformer &t expression = std::move(expression_value); } auto result = TransformBoundedListExpression(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeStructExpressionTrampoline(PEGTransformer &transformer, @@ -14864,7 +15422,7 @@ void PEGTransformerFactory::InitializeStructExpressionTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeStructExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -14884,7 +15442,7 @@ PEGTransformerFactory::FinalizeStructExpressionTrampoline(PEGTransformer &transf struct_field = std::move(struct_field_value); } auto result = TransformStructExpression(transformer, std::move(struct_field)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeStructFieldTrampoline(PEGTransformer &transformer, @@ -14895,12 +15453,12 @@ void PEGTransformerFactory::InitializeStructFieldTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeStructFieldTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id_or_string = process.TakeResult(0); auto expression = process.TakeResult>(1); auto result = TransformStructField(transformer, col_id_or_string, std::move(expression)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeMapExpressionTrampoline(PEGTransformer &transformer, @@ -14910,12 +15468,12 @@ void PEGTransformerFactory::InitializeMapExpressionTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("MapStructExpression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMapExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto map_struct_expression = process.TakeResult>>(0); auto result = TransformMapExpression(transformer, std::move(map_struct_expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeMapStructExpressionTrampoline(PEGTransformer &transformer, @@ -14936,7 +15494,7 @@ void PEGTransformerFactory::InitializeMapStructExpressionTrampoline(PEGTransform } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMapStructExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -14956,7 +15514,7 @@ PEGTransformerFactory::FinalizeMapStructExpressionTrampoline(PEGTransformer &tra map_struct_field = std::move(map_struct_field_value); } auto result = TransformMapStructExpression(transformer, std::move(map_struct_field)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeMapStructFieldTrampoline(PEGTransformer &transformer, @@ -14967,13 +15525,13 @@ void PEGTransformerFactory::InitializeMapStructFieldTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMapStructFieldTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto expression_1 = process.TakeResult>(1); auto result = TransformMapStructField(transformer, std::move(expression), std::move(expression_1)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeGroupingExpressionTrampoline(PEGTransformer &transformer, @@ -14995,7 +15553,7 @@ void PEGTransformerFactory::InitializeGroupingExpressionTrampoline(PEGTransforme process.PushChild({transformer.GetRule("GroupingOrGroupingId"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGroupingExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -15016,7 +15574,7 @@ PEGTransformerFactory::FinalizeGroupingExpressionTrampoline(PEGTransformer &tran expression = std::move(expression_value); } auto result = TransformGroupingExpression(transformer, grouping_or_grouping_id, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeGroupingOrGroupingIdTrampoline(PEGTransformer &transformer, @@ -15028,16 +15586,16 @@ void PEGTransformerFactory::InitializeGroupingOrGroupingIdTrampoline(PEGTransfor auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGroupingOrGroupingIdTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeGroupingKeywordTrampoline(PEGTransformer &transformer, @@ -15045,11 +15603,11 @@ void PEGTransformerFactory::InitializeGroupingKeywordTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGroupingKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformGroupingKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeGroupingIdKeywordTrampoline(PEGTransformer &transformer, @@ -15057,11 +15615,11 @@ void PEGTransformerFactory::InitializeGroupingIdKeywordTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGroupingIdKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformGroupingIdKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeParameterTrampoline(PEGTransformer &transformer, @@ -15073,15 +15631,15 @@ void PEGTransformerFactory::InitializeParameterTrampoline(PEGTransformer &transf auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeParameterTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeQuestionMarkNumberedParameterTrampoline(PEGTransformer &transformer, @@ -15089,13 +15647,13 @@ void PEGTransformerFactory::InitializeQuestionMarkNumberedParameterTrampoline(PE process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQuestionMarkNumberedParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto number_literal = TransformNumberLiteral(transformer, list_pr.GetChild(1)); auto result = TransformQuestionMarkNumberedParameter(transformer, std::move(number_literal)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAnonymousParameterTrampoline(PEGTransformer &transformer, @@ -15103,11 +15661,11 @@ void PEGTransformerFactory::InitializeAnonymousParameterTrampoline(PEGTransforme process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAnonymousParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformAnonymousParameter(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNumberedParameterTrampoline(PEGTransformer &transformer, @@ -15115,13 +15673,13 @@ void PEGTransformerFactory::InitializeNumberedParameterTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNumberedParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto number_literal = TransformNumberLiteral(transformer, list_pr.GetChild(1)); auto result = TransformNumberedParameter(transformer, std::move(number_literal)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeColLabelParameterTrampoline(PEGTransformer &transformer, @@ -15131,12 +15689,12 @@ void PEGTransformerFactory::InitializeColLabelParameterTrampoline(PEGTransformer process.PushChild({transformer.GetRule("ColLabel"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColLabelParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_label = process.TakeResult(0); auto result = TransformColLabelParameter(transformer, col_label); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializePositionalExpressionTrampoline(PEGTransformer &transformer, @@ -15144,13 +15702,13 @@ void PEGTransformerFactory::InitializePositionalExpressionTrampoline(PEGTransfor process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePositionalExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto number_literal = TransformNumberLiteral(transformer, list_pr.GetChild(1)); auto result = TransformPositionalExpression(transformer, std::move(number_literal)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDefaultExpressionTrampoline(PEGTransformer &transformer, @@ -15158,11 +15716,11 @@ void PEGTransformerFactory::InitializeDefaultExpressionTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDefaultExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformDefaultExpression(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeListComprehensionExpressionTrampoline(PEGTransformer &transformer, @@ -15184,7 +15742,7 @@ void PEGTransformerFactory::InitializeListComprehensionExpressionTrampoline(PEGT process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeListComprehensionExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -15202,7 +15760,7 @@ PEGTransformerFactory::FinalizeListComprehensionExpressionTrampoline(PEGTransfor } auto result = TransformListComprehensionExpression(transformer, std::move(expression), col_id_or_string, std::move(expression_1), std::move(list_comprehension_filter)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeListComprehensionFilterTrampoline(PEGTransformer &transformer, @@ -15212,12 +15770,12 @@ void PEGTransformerFactory::InitializeListComprehensionFilterTrampoline(PEGTrans process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeListComprehensionFilterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformListComprehensionFilter(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeParensExpressionTrampoline(PEGTransformer &transformer, @@ -15227,12 +15785,12 @@ void PEGTransformerFactory::InitializeParensExpressionTrampoline(PEGTransformer process.PushChild({transformer.GetRule("Expression"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeParensExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformParensExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeMeasureSemanticsExpressionTrampoline(PEGTransformer &transformer, @@ -15243,13 +15801,13 @@ void PEGTransformerFactory::InitializeMeasureSemanticsExpressionTrampoline(PEGTr process.PushChild({transformer.GetRule("MeasureSemantics"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMeasureSemanticsExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto measure_semantics = process.TakeResult(0); auto function_expression = process.TakeResult>(1); auto result = TransformMeasureSemanticsExpression(transformer, measure_semantics, std::move(function_expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSingleExpressionTrampoline(PEGTransformer &transformer, @@ -15261,16 +15819,16 @@ void PEGTransformerFactory::InitializeSingleExpressionTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSingleExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeExpressionTrampoline(PEGTransformer &transformer, @@ -15280,10 +15838,10 @@ void PEGTransformerFactory::InitializeExpressionTrampoline(PEGTransformer &trans process.PushChild({transformer.GetRule("LambdaArrowExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeColumnDefaultExprTrampoline(PEGTransformer &transformer, @@ -15293,12 +15851,12 @@ void PEGTransformerFactory::InitializeColumnDefaultExprTrampoline(PEGTransformer process.PushChild({transformer.GetRule("ColDefOrExpr"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColumnDefaultExprTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_def_or_expr = process.TakeResult>(0); auto result = TransformColumnDefaultExpr(transformer, std::move(col_def_or_expr)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeLambdaArrowExpressionTrampoline(PEGTransformer &transformer, @@ -15322,7 +15880,7 @@ void PEGTransformerFactory::InitializeLambdaArrowExpressionTrampoline(PEGTransfo process.PushChild({transformer.GetRule("LogicalOrExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLambdaArrowExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -15344,7 +15902,7 @@ PEGTransformerFactory::FinalizeLambdaArrowExpressionTrampoline(PEGTransformer &t } auto result = TransformLambdaArrowExpression(transformer, std::move(logical_or_expression), std::move(single_arrow_pair)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSingleArrowPairTrampoline(PEGTransformer &transformer, @@ -15354,12 +15912,12 @@ void PEGTransformerFactory::InitializeSingleArrowPairTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("LogicalOrExpression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSingleArrowPairTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto logical_or_expression = process.TakeResult>(0); auto result = TransformSingleArrowPair(transformer, std::move(logical_or_expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeLogicalOrExpressionTrampoline(PEGTransformer &transformer, @@ -15383,7 +15941,7 @@ void PEGTransformerFactory::InitializeLogicalOrExpressionTrampoline(PEGTransform process.PushChild({transformer.GetRule("LogicalAndExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLogicalOrExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -15405,7 +15963,7 @@ PEGTransformerFactory::FinalizeLogicalOrExpressionTrampoline(PEGTransformer &tra } auto result = TransformLogicalOrExpression(transformer, std::move(logical_and_expression), std::move(logical_or_expression_tail)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeLogicalOrExpressionTailTrampoline(PEGTransformer &transformer, @@ -15415,12 +15973,12 @@ void PEGTransformerFactory::InitializeLogicalOrExpressionTailTrampoline(PEGTrans process.PushChild({transformer.GetRule("LogicalAndExpression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLogicalOrExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto logical_and_expression = process.TakeResult>(0); auto result = TransformLogicalOrExpressionTail(transformer, std::move(logical_and_expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeColDefOrExprTrampoline(PEGTransformer &transformer, @@ -15444,7 +16002,7 @@ void PEGTransformerFactory::InitializeColDefOrExprTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("ColDefAndExpr"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColDefOrExprTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); idx_t dynamic_child_count = 0; @@ -15465,7 +16023,7 @@ PEGTransformerFactory::FinalizeColDefOrExprTrampoline(PEGTransformer &transforme } auto result = TransformColDefOrExpr(transformer, std::move(col_def_and_expr), std::move(col_def_or_expression_tail)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeColDefOrExpressionTailTrampoline(PEGTransformer &transformer, @@ -15475,12 +16033,12 @@ void PEGTransformerFactory::InitializeColDefOrExpressionTailTrampoline(PEGTransf process.PushChild({transformer.GetRule("ColDefAndExpr"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColDefOrExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_def_and_expr = process.TakeResult>(0); auto result = TransformColDefOrExpressionTail(transformer, std::move(col_def_and_expr)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeLogicalAndExpressionTrampoline(PEGTransformer &transformer, @@ -15504,7 +16062,7 @@ void PEGTransformerFactory::InitializeLogicalAndExpressionTrampoline(PEGTransfor process.PushChild({transformer.GetRule("LogicalNotExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLogicalAndExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -15526,7 +16084,7 @@ PEGTransformerFactory::FinalizeLogicalAndExpressionTrampoline(PEGTransformer &tr } auto result = TransformLogicalAndExpression(transformer, std::move(logical_not_expression), std::move(logical_and_expression_tail)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeLogicalAndExpressionTailTrampoline(PEGTransformer &transformer, @@ -15536,12 +16094,12 @@ void PEGTransformerFactory::InitializeLogicalAndExpressionTailTrampoline(PEGTran process.PushChild({transformer.GetRule("LogicalNotExpression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLogicalAndExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto logical_not_expression = process.TakeResult>(0); auto result = TransformLogicalAndExpressionTail(transformer, std::move(logical_not_expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeColDefAndExprTrampoline(PEGTransformer &transformer, @@ -15565,7 +16123,7 @@ void PEGTransformerFactory::InitializeColDefAndExprTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("IsDistinctFromExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColDefAndExprTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -15587,7 +16145,7 @@ PEGTransformerFactory::FinalizeColDefAndExprTrampoline(PEGTransformer &transform } auto result = TransformColDefAndExpr(transformer, std::move(is_distinct_from_expression), std::move(col_def_and_expression_tail)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeColDefAndExpressionTailTrampoline(PEGTransformer &transformer, @@ -15597,12 +16155,12 @@ void PEGTransformerFactory::InitializeColDefAndExpressionTailTrampoline(PEGTrans process.PushChild({transformer.GetRule("IsDistinctFromExpression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColDefAndExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto is_distinct_from_expression = process.TakeResult>(0); auto result = TransformColDefAndExpressionTail(transformer, std::move(is_distinct_from_expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeLogicalNotExpressionTrampoline(PEGTransformer &transformer, @@ -15616,7 +16174,7 @@ void PEGTransformerFactory::InitializeLogicalNotExpressionTrampoline(PEGTransfor } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLogicalNotExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional> not_expression {}; @@ -15625,7 +16183,7 @@ PEGTransformerFactory::FinalizeLogicalNotExpressionTrampoline(PEGTransformer &tr } auto is_expression = process.TakeResult>(1); auto result = TransformLogicalNotExpression(transformer, std::move(not_expression), std::move(is_expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNotExpressionTrampoline(PEGTransformer &transformer, @@ -15641,7 +16199,7 @@ void PEGTransformerFactory::InitializeNotExpressionTrampoline(PEGTransformer &tr } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNotExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -15653,7 +16211,7 @@ PEGTransformerFactory::FinalizeNotExpressionTrampoline(PEGTransformer &transform not_keyword.push_back(process.TakeResult(i)); } auto result = TransformNotExpression(transformer, not_keyword); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNotKeywordTrampoline(PEGTransformer &transformer, @@ -15661,15 +16219,39 @@ void PEGTransformerFactory::InitializeNotKeywordTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNotKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformNotKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeIsExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(2); + auto &is_expression_continuation_opt = list_pr.GetChild(1).Cast(); + if (is_expression_continuation_opt.HasResult()) { + process.PushChild({transformer.GetRule("IsExpressionContinuation"), is_expression_continuation_opt.GetResult()}, + 1); + } + process.PushChild({transformer.GetRule("IsDistinctFromExpression"), list_pr.GetChild(0)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeIsExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { + auto is_distinct_from_expression = process.TakeResult>(0); + optional> is_expression_continuation {}; + if (process.child_results[1]) { + is_expression_continuation = process.TakeResult>(1); + } + auto result = TransformIsExpression(transformer, std::move(is_distinct_from_expression), + std::move(is_expression_continuation)); + return transformer.MakeResult>(std::move(result)); +} + +void PEGTransformerFactory::InitializeIsExpressionContinuationTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); auto &repeat_opt = list_pr.GetChild(1).Cast(); idx_t dynamic_child_count = 0; if (repeat_opt.HasResult()) { @@ -15679,16 +16261,18 @@ void PEGTransformerFactory::InitializeIsExpressionTrampoline(PEGTransformer &tra process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("IsTest"), repeat_children[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule("IsExpressionTail"), repeat_children[child_idx].get()}, + 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("IsDistinctFromExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule("IsTest"), list_pr.GetChild(0)}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeIsExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr +PEGTransformerFactory::FinalizeIsExpressionContinuationTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); idx_t dynamic_child_count = 0; auto &dynamic_repeat_opt = list_pr.GetChild(1).Cast(); @@ -15697,17 +16281,97 @@ PEGTransformerFactory::FinalizeIsExpressionTrampoline(PEGTransformer &transforme auto dynamic_repeat_children = dynamic_repeat_pr.GetChildren(); dynamic_child_count = dynamic_repeat_children.size(); } - auto is_distinct_from_expression = process.TakeResult>(0); - optional>> is_test {}; + auto is_test = process.TakeResult>(0); + optional> is_expression_tail {}; if (dynamic_child_count > 0) { - vector> is_test_value; + vector is_expression_tail_value; for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { - is_test_value.push_back(process.TakeResult>(i)); + is_expression_tail_value.push_back(process.TakeResult(i)); } - is_test = std::move(is_test_value); + is_expression_tail = std::move(is_expression_tail_value); + } + auto result = TransformIsExpressionContinuation(transformer, std::move(is_test), std::move(is_expression_tail)); + return transformer.MakeResult>(std::move(result)); +} + +void PEGTransformerFactory::InitializeIsExpressionTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + auto &choice_pr = list_pr.Child(0); + auto &choice_result = choice_pr.GetResult(); + process.ReserveChildSlots(1); + auto child_rule = choice_result.GetRule(); + auto has_transform_process = child_rule && child_rule->transform_process; + if (!has_transform_process) { + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } - auto result = TransformIsExpression(transformer, std::move(is_distinct_from_expression), std::move(is_test)); - return make_uniq>>(std::move(result)); + process.PushChild({*child_rule, choice_result}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeIsExpressionTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto result = process.TakeResult(0); + return transformer.MakeResult(std::move(result)); +} + +void PEGTransformerFactory::InitializeIsTestTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + process.PushChild({transformer.GetRule("IsTest"), list_pr.GetChild(0)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeIsTestTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { + auto is_test = process.TakeResult>(0); + auto result = TransformIsTestTail(transformer, std::move(is_test)); + return transformer.MakeResult(std::move(result)); +} + +void PEGTransformerFactory::InitializeIsDistinctTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + process.PushChild({transformer.GetRule("IsDistinctFromTail"), list_pr.GetChild(0)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeIsDistinctTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto is_distinct_from_tail = process.TakeResult(0); + auto result = TransformIsDistinctTail(transformer, std::move(is_distinct_from_tail)); + return transformer.MakeResult(std::move(result)); +} + +void PEGTransformerFactory::InitializeIsComparisonTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + process.PushChild({transformer.GetRule("ComparisonExpressionTail"), list_pr.GetChild(0)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeIsComparisonTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto comparison_expression_tail = process.TakeResult(0); + auto result = TransformIsComparisonTail(transformer, std::move(comparison_expression_tail)); + return transformer.MakeResult(std::move(result)); +} + +void PEGTransformerFactory::InitializeIsOtherOperatorTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + process.PushChild({transformer.GetRule("OtherOperatorTail"), list_pr.GetChild(0)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeIsOtherOperatorTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto other_operator_tail = process.TakeResult(0); + auto result = TransformIsOtherOperatorTail(transformer, std::move(other_operator_tail)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeIsTestTrampoline(PEGTransformer &transformer, @@ -15719,15 +16383,15 @@ void PEGTransformerFactory::InitializeIsTestTrampoline(PEGTransformer &transform auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr PEGTransformerFactory::FinalizeIsTestTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeIsTestTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIsLiteralTrampoline(PEGTransformer &transformer, @@ -15737,15 +16401,15 @@ void PEGTransformerFactory::InitializeIsLiteralTrampoline(PEGTransformer &transf process.PushChild({transformer.GetRule("IsLiteralValue"), list_pr.GetChild(2)}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeIsLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeIsLiteralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); bool has_result {}; auto &has_result_opt = list_pr.GetChild(1).Cast(); has_result = has_result_opt.HasResult(); auto is_literal_value = process.TakeResult(0); auto result = TransformIsLiteral(transformer, has_result, is_literal_value); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIsLiteralValueTrampoline(PEGTransformer &transformer, @@ -15757,16 +16421,16 @@ void PEGTransformerFactory::InitializeIsLiteralValueTrampoline(PEGTransformer &t auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIsLiteralValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeUnknownLiteralTrampoline(PEGTransformer &transformer, @@ -15774,11 +16438,11 @@ void PEGTransformerFactory::InitializeUnknownLiteralTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUnknownLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformUnknownLiteral(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeNotNullTrampoline(PEGTransformer &transformer, @@ -15790,15 +16454,15 @@ void PEGTransformerFactory::InitializeNotNullTrampoline(PEGTransformer &transfor auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr PEGTransformerFactory::FinalizeNotNullTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeNotNullTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNotNullKeywordTrampoline(PEGTransformer &transformer, @@ -15806,11 +16470,11 @@ void PEGTransformerFactory::InitializeNotNullKeywordTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNotNullKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformNotNullKeyword(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNotNullOperatorTrampoline(PEGTransformer &transformer, @@ -15818,11 +16482,11 @@ void PEGTransformerFactory::InitializeNotNullOperatorTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNotNullOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformNotNullOperator(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIsNullTrampoline(PEGTransformer &transformer, @@ -15832,11 +16496,11 @@ void PEGTransformerFactory::InitializeIsNullTrampoline(PEGTransformer &transform process.PushChild({transformer.GetRule("IsNullOperator"), list_pr.GetChild(0)}, 0); } -unique_ptr PEGTransformerFactory::FinalizeIsNullTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeIsNullTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto is_null_operator = process.TakeResult>(0); auto result = TransformIsNull(transformer, std::move(is_null_operator)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIsNullOperatorTrampoline(PEGTransformer &transformer, @@ -15844,11 +16508,11 @@ void PEGTransformerFactory::InitializeIsNullOperatorTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIsNullOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformIsNullOperator(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIsDistinctFromExpressionTrampoline(PEGTransformer &transformer, @@ -15872,7 +16536,7 @@ void PEGTransformerFactory::InitializeIsDistinctFromExpressionTrampoline(PEGTran process.PushChild({transformer.GetRule("ComparisonExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIsDistinctFromExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -15894,7 +16558,7 @@ PEGTransformerFactory::FinalizeIsDistinctFromExpressionTrampoline(PEGTransformer } auto result = TransformIsDistinctFromExpression(transformer, std::move(comparison_expression), std::move(is_distinct_from_tail)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIsDistinctFromTailTrampoline(PEGTransformer &transformer, @@ -15905,13 +16569,13 @@ void PEGTransformerFactory::InitializeIsDistinctFromTailTrampoline(PEGTransforme process.PushChild({transformer.GetRule("IsDistinctFromOp"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIsDistinctFromTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto is_distinct_from_op = process.TakeResult(0); auto comparison_expression = process.TakeResult>(1); auto result = TransformIsDistinctFromTail(transformer, is_distinct_from_op, std::move(comparison_expression)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeIsDistinctFromOpTrampoline(PEGTransformer &transformer, @@ -15919,7 +16583,7 @@ void PEGTransformerFactory::InitializeIsDistinctFromOpTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIsDistinctFromOpTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -15927,7 +16591,7 @@ PEGTransformerFactory::FinalizeIsDistinctFromOpTrampoline(PEGTransformer &transf auto &has_result_opt = list_pr.GetChild(1).Cast(); has_result = has_result_opt.HasResult(); auto result = TransformIsDistinctFromOp(transformer, has_result); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeComparisonExpressionTrampoline(PEGTransformer &transformer, @@ -15951,7 +16615,7 @@ void PEGTransformerFactory::InitializeComparisonExpressionTrampoline(PEGTransfor process.PushChild({transformer.GetRule("BetweenInLikeExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeComparisonExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -15973,7 +16637,7 @@ PEGTransformerFactory::FinalizeComparisonExpressionTrampoline(PEGTransformer &tr } auto result = TransformComparisonExpression(transformer, std::move(between_in_like_expression), std::move(comparison_expression_tail)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeComparisonExpressionTailTrampoline(PEGTransformer &transformer, @@ -15988,7 +16652,7 @@ void PEGTransformerFactory::InitializeComparisonExpressionTailTrampoline(PEGTran process.PushChild({transformer.GetRule("ComparisonOperator"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeComparisonExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto comparison_operator = process.TakeResult(0); @@ -15999,7 +16663,7 @@ PEGTransformerFactory::FinalizeComparisonExpressionTailTrampoline(PEGTransformer auto between_in_like_expression = process.TakeResult>(2); auto result = TransformComparisonExpressionTail(transformer, comparison_operator, std::move(not_expression), std::move(between_in_like_expression)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeComparisonOperatorTrampoline(PEGTransformer &transformer, @@ -16011,16 +16675,16 @@ void PEGTransformerFactory::InitializeComparisonOperatorTrampoline(PEGTransforme auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeComparisonOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeOperatorEqualTrampoline(PEGTransformer &transformer, @@ -16028,11 +16692,11 @@ void PEGTransformerFactory::InitializeOperatorEqualTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOperatorEqualTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformOperatorEqual(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeOperatorNotEqualTrampoline(PEGTransformer &transformer, @@ -16040,11 +16704,11 @@ void PEGTransformerFactory::InitializeOperatorNotEqualTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOperatorNotEqualTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformOperatorNotEqual(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeOperatorLessThanTrampoline(PEGTransformer &transformer, @@ -16052,11 +16716,11 @@ void PEGTransformerFactory::InitializeOperatorLessThanTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOperatorLessThanTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformOperatorLessThan(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeOperatorGreaterThanTrampoline(PEGTransformer &transformer, @@ -16064,11 +16728,11 @@ void PEGTransformerFactory::InitializeOperatorGreaterThanTrampoline(PEGTransform process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOperatorGreaterThanTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformOperatorGreaterThan(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeOperatorLessThanEqualsTrampoline(PEGTransformer &transformer, @@ -16076,11 +16740,11 @@ void PEGTransformerFactory::InitializeOperatorLessThanEqualsTrampoline(PEGTransf process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOperatorLessThanEqualsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformOperatorLessThanEquals(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeOperatorGreaterThanEqualsTrampoline(PEGTransformer &transformer, @@ -16088,58 +16752,111 @@ void PEGTransformerFactory::InitializeOperatorGreaterThanEqualsTrampoline(PEGTra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOperatorGreaterThanEqualsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformOperatorGreaterThanEquals(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeBetweenInLikeExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(2); - auto &between_in_like_op_opt = list_pr.GetChild(1).Cast(); - if (between_in_like_op_opt.HasResult()) { - process.PushChild({transformer.GetRule("BetweenInLikeOp"), between_in_like_op_opt.GetResult()}, 1); + auto &repeat_opt = list_pr.GetChild(1).Cast(); + idx_t dynamic_child_count = 0; + if (repeat_opt.HasResult()) { + auto &repeat_pr = repeat_opt.GetResult().Cast(); + auto repeat_children = repeat_pr.GetChildren(); + dynamic_child_count = repeat_children.size(); + process.ReserveChildSlots(3 + dynamic_child_count - 1); + auto &between_like_op_opt = list_pr.GetChild(2).Cast(); + if (between_like_op_opt.HasResult()) { + process.PushChild({transformer.GetRule("BetweenLikeOp"), between_like_op_opt.GetResult()}, + 2 + dynamic_child_count - 1); + } + for (idx_t i = repeat_children.size(); i > 0; i--) { + auto child_idx = i - 1; + process.PushChild({transformer.GetRule("InPredicate"), repeat_children[child_idx].get()}, 1 + child_idx); + } + } else { + process.ReserveChildSlots(3 - 1); + auto &between_like_op_opt = list_pr.GetChild(2).Cast(); + if (between_like_op_opt.HasResult()) { + process.PushChild({transformer.GetRule("BetweenLikeOp"), between_like_op_opt.GetResult()}, + 2 + dynamic_child_count - 1); + } } process.PushChild({transformer.GetRule("OtherOperatorExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBetweenInLikeExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + idx_t dynamic_child_count = 0; + auto &dynamic_repeat_opt = list_pr.GetChild(1).Cast(); + if (dynamic_repeat_opt.HasResult()) { + auto &dynamic_repeat_pr = dynamic_repeat_opt.GetResult().Cast(); + auto dynamic_repeat_children = dynamic_repeat_pr.GetChildren(); + dynamic_child_count = dynamic_repeat_children.size(); + } auto other_operator_expression = process.TakeResult>(0); - optional between_in_like_op {}; - if (process.child_results[1]) { - between_in_like_op = process.TakeResult(1); + optional> in_predicate {}; + if (dynamic_child_count > 0) { + vector in_predicate_value; + for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { + in_predicate_value.push_back(process.TakeResult(i)); + } + in_predicate = std::move(in_predicate_value); + } + optional between_like_op {}; + if (process.child_results[2 + dynamic_child_count - 1]) { + between_like_op = process.TakeResult(2 + dynamic_child_count - 1); } auto result = TransformBetweenInLikeExpression(transformer, std::move(other_operator_expression), - std::move(between_in_like_op)); - return make_uniq>>(std::move(result)); + std::move(in_predicate), std::move(between_like_op)); + return transformer.MakeResult>(std::move(result)); } -void PEGTransformerFactory::InitializeBetweenInLikeOpTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +void PEGTransformerFactory::InitializeInPredicateTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("BetweenInLikeOpExpression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule("InClause"), list_pr.GetChild(1)}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeBetweenInLikeOpTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr +PEGTransformerFactory::FinalizeInPredicateTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); bool has_result {}; auto &has_result_opt = list_pr.GetChild(0).Cast(); has_result = has_result_opt.HasResult(); - auto between_in_like_op_expression = process.TakeResult>(0); - auto result = TransformBetweenInLikeOp(transformer, has_result, std::move(between_in_like_op_expression)); - return make_uniq>(std::move(result)); + auto in_clause = process.TakeResult>(0); + auto result = TransformInPredicate(transformer, has_result, std::move(in_clause)); + return transformer.MakeResult(std::move(result)); } -void PEGTransformerFactory::InitializeBetweenInLikeOpExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +void PEGTransformerFactory::InitializeBetweenLikeOpTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + process.PushChild({transformer.GetRule("BetweenLikeOpExpression"), list_pr.GetChild(1)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeBetweenLikeOpTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + bool has_result {}; + auto &has_result_opt = list_pr.GetChild(0).Cast(); + has_result = has_result_opt.HasResult(); + auto between_like_op_expression = process.TakeResult>(0); + auto result = TransformBetweenLikeOp(transformer, has_result, std::move(between_like_op_expression)); + return transformer.MakeResult(std::move(result)); +} + +void PEGTransformerFactory::InitializeBetweenLikeOpExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto &choice_pr = list_pr.Child(0); auto &choice_result = choice_pr.GetResult(); @@ -16147,16 +16864,16 @@ void PEGTransformerFactory::InitializeBetweenInLikeOpExpressionTrampoline(PEGTra auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeBetweenInLikeOpExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr +PEGTransformerFactory::FinalizeBetweenLikeOpExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeLikeClauseTrampoline(PEGTransformer &transformer, @@ -16171,7 +16888,7 @@ void PEGTransformerFactory::InitializeLikeClauseTrampoline(PEGTransformer &trans process.PushChild({transformer.GetRule("LikeVariations"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLikeClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto like_variations = process.TakeResult(0); auto other_operator_expression = process.TakeResult>(1); @@ -16181,21 +16898,21 @@ PEGTransformerFactory::FinalizeLikeClauseTrampoline(PEGTransformer &transformer, } auto result = TransformLikeClause(transformer, like_variations, std::move(other_operator_expression), std::move(escape_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeEscapeClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ComparisonExpression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule("OtherOperatorExpression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeEscapeClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto comparison_expression = process.TakeResult>(0); - auto result = TransformEscapeClause(transformer, std::move(comparison_expression)); - return make_uniq>>(std::move(result)); + auto other_operator_expression = process.TakeResult>(0); + auto result = TransformEscapeClause(transformer, std::move(other_operator_expression)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeLikeVariationsTrampoline(PEGTransformer &transformer, @@ -16211,12 +16928,12 @@ void PEGTransformerFactory::InitializeLikeVariationsTrampoline(PEGTransformer &t return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLikeVariationsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { string result; @@ -16237,7 +16954,7 @@ PEGTransformerFactory::FinalizeLikeVariationsTrampoline(PEGTransformer &transfor result = transformer.Transform(choice_result); } } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeLikeTokenTrampoline(PEGTransformer &transformer, @@ -16245,10 +16962,10 @@ void PEGTransformerFactory::InitializeLikeTokenTrampoline(PEGTransformer &transf process.ReserveChildSlots(0); } -unique_ptr -PEGTransformerFactory::FinalizeLikeTokenTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeLikeTokenTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformLikeToken(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeILikeTokenTrampoline(PEGTransformer &transformer, @@ -16256,10 +16973,10 @@ void PEGTransformerFactory::InitializeILikeTokenTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeILikeTokenTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformILikeToken(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeGlobTokenTrampoline(PEGTransformer &transformer, @@ -16267,10 +16984,10 @@ void PEGTransformerFactory::InitializeGlobTokenTrampoline(PEGTransformer &transf process.ReserveChildSlots(0); } -unique_ptr -PEGTransformerFactory::FinalizeGlobTokenTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeGlobTokenTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformGlobToken(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSimilarToTokenTrampoline(PEGTransformer &transformer, @@ -16278,11 +16995,11 @@ void PEGTransformerFactory::InitializeSimilarToTokenTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSimilarToTokenTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformSimilarToToken(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeRegexMatchTokenTrampoline(PEGTransformer &transformer, @@ -16290,11 +17007,11 @@ void PEGTransformerFactory::InitializeRegexMatchTokenTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRegexMatchTokenTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformRegexMatchToken(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeRegexInsensitiveMatchTokenTrampoline(PEGTransformer &transformer, @@ -16302,11 +17019,11 @@ void PEGTransformerFactory::InitializeRegexInsensitiveMatchTokenTrampoline(PEGTr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRegexInsensitiveMatchTokenTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformRegexInsensitiveMatchToken(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeNotILikeOpTrampoline(PEGTransformer &transformer, @@ -16314,10 +17031,10 @@ void PEGTransformerFactory::InitializeNotILikeOpTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNotILikeOpTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformNotILikeOp(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeNotLikeOpTrampoline(PEGTransformer &transformer, @@ -16325,10 +17042,10 @@ void PEGTransformerFactory::InitializeNotLikeOpTrampoline(PEGTransformer &transf process.ReserveChildSlots(0); } -unique_ptr -PEGTransformerFactory::FinalizeNotLikeOpTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeNotLikeOpTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformNotLikeOp(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeNotRegexInsensitiveMatchOpTrampoline(PEGTransformer &transformer, @@ -16336,11 +17053,11 @@ void PEGTransformerFactory::InitializeNotRegexInsensitiveMatchOpTrampoline(PEGTr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNotRegexInsensitiveMatchOpTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformNotRegexInsensitiveMatchOp(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeNotSimilarToOpTrampoline(PEGTransformer &transformer, @@ -16348,11 +17065,11 @@ void PEGTransformerFactory::InitializeNotSimilarToOpTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNotSimilarToOpTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformNotSimilarToOp(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeInClauseTrampoline(PEGTransformer &transformer, @@ -16362,11 +17079,11 @@ void PEGTransformerFactory::InitializeInClauseTrampoline(PEGTransformer &transfo process.PushChild({transformer.GetRule("InExpression"), list_pr.GetChild(1)}, 0); } -unique_ptr PEGTransformerFactory::FinalizeInClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeInClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto in_expression = process.TakeResult>(0); auto result = TransformInClause(transformer, std::move(in_expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeInExpressionTrampoline(PEGTransformer &transformer, @@ -16378,15 +17095,15 @@ void PEGTransformerFactory::InitializeInExpressionTrampoline(PEGTransformer &tra auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeInContainsExpressionTrampoline(PEGTransformer &transformer, @@ -16396,12 +17113,12 @@ void PEGTransformerFactory::InitializeInContainsExpressionTrampoline(PEGTransfor process.PushChild({transformer.GetRule("OtherOperatorExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInContainsExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto other_operator_expression = process.TakeResult>(0); auto result = TransformInContainsExpression(transformer, std::move(other_operator_expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeInExpressionListTrampoline(PEGTransformer &transformer, @@ -16416,7 +17133,7 @@ void PEGTransformerFactory::InitializeInExpressionListTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInExpressionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -16427,7 +17144,7 @@ PEGTransformerFactory::FinalizeInExpressionListTrampoline(PEGTransformer &transf expression.push_back(process.TakeResult>(i)); } auto result = TransformInExpressionList(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeInSelectStatementTrampoline(PEGTransformer &transformer, @@ -16438,35 +17155,60 @@ void PEGTransformerFactory::InitializeInSelectStatementTrampoline(PEGTransformer 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInSelectStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto select_statement_internal = process.TakeResult>(0); auto result = TransformInSelectStatement(transformer, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeBetweenClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("OtherOperatorExpression"), list_pr.GetChild(3)}, 1); - process.PushChild({transformer.GetRule("OtherOperatorExpression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule("OtherOperatorExpression"), list_pr.GetChild(4)}, 1); + process.PushChild({transformer.GetRule("OtherOperatorExpression"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBetweenClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + bool has_result {}; + auto &has_result_opt = list_pr.GetChild(1).Cast(); + has_result = has_result_opt.HasResult(); auto other_operator_expression = process.TakeResult>(0); auto other_operator_expression_1 = process.TakeResult>(1); - auto result = TransformBetweenClause(transformer, std::move(other_operator_expression), + auto result = TransformBetweenClause(transformer, has_result, std::move(other_operator_expression), std::move(other_operator_expression_1)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeOtherOperatorExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); + auto &choice_pr = list_pr.Child(0); + auto &choice_result = choice_pr.GetResult(); + process.ReserveChildSlots(1); + auto child_rule = choice_result.GetRule(); + auto has_transform_process = child_rule && child_rule->transform_process; + if (!has_transform_process) { + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); + } + process.PushChild({*child_rule, choice_result}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeOtherOperatorExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto result = process.TakeResult>(0); + return transformer.MakeResult>(std::move(result)); +} + +void PEGTransformerFactory::InitializeInfixOtherOperatorExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); auto &repeat_opt = list_pr.GetChild(1).Cast(); idx_t dynamic_child_count = 0; if (repeat_opt.HasResult()) { @@ -16485,9 +17227,9 @@ void PEGTransformerFactory::InitializeOtherOperatorExpressionTrampoline(PEGTrans process.PushChild({transformer.GetRule("BitwiseExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeOtherOperatorExpressionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr +PEGTransformerFactory::FinalizeInfixOtherOperatorExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); idx_t dynamic_child_count = 0; auto &dynamic_repeat_opt = list_pr.GetChild(1).Cast(); @@ -16505,9 +17247,26 @@ PEGTransformerFactory::FinalizeOtherOperatorExpressionTrampoline(PEGTransformer } other_operator_tail = std::move(other_operator_tail_value); } - auto result = - TransformOtherOperatorExpression(transformer, std::move(bitwise_expression), std::move(other_operator_tail)); - return make_uniq>>(std::move(result)); + auto result = TransformInfixOtherOperatorExpression(transformer, std::move(bitwise_expression), + std::move(other_operator_tail)); + return transformer.MakeResult>(std::move(result)); +} + +void PEGTransformerFactory::InitializeCustomPrefixExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + process.PushChild({transformer.GetRule("OtherOperatorExpression"), list_pr.GetChild(1)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeCustomPrefixExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + auto operator_literal = list_pr.GetChild(0).Cast().operator_token; + auto other_operator_expression = process.TakeResult>(0); + auto result = TransformCustomPrefixExpression(transformer, operator_literal, std::move(other_operator_expression)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeOtherOperatorTailTrampoline(PEGTransformer &transformer, @@ -16518,13 +17277,13 @@ void PEGTransformerFactory::InitializeOtherOperatorTailTrampoline(PEGTransformer process.PushChild({transformer.GetRule("OtherOperator"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOtherOperatorTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto other_operator = process.TakeResult(0); auto bitwise_expression = process.TakeResult>(1); auto result = TransformOtherOperatorTail(transformer, std::move(other_operator), std::move(bitwise_expression)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeOtherOperatorTrampoline(PEGTransformer &transformer, @@ -16536,16 +17295,16 @@ void PEGTransformerFactory::InitializeOtherOperatorTrampoline(PEGTransformer &tr auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOtherOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeAnyAllParsedOperatorTrampoline(PEGTransformer &transformer, @@ -16555,12 +17314,12 @@ void PEGTransformerFactory::InitializeAnyAllParsedOperatorTrampoline(PEGTransfor process.PushChild({transformer.GetRule("AnyAllOperator"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAnyAllParsedOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto any_all_operator = process.TakeResult>(0); auto result = TransformAnyAllParsedOperator(transformer, any_all_operator); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeNamedOtherOperatorTrampoline(PEGTransformer &transformer, @@ -16578,12 +17337,12 @@ void PEGTransformerFactory::InitializeNamedOtherOperatorTrampoline(PEGTransforme return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNamedOtherOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { string child; @@ -16605,7 +17364,7 @@ PEGTransformerFactory::FinalizeNamedOtherOperatorTrampoline(PEGTransformer &tran } } auto result = TransformNamedOtherOperator(transformer, child); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeOperatorLiteralTrampoline(PEGTransformer &transformer, @@ -16613,28 +17372,40 @@ void PEGTransformerFactory::InitializeOperatorLiteralTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOperatorLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.parse_result.Cast().operator_token; - return make_uniq>(result); + return transformer.MakeResult(result); +} + +void PEGTransformerFactory::InitializeAnyOperatorLiteralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + process.ReserveChildSlots(0); +} + +arena_ptr +PEGTransformerFactory::FinalizeAnyOperatorLiteralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto result = process.parse_result.Cast().operator_token; + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeAnyAllOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("AnyOrAll"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("AnyOp"), list_pr.GetChild(0)}, 0); + process.ReserveChildSlots(1); + process.PushChild({transformer.GetRule("AnyOrAll"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAnyAllOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto any_op = process.TakeResult(0); - auto any_or_all = process.TakeResult(1); - auto result = TransformAnyAllOperator(transformer, any_op, any_or_all); - return make_uniq>>(result); + auto &list_pr = process.parse_result.Cast(); + auto any_operator_literal = list_pr.GetChild(0).Cast().operator_token; + auto any_or_all = process.TakeResult(0); + auto result = TransformAnyAllOperator(transformer, any_operator_literal, any_or_all); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeAnyOrAllTrampoline(PEGTransformer &transformer, @@ -16646,15 +17417,15 @@ void PEGTransformerFactory::InitializeAnyOrAllTrampoline(PEGTransformer &transfo auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr PEGTransformerFactory::FinalizeAnyOrAllTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeAnyOrAllTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSubqueryAnyTrampoline(PEGTransformer &transformer, @@ -16662,72 +17433,21 @@ void PEGTransformerFactory::InitializeSubqueryAnyTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr -PEGTransformerFactory::FinalizeSubqueryAnyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto result = TransformSubqueryAny(transformer); - return make_uniq>(result); -} - -void PEGTransformerFactory::InitializeSubqueryAllTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { - process.ReserveChildSlots(0); -} - -unique_ptr -PEGTransformerFactory::FinalizeSubqueryAllTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto result = TransformSubqueryAll(transformer); - return make_uniq>(result); -} - -void PEGTransformerFactory::InitializeInetOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { - process.ReserveChildSlots(0); -} - -unique_ptr -PEGTransformerFactory::FinalizeInetOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto &list_pr = process.parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = choice_pr.GetResult().Cast().keyword; - return make_uniq>(result); -} - -void PEGTransformerFactory::InitializeJsonOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { - process.ReserveChildSlots(0); -} - -unique_ptr -PEGTransformerFactory::FinalizeJsonOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - string result = "->>"; - return make_uniq>(result); -} - -void PEGTransformerFactory::InitializeListOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { - process.ReserveChildSlots(0); -} - -unique_ptr -PEGTransformerFactory::FinalizeListOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto &list_pr = process.parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = choice_pr.GetResult().Cast().keyword; - return make_uniq>(result); -} - -void PEGTransformerFactory::InitializeStringOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr +PEGTransformerFactory::FinalizeSubqueryAnyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { + auto result = TransformSubqueryAny(transformer); + return transformer.MakeResult(result); +} + +void PEGTransformerFactory::InitializeSubqueryAllTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { process.ReserveChildSlots(0); } -unique_ptr -PEGTransformerFactory::FinalizeStringOperatorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { - auto &list_pr = process.parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = choice_pr.GetResult().Cast().keyword; - return make_uniq>(result); +arena_ptr +PEGTransformerFactory::FinalizeSubqueryAllTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { + auto result = TransformSubqueryAll(transformer); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeQualifiedOperatorTrampoline(PEGTransformer &transformer, @@ -16738,12 +17458,12 @@ void PEGTransformerFactory::InitializeQualifiedOperatorTrampoline(PEGTransformer 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQualifiedOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto qualified_operator_contents = process.TakeResult(0); auto result = TransformQualifiedOperator(transformer, qualified_operator_contents); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeQualifiedOperatorContentsTrampoline(PEGTransformer &transformer, @@ -16755,19 +17475,17 @@ void PEGTransformerFactory::InitializeQualifiedOperatorContentsTrampoline(PEGTra auto &repeat_pr = repeat_opt.GetResult().Cast(); auto repeat_children = repeat_pr.GetChildren(); dynamic_child_count = repeat_children.size(); - process.ReserveChildSlots(2 + dynamic_child_count - 1); - process.PushChild({transformer.GetRule("AnyOp"), list_pr.GetChild(1)}, 1 + dynamic_child_count - 1); + process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; process.PushChild({transformer.GetRule("ColIdDot"), repeat_children[child_idx].get()}, 0 + child_idx); } } else { - process.ReserveChildSlots(2 - 1); - process.PushChild({transformer.GetRule("AnyOp"), list_pr.GetChild(1)}, 1 + dynamic_child_count - 1); + process.ReserveChildSlots(1 - 1); } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQualifiedOperatorContentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -16786,21 +17504,9 @@ PEGTransformerFactory::FinalizeQualifiedOperatorContentsTrampoline(PEGTransforme } col_id_dot = std::move(col_id_dot_value); } - auto any_op = process.TakeResult(1 + dynamic_child_count - 1); - auto result = TransformQualifiedOperatorContents(transformer, col_id_dot, any_op); - return make_uniq>(result); -} - -void PEGTransformerFactory::InitializeAnyOpTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - process.ReserveChildSlots(0); -} - -unique_ptr PEGTransformerFactory::FinalizeAnyOpTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { - auto &list_pr = process.parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto result = choice_pr.GetResult().Cast().keyword; - return make_uniq>(result); + auto any_operator_literal = list_pr.GetChild(1).Cast().operator_token; + auto result = TransformQualifiedOperatorContents(transformer, col_id_dot, any_operator_literal); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeBitwiseExpressionTrampoline(PEGTransformer &transformer, @@ -16821,10 +17527,10 @@ void PEGTransformerFactory::InitializeBitwiseExpressionTrampoline(PEGTransformer } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("AdditiveExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule("TildeExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBitwiseExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -16835,7 +17541,7 @@ PEGTransformerFactory::FinalizeBitwiseExpressionTrampoline(PEGTransformer &trans auto dynamic_repeat_children = dynamic_repeat_pr.GetChildren(); dynamic_child_count = dynamic_repeat_children.size(); } - auto additive_expression = process.TakeResult>(0); + auto tilde_expression = process.TakeResult>(0); optional> bitwise_expression_tail {}; if (dynamic_child_count > 0) { vector bitwise_expression_tail_value; @@ -16845,25 +17551,25 @@ PEGTransformerFactory::FinalizeBitwiseExpressionTrampoline(PEGTransformer &trans bitwise_expression_tail = std::move(bitwise_expression_tail_value); } auto result = - TransformBitwiseExpression(transformer, std::move(additive_expression), std::move(bitwise_expression_tail)); - return make_uniq>>(std::move(result)); + TransformBitwiseExpression(transformer, std::move(tilde_expression), std::move(bitwise_expression_tail)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeBitwiseExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("AdditiveExpression"), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule("TildeExpression"), list_pr.GetChild(1)}, 1); process.PushChild({transformer.GetRule("BitOperator"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBitwiseExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto bit_operator = process.TakeResult(0); - auto additive_expression = process.TakeResult>(1); - auto result = TransformBitwiseExpressionTail(transformer, bit_operator, std::move(additive_expression)); - return make_uniq>(std::move(result)); + auto tilde_expression = process.TakeResult>(1); + auto result = TransformBitwiseExpressionTail(transformer, bit_operator, std::move(tilde_expression)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeBitOperatorTrampoline(PEGTransformer &transformer, @@ -16871,12 +17577,60 @@ void PEGTransformerFactory::InitializeBitOperatorTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBitOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto &choice_pr = list_pr.Child(0); auto result = choice_pr.GetResult().Cast().keyword; - return make_uniq>(result); + return transformer.MakeResult(result); +} + +void PEGTransformerFactory::InitializeTildeExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + auto &repeat_opt = list_pr.GetChild(0).Cast(); + idx_t dynamic_child_count = 0; + if (repeat_opt.HasResult()) { + auto &repeat_pr = repeat_opt.GetResult().Cast(); + auto repeat_children = repeat_pr.GetChildren(); + dynamic_child_count = repeat_children.size(); + process.ReserveChildSlots(2 + dynamic_child_count - 1); + process.PushChild({transformer.GetRule("AdditiveExpression"), list_pr.GetChild(1)}, + 1 + dynamic_child_count - 1); + for (idx_t i = repeat_children.size(); i > 0; i--) { + auto child_idx = i - 1; + process.PushChild({transformer.GetRule("TildePrefixOperator"), repeat_children[child_idx].get()}, + 0 + child_idx); + } + } else { + process.ReserveChildSlots(2 - 1); + process.PushChild({transformer.GetRule("AdditiveExpression"), list_pr.GetChild(1)}, + 1 + dynamic_child_count - 1); + } +} + +arena_ptr +PEGTransformerFactory::FinalizeTildeExpressionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + idx_t dynamic_child_count = 0; + auto &dynamic_repeat_opt = list_pr.GetChild(0).Cast(); + if (dynamic_repeat_opt.HasResult()) { + auto &dynamic_repeat_pr = dynamic_repeat_opt.GetResult().Cast(); + auto dynamic_repeat_children = dynamic_repeat_pr.GetChildren(); + dynamic_child_count = dynamic_repeat_children.size(); + } + optional> tilde_prefix_operator {}; + if (dynamic_child_count > 0) { + vector tilde_prefix_operator_value; + for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { + tilde_prefix_operator_value.push_back(process.TakeResult(i)); + } + tilde_prefix_operator = std::move(tilde_prefix_operator_value); + } + auto additive_expression = process.TakeResult>(1 + dynamic_child_count - 1); + auto result = TransformTildeExpression(transformer, tilde_prefix_operator, std::move(additive_expression)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAdditiveExpressionTrampoline(PEGTransformer &transformer, @@ -16900,7 +17654,7 @@ void PEGTransformerFactory::InitializeAdditiveExpressionTrampoline(PEGTransforme process.PushChild({transformer.GetRule("MultiplicativeExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAdditiveExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -16922,7 +17676,7 @@ PEGTransformerFactory::FinalizeAdditiveExpressionTrampoline(PEGTransformer &tran } auto result = TransformAdditiveExpression(transformer, std::move(multiplicative_expression), std::move(additive_expression_tail)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAdditiveExpressionTailTrampoline(PEGTransformer &transformer, @@ -16933,26 +17687,26 @@ void PEGTransformerFactory::InitializeAdditiveExpressionTailTrampoline(PEGTransf process.PushChild({transformer.GetRule("Term"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAdditiveExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto term = process.TakeResult(0); auto multiplicative_expression = process.TakeResult>(1); auto result = TransformAdditiveExpressionTail(transformer, term, std::move(multiplicative_expression), process.parse_result.offset); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeTermTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeTermTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeTermTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto &choice_pr = list_pr.Child(0); auto result = choice_pr.GetResult().Cast().keyword; - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeMultiplicativeExpressionTrampoline(PEGTransformer &transformer, @@ -16976,7 +17730,7 @@ void PEGTransformerFactory::InitializeMultiplicativeExpressionTrampoline(PEGTran process.PushChild({transformer.GetRule("ExponentiationExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMultiplicativeExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -16998,7 +17752,7 @@ PEGTransformerFactory::FinalizeMultiplicativeExpressionTrampoline(PEGTransformer } auto result = TransformMultiplicativeExpression(transformer, std::move(exponentiation_expression), std::move(multiplicative_expression_tail)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeMultiplicativeExpressionTailTrampoline(PEGTransformer &transformer, @@ -17009,13 +17763,13 @@ void PEGTransformerFactory::InitializeMultiplicativeExpressionTailTrampoline(PEG process.PushChild({transformer.GetRule("Factor"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMultiplicativeExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto factor = process.TakeResult(0); auto exponentiation_expression = process.TakeResult>(1); auto result = TransformMultiplicativeExpressionTail(transformer, factor, std::move(exponentiation_expression)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeFactorTrampoline(PEGTransformer &transformer, @@ -17023,12 +17777,12 @@ void PEGTransformerFactory::InitializeFactorTrampoline(PEGTransformer &transform process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeFactorTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeFactorTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto &choice_pr = list_pr.Child(0); auto result = choice_pr.GetResult().Cast().keyword; - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeExponentiationExpressionTrampoline(PEGTransformer &transformer, @@ -17052,7 +17806,7 @@ void PEGTransformerFactory::InitializeExponentiationExpressionTrampoline(PEGTran process.PushChild({transformer.GetRule("CollateExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExponentiationExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -17074,7 +17828,7 @@ PEGTransformerFactory::FinalizeExponentiationExpressionTrampoline(PEGTransformer } auto result = TransformExponentiationExpression(transformer, std::move(collate_expression), std::move(exponentiation_expression_tail)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeExponentiationExpressionTailTrampoline(PEGTransformer &transformer, @@ -17085,13 +17839,13 @@ void PEGTransformerFactory::InitializeExponentiationExpressionTailTrampoline(PEG process.PushChild({transformer.GetRule("ExponentOperator"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExponentiationExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto exponent_operator = process.TakeResult(0); auto collate_expression = process.TakeResult>(1); auto result = TransformExponentiationExpressionTail(transformer, exponent_operator, std::move(collate_expression)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeExponentOperatorTrampoline(PEGTransformer &transformer, @@ -17099,13 +17853,13 @@ void PEGTransformerFactory::InitializeExponentOperatorTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExponentOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto &choice_pr = list_pr.Child(0); auto result = choice_pr.GetResult().Cast().keyword; - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCollateExpressionTrampoline(PEGTransformer &transformer, @@ -17129,7 +17883,7 @@ void PEGTransformerFactory::InitializeCollateExpressionTrampoline(PEGTransformer process.PushChild({transformer.GetRule("AtTimeZoneExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCollateExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -17151,7 +17905,7 @@ PEGTransformerFactory::FinalizeCollateExpressionTrampoline(PEGTransformer &trans } auto result = TransformCollateExpression(transformer, std::move(at_time_zone_expression), std::move(collate_expression_tail)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCollateExpressionTailTrampoline(PEGTransformer &transformer, @@ -17161,12 +17915,12 @@ void PEGTransformerFactory::InitializeCollateExpressionTailTrampoline(PEGTransfo process.PushChild({transformer.GetRule("AtTimeZoneExpression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCollateExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto at_time_zone_expression = process.TakeResult>(0); auto result = TransformCollateExpressionTail(transformer, std::move(at_time_zone_expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAtTimeZoneExpressionTrampoline(PEGTransformer &transformer, @@ -17190,7 +17944,7 @@ void PEGTransformerFactory::InitializeAtTimeZoneExpressionTrampoline(PEGTransfor process.PushChild({transformer.GetRule("PrefixExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAtTimeZoneExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -17212,7 +17966,7 @@ PEGTransformerFactory::FinalizeAtTimeZoneExpressionTrampoline(PEGTransformer &tr } auto result = TransformAtTimeZoneExpression(transformer, std::move(prefix_expression), std::move(at_time_zone_expression_tail)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAtTimeZoneExpressionTailTrampoline(PEGTransformer &transformer, @@ -17222,12 +17976,12 @@ void PEGTransformerFactory::InitializeAtTimeZoneExpressionTailTrampoline(PEGTran process.PushChild({transformer.GetRule("PrefixExpression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAtTimeZoneExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto prefix_expression = process.TakeResult>(0); auto result = TransformAtTimeZoneExpressionTail(transformer, std::move(prefix_expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializePrefixOperatorTrampoline(PEGTransformer &transformer, @@ -17243,12 +17997,12 @@ void PEGTransformerFactory::InitializePrefixOperatorTrampoline(PEGTransformer &t return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePrefixOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { string result; @@ -17269,7 +18023,7 @@ PEGTransformerFactory::FinalizePrefixOperatorTrampoline(PEGTransformer &transfor result = transformer.Transform(choice_result); } } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeMinusPrefixOperatorTrampoline(PEGTransformer &transformer, @@ -17277,11 +18031,11 @@ void PEGTransformerFactory::InitializeMinusPrefixOperatorTrampoline(PEGTransform process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMinusPrefixOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { string result = "-"; - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializePlusPrefixOperatorTrampoline(PEGTransformer &transformer, @@ -17289,11 +18043,11 @@ void PEGTransformerFactory::InitializePlusPrefixOperatorTrampoline(PEGTransforme process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePlusPrefixOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { string result = "+"; - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTildePrefixOperatorTrampoline(PEGTransformer &transformer, @@ -17301,11 +18055,11 @@ void PEGTransformerFactory::InitializeTildePrefixOperatorTrampoline(PEGTransform process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTildePrefixOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { string result = "~"; - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeBaseExpressionTrampoline(PEGTransformer &transformer, @@ -17319,7 +18073,7 @@ void PEGTransformerFactory::InitializeBaseExpressionTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("SingleExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBaseExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto single_expression = process.TakeResult>(0); @@ -17328,7 +18082,7 @@ PEGTransformerFactory::FinalizeBaseExpressionTrampoline(PEGTransformer &transfor indirection_list = process.TakeResult>>(1); } auto result = TransformBaseExpression(transformer, std::move(single_expression), std::move(indirection_list)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIndirectionListTrampoline(PEGTransformer &transformer, @@ -17344,7 +18098,7 @@ void PEGTransformerFactory::InitializeIndirectionListTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIndirectionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -17356,7 +18110,7 @@ PEGTransformerFactory::FinalizeIndirectionListTrampoline(PEGTransformer &transfo indirection.push_back(process.TakeResult>(i)); } auto result = TransformIndirectionList(transformer, std::move(indirection)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeIndirectionTrampoline(PEGTransformer &transformer, @@ -17368,15 +18122,15 @@ void PEGTransformerFactory::InitializeIndirectionTrampoline(PEGTransformer &tran auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIndirectionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCastOperatorTrampoline(PEGTransformer &transformer, @@ -17386,11 +18140,11 @@ void PEGTransformerFactory::InitializeCastOperatorTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("Type"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCastOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto type = process.TakeResult(0); auto result = TransformCastOperator(transformer, type); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDotOperatorTrampoline(PEGTransformer &transformer, @@ -17402,15 +18156,15 @@ void PEGTransformerFactory::InitializeDotOperatorTrampoline(PEGTransformer &tran auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDotOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDotMethodOperatorTrampoline(PEGTransformer &transformer, @@ -17420,12 +18174,12 @@ void PEGTransformerFactory::InitializeDotMethodOperatorTrampoline(PEGTransformer process.PushChild({transformer.GetRule("MethodExpression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDotMethodOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto method_expression = process.TakeResult>(0); auto result = TransformDotMethodOperator(transformer, std::move(method_expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDotColumnOperatorTrampoline(PEGTransformer &transformer, @@ -17435,12 +18189,12 @@ void PEGTransformerFactory::InitializeDotColumnOperatorTrampoline(PEGTransformer process.PushChild({transformer.GetRule("ColLabel"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDotColumnOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_label = process.TakeResult(0); auto result = TransformDotColumnOperator(transformer, col_label); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeMethodExpressionTrampoline(PEGTransformer &transformer, @@ -17451,13 +18205,13 @@ void PEGTransformerFactory::InitializeMethodExpressionTrampoline(PEGTransformer process.PushChild({transformer.GetRule("ColLabel"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMethodExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_label = process.TakeResult(0); auto method_expression_arguments = process.TakeResult(1); auto result = TransformMethodExpression(transformer, col_label, std::move(method_expression_arguments)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeMethodExpressionArgumentsTrampoline(PEGTransformer &transformer, @@ -17468,12 +18222,12 @@ void PEGTransformerFactory::InitializeMethodExpressionArgumentsTrampoline(PEGTra {transformer.GetRule("MethodExpressionArgumentList"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMethodExpressionArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto method_expression_argument_list = process.TakeResult(0); auto result = TransformMethodExpressionArguments(transformer, std::move(method_expression_argument_list)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeMethodExpressionArgumentListTrampoline(PEGTransformer &transformer, @@ -17499,7 +18253,7 @@ void PEGTransformerFactory::InitializeMethodExpressionArgumentListTrampoline(PEG } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMethodExpressionArgumentListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional distinct_or_all {}; @@ -17521,7 +18275,7 @@ PEGTransformerFactory::FinalizeMethodExpressionArgumentListTrampoline(PEGTransfo auto result = TransformMethodExpressionArgumentList(transformer, distinct_or_all, std::move(method_function_arguments), std::move(order_by_clause), ignore_or_respect_nulls); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeMethodFunctionArgumentsTrampoline(PEGTransformer &transformer, @@ -17536,7 +18290,7 @@ void PEGTransformerFactory::InitializeMethodFunctionArgumentsTrampoline(PEGTrans } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMethodFunctionArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -17547,7 +18301,7 @@ PEGTransformerFactory::FinalizeMethodFunctionArgumentsTrampoline(PEGTransformer function_argument.push_back(process.TakeResult(i)); } auto result = TransformMethodFunctionArguments(transformer, std::move(function_argument)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSliceExpressionTrampoline(PEGTransformer &transformer, @@ -17557,25 +18311,21 @@ void PEGTransformerFactory::InitializeSliceExpressionTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("SliceBound"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSliceExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto slice_bound = process.TakeResult>>(0); auto result = TransformSliceExpression(transformer, std::move(slice_bound)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSliceBoundTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(3); - auto &step_slice_bound_opt = list_pr.GetChild(2).Cast(); - if (step_slice_bound_opt.HasResult()) { - process.PushChild({transformer.GetRule("StepSliceBound"), step_slice_bound_opt.GetResult()}, 2); - } - auto &end_slice_bound_opt = list_pr.GetChild(1).Cast(); - if (end_slice_bound_opt.HasResult()) { - process.PushChild({transformer.GetRule("EndSliceBound"), end_slice_bound_opt.GetResult()}, 1); + process.ReserveChildSlots(2); + auto &slice_upper_bound_opt = list_pr.GetChild(1).Cast(); + if (slice_upper_bound_opt.HasResult()) { + process.PushChild({transformer.GetRule("SliceUpperBound"), slice_upper_bound_opt.GetResult()}, 1); } auto &expression_opt = list_pr.GetChild(0).Cast(); if (expression_opt.HasResult()) { @@ -17583,77 +18333,98 @@ void PEGTransformerFactory::InitializeSliceBoundTrampoline(PEGTransformer &trans } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSliceBoundTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional> expression {}; if (process.child_results[0]) { expression = process.TakeResult>(0); } - optional> end_slice_bound {}; + optional>> slice_upper_bound {}; if (process.child_results[1]) { - end_slice_bound = process.TakeResult>(1); - } - optional> step_slice_bound {}; - if (process.child_results[2]) { - step_slice_bound = process.TakeResult>(2); + slice_upper_bound = process.TakeResult>>(1); } - auto result = TransformSliceBound(transformer, std::move(expression), std::move(end_slice_bound), - std::move(step_slice_bound)); - return make_uniq>>>(std::move(result)); + auto result = TransformSliceBound(transformer, std::move(expression), std::move(slice_upper_bound)); + return transformer.MakeResult>>(std::move(result)); } -void PEGTransformerFactory::InitializeEndSliceBoundTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +void PEGTransformerFactory::InitializeSliceUpperBoundTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); + auto &choice_pr = list_pr.Child(0); + auto &choice_result = choice_pr.GetResult(); process.ReserveChildSlots(1); - auto &end_slice_value_opt = list_pr.GetChild(1).Cast(); - if (end_slice_value_opt.HasResult()) { - process.PushChild({transformer.GetRule("EndSliceValue"), end_slice_value_opt.GetResult()}, 0); + auto child_rule = choice_result.GetRule(); + auto has_transform_process = child_rule && child_rule->transform_process; + if (!has_transform_process) { + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } + process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeEndSliceBoundTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { - optional> end_slice_value {}; - if (process.child_results[0]) { - end_slice_value = process.TakeResult>(0); - } - auto result = TransformEndSliceBound(transformer, std::move(end_slice_value)); - return make_uniq>>(std::move(result)); +arena_ptr +PEGTransformerFactory::FinalizeSliceUpperBoundTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto result = process.TakeResult>>(0); + return transformer.MakeResult>>(std::move(result)); } -void PEGTransformerFactory::InitializeEndSliceValueTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +void PEGTransformerFactory::InitializeMinusStepSliceBoundTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - auto &choice_pr = list_pr.Child(0); - auto &choice_result = choice_pr.GetResult(); process.ReserveChildSlots(1); - auto child_rule = choice_result.GetRule(); - auto has_transform_process = child_rule && child_rule->transform_process; - if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + process.PushChild({transformer.GetRule("StepSliceBound"), list_pr.GetChild(2)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeMinusStepSliceBoundTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto step_slice_bound = process.TakeResult>(0); + auto result = TransformMinusStepSliceBound(transformer, std::move(step_slice_bound)); + return transformer.MakeResult>>(std::move(result)); +} + +void PEGTransformerFactory::InitializeEndStepSliceBoundTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(2); + auto &step_slice_bound_opt = list_pr.GetChild(1).Cast(); + if (step_slice_bound_opt.HasResult()) { + process.PushChild({transformer.GetRule("StepSliceBound"), step_slice_bound_opt.GetResult()}, 1); } - process.PushChild({*child_rule, choice_result}, 0); + process.PushChild({transformer.GetRule("EndSliceBound"), list_pr.GetChild(0)}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeEndSliceValueTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { - auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); +arena_ptr +PEGTransformerFactory::FinalizeEndStepSliceBoundTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto end_slice_bound = process.TakeResult>(0); + optional> step_slice_bound {}; + if (process.child_results[1]) { + step_slice_bound = process.TakeResult>(1); + } + auto result = TransformEndStepSliceBound(transformer, std::move(end_slice_bound), std::move(step_slice_bound)); + return transformer.MakeResult>>(std::move(result)); } -void PEGTransformerFactory::InitializeEndSliceMinusTrampoline(PEGTransformer &transformer, +void PEGTransformerFactory::InitializeEndSliceBoundTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - process.ReserveChildSlots(0); + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + auto &expression_opt = list_pr.GetChild(1).Cast(); + if (expression_opt.HasResult()) { + process.PushChild({transformer.GetRule("Expression"), expression_opt.GetResult()}, 0); + } } -unique_ptr -PEGTransformerFactory::FinalizeEndSliceMinusTrampoline(PEGTransformer &transformer, +arena_ptr +PEGTransformerFactory::FinalizeEndSliceBoundTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto result = TransformEndSliceMinus(transformer); - return make_uniq>>(std::move(result)); + optional> expression {}; + if (process.child_results[0]) { + expression = process.TakeResult>(0); + } + auto result = TransformEndSliceBound(transformer, std::move(expression)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeStepSliceBoundTrampoline(PEGTransformer &transformer, @@ -17666,7 +18437,7 @@ void PEGTransformerFactory::InitializeStepSliceBoundTrampoline(PEGTransformer &t } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeStepSliceBoundTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional> expression {}; @@ -17674,7 +18445,7 @@ PEGTransformerFactory::FinalizeStepSliceBoundTrampoline(PEGTransformer &transfor expression = process.TakeResult>(0); } auto result = TransformStepSliceBound(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializePostfixOperatorTrampoline(PEGTransformer &transformer, @@ -17682,11 +18453,11 @@ void PEGTransformerFactory::InitializePostfixOperatorTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePostfixOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformPostfixOperator(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSpecialFunctionExpressionTrampoline(PEGTransformer &transformer, @@ -17698,16 +18469,16 @@ void PEGTransformerFactory::InitializeSpecialFunctionExpressionTrampoline(PEGTra auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSpecialFunctionExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCoalesceExpressionTrampoline(PEGTransformer &transformer, @@ -17722,7 +18493,7 @@ void PEGTransformerFactory::InitializeCoalesceExpressionTrampoline(PEGTransforme } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCoalesceExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -17733,7 +18504,7 @@ PEGTransformerFactory::FinalizeCoalesceExpressionTrampoline(PEGTransformer &tran expression.push_back(process.TakeResult>(i)); } auto result = TransformCoalesceExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUnpackExpressionTrampoline(PEGTransformer &transformer, @@ -17743,12 +18514,12 @@ void PEGTransformerFactory::InitializeUnpackExpressionTrampoline(PEGTransformer process.PushChild({transformer.GetRule("Expression"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUnpackExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformUnpackExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTryExpressionTrampoline(PEGTransformer &transformer, @@ -17758,12 +18529,12 @@ void PEGTransformerFactory::InitializeTryExpressionTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("Expression"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTryExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformTryExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeColumnsExpressionTrampoline(PEGTransformer &transformer, @@ -17773,7 +18544,7 @@ void PEGTransformerFactory::InitializeColumnsExpressionTrampoline(PEGTransformer process.PushChild({transformer.GetRule("Expression"), ExtractResultFromParens(list_pr.GetChild(2))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColumnsExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -17782,7 +18553,7 @@ PEGTransformerFactory::FinalizeColumnsExpressionTrampoline(PEGTransformer &trans has_result = has_result_opt.HasResult(); auto expression = process.TakeResult>(0); auto result = TransformColumnsExpression(transformer, has_result, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeExtractExpressionTrampoline(PEGTransformer &transformer, @@ -17792,12 +18563,12 @@ void PEGTransformerFactory::InitializeExtractExpressionTrampoline(PEGTransformer process.PushChild({transformer.GetRule("ExtractArguments"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExtractExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto extract_arguments = process.TakeResult>>(0); auto result = TransformExtractExpression(transformer, std::move(extract_arguments)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeExtractArgumentsTrampoline(PEGTransformer &transformer, @@ -17808,13 +18579,13 @@ void PEGTransformerFactory::InitializeExtractArgumentsTrampoline(PEGTransformer process.PushChild({transformer.GetRule("ExtractArgument"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExtractArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto extract_argument = process.TakeResult>(0); auto expression = process.TakeResult>(1); auto result = TransformExtractArguments(transformer, std::move(extract_argument), std::move(expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeLambdaExpressionTrampoline(PEGTransformer &transformer, @@ -17830,7 +18601,7 @@ void PEGTransformerFactory::InitializeLambdaExpressionTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLambdaExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -17842,7 +18613,7 @@ PEGTransformerFactory::FinalizeLambdaExpressionTrampoline(PEGTransformer &transf } auto expression = process.TakeResult>(1 + dynamic_child_count - 1); auto result = TransformLambdaExpression(transformer, col_id_or_string, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNullIfExpressionTrampoline(PEGTransformer &transformer, @@ -17852,12 +18623,12 @@ void PEGTransformerFactory::InitializeNullIfExpressionTrampoline(PEGTransformer process.PushChild({transformer.GetRule("NullIfArguments"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNullIfExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto null_if_arguments = process.TakeResult>>(0); auto result = TransformNullIfExpression(transformer, std::move(null_if_arguments)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNullIfArgumentsTrampoline(PEGTransformer &transformer, @@ -17868,13 +18639,13 @@ void PEGTransformerFactory::InitializeNullIfArgumentsTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNullIfArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto expression_1 = process.TakeResult>(1); auto result = TransformNullIfArguments(transformer, std::move(expression), std::move(expression_1)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializePositionExpressionTrampoline(PEGTransformer &transformer, @@ -17884,12 +18655,12 @@ void PEGTransformerFactory::InitializePositionExpressionTrampoline(PEGTransforme process.PushChild({transformer.GetRule("PositionArguments"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePositionExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto position_arguments = process.TakeResult>>(0); auto result = TransformPositionExpression(transformer, std::move(position_arguments)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializePositionArgumentsTrampoline(PEGTransformer &transformer, @@ -17900,13 +18671,13 @@ void PEGTransformerFactory::InitializePositionArgumentsTrampoline(PEGTransformer process.PushChild({transformer.GetRule("OtherOperatorExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePositionArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto other_operator_expression = process.TakeResult>(0); auto expression = process.TakeResult>(1); auto result = TransformPositionArguments(transformer, std::move(other_operator_expression), std::move(expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeRowExpressionTrampoline(PEGTransformer &transformer, @@ -17927,7 +18698,7 @@ void PEGTransformerFactory::InitializeRowExpressionTrampoline(PEGTransformer &tr } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRowExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -17947,7 +18718,7 @@ PEGTransformerFactory::FinalizeRowExpressionTrampoline(PEGTransformer &transform expression = std::move(expression_value); } auto result = TransformRowExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSubstringExpressionTrampoline(PEGTransformer &transformer, @@ -17957,12 +18728,12 @@ void PEGTransformerFactory::InitializeSubstringExpressionTrampoline(PEGTransform process.PushChild({transformer.GetRule("SubstringArguments"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSubstringExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto substring_arguments = process.TakeResult>>(0); auto result = TransformSubstringExpression(transformer, std::move(substring_arguments)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSubstringArgumentsTrampoline(PEGTransformer &transformer, @@ -17974,16 +18745,16 @@ void PEGTransformerFactory::InitializeSubstringArgumentsTrampoline(PEGTransforme auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSubstringArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>>(0); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeSubstringExpressionListTrampoline(PEGTransformer &transformer, @@ -17998,7 +18769,7 @@ void PEGTransformerFactory::InitializeSubstringExpressionListTrampoline(PEGTrans } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSubstringExpressionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -18009,7 +18780,7 @@ PEGTransformerFactory::FinalizeSubstringExpressionListTrampoline(PEGTransformer expression.push_back(process.TakeResult>(i)); } auto result = TransformSubstringExpressionList(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeSubstringParametersTrampoline(PEGTransformer &transformer, @@ -18020,13 +18791,13 @@ void PEGTransformerFactory::InitializeSubstringParametersTrampoline(PEGTransform process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSubstringParametersTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto substring_from_for = process.TakeResult>>(1); auto result = TransformSubstringParameters(transformer, std::move(expression), std::move(substring_from_for)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeSubstringFromForTrampoline(PEGTransformer &transformer, @@ -18038,16 +18809,16 @@ void PEGTransformerFactory::InitializeSubstringFromForTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSubstringFromForTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>>(0); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeSubstringFromOptionalForTrampoline(PEGTransformer &transformer, @@ -18061,7 +18832,7 @@ void PEGTransformerFactory::InitializeSubstringFromOptionalForTrampoline(PEGTran process.PushChild({transformer.GetRule("FromExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSubstringFromOptionalForTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto from_expression = process.TakeResult>(0); @@ -18070,7 +18841,7 @@ PEGTransformerFactory::FinalizeSubstringFromOptionalForTrampoline(PEGTransformer for_expression = process.TakeResult>(1); } auto result = TransformSubstringFromOptionalFor(transformer, std::move(from_expression), std::move(for_expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeSubstringForTrampoline(PEGTransformer &transformer, @@ -18080,11 +18851,11 @@ void PEGTransformerFactory::InitializeSubstringForTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("ForExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSubstringForTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto for_expression = process.TakeResult>(0); auto result = TransformSubstringFor(transformer, std::move(for_expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeTrimExpressionTrampoline(PEGTransformer &transformer, @@ -18094,12 +18865,12 @@ void PEGTransformerFactory::InitializeTrimExpressionTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("TrimArguments"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTrimExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto trim_arguments = process.TakeResult(0); auto result = TransformTrimExpression(transformer, std::move(trim_arguments)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTrimArgumentsTrampoline(PEGTransformer &transformer, @@ -18122,7 +18893,7 @@ void PEGTransformerFactory::InitializeTrimArgumentsTrampoline(PEGTransformer &tr } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTrimArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -18141,7 +18912,7 @@ PEGTransformerFactory::FinalizeTrimArgumentsTrampoline(PEGTransformer &transform expression.push_back(process.TakeResult>(i)); } auto result = TransformTrimArguments(transformer, trim_direction, std::move(trim_source), std::move(expression)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeTrimDirectionTrampoline(PEGTransformer &transformer, @@ -18157,12 +18928,12 @@ void PEGTransformerFactory::InitializeTrimDirectionTrampoline(PEGTransformer &tr return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTrimDirectionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { string result; @@ -18183,7 +18954,7 @@ PEGTransformerFactory::FinalizeTrimDirectionTrampoline(PEGTransformer &transform result = transformer.Transform(choice_result); } } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTrimBothTrampoline(PEGTransformer &transformer, @@ -18191,10 +18962,10 @@ void PEGTransformerFactory::InitializeTrimBothTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeTrimBothTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeTrimBothTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformTrimBoth(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTrimLeadingTrampoline(PEGTransformer &transformer, @@ -18202,10 +18973,10 @@ void PEGTransformerFactory::InitializeTrimLeadingTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTrimLeadingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformTrimLeading(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTrimTrailingTrampoline(PEGTransformer &transformer, @@ -18213,10 +18984,10 @@ void PEGTransformerFactory::InitializeTrimTrailingTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTrimTrailingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformTrimTrailing(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTrimSourceTrampoline(PEGTransformer &transformer, @@ -18229,14 +19000,14 @@ void PEGTransformerFactory::InitializeTrimSourceTrampoline(PEGTransformer &trans } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTrimSourceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional> expression {}; if (process.child_results[0]) { expression = process.TakeResult>(0); } auto result = TransformTrimSource(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeOverlayExpressionTrampoline(PEGTransformer &transformer, @@ -18246,12 +19017,12 @@ void PEGTransformerFactory::InitializeOverlayExpressionTrampoline(PEGTransformer process.PushChild({transformer.GetRule("OverlayArguments"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOverlayExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto overlay_arguments = process.TakeResult>>(0); auto result = TransformOverlayExpression(transformer, std::move(overlay_arguments)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeOverlayArgumentsTrampoline(PEGTransformer &transformer, @@ -18263,16 +19034,16 @@ void PEGTransformerFactory::InitializeOverlayArgumentsTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOverlayArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>>(0); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeOverlayParametersTrampoline(PEGTransformer &transformer, @@ -18288,7 +19059,7 @@ void PEGTransformerFactory::InitializeOverlayParametersTrampoline(PEGTransformer process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOverlayParametersTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); @@ -18300,7 +19071,7 @@ PEGTransformerFactory::FinalizeOverlayParametersTrampoline(PEGTransformer &trans } auto result = TransformOverlayParameters(transformer, std::move(expression), std::move(expression_1), std::move(from_expression), std::move(for_expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeFromExpressionTrampoline(PEGTransformer &transformer, @@ -18310,12 +19081,12 @@ void PEGTransformerFactory::InitializeFromExpressionTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFromExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformFromExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeForExpressionTrampoline(PEGTransformer &transformer, @@ -18325,12 +19096,12 @@ void PEGTransformerFactory::InitializeForExpressionTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeForExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformForExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeOverlayExpressionListTrampoline(PEGTransformer &transformer, @@ -18345,7 +19116,7 @@ void PEGTransformerFactory::InitializeOverlayExpressionListTrampoline(PEGTransfo } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOverlayExpressionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -18356,7 +19127,7 @@ PEGTransformerFactory::FinalizeOverlayExpressionListTrampoline(PEGTransformer &t expression.push_back(process.TakeResult>(i)); } auto result = TransformOverlayExpressionList(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeExtractArgumentTrampoline(PEGTransformer &transformer, @@ -18368,16 +19139,16 @@ void PEGTransformerFactory::InitializeExtractArgumentTrampoline(PEGTransformer & auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExtractArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeExtractDatePartArgumentTrampoline(PEGTransformer &transformer, @@ -18387,12 +19158,12 @@ void PEGTransformerFactory::InitializeExtractDatePartArgumentTrampoline(PEGTrans process.PushChild({transformer.GetRule("ExtractDatePart"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExtractDatePartArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto extract_date_part = process.TakeResult(0); auto result = TransformExtractDatePartArgument(transformer, extract_date_part); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeExtractIdentifierArgumentTrampoline(PEGTransformer &transformer, @@ -18400,13 +19171,13 @@ void PEGTransformerFactory::InitializeExtractIdentifierArgumentTrampoline(PEGTra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExtractIdentifierArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto identifier = list_pr.GetChild(0).Cast().identifier; auto result = TransformExtractIdentifierArgument(transformer, identifier); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeExtractStringArgumentTrampoline(PEGTransformer &transformer, @@ -18414,13 +19185,13 @@ void PEGTransformerFactory::InitializeExtractStringArgumentTrampoline(PEGTransfo process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExtractStringArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(0)); auto result = TransformExtractStringArgument(transformer, string_literal); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeExtractDatePartTrampoline(PEGTransformer &transformer, @@ -18432,16 +19203,16 @@ void PEGTransformerFactory::InitializeExtractDatePartTrampoline(PEGTransformer & auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExtractDatePartTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeExternalResourceStatementTrampoline(PEGTransformer &transformer, @@ -18453,39 +19224,39 @@ void PEGTransformerFactory::InitializeExternalResourceStatementTrampoline(PEGTra auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExternalResourceStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCreateExternalResourceStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - auto &external_resource_creation_options_opt = list_pr.GetChild(5).Cast(); + auto &external_resource_creation_options_opt = list_pr.GetChild(4).Cast(); if (external_resource_creation_options_opt.HasResult()) { process.PushChild({transformer.GetRule("ExternalResourceCreationOptions"), external_resource_creation_options_opt.GetResult()}, 1); } - auto &attach_alias_opt = list_pr.GetChild(4).Cast(); + auto &attach_alias_opt = list_pr.GetChild(3).Cast(); if (attach_alias_opt.HasResult()) { process.PushChild({transformer.GetRule("AttachAlias"), attach_alias_opt.GetResult()}, 0); } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateExternalResourceStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(3)); + auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(2)); optional attach_alias {}; if (process.child_results[0]) { attach_alias = process.TakeResult(0); @@ -18496,25 +19267,25 @@ PEGTransformerFactory::FinalizeCreateExternalResourceStmtTrampoline(PEGTransform } auto result = TransformCreateExternalResourceStmt(transformer, string_literal, attach_alias, external_resource_creation_options); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeRegisterExternalResourceStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(6)}, 1); - auto &attach_alias_opt = list_pr.GetChild(4).Cast(); + process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(5)}, 1); + auto &attach_alias_opt = list_pr.GetChild(3).Cast(); if (attach_alias_opt.HasResult()) { process.PushChild({transformer.GetRule("AttachAlias"), attach_alias_opt.GetResult()}, 0); } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRegisterExternalResourceStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(3)); + auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(2)); optional attach_alias {}; if (process.child_results[0]) { attach_alias = process.TakeResult(0); @@ -18522,22 +19293,22 @@ PEGTransformerFactory::FinalizeRegisterExternalResourceStmtTrampoline(PEGTransfo auto expression = process.TakeResult>(1); auto result = TransformRegisterExternalResourceStmt(transformer, string_literal, attach_alias, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDestroyExternalResourceStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(3)}, 0); + process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDestroyExternalResourceStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id = process.TakeResult(0); auto result = TransformDestroyExternalResourceStmt(transformer, col_id); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeShowExternalResourcesStmtTrampoline(PEGTransformer &transformer, @@ -18550,7 +19321,7 @@ void PEGTransformerFactory::InitializeShowExternalResourcesStmtTrampoline(PEGTra } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeShowExternalResourcesStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional show_all_modifier {}; @@ -18558,7 +19329,7 @@ PEGTransformerFactory::FinalizeShowExternalResourcesStmtTrampoline(PEGTransforme show_all_modifier = process.TakeResult(0); } auto result = TransformShowExternalResourcesStmt(transformer, show_all_modifier); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeShowAllModifierTrampoline(PEGTransformer &transformer, @@ -18566,11 +19337,11 @@ void PEGTransformerFactory::InitializeShowAllModifierTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeShowAllModifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformShowAllModifier(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeExternalResourceCreationOptionsTrampoline(PEGTransformer &transformer, @@ -18580,26 +19351,137 @@ void PEGTransformerFactory::InitializeExternalResourceCreationOptionsTrampoline( process.PushChild({transformer.GetRule("GenericCopyOptionList"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExternalResourceCreationOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(result); + return transformer.MakeResult>(result); +} + +void PEGTransformerFactory::InitializeAttachToExternalResourceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(3); + auto &attach_options_opt = list_pr.GetChild(4).Cast(); + if (attach_options_opt.HasResult()) { + process.PushChild({transformer.GetRule("AttachOptions"), attach_options_opt.GetResult()}, 2); + } + process.PushChild({transformer.GetRule("AttachAlias"), list_pr.GetChild(3)}, 1); + process.PushChild({transformer.GetRule("ExternalResourceSource"), list_pr.GetChild(2)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeAttachToExternalResourceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto external_resource_source = process.TakeResult>(0); + auto attach_alias = process.TakeResult(1); + optional> attach_options {}; + if (process.child_results[2]) { + attach_options = process.TakeResult>(2); + } + auto result = TransformAttachToExternalResource(transformer, std::move(external_resource_source), attach_alias, + attach_options); + return transformer.MakeResult>(std::move(result)); +} + +void PEGTransformerFactory::InitializeConnectToExternalResourceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(2); + auto &attach_options_opt = list_pr.GetChild(3).Cast(); + if (attach_options_opt.HasResult()) { + process.PushChild({transformer.GetRule("AttachOptions"), attach_options_opt.GetResult()}, 1); + } + process.PushChild({transformer.GetRule("ExternalResourceSource"), list_pr.GetChild(2)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeConnectToExternalResourceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto external_resource_source = process.TakeResult>(0); + optional> attach_options {}; + if (process.child_results[1]) { + attach_options = process.TakeResult>(1); + } + auto result = TransformConnectToExternalResource(transformer, std::move(external_resource_source), attach_options); + return transformer.MakeResult>(std::move(result)); +} + +void PEGTransformerFactory::InitializeExternalResourceSourceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + auto &choice_pr = list_pr.Child(0); + auto &choice_result = choice_pr.GetResult(); + process.ReserveChildSlots(1); + auto child_rule = choice_result.GetRule(); + auto has_transform_process = child_rule && child_rule->transform_process; + if (!has_transform_process) { + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); + } + process.PushChild({*child_rule, choice_result}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeExternalResourceSourceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto result = process.TakeResult>(0); + return transformer.MakeResult>(std::move(result)); +} + +void PEGTransformerFactory::InitializeExternalResourceCreateClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + auto &external_resource_creation_options_opt = list_pr.GetChild(4).Cast(); + if (external_resource_creation_options_opt.HasResult()) { + process.PushChild({transformer.GetRule("ExternalResourceCreationOptions"), + external_resource_creation_options_opt.GetResult()}, + 0); + } +} + +arena_ptr +PEGTransformerFactory::FinalizeExternalResourceCreateClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(3)); + optional> external_resource_creation_options {}; + if (process.child_results[0]) { + external_resource_creation_options = process.TakeResult>(0); + } + auto result = + TransformExternalResourceCreateClause(transformer, string_literal, external_resource_creation_options); + return transformer.MakeResult>(std::move(result)); +} + +void PEGTransformerFactory::InitializeExternalResourceReferenceClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(1)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeExternalResourceReferenceClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto col_id = process.TakeResult(0); + auto result = TransformExternalResourceReferenceClause(transformer, col_id); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeInsertStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(8); - auto &returning_clause_opt = list_pr.GetChild(9).Cast(); + auto &returning_clause_opt = list_pr.GetChild(10).Cast(); if (returning_clause_opt.HasResult()) { process.PushChild({transformer.GetRule("ReturningClause"), returning_clause_opt.GetResult()}, 7); } - auto &on_conflict_clause_opt = list_pr.GetChild(8).Cast(); + auto &on_conflict_clause_opt = list_pr.GetChild(9).Cast(); if (on_conflict_clause_opt.HasResult()) { process.PushChild({transformer.GetRule("OnConflictClause"), on_conflict_clause_opt.GetResult()}, 6); } - process.PushChild({transformer.GetRule("InsertValues"), list_pr.GetChild(7)}, 5); + process.PushChild({transformer.GetRule("InsertValues"), list_pr.GetChild(8)}, 5); auto &insert_column_list_opt = list_pr.GetChild(6).Cast(); if (insert_column_list_opt.HasResult()) { process.PushChild({transformer.GetRule("InsertColumnList"), insert_column_list_opt.GetResult()}, 4); @@ -18619,9 +19501,10 @@ void PEGTransformerFactory::InitializeInsertStatementTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInsertStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); optional with_clause {}; if (process.child_results[0]) { with_clause = process.TakeResult(0); @@ -18639,6 +19522,9 @@ PEGTransformerFactory::FinalizeInsertStatementTrampoline(PEGTransformer &transfo if (process.child_results[4]) { insert_column_list = process.TakeResult>(4); } + bool has_result {}; + auto &has_result_opt = list_pr.GetChild(7).Cast(); + has_result = has_result_opt.HasResult(); auto insert_values = process.TakeResult(5); optional> on_conflict_clause {}; if (process.child_results[6]) { @@ -18648,10 +19534,11 @@ PEGTransformerFactory::FinalizeInsertStatementTrampoline(PEGTransformer &transfo if (process.child_results[7]) { returning_clause = process.TakeResult>>(7); } - auto result = TransformInsertStatement(transformer, std::move(with_clause), or_action, std::move(insert_target), - by_name_or_position, insert_column_list, std::move(insert_values), - std::move(on_conflict_clause), std::move(returning_clause)); - return make_uniq>>(std::move(result)); + auto result = + TransformInsertStatement(transformer, std::move(with_clause), or_action, std::move(insert_target), + by_name_or_position, insert_column_list, has_result, std::move(insert_values), + std::move(on_conflict_clause), std::move(returning_clause)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeOrActionTrampoline(PEGTransformer &transformer, @@ -18663,15 +19550,15 @@ void PEGTransformerFactory::InitializeOrActionTrampoline(PEGTransformer &transfo auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr PEGTransformerFactory::FinalizeOrActionTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeOrActionTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeInsertOrReplaceTrampoline(PEGTransformer &transformer, @@ -18679,11 +19566,11 @@ void PEGTransformerFactory::InitializeInsertOrReplaceTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInsertOrReplaceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformInsertOrReplace(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeInsertOrIgnoreTrampoline(PEGTransformer &transformer, @@ -18691,11 +19578,11 @@ void PEGTransformerFactory::InitializeInsertOrIgnoreTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInsertOrIgnoreTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformInsertOrIgnore(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeByNameOrPositionTrampoline(PEGTransformer &transformer, @@ -18707,16 +19594,16 @@ void PEGTransformerFactory::InitializeByNameOrPositionTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeByNameOrPositionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeInsertByNameOrderTrampoline(PEGTransformer &transformer, @@ -18726,11 +19613,11 @@ void PEGTransformerFactory::InitializeInsertByNameOrderTrampoline(PEGTransformer process.PushChild({transformer.GetRule("InsertByName"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInsertByNameOrderTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeInsertByPositionOrderTrampoline(PEGTransformer &transformer, @@ -18740,11 +19627,11 @@ void PEGTransformerFactory::InitializeInsertByPositionOrderTrampoline(PEGTransfo process.PushChild({transformer.GetRule("InsertByPosition"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInsertByPositionOrderTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeInsertByNameTrampoline(PEGTransformer &transformer, @@ -18752,10 +19639,10 @@ void PEGTransformerFactory::InitializeInsertByNameTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInsertByNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformInsertByName(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeInsertByPositionTrampoline(PEGTransformer &transformer, @@ -18763,11 +19650,11 @@ void PEGTransformerFactory::InitializeInsertByPositionTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInsertByPositionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformInsertByPosition(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeInsertTargetTrampoline(PEGTransformer &transformer, @@ -18781,7 +19668,7 @@ void PEGTransformerFactory::InitializeInsertTargetTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInsertTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto base_table_name = process.TakeResult>(0); optional insert_alias {}; @@ -18789,7 +19676,7 @@ PEGTransformerFactory::FinalizeInsertTargetTrampoline(PEGTransformer &transforme insert_alias = process.TakeResult(1); } auto result = TransformInsertTarget(transformer, std::move(base_table_name), insert_alias); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeInsertAliasTrampoline(PEGTransformer &transformer, @@ -18797,12 +19684,12 @@ void PEGTransformerFactory::InitializeInsertAliasTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInsertAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto identifier = list_pr.GetChild(1).Cast().identifier; auto result = TransformInsertAlias(transformer, identifier); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeColumnListTrampoline(PEGTransformer &transformer, @@ -18817,7 +19704,7 @@ void PEGTransformerFactory::InitializeColumnListTrampoline(PEGTransformer &trans } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); @@ -18827,7 +19714,7 @@ PEGTransformerFactory::FinalizeColumnListTrampoline(PEGTransformer &transformer, col_id.push_back(process.TakeResult(i)); } auto result = TransformColumnList(transformer, col_id); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeInsertColumnListTrampoline(PEGTransformer &transformer, @@ -18837,12 +19724,12 @@ void PEGTransformerFactory::InitializeInsertColumnListTrampoline(PEGTransformer process.PushChild({transformer.GetRule("ColumnList"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInsertColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto column_list = process.TakeResult>(0); auto result = TransformInsertColumnList(transformer, column_list); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeInsertValuesTrampoline(PEGTransformer &transformer, @@ -18854,15 +19741,15 @@ void PEGTransformerFactory::InitializeInsertValuesTrampoline(PEGTransformer &tra auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInsertValuesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeSelectInsertValuesTrampoline(PEGTransformer &transformer, @@ -18872,12 +19759,12 @@ void PEGTransformerFactory::InitializeSelectInsertValuesTrampoline(PEGTransforme process.PushChild({transformer.GetRule("SelectStatementInternal"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSelectInsertValuesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto select_statement_internal = process.TakeResult>(0); auto result = TransformSelectInsertValues(transformer, std::move(select_statement_internal)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeDefaultValuesTrampoline(PEGTransformer &transformer, @@ -18885,11 +19772,11 @@ void PEGTransformerFactory::InitializeDefaultValuesTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDefaultValuesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformDefaultValues(transformer); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeOnConflictClauseTrampoline(PEGTransformer &transformer, @@ -18903,7 +19790,7 @@ void PEGTransformerFactory::InitializeOnConflictClauseTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOnConflictClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional on_conflict_target {}; @@ -18912,7 +19799,7 @@ PEGTransformerFactory::FinalizeOnConflictClauseTrampoline(PEGTransformer &transf } auto on_conflict_action = process.TakeResult>(1); auto result = TransformOnConflictClause(transformer, std::move(on_conflict_target), std::move(on_conflict_action)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeOnConflictTargetTrampoline(PEGTransformer &transformer, @@ -18924,16 +19811,16 @@ void PEGTransformerFactory::InitializeOnConflictTargetTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOnConflictTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeOnConflictExpressionTargetTrampoline(PEGTransformer &transformer, @@ -18947,7 +19834,7 @@ void PEGTransformerFactory::InitializeOnConflictExpressionTargetTrampoline(PEGTr process.PushChild({transformer.GetRule("ColumnIdList"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOnConflictExpressionTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto column_id_list = process.TakeResult>(0); @@ -18956,7 +19843,7 @@ PEGTransformerFactory::FinalizeOnConflictExpressionTargetTrampoline(PEGTransform where_clause = process.TakeResult>(1); } auto result = TransformOnConflictExpressionTarget(transformer, column_id_list, std::move(where_clause)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeOnConflictIndexTargetTrampoline(PEGTransformer &transformer, @@ -18966,12 +19853,12 @@ void PEGTransformerFactory::InitializeOnConflictIndexTargetTrampoline(PEGTransfo process.PushChild({transformer.GetRule("ConstraintName"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOnConflictIndexTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto constraint_name = process.TakeResult(0); auto result = TransformOnConflictIndexTarget(transformer, constraint_name); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeOnConflictActionTrampoline(PEGTransformer &transformer, @@ -18983,16 +19870,16 @@ void PEGTransformerFactory::InitializeOnConflictActionTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOnConflictActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeOnConflictUpdateTrampoline(PEGTransformer &transformer, @@ -19003,19 +19890,19 @@ void PEGTransformerFactory::InitializeOnConflictUpdateTrampoline(PEGTransformer if (where_clause_opt.HasResult()) { process.PushChild({transformer.GetRule("WhereClause"), where_clause_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("UpdateSetClause"), list_pr.GetChild(3)}, 0); + process.PushChild({transformer.GetRule("UpdateSetClauseList"), list_pr.GetChild(3)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOnConflictUpdateTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto update_set_clause = process.TakeResult>(0); + auto update_set_clause_list = process.TakeResult>(0); optional> where_clause {}; if (process.child_results[1]) { where_clause = process.TakeResult>(1); } - auto result = TransformOnConflictUpdate(transformer, std::move(update_set_clause), std::move(where_clause)); - return make_uniq>>(std::move(result)); + auto result = TransformOnConflictUpdate(transformer, std::move(update_set_clause_list), std::move(where_clause)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeOnConflictNothingTrampoline(PEGTransformer &transformer, @@ -19023,11 +19910,11 @@ void PEGTransformerFactory::InitializeOnConflictNothingTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOnConflictNothingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformOnConflictNothing(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeReturningClauseTrampoline(PEGTransformer &transformer, @@ -19037,12 +19924,12 @@ void PEGTransformerFactory::InitializeReturningClauseTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("TargetList"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeReturningClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto target_list = process.TakeResult>>(0); auto result = TransformReturningClause(transformer, std::move(target_list)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeLoadStatementTrampoline(PEGTransformer &transformer, @@ -19060,7 +19947,7 @@ void PEGTransformerFactory::InitializeLoadStatementTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLoadStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id_or_string = process.TakeResult(0); @@ -19073,7 +19960,7 @@ PEGTransformerFactory::FinalizeLoadStatementTrampoline(PEGTransformer &transform extension_alias = process.TakeResult(2); } auto result = TransformLoadStatement(transformer, col_id_or_string, from_source, extension_alias); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeExtensionAliasTrampoline(PEGTransformer &transformer, @@ -19081,13 +19968,13 @@ void PEGTransformerFactory::InitializeExtensionAliasTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExtensionAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto identifier = list_pr.GetChild(1).Cast().identifier; auto result = TransformExtensionAlias(transformer, identifier); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeInstallStatementTrampoline(PEGTransformer &transformer, @@ -19109,7 +19996,7 @@ void PEGTransformerFactory::InitializeInstallStatementTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInstallStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -19131,7 +20018,7 @@ PEGTransformerFactory::FinalizeInstallStatementTrampoline(PEGTransformer &transf } auto result = TransformInstallStatement(transformer, has_result, install_and_load, identifier_or_string_literal, from_source, version_number); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeInstallAndLoadTrampoline(PEGTransformer &transformer, @@ -19139,11 +20026,11 @@ void PEGTransformerFactory::InitializeInstallAndLoadTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInstallAndLoadTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformInstallAndLoad(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeUpdateExtensionsStatementTrampoline(PEGTransformer &transformer, @@ -19151,7 +20038,7 @@ void PEGTransformerFactory::InitializeUpdateExtensionsStatementTrampoline(PEGTra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUpdateExtensionsStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -19167,7 +20054,7 @@ PEGTransformerFactory::FinalizeUpdateExtensionsStatementTrampoline(PEGTransforme identifier = std::move(identifier_value); } auto result = TransformUpdateExtensionsStatement(transformer, identifier); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeFromSourceTrampoline(PEGTransformer &transformer, @@ -19179,15 +20066,15 @@ void PEGTransformerFactory::InitializeFromSourceTrampoline(PEGTransformer &trans auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFromSourceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeFromSourceIdentifierTrampoline(PEGTransformer &transformer, @@ -19195,13 +20082,13 @@ void PEGTransformerFactory::InitializeFromSourceIdentifierTrampoline(PEGTransfor process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFromSourceIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto identifier = list_pr.GetChild(1).Cast().identifier; auto result = TransformFromSourceIdentifier(transformer, identifier); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeFromSourceStringTrampoline(PEGTransformer &transformer, @@ -19209,13 +20096,13 @@ void PEGTransformerFactory::InitializeFromSourceStringTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFromSourceStringTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(1)); auto result = TransformFromSourceString(transformer, string_literal); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeVersionNumberTrampoline(PEGTransformer &transformer, @@ -19225,12 +20112,12 @@ void PEGTransformerFactory::InitializeVersionNumberTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("IdentifierOrStringLiteral"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeVersionNumberTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto identifier_or_string_literal = process.TakeResult(0); auto result = TransformVersionNumber(transformer, identifier_or_string_literal); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeExtensionRepositoryStatementTrampoline(PEGTransformer &transformer, @@ -19242,16 +20129,16 @@ void PEGTransformerFactory::InitializeExtensionRepositoryStatementTrampoline(PEG auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExtensionRepositoryStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCreateExtensionRepositoryStmtTrampoline(PEGTransformer &transformer, @@ -19274,7 +20161,7 @@ void PEGTransformerFactory::InitializeCreateExtensionRepositoryStmtTrampoline(PE } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCreateExtensionRepositoryStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional or_replace {}; @@ -19293,7 +20180,7 @@ PEGTransformerFactory::FinalizeCreateExtensionRepositoryStmtTrampoline(PEGTransf } auto result = TransformCreateExtensionRepositoryStmt(transformer, or_replace, if_not_exists, col_id_or_string, repository_prefix, repository_public_key); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeRepositoryPrefixTrampoline(PEGTransformer &transformer, @@ -19301,7 +20188,7 @@ void PEGTransformerFactory::InitializeRepositoryPrefixTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRepositoryPrefixTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -19310,7 +20197,7 @@ PEGTransformerFactory::FinalizeRepositoryPrefixTrampoline(PEGTransformer &transf has_result = has_result_opt.HasResult(); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(2)); auto result = TransformRepositoryPrefix(transformer, has_result, string_literal); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeRepositoryPublicKeyTrampoline(PEGTransformer &transformer, @@ -19318,7 +20205,7 @@ void PEGTransformerFactory::InitializeRepositoryPublicKeyTrampoline(PEGTransform process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRepositoryPublicKeyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -19332,7 +20219,7 @@ PEGTransformerFactory::FinalizeRepositoryPublicKeyTrampoline(PEGTransformer &tra string_literal.push_back(string_literal_value); } auto result = TransformRepositoryPublicKey(transformer, has_result, string_literal); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeDropExtensionRepositoryStmtTrampoline(PEGTransformer &transformer, @@ -19346,7 +20233,7 @@ void PEGTransformerFactory::InitializeDropExtensionRepositoryStmtTrampoline(PEGT } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDropExtensionRepositoryStmtTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional if_exists {}; @@ -19355,7 +20242,7 @@ PEGTransformerFactory::FinalizeDropExtensionRepositoryStmtTrampoline(PEGTransfor } auto col_id_or_string = process.TakeResult(1); auto result = TransformDropExtensionRepositoryStmt(transformer, if_exists, col_id_or_string); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTableMatchRecognizeClauseTrampoline(PEGTransformer &transformer, @@ -19369,7 +20256,7 @@ void PEGTransformerFactory::InitializeTableMatchRecognizeClauseTrampoline(PEGTra process.PushChild({transformer.GetRule("MatchRecognizeBody"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTableMatchRecognizeClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto match_recognize_body = process.TakeResult>(0); @@ -19378,7 +20265,7 @@ PEGTransformerFactory::FinalizeTableMatchRecognizeClauseTrampoline(PEGTransforme table_alias = process.TakeResult(1); } auto result = TransformTableMatchRecognizeClause(transformer, std::move(match_recognize_body), table_alias); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeMatchRecognizeBodyTrampoline(PEGTransformer &transformer, @@ -19395,7 +20282,7 @@ void PEGTransformerFactory::InitializeMatchRecognizeBodyTrampoline(PEGTransforme } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMatchRecognizeBodyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -19407,7 +20294,7 @@ PEGTransformerFactory::FinalizeMatchRecognizeBodyTrampoline(PEGTransformer &tran match_recognize_clause.push_back(process.TakeResult(i)); } auto result = TransformMatchRecognizeBody(transformer, std::move(match_recognize_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeMatchRecognizeClauseTrampoline(PEGTransformer &transformer, @@ -19419,16 +20306,16 @@ void PEGTransformerFactory::InitializeMatchRecognizeClauseTrampoline(PEGTransfor auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMatchRecognizeClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeMRPartitionTrampoline(PEGTransformer &transformer, @@ -19438,11 +20325,11 @@ void PEGTransformerFactory::InitializeMRPartitionTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("WindowPartition"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMRPartitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto window_partition = process.TakeResult>>(0); auto result = TransformMRPartition(transformer, std::move(window_partition)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeMROrderByTrampoline(PEGTransformer &transformer, @@ -19452,11 +20339,11 @@ void PEGTransformerFactory::InitializeMROrderByTrampoline(PEGTransformer &transf process.PushChild({transformer.GetRule("OrderByClause"), list_pr.GetChild(0)}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeMROrderByTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeMROrderByTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto order_by_clause = process.TakeResult>(0); auto result = TransformMROrderBy(transformer, std::move(order_by_clause)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeMRMeasuresTrampoline(PEGTransformer &transformer, @@ -19466,11 +20353,11 @@ void PEGTransformerFactory::InitializeMRMeasuresTrampoline(PEGTransformer &trans process.PushChild({transformer.GetRule("MeasuresClause"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMRMeasuresTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto measures_clause = process.TakeResult>>(0); auto result = TransformMRMeasures(transformer, std::move(measures_clause)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeMRRowsTrampoline(PEGTransformer &transformer, @@ -19480,11 +20367,11 @@ void PEGTransformerFactory::InitializeMRRowsTrampoline(PEGTransformer &transform process.PushChild({transformer.GetRule("RowsPerMatch"), list_pr.GetChild(0)}, 0); } -unique_ptr PEGTransformerFactory::FinalizeMRRowsTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeMRRowsTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto rows_per_match = process.TakeResult(0); auto result = TransformMRRows(transformer, rows_per_match); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeMRSkipTrampoline(PEGTransformer &transformer, @@ -19494,11 +20381,11 @@ void PEGTransformerFactory::InitializeMRSkipTrampoline(PEGTransformer &transform process.PushChild({transformer.GetRule("AfterMatchSkip"), list_pr.GetChild(0)}, 0); } -unique_ptr PEGTransformerFactory::FinalizeMRSkipTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeMRSkipTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto after_match_skip = process.TakeResult(0); auto result = TransformMRSkip(transformer, std::move(after_match_skip)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeMRPatternTrampoline(PEGTransformer &transformer, @@ -19508,11 +20395,11 @@ void PEGTransformerFactory::InitializeMRPatternTrampoline(PEGTransformer &transf process.PushChild({transformer.GetRule("PatternClause"), list_pr.GetChild(0)}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeMRPatternTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeMRPatternTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto pattern_clause = process.TakeResult>(0); auto result = TransformMRPattern(transformer, std::move(pattern_clause)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeMRSubsetTrampoline(PEGTransformer &transformer, @@ -19522,11 +20409,11 @@ void PEGTransformerFactory::InitializeMRSubsetTrampoline(PEGTransformer &transfo process.PushChild({transformer.GetRule("SubsetClause"), list_pr.GetChild(0)}, 0); } -unique_ptr PEGTransformerFactory::FinalizeMRSubsetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeMRSubsetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto subset_clause = process.TakeResult>(0); auto result = TransformMRSubset(transformer, std::move(subset_clause)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeMRDefineAutoTrampoline(PEGTransformer &transformer, @@ -19534,10 +20421,10 @@ void PEGTransformerFactory::InitializeMRDefineAutoTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMRDefineAutoTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformMRDefineAuto(transformer); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeMRDefineTrampoline(PEGTransformer &transformer, @@ -19547,11 +20434,11 @@ void PEGTransformerFactory::InitializeMRDefineTrampoline(PEGTransformer &transfo process.PushChild({transformer.GetRule("DefineClause"), list_pr.GetChild(0)}, 0); } -unique_ptr PEGTransformerFactory::FinalizeMRDefineTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeMRDefineTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto define_clause = process.TakeResult>>(0); auto result = TransformMRDefine(transformer, std::move(define_clause)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeMeasuresClauseTrampoline(PEGTransformer &transformer, @@ -19566,7 +20453,7 @@ void PEGTransformerFactory::InitializeMeasuresClauseTrampoline(PEGTransformer &t } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMeasuresClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -19577,7 +20464,7 @@ PEGTransformerFactory::FinalizeMeasuresClauseTrampoline(PEGTransformer &transfor measures_element.push_back(process.TakeResult>(i)); } auto result = TransformMeasuresClause(transformer, std::move(measures_element)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeMeasuresElementTrampoline(PEGTransformer &transformer, @@ -19588,13 +20475,13 @@ void PEGTransformerFactory::InitializeMeasuresElementTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMeasuresElementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto col_label_or_string = process.TakeResult(1); auto result = TransformMeasuresElement(transformer, std::move(expression), col_label_or_string); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeMeasureSemanticsTrampoline(PEGTransformer &transformer, @@ -19606,16 +20493,16 @@ void PEGTransformerFactory::InitializeMeasureSemanticsTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMeasureSemanticsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeRunningSemanticsTrampoline(PEGTransformer &transformer, @@ -19623,11 +20510,11 @@ void PEGTransformerFactory::InitializeRunningSemanticsTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRunningSemanticsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformRunningSemantics(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeFinalSemanticsTrampoline(PEGTransformer &transformer, @@ -19635,11 +20522,11 @@ void PEGTransformerFactory::InitializeFinalSemanticsTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFinalSemanticsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformFinalSemantics(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeRowsPerMatchTrampoline(PEGTransformer &transformer, @@ -19651,15 +20538,15 @@ void PEGTransformerFactory::InitializeRowsPerMatchTrampoline(PEGTransformer &tra auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRowsPerMatchTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeOneRowPerMatchTrampoline(PEGTransformer &transformer, @@ -19667,11 +20554,11 @@ void PEGTransformerFactory::InitializeOneRowPerMatchTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOneRowPerMatchTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformOneRowPerMatch(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeAllRowsPerMatchTrampoline(PEGTransformer &transformer, @@ -19684,7 +20571,7 @@ void PEGTransformerFactory::InitializeAllRowsPerMatchTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAllRowsPerMatchTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional all_rows_empty_matches {}; @@ -19692,7 +20579,7 @@ PEGTransformerFactory::FinalizeAllRowsPerMatchTrampoline(PEGTransformer &transfo all_rows_empty_matches = process.TakeResult(0); } auto result = TransformAllRowsPerMatch(transformer, all_rows_empty_matches); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeAllRowsEmptyMatchesTrampoline(PEGTransformer &transformer, @@ -19704,16 +20591,16 @@ void PEGTransformerFactory::InitializeAllRowsEmptyMatchesTrampoline(PEGTransform auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAllRowsEmptyMatchesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeShowEmptyMatchesTrampoline(PEGTransformer &transformer, @@ -19721,11 +20608,11 @@ void PEGTransformerFactory::InitializeShowEmptyMatchesTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeShowEmptyMatchesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformShowEmptyMatches(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeOmitEmptyMatchesTrampoline(PEGTransformer &transformer, @@ -19733,11 +20620,11 @@ void PEGTransformerFactory::InitializeOmitEmptyMatchesTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOmitEmptyMatchesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformOmitEmptyMatches(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeWithUnmatchedRowsTrampoline(PEGTransformer &transformer, @@ -19745,11 +20632,11 @@ void PEGTransformerFactory::InitializeWithUnmatchedRowsTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWithUnmatchedRowsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformWithUnmatchedRows(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeAfterMatchSkipTrampoline(PEGTransformer &transformer, @@ -19759,11 +20646,11 @@ void PEGTransformerFactory::InitializeAfterMatchSkipTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("AfterMatchSkipTarget"), list_pr.GetChild(3)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAfterMatchSkipTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeAfterMatchSkipTargetTrampoline(PEGTransformer &transformer, @@ -19775,16 +20662,16 @@ void PEGTransformerFactory::InitializeAfterMatchSkipTargetTrampoline(PEGTransfor auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAfterMatchSkipTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeSkipToNextRowTrampoline(PEGTransformer &transformer, @@ -19792,11 +20679,11 @@ void PEGTransformerFactory::InitializeSkipToNextRowTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSkipToNextRowTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformSkipToNextRow(transformer); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeSkipPastLastRowTrampoline(PEGTransformer &transformer, @@ -19804,11 +20691,11 @@ void PEGTransformerFactory::InitializeSkipPastLastRowTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSkipPastLastRowTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformSkipPastLastRow(transformer); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeSkipToFirstVarTrampoline(PEGTransformer &transformer, @@ -19818,12 +20705,12 @@ void PEGTransformerFactory::InitializeSkipToFirstVarTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("ColLabelOrString"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSkipToFirstVarTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_label_or_string = process.TakeResult(0); auto result = TransformSkipToFirstVar(transformer, col_label_or_string); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeSkipToLastVarTrampoline(PEGTransformer &transformer, @@ -19833,12 +20720,12 @@ void PEGTransformerFactory::InitializeSkipToLastVarTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("ColLabelOrString"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSkipToLastVarTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_label_or_string = process.TakeResult(0); auto result = TransformSkipToLastVar(transformer, col_label_or_string); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeSkipToVarTrampoline(PEGTransformer &transformer, @@ -19848,11 +20735,11 @@ void PEGTransformerFactory::InitializeSkipToVarTrampoline(PEGTransformer &transf process.PushChild({transformer.GetRule("ColLabelOrString"), list_pr.GetChild(1)}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeSkipToVarTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeSkipToVarTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto col_label_or_string = process.TakeResult(0); auto result = TransformSkipToVar(transformer, col_label_or_string); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializePatternClauseTrampoline(PEGTransformer &transformer, @@ -19862,11 +20749,11 @@ void PEGTransformerFactory::InitializePatternClauseTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("RowPattern"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePatternClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeRowPatternTrampoline(PEGTransformer &transformer, @@ -19890,7 +20777,7 @@ void PEGTransformerFactory::InitializeRowPatternTrampoline(PEGTransformer &trans process.PushChild({transformer.GetRule("RowPatternTerm"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRowPatternTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); idx_t dynamic_child_count = 0; @@ -19910,7 +20797,7 @@ PEGTransformerFactory::FinalizeRowPatternTrampoline(PEGTransformer &transformer, row_pattern_alternative = std::move(row_pattern_alternative_value); } auto result = TransformRowPattern(transformer, std::move(row_pattern_term), std::move(row_pattern_alternative)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeRowPatternAlternativeTrampoline(PEGTransformer &transformer, @@ -19920,11 +20807,11 @@ void PEGTransformerFactory::InitializeRowPatternAlternativeTrampoline(PEGTransfo process.PushChild({transformer.GetRule("RowPatternTerm"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRowPatternAlternativeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeRowPatternTermTrampoline(PEGTransformer &transformer, @@ -19940,7 +20827,7 @@ void PEGTransformerFactory::InitializeRowPatternTermTrampoline(PEGTransformer &t } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRowPatternTermTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -19952,7 +20839,7 @@ PEGTransformerFactory::FinalizeRowPatternTermTrampoline(PEGTransformer &transfor row_pattern_factor.push_back(process.TakeResult>(i)); } auto result = TransformRowPatternTerm(transformer, std::move(row_pattern_factor)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeRowPatternFactorTrampoline(PEGTransformer &transformer, @@ -19966,7 +20853,7 @@ void PEGTransformerFactory::InitializeRowPatternFactorTrampoline(PEGTransformer process.PushChild({transformer.GetRule("RowPatternPrimary"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRowPatternFactorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto row_pattern_primary = process.TakeResult>(0); @@ -19975,7 +20862,7 @@ PEGTransformerFactory::FinalizeRowPatternFactorTrampoline(PEGTransformer &transf row_pattern_quantifier = process.TakeResult(1); } auto result = TransformRowPatternFactor(transformer, std::move(row_pattern_primary), row_pattern_quantifier); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeRowPatternPrimaryTrampoline(PEGTransformer &transformer, @@ -19987,16 +20874,16 @@ void PEGTransformerFactory::InitializeRowPatternPrimaryTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRowPatternPrimaryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeRowPatternPermuteTrampoline(PEGTransformer &transformer, @@ -20011,7 +20898,7 @@ void PEGTransformerFactory::InitializeRowPatternPermuteTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRowPatternPermuteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -20022,7 +20909,7 @@ PEGTransformerFactory::FinalizeRowPatternPermuteTrampoline(PEGTransformer &trans row_pattern.push_back(process.TakeResult>(i)); } auto result = TransformRowPatternPermute(transformer, std::move(row_pattern)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeRowPatternAnchorTrampoline(PEGTransformer &transformer, @@ -20034,16 +20921,16 @@ void PEGTransformerFactory::InitializeRowPatternAnchorTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRowPatternAnchorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializePatternStartTrampoline(PEGTransformer &transformer, @@ -20051,10 +20938,10 @@ void PEGTransformerFactory::InitializePatternStartTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePatternStartTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformPatternStart(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializePatternEndTrampoline(PEGTransformer &transformer, @@ -20062,10 +20949,10 @@ void PEGTransformerFactory::InitializePatternEndTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePatternEndTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformPatternEnd(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeRowPatternGroupTrampoline(PEGTransformer &transformer, @@ -20075,11 +20962,11 @@ void PEGTransformerFactory::InitializeRowPatternGroupTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("RowPattern"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRowPatternGroupTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeRowPatternEmptyTrampoline(PEGTransformer &transformer, @@ -20087,11 +20974,11 @@ void PEGTransformerFactory::InitializeRowPatternEmptyTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRowPatternEmptyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformRowPatternEmpty(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeRowPatternExclusionTrampoline(PEGTransformer &transformer, @@ -20101,12 +20988,12 @@ void PEGTransformerFactory::InitializeRowPatternExclusionTrampoline(PEGTransform process.PushChild({transformer.GetRule("RowPattern"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRowPatternExclusionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto row_pattern = process.TakeResult>(0); auto result = TransformRowPatternExclusion(transformer, std::move(row_pattern)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeRowPatternLabelTrampoline(PEGTransformer &transformer, @@ -20116,12 +21003,12 @@ void PEGTransformerFactory::InitializeRowPatternLabelTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("ColLabelOrString"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRowPatternLabelTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_label_or_string = process.TakeResult(0); auto result = TransformRowPatternLabel(transformer, col_label_or_string); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeRowPatternQuantifierTrampoline(PEGTransformer &transformer, @@ -20135,7 +21022,7 @@ void PEGTransformerFactory::InitializeRowPatternQuantifierTrampoline(PEGTransfor process.PushChild({transformer.GetRule("RowPatternQuantifierKind"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRowPatternQuantifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto row_pattern_quantifier_kind = process.TakeResult(0); @@ -20144,7 +21031,7 @@ PEGTransformerFactory::FinalizeRowPatternQuantifierTrampoline(PEGTransformer &tr quantifier_reluctant = process.TakeResult(1); } auto result = TransformRowPatternQuantifier(transformer, row_pattern_quantifier_kind, quantifier_reluctant); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeRowPatternQuantifierKindTrampoline(PEGTransformer &transformer, @@ -20156,16 +21043,16 @@ void PEGTransformerFactory::InitializeRowPatternQuantifierKindTrampoline(PEGTran auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRowPatternQuantifierKindTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeQuantifierReluctantTrampoline(PEGTransformer &transformer, @@ -20173,11 +21060,11 @@ void PEGTransformerFactory::InitializeQuantifierReluctantTrampoline(PEGTransform process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQuantifierReluctantTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformQuantifierReluctant(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeQuantifierStarTrampoline(PEGTransformer &transformer, @@ -20185,11 +21072,11 @@ void PEGTransformerFactory::InitializeQuantifierStarTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQuantifierStarTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformQuantifierStar(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeQuantifierPlusTrampoline(PEGTransformer &transformer, @@ -20197,11 +21084,11 @@ void PEGTransformerFactory::InitializeQuantifierPlusTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQuantifierPlusTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformQuantifierPlus(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeQuantifierOptionalTrampoline(PEGTransformer &transformer, @@ -20209,11 +21096,11 @@ void PEGTransformerFactory::InitializeQuantifierOptionalTrampoline(PEGTransforme process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQuantifierOptionalTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformQuantifierOptional(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeQuantifierRangeTrampoline(PEGTransformer &transformer, @@ -20223,11 +21110,11 @@ void PEGTransformerFactory::InitializeQuantifierRangeTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("QuantifierBounds"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQuantifierRangeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeQuantifierBoundsTrampoline(PEGTransformer &transformer, @@ -20239,16 +21126,16 @@ void PEGTransformerFactory::InitializeQuantifierBoundsTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQuantifierBoundsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeQuantifierMinMaxTrampoline(PEGTransformer &transformer, @@ -20256,14 +21143,14 @@ void PEGTransformerFactory::InitializeQuantifierMinMaxTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQuantifierMinMaxTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto number_literal = TransformNumberLiteral(transformer, list_pr.GetChild(0)); auto number_literal_1 = TransformNumberLiteral(transformer, list_pr.GetChild(2)); auto result = TransformQuantifierMinMax(transformer, std::move(number_literal), std::move(number_literal_1)); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeQuantifierMinTrampoline(PEGTransformer &transformer, @@ -20271,13 +21158,13 @@ void PEGTransformerFactory::InitializeQuantifierMinTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQuantifierMinTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto number_literal = TransformNumberLiteral(transformer, list_pr.GetChild(0)); auto result = TransformQuantifierMin(transformer, std::move(number_literal)); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeQuantifierMaxTrampoline(PEGTransformer &transformer, @@ -20285,13 +21172,13 @@ void PEGTransformerFactory::InitializeQuantifierMaxTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQuantifierMaxTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto number_literal = TransformNumberLiteral(transformer, list_pr.GetChild(1)); auto result = TransformQuantifierMax(transformer, std::move(number_literal)); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeQuantifierExactTrampoline(PEGTransformer &transformer, @@ -20299,13 +21186,13 @@ void PEGTransformerFactory::InitializeQuantifierExactTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQuantifierExactTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto number_literal = TransformNumberLiteral(transformer, list_pr.GetChild(0)); auto result = TransformQuantifierExact(transformer, std::move(number_literal)); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSubsetClauseTrampoline(PEGTransformer &transformer, @@ -20320,7 +21207,7 @@ void PEGTransformerFactory::InitializeSubsetClauseTrampoline(PEGTransformer &tra } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSubsetClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(1)); @@ -20330,7 +21217,7 @@ PEGTransformerFactory::FinalizeSubsetClauseTrampoline(PEGTransformer &transforme subset_element.push_back(process.TakeResult(i)); } auto result = TransformSubsetClause(transformer, std::move(subset_element)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSubsetElementTrampoline(PEGTransformer &transformer, @@ -20346,7 +21233,7 @@ void PEGTransformerFactory::InitializeSubsetElementTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("ColLabelOrString"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSubsetElementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -20358,7 +21245,7 @@ PEGTransformerFactory::FinalizeSubsetElementTrampoline(PEGTransformer &transform col_label_or_string_1.push_back(process.TakeResult(i)); } auto result = TransformSubsetElement(transformer, col_label_or_string, col_label_or_string_1); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeDefineClauseTrampoline(PEGTransformer &transformer, @@ -20373,7 +21260,7 @@ void PEGTransformerFactory::InitializeDefineClauseTrampoline(PEGTransformer &tra } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDefineClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(1)); @@ -20383,7 +21270,7 @@ PEGTransformerFactory::FinalizeDefineClauseTrampoline(PEGTransformer &transforme define_element.push_back(process.TakeResult>(i)); } auto result = TransformDefineClause(transformer, std::move(define_element)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeDefineElementTrampoline(PEGTransformer &transformer, @@ -20394,13 +21281,13 @@ void PEGTransformerFactory::InitializeDefineElementTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("ColLabelOrString"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDefineElementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_label_or_string = process.TakeResult(0); auto expression = process.TakeResult>(1); auto result = TransformDefineElement(transformer, col_label_or_string, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeMergeIntoStatementTrampoline(PEGTransformer &transformer, @@ -20428,7 +21315,7 @@ void PEGTransformerFactory::InitializeMergeIntoStatementTrampoline(PEGTransforme } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMergeIntoStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -20453,7 +21340,7 @@ PEGTransformerFactory::FinalizeMergeIntoStatementTrampoline(PEGTransformer &tran auto result = TransformMergeIntoStatement(transformer, std::move(with_clause), std::move(target_opt_alias), std::move(merge_into_using_clause), std::move(join_qualifier), std::move(merge_match), std::move(returning_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeMergeIntoUsingClauseTrampoline(PEGTransformer &transformer, @@ -20463,12 +21350,12 @@ void PEGTransformerFactory::InitializeMergeIntoUsingClauseTrampoline(PEGTransfor process.PushChild({transformer.GetRule("TableRef"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMergeIntoUsingClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto table_ref = process.TakeResult>(0); auto result = TransformMergeIntoUsingClause(transformer, std::move(table_ref)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeMergeMatchTrampoline(PEGTransformer &transformer, @@ -20480,15 +21367,15 @@ void PEGTransformerFactory::InitializeMergeMatchTrampoline(PEGTransformer &trans auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMergeMatchTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>>(0); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeMatchedClauseTrampoline(PEGTransformer &transformer, @@ -20502,7 +21389,7 @@ void PEGTransformerFactory::InitializeMatchedClauseTrampoline(PEGTransformer &tr } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMatchedClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional> and_expression {}; @@ -20511,7 +21398,7 @@ PEGTransformerFactory::FinalizeMatchedClauseTrampoline(PEGTransformer &transform } auto matched_clause_action = process.TakeResult>(1); auto result = TransformMatchedClause(transformer, std::move(and_expression), std::move(matched_clause_action)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeMatchedClauseActionTrampoline(PEGTransformer &transformer, @@ -20523,16 +21410,16 @@ void PEGTransformerFactory::InitializeMatchedClauseActionTrampoline(PEGTransform auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMatchedClauseActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUpdateMatchClauseTrampoline(PEGTransformer &transformer, @@ -20545,7 +21432,7 @@ void PEGTransformerFactory::InitializeUpdateMatchClauseTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUpdateMatchClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional> update_match_info {}; @@ -20553,7 +21440,7 @@ PEGTransformerFactory::FinalizeUpdateMatchClauseTrampoline(PEGTransformer &trans update_match_info = process.TakeResult>(0); } auto result = TransformUpdateMatchClause(transformer, std::move(update_match_info)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUpdateMatchInfoTrampoline(PEGTransformer &transformer, @@ -20565,16 +21452,16 @@ void PEGTransformerFactory::InitializeUpdateMatchInfoTrampoline(PEGTransformer & auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUpdateMatchInfoTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUpdateMatchSetActionTrampoline(PEGTransformer &transformer, @@ -20584,12 +21471,12 @@ void PEGTransformerFactory::InitializeUpdateMatchSetActionTrampoline(PEGTransfor process.PushChild({transformer.GetRule("UpdateMatchSetClause"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUpdateMatchSetActionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto update_match_set_clause = process.TakeResult>(0); auto result = TransformUpdateMatchSetAction(transformer, std::move(update_match_set_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUpdateByNameOrPositionTrampoline(PEGTransformer &transformer, @@ -20599,12 +21486,12 @@ void PEGTransformerFactory::InitializeUpdateByNameOrPositionTrampoline(PEGTransf process.PushChild({transformer.GetRule("ByNameOrPosition"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUpdateByNameOrPositionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto by_name_or_position = process.TakeResult(0); auto result = TransformUpdateByNameOrPosition(transformer, by_name_or_position); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDeleteMatchClauseTrampoline(PEGTransformer &transformer, @@ -20612,11 +21499,11 @@ void PEGTransformerFactory::InitializeDeleteMatchClauseTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDeleteMatchClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformDeleteMatchClause(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeInsertMatchClauseTrampoline(PEGTransformer &transformer, @@ -20629,7 +21516,7 @@ void PEGTransformerFactory::InitializeInsertMatchClauseTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInsertMatchClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional> insert_match_info {}; @@ -20637,7 +21524,7 @@ PEGTransformerFactory::FinalizeInsertMatchClauseTrampoline(PEGTransformer &trans insert_match_info = process.TakeResult>(0); } auto result = TransformInsertMatchClause(transformer, std::move(insert_match_info)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeInsertMatchInfoTrampoline(PEGTransformer &transformer, @@ -20649,16 +21536,16 @@ void PEGTransformerFactory::InitializeInsertMatchInfoTrampoline(PEGTransformer & auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInsertMatchInfoTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeInsertDefaultValuesTrampoline(PEGTransformer &transformer, @@ -20666,11 +21553,11 @@ void PEGTransformerFactory::InitializeInsertDefaultValuesTrampoline(PEGTransform process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInsertDefaultValuesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformInsertDefaultValues(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeInsertByNameOrPositionTrampoline(PEGTransformer &transformer, @@ -20683,7 +21570,7 @@ void PEGTransformerFactory::InitializeInsertByNameOrPositionTrampoline(PEGTransf } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInsertByNameOrPositionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -20695,7 +21582,7 @@ PEGTransformerFactory::FinalizeInsertByNameOrPositionTrampoline(PEGTransformer & auto &has_result_opt = list_pr.GetChild(1).Cast(); has_result = has_result_opt.HasResult(); auto result = TransformInsertByNameOrPosition(transformer, by_name_or_position, has_result); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeInsertValuesListTrampoline(PEGTransformer &transformer, @@ -20714,7 +21601,7 @@ void PEGTransformerFactory::InitializeInsertValuesListTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInsertValuesListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -20729,7 +21616,7 @@ PEGTransformerFactory::FinalizeInsertValuesListTrampoline(PEGTransformer &transf expression.push_back(process.TakeResult>(i)); } auto result = TransformInsertValuesList(transformer, insert_column_list, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeDoNothingMatchClauseTrampoline(PEGTransformer &transformer, @@ -20737,11 +21624,11 @@ void PEGTransformerFactory::InitializeDoNothingMatchClauseTrampoline(PEGTransfor process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDoNothingMatchClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformDoNothingMatchClause(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeErrorMatchClauseTrampoline(PEGTransformer &transformer, @@ -20754,7 +21641,7 @@ void PEGTransformerFactory::InitializeErrorMatchClauseTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeErrorMatchClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional> expression {}; @@ -20762,7 +21649,7 @@ PEGTransformerFactory::FinalizeErrorMatchClauseTrampoline(PEGTransformer &transf expression = process.TakeResult>(0); } auto result = TransformErrorMatchClause(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUpdateMatchSetClauseTrampoline(PEGTransformer &transformer, @@ -20772,11 +21659,11 @@ void PEGTransformerFactory::InitializeUpdateMatchSetClauseTrampoline(PEGTransfor process.PushChild({transformer.GetRule("UpdateMatchSetInfo"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUpdateMatchSetClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUpdateMatchSetInfoTrampoline(PEGTransformer &transformer, @@ -20787,23 +21674,23 @@ void PEGTransformerFactory::InitializeUpdateMatchSetInfoTrampoline(PEGTransforme process.ReserveChildSlots(1); auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; - if (!has_transform_process && (choice_result.name == "StarSymbol")) { + if (!has_transform_process && (choice_result.Name() == "StarSymbol")) { return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUpdateMatchSetInfoTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { unique_ptr result {}; if (process.child_results[0]) { result = process.TakeResult>(0); } - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAndExpressionTrampoline(PEGTransformer &transformer, @@ -20813,12 +21700,12 @@ void PEGTransformerFactory::InitializeAndExpressionTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAndExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformAndExpression(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNotMatchedClauseTrampoline(PEGTransformer &transformer, @@ -20836,7 +21723,7 @@ void PEGTransformerFactory::InitializeNotMatchedClauseTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNotMatchedClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional by_source_or_target {}; @@ -20850,7 +21737,7 @@ PEGTransformerFactory::FinalizeNotMatchedClauseTrampoline(PEGTransformer &transf auto matched_clause_action = process.TakeResult>(2); auto result = TransformNotMatchedClause(transformer, by_source_or_target, std::move(and_expression), std::move(matched_clause_action)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeBySourceOrTargetTrampoline(PEGTransformer &transformer, @@ -20862,16 +21749,16 @@ void PEGTransformerFactory::InitializeBySourceOrTargetTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBySourceOrTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeBySourceTrampoline(PEGTransformer &transformer, @@ -20879,10 +21766,10 @@ void PEGTransformerFactory::InitializeBySourceTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeBySourceTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeBySourceTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformBySource(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeByTargetTrampoline(PEGTransformer &transformer, @@ -20890,10 +21777,10 @@ void PEGTransformerFactory::InitializeByTargetTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeByTargetTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeByTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformByTarget(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializePivotOnTrampoline(PEGTransformer &transformer, @@ -20903,11 +21790,11 @@ void PEGTransformerFactory::InitializePivotOnTrampoline(PEGTransformer &transfor process.PushChild({transformer.GetRule("PivotColumnList"), list_pr.GetChild(1)}, 0); } -unique_ptr PEGTransformerFactory::FinalizePivotOnTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizePivotOnTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto pivot_column_list = process.TakeResult>(0); auto result = TransformPivotOn(transformer, std::move(pivot_column_list)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializePivotUsingTrampoline(PEGTransformer &transformer, @@ -20917,11 +21804,11 @@ void PEGTransformerFactory::InitializePivotUsingTrampoline(PEGTransformer &trans process.PushChild({transformer.GetRule("TargetList"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePivotUsingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto target_list = process.TakeResult>>(0); auto result = TransformPivotUsing(transformer, std::move(target_list)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializePivotColumnListTrampoline(PEGTransformer &transformer, @@ -20936,7 +21823,7 @@ void PEGTransformerFactory::InitializePivotColumnListTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePivotColumnListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -20947,7 +21834,7 @@ PEGTransformerFactory::FinalizePivotColumnListTrampoline(PEGTransformer &transfo pivot_column_entry.push_back(process.TakeResult(i)); } auto result = TransformPivotColumnList(transformer, std::move(pivot_column_entry)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializePivotColumnEntryTrampoline(PEGTransformer &transformer, @@ -20959,16 +21846,16 @@ void PEGTransformerFactory::InitializePivotColumnEntryTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePivotColumnEntryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializePivotColumnExpressionTrampoline(PEGTransformer &transformer, @@ -20978,12 +21865,12 @@ void PEGTransformerFactory::InitializePivotColumnExpressionTrampoline(PEGTransfo process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePivotColumnExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformPivotColumnExpression(transformer, std::move(expression)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializePivotColumnSubqueryTrampoline(PEGTransformer &transformer, @@ -20995,14 +21882,14 @@ void PEGTransformerFactory::InitializePivotColumnSubqueryTrampoline(PEGTransform process.PushChild({transformer.GetRule("BaseExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePivotColumnSubqueryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto base_expression = process.TakeResult>(0); auto select_statement_internal = process.TakeResult>(1); auto result = TransformPivotColumnSubquery(transformer, std::move(base_expression), std::move(select_statement_internal)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeIntoNameValuesTrampoline(PEGTransformer &transformer, @@ -21013,13 +21900,13 @@ void PEGTransformerFactory::InitializeIntoNameValuesTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIntoNameValuesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id_or_string = process.TakeResult(0); auto optional_parens_name_list = process.TakeResult>(1); auto result = TransformIntoNameValues(transformer, col_id_or_string, optional_parens_name_list); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeOptionalParensNameListTrampoline(PEGTransformer &transformer, @@ -21031,16 +21918,16 @@ void PEGTransformerFactory::InitializeOptionalParensNameListTrampoline(PEGTransf auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOptionalParensNameListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeParenthesizedNameListTrampoline(PEGTransformer &transformer, @@ -21055,7 +21942,7 @@ void PEGTransformerFactory::InitializeParenthesizedNameListTrampoline(PEGTransfo } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeParenthesizedNameListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -21066,7 +21953,7 @@ PEGTransformerFactory::FinalizeParenthesizedNameListTrampoline(PEGTransformer &t col_id_or_string.push_back(process.TakeResult(i)); } auto result = TransformParenthesizedNameList(transformer, col_id_or_string); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeBareNameListTrampoline(PEGTransformer &transformer, @@ -21081,7 +21968,7 @@ void PEGTransformerFactory::InitializeBareNameListTrampoline(PEGTransformer &tra } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBareNameListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); @@ -21091,7 +21978,7 @@ PEGTransformerFactory::FinalizeBareNameListTrampoline(PEGTransformer &transforme col_id_or_string.push_back(process.TakeResult(i)); } auto result = TransformBareNameList(transformer, col_id_or_string); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeIncludeOrExcludeNullsTrampoline(PEGTransformer &transformer, @@ -21103,16 +21990,16 @@ void PEGTransformerFactory::InitializeIncludeOrExcludeNullsTrampoline(PEGTransfo auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIncludeOrExcludeNullsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeIncludeNullsTrampoline(PEGTransformer &transformer, @@ -21120,10 +22007,10 @@ void PEGTransformerFactory::InitializeIncludeNullsTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIncludeNullsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformIncludeNulls(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeExcludeNullsTrampoline(PEGTransformer &transformer, @@ -21131,10 +22018,10 @@ void PEGTransformerFactory::InitializeExcludeNullsTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExcludeNullsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformExcludeNulls(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeUnpivotHeaderTrampoline(PEGTransformer &transformer, @@ -21146,16 +22033,16 @@ void PEGTransformerFactory::InitializeUnpivotHeaderTrampoline(PEGTransformer &tr auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUnpivotHeaderTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeUnpivotHeaderSingleTrampoline(PEGTransformer &transformer, @@ -21165,12 +22052,12 @@ void PEGTransformerFactory::InitializeUnpivotHeaderSingleTrampoline(PEGTransform process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUnpivotHeaderSingleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id_or_string = process.TakeResult(0); auto result = TransformUnpivotHeaderSingle(transformer, col_id_or_string); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeUnpivotHeaderListTrampoline(PEGTransformer &transformer, @@ -21185,7 +22072,7 @@ void PEGTransformerFactory::InitializeUnpivotHeaderListTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUnpivotHeaderListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -21196,7 +22083,7 @@ PEGTransformerFactory::FinalizeUnpivotHeaderListTrampoline(PEGTransformer &trans col_id_or_string.push_back(process.TakeResult(i)); } auto result = TransformUnpivotHeaderList(transformer, col_id_or_string); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializePragmaStatementTrampoline(PEGTransformer &transformer, @@ -21206,12 +22093,12 @@ void PEGTransformerFactory::InitializePragmaStatementTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("PragmaAssignOrFunction"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePragmaStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto pragma_assign_or_function = process.TakeResult>(0); auto result = TransformPragmaStatement(transformer, std::move(pragma_assign_or_function)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializePragmaAssignOrFunctionTrampoline(PEGTransformer &transformer, @@ -21223,16 +22110,16 @@ void PEGTransformerFactory::InitializePragmaAssignOrFunctionTrampoline(PEGTransf auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePragmaAssignOrFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializePragmaAssignTrampoline(PEGTransformer &transformer, @@ -21242,13 +22129,13 @@ void PEGTransformerFactory::InitializePragmaAssignTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("VariableList"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePragmaAssignTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto setting_name = list_pr.GetChild(0).Cast().identifier; auto variable_list = process.TakeResult>>(0); auto result = TransformPragmaAssign(transformer, setting_name, std::move(variable_list)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializePragmaFunctionTrampoline(PEGTransformer &transformer, @@ -21261,7 +22148,7 @@ void PEGTransformerFactory::InitializePragmaFunctionTrampoline(PEGTransformer &t } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePragmaFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -21271,7 +22158,7 @@ PEGTransformerFactory::FinalizePragmaFunctionTrampoline(PEGTransformer &transfor pragma_parameters = process.TakeResult>>(0); } auto result = TransformPragmaFunction(transformer, pragma_name, std::move(pragma_parameters)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializePragmaParametersTrampoline(PEGTransformer &transformer, @@ -21286,7 +22173,7 @@ void PEGTransformerFactory::InitializePragmaParametersTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePragmaParametersTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -21297,32 +22184,32 @@ PEGTransformerFactory::FinalizePragmaParametersTrampoline(PEGTransformer &transf expression.push_back(process.TakeResult>(i)); } auto result = TransformPragmaParameters(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializePrepareStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("Statement"), list_pr.GetChild(4)}, 1); + process.ReserveChildSlots(3); + process.PushChild({transformer.GetRule("Statement"), list_pr.GetChild(4)}, 2); auto &type_list_opt = list_pr.GetChild(2).Cast(); if (type_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("TypeList"), type_list_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule("TypeList"), type_list_opt.GetResult()}, 1); } + process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePrepareStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto &list_pr = process.parse_result.Cast(); - auto identifier = list_pr.GetChild(1).Cast().identifier; + auto col_id_or_string = process.TakeResult(0); optional> type_list {}; - if (process.child_results[0]) { - type_list = process.TakeResult>(0); + if (process.child_results[1]) { + type_list = process.TakeResult>(1); } - auto statement = process.TakeResult>(1); - auto result = TransformPrepareStatement(transformer, identifier, type_list, std::move(statement)); - return make_uniq>>(std::move(result)); + auto statement = process.TakeResult>(2); + auto result = TransformPrepareStatement(transformer, col_id_or_string, type_list, std::move(statement)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTypeListTrampoline(PEGTransformer &transformer, @@ -21337,8 +22224,8 @@ void PEGTransformerFactory::InitializeTypeListTrampoline(PEGTransformer &transfo } } -unique_ptr PEGTransformerFactory::FinalizeTypeListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeTypeListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); @@ -21347,7 +22234,7 @@ unique_ptr PEGTransformerFactory::FinalizeTypeListTrampoli type.push_back(process.TakeResult(i)); } auto result = TransformTypeList(transformer, type); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeSelectStatementTrampoline(PEGTransformer &transformer, @@ -21357,12 +22244,12 @@ void PEGTransformerFactory::InitializeSelectStatementTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("SelectStatementInternal"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSelectStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto select_statement_internal = process.TakeResult>(0); auto result = TransformSelectStatement(transformer, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSelectSetOpChainTrampoline(PEGTransformer &transformer, @@ -21386,7 +22273,7 @@ void PEGTransformerFactory::InitializeSelectSetOpChainTrampoline(PEGTransformer process.PushChild({transformer.GetRule("IntersectChain"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSelectSetOpChainTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -21409,7 +22296,7 @@ PEGTransformerFactory::FinalizeSelectSetOpChainTrampoline(PEGTransformer &transf } auto result = TransformSelectSetOpChain(transformer, std::move(intersect_chain), std::move(select_set_op_chain_tail)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSelectSetOpChainTailTrampoline(PEGTransformer &transformer, @@ -21420,14 +22307,13 @@ void PEGTransformerFactory::InitializeSelectSetOpChainTailTrampoline(PEGTransfor process.PushChild({transformer.GetRule("SetopClause"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSelectSetOpChainTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto setop_clause = process.TakeResult>(0); auto intersect_chain = process.TakeResult>(1); auto result = TransformSelectSetOpChainTail(transformer, std::move(setop_clause), std::move(intersect_chain)); - return make_uniq, unique_ptr>>>( - std::move(result)); + return transformer.MakeResult, unique_ptr>>(std::move(result)); } void PEGTransformerFactory::InitializeIntersectChainTrampoline(PEGTransformer &transformer, @@ -21451,7 +22337,7 @@ void PEGTransformerFactory::InitializeIntersectChainTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("SelectAtom"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIntersectChainTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -21473,7 +22359,7 @@ PEGTransformerFactory::FinalizeIntersectChainTrampoline(PEGTransformer &transfor intersect_chain_tail = std::move(intersect_chain_tail_value); } auto result = TransformIntersectChain(transformer, std::move(select_atom), std::move(intersect_chain_tail)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeIntersectChainTailTrampoline(PEGTransformer &transformer, @@ -21484,14 +22370,13 @@ void PEGTransformerFactory::InitializeIntersectChainTailTrampoline(PEGTransforme process.PushChild({transformer.GetRule("SetIntersectClause"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeIntersectChainTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto set_intersect_clause = process.TakeResult>(0); auto select_atom = process.TakeResult>(1); auto result = TransformIntersectChainTail(transformer, std::move(set_intersect_clause), std::move(select_atom)); - return make_uniq, unique_ptr>>>( - std::move(result)); + return transformer.MakeResult, unique_ptr>>(std::move(result)); } void PEGTransformerFactory::InitializeSetIntersectClauseTrampoline(PEGTransformer &transformer, @@ -21504,7 +22389,7 @@ void PEGTransformerFactory::InitializeSetIntersectClauseTrampoline(PEGTransforme } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSetIntersectClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional distinct_or_all {}; @@ -21512,7 +22397,7 @@ PEGTransformerFactory::FinalizeSetIntersectClauseTrampoline(PEGTransformer &tran distinct_or_all = process.TakeResult(0); } auto result = TransformSetIntersectClause(transformer, distinct_or_all); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSelectAtomTrampoline(PEGTransformer &transformer, @@ -21524,15 +22409,15 @@ void PEGTransformerFactory::InitializeSelectAtomTrampoline(PEGTransformer &trans auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSelectAtomTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSelectParensTrampoline(PEGTransformer &transformer, @@ -21543,11 +22428,11 @@ void PEGTransformerFactory::InitializeSelectParensTrampoline(PEGTransformer &tra 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSelectParensTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto select_statement_internal = process.TakeResult>(0); auto result = TransformSelectParens(transformer, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSetopClauseTrampoline(PEGTransformer &transformer, @@ -21561,7 +22446,7 @@ void PEGTransformerFactory::InitializeSetopClauseTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("SetopType"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSetopClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto setop_type = process.TakeResult(0); @@ -21573,7 +22458,7 @@ PEGTransformerFactory::FinalizeSetopClauseTrampoline(PEGTransformer &transformer auto &has_result_opt = list_pr.GetChild(2).Cast(); has_result = has_result_opt.HasResult(); auto result = TransformSetopClause(transformer, setop_type, distinct_or_all, has_result); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSetopTypeTrampoline(PEGTransformer &transformer, @@ -21585,15 +22470,15 @@ void PEGTransformerFactory::InitializeSetopTypeTrampoline(PEGTransformer &transf auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeSetopTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeSetopTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSetopUnionTrampoline(PEGTransformer &transformer, @@ -21601,10 +22486,10 @@ void PEGTransformerFactory::InitializeSetopUnionTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSetopUnionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformSetopUnion(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSetopExceptTrampoline(PEGTransformer &transformer, @@ -21612,10 +22497,10 @@ void PEGTransformerFactory::InitializeSetopExceptTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSetopExceptTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformSetopExcept(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSelectStatementTypeTrampoline(PEGTransformer &transformer, @@ -21627,16 +22512,16 @@ void PEGTransformerFactory::InitializeSelectStatementTypeTrampoline(PEGTransform auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSelectStatementTypeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeResultModifiersTrampoline(PEGTransformer &transformer, @@ -21653,7 +22538,7 @@ void PEGTransformerFactory::InitializeResultModifiersTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeResultModifiersTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional> order_by_clause {}; @@ -21665,7 +22550,7 @@ PEGTransformerFactory::FinalizeResultModifiersTrampoline(PEGTransformer &transfo limit_offset = process.TakeResult>(1); } auto result = TransformResultModifiers(transformer, std::move(order_by_clause), std::move(limit_offset)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeLimitOffsetTrampoline(PEGTransformer &transformer, @@ -21677,15 +22562,15 @@ void PEGTransformerFactory::InitializeLimitOffsetTrampoline(PEGTransformer &tran auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLimitOffsetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeLimitOffsetClauseTrampoline(PEGTransformer &transformer, @@ -21699,7 +22584,7 @@ void PEGTransformerFactory::InitializeLimitOffsetClauseTrampoline(PEGTransformer process.PushChild({transformer.GetRule("LimitClause"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLimitOffsetClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto limit_clause = process.TakeResult(0); @@ -21708,7 +22593,7 @@ PEGTransformerFactory::FinalizeLimitOffsetClauseTrampoline(PEGTransformer &trans offset_clause = process.TakeResult(1); } auto result = TransformLimitOffsetClause(transformer, std::move(limit_clause), std::move(offset_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeOffsetLimitClauseTrampoline(PEGTransformer &transformer, @@ -21722,7 +22607,7 @@ void PEGTransformerFactory::InitializeOffsetLimitClauseTrampoline(PEGTransformer process.PushChild({transformer.GetRule("OffsetClause"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOffsetLimitClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto offset_clause = process.TakeResult(0); @@ -21731,7 +22616,7 @@ PEGTransformerFactory::FinalizeOffsetLimitClauseTrampoline(PEGTransformer &trans limit_clause = process.TakeResult(1); } auto result = TransformOffsetLimitClause(transformer, std::move(offset_clause), std::move(limit_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeOffsetFetchClauseTrampoline(PEGTransformer &transformer, @@ -21742,13 +22627,30 @@ void PEGTransformerFactory::InitializeOffsetFetchClauseTrampoline(PEGTransformer process.PushChild({transformer.GetRule("OffsetClause"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOffsetFetchClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto offset_clause = process.TakeResult(0); auto fetch_clause = process.TakeResult(1); auto result = TransformOffsetFetchClause(transformer, std::move(offset_clause), std::move(fetch_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); +} + +void PEGTransformerFactory::InitializeFetchOffsetClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(2); + process.PushChild({transformer.GetRule("OffsetClause"), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule("FetchClause"), list_pr.GetChild(0)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeFetchOffsetClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto fetch_clause = process.TakeResult(0); + auto offset_clause = process.TakeResult(1); + auto result = TransformFetchOffsetClause(transformer, std::move(fetch_clause), std::move(offset_clause)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeFetchOnlyClauseTrampoline(PEGTransformer &transformer, @@ -21758,12 +22660,12 @@ void PEGTransformerFactory::InitializeFetchOnlyClauseTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("FetchClause"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFetchOnlyClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto fetch_clause = process.TakeResult(0); auto result = TransformFetchOnlyClause(transformer, std::move(fetch_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTableStatementTrampoline(PEGTransformer &transformer, @@ -21773,12 +22675,12 @@ void PEGTransformerFactory::InitializeTableStatementTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTableStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto base_table_name = process.TakeResult>(0); auto result = TransformTableStatement(transformer, std::move(base_table_name)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeOptionalParensSimpleSelectTrampoline(PEGTransformer &transformer, @@ -21790,16 +22692,16 @@ void PEGTransformerFactory::InitializeOptionalParensSimpleSelectTrampoline(PEGTr auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOptionalParensSimpleSelectTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSimpleSelectParensTrampoline(PEGTransformer &transformer, @@ -21809,12 +22711,12 @@ void PEGTransformerFactory::InitializeSimpleSelectParensTrampoline(PEGTransforme process.PushChild({transformer.GetRule("SimpleSelect"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSimpleSelectParensTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto simple_select = process.TakeResult>(0); auto result = TransformSimpleSelectParens(transformer, std::move(simple_select)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSelectFromTrampoline(PEGTransformer &transformer, @@ -21826,15 +22728,15 @@ void PEGTransformerFactory::InitializeSelectFromTrampoline(PEGTransformer &trans auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSelectFromTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSelectFromClauseTrampoline(PEGTransformer &transformer, @@ -21848,7 +22750,7 @@ void PEGTransformerFactory::InitializeSelectFromClauseTrampoline(PEGTransformer process.PushChild({transformer.GetRule("SelectClause"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSelectFromClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto select_clause = process.TakeResult>(0); @@ -21857,7 +22759,7 @@ PEGTransformerFactory::FinalizeSelectFromClauseTrampoline(PEGTransformer &transf from_clause = process.TakeResult>(1); } auto result = TransformSelectFromClause(transformer, std::move(select_clause), std::move(from_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeFromSelectClauseTrampoline(PEGTransformer &transformer, @@ -21871,7 +22773,7 @@ void PEGTransformerFactory::InitializeFromSelectClauseTrampoline(PEGTransformer process.PushChild({transformer.GetRule("FromClause"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFromSelectClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto from_clause = process.TakeResult>(0); @@ -21880,7 +22782,7 @@ PEGTransformerFactory::FinalizeFromSelectClauseTrampoline(PEGTransformer &transf select_clause = process.TakeResult>(1); } auto result = TransformFromSelectClause(transformer, std::move(from_clause), std::move(select_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeWithStatementTrampoline(PEGTransformer &transformer, @@ -21896,20 +22798,20 @@ void PEGTransformerFactory::InitializeWithStatementTrampoline(PEGTransformer &tr if (using_key_opt.HasResult()) { process.PushChild({transformer.GetRule("UsingKey"), using_key_opt.GetResult()}, 2); } - auto &insert_column_list_opt = list_pr.GetChild(1).Cast(); - if (insert_column_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("InsertColumnList"), insert_column_list_opt.GetResult()}, 1); + auto &column_aliases_opt = list_pr.GetChild(1).Cast(); + if (column_aliases_opt.HasResult()) { + process.PushChild({transformer.GetRule("ColumnAliases"), column_aliases_opt.GetResult()}, 1); } process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWithStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id_or_string = process.TakeResult(0); - optional> insert_column_list {}; + optional> column_aliases {}; if (process.child_results[1]) { - insert_column_list = process.TakeResult>(1); + column_aliases = process.TakeResult>(1); } optional>> using_key {}; if (process.child_results[2]) { @@ -21920,9 +22822,9 @@ PEGTransformerFactory::FinalizeWithStatementTrampoline(PEGTransformer &transform materialized = process.TakeResult(3); } auto cte_body = process.TakeResult>(4); - auto result = TransformWithStatement(transformer, col_id_or_string, insert_column_list, std::move(using_key), + auto result = TransformWithStatement(transformer, col_id_or_string, column_aliases, std::move(using_key), materialized, std::move(cte_body)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeCTEBodyTrampoline(PEGTransformer &transformer, @@ -21934,15 +22836,15 @@ void PEGTransformerFactory::InitializeCTEBodyTrampoline(PEGTransformer &transfor auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr PEGTransformerFactory::FinalizeCTEBodyTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeCTEBodyTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCTESelectBodyTrampoline(PEGTransformer &transformer, @@ -21953,12 +22855,12 @@ void PEGTransformerFactory::InitializeCTESelectBodyTrampoline(PEGTransformer &tr 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCTESelectBodyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto select_statement_internal = process.TakeResult>(0); auto result = TransformCTESelectBody(transformer, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCTEDMLBodyTrampoline(PEGTransformer &transformer, @@ -21968,11 +22870,11 @@ void PEGTransformerFactory::InitializeCTEDMLBodyTrampoline(PEGTransformer &trans process.PushChild({transformer.GetRule("Statement"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCTEDMLBodyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto statement = process.TakeResult>(0); auto result = TransformCTEDMLBody(transformer, std::move(statement)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUsingKeyTrampoline(PEGTransformer &transformer, @@ -21982,11 +22884,11 @@ void PEGTransformerFactory::InitializeUsingKeyTrampoline(PEGTransformer &transfo process.PushChild({transformer.GetRule("TargetList"), ExtractResultFromParens(list_pr.GetChild(2))}, 0); } -unique_ptr PEGTransformerFactory::FinalizeUsingKeyTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeUsingKeyTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto target_list = process.TakeResult>>(0); auto result = TransformUsingKey(transformer, std::move(target_list)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeMaterializedTrampoline(PEGTransformer &transformer, @@ -21994,14 +22896,14 @@ void PEGTransformerFactory::InitializeMaterializedTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeMaterializedTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); bool has_result {}; auto &has_result_opt = list_pr.GetChild(0).Cast(); has_result = has_result_opt.HasResult(); auto result = TransformMaterialized(transformer, has_result); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSelectClauseTrampoline(PEGTransformer &transformer, @@ -22018,7 +22920,7 @@ void PEGTransformerFactory::InitializeSelectClauseTrampoline(PEGTransformer &tra } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSelectClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional distinct_clause {}; if (process.child_results[0]) { @@ -22029,7 +22931,7 @@ PEGTransformerFactory::FinalizeSelectClauseTrampoline(PEGTransformer &transforme target_list = process.TakeResult>>(1); } auto result = TransformSelectClause(transformer, std::move(distinct_clause), std::move(target_list)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTargetListTrampoline(PEGTransformer &transformer, @@ -22044,7 +22946,7 @@ void PEGTransformerFactory::InitializeTargetListTrampoline(PEGTransformer &trans } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTargetListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); @@ -22054,7 +22956,7 @@ PEGTransformerFactory::FinalizeTargetListTrampoline(PEGTransformer &transformer, aliased_expression.push_back(process.TakeResult>(i)); } auto result = TransformTargetList(transformer, std::move(aliased_expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeColumnAliasesTrampoline(PEGTransformer &transformer, @@ -22069,7 +22971,7 @@ void PEGTransformerFactory::InitializeColumnAliasesTrampoline(PEGTransformer &tr } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColumnAliasesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -22080,7 +22982,7 @@ PEGTransformerFactory::FinalizeColumnAliasesTrampoline(PEGTransformer &transform col_id_or_string.push_back(process.TakeResult(i)); } auto result = TransformColumnAliases(transformer, col_id_or_string); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeDistinctClauseTrampoline(PEGTransformer &transformer, @@ -22092,16 +22994,16 @@ void PEGTransformerFactory::InitializeDistinctClauseTrampoline(PEGTransformer &t auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDistinctClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeDistinctAllTrampoline(PEGTransformer &transformer, @@ -22109,10 +23011,10 @@ void PEGTransformerFactory::InitializeDistinctAllTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDistinctAllTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformDistinctAll(transformer); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeDistinctOnTrampoline(PEGTransformer &transformer, @@ -22125,14 +23027,14 @@ void PEGTransformerFactory::InitializeDistinctOnTrampoline(PEGTransformer &trans } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDistinctOnTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional>> distinct_on_targets {}; if (process.child_results[0]) { distinct_on_targets = process.TakeResult>>(0); } auto result = TransformDistinctOn(transformer, std::move(distinct_on_targets)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeDistinctOnTargetsTrampoline(PEGTransformer &transformer, @@ -22147,7 +23049,7 @@ void PEGTransformerFactory::InitializeDistinctOnTargetsTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDistinctOnTargetsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -22158,7 +23060,7 @@ PEGTransformerFactory::FinalizeDistinctOnTargetsTrampoline(PEGTransformer &trans expression.push_back(process.TakeResult>(i)); } auto result = TransformDistinctOnTargets(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeInnerTableRefTrampoline(PEGTransformer &transformer, @@ -22170,22 +23072,26 @@ void PEGTransformerFactory::InitializeInnerTableRefTrampoline(PEGTransformer &tr auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeInnerTableRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTableSubqueryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(4); + process.ReserveChildSlots(5); + auto &sample_clause_opt = list_pr.GetChild(4).Cast(); + if (sample_clause_opt.HasResult()) { + process.PushChild({transformer.GetRule("SampleClause"), sample_clause_opt.GetResult()}, 4); + } auto &table_alias_opt = list_pr.GetChild(3).Cast(); if (table_alias_opt.HasResult()) { process.PushChild({transformer.GetRule("TableAlias"), table_alias_opt.GetResult()}, 3); @@ -22201,7 +23107,7 @@ void PEGTransformerFactory::InitializeTableSubqueryTrampoline(PEGTransformer &tr } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTableSubqueryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional table_alias_colon {}; @@ -22217,9 +23123,13 @@ PEGTransformerFactory::FinalizeTableSubqueryTrampoline(PEGTransformer &transform if (process.child_results[3]) { table_alias = process.TakeResult(3); } - auto result = - TransformTableSubquery(transformer, table_alias_colon, lateral, std::move(subquery_reference), table_alias); - return make_uniq>>(std::move(result)); + optional> sample_clause {}; + if (process.child_results[4]) { + sample_clause = process.TakeResult>(4); + } + auto result = TransformTableSubquery(transformer, table_alias_colon, lateral, std::move(subquery_reference), + table_alias, std::move(sample_clause)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeBaseTableRefTrampoline(PEGTransformer &transformer, @@ -22245,7 +23155,7 @@ void PEGTransformerFactory::InitializeBaseTableRefTrampoline(PEGTransformer &tra } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBaseTableRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional table_alias_colon {}; if (process.child_results[0]) { @@ -22266,7 +23176,7 @@ PEGTransformerFactory::FinalizeBaseTableRefTrampoline(PEGTransformer &transforme } auto result = TransformBaseTableRef(transformer, table_alias_colon, std::move(base_table_name), table_alias, std::move(at_clause), std::move(sample_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTableAliasColonTrampoline(PEGTransformer &transformer, @@ -22276,12 +23186,12 @@ void PEGTransformerFactory::InitializeTableAliasColonTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTableAliasColonTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id_or_string = process.TakeResult(0); auto result = TransformTableAliasColon(transformer, col_id_or_string); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeValuesRefTrampoline(PEGTransformer &transformer, @@ -22299,8 +23209,8 @@ void PEGTransformerFactory::InitializeValuesRefTrampoline(PEGTransformer &transf } } -unique_ptr -PEGTransformerFactory::FinalizeValuesRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeValuesRefTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { optional table_alias_colon {}; if (process.child_results[0]) { table_alias_colon = process.TakeResult(0); @@ -22311,7 +23221,7 @@ PEGTransformerFactory::FinalizeValuesRefTrampoline(PEGTransformer &transformer, table_alias = process.TakeResult(2); } auto result = TransformValuesRef(transformer, table_alias_colon, std::move(values_clause), table_alias); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeParensTableRefTrampoline(PEGTransformer &transformer, @@ -22333,7 +23243,7 @@ void PEGTransformerFactory::InitializeParensTableRefTrampoline(PEGTransformer &t } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeParensTableRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional table_alias_colon {}; @@ -22351,7 +23261,7 @@ PEGTransformerFactory::FinalizeParensTableRefTrampoline(PEGTransformer &transfor } auto result = TransformParensTableRef(transformer, table_alias_colon, std::move(table_ref), table_alias, std::move(sample_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeJoinOrPivotTrampoline(PEGTransformer &transformer, @@ -22363,15 +23273,15 @@ void PEGTransformerFactory::InitializeJoinOrPivotTrampoline(PEGTransformer &tran auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeJoinOrPivotTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTablePivotClauseTrampoline(PEGTransformer &transformer, @@ -22385,7 +23295,7 @@ void PEGTransformerFactory::InitializeTablePivotClauseTrampoline(PEGTransformer process.PushChild({transformer.GetRule("TablePivotClauseBody"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTablePivotClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto table_pivot_clause_body = process.TakeResult>(0); @@ -22394,7 +23304,7 @@ PEGTransformerFactory::FinalizeTablePivotClauseTrampoline(PEGTransformer &transf table_alias = process.TakeResult(1); } auto result = TransformTablePivotClause(transformer, std::move(table_pivot_clause_body), table_alias); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTablePivotClauseBodyTrampoline(PEGTransformer &transformer, @@ -22416,7 +23326,7 @@ void PEGTransformerFactory::InitializeTablePivotClauseBodyTrampoline(PEGTransfor process.PushChild({transformer.GetRule("TargetList"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTablePivotClauseBodyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -22434,7 +23344,7 @@ PEGTransformerFactory::FinalizeTablePivotClauseBodyTrampoline(PEGTransformer &tr } auto result = TransformTablePivotClauseBody(transformer, std::move(target_list), std::move(pivot_value_list), pivot_group_by_list); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializePivotGroupByListTrampoline(PEGTransformer &transformer, @@ -22444,12 +23354,12 @@ void PEGTransformerFactory::InitializePivotGroupByListTrampoline(PEGTransformer process.PushChild({transformer.GetRule("OptionalParensNameList"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePivotGroupByListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto optional_parens_name_list = process.TakeResult>(0); auto result = TransformPivotGroupByList(transformer, optional_parens_name_list); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeTableUnpivotClauseTrampoline(PEGTransformer &transformer, @@ -22467,7 +23377,7 @@ void PEGTransformerFactory::InitializeTableUnpivotClauseTrampoline(PEGTransforme } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTableUnpivotClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional include_or_exclude_nulls {}; @@ -22481,7 +23391,7 @@ PEGTransformerFactory::FinalizeTableUnpivotClauseTrampoline(PEGTransformer &tran } auto result = TransformTableUnpivotClause(transformer, include_or_exclude_nulls, std::move(table_unpivot_clause_body), table_alias); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTableUnpivotClauseBodyTrampoline(PEGTransformer &transformer, @@ -22498,7 +23408,7 @@ void PEGTransformerFactory::InitializeTableUnpivotClauseBodyTrampoline(PEGTransf process.PushChild({transformer.GetRule("UnpivotHeader"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTableUnpivotClauseBodyTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -22511,7 +23421,7 @@ PEGTransformerFactory::FinalizeTableUnpivotClauseBodyTrampoline(PEGTransformer & unpivot_value_list.push_back(process.TakeResult(i)); } auto result = TransformTableUnpivotClauseBody(transformer, unpivot_header, std::move(unpivot_value_list)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializePivotHeaderTrampoline(PEGTransformer &transformer, @@ -22521,11 +23431,11 @@ void PEGTransformerFactory::InitializePivotHeaderTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("BaseExpression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePivotHeaderTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto base_expression = process.TakeResult>(0); auto result = TransformPivotHeader(transformer, std::move(base_expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializePivotValueListTrampoline(PEGTransformer &transformer, @@ -22536,13 +23446,13 @@ void PEGTransformerFactory::InitializePivotValueListTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("PivotHeader"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePivotValueListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto pivot_header = process.TakeResult>(0); auto pivot_value_target = process.TakeResult(1); auto result = TransformPivotValueList(transformer, std::move(pivot_header), std::move(pivot_value_target)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializePivotValueTargetTrampoline(PEGTransformer &transformer, @@ -22554,16 +23464,16 @@ void PEGTransformerFactory::InitializePivotValueTargetTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePivotValueTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializePivotEnumTargetTrampoline(PEGTransformer &transformer, @@ -22571,13 +23481,13 @@ void PEGTransformerFactory::InitializePivotEnumTargetTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePivotEnumTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto identifier = list_pr.GetChild(0).Cast().identifier; auto result = TransformPivotEnumTarget(transformer, identifier); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializePivotListTargetTrampoline(PEGTransformer &transformer, @@ -22587,12 +23497,12 @@ void PEGTransformerFactory::InitializePivotListTargetTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("PivotTargetList"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePivotListTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto pivot_target_list = process.TakeResult>(0); auto result = TransformPivotListTarget(transformer, std::move(pivot_target_list)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeUnpivotValueListTrampoline(PEGTransformer &transformer, @@ -22603,13 +23513,13 @@ void PEGTransformerFactory::InitializeUnpivotValueListTrampoline(PEGTransformer process.PushChild({transformer.GetRule("UnpivotHeader"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUnpivotValueListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto unpivot_header = process.TakeResult>(0); auto unpivot_target_list = process.TakeResult>(1); auto result = TransformUnpivotValueList(transformer, unpivot_header, std::move(unpivot_target_list)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializePivotTargetListTrampoline(PEGTransformer &transformer, @@ -22619,12 +23529,12 @@ void PEGTransformerFactory::InitializePivotTargetListTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("TargetList"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePivotTargetListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto target_list = process.TakeResult>>(0); auto result = TransformPivotTargetList(transformer, std::move(target_list)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUnpivotTargetListTrampoline(PEGTransformer &transformer, @@ -22634,12 +23544,12 @@ void PEGTransformerFactory::InitializeUnpivotTargetListTrampoline(PEGTransformer process.PushChild({transformer.GetRule("TargetList"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUnpivotTargetListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto target_list = process.TakeResult>>(0); auto result = TransformUnpivotTargetList(transformer, std::move(target_list)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeLateralTrampoline(PEGTransformer &transformer, @@ -22647,10 +23557,10 @@ void PEGTransformerFactory::InitializeLateralTrampoline(PEGTransformer &transfor process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeLateralTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeLateralTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformLateral(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeBaseTableNameTrampoline(PEGTransformer &transformer, @@ -22662,16 +23572,16 @@ void PEGTransformerFactory::InitializeBaseTableNameTrampoline(PEGTransformer &tr auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBaseTableNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUnqualifiedBaseTableNameTrampoline(PEGTransformer &transformer, @@ -22679,13 +23589,13 @@ void PEGTransformerFactory::InitializeUnqualifiedBaseTableNameTrampoline(PEGTran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUnqualifiedBaseTableNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto table_name = list_pr.GetChild(0).Cast().identifier; auto result = TransformUnqualifiedBaseTableName(transformer, table_name); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeQualifiedTableNameTrampoline(PEGTransformer &transformer, @@ -22697,16 +23607,16 @@ void PEGTransformerFactory::InitializeQualifiedTableNameTrampoline(PEGTransforme auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQualifiedTableNameTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSchemaReservedTableTrampoline(PEGTransformer &transformer, @@ -22716,14 +23626,14 @@ void PEGTransformerFactory::InitializeSchemaReservedTableTrampoline(PEGTransform process.PushChild({transformer.GetRule("SchemaQualification"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSchemaReservedTableTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto schema_qualification = process.TakeResult(0); auto reserved_table_name = list_pr.GetChild(1).Cast().identifier; auto result = TransformSchemaReservedTable(transformer, schema_qualification, reserved_table_name); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCatalogReservedSchemaTableTrampoline(PEGTransformer &transformer, @@ -22741,7 +23651,7 @@ void PEGTransformerFactory::InitializeCatalogReservedSchemaTableTrampoline(PEGTr process.PushChild({transformer.GetRule("CatalogQualification"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCatalogReservedSchemaTableTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -22756,7 +23666,7 @@ PEGTransformerFactory::FinalizeCatalogReservedSchemaTableTrampoline(PEGTransform auto reserved_table_name = list_pr.GetChild(2).Cast().identifier; auto result = TransformCatalogReservedSchemaTable(transformer, catalog_qualification, reserved_schema_qualification, reserved_table_name); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTableFunctionTrampoline(PEGTransformer &transformer, @@ -22768,22 +23678,26 @@ void PEGTransformerFactory::InitializeTableFunctionTrampoline(PEGTransformer &tr auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTableFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTableFunctionLateralOptTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(5); + process.ReserveChildSlots(6); + auto &sample_clause_opt = list_pr.GetChild(5).Cast(); + if (sample_clause_opt.HasResult()) { + process.PushChild({transformer.GetRule("SampleClause"), sample_clause_opt.GetResult()}, 5); + } auto &table_alias_opt = list_pr.GetChild(4).Cast(); if (table_alias_opt.HasResult()) { process.PushChild({transformer.GetRule("TableAlias"), table_alias_opt.GetResult()}, 4); @@ -22800,7 +23714,7 @@ void PEGTransformerFactory::InitializeTableFunctionLateralOptTrampoline(PEGTrans } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTableFunctionLateralOptTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional lateral {}; @@ -22817,9 +23731,14 @@ PEGTransformerFactory::FinalizeTableFunctionLateralOptTrampoline(PEGTransformer if (process.child_results[4]) { table_alias = process.TakeResult(4); } + optional> sample_clause {}; + if (process.child_results[5]) { + sample_clause = process.TakeResult>(5); + } auto result = TransformTableFunctionLateralOpt(transformer, lateral, qualified_table_function, - std::move(table_function_arguments), with_ordinality, table_alias); - return make_uniq>>(std::move(result)); + std::move(table_function_arguments), with_ordinality, table_alias, + std::move(sample_clause)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeTableFunctionAliasColonTrampoline(PEGTransformer &transformer, @@ -22839,7 +23758,7 @@ void PEGTransformerFactory::InitializeTableFunctionAliasColonTrampoline(PEGTrans process.PushChild({transformer.GetRule("TableAliasColon"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTableFunctionAliasColonTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto table_alias_colon = process.TakeResult(0); @@ -22856,7 +23775,7 @@ PEGTransformerFactory::FinalizeTableFunctionAliasColonTrampoline(PEGTransformer auto result = TransformTableFunctionAliasColon(transformer, table_alias_colon, qualified_table_function, std::move(table_function_arguments), with_ordinality, std::move(sample_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeWithOrdinalityTrampoline(PEGTransformer &transformer, @@ -22864,11 +23783,11 @@ void PEGTransformerFactory::InitializeWithOrdinalityTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWithOrdinalityTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformWithOrdinality(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeQualifiedTableFunctionTrampoline(PEGTransformer &transformer, @@ -22895,7 +23814,7 @@ void PEGTransformerFactory::InitializeQualifiedTableFunctionTrampoline(PEGTransf } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQualifiedTableFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -22921,7 +23840,7 @@ PEGTransformerFactory::FinalizeQualifiedTableFunctionTrampoline(PEGTransformer & auto table_function_name = list_pr.GetChild(2).Cast().identifier; auto result = TransformQualifiedTableFunction(transformer, catalog_qualification, schema_qualification, table_function_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTableFunctionArgumentsTrampoline(PEGTransformer &transformer, @@ -22942,7 +23861,7 @@ void PEGTransformerFactory::InitializeTableFunctionArgumentsTrampoline(PEGTransf } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTableFunctionArgumentsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -22962,7 +23881,7 @@ PEGTransformerFactory::FinalizeTableFunctionArgumentsTrampoline(PEGTransformer & function_argument = std::move(function_argument_value); } auto result = TransformTableFunctionArguments(transformer, std::move(function_argument)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeFunctionArgumentTrampoline(PEGTransformer &transformer, @@ -22974,16 +23893,16 @@ void PEGTransformerFactory::InitializeFunctionArgumentTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFunctionArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeNamedFunctionArgumentTrampoline(PEGTransformer &transformer, @@ -22993,12 +23912,12 @@ void PEGTransformerFactory::InitializeNamedFunctionArgumentTrampoline(PEGTransfo process.PushChild({transformer.GetRule("NamedParameter"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNamedFunctionArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto named_parameter = process.TakeResult(0); auto result = TransformNamedFunctionArgument(transformer, std::move(named_parameter)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializePositionalFunctionArgumentTrampoline(PEGTransformer &transformer, @@ -23008,12 +23927,12 @@ void PEGTransformerFactory::InitializePositionalFunctionArgumentTrampoline(PEGTr process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePositionalFunctionArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformPositionalFunctionArgument(transformer, std::move(expression)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeNamedParameterTrampoline(PEGTransformer &transformer, @@ -23028,7 +23947,7 @@ void PEGTransformerFactory::InitializeNamedParameterTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("TypeFuncName"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNamedParameterTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto type_func_name = process.TakeResult(0); @@ -23038,7 +23957,7 @@ PEGTransformerFactory::FinalizeNamedParameterTrampoline(PEGTransformer &transfor } auto expression = process.TakeResult>(2); auto result = TransformNamedParameter(transformer, type_func_name, type, std::move(expression)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeTableAliasTrampoline(PEGTransformer &transformer, @@ -23050,15 +23969,15 @@ void PEGTransformerFactory::InitializeTableAliasTrampoline(PEGTransformer &trans auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTableAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTableAliasAsTrampoline(PEGTransformer &transformer, @@ -23072,7 +23991,7 @@ void PEGTransformerFactory::InitializeTableAliasAsTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("IdentifierOrStringLiteral"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTableAliasAsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto identifier_or_string_literal = process.TakeResult(0); optional> column_aliases {}; @@ -23080,7 +23999,7 @@ PEGTransformerFactory::FinalizeTableAliasAsTrampoline(PEGTransformer &transforme column_aliases = process.TakeResult>(1); } auto result = TransformTableAliasAs(transformer, identifier_or_string_literal, column_aliases); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTableAliasWithoutAsTrampoline(PEGTransformer &transformer, @@ -23093,7 +24012,7 @@ void PEGTransformerFactory::InitializeTableAliasWithoutAsTrampoline(PEGTransform } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTableAliasWithoutAsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -23103,7 +24022,7 @@ PEGTransformerFactory::FinalizeTableAliasWithoutAsTrampoline(PEGTransformer &tra column_aliases = process.TakeResult>(0); } auto result = TransformTableAliasWithoutAs(transformer, identifier, column_aliases); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeAtClauseTrampoline(PEGTransformer &transformer, @@ -23113,11 +24032,11 @@ void PEGTransformerFactory::InitializeAtClauseTrampoline(PEGTransformer &transfo process.PushChild({transformer.GetRule("AtSpecifier"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } -unique_ptr PEGTransformerFactory::FinalizeAtClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeAtClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto at_specifier = process.TakeResult>(0); auto result = TransformAtClause(transformer, std::move(at_specifier)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAtSpecifierTrampoline(PEGTransformer &transformer, @@ -23128,12 +24047,12 @@ void PEGTransformerFactory::InitializeAtSpecifierTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("AtUnit"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAtSpecifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto at_unit = process.TakeResult(0); auto expression = process.TakeResult>(1); auto result = TransformAtSpecifier(transformer, at_unit, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAtUnitTrampoline(PEGTransformer &transformer, @@ -23149,13 +24068,13 @@ void PEGTransformerFactory::InitializeAtUnitTrampoline(PEGTransformer &transform return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr PEGTransformerFactory::FinalizeAtUnitTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeAtUnitTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { string result; if (process.child_results[0]) { result = process.TakeResult(0); @@ -23174,7 +24093,7 @@ unique_ptr PEGTransformerFactory::FinalizeAtUnitTrampoline result = transformer.Transform(choice_result); } } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeVersionAtUnitTrampoline(PEGTransformer &transformer, @@ -23182,11 +24101,11 @@ void PEGTransformerFactory::InitializeVersionAtUnitTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeVersionAtUnitTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformVersionAtUnit(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeTimestampAtUnitTrampoline(PEGTransformer &transformer, @@ -23194,11 +24113,11 @@ void PEGTransformerFactory::InitializeTimestampAtUnitTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTimestampAtUnitTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformTimestampAtUnit(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeJoinClauseTrampoline(PEGTransformer &transformer, @@ -23210,15 +24129,15 @@ void PEGTransformerFactory::InitializeJoinClauseTrampoline(PEGTransformer &trans auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeJoinClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNearestJoinClauseTrampoline(PEGTransformer &transformer, @@ -23230,16 +24149,16 @@ void PEGTransformerFactory::InitializeNearestJoinClauseTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNearestJoinClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNearestJoinAliasedTrampoline(PEGTransformer &transformer, @@ -23259,7 +24178,7 @@ void PEGTransformerFactory::InitializeNearestJoinAliasedTrampoline(PEGTransforme } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNearestJoinAliasedTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -23281,7 +24200,7 @@ PEGTransformerFactory::FinalizeNearestJoinAliasedTrampoline(PEGTransformer &tran auto expression = process.TakeResult>(4); auto result = TransformNearestJoinAliased(transformer, join_type, std::move(table_ref), approx_or_exact, std::move(number_literal), distance_or_similarity, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNearestJoinBareTrampoline(PEGTransformer &transformer, @@ -23301,7 +24220,7 @@ void PEGTransformerFactory::InitializeNearestJoinBareTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNearestJoinBareTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -23323,7 +24242,7 @@ PEGTransformerFactory::FinalizeNearestJoinBareTrampoline(PEGTransformer &transfo auto expression = process.TakeResult>(4); auto result = TransformNearestJoinBare(transformer, join_type, std::move(nearest_bare_table_ref), approx_or_exact, std::move(number_literal), distance_or_similarity, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNearestBareTableRefTrampoline(PEGTransformer &transformer, @@ -23335,16 +24254,16 @@ void PEGTransformerFactory::InitializeNearestBareTableRefTrampoline(PEGTransform auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNearestBareTableRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNearestValuesRefTrampoline(PEGTransformer &transformer, @@ -23354,12 +24273,12 @@ void PEGTransformerFactory::InitializeNearestValuesRefTrampoline(PEGTransformer process.PushChild({transformer.GetRule("ValuesClause"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNearestValuesRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto values_clause = process.TakeResult>(0); auto result = TransformNearestValuesRef(transformer, std::move(values_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNearestTableFunctionTrampoline(PEGTransformer &transformer, @@ -23378,7 +24297,7 @@ void PEGTransformerFactory::InitializeNearestTableFunctionTrampoline(PEGTransfor } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNearestTableFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional lateral {}; @@ -23393,7 +24312,7 @@ PEGTransformerFactory::FinalizeNearestTableFunctionTrampoline(PEGTransformer &tr } auto result = TransformNearestTableFunction(transformer, lateral, qualified_table_function, std::move(table_function_arguments), with_ordinality); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNearestTableSubqueryTrampoline(PEGTransformer &transformer, @@ -23407,7 +24326,7 @@ void PEGTransformerFactory::InitializeNearestTableSubqueryTrampoline(PEGTransfor } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNearestTableSubqueryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional lateral {}; @@ -23416,7 +24335,7 @@ PEGTransformerFactory::FinalizeNearestTableSubqueryTrampoline(PEGTransformer &tr } auto subquery_reference = process.TakeResult>(1); auto result = TransformNearestTableSubquery(transformer, lateral, std::move(subquery_reference)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNearestBaseTableRefTrampoline(PEGTransformer &transformer, @@ -23434,7 +24353,7 @@ void PEGTransformerFactory::InitializeNearestBaseTableRefTrampoline(PEGTransform process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNearestBaseTableRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto base_table_name = process.TakeResult>(0); @@ -23448,7 +24367,7 @@ PEGTransformerFactory::FinalizeNearestBaseTableRefTrampoline(PEGTransformer &tra } auto result = TransformNearestBaseTableRef(transformer, std::move(base_table_name), std::move(at_clause), std::move(sample_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeNearestParensTableRefTrampoline(PEGTransformer &transformer, @@ -23462,7 +24381,7 @@ void PEGTransformerFactory::InitializeNearestParensTableRefTrampoline(PEGTransfo process.PushChild({transformer.GetRule("TableRef"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNearestParensTableRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto table_ref = process.TakeResult>(0); @@ -23471,7 +24390,7 @@ PEGTransformerFactory::FinalizeNearestParensTableRefTrampoline(PEGTransformer &t sample_clause = process.TakeResult>(1); } auto result = TransformNearestParensTableRef(transformer, std::move(table_ref), std::move(sample_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeApproxOrExactTrampoline(PEGTransformer &transformer, @@ -23483,16 +24402,16 @@ void PEGTransformerFactory::InitializeApproxOrExactTrampoline(PEGTransformer &tr auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeApproxOrExactTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeNearestApproxTrampoline(PEGTransformer &transformer, @@ -23500,11 +24419,11 @@ void PEGTransformerFactory::InitializeNearestApproxTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNearestApproxTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformNearestApprox(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeNearestExactTrampoline(PEGTransformer &transformer, @@ -23512,10 +24431,10 @@ void PEGTransformerFactory::InitializeNearestExactTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNearestExactTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformNearestExact(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDistanceOrSimilarityTrampoline(PEGTransformer &transformer, @@ -23527,16 +24446,16 @@ void PEGTransformerFactory::InitializeDistanceOrSimilarityTrampoline(PEGTransfor auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDistanceOrSimilarityTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeNearestDistanceTrampoline(PEGTransformer &transformer, @@ -23544,11 +24463,11 @@ void PEGTransformerFactory::InitializeNearestDistanceTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNearestDistanceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformNearestDistance(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeNearestSimilarityTrampoline(PEGTransformer &transformer, @@ -23556,11 +24475,11 @@ void PEGTransformerFactory::InitializeNearestSimilarityTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNearestSimilarityTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformNearestSimilarity(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeRegularJoinClauseTrampoline(PEGTransformer &transformer, @@ -23579,7 +24498,7 @@ void PEGTransformerFactory::InitializeRegularJoinClauseTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRegularJoinClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional asof {}; @@ -23594,7 +24513,7 @@ PEGTransformerFactory::FinalizeRegularJoinClauseTrampoline(PEGTransformer &trans auto join_qualifier = process.TakeResult(3); auto result = TransformRegularJoinClause(transformer, asof, join_type, std::move(table_ref), std::move(join_qualifier)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeJoinByClauseTrampoline(PEGTransformer &transformer, @@ -23608,23 +24527,23 @@ void PEGTransformerFactory::InitializeJoinByClauseTrampoline(PEGTransformer &tra 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeJoinByClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_label = process.TakeResult(0); auto table_ref = process.TakeResult>(1); auto join_qualifier = process.TakeResult(2); auto result = TransformJoinByClause(transformer, col_label, std::move(table_ref), std::move(join_qualifier)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeAsofTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeAsofTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeAsofTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformAsof(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeJoinWithoutOnClauseTrampoline(PEGTransformer &transformer, @@ -23635,13 +24554,13 @@ void PEGTransformerFactory::InitializeJoinWithoutOnClauseTrampoline(PEGTransform process.PushChild({transformer.GetRule("JoinPrefix"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeJoinWithoutOnClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto join_prefix = process.TakeResult(0); auto inner_table_ref = process.TakeResult>(1); auto result = TransformJoinWithoutOnClause(transformer, join_prefix, std::move(inner_table_ref)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeJoinQualifierTrampoline(PEGTransformer &transformer, @@ -23653,16 +24572,16 @@ void PEGTransformerFactory::InitializeJoinQualifierTrampoline(PEGTransformer &tr auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeJoinQualifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeOnClauseTrampoline(PEGTransformer &transformer, @@ -23672,28 +24591,76 @@ void PEGTransformerFactory::InitializeOnClauseTrampoline(PEGTransformer &transfo process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); } -unique_ptr PEGTransformerFactory::FinalizeOnClauseTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeOnClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformOnClause(transformer, std::move(expression)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeUsingClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - process.ReserveChildSlots(0); + auto &list_pr = process.parse_result.Cast(); + auto list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(1))); + auto dynamic_child_count = list_items.size(); + process.ReserveChildSlots(1 + dynamic_child_count - 1); + for (idx_t i = list_items.size(); i > 0; i--) { + auto child_idx = i - 1; + process.PushChild({transformer.GetRule("UsingColumnName"), list_items[child_idx].get()}, 0 + child_idx); + } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUsingClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - vector column_name; - auto column_name_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(1))); - for (auto &column_name_item : column_name_items) { - column_name.push_back(column_name_item.get().Cast().identifier); + auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(1))); + auto dynamic_child_count = dynamic_list_items.size(); + vector using_column_name; + for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { + using_column_name.push_back(process.TakeResult(i)); + } + auto result = TransformUsingClause(transformer, using_column_name); + return transformer.MakeResult(std::move(result)); +} + +void PEGTransformerFactory::InitializeUsingColumnNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + auto &choice_pr = list_pr.Child(0); + auto &choice_result = choice_pr.GetResult(); + process.ReserveChildSlots(1); + auto child_rule = choice_result.GetRule(); + auto has_transform_process = child_rule && child_rule->transform_process; + if (choice_result.Name().empty() || choice_result.type == ParseResultType::IDENTIFIER || + choice_result.type == ParseResultType::KEYWORD || choice_result.type == ParseResultType::STRING) { + return; + } + if (!has_transform_process) { + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); + } + process.PushChild({*child_rule, choice_result}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeUsingColumnNameTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + Identifier result; + if (process.child_results[0]) { + result = process.TakeResult(0); + } else { + auto &list_pr = process.parse_result.Cast(); + auto &choice_result = list_pr.Child(0).GetResult(); + if (choice_result.type == ParseResultType::IDENTIFIER) { + result = choice_result.Cast().identifier; + } else if (choice_result.type == ParseResultType::KEYWORD) { + result = Identifier(choice_result.Cast().keyword); + } else if (choice_result.type == ParseResultType::STRING) { + result = Identifier(choice_result.Cast().result); + } else { + result = Identifier(TransformIdentifierOrKeyword(transformer, choice_result)); + } } - auto result = TransformUsingClause(transformer, column_name); - return make_uniq>(std::move(result)); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeJoinTypeTrampoline(PEGTransformer &transformer, @@ -23705,15 +24672,15 @@ void PEGTransformerFactory::InitializeJoinTypeTrampoline(PEGTransformer &transfo auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr PEGTransformerFactory::FinalizeJoinTypeTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeJoinTypeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeJoinPrefixTrampoline(PEGTransformer &transformer, @@ -23725,15 +24692,15 @@ void PEGTransformerFactory::InitializeJoinPrefixTrampoline(PEGTransformer &trans auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeJoinPrefixTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCrossJoinPrefixTrampoline(PEGTransformer &transformer, @@ -23741,11 +24708,11 @@ void PEGTransformerFactory::InitializeCrossJoinPrefixTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCrossJoinPrefixTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCrossJoinPrefix(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeNaturalJoinPrefixTrampoline(PEGTransformer &transformer, @@ -23758,7 +24725,7 @@ void PEGTransformerFactory::InitializeNaturalJoinPrefixTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNaturalJoinPrefixTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional join_type {}; @@ -23766,7 +24733,7 @@ PEGTransformerFactory::FinalizeNaturalJoinPrefixTrampoline(PEGTransformer &trans join_type = process.TakeResult(0); } auto result = TransformNaturalJoinPrefix(transformer, join_type); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializePositionalJoinPrefixTrampoline(PEGTransformer &transformer, @@ -23774,11 +24741,11 @@ void PEGTransformerFactory::InitializePositionalJoinPrefixTrampoline(PEGTransfor process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePositionalJoinPrefixTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformPositionalJoinPrefix(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeFullJoinTrampoline(PEGTransformer &transformer, @@ -23786,14 +24753,14 @@ void PEGTransformerFactory::InitializeFullJoinTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeFullJoinTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeFullJoinTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); bool has_result {}; auto &has_result_opt = list_pr.GetChild(1).Cast(); has_result = has_result_opt.HasResult(); auto result = TransformFullJoin(transformer, has_result); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeLeftJoinTrampoline(PEGTransformer &transformer, @@ -23801,14 +24768,14 @@ void PEGTransformerFactory::InitializeLeftJoinTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeLeftJoinTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeLeftJoinTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); bool has_result {}; auto &has_result_opt = list_pr.GetChild(1).Cast(); has_result = has_result_opt.HasResult(); auto result = TransformLeftJoin(transformer, has_result); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeRightJoinTrampoline(PEGTransformer &transformer, @@ -23816,14 +24783,14 @@ void PEGTransformerFactory::InitializeRightJoinTrampoline(PEGTransformer &transf process.ReserveChildSlots(0); } -unique_ptr -PEGTransformerFactory::FinalizeRightJoinTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeRightJoinTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); bool has_result {}; auto &has_result_opt = list_pr.GetChild(1).Cast(); has_result = has_result_opt.HasResult(); auto result = TransformRightJoin(transformer, has_result); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSemiJoinTrampoline(PEGTransformer &transformer, @@ -23831,10 +24798,10 @@ void PEGTransformerFactory::InitializeSemiJoinTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeSemiJoinTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeSemiJoinTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformSemiJoin(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeAntiJoinTrampoline(PEGTransformer &transformer, @@ -23842,10 +24809,10 @@ void PEGTransformerFactory::InitializeAntiJoinTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeAntiJoinTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeAntiJoinTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformAntiJoin(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeInnerJoinTrampoline(PEGTransformer &transformer, @@ -23853,10 +24820,10 @@ void PEGTransformerFactory::InitializeInnerJoinTrampoline(PEGTransformer &transf process.ReserveChildSlots(0); } -unique_ptr -PEGTransformerFactory::FinalizeInnerJoinTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeInnerJoinTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformInnerJoin(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeFromClauseTrampoline(PEGTransformer &transformer, @@ -23871,7 +24838,7 @@ void PEGTransformerFactory::InitializeFromClauseTrampoline(PEGTransformer &trans } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFromClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(1)); @@ -23881,7 +24848,7 @@ PEGTransformerFactory::FinalizeFromClauseTrampoline(PEGTransformer &transformer, table_ref.push_back(process.TakeResult>(i)); } auto result = TransformFromClause(transformer, std::move(table_ref)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeWhereClauseTrampoline(PEGTransformer &transformer, @@ -23891,11 +24858,11 @@ void PEGTransformerFactory::InitializeWhereClauseTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWhereClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformWhereClause(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeGroupByClauseTrampoline(PEGTransformer &transformer, @@ -23905,12 +24872,12 @@ void PEGTransformerFactory::InitializeGroupByClauseTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("GroupByExpressions"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGroupByClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto group_by_expressions = process.TakeResult(0); auto result = TransformGroupByClause(transformer, std::move(group_by_expressions)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeHavingClauseTrampoline(PEGTransformer &transformer, @@ -23920,11 +24887,11 @@ void PEGTransformerFactory::InitializeHavingClauseTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeHavingClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformHavingClause(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeQualifyClauseTrampoline(PEGTransformer &transformer, @@ -23934,12 +24901,12 @@ void PEGTransformerFactory::InitializeQualifyClauseTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeQualifyClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformQualifyClause(transformer, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSampleClauseTrampoline(PEGTransformer &transformer, @@ -23949,11 +24916,26 @@ void PEGTransformerFactory::InitializeSampleClauseTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("SampleEntry"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSampleClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto sample_entry = process.TakeResult>(0); auto result = TransformSampleClause(transformer, std::move(sample_entry)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); +} + +void PEGTransformerFactory::InitializeSelectSampleClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + process.PushChild({transformer.GetRule("SampleEntry"), list_pr.GetChild(1)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeSelectSampleClauseTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto sample_entry = process.TakeResult>(0); + auto result = TransformSelectSampleClause(transformer, std::move(sample_entry)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeWindowClauseTrampoline(PEGTransformer &transformer, @@ -23968,7 +24950,7 @@ void PEGTransformerFactory::InitializeWindowClauseTrampoline(PEGTransformer &tra } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWindowClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(1)); @@ -23978,7 +24960,7 @@ PEGTransformerFactory::FinalizeWindowClauseTrampoline(PEGTransformer &transforme window_definition.push_back(process.TakeResult>(i)); } auto result = TransformWindowClause(transformer, std::move(window_definition)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeSampleEntryTrampoline(PEGTransformer &transformer, @@ -23990,15 +24972,15 @@ void PEGTransformerFactory::InitializeSampleEntryTrampoline(PEGTransformer &tran auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSampleEntryTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSampleEntryCountTrampoline(PEGTransformer &transformer, @@ -24013,7 +24995,7 @@ void PEGTransformerFactory::InitializeSampleEntryCountTrampoline(PEGTransformer process.PushChild({transformer.GetRule("SampleCount"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSampleEntryCountTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto sample_count = process.TakeResult>(0); @@ -24022,7 +25004,7 @@ PEGTransformerFactory::FinalizeSampleEntryCountTrampoline(PEGTransformer &transf sample_properties = process.TakeResult>(1); } auto result = TransformSampleEntryCount(transformer, std::move(sample_count), sample_properties); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSampleEntryFunctionTrampoline(PEGTransformer &transformer, @@ -24040,7 +25022,7 @@ void PEGTransformerFactory::InitializeSampleEntryFunctionTrampoline(PEGTransform } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSampleEntryFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional sample_function {}; @@ -24054,7 +25036,7 @@ PEGTransformerFactory::FinalizeSampleEntryFunctionTrampoline(PEGTransformer &tra } auto result = TransformSampleEntryFunction(transformer, sample_function, std::move(sample_count), repeatable_sample); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSampleFunctionTrampoline(PEGTransformer &transformer, @@ -24064,12 +25046,12 @@ void PEGTransformerFactory::InitializeSampleFunctionTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSampleFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id = process.TakeResult(0); auto result = TransformSampleFunction(transformer, col_id); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSamplePropertiesTrampoline(PEGTransformer &transformer, @@ -24084,7 +25066,7 @@ void PEGTransformerFactory::InitializeSamplePropertiesTrampoline(PEGTransformer process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSamplePropertiesTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id = process.TakeResult(0); @@ -24093,7 +25075,7 @@ PEGTransformerFactory::FinalizeSamplePropertiesTrampoline(PEGTransformer &transf sample_seed = process.TakeResult(1); } auto result = TransformSampleProperties(transformer, col_id, sample_seed); - return make_uniq>>(result); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeRepeatableSampleTrampoline(PEGTransformer &transformer, @@ -24103,12 +25085,12 @@ void PEGTransformerFactory::InitializeRepeatableSampleTrampoline(PEGTransformer process.PushChild({transformer.GetRule("SampleSeed"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRepeatableSampleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto sample_seed = process.TakeResult(0); auto result = TransformRepeatableSample(transformer, sample_seed); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSampleSeedTrampoline(PEGTransformer &transformer, @@ -24116,12 +25098,12 @@ void PEGTransformerFactory::InitializeSampleSeedTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSampleSeedTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto number_literal = TransformNumberLiteral(transformer, list_pr.GetChild(0)); auto result = TransformSampleSeed(transformer, std::move(number_literal)); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSampleCountTrampoline(PEGTransformer &transformer, @@ -24135,7 +25117,7 @@ void PEGTransformerFactory::InitializeSampleCountTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("SampleValue"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSampleCountTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto sample_value = process.TakeResult>(0); optional sample_unit {}; @@ -24143,7 +25125,7 @@ PEGTransformerFactory::FinalizeSampleCountTrampoline(PEGTransformer &transformer sample_unit = process.TakeResult(1); } auto result = TransformSampleCount(transformer, std::move(sample_value), sample_unit); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSampleValueTrampoline(PEGTransformer &transformer, @@ -24155,15 +25137,15 @@ void PEGTransformerFactory::InitializeSampleValueTrampoline(PEGTransformer &tran auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSampleValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSampleUnitTrampoline(PEGTransformer &transformer, @@ -24175,15 +25157,15 @@ void PEGTransformerFactory::InitializeSampleUnitTrampoline(PEGTransformer &trans auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSampleUnitTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSamplePercentageTrampoline(PEGTransformer &transformer, @@ -24191,11 +25173,11 @@ void PEGTransformerFactory::InitializeSamplePercentageTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSamplePercentageTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformSamplePercentage(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSampleRowsTrampoline(PEGTransformer &transformer, @@ -24203,10 +25185,10 @@ void PEGTransformerFactory::InitializeSampleRowsTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSampleRowsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformSampleRows(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeGroupByExpressionsTrampoline(PEGTransformer &transformer, @@ -24218,16 +25200,16 @@ void PEGTransformerFactory::InitializeGroupByExpressionsTrampoline(PEGTransforme auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGroupByExpressionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeGroupByAllTrampoline(PEGTransformer &transformer, @@ -24235,10 +25217,10 @@ void PEGTransformerFactory::InitializeGroupByAllTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGroupByAllTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformGroupByAll(transformer); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeGroupByListTrampoline(PEGTransformer &transformer, @@ -24253,7 +25235,7 @@ void PEGTransformerFactory::InitializeGroupByListTrampoline(PEGTransformer &tran } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGroupByListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); @@ -24263,7 +25245,7 @@ PEGTransformerFactory::FinalizeGroupByListTrampoline(PEGTransformer &transformer group_by_expression.push_back(process.TakeResult(i)); } auto result = TransformGroupByList(transformer, std::move(group_by_expression)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeGroupByExpressionTrampoline(PEGTransformer &transformer, @@ -24275,16 +25257,16 @@ void PEGTransformerFactory::InitializeGroupByExpressionTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGroupByExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeGroupByBaseExpressionTrampoline(PEGTransformer &transformer, @@ -24294,12 +25276,12 @@ void PEGTransformerFactory::InitializeGroupByBaseExpressionTrampoline(PEGTransfo process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGroupByBaseExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformGroupByBaseExpression(transformer, std::move(expression)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeEmptyGroupingItemTrampoline(PEGTransformer &transformer, @@ -24307,11 +25289,11 @@ void PEGTransformerFactory::InitializeEmptyGroupingItemTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeEmptyGroupingItemTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformEmptyGroupingItem(transformer); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeCubeOrRollupClauseTrampoline(PEGTransformer &transformer, @@ -24333,7 +25315,7 @@ void PEGTransformerFactory::InitializeCubeOrRollupClauseTrampoline(PEGTransforme process.PushChild({transformer.GetRule("CubeOrRollup"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCubeOrRollupClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -24354,7 +25336,7 @@ PEGTransformerFactory::FinalizeCubeOrRollupClauseTrampoline(PEGTransformer &tran expression = std::move(expression_value); } auto result = TransformCubeOrRollupClause(transformer, cube_or_rollup, std::move(expression)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeCubeOrRollupTrampoline(PEGTransformer &transformer, @@ -24370,12 +25352,12 @@ void PEGTransformerFactory::InitializeCubeOrRollupTrampoline(PEGTransformer &tra return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCubeOrRollupTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { string result; if (process.child_results[0]) { @@ -24395,7 +25377,7 @@ PEGTransformerFactory::FinalizeCubeOrRollupTrampoline(PEGTransformer &transforme result = transformer.Transform(choice_result); } } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCubeKeywordTrampoline(PEGTransformer &transformer, @@ -24403,10 +25385,10 @@ void PEGTransformerFactory::InitializeCubeKeywordTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCubeKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformCubeKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeRollupKeywordTrampoline(PEGTransformer &transformer, @@ -24414,11 +25396,11 @@ void PEGTransformerFactory::InitializeRollupKeywordTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRollupKeywordTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformRollupKeyword(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeGroupingSetsClauseTrampoline(PEGTransformer &transformer, @@ -24433,7 +25415,7 @@ void PEGTransformerFactory::InitializeGroupingSetsClauseTrampoline(PEGTransforme } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGroupingSetsClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -24444,7 +25426,7 @@ PEGTransformerFactory::FinalizeGroupingSetsClauseTrampoline(PEGTransformer &tran group_by_expression.push_back(process.TakeResult(i)); } auto result = TransformGroupingSetsClause(transformer, std::move(group_by_expression)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeSubqueryReferenceTrampoline(PEGTransformer &transformer, @@ -24455,12 +25437,12 @@ void PEGTransformerFactory::InitializeSubqueryReferenceTrampoline(PEGTransformer 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSubqueryReferenceTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto select_statement_internal = process.TakeResult>(0); auto result = TransformSubqueryReference(transformer, std::move(select_statement_internal)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeOrderByExpressionTrampoline(PEGTransformer &transformer, @@ -24478,7 +25460,7 @@ void PEGTransformerFactory::InitializeOrderByExpressionTrampoline(PEGTransformer process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOrderByExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); @@ -24491,7 +25473,7 @@ PEGTransformerFactory::FinalizeOrderByExpressionTrampoline(PEGTransformer &trans nulls_first_or_last = process.TakeResult(2); } auto result = TransformOrderByExpression(transformer, std::move(expression), desc_or_asc, nulls_first_or_last); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeDescOrAscTrampoline(PEGTransformer &transformer, @@ -24503,15 +25485,15 @@ void PEGTransformerFactory::InitializeDescOrAscTrampoline(PEGTransformer &transf auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeDescOrAscTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeDescOrAscTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDescendingOrderTrampoline(PEGTransformer &transformer, @@ -24519,11 +25501,11 @@ void PEGTransformerFactory::InitializeDescendingOrderTrampoline(PEGTransformer & process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDescendingOrderTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformDescendingOrder(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeAscendingOrderTrampoline(PEGTransformer &transformer, @@ -24531,11 +25513,11 @@ void PEGTransformerFactory::InitializeAscendingOrderTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAscendingOrderTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformAscendingOrder(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeNullsFirstOrLastTrampoline(PEGTransformer &transformer, @@ -24547,16 +25529,16 @@ void PEGTransformerFactory::InitializeNullsFirstOrLastTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNullsFirstOrLastTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeNullsFirstTrampoline(PEGTransformer &transformer, @@ -24564,10 +25546,10 @@ void PEGTransformerFactory::InitializeNullsFirstTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeNullsFirstTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformNullsFirst(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeNullsLastTrampoline(PEGTransformer &transformer, @@ -24575,10 +25557,10 @@ void PEGTransformerFactory::InitializeNullsLastTrampoline(PEGTransformer &transf process.ReserveChildSlots(0); } -unique_ptr -PEGTransformerFactory::FinalizeNullsLastTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeNullsLastTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformNullsLast(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeOrderByClauseTrampoline(PEGTransformer &transformer, @@ -24588,12 +25570,12 @@ void PEGTransformerFactory::InitializeOrderByClauseTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("OrderByExpressions"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOrderByClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto order_by_expressions = process.TakeResult>(0); auto result = TransformOrderByClause(transformer, std::move(order_by_expressions)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeOrderByExpressionsTrampoline(PEGTransformer &transformer, @@ -24605,16 +25587,16 @@ void PEGTransformerFactory::InitializeOrderByExpressionsTrampoline(PEGTransforme auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOrderByExpressionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeOrderByExpressionListTrampoline(PEGTransformer &transformer, @@ -24629,7 +25611,7 @@ void PEGTransformerFactory::InitializeOrderByExpressionListTrampoline(PEGTransfo } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOrderByExpressionListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -24640,7 +25622,7 @@ PEGTransformerFactory::FinalizeOrderByExpressionListTrampoline(PEGTransformer &t order_by_expression.push_back(process.TakeResult(i)); } auto result = TransformOrderByExpressionList(transformer, std::move(order_by_expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeOrderByAllTrampoline(PEGTransformer &transformer, @@ -24657,7 +25639,7 @@ void PEGTransformerFactory::InitializeOrderByAllTrampoline(PEGTransformer &trans } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOrderByAllTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional desc_or_asc {}; if (process.child_results[0]) { @@ -24668,7 +25650,7 @@ PEGTransformerFactory::FinalizeOrderByAllTrampoline(PEGTransformer &transformer, nulls_first_or_last = process.TakeResult(1); } auto result = TransformOrderByAll(transformer, desc_or_asc, nulls_first_or_last); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeLimitClauseTrampoline(PEGTransformer &transformer, @@ -24678,11 +25660,11 @@ void PEGTransformerFactory::InitializeLimitClauseTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("LimitValue"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLimitClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto limit_value = process.TakeResult(0); auto result = TransformLimitClause(transformer, std::move(limit_value)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeOffsetClauseTrampoline(PEGTransformer &transformer, @@ -24692,11 +25674,11 @@ void PEGTransformerFactory::InitializeOffsetClauseTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("OffsetValue"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOffsetClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto offset_value = process.TakeResult(0); auto result = TransformOffsetClause(transformer, std::move(offset_value)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeOffsetValueTrampoline(PEGTransformer &transformer, @@ -24706,7 +25688,7 @@ void PEGTransformerFactory::InitializeOffsetValueTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOffsetValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto expression = process.TakeResult>(0); @@ -24714,7 +25696,7 @@ PEGTransformerFactory::FinalizeOffsetValueTrampoline(PEGTransformer &transformer auto &has_result_opt = list_pr.GetChild(1).Cast(); has_result = has_result_opt.HasResult(); auto result = TransformOffsetValue(transformer, std::move(expression), has_result); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeLimitValueTrampoline(PEGTransformer &transformer, @@ -24726,15 +25708,15 @@ void PEGTransformerFactory::InitializeLimitValueTrampoline(PEGTransformer &trans auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLimitValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeLimitAllTrampoline(PEGTransformer &transformer, @@ -24742,10 +25724,10 @@ void PEGTransformerFactory::InitializeLimitAllTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeLimitAllTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeLimitAllTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformLimitAll(transformer); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeLimitLiteralPercentTrampoline(PEGTransformer &transformer, @@ -24753,13 +25735,13 @@ void PEGTransformerFactory::InitializeLimitLiteralPercentTrampoline(PEGTransform process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLimitLiteralPercentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto number_literal = TransformNumberLiteral(transformer, list_pr.GetChild(0)); auto result = TransformLimitLiteralPercent(transformer, std::move(number_literal)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeLimitExpressionTrampoline(PEGTransformer &transformer, @@ -24769,7 +25751,7 @@ void PEGTransformerFactory::InitializeLimitExpressionTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLimitExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -24778,20 +25760,53 @@ PEGTransformerFactory::FinalizeLimitExpressionTrampoline(PEGTransformer &transfo auto &has_result_opt = list_pr.GetChild(1).Cast(); has_result = has_result_opt.HasResult(); auto result = TransformLimitExpression(transformer, std::move(expression), has_result); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeFetchClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); + auto &choice_pr = list_pr.Child(0); + auto &choice_result = choice_pr.GetResult(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("FetchValue"), list_pr.GetChild(2)}, 0); + auto child_rule = choice_result.GetRule(); + auto has_transform_process = child_rule && child_rule->transform_process; + if (!has_transform_process) { + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); + } + process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFetchClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); +} + +void PEGTransformerFactory::InitializeFetchClauseWithoutValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + process.ReserveChildSlots(0); +} + +arena_ptr +PEGTransformerFactory::FinalizeFetchClauseWithoutValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto result = TransformFetchClauseWithoutValue(transformer); + return transformer.MakeResult(std::move(result)); +} + +void PEGTransformerFactory::InitializeFetchClauseWithValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + process.PushChild({transformer.GetRule("FetchValue"), list_pr.GetChild(2)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeFetchClauseWithValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto result = process.TakeResult(0); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeFetchValueTrampoline(PEGTransformer &transformer, @@ -24801,11 +25816,11 @@ void PEGTransformerFactory::InitializeFetchValueTrampoline(PEGTransformer &trans process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeFetchValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto result = TransformFetchValue(transformer, std::move(expression)); - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } void PEGTransformerFactory::InitializeAliasedExpressionTrampoline(PEGTransformer &transformer, @@ -24817,16 +25832,16 @@ void PEGTransformerFactory::InitializeAliasedExpressionTrampoline(PEGTransformer auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeAliasedExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeColIdExpressionTrampoline(PEGTransformer &transformer, @@ -24837,13 +25852,13 @@ void PEGTransformerFactory::InitializeColIdExpressionTrampoline(PEGTransformer & process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeColIdExpressionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto col_id = process.TakeResult(0); auto expression = process.TakeResult>(1); auto result = TransformColIdExpression(transformer, col_id, std::move(expression)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeExpressionAsCollabelTrampoline(PEGTransformer &transformer, @@ -24854,13 +25869,13 @@ void PEGTransformerFactory::InitializeExpressionAsCollabelTrampoline(PEGTransfor process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExpressionAsCollabelTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto expression = process.TakeResult>(0); auto col_label_or_string = process.TakeResult(1); auto result = TransformExpressionAsCollabel(transformer, std::move(expression), col_label_or_string); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeExpressionOptIdentifierTrampoline(PEGTransformer &transformer, @@ -24870,18 +25885,18 @@ void PEGTransformerFactory::InitializeExpressionOptIdentifierTrampoline(PEGTrans process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeExpressionOptIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto expression = process.TakeResult>(0); - optional identifier {}; - auto &identifier_opt = list_pr.GetChild(1).Cast(); - if (identifier_opt.HasResult()) { - identifier = identifier_opt.GetResult().Cast().identifier; + optional bare_col_label {}; + auto &bare_col_label_opt = list_pr.GetChild(1).Cast(); + if (bare_col_label_opt.HasResult()) { + bare_col_label = bare_col_label_opt.GetResult().Cast().identifier; } - auto result = TransformExpressionOptIdentifier(transformer, std::move(expression), identifier); - return make_uniq>>(std::move(result)); + auto result = TransformExpressionOptIdentifier(transformer, std::move(expression), bare_col_label); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeValuesClauseTrampoline(PEGTransformer &transformer, @@ -24896,7 +25911,7 @@ void PEGTransformerFactory::InitializeValuesClauseTrampoline(PEGTransformer &tra } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeValuesClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(1)); @@ -24906,7 +25921,7 @@ PEGTransformerFactory::FinalizeValuesClauseTrampoline(PEGTransformer &transforme values_expressions.push_back(process.TakeResult>>(i)); } auto result = TransformValuesClause(transformer, std::move(values_expressions)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeValuesExpressionsTrampoline(PEGTransformer &transformer, @@ -24921,7 +25936,7 @@ void PEGTransformerFactory::InitializeValuesExpressionsTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeValuesExpressionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -24932,7 +25947,7 @@ PEGTransformerFactory::FinalizeValuesExpressionsTrampoline(PEGTransformer &trans expression.push_back(process.TakeResult>(i)); } auto result = TransformValuesExpressions(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeSetStatementTrampoline(PEGTransformer &transformer, @@ -24942,11 +25957,11 @@ void PEGTransformerFactory::InitializeSetStatementTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("SetAssignmentOrTimeZone"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSetStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto set_assignment_or_time_zone = process.TakeResult>(0); auto result = TransformSetStatement(transformer, std::move(set_assignment_or_time_zone)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSetAssignmentOrTimeZoneTrampoline(PEGTransformer &transformer, @@ -24958,16 +25973,16 @@ void PEGTransformerFactory::InitializeSetAssignmentOrTimeZoneTrampoline(PEGTrans auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSetAssignmentOrTimeZoneTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeResetStatementTrampoline(PEGTransformer &transformer, @@ -24977,12 +25992,12 @@ void PEGTransformerFactory::InitializeResetStatementTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("SetVariableOrSetting"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeResetStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto set_variable_or_setting = process.TakeResult(0); auto result = TransformResetStatement(transformer, set_variable_or_setting); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSetSchemaTrampoline(PEGTransformer &transformer, @@ -24990,12 +26005,12 @@ void PEGTransformerFactory::InitializeSetSchemaTrampoline(PEGTransformer &transf process.ReserveChildSlots(0); } -unique_ptr -PEGTransformerFactory::FinalizeSetSchemaTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeSetSchemaTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(1)); auto result = TransformSetSchema(transformer, string_literal); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeStandardAssignmentTrampoline(PEGTransformer &transformer, @@ -25006,13 +26021,13 @@ void PEGTransformerFactory::InitializeStandardAssignmentTrampoline(PEGTransforme process.PushChild({transformer.GetRule("SetVariableOrSetting"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeStandardAssignmentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto set_variable_or_setting = process.TakeResult(0); auto set_assignment = process.TakeResult>>(1); auto result = TransformStandardAssignment(transformer, set_variable_or_setting, std::move(set_assignment)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSetVariableOrSettingTrampoline(PEGTransformer &transformer, @@ -25024,16 +26039,16 @@ void PEGTransformerFactory::InitializeSetVariableOrSettingTrampoline(PEGTransfor auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSetVariableOrSettingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSetTimeZoneTrampoline(PEGTransformer &transformer, @@ -25043,11 +26058,11 @@ void PEGTransformerFactory::InitializeSetTimeZoneTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("ZoneValue"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSetTimeZoneTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto zone_value = process.TakeResult>(0); auto result = TransformSetTimeZone(transformer, std::move(zone_value)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeZoneValueTrampoline(PEGTransformer &transformer, @@ -25059,15 +26074,15 @@ void PEGTransformerFactory::InitializeZoneValueTrampoline(PEGTransformer &transf auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeZoneValueTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeZoneValueTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeZoneLocalTrampoline(PEGTransformer &transformer, @@ -25075,10 +26090,10 @@ void PEGTransformerFactory::InitializeZoneLocalTrampoline(PEGTransformer &transf process.ReserveChildSlots(0); } -unique_ptr -PEGTransformerFactory::FinalizeZoneLocalTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeZoneLocalTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformZoneLocal(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeZoneDefaultTrampoline(PEGTransformer &transformer, @@ -25086,10 +26101,10 @@ void PEGTransformerFactory::InitializeZoneDefaultTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeZoneDefaultTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformZoneDefault(transformer); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeZoneStringLiteralTrampoline(PEGTransformer &transformer, @@ -25097,13 +26112,13 @@ void PEGTransformerFactory::InitializeZoneStringLiteralTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeZoneStringLiteralTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(0)); auto result = TransformZoneStringLiteral(transformer, string_literal); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeZoneIdentifierTrampoline(PEGTransformer &transformer, @@ -25111,13 +26126,13 @@ void PEGTransformerFactory::InitializeZoneIdentifierTrampoline(PEGTransformer &t process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeZoneIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto identifier = list_pr.GetChild(0).Cast().identifier; auto result = TransformZoneIdentifier(transformer, identifier); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeZoneIntervalWithIntervalTrampoline(PEGTransformer &transformer, @@ -25130,7 +26145,7 @@ void PEGTransformerFactory::InitializeZoneIntervalWithIntervalTrampoline(PEGTran } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeZoneIntervalWithIntervalTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -25140,7 +26155,7 @@ PEGTransformerFactory::FinalizeZoneIntervalWithIntervalTrampoline(PEGTransformer interval = process.TakeResult(0); } auto result = TransformZoneIntervalWithInterval(transformer, string_literal, interval); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeZoneIntervalWithPrecisionTrampoline(PEGTransformer &transformer, @@ -25148,14 +26163,14 @@ void PEGTransformerFactory::InitializeZoneIntervalWithPrecisionTrampoline(PEGTra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeZoneIntervalWithPrecisionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto number_literal = TransformNumberLiteral(transformer, ExtractResultFromParens(list_pr.GetChild(1))); auto string_literal = TransformStringLiteral(transformer, list_pr.GetChild(2)); auto result = TransformZoneIntervalWithPrecision(transformer, std::move(number_literal), string_literal); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeSetSettingTrampoline(PEGTransformer &transformer, @@ -25168,7 +26183,7 @@ void PEGTransformerFactory::InitializeSetSettingTrampoline(PEGTransformer &trans } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSetSettingTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); optional setting_scope {}; @@ -25177,7 +26192,7 @@ PEGTransformerFactory::FinalizeSetSettingTrampoline(PEGTransformer &transformer, } auto setting_name = list_pr.GetChild(1).Cast().identifier; auto result = TransformSetSetting(transformer, setting_scope, setting_name); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSetVariableTrampoline(PEGTransformer &transformer, @@ -25187,13 +26202,13 @@ void PEGTransformerFactory::InitializeSetVariableTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("VariableScope"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSetVariableTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto variable_scope = process.TakeResult(0); auto identifier = list_pr.GetChild(1).Cast().identifier; auto result = TransformSetVariable(transformer, variable_scope, identifier); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeVariableScopeTrampoline(PEGTransformer &transformer, @@ -25201,11 +26216,11 @@ void PEGTransformerFactory::InitializeVariableScopeTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeVariableScopeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformVariableScope(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSettingScopeTrampoline(PEGTransformer &transformer, @@ -25217,15 +26232,15 @@ void PEGTransformerFactory::InitializeSettingScopeTrampoline(PEGTransformer &tra auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSettingScopeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeLocalScopeTrampoline(PEGTransformer &transformer, @@ -25233,10 +26248,10 @@ void PEGTransformerFactory::InitializeLocalScopeTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeLocalScopeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformLocalScope(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSessionScopeTrampoline(PEGTransformer &transformer, @@ -25244,10 +26259,10 @@ void PEGTransformerFactory::InitializeSessionScopeTrampoline(PEGTransformer &tra process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSessionScopeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformSessionScope(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeGlobalScopeTrampoline(PEGTransformer &transformer, @@ -25255,10 +26270,10 @@ void PEGTransformerFactory::InitializeGlobalScopeTrampoline(PEGTransformer &tran process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeGlobalScopeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformGlobalScope(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSetAssignmentTrampoline(PEGTransformer &transformer, @@ -25268,12 +26283,12 @@ void PEGTransformerFactory::InitializeSetAssignmentTrampoline(PEGTransformer &tr process.PushChild({transformer.GetRule("VariableList"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSetAssignmentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto variable_list = process.TakeResult>>(0); auto result = TransformSetAssignment(transformer, std::move(variable_list)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeVariableListTrampoline(PEGTransformer &transformer, @@ -25288,7 +26303,7 @@ void PEGTransformerFactory::InitializeVariableListTrampoline(PEGTransformer &tra } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeVariableListTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); @@ -25298,7 +26313,7 @@ PEGTransformerFactory::FinalizeVariableListTrampoline(PEGTransformer &transforme expression.push_back(process.TakeResult>(i)); } auto result = TransformVariableList(transformer, std::move(expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>>(std::move(result)); } void PEGTransformerFactory::InitializeTransactionStatementTrampoline(PEGTransformer &transformer, @@ -25310,16 +26325,16 @@ void PEGTransformerFactory::InitializeTransactionStatementTrampoline(PEGTransfor auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeTransactionStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeBeginTransactionTrampoline(PEGTransformer &transformer, @@ -25332,7 +26347,7 @@ void PEGTransformerFactory::InitializeBeginTransactionTrampoline(PEGTransformer } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBeginTransactionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -25344,7 +26359,7 @@ PEGTransformerFactory::FinalizeBeginTransactionTrampoline(PEGTransformer &transf read_or_write = process.TakeResult(0); } auto result = TransformBeginTransaction(transformer, has_result, read_or_write); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeRollbackTransactionTrampoline(PEGTransformer &transformer, @@ -25352,7 +26367,7 @@ void PEGTransformerFactory::InitializeRollbackTransactionTrampoline(PEGTransform process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeRollbackTransactionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -25360,7 +26375,7 @@ PEGTransformerFactory::FinalizeRollbackTransactionTrampoline(PEGTransformer &tra auto &has_result_opt = list_pr.GetChild(1).Cast(); has_result = has_result_opt.HasResult(); auto result = TransformRollbackTransaction(transformer, has_result); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeCommitTransactionTrampoline(PEGTransformer &transformer, @@ -25368,7 +26383,7 @@ void PEGTransformerFactory::InitializeCommitTransactionTrampoline(PEGTransformer process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCommitTransactionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -25376,7 +26391,7 @@ PEGTransformerFactory::FinalizeCommitTransactionTrampoline(PEGTransformer &trans auto &has_result_opt = list_pr.GetChild(1).Cast(); has_result = has_result_opt.HasResult(); auto result = TransformCommitTransaction(transformer, has_result); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeReadOrWriteTrampoline(PEGTransformer &transformer, @@ -25386,11 +26401,11 @@ void PEGTransformerFactory::InitializeReadOrWriteTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("ReadOnlyOrReadWrite"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeReadOrWriteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto read_only_or_read_write = process.TakeResult(0); auto result = TransformReadOrWrite(transformer, read_only_or_read_write); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeReadOnlyOrReadWriteTrampoline(PEGTransformer &transformer, @@ -25402,16 +26417,16 @@ void PEGTransformerFactory::InitializeReadOnlyOrReadWriteTrampoline(PEGTransform auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeReadOnlyOrReadWriteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeReadOnlyTrampoline(PEGTransformer &transformer, @@ -25419,10 +26434,10 @@ void PEGTransformerFactory::InitializeReadOnlyTrampoline(PEGTransformer &transfo process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeReadOnlyTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeReadOnlyTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformReadOnly(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeReadWriteTrampoline(PEGTransformer &transformer, @@ -25430,10 +26445,10 @@ void PEGTransformerFactory::InitializeReadWriteTrampoline(PEGTransformer &transf process.ReserveChildSlots(0); } -unique_ptr -PEGTransformerFactory::FinalizeReadWriteTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeReadWriteTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformReadWrite(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeUpdateStatementTrampoline(PEGTransformer &transformer, @@ -25452,7 +26467,7 @@ void PEGTransformerFactory::InitializeUpdateStatementTrampoline(PEGTransformer & if (from_clause_opt.HasResult()) { process.PushChild({transformer.GetRule("FromClause"), from_clause_opt.GetResult()}, 3); } - process.PushChild({transformer.GetRule("UpdateSetClause"), list_pr.GetChild(3)}, 2); + process.PushChild({transformer.GetRule("UpdateSetClauseList"), list_pr.GetChild(3)}, 2); process.PushChild({transformer.GetRule("UpdateTarget"), list_pr.GetChild(2)}, 1); auto &with_clause_opt = list_pr.GetChild(0).Cast(); if (with_clause_opt.HasResult()) { @@ -25460,7 +26475,7 @@ void PEGTransformerFactory::InitializeUpdateStatementTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUpdateStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional with_clause {}; @@ -25468,7 +26483,7 @@ PEGTransformerFactory::FinalizeUpdateStatementTrampoline(PEGTransformer &transfo with_clause = process.TakeResult(0); } auto update_target = process.TakeResult>(1); - auto update_set_clause = process.TakeResult>(2); + auto update_set_clause_list = process.TakeResult>(2); optional> from_clause {}; if (process.child_results[3]) { from_clause = process.TakeResult>(3); @@ -25482,9 +26497,9 @@ PEGTransformerFactory::FinalizeUpdateStatementTrampoline(PEGTransformer &transfo returning_clause = process.TakeResult>>(5); } auto result = TransformUpdateStatement(transformer, std::move(with_clause), std::move(update_target), - std::move(update_set_clause), std::move(from_clause), + std::move(update_set_clause_list), std::move(from_clause), std::move(where_clause), std::move(returning_clause)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUpdateTargetTrampoline(PEGTransformer &transformer, @@ -25496,15 +26511,15 @@ void PEGTransformerFactory::InitializeUpdateTargetTrampoline(PEGTransformer &tra auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUpdateTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeBaseTableSetTrampoline(PEGTransformer &transformer, @@ -25514,11 +26529,11 @@ void PEGTransformerFactory::InitializeBaseTableSetTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBaseTableSetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto base_table_name = process.TakeResult>(0); auto result = TransformBaseTableSet(transformer, std::move(base_table_name)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeBaseTableAliasSetTrampoline(PEGTransformer &transformer, @@ -25532,7 +26547,7 @@ void PEGTransformerFactory::InitializeBaseTableAliasSetTrampoline(PEGTransformer process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeBaseTableAliasSetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto base_table_name = process.TakeResult>(0); @@ -25541,7 +26556,7 @@ PEGTransformerFactory::FinalizeBaseTableAliasSetTrampoline(PEGTransformer &trans update_alias = process.TakeResult(1); } auto result = TransformBaseTableAliasSet(transformer, std::move(base_table_name), update_alias); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUpdateAliasTrampoline(PEGTransformer &transformer, @@ -25551,7 +26566,7 @@ void PEGTransformerFactory::InitializeUpdateAliasTrampoline(PEGTransformer &tran process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUpdateAliasTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); bool has_result {}; @@ -25559,7 +26574,33 @@ PEGTransformerFactory::FinalizeUpdateAliasTrampoline(PEGTransformer &transformer has_result = has_result_opt.HasResult(); auto col_id = process.TakeResult(0); auto result = TransformUpdateAlias(transformer, has_result, col_id); - return make_uniq>(result); + return transformer.MakeResult(result); +} + +void PEGTransformerFactory::InitializeUpdateSetClauseListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + auto list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); + auto dynamic_child_count = list_items.size(); + process.ReserveChildSlots(1 + dynamic_child_count - 1); + for (idx_t i = list_items.size(); i > 0; i--) { + auto child_idx = i - 1; + process.PushChild({transformer.GetRule("UpdateSetClause"), list_items[child_idx].get()}, 0 + child_idx); + } +} + +arena_ptr +PEGTransformerFactory::FinalizeUpdateSetClauseListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); + auto dynamic_child_count = dynamic_list_items.size(); + vector> update_set_clause; + for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { + update_set_clause.push_back(process.TakeResult>(i)); + } + auto result = TransformUpdateSetClauseList(transformer, std::move(update_set_clause)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUpdateSetClauseTrampoline(PEGTransformer &transformer, @@ -25571,16 +26612,16 @@ void PEGTransformerFactory::InitializeUpdateSetClauseTrampoline(PEGTransformer & auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUpdateSetClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult>(0); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUpdateSetTupleTrampoline(PEGTransformer &transformer, @@ -25590,7 +26631,7 @@ void PEGTransformerFactory::InitializeUpdateSetTupleTrampoline(PEGTransformer &t process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUpdateSetTupleTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -25601,33 +26642,7 @@ PEGTransformerFactory::FinalizeUpdateSetTupleTrampoline(PEGTransformer &transfor } auto expression = process.TakeResult>(0); auto result = TransformUpdateSetTuple(transformer, column_name, std::move(expression)); - return make_uniq>>(std::move(result)); -} - -void PEGTransformerFactory::InitializeUpdateSetElementListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { - auto &list_pr = process.parse_result.Cast(); - auto list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - auto dynamic_child_count = list_items.size(); - process.ReserveChildSlots(1 + dynamic_child_count - 1); - for (idx_t i = list_items.size(); i > 0; i--) { - auto child_idx = i - 1; - process.PushChild({transformer.GetRule("UpdateSetElement"), list_items[child_idx].get()}, 0 + child_idx); - } -} - -unique_ptr -PEGTransformerFactory::FinalizeUpdateSetElementListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { - auto &list_pr = process.parse_result.Cast(); - auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); - auto dynamic_child_count = dynamic_list_items.size(); - vector>> update_set_element; - for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { - update_set_element.push_back(process.TakeResult>>(i)); - } - auto result = TransformUpdateSetElementList(transformer, std::move(update_set_element)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUpdateSetElementTrampoline(PEGTransformer &transformer, @@ -25638,13 +26653,13 @@ void PEGTransformerFactory::InitializeUpdateSetElementTrampoline(PEGTransformer process.PushChild({transformer.GetRule("UpdateSetColumnTarget"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUpdateSetElementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto update_set_column_target = process.TakeResult(0); auto expression = process.TakeResult>(1); auto result = TransformUpdateSetElement(transformer, update_set_column_target, std::move(expression)); - return make_uniq>>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUpdateSetColumnTargetTrampoline(PEGTransformer &transformer, @@ -25666,7 +26681,7 @@ void PEGTransformerFactory::InitializeUpdateSetColumnTargetTrampoline(PEGTransfo } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUpdateSetColumnTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -25687,7 +26702,7 @@ PEGTransformerFactory::FinalizeUpdateSetColumnTargetTrampoline(PEGTransformer &t dot_identifier = std::move(dot_identifier_value); } auto result = TransformUpdateSetColumnTarget(transformer, column_name, dot_identifier); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeUseStatementTrampoline(PEGTransformer &transformer, @@ -25697,11 +26712,11 @@ void PEGTransformerFactory::InitializeUseStatementTrampoline(PEGTransformer &tra process.PushChild({transformer.GetRule("UseTarget"), list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUseStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto use_target = process.TakeResult(0); auto result = TransformUseStatement(transformer, use_target); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeUseTargetTrampoline(PEGTransformer &transformer, @@ -25713,43 +26728,105 @@ void PEGTransformerFactory::InitializeUseTargetTrampoline(PEGTransformer &transf auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr -PEGTransformerFactory::FinalizeUseTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeUseTargetTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeSchemaNameAsUseTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - process.ReserveChildSlots(0); + auto &list_pr = process.parse_result.Cast(); + auto &choice_pr = list_pr.Child(0); + auto &choice_result = choice_pr.GetResult(); + process.ReserveChildSlots(1); + auto child_rule = choice_result.GetRule(); + auto has_transform_process = child_rule && child_rule->transform_process; + if (!has_transform_process && + (choice_result.type == ParseResultType::IDENTIFIER || choice_result.type == ParseResultType::KEYWORD || + choice_result.type == ParseResultType::STRING || choice_result.type == ParseResultType::OPERATOR || + choice_result.type == ParseResultType::CHOICE || choice_result.type == ParseResultType::LIST)) { + return; + } + if (!has_transform_process) { + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); + } + process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSchemaNameAsUseTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto &list_pr = process.parse_result.Cast(); - auto schema_name = list_pr.GetChild(0).Cast().identifier; - auto result = TransformSchemaNameAsUseTarget(transformer, schema_name); - return make_uniq>(result); + string child; + if (process.child_results[0]) { + child = process.TakeResult(0); + } else { + auto &list_pr = process.parse_result.Cast(); + auto &choice_result = list_pr.Child(0).GetResult(); + if (choice_result.type == ParseResultType::IDENTIFIER) { + child = choice_result.Cast().identifier.GetIdentifierName(); + } else if (choice_result.type == ParseResultType::KEYWORD) { + child = choice_result.Cast().keyword; + } else if (choice_result.type == ParseResultType::STRING) { + child = choice_result.Cast().result; + } else if (choice_result.type == ParseResultType::OPERATOR) { + child = choice_result.Cast().operator_token; + } else { + child = TransformIdentifierOrKeyword(transformer, choice_result); + } + } + auto result = TransformSchemaNameAsUseTarget(transformer, child); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeCatalogNameAsUseTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - process.ReserveChildSlots(0); + auto &list_pr = process.parse_result.Cast(); + auto &choice_pr = list_pr.Child(0); + auto &choice_result = choice_pr.GetResult(); + process.ReserveChildSlots(1); + auto child_rule = choice_result.GetRule(); + auto has_transform_process = child_rule && child_rule->transform_process; + if (!has_transform_process && + (choice_result.type == ParseResultType::IDENTIFIER || choice_result.type == ParseResultType::KEYWORD || + choice_result.type == ParseResultType::STRING || choice_result.type == ParseResultType::OPERATOR || + choice_result.type == ParseResultType::CHOICE || choice_result.type == ParseResultType::LIST)) { + return; + } + if (!has_transform_process) { + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); + } + process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeCatalogNameAsUseTargetTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto &list_pr = process.parse_result.Cast(); - auto catalog_name = list_pr.GetChild(0).Cast().identifier; - auto result = TransformCatalogNameAsUseTarget(transformer, catalog_name); - return make_uniq>(result); + string child; + if (process.child_results[0]) { + child = process.TakeResult(0); + } else { + auto &list_pr = process.parse_result.Cast(); + auto &choice_result = list_pr.Child(0).GetResult(); + if (choice_result.type == ParseResultType::IDENTIFIER) { + child = choice_result.Cast().identifier.GetIdentifierName(); + } else if (choice_result.type == ParseResultType::KEYWORD) { + child = choice_result.Cast().keyword; + } else if (choice_result.type == ParseResultType::STRING) { + child = choice_result.Cast().result; + } else if (choice_result.type == ParseResultType::OPERATOR) { + child = choice_result.Cast().operator_token; + } else { + child = TransformIdentifierOrKeyword(transformer, choice_result); + } + } + auto result = TransformCatalogNameAsUseTarget(transformer, child); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeUseTargetCatalogSchemaTrampoline(PEGTransformer &transformer, @@ -25771,7 +26848,7 @@ void PEGTransformerFactory::InitializeUseTargetCatalogSchemaTrampoline(PEGTransf } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUseTargetCatalogSchemaTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -25793,7 +26870,7 @@ PEGTransformerFactory::FinalizeUseTargetCatalogSchemaTrampoline(PEGTransformer & dot_identifier = std::move(dot_identifier_value); } auto result = TransformUseTargetCatalogSchema(transformer, catalog_name, reserved_schema_name, dot_identifier); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeDotIdentifierTrampoline(PEGTransformer &transformer, @@ -25801,13 +26878,13 @@ void PEGTransformerFactory::InitializeDotIdentifierTrampoline(PEGTransformer &tr process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeDotIdentifierTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto identifier = list_pr.GetChild(1).Cast().identifier; auto result = TransformDotIdentifier(transformer, identifier); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeVacuumStatementTrampoline(PEGTransformer &transformer, @@ -25824,7 +26901,7 @@ void PEGTransformerFactory::InitializeVacuumStatementTrampoline(PEGTransformer & } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeVacuumStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional vacuum_options {}; @@ -25836,7 +26913,7 @@ PEGTransformerFactory::FinalizeVacuumStatementTrampoline(PEGTransformer &transfo analyze_target = process.TakeResult(1); } auto result = TransformVacuumStatement(transformer, vacuum_options, std::move(analyze_target)); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } void PEGTransformerFactory::InitializeVacuumOptionsTrampoline(PEGTransformer &transformer, @@ -25848,16 +26925,16 @@ void PEGTransformerFactory::InitializeVacuumOptionsTrampoline(PEGTransformer &tr auto child_rule = choice_result.GetRule(); auto has_transform_process = child_rule && child_rule->transform_process; if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeVacuumOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = process.TakeResult(0); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeVacuumParensOptionsTrampoline(PEGTransformer &transformer, @@ -25872,7 +26949,7 @@ void PEGTransformerFactory::InitializeVacuumParensOptionsTrampoline(PEGTransform } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeVacuumParensOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -25883,7 +26960,7 @@ PEGTransformerFactory::FinalizeVacuumParensOptionsTrampoline(PEGTransformer &tra vacuum_option.push_back(process.TakeResult(i)); } auto result = TransformVacuumParensOptions(transformer, vacuum_option); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeVacuumLegacyOptionsTrampoline(PEGTransformer &transformer, @@ -25908,7 +26985,7 @@ void PEGTransformerFactory::InitializeVacuumLegacyOptionsTrampoline(PEGTransform } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeVacuumLegacyOptionsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { optional opt_full {}; @@ -25928,7 +27005,7 @@ PEGTransformerFactory::FinalizeVacuumLegacyOptionsTrampoline(PEGTransformer &tra opt_analyze = process.TakeResult(3); } auto result = TransformVacuumLegacyOptions(transformer, opt_full, opt_freeze, opt_verbose, opt_analyze); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeVacuumOptionTrampoline(PEGTransformer &transformer, @@ -25944,12 +27021,12 @@ void PEGTransformerFactory::InitializeVacuumOptionTrampoline(PEGTransformer &tra return; } if (!has_transform_process) { - throw InternalException("No transform process registered for rule '%s'", choice_result.name); + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); } process.PushChild({*child_rule, choice_result}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeVacuumOptionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { string result; if (process.child_results[0]) { @@ -25969,7 +27046,7 @@ PEGTransformerFactory::FinalizeVacuumOptionTrampoline(PEGTransformer &transforme result = transformer.Transform(choice_result); } } - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeOptAnalyzeTrampoline(PEGTransformer &transformer, @@ -25979,11 +27056,11 @@ void PEGTransformerFactory::InitializeOptAnalyzeTrampoline(PEGTransformer &trans process.PushChild({transformer.GetRule("AnalyzeKeyword"), list_pr.GetChild(0)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOptAnalyzeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto analyze_keyword = process.TakeResult(0); auto result = TransformOptAnalyze(transformer, analyze_keyword); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeOptFullTrampoline(PEGTransformer &transformer, @@ -25991,10 +27068,10 @@ void PEGTransformerFactory::InitializeOptFullTrampoline(PEGTransformer &transfor process.ReserveChildSlots(0); } -unique_ptr PEGTransformerFactory::FinalizeOptFullTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeOptFullTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformOptFull(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeOptFreezeTrampoline(PEGTransformer &transformer, @@ -26002,10 +27079,10 @@ void PEGTransformerFactory::InitializeOptFreezeTrampoline(PEGTransformer &transf process.ReserveChildSlots(0); } -unique_ptr -PEGTransformerFactory::FinalizeOptFreezeTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeOptFreezeTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto result = TransformOptFreeze(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeOptVerboseTrampoline(PEGTransformer &transformer, @@ -26013,10 +27090,10 @@ void PEGTransformerFactory::InitializeOptVerboseTrampoline(PEGTransformer &trans process.ReserveChildSlots(0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeOptVerboseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto result = TransformOptVerbose(transformer); - return make_uniq>(result); + return transformer.MakeResult(result); } void PEGTransformerFactory::InitializeNameListTrampoline(PEGTransformer &transformer, @@ -26027,21 +27104,21 @@ void PEGTransformerFactory::InitializeNameListTrampoline(PEGTransformer &transfo process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColId"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule("ColIdOrString"), list_items[child_idx].get()}, 0 + child_idx); } } -unique_ptr PEGTransformerFactory::FinalizeNameListTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeNameListTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); - vector col_id; + vector col_id_or_string; for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { - col_id.push_back(process.TakeResult(i)); + col_id_or_string.push_back(process.TakeResult(i)); } - auto result = TransformNameList(transformer, col_id); - return make_uniq>>(result); + auto result = TransformNameList(transformer, col_id_or_string); + return transformer.MakeResult>(result); } } // namespace duckdb diff --git a/src/duckdb/src/parser/peg/transformer/transform_generic_copy_option.cpp b/src/duckdb/src/parser/peg/transformer/transform_generic_copy_option.cpp index 3395bff41..3bc06b644 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_generic_copy_option.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_generic_copy_option.cpp @@ -11,6 +11,13 @@ namespace duckdb { vector PEGTransformerFactory::TransformGenericCopyOptionList(PEGTransformer &transformer, const vector &generic_copy_option) { + identifier_set_t option_names; + for (const auto &option : generic_copy_option) { + if (option_names.find(option.name) != option_names.end()) { + throw ParserException("Unexpected duplicate option %s", option.name); + } + option_names.insert(option.name); + } return generic_copy_option; } @@ -87,7 +94,27 @@ static GenericCopyOption BuildGenericCopyOption(const Identifier &generic_copy_o optional generic_copy_option_value) { GenericCopyOption copy_option; copy_option.name = Identifier(StringUtil::Lower(generic_copy_option_name.GetIdentifierName())); - if (!generic_copy_option_value || !generic_copy_option_value->has_value) { + const bool has_value = generic_copy_option_value && generic_copy_option_value->has_value; + if (!has_value && copy_option.name == "PARTITION_PATH") { + throw ParserException("PARTITION_PATH expects an expression"); + } + if (!has_value) { + return copy_option; + } + + if (copy_option.name == "PARTITION_PATH") { + // kept as-is, as it is bound against the partition columns + if (!generic_copy_option_value->is_order_list) { + copy_option.expression = std::move(generic_copy_option_value->expression); + return copy_option; + } + // an expression wrapped in () will trigger this case + auto &orders = generic_copy_option_value->order_list; + if (orders.size() != 1 || orders[0].type != OrderType::ORDER_DEFAULT || + orders[0].null_order != OrderByNullType::ORDER_DEFAULT) { + throw ParserException("PARTITION_PATH expects a single expression"); + } + copy_option.expression = std::move(orders[0].expression); return copy_option; } @@ -179,4 +206,28 @@ void PEGTransformerFactory::SplitGenericOptions(const vector } } +void PEGTransformerFactory::CollectGenericOptions(const vector &options_in, + case_insensitive_map_t> &options, + const char *statement_name) { + for (const auto &option : options_in) { + auto name = option.name.GetIdentifierName(); + if (option.expression) { + options[name] = option.expression->Copy(); + continue; + } + if (option.children.empty()) { + // Bare flag (e.g. `(SPOT)`) binds to boolean true, as in SplitGenericOptions. + options[name] = ConstantExpression::Boolean(true); + } else if (option.children.size() == 1) { + if (option.children[0].IsNull()) { + throw BinderException("NULL is not supported as a valid option for %s option %s", statement_name, + option.name); + } + options[name] = ConstantExpression::FromValue(option.children[0]); + } else { + throw ParserException("Option %s can only have one argument", option.name); + } + } +} + } // namespace duckdb diff --git a/src/duckdb/src/parser/peg/transformer/transform_insert.cpp b/src/duckdb/src/parser/peg/transformer/transform_insert.cpp index 4bd7f30df..0f152888e 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_insert.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_insert.cpp @@ -10,7 +10,7 @@ unique_ptr PEGTransformerFactory::TransformInsertStatement( PEGTransformer &transformer, optional with_clause, const optional &or_action, unique_ptr insert_target, const optional &by_name_or_position, const optional> &insert_column_list, - InsertValues insert_values, optional> on_conflict_clause, + bool has_overriding_clause, InsertValues insert_values, optional> on_conflict_clause, optional>> returning_clause) { auto result = make_uniq(); auto &node = *result->node; @@ -26,6 +26,10 @@ unique_ptr PEGTransformerFactory::TransformInsertStatement( throw ParserException( "You can not provide both a column list and DEFAULT VALUES, please remove one of the two"); } + if (has_overriding_clause && insert_values.default_values) { + throw ParserException("OVERRIDING SYSTEM|USER VALUE can not be combined with DEFAULT VALUES, " + "please remove one of the two"); + } if (insert_values.default_values) { node.default_values = true; } diff --git a/src/duckdb/src/parser/peg/transformer/transform_pivot.cpp b/src/duckdb/src/parser/peg/transformer/transform_pivot.cpp index 13bf15bef..0c168a38d 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_pivot.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_pivot.cpp @@ -73,85 +73,6 @@ PivotColumn PEGTransformerFactory::TransformPivotColumnExpression(PEGTransformer return result; } -unique_ptr PEGTransformerFactory::TransformPivotStatement(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - - auto current_param_count = transformer.ParamCount(); - auto source = TransformTableRef(transformer, list_pr.GetChild(1)); - auto next_param_count = transformer.ParamCount(); - bool has_parameters = next_param_count > current_param_count; - auto &pivot_columns = list_pr.Child(2); - auto select_node = make_uniq(); - auto &pivot_group = list_pr.Child(4); - if (!pivot_columns.HasResult()) { - select_node->from_table = TransformTableRef(transformer, list_pr.GetChild(1)); - if (pivot_group.HasResult()) { - auto pivot_group_list = transformer.Transform>(pivot_group.GetResult()); - GroupingSet set; - for (idx_t gr = 0; gr < pivot_group_list.size(); gr++) { - auto &group = pivot_group_list[gr]; - auto colref = make_uniq(Identifier(group)); - select_node->select_list.push_back(colref->Copy()); - select_node->groups.group_expressions.push_back(std::move(colref)); - set.insert(ProjectionIndex(gr)); - } - select_node->groups.grouping_sets.push_back(std::move(set)); - } - - vector> pivot_using; - transformer.TransformOptional>>(list_pr, 3, pivot_using); - for (auto &col : pivot_using) { - select_node->select_list.push_back(std::move(col)); - } - auto result = make_uniq(); - result->node = std::move(select_node); - return result; - } - auto columns = transformer.Transform>(pivot_columns.GetResult()); - for (idx_t c = 0; c < columns.size(); c++) { - auto &col = columns[c]; - if (!col.pivot_enum.empty() || !col.entries.empty()) { - continue; - } - if (col.pivot_expressions.size() != 1) { - throw InternalException("PIVOT statement with multiple names in pivot entry!?"); - } - auto enum_name = "__pivot_enum_" + UUID::ToString(UUID::GenerateRandomUUID()); - - auto new_select = make_uniq(); - transformer.ExtractCTEsRecursive(new_select->cte_map); - new_select->from_table = source->Copy(); - AddPivotEntry(transformer, enum_name, std::move(new_select), col.pivot_expressions[0]->Copy(), - std::move(col.subquery), has_parameters); - col.pivot_enum = Identifier(enum_name); - } - - // Generate the actual query, including the pivot - select_node->select_list.push_back(make_uniq()); - - auto pivot_ref = make_uniq(); - pivot_ref->source = std::move(source); - auto &pivot_aggregates = list_pr.Child(3); - if (pivot_aggregates.HasResult()) { - pivot_ref->aggregates = - transformer.Transform>>(pivot_aggregates.GetResult()); - } else { - // pivot but no aggregates specified - push a count star - vector> children; - auto function = make_uniq("count_star", std::move(children)); - pivot_ref->aggregates.push_back(std::move(function)); - } - if (pivot_group.HasResult()) { - pivot_ref->groups = transformer.Transform>(pivot_group.GetResult()); - } - pivot_ref->pivots = std::move(columns); - select_node->from_table = std::move(pivot_ref); - auto select_statement = make_uniq(); - select_statement->node = std::move(select_node); - return select_statement; -} - void PEGTransformerFactory::InitializePivotStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -160,16 +81,19 @@ void PEGTransformerFactory::InitializePivotStatementTrampoline(PEGTransformer &t process.PushChild({list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizePivotStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); + auto &pivot_columns = list_pr.Child(2); + auto &pivot_aggregates = list_pr.Child(3); + auto &pivot_group = list_pr.Child(4); + if (!pivot_columns.HasResult() && !pivot_aggregates.HasResult() && !pivot_group.HasResult()) { + throw ParserException("PIVOT requires an ON, USING or GROUP BY clause"); + } if (!process.child_results[4]) { bool has_parameters = transformer.ParamCount() > process.manual_state; - process.SetChildResult(4, make_uniq>(has_parameters)); - auto &pivot_columns = list_pr.Child(2); - auto &pivot_aggregates = list_pr.Child(3); - auto &pivot_group = list_pr.Child(4); + process.SetChildResult(4, transformer.MakeResult(has_parameters)); bool pushed_child = false; if (pivot_group.HasResult()) { process.PushChild({pivot_group.GetResult()}, 3); @@ -189,8 +113,6 @@ PEGTransformerFactory::FinalizePivotStatementTrampoline(PEGTransformer &transfor } auto source = process.TakeResult>(0); auto has_parameters = process.TakeResult(4); - auto &pivot_columns = list_pr.Child(2); - auto &pivot_group = list_pr.Child(4); auto select_node = make_uniq(); if (!pivot_columns.HasResult()) { select_node->from_table = std::move(source); @@ -215,7 +137,7 @@ PEGTransformerFactory::FinalizePivotStatementTrampoline(PEGTransformer &transfor } auto result = make_uniq(); result->node = std::move(select_node); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } auto columns = process.TakeResult>(1); for (idx_t c = 0; c < columns.size(); c++) { @@ -239,7 +161,6 @@ PEGTransformerFactory::FinalizePivotStatementTrampoline(PEGTransformer &transfor select_node->select_list.push_back(make_uniq()); auto pivot_ref = make_uniq(); pivot_ref->source = std::move(source); - auto &pivot_aggregates = list_pr.Child(3); if (pivot_aggregates.HasResult()) { pivot_ref->aggregates = process.TakeResult>>(2); } else { @@ -254,7 +175,7 @@ PEGTransformerFactory::FinalizePivotStatementTrampoline(PEGTransformer &transfor select_node->from_table = std::move(pivot_ref); auto result = make_uniq(); result->node = std::move(select_node); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } vector> @@ -293,79 +214,6 @@ bool PEGTransformerFactory::TransformExcludeNulls(PEGTransformer &transformer) { return false; } -unique_ptr PEGTransformerFactory::TransformUnpivotStatement(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto current_param_count = transformer.ParamCount(); - auto source = TransformTableRef(transformer, list_pr.GetChild(1)); - auto next_param_count = transformer.ParamCount(); - bool has_parameters = next_param_count > current_param_count; - auto target_list = transformer.Transform>>(list_pr.GetChild(3)); - auto &unpivot_names_opt = list_pr.Child(4); - vector columns; - UnpivotNameValues name_and_values; - if (!unpivot_names_opt.HasResult()) { - PivotColumn col; - col.unpivot_names.push_back("name"); - for (auto &expr : target_list) { - PivotColumnEntry entry; - entry.alias = expr->GetAlias(); - entry.expr = std::move(expr); - col.entries.push_back(std::move(entry)); - } - columns.push_back(std::move(col)); - } else { - name_and_values = transformer.Transform(unpivot_names_opt.GetResult()); - auto unpivot_list_size = name_and_values.column.unpivot_names.size(); - if (unpivot_list_size != 1) { - throw NotImplementedException("Only expected a single value after NAME, got %d instead.", - unpivot_list_size); - } - auto &col = name_and_values.column; - for (auto &expr : target_list) { - PivotColumnEntry entry; - entry.alias = expr->GetAlias(); - entry.expr = std::move(expr); - col.entries.push_back(std::move(entry)); - } - columns.push_back(std::move(col)); - } - - for (idx_t c = 0; c < columns.size(); c++) { - auto &col = columns[c]; - if (!col.pivot_enum.empty() || !col.entries.empty()) { - continue; - } - if (col.pivot_expressions.size() != 1) { - throw InternalException("PIVOT statement with multiple names in pivot entry!?"); - } - auto enum_name = "__pivot_enum_" + UUID::ToString(UUID::GenerateRandomUUID()); - - auto new_select = make_uniq(); - transformer.ExtractCTEsRecursive(new_select->cte_map); - new_select->from_table = source->Copy(); - AddPivotEntry(transformer, enum_name, std::move(new_select), col.pivot_expressions[0]->Copy(), - std::move(col.subquery), has_parameters); - col.pivot_enum = Identifier(enum_name); - } - - auto pivot_ref = make_uniq(); - pivot_ref->source = std::move(source); - if (!unpivot_names_opt.HasResult()) { - pivot_ref->unpivot_names.push_back("value"); - } else { - pivot_ref->unpivot_names = name_and_values.unpivot_names; - } - - auto result = make_uniq(); - pivot_ref->pivots = std::move(columns); - auto select_node = make_uniq(); - select_node->from_table = std::move(pivot_ref); - select_node->select_list.push_back(make_uniq()); - result->node = std::move(select_node); - return result; -} - void PEGTransformerFactory::InitializeUnpivotStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -374,13 +222,13 @@ void PEGTransformerFactory::InitializeUnpivotStatementTrampoline(PEGTransformer process.PushChild({list_pr.GetChild(1)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeUnpivotStatementTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); if (!process.child_results[3]) { bool has_parameters = transformer.ParamCount() > process.manual_state; - process.SetChildResult(3, make_uniq>(has_parameters)); + process.SetChildResult(3, transformer.MakeResult(has_parameters)); auto &unpivot_names_opt = list_pr.Child(4); if (unpivot_names_opt.HasResult()) { process.PushChild({unpivot_names_opt.GetResult()}, 2); @@ -453,7 +301,7 @@ PEGTransformerFactory::FinalizeUnpivotStatementTrampoline(PEGTransformer &transf select_node->select_list.push_back(make_uniq()); auto result = make_uniq(); result->node = std::move(select_node); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } UnpivotNameValues PEGTransformerFactory::TransformIntoNameValues(PEGTransformer &transformer, diff --git a/src/duckdb/src/parser/peg/transformer/transform_select.cpp b/src/duckdb/src/parser/peg/transformer/transform_select.cpp index 2315cd057..ce09ba78a 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_select.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_select.cpp @@ -52,45 +52,6 @@ PEGTransformerFactory::TransformSelectStatement(PEGTransformer &transformer, return std::move(select_statement_internal); } -unique_ptr PEGTransformerFactory::TransformSelectStatementInternalRule(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - CommonTableExpressionMap cte_map; - transformer.TransformOptional(list_pr, 0, cte_map); - if (!cte_map.map.empty()) { - transformer.stored_cte_map.push_back(cte_map); - } - auto select_statement = transformer.Transform>(list_pr.Child(1)); - - vector> result_modifiers; - transformer.TransformOptional>>(list_pr, 2, result_modifiers); - if (!cte_map.map.empty()) { - // the CTEs are visible while transforming the select statement and its result modifiers - transformer.stored_cte_map.pop_back(); - select_statement->node->cte_map = std::move(cte_map); - } - for (auto &result_modifier : result_modifiers) { - select_statement->node->modifiers.push_back(std::move(result_modifier)); - } - if (select_statement->node->type != QueryNodeType::SELECT_NODE) { - return select_statement; - } - auto &select_node = select_statement->node->Cast(); - if (select_node.from_table->type != TableReferenceType::SHOW_REF) { - return select_statement; - } - auto &show_ref = select_node.from_table->Cast(); - if (!select_statement->node->cte_map.map.empty()) { - throw ParserException("%s with CTE not allowed - wrap the statement in a subquery instead", - EnumUtil::ToString(show_ref.show_type)); - } - if (!select_statement->node->modifiers.empty()) { - throw ParserException("%s with ORDER BY not allowed - wrap the statement in a subquery instead", - EnumUtil::ToString(show_ref.show_type)); - } - return select_statement; -} - static void PushSelectStatementInternalRemainder(GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto &result_modifiers_opt = list_pr.Child(2); @@ -114,7 +75,7 @@ void PEGTransformerFactory::InitializeSelectStatementInternalTrampoline(PEGTrans PushSelectStatementInternalRemainder(process); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSelectStatementInternalTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { if (process.manual_state == 0) { @@ -159,7 +120,7 @@ PEGTransformerFactory::FinalizeSelectStatementInternalTrampoline(PEGTransformer } } } - return make_uniq>>(std::move(select_statement)); + return transformer.MakeResult>(std::move(select_statement)); } unique_ptr PEGTransformerFactory::TransformSelectSetOpChain( @@ -265,42 +226,17 @@ bool PEGTransformerFactory::TransformWithOrdinality(PEGTransformer &transformer) return true; } -unique_ptr PEGTransformerFactory::TransformSimpleSelect(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto &opt_window_clause = list_pr.Child(4); - if (opt_window_clause.HasResult()) { - transformer.Transform>>(opt_window_clause.GetResult()); - } - auto select_node = transformer.Transform>(list_pr.Child(0)); - transformer.TransformOptional>(list_pr, 1, select_node->where_clause); - auto &group_opt = list_pr.Child(2); - if (group_opt.HasResult()) { - auto group_by_node = transformer.Transform(group_opt.GetResult()); - if (group_by_node.group_expressions.size() == 1 && ExpressionIsEmptyStar(*group_by_node.group_expressions[0])) { - select_node->aggregate_handling = AggregateHandling::FORCE_AGGREGATES; - group_by_node.group_expressions.clear(); - group_by_node.grouping_sets.clear(); - } - select_node->groups = std::move(group_by_node); - } - transformer.TransformOptional>(list_pr, 3, select_node->having); - transformer.TransformOptional>(list_pr, 5, select_node->qualify); - transformer.TransformOptional>(list_pr, 6, select_node->sample); - auto select_statement = make_uniq(); - select_statement->node = std::move(select_node); - transformer.window_clauses.clear(); - return select_statement; -} - static void RegisterWindowClause(PEGTransformer &transformer, const Identifier &window_name, WindowExpression &window_function) { - auto it = transformer.window_clauses.find(window_name); - if (it != transformer.window_clauses.end()) { + if (transformer.window_clauses.empty()) { + throw InternalException("WINDOW clause registered outside of a SELECT"); + } + auto ¤t_windows = transformer.window_clauses.back(); + auto it = current_windows.find(window_name); + if (it != current_windows.end()) { throw ParserException("window %s is already defined", window_name); } - transformer.window_clauses[window_name] = - unique_ptr_cast(window_function.Copy()); + current_windows[window_name] = unique_ptr_cast(window_function.Copy()); } static void PushSimpleSelectRemainder(GeneratedTransformProcess &process) { @@ -332,6 +268,7 @@ void PEGTransformerFactory::InitializeSimpleSelectTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(7); + transformer.window_clauses.emplace_back(); auto &window_clause_opt = list_pr.Child(4); if (window_clause_opt.HasResult()) { process.manual_state = 0; @@ -342,7 +279,7 @@ void PEGTransformerFactory::InitializeSimpleSelectTrampoline(PEGTransformer &tra PushSimpleSelectRemainder(process); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeSimpleSelectTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { if (process.manual_state == 0) { process.TakeResult>>(4); @@ -375,12 +312,17 @@ PEGTransformerFactory::FinalizeSimpleSelectTrampoline(PEGTransformer &transforme } auto select_statement = make_uniq(); select_statement->node = std::move(select_node); - transformer.window_clauses.clear(); - return make_uniq>>(std::move(select_statement)); + D_ASSERT(!transformer.window_clauses.empty()); + transformer.window_clauses.pop_back(); + return transformer.MakeResult>(std::move(select_statement)); } FunctionArgument PEGTransformerFactory::TransformNamedFunctionArgument(PEGTransformer &transformer, MacroParameter named_parameter) { + if (named_parameter.type != LogicalType::UNKNOWN) { + throw ParserException("Named argument \"%s\" of a function call cannot have a type", + named_parameter.name.GetIdentifierName()); + } named_parameter.expression->SetAlias(named_parameter.name); return FunctionArgument(named_parameter.name, std::move(named_parameter.expression)); } @@ -443,36 +385,6 @@ QualifiedName PEGTransformerFactory::TransformSchemaReservedIdentifierOrStringLi return result; } -unique_ptr PEGTransformerFactory::TransformTableRef(PEGTransformer &transformer, ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - auto inner_table_ref = transformer.Transform>(list_pr.Child(0)); - auto &join_or_pivot_opt = list_pr.Child(1); - if (!join_or_pivot_opt.HasResult()) { - return inner_table_ref; - } - auto &repeat_join_or_pivot = join_or_pivot_opt.GetResult().Cast(); - for (auto join_or_pivot : repeat_join_or_pivot.GetChildren()) { - auto transform_join_or_pivot = transformer.Transform>(join_or_pivot); - if (transform_join_or_pivot->type == TableReferenceType::JOIN) { - auto &join_ref = transform_join_or_pivot->Cast(); - join_ref.left = std::move(inner_table_ref); - inner_table_ref = std::move(transform_join_or_pivot); - } else if (transform_join_or_pivot->type == TableReferenceType::PIVOT) { - auto &pivot_ref = transform_join_or_pivot->Cast(); - pivot_ref.source = std::move(inner_table_ref); - inner_table_ref = std::move(transform_join_or_pivot); - } else if (transform_join_or_pivot->type == TableReferenceType::MATCH_RECOGNIZE) { - auto &match_recognize_ref = transform_join_or_pivot->Cast(); - match_recognize_ref.input = std::move(inner_table_ref); - inner_table_ref = std::move(transform_join_or_pivot); - } else { - throw NotImplementedException("Unsupported TableRef type encountered: %s", - EnumUtil::ToString(transform_join_or_pivot->type)); - } - } - return inner_table_ref; -} - void PEGTransformerFactory::InitializeTableRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -492,13 +404,13 @@ void PEGTransformerFactory::InitializeTableRefTrampoline(PEGTransformer &transfo process.PushChild({list_pr.GetChild(0)}, 0); } -unique_ptr PEGTransformerFactory::FinalizeTableRefTrampoline(PEGTransformer &transformer, - GeneratedTransformProcess &process) { +arena_ptr PEGTransformerFactory::FinalizeTableRefTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); auto inner_table_ref = process.TakeResult>(0); auto &join_or_pivot_opt = list_pr.Child(1); if (!join_or_pivot_opt.HasResult()) { - return make_uniq>>(std::move(inner_table_ref)); + return transformer.MakeResult>(std::move(inner_table_ref)); } auto &repeat_join_or_pivot = join_or_pivot_opt.GetResult().Cast(); auto repeat_children = repeat_join_or_pivot.GetChildren(); @@ -521,7 +433,7 @@ unique_ptr PEGTransformerFactory::FinalizeTableRefTrampoli EnumUtil::ToString(transform_join_or_pivot->type)); } } - return make_uniq>>(std::move(inner_table_ref)); + return transformer.MakeResult>(std::move(inner_table_ref)); } unique_ptr PEGTransformerFactory::TransformTableUnpivotClauseBody(PEGTransformer &transformer, @@ -581,7 +493,7 @@ bool PEGTransformerFactory::TransformPivotInList(unique_ptr &e } case ExpressionType::FUNCTION: { auto &function = expr->Cast(); - if (function.FunctionName() != "row") { + if (function.FunctionName() != "row" || function.GetArgumentsMutable().empty()) { return false; } for (auto &child : function.GetArgumentsMutable()) { @@ -866,7 +778,7 @@ JoinType PEGTransformerFactory::TransformInnerJoin(PEGTransformer &transformer) unique_ptr PEGTransformerFactory::TransformTableFunctionLateralOpt( PEGTransformer &transformer, const optional &lateral, const QualifiedName &qualified_table_function, vector table_function_arguments, const optional &with_ordinality, - const optional &table_alias) { + const optional &table_alias, optional> sample_clause) { auto result = make_uniq(); result->with_ordinality = @@ -876,6 +788,9 @@ unique_ptr PEGTransformerFactory::TransformTableFunctionLateralOpt( result->alias = table_alias->name; result->column_name_alias = table_alias->column_name_alias; } + if (sample_clause) { + result->sample = std::move(*sample_clause); + } return std::move(result); } @@ -1021,6 +936,7 @@ vector PEGTransformerFactory::GroupByExpressionUnfolding(GroupByExp for (auto &child_expr : group_by_expr.children) { auto child_sets = GroupByExpressionUnfolding(child_expr, map, result); result_sets.insert(result_sets.end(), child_sets.begin(), child_sets.end()); + CheckGroupingSetMax(result_sets.size()); } } else if (group_by_expr.type == GroupByExpressionInfoType::CUBE || group_by_expr.type == GroupByExpressionInfoType::ROLLUP) { @@ -1129,39 +1045,6 @@ PEGTransformerFactory::TransformGroupingSetsClause(PEGTransformer &transformer, return result; } -CommonTableExpressionMap PEGTransformerFactory::TransformWithClause(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - bool is_recursive = list_pr.Child(1).HasResult(); - auto with_statement_list = PEGTransformerFactory::ExtractParseResultsFromList(list_pr.Child(2)); - CommonTableExpressionMap result; - - for (idx_t entry_idx = 0; entry_idx < with_statement_list.size(); entry_idx++) { - auto with_entry = transformer.Transform>>( - with_statement_list[entry_idx]); - - if (is_recursive) { - auto &query_node = with_entry.second->query_node; - if (!query_node) { - throw ParserException("Recursive CTEs with DML statements are not supported"); - } - ValidateRecursiveCTEQueryNode(*query_node); - // Now safe to call on SELECT, VALUES, etc. - query_node = ToRecursiveCTE(std::move(query_node), with_entry.first, with_entry.second->aliases, - with_entry.second->key_targets); - } - auto &cte_name = with_entry.first; - - auto it = result.map.find(cte_name); - if (it != result.map.end()) { - // can't have two CTEs with same name - throw ParserException("Duplicate CTE name %s", cte_name); - } - result.map.insert(with_entry.first, std::move(with_entry.second)); - } - return result; -} - void PEGTransformerFactory::InitializeWithClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -1173,7 +1056,7 @@ void PEGTransformerFactory::InitializeWithClauseTrampoline(PEGTransformer &trans } } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWithClauseTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); bool is_recursive = list_pr.Child(1).HasResult(); @@ -1196,18 +1079,18 @@ PEGTransformerFactory::FinalizeWithClauseTrampoline(PEGTransformer &transformer, } result.map.insert(with_entry.first, std::move(with_entry.second)); } - return make_uniq>(std::move(result)); + return transformer.MakeResult(std::move(result)); } pair> PEGTransformerFactory::TransformWithStatement(PEGTransformer &transformer, const Identifier &col_id_or_string, - const optional> &insert_column_list, + const optional> &column_aliases, optional>> using_key, const optional &materialized, unique_ptr cte_body) { auto result = make_uniq(); auto cte_name = col_id_or_string; - if (insert_column_list) { - result->aliases = StringsToIdentifiers(*insert_column_list); + if (column_aliases) { + result->aliases = StringsToIdentifiers(*column_aliases); } if (using_key) { result->key_targets = std::move(*using_key); @@ -1265,17 +1148,6 @@ bool PEGTransformerFactory::TransformMaterialized(PEGTransformer &transformer, c return has_result; } -unique_ptr PEGTransformerFactory::TransformWindowDefinition(PEGTransformer &transformer, - ParseResult &parse_result) { - auto &list_pr = parse_result.Cast(); - transformer.in_window_definition = true; - auto window_function = transformer.Transform>(list_pr.Child(2)); - transformer.in_window_definition = false; - auto window_name = list_pr.Child(0).identifier; - RegisterWindowClause(transformer, window_name, *window_function); - return std::move(window_function); -} - void PEGTransformerFactory::InitializeWindowDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -1284,7 +1156,7 @@ void PEGTransformerFactory::InitializeWindowDefinitionTrampoline(PEGTransformer process.PushChild({list_pr.GetChild(2)}, 0); } -unique_ptr +arena_ptr PEGTransformerFactory::FinalizeWindowDefinitionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -1293,7 +1165,7 @@ PEGTransformerFactory::FinalizeWindowDefinitionTrampoline(PEGTransformer &transf auto window_name = list_pr.Child(0).identifier; RegisterWindowClause(transformer, window_name, *window_function); unique_ptr result = std::move(window_function); - return make_uniq>>(std::move(result)); + return transformer.MakeResult>(std::move(result)); } unique_ptr PEGTransformerFactory::TransformSampleEntryFunction( @@ -1372,6 +1244,12 @@ unique_ptr PEGTransformerFactory::TransformOffsetFetchClause(PEG return VerifyLimitOffset(fetch_clause, offset_clause); } +unique_ptr PEGTransformerFactory::TransformFetchOffsetClause(PEGTransformer &transformer, + LimitPercentResult fetch_clause, + LimitPercentResult offset_clause) { + return VerifyLimitOffset(fetch_clause, offset_clause); +} + unique_ptr PEGTransformerFactory::TransformFetchOnlyClause(PEGTransformer &transformer, LimitPercentResult fetch_clause) { LimitPercentResult empty_offset; @@ -1483,7 +1361,8 @@ unique_ptr PEGTransformerFactory::TransformTableSubquery(PEGTransforme const optional &table_alias_colon, const optional &lateral, unique_ptr subquery_reference, - const optional &table_alias) { + const optional &table_alias, + optional> sample_clause) { if (table_alias_colon && table_alias) { throw ParserException("Table reference %s cannot have two aliases", subquery_reference->ToString()); } @@ -1494,6 +1373,9 @@ unique_ptr PEGTransformerFactory::TransformTableSubquery(PEGTransforme subquery_reference->alias = table_alias->name; subquery_reference->column_name_alias = table_alias->column_name_alias; } + if (sample_clause) { + subquery_reference->sample = std::move(*sample_clause); + } return subquery_reference; } @@ -1605,7 +1487,7 @@ PEGTransformerFactory::TransformPivotTargetList(PEGTransformer &transformer, PivotColumnEntry pivot_entry; pivot_entry.alias = target->GetAlias(); bool transformed = TransformPivotInList(target, pivot_entry); - if (!transformed) { + if (!transformed || pivot_entry.values.empty()) { pivot_entry.expr = std::move(target); } result.push_back(std::move(pivot_entry)); @@ -1792,6 +1674,11 @@ unique_ptr PEGTransformerFactory::TransformSampleClause(PEGTransf return sample_entry; } +unique_ptr PEGTransformerFactory::TransformSelectSampleClause(PEGTransformer &transformer, + unique_ptr sample_entry) { + return sample_entry; +} + vector> PEGTransformerFactory::TransformWindowClause(PEGTransformer &transformer, vector> window_definition) { @@ -1931,6 +1818,12 @@ LimitPercentResult PEGTransformerFactory::TransformFetchValue(PEGTransformer &tr return result; } +LimitPercentResult PEGTransformerFactory::TransformFetchClauseWithoutValue(PEGTransformer &transformer) { + LimitPercentResult result; + result.expression = ConstantExpression::Integer(1); + return result; +} + unique_ptr PEGTransformerFactory::TransformColIdExpression(PEGTransformer &transformer, const Identifier &col_id, unique_ptr expression) { diff --git a/src/duckdb/src/parser/peg/transformer/transform_update.cpp b/src/duckdb/src/parser/peg/transformer/transform_update.cpp index d8c447127..439621d07 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_update.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_update.cpp @@ -81,20 +81,26 @@ unique_ptr PEGTransformerFactory::TransformUpdateSetTuple(PEGTran return result; } -unique_ptr PEGTransformerFactory::TransformUpdateSetElementList( - PEGTransformer &transformer, vector>> update_set_element) { +unique_ptr +PEGTransformerFactory::TransformUpdateSetClauseList(PEGTransformer &transformer, + vector> update_set_info) { auto result = make_uniq(); - for (auto &element : update_set_element) { - result->columns.emplace_back(std::move(element.first)); - result->expressions.push_back(std::move(element.second)); + for (auto &info : update_set_info) { + for (idx_t i = 0; i < info->columns.size(); i++) { + result->columns.emplace_back(std::move(info->columns[i])); + result->expressions.push_back(std::move(info->expressions[i])); + } } return result; } -pair> -PEGTransformerFactory::TransformUpdateSetElement(PEGTransformer &transformer, const string &update_set_column_target, - unique_ptr expression) { - return {update_set_column_target, std::move(expression)}; +unique_ptr PEGTransformerFactory::TransformUpdateSetElement(PEGTransformer &transformer, + const string &update_set_column_target, + unique_ptr expression) { + auto result = make_uniq(); + result->columns.emplace_back(update_set_column_target); + result->expressions.push_back(std::move(expression)); + return result; } string PEGTransformerFactory::TransformUpdateSetColumnTarget(PEGTransformer &transformer, const Identifier &column_name, diff --git a/src/duckdb/src/parser/peg/transformer/transform_use.cpp b/src/duckdb/src/parser/peg/transformer/transform_use.cpp index e702245ac..67b487a30 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_use.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_use.cpp @@ -17,19 +17,17 @@ unique_ptr PEGTransformerFactory::TransformUseStatement(PEGTransfo return make_uniq("schema", std::move(value_expr), SetScope::AUTOMATIC); } -// UseTarget <- UseTargetCatalogSchema / SchemaName / CatalogName +// UseTarget <- UseTargetCatalogSchema / SchemaNameAsUseTarget / CatalogNameAsUseTarget +// SchemaNameAsUseTarget <- SchemaName / StringLiteral QualifiedName PEGTransformerFactory::TransformSchemaNameAsUseTarget(PEGTransformer &transformer, - const Identifier &schema_name) { - QualifiedName result; - result = QualifiedName(schema_name); - return result; + const string &schema_name) { + return QualifiedName(Identifier(schema_name)); } +// CatalogNameAsUseTarget <- CatalogName / StringLiteral QualifiedName PEGTransformerFactory::TransformCatalogNameAsUseTarget(PEGTransformer &transformer, - const Identifier &catalog_name) { - QualifiedName result; - result = QualifiedName(catalog_name); - return result; + const string &catalog_name) { + return QualifiedName(Identifier(catalog_name)); } // UseTargetCatalogSchema <- CatalogName '.' ReservedSchemaName DotIdentifier* diff --git a/src/duckdb/src/parser/peg/transformer/transform_vacuum.cpp b/src/duckdb/src/parser/peg/transformer/transform_vacuum.cpp index eca1e184c..22b371bbb 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_vacuum.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_vacuum.cpp @@ -58,6 +58,8 @@ VacuumOptions PEGTransformerFactory::TransformVacuumParensOptions(PEGTransformer } if (StringUtil::CIEquals(option, "analyze")) { options.analyze = true; + } else { + throw ParserException("unrecognized VACUUM option \"%s\"", option); } } return options; diff --git a/src/duckdb/src/parser/qualified_name.cpp b/src/duckdb/src/parser/qualified_name.cpp index 99899b0e2..7b4a5f19b 100644 --- a/src/duckdb/src/parser/qualified_name.cpp +++ b/src/duckdb/src/parser/qualified_name.cpp @@ -1,6 +1,7 @@ #include "duckdb/parser/qualified_name.hpp" #include "duckdb/parser/parsed_data/parse_info.hpp" #include "duckdb/common/exception/parser_exception.hpp" +#include "duckdb/common/string_util.hpp" #include "duckdb/common/types/hash.hpp" #include "duckdb/planner/binding_alias.hpp" #include "duckdb/common/serializer/serializer.hpp" @@ -47,61 +48,47 @@ string QualifiedName::ToString(QualifiedNameToStringMode mode) const { //! characters like ()'- and keywords without requiring double quotes. It only requires double quotes around . //! characters and doubled double quotes (which collapse into a single double quote). It's only possible to fully //! double quote a component or not quote it at all. -vector QualifiedName::ParseComponents(const string &input) { - vector result; +static bool TryParseComponents(const string &input, vector &result, string &error) { idx_t idx = 0; - string entry; - -normal: - //! quote - for (; idx < input.size(); idx++) { - if (input[idx] == '"') { - if (!entry.empty()) { - //! a quote may only open a component, e.g. abc"xyz" is not a valid identifier - throw ParserException("Unexpected quote in the middle of a qualified name component! (input: %s)", - input); - } - idx++; - goto quoted; - } else if (input[idx] == '.') { - goto separator; - } - entry += input[idx]; - } - goto end; -separator: - result.push_back(Identifier(entry)); - entry = ""; - idx++; - goto normal; -quoted: - //! look for another quote - for (; idx < input.size(); idx++) { + while (idx < input.size()) { + string entry; if (input[idx] == '"') { - if (idx + 1 < input.size() && input[idx + 1] == '"') { - //! escaped quote ("" inside a quoted identifier is a literal ") - entry += '"'; - idx++; - continue; + if (!StringUtil::TryParseQuotedString(input, idx, entry)) { + error = StringUtil::Format("Unterminated quote in qualified name! (input: %s)", input); + return false; } if (entry.empty()) { - //! the SQL parser also rejects "" as a zero-length delimited identifier - throw ParserException("Zero-length delimited identifier in qualified name! (input: %s)", input); + error = StringUtil::Format("Zero-length delimited identifier in qualified name! (input: %s)", input); + return false; } - //! unquote; a closing quote must end the component, e.g. "abc"xyz is not a valid identifier - idx++; if (idx < input.size() && input[idx] != '.') { - throw ParserException("Unexpected character after a quoted identifier in a qualified name! (input: %s)", - input); + error = StringUtil::Format( + "Unexpected character after a quoted identifier in a qualified name! (input: %s)", input); + return false; + } + } else { + for (; idx < input.size() && input[idx] != '.'; idx++) { + if (input[idx] == '"') { + error = StringUtil::Format( + "Unexpected quote in the middle of a qualified name component! (input: %s)", input); + return false; + } + entry += input[idx]; } - goto normal; } - entry += input[idx]; + result.emplace_back(std::move(entry)); + if (idx < input.size()) { + idx++; + } } - throw ParserException("Unterminated quote in qualified name! (input: %s)", input); -end: - if (!entry.empty()) { - result.push_back(Identifier(entry)); + return true; +} + +vector QualifiedName::ParseComponents(const string &input) { + vector result; + string error; + if (!TryParseComponents(input, result, error)) { + throw ParserException(error); } return result; } @@ -133,14 +120,26 @@ bool QualifiedName::operator!=(const QualifiedName &rhs) const { } QualifiedName QualifiedName::Parse(const string &input) { - auto entries = ParseComponents(input); - if (entries.empty()) { - return QualifiedName(); - } - // the last component is the name, anything before it is the (possibly nested) catalog/schema path - Identifier name = std::move(entries.back()); - entries.pop_back(); - return QualifiedName(std::move(entries), std::move(name)); + return FromPath(ParseComponents(input)); +} + +bool QualifiedName::TryParse(const string &input, QualifiedName &result, string &error) { + vector components; + if (!TryParseComponents(input, components, error)) { + return false; + } + if (components.empty()) { + error = StringUtil::Format("Zero-length identifier in qualified name! (input: %s)", input); + return false; + } + for (auto &component : components) { + if (component.empty()) { + error = StringUtil::Format("Zero-length identifier in qualified name! (input: %s)", input); + return false; + } + } + result = FromPath(std::move(components)); + return true; } QualifiedColumnName::QualifiedColumnName() { diff --git a/src/duckdb/src/parser/query_node.cpp b/src/duckdb/src/parser/query_node.cpp index 2cd34e8f4..623878f0a 100644 --- a/src/duckdb/src/parser/query_node.cpp +++ b/src/duckdb/src/parser/query_node.cpp @@ -5,6 +5,8 @@ #include "duckdb/parser/query_node/recursive_cte_node.hpp" #include "duckdb/parser/query_node/cte_node.hpp" #include "duckdb/common/limits.hpp" +#include "duckdb/parser/common_table_expression_info.hpp" +#include "duckdb/parser/result_modifier.hpp" namespace duckdb { CommonTableExpressionMap::CommonTableExpressionMap() { @@ -78,7 +80,7 @@ string CommonTableExpressionMap::ToString() const { result += ")"; first_cte = false; } - return result; + return result + " "; } string QueryNode::ResultModifiersToString() const { @@ -186,4 +188,61 @@ void QueryNode::AddDistinct() { modifiers.push_back(make_uniq()); } +void QueryNode::VerifyDeserialized(const unique_ptr &node) { + if (!node) { + throw SerializationException("Failed to deserialize query node: missing query node"); + } + for (auto &modifier : node->modifiers) { + if (!modifier) { + throw SerializationException("Failed to deserialize query node: missing result modifier"); + } + if (modifier->type == ResultModifierType::ORDER_MODIFIER) { + for (auto &order : modifier->Cast().orders) { + if (!order.expression) { + throw SerializationException("Failed to deserialize query node: missing ORDER BY expression"); + } + } + } else if (modifier->type == ResultModifierType::DISTINCT_MODIFIER) { + for (auto &target : modifier->Cast().distinct_on_targets) { + if (!target) { + throw SerializationException("Failed to deserialize query node: missing DISTINCT ON expression"); + } + } + } + } + for (auto &cte : node->cte_map.map) { + if (!cte.second || !cte.second->query_node) { + throw SerializationException("Failed to deserialize query node: missing CTE query"); + } + } + switch (node->type) { + case QueryNodeType::SELECT_NODE: { + auto &select_node = node->Cast(); + if (select_node.select_list.empty()) { + throw SerializationException("Failed to deserialize query node: empty select list"); + } + for (auto &expr : select_node.select_list) { + if (!expr) { + throw SerializationException("Failed to deserialize query node: missing select list expression"); + } + } + for (auto &expr : select_node.groups.group_expressions) { + if (!expr) { + throw SerializationException("Failed to deserialize query node: missing group expression"); + } + } + break; + } + case QueryNodeType::RECURSIVE_CTE_NODE: { + auto &cte_node = node->Cast(); + if (!cte_node.left || !cte_node.right) { + throw SerializationException("Failed to deserialize query node: missing recursive CTE child"); + } + break; + } + default: + break; + } +} + } // namespace duckdb diff --git a/src/duckdb/src/parser/query_node/recursive_cte_node.cpp b/src/duckdb/src/parser/query_node/recursive_cte_node.cpp index eb6c1e009..9cf1f760b 100644 --- a/src/duckdb/src/parser/query_node/recursive_cte_node.cpp +++ b/src/duckdb/src/parser/query_node/recursive_cte_node.cpp @@ -9,7 +9,7 @@ string RecursiveCTENode::ToString() const { result += "(" + left->ToString() + ")"; result += " UNION "; if (union_all) { - result += " ALL "; + result += "ALL "; } result += "(" + right->ToString() + ")"; return result; diff --git a/src/duckdb/src/parser/query_node/set_operation_node.cpp b/src/duckdb/src/parser/query_node/set_operation_node.cpp index a6c3808ee..8ec3f43ad 100644 --- a/src/duckdb/src/parser/query_node/set_operation_node.cpp +++ b/src/duckdb/src/parser/query_node/set_operation_node.cpp @@ -87,6 +87,12 @@ SetOperationNode::SetOperationNode(SetOperationType setop_type, unique_ptr 2 && setop_type != SetOperationType::UNION && setop_type != SetOperationType::UNION_BY_NAME) { + throw SerializationException("SetOperationNode must have exactly two children, except for UNION"); + } + if (setop_type == SetOperationType::NONE) { + throw SerializationException("SetOperationNode must have a set operation type"); + } for (auto &child : children) { if (!child) { throw SerializationException("SetOperationNode children cannot be NULL"); diff --git a/src/duckdb/src/parser/query_node/statement_node.cpp b/src/duckdb/src/parser/query_node/statement_node.cpp index 66e7b8e5a..ef7b9f216 100644 --- a/src/duckdb/src/parser/query_node/statement_node.cpp +++ b/src/duckdb/src/parser/query_node/statement_node.cpp @@ -5,6 +5,10 @@ namespace duckdb { StatementNode::StatementNode(SQLStatement &stmt_p) : QueryNode(QueryNodeType::STATEMENT_NODE), stmt(stmt_p) { } +StatementNode::StatementNode(unique_ptr stmt_p) + : QueryNode(QueryNodeType::STATEMENT_NODE), stmt(*stmt_p), owned_statement(std::move(stmt_p)) { +} + //! Convert the query node to a string string StatementNode::ToString() const { return stmt.ToString(); @@ -23,6 +27,9 @@ bool StatementNode::Equals(const QueryNode *other_p) const { //! Create a copy of this SelectNode unique_ptr StatementNode::Copy() const { + if (owned_statement) { + return make_uniq(owned_statement->Copy()); + } return make_uniq(stmt); } diff --git a/src/duckdb/src/parser/statement/explain_statement.cpp b/src/duckdb/src/parser/statement/explain_statement.cpp index dc18e4296..4d1e0f2cd 100644 --- a/src/duckdb/src/parser/statement/explain_statement.cpp +++ b/src/duckdb/src/parser/statement/explain_statement.cpp @@ -10,7 +10,8 @@ ExplainStatement::ExplainStatement(unique_ptr stmt, ExplainType ex } ExplainStatement::ExplainStatement(const ExplainStatement &other) - : SQLStatement(other), stmt(other.stmt->Copy()), explain_type(other.explain_type), format(other.format) { + : SQLStatement(other), stmt(other.stmt->Copy()), explain_type(other.explain_type), + allow_unsupported_sql(other.allow_unsupported_sql), format(other.format) { } unique_ptr ExplainStatement::Copy() const { @@ -18,6 +19,9 @@ unique_ptr ExplainStatement::Copy() const { } string ExplainStatement::OptionsToString() const { + if (explain_type == ExplainType::EXPLAIN_SQL) { + return "(SQL)"; + } string options; if (explain_type == ExplainType::EXPLAIN_ANALYZE) { options += "("; diff --git a/src/duckdb/src/parser/statement/external_resource_statement.cpp b/src/duckdb/src/parser/statement/external_resource_statement.cpp index 3d67ed736..c0f90e494 100644 --- a/src/duckdb/src/parser/statement/external_resource_statement.cpp +++ b/src/duckdb/src/parser/statement/external_resource_statement.cpp @@ -33,12 +33,12 @@ string ExternalResourceStatement::ToString() const { result += " AS " + SQLIdentifier(name); } if (!options.empty()) { - vector stringified; - for (auto &opt : options) { - stringified.push_back( - StringUtil::Format("%s %s", SQLIdentifier(opt.first).ToString(opt.first), opt.second->ToString())); - } - result += " (" + StringUtil::Join(stringified, ", ") + ")"; + result += " ("; + result += StringUtil::Join(options, ", ", [](const auto &opt) { + return StringUtil::Format("%s %s", SQLIdentifier(opt.first).ToString(opt.first), + opt.second->ToString()); + }); + result += ")"; } break; case ExternalResourceOperation::REGISTER: diff --git a/src/duckdb/src/parser/statement/multi_statement.cpp b/src/duckdb/src/parser/statement/multi_statement.cpp index 2821084cd..bf4be6dfe 100644 --- a/src/duckdb/src/parser/statement/multi_statement.cpp +++ b/src/duckdb/src/parser/statement/multi_statement.cpp @@ -16,11 +16,7 @@ unique_ptr MultiStatement::Copy() const { } string MultiStatement::ToString() const { - vector stringified; - for (auto &stmt : statements) { - stringified.push_back(stmt->ToString()); - } - return StringUtil::Join(stringified, ";") + ";"; + return StringUtil::Join(statements, ";", [](const auto &stmt) { return stmt->ToString(); }) + ";"; } } // namespace duckdb diff --git a/src/duckdb/src/parser/statement/passthrough_statement.cpp b/src/duckdb/src/parser/statement/passthrough_statement.cpp new file mode 100644 index 000000000..02408adb9 --- /dev/null +++ b/src/duckdb/src/parser/statement/passthrough_statement.cpp @@ -0,0 +1,16 @@ +#include "duckdb/parser/statement/passthrough_statement.hpp" + +namespace duckdb { + +PassthroughStatement::PassthroughStatement() : SQLStatement(StatementType::PASSTHROUGH_STATEMENT) { +} + +unique_ptr PassthroughStatement::Copy() const { + return unique_ptr(new PassthroughStatement(*this)); +} + +string PassthroughStatement::ToString() const { + return query; +} + +} // namespace duckdb diff --git a/src/duckdb/src/parser/tableref.cpp b/src/duckdb/src/parser/tableref.cpp index 4817d7a01..912dfa179 100644 --- a/src/duckdb/src/parser/tableref.cpp +++ b/src/duckdb/src/parser/tableref.cpp @@ -4,6 +4,8 @@ #include "duckdb/parser/keyword_helper.hpp" #include "duckdb/common/enum_util.hpp" #include "duckdb/common/to_string.hpp" +#include "duckdb/parser/statement/select_statement.hpp" +#include "duckdb/parser/tableref/list.hpp" namespace duckdb { @@ -75,4 +77,57 @@ bool TableRef::Equals(const unique_ptr &left, const unique_ptrEquals(*right); } +void TableRef::VerifyDeserialized() const { + auto invalid = [&](const string &reason) { + return SerializationException("Failed to deserialize %s table reference: %s", EnumUtil::ToString(type), reason); + }; + switch (type) { + case TableReferenceType::JOIN: { + auto &join_ref = Cast(); + if (!join_ref.left || !join_ref.right) { + throw invalid("missing join side"); + } + if (join_ref.ref_type == JoinRefType::REGULAR && !join_ref.condition && join_ref.using_columns.empty()) { + throw invalid("a regular join requires a condition or USING columns"); + } + break; + } + case TableReferenceType::SUBQUERY: { + auto &subquery_ref = Cast(); + if (!subquery_ref.subquery || !subquery_ref.subquery->node) { + throw invalid("missing subquery"); + } + break; + } + case TableReferenceType::PIVOT: { + auto &pivot_ref = Cast(); + if (!pivot_ref.source) { + throw invalid("missing source"); + } + if (pivot_ref.pivots.empty()) { + throw invalid("no PIVOT or UNPIVOT columns"); + } + break; + } + case TableReferenceType::TABLE_FUNCTION: { + auto &function = Cast().function; + if (!function || function->GetExpressionClass() != ExpressionClass::FUNCTION) { + throw invalid("the table function must be a function expression"); + } + break; + } + case TableReferenceType::EXPRESSION_LIST: + for (auto &row : Cast().values) { + for (auto &value : row) { + if (!value) { + throw invalid("missing value"); + } + } + } + break; + default: + break; + } +} + } // namespace duckdb diff --git a/src/duckdb/src/parser/tableref/column_data_ref.cpp b/src/duckdb/src/parser/tableref/column_data_ref.cpp index edaca66a4..068f0cba0 100644 --- a/src/duckdb/src/parser/tableref/column_data_ref.cpp +++ b/src/duckdb/src/parser/tableref/column_data_ref.cpp @@ -53,9 +53,7 @@ optionally_owned_ptr CopyCollection(optionally_owned_ptr(collection->GetAllocator(), collection->Types()); - for (auto &chunk : collection->Chunks()) { - new_collection->Append(chunk); - } + new_collection->Append(*collection); return std::move(new_collection); } auto &shared = collection.get_owned_shared(); diff --git a/src/duckdb/src/parser/tableref/table_function.cpp b/src/duckdb/src/parser/tableref/table_function.cpp index 735623a9a..53b798e6b 100644 --- a/src/duckdb/src/parser/tableref/table_function.cpp +++ b/src/duckdb/src/parser/tableref/table_function.cpp @@ -19,13 +19,21 @@ bool TableFunctionRef::Equals(const TableRef &other_p) const { return false; } auto &other = other_p.Cast(); - return function->Equals(*other.function); + return bind_info == other.bind_info && function->Equals(*other.function); +} + +const unique_ptr &TableFunctionRef::SerializableFunction() const { + if (bind_info) { + throw NotImplementedException("Cannot serialize a table function with process-local bind input"); + } + return function; } unique_ptr TableFunctionRef::Copy() { auto copy = make_uniq(); copy->function = function->Copy(); + copy->bind_info = bind_info; copy->column_name_alias = column_name_alias; copy->with_ordinality = with_ordinality; CopyProperties(*copy); diff --git a/src/duckdb/src/parser/token_iterator.cpp b/src/duckdb/src/parser/token_iterator.cpp index 0d9eab10e..7406e0029 100644 --- a/src/duckdb/src/parser/token_iterator.cpp +++ b/src/duckdb/src/parser/token_iterator.cpp @@ -10,9 +10,16 @@ TokenIterator::TokenIterator(unique_ptr> owned_tokens_p) if (!owned_tokens) { throw InternalException("Cannot construct an owning TokenIterator without tokens"); } + for (auto &token : tokens) { + token.ResetLiteralInfo(); + } } TokenIterator::TokenIterator(vector &tokens_p) : tokens(tokens_p) { + // A new root can receive tokens edited since an earlier match. + for (auto &token : tokens) { + token.ResetLiteralInfo(); + } } TokenIterator::TokenIterator(const TokenIterator &other) : tokens(other.tokens), position(other.position) { diff --git a/src/duckdb/src/planner/bind_context.cpp b/src/duckdb/src/planner/bind_context.cpp index a2d68d4d2..bf8423944 100644 --- a/src/duckdb/src/planner/bind_context.cpp +++ b/src/duckdb/src/planner/bind_context.cpp @@ -557,7 +557,7 @@ string CandidateColumnMessage(const vector &candidate_columns, const void BindContext::GenerateAllColumnExpressions(StarExpression &expr, vector> &new_select_list) { - if (bindings_list.empty()) { + if (bindings_list.empty() && expr.RelationName().empty()) { throw BinderException("* expression without FROM clause!"); } StarBindState star_state(new_select_list); @@ -625,6 +625,21 @@ void BindContext::GenerateAllColumnExpressions(StarExpression &expr, if (!binding) { binding = GetMatchingBinding(expr.RelationName(), expr); if (!binding) { + // Expand the nearest enclosing match into column references. Expression binding then + // resolves their correlation depth, just as it does for explicit qualified columns. + auto &scopes = binder.GetEnclosingScopes(); + for (idx_t i = scopes.size(); i > 0; i--) { + auto &scope_context = scopes[i - 1].get().GetBinder().bind_context; + ErrorData scope_error; + if (scope_context.GetBinding(expr.RelationName(), scope_error) || + scope_context.GetMatchingBinding(expr.RelationName(), expr)) { + scope_context.GenerateAllColumnExpressions(expr, new_select_list); + return; + } + } + if (bindings_list.empty()) { + throw BinderException("* expression without FROM clause!"); + } error.Throw(); } is_struct_ref = true; diff --git a/src/duckdb/src/planner/binder.cpp b/src/duckdb/src/planner/binder.cpp index fcfb5a64f..1eb85f78b 100644 --- a/src/duckdb/src/planner/binder.cpp +++ b/src/duckdb/src/planner/binder.cpp @@ -146,6 +146,11 @@ BoundStatement Binder::Bind(QueryNode &node) { } BoundStatement Binder::Bind(TableRef &ref) { + for (auto &column_alias : ref.column_name_alias) { + if (column_alias.empty()) { + throw BinderException(ref, "column alias cannot be empty"); + } + } BoundStatement result; switch (ref.type) { case TableReferenceType::BASE_TABLE: @@ -230,6 +235,19 @@ void Binder::AddBoundView(ViewCatalogEntry &view) { bound_views.insert(view); } +void Binder::AddBoundTableMacro(CatalogEntry &table_macro) { + // check if the table macro is already being expanded + auto current = this; + while (current) { + if (current->bound_table_macros.find(table_macro) != current->bound_table_macros.end()) { + throw BinderException("infinite recursion detected: attempting to recursively expand table macro %s", + table_macro.name); + } + current = current->parent.get(); + } + bound_table_macros.insert(table_macro); +} + TableIndex Binder::GenerateTableIndex() { return TableIndex(global_binder_state->bound_tables++); } @@ -603,12 +621,7 @@ BoundStatement Binder::BindReturning(vector> return projection->AddChild(std::move(child_operator)); D_ASSERT(result.types.size() == result.names.size()); result.plan = std::move(projection); - // If an insert/delete/update statement returns data, there are sometimes issues with streaming results - // where the data modification doesn't take place until the streamed result is exhausted. Once a row is - // returned, it should be guaranteed that the row has been inserted. - // see https://github.com/duckdb/duckdb/issues/8310 auto &properties = GetStatementProperties(); - properties.output_type = QueryResultOutputType::FORCE_MATERIALIZED; properties.return_type = StatementReturnType::QUERY_RESULT; return result; } diff --git a/src/duckdb/src/planner/binder/expression/bind_aggregate_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_aggregate_expression.cpp index ef6ca7e66..89bf6a80b 100644 --- a/src/duckdb/src/planner/binder/expression/bind_aggregate_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_aggregate_expression.cpp @@ -114,6 +114,10 @@ static void NegatePercentileFractions(ClientContext &context, unique_ptrCast(), func, depth); + } if (inside_try) { throw BinderException("aggregates are not allowed inside the TRY expression"); } @@ -123,6 +127,21 @@ BindResult BaseSelectBinder::BindAggregate(FunctionExpression &aggr, AggregateFu if (inside_aggregate) { throw BinderException(aggr, "aggregate function calls cannot be nested"); } + // an aggregate is computed over the rows of the query, not over the elements an enclosing lambda is applied to - + // so the parameters of the enclosing lambdas can not be referenced in the aggregate + struct InaccessibleLambdaBindings { + InaccessibleLambdaBindings(ExpressionBinder &binder_p, idx_t count) + : binder(binder_p), previous_count(binder.inaccessible_lambda_binding_count) { + binder.inaccessible_lambda_binding_count = count; + } + ~InaccessibleLambdaBindings() { + binder.inaccessible_lambda_binding_count = previous_count; + } + ExpressionBinder &binder; + idx_t previous_count; + }; + InaccessibleLambdaBindings inaccessible_lambdas(*this, lambda_bindings ? lambda_bindings->size() : 0); + // first bind the child of the aggregate expression (if any) this->bound_aggregate = true; unique_ptr bound_filter; diff --git a/src/duckdb/src/planner/binder/expression/bind_columnref_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_columnref_expression.cpp index 23945d2a6..6c57f4cd6 100644 --- a/src/duckdb/src/planner/binder/expression/bind_columnref_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_columnref_expression.cpp @@ -60,6 +60,9 @@ void ExpressionBinder::QualifyColumnNames(ExpressionBinder &expression_binder, u BindResult ExpressionBinder::BindExpression(LambdaRefExpression &lambda_ref, idx_t depth) { D_ASSERT(lambda_bindings && lambda_ref.LambdaIndex() < lambda_bindings->size()); + if (lambda_ref.LambdaIndex() < inaccessible_lambda_binding_count) { + throw BinderException(lambda_ref, "aggregate functions cannot reference lambda parameters"); + } return (*lambda_bindings)[lambda_ref.LambdaIndex()].Bind(lambda_ref, depth); } diff --git a/src/duckdb/src/planner/binder/expression/bind_function_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_function_expression.cpp index 79bd7e184..37c6c38c1 100644 --- a/src/duckdb/src/planner/binder/expression/bind_function_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_function_expression.cpp @@ -106,6 +106,25 @@ struct ListReduceRebindResult { bool override_has_accumulator_type = false; }; +//! Avoid repeated rebinds for DECIMAL type widening by forcing the lambda return type to the chosen accumulator type +//! when decimals are involved +static void CastDecimalLambdaToAccumulatorType(ClientContext &context, BindResult &bind_lambda_result, + const LogicalType &accumulator_type) { + auto &rebound_lambda_expr = bind_lambda_result.expression->Cast(); + const auto return_type = rebound_lambda_expr.LambdaExpr()->GetReturnType(); + if (return_type == accumulator_type || + (!TypeContainsDecimal(accumulator_type) && !TypeContainsDecimal(return_type))) { + return; + } + auto cast_expr = BoundCastExpression::AddCastToType(context, std::move(rebound_lambda_expr.LambdaExprMutable()), + accumulator_type); + if (!cast_expr) { + throw BinderException("Could not cast lambda return type %s to accumulator type %s", return_type.ToString(), + accumulator_type.ToString()); + } + rebound_lambda_expr.LambdaExprMutable() = std::move(cast_expr); +} + ListReduceRebindResult MaybeRebindListReduceLambda(ClientContext &context, idx_t depth, const vector &function_child_types, bind_lambda_function_t bind_lambda_function, @@ -158,22 +177,7 @@ ListReduceRebindResult MaybeRebindListReduceLambda(ClientContext &context, idx_t bind_lambda_result.error.Throw(); } - auto &rebound_lambda_expr = bind_lambda_result.expression->Cast(); - // Avoid repeated rebinds for DECIMAL type widening by forcing the lambda return type to the chosen - // accumulator type when decimals are involved. - if (TypeContainsDecimal(accumulator_type) || - TypeContainsDecimal(rebound_lambda_expr.LambdaExpr()->GetReturnType())) { - if (rebound_lambda_expr.LambdaExpr()->GetReturnType() != accumulator_type) { - const auto old_return_type = rebound_lambda_expr.LambdaExpr()->GetReturnType(); - auto cast_expr = BoundCastExpression::AddCastToType( - context, std::move(rebound_lambda_expr.LambdaExprMutable()), accumulator_type); - if (!cast_expr) { - throw BinderException("Could not cast lambda return type %s to accumulator type %s", - old_return_type.ToString(), accumulator_type.ToString()); - } - rebound_lambda_expr.LambdaExprMutable() = std::move(cast_expr); - } - } + CastDecimalLambdaToAccumulatorType(context, bind_lambda_result, accumulator_type); result.did_rebind = true; result.capture_child_types = std::move(rebind_child_types); } else if (accumulator_type != list_child_type) { @@ -188,6 +192,7 @@ ListReduceRebindResult MaybeRebindListReduceLambda(ClientContext &context, idx_t if (bind_lambda_result.HasError()) { bind_lambda_result.error.Throw(); } + CastDecimalLambdaToAccumulatorType(context, bind_lambda_result, accumulator_type); result.did_rebind = true; result.override_accumulator_type_storage = accumulator_type; result.override_has_accumulator_type = true; @@ -461,7 +466,8 @@ BindResult ExpressionBinder::BindLambdaFunction(FunctionExpression &function, Sc auto &lambda_expr = args[lambda_expr_idx].GetExpressionMutable()->Cast(); unique_ptr lambda_expr_copy; - const bool is_list_reduce = func.name == "list_reduce"; + // the aliases of list_reduce (e.g. reduce) are separate catalog entries + const bool is_list_reduce = func.name == "list_reduce" || func.alias_of == "list_reduce"; if (is_list_reduce) { // We have to make this copy before binding in case we have to double-bind lambda_expr_copy = lambda_expr.Copy(); diff --git a/src/duckdb/src/planner/binder/expression/bind_macro_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_macro_expression.cpp index ca14f9277..5953ec8ba 100644 --- a/src/duckdb/src/planner/binder/expression/bind_macro_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_macro_expression.cpp @@ -7,6 +7,7 @@ #include "duckdb/common/exception/binder_exception.hpp" #include "duckdb/common/unique_ptr.hpp" #include "duckdb/function/scalar_macro_function.hpp" +#include "duckdb/main/client_context.hpp" #include "duckdb/parser/expression/conjunction_expression.hpp" #include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/parser/expression/subquery_expression.hpp" @@ -204,6 +205,8 @@ void ExpressionBinder::UnfoldMacroExpression(FunctionExpression &function, Scala BindResult ExpressionBinder::BindMacro(FunctionExpression &function, ScalarMacroCatalogEntry ¯o_func, idx_t depth, unique_ptr &expr) { auto stack_checker = StackCheck(*expr, 3); + // expanding nested macros can take a long time, allow the query to be interrupted + context.InterruptCheck(); // unfold the macro expression UnfoldMacroExpression(function, macro_func, expr, depth); diff --git a/src/duckdb/src/planner/binder/expression/bind_operator_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_operator_expression.cpp index 6d5f9a6af..0e18cec08 100644 --- a/src/duckdb/src/planner/binder/expression/bind_operator_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_operator_expression.cpp @@ -213,12 +213,11 @@ BindResult ExpressionBinder::BindExpression(OperatorExpression &op, idx_t depth) if (uinteger_value) { // Array extraction: if the cast fails it's definitely out-of-bounds for a JSON array auto index = UIntegerValue::Get(*uinteger_value); - const_exp.GetValueMutable() = StringUtil::Format("$[%lld]", index); + const_exp.SetValue(StringUtil::Format("$[%lld]", index)); const_exp.SetReturnType(LogicalType::VARCHAR); } else if (const_exp.GetReturnType().id() == LogicalType::VARCHAR) { // Field extraction - const_exp.GetValueMutable() = - StringUtil::Format("$.\"%s\"", const_exp.GetValueMutable().ToString()); + const_exp.SetValue(StringUtil::Format("$.\"%s\"", const_exp.GetValue().ToString())); const_exp.SetReturnType(LogicalType::VARCHAR); } } @@ -227,9 +226,8 @@ BindResult ExpressionBinder::BindExpression(OperatorExpression &op, idx_t depth) auto &i_exp = *children[1]; if (i_exp.GetExpressionClass() == ExpressionClass::BOUND_CONSTANT) { auto &const_exp = i_exp.Cast(); - if (!const_exp.GetValueMutable().IsNull() && const_exp.GetReturnType().IsIntegral()) { - const_exp.GetValueMutable() = - const_exp.GetValueMutable().DefaultCastAs(LogicalType::UINTEGER, true); + if (!const_exp.GetValue().IsNull() && const_exp.GetReturnType().IsIntegral()) { + const_exp.SetValue(const_exp.GetValue().DefaultCastAs(LogicalType::UINTEGER, true)); const_exp.SetReturnType(LogicalType::UINTEGER); } } @@ -266,8 +264,8 @@ BindResult ExpressionBinder::BindExpression(OperatorExpression &op, idx_t depth) auto &i_exp = *children[1]; if (i_exp.GetExpressionClass() == ExpressionClass::BOUND_CONSTANT) { auto &const_exp = i_exp.Cast(); - if (!const_exp.GetValueMutable().IsNull()) { - const_exp.GetValueMutable() = StringUtil::Format("%s", const_exp.GetValueMutable().ToString()); + if (!const_exp.GetValue().IsNull()) { + const_exp.SetValue(StringUtil::Format("%s", const_exp.GetValue().ToString())); const_exp.SetReturnType(LogicalType::VARCHAR); } } @@ -276,9 +274,8 @@ BindResult ExpressionBinder::BindExpression(OperatorExpression &op, idx_t depth) // Make sure we only extract fields, not array elements, by adding $. syntax if (name_exp.GetExpressionClass() == ExpressionClass::BOUND_CONSTANT) { auto &const_exp = name_exp.Cast(); - if (!const_exp.GetValueMutable().IsNull()) { - const_exp.GetValueMutable() = - StringUtil::Format("$.\"%s\"", const_exp.GetValueMutable().ToString()); + if (!const_exp.GetValue().IsNull()) { + const_exp.SetValue(StringUtil::Format("$.\"%s\"", const_exp.GetValue().ToString())); const_exp.SetReturnType(LogicalType::VARCHAR); } } diff --git a/src/duckdb/src/planner/binder/expression/bind_positional_reference_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_positional_reference_expression.cpp index fbb27206c..38f4d9dcf 100644 --- a/src/duckdb/src/planner/binder/expression/bind_positional_reference_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_positional_reference_expression.cpp @@ -7,10 +7,9 @@ namespace duckdb { BindResult ExpressionBinder::BindPositionalReference(unique_ptr &expr, idx_t depth, bool root_expression) { auto &ref = expr->Cast(); - if (depth != 0) { - throw InternalException("Positional reference expression could not be bound"); - } - // replace the positional reference with a column + // Resolve the position against this binder, which owns the positions. The reference can reach this point from an + // inner scope through alias expansion, e.g. "SELECT #2 AS x FROM t WHERE (SELECT x)" copies "#2" into the + // subquery; it then binds here at depth > 0 and must be resolved as a correlated reference to the outer position. auto column = binder.bind_context.PositionToColumn(ref); expr = std::move(column); return BindExpression(expr, depth, root_expression); diff --git a/src/duckdb/src/planner/binder/expression/bind_star_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_star_expression.cpp index baa0825c3..f28df17bb 100644 --- a/src/duckdb/src/planner/binder/expression/bind_star_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_star_expression.cpp @@ -1,4 +1,5 @@ #include "duckdb/planner/binder.hpp" +#include "duckdb/planner/table_binding.hpp" #include "duckdb/parser/expression/star_expression.hpp" #include "duckdb/parser/expression/constant_expression.hpp" #include "duckdb/parser/expression/columnref_expression.hpp" @@ -9,6 +10,9 @@ #include "duckdb/function/scalar/regexp.hpp" #include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/parser/expression/lambda_expression.hpp" +#include "duckdb/parser/expression/case_expression.hpp" +#include "duckdb/parser/expression/conjunction_expression.hpp" +#include "duckdb/parser/expression/window_expression.hpp" namespace duckdb { @@ -21,6 +25,20 @@ string GetColumnsStringValue(ParsedExpression &expr) { } } +static const vector &GetFunctionArguments(const ParsedExpression &expr) { + if (expr.GetExpressionClass() == ExpressionClass::WINDOW) { + return expr.Cast().GetArguments(); + } + return expr.Cast().GetArguments(); +} + +static vector &GetFunctionArgumentsMutable(ParsedExpression &expr) { + if (expr.GetExpressionClass() == ExpressionClass::WINDOW) { + return expr.Cast().GetArgumentsMutable(); + } + return expr.Cast().GetArgumentsMutable(); +} + StarExpressionType Binder::FindStarExpression(unique_ptr &expr, StarExpression **star, bool is_root, bool in_columns) { StarExpressionType has_star = StarExpressionType::NONE; @@ -89,7 +107,15 @@ StarExpressionType Binder::FindStarExpression(unique_ptr &expr *star = ¤t_star; has_star = StarExpressionType::STAR; } + // the star of COUNT(tbl.*) is rewritten when the aggregate is bound + optional_ptr count_star; + if (IsQualifiedCountStar(*expr)) { + count_star = &GetFunctionArguments(*expr)[0].GetExpression(); + } ParsedExpressionIterator::EnumerateChildren(*expr, [&](unique_ptr &child_expr) { + if (child_expr.get() == count_star.get()) { + return; + } auto res = FindStarExpression(child_expr, star, false, in_columns); if (res != StarExpressionType::NONE) { has_star = res; @@ -254,6 +280,139 @@ optional_ptr Binder::GetResolvedColumnExpression(ParsedExpress return expr; } +static bool IsQualifiedStar(const ParsedExpression &expr) { + if (!StarExpression::IsStar(expr)) { + return false; + } + auto &star = expr.Cast(); + if (star.RelationName().empty()) { + return false; + } + if (!star.ExcludeList().empty()) { + return false; + } + if (!star.ReplaceList().empty()) { + return false; + } + if (!star.RenameList().empty()) { + return false; + } + return true; +} + +static bool IsCount(const Identifier &function_name, bool distinct) { + return function_name == "count" && !distinct; +} + +bool Binder::IsQualifiedCountStar(const ParsedExpression &expr) { + switch (expr.GetExpressionClass()) { + case ExpressionClass::FUNCTION: { + auto &function = expr.Cast(); + if (!IsCount(function.FunctionName(), function.Distinct())) { + return false; + } + break; + } + case ExpressionClass::WINDOW: { + auto &window = expr.Cast(); + if (!IsCount(window.FunctionName(), window.Distinct())) { + return false; + } + break; + } + default: + return false; + } + auto &arguments = GetFunctionArguments(expr); + return arguments.size() == 1 && IsQualifiedStar(arguments[0].GetExpression()); +} + +//! CASE WHEN col1 IS NOT NULL OR ... OR coln IS NOT NULL THEN 1 END +static unique_ptr AnyColumnNotNull(vector> columns) { + vector> checks; + for (auto &column : columns) { + checks.push_back(make_uniq(ExpressionType::OPERATOR_IS_NOT_NULL, std::move(column))); + } + auto case_expr = make_uniq(); + CaseCheck case_check; + case_check.when_expr = make_uniq(ExpressionType::CONJUNCTION_OR, std::move(checks)); + case_check.then_expr = ConstantExpression::FromValue(Value::BIGINT(1)); + case_expr->CaseChecksMutable().push_back(std::move(case_check)); + case_expr->ElseMutable() = ConstantExpression::FromValue(Value()); + return std::move(case_expr); +} + +static unique_ptr GetRowIdColumn(Binding &binding) { + if (binding.GetBindingType() != BindingType::TABLE) { + return nullptr; + } + auto &virtual_columns = binding.Cast().virtual_columns; + auto entry = virtual_columns.find(COLUMN_IDENTIFIER_ROW_ID); + if (entry == virtual_columns.end()) { + return nullptr; + } + column_t column_index; + if (!binding.TryGetBindingIndex(entry->second.name, column_index) || column_index != COLUMN_IDENTIFIER_ROW_ID) { + // the row id is shadowed by a regular column + return nullptr; + } + return make_uniq(entry->second.name, binding.GetBindingAlias()); +} + +//! Returns the argument that COUNT(tbl.*) counts, or nullptr if every row of tbl is counted +static unique_ptr GetCountStarArgument(StarExpression &star, BindContext &bind_context) { + if (bind_context.GetBindingsList().empty()) { + // nothing to resolve the relation against (e.g. when verifying a macro definition) + return nullptr; + } + ErrorData error; + auto binding = bind_context.GetBinding(star.RelationName(), error); + if (binding) { + if (!binding->IsNullExtended()) { + return nullptr; + } + auto row_id = GetRowIdColumn(*binding); + if (row_id) { + // the row id is only NULL for NULL-extended rows + return row_id; + } + // without a row id, a row in which all columns are NULL is indistinguishable from a NULL-extended row + vector> columns; + bind_context.GenerateAllColumnExpressions(star, columns); + return AnyColumnNotNull(std::move(columns)); + } + // struct.* + auto struct_binding = bind_context.GetMatchingBinding(star.RelationName(), star); + if (!struct_binding) { + error.Throw(); + } + if (!struct_binding->IsNullExtended()) { + return nullptr; + } + return make_uniq(star.RelationName(), struct_binding->GetBindingAlias()); +} + +//! COUNT(tbl.*) counts every row of tbl, except for the rows that an outer join NULL-extended +unique_ptr Binder::TryRewriteQualifiedCountStar(const ParsedExpression &expr) { + if (!IsQualifiedCountStar(expr)) { + return nullptr; + } + auto result = expr.Copy(); + if (result->GetAlias().empty()) { + // the rewrite is an implementation detail - keep the original expression as the name + result->SetAlias(Identifier(expr.ToString())); + } + auto &arguments = GetFunctionArgumentsMutable(*result); + auto &star = arguments[0].GetExpressionMutable()->Cast(); + auto count_argument = GetCountStarArgument(star, bind_context); + if (count_argument) { + arguments[0] = FunctionArgument(std::move(count_argument)); + } else { + arguments.clear(); + } + return result; +} + void Binder::ExpandStarExpression(unique_ptr expr, vector> &new_select_list) { TryTransformStarLike(expr); diff --git a/src/duckdb/src/planner/binder/expression/bind_type_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_type_expression.cpp index 634332130..12c63762b 100644 --- a/src/duckdb/src/planner/binder/expression/bind_type_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_type_expression.cpp @@ -80,6 +80,7 @@ BindResult ExpressionBinder::BindExpression(TypeExpression &type_expr, idx_t dep for (auto ¶m : unbound_parameters) { // Otherwise, try to fold it to a constant value ConstantBinder binder(this->binder, context, StringUtil::Format("Type parameter for type '%s'", type_name)); + binder.ContinueStackDepth(*this); auto expr = param->Copy(); auto bound_expr = binder.Bind(expr); diff --git a/src/duckdb/src/planner/binder/expression/bind_unnest_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_unnest_expression.cpp index da542cbd4..02c930fa2 100644 --- a/src/duckdb/src/planner/binder/expression/bind_unnest_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_unnest_expression.cpp @@ -1,4 +1,6 @@ #include "duckdb/catalog/catalog_entry/aggregate_function_catalog_entry.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" +#include "duckdb/function/scalar/struct_functions.hpp" #include "duckdb/main/client_context.hpp" #include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/planner/expression/bound_aggregate_expression.hpp" @@ -19,12 +21,23 @@ namespace duckdb { +static unique_ptr BindStructExtract(ClientContext &context, vector> arguments, + const LogicalType &child_type) { + auto result = BindBuiltinScalarExpression(context, StructExtractFun::Name, std::move(arguments)); + if (result->GetReturnType() != child_type) { + // a NULL input is folded into an untyped NULL constant + result = BoundCastExpression::AddCastToType(context, std::move(result), child_type); + } + return result; +} + static unique_ptr CreateBoundStructExtract(ClientContext &context, unique_ptr expr, - const vector &key_path, bool keep_parent_names) { + const LogicalType &child_type, const vector &key_path, + bool keep_parent_names) { vector> arguments; arguments.push_back(std::move(expr)); arguments.push_back(make_uniq(Value(key_path.back()))); - auto result = GetKeyExtractFunction().Bind(context, std::move(arguments)); + auto result = BindStructExtract(context, std::move(arguments), child_type); if (keep_parent_names) { auto alias = StringUtil::Join(key_path, "."); @@ -35,18 +48,18 @@ static unique_ptr CreateBoundStructExtract(ClientContext &context, u } else { result->SetAlias(Identifier(key_path[0])); } - return std::move(result); + return result; } static unique_ptr CreateBoundStructExtractIndex(ClientContext &context, unique_ptr expr, - idx_t key) { + const LogicalType &child_type, idx_t key) { vector> arguments; arguments.push_back(std::move(expr)); arguments.push_back(make_uniq(Value::BIGINT(int64_t(key)))); - auto result = GetIndexExtractFunction().Bind(context, std::move(arguments)); + auto result = BindStructExtract(context, std::move(arguments), child_type); result->SetAlias(Identifier("element" + to_string(key))); - return std::move(result); + return result; } void SelectBinder::ThrowIfUnnestInLambda(const ColumnBinding &column_binding) { @@ -151,7 +164,13 @@ BindResult UnnestBinder::Bind(FunctionExpression &function, idx_t depth, bool ro if (error.HasError()) { return BindResult(std::move(error)); } + if (!const_child->IsFoldable()) { + throw BinderException(function, "UNNEST parameter %s must be a constant", alias); + } auto value = ExpressionExecutor::EvaluateScalar(context, *const_child, true); + if (value.IsNull()) { + throw BinderException(function, "UNNEST parameter %s cannot be NULL", alias); + } if (alias == "recursive") { auto recursive = value.GetValue(); if (recursive) { @@ -279,8 +298,8 @@ BindResult UnnestBinder::Bind(FunctionExpression &function, idx_t depth, bool ro auto &child_types = StructType::GetChildTypes(expr->GetReturnType()); if (expr->GetReturnType().id() == LogicalTypeId::TUPLE) { for (idx_t child_index = 0; child_index < child_types.size(); child_index++) { - new_expressions.push_back( - CreateBoundStructExtractIndex(context, expr->Copy(), child_index + 1)); + new_expressions.push_back(CreateBoundStructExtractIndex( + context, expr->Copy(), child_types[child_index].second, child_index + 1)); } } else { for (auto &entry : child_types) { @@ -290,8 +309,8 @@ BindResult UnnestBinder::Bind(FunctionExpression &function, idx_t depth, bool ro current_key_path.emplace_back(expr->GetAlias()); } current_key_path.emplace_back(entry.first); - new_expressions.push_back( - CreateBoundStructExtract(context, expr->Copy(), current_key_path, keep_parent_names)); + new_expressions.push_back(CreateBoundStructExtract(context, expr->Copy(), entry.second, + current_key_path, keep_parent_names)); } } has_structs = true; diff --git a/src/duckdb/src/planner/binder/expression/bind_window_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_window_expression.cpp index c2df11d7b..cc6b5f824 100644 --- a/src/duckdb/src/planner/binder/expression/bind_window_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_window_expression.cpp @@ -10,6 +10,7 @@ #include "duckdb/parser/expression/constant_expression.hpp" #include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/parser/expression/window_expression.hpp" +#include "duckdb/planner/binder.hpp" #include "duckdb/planner/expression/bound_aggregate_expression.hpp" #include "duckdb/planner/expression/bound_cast_expression.hpp" #include "duckdb/planner/expression/bound_columnref_expression.hpp" @@ -48,7 +49,8 @@ static bool IsRangeType(const LogicalType &type) { } static LogicalType BindRangeExpression(ClientContext &context, const string &name, unique_ptr &bound, - unique_ptr &bound_order) { + unique_ptr &bound_order, + optional_ptr> origin = nullptr) { vector> children; D_ASSERT(bound_order); @@ -59,20 +61,23 @@ static LogicalType BindRangeExpression(ClientContext &context, const string &nam if (bound->GetReturnType() == LogicalType::SQLNULL) { throw BinderException(error_context, "Window RANGE expressions cannot be NULL"); } + // the function binder casts the operands and folds a constant offset to its value. The origin recognizes a + // constant offset in the bound call by value, so keep a copy of it - any other offset stays in place + unique_ptr offset_copy = origin && bound->IsFoldable() ? bound->Copy() : nullptr; + optional_ptr offset = offset_copy ? offset_copy.get() : bound.get(); children.emplace_back(std::move(bound)); - ErrorData error; FunctionBinder function_binder(context); - auto function = function_binder.BindScalarFunction(Identifier::DefaultSchema(), Identifier(name), - std::move(children), error, true); - if (!function) { - error.Throw(); - } + auto function = + function_binder.BindScalarFunction(Identifier::DefaultSchema(), Identifier(name), std::move(children), true); // +/- can be applied to non-scalar types, // so we can't rely on function binding to catch all problems. if (!IsRangeType(function->GetReturnType())) { throw BinderException(error_context, "Invalid type for Window RANGE expression"); } + if (origin) { + *origin = WindowRangeBoundary::Capture(*function, *bound_order, *offset); + } bound = std::move(function); return bound->GetReturnType(); } @@ -119,6 +124,10 @@ BindResult BaseSelectBinder::BindWindowExpression(WindowExpression &window, idx_ std::move(window.FilterMutable()), nullptr, window.Distinct()); return BindMacro(*macro, entry->Cast(), depth, macro_expr); } + auto count_star = binder.TryRewriteQualifiedCountStar(window); + if (count_star) { + return BindWindowExpression(count_star->Cast(), depth); + } auto name = window.GetAlias(); @@ -256,7 +265,9 @@ BindResult BaseSelectBinder::BindWindowExpression(WindowExpression &window, idx_ // bind the aggregate FunctionBinder function_binder(binder); - auto window_bound_aggregate = function_binder.BindAggregateFunction(func, std::move(arguments), error, nullptr); + auto window_bound_aggregate = function_binder.BindAggregateFunction( + func, std::move(arguments), error, nullptr, + window.Distinct() ? AggregateType::DISTINCT : AggregateType::NON_DISTINCT); // No function found, throw an error if (!window_bound_aggregate) { error.AddQueryLocation(window); @@ -329,6 +340,15 @@ BindResult BaseSelectBinder::BindWindowExpression(WindowExpression &window, idx_ result->IgnoreNullsMutable() = window.IgnoreNulls(); result->DistinctMutable() = window.Distinct(); + const bool range_start = window.WindowStart() == WindowBoundary::EXPR_PRECEDING_RANGE || + window.WindowStart() == WindowBoundary::EXPR_FOLLOWING_RANGE; + const bool range_end = window.WindowEnd() == WindowBoundary::EXPR_PRECEDING_RANGE || + window.WindowEnd() == WindowBoundary::EXPR_FOLLOWING_RANGE; + if ((range_start || range_end) && bound_orders.size() == 1) { + result->RetainSQLRange(range_start ? bound_start.get() : nullptr, range_end ? bound_end.get() : nullptr, + bound_orders[0]->GetReturnType()); + } + // Convert RANGE boundary expressions to ORDER +/- expressions. // Note that PRECEDING and FOLLOWING refer to the sequential order in the frame, // not the natural ordering of the type. This means that the offset arithmetic must be reversed @@ -340,13 +360,15 @@ BindResult BaseSelectBinder::BindWindowExpression(WindowExpression &window, idx_ D_ASSERT(window.OrderBy().size() == 1); range_sense = config.ResolveOrder(context, window.OrderByMutable()[0].type); const auto range_name = (range_sense == OrderType::ASCENDING) ? "-" : "+"; - start_type = BindRangeExpression(context, range_name, bound_start, bound_orders[0]); + start_type = BindRangeExpression(context, range_name, bound_start, bound_orders[0], + &result->SQLRangeStartBoundaryMutable()); } else if (window.WindowStart() == WindowBoundary::EXPR_FOLLOWING_RANGE) { D_ASSERT(window.OrderBy().size() == 1); range_sense = config.ResolveOrder(context, window.OrderByMutable()[0].type); const auto range_name = (range_sense == OrderType::ASCENDING) ? "+" : "-"; - start_type = BindRangeExpression(context, range_name, bound_start, bound_orders[0]); + start_type = BindRangeExpression(context, range_name, bound_start, bound_orders[0], + &result->SQLRangeStartBoundaryMutable()); } LogicalType end_type = LogicalType::BIGINT; @@ -354,13 +376,24 @@ BindResult BaseSelectBinder::BindWindowExpression(WindowExpression &window, idx_ D_ASSERT(window.OrderBy().size() == 1); range_sense = config.ResolveOrder(context, window.OrderByMutable()[0].type); const auto range_name = (range_sense == OrderType::ASCENDING) ? "-" : "+"; - end_type = BindRangeExpression(context, range_name, bound_end, bound_orders[0]); + end_type = + BindRangeExpression(context, range_name, bound_end, bound_orders[0], &result->SQLRangeEndBoundaryMutable()); } else if (window.WindowEnd() == WindowBoundary::EXPR_FOLLOWING_RANGE) { D_ASSERT(window.OrderBy().size() == 1); range_sense = config.ResolveOrder(context, window.OrderByMutable()[0].type); const auto range_name = (range_sense == OrderType::ASCENDING) ? "+" : "-"; - end_type = BindRangeExpression(context, range_name, bound_end, bound_orders[0]); + end_type = + BindRangeExpression(context, range_name, bound_end, bound_orders[0], &result->SQLRangeEndBoundaryMutable()); + } + + if (result->SQLRangeStartBoundary()) { + result->SQLRangeStartBoundaryMutable()->boundary = window.WindowStart(); + result->SQLRangeStartBoundaryMutable()->direction = range_sense; + } + if (result->SQLRangeEndBoundary()) { + result->SQLRangeEndBoundaryMutable()->boundary = window.WindowEnd(); + result->SQLRangeEndBoundaryMutable()->direction = range_sense; } // Cast ORDER and boundary expressions to the same type @@ -378,7 +411,12 @@ BindResult BaseSelectBinder::BindWindowExpression(WindowExpression &window, idx_ } // Cast all three to match + auto &original_order = *bound_order; bound_order = BoundCastExpression::AddCastToType(context, std::move(bound_order), order_type); + if (!WindowRangeCast::Capture(*bound_order, original_order, result->SQLRangeOrderCastsMutable())) { + result->SQLRangeStartBoundaryMutable().reset(); + result->SQLRangeEndBoundaryMutable().reset(); + } start_type = end_type = order_type; } @@ -398,8 +436,20 @@ BindResult BaseSelectBinder::BindWindowExpression(WindowExpression &window, idx_ } result->FilterMutable() = CastWindowExpression(std::move(bound_filter), LogicalType::BOOLEAN); + optional_ptr original_start = bound_start.get(); + optional_ptr original_end = bound_end.get(); result->StartExprMutable() = CastWindowExpression(std::move(bound_start), start_type); result->EndExprMutable() = CastWindowExpression(std::move(bound_end), end_type); + if (result->SQLRangeStartBoundary() && + !WindowRangeCast::Capture(*result->StartExpr(), *original_start, + result->SQLRangeStartBoundaryMutable()->result_casts)) { + result->SQLRangeStartBoundaryMutable().reset(); + } + if (result->SQLRangeEndBoundary() && + !WindowRangeCast::Capture(*result->EndExpr(), *original_end, + result->SQLRangeEndBoundaryMutable()->result_casts)) { + result->SQLRangeEndBoundaryMutable().reset(); + } result->WindowStartMutable() = window.WindowStart(); result->WindowEndMutable() = window.WindowEnd(); result->WindowExcludeMutable() = window.WindowExclude(); diff --git a/src/duckdb/src/planner/binder/query_node/bind_cte_node.cpp b/src/duckdb/src/planner/binder/query_node/bind_cte_node.cpp index c0709ba68..d798f33ae 100644 --- a/src/duckdb/src/planner/binder/query_node/bind_cte_node.cpp +++ b/src/duckdb/src/planner/binder/query_node/bind_cte_node.cpp @@ -142,6 +142,9 @@ void CTEBindState::Bind(CTEBinding &binding) { // names are picked from the LHS, unless aliases are explicitly specified names = query.names; for (idx_t i = 0; i < aliases.size() && i < names.size(); i++) { + if (aliases[i].empty()) { + throw BinderException("column alias cannot be empty"); + } names[i] = aliases[i]; } diff --git a/src/duckdb/src/planner/binder/query_node/bind_recursive_cte_node.cpp b/src/duckdb/src/planner/binder/query_node/bind_recursive_cte_node.cpp index b9cbdede3..9ce269546 100644 --- a/src/duckdb/src/planner/binder/query_node/bind_recursive_cte_node.cpp +++ b/src/duckdb/src/planner/binder/query_node/bind_recursive_cte_node.cpp @@ -1,4 +1,6 @@ #include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/function/aggregate/distributive_functions.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/planner/expression/bound_aggregate_expression.hpp" #include "duckdb/parser/expression_map.hpp" @@ -40,6 +42,9 @@ BoundStatement Binder::BindNode(RecursiveCTENode &statement) { // names are picked from the LHS, unless aliases are explicitly specified result.names = left.names; for (idx_t i = 0; i < statement.aliases.size() && i < result.names.size(); i++) { + if (statement.aliases[i].empty()) { + throw BinderException("column alias cannot be empty"); + } result.names[i] = statement.aliases[i]; } @@ -99,10 +104,11 @@ BoundStatement Binder::BindNode(RecursiveCTENode &statement) { QueryErrorContext error_context(expr->GetQueryLocation()); - EntryLookupInfo function_lookup(CatalogType::AGGREGATE_FUNCTION_ENTRY, - QualifiedName(func_expr.FunctionName()), error_context); - auto entry = GetCatalogEntry(func_expr.GetQualifiedName().Catalog(), Identifier::DefaultSchema(), - function_lookup, OnEntryNotFound::RETURN_NULL); + auto function_name = func_expr.GetQualifiedName(); + BindSchemaOrCatalog(function_name); + EntryLookupInfo function_lookup(CatalogType::AGGREGATE_FUNCTION_ENTRY, BindTableName(function_name), + error_context); + auto entry = GetCatalogEntry(function_lookup, OnEntryNotFound::RETURN_NULL); if (!entry || entry->type != CatalogType::AGGREGATE_FUNCTION_ENTRY) { throw BinderException( @@ -202,9 +208,9 @@ BoundStatement Binder::BindNode(RecursiveCTENode &statement) { first_children.push_back(std::move(bound)); // Create a last aggregate for the newly bound column reference + auto last_fun = GetBuiltinAggregateFunction(context, LastFun::Name, {result.types[i]}); auto first_aggregate = function_binder.BindAggregateFunction( - LastFunctionGetter::GetFunction(result.types[i]), std::move(first_children), nullptr, - AggregateType::NON_DISTINCT); + std::move(last_fun), std::move(first_children), nullptr, AggregateType::NON_DISTINCT); payload_aggregates.push_back(std::move(first_aggregate)); } else { @@ -282,8 +288,8 @@ BoundStatement Binder::BindNode(RecursiveCTENode &statement) { vector> first_children; first_children.push_back(std::move(uncollated_group)); FunctionBinder function_binder(*this); - auto first = function_binder.BindAggregateFunction(FirstFunctionGetter::GetFunction(group_type), - std::move(first_children)); + auto first_fun = GetBuiltinAggregateFunction(context, FirstFun::Name, {group_type}); + auto first = function_binder.BindAggregateFunction(std::move(first_fun), std::move(first_children)); first->SetAlias("__collated_group"); collated_group_bindings[column_index] = ProjectionIndex(payload_aggregates.size()); payload_aggregates.push_back(std::move(first)); diff --git a/src/duckdb/src/planner/binder/query_node/bind_select_node.cpp b/src/duckdb/src/planner/binder/query_node/bind_select_node.cpp index 938eeb957..27ce506ff 100644 --- a/src/duckdb/src/planner/binder/query_node/bind_select_node.cpp +++ b/src/duckdb/src/planner/binder/query_node/bind_select_node.cpp @@ -4,6 +4,8 @@ #include "duckdb/common/exception/parser_exception.hpp" #include "duckdb/execution/expression_executor.hpp" #include "duckdb/function/aggregate/distributive_function_utils.hpp" +#include "duckdb/function/aggregate/distributive_functions.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/function/function_binder.hpp" #include "duckdb/main/config.hpp" #include "duckdb/parser/expression/columnref_expression.hpp" @@ -631,12 +633,13 @@ BoundStatement Binder::BindSelectNode(SelectNode &statement, BoundStatement from // if there is a collation on a group x, we should group by the collated expr, // but also push a first(x) aggregate in case x is selected (uncollated) - auto first_fun = FirstFunctionGetter::GetFunction(uncollated_expr->GetReturnType()); + auto first_fun = + GetBuiltinAggregateFunction(context, FirstFun::Name, {uncollated_expr->GetReturnType()}); vector> first_children; first_children.push_back(std::move(uncollated_expr)); FunctionBinder function_binder(*this); - auto function = function_binder.BindAggregateFunction(first_fun, std::move(first_children)); + auto function = function_binder.BindAggregateFunction(std::move(first_fun), std::move(first_children)); function->SetAlias("__collated_group"); auto collated_idx = ColumnBinding::PushExpression(result.aggregates, std::move(function)); diff --git a/src/duckdb/src/planner/binder/query_node/bind_setop_node.cpp b/src/duckdb/src/planner/binder/query_node/bind_setop_node.cpp index 3e031872a..870580d3f 100644 --- a/src/duckdb/src/planner/binder/query_node/bind_setop_node.cpp +++ b/src/duckdb/src/planner/binder/query_node/bind_setop_node.cpp @@ -1,8 +1,12 @@ #include "duckdb/parser/expression/columnref_expression.hpp" #include "duckdb/parser/expression/constant_expression.hpp" #include "duckdb/parser/expression_map.hpp" +#include "duckdb/parser/expression/star_expression.hpp" +#include "duckdb/parser/expression/collate_expression.hpp" #include "duckdb/parser/query_node/select_node.hpp" #include "duckdb/parser/query_node/set_operation_node.hpp" +#include "duckdb/parser/statement/select_statement.hpp" +#include "duckdb/parser/tableref/subqueryref.hpp" #include "duckdb/planner/binder.hpp" #include "duckdb/planner/expression/bound_columnref_expression.hpp" #include "duckdb/planner/expression/bound_constant_expression.hpp" @@ -235,7 +239,84 @@ static void GatherSetOpBinders(vector &children, vector().children) { + if (SetOpProvidesOrderExpression(expr, *child)) { + return true; + } + } + return false; + } + if (node.type != QueryNodeType::SELECT_NODE) { + return true; + } + for (auto &select_expr : node.Cast().select_list) { + if (select_expr->Equals(expr)) { + return true; + } + } + return false; +} + +static bool OrderExpressionIsSimple(const ParsedExpression &expr) { + switch (expr.GetExpressionClass()) { + case ExpressionClass::COLUMN_REF: + case ExpressionClass::POSITIONAL_REFERENCE: + case ExpressionClass::CONSTANT: + case ExpressionClass::PARAMETER: + case ExpressionClass::STAR: + return true; + case ExpressionClass::COLLATE: + return OrderExpressionIsSimple(expr.Cast().Child()); + default: + return false; + } +} + +// ORDER BY expressions over the columns of a set operation (e.g. "ORDER BY col IS NULL, col") cannot be pushed into +// every child; they are evaluated over the result instead by binding SELECT * FROM (set operation) ORDER BY ... +static bool SetOpNeedsOrderWrap(const SetOperationNode &statement) { + for (auto &modifier : statement.modifiers) { + if (modifier->type != ResultModifierType::ORDER_MODIFIER) { + continue; + } + for (auto &order : modifier->Cast().orders) { + if (!OrderExpressionIsSimple(*order.expression) && + !SetOpProvidesOrderExpression(*order.expression, statement)) { + return true; + } + } + } + return false; +} + +static unique_ptr WrapSetOpForOrdering(const SetOperationNode &statement) { + auto inner = statement.Copy(); + auto &inner_setop = inner->Cast(); + auto outer = make_uniq(); + vector> inner_modifiers; + for (auto &modifier : inner_setop.modifiers) { + if (modifier->type == ResultModifierType::DISTINCT_MODIFIER) { + inner_modifiers.push_back(std::move(modifier)); + } else { + outer->modifiers.push_back(std::move(modifier)); + } + } + inner_setop.modifiers = std::move(inner_modifiers); + auto subquery = make_uniq(); + subquery->node = std::move(inner); + outer->select_list.push_back(make_uniq()); + outer->from_table = make_uniq(std::move(subquery)); + return outer; +} + BoundStatement Binder::BindNode(SetOperationNode &statement) { + if (SetOpNeedsOrderWrap(statement)) { + auto wrapped = WrapSetOpForOrdering(statement); + return BindNode(*wrapped); + } BoundSetOperationNode result; result.setop_type = statement.setop_type; result.setop_all = statement.setop_all; diff --git a/src/duckdb/src/planner/binder/query_node/bind_table_macro_node.cpp b/src/duckdb/src/planner/binder/query_node/bind_table_macro_node.cpp index 52387f016..5b4e01ca8 100644 --- a/src/duckdb/src/planner/binder/query_node/bind_table_macro_node.cpp +++ b/src/duckdb/src/planner/binder/query_node/bind_table_macro_node.cpp @@ -5,6 +5,7 @@ #include "duckdb/planner/expression_binder.hpp" #include "duckdb/common/string_util.hpp" #include "duckdb/common/limits.hpp" +#include "duckdb/main/client_context.hpp" #include "duckdb/main/config.hpp" #include "duckdb/parser/expression/columnref_expression.hpp" #include "duckdb/parser/expression/comparison_expression.hpp" @@ -18,6 +19,8 @@ namespace duckdb { unique_ptr Binder::BindTableMacro(FunctionExpression &function, TableMacroCatalogEntry ¯o_func, idx_t depth) { + // expanding nested macros can take a long time, allow the query to be interrupted + context.InterruptCheck(); // validate the arguments and separate positional and default arguments vector> positional_arguments; InsertionOrderPreservingMap, Identifier, identifier_map_t> named_arguments; diff --git a/src/duckdb/src/planner/binder/query_node/bind_trigger_expansion.cpp b/src/duckdb/src/planner/binder/query_node/bind_trigger_expansion.cpp index 66281e15e..93461a6c0 100644 --- a/src/duckdb/src/planner/binder/query_node/bind_trigger_expansion.cpp +++ b/src/duckdb/src/planner/binder/query_node/bind_trigger_expansion.cpp @@ -460,7 +460,6 @@ BoundStatement Binder::ExpandTriggers(QueryNode &node, TableCatalogEntry &table, if (!has_returning) { auto &properties = GetStatementProperties(); - properties.output_type = QueryResultOutputType::FORCE_MATERIALIZED; properties.return_type = StatementReturnType::CHANGED_ROWS; return chain; } @@ -533,7 +532,6 @@ BoundStatement Binder::ExpandTriggers(QueryNode &node, TableCatalogEntry &table, result.plan = std::move(returning_projection); auto &properties = GetStatementProperties(); - properties.output_type = QueryResultOutputType::FORCE_MATERIALIZED; properties.return_type = StatementReturnType::QUERY_RESULT; return result; } @@ -720,7 +718,6 @@ BoundStatement Binder::ExpandRowTriggers(QueryNode &node, vector PlanExtremumRewrite(Binder &binder, BoundSubqueryE } min_max_children.push_back(std::move(min_max_child)); - auto extremum_aggr = - function_binder.BindAggregateFunction(is_min ? MinFunction::GetFunction() : MaxFunction::GetFunction(), - std::move(min_max_children), nullptr, AggregateType::NON_DISTINCT); + auto extremum_fun = GetBuiltinAggregateFunction(binder.context, is_min ? MinFun::Name : MaxFun::Name, + {min_max_children[0]->GetReturnType()}); + auto extremum_aggr = function_binder.BindAggregateFunction(std::move(extremum_fun), std::move(min_max_children), + nullptr, AggregateType::NON_DISTINCT); auto extremum_type = extremum_aggr->GetReturnType(); aggregate_list.push_back(std::move(extremum_aggr)); // 2. count_star = COUNT(*) - auto count_star_aggr = - function_binder.BindAggregateFunction(CountStarFun::GetFunction(), {}, nullptr, AggregateType::NON_DISTINCT); + auto count_star_aggr = function_binder.BindAggregateFunction( + GetBuiltinAggregateFunction(binder.context, CountStarFun::Name, {}), {}, nullptr, AggregateType::NON_DISTINCT); aggregate_list.push_back(std::move(count_star_aggr)); // 3. count_child = COUNT(child) vector> count_child_children; count_child_children.push_back(bound_colref->Copy()); - auto count_child_aggr = function_binder.BindAggregateFunction( - CountFunctionBase::GetFunction(), std::move(count_child_children), nullptr, AggregateType::NON_DISTINCT); + auto count_fun = + GetBuiltinAggregateFunction(binder.context, CountFun::Name, {count_child_children[0]->GetReturnType()}); + auto count_child_aggr = function_binder.BindAggregateFunction(std::move(count_fun), std::move(count_child_children), + nullptr, AggregateType::NON_DISTINCT); aggregate_list.push_back(std::move(count_child_aggr)); auto aggr_index = binder.GenerateTableIndex(); @@ -216,11 +220,11 @@ static unique_ptr PlanUncorrelatedSubquery(Binder &binder, BoundSubq plan = std::move(limit); // now we push a COUNT(*) aggregate onto the limit, this will be either 0 or 1 (EXISTS or NOT EXISTS) - auto count_star_fun = CountStarFun::GetFunction(); + auto count_star_fun = GetBuiltinAggregateFunction(binder.context, CountStarFun::Name, {}); FunctionBinder function_binder(binder); auto count_star = - function_binder.BindAggregateFunction(count_star_fun, {}, nullptr, AggregateType::NON_DISTINCT); + function_binder.BindAggregateFunction(std::move(count_star_fun), {}, nullptr, AggregateType::NON_DISTINCT); auto idx_type = count_star->GetReturnType(); vector> aggregate_list; aggregate_list.push_back(std::move(count_star)); @@ -275,15 +279,16 @@ static unique_ptr PlanUncorrelatedSubquery(Binder &binder, BoundSubq first_children.push_back(std::move(bound)); FunctionBinder function_binder(binder); - auto first_agg = - function_binder.BindAggregateFunction(FirstFunctionGetter::GetFunction(expr.GetReturnType()), - std::move(first_children), nullptr, AggregateType::NON_DISTINCT); + auto first_fun = GetBuiltinAggregateFunction(binder.context, FirstFun::Name, {expr.GetReturnType()}); + auto first_agg = function_binder.BindAggregateFunction(std::move(first_fun), std::move(first_children), nullptr, + AggregateType::NON_DISTINCT); expressions.push_back(std::move(first_agg)); if (error_on_multiple_rows) { vector> count_children; auto count_agg = function_binder.BindAggregateFunction( - CountStarFun::GetFunction(), std::move(count_children), nullptr, AggregateType::NON_DISTINCT); + GetBuiltinAggregateFunction(binder.context, CountStarFun::Name, {}), std::move(count_children), nullptr, + AggregateType::NON_DISTINCT); expressions.push_back(std::move(count_agg)); } auto aggr_index = binder.GenerateTableIndex(); @@ -310,7 +315,7 @@ static unique_ptr PlanUncorrelatedSubquery(Binder &binder, BoundSubq Value("More than one row returned by a subquery used as an expression - scalar subqueries can only " "return a single row.\n\nUse \"SET scalar_subquery_error_on_multiple_rows=false\" to revert to " "previous behavior of returning a random row."))); - auto error_expr = function_binder.BindScalarFunction(ErrorFun::GetFunction(), std::move(error_children)); + auto error_expr = BindBuiltinScalarFunction(binder.context, ErrorFun::Name, std::move(error_children)); error_expr->SetReturnType(first_ref->GetReturnType()); auto case_expr = make_uniq(std::move(count_check), std::move(error_expr), std::move(first_ref)); @@ -375,7 +380,7 @@ static unique_ptr PlanUncorrelatedSubquery(Binder &binder, BoundSubq // Create a struct expression from the subquery columns using the "row" function FunctionBinder function_binder(binder); - auto struct_expr = function_binder.BindScalarFunction(RowFun::GetFunction(), std::move(struct_children)); + auto struct_expr = BindBuiltinScalarFunction(binder.context, RowFun::Name, std::move(struct_children)); JoinCondition cond(std::move(expr.GetChildrenMutable()[0]), std::move(struct_expr), expr.ComparisonType()); @@ -703,10 +708,11 @@ BindingReplacementGraph RecursiveDependentJoinPlanner::PlanOperator(unique_ptrchildren.size(); i++) { D_ASSERT(op_ptr->children[i]); - auto old_child_bindings = op_ptr->children[i]->GetColumnBindings(); - auto child_replacements = PlanOperator(op_ptr->children[i]); - ColumnBindingRewrite::ApplyToChild(op_ptr, i, std::move(old_child_bindings), child_replacements); - operator_replacements.Merge(child_replacements); + ColumnBindingRewrite::RewriteChild( + op_ptr, i, [&](unique_ptr &child, BindingReplacementGraph &child_replacements) { + child_replacements = PlanOperator(child); + operator_replacements.Merge(child_replacements); + }); } } ColumnBindingRewrite::ValidateOutput(old_output, op_ptr->GetColumnBindings(), operator_replacements); @@ -718,10 +724,11 @@ BindingReplacementGraph RecursiveDependentJoinPlanner::PlanAnyJoinCondition(uniq BindingReplacementGraph operator_replacements; for (idx_t child_index = 0; child_index < op_ptr->children.size(); child_index++) { - auto old_child_bindings = op_ptr->children[child_index]->GetColumnBindings(); - auto child_replacements = PlanOperator(op_ptr->children[child_index]); - ColumnBindingRewrite::ApplyToChild(op_ptr, child_index, std::move(old_child_bindings), child_replacements); - operator_replacements.Merge(child_replacements); + ColumnBindingRewrite::RewriteChild( + op_ptr, child_index, [&](unique_ptr &child, BindingReplacementGraph &child_replacements) { + child_replacements = PlanOperator(child); + operator_replacements.Merge(child_replacements); + }); } BindingReplacementGraph pair_replacements; @@ -737,10 +744,11 @@ BindingReplacementGraph RecursiveDependentJoinPlanner::PlanAnyJoinCondition(uniq D_ASSERT(join.condition); PlanJoinSubqueries(join, join.condition, JoinSide::LEFT); for (idx_t child_index = 0; child_index < join.children.size(); child_index++) { - auto old_child_bindings = join.children[child_index]->GetColumnBindings(); - auto child_replacements = PlanOperator(join.children[child_index]); - ColumnBindingRewrite::ApplyToChild(op_ptr, child_index, std::move(old_child_bindings), child_replacements); - operator_replacements.Merge(child_replacements); + ColumnBindingRewrite::RewriteChild( + op_ptr, child_index, [&](unique_ptr &child, BindingReplacementGraph &child_replacements) { + child_replacements = PlanOperator(child); + operator_replacements.Merge(child_replacements); + }); } ColumnBindingRewrite::ValidateOutput(old_output, op_ptr->GetColumnBindings(), operator_replacements); return operator_replacements; diff --git a/src/duckdb/src/planner/binder/statement/bind_alter.cpp b/src/duckdb/src/planner/binder/statement/bind_alter.cpp index 725915d7a..7b0d35478 100644 --- a/src/duckdb/src/planner/binder/statement/bind_alter.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_alter.cpp @@ -5,6 +5,9 @@ #include "duckdb/execution/index/art/art.hpp" #include "duckdb/function/table/table_scan.hpp" #include "duckdb/parser/constraints/unique_constraint.hpp" +#include "duckdb/parser/expression/cast_expression.hpp" +#include "duckdb/parser/expression/columnref_expression.hpp" +#include "duckdb/parser/parsed_data/alter_schema_info.hpp" #include "duckdb/parser/parsed_data/comment_on_column_info.hpp" #include "duckdb/parser/statement/alter_statement.hpp" #include "duckdb/parser/tableref/basetableref.hpp" @@ -93,6 +96,11 @@ static void BindAlterTypes(Binder &binder, AlterStatement &stmt) { case AlterTableType::ALTER_COLUMN_TYPE: { auto &alter_column_info = table_info.Cast(); binder.BindLogicalType(alter_column_info.target_type); + if (!alter_column_info.expression) { + // without USING, the column is cast to the target type + alter_column_info.expression = make_uniq( + alter_column_info.target_type, make_uniq(alter_column_info.column_path)); + } } break; default: break; @@ -114,6 +122,18 @@ BoundStatement Binder::Bind(AlterStatement &stmt) { return result; } + if (stmt.info->type == AlterType::ALTER_SCHEMA) { + // resolve the schema path the same way as CREATE SCHEMA does + auto &info = stmt.info->Cast(); + info.SetQualifiedName(ResolveCatalog(context, info.GetQualifiedName())); + auto &catalog = Catalog::GetCatalog(context, info.SchemaCatalog()); + auto &properties = GetStatementProperties(); + properties.return_type = StatementReturnType::NOTHING; + properties.RegisterDBModify(catalog, context, DatabaseModificationType::ALTER_TABLE); + result.plan = make_uniq(std::move(stmt.info)); + return result; + } + // resolve the (possibly nested) catalog/schema qualification of the altered entry stmt.info->SetQualifiedName(BindTableName(stmt.info->GetQualifiedName())); @@ -155,10 +175,7 @@ BoundStatement Binder::Bind(AlterStatement &stmt) { if (catalog.IsSystemCatalog()) { throw BinderException("Can not comment on System Catalog entries"); } - if (!entry->temporary) { - // We can only alter temporary tables and views in read-only mode. - properties.RegisterDBModify(catalog, context, stmt.info->GetModifications()); - } + properties.RegisterDBModify(catalog, context, stmt.info->GetModifications()); stmt.info->SetQualifiedName(entry->ParentSchema().GetQualifiedName(stmt.info->GetQualifiedName().Name())); if (!stmt.info->IsAddUniqueConstraint()) { diff --git a/src/duckdb/src/planner/binder/statement/bind_attach.cpp b/src/duckdb/src/planner/binder/statement/bind_attach.cpp index cff94a894..81718c46e 100644 --- a/src/duckdb/src/planner/binder/statement/bind_attach.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_attach.cpp @@ -1,5 +1,6 @@ #include "duckdb/planner/binder.hpp" #include "duckdb/parser/statement/attach_statement.hpp" +#include "duckdb/parser/parsed_data/external_resource_options.hpp" #include "duckdb/parser/tableref/table_function_ref.hpp" #include "duckdb/planner/operator/logical_attach.hpp" #include "duckdb/planner/expression_binder/table_function_binder.hpp" @@ -7,6 +8,19 @@ namespace duckdb { +void Binder::BindExternalResource(ExternalResourceOptions &external_resource) { + // Reference form (`EXTERNAL RESOURCE `): the resource already exists — the physical operator + // looks it up in the manager at execution. Nothing to resolve here. + if (!external_resource.reference_name.empty()) { + return; + } + + // Create form (`NEW TEMPORARY EXTERNAL RESOURCE '' (opts)`): the type is a string literal set by the + // parser, so only the create params need resolving. + BindExternalResourceParams(external_resource.parsed_params, external_resource.params); + external_resource.parsed_params.clear(); +} + BoundStatement Binder::Bind(AttachStatement &stmt) { BoundStatement result; result.types = {LogicalType::BOOLEAN}; @@ -16,7 +30,7 @@ BoundStatement Binder::Bind(AttachStatement &stmt) { if (stmt.info->parsed_path) { TableFunctionBinder path_binder(*this, context, "Attach", "Attach path"); auto bound_path = path_binder.Bind(stmt.info->parsed_path); - auto path_val = ExpressionExecutor::EvaluateScalar(context, *bound_path); + auto path_val = ExpressionExecutor::EvaluateScalar(context, *bound_path, true); if (path_val.IsNull()) { throw BinderException("ATTACH path expression must not evaluate to NULL"); } @@ -29,7 +43,7 @@ BoundStatement Binder::Bind(AttachStatement &stmt) { unordered_map kv_options; for (auto &entry : stmt.info->parsed_options) { auto bound_expr = option_binder.Bind(entry.second); - auto val = ExpressionExecutor::EvaluateScalar(context, *bound_expr); + auto val = ExpressionExecutor::EvaluateScalar(context, *bound_expr, true); if (val.IsNull()) { throw BinderException("NULL is not supported as a valid option for ATTACH option \"" + entry.first + "\""); } @@ -37,10 +51,14 @@ BoundStatement Binder::Bind(AttachStatement &stmt) { } stmt.info->parsed_options.clear(); + // Bind the external resource clause (ATTACH/CONNECT TO EXTERNAL RESOURCE ...): resolve the type + create params. + if (stmt.info->external_resource) { + BindExternalResource(*stmt.info->external_resource); + } + result.plan = make_uniq(std::move(stmt.info)); auto &properties = GetStatementProperties(); - properties.output_type = QueryResultOutputType::FORCE_MATERIALIZED; properties.return_type = StatementReturnType::NOTHING; return result; } diff --git a/src/duckdb/src/planner/binder/statement/bind_call.cpp b/src/duckdb/src/planner/binder/statement/bind_call.cpp index 32ee6fa34..28e8026a0 100644 --- a/src/duckdb/src/planner/binder/statement/bind_call.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_call.cpp @@ -15,10 +15,8 @@ BoundStatement Binder::Bind(CallStatement &stmt) { select_node->from_table = std::move(table_function); select_statement.node = std::move(select_node); - // CALL is `SELECT * FROM func()` (which already propagates call_return_type) forced to materialize. - auto result = Bind(select_statement); - GetStatementProperties().output_type = QueryResultOutputType::FORCE_MATERIALIZED; - return result; + // CALL is `SELECT * FROM func()`, which already propagates call_return_type + return Bind(select_statement); } } // namespace duckdb diff --git a/src/duckdb/src/planner/binder/statement/bind_connect.cpp b/src/duckdb/src/planner/binder/statement/bind_connect.cpp index e8a237b5e..23823cbba 100644 --- a/src/duckdb/src/planner/binder/statement/bind_connect.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_connect.cpp @@ -19,7 +19,7 @@ BoundStatement Binder::Bind(ConnectStatement &stmt) { TableFunctionBinder option_binder(*this, context, "Connect", "Connect parameter"); for (auto &entry : stmt.info->parsed_options) { auto bound_expr = option_binder.Bind(entry.second); - auto val = ExpressionExecutor::EvaluateScalar(context, *bound_expr); + auto val = ExpressionExecutor::EvaluateScalar(context, *bound_expr, true); if (val.IsNull()) { throw BinderException("NULL is not supported as a valid option for CONNECT option \"" + entry.first + "\""); @@ -29,10 +29,14 @@ BoundStatement Binder::Bind(ConnectStatement &stmt) { stmt.info->parsed_options.clear(); } + // Bind the external resource clause (ATTACH/CONNECT TO EXTERNAL RESOURCE ...): resolve the type + create params. + if (stmt.info->external_resource) { + BindExternalResource(*stmt.info->external_resource); + } + result.plan = make_uniq(std::move(stmt.info)); auto &properties = GetStatementProperties(); - properties.output_type = QueryResultOutputType::FORCE_MATERIALIZED; properties.return_type = StatementReturnType::NOTHING; return result; } @@ -45,7 +49,6 @@ BoundStatement Binder::Bind(DisconnectStatement &stmt) { result.plan = make_uniq(std::move(stmt.info)); auto &properties = GetStatementProperties(); - properties.output_type = QueryResultOutputType::FORCE_MATERIALIZED; properties.return_type = StatementReturnType::NOTHING; return result; } diff --git a/src/duckdb/src/planner/binder/statement/bind_copy.cpp b/src/duckdb/src/planner/binder/statement/bind_copy.cpp index fbc5cb59b..6a8d67aee 100644 --- a/src/duckdb/src/planner/binder/statement/bind_copy.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_copy.cpp @@ -11,6 +11,7 @@ #include "duckdb/main/client_context.hpp" #include "duckdb/main/database.hpp" #include "duckdb/parser/expression/columnref_expression.hpp" +#include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/parser/expression/star_expression.hpp" #include "duckdb/parser/query_node/select_node.hpp" #include "duckdb/parser/statement/copy_statement.hpp" @@ -21,6 +22,7 @@ #include "duckdb/parser/tableref/basetableref.hpp" #include "duckdb/parser/tableref/column_data_ref.hpp" #include "duckdb/planner/binder.hpp" +#include "duckdb/planner/expression/bound_reference_expression.hpp" #include "duckdb/planner/operator/logical_column_data_get.hpp" #include "duckdb/planner/operator/logical_copy_to_file.hpp" #include "duckdb/planner/operator/logical_get.hpp" @@ -90,7 +92,7 @@ identifier_map_t Binder::GetFullCopyOptionsList(const CopyFunction & copy_options["row_group_size_bytes"] = CopyOption(LogicalType::ANY, CopyOptionMode::WRITE_ONLY); copy_options["row_groups_per_file"] = CopyOption(LogicalType::UBIGINT, CopyOptionMode::WRITE_ONLY); copy_options["file_size_bytes"] = CopyOption(LogicalType::ANY, CopyOptionMode::WRITE_ONLY); - copy_options["partition_by"] = CopyOption(LogicalType::ANY, CopyOptionMode::WRITE_ONLY); + copy_options["partition_by"] = CopyOption(LogicalType::LIST(LogicalType::VARCHAR), CopyOptionMode::WRITE_ONLY); copy_options["order_by"] = CopyOption(LogicalType::ANY, CopyOptionMode::WRITE_ONLY); copy_options["return_files"] = CopyOption(LogicalType::BOOLEAN, CopyOptionMode::WRITE_ONLY); copy_options["preserve_order"] = CopyOption(LogicalType::BOOLEAN, CopyOptionMode::WRITE_ONLY); @@ -103,15 +105,30 @@ identifier_map_t Binder::GetFullCopyOptionsList(const CopyFunction & } static idx_t ParseBytesArg(const Identifier &name, Value &arg) { + if (arg.IsNull()) { + throw BinderException("NULL is not supported as a valid option for COPY option \"%s\"", + StringUtil::Upper(name.GetIdentifierName())); + } + optional_idx result; if (arg.type().id() == LogicalTypeId::VARCHAR) { - return DBConfig::ParseMemoryLimit(arg.ToString()); + result = DBConfig::ParseMemoryLimit(arg.ToString()); + } else { + auto cast_arg = arg.DefaultTryCastAs(LogicalType::UBIGINT); + if (!cast_arg) { + throw BinderException("Unable to parse bytes from \"%s\" for copy option \"%s\" ", arg.ToString(), + StringUtil::Upper(name.GetIdentifierName())); + } + auto bytes = cast_arg->GetValue(); + if (bytes != DConstants::INVALID_INDEX) { + result = bytes; + } } - auto cast_arg = arg.DefaultTryCastAs(LogicalType::UBIGINT); - if (!cast_arg) { - throw BinderException("Unable to parse bytes from \"%s\" for copy option \"%s\" ", arg.ToString(), - StringUtil::Upper(name.GetIdentifierName())); + if (!result.IsValid()) { + // e.g. '-1' or 'none', which ParseMemoryLimit parses as unlimited + throw BinderException("Copy option \"%s\" must be a valid size, not \"%s\"", + StringUtil::Upper(name.GetIdentifierName()), arg.ToString()); } - return cast_arg->GetValue(); + return result.GetIndex(); } struct CopyToParsedOptions { @@ -130,6 +147,7 @@ struct CopyToParsedOptions { optional hive_file_pattern; optional preserve_order; optional return_type; + unique_ptr partition_path; bool UserSetUseTmpFile() const { return use_tmp_file.has_value(); @@ -209,6 +227,8 @@ struct CopyToResolvedOptions { bool hive_file_pattern = true; PreserveOrderType preserve_order = PreserveOrderType::AUTOMATIC; CopyFunctionReturnType return_type = CopyFunctionReturnType::CHANGED_ROWS; + //! Unbound, as it is bound against the partition columns + unique_ptr partition_path; bool Rotate() const { return file_size_bytes.IsValid() || batches_per_file.IsValid(); @@ -265,6 +285,7 @@ static CopyToResolvedOptions ResolveCopyToOptions(ClientContext &context, const result.hive_file_pattern = options.HiveFilePattern(); result.preserve_order = options.PreserveOrder(); result.return_type = options.ReturnType(); + result.partition_path = std::move(options.partition_path); return result; } @@ -306,6 +327,14 @@ static void ValidateCopyToOptionCombinations(const CopyToParsedOptions &options, throw NotImplementedException("Can't combine WRITE_EMPTY_FILE false with ORDER BY"); } } + if (options.partition_path) { + if (!options.Partitioned()) { + throw BinderException("PARTITION_PATH requires PARTITION_BY"); + } + if (options.hive_file_pattern.has_value()) { + throw BinderException("Can't combine HIVE_FILE_PATTERN and PARTITION_PATH for COPY"); + } + } if (options.ReturnType() == CopyFunctionReturnType::WRITTEN_FILE_STATISTICS && !function.copy_to_get_written_statistics) { throw NotImplementedException("RETURN_STATS is not supported for the \"%s\" copy format", format); @@ -319,10 +348,70 @@ static void ValidateCopyToOutputColumns(const CopyToResolvedOptions &options, id } } +//! Binds the PARTITION_PATH expression, which may only reference partition columns. Partition column i (in PARTITION_BY +//! order) is bound as reference i. +class PartitionPathBinder : public ExpressionBinder { +public: + PartitionPathBinder(Binder &binder, ClientContext &context, const vector &names, + const vector &types, const vector &partition_columns) + : ExpressionBinder(binder, context), names(names), types(types), partition_columns(partition_columns) { + target_type = LogicalType::VARCHAR; + } + +protected: + BindResult BindExpression(unique_ptr &expr_ptr, idx_t depth, bool root_expression) override { + auto &expr = *expr_ptr; + switch (expr.GetExpressionClass()) { + case ExpressionClass::WINDOW: + return BindResult(BinderException::Unsupported(expr, "window functions are not allowed in PARTITION_PATH")); + case ExpressionClass::SUBQUERY: + return BindResult(BinderException::Unsupported(expr, "cannot use subquery in PARTITION_PATH")); + case ExpressionClass::COLUMN_REF: + return BindColumnReference(expr.Cast()); + default: + return ExpressionBinder::BindExpression(expr_ptr, depth, root_expression); + } + } + + BindResult BindLambdaFunction(FunctionExpression &expr, ScalarFunctionCatalogEntry &function, + idx_t depth) override { + return BindResult(BinderException::Unsupported(expr, "lambda functions are not allowed in PARTITION_PATH")); + } + + string UnsupportedAggregateMessage() override { + return "aggregate functions are not allowed in PARTITION_PATH"; + } + +private: + BindResult BindColumnReference(ColumnRefExpression &col_ref) { + if (col_ref.ColumnNames().size() == 1) { + auto &column_name = col_ref.GetColumnName(); + for (idx_t i = 0; i < partition_columns.size(); i++) { + auto column_idx = partition_columns[i]; + if (names[column_idx] == column_name) { + return BindResult(make_uniq(types[column_idx], i)); + } + } + } + throw BinderException(col_ref, "Column \"%s\" referenced in PARTITION_PATH is not a PARTITION_BY column", + col_ref.ToString()); + } + +private: + const vector &names; + const vector &types; + const vector &partition_columns; +}; + BoundStatement Binder::BindCopyTo(CopyStatement &stmt, const CopyFunction &function, CopyToType copy_to_type) { if (function.plan) { // plan rewrite COPY TO - return function.plan(*this, stmt); + auto result = function.plan(*this, stmt); + // a rewrite that re-binds the COPY consumes PARTITION_PATH + if (stmt.info && stmt.info->parsed_options.find("partition_path") != stmt.info->parsed_options.end()) { + throw NotImplementedException("PARTITION_PATH is not supported for FORMAT \"%s\"", stmt.info->format); + } + return result; } auto ©_info = *stmt.info; @@ -337,6 +426,11 @@ BoundStatement Binder::BindCopyTo(CopyStatement &stmt, const CopyFunction &funct } CopyToParsedOptions parsed_options; + auto partition_path_entry = stmt.info->parsed_options.find("partition_path"); + if (partition_path_entry != stmt.info->parsed_options.end()) { + parsed_options.partition_path = std::move(partition_path_entry->second); + stmt.info->parsed_options.erase(partition_path_entry); + } CopyFunctionBindInput bind_input(*stmt.info, function.function_info); bind_input.file_extension = function.extension; @@ -383,7 +477,11 @@ BoundStatement Binder::BindCopyTo(CopyStatement &stmt, const CopyFunction &funct if (option_values.empty()) { throw BinderException("BATCH_SIZE/ROW_GROUP_SIZE cannot be empty"); } - parsed_options.batch_size = option_values[0].GetValue(); + auto batch_size = option_values[0].GetValue(); + if (batch_size == DConstants::INVALID_INDEX) { + throw BinderException("BATCH_SIZE/ROW_GROUP_SIZE value %llu is too large", batch_size); + } + parsed_options.batch_size = batch_size; } else if (option_name == "batch_size_bytes" || option_name == "row_group_size_bytes") { if (option_values.empty()) { throw BinderException("BATCH_SIZE_BYTES/ROW_GROUP_SIZE_BYTES cannot be empty"); @@ -401,7 +499,11 @@ BoundStatement Binder::BindCopyTo(CopyStatement &stmt, const CopyFunction &funct if (option_values.empty()) { throw BinderException("BATCHES_PER_FILE/ROW_GROUPS_PER_FILE cannot be empty"); } - parsed_options.batches_per_file = option_values[0].GetValue(); + auto batches_per_file = option_values[0].GetValue(); + if (batches_per_file == 0 || batches_per_file == DConstants::INVALID_INDEX) { + throw BinderException("BATCHES_PER_FILE/ROW_GROUPS_PER_FILE must be a positive number"); + } + parsed_options.batches_per_file = batches_per_file; } else if (option_name == "partition_by") { auto converted = ConvertVectorToValue(std::move(option_values)); parsed_options.partition_cols = ParseColumnsOrdered(converted, select_node.names, option_name); @@ -480,6 +582,16 @@ BoundStatement Binder::BindCopyTo(CopyStatement &stmt, const CopyFunction &funct select_node.types, resolved_options.partition_cols, resolved_options.write_partition_columns); auto function_data = function.copy_to_bind(context, bind_input, names_to_write, types_to_write); + unique_ptr partition_path_expression; + if (resolved_options.partition_path) { + PartitionPathBinder partition_path_binder(*this, context, select_node.names, select_node.types, + resolved_options.partition_cols); + partition_path_expression = partition_path_binder.Bind(resolved_options.partition_path); + if (partition_path_expression->HasParameter()) { + throw ParameterNotResolvedException(); + } + } + // now create the copy information auto copy = make_uniq(function, std::move(function_data), std::move(stmt.info), GenerateTableIndex()); @@ -502,6 +614,7 @@ BoundStatement Binder::BindCopyTo(CopyStatement &stmt, const CopyFunction &funct copy->preserve_order = resolved_options.preserve_order; copy->hive_file_pattern = resolved_options.hive_file_pattern; copy->order_columns = std::move(resolved_options.order_columns); + copy->partition_path_expression = std::move(partition_path_expression); copy->names = unique_column_names; copy->expected_types = select_node.types; @@ -603,7 +716,7 @@ BoundStatement Binder::BindCopyFrom(CopyStatement &stmt, const CopyFunction &fun auto copy_from_function = function.copy_from_function; CopyFromFunctionBindInput input(*stmt.info, copy_from_function); auto function_data = function.copy_from_bind(context, input, expected_names, expected_types); - auto get = make_uniq(source_bindings[0].table_index, std::move(copy_from_function), + auto get = make_uniq(source_bindings[0].table_index, BoundTableFunction(copy_from_function), std::move(function_data), expected_types, expected_names); for (idx_t i = 0; i < expected_types.size(); i++) { get->AddColumnId(i); @@ -613,8 +726,9 @@ BoundStatement Binder::BindCopyFrom(CopyStatement &stmt, const CopyFunction &fun return result; } -vector BindCopyOption(ClientContext &context, TableFunctionBinder &option_binder, const Identifier &name, - unique_ptr &expr) { +vector BindCopyOption(ClientContext &context, Binder &binder, const Identifier &name, + unique_ptr &expr, + TableFunctionBinder::IdentifierConversionPolicy conversion_policy) { vector result; if (!expr) { return result; @@ -628,21 +742,11 @@ vector BindCopyOption(ClientContext &context, TableFunctionBinder &option return result; } } - const bool is_partition_by = name == "partition_by"; - - if (is_partition_by) { - //! When binding the 'partition_by' option, we don't want to resolve a column reference to a SQLValueFunction - //! (like 'user') - option_binder.DisableSQLValueFunctions(); - } + TableFunctionBinder option_binder(binder, context, "Copy", "Copy options", conversion_policy); auto bound_expr = option_binder.Bind(expr); if (bound_expr->HasParameter()) { throw ParameterNotResolvedException(); } - if (is_partition_by) { - option_binder.EnableSQLValueFunctions(); - } - auto val = ExpressionExecutor::EvaluateScalar(context, *bound_expr, true); if (val.IsNull()) { throw BinderException("NULL is not supported as a valid option for COPY option \"" + name + "\""); @@ -673,12 +777,12 @@ string ExtractFormat(const string &file_path) { return format.substr(dot_pos + 1); } -void Binder::BindCopyOptions(CopyInfo &info) { - TableFunctionBinder option_binder(*this, context, "Copy", "Copy options"); +void Binder::BindCopyFormat(CopyInfo &info) { + const auto conversion_policy = TableFunctionBinder::IdentifierConversionPolicy::FOLLOW_SETTING; if (info.file_path_expression) { - auto inputs = BindCopyOption(context, option_binder, "filename", info.file_path_expression); + auto inputs = BindCopyOption(context, *this, "filename", info.file_path_expression, conversion_policy); if (inputs.size() != 1 || inputs[0].type().id() != LogicalTypeId::VARCHAR) { - throw InternalException("Unsupported parameter type for filename: expected e.g. TARGET 'file.parquet'"); + throw BinderException("Unsupported parameter type for filename: expected e.g. TARGET 'file.parquet'"); } if (!info.file_path.empty()) { throw InternalException("Both a file path and a file path expression were provided for COPY - only one of " @@ -687,28 +791,55 @@ void Binder::BindCopyOptions(CopyInfo &info) { info.file_path = inputs[0].ToString(); info.file_path_expression.reset(); } - for (auto &[option_name, option_expr] : info.parsed_options) { - auto inputs = BindCopyOption(context, option_binder, option_name, option_expr); - if (option_name == "format") { - // format specifier: interpret this option - if (inputs.size() != 1 || inputs[0].type().id() != LogicalTypeId::VARCHAR) { - throw ParserException("Unsupported parameter type for FORMAT: expected e.g. FORMAT 'csv', 'parquet'"); - } - info.format = StringUtil::Lower(inputs[0].ToString()); - info.is_format_auto_detected = false; - continue; + auto format_entry = info.parsed_options.find("format"); + if (format_entry != info.parsed_options.end()) { + auto inputs = BindCopyOption(context, *this, "format", format_entry->second, conversion_policy); + if (inputs.size() != 1 || inputs[0].type().id() != LogicalTypeId::VARCHAR) { + throw ParserException("Unsupported parameter type for FORMAT: expected e.g. FORMAT 'csv', 'parquet'"); } - info.options[option_name] = std::move(inputs); + info.format = StringUtil::Lower(inputs[0].ToString()); + info.is_format_auto_detected = false; + info.parsed_options.erase(format_entry); } if (info.is_format_auto_detected && info.format.empty()) { info.format = ExtractFormat(info.file_path); } +} + +void Binder::BindCopyOptions(CopyInfo &info, const CopyFunction &function) { + identifier_map_t copy_options; + if (function.copy_options) { + copy_options = GetFullCopyOptionsList(function, CopyOptionMode::READ_WRITE); + } + unique_ptr partition_path; + for (auto &[option_name, option_expr] : info.parsed_options) { + if (option_name == "partition_path") { + // bound later against the partition columns + if (!option_expr) { + throw BinderException("PARTITION_PATH expects an expression"); + } + partition_path = std::move(option_expr); + continue; + } + // column list options accept bare column names, also ones that spell a SQL value function such as user + auto copy_option = copy_options.find(option_name); + const bool is_column_list = + copy_option != copy_options.end() && copy_option->second.type == LogicalType::LIST(LogicalType::VARCHAR); + auto conversion_policy = is_column_list ? TableFunctionBinder::IdentifierConversionPolicy::ALLOW + : TableFunctionBinder::IdentifierConversionPolicy::FOLLOW_SETTING; + info.options[option_name] = BindCopyOption(context, *this, option_name, option_expr, conversion_policy); + } info.parsed_options.clear(); + if (partition_path) { + info.parsed_options["partition_path"] = std::move(partition_path); + } } BoundStatement Binder::Bind(CopyStatement &stmt, CopyToType copy_to_type) { - // bind the copy options - BindCopyOptions(*stmt.info); + BindCopyFormat(*stmt.info); + if (stmt.info->is_from && stmt.info->parsed_options.find("partition_path") != stmt.info->parsed_options.end()) { + throw InvalidInputException("Option partition_path is not supported for reading - only for writing"); + } if (!stmt.info->is_from && !stmt.info->select_statement) { // copy table into file without a query @@ -752,6 +883,8 @@ BoundStatement Binder::Bind(CopyStatement &stmt, CopyToType copy_to_type) { auto ©_function = entry->Cast(); auto &function = copy_function.function; + // bind the copy options + BindCopyOptions(*stmt.info, function); if (function.copy_options) { // list all copy options - then bind them and offer alternatives auto copy_mode = stmt.info->is_from ? CopyOptionMode::READ_ONLY : CopyOptionMode::WRITE_ONLY; @@ -777,7 +910,7 @@ BoundStatement Binder::Bind(CopyStatement &stmt, CopyToType copy_to_type) { stmt.info->is_from ? "reading" : "writing", stmt.info->is_from ? "writing" : "reading"); } - if (copy_option.type.id() != LogicalTypeId::ANY) { + if (copy_option.type.id() != LogicalTypeId::ANY && copy_option.type.id() != LogicalTypeId::LIST) { if (provided_values.empty()) { if (copy_option.type.id() == LogicalTypeId::BOOLEAN) { // boolean can be empty (e.g. "HEADER") @@ -825,7 +958,6 @@ BoundStatement Binder::Bind(CopyStatement &stmt, CopyToType copy_to_type) { } auto &properties = GetStatementProperties(); - properties.output_type = QueryResultOutputType::FORCE_MATERIALIZED; properties.return_type = StatementReturnType::CHANGED_ROWS; if (stmt.info->is_from) { return BindCopyFrom(stmt, function); diff --git a/src/duckdb/src/planner/binder/statement/bind_copy_database.cpp b/src/duckdb/src/planner/binder/statement/bind_copy_database.cpp index 7e872df0a..af07d6889 100644 --- a/src/duckdb/src/planner/binder/statement/bind_copy_database.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_copy_database.cpp @@ -51,14 +51,13 @@ unique_ptr Binder::BindCopyDatabaseSchema(Catalog &from_databas unique_ptr Binder::BindCopyDatabaseData(Catalog &source_catalog, const Identifier &target_database_name) { - auto source_schemas = source_catalog.GetSchemas(context); - - // We can just use ExtractEntries here because the order doesn't matter - ExportEntries entries; - PhysicalExport::ExtractEntries(context, source_schemas, entries); + auto entries = PhysicalExport::GetNaiveExportOrder(context, source_catalog); vector> insert_nodes; - for (auto &table_ref : entries.tables) { + for (auto &table_ref : entries) { + if (table_ref.get().type != CatalogType::TABLE_ENTRY) { + continue; + } auto &table = table_ref.get().Cast(); // generate the insert statement InsertStatement insert_stmt; @@ -128,7 +127,6 @@ BoundStatement Binder::Bind(CopyDatabaseStatement &stmt) { result.plan = std::move(plan); auto &properties = GetStatementProperties(); - properties.output_type = QueryResultOutputType::FORCE_MATERIALIZED; properties.return_type = StatementReturnType::NOTHING; DatabaseModificationType modification; diff --git a/src/duckdb/src/planner/binder/statement/bind_create.cpp b/src/duckdb/src/planner/binder/statement/bind_create.cpp index df25bd1d1..f787194e3 100644 --- a/src/duckdb/src/planner/binder/statement/bind_create.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_create.cpp @@ -13,6 +13,7 @@ #include "duckdb/main/database.hpp" #include "duckdb/main/database_manager.hpp" #include "duckdb/main/secret/secret_manager.hpp" +#include "duckdb/optimizer/remote_pushdown_optimizer.hpp" #include "duckdb/parser/constraints/foreign_key_constraint.hpp" #include "duckdb/parser/constraints/list.hpp" #include "duckdb/parser/constraints/unique_constraint.hpp" @@ -87,7 +88,11 @@ void Binder::BindSchemaOrCatalog(CatalogEntryRetriever &retriever, Identifier &c auto &search_path = retriever.GetSearchPath(); auto catalog_names = search_path.GetCatalogsForSchema(schema); if (catalog_names.empty()) { - catalog_names.emplace_back(DatabaseManager::GetDefaultDatabase(context)); + // with no default database there is no schema for the name to be ambiguous with + auto default_database = DatabaseManager::TryGetDefaultDatabase(context); + if (!IsInvalidCatalog(default_database)) { + catalog_names.emplace_back(std::move(default_database)); + } } for (auto &catalog_name : catalog_names) { auto catalog_ptr = Catalog::GetCatalogEntry(retriever, catalog_name); @@ -180,7 +185,7 @@ void Binder::SearchSchema(CreateInfo &info) { schema_path.push_back(*default_schema); } else if (IsInvalidCatalog(catalog)) { // a schema was given but no catalog: resolve the catalog that holds it - catalog = Identifier(search_path->GetDefaultCatalog(schema_path[0])); + catalog = search_path->ResolveCatalog(schema_path[0]); } if (IsInvalidCatalog(catalog)) { catalog = DatabaseManager::GetDefaultDatabase(context); @@ -192,13 +197,8 @@ void Binder::SearchSchema(CreateInfo &info) { } else if (catalog != TEMP_CATALOG) { throw ParserException("TEMPORARY table names can *only* use the \"%s\" catalog", TEMP_CATALOG); } - // store the resolved name as [catalog, schema_path..., name] - vector resolved_path; - resolved_path.push_back(std::move(catalog)); - for (auto &schema : schema_path) { - resolved_path.push_back(std::move(schema)); - } - info.SetQualifiedName(QualifiedName(std::move(resolved_path), std::move(name))); + info.SetQualifiedName( + QualifiedName::FromCatalogSchema(std::move(catalog), std::move(schema_path), std::move(name))); } QualifiedName Binder::ResolveCatalog(ClientContext &context, const QualifiedName &name, bool default_catalog) { @@ -238,12 +238,7 @@ QualifiedName Binder::ResolveCatalog(CatalogEntryRetriever &retriever, const Qua } if (default_catalog && IsInvalidCatalog(catalog)) { // the leading component (if any) is a schema - resolve the catalog that holds it, else the default database - auto &search_path = retriever.GetSearchPath(); - catalog = - path.empty() ? search_path.GetDefault().GetCatalog() : Identifier(search_path.GetDefaultCatalog(path[0])); - if (IsInvalidCatalog(catalog)) { - catalog = DatabaseManager::GetDefaultDatabase(context); - } + catalog = retriever.GetSearchPath().ResolveCatalog(path.empty() ? Identifier() : path[0]); } path.insert(path.begin(), std::move(catalog)); return QualifiedName(std::move(path), std::move(trailing)); @@ -252,6 +247,9 @@ QualifiedName Binder::ResolveCatalog(CatalogEntryRetriever &retriever, const Qua QualifiedName Binder::BindTableName(CatalogEntryRetriever &retriever, const QualifiedName &name) { auto resolved = ResolveCatalog(retriever, name, false); auto &path = resolved.Path(); + if (path.size() < 2) { + throw InternalException("Binder::BindTableName called with an empty name"); + } vector schema_path(path.begin() + 1, path.end() - 1); if (schema_path.size() <= 1) { // unqualified, or qualified with a single schema level: keep the (catalog, schema, name) shape so that the @@ -262,13 +260,15 @@ QualifiedName Binder::BindTableName(CatalogEntryRetriever &retriever, const Qual // [catalog, schema path..., name] is fully qualified auto catalog = path.front(); if (IsInvalidCatalog(catalog)) { - catalog = Identifier(retriever.GetSearchPath().GetDefaultCatalog(schema_path[0])); - if (IsInvalidCatalog(catalog)) { + EntryLookupInfo schema_lookup(CatalogType::SCHEMA_ENTRY, QualifiedName(schema_path[0])); + auto schema = Catalog::GetSchema(retriever, schema_lookup, OnEntryNotFound::RETURN_NULL); + if (schema) { + catalog = schema->ParentCatalog().GetName(); + } else { catalog = DatabaseManager::GetDefaultDatabase(retriever.GetContext()); } } - schema_path.insert(schema_path.begin(), std::move(catalog)); - return QualifiedName(std::move(schema_path), resolved.Name()); + return QualifiedName::FromCatalogSchema(std::move(catalog), std::move(schema_path), resolved.Name()); } QualifiedName Binder::BindTableName(const QualifiedName &name) { @@ -293,9 +293,13 @@ void Binder::BindCreateSchema(CreateSchemaInfo &info) { // component into a catalog (prepending the default catalog when it is a schema) info.SetQualifiedName(ResolveCatalog(context, info.GetQualifiedName())); + auto &resolved_catalog = Catalog::GetCatalog(context, info.SchemaCatalog()); + auto supports_create_schema = resolved_catalog.SupportsCreateSchema(info); + if (supports_create_schema.HasError()) { + supports_create_schema.Throw(); + } if (info.IsNested()) { // nested schemas can only be persisted with storage version v2.0.0 or higher - auto &resolved_catalog = Catalog::GetCatalog(context, info.SchemaCatalog()); auto &attached = resolved_catalog.GetAttached(); if (attached.HasStorageManager()) { auto &storage_manager = attached.GetStorageManager(); @@ -315,10 +319,7 @@ SchemaCatalogEntry &Binder::BindSchema(CreateInfo &info) { vector schema_path(path.begin() + 1, path.end() - 1); auto &schema_obj = *Catalog::GetSchema(context, path.front(), schema_path, OnEntryNotFound::THROW_EXCEPTION); D_ASSERT(schema_obj.type == CatalogType::SCHEMA_ENTRY); - if (!info.temporary) { - auto &properties = GetStatementProperties(); - properties.RegisterDBModify(schema_obj.catalog, context, info.GetModifications()); - } + GetStatementProperties().RegisterDBModify(schema_obj.catalog, context, info.GetModifications()); return schema_obj; } @@ -365,6 +366,12 @@ void Binder::BindView(ClientContext &context, const SelectStatement &stmt, const } void Binder::BindCreateViewInfo(CreateViewInfo &base) { + // references to the view's own catalog are resolved through the view's search path anyway - drop the qualifier so + // the view keeps working when the database is attached under a different alias + auto &view_catalog = base.GetQualifiedName().Catalog(); + if (base.query && !view_catalog.empty()) { + RemotePushdownOptimizer::StripCatalogName(*base.query, view_catalog); + } if (base.binding_mode == CreateViewBindingMode::SKIP_BINDING) { return; } @@ -842,7 +849,6 @@ BoundStatement Binder::Bind(CreateStatement &stmt) { auto catalog_type = stmt.info->type; auto return_type = StatementReturnType::NOTHING; - auto output_type = QueryResultOutputType::FORCE_MATERIALIZED; auto &properties = GetStatementProperties(); switch (catalog_type) { case CatalogType::SCHEMA_ENTRY: { @@ -1094,7 +1100,6 @@ BoundStatement Binder::Bind(CreateStatement &stmt) { } properties.return_type = return_type; - properties.output_type = output_type; return result; } diff --git a/src/duckdb/src/planner/binder/statement/bind_create_table.cpp b/src/duckdb/src/planner/binder/statement/bind_create_table.cpp index 70f1777e1..7de3e148b 100644 --- a/src/duckdb/src/planner/binder/statement/bind_create_table.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_create_table.cpp @@ -43,6 +43,11 @@ static void VerifyCompressionType(ClientContext &context, optional_ptrCast(); for (auto &col : base.columns.Logical()) { auto compression_type = col.CompressionType(); + if (col.Generated() && compression_type != CompressionType::COMPRESSION_AUTO) { + throw BinderException("Can't set a compression type on generated column %s, generated columns are " + "not stored", + col.Name()); + } auto compression_availability_result = CompressionTypeIsAvailable(compression_type, storage_manager); if (!compression_availability_result.IsAvailable()) { if (compression_availability_result.IsDeprecated()) { @@ -462,7 +467,7 @@ static void FindForeignKeyIndexes(const ColumnList &columns, const vector Binder::BindCreateTableInfo(unique_ptrBase(); base.dependencies = LogicalDependencyList(); auto &dependencies = base.dependencies; + dependencies.AddDependency(schema); auto &catalog = schema.ParentCatalog(); optional_ptr storage_manager; if (catalog.IsDuckCatalog() && !catalog.InMemory()) { @@ -755,8 +761,6 @@ unique_ptr Binder::BindCreateTableInfo(unique_ptrSetQualifiedName(entry->ParentSchema().GetQualifiedName(stmt.info->GetQualifiedName().Name())); - if (!entry->temporary) { - // we can only drop temporary schema entries in read-only mode - properties.RegisterDBModify(entry->ParentCatalog(), context, stmt.info->GetModifications()); - } + properties.RegisterDBModify(entry->ParentCatalog(), context, stmt.info->GetModifications()); break; } case CatalogType::SECRET_ENTRY: { @@ -120,7 +117,6 @@ BoundStatement Binder::Bind(DropStatement &stmt) { result.names = {"Success"}; result.types = {LogicalType::BOOLEAN}; - properties.output_type = QueryResultOutputType::FORCE_MATERIALIZED; properties.return_type = StatementReturnType::NOTHING; return result; } diff --git a/src/duckdb/src/planner/binder/statement/bind_execute.cpp b/src/duckdb/src/planner/binder/statement/bind_execute.cpp index 45863b1c1..b3859c644 100644 --- a/src/duckdb/src/planner/binder/statement/bind_execute.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_execute.cpp @@ -54,11 +54,11 @@ BoundStatement Binder::Bind(ExecuteStatement &stmt) { StringType::GetCollation(constant.GetReturnType()).empty()) { return_type = LogicalTypeId::STRING_LITERAL; } else if (constant.GetReturnType().IsIntegral()) { - return_type = LogicalType::INTEGER_LITERAL(constant.GetValueMutable()); + return_type = LogicalType::INTEGER_LITERAL(constant.GetValue()); } else { - return_type = constant.GetValueMutable().type(); + return_type = constant.GetValue().type(); } - parameter_data = BoundParameterData(std::move(constant.GetValueMutable()), std::move(return_type)); + parameter_data = BoundParameterData(constant.TakeValue(), std::move(return_type)); } else { auto value = ExpressionExecutor::EvaluateScalar(context, *bound_expr, true); auto value_type = value.type(); diff --git a/src/duckdb/src/planner/binder/statement/bind_explain.cpp b/src/duckdb/src/planner/binder/statement/bind_explain.cpp index 6ed696907..0797b4171 100644 --- a/src/duckdb/src/planner/binder/statement/bind_explain.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_explain.cpp @@ -1,4 +1,6 @@ #include "duckdb/planner/binder.hpp" +#include "duckdb/parser/parsed_data/create_table_info.hpp" +#include "duckdb/parser/statement/create_statement.hpp" #include "duckdb/parser/statement/explain_statement.hpp" #include "duckdb/planner/operator/logical_explain.hpp" #include "duckdb/common/tree_renderer.hpp" @@ -7,13 +9,35 @@ namespace duckdb { BoundStatement Binder::Bind(ExplainStatement &stmt) { BoundStatement result; + if (stmt.explain_type == ExplainType::EXPLAIN_SQL) { + switch (stmt.stmt->type) { + case StatementType::SELECT_STATEMENT: + case StatementType::INSERT_STATEMENT: + case StatementType::UPDATE_STATEMENT: + case StatementType::DELETE_STATEMENT: + case StatementType::MERGE_INTO_STATEMENT: + case StatementType::COPY_STATEMENT: + break; + case StatementType::CREATE_STATEMENT: { + auto &info = *stmt.stmt->Cast().info; + if (info.type == CatalogType::TABLE_ENTRY && info.Cast().query) { + break; + } + throw NotImplementedException("EXPLAIN (SQL) supports SELECT, VALUES, WITH, INSERT, UPDATE, DELETE, " + "MERGE INTO, CREATE TABLE ... AS and COPY ... TO statements only"); + } + default: + throw NotImplementedException("EXPLAIN (SQL) supports SELECT, VALUES, WITH, INSERT, UPDATE, DELETE, " + "MERGE INTO, CREATE TABLE ... AS and COPY ... TO statements only"); + } + } // bind the underlying statement auto plan = Bind(*stmt.stmt); // render the unoptimized logical plan, but only when it will be shown: a plain EXPLAIN in a multi-plan format. // (it is unused for EXPLAIN ANALYZE, and single-plan formats like FORMAT WEB render only the final plan) string logical_plan_unopt; - if (stmt.explain_type != ExplainType::EXPLAIN_ANALYZE) { + if (stmt.explain_type == ExplainType::EXPLAIN_STANDARD) { auto renderer = TreeRenderer::CreateRenderer(context, stmt.format); if (!renderer || !renderer->RendersSinglePlan()) { logical_plan_unopt = plan.plan->ToString(context, stmt.format); @@ -21,6 +45,10 @@ BoundStatement Binder::Bind(ExplainStatement &stmt) { } auto explain = make_uniq(std::move(plan.plan), stmt.explain_type, stmt.format); explain->logical_plan_unopt = logical_plan_unopt; + if (stmt.explain_type == ExplainType::EXPLAIN_SQL) { + explain->sql_output_names = std::move(plan.names); + explain->allow_unsupported_sql = stmt.allow_unsupported_sql; + } result.plan = std::move(explain); result.names = {"explain_key", "explain_value"}; diff --git a/src/duckdb/src/planner/binder/statement/bind_export.cpp b/src/duckdb/src/planner/binder/statement/bind_export.cpp index 9d1319fc4..7869d95b1 100644 --- a/src/duckdb/src/planner/binder/statement/bind_export.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_export.cpp @@ -46,10 +46,11 @@ string SanitizeExportIdentifier(const Identifier &str) { return result; } -bool ReferencedTableIsOrdered(const Identifier &referenced_table, catalog_entry_vector_t &ordered) { +bool ReferencedTableIsOrdered(const TableCatalogEntry &referencing_table, const Identifier &referenced_table, + catalog_entry_vector_t &ordered) { for (auto &entry : ordered) { auto &table_entry = entry.get().Cast(); - if (table_entry.name == referenced_table) { + if (&table_entry.ParentSchema() == &referencing_table.ParentSchema() && table_entry.name == referenced_table) { // The referenced table is already ordered return true; } @@ -79,7 +80,7 @@ void ScanForeignKeyTable(catalog_entry_vector_t &ordered, catalog_entry_vector_t // This table references a table, don't move it yet move_to_ordered = false; break; - } else if (!ReferencedTableIsOrdered(fk.info.table, ordered)) { + } else if (!ReferencedTableIsOrdered(table_entry, fk.info.table, ordered)) { // The table that it references isn't ordered yet move_to_ordered = false; break; @@ -134,14 +135,18 @@ static unique_ptr CreateSelectStatement(CopyStatement &stmt, child_li return std::move(statement); } -unique_ptr Binder::UnionOperators(vector> nodes) { +unique_ptr Binder::UnionOperators(vector> nodes, idx_t column_count, + TableIndex table_index) { if (nodes.empty()) { return nullptr; } if (nodes.size() == 1) { return std::move(nodes[0]); } - return make_uniq(GenerateTableIndex(), 1U, std::move(nodes), + if (!table_index.IsValid()) { + table_index = GenerateTableIndex(); + } + return make_uniq(table_index, column_count, std::move(nodes), LogicalOperatorType::LOGICAL_UNION, true, false); } @@ -151,13 +156,14 @@ BoundStatement Binder::Bind(ExportStatement &stmt) { result.types = {LogicalType::BOOLEAN}; result.names = {"Success"}; - // bind copy options - BindCopyOptions(*stmt.info); + BindCopyFormat(*stmt.info); // lookup the format in the catalog auto ©_function = Catalog::GetEntry( context, QualifiedName(Identifier::InvalidCatalog(), Identifier::DefaultSchema(), Identifier(stmt.info->format))); + // bind copy options + BindCopyOptions(*stmt.info, copy_function.function); if (!copy_function.function.copy_to_bind && !copy_function.function.plan) { throw NotImplementedException("COPY TO is not supported for FORMAT \"%s\"", stmt.info->format); } @@ -320,7 +326,6 @@ BoundStatement Binder::Bind(ExportStatement &stmt) { result.plan = std::move(export_node); auto &properties = GetStatementProperties(); - properties.output_type = QueryResultOutputType::FORCE_MATERIALIZED; properties.return_type = StatementReturnType::NOTHING; return result; } diff --git a/src/duckdb/src/planner/binder/statement/bind_external_resource.cpp b/src/duckdb/src/planner/binder/statement/bind_external_resource.cpp index 6dd3e106c..0bbee6fd6 100644 --- a/src/duckdb/src/planner/binder/statement/bind_external_resource.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_external_resource.cpp @@ -12,6 +12,19 @@ namespace duckdb { +Value Binder::BindExternalResourceValue(unique_ptr &expr) { + TableFunctionBinder binder(*this, context, "External resource", "External resource parameter"); + auto bound = binder.Bind(expr); + return ExpressionExecutor::EvaluateScalar(context, *bound, true); +} + +void Binder::BindExternalResourceParams(case_insensitive_map_t> &parsed_params, + unordered_map ¶ms) { + for (auto &entry : parsed_params) { + params[entry.first] = BindExternalResourceValue(entry.second); + } +} + BoundStatement Binder::Bind(ExternalResourceStatement &stmt) { if (stmt.operation == ExternalResourceOperation::SHOW) { // SHOW [ALL] EXTERNAL RESOURCES desugars to `SELECT * FROM duckdb_external_resources(all := )`. @@ -30,21 +43,13 @@ BoundStatement Binder::Bind(ExternalResourceStatement &stmt) { SelectStatement select_statement; select_statement.node = std::move(select_node); - auto show_result = Bind(select_statement); - GetStatementProperties().output_type = QueryResultOutputType::FORCE_MATERIALIZED; - return show_result; + return Bind(select_statement); } BoundStatement result; result.types = {LogicalType::BOOLEAN}; result.names = {"Success"}; - TableFunctionBinder binder(*this, context, "External resource", "External resource parameter"); - auto eval = [&](unique_ptr &expr) { - auto bound = binder.Bind(expr); - return ExpressionExecutor::EvaluateScalar(context, *bound); - }; - BoundExternalResource data; data.operation = stmt.operation; data.name = stmt.name.GetIdentifierName(); @@ -57,11 +62,9 @@ BoundStatement Binder::Bind(ExternalResourceStatement &stmt) { if (stmt.operation != ExternalResourceOperation::DESTROY) { // CREATE / REGISTER: the resource type is a string literal; only the create params need resolving. data.type = stmt.type; - for (auto &entry : stmt.options) { - data.params[entry.first] = eval(entry.second); - } + BindExternalResourceParams(stmt.options, data.params); if (stmt.operation == ExternalResourceOperation::REGISTER) { - data.handle = eval(stmt.handle); + data.handle = BindExternalResourceValue(stmt.handle); if (data.handle.IsNull()) { throw BinderException("REGISTER EXTERNAL RESOURCE: the handle must not be NULL"); } @@ -71,7 +74,6 @@ BoundStatement Binder::Bind(ExternalResourceStatement &stmt) { result.plan = make_uniq(std::move(data)); auto &properties = GetStatementProperties(); - properties.output_type = QueryResultOutputType::FORCE_MATERIALIZED; properties.return_type = StatementReturnType::NOTHING; return result; } diff --git a/src/duckdb/src/planner/binder/statement/bind_insert.cpp b/src/duckdb/src/planner/binder/statement/bind_insert.cpp index 35a35caae..2cea68172 100644 --- a/src/duckdb/src/planner/binder/statement/bind_insert.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_insert.cpp @@ -630,14 +630,7 @@ BoundStatement Binder::BindNode(InsertQueryNode &node) { auto merge_into = GenerateMergeInto(node, table); return Bind(*merge_into); } - if (table.temporary) { - // Temporary inserts still need a catalog dependency so prepared statements are rebound if the table is dropped. - GetStatementProperties().RegisterDBRead(table.catalog, context); - } else { - // inserting into a non-temporary table: alters underlying database - DatabaseModificationType modification_type = DatabaseModificationType::INSERT_DATA; - GetStatementProperties().RegisterDBModify(table.catalog, context, modification_type); - } + GetStatementProperties().RegisterDBModify(table.catalog, context, DatabaseModificationType::INSERT_DATA); auto insert = make_uniq(table, GenerateTableIndex()); @@ -729,7 +722,6 @@ BoundStatement Binder::BindNode(InsertQueryNode &node) { result.plan = std::move(insert); auto &properties = GetStatementProperties(); - properties.output_type = QueryResultOutputType::FORCE_MATERIALIZED; properties.return_type = StatementReturnType::CHANGED_ROWS; return result; } diff --git a/src/duckdb/src/planner/binder/statement/bind_load.cpp b/src/duckdb/src/planner/binder/statement/bind_load.cpp index 8bcf5c90e..f0c8f6d4e 100644 --- a/src/duckdb/src/planner/binder/statement/bind_load.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_load.cpp @@ -31,7 +31,6 @@ BoundStatement Binder::Bind(LoadStatement &stmt) { result.plan = make_uniq(std::move(stmt.info)); auto &properties = GetStatementProperties(); - properties.output_type = QueryResultOutputType::FORCE_MATERIALIZED; properties.return_type = load_type == LoadType::CREATE_REPOSITORY ? StatementReturnType::QUERY_RESULT : StatementReturnType::NOTHING; return result; diff --git a/src/duckdb/src/planner/binder/statement/bind_logical_plan.cpp b/src/duckdb/src/planner/binder/statement/bind_logical_plan.cpp index ad7dbe43b..e2a79696a 100644 --- a/src/duckdb/src/planner/binder/statement/bind_logical_plan.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_logical_plan.cpp @@ -26,7 +26,6 @@ BoundStatement Binder::Bind(LogicalPlanStatement &stmt) { result.plan = std::move(stmt.plan); auto &properties = GetStatementProperties(); - properties.output_type = QueryResultOutputType::ALLOW_STREAMING; properties.return_type = StatementReturnType::QUERY_RESULT; // TODO could also be something else if (parent) { diff --git a/src/duckdb/src/planner/binder/statement/bind_merge_into.cpp b/src/duckdb/src/planner/binder/statement/bind_merge_into.cpp index b034a266c..20b47314c 100644 --- a/src/duckdb/src/planner/binder/statement/bind_merge_into.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_merge_into.cpp @@ -3,6 +3,7 @@ #include "duckdb/parser/query_node/merge_query_node.hpp" #include "duckdb/planner/tableref/bound_joinref.hpp" #include "duckdb/planner/operator/logical_get.hpp" +#include "duckdb/planner/operator/logical_join.hpp" #include "duckdb/planner/expression_binder/where_binder.hpp" #include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp" #include "duckdb/planner/operator/logical_merge_into.hpp" @@ -185,6 +186,39 @@ void RewriteMergeBindings(unique_ptr &expr, const vector FindMergeJoin(LogicalOperator &op, LogicalOperator &source, + LogicalOperator &target, idx_t &source_idx) { + if (op.children.size() == 2) { + for (idx_t child_idx = 0; child_idx < 2; child_idx++) { + if (ContainsOperator(*op.children[child_idx], source) && + ContainsOperator(*op.children[1 - child_idx], target)) { + source_idx = child_idx; + return op; + } + } + } + for (auto &child : op.children) { + auto result = FindMergeJoin(*child, source, target, source_idx); + if (result) { + return result; + } + } + return nullptr; +} + void RewriteMergeBindings(LogicalOperator &op, const vector &source_bindings, TableIndex new_table_index) { LogicalOperatorVisitor::EnumerateExpressions( @@ -231,30 +265,26 @@ BoundStatement Binder::BindNode(MergeQueryNode &node) { throw NotImplementedException("MERGE INTO is not supported on tables with triggers"); } - if (!table.temporary) { - // update of persistent table: not read only! - auto &properties = GetStatementProperties(); - // modification type depends on actions - DatabaseModificationType modification; - for (auto &action_condition : node.actions) { - for (auto &action : action_condition.second) { - switch (action->action_type) { - case MergeActionType::MERGE_UPDATE: - modification |= DatabaseModificationType::UPDATE_DATA; - break; - case MergeActionType::MERGE_DELETE: - modification |= DatabaseModificationType::DELETE_DATA; - break; - case MergeActionType::MERGE_INSERT: - modification |= DatabaseModificationType::INSERT_DATA; - break; - default: - break; - } + // modification type depends on actions + DatabaseModificationType modification; + for (auto &action_condition : node.actions) { + for (auto &action : action_condition.second) { + switch (action->action_type) { + case MergeActionType::MERGE_UPDATE: + modification |= DatabaseModificationType::UPDATE_DATA; + break; + case MergeActionType::MERGE_DELETE: + modification |= DatabaseModificationType::DELETE_DATA; + break; + case MergeActionType::MERGE_INSERT: + modification |= DatabaseModificationType::INSERT_DATA; + break; + default: + break; } } - properties.RegisterDBModify(table.catalog, context, modification); } + GetStatementProperties().RegisterDBModify(table.catalog, context, modification); // bind the source auto source_binder = Binder::CreateBinder(context, this); @@ -274,6 +304,7 @@ BoundStatement Binder::BindNode(MergeQueryNode &node) { // bind the WHEN NOT MATCHED BY SOURCE / TARGET merge actions auto &get = bound_table.plan->Cast(); auto merge_into = make_uniq(table); + merge_into->return_chunk = !node.returning_list.empty(); merge_into->table_index = GenerateTableIndex(); auto proj_index = GenerateTableIndex(); vector> projection_expressions; @@ -319,6 +350,7 @@ BoundStatement Binder::BindNode(MergeQueryNode &node) { } else { join.type = JoinType::INNER; } + auto &source_op = *source_binding.plan; join.left = make_uniq(std::move(source_binding), std::move(source_binder)); join.right = make_uniq(std::move(bound_table), std::move(target_binder)); if (node.join_condition) { @@ -329,22 +361,16 @@ BoundStatement Binder::BindNode(MergeQueryNode &node) { auto bound_join_node = Bind(join); auto root = std::move(bound_join_node.plan); - auto join_ref = reference(*root); - while (join_ref.get().children.size() == 1) { - join_ref = *join_ref.get().children[0]; - } - if (join_ref.get().children.size() != 2) { - throw NotImplementedException("Expected a join after binding a join operator - but got a %s", - join_ref.get().type); + idx_t source_idx = 0; + auto merge_join = FindMergeJoin(*root, source_op, get, source_idx); + if (!merge_join) { + throw NotImplementedException("Expected a join after binding a join operator - but got a %s", root->type); } - // kind of hacky, CreatePlan turns a RIGHT join into a LEFT join so the children get reversed from what we need - bool inverted = join.type == JoinType::RIGHT; + auto join_ref = reference(*merge_join); + // CreatePlan can turn a RIGHT join into a LEFT join - in that case the children are reversed + bool inverted = source_idx == 1; auto &source = join_ref.get().children[inverted ? 1 : 0]; - if (!node.returning_list.empty()) { - merge_into->return_chunk = true; - } - // bind WHEN_MATCHED merge actions (can contain references to both source and target) for (auto &entry : node.actions) { if (entry.first != MergeActionCondition::WHEN_MATCHED) { @@ -368,24 +394,15 @@ BoundStatement Binder::BindNode(MergeQueryNode &node) { // if we have "has_not_matched_by_source" we need to push an extra marker into the source // this marker tells us if we have found a source match or not auto new_proj_index = GenerateTableIndex(); - - source->ResolveOperatorTypes(); auto source_bindings = source->GetColumnBindings(); - vector> select_list; - for (idx_t c = 0; c < source_bindings.size(); c++) { - select_list.push_back(make_uniq(source->types[c], source_bindings[c])); - } + auto proj = LogicalProjection::CreateIdentity(new_proj_index, std::move(source)); // insert the source marker auto marker = make_uniq(Value::INTEGER(42)); marker->SetAlias("source_marker"); ColumnBinding source_marker; - auto source_marker_idx = ColumnBinding::PushExpression(select_list, std::move(marker)); + auto source_marker_idx = ColumnBinding::PushExpression(proj->expressions, std::move(marker)); source_marker = ColumnBinding(new_proj_index, source_marker_idx); - - // construct the new projection - auto proj = make_uniq(new_proj_index, std::move(select_list)); - proj->children.push_back(std::move(source)); source = std::move(proj); // rewrite "column_bindings" in the join to refer to the new projection we have just pushed @@ -463,7 +480,6 @@ BoundStatement Binder::BindNode(MergeQueryNode &node) { result.types = {LogicalType::BIGINT}; auto &properties = GetStatementProperties(); - properties.output_type = QueryResultOutputType::FORCE_MATERIALIZED; properties.return_type = StatementReturnType::CHANGED_ROWS; return result; } diff --git a/src/duckdb/src/planner/binder/statement/bind_pragma.cpp b/src/duckdb/src/planner/binder/statement/bind_pragma.cpp index 3e0b36adc..69af2a1a5 100644 --- a/src/duckdb/src/planner/binder/statement/bind_pragma.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_pragma.cpp @@ -11,21 +11,17 @@ namespace duckdb { unique_ptr Binder::BindPragma(PragmaInfo &info, QueryErrorContext error_context) { - vector params; - named_parameter_map_t named_parameters; + // the arguments are bound here and folded to constants only after the overload has been chosen, so that a + // literal keeps its literal type for overload selection - as for every other function kind + vector> positional_arguments; + vector>> named_arguments; - // resolve the parameters ConstantBinder pragma_binder(*this, context, "PRAGMA value"); for (auto ¶m : info.parameters) { - auto bound_value = pragma_binder.Bind(param); - auto value = ExpressionExecutor::EvaluateScalar(context, *bound_value, true); - params.push_back(std::move(value)); + positional_arguments.push_back(pragma_binder.Bind(param)); } - for (auto &entry : info.named_parameters) { - auto bound_value = pragma_binder.Bind(entry.second); - auto value = ExpressionExecutor::EvaluateScalar(context, *bound_value, true); - named_parameters.insert(make_pair(entry.first, std::move(value))); + named_arguments.emplace_back(Identifier(entry.first), pragma_binder.Bind(entry.second)); } // bind the pragma function @@ -51,15 +47,17 @@ unique_ptr Binder::BindPragma(PragmaInfo &info, QueryErrorConte FunctionBinder function_binder(*this); ErrorData error; - auto bound_idx = function_binder.BindFunction(entry->name, entry->functions, params, error); + // selection, folding, casting and named-argument checking all happen in the function binder + vector params; + named_argument_map_t named_parameters; + auto bound_idx = function_binder.BindFunction(entry->name, entry->functions, positional_arguments, named_arguments, + params, named_parameters, error); if (!bound_idx.IsValid()) { D_ASSERT(error.HasError()); error.AddQueryLocation(error_context); error.Throw(); } auto bound_function = *entry->functions.GetFunctionByOffset(bound_idx.GetIndex()); - // bind and check named params - BindNamedParameters(bound_function.named_parameters, named_parameters, error_context, bound_function.name); return make_uniq(std::move(bound_function), std::move(params), std::move(named_parameters)); } diff --git a/src/duckdb/src/planner/binder/statement/bind_prepare.cpp b/src/duckdb/src/planner/binder/statement/bind_prepare.cpp index 4c0579726..38d1d419b 100644 --- a/src/duckdb/src/planner/binder/statement/bind_prepare.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_prepare.cpp @@ -20,7 +20,6 @@ BoundStatement Binder::Bind(PrepareStatement &stmt) { // this is required because most clients ALWAYS invoke prepared statements auto &properties = GetStatementProperties(); properties.requires_valid_transaction = false; - properties.output_type = QueryResultOutputType::FORCE_MATERIALIZED; properties.bound_all_parameters = true; properties.parameter_count = 0; properties.return_type = StatementReturnType::NOTHING; diff --git a/src/duckdb/src/planner/binder/statement/bind_select.cpp b/src/duckdb/src/planner/binder/statement/bind_select.cpp index f878587f9..0085a19b8 100644 --- a/src/duckdb/src/planner/binder/statement/bind_select.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_select.cpp @@ -7,7 +7,6 @@ namespace duckdb { BoundStatement Binder::Bind(SelectStatement &stmt) { auto &properties = GetStatementProperties(); - properties.output_type = QueryResultOutputType::ALLOW_STREAMING; properties.return_type = StatementReturnType::QUERY_RESULT; auto result = Bind(*stmt.node); // A bare table-function passthrough honors the function's call_return_type (e.g. a CONNECT-routed diff --git a/src/duckdb/src/planner/binder/statement/bind_update.cpp b/src/duckdb/src/planner/binder/statement/bind_update.cpp index 9b146b998..01ce1e195 100644 --- a/src/duckdb/src/planner/binder/statement/bind_update.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_update.cpp @@ -50,10 +50,10 @@ void Binder::BindUpdateSet(TableIndex proj_index, unique_ptr &r } auto &column = table.GetColumn(colname); if (column.Generated()) { - throw BinderException("Cant update column \"%s\" because it is a generated column!", column.Name()); + throw BinderException("Cant update column %s because it is a generated column!", column.Name()); } if (std::find(columns.begin(), columns.end(), column.Physical()) != columns.end()) { - throw BinderException("Multiple assignments to same column \"%s\"", colname); + throw BinderException("Multiple assignments to same column %s", colname); } columns.push_back(column.Physical()); if (expr->GetExpressionType() == ExpressionType::VALUE_DEFAULT) { @@ -190,11 +190,7 @@ BoundStatement Binder::BindNode(UpdateQueryNode &node) { get = &root->Cast(); } - if (!table.temporary) { - // update of persistent table: not read only! - auto &properties = GetStatementProperties(); - properties.RegisterDBModify(table.catalog, context, DatabaseModificationType::UPDATE_DATA); - } + GetStatementProperties().RegisterDBModify(table.catalog, context, DatabaseModificationType::UPDATE_DATA); auto update = make_uniq(table); // Trigger expansion flags its generated base UPDATE for OLD capture via scoped binder state (keyed by node @@ -285,7 +281,6 @@ BoundStatement Binder::BindNode(UpdateQueryNode &node) { result.plan = std::move(update); auto &properties = GetStatementProperties(); - properties.output_type = QueryResultOutputType::FORCE_MATERIALIZED; properties.return_type = StatementReturnType::CHANGED_ROWS; return result; } diff --git a/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp b/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp index aad01499b..46ecedf6b 100644 --- a/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp +++ b/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp @@ -62,8 +62,8 @@ BoundStatement Binder::BindWithReplacementScan(ClientContext &context, BaseTable // Then the database-wide ones, including the built-in file scans if (!replacement_function) { - for (const auto &scan : DBConfig::GetConfig(context).replacement_scans) { - if (auto result = scan.function(context, input, scan.data.get())) { + for (const auto &scan : DBConfig::GetConfig(context).replacement_scans.Snapshot()) { + if (auto result = scan->function(context, input, scan->data.get())) { replacement_function = std::move(result); break; } @@ -213,9 +213,7 @@ BoundStatement Binder::Bind(BaseTableRef &ref) { } // Try autoloading an extension, then retry the replacement scan bind - auto full_path = ReplacementScan::GetFullPath(ref.GetQualifiedName().Catalog().GetIdentifierName(), - ref.GetQualifiedName().Schema().GetIdentifierName(), - ref.Table().GetIdentifierName()); + auto full_path = ReplacementScan::GetFullPath(ref.GetQualifiedName()); auto extension_loaded = TryLoadExtensionForReplacementScan(context, full_path); if (extension_loaded) { replacement_scan_bind_result = BindWithReplacementScan(context, ref); @@ -285,8 +283,11 @@ BoundStatement Binder::Bind(BaseTableRef &ref) { virtual_columns = table.GetVirtualColumns(); } auto logical_get = - make_uniq(table_index, scan_function, std::move(bind_data), std::move(return_types), - std::move(return_names), std::move(virtual_columns)); + make_uniq(table_index, BoundTableFunction(scan_function), std::move(bind_data), + std::move(return_types), std::move(return_names), std::move(virtual_columns)); + if (entry_at_clause) { + logical_get->at_clause = make_uniq(entry_at_clause->Unit(), entry_at_clause->GetValue()); + } auto table_entry = logical_get->GetTable(); auto &col_ids = logical_get->GetMutableColumnIds(); if (!table_entry) { @@ -303,6 +304,8 @@ BoundStatement Binder::Bind(BaseTableRef &ref) { case CatalogType::VIEW_ENTRY: { // the node is a view: get the query that the view represents auto &view_catalog_entry = table_or_view->Cast(); + // expanding nested views can take a long time, allow the query to be interrupted + context.InterruptCheck(); // We need to use a new binder for the view that doesn't reference any CTEs // defined for this binder so there are no collisions between the CTEs defined // for the view and for the current query @@ -344,8 +347,10 @@ BoundStatement Binder::Bind(BaseTableRef &ref) { auto root_index = bound_child.plan->GetRootIndex(); if (view_catalog_entry.security_type == ViewSecurityType::SECURE_VIEW) { // wrap the plan of a secure view - this prevents the optimizer from pushing into the view - bound_child.plan = - make_uniq(view_catalog_entry.name.GetIdentifierName(), std::move(bound_child.plan)); + bound_child.plan = make_uniq( + view_catalog_entry.name.GetIdentifierName(), + view_catalog_entry.ParentSchema().GetQualifiedName(view_catalog_entry.name), bound_child.types, + entry_at_clause, std::move(bound_child.plan)); } bind_context.AddView(root_index, subquery.alias, subquery, bound_child, view_catalog_entry); return bound_child; diff --git a/src/duckdb/src/planner/binder/tableref/bind_expressionlistref.cpp b/src/duckdb/src/planner/binder/tableref/bind_expressionlistref.cpp index 40722ec1e..b26661ad4 100644 --- a/src/duckdb/src/planner/binder/tableref/bind_expressionlistref.cpp +++ b/src/duckdb/src/planner/binder/tableref/bind_expressionlistref.cpp @@ -2,13 +2,63 @@ #include "duckdb/parser/tableref/expressionlistref.hpp" #include "duckdb/planner/expression_binder/insert_binder.hpp" #include "duckdb/common/to_string.hpp" +#include "duckdb/logging/logger.hpp" #include "duckdb/planner/expression/bound_cast_expression.hpp" #include "duckdb/planner/operator/logical_expression_get.hpp" #include "duckdb/planner/operator/logical_dummy_scan.hpp" namespace duckdb { +static void WarnIfDecimalScaleIsReduced(ClientContext &context, const LogicalType &source_type, + const LogicalType &target_type, idx_t column_idx, + vector &warned_about_scale_reduction) { + if (warned_about_scale_reduction[column_idx]) { + return; + } + if (source_type.id() != LogicalTypeId::DECIMAL || target_type.id() != LogicalTypeId::DECIMAL) { + return; + } + auto source_scale = DecimalType::GetScale(source_type); + auto target_scale = DecimalType::GetScale(target_type); + if (source_scale <= target_scale) { + return; + } + DUCKDB_LOG_WARNING(context, + "Potential loss of decimal precision while resolving a VALUES column: type %s is being cast " + "to %s, reducing the scale from %d to %d. This may cause values to be rounded. Explicitly " + "cast all values to a compatible DECIMAL type to avoid this warning.", + source_type.ToString(), target_type.ToString(), source_scale, target_scale); + warned_about_scale_reduction[column_idx] = true; +} + +static void VerifyExpressionListRef(const ExpressionListRef &expr) { + // the parser guarantees these, but a deserialized ExpressionListRef might not + if (expr.values.empty()) { + return; + } + auto column_count = expr.values[0].size(); + for (auto &expression_list : expr.values) { + if (expression_list.size() != column_count) { + throw BinderException("VALUES lists must all be the same length"); + } + } + if (!expr.expected_types.empty() && expr.expected_types.size() != column_count) { + throw BinderException("VALUES list has %d columns but %d expected types", column_count, + expr.expected_types.size()); + } + if (!expr.expected_names.empty() && expr.expected_names.size() != column_count) { + throw BinderException("VALUES list has %d columns but %d expected names", column_count, + expr.expected_names.size()); + } + for (auto &type : expr.expected_types) { + if (type.IsValid() && !type.IsComplete()) { + throw BinderException("VALUES list has an incomplete expected type %s", type.ToString()); + } + } +} + BoundStatement Binder::Bind(ExpressionListRef &expr) { + VerifyExpressionListRef(expr); BoundStatement result; result.types = expr.expected_types; result.names = expr.expected_names; @@ -85,14 +135,17 @@ BoundStatement Binder::Bind(ExpressionListRef &expr) { type = LogicalType::NormalizeType(type); } // finally do another loop over the expressions and add casts where required + vector warned_about_scale_reduction(result.types.size(), false); for (idx_t list_idx = 0; list_idx < values.size(); list_idx++) { auto &list = values[list_idx]; for (idx_t val_idx = 0; val_idx < list.size(); val_idx++) { if (!should_infer[val_idx]) { continue; } - list[val_idx] = - BoundCastExpression::AddCastToType(context, std::move(list[val_idx]), result.types[val_idx]); + auto source_type = ExpressionBinder::GetExpressionReturnType(*list[val_idx]); + auto &target_type = result.types[val_idx]; + WarnIfDecimalScaleIsReduced(context, source_type, target_type, val_idx, warned_about_scale_reduction); + list[val_idx] = BoundCastExpression::AddCastToType(context, std::move(list[val_idx]), target_type); } } } diff --git a/src/duckdb/src/planner/binder/tableref/bind_joinref.cpp b/src/duckdb/src/planner/binder/tableref/bind_joinref.cpp index e4c96f7ec..4bd8d8543 100644 --- a/src/duckdb/src/planner/binder/tableref/bind_joinref.cpp +++ b/src/duckdb/src/planner/binder/tableref/bind_joinref.cpp @@ -365,6 +365,18 @@ BoundStatement Binder::Bind(JoinRef &ref) { } } + // a positional join pads the shorter side with NULLs + const bool full_outer = result->type == JoinType::OUTER || ref.ref_type == JoinRefType::POSITIONAL; + if (result->type == JoinType::LEFT || full_outer) { + for (auto &binding : right_binder.bind_context.GetBindingsList()) { + binding->SetNullExtended(); + } + } + if (result->type == JoinType::RIGHT || full_outer) { + for (auto &binding : left_binder.bind_context.GetBindingsList()) { + binding->SetNullExtended(); + } + } auto right_bindings = right_binder.bind_context.GetBindingAliases(); auto left_bindings = left_binder.bind_context.GetBindingAliases(); bind_context.AddContext(std::move(left_binder.bind_context)); diff --git a/src/duckdb/src/planner/binder/tableref/bind_named_parameters.cpp b/src/duckdb/src/planner/binder/tableref/bind_named_parameters.cpp deleted file mode 100644 index 288c6d6e0..000000000 --- a/src/duckdb/src/planner/binder/tableref/bind_named_parameters.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "duckdb/planner/binder.hpp" - -namespace duckdb { - -template -map order_case_insensitive_map(const case_insensitive_map_t &input_map) { - return map(input_map.begin(), input_map.end()); -} - -template -map order_case_insensitive_map(const identifier_map_t &input_map) { - map result; - for (auto &entry : input_map) { - result[entry.first.GetIdentifierName()] = entry.second; - } - return result; -} - -void Binder::BindNamedParameters(named_parameter_type_map_t &types, named_parameter_map_t &values, - QueryErrorContext &error_context, const Identifier &func_name) { - for (auto &kv : values) { - auto entry = types.find(kv.first); - if (entry == types.end()) { - auto ordered_params = order_case_insensitive_map(types); - // create a list of named parameters for the error - string named_params; - for (auto &kv_ordered_params : ordered_params) { - named_params += " "; - named_params += kv_ordered_params.first; - named_params += " "; - named_params += kv_ordered_params.second.ToString(); - named_params += "\n"; - } - string error_msg; - if (named_params.empty()) { - error_msg = "Function does not accept any named parameters."; - } else { - error_msg = "Candidates:\n" + named_params; - } - throw BinderException(error_context, "Invalid named parameter %s for function %s\n%s", kv.first, - func_name.GetIdentifierName(), error_msg); - } - if (entry->second.id() != LogicalTypeId::ANY) { - kv.second = kv.second.DefaultCastAs(entry->second); - } - } -} - -} // namespace duckdb diff --git a/src/duckdb/src/planner/binder/tableref/bind_pivot.cpp b/src/duckdb/src/planner/binder/tableref/bind_pivot.cpp index 1cc7997c4..453117558 100644 --- a/src/duckdb/src/planner/binder/tableref/bind_pivot.cpp +++ b/src/duckdb/src/planner/binder/tableref/bind_pivot.cpp @@ -51,6 +51,10 @@ static void ConstructPivots(PivotRef &ref, vector &pivot_valu } else { new_value.name = std::move(name); } + if (new_value.name.empty()) { + // the pivot value is an empty string - column names cannot be empty + new_value.name = "''"; + } if (last_pivot) { pivot_values.push_back(std::move(new_value)); } else { @@ -267,11 +271,35 @@ static unique_ptr PivotInitialAggregate(ClientContext &context, Pivo return subquery_stage1; } +//! The key of a pivot value: the length of the value, followed by the value - or "N" for NULL values. +//! Prefixing the length makes the concatenated keys of multiple pivot values unambiguous. +static string PivotValueKey(const Value &value) { + if (value.IsNull()) { + return "N"; + } + auto str = value.ToString(); + return to_string(str.size()) + ":" + str; +} + unique_ptr ConstructPivotExpression(unique_ptr pivot_expr) { + // coalesce(strlen(pivot::VARCHAR)::VARCHAR || ':' || pivot::VARCHAR, 'N'), see PivotValueKey auto cast = make_uniq(LogicalType::VARCHAR, std::move(pivot_expr)); + vector> strlen_children; + strlen_children.push_back(cast->Copy()); + auto length = make_uniq(LogicalType::VARCHAR, + make_uniq("strlen", std::move(strlen_children))); + vector> prefix_children; + prefix_children.push_back(std::move(length)); + prefix_children.push_back(ConstantExpression::String(":")); + auto prefix = make_uniq("||", std::move(prefix_children)); + vector> key_children; + key_children.push_back(std::move(prefix)); + key_children.push_back(std::move(cast)); + auto key = make_uniq("||", std::move(key_children)); + vector> coalesce_children; - coalesce_children.push_back(std::move(cast)); - coalesce_children.push_back(ConstantExpression::String("NULL")); + coalesce_children.push_back(std::move(key)); + coalesce_children.push_back(ConstantExpression::String("N")); auto coalesce = make_uniq(ExpressionType::OPERATOR_COALESCE, std::move(coalesce_children)); return std::move(coalesce); } @@ -443,8 +471,9 @@ BoundStatement Binder::BindBoundPivot(PivotRef &ref) { } if (unique_names.size() != aggregates.size()) { - throw InternalException("Pivot aggregate mismatch: %llu unique names, %llu aggregates", unique_names.size(), - aggregates.size()); + // identical aggregates with different names are bound to a single aggregate + throw BinderException("PIVOT: identical aggregates must have the same name - use a single aggregate, or " + "make the aggregates differ"); } identifier_map_t name_to_position; @@ -485,13 +514,11 @@ BoundStatement Binder::BindBoundPivot(PivotRef &ref) { auto name = pivot_value.name + aggr_name; string pivot_str; - for (auto &value : pivot_value.values) { - auto str = value.ToString(); - if (pivot_str.empty()) { - pivot_str = std::move(str); - } else { - pivot_str += "_" + str; + for (idx_t value_idx = 0; value_idx < pivot_value.values.size(); value_idx++) { + if (value_idx > 0) { + pivot_str += "_"; } + pivot_str += PivotValueKey(pivot_value.values[value_idx]); } result.bound_pivot.pivot_values.push_back(std::move(pivot_str)); names.push_back(Identifier(std::move(name))); @@ -541,7 +568,7 @@ static bool TryExtractUnpivotList(ParsedExpression &expr, vector &co } case ExpressionType::FUNCTION: { auto &function = expr.Cast(); - if (function.FunctionName() != "row") { + if (function.FunctionName() != "row" || function.GetArgumentsMutable().empty()) { return false; } for (auto &child : function.GetArgumentsMutable()) { @@ -618,6 +645,9 @@ unique_ptr Binder::BindPivot(PivotRef &ref, vector(std::move(select)); return Bind(*subquery); } @@ -273,6 +275,9 @@ static void WarnShowDescribesQuery(ClientContext &context) { BoundStatement Binder::BindShow(ShowRef &ref) { BoundStatement result; if (ref.GetTableName().empty()) { + if (!ref.query) { + throw BinderException("SHOW requires a setting name, a table name or a query"); + } // "SHOW (SELECT ...)": describe the columns of the query. Deprecated in favor of DESCRIBE, and not affected // by show_behavior (a parenthesized query is neither a setting nor a named table). WarnShowDescribesQuery(context); diff --git a/src/duckdb/src/planner/binder/tableref/bind_table_function.cpp b/src/duckdb/src/planner/binder/tableref/bind_table_function.cpp index 19fe5ba06..0332bf655 100644 --- a/src/duckdb/src/planner/binder/tableref/bind_table_function.cpp +++ b/src/duckdb/src/planner/binder/tableref/bind_table_function.cpp @@ -1,6 +1,6 @@ #include "duckdb/catalog/catalog.hpp" #include "duckdb/catalog/catalog_entry/table_macro_catalog_entry.hpp" -#include "duckdb/execution/expression_executor.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/parser/expression/columnref_expression.hpp" #include "duckdb/parser/expression/comparison_expression.hpp" #include "duckdb/parser/expression/function_expression.hpp" @@ -18,6 +18,7 @@ #include "duckdb/planner/expression/bound_window_expression.hpp" #include "duckdb/planner/operator/logical_window.hpp" +#include "duckdb/planner/expression/bound_constant_expression.hpp" namespace duckdb { @@ -35,6 +36,15 @@ static TableFunctionBindType GetTableFunctionBindType(TableFunctionCatalogEntry } } if (all_scalar) { + for (idx_t function_idx = 0; function_idx < table_function.functions.Size(); function_idx++) { + const auto &function = *table_function.functions.GetFunctionByOffset(function_idx); + for (auto ¶m : function.GetSignature().GetParameters()) { + if (param.GetType().id() == LogicalTypeId::TABLE) { + throw BinderException("Table function %s requires a subquery as its TABLE parameter", + table_function.name); + } + } + } return TableFunctionBindType::STANDARD_TABLE_FUNCTION; } // if we have non-scalar parameters - we need to look at the function definition to decide how to bind @@ -44,8 +54,8 @@ static TableFunctionBindType GetTableFunctionBindType(TableFunctionCatalogEntry bool has_table_parameter = false; for (idx_t function_idx = 0; function_idx < table_function.functions.Size(); function_idx++) { const auto &function = *table_function.functions.GetFunctionByOffset(function_idx); - for (auto &arg : function.GetArguments()) { - if (arg.id() == LogicalTypeId::TABLE) { + for (auto ¶m : function.GetSignature().GetParameters()) { + if (param.GetType().id() == LogicalTypeId::TABLE) { has_table_parameter = true; } } @@ -86,15 +96,14 @@ void Binder::BindTableInTableOutFunction(vector> &e bool Binder::BindTableFunctionParameters(TableFunctionCatalogEntry &table_function, vector> &expressions, - vector &arguments, vector ¶meters, - named_parameter_map_t &named_parameters, BoundStatement &subquery, - ErrorData &error) { + vector> &positional_arguments, + vector>> &named_arguments, + BoundStatement &subquery, bool &table_in_out, ErrorData &error) { auto bind_type = GetTableFunctionBindType(table_function, expressions); if (bind_type == TableFunctionBindType::TABLE_IN_OUT_FUNCTION) { - // bind table in-out function + // bind table in-out function - its arguments are the columns of the subquery, so there is nothing to fold BindTableInTableOutFunction(expressions, subquery); - // fetch the arguments from the subquery - arguments = subquery.types; + table_in_out = true; return true; } bool seen_subquery = false; @@ -120,7 +129,7 @@ bool Binder::BindTableFunctionParameters(TableFunctionCatalogEntry &table_functi child->GetExpressionType() == ExpressionType::SUBQUERY) { D_ASSERT(table_function.functions.Size() == 1); const auto &fun = *table_function.functions.GetFunctionByOffset(0); - if (table_function.functions.Size() != 1 || fun.GetArguments().empty()) { + if (table_function.functions.Size() != 1 || fun.GetSignature().GetPositionalParameterCount() == 0) { throw BinderException( "Only table-in-out functions can have subquery parameters - %s only accepts constant parameters", fun.GetName()); @@ -135,14 +144,12 @@ bool Binder::BindTableFunctionParameters(TableFunctionCatalogEntry &table_functi subquery = binder->BindNode(*se.Subquery()->node); MoveCorrelatedExpressions(*binder); seen_subquery = true; - arguments.emplace_back(LogicalTypeId::TABLE); - parameters.emplace_back(); + positional_arguments.push_back(make_uniq(Value(LogicalType::TABLE))); continue; } TableFunctionBinder binder(*this, context, table_function.name.GetIdentifierName()); - LogicalType sql_type; - auto expr = binder.Bind(child, &sql_type); + auto expr = binder.Bind(child); if (expr->HasParameter()) { throw ParameterNotResolvedException(); } @@ -150,17 +157,18 @@ bool Binder::BindTableFunctionParameters(TableFunctionCatalogEntry &table_functi // should have been eliminated before throw InternalException("Table function requires a constant parameter"); } - auto constant = ExpressionExecutor::EvaluateScalar(context, *expr, true); + // The argument is folded to a constant only after the overload has been chosen, so that the bound + // expression's STRING_LITERAL / INTEGER_LITERAL type reaches overload selection - which is what lets + // "header = 1" match a BOOLEAN option, exactly as it already does for scalar functions if (parameter_name.empty()) { // unnamed parameter - if (!named_parameters.empty()) { + if (!named_arguments.empty()) { error = ErrorData("Unnamed parameters cannot come after named parameters"); return false; } - arguments.emplace_back(constant.IsNull() ? LogicalType::SQLNULL : sql_type); - parameters.emplace_back(std::move(constant)); + positional_arguments.push_back(std::move(expr)); } else { - named_parameters[parameter_name] = std::move(constant); + named_arguments.emplace_back(parameter_name, std::move(expr)); } } return true; @@ -180,8 +188,8 @@ static string GetAlias(const TableFunctionRef &ref) { //! Postgres names the single column of a set-returning function after the alias of the function //! (e.g. "SELECT t FROM generate_series(1, 2) t" returns the values, not a struct) //! returns the original column name, which is kept available as a column alias -static Identifier ApplyPostgresSetofAliasCompatibility(const TableFunction &table_function, const TableFunctionRef &ref, - vector &return_names) { +static Identifier ApplyPostgresSetofAliasCompatibility(const BaseTableFunction &table_function, + const TableFunctionRef &ref, vector &return_names) { if (table_function.return_type != TableFunctionReturnType::SET_RETURNING_FUNCTION || ref.alias.empty() || !ref.column_name_alias.empty() || return_names.size() != 1) { return Identifier(); @@ -201,8 +209,23 @@ static void AddPostgresSetofColumnAlias(BindContext &bind_context, TableIndex bi bind_context.AddColumnAlias(bind_index, original_name, 0); } -BoundStatement Binder::BindTableFunctionInternal(TableFunction &table_function, const TableFunctionRef &ref, - vector parameters, named_parameter_map_t named_parameters, +static bool PlanSupportsStatementCache(const LogicalOperator &op) { + if (op.type == LogicalOperatorType::LOGICAL_GET) { + auto &get = op.Cast(); + if (get.bind_data && !get.bind_data->SupportStatementCache()) { + return false; + } + } + for (auto &child : op.children) { + if (!PlanSupportsStatementCache(*child)) { + return false; + } + } + return true; +} + +BoundStatement Binder::BindTableFunctionInternal(BoundTableFunction &table_function, const TableFunctionRef &ref, + vector parameters, named_argument_map_t named_parameters, vector input_table_types, vector input_table_names, optional_ptr> input_plan) { @@ -216,22 +239,28 @@ BoundStatement Binder::BindTableFunctionInternal(TableFunction &table_function, auto constexpr ordinality_name = "ordinality"; string ordinality_column_name = ordinality_name; optional_idx ordinality_column_id; + // the function binder has placed them already, but not for a table in-out call or a direct bind + table_function.GetSignature().FillNamedDefaults(context, named_parameters); + table_function.SetCallArguments(parameters, named_parameters); if (table_function.bind || table_function.bind_replace || table_function.bind_operator) { TableFunctionBindInput bind_input(parameters, named_parameters, input_table_types, input_table_names, table_function.function_info.get(), this, table_function, ref, input_plan); if (table_function.bind_operator) { auto new_plan = table_function.bind_operator(context, bind_input, bind_index, return_names); if (new_plan) { + if (!PlanSupportsStatementCache(*new_plan)) { + SetAlwaysRequireRebind(); + } new_plan->ResolveOperatorTypes(); if (new_plan->types.size() != return_names.size()) { throw InternalException("Failed to bind \"%s\": return_types/names must have same size", - table_function.name); + table_function.GetName()); } for (auto &binding : new_plan->GetColumnBindings()) { if (binding.table_index != bind_index) { throw InternalException( "Failed to bind \"%s\": root bind index must be the passed in bind index", - table_function.name); + table_function.GetName()); } } auto setof_column_name = ApplyPostgresSetofAliasCompatibility(table_function, ref, return_names); @@ -258,7 +287,7 @@ BoundStatement Binder::BindTableFunctionInternal(TableFunction &table_function, } if (!table_function.bind) { throw BinderException("Failed to bind \"%s\": nullptr returned from bind_replace without bind function", - table_function.name); + table_function.GetName()); } bind_data = table_function.bind(context, bind_input, return_types, return_names); if (ref.with_ordinality == OrdinalityType::WITH_ORDINALITY) { @@ -284,17 +313,18 @@ BoundStatement Binder::BindTableFunctionInternal(TableFunction &table_function, } } else { throw InvalidInputException("Cannot call function \"%s\" directly - it has no bind function", - table_function.name); + table_function.GetName()); } if (bind_data && !bind_data->SupportStatementCache()) { SetAlwaysRequireRebind(); } if (return_types.size() != return_names.size()) { - throw InternalException("Failed to bind \"%s\": return_types/names must have same size", table_function.name); + throw InternalException("Failed to bind \"%s\": return_types/names must have same size", + table_function.GetName()); } if (return_types.empty()) { throw InternalException("Failed to bind \"%s\": Table function must return at least one column", - table_function.name); + table_function.GetName()); } auto setof_column_name = ApplyPostgresSetofAliasCompatibility(table_function, ref, return_names); // overwrite the names with any supplied aliases @@ -313,8 +343,10 @@ BoundStatement Binder::BindTableFunctionInternal(TableFunction &table_function, auto get = make_uniq(bind_index, table_function, std::move(bind_data), return_types, return_names, virtual_columns); + get->bind_info = ref.bind_info; get->parameters = parameters; get->named_parameters = named_parameters; + get->source_ordinality = ref.with_ordinality; get->input_table_types = input_table_types; get->input_table_names = input_table_names; if (ref.with_ordinality == OrdinalityType::WITH_ORDINALITY && !correlated_columns.empty()) { @@ -333,7 +365,7 @@ BoundStatement Binder::BindTableFunctionInternal(TableFunction &table_function, auto window_index = GenerateTableIndex(); auto window = make_uniq(window_index); - auto row_number = RowNumberFun::GetFunction().Bind(context); + auto row_number = GetBuiltinWindowFunction(context, RowNumberFun::Name, {})->Bind(context); row_number->WindowStartMutable() = WindowBoundary::UNBOUNDED_PRECEDING; row_number->WindowEndMutable() = WindowBoundary::CURRENT_ROW_ROWS; Identifier ordinality_alias(ordinality_column_name); @@ -369,14 +401,15 @@ BoundStatement Binder::BindTableFunctionInternal(TableFunction &table_function, } BoundStatement Binder::BindTableFunction(TableFunction &function, vector parameters) { - named_parameter_map_t named_parameters; + named_argument_map_t named_parameters; vector input_table_types; vector input_table_names; TableFunctionRef ref; ref.alias = function.name; D_ASSERT(!ref.alias.empty()); - return BindTableFunctionInternal(function, ref, std::move(parameters), std::move(named_parameters), + BoundTableFunction bound_function(function); + return BindTableFunctionInternal(bound_function, ref, std::move(parameters), std::move(named_parameters), std::move(input_table_types), std::move(input_table_names), nullptr); } @@ -401,6 +434,7 @@ BoundStatement Binder::Bind(TableFunctionRef &ref) { auto binder = Binder::CreateBinder(context, this); binder->SetCanContainNulls(true); + binder->AddBoundTableMacro(macro_func); binder->alias = ref.alias.empty() ? "unnamed_query" : ref.alias; BoundStatement query; @@ -425,11 +459,11 @@ BoundStatement Binder::Bind(TableFunctionRef &ref) { D_ASSERT(func_catalog.type == CatalogType::TABLE_FUNCTION_ENTRY); auto &function = func_catalog.Cast(); - // evaluate the input parameters to the function - vector arguments; - vector parameters; - named_parameter_map_t named_parameters; + // bind the input parameters to the function - they are folded to constants after the overload is chosen + vector> positional_arguments; + vector>> named_arguments; BoundStatement subquery; + bool table_in_out = false; ErrorData error; vector> children; @@ -437,23 +471,26 @@ BoundStatement Binder::Bind(TableFunctionRef &ref) { children.push_back(std::move(child.GetExpressionMutable())); } - if (!BindTableFunctionParameters(function, children, arguments, parameters, named_parameters, subquery, error)) { + if (!BindTableFunctionParameters(function, children, positional_arguments, named_arguments, subquery, table_in_out, + error)) { error.AddQueryLocation(ref); error.Throw(); } - // select the function based on the input parameters + // selection, named-argument checking, folding and casting all happen in the function binder FunctionBinder function_binder(*this); - auto best_function_idx = function_binder.BindFunction(function.name, function.functions, arguments, error); + vector parameters; + named_argument_map_t named_parameters; + auto best_function_idx = + table_in_out ? function_binder.BindTableInOutFunction(function.name, function.functions, subquery.types, error) + : function_binder.BindFunction(function.name, function.functions, positional_arguments, + named_arguments, parameters, named_parameters, error); if (!best_function_idx.IsValid()) { error.AddQueryLocation(ref); error.Throw(); } - // copied out of the set: BindTableFunctionInternal fills in the bound arguments/return types - auto table_function = *function.functions.GetFunctionByOffset(best_function_idx.GetIndex()); - - // now check the named parameters - BindNamedParameters(table_function.named_parameters, named_parameters, error_context, table_function.name); + // copied out of the set: BindTableFunctionInternal fills in the bound return types + BoundTableFunction table_function(function.functions.GetFunctionByOffset(best_function_idx.GetIndex())); vector input_table_types; vector input_table_names; @@ -467,22 +504,17 @@ BoundStatement Binder::Bind(TableFunctionRef &ref) { input_table_names.push_back(Identifier()); } } - if (!parameters.empty()) { - // cast the parameters to the type of the function - for (idx_t i = 0; i < arguments.size(); i++) { - auto target_type = i < table_function.GetArguments().size() ? table_function.GetArguments()[i] - : table_function.GetVarArgs(); - - if (target_type != LogicalType::ANY && target_type != LogicalType::POINTER && - target_type.id() != LogicalTypeId::LIST && target_type != LogicalType::TABLE) { - parameters[i] = parameters[i].CastAs(context, target_type); + if (parameters.empty() && subquery.plan) { + // the arguments are the columns of a subquery, so the declared types apply to those instead - the function + // binder casts the folded constants of every other call shape + const auto &signature = table_function.GetSignature(); + auto args = signature.GetArgs(); + for (idx_t i = 0; i < input_table_types.size(); i++) { + if (i >= signature.GetPositionalParameterCount() && !args) { + break; } - } - } else if (subquery.plan) { - for (idx_t i = 0; i < arguments.size(); i++) { - auto target_type = i < table_function.GetArguments().size() ? table_function.GetArguments()[i] - : table_function.GetVarArgs(); - + auto target_type = + i < signature.GetPositionalParameterCount() ? signature.GetParameter(i).GetType() : args->GetType(); if (target_type != LogicalType::ANY && target_type != LogicalType::POINTER && target_type.id() != LogicalTypeId::LIST) { input_table_types[i] = target_type; diff --git a/src/duckdb/src/planner/binder/tableref/plan_joinref.cpp b/src/duckdb/src/planner/binder/tableref/plan_joinref.cpp index 4b471496c..5cee18c17 100644 --- a/src/duckdb/src/planner/binder/tableref/plan_joinref.cpp +++ b/src/duckdb/src/planner/binder/tableref/plan_joinref.cpp @@ -157,12 +157,16 @@ static bool CreateJoinCondition(Expression &expr, const unordered_setGetExpressionType()) && CreateJoinCondition(*expr, left_bindings, right_bindings, conditions)) { continue; @@ -359,6 +365,28 @@ unique_ptr LogicalComparisonJoin::CreateJoin(ClientContext &con std::move(conditions)); } +static void PlanMarkJoin(LogicalOperator &op) { + if (op.type == LogicalOperatorType::LOGICAL_COMPARISON_JOIN) { + auto &join = op.Cast(); + if (join.TryGetMarkJoinGroupTypes(join.mark_types)) { + return; + } + bool all_equal = !join.conditions.empty(); + for (auto &condition : join.conditions) { + if (!condition.IsComparison()) { + all_equal = false; + break; + } + all_equal &= condition.GetComparisonType() == ExpressionType::COMPARE_EQUAL; + } + if (all_equal || join.HasNullSafeMarkJoinConditions() || + (join.conditions.size() == 1 && join.conditions[0].IsComparison())) { + return; + } + } + throw NotImplementedException("Unsupported explicit MARK join conditions"); +} + unique_ptr Binder::CreatePlan(BoundJoinRef &ref) { auto old_is_outside_flattened = is_outside_flattened; // Plan laterals from outermost to innermost @@ -378,6 +406,9 @@ unique_ptr Binder::CreatePlan(BoundJoinRef &ref) { } if (ref.lateral) { + if (ref.type == JoinType::MARK) { + throw NotImplementedException("Unsupported explicit MARK join conditions"); + } auto new_plan = PlanLateralJoin(std::move(left), std::move(right), ref.correlated_columns, ref.type, std::move(ref.condition)); return new_plan; @@ -414,6 +445,9 @@ unique_ptr Binder::CreatePlan(BoundJoinRef &ref) { return std::move(filter); } if (has_dependent_condition) { + if (ref.type == JoinType::MARK) { + throw NotImplementedException("Unsupported explicit MARK join conditions"); + } auto join = make_uniq(ref.type); join->children.push_back(std::move(left)); join->children.push_back(std::move(right)); @@ -433,6 +467,7 @@ unique_ptr Binder::CreatePlan(BoundJoinRef &ref) { if (ref.type == JoinType::MARK) { join->Cast().mark_index = ref.mark_index; + PlanMarkJoin(*join); } if (!ref.duplicate_eliminated_columns.empty()) { D_ASSERT(join->type == LogicalOperatorType::LOGICAL_COMPARISON_JOIN); diff --git a/src/duckdb/src/planner/bound_expression_sql_exporter.cpp b/src/duckdb/src/planner/bound_expression_sql_exporter.cpp deleted file mode 100644 index ba61e3224..000000000 --- a/src/duckdb/src/planner/bound_expression_sql_exporter.cpp +++ /dev/null @@ -1,805 +0,0 @@ -#include "duckdb/planner/bound_expression_sql_exporter.hpp" - -#include "duckdb/planner/sql_export_helpers.hpp" -#include "duckdb/common/types/variant_iterator.hpp" -#include "duckdb/parser/expression/between_expression.hpp" -#include "duckdb/parser/expression/case_expression.hpp" -#include "duckdb/parser/expression/cast_expression.hpp" -#include "duckdb/parser/expression/columnref_expression.hpp" -#include "duckdb/parser/expression/comparison_expression.hpp" -#include "duckdb/parser/expression/conjunction_expression.hpp" -#include "duckdb/parser/expression/constant_expression.hpp" -#include "duckdb/parser/expression/function_expression.hpp" -#include "duckdb/parser/expression/operator_expression.hpp" -#include "duckdb/planner/bound_result_modifier.hpp" -#include "duckdb/planner/expression/bound_aggregate_expression.hpp" -#include "duckdb/planner/expression/bound_between_expression.hpp" -#include "duckdb/planner/expression/bound_case_expression.hpp" -#include "duckdb/planner/expression/bound_cast_expression.hpp" -#include "duckdb/planner/expression/bound_columnref_expression.hpp" -#include "duckdb/planner/expression/bound_comparison_expression.hpp" -#include "duckdb/planner/expression/bound_conjunction_expression.hpp" -#include "duckdb/planner/expression/bound_constant_expression.hpp" -#include "duckdb/planner/expression/bound_function_expression.hpp" -#include "duckdb/planner/expression/bound_operator_expression.hpp" - -namespace duckdb { - -using BoundExpressionSQLExportResult = LogicalPlanVerificationResult>; - -using SQLExportHelpers::ChildPath; -using SQLExportHelpers::IsSQLRepresentableType; -using SQLExportHelpers::IsValidIdentifier; - -static bool HasUnsupportedVariantKeys(const VariantNode &node) { - // Struct literals require nonempty, case-insensitively unique keys; inspect before the lossy STRUCT conversion. - if (node.GetTypeId() == VariantLogicalType::OBJECT) { - identifier_set_t names; - for (auto &child : node.GetObjectChildren()) { - if (child.key.GetSize() == 0 || !names.insert(Identifier(child.key.GetString())).second || - HasUnsupportedVariantKeys(child.value)) { - return true; - } - } - } else if (node.GetTypeId() == VariantLogicalType::ARRAY) { - for (auto child : node.GetArrayChildren()) { - if (HasUnsupportedVariantKeys(child)) { - return true; - } - } - } - return false; -} - -static bool HasUnsupportedVariantKeys(const Value &value) { - if (value.IsNull()) { - return false; - } - optional_ptr> children; - switch (value.type().id()) { - case LogicalTypeId::VARIANT: { - Vector vector(value, count_t(1)); - VariantIterator iterator(vector); - return HasUnsupportedVariantKeys(iterator.Root(0)); - } - case LogicalTypeId::STRUCT: - children = StructValue::GetChildren(value); - break; - case LogicalTypeId::LIST: - children = ListValue::GetChildren(value); - break; - case LogicalTypeId::ARRAY: - children = ArrayValue::GetChildren(value); - break; - case LogicalTypeId::MAP: - children = MapValue::GetChildren(value); - break; - case LogicalTypeId::UNION: - return HasUnsupportedVariantKeys(UnionValue::GetValue(value)); - default: - return false; - } - for (auto &child : *children) { - if (HasUnsupportedVariantKeys(child)) { - return true; - } - } - return false; -} - -static bool IsExpressionRootPath(const LogicalPlanVerificationPath &path) { - if (!path.IsValid()) { - return false; - } - if (path.root == LogicalPlanVerificationPathRoot::STANDALONE_EXPRESSION) { - return true; - } - for (auto &component : path.components) { - if (component.type == LogicalPlanVerificationPathComponentType::OPERATOR_EXPRESSION) { - return true; - } - } - return false; -} - -static LogicalPlanVerificationIssue -InternalInvariant(optional path, string message, - optional construct = {}) { - return SQLExportHelpers::MakeIssue(LogicalPlanVerificationIssueCode::INTERNAL_INVARIANT, - LogicalPlanVerificationPhase::EXPRESSION_EXPORT, std::move(path), - std::move(construct), std::move(message)); -} - -static LogicalPlanVerificationIssue InternalExpressionInvariant(const LogicalPlanVerificationPath &path, - const Expression &expression, string message) { - return InternalInvariant(path, std::move(message), - LogicalPlanVerificationConstructIdentity::Expression(expression.GetExpressionClass())); -} - -static LogicalPlanVerificationIssue UnsupportedExpression(const LogicalPlanVerificationPath &path, - ExpressionClass expression_class) { - return SQLExportHelpers::MakeIssue( - LogicalPlanVerificationIssueCode::UNSUPPORTED_EXPRESSION, LogicalPlanVerificationPhase::EXPRESSION_EXPORT, path, - LogicalPlanVerificationConstructIdentity::Expression(expression_class), - "The bound expression class does not have a SQL AST representation in this exporter"); -} - -static LogicalPlanVerificationIssue UnsupportedFeature(const LogicalPlanVerificationPath &path, string feature, - string message) { - return SQLExportHelpers::MakeIssue( - LogicalPlanVerificationIssueCode::UNSUPPORTED_EXPORT_FEATURE, LogicalPlanVerificationPhase::EXPRESSION_EXPORT, - path, LogicalPlanVerificationConstructIdentity::ExportFeature(std::move(feature)), std::move(message)); -} - -static LogicalPlanVerificationIssue UnsupportedFunction(const LogicalPlanVerificationPath &path, - LogicalPlanVerificationFunctionIdentity identity, - string message) { - return SQLExportHelpers::MakeIssue( - LogicalPlanVerificationIssueCode::UNSUPPORTED_FUNCTION, LogicalPlanVerificationPhase::EXPRESSION_EXPORT, path, - LogicalPlanVerificationConstructIdentity::Function(std::move(identity)), std::move(message)); -} - -static BoundExpressionSQLExportResult Failure(LogicalPlanVerificationIssue issue) { - vector issues; - issues.push_back(std::move(issue)); - return BoundExpressionSQLExportResult::Failure(std::move(issues)); -} - -static bool ChildrenAreConsistentWithArguments(const vector> &children, - const vector &arguments) { - if (children.size() != arguments.size()) { - return false; - } - for (idx_t child_index = 0; child_index < children.size(); child_index++) { - if (!children[child_index] || !children[child_index]->GetReturnType().IsComplete() || - (arguments[child_index].IsComplete() && children[child_index]->GetReturnType() != arguments[child_index])) { - return false; - } - } - return true; -} - -template -static LogicalPlanVerificationFunctionIdentity DefinitionFunctionIdentity(const FUNCTION &definition, - const vector &arguments, - const LogicalType &return_type) { - LogicalPlanVerificationFunctionIdentity identity; - identity.catalog = definition.GetCatalogName().GetIdentifierName(); - identity.schema = definition.GetSchemaName().GetIdentifierName(); - identity.name = definition.GetName().GetIdentifierName(); - identity.arguments = arguments; - identity.return_type = return_type; - return identity; -} - -class BoundExpressionSQLExportState { -public: - explicit BoundExpressionSQLExportState(const BoundExpressionSQLExportContext &context_p) : context(context_p) { - } - - BoundExpressionSQLExportResult Export(const Expression &expression, const LogicalPlanVerificationPath &path) { - switch (expression.GetExpressionClass()) { - case ExpressionClass::BOUND_CONSTANT: - return ExportConstant(expression.Cast(), path); - case ExpressionClass::BOUND_COLUMN_REF: - return ExportColumnRef(expression.Cast(), path); - case ExpressionClass::BOUND_FUNCTION: - return ExportFunction(expression.Cast(), path); - case ExpressionClass::BOUND_CONJUNCTION: - return ExportConjunction(expression.Cast(), path); - case ExpressionClass::BOUND_CASE: - return ExportCase(expression.Cast(), path); - case ExpressionClass::BOUND_OPERATOR: - return ExportOperator(expression.Cast(), path); - case ExpressionClass::BOUND_AGGREGATE: - return ExportAggregate(expression.Cast(), path); - case ExpressionClass::BOUND_DEFAULT: - case ExpressionClass::BOUND_PARAMETER: - case ExpressionClass::BOUND_REF: - case ExpressionClass::BOUND_SUBQUERY: - case ExpressionClass::BOUND_WINDOW: - case ExpressionClass::BOUND_UNNEST: - case ExpressionClass::BOUND_LAMBDA: - case ExpressionClass::BOUND_LAMBDA_REF: - case ExpressionClass::LEGACY_BOUND_CAST: - case ExpressionClass::LEGACY_BOUND_COMPARISON: - case ExpressionClass::LEGACY_BOUND_BETWEEN: - return Failure(UnsupportedExpression(path, expression.GetExpressionClass())); - case ExpressionClass::BOUND_EXPANDED: - case ExpressionClass::AGGREGATE: - case ExpressionClass::CASE: - case ExpressionClass::CAST: - case ExpressionClass::COLUMN_REF: - case ExpressionClass::COMPARISON: - case ExpressionClass::CONJUNCTION: - case ExpressionClass::CONSTANT: - case ExpressionClass::DEFAULT: - case ExpressionClass::FUNCTION: - case ExpressionClass::OPERATOR: - case ExpressionClass::STAR: - case ExpressionClass::SUBQUERY: - case ExpressionClass::WINDOW: - case ExpressionClass::PARAMETER: - case ExpressionClass::COLLATE: - case ExpressionClass::LAMBDA: - case ExpressionClass::POSITIONAL_REFERENCE: - case ExpressionClass::BETWEEN: - case ExpressionClass::LAMBDA_REF: - case ExpressionClass::PATTERN: - case ExpressionClass::TYPE: - return Failure( - InternalExpressionInvariant(path, expression, "Expression export requires a final bound class")); - case ExpressionClass::INVALID: - return Failure(InternalInvariant(path, "Expression export received an invalid expression class")); - } - return Failure(InternalInvariant(path, "Expression export received an unknown expression class")); - } - -private: - BoundExpressionSQLExportResult ExportConstant(const BoundConstantExpression &expression, - const LogicalPlanVerificationPath &path) { - if (expression.GetExpressionType() != ExpressionType::VALUE_CONSTANT) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound constant has an invalid expression type")); - } - auto &return_type = expression.GetReturnType(); - auto &value = expression.GetValue(); - if (!IsSQLRepresentableType(return_type) || !IsSQLRepresentableType(value.type())) { - return Failure(InternalExpressionInvariant(path, expression, "Bound constant has an unexportable type")); - } - if (return_type != value.type()) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound constant value and return types differ")); - } - if (TypeVisitor::Contains(return_type, [](const LogicalType &type) { return type.IsAggregateState(); })) { - return Failure(UnsupportedFeature(path, "aggregate_state_literal", - "Aggregate-state values do not have a SQL literal representation")); - } - if (TypeVisitor::Contains(return_type, LogicalTypeId::VARIANT) && HasUnsupportedVariantKeys(value)) { - return Failure(UnsupportedFeature(path, "variant_literal", - "VARIANT object keys cannot be represented by a struct literal")); - } - auto result = ConstantExpression::FromValue(value); - if (return_type.id() != LogicalTypeId::SQLNULL) { - result = make_uniq(return_type, std::move(result)); - } - return BoundExpressionSQLExportResult::Success(std::move(result)); - } - - BoundExpressionSQLExportResult ExportColumnRef(const BoundColumnRefExpression &expression, - const LogicalPlanVerificationPath &path) { - if (expression.GetExpressionType() != ExpressionType::BOUND_COLUMN_REF) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound column reference has an invalid expression type")); - } - auto &binding = expression.Binding(); - if (!binding.table_index.IsValid() || !binding.column_index.IsValid()) { - return Failure(InvalidBinding(path, binding, "Bound column reference has an incomplete binding")); - } - if (!IsSQLRepresentableType(expression.GetReturnType())) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound column reference has an incomplete type")); - } - if (expression.Depth() != 0) { - auto issue = UnsupportedFeature(path, "correlated_column_reference", - "Correlated column references require an owning query export context"); - issue.facts.emplace_back("depth", Value::UBIGINT(expression.Depth())); - return Failure(std::move(issue)); - } - if (!context.resolve_binding) { - return Failure(InvalidBinding(path, binding, "No SQL column binding resolver was provided")); - } - auto resolved = context.resolve_binding(binding); - if (!resolved) { - return Failure(InvalidBinding(path, binding, "The SQL column binding resolver has no matching entry")); - } - if (resolved->names.empty()) { - return Failure(InvalidBinding(path, binding, "The resolved SQL column name is empty")); - } - for (auto &name : resolved->names) { - if (!IsValidIdentifier(name)) { - return Failure( - InvalidBinding(path, binding, "The resolved SQL column name contains an invalid identifier")); - } - } - if (!IsSQLRepresentableType(resolved->type)) { - return Failure(InternalExpressionInvariant(path, expression, "The resolved SQL column type is incomplete")); - } - if (resolved->type != expression.GetReturnType()) { - LogicalPlanVerificationIssue issue; - issue.code = LogicalPlanVerificationIssueCode::TYPE_MISMATCH; - issue.phase = LogicalPlanVerificationPhase::EXPRESSION_EXPORT; - issue.path = path; - issue.construct = LogicalPlanVerificationConstructIdentity::BindingTypeMismatch(resolved->type, - expression.GetReturnType()); - issue.message = "The resolved SQL column type differs from the bound expression type"; - return Failure(std::move(issue)); - } - return BoundExpressionSQLExportResult::Success(make_uniq(std::move(resolved->names))); - } - - LogicalPlanVerificationIssue InvalidBinding(const LogicalPlanVerificationPath &path, const ColumnBinding &binding, - string message) { - LogicalPlanVerificationIssue issue; - issue.code = LogicalPlanVerificationIssueCode::INVALID_BINDING; - issue.phase = LogicalPlanVerificationPhase::EXPRESSION_EXPORT; - issue.path = path; - issue.facts.emplace_back("column_index", Value::UBIGINT(binding.column_index.GetIndexUnsafe())); - issue.facts.emplace_back("table_index", Value::UBIGINT(binding.table_index.index)); - issue.message = std::move(message); - return issue; - } - - BoundExpressionSQLExportResult ExportFunction(const BoundFunctionExpression &expression, - const LogicalPlanVerificationPath &path) { - switch (expression.GetExpressionType()) { - case ExpressionType::BOUND_FUNCTION: - return ExportScalarFunction(expression, path); - case ExpressionType::OPERATOR_CAST: - return ExportCast(expression, path); - case ExpressionType::COMPARE_EQUAL: - case ExpressionType::COMPARE_NOTEQUAL: - case ExpressionType::COMPARE_LESSTHAN: - case ExpressionType::COMPARE_GREATERTHAN: - case ExpressionType::COMPARE_LESSTHANOREQUALTO: - case ExpressionType::COMPARE_GREATERTHANOREQUALTO: - case ExpressionType::COMPARE_DISTINCT_FROM: - case ExpressionType::COMPARE_NOT_DISTINCT_FROM: - return ExportComparison(expression, path); - case ExpressionType::COMPARE_BETWEEN: - return ExportBetween(expression, path); - default: - return Failure( - InternalExpressionInvariant(path, expression, "Bound function has an invalid expression type")); - } - } - - BoundExpressionSQLExportResult ExportCast(const BoundFunctionExpression &expression, - const LogicalPlanVerificationPath &path) { - if (expression.GetExpressionType() != ExpressionType::OPERATOR_CAST || expression.GetChildren().size() != 1 || - !expression.GetChildren()[0] || !BoundCastExpression::HasValidBindData(expression) || - !ChildrenAreConsistentWithArguments(expression.GetChildren(), expression.Function().GetArguments()) || - expression.GetReturnType() != expression.Function().GetReturnType() || - !IsSQLRepresentableType(expression.GetReturnType()) || - !IsSQLRepresentableType(expression.GetChildren()[0]->GetReturnType())) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound cast has malformed type, data, or arity")); - } - if (BoundCastExpression::IsDefaultCast(expression)) { - return Failure(UnsupportedFeature(path, "default_cast_binding", - "A default-only bound cast cannot be reconstructed through SQL binding")); - } - auto child = ExportChild(*expression.GetChildren()[0], path, 0); - if (child.HasError()) { - return child; - } - return BoundExpressionSQLExportResult::Success(make_uniq( - expression.GetReturnType(), std::move(child.GetValue()), BoundCastExpression::IsTryCast(expression))); - } - - BoundExpressionSQLExportResult ExportComparison(const BoundFunctionExpression &expression, - const LogicalPlanVerificationPath &path) { - if (!BoundComparisonExpression::IsComparison(expression.GetExpressionType()) || - expression.GetReturnType() != LogicalType::BOOLEAN || expression.GetChildren().size() != 2 || - !expression.GetChildren()[0] || !expression.GetChildren()[1] || expression.BindInfo() || - !ChildrenAreConsistentWithArguments(expression.GetChildren(), expression.Function().GetArguments()) || - expression.GetReturnType() != expression.Function().GetReturnType() || - !IsSQLRepresentableType(expression.GetChildren()[0]->GetReturnType()) || - expression.GetChildren()[0]->GetReturnType() != expression.GetChildren()[1]->GetReturnType()) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound comparison has malformed type or arity")); - } - vector> children; - vector issues; - ExportChildren(expression.GetChildren(), path, children, issues); - if (!issues.empty()) { - return BoundExpressionSQLExportResult::Failure(std::move(issues)); - } - return BoundExpressionSQLExportResult::Success(make_uniq( - expression.GetExpressionType(), std::move(children[0]), std::move(children[1]))); - } - - BoundExpressionSQLExportResult ExportBetween(const BoundFunctionExpression &expression, - const LogicalPlanVerificationPath &path) { - if (expression.GetExpressionType() != ExpressionType::COMPARE_BETWEEN || - expression.GetReturnType() != LogicalType::BOOLEAN || expression.GetChildren().size() != 3 || - !expression.GetChildren()[0] || !expression.GetChildren()[1] || !expression.GetChildren()[2] || - !BoundBetweenExpression::HasValidBindData(expression) || - !ChildrenAreConsistentWithArguments(expression.GetChildren(), expression.Function().GetArguments()) || - expression.GetReturnType() != expression.Function().GetReturnType() || - !IsSQLRepresentableType(expression.GetChildren()[0]->GetReturnType()) || - expression.GetChildren()[0]->GetReturnType() != expression.GetChildren()[1]->GetReturnType() || - expression.GetChildren()[0]->GetReturnType() != expression.GetChildren()[2]->GetReturnType()) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound BETWEEN has malformed type, data, or arity")); - } - vector> children; - vector issues; - ExportChildren(expression.GetChildren(), path, children, issues); - if (!issues.empty()) { - return BoundExpressionSQLExportResult::Failure(std::move(issues)); - } - auto lower_inclusive = BoundBetweenExpression::LowerInclusive(expression); - auto upper_inclusive = BoundBetweenExpression::UpperInclusive(expression); - if (lower_inclusive && upper_inclusive) { - return BoundExpressionSQLExportResult::Success( - make_uniq(std::move(children[0]), std::move(children[1]), std::move(children[2]))); - } - if (expression.GetChildren()[0]->IsVolatile()) { - return Failure(UnsupportedFeature( - path, "exclusive_between_input_evaluation", - "An exclusive BETWEEN cannot duplicate a volatile input while preserving evaluation semantics")); - } - auto lower = make_uniq(BoundBetweenExpression::LowerComparisonType(expression), - children[0]->Copy(), std::move(children[1])); - auto upper = make_uniq(BoundBetweenExpression::UpperComparisonType(expression), - std::move(children[0]), std::move(children[2])); - return BoundExpressionSQLExportResult::Success( - make_uniq(ExpressionType::CONJUNCTION_AND, std::move(lower), std::move(upper))); - } - - BoundExpressionSQLExportResult ExportConjunction(const BoundConjunctionExpression &expression, - const LogicalPlanVerificationPath &path) { - if ((expression.GetExpressionType() != ExpressionType::CONJUNCTION_AND && - expression.GetExpressionType() != ExpressionType::CONJUNCTION_OR) || - expression.GetReturnType() != LogicalType::BOOLEAN || expression.GetChildren().size() < 2) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound conjunction has malformed type or arity")); - } - vector> children; - vector issues; - ExportChildren(expression.GetChildren(), path, children, issues, LogicalType::BOOLEAN); - if (!issues.empty()) { - return BoundExpressionSQLExportResult::Failure(std::move(issues)); - } - auto result = make_uniq(expression.GetExpressionType()); - result->GetChildrenMutable() = std::move(children); - return BoundExpressionSQLExportResult::Success(std::move(result)); - } - - BoundExpressionSQLExportResult ExportCase(const BoundCaseExpression &expression, - const LogicalPlanVerificationPath &path) { - if (expression.GetExpressionType() != ExpressionType::CASE_EXPR) { - return Failure(InternalExpressionInvariant(path, expression, "Bound CASE has an invalid expression type")); - } - if (!IsSQLRepresentableType(expression.GetReturnType()) || expression.CaseChecks().empty()) { - return Failure(InternalExpressionInvariant(path, expression, "Bound CASE has malformed type or arity")); - } - vector> source_children; - vector> expected_types; - for (auto &check : expression.CaseChecks()) { - source_children.push_back(check.when_expr.get()); - expected_types.push_back(LogicalType::BOOLEAN); - source_children.push_back(check.then_expr.get()); - expected_types.push_back(expression.GetReturnType()); - } - source_children.push_back(expression.ElseExpression().get()); - expected_types.push_back(expression.GetReturnType()); - - vector> children; - vector issues; - ExportChildren(source_children, path, children, issues, expected_types); - if (!issues.empty()) { - return BoundExpressionSQLExportResult::Failure(std::move(issues)); - } - auto result = make_uniq(); - for (idx_t check_index = 0; check_index < expression.CaseChecks().size(); check_index++) { - CaseCheck check; - check.when_expr = std::move(children[check_index * 2]); - check.then_expr = std::move(children[check_index * 2 + 1]); - result->CaseChecksMutable().push_back(std::move(check)); - } - result->ElseMutable() = std::move(children.back()); - return BoundExpressionSQLExportResult::Success(std::move(result)); - } - - BoundExpressionSQLExportResult ExportOperator(const BoundOperatorExpression &expression, - const LogicalPlanVerificationPath &path) { - optional expected_type; - auto child_count = expression.GetChildren().size(); - switch (expression.GetExpressionType()) { - case ExpressionType::OPERATOR_NOT: - if (child_count != 1 || expression.GetReturnType() != LogicalType::BOOLEAN) { - return Failure(InternalExpressionInvariant(path, expression, "Bound NOT has malformed type or arity")); - } - expected_type = LogicalType::BOOLEAN; - break; - case ExpressionType::OPERATOR_IS_NULL: - case ExpressionType::OPERATOR_IS_NOT_NULL: - if (child_count != 1 || expression.GetReturnType() != LogicalType::BOOLEAN) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound NULL test has malformed type or arity")); - } - break; - case ExpressionType::COMPARE_IN: - case ExpressionType::COMPARE_NOT_IN: - if (child_count < 2 || expression.GetReturnType() != LogicalType::BOOLEAN || !expression.GetChildren()[0]) { - return Failure(InternalExpressionInvariant(path, expression, "Bound IN has malformed type or arity")); - } - expected_type = expression.GetChildren()[0]->GetReturnType(); - break; - case ExpressionType::OPERATOR_COALESCE: - if (child_count < 2 || !IsSQLRepresentableType(expression.GetReturnType())) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound COALESCE has malformed type or arity")); - } - expected_type = expression.GetReturnType(); - break; - case ExpressionType::OPERATOR_TRY: - if (child_count != 1 || !expression.GetChildren()[0] || - !IsSQLRepresentableType(expression.GetReturnType())) { - return Failure(InternalExpressionInvariant(path, expression, "Bound TRY has malformed type or arity")); - } - if (expression.GetChildren()[0]->IsVolatile()) { - return Failure(UnsupportedFeature(path, "try_volatile_child", - "TRY cannot be rebound around a volatile expression")); - } - expected_type = expression.GetReturnType(); - break; - case ExpressionType::OPERATOR_UNPACK: - case ExpressionType::OPERATOR_NULLIF: - case ExpressionType::GROUPING_FUNCTION: - case ExpressionType::ARRAY_EXTRACT: - case ExpressionType::ARRAY_SLICE: - case ExpressionType::STRUCT_EXTRACT: - case ExpressionType::ARRAY_CONSTRUCTOR: - case ExpressionType::ARROW: - return Failure( - UnsupportedFeature(path, "bound_operator", "The bound operator has no admitted parsed SQL AST form")); - default: - return Failure( - InternalExpressionInvariant(path, expression, "Bound operator has an invalid expression type")); - } - vector> children; - vector issues; - ExportChildren(expression.GetChildren(), path, children, issues, expected_type); - if (!issues.empty()) { - return BoundExpressionSQLExportResult::Failure(std::move(issues)); - } - return BoundExpressionSQLExportResult::Success( - make_uniq(expression.GetExpressionType(), std::move(children))); - } - - BoundExpressionSQLExportResult ExportScalarFunction(const BoundFunctionExpression &expression, - const LogicalPlanVerificationPath &path) { - auto &function = expression.Function(); - for (auto &child : expression.GetChildren()) { - if (!child) { - return Failure(InternalExpressionInvariant(path, expression, "Bound scalar function has a null child")); - } - } - auto &definition = function.GetDefinition(); - if (!definition) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound scalar function has no retained definition")); - } - auto identity = - DefinitionFunctionIdentity(*definition, function.GetLogicalArguments(), function.GetLogicalReturnType()); - if (!identity.IsValid()) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound scalar function identity is incomplete")); - } - QualifiedName name(definition->GetCatalogName(), definition->GetSchemaName(), definition->GetName()); - if (!IsValidIdentifier(name.Catalog()) || !IsValidIdentifier(name.Schema()) || - !IsValidIdentifier(name.Name()) || !IsSQLRepresentableType(expression.GetReturnType())) { - return Failure(UnsupportedFunction(path, std::move(identity), - "The retained scalar function definition is not representable as SQL")); - } - for (auto &argument : identity.arguments) { - if (!IsSQLRepresentableType(argument)) { - return Failure(UnsupportedFunction(path, std::move(identity), - "The bound scalar function uses an internal argument type")); - } - } - if (definition->GetProperties().GetCaptureArgumentAliases() || - definition->GetProperties().RequiresExpressionNames()) { - return Failure(UnsupportedFunction( - path, std::move(identity), - "The bound scalar function requires expression names that SQL export does not preserve")); - } - if (!ChildrenAreConsistentWithArguments(expression.GetChildren(), function.GetArguments()) || - expression.GetReturnType() != function.GetReturnType() || - !ChildrenAreConsistentWithArguments(expression.GetChildren(), function.GetLogicalArguments()) || - expression.GetReturnType() != function.GetLogicalReturnType()) { - return Failure(UnsupportedFunction( - path, std::move(identity), "The bound scalar function no longer represents its logical SQL signature")); - } - vector> children; - vector issues; - ExportChildren(expression.GetChildren(), path, children, issues); - if (!issues.empty()) { - return BoundExpressionSQLExportResult::Failure(std::move(issues)); - } - unique_ptr result = - make_uniq(name, std::move(children), nullptr, nullptr, false, false, false); - // Aggregate-state schemas are inferred from the arguments, not expressible as SQL cast targets. - if (!function.GetLogicalReturnType().IsAggregateState() && definition->HasBindCallback() && - definition->GetReturnType() != function.GetLogicalReturnType()) { - result = make_uniq(function.GetLogicalReturnType(), std::move(result)); - } - return BoundExpressionSQLExportResult::Success(std::move(result)); - } - - BoundExpressionSQLExportResult ExportAggregate(const BoundAggregateExpression &expression, - const LogicalPlanVerificationPath &path) { - for (auto &child : expression.GetChildren()) { - if (!child) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound aggregate function has a null child")); - } - } - if (expression.GetExpressionType() != ExpressionType::BOUND_AGGREGATE) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound aggregate has an invalid expression type")); - } - auto &function = expression.Function(); - auto &definition = function.GetDefinition(); - if (!definition) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound aggregate function has no retained definition")); - } - auto identity = - DefinitionFunctionIdentity(*definition, function.GetLogicalArguments(), function.GetLogicalReturnType()); - if (!identity.IsValid()) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound aggregate function identity is incomplete")); - } - QualifiedName name(definition->GetCatalogName(), definition->GetSchemaName(), definition->GetName()); - if (!IsValidIdentifier(name.Catalog()) || !IsValidIdentifier(name.Schema()) || - !IsValidIdentifier(name.Name()) || !IsSQLRepresentableType(expression.GetReturnType())) { - return Failure(UnsupportedFunction( - path, std::move(identity), "The retained aggregate function definition is not representable as SQL")); - } - if (expression.GetAggregateType() != AggregateType::NON_DISTINCT && - expression.GetAggregateType() != AggregateType::DISTINCT) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound aggregate has an invalid distinct mode")); - } - if (expression.StateExportMode() != AggregateStateExportMode::NONE && - expression.StateExportMode() != AggregateStateExportMode::STATE_EXPORT) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound aggregate has an invalid state export mode")); - } - for (auto &argument : identity.arguments) { - if (!IsSQLRepresentableType(argument)) { - return Failure(UnsupportedFunction(path, std::move(identity), - "The bound aggregate uses an internal argument type")); - } - } - if (definition->GetProperties().GetCaptureArgumentAliases() || - definition->GetProperties().RequiresExpressionNames()) { - return Failure( - UnsupportedFunction(path, std::move(identity), - "The bound aggregate requires expression names that SQL export does not preserve")); - } - if (!ChildrenAreConsistentWithArguments(expression.GetChildren(), function.GetLogicalArguments())) { - return Failure(UnsupportedFunction(path, std::move(identity), - "The bound aggregate no longer represents its logical SQL arguments")); - } - if (expression.StateExportMode() == AggregateStateExportMode::NONE && - expression.GetReturnType() != function.GetLogicalReturnType()) { - return Failure(UnsupportedFunction(path, std::move(identity), - "The bound aggregate no longer represents its logical SQL return type")); - } - vector> source_children; - vector> expected_types; - for (idx_t child_index = 0; child_index < expression.GetChildren().size(); child_index++) { - source_children.push_back(expression.GetChildren()[child_index].get()); - expected_types.push_back(identity.arguments[child_index]); - } - if (expression.GetFilter()) { - source_children.push_back(expression.GetFilter().get()); - expected_types.push_back(LogicalType::BOOLEAN); - } - if (expression.GetOrderBys()) { - for (auto &order : expression.GetOrderBys()->orders) { - if ((order.type != OrderType::ASCENDING && order.type != OrderType::DESCENDING) || - (order.null_order != OrderByNullType::NULLS_FIRST && - order.null_order != OrderByNullType::NULLS_LAST)) { - return Failure( - InternalExpressionInvariant(path, expression, "Bound aggregate has an invalid ordering mode")); - } - source_children.push_back(order.expression.get()); - expected_types.emplace_back(); - } - } - - vector> children; - vector issues; - ExportChildren(source_children, path, children, issues, expected_types); - if (!issues.empty()) { - return BoundExpressionSQLExportResult::Failure(std::move(issues)); - } - vector> arguments; - for (idx_t child_index = 0; child_index < expression.GetChildren().size(); child_index++) { - arguments.push_back(std::move(children[child_index])); - } - idx_t child_index = expression.GetChildren().size(); - unique_ptr filter; - if (expression.GetFilter()) { - filter = std::move(children[child_index++]); - } - unique_ptr order_bys; - if (expression.GetOrderBys()) { - order_bys = make_uniq(); - for (auto &order : expression.GetOrderBys()->orders) { - order_bys->orders.emplace_back(order.type, order.null_order, std::move(children[child_index++])); - } - } - unique_ptr result = make_uniq( - name, std::move(arguments), std::move(filter), std::move(order_bys), expression.IsDistinct(), false, - expression.StateExportMode() == AggregateStateExportMode::STATE_EXPORT); - if (expression.StateExportMode() == AggregateStateExportMode::NONE && - !function.GetLogicalReturnType().IsAggregateState() && definition->HasBindCallback() && - definition->GetReturnType() != function.GetLogicalReturnType()) { - result = make_uniq(function.GetLogicalReturnType(), std::move(result)); - } - return BoundExpressionSQLExportResult::Success(std::move(result)); - } - - BoundExpressionSQLExportResult ExportChild(const Expression &expression, const LogicalPlanVerificationPath &path, - idx_t child_index) { - return Export(expression, ChildPath(path, child_index)); - } - - void ExportChildren(const vector> &source, const LogicalPlanVerificationPath &path, - vector> &result, vector &issues, - const optional &expected_type = {}) { - vector> source_refs; - vector> expected_types; - for (auto &child : source) { - source_refs.push_back(child.get()); - expected_types.push_back(expected_type); - } - ExportChildren(source_refs, path, result, issues, expected_types); - } - - void ExportChildren(const vector> &source, const LogicalPlanVerificationPath &path, - vector> &result, vector &issues, - const vector> &expected_types) { - D_ASSERT(source.size() == expected_types.size()); - result.resize(source.size()); - for (idx_t child_index = 0; child_index < source.size(); child_index++) { - auto child_path = ChildPath(path, child_index); - if (!source[child_index]) { - issues.push_back(InternalInvariant(child_path, "Bound expression has a null child")); - continue; - } - if (expected_types[child_index] && source[child_index]->GetReturnType() != *expected_types[child_index]) { - issues.push_back(InternalExpressionInvariant(child_path, *source[child_index], - "Bound expression child has an unexpected type")); - continue; - } - auto child = Export(*source[child_index], child_path); - if (child.HasError()) { - for (auto &issue : child.GetIssues()) { - issues.push_back(issue); - } - } else { - result[child_index] = std::move(child.GetValue()); - } - } - } - -private: - const BoundExpressionSQLExportContext &context; -}; - -LogicalPlanVerificationResult> -BoundExpressionSQLExporter::Export(const Expression &expression, const BoundExpressionSQLExportContext &context) { - LogicalPlanVerificationPath path; - path.root = LogicalPlanVerificationPathRoot::STANDALONE_EXPRESSION; - return ExportAtPath(expression, context, path); -} - -LogicalPlanVerificationResult> -BoundExpressionSQLExporter::ExportAtPath(const Expression &expression, const BoundExpressionSQLExportContext &context, - const LogicalPlanVerificationPath &path) { - if (!IsExpressionRootPath(path)) { - return Failure(InternalInvariant({}, "Expression export requires an already-valid expression root path")); - } - BoundExpressionSQLExportState state(context); - return state.Export(expression, path); -} - -} // namespace duckdb diff --git a/src/duckdb/src/planner/collation_binding.cpp b/src/duckdb/src/planner/collation_binding.cpp index 509d962b3..9bad73fe6 100644 --- a/src/duckdb/src/planner/collation_binding.cpp +++ b/src/duckdb/src/planner/collation_binding.cpp @@ -2,7 +2,10 @@ #include "duckdb/catalog/catalog_entry/collate_catalog_entry.hpp" #include "duckdb/catalog/catalog_entry/scalar_function_catalog_entry.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" +#include "duckdb/planner/expression/bound_case_expression.hpp" +#include "duckdb/planner/expression/bound_constant_expression.hpp" #include "duckdb/planner/expression/bound_lambda_expression.hpp" +#include "duckdb/planner/expression/bound_operator_expression.hpp" #include "duckdb/planner/expression/bound_reference_expression.hpp" #include "duckdb/main/config.hpp" #include "duckdb/main/settings.hpp" @@ -109,23 +112,67 @@ void CollationBinding::RegisterCollation(CollationCallback callback) { //! Binds the scalar function with the given name (looked up from the system catalog) around "source". static unique_ptr ApplyCollationFunction(ClientContext &context, const string &function_name, unique_ptr source) { - auto &catalog = Catalog::GetSystemCatalog(context); - auto &function_entry = catalog.GetEntry( - context, QualifiedName(catalog.GetName(), Identifier::DefaultSchema(), Identifier(function_name))); auto source_alias = source->GetAlias(); vector> children; children.push_back(std::move(source)); - FunctionBinder function_binder(context); - ErrorData error; - auto function = function_binder.BindScalarFunction(function_entry, std::move(children), error); - if (!function) { - error.Throw(); - } + auto function = + function_binder.BindScalarFunction(Identifier::DefaultSchema(), Identifier(function_name), std::move(children)); function->SetAlias(source_alias); return function; } +//! Pushes collations into STRUCT/TUPLE fields using invoke with a lambda that rebuilds the value and preserves NULLs. +//! Evaluates the source once. Returns false if no fields require collation. +static bool PushStructCollation(ClientContext &context, unique_ptr &source, const LogicalType &sql_type, + CollationType type, const CollationBinding &binding) { + const Identifier lambda_parameter("x"); + auto lambda_parameter_expr = make_uniq(lambda_parameter, sql_type, idx_t(0)); + const bool is_tuple = sql_type.id() == LogicalTypeId::TUPLE; + vector> fields; + FunctionBinder function_binder(context); + + bool requires_collation = false; + auto &child_types = StructType::GetChildTypes(sql_type); + for (idx_t i = 0; i < child_types.size(); i++) { + vector> arguments; + arguments.push_back(lambda_parameter_expr->Copy()); + arguments.push_back(make_uniq(Value::BIGINT(NumericCast(i + 1)))); + auto field = function_binder.BindScalarFunction(Identifier::DefaultSchema(), + Identifier(is_tuple ? "struct_extract" : "struct_extract_at"), + std::move(arguments)); + // Wrap the extracted field in its collation functions, recursing into nested fields. + requires_collation |= binding.PushCollation(context, field, child_types[i].second, type); + field->SetAlias(Identifier(child_types[i].first)); + fields.push_back(std::move(field)); + } + + if (!requires_collation) { + return false; + } + + // Rebuild with the transformed field types, as the fields type can change + auto result = function_binder.BindScalarFunction(Identifier::DefaultSchema(), + Identifier(is_tuple ? "row" : "struct_pack"), std::move(fields)); + // A NULL struct must remain distinct from a struct containing only NULL fields. + auto is_null = make_uniq(ExpressionType::OPERATOR_IS_NULL, LogicalType::BOOLEAN); + is_null->GetChildrenMutable().push_back(lambda_parameter_expr->Copy()); + auto null_result = make_uniq(Value(result->GetReturnType())); + auto body = make_uniq(std::move(is_null), std::move(null_result), std::move(result)); + auto lambda = + make_uniq(ExpressionType::LAMBDA, LogicalType::LAMBDA, std::move(body), idx_t(1)); + lambda->SetParameterNames({lambda_parameter}); + + // Invoke evaluates the source once, even when several fields need collations. + auto source_alias = source->GetAlias(); + vector> arguments; + arguments.push_back(std::move(lambda)); + arguments.push_back(std::move(source)); + source = function_binder.BindScalarFunction(Identifier::DefaultSchema(), "invoke", std::move(arguments)); + source->SetAlias(source_alias); + return true; +} + //! Pushes a collation into a LIST/ARRAY type by wrapping the source in a list_transform that applies the collation to //! every element via a lambda. Returns false if the elements do not require collation, or if list_transform is not //! available (i.e. the core_functions extension is not loaded). @@ -167,11 +214,17 @@ static bool PushNestedCollation(ClientContext &context, unique_ptr & bool CollationBinding::PushCollation(ClientContext &context, unique_ptr &source, const LogicalType &sql_type, CollationType type) const { - if (sql_type.id() == LogicalTypeId::LIST) { - return PushNestedCollation(context, source, ListType::GetChildType(sql_type), type, *this); - } - if (sql_type.id() == LogicalTypeId::ARRAY) { - return PushNestedCollation(context, source, ArrayType::GetChildType(sql_type), type, *this); + // Leave collation of aliased structs, lists and arrays to the registered callbacks. + if (!sql_type.HasAlias()) { + if (StructType::IsStruct(sql_type)) { + return PushStructCollation(context, source, sql_type, type, *this); + } + if (sql_type.id() == LogicalTypeId::LIST) { + return PushNestedCollation(context, source, ListType::GetChildType(sql_type), type, *this); + } + if (sql_type.id() == LogicalTypeId::ARRAY) { + return PushNestedCollation(context, source, ArrayType::GetChildType(sql_type), type, *this); + } } for (auto &collation : collations) { auto functions = collation.get_collation_functions(context, sql_type, type); diff --git a/src/duckdb/src/planner/expression.cpp b/src/duckdb/src/planner/expression.cpp index 5da7f0522..c33e9ecc4 100644 --- a/src/duckdb/src/planner/expression.cpp +++ b/src/duckdb/src/planner/expression.cpp @@ -13,6 +13,13 @@ Expression::Expression(ExpressionType type, ExpressionClass expression_class, Lo : BaseExpression(type, expression_class), return_type(std::move(return_type)) { } +unique_ptr Expression::PreserveReturnType(const LogicalType &type, unique_ptr replacement) { + D_ASSERT(replacement); + D_ASSERT(replacement->GetReturnType() == type); + replacement->SetReturnType(type); + return replacement; +} + Expression::~Expression() { } diff --git a/src/duckdb/src/planner/expression/bound_aggregate_expression.cpp b/src/duckdb/src/planner/expression/bound_aggregate_expression.cpp index a596224a3..07437da74 100644 --- a/src/duckdb/src/planner/expression/bound_aggregate_expression.cpp +++ b/src/duckdb/src/planner/expression/bound_aggregate_expression.cpp @@ -1,4 +1,5 @@ #include "duckdb/planner/expression/bound_aggregate_expression.hpp" + #include "duckdb/parser/expression/function_expression.hpp" #include "duckdb/catalog/catalog_entry/aggregate_function_catalog_entry.hpp" @@ -91,6 +92,11 @@ bool BoundAggregateExpression::Equals(const BaseExpression &other_p) const { if (!Expression::Equals(*children[i], *other.children[i])) { return false; } + // expression equality ignores the return type, but a collation lives in the return type and decides + // which argument values compare equal - two aggregates that disagree on it are not the same aggregate + if (!children[i]->GetReturnType().EqualsIncludingCollation(other.children[i]->GetReturnType())) { + return false; + } } if (state_export_mode != other.state_export_mode) { return false; @@ -154,13 +160,13 @@ unique_ptr BoundAggregateExpression::Deserialize(Deserializer &deser if (!return_type.IsAggregateState()) { throw SerializationException("Aggregate State export should return an aggregate state type"); } - ExportAggregateFunction::SetStateExport(*result, std::move(return_type)); + ExportAggregateFunction::SetStateExport(*result, return_type); } else if (result->return_type != return_type) { // return type mismatch - push a cast auto &context = deserializer.Get(); return BoundCastExpression::AddCastToType(context, std::move(result), return_type); } - return std::move(result); + return Expression::PreserveReturnType(return_type, std::move(result)); } } // namespace duckdb diff --git a/src/duckdb/src/planner/expression/bound_cast_expression.cpp b/src/duckdb/src/planner/expression/bound_cast_expression.cpp index 101995c08..26d925cb2 100644 --- a/src/duckdb/src/planner/expression/bound_cast_expression.cpp +++ b/src/duckdb/src/planner/expression/bound_cast_expression.cpp @@ -109,6 +109,48 @@ unique_ptr BoundCastExpression::AddArrayCastToList(ClientContext &co return BoundCastExpression::AddCastToType(context, std::move(expr), LogicalType::LIST(child_type)); } +//! Whether a cast between nested types is invertible: the structure has to stay the same, and all child casts have to +//! be invertible +static bool NestedCastIsInvertible(const LogicalType &source_type, const LogicalType &target_type) { + if (source_type.id() != target_type.id()) { + return false; + } + switch (source_type.id()) { + case LogicalTypeId::LIST: + return BoundCastExpression::CastIsInvertible(ListType::GetChildType(source_type), + ListType::GetChildType(target_type)); + case LogicalTypeId::ARRAY: + if (ArrayType::IsAnySize(source_type) || ArrayType::IsAnySize(target_type) || + ArrayType::GetSize(source_type) != ArrayType::GetSize(target_type)) { + return false; + } + return BoundCastExpression::CastIsInvertible(ArrayType::GetChildType(source_type), + ArrayType::GetChildType(target_type)); + case LogicalTypeId::MAP: + return BoundCastExpression::CastIsInvertible(MapType::KeyType(source_type), MapType::KeyType(target_type)) && + BoundCastExpression::CastIsInvertible(MapType::ValueType(source_type), MapType::ValueType(target_type)); + case LogicalTypeId::STRUCT: + case LogicalTypeId::TUPLE: { + // adding or removing fields is not invertible + auto &source_children = StructType::GetChildTypes(source_type); + auto &target_children = StructType::GetChildTypes(target_type); + if (source_children.size() != target_children.size()) { + return false; + } + for (idx_t child_idx = 0; child_idx < source_children.size(); child_idx++) { + if (source_children[child_idx].first != target_children[child_idx].first || + !BoundCastExpression::CastIsInvertible(source_children[child_idx].second, + target_children[child_idx].second)) { + return false; + } + } + return true; + } + default: + return false; + } +} + bool BoundCastExpression::CastIsInvertible(const LogicalType &source_type, const LogicalType &target_type) { D_ASSERT(source_type.IsValid() && target_type.IsValid()); if (source_type.id() == LogicalTypeId::BOOLEAN || target_type.id() == LogicalTypeId::BOOLEAN) { @@ -123,6 +165,9 @@ bool BoundCastExpression::CastIsInvertible(const LogicalType &source_type, const if (source_type.id() == LogicalTypeId::VARIANT || target_type.id() == LogicalTypeId::VARIANT) { return false; } + if (source_type.IsNested() || target_type.IsNested()) { + return NestedCastIsInvertible(source_type, target_type); + } if (source_type.id() == LogicalTypeId::DECIMAL || target_type.id() == LogicalTypeId::DECIMAL) { uint8_t source_width, target_width; uint8_t source_scale, target_scale; @@ -137,7 +182,22 @@ bool BoundCastExpression::CastIsInvertible(const LogicalType &source_type, const if (target_scale < source_scale) { return false; } - return true; + if (target_type.IsUnsigned()) { + // decimals can be negative + return false; + } + // the target needs to fit all digits before the decimal point + // integral types do not fit all numbers of their decimal width, e.g. SMALLINT (width 5) does not fit 99999 + auto source_digits = source_width - source_scale; + auto target_digits = target_width - target_scale; + if (source_type.id() == LogicalTypeId::HUGEINT || source_type.id() == LogicalTypeId::UHUGEINT) { + // (u)hugeint values can have up to 39 digits + source_digits++; + } + if (target_type.id() != LogicalTypeId::DECIMAL) { + target_digits--; + } + return source_digits <= target_digits; } switch (source_type.id()) { case LogicalTypeId::TIMESTAMP: @@ -169,6 +229,12 @@ bool BoundCastExpression::CastIsInvertible(const LogicalType &source_type, const break; } break; + case LogicalTypeId::TIME_NS: + // a cast to microseconds rounds the nanoseconds + if (target_type.id() == LogicalTypeId::TIME) { + return false; + } + break; case LogicalTypeId::VARCHAR: case LogicalTypeId::BIT: case LogicalTypeId::TIME_TZ: @@ -298,6 +364,11 @@ bool BoundCastExpression::CastCanThrow(const LogicalType &source_type, const Log if (source_type.id() == LogicalTypeId::BIGNUM) { return true; } + // Floating point values that are not finite (NaN, inf) cannot be represented as a BIGNUM + if (target_type.id() == LogicalTypeId::BIGNUM && + (source_type.id() == LogicalTypeId::FLOAT || source_type.id() == LogicalTypeId::DOUBLE)) { + return true; + } // Casts of nested types are executed on the child types - recurse into them if (source_type.IsNested() || target_type.IsNested()) { return NestedCastCanThrow(source_type, target_type); diff --git a/src/duckdb/src/planner/expression/bound_conjunction_expression.cpp b/src/duckdb/src/planner/expression/bound_conjunction_expression.cpp index 1ad649bfc..389d95460 100644 --- a/src/duckdb/src/planner/expression/bound_conjunction_expression.cpp +++ b/src/duckdb/src/planner/expression/bound_conjunction_expression.cpp @@ -15,6 +15,17 @@ BoundConjunctionExpression::BoundConjunctionExpression(ExpressionType type, uniq children.push_back(std::move(right)); } +unique_ptr BoundConjunctionExpression::Create(ExpressionType type, + vector> children) { + D_ASSERT(!children.empty()); + if (children.size() == 1) { + return std::move(children[0]); + } + auto result = make_uniq(type); + result->children = std::move(children); + return std::move(result); +} + string BoundConjunctionExpression::ToString() const { return ConjunctionExpression::ToString(*this); } diff --git a/src/duckdb/src/planner/expression/bound_constant_expression.cpp b/src/duckdb/src/planner/expression/bound_constant_expression.cpp index 27b1eccc7..41f8a477a 100644 --- a/src/duckdb/src/planner/expression/bound_constant_expression.cpp +++ b/src/duckdb/src/planner/expression/bound_constant_expression.cpp @@ -1,6 +1,8 @@ #include "duckdb/planner/expression/bound_constant_expression.hpp" #include "duckdb/common/types/hash.hpp" +#include "duckdb/common/type_visitor.hpp" #include "duckdb/common/value_operations/value_operations.hpp" +#include "duckdb/common/vector_operations/vector_operations.hpp" namespace duckdb { @@ -9,16 +11,43 @@ BoundConstantExpression::BoundConstantExpression(Value value_p) value(std::move(value_p)) { } +void BoundConstantExpression::SetValue(Value value_p) { + return_type = value_p.type(); + value = std::move(value_p); +} + +Value BoundConstantExpression::TakeValue() { + return std::move(value); +} + +void BoundConstantExpression::SetReturnType(LogicalType type) { + D_ASSERT(value.type() == type); + value = value.WithType(type); + return_type = std::move(type); +} + string BoundConstantExpression::ToString() const { return value.ToSQLString(); } +//! Two constants are the same expression if their values have the same representation. VARIANT values compare by +//! meaning - 1::TINYINT and 1::INTEGER are equal - but as constants they are different: variant_typeof tells them +//! apart, and they hash differently. Vectors compare a VARIANT as the STRUCT it is stored as. +static bool SameRepresentation(const Value &left, const Value &right) { + if (!TypeVisitor::Contains(left.type(), LogicalTypeId::VARIANT)) { + return !ValueOperations::DistinctFrom(left, right); + } + Vector left_vector(left, count_t(1)); + Vector right_vector(right, count_t(1)); + return VectorOperations::NotDistinctFrom(left_vector, right_vector, nullptr, 1, nullptr, nullptr) == 1; +} + bool BoundConstantExpression::Equals(const BaseExpression &other_p) const { if (!Expression::Equals(other_p)) { return false; } auto &other = other_p.Cast(); - return value.type() == other.value.type() && !ValueOperations::DistinctFrom(value, other.value); + return value.type() == other.value.type() && SameRepresentation(value, other.value); } hash_t BoundConstantExpression::Hash() const { diff --git a/src/duckdb/src/planner/expression/bound_function_expression.cpp b/src/duckdb/src/planner/expression/bound_function_expression.cpp index 478d436f6..07d90bcb7 100644 --- a/src/duckdb/src/planner/expression/bound_function_expression.cpp +++ b/src/duckdb/src/planner/expression/bound_function_expression.cpp @@ -11,6 +11,14 @@ namespace duckdb { +static optional_ptr GetLambdaExpression(const BoundFunctionExpression &expr) { + if (!expr.Function().HasBindLambdaCallback()) { + return nullptr; + } + D_ASSERT(expr.BindInfo()); + return expr.BindInfo()->Cast().GetLambdaExpression(); +} + BoundFunctionExpression::BoundFunctionExpression(BoundScalarFunction bound_function, vector> arguments, unique_ptr bind_info_p, bool is_operator) @@ -44,32 +52,30 @@ bool BoundFunctionExpression::RequiresOrderedExecution() const { } bool BoundFunctionExpression::IsVolatile() const { - return function.GetStability() == FunctionStability::VOLATILE ? true : Expression::IsVolatile(); + auto lambda_expr = GetLambdaExpression(*this); + return function.GetStability() == FunctionStability::VOLATILE || (lambda_expr && lambda_expr->IsVolatile()) || + Expression::IsVolatile(); } bool BoundFunctionExpression::IsConsistent() const { - return function.GetStability() != FunctionStability::CONSISTENT ? false : Expression::IsConsistent(); + auto lambda_expr = GetLambdaExpression(*this); + return function.GetStability() == FunctionStability::CONSISTENT && (!lambda_expr || lambda_expr->IsConsistent()) && + Expression::IsConsistent(); } bool BoundFunctionExpression::IsFoldable() const { // functions with side effects cannot be folded: they have to be executed once for every row - if (function.HasBindLambdaCallback()) { - // This is a lambda function - D_ASSERT(bind_info); - auto &lambda_bind_data = bind_info->Cast(); - auto lambda_expr = lambda_bind_data.GetLambdaExpression(); - if (lambda_expr && lambda_expr->IsVolatile()) { - return false; - } + auto lambda_expr = GetLambdaExpression(*this); + if (lambda_expr && lambda_expr->IsVolatile()) { + return false; } return function.GetStability() == FunctionStability::VOLATILE ? false : Expression::IsFoldable(); } bool BoundFunctionExpression::CanThrow() const { - if (function.GetErrorMode() == FunctionErrors::CAN_THROW_RUNTIME_ERROR) { - return true; - } - return Expression::CanThrow(); + auto lambda_expr = GetLambdaExpression(*this); + return function.GetErrorMode() == FunctionErrors::CAN_THROW_RUNTIME_ERROR || + (lambda_expr && lambda_expr->CanThrow()) || Expression::CanThrow(); } string BoundFunctionExpression::ToString() const { @@ -155,6 +161,9 @@ void BoundFunctionExpression::Serialize(Serializer &serializer) const { // serialize legacy expression for backwards compatibility FunctionToStringInput input(function, bind_info.get(), children); auto legacy_expr = function.GetLegacySerializeCallback()(input); + legacy_expr->SetReturnType(return_type); + legacy_expr->SetAlias(alias); + legacy_expr->SetQueryLocation(query_location); legacy_expr->Serialize(serializer); return; } @@ -212,7 +221,10 @@ unique_ptr BoundFunctionExpression::Deserialize(Deserializer &deseri // replace the function expression with the bound expression auto bound_expression = entry.first.GetBindExpressionCallback()(bind_input); if (bound_expression) { - return bound_expression; + if (bound_expression->GetReturnType() != return_type) { + return BoundCastExpression::AddCastToType(context, std::move(bound_expression), return_type); + } + return Expression::PreserveReturnType(return_type, std::move(bound_expression)); } // Otherwise, fall through and continue on normally } @@ -224,7 +236,7 @@ unique_ptr BoundFunctionExpression::Deserialize(Deserializer &deseri auto &context = deserializer.Get(); return BoundCastExpression::AddCastToType(context, std::move(result), return_type); } - return std::move(result); + return Expression::PreserveReturnType(return_type, std::move(result)); } } // namespace duckdb diff --git a/src/duckdb/src/planner/expression/bound_window_expression.cpp b/src/duckdb/src/planner/expression/bound_window_expression.cpp index 66252c8ea..1771cc977 100644 --- a/src/duckdb/src/planner/expression/bound_window_expression.cpp +++ b/src/duckdb/src/planner/expression/bound_window_expression.cpp @@ -1,6 +1,8 @@ #include "duckdb/planner/expression/bound_window_expression.hpp" #include "duckdb/catalog/catalog_entry/window_function_catalog_entry.hpp" #include "duckdb/parser/expression/window_expression.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/planner/expression/bound_constant_expression.hpp" #include "duckdb/parser/expression_map.hpp" #include "duckdb/function/aggregate_function.hpp" @@ -151,6 +153,17 @@ bool BoundWindowExpression::KeysAreCompatible(const BoundWindowExpression &other return true; } +void BoundWindowExpression::RetainSQLRange(optional_ptr start, optional_ptr end, + const LogicalType &order_type) { + sql_range_start = start && start->GetExpressionClass() == ExpressionClass::BOUND_CONSTANT + ? ConstantExpression::FromValue(start->Cast().GetValue()) + : nullptr; + sql_range_end = end && end->GetExpressionClass() == ExpressionClass::BOUND_CONSTANT + ? ConstantExpression::FromValue(end->Cast().GetValue()) + : nullptr; + sql_range_order_type = order_type; +} + unique_ptr BoundWindowExpression::Copy() const { unique_ptr agg_copy; if (aggregate) { @@ -195,6 +208,14 @@ unique_ptr BoundWindowExpression::Copy() const { new_window->exclude_clause = exclude_clause; new_window->start_expr = start_expr ? start_expr->Copy() : nullptr; new_window->end_expr = end_expr ? end_expr->Copy() : nullptr; + new_window->sql_range_start = sql_range_start ? sql_range_start->Copy() : nullptr; + new_window->sql_range_end = sql_range_end ? sql_range_end->Copy() : nullptr; + new_window->sql_range_order_type = sql_range_order_type; + new_window->sql_range_start_boundary = + sql_range_start_boundary ? make_uniq(*sql_range_start_boundary) : nullptr; + new_window->sql_range_end_boundary = + sql_range_end_boundary ? make_uniq(*sql_range_end_boundary) : nullptr; + new_window->sql_range_order_casts = sql_range_order_casts; new_window->ignore_nulls = ignore_nulls; new_window->distinct = distinct; @@ -273,6 +294,15 @@ void BoundWindowExpression::Serialize(Serializer &serializer) const { serializer.WriteProperty(212, "exclude_clause", exclude_clause); serializer.WriteProperty(213, "distinct", distinct); serializer.WriteProperty(214, "arg_orders", arg_orders); + serializer.WritePropertyWithDefault(215, "sql_range_start", sql_range_start, unique_ptr()); + serializer.WritePropertyWithDefault(216, "sql_range_end", sql_range_end, unique_ptr()); + serializer.WritePropertyWithDefault(217, "sql_range_order_type", sql_range_order_type, + LogicalType::INVALID); + serializer.WritePropertyWithDefault(218, "sql_range_start_boundary", sql_range_start_boundary, + unique_ptr()); + serializer.WritePropertyWithDefault(219, "sql_range_end_boundary", sql_range_end_boundary, + unique_ptr()); + serializer.WritePropertyWithDefault>(220, "sql_range_order_casts", sql_range_order_casts); } unique_ptr BoundWindowExpression::Deserialize(Deserializer &deserializer) { @@ -318,6 +348,18 @@ unique_ptr BoundWindowExpression::Deserialize(Deserializer &deserial deserializer.ReadProperty(212, "exclude_clause", result->exclude_clause); deserializer.ReadProperty(213, "distinct", result->distinct); deserializer.ReadPropertyWithExplicitDefault(214, "arg_orders", result->arg_orders, vector()); + deserializer.ReadPropertyWithExplicitDefault(215, "sql_range_start", result->sql_range_start, + unique_ptr()); + deserializer.ReadPropertyWithExplicitDefault(216, "sql_range_end", result->sql_range_end, + unique_ptr()); + deserializer.ReadPropertyWithExplicitDefault(217, "sql_range_order_type", result->sql_range_order_type, + LogicalType::INVALID); + deserializer.ReadPropertyWithExplicitDefault(218, "sql_range_start_boundary", result->sql_range_start_boundary, + unique_ptr()); + deserializer.ReadPropertyWithExplicitDefault(219, "sql_range_end_boundary", result->sql_range_end_boundary, + unique_ptr()); + deserializer.ReadPropertyWithExplicitDefault>(220, "sql_range_order_casts", + result->sql_range_order_casts, {}); // Builtin window functions didn't used to be serialized, so we need to look them up in the system catalog if (!result->aggregate && !result->window) { diff --git a/src/duckdb/src/planner/expression/window_range_info.cpp b/src/duckdb/src/planner/expression/window_range_info.cpp new file mode 100644 index 000000000..2301b1f02 --- /dev/null +++ b/src/duckdb/src/planner/expression/window_range_info.cpp @@ -0,0 +1,154 @@ +#include "duckdb/planner/expression/window_range_info.hpp" +#include "duckdb/planner/expression/bound_cast_expression.hpp" +#include "duckdb/planner/expression/bound_function_expression.hpp" +#include "duckdb/common/serializer/serializer.hpp" +#include "duckdb/common/serializer/deserializer.hpp" + +namespace duckdb { + +static bool HasDirectRangeArguments(const BoundScalarFunction &function) { + const auto &definition = function.GetDefinition(); + if (!definition || function.HasBindExpressionCallback() || definition->HasUnbindCallback()) { + return false; + } + auto &properties = definition->GetProperties(); + return !properties.GetCaptureArgumentAliases() && !properties.RequiresExpressionNames(); +} + +bool WindowRangeCast::Capture(const Expression &expression, const Expression &operand, vector &casts) { + optional_ptr current = expression; + // the operand is the expression itself when it is still in place, or a copy of it when the function binder may + // have folded it (a subquery can neither be copied nor compared, it is always matched by identity) + while (current.get() != &operand && !current->Equals(operand)) { + if (operand.IsFoldable() && current->GetExpressionClass() == ExpressionClass::BOUND_CONSTANT) { + // the function binder folded the constant operand to its value + return true; + } + if (!BoundCastExpression::IsCast(*current)) { + return false; + } + auto &cast = current->Cast(); + if (!BoundCastExpression::HasValidBindData(cast)) { + return false; + } + casts.push_back({BoundCastExpression::SourceType(cast), cast.GetReturnType(), + BoundCastExpression::IsTryCast(cast), BoundCastExpression::IsDefaultCast(cast)}); + current = &BoundCastExpression::Child(cast); + } + return true; +} + +optional_ptr WindowRangeCast::Match(const Expression &expression, + const vector &casts) { + optional_ptr current = expression; + for (auto &origin : casts) { + if (!BoundCastExpression::IsCast(*current)) { + return nullptr; + } + auto &cast = current->Cast(); + if (!BoundCastExpression::HasValidBindData(cast) || + !BoundCastExpression::SourceType(cast).EqualsIncludingCollation(origin.source_type) || + !cast.GetReturnType().EqualsIncludingCollation(origin.target_type) || + BoundCastExpression::IsTryCast(cast) != origin.try_cast || + BoundCastExpression::IsDefaultCast(cast) != origin.default_cast) { + return nullptr; + } + current = &BoundCastExpression::Child(cast); + } + return current; +} + +unique_ptr WindowRangeBoundary::Capture(const Expression &expression, const Expression &order, + const Expression &offset) { + if (expression.GetExpressionClass() != ExpressionClass::BOUND_FUNCTION) { + return nullptr; + } + auto &call = expression.Cast(); + auto &function = call.Function(); + const auto &definition = function.GetDefinition(); + if (!HasDirectRangeArguments(function) || call.GetChildren().size() != 2) { + return nullptr; + } + auto result = make_uniq(); + vector offset_casts; + if (!WindowRangeCast::Capture(*call.GetChildren()[0], order, result->order_casts) || + !WindowRangeCast::Capture(*call.GetChildren()[1], offset, offset_casts)) { + return nullptr; + } + result->function_name = definition->GetQualifiedName(); + result->arguments = function.GetLogicalArguments(); + result->return_type = function.GetLogicalReturnType(); + result->order_type = call.GetChildren()[0]->GetReturnType(); + result->offset_type = call.GetChildren()[1]->GetReturnType(); + return result; +} + +optional_ptr WindowRangeBoundary::Match(const Expression &expression, const Expression &order) const { + auto endpoint = WindowRangeCast::Match(expression, result_casts); + if (!endpoint || endpoint->GetExpressionClass() != ExpressionClass::BOUND_FUNCTION) { + return nullptr; + } + auto &call = endpoint->Cast(); + auto &function = call.Function(); + const auto &definition = function.GetDefinition(); + if (!HasDirectRangeArguments(function) || call.GetChildren().size() != 2) { + return nullptr; + } + const bool matches_signature = definition->GetQualifiedName() == function_name && + function.GetLogicalArguments() == arguments && + function.GetLogicalReturnType() == return_type; + const bool matches_types = call.GetReturnType().EqualsIncludingCollation(return_type) && + call.GetChildren()[0]->GetReturnType().EqualsIncludingCollation(order_type) && + call.GetChildren()[1]->GetReturnType().EqualsIncludingCollation(offset_type); + if (!matches_signature || !matches_types) { + return nullptr; + } + auto input_order = WindowRangeCast::Match(*call.GetChildren()[0], order_casts); + if (!input_order || !Expression::Equals(*input_order, order)) { + return nullptr; + } + return call.GetChildren()[1].get(); +} + +void WindowRangeCast::Serialize(Serializer &serializer) const { + serializer.WriteProperty(100, "source_type", source_type); + serializer.WriteProperty(101, "target_type", target_type); + serializer.WriteProperty(102, "try_cast", try_cast); + serializer.WriteProperty(103, "default_cast", default_cast); +} + +WindowRangeCast WindowRangeCast::Deserialize(Deserializer &deserializer) { + WindowRangeCast result; + deserializer.ReadProperty(100, "source_type", result.source_type); + deserializer.ReadProperty(101, "target_type", result.target_type); + deserializer.ReadProperty(102, "try_cast", result.try_cast); + deserializer.ReadProperty(103, "default_cast", result.default_cast); + return result; +} + +void WindowRangeBoundary::Serialize(Serializer &serializer) const { + serializer.WriteProperty(100, "function_name", function_name); + serializer.WriteProperty(101, "arguments", arguments); + serializer.WriteProperty(102, "return_type", return_type); + serializer.WriteProperty(103, "order_type", order_type); + serializer.WriteProperty(104, "offset_type", offset_type); + serializer.WriteProperty(105, "order_casts", order_casts); + serializer.WriteProperty(106, "result_casts", result_casts); + serializer.WriteProperty(107, "boundary", boundary); + serializer.WriteProperty(108, "direction", direction); +} + +unique_ptr WindowRangeBoundary::Deserialize(Deserializer &deserializer) { + auto result = make_uniq(); + deserializer.ReadProperty(100, "function_name", result->function_name); + deserializer.ReadProperty(101, "arguments", result->arguments); + deserializer.ReadProperty(102, "return_type", result->return_type); + deserializer.ReadProperty(103, "order_type", result->order_type); + deserializer.ReadProperty(104, "offset_type", result->offset_type); + deserializer.ReadProperty(105, "order_casts", result->order_casts); + deserializer.ReadProperty(106, "result_casts", result->result_casts); + deserializer.ReadProperty(107, "boundary", result->boundary); + deserializer.ReadProperty(108, "direction", result->direction); + return result; +} +} // namespace duckdb diff --git a/src/duckdb/src/planner/expression_binder.cpp b/src/duckdb/src/planner/expression_binder.cpp index f8e91f386..97d819e0f 100644 --- a/src/duckdb/src/planner/expression_binder.cpp +++ b/src/duckdb/src/planner/expression_binder.cpp @@ -34,6 +34,10 @@ void ExpressionBinder::InitializeStackCheck() { } } +void ExpressionBinder::ContinueStackDepth(const ExpressionBinder &parent) { + stack_depth = parent.stack_depth; +} + StackChecker ExpressionBinder::StackCheck(const ParsedExpression &expr, idx_t extra_stack) { D_ASSERT(stack_depth != DConstants::INVALID_INDEX); auto max_expression_depth = Settings::Get(context); diff --git a/src/duckdb/src/planner/expression_binder/check_binder.cpp b/src/duckdb/src/planner/expression_binder/check_binder.cpp index be5ceace5..519488add 100644 --- a/src/duckdb/src/planner/expression_binder/check_binder.cpp +++ b/src/duckdb/src/planner/expression_binder/check_binder.cpp @@ -35,8 +35,9 @@ BindResult ExpressionBinder::BindQualifiedColumnName(ColumnRefExpression &colref if (colref.ColumnNames()[0] == table_name) { struct_start++; } - auto result = make_uniq_base(colref.ColumnNames().back()); - for (idx_t i = struct_start; i + 1 < colref.ColumnNames().size(); i++) { + // the first name (after the table name) is the column, the remaining names are struct fields + auto result = make_uniq_base(colref.ColumnNames()[struct_start]); + for (idx_t i = struct_start + 1; i < colref.ColumnNames().size(); i++) { result = CreateStructExtract(std::move(result), colref.ColumnNames()[i]); } return BindExpression(result, 0); diff --git a/src/duckdb/src/planner/expression_binder/index_binder.cpp b/src/duckdb/src/planner/expression_binder/index_binder.cpp index f3a3d7350..0a6782dfc 100644 --- a/src/duckdb/src/planner/expression_binder/index_binder.cpp +++ b/src/duckdb/src/planner/expression_binder/index_binder.cpp @@ -1,9 +1,11 @@ #include "duckdb/planner/expression_binder/index_binder.hpp" #include "duckdb/catalog/catalog.hpp" +#include "duckdb/planner/binder.hpp" #include "duckdb/parser/parsed_data/create_index_info.hpp" #include "duckdb/parser/expression/columnref_expression.hpp" #include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/binder.hpp" #include "duckdb/planner/column_binding.hpp" #include "duckdb/execution/index/bound_index.hpp" #include "duckdb/execution/index/unbound_index.hpp" @@ -81,19 +83,34 @@ unique_ptr IndexBinder::BindCreateIndex(ClientContext &context, // Add the dependencies. auto &dependencies = create_index_info->dependencies; auto &catalog = table_entry.ParentCatalog(); - SetCatalogLookupCallback([&dependencies, &catalog](CatalogEntry &entry) { + SetCatalogLookupCallback([&dependencies, &catalog, &table_entry](CatalogEntry &entry) { if (&catalog != &entry.ParentCatalog()) { return; } - // indexes do not require CASCADE to be dropped, they are simply always dropped along with the table - dependencies.AddDependency(entry, DependencyDependentFlags()); + if (&entry == &table_entry) { + // indexes do not require CASCADE to be dropped, they are simply always dropped along with the table + dependencies.AddDependency(entry, DependencyDependentFlags()); + return; + } + // other entries the index expressions use (e.g. macros) cannot be dropped, replaced or altered while the index + // exists (note that blocking alone is the legacy value that is read as non-blocking for indexes) + dependencies.AddDependency(entry, DependencyDependentFlags().SetBlocking().SetAlterBlocking()); }); // Bind the index expressions. + // The expressions are bound again when the index is loaded, so they cannot depend on the session or the query + // they are bound in (e.g. now(), getvariable()) - such functions are folded to constants while binding. + auto &properties = binder.GetStatementProperties(); + const auto statement_requires_rebind = properties.always_require_rebind; vector> expressions; for (auto &expr : create_index_info->expressions) { + properties.always_require_rebind = false; expressions.push_back(Bind(expr)); + if (properties.always_require_rebind) { + throw BinderException("Index keys cannot contain expressions with side effects."); + } } + properties.always_require_rebind = statement_requires_rebind; auto &get = plan->Cast(); InitCreateIndexInfo(get, *create_index_info); diff --git a/src/duckdb/src/planner/expression_binder/table_function_binder.cpp b/src/duckdb/src/planner/expression_binder/table_function_binder.cpp index bf694dbaa..7f29b9bc1 100644 --- a/src/duckdb/src/planner/expression_binder/table_function_binder.cpp +++ b/src/duckdb/src/planner/expression_binder/table_function_binder.cpp @@ -11,9 +11,9 @@ namespace duckdb { TableFunctionBinder::TableFunctionBinder(Binder &binder, ClientContext &context, string table_function_name_p, - string clause_p) + string clause_p, IdentifierConversionPolicy identifier_conversion_policy_p) : ExpressionBinder(binder, context), table_function_name(std::move(table_function_name_p)), - clause(std::move(clause_p)) { + clause(std::move(clause_p)), identifier_conversion_policy(identifier_conversion_policy_p) { } BindResult TableFunctionBinder::BindLambdaReference(LambdaRefExpression &expr, idx_t depth) { @@ -32,11 +32,11 @@ BindResult TableFunctionBinder::BindColumnReference(unique_ptr return BindLambdaReference(lambda_ref->Cast(), depth); } - if (binder.macro_binding && binder.macro_binding->HasMatchingBinding(Identifier(col_ref.GetName()))) { + if (binder.macro_binding && binder.macro_binding->HasMatchingBinding(col_ref.GetColumnName())) { throw ParameterNotResolvedException(); } } else if (col_ref.ColumnNames()[0].StartsWith(DummyBinding::DUMMY_NAME) && binder.macro_binding && - binder.macro_binding->HasMatchingBinding(Identifier(col_ref.GetName()))) { + binder.macro_binding->HasMatchingBinding(col_ref.GetColumnName())) { throw ParameterNotResolvedException(); } @@ -60,7 +60,7 @@ BindResult TableFunctionBinder::BindColumnReference(unique_ptr } } - if (accept_sql_value_functions) { + if (identifier_conversion_policy != IdentifierConversionPolicy::ALLOW) { auto value_function = ExpressionBinder::GetSQLValueFunction(column_names.back()); if (value_function) { return BindExpression(value_function, depth, root_expression); @@ -78,7 +78,9 @@ BindResult TableFunctionBinder::BindColumnReference(unique_ptr result_name); } - auto setting = Settings::Get(context); + auto setting = identifier_conversion_policy == IdentifierConversionPolicy::ALLOW + ? TableFunctionIdentifierConversion::ENABLE_IMPLICIT_STRING + : Settings::Get(context); auto implicit_conversion_disabled = setting == TableFunctionIdentifierConversion::DISABLE_IMPLICIT_STRING; auto warn_implicit_conversion = setting == TableFunctionIdentifierConversion::DEFAULT; const auto msg = diff --git a/src/duckdb/src/planner/expression_iterator.cpp b/src/duckdb/src/planner/expression_iterator.cpp index 18c3c4c92..923c2980c 100644 --- a/src/duckdb/src/planner/expression_iterator.cpp +++ b/src/duckdb/src/planner/expression_iterator.cpp @@ -158,4 +158,21 @@ void ExpressionIterator::VisitExpressionClassMutable( *expr, [&](unique_ptr &child) { VisitExpressionClassMutable(child, expr_class, callback); }); } +static void ReplaceExpressionInPlace(unique_ptr &expr, const Expression &target, + const Expression &replacement) { + if (expr->Equals(target)) { + expr = replacement.Copy(); + return; + } + ExpressionIterator::EnumerateChildren( + *expr, [&](unique_ptr &child) { ReplaceExpressionInPlace(child, target, replacement); }); +} + +unique_ptr ExpressionIterator::ReplaceExpression(const Expression &expr, const Expression &target, + const Expression &replacement) { + auto result = expr.Copy(); + ReplaceExpressionInPlace(result, target, replacement); + return result; +} + } // namespace duckdb diff --git a/src/duckdb/src/planner/expression_nullability.cpp b/src/duckdb/src/planner/expression_nullability.cpp index 94cbc7417..c317acc7c 100644 --- a/src/duckdb/src/planner/expression_nullability.cpp +++ b/src/duckdb/src/planner/expression_nullability.cpp @@ -5,6 +5,7 @@ #include "duckdb/planner/expression/bound_comparison_expression.hpp" #include "duckdb/planner/expression/bound_conjunction_expression.hpp" #include "duckdb/planner/expression/bound_operator_expression.hpp" +#include "duckdb/planner/expression_iterator.hpp" #include "duckdb/planner/filter/expression_filter.hpp" #include "duckdb/planner/operator/list.hpp" #include "duckdb/storage/statistics/base_statistics.hpp" @@ -13,6 +14,22 @@ namespace duckdb { +bool ExpressionBecomesNull(const Expression &expr, + const std::function &column_becomes_null) { + if (expr.GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF) { + auto &column = expr.Cast(); + return column_becomes_null(column); + } + if (!expr.PropagatesNullValues()) { + return false; + } + bool child_becomes_null = false; + ExpressionIterator::EnumerateChildren(expr, [&](const Expression &child) { + child_becomes_null |= ExpressionBecomesNull(child, column_becomes_null); + }); + return child_becomes_null; +} + NotNullExpressionAnalyzer::NotNullExpressionAnalyzer(ClientContext &context_p, optional_ptr plan_root_p) : context(context_p), plan_root(plan_root_p) { @@ -30,7 +47,7 @@ static bool GetColumnRefBinding(const Expression &expr, ColumnBinding &binding) return true; } -static bool FilterRejectsNull(const Expression &filter, const Expression &expr) { +bool FilterRejectsNull(const Expression &filter, const Expression &expr) { if (filter.GetExpressionType() == ExpressionType::CONJUNCTION_AND) { auto &conjunction = filter.Cast(); for (auto &child : conjunction.GetChildren()) { @@ -212,11 +229,11 @@ bool NotNullExpressionAnalyzer::IsNotNull(LogicalOperator &op, const Expression auto column_index = binding.column_index.GetIndex(); auto &cte_source = *cte->children[0]; auto source_bindings = cte_source.GetColumnBindings(); - if (column_index >= source_bindings.size() || column_index >= cte_source.types.size()) { + if (column_index >= source_bindings.size()) { return false; } seen_ctes.push_back(cte_ref.cte_index); - auto source_expr = BoundColumnRefExpression(cte_source.types[column_index], source_bindings[column_index]); + auto source_expr = BoundColumnRefExpression(expr.GetReturnType(), source_bindings[column_index]); auto result = IsNotNull(cte_source, source_expr, seen_ctes); seen_ctes.pop_back(); return result; diff --git a/src/duckdb/src/planner/filter/dynamic_filter.cpp b/src/duckdb/src/planner/filter/dynamic_filter.cpp index dbc4b237e..4e235b845 100644 --- a/src/duckdb/src/planner/filter/dynamic_filter.cpp +++ b/src/duckdb/src/planner/filter/dynamic_filter.cpp @@ -5,7 +5,6 @@ #include "duckdb/storage/statistics/base_statistics.hpp" #include "duckdb/storage/statistics/numeric_stats.hpp" #include "duckdb/storage/statistics/string_stats.hpp" -#include "duckdb/planner/expression/bound_constant_expression.hpp" namespace duckdb { @@ -17,25 +16,21 @@ LegacyDynamicFilter::LegacyDynamicFilter(shared_ptr filter_da } unique_ptr LegacyDynamicFilter::ToExpression(const Expression &column) const { - if (!filter_data || !filter_data->initialized) { - auto bound_constant = make_uniq(Value(true)); - return std::move(bound_constant); - } - lock_guard l(filter_data->lock); - return filter_data->ToExpression(column); + // emit the dynamic filter even without filter data (e.g. after deserialization), so it can be set later + return CreateDynamicFilterExpression(filter_data, column.GetReturnType(), column.Copy()); } void DynamicFilterData::SetValue(Value val) { if (val.IsNull()) { return; } - lock_guard l(lock); + annotated_lock_guard l(lock); constant = std::move(val); initialized = true; } void DynamicFilterData::Reset() { - lock_guard l(lock); + annotated_lock_guard l(lock); initialized = false; } diff --git a/src/duckdb/src/planner/filter/expression_filter.cpp b/src/duckdb/src/planner/filter/expression_filter.cpp index 6c1b4882b..9c7f14073 100644 --- a/src/duckdb/src/planner/filter/expression_filter.cpp +++ b/src/duckdb/src/planner/filter/expression_filter.cpp @@ -77,6 +77,34 @@ unique_ptr ExpressionFilter::CreateNullCheckExpression(unique_ptr ExpressionFilter::CreateComparisonFilter(ExpressionType comparison_type, Value constant) { + auto column = make_uniq(constant.type(), 0ULL); + auto comparison = BoundComparisonExpression::Create(comparison_type, std::move(column), + make_uniq(std::move(constant))); + return make_uniq(std::move(comparison)); +} + +bool ExpressionFilter::IsSimpleFilterColumnRef(const Expression &expr) { + return expr.GetExpressionClass() == ExpressionClass::BOUND_REF || + expr.GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF; +} + +optional_ptr +ExpressionFilter::TryGetColumnConstantComparison(const BoundFunctionExpression &comparison, + ExpressionType &comparison_type) { + auto &left = BoundComparisonExpression::Left(comparison); + auto &right = BoundComparisonExpression::Right(comparison); + comparison_type = comparison.GetExpressionType(); + if (IsSimpleFilterColumnRef(left) && right.GetExpressionClass() == ExpressionClass::BOUND_CONSTANT) { + return right.Cast(); + } + if (IsSimpleFilterColumnRef(right) && left.GetExpressionClass() == ExpressionClass::BOUND_CONSTANT) { + comparison_type = FlipComparisonExpression(comparison_type); + return left.Cast(); + } + return nullptr; +} + static bool IsOptionalInternalFunction(const BoundFunctionExpression &func) { return func.Function().GetName() == OptionalFilterScalarFun::NAME || func.Function().GetName() == SelectivityOptionalFilterScalarFun::NAME; @@ -115,17 +143,9 @@ static bool ContainsInternalTableFilterFunction(const Expression &expr) { if (TableFilterFunctions::IsTableFilterFunction(func.Function())) { return true; } - if (func.Function().GetName() == OptionalFilterScalarFun::NAME && func.BindInfo()) { - auto &data = func.BindInfo()->Cast(); - if (data.child_filter_expr && ContainsInternalTableFilterFunction(*data.child_filter_expr)) { - return true; - } - } - if (func.Function().GetName() == SelectivityOptionalFilterScalarFun::NAME && func.BindInfo()) { - auto &data = func.BindInfo()->Cast(); - if (data.child_filter_expr && ContainsInternalTableFilterFunction(*data.child_filter_expr)) { - return true; - } + auto optional_child = ExpressionFilter::GetOptionalFilterChild(func); + if (optional_child && ContainsInternalTableFilterFunction(*optional_child)) { + return true; } } bool found = false; @@ -140,15 +160,10 @@ static bool ContainsInternalTableFilterFunction(const Expression &expr) { static unique_ptr UnwrapOptionalFiltersForConstantEvaluation(const Expression &expr) { if (expr.GetExpressionClass() == ExpressionClass::BOUND_FUNCTION) { auto &func = expr.Cast(); - if (func.Function().GetName() == OptionalFilterScalarFun::NAME && func.BindInfo()) { - auto &data = func.BindInfo()->Cast(); - return data.child_filter_expr ? UnwrapOptionalFiltersForConstantEvaluation(*data.child_filter_expr) - : make_uniq(Value::BOOLEAN(true)); - } - if (func.Function().GetName() == SelectivityOptionalFilterScalarFun::NAME && func.BindInfo()) { - auto &data = func.BindInfo()->Cast(); - return data.child_filter_expr ? UnwrapOptionalFiltersForConstantEvaluation(*data.child_filter_expr) - : make_uniq(Value::BOOLEAN(true)); + if (IsOptionalInternalFunction(func) && func.BindInfo()) { + auto optional_child = ExpressionFilter::GetOptionalFilterChild(func); + return optional_child ? UnwrapOptionalFiltersForConstantEvaluation(*optional_child) + : make_uniq(Value::BOOLEAN(true)); } } auto result = expr.Copy(); @@ -900,17 +915,9 @@ bool ExpressionFilter::ContainsInternalFunction(const Expression &expr, const st if (func.Function().GetName() == func_name) { return true; } - if (func.Function().GetName() == OptionalFilterScalarFun::NAME && func.BindInfo()) { - auto &data = func.BindInfo()->Cast(); - if (data.child_filter_expr && ContainsInternalFunction(*data.child_filter_expr, func_name)) { - return true; - } - } - if (func.Function().GetName() == SelectivityOptionalFilterScalarFun::NAME && func.BindInfo()) { - auto &data = func.BindInfo()->Cast(); - if (data.child_filter_expr && ContainsInternalFunction(*data.child_filter_expr, func_name)) { - return true; - } + auto optional_child = GetOptionalFilterChild(func); + if (optional_child && ContainsInternalFunction(*optional_child, func_name)) { + return true; } } bool found = false; @@ -930,6 +937,23 @@ bool ExpressionFilter::IsRootOptionalExpression(const Expression &expr) { return IsOptionalExpressionInternal(expr, false, true); } +optional_ptr ExpressionFilter::GetOptionalFilterChild(const Expression &expr) { + if (expr.GetExpressionClass() != ExpressionClass::BOUND_FUNCTION) { + return nullptr; + } + auto &func = expr.Cast(); + if (!func.BindInfo()) { + return nullptr; + } + if (func.Function().GetName() == OptionalFilterScalarFun::NAME) { + return func.BindInfo()->Cast().child_filter_expr.get(); + } + if (func.Function().GetName() == SelectivityOptionalFilterScalarFun::NAME) { + return func.BindInfo()->Cast().child_filter_expr.get(); + } + return nullptr; +} + bool ExpressionFilter::IsOptionalFilter(const TableFilter &filter) { auto &expr_filter = GetExpressionFilter(filter, "ExpressionFilter::IsOptionalFilter"); return IsOptionalExpression(*expr_filter.expr); @@ -956,21 +980,31 @@ static shared_ptr TryGetRootDynamicFilterData(const Expressio return func.BindInfo()->Cast().filter_data; } +static shared_ptr TryGetOptionalDynamicFilterData(const Expression &expr) { + if (expr.GetExpressionType() == ExpressionType::CONJUNCTION_AND) { + for (auto &child : expr.Cast().GetChildren()) { + auto filter_data = TryGetOptionalDynamicFilterData(*child); + if (filter_data) { + return filter_data; + } + } + return nullptr; + } + auto optional_child = ExpressionFilter::GetOptionalFilterChild(expr); + return optional_child ? TryGetRootDynamicFilterData(*optional_child) : nullptr; +} + shared_ptr ExpressionFilter::GetRootOptionalDynamicFilterData(const TableFilter &filter) { auto &expr_filter = GetExpressionFilter(filter, "ExpressionFilter::GetRootOptionalDynamicFilterData"); - if (expr_filter.expr->GetExpressionClass() != ExpressionClass::BOUND_FUNCTION) { + if (expr_filter.expr->GetExpressionType() == ExpressionType::CONJUNCTION_AND) { return nullptr; } - auto &func = expr_filter.expr->Cast(); - if (func.Function().GetName() == OptionalFilterScalarFun::NAME && func.BindInfo()) { - auto &data = func.BindInfo()->Cast(); - return data.child_filter_expr ? TryGetRootDynamicFilterData(*data.child_filter_expr) : nullptr; - } - if (func.Function().GetName() == SelectivityOptionalFilterScalarFun::NAME && func.BindInfo()) { - auto &data = func.BindInfo()->Cast(); - return data.child_filter_expr ? TryGetRootDynamicFilterData(*data.child_filter_expr) : nullptr; - } - return nullptr; + return TryGetOptionalDynamicFilterData(*expr_filter.expr); +} + +shared_ptr ExpressionFilter::GetOptionalDynamicFilterData(const TableFilter &filter) { + auto &expr_filter = GetExpressionFilter(filter, "ExpressionFilter::GetOptionalDynamicFilterData"); + return TryGetOptionalDynamicFilterData(*expr_filter.expr); } unique_ptr ExpressionFilter::FromTableFilter(const TableFilter &filter, const LogicalType &col_type) { @@ -999,22 +1033,11 @@ string ExpressionFilter::InternalFunctionToString(const BoundFunctionExpression const auto has_filter_data = func_expr.BindInfo() && func_expr.BindInfo()->Cast().filter_data; return DynamicFilterScalarFun::ToString(column_name, has_filter_data); - } else if (func_name == OptionalFilterScalarFun::NAME) { - string child_filter_string; - if (func_expr.BindInfo()) { - auto &data = func_expr.BindInfo()->Cast(); - if (data.child_filter_expr) { - child_filter_string = ExpressionToFriendlyString(*data.child_filter_expr, column_name); - } - } - return OptionalFilterScalarFun::ToString(child_filter_string); - } else if (func_name == SelectivityOptionalFilterScalarFun::NAME) { - string child_filter_string; - if (func_expr.BindInfo()) { - auto &data = func_expr.BindInfo()->Cast(); - if (data.child_filter_expr) { - child_filter_string = ExpressionToFriendlyString(*data.child_filter_expr, column_name); - } + } else if (IsOptionalInternalFunction(func_expr)) { + auto optional_child = GetOptionalFilterChild(func_expr); + auto child_filter_string = optional_child ? ExpressionToFriendlyString(*optional_child, column_name) : string(); + if (func_name == OptionalFilterScalarFun::NAME) { + return OptionalFilterScalarFun::ToString(child_filter_string); } return SelectivityOptionalFilterScalarFun::ToString(child_filter_string); } diff --git a/src/duckdb/src/planner/filter/table_filter_bloom_function.cpp b/src/duckdb/src/planner/filter/table_filter_bloom_function.cpp index dbb5ad7ce..3a0a18b78 100644 --- a/src/duckdb/src/planner/filter/table_filter_bloom_function.cpp +++ b/src/duckdb/src/planner/filter/table_filter_bloom_function.cpp @@ -11,6 +11,7 @@ #include "duckdb/common/operator/cast_operators.hpp" #include "duckdb/common/operator/subtract.hpp" +#include "duckdb/common/vector/constant_vector.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" #include "duckdb/common/vector_size.hpp" #include "duckdb/execution/expression_executor_state.hpp" @@ -18,6 +19,9 @@ #include "duckdb/storage/statistics/numeric_stats.hpp" #include "duckdb/storage/buffer_manager.hpp" +#include +#include + namespace duckdb { static constexpr idx_t MAX_NUM_SECTORS = (1ULL << 26); @@ -26,11 +30,23 @@ static constexpr idx_t MIN_NUM_BITS = 512; static constexpr idx_t LOG_SECTOR_SIZE = 6; // a sector is 64 bits, log2(64) = 6 static constexpr idx_t SHIFT_MASK = 0x3F3F3F3F3F3F3F3F; // 6 bits for 64 positions static constexpr idx_t N_BITS = 4; // the number of bits to set per hash +static constexpr idx_t BITS_PER_SECTOR = 1ULL << LOG_SECTOR_SIZE; + +//! A sector is one entry of the bit array, so the two have to agree on how wide it is +static_assert(BITS_PER_SECTOR == sizeof(uint64_t) * 8, "a bloom filter sector must be exactly one array entry"); + +//! A filter is sized from a cardinality estimate, so never let it claim more than this share of the memory budget +static constexpr idx_t MAX_MEMORY_FRACTION = 64; void BloomFilter::Initialize(ClientContext &context_p, idx_t number_of_rows) { BufferManager &buffer_manager = BufferManager::GetBufferManager(context_p); - num_sectors = GetNumberOfSectors(number_of_rows); + // a capped filter stays complete - fewer sectors only raise the false positive rate, and Seal drops a + // filter that ended up too dense to reject anything + const idx_t min_sectors = MIN_NUM_BITS >> LOG_SECTOR_SIZE; + const idx_t budget_bytes = buffer_manager.GetMaxMemory() / MAX_MEMORY_FRACTION; + const idx_t budget_sectors = MaxValue(PreviousPowerOfTwo(budget_bytes / sizeof(uint64_t)), min_sectors); + num_sectors = MinValue(GetNumberOfSectors(number_of_rows), budget_sectors); bitmask = num_sectors - 1; buf_ = buffer_manager.GetBufferAllocator().Allocate(64 + num_sectors * sizeof(uint64_t)); @@ -39,24 +55,79 @@ void BloomFilter::Initialize(ClientContext &context_p, idx_t number_of_rows) { std::fill_n(bf, num_sectors, 0); initialized = true; + sealed = false; } -void BloomFilter::Merge(const BloomFilter &other) { +void BloomFilter::Fold(idx_t new_num_sectors) { D_ASSERT(initialized); - D_ASSERT(other.initialized); - D_ASSERT(num_sectors == other.num_sectors); - D_ASSERT(bitmask == other.bitmask); + D_ASSERT(!sealed); + D_ASSERT(IsPowerOfTwo(new_num_sectors)); + D_ASSERT(new_num_sectors <= num_sectors); + const uint64_t new_bitmask = new_num_sectors - 1; + for (idx_t i = new_num_sectors; i < num_sectors; i++) { + bf[i & new_bitmask] |= bf[i]; + } + num_sectors = new_num_sectors; + bitmask = new_bitmask; +} + +double BloomFilter::Density() const { + D_ASSERT(initialized); + idx_t bits_set = 0; for (idx_t i = 0; i < num_sectors; i++) { - bf[i] |= other.bf[i]; + bits_set += static_cast(std::bitset(bf[i]).count()); } + return static_cast(bits_set) / static_cast(num_sectors * BITS_PER_SECTOR); } -void BloomFilter::Reset() { - buf_.Reset(); - num_sectors = 0; - bitmask = 0; - initialized = false; - bf = nullptr; +//! False positive rate the sizing in GetNumberOfSectors aims for. With m bits, n keys and k probes per key a +//! given bit stays zero with probability (1 - 1/m)^(n*k) ~ e^(-k*n/m), so all k probed bits are set - a false +//! positive - with probability (1 - e^(-k*n/m))^k. Here m/n is MIN_NUM_BITS_PER_KEY and k is N_BITS. +static double TargetFalsePositiveRate() { + const double load = static_cast(N_BITS) / static_cast(MIN_NUM_BITS_PER_KEY); + return std::pow(1.0 - std::exp(-load), static_cast(N_BITS)); +} + +void BloomFilter::Seal(ClientContext &context) { + if (!initialized || sealed) { + sealed = true; + return; + } + const idx_t min_sectors = MIN_NUM_BITS >> LOG_SECTOR_SIZE; + double density = Density(); + // a density of exactly zero means no key was ever inserted, so there is nothing to fold down + if (density > 0.0) { + // each fold ORs two sectors together, so the expected density goes from p to 1 - (1 - p)^2 + const double target = TargetFalsePositiveRate(); + // target_sectors is only a candidate size - nothing is folded until the loop has settled on one + idx_t target_sectors = num_sectors; + while (target_sectors > min_sectors) { + const double folded_density = 1.0 - (1.0 - density) * (1.0 - density); + if (std::pow(folded_density, static_cast(N_BITS)) > target) { + break; + } + density = folded_density; + target_sectors >>= 1; + } + if (target_sectors < num_sectors) { + Fold(target_sectors); + // the filter was sized from an estimate, so hand the memory folding freed up back to the allocator + auto &allocator = BufferManager::GetBufferManager(context).GetBufferAllocator(); + auto compacted_buf = allocator.Allocate(64 + num_sectors * sizeof(uint64_t)); + auto compacted_bf = + reinterpret_cast((64ULL + reinterpret_cast(compacted_buf.get())) & ~63ULL); + std::copy_n(bf, num_sectors, compacted_bf); + buf_ = std::move(compacted_buf); + bf = compacted_bf; + } + } + sealed = true; + // a filter that accepts more than the scan-time policy tolerates gets paused on its first vectors anyway, + // so it is not worth the memory it holds or the hashing it costs + float selectivity_threshold; + idx_t n_vectors_to_check; + GetThresholdAndVectorsToCheck(SelectivityOptionalFilterType::BF, selectivity_threshold, n_vectors_to_check); + useful = std::pow(density, static_cast(N_BITS)) < static_cast(selectivity_threshold); } idx_t BloomFilter::GetNumberOfSectors(idx_t number_of_rows) { @@ -78,7 +149,25 @@ inline uint64_t GetMask(const hash_t hash) { return mask; } -void BloomFilter::InsertHashes(const Vector &hashes_v) const { +void BloomFilter::InsertHashes(const Vector &hashes_v, const SelectionVector &sel, const idx_t count) { + D_ASSERT(hashes_v.GetType() == LogicalType::HASH); + if (count == 0) { + return; + } + // VectorOperations::Hash produces a constant vector for a constant input, and a flat one otherwise + if (hashes_v.GetVectorType() == VectorType::CONSTANT_VECTOR) { + InsertOne(*ConstantVector::GetData(hashes_v)); + return; + } + D_ASSERT(hashes_v.GetVectorType() == VectorType::FLAT_VECTOR); + const auto hashes = FlatVector::GetData(hashes_v); + for (idx_t i = 0; i < count; i++) { + // sel can be the incremental selection vector, so this cannot use get_index_unsafe + InsertOne(hashes[sel.get_index(i)]); + } +} + +void BloomFilter::InsertHashes(const Vector &hashes_v) { for (auto hash : hashes_v.Values()) { InsertOne(hash.GetValue()); } @@ -111,17 +200,23 @@ idx_t BloomFilter::LookupHashes(const Vector &hashes_v, const SelectionVector &s return found_count; } -inline void BloomFilter::InsertOne(const hash_t hash) const { +inline void BloomFilter::InsertOne(const hash_t hash) { D_ASSERT(initialized); + D_ASSERT(!sealed); const uint64_t bf_offset = hash & bitmask; const uint64_t mask = GetMask(hash); atomic &slot = *reinterpret_cast *>(&bf[bf_offset]); - slot.fetch_or(mask, std::memory_order_relaxed); + // duplicate keys find their bits already set - checking first keeps the shared sector read-only then, + // instead of a contended read-modify-write + if ((slot.load(std::memory_order_relaxed) & mask) != mask) { + slot.fetch_or(mask, std::memory_order_relaxed); + } } bool BloomFilter::LookupOne(const uint64_t hash) const { D_ASSERT(initialized); + D_ASSERT(sealed); const uint64_t bf_offset = hash & bitmask; const uint64_t mask = GetMask(hash); atomic &slot = *reinterpret_cast *>(&bf[bf_offset]); @@ -130,10 +225,10 @@ bool BloomFilter::LookupOne(const uint64_t hash) const { return (bf_entry & mask) == mask; } -BloomFilterFunctionData::BloomFilterFunctionData(optional_ptr filter_p, bool filters_null_values_p, +BloomFilterFunctionData::BloomFilterFunctionData(shared_ptr filter_p, bool filters_null_values_p, const string &key_column_name_p, const LogicalType &key_type_p, float selectivity_threshold_p, idx_t n_vectors_to_check_p) - : filter(filter_p), filters_null_values(filters_null_values_p), key_column_name(key_column_name_p), + : filter(std::move(filter_p)), filters_null_values(filters_null_values_p), key_column_name(key_column_name_p), key_type(key_type_p), selectivity_threshold(selectivity_threshold_p), n_vectors_to_check(n_vectors_to_check_p) { } @@ -148,18 +243,48 @@ bool BloomFilterFunctionData::Equals(const FunctionData &other_p) const { key_column_name == other.key_column_name && key_type == other.key_type; } -static idx_t SelectBloomFilter(Vector &input, const BloomFilterFunctionData &func_data, SelectionVector &result_sel, - idx_t count) { +struct BloomFilterLocalState : public SelectivityTrackingLocalState { + BloomFilterLocalState(idx_t n_vectors_to_check, float selectivity_threshold) + : SelectivityTrackingLocalState(n_vectors_to_check, selectivity_threshold), hashes(LogicalType::HASH) { + } + + void Prepare(idx_t count) { + if (count <= capacity) { + return; + } + hashes.Initialize(VectorDataInitialization::UNINITIALIZED, count); + bloom_sel.Initialize(count); + capacity = count; + } + + Vector hashes; + SelectionVector bloom_sel; + SelectionVector result_sel; + idx_t capacity = 0; +}; + +static idx_t SelectBloomFilter(Vector &input, const BloomFilterFunctionData &func_data, BloomFilterLocalState &state, + SelectionVector &result_sel, idx_t count) { D_ASSERT(func_data.filter); - Vector hashes(LogicalType::HASH, count); + state.Prepare(count); + auto &hashes = state.hashes; VectorOperations::Hash(input, hashes, count); - hashes.Flatten(); - UnifiedVectorFormat input_data; input.ToUnifiedFormat(input_data); + auto &bloom_sel = input_data.validity.CannotHaveNull() ? result_sel : state.bloom_sel; + idx_t bloom_count; + if (hashes.GetVectorType() == VectorType::CONSTANT_VECTOR) { + bloom_count = func_data.filter->LookupOne(*ConstantVector::GetData(hashes)) ? count : 0; + for (idx_t i = 0; i < bloom_count; i++) { + bloom_sel.set_index(i, i); + } + } else { + bloom_count = func_data.filter->LookupHashes(hashes, bloom_sel, count); + } - SelectionVector bloom_sel(count); - const auto bloom_count = func_data.filter->LookupHashes(hashes, bloom_sel, count); + if (input_data.validity.CannotHaveNull()) { + return bloom_count; + } idx_t result_count = 0; idx_t bloom_idx = 0; @@ -188,7 +313,7 @@ BloomFilterInitLocalState(ExpressionState &state, const BoundFunctionExpression if (!data.filter) { return nullptr; } - return InitSelectivityTrackingLocalState(data.n_vectors_to_check, data.selectivity_threshold); + return make_uniq(data.n_vectors_to_check, data.selectivity_threshold); } static idx_t BloomFilterSelect(DataChunk &args, ExpressionState &state, optional_ptr sel, @@ -196,23 +321,22 @@ static idx_t BloomFilterSelect(DataChunk &args, ExpressionState &state, optional auto &func_expr = state.expr.Cast(); auto &func_data = func_expr.BindInfo()->Cast(); auto local_state_ptr = ExecuteFunctionState::GetFunctionState(state); - auto tracking_state = local_state_ptr ? &local_state_ptr->Cast() : nullptr; - auto count = args.size(); if (!func_data.filter) { return SetAllTrueSelection(count, sel, true_sel, false_sel); } - if (tracking_state && !tracking_state->IsActive()) { - tracking_state->Update(0, 0); + D_ASSERT(local_state_ptr); + auto &local_state = local_state_ptr->Cast(); + if (func_data.n_vectors_to_check != 0 && !local_state.IsActive()) { + local_state.Update(0, 0); return SetAllTrueSelection(count, sel, true_sel, false_sel); } - SelectionVector temp_true(count); - auto result_true_sel = (!true_sel || (sel && true_sel.get() == sel.get())) ? &temp_true : true_sel.get(); - auto approved_count = SelectBloomFilter(args.data[0], func_data, *result_true_sel, count); - approved_count = TranslateSelection(count, sel, *result_true_sel, approved_count, true_sel, false_sel); - if (tracking_state) { - tracking_state->Update(approved_count, count); + auto &result_true_sel = GetFilterResultSelection(count, sel, true_sel, local_state.result_sel); + auto approved_count = SelectBloomFilter(args.data[0], func_data, local_state, result_true_sel, count); + approved_count = TranslateSelection(count, sel, result_true_sel, approved_count, true_sel, false_sel); + if (func_data.n_vectors_to_check != 0) { + local_state.Update(approved_count, count); } return approved_count; } diff --git a/src/duckdb/src/planner/filter/table_filter_dynamic_function.cpp b/src/duckdb/src/planner/filter/table_filter_dynamic_function.cpp index a60b57e0b..4b0a0cbf8 100644 --- a/src/duckdb/src/planner/filter/table_filter_dynamic_function.cpp +++ b/src/duckdb/src/planner/filter/table_filter_dynamic_function.cpp @@ -21,6 +21,7 @@ DynamicFilterData::DynamicFilterData(ExpressionType comparison_type_p, Value con } unique_ptr DynamicFilterData::ToExpression(const Expression &column) const { + annotated_shared_lock guard(lock); return BoundComparisonExpression::Create(comparison_type, column.Copy(), make_uniq(constant)); } @@ -46,6 +47,11 @@ bool DynamicFilterData::CompareValue(ExpressionType comparison_type, const Value FilterPropagateResult DynamicFilterData::CheckStatistics(const BaseStatistics &stats, ExpressionType comparison_type, const Value &constant) { + if (!stats.CanHaveNoNull()) { + return comparison_type == ExpressionType::COMPARE_DISTINCT_FROM ? FilterPropagateResult::FILTER_ALWAYS_TRUE + : FilterPropagateResult::FILTER_ALWAYS_FALSE; + } + switch (constant.type().InternalType()) { case PhysicalType::UINT8: case PhysicalType::UINT16: @@ -115,15 +121,15 @@ static idx_t DynamicFilterSelect(DataChunk &args, ExpressionState &state, option ExpressionType comparison_type; Value constant; { - lock_guard lock(func_data.filter_data->lock); + annotated_shared_lock lock(func_data.filter_data->lock); comparison_type = func_data.filter_data->comparison_type; constant = func_data.filter_data->constant; } - SelectionVector temp_true(count); - auto result_true_sel = (!true_sel || (sel && true_sel.get() == sel.get())) ? &temp_true : true_sel.get(); - auto approved_count = SelectDynamicFilter(args.data[0], comparison_type, constant, *result_true_sel, count); - return TranslateSelection(count, sel, *result_true_sel, approved_count, true_sel, false_sel); + SelectionVector temp_true; + auto &result_true_sel = GetFilterResultSelection(count, sel, true_sel, temp_true); + auto approved_count = SelectDynamicFilter(args.data[0], comparison_type, constant, result_true_sel, count); + return TranslateSelection(count, sel, result_true_sel, approved_count, true_sel, false_sel); } ScalarFunction DynamicFilterScalarFun::GetFunction(const LogicalType &input_type) { @@ -145,7 +151,7 @@ FilterPropagateResult DynamicFilterScalarFun::FilterPrune(const FunctionStatisti if (!data.filter_data) { return FilterPropagateResult::NO_PRUNING_POSSIBLE; } - lock_guard lock(data.filter_data->lock); + annotated_shared_lock lock(data.filter_data->lock); if (!data.filter_data->initialized) { return FilterPropagateResult::NO_PRUNING_POSSIBLE; } diff --git a/src/duckdb/src/planner/filter/table_filter_functions.cpp b/src/duckdb/src/planner/filter/table_filter_functions.cpp index 0d01a4b05..5e84362cf 100644 --- a/src/duckdb/src/planner/filter/table_filter_functions.cpp +++ b/src/duckdb/src/planner/filter/table_filter_functions.cpp @@ -61,6 +61,9 @@ void GetThresholdAndVectorsToCheck(SelectivityOptionalFilterType type, float &se } } +// The table filter functions are registered for (de)serialization only - TableFilterFunctions::Bind rejects +// ordinary binding - so they are constructed and specialized for the column type here rather than resolved +// through the catalog, and carry no catalog or schema name. static unique_ptr CreateSingleArgumentFunctionExpression(const ScalarFunction &function, const LogicalType &target_type, unique_ptr bind_data) { @@ -115,7 +118,8 @@ void TableFilterFunctionSerialize(Serializer &serializer, const optional_ptr TableFilterFunctionDeserialize(Deserializer &deserializer, BoundScalarFunction &function) { auto key_type = function.GetArguments().empty() ? LogicalType::ANY : function.GetArguments()[0]; if (function.GetName() == BloomFilterScalarFun::NAME) { - return make_uniq(nullptr, false, string(), key_type, 0.0f, idx_t(0)); + return make_uniq(shared_ptr(), false, string(), key_type, 0.0f, + idx_t(0)); } if (function.GetName() == PrefixRangeScalarFun::NAME) { return make_uniq(nullptr, true, string(), key_type, 0.0f, idx_t(0)); diff --git a/src/duckdb/src/planner/filter/table_filter_prefix_range_function.cpp b/src/duckdb/src/planner/filter/table_filter_prefix_range_function.cpp index 20709c8f6..62bfabad8 100644 --- a/src/duckdb/src/planner/filter/table_filter_prefix_range_function.cpp +++ b/src/duckdb/src/planner/filter/table_filter_prefix_range_function.cpp @@ -76,6 +76,7 @@ class PrefixRangeBitmap { // Only mark initialized as true when local bitmaps are merged. initialized = false; + dense = false; } unique_ptr InitializeBuildState(ClientContext &context) const { @@ -109,8 +110,12 @@ class PrefixRangeBitmap { } void MergeBuildState(PrefixRangeBitmapBuildState &state) { + const auto last_word_mask = ~uint64_t(0) >> (WORD_MASK - UnsafeNumericCast((span >> shift) & WORD_MASK)); + dense = true; for (idx_t word_idx = 0; word_idx < word_count; word_idx++) { bitmap[word_idx] |= state.bitmap[word_idx]; + const auto expected = word_idx + 1 == word_count ? last_word_mask : ~uint64_t(0); + dense &= bitmap[word_idx] == expected; } initialized = true; } @@ -127,6 +132,9 @@ class PrefixRangeBitmap { const U comparable = CONVERTER::Convert(value.GetValueUnsafe()); const U y = comparable - min; + if (dense) { + return y <= span; + } const U bit_idx = y >> shift; const uint8_t in_range = y <= span; const uint32_t word_idx = (bit_idx >> WORD_SHIFT) & (0U - in_range); @@ -138,6 +146,9 @@ class PrefixRangeBitmap { idx_t LookupKeys(Vector &keys, SelectionVector &result_sel, idx_t count) const { D_ASSERT(count <= keys.size()); auto key_entries = keys.Values(); + if (dense) { + return LookupDense(key_entries, SelectionVector(), result_sel, count); + } idx_t found_count = 0; for (idx_t i = 0; i < count; i++) { const auto key_entry = key_entries[i]; @@ -160,6 +171,9 @@ class PrefixRangeBitmap { template idx_t LookupKeys(Vector &keys, const SelectionVector &sel, SelectionVector &result_sel, idx_t count) const { auto key_entries = keys.Values(); + if (dense) { + return LookupDense(key_entries, sel, result_sel, count); + } idx_t found_count = 0; for (idx_t i = 0; i < count; i++) { const auto key_entry = key_entries[sel.get_index_unsafe(i)]; @@ -179,6 +193,23 @@ class PrefixRangeBitmap { return found_count; } + template + idx_t LookupDense(const VectorIterator &keys, const SelectionVector &sel, SelectionVector &result_sel, + idx_t count) const { + // A bitmap with every bucket set is equivalent to its bounds. + idx_t found_count = 0; + for (idx_t i = 0; i < count; i++) { + const auto entry = keys[sel.get_index(i)]; + if (!entry.IsValid()) { + continue; + } + const U y = CONVERTER::Convert(entry.GetValue()) - min; + result_sel.set_index(found_count, i); + found_count += y <= span; + } + return found_count; + } + FilterPropagateResult LookupRange(U lower_bound, U upper_bound) const { const U lb_y = lower_bound - min; const U lb_bit_idx = lb_y >> shift; @@ -237,6 +268,8 @@ class PrefixRangeBitmap { static constexpr idx_t WORD_MASK = 63; bool initialized = false; + //! All buckets within span are set, excluding padding bits in the final word. + bool dense = false; U min; U span; idx_t shift; @@ -619,10 +652,10 @@ static idx_t PrefixRangeSelect(DataChunk &args, ExpressionState &state, optional return SetAllTrueSelection(count, sel, true_sel, false_sel); } - SelectionVector temp_true(count); - auto result_true_sel = (!true_sel || (sel && true_sel.get() == sel.get())) ? &temp_true : true_sel.get(); - auto approved_count = SelectPrefixRange(args.data[0], func_data, *result_true_sel, count); - approved_count = TranslateSelection(count, sel, *result_true_sel, approved_count, true_sel, false_sel); + SelectionVector temp_true; + auto &result_true_sel = GetFilterResultSelection(count, sel, true_sel, temp_true); + auto approved_count = SelectPrefixRange(args.data[0], func_data, result_true_sel, count); + approved_count = TranslateSelection(count, sel, result_true_sel, approved_count, true_sel, false_sel); if (tracking_state) { tracking_state->Update(approved_count, count); } diff --git a/src/duckdb/src/planner/filter/table_filter_selectivity_optional_function.cpp b/src/duckdb/src/planner/filter/table_filter_selectivity_optional_function.cpp index e6d709e2a..683345063 100644 --- a/src/duckdb/src/planner/filter/table_filter_selectivity_optional_function.cpp +++ b/src/duckdb/src/planner/filter/table_filter_selectivity_optional_function.cpp @@ -34,7 +34,7 @@ void SelectivityOptionalFilterState::SelectivityStats::Update(idx_t accepted, id tuples_accepted = 0; tuples_processed = 0; status = FilterStatus::ACTIVE; - } else if (vectors_processed >= n_vectors_to_check) { + } else if (status == FilterStatus::ACTIVE && vectors_processed >= n_vectors_to_check) { // pause the filter if we processed enough vectors and the selectivity is too high if (GetSelectivity() >= selectivity_threshold) { status = FilterStatus::PAUSED_DUE_TO_HIGH_SELECTIVITY; @@ -145,10 +145,10 @@ static idx_t SelectivityOptionalFilterSelect(DataChunk &args, ExpressionState &s return SetAllTrueSelection(count, sel, true_sel, false_sel); } - SelectionVector temp_true(count); - auto result_true_sel = (!true_sel || (sel && true_sel.get() == sel.get())) ? &temp_true : true_sel.get(); - auto approved_count = local_state.executor.SelectExpression(args, *result_true_sel); - approved_count = TranslateSelection(count, sel, *result_true_sel, approved_count, true_sel, false_sel); + SelectionVector temp_true; + auto &result_true_sel = GetFilterResultSelection(count, sel, true_sel, temp_true); + auto approved_count = local_state.executor.SelectExpression(args, result_true_sel); + approved_count = TranslateSelection(count, sel, result_true_sel, approved_count, true_sel, false_sel); local_state.Update(approved_count, count); return approved_count; } diff --git a/src/duckdb/src/planner/logical_operator_deep_copy.cpp b/src/duckdb/src/planner/logical_operator_deep_copy.cpp index 712c54356..1a8aa50b5 100644 --- a/src/duckdb/src/planner/logical_operator_deep_copy.cpp +++ b/src/duckdb/src/planner/logical_operator_deep_copy.cpp @@ -135,6 +135,15 @@ void LogicalOperatorDeepCopy::VisitOperator(LogicalOperator &op) { ReplaceTableIndex(op); break; } + case LogicalOperatorType::LOGICAL_EMPTY_RESULT: { + auto &empty_result = op.Cast(); + for (auto &binding : empty_result.bindings) { + if (table_idx_replacements.find(binding.table_index) == table_idx_replacements.end()) { + table_idx_replacements[binding.table_index] = binder.GenerateTableIndex(); + } + } + break; + } case LogicalOperatorType::LOGICAL_CTE_REF: { ReplaceTableIndex(op); break; @@ -234,6 +243,26 @@ void TableBindingReplacer::VisitOperator(LogicalOperator &op) { } break; } + case LogicalOperatorType::LOGICAL_SECURE_VIEW: { + auto &view = op.Cast(); + for (auto &binding : view.output_bindings) { + auto entry = table_idx_replacements.find(binding.table_index); + if (entry != table_idx_replacements.end()) { + binding.table_index = entry->second; + } + } + break; + } + case LogicalOperatorType::LOGICAL_EMPTY_RESULT: { + auto &empty_result = op.Cast(); + for (auto &binding : empty_result.bindings) { + auto entry = table_idx_replacements.find(binding.table_index); + if (entry != table_idx_replacements.end()) { + binding.table_index = entry->second; + } + } + break; + } default: break; } diff --git a/src/duckdb/src/planner/logical_plan_verification_result.cpp b/src/duckdb/src/planner/logical_plan_verification_result.cpp index 3a997b438..b6f473064 100644 --- a/src/duckdb/src/planner/logical_plan_verification_result.cpp +++ b/src/duckdb/src/planner/logical_plan_verification_result.cpp @@ -434,7 +434,8 @@ bool LogicalPlanVerificationIssue::IsValid() const { case LogicalPlanVerificationIssueCode::UNSUPPORTED_OPERATOR: return path.has_value() && construct.has_value() && construct->type == LogicalPlanVerificationConstructType::LOGICAL_OPERATOR && - *construct->logical_operator != LogicalOperatorType::LOGICAL_EXTENSION_OPERATOR; + construct->logical_operator.has_value() && + construct->logical_operator.value() != LogicalOperatorType::LOGICAL_EXTENSION_OPERATOR; case LogicalPlanVerificationIssueCode::UNSUPPORTED_EXPRESSION: return path.has_value() && construct.has_value() && construct->type == LogicalPlanVerificationConstructType::EXPRESSION; diff --git a/src/duckdb/src/planner/logical_plan_verifier.cpp b/src/duckdb/src/planner/logical_plan_verifier.cpp index 5dd934c2d..e81783c90 100644 --- a/src/duckdb/src/planner/logical_plan_verifier.cpp +++ b/src/duckdb/src/planner/logical_plan_verifier.cpp @@ -186,6 +186,29 @@ struct LogicalPlanVerificationState { issues.push_back(std::move(issue)); } + void AddNullOperatorChild(LogicalOperator &op, const LogicalPlanVerificationPath &path, idx_t child_index) { + LogicalPlanVerificationIssue issue; + issue.code = LogicalPlanVerificationIssueCode::INTERNAL_INVARIANT; + issue.path = path; + issue.construct = GetOperatorConstruct(op); + issue.facts.emplace_back("invariant", Value("null_operator_child")); + issue.facts.emplace_back("child_index", Value::UBIGINT(child_index)); + issue.message = StringUtil::Format("Logical operator child %llu is null", child_index); + issues.push_back(std::move(issue)); + } + + void AddNullOperatorExpression(LogicalOperator &op, const LogicalPlanVerificationPath &path, + idx_t expression_index) { + LogicalPlanVerificationIssue issue; + issue.code = LogicalPlanVerificationIssueCode::INTERNAL_INVARIANT; + issue.path = path; + issue.construct = GetOperatorConstruct(op); + issue.facts.emplace_back("invariant", Value("null_operator_expression")); + issue.facts.emplace_back("expression_index", Value::UBIGINT(expression_index)); + issue.message = StringUtil::Format("Logical operator expression %llu is null", expression_index); + issues.push_back(std::move(issue)); + } + bool HasResolvedInputs(LogicalOperator &op) const { return resolved_inputs.find(reference(op)) != resolved_inputs.end(); } @@ -194,6 +217,27 @@ struct LogicalPlanVerificationState { return resolved_outputs.find(reference(op)) != resolved_outputs.end(); } + void AddInvalidResolvedSchema(LogicalOperator &op) { + LogicalPlanVerificationIssue issue; + issue.code = LogicalPlanVerificationIssueCode::INTERNAL_INVARIANT; + issue.path = GetPath(op); + issue.construct = GetOperatorConstruct(op); + issue.message = "Resolved operator bindings require a complete output type for every column"; + issues.push_back(std::move(issue)); + } + + bool HasNullSlots(LogicalOperator &op) const { + for (auto &child : op.children) { + if (!child) { + return true; + } + } + bool has_null = false; + LogicalOperatorVisitor::EnumerateExpressions( + op, [&](unique_ptr *expression) { has_null |= !expression || !*expression; }); + return has_null; + } + private: static void AddBindingFacts(LogicalPlanVerificationIssue &issue, const ColumnBinding &binding) { issue.facts.emplace_back("table_index", Value::UBIGINT(binding.table_index.index)); @@ -230,11 +274,19 @@ struct LogicalPlanVerificationState { auto expression_path = path; expression_path.components.push_back( {LogicalPlanVerificationPathComponentType::OPERATOR_EXPRESSION, expression_index++}); + if (!expression || !*expression) { + AddNullOperatorExpression(op, expression_path, expression_index - 1); + return; + } IndexExpression(**expression, expression_path); }); for (idx_t child_index = 0; child_index < op.children.size(); child_index++) { auto child_path = path; child_path.components.push_back({LogicalPlanVerificationPathComponentType::OPERATOR_CHILD, child_index}); + if (!op.children[child_index]) { + AddNullOperatorChild(op, child_path, child_index); + continue; + } IndexOperator(*op.children[child_index], child_path); } } @@ -282,20 +334,42 @@ void LogicalPlanVerifier::Verify(ClientContext &context, LogicalOperator &op) { } } -bool LogicalPlanVerifier::ResolveOperatorTypes(LogicalOperator &op, LogicalPlanVerificationState &verification_state) { - op.types.clear(); +bool LogicalPlanVerifier::ResolveOperatorTypes(LogicalOperator &op, LogicalPlanVerificationState &verification_state, + TypeResolution resolution) { + if (resolution == TypeResolution::RESOLVE) { + op.types.clear(); + } bool children_resolved = true; for (auto &child : op.children) { - if (!ResolveOperatorTypes(*child, verification_state)) { + if (!child) { + children_resolved = false; + continue; + } + if (!ResolveOperatorTypes(*child, verification_state, resolution)) { children_resolved = false; } } - if (!children_resolved) { + if (!children_resolved || verification_state.HasNullSlots(op)) { return false; } verification_state.resolved_inputs.insert(reference(op)); - op.ResolveTypes(); + if (resolution == TypeResolution::RESOLVE) { + op.ResolveTypes(); + } auto bindings = op.GetColumnBindings(); + if (resolution == TypeResolution::EXISTING) { + bool valid = bindings.size() == op.types.size(); + for (auto &type : op.types) { + valid &= type.IsComplete(); + } + for (auto &binding : bindings) { + valid &= binding.table_index.IsValid() && binding.column_index.IsValid(); + } + if (!valid) { + verification_state.AddInvalidResolvedSchema(op); + return false; + } + } if (op.type == LogicalOperatorType::LOGICAL_EXTENSION_OPERATOR) { auto &extension_op = op.Cast(); auto identifier = extension_op.GetTypeBindingVerificationIdentifier(); @@ -357,14 +431,21 @@ void LogicalPlanVerifier::VerifyColumnBindings(LogicalOperator &op, LogicalPlanV return; } for (auto &child : op.children) { - VerifyColumnBindings(*child, verification_state); + if (child) { + VerifyColumnBindings(*child, verification_state); + } } } static void VerifyTableIndexes(LogicalOperator &op, LogicalPlanVerificationState &verification_state, unordered_map &seen_indexes) { for (auto &child : op.children) { - VerifyTableIndexes(*child, verification_state, seen_indexes); + if (child) { + VerifyTableIndexes(*child, verification_state, seen_indexes); + } + } + if (verification_state.HasNullSlots(op)) { + return; } auto table_indexes = op.GetTableIndex(); for (idx_t table_index_ordinal = 0; table_index_ordinal < table_indexes.size(); table_index_ordinal++) { @@ -385,6 +466,20 @@ LogicalPlanVerificationResult LogicalPlanVerifie return VerifyAlwaysInternal(op, nullptr); } +LogicalPlanVerificationResult +LogicalPlanVerifier::VerifyResolvedBindings(LogicalOperator &op) { + LogicalPlanVerificationState verification_state(op); + ResolveOperatorTypes(op, verification_state, TypeResolution::EXISTING); + VerifyColumnBindings(op, verification_state); + unordered_map seen_indexes; + VerifyTableIndexes(op, verification_state, seen_indexes); + if (!verification_state.issues.empty()) { + return LogicalPlanVerificationResult::Failure( + std::move(verification_state.issues)); + } + return LogicalPlanVerificationResult::Success(LogicalPlanVerificationSuccess()); +} + LogicalPlanVerificationResult LogicalPlanVerifier::VerifyAlwaysInternal(LogicalOperator &op, optional_ptr first_error) { LogicalPlanVerificationState verification_state(op); diff --git a/src/duckdb/src/planner/operator/logical_comparison_join.cpp b/src/duckdb/src/planner/operator/logical_comparison_join.cpp index 3e8e1ef19..8554da064 100644 --- a/src/duckdb/src/planner/operator/logical_comparison_join.cpp +++ b/src/duckdb/src/planner/operator/logical_comparison_join.cpp @@ -1,6 +1,7 @@ #include "duckdb/planner/operator/logical_comparison_join.hpp" #include "duckdb/planner/expression/bound_comparison_expression.hpp" #include "duckdb/common/enum_util.hpp" +#include "duckdb/common/type_visitor.hpp" namespace duckdb { @@ -69,4 +70,97 @@ bool LogicalComparisonJoin::HasArbitraryConditions() const { return false; } +static bool MarkJoinTypesMatch(const LogicalType &left, const LogicalType &right) { + if (left != right) { + return false; + } + vector collations; + TypeVisitor::Contains(left, [&](const LogicalType &type) { + if (type.id() == LogicalTypeId::VARCHAR) { + collations.push_back(StringType::GetCollation(type)); + } + return false; + }); + idx_t index = 0; + const auto mismatch = TypeVisitor::Contains(right, [&](const LogicalType &type) { + if (type.id() != LogicalTypeId::VARCHAR) { + return false; + } + if (index >= collations.size()) { + return true; + } + const auto &collation = collations[index]; + index++; + return collation != StringType::GetCollation(type); + }); + return !mismatch && index == collations.size(); +} + +bool LogicalComparisonJoin::HasNullSafeMarkJoinConditions() const { + if (join_type != JoinType::MARK) { + return false; + } + bool has_hash_key = false; + for (auto &condition : conditions) { + if (!condition.IsComparison()) { + return false; + } + switch (condition.GetComparisonType()) { + case ExpressionType::COMPARE_NOT_DISTINCT_FROM: + has_hash_key = true; + break; + case ExpressionType::COMPARE_DISTINCT_FROM: + break; + default: + return false; + } + } + return has_hash_key; +} + +bool LogicalComparisonJoin::TryGetMarkJoinGroupTypes(vector &group_types) const { + group_types.clear(); + if (join_type != JoinType::MARK || conditions.size() < 2) { + return false; + } + vector result; + for (idx_t i = 0; i < conditions.size(); i++) { + auto &condition = conditions[i]; + // Reconstruct matching bound types only; SQL binding may first insert casts. + if (!condition.IsComparison() || condition.GetLHS().GetReturnType() != condition.GetRHS().GetReturnType()) { + return false; + } + if (i + 1 < conditions.size()) { + if (condition.GetComparisonType() != ExpressionType::COMPARE_NOT_DISTINCT_FROM || + !MarkJoinTypesMatch(condition.GetLHS().GetReturnType(), condition.GetRHS().GetReturnType())) { + return false; + } + result.push_back(condition.GetLHS().GetReturnType()); + continue; + } + auto type_id = condition.GetLHS().GetReturnType().id(); + // Native UNION quantifiers are supported, but grouped SQL reconstruction is not yet supported. + if (type_id == LogicalTypeId::TUPLE || type_id == LogicalTypeId::UNION) { + return false; + } + switch (condition.GetComparisonType()) { + case ExpressionType::COMPARE_EQUAL: + case ExpressionType::COMPARE_NOTEQUAL: + break; + case ExpressionType::COMPARE_LESSTHAN: + case ExpressionType::COMPARE_GREATERTHAN: + case ExpressionType::COMPARE_LESSTHANOREQUALTO: + case ExpressionType::COMPARE_GREATERTHANOREQUALTO: + if (condition.GetLHS().GetReturnType().IsNested()) { + return false; + } + break; + default: + return false; + } + } + group_types = std::move(result); + return true; +} + } // namespace duckdb diff --git a/src/duckdb/src/planner/operator/logical_copy_to_file.cpp b/src/duckdb/src/planner/operator/logical_copy_to_file.cpp index 2b9047bd8..947262b3c 100644 --- a/src/duckdb/src/planner/operator/logical_copy_to_file.cpp +++ b/src/duckdb/src/planner/operator/logical_copy_to_file.cpp @@ -76,6 +76,8 @@ void LogicalCopyToFile::Serialize(Serializer &serializer) const { serializer.WritePropertyWithDefault(223, "batches_per_file", batches_per_file, optional_idx()); serializer.WritePropertyWithDefault(224, "order_columns", order_columns); serializer.WritePropertyWithDefault(225, "table_index", table_index, TableIndex(0)); + serializer.WritePropertyWithDefault>(226, "partition_path_expression", + partition_path_expression); } unique_ptr LogicalCopyToFile::Deserialize(Deserializer &deserializer) { @@ -130,6 +132,9 @@ unique_ptr LogicalCopyToFile::Deserialize(Deserializer &deseria auto batches_per_file = deserializer.ReadPropertyWithExplicitDefault(223, "batches_per_file", optional_idx()); auto order_columns = deserializer.ReadPropertyWithExplicitDefault(224, "order_columns", vector()); auto table_index = deserializer.ReadPropertyWithExplicitDefault(225, "table_index", TableIndex(0)); + unique_ptr partition_path_expression; + deserializer.ReadPropertyWithDefault>(226, "partition_path_expression", + partition_path_expression); if (!has_serialize) { // If not serialized, re-bind with the copy info @@ -165,6 +170,7 @@ unique_ptr LogicalCopyToFile::Deserialize(Deserializer &deseria result->batch_size_bytes = batch_size_bytes; result->batches_per_file = batches_per_file; result->order_columns = std::move(order_columns); + result->partition_path_expression = std::move(partition_path_expression); return std::move(result); } diff --git a/src/duckdb/src/planner/operator/logical_explain.cpp b/src/duckdb/src/planner/operator/logical_explain.cpp index 9c771f906..055db3354 100644 --- a/src/duckdb/src/planner/operator/logical_explain.cpp +++ b/src/duckdb/src/planner/operator/logical_explain.cpp @@ -1,4 +1,9 @@ #include "duckdb/planner/operator/logical_explain.hpp" +#include "duckdb/planner/logical_plan_sql_exporter.hpp" +#include "duckdb/planner/operator/logical_column_data_get.hpp" +#include "duckdb/common/types/column/column_data_collection.hpp" +#include "duckdb/common/types/data_chunk.hpp" +#include "duckdb/parser/peg/sql_formatter.hpp" namespace duckdb { @@ -8,6 +13,67 @@ LogicalExplain::LogicalExplain(unique_ptr plan, ExplainType exp children.push_back(std::move(plan)); } +unique_ptr LogicalExplain::CreateSQLResult(ClientContext &context, TableIndex table_index) { + D_ASSERT(explain_type == ExplainType::EXPLAIN_SQL); + D_ASSERT(children.size() == 1); + LogicalPlanSQLExportOptions options; + switch (children[0]->type) { + case LogicalOperatorType::LOGICAL_INSERT: + case LogicalOperatorType::LOGICAL_UPDATE: + case LogicalOperatorType::LOGICAL_DELETE: + case LogicalOperatorType::LOGICAL_MERGE_INTO: + case LogicalOperatorType::LOGICAL_CREATE_TABLE: + case LogicalOperatorType::LOGICAL_COPY_TO_FILE: + // a data-modifying statement reports a change count; that is not a projection of the generated SQL + break; + default: + options.output_names = sql_output_names; + break; + } + auto exported = LogicalPlanSQLExporter::Export(context, *children[0], options); + if (exported.HasError()) { + auto &issue = exported.GetIssues()[0]; + auto message = "EXPLAIN (SQL) cannot render this query: " + issue.message; + if (issue.construct && issue.construct->type == LogicalPlanVerificationConstructType::SOURCE_FUNCTION) { + auto &construct = *issue.construct; + if (construct.function) { + auto &function = *construct.function; + if (function.name != "logical_source") { + message = StringUtil::Format("EXPLAIN (SQL) cannot render table function \"%s\".", function.name); + } + } + } + for (auto &entry : exported.GetIssues()) { + switch (entry.code) { + case LogicalPlanVerificationIssueCode::UNSUPPORTED_OPERATOR: + case LogicalPlanVerificationIssueCode::UNSUPPORTED_EXPRESSION: + case LogicalPlanVerificationIssueCode::UNSUPPORTED_FUNCTION: + case LogicalPlanVerificationIssueCode::UNSUPPORTED_SOURCE: + case LogicalPlanVerificationIssueCode::UNSUPPORTED_EXTENSION: + case LogicalPlanVerificationIssueCode::UNSUPPORTED_EXPORT_FEATURE: + break; + default: + throw InternalException("EXPLAIN (SQL) cannot render this query: " + entry.message); + } + } + if (!allow_unsupported_sql) { + throw NotImplementedException({{"sql_export_unsupported", "true"}}, message); + } + } + vector result_types {LogicalType::VARCHAR, LogicalType::VARCHAR}; + auto collection = + make_uniq(context, result_types, ColumnDataAllocatorType::IN_MEMORY_ALLOCATOR); + if (exported.IsSuccess()) { + auto sql = FormatSQL(exported.GetValue().query->ToString()); + DataChunk chunk; + chunk.Initialize(Allocator::Get(context), result_types); + chunk.data[0].Append(Value("sql")); + chunk.data[1].Append(Value(sql)); + collection->Append(chunk); + } + return make_uniq(table_index, std::move(result_types), std::move(collection)); +} + idx_t LogicalExplain::EstimateCardinality(ClientContext &context) { return 3; } diff --git a/src/duckdb/src/planner/operator/logical_get.cpp b/src/duckdb/src/planner/operator/logical_get.cpp index acf2978c7..da15b8b85 100644 --- a/src/duckdb/src/planner/operator/logical_get.cpp +++ b/src/duckdb/src/planner/operator/logical_get.cpp @@ -9,7 +9,6 @@ #include "duckdb/storage/data_table.hpp" #include "duckdb/common/serializer/serializer.hpp" #include "duckdb/common/serializer/deserializer.hpp" -#include "duckdb/parser/tableref/table_function_ref.hpp" #include "duckdb/planner/filter/expression_filter.hpp" namespace duckdb { @@ -35,10 +34,40 @@ static void ConvertLegacyTableFilters(LogicalGet &get) { } } +//! Pre-v2.0.0 versions key their table filters by table column index. Map each key to the scanned column it names. +static void MapLegacyTableFilterKeys(LogicalGet &get) { + if (get.table_filters.HasMultiColumnFilters()) { + throw SerializationException( + "LogicalGet::Deserialize - unexpected multi-column filters in legacy table filters"); + } + auto &column_ids = get.GetColumnIds(); + vector>> filters; + for (auto &entry : get.table_filters) { + auto table_column = entry.GetIndex().GetIndexUnsafe(); + optional_idx projection_index; + for (idx_t i = 0; i < column_ids.size(); i++) { + auto &column_index = column_ids[i]; + if (column_index.HasPrimaryIndex() && column_index.GetPrimaryIndex() == table_column) { + projection_index = i; + break; + } + } + if (!projection_index.IsValid()) { + throw SerializationException("LogicalGet::Deserialize - table filter on column %llu that is not scanned", + table_column); + } + filters.emplace_back(ProjectionIndex(projection_index.GetIndex()), entry.TakeFilter()); + } + get.table_filters.ClearFilters(); + for (auto &filter : filters) { + get.table_filters.SetFilterByColumnIndex(filter.first, std::move(filter.second)); + } +} + LogicalGet::LogicalGet() : LogicalOperator(LogicalOperatorType::LOGICAL_GET) { } -LogicalGet::LogicalGet(TableIndex table_index, TableFunction function, unique_ptr bind_data, +LogicalGet::LogicalGet(TableIndex table_index, BoundTableFunction function, unique_ptr bind_data, vector returned_types, vector returned_names, virtual_column_map_t virtual_columns_p) : LogicalOperator(LogicalOperatorType::LOGICAL_GET), table_index(table_index), function(std::move(function)), @@ -47,10 +76,10 @@ LogicalGet::LogicalGet(TableIndex table_index, TableFunction function, unique_pt } optional_ptr LogicalGet::GetTable() const { - if (!function.get_bind_info) { + if (!function.get_table_entry) { return nullptr; } - return function.get_bind_info(bind_data.get()).table; + return function.get_table_entry(bind_data.get()); } InsertionOrderPreservingMap LogicalGet::ParamsToString() const { @@ -276,7 +305,13 @@ idx_t LogicalGet::EstimateCardinality(ClientContext &context) { if (has_estimated_cardinality) { return estimated_cardinality; } - if (function.cardinality) { + return EstimateSourceCardinality(context); +} + +idx_t LogicalGet::EstimateSourceCardinality(ClientContext &context) { + // planning a LogicalGet consumes its bind data - the callbacks that require it must not be called, + // while callbacks that accept a null bind data (e.g. the range functions) return no estimate anyway + if (function.cardinality && bind_data) { auto node_stats = function.cardinality(context, bind_data.get()); if (node_stats && node_stats->has_estimated_cardinality) { return node_stats->estimated_cardinality; @@ -305,6 +340,9 @@ void LogicalGet::SetPartitionsToScan(vector partition_indices) { } void LogicalGet::Serialize(Serializer &serializer) const { + if (bind_info) { + throw NotImplementedException("Cannot serialize a table function with process-local bind input"); + } LogicalOperator::Serialize(serializer); serializer.WriteProperty(200, "table_index", table_index); serializer.WriteProperty(201, "returned_types", returned_types); @@ -313,9 +351,7 @@ void LogicalGet::Serialize(Serializer &serializer) const { serializer.WriteProperty(204, "projection_ids", projection_ids); serializer.WriteProperty(205, "table_filters", table_filters); FunctionSerializer::Serialize(serializer, function, bind_data.get()); - if (!function.serialize) { - D_ASSERT(!function.serialize); - // no serialize method: serialize input values and named_parameters for rebinding purposes + if (!function.HasSerializationCallbacks() || serializer.ShouldSerialize(StorageVersion::V2_0_0)) { serializer.WriteProperty(206, "parameters", parameters); serializer.WriteProperty(207, "named_parameters", named_parameters); serializer.WriteProperty(208, "input_table_types", input_table_types); @@ -328,6 +364,11 @@ void LogicalGet::Serialize(Serializer &serializer) const { serializer.WritePropertyWithDefault>(214, "row_group_order_options", row_group_order_options); serializer.WritePropertyWithDefault(215, "scan_partition_indices", scan_partition_indices, vector()); + serializer.WritePropertyWithDefault(216, "source_ordinality", source_ordinality, + OrdinalityType::WITHOUT_ORDINALITY); + serializer.WriteProperty(217, "table_filters_by_projection", true); + serializer.WritePropertyWithDefault(218, "has_pushed_projection", has_pushed_projection, false); + serializer.WritePropertyWithDefault(219, "at_clause", at_clause); } unique_ptr LogicalGet::Deserialize(Deserializer &deserializer) { @@ -340,20 +381,20 @@ unique_ptr LogicalGet::Deserialize(Deserializer &deserializer) deserializer.ReadPropertyWithDefault(203, "column_ids", legacy_column_ids); deserializer.ReadProperty(204, "projection_ids", result->projection_ids); deserializer.ReadProperty(205, "table_filters", result->table_filters); - auto entry = FunctionSerializer::DeserializeBase( - deserializer, CatalogType::TABLE_FUNCTION_ENTRY); + auto entry = FunctionSerializer::DeserializeTableFunction(deserializer); result->function = entry.first; auto &function = result->function; auto has_serialize = entry.second; unique_ptr bind_data; - if (!has_serialize) { - deserializer.ReadProperty(206, "parameters", result->parameters); - deserializer.ReadProperty(207, "named_parameters", result->named_parameters); - deserializer.ReadProperty(208, "input_table_types", result->input_table_types); - deserializer.ReadProperty(209, "input_table_names", result->input_table_names); - } else { + if (has_serialize) { bind_data = FunctionSerializer::FunctionDeserialize(deserializer, function); } + deserializer.ReadPropertyWithDefault(206, "parameters", result->parameters); + deserializer.ReadPropertyWithDefault(207, "named_parameters", result->named_parameters); + // a plan written by an older version holds only the arguments the call passed + function.GetSignature().FillNamedDefaults(deserializer.Get(), result->named_parameters); + deserializer.ReadPropertyWithDefault(208, "input_table_types", result->input_table_types); + deserializer.ReadPropertyWithDefault(209, "input_table_names", result->input_table_names); deserializer.ReadProperty(210, "projected_input", result->projected_input); deserializer.ReadPropertyWithDefault(211, "column_indexes", result->column_ids); result->extra_info = deserializer.ReadPropertyWithExplicitDefault(212, "extra_info", {}); @@ -362,6 +403,13 @@ unique_ptr LogicalGet::Deserialize(Deserializer &deserializer) deserializer.ReadPropertyWithDefault>(214, "row_group_order_options"); auto scan_partition_indices = deserializer.ReadPropertyWithExplicitDefault>(215, "scan_partition_indices", vector()); + result->source_ordinality = deserializer.ReadPropertyWithExplicitDefault( + 216, "source_ordinality", OrdinalityType::WITHOUT_ORDINALITY); + auto table_filters_by_projection = + deserializer.ReadPropertyWithExplicitDefault(217, "table_filters_by_projection", false); + result->has_pushed_projection = + deserializer.ReadPropertyWithExplicitDefault(218, "has_pushed_projection", false); + result->at_clause = deserializer.ReadPropertyWithDefault>(219, "at_clause"); if (!legacy_column_ids.empty()) { if (!result->column_ids.empty()) { throw SerializationException( @@ -371,6 +419,9 @@ unique_ptr LogicalGet::Deserialize(Deserializer &deserializer) result->column_ids.emplace_back(col_id); } } + if (!table_filters_by_projection) { + MapLegacyTableFilterKeys(*result); + } auto &context = deserializer.Get(); virtual_column_map_t virtual_columns; if (!has_serialize) { @@ -382,7 +433,7 @@ unique_ptr LogicalGet::Deserialize(Deserializer &deserializer) vector bind_return_types; vector bind_names; if (!function.bind) { - throw InternalException("Table function \"%s\" has neither bind nor (de)serialize", function.name); + throw InternalException("Table function \"%s\" has neither bind nor (de)serialize", function.GetName()); } bind_data = function.bind(context, input, bind_return_types, bind_names); if (result->ordinality_idx.IsValid()) { @@ -407,7 +458,7 @@ unique_ptr LogicalGet::Deserialize(Deserializer &deserializer) if (bind_return_types[idx] != ret_type) { throw SerializationException("Table function deserialization failure in function %s - column with " "name %s was serialized with type %s, but now has type %s", - function.name, col_name, ret_type, bind_return_types[idx]); + function.GetName(), col_name, ret_type, bind_return_types[idx]); } } } @@ -434,10 +485,11 @@ vector LogicalGet::GetTableIndex() const { string LogicalGet::GetName() const { #ifdef DEBUG if (DBConfigOptions::debug_print_bindings) { - return StringUtil::Upper(function.name.GetIdentifierName()) + StringUtil::Format(" #%llu", table_index.index); + return StringUtil::Upper(function.GetName().GetIdentifierName()) + + StringUtil::Format(" #%llu", table_index.index); } #endif - return StringUtil::Upper(function.name.GetIdentifierName()); + return StringUtil::Upper(function.GetName().GetIdentifierName()); } } // namespace duckdb diff --git a/src/duckdb/src/planner/operator/logical_join.cpp b/src/duckdb/src/planner/operator/logical_join.cpp index c083f190f..ab5ec92f9 100644 --- a/src/duckdb/src/planner/operator/logical_join.cpp +++ b/src/duckdb/src/planner/operator/logical_join.cpp @@ -2,6 +2,7 @@ #include "duckdb/planner/expression/bound_columnref_expression.hpp" #include "duckdb/planner/expression_iterator.hpp" +#include "duckdb/planner/operator/logical_filter.hpp" #include "duckdb/main/config.hpp" namespace duckdb { @@ -83,6 +84,21 @@ void LogicalJoin::GetExpressionBindings(const Expression &root_expr, unordered_s }); } +unique_ptr LogicalJoin::PromoteJoinSide(unique_ptr child, + vector projection_map) { + if (projection_map.empty()) { + return child; + } + auto filter = make_uniq(); + filter->projection_map = std::move(projection_map); + filter->children.push_back(std::move(child)); + if (filter->children[0]->has_estimated_cardinality) { + filter->SetEstimatedCardinality(filter->children[0]->estimated_cardinality); + } + filter->ResolveOperatorTypes(); + return std::move(filter); +} + void LogicalJoin::MoveJoinState(LogicalJoin &source, LogicalJoin &target) { D_ASSERT(source.join_type == target.join_type); target.mark_index = source.mark_index; diff --git a/src/duckdb/src/planner/operator/logical_materialized_cte.cpp b/src/duckdb/src/planner/operator/logical_materialized_cte.cpp index 1a04417df..8c4022903 100644 --- a/src/duckdb/src/planner/operator/logical_materialized_cte.cpp +++ b/src/duckdb/src/planner/operator/logical_materialized_cte.cpp @@ -1,7 +1,21 @@ #include "duckdb/planner/operator/logical_materialized_cte.hpp" +#include "duckdb/planner/operator/logical_cteref.hpp" namespace duckdb { +bool CTEFilterDependency::MatchesConsumers(const vector> &references) const { + if (references.size() != 2) { + return false; + } + idx_t row_scans = 0; + idx_t domain_scans = 0; + for (auto &ref : references) { + row_scans += ref.get().table_index == row_scan; + domain_scans += ref.get().table_index == domain_scan; + } + return row_scans == 1 && domain_scans == 1; +} + InsertionOrderPreservingMap LogicalMaterializedCTE::ParamsToString() const { InsertionOrderPreservingMap result; result["CTE Name"] = ctename.GetIdentifierName(); diff --git a/src/duckdb/src/planner/operator/logical_projection.cpp b/src/duckdb/src/planner/operator/logical_projection.cpp index 9fced651e..9058289ab 100644 --- a/src/duckdb/src/planner/operator/logical_projection.cpp +++ b/src/duckdb/src/planner/operator/logical_projection.cpp @@ -1,6 +1,7 @@ #include "duckdb/planner/operator/logical_projection.hpp" #include "duckdb/main/config.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" namespace duckdb { @@ -8,6 +9,20 @@ LogicalProjection::LogicalProjection(TableIndex table_index, vector LogicalProjection::CreateIdentity(TableIndex table_index, + unique_ptr child) { + child->ResolveOperatorTypes(); + auto bindings = child->GetColumnBindings(); + vector> expressions; + expressions.reserve(bindings.size()); + for (idx_t i = 0; i < bindings.size(); i++) { + expressions.push_back(make_uniq(child->types[i], bindings[i])); + } + auto projection = make_uniq(table_index, std::move(expressions)); + projection->children.push_back(std::move(child)); + return projection; +} + vector LogicalProjection::GetColumnBindings() { return GenerateColumnBindings(table_index, expressions.size()); } diff --git a/src/duckdb/src/planner/operator/logical_secure_view.cpp b/src/duckdb/src/planner/operator/logical_secure_view.cpp index 8d20b0158..719025285 100644 --- a/src/duckdb/src/planner/operator/logical_secure_view.cpp +++ b/src/duckdb/src/planner/operator/logical_secure_view.cpp @@ -1,9 +1,48 @@ #include "duckdb/planner/operator/logical_secure_view.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/tableref/bound_at_clause.hpp" #include "duckdb/common/string_util.hpp" +#include "duckdb/planner/operator/logical_get.hpp" namespace duckdb { +//! Statistics gathered inside a view describe the data it reads - they may only be exposed to the caller if +//! every row that is read is also emitted. Any operator that removes rows means the statistics below it also cover +//! rows that the view hides, so only row-preserving operators are accepted here. +static bool EmitsAllRows(const LogicalOperator &op) { + switch (op.type) { + case LogicalOperatorType::LOGICAL_SECURE_VIEW: + // a nested secure view applies this same rule to its own contents - nothing beyond what it exposes itself + // can reach this boundary, so there is no need to look inside it + return true; + case LogicalOperatorType::LOGICAL_PROJECTION: + case LogicalOperatorType::LOGICAL_ORDER_BY: + case LogicalOperatorType::LOGICAL_EXPRESSION_GET: + case LogicalOperatorType::LOGICAL_DUMMY_SCAN: + break; + case LogicalOperatorType::LOGICAL_GET: { + auto &get = op.Cast(); + if (get.table_filters.HasFilters() || get.extra_info.sample_options || get.dynamic_filters) { + return false; + } + if (!get.children.empty()) { + // an in-out function decides itself which of the rows it reads it emits + return false; + } + break; + } + default: + return false; + } + for (auto &child : op.children) { + if (!EmitsAllRows(*child)) { + return false; + } + } + return true; +} + LogicalSecureView::LogicalSecureView() : LogicalOperator(LogicalOperatorType::LOGICAL_SECURE_VIEW) { } @@ -12,6 +51,36 @@ LogicalSecureView::LogicalSecureView(string view_name_p, unique_ptr source_types_p, optional_ptr at_clause, + unique_ptr child) + : LogicalOperator(LogicalOperatorType::LOGICAL_SECURE_VIEW), view_name(std::move(view_name_p)), has_source(true), + source_name(std::move(source_name_p)), source_types(std::move(source_types_p)), + has_at_clause(at_clause != nullptr) { + if (at_clause) { + at_unit = at_clause->Unit(); + at_value = at_clause->GetValue(); + } + output_bindings = child->GetColumnBindings(); + D_ASSERT(output_bindings.size() == source_types.size()); + for (idx_t i = 0; i < source_types.size(); i++) { + output_expressions.push_back( + make_uniq(source_types[i], ColumnBinding(TableIndex(0), ProjectionIndex(i)))); + } + children.push_back(std::move(child)); +} + +void LogicalSecureView::AnalyzeStatistics(LogicalOperator &plan) { + if (plan.type == LogicalOperatorType::LOGICAL_SECURE_VIEW) { + // the statistics of a view may only be exposed if the view emits every row it reads - otherwise they would + // describe values from rows that the view does not return + plan.Cast().propagate_statistics = EmitsAllRows(*plan.children[0]); + } + for (auto &child : plan.children) { + AnalyzeStatistics(*child); + } +} + vector LogicalSecureView::GetColumnBindings() { return children[0]->GetColumnBindings(); } @@ -22,7 +91,11 @@ idx_t LogicalSecureView::EstimateCardinality(ClientContext &context) { // filters using the statistics of the view contents must not escape the boundary return estimated_cardinality; } - return children[0]->EstimateCardinality(context); + if (!propagate_statistics) { + // the estimate of the child changes as filters are pushed into the view - never remember it here + return children[0]->EstimateCardinality(context); + } + return LogicalOperator::EstimateCardinality(context); } InsertionOrderPreservingMap LogicalSecureView::ParamsToString() const { diff --git a/src/duckdb/src/planner/planner.cpp b/src/duckdb/src/planner/planner.cpp index ba4bad57b..1ff2280ba 100644 --- a/src/duckdb/src/planner/planner.cpp +++ b/src/duckdb/src/planner/planner.cpp @@ -26,6 +26,7 @@ #include "duckdb/planner/operator_extension.hpp" #include "duckdb/planner/planner_extension.hpp" #include "duckdb/planner/logical_plan_verifier.hpp" +#include "duckdb/planner/operator/logical_explain.hpp" #include "duckdb/optimizer/optimizer.hpp" namespace duckdb { @@ -33,6 +34,49 @@ namespace duckdb { Planner::Planner(ClientContext &context) : binder(Binder::CreateBinder(context)), context(context) { } +static void RenderSQLExplain(unique_ptr &op, ClientContext &context, Binder &binder) { + if (op->type == LogicalOperatorType::LOGICAL_PREPARE || op->type == LogicalOperatorType::LOGICAL_EXECUTE) { + for (auto &child : op->children) { + RenderSQLExplain(child, context, binder); + } + } else if (op->type == LogicalOperatorType::LOGICAL_EXPLAIN) { + auto &explain = op->Cast(); + if (explain.explain_type == ExplainType::EXPLAIN_SQL) { + op = explain.CreateSQLResult(context, binder.GenerateTableIndex()); + } + } +} + +void Planner::Optimize() { + auto &profiler = QueryProfiler::Get(context); +#ifdef DEBUG + plan->Verify(context); +#endif + bool optimize = Settings::Get(context); + if (Settings::Get(context)) { + // verify disable optimizer - disable EXCEPT for explain, otherwise every single EXPLAIN query breaks + if (plan->type != LogicalOperatorType::LOGICAL_EXPLAIN) { + optimize = false; + } + } + if (plan->RequireOptimizer()) { + { + auto optimizer_timer = profiler.StartTimer(); + Optimizer optimizer(*binder, context); + if (optimize) { + plan = optimizer.Optimize(std::move(plan)); + } else { + plan = optimizer.LowerMandatoryAggregateRewrites(std::move(plan)); + } + D_ASSERT(plan); + } +#ifdef DEBUG + plan->Verify(context); +#endif + } + RenderSQLExplain(plan, context, *binder); +} + // Pre-decorrelation pass: replace LogicalTrigger with LogicalDependentJoin so the standard // FlattenDependentJoins machinery can decorrelate the trigger body. static void RewriteTriggersToDependent(Binder &binder, LogicalOperator &op) { @@ -196,9 +240,12 @@ void Planner::CreatePlan(SQLStatement &statement) { auto max_tree_depth = Settings::Get(context); CheckTreeDepth(*plan, max_tree_depth); - RewriteTriggersToDependent(*this->binder, *this->plan); - RecursiveDependentJoinPlanner::Plan(*this->binder, this->plan); - this->plan = FlattenDependentJoins::DecorrelateIndependent(*this->binder, std::move(this->plan)); + // A LogicalPlanStatement carries an already planned (and possibly optimized) plan + if (statement.type != StatementType::LOGICAL_PLAN_STATEMENT) { + RewriteTriggersToDependent(*this->binder, *this->plan); + RecursiveDependentJoinPlanner::Plan(*this->binder, this->plan); + this->plan = FlattenDependentJoins::DecorrelateIndependent(*this->binder, std::move(this->plan)); + } D_ASSERT(!ContainsDependentJoin(*this->plan)); D_ASSERT(VerifyPlannedExpressions(*this->plan)); D_ASSERT(VerifyCanonicalComparisonJoins(*this->plan)); diff --git a/src/duckdb/src/planner/sql_export/bound_expression_sql_exporter.cpp b/src/duckdb/src/planner/sql_export/bound_expression_sql_exporter.cpp new file mode 100644 index 000000000..90f34b278 --- /dev/null +++ b/src/duckdb/src/planner/sql_export/bound_expression_sql_exporter.cpp @@ -0,0 +1,681 @@ +#include "duckdb/planner/sql_export/bound_expression_sql_exporter_internal.hpp" +#include "duckdb/common/extension_type_info.hpp" +#include "duckdb/function/scalar/compressed_materialization_utils.hpp" +#include "duckdb/planner/bound_expression_sql_exporter.hpp" +#include "duckdb/planner/sql_export_helpers.hpp" +#include "duckdb/parser/expression/between_expression.hpp" +#include "duckdb/parser/expression/case_expression.hpp" +#include "duckdb/parser/expression/cast_expression.hpp" +#include "duckdb/parser/expression/columnref_expression.hpp" +#include "duckdb/parser/expression/collate_expression.hpp" +#include "duckdb/parser/expression/comparison_expression.hpp" +#include "duckdb/parser/expression/conjunction_expression.hpp" +#include "duckdb/parser/expression/function_expression.hpp" +#include "duckdb/parser/expression/operator_expression.hpp" +#include "duckdb/planner/expression/bound_aggregate_expression.hpp" +#include "duckdb/planner/expression/bound_between_expression.hpp" +#include "duckdb/planner/expression/bound_case_expression.hpp" +#include "duckdb/planner/expression/bound_cast_expression.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/expression/bound_comparison_expression.hpp" +#include "duckdb/planner/expression/bound_conjunction_expression.hpp" +#include "duckdb/planner/expression/bound_constant_expression.hpp" +#include "duckdb/planner/expression/bound_function_expression.hpp" +#include "duckdb/planner/expression/bound_operator_expression.hpp" +#include "duckdb/planner/expression/bound_reference_expression.hpp" +#include "duckdb/function/cast/cast_function_set.hpp" +#include "duckdb/function/scalar/generic_common.hpp" +#include "duckdb/planner/expression/bound_window_expression.hpp" +#include "duckdb/planner/expression/bound_unnest_expression.hpp" + +namespace duckdb { + +static inline bool IsExpressionRootPath(const LogicalPlanVerificationPath &path) { + if (!path.IsValid()) { + return false; + } + if (path.root == LogicalPlanVerificationPathRoot::STANDALONE_EXPRESSION) { + return true; + } + for (auto &component : path.components) { + if (component.type == LogicalPlanVerificationPathComponentType::OPERATOR_EXPRESSION) { + return true; + } + } + return false; +} + +static LogicalPlanVerificationIssue UnsupportedExpression(const LogicalPlanVerificationPath &path, + ExpressionClass expression_class) { + return SQLExportHelpers::MakeIssue( + LogicalPlanVerificationIssueCode::UNSUPPORTED_EXPRESSION, LogicalPlanVerificationPhase::EXPRESSION_EXPORT, path, + LogicalPlanVerificationConstructIdentity::Expression(expression_class), + "The bound expression class does not have a SQL AST representation in this exporter"); +} + +static inline bool ChildrenAreConsistentWithArguments(const vector> &children, + const vector &arguments) { + if (children.size() != arguments.size()) { + return false; + } + for (idx_t child_index = 0; child_index < children.size(); child_index++) { + if (!children[child_index] || !children[child_index]->GetReturnType().IsComplete()) { + return false; + } + if (arguments[child_index].IsComplete() && children[child_index]->GetReturnType() != arguments[child_index]) { + return false; + } + } + return true; +} + +LogicalPlanVerificationIssue +BoundExpressionSQLExportState::InternalInvariant(optional path, string message, + optional construct) { + return SQLExportHelpers::MakeIssue(LogicalPlanVerificationIssueCode::INTERNAL_INVARIANT, + LogicalPlanVerificationPhase::EXPRESSION_EXPORT, std::move(path), + std::move(construct), std::move(message)); +} + +LogicalPlanVerificationIssue +BoundExpressionSQLExportState::InternalExpressionInvariant(const LogicalPlanVerificationPath &path, + const Expression &expression, string message) { + return BoundExpressionSQLExportState::InternalInvariant( + path, std::move(message), + LogicalPlanVerificationConstructIdentity::Expression(expression.GetExpressionClass())); +} + +LogicalPlanVerificationIssue BoundExpressionSQLExportState::UnsupportedFeature(const LogicalPlanVerificationPath &path, + string feature, string message) { + return SQLExportHelpers::MakeIssue( + LogicalPlanVerificationIssueCode::UNSUPPORTED_EXPORT_FEATURE, LogicalPlanVerificationPhase::EXPRESSION_EXPORT, + path, LogicalPlanVerificationConstructIdentity::ExportFeature(std::move(feature)), std::move(message)); +} + +LogicalPlanVerificationIssue +BoundExpressionSQLExportState::UnsupportedFunction(const LogicalPlanVerificationPath &path, + LogicalPlanVerificationFunctionIdentity identity, string message) { + return SQLExportHelpers::MakeIssue( + LogicalPlanVerificationIssueCode::UNSUPPORTED_FUNCTION, LogicalPlanVerificationPhase::EXPRESSION_EXPORT, path, + LogicalPlanVerificationConstructIdentity::Function(std::move(identity)), std::move(message)); +} + +bool BoundExpressionSQLExportState::HasNestedCollation(const LogicalType &type) { + return type.id() != LogicalTypeId::VARCHAR && TypeVisitor::Contains(type, [](const LogicalType &child) { + return child.id() == LogicalTypeId::VARCHAR && !StringType::GetCollation(child).empty(); + }); +} + +BoundExpressionSQLExportResult +BoundExpressionSQLExportState::PreserveCollation(const LogicalType &type, BoundExpressionSQLExportResult result, + const LogicalPlanVerificationPath &path) { + if (result.HasError()) { + return result; + } + if (type.id() == LogicalTypeId::VARCHAR) { + auto collation = StringType::GetCollation(type); + if (!collation.empty()) { + result.GetValue() = make_uniq(std::move(collation), std::move(result.GetValue())); + } + } else if (BoundExpressionSQLExportState::HasNestedCollation(type)) { + return CastToConstructedType(type, std::move(result.GetValue()), path); + } + return result; +} + +LogicalType BoundExpressionSQLExportState::SQLCastType(const LogicalType &type) { + // Scalar collations are applied by COLLATE, outside the cast's type expression. + return type.id() == LogicalTypeId::VARCHAR && !type.HasAlias() ? LogicalType::VARCHAR : type; +} + +unique_ptr BoundExpressionSQLExportState::SQLCast(const LogicalType &type, + unique_ptr child, bool try_cast) { + return make_uniq(BoundExpressionSQLExportState::SQLCastType(type), std::move(child), try_cast); +} + +BoundExpressionSQLExportState::BoundExpressionSQLExportState(const BoundExpressionSQLExportContext &context_p) + : context(context_p) { +} + +BoundExpressionSQLExportResult BoundExpressionSQLExportState::Export(const Expression &expression, + const LogicalPlanVerificationPath &path) { + auto result = ExportInternal(expression, path); + const bool preserves_collation = expression.GetExpressionClass() == ExpressionClass::BOUND_CONSTANT || + (expression.GetExpressionClass() == ExpressionClass::BOUND_FUNCTION && + expression.GetReturnType().id() != LogicalTypeId::VARCHAR) || + (expression.GetExpressionClass() == ExpressionClass::BOUND_AGGREGATE && + expression.GetReturnType().IsAggregateState()); + if (expression.GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF || preserves_collation) { + return result; + } + return BoundExpressionSQLExportState::PreserveCollation(expression.GetReturnType(), std::move(result), path); +} + +BoundExpressionSQLExportResult BoundExpressionSQLExportState::ExportInternal(const Expression &expression, + const LogicalPlanVerificationPath &path) { + switch (expression.GetExpressionClass()) { + case ExpressionClass::BOUND_CONSTANT: + return ExportConstant(expression.Cast(), path); + case ExpressionClass::BOUND_COLUMN_REF: + return ExportColumnRef(expression.Cast(), path); + case ExpressionClass::BOUND_REF: + return ExportReference(expression.Cast(), path); + case ExpressionClass::BOUND_FUNCTION: + return ExportFunction(expression.Cast(), path); + case ExpressionClass::BOUND_CONJUNCTION: + return ExportConjunction(expression.Cast(), path); + case ExpressionClass::BOUND_CASE: + return ExportCase(expression.Cast(), path); + case ExpressionClass::BOUND_OPERATOR: + return ExportOperator(expression.Cast(), path); + case ExpressionClass::BOUND_AGGREGATE: + return ExportAggregate(expression.Cast(), path); + case ExpressionClass::BOUND_DEFAULT: + case ExpressionClass::BOUND_PARAMETER: + case ExpressionClass::BOUND_SUBQUERY: + case ExpressionClass::BOUND_WINDOW: + case ExpressionClass::BOUND_UNNEST: + case ExpressionClass::BOUND_LAMBDA: + case ExpressionClass::BOUND_LAMBDA_REF: + case ExpressionClass::LEGACY_BOUND_CAST: + case ExpressionClass::LEGACY_BOUND_COMPARISON: + case ExpressionClass::LEGACY_BOUND_BETWEEN: + return BoundExpressionSQLExportResult::Failure({UnsupportedExpression(path, expression.GetExpressionClass())}); + case ExpressionClass::BOUND_EXPANDED: + case ExpressionClass::AGGREGATE: + case ExpressionClass::CASE: + case ExpressionClass::CAST: + case ExpressionClass::COLUMN_REF: + case ExpressionClass::COMPARISON: + case ExpressionClass::CONJUNCTION: + case ExpressionClass::CONSTANT: + case ExpressionClass::DEFAULT: + case ExpressionClass::FUNCTION: + case ExpressionClass::OPERATOR: + case ExpressionClass::STAR: + case ExpressionClass::SUBQUERY: + case ExpressionClass::WINDOW: + case ExpressionClass::PARAMETER: + case ExpressionClass::COLLATE: + case ExpressionClass::LAMBDA: + case ExpressionClass::POSITIONAL_REFERENCE: + case ExpressionClass::BETWEEN: + case ExpressionClass::LAMBDA_REF: + case ExpressionClass::PATTERN: + case ExpressionClass::TYPE: + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::InternalExpressionInvariant( + path, expression, "Expression export requires a final bound class")}); + case ExpressionClass::INVALID: + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::InternalInvariant( + path, "Expression export received an invalid expression class")}); + } + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::InternalInvariant( + path, "Expression export received an unknown expression class")}); +} + +BoundExpressionSQLExportResult BoundExpressionSQLExportState::ExportUnnest(const BoundUnnestExpression &expression, + const LogicalPlanVerificationPath &path) { + D_ASSERT(expression.Child()); + auto child = ExportChild(*expression.Child(), path, 0); + if (child.HasError()) { + return child; + } + vector> arguments; + arguments.push_back(std::move(child.GetValue())); + return BoundExpressionSQLExportState::PreserveCollation( + expression.GetReturnType(), + BoundExpressionSQLExportResult::Success(make_uniq("unnest", std::move(arguments))), path); +} + +BoundExpressionSQLExportResult +BoundExpressionSQLExportState::ExportReference(const BoundReferenceExpression &expression, + const LogicalPlanVerificationPath &path) { + if (expression.GetExpressionType() != ExpressionType::BOUND_REF || lambda_reference_scopes.empty() || + expression.Index() >= lambda_reference_scopes.back().size()) { + return BoundExpressionSQLExportResult::Failure({UnsupportedExpression(path, expression.GetExpressionClass())}); + } + return BoundExpressionSQLExportResult::Success(lambda_reference_scopes.back()[expression.Index()]->Copy()); +} + +BoundExpressionSQLExportResult +BoundExpressionSQLExportState::ExportColumnRef(const BoundColumnRefExpression &expression, + const LogicalPlanVerificationPath &path) { + D_ASSERT(expression.GetExpressionType() == ExpressionType::BOUND_COLUMN_REF); + auto &binding = expression.Binding(); + if (!binding.table_index.IsValid() || !binding.column_index.IsValid()) { + return BoundExpressionSQLExportResult::Failure( + {InvalidBinding(path, binding, "Bound column reference has an incomplete binding")}); + } + if (!SQLExportHelpers::IsSQLValueType(expression.GetReturnType())) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::InternalExpressionInvariant( + path, expression, "Bound column reference has an incomplete type")}); + } + if (expression.Depth() != 0) { + auto issue = BoundExpressionSQLExportState::UnsupportedFeature( + path, "correlated_column_reference", "Correlated column references require an owning query export context"); + issue.facts.emplace_back("depth", Value::UBIGINT(expression.Depth())); + return BoundExpressionSQLExportResult::Failure({std::move(issue)}); + } + if (!context.resolve_binding) { + return BoundExpressionSQLExportResult::Failure( + {InvalidBinding(path, binding, "No SQL column binding resolver was provided")}); + } + auto resolved = context.resolve_binding(binding); + if (!resolved) { + return BoundExpressionSQLExportResult::Failure( + {InvalidBinding(path, binding, "The SQL column binding resolver has no matching entry")}); + } + if (resolved->names.empty()) { + return BoundExpressionSQLExportResult::Failure( + {InvalidBinding(path, binding, "The resolved SQL column name is empty")}); + } + for (auto &name : resolved->names) { + if (!SQLExportHelpers::IsValidIdentifier(name)) { + return BoundExpressionSQLExportResult::Failure( + {InvalidBinding(path, binding, "The resolved SQL column name contains an invalid identifier")}); + } + } + if (!SQLExportHelpers::IsSQLValueType(resolved->type)) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::InternalExpressionInvariant( + path, expression, "The resolved SQL column type is incomplete")}); + } + auto optimizer_type_match = resolved->optimizer_type && *resolved->optimizer_type == expression.GetReturnType(); + if (resolved->type != expression.GetReturnType() && !optimizer_type_match) { + LogicalPlanVerificationIssue issue; + issue.code = LogicalPlanVerificationIssueCode::TYPE_MISMATCH; + issue.phase = LogicalPlanVerificationPhase::EXPRESSION_EXPORT; + issue.path = path; + issue.construct = + LogicalPlanVerificationConstructIdentity::BindingTypeMismatch(resolved->type, expression.GetReturnType()); + issue.message = "The resolved SQL column type differs from the bound expression type"; + return BoundExpressionSQLExportResult::Failure({std::move(issue)}); + } + auto replacement = substitutions.find(binding); + auto result = BoundExpressionSQLExportResult::Success( + replacement == substitutions.end() ? make_uniq(std::move(resolved->names)) + : replacement->second.get().Copy()); + result.GetValue()->ClearAlias(); + if (optimizer_type_match) { + return result; + } + if (!expression.GetReturnType().EqualsIncludingCollation(resolved->type)) { + if (expression.GetReturnType().id() != LogicalTypeId::VARCHAR) { + auto issue = BoundExpressionSQLExportState::UnsupportedFeature( + path, "nested_result_collation", + "Changing nested input collations requires a typed SQL representation"); + issue.facts.emplace_back("logical_type", Value(expression.GetReturnType().ToString())); + issue.facts.emplace_back("input_logical_type", Value(resolved->type.ToString())); + issue.facts.emplace_back("input_varchar_collations", + Value(SQLExportHelpers::TypeCollationSignature(resolved->type))); + issue.facts.emplace_back("varchar_collations", + Value(SQLExportHelpers::TypeCollationSignature(expression.GetReturnType()))); + return BoundExpressionSQLExportResult::Failure({std::move(issue)}); + } + if (StringType::GetCollation(expression.GetReturnType()).empty()) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFeature( + path, "column_collation_reset", "Clearing an input collation requires a SQL representation")}); + } + return BoundExpressionSQLExportState::PreserveCollation(expression.GetReturnType(), std::move(result), path); + } + return result; +} + +LogicalPlanVerificationIssue BoundExpressionSQLExportState::InvalidBinding(const LogicalPlanVerificationPath &path, + const ColumnBinding &binding, + string message) { + LogicalPlanVerificationIssue issue; + issue.code = LogicalPlanVerificationIssueCode::INVALID_BINDING; + issue.phase = LogicalPlanVerificationPhase::EXPRESSION_EXPORT; + issue.path = path; + issue.facts.emplace_back("column_index", Value::UBIGINT(binding.column_index.GetIndexUnsafe())); + issue.facts.emplace_back("table_index", Value::UBIGINT(binding.table_index.index)); + issue.message = std::move(message); + return issue; +} + +BoundExpressionSQLExportResult BoundExpressionSQLExportState::ExportFunction(const BoundFunctionExpression &expression, + const LogicalPlanVerificationPath &path) { + switch (expression.GetExpressionType()) { + case ExpressionType::BOUND_FUNCTION: + return ExportScalarFunction(expression, path); + case ExpressionType::OPERATOR_CAST: + return ExportCast(expression, path); + case ExpressionType::COMPARE_EQUAL: + case ExpressionType::COMPARE_NOTEQUAL: + case ExpressionType::COMPARE_LESSTHAN: + case ExpressionType::COMPARE_GREATERTHAN: + case ExpressionType::COMPARE_LESSTHANOREQUALTO: + case ExpressionType::COMPARE_GREATERTHANOREQUALTO: + case ExpressionType::COMPARE_DISTINCT_FROM: + case ExpressionType::COMPARE_NOT_DISTINCT_FROM: + return ExportComparison(expression, path); + case ExpressionType::COMPARE_BETWEEN: + return ExportBetween(expression, path); + default: + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::InternalExpressionInvariant( + path, expression, "Bound function has an invalid expression type")}); + } +} + +BoundExpressionSQLExportResult BoundExpressionSQLExportState::ExportCast(const BoundFunctionExpression &expression, + const LogicalPlanVerificationPath &path) { + D_ASSERT(expression.GetExpressionType() == ExpressionType::OPERATOR_CAST); + D_ASSERT(expression.GetChildren().size() == 1 && expression.GetChildren()[0]); + D_ASSERT(BoundCastExpression::HasValidBindData(expression)); + D_ASSERT(ChildrenAreConsistentWithArguments(expression.GetChildren(), expression.Function().GetArguments())); + D_ASSERT(expression.GetReturnType() == expression.Function().GetReturnType()); + if (!SQLExportHelpers::IsSQLRepresentableType(expression.GetReturnType()) || + !SQLExportHelpers::IsSQLRepresentableType(expression.GetChildren()[0]->GetReturnType())) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFeature( + path, "cast_type", "The cast type has no SQL type representation")}); + } + if (context.discard_optimizer_metadata && CMUtils::GetExpressionType(expression) == CMExpressionType::CAST) { + if (!BoundCastExpression::IsDefaultCast(expression)) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFeature( + path, "compressed_materialization_cast", + "The compressed materialization projection contains a non-default cast")}); + } + return ExportChild(*expression.GetChildren()[0], path, 0); + } + if (BoundCastExpression::IsDefaultCast(expression)) { + if (!context.client_context || + CastFunctionSet::Get(*context.client_context) + .CanOverrideDefaultCast(expression.GetChildren()[0]->GetReturnType(), expression.GetReturnType())) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFeature( + path, "default_cast_binding", + "A default-only bound cast cannot be reconstructed through this SQL binding")}); + } + } + auto child = ExportChild(*expression.GetChildren()[0], path, 0); + if (child.HasError()) { + return child; + } + if (expression.GetReturnType().IsAggregateState()) { + auto storage_type = expression.GetReturnType().WithAlias("").WithExtensionInfo(nullptr); + auto &source_type = expression.GetChildren()[0]->GetReturnType(); + if (BoundCastExpression::IsTryCast(expression) || !context.client_context || + CastFunctionSet::Get(*context.client_context) + .CanOverrideDefaultCast(source_type, expression.GetReturnType()) || + CastFunctionSet::Get(*context.client_context).CanOverrideDefaultCast(source_type, storage_type)) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFeature( + path, "aggregate_state_try_cast", + "Aggregate state TRY_CAST or custom casts require a SQL representation")}); + } + auto storage = RestoreResultType(storage_type, std::move(child.GetValue()), path); + if (storage.HasError()) { + return storage; + } + auto result = ExportAggregateFunction::StateToSQL(expression.GetReturnType(), std::move(storage.GetValue())); + if (!result) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFeature( + path, "aggregate_state_parameters", "Aggregate state SQL parameters are not representable")}); + } + return BoundExpressionSQLExportResult::Success(std::move(result)); + } + if (BoundExpressionSQLExportState::HasNestedCollation(expression.GetReturnType())) { + if (BoundCastExpression::IsTryCast(expression) || + expression.GetReturnType() == expression.GetChildren()[0]->GetReturnType()) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFeature( + path, "nested_result_collation", "This cast cannot preserve nested collations through SQL")}); + } + return CastToConstructedType(expression.GetReturnType(), std::move(child.GetValue()), path); + } + if (RequiresConstantConstructor(expression.GetReturnType()) && !BoundCastExpression::IsTryCast(expression)) { + return CastToConstructedType(expression.GetReturnType(), std::move(child.GetValue()), path); + } + return BoundExpressionSQLExportResult::Success(BoundExpressionSQLExportState::SQLCast( + expression.GetReturnType(), std::move(child.GetValue()), BoundCastExpression::IsTryCast(expression))); +} + +BoundExpressionSQLExportResult +BoundExpressionSQLExportState::ExportComparison(const BoundFunctionExpression &expression, + const LogicalPlanVerificationPath &path) { + D_ASSERT(BoundComparisonExpression::IsComparison(expression.GetExpressionType())); + D_ASSERT(expression.GetReturnType() == LogicalType::BOOLEAN); + D_ASSERT(expression.GetChildren().size() == 2 && expression.GetChildren()[0] && expression.GetChildren()[1]); + D_ASSERT(!expression.BindInfo()); + D_ASSERT(ChildrenAreConsistentWithArguments(expression.GetChildren(), expression.Function().GetArguments())); + D_ASSERT(expression.GetReturnType() == expression.Function().GetReturnType()); + auto &left_type = expression.GetChildren()[0]->GetReturnType(); + auto &right_type = expression.GetChildren()[1]->GetReturnType(); + // the regex range filter compares VARCHAR values with BLOB bounds, as the bounds need not be valid UTF-8 + const bool varchar_blob = (left_type.id() == LogicalTypeId::VARCHAR && right_type.id() == LogicalTypeId::BLOB) || + (left_type.id() == LogicalTypeId::BLOB && right_type.id() == LogicalTypeId::VARCHAR); + D_ASSERT(left_type == right_type || varchar_blob); + vector> children; + vector issues; + ExportChildren(expression.GetChildren(), path, children, issues); + if (!issues.empty()) { + return BoundExpressionSQLExportResult::Failure(std::move(issues)); + } + if (varchar_blob) { + // encode reinterprets the VARCHAR bytes as a BLOB, so the bytes are compared as before + auto &varchar_child = left_type.id() == LogicalTypeId::VARCHAR ? children[0] : children[1]; + vector> arguments; + arguments.push_back(std::move(varchar_child)); + varchar_child = SQLExportHelpers::SystemFunction("encode", std::move(arguments)); + } + return BoundExpressionSQLExportResult::Success(make_uniq( + expression.GetExpressionType(), std::move(children[0]), std::move(children[1]))); +} + +BoundExpressionSQLExportResult BoundExpressionSQLExportState::ExportBetween(const BoundFunctionExpression &expression, + const LogicalPlanVerificationPath &path) { + D_ASSERT(expression.GetExpressionType() == ExpressionType::COMPARE_BETWEEN); + D_ASSERT(expression.GetReturnType() == LogicalType::BOOLEAN); + D_ASSERT(expression.GetChildren().size() == 3 && expression.GetChildren()[0] && expression.GetChildren()[1] && + expression.GetChildren()[2]); + D_ASSERT(BoundBetweenExpression::HasValidBindData(expression)); + D_ASSERT(ChildrenAreConsistentWithArguments(expression.GetChildren(), expression.Function().GetArguments())); + D_ASSERT(expression.GetReturnType() == expression.Function().GetReturnType()); + D_ASSERT(expression.GetChildren()[0]->GetReturnType() == expression.GetChildren()[1]->GetReturnType()); + D_ASSERT(expression.GetChildren()[0]->GetReturnType() == expression.GetChildren()[2]->GetReturnType()); + vector> children; + vector issues; + ExportChildren(expression.GetChildren(), path, children, issues); + if (!issues.empty()) { + return BoundExpressionSQLExportResult::Failure(std::move(issues)); + } + auto lower_inclusive = BoundBetweenExpression::LowerInclusive(expression); + auto upper_inclusive = BoundBetweenExpression::UpperInclusive(expression); + if (lower_inclusive && upper_inclusive) { + return BoundExpressionSQLExportResult::Success( + make_uniq(std::move(children[0]), std::move(children[1]), std::move(children[2]))); + } + if (expression.GetChildren()[0]->IsVolatile()) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFeature( + path, "exclusive_between_input_evaluation", + "An exclusive BETWEEN cannot duplicate a volatile input while preserving evaluation semantics")}); + } + auto lower = make_uniq(BoundBetweenExpression::LowerComparisonType(expression), + children[0]->Copy(), std::move(children[1])); + auto upper = make_uniq(BoundBetweenExpression::UpperComparisonType(expression), + std::move(children[0]), std::move(children[2])); + return BoundExpressionSQLExportResult::Success( + make_uniq(ExpressionType::CONJUNCTION_AND, std::move(lower), std::move(upper))); +} + +BoundExpressionSQLExportResult +BoundExpressionSQLExportState::ExportConjunction(const BoundConjunctionExpression &expression, + const LogicalPlanVerificationPath &path) { + D_ASSERT(expression.GetExpressionType() == ExpressionType::CONJUNCTION_AND || + expression.GetExpressionType() == ExpressionType::CONJUNCTION_OR); + D_ASSERT(expression.GetReturnType() == LogicalType::BOOLEAN); + D_ASSERT(expression.GetChildren().size() >= 2); + vector> children; + vector issues; + ExportChildren(expression.GetChildren(), path, children, issues, LogicalType::BOOLEAN); + if (!issues.empty()) { + return BoundExpressionSQLExportResult::Failure(std::move(issues)); + } + auto result = make_uniq(expression.GetExpressionType()); + result->GetChildrenMutable() = std::move(children); + return BoundExpressionSQLExportResult::Success(std::move(result)); +} + +BoundExpressionSQLExportResult BoundExpressionSQLExportState::ExportCase(const BoundCaseExpression &expression, + const LogicalPlanVerificationPath &path) { + D_ASSERT(expression.GetExpressionType() == ExpressionType::CASE_EXPR); + D_ASSERT(expression.GetReturnType().IsComplete()); + D_ASSERT(!expression.CaseChecks().empty()); + vector source_children; + for (auto &check : expression.CaseChecks()) { + source_children.emplace_back(check.when_expr.get(), LogicalType::BOOLEAN); + source_children.emplace_back(check.then_expr.get(), expression.GetReturnType()); + } + source_children.emplace_back(expression.ElseExpression().get(), expression.GetReturnType()); + + vector> children; + vector issues; + ExportChildren(source_children, path, children, issues); + if (!issues.empty()) { + return BoundExpressionSQLExportResult::Failure(std::move(issues)); + } + auto result = make_uniq(); + for (idx_t check_index = 0; check_index < expression.CaseChecks().size(); check_index++) { + CaseCheck check; + check.when_expr = std::move(children[check_index * 2]); + check.then_expr = std::move(children[check_index * 2 + 1]); + result->CaseChecksMutable().push_back(std::move(check)); + } + result->ElseMutable() = std::move(children.back()); + return BoundExpressionSQLExportResult::Success(std::move(result)); +} + +BoundExpressionSQLExportResult BoundExpressionSQLExportState::ExportOperator(const BoundOperatorExpression &expression, + const LogicalPlanVerificationPath &path) { + optional expected_type; + switch (expression.GetExpressionType()) { + case ExpressionType::OPERATOR_NOT: + D_ASSERT(expression.GetChildren().size() == 1 && expression.GetReturnType() == LogicalType::BOOLEAN); + expected_type = LogicalType::BOOLEAN; + break; + case ExpressionType::OPERATOR_IS_NULL: + case ExpressionType::OPERATOR_IS_NOT_NULL: + D_ASSERT(expression.GetChildren().size() == 1 && expression.GetReturnType() == LogicalType::BOOLEAN); + break; + case ExpressionType::COMPARE_IN: + case ExpressionType::COMPARE_NOT_IN: + D_ASSERT(expression.GetChildren().size() >= 2 && expression.GetReturnType() == LogicalType::BOOLEAN && + expression.GetChildren()[0]); + expected_type = expression.GetChildren()[0]->GetReturnType(); + break; + case ExpressionType::OPERATOR_COALESCE: + D_ASSERT(expression.GetChildren().size() >= 2 && expression.GetReturnType().IsComplete()); + expected_type = expression.GetReturnType(); + break; + case ExpressionType::OPERATOR_TRY: + D_ASSERT(expression.GetChildren().size() == 1 && expression.GetChildren()[0] && + expression.GetReturnType().IsComplete()); + if (expression.GetChildren()[0]->IsVolatile()) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFeature( + path, "try_volatile_child", "TRY cannot be rebound around a volatile expression")}); + } + expected_type = expression.GetReturnType(); + break; + case ExpressionType::OPERATOR_UNPACK: + case ExpressionType::OPERATOR_NULLIF: + case ExpressionType::GROUPING_FUNCTION: + case ExpressionType::ARRAY_EXTRACT: + case ExpressionType::ARRAY_SLICE: + case ExpressionType::STRUCT_EXTRACT: + case ExpressionType::ARRAY_CONSTRUCTOR: + case ExpressionType::ARROW: + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFeature( + path, "bound_operator", "The bound operator has no admitted parsed SQL AST form")}); + default: + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::InternalExpressionInvariant( + path, expression, "Bound operator has an invalid expression type")}); + } + vector> children; + vector issues; + ExportChildren(expression.GetChildren(), path, children, issues, expected_type); + if (!issues.empty()) { + return BoundExpressionSQLExportResult::Failure(std::move(issues)); + } + return BoundExpressionSQLExportResult::Success( + make_uniq(expression.GetExpressionType(), std::move(children))); +} + +BoundExpressionSQLExportResult BoundExpressionSQLExportState::ExportChild(const Expression &expression, + const LogicalPlanVerificationPath &path, + idx_t child_index) { + return Export(expression, SQLExportHelpers::ChildPath(path, child_index)); +} + +void BoundExpressionSQLExportState::ExportChildren(const vector> &source, + const LogicalPlanVerificationPath &path, + vector> &result, + vector &issues, + const optional &expected_type) { + vector source_children; + for (auto &child : source) { + source_children.emplace_back(child.get(), expected_type); + } + ExportChildren(source_children, path, result, issues); +} + +void BoundExpressionSQLExportState::ExportChildren(const vector &source, + const LogicalPlanVerificationPath &path, + vector> &result, + vector &issues) { + result.resize(source.size()); + for (idx_t child_index = 0; child_index < source.size(); child_index++) { + auto child_path = SQLExportHelpers::ChildPath(path, child_index); + auto &input = source[child_index]; + D_ASSERT(input.expression); + D_ASSERT(!input.expected_type || input.expression->GetReturnType() == *input.expected_type); + auto child = Export(*input.expression, child_path); + if (child.HasError()) { + for (auto &issue : child.GetIssues()) { + issues.push_back(issue); + } + } else { + result[child_index] = std::move(child.GetValue()); + } + } +} + +LogicalPlanVerificationResult> +BoundExpressionSQLExporter::Export(const Expression &expression, const BoundExpressionSQLExportContext &context) { + LogicalPlanVerificationPath path; + path.root = LogicalPlanVerificationPathRoot::STANDALONE_EXPRESSION; + return ExportAtPath(expression, context, path); +} + +LogicalPlanVerificationResult> +BoundExpressionSQLExporter::ExportAtPath(const Expression &expression, const BoundExpressionSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + D_ASSERT(IsExpressionRootPath(path)); + BoundExpressionSQLExportState state(context); + return state.Export(expression, path); +} + +LogicalPlanVerificationResult> +BoundExpressionSQLExporter::ExportAggregateCallAtPath(const BoundAggregateExpression &expression, + const BoundExpressionSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + D_ASSERT(IsExpressionRootPath(path)); + BoundExpressionSQLExportState state(context); + return state.ExportAggregateCall(expression, path); +} + +LogicalPlanVerificationResult> +BoundExpressionSQLExporter::ExportWindowAtPath(const BoundWindowExpression &expression, + const BoundExpressionSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + D_ASSERT(IsExpressionRootPath(path)); + BoundExpressionSQLExportState state(context); + return state.ExportWindow(expression, path); +} + +LogicalPlanVerificationResult> +BoundExpressionSQLExporter::ExportUnnestAtPath(const BoundUnnestExpression &expression, + const BoundExpressionSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + D_ASSERT(IsExpressionRootPath(path)); + BoundExpressionSQLExportState state(context); + return state.ExportUnnest(expression, path); +} + +} // namespace duckdb diff --git a/src/duckdb/src/planner/sql_export/logical_plan_sql_exporter.cpp b/src/duckdb/src/planner/sql_export/logical_plan_sql_exporter.cpp new file mode 100644 index 000000000..efaa90a6b --- /dev/null +++ b/src/duckdb/src/planner/sql_export/logical_plan_sql_exporter.cpp @@ -0,0 +1,479 @@ +#include "duckdb/planner/sql_export/logical_plan_sql_exporter_internal.hpp" +#include "duckdb/function/scalar/compressed_materialization_utils.hpp" +#include "duckdb/planner/logical_plan_sql_exporter.hpp" +#include "duckdb/planner/sql_export/bound_expression_sql_exporter_internal.hpp" +#include "duckdb/parser/expression/columnref_expression.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/parser/expression/subquery_expression.hpp" +#include "duckdb/parser/parsed_expression_iterator.hpp" +#include "duckdb/parser/query_node/select_node.hpp" +#include "duckdb/parser/tableref/basetableref.hpp" +#include "duckdb/parser/common_table_expression_info.hpp" +#include "duckdb/planner/bound_expression_sql_exporter.hpp" +#include "duckdb/planner/operator/logical_get.hpp" +#include "duckdb/planner/expression/bound_aggregate_expression.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/expression/bound_constant_expression.hpp" +#include "duckdb/planner/expression/bound_function_expression.hpp" +#include "duckdb/planner/logical_operator.hpp" +#include "duckdb/planner/logical_plan_verifier.hpp" +#include "duckdb/planner/operator/logical_aggregate.hpp" +#include "duckdb/planner/operator/logical_expression_get.hpp" +#include "duckdb/planner/sql_export_helpers.hpp" +#include "duckdb/planner/operator/logical_projection.hpp" +#include "duckdb/common/unordered_set.hpp" +#include + +namespace duckdb { + +static LogicalPlanVerificationIssue UnsupportedOperator(const LogicalPlanVerificationPath &path, + LogicalOperatorType type) { + return SQLExportHelpers::MakeIssue(LogicalPlanVerificationIssueCode::UNSUPPORTED_OPERATOR, + LogicalPlanVerificationPhase::PLAN_EXPORT, path, + LogicalPlanVerificationConstructIdentity::LogicalOperator(type), + "The logical operator does not have a SQL AST representation in this exporter"); +} + +static optional ConstantSQLInput(const Expression &expression, LogicalOperator &input, + const LogicalPlanSQLExportContext &context) { + if (expression.GetExpressionClass() == ExpressionClass::BOUND_CONSTANT) { + return expression.Cast().GetValue(); + } + if (auto wrapped = CMUtils::GetWrappedInput(expression)) { + return ConstantSQLInput(*wrapped, input, context); + } + if (context.FindSource(input) || expression.GetExpressionClass() != ExpressionClass::BOUND_COLUMN_REF || + input.children.size() != 1) { + return {}; + } + auto &column = expression.Cast(); + if (column.Depth() != 0) { + return {}; + } + switch (input.type) { + case LogicalOperatorType::LOGICAL_PROJECTION: { + auto &projection = input.Cast(); + if (column.Binding().table_index != projection.table_index) { + return {}; + } + return ConstantSQLInput(*projection.expressions[column.Binding().column_index], *input.children[0], context); + } + case LogicalOperatorType::LOGICAL_AGGREGATE_AND_GROUP_BY: { + auto &aggregate = input.Cast(); + auto index = column.Binding().column_index; + if (column.Binding().table_index != aggregate.group_index) { + return {}; + } + for (auto &grouping_set : aggregate.grouping_sets) { + if (!grouping_set.count(index)) { + return {}; + } + } + return ConstantSQLInput(*aggregate.groups[index], *input.children[0], context); + } + case LogicalOperatorType::LOGICAL_FILTER: + case LogicalOperatorType::LOGICAL_ORDER_BY: + case LogicalOperatorType::LOGICAL_TOP_N: + case LogicalOperatorType::LOGICAL_LIMIT: + case LogicalOperatorType::LOGICAL_DISTINCT: + return ConstantSQLInput(expression, *input.children[0], context); + default: + return {}; + } +} + +static LogicalPlanSQLExportResult EnsureSQLProjection(LogicalPlanSQLExportResult result) { + if (result.HasError() || !result.GetValue().fields.empty() || + result.GetValue().query->type != QueryNodeType::SELECT_NODE) { + return result; + } + auto &select = result.GetValue().query->Cast(); + if (select.select_list.empty()) { + // SQL needs a SELECT expression even when the logical relation only carries row counts. + select.select_list.push_back(make_uniq(Literal::Null())); + } + return result; +} + +LogicalPlanSQLExportResult LogicalPlanSQLExportHelpers::ApplyOutputNames(LogicalPlanSQLExportResult result, + const vector &output_names) { + if (result.HasError()) { + return result; + } + if (SQLExportDMLHelpers::IsDMLNode(*result.GetValue().query)) { + // a data-modifying statement's result shape is inherent; its names are not a projection + return result; + } + if (output_names.size() != result.GetValue().fields.size()) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + LogicalPlanVerificationPath(), "output_names", "Output name count does not match the exported plan")}); + } + if (output_names.empty()) { + return result; + } + if (result.GetValue().query->type == QueryNodeType::SELECT_NODE) { + auto &select = result.GetValue().query->Cast(); + D_ASSERT(select.select_list.size() == output_names.size()); + for (idx_t i = 0; i < output_names.size(); i++) { + select.select_list[i]->SetAlias(output_names[i]); + } + return result; + } + auto fields = result.GetValue().fields; + LogicalPlanSQLExportedChild child {std::move(result.GetValue()), Identifier("exported_query")}; + auto select = make_uniq(); + for (idx_t i = 0; i < output_names.size(); i++) { + auto expression = + make_uniq(LogicalPlanSQLExportHelpers::FieldIdentifier(i), child.relation_alias); + expression->SetAlias(output_names[i]); + select->select_list.push_back(std::move(expression)); + } + select->from_table = LogicalPlanSQLExportHelpers::CreateSubquery(std::move(child)); + return LogicalPlanSQLExportResult::Success({std::move(select), std::move(fields)}); +} + +struct LogicalPlanSQLExportContext::SourceScope { + SourceScope(LogicalPlanSQLExportContext &context_p, const vector &sources_p) + : context(context_p), sources(sources_p), parent(context.source_scope) { + context.source_scope = this; + } + ~SourceScope() { + context.source_scope = parent; + } + + LogicalPlanSQLExportContext &context; + const vector &sources; + optional_ptr parent; +}; + +LogicalPlanSQLExportContext::LogicalPlanSQLExportContext(ClientContext &context_p) : context(context_p) { +} + +optional_ptr +LogicalPlanSQLExportContext::FindSource(const LogicalOperator &op) const { + for (auto scope = source_scope; scope; scope = scope->parent) { + for (auto &source : scope->sources) { + if (&source.op.get() == &op) { + return &source; + } + } + } + return nullptr; +} + +LogicalPlanSQLExportResult LogicalPlanSQLExportContext::Export(LogicalOperator &op, + const LogicalPlanVerificationPath &path) { + if (auto source = FindSource(op)) { + auto read = CreateNamedSource(source->name, source->relation.fields); + source_reads.insert(*read->from_table); + return EnsureSQLProjection(LogicalPlanSQLExportResult::Success({std::move(read), source->relation.fields})); + } + ancestors.push_back(op); + auto result = op.ToSQL(*this, path); + ancestors.pop_back(); + return EnsureSQLProjection(std::move(result)); +} + +void LogicalPlanSQLExportContext::PushNamedRelation(TableIndex index, const Identifier &name, bool is_recurring) { + named_relations.push_back({index, name, is_recurring, 0}); +} + +idx_t LogicalPlanSQLExportContext::PopNamedRelation() { + D_ASSERT(!named_relations.empty()); + auto references = named_relations.back().references; + named_relations.pop_back(); + return references; +} + +optional LogicalPlanSQLExportContext::ReferenceNamedRelation(TableIndex index, bool is_recurring) { + for (idx_t i = named_relations.size(); i > 0; i--) { + auto &relation = named_relations[i - 1]; + if (relation.index == index && relation.is_recurring == is_recurring) { + relation.references++; + return relation.name; + } + } + return optional(); +} + +Identifier LogicalPlanSQLExportContext::NextRelationAlias(const Identifier &preferred) { + if (!preferred.empty() && relation_aliases.insert(preferred).second) { + return preferred; + } + while (true) { + auto name = Identifier("r" + to_string(next_relation_ordinal++)); + if (relation_aliases.insert(name).second) { + return name; + } + } +} + +static void VisitRelationReferences(QueryNode &query, const std::function &callback) { + std::function visit_expression = [&](const ParsedExpression &expression) { + if (expression.GetExpressionClass() == ExpressionClass::SUBQUERY) { + VisitRelationReferences(*expression.Cast().Subquery()->node, callback); + } + ParsedExpressionIterator::EnumerateChildren(expression, visit_expression); + }; + ParsedExpressionIterator::EnumerateQueryNodeChildren( + query, [&](unique_ptr &expression) { visit_expression(*expression); }, callback); +} + +void LogicalPlanSQLExportContext::ReserveRelationNames(QueryNode &query) { + VisitRelationReferences(query, [&](TableRef &ref) { + if (ref.type == TableReferenceType::BASE_TABLE) { + relation_aliases.insert(ref.Cast().Table()); + } + }); +} + +optional +LogicalPlanSQLExportContext::FindCapturedSourceReference(QueryNode &query, const identifier_set_t &source_names) const { + optional captured; + VisitRelationReferences(query, [&](TableRef &ref) { + if (captured || ref.type != TableReferenceType::BASE_TABLE) { + return; + } + // Qualified references bind past the generated WITH scope. + auto &name = ref.Cast().GetQualifiedName(); + if (!name.Catalog().empty() || !name.Schema().empty()) { + return; + } + if (source_names.count(name.Name()) && + source_reads.find(reference(ref)) == source_reads.end()) { + captured = name.Name(); + } + }); + return captured; +} + +LogicalPlanVerificationResult +LogicalPlanSQLExportContext::ExportChild(LogicalOperator &child, const LogicalPlanVerificationPath &path) { + auto exported = Export(child, path); + if (exported.HasError()) { + return LogicalPlanVerificationResult::Failure(exported); + } + LogicalPlanSQLExportedChild result {std::move(exported.GetValue()), NextRelationAlias()}; + return LogicalPlanVerificationResult::Success(std::move(result)); +} + +LogicalPlanVerificationResult +LogicalPlanSQLExportContext::ExportChild(LogicalOperator &child, const LogicalPlanVerificationPath &path, + const vector &sources) { + SourceScope scope(*this, sources); + return ExportChild(child, path); +} + +LogicalPlanVerificationResult +LogicalPlanSQLExportContext::ExportRoot(LogicalOperator &root, const LogicalPlanVerificationPath &path, + const vector &sources, + const optional> &output_names) { + SourceScope scope(*this, sources); + auto returning = SQLExportDMLHelpers::MatchReturningRoot(root); + // a replaced root or a replaced statement below it exports through the ordinary + // relational path, which reads the supplied replacement instead of the statement + if (!returning || SQLExportDMLHelpers::ContainsReplacedBoundary(*this, root)) { + return ExportChild(root, path); + } + // consumers of an exported statement address its RETURNING columns positionally + optional> names = output_names; + if (!names) { + vector positional; + for (idx_t i = 0; i < returning->expressions.size(); i++) { + positional.push_back(LogicalPlanSQLExportHelpers::FieldIdentifier(i)); + } + names = std::move(positional); + } + auto exported = SQLExportDMLHelpers::ExportReturning(*this, *returning, names, path); + if (exported.HasError()) { + return LogicalPlanVerificationResult::Failure(exported); + } + return LogicalPlanVerificationResult::Success( + {std::move(exported.GetValue()), NextRelationAlias()}); +} + +LogicalPlanVerificationResult> LogicalPlanSQLExportContext::ExportExpression( + const LogicalOperator &op, const vector> &expressions, idx_t expression_ordinal, + const BoundExpressionSQLExportContext &expression_context, const LogicalPlanVerificationPath &path, + optional_ptr composition) { + D_ASSERT(expression_ordinal < expressions.size()); + auto &expression = expressions[expression_ordinal].get(); + unique_ptr restored; + if (expression.GetExpressionClass() == ExpressionClass::BOUND_AGGREGATE && op.children.size() == 1) { + auto &arguments = expression.Cast().GetChildren(); + for (idx_t i = 0; i < arguments.size(); i++) { + if (arguments[i]->GetExpressionClass() == ExpressionClass::BOUND_CONSTANT) { + continue; + } + auto value = ConstantSQLInput(*arguments[i], *op.children[0], *this); + if (value && value->type().EqualsIncludingCollation(arguments[i]->GetReturnType())) { + if (!restored) { + restored = expression.Copy(); + } + restored->Cast().GetChildrenMutable()[i] = + make_uniq(*value); + } + } + } + if (composition) { + return composition->Export(restored ? *restored : expression, + LogicalPlanSQLExportHelpers::PlanExpressionPath(path, expression_ordinal)); + } + return BoundExpressionSQLExporter::ExportAtPath( + restored ? *restored : expression, expression_context, + LogicalPlanSQLExportHelpers::PlanExpressionPath(path, expression_ordinal)); +} + +unique_ptr LogicalPlanSQLExportContext::ForwardFields(const LogicalPlanSQLExportedChild &child, + const vector &fields, + optional_ptr plain) { + auto select = make_uniq(); + for (auto &field : fields) { + bool found = false; + for (idx_t i = 0; i < child.relation.fields.size(); i++) { + if (field.source_binding != child.relation.fields[i].source_binding) { + continue; + } + auto expression = + plain ? plain->select_list[i]->Copy() : LogicalPlanSQLExportHelpers::ChildColumn(child, i); + expression->SetAlias(LogicalPlanSQLExportHelpers::FieldIdentifier(select->select_list.size())); + select->select_list.push_back(std::move(expression)); + found = true; + break; + } + D_ASSERT(found); + (void)found; + } + return select; +} + +LogicalPlanVerificationResult +LogicalOperator::ToSQL(LogicalPlanSQLExportContext &, const LogicalPlanVerificationPath &path) { + if (type == LogicalOperatorType::LOGICAL_DELIM_GET) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::UnsupportedSource( + path, LogicalPlanSQLExportHelpers::LogicalSourceIdentity(), "delim_get")}); + } + D_ASSERT(type != LogicalOperatorType::LOGICAL_INVALID); + return LogicalPlanSQLExportResult::Failure({UnsupportedOperator(path, type)}); +} + +LogicalPlanVerificationResult +LogicalPlanSQLExporter::Export(ClientContext &context, LogicalOperator &root, + const LogicalPlanSQLExportOptions &options) { + auto verification = LogicalPlanVerifier::VerifyAlways(root); + if (verification.HasError()) { + return LogicalPlanVerificationResult::Failure(verification); + } + LogicalPlanSQLExportContext state(context); + if (auto returning = SQLExportDMLHelpers::MatchReturningRoot(root)) { + // output names become the RETURNING aliases, so they are not reapplied below + return SQLExportDMLHelpers::ExportReturning(state, *returning, options.output_names, + LogicalPlanVerificationPath()); + } + auto result = state.Export(root, LogicalPlanVerificationPath()); + if (!options.output_names) { + return result; + } + return LogicalPlanSQLExportHelpers::ApplyOutputNames(std::move(result), *options.output_names); +} + +LogicalPlanSQLExportResult +LogicalPlanSQLExporter::ExportWithSources(ClientContext &context, LogicalOperator &root, + const vector &replacements, + const LogicalPlanSQLExportOptions &options) { + auto verification = LogicalPlanVerifier::VerifyResolvedBindings(root); + if (verification.HasError()) { + return LogicalPlanSQLExportResult::Failure(verification); + } + auto invalid = [](const string &message) { + auto issue = SQLExportHelpers::MakeIssue( + LogicalPlanVerificationIssueCode::INTERNAL_INVARIANT, LogicalPlanVerificationPhase::PLAN_EXPORT, + LogicalPlanVerificationPath(), LogicalPlanVerificationConstructIdentity::ExportFeature("source_boundary"), + message); + return LogicalPlanSQLExportResult::Failure({std::move(issue)}); + }; + unordered_set boundaries; + for (auto &replacement : replacements) { + auto &op = replacement.op.get(); + if (!boundaries.insert(&op).second || !replacement.relation.query) { + return invalid("SQL source replacement requires a unique subtree and an owned relation"); + } + auto bindings = op.GetColumnBindings(); + if (replacement.relation.fields.size() != bindings.size() || op.types.size() != bindings.size()) { + return invalid("SQL source replacement width differs from its subtree"); + } + for (idx_t i = 0; i < bindings.size(); i++) { + auto &field = replacement.relation.fields[i]; + auto &type = field.optimizer_type ? *field.optimizer_type : field.type; + if (field.source_binding != bindings[i] || !field.type.IsComplete() || + !type.EqualsIncludingCollation(op.types[i])) { + return invalid("SQL source replacement bindings or types differ from its subtree"); + } + } + } + unordered_set reached; + bool overlap = false; + std::function visit = [&](LogicalOperator &op, bool replaced) { + if (boundaries.count(&op)) { + overlap |= replaced; + reached.insert(&op); + replaced = true; + } + for (auto &child : op.children) { + visit(*child, replaced); + } + }; + visit(root, false); + if (overlap || reached.size() != boundaries.size()) { + return invalid("SQL source replacements must be disjoint subtrees of this plan"); + } + LogicalPlanSQLExportContext state(context); + vector sources; + // All definitions share a CTE scope. Reserve references from every replacement + // before allocating any name, so a generated source cannot capture another query's input. + for (auto &replacement : replacements) { + state.ReserveRelationNames(*replacement.relation.query); + } + for (auto &replacement : replacements) { + sources.push_back({replacement.op, + state.NextRelationAlias(Identifier("__export_source")), + {replacement.relation.query->Copy(), replacement.relation.fields}}); + } + auto exported = state.ExportRoot(root, LogicalPlanVerificationPath(), sources, options.output_names); + if (exported.HasError()) { + return LogicalPlanSQLExportResult::Failure(exported); + } + auto result = std::move(exported.GetValue().relation); + // an exported statement that cannot carry a WITH clause holds its definitions in its source query + auto &definitions = SQLExportStatementHelpers::SourceDefinitionNode(*result.query); + auto consumers = std::move(definitions.cte_map); + definitions.cte_map = CommonTableExpressionMap(); + for (auto &source : sources) { + auto info = make_uniq(); + for (idx_t i = 0; i < source.relation.fields.size(); i++) { + info->aliases.push_back(LogicalPlanSQLExportHelpers::FieldIdentifier(i)); + } + info->query_node = std::move(source.relation.query); + info->materialized = CTEMaterialize::CTE_MATERIALIZE_ALWAYS; + definitions.cte_map.map.insert(source.name, std::move(info)); + } + for (auto &consumer : consumers.map) { + definitions.cte_map.map.insert(consumer.first, std::move(consumer.second)); + } + // A source callback may emit an unqualified reference the plan verifier never sees; + // refuse a result whose generated names would rebind such a reference. + identifier_set_t source_names; + for (auto &source : sources) { + source_names.insert(source.name); + } + if (auto captured = state.FindCapturedSourceReference(definitions, source_names)) { + return invalid("A generated SQL source name would capture a relation reference of the exported plan: " + + captured->GetIdentifierName()); + } + auto success = LogicalPlanSQLExportResult::Success(std::move(result)); + return options.output_names + ? LogicalPlanSQLExportHelpers::ApplyOutputNames(std::move(success), *options.output_names) + : std::move(success); +} + +} // namespace duckdb diff --git a/src/duckdb/src/planner/sql_export/sql_export_composition.cpp b/src/duckdb/src/planner/sql_export/sql_export_composition.cpp new file mode 100644 index 000000000..21298cdce --- /dev/null +++ b/src/duckdb/src/planner/sql_export/sql_export_composition.cpp @@ -0,0 +1,370 @@ +#include "duckdb/planner/sql_export/logical_plan_sql_exporter_internal.hpp" +#include "duckdb/planner/sql_export/bound_expression_sql_exporter_internal.hpp" +#include "duckdb/function/scalar/compressed_materialization_utils.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/expression_iterator.hpp" +#include "duckdb/planner/operator/logical_aggregate.hpp" +#include "duckdb/planner/operator/logical_projection.hpp" + +namespace duckdb { + +bool LogicalPlanSQLExportHelpers::HasSimpleGroups(const LogicalAggregate &aggregate) { + if (!aggregate.grouping_functions.empty() || aggregate.grouping_sets.size() > 1) { + return false; + } + if (!aggregate.grouping_sets.empty() && aggregate.grouping_sets[0].size() != aggregate.groups.size()) { + return false; + } + column_binding_set_t bindings; + for (auto &group : aggregate.groups) { + if (group->GetExpressionClass() != ExpressionClass::BOUND_COLUMN_REF) { + return false; + } + auto &column = group->Cast(); + if (column.Depth() || !bindings.insert(column.Binding()).second) { + return false; + } + } + return true; +} + +static idx_t ExpressionSize(const ParsedExpression &expression) { + idx_t result = 1; + for (auto &child : expression.Children()) { + result += ExpressionSize(child); + } + return result; +} + +static bool IsLeaf(const ParsedExpression &expression) { + // Bare integers would become positional references in GROUP BY and ORDER BY. + return expression.GetExpressionClass() == ExpressionClass::COLUMN_REF || + (expression.GetExpressionClass() == ExpressionClass::CONSTANT && + expression.Cast().GetLiteral().IsNull()); +} + +static optional_ptr CompositionScope(QueryNode &query) { + if (query.type != QueryNodeType::SELECT_NODE || !query.cte_map.map.empty()) { + return nullptr; + } + auto &select = query.Cast(); + if (!select.from_table || select.sample || select.from_table->sample || select.having || select.qualify || + select.aggregate_handling != AggregateHandling::STANDARD_HANDLING) { + return nullptr; + } + return select; +} + +static void MoveScope(SelectNode &target, SelectNode &source) { + target.select_list = std::move(source.select_list); + target.from_table = std::move(source.from_table); + target.where_clause = std::move(source.where_clause); + target.groups = std::move(source.groups); + target.having = std::move(source.having); + target.qualify = std::move(source.qualify); + target.sample = std::move(source.sample); + target.modifiers = std::move(source.modifiers); + target.aggregate_handling = source.aggregate_handling; +} + +static optional_ptr SemanticInput(optional_ptr expression) { + while (auto wrapped = CMUtils::GetWrappedInput(*expression)) { + expression = wrapped; + } + return expression; +} + +struct CompositionProperties { + bool safe = false; + bool aggregate = false; + bool volatile_expression = true; +}; + +static CompositionProperties OutputProperties(LogicalOperator &op, const ColumnBinding &binding); + +static bool SafeExpression(const Expression &expression, LogicalOperator &input) { + auto &semantic = *SemanticInput(expression); + if (semantic.GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF) { + auto &column = semantic.Cast(); + return !column.Depth() && OutputProperties(input, column.Binding()).safe; + } + if (semantic.IsVolatile() || semantic.CanThrow() || + semantic.GetExpressionClass() == ExpressionClass::BOUND_LAMBDA || + semantic.GetExpressionClass() == ExpressionClass::BOUND_WINDOW || + semantic.GetExpressionClass() == ExpressionClass::BOUND_UNNEST) { + return false; + } + bool safe = true; + ExpressionIterator::EnumerateChildren( + semantic, [&](const Expression &child) { safe = safe && SafeExpression(child, input); }); + return safe; +} + +static CompositionProperties OutputProperties(LogicalOperator &op, const ColumnBinding &binding) { + auto bindings = op.GetColumnBindings(); + for (idx_t i = 0; i < bindings.size(); i++) { + if (bindings[i] != binding) { + continue; + } + if (op.type == LogicalOperatorType::LOGICAL_PROJECTION) { + auto &expression = *SemanticInput(*op.expressions[i]); + if (expression.GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF) { + return OutputProperties(*op.children[0], expression.Cast().Binding()); + } + return {SafeExpression(expression, *op.children[0]), false, expression.IsVolatile()}; + } + if (op.type == LogicalOperatorType::LOGICAL_AGGREGATE_AND_GROUP_BY) { + auto &aggregate = op.Cast(); + if (i < aggregate.groups.size()) { + return {SafeExpression(*aggregate.groups[i], *op.children[0]), false, + aggregate.groups[i]->IsVolatile()}; + } + i -= aggregate.groups.size(); + if (i < aggregate.expressions.size()) { + return {SafeExpression(*aggregate.expressions[i], *op.children[0]), true, + aggregate.expressions[i]->IsVolatile()}; + } + return {}; + } + if (op.type == LogicalOperatorType::LOGICAL_GET || op.type == LogicalOperatorType::LOGICAL_DUMMY_SCAN) { + return {true, false, false}; + } + if (op.children.size() == 1 && + (op.type == LogicalOperatorType::LOGICAL_FILTER || op.type == LogicalOperatorType::LOGICAL_ORDER_BY)) { + return OutputProperties(*op.children[0], binding); + } + return {}; + } + return {}; +} + +static bool HasCompositionBarrier(const Expression &expression) { + if (expression.GetExpressionType() == ExpressionType::OPERATOR_TRY || + expression.GetExpressionType() == ExpressionType::OPERATOR_COALESCE || + expression.GetExpressionClass() == ExpressionClass::BOUND_CASE || + expression.GetExpressionClass() == ExpressionClass::BOUND_LAMBDA || + expression.GetExpressionClass() == ExpressionClass::BOUND_WINDOW || + expression.GetExpressionClass() == ExpressionClass::BOUND_UNNEST) { + return true; + } + bool barrier = false; + ExpressionIterator::EnumerateChildren( + expression, [&](const Expression &child) { barrier = barrier || HasCompositionBarrier(child); }); + return barrier; +} + +static bool HasStageBarrier(LogicalOperator &op) { + if (op.type == LogicalOperatorType::LOGICAL_PROJECTION || op.type == LogicalOperatorType::LOGICAL_ORDER_BY) { + for (auto &expression : op.expressions) { + if (HasCompositionBarrier(*expression)) { + return true; + } + } + return HasStageBarrier(*op.children[0]); + } + return op.type != LogicalOperatorType::LOGICAL_GET && op.type != LogicalOperatorType::LOGICAL_FILTER && + op.type != LogicalOperatorType::LOGICAL_AGGREGATE_AND_GROUP_BY; +} + +static bool HasAggregateStage(LogicalOperator &op) { + if (op.type == LogicalOperatorType::LOGICAL_PROJECTION) { + return HasAggregateStage(*op.children[0]); + } + return op.type == LogicalOperatorType::LOGICAL_AGGREGATE_AND_GROUP_BY; +} + +static void AddSubstitutions(BoundExpressionSQLExportState &state, const LogicalPlanSQLExportedChild &child, + const SelectNode &source) { + for (idx_t i = 0; i < child.relation.fields.size(); i++) { + state.substitutions.emplace(child.relation.fields[i].source_binding, *source.select_list[i]); + } +} + +optional_ptr LogicalPlanSQLExportHelpers::ComposeInput(LogicalOperator &op, + const LogicalPlanSQLExportedChild &child, + BoundExpressionSQLExportState &state) { + auto source = CompositionScope(*child.relation.query); + if (!source || !source->modifiers.empty() || !source->groups.group_expressions.empty() || + !source->groups.grouping_sets.empty() || op.children[0]->type != LogicalOperatorType::LOGICAL_PROJECTION || + HasStageBarrier(*op.children[0]) || HasAggregateStage(*op.children[0])) { + return nullptr; + } + column_binding_map_t uses; + for (auto &expression : CollectExpressions(op)) { + if (HasCompositionBarrier(expression)) { + return nullptr; + } + ExpressionIterator::VisitExpression( + expression, [&](const BoundColumnRefExpression &ref) { uses[ref.Binding()]++; }); + } + if (op.type == LogicalOperatorType::LOGICAL_AGGREGATE_AND_GROUP_BY) { + // Group keys occur in both the SELECT list and GROUP BY. + for (auto &group : op.Cast().groups) { + ExpressionIterator::VisitExpression( + *group, [&](const BoundColumnRefExpression &ref) { uses[ref.Binding()]++; }); + } + } + if (op.type == LogicalOperatorType::LOGICAL_FILTER) { + // Combining filters must not expose either predicate to additional rows. + if (source->where_clause) { + return nullptr; + } + for (auto &binding : op.GetColumnBindings()) { + uses[binding]++; + } + } + for (idx_t i = 0; i < child.relation.fields.size(); i++) { + if (IsLeaf(*source->select_list[i])) { + continue; + } + auto binding = child.relation.fields[i].source_binding; + auto properties = OutputProperties(*op.children[0], binding); + if (!properties.safe || properties.aggregate || uses[binding] > 1) { + return nullptr; + } + } + AddSubstitutions(state, child, *source); + return *source; +} + +bool LogicalPlanSQLExportHelpers::ComposeProjection(LogicalProjection &projection, LogicalPlanSQLExportedChild &child, + SelectNode &select, const BoundExpressionSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + auto source = CompositionScope(*child.relation.query); + if (!source) { + return false; + } + bool identity = select.select_list.size() == child.relation.fields.size(); + for (idx_t i = 0; identity && i < select.select_list.size(); i++) { + identity = select.select_list[i]->Equals(*ChildColumn(child, i)); + } + if (identity) { + MoveScope(select, *source); + return true; + } + if (!source->modifiers.empty() || HasStageBarrier(*projection.children[0])) { + return false; + } + column_binding_map_t uses; + column_binding_set_t forwarded; + const bool parent_effects = HasEffectfulExpressions(projection); + for (auto &expression : projection.expressions) { + if (HasCompositionBarrier(*expression)) { + return false; + } + auto &semantic = *SemanticInput(*expression); + if (semantic.GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF) { + forwarded.insert(semantic.Cast().Binding()); + } + ExpressionIterator::VisitExpression( + semantic, [&](const BoundColumnRefExpression &ref) { uses[ref.Binding()]++; }); + } + idx_t old_size = 0; + bool retains_aggregate = !source->groups.group_expressions.empty() || !source->groups.grouping_sets.empty(); + for (idx_t i = 0; i < child.relation.fields.size(); i++) { + auto binding = child.relation.fields[i].source_binding; + auto properties = OutputProperties(*projection.children[0], binding); + auto count = uses[binding]; + if (!IsLeaf(*source->select_list[i])) { + // Aggregate implementations can throw independently of their arguments. + if (!count) { + return false; + } + if (properties.volatile_expression || (!properties.safe && (count != 1 || !forwarded.count(binding)))) { + return false; + } + if ((!properties.safe && !properties.aggregate && parent_effects) || (count > 1 && !properties.aggregate)) { + return false; + } + } + retains_aggregate = retains_aggregate || (properties.aggregate && count); + old_size += ExpressionSize(*source->select_list[i]); + } + // A global aggregate must still produce one row for empty input. + if (HasAggregateStage(*projection.children[0]) && !retains_aggregate) { + return false; + } + vector> composed; + idx_t new_size = 0; + { + BoundExpressionSQLExportState state(context); + AddSubstitutions(state, child, *source); + for (idx_t i = 0; i < projection.expressions.size(); i++) { + old_size += ExpressionSize(*select.select_list[i]); + auto expression = state.Export(*projection.expressions[i], PlanExpressionPath(path, i)); + if (expression.HasError()) { + return false; + } + new_size += ExpressionSize(*expression.GetValue()); + expression.GetValue()->SetAlias(FieldIdentifier(i)); + composed.push_back(std::move(expression.GetValue())); + } + } + if (new_size > old_size) { + return false; + } + MoveScope(select, *source); + select.select_list = std::move(composed); + return true; +} + +bool LogicalPlanSQLExportHelpers::ComposeOrder(LogicalOperator &op, LogicalPlanSQLExportedChild &child, + SelectNode &select, const BoundExpressionSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + auto source = CompositionScope(*child.relation.query); + if (!source || !source->modifiers.empty() || HasStageBarrier(*op.children[0]) || + select.select_list.size() != source->select_list.size() || + (op.type == LogicalOperatorType::LOGICAL_TOP_N && HasAggregateStage(*op.children[0]))) { + return false; + } + for (idx_t i = 0; i < child.relation.fields.size(); i++) { + if (!select.select_list[i]->Equals(*ChildColumn(child, i))) { + return false; + } + if (op.type == LogicalOperatorType::LOGICAL_TOP_N && + !OutputProperties(*op.children[0], child.relation.fields[i].source_binding).safe) { + return false; + } + } + BoundExpressionSQLExportState state(context); + AddSubstitutions(state, child, *source); + auto expressions = CollectExpressions(op); + auto &modifier = select.modifiers[0]->Cast(); + if (modifier.orders.size() != expressions.size()) { + return false; + } + vector> orders; + for (idx_t i = 0; i < expressions.size(); i++) { + auto &expression = *SemanticInput(expressions[i].get()); + if (expression.GetExpressionClass() != ExpressionClass::BOUND_COLUMN_REF) { + return false; + } + auto &column = expression.Cast(); + auto replacement = state.substitutions.find(column.Binding()); + // Literal order keys are positional or require order_by_non_integer_literal. + if (replacement == state.substitutions.end() || + replacement->second.get().GetExpressionClass() != ExpressionClass::COLUMN_REF || + !OutputProperties(*op.children[0], column.Binding()).safe) { + return false; + } + auto exported = state.Export(expressions[i], PlanExpressionPath(path, i)); + if (exported.HasError()) { + return false; + } + auto resolved = context.resolve_binding(column.Binding()); + if (!resolved) { + return false; + } + orders.push_back(SQLExportHelpers::OrderExpression(resolved->type, std::move(exported.GetValue()))); + } + for (idx_t i = 0; i < orders.size(); i++) { + modifier.orders[i].expression = std::move(orders[i]); + } + auto modifiers = std::move(select.modifiers); + MoveScope(select, *source); + select.modifiers = std::move(modifiers); + return true; +} + +} // namespace duckdb diff --git a/src/duckdb/src/planner/sql_export/sql_export_constants.cpp b/src/duckdb/src/planner/sql_export/sql_export_constants.cpp new file mode 100644 index 000000000..e098cfee3 --- /dev/null +++ b/src/duckdb/src/planner/sql_export/sql_export_constants.cpp @@ -0,0 +1,71 @@ +#include "duckdb/planner/sql_export/bound_expression_sql_exporter_internal.hpp" +#include "duckdb/common/error_data.hpp" +#include "duckdb/parser/expression/cast_expression.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/parser/expression/function_expression.hpp" +#include "duckdb/planner/expression/bound_constant_expression.hpp" +#include "duckdb/planner/sql_export_helpers.hpp" + +namespace duckdb { + +bool BoundExpressionSQLExportState::RequiresConstantConstructor(const LogicalType &type) { + return ConstantExpression::RequiresTypeWitness(type); +} + +BoundExpressionSQLExportResult +BoundExpressionSQLExportState::CastToConstructedType(const LogicalType &type, unique_ptr child, + const LogicalPlanVerificationPath &path) { + vector> arguments; + arguments.push_back(std::move(child)); + try { + arguments.push_back(ConstantExpression::FromValue(Value(type))); + } catch (const NotImplementedException &ex) { + return BoundExpressionSQLExportResult::Failure( + {BoundExpressionSQLExportState::UnsupportedFeature(path, "constant_type", ErrorData(ex).RawMessage())}); + } + return BoundExpressionSQLExportResult::Success( + SQLExportHelpers::SystemFunction("cast_to_type", std::move(arguments))); +} + +BoundExpressionSQLExportResult +BoundExpressionSQLExportState::RestoreResultType(const LogicalType &type, unique_ptr result, + const LogicalPlanVerificationPath &path) { + if (RequiresConstantConstructor(type)) { + return CastToConstructedType(type, std::move(result), path); + } + return BoundExpressionSQLExportResult::Success(BoundExpressionSQLExportState::SQLCast(type, std::move(result))); +} + +BoundExpressionSQLExportResult BoundExpressionSQLExportState::ExportConstant(const BoundConstantExpression &expression, + const LogicalPlanVerificationPath &path) { + D_ASSERT(expression.GetExpressionType() == ExpressionType::VALUE_CONSTANT); + auto &return_type = expression.GetReturnType(); + auto &value = expression.GetValue(); + D_ASSERT(return_type == value.type()); + if (!SQLExportHelpers::IsSQLValueType(return_type)) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::InternalExpressionInvariant( + path, expression, "Bound constant has an unexportable type")}); + } + unique_ptr result; + try { + result = ConstantExpression::FromValue(value); + } catch (const NotImplementedException &ex) { + return BoundExpressionSQLExportResult::Failure( + {BoundExpressionSQLExportState::UnsupportedFeature(path, "constant_value", ErrorData(ex).RawMessage())}); + } + if (RequiresConstantConstructor(return_type) || !SQLExportHelpers::IsSQLRepresentableType(return_type)) { + return BoundExpressionSQLExportResult::Success(std::move(result)); + } + if (return_type.id() != LogicalTypeId::SQLNULL) { + const bool has_result_cast = + result->GetExpressionClass() == ExpressionClass::CAST && + result->Cast().TargetType().Equals( + *TypeExpression::FromLogicalType(BoundExpressionSQLExportState::SQLCastType(return_type))); + if (!has_result_cast) { + result = BoundExpressionSQLExportState::SQLCast(return_type, std::move(result)); + } + } + return BoundExpressionSQLExportResult::Success(std::move(result)); +} + +} // namespace duckdb diff --git a/src/duckdb/src/planner/sql_export/sql_export_cte.cpp b/src/duckdb/src/planner/sql_export/sql_export_cte.cpp new file mode 100644 index 000000000..8d2596e61 --- /dev/null +++ b/src/duckdb/src/planner/sql_export/sql_export_cte.cpp @@ -0,0 +1,328 @@ +#include "duckdb/planner/sql_export/logical_plan_sql_exporter_internal.hpp" +#include "duckdb/planner/logical_plan_sql_exporter.hpp" +#include "duckdb/main/settings.hpp" +#include "duckdb/optimizer/optimizer.hpp" +#include "duckdb/parser/query_node/recursive_cte_node.hpp" +#include "duckdb/planner/operator/logical_recursive_cte.hpp" +#include "duckdb/parser/expression/columnref_expression.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/parser/common_table_expression_info.hpp" +#include "duckdb/parser/tableref/basetableref.hpp" +#include "duckdb/parser/query_node/select_node.hpp" +#include "duckdb/parser/query_node/set_operation_node.hpp" +#include "duckdb/planner/bound_expression_sql_exporter.hpp" +#include "duckdb/planner/expression/bound_aggregate_expression.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/logical_operator.hpp" +#include "duckdb/planner/operator/logical_materialized_cte.hpp" +#include "duckdb/planner/operator/logical_cteref.hpp" +#include "duckdb/planner/operator/logical_projection.hpp" + +namespace duckdb { + +LogicalPlanSQLExportResult LogicalMaterializedCTE::ToSQL(LogicalPlanSQLExportContext &export_context, + const LogicalPlanVerificationPath &path) { + auto &cte = *this; + D_ASSERT(cte.children.size() == 2); + auto fields = LogicalPlanSQLExportHelpers::CreateFields(cte, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + auto name = export_context.NextRelationAlias(cte.ctename); + auto producer = + export_context.ExportNamedProducer(*cte.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0), name); + if (producer.HasError()) { + return LogicalPlanSQLExportResult::Failure(producer); + } + export_context.PushNamedRelation(cte.table_index, name, false); + auto consumer_path = LogicalPlanSQLExportHelpers::PlanChildPath(path, 1); + // A data-modifying consumer exports as the statement itself rather than a relation to select from. + auto consumer = [&]() -> LogicalPlanVerificationResult { + if (!SQLExportDMLHelpers::MatchDMLBody(*cte.children[1]) || + SQLExportDMLHelpers::ContainsReplacedBoundary(export_context, *cte.children[1])) { + return export_context.ExportChild(*cte.children[1], consumer_path); + } + auto body = SQLExportDMLHelpers::ExportBody(export_context, *cte.children[1], consumer_path); + if (body.HasError()) { + return LogicalPlanVerificationResult::Failure(body); + } + return LogicalPlanVerificationResult::Success( + {std::move(body.GetValue()), Identifier()}); + }(); + auto references = export_context.PopNamedRelation(); + if (consumer.HasError()) { + return LogicalPlanSQLExportResult::Failure(consumer); + } + if (references == 0) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "cte_unreferenced_evaluation", "SQL would discard the unreferenced CTE producer")}); + } + auto info = make_uniq(); + for (idx_t i = 0; i < producer.GetValue().relation.fields.size(); i++) { + info->aliases.push_back(LogicalPlanSQLExportHelpers::FieldIdentifier(i)); + } + if (producer.GetValue().relation.query->type == QueryNodeType::RECURSIVE_CTE_NODE) { + for (auto &key : producer.GetValue().relation.query->Cast().key_targets) { + info->key_targets.push_back(key->Copy()); + } + } + info->query_node = std::move(producer.GetValue().relation.query); + info->materialized = CTEMaterialize::CTE_MATERIALIZE_ALWAYS; + if (SQLExportDMLHelpers::IsDMLNode(*consumer.GetValue().relation.query)) { + // a data-modifying statement cannot be wrapped in a SELECT: it carries this CTE itself, + // in front of the definitions its export already collected + auto relation = std::move(consumer.GetValue().relation); + if (relation.query->type == QueryNodeType::COPY_QUERY_NODE || + relation.query->type == QueryNodeType::STATEMENT_NODE) { + // the generated text of these nodes cannot carry a WITH clause + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "cte_statement_consumer", "The statement cannot carry the CTE definitions it consumes")}); + } + if (relation.fields.size() != fields.GetValue().size()) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "cte_dml_consumer", "The data-modifying CTE consumer does not forward the CTE's columns")}); + } + for (idx_t i = 0; i < relation.fields.size(); i++) { + if (relation.fields[i].source_binding != fields.GetValue()[i].source_binding) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "cte_dml_consumer", "The data-modifying CTE consumer does not forward the CTE's columns")}); + } + } + auto collected = std::move(relation.query->cte_map); + relation.query->cte_map = CommonTableExpressionMap(); + relation.query->cte_map.map.insert(name, std::move(info)); + for (auto &entry : collected.map) { + relation.query->cte_map.map.insert(entry.first, std::move(entry.second)); + } + return LogicalPlanSQLExportResult::Success(std::move(relation)); + } + auto select = export_context.ForwardFields(consumer.GetValue(), fields.GetValue()); + select->cte_map.map.insert(name, std::move(info)); + // A data-modifying CTE definition must stay in the top-level WITH clause. If the consumer + // collected one, hoist its entire WITH clause above the subquery boundary, behind this CTE's + // own definition: a definition may reference those before it, so they move together. + auto &consumer_query = *consumer.GetValue().relation.query; + bool hoist = false; + for (auto &entry : consumer_query.cte_map.map) { + hoist |= SQLExportDMLHelpers::IsDMLNode(*entry.second->query_node); + } + if (hoist) { + auto collected = std::move(consumer_query.cte_map); + consumer_query.cte_map = CommonTableExpressionMap(); + for (auto &entry : collected.map) { + select->cte_map.map.insert(entry.first, std::move(entry.second)); + } + } + select->from_table = LogicalPlanSQLExportHelpers::CreateSubquery(std::move(consumer.GetValue())); + return LogicalPlanSQLExportResult::Success({std::move(select), std::move(fields.GetValue())}); +} + +LogicalPlanSQLExportResult LogicalCTERef::ToSQL(LogicalPlanSQLExportContext &export_context, + const LogicalPlanVerificationPath &path) { + auto &ref = *this; + D_ASSERT(ref.children.empty()); + auto name = export_context.ReferenceNamedRelation(ref.cte_index, ref.is_recurring); + if (!name) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "cte_reference_scope", "The referenced CTE is not in the exported relation scope")}); + } + auto fields = LogicalPlanSQLExportHelpers::CreateFields(ref, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + return LogicalPlanSQLExportResult::Success( + {export_context.CreateNamedSource(*name, fields.GetValue(), ref.is_recurring), std::move(fields.GetValue())}); +} + +unique_ptr LogicalPlanSQLExportContext::CreateNamedSource(const Identifier &name, + const vector &fields, + bool recurring) { + auto table = make_uniq(); + table->SetTable(name); + if (recurring) { + table->SetQualifiedName(Identifier(), Identifier("recurring"), name); + } + table->alias = NextRelationAlias(); + auto select = make_uniq(); + for (idx_t i = 0; i < fields.size(); i++) { + table->column_name_alias.push_back(LogicalPlanSQLExportHelpers::FieldIdentifier(i)); + select->select_list.push_back( + make_uniq(LogicalPlanSQLExportHelpers::FieldIdentifier(i), table->alias)); + } + select->from_table = std::move(table); + return select; +} + +LogicalPlanSQLExportResult LogicalRecursiveCTE::ExportSQLDefinition(LogicalPlanSQLExportContext &export_context, + const LogicalPlanVerificationPath &path, + const Identifier &name) { + auto &cte = *this; + D_ASSERT(cte.children.size() == 2); + auto fields = LogicalPlanSQLExportHelpers::CreateFields(cte, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + auto seed = export_context.Export(*cte.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (seed.HasError()) { + return LogicalPlanSQLExportResult::Failure(seed); + } + export_context.PushNamedRelation(cte.table_index, name, false); + export_context.PushNamedRelation(cte.table_index, name, true); + auto step = export_context.Export(*cte.children[1], LogicalPlanSQLExportHelpers::PlanChildPath(path, 1)); + auto recurring_references = export_context.PopNamedRelation(); + auto references = export_context.PopNamedRelation(); + if (step.HasError()) { + return LogicalPlanSQLExportResult::Failure(step); + } + if ((references == 0 && recurring_references == 0) || (cte.ref_recurring && recurring_references == 0)) { + // Binding still needs a self reference when optimization removed the recursive scan. + auto empty = export_context.CreateNamedSource(name, fields.GetValue(), cte.ref_recurring); + empty->select_list.clear(); + for (auto &type : cte.internal_types) { + auto value = LogicalPlanSQLExportHelpers::ExportTypedNull(type, path); + if (value.HasError()) { + return LogicalPlanSQLExportResult::Failure(value); + } + empty->select_list.push_back(std::move(value.GetValue())); + } + empty->where_clause = ConstantExpression::FromValue(Value::BOOLEAN(false)); + auto recursive_step = make_uniq(); + recursive_step->setop_type = SetOperationType::UNION; + recursive_step->setop_all = true; + recursive_step->children.push_back(std::move(step.GetValue().query)); + recursive_step->children.push_back(std::move(empty)); + step.GetValue().query = std::move(recursive_step); + } + auto query = make_uniq(); + query->ctename = name; + query->union_all = cte.union_all; + for (idx_t i = 0; i < fields.GetValue().size(); i++) { + query->aliases.push_back(LogicalPlanSQLExportHelpers::FieldIdentifier(i)); + } + BoundExpressionSQLExportContext key_context; + key_context.client_context = &export_context.GetClientContext(); + key_context.resolve_binding = [&](const ColumnBinding &binding) -> optional { + if (binding.table_index != cte.table_index || binding.column_index.GetIndex() >= cte.internal_types.size()) { + return {}; + } + return ResolvedSQLColumnReference { + {LogicalPlanSQLExportHelpers::FieldIdentifier(binding.column_index.GetIndex())}, + cte.internal_types[binding.column_index.GetIndex()]}; + }; + auto expressions = LogicalPlanSQLExportHelpers::CollectExpressions(cte); + idx_t expression_ordinal = 0; + unordered_set key_columns; + for (auto &key : cte.key_targets) { + auto exported = export_context.ExportExpression(cte, expressions, expression_ordinal++, key_context, path); + if (exported.HasError()) { + return LogicalPlanSQLExportResult::Failure(exported); + } + key_columns.insert(key->Cast().Binding().column_index); + query->key_targets.push_back(std::move(exported.GetValue())); + } + if (!cte.key_targets.empty()) { + for (idx_t column = 0; column < cte.internal_types.size(); column++) { + if (key_columns.count(ProjectionIndex(column))) { + continue; + } + auto ordinal = expression_ordinal++; + auto &aggregate = expressions[ordinal].get().Cast(); + const bool has_order = aggregate.GetOrderBys() && !aggregate.GetOrderBys()->orders.empty(); + const bool has_modifiers = aggregate.IsDistinct() || aggregate.GetFilter() || has_order; + if (has_modifiers || aggregate.StateExportMode() != AggregateStateExportMode::NONE) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + LogicalPlanSQLExportHelpers::PlanExpressionPath(path, ordinal), "recursive_payload_modifiers", + "The recursive payload clause cannot preserve these aggregate modifiers")}); + } + auto exported = BoundExpressionSQLExporter::ExportAggregateCallAtPath( + aggregate, key_context, LogicalPlanSQLExportHelpers::PlanExpressionPath(path, ordinal)); + if (exported.HasError()) { + return LogicalPlanSQLExportResult::Failure(exported); + } + exported.GetValue()->SetAlias(LogicalPlanSQLExportHelpers::FieldIdentifier(column)); + query->key_targets.push_back(std::move(exported.GetValue())); + } + } + query->left = std::move(seed.GetValue().query); + query->right = std::move(step.GetValue().query); + return LogicalPlanSQLExportResult::Success({std::move(query), std::move(fields.GetValue())}); +} + +LogicalPlanVerificationResult +LogicalPlanSQLExportContext::ExportNamedProducer(LogicalOperator &op, const LogicalPlanVerificationPath &path, + const Identifier &name) { + if (FindSource(op)) { + return ExportChild(op, path); + } + // A data-modifying body exports as the statement itself; parsed DML nodes are legal CTE bodies. + // This runs before the identity-projection reduction so a RETURNING projection stays intact. + if (SQLExportDMLHelpers::MatchDMLBody(op) && !SQLExportDMLHelpers::ContainsReplacedBoundary(*this, op)) { + auto exported = SQLExportDMLHelpers::ExportBody(*this, op, path); + if (exported.HasError()) { + return LogicalPlanVerificationResult::Failure(exported); + } + if (exported.GetValue().query->type == QueryNodeType::MERGE_QUERY_NODE) { + // ON CONFLICT inserts bind to MERGE INTO, which SQL does not accept as a CTE body + return LogicalPlanVerificationResult::Failure( + {LogicalPlanSQLExportHelpers::PlanUnsupportedFeature(path, "cte_merge_body", + "MERGE INTO cannot be exported as a CTE body")}); + } + return LogicalPlanVerificationResult::Success( + {std::move(exported.GetValue()), NextRelationAlias()}); + } + if (op.type == LogicalOperatorType::LOGICAL_PROJECTION) { + D_ASSERT(op.children.size() == 1); + auto fields = LogicalPlanSQLExportHelpers::CreateFields(op, path); + auto child_path = LogicalPlanSQLExportHelpers::PlanChildPath(path, 0); + auto child_fields = LogicalPlanSQLExportHelpers::CreateFields(*op.children[0], child_path); + if (fields.IsSuccess() && child_fields.IsSuccess() && + LogicalPlanSQLExportHelpers::IsIdentityProjection(op.Cast(), child_fields.GetValue())) { + ancestors.push_back(op); + auto exported = ExportNamedProducer(*op.children[0], child_path, name); + ancestors.pop_back(); + if (exported.IsSuccess()) { + exported.GetValue().relation.fields = std::move(fields.GetValue()); + } + return exported; + } + } + if (op.type != LogicalOperatorType::LOGICAL_RECURSIVE_CTE) { + return ExportChild(op, path); + } + ancestors.push_back(op); + auto exported = op.Cast().ExportSQLDefinition(*this, path, name); + ancestors.pop_back(); + if (exported.HasError()) { + return LogicalPlanVerificationResult::Failure(exported); + } + return LogicalPlanVerificationResult::Success( + {std::move(exported.GetValue()), NextRelationAlias()}); +} + +LogicalPlanSQLExportResult LogicalRecursiveCTE::ToSQL(LogicalPlanSQLExportContext &export_context, + const LogicalPlanVerificationPath &path) { + auto &cte = *this; + if (Optimizer::OptimizerDisabled(export_context.GetClientContext(), OptimizerType::CTE_INLINING) || + Settings::Get(export_context.GetClientContext())) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "recursive_cte_materialization", + "The SQL wrapper requires CTE inlining to preserve recursive evaluation")}); + } + auto name = export_context.NextRelationAlias(cte.ctename); + auto recursive = ExportSQLDefinition(export_context, path, name); + if (recursive.HasError()) { + return recursive; + } + auto info = make_uniq(); + info->aliases = recursive.GetValue().query->Cast().aliases; + for (auto &key : recursive.GetValue().query->Cast().key_targets) { + info->key_targets.push_back(key->Copy()); + } + info->query_node = std::move(recursive.GetValue().query); + info->materialized = CTEMaterialize::CTE_MATERIALIZE_NEVER; + auto select = export_context.CreateNamedSource(name, recursive.GetValue().fields); + select->cte_map.map.insert(name, std::move(info)); + return LogicalPlanSQLExportResult::Success({std::move(select), std::move(recursive.GetValue().fields)}); +} + +} // namespace duckdb diff --git a/src/duckdb/src/planner/sql_export/sql_export_dml.cpp b/src/duckdb/src/planner/sql_export/sql_export_dml.cpp new file mode 100644 index 000000000..6b3c2c07a --- /dev/null +++ b/src/duckdb/src/planner/sql_export/sql_export_dml.cpp @@ -0,0 +1,666 @@ +#include "duckdb/catalog/catalog_entry/schema_catalog_entry.hpp" +#include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp" +#include "duckdb/parser/expression/columnref_expression.hpp" +#include "duckdb/parser/expression/comparison_expression.hpp" +#include "duckdb/parser/expression/subquery_expression.hpp" +#include "duckdb/parser/query_node/delete_query_node.hpp" +#include "duckdb/parser/query_node/insert_query_node.hpp" +#include "duckdb/parser/query_node/merge_query_node.hpp" +#include "duckdb/parser/query_node/update_query_node.hpp" +#include "duckdb/parser/statement/insert_statement.hpp" +#include "duckdb/parser/statement/merge_into_statement.hpp" +#include "duckdb/parser/statement/select_statement.hpp" +#include "duckdb/parser/tableref/basetableref.hpp" +#include "duckdb/planner/column_binding_map.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/expression_iterator.hpp" +#include "duckdb/planner/operator/logical_delete.hpp" +#include "duckdb/planner/operator/logical_insert.hpp" +#include "duckdb/planner/operator/logical_copy_to_file.hpp" +#include "duckdb/planner/operator/logical_merge_into.hpp" +#include "duckdb/planner/operator/logical_projection.hpp" +#include "duckdb/planner/operator/logical_update.hpp" +#include "duckdb/planner/sql_export/logical_plan_sql_exporter_internal.hpp" + +namespace duckdb { + +static LogicalPlanVerificationIssue UnsupportedDML(const LogicalPlanVerificationPath &path, string feature, + string message) { + return LogicalPlanSQLExportHelpers::PlanUnsupportedFeature(path, std::move(feature), std::move(message)); +} + +//! The single row-id column SQL can reference on the modified table; multi-column row ids have no SQL name, +//! and a physical column with the row-id name would capture the generated references. +static LogicalPlanVerificationResult ExportedRowIdName(const TableCatalogEntry &table, + const LogicalPlanVerificationPath &path) { + auto row_id_columns = table.GetRowIdColumns(); + auto virtual_columns = table.GetVirtualColumns(); + auto entry = row_id_columns.size() == 1 ? virtual_columns.find(row_id_columns[0]) : virtual_columns.end(); + if (entry == virtual_columns.end() || table.ColumnExists(entry->second.name)) { + return LogicalPlanVerificationResult::Failure( + {UnsupportedDML(path, "dml_row_id", "The modified table does not expose a single named row-id column")}); + } + return LogicalPlanVerificationResult::Success(Identifier(entry->second.name)); +} + +static unique_ptr ModifiedTableRef(const TableCatalogEntry &table) { + auto reference = make_uniq(); + reference->SetQualifiedName(table.schema.GetQualifiedName(table.name)); + return std::move(reference); +} + +static LogicalPlanSQLExportResult ExportInsertNode(LogicalInsert &op, LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + if (op.on_conflict_info.action_type != OnConflictAction::THROW || !op.insert_values.empty()) { + return LogicalPlanSQLExportResult::Failure( + {UnsupportedDML(path, "insert_on_conflict", "INSERT ... ON CONFLICT cannot be exported")}); + } + if (op.children.size() != 1) { + return LogicalPlanSQLExportResult::Failure( + {UnsupportedDML(path, "dml_source", "INSERT without a source relation cannot be exported")}); + } + auto fields = LogicalPlanSQLExportHelpers::CreateFields(op, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + auto child = context.Export(*op.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (child.HasError()) { + return child; + } + // The bound source produces every physical column in declaration order, with defaults filled in. + vector target_columns; + for (auto &column : op.table.GetColumns().Physical()) { + target_columns.push_back(column.Name()); + } + if (child.GetValue().fields.size() != target_columns.size()) { + return LogicalPlanSQLExportResult::Failure( + {UnsupportedDML(path, "insert_source", "The INSERT source does not cover the table's physical columns")}); + } + auto node = make_uniq(); + node->qualified_name = op.table.schema.GetQualifiedName(op.table.name); + node->columns = std::move(target_columns); + node->select_statement = make_uniq(); + node->select_statement->node = std::move(child.GetValue().query); + return LogicalPlanSQLExportResult::Success({std::move(node), std::move(fields.GetValue())}); +} + +static LogicalPlanSQLExportResult ExportDeleteNode(LogicalDelete &op, LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + if (op.children.size() != 1) { + return LogicalPlanSQLExportResult::Failure( + {UnsupportedDML(path, "dml_source", "DELETE without a source relation cannot be exported")}); + } + auto row_id_name = ExportedRowIdName(op.table, path); + if (row_id_name.HasError()) { + return LogicalPlanSQLExportResult::Failure(row_id_name); + } + auto fields = LogicalPlanSQLExportHelpers::CreateFields(op, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + // The row-id reference the operator deletes by carries the row-id column's name as its alias. + optional_ptr row_id_reference; + bool ambiguous = false; + for (auto &expression : op.expressions) { + if (expression->GetAlias() != row_id_name.GetValue()) { + continue; + } + ambiguous |= static_cast(row_id_reference); + if (expression->GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF) { + row_id_reference = &expression->Cast(); + } + } + if (!row_id_reference || ambiguous || row_id_reference->Depth() != 0) { + return LogicalPlanSQLExportResult::Failure( + {UnsupportedDML(path, "dml_row_id", "The deleted row-id column was not found in the plan")}); + } + auto child = context.ExportChild(*op.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (child.HasError()) { + return LogicalPlanSQLExportResult::Failure(child); + } + auto &child_fields = child.GetValue().relation.fields; + optional_idx row_id_field; + for (idx_t i = 0; i < child_fields.size(); i++) { + if (child_fields[i].source_binding == row_id_reference->Binding()) { + row_id_field = i; + break; + } + } + if (!row_id_field.IsValid()) { + return LogicalPlanSQLExportResult::Failure( + {UnsupportedDML(path, "dml_row_id", "The deleted row-id column was not found in the exported source")}); + } + auto plain = LogicalPlanSQLExportHelpers::PlainScope(*child.GetValue().relation.query); + auto select = make_uniq(); + select->select_list.push_back( + LogicalPlanSQLExportHelpers::ChildColumn(child.GetValue(), row_id_field.GetIndex(), plain)); + LogicalPlanSQLExportHelpers::SetChildScope(*select, std::move(child.GetValue()), plain); + auto subquery = make_uniq(); + subquery->SubqueryMutable() = make_uniq(); + subquery->SubqueryMutable()->node = std::move(select); + subquery->GetSubqueryTypeMutable() = SubqueryType::ANY; + subquery->GetComparisonTypeMutable() = ExpressionType::COMPARE_EQUAL; + subquery->GetChildMutable() = make_uniq(row_id_name.GetValue()); + auto node = make_uniq(); + node->table = ModifiedTableRef(op.table); + node->condition = std::move(subquery); + return LogicalPlanSQLExportResult::Success({std::move(node), std::move(fields.GetValue())}); +} + +static LogicalPlanSQLExportResult ExportUpdateNode(LogicalUpdate &op, LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + if (op.children.size() != 1) { + return LogicalPlanSQLExportResult::Failure( + {UnsupportedDML(path, "dml_source", "UPDATE without a source relation cannot be exported")}); + } + auto row_id_name = ExportedRowIdName(op.table, path); + if (row_id_name.HasError()) { + return LogicalPlanSQLExportResult::Failure(row_id_name); + } + auto fields = LogicalPlanSQLExportHelpers::CreateFields(op, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + // Keep the table's own name reserved so the generated source alias cannot capture it. + context.NextRelationAlias(op.table.name); + auto child = context.ExportChild(*op.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (child.HasError()) { + return LogicalPlanSQLExportResult::Failure(child); + } + auto &child_fields = child.GetValue().relation.fields; + if (op.expressions.size() != op.columns.size() || child_fields.size() <= op.columns.size()) { + return LogicalPlanSQLExportResult::Failure( + {UnsupportedDML(path, "dml_row_id", "The updated row-id column was not found in the exported source")}); + } + // The bound source keeps the row id as its final column. + auto row_id_field = child_fields.size() - 1; + auto expression_context = + LogicalPlanSQLExportHelpers::CreateBindingContext(context.GetClientContext(), {child.GetValue()}); + vector> set_expressions; + for (auto &expression : op.expressions) { + set_expressions.push_back(*expression); + } + auto set_info = make_uniq(); + for (idx_t i = 0; i < op.columns.size(); i++) { + auto exported = context.ExportExpression(op, set_expressions, i, expression_context, path); + if (exported.HasError()) { + return LogicalPlanSQLExportResult::Failure(exported); + } + set_info->columns.push_back(op.table.GetColumns().GetColumn(op.columns[i]).Name()); + set_info->expressions.push_back(std::move(exported.GetValue())); + } + set_info->condition = make_uniq( + ExpressionType::COMPARE_EQUAL, make_uniq(row_id_name.GetValue(), op.table.name), + LogicalPlanSQLExportHelpers::ChildColumn(child.GetValue(), row_id_field)); + auto node = make_uniq(); + node->table = ModifiedTableRef(op.table); + node->from_table = LogicalPlanSQLExportHelpers::CreateSubquery(std::move(child.GetValue())); + node->set_info = std::move(set_info); + return LogicalPlanSQLExportResult::Success({std::move(node), std::move(fields.GetValue())}); +} + +//! The merged rows are keyed back to the target by its row id: a MATCHED source row carries the +//! matched row id, a NOT MATCHED (by target) row carries NULL and never rejoins. A target row the +//! source never matched does not appear as unmatched under this key, so WHEN NOT MATCHED BY SOURCE +//! actions cannot be reconstructed. +static LogicalPlanSQLExportResult ExportMergeNode(LogicalMergeInto &op, LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + if (op.children.size() != 1) { + return LogicalPlanSQLExportResult::Failure( + {UnsupportedDML(path, "dml_source", "MERGE INTO without a source relation cannot be exported")}); + } + if (op.actions.count(MergeActionCondition::WHEN_NOT_MATCHED_BY_SOURCE)) { + return LogicalPlanSQLExportResult::Failure( + {UnsupportedDML(path, "merge_not_matched_by_source", + "WHEN NOT MATCHED BY SOURCE actions cannot be keyed by the exported row id")}); + } + auto row_id_name = ExportedRowIdName(op.table, path); + if (row_id_name.HasError()) { + return LogicalPlanSQLExportResult::Failure(row_id_name); + } + auto fields = LogicalPlanSQLExportHelpers::CreateFields(op, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + // Keep the table's own name reserved so the generated source alias cannot capture it. + context.NextRelationAlias(op.table.name); + auto child = context.ExportChild(*op.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (child.HasError()) { + return LogicalPlanSQLExportResult::Failure(child); + } + // The bound source keeps the row id as its final column, after every action input. + if (op.row_id_start + 1 != child.GetValue().relation.fields.size()) { + return LogicalPlanSQLExportResult::Failure( + {UnsupportedDML(path, "dml_row_id", "The merged row-id column was not found in the exported source")}); + } + auto expression_context = + LogicalPlanSQLExportHelpers::CreateBindingContext(context.GetClientContext(), {child.GetValue()}); + // The action inputs are exported in a stable order: per action its condition, then its expressions. + vector> action_inputs; + for (auto &entry : op.actions) { + for (auto &action : entry.second) { + if (action->condition) { + action_inputs.push_back(*action->condition); + } + for (auto &expression : action->expressions) { + action_inputs.push_back(*expression); + } + } + } + auto node = make_uniq(); + node->target = ModifiedTableRef(op.table); + node->join_condition = make_uniq( + ExpressionType::COMPARE_EQUAL, make_uniq(row_id_name.GetValue(), op.table.name), + LogicalPlanSQLExportHelpers::ChildColumn(child.GetValue(), op.row_id_start)); + idx_t input_ordinal = 0; + auto export_input = [&]() { + return context.ExportExpression(op, action_inputs, input_ordinal++, expression_context, path); + }; + for (auto &entry : op.actions) { + vector> actions; + for (auto &bound_action : entry.second) { + auto action = make_uniq(); + action->action_type = bound_action->action_type; + if (bound_action->condition) { + auto condition = export_input(); + if (condition.HasError()) { + return LogicalPlanSQLExportResult::Failure(condition); + } + action->condition = std::move(condition.GetValue()); + } + switch (bound_action->action_type) { + case MergeActionType::MERGE_UPDATE: { + if (bound_action->columns.size() != bound_action->expressions.size()) { + return LogicalPlanSQLExportResult::Failure({UnsupportedDML( + path, "merge_update", "The MERGE update action does not pair its columns and expressions")}); + } + auto set_info = make_uniq(); + for (idx_t i = 0; i < bound_action->columns.size(); i++) { + auto value = export_input(); + if (value.HasError()) { + return LogicalPlanSQLExportResult::Failure(value); + } + set_info->columns.push_back(op.table.GetColumns().GetColumn(bound_action->columns[i]).Name()); + set_info->expressions.push_back(std::move(value.GetValue())); + } + action->update_info = std::move(set_info); + break; + } + case MergeActionType::MERGE_INSERT: { + // the bound action provides every physical column in declaration order, with defaults filled in + for (auto &column : op.table.GetColumns().Physical()) { + action->insert_columns.push_back(column.Name()); + } + if (bound_action->expressions.size() != action->insert_columns.size()) { + return LogicalPlanSQLExportResult::Failure({UnsupportedDML( + path, "merge_insert", "The MERGE insert action does not cover the table's physical columns")}); + } + for (idx_t i = 0; i < bound_action->expressions.size(); i++) { + auto value = export_input(); + if (value.HasError()) { + return LogicalPlanSQLExportResult::Failure(value); + } + action->expressions.push_back(std::move(value.GetValue())); + } + break; + } + case MergeActionType::MERGE_ERROR: { + for (idx_t i = 0; i < bound_action->expressions.size(); i++) { + auto message = export_input(); + if (message.HasError()) { + return LogicalPlanSQLExportResult::Failure(message); + } + action->expressions.push_back(std::move(message.GetValue())); + } + break; + } + default: + // DELETE and DO NOTHING carry no inputs + break; + } + actions.push_back(std::move(action)); + } + node->actions.emplace(entry.first, std::move(actions)); + } + node->source = LogicalPlanSQLExportHelpers::CreateSubquery(std::move(child.GetValue())); + return LogicalPlanSQLExportResult::Success({std::move(node), std::move(fields.GetValue())}); +} + +static optional DMLStatementIssue(const LogicalOperator &op, + const LogicalPlanVerificationPath &path) { + if (op.type == LogicalOperatorType::LOGICAL_UPDATE && op.Cast().capture_old_rows) { + return UnsupportedDML(path, "dml_old_rows", "UPDATE with OLD row capture cannot be exported"); + } + // A bare operator never returns modified rows; that shape only exists under a RETURNING projection. + bool return_chunk; + switch (op.type) { + case LogicalOperatorType::LOGICAL_INSERT: + return_chunk = op.Cast().return_chunk; + break; + case LogicalOperatorType::LOGICAL_UPDATE: + return_chunk = op.Cast().return_chunk; + break; + case LogicalOperatorType::LOGICAL_MERGE_INTO: + return_chunk = op.Cast().return_chunk; + break; + case LogicalOperatorType::LOGICAL_COPY_TO_FILE: + return_chunk = false; + break; + default: + return_chunk = op.Cast().return_chunk; + break; + } + if (return_chunk) { + return UnsupportedDML(path, "dml_returning", "A data-modifying operator returning rows cannot be exported"); + } + return optional(); +} + +static optional DMLBoundaryIssue(const LogicalPlanSQLExportContext &context, + const LogicalOperator &op, + const LogicalPlanVerificationPath &path) { + if (context.Ancestors().size() != 1) { + return UnsupportedDML(path, "dml_subplan", "A data-modifying operator below the plan root cannot be exported"); + } + return DMLStatementIssue(op, path); +} + +LogicalPlanSQLExportResult LogicalInsert::ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + if (auto issue = DMLBoundaryIssue(context, *this, path)) { + return LogicalPlanSQLExportResult::Failure({std::move(*issue)}); + } + return ExportInsertNode(*this, context, path); +} + +LogicalPlanSQLExportResult LogicalDelete::ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + if (auto issue = DMLBoundaryIssue(context, *this, path)) { + return LogicalPlanSQLExportResult::Failure({std::move(*issue)}); + } + return ExportDeleteNode(*this, context, path); +} + +LogicalPlanSQLExportResult LogicalUpdate::ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + if (auto issue = DMLBoundaryIssue(context, *this, path)) { + return LogicalPlanSQLExportResult::Failure({std::move(*issue)}); + } + return ExportUpdateNode(*this, context, path); +} + +LogicalPlanSQLExportResult LogicalMergeInto::ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + if (auto issue = DMLBoundaryIssue(context, *this, path)) { + return LogicalPlanSQLExportResult::Failure({std::move(*issue)}); + } + return ExportMergeNode(*this, context, path); +} + +//! An identity projection over a data-modifying body only forwards its columns under new bindings. +static bool IsIdentityProjectionOverDML(LogicalOperator &op) { + if (op.type != LogicalOperatorType::LOGICAL_PROJECTION || op.children.size() != 1 || + !SQLExportDMLHelpers::MatchDMLBody(*op.children[0])) { + return false; + } + auto child_fields = LogicalPlanSQLExportHelpers::CreateFields(*op.children[0], LogicalPlanVerificationPath()); + return child_fields.IsSuccess() && + LogicalPlanSQLExportHelpers::IsIdentityProjection(op.Cast(), child_fields.GetValue()); +} + +bool SQLExportDMLHelpers::MatchDMLBody(LogicalOperator &op) { + switch (op.type) { + case LogicalOperatorType::LOGICAL_INSERT: + case LogicalOperatorType::LOGICAL_UPDATE: + case LogicalOperatorType::LOGICAL_DELETE: + case LogicalOperatorType::LOGICAL_MERGE_INTO: + case LogicalOperatorType::LOGICAL_COPY_TO_FILE: + return true; + default: + return MatchReturningRoot(op) || IsIdentityProjectionOverDML(op); + } +} + +bool SQLExportDMLHelpers::ContainsReplacedBoundary(const LogicalPlanSQLExportContext &context, LogicalOperator &op) { + reference current(op); + while (true) { + if (context.FindSource(current)) { + return true; + } + auto &node = current.get(); + if (node.type == LogicalOperatorType::LOGICAL_PROJECTION && node.children.size() == 1 && + MatchDMLBody(*node.children[0])) { + current = *node.children[0]; + continue; + } + return false; + } +} + +bool SQLExportDMLHelpers::IsDMLNode(const QueryNode &query) { + switch (query.type) { + case QueryNodeType::INSERT_QUERY_NODE: + case QueryNodeType::UPDATE_QUERY_NODE: + case QueryNodeType::DELETE_QUERY_NODE: + case QueryNodeType::MERGE_QUERY_NODE: + case QueryNodeType::COPY_QUERY_NODE: + case QueryNodeType::STATEMENT_NODE: + return true; + default: + return false; + } +} + +LogicalPlanSQLExportResult SQLExportDMLHelpers::ExportBody(LogicalPlanSQLExportContext &context, LogicalOperator &op, + const LogicalPlanVerificationPath &path) { + if (auto returning = MatchReturningRoot(op)) { + return ExportReturning(context, *returning, optional>(), path); + } + if (IsIdentityProjectionOverDML(op)) { + auto fields = LogicalPlanSQLExportHelpers::CreateFields(op, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + LogicalPlanSQLExportContext::AncestorScope projection_scope(context, op); + auto exported = ExportBody(context, *op.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (exported.HasError()) { + return exported; + } + // the projection only renames the forwarded columns' bindings + D_ASSERT(exported.GetValue().fields.size() == fields.GetValue().size()); + exported.GetValue().fields = std::move(fields.GetValue()); + return exported; + } + if (auto issue = DMLStatementIssue(op, path)) { + return LogicalPlanSQLExportResult::Failure({std::move(*issue)}); + } + LogicalPlanSQLExportContext::AncestorScope op_scope(context, op); + switch (op.type) { + case LogicalOperatorType::LOGICAL_INSERT: + return ExportInsertNode(op.Cast(), context, path); + case LogicalOperatorType::LOGICAL_UPDATE: + return ExportUpdateNode(op.Cast(), context, path); + case LogicalOperatorType::LOGICAL_MERGE_INTO: + return ExportMergeNode(op.Cast(), context, path); + case LogicalOperatorType::LOGICAL_COPY_TO_FILE: + return SQLExportStatementHelpers::ExportCopyToNode(op.Cast(), context, path); + default: + return ExportDeleteNode(op.Cast(), context, path); + } +} + +optional_ptr SQLExportDMLHelpers::MatchReturningRoot(LogicalOperator &root) { + if (root.type != LogicalOperatorType::LOGICAL_PROJECTION || root.children.size() != 1) { + return nullptr; + } + auto &child = *root.children[0]; + switch (child.type) { + case LogicalOperatorType::LOGICAL_INSERT: + if (!child.Cast().return_chunk) { + return nullptr; + } + break; + case LogicalOperatorType::LOGICAL_UPDATE: + if (!child.Cast().return_chunk) { + return nullptr; + } + break; + case LogicalOperatorType::LOGICAL_DELETE: + if (!child.Cast().return_chunk) { + return nullptr; + } + break; + case LogicalOperatorType::LOGICAL_MERGE_INTO: + if (!child.Cast().return_chunk) { + return nullptr; + } + break; + default: + return nullptr; + } + return &root.Cast(); +} + +//! Binding context resolving the modified rows a RETURNING projection references: the table's physical +//! columns bind positionally, and each referenced virtual column (e.g. rowid) is appended behind them +//! carrying the column name it was bound from. +static LogicalPlanVerificationResult +ReturningBindingContext(ClientContext &client, const LogicalProjection &projection, const TableCatalogEntry &table, + TableIndex table_index, const LogicalPlanVerificationPath &path, + vector statement_columns = {}) { + column_binding_map_t entries; + idx_t physical_count = 0; + for (auto &column : table.GetColumns().Physical()) { + entries.emplace(ColumnBinding(table_index, ProjectionIndex(physical_count++)), + ResolvedSQLColumnReference {{column.Name()}, column.Type()}); + } + for (auto &entry : table.GetVirtualColumns()) { + statement_columns.push_back(entry.second); + } + optional unsupported; + for (auto &expression : projection.expressions) { + ExpressionIterator::VisitExpression( + *expression, [&](const BoundColumnRefExpression &column) { + if (unsupported || column.Binding().table_index != table_index || + column.Binding().column_index < physical_count) { + return; + } + for (auto &entry : statement_columns) { + if (entry.name != column.GetAlias() || table.ColumnExists(entry.name) || + !entry.type.EqualsIncludingCollation(column.GetReturnType())) { + continue; + } + entries.emplace(column.Binding(), ResolvedSQLColumnReference {{entry.name}, entry.type}); + return; + } + unsupported = UnsupportedDML(path, "dml_returning_reference", + "RETURNING references a column SQL cannot name on the modified table"); + }); + } + if (unsupported) { + return LogicalPlanVerificationResult::Failure({std::move(*unsupported)}); + } + BoundExpressionSQLExportContext result; + result.client_context = &client; + result.discard_optimizer_metadata = true; + result.resolve_binding = + [entries = std::move(entries)](const ColumnBinding &binding) -> optional { + auto entry = entries.find(binding); + if (entry != entries.end()) { + return entry->second; + } + return {}; + }; + return LogicalPlanVerificationResult::Success(std::move(result)); +} + +LogicalPlanSQLExportResult SQLExportDMLHelpers::ExportReturning(LogicalPlanSQLExportContext &context, + LogicalProjection &root, + const optional> &output_names, + const LogicalPlanVerificationPath &path) { + auto fields = LogicalPlanSQLExportHelpers::CreateFields(root, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + auto &op = *root.children[0]; + auto child_path = LogicalPlanSQLExportHelpers::PlanChildPath(path, 0); + LogicalPlanSQLExportContext::AncestorScope root_scope(context, root); + LogicalPlanSQLExportContext::AncestorScope op_scope(context, op); + optional_ptr table; + TableIndex table_index; + vector statement_columns; + auto exported = [&]() -> LogicalPlanSQLExportResult { + switch (op.type) { + case LogicalOperatorType::LOGICAL_INSERT: { + auto &insert = op.Cast(); + table = &insert.table; + table_index = insert.table_index; + return ExportInsertNode(insert, context, child_path); + } + case LogicalOperatorType::LOGICAL_UPDATE: { + auto &update = op.Cast(); + if (update.capture_old_rows) { + return LogicalPlanSQLExportResult::Failure( + {UnsupportedDML(child_path, "dml_old_rows", "UPDATE with OLD row capture cannot be exported")}); + } + table = &update.table; + table_index = update.table_index; + return ExportUpdateNode(update, context, child_path); + } + case LogicalOperatorType::LOGICAL_MERGE_INTO: { + auto &merge = op.Cast(); + table = &merge.table; + table_index = merge.table_index; + // MERGE RETURNING binds the statement-provided merge_action column instead of the table's virtuals + statement_columns.emplace_back(Identifier("merge_action"), LogicalType::VARCHAR); + return ExportMergeNode(merge, context, child_path); + } + default: { + auto &del = op.Cast(); + table = &del.table; + table_index = del.table_index; + return ExportDeleteNode(del, context, child_path); + } + } + }(); + if (exported.HasError()) { + return exported; + } + auto expression_context = ReturningBindingContext(context.GetClientContext(), root, *table, table_index, path, + std::move(statement_columns)); + if (expression_context.HasError()) { + return LogicalPlanSQLExportResult::Failure(expression_context); + } + auto expressions = LogicalPlanSQLExportHelpers::CollectExpressions(root); + vector> returning_list; + for (idx_t i = 0; i < root.expressions.size(); i++) { + auto expression = context.ExportExpression(root, expressions, i, expression_context.GetValue(), path); + if (expression.HasError()) { + return LogicalPlanSQLExportResult::Failure(expression); + } + if (output_names && output_names->size() == root.expressions.size()) { + expression.GetValue()->SetAlias((*output_names)[i]); + } else if (!root.expressions[i]->GetAlias().empty()) { + expression.GetValue()->SetAlias(root.expressions[i]->GetAlias()); + } + returning_list.push_back(std::move(expression.GetValue())); + } + auto &query = *exported.GetValue().query; + switch (query.type) { + case QueryNodeType::INSERT_QUERY_NODE: + query.Cast().returning_list = std::move(returning_list); + break; + case QueryNodeType::UPDATE_QUERY_NODE: + query.Cast().returning_list = std::move(returning_list); + break; + case QueryNodeType::MERGE_QUERY_NODE: + query.Cast().returning_list = std::move(returning_list); + break; + default: + query.Cast().returning_list = std::move(returning_list); + break; + } + return LogicalPlanSQLExportResult::Success({std::move(exported.GetValue().query), std::move(fields.GetValue())}); +} + +} // namespace duckdb diff --git a/src/duckdb/src/planner/sql_export/sql_export_functions.cpp b/src/duckdb/src/planner/sql_export/sql_export_functions.cpp new file mode 100644 index 000000000..b17415c21 --- /dev/null +++ b/src/duckdb/src/planner/sql_export/sql_export_functions.cpp @@ -0,0 +1,387 @@ +#include "duckdb/planner/sql_export/bound_expression_sql_exporter_internal.hpp" +#include "duckdb/function/scalar/compressed_materialization_utils.hpp" +#include "duckdb/planner/bound_expression_sql_exporter.hpp" +#include "duckdb/planner/sql_export_helpers.hpp" +#include "duckdb/parser/expression/columnref_expression.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/parser/expression/function_expression.hpp" +#include "duckdb/parser/expression/lambda_expression.hpp" +#include "duckdb/planner/expression/bound_aggregate_expression.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/expression/bound_function_expression.hpp" +#include "duckdb/planner/expression/bound_lambda_expression.hpp" +#include "duckdb/planner/filter/table_filter_functions.hpp" + +namespace duckdb { + +template +static bool IsOptimizerFunctionQualification(const FUNCTION &function) { + if (function.GetCatalogName().empty() && function.GetSchemaName().empty()) { + return true; + } + return function.GetCatalogName() == "system" && function.GetSchemaName() == "main"; +} + +BoundExpressionSQLExportResult BoundExpressionSQLExportState::ExportLambda(const BoundLambdaExpression &lambda, + const BoundFunctionExpression &function, + idx_t logical_argument_count, + const LogicalPlanVerificationPath &path) { + const bool has_lambda_body = lambda.GetExpressionType() == ExpressionType::LAMBDA && + lambda.GetReturnType() == LogicalType::LAMBDA && lambda.LambdaExpr(); + const bool has_parameter_names = + lambda.ParameterCount() > 0 && lambda.ParameterNames().size() == lambda.ParameterCount(); + if (!has_lambda_body || !lambda.Captures().empty() || !has_parameter_names) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFeature( + path, "lambda_binding", "The bound lambda does not retain its SQL parameter binding")}); + } + + vector> references; + for (idx_t index = 0; index < lambda.ParameterCount(); index++) { + auto ¶meter = lambda.ParameterNames()[lambda.ParameterCount() - index - 1]; + references.push_back(make_uniq(parameter)); + } + for (idx_t index = logical_argument_count; index < function.GetChildren().size(); index++) { + auto reference = Export(*function.GetChildren()[index], SQLExportHelpers::ChildPath(path, index)); + if (reference.HasError()) { + return reference; + } + references.push_back(std::move(reference.GetValue())); + } + + lambda_reference_scopes.push_back(std::move(references)); + try { + auto body = Export(*lambda.LambdaExpr(), SQLExportHelpers::ChildPath(path, 0)); + lambda_reference_scopes.pop_back(); + if (body.HasError()) { + return body; + } + vector parameter_names; + for (auto ¶meter : lambda.ParameterNames()) { + parameter_names.push_back(parameter.GetIdentifierName()); + } + return BoundExpressionSQLExportResult::Success( + make_uniq(std::move(parameter_names), std::move(body.GetValue()))); + } catch (...) { + lambda_reference_scopes.pop_back(); + throw; + } +} + +BoundExpressionSQLExportResult BoundExpressionSQLExportState::CompressedMaterializationFailure( + const BoundFunctionExpression &expression, const LogicalPlanVerificationPath &path, string message) { + auto &function = expression.Function(); + auto &definition = function.GetDefinition(); + D_ASSERT(definition); + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, + BoundExpressionSQLExportState::DefinitionFunctionIdentity(*definition, function.GetLogicalArguments(), + function.GetLogicalReturnType()), + std::move(message))}); +} + +optional +BoundExpressionSQLExportState::TryExportCompressedMaterialization(const BoundFunctionExpression &expression, + const LogicalPlanVerificationPath &path) { + auto &function = expression.Function(); + auto &definition = function.GetDefinition(); + if (!definition) { + return {}; + } + auto compress = CMUtils::GetExpressionType(expression) == CMExpressionType::COMPRESS; + auto decompress = CMUtils::GetExpressionType(expression) == CMExpressionType::DECOMPRESS; + if (!compress && !decompress) { + return {}; + } + if (!IsOptimizerFunctionQualification(*definition) || !IsOptimizerFunctionQualification(function) || + expression.GetChildren().empty()) { + return CompressedMaterializationFailure(expression, path, + "The compressed materialization expression is malformed"); + } + if (!context.discard_optimizer_metadata) { + return CompressedMaterializationFailure(expression, path, + "Compressed materialization requires its complete logical plan"); + } + if (decompress && expression.GetChildren()[0]->GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF) { + auto &column = expression.GetChildren()[0]->Cast(); + auto resolved = context.resolve_binding ? context.resolve_binding(column.Binding()) + : optional(); + if (!resolved || resolved->type != expression.GetReturnType()) { + return CompressedMaterializationFailure(expression, path, + "The decompression input has no equivalent SQL representation"); + } + } + return ExportChild(*expression.GetChildren()[0], path, 0); +} + +BoundExpressionSQLExportResult +BoundExpressionSQLExportState::ExportScalarFunction(const BoundFunctionExpression &expression, + const LogicalPlanVerificationPath &path) { + auto &function = expression.Function(); + auto &definition = function.GetDefinition(); + D_ASSERT(definition); + if (function.GetBindCallback() == TableFilterFunctions::Bind && + TableFilterFunctions::IsTableFilterFunction(function.GetName())) { + auto identity = BoundExpressionSQLExportState::DefinitionFunctionIdentity( + *definition, function.GetLogicalArguments(), function.GetLogicalReturnType()); + if (!context.discard_optimizer_metadata) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, std::move(identity), "Internal table filters require their complete logical plan")}); + } + D_ASSERT(expression.GetReturnType() == LogicalType::BOOLEAN && expression.GetChildren().size() == 1); + return BoundExpressionSQLExportResult::Success(ConstantExpression::FromValue(Value::BOOLEAN(true))); + } + auto compressed = TryExportCompressedMaterialization(expression, path); + if (compressed) { + return std::move(*compressed); + } + auto identity = BoundExpressionSQLExportState::DefinitionFunctionIdentity( + *definition, function.GetLogicalArguments(), function.GetLogicalReturnType()); + if (!identity.IsValid()) { + identity.arguments.clear(); + for (auto &child : expression.GetChildren()) { + identity.arguments.push_back(child->GetReturnType()); + } + identity.return_type = expression.GetReturnType(); + if (!identity.IsValid()) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::InternalExpressionInvariant( + path, expression, "Bound scalar function identity is incomplete")}); + } + } + optional_idx lambda_index; + for (idx_t index = 0; index < MinValue(expression.GetChildren().size(), function.GetLogicalArguments().size()); + index++) { + if (expression.GetChildren()[index]->GetExpressionClass() == ExpressionClass::BOUND_LAMBDA) { + if (lambda_index.IsValid()) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, std::move(identity), "The scalar function retains multiple SQL lambda arguments")}); + } + lambda_index = index; + } + } + if (function.HasBindLambdaCallback() != lambda_index.IsValid()) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, std::move(identity), "The scalar function does not retain its SQL lambda argument")}); + } + const auto logical_argument_count = function.GetLogicalArguments().size(); + const auto child_count = expression.GetChildren().size(); + const bool retained_variadic_arguments = + definition->GetSignature().GetArgs() && child_count >= logical_argument_count; + const bool has_scalar_arguments = + child_count == logical_argument_count || retained_variadic_arguments || definition->HasUnbindCallback(); + const bool has_expected_arguments = + lambda_index.IsValid() ? child_count >= logical_argument_count : has_scalar_arguments; + if (!has_expected_arguments) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, std::move(identity), "The scalar function does not retain every SQL argument")}); + } + auto name = BoundExpressionSQLExportState::RebindableFunctionName(*definition); + if (!name || !SQLExportHelpers::IsSQLValueType(expression.GetReturnType())) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, std::move(identity), "The retained scalar function definition is not representable as SQL")}); + } + const bool can_reconstruct_argument_names = definition->HasUnbindCallback(); + if (definition->GetProperties().GetCaptureArgumentAliases() && !can_reconstruct_argument_names) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, std::move(identity), "The bound scalar function does not expose its SQL argument names")}); + } + if (definition->GetProperties().RequiresExpressionNames() && !can_reconstruct_argument_names) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, std::move(identity), "The bound scalar function requires expression names that are not retained")}); + } + vector argument_names; + if (!definition->HasUnbindCallback() && !function.GetNamedArguments().empty()) { + auto positional_count = function.GetPositionalArgumentCount(); + if (positional_count + function.GetNamedArguments().size() != expression.GetChildren().size()) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, std::move(identity), "The named SQL arguments are incomplete")}); + } + argument_names.resize(expression.GetChildren().size()); + for (idx_t i = 0; i < function.GetNamedArguments().size(); i++) { + argument_names[positional_count + i] = function.GetNamedArguments()[i]; + } + } + vector> children; + auto sql_argument_count = + lambda_index.IsValid() ? function.GetLogicalArguments().size() : expression.GetChildren().size(); + for (idx_t child_index = 0; child_index < sql_argument_count; child_index++) { + auto child = + lambda_index == child_index + ? ExportLambda(expression.GetChildren()[child_index]->Cast(), expression, + sql_argument_count, SQLExportHelpers::ChildPath(path, child_index)) + : Export(*expression.GetChildren()[child_index], SQLExportHelpers::ChildPath(path, child_index)); + if (child.HasError()) { + return child; + } + children.push_back(std::move(child.GetValue())); + } + unique_ptr result; + if (definition->HasUnbindCallback()) { + FunctionUnbindInput input(expression, std::move(children)); + result = definition->GetUnbindCallback()(input); + if (!result) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, std::move(identity), "The function cannot reconstruct its bound invocation")}); + } + } else if (!argument_names.empty()) { + vector arguments; + for (idx_t argument_index = 0; argument_index < children.size(); argument_index++) { + arguments.emplace_back(argument_names[argument_index], std::move(children[argument_index])); + } + result = make_uniq(*name, std::move(arguments), nullptr, nullptr, false, false, false); + } else { + result = make_uniq(*name, std::move(children), nullptr, nullptr, false, false, false); + } + // Restore result types when binding or optimization changed argument types. + const bool can_restore_result_type = SQLExportHelpers::IsSQLRepresentableType(expression.GetReturnType()) && + !expression.GetReturnType().IsAggregateState(); + const bool has_specialized_result_type = (definition->HasBindCallback() || definition->HasResolveTypesCallback() || + definition->GetReturnType().id() == LogicalTypeId::SQLNULL) && + definition->GetReturnType() != expression.GetReturnType(); + if (can_restore_result_type && has_specialized_result_type) { + return RestoreResultType(expression.GetReturnType(), std::move(result), path); + } + return BoundExpressionSQLExportResult::Success(std::move(result)); +} + +BoundAggregateSQLExportResult +BoundExpressionSQLExportState::BuildAggregateCall(const BoundAggregateExpression &expression, + const LogicalPlanVerificationPath &path) { + D_ASSERT(expression.GetExpressionType() == ExpressionType::BOUND_AGGREGATE); + auto &function = expression.Function(); + auto &definition = function.GetDefinition(); + D_ASSERT(definition); + auto identity = BoundExpressionSQLExportState::DefinitionFunctionIdentity( + *definition, function.GetLogicalArguments(), function.GetLogicalReturnType()); + if (!identity.IsValid()) { + return BoundAggregateSQLExportResult::Failure({BoundExpressionSQLExportState::InternalExpressionInvariant( + path, expression, "Bound aggregate function identity is incomplete")}); + } + auto name = BoundExpressionSQLExportState::RebindableFunctionName(*definition); + if (!definition->HasUnbindCallback() && expression.GetChildren().size() != function.GetLogicalArguments().size()) { + return BoundAggregateSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, std::move(identity), "The aggregate does not retain every SQL argument")}); + } + if (!name || !SQLExportHelpers::IsSQLValueType(expression.GetReturnType())) { + return BoundAggregateSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, std::move(identity), "The retained aggregate function definition is not representable as SQL")}); + } + D_ASSERT(expression.GetAggregateType() == AggregateType::NON_DISTINCT || + expression.GetAggregateType() == AggregateType::DISTINCT); + D_ASSERT(expression.StateExportMode() == AggregateStateExportMode::NONE || + expression.StateExportMode() == AggregateStateExportMode::STATE_EXPORT); + if (!definition->HasUnbindCallback() && (definition->GetProperties().GetCaptureArgumentAliases() || + definition->GetProperties().RequiresExpressionNames())) { + return BoundAggregateSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, std::move(identity), "The bound aggregate requires argument aliases that are not retained")}); + } + vector source_children; + for (idx_t child_index = 0; child_index < expression.GetChildren().size(); child_index++) { + source_children.emplace_back(expression.GetChildren()[child_index].get()); + } + if (expression.GetFilter()) { + source_children.emplace_back(expression.GetFilter().get(), LogicalType::BOOLEAN); + } + if (expression.GetOrderBys()) { + for (auto &order : expression.GetOrderBys()->orders) { + D_ASSERT(order.type == OrderType::ASCENDING || order.type == OrderType::DESCENDING); + D_ASSERT(order.null_order == OrderByNullType::NULLS_FIRST || + order.null_order == OrderByNullType::NULLS_LAST); + source_children.emplace_back(order.expression.get()); + } + } + + vector> children; + vector issues; + ExportChildren(source_children, path, children, issues); + if (!issues.empty()) { + return BoundAggregateSQLExportResult::Failure(std::move(issues)); + } + unique_ptr result; + if (definition->HasUnbindCallback()) { + vector> arguments; + for (idx_t i = 0; i < expression.GetChildren().size(); i++) { + arguments.push_back(std::move(children[i])); + } + AggregateFunctionUnbindInput input(expression, std::move(arguments)); + result = definition->GetUnbindCallback()(input); + if (!result) { + return BoundAggregateSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, std::move(identity), "The aggregate cannot reconstruct its bound invocation")}); + } + } else { + auto &named_arguments = function.GetNamedArguments(); + auto positional_count = function.GetPositionalArgumentCount(); + if (!named_arguments.empty() && positional_count + named_arguments.size() != expression.GetChildren().size()) { + return BoundAggregateSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, std::move(identity), "The named SQL arguments are incomplete")}); + } + vector arguments; + for (idx_t i = 0; i < expression.GetChildren().size(); i++) { + auto argument_name = !named_arguments.empty() && i >= positional_count + ? named_arguments[i - positional_count] + : Identifier(); + arguments.emplace_back(std::move(argument_name), std::move(children[i])); + } + result = make_uniq(*name, std::move(arguments)); + } + idx_t child_index = expression.GetChildren().size(); + unique_ptr filter; + if (expression.GetFilter()) { + filter = std::move(children[child_index++]); + } + auto order_bys = make_uniq(); + if (expression.GetOrderBys()) { + for (auto &order : expression.GetOrderBys()->orders) { + auto &child = children[child_index++]; + if (SQLExportHelpers::IsConstantOrderKey(*order.expression, *child)) { + continue; + } + order_bys->orders.emplace_back( + order.type, order.null_order, + SQLExportHelpers::OrderExpression(order.expression->GetReturnType(), std::move(child))); + } + } + result->FilterMutable() = std::move(filter); + result->OrderByMutable() = std::move(order_bys); + result->DistinctMutable() = expression.IsDistinct(); + result->ExportStateMutable() = expression.StateExportMode() == AggregateStateExportMode::STATE_EXPORT; + return BoundAggregateSQLExportResult::Success(std::move(result)); +} + +BoundExpressionSQLExportResult +BoundExpressionSQLExportState::ExportAggregate(const BoundAggregateExpression &expression, + const LogicalPlanVerificationPath &path) { + auto call = BuildAggregateCall(expression, path); + if (call.HasError()) { + return BoundExpressionSQLExportResult::Failure(call); + } + auto &function = expression.Function(); + auto &definition = function.GetDefinition(); + unique_ptr result = std::move(call.GetValue()); + const bool can_restore_result_type = expression.StateExportMode() == AggregateStateExportMode::NONE && + SQLExportHelpers::IsSQLRepresentableType(expression.GetReturnType()) && + !expression.GetReturnType().IsAggregateState(); + const bool has_specialized_result_type = + definition->HasBindCallback() && definition->GetReturnType() != expression.GetReturnType(); + if (can_restore_result_type && has_specialized_result_type) { + return RestoreResultType(expression.GetReturnType(), std::move(result), path); + } + return BoundExpressionSQLExportResult::Success(std::move(result)); +} + +BoundAggregateSQLExportResult +BoundExpressionSQLExportState::ExportAggregateCall(const BoundAggregateExpression &expression, + const LogicalPlanVerificationPath &path) { + auto call = BuildAggregateCall(expression, path); + if (call.HasError()) { + return call; + } + if (!expression.GetReturnType().EqualsIncludingCollation(expression.Function().GetLogicalReturnType())) { + return BoundAggregateSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFeature( + path, "aggregate_call_result_type", + "A bare aggregate call cannot preserve the bound expression's logical result type")}); + } + return call; +} + +} // namespace duckdb diff --git a/src/duckdb/src/planner/sql_export/sql_export_joins.cpp b/src/duckdb/src/planner/sql_export/sql_export_joins.cpp new file mode 100644 index 000000000..f840ef38d --- /dev/null +++ b/src/duckdb/src/planner/sql_export/sql_export_joins.cpp @@ -0,0 +1,239 @@ +#include "duckdb/planner/operator/logical_unconditional_join.hpp" +#include "duckdb/planner/operator/logical_join.hpp" +#include "duckdb/planner/sql_export/logical_plan_sql_exporter_internal.hpp" +#include "duckdb/parser/expression/columnref_expression.hpp" +#include "duckdb/parser/expression/comparison_expression.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/parser/query_node/select_node.hpp" +#include "duckdb/parser/tableref/joinref.hpp" +#include "duckdb/planner/logical_operator.hpp" +#include "duckdb/planner/operator/logical_comparison_join.hpp" +#include "duckdb/planner/sql_export_helpers.hpp" + +namespace duckdb { + +static string MarkConditionUnsupportedReason(const LogicalComparisonJoin &join) { + if (join.HasNullSafeMarkJoinConditions()) { + return string(); + } + bool comparisons_only = !join.conditions.empty(); + bool all_equal = true; + for (auto &condition : join.conditions) { + if (!condition.IsComparison()) { + comparisons_only = false; + continue; + } + all_equal &= condition.GetComparisonType() == ExpressionType::COMPARE_EQUAL; + } + const bool has_supported_conjunction = join.conditions.size() == 1 || all_equal; + if (!comparisons_only || !has_supported_conjunction) { + return "The MARK condition requires conjunction execution semantics"; + } + return string(); +} + +static bool CanReconstructMarkGroups(const LogicalComparisonJoin &join) { + vector group_types; + if (!join.TryGetMarkJoinGroupTypes(group_types) || group_types.size() != join.mark_types.size()) { + return false; + } + for (idx_t i = 0; i < group_types.size(); i++) { + if (!group_types[i].EqualsIncludingCollation(join.mark_types[i])) { + return false; + } + } + return true; +} + +static bool RequiresMarkGroupMetadata(const LogicalComparisonJoin &join) { + if (join.join_type != JoinType::MARK || join.mark_types.empty()) { + return false; + } + idx_t comparison_count = 0; + bool tuple_comparison = false; + bool all_equal = true; + for (auto &condition : join.conditions) { + if (!condition.IsComparison()) { + continue; + } + comparison_count++; + tuple_comparison |= condition.GetLHS().GetReturnType().id() == LogicalTypeId::TUPLE; + all_equal &= condition.GetComparisonType() == ExpressionType::COMPARE_EQUAL; + } + if (comparison_count == join.mark_types.size() + 1) { + return true; + } + // Retained types also disable uncorrelated row-equality NULL handling. + return (comparison_count > 1 || tuple_comparison) && all_equal; +} + +static LogicalPlanVerificationResult> +ExportJoinCondition(LogicalJoin &op, LogicalPlanSQLExportContext &context, + const BoundExpressionSQLExportContext &expression_context, + const LogicalPlanVerificationPath &path) { + using Result = LogicalPlanVerificationResult>; + unique_ptr predicate; + auto expressions = LogicalPlanSQLExportHelpers::CollectExpressions(op); + if (op.type == LogicalOperatorType::LOGICAL_ANY_JOIN) { + if (op.join_type == JoinType::MARK) { + return Result::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "mark_condition_semantics", "The MARK condition requires conjunction execution semantics")}); + } + auto exported = context.ExportExpression(op, expressions, 0, expression_context, path); + if (exported.HasError()) { + return Result::Failure(exported); + } + predicate = std::move(exported.GetValue()); + } else { + auto &comparison = op.Cast(); + if (!comparison.duplicate_eliminated_columns.empty()) { + return Result::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "join_delim_state", "The join requires a duplicate-eliminated input scope")}); + } + if (comparison.join_type == JoinType::MARK && !CanReconstructMarkGroups(comparison)) { + auto reason = MarkConditionUnsupportedReason(comparison); + if (!reason.empty()) { + return Result::Failure( + {LogicalPlanSQLExportHelpers::PlanUnsupportedFeature(path, "mark_condition_semantics", reason)}); + } + if (RequiresMarkGroupMetadata(comparison)) { + return Result::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "mark_group_null_semantics", "The MARK join requires its group-specific NULL semantics")}); + } + } + idx_t ordinal = 0; + for (auto &condition : comparison.conditions) { + auto lhs = context.ExportExpression(op, expressions, ordinal++, expression_context, path); + if (lhs.HasError()) { + return Result::Failure(lhs); + } + auto conjunct = std::move(lhs.GetValue()); + if (condition.IsComparison()) { + auto rhs = context.ExportExpression(op, expressions, ordinal++, expression_context, path); + if (rhs.HasError()) { + return Result::Failure(rhs); + } + conjunct = make_uniq(condition.GetComparisonType(), std::move(conjunct), + std::move(rhs.GetValue())); + } + predicate = SQLExportHelpers::Conjoin(std::move(predicate), std::move(conjunct)); + } + if (!predicate) { + predicate = ConstantExpression::FromValue(Value::BOOLEAN(true)); + } + } + return Result::Success(std::move(predicate)); +} + +static LogicalPlanSQLExportResult ExportJoin(LogicalOperator &op, LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + D_ASSERT(op.children.size() == 2); + auto fields = LogicalPlanSQLExportHelpers::CreateFields(op, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + auto left = context.ExportChild(*op.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (left.HasError()) { + return LogicalPlanSQLExportResult::Failure(left); + } + auto right = context.ExportChild(*op.children[1], LogicalPlanSQLExportHelpers::PlanChildPath(path, 1)); + if (right.HasError()) { + return LogicalPlanSQLExportResult::Failure(right); + } + vector> children {left.GetValue(), right.GetValue()}; + LogicalPlanSQLExportHelpers::PropagateSemanticTypes(fields.GetValue(), children); + auto left_plain = LogicalPlanSQLExportHelpers::PlainScope(*left.GetValue().relation.query); + auto right_plain = LogicalPlanSQLExportHelpers::PlainScope(*right.GetValue().relation.query); + if (left_plain && left_plain->where_clause) { + left_plain = nullptr; + } + if (right_plain && right_plain->where_clause) { + right_plain = nullptr; + } + identifier_set_t left_aliases, right_aliases; + if (left_plain) { + LogicalPlanSQLExportHelpers::CollectScopeAliases(*left_plain->from_table, left_aliases); + if (left_aliases.count(right.GetValue().relation_alias)) { + left_plain = nullptr; + } + } + if (right_plain) { + LogicalPlanSQLExportHelpers::CollectScopeAliases(*right_plain->from_table, right_aliases); + if (right_aliases.count(left.GetValue().relation_alias)) { + right_plain = nullptr; + } + } + if (left_plain && right_plain) { + for (auto &alias : left_aliases) { + if (right_aliases.count(alias)) { + right_plain = nullptr; + break; + } + } + } + auto expression_context = LogicalPlanSQLExportHelpers::CreateBindingContext(context.GetClientContext(), children, + {left_plain, right_plain}); + auto join = make_uniq(); + optional_ptr logical_join; + if (op.type == LogicalOperatorType::LOGICAL_CROSS_PRODUCT) { + join->ref_type = JoinRefType::CROSS; + } else if (op.type == LogicalOperatorType::LOGICAL_POSITIONAL_JOIN) { + join->ref_type = JoinRefType::POSITIONAL; + } else { + logical_join = op.Cast(); + join->type = logical_join->join_type; + if (op.type == LogicalOperatorType::LOGICAL_ASOF_JOIN) { + join->ref_type = JoinRefType::ASOF; + } + auto condition = ExportJoinCondition(*logical_join, context, expression_context, path); + if (condition.HasError()) { + return LogicalPlanSQLExportResult::Failure(condition); + } + join->condition = std::move(condition.GetValue()); + } + auto select = make_uniq(); + for (auto &field : fields.GetValue()) { + unique_ptr expression; + if (logical_join && logical_join->join_type == JoinType::MARK && + field.source_binding.table_index == logical_join->mark_index) { + expression = make_uniq(Identifier("__mark_join_marker")); + } else { + auto resolved = expression_context.resolve_binding(field.source_binding); + if (!resolved || resolved->type != field.type) { + auto issue = SQLExportHelpers::MakeIssue( + LogicalPlanVerificationIssueCode::INTERNAL_INVARIANT, LogicalPlanVerificationPhase::PLAN_EXPORT, + path, LogicalPlanVerificationConstructIdentity::LogicalOperator(op.type), + "Join output field has no matching SQL column binding"); + return LogicalPlanSQLExportResult::Failure({std::move(issue)}); + } + expression = make_uniq(resolved->names); + } + expression->SetAlias(LogicalPlanSQLExportHelpers::FieldIdentifier(select->select_list.size())); + select->select_list.push_back(std::move(expression)); + } + join->left = left_plain ? std::move(left.GetValue().relation.query->Cast().from_table) + : LogicalPlanSQLExportHelpers::CreateSubquery(std::move(left.GetValue())); + join->right = right_plain ? std::move(right.GetValue().relation.query->Cast().from_table) + : LogicalPlanSQLExportHelpers::CreateSubquery(std::move(right.GetValue())); + select->from_table = std::move(join); + return LogicalPlanSQLExportResult::Success({std::move(select), std::move(fields.GetValue())}); +} + +LogicalPlanVerificationResult +LogicalJoin::ToSQL(LogicalPlanSQLExportContext &context, const LogicalPlanVerificationPath &path) { + if (type != LogicalOperatorType::LOGICAL_COMPARISON_JOIN && type != LogicalOperatorType::LOGICAL_ANY_JOIN && + type != LogicalOperatorType::LOGICAL_ASOF_JOIN) { + return LogicalOperator::ToSQL(context, path); + } + return ExportJoin(*this, context, path); +} + +LogicalPlanVerificationResult +LogicalUnconditionalJoin::ToSQL(LogicalPlanSQLExportContext &context, const LogicalPlanVerificationPath &path) { + if (type != LogicalOperatorType::LOGICAL_CROSS_PRODUCT && type != LogicalOperatorType::LOGICAL_POSITIONAL_JOIN) { + return LogicalOperator::ToSQL(context, path); + } + return ExportJoin(*this, context, path); +} + +} // namespace duckdb diff --git a/src/duckdb/src/planner/sql_export/sql_export_limit.cpp b/src/duckdb/src/planner/sql_export/sql_export_limit.cpp new file mode 100644 index 000000000..4cc9c8911 --- /dev/null +++ b/src/duckdb/src/planner/sql_export/sql_export_limit.cpp @@ -0,0 +1,306 @@ +#include "duckdb/planner/sql_export/logical_plan_sql_exporter_internal.hpp" +#include "duckdb/parser/expression/columnref_expression.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/parser/expression/subquery_expression.hpp" +#include "duckdb/parser/parsed_expression_iterator.hpp" +#include "duckdb/parser/common_table_expression_info.hpp" +#include "duckdb/parser/tableref/basetableref.hpp" +#include "duckdb/parser/query_node/select_node.hpp" +#include "duckdb/parser/statement/select_statement.hpp" +#include "duckdb/planner/bound_expression_sql_exporter.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/logical_operator.hpp" +#include "duckdb/planner/operator/logical_aggregate.hpp" +#include "duckdb/planner/operator/logical_materialized_cte.hpp" +#include "duckdb/planner/operator/logical_cteref.hpp" +#include "duckdb/planner/operator/logical_limit.hpp" +#include "duckdb/planner/operator/logical_expression_get.hpp" + +namespace duckdb { + +namespace { + +struct LimitSQLExport { + unique_ptr modifier; + vector sources; +}; + +class LimitSQLExporter { +public: + explicit LimitSQLExporter(LogicalPlanSQLExportContext &context_p) : context(context_p) { + } + LogicalPlanVerificationResult Export(LogicalLimit &limit, const LogicalPlanVerificationPath &path); + +private: + using LimitExpressionResult = LogicalPlanVerificationResult>; + static bool ProducesOneRow(const LogicalOperator &op, const vector &single_row_ctes = {}); + static LimitExpressionResult LimitBindingFailure(const LogicalPlanVerificationPath &path); + LimitExpressionResult ResolveLimitColumn(const ColumnBinding &binding, LogicalOperator &input, + const LogicalPlanVerificationPath &path); + LimitExpressionResult ExportLimitExpression(const Expression &expression, LogicalOperator &input, + const LogicalPlanVerificationPath &path, + const LogicalPlanVerificationPath &expression_path); + +private: + LogicalPlanSQLExportContext &context; + vector sources; +}; + +bool LimitSQLExporter::ProducesOneRow(const LogicalOperator &op, const vector &single_row_ctes) { + if (op.type == LogicalOperatorType::LOGICAL_DUMMY_SCAN) { + return true; + } + if (op.type == LogicalOperatorType::LOGICAL_AGGREGATE_AND_GROUP_BY) { + auto &aggregate = op.Cast(); + return aggregate.groups.empty() && aggregate.grouping_sets.size() <= 1; + } + if (op.type == LogicalOperatorType::LOGICAL_PROJECTION) { + return ProducesOneRow(*op.children[0], single_row_ctes); + } + if (op.type == LogicalOperatorType::LOGICAL_EXPRESSION_GET) { + return op.Cast().expressions.size() == 1 && + ProducesOneRow(*op.children[0], single_row_ctes); + } + if (op.type == LogicalOperatorType::LOGICAL_CROSS_PRODUCT) { + return ProducesOneRow(*op.children[0], single_row_ctes) && ProducesOneRow(*op.children[1], single_row_ctes); + } + if (op.type == LogicalOperatorType::LOGICAL_MATERIALIZED_CTE) { + auto ctes = single_row_ctes; + if (ProducesOneRow(*op.children[0], ctes)) { + ctes.push_back(op.Cast().table_index); + } + return ProducesOneRow(*op.children[1], ctes); + } + if (op.type == LogicalOperatorType::LOGICAL_CTE_REF) { + auto index = op.Cast().cte_index; + return std::find(single_row_ctes.begin(), single_row_ctes.end(), index) != single_row_ctes.end(); + } + return false; +} + +LimitSQLExporter::LimitExpressionResult LimitSQLExporter::LimitBindingFailure(const LogicalPlanVerificationPath &path) { + return LimitExpressionResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "limit_binding", "SQL LIMIT requires an independent, single-row scalar input")}); +} + +LimitSQLExporter::LimitExpressionResult LimitSQLExporter::ResolveLimitColumn(const ColumnBinding &binding, + LogicalOperator &input, + const LogicalPlanVerificationPath &path) { + if (context.FindSource(input)) { + // A row-stream replacement exposes columns, not the original expressions + // that made this LIMIT input independent of the rows being limited. + return LimitBindingFailure(path); + } + auto bindings = input.GetColumnBindings(); + auto found = std::find(bindings.begin(), bindings.end(), binding); + if (found == bindings.end()) { + return LimitBindingFailure(path); + } + auto column = NumericCast(found - bindings.begin()); + if (input.type == LogicalOperatorType::LOGICAL_PROJECTION) { + return ExportLimitExpression(*input.expressions[column], *input.children[0], + LogicalPlanSQLExportHelpers::PlanChildPath(path, 0), + LogicalPlanSQLExportHelpers::PlanExpressionPath(path, column)); + } + if (input.type == LogicalOperatorType::LOGICAL_ORDER_BY) { + return ResolveLimitColumn(binding, *input.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + } + if (input.type != LogicalOperatorType::LOGICAL_CROSS_PRODUCT) { + return LimitBindingFailure(path); + } + for (idx_t child_index = 0; child_index < input.children.size(); child_index++) { + auto &child = *input.children[child_index]; + auto child_bindings = child.GetColumnBindings(); + auto child_column = std::find(child_bindings.begin(), child_bindings.end(), binding); + if (child_column == child_bindings.end()) { + continue; + } + auto child_path = LogicalPlanSQLExportHelpers::PlanChildPath(path, child_index); + if (!ProducesOneRow(child)) { + return ResolveLimitColumn(binding, child, child_path); + } + idx_t source_index = 0; + for (; source_index < sources.size(); source_index++) { + if (&sources[source_index].op.get() == &child) { + break; + } + } + if (source_index == sources.size()) { + auto exported = context.ExportChild(child, child_path, sources); + if (exported.HasError()) { + return LimitExpressionResult::Failure(exported); + } + sources.push_back( + {child, std::move(exported.GetValue().relation_alias), std::move(exported.GetValue().relation)}); + } + auto scalar = make_uniq(); + auto table = make_uniq(); + table->SetTable(sources[source_index].name); + scalar->from_table = std::move(table); + scalar->select_list.push_back(make_uniq( + LogicalPlanSQLExportHelpers::FieldIdentifier(NumericCast(child_column - child_bindings.begin())))); + auto subquery = make_uniq(); + subquery->SubqueryMutable() = make_uniq(); + subquery->SubqueryMutable()->node = std::move(scalar); + subquery->GetSubqueryTypeMutable() = SubqueryType::SCALAR; + return LimitExpressionResult::Success(std::move(subquery)); + } + return LimitBindingFailure(path); +} + +LimitSQLExporter::LimitExpressionResult +LimitSQLExporter::ExportLimitExpression(const Expression &expression, LogicalOperator &input, + const LogicalPlanVerificationPath &path, + const LogicalPlanVerificationPath &expression_path) { + if (expression.GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF) { + auto &column = expression.Cast(); + return column.Depth() == 0 ? ResolveLimitColumn(column.Binding(), input, path) : LimitBindingFailure(path); + } + if (expression.IsVolatile()) { + return LimitBindingFailure(path); + } + vector, unique_ptr>> replacements; + vector issues; + BoundExpressionSQLExportContext expression_context; + expression_context.client_context = context.GetClientContext(); + expression_context.resolve_binding = [&](const ColumnBinding &binding) -> optional { + auto resolved = ResolveLimitColumn(binding, input, path); + if (resolved.HasError()) { + issues = resolved.GetIssues(); + return {}; + } + auto bindings = input.GetColumnBindings(); + auto found = std::find(bindings.begin(), bindings.end(), binding); + D_ASSERT(found != bindings.end()); + vector names {context.NextRelationAlias(), LogicalPlanSQLExportHelpers::FieldIdentifier(0)}; + replacements.emplace_back(names, std::move(resolved.GetValue())); + return ResolvedSQLColumnReference { + std::move(names), input.types[NumericCast(found - bindings.begin())], {}}; + }; + auto result = BoundExpressionSQLExporter::ExportAtPath(expression, expression_context, expression_path); + if (!issues.empty()) { + return LimitExpressionResult::Failure(std::move(issues)); + } + if (result.HasError()) { + return result; + } + std::function &)> replace = [&](unique_ptr &expr) { + if (expr->GetExpressionClass() == ExpressionClass::COLUMN_REF) { + for (auto &entry : replacements) { + if (expr->Cast().ColumnNames() == entry.first) { + expr = entry.second->Copy(); + return; + } + } + } + ParsedExpressionIterator::EnumerateChildren(*expr, replace); + }; + replace(result.GetValue()); + return result; +} + +LogicalPlanVerificationResult LimitSQLExporter::Export(LogicalLimit &limit, + const LogicalPlanVerificationPath &path) { + auto modifier = make_uniq(); + idx_t expression_ordinal = 0; + for (idx_t i = 0; i < 2; i++) { + auto &value = i == 0 ? limit.limit_val : limit.offset_val; + auto &target = i == 0 ? modifier->limit : modifier->offset; + switch (value.Type()) { + case LimitNodeType::UNSET: + break; + case LimitNodeType::CONSTANT_VALUE: + target = ConstantExpression::FromValue(Value::BIGINT(NumericCast(value.GetConstantValue()))); + break; + case LimitNodeType::CONSTANT_PERCENTAGE: + modifier->limit_type = LimitValueType::PERCENTAGE; + target = ConstantExpression::FromValue(Value::DOUBLE(value.GetConstantPercentage())); + break; + case LimitNodeType::EXPRESSION_VALUE: + case LimitNodeType::EXPRESSION_PERCENTAGE: { + if (!value.GetExpression()->IsScalar()) { + auto expression = ExportLimitExpression( + *value.GetExpression(), *limit.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0), + LogicalPlanSQLExportHelpers::PlanExpressionPath(path, expression_ordinal++)); + if (expression.HasError()) { + return LogicalPlanVerificationResult::Failure(expression); + } + target = std::move(expression.GetValue()); + if (value.Type() == LimitNodeType::EXPRESSION_PERCENTAGE) { + modifier->limit_type = LimitValueType::PERCENTAGE; + } + break; + } + if (value.Type() == LimitNodeType::EXPRESSION_PERCENTAGE) { + modifier->limit_type = LimitValueType::PERCENTAGE; + } + auto expression = BoundExpressionSQLExporter::ExportAtPath( + *value.GetExpression(), {}, + LogicalPlanSQLExportHelpers::PlanExpressionPath(path, expression_ordinal++)); + if (expression.HasError()) { + return LogicalPlanVerificationResult::Failure(expression); + } + target = std::move(expression.GetValue()); + break; + } + default: + D_ASSERT(false); + } + } + return LogicalPlanVerificationResult::Success({std::move(modifier), std::move(sources)}); +} + +} // namespace + +LogicalPlanSQLExportResult LogicalLimit::ToSQL(LogicalPlanSQLExportContext &export_context, + const LogicalPlanVerificationPath &path) { + auto &limit = *this; + D_ASSERT(limit.children.size() == 1); + auto fields = LogicalPlanSQLExportHelpers::CreateFields(limit, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + LimitSQLExporter modifier_exporter(export_context); + auto exported = modifier_exporter.Export(limit, path); + if (exported.HasError()) { + return LogicalPlanSQLExportResult::Failure(exported); + } + auto &modifier = exported.GetValue().modifier; + auto &sources = exported.GetValue().sources; + auto child = + export_context.ExportChild(*limit.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0), sources); + if (child.HasError()) { + return LogicalPlanSQLExportResult::Failure(child); + } + LogicalPlanSQLExportHelpers::PropagateSemanticTypes(fields.GetValue(), {child.GetValue()}); + if (limit.unpruned_offset.IsValid()) { + modifier->offset = + ConstantExpression::FromValue(Value::BIGINT(NumericCast(limit.unpruned_offset.GetIndex()))); + } + // Keep the ordering and its LIMIT consumer in the same query scope. + bool has_limit = false; + for (auto &existing : child.GetValue().relation.query->modifiers) { + has_limit |= existing->type == ResultModifierType::LIMIT_MODIFIER; + } + unique_ptr query; + if (has_limit) { + auto select = export_context.ForwardFields(child.GetValue(), fields.GetValue()); + select->from_table = LogicalPlanSQLExportHelpers::CreateSubquery(std::move(child.GetValue())); + query = std::move(select); + } else { + query = std::move(child.GetValue().relation.query); + } + query->modifiers.push_back(std::move(modifier)); + for (auto &source : sources) { + auto info = make_uniq(); + for (idx_t i = 0; i < source.relation.fields.size(); i++) { + info->aliases.push_back(LogicalPlanSQLExportHelpers::FieldIdentifier(i)); + } + info->query_node = std::move(source.relation.query); + info->materialized = CTEMaterialize::CTE_MATERIALIZE_ALWAYS; + query->cte_map.map.insert(source.name, std::move(info)); + } + return LogicalPlanSQLExportResult::Success({std::move(query), std::move(fields.GetValue())}); +} + +} // namespace duckdb diff --git a/src/duckdb/src/planner/sql_export/sql_export_pivot.cpp b/src/duckdb/src/planner/sql_export/sql_export_pivot.cpp new file mode 100644 index 000000000..6edb00337 --- /dev/null +++ b/src/duckdb/src/planner/sql_export/sql_export_pivot.cpp @@ -0,0 +1,215 @@ +#include "duckdb/planner/sql_export/logical_plan_sql_exporter_internal.hpp" +#include "duckdb/parser/expression/case_expression.hpp" +#include "duckdb/parser/expression/columnref_expression.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/parser/expression/operator_expression.hpp" +#include "duckdb/parser/expression/function_expression.hpp" +#include "duckdb/parser/expression/lambda_expression.hpp" +#include "duckdb/parser/common_table_expression_info.hpp" +#include "duckdb/parser/tableref/basetableref.hpp" +#include "duckdb/parser/query_node/select_node.hpp" +#include "duckdb/parser/tableref/emptytableref.hpp" +#include "duckdb/parser/tableref/joinref.hpp" +#include "duckdb/planner/bound_expression_sql_exporter.hpp" +#include "duckdb/planner/expression/bound_aggregate_expression.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/expression/bound_constant_expression.hpp" +#include "duckdb/planner/expression_iterator.hpp" +#include "duckdb/planner/sql_export_helpers.hpp" +#include "duckdb/planner/operator/logical_pivot.hpp" + +namespace duckdb { + +static LogicalPlanVerificationResult> +ExportPivotDefault(ClientContext &context, const BoundAggregateExpression &aggregate, + const LogicalPlanVerificationPath &path) { + if (aggregate.Function().GetStability() == FunctionStability::VOLATILE || + aggregate.Function().GetErrorMode() == FunctionErrors::CAN_THROW_RUNTIME_ERROR) { + return LogicalPlanVerificationResult>::Failure( + {LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "pivot_empty_aggregate", + "A volatile or fallible PIVOT default must be evaluated before query execution")}); + } + if (aggregate.StateExportMode() != AggregateStateExportMode::NONE) { + return LogicalPlanVerificationResult>::Failure( + {LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "pivot_empty_aggregate", "The PIVOT default requires an ordinary aggregate invocation")}); + } + auto copy = aggregate.Copy(); + bool outer_reference = false; + std::function &)> replace = [&](unique_ptr &expression) { + if (expression->GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF) { + if (expression->Cast().Depth() != 0) { + outer_reference = true; + return; + } + expression = make_uniq(Value(expression->GetReturnType())); + return; + } + ExpressionIterator::EnumerateChildren(*expression, replace); + }; + replace(copy); + if (outer_reference) { + return LogicalPlanVerificationResult>::Failure( + {LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "pivot_empty_aggregate", "The PIVOT default contains an unresolved outer reference")}); + } + auto result = BoundExpressionSQLExporter::ExportAggregateCallAtPath( + copy->Cast(), LogicalPlanSQLExportHelpers::CreateBindingContext(context, {}), path); + if (result.HasError()) { + return LogicalPlanVerificationResult>::Failure(result); + } + return LogicalPlanVerificationResult>::Success(std::move(result.GetValue())); +} + +static inline bool HasConsistentPivotLayout(const LogicalPivot &pivot) { + auto &info = pivot.bound_pivot; + auto aggregate_count = info.aggregates.size(); + if (aggregate_count == 0 || info.group_count > pivot.types.size() || info.types != pivot.types || + info.pivot_values.size() != pivot.types.size() - info.group_count || + info.pivot_values.size() % aggregate_count != 0) { + return false; + } + auto &child_types = pivot.children[0]->types; + if (child_types.size() != info.group_count + aggregate_count + 1) { + return false; + } + for (idx_t group_idx = 0; group_idx < info.group_count; group_idx++) { + if (!child_types[group_idx].EqualsIncludingCollation(info.types[group_idx])) { + return false; + } + } + for (idx_t aggregate_idx = 0; aggregate_idx < aggregate_count; aggregate_idx++) { + auto &aggregate = info.aggregates[aggregate_idx]; + auto &list_type = child_types[info.group_count + aggregate_idx]; + if (!aggregate || aggregate->GetExpressionClass() != ExpressionClass::BOUND_AGGREGATE || + list_type.id() != LogicalTypeId::LIST || + !ListType::GetChildType(list_type).EqualsIncludingCollation(aggregate->GetReturnType())) { + return false; + } + for (idx_t target_idx = 0; target_idx < info.pivot_values.size(); target_idx += aggregate_count) { + if (info.pivot_values[target_idx + aggregate_idx] != info.pivot_values[target_idx] || + !info.types[info.group_count + target_idx + aggregate_idx].EqualsIncludingCollation( + aggregate->GetReturnType())) { + return false; + } + } + } + auto &key_type = child_types.back(); + return key_type.id() == LogicalTypeId::LIST && ListType::GetChildType(key_type).id() == LogicalTypeId::VARCHAR; +} + +LogicalPlanSQLExportResult LogicalPivot::ToSQL(LogicalPlanSQLExportContext &export_context, + const LogicalPlanVerificationPath &path) { + auto &pivot = *this; + D_ASSERT(pivot.children.size() == 1); + D_ASSERT(HasConsistentPivotLayout(pivot)); + auto fields = LogicalPlanSQLExportHelpers::CreateFields(pivot, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + auto &info = pivot.bound_pivot; + auto aggregate_count = info.aggregates.size(); + auto target_count = (fields.GetValue().size() - info.group_count) / aggregate_count; + if (target_count == 0) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "pivot_layout", "The PIVOT has no output targets")}); + } + + auto defaults = make_uniq(); + defaults->from_table = make_uniq(); + defaults->where_clause = ConstantExpression::FromValue(Value::BOOLEAN(false)); + auto defaults_name = export_context.NextRelationAlias(); + for (idx_t aggregate_idx = 0; aggregate_idx < aggregate_count; aggregate_idx++) { + auto &expression = info.aggregates[aggregate_idx]; + auto &aggregate = expression->Cast(); + auto value = ExportPivotDefault(export_context.GetClientContext(), aggregate, + LogicalPlanSQLExportHelpers::PlanExpressionPath(path, aggregate_idx)); + if (value.HasError()) { + return LogicalPlanSQLExportResult::Failure(value); + } + value.GetValue()->SetAlias(LogicalPlanSQLExportHelpers::FieldIdentifier(aggregate_idx)); + defaults->select_list.push_back(std::move(value.GetValue())); + } + auto child = export_context.ExportChild(*pivot.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (child.HasError()) { + return LogicalPlanSQLExportResult::Failure(child); + } + auto call = [](const char *name, unique_ptr first, + unique_ptr second = nullptr) -> unique_ptr { + vector> arguments; + arguments.push_back(std::move(first)); + if (second) { + arguments.push_back(std::move(second)); + } + return SQLExportHelpers::SystemFunction(name, std::move(arguments)); + }; + auto key_name = export_context.NextRelationAlias(); + auto encoded_keys = + call("list_transform", + LogicalPlanSQLExportHelpers::ChildColumn(child.GetValue(), info.group_count + aggregate_count), + make_uniq(vector {key_name.GetIdentifierName()}, + call("encode", make_uniq(key_name)))); + auto reversed_keys = call("list_reverse", std::move(encoded_keys)); + auto length = + call("len", LogicalPlanSQLExportHelpers::ChildColumn(child.GetValue(), info.group_count + aggregate_count)); + auto select = make_uniq(); + for (idx_t group_idx = 0; group_idx < info.group_count; group_idx++) { + auto expression = LogicalPlanSQLExportHelpers::ChildColumn(child.GetValue(), group_idx); + expression->SetAlias(LogicalPlanSQLExportHelpers::FieldIdentifier(group_idx)); + select->select_list.push_back(std::move(expression)); + } + unordered_set seen_keys; + for (idx_t target_idx = 0; target_idx < target_count; target_idx++) { + auto &key = info.pivot_values[target_idx * aggregate_count]; + unique_ptr position = ConstantExpression::FromValue(Value(LogicalType::BIGINT)); + auto first_target = seen_keys.insert(key).second; + if (first_target) { + position = + call("list_position", reversed_keys->Copy(), call("encode", ConstantExpression::FromValue(Value(key)))); + } + auto index = + call("-", call("+", length->Copy(), ConstantExpression::FromValue(Value::BIGINT(1))), position->Copy()); + for (idx_t aggregate_idx = 0; aggregate_idx < aggregate_count; aggregate_idx++) { + auto expression = + call("list_extract", + LogicalPlanSQLExportHelpers::ChildColumn(child.GetValue(), info.group_count + aggregate_idx), + index->Copy()); + auto fallback = make_uniq(LogicalPlanSQLExportHelpers::FieldIdentifier(aggregate_idx), + defaults_name); + if (!first_target) { + expression = std::move(fallback); + } else { + auto result = make_uniq(); + CaseCheck check; + check.when_expr = make_uniq(ExpressionType::OPERATOR_IS_NULL, position->Copy()); + check.then_expr = std::move(fallback); + result->CaseChecksMutable().push_back(std::move(check)); + result->ElseMutable() = std::move(expression); + expression = std::move(result); + } + expression->SetAlias(LogicalPlanSQLExportHelpers::FieldIdentifier(select->select_list.size())); + select->select_list.push_back(std::move(expression)); + } + } + auto source = make_uniq(); + source->SetTable(defaults_name); + source->alias = defaults_name; + auto join = make_uniq(JoinRefType::CROSS); + join->left = LogicalPlanSQLExportHelpers::CreateSubquery(std::move(child.GetValue())); + join->right = std::move(source); + select->from_table = std::move(join); + auto default_info = make_uniq(); + default_info->query_node = std::move(defaults); + default_info->materialized = CTEMaterialize::CTE_MATERIALIZE_ALWAYS; + select->cte_map.map.insert(defaults_name, std::move(default_info)); + vector row; + for (idx_t i = 0; i < select->select_list.size(); i++) { + row.emplace_back(LogicalPlanSQLExportHelpers::FieldIdentifier(i), std::move(select->select_list[i])); + } + auto packed_row = SQLExportHelpers::SystemFunction("struct_pack", std::move(row)); + select->select_list.clear(); + return export_context.ExportRow(std::move(select), std::move(packed_row), std::move(fields.GetValue())); +} + +} // namespace duckdb diff --git a/src/duckdb/src/planner/sql_export/sql_export_relations.cpp b/src/duckdb/src/planner/sql_export/sql_export_relations.cpp new file mode 100644 index 000000000..f0a40204a --- /dev/null +++ b/src/duckdb/src/planner/sql_export/sql_export_relations.cpp @@ -0,0 +1,547 @@ +#include "duckdb/planner/operator/logical_unnest.hpp" +#include "duckdb/planner/sql_export/bound_expression_sql_exporter_internal.hpp" +#include "duckdb/planner/operator/logical_window.hpp" +#include "duckdb/planner/operator/logical_distinct.hpp" +#include "duckdb/planner/operator/logical_top_n.hpp" +#include "duckdb/planner/operator/logical_order.hpp" +#include "duckdb/planner/sql_export/logical_plan_sql_exporter_internal.hpp" +#include "duckdb/function/scalar/compressed_materialization_utils.hpp" +#include "duckdb/planner/logical_plan_sql_exporter.hpp" +#include "duckdb/common/limits.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/parser/expression/operator_expression.hpp" +#include "duckdb/parser/query_node/select_node.hpp" +#include "duckdb/parser/query_node/set_operation_node.hpp" +#include "duckdb/parser/tableref/emptytableref.hpp" +#include "duckdb/planner/bound_expression_sql_exporter.hpp" +#include "duckdb/planner/operator/logical_get.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/logical_operator.hpp" +#include "duckdb/planner/operator/logical_aggregate.hpp" +#include "duckdb/planner/operator/logical_empty_result.hpp" +#include "duckdb/planner/operator/logical_set_operation.hpp" +#include "duckdb/planner/operator/logical_filter.hpp" +#include "duckdb/planner/sql_export_helpers.hpp" +#include "duckdb/planner/operator/logical_projection.hpp" +#include "duckdb/planner/operator/logical_sample.hpp" +#include "duckdb/planner/expression/bound_window_expression.hpp" +#include "duckdb/planner/expression/bound_unnest_expression.hpp" + +namespace duckdb { + +static LogicalType SemanticExpressionType(const Expression &expression, + const BoundExpressionSQLExportContext &context) { + if (expression.GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF && context.resolve_binding) { + auto &column = expression.Cast(); + auto resolved = context.resolve_binding(column.Binding()); + if (resolved) { + const bool matches_optimizer_type = + resolved->optimizer_type && *resolved->optimizer_type == column.GetReturnType(); + if (resolved->type == column.GetReturnType() || matches_optimizer_type) { + return resolved->type; + } + } + } + if (context.discard_optimizer_metadata) { + if (auto wrapped = CMUtils::GetWrappedInput(expression)) { + return SemanticExpressionType(*wrapped, context); + } + } + return expression.GetReturnType(); +} + +static void ApplySemanticType(LogicalPlanSQLExportField &field, const Expression &expression, + const BoundExpressionSQLExportContext &context) { + auto semantic_type = SemanticExpressionType(expression, context); + if (semantic_type == field.type) { + return; + } + field.optimizer_type = field.type; + field.type = std::move(semantic_type); +} + +static bool HasSafePredicates(const LogicalOperator &op) { + if (op.type == LogicalOperatorType::LOGICAL_EXPRESSION_GET) { + return !LogicalPlanSQLExportHelpers::HasEffectfulExpressions(op) && + (op.children[0]->type == LogicalOperatorType::LOGICAL_DUMMY_SCAN || HasSafePredicates(*op.children[0])); + } + if (op.type != LogicalOperatorType::LOGICAL_FILTER && op.type != LogicalOperatorType::LOGICAL_PROJECTION) { + return false; + } + for (auto &expression : op.expressions) { + if (expression->IsVolatile() || expression->CanThrow()) { + return false; + } + } + return HasSafePredicates(*op.children[0]); +} + +template +static LogicalPlanSQLExportResult ExportContextExpressions(LogicalOperator &op, LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path, + vector fields, + EXPORTER exporter) { + D_ASSERT(op.children.size() == 1); + auto child = context.ExportChild(*op.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (child.HasError()) { + return LogicalPlanSQLExportResult::Failure(child); + } + LogicalPlanSQLExportHelpers::PropagateSemanticTypes(fields, {child.GetValue()}); + auto plain = LogicalPlanSQLExportHelpers::PlainScope(*child.GetValue().relation.query); + auto select = make_uniq(); + for (idx_t i = 0; i < child.GetValue().relation.fields.size(); i++) { + select->select_list.push_back(LogicalPlanSQLExportHelpers::ChildColumn(child.GetValue(), i, plain)); + } + auto expression_context = + LogicalPlanSQLExportHelpers::CreateBindingContext(context.GetClientContext(), {child.GetValue()}, {plain}); + for (idx_t i = 0; i < op.expressions.size(); i++) { + auto expression_path = LogicalPlanSQLExportHelpers::PlanExpressionPath(path, i); + auto expression = exporter(op.expressions[i]->Cast(), expression_context, expression_path); + if (expression.HasError()) { + return LogicalPlanSQLExportResult::Failure(expression); + } + expression.GetValue()->SetAlias(LogicalPlanSQLExportHelpers::FieldIdentifier(select->select_list.size())); + select->select_list.push_back(std::move(expression.GetValue())); + } + LogicalPlanSQLExportHelpers::SetChildScope(*select, std::move(child.GetValue()), plain); + return LogicalPlanSQLExportResult::Success({std::move(select), std::move(fields)}); +} + +LogicalPlanSQLExportResult LogicalFilter::ToSQL(LogicalPlanSQLExportContext &export_context, + const LogicalPlanVerificationPath &path) { + auto &filter = *this; + D_ASSERT(filter.children.size() == 1); +#ifdef D_ASSERT_IS_ENABLED + for (auto &expression : filter.expressions) { + D_ASSERT(expression && expression->GetReturnType() == LogicalType::BOOLEAN); + } +#endif + auto fields = LogicalPlanSQLExportHelpers::CreateFields(filter, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + auto child = export_context.ExportChild(*filter.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (child.HasError()) { + return LogicalPlanSQLExportResult::Failure(child); + } + LogicalPlanSQLExportHelpers::PropagateSemanticTypes(fields.GetValue(), {child.GetValue()}); + auto plain = LogicalPlanSQLExportHelpers::PlainScope(*child.GetValue().relation.query); + if (plain && plain->where_clause && !HasSafePredicates(*filter.children[0])) { + plain = nullptr; + } + if (plain && plain->where_clause) { + for (auto &predicate : filter.expressions) { + if (predicate->IsVolatile() || predicate->CanThrow()) { + plain = nullptr; + break; + } + } + } + vector> child_references {child.GetValue()}; + auto expression_context = + LogicalPlanSQLExportHelpers::CreateBindingContext(export_context.GetClientContext(), child_references, {plain}); + optional_ptr composed; + auto expressions = LogicalPlanSQLExportHelpers::CollectExpressions(filter); + vector> predicates; + { + BoundExpressionSQLExportState composition(expression_context); + composed = plain ? nullptr : LogicalPlanSQLExportHelpers::ComposeInput(filter, child.GetValue(), composition); + for (idx_t expression_index = 0; expression_index < filter.expressions.size(); expression_index++) { + auto predicate = export_context.ExportExpression(filter, expressions, expression_index, expression_context, + path, composed ? &composition : nullptr); + if (predicate.HasError()) { + return LogicalPlanSQLExportResult::Failure(predicate); + } + predicates.push_back(std::move(predicate.GetValue())); + } + } + auto select = make_uniq(); + for (idx_t field_index = 0; field_index < fields.GetValue().size(); field_index++) { + auto child_field_index = + filter.projection_map.empty() ? field_index : filter.projection_map[field_index].GetIndexUnsafe(); + auto expression = + LogicalPlanSQLExportHelpers::ChildColumn(child.GetValue(), child_field_index, composed ? composed : plain); + expression->SetAlias(LogicalPlanSQLExportHelpers::FieldIdentifier(field_index)); + select->select_list.push_back(std::move(expression)); + } + select->where_clause = SQLExportHelpers::Conjoin(std::move(predicates)); + LogicalPlanSQLExportHelpers::SetChildScope(*select, std::move(child.GetValue()), composed ? composed : plain); + LogicalPlanSQLExportRelation relation {std::move(select), std::move(fields.GetValue())}; + return LogicalPlanSQLExportResult::Success(std::move(relation)); +} + +LogicalPlanSQLExportResult LogicalProjection::ToSQL(LogicalPlanSQLExportContext &export_context, + const LogicalPlanVerificationPath &path) { + auto &projection = *this; + D_ASSERT(projection.children.size() == 1 && !projection.expressions.empty()); + auto fields = LogicalPlanSQLExportHelpers::CreateFields(projection, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + D_ASSERT(projection.expressions.size() == fields.GetValue().size()); + bool fromless = projection.children[0]->type == LogicalOperatorType::LOGICAL_DUMMY_SCAN; + auto child = + export_context.ExportChild(*projection.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (child.HasError()) { + return LogicalPlanSQLExportResult::Failure(child); + } + if (LogicalPlanSQLExportHelpers::IsIdentityProjection(projection, child.GetValue().relation.fields)) { + return LogicalPlanSQLExportResult::Success( + {std::move(child.GetValue().relation.query), std::move(fields.GetValue())}); + } + auto plain = LogicalPlanSQLExportHelpers::PlainScope(*child.GetValue().relation.query); + vector> child_references {child.GetValue()}; + auto expression_context = + LogicalPlanSQLExportHelpers::CreateBindingContext(export_context.GetClientContext(), child_references, {plain}); + auto expressions = LogicalPlanSQLExportHelpers::CollectExpressions(projection); + auto select = make_uniq(); + for (idx_t expression_index = 0; expression_index < projection.expressions.size(); expression_index++) { + fromless = fromless && projection.expressions[expression_index]->IsScalar(); + ApplySemanticType(fields.GetValue()[expression_index], *projection.expressions[expression_index], + expression_context); + auto expression = + export_context.ExportExpression(projection, expressions, expression_index, expression_context, path); + if (expression.HasError()) { + return LogicalPlanSQLExportResult::Failure(expression); + } + expression.GetValue()->SetAlias(LogicalPlanSQLExportHelpers::FieldIdentifier(expression_index)); + select->select_list.push_back(std::move(expression.GetValue())); + } + if (!plain && !fromless && + LogicalPlanSQLExportHelpers::ComposeProjection(projection, child.GetValue(), *select, expression_context, + path)) { + return LogicalPlanSQLExportResult::Success({std::move(select), std::move(fields.GetValue())}); + } + if (fromless) { + select->from_table = make_uniq(); + } else { + LogicalPlanSQLExportHelpers::SetChildScope(*select, std::move(child.GetValue()), plain); + } + LogicalPlanSQLExportRelation relation {std::move(select), std::move(fields.GetValue())}; + return LogicalPlanSQLExportResult::Success(std::move(relation)); +} + +static LogicalPlanVerificationResult> +ExportOrderModifier(LogicalOperator &op, LogicalPlanSQLExportContext &context, const LogicalPlanVerificationPath &path, + const vector &orders, const BoundExpressionSQLExportContext &expression_context, + idx_t expression_ordinal) { + using Result = LogicalPlanVerificationResult>; + auto expressions = LogicalPlanSQLExportHelpers::CollectExpressions(op); + auto modifier = make_uniq(); + for (auto &order : orders) { + auto expression = context.ExportExpression(op, expressions, expression_ordinal++, expression_context, path); + if (expression.HasError()) { + return Result::Failure(expression); + } + if (SQLExportHelpers::IsConstantOrderKey(*order.expression, *expression.GetValue())) { + continue; + } + modifier->orders.emplace_back( + order.type, order.null_order, + SQLExportHelpers::OrderExpression(SemanticExpressionType(*order.expression, expression_context), + std::move(expression.GetValue()))); + } + return Result::Success(std::move(modifier)); +} + +static LogicalPlanSQLExportResult ExportOrderedRelation(LogicalOperator &op, LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path, + const vector &orders) { + D_ASSERT(op.children.size() == 1); + auto fields = LogicalPlanSQLExportHelpers::CreateFields(op, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + auto child = context.ExportChild(*op.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (child.HasError()) { + return LogicalPlanSQLExportResult::Failure(child); + } + LogicalPlanSQLExportHelpers::PropagateSemanticTypes(fields.GetValue(), {child.GetValue()}); + auto plain = LogicalPlanSQLExportHelpers::PlainScope(*child.GetValue().relation.query); + auto expression_context = + LogicalPlanSQLExportHelpers::CreateBindingContext(context.GetClientContext(), {child.GetValue()}, {plain}); + auto select = context.ForwardFields(child.GetValue(), fields.GetValue(), plain); + auto modifier = ExportOrderModifier(op, context, path, orders, expression_context, 0); + if (modifier.HasError()) { + return LogicalPlanSQLExportResult::Failure(modifier); + } + if (modifier.GetValue()->orders.empty()) { + LogicalPlanSQLExportHelpers::SetChildScope(*select, std::move(child.GetValue()), plain); + return LogicalPlanSQLExportResult::Success({std::move(select), std::move(fields.GetValue())}); + } + select->modifiers.push_back(std::move(modifier.GetValue())); + if (!plain && LogicalPlanSQLExportHelpers::ComposeOrder(op, child.GetValue(), *select, expression_context, path)) { + return LogicalPlanSQLExportResult::Success({std::move(select), std::move(fields.GetValue())}); + } + LogicalPlanSQLExportHelpers::SetChildScope(*select, std::move(child.GetValue()), plain); + return LogicalPlanSQLExportResult::Success({std::move(select), std::move(fields.GetValue())}); +} + +LogicalPlanSQLExportResult LogicalSample::ToSQL(LogicalPlanSQLExportContext &export_context, + const LogicalPlanVerificationPath &path) { + auto &sample = *this; + D_ASSERT(sample.children.size() == 1 && sample.sample_options); + auto &sampling = *sample.sample_options; + if (sampling.seed.IsValid() != sampling.repeatable) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "sample_repeatability", "SQL sampling seeds imply repeatable sampling")}); + } + if (sampling.seed.IsValid() && sampling.seed.GetIndex() > idx_t(NumericLimits::Maximum())) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "sample_seed", "The sampling seed has no SQL spelling")}); + } + auto fields = LogicalPlanSQLExportHelpers::CreateFields(sample, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + auto child = export_context.ExportChild(*sample.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (child.HasError()) { + return LogicalPlanSQLExportResult::Failure(child); + } + auto select = export_context.ForwardFields(child.GetValue(), fields.GetValue()); + select->sample = sampling.Copy(); + select->from_table = LogicalPlanSQLExportHelpers::CreateSubquery(std::move(child.GetValue())); + return LogicalPlanSQLExportResult::Success({std::move(select), std::move(fields.GetValue())}); +} + +LogicalPlanSQLExportResult LogicalSetOperation::ToSQL(LogicalPlanSQLExportContext &export_context, + const LogicalPlanVerificationPath &path) { + auto &op = *this; + D_ASSERT(!op.children.empty()); + D_ASSERT(op.type == LogicalOperatorType::LOGICAL_UNION || op.children.size() == 2); + auto fields = LogicalPlanSQLExportHelpers::CreateFields(op, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + auto query = make_uniq(); + query->setop_all = op.setop_all; + query->setop_type = op.type == LogicalOperatorType::LOGICAL_UNION ? SetOperationType::UNION + : op.type == LogicalOperatorType::LOGICAL_EXCEPT ? SetOperationType::EXCEPT + : SetOperationType::INTERSECT; + for (idx_t i = 0; i < op.children.size(); i++) { + auto child = export_context.Export(*op.children[i], LogicalPlanSQLExportHelpers::PlanChildPath(path, i)); + if (child.HasError()) { + return LogicalPlanSQLExportResult::Failure(child); + } + D_ASSERT(child.GetValue().fields.size() == fields.GetValue().size()); + query->children.push_back(std::move(child.GetValue().query)); + } + if (op.children.size() == 1) { + // Retain the UNION boundary when optimization removed every other arm. + LogicalEmptyResult empty(op.types, op.GetColumnBindings()); + empty.ResolveOperatorTypes(); + auto exported = export_context.Export(empty, path); + if (exported.HasError()) { + return exported; + } + query->children.push_back(std::move(exported.GetValue().query)); + } + return LogicalPlanSQLExportResult::Success({std::move(query), std::move(fields.GetValue())}); +} + +LogicalPlanSQLExportResult LogicalAggregate::ToSQL(LogicalPlanSQLExportContext &export_context, + const LogicalPlanVerificationPath &path) { + auto &aggregate = *this; + D_ASSERT(aggregate.children.size() == 1); +#ifdef D_ASSERT_IS_ENABLED + for (auto &expression : aggregate.expressions) { + D_ASSERT(expression && expression->GetExpressionClass() == ExpressionClass::BOUND_AGGREGATE); + } +#endif + auto fields = LogicalPlanSQLExportHelpers::CreateFields(aggregate, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + D_ASSERT(aggregate.groups.size() + aggregate.expressions.size() + aggregate.grouping_functions.size() == + fields.GetValue().size()); + auto child = + export_context.ExportChild(*aggregate.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (child.HasError()) { + return LogicalPlanSQLExportResult::Failure(child); + } + auto plain = LogicalPlanSQLExportHelpers::PlainScope(*child.GetValue().relation.query); + vector> child_references {child.GetValue()}; + auto expression_context = + LogicalPlanSQLExportHelpers::CreateBindingContext(export_context.GetClientContext(), child_references, {plain}); + auto expressions = LogicalPlanSQLExportHelpers::CollectExpressions(aggregate); + for (idx_t group_index = 0; group_index < aggregate.groups.size(); group_index++) { + ApplySemanticType(fields.GetValue()[group_index], *aggregate.groups[group_index], expression_context); + } + auto input_field_count = child.GetValue().relation.fields.size(); + const bool stage_groups = !LogicalPlanSQLExportHelpers::HasSimpleGroups(aggregate); + if (stage_groups && !aggregate.groups.empty()) { + // Equal group expressions can still occupy distinct grouping-set positions. + auto input = make_uniq(); + auto input_fields = child.GetValue().relation.fields; + for (idx_t i = 0; i < input_field_count; i++) { + auto expression = LogicalPlanSQLExportHelpers::ChildColumn(child.GetValue(), i, plain); + expression->SetAlias(LogicalPlanSQLExportHelpers::FieldIdentifier(i)); + input->select_list.push_back(std::move(expression)); + } + for (idx_t i = 0; i < aggregate.groups.size(); i++) { + auto expression = export_context.ExportExpression(aggregate, expressions, i, expression_context, path); + if (expression.HasError()) { + return LogicalPlanSQLExportResult::Failure(expression); + } + expression.GetValue()->SetAlias(LogicalPlanSQLExportHelpers::FieldIdentifier(input_fields.size())); + input->select_list.push_back(std::move(expression.GetValue())); + input_fields.push_back(fields.GetValue()[i]); + } + LogicalPlanSQLExportHelpers::SetChildScope(*input, std::move(child.GetValue()), plain); + child.GetValue() = {{std::move(input), std::move(input_fields)}, export_context.NextRelationAlias()}; + plain = nullptr; + expression_context = + LogicalPlanSQLExportHelpers::CreateBindingContext(export_context.GetClientContext(), {child.GetValue()}); + } + optional_ptr composed; + auto select = make_uniq(); + { + BoundExpressionSQLExportState composition(expression_context); + composed = !plain && !stage_groups + ? LogicalPlanSQLExportHelpers::ComposeInput(aggregate, child.GetValue(), composition) + : nullptr; + for (idx_t expression_index = 0; expression_index < expressions.size(); expression_index++) { + unique_ptr result; + if (expression_index < aggregate.groups.size() && stage_groups) { + result = + LogicalPlanSQLExportHelpers::ChildColumn(child.GetValue(), input_field_count + expression_index); + } else { + auto expression = + export_context.ExportExpression(aggregate, expressions, expression_index, expression_context, path, + composed ? &composition : nullptr); + if (expression.HasError()) { + return LogicalPlanSQLExportResult::Failure(expression); + } + result = std::move(expression.GetValue()); + } + if (expression_index < aggregate.groups.size()) { + select->groups.group_expressions.push_back(result->Copy()); + } + result->SetAlias(LogicalPlanSQLExportHelpers::FieldIdentifier(expression_index)); + select->select_list.push_back(std::move(result)); + } + } + select->groups.grouping_sets = aggregate.grouping_sets; + if (select->groups.grouping_sets.empty() && !aggregate.groups.empty()) { + GroupingSet all_groups; + for (idx_t i = 0; i < aggregate.groups.size(); i++) { + all_groups.insert(ProjectionIndex(i)); + } + select->groups.grouping_sets.push_back(std::move(all_groups)); + } + for (auto &grouping : aggregate.grouping_functions) { + vector> arguments; + for (auto index : grouping) { + arguments.push_back(select->groups.group_expressions[index]->Copy()); + } + auto expression = make_uniq(ExpressionType::GROUPING_FUNCTION, std::move(arguments)); + expression->SetAlias(LogicalPlanSQLExportHelpers::FieldIdentifier(select->select_list.size())); + select->select_list.push_back(std::move(expression)); + } + LogicalPlanSQLExportHelpers::SetChildScope(*select, std::move(child.GetValue()), composed ? composed : plain); + LogicalPlanSQLExportRelation relation {std::move(select), std::move(fields.GetValue())}; + return LogicalPlanSQLExportResult::Success(std::move(relation)); +} + +LogicalPlanSQLExportResult LogicalDistinct::ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + auto &op = *this; + D_ASSERT(op.children.size() == 1); + auto fields = LogicalPlanSQLExportHelpers::CreateFields(op, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + auto child = context.ExportChild(*op.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (child.HasError()) { + return LogicalPlanSQLExportResult::Failure(child); + } + LogicalPlanSQLExportHelpers::PropagateSemanticTypes(fields.GetValue(), {child.GetValue()}); + auto expression_context = + LogicalPlanSQLExportHelpers::CreateBindingContext(context.GetClientContext(), {child.GetValue()}); + auto select = context.ForwardFields(child.GetValue(), fields.GetValue()); + auto expressions = LogicalPlanSQLExportHelpers::CollectExpressions(op); + auto modifier = make_uniq(); + idx_t expression_ordinal = 0; + // DISTINCT targets include optimizer-pruned full-row DISTINCT. + for (idx_t i = 0; i < distinct_targets.size(); i++) { + auto expression = context.ExportExpression(op, expressions, expression_ordinal++, expression_context, path); + if (expression.HasError()) { + return LogicalPlanSQLExportResult::Failure(expression); + } + modifier->distinct_on_targets.push_back(std::move(expression.GetValue())); + } + select->modifiers.push_back(std::move(modifier)); + if (order_by) { + auto order = ExportOrderModifier(op, context, path, order_by->orders, expression_context, expression_ordinal); + if (order.HasError()) { + return LogicalPlanSQLExportResult::Failure(order); + } + if (!order.GetValue()->orders.empty()) { + select->modifiers.push_back(std::move(order.GetValue())); + } + } + select->from_table = LogicalPlanSQLExportHelpers::CreateSubquery(std::move(child.GetValue())); + return LogicalPlanSQLExportResult::Success({std::move(select), std::move(fields.GetValue())}); +} + +LogicalPlanSQLExportResult LogicalOrder::ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + return ExportOrderedRelation(*this, context, path, orders); +} + +LogicalPlanSQLExportResult LogicalTopN::ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + auto result = ExportOrderedRelation(*this, context, path, orders); + if (result.HasError()) { + return result; + } + auto modifier = make_uniq(); + modifier->limit = ConstantExpression::FromValue(Value::BIGINT(NumericCast(limit))); + auto original_offset = unpruned_offset.IsValid() ? unpruned_offset.GetIndex() : offset; + modifier->offset = ConstantExpression::FromValue(Value::BIGINT(NumericCast(original_offset))); + result.GetValue().query->modifiers.push_back(std::move(modifier)); + return result; +} + +LogicalPlanSQLExportResult LogicalWindow::ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + auto &op = *this; + D_ASSERT(children.size() == 1); + auto fields = LogicalPlanSQLExportHelpers::CreateFields(op, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + // A replaced input exports as a named source, so its ordinality fusion does not apply. + if (op.children[0]->type == LogicalOperatorType::LOGICAL_GET && !context.FindSource(*op.children[0])) { + auto &get = op.children[0]->Cast(); + if (get.source_ordinality == OrdinalityType::WITH_ORDINALITY && !get.ordinality_idx.IsValid()) { + bool supported = + op.expressions.size() == 1 && !get.table_filters.HasFilters() && !get.extra_info.sample_options; + if (supported) { + auto &window = op.expressions[0]->Cast(); + supported = window.GetExpressionType() == ExpressionType::WINDOW_ROW_NUMBER && + window.Partitions().empty() && window.OrderBy().empty() && window.GetChildren().empty(); + } + if (!supported) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "ordinality_window", "The source ordinality cannot be reconstructed through this window")}); + } + return get.ExportSQLSource(context, LogicalPlanSQLExportHelpers::PlanChildPath(path, 0), + &fields.GetValue().back()); + } + } + return ExportContextExpressions(*this, context, path, std::move(fields.GetValue()), + BoundExpressionSQLExporter::ExportWindowAtPath); +} + +LogicalPlanSQLExportResult LogicalUnnest::ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + auto fields = LogicalPlanSQLExportHelpers::CreateFields(*this, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + return ExportContextExpressions(*this, context, path, std::move(fields.GetValue()), + BoundExpressionSQLExporter::ExportUnnestAtPath); +} + +} // namespace duckdb diff --git a/src/duckdb/src/planner/sql_export/sql_export_scope.cpp b/src/duckdb/src/planner/sql_export/sql_export_scope.cpp new file mode 100644 index 000000000..e85e55d95 --- /dev/null +++ b/src/duckdb/src/planner/sql_export/sql_export_scope.cpp @@ -0,0 +1,270 @@ +#include "duckdb/planner/sql_export/logical_plan_sql_exporter_internal.hpp" +#include "duckdb/planner/logical_plan_sql_exporter.hpp" +#include "duckdb/parser/expression/columnref_expression.hpp" +#include "duckdb/parser/query_node/select_node.hpp" +#include "duckdb/parser/statement/select_statement.hpp" +#include "duckdb/parser/tableref/joinref.hpp" +#include "duckdb/parser/tableref/subqueryref.hpp" +#include "duckdb/planner/bound_expression_sql_exporter.hpp" +#include "duckdb/planner/column_binding_map.hpp" +#include "duckdb/planner/operator/logical_get.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/expression/bound_constant_expression.hpp" +#include "duckdb/planner/logical_operator.hpp" +#include "duckdb/planner/logical_operator_visitor.hpp" +#include "duckdb/planner/sql_export_helpers.hpp" +#include "duckdb/planner/operator/logical_projection.hpp" + +namespace duckdb { + +LogicalPlanVerificationPath LogicalPlanSQLExportHelpers::PlanChildPath(const LogicalPlanVerificationPath &path, + idx_t ordinal) { + return SQLExportHelpers::ChildPath(path, ordinal, LogicalPlanVerificationPathComponentType::OPERATOR_CHILD); +} + +LogicalPlanVerificationPath LogicalPlanSQLExportHelpers::PlanExpressionPath(const LogicalPlanVerificationPath &path, + idx_t ordinal) { + return SQLExportHelpers::ChildPath(path, ordinal, LogicalPlanVerificationPathComponentType::OPERATOR_EXPRESSION); +} + +LogicalPlanVerificationResult> +LogicalPlanSQLExportHelpers::ExportTypedNull(const LogicalType &type, const LogicalPlanVerificationPath &path) { + auto result = BoundExpressionSQLExporter::Export(BoundConstantExpression(Value(type)), {}); + if (!result.HasError()) { + return result; + } + auto issues = result.GetIssues(); + for (auto &issue : issues) { + issue.path = path; + issue.phase = LogicalPlanVerificationPhase::PLAN_EXPORT; + } + return LogicalPlanVerificationResult>::Failure(std::move(issues)); +} + +LogicalPlanVerificationIssue +LogicalPlanSQLExportHelpers::PlanUnsupportedFeature(const LogicalPlanVerificationPath &path, string feature, + string message) { + return SQLExportHelpers::MakeIssue( + LogicalPlanVerificationIssueCode::UNSUPPORTED_EXPORT_FEATURE, LogicalPlanVerificationPhase::PLAN_EXPORT, path, + LogicalPlanVerificationConstructIdentity::ExportFeature(std::move(feature)), std::move(message)); +} + +LogicalPlanVerificationFunctionIdentity LogicalPlanSQLExportHelpers::LogicalSourceIdentity() { + LogicalPlanVerificationFunctionIdentity source; + source.name = "logical_source"; + source.return_type = LogicalType::TABLE; + return source; +} + +LogicalPlanVerificationFunctionIdentity LogicalPlanSQLExportHelpers::LogicalSourceIdentity(const LogicalGet &get) { + LogicalPlanVerificationFunctionIdentity source; + source.catalog = get.function.GetCatalogName().GetIdentifierName(); + source.schema = get.function.GetSchemaName().GetIdentifierName(); + source.name = get.function.GetName().GetIdentifierName(); + for (auto ¶meter : get.parameters) { + source.arguments.push_back(parameter.type()); + } + for (auto ¶meter : get.named_parameters) { + source.arguments.push_back(parameter.second.type()); + } + source.return_type = LogicalType::TABLE; + return source; +} + +LogicalPlanVerificationIssue +LogicalPlanSQLExportHelpers::UnsupportedSource(const LogicalPlanVerificationPath &path, + LogicalPlanVerificationFunctionIdentity source, string guard) { + auto issue = SQLExportHelpers::MakeIssue( + LogicalPlanVerificationIssueCode::UNSUPPORTED_SOURCE, LogicalPlanVerificationPhase::PLAN_EXPORT, path, + LogicalPlanVerificationConstructIdentity::SourceFunction(std::move(source)), + "The logical source does not expose structural SQL export semantics"); + issue.facts.emplace_back("guard", Value(std::move(guard))); + return issue; +} + +Identifier LogicalPlanSQLExportHelpers::FieldIdentifier(idx_t ordinal) { + return Identifier("c" + to_string(ordinal)); +} + +LogicalPlanSQLFieldResult LogicalPlanSQLExportHelpers::CreateFields(LogicalOperator &op, + const LogicalPlanVerificationPath &path) { + auto bindings = op.GetColumnBindings(); + D_ASSERT(bindings.size() == op.types.size()); + vector fields; + for (idx_t i = 0; i < bindings.size(); i++) { + D_ASSERT(bindings[i].table_index.IsValid() && bindings[i].column_index.IsValid()); + if (!SQLExportHelpers::IsSQLValueType(op.types[i])) { + auto issue = LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "output_type", "Output type cannot be represented in SQL"); + issue.facts.emplace_back("column_index", Value::UBIGINT(i)); + issue.facts.emplace_back("logical_type", Value(op.types[i].ToString())); + issue.facts.emplace_back("varchar_collations", + Value(SQLExportHelpers::TypeCollationSignature(op.types[i]))); + return LogicalPlanSQLFieldResult::Failure({std::move(issue)}); + } + fields.push_back({bindings[i], op.types[i]}); + } + return LogicalPlanSQLFieldResult::Success(std::move(fields)); +} + +BoundExpressionSQLExportContext +LogicalPlanSQLExportHelpers::CreateBindingContext(ClientContext &context, + const vector> &children, + const vector> &plain_scopes) { + D_ASSERT(plain_scopes.empty() || plain_scopes.size() == children.size()); + column_binding_map_t entries; + for (idx_t child_index = 0; child_index < children.size(); child_index++) { + auto &child = children[child_index].get(); + auto plain = plain_scopes.empty() ? nullptr : plain_scopes[child_index]; + for (idx_t i = 0; i < child.relation.fields.size(); i++) { + auto &field = child.relation.fields[i]; + auto names = + plain ? plain->select_list[i]->Cast().ColumnNames() + : vector {child.relation_alias, LogicalPlanSQLExportHelpers::FieldIdentifier(i)}; + entries.emplace(field.source_binding, + ResolvedSQLColumnReference {std::move(names), field.type, field.optimizer_type}); + } + } + BoundExpressionSQLExportContext result; + result.client_context = &context; + result.discard_optimizer_metadata = true; + result.resolve_binding = + [entries = std::move(entries)](const ColumnBinding &binding) -> optional { + auto entry = entries.find(binding); + if (entry != entries.end()) { + return entry->second; + } + return {}; + }; + return result; +} + +void LogicalPlanSQLExportHelpers::PropagateSemanticTypes( + vector &fields, const vector> &children) { + for (auto &field : fields) { + for (auto &child : children) { + for (auto &child_field : child.get().relation.fields) { + if (field.source_binding != child_field.source_binding || field.type == child_field.type) { + continue; + } + field.optimizer_type = field.type; + field.type = child_field.type; + } + } + } +} + +unique_ptr LogicalPlanSQLExportHelpers::CreateSubquery(LogicalPlanSQLExportedChild child) { + auto statement = make_uniq(); + statement->node = std::move(child.relation.query); + auto result = make_uniq(std::move(statement), std::move(child.relation_alias)); + for (idx_t i = 0; i < child.relation.fields.size(); i++) { + result->column_name_alias.push_back(LogicalPlanSQLExportHelpers::FieldIdentifier(i)); + } + return std::move(result); +} + +unique_ptr LogicalPlanSQLExportHelpers::ChildColumn(const LogicalPlanSQLExportedChild &child, + idx_t field_index, + optional_ptr plain) { + D_ASSERT(field_index < child.relation.fields.size()); + if (plain) { + return plain->select_list[field_index]->Copy(); + } + return make_uniq(LogicalPlanSQLExportHelpers::FieldIdentifier(field_index), + child.relation_alias); +} + +vector> LogicalPlanSQLExportHelpers::CollectExpressions(const LogicalOperator &op) { + vector> expressions; + LogicalOperatorVisitor::EnumerateExpressions(op, [&](const unique_ptr *expression) { + D_ASSERT(expression && *expression); + expressions.push_back(reference(**expression)); + }); + return expressions; +} + +bool LogicalPlanSQLExportHelpers::HasEffectfulExpressions(const LogicalOperator &op) { + for (auto &expression : LogicalPlanSQLExportHelpers::CollectExpressions(op)) { + if (expression.get().IsVolatile() || expression.get().CanThrow()) { + return true; + } + } + return false; +} + +bool LogicalPlanSQLExportHelpers::CollectScopeAliases(const TableRef &table, identifier_set_t &aliases) { + if (table.sample) { + return false; + } + if (!table.alias.empty()) { + return aliases.insert(table.alias).second; + } + if (table.type != TableReferenceType::JOIN) { + return false; + } + auto &join = table.Cast(); + return join.left && join.right && LogicalPlanSQLExportHelpers::CollectScopeAliases(*join.left, aliases) && + LogicalPlanSQLExportHelpers::CollectScopeAliases(*join.right, aliases); +} + +optional_ptr LogicalPlanSQLExportHelpers::PlainScope(const QueryNode &query) { + if (query.type != QueryNodeType::SELECT_NODE || !query.modifiers.empty() || !query.cte_map.map.empty()) { + return nullptr; + } + auto &select = query.Cast(); + if (!select.from_table || select.sample || select.from_table->sample) { + return nullptr; + } + const bool has_groups = !select.groups.group_expressions.empty() || !select.groups.grouping_sets.empty(); + const bool has_aggregate_handling = select.aggregate_handling != AggregateHandling::STANDARD_HANDLING; + if (has_groups || select.having || select.qualify || has_aggregate_handling) { + return nullptr; + } + identifier_set_t aliases; + if (!LogicalPlanSQLExportHelpers::CollectScopeAliases(*select.from_table, aliases)) { + return nullptr; + } + for (auto &expression : select.select_list) { + if (expression->GetExpressionClass() != ExpressionClass::COLUMN_REF) { + return nullptr; + } + auto &names = expression->Cast().ColumnNames(); + if (names.size() != 2 || !aliases.count(names[0])) { + return nullptr; + } + } + return select; +} + +void LogicalPlanSQLExportHelpers::SetChildScope(SelectNode &select, LogicalPlanSQLExportedChild child, + optional_ptr plain) { + if (!plain) { + select.from_table = LogicalPlanSQLExportHelpers::CreateSubquery(std::move(child)); + return; + } + auto &source = child.relation.query->Cast(); + select.from_table = std::move(source.from_table); + select.where_clause = SQLExportHelpers::Conjoin(std::move(source.where_clause), std::move(select.where_clause)); +} + +bool LogicalPlanSQLExportHelpers::IsIdentityProjection(const LogicalProjection &projection, + const vector &fields) { + if (projection.expressions.size() != fields.size()) { + return false; + } + for (idx_t i = 0; i < fields.size(); i++) { + auto &expression = *projection.expressions[i]; + if (expression.GetExpressionClass() != ExpressionClass::BOUND_COLUMN_REF) { + return false; + } + auto &column = expression.Cast(); + if (column.Depth() != 0 || column.Binding() != fields[i].source_binding || + !column.GetReturnType().EqualsIncludingCollation(fields[i].type)) { + return false; + } + } + return true; +} + +} // namespace duckdb diff --git a/src/duckdb/src/planner/sql_export/sql_export_sources.cpp b/src/duckdb/src/planner/sql_export/sql_export_sources.cpp new file mode 100644 index 000000000..6a92ee17c --- /dev/null +++ b/src/duckdb/src/planner/sql_export/sql_export_sources.cpp @@ -0,0 +1,260 @@ +#include "duckdb/planner/operator/logical_extension_operator.hpp" +#include "duckdb/planner/operator/logical_get.hpp" +#include "duckdb/planner/sql_export/logical_plan_sql_exporter_internal.hpp" +#include "duckdb/planner/logical_plan_sql_exporter.hpp" +#include "duckdb/common/limits.hpp" +#include "duckdb/parser/expression/conjunction_expression.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/parser/tableref/basetableref.hpp" +#include "duckdb/parser/tableref/at_clause.hpp" +#include "duckdb/parser/query_node/select_node.hpp" +#include "duckdb/planner/bound_expression_sql_exporter.hpp" +#include "duckdb/planner/filter/expression_filter.hpp" +#include "duckdb/planner/operator/logical_get.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/operator/logical_limit.hpp" +#include "duckdb/planner/operator/logical_top_n.hpp" +#include "duckdb/planner/operator/logical_extension_operator.hpp" +#include "duckdb/planner/sql_export_helpers.hpp" +#include "duckdb/planner/operator/logical_secure_view.hpp" + +namespace duckdb { + +static LogicalPlanVerificationIssue ExtensionIssue(LogicalPlanVerificationIssueCode code, + const LogicalPlanVerificationPath &path, const string &identifier, + string message) { + return SQLExportHelpers::MakeIssue(code, LogicalPlanVerificationPhase::PLAN_EXPORT, path, + LogicalPlanVerificationConstructIdentity::Extension(identifier), + std::move(message)); +} + +LogicalPlanSQLExportResult LogicalGet::ExportSQLSource(LogicalPlanSQLExportContext &export_context, + const LogicalPlanVerificationPath &path, + optional_ptr ordinality) { + auto &get = *this; + D_ASSERT(get.children.size() <= 1); + if (get.has_pushed_projection) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::UnsupportedSource( + path, LogicalPlanSQLExportHelpers::LogicalSourceIdentity(get), "pushed_projection")}); + } + auto fields = LogicalPlanSQLExportHelpers::CreateFields(get, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + if ((!get.extra_info.file_filters.empty() || get.extra_info.total_files.IsValid()) && + !get.extra_info.file_filter_expressions) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "file_filter_residual", "The source does not retain the SQL predicate used for file pruning")}); + } + if (get.row_group_order_options && + (get.row_group_order_options->row_group_offset || get.row_group_order_options->leading_null_group_offset)) { + bool has_unpruned_offset = false; + for (idx_t i = export_context.Ancestors().size(); i > 0; i--) { + auto &ancestor = export_context.Ancestors()[i - 1].get(); + if (ancestor.type == LogicalOperatorType::LOGICAL_LIMIT) { + has_unpruned_offset = ancestor.Cast().unpruned_offset.IsValid(); + break; + } else if (ancestor.type == LogicalOperatorType::LOGICAL_TOP_N) { + has_unpruned_offset = ancestor.Cast().unpruned_offset.IsValid(); + break; + } + } + if (!has_unpruned_offset) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "pruned_offset", "The row group pruning does not retain its original SQL offset")}); + } + } + + vector scan_fields; + for (idx_t i = 0; i < get.GetColumnIds().size(); i++) { + auto &type = get.GetColumnType(get.GetColumnIds()[i]); + if (!SQLExportHelpers::IsSQLValueType(type)) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "scan_type", "Scan type cannot be represented in SQL")}); + } + scan_fields.push_back({ColumnBinding(get.table_index, ProjectionIndex(i)), type}); + } + unique_ptr input; + if (!get.children.empty()) { + auto child = export_context.ExportChild(*get.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (child.HasError()) { + return LogicalPlanSQLExportResult::Failure(child); + } + for (auto index : get.projected_input) { + scan_fields.push_back(child.GetValue().relation.fields[index]); + } + input = LogicalPlanSQLExportHelpers::CreateSubquery(std::move(child.GetValue())); + } + if (ordinality) { + fields.GetValue().push_back(*ordinality); + scan_fields.push_back(*ordinality); + } + auto relation_alias = export_context.NextRelationAlias(); + auto source_sql = LogicalPlanSQLExportHelpers::ReconstructSQLSource( + export_context.GetClientContext(), get, std::move(input), relation_alias, bool(ordinality)); + if (!source_sql.query) { + auto guard = source_sql.unsupported_reason.empty() ? "to_sql_callback_declined" : source_sql.unsupported_reason; + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::UnsupportedSource( + path, LogicalPlanSQLExportHelpers::LogicalSourceIdentity(get), std::move(guard))}); + } + auto query = std::move(source_sql.query); + if (get.extra_info.sample_options) { + auto sampling = get.extra_info.sample_options->Copy(); + if (!sampling->repeatable && sampling->seed.IsValid()) { + sampling->seed = optional_idx::Invalid(); + } + if (sampling->repeatable && !sampling->seed.IsValid()) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "sample_repeatability", "SQL sampling seeds imply repeatable sampling")}); + } + if (sampling->seed.IsValid() && sampling->seed.GetIndex() > idx_t(NumericLimits::Maximum())) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "sample_seed", "The sampling seed has no SQL spelling")}); + } + sampling->sample_rate = -1.0; + LogicalPlanSQLExportedChild unsampled {{std::move(query), scan_fields}, export_context.NextRelationAlias()}; + auto sampled = export_context.ForwardFields(unsampled, scan_fields); + sampled->sample = std::move(sampling); + sampled->from_table = LogicalPlanSQLExportHelpers::CreateSubquery(std::move(unsampled)); + query = std::move(sampled); + } + LogicalPlanSQLExportedChild source {{std::move(query), std::move(scan_fields)}, std::move(relation_alias)}; + auto plain = LogicalPlanSQLExportHelpers::PlainScope(*source.relation.query); + if (plain && (plain->where_clause || plain->select_list.size() != source.relation.fields.size())) { + plain = nullptr; + } + auto binding_context = + LogicalPlanSQLExportHelpers::CreateBindingContext(export_context.GetClientContext(), {source}, {plain}); + auto select = export_context.ForwardFields(source, fields.GetValue(), plain); + vector> predicates; + for (auto &entry : get.table_filters) { + if (ExpressionFilter::IsOptionalFilter(entry.Filter())) { + continue; + } + auto &field = source.relation.fields[entry.GetIndex().GetIndex()]; + BoundColumnRefExpression column(field.type, field.source_binding); + predicates.push_back(entry.Filter().ToExpression(column)); + } + auto conjunction = make_uniq(ExpressionType::CONJUNCTION_AND); + auto ordinal = LogicalPlanSQLExportHelpers::CollectExpressions(get).size(); + for (auto &predicate : predicates) { + auto exported = BoundExpressionSQLExporter::ExportAtPath( + *predicate, binding_context, LogicalPlanSQLExportHelpers::PlanExpressionPath(path, ordinal++)); + if (exported.HasError()) { + return LogicalPlanSQLExportResult::Failure(exported); + } + conjunction->AddExpression(std::move(exported.GetValue())); + } + if (conjunction->GetChildren().size() == 1) { + select->where_clause = std::move(conjunction->GetChildrenMutable()[0]); + } else if (!conjunction->GetChildren().empty()) { + select->where_clause = std::move(conjunction); + } + LogicalPlanSQLExportHelpers::SetChildScope(*select, std::move(source), plain); + return LogicalPlanSQLExportResult::Success({std::move(select), std::move(fields.GetValue())}); +} + +LogicalPlanSQLExportResult LogicalSecureView::ToSQL(LogicalPlanSQLExportContext &export_context, + const LogicalPlanVerificationPath &path) { + auto &view = *this; + auto fields = LogicalPlanSQLExportHelpers::CreateFields(view, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + if (!view.has_source || view.source_name.Path().empty() || view.source_types.empty()) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "secure_view_source", "The secure view does not retain its qualified source metadata")}); + } + for (auto &component : view.source_name.Path()) { + if (component.empty()) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "secure_view_source", "The secure view source name cannot be represented in SQL")}); + } + } + if (view.output_bindings.size() != fields.GetValue().size() || + view.output_expressions.size() != fields.GetValue().size()) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "secure_view_output", "The secure view output mapping is incomplete")}); + } + for (auto &type : view.source_types) { + if (!SQLExportHelpers::IsSQLRepresentableType(type)) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "secure_view_source", "A secure view source type cannot be represented in SQL")}); + } + } + + if (view.source_filters.size() != view.pushed_filters.size()) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "secure_view_filter", "The secure view does not retain every caller predicate")}); + } + + auto source_alias = export_context.NextRelationAlias(); + auto table = make_uniq(); + table->SetQualifiedName(view.source_name); + table->alias = source_alias; + for (idx_t i = 0; i < view.source_types.size(); i++) { + table->column_name_alias.push_back(LogicalPlanSQLExportHelpers::FieldIdentifier(i)); + } + if (view.has_at_clause) { + table->at_clause = make_uniq(view.at_unit, ConstantExpression::FromValue(view.at_value)); + } + + BoundExpressionSQLExportContext expression_context; + expression_context.client_context = &export_context.GetClientContext(); + expression_context.resolve_binding = [source_alias, source_types = view.source_types]( + const ColumnBinding &binding) -> optional { + if (binding.table_index != TableIndex(0) || binding.column_index.GetIndex() >= source_types.size()) { + return {}; + } + auto index = binding.column_index.GetIndex(); + return ResolvedSQLColumnReference {{source_alias, LogicalPlanSQLExportHelpers::FieldIdentifier(index)}, + source_types[index]}; + }; + + auto select = make_uniq(); + select->from_table = std::move(table); + for (idx_t i = 0; i < fields.GetValue().size(); i++) { + if (view.output_bindings[i] != fields.GetValue()[i].source_binding || + !view.output_expressions[i]->GetReturnType().EqualsIncludingCollation(fields.GetValue()[i].type)) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "secure_view_output", "The secure view output mapping does not match its current schema")}); + } + auto expression = BoundExpressionSQLExporter::ExportAtPath( + *view.output_expressions[i], expression_context, LogicalPlanSQLExportHelpers::PlanExpressionPath(path, i)); + if (expression.HasError()) { + return LogicalPlanSQLExportResult::Failure(expression); + } + expression.GetValue()->SetAlias(LogicalPlanSQLExportHelpers::FieldIdentifier(i)); + select->select_list.push_back(std::move(expression.GetValue())); + } + for (idx_t i = 0; i < view.source_filters.size(); i++) { + if (!view.source_filters[i]) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "secure_view_filter", "The secure view caller predicate has no complete source mapping")}); + } + auto predicate = BoundExpressionSQLExporter::ExportAtPath( + *view.source_filters[i], expression_context, + LogicalPlanSQLExportHelpers::PlanExpressionPath(path, fields.GetValue().size() + i)); + if (predicate.HasError()) { + return LogicalPlanSQLExportResult::Failure(predicate); + } + select->where_clause = + SQLExportHelpers::Conjoin(std::move(select->where_clause), std::move(predicate.GetValue())); + } + + return LogicalPlanSQLExportResult::Success({std::move(select), std::move(fields.GetValue())}); +} + +LogicalPlanVerificationResult LogicalGet::ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + return ExportSQLSource(context, path); +} + +LogicalPlanVerificationResult +LogicalExtensionOperator::ToSQL(LogicalPlanSQLExportContext &context, const LogicalPlanVerificationPath &path) { + return LogicalPlanSQLExportResult::Failure( + {ExtensionIssue(LogicalPlanVerificationIssueCode::UNSUPPORTED_EXTENSION, path, GetExtensionName(), + "The extension operator does not implement SQL reconstruction")}); +} + +} // namespace duckdb diff --git a/src/duckdb/src/planner/sql_export/sql_export_statements.cpp b/src/duckdb/src/planner/sql_export/sql_export_statements.cpp new file mode 100644 index 000000000..935845fae --- /dev/null +++ b/src/duckdb/src/planner/sql_export/sql_export_statements.cpp @@ -0,0 +1,293 @@ +#include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp" +#include "duckdb/common/filename_pattern.hpp" +#include "duckdb/common/sql_identifier.hpp" +#include "duckdb/parser/parsed_data/copy_info.hpp" +#include "duckdb/parser/parsed_data/create_table_info.hpp" +#include "duckdb/parser/query_node/copy_query_node.hpp" +#include "duckdb/parser/query_node/statement_node.hpp" +#include "duckdb/parser/statement/create_statement.hpp" +#include "duckdb/parser/statement/select_statement.hpp" +#include "duckdb/planner/expression/bound_reference_expression.hpp" +#include "duckdb/planner/operator/logical_copy_to_file.hpp" +#include "duckdb/planner/operator/logical_create_table.hpp" +#include "duckdb/planner/sql_export/logical_plan_sql_exporter_internal.hpp" + +namespace duckdb { + +static LogicalPlanVerificationIssue UnsupportedStatement(const LogicalPlanVerificationPath &path, string feature, + string message) { + return LogicalPlanSQLExportHelpers::PlanUnsupportedFeature(path, std::move(feature), std::move(message)); +} + +static optional StatementBoundaryIssue(const LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + if (context.Ancestors().size() != 1) { + return UnsupportedStatement(path, "dml_subplan", + "A data-modifying operator below the plan root cannot be exported"); + } + return optional(); +} + +//! The filename pattern string the bound segments were parsed from, empty for the default pattern. +static optional FilenamePatternOption(const FilenamePattern &pattern) { + // the legacy serialization helpers drop legacy-compatible segments, read the segments directly + auto &segments = pattern.Segments(); + string result; + for (auto &segment : segments) { + switch (segment.type) { + case FileNameSegmentType::LITERAL: + result += segment.data; + break; + case FileNameSegmentType::OFFSET: + result += "{i}"; + break; + case FileNameSegmentType::UUID_V4: + result += "{uuid}"; + break; + case FileNameSegmentType::UUID_V7: + result += "{uuidv7}"; + break; + default: + return optional(); + } + } + return result; +} + +LogicalPlanSQLExportResult SQLExportStatementHelpers::ExportCopyToNode(LogicalCopyToFile &op, + LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + D_ASSERT(op.copy_info); + if (op.copy_info->is_from || op.children.size() != 1) { + return LogicalPlanSQLExportResult::Failure( + {UnsupportedStatement(path, "copy_shape", "COPY without a source query cannot be exported")}); + } + if (op.copy_info->file_path_expression) { + return LogicalPlanSQLExportResult::Failure( + {UnsupportedStatement(path, "copy_dynamic_path", "COPY with a computed file path cannot be exported")}); + } + if (op.partition_path_expression) { + return LogicalPlanSQLExportResult::Failure( + {UnsupportedStatement(path, "copy_partition_path", "COPY with PARTITION_PATH cannot be exported")}); + } + auto pattern = FilenamePatternOption(op.filename_pattern); + if (!pattern) { + return LogicalPlanSQLExportResult::Failure({UnsupportedStatement( + path, "copy_filename_pattern", "The COPY filename pattern cannot be reconstructed as an option")}); + } + auto fields = LogicalPlanSQLExportHelpers::CreateFields(op, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + auto child = context.Export(*op.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (child.HasError()) { + return child; + } + // the written file schema uses the source's output names + if (child.GetValue().fields.size() != op.names.size()) { + return LogicalPlanSQLExportResult::Failure( + {UnsupportedStatement(path, "copy_shape", "The COPY source does not cover the written columns")}); + } + child = LogicalPlanSQLExportHelpers::ApplyOutputNames(std::move(child), op.names); + if (child.HasError()) { + return child; + } + auto info = make_uniq(); + info->is_from = false; + // the bound format may be auto-detected or fallen back, the selected function is authoritative + info->format = op.function.name.GetIdentifierName(); + info->is_format_auto_detected = false; + info->file_path = op.file_path; + // format-specific options stay in the bound info, sink options re-emit from the operator's resolved state + for (auto &entry : op.copy_info->options) { + info->options[entry.first] = entry.second; + } + auto set_option = [&](const char *name, Value value) { + info->options[Identifier(name)] = {std::move(value)}; + }; + // binding rejects an explicit USE_TMP_FILE next to these modes and resolves it to false + const bool use_tmp_file_implied = + op.per_thread_output || op.rotate || op.partition_output || !op.order_columns.empty(); + if (!use_tmp_file_implied) { + set_option("use_tmp_file", Value::BOOLEAN(op.use_tmp_file)); + } + switch (op.overwrite_mode) { + case CopyOverwriteMode::COPY_OVERWRITE_OR_IGNORE: + set_option("overwrite_or_ignore", Value::BOOLEAN(true)); + break; + case CopyOverwriteMode::COPY_OVERWRITE: + set_option("overwrite", Value::BOOLEAN(true)); + break; + case CopyOverwriteMode::COPY_APPEND: + set_option("append", Value::BOOLEAN(true)); + break; + default: + break; + } + if (*pattern != "data_{i}") { + // everything but the default renders, including an explicitly empty pattern + set_option("filename_pattern", Value(*pattern)); + } + // binding defaults the extension from the function, so any difference was set explicitly, + // including an explicitly empty extension + if (op.file_extension != op.function.extension) { + set_option("file_extension", Value(op.file_extension)); + } + if (op.per_thread_output) { + set_option("per_thread_output", Value::BOOLEAN(true)); + } + if (op.batch_size.IsValid()) { + // the binder backfills an unset batch size from the function's desired batch size, which a + // rebind re-derives from the format options, so only an overriding value needs to render + auto rederived = op.function.desired_batch_size && op.bind_data + ? op.function.desired_batch_size(context.GetClientContext(), *op.bind_data) + : optional_idx(); + if (op.batch_size != rederived) { + // the SQL surface names these the row group options, batch_size is not a bindable name + set_option("row_group_size", Value::UBIGINT(op.batch_size.GetIndex())); + } + } + if (op.batch_size_bytes.IsValid()) { + set_option("row_group_size_bytes", Value::UBIGINT(op.batch_size_bytes.GetIndex())); + } + if (op.batches_per_file.IsValid()) { + set_option("row_groups_per_file", Value::UBIGINT(op.batches_per_file.GetIndex())); + } + if (op.file_size_bytes.IsValid()) { + set_option("file_size_bytes", Value::UBIGINT(op.file_size_bytes.GetIndex())); + } + // ordered writes set partition_output without columns, which rebinding re-derives from order_by + if (op.partition_output && !op.partition_columns.empty()) { + vector partition_names; + for (auto column_index : op.partition_columns) { + partition_names.emplace_back(op.names[column_index].GetIdentifierName()); + } + info->options[Identifier("partition_by")] = std::move(partition_names); + if (op.write_partition_columns) { + set_option("write_partition_columns", Value::BOOLEAN(true)); + } + if (!op.hive_file_pattern) { + set_option("hive_file_pattern", Value::BOOLEAN(false)); + } + } + if (!op.write_empty_file) { + set_option("write_empty_file", Value::BOOLEAN(false)); + } + if (!op.order_columns.empty()) { + vector order_values; + for (auto &order : op.order_columns) { + if (order.expression->GetExpressionClass() != ExpressionClass::BOUND_REF) { + return LogicalPlanSQLExportResult::Failure({UnsupportedStatement( + path, "copy_order_expression", "The COPY ordering is not a plain written column")}); + } + string entry = SQLIdentifier::ToString(order.expression->GetAlias()); + entry += order.type == OrderType::DESCENDING ? " DESC" : " ASC"; + entry += order.null_order == OrderByNullType::NULLS_FIRST ? " NULLS FIRST" : " NULLS LAST"; + order_values.emplace_back(std::move(entry)); + } + info->options[Identifier("order_by")] = std::move(order_values); + } + if (op.preserve_order != PreserveOrderType::AUTOMATIC) { + set_option("preserve_order", Value::BOOLEAN(op.preserve_order == PreserveOrderType::PRESERVE_ORDER)); + } + switch (op.return_type) { + case CopyFunctionReturnType::CHANGED_ROWS_AND_FILE_LIST: + set_option("return_files", Value::BOOLEAN(true)); + break; + case CopyFunctionReturnType::WRITTEN_FILE_STATISTICS: + set_option("return_stats", Value::BOOLEAN(true)); + break; + default: + break; + } + info->select_statement = std::move(child.GetValue().query); + auto node = make_uniq(std::move(info)); + return LogicalPlanSQLExportResult::Success({std::move(node), std::move(fields.GetValue())}); +} + +LogicalPlanSQLExportResult SQLExportStatementHelpers::ExportCreateTableAsNode(LogicalCreateTable &op, + LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + D_ASSERT(op.info && op.info->base); + auto &base = op.info->base->Cast(); + if (op.children.size() != 1) { + return LogicalPlanSQLExportResult::Failure({UnsupportedStatement( + path, "create_table_plain", "CREATE TABLE without a source query cannot be exported")}); + } + auto fields = LogicalPlanSQLExportHelpers::CreateFields(op, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + auto child = context.Export(*op.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (child.HasError()) { + return child; + } + // the created columns take the source's output names + vector column_names; + for (auto &column : base.columns.Logical()) { + column_names.push_back(column.Name()); + } + if (child.GetValue().fields.size() != column_names.size()) { + return LogicalPlanSQLExportResult::Failure({UnsupportedStatement( + path, "create_table_shape", "The CREATE TABLE source does not cover the created columns")}); + } + child = LogicalPlanSQLExportHelpers::ApplyOutputNames(std::move(child), column_names); + if (child.HasError()) { + return child; + } + auto info = make_uniq(base.GetQualifiedName()); + info->temporary = base.temporary; + info->on_conflict = base.on_conflict; + for (auto &partition : base.partition_keys) { + info->partition_keys.push_back(partition->Copy()); + } + for (auto &order : base.sort_keys) { + info->sort_keys.push_back(order->Copy()); + } + for (auto &entry : base.options) { + info->options[entry.first] = entry.second->Copy(); + } + info->query = make_uniq(); + info->query->node = std::move(child.GetValue().query); + auto statement = make_uniq(); + statement->info = std::move(info); + auto node = make_uniq(std::move(statement)); + return LogicalPlanSQLExportResult::Success({std::move(node), std::move(fields.GetValue())}); +} + +QueryNode &SQLExportStatementHelpers::SourceDefinitionNode(QueryNode &query) { + switch (query.type) { + case QueryNodeType::COPY_QUERY_NODE: { + auto &info = *query.Cast().info; + D_ASSERT(info.select_statement); + return SourceDefinitionNode(*info.select_statement); + } + case QueryNodeType::STATEMENT_NODE: { + auto &statement = query.Cast().stmt; + D_ASSERT(statement.type == StatementType::CREATE_STATEMENT); + auto &info = statement.Cast().info->Cast(); + D_ASSERT(info.query && info.query->node); + return *info.query->node; + } + default: + return query; + } +} + +LogicalPlanSQLExportResult LogicalCopyToFile::ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + if (auto issue = StatementBoundaryIssue(context, path)) { + return LogicalPlanSQLExportResult::Failure({std::move(*issue)}); + } + return SQLExportStatementHelpers::ExportCopyToNode(*this, context, path); +} + +LogicalPlanSQLExportResult LogicalCreateTable::ToSQL(LogicalPlanSQLExportContext &context, + const LogicalPlanVerificationPath &path) { + if (auto issue = StatementBoundaryIssue(context, path)) { + return LogicalPlanSQLExportResult::Failure({std::move(*issue)}); + } + return SQLExportStatementHelpers::ExportCreateTableAsNode(*this, context, path); +} + +} // namespace duckdb diff --git a/src/duckdb/src/planner/sql_export/sql_export_values.cpp b/src/duckdb/src/planner/sql_export/sql_export_values.cpp new file mode 100644 index 000000000..11b067408 --- /dev/null +++ b/src/duckdb/src/planner/sql_export/sql_export_values.cpp @@ -0,0 +1,458 @@ +#include "duckdb/planner/operator/logical_empty_result.hpp" +#include "duckdb/planner/operator/logical_dummy_scan.hpp" +#include "duckdb/planner/sql_export/logical_plan_sql_exporter_internal.hpp" +#include "duckdb/planner/operator/logical_join.hpp" +#include "duckdb/function/scalar/compressed_materialization_utils.hpp" +#include "duckdb/planner/logical_plan_sql_exporter.hpp" +#include "duckdb/main/settings.hpp" +#include "duckdb/parser/expression/case_expression.hpp" +#include "duckdb/parser/expression/columnref_expression.hpp" +#include "duckdb/parser/expression/comparison_expression.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/parser/expression/operator_expression.hpp" +#include "duckdb/parser/expression/function_expression.hpp" +#include "duckdb/parser/query_node/select_node.hpp" +#include "duckdb/parser/statement/select_statement.hpp" +#include "duckdb/parser/tableref/emptytableref.hpp" +#include "duckdb/parser/tableref/expressionlistref.hpp" +#include "duckdb/parser/tableref/joinref.hpp" +#include "duckdb/parser/tableref/subqueryref.hpp" +#include "duckdb/planner/bound_expression_sql_exporter.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/expression/bound_constant_expression.hpp" +#include "duckdb/planner/expression/bound_function_expression.hpp" +#include "duckdb/planner/expression/bound_subquery_expression.hpp" +#include "duckdb/planner/expression/bound_window_expression.hpp" +#include "duckdb/planner/expression/bound_aggregate_expression.hpp" +#include "duckdb/function/lambda_functions.hpp" +#include "duckdb/planner/expression_iterator.hpp" +#include "duckdb/planner/logical_operator.hpp" +#include "duckdb/planner/operator/logical_column_data_get.hpp" +#include "duckdb/planner/operator/logical_empty_result.hpp" +#include "duckdb/planner/operator/logical_distinct.hpp" +#include "duckdb/planner/operator/logical_order.hpp" +#include "duckdb/planner/operator/logical_top_n.hpp" +#include "duckdb/planner/operator/logical_expression_get.hpp" +#include "duckdb/planner/sql_export_helpers.hpp" + +namespace duckdb { + +//! Whether the expression is volatile or can throw, ignoring compressed materialization wrappers +static bool IsEffectfulSemanticExpression(const Expression &expression) { + if (auto wrapped = CMUtils::GetWrappedInput(expression)) { + return IsEffectfulSemanticExpression(*wrapped); + } + bool effectful = false; + ExpressionIterator::EnumerateChildren( + expression, [&](const Expression &child) { effectful = effectful || IsEffectfulSemanticExpression(child); }); + if (effectful) { + return true; + } + switch (expression.GetExpressionClass()) { + case ExpressionClass::BOUND_FUNCTION: { + auto &bound_function = expression.Cast(); + auto &function = bound_function.Function(); + if (function.GetStability() == FunctionStability::VOLATILE || + function.GetErrorMode() == FunctionErrors::CAN_THROW_RUNTIME_ERROR) { + return true; + } + if (!function.HasBindLambdaCallback()) { + return false; + } + auto lambda = bound_function.BindInfo()->Cast().GetLambdaExpression(); + return lambda && (lambda->IsVolatile() || lambda->CanThrow()); + } + case ExpressionClass::BOUND_AGGREGATE: + return expression.Cast().Function().GetStability() == FunctionStability::VOLATILE; + case ExpressionClass::BOUND_WINDOW: { + auto &window = expression.Cast(); + auto stability = window.AggregateFunction() ? window.AggregateFunction()->GetStability() + : window.WindowFunction()->GetStability(); + return stability == FunctionStability::VOLATILE; + } + case ExpressionClass::BOUND_SUBQUERY: { + auto &plan = expression.Cast().Subquery().plan; + return plan && plan->HasVolatileExpressions(); + } + default: + return false; + } +} + +static bool HasEffectfulExpressionSubtree(const LogicalOperator &op) { + for (auto &expression : LogicalPlanSQLExportHelpers::CollectExpressions(op)) { + if (IsEffectfulSemanticExpression(expression.get())) { + return true; + } + } + for (auto &child : op.children) { + if (HasEffectfulExpressionSubtree(*child)) { + return true; + } + } + return false; +} + +static bool OrderDistinguishesValues(ClientContext &context, const LogicalType &type) { + auto default_collation = Settings::Get(context); + return !TypeVisitor::Contains(type, [&](const LogicalType &child) { + if (child.IsIntegral()) { + return false; + } + switch (child.id()) { + case LogicalTypeId::BOOLEAN: + case LogicalTypeId::DECIMAL: + case LogicalTypeId::DATE: + case LogicalTypeId::TIME: + case LogicalTypeId::TIMESTAMP: + case LogicalTypeId::TIMESTAMP_SEC: + case LogicalTypeId::TIMESTAMP_MS: + case LogicalTypeId::TIMESTAMP_NS: + case LogicalTypeId::TIMESTAMP_TZ: + case LogicalTypeId::BLOB: + case LogicalTypeId::BIT: + case LogicalTypeId::UUID: + case LogicalTypeId::ENUM: + case LogicalTypeId::LIST: + case LogicalTypeId::ARRAY: + case LogicalTypeId::STRUCT: + case LogicalTypeId::MAP: + return false; + case LogicalTypeId::VARCHAR: + return !StringType::GetCollation(child).empty() || !default_collation.empty(); + default: + return true; + } + }); +} + +static bool OrdersAllFields(ClientContext &context, const vector &orders, LogicalOperator &op) { + auto bindings = op.GetColumnBindings(); + for (idx_t i = 0; i < bindings.size(); i++) { + if (!OrderDistinguishesValues(context, op.types[i])) { + return false; + } + bool found = false; + for (auto &order : orders) { + if (order.expression->GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF && + order.expression->Cast().Binding() == bindings[i]) { + found = true; + break; + } + } + if (!found) { + return false; + } + } + return true; +} + +static bool HasChunkSensitiveConsumer(ClientContext &context, LogicalOperator &op, bool single_join_errors, + bool order_required = false) { + if (LogicalPlanSQLExportHelpers::HasEffectfulExpressions(op)) { + return true; + } + switch (op.type) { + case LogicalOperatorType::LOGICAL_COMPARISON_JOIN: + case LogicalOperatorType::LOGICAL_ANY_JOIN: + case LogicalOperatorType::LOGICAL_ASOF_JOIN: + case LogicalOperatorType::LOGICAL_DELIM_JOIN: + if (order_required || (single_join_errors && op.Cast().join_type == JoinType::SINGLE)) { + return true; + } + break; + case LogicalOperatorType::LOGICAL_CROSS_PRODUCT: + case LogicalOperatorType::LOGICAL_POSITIONAL_JOIN: + if (order_required) { + return true; + } + break; + case LogicalOperatorType::LOGICAL_LIMIT: + order_required = true; + break; + case LogicalOperatorType::LOGICAL_TOP_N: + order_required = !OrdersAllFields(context, op.Cast().orders, op); + break; + case LogicalOperatorType::LOGICAL_ORDER_BY: + if (OrdersAllFields(context, op.Cast().orders, op)) { + order_required = false; + } + break; + case LogicalOperatorType::LOGICAL_DISTINCT: + order_required |= op.Cast().distinct_type == DistinctType::DISTINCT_ON; + break; + case LogicalOperatorType::LOGICAL_GET: + if (!op.children.empty()) { + return true; + } + break; + case LogicalOperatorType::LOGICAL_PROJECTION: + case LogicalOperatorType::LOGICAL_FILTER: + case LogicalOperatorType::LOGICAL_EXPRESSION_GET: + case LogicalOperatorType::LOGICAL_UNION: + case LogicalOperatorType::LOGICAL_EXCEPT: + case LogicalOperatorType::LOGICAL_INTERSECT: + case LogicalOperatorType::LOGICAL_UNNEST: + case LogicalOperatorType::LOGICAL_CHUNK_GET: + case LogicalOperatorType::LOGICAL_DUMMY_SCAN: + case LogicalOperatorType::LOGICAL_EMPTY_RESULT: + break; + default: + return true; + } + for (auto &child : op.children) { + if (HasChunkSensitiveConsumer(context, *child, single_join_errors, order_required)) { + return true; + } + } + return false; +} + +LogicalPlanSQLExportResult LogicalColumnDataGet::ToSQL(LogicalPlanSQLExportContext &export_context, + const LogicalPlanVerificationPath &path) { + auto &get = *this; + D_ASSERT(get.children.empty() && get.collection); + if (!get.collection.is_owned()) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::UnsupportedSource( + path, LogicalPlanSQLExportHelpers::LogicalSourceIdentity(), "borrowed_chunk_collection")}); + } + if (get.collection->Count() == 0) { + LogicalEmptyResult empty(get.types, get.GetColumnBindings()); + empty.ResolveOperatorTypes(); + return empty.ToSQL(export_context, path); + } + auto fields = LogicalPlanSQLExportHelpers::CreateFields(get, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + auto values = make_uniq(); + values->alias = export_context.NextRelationAlias(); + values->expected_types = get.types; + for (idx_t i = 0; i < fields.GetValue().size(); i++) { + values->expected_names.push_back(LogicalPlanSQLExportHelpers::FieldIdentifier(i)); + } + idx_t remaining = get.collection->Count(); + bool repacked = false; + for (auto &chunk : get.collection->Chunks(get.GetColumnIds())) { + repacked |= chunk.size() != MinValue(remaining, STANDARD_VECTOR_SIZE); + remaining -= chunk.size(); + for (idx_t row = 0; row < chunk.size(); row++) { + vector> exported_row; + if (chunk.ColumnCount() == 0) { + exported_row.push_back(make_uniq(Literal::Null())); + } else { + for (idx_t column = 0; column < chunk.ColumnCount(); column++) { + auto value = + BoundExpressionSQLExporter::Export(BoundConstantExpression(chunk.GetValue(column, row)), {}); + if (value.HasError()) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "chunk_value", "The materialized value cannot be represented in SQL")}); + } + exported_row.push_back(std::move(value.GetValue())); + } + } + values->values.push_back(std::move(exported_row)); + } + } + if (repacked && HasChunkSensitiveConsumer( + export_context.GetClientContext(), export_context.Ancestors().front().get(), + Settings::Get(export_context.GetClientContext()))) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "chunk_consumer_evaluation", "SQL cannot retain source chunks for an effectful consumer")}); + } + auto select = make_uniq(); + for (idx_t i = 0; i < fields.GetValue().size(); i++) { + select->select_list.push_back( + make_uniq(LogicalPlanSQLExportHelpers::FieldIdentifier(i), values->alias)); + } + select->from_table = std::move(values); + return LogicalPlanSQLExportResult::Success({std::move(select), std::move(fields.GetValue())}); +} + +LogicalPlanSQLExportResult LogicalExpressionGet::ToSQL(LogicalPlanSQLExportContext &export_context, + const LogicalPlanVerificationPath &path) { + auto &get = *this; + D_ASSERT(get.children.size() == 1 && get.children[0]); + D_ASSERT(!get.expressions.empty() && !get.expressions[0].empty()); +#ifdef D_ASSERT_IS_ENABLED + auto column_count = get.expressions[0].size(); + D_ASSERT(get.expr_types.size() == column_count); + for (auto &row : get.expressions) { + D_ASSERT(row.size() == column_count); + for (idx_t i = 0; i < column_count; i++) { + D_ASSERT(row[i] && row[i]->GetReturnType() == get.expr_types[i]); + } + } +#endif + auto fields = LogicalPlanSQLExportHelpers::CreateFields(get, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + if (get.children[0]->type != LogicalOperatorType::LOGICAL_DUMMY_SCAN) { + return ExportSQLInput(export_context, path, std::move(fields.GetValue())); + } + + auto values = make_uniq(); + values->alias = export_context.NextRelationAlias(); + values->expected_types = get.expr_types; + for (idx_t i = 0; i < fields.GetValue().size(); i++) { + values->expected_names.push_back(LogicalPlanSQLExportHelpers::FieldIdentifier(i)); + } + BoundExpressionSQLExportContext expression_context; + expression_context.client_context = &export_context.GetClientContext(); + auto expressions = LogicalPlanSQLExportHelpers::CollectExpressions(get); + idx_t expression_ordinal = 0; + for (auto &row : get.expressions) { + vector> exported_row; + for (idx_t column_index = 0; column_index < row.size(); column_index++) { + auto expression = + export_context.ExportExpression(get, expressions, expression_ordinal++, expression_context, path); + if (expression.HasError()) { + return LogicalPlanSQLExportResult::Failure(expression); + } + exported_row.push_back(std::move(expression.GetValue())); + } + values->values.push_back(std::move(exported_row)); + } + auto values_alias = values->alias; + auto select = make_uniq(); + for (idx_t i = 0; i < fields.GetValue().size(); i++) { + auto expression = make_uniq(LogicalPlanSQLExportHelpers::FieldIdentifier(i), values_alias); + select->select_list.push_back(std::move(expression)); + } + select->from_table = std::move(values); + LogicalPlanSQLExportRelation relation {std::move(select), std::move(fields.GetValue())}; + return LogicalPlanSQLExportResult::Success(std::move(relation)); +} + +LogicalPlanSQLExportResult LogicalExpressionGet::ExportSQLInput(LogicalPlanSQLExportContext &export_context, + const LogicalPlanVerificationPath &path, + vector fields) { + auto &get = *this; + if (HasEffectfulExpressionSubtree(get)) { + return LogicalPlanSQLExportResult::Failure({LogicalPlanSQLExportHelpers::PlanUnsupportedFeature( + path, "values_expression_evaluation", "VALUES with input requires nonvolatile, nonthrowing expressions")}); + } + auto child = export_context.ExportChild(*get.children[0], LogicalPlanSQLExportHelpers::PlanChildPath(path, 0)); + if (child.HasError()) { + return LogicalPlanSQLExportResult::Failure(child); + } + auto expression_context = + LogicalPlanSQLExportHelpers::CreateBindingContext(export_context.GetClientContext(), {child.GetValue()}); + auto expressions = LogicalPlanSQLExportHelpers::CollectExpressions(get); + auto select = make_uniq(); + select->from_table = LogicalPlanSQLExportHelpers::CreateSubquery(std::move(child.GetValue())); + + auto row_alias = export_context.NextRelationAlias(); + auto cases = make_uniq(); + auto rows = make_uniq(); + rows->alias = row_alias; + rows->expected_names.push_back(LogicalPlanSQLExportHelpers::FieldIdentifier(0)); + rows->expected_types.push_back(LogicalType::BIGINT); + for (idx_t row = 0; row < get.expressions.size(); row++) { + // Keep all expressions of a VALUES row in one evaluation group. + vector arguments; + for (idx_t column = 0; column < fields.size(); column++) { + auto expression = export_context.ExportExpression(get, expressions, row * fields.size() + column, + expression_context, path); + if (expression.HasError()) { + return LogicalPlanSQLExportResult::Failure(expression); + } + arguments.emplace_back(LogicalPlanSQLExportHelpers::FieldIdentifier(column), + std::move(expression.GetValue())); + } + auto value = SQLExportHelpers::SystemFunction("struct_pack", std::move(arguments)); + if (row + 1 == get.expressions.size()) { + cases->ElseMutable() = std::move(value); + } else { + CaseCheck check; + check.when_expr = make_uniq( + ExpressionType::COMPARE_EQUAL, + make_uniq(LogicalPlanSQLExportHelpers::FieldIdentifier(0), row_alias), + ConstantExpression::FromValue(Value::BIGINT(int64_t(row)))); + check.then_expr = std::move(value); + cases->CaseChecksMutable().push_back(std::move(check)); + } + vector> ordinal; + ordinal.push_back(ConstantExpression::FromValue(Value::BIGINT(int64_t(row)))); + rows->values.push_back(std::move(ordinal)); + } + unique_ptr value; + if (get.expressions.size() == 1) { + value = std::move(cases->ElseMutable()); + } else { + auto join = make_uniq(JoinRefType::CROSS); + join->left = std::move(select->from_table); + join->right = std::move(rows); + select->from_table = std::move(join); + value = std::move(cases); + } + return export_context.ExportRow(std::move(select), std::move(value), std::move(fields)); +} + +LogicalPlanSQLExportResult LogicalPlanSQLExportContext::ExportRow(unique_ptr select, + unique_ptr value, + vector fields) { + // Keep row evaluation below consumers that can filter or limit emitted rows. + vector> list_arguments; + list_arguments.push_back(std::move(value)); + auto list = SQLExportHelpers::SystemFunction("list_value", std::move(list_arguments)); + vector> unnest_arguments; + unnest_arguments.push_back(std::move(list)); + auto unnest = make_uniq(Identifier("unnest"), std::move(unnest_arguments)); + unnest->SetAlias(LogicalPlanSQLExportHelpers::FieldIdentifier(0)); + select->select_list.push_back(std::move(unnest)); + + auto statement = make_uniq(); + statement->node = std::move(select); + auto packed_alias = NextRelationAlias(); + auto packed = make_uniq(std::move(statement), packed_alias); + packed->column_name_alias.push_back(LogicalPlanSQLExportHelpers::FieldIdentifier(0)); + select = make_uniq(); + select->from_table = std::move(packed); + for (idx_t column = 0; column < fields.size(); column++) { + auto value = make_uniq( + ExpressionType::STRUCT_EXTRACT, + make_uniq(LogicalPlanSQLExportHelpers::FieldIdentifier(0), packed_alias), + ConstantExpression::FromValue( + Value(LogicalPlanSQLExportHelpers::FieldIdentifier(column).GetIdentifierName()))); + value->SetAlias(LogicalPlanSQLExportHelpers::FieldIdentifier(column)); + select->select_list.push_back(std::move(value)); + } + return LogicalPlanSQLExportResult::Success({std::move(select), std::move(fields)}); +} + +static LogicalPlanSQLExportResult ExportConstantSource(LogicalOperator &op, bool has_rows, + const LogicalPlanVerificationPath &path) { + D_ASSERT(op.children.empty() && op.expressions.empty()); + auto fields = LogicalPlanSQLExportHelpers::CreateFields(op, path); + if (fields.HasError()) { + return LogicalPlanSQLExportResult::Failure(fields); + } + auto select = make_uniq(); + select->from_table = make_uniq(); + for (idx_t i = 0; i < fields.GetValue().size(); i++) { + auto expression = LogicalPlanSQLExportHelpers::ExportTypedNull(fields.GetValue()[i].type, path); + if (expression.HasError()) { + return LogicalPlanSQLExportResult::Failure(expression); + } + expression.GetValue()->SetAlias(LogicalPlanSQLExportHelpers::FieldIdentifier(i)); + select->select_list.push_back(std::move(expression.GetValue())); + } + if (!has_rows) { + select->where_clause = ConstantExpression::FromValue(Value::BOOLEAN(false)); + } + return LogicalPlanSQLExportResult::Success({std::move(select), std::move(fields.GetValue())}); +} + +LogicalPlanVerificationResult +LogicalDummyScan::ToSQL(LogicalPlanSQLExportContext &context, const LogicalPlanVerificationPath &path) { + return ExportConstantSource(*this, true, path); +} + +LogicalPlanVerificationResult +LogicalEmptyResult::ToSQL(LogicalPlanSQLExportContext &context, const LogicalPlanVerificationPath &path) { + return ExportConstantSource(*this, false, path); +} + +} // namespace duckdb diff --git a/src/duckdb/src/planner/sql_export/sql_export_window.cpp b/src/duckdb/src/planner/sql_export/sql_export_window.cpp new file mode 100644 index 000000000..a4970a188 --- /dev/null +++ b/src/duckdb/src/planner/sql_export/sql_export_window.cpp @@ -0,0 +1,259 @@ +#include "duckdb/planner/sql_export/bound_expression_sql_exporter_internal.hpp" +#include "duckdb/planner/expression/bound_aggregate_expression.hpp" +#include "duckdb/planner/sql_export_helpers.hpp" +#include "duckdb/parser/expression/cast_expression.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/planner/expression/bound_cast_expression.hpp" +#include "duckdb/planner/expression/bound_constant_expression.hpp" +#include "duckdb/planner/expression/bound_function_expression.hpp" +#include "duckdb/function/window_function.hpp" +#include "duckdb/planner/expression/bound_window_expression.hpp" + +namespace duckdb { + +struct WindowSQLFrame { + optional_ptr order; + optional_ptr start; + optional_ptr end; + unique_ptr start_literal; + unique_ptr end_literal; +}; + +static optional NumericRangeLiteral(const ParsedExpression &literal) { + if (literal.GetExpressionClass() == ExpressionClass::CONSTANT) { + auto value = literal.Cast().GetLiteral().ToValue(); + return value.type().IsNumeric() && !value.IsNull() ? optional(value) : optional(); + } + if (literal.GetExpressionClass() == ExpressionClass::CAST) { + auto &cast = literal.Cast(); + auto type = UnboundType::TryDefaultBind(cast.TargetType()); + auto child = NumericRangeLiteral(cast.Child()); + if (child && type.IsNumeric() && !cast.IsTryCast()) { + return child->DefaultTryCastAs(type); + } + } + return {}; +} + +//! The SQL offset of a RANGE frame endpoint: the retained literal when it still describes the endpoint, +//! otherwise the live input the binder's endpoint arithmetic was derived from +static optional_ptr RangeOffset(const BoundWindowExpression &expression, const WindowSQLFrame &frame, + const unique_ptr &endpoint, WindowBoundary boundary, + const unique_ptr &literal, + const unique_ptr &origin) { + const bool is_range_offset = + boundary == WindowBoundary::EXPR_PRECEDING_RANGE || boundary == WindowBoundary::EXPR_FOLLOWING_RANGE; + if (!is_range_offset) { + return endpoint.get(); + } + if (!origin || !endpoint || !frame.order) { + return nullptr; + } + auto &order = expression.OrderBy()[0]; + const bool origin_matches_endpoint = origin->boundary == boundary && origin->direction == order.type; + if (!origin_matches_endpoint) { + return nullptr; + } + const bool has_literal_offset = literal && literal->GetExpressionClass() == ExpressionClass::BOUND_CONSTANT; + const bool endpoint_is_order = has_literal_offset && Expression::Equals(*endpoint, *order.expression); + if (endpoint_is_order) { + auto &value = literal->Cast().GetValue(); + if (!value.IsNull() && value.type().IsNumeric() && value == Value::Numeric(value.type(), 0)) { + return literal.get(); + } + } + auto input = origin->Match(*endpoint, *frame.order); + if (!input) { + return nullptr; + } + auto &offset = *input; + const bool offset_is_constant = offset.GetExpressionClass() == ExpressionClass::BOUND_CONSTANT; + if (offset_is_constant && offset.Cast().GetValue().IsNull()) { + return nullptr; + } + if (has_literal_offset && offset_is_constant) { + auto &original = literal->Cast().GetValue(); + auto ¤t = offset.Cast().GetValue(); + const bool has_numeric_offsets = original.type().IsNumeric() && current.type().IsNumeric(); + if (!original.IsNull() && has_numeric_offsets && original == current) { + return literal.get(); + } + } + return &offset; +} + +static bool IsOrderDependentBoundary(WindowBoundary boundary) { + switch (boundary) { + case WindowBoundary::EXPR_PRECEDING_RANGE: + case WindowBoundary::EXPR_FOLLOWING_RANGE: + case WindowBoundary::CURRENT_ROW_GROUPS: + case WindowBoundary::EXPR_PRECEDING_GROUPS: + case WindowBoundary::EXPR_FOLLOWING_GROUPS: + return true; + default: + return false; + } +} + +static WindowSQLFrame ReconstructWindowFrame(const BoundWindowExpression &expression) { + WindowSQLFrame frame; + if (expression.OrderBy().size() == 1) { + frame.order = WindowRangeCast::Match(*expression.OrderBy()[0].expression, expression.SQLRangeOrderCasts()); + if (frame.order && expression.SQLRangeOrderType().IsComplete() && + !frame.order->GetReturnType().EqualsIncludingCollation(expression.SQLRangeOrderType())) { + frame.order = nullptr; + } + } + auto retained_offset = [&](const unique_ptr &literal) -> unique_ptr { + auto value = literal ? NumericRangeLiteral(*literal) : optional(); + return value ? make_uniq(*value) : nullptr; + }; + frame.start_literal = retained_offset(expression.SQLRangeStart()); + frame.end_literal = retained_offset(expression.SQLRangeEnd()); + frame.start = RangeOffset(expression, frame, expression.StartExpr(), expression.WindowStart(), frame.start_literal, + expression.SQLRangeStartBoundary()); + frame.end = RangeOffset(expression, frame, expression.EndExpr(), expression.WindowEnd(), frame.end_literal, + expression.SQLRangeEndBoundary()); + return frame; +} + +BoundExpressionSQLExportResult BoundExpressionSQLExportState::ExportWindow(const BoundWindowExpression &expression, + const LogicalPlanVerificationPath &path) { + if (expression.AggregateFunction()) { + return BoundExpressionSQLExportState::PreserveCollation( + expression.GetReturnType(), ExportWindowFunction(expression, *expression.AggregateFunction(), path), path); + } + D_ASSERT(expression.WindowFunction()); + return BoundExpressionSQLExportState::PreserveCollation( + expression.GetReturnType(), ExportWindowFunction(expression, *expression.WindowFunction(), path), path); +} + +template +BoundExpressionSQLExportResult +BoundExpressionSQLExportState::ExportWindowFunction(const BoundWindowExpression &expression, const FUNCTION &function, + const LogicalPlanVerificationPath &path) { + auto &definition = function.GetDefinition(); + D_ASSERT(definition); + auto identity = BoundExpressionSQLExportState::DefinitionFunctionIdentity( + *definition, function.GetLogicalArguments(), function.GetLogicalReturnType()); + if (!identity.IsValid()) { + identity.arguments.clear(); + for (auto &child : expression.GetChildren()) { + identity.arguments.push_back(child->GetReturnType()); + } + identity.return_type = expression.GetReturnType(); + if (!identity.IsValid()) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::InternalExpressionInvariant( + path, expression, "Bound window function identity is incomplete")}); + } + // Without a complete logical signature the function cannot be rebound from SQL + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, std::move(identity), "The window function does not have a complete logical signature")}); + } + if (expression.GetChildren().size() != function.GetLogicalArguments().size()) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, std::move(identity), "The window function does not retain every SQL argument")}); + } + auto name = BoundExpressionSQLExportState::RebindableFunctionName(*definition); + if (!name || !SQLExportHelpers::IsSQLValueType(expression.GetReturnType()) || + definition->GetProperties().GetCaptureArgumentAliases()) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, std::move(identity), "The window function no longer represents its logical SQL signature")}); + } + auto frame = ReconstructWindowFrame(expression); + if ((expression.StartExpr() && !frame.start) || (expression.EndExpr() && !frame.end)) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFeature( + path, "window_range_offset", "The RANGE endpoint does not retain its SQL offset and ordering operand")}); + } + + vector source_children; + for (auto &partition : expression.Partitions()) { + source_children.emplace_back(partition.get()); + } + for (auto &order : expression.OrderBy()) { + source_children.emplace_back(frame.order ? frame.order : order.expression.get()); + } + for (idx_t i = 0; i < expression.GetChildren().size(); i++) { + source_children.emplace_back(expression.GetChildren()[i].get()); + } + if (expression.Filter()) { + source_children.emplace_back(expression.Filter().get(), LogicalType::BOOLEAN); + } + if (expression.StartExpr()) { + source_children.emplace_back(frame.start); + } + if (expression.EndExpr()) { + source_children.emplace_back(frame.end); + } + for (auto &order : expression.ArgOrders()) { + source_children.emplace_back(order.expression.get()); + } + vector> children; + vector issues; + ExportChildren(source_children, path, children, issues); + if (!issues.empty()) { + return BoundExpressionSQLExportResult::Failure(std::move(issues)); + } + auto window = make_uniq(name->Catalog().GetIdentifierName(), name->Schema().GetIdentifierName(), + name->Name().GetIdentifierName()); + window->SetQualifiedName(*name); + idx_t ordinal = 0; + for (idx_t i = 0; i < expression.Partitions().size(); i++) { + window->PartitionsMutable().push_back(std::move(children[ordinal++])); + } + // RANGE offsets and GROUPS frames are defined in terms of the ORDER BY + const bool frame_needs_order = + IsOrderDependentBoundary(expression.WindowStart()) || IsOrderDependentBoundary(expression.WindowEnd()); + for (auto &order : expression.OrderBy()) { + auto &child = children[ordinal++]; + if (!frame_needs_order && SQLExportHelpers::IsConstantOrderKey(*order.expression, *child)) { + continue; + } + window->OrderByMutable().emplace_back( + order.type, order.null_order, + SQLExportHelpers::OrderExpression(order.expression->GetReturnType(), std::move(child))); + } + auto &named_arguments = function.GetNamedArguments(); + auto positional_count = function.GetPositionalArgumentCount(); + if (!named_arguments.empty() && positional_count + named_arguments.size() != expression.GetChildren().size()) { + return BoundExpressionSQLExportResult::Failure({BoundExpressionSQLExportState::UnsupportedFunction( + path, std::move(identity), "The named SQL arguments are incomplete")}); + } + for (idx_t i = 0; i < expression.GetChildren().size(); i++) { + auto argument_name = + !named_arguments.empty() && i >= positional_count ? named_arguments[i - positional_count] : Identifier(); + window->GetArgumentsMutable().emplace_back(std::move(argument_name), std::move(children[ordinal++])); + } + if (expression.Filter()) { + window->FilterMutable() = std::move(children[ordinal++]); + } + if (expression.StartExpr()) { + window->StartExprMutable() = std::move(children[ordinal++]); + } + if (expression.EndExpr()) { + window->EndExprMutable() = std::move(children[ordinal++]); + } + for (auto &order : expression.ArgOrders()) { + auto &child = children[ordinal++]; + if (SQLExportHelpers::IsConstantOrderKey(*order.expression, *child)) { + continue; + } + window->ArgOrdersMutable().emplace_back( + order.type, order.null_order, + SQLExportHelpers::OrderExpression(order.expression->GetReturnType(), std::move(child))); + } + window->IgnoreNullsMutable() = expression.IgnoreNulls(); + window->HasIgnoreNullsMutable() = expression.IgnoreNulls(); + window->DistinctMutable() = expression.Distinct(); + window->WindowStartMutable() = expression.WindowStart(); + window->WindowEndMutable() = expression.WindowEnd(); + window->WindowExcludeMutable() = expression.WindowExclude(); + unique_ptr result = std::move(window); + if (SQLExportHelpers::IsSQLRepresentableType(expression.GetReturnType()) && definition->HasBindCallback() && + definition->GetReturnType() != expression.GetReturnType()) { + return RestoreResultType(expression.GetReturnType(), std::move(result), path); + } + return BoundExpressionSQLExportResult::Success(std::move(result)); +} + +} // namespace duckdb diff --git a/src/duckdb/src/planner/sql_export/table_function_sql_export.cpp b/src/duckdb/src/planner/sql_export/table_function_sql_export.cpp new file mode 100644 index 000000000..828f813c1 --- /dev/null +++ b/src/duckdb/src/planner/sql_export/table_function_sql_export.cpp @@ -0,0 +1,343 @@ +#include "duckdb/planner/sql_export/logical_plan_sql_exporter_internal.hpp" +#include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp" +#include "duckdb/catalog/catalog_entry/schema_catalog_entry.hpp" +#include "duckdb/parser/tableref/basetableref.hpp" +#include "duckdb/parser/tableref/at_clause.hpp" +#include "duckdb/parser/expression/operator_expression.hpp" +#include "duckdb/parser/expression/window_expression.hpp" +#include "duckdb/function/table_function.hpp" +#include "duckdb/parser/expression/cast_expression.hpp" +#include "duckdb/parser/expression/columnref_expression.hpp" +#include "duckdb/parser/expression/comparison_expression.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/parser/expression/function_expression.hpp" +#include "duckdb/parser/expression/subquery_expression.hpp" +#include "duckdb/parser/query_node/select_node.hpp" +#include "duckdb/parser/tableref/joinref.hpp" +#include "duckdb/parser/tableref/table_function_ref.hpp" +#include "duckdb/planner/operator/logical_get.hpp" +#include "duckdb/planner/bound_expression_sql_exporter.hpp" +#include "duckdb/planner/expression/bound_constant_expression.hpp" +#include "duckdb/planner/sql_export_helpers.hpp" + +namespace duckdb { + +static string SQLFunctionCallGuard(const LogicalGet &get, bool has_input) { + const auto &signature = get.function.GetSignature(); + const auto argument_count = signature.GetPositionalParameterCount(); + const bool has_expected_parameters = + signature.GetArgs() ? get.parameters.size() >= argument_count : get.parameters.size() == argument_count; + if (!has_input && !has_expected_parameters) { + return "positional_parameters"; + } + if (has_input && !get.function.in_out_function) { + return "input_child"; + } + if (has_input && (get.input_table_types.empty() || get.input_table_types.size() > get.children[0]->types.size())) { + return "input_columns"; + } + if (get.ordinality_idx.IsValid() && get.source_ordinality != OrdinalityType::WITH_ORDINALITY) { + return "ordinality"; + } + if (!get.scan_partition_indices.empty()) { + return "scan_partitions"; + } + { + auto name = get.function.GetQualifiedName(); + if (name.Catalog().empty()) { + return "catalog_identifier"; + } + if (name.Schema().empty()) { + return "schema_identifier"; + } + for (auto &component : name.Path()) { + if (component.empty()) { + return "function_identifier"; + } + } + } + return string(); +} + +static bool AppendTableFunctionColumnPath(vector &path, const LogicalType &parent_type, + const ColumnIndex &index, LogicalType &source_type) { + Identifier name; + LogicalType child_type = LogicalType::INVALID; + if (parent_type.id() == LogicalTypeId::STRUCT) { + auto &children = StructType::GetChildTypes(parent_type); + if (index.HasPrimaryIndex()) { + if (index.GetPrimaryIndex() >= children.size()) { + return false; + } + name = children[index.GetPrimaryIndex()].first; + child_type = children[index.GetPrimaryIndex()].second; + } else { + name = Identifier(index.GetFieldName()); + for (auto &child : children) { + if (child.first == name) { + child_type = child.second; + break; + } + } + } + } else if (parent_type.id() == LogicalTypeId::VARIANT && !index.HasPrimaryIndex()) { + name = Identifier(index.GetFieldName()); + child_type = parent_type; + } else { + return false; + } + path.push_back(std::move(name)); + if (index.GetChildIndexes().empty()) { + source_type = std::move(child_type); + return true; + } + if (index.GetChildIndexes().size() != 1 || child_type.id() == LogicalTypeId::INVALID) { + return false; + } + return AppendTableFunctionColumnPath(path, child_type, index.GetChildIndexes()[0], source_type); +} + +static unique_ptr TableScanColumnToSQL(const ColumnIndex &index, const LogicalType &type, + unique_ptr expression) { + if (!index.IsPushdownExtract()) { + if (index.HasType() && index.GetType() != type) { + return make_uniq(index.GetType(), std::move(expression)); + } + return expression; + } + D_ASSERT(index.ChildIndexCount() == 1); + auto &child = index.GetChildIndex(0); + Value key; + LogicalType child_type; + if (child.HasPrimaryIndex()) { + D_ASSERT(type.id() == LogicalTypeId::STRUCT); + auto field = child.GetPrimaryIndex(); + key = StructType::IsUnnamed(type) ? Value::BIGINT(NumericCast(field + 1)) + : Value(StructType::GetChildName(type, field)); + child_type = StructType::GetChildType(type, field); + } else { + D_ASSERT(type.id() == LogicalTypeId::VARIANT); + key = Value(child.GetFieldName()); + child_type = type; + } + auto extract = make_uniq(ExpressionType::ARRAY_EXTRACT); + extract->GetChildrenMutable().push_back(std::move(expression)); + extract->GetChildrenMutable().push_back(ConstantExpression::FromValue(key)); + return TableScanColumnToSQL(child, child_type, std::move(extract)); +} + +static unique_ptr TableFunctionColumn(const LogicalGet &get, const TableRef &function_ref, + const ColumnIndex &index) { + if (get.GetTable()) { + if (index.IsRowNumberColumn()) { + auto row_number = make_uniq("system", "main", "row_number"); + row_number->WindowStartMutable() = WindowBoundary::UNBOUNDED_PRECEDING; + row_number->WindowEndMutable() = WindowBoundary::CURRENT_ROW_RANGE; + return std::move(row_number); + } + if (!index.IsVirtualColumn()) { + auto &column = get.GetTable()->GetColumn(index.ToLogical()); + auto expression = make_uniq(function_ref.column_name_alias[index.GetPrimaryIndex()], + function_ref.alias); + return TableScanColumnToSQL(index, column.Type(), std::move(expression)); + } + } + if (index.IsVirtualColumn()) { + if (index.IsEmptyColumn()) { + return ConstantExpression::FromValue(Value::INTEGER(1)); + } + auto entry = get.virtual_columns.find(index.GetPrimaryIndex()); + if (entry == get.virtual_columns.end()) { + return nullptr; + } + return make_uniq(entry->second.name, function_ref.alias); + } + auto primary_index = index.GetPrimaryIndex(); + if (primary_index >= function_ref.column_name_alias.size() || primary_index >= get.returned_types.size()) { + return nullptr; + } + vector path {function_ref.alias, function_ref.column_name_alias[primary_index]}; + if (!index.IsPushdownExtract()) { + auto column = make_uniq(std::move(path)); + if (index.HasType() && !get.returned_types[primary_index].EqualsIncludingCollation(index.GetScanType())) { + return make_uniq(index.GetScanType(), std::move(column)); + } + return std::move(column); + } + LogicalType source_type; + if (index.GetChildIndexes().size() != 1 || + !AppendTableFunctionColumnPath(path, get.returned_types[primary_index], index.GetChildIndexes()[0], + source_type)) { + return nullptr; + } + auto column = make_uniq(std::move(path)); + if (!source_type.EqualsIncludingCollation(index.GetScanType())) { + return make_uniq(index.GetScanType(), std::move(column)); + } + return std::move(column); +} + +SQLSourceQueryResult LogicalPlanSQLExportHelpers::ReconstructSQLSource(ClientContext &context, const LogicalGet &get, + unique_ptr input, + const Identifier &relation_alias, + bool source_ordinality) { + if (!get.scan_partition_indices.empty()) { + return {nullptr, "scan_partitions"}; + } + unique_ptr source; + if (get.function.to_sql) { + auto result = get.function.to_sql(context, get); + if (!result.source) { + return {nullptr, + result.unsupported_reason.empty() ? "source_callback_declined" : result.unsupported_reason}; + } + if (input) { + return {nullptr, "custom_source_input"}; + } + source = std::move(result.source); + } else if (auto table = get.GetTable()) { + auto reference = make_uniq(); + reference->SetQualifiedName(table->schema.GetQualifiedName(table->name)); + source = std::move(reference); + } else { + if (get.bind_info) { + return {nullptr, "process_local_input"}; + } + auto guard = SQLFunctionCallGuard(get, input != nullptr); + if (!guard.empty()) { + return {nullptr, std::move(guard)}; + } + auto function = make_uniq(); + vector> parameters; + const auto &signature = get.function.GetArguments(); + const bool table_parameter = + std::find(signature.begin(), signature.end(), LogicalType::TABLE) != signature.end(); + if (input && !table_parameter) { + if (input->column_name_alias.size() < get.input_table_types.size()) { + return {nullptr, "input_columns"}; + } + for (idx_t i = 0; i < get.input_table_types.size(); i++) { + parameters.push_back(make_uniq(input->column_name_alias[i], input->alias)); + } + } else { + bool consumed_input = false; + for (idx_t i = 0; i < get.parameters.size(); i++) { + if (i < signature.size() && signature[i] == LogicalType::TABLE) { + const bool has_available_input = input && !consumed_input && get.projected_input.empty(); + const bool has_input_columns = has_available_input && + input->column_name_alias.size() >= get.input_table_types.size() && + get.input_table_names.size() == get.input_table_types.size(); + if (!has_input_columns) { + return {nullptr, "table_parameter"}; + } + auto query = make_uniq(); + for (idx_t column_idx = 0; column_idx < get.input_table_types.size(); column_idx++) { + auto column = + make_uniq(input->column_name_alias[column_idx], input->alias); + column->SetAlias(get.input_table_names[column_idx]); + query->select_list.push_back(std::move(column)); + } + query->from_table = std::move(input); + input.reset(); + auto subquery = make_uniq(); + subquery->GetSubqueryTypeMutable() = SubqueryType::SCALAR; + subquery->SubqueryMutable() = make_uniq(); + subquery->SubqueryMutable()->node = std::move(query); + parameters.push_back(std::move(subquery)); + consumed_input = true; + continue; + } + auto exported = BoundExpressionSQLExporter::Export(BoundConstantExpression(get.parameters[i]), {}); + if (exported.HasError()) { + return {nullptr, "positional_parameter_expression"}; + } + parameters.push_back(std::move(exported.GetValue())); + } + if (input) { + return {nullptr, "table_parameter"}; + } + } + for (auto ¶meter : get.named_parameters) { + auto exported = BoundExpressionSQLExporter::Export(BoundConstantExpression(parameter.second), {}); + if (exported.HasError()) { + return {nullptr, "named_parameter_expression"}; + } + parameters.push_back(make_uniq(ExpressionType::COMPARE_EQUAL, + make_uniq(parameter.first), + std::move(exported.GetValue()))); + } + function->function = make_uniq(get.function.GetQualifiedName(), std::move(parameters)); + source = std::move(function); + } + if (get.at_clause) { + if (source->type != TableReferenceType::BASE_TABLE) { + return {nullptr, "at_clause"}; + } + source->Cast().at_clause = + make_uniq(get.at_clause->Unit(), ConstantExpression::FromValue(get.at_clause->GetValue())); + } + auto &function_ref = *source; + if (source->type == TableReferenceType::TABLE_FUNCTION) { + source->Cast().with_ordinality = get.ordinality_idx.IsValid() || source_ordinality + ? OrdinalityType::WITH_ORDINALITY + : OrdinalityType::WITHOUT_ORDINALITY; + } else if (get.ordinality_idx.IsValid() || source_ordinality) { + return {nullptr, "source_ordinality"}; + } + function_ref.alias = relation_alias; + function_ref.column_name_alias.clear(); + for (idx_t i = 0; i < get.returned_types.size(); i++) { + function_ref.column_name_alias.emplace_back("column" + std::to_string(i)); + } + if (source_ordinality) { + function_ref.column_name_alias.emplace_back("column" + std::to_string(get.returned_types.size())); + } + auto select = make_uniq(); + for (auto &index : get.GetColumnIds()) { + auto column = TableFunctionColumn(get, function_ref, index); + if (!column) { + return {nullptr, "to_sql_callback_declined_without_guard"}; + } + select->select_list.push_back(std::move(column)); + } + for (auto index : get.projected_input) { + select->select_list.push_back(make_uniq(input->column_name_alias[index], input->alias)); + } + if (source_ordinality) { + select->select_list.push_back( + make_uniq(function_ref.column_name_alias.back(), function_ref.alias)); + } + if (get.extra_info.file_filter_expressions) { + BoundExpressionSQLExportContext export_context; + export_context.client_context = &context; + export_context.resolve_binding = [&](const ColumnBinding &binding) -> optional { + auto index = binding.column_index.GetIndex(); + if (binding.table_index != TableIndex(ExtraOperatorInfo::FILE_FILTER_TABLE_INDEX) || + index >= get.returned_types.size()) { + return {}; + } + return ResolvedSQLColumnReference {{function_ref.alias, function_ref.column_name_alias[index]}, + get.returned_types[index]}; + }; + for (auto &predicate : *get.extra_info.file_filter_expressions) { + auto exported = BoundExpressionSQLExporter::Export(*predicate, export_context); + if (exported.HasError()) { + return {nullptr, "to_sql_callback_declined_without_guard"}; + } + select->where_clause = + SQLExportHelpers::Conjoin(std::move(select->where_clause), std::move(exported.GetValue())); + } + } + + if (!input) { + select->from_table = std::move(source); + return {std::move(select), {}}; + } + auto join = make_uniq(JoinRefType::CROSS); + join->left = std::move(input); + join->right = std::move(source); + select->from_table = std::move(join); + return {std::move(select), {}}; +} + +} // namespace duckdb diff --git a/src/duckdb/src/planner/statement_preprocessor.cpp b/src/duckdb/src/planner/statement_preprocessor.cpp index aa7147940..219cb2ae0 100644 --- a/src/duckdb/src/planner/statement_preprocessor.cpp +++ b/src/duckdb/src/planner/statement_preprocessor.cpp @@ -13,96 +13,12 @@ #include "duckdb/common/string_util.hpp" #include "duckdb/common/file_system.hpp" #include "duckdb/function/function_binder.hpp" -#include "duckdb/parser/statement/transaction_statement.hpp" -#include "duckdb/parser/expression/constant_expression.hpp" -#include "duckdb/parser/statement/set_statement.hpp" -#include "duckdb/common/enums/current_transaction_state.hpp" namespace duckdb { -enum class PreprocessingTransactionHandling : uint8_t { - // Not in a transaction and should wrap in an implicit BEGIN/COMMIT - WRAP_IN_TRANSACTION, - // Already in an active transaction — set invalidation policy for multi-statement bodies - SET_INVALIDATION_POLICY, - // No transaction handling needed (single statement, or no transaction context applies) - NONE -}; - -void AddStatements(vector> &body_statements, - const PreprocessingTransactionHandling transaction_handling, - vector> &result_statements) { - if (transaction_handling == PreprocessingTransactionHandling::WRAP_IN_TRANSACTION) { - auto begin_info = make_uniq( - TransactionType::BEGIN_TRANSACTION, TransactionInvalidationPolicy::ALL_ERRORS_INVALIDATE_TRANSACTION, true); - auto begin_stmt = make_uniq(std::move(begin_info)); - begin_stmt->query = begin_stmt->ToString(); - result_statements.push_back(std::move(begin_stmt)); - } else if (transaction_handling == PreprocessingTransactionHandling::SET_INVALIDATION_POLICY) { - // Here we do a `SET current_transaction_invalidation_policy='ALL_ERRORS_INVALIDATE_TRANSACTION';`, for the - // current transaction, to make sure multistatements/pragmas are fully transactional, and invalidate even with - // minor errors such as binder, parser, etc. - auto set_stmt = make_uniq("current_transaction_invalidation_policy", - ConstantExpression::String("ALL_ERRORS_INVALIDATE_TRANSACTION"), - SetScope::GLOBAL); - set_stmt->query = set_stmt->ToString(); - result_statements.push_back(std::move(set_stmt)); - } - - // insert body_statements into result_statements - result_statements.insert(result_statements.end(), std::make_move_iterator(body_statements.begin()), - std::make_move_iterator(body_statements.end())); - - if (transaction_handling == PreprocessingTransactionHandling::WRAP_IN_TRANSACTION) { - auto commit_info = make_uniq( - TransactionType::COMMIT, TransactionInvalidationPolicy::ALL_ERRORS_INVALIDATE_TRANSACTION, true); - auto commit_stmt = make_uniq(std::move(commit_info)); - commit_stmt->query = commit_stmt->ToString(); - result_statements.push_back(std::move(commit_stmt)); - } else if (transaction_handling == PreprocessingTransactionHandling::SET_INVALIDATION_POLICY) { - auto set_stmt = make_uniq( - "current_transaction_invalidation_policy", ConstantExpression::String("STANDARD_POLICY"), SetScope::GLOBAL); - set_stmt->query = set_stmt->ToString(); - result_statements.push_back(std::move(set_stmt)); - } -} - StatementPreprocessor::StatementPreprocessor(ClientContext &context) : context(context) { } -PreprocessingTransactionHandling GetTransactionHandling(vector> &body_statements, - CurrentTransactionState full_transaction_state, - bool skip_handling = false) { - if (body_statements.size() <= 1 || skip_handling) { - return PreprocessingTransactionHandling::NONE; - } - if (full_transaction_state == NOT_IN_ACTIVE_TRANSACTION) { - return PreprocessingTransactionHandling::WRAP_IN_TRANSACTION; - } - if (full_transaction_state == IN_ACTIVE_TRANSACTION) { - return PreprocessingTransactionHandling::SET_INVALIDATION_POLICY; - } - return PreprocessingTransactionHandling::NONE; -} - -void UnpackMultiStatement(MultiStatement &multi_statement, const CurrentTransactionState current_transaction_state, - vector> &new_statements) { -#ifdef DEBUG // MultiStatement should not contain transaction statements - for (auto &sub_statement : multi_statement.statements) { - D_ASSERT(sub_statement->type != StatementType::TRANSACTION_STATEMENT); - } -#endif - bool has_select = false; - for (auto &stmt : multi_statement.statements) { - if (stmt->type == StatementType::SELECT_STATEMENT) { - // Pivot statements have select, and we don't want to wrap those in transactions. - has_select = true; - } - } - auto handling = GetTransactionHandling(multi_statement.statements, current_transaction_state, has_select); - AddStatements(multi_statement.statements, handling, new_statements); -} - vector> StatementPreprocessor::TryReparsePragma(unique_ptr statement) const { // Try reparsing const auto info = statement->Cast().info->Copy(); @@ -111,9 +27,9 @@ vector> StatementPreprocessor::TryReparsePragma(unique_ const auto bound_info = binder->BindPragma(*info, error_context); if (bound_info->function.query) { // Needs reparsing - FunctionParameters parameters {bound_info->parameters, bound_info->named_parameters}; + FunctionParameters parameters {bound_info->parameters, bound_info->named_parameters, &bound_info->function}; const auto query_to_reparse = bound_info->function.query(context, parameters); - Parser parser(context.GetParserOptions()); + Parser parser(context); parser.ParseQuery(query_to_reparse); return std::move(parser.statements); } @@ -122,68 +38,43 @@ vector> StatementPreprocessor::TryReparsePragma(unique_ return res; } -void StatementPreprocessor::Preprocess(ClientContextLock &lock, vector> &statements, - CurrentTransactionState transaction_context_state) { - // Quick check: do we need preprocessing at all? - bool needs_preprocessing = false; - for (auto &stmt : statements) { - if (stmt->type == StatementType::PRAGMA_STATEMENT || stmt->type == StatementType::MULTI_STATEMENT) { - needs_preprocessing = true; - break; - } +PreprocessedStatement StatementPreprocessor::ExpandStatement(unique_ptr statement) const { + PreprocessedStatement result; + switch (statement->type) { + case StatementType::MULTI_STATEMENT: { + auto &multi_statement = statement->Cast(); + result.statements = std::move(multi_statement.statements); + break; } - if (!needs_preprocessing) { - return; + case StatementType::PRAGMA_STATEMENT: + result.statements = TryReparsePragma(std::move(statement)); + break; + default: + result.statements.push_back(std::move(statement)); + return result; } - - context.RunFunctionInTransactionInternal(lock, - [&] { PreprocessInternal(lock, statements, transaction_context_state); }); -} - -void StatementPreprocessor::PreprocessInternal(ClientContextLock &lock, vector> &statements, - const CurrentTransactionState transaction_context_state) { - CurrentTransactionState chained_transaction_state = NOT_IN_ACTIVE_TRANSACTION; - vector> new_statements; - for (idx_t i = 0; i < statements.size(); i++) { - auto query = statements[i]->query; - const CurrentTransactionState full_transaction_state = - (transaction_context_state == IN_ACTIVE_TRANSACTION || chained_transaction_state == IN_ACTIVE_TRANSACTION) - ? IN_ACTIVE_TRANSACTION - : NOT_IN_ACTIVE_TRANSACTION; - - switch (statements[i]->type) { - case StatementType::PRAGMA_STATEMENT: { - auto reparsed_statements = TryReparsePragma(std::move(statements[i])); - const auto handling = GetTransactionHandling(reparsed_statements, full_transaction_state); - AddStatements(reparsed_statements, handling, new_statements); - break; - } - case StatementType::MULTI_STATEMENT: { - auto &multi_statement = statements[i]->Cast(); - UnpackMultiStatement(multi_statement, full_transaction_state, new_statements); - break; - } - case StatementType::TRANSACTION_STATEMENT: { - auto &transaction_stmt = statements[i]->Cast(); - - if (transaction_stmt.info->type == TransactionType::BEGIN_TRANSACTION) { - new_statements.push_back(std::move(statements[i])); - chained_transaction_state = IN_ACTIVE_TRANSACTION; - break; - } - if (transaction_stmt.info->type == TransactionType::COMMIT || - transaction_stmt.info->type == TransactionType::ROLLBACK) { - chained_transaction_state = NOT_IN_ACTIVE_TRANSACTION; + if (result.statements.size() > 1) { + for (auto &expanded : result.statements) { + // The statements run as one query in one transaction, which a transaction statement would end or take over + if (expanded->type == StatementType::TRANSACTION_STATEMENT) { + throw InvalidInputException( + "A statement that expands into several cannot contain a transaction statement: %s", + expanded->query); } - new_statements.push_back(std::move(statements[i])); - break; - } - default: { - new_statements.push_back(std::move(statements[i])); - } } } + return result; +} - statements = std::move(new_statements); +PreprocessedStatement StatementPreprocessor::PreprocessStatement(ClientContextLock &lock, + unique_ptr statement) { + if (statement->type != StatementType::PRAGMA_STATEMENT && statement->type != StatementType::MULTI_STATEMENT) { + return ExpandStatement(std::move(statement)); + } + // Only PRAGMA and multi-statements touch the catalog and need a transaction + PreprocessedStatement result; + context.RunFunctionInTransactionInternal(lock, [&] { result = ExpandStatement(std::move(statement)); }); + return result; } + } // namespace duckdb diff --git a/src/duckdb/src/planner/subquery/delim_join_cte_rewriter.cpp b/src/duckdb/src/planner/subquery/delim_join_cte_rewriter.cpp index ac68f2b45..8c9dd8106 100644 --- a/src/duckdb/src/planner/subquery/delim_join_cte_rewriter.cpp +++ b/src/duckdb/src/planner/subquery/delim_join_cte_rewriter.cpp @@ -17,6 +17,7 @@ #include "duckdb/planner/expression/bound_columnref_expression.hpp" #include "duckdb/planner/expression/bound_comparison_expression.hpp" #include "duckdb/planner/expression/bound_operator_expression.hpp" +#include "duckdb/planner/expression/expression_barrier.hpp" #include "duckdb/planner/expression_nullability.hpp" #include "duckdb/planner/expression_iterator.hpp" #include "duckdb/planner/filter/expression_filter.hpp" @@ -66,16 +67,9 @@ static BindingReplacementGraph CreateConstructedBindingReplacements(const vector static unique_ptr CreateIdentityProjection(Binder &binder, unique_ptr child, BindingReplacementGraph &replacements) { - child->ResolveOperatorTypes(); - auto bindings = child->GetColumnBindings(); - vector> expressions; - expressions.reserve(bindings.size()); - for (idx_t i = 0; i < bindings.size(); i++) { - expressions.push_back(make_uniq(child->types[i], bindings[i])); - } - auto projection = make_uniq(binder.GenerateTableIndex(), std::move(expressions)); - projection->children.push_back(std::move(child)); + auto projection = LogicalProjection::CreateIdentity(binder.GenerateTableIndex(), std::move(child)); projection->ResolveOperatorTypes(); + auto bindings = projection->children[0]->GetColumnBindings(); replacements = CreateConstructedBindingReplacements(bindings, projection->GetColumnBindings()); return std::move(projection); } @@ -179,10 +173,6 @@ static bool ExpressionReferencesChild(Expression &expr, LogicalOperator &child) return false; } -static bool ExpressionNullPropagatesForChild(Expression &expr, LogicalOperator &child) { - return ExpressionReferencesChild(expr, child) && expr.PropagatesNullValues(); -} - static bool ExpressionNullRejectsDelimJoinRHS(Expression &expr, LogicalComparisonJoin &delim_join) { auto &rhs = *delim_join.children[1]; if (!ExpressionReferencesChild(expr, rhs)) { @@ -193,9 +183,14 @@ static bool ExpressionNullRejectsDelimJoinRHS(Expression &expr, LogicalCompariso } if (expr.GetExpressionClass() == ExpressionClass::BOUND_OPERATOR && expr.GetExpressionType() == ExpressionType::OPERATOR_IS_NOT_NULL) { + auto rhs_output = rhs.GetColumnBindings(); + column_binding_set_t rhs_bindings(rhs_output.begin(), rhs_output.end()); + auto column_becomes_null = [&](const BoundColumnRefExpression &column) { + return column.Depth() == 0 && rhs_bindings.find(column.Binding()) != rhs_bindings.end(); + }; bool null_propagating_child = false; ExpressionIterator::EnumerateChildren(expr, [&](Expression &child) { - null_propagating_child = null_propagating_child || ExpressionNullPropagatesForChild(child, rhs); + null_propagating_child |= ExpressionBecomesNull(child, column_becomes_null); }); return null_propagating_child; } @@ -366,21 +361,80 @@ static void CollectMarkerReferences(LogicalOperator &op, marker_binding_map_t &m } } +static bool RewriteFilterThroughProjections(unique_ptr &expr, + const vector> &projections) { + for (auto &projection : projections) { + bool can_push = true; + ExpressionIterator::VisitExpressionMutable( + expr, [&](BoundColumnRefExpression &colref, unique_ptr &) { + if (colref.Depth() != 0) { + can_push = false; + return; + } + auto &projected = projection.get().GetExpression(colref.Binding()); + if (projected.GetExpressionClass() != ExpressionClass::BOUND_COLUMN_REF) { + can_push = false; + return; + } + auto &input = projected.Cast(); + if (input.Depth() != 0) { + can_push = false; + return; + } + colref.BindingMutable() = input.Binding(); + }); + if (!can_push) { + return false; + } + } + return true; +} + static bool PushEligibleFilterExpressionsIntoDelimJoinInputs(unique_ptr &plan) { auto &filter = plan->Cast(); if (filter.HasProjectionMap()) { return false; } - if (filter.children[0]->type != LogicalOperatorType::LOGICAL_DELIM_JOIN) { + reference input = *filter.children[0]; + vector> projections; + while (input.get().type == LogicalOperatorType::LOGICAL_PROJECTION) { + auto &projection = input.get().Cast(); + projections.push_back(projection); + input = *projection.children[0]; + } + if (input.get().type != LogicalOperatorType::LOGICAL_DELIM_JOIN) { return false; } bool changed = false; - auto &delim_join = filter.children[0]->Cast(); + auto &delim_join = input.get().Cast(); + if (!projections.empty() && delim_join.join_type != JoinType::INNER && delim_join.join_type != JoinType::LEFT) { + return false; + } + for (auto &projection : projections) { + for (auto &expr : projection.get().expressions) { + if (ExpressionBarrier::Required(*expr) || ExpressionBarrier::Contains(*expr)) { + return false; + } + } + } vector> remaining_expressions; auto expressions = std::move(filter.expressions); LogicalFilter::SplitPredicates(expressions); for (auto &expr : expressions) { + if (!projections.empty()) { + if (!ExpressionBarrier::Required(*expr) && !ExpressionBarrier::Contains(*expr)) { + auto rewritten = expr->Copy(); + if (RewriteFilterThroughProjections(rewritten, projections) && + FilterReferencesDelimInput(delim_join, *rewritten)) { + AddFilterToOperator(delim_join.children[0], std::move(rewritten)); + changed = true; + continue; + } + } + remaining_expressions.push_back(std::move(expr)); + continue; + } if (FilterReferencesDelimInput(delim_join, *expr)) { AddFilterToOperator(delim_join.children[0], std::move(expr)); changed = true; @@ -2476,11 +2530,109 @@ static bool SingleJoinRHSIsDeduplicated(LogicalComparisonJoin &join) { return true; } +static bool HasPartitionedRecursiveReferences(LogicalOperator &op, const LogicalRecursiveCTE &cte) { + if (op.type == LogicalOperatorType::LOGICAL_CTE_REF) { + auto &ref = op.Cast(); + if (ref.cte_index == cte.table_index && ref.correlated_columns != cte.correlated_columns.size()) { + return false; + } + } + for (auto &child : op.children) { + if (!HasPartitionedRecursiveReferences(*child, cte)) { + return false; + } + } + return true; +} + +static bool HasPartitionedRecursiveKeys(LogicalRecursiveCTE &cte) { + if (cte.key_targets.empty() && !cte.ref_recurring) { + return true; + } + if (cte.correlated_columns.empty() || cte.column_count < cte.correlated_columns.size()) { + return false; + } + // Decorrelation appends the correlation tuple to both recursive rows and USING KEY targets. + const auto offset = cte.column_count - cte.correlated_columns.size(); + for (idx_t i = offset; i < cte.column_count; i++) { + bool found = false; + for (auto &key : cte.key_targets) { + if (key->GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF && + key->Cast().Binding() == ColumnBinding(cte.table_index, ProjectionIndex(i))) { + found = true; + break; + } + } + if (!found) { + return false; + } + } + return HasPartitionedRecursiveReferences(*cte.children[1], cte); +} + +// Decorrelation partitions the RHS by its domain. Only retain this fact for repeatable, locally owned inputs. +static bool CanRestrictCorrelationDomain(LogicalOperator &op, unordered_set &local_ctes) { + switch (op.type) { + case LogicalOperatorType::LOGICAL_GET: + if (!op.Cast().GetTable()) { + return false; + } + break; + case LogicalOperatorType::LOGICAL_RECURSIVE_CTE: { + auto &cte = op.Cast(); + if (!HasPartitionedRecursiveKeys(cte)) { + return false; + } + local_ctes.insert(cte.table_index); + break; + } + case LogicalOperatorType::LOGICAL_MATERIALIZED_CTE: + local_ctes.insert(op.Cast().table_index); + break; + case LogicalOperatorType::LOGICAL_CTE_REF: + if (!local_ctes.count(op.Cast().cte_index)) { + return false; + } + break; + case LogicalOperatorType::LOGICAL_COMPARISON_JOIN: + case LogicalOperatorType::LOGICAL_PROJECTION: + case LogicalOperatorType::LOGICAL_FILTER: + case LogicalOperatorType::LOGICAL_AGGREGATE_AND_GROUP_BY: + case LogicalOperatorType::LOGICAL_CROSS_PRODUCT: + case LogicalOperatorType::LOGICAL_DISTINCT: + case LogicalOperatorType::LOGICAL_WINDOW: + case LogicalOperatorType::LOGICAL_LIMIT: + case LogicalOperatorType::LOGICAL_ORDER_BY: + case LogicalOperatorType::LOGICAL_UNION: + case LogicalOperatorType::LOGICAL_DUMMY_SCAN: + case LogicalOperatorType::LOGICAL_EMPTY_RESULT: + case LogicalOperatorType::LOGICAL_EXPRESSION_GET: + break; + default: + return false; + } + bool repeatable = true; + LogicalOperatorVisitor::EnumerateExpressions(op, [&](unique_ptr *expr) { + repeatable &= !(*expr)->IsVolatile() && !ExpressionBarrier::Contains(**expr); + ExpressionIterator::VisitExpression(**expr, [&](const BoundFunctionExpression &func) { + repeatable &= !func.Function().RequiresOrderedExecution(); + }); + }); + if (!repeatable) { + return false; + } + for (auto &child : op.children) { + if (!CanRestrictCorrelationDomain(*child, local_ctes)) { + return false; + } + } + return true; +} + DelimJoinCTERewriter::DelimJoinCTERewriter(Binder &binder) : binder(binder) { auto &config = DBConfig::GetConfig(binder.context); - cte_deliminator_enabled = - Settings::Get(binder.context) && - config.options.disabled_optimizers.find(OptimizerType::DELIMINATOR) == config.options.disabled_optimizers.end(); + cte_deliminator_enabled = Settings::Get(binder.context) && + !config.IsOptimizerDisabled(OptimizerType::DELIMINATOR); } BindingReplacementGraph DelimJoinCTERewriter::MaterializeDelimJoinAsCTE(unique_ptr &plan, @@ -2517,10 +2669,14 @@ BindingReplacementGraph DelimJoinCTERewriter::MaterializeDelimJoinAsCTE(unique_p return {}; } generated_dedup_cte_indexes.push_back(dedup_cte_index); + unordered_set local_ctes {dedup_cte_index}; + const bool requires_left_row = join.join_type == JoinType::INNER || join.join_type == JoinType::LEFT || + join.join_type == JoinType::SEMI || join.join_type == JoinType::ANTI || + join.join_type == JoinType::MARK; + // Only RHS partitions are removed; ordinary filter pushdown handles the producer. + const bool can_restrict_input = requires_left_row && CanRestrictCorrelationDomain(*plan->children[1], local_ctes); - plan->children[0]->ResolveOperatorTypes(); auto left_bindings = plan->children[0]->GetColumnBindings(); - auto left_types = plan->children[0]->types; auto visible_left_column_count = left_bindings.size(); vector dedup_column_indices; @@ -2548,20 +2704,12 @@ BindingReplacementGraph DelimJoinCTERewriter::MaterializeDelimJoinAsCTE(unique_p if (!extra_left_expressions.empty()) { auto old_left_bindings = left_bindings; - vector> expressions; - expressions.reserve(left_bindings.size() + extra_left_expressions.size()); - for (idx_t i = 0; i < left_bindings.size(); i++) { - expressions.push_back(make_uniq(left_types[i], left_bindings[i])); - } + auto projection = LogicalProjection::CreateIdentity(binder.GenerateTableIndex(), std::move(plan->children[0])); for (auto &expr : extra_left_expressions) { - expressions.push_back(std::move(expr)); + projection->expressions.push_back(std::move(expr)); } - auto projection = make_uniq(binder.GenerateTableIndex(), std::move(expressions)); - projection->children.push_back(std::move(plan->children[0])); plan->children[0] = std::move(projection); - plan->children[0]->ResolveOperatorTypes(); left_bindings = plan->children[0]->GetColumnBindings(); - left_types = plan->children[0]->types; vector projected_left_bindings( left_bindings.begin(), left_bindings.begin() + NumericCast::difference_type>(old_left_bindings.size())); @@ -2581,15 +2729,9 @@ BindingReplacementGraph DelimJoinCTERewriter::MaterializeDelimJoinAsCTE(unique_p auto left_column_count = left_bindings.size(); auto cte_source_bindings = left_bindings; - vector> cte_source_expressions; - cte_source_expressions.reserve(left_column_count); - for (idx_t i = 0; i < left_column_count; i++) { - cte_source_expressions.push_back(make_uniq(left_types[i], left_bindings[i])); - } - auto cte_source = make_uniq(binder.GenerateTableIndex(), std::move(cte_source_expressions)); - cte_source->children.push_back(std::move(plan->children[0])); + auto cte_source = LogicalProjection::CreateIdentity(binder.GenerateTableIndex(), std::move(plan->children[0])); cte_source->ResolveOperatorTypes(); - left_types = cte_source->types; + auto left_types = cte_source->types; auto cte_index = binder.GenerateTableIndex(); auto cte_name = Identifier("__duckdb_delim_" + to_string(cte_index.index)); @@ -2639,6 +2781,9 @@ BindingReplacementGraph DelimJoinCTERewriter::MaterializeDelimJoinAsCTE(unique_p output_replacements.Merge(cte_output_replacements); auto cte = make_uniq(cte_name, cte_index, left_column_count, std::move(cte_source), std::move(cte_child), CTEMaterialize::CTE_MATERIALIZE_DEFAULT); + if (can_restrict_input) { + cte->filter_dependency = make_uniq(left_cte_ref_index, dedup_child_index); + } plan = std::move(cte); return output_replacements; } diff --git a/src/duckdb/src/planner/subquery/flatten_dependent_join.cpp b/src/duckdb/src/planner/subquery/flatten_dependent_join.cpp index 3b50606c1..69038c5d7 100644 --- a/src/duckdb/src/planner/subquery/flatten_dependent_join.cpp +++ b/src/duckdb/src/planner/subquery/flatten_dependent_join.cpp @@ -1,9 +1,11 @@ #include "duckdb/planner/subquery/flatten_dependent_join.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/planner/subquery/delim_join_cte_rewriter.hpp" #include "duckdb/planner/subquery/column_binding_layout.hpp" #include "duckdb/common/operator/add.hpp" #include "duckdb/common/exception/parser_exception.hpp" +#include "duckdb/execution/expression_executor.hpp" #include "duckdb/function/aggregate/distributive_functions.hpp" #include "duckdb/function/aggregate/distributive_function_utils.hpp" #include "duckdb/function/function_binder.hpp" @@ -17,6 +19,7 @@ #include "duckdb/planner/expression/bound_operator_expression.hpp" #include "duckdb/planner/expression/list.hpp" #include "duckdb/planner/expression_iterator.hpp" +#include "duckdb/planner/expression_nullability.hpp" #include "duckdb/planner/operator/list.hpp" #include "duckdb/planner/logical_plan_verifier.hpp" #include "duckdb/planner/subquery/rewrite_correlated_expressions.hpp" @@ -413,7 +416,7 @@ static unique_ptr CreateRowNumberWindow(Binder &binder, unique_pt vector orders = {}) { auto window = make_uniq(table_index); - auto row_number = RowNumberFun::GetFunction().Bind(binder.context); + auto row_number = GetBuiltinWindowFunction(binder.context, RowNumberFun::Name, {})->Bind(binder.context); row_number->PartitionsMutable() = std::move(partitions); row_number->OrderByMutable() = std::move(orders); row_number->WindowStartMutable() = WindowBoundary::UNBOUNDED_PRECEDING; @@ -790,23 +793,57 @@ void FlattenDependentJoins::AddCorrelatedFirstAggregates(LogicalAggregate &aggr, const vector &state) const { for (idx_t i = 0; i < correlated_columns.size(); i++) { auto &col = correlated_columns[i]; - auto first_aggregate = FirstFunctionGetter::GetFunction(col.type); auto colref = make_uniq(col.name, col.type, state[i]); vector> aggr_children; aggr_children.push_back(std::move(colref)); - BoundAggregateFunction bound_func(first_aggregate); - auto first_fun = make_uniq(std::move(bound_func), std::move(aggr_children), nullptr, - nullptr, AggregateType::NON_DISTINCT); + auto first_aggregate = GetBuiltinAggregateFunction(binder.context, FirstFun::Name, {col.type}); + FunctionBinder function_binder(binder.context); + auto first_fun = function_binder.BindAggregateFunction(std::move(first_aggregate), std::move(aggr_children), + nullptr, AggregateType::NON_DISTINCT); aggr.expressions.push_back(std::move(first_fun)); } } +static bool +ExpressionCanThrowOnNullInput(ClientContext &context, const Expression &expr, + const std::function &column_becomes_null) { + bool can_throw = false; + ExpressionIterator::EnumerateChildren(expr, [&](const Expression &child) { + if (can_throw || !child.CanThrow()) { + return; + } + // CanThrow is conservative for foldable children such as implicit constant casts. + Value constant_value; + if (child.IsConsistent() && child.IsFoldable() && + ExpressionExecutor::TryEvaluateScalar(context, child, constant_value)) { + return; + } + if (!ExpressionBecomesNull(child, column_becomes_null) || + ExpressionCanThrowOnNullInput(context, child, column_becomes_null)) { + // Independent children are evaluated before the parent can propagate NULL. + can_throw = true; + } + }); + return can_throw; +} + FlattenDependentJoins::UnnestingState FlattenDependentJoins::PushDownProjection(unique_ptr &plan, bool propagate_null_values, vector state) { - for (auto &expr : plan->expressions) { - propagate_null_values &= expr->PropagatesNullValues(); + if (propagate_null_values) { + auto child_output = plan->children[0]->GetColumnBindings(); + column_binding_set_t child_bindings(child_output.begin(), child_output.end()); + auto column_becomes_null = [&](const BoundColumnRefExpression &column) { + return column.Depth() == 0 && child_bindings.find(column.Binding()) != child_bindings.end(); + }; + for (auto &expr : plan->expressions) { + if (expr->IsVolatile() || !ExpressionBecomesNull(*expr, column_becomes_null) || + ExpressionCanThrowOnNullInput(binder.context, *expr, column_becomes_null)) { + propagate_null_values = false; + break; + } + } } propagate_null_values &= plan->children[0]->type != LogicalOperatorType::LOGICAL_DEPENDENT_JOIN; @@ -826,7 +863,8 @@ FlattenDependentJoins::UnnestingState FlattenDependentJoins::PushDownAggregate(u vector state) { auto &aggr = plan->Cast(); for (auto &expr : plan->expressions) { - propagate_null_values &= expr->PropagatesNullValues(); + auto &aggr_expr = expr->Cast(); + propagate_null_values &= !aggr_expr.IsVolatile() && aggr_expr.PropagatesNullValues(); } auto result = PushDownChild(plan, propagate_null_values, std::move(state)); @@ -855,17 +893,7 @@ FlattenDependentJoins::UnnestingState FlattenDependentJoins::PushDownAggregate(u return result; } - JoinType join_type = JoinType::INNER; - if (any_join || !propagate_null_values) { - join_type = JoinType::LEFT; - } - for (auto &aggr_exp : aggr.expressions) { - auto &b_aggr_exp = aggr_exp->Cast(); - if (!b_aggr_exp.PropagatesNullValues()) { - join_type = JoinType::LEFT; - break; - } - } + auto join_type = !any_join && propagate_null_values ? JoinType::INNER : JoinType::LEFT; unique_ptr join = make_uniq(join_type); auto left_index = binder.GenerateTableIndex(); @@ -909,8 +937,9 @@ FlattenDependentJoins::UnnestingState FlattenDependentJoins::PushDownAggregate(u auto marker_index = ProjectionIndex(aggr.expressions.size()); FunctionBinder function_binder(binder.context); - aggr.expressions.push_back(function_binder.BindAggregateFunction(CountStarFun::GetFunction(), {}, nullptr, - AggregateType::NON_DISTINCT)); + aggr.expressions.push_back( + function_binder.BindAggregateFunction(GetBuiltinAggregateFunction(binder.context, CountStarFun::Name, {}), + {}, nullptr, AggregateType::NON_DISTINCT)); auto marker_binding = ColumnBinding(aggr.aggregate_index, marker_index); for (idx_t i = 0; i < special_handling_bindings.size(); i++) { replacement_map[special_handling_bindings[i]] = {marker_binding, @@ -1078,12 +1107,22 @@ FlattenDependentJoins::UnnestingState FlattenDependentJoins::PushDownJoin(unique } if (join_type == JoinType::MARK) { - if (!left_requires_domain && right_requires_domain) { + if (right_requires_domain) { auto right_state = PushDownChild(plan, propagate_null_values, state, false, 1); auto left_state = PushDownChild(plan, propagate_null_values, right_state.bindings, false, 0); - AddCorrelatedJoinConditions(plan->Cast(), left_state.bindings, right_state.bindings); + auto &comparison_join = plan->Cast(); + auto conditions = std::move(comparison_join.conditions); + comparison_join.conditions.clear(); + AddCorrelatedJoinConditions(comparison_join, left_state.bindings, right_state.bindings); + // Keep the correlation keys before the IN predicate for per-group NULL and empty-set handling. + comparison_join.mark_types.insert(comparison_join.mark_types.begin(), delim_types.begin(), + delim_types.end()); + for (auto &condition : conditions) { + comparison_join.conditions.push_back(std::move(condition)); + } + RewriteCorrelatedBindings(*plan, left_state.bindings); left_state.replacement_graph.Merge(right_state.replacement_graph); return left_state; } @@ -1179,9 +1218,11 @@ FlattenDependentJoins::UnnestingState FlattenDependentJoins::PushDownLimit(uniqu if (limit.limit_val.Type() == LimitNodeType::CONSTANT_VALUE) { auto limit_val = limit.limit_val.GetConstantValue(); - if (limit.offset_val.Type() == LimitNodeType::CONSTANT_VALUE) { - TryAddOperator::Operation(limit_val, limit.offset_val.GetConstantValue(), limit_val); + if (limit.offset_val.Type() == LimitNodeType::CONSTANT_VALUE && + !TryAddOperator::Operation(limit_val, limit.offset_val.GetConstantValue(), limit_val)) { + limit_val = NumericLimits::Maximum(); } + limit_val = MinValue(limit_val, idx_t(NumericLimits::Maximum())); auto upper_bound = make_uniq(int64_t(limit_val)); condition = BoundComparisonExpression::Create(ExpressionType::COMPARE_LESSTHANOREQUALTO, row_num_ref->Copy(), std::move(upper_bound)); @@ -1241,20 +1282,8 @@ FlattenDependentJoins::UnnestingState FlattenDependentJoins::PushDownSetOperatio } for (idx_t i = 0; i < plan->children.size(); i++) { if (plan->children[i]->type == LogicalOperatorType::LOGICAL_CROSS_PRODUCT) { - auto proj_index = binder.GenerateTableIndex(); - auto bindings = plan->children[i]->GetColumnBindings(); - plan->children[i]->ResolveOperatorTypes(); - auto types = plan->children[i]->types; - vector> expressions; - expressions.reserve(bindings.size()); - D_ASSERT(bindings.size() == types.size()); - - for (idx_t col_idx = 0; col_idx < bindings.size(); col_idx++) { - expressions.push_back(make_uniq(types[col_idx], bindings[col_idx])); - } - auto proj = make_uniq(proj_index, std::move(expressions)); - proj->children.push_back(std::move(plan->children[i])); - plan->children[i] = std::move(proj); + plan->children[i] = + LogicalProjection::CreateIdentity(binder.GenerateTableIndex(), std::move(plan->children[i])); } } @@ -1488,8 +1517,16 @@ FlattenDependentJoins::PushDownCorrelatedNodeInternal(unique_ptrtype) { - case LogicalOperatorType::LOGICAL_UNNEST: case LogicalOperatorType::LOGICAL_FILTER: { + for (auto &expr : plan->expressions) { + if (expr->IsVolatile() || expr->CanThrow()) { + propagate_null_values = false; + break; + } + } + return PushDownChild(plan, propagate_null_values, std::move(state)); + } + case LogicalOperatorType::LOGICAL_UNNEST: { return PushDownChild(plan, propagate_null_values, std::move(state)); } case LogicalOperatorType::LOGICAL_PROJECTION: { diff --git a/src/duckdb/src/planner/subquery/pair_dependent_full_outer_join.cpp b/src/duckdb/src/planner/subquery/pair_dependent_full_outer_join.cpp index b2ffb2228..0d6415473 100644 --- a/src/duckdb/src/planner/subquery/pair_dependent_full_outer_join.cpp +++ b/src/duckdb/src/planner/subquery/pair_dependent_full_outer_join.cpp @@ -1,4 +1,5 @@ #include "duckdb/optimizer/column_binding_replacer.hpp" +#include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/planner/subquery/pair_dependent_full_outer_join.hpp" #include "duckdb/function/window/rows_functions.hpp" #include "duckdb/planner/binder.hpp" @@ -92,7 +93,7 @@ static void CollectPairDependentBindings(Expression &expression, const unordered static unique_ptr AddRowIdentity(Binder &binder, unique_ptr source) { auto window = make_uniq(binder.GenerateTableIndex()); - auto row_number = RowNumberFun::GetFunction().Bind(binder.context); + auto row_number = GetBuiltinWindowFunction(binder.context, RowNumberFun::Name, {})->Bind(binder.context); row_number->WindowStartMutable() = WindowBoundary::UNBOUNDED_PRECEDING; row_number->WindowEndMutable() = WindowBoundary::CURRENT_ROW_ROWS; row_number->SetAlias("__duckdb_pair_rowid"); @@ -226,19 +227,9 @@ PairDependentJoinPlan PairDependentFullOuterJoinBuilder::Build() { condition_replacer.VisitExpression(&condition); auto match_root = LogicalCrossProduct::Create(std::move(left_domain.plan), std::move(right_domain.plan)); - - vector> match_expressions; - for (idx_t i = 0; i < left_domain.bindings.size(); i++) { - match_expressions.push_back(make_uniq(left_domain.types[i], left_domain.bindings[i])); - } - for (idx_t i = 0; i < right_domain.bindings.size(); i++) { - match_expressions.push_back( - make_uniq(right_domain.types[i], right_domain.bindings[i])); - } - match_expressions.push_back(std::move(condition)); unique_ptr match_projection = - make_uniq(binder.GenerateTableIndex(), std::move(match_expressions)); - match_projection->children.push_back(std::move(match_root)); + LogicalProjection::CreateIdentity(binder.GenerateTableIndex(), std::move(match_root)); + match_projection->expressions.push_back(std::move(condition)); RecursiveDependentJoinPlanner::Plan(binder, match_projection); match_projection->ResolveOperatorTypes(); auto match_bindings = match_projection->GetColumnBindings(); diff --git a/src/duckdb/src/planner/table_binding.cpp b/src/duckdb/src/planner/table_binding.cpp index c1cd9ce83..8202018d6 100644 --- a/src/duckdb/src/planner/table_binding.cpp +++ b/src/duckdb/src/planner/table_binding.cpp @@ -65,6 +65,14 @@ void Binding::SetColumnType(idx_t col_idx, LogicalType type_p) { types[col_idx] = std::move(type_p); } +void Binding::SetNullExtended() { + null_extended = true; +} + +bool Binding::IsNullExtended() const { + return null_extended; +} + const Identifier &Binding::GetAlias() const { return alias.GetAlias(); } diff --git a/src/duckdb/src/planner/table_filter_set.cpp b/src/duckdb/src/planner/table_filter_set.cpp index b393b66a7..ef6ef4dbc 100644 --- a/src/duckdb/src/planner/table_filter_set.cpp +++ b/src/duckdb/src/planner/table_filter_set.cpp @@ -24,28 +24,15 @@ namespace duckdb { -struct LegacyStructPathEntry { - idx_t child_idx; - Identifier child_name; -}; - static bool ContainsInternalTableFilterFunction(const Expression &expr) { if (expr.GetExpressionClass() == ExpressionClass::BOUND_FUNCTION) { auto &func = expr.Cast(); if (TableFilterFunctions::IsTableFilterFunction(func.Function())) { return true; } - if (func.Function().GetName() == OptionalFilterScalarFun::NAME && func.BindInfo()) { - auto &data = func.BindInfo()->Cast(); - if (data.child_filter_expr && ContainsInternalTableFilterFunction(*data.child_filter_expr)) { - return true; - } - } - if (func.Function().GetName() == SelectivityOptionalFilterScalarFun::NAME && func.BindInfo()) { - auto &data = func.BindInfo()->Cast(); - if (data.child_filter_expr && ContainsInternalTableFilterFunction(*data.child_filter_expr)) { - return true; - } + auto optional_child = ExpressionFilter::GetOptionalFilterChild(func); + if (optional_child && ContainsInternalTableFilterFunction(*optional_child)) { + return true; } } bool found = false; @@ -89,35 +76,12 @@ static bool IsSupportedConstantComparison(ExpressionType type) { } } -static bool TryExtractLegacySubject(const Expression &expr, vector &struct_path) { - switch (expr.GetExpressionClass()) { - case ExpressionClass::BOUND_REF: - case ExpressionClass::BOUND_COLUMN_REF: - return true; - case ExpressionClass::BOUND_FUNCTION: { - auto &func = expr.Cast(); - idx_t child_idx; - if (!TryGetStructExtractChildIndex(func, child_idx) || func.GetChildren().empty()) { - return false; - } - if (!TryExtractLegacySubject(*func.GetChildren()[0], struct_path)) { - return false; - } - Identifier child_name; - if (func.GetChildren()[0]->GetReturnType().id() == LogicalTypeId::STRUCT && - !StructType::IsUnnamed(func.GetChildren()[0]->GetReturnType())) { - child_name = StructType::GetChildName(func.GetChildren()[0]->GetReturnType(), child_idx); - } - struct_path.push_back({child_idx, std::move(child_name)}); - return true; - } - default: - return false; - } +static bool TryExtractLegacySubject(const Expression &expr, vector &struct_path) { + return ExpressionFilter::IsSimpleFilterColumnRef(PeelStructExtractPath(expr, struct_path)); } static unique_ptr WrapStructFilterPath(unique_ptr filter, - const vector &struct_path) { + const vector &struct_path) { for (auto it = struct_path.rbegin(); it != struct_path.rend(); ++it) { filter = make_uniq(it->child_idx, it->child_name, std::move(filter)); } @@ -149,7 +113,7 @@ static unique_ptr TrySerializeComparisonToLegacyFilter(const BoundF comparison_type = FlipComparisonType(comparison_type); } - vector struct_path; + vector struct_path; if (!TryExtractLegacySubject(subject, struct_path)) { return nullptr; } @@ -176,7 +140,7 @@ static unique_ptr TrySerializeOperatorToLegacyFilter(const BoundOpe if (op.GetChildren().size() != 1) { return nullptr; } - vector struct_path; + vector struct_path; if (!TryExtractLegacySubject(*op.GetChildren()[0], struct_path)) { return nullptr; } @@ -189,7 +153,7 @@ static unique_ptr TrySerializeOperatorToLegacyFilter(const BoundOpe if (op.GetChildren().empty()) { return nullptr; } - vector struct_path; + vector struct_path; if (!TryExtractLegacySubject(*op.GetChildren()[0], struct_path)) { return nullptr; } @@ -223,20 +187,8 @@ static unique_ptr SerializeOptionalChild(const optional_ptr SerializeInternalFunctionToLegacyFilter(const BoundFunctionExpression &func_expr) { auto &func_name = func_expr.Function().GetName(); - if (func_name == OptionalFilterScalarFun::NAME) { - unique_ptr child_filter; - if (func_expr.BindInfo()) { - auto &data = func_expr.BindInfo()->Cast(); - child_filter = SerializeOptionalChild(data.child_filter_expr.get()); - } - return make_uniq(std::move(child_filter)); - } - if (func_name == SelectivityOptionalFilterScalarFun::NAME) { - unique_ptr child_filter; - if (func_expr.BindInfo()) { - auto &data = func_expr.BindInfo()->Cast(); - child_filter = SerializeOptionalChild(data.child_filter_expr.get()); - } + if (ExpressionFilter::IsRootOptionalExpression(func_expr)) { + auto child_filter = SerializeOptionalChild(ExpressionFilter::GetOptionalFilterChild(func_expr)); return make_uniq(std::move(child_filter)); } if (func_name == DynamicFilterScalarFun::NAME) { diff --git a/src/duckdb/src/planner/table_filter_state.cpp b/src/duckdb/src/planner/table_filter_state.cpp index b1694eb93..33bfb58bb 100644 --- a/src/duckdb/src/planner/table_filter_state.cpp +++ b/src/duckdb/src/planner/table_filter_state.cpp @@ -348,7 +348,7 @@ class BloomFilterExecutor final : public ExpressionFilterExecutor { } } - optional_ptr filter; + shared_ptr filter; bool filters_null_values; Vector hashes; SelectionVector bloom_sel; @@ -547,17 +547,15 @@ static unique_ptr TryCreateFunctionExecutor(const Boun return make_uniq(); } if (func_name == SelectivityOptionalFilterScalarFun::NAME) { - if (!func.BindInfo()) { + auto optional_child = ExpressionFilter::GetOptionalFilterChild(func); + if (!optional_child) { return make_uniq(); } - auto &data = func.BindInfo()->Cast(); - if (!data.child_filter_expr) { - return make_uniq(); - } - auto child = TryCreateFastExecutor(*data.child_filter_expr, true); + auto child = TryCreateFastExecutor(*optional_child, true); if (!child) { return nullptr; } + auto &data = func.BindInfo()->Cast(); return make_uniq(std::move(child), data.n_vectors_to_check, data.selectivity_threshold); } diff --git a/src/duckdb/src/storage/arena_allocator.cpp b/src/duckdb/src/storage/arena_allocator.cpp index e5ad43294..29d4b314a 100644 --- a/src/duckdb/src/storage/arena_allocator.cpp +++ b/src/duckdb/src/storage/arena_allocator.cpp @@ -133,6 +133,33 @@ data_ptr_t ArenaAllocator::ReallocateAligned(data_ptr_t pointer, idx_t old_size, return Reallocate(pointer, old_size, AlignValue(size)); } +ArenaAllocator::Position ArenaAllocator::GetPosition() const { + Position result; + if (head) { + result.chunk = head.get(); + result.current_position = head->current_position; + } + return result; +} + +void ArenaAllocator::Rewind(const Position &position) { + if (!position.chunk) { + // nothing was allocated when the position was taken + FreeAll(); + return; + } + // destroy the chunks that were allocated after the position was taken, newest first + while (head.get() != position.chunk) { + D_ASSERT(head && head->next); + allocated_size -= head->maximum_size; + auto older = std::move(head->next); + head = std::move(older); + head->prev = nullptr; + } + D_ASSERT(position.current_position <= head->current_position); + head->current_position = position.current_position; +} + void ArenaAllocator::FreeAll() { Reset(); if (head) { diff --git a/src/duckdb/src/storage/block_allocator.cpp b/src/duckdb/src/storage/block_allocator.cpp index 7cf54028b..60b19d2d7 100644 --- a/src/duckdb/src/storage/block_allocator.cpp +++ b/src/duckdb/src/storage/block_allocator.cpp @@ -329,6 +329,7 @@ data_ptr_t BlockAllocator::AllocateData(const idx_t size) const { } void BlockAllocator::FreeData(const data_ptr_t pointer, const idx_t size) const { + estimated_unflushed_memory.fetch_add(size, std::memory_order_relaxed); if (!IsActive() || !IsInPool(pointer)) { return allocator.FreeData(pointer, size); } @@ -367,6 +368,7 @@ void BlockAllocator::ThreadFlush(bool allocator_background_threads, idx_t thresh } void BlockAllocator::FlushAll(const optional_idx extra_memory) const { + estimated_unflushed_memory.store(0, std::memory_order_relaxed); if (IsActive() && IsEnabled() && extra_memory.IsValid()) { FreeInternal(extra_memory.GetIndex()); } @@ -375,6 +377,39 @@ void BlockAllocator::FlushAll(const optional_idx extra_memory) const { } } +idx_t BlockAllocator::GetEstimatedUnflushedMemory() const { + return estimated_unflushed_memory.load(std::memory_order_relaxed); +} + +bool BlockAllocator::TryFlushDeallocated(const idx_t threshold, const idx_t memory_limit, + const idx_t used_memory) const { + if (!SupportsFlush()) { + return false; + } + auto deallocated = estimated_unflushed_memory.load(std::memory_order_relaxed); + if (deallocated < threshold) { + return false; + } + // the thread that claims the accumulated amount checks whether a flush is needed + if (!estimated_unflushed_memory.compare_exchange_strong(deallocated, 0, std::memory_order_relaxed)) { + return false; + } + if (!IsActive()) { + const auto mapped_memory = allocator.GetMappedMemory(); + if (mapped_memory.IsValid()) { + // Freed pages cannot occupy more than the allocator's entire mapped footprint. + const auto unflushed = MinValue(deallocated, mapped_memory.GetIndex()); + if (used_memory + unflushed <= memory_limit) { + // Keep the upper bound in subsequent pressure checks, adding any concurrent frees. + estimated_unflushed_memory.fetch_add(unflushed, std::memory_order_relaxed); + return false; + } + } + } + FlushAll(deallocated); + return true; +} + void BlockAllocator::FreeInternal(const idx_t extra_memory) const { auto count = DivBlockSize(extra_memory); unsafe_vector to_free_buffer; diff --git a/src/duckdb/src/storage/buffer/block_handle.cpp b/src/duckdb/src/storage/buffer/block_handle.cpp index 1ae4950dc..8bfc529ca 100644 --- a/src/duckdb/src/storage/buffer/block_handle.cpp +++ b/src/duckdb/src/storage/buffer/block_handle.cpp @@ -15,8 +15,7 @@ BlockMemory::BlockMemory(BufferManager &buffer_manager, block_id_t block_id_p, M : buffer_manager(buffer_manager), block_id(block_id_p), state(BlockState::BLOCK_UNLOADED), readers(0), tag(tag_p), buffer_type(FileBufferType::BLOCK), buffer(nullptr), eviction_seq_num(0), has_queue_entry(false), lru_timestamp_msec(), destroy_buffer_upon(DestroyBufferUpon::BLOCK), memory_usage(block_alloc_size_p), - memory_charge(tag, buffer_manager.GetBufferPool()), unswizzled(nullptr), - eviction_queue_idx(DConstants::INVALID_INDEX) { + memory_charge(tag, buffer_manager.GetBufferPool()), eviction_queue_idx(DConstants::INVALID_INDEX) { } BlockMemory::BlockMemory(BufferManager &buffer_manager, block_id_t block_id_p, MemoryTag tag_p, @@ -25,14 +24,11 @@ BlockMemory::BlockMemory(BufferManager &buffer_manager, block_id_t block_id_p, M : buffer_manager(buffer_manager), block_id(block_id_p), state(BlockState::BLOCK_LOADED), readers(0), tag(tag_p), buffer_type(buffer_p->GetBufferType()), buffer(std::move(buffer_p)), eviction_seq_num(0), has_queue_entry(false), lru_timestamp_msec(), destroy_buffer_upon(destroy_buffer_upon_p), memory_usage(size_p), - memory_charge(tag, buffer_manager.GetBufferPool()), unswizzled(nullptr), - eviction_queue_idx(DConstants::INVALID_INDEX) { + memory_charge(tag, buffer_manager.GetBufferPool()), eviction_queue_idx(DConstants::INVALID_INDEX) { memory_charge = std::move(reservation); // Moved to constructor body due to tidy check. } BlockMemory::~BlockMemory() { // NOLINT: allow internal exceptions - // The block memory is being destroyed, meaning that any unswizzled pointers are now binary junk. - SetSwizzling(nullptr); D_ASSERT(!GetBuffer() || GetBuffer()->GetBufferType() == GetBufferType()); if (HasLiveQueueEntry() && GetBufferType() != FileBufferType::TINY_BUFFER) { // The block still has a live entry in the eviction queue. That entry is now dead; @@ -106,22 +102,22 @@ void BlockMemory::ResizeBuffer(BlockLock &l, idx_t block_size, idx_t block_heade D_ASSERT(memory_usage == buffer->AllocSize()); } -bool BlockMemory::CanUnload() const { +CanUnloadResult BlockMemory::CanUnload() const { if (GetState() == BlockState::BLOCK_UNLOADED) { // The block has already been unloaded. - return false; + return CanUnloadResult::ALREADY_UNLOADED; } if (GetReaders() > 0) { // There are active readers. - return false; + return CanUnloadResult::PINNED; } if (BlockId() >= MAXIMUM_BLOCK && MustWriteToTemporaryFile() && !GetBufferManager().HasTemporaryDirectory()) { // The block memory cannot be destroyed upon eviction/unpinning. // In order to unload this block we need to write it to a temporary buffer. // However, no temporary directory is specified, hence, we cannot unload. - return false; + return CanUnloadResult::NO_TEMP_DIRECTORY; } - return true; + return CanUnloadResult::CAN_UNLOAD; } unique_ptr BlockMemory::UnloadAndTakeBlock(BlockLock &l, QueryContext context) { @@ -131,8 +127,7 @@ unique_ptr BlockMemory::UnloadAndTakeBlock(BlockLock &l, QueryContex // The block was already unloaded: nothing to do. return nullptr; } - D_ASSERT(IsSwizzled()); - D_ASSERT(CanUnload()); + D_ASSERT(CanUnload() == CanUnloadResult::CAN_UNLOAD); if (BlockId() >= MAXIMUM_BLOCK && MustWriteToTemporaryFile()) { // This is a temporary block that cannot be destroyed upon evict/unpin. @@ -211,34 +206,37 @@ BufferHandle BlockHandle::LoadFromBuffer(BlockLock &l, data_ptr_t data, unique_p memcpy(block->InternalBuffer(), data, block->AllocSize()); memory.GetBuffer() = std::move(block); memory.SetState(BlockState::BLOCK_LOADED); - memory.SetReaders(1); + memory.SetReaders(l, 1); memory.GetMemoryCharge() = std::move(reservation); return BufferHandle(shared_from_this(), memory.GetBuffer()); } -BufferHandle BlockHandle::Load(QueryContext context, unique_ptr reusable_buffer) { +BufferHandle BlockHandle::Load(BlockLock &l, QueryContext context, unique_ptr reusable_buffer) { + memory.VerifyMutex(l); if (memory.GetState() == BlockState::BLOCK_LOADED) { // The block has already been loaded. D_ASSERT(memory.GetBuffer()); - memory.IncrementReaders(); + memory.IncrementReaders(l); return BufferHandle(shared_from_this(), memory.GetBuffer()); } + if (!memory.CanReload()) { + // the buffer was destroyed upon unpin/evict, or the handle was replaced and its disk block + // may be rewritten in place - the caller must re-resolve the block id + return BufferHandle(); + } + if (BlockId() < MAXIMUM_BLOCK) { auto block = AllocateBlock(block_manager, std::move(reusable_buffer), block_id); block_manager.Read(context, *block); memory.GetBuffer() = std::move(block); } else { - if (!memory.MustWriteToTemporaryFile()) { - // The buffer was destroyed upon unpin/evict, so there is no temporary buffer to read. - return BufferHandle(); - } auto &buffer_manager = memory.GetBufferManager(); auto &buffer = memory.GetBuffer(); buffer = buffer_manager.ReadTemporaryBuffer(context, memory.GetMemoryTag(), *this, std::move(reusable_buffer)); } memory.SetState(BlockState::BLOCK_LOADED); - memory.SetReaders(1); + memory.SetReaders(l, 1); return BufferHandle(shared_from_this(), memory.GetBuffer()); } diff --git a/src/duckdb/src/storage/buffer/block_manager.cpp b/src/duckdb/src/storage/buffer/block_manager.cpp index 44abcf0a5..e20b25bd3 100644 --- a/src/duckdb/src/storage/buffer/block_manager.cpp +++ b/src/duckdb/src/storage/buffer/block_manager.cpp @@ -132,13 +132,29 @@ void BlockManager::UnregisterBlock(block_id_t id) { blocks.erase(id); } +bool BlockManager::UnregisterExpiredBlock(block_id_t id) { + D_ASSERT(id < MAXIMUM_BLOCK); + lock_guard lock(blocks_lock); + auto entry = blocks.find(id); + if (entry == blocks.end()) { + // the block id was already unregistered + return true; + } + if (!entry->second.expired()) { + // a newer handle was registered for this block id - the registration is not ours to remove + return false; + } + blocks.erase(entry); + return true; +} + void BlockManager::UnregisterPersistentBlock(BlockHandle &block) { if (in_destruction) { return; } auto id = block.BlockId(); D_ASSERT(id < MAXIMUM_BLOCK); - UnregisterBlock(id); + UnregisterExpiredBlock(id); } MetadataManager &BlockManager::GetMetadataManager() { diff --git a/src/duckdb/src/storage/buffer/buffer_pool.cpp b/src/duckdb/src/storage/buffer/buffer_pool.cpp index fb71ead11..6106abb0a 100644 --- a/src/duckdb/src/storage/buffer/buffer_pool.cpp +++ b/src/duckdb/src/storage/buffer/buffer_pool.cpp @@ -287,7 +287,7 @@ bool BufferPool::AddToEvictionQueue(BlockLock &lock, shared_ptr &ha queue.IncrementDeadNodes(); } - auto ts = memory.NextEvictionSequenceNumber(); + auto ts = memory.NextEvictionSequenceNumber(lock); if (track_eviction_timestamps) { memory.SetLRUTimestamp(std::chrono::time_point_cast(std::chrono::steady_clock::now()) .time_since_epoch() @@ -350,14 +350,17 @@ BufferPool::EvictionResult BufferPool::EvictObjectCacheEntries(MemoryTag tag, id if (memory_usage.GetUsedMemory(MemoryUsageCaches::NO_FLUSH) <= memory_limit) { if (extra_memory > allocator_bulk_deallocation_flush_threshold) { - block_allocator.FlushAll(extra_memory); + FlushAllocator(extra_memory); } return {true, std::move(r)}; } bool success = false; while (!object_cache->IsEmpty()) { - const idx_t freed_mem = object_cache->EvictToReduceMemory(extra_memory); + const idx_t used = memory_usage.GetUsedMemory(MemoryUsageCaches::NO_FLUSH); + const idx_t overshoot = used > memory_limit ? (used - memory_limit) : 0; + const idx_t target = MaxValue(extra_memory, overshoot); + const idx_t freed_mem = object_cache->EvictToReduceMemory(target); // Break if all entries cannot be evicted. if (freed_mem == 0) { break; @@ -371,7 +374,7 @@ BufferPool::EvictionResult BufferPool::EvictObjectCacheEntries(MemoryTag tag, id if (!success) { r.Resize(0); } else if (extra_memory > allocator_bulk_deallocation_flush_threshold) { - block_allocator.FlushAll(extra_memory); + FlushAllocator(extra_memory); } return {success, std::move(r)}; @@ -379,6 +382,7 @@ BufferPool::EvictionResult BufferPool::EvictObjectCacheEntries(MemoryTag tag, id BufferPool::EvictionResult BufferPool::EvictBlocks(QueryContext context, MemoryTag tag, idx_t extra_memory, idx_t memory_limit, unique_ptr *buffer) { + FlushOnBulkDeallocation(); for (auto &queue : queues) { auto block_result = EvictBlocksInternal(context, *queue, tag, extra_memory, memory_limit, buffer); if (block_result.success) { @@ -399,7 +403,7 @@ BufferPool::EvictionResult BufferPool::EvictBlocksInternal(QueryContext context, if (memory_usage.GetUsedMemory(MemoryUsageCaches::NO_FLUSH) <= memory_limit) { if (extra_memory > allocator_bulk_deallocation_flush_threshold) { - block_allocator.FlushAll(extra_memory); + FlushAllocator(extra_memory); } return {true, std::move(r)}; } @@ -428,7 +432,7 @@ BufferPool::EvictionResult BufferPool::EvictBlocksInternal(QueryContext context, if (!found) { r.Resize(0); } else if (extra_memory > allocator_bulk_deallocation_flush_threshold) { - block_allocator.FlushAll(extra_memory); + FlushAllocator(extra_memory); } return {found, std::move(r)}; @@ -500,13 +504,32 @@ void EvictionQueue::IterateUnloadableBlocks(FN fn) { // This node is the block's live queue entry, and we just dequeued it: the block no longer // has an entry in the queue. Live entries are never counted as dead, so no decrement. handle->SetHasLiveQueueEntry(lock, false); - if (!handle->CanUnload()) { + switch (handle->CanUnload()) { + case CanUnloadResult::CAN_UNLOAD: + break; + case CanUnloadResult::NO_TEMP_DIRECTORY: + // Unpinned temporary block that cannot be offloaded yet: re-enqueue until temp directory is set. + handle->SetHasLiveQueueEntry(lock, true); + q.enqueue(std::move(node)); + return; + case CanUnloadResult::PINNED: + case CanUnloadResult::ALREADY_UNLOADED: // The block cannot be unloaded right now (e.g. it is pinned). It gets a new queue // entry when it is unpinned again. continue; } - if (!fn(node, handle, lock)) { + bool continue_iteration; + try { + continue_iteration = fn(node, handle, lock); + } catch (...) { + // The unload failed (e.g. the temporary directory is full) and the block is still loaded. + // Give it its queue entry back, or it stays un-evictable until the next unpin. + handle->SetHasLiveQueueEntry(lock, true); + q.enqueue(std::move(node)); + throw; + } + if (!continue_iteration) { break; } } @@ -552,6 +575,29 @@ idx_t BufferPool::GetAllocatorBulkDeallocationFlushThreshold() { return allocator_bulk_deallocation_flush_threshold; } +idx_t BufferPool::GetBulkDeallocationFlushThreshold() const { + // freed memory that the allocator still holds must stay a small fraction of the memory limit + return maximum_memory.load(std::memory_order_relaxed) / BULK_DEALLOCATION_FLUSH_DIVISOR; +} + +void BufferPool::FlushAllocator(const optional_idx extra_memory) { + block_allocator.FlushAll(extra_memory); +} + +void BufferPool::FlushOnBulkDeallocation() { + const auto threshold = GetBulkDeallocationFlushThreshold(); + const auto deallocated = block_allocator.GetEstimatedUnflushedMemory(); + if (deallocated < threshold) { + return; + } + // freed memory that the allocator still holds only matters once it could push us past the memory limit + const auto used_memory = memory_usage.GetUsedMemory(MemoryUsageCaches::NO_FLUSH); + if (used_memory + deallocated <= maximum_memory.load(std::memory_order_relaxed)) { + return; + } + block_allocator.TryFlushDeallocated(threshold, maximum_memory.load(std::memory_order_relaxed), used_memory); +} + vector BufferPool::GetEvictionQueueInfo() const { static const char *QUEUE_TYPE_NAMES[] = {"BLOCK_AND_EXTERNAL_FILE", "MANAGED_BUFFER", "TINY_BUFFER"}; vector result; @@ -578,29 +624,36 @@ BufferPool::MemoryUsage::MemoryUsage() { v = 0; } for (auto &cache : memory_usage_caches) { - for (auto &v : cache) { + for (auto &v : cache.counters) { v = 0; } } } void BufferPool::MemoryUsage::UpdateUsedMemory(MemoryTag tag, int64_t size) { - auto tag_idx = (idx_t)tag; - if ((idx_t)AbsValue(size) < MEMORY_USAGE_CACHE_THRESHOLD) { + if (size == 0) { + return; + } + auto tag_idx = static_cast(tag); + if (static_cast(AbsValue(size)) < MEMORY_USAGE_TAG_CACHE_THRESHOLD) { // update cache and update global counter when cache exceeds threshold // Get corresponding cache slot based on current CPU core index // Two threads may access the same cache simultaneously, // ensuring correctness through atomic operations - auto cache_idx = (idx_t)TaskScheduler::GetEstimatedCPUId() % MEMORY_USAGE_CACHE_COUNT; - auto &cache = memory_usage_caches[cache_idx]; + auto cache_idx = static_cast(TaskScheduler::GetEstimatedCPUId()) % MEMORY_USAGE_CACHE_COUNT; + auto &cache = memory_usage_caches[cache_idx].counters; auto new_tag_size = cache[tag_idx].fetch_add(size, std::memory_order_relaxed) + size; - if ((idx_t)AbsValue(new_tag_size) >= MEMORY_USAGE_CACHE_THRESHOLD) { + if (static_cast(AbsValue(new_tag_size)) >= MEMORY_USAGE_TAG_CACHE_THRESHOLD) { // cached tag memory usage exceeds threshold auto tag_size = cache[tag_idx].exchange(0, std::memory_order_relaxed); memory_usage[tag_idx].fetch_add(tag_size, std::memory_order_relaxed); } + if (static_cast(AbsValue(size)) >= MEMORY_USAGE_CACHE_THRESHOLD) { + memory_usage[TOTAL_MEMORY_USAGE_INDEX].fetch_add(size, std::memory_order_relaxed); + return; + } auto new_total_size = cache[TOTAL_MEMORY_USAGE_INDEX].fetch_add(size, std::memory_order_relaxed) + size; - if ((idx_t)AbsValue(new_total_size) >= MEMORY_USAGE_CACHE_THRESHOLD) { + if (static_cast(AbsValue(new_total_size)) >= MEMORY_USAGE_CACHE_THRESHOLD) { // cached total memory usage exceeds threshold auto total_size = cache[TOTAL_MEMORY_USAGE_INDEX].exchange(0, std::memory_order_relaxed); memory_usage[TOTAL_MEMORY_USAGE_INDEX].fetch_add(total_size, std::memory_order_relaxed); diff --git a/src/duckdb/src/storage/checkpoint/write_overflow_strings_to_disk.cpp b/src/duckdb/src/storage/checkpoint/write_overflow_strings_to_disk.cpp index 3c7a9426f..854cb30cb 100644 --- a/src/duckdb/src/storage/checkpoint/write_overflow_strings_to_disk.cpp +++ b/src/duckdb/src/storage/checkpoint/write_overflow_strings_to_disk.cpp @@ -14,6 +14,18 @@ WriteOverflowStringsToDisk::~WriteOverflowStringsToDisk() { D_ASSERT(Exception::UncaughtException() || offset == 0); } +void UncompressedStringSegmentState::InitializeOnDiskBlocks(vector blocks) { + D_ASSERT(on_disk_blocks.empty()); + D_ASSERT(on_disk_block_set.empty()); + D_ASSERT(handles.empty()); + on_disk_block_set.reserve(blocks.size()); + on_disk_block_set.insert(blocks.begin(), blocks.end()); + if (on_disk_block_set.size() != blocks.size()) { + throw DataCorruptionException("Corrupted string segment: duplicate overflow block IDs"); + } + on_disk_blocks = std::move(blocks); +} + shared_ptr UncompressedStringSegmentState::GetHandle(BlockManager &manager_p, block_id_t block_id) { lock_guard lock(block_lock); auto entry = handles.find(block_id); @@ -21,6 +33,10 @@ shared_ptr UncompressedStringSegmentState::GetHandle(BlockManager & return entry->second; } auto &manager = block_manager ? *block_manager : manager_p; + if (on_disk_block_set.find(block_id) == on_disk_block_set.end()) { + throw DataCorruptionException( + "Corrupted uncompressed string segment: overflow string block is not owned by the segment"); + } auto result = manager.RegisterBlock(block_id); handles.insert(make_pair(block_id, result)); return result; @@ -37,6 +53,7 @@ void UncompressedStringSegmentState::RegisterBlock(BlockManager &manager_p, bloc auto result = manager.RegisterBlock(block_id); handles.insert(make_pair(block_id, std::move(result))); on_disk_blocks.push_back(block_id); + on_disk_block_set.insert(block_id); } string UncompressedStringSegmentState::GetSegmentInfo() const { @@ -56,7 +73,10 @@ void UncompressedStringSegmentState::InsertOverflowBlock(block_id_t block_id, re reference UncompressedStringSegmentState::FindOverflowBlock(block_id_t block_id) { auto read_lock = overflow_blocks_lock.GetSharedLock(); auto entry = overflow_blocks.find(block_id); - D_ASSERT(entry != overflow_blocks.end()); + if (entry == overflow_blocks.end()) { + throw DataCorruptionException( + "Corrupted uncompressed string segment: overflow string block is not owned by the segment"); + } return entry->second; } diff --git a/src/duckdb/src/storage/checkpoint_manager.cpp b/src/duckdb/src/storage/checkpoint_manager.cpp index bf5efc78f..7acb7cd93 100644 --- a/src/duckdb/src/storage/checkpoint_manager.cpp +++ b/src/duckdb/src/storage/checkpoint_manager.cpp @@ -50,7 +50,7 @@ ActiveCheckpointWrapper::ActiveCheckpointWrapper(optional_ptr con if (!context) { return; } - checkpoint_connection = make_uniq(db.GetDatabase()); + checkpoint_connection = make_uniq(Connection::CreateInternal(db.GetDatabase())); checkpoint_context = checkpoint_connection->context.get(); } @@ -97,6 +97,10 @@ void ActiveCheckpointWrapper::Commit() { checkpoint_transaction = nullptr; } +optional_ptr ActiveCheckpointWrapper::GetCheckpointContext() const { + return checkpoint_context; +} + void ReorderTableEntries(catalog_entry_vector_t &tables); SingleFileCheckpointWriter::SingleFileCheckpointWriter(QueryContext context, AttachedDatabase &db, @@ -209,6 +213,25 @@ static catalog_entry_vector_t GetCatalogEntries(vector(); + catalog.ScanSchemas(VisibilityBound::AllCommitted(), [&](DuckSchemaEntry &schema) { + if (has_buffered_replays) { + return; + } + schema.Scan(CatalogType::TABLE_ENTRY, VisibilityBound::AllCommitted(), [&](CatalogEntry &entry) { + if (has_buffered_replays || entry.type != CatalogType::TABLE_ENTRY) { + return; + } + auto &table = entry.Cast(); + auto &indexes = table.GetStorage().GetDataTableInfo()->GetIndexes(); + has_buffered_replays = indexes.HasBufferedReplays(); + }); + }); + return has_buffered_replays; +} + void SingleFileCheckpointWriter::CreateCheckpoint() { auto &storage_manager = db.GetStorageManager().Cast(); if (storage_manager.InMemory()) { @@ -218,6 +241,10 @@ void SingleFileCheckpointWriter::CreateCheckpoint() { // don't checkpoint invalidated databases return; } + // A context-free checkpoint cannot persist buffered operations on an unbound index. Keep the WAL instead. + if (!context && HasBufferedIndexReplays(db)) { + return; + } // assert that the checkpoint manager hasn't been used before D_ASSERT(!metadata_writer); @@ -246,6 +273,7 @@ void SingleFileCheckpointWriter::CreateCheckpoint() { // WALStartCheckpoint we will create a transaction for the checkpoint. ActiveCheckpointWrapper active_checkpoint(context, db, transaction_manager); auto has_wal = storage_manager.WALStartCheckpoint(meta_block, options, active_checkpoint); + checkpoint_context = active_checkpoint.GetCheckpointContext(); catalog_entry_vector_t catalog_entries; @@ -385,6 +413,7 @@ void SingleFileCheckpointWriter::CreateCheckpoint() { auto &index_list = table_info->GetIndexes(); index_list.MergeCheckpointDeltas(options.checkpoint_id); } + checkpoint_context = nullptr; active_checkpoint.Commit(); } @@ -599,7 +628,12 @@ void CheckpointWriter::WriteSequence(SequenceCatalogEntry &seq, Serializer &seri void CheckpointReader::ReadSequence(CatalogTransaction transaction, Deserializer &deserializer) { auto info = ReadCreateInfo(deserializer, CatalogType::SEQUENCE_ENTRY, "sequence"); auto &sequence_info = info->Cast(); - catalog.CreateSequence(transaction, sequence_info); + auto entry = catalog.CreateSequence(transaction, sequence_info); + auto &storage_manager = catalog.GetAttached().GetStorageManager(); + if (entry && storage_manager.HasStorageVersion() && + StorageManager::IsPriorToVersion(StorageVersion::V2_0_0, storage_manager.GetStorageVersion())) { + entry->Cast().DeriveLastValue(); + } } //===--------------------------------------------------------------------===// @@ -662,7 +696,7 @@ void CheckpointReader::ReadIndex(CatalogTransaction transaction, Deserializer &d // Create an unbound index and add it to the table. auto unbound_index = make_uniq(std::move(create_info), std::move(index_storage_info), TableIOManager::Get(data_table), data_table.db); - table_info->GetIndexes().AddIndex(std::move(unbound_index), ConstraintCheckMode::DEFAULT); + table_info->GetIndexes().AddIndex(std::move(unbound_index), index.oid, ConstraintCheckMode::DEFAULT); } //===--------------------------------------------------------------------===// @@ -708,12 +742,15 @@ void SingleFileCheckpointWriter::WriteTable(TableCatalogEntry &table, Serializer // Write the table metadata serializer.WriteProperty(100, "table", &table); - // If there is a context available, bind indexes before serialization. - // This is necessary so that buffered index operations are replayed before we checkpoint, otherwise - // we would lose them if there was a restart after this. - if (context && context->transaction.HasActiveTransaction()) { + // Explicit checkpoints bind indexes before serialization, so that buffered index operations are replayed + // and not lost on a restart. During a commit-time checkpoint the caller has no active transaction. + if (context && context->transaction.HasActiveTransaction() && checkpoint_context) { + D_ASSERT(checkpoint_context->transaction.HasActiveTransaction()); + // Bind indexes with checkpoint transaction, which is already running and read-only, so any transaction the + // binder still starts skips start_transaction_lock. + D_ASSERT(MetaTransaction::Get(*checkpoint_context).IsReadOnly()); auto &info = table.GetStorage().GetDataTableInfo(); - info->BindIndexes(*context); + info->BindIndexes(*checkpoint_context); } // FIXME: If we do not have a context, however, the unbound indexes have to be serialized to disk. diff --git a/src/duckdb/src/storage/compression/bitpacking.cpp b/src/duckdb/src/storage/compression/bitpacking.cpp index 655619438..5b903b70f 100644 --- a/src/duckdb/src/storage/compression/bitpacking.cpp +++ b/src/duckdb/src/storage/compression/bitpacking.cpp @@ -81,6 +81,58 @@ static void ValidateBitpackingMode(BitpackingMode mode) { throw DataCorruptionException("Corrupted bitpacking segment: read exceeds the segment row count"); } +[[noreturn]] static void ThrowBitpackingInvalidBoolean() { + throw DataCorruptionException("Corrupted bitpacking segment: BOOL group contains values other than 0 and 1"); +} + +//! BOOL is bitpacked as int8_t - verify from the group header that the group can only decode to 0 and 1. The values of +//! DELTA_FOR groups also depend on the deltas, so these are verified after decoding (VerifyBooleanValues). +template +static void ValidateBooleanGroup(BitpackingMode mode, bitpacking_width_t width, T frame_of_reference, T &constant, + idx_t count) { + auto first = static_cast(frame_of_reference); + int64_t last; + switch (mode) { + case BitpackingMode::CONSTANT: + if (constant == NumericLimits::Minimum()) { + // groups that only contain NULLs store the initial maximum as their constant + constant = 0; + } + first = static_cast(constant); + last = first; + break; + case BitpackingMode::FOR: + if (width > 1) { + ThrowBitpackingInvalidBoolean(); + } + last = first + width; + break; + case BitpackingMode::CONSTANT_DELTA: + last = first + static_cast(count - 1) * static_cast(constant); + break; + case BitpackingMode::DELTA_FOR: + // the deltas between 0 and 1 values are in [-1, 1] + if (width > 2 || first < -1 || first + (int64_t(1) << width) - 1 > 1) { + ThrowBitpackingInvalidBoolean(); + } + return; + default: + ThrowBitpackingInvalidBoolean(); + } + if (first < 0 || first > 1 || last < 0 || last > 1) { + ThrowBitpackingInvalidBoolean(); + } +} + +template +static void VerifyBooleanValues(const T *values, idx_t count) { + for (idx_t i = 0; i < count; i++) { + if (values[i] != 0 && values[i] != 1) { + ThrowBitpackingInvalidBoolean(); + } + } +} + template ::type> static bitpacking_width_t ValidateBitpackingWidth(T stored_width) { if (static_cast(stored_width) > sizeof(T) * 8) { @@ -678,7 +730,9 @@ struct BitpackingScanState : public SegmentScanState { unsafe_array_ptr GetAlgorithmGroup() const { D_ASSERT(!AtEnd()); D_ASSERT(width > 0); - D_ASSERT(payload); + if (!payload) { + throw InternalException("Bitpacking algorithm group has no payload"); + } auto algorithm_group_index = offset / BitpackingPrimitives::BITPACKING_ALGORITHM_GROUP_SIZE; D_ASSERT(algorithm_group_index < algorithm_group_count); auto algorithm_group_offset = algorithm_group_index * algorithm_group_size; @@ -706,8 +760,9 @@ struct BitpackingScanState : public SegmentScanState { explicit BitpackingScanState(BufferHandle handle_p, ColumnSegment &segment) : handle(std::move(handle_p)), reader(CompressionSegmentReader::FromSegment(handle, segment, "bitpacking segment")), - segment_count(segment.count.load()), group_count(segment_count / BITPACKING_METADATA_GROUP_SIZE + - (segment_count % BITPACKING_METADATA_GROUP_SIZE != 0)), + is_bool(segment.GetType().InternalType() == PhysicalType::BOOL), segment_count(segment.count.load()), + group_count(segment_count / BITPACKING_METADATA_GROUP_SIZE + + (segment_count % BITPACKING_METADATA_GROUP_SIZE != 0)), metadata_table_start(GetMetadataTableStart(reader, group_count)), metadata_table(reader.GetArray(metadata_table_start, group_count)) { if (metadata_table_start < BitpackingPrimitives::BITPACKING_HEADER_SIZE) { @@ -721,6 +776,8 @@ struct BitpackingScanState : public SegmentScanState { BufferHandle handle; //! Group data between the segment header and reverse metadata table. CompressionSegmentReader reader; + //! BOOL segments are bitpacked as int8_t, but may only contain 0 and 1 + bool is_bool; //! Segment row count retained for consistent group bounds. idx_t segment_count; @@ -828,6 +885,10 @@ struct BitpackingScanState : public SegmentScanState { // The row count for this group comes from the segment count read from disk. auto group_row_count = MinValue(BITPACKING_METADATA_GROUP_SIZE, segment_count - group_index * BITPACKING_METADATA_GROUP_SIZE); + if (is_bool) { + ValidateBooleanGroup(current_group.mode, current_width, current_frame_of_reference, current_constant, + group_row_count); + } // The payload size comes from the group row count and width read from disk, so calculate and validate it. idx_t algorithm_group_count = 0; @@ -1051,6 +1112,9 @@ void BitpackingScanPartial(ColumnSegment &segment, ColumnScanState &state, idx_t ApplyFrameOfReference(current_results, static_cast(current_group.frame_of_reference)); current_group.delta_offset = static_cast(DeltaDecode(current_results, static_cast(current_group.delta_offset))); + if (scan_state.is_bool) { + VerifyBooleanValues(current_result_ptr, to_scan); + } } else { ApplyFrameOfReference(unsafe_array_ptr(current_result_ptr, to_scan), current_group.frame_of_reference); @@ -1125,6 +1189,9 @@ void BitpackingFetchRow(ColumnSegment &segment, ColumnFetchState &state, row_t r value += static_cast(group.delta_offset); } *current_result_ptr = static_cast(value); + if (scan_state.is_bool) { + VerifyBooleanValues(current_result_ptr, 1); + } } template diff --git a/src/duckdb/src/storage/compression/chimp/chimp.cpp b/src/duckdb/src/storage/compression/chimp/chimp.cpp index 088c6dbdc..1c13d684a 100644 --- a/src/duckdb/src/storage/compression/chimp/chimp.cpp +++ b/src/duckdb/src/storage/compression/chimp/chimp.cpp @@ -7,6 +7,31 @@ namespace duckdb { +void ThrowChimpMetadataBeforeHeader() { + throw DataCorruptionException("Corrupted Chimp segment: metadata ends before the segment header"); +} + +void ThrowChimpLeadingZeroBlockCountOutOfBounds(uint8_t block_count) { + throw DataCorruptionException("Corrupted Chimp segment: leading-zero block count %d exceeds %d", block_count, + ChimpPrimitives::CHIMP_SEQUENCE_SIZE / 8); +} + +void ThrowChimpLeadingZeroCountMismatch(idx_t stored_count, idx_t required_count) { + throw DataCorruptionException( + "Corrupted Chimp segment: leading-zero metadata contains %d values but %d are required", stored_count, + required_count); +} + +void ThrowChimpPackedDataExceedsType(uint8_t leading_zero, uint8_t significant_bits, idx_t bit_width) { + throw DataCorruptionException( + "Corrupted Chimp segment: packed data uses %d leading-zero and %d significant bits for a %d-bit value", + leading_zero, significant_bits, bit_width); +} + +void ThrowChimpLeadingZeroStateMissing() { + throw DataCorruptionException("Corrupted Chimp segment: leading-zero equality has no preceding leading-zero value"); +} + template CompressionFunction GetChimpFunction(PhysicalType data_type) { return CompressionFunction(CompressionType::COMPRESSION_CHIMP, data_type, ChimpInitAnalyze, ChimpAnalyze, diff --git a/src/duckdb/src/storage/compression/compression_segment_reader.cpp b/src/duckdb/src/storage/compression/compression_segment_reader.cpp index d43c1e1ae..a20f1eff9 100644 --- a/src/duckdb/src/storage/compression/compression_segment_reader.cpp +++ b/src/duckdb/src/storage/compression/compression_segment_reader.cpp @@ -73,4 +73,12 @@ void CompressionSegmentReader::Align(idx_t alignment) { position += padding; } +void CompressionSegmentReader::AlignBackward(idx_t alignment) { + if (alignment == 0) { + throw InternalException("CompressionSegmentReader::AlignBackward called with a zero alignment"); + } + auto padding = position % alignment; + position -= padding; +} + } // namespace duckdb diff --git a/src/duckdb/src/storage/compression/dict_fsst.cpp b/src/duckdb/src/storage/compression/dict_fsst.cpp index 0d9cd73f3..71d024ea1 100644 --- a/src/duckdb/src/storage/compression/dict_fsst.cpp +++ b/src/duckdb/src/storage/compression/dict_fsst.cpp @@ -160,17 +160,15 @@ void DictFSSTCompressionStorage::StringFetchRow(ColumnSegment &segment, ColumnFe // Select //===--------------------------------------------------------------------===// void DictFSSTSelect(ColumnSegment &segment, ColumnScanState &state, idx_t vector_count, Vector &result, - const SelectionVector &sel, idx_t sel_count) { + const SelectionVector &sel, idx_t sel_count, idx_t result_offset, ScanVectorType scan_type) { auto &scan_state = state.scan_state->Cast(); - if (scan_state.mode == DictFSSTMode::FSST_ONLY) { - // for FSST only - auto start = state.GetPositionInSegment(); - scan_state.Select(result, start, sel, sel_count); + if (scan_state.mode != DictFSSTMode::FSST_ONLY && scan_type == ScanVectorType::SCAN_ENTIRE_VECTOR) { + // scan + slice, so that we can emit a dictionary vector + DictFSSTCompressionStorage::StringScan(segment, state, vector_count, result); + result.Slice(sel, sel_count); return; } - // fallback: scan + slice - DictFSSTCompressionStorage::StringScan(segment, state, vector_count, result); - result.Slice(sel, sel_count); + scan_state.Select(result, result_offset, state.GetPositionInSegment(), sel, sel_count); } //===--------------------------------------------------------------------===// diff --git a/src/duckdb/src/storage/compression/dict_fsst/decompression.cpp b/src/duckdb/src/storage/compression/dict_fsst/decompression.cpp index 6ae6087ad..d07d8bbcb 100644 --- a/src/duckdb/src/storage/compression/dict_fsst/decompression.cpp +++ b/src/duckdb/src/storage/compression/dict_fsst/decompression.cpp @@ -62,6 +62,10 @@ void CompressedStringScanState::Initialize(bool initialize_dictionary) { dictionary_indices_width = (bitpacking_width_t)(Load(data_ptr_cast(&header_ptr->dictionary_indices_width))); string_lengths_width = (bitpacking_width_t)(Load(data_ptr_cast(&header_ptr->string_lengths_width))); + if (dictionary_indices_width > sizeof(uint32_t) * 8 || string_lengths_width > sizeof(uint32_t) * 8) { + throw IOException("Failed to scan dictionary string - bitpacking width was invalid. Database file appears to " + "be corrupted."); + } auto string_lengths_space = BitpackingPrimitives::GetRequiredSize(dict_count, string_lengths_width); auto dictionary_indices_space = @@ -153,19 +157,25 @@ const SelectionVector &CompressedStringScanState::GetSelVec(idx_t start, idx_t s } void CompressedStringScanState::ScanToFlatVector(Vector &result, idx_t result_offset, idx_t start, idx_t scan_count) { - // Create a decompression buffer of sufficient size if we don't already have one. + Select(result, result_offset, start, *FlatVector::IncrementalSelectionVector(), scan_count); + result.Verify(); +} + +void CompressedStringScanState::Select(Vector &result, idx_t result_offset, idx_t start, const SelectionVector &sel, + idx_t sel_count) { + const auto scan_count = sel_count == 0 ? 0 : sel.get_index(sel_count - 1) + 1; auto &selvec = GetSelVec(start, scan_count); //! (index 0 is reserved for NULL, which we don't have in this mode) const idx_t start_offset = mode == DictFSSTMode::FSST_ONLY ? start + 1 : 0; - auto result_data = FlatVector::Writer(result, scan_count, result_offset); + auto result_data = FlatVector::Writer(result, sel_count, result_offset); if (dictionary) { // We have prepared the full dictionary, we can reference these strings directly auto dictionary_values = FlatVector::GetData(dictionary->data); - for (idx_t i = 0; i < scan_count; i++) { + for (idx_t i = 0; i < sel_count; i++) { // Lookup dict offset in index buffer - auto string_number = selvec.get_index(i + start_offset); + auto string_number = selvec.get_index(start_offset + sel.get_index(i)); if (string_number == 0) { result_data.WriteNull(); continue; @@ -173,9 +183,9 @@ void CompressedStringScanState::ScanToFlatVector(Vector &result, idx_t result_of result_data.WriteStringRef(dictionary_values[string_number]); } } else { - for (idx_t i = 0; i < scan_count; i++) { + for (idx_t i = 0; i < sel_count; i++) { // Lookup dict offset in index buffer - auto string_number = selvec.get_index(start_offset + i); + auto string_number = selvec.get_index(start_offset + sel.get_index(i)); if (string_number == 0) { result_data.WriteNull(); continue; @@ -189,25 +199,6 @@ void CompressedStringScanState::ScanToFlatVector(Vector &result, idx_t result_of result_data.WriteStringRef(FetchStringFromDict(result, decompress_offset, string_number)); } } - result.Verify(); -} - -void CompressedStringScanState::Select(Vector &result, idx_t start, const SelectionVector &sel, idx_t sel_count) { - D_ASSERT(!dictionary); - D_ASSERT(mode == DictFSSTMode::FSST_ONLY); - idx_t start_offset = start + 1; - auto result_data = FlatVector::Writer(result, sel_count); - for (idx_t i = 0; i < sel_count; i++) { - // Lookup dict offset in index buffer - auto string_number = start_offset + sel.get_index(i); - if (decompress_position > string_number) { - throw InternalException("DICT_FSST: not performing a sequential scan?"); - } - for (; decompress_position < string_number; decompress_position++) { - decompress_offset += string_lengths[decompress_position]; - } - result_data.WriteValue(FetchStringFromDict(result, decompress_offset, string_number)); - } } bool CompressedStringScanState::AllowDictionaryScan(idx_t scan_count) { diff --git a/src/duckdb/src/storage/compression/dictionary/analyze.cpp b/src/duckdb/src/storage/compression/dictionary/analyze.cpp index d7ff65164..efbda4dc6 100644 --- a/src/duckdb/src/storage/compression/dictionary/analyze.cpp +++ b/src/duckdb/src/storage/compression/dictionary/analyze.cpp @@ -4,7 +4,7 @@ namespace duckdb { DictionaryAnalyzeState::DictionaryAnalyzeState(BlockManager &block_manager) : AnalyzeState(block_manager), segment_count(0), current_tuple_count(0), current_unique_count(0), - current_dict_size(0), current_width(0), next_width(0) { + current_dict_size(0), heap(block_manager.buffer_manager.GetBufferAllocator()), current_width(0), next_width(0) { } bool DictionaryAnalyzeState::LookupString(string_t str) { diff --git a/src/duckdb/src/storage/compression/dictionary/compression.cpp b/src/duckdb/src/storage/compression/dictionary/compression.cpp index 3c100ed16..5dd79e061 100644 --- a/src/duckdb/src/storage/compression/dictionary/compression.cpp +++ b/src/duckdb/src/storage/compression/dictionary/compression.cpp @@ -146,8 +146,9 @@ idx_t DictionaryCompressionCompressState::Finalize() { D_ASSERT(current_width == BitpackingPrimitives::MinimumBitWidth(index_buffer.size() - 1)); D_ASSERT(DictionaryCompression::HasEnoughSpace(current_segment->count, index_buffer.size(), current_dictionary.size, current_width, info.GetBlockSize())); - D_ASSERT((uint64_t)*max_element(std::begin(selection_buffer), std::end(selection_buffer)) == - index_buffer.size() - 1); + // an empty segment (e.g. the child of a column of empty lists) has no selection entries + D_ASSERT(selection_buffer.empty() || (uint64_t)*max_element(std::begin(selection_buffer), + std::end(selection_buffer)) == index_buffer.size() - 1); // Early-out, if the block is sufficiently full. if (total_size >= info.GetCompactionFlushLimit()) { diff --git a/src/duckdb/src/storage/compression/dictionary/decompression.cpp b/src/duckdb/src/storage/compression/dictionary/decompression.cpp index b01637426..35faf1078 100644 --- a/src/duckdb/src/storage/compression/dictionary/decompression.cpp +++ b/src/duckdb/src/storage/compression/dictionary/decompression.cpp @@ -4,12 +4,18 @@ namespace duckdb { -void CompressedStringScanState::ValidateDictionary(const SelectionVector &sel, const idx_t scan_count) const { +//===--------------------------------------------------------------------===// +// Dictionary Validation +//===--------------------------------------------------------------------===// +void CompressedStringScanState::ValidateDictionaryIndices(const SelectionVector &sel, const idx_t start_offset, + const idx_t scan_count) const { D_ASSERT(sel.IsSet()); + D_ASSERT(start_offset <= sel.Capacity()); + D_ASSERT(scan_count <= sel.Capacity() - start_offset); bool has_error = false; for (idx_t i = 0; i < scan_count; i++) { - const idx_t sel_idx = sel.get_index_unsafe(i); - has_error |= sel_idx >= index_buffer_count; + const idx_t sel_idx = sel.get_index_unsafe(i + start_offset); + has_error |= sel_idx >= layout.index_buffer.size(); } if (has_error) { @@ -23,10 +29,10 @@ void CompressedStringScanState::ValidateIndexBuffer() const { // Only the checks required to avoid out-of-bounds reads when trusting the buffer: offsets must be // monotonically increasing (else a length underflows) and the largest offset must lie within the dictionary. bool has_error = false; - for (uint32_t i = 1; i < index_buffer_count; i++) { - has_error |= index_buffer_ptr[i] < index_buffer_ptr[i - 1]; + for (idx_t i = 1; i < layout.index_buffer.size(); i++) { + has_error |= layout.index_buffer[i] < layout.index_buffer[i - 1]; } - has_error |= index_buffer_ptr[index_buffer_count - 1] > dict.size; + has_error |= layout.index_buffer[layout.index_buffer.size() - 1] > layout.dictionary_reader.Size(); if (has_error) { throw DataCorruptionException( @@ -35,96 +41,121 @@ void CompressedStringScanState::ValidateIndexBuffer() const { } } -uint16_t CompressedStringScanState::GetStringLength(sel_t index) { +//===--------------------------------------------------------------------===// +// String Reading +//===--------------------------------------------------------------------===// +uint32_t CompressedStringScanState::GetStringLength(idx_t index) const { + D_ASSERT(index < layout.index_buffer.size()); if (index == 0) { return 0; } + D_ASSERT(layout.index_buffer[index] >= layout.index_buffer[index - 1]); // Offsets are validated up front by ValidateIndexBuffer, so the length can be read directly. - const auto string_length = index_buffer_ptr[index] - index_buffer_ptr[index - 1]; - return UnsafeNumericCast(string_length); + const auto string_length = layout.index_buffer[index] - layout.index_buffer[index - 1]; + return string_length; } -string_t CompressedStringScanState::FetchStringFromDict(uint32_t dict_offset, uint16_t string_len) { +string_t CompressedStringScanState::FetchStringFromDict(uint32_t dict_offset, uint32_t string_len) const { + D_ASSERT(dict_offset <= layout.dictionary_reader.Size()); + D_ASSERT(string_len <= dict_offset); if (dict_offset == 0) { return string_t(nullptr, 0); } // normal string: read string from this block - auto dict_end = baseptr + dict.end; - auto dict_pos = dict_end - dict_offset; - - auto str_ptr = char_ptr_cast(dict_pos); - return string_t(str_ptr, string_len); + auto string_data = layout.dictionary_reader.GetBytes(layout.dictionary_reader.Size() - dict_offset, string_len); + return string_t(const_char_ptr_cast(string_data.data()), string_len); } -void CompressedStringScanState::Initialize(ColumnSegment &segment, bool initialize_dictionary) { - block_size = segment.GetBlockSize(); - auto block_offset = segment.GetBlockOffset(); - if (block_offset > block_size || DictionaryCompression::DICTIONARY_HEADER_SIZE > block_size - block_offset) { +//===--------------------------------------------------------------------===// +// Segment Layout +//===--------------------------------------------------------------------===// +CompressedStringScanState::SegmentLayout CompressedStringScanState::ReadLayout(const BufferHandle &handle, + const ColumnSegment &segment) { + auto reader = CompressionSegmentReader::FromSegment(handle, segment, "dictionary segment"); + auto header = reader.Read(); + // Index zero represents NULL, so even an all-NULL segment needs one dictionary offset. + if (header.index_buffer_count == 0) { throw DataCorruptionException( "Failed to scan dictionary string - dictionary was out of range. Database file appears to be corrupted."); } - auto segment_capacity = block_size - block_offset; - baseptr = handle->GetDataMutable() + block_offset; - - // Load header values - auto header_ptr = reinterpret_cast(baseptr); - auto index_buffer_offset = Load(data_ptr_cast(&header_ptr->index_buffer_offset)); - index_buffer_count = Load(data_ptr_cast(&header_ptr->index_buffer_count)); - auto stored_width = Load(data_ptr_cast(&header_ptr->bitpacking_width)); - if (index_buffer_count == 0) { - throw DataCorruptionException( - "Failed to scan dictionary string - dictionary was out of range. Database file appears to be corrupted."); - } - auto expected_width = BitpackingPrimitives::MinimumBitWidth(index_buffer_count - 1); - if (stored_width != expected_width) { + // Selections refer to entries in the offset table, so the last index determines the bit width. + auto expected_width = BitpackingPrimitives::MinimumBitWidth(header.index_buffer_count - 1); + if (header.bitpacking_width != expected_width) { throw DataCorruptionException( "Failed to scan dictionary string - bitpacking width was invalid. Database file appears to be " "corrupted."); } - current_width = expected_width; - auto selection_buffer_size = BitpackingPrimitives::GetRequiredSize(segment.count.load(), current_width); - auto expected_index_buffer_offset = DictionaryCompression::DICTIONARY_HEADER_SIZE + selection_buffer_size; - if (index_buffer_offset != expected_index_buffer_offset) { + // The offset table must start at (or after) the header's end. + if (header.index_buffer_offset < reader.Position()) { throw DataCorruptionException( "Failed to scan dictionary string - selection buffer was out of range. Database file appears " "to be corrupted."); } - if (index_buffer_offset > segment_capacity || - index_buffer_count > (segment_capacity - index_buffer_offset) / sizeof(uint32_t)) { - throw DataCorruptionException( - "Failed to scan dictionary string - index was out of range. Database file appears to be corrupted."); + auto selection_reader = + reader.ReadSubReader(header.index_buffer_offset - reader.Position(), "dictionary selections"); + + constexpr auto group_size = BitpackingPrimitives::BITPACKING_ALGORITHM_GROUP_SIZE; + const auto row_count = segment.count.load(); + // Count the final partial group without rounding up the row count, which could overflow. + const auto group_count = row_count / group_size + (row_count % group_size != 0); + const auto group_bytes = group_size * expected_width / 8; + if (group_bytes == 0) { + // With only the NULL entry to select, no bits are encoded. + // The selection reader must therefore be empty, otherwise its size conflicts with the zero bit width. + if (selection_reader.Size() != 0) { + throw DataCorruptionException( + "Failed to scan dictionary string - selection buffer was out of range. Database file appears " + "to be corrupted."); + } + } else { + // Require exactly enough whole groups for the rows. Compare by division to avoid overflowing. + if (selection_reader.Size() % group_bytes != 0 || selection_reader.Size() / group_bytes != group_count) { + throw DataCorruptionException( + "Failed to scan dictionary string - selection buffer was out of range. Database file appears " + "to be corrupted."); + } } - index_buffer_ptr = reinterpret_cast(baseptr + index_buffer_offset); - base_data = data_ptr_cast(baseptr + DictionaryCompression::DICTIONARY_HEADER_SIZE); - dict = DictionaryCompression::GetDictionary(segment, *handle); - auto index_buffer_end = index_buffer_offset + sizeof(uint32_t) * index_buffer_count; - if (dict.end > segment_capacity || dict.size > dict.end || dict.end - dict.size < index_buffer_end) { + // Check that the offset table is aligned and fits within the segment. + auto index_buffer = reader.GetArray(header.index_buffer_offset, header.index_buffer_count); + auto index_buffer_end = header.index_buffer_offset + sizeof(uint32_t) * index_buffer.size(); + // Dictionary bytes grow backwards from dict_end and must not overlap the offset table. + if (header.dict_size > header.dict_end || header.dict_end - header.dict_size < index_buffer_end) { throw DataCorruptionException( "Failed to scan dictionary string - dictionary was out of range. Database file appears to be corrupted."); } - if (!initialize_dictionary) { - // Used by fetch, as fetch will never produce a DictionaryVector - return; - } + // Check that the dictionary fits within the reader, then restrict string reads to its bytes. + auto dictionary_reader = + reader.GetSubReader(header.dict_end - header.dict_size, header.dict_size, "dictionary strings"); + return {expected_width, selection_reader, dictionary_reader, index_buffer}; +} +//===--------------------------------------------------------------------===// +// Scan +//===--------------------------------------------------------------------===// +void CompressedStringScanState::InitializeDictionary(const ColumnSegment &segment) { // Validate the whole index buffer once so the dictionary build below can trust it. ValidateIndexBuffer(); - dictionary = DictionaryVector::CreateReusableDictionary(segment.GetType(), index_buffer_count); - dictionary_size = index_buffer_count; - auto dict_child_data = FlatVector::Writer(dictionary->data, index_buffer_count); - dict_child_data.WriteNull(); - for (uint32_t i = 1; i < index_buffer_count; i++) { - // NOTE: the passing of dict_child_vector, will not be used, its for big strings - uint16_t str_len = GetStringLength(i); - dict_child_data.WriteStringRef(FetchStringFromDict(index_buffer_ptr[i], str_len)); + dictionary = DictionaryVector::CreateReusableDictionary(segment.GetType(), layout.index_buffer.size()); + auto dict_child_data = FlatVector::Writer(dictionary->data, layout.index_buffer.size()); + // A separate validity scan can mark a row selecting index zero valid, so initialize its string slot. + dict_child_data.WriteStringRef(string_t(nullptr, 0)); + FlatVector::SetNull(dictionary->data, 0, true); + for (idx_t i = 1; i < layout.index_buffer.size(); i++) { + const auto str_len = GetStringLength(i); + dict_child_data.WriteStringRef(FetchStringFromDict(layout.index_buffer[i], str_len)); } } template void CompressedStringScanState::ScanToFlatVector(Vector &result, idx_t result_offset, idx_t start, idx_t scan_count) { + D_ASSERT(NEEDS_STRING_OFFSET_CHECK || dictionary); + if (NEEDS_STRING_OFFSET_CHECK) { + D_ASSERT(result.GetVectorType() == VectorType::FLAT_VECTOR); + D_ASSERT(result_offset < FlatVector::GetCapacity(result)); + } // Handling non-bitpacking-group-aligned start values; idx_t start_offset = start % BitpackingPrimitives::BITPACKING_ALGORITHM_GROUP_SIZE; @@ -137,10 +168,12 @@ void CompressedStringScanState::ScanToFlatVector(Vector &result, idx_t result_of sel_vec = make_buffer(decompress_count); } - data_ptr_t src = &base_data[((start - start_offset) * current_width) / 8]; + D_ASSERT(decompress_count <= sel_vec->Capacity()); + auto src = GetSelectionBytes(start, decompress_count); sel_t *sel_vec_ptr = sel_vec->data(); - BitpackingPrimitives::UnPackBuffer(data_ptr_cast(sel_vec_ptr), src, decompress_count, current_width); + BitpackingPrimitives::UnPackBuffer(data_ptr_cast(sel_vec_ptr), src.data(), decompress_count, + layout.current_width); auto result_data = FlatVector::Writer(result, scan_count, result_offset); @@ -149,14 +182,14 @@ void CompressedStringScanState::ScanToFlatVector(Vector &result, idx_t result_of // Lookup dict offset in index buffer auto string_dict_index = sel_vec->get_index(i + start_offset); - bool elem_error = string_dict_index >= index_buffer_count; + bool elem_error = string_dict_index >= layout.index_buffer.size(); string_dict_index = elem_error ? 0 : string_dict_index; - auto str_dict_offset = index_buffer_ptr[string_dict_index]; + auto str_dict_offset = layout.index_buffer[string_dict_index]; if (NEEDS_STRING_OFFSET_CHECK) { - elem_error |= str_dict_offset > dict.size; + elem_error |= str_dict_offset > layout.dictionary_reader.Size(); if (string_dict_index > 0) { - elem_error |= str_dict_offset < index_buffer_ptr[string_dict_index - 1]; + elem_error |= str_dict_offset < layout.index_buffer[string_dict_index - 1]; } // On error, fall back to index/offset 0 so the fetch below stays in bounds. string_dict_index = elem_error ? 0 : string_dict_index; @@ -164,7 +197,7 @@ void CompressedStringScanState::ScanToFlatVector(Vector &result, idx_t result_of } has_error |= elem_error; - const auto str_len = GetStringLength(UnsafeNumericCast(string_dict_index)); + const auto str_len = GetStringLength(string_dict_index); result_data.WriteStringRef(FetchStringFromDict(str_dict_offset, str_len)); } @@ -196,14 +229,29 @@ void CompressedStringScanState::ScanToDictionaryVector(ColumnSegment &segment, V // Scanning 2048 values, emitting a dict vector data_ptr_t dst = data_ptr_cast(sel_vec->data()); - data_ptr_t src = data_ptr_cast(&base_data[((start - start_offset) * current_width) / 8]); + auto src = GetSelectionBytes(start, decompress_count); - BitpackingPrimitives::UnPackBuffer(dst, src, decompress_count, current_width); + BitpackingPrimitives::UnPackBuffer(dst, src.data(), decompress_count, layout.current_width); sel_vec->ShiftLeft(start_offset, scan_count); - ValidateDictionary(*sel_vec, scan_count); + ValidateDictionaryIndices(*sel_vec, 0, scan_count); result.Dictionary(dictionary, *sel_vec, scan_count); } +unsafe_array_ptr CompressedStringScanState::GetSelectionBytes(idx_t start, + idx_t decompress_count) const { + const auto group_size = BitpackingPrimitives::BITPACKING_ALGORITHM_GROUP_SIZE; + const auto group_bytes = group_size * layout.current_width / 8; + D_ASSERT(decompress_count % group_size == 0); + D_ASSERT(group_bytes == 0 || start / group_size <= layout.selection_reader.Size() / group_bytes); + // Start at the group containing the requested row. + // Divide before multiplying to avoid overflowing start * current_width. + const auto source_offset = (start / group_size) * group_bytes; + D_ASSERT(group_bytes == 0 || + decompress_count / group_size <= (layout.selection_reader.Size() - source_offset) / group_bytes); + const auto source_size = BitpackingPrimitives::GetRequiredSize(decompress_count, layout.current_width); + return layout.selection_reader.GetBytes(source_offset, source_size); +} + } // namespace duckdb diff --git a/src/duckdb/src/storage/compression/dictionary_compression.cpp b/src/duckdb/src/storage/compression/dictionary_compression.cpp index 07a1aa4c9..c933468f0 100644 --- a/src/duckdb/src/storage/compression/dictionary_compression.cpp +++ b/src/duckdb/src/storage/compression/dictionary_compression.cpp @@ -122,8 +122,8 @@ void DictionaryCompressionStorage::FinalizeCompress(CompressionState &state_p) { unique_ptr DictionaryCompressionStorage::StringInitScan(const QueryContext &context, ColumnSegment &segment) { auto &buffer_manager = BufferManager::GetBufferManager(segment.GetDatabase()); - auto state = make_uniq(buffer_manager.Pin(segment.GetBlockHandle())); - state->Initialize(segment, true); + auto state = make_uniq(buffer_manager.Pin(context, segment.GetBlockHandle()), segment); + state->InitializeDictionary(segment); return std::move(state); } @@ -137,6 +137,8 @@ void DictionaryCompressionStorage::StringScanPartial(ColumnSegment &segment, Col auto &scan_state = state.scan_state->Cast(); auto start = state.GetPositionInSegment(); + D_ASSERT(start <= segment.count.load()); + D_ASSERT(scan_count <= segment.count.load() - start); if (!ALLOW_DICT_VECTORS || scan_count != STANDARD_VECTOR_SIZE) { scan_state.ScanToFlatVector(result, result_offset, start, scan_count); } else { @@ -154,9 +156,10 @@ void DictionaryCompressionStorage::StringScan(ColumnSegment &segment, ColumnScan //===--------------------------------------------------------------------===// void DictionaryCompressionStorage::StringFetchRow(ColumnSegment &segment, ColumnFetchState &state, row_t row_id, Vector &result, idx_t result_idx) { + D_ASSERT(row_id >= 0); + D_ASSERT(NumericCast(row_id) < segment.count.load()); // fetch a single row from the string segment - CompressedStringScanState scan_state(state.GetOrInsertHandle(segment)); - scan_state.Initialize(segment, false); + CompressedStringScanState scan_state(state.GetOrInsertHandle(segment), segment); scan_state.ScanToFlatVector(result, result_idx, NumericCast(row_id), 1); } diff --git a/src/duckdb/src/storage/compression/fsst.cpp b/src/duckdb/src/storage/compression/fsst.cpp index 099f2aff7..c6c724e5a 100644 --- a/src/duckdb/src/storage/compression/fsst.cpp +++ b/src/duckdb/src/storage/compression/fsst.cpp @@ -11,6 +11,7 @@ #include "duckdb/storage/string_uncompressed.hpp" #include "duckdb/storage/table/column_data_checkpointer.hpp" #include "duckdb/storage/compression/standard_compression_state.hpp" +#include "duckdb/storage/compression/compression_segment_reader.hpp" #include "duckdb/main/settings.hpp" #include "fsst.h" @@ -54,19 +55,12 @@ struct FSSTStorage { static void StringFetchRow(ColumnSegment &segment, ColumnFetchState &state, row_t row_id, Vector &result, idx_t result_idx); static void Select(ColumnSegment &segment, ColumnScanState &state, idx_t vector_count, Vector &result, - const SelectionVector &sel, idx_t sel_count); + const SelectionVector &sel, idx_t sel_count, idx_t result_offset, ScanVectorType scan_type); static void SetDictionary(ColumnSegment &segment, BufferHandle &handle, StringDictionaryContainer container); static StringDictionaryContainer GetDictionary(ColumnSegment &segment, BufferHandle &handle); - static char *FetchStringPointer(StringDictionaryContainer dict, data_ptr_t baseptr, int32_t dict_offset); static bp_delta_offsets_t CalculateBpDeltaOffsets(int64_t last_known_row, idx_t start, idx_t scan_count); - static bool ParseFSSTSegmentHeader(data_ptr_t base_ptr, duckdb_fsst_decoder_t *decoder_out, - bitpacking_width_t *width_out, const idx_t segment_capacity, - const idx_t segment_count); - static bp_delta_offsets_t StartScan(FSSTScanState &scan_state, data_ptr_t base_data, idx_t start, - idx_t vector_count); - static void EndScan(FSSTScanState &scan_state, bp_delta_offsets_t &offsets, idx_t start, idx_t scan_count); }; //===--------------------------------------------------------------------===// @@ -74,7 +68,8 @@ struct FSSTStorage { //===--------------------------------------------------------------------===// struct FSSTAnalyzeState : public AnalyzeState { explicit FSSTAnalyzeState(BlockManager &block_manager) - : AnalyzeState(block_manager), count(0), fsst_string_total_size(0), empty_strings(0) { + : AnalyzeState(block_manager), count(0), fsst_string_heap(block_manager.buffer_manager.GetBufferAllocator()), + fsst_string_total_size(0), empty_strings(0) { } ~FSSTAnalyzeState() override { @@ -161,30 +156,37 @@ idx_t FSSTStorage::StringFinalAnalyze(AnalyzeState &state_p) { size_t output_buffer_size = 7 + 2 * state.fsst_string_total_size; // size as specified in fsst.h - vector fsst_string_sizes; - vector fsst_string_ptrs; - for (auto &str : state.fsst_strings) { - fsst_string_sizes.push_back(str.GetSize()); - fsst_string_ptrs.push_back((unsigned char *)str.GetData()); // NOLINT + // the sample can be large, so all scratch buffers are allocated through the buffer allocator + auto &allocator = state.info.GetBlockManager().buffer_manager.GetBufferAllocator(); + auto fsst_string_sizes_data = allocator.Allocate(string_count * sizeof(size_t)); + auto fsst_string_ptrs_data = allocator.Allocate(string_count * sizeof(unsigned char *)); + auto fsst_string_sizes = reinterpret_cast(fsst_string_sizes_data.get()); + auto fsst_string_ptrs = reinterpret_cast(fsst_string_ptrs_data.get()); + for (idx_t i = 0; i < string_count; i++) { + auto &str = state.fsst_strings[i]; + fsst_string_sizes[i] = str.GetSize(); + fsst_string_ptrs[i] = (unsigned char *)str.GetData(); // NOLINT } - state.fsst_encoder = duckdb_fsst_create(string_count, &fsst_string_sizes[0], &fsst_string_ptrs[0], 0); + state.fsst_encoder = duckdb_fsst_create(string_count, fsst_string_sizes, fsst_string_ptrs, 0); // TODO: do we really need to encode to get a size estimate? - auto compressed_ptrs = vector(string_count, nullptr); - auto compressed_sizes = vector(string_count, 0); - unique_ptr compressed_buffer(new unsigned char[output_buffer_size]); + auto compressed_ptrs_data = allocator.Allocate(string_count * sizeof(unsigned char *)); + auto compressed_sizes_data = allocator.Allocate(string_count * sizeof(size_t)); + auto compressed_ptrs = reinterpret_cast(compressed_ptrs_data.get()); + auto compressed_sizes = reinterpret_cast(compressed_sizes_data.get()); + auto compressed_buffer = allocator.Allocate(output_buffer_size); - auto res = - duckdb_fsst_compress(state.fsst_encoder, string_count, &fsst_string_sizes[0], &fsst_string_ptrs[0], - output_buffer_size, compressed_buffer.get(), &compressed_sizes[0], &compressed_ptrs[0]); + auto res = duckdb_fsst_compress(state.fsst_encoder, string_count, fsst_string_sizes, fsst_string_ptrs, + output_buffer_size, compressed_buffer.get(), compressed_sizes, compressed_ptrs); if (string_count != res) { throw std::runtime_error("FSST output buffer is too small unexpectedly"); } // Sum and and Max compressed lengths - for (auto &size : compressed_sizes) { + for (idx_t i = 0; i < string_count; i++) { + const auto size = compressed_sizes[i]; compressed_dict_size += size; max_compressed_string_length = MaxValue(max_compressed_string_length, size); } @@ -366,6 +368,8 @@ class FSSTCompressionState : public StandardCompressionState { if (total_size >= info.GetCompactionFlushLimit()) { // the block is full enough, don't bother moving around the dictionary + // NOTE: We forgot to call SetDictionary here, so dict_size on disk is stale. + // not worth fixing as it's a legacy encoder, the reader can be bounded by dict_end return info.GetBlockSize(); } @@ -505,18 +509,69 @@ void FSSTStorage::FinalizeCompress(CompressionState &state_p) { //===--------------------------------------------------------------------===// // Scan //===--------------------------------------------------------------------===// +[[noreturn]] static void ThrowInvalidFSSTSegment(const char *reason) { + throw DataCorruptionException("Failed to read FSST string segment - %s. Database file appears to be corrupted.", + reason); +} + struct FSSTScanState : public SegmentScanState { - explicit FSSTScanState(const idx_t string_block_limit) { + struct SegmentLayout { + //! Bitpacking width (read from the header and checked to be at most 32 bits) + bitpacking_width_t width; + //! Bitpacked string lengths (bounded by the row count and width, before decoding individual lengths) + CompressionSegmentReader lengths; + //! Serialized FSST symbol table (bounded by dict_end, narrowed to the imported size on success) + CompressionSegmentReader symbol_table; + //! Compressed string bytes (starts after the symbol table once imported, ends at dict_end) + CompressionSegmentReader dictionary; + }; + + struct ValidatedStringRange { + public: + unsafe_array_ptr GetCompressedString(idx_t index) const { + D_ASSERT(index < dictionary_offsets.size()); + auto current_offset = dictionary_offsets[index]; + auto previous_offset = index > 0 ? dictionary_offsets[index - 1] : preceding_offset; + // Offsets count backwards from the dictionary end, lengths are the differences between offsets. + return dictionary.SubArray(dictionary.size() - current_offset, current_offset - previous_offset); + } + + ValidatedStringRange Slice(idx_t start, idx_t count) const { + D_ASSERT(start <= dictionary_offsets.size()); + D_ASSERT(count <= dictionary_offsets.size() - start); + auto previous_offset = start > 0 ? dictionary_offsets[start - 1] : preceding_offset; + return ValidatedStringRange(dictionary, dictionary_offsets.SubArray(start, count), previous_offset); + } + + private: + friend struct FSSTScanState; + ValidatedStringRange(unsafe_array_ptr dictionary_p, + unsafe_array_ptr dictionary_offsets_p, uint32_t preceding_offset_p) + : dictionary(dictionary_p), dictionary_offsets(dictionary_offsets_p), preceding_offset(preceding_offset_p) { + } + + unsafe_array_ptr dictionary; + unsafe_array_ptr dictionary_offsets; + uint32_t preceding_offset; + }; + + static SegmentLayout ParseFSSTSegmentHeader(CompressionSegmentReader reader, const idx_t segment_count); + static bool TryImportSymbolTable(SegmentLayout &layout, duckdb_fsst_decoder_t *decoder_out); + static ValidatedStringRange DecodeAndValidateOffsets(const SegmentLayout &layout, const uint32_t *lengths, + uint32_t *dictionary_offsets, idx_t decode_count, + uint32_t preceding_offset, bool have_symbol_table); + //! The returned range borrows the decoding buffer and is valid until the next ValidateRange call. + ValidatedStringRange ValidateRange(idx_t start, idx_t scan_count); + + explicit FSSTScanState(BufferHandle handle_p, const SegmentLayout &layout, buffer_ptr decoder_p) + : duckdb_fsst_decoder(std::move(decoder_p)), handle(std::move(handle_p)), layout(layout) { ResetStoredDelta(); - decompress_buffer.resize(string_block_limit + 1); } buffer_ptr duckdb_fsst_decoder; void *duckdb_fsst_decoder_ptr = nullptr; BufferHandle handle; - - vector decompress_buffer; - bitpacking_width_t current_width; + SegmentLayout layout; // To speed up delta decoding we store the last index uint32_t last_known_index; @@ -537,17 +592,13 @@ struct FSSTScanState : public SegmentScanState { last_known_index = 0; last_known_row = -1; } - inline string_t DecompressString(StringDictionaryContainer dict, data_ptr_t baseptr, - const bp_delta_offsets_t &offsets, idx_t index, + inline string_t DecompressString(unsafe_array_ptr compressed_string, ArenaAllocator &str_allocator) const { - uint32_t str_len = bitunpack_buffer[offsets.scan_offset + index]; - auto str_ptr = FSSTStorage::FetchStringPointer( - dict, baseptr, - UnsafeNumericCast(delta_decode_buffer[index + offsets.unused_delta_decoded_values])); - + auto str_len = compressed_string.size(); if (str_len == 0) { return string_t(nullptr, 0); } + auto str_ptr = const_char_ptr_cast(compressed_string.data()); if (all_values_inlined) { return FSSTPrimitives::DecompressInlinedValue(duckdb_fsst_decoder_ptr, str_ptr, str_len); } else { @@ -557,27 +608,15 @@ struct FSSTScanState : public SegmentScanState { }; unique_ptr FSSTStorage::StringInitScan(const QueryContext &context, ColumnSegment &segment) { - auto block_size = segment.GetBlockSize(); - auto block_offset = segment.GetBlockOffset(); - if (block_offset > block_size) { - throw DataCorruptionException( - "Failed to read FSST string segment - header was out of range. Database file appears to be " - "corrupted."); - } - auto segment_capacity = block_size - block_offset; - auto string_block_limit = StringUncompressed::GetStringBlockLimit(block_size); - auto state = make_uniq(string_block_limit); auto &buffer_manager = BufferManager::GetBufferManager(segment.GetDatabase()); - state->handle = buffer_manager.Pin(segment.GetBlockHandle()); - auto base_ptr = state->handle.GetDataMutable() + block_offset; - - state->duckdb_fsst_decoder = make_buffer(); - auto decoder = reinterpret_cast(state->duckdb_fsst_decoder.get()); - auto retval = - ParseFSSTSegmentHeader(base_ptr, decoder, &state->current_width, segment_capacity, segment.count.load()); - if (!retval) { - state->duckdb_fsst_decoder = nullptr; - } + auto handle = buffer_manager.Pin(context, segment.GetBlockHandle()); + auto layout = FSSTScanState::ParseFSSTSegmentHeader(CompressionSegmentReader::FromSegment(handle, segment, "FSST"), + segment.count.load()); + auto decoder = make_buffer(); + if (!FSSTScanState::TryImportSymbolTable(layout, decoder.get())) { + decoder = nullptr; + } + auto state = make_uniq(std::move(handle), layout, std::move(decoder)); state->duckdb_fsst_decoder_ptr = state->duckdb_fsst_decoder.get(); const auto &stats = segment.GetStats(); @@ -588,49 +627,87 @@ unique_ptr FSSTStorage::StringInitScan(const QueryContext &con return std::move(state); } -void DeltaDecodeIndices(uint32_t *buffer_in, uint32_t *buffer_out, idx_t decode_count, uint32_t last_known_value) { - buffer_out[0] = buffer_in[0]; - buffer_out[0] += last_known_value; - for (idx_t i = 1; i < decode_count; i++) { - buffer_out[i] = buffer_in[i] + buffer_out[i - 1]; +FSSTScanState::ValidatedStringRange +FSSTScanState::DecodeAndValidateOffsets(const SegmentLayout &layout, const uint32_t *lengths, + uint32_t *dictionary_offsets, idx_t decode_count, uint32_t preceding_offset, + bool have_symbol_table) { + auto dictionary_size = layout.dictionary.Size(); + if (preceding_offset > dictionary_size) { + ThrowInvalidFSSTSegment("dictionary offset was out of range"); + } + auto dictionary_offset = preceding_offset; + for (idx_t i = 0; i < decode_count; i++) { + auto string_length = lengths[i]; + // Check the remaining dictionary bytes before adding the length so the offset cannot wrap. + if (string_length > dictionary_size - dictionary_offset) { + ThrowInvalidFSSTSegment("string length exceeded the remaining dictionary bytes"); + } + dictionary_offset += string_length; + dictionary_offsets[i] = dictionary_offset; + } + if (!have_symbol_table && dictionary_offset != preceding_offset) { + ThrowInvalidFSSTSegment("nonempty string had no symbol table"); } + return ValidatedStringRange(layout.dictionary.GetBytes(0, dictionary_size), + unsafe_array_ptr(dictionary_offsets, decode_count), preceding_offset); } -void BitUnpackRange(data_ptr_t src_ptr, data_ptr_t dst_ptr, idx_t count, idx_t row, bitpacking_width_t width) { - auto bitunpack_src_ptr = &src_ptr[(row * width) / 8]; - BitpackingPrimitives::UnPackBuffer(dst_ptr, bitunpack_src_ptr, count, width); +void BitUnpackRange(const CompressionSegmentReader &lengths, data_ptr_t dst_ptr, idx_t count, idx_t row, + bitpacking_width_t width) { + constexpr auto group_size = BitpackingPrimitives::BITPACKING_ALGORITHM_GROUP_SIZE; + D_ASSERT(row % group_size == 0); + D_ASSERT(width <= sizeof(uint32_t) * 8); + auto start_group = row / group_size; + // Include the final partial group because UnPackBuffer reads a full group even for fewer values. + auto group_count = count / group_size + (count % group_size != 0); + auto bytes_per_group = (group_size * static_cast(width)) / 8; + // Unpacking reads complete groups. Check their range before multiplying by the group byte size. + if (DUCKDB_LIKELY(bytes_per_group > 0)) { + auto available_groups = lengths.Size() / bytes_per_group; + if (start_group > available_groups || group_count > available_groups - start_group) { + ThrowInvalidFSSTSegment("bitpacking buffer was out of range"); + } + } + auto source = lengths.GetBytes(start_group * bytes_per_group, group_count * bytes_per_group); + BitpackingPrimitives::UnPackBuffer(dst_ptr, source.data(), count, width); } //===--------------------------------------------------------------------===// // Scan base data //===--------------------------------------------------------------------===// -bp_delta_offsets_t FSSTStorage::StartScan(FSSTScanState &scan_state, data_ptr_t base_data, idx_t start, - idx_t scan_count) { - if (start == 0 || scan_state.last_known_row >= (int64_t)start) { - scan_state.ResetStoredDelta(); - } - - auto offsets = CalculateBpDeltaOffsets(scan_state.last_known_row, start, scan_count); - - if (scan_state.bitunpack_buffer_capacity < offsets.total_bitunpack_count) { - scan_state.bitunpack_buffer = make_unsafe_uniq_array(offsets.total_bitunpack_count); - scan_state.bitunpack_buffer_capacity = offsets.total_bitunpack_count; +FSSTScanState::ValidatedStringRange FSSTScanState::ValidateRange(idx_t start, idx_t scan_count) { + if (scan_count == 0) { + // No decoding buffer is needed for an empty range, leave the cached position unchanged. + return ValidatedStringRange(layout.dictionary.GetBytes(0, layout.dictionary.Size()), + unsafe_array_ptr(&last_known_index, 0), last_known_index); } - BitUnpackRange(base_data, data_ptr_cast(scan_state.bitunpack_buffer.get()), offsets.total_bitunpack_count, - offsets.bitunpack_start_row, scan_state.current_width); - if (scan_state.delta_decode_capacity < offsets.total_delta_decode_count) { - scan_state.delta_decode_buffer = make_unsafe_uniq_array(offsets.total_delta_decode_count); - scan_state.delta_decode_capacity = offsets.total_delta_decode_count; + // Offsets are cumulative, so a cached offset at or past the requested start cannot be reused. + if (start == 0 || last_known_row >= (int64_t)start) { + ResetStoredDelta(); } - DeltaDecodeIndices(scan_state.bitunpack_buffer.get() + offsets.bitunpack_alignment_offset, - scan_state.delta_decode_buffer.get(), offsets.total_delta_decode_count, - scan_state.last_known_index); - return offsets; -} -void FSSTStorage::EndScan(FSSTScanState &scan_state, bp_delta_offsets_t &offsets, idx_t start, idx_t scan_count) { - scan_state.StoreLastDelta(scan_state.delta_decode_buffer[scan_count + offsets.unused_delta_decoded_values - 1], - UnsafeNumericCast(start + scan_count - 1)); + // Decode from the cached position through the requested range, including any skipped rows. + // Unpacking also includes padding to complete the first and last bitpacking groups. + const auto offsets = FSSTStorage::CalculateBpDeltaOffsets(last_known_row, start, scan_count); + + if (bitunpack_buffer_capacity < offsets.total_bitunpack_count) { + bitunpack_buffer = make_unsafe_uniq_array(offsets.total_bitunpack_count); + bitunpack_buffer_capacity = offsets.total_bitunpack_count; + } + BitUnpackRange(layout.lengths, data_ptr_cast(bitunpack_buffer.get()), offsets.total_bitunpack_count, + offsets.bitunpack_start_row, layout.width); + if (delta_decode_capacity < offsets.total_delta_decode_count) { + delta_decode_buffer = make_unsafe_uniq_array(offsets.total_delta_decode_count); + delta_decode_capacity = offsets.total_delta_decode_count; + } + const auto strings = DecodeAndValidateOffsets(layout, bitunpack_buffer.get() + offsets.bitunpack_alignment_offset, + delta_decode_buffer.get(), offsets.total_delta_decode_count, + last_known_index, duckdb_fsst_decoder != nullptr); + // Cache the final offset only after all decoded lengths have passed validation. + StoreLastDelta(delta_decode_buffer[offsets.total_delta_decode_count - 1], + UnsafeNumericCast(start + scan_count - 1)); + // Preceding rows were needed to calculate offsets, but are not part of the requested range. + return strings.Slice(offsets.unused_delta_decoded_values, scan_count); } template @@ -638,17 +715,15 @@ void FSSTStorage::StringScanPartial(ColumnSegment &segment, ColumnScanState &sta idx_t result_offset) { auto &scan_state = state.scan_state->Cast(); auto start = state.GetPositionInSegment(); + D_ASSERT(start <= segment.count.load()); + D_ASSERT(scan_count <= segment.count.load() - start); - bool enable_fsst_vectors; - if (ALLOW_FSST_VECTORS) { + bool enable_fsst_vectors = false; + // With a decoder and when enabled, use the FSST vector path. + if (ALLOW_FSST_VECTORS && scan_state.duckdb_fsst_decoder) { enable_fsst_vectors = Settings::Get(segment.GetDatabase()); - } else { - enable_fsst_vectors = false; } - auto baseptr = scan_state.handle.GetDataMutable() + segment.GetBlockOffset(); - auto dict = GetDictionary(segment, scan_state.handle); - auto base_data = data_ptr_cast(baseptr + sizeof(fsst_compression_header_t)); string_t *result_data; if (scan_count == 0) { @@ -657,41 +732,32 @@ void FSSTStorage::StringScanPartial(ColumnSegment &segment, ColumnScanState &sta if (enable_fsst_vectors) { D_ASSERT(result_offset == 0); - if (scan_state.duckdb_fsst_decoder) { - D_ASSERT(result_offset == 0 || result.GetVectorType() == VectorType::FSST_VECTOR); - auto string_block_limit = StringUncompressed::GetStringBlockLimit(segment.GetBlockSize()); - FSSTVector::Create(result, scan_state.duckdb_fsst_decoder, string_block_limit, scan_count); - result_data = FSSTVector::GetCompressedData(result); - } else { - D_ASSERT(result.GetVectorType() == VectorType::FLAT_VECTOR); - result_data = FlatVector::GetDataMutable(result); - } + auto string_block_limit = StringUncompressed::GetStringBlockLimit(segment.GetBlockSize()); + FSSTVector::Create(result, scan_state.duckdb_fsst_decoder, string_block_limit, scan_count); + result_data = FSSTVector::GetCompressedData(result); } else { D_ASSERT(result.GetVectorType() == VectorType::FLAT_VECTOR); result_data = FlatVector::GetDataMutable(result); } - auto offsets = StartScan(scan_state, base_data, start, scan_count); - auto &bitunpack_buffer = scan_state.bitunpack_buffer; - auto &delta_decode_buffer = scan_state.delta_decode_buffer; + auto strings = scan_state.ValidateRange(start, scan_count); if (enable_fsst_vectors) { - // Lookup decompressed offsets in dict + // The result could outlive the ScanState, so keep the segment block pinned with the result vector + auto &buffer_manager = BufferManager::GetBufferManager(segment.GetDatabase()); + StringVector::AddHandle(result, buffer_manager.Pin(state.context, segment.GetBlockHandle())); for (idx_t i = 0; i < scan_count; i++) { - uint32_t string_length = bitunpack_buffer[i + offsets.scan_offset]; - result_data[i] = UncompressedStringStorage::FetchStringFromDict( - state.context, segment, dict.end, result, baseptr, - UnsafeNumericCast(delta_decode_buffer[i + offsets.unused_delta_decoded_values]), - string_length); + auto compressed_string = strings.GetCompressedString(i); + result_data[i] = string_t(const_char_ptr_cast(compressed_string.data()), + NumericCast(compressed_string.size())); } FSSTVector::SetCount(result, scan_count); } else { // Just decompress auto &str_allocator = StringVector::GetStringAllocator(result); for (idx_t i = 0; i < scan_count; i++) { - result_data[i + result_offset] = scan_state.DecompressString(dict, baseptr, offsets, i, str_allocator); + result_data[i + result_offset] = scan_state.DecompressString(strings.GetCompressedString(i), str_allocator); } } - EndScan(scan_state, offsets, start, scan_count); } void FSSTStorage::StringScan(ColumnSegment &segment, ColumnScanState &state, idx_t scan_count, Vector &result) { @@ -702,25 +768,23 @@ void FSSTStorage::StringScan(ColumnSegment &segment, ColumnScanState &state, idx // Select //===--------------------------------------------------------------------===// void FSSTStorage::Select(ColumnSegment &segment, ColumnScanState &state, idx_t vector_count, Vector &result, - const SelectionVector &sel, idx_t sel_count) { + const SelectionVector &sel, idx_t sel_count, idx_t result_offset, ScanVectorType) { auto &scan_state = state.scan_state->Cast(); auto start = state.GetPositionInSegment(); - - auto baseptr = scan_state.handle.GetDataMutable() + segment.GetBlockOffset(); - auto dict = GetDictionary(segment, scan_state.handle); - auto base_data = data_ptr_cast(baseptr + sizeof(fsst_compression_header_t)); - + D_ASSERT(start <= segment.count.load()); + D_ASSERT(vector_count <= segment.count.load() - start); + D_ASSERT(sel_count <= vector_count); D_ASSERT(result.GetVectorType() == VectorType::FLAT_VECTOR); auto &str_allocator = StringVector::GetStringAllocator(result); - auto offsets = StartScan(scan_state, base_data, start, vector_count); + auto strings = scan_state.ValidateRange(start, vector_count); auto result_data = FlatVector::GetDataMutable(result); for (idx_t i = 0; i < sel_count; i++) { idx_t index = sel.get_index(i); - result_data[i] = scan_state.DecompressString(dict, baseptr, offsets, index, str_allocator); + D_ASSERT(index < vector_count); + result_data[result_offset + i] = scan_state.DecompressString(strings.GetCompressedString(index), str_allocator); } - EndScan(scan_state, offsets, start, vector_count); } //===--------------------------------------------------------------------===// @@ -728,51 +792,39 @@ void FSSTStorage::Select(ColumnSegment &segment, ColumnScanState &state, idx_t v //===--------------------------------------------------------------------===// void FSSTStorage::StringFetchRow(ColumnSegment &segment, ColumnFetchState &state, row_t row_id, Vector &result, idx_t result_idx) { - auto &buffer_manager = BufferManager::GetBufferManager(segment.GetDatabase()); - auto handle = buffer_manager.Pin(segment.GetBlockHandle()); - auto block_size = segment.GetBlockSize(); - auto block_offset = segment.GetBlockOffset(); - if (block_offset > block_size) { - throw DataCorruptionException( - "Failed to read FSST string segment - header was out of range. Database file appears to be " - "corrupted."); - } - auto segment_capacity = block_size - block_offset; - auto base_ptr = handle.GetDataMutable() + block_offset; - auto base_data = data_ptr_cast(base_ptr + sizeof(fsst_compression_header_t)); - auto dict = GetDictionary(segment, handle); + D_ASSERT(row_id >= 0); + auto row_index = NumericCast(row_id); + D_ASSERT(row_index < segment.count.load()); + auto &buffer_manager = BufferManager::GetBufferManager(segment.GetDatabase()); + auto handle = buffer_manager.Pin(state.context, segment.GetBlockHandle()); + auto reader = CompressionSegmentReader::FromSegment(handle, segment, "FSST"); duckdb_fsst_decoder_t decoder; - bitpacking_width_t width; - auto have_symbol_table = ParseFSSTSegmentHeader(base_ptr, &decoder, &width, segment_capacity, segment.count.load()); + auto layout = FSSTScanState::ParseFSSTSegmentHeader(reader, segment.count.load()); + auto have_symbol_table = FSSTScanState::TryImportSymbolTable(layout, &decoder); auto result_data = FlatVector::GetDataMutable(result); - if (!have_symbol_table) { - // There is no FSST symtable. This is only the case for empty strings or NULLs. We emit an empty string. - result_data[result_idx] = string_t(nullptr, 0); - return; - } // We basically just do a scan of 1 which is kinda expensive as we need to repeatedly delta decode until we // reach the row we want, we could consider a more clever caching trick if this is slow - auto offsets = CalculateBpDeltaOffsets(-1, UnsafeNumericCast(row_id), 1); + auto offsets = CalculateBpDeltaOffsets(-1, row_index, 1); auto bitunpack_buffer = unique_ptr(new uint32_t[offsets.total_bitunpack_count]); - BitUnpackRange(base_data, data_ptr_cast(bitunpack_buffer.get()), offsets.total_bitunpack_count, - offsets.bitunpack_start_row, width); + BitUnpackRange(layout.lengths, data_ptr_cast(bitunpack_buffer.get()), offsets.total_bitunpack_count, + offsets.bitunpack_start_row, layout.width); auto delta_decode_buffer = unique_ptr(new uint32_t[offsets.total_delta_decode_count]); - DeltaDecodeIndices(bitunpack_buffer.get() + offsets.bitunpack_alignment_offset, delta_decode_buffer.get(), - offsets.total_delta_decode_count, 0); - - uint32_t string_length = bitunpack_buffer[offsets.scan_offset]; - - string_t compressed_string = UncompressedStringStorage::FetchStringFromDict( - state.context, segment, dict.end, result, base_ptr, - UnsafeNumericCast(delta_decode_buffer[offsets.unused_delta_decoded_values]), string_length); + auto strings = FSSTScanState::DecodeAndValidateOffsets( + layout, bitunpack_buffer.get() + offsets.bitunpack_alignment_offset, delta_decode_buffer.get(), + offsets.total_delta_decode_count, 0, have_symbol_table); + auto compressed_string = strings.GetCompressedString(offsets.unused_delta_decoded_values); + if (compressed_string.empty()) { + result_data[result_idx] = string_t(nullptr, 0); + return; + } auto &str_allocator = StringVector::GetStringAllocator(result); - result_data[result_idx] = FSSTPrimitives::DecompressValue((void *)&decoder, str_allocator, - compressed_string.GetData(), compressed_string.GetSize()); + result_data[result_idx] = FSSTPrimitives::DecompressValue( + (void *)&decoder, str_allocator, const_char_ptr_cast(compressed_string.data()), compressed_string.size()); } //===--------------------------------------------------------------------===// @@ -810,78 +862,72 @@ StringDictionaryContainer FSSTStorage::GetDictionary(ColumnSegment &segment, Buf return container; } -char *FSSTStorage::FetchStringPointer(StringDictionaryContainer dict, data_ptr_t baseptr, int32_t dict_offset) { - if (dict_offset == 0) { - return nullptr; - } - - auto dict_end = baseptr + dict.end; - auto dict_pos = dict_end - dict_offset; - return char_ptr_cast(dict_pos); -} - -static void ThrowInvalidFSSTSegment(const char *reason) { - throw DataCorruptionException("Failed to read FSST string segment - %s. Database file appears to be corrupted.", - reason); -} - -// Returns false if no symbol table was found. This means all strings are either empty or null -bool FSSTStorage::ParseFSSTSegmentHeader(data_ptr_t base_ptr, duckdb_fsst_decoder_t *decoder_out, - bitpacking_width_t *width_out, const idx_t segment_capacity, - const idx_t segment_count) { - if (sizeof(fsst_compression_header_t) > segment_capacity) { - ThrowInvalidFSSTSegment("header was out of range"); - } - auto header_ptr = reinterpret_cast(base_ptr); - auto fsst_symbol_table_offset = Load(data_ptr_cast(&header_ptr->fsst_symbol_table_offset)); - auto stored_width = Load(data_ptr_cast(&header_ptr->bitpacking_width)); - if (stored_width > sizeof(uint32_t) * 8) { +FSSTScanState::SegmentLayout FSSTScanState::ParseFSSTSegmentHeader(CompressionSegmentReader reader, + const idx_t segment_count) { + const auto header = reader.Read(); + if (header.bitpacking_width > sizeof(uint32_t) * 8) { ThrowInvalidFSSTSegment("bitpacking width was invalid"); } - auto width = UnsafeNumericCast(stored_width); - auto compressed_index_buffer_size = BitpackingPrimitives::GetRequiredSize(segment_count, width); - if (compressed_index_buffer_size > segment_capacity - sizeof(fsst_compression_header_t)) { + auto width = UnsafeNumericCast(header.bitpacking_width); + // Count groups before multiplying so a corrupt row count cannot overflow the buffer size. + constexpr auto group_size = BitpackingPrimitives::BITPACKING_ALGORITHM_GROUP_SIZE; + auto group_count = segment_count / group_size + (segment_count % group_size != 0); + auto bytes_per_group = (group_size * idx_t(width)) / 8; + if (bytes_per_group && group_count > reader.Remaining() / bytes_per_group) { ThrowInvalidFSSTSegment("bitpacking buffer was out of range"); } - auto expected_symbol_table_offset = sizeof(fsst_compression_header_t) + compressed_index_buffer_size; - if (fsst_symbol_table_offset != expected_symbol_table_offset) { + auto lengths = reader.ReadSubReader(group_count * bytes_per_group, "FSST lengths"); + if (header.fsst_symbol_table_offset != reader.Position()) { ThrowInvalidFSSTSegment("bitpacking width did not match the stored layout"); } - StringDictionaryContainer container; - container.size = Load(data_ptr_cast(&header_ptr->dict_size)); - container.end = Load(data_ptr_cast(&header_ptr->dict_end)); - if (container.end > segment_capacity || container.size > container.end || - container.end - container.size < fsst_symbol_table_offset) { + if (header.dict_end < reader.Position()) { ThrowInvalidFSSTSegment("dictionary was out of range"); } - // the symbol table occupies [symbol_table_offset, string_container_start), so its bytes end where the string - // dictionary container begins. Bound duckdb_fsst_import to that size so it can never read out of bounds. - const auto container_start = (container.end - container.size); - const auto expected_symbol_table_size = container_start - fsst_symbol_table_offset; - const auto consumed = - duckdb_fsst_import(decoder_out, base_ptr + fsst_symbol_table_offset, expected_symbol_table_size); + // Non-compacted blocks leave dict_size at zero, even when they contain strings. + // Bound the symbol table input by dict_end. Importing it establishes the dictionary's lower bound. + auto symbol_table = reader.ReadSubReader(header.dict_end - reader.Position(), "FSST symbol table"); + auto dictionary = symbol_table.GetSubReader(0, symbol_table.Size(), "FSST dictionary"); + return {width, lengths, symbol_table, dictionary}; +} + +bool FSSTScanState::TryImportSymbolTable(SegmentLayout &layout, duckdb_fsst_decoder_t *decoder_out) { + auto symbol_table_data = layout.symbol_table.GetBytes(0, layout.symbol_table.Size()); + const auto consumed = duckdb_fsst_import(decoder_out, symbol_table_data.data(), symbol_table_data.size()); // an inconsistent header is corruption; a version mismatch just means there is no symbol table (all strings are // empty/null), in which case we report "no table" and the scan continues without a decoder if (consumed == DUCKDB_FSST_IMPORT_OUT_OF_BOUNDS) { ThrowInvalidFSSTSegment("symbol table was out of range"); } - *width_out = width; // Currently, we allow an empty symbol table for a row group all strings are of length 0. - // This case is detected by reading a symbol table of size 0, which will fail on VERSION_MISMATCH + // This case is detected by reading a zeroed symbol table, which will fail on VERSION_MISMATCH // as the data we are reading is not really a fsst symbol table. - return consumed != DUCKDB_FSST_IMPORT_VERSION_MISMATCH; + if (consumed == DUCKDB_FSST_IMPORT_VERSION_MISMATCH) { + return false; + } + // Dictionary offsets count backwards from dict_end and must not reach into the symbol table. + layout.dictionary = + layout.dictionary.GetSubReader(consumed, layout.dictionary.Size() - consumed, "FSST dictionary"); + layout.symbol_table = layout.symbol_table.GetSubReader(0, consumed, "FSST symbol table"); + return true; } // The calculation of offsets and counts while scanning or fetching is a bit tricky, for two reasons: // - bitunpacking needs to be aligned to BITPACKING_ALGORITHM_GROUP_SIZE // - delta decoding needs to decode from the last known value. bp_delta_offsets_t FSSTStorage::CalculateBpDeltaOffsets(int64_t last_known_row, idx_t start, idx_t scan_count) { - D_ASSERT((idx_t)(last_known_row + 1) <= start); + D_ASSERT(last_known_row >= -1); + // The next scan resumes at last_known_row + 1, which is start + scan_count. + // Keep that value within int64_t. + const auto max_row = static_cast(NumericLimits::Maximum()); + if (start > max_row || scan_count > max_row - start) { + ThrowInvalidFSSTSegment("row range exceeded the supported limit"); + } bp_delta_offsets_t result; - result.delta_decode_start_row = (idx_t)(last_known_row + 1); + result.delta_decode_start_row = last_known_row < 0 ? 0 : static_cast(last_known_row) + 1; + D_ASSERT(result.delta_decode_start_row <= start); result.bitunpack_alignment_offset = result.delta_decode_start_row % BitpackingPrimitives::BITPACKING_ALGORITHM_GROUP_SIZE; result.bitunpack_start_row = result.delta_decode_start_row - result.bitunpack_alignment_offset; @@ -890,6 +936,10 @@ bp_delta_offsets_t FSSTStorage::CalculateBpDeltaOffsets(int64_t last_known_row, result.total_delta_decode_count = scan_count + result.unused_delta_decoded_values; result.total_bitunpack_count = BitpackingPrimitives::RoundUpToAlgorithmGroupSize(scan_count + result.scan_offset); + // Both decoding buffers contain uint32_t values, including complete groups in the unpack buffer. + if (result.total_bitunpack_count > NumericLimits::Maximum() / sizeof(uint32_t)) { + ThrowInvalidFSSTSegment("decoding buffer size was out of range"); + } D_ASSERT(result.total_delta_decode_count + result.bitunpack_alignment_offset <= result.total_bitunpack_count); return result; diff --git a/src/duckdb/src/storage/compression/numeric_constant.cpp b/src/duckdb/src/storage/compression/numeric_constant.cpp index 375ed7061..fa9da5fb2 100644 --- a/src/duckdb/src/storage/compression/numeric_constant.cpp +++ b/src/duckdb/src/storage/compression/numeric_constant.cpp @@ -23,23 +23,11 @@ static optional_ptr TryGetFunctionExpression(cons return expression.Cast(); } -static bool IsSimpleFilterColumnRef(const Expression &expression) { - return expression.GetExpressionType() == ExpressionType::BOUND_REF || - expression.GetExpressionClass() == ExpressionClass::BOUND_COLUMN_REF; -} - static bool TryComparisonFiltersNullValues(const BoundFunctionExpression &comparison, bool &filters_nulls, bool &filters_valid_values) { - optional_ptr constant_expr; - auto &left = BoundComparisonExpression::Left(comparison); - auto &right = BoundComparisonExpression::Right(comparison); - auto comparison_type = comparison.GetExpressionType(); - if (IsSimpleFilterColumnRef(left) && right.GetExpressionType() == ExpressionType::VALUE_CONSTANT) { - constant_expr = right.Cast(); - } else if (IsSimpleFilterColumnRef(right) && left.GetExpressionType() == ExpressionType::VALUE_CONSTANT) { - constant_expr = left.Cast(); - comparison_type = FlipComparisonExpression(comparison_type); - } else { + ExpressionType comparison_type; + auto constant_expr = ExpressionFilter::TryGetColumnConstantComparison(comparison, comparison_type); + if (!constant_expr) { return false; } if (constant_expr->GetValue().IsNull()) { @@ -111,7 +99,7 @@ static bool TryExpressionFiltersNullValues(const Expression &expression, bool &f if (expression.GetExpressionClass() == ExpressionClass::BOUND_OPERATOR) { auto &op = expression.Cast(); - if (op.GetChildren().size() != 1 || !IsSimpleFilterColumnRef(*op.GetChildren()[0])) { + if (op.GetChildren().size() != 1 || !ExpressionFilter::IsSimpleFilterColumnRef(*op.GetChildren()[0])) { return false; } switch (expression.GetExpressionType()) { @@ -147,14 +135,8 @@ static bool TryExpressionFiltersNullValues(const Expression &expression, bool &f return true; } if (function_name == SelectivityOptionalFilterScalarFun::NAME) { - if (!func_expr->BindInfo()) { - return false; - } - auto &data = func_expr->BindInfo()->Cast(); - if (!data.child_filter_expr) { - return false; - } - return TryExpressionFiltersNullValues(*data.child_filter_expr, filters_nulls, filters_valid_values); + auto optional_child = ExpressionFilter::GetOptionalFilterChild(*func_expr); + return optional_child && TryExpressionFiltersNullValues(*optional_child, filters_nulls, filters_valid_values); } if (function_name == PrefixRangeScalarFun::NAME) { if (!func_expr->BindInfo()) { @@ -205,6 +187,9 @@ template void ConstantFillFunction(ColumnSegment &segment, Vector &result, idx_t start_idx, idx_t count) { const auto &nstats = segment.GetStats(); + if (!NumericStats::HasMin(nstats)) { + throw DataCorruptionException("Constant segment is missing its constant value"); + } auto data = FlatVector::GetDataMutable(result); auto constant_value = NumericStats::GetMin(nstats); for (idx_t i = 0; i < count; i++) { @@ -266,14 +251,23 @@ void ConstantFetchRow(ColumnSegment &segment, ColumnFetchState &state, row_t row // Select //===--------------------------------------------------------------------===// void ConstantSelectValidity(ColumnSegment &segment, ColumnScanState &state, idx_t vector_count, Vector &result, - const SelectionVector &sel, idx_t sel_count) { - ConstantScanFunctionValidity(segment, state, sel_count, result); + const SelectionVector &sel, idx_t sel_count, idx_t result_offset, + ScanVectorType scan_type) { + if (scan_type == ScanVectorType::SCAN_ENTIRE_VECTOR) { + ConstantScanFunctionValidity(segment, state, sel_count, result); + return; + } + ConstantScanPartialValidity(segment, state, sel_count, result, result_offset); } template void ConstantSelect(ColumnSegment &segment, ColumnScanState &state, idx_t vector_count, Vector &result, - const SelectionVector &sel, idx_t sel_count) { - ConstantScanFunction(segment, state, vector_count, result); + const SelectionVector &sel, idx_t sel_count, idx_t result_offset, ScanVectorType scan_type) { + if (scan_type == ScanVectorType::SCAN_ENTIRE_VECTOR) { + ConstantScanFunction(segment, state, vector_count, result); + return; + } + ConstantScanPartial(segment, state, sel_count, result, result_offset); } //===--------------------------------------------------------------------===// diff --git a/src/duckdb/src/storage/compression/patas.cpp b/src/duckdb/src/storage/compression/patas.cpp index 14f82d598..f272fca3a 100644 --- a/src/duckdb/src/storage/compression/patas.cpp +++ b/src/duckdb/src/storage/compression/patas.cpp @@ -7,6 +7,26 @@ namespace duckdb { +void ThrowPatasInvalidBackwardReference() { + throw DataCorruptionException("Corrupted Patas segment: invalid backward reference"); +} + +void ThrowPatasInvalidPackedValueMetadata() { + throw DataCorruptionException("Corrupted Patas segment: invalid packed value metadata"); +} + +void ThrowPatasMetadataBeforeHeader() { + throw DataCorruptionException("Corrupted Patas segment: metadata ends before the segment header"); +} + +void ThrowPatasMetadataTableOutOfBounds() { + throw DataCorruptionException("Corrupted Patas segment: metadata table exceeds the segment"); +} + +void ThrowPatasGroupDataOutOfBounds() { + throw DataCorruptionException("Corrupted Patas segment: group data is outside the data region"); +} + template CompressionFunction GetPatasFunction(PhysicalType data_type) { throw NotImplementedException("GetPatasFunction not implemented for the given datatype"); diff --git a/src/duckdb/src/storage/compression/rle.cpp b/src/duckdb/src/storage/compression/rle.cpp index b2f67a49c..bbdbbc0b0 100644 --- a/src/duckdb/src/storage/compression/rle.cpp +++ b/src/duckdb/src/storage/compression/rle.cpp @@ -14,6 +14,22 @@ namespace duckdb { using rle_count_t = uint16_t; +template +bool RLEValueEqual(const T &lhs, const T &rhs) { + return lhs == rhs; +} + +template <> +bool RLEValueEqual(const float &lhs, const float &rhs) { + // IEEE == collapses +0 and -0; compare the bit pattern instead. + return Load(const_data_ptr_cast(&lhs)) == Load(const_data_ptr_cast(&rhs)); +} + +template <> +bool RLEValueEqual(const double &lhs, const double &rhs) { + return Load(const_data_ptr_cast(&lhs)) == Load(const_data_ptr_cast(&rhs)); +} + //===--------------------------------------------------------------------===// // Analyze //===--------------------------------------------------------------------===// @@ -53,7 +69,7 @@ struct RLEState { seen_count++; last_seen_count++; all_null = false; - } else if (last_value == data[idx]) { + } else if (RLEValueEqual(last_value, data[idx])) { // the last value is identical to this value: increment the last_seen_count last_seen_count++; } else { @@ -503,19 +519,21 @@ void RLEScan(ColumnSegment &segment, ColumnScanState &state, idx_t scan_count, V //===--------------------------------------------------------------------===// template void RLESelect(ColumnSegment &segment, ColumnScanState &state, idx_t vector_count, Vector &result, - const SelectionVector &sel, idx_t sel_count) { + const SelectionVector &sel, idx_t sel_count, idx_t result_offset, ScanVectorType scan_type) { auto &scan_state = state.scan_state->Cast>(); // If we are scanning an entire Vector and it contains only a single run we don't need to select at all auto current_run = scan_state.ValidateAndGetCurrentRun(); - if (scan_state.position_in_entry < current_run.length && + if (scan_type == ScanVectorType::SCAN_ENTIRE_VECTOR && scan_state.position_in_entry < current_run.length && CanEmitConstantVector(scan_state.position_in_entry, current_run.length, vector_count)) { RLEScanConstant(scan_state, current_run, vector_count, result); return; } - auto result_data = FlatVector::Writer(result, sel_count); + auto result_data = FlatVector::Writer(result, sel_count, result_offset); + // Keep the current run until the selection advances past it. + idx_t run_end = current_run.length - scan_state.position_in_entry; idx_t prev_idx = 0; for (idx_t i = 0; i < sel_count; i++) { auto next_idx = sel.get_index(i); @@ -523,14 +541,16 @@ void RLESelect(ColumnSegment &segment, ColumnScanState &state, idx_t vector_coun throw InternalException("Error in RLESelect - selection vector indices are not ordered"); } D_ASSERT(next_idx < vector_count); - // skip forward to the next index - scan_state.SkipInternal(next_idx - prev_idx); - // read the element - result_data.WriteValue(scan_state.ValidateAndGetCurrentRun().value); - // move the next to the prev + while (next_idx >= run_end) { + scan_state.ForwardToNextRun(); + current_run = scan_state.ValidateAndGetCurrentRun(); + run_end += current_run.length; + } + result_data.WriteValue(current_run.value); prev_idx = next_idx; } - // skip the tail + // Restore the position at the last selected row before skipping the tail. + scan_state.position_in_entry = current_run.length - (run_end - prev_idx); scan_state.SkipInternal(vector_count - prev_idx); } diff --git a/src/duckdb/src/storage/compression/string_uncompressed.cpp b/src/duckdb/src/storage/compression/string_uncompressed.cpp index 5ae47949c..9abdb7f15 100644 --- a/src/duckdb/src/storage/compression/string_uncompressed.cpp +++ b/src/duckdb/src/storage/compression/string_uncompressed.cpp @@ -9,6 +9,46 @@ namespace duckdb { +//===--------------------------------------------------------------------===// +// Error Helpers +//===--------------------------------------------------------------------===// +[[noreturn]] static void ThrowInvalidStringDictionary() { + throw DataCorruptionException("Corrupted uncompressed string segment: dictionary is outside the segment"); +} + +[[noreturn]] static void ThrowStringOffsetTableOutOfBounds() { + throw DataCorruptionException("Corrupted uncompressed string segment: offset table overlaps the dictionary"); +} + +[[noreturn]] static void ThrowDictionaryOffsetMinimumValue() { + throw DataCorruptionException("Corrupted uncompressed string segment: dictionary offset is INT32_MIN"); +} + +[[noreturn]] static void ThrowDictionaryOffsetOutOfBounds(uint32_t offset, uint32_t dictionary_size) { + throw DataCorruptionException( + "Corrupted uncompressed string segment: dictionary offset %u exceeds dictionary size %u", offset, + dictionary_size); +} + +[[noreturn]] static void ThrowDecreasingDictionaryOffset(uint32_t offset, uint32_t previous_offset) { + throw DataCorruptionException( + "Corrupted uncompressed string segment: dictionary offset %u is smaller than preceding offset %u", offset, + previous_offset); +} + +[[noreturn]] static void ThrowInvalidDictionaryEntryMarker() { + throw DataCorruptionException( + "Corrupted uncompressed string segment: negative dictionary offset does not describe an overflow marker"); +} + +[[noreturn]] static void ThrowInvalidOverflowStringBlock() { + throw DataCorruptionException("Corrupted uncompressed string segment: invalid overflow string block ID"); +} + +[[noreturn]] static void ThrowOverflowStringOffsetOutOfBounds() { + throw DataCorruptionException("Corrupted uncompressed string segment: overflow string offset is outside its block"); +} + //===--------------------------------------------------------------------===// // Storage Class //===--------------------------------------------------------------------===// @@ -65,13 +105,16 @@ idx_t UncompressedStringStorage::StringFinalAnalyze(AnalyzeState &state_p) { //===--------------------------------------------------------------------===// // Scan //===--------------------------------------------------------------------===// +StringScanState::StringScanState(BufferHandle handle_p) : handle(std::move(handle_p)) { +} + void UncompressedStringInitPrefetch(ColumnSegment &segment, PrefetchState &prefetch_state) { prefetch_state.AddBlock(segment.GetBlockHandle()); auto segment_state = segment.GetSegmentState(); if (segment_state) { auto &state = segment_state->Cast(); auto &block_manager = segment.GetBlockHandle()->GetBlockManager(); - for (auto &block_id : state.on_disk_blocks) { + for (auto &block_id : state.GetOnDiskBlocks()) { auto block_handle = state.GetHandle(block_manager, block_id); prefetch_state.AddBlock(block_handle); } @@ -80,10 +123,9 @@ void UncompressedStringInitPrefetch(ColumnSegment &segment, PrefetchState &prefe unique_ptr UncompressedStringStorage::StringInitScan(const QueryContext &context, ColumnSegment &segment) { - auto result = make_uniq(); auto &buffer_manager = BufferManager::GetBufferManager(segment.GetDatabase()); - result->handle = buffer_manager.Pin(context, segment.GetBlockHandle()); - return std::move(result); + auto handle = buffer_manager.Pin(context, segment.GetBlockHandle()); + return make_uniq(std::move(handle)); } //===--------------------------------------------------------------------===// @@ -93,22 +135,17 @@ void UncompressedStringStorage::StringScanPartial(ColumnSegment &segment, Column Vector &result, idx_t result_offset) { // clear any previously locked buffers and get the primary buffer handle auto &scan_state = state.scan_state->Cast(); + auto layout = StringSegmentLayout::Read(scan_state.handle, segment); auto start = state.GetPositionInSegment(); + D_ASSERT(start <= segment.count.load()); + D_ASSERT(scan_count <= segment.count.load() - start); - auto baseptr = scan_state.handle.GetDataMutable() + segment.GetBlockOffset(); - auto dict_end = GetDictionaryEnd(segment, scan_state.handle); - auto base_data = reinterpret_cast(baseptr + DICTIONARY_HEADER_SIZE); auto result_data = FlatVector::GetDataMutable(result); - - int32_t previous_offset = start > 0 ? base_data[start - 1] : 0; + auto strings = layout.ValidateRange(start, scan_count); for (idx_t i = 0; i < scan_count; i++) { - // std::abs used since offsets can be negative to indicate big strings - auto current_offset = base_data[start + i]; - auto string_length = UnsafeNumericCast(std::abs(current_offset) - std::abs(previous_offset)); - result_data[result_offset + i] = - FetchStringFromDict(state.context, segment, dict_end, result, baseptr, current_offset, string_length); - previous_offset = base_data[start + i]; + auto entry = strings.GetEntry(i); + result_data[result_offset + i] = FetchStringFromEntry(state.context, segment, result, entry); } } @@ -121,23 +158,24 @@ void UncompressedStringStorage::StringScan(ColumnSegment &segment, ColumnScanSta // Select //===--------------------------------------------------------------------===// void UncompressedStringStorage::Select(ColumnSegment &segment, ColumnScanState &state, idx_t vector_count, - Vector &result, const SelectionVector &sel, idx_t sel_count) { + Vector &result, const SelectionVector &sel, idx_t sel_count, idx_t result_offset, + ScanVectorType) { // clear any previously locked buffers and get the primary buffer handle auto &scan_state = state.scan_state->Cast(); + auto layout = StringSegmentLayout::Read(scan_state.handle, segment); auto start = state.GetPositionInSegment(); + D_ASSERT(start <= segment.count.load()); + D_ASSERT(vector_count <= segment.count.load() - start); + D_ASSERT(sel_count <= vector_count); - auto baseptr = scan_state.handle.GetDataMutable() + segment.GetBlockOffset(); - auto dict_end = GetDictionaryEnd(segment, scan_state.handle); - auto base_data = reinterpret_cast(baseptr + DICTIONARY_HEADER_SIZE); auto result_data = FlatVector::GetDataMutable(result); + auto strings = layout.ValidateRange(start, vector_count); for (idx_t i = 0; i < sel_count; i++) { - idx_t index = start + sel.get_index(i); - auto current_offset = base_data[index]; - auto prev_offset = index > 0 ? base_data[index - 1] : 0; - auto string_length = UnsafeNumericCast(std::abs(current_offset) - std::abs(prev_offset)); - result_data[i] = - FetchStringFromDict(state.context, segment, dict_end, result, baseptr, current_offset, string_length); + auto selection_index = sel.get_index(i); + D_ASSERT(selection_index < vector_count); + auto entry = strings.GetEntry(selection_index); + result_data[result_offset + i] = FetchStringFromEntry(state.context, segment, result, entry); } } @@ -162,25 +200,19 @@ BufferHandle &ColumnFetchState::GetOrInsertHandle(ColumnSegment &segment) { void UncompressedStringStorage::StringFetchRow(ColumnSegment &segment, ColumnFetchState &state, row_t row_id, Vector &result, idx_t result_idx) { + D_ASSERT(row_id >= 0); + auto row_index = NumericCast(row_id); + D_ASSERT(row_index < segment.count.load()); + // fetch a single row from the string segment // first pin the main buffer if it is not already pinned auto &handle = state.GetOrInsertHandle(segment); + auto layout = StringSegmentLayout::Read(handle, segment); - auto baseptr = handle.GetDataMutable() + segment.GetBlockOffset(); - auto dict_end = GetDictionaryEnd(segment, handle); - auto base_data = reinterpret_cast(baseptr + DICTIONARY_HEADER_SIZE); auto result_data = FlatVector::GetDataMutable(result); - auto dict_offset = base_data[row_id]; - uint32_t string_length; - if (DUCKDB_UNLIKELY(row_id == 0LL)) { - // edge case where this is the first string in the dict - string_length = NumericCast(std::abs(dict_offset)); - } else { - string_length = NumericCast(std::abs(dict_offset) - std::abs(base_data[row_id - 1])); - } - result_data[result_idx] = - FetchStringFromDict(state.context, segment, dict_end, result, baseptr, dict_offset, string_length); + auto entry = layout.ValidateAndGetEntry(row_index); + result_data[result_idx] = FetchStringFromEntry(state.context, segment, result, entry); } //===--------------------------------------------------------------------===// @@ -211,7 +243,7 @@ UncompressedStringStorage::StringInitSegment(ColumnSegment &segment, block_id_t auto result = make_uniq(); if (segment_state) { auto &serialized_state = segment_state->Cast(); - result->on_disk_blocks = std::move(serialized_state.blocks); + result->InitializeOnDiskBlocks(std::move(serialized_state.blocks)); } return std::move(result); } @@ -248,11 +280,11 @@ idx_t UncompressedStringStorage::FinalizeAppend(ColumnSegment &segment, BaseStat //===--------------------------------------------------------------------===// unique_ptr UncompressedStringStorage::SerializeState(ColumnSegment &segment) { auto &state = segment.GetSegmentState()->Cast(); - if (state.on_disk_blocks.empty()) { + if (state.GetOnDiskBlocks().empty()) { // no on-disk blocks - nothing to write return nullptr; } - return make_uniq(state.on_disk_blocks); + return make_uniq(state.GetOnDiskBlocks()); } unique_ptr UncompressedStringStorage::DeserializeState(Deserializer &deserializer) { @@ -263,7 +295,7 @@ unique_ptr UncompressedStringStorage::DeserializeState(Deser void UncompressedStringStorage::VisitBlockIds(const ColumnSegment &segment, BlockIdVisitor &visitor) { auto &state = segment.GetSegmentState()->Cast(); - for (auto &block_id : state.on_disk_blocks) { + for (auto &block_id : state.GetOnDiskBlocks()) { visitor.Visit(block_id); } } @@ -287,6 +319,150 @@ CompressionFunction StringUncompressed::GetFunction(PhysicalType data_type) { UncompressedStringStorage::Select); } +//===--------------------------------------------------------------------===// +// Dictionary Validation +//===--------------------------------------------------------------------===// +//! Returns the absolute offset without validating it. +static inline uint32_t GetDictionaryOffset(const int32_t encoded_offset) { + // Widen first so taking the absolute value of INT32_MIN does not overflow. + return UnsafeNumericCast(AbsValue(encoded_offset)); +} + +static uint32_t ValidateAndGetDictionaryOffset(const int32_t encoded_offset, const uint32_t dictionary_size) { + if (encoded_offset == NumericLimits::Minimum()) { + ThrowDictionaryOffsetMinimumValue(); + } + + const auto dictionary_offset = GetDictionaryOffset(encoded_offset); + if (dictionary_offset > dictionary_size) { + ThrowDictionaryOffsetOutOfBounds(dictionary_offset, dictionary_size); + } + return dictionary_offset; +} + +//! SIMD friendly version of ValidateDictionaryEntry that avoids early exits. +//! Returns 1 if ValidateDictionaryEntry would accept the offsets, otherwise 0. +static uint32_t ValidateDictionaryEntryFast(const int32_t current_offset, const int32_t previous_offset, + const uint32_t dictionary_size) { + const auto current_dictionary_offset = GetDictionaryOffset(current_offset); + const auto previous_dictionary_offset = GetDictionaryOffset(previous_offset); + // Offsets store the cumulative number of dictionary bytes used. + const auto string_length = current_dictionary_offset - previous_dictionary_offset; + uint32_t invalid = current_offset == NumericLimits::Minimum(); + invalid |= current_dictionary_offset > dictionary_size; + // Dictionary offsets must not decrease. + invalid |= current_dictionary_offset < previous_dictionary_offset; + + // If the offset is negative, the entry is either NULL or an overflow string. + const uint32_t is_negative = current_offset < 0; + const uint32_t has_zero_length = string_length == 0; + const uint32_t has_bytes = string_length != 0; + const uint32_t offset_changed = current_offset != previous_offset; + const uint32_t wrong_marker_size = string_length != UncompressedStringStorage::BIG_STRING_MARKER_SIZE; + + // If it is NULL, the current offset must be inherited unchanged from the previous entry. + const auto invalid_null = has_zero_length & offset_changed; + // If it is an overflow string, the entry's length must match the marker's length. + const auto invalid_overflow = has_bytes & wrong_marker_size; + invalid |= is_negative & (invalid_null | invalid_overflow); + + return invalid == 0; +} + +//! Validates both offsets and their entry, throwing on invalid data. +static void ValidateDictionaryEntry(const int32_t current_offset, const int32_t previous_offset, + const uint32_t dictionary_size) { + const auto previous_dictionary_offset = ValidateAndGetDictionaryOffset(previous_offset, dictionary_size); + const auto current_dictionary_offset = ValidateAndGetDictionaryOffset(current_offset, dictionary_size); + + // Dictionary offsets must not decrease. + if (current_dictionary_offset < previous_dictionary_offset) { + D_ASSERT(!ValidateDictionaryEntryFast(current_offset, previous_offset, dictionary_size)); + ThrowDecreasingDictionaryOffset(current_dictionary_offset, previous_dictionary_offset); + } + + // Offsets store the cumulative number of dictionary bytes used. + const auto string_length = current_dictionary_offset - previous_dictionary_offset; + + // If the offset is negative, the entry is either NULL or an overflow string. + if (current_offset < 0) { + // If it is NULL, the current offset must be inherited unchanged from the previous entry. + if (string_length == 0 && current_offset != previous_offset) { + D_ASSERT(!ValidateDictionaryEntryFast(current_offset, previous_offset, dictionary_size)); + ThrowInvalidDictionaryEntryMarker(); + } + + // If it is an overflow string, the entry's length must match the marker's length. + if (string_length > 0 && string_length != UncompressedStringStorage::BIG_STRING_MARKER_SIZE) { + D_ASSERT(!ValidateDictionaryEntryFast(current_offset, previous_offset, dictionary_size)); + ThrowInvalidDictionaryEntryMarker(); + } + } + + D_ASSERT(ValidateDictionaryEntryFast(current_offset, previous_offset, dictionary_size)); +} + +StringSegmentLayout StringSegmentLayout::Read(const BufferHandle &handle, const ColumnSegment &segment) { + auto reader = CompressionSegmentReader::FromSegment(handle, segment, "uncompressed string segment"); + // Appends change the dictionary size, so reading it during a scan would race. + // The dictionary end does not change during appends, so use that instead. + auto dictionary_end = NumericCast(reader.Get(sizeof(uint32_t))); + if (dictionary_end > reader.Size()) { + ThrowInvalidStringDictionary(); + } + auto offset_count = segment.count.load(); + if (dictionary_end < UncompressedStringStorage::DICTIONARY_HEADER_SIZE || + offset_count > (dictionary_end - UncompressedStringStorage::DICTIONARY_HEADER_SIZE) / sizeof(int32_t)) { + ThrowStringOffsetTableOutOfBounds(); + } + // Dictionary bytes do not overlap the offsets table, so use the end of the table + // as the lower bound instead of reading the dictionary size. + auto dictionary_start = UncompressedStringStorage::DICTIONARY_HEADER_SIZE + offset_count * sizeof(int32_t); + auto dictionary_data = reader.GetBytes(dictionary_start, dictionary_end - dictionary_start); + auto offsets = reader.GetArray(UncompressedStringStorage::DICTIONARY_HEADER_SIZE, offset_count); + return {dictionary_data, offsets}; +} + +ValidatedStringRange StringSegmentLayout::ValidateRange(const idx_t start, const idx_t count) const { + D_ASSERT(start <= offsets.size()); + D_ASSERT(count <= offsets.size() - start); + auto range_offsets = offsets.SubArray(start, count); + const auto previous_offset = start > 0 ? offsets[start - 1] : 0; + const auto dictionary_size = NumericCast(dictionary_data.size()); + const auto preceding_dictionary_offset = ValidateAndGetDictionaryOffset(previous_offset, dictionary_size); + if (DUCKDB_UNLIKELY(count == 0)) { + return ValidatedStringRange(dictionary_data, range_offsets, preceding_dictionary_offset); + } + + uint32_t valid = ValidateDictionaryEntryFast(range_offsets[0], previous_offset, dictionary_size); + // Hot path assumes no errors, so accumulate and later deal with them. + for (idx_t i = 1; i < count; i++) { + valid &= ValidateDictionaryEntryFast(range_offsets[i], range_offsets[i - 1], dictionary_size); + } + + // If we have an error, find it and report it. + if (DUCKDB_UNLIKELY(!valid)) { + for (idx_t i = 0; i < count; i++) { + ValidateDictionaryEntry(range_offsets[i], i > 0 ? range_offsets[i - 1] : previous_offset, dictionary_size); + } + throw InternalException("ValidateDictionaryEntryFast and ValidateDictionaryEntry disagree"); + } + + return ValidatedStringRange(dictionary_data, range_offsets, preceding_dictionary_offset); +} + +StringDictionaryEntry StringSegmentLayout::ValidateAndGetEntry(const idx_t row_index) const { + D_ASSERT(row_index < offsets.size()); + const auto current_offset = offsets[row_index]; + const auto previous_offset = row_index > 0 ? offsets[row_index - 1] : 0; + ValidateDictionaryEntry(current_offset, previous_offset, NumericCast(dictionary_data.size())); + const auto previous_dictionary_offset = GetDictionaryOffset(previous_offset); + const auto current_dictionary_offset = GetDictionaryOffset(current_offset); + const auto string_length = current_dictionary_offset - previous_dictionary_offset; + const auto is_overflow = current_offset < 0 && string_length > 0; + return {dictionary_data.SubArray(dictionary_data.size() - current_dictionary_offset, string_length), is_overflow}; +} + //===--------------------------------------------------------------------===// // Helper Functions //===--------------------------------------------------------------------===// @@ -298,18 +474,13 @@ void UncompressedStringStorage::SetDictionary(ColumnSegment &segment, BufferHand } StringDictionaryContainer UncompressedStringStorage::GetDictionary(ColumnSegment &segment, BufferHandle &handle) { - auto startptr = handle.GetDataMutable() + segment.GetBlockOffset(); + auto startptr = handle.Ptr() + segment.GetBlockOffset(); StringDictionaryContainer container; container.size = Load(startptr); container.end = Load(startptr + sizeof(uint32_t)); return container; } -uint32_t UncompressedStringStorage::GetDictionaryEnd(ColumnSegment &segment, BufferHandle &handle) { - auto startptr = handle.GetDataMutable() + segment.GetBlockOffset(); - return Load(startptr + sizeof(uint32_t)); -} - idx_t UncompressedStringStorage::RemainingSpace(ColumnSegment &segment, BufferHandle &handle) { auto dictionary = GetDictionary(segment, handle); D_ASSERT(dictionary.end == segment.SegmentSize()); @@ -374,8 +545,12 @@ string_t UncompressedStringStorage::ReadOverflowString(const QueryContext &conte auto &buffer_manager = segment.GetBlockHandle()->GetMemory().GetBufferManager(); auto &state = segment.GetSegmentState()->Cast(); - D_ASSERT(block != INVALID_BLOCK); - D_ASSERT(offset < NumericCast(segment.GetBlockSize())); + if (block < 0) { + ThrowInvalidOverflowStringBlock(); + } + if (offset < 0) { + ThrowOverflowStringOffsetOutOfBounds(); + } if (block < MAXIMUM_BLOCK) { // read the overflow string from disk @@ -384,9 +559,13 @@ string_t UncompressedStringStorage::ReadOverflowString(const QueryContext &conte auto handle = buffer_manager.Pin(context, block_handle); // read header - uint32_t length = Load(handle.GetDataMutable() + offset); + auto block_size = segment.GetBlockSize(); + auto string_space = block_size - sizeof(block_id_t); + CompressionSegmentReader block_reader(handle.Ptr(), block_size, "overflow string block"); + auto reader = block_reader.GetSubReader(0, string_space, "overflow string data"); + reader.SetPosition(NumericCast(offset)); + uint32_t length = reader.Read(); uint32_t remaining = length; - offset += sizeof(uint32_t); BufferHandle target_handle; string_t overflow_string; @@ -404,24 +583,23 @@ string_t UncompressedStringStorage::ReadOverflowString(const QueryContext &conte // now append the string to the single buffer while (remaining > 0) { - idx_t to_write = MinValue(remaining, segment.GetBlockSize() - sizeof(block_id_t) - - UnsafeNumericCast(offset)); - memcpy(target_ptr, handle.GetDataMutable() + offset, to_write); + idx_t to_write = MinValue(remaining, reader.Remaining()); + reader.ReadBytesInto(target_ptr, to_write); remaining -= to_write; - offset += UnsafeNumericCast(to_write); target_ptr += to_write; if (remaining > 0) { // read the next block - block_id_t next_block = Load(handle.GetDataMutable() + offset); + block_id_t next_block = block_reader.Get(string_space); block_handle = state.GetHandle(segment.GetBlockHandle()->GetBlockManager(), next_block); handle = buffer_manager.Pin(context, block_handle); - offset = 0; + block_reader = CompressionSegmentReader(handle.Ptr(), block_size, "overflow string block"); + reader = block_reader.GetSubReader(0, string_space, "overflow string data"); } } if (allocate_block) { - auto final_buffer = target_handle.GetDataMutable(); + auto final_buffer = target_handle.Ptr(); StringVector::AddHandle(result, std::move(target_handle)); - return ReadString(final_buffer, 0, length); + return string_t(const_char_ptr_cast(final_buffer), length); } else { overflow_string.Finalize(); return overflow_string; @@ -432,22 +610,17 @@ string_t UncompressedStringStorage::ReadOverflowString(const QueryContext &conte // first pin the handle, if it is not pinned yet auto string_block = state.FindOverflowBlock(block); auto handle = buffer_manager.Pin(context, string_block.get().block); - auto final_buffer = handle.GetDataMutable(); + auto final_buffer = handle.Ptr(); StringVector::AddHandle(result, std::move(handle)); - return ReadStringWithLength(final_buffer, offset); + CompressionSegmentReader reader(final_buffer, string_block.get().size, "in-memory overflow string block"); + return ReadStringWithLength(reader, offset); } -string_t UncompressedStringStorage::ReadString(data_ptr_t target, int32_t offset, uint32_t string_length) { - auto ptr = target + offset; - auto str_ptr = char_ptr_cast(ptr); - return string_t(str_ptr, string_length); -} - -string_t UncompressedStringStorage::ReadStringWithLength(data_ptr_t target, int32_t offset) { - auto ptr = target + offset; - auto str_length = Load(ptr); - auto str_ptr = char_ptr_cast(ptr + sizeof(uint32_t)); - return string_t(str_ptr, str_length); +string_t UncompressedStringStorage::ReadStringWithLength(CompressionSegmentReader reader, int32_t offset) { + reader.SetPosition(NumericCast(offset)); + auto string_length = reader.Read(); + auto string_data = reader.ReadBytes(string_length); + return string_t(const_char_ptr_cast(string_data.data()), string_length); } void UncompressedStringStorage::WriteStringMarker(data_ptr_t target, block_id_t block_id, int32_t offset) { @@ -456,10 +629,4 @@ void UncompressedStringStorage::WriteStringMarker(data_ptr_t target, block_id_t memcpy(target, &offset, sizeof(int32_t)); } -void UncompressedStringStorage::ReadStringMarker(data_ptr_t target, block_id_t &block_id, int32_t &offset) { - memcpy(&block_id, target, sizeof(block_id_t)); - target += sizeof(block_id_t); - memcpy(&offset, target, sizeof(int32_t)); -} - } // namespace duckdb diff --git a/src/duckdb/src/storage/compression/validity_uncompressed.cpp b/src/duckdb/src/storage/compression/validity_uncompressed.cpp index a9778897b..6ab8fbe5b 100644 --- a/src/duckdb/src/storage/compression/validity_uncompressed.cpp +++ b/src/duckdb/src/storage/compression/validity_uncompressed.cpp @@ -520,7 +520,7 @@ void ValidityScan(ColumnSegment &segment, ColumnScanState &state, idx_t scan_cou // Select //===--------------------------------------------------------------------===// void ValiditySelect(ColumnSegment &segment, ColumnScanState &state, idx_t vector_count, Vector &result, - const SelectionVector &sel, idx_t sel_count) { + const SelectionVector &sel, idx_t sel_count, idx_t result_offset, ScanVectorType) { auto start = state.GetPositionInSegment(); if (sel_count == 0) { return; @@ -528,18 +528,21 @@ void ValiditySelect(ColumnSegment &segment, ColumnScanState &state, idx_t vector D_ASSERT(start <= segment.count && vector_count <= segment.count - start); D_ASSERT(sel_count <= vector_count); - result.Flatten(); + if (result_offset == 0) { + result.Flatten(); + } auto &scan_state = state.scan_state->Cast(); + auto input_data = ValidityScanData(scan_state.reader, start, vector_count); + auto input_start = start % ValidityMask::BITS_PER_VALUE; auto &result_mask = FlatVector::ValidityMutable(result); for (idx_t i = 0; i < sel_count; i++) { auto selected_index = sel.get_index(i); D_ASSERT(selected_index < vector_count); - auto source_idx = start + selected_index; - auto entry_offset = source_idx / ValidityMask::BITS_PER_VALUE * sizeof(validity_t); - auto entry = scan_state.reader.Get(entry_offset); + auto source_idx = input_start + selected_index; + auto entry = input_data[source_idx / ValidityMask::BITS_PER_VALUE]; if (!ValidityMask::RowIsValid(entry, source_idx % ValidityMask::BITS_PER_VALUE)) { - result_mask.SetInvalid(i); + result_mask.SetInvalid(result_offset + i); } } } @@ -638,12 +641,14 @@ void ValidityRevertAppend(ColumnSegment &segment, idx_t new_count) { //===--------------------------------------------------------------------===// CompressionFunction ValidityUncompressed::GetFunction(PhysicalType data_type) { D_ASSERT(data_type == PhysicalType::BIT); - return CompressionFunction(CompressionType::COMPRESSION_UNCOMPRESSED, data_type, ValidityInitAnalyze, + CompressionFunction result(CompressionType::COMPRESSION_UNCOMPRESSED, data_type, ValidityInitAnalyze, ValidityAnalyze, ValidityFinalAnalyze, UncompressedFunctions::InitCompression, UncompressedFunctions::Compress, UncompressedFunctions::FinalizeCompress, ValidityInitScan, ValidityScan, ValidityScanPartial, ValidityFetchRow, UncompressedFunctions::EmptySkip, ValidityInitSegment, ValidityInitAppend, ValidityAppend, ValidityFinalizeAppend, ValidityRevertAppend); + result.select = ValiditySelect; + return result; } } // namespace duckdb diff --git a/src/duckdb/src/storage/compression/zstd.cpp b/src/duckdb/src/storage/compression/zstd.cpp index 099c4765d..b60ae6c81 100644 --- a/src/duckdb/src/storage/compression/zstd.cpp +++ b/src/duckdb/src/storage/compression/zstd.cpp @@ -1035,18 +1035,18 @@ unique_ptr ZSTDStorage::StringInitSegment(ColumnSegment auto result = make_uniq(); if (segment_state) { auto &serialized_state = segment_state->Cast(); - result->on_disk_blocks = std::move(serialized_state.blocks); + result->InitializeOnDiskBlocks(std::move(serialized_state.blocks)); } return std::move(result); } unique_ptr ZSTDStorage::SerializeState(ColumnSegment &segment) { auto &state = segment.GetSegmentState()->Cast(); - if (state.on_disk_blocks.empty()) { + if (state.GetOnDiskBlocks().empty()) { // no on-disk blocks - nothing to write return nullptr; } - return make_uniq(state.on_disk_blocks); + return make_uniq(state.GetOnDiskBlocks()); } unique_ptr ZSTDStorage::DeserializeState(Deserializer &deserializer) { @@ -1057,7 +1057,7 @@ unique_ptr ZSTDStorage::DeserializeState(Deserializer &deser void ZSTDStorage::VisitBlockIds(const ColumnSegment &segment, BlockIdVisitor &visitor) { auto &state = segment.GetSegmentState()->Cast(); - for (auto &block_id : state.on_disk_blocks) { + for (auto &block_id : state.GetOnDiskBlocks()) { visitor.Visit(block_id); } } diff --git a/src/duckdb/src/storage/data_table.cpp b/src/duckdb/src/storage/data_table.cpp index a6e963fb8..a51fa2cbe 100644 --- a/src/duckdb/src/storage/data_table.cpp +++ b/src/duckdb/src/storage/data_table.cpp @@ -298,7 +298,13 @@ idx_t DataTable::MaxThreads(ClientContext &context) const { parallel_scan_vector_count = 1; } idx_t parallel_scan_tuple_count = STANDARD_VECTOR_SIZE * parallel_scan_vector_count; - return GetTotalRows() / parallel_scan_tuple_count + 1; + idx_t total_rows = GetTotalRows(); + auto local_storage = LocalStorage::Get(context, db).GetStorage(*this); + if (local_storage) { + // transaction-local rows are scanned in parallel as well + total_rows += local_storage->GetCollection().GetTotalRows(); + } + return total_rows / parallel_scan_tuple_count + 1; } void DataTable::InitializeParallelScan(ClientContext &context, ParallelTableScanState &state, @@ -360,8 +366,8 @@ bool DataTable::HasUniqueIndexes() const { return info->indexes.HasUniqueIndexes(); } -void DataTable::AddIndex(unique_ptr index) { - info->indexes.AddIndex(std::move(index), ConstraintCheckMode::DEFAULT); +void DataTable::AddIndex(unique_ptr index, idx_t index_oid) { + info->indexes.AddIndex(std::move(index), index_oid, ConstraintCheckMode::DEFAULT); } bool DataTable::HasForeignKeyIndex(const vector &keys, ForeignKeyType type) { @@ -429,10 +435,6 @@ void DataTable::CleanupAppend(VisibilityBound lowest_visibility_bound, idx_t sta row_groups->CleanupAppend(lowest_visibility_bound, start, count); } -bool DataTable::IndexNameIsUnique(const string &name) { - return info->indexes.NameIsUnique(name); -} - Identifier DataTableInfo::GetSchemaName() { return schema_path.back(); } @@ -661,10 +663,8 @@ void DataTable::VerifyForeignKeyConstraint(optional_ptr stora // Get the column types in their physical order. A foreign key always references a table in the same (possibly // nested) schema, so we qualify it with this table's schema path. - auto schema_path = info->GetSchemaPath(); - schema_path.insert(schema_path.begin(), db.GetName()); auto &table_entry = Catalog::GetEntry( - context, QualifiedName(std::move(schema_path), bound_foreign_key.info.table)); + context, QualifiedName::FromCatalogSchema(db.GetName(), info->GetSchemaPath(), bound_foreign_key.info.table)); vector types; for (auto &col : table_entry.GetColumns().Physical()) { types.emplace_back(col.Type()); @@ -743,9 +743,9 @@ void DataTable::VerifyForeignKeyConstraint(optional_ptr stora if (!global_conflicts && !local_conflicts) { conflict = 0; } else if (!global_conflicts && local_conflicts) { - conflict = local_conflict_manager.GetFirstInvalidIndex(count); + conflict = local_conflict_manager.GetFirstInvalidIndex(count, /*negate=*/true); } else if (global_conflicts && !local_conflicts) { - conflict = global_conflict_manager.GetFirstInvalidIndex(count); + conflict = global_conflict_manager.GetFirstInvalidIndex(count, /*negate=*/true); } else { auto &global_validity = global_conflict_manager.GetFirstValidity(); auto &local_validity = local_conflict_manager.GetFirstValidity(); @@ -902,6 +902,8 @@ void DataTable::InitializeLocalStorage(LocalAppendState &state, DuckTableEntry & GetTableName(), TableModification()); } + // The transaction-local indexes are created with the local storage, and only for indexes that are bound + info->BindIndexes(context); auto &local_storage = LocalStorage::Get(context, db); local_storage.InitializeStorage(state, *this, table); state.constraint_state = InitializeConstraintState(table, bound_constraints); @@ -1133,7 +1135,7 @@ void DataTable::ScanTableSegment(DuckTransaction &transaction, idx_t row_start, InitializeScanWithOffset(transaction, state, column_ids, row_start, row_start + count); auto row_start_aligned = - state.table_state.row_group->GetRowStart() + state.table_state.vector_index * STANDARD_VECTOR_SIZE; + state.table_state.GetRowGroup()->GetRowStart() + state.table_state.vector_index * STANDARD_VECTOR_SIZE; idx_t current_row = row_start_aligned; while (current_row < end) { @@ -1665,7 +1667,7 @@ bool DataTable::ScanColumnSegmentInfo(const QueryContext &context, ColumnSegment // Index Constraint Creation //===--------------------------------------------------------------------===// void DataTable::AddIndex(const ColumnList &columns, const vector &column_indexes, - const IndexConstraintType type, IndexStorageInfo index_info, + const IndexConstraintType type, IndexStorageInfo index_info, idx_t index_oid, const ConstraintCheckMode check_mode) { if (!IsMainTable()) { throw TransactionException("Transaction conflict: attempting to add an index to table \"%s\" but it has been " @@ -1689,7 +1691,7 @@ void DataTable::AddIndex(const ColumnList &columns, const vector & auto &io_manager = TableIOManager::Get(*this); auto art = make_uniq(index_info.name, type, physical_ids, io_manager, std::move(expressions), db, nullptr, index_info); - info->indexes.AddIndex(std::move(art), check_mode); + info->indexes.AddIndex(std::move(art), index_oid, check_mode); } } // namespace duckdb diff --git a/src/duckdb/src/storage/database_handle.cpp b/src/duckdb/src/storage/database_handle.cpp index cb4542bd7..36f4ac369 100644 --- a/src/duckdb/src/storage/database_handle.cpp +++ b/src/duckdb/src/storage/database_handle.cpp @@ -1,5 +1,6 @@ #include "duckdb/storage/database_handle.hpp" #include "duckdb/storage/single_file_block_manager.hpp" +#include "duckdb/common/query_context.hpp" #include @@ -10,17 +11,29 @@ DatabaseHandle::DatabaseHandle(unique_ptr handle_p) : handle(std::mo DatabaseHandle::DatabaseHandle(unique_ptr mmap_handle_p) : mmap_handle(std::move(mmap_handle_p)) { } -unique_ptr DatabaseHandle::Open(AttachedDatabase &db, const string &path, +//! The file system to open the database file with. The handle outlives the statement that opens it, but file systems +//! take what they need from the opener when the file is opened: through the client's file system, a remote database +//! file uses the settings of the session that attached it (http_proxy, ca_cert_file, ...) and reports its requests to +//! that session's logger. Without a client, the database-wide file system only sees global settings. +static FileSystem &GetDatabaseFileSystem(QueryContext context, AttachedDatabase &db) { + auto client_context = context.GetClientContext(); + if (client_context) { + return FileSystem::GetFileSystem(*client_context); + } + return FileSystem::Get(db); +} + +unique_ptr DatabaseHandle::Open(QueryContext context, AttachedDatabase &db, const string &path, const StorageManagerOptions &options, DatabaseOpenMode open_mode) { if (options.io_mode == FileIOMode::MMAP) { // mmap reads are zero-copy from the mapping, so the prefetched header is not needed here. - return OpenMemoryMap(db, path, options, open_mode); + return OpenMemoryMap(context, db, path, options, open_mode); } else { - return OpenFile(db, path, options, open_mode); + return OpenFile(context, db, path, options, open_mode); } } -unique_ptr DatabaseHandle::OpenFile(AttachedDatabase &db, const string &path, +unique_ptr DatabaseHandle::OpenFile(QueryContext context, AttachedDatabase &db, const string &path, const StorageManagerOptions &options, DatabaseOpenMode open_mode) { FileOpenFlags file_flags; if (options.read_only) { @@ -39,7 +52,7 @@ unique_ptr DatabaseHandle::OpenFile(AttachedDatabase &db, const file_flags |= FileFlags::FILE_FLAGS_PARALLEL_ACCESS; file_flags |= FileFlags::FILE_FLAGS_MULTI_CLIENT_ACCESS; - auto &fs = FileSystem::Get(db); + auto &fs = GetDatabaseFileSystem(context, db); auto file_handle = fs.OpenFile(path, file_flags); if (!file_handle) { // this can only happen in read-only mode - as that is when we set FILE_FLAGS_NULL_IF_NOT_EXISTS @@ -54,7 +67,7 @@ unique_ptr DatabaseHandle::OpenFile(AttachedDatabase &db, const return result; } -unique_ptr DatabaseHandle::OpenMemoryMap(AttachedDatabase &db, const string &path, +unique_ptr DatabaseHandle::OpenMemoryMap(QueryContext context, AttachedDatabase &db, const string &path, const StorageManagerOptions &options, DatabaseOpenMode open_mode) { if (options.encryption_options.encryption_enabled) { @@ -76,7 +89,7 @@ unique_ptr DatabaseHandle::OpenMemoryMap(AttachedDatabase &db, c mmap_flags |= FileFlags::FILE_FLAGS_FILE_CREATE; } } - auto &fs = FileSystem::Get(db); + auto &fs = GetDatabaseFileSystem(context, db); auto mmap_handle = fs.MemoryMapFile(path, mmap_flags, mmap_options); if (!mmap_handle) { // Only happens in read-only mode, where FILE_FLAGS_NULL_IF_NOT_EXISTS is set. @@ -156,6 +169,13 @@ void DatabaseHandle::Trim(idx_t offset, idx_t length) { } } +idx_t DatabaseHandle::GetFileSize() { + if (mmap_handle) { + return 0; + } + return handle->GetFileSize(); +} + FileHandle &DatabaseHandle::GetFileHandle() { return *handle; } diff --git a/src/duckdb/src/storage/external_file_cache/caching_file_system.cpp b/src/duckdb/src/storage/external_file_cache/caching_file_system.cpp index 7bda90c4c..3c9b26937 100644 --- a/src/duckdb/src/storage/external_file_cache/caching_file_system.cpp +++ b/src/duckdb/src/storage/external_file_cache/caching_file_system.cpp @@ -7,6 +7,7 @@ #include "duckdb/common/enums/memory_tag.hpp" #include "duckdb/common/file_system.hpp" #include "duckdb/main/client_context.hpp" +#include "duckdb/main/settings.hpp" #include "duckdb/parallel/task_executor.hpp" #include "duckdb/parallel/task_scheduler.hpp" #include "duckdb/storage/buffer/block_handle.hpp" @@ -53,11 +54,9 @@ void TightenCacheDeadline(optional &cached, const optional block_p, idx_t block_idx_p, - idx_t block_size_p, BufferHandle &result_pin_p) + BufferManager &buffer_manager_p, shared_ptr block_p, BufferHandle &result_pin_p) : BaseExecutorTask(executor), caching_file_handle(caching_file_handle_p), context(context_p), - buffer_manager(buffer_manager_p), block(std::move(block_p)), block_idx(block_idx_p), block_size(block_size_p), - result_pin(result_pin_p) { + buffer_manager(buffer_manager_p), block(std::move(block_p)), result_pin(result_pin_p) { } void ExecuteTask() override { @@ -84,7 +83,7 @@ class FetchBlockTask : public BaseExecutorTask { try { const idx_t file_size = caching_file_handle.GetFileSize(); - const idx_t offset = block_idx * block_size; + const idx_t offset = block->location; if (offset >= file_size) { lk.lock(); // If there're other workers waiting for this block, we need to reset the block to empty state @@ -93,7 +92,7 @@ class FetchBlockTask : public BaseExecutorTask { block->cv.notify_all(); return; } - const idx_t to_read = MinValue(block_size, file_size - offset); + const idx_t to_read = MinValue(block->size, file_size - offset); auto buf = ExternalFileCache::AllocateCacheBuffer(buffer_manager, caching_file_handle.GetPath(), to_read); caching_file_handle.ReadAndRecord(context, buf.GetDataMutable(), to_read, offset); @@ -142,11 +141,33 @@ class FetchBlockTask : public BaseExecutorTask { QueryContext context; BufferManager &buffer_manager; shared_ptr block; - idx_t block_idx; - idx_t block_size; BufferHandle &result_pin; }; +//===----------------------------------------------------------------------===// +// ReadRangeTask +//===----------------------------------------------------------------------===// + +class ReadRangeTask : public BaseExecutorTask { +public: + ReadRangeTask(CachingFileHandle &caching_file_handle_p, TaskExecutor &executor, QueryContext context_p, + data_ptr_t buffer_p, idx_t nr_bytes_p, idx_t location_p) + : BaseExecutorTask(executor), caching_file_handle(caching_file_handle_p), context(context_p), buffer(buffer_p), + nr_bytes(nr_bytes_p), location(location_p) { + } + + void ExecuteTask() override { + caching_file_handle.ReadAndRecord(context, buffer, nr_bytes, location); + } + +private: + CachingFileHandle &caching_file_handle; + QueryContext context; + data_ptr_t buffer; + idx_t nr_bytes; + idx_t location; +}; + } // namespace //===----------------------------------------------------------------------===// @@ -164,13 +185,13 @@ CachingFileSystem CachingFileSystem::Get(ClientContext &context) { return CachingFileSystem(FileSystem::GetFileSystem(context), *context.db); } -unique_ptr CachingFileSystem::OpenFile(const OpenFileInfo &path, FileOpenFlags flags, +unique_ptr CachingFileSystem::OpenFile(const OpenFileInfo &path, const FileOpenFlags &flags, optional_ptr opener) { return make_uniq(QueryContext(), *this, path, flags, opener); } unique_ptr CachingFileSystem::OpenFile(QueryContext context, const OpenFileInfo &path, - FileOpenFlags flags, optional_ptr opener) { + const FileOpenFlags &flags, optional_ptr opener) { return make_uniq(context, *this, path, flags, opener); } @@ -242,8 +263,7 @@ bool CachingFileHandle::CanUseCache() { return !current_cached_file->validation_info.IsExpired(); } -void CachingFileHandle::ReconcileCacheAfterRead(CachedFile &cached_file, idx_t first_block, - const vector> &blocks) { +void CachingFileHandle::ReconcileCacheAfterRead(CachedFile &cached_file) { CacheValidationInfo current; { const annotated_lock_guard guard(file_handle_mutex); @@ -259,14 +279,14 @@ void CachingFileHandle::ReconcileCacheAfterRead(CachedFile &cached_file, idx_t f cached = current; // Existing readers retain their pinned blocks while future reads fetch replacements. - external_file_cache.RetireBlocks(cached_file, first_block, blocks); + external_file_cache.DropBlocks(cached_file); } CachingFileHandle::CachingFileHandle(QueryContext context, CachingFileSystem &caching_file_system_p, const OpenFileInfo &path_p, FileOpenFlags flags_p, optional_ptr opener_p) - : context(context), caching_file_system(caching_file_system_p), - external_file_cache(caching_file_system.external_file_cache), path(path_p), flags(std::move(flags_p)), + : context(context), file_system(caching_file_system_p.file_system), db(caching_file_system_p.db), + external_file_cache(caching_file_system_p.external_file_cache), path(path_p), flags(std::move(flags_p)), opener(opener_p), validate(ExternalFileCacheUtil::GetCacheValidationMode(path_p, context.GetClientContext(), caching_file_system_p.db)), cached_file(nullptr), position(0) { @@ -304,9 +324,9 @@ shared_ptr CachingFileHandle::GetFileHandle() { // Request parallel access on the underlying filesystem handle to avoid races in implementations that // require explicit opt-in for concurrent pread-style access (e.g., HTTPFS). auto internal_flags = flags | FileFlags::FILE_FLAGS_PARALLEL_ACCESS; - file_handle = caching_file_system.file_system.OpenFile(path, internal_flags, opener); + file_handle = file_system.OpenFile(path, internal_flags, opener); // Snapshot the metadata with a single Stats call, avoiding repeated metadata lookups (e.g., fstat) - validation_info = GetValidationInfo(caching_file_system.file_system, *file_handle); + validation_info = GetValidationInfo(file_system, *file_handle); { annotated_lock_guard meta_guard(cached_file->meta_lock); @@ -317,9 +337,7 @@ shared_ptr CachingFileHandle::GetFileHandle() { !refresh_expired_cache && ExternalFileCache::IsValid(Validate(), cached_file->validation_info, validation_info); if (!cache_is_valid) { - annotated_lock_guard map_guard(cached_file->map_lock); - cached_file->blocks.clear(); - cached_file->cached_block_size.SetInvalid(); + external_file_cache.DropBlocks(*cached_file); } // A successful validator check refreshes freshness. Without validators, preserve the original deadline. const bool revalidated = cache_is_valid && ExternalFileCache::HasValidationMetadata(validation_info); @@ -346,31 +364,57 @@ FileBufferHandleGroup CachingFileHandle::Read(const idx_t nr_bytes, const idx_t if (!external_file_cache.IsEnabled() || !external_file_cache.ShouldCacheFile(path.path) || !CanUseCache()) { auto buf = AllocateUncachedReadBuffer(external_file_cache.GetBufferManager(), nr_bytes); - ReadAndRecord(context, buf.GetDataMutable(), nr_bytes, location); + ReadUncached(buf.GetDataMutable(), nr_bytes, location); vector mem_handles; mem_handles.push_back({std::move(buf), 0, nr_bytes}); return FileBufferHandleGroup(std::move(mem_handles)); } auto current_cached_file = EnsureCachedFileCurrent(); - const idx_t block_size = external_file_cache.GetCacheBlockSize(current_cached_file->path); - const idx_t first_block = location / block_size; - const idx_t last_block = (location + nr_bytes - 1) / block_size; - const idx_t num_blocks = last_block - first_block + 1; - - // Atomically reindex (if needed) and acquire the block range. - auto blocks = - external_file_cache.ReindexAndAcquireBlocks(*current_cached_file, block_size, first_block, num_blocks); + const idx_t file_size = GetFileSize(); + if (location >= file_size) { + return FileBufferHandleGroup(); + } + const idx_t max_block_size = external_file_cache.GetCacheMaxBlockSize(current_cached_file->path); + const idx_t min_block_size = + MinValue(external_file_cache.GetCacheMinBlockSize(current_cached_file->path), max_block_size); + auto request_sizing = flags.GetRequestSizing(); + switch (Settings::Get(db)) { + case ExternalFileCacheRequestSizing::GRID: + request_sizing = RequestSizing::BY_CACHE; + break; + case ExternalFileCacheRequestSizing::EXACT: + request_sizing = RequestSizing::BY_READER; + break; + case ExternalFileCacheRequestSizing::AUTO: + break; + } + idx_t fetch_location = location; + idx_t fetch_end = location + nr_bytes; + if (file_size <= max_block_size) { + // fetch a file that fits in one block whole, so later reads of it hit the cache + fetch_location = 0; + fetch_end = file_size; + } else if (request_sizing == RequestSizing::BY_CACHE) { + fetch_location = location - location % max_block_size; + fetch_end = AlignValue(fetch_end, max_block_size); + } else { + fetch_location = location - location % min_block_size; + fetch_end = AlignValue(fetch_end, min_block_size); + } + fetch_end = MinValue(fetch_end, file_size); + auto blocks = external_file_cache.AcquireBlocks(*current_cached_file, fetch_location, fetch_end - fetch_location, + max_block_size); + const idx_t num_blocks = blocks.size(); // Schedule block fetch tasks for all blocks. vector pins(num_blocks); - auto &scheduler = TaskScheduler::GetScheduler(caching_file_system.db); + auto &scheduler = TaskScheduler::GetScheduler(db); TaskExecutor executor(scheduler, TaskSchedulerType::ASYNC); for (idx_t idx = 0; idx < num_blocks; idx++) { - executor.ScheduleTask(make_uniq(*this, executor, context, - external_file_cache.GetBufferManager(), blocks[idx], - first_block + idx, block_size, pins[idx])); + executor.ScheduleTask(make_uniq( + *this, executor, context, external_file_cache.GetBufferManager(), blocks[idx], pins[idx])); } executor.WorkOnTasks(); @@ -378,12 +422,15 @@ FileBufferHandleGroup CachingFileHandle::Read(const idx_t nr_bytes, const idx_t vector mem_handles; mem_handles.reserve(num_blocks); idx_t remaining = nr_bytes; - for (idx_t idx = 0; idx < num_blocks; idx++) { - const idx_t block_start = (first_block + idx) * block_size; - const idx_t offset_in_block = (idx == 0) ? (location - block_start) : 0; + for (idx_t idx = 0; idx < num_blocks && remaining > 0; idx++) { + auto &block = *blocks[idx]; + if (block.location + block.size <= location) { + // part of a small file fetched whole, before the requested range + continue; + } + const idx_t offset_in_block = location > block.location ? location - block.location : 0; idx_t block_valid_bytes = 0; { - auto &block = *blocks[idx]; annotated_lock_guard block_guard(block.mtx); block_valid_bytes = block.nr_bytes; } @@ -394,11 +441,30 @@ FileBufferHandleGroup CachingFileHandle::Read(const idx_t nr_bytes, const idx_t remaining -= length; } - ReconcileCacheAfterRead(*current_cached_file, first_block, blocks); + ReconcileCacheAfterRead(*current_cached_file); return FileBufferHandleGroup(std::move(mem_handles)); } +void CachingFileHandle::ReadUncached(data_ptr_t buffer, idx_t nr_bytes, idx_t location) { + const idx_t max_block_size = external_file_cache.GetCacheMaxBlockSize(path.path); + auto &scheduler = TaskScheduler::GetScheduler(db); + // idle worker threads also run async tasks, so the pieces are only sure to run serially without any other thread + const bool single_threaded = scheduler.NumberOfThreads() <= 1 && scheduler.NumberOfAsyncThreads() == 0; + if (nr_bytes <= max_block_size || !external_file_cache.ShouldCacheFile(path.path) || !CanSeek() || + single_threaded) { + ReadAndRecord(context, buffer, nr_bytes, location); + return; + } + // split like cached reads, so the requests are the same whether or not the cache is enabled + TaskExecutor executor(scheduler, TaskSchedulerType::ASYNC); + for (idx_t offset = 0; offset < nr_bytes; offset += max_block_size) { + executor.ScheduleTask(make_uniq(*this, executor, context, buffer + offset, + MinValue(max_block_size, nr_bytes - offset), location + offset)); + } + executor.WorkOnTasks(); +} + FileBufferHandleGroup CachingFileHandle::Read(idx_t &nr_bytes) { // If we can't seek, we can't use the cache for these calls, // because we won't be able to seek over any parts we skipped by reading from the cache @@ -550,6 +616,12 @@ bool CachingFileHandle::TryGetNetworkThroughput(NetworkThroughputEstimate &resul void CachingFileHandle::ReadAndRecord(QueryContext context, data_ptr_t buffer, idx_t nr_bytes, idx_t location) { auto handle = GetFileHandle(); + if (handle->file_system.IsLocalFileSystem()) { + // Pre-fault local read buffers before the kernel fills them. + for (idx_t offset = 0; offset < nr_bytes; offset += 4096) { + buffer[offset] = 0; + } + } const auto read_start = steady_clock::now(); handle->Read(context, buffer, nr_bytes, location); auto cache_valid_until = handle->file_system.GetCacheValidUntil(*handle); diff --git a/src/duckdb/src/storage/external_file_cache/caching_file_system_wrapper.cpp b/src/duckdb/src/storage/external_file_cache/caching_file_system_wrapper.cpp index 9442f056e..c54fb20a8 100644 --- a/src/duckdb/src/storage/external_file_cache/caching_file_system_wrapper.cpp +++ b/src/duckdb/src/storage/external_file_cache/caching_file_system_wrapper.cpp @@ -221,6 +221,11 @@ FileMetadata CachingFileSystemWrapper::Stats(FileHandle &handle) { return underlying_file_system.Stats(*file_handle); } +optional CachingFileSystemWrapper::GetStatsIfExists(const OpenFileInfo &file, + optional_ptr opener) { + return underlying_file_system.GetStatsIfExists(file, opener); +} + //===----------------------------------------------------------------------===// // Directory Operations //===----------------------------------------------------------------------===// diff --git a/src/duckdb/src/storage/external_file_cache/external_file_cache.cpp b/src/duckdb/src/storage/external_file_cache/external_file_cache.cpp index 7573dd9d1..01fa9d64e 100644 --- a/src/duckdb/src/storage/external_file_cache/external_file_cache.cpp +++ b/src/duckdb/src/storage/external_file_cache/external_file_cache.cpp @@ -14,6 +14,33 @@ namespace duckdb { +namespace { + +bool IsDroppedBlock(CacheBlock &block) { + const annotated_lock_guard block_guard(block.mtx); + if (block.state != CacheBlockState::LOADED || !block.block_handle) { + return false; + } + auto &memory = block.block_handle->GetMemory(); + return memory.IsUnloaded() && !memory.MustWriteToTemporaryFile(); +} + +bool IsLoadedBlock(CacheBlock &block) { + const annotated_lock_guard block_guard(block.mtx); + return block.state == CacheBlockState::LOADED; +} + +idx_t GapBlockCount(idx_t nr_bytes, idx_t max_block_size) { + return (nr_bytes + max_block_size - 1) / max_block_size; +} + +//! Whether the last modified timestamp is usable as a cache validator +bool HasUsableLastModified(timestamp_t last_modified) { + return last_modified.IsFinite() && last_modified != timestamp_t(0); +} + +} // namespace + bool CacheValidationInfo::IsCacheReuseProhibited() const { return cache_valid_until && *cache_valid_until == timestamp_t::ninfinity(); } @@ -47,7 +74,7 @@ class ExternalFileCache::ExternalFileCacheObjectCacheEntry : public ObjectCacheE const annotated_lock_guard meta_guard(cached_file->meta_lock); file_size = cached_file->validation_info.file_size; } - const idx_t block_size = cache.GetCacheBlockSize(cached_file->path); + const idx_t block_size = cache.GetCacheMaxBlockSize(cached_file->path); const idx_t num_blocks = (file_size + block_size - 1) / block_size; // Estimated memory consumption for each block metadata. static constexpr idx_t BLOCK_METADATA_SIZE = sizeof(CacheBlock); @@ -65,12 +92,20 @@ class ExternalFileCache::ExternalFileCacheObjectCacheEntry : public ObjectCacheE shared_ptr cached_file; }; -idx_t ExternalFileCache::GetCacheBlockSize(const string &path) const { +idx_t ExternalFileCache::GetCacheMaxBlockSize(const string &path) const { auto &db = buffer_manager.GetDatabase(); if (FileSystem::IsRemoteFile(path)) { - return Settings::Get(db); + return Settings::Get(db); } - return Settings::Get(db); + return Settings::Get(db); +} + +idx_t ExternalFileCache::GetCacheMinBlockSize(const string &path) const { + if (!FileSystem::IsRemoteFile(path)) { + return 1; + } + auto &db = buffer_manager.GetDatabase(); + return Settings::Get(db); } bool ExternalFileCache::ShouldCacheFile(const string &path) const { @@ -82,158 +117,82 @@ bool ExternalFileCache::ShouldCacheFile(const string &path) const { return Settings::Get(db); } -void ExternalFileCache::ReindexCachedFileCore(CachedFile &cached_file, idx_t file_size, idx_t old_block_size, - idx_t new_block_size) { - D_ASSERT(old_block_size > 0); - D_ASSERT(new_block_size > 0); - - // Phase 1: Pin all LOADED old blocks, sorted by block index. - map> pinned; - for (auto &block_entry : cached_file.blocks) { - const idx_t old_idx = block_entry.first; - auto &block = *block_entry.second; - const annotated_lock_guard block_guard(block.mtx); - if (block.state != CacheBlockState::LOADED || !block.block_handle) { - continue; - } - if (block.block_handle->GetMemory().IsUnloaded()) { - // Evicted blocks do not survive a re-index, whether they spilled or were dropped: - // pinning all of them to copy them over could exceed the memory limit - continue; - } - auto pin = buffer_manager.Pin(block.block_handle); - if (pin.IsValid()) { - pinned.emplace(old_idx, make_pair(std::move(pin), block.nr_bytes)); - } - } +vector> ExternalFileCache::AcquireBlocks(CachedFile &cached_file, idx_t location, idx_t nr_bytes, + idx_t max_block_size) { + D_ASSERT(nr_bytes > 0); + D_ASSERT(max_block_size > 0); + const idx_t end = location + nr_bytes; + // smaller cached blocks between two gaps are re-fetched with them when that saves a request + const idx_t absorb_size = max_block_size / 8; - if (pinned.empty()) { - cached_file.blocks.clear(); - return; - } - - // Phase 2: Find contiguous runs of old blocks and create new blocks from each run. - // A new block is only created if its entire byte range is covered by the run. - unordered_map> new_blocks; - - auto it = pinned.begin(); - while (it != pinned.end()) { - // Find a contiguous run of old blocks starting at current block. - const idx_t run_byte_start = it->first * old_block_size; - idx_t run_byte_end = run_byte_start; - idx_t expected_idx = it->first; - auto run_end = it; - while (run_end != pinned.end() && run_end->first == expected_idx) { - run_byte_end = run_end->first * old_block_size + run_end->second.second; - expected_idx++; - ++run_end; + const annotated_lock_guard map_guard(cached_file.map_lock); + auto &blocks = cached_file.blocks; + // start at the block that covers `location`, if any + auto it = blocks.upper_bound(location); + if (it != blocks.begin()) { + auto prev = std::prev(it); + if (prev->first + prev->second->size > location) { + it = prev; } + } - // This contiguous run covers file bytes [run_byte_start, run_byte_end). - // Create all new blocks whose byte range fits entirely within this run. - const idx_t first_new = run_byte_start / new_block_size; - const idx_t last_new = (run_byte_end - 1) / new_block_size; - - for (idx_t new_idx = first_new; new_idx <= last_new; new_idx++) { - const idx_t new_start = new_idx * new_block_size; - const idx_t new_end = MinValue(new_start + new_block_size, file_size); - if (!(new_start < new_end && new_start >= run_byte_start && new_end <= run_byte_end)) { + vector> result; + idx_t pos = location; + while (pos < end) { + if (it != blocks.end() && it->first <= pos) { + if (!IsDroppedBlock(*it->second) || it->second.use_count() > 1) { + result.push_back(it->second); + pos = it->first + it->second->size; + ++it; continue; } - const idx_t new_size = new_end - new_start; - - auto buf = AllocateCacheBuffer(buffer_manager, cached_file.path, new_size); - - // Copy from each contributing old block in the run. - const idx_t contrib_first = new_start / old_block_size; - const idx_t contrib_last = (new_end - 1) / old_block_size; - for (idx_t oi = contrib_first; oi <= contrib_last; oi++) { - auto &old_entry = pinned.at(oi); - const idx_t oi_file_start = oi * old_block_size; - const idx_t copy_start = MaxValue(new_start, oi_file_start); - const idx_t copy_end = MinValue(new_end, oi_file_start + old_entry.second); - if (copy_start >= copy_end) { - continue; - } - memcpy(buf.GetDataMutable() + (copy_start - new_start), - old_entry.first.Ptr() + (copy_start - oi_file_start), copy_end - copy_start); + // re-fetch a dropped block only as far as this read needs it + it = blocks.erase(it); + } + // create blocks for the missing bytes up to the next cached block + idx_t gap_end = it == blocks.end() ? end : MinValue(end, it->first); + while (it != blocks.end() && it->first < end && it->second->size < absorb_size && IsLoadedBlock(*it->second)) { + const idx_t block_end = it->first + it->second->size; + const auto next = std::next(it); + const idx_t next_gap_end = next == blocks.end() ? end : MinValue(end, next->first); + if (block_end >= next_gap_end) { + break; } - - auto new_block = make_shared_ptr(); - { - const annotated_lock_guard block_guard(new_block->mtx); - new_block->block_handle = buf.GetBlockHandle(); - new_block->nr_bytes = new_size; - new_block->state = CacheBlockState::LOADED; -#ifdef DEBUG - new_block->checksum = Checksum(buf.Ptr(), new_size); -#endif + const idx_t gap_before = gap_end - pos; + const idx_t gap_after = next_gap_end - block_end; + if (GapBlockCount(gap_before + it->second->size + gap_after, max_block_size) >= + GapBlockCount(gap_before, max_block_size) + GapBlockCount(gap_after, max_block_size)) { + break; } - new_blocks[new_idx] = std::move(new_block); + it = blocks.erase(it); + gap_end = next_gap_end; + } + while (pos < gap_end) { + const idx_t size = MinValue(gap_end - pos, max_block_size); + auto block = make_shared_ptr(pos, size); + blocks.emplace_hint(it, pos, block); + result.push_back(std::move(block)); + pos += size; } - - it = run_end; } - - // Phase 3: Replace old blocks with new blocks. - cached_file.blocks = std::move(new_blocks); + return result; } -vector> ExternalFileCache::ReindexAndAcquireBlocks(CachedFile &cached_file, - idx_t current_block_size, idx_t first_block, - idx_t num_blocks) { - D_ASSERT(current_block_size > 0); - - idx_t file_size = 0; - { - const annotated_lock_guard meta_guard(cached_file.meta_lock); - file_size = cached_file.validation_info.file_size; - } - +void ExternalFileCache::DropBlocks(CachedFile &cached_file) { const annotated_lock_guard map_guard(cached_file.map_lock); - - if (cached_file.cached_block_size.IsValid() && cached_file.cached_block_size.GetIndex() != current_block_size) { - const idx_t old_block_size = cached_file.cached_block_size.GetIndex(); - if (file_size > 0) { - ReindexCachedFileCore(cached_file, file_size, old_block_size, current_block_size); - } - } - cached_file.cached_block_size = current_block_size; - - vector> blocks(num_blocks); - for (idx_t idx = 0; idx < num_blocks; idx++) { - const idx_t block_idx = first_block + idx; - auto &entry = cached_file.blocks[block_idx]; - if (!entry) { - entry = make_shared_ptr(); - } - blocks[idx] = entry; - } - return blocks; + cached_file.blocks.clear(); + cached_file.content_generation++; } -void ExternalFileCache::RetireBlocks(CachedFile &cached_file, idx_t first_block, - const vector> &blocks) { +idx_t ExternalFileCache::GetContentGeneration(CachedFile &cached_file) { const annotated_lock_guard map_guard(cached_file.map_lock); - for (idx_t idx = 0; idx < blocks.size(); idx++) { - const auto block_idx = first_block + idx; - auto entry = cached_file.blocks.find(block_idx); - if (entry == cached_file.blocks.end() || entry->second != blocks[idx]) { - continue; - } - cached_file.blocks.erase(entry); - } + return cached_file.content_generation; } ExternalFileCache::CachedFile::CachedFile(string path_p, idx_t generation_p) : path(std::move(path_p)), generation(generation_p) { } -//! Whether the last modified timestamp is usable as a cache validator -static bool HasUsableLastModified(timestamp_t last_modified) { - return last_modified.IsFinite() && last_modified != timestamp_t(0); -} - bool ExternalFileCache::IsValid(bool validate, const string &cached_version_tag, timestamp_t cached_last_modified, const string ¤t_version_tag, timestamp_t current_last_modified) { if (!validate) { @@ -342,17 +301,14 @@ vector ExternalFileCache::GetCachedFileInformation() cons } auto file = entry->GetCachedFile(); const annotated_lock_guard map_guard(file->map_lock); - const idx_t block_size = - file->cached_block_size.IsValid() ? file->cached_block_size.GetIndex() : GetCacheBlockSize(file->path); for (const auto &block_entry : file->blocks) { - const idx_t block_idx = block_entry.first; const auto &block = *block_entry.second; annotated_lock_guard block_guard(block.mtx); if (block.state != CacheBlockState::LOADED || !block.block_handle) { continue; } - const idx_t location = block_idx * block_size; + const idx_t location = block.location; const auto &memory = block.block_handle->GetMemory(); const bool loaded = !memory.IsUnloaded(); // An unloaded cache block is spilled if it still has a temporary file backing diff --git a/src/duckdb/src/storage/index_entry.cpp b/src/duckdb/src/storage/index_entry.cpp index e1c0c3308..b1a5da5ec 100644 --- a/src/duckdb/src/storage/index_entry.cpp +++ b/src/duckdb/src/storage/index_entry.cpp @@ -14,8 +14,8 @@ namespace duckdb { -IndexEntry::IndexEntry(unique_ptr index_p, const ConstraintCheckMode check_mode_p) - : owned_index(std::move(index_p)), check_mode(check_mode_p) { +IndexEntry::IndexEntry(unique_ptr index_p, idx_t index_oid_p, const ConstraintCheckMode check_mode_p) + : index_oid(index_oid_p), owned_index(std::move(index_p)), check_mode(check_mode_p) { if (owned_index->IsBound()) { bind_state = IndexBindState::BOUND; } else { @@ -120,10 +120,10 @@ void IndexEntry::InitializeLocalIndexesInternal(TableIndexList &delete_indexes, } auto constraint_type = bound_index.GetConstraintType(); - delete_indexes.AddIndex(bound_index.CreateEmptyCopy(constraint_type), ConstraintCheckMode::DEFAULT); + delete_indexes.AddIndex(bound_index.CreateEmptyCopy(constraint_type), index_oid, ConstraintCheckMode::DEFAULT); // Deferred constraints have no append index: transaction-local rows are verified when committing. if (append_indexes && !IsDeferred()) { - append_indexes->AddIndex(bound_index.CreateEmptyCopy(constraint_type), ConstraintCheckMode::DEFAULT); + append_indexes->AddIndex(bound_index.CreateEmptyCopy(constraint_type), index_oid, ConstraintCheckMode::DEFAULT); } } @@ -333,6 +333,14 @@ string IndexEntry::GetIndexType() const { return owned_index->GetIndexType(); } +bool IndexEntry::HasBufferedReplays() const { + auto entry_lock = lock.GetSharedLock(); + if (!owned_index || owned_index->IsBound()) { + return false; + } + return owned_index->Cast().HasBufferedReplays(); +} + void IndexEntry::Retire() { auto entry_lock = lock.GetExclusiveLock(); deltas.Reset(); @@ -371,6 +379,10 @@ void IndexEntry::VerifyAppend(const shared_ptr &delete_entry, DataCh } } bound_index.VerifyAppend(chunk, index_append_info, manager); + if (auto delta = deltas.Find(IndexDeltaType::ADDED_DATA_DURING_CHECKPOINT)) { + // rows committed while a checkpoint runs are only in this delta + delta->VerifyAppend(chunk, index_append_info, manager); + } } void IndexEntry::VerifyForeignKey(const shared_ptr &delete_entry, DataChunk &chunk, diff --git a/src/duckdb/src/storage/local_storage.cpp b/src/duckdb/src/storage/local_storage.cpp index 4e962a4fc..b1f842505 100644 --- a/src/duckdb/src/storage/local_storage.cpp +++ b/src/duckdb/src/storage/local_storage.cpp @@ -36,7 +36,7 @@ LocalTableStorage::LocalTableStorage(ClientContext &context, DataTable &new_data const vector &bound_columns, Expression &cast_expr, TransactionData transaction) : context(context), table_ref(new_data_table), allocator(Allocator::Get(new_data_table.db)), - deleted_rows(parent.deleted_rows), optimistic_collections(std::move(parent.optimistic_collections)), + deleted_rows(parent.deleted_rows.load()), optimistic_collections(std::move(parent.optimistic_collections)), optimistic_writer(new_data_table, parent.optimistic_writer) { // Alter the column type. auto &parent_collection = *parent.row_groups->collection; @@ -45,6 +45,7 @@ LocalTableStorage::LocalTableStorage(ClientContext &context, DataTable &new_data parent_collection.CommitDropColumn(alter_column_index); row_groups = std::move(parent.row_groups); row_groups->collection = std::move(new_collection); + row_groups->ResetCollectionAccounting(); append_indexes.Move(parent.append_indexes); delete_indexes.Move(parent.delete_indexes); @@ -52,7 +53,7 @@ LocalTableStorage::LocalTableStorage(ClientContext &context, DataTable &new_data LocalTableStorage::LocalTableStorage(DataTable &new_data_table, LocalTableStorage &parent, const idx_t drop_column_index) - : table_ref(new_data_table), allocator(Allocator::Get(new_data_table.db)), deleted_rows(parent.deleted_rows), + : table_ref(new_data_table), allocator(Allocator::Get(new_data_table.db)), deleted_rows(parent.deleted_rows.load()), optimistic_collections(std::move(parent.optimistic_collections)), optimistic_writer(new_data_table, parent.optimistic_writer) { // Remove the column from the previous table storage. @@ -61,6 +62,7 @@ LocalTableStorage::LocalTableStorage(DataTable &new_data_table, LocalTableStorag parent_collection.CommitDropColumn(drop_column_index); row_groups = std::move(parent.row_groups); row_groups->collection = std::move(new_collection); + row_groups->ResetCollectionAccounting(); append_indexes.Move(parent.append_indexes); delete_indexes.Move(parent.delete_indexes); @@ -68,13 +70,14 @@ LocalTableStorage::LocalTableStorage(DataTable &new_data_table, LocalTableStorag LocalTableStorage::LocalTableStorage(ClientContext &context, DataTable &new_dt, LocalTableStorage &parent, ColumnDefinition &new_column, ExpressionExecutor &default_executor) - : table_ref(new_dt), allocator(Allocator::Get(new_dt.db)), deleted_rows(parent.deleted_rows), + : table_ref(new_dt), allocator(Allocator::Get(new_dt.db)), deleted_rows(parent.deleted_rows.load()), optimistic_collections(std::move(parent.optimistic_collections)), optimistic_writer(new_dt, parent.optimistic_writer) { auto &parent_collection = *parent.row_groups->collection; auto new_collection = parent_collection.AddColumn(context, new_column, default_executor); row_groups = std::move(parent.row_groups); row_groups->collection = std::move(new_collection); + row_groups->ResetCollectionAccounting(); append_indexes.Move(parent.append_indexes); delete_indexes.Move(parent.delete_indexes); } @@ -281,7 +284,7 @@ void LocalTableStorage::Rollback() { //===--------------------------------------------------------------------===// // LocalTableManager //===--------------------------------------------------------------------===// -optional_ptr LocalTableManager::GetStorage(DataTable &table) const { +optional_ptr LocalTableManager::GetStorage(const DataTable &table) const { lock_guard l(table_storage_lock); auto entry = table_storage.find(table); return entry == table_storage.end() ? nullptr : entry->second.get(); @@ -293,7 +296,7 @@ LocalTableStorage &LocalTableManager::GetOrCreateStorage(ClientContext &context, if (entry == table_storage.end()) { auto new_storage = make_shared_ptr(context, table); auto storage = new_storage.get(); - table_storage.insert(make_pair(reference(table), std::move(new_storage))); + table_storage.insert(make_pair(reference(table), std::move(new_storage))); return *storage; } else { return *entry->second.get(); @@ -316,7 +319,7 @@ shared_ptr LocalTableManager::MoveEntry(DataTable &table) { return storage_entry; } -reference_map_t> LocalTableManager::MoveEntries() { +reference_map_t> LocalTableManager::MoveEntries() { lock_guard l(table_storage_lock); return std::move(table_storage); } @@ -561,6 +564,7 @@ void LocalStorage::Update(DataTable &table, DuckTableEntry &table_entry, Vector void LocalStorage::Flush(DataTable &table, LocalTableStorage &storage, optional_ptr commit_state) { if (storage.is_dropped) { + storage.Rollback(); return; } if (storage.GetCollection().GetTotalRows() <= storage.deleted_rows) { @@ -591,9 +595,8 @@ void LocalStorage::Flush(DataTable &table, LocalTableStorage &storage, optional_ // check if we have written data // if we have, we cannot merge to disk after all // so we need to revert the data we have already written - // this only happens for transactions that deleted rows after bulk-appending: a pure bulk - // append always takes the merge path above, using its pre-flushed blocks as written - D_ASSERT(!storage.HasFlushedRowGroups() || storage.deleted_rows > 0); + // this happens when rows were deleted after a bulk append, or when the optimistic writer + // flushed a partial row group that does not qualify as a bulk append storage.Rollback(); // append to the indexes storage.AppendToIndexes(transaction, append_state); @@ -634,9 +637,9 @@ void LocalStorage::Commit(optional_ptr commit_state) { // after this, the local storage is no longer required and can be cleared auto table_storage = table_manager.MoveEntries(); for (auto &entry : table_storage) { - auto table = entry.first; auto storage = entry.second.get(); - Flush(table, *storage, commit_state); + D_ASSERT(RefersToSameObject(entry.first, storage->table_ref)); + Flush(storage->table_ref, *storage, commit_state); entry.second.reset(); } } @@ -745,7 +748,7 @@ TableIndexList &LocalStorage::GetIndexes(ClientContext &context, DataTable &tabl return storage.append_indexes; } -optional_ptr LocalStorage::GetStorage(DataTable &table) { +optional_ptr LocalStorage::GetStorage(const DataTable &table) { return table_manager.GetStorage(table); } diff --git a/src/duckdb/src/storage/metadata/metadata_manager.cpp b/src/duckdb/src/storage/metadata/metadata_manager.cpp index dfa05660c..b465283b2 100644 --- a/src/duckdb/src/storage/metadata/metadata_manager.cpp +++ b/src/duckdb/src/storage/metadata/metadata_manager.cpp @@ -101,30 +101,38 @@ MetadataHandle MetadataManager::Pin(const MetadataPointer &pointer) { MetadataHandle MetadataManager::Pin(const QueryContext &context, const MetadataPointer &pointer) { D_ASSERT(pointer.index < METADATA_BLOCK_COUNT); - shared_ptr block_handle; - { - lock_guard guard(block_lock); - auto entry = blocks.find(UnsafeNumericCast(pointer.block_index)); - if (entry == blocks.end()) { - throw InternalException("Trying to pin block %llu - but the block did not exist", pointer.block_index); - } - auto &block = entry->second; + while (true) { + shared_ptr block_handle; + { + lock_guard guard(block_lock); + auto entry = blocks.find(UnsafeNumericCast(pointer.block_index)); + if (entry == blocks.end()) { + throw InternalException("Trying to pin block %llu - but the block did not exist", pointer.block_index); + } + auto &block = entry->second; #ifdef DEBUG - for (auto &free_block : block.free_blocks) { - if (free_block == pointer.index) { - throw InternalException("Pinning block %d.%d but it is marked as a free block", block.block_id, - free_block); + for (auto &free_block : block.free_blocks) { + if (free_block == pointer.index) { + throw InternalException("Pinning block %d.%d but it is marked as a free block", block.block_id, + free_block); + } } - } #endif - block_handle = block.block; - } + block_handle = block.block; + } - MetadataHandle handle; - handle.pointer.block_index = pointer.block_index; - handle.pointer.index = pointer.index; - handle.handle = buffer_manager.Pin(block_handle); - return handle; + auto buffer_handle = buffer_manager.Pin(block_handle); + if (!buffer_handle.IsValid()) { + // the handle was replaced by ConvertToTransient while we were pinning it and can no + // longer be loaded from disk - retry with the current handle + continue; + } + MetadataHandle handle; + handle.pointer.block_index = pointer.block_index; + handle.pointer.index = pointer.index; + handle.handle = std::move(buffer_handle); + return handle; + } } void MetadataManager::ConvertToTransient(unique_lock &block_lock, MetadataBlock &metadata_block) { @@ -141,6 +149,13 @@ void MetadataManager::ConvertToTransient(unique_lock &block_lock, Metadat // copy the data to the transient block memcpy(new_buffer.GetDataMutable(), old_buffer.Ptr(), block_manager.GetBlockSize()); + // the disk block now belongs to the transient block and is rewritten in place by a later + // checkpoint - the old handle must not reload it from disk after its buffer is evicted + { + auto old_block_lock = old_block->GetMemory().GetLock(); + old_block->GetMemory().SetDestroyBufferUpon(DestroyBufferUpon::EVICTION); + } + // unregister the old block block_manager.UnregisterBlock(metadata_block.block_id); diff --git a/src/duckdb/src/storage/open_file_storage_extension.cpp b/src/duckdb/src/storage/open_file_storage_extension.cpp index 3315cdf1f..345b43ebb 100644 --- a/src/duckdb/src/storage/open_file_storage_extension.cpp +++ b/src/duckdb/src/storage/open_file_storage_extension.cpp @@ -1,3 +1,4 @@ +#include "duckdb/parser/parser.hpp" #include "duckdb/storage/storage_extension.hpp" #include "duckdb/parser/parsed_data/attach_info.hpp" #include "duckdb/catalog/duck_catalog.hpp" @@ -26,7 +27,8 @@ class OpenFileDefaultGenerator : public DefaultGenerator { auto result = make_uniq(); result->SetQualifiedName(QualifiedName({Identifier::DefaultSchema()}, entry)); result->sql = StringUtil::Format("SELECT * FROM %s", SQLString(file)); - auto view_info = CreateViewInfo::FromSelect(context, std::move(result)); + auto parser = Parser::GetBuiltinParser(); + auto view_info = CreateViewInfo::FromSelect(parser, std::move(result)); return make_uniq_base(catalog, schema, *view_info); } } diff --git a/src/duckdb/src/storage/optimistic_data_writer.cpp b/src/duckdb/src/storage/optimistic_data_writer.cpp index e155b2e10..7c2569d53 100644 --- a/src/duckdb/src/storage/optimistic_data_writer.cpp +++ b/src/duckdb/src/storage/optimistic_data_writer.cpp @@ -121,6 +121,13 @@ void OptimisticWriteCollection::FinalizeFlush() { unflushed_data_size = 0; } +void OptimisticWriteCollection::ResetCollectionAccounting() { + prev_allocated_size = 0; + unflushed_data_size = 0; + unflushed_row_groups.clear(); + flushed_row_groups.clear(); +} + void OptimisticDataWriter::WriteUnflushedRowGroups(OptimisticWriteCollection &row_groups) { auto total_row_groups = row_groups.collection->GetRowGroupCount(); if (row_groups.flushed_row_groups.size() == total_row_groups && row_groups.partial_block_managers.empty()) { diff --git a/src/duckdb/src/storage/serialization/serialize_create_info.cpp b/src/duckdb/src/storage/serialization/serialize_create_info.cpp index 289eed168..8e01fed44 100644 --- a/src/duckdb/src/storage/serialization/serialize_create_info.cpp +++ b/src/duckdb/src/storage/serialization/serialize_create_info.cpp @@ -150,10 +150,12 @@ unique_ptr CreateMacroInfo::Deserialize(Deserializer &deserializer) void CreateSchemaInfo::Serialize(Serializer &serializer) const { CreateInfo::Serialize(serializer); + serializer.WritePropertyWithDefault>>(200, "options", options); } unique_ptr CreateSchemaInfo::Deserialize(Deserializer &deserializer) { auto result = duckdb::unique_ptr(new CreateSchemaInfo()); + deserializer.ReadPropertyWithDefault>>(200, "options", result->options); return std::move(result); } diff --git a/src/duckdb/src/storage/serialization/serialize_logical_operator.cpp b/src/duckdb/src/storage/serialization/serialize_logical_operator.cpp index f0f41016b..440ba5ba4 100644 --- a/src/duckdb/src/storage/serialization/serialize_logical_operator.cpp +++ b/src/duckdb/src/storage/serialization/serialize_logical_operator.cpp @@ -555,6 +555,7 @@ void LogicalExplain::Serialize(Serializer &serializer) const { serializer.WritePropertyWithDefault(201, "physical_plan", physical_plan); serializer.WritePropertyWithDefault(202, "logical_plan_unopt", logical_plan_unopt); serializer.WritePropertyWithDefault(203, "logical_plan_opt", logical_plan_opt); + serializer.WritePropertyWithDefault>(204, "sql_output_names", sql_output_names, vector()); } unique_ptr LogicalExplain::Deserialize(Deserializer &deserializer) { @@ -563,6 +564,7 @@ unique_ptr LogicalExplain::Deserialize(Deserializer &deserializ deserializer.ReadPropertyWithDefault(201, "physical_plan", result->physical_plan); deserializer.ReadPropertyWithDefault(202, "logical_plan_unopt", result->logical_plan_unopt); deserializer.ReadPropertyWithDefault(203, "logical_plan_opt", result->logical_plan_opt); + deserializer.ReadPropertyWithExplicitDefault>(204, "sql_output_names", result->sql_output_names, vector()); return std::move(result); } @@ -673,12 +675,14 @@ void LogicalLimit::Serialize(Serializer &serializer) const { LogicalOperator::Serialize(serializer); serializer.WriteProperty(200, "limit_val", limit_val); serializer.WriteProperty(201, "offset_val", offset_val); + serializer.WritePropertyWithDefault(202, "unpruned_offset", unpruned_offset, optional_idx()); } unique_ptr LogicalLimit::Deserialize(Deserializer &deserializer) { auto limit_val = deserializer.ReadProperty(200, "limit_val"); auto offset_val = deserializer.ReadProperty(201, "offset_val"); auto result = duckdb::unique_ptr(new LogicalLimit(std::move(limit_val), std::move(offset_val))); + deserializer.ReadPropertyWithExplicitDefault(202, "unpruned_offset", result->unpruned_offset, optional_idx()); return std::move(result); } @@ -836,12 +840,30 @@ void LogicalSecureView::Serialize(Serializer &serializer) const { LogicalOperator::Serialize(serializer); serializer.WritePropertyWithDefault(200, "view_name", view_name); serializer.WritePropertyWithDefault>(201, "pushed_filters", pushed_filters); + serializer.WritePropertyWithDefault(202, "has_source", has_source); + serializer.WritePropertyWithDefault(203, "source_name", source_name, QualifiedName()); + serializer.WritePropertyWithDefault>(204, "source_types", source_types); + serializer.WritePropertyWithDefault(205, "has_at_clause", has_at_clause); + serializer.WritePropertyWithDefault(206, "at_unit", at_unit); + serializer.WritePropertyWithDefault(207, "at_value", at_value, Value()); + serializer.WritePropertyWithDefault>(208, "output_bindings", output_bindings); + serializer.WritePropertyWithDefault>>(209, "output_expressions", output_expressions); + serializer.WritePropertyWithDefault>>(210, "source_filters", source_filters); } unique_ptr LogicalSecureView::Deserialize(Deserializer &deserializer) { auto result = duckdb::unique_ptr(new LogicalSecureView()); deserializer.ReadPropertyWithDefault(200, "view_name", result->view_name); deserializer.ReadPropertyWithDefault>(201, "pushed_filters", result->pushed_filters); + deserializer.ReadPropertyWithDefault(202, "has_source", result->has_source); + deserializer.ReadPropertyWithExplicitDefault(203, "source_name", result->source_name, QualifiedName()); + deserializer.ReadPropertyWithDefault>(204, "source_types", result->source_types); + deserializer.ReadPropertyWithDefault(205, "has_at_clause", result->has_at_clause); + deserializer.ReadPropertyWithDefault(206, "at_unit", result->at_unit); + deserializer.ReadPropertyWithExplicitDefault(207, "at_value", result->at_value, Value()); + deserializer.ReadPropertyWithDefault>(208, "output_bindings", result->output_bindings); + deserializer.ReadPropertyWithDefault>>(209, "output_expressions", result->output_expressions); + deserializer.ReadPropertyWithDefault>>(210, "source_filters", result->source_filters); return std::move(result); } @@ -882,6 +904,7 @@ void LogicalTopN::Serialize(Serializer &serializer) const { serializer.WritePropertyWithDefault>(200, "orders", orders); serializer.WritePropertyWithDefault(201, "limit", limit); serializer.WritePropertyWithDefault(202, "offset", offset); + serializer.WritePropertyWithDefault(203, "unpruned_offset", unpruned_offset, optional_idx()); } unique_ptr LogicalTopN::Deserialize(Deserializer &deserializer) { @@ -889,6 +912,7 @@ unique_ptr LogicalTopN::Deserialize(Deserializer &deserializer) auto limit = deserializer.ReadPropertyWithDefault(201, "limit"); auto offset = deserializer.ReadPropertyWithDefault(202, "offset"); auto result = duckdb::unique_ptr(new LogicalTopN(std::move(orders), limit, offset)); + deserializer.ReadPropertyWithExplicitDefault(203, "unpruned_offset", result->unpruned_offset, optional_idx()); return std::move(result); } diff --git a/src/duckdb/src/storage/serialization/serialize_nodes.cpp b/src/duckdb/src/storage/serialization/serialize_nodes.cpp index dfa945aa9..2e0a65959 100644 --- a/src/duckdb/src/storage/serialization/serialize_nodes.cpp +++ b/src/duckdb/src/storage/serialization/serialize_nodes.cpp @@ -9,6 +9,7 @@ #include "duckdb/parser/statement/select_statement.hpp" #include "duckdb/parser/query_node.hpp" #include "duckdb/parser/result_modifier.hpp" +#include "duckdb/planner/tableref/bound_at_clause.hpp" #include "duckdb/planner/bound_result_modifier.hpp" #include "duckdb/planner/operator/logical_external_resource.hpp" #include "duckdb/parser/expression/case_expression.hpp" @@ -30,6 +31,7 @@ #include "duckdb/planner/expression.hpp" #include "duckdb/common/multi_file/multi_file_options.hpp" #include "duckdb/common/multi_file/multi_file_reader.hpp" +#include "duckdb/common/multi_file/multi_file_data.hpp" #include "duckdb/execution/operator/csv_scanner/csv_option.hpp" #include "duckdb/function/table/read_csv.hpp" #include "duckdb/function/scalar/strftime_format.hpp" @@ -91,6 +93,18 @@ unique_ptr BaseReservoirSampling::Deserialize(Deserialize return result; } +void BoundAtClause::Serialize(Serializer &serializer) const { + serializer.WritePropertyWithDefault(100, "unit", unit); + serializer.WriteProperty(101, "value", val); +} + +unique_ptr BoundAtClause::Deserialize(Deserializer &deserializer) { + auto unit = deserializer.ReadPropertyWithDefault(100, "unit"); + auto val = deserializer.ReadProperty(101, "value"); + auto result = duckdb::unique_ptr(new BoundAtClause(std::move(unit), val)); + return result; +} + void BoundCaseCheck::Serialize(Serializer &serializer) const { serializer.WritePropertyWithDefault>(100, "when_expr", when_expr); serializer.WritePropertyWithDefault>(101, "then_expr", then_expr); @@ -358,6 +372,7 @@ void ExtraOperatorInfo::Serialize(Serializer &serializer) const { serializer.WriteProperty(101, "total_files", total_files); serializer.WriteProperty(102, "filtered_files", filtered_files); serializer.WritePropertyWithDefault>(103, "sample_options", sample_options); + serializer.WritePropertyWithDefault>>>(104, "file_filter_expressions", file_filter_expressions); } ExtraOperatorInfo ExtraOperatorInfo::Deserialize(Deserializer &deserializer) { @@ -366,6 +381,7 @@ ExtraOperatorInfo ExtraOperatorInfo::Deserialize(Deserializer &deserializer) { deserializer.ReadProperty(101, "total_files", result.total_files); deserializer.ReadProperty(102, "filtered_files", result.filtered_files); deserializer.ReadPropertyWithDefault>(103, "sample_options", result.sample_options); + deserializer.ReadPropertyWithDefault>>>(104, "file_filter_expressions", result.file_filter_expressions); return result; } @@ -459,6 +475,24 @@ MatchRecognizeSubset MatchRecognizeSubset::Deserialize(Deserializer &deserialize return result; } +void MultiFileColumnDefinition::Serialize(Serializer &serializer) const { + serializer.WritePropertyWithDefault(100, "name", name); + serializer.WriteProperty(101, "type", type); + serializer.WritePropertyWithDefault>(102, "children", children); + serializer.WritePropertyWithDefault>(103, "default_expression", default_expression); + serializer.WriteProperty(104, "identifier", identifier); +} + +MultiFileColumnDefinition MultiFileColumnDefinition::Deserialize(Deserializer &deserializer) { + auto name = deserializer.ReadPropertyWithDefault(100, "name"); + auto type = deserializer.ReadProperty(101, "type"); + MultiFileColumnDefinition result(std::move(name), std::move(type)); + deserializer.ReadPropertyWithDefault>(102, "children", result.children); + deserializer.ReadPropertyWithDefault>(103, "default_expression", result.default_expression); + deserializer.ReadProperty(104, "identifier", result.identifier); + return result; +} + void MultiFileOptions::Serialize(Serializer &serializer) const { serializer.WritePropertyWithDefault(100, "filename", filename); serializer.WritePropertyWithDefault(101, "hive_partitioning", hive_partitioning); @@ -469,6 +503,11 @@ void MultiFileOptions::Serialize(Serializer &serializer) const { serializer.WritePropertyWithDefault(106, "filename_column", filename_column, MultiFileOptions::DEFAULT_FILENAME_COLUMN); serializer.WritePropertyWithDefault(107, "allow_empty", allow_empty); serializer.WritePropertyWithDefault(108, "maximum_sample_files", maximum_sample_files, 1); + serializer.WritePropertyWithDefault(109, "sampled_schema_is_union", sampled_schema_is_union, true); + serializer.WritePropertyWithDefault>(110, "schema", schema); + serializer.WritePropertyWithDefault(111, "file_row_number", file_row_number); + serializer.WriteProperty(112, "column_statistics", column_statistics); + serializer.WritePropertyWithDefault>(113, "bound_schema", bound_schema); } MultiFileOptions MultiFileOptions::Deserialize(Deserializer &deserializer) { @@ -482,18 +521,25 @@ MultiFileOptions MultiFileOptions::Deserialize(Deserializer &deserializer) { deserializer.ReadPropertyWithExplicitDefault(106, "filename_column", result.filename_column, MultiFileOptions::DEFAULT_FILENAME_COLUMN); deserializer.ReadPropertyWithDefault(107, "allow_empty", result.allow_empty); deserializer.ReadPropertyWithExplicitDefault(108, "maximum_sample_files", result.maximum_sample_files, 1); + deserializer.ReadPropertyWithExplicitDefault(109, "sampled_schema_is_union", result.sampled_schema_is_union, true); + deserializer.ReadPropertyWithDefault>(110, "schema", result.schema); + deserializer.ReadPropertyWithDefault(111, "file_row_number", result.file_row_number); + deserializer.ReadProperty(112, "column_statistics", result.column_statistics); + deserializer.ReadPropertyWithDefault>(113, "bound_schema", result.bound_schema); return result; } void MultiFileReaderBindData::Serialize(Serializer &serializer) const { serializer.WriteProperty(100, "filename_idx", filename_idx); serializer.WritePropertyWithDefault>(101, "hive_partitioning_indexes", hive_partitioning_indexes); + serializer.WriteProperty(102, "file_row_number_idx", file_row_number_idx); } MultiFileReaderBindData MultiFileReaderBindData::Deserialize(Deserializer &deserializer) { MultiFileReaderBindData result; deserializer.ReadProperty(100, "filename_idx", result.filename_idx); deserializer.ReadPropertyWithDefault>(101, "hive_partitioning_indexes", result.hive_partitioning_indexes); + deserializer.ReadProperty(102, "file_row_number_idx", result.file_row_number_idx); return result; } diff --git a/src/duckdb/src/storage/serialization/serialize_parse_info.cpp b/src/duckdb/src/storage/serialization/serialize_parse_info.cpp index abfead377..889af757c 100644 --- a/src/duckdb/src/storage/serialization/serialize_parse_info.cpp +++ b/src/duckdb/src/storage/serialization/serialize_parse_info.cpp @@ -10,6 +10,7 @@ #include "duckdb/parser/parsed_data/alter_table_info.hpp" #include "duckdb/parser/parsed_data/comment_on_column_info.hpp" #include "duckdb/parser/parsed_data/alter_database_info.hpp" +#include "duckdb/parser/parsed_data/alter_schema_info.hpp" #include "duckdb/parser/parsed_data/attach_info.hpp" #include "duckdb/parser/parsed_data/copy_database_info.hpp" #include "duckdb/parser/parsed_data/copy_info.hpp" @@ -108,6 +109,9 @@ unique_ptr AlterInfo::Deserialize(Deserializer &deserializer) { case AlterType::ALTER_DATABASE: result = AlterDatabaseInfo::Deserialize(deserializer); break; + case AlterType::ALTER_SCHEMA: + result = AlterSchemaInfo::Deserialize(deserializer); + break; case AlterType::ALTER_TABLE: result = AlterTableInfo::Deserialize(deserializer); break; @@ -237,18 +241,41 @@ unique_ptr AlterDatabaseInfo::Deserialize(Deserializer &deserializer) return std::move(result); } +void AlterSchemaInfo::Serialize(Serializer &serializer) const { + AlterInfo::Serialize(serializer); + serializer.WriteProperty(300, "alter_schema_type", alter_schema_type); +} + +unique_ptr AlterSchemaInfo::Deserialize(Deserializer &deserializer) { + auto alter_schema_type = deserializer.ReadProperty(300, "alter_schema_type"); + unique_ptr result; + switch (alter_schema_type) { + case AlterSchemaType::RESET_SCHEMA_OPTIONS: + result = ResetSchemaOptionsInfo::Deserialize(deserializer); + break; + case AlterSchemaType::SET_SCHEMA_OPTIONS: + result = SetSchemaOptionsInfo::Deserialize(deserializer); + break; + default: + throw SerializationException("Unsupported type for deserialization of AlterSchemaInfo!"); + } + return std::move(result); +} + void AddColumnInfo::Serialize(Serializer &serializer) const { AlterTableInfo::Serialize(serializer); serializer.WriteProperty(400, "new_column", new_column); serializer.WritePropertyWithDefault(401, "if_column_not_exists", if_column_not_exists); - serializer.WritePropertyWithDefault(402, "add_not_null", add_not_null); + serializer.WritePropertyWithDefault(402, "add_not_null", add_column_constraints.add_not_null); + serializer.WritePropertyWithDefault(403, "add_unique", add_column_constraints.add_unique); } unique_ptr AddColumnInfo::Deserialize(Deserializer &deserializer) { auto new_column = deserializer.ReadProperty(400, "new_column"); auto result = duckdb::unique_ptr(new AddColumnInfo(std::move(new_column))); deserializer.ReadPropertyWithDefault(401, "if_column_not_exists", result->if_column_not_exists); - deserializer.ReadPropertyWithDefault(402, "add_not_null", result->add_not_null); + deserializer.ReadPropertyWithDefault(402, "add_not_null", result->add_column_constraints.add_not_null); + deserializer.ReadPropertyWithDefault(403, "add_unique", result->add_column_constraints.add_unique); return std::move(result); } @@ -305,6 +332,7 @@ void AttachInfo::Serialize(Serializer &serializer) const { serializer.WritePropertyWithDefault(201, "path", path); serializer.WritePropertyWithDefault>(202, "options", options); serializer.WritePropertyWithDefault(203, "on_conflict", on_conflict, OnCreateConflict::ERROR_ON_CONFLICT); + serializer.WritePropertyWithDefault>(204, "external_resource", external_resource); } unique_ptr AttachInfo::Deserialize(Deserializer &deserializer) { @@ -313,6 +341,7 @@ unique_ptr AttachInfo::Deserialize(Deserializer &deserializer) { deserializer.ReadPropertyWithDefault(201, "path", result->path); deserializer.ReadPropertyWithDefault>(202, "options", result->options); deserializer.ReadPropertyWithExplicitDefault(203, "on_conflict", result->on_conflict, OnCreateConflict::ERROR_ON_CONFLICT); + deserializer.ReadPropertyWithDefault>(204, "external_resource", result->external_resource); return std::move(result); } @@ -329,16 +358,21 @@ unique_ptr BoundExportData::Deserialize(Deserializer &deserializer) { void ChangeColumnTypeInfo::Serialize(Serializer &serializer) const { AlterTableInfo::Serialize(serializer); - serializer.WritePropertyWithDefault(400, "column_name", column_name); + serializer.WritePropertyWithDefault(400, "column_name", column_path[0]); serializer.WriteProperty(401, "target_type", target_type); serializer.WritePropertyWithDefault>(402, "expression", expression); + serializer.WritePropertyWithDefault>(403, "column_path", column_path.size() > 1 ? column_path : vector(), vector()); } unique_ptr ChangeColumnTypeInfo::Deserialize(Deserializer &deserializer) { auto result = duckdb::unique_ptr(new ChangeColumnTypeInfo()); - deserializer.ReadPropertyWithDefault(400, "column_name", result->column_name); + auto column_name = deserializer.ReadPropertyWithDefault(400, "column_name"); deserializer.ReadProperty(401, "target_type", result->target_type); deserializer.ReadPropertyWithDefault>(402, "expression", result->expression); + deserializer.ReadPropertyWithExplicitDefault>(403, "column_path", result->column_path, vector()); + if (result->column_path.empty()) { + result->column_path.push_back(std::move(column_name)); + } return std::move(result); } @@ -363,6 +397,7 @@ void ConnectInfo::Serialize(Serializer &serializer) const { serializer.WritePropertyWithDefault(201, "name_is_string_literal", name_is_string_literal); serializer.WritePropertyWithDefault(202, "target_is_local", target_is_local); serializer.WritePropertyWithDefault>(203, "options", options); + serializer.WritePropertyWithDefault>(204, "external_resource", external_resource); } unique_ptr ConnectInfo::Deserialize(Deserializer &deserializer) { @@ -371,6 +406,7 @@ unique_ptr ConnectInfo::Deserialize(Deserializer &deserializer) { deserializer.ReadPropertyWithDefault(201, "name_is_string_literal", result->name_is_string_literal); deserializer.ReadPropertyWithDefault(202, "target_is_local", result->target_is_local); deserializer.ReadPropertyWithDefault>(203, "options", result->options); + deserializer.ReadPropertyWithDefault>(204, "external_resource", result->external_resource); return std::move(result); } @@ -485,12 +521,17 @@ unique_ptr DropInfo::Deserialize(Deserializer &deserializer) { void DropNotNullInfo::Serialize(Serializer &serializer) const { AlterTableInfo::Serialize(serializer); - serializer.WritePropertyWithDefault(400, "column_name", column_name); + serializer.WritePropertyWithDefault(400, "column_name", column_path[0]); + serializer.WritePropertyWithDefault>(401, "column_path", column_path.size() > 1 ? column_path : vector(), vector()); } unique_ptr DropNotNullInfo::Deserialize(Deserializer &deserializer) { auto result = duckdb::unique_ptr(new DropNotNullInfo()); - deserializer.ReadPropertyWithDefault(400, "column_name", result->column_name); + auto column_name = deserializer.ReadPropertyWithDefault(400, "column_name"); + deserializer.ReadPropertyWithExplicitDefault>(401, "column_path", result->column_path, vector()); + if (result->column_path.empty()) { + result->column_path.push_back(std::move(column_name)); + } return std::move(result); } @@ -629,6 +670,17 @@ unique_ptr RenameViewInfo::Deserialize(Deserializer &deserializer return std::move(result); } +void ResetSchemaOptionsInfo::Serialize(Serializer &serializer) const { + AlterSchemaInfo::Serialize(serializer); + serializer.WritePropertyWithDefault(400, "options", options); +} + +unique_ptr ResetSchemaOptionsInfo::Deserialize(Deserializer &deserializer) { + auto result = duckdb::unique_ptr(new ResetSchemaOptionsInfo()); + deserializer.ReadPropertyWithDefault(400, "options", result->options); + return std::move(result); +} + void ResetTableOptionsInfo::Serialize(Serializer &serializer) const { AlterTableInfo::Serialize(serializer); if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { @@ -674,25 +726,35 @@ unique_ptr SetCommentInfo::Deserialize(Deserializer &deserializer) { void SetDefaultInfo::Serialize(Serializer &serializer) const { AlterTableInfo::Serialize(serializer); - serializer.WritePropertyWithDefault(400, "column_name", column_name); + serializer.WritePropertyWithDefault(400, "column_name", column_path[0]); serializer.WritePropertyWithDefault>(401, "expression", expression); + serializer.WritePropertyWithDefault>(402, "column_path", column_path.size() > 1 ? column_path : vector(), vector()); } unique_ptr SetDefaultInfo::Deserialize(Deserializer &deserializer) { auto result = duckdb::unique_ptr(new SetDefaultInfo()); - deserializer.ReadPropertyWithDefault(400, "column_name", result->column_name); + auto column_name = deserializer.ReadPropertyWithDefault(400, "column_name"); deserializer.ReadPropertyWithDefault>(401, "expression", result->expression); + deserializer.ReadPropertyWithExplicitDefault>(402, "column_path", result->column_path, vector()); + if (result->column_path.empty()) { + result->column_path.push_back(std::move(column_name)); + } return std::move(result); } void SetNotNullInfo::Serialize(Serializer &serializer) const { AlterTableInfo::Serialize(serializer); - serializer.WritePropertyWithDefault(400, "column_name", column_name); + serializer.WritePropertyWithDefault(400, "column_name", column_path[0]); + serializer.WritePropertyWithDefault>(401, "column_path", column_path.size() > 1 ? column_path : vector(), vector()); } unique_ptr SetNotNullInfo::Deserialize(Deserializer &deserializer) { auto result = duckdb::unique_ptr(new SetNotNullInfo()); - deserializer.ReadPropertyWithDefault(400, "column_name", result->column_name); + auto column_name = deserializer.ReadPropertyWithDefault(400, "column_name"); + deserializer.ReadPropertyWithExplicitDefault>(401, "column_path", result->column_path, vector()); + if (result->column_path.empty()) { + result->column_path.push_back(std::move(column_name)); + } return std::move(result); } @@ -707,6 +769,17 @@ unique_ptr SetPartitionedByInfo::Deserialize(Deserializer &deser return std::move(result); } +void SetSchemaOptionsInfo::Serialize(Serializer &serializer) const { + AlterSchemaInfo::Serialize(serializer); + serializer.WritePropertyWithDefault>>(400, "options", options); +} + +unique_ptr SetSchemaOptionsInfo::Deserialize(Deserializer &deserializer) { + auto result = duckdb::unique_ptr(new SetSchemaOptionsInfo()); + deserializer.ReadPropertyWithDefault>>(400, "options", result->options); + return std::move(result); +} + void SetSortedByInfo::Serialize(Serializer &serializer) const { AlterTableInfo::Serialize(serializer); serializer.WritePropertyWithDefault>(400, "orders", orders); diff --git a/src/duckdb/src/storage/serialization/serialize_parsed_expression.cpp b/src/duckdb/src/storage/serialization/serialize_parsed_expression.cpp index 44dbf5167..bade21ab3 100644 --- a/src/duckdb/src/storage/serialization/serialize_parsed_expression.cpp +++ b/src/duckdb/src/storage/serialization/serialize_parsed_expression.cpp @@ -95,6 +95,7 @@ unique_ptr ParsedExpression::Deserialize(Deserializer &deseria result->alias = std::move(alias); result->query_location = query_location; result->query_location.length = query_location_length; + result->VerifyDeserialized(); return result; } diff --git a/src/duckdb/src/storage/serialization/serialize_query_node.cpp b/src/duckdb/src/storage/serialization/serialize_query_node.cpp index b933c654d..865f0c2d5 100644 --- a/src/duckdb/src/storage/serialization/serialize_query_node.cpp +++ b/src/duckdb/src/storage/serialization/serialize_query_node.cpp @@ -6,6 +6,7 @@ #include "duckdb/common/serializer/serializer.hpp" #include "duckdb/common/serializer/deserializer.hpp" #include "duckdb/parser/query_node/list.hpp" +#include "duckdb/parser/tableref/emptytableref.hpp" #include "duckdb/parser/query_node/update_query_node.hpp" #include "duckdb/parser/query_node/delete_query_node.hpp" #include "duckdb/parser/query_node/insert_query_node.hpp" @@ -65,6 +66,7 @@ unique_ptr QueryNode::Deserialize(Deserializer &deserializer) { if (type == QueryNodeType::CTE_NODE) { result = std::move(result->Cast().child); } + QueryNode::VerifyDeserialized(result); return result; } @@ -218,6 +220,16 @@ unique_ptr SelectNode::Deserialize(Deserializer &deserializer) { deserializer.ReadPropertyWithDefault>(206, "having", result->having); deserializer.ReadPropertyWithDefault>(207, "sample", result->sample); deserializer.ReadPropertyWithDefault>(208, "qualify", result->qualify); + if (!result->from_table) { + result->from_table = make_uniq(); + } + for (auto &grouping_set : result->groups.grouping_sets) { + for (auto &group_index : grouping_set) { + if (!group_index.IsValid() || group_index.GetIndex() >= result->groups.group_expressions.size()) { + throw SerializationException("Grouping set refers to a group that does not exist"); + } + } + } return std::move(result); } diff --git a/src/duckdb/src/storage/serialization/serialize_tableref.cpp b/src/duckdb/src/storage/serialization/serialize_tableref.cpp index 535e80b60..e5ef1b1a0 100644 --- a/src/duckdb/src/storage/serialization/serialize_tableref.cpp +++ b/src/duckdb/src/storage/serialization/serialize_tableref.cpp @@ -65,6 +65,7 @@ unique_ptr TableRef::Deserialize(Deserializer &deserializer) { result->sample = std::move(sample); result->query_location = query_location; result->query_location.length = query_location_length; + result->VerifyDeserialized(); return result; } @@ -117,6 +118,9 @@ unique_ptr ColumnDataRef::Deserialize(Deserializer &deserializer) { auto expected_names = deserializer.ReadPropertyWithDefault>(200, "expected_names"); auto collection = deserializer.ReadPropertyWithDefault>(202, "collection"); auto result = duckdb::unique_ptr(new ColumnDataRef(std::move(collection), std::move(expected_names))); + if (!result->collection) { + throw SerializationException("ColumnDataRef is missing its collection"); + } return std::move(result); } @@ -269,7 +273,7 @@ unique_ptr SubqueryRef::Deserialize(Deserializer &deserializer) { void TableFunctionRef::Serialize(Serializer &serializer) const { TableRef::Serialize(serializer); - serializer.WritePropertyWithDefault>(200, "function", function); + serializer.WritePropertyWithDefault>(200, "function", SerializableFunction()); serializer.WritePropertyWithDefault>(201, "column_name_alias", column_name_alias); serializer.WritePropertyWithDefault(202, "with_ordinality", with_ordinality, OrdinalityType::WITHOUT_ORDINALITY); } diff --git a/src/duckdb/src/storage/serialization/serialize_types.cpp b/src/duckdb/src/storage/serialization/serialize_types.cpp index 4147596b0..4188a5e69 100644 --- a/src/duckdb/src/storage/serialization/serialize_types.cpp +++ b/src/duckdb/src/storage/serialization/serialize_types.cpp @@ -6,67 +6,67 @@ #include "duckdb/common/serializer/serializer.hpp" #include "duckdb/common/serializer/deserializer.hpp" #include "duckdb/common/extension_type_info.hpp" -#include "duckdb/common/extra_type_info.hpp" +#include "duckdb/common/logical_type_info.hpp" namespace duckdb { -void ExtraTypeInfo::Serialize(Serializer &serializer) const { - serializer.WriteProperty(100, "type", type); +void LogicalTypeInfo::Serialize(Serializer &serializer) const { + serializer.WriteProperty(100, "type", type); serializer.WritePropertyWithDefault(101, "alias", alias); /* [Deleted] (vector) "modifiers" */ serializer.WritePropertyWithDefault>(103, "extension_info", extension_info); } -shared_ptr ExtraTypeInfo::Deserialize(Deserializer &deserializer) { - auto type = deserializer.ReadProperty(100, "type"); +unique_ptr LogicalTypeInfo::Deserialize(Deserializer &deserializer) { + auto type = deserializer.ReadProperty(100, "type"); auto alias = deserializer.ReadPropertyWithDefault(101, "alias"); deserializer.ReadDeletedProperty>(102, "modifiers"); auto extension_info = deserializer.ReadPropertyWithDefault>(103, "extension_info"); - shared_ptr result; + unique_ptr result; switch (type) { - case ExtraTypeInfoType::ANY_TYPE_INFO: + case LogicalTypeInfoType::ANY_TYPE_INFO: result = AnyTypeInfo::Deserialize(deserializer); break; - case ExtraTypeInfoType::ARRAY_TYPE_INFO: + case LogicalTypeInfoType::ARRAY_TYPE_INFO: result = ArrayTypeInfo::Deserialize(deserializer); break; - case ExtraTypeInfoType::DECIMAL_TYPE_INFO: + case LogicalTypeInfoType::DECIMAL_TYPE_INFO: result = DecimalTypeInfo::Deserialize(deserializer); break; - case ExtraTypeInfoType::ENUM_TYPE_INFO: + case LogicalTypeInfoType::ENUM_TYPE_INFO: result = EnumTypeInfo::Deserialize(deserializer); break; - case ExtraTypeInfoType::GENERIC_TYPE_INFO: - result = make_shared_ptr(type); + case LogicalTypeInfoType::GENERIC_TYPE_INFO: + result = make_uniq(type); break; - case ExtraTypeInfoType::GEO_TYPE_INFO: + case LogicalTypeInfoType::GEO_TYPE_INFO: result = GeoTypeInfo::Deserialize(deserializer); break; - case ExtraTypeInfoType::INTEGER_LITERAL_TYPE_INFO: + case LogicalTypeInfoType::INTEGER_LITERAL_TYPE_INFO: result = IntegerLiteralTypeInfo::Deserialize(deserializer); break; - case ExtraTypeInfoType::INVALID_TYPE_INFO: + case LogicalTypeInfoType::INVALID_TYPE_INFO: return nullptr; - case ExtraTypeInfoType::LEGACY_AGGREGATE_STATE_TYPE_INFO: + case LogicalTypeInfoType::LEGACY_AGGREGATE_STATE_TYPE_INFO: result = LegacyAggregateStateTypeInfo::Deserialize(deserializer); break; - case ExtraTypeInfoType::LIST_TYPE_INFO: + case LogicalTypeInfoType::LIST_TYPE_INFO: result = ListTypeInfo::Deserialize(deserializer); break; - case ExtraTypeInfoType::STRING_TYPE_INFO: + case LogicalTypeInfoType::STRING_TYPE_INFO: result = StringTypeInfo::Deserialize(deserializer); break; - case ExtraTypeInfoType::STRUCT_TYPE_INFO: + case LogicalTypeInfoType::STRUCT_TYPE_INFO: result = StructTypeInfo::Deserialize(deserializer); break; - case ExtraTypeInfoType::TEMPLATE_TYPE_INFO: + case LogicalTypeInfoType::TEMPLATE_TYPE_INFO: result = TemplateTypeInfo::Deserialize(deserializer); break; - case ExtraTypeInfoType::UNBOUND_TYPE_INFO: + case LogicalTypeInfoType::UNBOUND_TYPE_INFO: result = UnboundTypeInfo::Deserialize(deserializer); break; default: - throw SerializationException("Unsupported type for deserialization of ExtraTypeInfo!"); + throw SerializationException("Unsupported type for deserialization of LogicalTypeInfo!"); } result->alias = std::move(alias); result->extension_info = std::move(extension_info); @@ -74,39 +74,39 @@ shared_ptr ExtraTypeInfo::Deserialize(Deserializer &deserializer) } void AnyTypeInfo::Serialize(Serializer &serializer) const { - ExtraTypeInfo::Serialize(serializer); + LogicalTypeInfo::Serialize(serializer); serializer.WriteProperty(200, "target_type", target_type); serializer.WritePropertyWithDefault(201, "cast_score", cast_score); } -shared_ptr AnyTypeInfo::Deserialize(Deserializer &deserializer) { - auto result = duckdb::shared_ptr(new AnyTypeInfo()); +unique_ptr AnyTypeInfo::Deserialize(Deserializer &deserializer) { + auto result = duckdb::unique_ptr(new AnyTypeInfo()); deserializer.ReadProperty(200, "target_type", result->target_type); deserializer.ReadPropertyWithDefault(201, "cast_score", result->cast_score); return std::move(result); } void ArrayTypeInfo::Serialize(Serializer &serializer) const { - ExtraTypeInfo::Serialize(serializer); + LogicalTypeInfo::Serialize(serializer); serializer.WriteProperty(200, "child_type", child_type); serializer.WritePropertyWithDefault(201, "size", size); } -shared_ptr ArrayTypeInfo::Deserialize(Deserializer &deserializer) { +unique_ptr ArrayTypeInfo::Deserialize(Deserializer &deserializer) { auto child_type = deserializer.ReadProperty(200, "child_type"); auto size = deserializer.ReadPropertyWithDefault(201, "size"); - auto result = duckdb::shared_ptr(new ArrayTypeInfo(std::move(child_type), size)); + auto result = duckdb::unique_ptr(new ArrayTypeInfo(std::move(child_type), size)); return std::move(result); } void DecimalTypeInfo::Serialize(Serializer &serializer) const { - ExtraTypeInfo::Serialize(serializer); + LogicalTypeInfo::Serialize(serializer); serializer.WritePropertyWithDefault(200, "width", width); serializer.WritePropertyWithDefault(201, "scale", scale); } -shared_ptr DecimalTypeInfo::Deserialize(Deserializer &deserializer) { - auto result = duckdb::shared_ptr(new DecimalTypeInfo()); +unique_ptr DecimalTypeInfo::Deserialize(Deserializer &deserializer) { + auto result = duckdb::unique_ptr(new DecimalTypeInfo()); deserializer.ReadPropertyWithDefault(200, "width", result->width); deserializer.ReadPropertyWithDefault(201, "scale", result->scale); return std::move(result); @@ -125,35 +125,35 @@ unique_ptr ExtensionTypeInfo::Deserialize(Deserializer &deser } void GeoTypeInfo::Serialize(Serializer &serializer) const { - ExtraTypeInfo::Serialize(serializer); + LogicalTypeInfo::Serialize(serializer); serializer.WriteProperty(200, "crs", crs); } -shared_ptr GeoTypeInfo::Deserialize(Deserializer &deserializer) { - auto result = duckdb::shared_ptr(new GeoTypeInfo()); +unique_ptr GeoTypeInfo::Deserialize(Deserializer &deserializer) { + auto result = duckdb::unique_ptr(new GeoTypeInfo()); deserializer.ReadProperty(200, "crs", result->crs); return std::move(result); } void IntegerLiteralTypeInfo::Serialize(Serializer &serializer) const { - ExtraTypeInfo::Serialize(serializer); + LogicalTypeInfo::Serialize(serializer); serializer.WriteProperty(200, "constant_value", constant_value); } -shared_ptr IntegerLiteralTypeInfo::Deserialize(Deserializer &deserializer) { - auto result = duckdb::shared_ptr(new IntegerLiteralTypeInfo()); +unique_ptr IntegerLiteralTypeInfo::Deserialize(Deserializer &deserializer) { + auto result = duckdb::unique_ptr(new IntegerLiteralTypeInfo()); deserializer.ReadProperty(200, "constant_value", result->constant_value); return std::move(result); } void LegacyAggregateStateTypeInfo::Serialize(Serializer &serializer) const { - ExtraTypeInfo::Serialize(serializer); + LogicalTypeInfo::Serialize(serializer); /* [Deleted] (string) "function_name" */ /* [Deleted] (LogicalType) "return_type" */ /* [Deleted] (vector) "bound_argument_types" */ } -shared_ptr LegacyAggregateStateTypeInfo::Deserialize(Deserializer &deserializer) { +unique_ptr LegacyAggregateStateTypeInfo::Deserialize(Deserializer &deserializer) { deserializer.ReadDeletedProperty(200, "function_name"); deserializer.ReadDeletedProperty(201, "return_type"); deserializer.ReadDeletedProperty>(202, "bound_argument_types"); @@ -162,12 +162,12 @@ shared_ptr LegacyAggregateStateTypeInfo::Deserialize(Deserializer } void ListTypeInfo::Serialize(Serializer &serializer) const { - ExtraTypeInfo::Serialize(serializer); + LogicalTypeInfo::Serialize(serializer); serializer.WriteProperty(200, "child_type", child_type); } -shared_ptr ListTypeInfo::Deserialize(Deserializer &deserializer) { - auto result = duckdb::shared_ptr(new ListTypeInfo()); +unique_ptr ListTypeInfo::Deserialize(Deserializer &deserializer) { + auto result = duckdb::unique_ptr(new ListTypeInfo()); deserializer.ReadProperty(200, "child_type", result->child_type); return std::move(result); } @@ -185,34 +185,34 @@ LogicalTypeModifier LogicalTypeModifier::Deserialize(Deserializer &deserializer) } void StringTypeInfo::Serialize(Serializer &serializer) const { - ExtraTypeInfo::Serialize(serializer); + LogicalTypeInfo::Serialize(serializer); serializer.WritePropertyWithDefault(200, "collation", collation); } -shared_ptr StringTypeInfo::Deserialize(Deserializer &deserializer) { - auto result = duckdb::shared_ptr(new StringTypeInfo()); +unique_ptr StringTypeInfo::Deserialize(Deserializer &deserializer) { + auto result = duckdb::unique_ptr(new StringTypeInfo()); deserializer.ReadPropertyWithDefault(200, "collation", result->collation); return std::move(result); } void StructTypeInfo::Serialize(Serializer &serializer) const { - ExtraTypeInfo::Serialize(serializer); + LogicalTypeInfo::Serialize(serializer); serializer.WritePropertyWithDefault>(200, "child_types", child_types); } -shared_ptr StructTypeInfo::Deserialize(Deserializer &deserializer) { - auto result = duckdb::shared_ptr(new StructTypeInfo()); +unique_ptr StructTypeInfo::Deserialize(Deserializer &deserializer) { + auto result = duckdb::unique_ptr(new StructTypeInfo()); deserializer.ReadPropertyWithDefault>(200, "child_types", result->child_types); return std::move(result); } void TemplateTypeInfo::Serialize(Serializer &serializer) const { - ExtraTypeInfo::Serialize(serializer); + LogicalTypeInfo::Serialize(serializer); serializer.WritePropertyWithDefault(200, "name", name); } -shared_ptr TemplateTypeInfo::Deserialize(Deserializer &deserializer) { - auto result = duckdb::shared_ptr(new TemplateTypeInfo()); +unique_ptr TemplateTypeInfo::Deserialize(Deserializer &deserializer) { + auto result = duckdb::unique_ptr(new TemplateTypeInfo()); deserializer.ReadPropertyWithDefault(200, "name", result->name); return std::move(result); } diff --git a/src/duckdb/src/storage/single_file_block_manager.cpp b/src/duckdb/src/storage/single_file_block_manager.cpp index 37f49faff..07cf07028 100644 --- a/src/duckdb/src/storage/single_file_block_manager.cpp +++ b/src/duckdb/src/storage/single_file_block_manager.cpp @@ -529,7 +529,7 @@ void SingleFileBlockManager::CreateNewDatabase(QueryContext context) { } // MAP mode opens only the mmap; other modes open the FileHandle. - handle = DatabaseHandle::Open(db, path, options, DatabaseOpenMode::CREATE_NEW_FILE); + handle = DatabaseHandle::Open(context, db, path, options, DatabaseOpenMode::CREATE_NEW_FILE); header_buffer.Clear(); if (options.storage_version == StorageVersion::INVALID) { @@ -631,7 +631,7 @@ void SingleFileBlockManager::CreateNewDatabase(QueryContext context) { } void SingleFileBlockManager::LoadExistingDatabase(QueryContext context) { - handle = DatabaseHandle::Open(db, path, options, DatabaseOpenMode::OPEN_EXISTING_FILE); + handle = DatabaseHandle::Open(context, db, path, options, DatabaseOpenMode::OPEN_EXISTING_FILE); handle->CheckMagicBytes(context); // otherwise, we check the metadata of the file @@ -1197,10 +1197,15 @@ void SingleFileBlockManager::ReadBlock(Block &block, bool skip_block_header) con void SingleFileBlockManager::Read(QueryContext context, Block &block) { D_ASSERT(block.id >= 0); - D_ASSERT(std::find(free_list.begin(), free_list.end(), block.id) == free_list.end()); + D_ASSERT(!BlockIsFreeListed(block.id)); ReadAndChecksum(context, block, GetBlockLocation(block.id)); } +bool SingleFileBlockManager::BlockIsFreeListed(block_id_t block_id) { + lock_guard lock(single_file_block_lock); + return free_list.find(block_id) != free_list.end(); +} + void SingleFileBlockManager::ReadBlocks(QueryContext context, FileBuffer &buffer, block_id_t start_block, idx_t block_count) { D_ASSERT(start_block >= 0); @@ -1241,14 +1246,14 @@ void SingleFileBlockManager::Truncate() { blocks_to_truncate++; max_block--; } - if (blocks_to_truncate == 0) { - // nothing to truncate - return; + if (blocks_to_truncate > 0) { + // truncate the file + free_list.erase(free_list.lower_bound(max_block), free_list.end()); } - // truncate the file - free_list.erase(free_list.lower_bound(max_block), free_list.end()); auto new_size = NumericCast(BLOCK_START + NumericCast(max_block) * GetBlockAllocSize()); - handle->Truncate(new_size); + if (blocks_to_truncate > 0 || handle->GetFileSize() > new_size) { + handle->Truncate(new_size); + } } vector SingleFileBlockManager::GetFreeListBlocks() { @@ -1338,6 +1343,7 @@ void SingleFileBlockManager::WriteHeader(QueryContext context, DatabaseHeader he header.iteration = ++iteration_count; set all_free_blocks = free_list; + all_free_blocks.insert(free_blocks_in_use.begin(), free_blocks_in_use.end()); auto checkpoint_freed_blocks = modified_blocks; for (auto &block : checkpoint_freed_blocks) { all_free_blocks.insert(block); @@ -1374,12 +1380,9 @@ void SingleFileBlockManager::WriteHeader(QueryContext context, DatabaseHeader he // no blocks in the free list header.free_list = DConstants::INVALID_INDEX; } - lock.unlock(); - metadata_manager.Flush(context); - - lock.lock(); header.block_count = NumericCast(max_block); lock.unlock(); + metadata_manager.Flush(context); header.storage_compatibility = options.storage_version; @@ -1434,11 +1437,11 @@ void SingleFileBlockManager::FileSync() { handle->Sync(); } -void SingleFileBlockManager::UnregisterBlock(block_id_t id) { - // perform the actual unregistration - BlockManager::UnregisterBlock(id); - // check if it is part of the newly free list - lock_guard lock(single_file_block_lock); +void SingleFileBlockManager::ReleaseFreeBlockInUse(unique_lock &lock, block_id_t id) { + if (!lock.owns_lock()) { + throw InternalException("ReleaseFreeBlockInUse must be called while holding the lock"); + } + // check if the block is part of the newly free list auto entry = free_blocks_in_use.find(id); if (entry != free_blocks_in_use.end()) { // it is! move it to the regular free list so the block can be re-used @@ -1447,6 +1450,27 @@ void SingleFileBlockManager::UnregisterBlock(block_id_t id) { } } +void SingleFileBlockManager::UnregisterBlock(block_id_t id) { + unique_lock lock(single_file_block_lock); + // perform the actual unregistration + BlockManager::UnregisterBlock(id); + ReleaseFreeBlockInUse(lock, id); +} + +bool SingleFileBlockManager::UnregisterExpiredBlock(block_id_t id) { + // hold the lock across the live-handle check and the free-list transition: otherwise a + // re-registration and retirement of the block id can interleave between them, and the + // free-list transition would run although a live handle exists + // lock order: single_file_block_lock before blocks_lock, as in AddFreeBlock + unique_lock lock(single_file_block_lock); + if (!BlockManager::UnregisterExpiredBlock(id)) { + // a newer handle is registered for this block id - the block id is still in use + return false; + } + ReleaseFreeBlockInUse(lock, id); + return true; +} + void SingleFileBlockManager::TrimFreeBlockRange(block_id_t start, block_id_t end) { auto block_count = NumericCast(end + 1 - start); auto offset = BLOCK_START + (NumericCast(start) * GetBlockAllocSize()); diff --git a/src/duckdb/src/storage/standard_buffer_manager.cpp b/src/duckdb/src/storage/standard_buffer_manager.cpp index ce725ee07..d33b33db3 100644 --- a/src/duckdb/src/storage/standard_buffer_manager.cpp +++ b/src/duckdb/src/storage/standard_buffer_manager.cpp @@ -127,7 +127,7 @@ idx_t StandardBufferManager::GetOperatorMemoryLimit() const { template TempBufferPoolReservation StandardBufferManager::EvictBlocksOrThrow(QueryContext context, MemoryTag tag, idx_t memory_delta, unique_ptr *buffer, - ARGS... args) { + const ARGS &...args) { auto r = buffer_pool.EvictBlocks(context, tag, memory_delta, buffer_pool.maximum_memory, buffer); if (!r.success) { string extra_text = StringUtil::Format(" (%s/%s used)", StringUtil::BytesToHumanReadableString(GetUsedMemory()), @@ -376,7 +376,7 @@ BufferHandle StandardBufferManager::Pin(const QueryContext &context, shared_ptr< // check if the block is already loaded if (block_memory.GetState() == BlockState::BLOCK_LOADED) { // the block is loaded, increment the reader count and set the BufferHandle - buf = handle->Load(context); + buf = handle->Load(lock, context); } required_memory = block_memory.GetMemoryUsage(); } @@ -397,11 +397,11 @@ BufferHandle StandardBufferManager::Pin(const QueryContext &context, shared_ptr< if (block_memory.GetState() == BlockState::BLOCK_LOADED) { // the block is loaded, increment the reader count and return a pointer to the handle reservation.Resize(0); - buf = handle->Load(context); + buf = handle->Load(lock, context); } else { // now we can actually load the current block D_ASSERT(block_memory.GetReaders() == 0); - buf = handle->Load(context, std::move(reusable_buffer)); + buf = handle->Load(lock, context, std::move(reusable_buffer)); if (!buf.IsValid()) { reservation.Resize(0); return buf; // Buffer was destroyed (e.g., due to DestroyBufferUpon::Eviction) @@ -461,7 +461,7 @@ void StandardBufferManager::Unpin(shared_ptr &handle) { return; } D_ASSERT(block_memory.GetReaders() > 0); - auto new_readers = block_memory.DecrementReaders(); + auto new_readers = block_memory.DecrementReaders(lock); if (new_readers == 0) { VerifyZeroReaders(lock, handle); if (block_memory.MustAddToEvictionQueue()) { @@ -510,17 +510,21 @@ string StandardBufferManager::GetTemporaryPath(block_id_t id) { } void StandardBufferManager::RequireTemporaryDirectory() { + if (temporary_directory.initialized.load(std::memory_order_acquire)) { + return; + } + lock_guard guard(temporary_directory.lock); + if (temporary_directory.handle) { + return; + } if (temporary_directory.path.empty()) { throw InvalidInputException( "Out-of-memory: cannot write buffer because no temporary directory is specified!\nTo enable " "temporary buffer eviction set a temporary directory using PRAGMA temp_directory='/path/to/tmp.tmp'"); } - lock_guard guard(temporary_directory.lock); - if (!temporary_directory.handle) { - // temp directory has not been created yet: initialize it - temporary_directory.handle = make_uniq( - db, temporary_directory.path, temporary_directory.size_on_disk, temporary_directory.maximum_swap_space); - } + temporary_directory.handle = make_uniq( + db, temporary_directory.path, temporary_directory.size_on_disk, temporary_directory.maximum_swap_space); + temporary_directory.initialized.store(true, std::memory_order_release); } bool StandardBufferManager::EncryptTemporaryFiles() { @@ -551,32 +555,43 @@ void StandardBufferManager::WriteTemporaryBuffer(QueryContext context, MemoryTag header_size += DEFAULT_ENCRYPTED_BUFFER_HEADER_SIZE; } - evicted_data_per_tag[uint8_t(tag)] += buffer.AllocSize(); - if (context.GetClientContext()) { - QueryProfiler::Get(*context.GetClientContext()).TrackBytesSpilled(buffer.AllocSize() + header_size); - } - // Create the file and write the size followed by the buffer contents. auto &fs = FileSystem::GetFileSystem(db); auto handle = fs.OpenFile(path, FileFlags::FILE_FLAGS_WRITE | FileFlags::FILE_FLAGS_FILE_CREATE); - temporary_directory.handle->GetTempFile().IncreaseSizeOnDisk(buffer.AllocSize() + header_size); - //! for very large buffers, we store the size of the buffer in plaintext. - idx_t block_header_size = buffer.GetHeaderSize(); - auto user_size = buffer.Size(); - handle->Write(context, &user_size, sizeof(idx_t), 0); - handle->Write(context, &block_header_size, sizeof(idx_t), sizeof(idx_t)); - - idx_t offset = sizeof(idx_t) * 2; + bool size_on_disk_increased = false; + try { + temporary_directory.handle->GetTempFile().IncreaseSizeOnDisk(buffer.AllocSize() + header_size); + size_on_disk_increased = true; + //! for very large buffers, we store the size of the buffer in plaintext. + idx_t block_header_size = buffer.GetHeaderSize(); + auto user_size = buffer.Size(); + handle->Write(context, &user_size, sizeof(idx_t), 0); + handle->Write(context, &block_header_size, sizeof(idx_t), sizeof(idx_t)); + + idx_t offset = sizeof(idx_t) * 2; + + if (EncryptTemporaryFiles()) { + uint8_t encryption_metadata[DEFAULT_ENCRYPTED_BUFFER_HEADER_SIZE]; + EncryptionEngine::EncryptTemporaryBuffer(db, buffer.InternalBuffer(), buffer.AllocSize(), + encryption_metadata); + handle->Write(context, encryption_metadata, DEFAULT_ENCRYPTED_BUFFER_HEADER_SIZE, offset); + offset += DEFAULT_ENCRYPTED_BUFFER_HEADER_SIZE; + } - if (EncryptTemporaryFiles()) { - uint8_t encryption_metadata[DEFAULT_ENCRYPTED_BUFFER_HEADER_SIZE]; - EncryptionEngine::EncryptTemporaryBuffer(db, buffer.InternalBuffer(), buffer.AllocSize(), encryption_metadata); - //! Write the nonce (and tag for GCM). - handle->Write(context, encryption_metadata, DEFAULT_ENCRYPTED_BUFFER_HEADER_SIZE, offset); - offset += DEFAULT_ENCRYPTED_BUFFER_HEADER_SIZE; + buffer.Write(context, *handle, offset); + } catch (...) { + // roll back the accounting and remove the partially written file + if (size_on_disk_increased) { + temporary_directory.handle->GetTempFile().DecreaseSizeOnDisk(buffer.AllocSize() + header_size); + } + handle.reset(); + fs.RemoveFile(path); + throw; + } + evicted_data_per_tag[uint8_t(tag)] += buffer.AllocSize(); + if (context.GetClientContext()) { + QueryProfiler::Get(*context.GetClientContext()).TrackBytesSpilled(buffer.AllocSize() + header_size); } - - buffer.Write(context, *handle, offset); } unique_ptr StandardBufferManager::ReadTemporaryBuffer(QueryContext context, MemoryTag tag, @@ -658,7 +673,8 @@ unique_ptr StandardBufferManager::ReadTemporaryBuffer(QueryContext c } void StandardBufferManager::DeleteTemporaryFile(BlockMemory &memory) { - if (!memory.IsUnloaded()) { + // Without spill storage, there are no temporary buffers to delete. + if (!memory.IsUnloaded() || GetUsedSwap() == 0) { return; } auto id = memory.BlockId(); @@ -684,13 +700,11 @@ void StandardBufferManager::DeleteTemporaryFile(BlockMemory &memory) { // The file is not in the shared pool of files. auto &fs = FileSystem::GetFileSystem(db); auto path = GetTemporaryPath(id); - if (fs.FileExists(path)) { + auto metadata = fs.GetStatsIfExists(path); + if (metadata) { evicted_data_per_tag[uint8_t(memory.GetMemoryTag())] -= memory.GetMemoryUsage(); - auto handle = fs.OpenFile(path, FileFlags::FILE_FLAGS_READ); - auto content_size = handle->GetFileSize(); - handle.reset(); fs.RemoveFile(path); - temporary_directory.handle->GetTempFile().DecreaseSizeOnDisk(content_size); + temporary_directory.handle->GetTempFile().DecreaseSizeOnDisk(NumericCast(metadata->file_size)); } } @@ -741,15 +755,15 @@ vector StandardBufferManager::GetTemporaryFiles() { } // Another process or thread can delete the file before we can get its file size. - auto handle = fs.OpenFile(name, FileFlags::FILE_FLAGS_READ | FileFlags::FILE_FLAGS_NULL_IF_NOT_EXISTS); - if (!handle) { + auto path = fs.JoinPath(temporary_directory.path, name); + auto metadata = fs.GetStatsIfExists(path); + if (!metadata) { return; } TemporaryFileInformation info; - info.path = name; - info.size = NumericCast(fs.GetFileSize(*handle)); - handle.reset(); + info.path = std::move(path); + info.size = NumericCast(metadata->file_size); result.push_back(info); }); return result; @@ -792,7 +806,7 @@ data_ptr_t StandardBufferManager::BufferAllocatorAllocate(PrivateAllocatorData * StringUtil::BytesToHumanReadableString(size)); // We rely on manual tracking of this one. :( reservation.size = 0; - return Allocator::Get(data.manager.db).AllocateData(size); + return BlockAllocator::Get(data.manager.db).AllocateData(size); } void StandardBufferManager::BufferAllocatorFree(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t size) { @@ -800,7 +814,7 @@ void StandardBufferManager::BufferAllocatorFree(PrivateAllocatorData *private_da BufferPoolReservation r(MemoryTag::ALLOCATOR, data.manager.GetBufferPool()); r.size = size; r.Resize(0); - return Allocator::Get(data.manager.db).FreeData(pointer, size); + return BlockAllocator::Get(data.manager.db).FreeData(pointer, size); } data_ptr_t StandardBufferManager::BufferAllocatorRealloc(PrivateAllocatorData *private_data, data_ptr_t pointer, @@ -813,7 +827,7 @@ data_ptr_t StandardBufferManager::BufferAllocatorRealloc(PrivateAllocatorData *p r.size = old_size; r.Resize(size); r.size = 0; - return Allocator::Get(data.manager.db).ReallocateData(pointer, old_size, size); + return BlockAllocator::Get(data.manager.db).ReallocateData(pointer, old_size, size); } Allocator &BufferAllocator::Get(ClientContext &context) { diff --git a/src/duckdb/src/storage/statistics/distinct_statistics.cpp b/src/duckdb/src/storage/statistics/distinct_statistics.cpp index d1ed53b81..da67816f0 100644 --- a/src/duckdb/src/storage/statistics/distinct_statistics.cpp +++ b/src/duckdb/src/storage/statistics/distinct_statistics.cpp @@ -1,6 +1,8 @@ #include "duckdb/storage/statistics/distinct_statistics.hpp" +#include "duckdb/common/random_engine.hpp" #include "duckdb/common/string_util.hpp" +#include "duckdb/common/types/hash.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" namespace duckdb { @@ -16,6 +18,88 @@ unique_ptr DistinctStatistics::Copy() const { return make_uniq(log->Copy(), sample_count, total_count); } +static SelectionVector CreateSampleSelection(idx_t sample_count) { + SelectionVector result(sample_count); + RandomEngine random(0); + for (idx_t i = 0; i < sample_count; i++) { + const auto begin = i * STANDARD_VECTOR_SIZE / sample_count; + const auto end = (i + 1) * STANDARD_VECTOR_SIZE / sample_count; + result.set_index(i, begin + random.NextRandomInteger64() % (end - begin)); + } + return result; +} + +template +static void UpdateSampleValues(HyperLogLog &log, const Vector &input, const SelectionVector &selection, idx_t count) { + auto values = input.Values(); + if (values.CanHaveNull()) { + for (idx_t i = 0; i < count; i++) { + auto entry = values[selection.get_index(i)]; + if (entry.IsValid()) { + log.InsertElement(duckdb::Hash(entry.GetValue())); + } + } + } else { + for (idx_t i = 0; i < count; i++) { + log.InsertElement(duckdb::Hash(values[selection.get_index(i)].GetValue())); + } + } +} + +static bool TryUpdateSampleValues(HyperLogLog &log, const Vector &input, const SelectionVector &selection, + idx_t count) { + if (input.GetVectorType() != VectorType::FLAT_VECTOR) { + return false; + } + switch (input.GetType().InternalType()) { + case PhysicalType::INT8: + UpdateSampleValues(log, input, selection, count); + break; + case PhysicalType::INT16: + UpdateSampleValues(log, input, selection, count); + break; + case PhysicalType::INT32: + UpdateSampleValues(log, input, selection, count); + break; + case PhysicalType::INT64: + UpdateSampleValues(log, input, selection, count); + break; + case PhysicalType::UINT8: + UpdateSampleValues(log, input, selection, count); + break; + case PhysicalType::UINT16: + UpdateSampleValues(log, input, selection, count); + break; + case PhysicalType::UINT32: + UpdateSampleValues(log, input, selection, count); + break; + case PhysicalType::UINT64: + UpdateSampleValues(log, input, selection, count); + break; + case PhysicalType::INT128: + UpdateSampleValues(log, input, selection, count); + break; + case PhysicalType::UINT128: + UpdateSampleValues(log, input, selection, count); + break; + case PhysicalType::FLOAT: + UpdateSampleValues(log, input, selection, count); + break; + case PhysicalType::DOUBLE: + UpdateSampleValues(log, input, selection, count); + break; + case PhysicalType::INTERVAL: + UpdateSampleValues(log, input, selection, count); + break; + case PhysicalType::VARCHAR: + UpdateSampleValues(log, input, selection, count); + break; + default: + return false; + } + return true; +} + void DistinctStatistics::Merge(const DistinctStatistics &other) { log->Merge(*other.log); sample_count += other.sample_count; @@ -31,7 +115,24 @@ void DistinctStatistics::UpdateSample(const Vector &new_data, idx_t count, Vecto // But never more than the original count count = MinValue(count, original_count); - UpdateInternal(new_data, count, hashes); + if (original_count != STANDARD_VECTOR_SIZE || count == original_count) { + UpdateInternal(new_data, count, hashes); + return; + } + + static auto integral_sample = + CreateSampleSelection(MaxValue(LossyNumericCast(INTEGRAL_SAMPLE_RATE * STANDARD_VECTOR_SIZE), 1)); + static auto other_sample = + CreateSampleSelection(MaxValue(LossyNumericCast(BASE_SAMPLE_RATE * STANDARD_VECTOR_SIZE), 1)); + auto &sample_selection = new_data.GetType().IsIntegral() ? integral_sample : other_sample; + if (TryUpdateSampleValues(*log, new_data, sample_selection, count)) { + sample_count += count; + return; + } + // Borrow the static indices to avoid reference-count contention between insertion threads. + SelectionVector selection(sample_selection.data(), count); + Vector sample(new_data, selection, count); + UpdateInternal(sample, count, hashes); } void DistinctStatistics::Update(const Vector &new_data, idx_t count, Vector &hashes) { diff --git a/src/duckdb/src/storage/statistics/geometry_stats.cpp b/src/duckdb/src/storage/statistics/geometry_stats.cpp index 7949c2265..7c6235bc7 100644 --- a/src/duckdb/src/storage/statistics/geometry_stats.cpp +++ b/src/duckdb/src/storage/statistics/geometry_stats.cpp @@ -60,6 +60,16 @@ vector GeometryTypeSet::ToString(bool snake_case) const { return result; } +bool GeometryTypeSet::TryAdd(const string &type_name) { + auto names = Unknown().ToString(true); + const auto idx = StringUtil::CIFind(names, type_name); + if (idx == DConstants::INVALID_INDEX) { + return false; + } + Add(static_cast(idx % PART_TYPES), static_cast(idx / PART_TYPES)); + return true; +} + BaseStatistics GeometryStats::CreateUnknown(LogicalType type) { BaseStatistics result(std::move(type)); result.InitializeUnknown(); diff --git a/src/duckdb/src/storage/statistics/numeric_stats.cpp b/src/duckdb/src/storage/statistics/numeric_stats.cpp index b2b49005c..14640c6a1 100644 --- a/src/duckdb/src/storage/statistics/numeric_stats.cpp +++ b/src/duckdb/src/storage/statistics/numeric_stats.cpp @@ -9,8 +9,20 @@ #include "duckdb/common/types/vector.hpp" #include "duckdb/storage/statistics/base_statistics.hpp" +#include + namespace duckdb { +namespace { + +template +void InitializeEmptyFloatingPointStats(BaseStatistics &result) { + NumericStats::SetMin(result, std::numeric_limits::quiet_NaN()); + NumericStats::SetMax(result, NumericLimits::Minimum()); +} + +} // namespace + BaseStatistics NumericStats::CreateUnknown(LogicalType type) { BaseStatistics result(std::move(type)); result.InitializeUnknown(); @@ -19,11 +31,42 @@ BaseStatistics NumericStats::CreateUnknown(LogicalType type) { return result; } +//! The type whose limits are used for the min/max of empty statistics +static LogicalType GetEmptyStatsLimitType(const LogicalType &type) { + if (type.id() != LogicalTypeId::ENUM) { + return type; + } + // the dictionary of an ENUM can be empty or have a single entry: use the limits of the index type instead, so + // that the min is larger than the max until a value is added + switch (type.InternalType()) { + case PhysicalType::UINT8: + return LogicalType::UTINYINT; + case PhysicalType::UINT16: + return LogicalType::USMALLINT; + case PhysicalType::UINT32: + return LogicalType::UINTEGER; + default: + throw InternalException("Invalid Internal Type for ENUMs"); + } +} + BaseStatistics NumericStats::CreateEmpty(LogicalType type) { BaseStatistics result(std::move(type)); result.InitializeEmpty(); - SetMin(result, Value::MaximumValue(result.GetType())); - SetMax(result, Value::MinimumValue(result.GetType())); + switch (result.GetType().InternalType()) { + case PhysicalType::FLOAT: + InitializeEmptyFloatingPointStats(result); + break; + case PhysicalType::DOUBLE: + InitializeEmptyFloatingPointStats(result); + break; + default: { + auto limit_type = GetEmptyStatsLimitType(result.GetType()); + SetMin(result, Value::MaximumValue(limit_type)); + SetMax(result, Value::MinimumValue(limit_type)); + break; + } + } return result; } @@ -42,6 +85,13 @@ void NumericStats::Merge(BaseStatistics &stats, const BaseStatistics &other) { return; } D_ASSERT(stats.GetType() == other.GetType()); + // If the min and max of the stats are not valid, we need to merge the min and max of the other stats. + if (NumericStats::HasMin(stats) && NumericStats::HasMax(stats) && + NumericStats::Min(stats) > NumericStats::Max(stats)) { + NumericStats::SetMin(stats, NumericStats::HasMin(other) ? NumericStats::Min(other) : Value()); + NumericStats::SetMax(stats, NumericStats::HasMax(other) ? NumericStats::Max(other) : Value()); + return; + } if (NumericStats::HasMin(other) && NumericStats::HasMin(stats)) { auto other_min = NumericStats::Min(other); if (other_min < NumericStats::Min(stats)) { @@ -321,8 +371,26 @@ bool NumericStats::ConstantsCoverRange(const BaseStatistics &stats, array_ptr +static bool ZeroSignsMatch(const BaseStatistics &stats) { + auto min = NumericStats::GetMinUnsafe(stats); + auto max = NumericStats::GetMaxUnsafe(stats); + return min != T(0) || std::signbit(min) == std::signbit(max); +} + bool NumericStats::IsConstant(const BaseStatistics &stats) { - return NumericStats::Max(stats) <= NumericStats::Min(stats); + if (NumericStats::Max(stats) > NumericStats::Min(stats)) { + return false; + } + // -0.0 == 0.0, so a zero range is only constant if both bounds have the same sign + switch (stats.GetType().InternalType()) { + case PhysicalType::FLOAT: + return ZeroSignsMatch(stats); + case PhysicalType::DOUBLE: + return ZeroSignsMatch(stats); + default: + return true; + } } void SetNumericValueInternal(const Value &input, const LogicalType &type, NumericValueUnion &val, bool &has_val) { diff --git a/src/duckdb/src/storage/statistics/string_stats.cpp b/src/duckdb/src/storage/statistics/string_stats.cpp index 1f2279265..358bb6304 100644 --- a/src/duckdb/src/storage/statistics/string_stats.cpp +++ b/src/duckdb/src/storage/statistics/string_stats.cpp @@ -626,8 +626,18 @@ void StringStats::MergeStats(BaseStatistics &stats, string_t &target, StringStat return; } bool new_is_more_extreme = is_min ? comparison < 0 : comparison > 0; + // for min: a prefix is always <= its extensions, so the shorter value is already a safe lower bound + // for max: a truncated prefix represents unknown larger extensions and must be kept as the upper bound + if (!is_min && memcmp(source.GetData(), target.GetData(), MinValue(source.GetSize(), target.GetSize())) == 0) { + if (comparison < 0) { + // source is a prefix of target: source wins if it is truncated + new_is_more_extreme = source_type == StringStatsType::TRUNCATED_STATS; + } else { + // target is a prefix of source: target wins if it is truncated + new_is_more_extreme = target_type != StringStatsType::TRUNCATED_STATS; + } + } if (!new_is_more_extreme) { - // old value is more extreme - bail return; } // assign the new value diff --git a/src/duckdb/src/storage/statistics/variant_stats.cpp b/src/duckdb/src/storage/statistics/variant_stats.cpp index 473da8b90..5440fb2d3 100644 --- a/src/duckdb/src/storage/statistics/variant_stats.cpp +++ b/src/duckdb/src/storage/statistics/variant_stats.cpp @@ -5,6 +5,7 @@ #include "duckdb/storage/statistics/base_statistics.hpp" #include "duckdb/common/types/vector.hpp" +#include "duckdb/common/type_visitor.hpp" #include "duckdb/common/serializer/serializer.hpp" #include "duckdb/common/serializer/deserializer.hpp" @@ -259,6 +260,12 @@ LogicalType VariantStats::GetShreddedStructuredType(const BaseStatistics &stats) return ToStructuredType(GetShreddedStats(stats).GetType()); } +LogicalType VariantStats::GetShreddingType(const LogicalType &structured_type) { + return TypeVisitor::VisitReplace(structured_type, [](const LogicalType &type) { + return LogicalType::STRUCT({{"typed_value", type}, {"untyped_value_index", LogicalType::UINTEGER}}); + }); +} + void VariantStats::CreateShreddedStats(BaseStatistics &stats, const LogicalType &shredded_type) { BaseStatistics::Construct(stats.child_stats[1], shredded_type); auto &data = GetDataUnsafe(stats); @@ -525,7 +532,7 @@ static unique_ptr TryBuildShreddingStats(const LogicalType &type return WrapTypedValue(typed_value, nullptr).ToUnique(); } default: - if (type.IsNested() || type.id() == LogicalTypeId::ENUM) { + if (type.IsNested() || type.id() == LogicalTypeId::ENUM || type.IsJSONType()) { // MAP / UNION / ENUM etc. are not stored in their source representation in the variant return nullptr; } @@ -623,10 +630,10 @@ bool VariantStats::MergeShredding(const BaseStatistics &stats, const BaseStatist auto &other_object_children = StructType::GetChildTypes(other_typed_value_type); //! Map field name to index, for 'other' - case_insensitive_map_t key_to_index; + unordered_map key_to_index; for (idx_t i = 0; i < other_object_children.size(); i++) { auto &other_object_child = other_object_children[i]; - key_to_index.emplace(other_object_child.first, i); + key_to_index.emplace(other_object_child.first.GetIdentifierName(), i); } //! Attempt to merge all overlapping fields, only keep the fields that were able to be merged @@ -689,8 +696,8 @@ bool VariantStats::MergeShredding(const BaseStatistics &stats, const BaseStatist return true; } else { D_ASSERT(!stats_typed_value_type.IsNested()); - if (stats_typed_value_type.id() != other_typed_value_type.id()) { - //! other is not the same type, can't merge + if (stats_typed_value_type != other_typed_value_type) { + //! other is not the same type (e.g. a DECIMAL of a different width or scale), can't merge return false; } auto new_typed_stats = stats_typed_value.Copy(); diff --git a/src/duckdb/src/storage/storage_manager.cpp b/src/duckdb/src/storage/storage_manager.cpp index 5c8aa286d..2c7c650e4 100644 --- a/src/duckdb/src/storage/storage_manager.cpp +++ b/src/duckdb/src/storage/storage_manager.cpp @@ -1,6 +1,7 @@ #include "duckdb/storage/storage_manager.hpp" #include "duckdb/catalog/catalog.hpp" +#include "duckdb/common/enums/checkpoint_abort.hpp" #include "duckdb/common/file_system.hpp" #include "duckdb/common/string_util.hpp" #include "duckdb/logging/logger.hpp" @@ -10,6 +11,7 @@ #include "duckdb/main/query_profiler.hpp" #include "duckdb/main/settings.hpp" #include "duckdb/main/database.hpp" +#include "duckdb/main/valid_checker.hpp" #include "duckdb/storage/checkpoint_manager.hpp" #include "duckdb/storage/in_memory_block_manager.hpp" #include "duckdb/storage/object_cache.hpp" @@ -141,7 +143,7 @@ void StorageOptions::Initialize(unordered_map &options) { entry.second.ToString()); } } else if (entry.first == "mmap_reserve_size") { - mmap_reserve_size = DBConfig::ParseMemoryLimit(entry.second.ToString()); + mmap_reserve_size = DBConfig::ParseMemoryLimitOrMaximum(entry.second.ToString()); } else { throw BinderException("Unrecognized option for attach \"%s\"", entry.first); } @@ -633,6 +635,7 @@ class SingleFileStorageCommitState : public StorageCommitState { bool HasRowGroupData() override; private: + StorageManager &storage; idx_t initial_wal_size = 0; idx_t initial_written = 0; WriteAheadLog &wal; @@ -641,7 +644,7 @@ class SingleFileStorageCommitState : public StorageCommitState { }; SingleFileStorageCommitState::SingleFileStorageCommitState(StorageManager &storage, WriteAheadLog &wal) - : wal(wal), state(WALCommitState::IN_PROGRESS) { + : storage(storage), wal(wal), state(WALCommitState::IN_PROGRESS) { auto initial_size = storage.GetWALSize(); initial_written = wal.GetTotalWritten(); initial_wal_size = initial_size; @@ -673,6 +676,16 @@ void SingleFileStorageCommitState::RevertCommit() { // remove any entries written into the WAL by truncating it wal.Truncate(initial_wal_size); } + auto &block_manager = storage.GetBlockManager(); + for (auto &entry : optimistically_written_data) { + for (auto &rg_entry : entry.second) { + if (rg_entry.second.row_group_data) { + for (auto &block_id : rg_entry.second.row_group_data->GetBlockIds()) { + block_manager.MarkBlockAsModified(block_id); + } + } + } + } state = WALCommitState::TRUNCATED; } @@ -680,7 +693,20 @@ idx_t SingleFileStorageCommitState::FlushCommit(bool sync_now) { if (state != WALCommitState::IN_PROGRESS) { return 0; } + // Move the blocks in this COMMIT into the WAL and mark them as "in use". + auto abort_mode = Settings::Get(storage.GetDatabase()); + if (wal.Initialized() && abort_mode == CheckpointAbort::DEBUG_ABORT_BEFORE_WAL_FLUSH) { + auto &writer = wal.Initialize(); + writer.Sync(); + storage.SetWALSize(writer.GetFileSize()); + ValidChecker::Invalidate(storage.GetDatabase(), "Simulated crash before WAL_FLUSH write"); + ValidChecker::Invalidate(storage.GetAttached(), "Simulated crash before WAL_FLUSH write"); + // Prevent `RevertCommit` from truncating the WAL so the torn records are kept for crash recovery + state = WALCommitState::FLUSHED; + throw FatalException("Simulated crash before WAL_FLUSH write"); + } + idx_t wal_sync_offset = 0; if (sync_now) { wal.Flush(); diff --git a/src/duckdb/src/storage/table/column_data.cpp b/src/duckdb/src/storage/table/column_data.cpp index 32803e7b0..c820ee4f8 100644 --- a/src/duckdb/src/storage/table/column_data.cpp +++ b/src/duckdb/src/storage/table/column_data.cpp @@ -3,6 +3,7 @@ #include "duckdb/common/exception/transaction_exception.hpp" #include "duckdb/common/types/validity_mask.hpp" +#include "duckdb/common/operator/add.hpp" #include "duckdb/main/settings.hpp" #include "duckdb/common/serializer/binary_deserializer.hpp" #include "duckdb/common/vector/flat_vector.hpp" @@ -261,23 +262,71 @@ idx_t ColumnData::ScanVector(ColumnScanState &state, Vector &result, idx_t remai } void ColumnData::SelectVector(ColumnScanState &state, Vector &result, idx_t target_count, const SelectionVector &sel, - idx_t sel_count) { + idx_t sel_count, ScanVectorType scan_type) { BeginScanVectorInternal(state); - auto ¤t = state.current->GetNode(); - if (state.current->GetRowStart() + current.count - state.offset_in_column < target_count) { - throw InternalException("ColumnData::SelectVector should be able to fetch everything from one segment"); - } - if (state.scan_options && state.scan_options->force_fetch_row) { - for (idx_t i = 0; i < sel_count; i++) { - auto source_idx = sel.get_index(i); - ColumnFetchState fetch_state; - current.FetchRow(fetch_state, UnsafeNumericCast(state.offset_in_column + source_idx), result, i); + // scratch space to rebase the selection vector to the start of a segment scan + // this owns its data, as compression functions can retain the selection vector (e.g. by slicing the result) + SelectionVector rebased_sel; + idx_t sel_offset = 0; + idx_t scanned_count = 0; + // the position up to which the scan state of the current segment has been advanced + // segments without any selected rows are not scanned, so they must be skipped in the next scan + idx_t segment_internal_index = state.offset_in_column; + while (scanned_count < target_count) { + auto ¤t = state.current->GetNode(); + auto current_start = state.current->GetRowStart(); + idx_t scan_count = + MinValue(target_count - scanned_count, current_start + current.count - state.offset_in_column); + segment_internal_index = state.offset_in_column; + // gather the selected rows that fall within this segment + idx_t segment_sel_count = 0; + while (sel_offset + segment_sel_count < sel_count && + sel.get_index(sel_offset + segment_sel_count) < scanned_count + scan_count) { + segment_sel_count++; + } + if (segment_sel_count > 0) { + // the indices must be relative to the start of the segment scan - for the first segment this is + // already the case, so we can forward the selection vector of the caller directly + reference segment_sel(sel); + if (scanned_count > 0) { + if (!rebased_sel.IsSet()) { + rebased_sel.Initialize(); + } + for (idx_t i = 0; i < segment_sel_count; i++) { + rebased_sel.set_index(i, sel.get_index(sel_offset + i) - scanned_count); + } + segment_sel = rebased_sel; + } + if (state.scan_options && state.scan_options->force_fetch_row) { + for (idx_t i = 0; i < segment_sel_count; i++) { + ColumnFetchState fetch_state; + auto source_idx = state.offset_in_column + segment_sel.get().get_index(i) - current_start; + current.FetchRow(fetch_state, UnsafeNumericCast(source_idx), result, sel_offset + i); + } + } else { + current.Select(state, scan_count, result, segment_sel.get(), segment_sel_count, sel_offset, scan_type); + } + segment_internal_index = state.offset_in_column + scan_count; + } + sel_offset += segment_sel_count; + scanned_count += scan_count; + state.offset_in_column += scan_count; + if (scanned_count < target_count) { + auto next = data.GetNextSegment(*state.current); + if (!next) { + break; + } + state.previous_states.emplace_back(std::move(state.scan_state)); + state.current = next; + state.current->GetNode().InitializeScan(state); + state.segment_checked = false; } - } else { - current.Select(state, target_count, result, sel, sel_count); } - state.offset_in_column += target_count; - state.internal_index = state.offset_in_column; + if (scan_type == ScanVectorType::SCAN_FLAT_VECTOR) { + // not every compression function sets the size of the result + FlatVector::SetSize(result, count_t(sel_count)); + } + state.internal_index = segment_internal_index; } void ColumnData::FilterVector(ColumnScanState &state, Vector &result, idx_t target_count, SelectionVector &sel, @@ -871,7 +920,7 @@ void ColumnData::CheckpointScan(ColumnSegment &segment, ColumnScanState &state, if (state.scan_options && state.scan_options->force_fetch_row) { for (idx_t i = 0; i < count; i++) { ColumnFetchState fetch_state; - fetch_state.row_group = state.parent->row_group; + fetch_state.row_group = state.parent->GetRowGroup(); segment.FetchRow(fetch_state, UnsafeNumericCast(state.offset_in_column + i), scan_vector, i); } } else { @@ -917,13 +966,20 @@ void ColumnData::InitializeColumn(PersistentColumnData &column_data) { } void ColumnData::InitializeColumn(PersistentColumnData &column_data, BaseStatistics &target_stats) { - D_ASSERT(type.InternalType() == column_data.logical_type.InternalType()); + if (type.InternalType() != column_data.logical_type.InternalType()) { + throw DataCorruptionException("Column data of type %s found for a column of type %s", column_data.logical_type, + type); + } // construct the segments based on the data pointers this->count = 0; for (auto &data_pointer : column_data.pointers) { // Update the count and statistics data_pointer.row_start = count; - this->count += data_pointer.tuple_count; + idx_t new_count; + if (!TryAddOperator::Operation(count.load(), data_pointer.tuple_count, new_count)) { + throw DataCorruptionException("Column data has more rows than can be represented"); + } + this->count = new_count; // Merge the statistics. If this is a child column, the target_stats reference will point into the parents stats // otherwise if this is a top level column, `stats->statistics` == `target_stats` @@ -1029,16 +1085,26 @@ static PersistentColumnData GetPersistentColumnDataType(Deserializer &deserializ } // Otherwise, the type of this segment may depend on extra data + auto &column_type = deserializer.Get(); switch (extra_data->GetType()) { case ExtraPersistentColumnDataType::VARIANT: { + if (column_type.id() != LogicalTypeId::VARIANT) { + throw DataCorruptionException("VARIANT column data found for a column of type %s", column_type); + } auto unshredded_type = VariantShredding::GetUnshreddedType(); PersistentColumnData result(LogicalType::VARIANT()); result.extra_data = std::move(extra_data); return result; } case ExtraPersistentColumnDataType::GEOMETRY: { + if (column_type.id() != LogicalTypeId::GEOMETRY) { + throw DataCorruptionException("GEOMETRY column data found for a column of type %s", column_type); + } const auto &geometry_data = extra_data->Cast(); - PersistentColumnData result(Geometry::GetVectorizedType(geometry_data.storage_type)); + // WKB is stored as the column's own type, which may carry type parameters (e.g., a CRS) + PersistentColumnData result(geometry_data.storage_type == GeometryStorageType::WKB + ? deserializer.Get() + : Geometry::GetVectorizedType(geometry_data.storage_type)); result.extra_data = std::move(extra_data); return result; } @@ -1065,8 +1131,7 @@ PersistentColumnData PersistentColumnData::Deserialize(Deserializer &deserialize // TODO: This is ugly if (result.extra_data && result.extra_data->GetType() == ExtraPersistentColumnDataType::GEOMETRY) { - auto &geo_data = result.extra_data->Cast(); - auto actual_type = Geometry::GetVectorizedType(geo_data.storage_type); + const auto &actual_type = type; // We need to set the actual type in scope, as when we deserialize "data_pointers" we use it to detect // the type of the statistics. diff --git a/src/duckdb/src/storage/table/column_segment.cpp b/src/duckdb/src/storage/table/column_segment.cpp index f164f4cd3..641f4deef 100644 --- a/src/duckdb/src/storage/table/column_segment.cpp +++ b/src/duckdb/src/storage/table/column_segment.cpp @@ -151,11 +151,16 @@ void ColumnSegment::Scan(ColumnScanState &state, idx_t scan_count, Vector &resul } void ColumnSegment::Select(ColumnScanState &state, idx_t scan_count, Vector &result, const SelectionVector &sel, - idx_t sel_count) { + idx_t sel_count, idx_t result_offset, ScanVectorType scan_type) { if (!function.get().select) { throw InternalException("ColumnSegment::Select not implemented for this compression method"); } - function.get().select(*this, state, scan_count, result, sel, sel_count); + if (scan_type == ScanVectorType::SCAN_ENTIRE_VECTOR) { + D_ASSERT(result_offset == 0); + } else { + D_ASSERT(result.GetVectorType() == VectorType::FLAT_VECTOR); + } + function.get().select(*this, state, scan_count, result, sel, sel_count, result_offset, scan_type); } void ColumnSegment::Filter(ColumnScanState &state, idx_t scan_count, Vector &result, SelectionVector &sel, @@ -490,8 +495,8 @@ static idx_t ExecuteExpressionFilterSelection(SelectionVector &sel, Vector &vect chunk.data.emplace_back(Vector::Ref(vector)); chunk.SetChildCardinality(scan_count); SelectionVector identity_sel; - optional_ptr current_sel = &sel; - if (!sel.IsSet()) { + optional_ptr current_sel = sel.IsSet() ? &sel : nullptr; + if (!sel.IsSet() && (vector.GetType().IsNested() || approved_tuple_count != scan_count)) { identity_sel = SelectionVector::Incremental(approved_tuple_count); current_sel = &identity_sel; } diff --git a/src/duckdb/src/storage/table/list_column_data.cpp b/src/duckdb/src/storage/table/list_column_data.cpp index 9704b2930..f13bfa668 100644 --- a/src/duckdb/src/storage/table/list_column_data.cpp +++ b/src/duckdb/src/storage/table/list_column_data.cpp @@ -176,7 +176,11 @@ void ListColumnData::Skip(ColumnScanState &state, idx_t count) { // however, let's just read all "count" entries for now Vector offset_vector(LogicalType::UBIGINT, count); idx_t scan_count = ScanVector(state, offset_vector, count, ScanVectorType::SCAN_FLAT_VECTOR); - D_ASSERT(scan_count > 0); + if (scan_count == 0) { + // no offsets were scanned - there is nothing to skip in the child, and reading offset[-1] below would be + // an out-of-bounds read + return; + } UnifiedVectorFormat offsets; offset_vector.ToUnifiedFormat(offsets); diff --git a/src/duckdb/src/storage/table/row_group.cpp b/src/duckdb/src/storage/table/row_group.cpp index 0f7fb3a9d..167689bb1 100644 --- a/src/duckdb/src/storage/table/row_group.cpp +++ b/src/duckdb/src/storage/table/row_group.cpp @@ -392,7 +392,7 @@ bool RowGroup::InitializeScanInternal(CollectionScanState &state, SegmentNode state.max_row ? 0 : MinValue(this->count, state.max_row - row_start); @@ -930,7 +930,7 @@ bool RowGroup::PrepareScan(ScanOptions options, CollectionScanState &state) { continue; } if (rate < 1) { - auto row_group_start = state.row_group->GetRowStart(); + auto row_group_start = state.GetRowGroup()->GetRowStart(); sample_count = SystemRowsSelection(sampling_info, row_group_start + current_row, max_count, sample_sel); if (sample_count == 0) { @@ -952,10 +952,8 @@ bool RowGroup::PrepareScan(ScanOptions options, CollectionScanState &state) { if (!CheckZonemapSegments(state)) { continue; } - auto ¤t_row_group = state.row_group->GetNode(); - // second, scan the version chunk manager to figure out which tuples to load for this transaction - idx_t count = current_row_group.GetSelVector(options, state.vector_index, state.valid_sel, max_count); + idx_t count = GetSelVector(options, state.vector_index, state.valid_sel, max_count); if (count == 0) { // nothing to scan for this vector, skip the entire vector NextVector(state); @@ -1275,6 +1273,9 @@ void RowGroup::RevertAppend(idx_t new_count) { for (auto &column : GetColumns()) { column->RevertAppend(UnsafeNumericCast(new_count)); } + if (transient) { + *transient = SuballocationBlock(); + } SetCount(new_count); Verify(); } @@ -1290,17 +1291,32 @@ void RowGroup::InitializeAppend(SegmentNode &row_group, RowGroupAppend append_state.row_group = row_group; row_group.GetNode().InitializeAppendInternal(append_state); } + +void RowGroup::InitializeNextAppend(SegmentNode &row_group, RowGroupAppendState &append_state) { + D_ASSERT(append_state.row_group); + auto &previous = append_state.row_group->GetNode(); + auto &next = row_group.GetNode(); + D_ASSERT(!RefersToSameObject(previous, next)); + D_ASSERT(RefersToSameObject(previous.GetCollection(), next.GetCollection())); + D_ASSERT(!next.transient); + next.transient = std::move(previous.transient); + InitializeAppend(row_group, append_state); +} + void RowGroup::InitializeAppendInternal(RowGroupAppendState &append_state) { if (!RefersToSameObject(append_state.row_group->GetNode(), *this)) { throw InternalException("RowGroup::InitializeAppend mismatch - call RowGroupAppendState::InitializeAppend"); } append_state.offset_in_row_group = this->count; + if (!transient) { + transient = make_uniq(); + } // for each column, initialize the append state append_state.states = make_unsafe_uniq_array(GetColumnCount()); for (idx_t i = 0; i < GetColumnCount(); i++) { auto &col_data = GetColumn(i); auto &state = append_state.states[i]; - state.transient = &append_state.transient; + state.transient = transient.get(); col_data.InitializeAppend(state); } } diff --git a/src/duckdb/src/storage/table/row_group_collection.cpp b/src/duckdb/src/storage/table/row_group_collection.cpp index 73064f80b..5cd955172 100644 --- a/src/duckdb/src/storage/table/row_group_collection.cpp +++ b/src/duckdb/src/storage/table/row_group_collection.cpp @@ -311,7 +311,6 @@ void RowGroupCollection::InitializeScan(const QueryContext &context, CollectionS optional_ptr table_filters) { state.row_groups = GetRowGroups(); auto row_group = state.GetRootSegment(); - D_ASSERT(row_group); state.max_row = state.row_groups->GetBaseRowId() + next_row_id.load(); state.Initialize(context, GetTypes()); while (row_group && !row_group->GetNode().InitializeScan(state, *row_group)) { @@ -342,7 +341,6 @@ bool RowGroupCollection::InitializeScanInRowGroup(ClientContext &context, Collec RowGroupCollection &collection, SegmentNode &row_group, idx_t vector_index, idx_t max_row, bool initialize_columns) { state.max_row = max_row; - state.row_groups = collection.GetRowGroups(); if (state.column_scans.empty()) { // initialize the scan state state.Initialize(context, collection.GetTypes()); @@ -357,7 +355,16 @@ void RowGroupCollection::InitializeParallelScan(ParallelCollectionScanState &sta state.vector_index = 0; state.max_row = state.row_groups->GetBaseRowId() + next_row_id.load(); state.batch_index = 0; - state.processed_rows = 0; + state.skipped_rows = 0; + if (state.reorderer) { + // row groups pruned by the reorderer are never scanned + idx_t total_rows = 0; + for (auto &row_group : state.row_groups->SegmentNodes()) { + total_rows += row_group.GetNode().count; + } + auto scan_rows = state.reorderer->ScanRowCount(); + state.skipped_rows = total_rows > scan_rows ? total_rows - scan_rows : 0; + } } optional_idx RowGroupCollection::NextParallelScan(ClientContext &context, ParallelCollectionScanState &state, @@ -401,7 +408,6 @@ optional_idx RowGroupCollection::NextParallelScan(ClientContext &context, Parall max_row = MinValue(max_row, state.max_row); const idx_t assignment_start = row_start + vector_index * STANDARD_VECTOR_SIZE; assignment_rows = max_row > assignment_start ? max_row - assignment_start : 0; - state.processed_rows += assignment_rows; scan_state.batch_index = ++state.batch_index; if (!state.row_number_base.IsValid() && scan_state.row_number_base.IsValid()) { state.row_number_base = scan_state.row_number_base.GetIndex(); @@ -422,6 +428,7 @@ optional_idx RowGroupCollection::NextParallelScan(ClientContext &context, Parall max_row, initialize_columns); if (!need_to_scan) { // skip this row group + state.skipped_rows.fetch_add(assignment_rows, std::memory_order_relaxed); continue; } return assignment_rows; @@ -714,7 +721,7 @@ optional_idx RowGroupCollection::Append(DataChunk &chunk, TableAppendState &stat AppendRowGroup(l, next_start); // set up the append state for this row_group auto last_row_group = state.row_groups->GetLastSegment(l); - RowGroup::InitializeAppend(*last_row_group, state.row_group_append_state); + RowGroup::InitializeNextAppend(*last_row_group, state.row_group_append_state); state.row_group_start = next_start; } state.current_row += row_t(total_append_count); @@ -1087,6 +1094,18 @@ void RowGroupCollection::UpdateColumn(TransactionData transaction, DuckTableEntr //===--------------------------------------------------------------------===// // Checkpoint State //===--------------------------------------------------------------------===// +struct VacuumState { + bool can_vacuum_deletes = true; + bool can_change_row_ids = false; + //! How vacuum handles the table's indexes when it changes rowids. + VacuumIndexStrategy index_strategy = VacuumIndexStrategy::KEEP_ROW_IDS; + //! The indexes to remap, populated only when index_strategy == REMAP. + vector> remap_indexes; + idx_t row_start = 0; + idx_t next_vacuum_idx = 0; + vector row_group_counts; +}; + struct CollectionCheckpointState { CollectionCheckpointState(RowGroupCollection &collection, TableDataWriter &writer, TableStatistics &global_stats, RowGroupSegmentTree &row_groups, idx_t segment_count) @@ -1098,11 +1117,19 @@ struct CollectionCheckpointState { overridden_segments.resize(segment_count); } + ~CollectionCheckpointState() { + // the tasks reference this state, so join here rather than relying on the executor's own destructor + // doing it after the members declared below it are already gone + executor->CancelAndDrain(); + } + RowGroupCollection &collection; TableDataWriter &writer; unique_ptr executor; vector> writers; vector write_data; + //! Owned here so that no task can outlive it, the destructor above joins before anything is torn down + VacuumState vacuum_state; TableStatistics &global_stats; RowGroupSegmentTree &row_groups; @@ -1288,18 +1315,6 @@ class VacuumIndexRemapper { DataChunk append_chunk; }; -struct VacuumState { - bool can_vacuum_deletes = true; - bool can_change_row_ids = false; - //! How vacuum handles the table's indexes when it changes rowids. - VacuumIndexStrategy index_strategy = VacuumIndexStrategy::KEEP_ROW_IDS; - //! The indexes to remap, populated only when index_strategy == REMAP. - vector> remap_indexes; - idx_t row_start = 0; - idx_t next_vacuum_idx = 0; - vector row_group_counts; -}; - class VacuumTask : public BaseCheckpointTask { public: VacuumTask(CollectionCheckpointState &checkpoint_state, VacuumState &vacuum_state, idx_t segment_idx, @@ -1404,8 +1419,8 @@ class VacuumTask : public BaseCheckpointTask { // move to the next row group current_append_idx++; - RowGroup::InitializeAppend(*new_row_groups[current_append_idx], - append_state.row_group_append_state); + RowGroup::InitializeNextAppend(*new_row_groups[current_append_idx], + append_state.row_group_append_state); // slice chunk for the next append append_chunk.Slice(append_count, remaining); } @@ -1736,12 +1751,12 @@ CollectionCheckpointResult RowGroupCollection::Checkpoint(TableDataWriter &write } CollectionCheckpointState checkpoint_state(*this, writer, global_stats, *row_groups, checkpointed_row_group_count); - VacuumState vacuum_state; + auto &vacuum_state = checkpoint_state.vacuum_state; InitializeVacuumState(checkpoint_state, vacuum_state, writer.GetRowGroupCount()); auto &transaction_manager = DuckTransactionManager::Get(GetAttached()); auto lowest_visibility_bound = transaction_manager.LowestVisibilityBound(); - try { + { // schedule tasks idx_t total_vacuum_tasks = 0; auto max_vacuum_tasks = Settings::Get(writer.GetDatabase()); @@ -1775,11 +1790,6 @@ CollectionCheckpointResult RowGroupCollection::Checkpoint(TableDataWriter &write } vacuum_state.row_start += row_group.count; } - } catch (const std::exception &e) { - ErrorData error(e); - checkpoint_state.executor->PushError(std::move(error)); - checkpoint_state.executor->WorkOnTasks(); // ensure all tasks have completed first before rethrowing - throw; } // all tasks have been successfully scheduled - execute tasks until we are done checkpoint_state.executor->WorkOnTasks(); @@ -1833,18 +1843,15 @@ CollectionCheckpointResult RowGroupCollection::Checkpoint(TableDataWriter &write } writer.WriteUnchangedTable(metadata_pointer, metadata_pointers, checkpoint_total_rows, checkpoint_next_row_id); - // copy over existing stats into the global stats - CopyStats(global_stats); return CollectionCheckpointResult(); } } // not all segments have stayed the same - we need to make a new segment tree with the new set of segments - auto new_row_groups = make_shared_ptr(*this, row_groups->GetBaseRowId()); - - // initialize new empty stats (before locking the new tree: ALTER takes the statistics lock before a tree lock) + // initialize new empty stats - before locking the new segment tree, as this takes the stats lock global_stats.InitializeEmpty(stats); + auto new_row_groups = make_shared_ptr(*this, row_groups->GetBaseRowId()); auto l = new_row_groups->Lock(); idx_t new_total_rows = 0; diff --git a/src/duckdb/src/storage/table/row_group_reorderer.cpp b/src/duckdb/src/storage/table/row_group_reorderer.cpp index 4ad40d46d..315230a31 100644 --- a/src/duckdb/src/storage/table/row_group_reorderer.cpp +++ b/src/duckdb/src/storage/table/row_group_reorderer.cpp @@ -210,6 +210,15 @@ RowGroupReorderer::RowGroupReorderer(const RowGroupOrderOptions &options_p, Tran : options(options_p), transaction(transaction_p), offset(0), initialized(false) { } +idx_t RowGroupReorderer::ScanRowCount() const { + D_ASSERT(initialized); + idx_t count = 0; + for (auto &row_group : ordered_row_groups) { + count += row_group.get().GetNode().count; + } + return count; +} + optional_ptr> RowGroupReorderer::GetNextRowGroup(SegmentNode &row_group) { D_ASSERT(RefersToSameObject(ordered_row_groups[offset].get(), row_group)); if (offset >= ordered_row_groups.size() - 1) { diff --git a/src/duckdb/src/storage/table/row_id_column_data.cpp b/src/duckdb/src/storage/table/row_id_column_data.cpp index dc749bd41..a285c42e4 100644 --- a/src/duckdb/src/storage/table/row_id_column_data.cpp +++ b/src/duckdb/src/storage/table/row_id_column_data.cpp @@ -12,7 +12,7 @@ RowIdColumnData::RowIdColumnData(BlockManager &block_manager, DataTableInfo &inf } idx_t RowIdColumnData::GetRowStart(ColumnScanState &state) { - return state.parent->row_group->GetRowStart(); + return state.parent->GetRowGroup()->GetRowStart(); } FilterPropagateResult RowIdColumnData::CheckZonemap(ColumnScanState &state, TableFilter &filter, diff --git a/src/duckdb/src/storage/table/scan_state.cpp b/src/duckdb/src/storage/table/scan_state.cpp index 8cb54810b..d7083ddbb 100644 --- a/src/duckdb/src/storage/table/scan_state.cpp +++ b/src/duckdb/src/storage/table/scan_state.cpp @@ -190,6 +190,16 @@ void ColumnScanState::Next(idx_t count) { } } +void ColumnScanState::ReleasePins() { + current = nullptr; + initialized = false; + scan_state.reset(); + previous_states.clear(); + for (auto &child_state : child_states) { + child_state.ReleasePins(); + } +} + const vector &CollectionScanState::GetColumnIds() { return parent.GetColumnIds(); } @@ -209,7 +219,7 @@ TableScanOptions &CollectionScanState::GetOptions() { } ParallelCollectionScanState::ParallelCollectionScanState() - : collection(nullptr), current_row_group(nullptr), processed_rows(0) { + : collection(nullptr), current_row_group(nullptr), skipped_rows(0) { } void ParallelCollectionScanState::AssignRowGroup(optional_ptr> row_group) { @@ -235,8 +245,17 @@ ParallelCollectionScanState::GetNextRowGroup(RowGroupSegmentTree &row_groups, Se } CollectionScanState::CollectionScanState(TableScanState &parent_p) - : row_group(nullptr), vector_index(0), max_row_group_row(0), row_groups(nullptr), max_row(0), batch_index(0), - valid_sel(STANDARD_VECTOR_SIZE), random(-1), parent(parent_p) { + : vector_index(0), max_row_group_row(0), row_groups(nullptr), max_row(0), batch_index(0), + valid_sel(STANDARD_VECTOR_SIZE), random(-1), row_group(nullptr), parent(parent_p) { +} + +optional_ptr> CollectionScanState::GetRowGroup() const { + return row_group; +} + +void CollectionScanState::SetRowGroup(optional_ptr> row_group_p) { + row_group = row_group_p; + pinned_row_group = row_group ? row_group->ReferenceNode() : nullptr; } optional_ptr> CollectionScanState::GetNextRowGroup(SegmentNode &row_group) const { @@ -265,23 +284,23 @@ bool CollectionScanState::Scan(DuckTransaction &transaction, DataChunk &result) bool CollectionScanState::Scan(ScanOptions options, DataChunk &result, optional_ptr l) { while (row_group) { - row_group->GetNode().Scan(options, *this, result); + pinned_row_group->Scan(options, *this, result); if (result.size() > 0) { return true; } - if (max_row <= row_group->GetRowStart() + row_group->GetNode().count) { - row_group = nullptr; + if (max_row <= row_group->GetRowStart() + pinned_row_group->count) { + SetRowGroup(nullptr); return false; } do { if (l) { - row_group = GetNextRowGroup(*l, *row_group).get(); + SetRowGroup(GetNextRowGroup(*l, *row_group)); } else { - row_group = GetNextRowGroup(*row_group).get(); + SetRowGroup(GetNextRowGroup(*row_group)); } if (row_group) { if (row_group->GetRowStart() >= max_row) { - row_group = nullptr; + SetRowGroup(nullptr); break; } bool scan_row_group = row_group->GetNode().InitializeScan(*this, *row_group); @@ -297,15 +316,15 @@ bool CollectionScanState::Scan(ScanOptions options, DataChunk &result, optional_ bool CollectionScanState::Scan(DataChunk &result, TableScanType type, optional_ptr l) { while (row_group) { - row_group->GetNode().Scan(*this, result, type); + pinned_row_group->Scan(*this, result, type); if (result.size() > 0) { return true; } // move to the next row group if (l) { - row_group = GetNextRowGroup(*l, *row_group).get(); + SetRowGroup(GetNextRowGroup(*l, *row_group)); } else { - row_group = GetNextRowGroup(*row_group).get(); + SetRowGroup(GetNextRowGroup(*row_group)); } if (row_group) { row_group->GetNode().InitializeScan(*this, *row_group); @@ -319,12 +338,12 @@ bool CollectionScanState::PrepareScanIO(DuckTransaction &transaction, vectorGetNode(); + auto ¤t_row_group = *pinned_row_group; ScanOptions options {TransactionData(transaction)}; if (!current_row_group.PrepareScan(options, *this)) { // the assignment is exhausted D_ASSERT(max_row <= row_group->GetRowStart() + current_row_group.count); - row_group = nullptr; + SetRowGroup(nullptr); return false; } if (prepared_vector.prepare_state == VectorPrepareState::IO_REGISTERED) { @@ -345,13 +364,13 @@ vector> CollectionScanState::RegisterAssignmentIO() { D_ASSERT(row_group); D_ASSERT(!assignment_io_registered); assignment_io_registered = true; - auto ¤t_row_group = row_group->GetNode(); + auto ¤t_row_group = *pinned_row_group; return current_row_group.CollectScanIOTasks(*this, current_row_group.PrefetchRowCount(*this)); } void CollectionScanState::InitializeColumnScans() { if (column_scans_pending) { - row_group->GetNode().InitializeColumnScans(*this); + pinned_row_group->InitializeColumnScans(*this); } } @@ -360,6 +379,20 @@ void TableScanState::InitializeColumnScans() { local_state.InitializeColumnScans(); } +void CollectionScanState::ReleasePins() { + SetRowGroup(nullptr); + prepared_vector.Reset(); + column_scans_pending = false; + for (auto &column_scan : column_scans) { + column_scan.ReleasePins(); + } +} + +void TableScanState::ReleasePins() { + table_state.ReleasePins(); + local_state.ReleasePins(); +} + idx_t CollectionScanState::RemainingAssignmentRows() const { const idx_t current_row = vector_index * STANDARD_VECTOR_SIZE; return current_row < max_row_group_row ? max_row_group_row - current_row : 0; @@ -369,7 +402,7 @@ void CollectionScanState::ProcessPreparedScan(DuckTransaction &transaction, Data D_ASSERT(row_group); D_ASSERT(prepared_vector.prepare_state == VectorPrepareState::IO_REGISTERED); ScanOptions options {TransactionData(transaction)}; - row_group->GetNode().ProcessPreparedScan(options, *this, result); + pinned_row_group->ProcessPreparedScan(options, *this, result); } PreparedScanVector::PreparedScanVector() : sample_sel(STANDARD_VECTOR_SIZE) { diff --git a/src/duckdb/src/storage/table/standard_column_data.cpp b/src/duckdb/src/storage/table/standard_column_data.cpp index 734db61b9..ac2fa3ed2 100644 --- a/src/duckdb/src/storage/table/standard_column_data.cpp +++ b/src/duckdb/src/storage/table/standard_column_data.cpp @@ -113,14 +113,13 @@ void StandardColumnData::Select(TransactionData transaction, idx_t vector_index, bool validity_has_select = validity_compression && validity_compression->select; auto target_count = GetVectorCount(vector_index); auto scan_type = GetVectorScanType(state, target_count, result); - bool scan_entire_vector = scan_type == ScanVectorType::SCAN_ENTIRE_VECTOR; - if (!has_select || !validity_has_select || !scan_entire_vector) { - // we are not scanning an entire vector - this can have several causes (updates, etc) + if (!has_select || !validity_has_select || HasUpdates() || validity->HasUpdates()) { + // the updates need to be merged into the entire vector, so we cannot select ColumnData::Select(transaction, vector_index, state, result, sel, sel_count); return; } - SelectVector(state, result, target_count, sel, sel_count); - validity->SelectVector(state.child_states[0], result, target_count, sel, sel_count); + SelectVector(state, result, target_count, sel, sel_count, scan_type); + validity->SelectVector(state.child_states[0], result, target_count, sel, sel_count, scan_type); } void StandardColumnData::InitializeAppend(ColumnAppendState &state) { diff --git a/src/duckdb/src/storage/table/struct_column_data.cpp b/src/duckdb/src/storage/table/struct_column_data.cpp index ca7a0463d..bae1392a4 100644 --- a/src/duckdb/src/storage/table/struct_column_data.cpp +++ b/src/duckdb/src/storage/table/struct_column_data.cpp @@ -16,8 +16,6 @@ StructColumnData::StructColumnData(BlockManager &block_manager, DataTableInfo &i : ColumnData(block_manager, info, column_index, std::move(type_p), data_type, parent) { D_ASSERT(type.InternalType() == PhysicalType::STRUCT); auto &child_types = StructType::GetChildTypes(type); - // unnamed structs are deserialized as TUPLEs and never produced as a column type otherwise - D_ASSERT(type.id() != LogicalTypeId::STRUCT || child_types.empty() || !StructType::IsUnnamed(type)); if (type.id() == LogicalTypeId::VARIANT) { throw NotImplementedException("A table cannot be created from a VARIANT column yet"); } @@ -45,7 +43,8 @@ void StructColumnData::SetDataType(ColumnDataType data_type) { } idx_t StructColumnData::GetMaxEntry() { - return sub_columns[0]->GetMaxEntry(); + // every struct has a validity column, while an empty TUPLE has no child columns + return validity->GetMaxEntry(); } FilterPropagateResult StructColumnData::CheckZonemap(ColumnScanState &state, TableFilter &filter, diff --git a/src/duckdb/src/storage/table/table_statistics.cpp b/src/duckdb/src/storage/table/table_statistics.cpp index 3208cdef5..09dfc2001 100644 --- a/src/duckdb/src/storage/table/table_statistics.cpp +++ b/src/duckdb/src/storage/table/table_statistics.cpp @@ -30,9 +30,9 @@ void TableStatistics::InitializeEmpty(const TableStatistics &other) { D_ASSERT(!table_sample); D_ASSERT(other.stats_lock); + // hold other's lock while reading its stats - a concurrent ANALYZE/VACUUM can replace them (e.g. SetDistinct) + lock_guard l(*other.stats_lock); stats_lock = make_shared_ptr(); - // concurrent appends merge into the other statistics - lock_guard other_lock(*other.stats_lock); if (other.table_sample) { D_ASSERT(other.table_sample->type == SampleType::RESERVOIR_SAMPLE); auto &res = other.table_sample->Cast(); diff --git a/src/duckdb/src/storage/table/update_segment.cpp b/src/duckdb/src/storage/table/update_segment.cpp index 73118deab..c459f82ff 100644 --- a/src/duckdb/src/storage/table/update_segment.cpp +++ b/src/duckdb/src/storage/table/update_segment.cpp @@ -782,8 +782,8 @@ void UpdateSegment::RollbackUpdate(UpdateInfo &info) { // obtain an exclusive lock auto lock_handle = lock.GetExclusiveLock(); - if (info.N == 0) { - // this update info was replaced by a larger one, or has no updates + if (info.N == 0 || !info.HasPrev()) { + // this update info was replaced by a larger one, has no updates, or was never linked (the update failed) return; } // move the data from the UpdateInfo back into the root @@ -802,16 +802,20 @@ void UpdateSegment::RollbackUpdate(UpdateInfo &info) { // Cleanup Update //===--------------------------------------------------------------------===// void UpdateSegment::CleanupUpdateInternal(const StorageLockKey &lock, UpdateInfo &info) { + // pin both neighbours before modifying either: Pin can throw (OOM), and a half-unlinked node corrupts the chain + UndoBufferReference prev_pin; + UndoBufferReference next_pin; if (info.HasPrev()) { - auto pin = info.prev.Pin(); - auto &prev_info = UpdateInfo::Get(pin); - prev_info.next = info.next; + prev_pin = info.prev.Pin(); } if (info.HasNext()) { - auto next = info.next; - auto next_pin = next.Pin(); - auto &next_info = UpdateInfo::Get(next_pin); - next_info.prev = info.prev; + next_pin = info.next.Pin(); + } + if (prev_pin.IsSet()) { + UpdateInfo::Get(prev_pin).next = info.next; + } + if (next_pin.IsSet()) { + UpdateInfo::Get(next_pin).prev = info.prev; } } @@ -1493,16 +1497,6 @@ void UpdateSegment::Update(TransactionData transaction, DuckTableEntry &table_en if (count == 0) { return; } - if (statistics_update_function == UpdateStringStatistics) { - // for strings - we need to push all strings we are going to place here into the string heap of the segment - update_p.Flatten(); - auto update_data = FlatVector::GetDataMutable(update_p); - for (idx_t i = 0; i < count; i++) { - auto idx = sel.get_index(i); - update_data[idx] = GetStringHeap().AddBlob(update_data[idx]); - } - update_p.ToUnifiedFormat(update_format); - } // subsequent algorithms used by the update require row ids to be (1) sorted, and (2) unique // this is usually the case for "standard" queries (e.g. UPDATE tbl SET x=bla WHERE cond) @@ -1527,6 +1521,16 @@ void UpdateSegment::Update(TransactionData transaction, DuckTableEntry &table_en return; } } + if (statistics_update_function == UpdateStringStatistics) { + // for strings - we need to push all strings we are going to place here into the string heap of the segment + update_p.Flatten(); + auto update_data = FlatVector::GetDataMutable(update_p); + for (idx_t i = 0; i < count; i++) { + auto idx = sel.get_index(i); + update_data[idx] = GetStringHeap().AddBlob(update_data[idx]); + } + update_p.ToUnifiedFormat(update_format); + } InitializeUpdateInfo(vector_index); @@ -1618,12 +1622,14 @@ void UpdateSegment::Update(TransactionData transaction, DuckTableEntry &table_en auto new_pointer = new_ref.GetBufferPointer(); new_node.prev = node ? node->prev : root_pointer; new_node.next = node ? node->next : root_info.next; - { - auto prev_pin = new_node.prev.Pin(); - UpdateInfo::Get(prev_pin).next = new_pointer; - } + // pin both neighbours before modifying either: Pin can throw (OOM) and must not leave a half-linked node + auto prev_pin = new_node.prev.Pin(); + UndoBufferReference next_pin; if (new_node.next.IsSet()) { - auto next_pin = new_node.next.Pin(); + next_pin = new_node.next.Pin(); + } + UpdateInfo::Get(prev_pin).next = new_pointer; + if (next_pin.IsSet()) { UpdateInfo::Get(next_pin).prev = new_pointer; } if (node) { diff --git a/src/duckdb/src/storage/table/variant/variant_shredding.cpp b/src/duckdb/src/storage/table/variant/variant_shredding.cpp index 9a1b50d14..51a5b6a26 100644 --- a/src/duckdb/src/storage/table/variant/variant_shredding.cpp +++ b/src/duckdb/src/storage/table/variant/variant_shredding.cpp @@ -439,12 +439,17 @@ bool VariantShreddingStats::GetShreddedTypeInternal(const VariantColumnStatsData bool fully_consistent = !force_partial && max_count == total_value_count; if (type_index == static_cast(VariantLogicalType::OBJECT)) { child_list_t child_types; + case_insensitive_string_set_t field_names; for (auto &entry : column.field_stats) { auto &child_column = GetColumnStats(entry.second); if (entry.first.empty()) { //! Do not include empty field names in the shredded type! continue; } + if (!field_names.emplace(entry.first).second) { + //! STRUCT field names are case-insensitive, but VARIANT object keys are case-sensitive. + return false; + } LogicalType child_type; if (GetShreddedTypeInternal(child_column, child_type, total_value_count, force_partial)) { child_types.emplace_back(entry.first, child_type); @@ -829,6 +834,7 @@ void VariantColumnData::DebugShred(Vector &variant, idx_t count) { Vector materialized(LogicalType::VARIANT(), count); VectorOperations::Copy(variant, materialized, count, 0, 0); + FlatVector::SetSize(materialized, count); variant.Reference(materialized); //! Derive the shredding schema from the *first* value only - subsequent values that don't match it diff --git a/src/duckdb/src/storage/table/variant_column_data.cpp b/src/duckdb/src/storage/table/variant_column_data.cpp index 5c2af59ec..f1ef92187 100644 --- a/src/duckdb/src/storage/table/variant_column_data.cpp +++ b/src/duckdb/src/storage/table/variant_column_data.cpp @@ -141,6 +141,10 @@ bool VariantColumnData::PushdownShreddedFieldExtract(const StorageIndex &variant return false; } if (root_type.id() != LogicalTypeId::INVALID) { + if (!VariantUtils::ShreddedCastMatchesVariantCast(shredded_stats.get().GetType(), root_type)) { + //! The cast has to be applied to the VARIANT + return false; + } column_index.SetPushdownExtractType(shredded.type, root_type); } diff --git a/src/duckdb/src/storage/table_index_list.cpp b/src/duckdb/src/storage/table_index_list.cpp index a83fc23e8..4fa432af1 100644 --- a/src/duckdb/src/storage/table_index_list.cpp +++ b/src/duckdb/src/storage/table_index_list.cpp @@ -90,10 +90,12 @@ TableIndexList::~TableIndexList() { } } -shared_ptr TableIndexList::AddIndex(unique_ptr index, const ConstraintCheckMode check_mode) { +shared_ptr TableIndexList::AddIndex(unique_ptr index, idx_t index_oid, + const ConstraintCheckMode check_mode) { D_ASSERT(index); + D_ASSERT(index_oid != DConstants::INVALID_INDEX); annotated_lock_guard lock(index_entries_lock); - auto index_entry = make_shared_ptr(std::move(index), check_mode); + auto index_entry = make_shared_ptr(std::move(index), index_oid, check_mode); if (index_entry->GetBindState() != IndexBindState::BOUND) { unbound_count++; } @@ -133,7 +135,7 @@ ErrorData TableIndexList::Append(optional_ptr delete_indexes, Da for (const auto &entry : index_entries) { shared_ptr delete_entry; if (delete_indexes && entry->IsUnique()) { - delete_entry = delete_indexes->FindEntry(entry->GetName()); + delete_entry = delete_indexes->FindEntry(*entry); } error = entry->Append(chunk, row_ids, delete_entry, append_mode, active_checkpoint); if (error.HasError()) { @@ -178,13 +180,13 @@ void TableIndexList::RemoveFromIndexes(DataChunk &chunk, Vector &row_ids, const } } -void TableIndexList::RemoveIndex(const Identifier &name) { +void TableIndexList::RemoveIndex(idx_t index_oid) { shared_ptr removed_entry; { annotated_lock_guard lock(index_entries_lock); for (idx_t i = 0; i < index_entries.size(); i++) { auto &entry = index_entries[i]; - if (entry->GetName() != name) { + if (entry->GetIndexOid() != index_oid) { continue; } if (entry->GetBindState() != IndexBindState::BOUND) { @@ -218,7 +220,7 @@ void TableIndexList::VerifyUniqueIndexes(optional_ptr dele if (!entry->IsUnique() || entry->IsDeferred() || entry->GetIndexType() != ART::TYPE_NAME) { continue; } - auto delete_entry = delete_indexes ? delete_indexes->FindEntry(entry->GetName()) : nullptr; + auto delete_entry = delete_indexes ? delete_indexes->FindEntry(*entry) : nullptr; entry->VerifyAppend(delete_entry, chunk, nullptr); } return; @@ -233,9 +235,8 @@ void TableIndexList::VerifyUniqueIndexes(optional_ptr dele !conflict_info.ConflictTargetMatches(index_info.is_unique, index_info.column_set)) { continue; } - auto index_name = entry->GetName(); - auto delete_entry = delete_indexes ? delete_indexes->FindEntry(index_name) : nullptr; - manager->AddIndex(entry, index_name, std::move(delete_entry)); + auto delete_entry = delete_indexes ? delete_indexes->FindEntry(*entry) : nullptr; + manager->AddIndex(entry, std::move(delete_entry)); } // Verify indexes matching the conflict target. @@ -250,10 +251,10 @@ void TableIndexList::VerifyUniqueIndexes(optional_ptr dele manager->SetMode(ConflictManagerMode::THROW); for (const auto &entry : index_entries) { if (!entry->IsUnique() || entry->IsDeferred() || entry->GetIndexType() != ART::TYPE_NAME || - manager->IndexMatches(entry->GetName())) { + manager->IndexMatches(entry)) { continue; } - auto delete_entry = delete_indexes ? delete_indexes->FindEntry(entry->GetName()) : nullptr; + auto delete_entry = delete_indexes ? delete_indexes->FindEntry(*entry) : nullptr; entry->VerifyAppend(delete_entry, chunk, manager); } } @@ -328,33 +329,35 @@ bool TableIndexList::AllIndexesBoundOfType(const string &index_type) const { return true; } -bool TableIndexList::NameIsUnique(const string &name) const { +bool TableIndexList::HasBufferedReplays() const { annotated_lock_guard lock(index_entries_lock); - // Only covers PK, FK, and UNIQUE indexes. - // is_unique also covers primary-key indexes. for (const auto &entry : index_entries) { - auto index_info = entry->GetStorageInfo(); - if ((index_info.is_unique || index_info.is_foreign) && entry->GetName() == name) { - return false; + if (entry->HasBufferedReplays()) { + return true; } } - return true; + return false; } -bool TableIndexList::Contains(const Identifier &name) const { +shared_ptr TableIndexList::FindEntry(const Identifier &name) const { annotated_lock_guard lock(index_entries_lock); for (const auto &entry : index_entries) { - if (entry->GetName() == name) { - return true; + if (entry->GetName() != name) { + continue; } + if (entry->GetBindState() != IndexBindState::BOUND) { + throw InternalException("TableIndexList::FindEntry cannot return an unbound index"); + } + return entry; } - return false; + return nullptr; } -shared_ptr TableIndexList::FindEntry(const Identifier &name) const { +shared_ptr TableIndexList::FindEntry(const IndexEntry &index) const { + auto index_oid = index.GetIndexOid(); annotated_lock_guard lock(index_entries_lock); for (const auto &entry : index_entries) { - if (entry->GetName() != name) { + if (entry->GetIndexOid() != index_oid) { continue; } if (entry->GetBindState() != IndexBindState::BOUND) { @@ -376,11 +379,9 @@ void TableIndexList::Bind(ClientContext &context, DataTableInfo &table_info, con // Get the table from the catalog, so we can add it to the binder. auto &catalog = table_info.GetDB().GetCatalog(); - // the table can live in a nested schema - qualify it with the full schema path - auto schema_path = table_info.GetSchemaPath(); - schema_path.insert(schema_path.begin(), catalog.GetName()); - auto &table_entry = - catalog.GetEntry(context, QualifiedName(std::move(schema_path), table_info.GetTableName())); + auto &table_entry = catalog.GetEntry( + context, + QualifiedName::FromCatalogSchema(catalog.GetName(), table_info.GetSchemaPath(), table_info.GetTableName())); auto &table = table_entry.Cast(); vector column_types; @@ -484,7 +485,7 @@ void TableIndexList::VerifyForeignKey(optional_ptr delete_ throw InternalException("TableIndexList::VerifyForeignKey failed to find foreign key index"); } - auto delete_entry = delete_indexes ? delete_indexes->FindEntry(entry->GetName()) : nullptr; + auto delete_entry = delete_indexes ? delete_indexes->FindEntry(*entry) : nullptr; entry->VerifyForeignKey(delete_entry, chunk, conflict_manager); } @@ -549,10 +550,10 @@ unordered_set TableIndexList::GetUniqueIndexColumns() const { return result; } -unique_ptr TableIndexList::SerializeToWAL(const Identifier &name, const StorageVersion version) { +unique_ptr TableIndexList::SerializeToWAL(idx_t index_oid, const StorageVersion version) { annotated_lock_guard lock(index_entries_lock); for (const auto &entry : index_entries) { - if (entry->GetName() == name) { + if (entry->GetIndexOid() == index_oid) { return make_uniq(entry->SerializeToWAL(version)); } } diff --git a/src/duckdb/src/storage/temporary_file_manager.cpp b/src/duckdb/src/storage/temporary_file_manager.cpp index c7a8d8eca..87d638399 100644 --- a/src/duckdb/src/storage/temporary_file_manager.cpp +++ b/src/duckdb/src/storage/temporary_file_manager.cpp @@ -102,10 +102,18 @@ bool TemporaryFileIndex::IsValid() const { //===--------------------------------------------------------------------===// // BlockIndexManager //===--------------------------------------------------------------------===// -BlockIndexManager::BlockIndexManager() : max_index(0), manager(nullptr) { +BlockIndexManager::BlockIndexManager() : max_index(0), manager(nullptr), encrypted(false) { } -BlockIndexManager::BlockIndexManager(TemporaryFileManager &manager) : max_index(0), manager(&manager) { +BlockIndexManager::BlockIndexManager(TemporaryFileManager &manager, bool encrypted) + : max_index(0), manager(&manager), encrypted(encrypted) { +} + +//! Physical stride of one block on disk: payload + (per-block encryption header, if encrypted). +static idx_t PhysicalBlockSize(const TemporaryBufferSize size, const bool encrypted) { + const auto base = size == TemporaryBufferSize::DEFAULT ? DEFAULT_BLOCK_ALLOC_SIZE : TemporaryBufferSizeToSize(size); + const auto header_size = encrypted ? DEFAULT_ENCRYPTED_BUFFER_HEADER_SIZE : 0; + return base + header_size; } idx_t BlockIndexManager::GetNewBlockIndex(const TemporaryBufferSize size) { @@ -168,8 +176,7 @@ idx_t BlockIndexManager::GetNewBlockIndexInternal(const TemporaryBufferSize size } void BlockIndexManager::SetMaxIndex(const idx_t new_index, const TemporaryBufferSize size) { - const auto temp_file_block_size = - size == TemporaryBufferSize::DEFAULT ? DEFAULT_BLOCK_ALLOC_SIZE : TemporaryBufferSizeToSize(size); + const auto temp_file_block_size = PhysicalBlockSize(size, encrypted); if (!manager) { max_index = new_index; } else { @@ -195,7 +202,7 @@ void BlockIndexManager::SetMaxIndex(const idx_t new_index, const TemporaryBuffer TemporaryFileHandle::TemporaryFileHandle(TemporaryFileManager &manager, TemporaryFileIdentifier identifier_p, idx_t temp_file_count) : db(manager.db), identifier(identifier_p), max_allowed_index((1 << temp_file_count) * MAX_ALLOWED_INDEX_BASE), - path(manager.CreateTemporaryFileName(identifier)), index_manager(manager) { + path(manager.CreateTemporaryFileName(identifier)), index_manager(manager, identifier.encrypted) { } TemporaryFileHandle::~TemporaryFileHandle() { @@ -333,7 +340,7 @@ TemporaryFileInformation TemporaryFileHandle::GetTemporaryFile() { TemporaryFileLock lock(file_lock); TemporaryFileInformation info; info.path = path; - info.size = GetPositionInFile(index_manager.GetUsedBlockCount()); + info.size = GetPositionInFile(index_manager.GetMaxIndex()); return info; } @@ -354,7 +361,7 @@ void TemporaryFileHandle::RemoveTempBlockIndex(TemporaryFileLock &, idx_t index) #ifndef WIN32 // this ended up causing issues when sorting auto max_index = index_manager.GetMaxIndex(); auto &fs = FileSystem::GetFileSystem(db); - fs.Truncate(*handle, NumericCast(GetPositionInFile(max_index + 1))); + fs.Truncate(*handle, NumericCast(GetPositionInFile(max_index))); #endif } } @@ -545,10 +552,17 @@ idx_t TemporaryFileManager::WriteTemporaryBuffer(QueryContext context, block_id_ D_ASSERT(handle); D_ASSERT(index.IsValid()); - handle->WriteTemporaryBuffer(context, buffer, index.block_index.GetIndex(), compressed_buffer); + try { + handle->WriteTemporaryBuffer(context, buffer, index.block_index.GetIndex(), compressed_buffer); + } catch (...) { + // the block was registered under the lock above - unregister it again on write failure + TemporaryFileManagerLock lock(manager_lock); + EraseUsedBlock(lock, block_id, *handle, index); + throw; + } compression_adaptivity.Update(compression_result.level, time_before); - return static_cast(compression_result.size); + return PhysicalBlockSize(compression_result.size, handle->IsEncrypted()); } TemporaryFileManager::CompressionResult @@ -666,7 +680,7 @@ unique_ptr TemporaryFileManager::ReadTemporaryBuffer(QueryContext co // If eviction size requested, set it to the size of the block (compressed size if applicable). if (eviction_size) { - *eviction_size = NumericCast(index.identifier.size); + *eviction_size = PhysicalBlockSize(index.identifier.size, index.identifier.encrypted); } auto &buffer_manager = BufferManager::GetBufferManager(db); @@ -688,7 +702,7 @@ idx_t TemporaryFileManager::DeleteTemporaryBuffer(block_id_t id) { auto index = GetTempBlockIndex(lock, id); auto handle = GetFileHandle(lock, index.identifier); EraseUsedBlock(lock, id, *handle, index); - return static_cast(index.identifier.size); + return PhysicalBlockSize(index.identifier.size, index.identifier.encrypted); } vector TemporaryFileManager::GetTemporaryFiles() { @@ -838,13 +852,16 @@ void TemporaryDirectoryHandle::ClaimOwner() { // count from zero. It is not locked, and nothing ever opens anybody else's: existing is its job. for (;;) { owner.instance = NextInstanceId(); + auto marker_path = fs.JoinPath(temp_directory, TemporaryOwnerMarkerName(owner)); try { - auto marker = fs.OpenFile(fs.JoinPath(temp_directory, TemporaryOwnerMarkerName(owner)), - FileFlags::FILE_FLAGS_WRITE | FileFlags::FILE_FLAGS_FILE_CREATE_NEW); + auto marker = fs.OpenFile(marker_path, FileFlags::FILE_FLAGS_WRITE | FileFlags::FILE_FLAGS_FILE_CREATE_NEW); marker->Close(); break; } catch (std::exception &) { // taken, by a live instance or by one that died holding it - either way not ours + if (!fs.FileExists(marker_path)) { + throw; + } } } file_prefix = TemporaryFilePrefix(owner); diff --git a/src/duckdb/src/storage/wal_replay.cpp b/src/duckdb/src/storage/wal_replay.cpp index a804fdd3a..ba547c4ea 100644 --- a/src/duckdb/src/storage/wal_replay.cpp +++ b/src/duckdb/src/storage/wal_replay.cpp @@ -20,6 +20,7 @@ #include "duckdb/main/config.hpp" #include "duckdb/main/connection.hpp" #include "duckdb/main/database.hpp" +#include "duckdb/main/database_manager.hpp" #include "duckdb/main/settings.hpp" #include "duckdb/parser/constraints/unique_constraint.hpp" #include "duckdb/parser/parsed_data/alter_table_info.hpp" @@ -64,9 +65,13 @@ class ReplayState { optional_idx checkpoint_end_position; optional_idx expected_checkpoint_id; WALReplayState replay_state; + //! Blocks referenced by ROW_GROUP_DATA entries, collected during the deserialize-only scan. They are marked as used + //! only once we have decided to replay the WAL, so if log replay is not needed, these blocks won't be + //! double-referenced. + vector row_group_blocks; struct ReplayIndexInfo { - ReplayIndexInfo(TableIndexList &index_list, unique_ptr index, idx_t table_oid, optional_idx index_oid, + ReplayIndexInfo(TableIndexList &index_list, unique_ptr index, idx_t table_oid, idx_t index_oid, ConstraintCheckMode check_mode) : index_list(index_list), index(std::move(index)), table_oid(table_oid), index_oid(index_oid), check_mode(check_mode) { @@ -76,10 +81,8 @@ class ReplayState { unique_ptr index; //! The oid of the table, used to uniquely identify the table (even after a rename). idx_t table_oid; - //! The oid of the index catalog entry, used to match a DROP INDEX in the same replayed transaction. - //! Invalid for constraint-backed indexes (i.e., UNIQUE): they have no separate catalog entry and cannot be - //! targeted by DROP INDEX. - optional_idx index_oid; + //! The oid of the index, used to match a DROP INDEX in the same replayed transaction. + idx_t index_oid; //! The check mode of the constraint enforced by the index. ConstraintCheckMode check_mode; }; @@ -89,17 +92,24 @@ class ReplayState { class WriteAheadLogDeserializer { public: WriteAheadLogDeserializer(ReplayState &state_p, BufferedFileReader &stream_p, bool deserialize_only = false) - : state(state_p), db(state.db), context(state.context), catalog(state.catalog), data(nullptr), - stream(nullptr, 0), deserializer(stream_p), deserialize_only(deserialize_only) { + : state(state_p), db(state.db), context(state.context), catalog(state.catalog), stream(nullptr, 0), + deserializer(stream_p), deserialize_only(deserialize_only) { deserializer.Set(catalog); } - WriteAheadLogDeserializer(ReplayState &state_p, unique_ptr data_p, idx_t size, - bool deserialize_only = false) + WriteAheadLogDeserializer(ReplayState &state_p, AllocatedData data_p, idx_t size, bool deserialize_only = false) : state(state_p), db(state.db), context(state.context), catalog(state.catalog), data(std::move(data_p)), stream(data.get(), size), deserializer(stream), deserialize_only(deserialize_only) { deserializer.Set(catalog); } + //! WAL entries are read into memory in full, so the buffer is tracked by the buffer manager + static AllocatedData AllocateEntryBuffer(ReplayState &state_p, idx_t size) { + if (size == 0) { + throw SerializationException("Corrupt WAL file: found an empty entry"); + } + return BufferAllocator::Get(state_p.db).Allocate(size); + } + static WriteAheadLogDeserializer GetEntryDeserializer(ReplayState &state_p, BufferedFileReader &stream, bool deserialize_only = false) { if (state_p.wal_version == 1) { @@ -122,7 +132,7 @@ class WriteAheadLogDeserializer { } // allocate a buffer and read data into the buffer - auto buffer = unique_ptr(new data_t[size]); + auto buffer = AllocateEntryBuffer(state_p, size); stream.ReadData(buffer.get(), size); // compute and verify the checksum @@ -182,8 +192,8 @@ class WriteAheadLogDeserializer { } //! Allocate a decryption buffer - auto buffer = unique_ptr(new data_t[ciphertext_size]); - auto out_buffer = unique_ptr(new data_t[size]); + auto buffer = AllocateEntryBuffer(state_p, ciphertext_size); + auto out_buffer = AllocateEntryBuffer(state_p, size); stream.ReadData(buffer.get(), ciphertext_size); encryption_state->Process(buffer.get(), ciphertext_size, buffer.get(), ciphertext_size); @@ -247,7 +257,7 @@ class WriteAheadLogDeserializer { void ReplayEntry(WALType wal_type); bool CanSkipPayload(WALType wal_type) const { // Unframed WAL v1 entries must still be deserialized; Replay* guards suppress their side effects. - if (!deserialize_only || !data) { + if (!deserialize_only || !data.get()) { return false; } D_ASSERT(state.wal_version == 2 || state.wal_version == 3); @@ -328,7 +338,7 @@ class WriteAheadLogDeserializer { AttachedDatabase &db; ClientContext &context; Catalog &catalog; - unique_ptr data; + AllocatedData data; MemoryStream stream; BinaryDeserializer deserializer; bool deserialize_only; @@ -483,6 +493,7 @@ unique_ptr WriteAheadLogReplayer::ReplayLog(unique_ptr WriteAheadLogReplayer::ReplayLog(unique_ptr WriteAheadLogReplayer::ReplayLog(unique_ptr checkpoint_handle; bool truncate_failed_checkpoint_marker = false; // A serialization error can leave a partially deserialized checkpoint marker in the replay state. Only reconcile @@ -610,6 +628,22 @@ unique_ptr WriteAheadLogReplayer::ReplayLog(unique_ptr(fs, std::move(main_handle)); } + + // Now we have decided to replay this WAL, mark the blocks referenced by ROW_GROUP_DATA entries as used. + // Notice, this must happen before replay, because replaying earlier entries can allocate blocks; without the marks, + // those allocations could hand out blocks that later entries reference. + auto &block_manager = storage_manager.GetBlockManager(); + for (auto &block_id : checkpoint_state.row_group_blocks) { + block_manager.MarkBlockAsUsed(block_id); + } + + // If there are no committed transactions in the WAL, rollback and truncate. + if (last_wal_flush_end == 0) { + con.Rollback(); + return make_uniq(storage_manager, wal_path, idx_t(0), + WALInitState::UNINITIALIZED_REQUIRES_TRUNCATE); + } + // we need to recover from the WAL: actually set up the replay state ReplayState state(database, *con.context, replay_state); @@ -623,7 +657,7 @@ unique_ptr WriteAheadLogReplayer::ReplayLog(unique_ptr WriteAheadLogReplayer::ReplayLog(unique_ptr= last_wal_flush_end) { // we finished reading the file: break - all_succeeded = true; + all_succeeded = wal_reader.Finished(); break; } con.BeginTransaction(); @@ -816,16 +850,9 @@ void WriteAheadLogDeserializer::ReplayVersion() { } } -//! Qualify a name stored in the WAL as [schema_path..., name] (i.e. without a catalog component) with the catalog we -//! are replaying into, so nested schemas can be navigated. Do not use WithCatalog() here: that treats the leading -//! component of a 3-element path as a catalog, which would drop the outermost schema of a nested path. +//! WAL names carry schema paths without a catalog component. static QualifiedName ReplayEntryName(Catalog &catalog, const QualifiedName &entry_name) { - vector path; - path.push_back(catalog.GetName()); - for (idx_t i = 0; i + 1 < entry_name.Path().size(); i++) { - path.push_back(entry_name.Path()[i]); - } - return QualifiedName(std::move(path), entry_name.Name()); + return QualifiedName::FromCatalogSchema(catalog.GetName(), entry_name.Parent().Path(), entry_name.Name()); } //! Re-qualify a serialized [catalog, schema_path..., name] entry name (as carried by a CreateInfo) for the catalog it @@ -860,16 +887,7 @@ void WriteAheadLogDeserializer::ReplayDropTable() { DropInfo info; info.type = CatalogType::TABLE_ENTRY; - // build the DropInfo path [catalog, schema_path..., name]; the qualified name's path is [schema_path..., name] - // (older WALs that only stored the immediate schema + table name are folded into it during deserialization) - vector path; - path.push_back(catalog.GetName()); - for (auto &component : entry.qualified_name.Path()) { - path.push_back(component); - } - Identifier table_name = std::move(path.back()); - path.pop_back(); - info.SetQualifiedName(QualifiedName(std::move(path), std::move(table_name))); + info.SetQualifiedName(ReplayEntryName(catalog, entry.qualified_name)); if (DeserializeOnly()) { return; } @@ -997,10 +1015,11 @@ void WriteAheadLogDeserializer::ReplayAlter() { auto index_instance = index_type->create_instance(input); auto &table_index_list = storage.GetDataTableInfo()->GetIndexes(); - state.replay_index_infos.emplace_back(table_index_list, std::move(index_instance), table.oid, - /*index_oid=*/optional_idx(), unique_info.check_mode); - + auto index_oid = DatabaseManager::Get(context).NextOid(); + unique_info.SetBackingIndexOid(index_oid); catalog.Alter(context, alter_info); + state.replay_index_infos.emplace_back(table_index_list, std::move(index_instance), table.oid, index_oid, + unique_info.check_mode); } //===--------------------------------------------------------------------===// @@ -1032,19 +1051,12 @@ void WriteAheadLogDeserializer::ReplayDropView() { void WriteAheadLogDeserializer::ReplayCreateSchema() { auto entry = WALCreateSchema::Deserialize(deserializer); CreateSchemaInfo info; - // build the CreateSchemaInfo path [catalog, parent schemas..., new schema, ] - vector path; - path.push_back(catalog.GetName()); - if (!entry.qualified_name.Path().empty()) { - // v2.0.0+: the qualified name's path is [parent schemas..., new schema] - for (auto &component : entry.qualified_name.Path()) { - path.push_back(component); - } - } else { - // legacy: only the (top-level) schema name was serialized - path.push_back(std::move(entry.schema)); + auto schema_path = entry.qualified_name.Path(); + if (schema_path.empty()) { + // Legacy WALs only store a top-level schema name. + schema_path.push_back(std::move(entry.schema)); } - info.SetQualifiedName(QualifiedName(std::move(path), Identifier())); + info.SetQualifiedName(QualifiedName::FromCatalogSchema(catalog.GetName(), std::move(schema_path), Identifier())); if (DeserializeOnly()) { return; } @@ -1056,22 +1068,11 @@ void WriteAheadLogDeserializer::ReplayDropSchema() { auto entry = WALDropSchema::Deserialize(deserializer); DropInfo info; info.type = CatalogType::SCHEMA_ENTRY; - // build the DropInfo path [catalog, parent schemas..., schema] with the schema name in the name slot - vector path; - path.push_back(catalog.GetName()); - Identifier schema_name; - if (!entry.qualified_name.Path().empty()) { - // v2.0.0+: the qualified name's path is [parent schemas..., schema] - auto &qpath = entry.qualified_name.Path(); - for (idx_t i = 0; i + 1 < qpath.size(); i++) { - path.push_back(qpath[i]); - } - schema_name = qpath.back(); - } else { - // legacy: only the (top-level) schema name was serialized - schema_name = std::move(entry.schema); - } - info.SetQualifiedName(QualifiedName(std::move(path), std::move(schema_name))); + auto schema_name = + entry.qualified_name.Path().empty() ? QualifiedName(std::move(entry.schema)) : std::move(entry.qualified_name); + auto path = schema_name.Path(); + path.insert(path.begin(), catalog.GetName()); + info.SetQualifiedName(QualifiedName::FromPath(std::move(path))); if (DeserializeOnly()) { return; } @@ -1180,6 +1181,11 @@ void WriteAheadLogDeserializer::ReplaySequenceValue() { // fetch the sequence from the catalog auto &seq = catalog.GetEntry(context, ReplayEntryName(catalog, entry.qualified_name)); seq.ReplayValue(entry.usage_count, entry.counter, entry.last_value); + auto &storage_manager = db.GetStorageManager(); + if (storage_manager.HasStorageVersion() && + StorageManager::IsPriorToVersion(StorageVersion::V2_0_0, storage_manager.GetStorageVersion())) { + seq.DeriveLastValue(); + } } //===--------------------------------------------------------------------===// @@ -1295,13 +1301,7 @@ void WriteAheadLogDeserializer::ReplayUseTable() { if (DeserializeOnly()) { return; } - // the qualified name holds the (possibly nested) schema path followed by the table name - prepend the catalog - auto path = entry.qualified_name.Path(); - auto table_name = std::move(path.back()); - path.pop_back(); - path.insert(path.begin(), catalog.GetName()); - state.current_table = - &catalog.GetEntry(context, QualifiedName(std::move(path), std::move(table_name))); + state.current_table = &catalog.GetEntry(context, ReplayEntryName(catalog, entry.qualified_name)); } void WriteAheadLogDeserializer::ReplayInsert() { @@ -1311,7 +1311,7 @@ void WriteAheadLogDeserializer::ReplayInsert() { return; } if (!state.current_table) { - throw InternalException("Corrupt WAL: insert without table"); + throw DataCorruptionException("Corrupt WAL: insert without table"); } // Append to the current table without constraint verification. @@ -1330,16 +1330,13 @@ void WriteAheadLogDeserializer::ReplayRowGroupData() { deserializer.Unset(); deserializer.Unset(); if (DeserializeOnly()) { - // label blocks in data as used - they will be used after the WAL replay is finished - // we need to do this during the deserialization phase to ensure the blocks will not be overwritten - // by previous deserialization steps for (auto &block_id : data.GetBlockIds()) { - block_manager.MarkBlockAsUsed(block_id); + state.row_group_blocks.push_back(block_id); } return; } if (!state.current_table) { - throw InternalException("Corrupt WAL: insert without table"); + throw DataCorruptionException("Corrupt WAL: insert without table"); } auto &storage = state.current_table->GetStorage(); auto &table_info = storage.GetDataTableInfo(); @@ -1379,7 +1376,7 @@ void WriteAheadLogDeserializer::ReplayDelete() { return; } if (!state.current_table) { - throw SerializationException("delete without a table"); + throw DataCorruptionException("Corrupt WAL: delete without table"); } D_ASSERT(chunk.ColumnCount() == 1 && chunk.data[0].GetType() == LogicalType::ROW_TYPE); @@ -1392,7 +1389,7 @@ void WriteAheadLogDeserializer::ReplayDelete() { auto next_row_id = storage.GetNextRowId(); for (idx_t i = 0; i < chunk.size(); i++) { if (source_ids[i] >= UnsafeNumericCast(next_row_id)) { - throw SerializationException("invalid row ID delete in WAL"); + throw DataCorruptionException("Corrupt WAL: row ID for delete out of bounds"); } } TableDeleteState delete_state; @@ -1409,11 +1406,18 @@ void WriteAheadLogDeserializer::ReplayUpdate() { return; } if (!state.current_table) { - throw InternalException("Corrupt WAL: update without table"); + throw DataCorruptionException("Corrupt WAL: update without table"); } - if (column_path[0] >= state.current_table->GetColumns().PhysicalColumnCount()) { - throw InternalException("Corrupt WAL: column index for update out of bounds"); + if (column_path.empty() || column_path[0] >= state.current_table->GetColumns().PhysicalColumnCount()) { + throw DataCorruptionException("Corrupt WAL: column index for update out of bounds"); + } + if (chunk.ColumnCount() != 2 || chunk.data[1].GetType() != LogicalType::ROW_TYPE) { + throw DataCorruptionException("Corrupt WAL: update must contain the updated values and the row ids"); + } + auto &column_type = state.current_table->GetColumns().GetColumn(PhysicalIndex(column_path[0])).Type(); + if (column_path.size() == 1 && chunk.data[0].GetType() != column_type) { + throw DataCorruptionException("Corrupt WAL: update type does not match the column type"); } // remove the row id vector from the chunk diff --git a/src/duckdb/src/storage/write_ahead_log.cpp b/src/duckdb/src/storage/write_ahead_log.cpp index 2abd21975..9a976cdb7 100644 --- a/src/duckdb/src/storage/write_ahead_log.cpp +++ b/src/duckdb/src/storage/write_ahead_log.cpp @@ -16,7 +16,6 @@ #include "duckdb/common/encryption_key_manager.hpp" #include "duckdb/common/serializer/binary_serializer.hpp" #include "duckdb/common/serializer/memory_stream.hpp" -#include "duckdb/parser/constraints/unique_constraint.hpp" #include "duckdb/parser/parsed_data/alter_table_info.hpp" #include "duckdb/storage/single_file_block_manager.hpp" #include "duckdb/storage/storage_manager.hpp" @@ -25,7 +24,6 @@ #include "duckdb/storage/data_table.hpp" #include "duckdb/storage/wal_entry.hpp" #include "duckdb/main/attached_database.hpp" -#include "duckdb/main/database.hpp" namespace duckdb { @@ -101,7 +99,7 @@ bool WriteAheadLog::Initialized() const { //===--------------------------------------------------------------------===// class ChecksumWriter : public WriteStream { public: - explicit ChecksumWriter(WriteAheadLog &wal) : wal(wal), memory_stream(Allocator::Get(wal.GetDatabase())) { + explicit ChecksumWriter(WriteAheadLog &wal) : wal(wal), memory_stream(BufferAllocator::Get(wal.GetDatabase())) { } void WriteData(const_data_ptr_t buffer, idx_t write_size) override { @@ -156,7 +154,7 @@ class ChecksumWriter : public WriteStream { // temp buffer const idx_t ciphertext_size = size + sizeof(uint64_t); - std::unique_ptr temp_buf(new uint8_t[ciphertext_size]); + auto temp_buf = BufferAllocator::Get(db).Allocate(ciphertext_size); EncryptionNonce nonce(db.GetStorageManager().GetCipher(), db.GetStorageManager().GetEncryptionVersion()); EncryptionTag tag; @@ -321,14 +319,7 @@ void WriteAheadLog::WriteCreateSchema(const SchemaCatalogEntry &entry) { WriteAheadLogSerializer serializer(*this, WALType::CREATE_SCHEMA); // serialize the schema as a QualifiedName: parent schemas form the path, the schema name is the name. For storage // versions older than v2.0.0 (which only support top-level schemas) the legacy "schema" name field is written. - vector parent_schemas; - auto parent = entry.GetParentSchema(); - while (parent) { - parent_schemas.push_back(parent->name); - parent = parent->GetParentSchema(); - } - std::reverse(parent_schemas.begin(), parent_schemas.end()); - serializer.WriteEntry(WALCreateSchema {entry.name, QualifiedName(std::move(parent_schemas), entry.name)}); + serializer.WriteEntry(WALCreateSchema {entry.name, QualifiedName::FromPath(entry.GetSchemaPath())}); serializer.End(); } @@ -387,10 +378,9 @@ void WriteAheadLog::WriteDropTableMacro(const TableMacroCatalogEntry &entry) { // Indexes //===--------------------------------------------------------------------===// -void SerializeIndex(AttachedDatabase &db, WriteAheadLogSerializer &serializer, TableIndexList &list, - const Identifier &name) { +void SerializeIndex(AttachedDatabase &db, WriteAheadLogSerializer &serializer, TableIndexList &list, idx_t index_oid) { auto storage_version = db.GetStorageManager().GetStorageVersion(); - auto info = list.SerializeToWAL(name, storage_version); + auto info = list.SerializeToWAL(index_oid, storage_version); if (!info) { return; } @@ -412,7 +402,7 @@ void WriteAheadLog::WriteCreateIndex(const IndexCatalogEntry &entry) { auto &index_entry = entry.Cast(); auto &list = index_entry.GetDataTableInfo().GetIndexes(); auto &database = GetDatabase(); - SerializeIndex(database, serializer, list, index_entry.name); + SerializeIndex(database, serializer, list, index_entry.oid); serializer.End(); } @@ -475,14 +465,7 @@ void WriteAheadLog::WriteDropSchema(const SchemaCatalogEntry &entry) { WriteAheadLogSerializer serializer(*this, WALType::DROP_SCHEMA); // serialize the schema as a QualifiedName: parent schemas form the path, the schema name is the name. For storage // versions older than v2.0.0 (which only support top-level schemas) the legacy "schema" name field is written. - vector parent_schemas; - auto parent = entry.GetParentSchema(); - while (parent) { - parent_schemas.push_back(parent->name); - parent = parent->GetParentSchema(); - } - std::reverse(parent_schemas.begin(), parent_schemas.end()); - serializer.WriteEntry(WALDropSchema {entry.name, QualifiedName(std::move(parent_schemas), entry.name)}); + serializer.WriteEntry(WALDropSchema {entry.name, QualifiedName::FromPath(entry.GetSchemaPath())}); serializer.End(); } @@ -551,18 +534,15 @@ void WriteAheadLog::WriteAlter(CatalogEntry &entry, const AlterInfo &info) { return serializer.End(); } - auto &table_info = info.Cast(); - auto &constraint_info = table_info.Cast(); - auto &unique = constraint_info.constraint->Cast(); - - auto &table_entry = entry.Cast(); - auto &parent = table_entry.Parent().Cast(); - auto &parent_info = parent.GetStorage().GetDataTableInfo(); - auto &list = parent_info->GetIndexes(); - - auto name = unique.GetName(parent.name); - auto &database = GetDatabase(); - SerializeIndex(database, serializer, list, name); + auto &table = entry.Cast(); + auto &parent = entry.Parent().Cast(); + auto added_oids = parent.GetAddedUniqueIndexOids(table); + if (added_oids.size() != 1) { + throw InternalException("WriteAlter: expected one added UNIQUE constraint on table \"%s\", found %llu", + parent.name, added_oids.size()); + } + auto &list = parent.GetStorage().GetDataTableInfo()->GetIndexes(); + SerializeIndex(GetDatabase(), serializer, list, added_oids[0]); serializer.End(); } diff --git a/src/duckdb/src/transaction/commit_state.cpp b/src/duckdb/src/transaction/commit_state.cpp index c6a58198c..bf5d89452 100644 --- a/src/duckdb/src/transaction/commit_state.cpp +++ b/src/duckdb/src/transaction/commit_state.cpp @@ -39,8 +39,8 @@ void CommitDropState::DropBlock(block_id_t block_id) { dropped_block_ids.push_back(block_id); } -void CommitDropState::RemoveIndex(TableIndexList &indexes, Identifier name) { - pending_index_removals.push_back(PendingIndexRemoval {indexes, std::move(name)}); +void CommitDropState::RemoveIndex(TableIndexList &indexes, idx_t index_oid) { + pending_index_removals.push_back(PendingIndexRemoval {indexes, index_oid}); } void CommitDropState::FinalizeCommit() { @@ -53,7 +53,7 @@ void CommitDropState::FinalizeCommit() { D_ASSERT(block_manager || dropped_block_ids.empty()); for (auto &removal : pending_index_removals) { - removal.indexes.get().RemoveIndex(removal.name); + removal.indexes.get().RemoveIndex(removal.index_oid); } dropped_block_ids.clear(); pending_index_removals.clear(); diff --git a/src/duckdb/src/transaction/duck_transaction_manager.cpp b/src/duckdb/src/transaction/duck_transaction_manager.cpp index c961129c3..204517423 100644 --- a/src/duckdb/src/transaction/duck_transaction_manager.cpp +++ b/src/duckdb/src/transaction/duck_transaction_manager.cpp @@ -38,6 +38,7 @@ void DuckCleanupInfo::Cleanup() { for (auto &transaction : transactions) { if (transaction->awaiting_cleanup) { transaction->Cleanup(lowest_visibility_bound); + transaction->awaiting_cleanup = false; } } } @@ -323,18 +324,25 @@ void DuckTransactionManager::WaitForDurability() { void DuckTransactionManager::CleanupTransactions() { lock_guard c_lock(cleanup_lock); while (true) { - unique_ptr top_cleanup_info; + DuckCleanupInfo *top_cleanup_info = nullptr; { lock_guard q_lock(cleanup_queue_lock); if (cleanup_queue.empty()) { // all transactions have been cleaned up - done return; } - top_cleanup_info = std::move(cleanup_queue.front()); - cleanup_queue.pop(); + top_cleanup_info = cleanup_queue.front().get(); } - if (top_cleanup_info) { + try { top_cleanup_info->Cleanup(); + } catch (FatalException &) { + throw; + } catch (std::exception &) { + return; + } + { + lock_guard q_lock(cleanup_queue_lock); + cleanup_queue.pop(); } } } @@ -547,6 +555,10 @@ ErrorData DuckTransactionManager::CommitTransaction(ClientContext &context, Tran lock.reset(); } + // A failed commit rolls back inline, queueing teardowns as RollbackTransaction does. Drain here too, + // so the call that fills the queue is the one that empties it. + DatabaseManager::Get(db.GetDatabase()).DrainPendingTeardowns(); + // now perform a checkpoint if (1) we are able to checkpoint, and (2) the WAL has reached sufficient size to // checkpoint if (checkpoint_decision.can_checkpoint) { @@ -591,6 +603,10 @@ void DuckTransactionManager::RollbackTransaction(Transaction &transaction_p) { CleanupTransactions(); + // Run external-resource teardowns the rollback queued (see RollbackState): they execute SQL, so + // they can only run here, after the transaction lock is released. + DatabaseManager::Get(db.GetDatabase()).DrainPendingTeardowns(); + if (error.HasError()) { throw FatalException("Failed to rollback transaction. Cannot continue operation.\nError: %s", error.Message()); } diff --git a/src/duckdb/src/transaction/meta_transaction.cpp b/src/duckdb/src/transaction/meta_transaction.cpp index cefa8e730..d74a02b47 100644 --- a/src/duckdb/src/transaction/meta_transaction.cpp +++ b/src/duckdb/src/transaction/meta_transaction.cpp @@ -245,9 +245,10 @@ AttachedDatabase &MetaTransaction::UseDatabase(shared_ptr &dat if (entry == referenced_databases.end()) { auto used_entry = used_databases.emplace(db_ref.GetName(), db_ref); if (!used_entry.second) { - // return used_entry.first->second.get(); - throw InternalException( - "Database name %s was already used by a different database for this meta transaction", + // e.g. a database was detached and another one attached under the same name in this transaction + throw TransactionException( + "Database name %s was already used by a different database in this transaction - commit or roll back " + "the transaction before using the name for another database", db_ref.GetName()); } referenced_databases.emplace(reference(db_ref), database); @@ -256,7 +257,8 @@ AttachedDatabase &MetaTransaction::UseDatabase(shared_ptr &dat } void MetaTransaction::ModifyDatabase(AttachedDatabase &db, DatabaseModificationType modification) { - if (IsReadOnly()) { + // Temporary objects are private to the connection, so a read-only transaction may still write them + if (IsReadOnly() && !db.IsTemporary()) { throw TransactionException("Cannot write to database \"%s\" - transaction is launched in read-only mode", db.GetName()); } diff --git a/src/duckdb/src/transaction/rollback_state.cpp b/src/duckdb/src/transaction/rollback_state.cpp index e6bcbbc13..370587735 100644 --- a/src/duckdb/src/transaction/rollback_state.cpp +++ b/src/duckdb/src/transaction/rollback_state.cpp @@ -12,6 +12,7 @@ #include "duckdb/storage/table/update_segment.hpp" #include "duckdb/storage/table/row_version_manager.hpp" #include "duckdb/main/attached_database.hpp" +#include "duckdb/main/external_resources_manager.hpp" #include "duckdb/main/database_manager.hpp" namespace duckdb { @@ -48,7 +49,16 @@ void RollbackState::RollbackEntry(UndoFlags type, data_ptr_t data) { case UndoFlags::ATTACHED_DATABASE: { auto db = Load(data); auto &db_manager = DatabaseManager::Get(db->GetDatabase()); - db_manager.DetachInternal(db->name); + auto attached_db = db_manager.DetachInternal(db->name); + if (attached_db) { + // Teardown runs SQL, which cannot happen under the transaction lock: it would need a + // transaction on the same manager, and rollback may not fail. Extracting the deleter is a + // field move, safe here; the DatabaseManager drains the queue once rollback completes. + auto deleter = attached_db->ExtractDeleter(); + if (deleter) { + db_manager.AddPendingTeardown(std::move(deleter)); + } + } break; } case UndoFlags::SEQUENCE_VALUE: diff --git a/src/duckdb/src/transaction/transaction_context.cpp b/src/duckdb/src/transaction/transaction_context.cpp index 48a7300d7..dcace6909 100644 --- a/src/duckdb/src/transaction/transaction_context.cpp +++ b/src/duckdb/src/transaction/transaction_context.cpp @@ -114,9 +114,15 @@ void TransactionContext::Rollback(optional_ptr error) { } catch (std::exception &ex) { rollback_error = ErrorData(ex); } - // Notify any registered state of transaction rollback + // Notify any registered state of transaction rollback - all of them, also when one of them fails for (auto const &s : context.registered_state->States()) { - s->TransactionRollback(*transaction, context, error); + try { + s->TransactionRollback(*transaction, context, error); + } catch (std::exception &ex) { + if (!rollback_error.HasError()) { + rollback_error = ErrorData(ex); + } + } } if (rollback_error.HasError()) { rollback_error.Throw(); diff --git a/src/duckdb/src/transaction/undo_buffer.cpp b/src/duckdb/src/transaction/undo_buffer.cpp index 6dd647d2e..441f5666f 100644 --- a/src/duckdb/src/transaction/undo_buffer.cpp +++ b/src/duckdb/src/transaction/undo_buffer.cpp @@ -61,41 +61,25 @@ void UndoBuffer::IterateEntries(UndoBuffer::IteratorState &state, T &&callback) } template -void UndoBuffer::IterateEntries(UndoBuffer::IteratorState &state, UndoBuffer::IteratorState &end_state, T &&callback) { +void UndoBuffer::ReverseIterateEntries(UndoBuffer::IteratorState &end_state, T &&callback) { if (!end_state.started) { return; } - // iterate in insertion order: start with the tail - state.current = allocator.tail.get(); - while (state.current) { - state.handle = allocator.buffer_manager.Pin(state.current->block); - state.start = state.handle.GetDataMutable(); - state.end = state.current == end_state.current ? end_state.start : state.start + state.current->position; - while (state.start < state.end) { - auto type = Load(state.start); - state.start += sizeof(UndoFlags); - auto len = Load(state.start); - state.start += sizeof(uint32_t); - callback(type, state.start); - state.start += len; - } - if (state.current == end_state.current) { - // finished executing until the current end state - return; - } - state.current = state.current->prev; + + idx_t end_position = 0; + if (end_state.current) { + auto block_start = end_state.handle.GetDataMutable(); + D_ASSERT(end_state.start >= block_start); + end_position = NumericCast(end_state.start - block_start); + D_ASSERT(end_position <= end_state.current->position); } -} -template -void UndoBuffer::ReverseIterateEntries(T &&callback) { - // iterate in reverse insertion order: start with the head - auto current = allocator.head.get(); + // Start at the newest committed entry and iterate back to the oldest one. + auto current = end_state.current ? end_state.current : allocator.head.get(); while (current) { auto handle = allocator.buffer_manager.Pin(current->block); data_ptr_t start = handle.GetDataMutable(); - data_ptr_t end = start + current->position; - // create a vector with all nodes in this chunk + data_ptr_t end = start + (current == end_state.current ? end_position : current->position); vector> nodes; while (start < end) { auto type = Load(start); @@ -105,7 +89,6 @@ void UndoBuffer::ReverseIterateEntries(T &&callback) { nodes.emplace_back(type, start); start += len; } - // iterate over it in reverse order for (idx_t i = nodes.size(); i > 0; i--) { callback(nodes[i - 1].first, nodes[i - 1].second); } @@ -113,6 +96,13 @@ void UndoBuffer::ReverseIterateEntries(T &&callback) { } } +template +void UndoBuffer::ReverseIterateEntries(T &&callback) { + UndoBuffer::IteratorState state; + state.started = true; + ReverseIterateEntries(state, std::forward(callback)); +} + bool UndoBuffer::ChangesMade() { // we need to search for any index creation entries return allocator.head.get(); @@ -184,7 +174,14 @@ void UndoBuffer::Cleanup(VisibilityBound lowest_visibility_bound) { // transaction CleanupState state(transaction, lowest_visibility_bound, active_transaction_state); UndoBuffer::IteratorState iterator_state; - IterateEntries(iterator_state, [&](UndoFlags type, data_ptr_t data) { state.CleanupEntry(type, data); }); + idx_t entry_idx = 0; + IterateEntries(iterator_state, [&](UndoFlags type, data_ptr_t data) { + if (entry_idx++ < cleaned_up_entries) { + return; + } + state.CleanupEntry(type, data); + cleaned_up_entries++; + }); } void UndoBuffer::WriteToWAL(WriteAheadLog &wal, optional_ptr commit_state) { @@ -203,8 +200,7 @@ void UndoBuffer::Commit(UndoBuffer::IteratorState &iterator_state, CommitInfo &i void UndoBuffer::RevertCommit(UndoBuffer::IteratorState &end_state, transaction_t transaction_id) { CommitState state(transaction, transaction_id, active_transaction_state, CommitMode::REVERT_COMMIT); - UndoBuffer::IteratorState start_state; - IterateEntries(start_state, end_state, [&](UndoFlags type, data_ptr_t data) { state.RevertCommit(type, data); }); + ReverseIterateEntries(end_state, [&](UndoFlags type, data_ptr_t data) { state.RevertCommit(type, data); }); state.Verify(); } diff --git a/src/duckdb/third_party/fmt/include/fmt/format.h b/src/duckdb/third_party/fmt/include/fmt/format.h index b5ab10c1e..814efbcba 100644 --- a/src/duckdb/third_party/fmt/include/fmt/format.h +++ b/src/duckdb/third_party/fmt/include/fmt/format.h @@ -1697,6 +1697,8 @@ template class basic_writer { return; } int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; + // DuckDB: limit the precision, formatting is not interruptible and the result size is linear in the precision + if (precision > 1000000) FMT_THROW(duckdb::InvalidInputException("Precision of %d is too large, the maximum is 1000000", precision)); if (fspecs.format == float_format::exp) ++precision; if (const_check(std::is_same())) fspecs.binary32 = true; fspecs.use_grisu = use_grisu(); diff --git a/src/duckdb/third_party/fsst/fsst.h b/src/duckdb/third_party/fsst/fsst.h index 2951d980c..f1b19deba 100644 --- a/src/duckdb/third_party/fsst/fsst.h +++ b/src/duckdb/third_party/fsst/fsst.h @@ -138,7 +138,7 @@ duckdb_fsst_destroy(duckdb_fsst_encoder_t*); unsigned int /* OUT: bytes consumed (a symbol table was decoded), or one of the sentinels above on failure. */ duckdb_fsst_import( duckdb_fsst_decoder_t *decoder, /* IN: this symbol table will be overwritten. */ - unsigned char *buf, /* IN: pointer to a byte-buffer where duckdb_fsst_export() serialized this symbol table. */ + const unsigned char *buf, /* IN: pointer to a byte-buffer where duckdb_fsst_export() serialized this symbol table. */ size_t buf_size /* IN: number of readable bytes in buf; import never reads past this. */ ); @@ -162,7 +162,10 @@ duckdb_fsst_compress( unsigned char *strOut[] /* OUT: output string start pointers. Will all point into [output,output+size). */ ); -/* Decompress a single string, inlined for speed. */ +#define DUCKDB_FSST_DECOMPRESS_INVALID_INPUT ((size_t)-1) + +/* Decompress a single string, inlined for speed. An escape must be followed by a literal byte. */ +/* Returns DUCKDB_FSST_DECOMPRESS_INVALID_INPUT if that byte is missing. */ inline size_t /* OUT: bytesize of the decompressed string. If > size, the decoded output is truncated to size. */ duckdb_fsst_decompress( duckdb_fsst_decoder_t *decoder, /* IN: use this symbol table for compression. */ @@ -196,7 +199,11 @@ duckdb_fsst_decompress( DUCKDB_FSST_EXPLICIT_FALLTHROUGH; case 1: code = strIn[posIn++]; FSST_UNALIGNED_STORE(strOut+posOut, symbol[code]); posOut += len[code]; DUCKDB_FSST_EXPLICIT_FALLTHROUGH; - case 0: posIn+=2; strOut[posOut++] = strIn[posIn-1]; /* decompress an escaped byte */ + case 0: + if (posIn+1 >= lenIn) { + return DUCKDB_FSST_DECOMPRESS_INVALID_INPUT; + } + posIn+=2; strOut[posOut++] = strIn[posIn-1]; /* decompress an escaped byte */ } } } @@ -207,7 +214,10 @@ duckdb_fsst_decompress( code = strIn[posIn++]; FSST_UNALIGNED_STORE(strOut+posOut, symbol[code]); posOut += len[code]; if (strIn[posIn] != FSST_ESC) { code = strIn[posIn++]; FSST_UNALIGNED_STORE(strOut+posOut, symbol[code]); posOut += len[code]; - } else { + } else { + if (posIn+1 >= lenIn) { + return DUCKDB_FSST_DECOMPRESS_INVALID_INPUT; + } posIn += 2; strOut[posOut++] = strIn[posIn-1]; } } else { @@ -215,7 +225,11 @@ duckdb_fsst_decompress( } } if (posIn < lenIn) { // last code cannot be an escape - code = strIn[posIn++]; FSST_UNALIGNED_STORE(strOut+posOut, symbol[code]); posOut += len[code]; + code = strIn[posIn++]; + if (code == FSST_ESC) { + return DUCKDB_FSST_DECOMPRESS_INVALID_INPUT; + } + FSST_UNALIGNED_STORE(strOut+posOut, symbol[code]); posOut += len[code]; } } #else @@ -224,6 +238,9 @@ duckdb_fsst_decompress( FSST_UNALIGNED_STORE(strOut+posOut, symbol[code]); /* unaligned memory write */ posOut += len[code]; } else { + if (posIn == lenIn) { + return DUCKDB_FSST_DECOMPRESS_INVALID_INPUT; + } strOut[posOut] = strIn[posIn]; /* decompress an escaped byte */ posIn++; posOut++; } @@ -231,16 +248,19 @@ duckdb_fsst_decompress( #endif while (posIn < lenIn) if ((code = strIn[posIn++]) < FSST_ESC) { - size_t posWrite = posOut, endWrite = posOut + len[code]; - unsigned char* __restrict__ symbolPointer = ((unsigned char* __restrict__) &symbol[code]) - posWrite; + size_t posWrite = posOut, endWrite = posOut + len[code], symbolOffset = 0; + unsigned char* __restrict__ symbolPointer = (unsigned char* __restrict__) &symbol[code]; if ((posOut = endWrite) > size) endWrite = size; for(; posWrite < endWrite; posWrite++) /* only write if there is room */ - strOut[posWrite] = symbolPointer[posWrite]; + strOut[posWrite] = symbolPointer[symbolOffset++]; } else { + if (posIn == lenIn) { + return DUCKDB_FSST_DECOMPRESS_INVALID_INPUT; + } if (posOut < size) strOut[posOut] = strIn[posIn]; /* idem */ posIn++; posOut++; } - if (posOut >= size && (decoder->zeroTerminated&1)) strOut[size-1] = 0; + if (size && posOut >= size && (decoder->zeroTerminated&1)) strOut[size-1] = 0; return posOut; /* full size of decompressed string (could be >size, then the actually decompressed part) */ } diff --git a/src/duckdb/third_party/fsst/libfsst.cpp b/src/duckdb/third_party/fsst/libfsst.cpp index 08001b0b3..875fddcae 100644 --- a/src/duckdb/third_party/fsst/libfsst.cpp +++ b/src/duckdb/third_party/fsst/libfsst.cpp @@ -438,7 +438,7 @@ extern "C" u32 duckdb_fsst_export(duckdb_fsst_encoder_t *encoder, u8 *buf) { #define FSST_CORRUPT 32774747032022883 /* 7-byte number in little endian containing "corrupt" */ -extern "C" u32 duckdb_fsst_import(duckdb_fsst_decoder_t *decoder, u8 *buf, size_t buf_size) { +extern "C" u32 duckdb_fsst_import(duckdb_fsst_decoder_t *decoder, const u8 *buf, size_t buf_size) { u64 version = 0; u32 code, pos = 17; u8 lenHisto[8]; diff --git a/src/duckdb/third_party/httplib/httplib.hpp b/src/duckdb/third_party/httplib/httplib.hpp index a933c6336..5675d7611 100644 --- a/src/duckdb/third_party/httplib/httplib.hpp +++ b/src/duckdb/third_party/httplib/httplib.hpp @@ -194,14 +194,6 @@ #define CPPHTTPLIB_RECV_FLAGS 0 #endif -#ifndef CPPHTTPLIB_SEND_FLAGS -#ifdef MSG_NOSIGNAL -#define CPPHTTPLIB_SEND_FLAGS MSG_NOSIGNAL -#else -#define CPPHTTPLIB_SEND_FLAGS 0 -#endif -#endif - #ifndef CPPHTTPLIB_LISTEN_BACKLOG #define CPPHTTPLIB_LISTEN_BACKLOG 128 #endif @@ -329,6 +321,14 @@ using socket_t = int; #endif #endif //_WIN32 +#ifndef CPPHTTPLIB_SEND_FLAGS +#ifdef MSG_NOSIGNAL +#define CPPHTTPLIB_SEND_FLAGS MSG_NOSIGNAL +#else +#define CPPHTTPLIB_SEND_FLAGS 0 +#endif +#endif + #if defined(__APPLE__) #include #endif @@ -730,10 +730,11 @@ inline from_chars_result from_chars(const char *first, const char *last, } if (digit < 0 || digit >= base) { break; } - if (result > ((std::numeric_limits::max)() - digit) / base) { + if (result > ((std::numeric_limits::max)() - static_cast(digit)) / + static_cast(base)) { return {p, std::errc::result_out_of_range}; } - result = result * base + digit; + result = result * static_cast(base) + static_cast(digit); } if (p == first || (negative && p == first + 1)) { diff --git a/src/duckdb/third_party/miniz/miniz_wrapper.hpp b/src/duckdb/third_party/miniz/miniz_wrapper.hpp index a763bd22f..0c02a16a4 100644 --- a/src/duckdb/third_party/miniz/miniz_wrapper.hpp +++ b/src/duckdb/third_party/miniz/miniz_wrapper.hpp @@ -86,6 +86,9 @@ struct MiniZStream { mz_inflateEnd(&stream); // Update indices + if (stream.total_in > compressed_size || compressed_size - stream.total_in < GZIP_FOOTER_SIZE) { + FormatException("Failed to decompress GZIP block: incomplete GZIP member footer"); + } compressed_data += GZIP_FOOTER_SIZE + stream.total_in; compressed_size -= GZIP_FOOTER_SIZE + stream.total_in; out_data += stream.total_out; diff --git a/src/duckdb/third_party/parquet/parquet_types.cpp b/src/duckdb/third_party/parquet/parquet_types.cpp index a508a69f2..cc73b3a98 100644 --- a/src/duckdb/third_party/parquet/parquet_types.cpp +++ b/src/duckdb/third_party/parquet/parquet_types.cpp @@ -1317,7 +1317,8 @@ Statistics::Statistics() noexcept max_value(), min_value(), is_max_value_exact(0), - is_min_value_exact(0) { + is_min_value_exact(0), + nan_count(0) { } void Statistics::__set_max(const std::string& val) { @@ -1359,6 +1360,11 @@ void Statistics::__set_is_min_value_exact(const bool val) { this->is_min_value_exact = val; __isset.is_min_value_exact = true; } + +void Statistics::__set_nan_count(const int64_t val) { + this->nan_count = val; +__isset.nan_count = true; +} std::ostream& operator<<(std::ostream& out, const Statistics& obj) { obj.printTo(out); @@ -1451,6 +1457,14 @@ uint32_t Statistics::read(::apache::thrift::protocol::TProtocol* iprot) { xfer += iprot->skip(ftype); } break; + case 9: + if (ftype == ::apache::thrift::protocol::T_I64) { + xfer += iprot->readI64(this->nan_count); + this->__isset.nan_count = true; + } else { + xfer += iprot->skip(ftype); + } + break; default: xfer += iprot->skip(ftype); break; @@ -1508,6 +1522,11 @@ uint32_t Statistics::write(::apache::thrift::protocol::TProtocol* oprot) const { xfer += oprot->writeBool(this->is_min_value_exact); xfer += oprot->writeFieldEnd(); } + if (this->__isset.nan_count) { + xfer += oprot->writeFieldBegin("nan_count", ::apache::thrift::protocol::T_I64, 9); + xfer += oprot->writeI64(this->nan_count); + xfer += oprot->writeFieldEnd(); + } xfer += oprot->writeFieldStop(); xfer += oprot->writeStructEnd(); return xfer; @@ -1523,6 +1542,7 @@ void swap(Statistics &a, Statistics &b) { swap(a.min_value, b.min_value); swap(a.is_max_value_exact, b.is_max_value_exact); swap(a.is_min_value_exact, b.is_min_value_exact); + swap(a.nan_count, b.nan_count); swap(a.__isset, b.__isset); } @@ -1535,6 +1555,7 @@ Statistics::Statistics(const Statistics& other30) { min_value = other30.min_value; is_max_value_exact = other30.is_max_value_exact; is_min_value_exact = other30.is_min_value_exact; + nan_count = other30.nan_count; __isset = other30.__isset; } Statistics::Statistics(Statistics&& other31) noexcept { @@ -1546,6 +1567,7 @@ Statistics::Statistics(Statistics&& other31) noexcept { min_value = std::move(other31.min_value); is_max_value_exact = other31.is_max_value_exact; is_min_value_exact = other31.is_min_value_exact; + nan_count = other31.nan_count; __isset = other31.__isset; } Statistics& Statistics::operator=(const Statistics& other32) { @@ -1557,6 +1579,7 @@ Statistics& Statistics::operator=(const Statistics& other32) { min_value = other32.min_value; is_max_value_exact = other32.is_max_value_exact; is_min_value_exact = other32.is_min_value_exact; + nan_count = other32.nan_count; __isset = other32.__isset; return *this; } @@ -1569,6 +1592,7 @@ Statistics& Statistics::operator=(Statistics&& other33) noexcept { min_value = std::move(other33.min_value); is_max_value_exact = other33.is_max_value_exact; is_min_value_exact = other33.is_min_value_exact; + nan_count = other33.nan_count; __isset = other33.__isset; return *this; } @@ -1583,6 +1607,7 @@ void Statistics::printTo(std::ostream& out) const { out << ", " << "min_value="; (__isset.min_value ? (out << to_string(min_value)) : (out << "")); out << ", " << "is_max_value_exact="; (__isset.is_max_value_exact ? (out << to_string(is_max_value_exact)) : (out << "")); out << ", " << "is_min_value_exact="; (__isset.is_min_value_exact ? (out << to_string(is_min_value_exact)) : (out << "")); + out << ", " << "nan_count="; (__isset.nan_count ? (out << to_string(nan_count)) : (out << "")); out << ")"; } diff --git a/src/duckdb/third_party/parquet/parquet_types.h b/src/duckdb/third_party/parquet/parquet_types.h index 762d3533a..77f709ac6 100644 --- a/src/duckdb/third_party/parquet/parquet_types.h +++ b/src/duckdb/third_party/parquet/parquet_types.h @@ -677,7 +677,7 @@ void swap(GeospatialStatistics &a, GeospatialStatistics &b); std::ostream& operator<<(std::ostream& out, const GeospatialStatistics& obj); typedef struct _Statistics__isset { - _Statistics__isset() : max(false), min(false), null_count(false), distinct_count(false), max_value(false), min_value(false), is_max_value_exact(false), is_min_value_exact(false) {} + _Statistics__isset() : max(false), min(false), null_count(false), distinct_count(false), max_value(false), min_value(false), is_max_value_exact(false), is_min_value_exact(false), nan_count(false) {} bool max :1; bool min :1; bool null_count :1; @@ -686,6 +686,7 @@ typedef struct _Statistics__isset { bool min_value :1; bool is_max_value_exact :1; bool is_min_value_exact :1; + bool nan_count :1; } _Statistics__isset; /** @@ -752,6 +753,13 @@ class Statistics : public virtual ::apache::thrift::TBase { * If true, min_value is the actual minimum value for a column */ bool is_min_value_exact; + /** + * Count of NaN values in the column; only present if physical type is FLOAT + * or DOUBLE, or logical type is FLOAT16. + * If this field is not present, readers MUST assume NaNs may be present + * (i.e. MUST assume nan_count > 0 and MAY NOT assume nan_count == 0). + */ + int64_t nan_count; _Statistics__isset __isset; @@ -771,6 +779,8 @@ class Statistics : public virtual ::apache::thrift::TBase { void __set_is_min_value_exact(const bool val); + void __set_nan_count(const int64_t val); + uint32_t read(::apache::thrift::protocol::TProtocol* iprot) override; uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const override; diff --git a/src/duckdb/third_party/ska_sort/ska_sort.hpp b/src/duckdb/third_party/ska_sort/ska_sort.hpp index 3296a8c1b..5347ea7d7 100644 --- a/src/duckdb/third_party/ska_sort/ska_sort.hpp +++ b/src/duckdb/third_party/ska_sort/ska_sort.hpp @@ -1070,25 +1070,39 @@ struct TypeAt<0, First, More...> // { // }; -template -inline void StdSortFallback(It begin, It end, ExtractKey & extract_key) -{ - // LNK note that we use the full comparison (not just extracted key) here - static const auto comp = [&](const typename std::remove_reference::type & l, const typename std::remove_reference::type & r){ return l < r; }; - static const auto fallback = [&](const It &fb_begin, const It &fb_end) { - duckdb_pdqsort::pdqsort_branchless(fb_begin, fb_end, comp); - }; - duckdb_vergesort::vergesort(begin, end, comp, fallback); +// LNK note that we use the full comparison (not just extracted key) here +template +struct FullComparison +{ + typedef typename std::remove_reference())>::type value_type; + bool operator()(const value_type & l, const value_type & r) const + { + return l < r; + } +}; + +// Comparison sort of a radix bucket whose extracted keys are all equal: vergesort over the full comparison, with +// the sorted runs it finds merged by the caller's merge policy (see duckdb_vergesort::inplace_merge_policy), so +// that the merge scratch memory of the nested sort is the one the caller controls +template +inline void StdSortFallback(It begin, It end, ExtractKey &, const Merge & merge) +{ + FullComparison comp; + const auto fallback = [&](const It &fb_begin, const It &fb_end) { + duckdb_pdqsort::pdqsort_branchless(fb_begin, fb_end, comp); + }; + duckdb_vergesort::vergesort(begin, end, comp, fallback, merge); } template -inline bool StdSortIfLessThanThreshold(It begin, It end, size_t num_elements, ExtractKey & extract_key) +inline bool StdSortIfLessThanThreshold(It begin, It end, size_t num_elements, ExtractKey &) { if (num_elements <= 1) return true; if (num_elements >= StdSortThreshold) return false; - StdSortFallback(begin, end, extract_key); + // below vergesort's minimum size (128), where it would hand the whole range to pdqsort without merging anything + duckdb_pdqsort::pdqsort_branchless(begin, end, FullComparison()); return true; } @@ -1422,8 +1436,8 @@ struct SortStarter> template struct SortStarter { - template - static void sort(It begin, It end, size_t num_elements, ExtractKey & extract_key, void * next_sort_data = nullptr) + template + static void sort(It begin, It end, size_t num_elements, ExtractKey & extract_key, const Merge & merge) { if (StdSortIfLessThanThreshold(begin, end, num_elements, extract_key)) return; @@ -1433,22 +1447,26 @@ struct SortStarter // next_sort = nullptr; // LNK: different templating for DuckDB + // The sort data pointer that the radix passes carry along is the merge policy for the comparison sort of the + // buckets that the radix passes cannot order (the sub keys of DuckDB's sort keys never look at it) void (*next_sort)(It, It, size_t, ExtractKey &, void *) = nullptr; + void * next_sort_data = nullptr; if (extract_key.requires_next_sort) { - next_sort = [](It b, It e, size_t, ExtractKey& ek, void*) { - StdSortFallback(b, e, ek); + next_sort = [](It b, It e, size_t, ExtractKey& ek, void* merge_policy) { + StdSortFallback(b, e, ek, *static_cast(merge_policy)); }; + next_sort_data = const_cast(static_cast(&merge)); } InplaceSorter::sort(begin, end, num_elements, extract_key, next_sort, next_sort_data); } }; -template -void inplace_radix_sort(It begin, It end, ExtractKey & extract_key) +template +void inplace_radix_sort(It begin, It end, ExtractKey & extract_key, const Merge & merge) { typedef SubKey SubKeyType; - SortStarter::sort(begin, end, end - begin, extract_key); + SortStarter::sort(begin, end, end - begin, extract_key, merge); } struct IdentityFunctor @@ -1461,10 +1479,18 @@ struct IdentityFunctor }; } +// Sorts [begin, end) by the radix of extract_key; buckets whose extracted keys are equal (when extract_key requires +// a next sort) are sorted by the full comparison with vergesort, whose run merges go through the given merge policy +template +static void ska_sort(It begin, It end, ExtractKey && extract_key, const Merge & merge) +{ + detail::inplace_radix_sort<128, 1024>(begin, end, extract_key, merge); +} + template static void ska_sort(It begin, It end, ExtractKey && extract_key) { - detail::inplace_radix_sort<128, 1024>(begin, end, extract_key); + ska_sort(begin, end, extract_key, duckdb_vergesort::inplace_merge_policy()); } template diff --git a/src/duckdb/third_party/tdigest/t_digest.hpp b/src/duckdb/third_party/tdigest/t_digest.hpp index 58ba523e0..24d68fa2e 100644 --- a/src/duckdb/third_party/tdigest/t_digest.hpp +++ b/src/duckdb/third_party/tdigest/t_digest.hpp @@ -105,6 +105,42 @@ struct CentroidComparator { }; class TDigest { + static constexpr Index INITIAL_CAPACITY = 8; + static Index MinimumInt(Index a, Index b) { + return a < b ? a : b; + } + // Make room for `needed` elements, doubling the capacity rather than fitting exactly, up to the `full` + // size the buffer would have been given up front: a long run of small merges then reallocates a + // logarithmic number of times instead of once per merge, and never grows past the old fixed reservation. + template + static void Grow(VECTOR &vec, size_t needed, size_t full) { + if (needed <= vec.capacity()) { + return; + } + size_t target = vec.capacity() * 2; + if (target > full) { + target = full; + } + if (target < needed) { + target = needed; + } + vec.reserve(target); + } + + // Keep every buffer large enough for the next process() -- which appends processed_ to unprocessed_, + // rebuilds processed_ from the merged run and rebuilds cumulative_ -- so that process() itself never + // allocates. process() also runs at finalize time (compress(), quantile()), on a thread that may be + // finalizing several partitions holding states from the same arena concurrently: the arena a digest's + // buffers live in is the local hash table's, which the sink hands to the global state and which is not + // thread-safe. Growing here, in add()/merge(), keeps all allocation on the thread that owns the arena. + // A merged digest holds at most as many centroids as it was given, and at most maxProcessed_. + void reserveForProcess() { + const size_t total = processed_.size() + unprocessed_.size(); + Grow(unprocessed_, total, size_t(maxUnprocessed_ + maxProcessed_ + 1)); + const size_t merged = total < size_t(maxProcessed_) ? total : size_t(maxProcessed_); + Grow(processed_, merged, size_t(maxProcessed_)); + Grow(cumulative_, merged + 1, size_t(maxProcessed_ + 1)); + } class TDigestComparator { public: TDigestComparator() { @@ -129,9 +165,11 @@ class TDigest { : compression_(compression), maxProcessed_(processedSize(mergedSize, compression)), maxUnprocessed_(unprocessedSize(unmergedSize, compression)), processed_(allocator), unprocessed_(allocator), cumulative_(allocator) { - processed_.reserve(maxProcessed_); - unprocessed_.reserve(maxUnprocessed_ + maxProcessed_ + 1); - cumulative_.reserve(maxProcessed_ + 1); + // The buffers grow on demand: an aggregate state per group that only ever sees a handful of values must not + // pay for the full merge buffer (~1000 centroids with the default compression) up front. + processed_.reserve(MinimumInt(maxProcessed_, INITIAL_CAPACITY)); + unprocessed_.reserve(MinimumInt(maxUnprocessed_ + maxProcessed_ + 1, INITIAL_CAPACITY)); + cumulative_.reserve(MinimumInt(maxProcessed_ + 1, INITIAL_CAPACITY)); } TDigest(duckdb::arena_vector &&processed, duckdb::arena_vector &&unprocessed, Value compression, @@ -147,6 +185,7 @@ class TDigest { max_ = std::max(max_, (processed_.cend() - 1)->mean()); } updateCumulative(); + reserveForProcess(); } static Weight weight(duckdb::arena_vector ¢roids) noexcept { @@ -247,6 +286,7 @@ class TDigest { mergeProcessed(batch); mergeUnprocessed(batch); processIfNecessary(); + reserveForProcess(); batch.clear(); totalSize = 0; } @@ -417,6 +457,7 @@ class TDigest { unprocessed_.push_back(Centroid(x, w)); unprocessedWeight_ += w; processIfNecessary(); + reserveForProcess(); return true; } @@ -432,6 +473,7 @@ class TDigest { process(); } } + reserveForProcess(); } private: @@ -476,7 +518,7 @@ class TDigest { total += td->unprocessed_.size(); } - unprocessed_.reserve(total); + Grow(unprocessed_, total, size_t(maxUnprocessed_ + maxProcessed_ + 1)); for (auto &td : tdigests) { unprocessed_.insert(unprocessed_.end(), td->unprocessed_.cbegin(), td->unprocessed_.cend()); unprocessedWeight_ += td->unprocessedWeight_; @@ -536,7 +578,7 @@ class TDigest { void updateCumulative() { const auto n = processed_.size(); cumulative_.clear(); - cumulative_.reserve(n + 1); + Grow(cumulative_, n + 1, size_t(maxProcessed_ + 1)); auto previous = 0.0; for (Index i = 0; i < n; i++) { auto current = weight(i); diff --git a/src/duckdb/third_party/utf8proc/utf8proc_wrapper.cpp b/src/duckdb/third_party/utf8proc/utf8proc_wrapper.cpp index fcc4dd5e4..6d046d112 100644 --- a/src/duckdb/third_party/utf8proc/utf8proc_wrapper.cpp +++ b/src/duckdb/third_party/utf8proc/utf8proc_wrapper.cpp @@ -75,7 +75,18 @@ UnicodeType Utf8Proc::Analyze(const char *s, size_t len, UnicodeInvalidReason *i UnicodeType type = UnicodeType::ASCII; static constexpr uint64_t MASK = 0x8080808080808080U; - for (size_t i = 0; i < len;) { + size_t i = 0; + if (len >= 64) { + // long input: check 32 bytes at a time with a single branch while everything is ASCII + for (; i + 4 * sizeof(uint64_t) <= len; i += 4 * sizeof(uint64_t)) { + auto acc = Load(const_data_ptr_cast(s + i)) | Load(const_data_ptr_cast(s + i + 8)) | + Load(const_data_ptr_cast(s + i + 16)) | Load(const_data_ptr_cast(s + i + 24)); + if (acc & MASK) { + break; + } + } + } + for (; i < len;) { // Check 8 bytes at a time until we hit non-ASCII for (; i + sizeof(uint64_t) <= len; i += sizeof(uint64_t)) { if (Load(const_data_ptr_cast(s + i)) & MASK) { diff --git a/src/duckdb/third_party/vergesort/vergesort.h b/src/duckdb/third_party/vergesort/vergesort.h index 1d7275318..e1322da65 100644 --- a/src/duckdb/third_party/vergesort/vergesort.h +++ b/src/duckdb/third_party/vergesort/vergesort.h @@ -47,6 +47,19 @@ namespace duckdb_vergesort { + // Merge policy used for the final pairwise run merges: a callable merge(first, middle, last, compare) that + // merges the sorted ranges [first, middle) and [middle, last). The default is std::inplace_merge, which takes + // its temporary buffer from operator new; callers that want to control that memory pass their own policy. + struct inplace_merge_policy + { + template + void operator()(RandomAccessIterator first, RandomAccessIterator middle, + RandomAccessIterator last, Compare compare) const + { + std::inplace_merge(first, middle, last, compare); + } + }; + namespace detail { // In-place merge where [first, middle1), [middle1, middle2) @@ -178,9 +191,9 @@ namespace duckdb_vergesort } } - template + template void vergesort(RandomAccessIterator first, RandomAccessIterator last, - Compare compare, std::random_access_iterator_tag, Fallback fallback) + Compare compare, std::random_access_iterator_tag, Fallback fallback, Merge merge) { typedef typename std::iterator_traits::difference_type difference_type; difference_type dist = last - first; @@ -321,7 +334,7 @@ namespace duckdb_vergesort for (typename std::list::iterator it = runs.begin() ; it != runs.end() && it != detail::prev(runs.end()) ; ++it) { - std::inplace_merge(begin, *it, *detail::next(it), compare); + merge(begin, *it, *detail::next(it), compare); // Remove the middle iterator and advance it = runs.erase(it); @@ -331,12 +344,19 @@ namespace duckdb_vergesort } } + template + void vergesort(BidirectionalIterator first, BidirectionalIterator last, + Compare compare, Fallback fallback, Merge merge) + { + typedef typename std::iterator_traits::iterator_category category; + detail::vergesort(first, last, compare, category(), fallback, merge); + } + template void vergesort(BidirectionalIterator first, BidirectionalIterator last, Compare compare, Fallback fallback) { - typedef typename std::iterator_traits::iterator_category category; - detail::vergesort(first, last, compare, category(), fallback); + vergesort(first, last, compare, fallback, inplace_merge_policy()); } template diff --git a/src/duckdb/third_party/zstd/include/zstd/common/cpu.h b/src/duckdb/third_party/zstd/include/zstd/common/cpu.h index 892f9851d..94111d049 100644 --- a/src/duckdb/third_party/zstd/include/zstd/common/cpu.h +++ b/src/duckdb/third_party/zstd/include/zstd/common/cpu.h @@ -37,7 +37,7 @@ MEM_STATIC ZSTD_cpuid_t ZSTD_cpuid(void) { U32 f7b = 0; U32 f7c = 0; #if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86)) -#if !defined(__clang__) +#if !defined(_M_X64) || !defined(__clang__) || __clang_major__ >= 16 int reg[4]; __cpuid((int*)reg, 0); { diff --git a/src/duckdb/ub_extension_parquet_reader_variant.cpp b/src/duckdb/ub_extension_parquet_reader_variant.cpp deleted file mode 100644 index b0e0398b5..000000000 --- a/src/duckdb/ub_extension_parquet_reader_variant.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "extension/parquet/reader/variant/parquet_variant_iterator.cpp" - -#include "extension/parquet/reader/variant/parquet_variant_shredding.cpp" - -#include "extension/parquet/reader/variant/variant_binary_decoder.cpp" - diff --git a/src/duckdb/ub_src_common.cpp b/src/duckdb/ub_src_common.cpp index 89aaf7d6f..df2ce731c 100644 --- a/src/duckdb/ub_src_common.cpp +++ b/src/duckdb/ub_src_common.cpp @@ -42,8 +42,6 @@ #include "src/common/exception_format_value.cpp" -#include "src/common/extra_type_info.cpp" - #include "src/common/file_buffer.cpp" #include "src/common/file_system.cpp" @@ -64,6 +62,8 @@ #include "src/common/local_file_system.cpp" +#include "src/common/logical_type_info.cpp" + #include "src/common/memory_mapped_file.cpp" #include "src/common/memory_safety.cpp" diff --git a/src/duckdb/ub_src_common_arrow.cpp b/src/duckdb/ub_src_common_arrow.cpp index cb6d7fc39..dfa8b109e 100644 --- a/src/duckdb/ub_src_common_arrow.cpp +++ b/src/duckdb/ub_src_common_arrow.cpp @@ -2,19 +2,11 @@ #include "src/common/arrow/arrow_converter.cpp" -#include "src/common/arrow/arrow_merge_event.cpp" - -#include "src/common/arrow/arrow_query_result.cpp" +#include "src/common/arrow/arrow_format.cpp" #include "src/common/arrow/arrow_type_extension.cpp" -#include "src/common/arrow/arrow_util.cpp" - #include "src/common/arrow/arrow_wrapper.cpp" -#include "src/common/arrow/physical_arrow_batch_collector.cpp" - -#include "src/common/arrow/physical_arrow_collector.cpp" - #include "src/common/arrow/schema_metadata.cpp" diff --git a/src/duckdb/ub_src_common_progress_bar.cpp b/src/duckdb/ub_src_common_progress_bar.cpp index 286ece8c1..e3e21119d 100644 --- a/src/duckdb/ub_src_common_progress_bar.cpp +++ b/src/duckdb/ub_src_common_progress_bar.cpp @@ -2,5 +2,3 @@ #include "src/common/progress_bar/terminal_progress_bar_display.cpp" -#include "src/common/progress_bar/unscented_kalman_filter.cpp" - diff --git a/src/duckdb/ub_src_common_tree_renderer.cpp b/src/duckdb/ub_src_common_tree_renderer.cpp index bd24ab2e6..148bd6ffa 100644 --- a/src/duckdb/ub_src_common_tree_renderer.cpp +++ b/src/duckdb/ub_src_common_tree_renderer.cpp @@ -1,3 +1,5 @@ +#include "src/common/tree_renderer/compact_tree_renderer.cpp" + #include "src/common/tree_renderer/graphviz_tree_renderer.cpp" #include "src/common/tree_renderer/html_tree_renderer.cpp" diff --git a/src/duckdb/ub_src_common_types_variant.cpp b/src/duckdb/ub_src_common_types_variant.cpp index 31b61b8ee..dc76281e1 100644 --- a/src/duckdb/ub_src_common_types_variant.cpp +++ b/src/duckdb/ub_src_common_types_variant.cpp @@ -1,5 +1,11 @@ +#include "src/common/types/variant/parquet_variant_encoding.cpp" + +#include "src/common/types/variant/parquet_variant_iterator.cpp" + #include "src/common/types/variant/variant.cpp" +#include "src/common/types/variant/variant_binary_decoder.cpp" + #include "src/common/types/variant/variant_comparison.cpp" #include "src/common/types/variant/variant_iterator.cpp" diff --git a/src/duckdb/ub_src_execution.cpp b/src/duckdb/ub_src_execution.cpp index d839e44ca..8159c07dc 100644 --- a/src/duckdb/ub_src_execution.cpp +++ b/src/duckdb/ub_src_execution.cpp @@ -12,6 +12,8 @@ #include "src/execution/expression_executor_state.cpp" +#include "src/execution/ie_join_union.cpp" + #include "src/execution/join_hashtable.cpp" #include "src/execution/mark_join_row_comparison.cpp" @@ -22,6 +24,8 @@ #include "src/execution/physical_plan_generator.cpp" +#include "src/execution/radix_ht_adaptivity.cpp" + #include "src/execution/radix_partitioned_hashtable.cpp" #include "src/execution/row_id_deduplicator.cpp" diff --git a/src/duckdb/ub_src_execution_operator_csv_scanner_table_function.cpp b/src/duckdb/ub_src_execution_operator_csv_scanner_table_function.cpp index f3f192b22..d280bd1a3 100644 --- a/src/duckdb/ub_src_execution_operator_csv_scanner_table_function.cpp +++ b/src/duckdb/ub_src_execution_operator_csv_scanner_table_function.cpp @@ -1,6 +1,8 @@ #include "src/execution/operator/csv_scanner/table_function/csv_file_scanner.cpp" -#include "src/execution/operator/csv_scanner/table_function/csv_multi_file_info.cpp" +#include "src/execution/operator/csv_scanner/table_function/csv_schema_discovery.cpp" #include "src/execution/operator/csv_scanner/table_function/global_csv_state.cpp" +#include "src/execution/operator/csv_scanner/table_function/read_single_csv_file.cpp" + diff --git a/src/duckdb/ub_src_execution_operator_helper.cpp b/src/duckdb/ub_src_execution_operator_helper.cpp index 17bc4f159..42a29cf47 100644 --- a/src/duckdb/ub_src_execution_operator_helper.cpp +++ b/src/duckdb/ub_src_execution_operator_helper.cpp @@ -1,7 +1,5 @@ #include "src/execution/operator/helper/launch_external_resource.cpp" -#include "src/execution/operator/helper/physical_batch_collector.cpp" - #include "src/execution/operator/helper/physical_connect.cpp" #include "src/execution/operator/helper/physical_create_secret.cpp" diff --git a/src/duckdb/ub_src_execution_operator_persistent.cpp b/src/duckdb/ub_src_execution_operator_persistent.cpp index 436261897..0d30df08a 100644 --- a/src/duckdb/ub_src_execution_operator_persistent.cpp +++ b/src/duckdb/ub_src_execution_operator_persistent.cpp @@ -1,3 +1,5 @@ +#include "src/execution/operator/persistent/copy_batch_slicer.cpp" + #include "src/execution/operator/persistent/copy_output_lifecycle.cpp" #include "src/execution/operator/persistent/csv_rejects_table.cpp" diff --git a/src/duckdb/ub_src_function.cpp b/src/duckdb/ub_src_function.cpp index bf27c87fc..ab9f72a7f 100644 --- a/src/duckdb/ub_src_function.cpp +++ b/src/duckdb/ub_src_function.cpp @@ -4,6 +4,8 @@ #include "src/function/built_in_functions.cpp" +#include "src/function/builtin_function_lookup.cpp" + #include "src/function/cast_rules.cpp" #include "src/function/combine_types_rules.cpp" diff --git a/src/duckdb/ub_src_function_scalar_string.cpp b/src/duckdb/ub_src_function_scalar_string.cpp index bd11d492b..a265e244c 100644 --- a/src/duckdb/ub_src_function_scalar_string.cpp +++ b/src/duckdb/ub_src_function_scalar_string.cpp @@ -6,6 +6,8 @@ #include "src/function/scalar/string/contains.cpp" +#include "src/function/scalar/string/hive_partition_component.cpp" + #include "src/function/scalar/string/length.cpp" #include "src/function/scalar/string/like.cpp" diff --git a/src/duckdb/ub_src_main.cpp b/src/duckdb/ub_src_main.cpp index 894eae36c..132382d1a 100644 --- a/src/duckdb/ub_src_main.cpp +++ b/src/duckdb/ub_src_main.cpp @@ -2,12 +2,12 @@ #include "src/main/attached_database.cpp" -#include "src/main/chunk_scan_state.cpp" - #include "src/main/client_config.cpp" #include "src/main/client_context.cpp" +#include "src/main/client_context_execution.cpp" + #include "src/main/client_context_file_opener.cpp" #include "src/main/client_context_wrapper.cpp" @@ -46,11 +46,9 @@ #include "src/main/external_resources_manager.cpp" -#include "src/main/materialized_query_result.cpp" - #include "src/main/parse_iterator.cpp" -#include "src/main/pending_query_result.cpp" +#include "src/main/parser_context.cpp" #include "src/main/prepared_statement.cpp" @@ -60,13 +58,19 @@ #include "src/main/query_result.cpp" +#include "src/main/query_result_stream.cpp" + #include "src/main/relation.cpp" +#include "src/main/result_format.cpp" + #include "src/main/result_set_manager.cpp" -#include "src/main/statement_iterator.cpp" +#include "src/main/result_unit.cpp" -#include "src/main/stream_query_result.cpp" +#include "src/main/retained_result_collection.cpp" + +#include "src/main/statement_iterator.cpp" #include "src/main/user_settings.cpp" diff --git a/src/duckdb/ub_src_main_capi_v2.cpp b/src/duckdb/ub_src_main_capi_v2.cpp index 490f3b5a2..b94d045db 100644 --- a/src/duckdb/ub_src_main_capi_v2.cpp +++ b/src/duckdb/ub_src_main_capi_v2.cpp @@ -8,14 +8,16 @@ #include "src/main/capi/v2/capi_v2_column_data_collection.cpp" +#include "src/main/capi/v2/capi_v2_config.cpp" + #include "src/main/capi/v2/capi_v2_connection.cpp" +#include "src/main/capi/v2/capi_v2_custom_option.cpp" + #include "src/main/capi/v2/capi_v2_custom_type.cpp" #include "src/main/capi/v2/capi_v2_data_chunk.cpp" -#include "src/main/capi/v2/capi_v2_database.cpp" - #include "src/main/capi/v2/capi_v2_environment.cpp" #include "src/main/capi/v2/capi_v2_error.cpp" @@ -24,14 +26,24 @@ #include "src/main/capi/v2/capi_v2_extension.cpp" +#include "src/main/capi/v2/capi_v2_factory.cpp" + #include "src/main/capi/v2/capi_v2_file_system.cpp" +#include "src/main/capi/v2/capi_v2_func.cpp" + #include "src/main/capi/v2/capi_v2_func_aggregate.cpp" #include "src/main/capi/v2/capi_v2_func_cast.cpp" #include "src/main/capi/v2/capi_v2_func_copy.cpp" +#include "src/main/capi/v2/capi_v2_func_docs.cpp" + +#include "src/main/capi/v2/capi_v2_func_macro.cpp" + +#include "src/main/capi/v2/capi_v2_func_pragma.cpp" + #include "src/main/capi/v2/capi_v2_func_properties.cpp" #include "src/main/capi/v2/capi_v2_func_scalar.cpp" @@ -42,6 +54,10 @@ #include "src/main/capi/v2/capi_v2_identifier.cpp" +#include "src/main/capi/v2/capi_v2_instance.cpp" + +#include "src/main/capi/v2/capi_v2_log_sink.cpp" + #include "src/main/capi/v2/capi_v2_logging.cpp" #include "src/main/capi/v2/capi_v2_logical_type.cpp" @@ -62,6 +78,10 @@ #include "src/main/capi/v2/capi_v2_stubs.cpp" +#include "src/main/capi/v2/capi_v2_tokenizer.cpp" + +#include "src/main/capi/v2/capi_v2_utf8.cpp" + #include "src/main/capi/v2/capi_v2_value.cpp" #include "src/main/capi/v2/capi_v2_vector.cpp" diff --git a/src/duckdb/ub_src_main_chunk_scan_state.cpp b/src/duckdb/ub_src_main_chunk_scan_state.cpp deleted file mode 100644 index 9fb6cf644..000000000 --- a/src/duckdb/ub_src_main_chunk_scan_state.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "src/main/chunk_scan_state/batched_data_collection.cpp" - -#include "src/main/chunk_scan_state/query_result.cpp" - diff --git a/src/duckdb/ub_src_optimizer.cpp b/src/duckdb/ub_src_optimizer.cpp index 665569589..41becf040 100644 --- a/src/duckdb/ub_src_optimizer.cpp +++ b/src/duckdb/ub_src_optimizer.cpp @@ -8,8 +8,6 @@ #include "src/optimizer/build_probe_side_optimizer.cpp" -#include "src/optimizer/builtin_function_lookup.cpp" - #include "src/optimizer/column_binding_replacer.cpp" #include "src/optimizer/column_lifetime_analyzer.cpp" @@ -28,6 +26,10 @@ #include "src/optimizer/cte_inlining.cpp" +#include "src/optimizer/cte_join_filter_collector.cpp" + +#include "src/optimizer/cte_join_filter_pusher.cpp" + #include "src/optimizer/deliminator.cpp" #include "src/optimizer/empty_result_pullup.cpp" diff --git a/src/duckdb/ub_src_parallel.cpp b/src/duckdb/ub_src_parallel.cpp index 51cae357a..da29c5c6c 100644 --- a/src/duckdb/ub_src_parallel.cpp +++ b/src/duckdb/ub_src_parallel.cpp @@ -30,6 +30,8 @@ #include "src/parallel/pipeline_schedule.cpp" +#include "src/parallel/progress_verifier.cpp" + #include "src/parallel/scan_read_ahead.cpp" #include "src/parallel/task_executor.cpp" diff --git a/src/duckdb/ub_src_parser_parsed_data.cpp b/src/duckdb/ub_src_parser_parsed_data.cpp index b07703b51..2bbd7f779 100644 --- a/src/duckdb/ub_src_parser_parsed_data.cpp +++ b/src/duckdb/ub_src_parser_parsed_data.cpp @@ -4,6 +4,8 @@ #include "src/parser/parsed_data/alter_scalar_function_info.cpp" +#include "src/parser/parsed_data/alter_schema_info.cpp" + #include "src/parser/parsed_data/alter_table_function_info.cpp" #include "src/parser/parsed_data/alter_table_info.cpp" @@ -62,6 +64,8 @@ #include "src/parser/parsed_data/exported_table_data.cpp" +#include "src/parser/parsed_data/external_resource_options.cpp" + #include "src/parser/parsed_data/extra_drop_info.cpp" #include "src/parser/parsed_data/load_info.cpp" diff --git a/src/duckdb/ub_src_parser_peg.cpp b/src/duckdb/ub_src_parser_peg.cpp index e24b54ab1..aee36f71b 100644 --- a/src/duckdb/ub_src_parser_peg.cpp +++ b/src/duckdb/ub_src_parser_peg.cpp @@ -2,6 +2,8 @@ #include "src/parser/peg/compiled_grammar.cpp" +#include "src/parser/peg/dialect_extension.cpp" + #include "src/parser/peg/grammar_change.cpp" #include "src/parser/peg/keyword_map.cpp" @@ -10,14 +12,20 @@ #include "src/parser/peg/matcher_factory.cpp" +#include "src/parser/peg/matcher_first_set.cpp" + #include "src/parser/peg/matcher_process.cpp" #include "src/parser/peg/matcher_stack.cpp" +#include "src/parser/peg/operator_matcher.cpp" + #include "src/parser/peg/parsed_grammar.cpp" #include "src/parser/peg/parser_packrat.cpp" +#include "src/parser/peg/passthrough_dialect.cpp" + #include "src/parser/peg/peg_parser.cpp" #include "src/parser/peg/sql_formatter.cpp" diff --git a/src/duckdb/ub_src_parser_peg_keyword_helper.cpp b/src/duckdb/ub_src_parser_peg_keyword_helper.cpp index 90d3a504a..0e79c89e3 100644 --- a/src/duckdb/ub_src_parser_peg_keyword_helper.cpp +++ b/src/duckdb/ub_src_parser_peg_keyword_helper.cpp @@ -2,5 +2,7 @@ #include "src/parser/peg/keyword_helper/duckdb_keyword_helper.cpp" +#include "src/parser/peg/keyword_helper/grammar_literal_table.cpp" + #include "src/parser/peg/keyword_helper/parsed_grammar_keyword_helper.cpp" diff --git a/src/duckdb/ub_src_parser_peg_transformer.cpp b/src/duckdb/ub_src_parser_peg_transformer.cpp index f0ef9dfc5..cb32cf39d 100644 --- a/src/duckdb/ub_src_parser_peg_transformer.cpp +++ b/src/duckdb/ub_src_parser_peg_transformer.cpp @@ -58,7 +58,7 @@ #include "src/parser/peg/transformer/transform_external_resource.cpp" -#include "src/parser/peg/transformer/transform_generated.cpp" +#include "src/parser/peg/transformer/transform_external_resource_source.cpp" #include "src/parser/peg/transformer/transform_generated_trampoline.cpp" diff --git a/src/duckdb/ub_src_parser_statement.cpp b/src/duckdb/ub_src_parser_statement.cpp index cce63436d..7e5862f60 100644 --- a/src/duckdb/ub_src_parser_statement.cpp +++ b/src/duckdb/ub_src_parser_statement.cpp @@ -38,6 +38,8 @@ #include "src/parser/statement/multi_statement.cpp" +#include "src/parser/statement/passthrough_statement.cpp" + #include "src/parser/statement/pragma_statement.cpp" #include "src/parser/statement/prepare_statement.cpp" diff --git a/src/duckdb/ub_src_planner.cpp b/src/duckdb/ub_src_planner.cpp index 6676dae76..caf2a8218 100644 --- a/src/duckdb/ub_src_planner.cpp +++ b/src/duckdb/ub_src_planner.cpp @@ -4,8 +4,6 @@ #include "src/planner/binding_alias.cpp" -#include "src/planner/bound_expression_sql_exporter.cpp" - #include "src/planner/bound_parameter_map.cpp" #include "src/planner/bound_result_modifier.cpp" diff --git a/src/duckdb/ub_src_planner_binder_tableref.cpp b/src/duckdb/ub_src_planner_binder_tableref.cpp index faab12a1f..8973efb30 100644 --- a/src/duckdb/ub_src_planner_binder_tableref.cpp +++ b/src/duckdb/ub_src_planner_binder_tableref.cpp @@ -14,8 +14,6 @@ #include "src/planner/binder/tableref/bind_match_recognize_ref.cpp" -#include "src/planner/binder/tableref/bind_named_parameters.cpp" - #include "src/planner/binder/tableref/bind_pivot.cpp" #include "src/planner/binder/tableref/bind_showref.cpp" diff --git a/src/duckdb/ub_src_planner_expression.cpp b/src/duckdb/ub_src_planner_expression.cpp index 19a61ea89..a66a4ed61 100644 --- a/src/duckdb/ub_src_planner_expression.cpp +++ b/src/duckdb/ub_src_planner_expression.cpp @@ -36,3 +36,5 @@ #include "src/planner/expression/legacy_bound_comparison_expression.cpp" +#include "src/planner/expression/window_range_info.cpp" + diff --git a/src/duckdb/ub_src_planner_sql_export.cpp b/src/duckdb/ub_src_planner_sql_export.cpp new file mode 100644 index 000000000..8e4e9e3f3 --- /dev/null +++ b/src/duckdb/ub_src_planner_sql_export.cpp @@ -0,0 +1,34 @@ +#include "src/planner/sql_export/bound_expression_sql_exporter.cpp" + +#include "src/planner/sql_export/logical_plan_sql_exporter.cpp" + +#include "src/planner/sql_export/sql_export_composition.cpp" + +#include "src/planner/sql_export/sql_export_constants.cpp" + +#include "src/planner/sql_export/sql_export_cte.cpp" + +#include "src/planner/sql_export/sql_export_dml.cpp" + +#include "src/planner/sql_export/sql_export_functions.cpp" + +#include "src/planner/sql_export/sql_export_joins.cpp" + +#include "src/planner/sql_export/sql_export_limit.cpp" + +#include "src/planner/sql_export/sql_export_pivot.cpp" + +#include "src/planner/sql_export/sql_export_relations.cpp" + +#include "src/planner/sql_export/sql_export_scope.cpp" + +#include "src/planner/sql_export/sql_export_sources.cpp" + +#include "src/planner/sql_export/sql_export_statements.cpp" + +#include "src/planner/sql_export/sql_export_values.cpp" + +#include "src/planner/sql_export/sql_export_window.cpp" + +#include "src/planner/sql_export/table_function_sql_export.cpp" + From 4e199c89fcb47060f6387f48d0cd36709a4da9d3 Mon Sep 17 00:00:00 2001 From: DuckLabs GitHub Bot Date: Fri, 16 Oct 2026 16:44:11 +0000 Subject: [PATCH 2/6] Update vendored DuckDB sources to e1ecb5b8877 --- .../aggregate/distributive/kurtosis.cpp | 92 ++-- .../aggregate/distributive/skew.cpp | 78 +-- .../holistic/approximate_quantile.cpp | 32 +- .../aggregate/holistic/reservoir_quantile.cpp | 2 + .../core_functions/lambda_functions.cpp | 6 +- .../core_functions/scalar/date/date_trunc.cpp | 7 +- .../core_functions/scalar/date/epoch.cpp | 4 +- .../core_functions/scalar/date/make_date.cpp | 3 + .../scalar/date/time_bucket.cpp | 6 + .../scalar/list/list_reduce.cpp | 3 + src/duckdb/extension/icu/icu-strptime.cpp | 8 +- .../json/json_functions/json_structure.cpp | 2 +- .../parquet/include/parquet_crypto.hpp | 4 +- .../extension/parquet/parquet_crypto.cpp | 10 +- .../extension/parquet/parquet_extension.cpp | 6 +- .../extension/parquet/parquet_metadata.cpp | 5 + ...nerated_extension_loader_package_build.cpp | 8 +- .../catalog_entry/duck_table_entry.cpp | 18 + .../catalog_entry/sequence_catalog_entry.cpp | 82 +-- src/duckdb/src/common/enum_util.cpp | 14 +- .../src/common/enums/optimizer_type.cpp | 1 + .../common/multi_file/multi_file_reader.cpp | 24 +- .../src/common/operator/convert_to_string.cpp | 6 + .../common/row_operations/row_aggregate.cpp | 33 +- src/duckdb/src/common/types.cpp | 7 + src/duckdb/src/common/types/bit.cpp | 34 +- .../types/row/tuple_data_scatter_gather.cpp | 37 +- src/duckdb/src/common/types/time.cpp | 14 +- src/duckdb/src/common/types/timestamp.cpp | 4 + .../variant/parquet_variant_encoding.cpp | 27 +- .../src/execution/aggregate_hashtable.cpp | 15 +- .../execution/index/fixed_size_allocator.cpp | 9 +- .../src/execution/index/fixed_size_buffer.cpp | 2 +- .../scanner/string_value_scanner.cpp | 6 +- .../operator/join/physical_asof_join.cpp | 9 +- .../operator/join/physical_hash_join.cpp | 34 ++ .../operator/join/physical_iejoin.cpp | 11 +- .../persistent/physical_copy_to_file.cpp | 21 + .../src/execution/physical_operator.cpp | 11 +- .../physical_plan/plan_asof_join.cpp | 18 + .../execution/radix_partitioned_hashtable.cpp | 11 +- src/duckdb/src/function/cast/array_casts.cpp | 10 +- src/duckdb/src/function/cast/bignum_casts.cpp | 34 +- .../src/function/cast/cast_function_set.cpp | 8 + src/duckdb/src/function/cast/enum_casts.cpp | 10 +- src/duckdb/src/function/cast/list_casts.cpp | 12 +- src/duckdb/src/function/cast/map_cast.cpp | 16 +- .../function/cast/nested_to_varchar_cast.cpp | 110 ++++ src/duckdb/src/function/cast/struct_cast.cpp | 11 +- .../src/function/combine_types_rules.cpp | 57 ++- src/duckdb/src/function/encoding_function.cpp | 40 +- src/duckdb/src/function/function_binder.cpp | 4 + .../src/function/pragma/pragma_queries.cpp | 5 +- .../src/function/scalar/list/list_resize.cpp | 4 + .../function/scalar/operator/arithmetic.cpp | 13 +- .../src/function/scalar/strftime_format.cpp | 3 +- .../src/function/scalar/string/like.cpp | 48 +- .../function/scalar/struct/remap_struct.cpp | 44 +- .../function/scalar/struct/struct_extract.cpp | 95 ++-- src/duckdb/src/function/table/arrow.cpp | 32 +- src/duckdb/src/function/table/checkpoint.cpp | 10 +- .../table/system/pragma_metadata_info.cpp | 4 + .../function/table/version/pragma_version.cpp | 6 +- .../function/variant/variant_shredding.cpp | 6 +- .../function/window/window_value_function.cpp | 129 ++++- .../catalog_entry/sequence_catalog_entry.hpp | 18 +- .../duckdb/common/enums/optimizer_type.hpp | 3 +- .../common/multi_file/multi_file_function.hpp | 7 + .../common/multi_file/multi_file_options.hpp | 7 +- .../common/operator/convert_to_string.hpp | 2 + .../operator/decimal_cast_operators.hpp | 8 +- .../include/duckdb/common/types/bignum.hpp | 28 +- .../src/include/duckdb/common/types/time.hpp | 2 +- .../vector_operations/aggregate_executor.hpp | 46 ++ .../execution/index/fixed_size_allocator.hpp | 35 +- .../function/cast/nested_to_varchar_cast.hpp | 6 + src/duckdb/src/include/duckdb/main/config.hpp | 5 + .../include/duckdb/main/extension_entries.hpp | 6 +- .../duckdb/main/secret/default_secrets.hpp | 3 + .../src/include/duckdb/main/secret/secret.hpp | 11 +- .../optimizer/disjunctive_join_rewriter.hpp | 95 ++++ .../include/duckdb/parser/parser_options.hpp | 2 + .../parser/peg/ast/expression_chain.hpp | 5 +- .../duckdb/parser/peg/inlined_grammar.hpp | 16 +- .../peg/matcher/number_literal_matcher.hpp | 8 + .../peg/transformer/peg_transformer.hpp | 36 +- .../include/duckdb/storage/block_manager.hpp | 13 +- src/duckdb/src/main/client_context.cpp | 1 + .../src/main/client_context_execution.cpp | 9 +- src/duckdb/src/main/config.cpp | 35 ++ src/duckdb/src/main/query_profiler.cpp | 3 + .../src/main/secret/default_secrets.cpp | 8 +- src/duckdb/src/main/secret/secret.cpp | 35 ++ src/duckdb/src/main/secret/secret_manager.cpp | 13 +- .../src/main/settings/custom_settings.cpp | 20 +- .../optimizer/disjunctive_join_rewriter.cpp | 476 ++++++++++++++++++ .../join_filter_pushdown_optimizer.cpp | 13 +- src/duckdb/src/optimizer/optimizer.cpp | 6 + .../pushdown/pushdown_projection.cpp | 24 +- .../src/optimizer/remove_unused_columns.cpp | 31 +- .../parser/peg/tokenizer/base_tokenizer.cpp | 18 +- .../transformer/peg_transformer_factory.cpp | 3 +- .../parser/peg/transformer/transform_copy.cpp | 13 +- .../parser/peg/transformer/transform_drop.cpp | 7 +- .../peg/transformer/transform_expression.cpp | 128 +++-- .../transform_generated_trampoline.cpp | 158 ++++-- .../transform_generic_copy_option.cpp | 3 +- .../peg/transformer/transform_pragma.cpp | 7 + .../peg/transformer/transform_select.cpp | 23 +- .../expression/bind_function_expression.cpp | 5 +- .../expression/bind_star_expression.cpp | 15 +- .../bind_unpacked_star_expression.cpp | 3 + .../binder/query_node/bind_select_node.cpp | 34 +- .../binder/statement/bind_copy_database.cpp | 3 + .../binder/tableref/bind_basetableref.cpp | 3 + .../planner/binder/tableref/bind_pivot.cpp | 76 ++- .../binder/tableref/bind_table_function.cpp | 3 + src/duckdb/src/planner/collation_binding.cpp | 16 +- .../src/storage/buffer/block_manager.cpp | 13 +- .../write_overflow_strings_to_disk.cpp | 4 +- .../src/storage/metadata/metadata_manager.cpp | 2 +- src/duckdb/src/storage/storage_manager.cpp | 6 +- .../src/storage/table/column_segment.cpp | 4 +- .../src/storage/temporary_file_manager.cpp | 19 +- src/duckdb/ub_src_optimizer.cpp | 2 + 125 files changed, 2439 insertions(+), 600 deletions(-) create mode 100644 src/duckdb/src/include/duckdb/optimizer/disjunctive_join_rewriter.hpp create mode 100644 src/duckdb/src/optimizer/disjunctive_join_rewriter.cpp diff --git a/src/duckdb/extension/core_functions/aggregate/distributive/kurtosis.cpp b/src/duckdb/extension/core_functions/aggregate/distributive/kurtosis.cpp index e90d27661..a5e0a5a39 100644 --- a/src/duckdb/extension/core_functions/aggregate/distributive/kurtosis.cpp +++ b/src/duckdb/extension/core_functions/aggregate/distributive/kurtosis.cpp @@ -9,15 +9,17 @@ namespace duckdb { namespace { +//! Online central moments (Terriberry / Pébay, DOI: 10.2172/1028931), the higher-moment extension of Welford's +//! method used by var_samp: M2..M4 are exactly zero for constant input instead of rounding noise of power sums struct KurtosisState { - static constexpr const char *STATE_NAMES[] = {"n", "sum", "sum_sqr", "sum_cub", "sum_four"}; + static constexpr const char *STATE_NAMES[] = {"n", "mean", "m2", "m3", "m4"}; using STATE_TYPE = StructStateType; idx_t n; - double sum; - double sum_sqr; - double sum_cub; - double sum_four; + double mean; // M1 + double m2; // M2 = sum((x - mean)^2) + double m3; // M3 = sum((x - mean)^3) + double m4; // M4 = sum((x - mean)^4) }; struct KurtosisFlagBiasCorrection {}; @@ -29,18 +31,29 @@ struct KurtosisOperation { template static void ConstantOperation(STATE &state, const INPUT_TYPE &input, AggregateUnaryInput &unary_input, idx_t count) { - for (idx_t i = 0; i < count; i++) { - Operation(state, input, unary_input); - } + // a block of identical values has mean "input" and zero central moments + STATE constant; + constant.n = count; + constant.mean = static_cast(input); + constant.m2 = 0; + constant.m3 = 0; + constant.m4 = 0; + Combine(constant, state, unary_input.input); } template - static void Operation(STATE &state, const INPUT_TYPE &input, AggregateUnaryInput &unary_input) { + static void Operation(STATE &state, const INPUT_TYPE &input, AggregateUnaryInput &) { + const double n1 = static_cast(state.n); state.n++; - state.sum += input; - state.sum_sqr += pow(input, 2); - state.sum_cub += pow(input, 3); - state.sum_four += pow(input, 4); + const double n = static_cast(state.n); + const double delta = static_cast(input) - state.mean; + const double delta_n = delta / n; + const double delta_n2 = delta_n * delta_n; + const double term1 = delta * delta_n * n1; + state.mean += delta_n; + state.m4 += term1 * delta_n2 * (n * n - 3 * n + 3) + 6 * delta_n2 * state.m2 - 4 * delta_n * state.m3; + state.m3 += term1 * delta_n * (n - 2) - 3 * delta_n * state.m2; + state.m2 += term1; } template @@ -48,47 +61,52 @@ struct KurtosisOperation { if (source.n == 0) { return; } - if (!TryAddOperator::Operation(target.n, source.n, target.n)) { + if (target.n == 0) { + target = source; + return; + } + idx_t count; + if (!TryAddOperator::Operation(target.n, source.n, count)) { throw OutOfRangeException("Overflow in aggregate state combine"); } - target.sum += source.sum; - target.sum_sqr += source.sum_sqr; - target.sum_cub += source.sum_cub; - target.sum_four += source.sum_four; + const double na = static_cast(target.n); + const double nb = static_cast(source.n); + const double n = static_cast(count); + const double delta = source.mean - target.mean; + const double delta_n = delta / n; + const double delta_n2 = delta_n * delta_n; + target.m4 += source.m4 + delta * delta_n * delta_n2 * na * nb * (na * na - na * nb + nb * nb) + + 6 * delta_n2 * (na * na * source.m2 + nb * nb * target.m2) + + 4 * delta_n * (na * source.m3 - nb * target.m3); + target.m3 += + source.m3 + delta * delta_n2 * na * nb * (na - nb) + 3 * delta_n * (na * source.m2 - nb * target.m2); + target.m2 += source.m2 + delta * delta_n * na * nb; + target.mean += delta_n * nb; + target.n = count; } template static void Finalize(STATE &state, TARGET_TYPE &target, AggregateFinalizeData &finalize_data) { - auto n = (double)state.n; - if (n <= 1) { + if (state.n <= 1) { finalize_data.ReturnNull(); return; } - if (std::is_same::value && n <= 3) { + if (std::is_same::value && state.n <= 3) { finalize_data.ReturnNull(); return; } - double temp = 1 / n; - //! This is necessary due to linux 32 bits - long double temp_aux = 1 / n; - if (state.sum_sqr - state.sum * state.sum * temp == 0 || - state.sum_sqr - state.sum * state.sum * temp_aux == 0) { - finalize_data.ReturnNull(); - return; - } - double m4 = - temp * (state.sum_four - 4 * state.sum_cub * state.sum * temp + - 6 * state.sum_sqr * state.sum * state.sum * temp * temp - 3 * pow(state.sum, 4) * pow(temp, 3)); - - double m2 = temp * (state.sum_sqr - state.sum * state.sum * temp); - if (m2 <= 0) { // m2 shouldn't be below 0 but floating points are weird + // kurtosis is undefined for zero variance (M2 is exactly 0 for constant input) + if (state.m2 <= 0) { finalize_data.ReturnNull(); return; } + const double n = static_cast(state.n); + // m4 / m2^2 with the population moments m4 = M4 / n and m2 = M2 / n + const double m4_over_m2_sqr = n * (state.m4 / state.m2) / state.m2; if (std::is_same::value) { - target = m4 / (m2 * m2) - 3; + target = m4_over_m2_sqr - 3; } else { - target = (n - 1) * ((n + 1) * m4 / (m2 * m2) - 3 * (n - 1)) / ((n - 2) * (n - 3)); + target = (n - 1) * ((n + 1) * m4_over_m2_sqr - 3 * (n - 1)) / ((n - 2) * (n - 3)); } if (!Value::DoubleIsFinite(target)) { throw OutOfRangeException("Kurtosis is out of range!"); diff --git a/src/duckdb/extension/core_functions/aggregate/distributive/skew.cpp b/src/duckdb/extension/core_functions/aggregate/distributive/skew.cpp index 8443c7e55..f354c98bf 100644 --- a/src/duckdb/extension/core_functions/aggregate/distributive/skew.cpp +++ b/src/duckdb/extension/core_functions/aggregate/distributive/skew.cpp @@ -3,37 +3,47 @@ #include "duckdb/common/vector_operations/vector_operations.hpp" #include "duckdb/planner/expression/bound_aggregate_expression.hpp" #include "duckdb/common/algorithm.hpp" -#include namespace duckdb { namespace { +//! Online central moments (Terriberry / Pébay, DOI: 10.2172/1028931), the higher-moment extension of Welford's +//! method used by var_samp: M2 and M3 are exactly zero for constant input instead of rounding noise of power sums struct SkewState { - static constexpr const char *STATE_NAMES[] = {"n", "sum", "sum_sqr", "sum_cub"}; + static constexpr const char *STATE_NAMES[] = {"n", "mean", "m2", "m3"}; using STATE_TYPE = StructStateType; idx_t n; - double sum; - double sum_sqr; - double sum_cub; + double mean; // M1 + double m2; // M2 = sum((x - mean)^2) + double m3; // M3 = sum((x - mean)^3) }; struct SkewnessOperation { template static void ConstantOperation(STATE &state, const INPUT_TYPE &input, AggregateUnaryInput &unary_input, idx_t count) { - for (idx_t i = 0; i < count; i++) { - Operation(state, input, unary_input); - } + // a block of identical values has mean "input" and zero central moments + STATE constant; + constant.n = count; + constant.mean = static_cast(input); + constant.m2 = 0; + constant.m3 = 0; + Combine(constant, state, unary_input.input); } template - static void Operation(STATE &state, const INPUT_TYPE &input, AggregateUnaryInput &unary_input) { + static void Operation(STATE &state, const INPUT_TYPE &input, AggregateUnaryInput &) { + const double n1 = static_cast(state.n); state.n++; - state.sum += input; - state.sum_sqr += pow(input, 2); - state.sum_cub += pow(input, 3); + const double n = static_cast(state.n); + const double delta = static_cast(input) - state.mean; + const double delta_n = delta / n; + const double term1 = delta * delta_n * n1; + state.mean += delta_n; + state.m3 += term1 * delta_n * (n - 2) - 3 * delta_n * state.m2; + state.m2 += term1; } template @@ -41,11 +51,22 @@ struct SkewnessOperation { if (source.n == 0) { return; } - - target.n += source.n; - target.sum += source.sum; - target.sum_sqr += source.sum_sqr; - target.sum_cub += source.sum_cub; + if (target.n == 0) { + target = source; + return; + } + const auto count = target.n + source.n; + D_ASSERT(count >= target.n); // This is a check that we are not overflowing + const double na = static_cast(target.n); + const double nb = static_cast(source.n); + const double n = static_cast(count); + const double delta = source.mean - target.mean; + const double delta_n = delta / n; + target.m3 += source.m3 + delta * delta_n * delta_n * na * nb * (na - nb) + + 3 * delta_n * (na * source.m2 - nb * target.m2); + target.m2 += source.m2 + delta * delta_n * na * nb; + target.mean += delta_n * nb; + target.n = count; } template @@ -54,26 +75,15 @@ struct SkewnessOperation { finalize_data.ReturnNull(); return; } - double n = state.n; - double temp = 1 / n; - double raw_m2 = state.sum_sqr - state.sum * state.sum * temp; - // Only treat finite second-moment noise as zero; overflow should still surface as out-of-range. - if (Value::DoubleIsFinite(raw_m2) && Value::DoubleIsFinite(state.sum_sqr) && - // Scale the tolerance by the accumulated squared magnitude instead of using a fixed epsilon. - std::abs(raw_m2) <= std::numeric_limits::epsilon() * std::max(1.0, std::abs(state.sum_sqr))) { - finalize_data.ReturnNull(); - return; - } - double variance = temp * raw_m2; - if (variance <= 0) { + // skewness is undefined for zero variance (M2 is exactly 0 for constant input) + if (state.m2 <= 0) { finalize_data.ReturnNull(); return; } - auto p = std::pow(variance, 3); - double div = std::sqrt(p); - double temp1 = std::sqrt(n * (n - 1)) / (n - 2); - target = temp1 * temp * - (state.sum_cub - 3 * state.sum_sqr * state.sum * temp + 2 * pow(state.sum, 3) * temp * temp) / div; + const double n = static_cast(state.n); + // g1 = (M3 / n) / (M2 / n)^1.5 = sqrt(n) * M3 / M2^1.5, with the bias correction sqrt(n (n - 1)) / (n - 2) + const double g1 = std::sqrt(n) * (state.m3 / state.m2) / std::sqrt(state.m2); + target = std::sqrt(n * (n - 1)) / (n - 2) * g1; if (!Value::DoubleIsFinite(target)) { throw OutOfRangeException("SKEW is out of range!"); } diff --git a/src/duckdb/extension/core_functions/aggregate/holistic/approximate_quantile.cpp b/src/duckdb/extension/core_functions/aggregate/holistic/approximate_quantile.cpp index b545e937a..93fe61290 100644 --- a/src/duckdb/extension/core_functions/aggregate/holistic/approximate_quantile.cpp +++ b/src/duckdb/extension/core_functions/aggregate/holistic/approximate_quantile.cpp @@ -57,6 +57,23 @@ struct ApproxQuantileCoding { } }; +template <> +double ApproxQuantileCoding::Encode(const dtime_t &input) { + return Encode(input.value); +} + +template <> +bool ApproxQuantileCoding::Decode(const double &source, dtime_t &target) { + int64_t micros; + auto decoded = Decode(source, micros); + if (micros < 0 || micros > Interval::MICROS_PER_DAY) { + micros = MinValue(MaxValue(micros, 0), Interval::MICROS_PER_DAY); + decoded = false; + } + target = dtime_t(micros); + return decoded; +} + template <> double ApproxQuantileCoding::Encode(const dtime_tz_t &input) { return Encode(input.sort_key()); @@ -67,11 +84,11 @@ bool ApproxQuantileCoding::Decode(const double &source, dtime_tz_t &target) { uint64_t sort_key; const auto decoded = Decode(source, sort_key); if (decoded) { - // We can invert the sort key because its offset was not touched. - auto offset = dtime_tz_t::decode_offset(sort_key); + // The offset bits of an interpolated sort key are meaningless: return the normalized time at +00. auto micros = dtime_tz_t::decode_micros(sort_key); - micros -= int64_t(dtime_tz_t::encode_offset(offset) * dtime_tz_t::OFFSET_MICROS); - target = dtime_tz_t(dtime_t(micros), offset); + micros -= int64_t(dtime_tz_t::MAX_OFFSET * dtime_tz_t::OFFSET_MICROS); + micros = (micros % Interval::MICROS_PER_DAY + Interval::MICROS_PER_DAY) % Interval::MICROS_PER_DAY; + target = dtime_tz_t(dtime_t(micros), 0); } else if (source < 0) { target = Value::MinimumValue(LogicalTypeId::TIME_TZ).GetValue(); } else { @@ -348,6 +365,10 @@ AggregateFunction GetApproximateQuantileAggregateFunction(const LogicalType &typ return AggregateFunction::UnaryAggregate(type, type); } + if (type == LogicalType::TIME) { + return AggregateFunction::UnaryAggregate( + type, type); + } if (type.id() == LogicalTypeId::TIMESTAMP || type.id() == LogicalTypeId::TIMESTAMP_TZ) { return AggregateFunction::UnaryAggregate(type, type); @@ -538,8 +559,9 @@ AggregateFunction GetApproxQuantileListAggregateFunction(const LogicalType &type case LogicalTypeId::DATE: return GetTypedApproxQuantileListAggregateFunction(type); case LogicalTypeId::BIGINT: - case LogicalTypeId::TIME: return GetTypedApproxQuantileListAggregateFunction(type); + case LogicalTypeId::TIME: + return GetTypedApproxQuantileListAggregateFunction(type); case LogicalTypeId::TIMESTAMP: case LogicalTypeId::TIMESTAMP_TZ: return GetTypedApproxQuantileListAggregateFunction(type); diff --git a/src/duckdb/extension/core_functions/aggregate/holistic/reservoir_quantile.cpp b/src/duckdb/extension/core_functions/aggregate/holistic/reservoir_quantile.cpp index 5947345a8..e25e76580 100644 --- a/src/duckdb/extension/core_functions/aggregate/holistic/reservoir_quantile.cpp +++ b/src/duckdb/extension/core_functions/aggregate/holistic/reservoir_quantile.cpp @@ -54,6 +54,8 @@ struct ReservoirQuantileState { D_ASSERT(r_samp->next_index_to_sample >= r_samp->num_entries_to_skip_b4_next_sample); if (r_samp->next_index_to_sample == r_samp->num_entries_to_skip_b4_next_sample) { ReplaceElement(element); + } else { + r_samp->num_entries_to_skip_b4_next_sample++; } } } diff --git a/src/duckdb/extension/core_functions/lambda_functions.cpp b/src/duckdb/extension/core_functions/lambda_functions.cpp index 2d68cebe2..335c10f96 100644 --- a/src/duckdb/extension/core_functions/lambda_functions.cpp +++ b/src/duckdb/extension/core_functions/lambda_functions.cpp @@ -233,12 +233,16 @@ void ListLambdaBindData::Serialize(Serializer &serializer, const optional_ptr(103, "has_initial", bind_data.has_initial, false); } -unique_ptr ListLambdaBindData::Deserialize(Deserializer &deserializer, BoundScalarFunction &) { +unique_ptr ListLambdaBindData::Deserialize(Deserializer &deserializer, + BoundScalarFunction &bound_function) { auto return_type = deserializer.ReadProperty(100, "return_type"); auto lambda_expr = deserializer.ReadPropertyWithExplicitDefault>(101, "lambda_expr", unique_ptr()); auto has_index = deserializer.ReadProperty(102, "has_index"); auto has_initial = deserializer.ReadPropertyWithExplicitDefault(103, "has_initial", false); + if (lambda_expr && lambda_expr->IsVolatile()) { + bound_function.SetVolatile(); + } return make_uniq(return_type, std::move(lambda_expr), has_index, has_initial); } diff --git a/src/duckdb/extension/core_functions/scalar/date/date_trunc.cpp b/src/duckdb/extension/core_functions/scalar/date/date_trunc.cpp index 9998eb3d5..7547113cd 100644 --- a/src/duckdb/extension/core_functions/scalar/date/date_trunc.cpp +++ b/src/duckdb/extension/core_functions/scalar/date/date_trunc.cpp @@ -2,6 +2,7 @@ #include "duckdb/common/enums/date_part_specifier.hpp" #include "duckdb/common/exception.hpp" #include "duckdb/common/operator/cast_operators.hpp" +#include "duckdb/common/operator/multiply.hpp" #include "duckdb/common/types/date.hpp" #include "duckdb/common/types/time.hpp" #include "duckdb/common/types/timestamp.hpp" @@ -102,7 +103,11 @@ struct DateTrunc { const int64_t v = ts.value; // Round towards negative infinity instead of 0 const int64_t q = v / interval_us - (v % interval_us != 0 && v < 0 ? 1 : 0); - return timestamp_t(q * interval_us); + int64_t result; + if (!TryMultiplyOperator::Operation(q, interval_us, result) || result == NumericLimits::Minimum()) { + throw ConversionException("Date out of range in timestamp conversion"); + } + return timestamp_t(result); } struct HourOperator { diff --git a/src/duckdb/extension/core_functions/scalar/date/epoch.cpp b/src/duckdb/extension/core_functions/scalar/date/epoch.cpp index bdea47a26..34661224a 100644 --- a/src/duckdb/extension/core_functions/scalar/date/epoch.cpp +++ b/src/duckdb/extension/core_functions/scalar/date/epoch.cpp @@ -12,7 +12,9 @@ struct EpochSecOperator { template static RESULT_TYPE Operation(INPUT_TYPE sec) { int64_t result; - if (!TryCast::Operation(sec * Interval::MICROS_PER_SEC, result)) { + // the first midnight of Timestamp::MIN_YEAR - smaller values are not valid timestamps + constexpr int64_t min_timestamp_micros = -9223372022400000000LL; + if (!TryCast::Operation(sec * Interval::MICROS_PER_SEC, result) || result < min_timestamp_micros) { throw ConversionException("Epoch seconds out of range for TIMESTAMP WITH TIME ZONE"); } return timestamp_t(result); diff --git a/src/duckdb/extension/core_functions/scalar/date/make_date.cpp b/src/duckdb/extension/core_functions/scalar/date/make_date.cpp index 6e61c3a22..158b3e28a 100644 --- a/src/duckdb/extension/core_functions/scalar/date/make_date.cpp +++ b/src/duckdb/extension/core_functions/scalar/date/make_date.cpp @@ -72,6 +72,9 @@ struct MakeTimeOperator { static RESULT_TYPE Operation(HH hh, MM mm, SS ss) { auto hh_32 = Cast::Operation(hh); auto mm_32 = Cast::Operation(mm); + if (!std::isfinite(static_cast(ss))) { + throw ConversionException("Time out of range: seconds must be a finite number"); + } // Have to check this separately because safe casting of DOUBLE => INT32 can round. int32_t ss_32 = 0; if (ss < 0 || ss > Interval::SECS_PER_MINUTE) { diff --git a/src/duckdb/extension/core_functions/scalar/date/time_bucket.cpp b/src/duckdb/extension/core_functions/scalar/date/time_bucket.cpp index 3ace819f3..bd78a9c0d 100644 --- a/src/duckdb/extension/core_functions/scalar/date/time_bucket.cpp +++ b/src/duckdb/extension/core_functions/scalar/date/time_bucket.cpp @@ -97,6 +97,9 @@ struct TimeBucket { return (Date::ExtractYear(ts_date) - 1970) * 12 + Date::ExtractMonth(ts_date) - 1; } + //! The first midnight of Timestamp::MIN_YEAR - smaller values are not valid timestamps + static constexpr int64_t MIN_TIMESTAMP_MICROS = -9223372022400000000LL; + static inline timestamp_t WidthConvertibleToMicrosCommon(int64_t bucket_width_micros, int64_t ts_micros, int64_t origin_micros) { origin_micros %= bucket_width_micros; @@ -108,6 +111,9 @@ struct TimeBucket { SubtractOperatorOverflowCheck::Operation(result_micros, bucket_width_micros); } result_micros = AddOperatorOverflowCheck::Operation(result_micros, origin_micros); + if (result_micros < MIN_TIMESTAMP_MICROS) { + throw OutOfRangeException("Overflow in TIME_BUCKET: the result is below the minimum TIMESTAMP"); + } return Timestamp::FromEpochMicroSeconds(result_micros); } diff --git a/src/duckdb/extension/core_functions/scalar/list/list_reduce.cpp b/src/duckdb/extension/core_functions/scalar/list/list_reduce.cpp index 0001a9e17..148f67f7e 100644 --- a/src/duckdb/extension/core_functions/scalar/list/list_reduce.cpp +++ b/src/duckdb/extension/core_functions/scalar/list/list_reduce.cpp @@ -262,6 +262,9 @@ unique_ptr ListReduceBind(BindScalarFunctionInput &input) { // cast in place, so that the bound lambda expression stays intact as a child of the function bound_lambda_expr.LambdaExprMutable() = std::move(cast_lambda_expr); bound_function.SetReturnType(bound_lambda_expr.LambdaExpr()->GetReturnType()); + if (bound_lambda_expr.LambdaExpr()->IsVolatile()) { + bound_function.SetVolatile(); + } return make_uniq(bound_function.GetReturnType(), bound_lambda_expr.LambdaExpr()->Copy(), has_index, has_initial); } diff --git a/src/duckdb/extension/icu/icu-strptime.cpp b/src/duckdb/extension/icu/icu-strptime.cpp index e53514780..57abd454a 100644 --- a/src/duckdb/extension/icu/icu-strptime.cpp +++ b/src/duckdb/extension/icu/icu-strptime.cpp @@ -678,10 +678,10 @@ struct ICUStrftime : public ICUDateFunc { auto offset = ExtractField(calendar, CAL_ZONE_OFFSET) + ExtractField(calendar, CAL_DST_OFFSET); offset /= Interval::MSECS_PER_SEC; - offset /= Interval::SECS_PER_MINUTE; - int hour_offset = offset / 60; - int minute_offset = offset % 60; - auto offset_str = Time::ToUTCOffset(hour_offset, minute_offset); + int hour_offset = offset / Interval::SECS_PER_HOUR; + int minute_offset = (offset % Interval::SECS_PER_HOUR) / Interval::SECS_PER_MINUTE; + int second_offset = offset % Interval::SECS_PER_MINUTE; + auto offset_str = Time::ToUTCOffset(hour_offset, minute_offset, second_offset); const auto offset_len = offset_str.size(); const auto len = date_len + 1 + time_len + offset_len; diff --git a/src/duckdb/extension/json/json_functions/json_structure.cpp b/src/duckdb/extension/json/json_functions/json_structure.cpp index 83e2f09c1..25540f760 100644 --- a/src/duckdb/extension/json/json_functions/json_structure.cpp +++ b/src/duckdb/extension/json/json_functions/json_structure.cpp @@ -677,8 +677,8 @@ static void GetStructureFunctionInternal(ScalarFunctionSet &set, const LogicalTy ScalarFunctionSet JSONFunctions::GetStructureFunction() { ScalarFunctionSet set("json_structure"); GetStructureFunctionInternal(set, LogicalType::VARCHAR); - set.SetFallible(); GetStructureFunctionInternal(set, LogicalType::JSON()); + set.SetFallible(); return set; } diff --git a/src/duckdb/extension/parquet/include/parquet_crypto.hpp b/src/duckdb/extension/parquet/include/parquet_crypto.hpp index ca3efade4..bc7d801fc 100644 --- a/src/duckdb/extension/parquet/include/parquet_crypto.hpp +++ b/src/duckdb/extension/parquet/include/parquet_crypto.hpp @@ -17,6 +17,7 @@ #include "duckdb/common/encryption_state.hpp" #include "duckdb/common/encryption_functions.hpp" #include "duckdb/storage/object_cache.hpp" +#include "duckdb/common/mutex.hpp" #include "duckdb/common/optional_idx.hpp" #include "duckdb/common/shared_ptr_ipp.hpp" #include "duckdb/common/string.hpp" @@ -56,7 +57,7 @@ class ParquetKeys : public ObjectCacheEntry { public: void AddKey(const string &key_name, const string &key); bool HasKey(const string &key_name) const; - const string &GetKey(const string &key_name) const; + string GetKey(const string &key_name) const; public: static string ObjectType(); @@ -66,6 +67,7 @@ class ParquetKeys : public ObjectCacheEntry { } private: + mutable mutex lock; unordered_map keys; }; diff --git a/src/duckdb/extension/parquet/parquet_crypto.cpp b/src/duckdb/extension/parquet/parquet_crypto.cpp index aaff30172..81d70bbc0 100644 --- a/src/duckdb/extension/parquet/parquet_crypto.cpp +++ b/src/duckdb/extension/parquet/parquet_crypto.cpp @@ -41,15 +41,18 @@ ParquetKeys &ParquetKeys::Get(ClientContext &context) { } void ParquetKeys::AddKey(const string &key_name, const string &key) { + lock_guard guard(lock); keys[key_name] = key; } bool ParquetKeys::HasKey(const string &key_name) const { + lock_guard guard(lock); return keys.find(key_name) != keys.end(); } -const string &ParquetKeys::GetKey(const string &key_name) const { - D_ASSERT(HasKey(key_name)); +string ParquetKeys::GetKey(const string &key_name) const { + lock_guard guard(lock); + D_ASSERT(keys.find(key_name) != keys.end()); return keys.at(key_name); } @@ -147,6 +150,9 @@ ParquetEncryptionConfig::ParquetEncryptionConfig(ClientContext &context, const V const auto &keys = ParquetKeys::Get(context); for (idx_t i = 0; i < StructType::GetChildCount(arg.type()); i++) { auto &struct_key = child_types[i].first; + if ((struct_key == "footer_key" || struct_key == "footer_key_value") && children[i].IsNull()) { + throw BinderException("Parquet encryption_config %s cannot be NULL", struct_key); + } if (struct_key == "footer_key") { const auto footer_key_name = StringValue::Get(children[i].DefaultCastAs(LogicalType::VARCHAR)); if (!keys.HasKey(footer_key_name)) { diff --git a/src/duckdb/extension/parquet/parquet_extension.cpp b/src/duckdb/extension/parquet/parquet_extension.cpp index fc01e0def..11117f493 100644 --- a/src/duckdb/extension/parquet/parquet_extension.cpp +++ b/src/duckdb/extension/parquet/parquet_extension.cpp @@ -245,8 +245,7 @@ static unique_ptr ParquetWriteBind(ClientContext &context, CopyFun const auto &struct_children = StructValue::GetChildren(shredding_types_value); D_ASSERT(StructType::GetChildTypes(struct_type).size() == struct_children.size()); for (idx_t i = 0; i < struct_children.size(); i++) { - const auto &col_name = - StringUtil::Lower(StructType::GetChildName(struct_type, i).GetIdentifierName()); + const auto &col_name = StructType::GetChildName(struct_type, i).GetIdentifierName(); auto it = variant_names.find(Identifier(col_name)); if (it == variant_names.end()) { string names; @@ -269,8 +268,7 @@ static unique_ptr ParquetWriteBind(ClientContext &context, CopyFun } } const auto &child_value = struct_children[i]; - bind_data->shredding_types.AddChild(Identifier(col_name), - ShreddingType::GetShreddingTypes(child_value, context)); + bind_data->shredding_types.AddChild(*it, ShreddingType::GetShreddingTypes(child_value, context)); } } } else if (option_name == "kv_metadata") { diff --git a/src/duckdb/extension/parquet/parquet_metadata.cpp b/src/duckdb/extension/parquet/parquet_metadata.cpp index 3d8b00473..100299515 100644 --- a/src/duckdb/extension/parquet/parquet_metadata.cpp +++ b/src/duckdb/extension/parquet/parquet_metadata.cpp @@ -80,6 +80,11 @@ struct ParquetMetadataFilePaths { struct ParquetMetaDataBindData : public TableFunctionData { unique_ptr file_paths; + + //! The file list is consumed by the first execution + bool SupportStatementCache() const override { + return false; + } }; struct ParquetBloomProbeBindData : public ParquetMetaDataBindData { diff --git a/src/duckdb/generated_extension_loader_package_build.cpp b/src/duckdb/generated_extension_loader_package_build.cpp index c64d9ec90..2b8cd6353 100644 --- a/src/duckdb/generated_extension_loader_package_build.cpp +++ b/src/duckdb/generated_extension_loader_package_build.cpp @@ -45,7 +45,7 @@ int32_t duckdb_extension_core_functions_describe(duckdb_extension_descriptor *de descriptor->version = 1; descriptor->name = "core_functions"; descriptor->extension_version = EXT_VERSION_CORE_FUNCTIONS; - descriptor->api_version = "v2.1.0-alpha48443"; + descriptor->api_version = "v2.1.0-alpha48623"; descriptor->entry_cpp = (void (*)(void))core_functions_duckdb_cpp_init; return 0; } @@ -78,7 +78,7 @@ int32_t duckdb_extension_parquet_describe(duckdb_extension_descriptor *descripto descriptor->version = 1; descriptor->name = "parquet"; descriptor->extension_version = EXT_VERSION_PARQUET; - descriptor->api_version = "v2.1.0-alpha48443"; + descriptor->api_version = "v2.1.0-alpha48623"; descriptor->entry_cpp = (void (*)(void))parquet_duckdb_cpp_init; return 0; } @@ -111,7 +111,7 @@ int32_t duckdb_extension_icu_describe(duckdb_extension_descriptor *descriptor) { descriptor->version = 1; descriptor->name = "icu"; descriptor->extension_version = EXT_VERSION_ICU; - descriptor->api_version = "v2.1.0-alpha48443"; + descriptor->api_version = "v2.1.0-alpha48623"; descriptor->entry_cpp = (void (*)(void))icu_duckdb_cpp_init; return 0; } @@ -144,7 +144,7 @@ int32_t duckdb_extension_json_describe(duckdb_extension_descriptor *descriptor) descriptor->version = 1; descriptor->name = "json"; descriptor->extension_version = EXT_VERSION_JSON; - descriptor->api_version = "v2.1.0-alpha48443"; + descriptor->api_version = "v2.1.0-alpha48623"; descriptor->entry_cpp = (void (*)(void))json_duckdb_cpp_init; return 0; } diff --git a/src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp b/src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp index 54a5d11d5..eebc44b0a 100644 --- a/src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp @@ -1458,6 +1458,24 @@ unique_ptr DuckTableEntry::AddConstraint(ClientContext &context, A EnumUtil::ToString(info.constraint->type)); } + // The indexes of constraints must precede all other indexes. A concurrent CREATE INDEX is not visible as a + // dependency yet, but it has already registered its index with the storage. + idx_t constraint_index_count = 0; + for (const auto &constraint : GetConstraints()) { + if (constraint->type == ConstraintType::UNIQUE) { + constraint_index_count++; + } else if (constraint->type == ConstraintType::FOREIGN_KEY) { + auto &fk = constraint->Cast(); + if (fk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE || + fk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE) { + constraint_index_count++; + } + } + } + if (storage->GetDataTableInfo()->GetIndexes().Count() > constraint_index_count) { + throw DependencyException("Cannot alter entry %s because there are entries that depend on it.", name); + } + // We create a physical table with a new constraint and a new unique index. const auto binder = Binder::CreateBinder(context); const auto bound_constraint = diff --git a/src/duckdb/src/catalog/catalog_entry/sequence_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/sequence_catalog_entry.cpp index 95c870b57..aa98813cf 100644 --- a/src/duckdb/src/catalog/catalog_entry/sequence_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/sequence_catalog_entry.cpp @@ -22,7 +22,8 @@ SequenceData::SequenceData(CreateSequenceInfo &info) } SequenceCatalogEntry::SequenceCatalogEntry(Catalog &catalog, SchemaCatalogEntry &schema, CreateSequenceInfo &info) - : StandardEntry(CatalogType::SEQUENCE_ENTRY, schema, catalog, info.GetSequenceName()), data(info) { + : StandardEntry(CatalogType::SEQUENCE_ENTRY, schema, catalog, info.GetSequenceName()), + state(make_shared_ptr(info)) { this->temporary = info.temporary; this->comment = info.comment; this->tags = info.tags; @@ -33,70 +34,73 @@ unique_ptr SequenceCatalogEntry::Copy(ClientContext &context) cons auto &cast_info = info_copy->Cast(); auto result = make_uniq(catalog, schema, cast_info); - result->data = GetData(); + // all versions of the entry share the sequence state + result->state = state; return std::move(result); } SequenceData SequenceCatalogEntry::GetData() const { - lock_guard seqlock(lock); - return data; + lock_guard seqlock(state->lock); + return state->data; } int64_t SequenceCatalogEntry::CurrentValue() { - lock_guard seqlock(lock); + lock_guard seqlock(state->lock); int64_t result; - if (!data.last_value) { + if (!state->data.last_value) { throw SequenceException("currval: sequence is not yet defined in this session"); } - result = data.last_value.value(); + result = state->data.last_value.value(); return result; } int64_t SequenceCatalogEntry::NextValue(DuckTransaction &transaction) { - lock_guard seqlock(lock); + lock_guard seqlock(state->lock); int64_t result; - result = data.counter; + result = state->data.counter; int64_t next_counter; - bool overflow = !TryAddOperator::Operation(data.counter, data.increment, next_counter); - if (data.cycle) { + bool overflow = !TryAddOperator::Operation(state->data.counter, state->data.increment, next_counter); + if (state->data.cycle) { if (overflow) { - next_counter = data.increment < 0 ? data.max_value : data.min_value; - } else if (next_counter < data.min_value) { - next_counter = data.max_value; - } else if (next_counter > data.max_value) { - next_counter = data.min_value; + next_counter = state->data.increment < 0 ? state->data.max_value : state->data.min_value; + } else if (next_counter < state->data.min_value) { + next_counter = state->data.max_value; + } else if (next_counter > state->data.max_value) { + next_counter = state->data.min_value; } } else { - if (result < data.min_value || (overflow && data.increment < 0)) { - throw SequenceException("nextval: reached minimum value of sequence %s (%lld)", name, data.min_value); + if (result < state->data.min_value || (overflow && state->data.increment < 0)) { + throw SequenceException("nextval: reached minimum value of sequence %s (%lld)", name, + state->data.min_value); } - if (result > data.max_value || overflow) { - throw SequenceException("nextval: reached maximum value of sequence \"%s\" (%lld)", name, data.max_value); + if (result > state->data.max_value || overflow) { + throw SequenceException("nextval: reached maximum value of sequence \"%s\" (%lld)", name, + state->data.max_value); } } - data.counter = next_counter; - data.last_value = result; - data.usage_count++; + state->data.counter = next_counter; + state->data.last_value = result; + state->data.usage_count++; if (!temporary) { - transaction.PushSequenceUsage(*this, data); + transaction.PushSequenceUsage(*this, state->data); } return result; } int64_t SequenceCatalogEntry::SetValue(DuckTransaction &transaction, int64_t value, bool is_called) { { - lock_guard seqlock(lock); - if (value < data.min_value || value > data.max_value) { + lock_guard seqlock(state->lock); + if (value < state->data.min_value || value > state->data.max_value) { throw SequenceException("setval: value %lld is out of bounds for sequence %s (%lld..%lld)", value, name, - data.min_value, data.max_value); + state->data.min_value, state->data.max_value); } - data.counter = value; + state->data.counter = value; if (!is_called) { - data.usage_count++; + state->data.usage_count++; if (!temporary) { - transaction.PushSequenceUsage(*this, data); + transaction.PushSequenceUsage(*this, state->data); } return value; } @@ -107,27 +111,27 @@ int64_t SequenceCatalogEntry::SetValue(DuckTransaction &transaction, int64_t val } void SequenceCatalogEntry::ReplayValue(uint64_t v_usage_count, int64_t v_counter, optional last_value) { - if (v_usage_count > data.usage_count) { - data.usage_count = v_usage_count; - data.counter = v_counter; - data.last_value = last_value; + if (v_usage_count > state->data.usage_count) { + state->data.usage_count = v_usage_count; + state->data.counter = v_counter; + state->data.last_value = last_value; } } void SequenceCatalogEntry::DeriveLastValue() { - lock_guard seqlock(lock); - if (data.last_value || data.usage_count == 0) { + lock_guard seqlock(state->lock); + if (state->data.last_value || state->data.usage_count == 0) { return; } int64_t last_value; - if (!TrySubtractOperator::Operation(data.counter, data.increment, last_value)) { + if (!TrySubtractOperator::Operation(state->data.counter, state->data.increment, last_value)) { return; } // a counter that wrapped around in a cycle does not tell us the last value - if (last_value < data.min_value || last_value > data.max_value) { + if (last_value < state->data.min_value || last_value > state->data.max_value) { return; } - data.last_value = last_value; + state->data.last_value = last_value; } unique_ptr SequenceCatalogEntry::GetInfo() const { diff --git a/src/duckdb/src/common/enum_util.cpp b/src/duckdb/src/common/enum_util.cpp index 81770861c..97e7bdbdb 100644 --- a/src/duckdb/src/common/enum_util.cpp +++ b/src/duckdb/src/common/enum_util.cpp @@ -2341,19 +2341,20 @@ const StringUtil::EnumStringLiteral *GetExpressionTailTypeValues() { { static_cast(ExpressionTailType::IS_TEST), "IS_TEST" }, { static_cast(ExpressionTailType::DISTINCT), "DISTINCT" }, { static_cast(ExpressionTailType::COMPARISON), "COMPARISON" }, - { static_cast(ExpressionTailType::OTHER_OPERATOR), "OTHER_OPERATOR" } + { static_cast(ExpressionTailType::OTHER_OPERATOR), "OTHER_OPERATOR" }, + { static_cast(ExpressionTailType::CAST), "CAST" } }; return values; } template<> const char* EnumUtil::ToChars(ExpressionTailType value) { - return StringUtil::EnumToString(GetExpressionTailTypeValues(), 4, "ExpressionTailType", static_cast(value)); + return StringUtil::EnumToString(GetExpressionTailTypeValues(), 5, "ExpressionTailType", static_cast(value)); } template<> ExpressionTailType EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetExpressionTailTypeValues(), 4, "ExpressionTailType", value)); + return static_cast(StringUtil::StringToEnum(GetExpressionTailTypeValues(), 5, "ExpressionTailType", value)); } const StringUtil::EnumStringLiteral *GetExpressionTypeValues() { @@ -4594,19 +4595,20 @@ const StringUtil::EnumStringLiteral *GetOptimizerTypeValues() { { static_cast(OptimizerType::DISTINCT_AGGREGATE_REWRITE), "DISTINCT_AGGREGATE_REWRITE" }, { static_cast(OptimizerType::AGGREGATE_REUSE), "AGGREGATE_REUSE" }, { static_cast(OptimizerType::PROJECTION_PLACEMENT), "PROJECTION_PLACEMENT" }, - { static_cast(OptimizerType::REDUNDANT_ORDER_KEYS), "REDUNDANT_ORDER_KEYS" } + { static_cast(OptimizerType::REDUNDANT_ORDER_KEYS), "REDUNDANT_ORDER_KEYS" }, + { static_cast(OptimizerType::DISJUNCTIVE_JOIN_REWRITER), "DISJUNCTIVE_JOIN_REWRITER" } }; return values; } template<> const char* EnumUtil::ToChars(OptimizerType value) { - return StringUtil::EnumToString(GetOptimizerTypeValues(), 47, "OptimizerType", static_cast(value)); + return StringUtil::EnumToString(GetOptimizerTypeValues(), 48, "OptimizerType", static_cast(value)); } template<> OptimizerType EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetOptimizerTypeValues(), 47, "OptimizerType", value)); + return static_cast(StringUtil::StringToEnum(GetOptimizerTypeValues(), 48, "OptimizerType", value)); } const StringUtil::EnumStringLiteral *GetOrderByColumnTypeValues() { diff --git a/src/duckdb/src/common/enums/optimizer_type.cpp b/src/duckdb/src/common/enums/optimizer_type.cpp index a11328ff2..718e4f3eb 100644 --- a/src/duckdb/src/common/enums/optimizer_type.cpp +++ b/src/duckdb/src/common/enums/optimizer_type.cpp @@ -59,6 +59,7 @@ static const DefaultOptimizerType internal_optimizer_types[] = { {"aggregate_reuse", OptimizerType::AGGREGATE_REUSE}, {"projection_placement", OptimizerType::PROJECTION_PLACEMENT}, {"redundant_order_keys", OptimizerType::REDUNDANT_ORDER_KEYS}, + {"disjunctive_join_rewriter", OptimizerType::DISJUNCTIVE_JOIN_REWRITER}, {nullptr, OptimizerType::INVALID}}; string OptimizerTypeToString(OptimizerType type) { diff --git a/src/duckdb/src/common/multi_file/multi_file_reader.cpp b/src/duckdb/src/common/multi_file/multi_file_reader.cpp index 58641a287..c791368b7 100644 --- a/src/duckdb/src/common/multi_file/multi_file_reader.cpp +++ b/src/duckdb/src/common/multi_file/multi_file_reader.cpp @@ -578,7 +578,8 @@ bool MultiFileReader::ParseOption(const Identifier &key, const Value &val, Multi if (options.schema.empty()) { throw BinderException("'schema' cannot be empty"); } - options.auto_detect_hive_partitioning = false; + // A given schema is not detected from the paths; that is decided where the schema is bound, so + // auto_detect_hive_partitioning keeps meaning what it says - whether the CALLER left it to us } else if (key == "file_row_number") { if (val.IsNull()) { throw InvalidInputException("Cannot use NULL as argument for %s", key); @@ -620,8 +621,9 @@ bool MultiFileReader::Bind(MultiFileOptions &options, MultiFileList &files, vect // the default MultiFileReader has no schema information of its own - the files have to be bound return false; } - if (options.union_by_name || options.hive_partitioning) { - throw BinderException("'schema' cannot be combined with union_by_name=true or hive_partitioning=true"); + if (options.union_by_name) { + // both decide the schema of the scan, from the files and from the option respectively + throw BinderException("'schema' cannot be combined with union_by_name=true"); } // the files are mapped onto the schema by field id when its columns are identified by INTEGER, by name otherwise const bool by_field_id = options.schema[0].identifier.type().id() == LogicalTypeId::INTEGER; @@ -730,9 +732,18 @@ void MultiFileReader::BindOptions(MultiFileOptions &options, MultiFileList &file "name also exists. Try setting a different name: filename=''", options.filename_column); } + auto hive_type = options.GetHiveLogicalType(part.first); + // the path does not get to change a type the schema declares + if (!options.schema.empty() && idx < bind_data.schema.size() && + !bind_data.schema[idx].type.EqualsIncludingCollation(hive_type)) { + throw BinderException("'schema' declares column \"%s\" as %s, but its hive partition type is %s - " + "set the partition type with hive_types", + bind_data.schema[idx].name.GetIdentifierName(), + bind_data.schema[idx].type.ToString(), hive_type.ToString()); + } // hive partitioning column also exists in file - override hive_partitioning_index = idx; - return_types[idx] = options.GetHiveLogicalType(part.first); + return_types[idx] = hive_type; } else { // hive partitioning column does not exist in file - add a new column containing the key hive_partitioning_index = names.size(); @@ -1330,7 +1341,8 @@ void MultiFileOptions::AutoDetectHiveTypesInternal(MultiFileList &files, ClientC hive_types_schema.insert(make_pair(entry.first, std::move(entry.second))); } } -void MultiFileOptions::AutoDetectHivePartitioning(MultiFileList &files, ClientContext &context) { +void MultiFileOptions::AutoDetectHivePartitioning(MultiFileList &files, ClientContext &context, + bool detect_partitioning) { if (files.GetExpandResult() == FileExpandResult::NO_FILES) { return; } @@ -1344,7 +1356,7 @@ void MultiFileOptions::AutoDetectHivePartitioning(MultiFileList &files, ClientCo hive_partitioning = true; auto_detect_hive_partitioning = false; } - if (auto_detect_hive_partitioning) { + if (detect_partitioning && auto_detect_hive_partitioning) { hive_partitioning = AutoDetectHivePartitioningInternal(files, context); } if (hive_partitioning && hive_types_autocast) { diff --git a/src/duckdb/src/common/operator/convert_to_string.cpp b/src/duckdb/src/common/operator/convert_to_string.cpp index acd77bc57..0130fd0a3 100644 --- a/src/duckdb/src/common/operator/convert_to_string.cpp +++ b/src/duckdb/src/common/operator/convert_to_string.cpp @@ -1,6 +1,7 @@ #include "duckdb/common/operator/convert_to_string.hpp" #include "duckdb/common/operator/string_cast.hpp" #include "duckdb/common/types/string_heap.hpp" +#include "duckdb/common/types/bignum.hpp" namespace duckdb { @@ -95,4 +96,9 @@ string ConvertToString::Operation(string_t input) { return input.GetString(); } +template <> +string ConvertToString::Operation(bignum_t input) { + return Bignum::BignumToVarchar(input); +} + } // namespace duckdb diff --git a/src/duckdb/src/common/row_operations/row_aggregate.cpp b/src/duckdb/src/common/row_operations/row_aggregate.cpp index 447a38c7a..9df3f28d6 100644 --- a/src/duckdb/src/common/row_operations/row_aggregate.cpp +++ b/src/duckdb/src/common/row_operations/row_aggregate.cpp @@ -1,4 +1,5 @@ #include "duckdb/catalog/catalog_entry/aggregate_function_catalog_entry.hpp" +#include "duckdb/common/array.hpp" #include "duckdb/common/row_operations/row_operations.hpp" #include "duckdb/common/vector/flat_vector.hpp" #include "duckdb/common/types/row/tuple_data_layout.hpp" @@ -13,22 +14,26 @@ void RowOperations::InitializeStates(TupleDataLayout &layout, Vector &addresses, } auto pointers = FlatVector::GetData(addresses); auto &offsets = layout.GetOffsets(); - auto aggr_idx = layout.ColumnCount(); - - for (const auto &aggr : layout.GetAggregates()) { - AggregateStateInput state_input(aggr.function, aggr.GetFunctionData()); - if (sel.IsSet()) { - for (idx_t i = 0; i < count; ++i) { - data_ptr_t state_ptr = pointers[sel.get_index_unsafe(i)] + offsets[aggr_idx]; - aggr.function.GetStateInitCallback()(state_input, &state_ptr, 1); - } - } else { - for (idx_t i = 0; i < count; ++i) { - data_ptr_t state_ptr = pointers[i] + offsets[aggr_idx]; - aggr.function.GetStateInitCallback()(state_input, &state_ptr, 1); + static constexpr idx_t INITIALIZE_BATCH_SIZE = 128; + array state_pointers; + + for (idx_t start = 0; start < count; start += INITIALIZE_BATCH_SIZE) { + const auto batch_count = MinValue(INITIALIZE_BATCH_SIZE, count - start); + auto aggr_idx = layout.ColumnCount(); + for (const auto &aggr : layout.GetAggregates()) { + AggregateStateInput state_input(aggr.function, aggr.GetFunctionData()); + const auto offset = offsets[aggr_idx++]; + if (sel.IsSet()) { + for (idx_t i = 0; i < batch_count; ++i) { + state_pointers[i] = pointers[sel.get_index_unsafe(start + i)] + offset; + } + } else { + for (idx_t i = 0; i < batch_count; ++i) { + state_pointers[i] = pointers[start + i] + offset; + } } + aggr.function.GetStateInitCallback()(state_input, state_pointers.data(), batch_count); } - ++aggr_idx; } } diff --git a/src/duckdb/src/common/types.cpp b/src/duckdb/src/common/types.cpp index 4aab4a9a4..0b56aec34 100644 --- a/src/duckdb/src/common/types.cpp +++ b/src/duckdb/src/common/types.cpp @@ -1906,6 +1906,13 @@ LogicalType ArrayType::ConvertToList(const LogicalType &type) { } return LogicalType::STRUCT(children); } + case LogicalTypeId::TUPLE: { + auto children = StructType::GetChildTypes(type); + for (auto &child : children) { + child.second = ConvertToList(child.second); + } + return LogicalType::TUPLE(children); + } case LogicalTypeId::MAP: { auto key_type = ConvertToList(MapType::KeyType(type)); auto value_type = ConvertToList(MapType::ValueType(type)); diff --git a/src/duckdb/src/common/types/bit.cpp b/src/duckdb/src/common/types/bit.cpp index 7fd24b723..14804328c 100644 --- a/src/duckdb/src/common/types/bit.cpp +++ b/src/duckdb/src/common/types/bit.cpp @@ -358,34 +358,18 @@ idx_t Bit::BitCount(bitstring_t bits) { } idx_t Bit::BitPosition(bitstring_t substring, bitstring_t bits) { - const char *buf = bits.GetData(); - auto len = bits.GetSize(); + auto bits_len = BitLength(bits); auto substr_len = BitLength(substring); - idx_t substr_idx = 0; - - for (idx_t bit_idx = GetBitPadding(bits); bit_idx < 8; bit_idx++) { - idx_t bit = buf[1] & (1 << (7 - bit_idx)) ? 1 : 0; - if (bit == GetBit(substring, substr_idx)) { + if (substr_len > bits_len) { + return 0; + } + for (idx_t start = 0; start + substr_len <= bits_len; start++) { + idx_t substr_idx = 0; + while (substr_idx < substr_len && GetBit(bits, start + substr_idx) == GetBit(substring, substr_idx)) { substr_idx++; - if (substr_idx == substr_len) { - return (bit_idx - GetBitPadding(bits)) - substr_len + 2; - } - } else { - substr_idx = 0; } - } - - for (idx_t byte_idx = 2; byte_idx < len; byte_idx++) { - for (idx_t bit_idx = 0; bit_idx < 8; bit_idx++) { - idx_t bit = buf[byte_idx] & (1 << (7 - bit_idx)) ? 1 : 0; - if (bit == GetBit(substring, substr_idx)) { - substr_idx++; - if (substr_idx == substr_len) { - return (((byte_idx - 1) * 8) + bit_idx - GetBitPadding(bits)) - substr_len + 2; - } - } else { - substr_idx = 0; - } + if (substr_idx == substr_len) { + return start + 1; } } return 0; diff --git a/src/duckdb/src/common/types/row/tuple_data_scatter_gather.cpp b/src/duckdb/src/common/types/row/tuple_data_scatter_gather.cpp index dabe91869..0abbd2169 100644 --- a/src/duckdb/src/common/types/row/tuple_data_scatter_gather.cpp +++ b/src/duckdb/src/common/types/row/tuple_data_scatter_gather.cpp @@ -1771,6 +1771,35 @@ static void TupleDataCollectionWithinCollectionGather(const TupleDataLayout &lay //------------------------------------------------------------------------------ // Special cases for arrays //------------------------------------------------------------------------------ +// Casts the rows at target_sel in cast_vector to the target type - only the rows at target_sel are written +static void TupleDataCastToArray(Vector &cast_vector, Vector &target, const SelectionVector &target_sel, + const idx_t scan_count) { + if (scan_count == 0) { + return; + } + // the cast overwrites the validity of the whole target, so it can only be used if no NULLs were set + bool identity = FlatVector::Validity(target).CannotHaveNull(); + for (idx_t i = 0; i < scan_count && identity; i++) { + identity = target_sel.get_index(i) == i; + } + if (identity) { + VectorOperations::DefaultCast(cast_vector, target, scan_count); + return; + } + // cast the gathered rows into a dense vector, and copy them to their positions in the target + Vector source(cast_vector, target_sel, scan_count); + Vector dense(target.GetType(), scan_count); + VectorOperations::DefaultCast(source, dense, scan_count); + idx_t run_start = 0; + for (idx_t i = 1; i <= scan_count; i++) { + if (i < scan_count && target_sel.get_index(i) == target_sel.get_index(i - 1) + 1) { + continue; + } + VectorOperations::Copy(dense, target, i, run_start, target_sel.get_index(run_start)); + run_start = i; + } +} + // A gather function that wraps another gather function and casts the result to the target array type static void TupleDataCastToArrayListGather(const TupleDataLayout &layout, Vector &row_locations, const idx_t col_idx, const SelectionVector &scan_sel, const idx_t scan_count, Vector &target, @@ -1780,13 +1809,13 @@ static void TupleDataCastToArrayListGather(const TupleDataLayout &layout, Vector // Reuse the cached cast vector TupleDataListGather(layout, row_locations, col_idx, scan_sel, scan_count, *cached_cast_vector, target_sel, cached_cast_vector, child_functions); - VectorOperations::DefaultCast(*cached_cast_vector, target, scan_count); + TupleDataCastToArray(*cached_cast_vector, target, target_sel, scan_count); } else { // Otherwise, create a new temporary cast vector Vector cast_vector(ArrayType::ConvertToList(target.GetType())); TupleDataListGather(layout, row_locations, col_idx, scan_sel, scan_count, cast_vector, target_sel, &cast_vector, child_functions); - VectorOperations::DefaultCast(cast_vector, target, scan_count); + TupleDataCastToArray(cast_vector, target, target_sel, scan_count); } } @@ -1798,13 +1827,13 @@ static void TupleDataCastToArrayStructGather(const TupleDataLayout &layout, Vect // Reuse the cached cast vector TupleDataStructGather(layout, row_locations, col_idx, scan_sel, scan_count, *cached_cast_vector, target_sel, cached_cast_vector, child_functions); - VectorOperations::DefaultCast(*cached_cast_vector, target, scan_count); + TupleDataCastToArray(*cached_cast_vector, target, target_sel, scan_count); } else { // Otherwise, create a new temporary cast vector Vector cast_vector(ArrayType::ConvertToList(target.GetType())); TupleDataStructGather(layout, row_locations, col_idx, scan_sel, scan_count, cast_vector, target_sel, &cast_vector, child_functions); - VectorOperations::DefaultCast(cast_vector, target, scan_count); + TupleDataCastToArray(cast_vector, target, target_sel, scan_count); } } diff --git a/src/duckdb/src/common/types/time.cpp b/src/duckdb/src/common/types/time.cpp index 04396b7a1..299708a0e 100644 --- a/src/duckdb/src/common/types/time.cpp +++ b/src/duckdb/src/common/types/time.cpp @@ -278,10 +278,11 @@ string Time::ToString(dtime_t time) { return string(buffer.get(), length); } -string Time::ToUTCOffset(int hour_offset, int minute_offset) { - dtime_t time((hour_offset * Interval::MINS_PER_HOUR + minute_offset) * Interval::MICROS_PER_MINUTE); +string Time::ToUTCOffset(int hour_offset, int minute_offset, int second_offset) { + dtime_t time(((hour_offset * Interval::MINS_PER_HOUR + minute_offset) * Interval::SECS_PER_MINUTE + second_offset) * + Interval::MICROS_PER_SEC); - char buffer[1 + 2 + 1 + 2]; + char buffer[1 + 2 + 1 + 2 + 1 + 2]; idx_t length = 0; buffer[length++] = (time.value < 0 ? '-' : '+'); time.value = std::abs(time.value); @@ -291,11 +292,16 @@ string Time::ToUTCOffset(int hour_offset, int minute_offset) { TimeToStringCast::FormatTwoDigits(buffer + length, time_units[0]); length += 2; - if (time_units[1]) { + if (time_units[1] || time_units[2]) { buffer[length++] = ':'; TimeToStringCast::FormatTwoDigits(buffer + length, time_units[1]); length += 2; } + if (time_units[2]) { + buffer[length++] = ':'; + TimeToStringCast::FormatTwoDigits(buffer + length, time_units[2]); + length += 2; + } return string(buffer, length); } diff --git a/src/duckdb/src/common/types/timestamp.cpp b/src/duckdb/src/common/types/timestamp.cpp index 9af372bf1..13bf112a4 100644 --- a/src/duckdb/src/common/types/timestamp.cpp +++ b/src/duckdb/src/common/types/timestamp.cpp @@ -450,6 +450,10 @@ timestamp_t Timestamp::FromEpochMsPossiblyInfinite(int64_t ms) { if (!TryMultiplyOperator::Operation(ms, Interval::MICROS_PER_MSEC, result)) { throw ConversionException("Could not convert Timestamp(MS) to Timestamp(US)"); } + // the first midnight of Timestamp::MIN_YEAR - smaller values are not valid timestamps + if (result < -9223372022400000000LL) { + throw ConversionException("Could not convert Timestamp(MS) to Timestamp(US)"); + } return timestamp_t(result); } diff --git a/src/duckdb/src/common/types/variant/parquet_variant_encoding.cpp b/src/duckdb/src/common/types/variant/parquet_variant_encoding.cpp index 6bde981f2..12b84ff0e 100644 --- a/src/duckdb/src/common/types/variant/parquet_variant_encoding.cpp +++ b/src/duckdb/src/common/types/variant/parquet_variant_encoding.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -216,20 +217,26 @@ vector GetChildIndices(const UnifiedVariantVectorData &variant, idx_t row for (idx_t i = 0; i < nested_data.child_count; i++) { child_indices.push_back(i); } - return child_indices; - } - auto shredded_fields = shredding_state->ObjectFields(); + } else { + auto shredded_fields = shredding_state->ObjectFields(); - for (idx_t i = 0; i < nested_data.child_count; i++) { - auto keys_index = variant.GetKeysIndex(row, i + nested_data.children_idx); - auto &key = variant.GetKey(row, keys_index); + for (idx_t i = 0; i < nested_data.child_count; i++) { + auto keys_index = variant.GetKeysIndex(row, i + nested_data.children_idx); + auto &key = variant.GetKey(row, keys_index); - if (shredded_fields.count(key.GetString())) { - //! This field is shredded on, omit it from the value - continue; + if (shredded_fields.count(key.GetString())) { + //! This field is shredded on, omit it from the value + continue; + } + child_indices.push_back(i); } - child_indices.push_back(i); } + // Object field IDs and offsets must follow UTF-8 field name order, even with an unsorted dictionary. + std::sort(child_indices.begin(), child_indices.end(), [&](idx_t left, idx_t right) { + auto left_key = variant.GetKeysIndex(row, left + nested_data.children_idx); + auto right_key = variant.GetKeysIndex(row, right + nested_data.children_idx); + return variant.GetKey(row, left_key) < variant.GetKey(row, right_key); + }); return child_indices; } diff --git a/src/duckdb/src/execution/aggregate_hashtable.cpp b/src/duckdb/src/execution/aggregate_hashtable.cpp index 2b2b1ba5b..8210371f7 100644 --- a/src/duckdb/src/execution/aggregate_hashtable.cpp +++ b/src/duckdb/src/execution/aggregate_hashtable.cpp @@ -830,6 +830,9 @@ static void GroupedAggregateHashTableInnerLoop(ht_entry_t *const entries, const } } +#if defined(__GNUC__) +__attribute__((aligned(128))) +#endif idx_t GroupedAggregateHashTable::FindOrCreateGroupsInternal(DataChunk &groups, Vector &group_hashes_v, Vector &addresses_v, SelectionVector &new_groups_out) { D_ASSERT(state.initialized && state.owner.get() == this); @@ -897,15 +900,10 @@ idx_t GroupedAggregateHashTable::FindOrCreateGroupsInternal(DataChunk &groups, V const auto ht_offsets = FlatVector::GetDataMutable(state.ht_offsets); const auto hash_salts = FlatVector::GetDataMutable(state.hash_salts); - // We also compute the occupied count, which is essentially useless. - // However, this loop is branchless, while the main lookup loop below is not. - // So, by doing the lookups here, we better amortize cache misses. - idx_t occupied_count = 0; for (idx_t r = 0; r < chunk_size; r++) { const auto &hash = hashes[r].GetValue(); auto &ht_offset = ht_offsets[r]; ht_offset = ApplyBitMask(hash); - occupied_count += entries[ht_offset].IsOccupied(); // Lookup D_ASSERT(ht_offset == hash % capacity); hash_salts[r] = ht_entry_t::ExtractSalt(hash); } @@ -933,13 +931,6 @@ idx_t GroupedAggregateHashTable::FindOrCreateGroupsInternal(DataChunk &groups, V } new_group_count += new_entry_count; - if (DUCKDB_UNLIKELY(occupied_count > new_entry_count + need_compare_count)) { - // We use the useless occupied_count we summed above here so the variable is used, - // and the compiler cannot optimize away the vectorized lookups above. This should never be triggered. - throw InternalException("Internal validation failed in GroupedAggregateHashTable"); - } - occupied_count = 0; // Have to set to 0 for next iterations - if (new_entry_count != 0) { // Append everything that belongs to an empty group optional_ptr data; diff --git a/src/duckdb/src/execution/index/fixed_size_allocator.cpp b/src/duckdb/src/execution/index/fixed_size_allocator.cpp index fb31792a0..04ec50e21 100644 --- a/src/duckdb/src/execution/index/fixed_size_allocator.cpp +++ b/src/duckdb/src/execution/index/fixed_size_allocator.cpp @@ -5,6 +5,11 @@ namespace duckdb { +void FixedSizeAllocator::ThrowInvalidPointer(const IndexPointer ptr) const { + throw DataCorruptionException("Index pointer to buffer %llu, segment %llu does not point to an allocated segment", + ptr.GetBufferId(), ptr.GetOffset()); +} + FixedSizeAllocator::FixedSizeAllocator(const idx_t segment_size, BlockManager &block_manager, MemoryTag memory_tag) : block_manager(block_manager), buffer_manager(block_manager.buffer_manager), memory_tag(memory_tag), segment_size(segment_size), total_segment_count(0) { @@ -88,7 +93,9 @@ void FixedSizeAllocator::Free(const IndexPointer ptr) { auto offset = ptr.GetOffset(); auto buffer_it = buffers.find(buffer_id); - D_ASSERT(buffer_it != buffers.end()); + if (offset >= available_segments_per_buffer || buffer_it == buffers.end()) { + ThrowInvalidPointer(ptr); + } auto &buffer = buffer_it->second; buffer->FreeSegment(offset, available_segments_per_buffer); diff --git a/src/duckdb/src/execution/index/fixed_size_buffer.cpp b/src/duckdb/src/execution/index/fixed_size_buffer.cpp index d6cb49962..5ae729114 100644 --- a/src/duckdb/src/execution/index/fixed_size_buffer.cpp +++ b/src/duckdb/src/execution/index/fixed_size_buffer.cpp @@ -63,7 +63,7 @@ FixedSizeBuffer::FixedSizeBuffer(BlockManager &block_manager, const idx_t segmen : block_manager(block_manager), readers(0), segment_count(segment_count), allocation_size(allocation_size), dirty(false), vacuum(false), loaded(false), block_pointer(block_pointer) { D_ASSERT(block_pointer.IsValid()); - block_handle = block_manager.RegisterBlock(block_pointer.block_id); + block_handle = block_manager.RegisterBlock(block_pointer.block_id, MemoryTag::ART_INDEX); D_ASSERT(block_handle->BlockId() < MAXIMUM_BLOCK); } diff --git a/src/duckdb/src/execution/operator/csv_scanner/scanner/string_value_scanner.cpp b/src/duckdb/src/execution/operator/csv_scanner/scanner/string_value_scanner.cpp index 96773a018..b8981f986 100644 --- a/src/duckdb/src/execution/operator/csv_scanner/scanner/string_value_scanner.cpp +++ b/src/duckdb/src/execution/operator/csv_scanner/scanner/string_value_scanner.cpp @@ -1329,9 +1329,9 @@ void StringValueScanner::ProcessExtraRow() { } result.SetQuoted(result, iterator.pos.buffer_pos); iterator.pos.buffer_pos++; - while (state_machine->transition_array - .skip_quoted[static_cast(buffer_handle_ptr[iterator.pos.buffer_pos])] && - iterator.pos.buffer_pos < to_pos - 1) { + while (iterator.pos.buffer_pos < to_pos - 1 && + state_machine->transition_array + .skip_quoted[static_cast(buffer_handle_ptr[iterator.pos.buffer_pos])]) { iterator.pos.buffer_pos++; } break; diff --git a/src/duckdb/src/execution/operator/join/physical_asof_join.cpp b/src/duckdb/src/execution/operator/join/physical_asof_join.cpp index 7a8913391..dc2e967f2 100644 --- a/src/duckdb/src/execution/operator/join/physical_asof_join.cpp +++ b/src/duckdb/src/execution/operator/join/physical_asof_join.cpp @@ -866,8 +866,13 @@ struct SortKeyPrefixComparison { break; case SortKeyPrefixComparisonType::NESTED: decoded.Reset(); - lhs_width = CreateSortKeyHelpers::DecodeSortKey(lhs_key, vec, 0, modifiers); - rhs_width = CreateSortKeyHelpers::DecodeSortKey(rhs_key, vec, 1, modifiers); + // Decode from the current column, not from the start of the whole key. + lhs_width = CreateSortKeyHelpers::DecodeSortKey( + string_t(lhs_ptr, UnsafeNumericCast(lhs_key.GetData() + lhs_key.GetSize() - lhs_ptr)), + vec, 0, modifiers); + rhs_width = CreateSortKeyHelpers::DecodeSortKey( + string_t(rhs_ptr, UnsafeNumericCast(rhs_key.GetData() + rhs_key.GetSize() - rhs_ptr)), + vec, 1, modifiers); cmp = memcmp(lhs_ptr, rhs_ptr, MinValue(lhs_width, rhs_width)); if (!cmp) { cmp = (rhs_width < lhs_width) - (lhs_width < rhs_width); diff --git a/src/duckdb/src/execution/operator/join/physical_hash_join.cpp b/src/duckdb/src/execution/operator/join/physical_hash_join.cpp index 81ff1e45d..76961cf4e 100644 --- a/src/duckdb/src/execution/operator/join/physical_hash_join.cpp +++ b/src/duckdb/src/execution/operator/join/physical_hash_join.cpp @@ -34,6 +34,7 @@ #include "duckdb/planner/expression/bound_conjunction_expression.hpp" #include "duckdb/planner/expression/bound_constant_expression.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" +#include "duckdb/execution/operator/projection/physical_projection.hpp" #include "duckdb/planner/expression/bound_reference_expression.hpp" #include "duckdb/planner/filter/expression_filter.hpp" #include "duckdb/planner/filter/table_filter_functions.hpp" @@ -331,6 +332,31 @@ static bool BuildSideHasMultipleSources(const PhysicalOperator &op) { return false; } +//! Whether a column of the build side may arrive as a dictionary for some chunks only, e.g. when a projection computes +//! it (CASE) or when it is a probe column of a hash join below. Its row-store slot can then not be narrowed +static bool BuildColumnIsComputed(const PhysicalOperator &op, idx_t column_idx) { + switch (op.type) { + case PhysicalOperatorType::PROJECTION: { + auto &select_list = op.Cast().select_list; + if (column_idx >= select_list.size()) { + return true; + } + auto &expr = *select_list[column_idx]; + if (expr.GetExpressionClass() != ExpressionClass::BOUND_REF) { + return true; + } + return BuildColumnIsComputed(op.children[0].get(), expr.Cast().Index()); + } + case PhysicalOperatorType::FILTER: + return BuildColumnIsComputed(op.children[0].get(), column_idx); + case PhysicalOperatorType::HASH_JOIN: + // a probe column is forwarded as it arrives, but read back from the row store when the probe side spills + return column_idx < op.Cast().lhs_output_columns.col_idxs.size(); + default: + return false; + } +} + //! Synchronisation state for the first-chunk publication of the TupleDataLayout: the canonical layout //! shared by all per-thread JHTs, plus the per-column decision on whether to narrow the row-store slot. struct LayoutGate { @@ -425,6 +451,9 @@ class HashJoinGlobalSinkState : public GlobalSinkState { can_use_perfect_hash = use_perfect_hash; // Multiple build producers can change dictionaries after the layout has been published. build_side_multi_source = BuildSideHasMultipleSources(op.children[1].get()); + for (auto &column_idx : op.payload_columns.col_idxs) { + computed_build_columns.push_back(BuildColumnIsComputed(op.children[1].get(), column_idx)); + } // For external hash join external = Settings::Get(context); // Set probe types @@ -564,6 +593,8 @@ class HashJoinGlobalSinkState : public GlobalSinkState { //! True iff the build subtree funnels multiple producer pipelines into this sink (UNION ALL / //! recursive CTE); disables dict-surviving because the first-chunk layout election is unsound there. bool build_side_multi_source = false; + //! Per payload column: whether a projection on the build side computes it (see BuildColumnIsComputed) + vector computed_build_columns; }; unique_ptr JoinFilterPushdownInfo::GetLocalState(JoinFilterGlobalState &gstate) const { @@ -727,6 +758,9 @@ void HashJoinGlobalSinkState::PublishLayoutIfFirst(HashJoinLocalSinkState &lstat DictSurvivingInfo info; info.columns.resize(build_types.size()); for (idx_t col = 0; col < MinValue(build_types.size(), payload_chunk.ColumnCount()); col++) { + if (col < computed_build_columns.size() && computed_build_columns[col]) { + continue; + } auto &incoming = payload_chunk.data[col]; const auto index_width = lstate.hash_table->GetDictSurvivingIndexWidth(col, incoming); if (index_width != 0) { diff --git a/src/duckdb/src/execution/operator/join/physical_iejoin.cpp b/src/duckdb/src/execution/operator/join/physical_iejoin.cpp index 0a525e3c2..d28a86e5f 100644 --- a/src/duckdb/src/execution/operator/join/physical_iejoin.cpp +++ b/src/duckdb/src/execution/operator/join/physical_iejoin.cpp @@ -1198,12 +1198,21 @@ void IEJoinLocalSourceState::ResolveComplexJoin(ExecutionContext &context, DataC // Apply any arbitrary predicate MergePayloads(chunk); + SelectionVector match_sel; if (op.predicate) { - sel = ApplyArbitraryPredicate(chunk); + // The predicate selection is relative to the rows that passed the tail conditions, + // so combine the two to index the original join pairs + auto tail_sel = sel; + auto predicate_sel = ApplyArbitraryPredicate(chunk); result_count = chunk.size(); if (!result_count) { continue; } + match_sel.Initialize(result_count); + for (idx_t i = 0; i < result_count; i++) { + match_sel.set_index(i, tail_sel->get_index(predicate_sel->get_index(i))); + } + sel = &match_sel; } // We need all of the data to compute other predicates, diff --git a/src/duckdb/src/execution/operator/persistent/physical_copy_to_file.cpp b/src/duckdb/src/execution/operator/persistent/physical_copy_to_file.cpp index bf6949364..911dc94c0 100644 --- a/src/duckdb/src/execution/operator/persistent/physical_copy_to_file.cpp +++ b/src/duckdb/src/execution/operator/persistent/physical_copy_to_file.cpp @@ -488,6 +488,7 @@ class CopyToFileGlobalState : public GlobalSinkState { DUCKDB_EXCLUDES(lock); FileStateHandle FinalizeFileStateLocked(FileStateHandle file_state) DUCKDB_REQUIRES(lock); void FinalizeFileState(FileStateHandle file_state) DUCKDB_EXCLUDES(lock); + void AbandonFileState(FileStateHandle file_state) noexcept DUCKDB_EXCLUDES(lock); FileStateHandle TryFinalizeOwnedFileStateLocked() DUCKDB_REQUIRES(lock); void TryFinalizeOwnedFileState() DUCKDB_EXCLUDES(lock); @@ -551,6 +552,7 @@ class PartitionedCopyLocalState; class CopyToFileLocalState : public LocalSinkState { public: CopyToFileLocalState(const PhysicalCopyToFile &op_p, ExecutionContext &context_p, CopyToFileGlobalState &gstate_p); + ~CopyToFileLocalState() override; public: const PhysicalCopyToFile &op; @@ -3806,6 +3808,19 @@ void CopyToFileGlobalState::FinalizeFileState(FileStateHandle file_state) { } } +void CopyToFileGlobalState::AbandonFileState(FileStateHandle file_state) noexcept { + try { + annotated_lock_guard guard(lock); + auto state = file_state.GetFileStatePtr(); + auto prepare_state = prepare_global_state.load(std::memory_order_acquire); + if (state && prepare_state && RefersToSameObject(*prepare_state.get(), *state.get())) { + // concurrent prepares still use this state - keep it alive until the global state is destroyed + prepare_global_state_owned = std::move(file_state); + } + } catch (...) { + } +} + FileStateHandle CopyToFileGlobalState::TryFinalizeOwnedFileStateLocked() { if (prepare_global_state_owned) { prepare_global_state.store(nullptr, std::memory_order_release); @@ -3836,6 +3851,12 @@ void CopyToFileGlobalState::MarkSuccessful() noexcept { //===--------------------------------------------------------------------===// // Copy Local State //===--------------------------------------------------------------------===// +CopyToFileLocalState::~CopyToFileLocalState() { + if (global_file_state) { + gstate.AbandonFileState(std::move(global_file_state)); + } +} + CopyToFileLocalState::CopyToFileLocalState(const PhysicalCopyToFile &op_p, ExecutionContext &context_p, CopyToFileGlobalState &gstate_p) : op(op_p), context(context_p), gstate(gstate_p) { diff --git a/src/duckdb/src/execution/physical_operator.cpp b/src/duckdb/src/execution/physical_operator.cpp index dd9c42839..1f5f1768b 100644 --- a/src/duckdb/src/execution/physical_operator.cpp +++ b/src/duckdb/src/execution/physical_operator.cpp @@ -509,8 +509,15 @@ static void AppendToCache(CachingOperatorState &state, DataChunk &source, Client auto &source_col = source.data[col_idx]; // Compare the pinned entry by pointer: cheaper than the string id and exact, since pinning keeps the // address stable (no ABA). The type check must come first to guard the Cast against a flat vector. - const bool same_dict = source_col.GetVectorType() == VectorType::DICTIONARY_VECTOR && - &source_col.Buffer().Cast().GetEntry() == slot.entry.get(); + bool same_dict = false; + if (source_col.GetVectorType() == VectorType::DICTIONARY_VECTOR) { + auto &source_entry = source_col.Buffer().Cast().GetEntry(); + // Reinterpret (e.g. a type-changing function) re-wraps a global dictionary in a new entry per chunk + // that keeps its id, so a global dictionary is also matched by id + same_dict = &source_entry == slot.entry.get() || + (slot.entry->global_dictionary && source_entry.global_dictionary && + !slot.entry->id.empty() && source_entry.id == slot.entry->id); + } if (same_dict) { const auto &source_sel = DictionaryVector::SelVector(source_col); for (idx_t row = 0; row < added; row++) { diff --git a/src/duckdb/src/execution/physical_plan/plan_asof_join.cpp b/src/duckdb/src/execution/physical_plan/plan_asof_join.cpp index f2b2dbbcc..0a89cc59e 100644 --- a/src/duckdb/src/execution/physical_plan/plan_asof_join.cpp +++ b/src/duckdb/src/execution/physical_plan/plan_asof_join.cpp @@ -14,6 +14,7 @@ #include "duckdb/execution/physical_plan_generator.hpp" #include "duckdb/function/function_binder.hpp" #include "duckdb/planner/binder.hpp" +#include "duckdb/planner/expression/bound_aggregate_expression.hpp" #include "duckdb/planner/expression/bound_constant_expression.hpp" #include "duckdb/planner/expression/bound_reference_expression.hpp" #include "duckdb/planner/expression/bound_window_expression.hpp" @@ -40,6 +41,17 @@ static unique_ptr CreatePredicateFromConditions(const vector().GetChildren()) { + if (child->GetExpressionClass() != ExpressionClass::BOUND_REF) { + return false; + } + } + return true; +} + optional_ptr PhysicalPlanGenerator::PlanAsOfLoopJoin(LogicalComparisonJoin &op, PhysicalOperator &probe, PhysicalOperator &build, const idx_t join_cardinality) { @@ -211,6 +223,9 @@ optional_ptr PhysicalPlanGenerator::PlanAsOfLoopJoin(LogicalCo GetBuiltinAggregateFunction(context, FirstFun::Name, {col_type})->Bind(context, std::move(aggr_children)); D_ASSERT(col_type == aggr_expr->GetReturnType()); + if (!HasOnlyReferenceChildren(*aggr_expr)) { + return nullptr; + } aggregates.emplace_back(std::move(aggr_expr)); } @@ -241,6 +256,9 @@ optional_ptr PhysicalPlanGenerator::PlanAsOfLoopJoin(LogicalCo auto aggr_expr = function_binder.BindAggregateFunction(bound_function, std::move(aggr_children), nullptr, AggregateType::NON_DISTINCT); D_ASSERT(col_type == aggr_expr->GetReturnType()); + if (!HasOnlyReferenceChildren(*aggr_expr)) { + return nullptr; + } aggregates.emplace_back(std::move(aggr_expr)); } diff --git a/src/duckdb/src/execution/radix_partitioned_hashtable.cpp b/src/duckdb/src/execution/radix_partitioned_hashtable.cpp index 500287f33..febd74043 100644 --- a/src/duckdb/src/execution/radix_partitioned_hashtable.cpp +++ b/src/duckdb/src/execution/radix_partitioned_hashtable.cpp @@ -139,7 +139,8 @@ RadixHTGlobalSinkState::RadixHTGlobalSinkState(ClientContext &context_p, const R auto tuples_per_block = block_alloc_size / radix_ht.GetLayout().GetRowWidth(); idx_t ht_count = LossyNumericCast(static_cast(config.sink_capacity) / GroupedAggregateHashTable::LOAD_FACTOR); - auto num_partitions = RadixPartitioning::NumberOfPartitions(config.GetMaximumSinkRadixBits()); + auto num_partitions = + RadixPartitioning::NumberOfPartitions(MaxValue(config.GetRadixBits(), config.GetMaximumSinkRadixBits())); auto count_per_partition = ht_count / num_partitions; auto blocks_per_partition = (count_per_partition + tuples_per_block) / tuples_per_block; if (!radix_ht.GetLayout().AllConstant()) { @@ -554,12 +555,16 @@ void UpdateSinkReservation(ClientContext &context, RadixHTGlobalSinkState &gstat } auto &temporary_memory_state = *gstate.temporary_memory_state; const auto aggregate_allocator_size = ht.GetAggregateAllocator()->AllocationSize(); + // Account for tuple metadata and the headroom deducted by GetThreadLimit. + const auto minimum_reservation = + (gstate.minimum_reservation + gstate.number_of_threads * gstate.block_alloc_size) / 8 * 10; temporary_memory_state.SetMinimumReservation(aggregate_allocator_size * gstate.number_of_threads + - gstate.minimum_reservation); + minimum_reservation); const auto remaining_size = MaxValue(gstate.number_of_threads * total_size, temporary_memory_state.GetRemainingSize()); + const auto reservation_limit = MaxValue(gstate.memory_limit, minimum_reservation); // Repeated pressure checks must not keep doubling an unsatisfied reservation. - const auto requested_size = remaining_size > gstate.memory_limit / 2 ? gstate.memory_limit : 2 * remaining_size; + const auto requested_size = remaining_size > reservation_limit / 2 ? reservation_limit : 2 * remaining_size; temporary_memory_state.SetRemainingSizeAndUpdateReservation(context, requested_size); } diff --git a/src/duckdb/src/function/cast/array_casts.cpp b/src/duckdb/src/function/cast/array_casts.cpp index 118dd5690..d0982a024 100644 --- a/src/duckdb/src/function/cast/array_casts.cpp +++ b/src/duckdb/src/function/cast/array_casts.cpp @@ -1,3 +1,4 @@ +#include "duckdb/function/cast/nested_to_varchar_cast.hpp" #include "duckdb/common/vector/array_vector.hpp" #include "duckdb/common/vector/constant_vector.hpp" #include "duckdb/common/vector/flat_vector.hpp" @@ -205,10 +206,11 @@ BoundCastInfo DefaultCasts::ArrayCastSwitch(BindCastInput &input, const LogicalT switch (target.id()) { case LogicalTypeId::VARCHAR: { auto size = ArrayType::GetSize(source); - return BoundCastInfo( - ArrayToVarcharCast, - ArrayBoundCastData::BindArrayToArrayCast(input, source, LogicalType::ARRAY(LogicalType::VARCHAR, size)), - ArrayBoundCastData::InitArrayLocalState); + auto cast_data = + ArrayBoundCastData::BindArrayToArrayCast(input, source, LogicalType::ARRAY(LogicalType::VARCHAR, size)); + NestedToVarcharCast::WrapChildCast(cast_data->Cast().child_cast_info, + ArrayType::GetChildType(source)); + return BoundCastInfo(ArrayToVarcharCast, std::move(cast_data), ArrayBoundCastData::InitArrayLocalState); } case LogicalTypeId::ARRAY: return BoundCastInfo(ArrayToArrayCast, ArrayBoundCastData::BindArrayToArrayCast(input, source, target), diff --git a/src/duckdb/src/function/cast/bignum_casts.cpp b/src/duckdb/src/function/cast/bignum_casts.cpp index 11b6e0ec3..a222595e3 100644 --- a/src/duckdb/src/function/cast/bignum_casts.cpp +++ b/src/duckdb/src/function/cast/bignum_casts.cpp @@ -288,39 +288,53 @@ struct BignumToDoubleErrorCast { } }; +struct BignumToIntErrorCast { + template + static inline bool Operation(SRC input, DST &result, CastParameters ¶meters) { + if (Bignum::BignumToInt(input, result, parameters.strict)) { + return true; + } + HandleCastError::AssignError(StringUtil::Format("BIGNUM value %s is out of range for type %s", + Bignum::BignumToVarchar(input), + TypeIdToString(GetTypeId())), + parameters); + return false; + } +}; + BoundCastInfo DefaultCasts::BignumCastSwitch(BindCastInput &input, const LogicalType &source, const LogicalType &target) { D_ASSERT(source.id() == LogicalTypeId::BIGNUM); // now switch on the result type switch (target.id()) { case LogicalTypeId::TINYINT: - return BoundCastInfo(&VectorCastHelpers::TryCastLoop); + return BoundCastInfo(&VectorCastHelpers::TryCastErrorLoop); case LogicalTypeId::UTINYINT: - return BoundCastInfo(&VectorCastHelpers::TryCastLoop); + return BoundCastInfo(&VectorCastHelpers::TryCastErrorLoop); case LogicalTypeId::SMALLINT: - return BoundCastInfo(&VectorCastHelpers::TryCastLoop); + return BoundCastInfo(&VectorCastHelpers::TryCastErrorLoop); case LogicalTypeId::USMALLINT: - return BoundCastInfo(&VectorCastHelpers::TryCastLoop); + return BoundCastInfo(&VectorCastHelpers::TryCastErrorLoop); case LogicalTypeId::INTEGER: - return BoundCastInfo(&VectorCastHelpers::TryCastLoop); + return BoundCastInfo(&VectorCastHelpers::TryCastErrorLoop); case LogicalTypeId::UINTEGER: - return BoundCastInfo(&VectorCastHelpers::TryCastLoop); + return BoundCastInfo(&VectorCastHelpers::TryCastErrorLoop); case LogicalTypeId::BIGINT: - return BoundCastInfo(&VectorCastHelpers::TryCastLoop); + return BoundCastInfo(&VectorCastHelpers::TryCastErrorLoop); case LogicalTypeId::UBIGINT: - return BoundCastInfo(&VectorCastHelpers::TryCastLoop); + return BoundCastInfo(&VectorCastHelpers::TryCastErrorLoop); case LogicalTypeId::HUGEINT: - return BoundCastInfo(&VectorCastHelpers::TryCastLoop); + return BoundCastInfo(&VectorCastHelpers::TryCastErrorLoop); case LogicalTypeId::UHUGEINT: - return BoundCastInfo(&VectorCastHelpers::TryCastLoop); + return BoundCastInfo(&VectorCastHelpers::TryCastErrorLoop); case LogicalTypeId::VARCHAR: return BoundCastInfo(&VectorCastHelpers::StringCast); diff --git a/src/duckdb/src/function/cast/cast_function_set.cpp b/src/duckdb/src/function/cast/cast_function_set.cpp index 665a066db..3720d8901 100644 --- a/src/duckdb/src/function/cast/cast_function_set.cpp +++ b/src/duckdb/src/function/cast/cast_function_set.cpp @@ -139,6 +139,14 @@ static auto RelaxedTypeMatch(MAP &map, const LogicalType &type) -> decltype(map. return map.find(LogicalTypeId::DECIMAL); case LogicalTypeId::ENUM: return map.find(LogicalTypeId::ENUM); + case LogicalTypeId::GEOMETRY: { + // a GEOMETRY with a CRS matches the casts of the GEOMETRY without a CRS + auto entry = map.find(LogicalType::GEOMETRY()); + if (entry != map.end()) { + return entry; + } + return map.find(LogicalType::ANY); + } default: return map.find(LogicalType::ANY); } diff --git a/src/duckdb/src/function/cast/enum_casts.cpp b/src/duckdb/src/function/cast/enum_casts.cpp index 18bdfc523..1cb924106 100644 --- a/src/duckdb/src/function/cast/enum_casts.cpp +++ b/src/duckdb/src/function/cast/enum_casts.cpp @@ -15,12 +15,10 @@ static bool EnumEnumCast(Vector &source, Vector &result, idx_t count, CastParame UnaryExecutor::Execute(source, result, count, [&](SRC_TYPE value) -> optional { auto key = EnumType::GetPos(res_enum_type, dictionary_data[value]); if (key == -1) { - if (!parameters.error_message) { - HandleCastError::AssignError( - CastExceptionText(dictionary_data[value], source.GetType(), res_enum_type), - vector_cast_data.parameters); - vector_cast_data.all_converted = false; - } + HandleCastError::AssignError( + CastExceptionText(dictionary_data[value], source.GetType(), res_enum_type), + vector_cast_data.parameters); + vector_cast_data.all_converted = false; return nullopt; } else { return UnsafeNumericCast(key); diff --git a/src/duckdb/src/function/cast/list_casts.cpp b/src/duckdb/src/function/cast/list_casts.cpp index 9e3dd9182..5d9e1c181 100644 --- a/src/duckdb/src/function/cast/list_casts.cpp +++ b/src/duckdb/src/function/cast/list_casts.cpp @@ -1,3 +1,4 @@ +#include "duckdb/function/cast/nested_to_varchar_cast.hpp" #include "duckdb/common/vector/array_vector.hpp" #include "duckdb/common/vector/constant_vector.hpp" #include "duckdb/common/vector/flat_vector.hpp" @@ -336,11 +337,12 @@ BoundCastInfo DefaultCasts::ListCastSwitch(BindCastInput &input, const LogicalTy case LogicalTypeId::LIST: return BoundCastInfo(ListCast::ListToListCast, ListBoundCastData::BindListToListCast(input, source, target), ListBoundCastData::InitListLocalState); - case LogicalTypeId::VARCHAR: - return BoundCastInfo( - ListToVarcharCast, - ListBoundCastData::BindListToListCast(input, source, LogicalType::LIST(LogicalType::VARCHAR)), - ListBoundCastData::InitListLocalState); + case LogicalTypeId::VARCHAR: { + auto cast_data = ListBoundCastData::BindListToListCast(input, source, LogicalType::LIST(LogicalType::VARCHAR)); + NestedToVarcharCast::WrapChildCast(cast_data->Cast().child_cast_info, + ListType::GetChildType(source)); + return BoundCastInfo(ListToVarcharCast, std::move(cast_data), ListBoundCastData::InitListLocalState); + } case LogicalTypeId::ARRAY: return BoundCastInfo(ListToArrayCast, BindListToArrayCast(input, source, target), ListBoundCastData::InitListLocalState); diff --git a/src/duckdb/src/function/cast/map_cast.cpp b/src/duckdb/src/function/cast/map_cast.cpp index 368831699..cace8906f 100644 --- a/src/duckdb/src/function/cast/map_cast.cpp +++ b/src/duckdb/src/function/cast/map_cast.cpp @@ -1,3 +1,4 @@ +#include "duckdb/function/cast/nested_to_varchar_cast.hpp" #include "duckdb/common/vector/constant_vector.hpp" #include "duckdb/common/vector/flat_vector.hpp" #include "duckdb/common/vector/list_vector.hpp" @@ -207,8 +208,19 @@ BoundCastInfo DefaultCasts::MapCastSwitch(BindCastInput &input, const LogicalTyp ListBoundCastData::InitListLocalState); case LogicalTypeId::VARCHAR: { auto varchar_type = LogicalType::MAP(LogicalType::VARCHAR, LogicalType::VARCHAR); - return BoundCastInfo(MapToVarcharCast, ListBoundCastData::BindListToListCast(input, source, varchar_type), - ListBoundCastData::InitListLocalState); + auto cast_data = ListBoundCastData::BindListToListCast(input, source, varchar_type); + // the entries are cast as STRUCT(key, value) + auto entry_cast_data = cast_data->Cast().child_cast_info.GetCastData(); + if (entry_cast_data) { + auto &struct_cast_data = entry_cast_data->Cast(); + D_ASSERT(struct_cast_data.child_cast_info.size() == 2); + auto &entry_children = StructType::GetChildTypes(ListType::GetChildType(source)); + for (idx_t i = 0; i < struct_cast_data.child_cast_info.size(); i++) { + NestedToVarcharCast::WrapChildCast(struct_cast_data.child_cast_info[i], + entry_children[struct_cast_data.source_indexes[i]].second); + } + } + return BoundCastInfo(MapToVarcharCast, std::move(cast_data), ListBoundCastData::InitListLocalState); } default: return TryVectorNullCast; diff --git a/src/duckdb/src/function/cast/nested_to_varchar_cast.cpp b/src/duckdb/src/function/cast/nested_to_varchar_cast.cpp index af473fe05..01f6fdd77 100644 --- a/src/duckdb/src/function/cast/nested_to_varchar_cast.cpp +++ b/src/duckdb/src/function/cast/nested_to_varchar_cast.cpp @@ -1,5 +1,12 @@ #include "duckdb/function/cast/nested_to_varchar_cast.hpp" +#include "duckdb/common/types/variant_iterator.hpp" +#include "duckdb/common/vector/flat_vector.hpp" +#include "duckdb/common/vector/union_vector.hpp" +#include "duckdb/common/vector/vector_iterator.hpp" +#include "duckdb/common/vector/vector_writer.hpp" +#include "duckdb/function/cast/vector_cast_helpers.hpp" + namespace duckdb { const bool NestedToVarcharCast::LOOKUP_TABLE[256] = { @@ -37,4 +44,107 @@ const bool NestedToVarcharCast::LOOKUP_TABLE[256] = { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}; +namespace { + +struct QuotedChildCastData : public BoundCastData { + QuotedChildCastData(BoundCastInfo child_cast_p, LogicalType child_type_p) + : child_cast(std::move(child_cast_p)), child_type(std::move(child_type_p)) { + } + + BoundCastInfo child_cast; + LogicalType child_type; + +public: + unique_ptr Copy() const override { + return make_uniq(child_cast.Copy(), child_type); + } +}; + +unique_ptr InitQuotedChildLocalState(CastLocalStateParameters ¶meters) { + auto &cast_data = parameters.cast_data->Cast(); + if (!cast_data.child_cast.HasInitLocalState()) { + return nullptr; + } + CastLocalStateParameters child_parameters(parameters, cast_data.child_cast.GetCastData()); + return cast_data.child_cast.InitLocalState(child_parameters); +} + +//! Marks the rows that are written as they are: NULL values, and nested values (a nested UNION member, or a VARIANT +//! OBJECT or ARRAY) +void GetUnquotedRows(const Vector &source, idx_t count, vector &is_unquoted) { + is_unquoted.assign(count, true); + auto &type = source.GetType(); + if (type.id() == LogicalTypeId::UNION) { + vector> member_validity; + for (idx_t member_idx = 0; member_idx < UnionType::GetMemberCount(type); member_idx++) { + member_validity.push_back(make_uniq(UnionVector::GetMember(source, member_idx))); + } + auto tags = UnionVector::GetTags(source).Values(); + for (idx_t i = 0; i < count; i++) { + auto tag = tags[i]; + if (!tag.IsValid()) { + continue; + } + auto member_idx = tag.GetValue(); + is_unquoted[i] = + UnionType::GetMemberType(type, member_idx).IsNested() || !member_validity[member_idx]->IsValid(i); + } + return; + } + D_ASSERT(type.id() == LogicalTypeId::VARIANT); + VectorIterator variants(source); + for (idx_t i = 0; i < count; i++) { + if (!variants.RowIsValid(i)) { + continue; + } + auto value = variants[i]; + if (value.IsNull() || value.IsMissing()) { + continue; + } + auto type_id = value.GetTypeId(); + is_unquoted[i] = type_id == VariantLogicalType::OBJECT || type_id == VariantLogicalType::ARRAY; + } +} + +bool QuotedChildCast(Vector &source, Vector &result, idx_t count, CastParameters ¶meters) { + auto &cast_data = parameters.cast_data->Cast(); + Vector varchar_values(LogicalType::VARCHAR, count); + CastParameters child_parameters(parameters, cast_data.child_cast.GetCastData(), parameters.local_state); + auto all_ok = cast_data.child_cast.Cast(source, varchar_values, count, child_parameters); + + vector is_unquoted; + GetUnquotedRows(source, count, is_unquoted); + + auto values = varchar_values.Values(); + auto writer = FlatVector::Writer(result, count); + for (idx_t i = 0; i < count; i++) { + auto entry = values[i]; + if (!entry.IsValid()) { + writer.WriteNull(); + continue; + } + auto &value = entry.GetValue(); + if (is_unquoted[i]) { + writer.WriteValue(value); + continue; + } + bool needs_quotes; + auto length = VectorCastHelpers::CalculateEscapedStringLength(value, needs_quotes); + auto &result_str = writer.WriteEmptyString(length); + VectorCastHelpers::WriteEscapedString(result_str.GetDataWriteable(), value, needs_quotes); + result_str.Finalize(); + } + return all_ok; +} + +} // namespace + +void NestedToVarcharCast::WrapChildCast(BoundCastInfo &child_cast, const LogicalType &child_type) { + if (child_type.id() != LogicalTypeId::UNION && child_type.id() != LogicalTypeId::VARIANT) { + return; + } + child_cast = BoundCastInfo(QuotedChildCast, make_uniq(std::move(child_cast), child_type), + InitQuotedChildLocalState); +} + } // namespace duckdb diff --git a/src/duckdb/src/function/cast/struct_cast.cpp b/src/duckdb/src/function/cast/struct_cast.cpp index 844aba549..49a878ca5 100644 --- a/src/duckdb/src/function/cast/struct_cast.cpp +++ b/src/duckdb/src/function/cast/struct_cast.cpp @@ -1,3 +1,4 @@ +#include "duckdb/function/cast/nested_to_varchar_cast.hpp" #include "duckdb/common/vector/constant_vector.hpp" #include "duckdb/common/vector/flat_vector.hpp" #include "duckdb/common/vector/list_vector.hpp" @@ -348,9 +349,13 @@ BoundCastInfo DefaultCasts::StructCastSwitch(BindCastInput &input, const Logical varchar_children.push_back(make_pair(child_entry.first, LogicalType::VARCHAR)); } auto varchar_type = LogicalType::STRUCT(varchar_children); - return BoundCastInfo(StructToVarcharCast, - StructBoundCastData::BindStructToStructCast(input, source, varchar_type), - StructBoundCastData::InitStructCastLocalState); + auto cast_data = StructBoundCastData::BindStructToStructCast(input, source, varchar_type); + auto &struct_cast_data = cast_data->Cast(); + for (idx_t i = 0; i < struct_cast_data.child_cast_info.size(); i++) { + NestedToVarcharCast::WrapChildCast(struct_cast_data.child_cast_info[i], + struct_children[struct_cast_data.source_indexes[i]].second); + } + return BoundCastInfo(StructToVarcharCast, std::move(cast_data), StructBoundCastData::InitStructCastLocalState); } case LogicalTypeId::MAP: { return BoundCastInfo(StructToMapCast, StructToMapBoundCastData::BindStructToMapCast(input, source, target), diff --git a/src/duckdb/src/function/combine_types_rules.cpp b/src/duckdb/src/function/combine_types_rules.cpp index 676e95e6e..a5461de9e 100644 --- a/src/duckdb/src/function/combine_types_rules.cpp +++ b/src/duckdb/src/function/combine_types_rules.cpp @@ -62,17 +62,19 @@ static bool CombineStructTypes(LogicalTypeResolver &logical_type_resolver, const // Create a super-set of the STRUCT fields. // First, create a name->index map of the right children. - InsertionOrderPreservingMap> right_children_map; + identifier_map_t right_children_map; for (idx_t i = 0; i < right_children.size(); i++) { auto &name = right_children[i].first; right_children_map[name] = i; } + // right children that matched a left child, the others are added after the left children + vector right_child_matched(right_children.size(), false); for (idx_t i = 0; i < left_children.size(); i++) { auto &left_child = left_children[i]; auto right_child_it = right_children_map.find(left_child.first); - if (right_child_it == right_children_map.end()) { + if (right_child_it == right_children_map.end() || right_child_matched[right_child_it->second]) { // We can directly put the left child. child_types.emplace_back(left_child.first, left_child.second); continue; @@ -85,13 +87,16 @@ static bool CombineStructTypes(LogicalTypeResolver &logical_type_resolver, const return false; } child_types.emplace_back(left_child.first, std::move(child_type)); - right_children_map.erase(right_child_it); + right_child_matched[right_child_it->second] = true; } // Add all remaining right children. - for (const auto &right_child_it : right_children_map) { - auto &right_child = right_children[right_child_it.second]; - child_types.emplace_back(right_child.first, right_child.second); + for (idx_t i = 0; i < right_children.size(); i++) { + auto right_child_it = right_children_map.find(right_children[i].first); + if (right_child_matched[i] || right_child_it->second != i) { + continue; + } + child_types.emplace_back(right_children[i].first, right_children[i].second); } result = LogicalType::STRUCT(child_types); @@ -178,13 +183,41 @@ static bool CombineEqualUnion(LogicalTypeResolver &logical_type_resolver, const const LogicalType &right, LogicalType &result) { auto left_member_count = UnionType::GetMemberCount(left); auto right_member_count = UnionType::GetMemberCount(right); - if (left_member_count != right_member_count) { - // return the "larger" type, with the most members - result = left_member_count > right_member_count ? left : right; - return true; + // start from the "larger" type, with the most members (the left one if they are equal) + auto &base = right_member_count > left_member_count ? right : left; + auto &other = right_member_count > left_member_count ? left : right; + // merge the member types, so that no member value is narrowed by the cast to the common type + child_list_t members; + for (idx_t i = 0; i < UnionType::GetMemberCount(base); i++) { + members.emplace_back(UnionType::GetMemberName(base, i), UnionType::GetMemberType(base, i)); + } + for (idx_t i = 0; i < UnionType::GetMemberCount(other); i++) { + auto &member_name = UnionType::GetMemberName(other, i); + auto &member_type = UnionType::GetMemberType(other, i); + bool found = false; + for (auto &member : members) { + if (member.first != member_name) { + continue; + } + found = true; + LogicalType new_member_type; + if (!logical_type_resolver.Operation(member.second, member_type, new_member_type)) { + // the members cannot be combined - keep the base type + result = base; + return true; + } + member.second = std::move(new_member_type); + break; + } + if (!found) { + if (members.size() >= UnionType::MAX_UNION_MEMBERS) { + result = base; + return true; + } + members.emplace_back(member_name, member_type); + } } - // otherwise, keep left, don't try to meld the two together. - result = left; + result = LogicalType::UNION(std::move(members)); return true; } diff --git a/src/duckdb/src/function/encoding_function.cpp b/src/duckdb/src/function/encoding_function.cpp index 392f86da3..2d428744e 100644 --- a/src/duckdb/src/function/encoding_function.cpp +++ b/src/duckdb/src/function/encoding_function.cpp @@ -32,7 +32,43 @@ void DecodeUTF16ToUTF8(CSVEncoderBuffer &encoded_buffer, char *target_buffer, id static_cast(static_cast(encoded_ptr[encoded_buffer.cur_pos]) | (static_cast(encoded_ptr[encoded_buffer.cur_pos + 1]) << 8)); if (ch >= 0xD800 && ch <= 0xDFFF) { - throw InvalidInputException("File is not utf-16 encoded"); + if (ch >= 0xDC00) { + // low surrogate without a preceding high surrogate + throw InvalidInputException("File is not utf-16 encoded"); + } + if (encoded_buffer.cur_pos + 3 >= encoded_buffer.actual_encoded_buffer_size) { + if (encoded_buffer.last_buffer) { + throw InvalidInputException("File is not utf-16 encoded"); + } + // the low surrogate is passed on to the next buffer together with the high surrogate + return; + } + const uint16_t low = + static_cast(static_cast(encoded_ptr[encoded_buffer.cur_pos + 2]) | + (static_cast(encoded_ptr[encoded_buffer.cur_pos + 3]) << 8)); + if (low < 0xDC00 || low > 0xDFFF) { + throw InvalidInputException("File is not utf-16 encoded"); + } + const uint32_t code_point = 0x10000 + ((static_cast(ch - 0xD800)) << 10) + (low - 0xDC00); + const char utf8[4] = { + static_cast(0xF0 | (code_point >> 18)), static_cast(0x80 | ((code_point >> 12) & 0x3F)), + static_cast(0x80 | ((code_point >> 6) & 0x3F)), static_cast(0x80 | (code_point & 0x3F))}; + idx_t written = 0; + while (written < 4 && target_buffer_current_position < target_buffer_size) { + target_buffer[target_buffer_current_position++] = utf8[written++]; + } + if (written < 4) { + // We are done, but we have to store the remaining bytes for the next chunk! + encoded_buffer.cur_pos += 4; + remaining_bytes_size = 0; + for (; written < 4; written++) { + remaining_bytes_buffer[remaining_bytes_size++] = utf8[written]; + } + return; + } + // skip the low surrogate, the loop skips the high surrogate + encoded_buffer.cur_pos += 2; + continue; } if (ch <= 0x007F) { // 1-byte UTF-8 for ASCII characters @@ -112,7 +148,7 @@ void DecodeUTF8(CSVEncoderBuffer &encoded_buffer, char *target_buffer, idx_t &ta void EncodingFunctionSet::Initialize(DBConfig &config) { config.RegisterEncodeFunction({"utf-8", DecodeUTF8, 1, 1}); config.RegisterEncodeFunction({"latin-1", DecodeLatin1ToUTF8, 2, 1}); - config.RegisterEncodeFunction({"utf-16", DecodeUTF16ToUTF8, 2, 2}); + config.RegisterEncodeFunction({"utf-16", DecodeUTF16ToUTF8, 3, 4}); } void DBConfig::RegisterEncodeFunction(const EncodingFunction &function) const { diff --git a/src/duckdb/src/function/function_binder.cpp b/src/duckdb/src/function/function_binder.cpp index 2d10779e9..2746e686b 100644 --- a/src/duckdb/src/function/function_binder.cpp +++ b/src/duckdb/src/function/function_binder.cpp @@ -470,6 +470,10 @@ static LogicalTypeComparisonResult RequiresCast(const LogicalType &source_type, return RequiresCast(ListType::GetChildType(source_type), ListType::GetChildType(target_type)); } if (source_type.id() == LogicalTypeId::ARRAY && target_type.id() == LogicalTypeId::ARRAY) { + auto target_size = ArrayType::GetSize(target_type); + if (target_size != 0 && target_size != ArrayType::GetSize(source_type)) { + return LogicalTypeComparisonResult::DIFFERENT_TYPES; + } return RequiresCast(ArrayType::GetChildType(source_type), ArrayType::GetChildType(target_type)); } return LogicalTypeComparisonResult::DIFFERENT_TYPES; diff --git a/src/duckdb/src/function/pragma/pragma_queries.cpp b/src/duckdb/src/function/pragma/pragma_queries.cpp index 8c69b06cb..0f4ba89a4 100644 --- a/src/duckdb/src/function/pragma/pragma_queries.cpp +++ b/src/duckdb/src/function/pragma/pragma_queries.cpp @@ -175,7 +175,10 @@ static string PragmaImportDatabase(ClientContext &context, const FunctionParamet auto copy_statements = std::move(parser.statements); query.clear(); for (auto &statement_p : copy_statements) { - D_ASSERT(statement_p->type == StatementType::COPY_STATEMENT); + if (statement_p->type != StatementType::COPY_STATEMENT) { + throw InvalidInputException("IMPORT DATABASE: load.sql may only contain COPY statements, found: %s", + statement_p->ToString()); + } auto &statement = statement_p->Cast(); auto &info = *statement.info; auto file_name = fs.ExtractName(info.file_path); diff --git a/src/duckdb/src/function/scalar/list/list_resize.cpp b/src/duckdb/src/function/scalar/list/list_resize.cpp index 33d53a291..07d7ff2e7 100644 --- a/src/duckdb/src/function/scalar/list/list_resize.cpp +++ b/src/duckdb/src/function/scalar/list/list_resize.cpp @@ -109,6 +109,10 @@ static void ListResizeResolveTypes(ResolveScalarFunctionTypesInput &input) { return; } + if (list_type.id() != LogicalTypeId::LIST) { + throw BinderException("%s can only operate on LISTs", bound_function.GetName()); + } + // Attempt implicit casting, if the default type does not match list the list child type. if (bound_function.GetArguments().size() == 3 && ListType::GetChildType(list_type) != input.GetArgumentType(2) && input.GetArgumentType(2) != LogicalTypeId::SQLNULL) { diff --git a/src/duckdb/src/function/scalar/operator/arithmetic.cpp b/src/duckdb/src/function/scalar/operator/arithmetic.cpp index bdc3e8e01..763e832ee 100644 --- a/src/duckdb/src/function/scalar/operator/arithmetic.cpp +++ b/src/duckdb/src/function/scalar/operator/arithmetic.cpp @@ -1477,9 +1477,18 @@ double InterpolateOperator::Operation(const double &lo, const double d, const do return lo * (1.0 - d) + hi * d; } +//! Interpolates between two integers without going through a double for the values themselves +static int64_t InterpolateInt64(const int64_t lo, const double d, const int64_t hi) { + int64_t delta; + if (!TrySubtractOperator::Operation(hi, lo, delta)) { + return std::llround(static_cast(lo) * (1.0 - d) + static_cast(hi) * d); + } + return lo + std::llround(static_cast(delta) * d); +} + template <> dtime_t InterpolateOperator::Operation(const dtime_t &lo, const double d, const dtime_t &hi) { - return dtime_t(std::llround(static_cast(lo.value) * (1.0 - d) + static_cast(hi.value) * d)); + return dtime_t(InterpolateInt64(lo.value, d, hi.value)); } //! TIMETZ values are interpolated by their time normalized to UTC - the result is the normalized time at offset +00 @@ -1502,7 +1511,7 @@ dtime_tz_t InterpolateOperator::Operation(const dtime_tz_t &lo, const double d, template <> timestamp_t InterpolateOperator::Operation(const timestamp_t &lo, const double d, const timestamp_t &hi) { - return timestamp_t(std::llround(static_cast(lo.value) * (1.0 - d) + static_cast(hi.value) * d)); + return timestamp_t(InterpolateInt64(lo.value, d, hi.value)); } template <> diff --git a/src/duckdb/src/function/scalar/strftime_format.cpp b/src/duckdb/src/function/scalar/strftime_format.cpp index 672224fac..d5cbd2b8b 100644 --- a/src/duckdb/src/function/scalar/strftime_format.cpp +++ b/src/duckdb/src/function/scalar/strftime_format.cpp @@ -237,6 +237,7 @@ char *StrfTimeFormat::WriteYear(char *target, int32_t year) const { // write a value in the range of 0..999999... padded to the given number of digits char *StrfTimeFormat::WritePadded(char *target, uint32_t value, size_t padding) const { D_ASSERT(padding > 1); + auto end = target + padding; if (padding % 2) { uint32_t decimals = value % 1000u; WritePadded3(target + padding - 3, decimals); @@ -248,7 +249,7 @@ char *StrfTimeFormat::WritePadded(char *target, uint32_t value, size_t padding) WritePadded2(target + padding - 2 * (i + 1), decimals); value /= 100; } - return target + padding; + return end; } bool StrfTimeFormat::IsDateSpecifier(StrTimeSpecifier specifier) { diff --git a/src/duckdb/src/function/scalar/string/like.cpp b/src/duckdb/src/function/scalar/string/like.cpp index 6ffc841de..eddc03b3b 100644 --- a/src/duckdb/src/function/scalar/string/like.cpp +++ b/src/duckdb/src/function/scalar/string/like.cpp @@ -6,10 +6,29 @@ #include "duckdb/planner/expression/bound_function_expression.hpp" #include "duckdb/execution/expression_executor.hpp" +#include "utf8proc_wrapper.hpp" namespace duckdb { -static bool GlobMatchesBracket(char s, const char *pattern, idx_t plen, idx_t &pidx, bool &valid_pattern) { +//! Returns the code point of the (UTF-8 encoded) character at pos, and sets its length in bytes +static int32_t GlobCharacter(const char *data, idx_t len, idx_t pos, idx_t &char_len) { + auto byte = static_cast(data[pos]); + if (byte < 0x80) { + char_len = 1; + return byte; + } + int sz = 1; + auto codepoint = Utf8Proc::UTF8ToCodepoint(data + pos, sz, len - pos); + if (codepoint < 0 || sz <= 0) { + // not valid UTF-8: treat the byte as a character + char_len = 1; + return byte; + } + char_len = UnsafeNumericCast(sz); + return codepoint; +} + +static bool GlobMatchesBracket(int32_t s, const char *pattern, idx_t plen, idx_t &pidx, bool &valid_pattern) { if (pidx == plen) { valid_pattern = false; return false; @@ -26,7 +45,8 @@ static bool GlobMatchesBracket(char s, const char *pattern, idx_t plen, idx_t &p bool found_closing_bracket = false; // now check the remainder of the pattern while (pidx < plen) { - auto p = pattern[pidx]; + idx_t p_len; + auto p = GlobCharacter(pattern, plen, pidx, p_len); // if the first character is a closing bracket, we match it literally // otherwise it indicates an end of bracket if (p == ']' && pidx > start_pos) { @@ -35,23 +55,24 @@ static bool GlobMatchesBracket(char s, const char *pattern, idx_t plen, idx_t &p pidx++; break; } - if (pidx + 1 == plen) { + if (pidx + p_len >= plen) { // no next character! break; } bool matches; - if (pattern[pidx + 1] == '-' && pidx + 2 < plen && pattern[pidx + 2] != ']') { + if (pattern[pidx + p_len] == '-' && pidx + p_len + 1 < plen && pattern[pidx + p_len + 1] != ']') { // range! find the next character in the range - auto next_char = pattern[pidx + 2]; + idx_t next_len; + auto next_char = GlobCharacter(pattern, plen, pidx + p_len + 1, next_len); // check if the current character is within the range matches = s >= p && s <= next_char; // shift the pattern forward past the range - pidx += 3; + pidx += p_len + 1 + next_len; } else { // no range! perform a direct match matches = p == s; // shift the pattern forward past the character - pidx++; + pidx += p_len; } if (found_match == invert && matches) { // found a match! set the found_matches flag @@ -79,6 +100,8 @@ bool Glob(const char *string, idx_t slen, const char *pattern, idx_t plen, bool while (sidx < slen) { bool matched = false; idx_t next_pidx = pidx; + // the number of bytes of the string that a match consumes + idx_t match_len = 1; if (pidx < plen) { auto p = pattern[pidx]; switch (p) { @@ -102,6 +125,8 @@ bool Glob(const char *string, idx_t slen, const char *pattern, idx_t plen, bool case '?': // when enabled: matches anything but null if (allow_question_mark) { + // a single character, which can consist of multiple bytes + GlobCharacter(string, slen, sidx, match_len); matched = true; } else { matched = string[sidx] == p; @@ -111,7 +136,8 @@ bool Glob(const char *string, idx_t slen, const char *pattern, idx_t plen, bool case '[': { next_pidx = pidx + 1; bool valid_pattern; - matched = GlobMatchesBracket(string[sidx], pattern, plen, next_pidx, valid_pattern); + auto character = GlobCharacter(string, slen, sidx, match_len); + matched = GlobMatchesBracket(character, pattern, plen, next_pidx, valid_pattern); if (!valid_pattern) { return false; } @@ -135,7 +161,7 @@ bool Glob(const char *string, idx_t slen, const char *pattern, idx_t plen, bool } } if (matched) { - sidx++; + sidx += match_len; pidx = next_pidx; continue; } @@ -143,7 +169,9 @@ bool Glob(const char *string, idx_t slen, const char *pattern, idx_t plen, bool return false; } // backtrack: the last '*' consumes one more character and we retry from there - star_sidx++; + idx_t star_char_len; + GlobCharacter(string, slen, star_sidx, star_char_len); + star_sidx += star_char_len; sidx = star_sidx; pidx = star_pidx; } diff --git a/src/duckdb/src/function/scalar/struct/remap_struct.cpp b/src/duckdb/src/function/scalar/struct/remap_struct.cpp index 824eb7832..87013c2f1 100644 --- a/src/duckdb/src/function/scalar/struct/remap_struct.cpp +++ b/src/duckdb/src/function/scalar/struct/remap_struct.cpp @@ -50,6 +50,23 @@ struct RemapStructBindData : public FunctionData { void RemapNested(Vector &input, Vector &default_vector, Vector &result, idx_t result_size, const vector &remap_info); +//! Marks the children of every struct row that is NULL in the validity as NULL +void PropagateStructNulls(Vector &vec, const ValidityMask &validity, idx_t count) { + if (vec.GetType().id() != LogicalTypeId::STRUCT) { + return; + } + for (auto &child : StructVector::GetEntries(vec)) { + child.Flatten(); + auto &child_validity = FlatVector::ValidityMutable(child); + for (idx_t row = 0; row < count; row++) { + if (!validity.RowIsValid(row)) { + child_validity.SetInvalid(row); + } + } + PropagateStructNulls(child, child_validity, count); + } +} + void RemapChildVectors(const Vector &result, const vector> &input_vectors, const vector> &result_vectors, const vector &remap_info, Vector &default_vector, const bool has_top_level_null, idx_t count) { @@ -67,6 +84,20 @@ void RemapChildVectors(const Vector &result, const vector> &in RemapNested(input_vector, child_default.get(), result_vectors[i], count, remap.child_remap_info); continue; } + if (!remap.index.IsValid() && remap.default_index.IsValid() && !remap.child_remap_info.empty()) { + // nested default without a source - remap the fields of the default to the target type + auto &defaults = StructVector::GetEntries(default_vector); + auto &nested_default = defaults[remap.default_index.GetIndex()]; + if (nested_default.GetType().id() == LogicalTypeId::STRUCT && + result_vectors[i].get().GetType().id() == LogicalTypeId::STRUCT) { + RemapNested(nested_default, nested_default, result_vectors[i], count, remap.child_remap_info); + if (has_top_level_null) { + FlatVector::SetValidity(result_vectors[i], FlatVector::Validity(result)); + PropagateStructNulls(result_vectors[i], FlatVector::Validity(result), count); + } + continue; + } + } // primitive type remap if (remap.default_index.IsValid()) { auto &defaults = StructVector::GetEntries(default_vector); @@ -492,8 +523,17 @@ struct RemapEntry { static LogicalType RemapCast(const LogicalType &type, const identifier_map_t &remap_map) { unordered_map source_name_map; for (auto &entry : remap_map) { - if (entry.second.index.IsValid()) { - source_name_map.emplace(entry.second.index.GetIndex(), entry.first); + if (!entry.second.index.IsValid()) { + continue; + } + auto result = source_name_map.emplace(entry.second.index.GetIndex(), entry.first); + if (!result.second) { + auto &other = remap_map.find(Identifier(result.first->second))->second; + if (other.target_type != entry.second.target_type) { + throw BinderException("remap_struct cannot map a single source field to targets of different " + "types ('%s' and '%s')", + other.target_type.ToString(), entry.second.target_type.ToString()); + } } } diff --git a/src/duckdb/src/function/scalar/struct/struct_extract.cpp b/src/duckdb/src/function/scalar/struct/struct_extract.cpp index 030f8a721..85856ef25 100644 --- a/src/duckdb/src/function/scalar/struct/struct_extract.cpp +++ b/src/duckdb/src/function/scalar/struct/struct_extract.cpp @@ -28,7 +28,41 @@ static void StructExtractFunction(DataChunk &args, ExpressionState &state, Vecto enum class StructExtractKey : uint8_t { NAME, INDEX, INDEX_AT }; -//! Validates the struct argument - the extracted entry and the return type are resolved in the bind +//! Looks up the entry with the given name, returns its index and sets the type of the entry +static idx_t ResolveStructEntryByName(const LogicalType &struct_type, const string &key_str, LogicalType &entry_type) { + auto &struct_children = StructType::GetChildTypes(struct_type); + if (key_str.empty()) { + throw BinderException("Key name for struct_extract must not be empty"); + } + auto key = Identifier(key_str); + for (idx_t i = 0; i < struct_children.size(); i++) { + if (struct_children[i].first == key) { + entry_type = struct_children[i].second; + return i; + } + } + vector candidates; + candidates.reserve(struct_children.size()); + for (auto &struct_child : struct_children) { + candidates.emplace_back(struct_child.first); + } + auto closest_settings = StringUtil::TopNJaroWinkler(candidates, key); + auto message = StringUtil::CandidatesMessage(closest_settings, "Candidate Entries"); + throw BinderException("Could not find key %s in struct\n%s", key, message); +} + +//! Looks up the entry with the given (1-based) index, returns its (0-based) index and sets the type of the entry +static idx_t ResolveStructEntryByIndex(const LogicalType &struct_type, int64_t index, LogicalType &entry_type) { + auto &struct_children = StructType::GetChildTypes(struct_type); + if (index <= 0 || idx_t(index) > struct_children.size()) { + throw BinderException("Key index %lld for struct_extract out of range - expected an index between 1 and %llu", + index, struct_children.size()); + } + entry_type = struct_children[NumericCast(index - 1)].second; + return NumericCast(index - 1); +} + +//! Validates the struct argument and resolves the extracted entry type when the key is a constant template static void StructExtractResolveTypes(ResolveScalarFunctionTypesInput &input) { auto &bound_function = input.GetBoundFunction(); @@ -50,60 +84,41 @@ static void StructExtractResolveTypes(ResolveScalarFunctionTypesInput &input) { "struct_extract with an integer key can only be used on unnamed structs, use a string key instead"); } bound_function.GetArguments()[0] = child_type; + + // resolve the return type here, so that it is also known (and the key is validated) for NULL structs + auto key_val = input.TryGetConstant(1); + if (!key_val || key_val->IsNull()) { + return; + } + LogicalType entry_type; + if (KEY == StructExtractKey::NAME) { + ResolveStructEntryByName(child_type, key_val->GetValue(), entry_type); + } else { + ResolveStructEntryByIndex(child_type, key_val->GetValue(), entry_type); + } + bound_function.SetReturnType(std::move(entry_type)); } static unique_ptr StructExtractBind(BindScalarFunctionInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &struct_children = StructType::GetChildTypes(bound_function.GetArguments()[0]); - auto key_val = input.GetNonNullConstant(1); D_ASSERT(key_val.type().id() == LogicalTypeId::VARCHAR); - auto &key_str = StringValue::Get(key_val); - if (key_str.empty()) { - throw BinderException("Key name for struct_extract must not be empty"); - } - auto key = Identifier(key_str); LogicalType return_type; - idx_t key_index = 0; - bool found_key = false; - - for (size_t i = 0; i < struct_children.size(); i++) { - auto &child = struct_children[i]; - if (child.first == key) { - found_key = true; - key_index = i; - return_type = child.second; - break; - } - } - - if (!found_key) { - vector candidates; - candidates.reserve(struct_children.size()); - for (auto &struct_child : struct_children) { - candidates.emplace_back(struct_child.first); - } - auto closest_settings = StringUtil::TopNJaroWinkler(candidates, key); - auto message = StringUtil::CandidatesMessage(closest_settings, "Candidate Entries"); - throw BinderException("Could not find key %s in struct\n%s", key, message); - } - + auto key_index = ResolveStructEntryByName(bound_function.GetArguments()[0], StringValue::Get(key_val), return_type); bound_function.SetReturnType(std::move(return_type)); return StructExtractAtFun::GetBindData(key_index); } static unique_ptr StructExtractBindIndex(BindScalarFunctionInput &input) { auto &bound_function = input.GetBoundFunction(); - auto &struct_children = StructType::GetChildTypes(bound_function.GetArguments()[0]); Value key_val = input.GetConstant(1); - auto index = key_val.GetValue(); - if (index <= 0 || idx_t(index) > struct_children.size()) { - throw BinderException("Key index %lld for struct_extract out of range - expected an index between 1 and %llu", - index, struct_children.size()); - } - bound_function.SetReturnType(struct_children[NumericCast(index - 1)].second); - return StructExtractAtFun::GetBindData(NumericCast(index - 1)); + + LogicalType return_type; + auto key_index = + ResolveStructEntryByIndex(bound_function.GetArguments()[0], key_val.GetValue(), return_type); + bound_function.SetReturnType(std::move(return_type)); + return StructExtractAtFun::GetBindData(key_index); } static unique_ptr PropagateStructExtractStats(ClientContext &context, FunctionStatisticsInput &input) { diff --git a/src/duckdb/src/function/table/arrow.cpp b/src/duckdb/src/function/table/arrow.cpp index 4c86ff705..878b4501c 100644 --- a/src/duckdb/src/function/table/arrow.cpp +++ b/src/duckdb/src/function/table/arrow.cpp @@ -213,6 +213,12 @@ OperatorPartitionData ArrowTableFunction::ArrowGetPartitionData(ClientContext &c } static bool CanPushdown(const ArrowType &type) { + if (type.RunEndEncoded()) { + return false; + } + if (type.HasDictionary()) { + return CanPushdown(type.GetDictionary()); + } auto duck_type = type.GetDuckType(); switch (duck_type.id()) { case LogicalTypeId::BOOLEAN: @@ -266,7 +272,13 @@ static bool CanPushdown(const ArrowType &type) { return false; } } -static bool HasViewType(const ArrowType &type) { + +// PyArrow's array_filter kernel has no implementation for string view, binary view and run-end encoded arrays. +// It filters a dictionary by its indices, so a dictionary of view values is fine here. +static bool HasUnfilterableType(const ArrowType &type) { + if (type.RunEndEncoded()) { + return true; + } auto duck_type = type.GetDuckType(); switch (duck_type.id()) { case LogicalTypeId::VARCHAR: @@ -277,7 +289,7 @@ static bool HasViewType(const ArrowType &type) { case LogicalTypeId::UNION: { const auto &struct_info = type.GetTypeInfo(); for (idx_t i = 0; i < struct_info.ChildCount(); i++) { - if (HasViewType(struct_info.GetChild(i))) { + if (HasUnfilterableType(struct_info.GetChild(i))) { return true; } } @@ -286,20 +298,20 @@ static bool HasViewType(const ArrowType &type) { case LogicalTypeId::LIST: case LogicalTypeId::MAP: { const auto &list_info = type.GetTypeInfo(); - return HasViewType(list_info.GetChild()); + return HasUnfilterableType(list_info.GetChild()); } case LogicalTypeId::ARRAY: { const auto &array_info = type.GetTypeInfo(); - return HasViewType(array_info.GetChild()); + return HasUnfilterableType(array_info.GetChild()); } default: return false; } } -static bool TableHasViewTypes(const arrow_column_map_t &column_info) { +static bool TableHasUnfilterableTypes(const arrow_column_map_t &column_info) { for (const auto &col : column_info) { - if (HasViewType(*col.second)) { + if (HasUnfilterableType(*col.second)) { return true; } } @@ -309,11 +321,9 @@ static bool TableHasViewTypes(const arrow_column_map_t &column_info) { bool ArrowTableFunction::ArrowPushdownType(const FunctionData &bind_data, idx_t col_idx) { auto &arrow_bind_data = bind_data.Cast(); const auto &column_info = arrow_bind_data.arrow_table.GetColumns(); - // PyArrow's array_filter kernel doesn't support string_view/binary_view types. - // The filter is applied to ALL columns in a record batch, so if any column has a - // view type, we must disable filter pushdown for the entire table. - // See https://github.com/duckdb/duckdb-python/issues/227 - if (TableHasViewTypes(column_info)) { + // The producer applies a pushed filter to every column of a record batch, so one column that cannot be + // filtered turns pushdown off for the entire table. + if (TableHasUnfilterableTypes(column_info)) { return false; } const auto column_type = column_info.at(col_idx); diff --git a/src/duckdb/src/function/table/checkpoint.cpp b/src/duckdb/src/function/table/checkpoint.cpp index c696a4d4d..e676af7e6 100644 --- a/src/duckdb/src/function/table/checkpoint.cpp +++ b/src/duckdb/src/function/table/checkpoint.cpp @@ -2,6 +2,7 @@ #include "duckdb/main/client_context.hpp" #include "duckdb/storage/storage_manager.hpp" #include "duckdb/transaction/transaction_manager.hpp" +#include "duckdb/main/attached_database.hpp" #include "duckdb/main/database_manager.hpp" #include "duckdb/function/function_set.hpp" @@ -41,7 +42,14 @@ static unique_ptr CheckpointBind(ClientContext &context, TableFunc throw BinderException("Database \"%s\" not found", db_name); } } else { - db = db_manager.GetDatabase(context, DatabaseManager::GetDefaultDatabase(context)); + auto default_database = DatabaseManager::GetDefaultDatabase(context); + db = db_manager.GetDatabase(context, default_database); + if (!db) { + throw BinderException("Catalog \"%s\" does not exist!", default_database); + } + } + if (db->IsSystem() || db->IsTemporary()) { + throw BinderException("Cannot checkpoint database \"%s\": it does not have storage", db->GetName()); } return make_uniq(db); } diff --git a/src/duckdb/src/function/table/system/pragma_metadata_info.cpp b/src/duckdb/src/function/table/system/pragma_metadata_info.cpp index 9df9956c5..ab958bd66 100644 --- a/src/duckdb/src/function/table/system/pragma_metadata_info.cpp +++ b/src/duckdb/src/function/table/system/pragma_metadata_info.cpp @@ -1,6 +1,7 @@ #include "duckdb/function/table/system_functions.hpp" #include "duckdb/catalog/catalog.hpp" +#include "duckdb/main/attached_database.hpp" #include "duckdb/storage/database_size.hpp" #include "duckdb/main/database_manager.hpp" #include "duckdb/function/function_set.hpp" @@ -44,6 +45,9 @@ static unique_ptr PragmaMetadataInfoBind(ClientContext &context, T db_name = Identifier(StringValue::Get(input.inputs[0])); } auto &catalog = Catalog::GetCatalog(context, db_name); + if (catalog.GetAttached().IsSystem() || catalog.GetAttached().IsTemporary()) { + throw BinderException("Database \"%s\" does not have storage", db_name.GetIdentifierName()); + } auto result = make_uniq(); result->metadata_info = catalog.GetMetadataInfo(context); return std::move(result); diff --git a/src/duckdb/src/function/table/version/pragma_version.cpp b/src/duckdb/src/function/table/version/pragma_version.cpp index 013a582ec..5125dbf1b 100644 --- a/src/duckdb/src/function/table/version/pragma_version.cpp +++ b/src/duckdb/src/function/table/version/pragma_version.cpp @@ -1,5 +1,5 @@ #ifndef DUCKDB_PATCH_VERSION -#define DUCKDB_PATCH_VERSION "0-alpha48443" +#define DUCKDB_PATCH_VERSION "0-alpha48623" #endif #ifndef DUCKDB_MINOR_VERSION #define DUCKDB_MINOR_VERSION 1 @@ -8,10 +8,10 @@ #define DUCKDB_MAJOR_VERSION 2 #endif #ifndef DUCKDB_VERSION -#define DUCKDB_VERSION "v2.1.0-alpha48443" +#define DUCKDB_VERSION "v2.1.0-alpha48623" #endif #ifndef DUCKDB_SOURCE_ID -#define DUCKDB_SOURCE_ID "e08224d653" +#define DUCKDB_SOURCE_ID "e1ecb5b887" #endif #include "duckdb/function/table/system_functions.hpp" #include "duckdb/main/database.hpp" diff --git a/src/duckdb/src/function/variant/variant_shredding.cpp b/src/duckdb/src/function/variant/variant_shredding.cpp index ea748f388..f3f511ff7 100644 --- a/src/duckdb/src/function/variant/variant_shredding.cpp +++ b/src/duckdb/src/function/variant/variant_shredding.cpp @@ -343,10 +343,10 @@ bool VariantShreddingState::ValueIsShredded(UnifiedVariantVectorData &variant, i return false; } if (type_id == VariantLogicalType::DECIMAL) { - auto physical_type = type.InternalType(); + // the shredded column stores the unscaled value, so it only holds decimals of its scale that fit its width auto decimal_data = VariantUtils::DecodeDecimalData(variant, row, values_index); - auto decimal_physical_type = decimal_data.GetPhysicalType(); - return physical_type == decimal_physical_type; + return type.InternalType() == decimal_data.GetPhysicalType() && + decimal_data.scale == DecimalType::GetScale(type) && decimal_data.width <= DecimalType::GetWidth(type); } return true; } diff --git a/src/duckdb/src/function/window/window_value_function.cpp b/src/duckdb/src/function/window/window_value_function.cpp index 6d9e66ecc..da06545c4 100644 --- a/src/duckdb/src/function/window/window_value_function.cpp +++ b/src/duckdb/src/function/window/window_value_function.cpp @@ -2,6 +2,7 @@ #include "duckdb/common/operator/add.hpp" #include "duckdb/common/operator/cast_operators.hpp" #include "duckdb/common/operator/interpolate.hpp" +#include "duckdb/common/operator/multiply.hpp" #include "duckdb/common/operator/subtract.hpp" #include "duckdb/function/window/window_aggregator.hpp" #include "duckdb/function/window/window_collection.hpp" @@ -681,7 +682,19 @@ void WindowLeadLagExecutor::GetData(ExecutionContext &context, DataChunk &eval_c frame = FrameBounds(frame_begin[i], MaxValue(frame_end[i], frame_begin[i])); const auto own_row = glstate.row_tree->Rank(frame.start, frame.end, row_idx) - 1; // (2) adjust the row number by adding or subtracting an offset - const auto val_idx = LeadLagIndex(wexpr.GetExpressionType(), NumericCast(own_row), offset); + auto val_idx = LeadLagIndex(wexpr.GetExpressionType(), NumericCast(own_row), offset); + const auto is_lead = (wexpr.GetExpressionType() == ExpressionType::WINDOW_LEAD); + if (row_idx < frame.start || frame.end <= row_idx) { + // An own row outside the frame ranks just before own_row instead of at it, + // so a zero shift is the own row itself and a forward shift lands one row earlier. + if (!offset) { + cursor.CopyCell(0, row_idx, result, i); + continue; + } + if (is_lead == (offset > 0)) { + --val_idx; + } + } const auto frame_width = NumericCast(frame.end - frame.start); if (val_idx >= 0 && val_idx < frame_width) { // (3) find the row at that offset @@ -739,13 +752,21 @@ void WindowLeadLagExecutor::GetData(ExecutionContext &context, DataChunk &eval_c idx_t delta = 0; if (val_idx < (int64_t)row_idx) { - // Count backwards + // Count backwards, starting from the last row at or before the own row delta = idx_t(row_idx - idx_t(val_idx)); - val_idx = int64_t(WindowBoundariesState::FindPrevStart(*ignore_nulls, partition_begin[i], row_idx, delta)); + const auto lo = partition_begin[i]; + const auto hi = MinValue(row_idx, partition_end[i]); + if (lo < hi) { + val_idx = int64_t(WindowBoundariesState::FindPrevStart(*ignore_nulls, lo, hi, delta)); + } } else if (val_idx > (int64_t)row_idx) { + // Count forwards, starting from the first row at or after the own row delta = idx_t(idx_t(val_idx) - row_idx); - val_idx = - int64_t(WindowBoundariesState::FindNextStart(*ignore_nulls, row_idx + 1, partition_end[i], delta)); + const auto lo = MaxValue(row_idx + 1, partition_begin[i]); + const auto hi = partition_end[i]; + if (lo < hi) { + val_idx = int64_t(WindowBoundariesState::FindNextStart(*ignore_nulls, lo, hi, delta)); + } } // else offset is zero, so don't move. @@ -1400,6 +1421,20 @@ static void FillInterpolateFunc(Vector &result, idx_t i, WindowCursor &cursor, i } FlatVector::SetNull(result, i, false); + if constexpr (std::is_integral::value) { + // The interpolated value lies between the two values, but the double result can round past them + const auto lo = MinValue(y0, y1); + const auto hi = MaxValue(y0, y1); + const auto interpolated = static_cast(y0) + (static_cast(y1) - static_cast(y0)) * slope; + if (interpolated <= static_cast(lo)) { + data[i] = lo; + } else if (interpolated >= static_cast(hi)) { + data[i] = hi; + } else { + data[i] = static_cast(interpolated); + } + return; + } data[i] = InterpolateOperator::Operation(y0, slope, y1); } @@ -1434,6 +1469,76 @@ static fill_interpolate_t GetFillInterpolateFunction(const LogicalType &type) { } } +// Interpolation and extrapolation only check the physical type for overflow, +// so the results still need to be checked against the range of the logical type +typedef bool (*fill_validate_t)(Vector &result, idx_t i, const LogicalType &type); + +template +static bool FillValidateTime(Vector &result, idx_t i, const LogicalType &type) { + const auto value = FlatVector::GetData(result)[i]; + return value >= 0 && value <= MAX_VALUE; +} + +template +static bool FillValidateDecimal(Vector &result, idx_t i, const LogicalType &type) { + const auto value = FlatVector::GetData(result)[i]; + T limit; + if (!TryCast::Operation(Hugeint::POWERS_OF_TEN[DecimalType::GetWidth(type)], limit)) { + return true; + } + return value < limit && value > -limit; +} + +template +static bool FillValidateFinite(Vector &result, idx_t i, const LogicalType &type) { + return Value::IsFinite(FlatVector::GetData(result)[i]); +} + +template +static bool FillValidateScaledTimestamp(Vector &result, idx_t i, const LogicalType &type) { + // the value must be representable in microseconds + int64_t micros; + if (!TryMultiplyOperator::Operation(FlatVector::GetData(result)[i], FACTOR, micros)) { + return false; + } + return Value::IsFinite(timestamp_t(micros)); +} + +static fill_validate_t GetFillValidateFunction(const LogicalType &type) { + switch (type.id()) { + case LogicalTypeId::TIME: + return FillValidateTime; + case LogicalTypeId::TIME_NS: + return FillValidateTime; + case LogicalTypeId::DATE: + return FillValidateFinite; + case LogicalTypeId::TIMESTAMP: + case LogicalTypeId::TIMESTAMP_TZ: + case LogicalTypeId::TIMESTAMP_NS: + case LogicalTypeId::TIMESTAMP_TZ_NS: + return FillValidateFinite; + case LogicalTypeId::TIMESTAMP_SEC: + return FillValidateScaledTimestamp; + case LogicalTypeId::TIMESTAMP_MS: + return FillValidateScaledTimestamp; + case LogicalTypeId::DECIMAL: + switch (type.InternalType()) { + case PhysicalType::INT16: + return FillValidateDecimal; + case PhysicalType::INT32: + return FillValidateDecimal; + case PhysicalType::INT64: + return FillValidateDecimal; + case PhysicalType::INT128: + return FillValidateDecimal; + default: + throw InternalException("Unsupported FILL decimal type."); + } + default: + return nullptr; + } +} + typedef bool (*fill_value_t)(idx_t i, WindowCursor &cursor); template @@ -1672,7 +1777,15 @@ void WindowFillExecutor::GetData(ExecutionContext &context, DataChunk &eval_chun idx_t next_valid = DConstants::INVALID_INDEX; const auto &wexpr = gfstate.executor.wexpr; - auto interpolate_func = GetFillInterpolateFunction(wexpr.GetChildren()[0]->GetReturnType()); + const auto &fill_type = wexpr.GetChildren()[0]->GetReturnType(); + auto interpolate_func = GetFillInterpolateFunction(fill_type); + auto validate_func = GetFillValidateFunction(fill_type); + auto interpolate = [&](idx_t i, idx_t lo, idx_t hi, double slope) { + interpolate_func(result, i, cursor, lo, hi, slope); + if (validate_func && FlatVector::Validity(result).RowIsValid(i) && !validate_func(result, i, fill_type)) { + FlatVector::SetNull(result, i, true); + } + }; auto value_func = GetFillValueFunction(wexpr.GetChildren()[0]->GetReturnType()); // Secondary sort - use the MSTs @@ -1783,7 +1896,7 @@ void WindowFillExecutor::GetData(ExecutionContext &context, DataChunk &eval_chun // Two values, so interpolate // y = y0 + (y1 - y0) / (x1 - x0) * (x - x0) const auto slope = slope_func(order_cursor, row_idx, prev_valid, next_valid); - interpolate_func(result, i, cursor, prev_valid, next_valid, slope); + interpolate(i, prev_valid, next_valid, slope); } return; } @@ -1891,7 +2004,7 @@ void WindowFillExecutor::GetData(ExecutionContext &context, DataChunk &eval_chun // Two values, so interpolate // y = y0 + (y1 - y0) / (x1 - x0) * (x - x0) const auto slope = slope_func(order_cursor, row_idx, prev_valid, next_valid); - interpolate_func(result, i, cursor, prev_valid, next_valid, slope); + interpolate(i, prev_valid, next_valid, slope); } } diff --git a/src/duckdb/src/include/duckdb/catalog/catalog_entry/sequence_catalog_entry.hpp b/src/duckdb/src/include/duckdb/catalog/catalog_entry/sequence_catalog_entry.hpp index b859af388..1cbf30d44 100644 --- a/src/duckdb/src/include/duckdb/catalog/catalog_entry/sequence_catalog_entry.hpp +++ b/src/duckdb/src/include/duckdb/catalog/catalog_entry/sequence_catalog_entry.hpp @@ -45,6 +45,18 @@ struct SequenceData { bool cycle; }; +//! The state of a sequence: shared by all versions of the catalog entry, so that altering the entry (e.g. setting a +//! comment) does not fork the counter +struct SequenceState { + explicit SequenceState(CreateSequenceInfo &info) : data(info) { + } + + //! Lock for getting a value on the sequence + mutable mutex lock; + //! Sequence data + SequenceData data; +}; + //! A sequence catalog entry class SequenceCatalogEntry : public StandardEntry { public: @@ -70,9 +82,7 @@ class SequenceCatalogEntry : public StandardEntry { string ToSQL() const override; private: - //! Lock for getting a value on the sequence - mutable mutex lock; - //! Sequence data - SequenceData data; + //! The sequence state (shared with the other versions of this entry) + shared_ptr state; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/enums/optimizer_type.hpp b/src/duckdb/src/include/duckdb/common/enums/optimizer_type.hpp index 5556ad1b4..7bfaca085 100644 --- a/src/duckdb/src/include/duckdb/common/enums/optimizer_type.hpp +++ b/src/duckdb/src/include/duckdb/common/enums/optimizer_type.hpp @@ -60,7 +60,8 @@ enum class OptimizerType : uint32_t { DISTINCT_AGGREGATE_REWRITE = 43, AGGREGATE_REUSE = 44, PROJECTION_PLACEMENT = 45, - REDUNDANT_ORDER_KEYS = 46 + REDUNDANT_ORDER_KEYS = 46, + DISJUNCTIVE_JOIN_REWRITER = 47 }; string OptimizerTypeToString(OptimizerType type); diff --git a/src/duckdb/src/include/duckdb/common/multi_file/multi_file_function.hpp b/src/duckdb/src/include/duckdb/common/multi_file/multi_file_function.hpp index 5a34fef5e..c9a98a4a9 100644 --- a/src/duckdb/src/include/duckdb/common/multi_file/multi_file_function.hpp +++ b/src/duckdb/src/include/duckdb/common/multi_file/multi_file_function.hpp @@ -156,6 +156,13 @@ class MultiFileFunction : public TableFunction { bool bound_on_first_file = true; if (result->multi_file_reader->Bind(result->file_options, *result->file_list, result->types, result->names, result->reader_bind)) { + // with the 'schema' option, hive partitioning is asked for (hive_partitioning or hive_types) rather than + // detected; the keys' types still are. Readers that bind a schema of their own skip this + if (!result->file_options.schema.empty() && + (result->file_options.hive_partitioning || !result->file_options.hive_types_schema.empty())) { + result->file_options.AutoDetectHivePartitioning(*result->file_list, context, + /* detect_partitioning = */ false); + } result->multi_file_reader->BindOptions(result->file_options, *result->file_list, result->types, result->names, result->reader_bind); bound_on_first_file = false; diff --git a/src/duckdb/src/include/duckdb/common/multi_file/multi_file_options.hpp b/src/duckdb/src/include/duckdb/common/multi_file/multi_file_options.hpp index bacc4526d..1de90627f 100644 --- a/src/duckdb/src/include/duckdb/common/multi_file/multi_file_options.hpp +++ b/src/duckdb/src/include/duckdb/common/multi_file/multi_file_options.hpp @@ -68,7 +68,12 @@ struct MultiFileOptions { DUCKDB_API void Serialize(Serializer &serializer) const; DUCKDB_API static MultiFileOptions Deserialize(Deserializer &source); DUCKDB_API void AddBatchInfo(BindInfo &bind_info) const; - DUCKDB_API void AutoDetectHivePartitioning(MultiFileList &files, ClientContext &context); + //! Settle the hive partitioning options against each other and against the files: hive_types implies + //! hive_partitioning, and the type of a partition key the caller did not give is taken from the values + //! in the paths. With detect_partitioning, WHETHER the files are partitioned is taken from the paths + //! too; a caller that already knows the scan's schema passes false and asks for partitioning itself + DUCKDB_API void AutoDetectHivePartitioning(MultiFileList &files, ClientContext &context, + bool detect_partitioning = true); DUCKDB_API static bool AutoDetectHivePartitioningInternal(MultiFileList &files, ClientContext &context); DUCKDB_API void AutoDetectHiveTypesInternal(MultiFileList &files, ClientContext &context); DUCKDB_API void VerifyHiveTypesArePartitions(const std::map &partitions) const; diff --git a/src/duckdb/src/include/duckdb/common/operator/convert_to_string.hpp b/src/duckdb/src/include/duckdb/common/operator/convert_to_string.hpp index 841e2cb43..9e5547baa 100644 --- a/src/duckdb/src/include/duckdb/common/operator/convert_to_string.hpp +++ b/src/duckdb/src/include/duckdb/common/operator/convert_to_string.hpp @@ -64,5 +64,7 @@ template <> DUCKDB_API string ConvertToString::Operation(timestamp_tz_ns_t input); template <> DUCKDB_API string ConvertToString::Operation(string_t input); +template <> +DUCKDB_API string ConvertToString::Operation(bignum_t input); } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/operator/decimal_cast_operators.hpp b/src/duckdb/src/include/duckdb/common/operator/decimal_cast_operators.hpp index 755515ff9..e8ce0ed86 100644 --- a/src/duckdb/src/include/duckdb/common/operator/decimal_cast_operators.hpp +++ b/src/duckdb/src/include/duckdb/common/operator/decimal_cast_operators.hpp @@ -595,7 +595,8 @@ struct DecimalCastOperation { return false; } } - return true; + // decimals moved before the decimal point by the exponent do not count towards the digit count + return WithinLimit(state); } } @@ -662,6 +663,11 @@ struct DecimalCastOperation { for (uint8_t i = state.decimal_count; i < state.scale; i++) { state.result *= 10; } + return WithinLimit(state); + } + + template + static bool WithinLimit(T &state) { if (NEGATIVE) { return state.result > -state.limit; } else { diff --git a/src/duckdb/src/include/duckdb/common/types/bignum.hpp b/src/duckdb/src/include/duckdb/common/types/bignum.hpp index 898b10bcc..43693e94b 100644 --- a/src/duckdb/src/include/duckdb/common/types/bignum.hpp +++ b/src/duckdb/src/include/duckdb/common/types/bignum.hpp @@ -90,20 +90,32 @@ class Bignum { if (is_negative) { byte = static_cast(~byte); } + if (abs_value > (NumericLimits::Maximum() >> 8)) { + // the absolute value does not fit in 128 bits + return false; + } abs_value = (abs_value << 8) | byte; } - if (is_negative) { - if (abs_value > static_cast(std::numeric_limits::max()) + 1) { - throw OutOfRangeException("Negative bignum too small for type"); - } - result = static_cast(-static_cast(abs_value)); - } else { - if (abs_value > static_cast(std::numeric_limits::max())) { - throw OutOfRangeException("Positive bignum too large for type"); + const auto max_value = static_cast(NumericLimits::Maximum()); + if (!is_negative || abs_value == 0) { + if (abs_value > max_value) { + return false; } result = static_cast(abs_value); + return true; + } + if (!NumericLimits::IsSigned()) { + return false; + } + if (abs_value > max_value + 1) { + return false; + } + if (abs_value == max_value + 1) { + result = NumericLimits::Minimum(); + return true; } + result = static_cast(-static_cast(abs_value)); return true; } }; diff --git a/src/duckdb/src/include/duckdb/common/types/time.hpp b/src/duckdb/src/include/duckdb/common/types/time.hpp index d15c2752d..420938679 100644 --- a/src/duckdb/src/include/duckdb/common/types/time.hpp +++ b/src/duckdb/src/include/duckdb/common/types/time.hpp @@ -35,7 +35,7 @@ class Time { //! Convert a time object to a string in the format "hh:mm:ss" DUCKDB_API static string ToString(dtime_t time); //! Convert a UTC offset to ±HH[:MM] - DUCKDB_API static string ToUTCOffset(int hour_offset, int minute_offset); + DUCKDB_API static string ToUTCOffset(int hour_offset, int minute_offset, int second_offset = 0); DUCKDB_API static dtime_t FromTime(int32_t hour, int32_t minute, int32_t second, int32_t microseconds = 0); DUCKDB_API static int64_t ToNanoTime(int32_t hour, int32_t minute, int32_t second, int32_t nanoseconds = 0); diff --git a/src/duckdb/src/include/duckdb/common/vector_operations/aggregate_executor.hpp b/src/duckdb/src/include/duckdb/common/vector_operations/aggregate_executor.hpp index 618bc14ff..34e81dd0f 100644 --- a/src/duckdb/src/include/duckdb/common/vector_operations/aggregate_executor.hpp +++ b/src/duckdb/src/include/duckdb/common/vector_operations/aggregate_executor.hpp @@ -233,6 +233,42 @@ class AggregateExecutor { } } +#if !DUCKDB_SMALLER_BINARY(aggregate_executor_flat) + template + static inline void BinaryFlatLoop(const A_TYPE *__restrict adata, AggregateInputData &aggr_input_data, + const B_TYPE *__restrict bdata, STATE_TYPE **__restrict states, idx_t count, + const ValidityMask &avalidity, const ValidityMask &bvalidity) { + AggregateBinaryInput input(aggr_input_data, avalidity, bvalidity); + if (OP::IgnoreNull() && (avalidity.CanHaveNull() || bvalidity.CanHaveNull())) { + const auto entry_count = ValidityMask::EntryCount(count); + for (idx_t entry_idx = 0; entry_idx < entry_count; entry_idx++) { + auto validity_entry = avalidity.GetValidityEntry(entry_idx) & bvalidity.GetValidityEntry(entry_idx); + const auto start = entry_idx * ValidityMask::BITS_PER_VALUE; + const auto next = MinValue(start + ValidityMask::BITS_PER_VALUE, count); + if (ValidityMask::AllValid(validity_entry)) { + for (idx_t i = start; i < next; i++) { + input.lidx = input.ridx = i; + OP::template Operation(*states[i], adata[i], bdata[i], input); + } + } else { + validity_entry &= ValidityMask::EntryWithValidBits(next - start); + while (validity_entry) { + const auto i = start + CountZeros::Trailing(validity_entry); + input.lidx = input.ridx = i; + OP::template Operation(*states[i], adata[i], bdata[i], input); + validity_entry &= validity_entry - 1; + } + } + } + } else { + for (idx_t i = 0; i < count; i++) { + input.lidx = input.ridx = i; + OP::template Operation(*states[i], adata[i], bdata[i], input); + } + } + } +#endif + template static inline void BinaryScatterLoop(const A_TYPE *__restrict adata, AggregateInputData &aggr_input_data, const B_TYPE *__restrict bdata, STATE_TYPE **__restrict states, idx_t count, @@ -812,6 +848,16 @@ class AggregateExecutor { template static void BinaryScatter(AggregateInputData &aggr_input_data, Vector &a, Vector &b, Vector &states, idx_t count) { +#if !DUCKDB_SMALLER_BINARY(aggregate_executor_flat) + if (OP::IgnoreNull() && a.GetVectorType() == VectorType::FLAT_VECTOR && + b.GetVectorType() == VectorType::FLAT_VECTOR && states.GetVectorType() == VectorType::FLAT_VECTOR) { + BinaryFlatLoop(FlatVector::GetData(a), aggr_input_data, + FlatVector::GetData(b), + FlatVector::GetDataMutable(states), count, + FlatVector::Validity(a), FlatVector::Validity(b)); + return; + } +#endif UnifiedVectorFormat adata, bdata, sdata; a.ToUnifiedFormat(adata); diff --git a/src/duckdb/src/include/duckdb/execution/index/fixed_size_allocator.hpp b/src/duckdb/src/include/duckdb/execution/index/fixed_size_allocator.hpp index e6ac2b80a..f56097a18 100644 --- a/src/duckdb/src/include/duckdb/execution/index/fixed_size_allocator.hpp +++ b/src/duckdb/src/include/duckdb/execution/index/fixed_size_allocator.hpp @@ -47,13 +47,8 @@ class FixedSizeAllocator { //! Get a segment handle. inline SegmentHandle GetHandle(const IndexPointer ptr) { - D_ASSERT(ptr.GetOffset() < available_segments_per_buffer); - - auto buffer_it = buffers.find(ptr.GetBufferId()); - D_ASSERT(buffer_it != buffers.end()); - + auto &buffer = GetBuffer(ptr); auto offset = ptr.GetOffset() * segment_size + bitmask_offset; - auto &buffer = *buffer_it->second; return SegmentHandle(buffer, offset); } @@ -67,13 +62,9 @@ class FixedSizeAllocator { //! Returns the data_ptr_t to a segment, and sets the dirty flag of the buffer containing that segment. //! DEPRECATED. Use segment handles. inline data_ptr_t Get(const IndexPointer ptr, const bool dirty = true) { - D_ASSERT(ptr.GetOffset() < available_segments_per_buffer); - - auto buffer_it = buffers.find(ptr.GetBufferId()); - D_ASSERT(buffer_it != buffers.end()); - + auto &buffer = GetBuffer(ptr); auto offset = ptr.GetOffset() * segment_size + bitmask_offset; - auto buffer_ptr = buffer_it->second->GetDeprecated(dirty); + auto buffer_ptr = buffer.GetDeprecated(dirty); return buffer_ptr + offset; } @@ -99,15 +90,12 @@ class FixedSizeAllocator { //! Returns the data_ptr_t to a segment, or nullptr, if the buffer is not in memory. //! DEPRECATED. Use segment handles. inline data_ptr_t GetIfLoaded(const IndexPointer ptr) { - D_ASSERT(ptr.GetOffset() < available_segments_per_buffer); - D_ASSERT(buffers.find(ptr.GetBufferId()) != buffers.end()); - - auto &buffer = buffers.find(ptr.GetBufferId())->second; - if (!buffer->InMemory()) { + auto &buffer = GetBuffer(ptr); + if (!buffer.InMemory()) { return nullptr; } - auto buffer_ptr = buffer->GetDeprecated(); + auto buffer_ptr = buffer.GetDeprecated(); auto raw_ptr = buffer_ptr + ptr.GetOffset() * segment_size + bitmask_offset; return raw_ptr; } @@ -167,6 +155,17 @@ class FixedSizeAllocator { void VerifyBuffers(); private: + //! Returns the buffer containing the segment of the IndexPointer. Pointers are read from storage, so we verify + //! that they point to a segment of an existing buffer + inline FixedSizeBuffer &GetBuffer(const IndexPointer ptr) const { + auto buffer_it = buffers.find(ptr.GetBufferId()); + if (ptr.GetOffset() >= available_segments_per_buffer || buffer_it == buffers.end()) { + ThrowInvalidPointer(ptr); + } + return *buffer_it->second; + } + [[noreturn]] void ThrowInvalidPointer(const IndexPointer ptr) const; + //! Memory tag of memory that is allocated through the allocator MemoryTag memory_tag; //! Allocation size of one segment in a buffer diff --git a/src/duckdb/src/include/duckdb/function/cast/nested_to_varchar_cast.hpp b/src/duckdb/src/include/duckdb/function/cast/nested_to_varchar_cast.hpp index 3eddcfab4..71dcab98d 100644 --- a/src/duckdb/src/include/duckdb/function/cast/nested_to_varchar_cast.hpp +++ b/src/duckdb/src/include/duckdb/function/cast/nested_to_varchar_cast.hpp @@ -1,10 +1,16 @@ #pragma once +#include "duckdb/function/cast/default_casts.hpp" + namespace duckdb { struct NestedToVarcharCast { public: static const bool LOOKUP_TABLE[256]; + + //! Wraps the VARCHAR cast of a child of a nested type that is cast to VARCHAR. UNION and VARIANT children + //! write their values as strings - these are quoted and escaped like VARCHAR children, unless the value is nested + static void WrapChildCast(BoundCastInfo &child_cast, const LogicalType &child_type); }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/config.hpp b/src/duckdb/src/include/duckdb/main/config.hpp index 757da6cd5..19507036b 100644 --- a/src/duckdb/src/include/duckdb/main/config.hpp +++ b/src/duckdb/src/include/duckdb/main/config.hpp @@ -364,6 +364,11 @@ struct DBConfig { void AddAllowedConfig(const Identifier &config_name); void AddAllowedDirectory(const string &path); void AddAllowedPath(const string &path); + //! Replace / reset the allowed directories and paths as a whole (under the lock that guards them) + void SetAllowedDirectories(const vector &paths); + void SetAllowedPaths(const vector &paths); + void ResetAllowedDirectories(); + void ResetAllowedPaths(); //! Whether the given optimizer is disabled (disabled_optimizers) DUCKDB_API bool IsOptimizerDisabled(OptimizerType type) const; DUCKDB_API set GetDisabledOptimizers() const; diff --git a/src/duckdb/src/include/duckdb/main/extension_entries.hpp b/src/duckdb/src/include/duckdb/main/extension_entries.hpp index 0671844d9..91b920f7b 100644 --- a/src/duckdb/src/include/duckdb/main/extension_entries.hpp +++ b/src/duckdb/src/include/duckdb/main/extension_entries.hpp @@ -403,6 +403,7 @@ static constexpr ExtensionFunctionEntry EXTENSION_FUNCTIONS[] = { {"iceberg_bucket", "iceberg", CatalogType::SCALAR_FUNCTION_ENTRY}, {"iceberg_column_stats", "iceberg", CatalogType::TABLE_FUNCTION_ENTRY}, {"iceberg_load_table_response", "iceberg", CatalogType::TABLE_FUNCTION_ENTRY}, + {"iceberg_manifests", "iceberg", CatalogType::TABLE_FUNCTION_ENTRY}, {"iceberg_metadata", "iceberg", CatalogType::TABLE_FUNCTION_ENTRY}, {"iceberg_partition_stats", "iceberg", CatalogType::TABLE_FUNCTION_ENTRY}, {"iceberg_rewrite_data_files", "iceberg", CatalogType::TABLE_FUNCTION_ENTRY}, @@ -415,6 +416,7 @@ static constexpr ExtensionFunctionEntry EXTENSION_FUNCTIONS[] = { {"iceberg_table_properties", "iceberg", CatalogType::TABLE_FUNCTION_ENTRY}, {"iceberg_to_ducklake", "iceberg", CatalogType::TABLE_FUNCTION_ENTRY}, {"iceberg_truncate", "iceberg", CatalogType::SCALAR_FUNCTION_ENTRY}, + {"iceberg_view_metadata", "iceberg", CatalogType::TABLE_FUNCTION_ENTRY}, {"icu_calendar_names", "icu", CatalogType::TABLE_FUNCTION_ENTRY}, {"icu_collate_aa", "icu", CatalogType::SCALAR_FUNCTION_ENTRY}, {"icu_collate_af", "icu", CatalogType::SCALAR_FUNCTION_ENTRY}, @@ -735,6 +737,7 @@ static constexpr ExtensionFunctionEntry EXTENSION_FUNCTIONS[] = { {"quack_check_token", "quack", CatalogType::SCALAR_FUNCTION_ENTRY}, {"quack_clear_cache", "quack", CatalogType::TABLE_FUNCTION_ENTRY}, {"quack_connection_id", "quack", CatalogType::SCALAR_FUNCTION_ENTRY}, + {"quack_generate_keys", "quack", CatalogType::TABLE_FUNCTION_ENTRY}, {"quack_identify", "quack", CatalogType::TABLE_FUNCTION_ENTRY}, {"quack_nop_authorization", "quack", CatalogType::SCALAR_FUNCTION_ENTRY}, {"quack_query", "quack", CatalogType::TABLE_FUNCTION_ENTRY}, @@ -1350,6 +1353,7 @@ static constexpr ExtensionEntry EXTENSION_SETTINGS[] = { {"iceberg_default_format_version", "iceberg"}, {"iceberg_equality_delete_fast_filter", "iceberg"}, {"iceberg_logging_post_body_truncate_limit", "iceberg"}, + {"iceberg_metadata_log_clock_skew_ms", "iceberg"}, {"iceberg_test_force_token_expiry", "iceberg"}, {"iceberg_unsafe_skip_puffin_verification", "iceberg"}, {"iceberg_use_metadata_log", "iceberg"}, @@ -1414,7 +1418,6 @@ static constexpr ExtensionEntry EXTENSION_SETTINGS[] = { {"quack_default_client_id", "quack"}, {"quack_default_heartbeat_timeout", "quack"}, {"quack_enable_reconnects", "quack"}, - {"quack_fetch_producer_buffer_bytes", "quack"}, {"quack_fetch_read_ahead", "quack"}, {"quack_loaded_at_us", "quack"}, {"quack_prepare_inline_rows", "quack"}, @@ -1422,6 +1425,7 @@ static constexpr ExtensionEntry EXTENSION_SETTINGS[] = { {"quack_result_ttl", "quack"}, {"quack_server_keep_alive_timeout", "quack"}, {"quack_server_max_connections", "quack"}, + {"quack_server_stream_buffer_bytes", "quack"}, {"quack_target_batch_bytes", "quack"}, {"rtree_index_scan_min_rows", "spatial"}, {"rtree_index_scan_ratio", "spatial"}, diff --git a/src/duckdb/src/include/duckdb/main/secret/default_secrets.hpp b/src/duckdb/src/include/duckdb/main/secret/default_secrets.hpp index 99a81cbb6..010b1afe9 100644 --- a/src/duckdb/src/include/duckdb/main/secret/default_secrets.hpp +++ b/src/duckdb/src/include/duckdb/main/secret/default_secrets.hpp @@ -9,6 +9,7 @@ #pragma once #include "duckdb/common/common.hpp" +#include "duckdb/common/identifier.hpp" namespace duckdb { class DatabaseInstance; @@ -25,6 +26,8 @@ struct CreateHTTPSecretFunctions { static vector GetDefaultSecretTypes(); //! Get the default secret functions static vector GetDefaultSecretFunctions(); + //! Current sensitive keys for both new and persisted HTTP secrets. + static identifier_set_t GetRedactedKeys(); protected: //! HTTP secret CONFIG provider diff --git a/src/duckdb/src/include/duckdb/main/secret/secret.hpp b/src/duckdb/src/include/duckdb/main/secret/secret.hpp index 6183a6e5c..7299301da 100644 --- a/src/duckdb/src/include/duckdb/main/secret/secret.hpp +++ b/src/duckdb/src/include/duckdb/main/secret/secret.hpp @@ -217,13 +217,7 @@ class KeyValueSecret : public BaseSecret { Deserialize(Deserializer &deserializer, BaseSecret base_secret) { // NOLINT(performance-unnecessary-value-param): callback signature. auto result = make_uniq(base_secret); - Value secret_map_value; - deserializer.ReadProperty(201, "secret_map", secret_map_value); - - for (const auto &entry : ListValue::GetChildren(secret_map_value)) { - auto kv_struct = StructValue::GetChildren(entry); - result->secret_map[Identifier(kv_struct[0].ToString())] = kv_struct[1]; - } + ReadSecretMap(deserializer, result->secret_map); Value redact_set_value; deserializer.ReadProperty(202, "redact_keys", redact_set_value); @@ -261,6 +255,9 @@ class KeyValueSecret : public BaseSecret { identifier_tree_t secret_map; //! keys that are sensitive and should be redacted identifier_set_t redact_keys; + +private: + static void ReadSecretMap(Deserializer &deserializer, identifier_tree_t &result); }; // Helper class to fetch secret parameters in a cascading way. The idea being that in many cases there is a direct diff --git a/src/duckdb/src/include/duckdb/optimizer/disjunctive_join_rewriter.hpp b/src/duckdb/src/include/duckdb/optimizer/disjunctive_join_rewriter.hpp new file mode 100644 index 000000000..5c411c70d --- /dev/null +++ b/src/duckdb/src/include/duckdb/optimizer/disjunctive_join_rewriter.hpp @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/optimizer/disjunctive_join_rewriter.hpp +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/optimizer/column_binding_replacer.hpp" +#include "duckdb/planner/binder.hpp" + +namespace duckdb { + +struct JoinCondition; + +class DisjunctiveJoinRewriter { +public: + explicit DisjunctiveJoinRewriter(Binder &binder); + + unique_ptr Optimize(unique_ptr op); + +private: + struct Branch { + unique_ptr left_expr; + unique_ptr right_expr; + ExpressionType comparison_type; + }; + + struct CTEInfo { + TableIndex table_index; + vector output_types; + vector original_bindings; + }; + + Binder &binder; + + TableIndex NewTableIndex(); + + unique_ptr OptimizeInternal(unique_ptr op, BindingReplacementGraph &replacements); + + bool ShouldRewrite(const LogicalOperator &join, const unordered_set &left_tables, + const unordered_set &right_tables, vector &out_branches) const; + bool FlattenOR(const Expression &expr, const unordered_set &left_tables, + const unordered_set &right_tables, vector &out) const; + + // Main builders for each join type + unique_ptr BuildInnerJoin(const CTEInfo &left_cte, const CTEInfo &right_cte, + const vector &branches); + unique_ptr BuildLeftJoin(const CTEInfo &left_cte, const CTEInfo &right_cte, + const vector &branches); + unique_ptr BuildRightJoin(const CTEInfo &left_cte, const CTEInfo &right_cte, + const vector &branches); + unique_ptr BuildFullJoin(const CTEInfo &left_cte, const CTEInfo &right_cte, + const vector &branches); + unique_ptr BuildSemiJoin(const CTEInfo &left_cte, const CTEInfo &right_cte, + const vector &branches); + unique_ptr BuildAntiJoin(const CTEInfo &left_cte, const CTEInfo &right_cte, + const vector &branches); + + // Helper builders + vector> CreateMatchedBranches(const CTEInfo &left_cte, const CTEInfo &right_cte, + const vector &branches); + unique_ptr CreateBranchJoin(const CTEInfo &left_cte, const CTEInfo &right_cte, + const vector &branches, idx_t branch_idx); + unique_ptr CreateUnmatchedProbeSide(const CTEInfo &probe_cte, const CTEInfo &build_cte, + const vector &branches); + unique_ptr CreateUnmatchedBuildSide(const CTEInfo &left_cte, const CTEInfo &right_cte, + const vector &branches); + unique_ptr CreateAntiJoinChain(const CTEInfo &probe_cte, const CTEInfo &build_cte, + const vector &branches, TableIndex probe_ref_idx); + + unique_ptr CreateUnionAll(vector> children, idx_t total_columns); + unique_ptr NormalizeOutput(unique_ptr plan, + const vector &orig_bindings, + const vector &orig_types, const CTEInfo &left_cte, + const CTEInfo &right_cte, JoinType join_type, + BindingReplacementGraph &replacements); + + // Expression and Operator utilities + JoinCondition CreateJoinCondition(const Branch &branch, ColumnBindingReplacer &left_replacer, + ColumnBindingReplacer &right_replacer); + + unique_ptr CreateCTERef(const CTEInfo &cte, TableIndex ref_idx); + unique_ptr CreateProjection(unique_ptr child, + vector> expressions); + unique_ptr CreateColRef(ColumnBinding binding, const LogicalType &type, const string &alias = ""); + unique_ptr CreateNullExpr(const LogicalType &type); + vector> CreateColRefs(TableIndex table_index, const vector &types); + vector> CreateNullExprs(const vector &types); + + vector SwapBranches(const vector &branches); +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/parser_options.hpp b/src/duckdb/src/include/duckdb/parser/parser_options.hpp index c36fc63fc..65bb77f87 100644 --- a/src/duckdb/src/include/duckdb/parser/parser_options.hpp +++ b/src/duckdb/src/include/duckdb/parser/parser_options.hpp @@ -38,6 +38,8 @@ struct ParserOptions { optional_ptr extensions; AllowParserOverride parser_override_setting = AllowParserOverride::DEFAULT_OVERRIDE; shared_ptr compiled_grammar; + //! The client context the statement is parsed for (not set when parsing without a client context) + optional_ptr context; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/ast/expression_chain.hpp b/src/duckdb/src/include/duckdb/parser/peg/ast/expression_chain.hpp index d1d76b01e..a2054a041 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/ast/expression_chain.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/ast/expression_chain.hpp @@ -11,13 +11,13 @@ struct IsDistinctFromTail { struct ComparisonExpressionTail { ExpressionType comparison_type; - vector not_keywords; unique_ptr expression; }; struct BetweenInLikeOperator { bool has_not = false; unique_ptr expression; + vector> casts; }; struct ParsedOperator { @@ -31,7 +31,7 @@ struct OtherOperatorTail { unique_ptr expression; }; -enum class ExpressionTailType : uint8_t { IS_TEST, DISTINCT, COMPARISON, OTHER_OPERATOR }; +enum class ExpressionTailType : uint8_t { IS_TEST, DISTINCT, COMPARISON, OTHER_OPERATOR, CAST }; struct IsExpressionTail { ExpressionTailType type = ExpressionTailType::IS_TEST; @@ -39,6 +39,7 @@ struct IsExpressionTail { IsDistinctFromTail distinct; ComparisonExpressionTail comparison; OtherOperatorTail other; + unique_ptr cast; }; struct BinaryExpressionTail { diff --git a/src/duckdb/src/include/duckdb/parser/peg/inlined_grammar.hpp b/src/duckdb/src/include/duckdb/parser/peg/inlined_grammar.hpp index a0dc651e1..e90818164 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/inlined_grammar.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/inlined_grammar.hpp @@ -1452,7 +1452,7 @@ const unsigned char INLINED_PEG_GRAMMAR[] = { 9, 68, 114, 111, 112, 84, 114, 105, 103, 103, 101, 114, 10, 68, 114, 111, 112, 84, 114, 105, 103, 103, 101, 114, 32, 60, 45, 32, 39, 84, 82, 73, 71, 71, 69, 82, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 84, 114, 105, 103, 103, 101, 114, 78, 97, 109, 101, 32, 39, 79, 78, 39, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 10, 68, 114, 111, 112, 84, 97, 98, 108, 101, 32, 60, 45, 32, 84, 97, 98, 108, 101, 79, 114, 86, 105, 101, 119, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 41, 10, - 68, 114, 111, 112, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 67, 111, 109, 109, 101, 110, 116, 77, 97, 99, 114, 111, 84, 97, 98, 108, 101, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 41, 10, + 68, 114, 111, 112, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 67, 111, 109, 109, 101, 110, 116, 77, 97, 99, 114, 111, 84, 97, 98, 108, 101, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 41, 10, 68, 114, 111, 112, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 70, 117, 110, 99, 116, 105, 111, 110, 84, 121, 112, 101, 77, 97, 99, 114, 111, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 70, 117, 110, 99, 116, 105, 111, 110, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 41, 10, 68, 114, 111, 112, 83, 99, 104, 101, 109, 97, 32, 60, 45, 32, 39, 83, 67, 72, 69, 77, 65, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 41, 10, 68, 114, 111, 112, 73, 110, 100, 101, 120, 32, 60, 45, 32, 39, 73, 78, 68, 69, 88, 39, 32, 73, 102, 69, 120, 105, 115, 116, 115, 63, 32, 76, 105, 115, 116, 40, 81, 117, 97, 108, 105, 102, 105, 101, 100, 73, 110, 100, 101, 120, 78, 97, 109, 101, 41, 10, @@ -1675,11 +1675,12 @@ const unsigned char INLINED_PEG_GRAMMAR[] = { 35, 32, 76, 69, 86, 69, 76, 32, 52, 10, 73, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, 114, 111, 109, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 73, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 67, 111, 110, 116, 105, 110, 117, 97, 116, 105, 111, 110, 63, 10, 73, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 67, 111, 110, 116, 105, 110, 117, 97, 116, 105, 111, 110, 32, 60, 45, 32, 73, 115, 84, 101, 115, 116, 32, 73, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, - 73, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 73, 115, 84, 101, 115, 116, 84, 97, 105, 108, 32, 47, 32, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 84, 97, 105, 108, 32, 47, 32, 73, 115, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 84, 97, 105, 108, 32, 47, 32, 73, 115, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 84, 97, 105, 108, 10, + 73, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 73, 115, 84, 101, 115, 116, 84, 97, 105, 108, 32, 47, 32, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 84, 97, 105, 108, 32, 47, 32, 73, 115, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 84, 97, 105, 108, 32, 47, 32, 73, 115, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 84, 97, 105, 108, 32, 47, 32, 73, 115, 67, 97, 115, 116, 84, 97, 105, 108, 10, 73, 115, 84, 101, 115, 116, 84, 97, 105, 108, 32, 60, 45, 32, 73, 115, 84, 101, 115, 116, 10, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 84, 97, 105, 108, 32, 60, 45, 32, 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, 114, 111, 109, 84, 97, 105, 108, 10, 73, 115, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 10, 73, 115, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 84, 97, 105, 108, 32, 60, 45, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 84, 97, 105, 108, 10, + 73, 115, 67, 97, 115, 116, 84, 97, 105, 108, 32, 60, 45, 32, 67, 97, 115, 116, 79, 112, 101, 114, 97, 116, 111, 114, 10, 73, 115, 84, 101, 115, 116, 32, 60, 45, 32, 73, 115, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 78, 111, 116, 78, 117, 108, 108, 32, 47, 32, 73, 115, 78, 117, 108, 108, 10, 73, 115, 76, 105, 116, 101, 114, 97, 108, 32, 60, 45, 32, 39, 73, 83, 39, 32, 39, 78, 79, 84, 39, 63, 32, 73, 115, 76, 105, 116, 101, 114, 97, 108, 86, 97, 108, 117, 101, 10, 73, 115, 76, 105, 116, 101, 114, 97, 108, 86, 97, 108, 117, 101, 32, 60, 45, 32, 84, 114, 117, 101, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 70, 97, 108, 115, 101, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 78, 117, 108, 108, 76, 105, 116, 101, 114, 97, 108, 32, 47, 32, 85, 110, 107, 110, 111, 119, 110, 76, 105, 116, 101, 114, 97, 108, 10, @@ -1695,7 +1696,10 @@ const unsigned char INLINED_PEG_GRAMMAR[] = { 73, 115, 68, 105, 115, 116, 105, 110, 99, 116, 70, 114, 111, 109, 79, 112, 32, 60, 45, 32, 39, 73, 83, 39, 32, 39, 78, 79, 84, 39, 63, 32, 39, 68, 73, 83, 84, 73, 78, 67, 84, 39, 32, 39, 70, 82, 79, 77, 39, 10, 35, 32, 76, 69, 86, 69, 76, 32, 54, 10, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 66, 101, 116, 119, 101, 101, 110, 73, 110, 76, 105, 107, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 42, 10, - 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 79, 112, 101, 114, 97, 116, 111, 114, 32, 78, 111, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 63, 32, 66, 101, 116, 119, 101, 101, 110, 73, 110, 76, 105, 107, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 84, 97, 105, 108, 32, 60, 45, 32, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 79, 112, 101, 114, 97, 116, 111, 114, 32, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 79, 112, 101, 114, 97, 110, 100, 10, + 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 79, 112, 101, 114, 97, 110, 100, 32, 60, 45, 32, 78, 111, 116, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 79, 112, 101, 114, 97, 110, 100, 32, 47, 32, 66, 101, 116, 119, 101, 101, 110, 73, 110, 76, 105, 107, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, + 35, 32, 78, 79, 84, 32, 97, 102, 116, 101, 114, 32, 97, 32, 99, 111, 109, 112, 97, 114, 105, 115, 111, 110, 32, 111, 112, 101, 114, 97, 116, 111, 114, 32, 98, 105, 110, 100, 115, 32, 101, 118, 101, 114, 121, 116, 104, 105, 110, 103, 32, 117, 112, 32, 116, 111, 32, 116, 104, 101, 32, 73, 83, 32, 108, 101, 118, 101, 108, 58, 32, 97, 32, 60, 32, 78, 79, 84, 32, 98, 32, 73, 83, 32, 78, 85, 76, 76, 32, 105, 115, 32, 97, 32, 60, 32, 40, 78, 79, 84, 32, 40, 98, 32, 73, 83, 32, 78, 85, 76, 76, 41, 41, 10, + 78, 111, 116, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 79, 112, 101, 114, 97, 110, 100, 32, 60, 45, 32, 78, 111, 116, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 73, 115, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 67, 111, 109, 112, 97, 114, 105, 115, 111, 110, 79, 112, 101, 114, 97, 116, 111, 114, 32, 60, 45, 10, 32, 32, 32, 32, 79, 112, 101, 114, 97, 116, 111, 114, 69, 113, 117, 97, 108, 32, 47, 10, 32, 32, 32, 32, 79, 112, 101, 114, 97, 116, 111, 114, 78, 111, 116, 69, 113, 117, 97, 108, 32, 47, 10, @@ -1711,7 +1715,7 @@ const unsigned char INLINED_PEG_GRAMMAR[] = { 79, 112, 101, 114, 97, 116, 111, 114, 71, 114, 101, 97, 116, 101, 114, 84, 104, 97, 110, 69, 113, 117, 97, 108, 115, 32, 60, 45, 32, 39, 62, 61, 39, 10, 35, 32, 76, 69, 86, 69, 76, 32, 55, 10, 66, 101, 116, 119, 101, 101, 110, 73, 110, 76, 105, 107, 101, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 73, 110, 80, 114, 101, 100, 105, 99, 97, 116, 101, 42, 32, 66, 101, 116, 119, 101, 101, 110, 76, 105, 107, 101, 79, 112, 63, 10, - 73, 110, 80, 114, 101, 100, 105, 99, 97, 116, 101, 32, 60, 45, 32, 39, 78, 79, 84, 39, 63, 32, 73, 110, 67, 108, 97, 117, 115, 101, 10, + 73, 110, 80, 114, 101, 100, 105, 99, 97, 116, 101, 32, 60, 45, 32, 39, 78, 79, 84, 39, 63, 32, 73, 110, 67, 108, 97, 117, 115, 101, 32, 67, 97, 115, 116, 79, 112, 101, 114, 97, 116, 111, 114, 42, 10, 66, 101, 116, 119, 101, 101, 110, 76, 105, 107, 101, 79, 112, 32, 60, 45, 32, 39, 78, 79, 84, 39, 63, 32, 66, 101, 116, 119, 101, 101, 110, 76, 105, 107, 101, 79, 112, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 66, 101, 116, 119, 101, 101, 110, 76, 105, 107, 101, 79, 112, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 60, 45, 32, 66, 101, 116, 119, 101, 101, 110, 67, 108, 97, 117, 115, 101, 32, 47, 32, 76, 105, 107, 101, 67, 108, 97, 117, 115, 101, 10, 76, 105, 107, 101, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 76, 105, 107, 101, 86, 97, 114, 105, 97, 116, 105, 111, 110, 115, 32, 79, 116, 104, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 32, 69, 115, 99, 97, 112, 101, 67, 108, 97, 117, 115, 101, 63, 10, @@ -2076,7 +2080,7 @@ const unsigned char INLINED_PEG_GRAMMAR[] = { 84, 97, 98, 108, 101, 83, 117, 98, 113, 117, 101, 114, 121, 32, 60, 45, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 67, 111, 108, 111, 110, 63, 32, 76, 97, 116, 101, 114, 97, 108, 63, 32, 83, 117, 98, 113, 117, 101, 114, 121, 82, 101, 102, 101, 114, 101, 110, 99, 101, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 32, 83, 97, 109, 112, 108, 101, 67, 108, 97, 117, 115, 101, 63, 10, 66, 97, 115, 101, 84, 97, 98, 108, 101, 82, 101, 102, 32, 60, 45, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 67, 111, 108, 111, 110, 63, 32, 66, 97, 115, 101, 84, 97, 98, 108, 101, 78, 97, 109, 101, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 32, 65, 116, 67, 108, 97, 117, 115, 101, 63, 32, 83, 97, 109, 112, 108, 101, 67, 108, 97, 117, 115, 101, 63, 10, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 67, 111, 108, 111, 110, 32, 60, 45, 32, 67, 111, 108, 73, 100, 79, 114, 83, 116, 114, 105, 110, 103, 32, 39, 58, 39, 10, - 86, 97, 108, 117, 101, 115, 82, 101, 102, 32, 60, 45, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 67, 111, 108, 111, 110, 63, 32, 86, 97, 108, 117, 101, 115, 67, 108, 97, 117, 115, 101, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 10, + 86, 97, 108, 117, 101, 115, 82, 101, 102, 32, 60, 45, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 67, 111, 108, 111, 110, 63, 32, 86, 97, 108, 117, 101, 115, 67, 108, 97, 117, 115, 101, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 32, 83, 97, 109, 112, 108, 101, 67, 108, 97, 117, 115, 101, 63, 10, 80, 97, 114, 101, 110, 115, 84, 97, 98, 108, 101, 82, 101, 102, 32, 60, 45, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 67, 111, 108, 111, 110, 63, 32, 80, 97, 114, 101, 110, 115, 40, 84, 97, 98, 108, 101, 82, 101, 102, 41, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 32, 83, 97, 109, 112, 108, 101, 67, 108, 97, 117, 115, 101, 63, 10, 74, 111, 105, 110, 79, 114, 80, 105, 118, 111, 116, 32, 60, 45, 32, 74, 111, 105, 110, 67, 108, 97, 117, 115, 101, 32, 47, 32, 84, 97, 98, 108, 101, 80, 105, 118, 111, 116, 67, 108, 97, 117, 115, 101, 32, 47, 32, 84, 97, 98, 108, 101, 85, 110, 112, 105, 118, 111, 116, 67, 108, 97, 117, 115, 101, 32, 47, 32, 84, 97, 98, 108, 101, 77, 97, 116, 99, 104, 82, 101, 99, 111, 103, 110, 105, 122, 101, 67, 108, 97, 117, 115, 101, 10, 84, 97, 98, 108, 101, 80, 105, 118, 111, 116, 67, 108, 97, 117, 115, 101, 32, 60, 45, 32, 39, 80, 73, 86, 79, 84, 39, 32, 80, 97, 114, 101, 110, 115, 40, 84, 97, 98, 108, 101, 80, 105, 118, 111, 116, 67, 108, 97, 117, 115, 101, 66, 111, 100, 121, 41, 32, 84, 97, 98, 108, 101, 65, 108, 105, 97, 115, 63, 10, @@ -2105,7 +2109,7 @@ const unsigned char INLINED_PEG_GRAMMAR[] = { 81, 117, 97, 108, 105, 102, 105, 101, 100, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 32, 60, 45, 32, 67, 97, 116, 97, 108, 111, 103, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 63, 32, 83, 99, 104, 101, 109, 97, 81, 117, 97, 108, 105, 102, 105, 99, 97, 116, 105, 111, 110, 42, 32, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 78, 97, 109, 101, 10, 84, 97, 98, 108, 101, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 115, 32, 60, 45, 32, 80, 97, 114, 101, 110, 115, 40, 76, 105, 115, 116, 40, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 41, 63, 41, 10, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 32, 60, 45, 32, 78, 97, 109, 101, 100, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 32, 47, 32, 80, 111, 115, 105, 116, 105, 111, 110, 97, 108, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 10, - 78, 97, 109, 101, 100, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 32, 60, 45, 32, 78, 97, 109, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 10, + 78, 97, 109, 101, 100, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 32, 60, 45, 32, 84, 121, 112, 101, 70, 117, 110, 99, 78, 97, 109, 101, 32, 78, 97, 109, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 65, 115, 115, 105, 103, 110, 109, 101, 110, 116, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 80, 111, 115, 105, 116, 105, 111, 110, 97, 108, 70, 117, 110, 99, 116, 105, 111, 110, 65, 114, 103, 117, 109, 101, 110, 116, 32, 60, 45, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 78, 97, 109, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 60, 45, 32, 84, 121, 112, 101, 70, 117, 110, 99, 78, 97, 109, 101, 32, 84, 121, 112, 101, 63, 32, 78, 97, 109, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 65, 115, 115, 105, 103, 110, 109, 101, 110, 116, 32, 69, 120, 112, 114, 101, 115, 115, 105, 111, 110, 10, 78, 97, 109, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 65, 115, 115, 105, 103, 110, 109, 101, 110, 116, 32, 60, 45, 32, 39, 58, 61, 39, 32, 47, 32, 39, 61, 62, 39, 10, diff --git a/src/duckdb/src/include/duckdb/parser/peg/matcher/number_literal_matcher.hpp b/src/duckdb/src/include/duckdb/parser/peg/matcher/number_literal_matcher.hpp index b7358cb14..64936d1c2 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/matcher/number_literal_matcher.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/matcher/number_literal_matcher.hpp @@ -61,7 +61,15 @@ class NumberLiteralMatcher : public AtomicMatcher { return false; } bool scientific_notation = false; + bool has_dot = token_text[0] == '.'; for (idx_t i = 1; i < token_text.size(); i++) { + if (token_text[i] == '.') { + // a number has at most one dot, and none in its exponent + if (has_dot || scientific_notation) { + return false; + } + has_dot = true; + } if (Tokenizer::CharacterIsScientific(token_text[i])) { if (scientific_notation) { throw ParserException("Already found scientific notation"); diff --git a/src/duckdb/src/include/duckdb/parser/peg/transformer/peg_transformer.hpp b/src/duckdb/src/include/duckdb/parser/peg/transformer/peg_transformer.hpp index 6d4eb30c7..db0bc8f28 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/transformer/peg_transformer.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/transformer/peg_transformer.hpp @@ -2671,6 +2671,9 @@ class PEGTransformerFactory { GeneratedTransformProcess &process); static arena_ptr FinalizeIsOtherOperatorTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeIsCastTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeIsCastTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeIsTestTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static arena_ptr FinalizeIsTestTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); @@ -2716,6 +2719,13 @@ class PEGTransformerFactory { GeneratedTransformProcess &process); static arena_ptr FinalizeComparisonExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static void InitializeComparisonOperandTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); + static arena_ptr FinalizeComparisonOperandTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static void InitializeNotComparisonOperandTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); + static arena_ptr FinalizeNotComparisonOperandTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process); static void InitializeComparisonOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); static arena_ptr FinalizeComparisonOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process); @@ -5044,7 +5054,7 @@ class PEGTransformerFactory { static unique_ptr TransformDropTableFunction(PEGTransformer &transformer, const CatalogType &comment_macro_table, const optional &if_exists, - const vector &table_function_name); + const vector &table_function_name); static unique_ptr TransformDropFunction(PEGTransformer &transformer, const bool &function_type_macro, const optional &if_exists, const vector &function_identifier); @@ -5347,7 +5357,7 @@ class PEGTransformerFactory { TransformColDefAndExpressionTail(PEGTransformer &transformer, unique_ptr is_distinct_from_expression); static unique_ptr TransformLogicalNotExpression(PEGTransformer &transformer, - optional> not_expression, + const optional> ¬_expression, unique_ptr is_expression); static vector TransformNotExpression(PEGTransformer &transformer, const vector ¬_keyword); static bool TransformNotKeyword(PEGTransformer &transformer); @@ -5364,6 +5374,8 @@ class PEGTransformerFactory { ComparisonExpressionTail comparison_expression_tail); static IsExpressionTail TransformIsOtherOperatorTail(PEGTransformer &transformer, OtherOperatorTail other_operator_tail); + static IsExpressionTail TransformIsCastTail(PEGTransformer &transformer, + unique_ptr cast_operator); static unique_ptr TransformIsLiteral(PEGTransformer &transformer, const bool &has_result, const Value &is_literal_value); static Value TransformUnknownLiteral(PEGTransformer &transformer); @@ -5382,10 +5394,12 @@ class PEGTransformerFactory { static unique_ptr TransformComparisonExpression(PEGTransformer &transformer, unique_ptr between_in_like_expression, optional> comparison_expression_tail); - static ComparisonExpressionTail - TransformComparisonExpressionTail(PEGTransformer &transformer, const ExpressionType &comparison_operator, - optional> not_expression, - unique_ptr between_in_like_expression); + static ComparisonExpressionTail TransformComparisonExpressionTail(PEGTransformer &transformer, + const ExpressionType &comparison_operator, + unique_ptr comparison_operand); + static unique_ptr TransformNotComparisonOperand(PEGTransformer &transformer, + const vector ¬_expression, + unique_ptr is_expression); static ExpressionType TransformOperatorEqual(PEGTransformer &transformer); static ExpressionType TransformOperatorNotEqual(PEGTransformer &transformer); static ExpressionType TransformOperatorLessThan(PEGTransformer &transformer); @@ -5396,7 +5410,8 @@ class PEGTransformerFactory { PEGTransformer &transformer, unique_ptr other_operator_expression, optional> in_predicate, optional between_like_op); static BetweenInLikeOperator TransformInPredicate(PEGTransformer &transformer, const bool &has_result, - unique_ptr in_clause); + unique_ptr in_clause, + optional>> cast_operator); static BetweenInLikeOperator TransformBetweenLikeOp(PEGTransformer &transformer, const bool &has_result, unique_ptr between_like_op_expression); static unique_ptr TransformLikeClause(PEGTransformer &transformer, const string &like_variations, @@ -5918,7 +5933,8 @@ class PEGTransformerFactory { static unique_ptr TransformValuesRef(PEGTransformer &transformer, const optional &table_alias_colon, unique_ptr values_clause, - const optional &table_alias); + const optional &table_alias, + optional> sample_clause); static unique_ptr TransformParensTableRef(PEGTransformer &transformer, const optional &table_alias_colon, unique_ptr table_ref, @@ -5980,7 +5996,9 @@ class PEGTransformerFactory { const Identifier &table_function_name); static vector TransformTableFunctionArguments(PEGTransformer &transformer, optional> function_argument); - static FunctionArgument TransformNamedFunctionArgument(PEGTransformer &transformer, MacroParameter named_parameter); + static FunctionArgument TransformNamedFunctionArgument(PEGTransformer &transformer, + const Identifier &type_func_name, + unique_ptr expression); static FunctionArgument TransformPositionalFunctionArgument(PEGTransformer &transformer, unique_ptr expression); static MacroParameter TransformNamedParameter(PEGTransformer &transformer, const Identifier &type_func_name, diff --git a/src/duckdb/src/include/duckdb/storage/block_manager.hpp b/src/duckdb/src/include/duckdb/storage/block_manager.hpp index 14e17686a..8276e7302 100644 --- a/src/duckdb/src/include/duckdb/storage/block_manager.hpp +++ b/src/duckdb/src/include/duckdb/storage/block_manager.hpp @@ -9,7 +9,9 @@ #pragma once #include "duckdb/common/common.hpp" +#include "duckdb/common/enums/memory_tag.hpp" #include "duckdb/common/mutex.hpp" +#include "duckdb/common/optional.hpp" #include "duckdb/common/optional_idx.hpp" #include "duckdb/common/unordered_map.hpp" #include "duckdb/common/query_context.hpp" @@ -101,18 +103,21 @@ class BlockManager { //! Truncate the underlying database file after a checkpoint virtual void Truncate(); - //! Register a block with the given block id in the base file - shared_ptr RegisterBlock(block_id_t block_id); + //! Register a block with the given block id in the base file, an already registered block must have the same tag + shared_ptr RegisterBlock(block_id_t block_id, MemoryTag tag = MemoryTag::BASE_TABLE); //! Convert an existing in-memory buffer into a persistent disk-backed block //! If mode is set to destructive (default) - the old_block will be destroyed as part of this method //! This can only be safely used when there is no other (lingering) usage of old_block //! If there is concurrent usage of the block elsewhere - use the THREAD_SAFE mode which creates an extra copy + //! The new block keeps the memory tag of old_block, unless a tag is given shared_ptr ConvertToPersistent(QueryContext context, block_id_t block_id, shared_ptr old_block, BufferHandle old_handle, - ConvertToPersistentMode mode = ConvertToPersistentMode::DESTRUCTIVE); + ConvertToPersistentMode mode = ConvertToPersistentMode::DESTRUCTIVE, + optional tag = nullopt); shared_ptr ConvertToPersistent(QueryContext context, block_id_t block_id, shared_ptr old_block, - ConvertToPersistentMode mode = ConvertToPersistentMode::DESTRUCTIVE); + ConvertToPersistentMode mode = ConvertToPersistentMode::DESTRUCTIVE, + optional tag = nullopt); void UnregisterPersistentBlock(BlockHandle &block); //! UnregisterBlock, only accepts non-temporary block ids diff --git a/src/duckdb/src/main/client_context.cpp b/src/duckdb/src/main/client_context.cpp index 62ae13551..1631051fd 100644 --- a/src/duckdb/src/main/client_context.cpp +++ b/src/duckdb/src/main/client_context.cpp @@ -1478,6 +1478,7 @@ ParserOptions ClientContext::GetParserOptions() { options.extensions = DBConfig::GetConfig(*this).GetCallbackManager(); options.parser_override_setting = Settings::Get(*this); options.compiled_grammar = CompiledGrammar::Get(*this); + options.context = *this; return options; } diff --git a/src/duckdb/src/main/client_context_execution.cpp b/src/duckdb/src/main/client_context_execution.cpp index 2b204776e..3e7f74601 100644 --- a/src/duckdb/src/main/client_context_execution.cpp +++ b/src/duckdb/src/main/client_context_execution.cpp @@ -130,7 +130,14 @@ ErrorData ClientContext::EndQueryInternal(ClientContextLock &lock, bool success, error = ErrorData("Unhandled exception!"); } // LCOV_EXCL_STOP - client_data->profiler->EndQuery(); + try { + client_data->profiler->EndQuery(); + } catch (std::exception &ex) { + // e.g. the profiling output could not be written - report it as the error of the query + if (!error.HasError()) { + error = ErrorData(ex); + } + } // Refresh the logger logger->Flush(); diff --git a/src/duckdb/src/main/config.cpp b/src/duckdb/src/main/config.cpp index 9917653b6..a81d4bb46 100644 --- a/src/duckdb/src/main/config.cpp +++ b/src/duckdb/src/main/config.cpp @@ -994,6 +994,41 @@ void DBConfig::SetDisabledOptimizers(set disabled_optimizers) { options.disabled_optimizers = std::move(disabled_optimizers); } +void DBConfig::SetAllowedDirectories(const vector &paths) { + decltype(options.allowed_directories) new_directories; + for (auto &path : paths) { + auto allowed_directory = SanitizeAllowedPath(path); + if (allowed_directory.empty()) { + throw InvalidInputException("Cannot provide an empty string for allowed_directory"); + } + if (!StringUtil::EndsWith(allowed_directory, "/")) { + allowed_directory += "/"; + } + new_directories.insert(std::move(allowed_directory)); + } + lock_guard guard(allowed_paths_lock); + options.allowed_directories = std::move(new_directories); +} + +void DBConfig::SetAllowedPaths(const vector &paths) { + decltype(options.allowed_paths) new_paths; + for (auto &path : paths) { + new_paths.insert(SanitizeAllowedPath(path)); + } + lock_guard guard(allowed_paths_lock); + options.allowed_paths = std::move(new_paths); +} + +void DBConfig::ResetAllowedDirectories() { + lock_guard guard(allowed_paths_lock); + options.allowed_directories = DBConfigOptions().allowed_directories; +} + +void DBConfig::ResetAllowedPaths() { + lock_guard guard(allowed_paths_lock); + options.allowed_paths = DBConfigOptions().allowed_paths; +} + void DBConfig::AddAllowedPath(const string &path) { auto allowed_path = SanitizeAllowedPath(path); lock_guard guard(allowed_paths_lock); diff --git a/src/duckdb/src/main/query_profiler.cpp b/src/duckdb/src/main/query_profiler.cpp index 4036cad2a..2dcb722ac 100644 --- a/src/duckdb/src/main/query_profiler.cpp +++ b/src/duckdb/src/main/query_profiler.cpp @@ -1034,6 +1034,9 @@ void QueryProfiler::WriteToFile(const char *path, string &info) const { auto &fs = FileSystem::GetFileSystem(context); auto flags = FileOpenFlags::FILE_FLAGS_WRITE | FileOpenFlags::FILE_FLAGS_FILE_CREATE_NEW; auto file = fs.OpenFile(path, flags); + if (!file) { + throw IOException("Could not open the profiling output file \"%s\" for writing", path); + } file->Write((void *)info.c_str(), info.size()); file->Close(); } diff --git a/src/duckdb/src/main/secret/default_secrets.cpp b/src/duckdb/src/main/secret/default_secrets.cpp index f0dce5f5d..4fa756253 100644 --- a/src/duckdb/src/main/secret/default_secrets.cpp +++ b/src/duckdb/src/main/secret/default_secrets.cpp @@ -3,6 +3,10 @@ namespace duckdb { +identifier_set_t CreateHTTPSecretFunctions::GetRedactedKeys() { + return {"http_proxy_password", "bearer_token", "extra_http_headers"}; +} + vector CreateHTTPSecretFunctions::GetDefaultSecretTypes() { vector result; @@ -89,7 +93,7 @@ unique_ptr CreateHTTPSecretFunctions::CreateHTTPSecretFromEnv(Client secret->TrySetValue("bearer_token", input); //! Set redact keys - secret->redact_keys = {"http_proxy_password", "bearer_token"}; + secret->redact_keys = GetRedactedKeys(); return std::move(secret); } @@ -107,7 +111,7 @@ unique_ptr CreateHTTPSecretFunctions::CreateHTTPSecretFromConfig(Cli secret->TrySetValue("bearer_token", input); //! Set redact keys - secret->redact_keys = {"http_proxy_password", "bearer_token"}; + secret->redact_keys = GetRedactedKeys(); return std::move(secret); } diff --git a/src/duckdb/src/main/secret/secret.cpp b/src/duckdb/src/main/secret/secret.cpp index cf6bf6538..32e5cc921 100644 --- a/src/duckdb/src/main/secret/secret.cpp +++ b/src/duckdb/src/main/secret/secret.cpp @@ -101,6 +101,41 @@ void KeyValueSecret::Serialize(Serializer &serializer) const { serializer.WriteProperty(202, "redact_keys", list); } +void KeyValueSecret::ReadSecretMap(Deserializer &deserializer, identifier_tree_t &result) { + deserializer.ReadObject(201, "secret_map", [&](Deserializer &map) { + auto stored_type = map.ReadProperty(100, "type"); + // Before DuckDB 1.1.3, secrets declared VARCHAR values even when storing other types. + if (stored_type.id() != LogicalTypeId::MAP || !stored_type.HasParameters() || + MapType::KeyType(stored_type) != LogicalType::VARCHAR) { + throw SerializationException("Secret map must be a MAP with VARCHAR keys"); + } + if (map.ReadProperty(101, "is_null")) { + throw SerializationException("Secret map cannot be NULL"); + } + + map.Set(ListType::GetChildType(stored_type)); + map.ReadObject(102, "value", [&](Deserializer &obj) { + obj.ReadList(100, "children", [&](Deserializer::List &list, idx_t i) { + // Before DuckDB 1.2.0, entries carried their actual value types, even when the map declared VARCHAR. + // Deserialize them separately so their payloads still undergo normal Value validation. + auto entry = list.ReadElement(); + if (entry.IsNull() || entry.type().id() != LogicalTypeId::STRUCT) { + throw SerializationException("Secret map entry must be a non-NULL struct"); + } + const auto &children = StructValue::GetChildren(entry); + if (children.size() != 2) { + throw SerializationException("Secret map entry must have two fields"); + } + if (children[0].IsNull() || children[0].type() != LogicalType::VARCHAR) { + throw SerializationException("Secret map key must be a non-NULL VARCHAR"); + } + result[Identifier(StringValue::Get(children[0]))] = children[1]; + }); + }); + map.Unset(); + }); +} + Value KeyValueSecret::TryGetValue(const Identifier &key, bool error_on_missing) const { auto lookup = secret_map.find(key); if (lookup == secret_map.end()) { diff --git a/src/duckdb/src/main/secret/secret_manager.cpp b/src/duckdb/src/main/secret/secret_manager.cpp index 39ff78bd9..16ca921b5 100644 --- a/src/duckdb/src/main/secret/secret_manager.cpp +++ b/src/duckdb/src/main/secret/secret_manager.cpp @@ -113,8 +113,17 @@ unique_ptr SecretManager::DeserializeSecret(Deserializer &deserializ switch (serialization_type) { // This allows us to skip looking up the secret type for deserialization altogether - case SecretSerializationType::KEY_VALUE_SECRET: - return KeyValueSecret::Deserialize(deserializer, BaseSecret(scope, type, provider, name)); + case SecretSerializationType::KEY_VALUE_SECRET: { + auto result = + KeyValueSecret::Deserialize(deserializer, BaseSecret(scope, type, provider, name)); + if (type == "http") { + // Preserve stored keys and apply current HTTP redaction rules to older secrets. + const auto keys = CreateHTTPSecretFunctions::GetRedactedKeys(); + auto &secret = result->Cast(); + secret.redact_keys.insert(keys.begin(), keys.end()); + } + return result; + } // Continues below: we need to do a type lookup to find the secret deserialize method case SecretSerializationType::CUSTOM: break; diff --git a/src/duckdb/src/main/settings/custom_settings.cpp b/src/duckdb/src/main/settings/custom_settings.cpp index 3b63cefb3..0a3fca8ab 100644 --- a/src/duckdb/src/main/settings/custom_settings.cpp +++ b/src/duckdb/src/main/settings/custom_settings.cpp @@ -331,18 +331,18 @@ void AllowedDirectoriesSetting::SetGlobal(DatabaseInstance *db, DBConfig &config if (!config.file_system) { throw InvalidInputException("Cannot change/set allowed_directories before the database is started"); } - config.options.allowed_directories.clear(); - auto &list = ListValue::GetChildren(input); - for (auto &val : list) { - config.AddAllowedDirectory(val.GetValue()); + vector directories; + for (auto &val : ListValue::GetChildren(input)) { + directories.push_back(val.GetValue()); } + config.SetAllowedDirectories(directories); } void AllowedDirectoriesSetting::ResetGlobal(DatabaseInstance *db, DBConfig &config) { if (!Settings::Get(config)) { throw InvalidInputException("Cannot change allowed_directories when enable_external_access is disabled"); } - config.options.allowed_directories = DBConfigOptions().allowed_directories; + config.ResetAllowedDirectories(); } Value AllowedDirectoriesSetting::GetSetting(const ClientContext &context) { @@ -368,18 +368,18 @@ void AllowedPathsSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, cons throw InvalidInputException("Cannot change/set allowed_paths before the database is started"); } - config.options.allowed_paths.clear(); - auto &list = ListValue::GetChildren(input); - for (auto &val : list) { - config.AddAllowedPath(val.GetValue()); + vector paths; + for (auto &val : ListValue::GetChildren(input)) { + paths.push_back(val.GetValue()); } + config.SetAllowedPaths(paths); } void AllowedPathsSetting::ResetGlobal(DatabaseInstance *db, DBConfig &config) { if (!Settings::Get(config)) { throw InvalidInputException("Cannot change allowed_paths when enable_external_access is disabled"); } - config.options.allowed_paths = DBConfigOptions().allowed_paths; + config.ResetAllowedPaths(); } Value AllowedPathsSetting::GetSetting(const ClientContext &context) { diff --git a/src/duckdb/src/optimizer/disjunctive_join_rewriter.cpp b/src/duckdb/src/optimizer/disjunctive_join_rewriter.cpp new file mode 100644 index 000000000..ba66cb0cd --- /dev/null +++ b/src/duckdb/src/optimizer/disjunctive_join_rewriter.cpp @@ -0,0 +1,476 @@ +#include "duckdb/optimizer/disjunctive_join_rewriter.hpp" + +#include "duckdb/planner/column_binding_map.hpp" +#include "duckdb/planner/logical_operator_repeatability.hpp" +#include "duckdb/planner/operator/logical_comparison_join.hpp" +#include "duckdb/planner/operator/logical_any_join.hpp" +#include "duckdb/planner/operator/logical_projection.hpp" +#include "duckdb/planner/operator/logical_filter.hpp" +#include "duckdb/planner/operator/logical_cteref.hpp" +#include "duckdb/planner/operator/logical_materialized_cte.hpp" +#include "duckdb/planner/operator/logical_set_operation.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/expression/bound_comparison_expression.hpp" +#include "duckdb/planner/expression/bound_conjunction_expression.hpp" +#include "duckdb/planner/expression/bound_constant_expression.hpp" +#include "duckdb/planner/expression/bound_operator_expression.hpp" + +namespace duckdb { + +DisjunctiveJoinRewriter::DisjunctiveJoinRewriter(Binder &binder) : binder(binder) { +} + +TableIndex DisjunctiveJoinRewriter::NewTableIndex() { + return binder.GenerateTableIndex(); +} + +unique_ptr DisjunctiveJoinRewriter::Optimize(unique_ptr op) { + op->ResolveOperatorTypes(); + BindingReplacementGraph replacements; + return OptimizeInternal(std::move(op), replacements); +} + +unique_ptr DisjunctiveJoinRewriter::OptimizeInternal(unique_ptr op, + BindingReplacementGraph &replacements) { + for (idx_t child_idx = 0; child_idx < op->children.size(); child_idx++) { + ColumnBindingRewrite::RewriteChild( + op, child_idx, [&](unique_ptr &child, BindingReplacementGraph &child_replacements) { + child = OptimizeInternal(std::move(child), child_replacements); + replacements.Merge(child_replacements); + }); + } + + if (op->type != LogicalOperatorType::LOGICAL_ANY_JOIN) { + return op; + } + auto &join = op->Cast(); + + unordered_set left_tables, right_tables; + for (auto &b : join.children[0]->GetColumnBindings()) { + left_tables.insert(b.table_index); + } + for (auto &b : join.children[1]->GetColumnBindings()) { + right_tables.insert(b.table_index); + } + + vector branches; + if (!ShouldRewrite(join, left_tables, right_tables, branches)) { + return op; + } + + auto orig_bindings = join.GetColumnBindings(); + auto orig_types = join.types; + auto left_child = std::move(op->children[0]); + auto right_child = std::move(op->children[1]); + + TableIndex left_cte_idx = NewTableIndex(); + TableIndex right_cte_idx = NewTableIndex(); + + CTEInfo left_cte {left_cte_idx, left_child->types, left_child->GetColumnBindings()}; + CTEInfo right_cte {right_cte_idx, right_child->types, right_child->GetColumnBindings()}; + + unique_ptr epilogue; + switch (join.join_type) { + case JoinType::INNER: + epilogue = BuildInnerJoin(left_cte, right_cte, branches); + break; + case JoinType::LEFT: + epilogue = BuildLeftJoin(left_cte, right_cte, branches); + break; + case JoinType::RIGHT: + epilogue = BuildRightJoin(left_cte, right_cte, branches); + break; + case JoinType::OUTER: + epilogue = BuildFullJoin(left_cte, right_cte, branches); + break; + case JoinType::SEMI: + epilogue = BuildSemiJoin(left_cte, right_cte, branches); + break; + case JoinType::ANTI: + epilogue = BuildAntiJoin(left_cte, right_cte, branches); + break; + default: + return op; + } + + epilogue = NormalizeOutput(std::move(epilogue), orig_bindings, orig_types, left_cte, right_cte, join.join_type, + replacements); + + // Every branch must consume the same input occurrences unless copying is proven safe. + auto right_materialize = LogicalSubtreeIsRepeatable(*right_child) ? CTEMaterialize::CTE_MATERIALIZE_DEFAULT + : CTEMaterialize::CTE_MATERIALIZE_ALWAYS; + auto left_materialize = LogicalSubtreeIsRepeatable(*left_child) ? CTEMaterialize::CTE_MATERIALIZE_DEFAULT + : CTEMaterialize::CTE_MATERIALIZE_ALWAYS; + epilogue = make_uniq("rhs_cte", right_cte.table_index, right_cte.output_types.size(), + std::move(right_child), std::move(epilogue), right_materialize); + + epilogue = make_uniq("lhs_cte", left_cte.table_index, left_cte.output_types.size(), + std::move(left_child), std::move(epilogue), left_materialize); + + epilogue->ResolveOperatorTypes(); + ColumnBindingRewrite::ValidateOutput(orig_bindings, epilogue->GetColumnBindings(), replacements); + D_ASSERT(epilogue->types == orig_types); + return epilogue; +} + +bool DisjunctiveJoinRewriter::ShouldRewrite(const LogicalOperator &join, const unordered_set &left_tables, + const unordered_set &right_tables, + vector &out_branches) const { + auto &any_join = join.Cast(); + switch (any_join.join_type) { + case JoinType::INNER: + case JoinType::LEFT: + case JoinType::RIGHT: + case JoinType::OUTER: + case JoinType::SEMI: + case JoinType::ANTI: + break; + default: + return false; + } + + if (!any_join.condition) { + return false; + } + + const Expression &expr = *any_join.condition; + if (expr.GetExpressionType() != ExpressionType::CONJUNCTION_OR) { + return false; + } + + if (expr.IsVolatile() || expr.CanThrow()) { + return false; + } + + if (!FlattenOR(expr, left_tables, right_tables, out_branches)) { + return false; + } + + return out_branches.size() >= 2; +} + +bool DisjunctiveJoinRewriter::FlattenOR(const Expression &expr, const unordered_set &left_tables, + const unordered_set &right_tables, vector &out) const { + // Bound the quadratic exclusion expansion and the cost of optimizing the generated plan. + static constexpr idx_t MAX_BRANCH_COUNT = 16; + if (out.size() >= MAX_BRANCH_COUNT) { + return false; + } + + if (expr.GetExpressionType() == ExpressionType::CONJUNCTION_OR) { + auto &conj = expr.Cast(); + for (const auto &child : conj.GetChildren()) { + if (!FlattenOR(*child, left_tables, right_tables, out)) { + return false; + } + } + return true; + } + + if (expr.GetExpressionType() != ExpressionType::COMPARE_EQUAL && + expr.GetExpressionType() != ExpressionType::COMPARE_NOT_DISTINCT_FROM) { + return false; + } + + const auto &comp = expr.Cast(); + auto &left = BoundComparisonExpression::Left(comp); + auto &right = BoundComparisonExpression::Right(comp); + + auto l_side = JoinSide::GetJoinSide(left, left_tables, right_tables); + auto r_side = JoinSide::GetJoinSide(right, left_tables, right_tables); + + if (l_side == JoinSide::BOTH || l_side == JoinSide::NONE || r_side == JoinSide::BOTH || r_side == JoinSide::NONE || + l_side == r_side) { + return false; + } + + Branch b; + if (l_side == JoinSide::LEFT) { + b.left_expr = left.Copy(); + b.right_expr = right.Copy(); + } else { + b.left_expr = right.Copy(); + b.right_expr = left.Copy(); + } + b.comparison_type = expr.GetExpressionType(); + out.push_back(std::move(b)); + return true; +} + +unique_ptr DisjunctiveJoinRewriter::BuildInnerJoin(const CTEInfo &left_cte, const CTEInfo &right_cte, + const vector &branches) { + auto children = CreateMatchedBranches(left_cte, right_cte, branches); + return CreateUnionAll(std::move(children), left_cte.output_types.size() + right_cte.output_types.size()); +} + +unique_ptr DisjunctiveJoinRewriter::BuildLeftJoin(const CTEInfo &left_cte, const CTEInfo &right_cte, + const vector &branches) { + auto children = CreateMatchedBranches(left_cte, right_cte, branches); + children.push_back(CreateUnmatchedProbeSide(left_cte, right_cte, branches)); + return CreateUnionAll(std::move(children), left_cte.output_types.size() + right_cte.output_types.size()); +} + +unique_ptr DisjunctiveJoinRewriter::BuildRightJoin(const CTEInfo &left_cte, const CTEInfo &right_cte, + const vector &branches) { + return BuildLeftJoin(right_cte, left_cte, SwapBranches(branches)); +} + +unique_ptr DisjunctiveJoinRewriter::BuildFullJoin(const CTEInfo &left_cte, const CTEInfo &right_cte, + const vector &branches) { + auto children = CreateMatchedBranches(left_cte, right_cte, branches); + children.push_back(CreateUnmatchedProbeSide(left_cte, right_cte, branches)); + children.push_back(CreateUnmatchedBuildSide(left_cte, right_cte, branches)); + return CreateUnionAll(std::move(children), left_cte.output_types.size() + right_cte.output_types.size()); +} + +unique_ptr DisjunctiveJoinRewriter::BuildSemiJoin(const CTEInfo &left_cte, const CTEInfo &right_cte, + const vector &branches) { + TableIndex probe_ref = NewTableIndex(); + auto current = CreateCTERef(left_cte, probe_ref); + ColumnBindingReplacer probe_replacer; + probe_replacer.AddReplacements(left_cte.original_bindings, current->GetColumnBindings()); + vector> marker_refs; + + for (idx_t i = 0; i < branches.size(); i++) { + TableIndex build_ref = NewTableIndex(); + TableIndex mark_tbl = NewTableIndex(); + + auto build_scan = CreateCTERef(right_cte, build_ref); + ColumnBindingReplacer build_replacer; + build_replacer.AddReplacements(right_cte.original_bindings, build_scan->GetColumnBindings()); + + auto mark_join = make_uniq(JoinType::MARK); + mark_join->conditions.push_back(CreateJoinCondition(branches[i], probe_replacer, build_replacer)); + mark_join->mark_index = mark_tbl; + mark_join->AddChild(std::move(current)); + mark_join->AddChild(std::move(build_scan)); + + current = std::move(mark_join); + marker_refs.push_back( + CreateColRef(ColumnBinding(mark_tbl, ProjectionIndex(0)), LogicalType::BOOLEAN, "mark_" + to_string(i))); + } + + auto filter = make_uniq(); + filter->expressions.push_back( + BoundConjunctionExpression::Create(ExpressionType::CONJUNCTION_OR, std::move(marker_refs))); + filter->AddChild(std::move(current)); + + return std::move(filter); +} + +unique_ptr DisjunctiveJoinRewriter::BuildAntiJoin(const CTEInfo &left_cte, const CTEInfo &right_cte, + const vector &branches) { + TableIndex probe_ref = NewTableIndex(); + return CreateAntiJoinChain(left_cte, right_cte, branches, probe_ref); +} + +unique_ptr DisjunctiveJoinRewriter::CreateBranchJoin(const CTEInfo &left_cte, const CTEInfo &right_cte, + const vector &branches, + idx_t branch_idx) { + auto left_scan = CreateCTERef(left_cte, NewTableIndex()); + auto right_scan = CreateCTERef(right_cte, NewTableIndex()); + ColumnBindingReplacer left_replacer, right_replacer; + left_replacer.AddReplacements(left_cte.original_bindings, left_scan->GetColumnBindings()); + right_replacer.AddReplacements(right_cte.original_bindings, right_scan->GetColumnBindings()); + + auto join = make_uniq(JoinType::INNER); + join->conditions.push_back(CreateJoinCondition(branches[branch_idx], left_replacer, right_replacer)); + for (idx_t i = 0; i < branch_idx; i++) { + auto previous = CreateJoinCondition(branches[i], left_replacer, right_replacer); + vector> exclusions; + if (previous.GetComparisonType() == ExpressionType::COMPARE_EQUAL) { + // Preserve UNKNOWN, with separate NULL checks that statistics can simplify. + auto left_null = make_uniq(ExpressionType::OPERATOR_IS_NULL, LogicalType::BOOLEAN); + left_null->GetChildrenMutable().push_back(previous.GetLHS().Copy()); + exclusions.push_back(std::move(left_null)); + auto right_null = + make_uniq(ExpressionType::OPERATOR_IS_NULL, LogicalType::BOOLEAN); + right_null->GetChildrenMutable().push_back(previous.GetRHS().Copy()); + exclusions.push_back(std::move(right_null)); + } + exclusions.push_back(BoundComparisonExpression::Create(NegateComparisonExpression(previous.GetComparisonType()), + std::move(previous.LeftReference()), + std::move(previous.RightReference()))); + join->conditions.emplace_back( + BoundConjunctionExpression::Create(ExpressionType::CONJUNCTION_OR, std::move(exclusions))); + } + + join->AddChild(std::move(left_scan)); + join->AddChild(std::move(right_scan)); + + return std::move(join); +} + +vector> DisjunctiveJoinRewriter::CreateMatchedBranches(const CTEInfo &left_cte, + const CTEInfo &right_cte, + const vector &branches) { + vector> union_children; + + for (idx_t i = 0; i < branches.size(); i++) { + union_children.push_back(CreateBranchJoin(left_cte, right_cte, branches, i)); + } + return union_children; +} + +unique_ptr DisjunctiveJoinRewriter::CreateUnmatchedProbeSide(const CTEInfo &probe_cte, + const CTEInfo &build_cte, + const vector &branches) { + TableIndex probe_ref = NewTableIndex(); + auto anti_chain = CreateAntiJoinChain(probe_cte, build_cte, branches, probe_ref); + + vector> proj_exprs; + for (idx_t col = 0; col < probe_cte.output_types.size(); col++) { + proj_exprs.push_back(CreateColRef(ColumnBinding(probe_ref, ProjectionIndex(col)), probe_cte.output_types[col])); + } + for (idx_t col = 0; col < build_cte.output_types.size(); col++) { + proj_exprs.push_back(CreateNullExpr(build_cte.output_types[col])); + } + return CreateProjection(std::move(anti_chain), std::move(proj_exprs)); +} + +unique_ptr DisjunctiveJoinRewriter::CreateUnmatchedBuildSide(const CTEInfo &left_cte, + const CTEInfo &right_cte, + const vector &branches) { + TableIndex rhs_ref = NewTableIndex(); + auto anti_chain = CreateAntiJoinChain(right_cte, left_cte, SwapBranches(branches), rhs_ref); + auto null_exprs = CreateNullExprs(left_cte.output_types); + auto right_exprs = CreateColRefs(rhs_ref, right_cte.output_types); + + null_exprs.insert(null_exprs.end(), std::make_move_iterator(right_exprs.begin()), + std::make_move_iterator(right_exprs.end())); + + return CreateProjection(std::move(anti_chain), std::move(null_exprs)); +} + +unique_ptr DisjunctiveJoinRewriter::CreateAntiJoinChain(const CTEInfo &probe_cte, + const CTEInfo &build_cte, + const vector &branches, + TableIndex probe_ref_idx) { + auto current = CreateCTERef(probe_cte, probe_ref_idx); + ColumnBindingReplacer probe_replacer; + probe_replacer.AddReplacements(probe_cte.original_bindings, current->GetColumnBindings()); + + for (idx_t i = 0; i < branches.size(); i++) { + TableIndex build_ref = NewTableIndex(); + auto build_scan = CreateCTERef(build_cte, build_ref); + ColumnBindingReplacer build_replacer; + build_replacer.AddReplacements(build_cte.original_bindings, build_scan->GetColumnBindings()); + + auto anti_join = make_uniq(JoinType::ANTI); + anti_join->conditions.push_back(CreateJoinCondition(branches[i], probe_replacer, build_replacer)); + anti_join->AddChild(std::move(current)); + anti_join->AddChild(std::move(build_scan)); + + current = std::move(anti_join); + } + return current; +} + +unique_ptr DisjunctiveJoinRewriter::CreateUnionAll(vector> children, + idx_t total_columns) { + TableIndex union_tbl = NewTableIndex(); + return make_uniq(union_tbl, total_columns, std::move(children), + LogicalOperatorType::LOGICAL_UNION, true, true); +} + +unique_ptr DisjunctiveJoinRewriter::NormalizeOutput( + unique_ptr plan, const vector &orig_bindings, const vector &orig_types, + const CTEInfo &left_cte, const CTEInfo &right_cte, JoinType join_type, BindingReplacementGraph &replacements) { + auto current_bindings = plan->GetColumnBindings(); + auto &first_cte = join_type == JoinType::RIGHT ? right_cte : left_cte; + auto &second_cte = join_type == JoinType::RIGHT ? left_cte : right_cte; + column_binding_map_t output_positions; + for (idx_t i = 0; i < first_cte.original_bindings.size(); i++) { + output_positions.emplace(first_cte.original_bindings[i], i); + } + if (join_type != JoinType::SEMI && join_type != JoinType::ANTI) { + for (idx_t i = 0; i < second_cte.original_bindings.size(); i++) { + output_positions.emplace(second_cte.original_bindings[i], first_cte.original_bindings.size() + i); + } + } + + vector> proj_exprs; + proj_exprs.reserve(orig_bindings.size()); + for (idx_t i = 0; i < orig_bindings.size(); i++) { + proj_exprs.push_back(CreateColRef(current_bindings[output_positions.at(orig_bindings[i])], orig_types[i])); + } + + TableIndex norm_tbl = NewTableIndex(); + auto proj = make_uniq(norm_tbl, std::move(proj_exprs)); + proj->AddChild(std::move(plan)); + + for (idx_t i = 0; i < orig_bindings.size(); i++) { + replacements.Add( + ReplacementBinding(orig_bindings[i], ColumnBinding(norm_tbl, ProjectionIndex(i)), orig_types[i])); + } + return std::move(proj); +} + +JoinCondition DisjunctiveJoinRewriter::CreateJoinCondition(const Branch &branch, ColumnBindingReplacer &left_replacer, + ColumnBindingReplacer &right_replacer) { + auto left_expr = branch.left_expr->Copy(); + auto right_expr = branch.right_expr->Copy(); + left_replacer.VisitExpression(&left_expr); + right_replacer.VisitExpression(&right_expr); + return JoinCondition(std::move(left_expr), std::move(right_expr), branch.comparison_type); +} + +unique_ptr DisjunctiveJoinRewriter::CreateCTERef(const CTEInfo &cte, TableIndex ref_idx) { + vector bound_columns; + bound_columns.reserve(cte.output_types.size()); + for (idx_t i = 0; i < cte.output_types.size(); ++i) { + bound_columns.emplace_back("col_" + to_string(i)); + } + return make_uniq(TableIndex(ref_idx), TableIndex(cte.table_index), cte.output_types, + std::move(bound_columns)); +} + +unique_ptr DisjunctiveJoinRewriter::CreateProjection(unique_ptr child, + vector> expressions) { + TableIndex proj_tbl = NewTableIndex(); + auto proj = make_uniq(proj_tbl, std::move(expressions)); + proj->AddChild(std::move(child)); + return std::move(proj); +} + +unique_ptr DisjunctiveJoinRewriter::CreateColRef(ColumnBinding binding, const LogicalType &type, + const string &alias) { + return make_uniq(Identifier(alias), type, binding); +} + +unique_ptr DisjunctiveJoinRewriter::CreateNullExpr(const LogicalType &type) { + return make_uniq(Value(type)); +} + +vector> DisjunctiveJoinRewriter::CreateColRefs(TableIndex table_index, + const vector &types) { + vector> exprs; + exprs.reserve(types.size()); + for (idx_t i = 0; i < types.size(); i++) { + exprs.push_back(CreateColRef(ColumnBinding(table_index, ProjectionIndex(i)), types[i])); + } + return exprs; +} + +vector> DisjunctiveJoinRewriter::CreateNullExprs(const vector &types) { + vector> exprs; + exprs.reserve(types.size()); + for (idx_t i = 0; i < types.size(); i++) { + exprs.push_back(make_uniq(Value(types[i]))); + } + return exprs; +} + +vector DisjunctiveJoinRewriter::SwapBranches(const vector &branches) { + vector swapped; + for (auto &branch : branches) { + Branch b; + b.left_expr = branch.right_expr->Copy(); + b.right_expr = branch.left_expr->Copy(); + b.comparison_type = branch.comparison_type; + swapped.push_back(std::move(b)); + } + return swapped; +} + +} // namespace duckdb diff --git a/src/duckdb/src/optimizer/join_filter_pushdown_optimizer.cpp b/src/duckdb/src/optimizer/join_filter_pushdown_optimizer.cpp index b9c150533..fb10f5873 100644 --- a/src/duckdb/src/optimizer/join_filter_pushdown_optimizer.cpp +++ b/src/duckdb/src/optimizer/join_filter_pushdown_optimizer.cpp @@ -233,11 +233,18 @@ void JoinFilterPushdownOptimizer::GetPushdownFilterTargets(LogicalOperator &op, } get.ResolveOperatorTypes(); const auto bindings = get.GetColumnBindings(); + vector accepted_columns; for (auto &filter : columns) { if (filter.probe_column_index.table_index != get.table_index) { // the filter does not apply to the probe side here - bail-out return; } + if (get.function.supports_pushdown_type) { + auto column_id = get.GetColumnIndex(filter.probe_column_index).GetPrimaryIndex(); + if (!get.function.supports_pushdown_type(*get.bind_data, column_id)) { + continue; + } + } // add storage type to columns for (idx_t i = 0; i < bindings.size(); i++) { if (filter.probe_column_index.column_index == bindings[i].column_index) { @@ -254,8 +261,12 @@ void JoinFilterPushdownOptimizer::GetPushdownFilterTargets(LogicalOperator &op, reconstructed_type.id() == LogicalTypeId::VARIANT) { return; } + accepted_columns.push_back(std::move(filter)); + } + if (accepted_columns.empty()) { + return; } - targets.emplace_back(get, std::move(columns)); + targets.emplace_back(get, std::move(accepted_columns)); break; } case LogicalOperatorType::LOGICAL_PROJECTION: { diff --git a/src/duckdb/src/optimizer/optimizer.cpp b/src/duckdb/src/optimizer/optimizer.cpp index e9ef3cb82..72ee19d1e 100644 --- a/src/duckdb/src/optimizer/optimizer.cpp +++ b/src/duckdb/src/optimizer/optimizer.cpp @@ -66,6 +66,7 @@ #include "duckdb/optimizer/remote_pushdown_optimizer.hpp" #include "duckdb/main/database_manager.hpp" #include "duckdb/main/settings.hpp" +#include "duckdb/optimizer/disjunctive_join_rewriter.hpp" namespace duckdb { @@ -320,6 +321,11 @@ void Optimizer::RunBuiltInOptimizers() { !OptimizerDisabled(OptimizerType::DISTINCT_AGGREGATE_REWRITE)); aggregate_rewriter.VisitOperator(plan); + RunOptimizer(OptimizerType::DISJUNCTIVE_JOIN_REWRITER, [&]() { + DisjunctiveJoinRewriter disjunctive_join_rewriter(binder); + plan = disjunctive_join_rewriter.Optimize(std::move(plan)); + }); + // try to inline CTEs instead of materialization RunOptimizer(OptimizerType::CTE_INLINING, [&]() { CTEInlining cte_inlining(*this); diff --git a/src/duckdb/src/optimizer/pushdown/pushdown_projection.cpp b/src/duckdb/src/optimizer/pushdown/pushdown_projection.cpp index bbe7685d2..4c375253f 100644 --- a/src/duckdb/src/optimizer/pushdown/pushdown_projection.cpp +++ b/src/duckdb/src/optimizer/pushdown/pushdown_projection.cpp @@ -157,18 +157,30 @@ unique_ptr FilterPushdown::PushdownProjection(unique_ptr> remain_expressions; vector> split_expressions; + auto stays_on_top = [&](const Filter &f) { + return IsVolatile(proj, *f.filter) || (f.filter->CanThrow() && !f.has_barrier); + }; + auto splits_off = [&](const Filter &f) { + return projection_mode == ProjectionMode::PRESERVE_COMPUTED_EXPRESSIONS && + ReferencesComputedExpression(proj, *f.filter); + }; + // barred expressions may only cross the projection if every other filter does: a filter kept on top of the + // projection, or split off below it, would otherwise be evaluated after them + bool hold_back_barriers = false; + for (auto &filter : filters) { + if (!filter->has_barrier && (stays_on_top(*filter) || splits_off(*filter))) { + hold_back_barriers = true; + } + } for (auto &filter : filters) { auto &f = *filter; D_ASSERT(f.bindings.size() <= 1); - bool is_volatile = IsVolatile(proj, *f.filter); - bool preserve_computed_expression = projection_mode == ProjectionMode::PRESERVE_COMPUTED_EXPRESSIONS && - ReferencesComputedExpression(proj, *f.filter); - if (is_volatile || (f.filter->CanThrow() && !f.has_barrier)) { + if (stays_on_top(f) || (f.has_barrier && hold_back_barriers)) { // Volatile and throwing expressions cannot move across the projection. Expressions carrying a barrier // are the exception: a projection does not remove any rows, so pushing them through it does not - // change which rows they are evaluated on. + // change which rows they are evaluated on - unless another filter stays behind, see above. remain_expressions.push_back(std::move(f.filter)); - } else if (preserve_computed_expression) { + } else if (splits_off(f)) { // Compute filter-dependent projection expressions once, below the filter. split_expressions.push_back(std::move(f.filter)); } else { diff --git a/src/duckdb/src/optimizer/remove_unused_columns.cpp b/src/duckdb/src/optimizer/remove_unused_columns.cpp index 5e1c46045..5e2bb6dd4 100644 --- a/src/duckdb/src/optimizer/remove_unused_columns.cpp +++ b/src/duckdb/src/optimizer/remove_unused_columns.cpp @@ -5,6 +5,7 @@ #include "duckdb/function/aggregate/distributive_functions.hpp" #include "duckdb/function/function_binder.hpp" #include "duckdb/function/scalar/struct_functions.hpp" +#include "duckdb/function/scalar/variant_functions.hpp" #include "duckdb/function/builtin_function_lookup.hpp" #include "duckdb/parser/parsed_data/vacuum_info.hpp" #include "duckdb/planner/binder.hpp" @@ -1118,22 +1119,28 @@ static unique_ptr ConstructStructExtractFromPath(ClientContext &cont reference type_iter(struct_type); reference path_iter(path); while (true) { - auto child_index = path_iter.get().GetPrimaryIndex(); - auto &child_types = StructType::GetChildTypes(type_iter.get()); - D_ASSERT(child_index < child_types.size()); - auto is_unnamed = StructType::IsUnnamed(type_iter.get()); - - type_iter = child_types[child_index].second; - vector> arguments(2); arguments[0] = (std::move(target)); - if (is_unnamed) { - arguments[1] = make_uniq(Value::BIGINT(NumericCast(child_index + 1))); + if (type_iter.get().id() == LogicalTypeId::VARIANT) { + // a VARIANT field is addressed by key, and is itself a VARIANT + arguments[1] = make_uniq(Value(path_iter.get().GetFieldName())); + target = BindBuiltinScalarFunction(context, VariantExtractFun::Name, std::move(arguments)); } else { - arguments[1] = make_uniq(Value(child_types[child_index].first)); + auto child_index = path_iter.get().GetPrimaryIndex(); + auto &child_types = StructType::GetChildTypes(type_iter.get()); + D_ASSERT(child_index < child_types.size()); + auto is_unnamed = StructType::IsUnnamed(type_iter.get()); + + type_iter = child_types[child_index].second; + + if (is_unnamed) { + arguments[1] = make_uniq(Value::BIGINT(NumericCast(child_index + 1))); + } else { + arguments[1] = make_uniq(Value(child_types[child_index].first)); + } + // the constant argument selects the key or the index overload of struct_extract + target = BindBuiltinScalarFunction(context, StructExtractFun::Name, std::move(arguments)); } - // the struct_extract set holds both the key and the index overload, the constant argument selects between them - target = BindBuiltinScalarFunction(context, StructExtractFun::Name, std::move(arguments)); if (!path_iter.get().HasChildren()) { break; } diff --git a/src/duckdb/src/parser/peg/tokenizer/base_tokenizer.cpp b/src/duckdb/src/parser/peg/tokenizer/base_tokenizer.cpp index c02196b46..9f8df5442 100644 --- a/src/duckdb/src/parser/peg/tokenizer/base_tokenizer.cpp +++ b/src/duckdb/src/parser/peg/tokenizer/base_tokenizer.cpp @@ -85,15 +85,6 @@ bool Tokenizer::CharacterIsInitialNumber(char c) { return c == '.'; } -static bool NumberHasDotOrExponent(const string &sql, idx_t start, idx_t end) { - for (idx_t i = start; i < end; i++) { - if (sql[i] == '.' || sql[i] == 'e' || sql[i] == 'E') { - return true; - } - } - return false; -} - bool Tokenizer::CharacterIsSpecialStringCharacter(char c) { if (c == 'N' || c == 'n') { return true; @@ -447,12 +438,13 @@ bool Tokenizer::TokenizeInputInternal(TokenizerBehavior &behavior) const { last_pos = i; break; case TokenizeState::NUMERIC: - // Check for "always allowed" numeric characters - a number has at most one dot, and none in its exponent - if (CharacterIsInitialNumber(c) && (c != '.' || !NumberHasDotOrExponent(sql, last_pos, i))) { + // Check for "always allowed" numeric characters + if (CharacterIsInitialNumber(c)) { break; // Continue tokenizing } - // Allow underscore only when immediately followed by a digit (no consecutive underscores) - if (c == '_' && i + 1 < sql.size() && CharacterIsInitialNumber(sql[i + 1])) { + // A digit separator must sit between two digits + if (c == '_' && StringUtil::CharacterIsDigit(sql[i - 1]) && i + 1 < sql.size() && + StringUtil::CharacterIsDigit(sql[i + 1])) { break; } diff --git a/src/duckdb/src/parser/peg/transformer/peg_transformer_factory.cpp b/src/duckdb/src/parser/peg/transformer/peg_transformer_factory.cpp index cc0ac5f8f..02ae6b485 100644 --- a/src/duckdb/src/parser/peg/transformer/peg_transformer_factory.cpp +++ b/src/duckdb/src/parser/peg/transformer/peg_transformer_factory.cpp @@ -163,9 +163,8 @@ PEGTransformerFactory::PEGTransformerFactory(ParsedGrammar &grammar_p) : grammar grammar.SetExpressionDepth("SingleExpression", ExpressionDepthKind::NESTING); grammar.SetExpressionDepth("Type", ExpressionDepthKind::NESTING); grammar.SetExpressionDepth("NestedType", ExpressionDepthKind::NESTING); + grammar.SetExpressionDepth("SubqueryReference", ExpressionDepthKind::NESTING); grammar.SetExpressionDepth("LambdaArrowExpression", ExpressionDepthKind::CHAIN); - grammar.SetExpressionDepth("LogicalOrExpression", ExpressionDepthKind::CHAIN); - grammar.SetExpressionDepth("LogicalAndExpression", ExpressionDepthKind::CHAIN); grammar.SetExpressionDepth("NotExpression", ExpressionDepthKind::CHAIN); grammar.SetExpressionDepth("IsExpressionContinuation", ExpressionDepthKind::CHAIN); grammar.SetExpressionDepth("IsDistinctFromExpression", ExpressionDepthKind::CHAIN); diff --git a/src/duckdb/src/parser/peg/transformer/transform_copy.cpp b/src/duckdb/src/parser/peg/transformer/transform_copy.cpp index cbae0a00c..af20b2ff8 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_copy.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_copy.cpp @@ -40,7 +40,9 @@ void SetCopyOptions(unique_ptr &info, vector &optio info->parsed_options[option.name] = std::move(row_func); } } else if (option.name == "HEADER" || option.name == "ESCAPE") { - if (option.children.empty()) { + if (option.expression) { + info->parsed_options[option.name] = std::move(option.expression); + } else if (option.children.empty()) { info->parsed_options[option.name] = nullptr; } else { info->parsed_options[option.name] = ConstantExpression::FromValue(option.children[0]); @@ -183,8 +185,13 @@ unique_ptr PEGTransformerFactory::TransformCopyFileNameStringL unique_ptr PEGTransformerFactory::TransformCopyFileNameIdentifier(PEGTransformer &transformer, const Identifier &identifier) { - string file_name = identifier == "stdout" ? "/dev/stdout" : identifier.GetIdentifierName(); - return ConstantExpression::String(file_name); + if (identifier == "stdin") { + return ConstantExpression::String("/dev/stdin"); + } + if (identifier == "stdout") { + return ConstantExpression::String("/dev/stdout"); + } + return ConstantExpression::String(identifier.GetIdentifierName()); } unique_ptr diff --git a/src/duckdb/src/parser/peg/transformer/transform_drop.cpp b/src/duckdb/src/parser/peg/transformer/transform_drop.cpp index c0473c3f3..ecc4f4394 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_drop.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_drop.cpp @@ -48,13 +48,13 @@ bool PEGTransformerFactory::TransformFunctionTypeFunction(PEGTransformer &transf unique_ptr PEGTransformerFactory::TransformDropTableFunction(PEGTransformer &transformer, const CatalogType &comment_macro_table, const optional &if_exists, - const vector &table_function_name) { + const vector &table_function_name) { auto result = make_uniq(); auto info = make_uniq(); if (table_function_name.size() > 1) { throw NotImplementedException("Can only drop one object at a time"); } - info->SetQualifiedName(QualifiedName(table_function_name[0])); + info->SetQualifiedName(table_function_name[0]); info->type = comment_macro_table; info->if_not_found = if_exists ? OnEntryNotFound::RETURN_NULL : OnEntryNotFound::THROW_EXCEPTION; result->info = std::move(info); @@ -214,6 +214,9 @@ unique_ptr PEGTransformerFactory::TransformDropSecret(PEGTransfor info->if_not_found = if_exists ? OnEntryNotFound::RETURN_NULL : OnEntryNotFound::THROW_EXCEPTION; info->SetName(secret_name); if (drop_secret_storage) { + if (extra_drop_info->persist_mode == SecretPersistType::TEMPORARY) { + throw ParserException("Can not combine TEMPORARY with specifying a storage for drop secret"); + } extra_drop_info->secret_storage = drop_secret_storage->GetIdentifierName(); } info->extra_drop_info = std::move(extra_drop_info); diff --git a/src/duckdb/src/parser/peg/transformer/transform_expression.cpp b/src/duckdb/src/parser/peg/transformer/transform_expression.cpp index 1a2a75c7b..da74e8020 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_expression.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_expression.cpp @@ -239,6 +239,19 @@ unique_ptr PEGTransformerFactory::TransformFunctionExpression( if (filter_clause) { filter_expr = std::move(*filter_clause); } + if (within_group_clause) { + if (distinct) { + throw ParserException("DISTINCT is not allowed in combination with WITHIN GROUP"); + } + if (!order_modifier->orders.empty()) { + throw ParserException("Cannot use multiple ORDER BY statements with WITHIN GROUP"); + } + order_modifier->orders = std::move(*within_group_clause); + if (order_modifier->orders.size() != 1) { + throw ParserException("Cannot use multiple ORDER BY clauses with WITHIN GROUP"); + } + } + if (function_children.size() == 1 && ExpressionIsUnqualifiedStar(function_children[0].GetExpression()) && !distinct && order_modifier->orders.empty()) { // COUNT(*) gets converted into COUNT() @@ -289,20 +302,6 @@ unique_ptr PEGTransformerFactory::TransformFunctionExpression( throw ParserException("RESPECT/IGNORE NULLS is not supported for non-window functions"); } if (within_group_clause) { - auto order_by_clause = std::move(*within_group_clause); - if (distinct) { - throw ParserException("DISTINCT is not allowed in combination with WITHIN GROUP"); - } - if (!order_modifier->orders.empty()) { - throw ParserException("Cannot use multiple ORDER BY statements with WITHIN GROUP"); - } - if (!order_modifier) { - throw InternalException("ORDER modifier for WITHIN GROUP is not initialized"); - } - order_modifier->orders = std::move(order_by_clause); - if (order_modifier->orders.size() != 1) { - throw ParserException("Cannot use multiple ORDER BY clauses with WITHIN GROUP"); - } if (lowercase_name == "percentile_cont") { if (function_children.size() != 1) { throw ParserException("Wrong number of arguments for PERCENTILE_CONT"); @@ -603,11 +602,14 @@ static unique_ptr FoldConjunctionExpression(PEGTransformer &tr if (!tails) { return expr; } - auto depth_guard = transformer.StackCheck(tails->size()); + auto depth_guard = transformer.StackCheck(1); + vector> children; + children.reserve(tails->size() + 1); + children.push_back(std::move(expr)); for (auto &tail : *tails) { - expr = make_uniq(conjunction_type, std::move(expr), std::move(tail)); + children.push_back(std::move(tail)); } - return expr; + return make_uniq(conjunction_type, std::move(children)); } unique_ptr PEGTransformerFactory::TransformLogicalOrExpression( @@ -662,15 +664,10 @@ PEGTransformerFactory::TransformColDefAndExpressionTail(PEGTransformer &transfor return is_distinct_from_expression; } -unique_ptr -PEGTransformerFactory::TransformLogicalNotExpression(PEGTransformer &transformer, optional> not_expression, - unique_ptr is_expression) { - auto expr = std::move(is_expression); - if (!not_expression) { - return expr; - } - auto not_depth_guard = transformer.StackCheck(not_expression->size()); - for (idx_t i = 0; i < not_expression->size(); i++) { +static unique_ptr ApplyNotKeywords(PEGTransformer &transformer, const vector ¬_keywords, + unique_ptr expr) { + auto not_depth_guard = transformer.StackCheck(not_keywords.size()); + for (idx_t i = 0; i < not_keywords.size(); i++) { vector> inner_list_children; inner_list_children.push_back(std::move(expr)); expr = make_uniq(ExpressionType::OPERATOR_NOT, std::move(inner_list_children)); @@ -678,6 +675,22 @@ PEGTransformerFactory::TransformLogicalNotExpression(PEGTransformer &transformer return expr; } +unique_ptr +PEGTransformerFactory::TransformLogicalNotExpression(PEGTransformer &transformer, + const optional> ¬_expression, + unique_ptr is_expression) { + if (!not_expression) { + return is_expression; + } + return ApplyNotKeywords(transformer, *not_expression, std::move(is_expression)); +} + +unique_ptr +PEGTransformerFactory::TransformNotComparisonOperand(PEGTransformer &transformer, const vector ¬_expression, + unique_ptr is_expression) { + return ApplyNotKeywords(transformer, not_expression, std::move(is_expression)); +} + vector PEGTransformerFactory::TransformNotExpression(PEGTransformer &transformer, const vector ¬_keyword) { return not_keyword; @@ -699,6 +712,13 @@ static unique_ptr ApplyIsTest(unique_ptr exp ExpressionClassToString(is_expr->GetExpressionClass())); } +static unique_ptr ApplyCastOperator(unique_ptr expr, + unique_ptr cast_operator) { + auto cast_expr = unique_ptr_cast(std::move(cast_operator)); + cast_expr->ChildMutable() = std::move(expr); + return std::move(cast_expr); +} + static unique_ptr ApplyIsDistinctFromTail(unique_ptr expr, IsDistinctFromTail tail) { return make_uniq(tail.comparison_type, std::move(expr), std::move(tail.expression)); @@ -706,13 +726,7 @@ static unique_ptr ApplyIsDistinctFromTail(unique_ptr ApplyComparisonTail(unique_ptr expr, ComparisonExpressionTail tail) { - auto right_expr = std::move(tail.expression); - for (idx_t i = 0; i < tail.not_keywords.size(); i++) { - vector> inner_list_children; - inner_list_children.push_back(std::move(right_expr)); - right_expr = make_uniq(ExpressionType::OPERATOR_NOT, std::move(inner_list_children)); - } - return make_uniq(tail.comparison_type, std::move(expr), std::move(right_expr)); + return make_uniq(tail.comparison_type, std::move(expr), std::move(tail.expression)); } unique_ptr @@ -745,6 +759,9 @@ PEGTransformerFactory::TransformIsExpression(PEGTransformer &transformer, expr = TransformInfixOtherOperatorExpression(transformer, std::move(expr), std::move(other_tail)); break; } + case ExpressionTailType::CAST: + expr = ApplyCastOperator(std::move(expr), std::move(tail.cast)); + break; } previous_type = tail.type; } @@ -796,6 +813,14 @@ IsExpressionTail PEGTransformerFactory::TransformIsOtherOperatorTail(PEGTransfor return result; } +IsExpressionTail PEGTransformerFactory::TransformIsCastTail(PEGTransformer &transformer, + unique_ptr cast_operator) { + IsExpressionTail result; + result.type = ExpressionTailType::CAST; + result.cast = std::move(cast_operator); + return result; +} + unique_ptr PEGTransformerFactory::TransformIsLiteral(PEGTransformer &transformer, const bool &has_result, const Value &is_literal_value) { @@ -869,15 +894,13 @@ PEGTransformerFactory::TransformIsDistinctFromTail(PEGTransformer &transformer, return {is_distinct_from_op, std::move(comparison_expression)}; } -ComparisonExpressionTail PEGTransformerFactory::TransformComparisonExpressionTail( - PEGTransformer &transformer, const ExpressionType &comparison_operator, optional> not_expression, - unique_ptr between_in_like_expression) { +ComparisonExpressionTail +PEGTransformerFactory::TransformComparisonExpressionTail(PEGTransformer &transformer, + const ExpressionType &comparison_operator, + unique_ptr comparison_operand) { ComparisonExpressionTail result; result.comparison_type = comparison_operator; - if (not_expression) { - result.not_keywords = std::move(*not_expression); - } - result.expression = std::move(between_in_like_expression); + result.expression = std::move(comparison_operand); return result; } @@ -1122,7 +1145,12 @@ unique_ptr PEGTransformerFactory::TransformBetweenInLikeExpres if (!in_predicate && !between_like_op) { return expr; } - auto predicate_count = in_predicate ? in_predicate->size() : 0; + idx_t predicate_count = 0; + if (in_predicate) { + for (auto &predicate : *in_predicate) { + predicate_count += 1 + predicate.casts.size(); + } + } if (between_like_op) { predicate_count++; } @@ -1130,6 +1158,9 @@ unique_ptr PEGTransformerFactory::TransformBetweenInLikeExpres if (in_predicate) { for (auto &predicate : *in_predicate) { expr = ApplyBetweenInLikeOperator(std::move(expr), predicate); + for (auto &cast : predicate.casts) { + expr = ApplyCastOperator(std::move(expr), std::move(cast)); + } } } if (between_like_op) { @@ -1138,9 +1169,15 @@ unique_ptr PEGTransformerFactory::TransformBetweenInLikeExpres return expr; } -BetweenInLikeOperator PEGTransformerFactory::TransformInPredicate(PEGTransformer &transformer, const bool &has_result, - unique_ptr in_clause) { - return {has_result, std::move(in_clause)}; +BetweenInLikeOperator +PEGTransformerFactory::TransformInPredicate(PEGTransformer &transformer, const bool &has_result, + unique_ptr in_clause, + optional>> cast_operator) { + BetweenInLikeOperator result {has_result, std::move(in_clause)}; + if (cast_operator) { + result.casts = std::move(*cast_operator); + } + return result; } BetweenInLikeOperator @@ -1216,6 +1253,9 @@ PEGTransformerFactory::TransformLikeClause(PEGTransformer &transformer, const st throw ParserException( "ESCAPE clause is not supported with case-insensitive regular expression match operators"); } + if (is_regex_operator && escape_clause) { + throw ParserException("ESCAPE clause is not supported with SIMILAR TO and regular expression match operators"); + } if (escape_clause) { if (like_variation == "~~") { like_variation = "like_escape"; diff --git a/src/duckdb/src/parser/peg/transformer/transform_generated_trampoline.cpp b/src/duckdb/src/parser/peg/transformer/transform_generated_trampoline.cpp index 26b598280..393374abb 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_generated_trampoline.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_generated_trampoline.cpp @@ -1734,6 +1734,8 @@ static const TransformFrameOps IS_COMPARISON_TAIL_OPS = {"IsComparisonTail", static const TransformFrameOps IS_OTHER_OPERATOR_TAIL_OPS = { "IsOtherOperatorTail", &PEGTransformerFactory::InitializeIsOtherOperatorTailTrampoline, &PEGTransformerFactory::FinalizeIsOtherOperatorTailTrampoline}; +static const TransformFrameOps IS_CAST_TAIL_OPS = {"IsCastTail", &PEGTransformerFactory::InitializeIsCastTailTrampoline, + &PEGTransformerFactory::FinalizeIsCastTailTrampoline}; static const TransformFrameOps IS_TEST_OPS = {"IsTest", &PEGTransformerFactory::InitializeIsTestTrampoline, &PEGTransformerFactory::FinalizeIsTestTrampoline}; static const TransformFrameOps IS_LITERAL_OPS = {"IsLiteral", &PEGTransformerFactory::InitializeIsLiteralTrampoline, @@ -1772,6 +1774,12 @@ static const TransformFrameOps COMPARISON_EXPRESSION_OPS = { static const TransformFrameOps COMPARISON_EXPRESSION_TAIL_OPS = { "ComparisonExpressionTail", &PEGTransformerFactory::InitializeComparisonExpressionTailTrampoline, &PEGTransformerFactory::FinalizeComparisonExpressionTailTrampoline}; +static const TransformFrameOps COMPARISON_OPERAND_OPS = {"ComparisonOperand", + &PEGTransformerFactory::InitializeComparisonOperandTrampoline, + &PEGTransformerFactory::FinalizeComparisonOperandTrampoline}; +static const TransformFrameOps NOT_COMPARISON_OPERAND_OPS = { + "NotComparisonOperand", &PEGTransformerFactory::InitializeNotComparisonOperandTrampoline, + &PEGTransformerFactory::FinalizeNotComparisonOperandTrampoline}; static const TransformFrameOps COMPARISON_OPERATOR_OPS = { "ComparisonOperator", &PEGTransformerFactory::InitializeComparisonOperatorTrampoline, &PEGTransformerFactory::FinalizeComparisonOperatorTrampoline}; @@ -3868,6 +3876,7 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"IsDistinctTail", &IS_DISTINCT_TAIL_OPS}, {"IsComparisonTail", &IS_COMPARISON_TAIL_OPS}, {"IsOtherOperatorTail", &IS_OTHER_OPERATOR_TAIL_OPS}, + {"IsCastTail", &IS_CAST_TAIL_OPS}, {"IsTest", &IS_TEST_OPS}, {"IsLiteral", &IS_LITERAL_OPS}, {"IsLiteralValue", &IS_LITERAL_VALUE_OPS}, @@ -3882,6 +3891,8 @@ const case_insensitive_map_t &PEGTransformerFactory:: {"IsDistinctFromOp", &IS_DISTINCT_FROM_OP_OPS}, {"ComparisonExpression", &COMPARISON_EXPRESSION_OPS}, {"ComparisonExpressionTail", &COMPARISON_EXPRESSION_TAIL_OPS}, + {"ComparisonOperand", &COMPARISON_OPERAND_OPS}, + {"NotComparisonOperand", &NOT_COMPARISON_OPERAND_OPS}, {"ComparisonOperator", &COMPARISON_OPERATOR_OPS}, {"OperatorEqual", &OPERATOR_EQUAL_OPS}, {"OperatorNotEqual", &OPERATOR_NOT_EQUAL_OPS}, @@ -12767,7 +12778,13 @@ arena_ptr PEGTransformerFactory::FinalizeDropTableTrampoli void PEGTransformerFactory::InitializeDropTableFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(2); + auto list_items = ExtractParseResultsFromList(list_pr.GetChild(2)); + auto dynamic_child_count = list_items.size(); + process.ReserveChildSlots(3 + dynamic_child_count - 1); + for (idx_t i = list_items.size(); i > 0; i--) { + auto child_idx = i - 1; + process.PushChild({transformer.GetRule("QualifiedTableFunction"), list_items[child_idx].get()}, 2 + child_idx); + } auto &if_exists_opt = list_pr.GetChild(1).Cast(); if (if_exists_opt.HasResult()) { process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 1); @@ -12779,17 +12796,18 @@ arena_ptr PEGTransformerFactory::FinalizeDropTableFunctionTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); + auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(2)); + auto dynamic_child_count = dynamic_list_items.size(); auto comment_macro_table = process.TakeResult(0); optional if_exists {}; if (process.child_results[1]) { if_exists = process.TakeResult(1); } - vector table_function_name; - auto table_function_name_items = ExtractParseResultsFromList(list_pr.GetChild(2)); - for (auto &table_function_name_item : table_function_name_items) { - table_function_name.push_back(table_function_name_item.get().Cast().identifier); + vector qualified_table_function; + for (idx_t i = 2; i < 2 + dynamic_child_count; i++) { + qualified_table_function.push_back(process.TakeResult(i)); } - auto result = TransformDropTableFunction(transformer, comment_macro_table, if_exists, table_function_name); + auto result = TransformDropTableFunction(transformer, comment_macro_table, if_exists, qualified_table_function); return transformer.MakeResult>(std::move(result)); } @@ -16182,7 +16200,7 @@ PEGTransformerFactory::FinalizeLogicalNotExpressionTrampoline(PEGTransformer &tr not_expression = process.TakeResult>(0); } auto is_expression = process.TakeResult>(1); - auto result = TransformLogicalNotExpression(transformer, std::move(not_expression), std::move(is_expression)); + auto result = TransformLogicalNotExpression(transformer, not_expression, std::move(is_expression)); return transformer.MakeResult>(std::move(result)); } @@ -16211,7 +16229,7 @@ PEGTransformerFactory::FinalizeNotExpressionTrampoline(PEGTransformer &transform not_keyword.push_back(process.TakeResult(i)); } auto result = TransformNotExpression(transformer, not_keyword); - return transformer.MakeResult>(std::move(result)); + return transformer.MakeResult>(result); } void PEGTransformerFactory::InitializeNotKeywordTrampoline(PEGTransformer &transformer, @@ -16374,6 +16392,20 @@ PEGTransformerFactory::FinalizeIsOtherOperatorTailTrampoline(PEGTransformer &tra return transformer.MakeResult(std::move(result)); } +void PEGTransformerFactory::InitializeIsCastTailTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(1); + process.PushChild({transformer.GetRule("CastOperator"), list_pr.GetChild(0)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeIsCastTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { + auto cast_operator = process.TakeResult>(0); + auto result = TransformIsCastTail(transformer, std::move(cast_operator)); + return transformer.MakeResult(std::move(result)); +} + void PEGTransformerFactory::InitializeIsTestTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -16643,12 +16675,8 @@ PEGTransformerFactory::FinalizeComparisonExpressionTrampoline(PEGTransformer &tr void PEGTransformerFactory::InitializeComparisonExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(3); - process.PushChild({transformer.GetRule("BetweenInLikeExpression"), list_pr.GetChild(2)}, 2); - auto ¬_expression_opt = list_pr.GetChild(1).Cast(); - if (not_expression_opt.HasResult()) { - process.PushChild({transformer.GetRule("NotExpression"), not_expression_opt.GetResult()}, 1); - } + process.ReserveChildSlots(2); + process.PushChild({transformer.GetRule("ComparisonOperand"), list_pr.GetChild(1)}, 1); process.PushChild({transformer.GetRule("ComparisonOperator"), list_pr.GetChild(0)}, 0); } @@ -16656,16 +16684,49 @@ arena_ptr PEGTransformerFactory::FinalizeComparisonExpressionTailTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto comparison_operator = process.TakeResult(0); - optional> not_expression {}; - if (process.child_results[1]) { - not_expression = process.TakeResult>(1); - } - auto between_in_like_expression = process.TakeResult>(2); - auto result = TransformComparisonExpressionTail(transformer, comparison_operator, std::move(not_expression), - std::move(between_in_like_expression)); + auto comparison_operand = process.TakeResult>(1); + auto result = TransformComparisonExpressionTail(transformer, comparison_operator, std::move(comparison_operand)); return transformer.MakeResult(std::move(result)); } +void PEGTransformerFactory::InitializeComparisonOperandTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + auto &choice_pr = list_pr.Child(0); + auto &choice_result = choice_pr.GetResult(); + process.ReserveChildSlots(1); + auto child_rule = choice_result.GetRule(); + auto has_transform_process = child_rule && child_rule->transform_process; + if (!has_transform_process) { + throw InternalException("No transform process registered for rule '%s'", choice_result.Name()); + } + process.PushChild({*child_rule, choice_result}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeComparisonOperandTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto result = process.TakeResult>(0); + return transformer.MakeResult>(std::move(result)); +} + +void PEGTransformerFactory::InitializeNotComparisonOperandTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto &list_pr = process.parse_result.Cast(); + process.ReserveChildSlots(2); + process.PushChild({transformer.GetRule("IsExpression"), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule("NotExpression"), list_pr.GetChild(0)}, 0); +} + +arena_ptr +PEGTransformerFactory::FinalizeNotComparisonOperandTrampoline(PEGTransformer &transformer, + GeneratedTransformProcess &process) { + auto not_expression = process.TakeResult>(0); + auto is_expression = process.TakeResult>(1); + auto result = TransformNotComparisonOperand(transformer, not_expression, std::move(is_expression)); + return transformer.MakeResult>(std::move(result)); +} + void PEGTransformerFactory::InitializeComparisonOperatorTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); @@ -16821,18 +16882,46 @@ PEGTransformerFactory::FinalizeBetweenInLikeExpressionTrampoline(PEGTransformer void PEGTransformerFactory::InitializeInPredicateTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(1); + auto &repeat_opt = list_pr.GetChild(2).Cast(); + idx_t dynamic_child_count = 0; + if (repeat_opt.HasResult()) { + auto &repeat_pr = repeat_opt.GetResult().Cast(); + auto repeat_children = repeat_pr.GetChildren(); + dynamic_child_count = repeat_children.size(); + process.ReserveChildSlots(2 + dynamic_child_count - 1); + for (idx_t i = repeat_children.size(); i > 0; i--) { + auto child_idx = i - 1; + process.PushChild({transformer.GetRule("CastOperator"), repeat_children[child_idx].get()}, 1 + child_idx); + } + } else { + process.ReserveChildSlots(2 - 1); + } process.PushChild({transformer.GetRule("InClause"), list_pr.GetChild(1)}, 0); } arena_ptr PEGTransformerFactory::FinalizeInPredicateTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); + idx_t dynamic_child_count = 0; + auto &dynamic_repeat_opt = list_pr.GetChild(2).Cast(); + if (dynamic_repeat_opt.HasResult()) { + auto &dynamic_repeat_pr = dynamic_repeat_opt.GetResult().Cast(); + auto dynamic_repeat_children = dynamic_repeat_pr.GetChildren(); + dynamic_child_count = dynamic_repeat_children.size(); + } bool has_result {}; auto &has_result_opt = list_pr.GetChild(0).Cast(); has_result = has_result_opt.HasResult(); auto in_clause = process.TakeResult>(0); - auto result = TransformInPredicate(transformer, has_result, std::move(in_clause)); + optional>> cast_operator {}; + if (dynamic_child_count > 0) { + vector> cast_operator_value; + for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { + cast_operator_value.push_back(process.TakeResult>(i)); + } + cast_operator = std::move(cast_operator_value); + } + auto result = TransformInPredicate(transformer, has_result, std::move(in_clause), std::move(cast_operator)); return transformer.MakeResult(std::move(result)); } @@ -23197,7 +23286,11 @@ PEGTransformerFactory::FinalizeTableAliasColonTrampoline(PEGTransformer &transfo void PEGTransformerFactory::InitializeValuesRefTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(3); + process.ReserveChildSlots(4); + auto &sample_clause_opt = list_pr.GetChild(3).Cast(); + if (sample_clause_opt.HasResult()) { + process.PushChild({transformer.GetRule("SampleClause"), sample_clause_opt.GetResult()}, 3); + } auto &table_alias_opt = list_pr.GetChild(2).Cast(); if (table_alias_opt.HasResult()) { process.PushChild({transformer.GetRule("TableAlias"), table_alias_opt.GetResult()}, 2); @@ -23220,7 +23313,12 @@ arena_ptr PEGTransformerFactory::FinalizeValuesRefTrampoli if (process.child_results[2]) { table_alias = process.TakeResult(2); } - auto result = TransformValuesRef(transformer, table_alias_colon, std::move(values_clause), table_alias); + optional> sample_clause {}; + if (process.child_results[3]) { + sample_clause = process.TakeResult>(3); + } + auto result = TransformValuesRef(transformer, table_alias_colon, std::move(values_clause), table_alias, + std::move(sample_clause)); return transformer.MakeResult>(std::move(result)); } @@ -23908,15 +24006,17 @@ PEGTransformerFactory::FinalizeFunctionArgumentTrampoline(PEGTransformer &transf void PEGTransformerFactory::InitializeNamedFunctionArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); - process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("NamedParameter"), list_pr.GetChild(0)}, 0); + process.ReserveChildSlots(2); + process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule("TypeFuncName"), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeNamedFunctionArgumentTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { - auto named_parameter = process.TakeResult(0); - auto result = TransformNamedFunctionArgument(transformer, std::move(named_parameter)); + auto type_func_name = process.TakeResult(0); + auto expression = process.TakeResult>(1); + auto result = TransformNamedFunctionArgument(transformer, type_func_name, std::move(expression)); return transformer.MakeResult(std::move(result)); } diff --git a/src/duckdb/src/parser/peg/transformer/transform_generic_copy_option.cpp b/src/duckdb/src/parser/peg/transformer/transform_generic_copy_option.cpp index 3bc06b644..74a0b8c0e 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_generic_copy_option.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_generic_copy_option.cpp @@ -44,7 +44,8 @@ static void SetGenericCopyOptionExpression(GenericCopyOption ©_option, uniqu ExpressionClassToString(child->GetExpressionClass())); } } - } else if (expression->GetExpressionType() == ExpressionType::FUNCTION) { + } else if (expression->GetExpressionType() == ExpressionType::FUNCTION || + expression->GetExpressionType() == ExpressionType::VALUE_PARAMETER) { copy_option.expression = std::move(expression); } else if (expression->GetExpressionType() == ExpressionType::STAR) { copy_option.children.push_back(Value("*")); diff --git a/src/duckdb/src/parser/peg/transformer/transform_pragma.cpp b/src/duckdb/src/parser/peg/transformer/transform_pragma.cpp index cd4860cb6..3aeb2e5b6 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_pragma.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_pragma.cpp @@ -1,3 +1,4 @@ +#include "duckdb/logging/logger.hpp" #include "duckdb/parser/statement/pragma_statement.hpp" #include "duckdb/parser/expression/columnref_expression.hpp" #include "duckdb/parser/peg/transformer/peg_transformer.hpp" @@ -39,6 +40,12 @@ PEGTransformerFactory::TransformPragmaAssign(PEGTransformer &transformer, const if (sqlite_compat_pragmas.find(info.name) != sqlite_compat_pragmas.end()) { return std::move(result); } + if (transformer.options.context) { + DUCKDB_LOG_WARNING(*transformer.options.context, + "Using \"PRAGMA %s=...\" to set a setting is deprecated and will be removed in a future " + "release, use \"SET %s=...\" instead.", + info.name.GetIdentifierName(), info.name.GetIdentifierName()); + } auto set_statement = make_uniq(info.name, std::move(info.parameters[0]), SetScope::AUTOMATIC); return std::move(set_statement); } diff --git a/src/duckdb/src/parser/peg/transformer/transform_select.cpp b/src/duckdb/src/parser/peg/transformer/transform_select.cpp index ce09ba78a..eb38b7a23 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_select.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_select.cpp @@ -318,13 +318,10 @@ PEGTransformerFactory::FinalizeSimpleSelectTrampoline(PEGTransformer &transforme } FunctionArgument PEGTransformerFactory::TransformNamedFunctionArgument(PEGTransformer &transformer, - MacroParameter named_parameter) { - if (named_parameter.type != LogicalType::UNKNOWN) { - throw ParserException("Named argument \"%s\" of a function call cannot have a type", - named_parameter.name.GetIdentifierName()); - } - named_parameter.expression->SetAlias(named_parameter.name); - return FunctionArgument(named_parameter.name, std::move(named_parameter.expression)); + const Identifier &type_func_name, + unique_ptr expression) { + expression->SetAlias(type_func_name); + return FunctionArgument(type_func_name, std::move(expression)); } FunctionArgument PEGTransformerFactory::TransformPositionalFunctionArgument(PEGTransformer &transformer, @@ -779,6 +776,9 @@ unique_ptr PEGTransformerFactory::TransformTableFunctionLateralOpt( PEGTransformer &transformer, const optional &lateral, const QualifiedName &qualified_table_function, vector table_function_arguments, const optional &with_ordinality, const optional &table_alias, optional> sample_clause) { + if (lateral.value_or(false) && sample_clause) { + throw ParserException("TABLESAMPLE cannot be applied to a LATERAL table function"); + } auto result = make_uniq(); result->with_ordinality = @@ -1366,6 +1366,9 @@ unique_ptr PEGTransformerFactory::TransformTableSubquery(PEGTransforme if (table_alias_colon && table_alias) { throw ParserException("Table reference %s cannot have two aliases", subquery_reference->ToString()); } + if (lateral.value_or(false) && sample_clause) { + throw ParserException("TABLESAMPLE cannot be applied to a LATERAL subquery"); + } if (table_alias_colon) { subquery_reference->alias = *table_alias_colon; } @@ -1412,7 +1415,8 @@ Identifier PEGTransformerFactory::TransformTableAliasColon(PEGTransformer &trans unique_ptr PEGTransformerFactory::TransformValuesRef(PEGTransformer &transformer, const optional &table_alias_colon, unique_ptr values_clause, - const optional &table_alias) { + const optional &table_alias, + optional> sample_clause) { if (table_alias_colon && table_alias) { throw ParserException("Table reference cannot have two aliases"); } @@ -1424,6 +1428,9 @@ unique_ptr PEGTransformerFactory::TransformValuesRef(PEGTransformer &t subquery_ref->alias = table_alias->name; subquery_ref->column_name_alias = table_alias->column_name_alias; } + if (sample_clause) { + subquery_ref->sample = std::move(*sample_clause); + } return std::move(subquery_ref); } diff --git a/src/duckdb/src/planner/binder/expression/bind_function_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_function_expression.cpp index 37c6c38c1..7b176d21e 100644 --- a/src/duckdb/src/planner/binder/expression/bind_function_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_function_expression.cpp @@ -308,7 +308,10 @@ BindResult ExpressionBinder::BindExpression(FunctionExpression &function, idx_t return BindFunction(function, func.Cast(), depth); } case CatalogType::MACRO_ENTRY: - // macro function + // macro function - a prepared statement must be re-bound when the macro changes + if (!func.ParentCatalog().IsSystemCatalog()) { + binder.GetStatementProperties().RegisterDBRead(func.ParentCatalog(), context); + } return BindMacro(function, func.Cast(), depth, expr_ptr); case CatalogType::AGGREGATE_FUNCTION_ENTRY: { // an aggregate belongs to the innermost query level in which one of its arguments resolves a diff --git a/src/duckdb/src/planner/binder/expression/bind_star_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_star_expression.cpp index f28df17bb..f9e1bba28 100644 --- a/src/duckdb/src/planner/binder/expression/bind_star_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_star_expression.cpp @@ -232,8 +232,19 @@ void TryTransformStarLike(unique_ptr &root) { unique_ptr child_expr; if (!inverse && function.FunctionName() == "regexp_full_match" && function.GetArguments().size() == 2 && star.ExcludeList().empty()) { - // * SIMILAR TO '[regex]' is equivalent to COLUMNS('[regex]') so we can just move the expression directly - child_expr = std::move(right.GetExpressionMutable()); + // * SIMILAR TO '[regex]' is equivalent to COLUMNS('^(?:[regex])$') - COLUMNS matches partially + auto &pattern = right.GetExpression().Cast(); + if (pattern.GetLiteral().kind == LiteralKind::STRING) { + child_expr = ConstantExpression::String("^(?:" + pattern.GetLiteral().text + ")$"); + } else { + vector> prefix_children; + prefix_children.push_back(ConstantExpression::String("^(?:")); + prefix_children.push_back(std::move(right.GetExpressionMutable())); + vector> anchored_children; + anchored_children.push_back(make_uniq("||", std::move(prefix_children))); + anchored_children.push_back(ConstantExpression::String(")$")); + child_expr = make_uniq("||", std::move(anchored_children)); + } } else { // for other expressions -> generate a columns expression // "* LIKE '%literal%' diff --git a/src/duckdb/src/planner/binder/expression/bind_unpacked_star_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_unpacked_star_expression.cpp index 6096da25d..39915d7db 100644 --- a/src/duckdb/src/planner/binder/expression/bind_unpacked_star_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_unpacked_star_expression.cpp @@ -95,6 +95,9 @@ static void ReplaceInOperator(unique_ptr &expr, expression_lis // Replace children expression_list_t new_children; for (auto &child : operator_expr.GetChildrenMutable()) { + if (star_list.empty() && StarExpression::IsColumnsUnpacked(*child)) { + throw BinderException("*COLUMNS() expanded to zero columns, which is not supported in this place"); + } AddChild(child, new_children, star_list, star, regex); } operator_expr.GetChildrenMutable() = std::move(new_children); diff --git a/src/duckdb/src/planner/binder/query_node/bind_select_node.cpp b/src/duckdb/src/planner/binder/query_node/bind_select_node.cpp index 27ce506ff..df6ac951e 100644 --- a/src/duckdb/src/planner/binder/query_node/bind_select_node.cpp +++ b/src/duckdb/src/planner/binder/query_node/bind_select_node.cpp @@ -615,21 +615,25 @@ BoundStatement Binder::BindSelectNode(SelectNode &statement, BoundStatement from for (idx_t i = 0; i < group_expressions.size(); i++) { // bind the groups LogicalType group_type; + // bound subqueries cannot be copied, so keep the parsed expression in case we need to bind it again + auto parsed_copy = group_expressions[i]->Copy(); auto bound_expr = group_binder.Bind(group_expressions[i], &group_type); if (bound_expr->GetExpressionType() == ExpressionType::BOUND_EXPANDED) { throw BinderException("UNNEST of struct cannot be used in GROUP BY clause"); } D_ASSERT(bound_expr->GetReturnType().id() != LogicalTypeId::INVALID); - // find out whether the expression contains a subquery, it can't be copied if so - bool contains_subquery = bound_expr->HasSubquery(); // the group and the first(x) aggregate below are separate expression trees, so we need our own // copy - it must be made before pushing the collation, which can replace the expression + bool contains_subquery = bound_expr->HasSubquery(); unique_ptr uncollated_expr = contains_subquery ? nullptr : bound_expr->Copy(); // push a potential collation, if necessary bool requires_collation = ExpressionBinder::PushCollation(context, bound_expr, group_type); - if (!contains_subquery && requires_collation) { + if (requires_collation && contains_subquery) { + uncollated_expr = group_binder.Bind(parsed_copy); + } + if (requires_collation) { // if there is a collation on a group x, we should group by the collated expr, // but also push a first(x) aggregate in case x is selected (uncollated) @@ -725,9 +729,6 @@ BoundStatement Binder::BindSelectNode(SelectNode &statement, BoundStatement from } bind_state.AddRegularColumn(); - if (can_group_by_all && is_window) { - throw BinderException("Cannot group on a window clause"); - } if (can_group_by_all && result.unnests.SelectList().size() > unnest_count) { throw BinderException("Cannot group on an UNNEST or UNLIST clause"); } @@ -735,6 +736,10 @@ BoundStatement Binder::BindSelectNode(SelectNode &statement, BoundStatement from if (select_binder.BoundAggregates()) { throw BinderException("Cannot mix aggregates with non-aggregated columns!"); } + if (is_window) { + // a window over aggregates (e.g. sum(sum(x)) OVER ()) is fine, a window over a group is not + throw BinderException("Cannot group on a window clause"); + } // we are forcing aggregates, and the node has columns bound // this entry becomes a group group_by_all_indexes.push_back(i); @@ -755,8 +760,23 @@ BoundStatement Binder::BindSelectNode(SelectNode &statement, BoundStatement from // push the GROUP BY ALL expressions into the group set for (auto &group_by_all_index : group_by_all_indexes) { auto &expr = result.select_list[group_by_all_index]; - auto &return_type = expr->GetReturnType(); + auto return_type = expr->GetReturnType(); + // group by the collated expression, and select the uncollated value through a first(x) aggregate + unique_ptr uncollated_expr = expr->HasSubquery() ? nullptr : expr->Copy(); + bool requires_collation = ExpressionBinder::PushCollation(context, expr, return_type); auto group_proj_idx = ColumnBinding::PushExpression(result.groups.group_expressions, std::move(expr)); + if (uncollated_expr && requires_collation) { + auto first_fun = GetBuiltinAggregateFunction(context, FirstFun::Name, {uncollated_expr->GetReturnType()}); + vector> first_children; + first_children.push_back(std::move(uncollated_expr)); + FunctionBinder function_binder(*this); + auto function = function_binder.BindAggregateFunction(std::move(first_fun), std::move(first_children)); + function->SetAlias("__collated_group"); + auto aggregate_idx = ColumnBinding::PushExpression(result.aggregates, std::move(function)); + expr = + make_uniq(return_type, ColumnBinding(result.aggregate_index, aggregate_idx)); + continue; + } auto group_ref = make_uniq(return_type, ColumnBinding(result.group_index, group_proj_idx)); expr = std::move(group_ref); diff --git a/src/duckdb/src/planner/binder/statement/bind_copy_database.cpp b/src/duckdb/src/planner/binder/statement/bind_copy_database.cpp index af07d6889..608976350 100644 --- a/src/duckdb/src/planner/binder/statement/bind_copy_database.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_copy_database.cpp @@ -112,6 +112,9 @@ BoundStatement Binder::Bind(CopyDatabaseStatement &stmt) { throw BinderException("Cannot copy from %s to %s - FROM and TO databases are the same", stmt.from_database, stmt.to_database); } + if (target_catalog.IsSystemCatalog()) { + throw BinderException("Cannot copy to the system catalog"); + } if (stmt.copy_type == CopyDatabaseType::COPY_SCHEMA) { result.types = {LogicalType::BOOLEAN}; result.names = {"Success"}; diff --git a/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp b/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp index 46ecedf6b..7cdbacbfe 100644 --- a/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp +++ b/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp @@ -304,6 +304,9 @@ BoundStatement Binder::Bind(BaseTableRef &ref) { case CatalogType::VIEW_ENTRY: { // the node is a view: get the query that the view represents auto &view_catalog_entry = table_or_view->Cast(); + if (!view_catalog_entry.ParentCatalog().IsSystemCatalog()) { + GetStatementProperties().RegisterDBRead(view_catalog_entry.ParentCatalog(), context); + } // expanding nested views can take a long time, allow the query to be interrupted context.InterruptCheck(); // We need to use a new binder for the view that doesn't reference any CTEs diff --git a/src/duckdb/src/planner/binder/tableref/bind_pivot.cpp b/src/duckdb/src/planner/binder/tableref/bind_pivot.cpp index 453117558..7e0083d11 100644 --- a/src/duckdb/src/planner/binder/tableref/bind_pivot.cpp +++ b/src/duckdb/src/planner/binder/tableref/bind_pivot.cpp @@ -198,7 +198,13 @@ static unique_ptr PivotFilteredAggregate(ClientContext &context, Piv D_ASSERT(aggregates.size() == 1); auto &aggr = aggregates[0].get().Cast(); - aggr.FilterMutable() = filter->Copy(); + if (aggr.FilterMutable()) { + // keep the FILTER of the aggregate - only the rows that match both filters are aggregated + aggr.FilterMutable() = make_uniq( + ExpressionType::CONJUNCTION_AND, std::move(aggr.FilterMutable()), filter->Copy()); + } else { + aggr.FilterMutable() = filter->Copy(); + } auto &aggr_name = aggregate->GetAlias(); auto name = pivot_value.name; if (ref.aggregates.size() > 1 || !aggr_name.empty()) { @@ -385,8 +391,9 @@ static unique_ptr PivotFinalOperator(PivotBindState &bind_state, Piv for (auto &group_name : bound_pivot->bound_group_names) { final_pivot_operator->select_list.push_back(make_uniq(group_name)); } - // gather aggregate names - vector aggregate_names; + // gather aggregate names - these are deduplicated together with the group names, as they are all output columns + vector aggregate_names = bound_pivot->bound_group_names; + const auto group_count = aggregate_names.size(); for (auto &pivot_value : bound_pivot->bound_pivot_values) { for (idx_t aggr_idx = 0; aggr_idx < ref.aggregates.size(); aggr_idx++) { auto aggr = ref.aggregates[aggr_idx]->Copy(); @@ -396,6 +403,7 @@ static unique_ptr PivotFinalOperator(PivotBindState &bind_state, Piv } } QueryResult::DeduplicateColumns(aggregate_names); + aggregate_names.erase(aggregate_names.begin(), aggregate_names.begin() + NumericCast(group_count)); idx_t aggr_name_idx = 0; for (idx_t pivot_idx = 0; pivot_idx < bound_pivot->bound_pivot_values.size(); pivot_idx++) { @@ -991,6 +999,10 @@ unique_ptr Binder::BindUnpivot(Binder &child_binder, PivotRef &ref, result_node->from_table = std::move(subquery); + // the names of the columns that are passed through (select_names is moved from below) + const vector passthrough_names(select_names.begin(), + select_names.begin() + NumericCast(column_count)); + // construct the final UNNEST calls which generate the final unpivot result for (idx_t i = 0; i < column_count; i++) { auto select_col = make_uniq(std::move(select_names[i])); @@ -1001,8 +1013,13 @@ unique_ptr Binder::BindUnpivot(Binder &child_binder, PivotRef &ref, vector> unnest_name_children; unnest_name_children.push_back(std::move(unpivot_name_list)); auto unnest_name_expr = make_uniq("unnest", std::move(unnest_name_children)); - unnest_name_expr->SetAlias(unpivot.unpivot_names[0]); + // when excluding NULLs, the unnested columns get internal names: the final names can collide with other columns + // and the NULL filter has to be applied to exactly these columns + const bool exclude_nulls = !ref.include_nulls; + const Identifier internal_name_alias("__unpivot_name"); + unnest_name_expr->SetAlias(exclude_nulls ? internal_name_alias : unpivot.unpivot_names[0]); result_node->select_list.push_back(std::move(unnest_name_expr)); + vector> value_aliases; for (idx_t i = 0; i < unnest_count; i++) { auto unpivot_internal_name = std::move(select_names[column_count + 1 + i]); @@ -1012,21 +1029,48 @@ unique_ptr Binder::BindUnpivot(Binder &child_binder, PivotRef &ref, unnest_val_children.push_back(std::move(unpivot_list_ref)); auto unnest_val_expr = make_uniq("unnest", std::move(unnest_val_children)); auto &unnest_name = i < ref.column_name_alias.size() ? ref.column_name_alias[i] : ref.unpivot_names[i]; - unnest_val_expr->SetAlias(unnest_name); + if (exclude_nulls) { + Identifier internal_value_alias("__unpivot_value_" + to_string(i)); + unnest_val_expr->SetAlias(internal_value_alias); + value_aliases.emplace_back(internal_value_alias, unnest_name); + } else { + unnest_val_expr->SetAlias(unnest_name); + } result_node->select_list.push_back(std::move(unnest_val_expr)); - if (!ref.include_nulls) { - // if we are running with EXCLUDE NULLS we need to add an IS NOT NULL filter - auto colref = make_uniq(unnest_name); - auto filter = make_uniq(ExpressionType::OPERATOR_IS_NOT_NULL, std::move(colref)); - if (where_clause) { - where_clause = make_uniq(ExpressionType::CONJUNCTION_AND, - std::move(where_clause), std::move(filter)); - } else { - where_clause = std::move(filter); - } + } + if (!exclude_nulls) { + return result_node; + } + + // if we are running with EXCLUDE NULLS we need to add an IS NOT NULL filter on the unnested columns, + // and then restore the final column names in an outer projection + auto filtered_node = make_uniq(); + for (auto &passthrough_name : passthrough_names) { + filtered_node->select_list.push_back(make_uniq(passthrough_name)); + } + auto name_ref = make_uniq(internal_name_alias); + name_ref->SetAlias(unpivot.unpivot_names[0]); + filtered_node->select_list.push_back(std::move(name_ref)); + for (auto &value_alias : value_aliases) { + auto value_ref = make_uniq(value_alias.first); + value_ref->SetAlias(value_alias.second); + filtered_node->select_list.push_back(std::move(value_ref)); + + auto colref = make_uniq(value_alias.first); + auto filter = make_uniq(ExpressionType::OPERATOR_IS_NOT_NULL, std::move(colref)); + if (filtered_node->where_clause) { + filtered_node->where_clause = make_uniq( + ExpressionType::CONJUNCTION_AND, std::move(filtered_node->where_clause), std::move(filter)); + } else { + filtered_node->where_clause = std::move(filter); } } - return result_node; + auto inner_select = make_uniq(); + inner_select->node = std::move(result_node); + auto inner_ref = make_uniq(std::move(inner_select)); + inner_ref->alias = "unpivot_unnested"; + filtered_node->from_table = std::move(inner_ref); + return filtered_node; } BoundStatement Binder::Bind(PivotRef &ref) { diff --git a/src/duckdb/src/planner/binder/tableref/bind_table_function.cpp b/src/duckdb/src/planner/binder/tableref/bind_table_function.cpp index 0332bf655..fe943ea43 100644 --- a/src/duckdb/src/planner/binder/tableref/bind_table_function.cpp +++ b/src/duckdb/src/planner/binder/tableref/bind_table_function.cpp @@ -429,6 +429,9 @@ BoundStatement Binder::Bind(TableFunctionRef &ref) { if (func_catalog.type == CatalogType::TABLE_MACRO_ENTRY) { auto ¯o_func = func_catalog.Cast(); + if (!func_catalog.ParentCatalog().IsSystemCatalog()) { + GetStatementProperties().RegisterDBRead(func_catalog.ParentCatalog(), context); + } auto query_node = BindTableMacro(fexpr, macro_func, 0); D_ASSERT(query_node); diff --git a/src/duckdb/src/planner/collation_binding.cpp b/src/duckdb/src/planner/collation_binding.cpp index 9bad73fe6..3f5a3e09b 100644 --- a/src/duckdb/src/planner/collation_binding.cpp +++ b/src/duckdb/src/planner/collation_binding.cpp @@ -132,8 +132,22 @@ static bool PushStructCollation(ClientContext &context, unique_ptr & vector> fields; FunctionBinder function_binder(context); - bool requires_collation = false; auto &child_types = StructType::GetChildTypes(sql_type); + // check whether any field requires a collation first: binding the field extracts is linear in the number of + // fields per field + bool any_field_requires_collation = false; + for (auto &child_type : child_types) { + unique_ptr field_ref = make_uniq(child_type.second, idx_t(0)); + if (binding.PushCollation(context, field_ref, child_type.second, type)) { + any_field_requires_collation = true; + break; + } + } + if (!any_field_requires_collation) { + return false; + } + + bool requires_collation = false; for (idx_t i = 0; i < child_types.size(); i++) { vector> arguments; arguments.push_back(lambda_parameter_expr->Copy()); diff --git a/src/duckdb/src/storage/buffer/block_manager.cpp b/src/duckdb/src/storage/buffer/block_manager.cpp index e20b25bd3..fd22e13b2 100644 --- a/src/duckdb/src/storage/buffer/block_manager.cpp +++ b/src/duckdb/src/storage/buffer/block_manager.cpp @@ -37,7 +37,7 @@ shared_ptr BlockManager::TryGetBlock(block_id_t block_id) { return entry->second.lock(); } -shared_ptr BlockManager::RegisterBlock(block_id_t block_id) { +shared_ptr BlockManager::RegisterBlock(block_id_t block_id, MemoryTag tag) { lock_guard lock(blocks_lock); // check if the block already exists auto entry = blocks.find(block_id); @@ -46,11 +46,12 @@ shared_ptr BlockManager::RegisterBlock(block_id_t block_id) { auto existing_ptr = entry->second.lock(); if (existing_ptr) { //! it hasn't! return it + D_ASSERT(existing_ptr->GetMemory().GetMemoryTag() == tag); return existing_ptr; } } // create a new block pointer for this block - auto result = make_shared_ptr(*this, block_id, MemoryTag::BASE_TABLE); + auto result = make_shared_ptr(*this, block_id, tag); // register the block pointer in the set of blocks as a weak pointer blocks[block_id] = weak_ptr(result); return result; @@ -58,9 +59,9 @@ shared_ptr BlockManager::RegisterBlock(block_id_t block_id) { shared_ptr BlockManager::ConvertToPersistent(QueryContext context, block_id_t block_id, shared_ptr old_block, BufferHandle old_handle, - ConvertToPersistentMode mode) { + ConvertToPersistentMode mode, optional tag) { // register a block with the new block id - auto new_block = RegisterBlock(block_id); + auto new_block = RegisterBlock(block_id, tag.value_or(old_block->GetMemory().GetMemoryTag())); D_ASSERT(new_block->GetMemory().GetState() == BlockState::BLOCK_UNLOADED); D_ASSERT(new_block->GetMemory().GetReaders() == 0); @@ -119,10 +120,10 @@ shared_ptr BlockManager::ConvertToPersistent(QueryContext context, shared_ptr BlockManager::ConvertToPersistent(QueryContext context, block_id_t block_id, shared_ptr old_block, - ConvertToPersistentMode mode) { + ConvertToPersistentMode mode, optional tag) { // pin the old block to ensure we have it loaded in memory auto handle = buffer_manager.Pin(old_block); - return ConvertToPersistent(context, block_id, std::move(old_block), std::move(handle), mode); + return ConvertToPersistent(context, block_id, std::move(old_block), std::move(handle), mode, tag); } void BlockManager::UnregisterBlock(block_id_t id) { diff --git a/src/duckdb/src/storage/checkpoint/write_overflow_strings_to_disk.cpp b/src/duckdb/src/storage/checkpoint/write_overflow_strings_to_disk.cpp index 854cb30cb..ffaf0d9bb 100644 --- a/src/duckdb/src/storage/checkpoint/write_overflow_strings_to_disk.cpp +++ b/src/duckdb/src/storage/checkpoint/write_overflow_strings_to_disk.cpp @@ -37,7 +37,7 @@ shared_ptr UncompressedStringSegmentState::GetHandle(BlockManager & throw DataCorruptionException( "Corrupted uncompressed string segment: overflow string block is not owned by the segment"); } - auto result = manager.RegisterBlock(block_id); + auto result = manager.RegisterBlock(block_id, MemoryTag::OVERFLOW_STRINGS); handles.insert(make_pair(block_id, result)); return result; } @@ -50,7 +50,7 @@ void UncompressedStringSegmentState::RegisterBlock(BlockManager &manager_p, bloc block_id); } auto &manager = block_manager ? *block_manager : manager_p; - auto result = manager.RegisterBlock(block_id); + auto result = manager.RegisterBlock(block_id, MemoryTag::OVERFLOW_STRINGS); handles.insert(make_pair(block_id, std::move(result))); on_disk_blocks.push_back(block_id); on_disk_block_set.insert(block_id); diff --git a/src/duckdb/src/storage/metadata/metadata_manager.cpp b/src/duckdb/src/storage/metadata/metadata_manager.cpp index b465283b2..410b39f5b 100644 --- a/src/duckdb/src/storage/metadata/metadata_manager.cpp +++ b/src/duckdb/src/storage/metadata/metadata_manager.cpp @@ -203,7 +203,7 @@ void MetadataManager::AddAndRegisterBlock(unique_lock &block_lock, Metada throw InternalException("AddAndRegisterBlock called with a transient block id"); } block_lock.unlock(); - block.block = block_manager.RegisterBlock(block.block_id); + block.block = block_manager.RegisterBlock(block.block_id, MemoryTag::METADATA); block_lock.lock(); AddBlock(block_lock, std::move(block), true); } diff --git a/src/duckdb/src/storage/storage_manager.cpp b/src/duckdb/src/storage/storage_manager.cpp index 2c7c650e4..f70c1f1d7 100644 --- a/src/duckdb/src/storage/storage_manager.cpp +++ b/src/duckdb/src/storage/storage_manager.cpp @@ -143,7 +143,11 @@ void StorageOptions::Initialize(unordered_map &options) { entry.second.ToString()); } } else if (entry.first == "mmap_reserve_size") { - mmap_reserve_size = DBConfig::ParseMemoryLimitOrMaximum(entry.second.ToString()); + const auto reserve_size = DBConfig::ParseMemoryLimitOrMaximum(entry.second.ToString()); + if (reserve_size > NumericCast(NumericLimits::Maximum())) { + throw BinderException("MMAP_RESERVE_SIZE \"%s\" is out of range", entry.second.ToString()); + } + mmap_reserve_size = reserve_size; } else { throw BinderException("Unrecognized option for attach \"%s\"", entry.first); } diff --git a/src/duckdb/src/storage/table/column_segment.cpp b/src/duckdb/src/storage/table/column_segment.cpp index 641f4deef..2757a46e6 100644 --- a/src/duckdb/src/storage/table/column_segment.cpp +++ b/src/duckdb/src/storage/table/column_segment.cpp @@ -266,7 +266,9 @@ void ColumnSegment::ConvertToPersistent(QueryContext context, optional_ptrConvertToPersistent(context, block_id, std::move(block)); + // transient segments are tagged IN_MEMORY_TABLE, once written they are part of the base table + block = block_manager->ConvertToPersistent(context, block_id, std::move(block), + ConvertToPersistentMode::DESTRUCTIVE, MemoryTag::BASE_TABLE); return; } diff --git a/src/duckdb/src/storage/temporary_file_manager.cpp b/src/duckdb/src/storage/temporary_file_manager.cpp index 87d638399..e4e96a750 100644 --- a/src/duckdb/src/storage/temporary_file_manager.cpp +++ b/src/duckdb/src/storage/temporary_file_manager.cpp @@ -645,18 +645,19 @@ To get usage information of the temp_directory, use 'CALL duckdb_temporary_files } void TemporaryFileManager::IncreaseSizeOnDisk(idx_t bytes) { - auto current_size_on_disk = GetTotalUsedSpaceInBytes(); - if (current_size_on_disk + bytes > max_swap_space) { - auto used = StringUtil::BytesToHumanReadableString(current_size_on_disk); - auto max = StringUtil::BytesToHumanReadableString(max_swap_space); - auto data_size = StringUtil::BytesToHumanReadableString(bytes); - throw OutOfMemoryException(R"(failed to offload data block of size %s (%s/%s used). + auto current_size_on_disk = size_on_disk.load(); + do { + if (current_size_on_disk + bytes > max_swap_space) { + auto used = StringUtil::BytesToHumanReadableString(current_size_on_disk); + auto max = StringUtil::BytesToHumanReadableString(max_swap_space); + auto data_size = StringUtil::BytesToHumanReadableString(bytes); + throw OutOfMemoryException(R"(failed to offload data block of size %s (%s/%s used). This limit was set by the 'max_temp_directory_size' setting. By default, this setting utilizes the available disk space on the drive where the 'temp_directory' is located. You can adjust this setting, by using (for example) PRAGMA max_temp_directory_size='10GiB')", - data_size, used, max); - } - size_on_disk += bytes; + data_size, used, max); + } + } while (!size_on_disk.compare_exchange_weak(current_size_on_disk, current_size_on_disk + bytes)); } void TemporaryFileManager::DecreaseSizeOnDisk(idx_t bytes) { diff --git a/src/duckdb/ub_src_optimizer.cpp b/src/duckdb/ub_src_optimizer.cpp index 41becf040..b02cc1baa 100644 --- a/src/duckdb/ub_src_optimizer.cpp +++ b/src/duckdb/ub_src_optimizer.cpp @@ -32,6 +32,8 @@ #include "src/optimizer/deliminator.cpp" +#include "src/optimizer/disjunctive_join_rewriter.cpp" + #include "src/optimizer/empty_result_pullup.cpp" #include "src/optimizer/expression_heuristics.cpp" From 6c2118cdd9fc9b79666214ed72c2643c50f0e81d Mon Sep 17 00:00:00 2001 From: DuckLabs GitHub Bot Date: Fri, 16 Oct 2026 17:22:50 +0000 Subject: [PATCH 3/6] Update vendored DuckDB sources to e1ecb5b8877 --- src/duckdb/generated_extension_loader_package_build.cpp | 8 ++++---- src/duckdb/src/function/table/version/pragma_version.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/duckdb/generated_extension_loader_package_build.cpp b/src/duckdb/generated_extension_loader_package_build.cpp index 2b8cd6353..21dccb4d5 100644 --- a/src/duckdb/generated_extension_loader_package_build.cpp +++ b/src/duckdb/generated_extension_loader_package_build.cpp @@ -45,7 +45,7 @@ int32_t duckdb_extension_core_functions_describe(duckdb_extension_descriptor *de descriptor->version = 1; descriptor->name = "core_functions"; descriptor->extension_version = EXT_VERSION_CORE_FUNCTIONS; - descriptor->api_version = "v2.1.0-alpha48623"; + descriptor->api_version = "v2.1.0-alpha48629"; descriptor->entry_cpp = (void (*)(void))core_functions_duckdb_cpp_init; return 0; } @@ -78,7 +78,7 @@ int32_t duckdb_extension_parquet_describe(duckdb_extension_descriptor *descripto descriptor->version = 1; descriptor->name = "parquet"; descriptor->extension_version = EXT_VERSION_PARQUET; - descriptor->api_version = "v2.1.0-alpha48623"; + descriptor->api_version = "v2.1.0-alpha48629"; descriptor->entry_cpp = (void (*)(void))parquet_duckdb_cpp_init; return 0; } @@ -111,7 +111,7 @@ int32_t duckdb_extension_icu_describe(duckdb_extension_descriptor *descriptor) { descriptor->version = 1; descriptor->name = "icu"; descriptor->extension_version = EXT_VERSION_ICU; - descriptor->api_version = "v2.1.0-alpha48623"; + descriptor->api_version = "v2.1.0-alpha48629"; descriptor->entry_cpp = (void (*)(void))icu_duckdb_cpp_init; return 0; } @@ -144,7 +144,7 @@ int32_t duckdb_extension_json_describe(duckdb_extension_descriptor *descriptor) descriptor->version = 1; descriptor->name = "json"; descriptor->extension_version = EXT_VERSION_JSON; - descriptor->api_version = "v2.1.0-alpha48623"; + descriptor->api_version = "v2.1.0-alpha48629"; descriptor->entry_cpp = (void (*)(void))json_duckdb_cpp_init; return 0; } diff --git a/src/duckdb/src/function/table/version/pragma_version.cpp b/src/duckdb/src/function/table/version/pragma_version.cpp index 5125dbf1b..63dca7d28 100644 --- a/src/duckdb/src/function/table/version/pragma_version.cpp +++ b/src/duckdb/src/function/table/version/pragma_version.cpp @@ -1,5 +1,5 @@ #ifndef DUCKDB_PATCH_VERSION -#define DUCKDB_PATCH_VERSION "0-alpha48623" +#define DUCKDB_PATCH_VERSION "0-alpha48629" #endif #ifndef DUCKDB_MINOR_VERSION #define DUCKDB_MINOR_VERSION 1 @@ -8,7 +8,7 @@ #define DUCKDB_MAJOR_VERSION 2 #endif #ifndef DUCKDB_VERSION -#define DUCKDB_VERSION "v2.1.0-alpha48623" +#define DUCKDB_VERSION "v2.1.0-alpha48629" #endif #ifndef DUCKDB_SOURCE_ID #define DUCKDB_SOURCE_ID "e1ecb5b887" From 164317978e9fdec14fda82b1056bf97e5b08636a Mon Sep 17 00:00:00 2001 From: DuckLabs GitHub Bot Date: Sat, 17 Oct 2026 02:56:47 +0000 Subject: [PATCH 4/6] Update vendored DuckDB sources to 1b4c3e54ba3 --- .../extension/parquet/column_writer.cpp | 4 +- ...nerated_extension_loader_package_build.cpp | 8 ++-- .../function/table/version/pragma_version.cpp | 6 +-- .../src/include/duckdb/common/type_util.hpp | 3 +- .../common/types/interval_no_normalize.hpp | 39 +++++++++++++++++++ .../parser/peg/grammar_literal_table.hpp | 4 ++ .../include/duckdb/parser/token_iterator.hpp | 23 ++++++++--- src/duckdb/src/optimizer/join_elimination.cpp | 9 +++++ src/duckdb/src/parser/parser.cpp | 8 ++-- .../keyword_helper/grammar_literal_table.cpp | 1 + src/duckdb/src/parser/peg/matcher_stack.cpp | 4 +- .../parser/peg/tokenizer/base_tokenizer.cpp | 39 ++++++++++++++++++- src/duckdb/src/parser/token_iterator.cpp | 23 ----------- 13 files changed, 128 insertions(+), 43 deletions(-) create mode 100644 src/duckdb/src/include/duckdb/common/types/interval_no_normalize.hpp diff --git a/src/duckdb/extension/parquet/column_writer.cpp b/src/duckdb/extension/parquet/column_writer.cpp index fbe5e7108..6aa058397 100644 --- a/src/duckdb/extension/parquet/column_writer.cpp +++ b/src/duckdb/extension/parquet/column_writer.cpp @@ -6,6 +6,7 @@ #include #include +#include "duckdb/common/types/interval_no_normalize.hpp" #include "parquet_writer.hpp" #include "writer/array_column_writer.hpp" #include "writer/boolean_column_writer.hpp" @@ -555,7 +556,8 @@ unique_ptr ColumnWriter::CreateWriterRecursive(ClientContext &cont return make_uniq>( writer, std::move(schema), path_in_schema); case LogicalTypeId::INTERVAL: - return make_uniq>( + return make_uniq< + StandardColumnWriter>( writer, std::move(schema), path_in_schema); case LogicalTypeId::ENUM: return make_uniq(writer, std::move(schema), path_in_schema); diff --git a/src/duckdb/generated_extension_loader_package_build.cpp b/src/duckdb/generated_extension_loader_package_build.cpp index 21dccb4d5..f82f99e39 100644 --- a/src/duckdb/generated_extension_loader_package_build.cpp +++ b/src/duckdb/generated_extension_loader_package_build.cpp @@ -45,7 +45,7 @@ int32_t duckdb_extension_core_functions_describe(duckdb_extension_descriptor *de descriptor->version = 1; descriptor->name = "core_functions"; descriptor->extension_version = EXT_VERSION_CORE_FUNCTIONS; - descriptor->api_version = "v2.1.0-alpha48629"; + descriptor->api_version = "v2.1.0-alpha48729"; descriptor->entry_cpp = (void (*)(void))core_functions_duckdb_cpp_init; return 0; } @@ -78,7 +78,7 @@ int32_t duckdb_extension_parquet_describe(duckdb_extension_descriptor *descripto descriptor->version = 1; descriptor->name = "parquet"; descriptor->extension_version = EXT_VERSION_PARQUET; - descriptor->api_version = "v2.1.0-alpha48629"; + descriptor->api_version = "v2.1.0-alpha48729"; descriptor->entry_cpp = (void (*)(void))parquet_duckdb_cpp_init; return 0; } @@ -111,7 +111,7 @@ int32_t duckdb_extension_icu_describe(duckdb_extension_descriptor *descriptor) { descriptor->version = 1; descriptor->name = "icu"; descriptor->extension_version = EXT_VERSION_ICU; - descriptor->api_version = "v2.1.0-alpha48629"; + descriptor->api_version = "v2.1.0-alpha48729"; descriptor->entry_cpp = (void (*)(void))icu_duckdb_cpp_init; return 0; } @@ -144,7 +144,7 @@ int32_t duckdb_extension_json_describe(duckdb_extension_descriptor *descriptor) descriptor->version = 1; descriptor->name = "json"; descriptor->extension_version = EXT_VERSION_JSON; - descriptor->api_version = "v2.1.0-alpha48629"; + descriptor->api_version = "v2.1.0-alpha48729"; descriptor->entry_cpp = (void (*)(void))json_duckdb_cpp_init; return 0; } diff --git a/src/duckdb/src/function/table/version/pragma_version.cpp b/src/duckdb/src/function/table/version/pragma_version.cpp index 63dca7d28..89ad3af2b 100644 --- a/src/duckdb/src/function/table/version/pragma_version.cpp +++ b/src/duckdb/src/function/table/version/pragma_version.cpp @@ -1,5 +1,5 @@ #ifndef DUCKDB_PATCH_VERSION -#define DUCKDB_PATCH_VERSION "0-alpha48629" +#define DUCKDB_PATCH_VERSION "0-alpha48729" #endif #ifndef DUCKDB_MINOR_VERSION #define DUCKDB_MINOR_VERSION 1 @@ -8,10 +8,10 @@ #define DUCKDB_MAJOR_VERSION 2 #endif #ifndef DUCKDB_VERSION -#define DUCKDB_VERSION "v2.1.0-alpha48629" +#define DUCKDB_VERSION "v2.1.0-alpha48729" #endif #ifndef DUCKDB_SOURCE_ID -#define DUCKDB_SOURCE_ID "e1ecb5b887" +#define DUCKDB_SOURCE_ID "1b4c3e54ba" #endif #include "duckdb/function/table/system_functions.hpp" #include "duckdb/main/database.hpp" diff --git a/src/duckdb/src/include/duckdb/common/type_util.hpp b/src/duckdb/src/include/duckdb/common/type_util.hpp index aebaae653..2c221f5cf 100644 --- a/src/duckdb/src/include/duckdb/common/type_util.hpp +++ b/src/duckdb/src/include/duckdb/common/type_util.hpp @@ -11,6 +11,7 @@ #include "duckdb/common/types.hpp" #include "duckdb/common/types/date.hpp" #include "duckdb/common/types/datetime.hpp" +#include "duckdb/common/types/interval_no_normalize.hpp" #include "duckdb/common/types/timestamp.hpp" #include "duckdb/common/types/interval.hpp" #include "duckdb/common/uhugeint.hpp" @@ -87,7 +88,7 @@ PhysicalType GetTypeId() { } else if (std::is_same() || std::is_same() || std::is_same() || std::is_same()) { return PhysicalType::VARCHAR; - } else if (std::is_same()) { + } else if (std::is_same() || std::is_same()) { return PhysicalType::INTERVAL; } else if (std::is_same()) { return PhysicalType::LIST; diff --git a/src/duckdb/src/include/duckdb/common/types/interval_no_normalize.hpp b/src/duckdb/src/include/duckdb/common/types/interval_no_normalize.hpp new file mode 100644 index 000000000..a07f8cb7b --- /dev/null +++ b/src/duckdb/src/include/duckdb/common/types/interval_no_normalize.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include "duckdb/common/types/hash.hpp" +#include "duckdb/common/types/interval.hpp" + +namespace duckdb { + +//! Dictionary-encoding wrapper for interval_t. interval_t's operators and hash normalize +//! months/days/micros first, so two spellings that are equal only after normalization land +//! in the same dictionary entry and a Parquet round trip rewrites one as the other. +struct interval_no_normalize { + int32_t months; + int32_t days; + int64_t micros; + + interval_no_normalize() : months(0), days(0), micros(0) { + } + explicit interval_no_normalize(const interval_t &val) : months(val.months), days(val.days), micros(val.micros) { + } + // NOLINTNEXTLINE: allow implicit conversion to interval_t + operator interval_t() const { + return interval_t {months, days, micros}; + } + + bool operator==(const interval_no_normalize &right) const { + return months == right.months && days == right.days && micros == right.micros; + } + bool operator!=(const interval_no_normalize &right) const { + return !(*this == right); + } +}; + +//! Deliberately not normalized: distinct (months, days, micros) are distinct keys, so a +//! Parquet dictionary does not merge intervals that are equal only after normalization. +inline hash_t Hash(interval_no_normalize val) { + return Hash(val.months) ^ Hash(val.days) ^ Hash(val.micros); +} + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/grammar_literal_table.hpp b/src/duckdb/src/include/duckdb/parser/peg/grammar_literal_table.hpp index 71989138d..bf077ee12 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/grammar_literal_table.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/grammar_literal_table.hpp @@ -26,6 +26,9 @@ class GrammarLiteralTable { } LiteralInfo Lookup(const string &text) const { + if (text.size() > max_literal_length) { + return LiteralInfo(); + } auto entry = literals.find(text); return entry == literals.end() ? LiteralInfo() : entry->second; } @@ -35,6 +38,7 @@ class GrammarLiteralTable { private: const uint64_t cache_id; + idx_t max_literal_length = 0; case_insensitive_map_t literals; }; diff --git a/src/duckdb/src/include/duckdb/parser/token_iterator.hpp b/src/duckdb/src/include/duckdb/parser/token_iterator.hpp index 8e72ee327..f7256d685 100644 --- a/src/duckdb/src/include/duckdb/parser/token_iterator.hpp +++ b/src/duckdb/src/include/duckdb/parser/token_iterator.hpp @@ -22,18 +22,31 @@ class TokenIterator { public: DUCKDB_API explicit TokenIterator(unique_ptr> owned_tokens); DUCKDB_API explicit TokenIterator(vector &tokens); - DUCKDB_API TokenIterator(const TokenIterator &other); + DUCKDB_API TokenIterator(const TokenIterator &other) : tokens(other.tokens), position(other.position) { + } DUCKDB_API TokenIterator(TokenIterator &&other) noexcept; TokenIterator &operator=(const TokenIterator &) = delete; TokenIterator &operator=(TokenIterator &&) = delete; - DUCKDB_API bool AtEnd() const; + DUCKDB_API bool AtEnd() const { + auto current = Current(); + return !current || current->type == TokenType::END_OF_INPUT; + } DUCKDB_API bool HasMoreStatements() const; - DUCKDB_API idx_t Position() const; - DUCKDB_API idx_t Size() const; + DUCKDB_API idx_t Position() const { + return position; + } + DUCKDB_API idx_t Size() const { + return tokens.size(); + } DUCKDB_API idx_t EndOffset() const; - DUCKDB_API optional_ptr Current() const; + DUCKDB_API optional_ptr Current() const { + if (position >= tokens.size()) { + return nullptr; + } + return tokens[position]; + } LiteralInfo CurrentLiteralInfo(const GrammarLiteralTable &table) { if (position >= tokens.size()) { return LiteralInfo(); diff --git a/src/duckdb/src/optimizer/join_elimination.cpp b/src/duckdb/src/optimizer/join_elimination.cpp index 2c7f42371..079f05fea 100644 --- a/src/duckdb/src/optimizer/join_elimination.cpp +++ b/src/duckdb/src/optimizer/join_elimination.cpp @@ -115,6 +115,15 @@ void JoinElimination::OptimizeChildren(LogicalOperator &op, optional_ptr(); if (get.table_filters.HasFilters()) { diff --git a/src/duckdb/src/parser/parser.cpp b/src/duckdb/src/parser/parser.cpp index 9ed616981..f3a5d7f96 100644 --- a/src/duckdb/src/parser/parser.cpp +++ b/src/duckdb/src/parser/parser.cpp @@ -75,12 +75,12 @@ static bool IsValidDollarQuotedStringTagSubsequentChar(const unsigned char &c) { //! Throw a ParserException if `query` contains invalid UTF-8, so the tokenizer never reads past //! bad bytes (a bad-byte query can otherwise recurse the tokenizer — see ossfuzz clusterfuzz-test-24). -static void ValidateUTF8Query(const string &query) { +static UnicodeType ValidateUTF8Query(const string &query) { UnicodeInvalidReason reason = UnicodeInvalidReason::INVALID_UNICODE; size_t invalid_pos = 0; auto unicode_type = Utf8Proc::Analyze(query.c_str(), query.size(), &reason, &invalid_pos); if (unicode_type != UnicodeType::INVALID) { - return; + return unicode_type; } const char *reason_str = reason == UnicodeInvalidReason::BYTE_MISMATCH ? "byte sequence mismatch" : "invalid unicode"; @@ -239,7 +239,9 @@ void Parser::ThrowParserOverrideError(ParserOverrideResult &result) { string Parser::NormalizeSQLString(const string &query) { // Validate before strip: StripUnicodeSpaces walks multi-byte sequences and assumes valid UTF-8. - ValidateUTF8Query(query); + if (ValidateUTF8Query(query) == UnicodeType::ASCII) { + return query; + } string normalized; if (StripUnicodeSpaces(query, normalized)) { return normalized; diff --git a/src/duckdb/src/parser/peg/keyword_helper/grammar_literal_table.cpp b/src/duckdb/src/parser/peg/keyword_helper/grammar_literal_table.cpp index dfec0c26a..c22c12a12 100644 --- a/src/duckdb/src/parser/peg/keyword_helper/grammar_literal_table.cpp +++ b/src/duckdb/src/parser/peg/keyword_helper/grammar_literal_table.cpp @@ -43,6 +43,7 @@ void GrammarLiteralTable::Register(const string &text, keyword_categories_t cate } auto id = static_cast(literals.size() + 1); entry = literals.emplace(text, LiteralInfo(id)).first; + max_literal_length = MaxValue(max_literal_length, text.size()); } entry->second.AddCategories(categories); } diff --git a/src/duckdb/src/parser/peg/matcher_stack.cpp b/src/duckdb/src/parser/peg/matcher_stack.cpp index 16653c385..e03f081f4 100644 --- a/src/duckdb/src/parser/peg/matcher_stack.cpp +++ b/src/duckdb/src/parser/peg/matcher_stack.cpp @@ -5,7 +5,6 @@ namespace duckdb { MatchStack::MatchStack() { - frames.reserve(INITIAL_FRAME_CAPACITY); } MatchStack::~MatchStack() { @@ -117,7 +116,7 @@ bool MatchStack::ExecuteFrame(MatchStackFrame &frame) { frame.result = step.GetResult(); return true; } - if (child->matcher.IsAtomic()) { + if (!child->matcher.IsStructural() && child->matcher.IsAtomic()) { frame.child_result = ExecuteAtomicMatcher(*child); return false; } @@ -156,6 +155,7 @@ MatcherResult MatchStack::Execute(MatchInput input) { if (input.matcher.IsAtomic()) { return ExecuteAtomicMatcher(input); } + frames.reserve(INITIAL_FRAME_CAPACITY); PushFrame(input); while (!frames.empty()) { if (!ExecuteFrame(frames.back())) { diff --git a/src/duckdb/src/parser/peg/tokenizer/base_tokenizer.cpp b/src/duckdb/src/parser/peg/tokenizer/base_tokenizer.cpp index 9f8df5442..57e69d9cf 100644 --- a/src/duckdb/src/parser/peg/tokenizer/base_tokenizer.cpp +++ b/src/duckdb/src/parser/peg/tokenizer/base_tokenizer.cpp @@ -305,6 +305,33 @@ void Tokenizer::PushOperatorToken(TokenizerBehavior &behavior, idx_t start, idx_ } } +//! Skip an ordinary run, leaving its first special byte (or the final input byte) to the state machine. +static idx_t SkipTokenizerBytes(const string &sql, idx_t pos, char first, char second) { + D_ASSERT(pos < sql.size()); + const auto end = sql.size() - 1; + // Most tokens are short; avoid setting up a block scan for them. + for (idx_t count = 0; count < 8 && pos < end; count++, pos++) { + if (sql[pos] == first || sql[pos] == second) { + return pos; + } + } + while (end - pos >= 32) { + uint8_t found = 0; + for (idx_t offset = 0; offset < 32; offset++) { + auto c = sql[pos + offset]; + found |= c == first || c == second; + } + if (found) { + break; + } + pos += 32; + } + while (pos < end && sql[pos] != first && sql[pos] != second) { + pos++; + } + return pos; +} + bool Tokenizer::TokenizeInputInternal(TokenizerBehavior &behavior) const { auto &sql = behavior.sql; auto &tokens = behavior.tokens; @@ -519,7 +546,9 @@ bool Tokenizer::TokenizeInputInternal(TokenizerBehavior &behavior) const { } break; case TokenizeState::STRING_LITERAL: - if ((escape_string || BackslashEscapesStringLiterals()) && c == '\\' && i + 1 < sql.size()) { + i = SkipTokenizerBytes(sql, i, '\'', '\\'); + c = sql[i]; + if (c == '\\' && i + 1 < sql.size() && (escape_string || BackslashEscapesStringLiterals())) { i++; break; } @@ -536,6 +565,8 @@ bool Tokenizer::TokenizeInputInternal(TokenizerBehavior &behavior) const { } break; case TokenizeState::QUOTED_IDENTIFIER: + i = SkipTokenizerBytes(sql, i, quoted_identifier_delimiter, quoted_identifier_delimiter); + c = sql[i]; if (c == quoted_identifier_delimiter) { if (i + 1 < sql.size() && sql[i + 1] == quoted_identifier_delimiter) { // escaped - skip escape @@ -548,6 +579,8 @@ bool Tokenizer::TokenizeInputInternal(TokenizerBehavior &behavior) const { } break; case TokenizeState::SINGLE_LINE_COMMENT: + i = SkipTokenizerBytes(sql, i, '\n', '\r'); + c = sql[i]; if (c == '\n' || c == '\r') { behavior.PushToken(last_pos, i + 1, TokenType::COMMENT); last_pos = i + 1; @@ -555,6 +588,8 @@ bool Tokenizer::TokenizeInputInternal(TokenizerBehavior &behavior) const { } break; case TokenizeState::MULTI_LINE_COMMENT: + i = SkipTokenizerBytes(sql, i, '/', '*'); + c = sql[i]; if (c == '/' && i + 1 < sql.size() && sql[i + 1] == '*') { i++; multi_line_comment_depth++; @@ -569,6 +604,8 @@ bool Tokenizer::TokenizeInputInternal(TokenizerBehavior &behavior) const { } break; case TokenizeState::DOLLAR_QUOTED_STRING: { + i = SkipTokenizerBytes(sql, i, '$', '$'); + c = sql[i]; // Dollar-quoted string -- all that will get us out is a $[marker]$ if (c != '$') { break; diff --git a/src/duckdb/src/parser/token_iterator.cpp b/src/duckdb/src/parser/token_iterator.cpp index 7406e0029..027226fed 100644 --- a/src/duckdb/src/parser/token_iterator.cpp +++ b/src/duckdb/src/parser/token_iterator.cpp @@ -22,18 +22,10 @@ TokenIterator::TokenIterator(vector &tokens_p) : tokens(tokens_p) } } -TokenIterator::TokenIterator(const TokenIterator &other) : tokens(other.tokens), position(other.position) { -} - TokenIterator::TokenIterator(TokenIterator &&other) noexcept : owned_tokens(std::move(other.owned_tokens)), tokens(other.tokens), position(other.position) { } -bool TokenIterator::AtEnd() const { - auto current = Current(); - return !current || current->type == TokenType::END_OF_INPUT; -} - bool TokenIterator::HasMoreStatements() const { for (idx_t index = position; index < tokens.size(); index++) { auto type = tokens[index].type; @@ -47,14 +39,6 @@ bool TokenIterator::HasMoreStatements() const { return false; } -idx_t TokenIterator::Position() const { - return position; -} - -idx_t TokenIterator::Size() const { - return tokens.size(); -} - idx_t TokenIterator::EndOffset() const { if (tokens.empty()) { return 0; @@ -63,13 +47,6 @@ idx_t TokenIterator::EndOffset() const { return last_token.offset + last_token.length; } -optional_ptr TokenIterator::Current() const { - if (position >= tokens.size()) { - return nullptr; - } - return tokens[position]; -} - const MatcherToken &TokenIterator::Previous() const { if (position == 0) { throw InternalException("TokenIterator has no previous token"); From 7e81c4cd1705fb9655996050969037d43a0a1e0d Mon Sep 17 00:00:00 2001 From: DuckLabs GitHub Bot Date: Mon, 19 Oct 2026 02:55:31 +0000 Subject: [PATCH 5/6] Update vendored DuckDB sources to 16917f94f9e --- .../core_functions/scalar/date/date_trunc.cpp | 2 +- ...nerated_extension_loader_package_build.cpp | 8 +- .../operator/join/physical_asof_join.cpp | 3 +- .../function/table/version/pragma_version.cpp | 6 +- .../duckdb/common/sorting/buffered_merge.hpp | 5 +- .../capi_v2/capi_v2_function_internal.hpp | 30 ++ .../duckdb/main/capi_v2/extension_api_v2.hpp | 71 +++ src/duckdb/src/include/duckdb/main/config.hpp | 2 + .../src/include/duckdb/main/database.hpp | 3 + .../duckdb/main/http/http_retry_budget.hpp | 2 +- .../src/include/duckdb/main/os_util.hpp | 54 ++ src/duckdb/src/include/duckdb_extension_v2.h | 114 ++++ src/duckdb/src/include/duckdb_v2.h | 497 ++++++++++++++++++ .../main/capi/v2/capi_v2_func_aggregate.cpp | 119 +++++ .../src/main/capi/v2/capi_v2_func_scalar.cpp | 119 +++++ .../src/main/capi/v2/capi_v2_func_table.cpp | 90 ++++ src/duckdb/src/main/capi/v2/capi_v2_stats.cpp | 51 ++ src/duckdb/src/main/client_context.cpp | 2 +- src/duckdb/src/main/config.cpp | 3 +- src/duckdb/src/main/database.cpp | 7 + src/duckdb/src/main/os_util.cpp | 106 ++++ .../parser/expression/operator_expression.cpp | 63 +++ src/duckdb/src/storage/data_table.cpp | 8 + .../serialize_parsed_expression.cpp | 11 - src/duckdb/ub_src_main.cpp | 2 + src/duckdb/ub_src_main_capi_v2.cpp | 2 + 26 files changed, 1354 insertions(+), 26 deletions(-) create mode 100644 src/duckdb/src/include/duckdb/main/os_util.hpp create mode 100644 src/duckdb/src/main/capi/v2/capi_v2_stats.cpp create mode 100644 src/duckdb/src/main/os_util.cpp diff --git a/src/duckdb/extension/core_functions/scalar/date/date_trunc.cpp b/src/duckdb/extension/core_functions/scalar/date/date_trunc.cpp index 7547113cd..af1bc0b62 100644 --- a/src/duckdb/extension/core_functions/scalar/date/date_trunc.cpp +++ b/src/duckdb/extension/core_functions/scalar/date/date_trunc.cpp @@ -84,7 +84,7 @@ struct DateTrunc { template static inline TR Operation(TA input) { date_t date = Date::GetMondayOfCurrentWeek(input); - return Date::SubtractDays(date, (Date::ExtractISOWeekNumber(date) - 1) * Interval::DAYS_PER_WEEK); + return Date::SubtractDays(date, int64_t(Date::ExtractISOWeekNumber(date) - 1) * Interval::DAYS_PER_WEEK); } }; diff --git a/src/duckdb/generated_extension_loader_package_build.cpp b/src/duckdb/generated_extension_loader_package_build.cpp index f82f99e39..092026dbb 100644 --- a/src/duckdb/generated_extension_loader_package_build.cpp +++ b/src/duckdb/generated_extension_loader_package_build.cpp @@ -45,7 +45,7 @@ int32_t duckdb_extension_core_functions_describe(duckdb_extension_descriptor *de descriptor->version = 1; descriptor->name = "core_functions"; descriptor->extension_version = EXT_VERSION_CORE_FUNCTIONS; - descriptor->api_version = "v2.1.0-alpha48729"; + descriptor->api_version = "v2.1.0-alpha48840"; descriptor->entry_cpp = (void (*)(void))core_functions_duckdb_cpp_init; return 0; } @@ -78,7 +78,7 @@ int32_t duckdb_extension_parquet_describe(duckdb_extension_descriptor *descripto descriptor->version = 1; descriptor->name = "parquet"; descriptor->extension_version = EXT_VERSION_PARQUET; - descriptor->api_version = "v2.1.0-alpha48729"; + descriptor->api_version = "v2.1.0-alpha48840"; descriptor->entry_cpp = (void (*)(void))parquet_duckdb_cpp_init; return 0; } @@ -111,7 +111,7 @@ int32_t duckdb_extension_icu_describe(duckdb_extension_descriptor *descriptor) { descriptor->version = 1; descriptor->name = "icu"; descriptor->extension_version = EXT_VERSION_ICU; - descriptor->api_version = "v2.1.0-alpha48729"; + descriptor->api_version = "v2.1.0-alpha48840"; descriptor->entry_cpp = (void (*)(void))icu_duckdb_cpp_init; return 0; } @@ -144,7 +144,7 @@ int32_t duckdb_extension_json_describe(duckdb_extension_descriptor *descriptor) descriptor->version = 1; descriptor->name = "json"; descriptor->extension_version = EXT_VERSION_JSON; - descriptor->api_version = "v2.1.0-alpha48729"; + descriptor->api_version = "v2.1.0-alpha48840"; descriptor->entry_cpp = (void (*)(void))json_duckdb_cpp_init; return 0; } diff --git a/src/duckdb/src/execution/operator/join/physical_asof_join.cpp b/src/duckdb/src/execution/operator/join/physical_asof_join.cpp index dc2e967f2..7584ab8da 100644 --- a/src/duckdb/src/execution/operator/join/physical_asof_join.cpp +++ b/src/duckdb/src/execution/operator/join/physical_asof_join.cpp @@ -123,8 +123,7 @@ AsOfGlobalSinkState::AsOfGlobalSinkState(ClientContext &client, const PhysicalAs const vector> partitions_stats; auto &lhs = op.children[0].get(); OperatorPartitionInfo empty_info; - const OperatorPartitionInfo &lhs_partition_info = - (op.partition_infos.size() > 0) ? op.partition_infos[0] : empty_info; + const OperatorPartitionInfo &lhs_partition_info = !op.partition_infos.empty() ? op.partition_infos[0] : empty_info; sort_strategies[0] = SortStrategy::Factory(client, op.lhs_partitions, op.lhs_orders, lhs.GetTypes(), partitions_stats, lhs_partition_info, lhs.estimated_cardinality, true); diff --git a/src/duckdb/src/function/table/version/pragma_version.cpp b/src/duckdb/src/function/table/version/pragma_version.cpp index 89ad3af2b..7701f2143 100644 --- a/src/duckdb/src/function/table/version/pragma_version.cpp +++ b/src/duckdb/src/function/table/version/pragma_version.cpp @@ -1,5 +1,5 @@ #ifndef DUCKDB_PATCH_VERSION -#define DUCKDB_PATCH_VERSION "0-alpha48729" +#define DUCKDB_PATCH_VERSION "0-alpha48840" #endif #ifndef DUCKDB_MINOR_VERSION #define DUCKDB_MINOR_VERSION 1 @@ -8,10 +8,10 @@ #define DUCKDB_MAJOR_VERSION 2 #endif #ifndef DUCKDB_VERSION -#define DUCKDB_VERSION "v2.1.0-alpha48729" +#define DUCKDB_VERSION "v2.1.0-alpha48840" #endif #ifndef DUCKDB_SOURCE_ID -#define DUCKDB_SOURCE_ID "1b4c3e54ba" +#define DUCKDB_SOURCE_ID "16917f94f9" #endif #include "duckdb/function/table/system_functions.hpp" #include "duckdb/main/database.hpp" diff --git a/src/duckdb/src/include/duckdb/common/sorting/buffered_merge.hpp b/src/duckdb/src/include/duckdb/common/sorting/buffered_merge.hpp index 773357fdc..56a09ba29 100644 --- a/src/duckdb/src/include/duckdb/common/sorting/buffered_merge.hpp +++ b/src/duckdb/src/include/duckdb/common/sorting/buffered_merge.hpp @@ -60,7 +60,8 @@ class SortMergeBuffer { //! of the two pieces goes through the scratch buffer when it fits, otherwise this is a plain std::rotate; neither //! allocates. template -ITERATOR BufferedRotate(ITERATOR first, ITERATOR middle, ITERATOR last, SORT_KEY *buffer, const idx_t buffer_count) { +ITERATOR BufferedRotate(const ITERATOR &first, const ITERATOR &middle, const ITERATOR &last, SORT_KEY *buffer, + const idx_t buffer_count) { const auto left_count = static_cast(middle - first); const auto right_count = static_cast(last - middle); if (left_count > right_count && right_count <= buffer_count) { @@ -180,7 +181,7 @@ class BufferedMergePolicy { } template - void operator()(ITERATOR first, ITERATOR middle, ITERATOR last, const COMPARE &compare) const { + void operator()(const ITERATOR &first, const ITERATOR &middle, const ITERATOR &last, const COMPARE &compare) const { BufferedMerge(first, middle, last, compare, keys, count); } diff --git a/src/duckdb/src/include/duckdb/main/capi_v2/capi_v2_function_internal.hpp b/src/duckdb/src/include/duckdb/main/capi_v2/capi_v2_function_internal.hpp index 4deccc93f..d41fdf263 100644 --- a/src/duckdb/src/include/duckdb/main/capi_v2/capi_v2_function_internal.hpp +++ b/src/duckdb/src/include/duckdb/main/capi_v2/capi_v2_function_internal.hpp @@ -13,6 +13,7 @@ #include "duckdb/function/function.hpp" #include "duckdb/function/table_function.hpp" #include "duckdb/parser/parsed_data/create_function_info.hpp" +#include "duckdb/storage/statistics/base_statistics.hpp" namespace duckdb::capiv2 { @@ -134,6 +135,35 @@ class CV2ConstantBindInfo final : public CV2FunctionBindInfo { const named_argument_map_t &named; }; +//! The statistics a stats callback reads or narrows. Argument statistics are read-only: they have no mutable view +class CV2Stats { +public: + explicit CV2Stats(const BaseStatistics &stats) : stats(stats) { + } + static CV2Stats Writable(BaseStatistics &stats) { + CV2Stats result(stats); + result.mutable_stats = &stats; + return result; + } + + const BaseStatistics &GetStats() const { + return stats; + } + //! Throws when the statistics are read-only + BaseStatistics &GetMutableStats(const char *function_name); + +private: + const BaseStatistics &stats; + optional_ptr mutable_stats; +}; + +inline auto Convert(duckdb_v2_stats_handle stats) -> CV2Stats * { + return reinterpret_cast(stats); +} +inline auto Convert(CV2Stats *stats) -> duckdb_v2_stats_handle { + return reinterpret_cast(stats); +} + inline auto Convert(duckdb_v2_function_bind_info_handle info) -> CV2FunctionBindInfo * { return reinterpret_cast(info); } diff --git a/src/duckdb/src/include/duckdb/main/capi_v2/extension_api_v2.hpp b/src/duckdb/src/include/duckdb/main/capi_v2/extension_api_v2.hpp index f749148a5..02632563c 100644 --- a/src/duckdb/src/include/duckdb/main/capi_v2/extension_api_v2.hpp +++ b/src/duckdb/src/include/duckdb/main/capi_v2/extension_api_v2.hpp @@ -1383,6 +1383,56 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_claim_batch_callback) (duckdb_v2_table_function_handle function, duckdb_v2_table_function_claim_batch_callback_fn callback, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_set_stats_callback) + (duckdb_v2_aggregate_function_handle function, duckdb_v2_aggregate_function_stats_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_stats_get_arg_count) + (duckdb_v2_aggregate_function_stats_info_handle info, idx_t *positional_fixed, idx_t *positional_variadic, + idx_t *named_fixed, idx_t *named_variadic, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_stats_get_arg_stats) + (duckdb_v2_aggregate_function_stats_info_handle info, idx_t index, duckdb_v2_stats_handle *stats, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_stats_get_bind_data) + (duckdb_v2_aggregate_function_stats_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_stats_get_result_stats) + (duckdb_v2_aggregate_function_stats_info_handle info, duckdb_v2_stats_handle *stats, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_stats_get_user_data) + (duckdb_v2_aggregate_function_stats_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_set_stats_callback) + (duckdb_v2_scalar_function_handle function, duckdb_v2_scalar_function_stats_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_stats_get_arg_count) + (duckdb_v2_scalar_function_stats_info_handle info, idx_t *positional_fixed, idx_t *positional_variadic, + idx_t *named_fixed, idx_t *named_variadic, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_stats_get_arg_stats) + (duckdb_v2_scalar_function_stats_info_handle info, idx_t index, duckdb_v2_stats_handle *stats, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_stats_get_bind_data) + (duckdb_v2_scalar_function_stats_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_stats_get_result_stats) + (duckdb_v2_scalar_function_stats_info_handle info, duckdb_v2_stats_handle *stats, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_stats_get_user_data) + (duckdb_v2_scalar_function_stats_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_stats_can_have_null) + (duckdb_v2_stats_handle stats, bool *out, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_stats_can_have_valid) + (duckdb_v2_stats_handle stats, bool *out, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_stats_set_can_have_null) + (duckdb_v2_stats_handle stats, bool value, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_stats_set_can_have_valid) + (duckdb_v2_stats_handle stats, bool value, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_stats_callback) + (duckdb_v2_table_function_handle function, duckdb_v2_table_function_stats_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_stats_get_bind_data) + (duckdb_v2_table_function_stats_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_stats_get_column_index) + (duckdb_v2_table_function_stats_info_handle info, idx_t *column_index, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_stats_get_result_stats) + (duckdb_v2_table_function_stats_info_handle info, duckdb_v2_stats_handle *stats, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_stats_get_user_data) + (duckdb_v2_table_function_stats_info_handle info, void **data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_table_function_get_bind_info_get_bind_data) (duckdb_v2_table_function_get_bind_info_info_handle info, void **data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_table_function_get_bind_info_get_user_data) @@ -2041,6 +2091,27 @@ inline duckdb_ext_api_v2 CreateAPIv2(void) { result.duckdb_v2_table_function_claim_batch_get_user_data = duckdb_v2_table_function_claim_batch_get_user_data; result.duckdb_v2_table_function_claim_batch_set_claimed = duckdb_v2_table_function_claim_batch_set_claimed; result.duckdb_v2_table_function_set_claim_batch_callback = duckdb_v2_table_function_set_claim_batch_callback; + result.duckdb_v2_aggregate_function_set_stats_callback = duckdb_v2_aggregate_function_set_stats_callback; + result.duckdb_v2_aggregate_function_stats_get_arg_count = duckdb_v2_aggregate_function_stats_get_arg_count; + result.duckdb_v2_aggregate_function_stats_get_arg_stats = duckdb_v2_aggregate_function_stats_get_arg_stats; + result.duckdb_v2_aggregate_function_stats_get_bind_data = duckdb_v2_aggregate_function_stats_get_bind_data; + result.duckdb_v2_aggregate_function_stats_get_result_stats = duckdb_v2_aggregate_function_stats_get_result_stats; + result.duckdb_v2_aggregate_function_stats_get_user_data = duckdb_v2_aggregate_function_stats_get_user_data; + result.duckdb_v2_scalar_function_set_stats_callback = duckdb_v2_scalar_function_set_stats_callback; + result.duckdb_v2_scalar_function_stats_get_arg_count = duckdb_v2_scalar_function_stats_get_arg_count; + result.duckdb_v2_scalar_function_stats_get_arg_stats = duckdb_v2_scalar_function_stats_get_arg_stats; + result.duckdb_v2_scalar_function_stats_get_bind_data = duckdb_v2_scalar_function_stats_get_bind_data; + result.duckdb_v2_scalar_function_stats_get_result_stats = duckdb_v2_scalar_function_stats_get_result_stats; + result.duckdb_v2_scalar_function_stats_get_user_data = duckdb_v2_scalar_function_stats_get_user_data; + result.duckdb_v2_stats_can_have_null = duckdb_v2_stats_can_have_null; + result.duckdb_v2_stats_can_have_valid = duckdb_v2_stats_can_have_valid; + result.duckdb_v2_stats_set_can_have_null = duckdb_v2_stats_set_can_have_null; + result.duckdb_v2_stats_set_can_have_valid = duckdb_v2_stats_set_can_have_valid; + result.duckdb_v2_table_function_set_stats_callback = duckdb_v2_table_function_set_stats_callback; + result.duckdb_v2_table_function_stats_get_bind_data = duckdb_v2_table_function_stats_get_bind_data; + result.duckdb_v2_table_function_stats_get_column_index = duckdb_v2_table_function_stats_get_column_index; + result.duckdb_v2_table_function_stats_get_result_stats = duckdb_v2_table_function_stats_get_result_stats; + result.duckdb_v2_table_function_stats_get_user_data = duckdb_v2_table_function_stats_get_user_data; result.duckdb_v2_table_function_get_bind_info_get_bind_data = duckdb_v2_table_function_get_bind_info_get_bind_data; result.duckdb_v2_table_function_get_bind_info_get_user_data = duckdb_v2_table_function_get_bind_info_get_user_data; result.duckdb_v2_table_function_get_bind_info_set_column_identifier = diff --git a/src/duckdb/src/include/duckdb/main/config.hpp b/src/duckdb/src/include/duckdb/main/config.hpp index 19507036b..1c04b657d 100644 --- a/src/duckdb/src/include/duckdb/main/config.hpp +++ b/src/duckdb/src/include/duckdb/main/config.hpp @@ -154,6 +154,8 @@ struct DBConfigOptions { bool abort_on_wal_failure = false; //! Paths that are explicitly allowed, even if enable_external_access is false unordered_set allowed_paths; + //! Environment variables the embedder configures itself from, readable with OSUtil::GetEnvUnrestricted + unordered_set configuration_env; //! Directories that are explicitly allowed, even if enable_external_access is false set allowed_directories; //! Additional configuration options that are allowed to be changed even when the configuration is locked diff --git a/src/duckdb/src/include/duckdb/main/database.hpp b/src/duckdb/src/include/duckdb/main/database.hpp index 0f4942193..041f3119d 100644 --- a/src/duckdb/src/include/duckdb/main/database.hpp +++ b/src/duckdb/src/include/duckdb/main/database.hpp @@ -38,6 +38,7 @@ struct AttachOptions; class DatabaseFileSystem; struct DatabaseCacheEntry; class LogManager; +class OSUtil; class MetricsManager; class ExternalFileCache; class ResultSetManager; @@ -70,6 +71,7 @@ class DatabaseInstance : public enable_shared_from_this { DUCKDB_API ExtensionManager &GetExtensionManager(); DUCKDB_API ValidChecker &GetValidChecker(); DUCKDB_API LogManager &GetLogManager() const; + DUCKDB_API OSUtil &GetOSUtil(); DUCKDB_API MetricsManager &GetMetricsManager(); DUCKDB_API ParserCache &GetParserCache(); @@ -126,6 +128,7 @@ class DatabaseInstance : public enable_shared_from_this { unique_ptr external_file_cache; unique_ptr result_set_manager; unique_ptr parser_cache; + unique_ptr os_util; duckdb_ext_api_v1 (*create_api_v1)(); //! Set in Initialize. Loading a V2 C API extension builds the C API function table and opens a connection, both of diff --git a/src/duckdb/src/include/duckdb/main/http/http_retry_budget.hpp b/src/duckdb/src/include/duckdb/main/http/http_retry_budget.hpp index 7ea6228e3..55ed69524 100644 --- a/src/duckdb/src/include/duckdb/main/http/http_retry_budget.hpp +++ b/src/duckdb/src/include/duckdb/main/http/http_retry_budget.hpp @@ -75,7 +75,7 @@ class HTTPRetryQuota { //! Refund for a request that succeeded without retrying. static constexpr int64_t NO_RETRY_REFUND = 1; //! A quota untouched for this long is full again when it is next used. - static constexpr int64_t IDLE_RESET_MS = 60 * 1000; + static constexpr int64_t IDLE_RESET_MS = int64_t(60) * 1000; public: //! Take `cost` tokens; false (and nothing taken) when fewer are available. diff --git a/src/duckdb/src/include/duckdb/main/os_util.hpp b/src/duckdb/src/include/duckdb/main/os_util.hpp new file mode 100644 index 000000000..95ae40c4f --- /dev/null +++ b/src/duckdb/src/include/duckdb/main/os_util.hpp @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/main/os_util.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/string.hpp" +#include "duckdb/common/unordered_set.hpp" +#include "duckdb/common/winapi.hpp" + +namespace duckdb { +class DatabaseInstance; + +//! Reads process-level state, such as environment variables, on behalf of a database +class OSUtil { +public: + //! Created when the database is configured, with the embedder's configuration variables + OSUtil(DatabaseInstance &db, unordered_set configuration_env); + + DUCKDB_API static OSUtil &Get(DatabaseInstance &db); + +public: + //! Reads an environment variable for the user; throws if enable_external_access is disabled + DUCKDB_API bool GetEnv(const string &name, string &value); + //! As above, returning an empty string if the variable is unset + DUCKDB_API string GetEnv(const string &name); + + //! Reads a known configuration variable (see IsConfigurationEnv and configuration_env), regardless of settings + DUCKDB_API bool GetEnvUnrestricted(const string &name, string &value); + //! As above, returning an empty string if the variable is unset + DUCKDB_API string GetEnvUnrestricted(const string &name); + + //! Whether the engine itself configures from this variable + DUCKDB_API static bool IsConfigurationEnv(const string &name); + //! Reads one of the engine's own configuration variables when no database exists yet + DUCKDB_API static bool GetConfigurationEnv(const string &name, string &value); + //! As above, returning an empty string if the variable is unset + DUCKDB_API static string GetConfigurationEnv(const string &name); + +private: + //! Reads a variable from the process environment, without any checks + static bool ReadEnv(const string &name, string &value); + +private: + DatabaseInstance &db; + //! Variables the embedding application declared as its configuration + const unordered_set configuration_env; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb_extension_v2.h b/src/duckdb/src/include/duckdb_extension_v2.h index bc3df295e..082332ae9 100644 --- a/src/duckdb/src/include/duckdb_extension_v2.h +++ b/src/duckdb/src/include/duckdb_extension_v2.h @@ -1449,6 +1449,56 @@ typedef struct { DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_claim_batch_callback) (duckdb_v2_table_function_handle function, duckdb_v2_table_function_claim_batch_callback_fn callback, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_set_stats_callback) + (duckdb_v2_aggregate_function_handle function, duckdb_v2_aggregate_function_stats_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_stats_get_arg_count) + (duckdb_v2_aggregate_function_stats_info_handle info, idx_t *positional_fixed, idx_t *positional_variadic, + idx_t *named_fixed, idx_t *named_variadic, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_stats_get_arg_stats) + (duckdb_v2_aggregate_function_stats_info_handle info, idx_t index, duckdb_v2_stats_handle *stats, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_stats_get_bind_data) + (duckdb_v2_aggregate_function_stats_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_stats_get_result_stats) + (duckdb_v2_aggregate_function_stats_info_handle info, duckdb_v2_stats_handle *stats, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_aggregate_function_stats_get_user_data) + (duckdb_v2_aggregate_function_stats_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_set_stats_callback) + (duckdb_v2_scalar_function_handle function, duckdb_v2_scalar_function_stats_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_stats_get_arg_count) + (duckdb_v2_scalar_function_stats_info_handle info, idx_t *positional_fixed, idx_t *positional_variadic, + idx_t *named_fixed, idx_t *named_variadic, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_stats_get_arg_stats) + (duckdb_v2_scalar_function_stats_info_handle info, idx_t index, duckdb_v2_stats_handle *stats, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_stats_get_bind_data) + (duckdb_v2_scalar_function_stats_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_stats_get_result_stats) + (duckdb_v2_scalar_function_stats_info_handle info, duckdb_v2_stats_handle *stats, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_scalar_function_stats_get_user_data) + (duckdb_v2_scalar_function_stats_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_stats_can_have_null) + (duckdb_v2_stats_handle stats, bool *out, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_stats_can_have_valid) + (duckdb_v2_stats_handle stats, bool *out, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_stats_set_can_have_null) + (duckdb_v2_stats_handle stats, bool value, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_stats_set_can_have_valid) + (duckdb_v2_stats_handle stats, bool value, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_set_stats_callback) + (duckdb_v2_table_function_handle function, duckdb_v2_table_function_stats_callback_fn callback, + duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_stats_get_bind_data) + (duckdb_v2_table_function_stats_info_handle info, void **data, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_stats_get_column_index) + (duckdb_v2_table_function_stats_info_handle info, idx_t *column_index, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_stats_get_result_stats) + (duckdb_v2_table_function_stats_info_handle info, duckdb_v2_stats_handle *stats, duckdb_v2_error_info_handle *err); + DUCKDB_V2_ERROR(*duckdb_v2_table_function_stats_get_user_data) + (duckdb_v2_table_function_stats_info_handle info, void **data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_table_function_get_bind_info_get_bind_data) (duckdb_v2_table_function_get_bind_info_info_handle info, void **data, duckdb_v2_error_info_handle *err); DUCKDB_V2_ERROR(*duckdb_v2_table_function_get_bind_info_get_user_data) @@ -2195,6 +2245,70 @@ typedef struct { duckdb_ext_api.duckdb_v2_table_function_set_claim_batch_callback #endif #if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_aggregate_function_set_stats_callback duckdb_ext_api.duckdb_v2_aggregate_function_set_stats_callback +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_aggregate_function_stats_get_arg_count duckdb_ext_api.duckdb_v2_aggregate_function_stats_get_arg_count +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_aggregate_function_stats_get_arg_stats duckdb_ext_api.duckdb_v2_aggregate_function_stats_get_arg_stats +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_aggregate_function_stats_get_bind_data duckdb_ext_api.duckdb_v2_aggregate_function_stats_get_bind_data +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_aggregate_function_stats_get_result_stats \ + duckdb_ext_api.duckdb_v2_aggregate_function_stats_get_result_stats +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_aggregate_function_stats_get_user_data duckdb_ext_api.duckdb_v2_aggregate_function_stats_get_user_data +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_scalar_function_set_stats_callback duckdb_ext_api.duckdb_v2_scalar_function_set_stats_callback +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_scalar_function_stats_get_arg_count duckdb_ext_api.duckdb_v2_scalar_function_stats_get_arg_count +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_scalar_function_stats_get_arg_stats duckdb_ext_api.duckdb_v2_scalar_function_stats_get_arg_stats +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_scalar_function_stats_get_bind_data duckdb_ext_api.duckdb_v2_scalar_function_stats_get_bind_data +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_scalar_function_stats_get_result_stats duckdb_ext_api.duckdb_v2_scalar_function_stats_get_result_stats +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_scalar_function_stats_get_user_data duckdb_ext_api.duckdb_v2_scalar_function_stats_get_user_data +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_stats_can_have_null duckdb_ext_api.duckdb_v2_stats_can_have_null +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_stats_can_have_valid duckdb_ext_api.duckdb_v2_stats_can_have_valid +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_stats_set_can_have_null duckdb_ext_api.duckdb_v2_stats_set_can_have_null +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_stats_set_can_have_valid duckdb_ext_api.duckdb_v2_stats_set_can_have_valid +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_table_function_set_stats_callback duckdb_ext_api.duckdb_v2_table_function_set_stats_callback +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_table_function_stats_get_bind_data duckdb_ext_api.duckdb_v2_table_function_stats_get_bind_data +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_table_function_stats_get_column_index duckdb_ext_api.duckdb_v2_table_function_stats_get_column_index +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_table_function_stats_get_result_stats duckdb_ext_api.duckdb_v2_table_function_stats_get_result_stats +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE +#define duckdb_v2_table_function_stats_get_user_data duckdb_ext_api.duckdb_v2_table_function_stats_get_user_data +#endif +#if DUCKDB_V2_API_ALLOW_UNSTABLE #define duckdb_v2_table_function_get_bind_info_get_bind_data \ duckdb_ext_api.duckdb_v2_table_function_get_bind_info_get_bind_data #endif diff --git a/src/duckdb/src/include/duckdb_v2.h b/src/duckdb/src/include/duckdb_v2.h index 2cede3650..aa3c3df80 100644 --- a/src/duckdb/src/include/duckdb_v2.h +++ b/src/duckdb/src/include/duckdb_v2.h @@ -4216,6 +4216,116 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_schema_destroy(duckdb_v2_schema_handle *s /* --- Struct definitions for schema --- */ +/* ============================================================================ + * MODULE: stats + * ============================================================================ */ + +/* --- Enums for stats --- */ + +/* --- Struct forward declarations for stats --- */ + +/* --- Types for stats --- */ + +/*! + * A borrowed opaque handle to the statistics of an expression or a column. A stats callback receives it for the result + * it describes and for each of its arguments; it is valid only for the duration of that callback. + * + * The statistics of an argument are read-only, and the setters fail on them. The statistics of the result start out + * unknown, with every kind of value possible, and the callback narrows them through the setters. A callback that leaves + * the result unchanged tells the engine nothing. + */ +typedef struct _duckdb_v2_stats { + void *internal_ptr; +} * duckdb_v2_stats_handle; + +/* --- Constants for stats --- */ + +/* --- Function pointer typedefs for stats --- */ + +/* --- Functions for stats --- */ + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Returns whether the values described can include NULL. + * + * False guarantees that every value is non-NULL. + * + * history: + * - unstable: v2.0.0 + * + * @param stats The statistics to read. + * @param out Receives true if a value can be NULL, false if none can. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_stats_can_have_null(duckdb_v2_stats_handle stats, bool *out, + duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Returns whether the values described can include a valid, non-NULL value. + * + * False guarantees that every value is NULL. When both this and `duckdb_v2_stats_can_have_null()` are false, there are + * no values at all. + * + * history: + * - unstable: v2.0.0 + * + * @param stats The statistics to read. + * @param out Receives true if a value can be non-NULL, false if none can. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_stats_can_have_valid(duckdb_v2_stats_handle stats, bool *out, + duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Sets whether the values described can include NULL. + * + * Setting false is a guarantee the engine relies on: a NULL among the values then produces wrong query results. Setting + * true is always safe. Fails with `ERROR_INPUT_INVALID` on read-only statistics, such as those of an argument. + * + * history: + * - unstable: v2.0.0 + * + * @param stats The statistics to modify. + * @param value True if a value can be NULL, false if none can. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_stats_set_can_have_null(duckdb_v2_stats_handle stats, bool value, + duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Sets whether the values described can include a valid, non-NULL value. + * + * Setting false is a guarantee the engine relies on: a non-NULL value among the values then produces wrong query + * results. Setting true is always safe. Fails with `ERROR_INPUT_INVALID` on read-only statistics, such as those of an + * argument. + * + * history: + * - unstable: v2.0.0 + * + * @param stats The statistics to modify. + * @param value True if a value can be non-NULL, false if none can. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_stats_set_can_have_valid(duckdb_v2_stats_handle stats, bool value, + duckdb_v2_error_info_handle *err); +#endif + +/* --- Struct definitions for stats --- */ + /* ============================================================================ * MODULE: tokenizer * ============================================================================ */ @@ -4380,6 +4490,15 @@ typedef struct _duckdb_v2_aggregate_function_bind_info { void *internal_ptr; } * duckdb_v2_aggregate_function_bind_info_handle; +/*! + * A borrowed opaque handle to the arguments supplied to an aggregate function during the "stats" phase of query + * optimization. The "stats" callback receives this handle and can use it to read the statistics of the arguments of the + * call site and narrow the statistics of its result. + */ +typedef struct _duckdb_v2_aggregate_function_stats_info { + void *internal_ptr; +} * duckdb_v2_aggregate_function_stats_info_handle; + /*! * A borrowed opaque handle to the arguments supplied to an aggregate function during the state sizing "size" phase. The * "size" callback receives this handle and must use it to report the size of a single aggregate state in bytes. @@ -4441,6 +4560,10 @@ typedef void (*duckdb_v2_aggregate_function_bind_callback_fn)(duckdb_v2_function duckdb_v2_context_handle context, duckdb_v2_error_info_handle *err); +typedef void (*duckdb_v2_aggregate_function_stats_callback_fn)(duckdb_v2_aggregate_function_stats_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + typedef void (*duckdb_v2_aggregate_function_size_callback_fn)(duckdb_v2_aggregate_function_size_info_handle info, duckdb_v2_error_info_handle *err); @@ -4723,6 +4846,29 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_set_destroy_callback( duckdb_v2_aggregate_function_handle function, duckdb_v2_aggregate_function_destroy_callback_fn callback, duckdb_v2_error_info_handle *err); +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Sets the optional stats callback of the aggregate function. + * + * The stats callback is invoked during query optimization for each call site of the function, after it has been bound. + * Through its `duckdb_v2_aggregate_function_stats_info_handle` it reads the statistics of the arguments and narrows the + * statistics of the result, which the engine then uses to simplify the query. A function without a stats callback gives + * the engine no statistics for its result. + * + * history: + * - unstable: v2.0.0 + * + * @param function The function to set the stats callback of. + * @param callback The stats callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_set_stats_callback( + duckdb_v2_aggregate_function_handle function, duckdb_v2_aggregate_function_stats_callback_fn callback, + duckdb_v2_error_info_handle *err); +#endif + /*! * Sets the concrete return type of the call site being bound. * @@ -5196,6 +5342,110 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_destroy_get_state_coun DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_destroy_get_states( duckdb_v2_aggregate_function_destroy_info_handle info, void ***states, duckdb_v2_error_info_handle *err); +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the user data set via `duckdb_v2_aggregate_function_set_user_data()`. + * + * history: + * - unstable: v2.0.0 + * + * @param info The stats info handle. + * @param data Receives the user data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_stats_get_user_data( + duckdb_v2_aggregate_function_stats_info_handle info, void **data, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the bind data set by the function's bind callback. + * + * history: + * - unstable: v2.0.0 + * + * @param info The stats info handle. + * @param data Receives the bind data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_stats_get_bind_data( + duckdb_v2_aggregate_function_stats_info_handle info, void **data, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Returns how many arguments the call site has, split into the four parts of the argument list. + * + * The parts and their order are those the bind callback saw through `duckdb_v2_function_bind_get_arg_count()`, and the + * arguments are at the same indices. Valid indices for `duckdb_v2_aggregate_function_stats_get_arg_stats()` are [0, the + * sum of the four counts). Every out-parameter may be NULL, in which case nothing is written to it. + * + * history: + * - unstable: v2.0.0 + * + * @param info The stats info handle. + * @param positional_fixed Optional. Receives the number of positional-only and standard parameters. + * @param positional_variadic Optional. Receives the number of arguments `*args` received, 0 when the signature has + * none. + * @param named_fixed Optional. Receives the number of named-only parameters. + * @param named_variadic Optional. Receives the number of arguments `**kwargs` received, 0 when the signature has none. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_aggregate_function_stats_get_arg_count( + duckdb_v2_aggregate_function_stats_info_handle info, idx_t *positional_fixed, idx_t *positional_variadic, + idx_t *named_fixed, idx_t *named_variadic, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the statistics of the argument at the given index. + * + * The index is the one the bind callback used for the argument. The statistics are read-only. Fails if the index is out + * of bounds. Borrowed; valid only for the duration of the callback. + * + * history: + * - unstable: v2.0.0 + * + * @param info The stats info handle. + * @param index The index of the argument. + * @param stats Receives the borrowed, read-only statistics of the argument. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR +duckdb_v2_aggregate_function_stats_get_arg_stats(duckdb_v2_aggregate_function_stats_info_handle info, idx_t index, + duckdb_v2_stats_handle *stats, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the statistics of the call site's result, for the callback to narrow. + * + * The statistics start out unknown, with every kind of value possible. They describe every result the aggregate + * produces, including the result of a group with no input rows, such as an ungrouped aggregate over an empty table. + * Borrowed; valid only for the duration of the callback. + * + * history: + * - unstable: v2.0.0 + * + * @param info The stats info handle. + * @param stats Receives the borrowed, writable statistics of the call site's result. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR +duckdb_v2_aggregate_function_stats_get_result_stats(duckdb_v2_aggregate_function_stats_info_handle info, + duckdb_v2_stats_handle *stats, duckdb_v2_error_info_handle *err); +#endif + /*! * Registers the aggregate function, making it available for use in SQL queries. * @@ -9406,6 +9656,15 @@ typedef struct _duckdb_v2_scalar_function_exec_info { void *internal_ptr; } * duckdb_v2_scalar_function_exec_info_handle; +/*! + * A borrowed opaque handle to the arguments supplied to a scalar function during the "stats" phase of query + * optimization. The "stats" callback receives this handle and can use it to read the statistics of the arguments of the + * call site and narrow the statistics of its result. + */ +typedef struct _duckdb_v2_scalar_function_stats_info { + void *internal_ptr; +} * duckdb_v2_scalar_function_stats_info_handle; + /* --- Constants for scalar --- */ /* --- Function pointer typedefs for scalar --- */ @@ -9427,6 +9686,10 @@ typedef void (*duckdb_v2_scalar_function_exec_callback_fn)(duckdb_v2_scalar_func duckdb_v2_context_handle context, duckdb_v2_error_info_handle *err); +typedef void (*duckdb_v2_scalar_function_stats_callback_fn)(duckdb_v2_scalar_function_stats_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + /* --- Functions for scalar --- */ /*! @@ -9639,6 +9902,29 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_exec_callback( duckdb_v2_scalar_function_handle function, duckdb_v2_scalar_function_exec_callback_fn callback, duckdb_v2_error_info_handle *err); +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Sets the optional stats callback of the scalar function. + * + * The stats callback is invoked during query optimization for each call site of the function, after it has been bound. + * Through its `duckdb_v2_scalar_function_stats_info_handle` it reads the statistics of the arguments and narrows the + * statistics of the result, which the engine then uses to simplify the query. A function without a stats callback gives + * the engine no statistics for its result. + * + * history: + * - unstable: v2.0.0 + * + * @param function The function to set the stats callback of. + * @param callback The stats callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_stats_callback( + duckdb_v2_scalar_function_handle function, duckdb_v2_scalar_function_stats_callback_fn callback, + duckdb_v2_error_info_handle *err); +#endif + /*! * Sets the concrete return type of the call site being bound. * @@ -9855,6 +10141,108 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_result(duckdb_v2 duckdb_v2_vector_handle *vector, duckdb_v2_error_info_handle *err); +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the user data set via `duckdb_v2_scalar_function_set_user_data()`. + * + * history: + * - unstable: v2.0.0 + * + * @param info The stats info handle. + * @param data Receives the user data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_stats_get_user_data( + duckdb_v2_scalar_function_stats_info_handle info, void **data, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the bind data set by the function's bind callback. + * + * history: + * - unstable: v2.0.0 + * + * @param info The stats info handle. + * @param data Receives the bind data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_stats_get_bind_data( + duckdb_v2_scalar_function_stats_info_handle info, void **data, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Returns how many arguments the call site has, split into the four parts of the argument list. + * + * The parts and their order are those the bind callback saw through `duckdb_v2_function_bind_get_arg_count()`, and the + * arguments are at the same indices. Valid indices for `duckdb_v2_scalar_function_stats_get_arg_stats()` are [0, the + * sum of the four counts). Every out-parameter may be NULL, in which case nothing is written to it. + * + * history: + * - unstable: v2.0.0 + * + * @param info The stats info handle. + * @param positional_fixed Optional. Receives the number of positional-only and standard parameters. + * @param positional_variadic Optional. Receives the number of arguments `*args` received, 0 when the signature has + * none. + * @param named_fixed Optional. Receives the number of named-only parameters. + * @param named_variadic Optional. Receives the number of arguments `**kwargs` received, 0 when the signature has none. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_stats_get_arg_count( + duckdb_v2_scalar_function_stats_info_handle info, idx_t *positional_fixed, idx_t *positional_variadic, + idx_t *named_fixed, idx_t *named_variadic, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the statistics of the argument at the given index. + * + * The index is the one the bind callback used for the argument. The statistics are read-only. Fails if the index is out + * of bounds. Borrowed; valid only for the duration of the callback. + * + * history: + * - unstable: v2.0.0 + * + * @param info The stats info handle. + * @param index The index of the argument. + * @param stats Receives the borrowed, read-only statistics of the argument. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR +duckdb_v2_scalar_function_stats_get_arg_stats(duckdb_v2_scalar_function_stats_info_handle info, idx_t index, + duckdb_v2_stats_handle *stats, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the statistics of the call site's result, for the callback to narrow. + * + * The statistics start out unknown, with every kind of value possible. Borrowed; valid only for the duration of the + * callback. + * + * history: + * - unstable: v2.0.0 + * + * @param info The stats info handle. + * @param stats Receives the borrowed, writable statistics of the call site's result. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_scalar_function_stats_get_result_stats( + duckdb_v2_scalar_function_stats_info_handle info, duckdb_v2_stats_handle *stats, duckdb_v2_error_info_handle *err); +#endif + /*! * Registers the scalar function, making it available for use in SQL queries. * @@ -11389,6 +11777,15 @@ typedef struct _duckdb_v2_table_function_partition_data_info { void *internal_ptr; } * duckdb_v2_table_function_partition_data_info_handle; +/*! + * A borrowed opaque handle to the arguments supplied to a table function during the "stats" phase of query + * optimization. The engine invokes the callback once per column the query reads, and the callback can use the handle to + * narrow the statistics of that column. + */ +typedef struct _duckdb_v2_table_function_stats_info { + void *internal_ptr; +} * duckdb_v2_table_function_stats_info_handle; + /*! * A borrowed opaque handle to the arguments supplied to a table function during the "partitioning" phase of query * optimization. The engine invokes the callback once per candidate `GROUP BY` column set, before execution starts, to @@ -11475,6 +11872,10 @@ typedef void (*duckdb_v2_table_function_get_bind_info_callback_fn)( duckdb_v2_table_function_get_bind_info_info_handle info, duckdb_v2_context_handle context, duckdb_v2_error_info_handle *err); +typedef void (*duckdb_v2_table_function_stats_callback_fn)(duckdb_v2_table_function_stats_info_handle info, + duckdb_v2_context_handle context, + duckdb_v2_error_info_handle *err); + /* --- Functions for table --- */ /*! @@ -11773,6 +12174,28 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_partitioning_callback( duckdb_v2_table_function_handle function, duckdb_v2_table_function_partitioning_callback_fn callback, duckdb_v2_error_info_handle *err); +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Sets the optional stats callback of the table function. + * + * The stats callback is invoked during query optimization once for each declared column the query reads. Through its + * `duckdb_v2_table_function_stats_info_handle` it narrows the statistics of that column, which the engine then uses to + * simplify the query. A function without a stats callback gives the engine no statistics for its columns. + * + * history: + * - unstable: v2.0.0 + * + * @param function The function to set the stats callback of. + * @param callback The stats callback to set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_set_stats_callback( + duckdb_v2_table_function_handle function, duckdb_v2_table_function_stats_callback_fn callback, + duckdb_v2_error_info_handle *err); +#endif + /*! * Declares one of the columns the function returns. * @@ -12656,6 +13079,80 @@ DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_partitioning_set_partition duckdb_v2_table_function_partitioning_info_handle info, DUCKDB_V2_TABLE_PARTITION_INFO partition_info, duckdb_v2_error_info_handle *err); +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the user data set via `duckdb_v2_table_function_set_user_data()`. + * + * history: + * - unstable: v2.0.0 + * + * @param info The stats info handle. + * @param data Receives the user data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_stats_get_user_data( + duckdb_v2_table_function_stats_info_handle info, void **data, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the bind data set by the function's bind callback. + * + * history: + * - unstable: v2.0.0 + * + * @param info The stats info handle. + * @param data Receives the bind data pointer, or null if none was set. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_stats_get_bind_data( + duckdb_v2_table_function_stats_info_handle info, void **data, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Returns which declared column the statistics describe. + * + * The result indexes the columns declared with `duckdb_v2_table_function_bind_add_result_column()`, in declaration + * order. + * + * history: + * - unstable: v2.0.0 + * + * @param info The stats info handle. + * @param column_index Receives the index of the declared column. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_stats_get_column_index( + duckdb_v2_table_function_stats_info_handle info, idx_t *column_index, duckdb_v2_error_info_handle *err); +#endif + +#if DUCKDB_V2_API_ALLOW_UNSTABLE +/*! + * Retrieves the statistics of the column, for the callback to narrow. + * + * The statistics start out unknown, with every kind of value possible. They describe every value the scan produces for + * the column. Borrowed; valid only for the duration of the callback. + * + * history: + * - unstable: v2.0.0 + * + * @param info The stats info handle. + * @param stats Receives the borrowed, writable statistics of the column. + * @param err Optional. On failure, receives an opaque info handle the caller must destroy via + * `duckdb_v2_error_info_destroy()`. + * @return DUCKDB_V2_ERROR + */ +DUCKDB_C_API DUCKDB_V2_ERROR duckdb_v2_table_function_stats_get_result_stats( + duckdb_v2_table_function_stats_info_handle info, duckdb_v2_stats_handle *stats, duckdb_v2_error_info_handle *err); +#endif + /*! * Registers the table function, making it available for use in SQL queries. * diff --git a/src/duckdb/src/main/capi/v2/capi_v2_func_aggregate.cpp b/src/duckdb/src/main/capi/v2/capi_v2_func_aggregate.cpp index d3d64d9da..b17d1f581 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_func_aggregate.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_func_aggregate.cpp @@ -3,6 +3,7 @@ #include "duckdb/catalog/catalog.hpp" #include "duckdb/function/aggregate_function.hpp" #include "duckdb/parser/parsed_data/create_aggregate_function_info.hpp" +#include "duckdb/planner/expression/bound_aggregate_expression.hpp" namespace duckdb::capiv2 { @@ -137,6 +138,28 @@ static auto Convert(CV2AggregateDestroyInfo *info) -> duckdb_v2_aggregate_functi return reinterpret_cast(info); } +class CV2AggregateStatsInfo { +public: + CV2AggregateStatsInfo(const BoundAggregateFunction &function, BaseStatistics &result) + : function(function), result_stats(CV2Stats::Writable(result)) { + } + + void *in_user_data = nullptr; + void *in_bind_data = nullptr; + + //! Tells the four parts of the argument list apart + const BoundAggregateFunction &function; + vector arg_stats; + CV2Stats result_stats; +}; + +static auto Convert(duckdb_v2_aggregate_function_stats_info_handle info) -> CV2AggregateStatsInfo * { + return reinterpret_cast(info); +} +static auto Convert(CV2AggregateStatsInfo *info) -> duckdb_v2_aggregate_function_stats_info_handle { + return reinterpret_cast(info); +} + class CV2AggregateFunctionInfo : public AggregateFunctionInfo { public: duckdb_v2_aggregate_function_bind_callback_fn bind_cb = nullptr; @@ -146,6 +169,7 @@ class CV2AggregateFunctionInfo : public AggregateFunctionInfo { duckdb_v2_aggregate_function_combine_callback_fn combine_cb = nullptr; duckdb_v2_aggregate_function_finalize_callback_fn finalize_cb = nullptr; duckdb_v2_aggregate_function_destroy_callback_fn destroy_cb = nullptr; + duckdb_v2_aggregate_function_stats_callback_fn stats_cb = nullptr; shared_ptr user_data = nullptr; }; @@ -311,6 +335,32 @@ static auto CV2AggregateDestroy(Vector &state, AggregateInputData &aggr_input_da // The destructor path may not throw: an error reported through the slot is dropped. } +static auto CV2AggregateStats(ClientContext &context, BoundAggregateExpression &expr, AggregateStatisticsInput &input) + -> unique_ptr { + const auto &function = expr.Function(); + const auto &info = function.GetExtraFunctionInfo().Cast(); + + auto result = BaseStatistics::CreateUnknown(expr.GetReturnType()); + CV2AggregateStatsInfo args(function, result); + args.in_user_data = info.user_data ? info.user_data->GetData() : nullptr; + args.in_bind_data = GetUserBindData(input.bind_data.get()); + args.arg_stats.reserve(input.child_stats.size()); + for (const auto &child_stats : input.child_stats) { + args.arg_stats.emplace_back(child_stats); + } + + CV2ErrorInfo err = {}; + auto err_ptr = Convert(&err); + CV2CallbackContext callback_context(context); + info.stats_cb(Convert(&args), Convert(&callback_context), &err_ptr); + + if (err.HasError()) { + err.ThrowAsException(); + } + + return result.ToUnique(); +} + class CV2AggregateFunction { public: explicit CV2AggregateFunction(DatabaseInstance &db) : db(db) { @@ -372,6 +422,9 @@ class CV2AggregateFunction { if (info.destroy_cb) { function.SetStateDestructorCallback(CV2AggregateDestroy); } + if (info.stats_cb) { + function.SetStatisticsCallback(CV2AggregateStats); + } function.SetExtraFunctionInfo(std::move(info)); return function; @@ -509,6 +562,13 @@ duckdb_v2_aggregate_function_set_destroy_callback(duckdb_v2_aggregate_function_h return WithErrorHandler(err, [&]() { Convert(function)->info.destroy_cb = callback; }); } +DUCKDB_V2_ERROR duckdb_v2_aggregate_function_set_stats_callback(duckdb_v2_aggregate_function_handle function, + duckdb_v2_aggregate_function_stats_callback_fn callback, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { Convert(function)->info.stats_cb = callback; }); +} + DUCKDB_V2_ERROR duckdb_v2_aggregate_function_bind_set_return_type(duckdb_v2_aggregate_function_bind_info_handle info, duckdb_v2_logical_type_handle return_type, duckdb_v2_error_info_handle *err) { @@ -749,6 +809,65 @@ DUCKDB_V2_ERROR duckdb_v2_aggregate_function_destroy_get_states(duckdb_v2_aggreg return WithErrorHandler(err, [&]() { *states = Convert(info)->states; }); } +DUCKDB_V2_ERROR duckdb_v2_aggregate_function_stats_get_user_data(duckdb_v2_aggregate_function_stats_info_handle info, + void **data, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_user_data; }); +} + +DUCKDB_V2_ERROR duckdb_v2_aggregate_function_stats_get_bind_data(duckdb_v2_aggregate_function_stats_info_handle info, + void **data, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_bind_data; }); +} + +DUCKDB_V2_ERROR duckdb_v2_aggregate_function_stats_get_arg_count(duckdb_v2_aggregate_function_stats_info_handle info, + idx_t *positional_fixed, idx_t *positional_variadic, + idx_t *named_fixed, idx_t *named_variadic, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + return WithErrorHandler(err, [&]() { + auto &function = Convert(info)->function; + if (positional_fixed) { + *positional_fixed = function.GetStandardArgumentCount(); + } + if (positional_variadic) { + *positional_variadic = function.GetVarArgsCount(); + } + if (named_fixed) { + *named_fixed = function.GetKeywordOnlyArgumentCount(); + } + if (named_variadic) { + *named_variadic = function.GetKwargsCount(); + } + }); +} + +DUCKDB_V2_ERROR duckdb_v2_aggregate_function_stats_get_arg_stats(duckdb_v2_aggregate_function_stats_info_handle info, + idx_t index, duckdb_v2_stats_handle *stats, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(stats); + return WithErrorHandler(err, [&]() { + auto &stats_info = *Convert(info); + if (index >= stats_info.arg_stats.size()) { + throw duckdb::InvalidInputException( + "Index out of bounds in duckdb_v2_aggregate_function_stats_get_arg_stats"); + } + *stats = Convert(&stats_info.arg_stats[index]); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_aggregate_function_stats_get_result_stats(duckdb_v2_aggregate_function_stats_info_handle info, + duckdb_v2_stats_handle *stats, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(stats); + return WithErrorHandler(err, [&]() { *stats = Convert(&Convert(info)->result_stats); }); +} + DUCKDB_V2_ERROR duckdb_v2_connection_register_aggregate_function(duckdb_v2_connection_handle conn, duckdb_v2_aggregate_function_handle function, duckdb_v2_error_info_handle *err) { diff --git a/src/duckdb/src/main/capi/v2/capi_v2_func_scalar.cpp b/src/duckdb/src/main/capi/v2/capi_v2_func_scalar.cpp index 5e5b1ff85..87d2fa6b4 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_func_scalar.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_func_scalar.cpp @@ -114,12 +114,35 @@ static auto Convert(CV2ExecInfo *info) -> duckdb_v2_scalar_function_exec_info_ha return reinterpret_cast(info); } +class CV2ScalarStatsInfo { +public: + CV2ScalarStatsInfo(const BoundScalarFunction &function, BaseStatistics &result) + : function(function), result_stats(CV2Stats::Writable(result)) { + } + + void *in_user_data = nullptr; + void *in_bind_data = nullptr; + + //! Tells the four parts of the argument list apart + const BoundScalarFunction &function; + vector arg_stats; + CV2Stats result_stats; +}; + +static auto Convert(duckdb_v2_scalar_function_stats_info_handle info) -> CV2ScalarStatsInfo * { + return reinterpret_cast(info); +} +static auto Convert(CV2ScalarStatsInfo *info) -> duckdb_v2_scalar_function_stats_info_handle { + return reinterpret_cast(info); +} + class CV2ScalarFunctionInfo : public ScalarFunctionInfo { public: duckdb_v2_scalar_function_resolve_types_callback_fn resolve_types_cb = nullptr; duckdb_v2_scalar_function_bind_callback_fn bind_cb = nullptr; duckdb_v2_scalar_function_init_callback_fn init_cb = nullptr; duckdb_v2_scalar_function_exec_callback_fn exec_cb = nullptr; + duckdb_v2_scalar_function_stats_callback_fn stats_cb = nullptr; shared_ptr user_data = nullptr; }; @@ -231,6 +254,34 @@ static auto CV2ScalarExec(DataChunk &input, ExpressionState &state, Vector &resu } } +static auto CV2ScalarStats(ClientContext &context, FunctionStatisticsInput &input) -> unique_ptr { + const auto &function = input.expr.Function(); + const auto &info = function.GetExtraFunctionInfo().Cast(); + + auto result = BaseStatistics::CreateUnknown(input.expr.GetReturnType()); + CV2ScalarStatsInfo args(function, result); + args.in_user_data = info.user_data ? info.user_data->GetData() : nullptr; + if (input.bind_data) { + const auto &bind_data = input.bind_data->Cast(); + args.in_bind_data = bind_data.handle ? bind_data.handle->GetData() : nullptr; + } + args.arg_stats.reserve(input.child_stats.size()); + for (const auto &child_stats : input.child_stats) { + args.arg_stats.emplace_back(child_stats); + } + + CV2ErrorInfo err = {}; + auto err_ptr = Convert(&err); + CV2CallbackContext callback_context(context); + info.stats_cb(Convert(&args), Convert(&callback_context), &err_ptr); + + if (err.HasError()) { + err.ThrowAsException(); + } + + return result.ToUnique(); +} + class CV2ScalarFunction { public: explicit CV2ScalarFunction(DatabaseInstance &db) : db(db) { @@ -282,6 +333,9 @@ class CV2ScalarFunction { if (info.init_cb) { function.SetInitStateCallback(CV2ScalarInit); } + if (info.stats_cb) { + function.SetStatisticsCallback(CV2ScalarStats); + } function.SetExtraFunctionInfo(std::move(info)); return function; @@ -394,6 +448,13 @@ DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_exec_callback(duckdb_v2_scalar_fun return WithErrorHandler(err, [&]() { Convert(function)->info.exec_cb = callback; }); } +DUCKDB_V2_ERROR duckdb_v2_scalar_function_set_stats_callback(duckdb_v2_scalar_function_handle function, + duckdb_v2_scalar_function_stats_callback_fn callback, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { Convert(function)->info.stats_cb = callback; }); +} + DUCKDB_V2_ERROR duckdb_v2_scalar_function_resolve_types_set_return_type(duckdb_v2_scalar_function_resolve_types_info_handle info, duckdb_v2_logical_type_handle return_type, @@ -517,6 +578,64 @@ DUCKDB_V2_ERROR duckdb_v2_scalar_function_exec_get_result(duckdb_v2_scalar_funct }); } +DUCKDB_V2_ERROR duckdb_v2_scalar_function_stats_get_user_data(duckdb_v2_scalar_function_stats_info_handle info, + void **data, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_user_data; }); +} + +DUCKDB_V2_ERROR duckdb_v2_scalar_function_stats_get_bind_data(duckdb_v2_scalar_function_stats_info_handle info, + void **data, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_bind_data; }); +} + +DUCKDB_V2_ERROR duckdb_v2_scalar_function_stats_get_arg_count(duckdb_v2_scalar_function_stats_info_handle info, + idx_t *positional_fixed, idx_t *positional_variadic, + idx_t *named_fixed, idx_t *named_variadic, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + return WithErrorHandler(err, [&]() { + auto &function = Convert(info)->function; + if (positional_fixed) { + *positional_fixed = function.GetStandardArgumentCount(); + } + if (positional_variadic) { + *positional_variadic = function.GetVarArgsCount(); + } + if (named_fixed) { + *named_fixed = function.GetKeywordOnlyArgumentCount(); + } + if (named_variadic) { + *named_variadic = function.GetKwargsCount(); + } + }); +} + +DUCKDB_V2_ERROR duckdb_v2_scalar_function_stats_get_arg_stats(duckdb_v2_scalar_function_stats_info_handle info, + idx_t index, duckdb_v2_stats_handle *stats, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(stats); + return WithErrorHandler(err, [&]() { + auto &stats_info = *Convert(info); + if (index >= stats_info.arg_stats.size()) { + throw duckdb::InvalidInputException("Index out of bounds in duckdb_v2_scalar_function_stats_get_arg_stats"); + } + *stats = Convert(&stats_info.arg_stats[index]); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_scalar_function_stats_get_result_stats(duckdb_v2_scalar_function_stats_info_handle info, + duckdb_v2_stats_handle *stats, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(stats); + return WithErrorHandler(err, [&]() { *stats = Convert(&Convert(info)->result_stats); }); +} + DUCKDB_V2_ERROR duckdb_v2_connection_register_scalar_function(duckdb_v2_connection_handle conn, duckdb_v2_scalar_function_handle function, duckdb_v2_error_info_handle *err) { diff --git a/src/duckdb/src/main/capi/v2/capi_v2_func_table.cpp b/src/duckdb/src/main/capi/v2/capi_v2_func_table.cpp index 31deaee5f..77128a981 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_func_table.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_func_table.cpp @@ -269,6 +269,25 @@ static auto Convert(CV2TablePartitionDataInfo *info) -> duckdb_v2_table_function return reinterpret_cast(info); } +class CV2TableStatsInfo { +public: + explicit CV2TableStatsInfo(BaseStatistics &result) : result_stats(CV2Stats::Writable(result)) { + } + + void *in_user_data = nullptr; + void *in_bind_data = nullptr; + idx_t in_column_index = 0; + + CV2Stats result_stats; +}; + +static auto Convert(duckdb_v2_table_function_stats_info_handle info) -> CV2TableStatsInfo * { + return reinterpret_cast(info); +} +static auto Convert(CV2TableStatsInfo *info) -> duckdb_v2_table_function_stats_info_handle { + return reinterpret_cast(info); +} + class CV2TablePartitioningInfo { public: void *in_user_data = nullptr; @@ -330,6 +349,7 @@ class CV2TableFunctionInfo : public TableFunctionInfo { duckdb_v2_table_function_filter_pushdown_callback_fn filter_pushdown_cb = nullptr; duckdb_v2_table_function_partition_data_callback_fn partition_data_cb = nullptr; duckdb_v2_table_function_partitioning_callback_fn partitioning_cb = nullptr; + duckdb_v2_table_function_stats_callback_fn stats_cb = nullptr; duckdb_v2_table_function_claim_batch_callback_fn claim_batch_cb = nullptr; duckdb_v2_table_function_get_bind_info_callback_fn get_bind_info_cb = nullptr; shared_ptr user_data = nullptr; @@ -641,6 +661,36 @@ static auto CV2TableCardinality(ClientContext &context, const FunctionData *bind return CV2TableMakeStatistics(data.cardinality, data.cardinality_is_exact); } +static auto CV2TableStats(ClientContext &context, TableFunctionGetStatisticsInput &input) + -> unique_ptr { + const auto &data = input.bind_data->Cast(); + const auto &info = *data.info; + + // The callback only describes whole declared columns, not virtual columns or fields extracted from a column + const auto &column_index = input.column_index; + if (!column_index.HasPrimaryIndex() || column_index.HasChildren() || column_index.IsVirtualColumn() || + column_index.GetPrimaryIndex() >= data.column_types.size()) { + return nullptr; + } + + auto result = BaseStatistics::CreateUnknown(data.column_types[column_index.GetPrimaryIndex()]); + CV2TableStatsInfo args(result); + args.in_user_data = info.user_data ? info.user_data->GetData() : nullptr; + args.in_bind_data = data.handle ? data.handle->GetData() : nullptr; + args.in_column_index = column_index.GetPrimaryIndex(); + + CV2ErrorInfo err = {}; + auto err_ptr = Convert(&err); + CV2CallbackContext callback_context(context); + info.stats_cb(Convert(&args), Convert(&callback_context), &err_ptr); + + if (err.HasError()) { + err.ThrowAsException(); + } + + return result.ToUnique(); +} + static auto CV2TableProgress(ClientContext &context, const FunctionData *bind_data, const GlobalTableFunctionState *global_state) -> double { const auto &data = bind_data->Cast(); @@ -826,6 +876,10 @@ class CV2TableFunction { if (info.partitioning_cb) { function.get_partition_info = CV2TableGetPartitionInfo; } + // The extended variant: setting the plain one would turn off pushing field extraction into the scan + if (info.stats_cb) { + function.statistics_extended = CV2TableStats; + } function.projection_pushdown = info.projection_pushdown; if (info.get_bind_info_cb) { function.get_bind_info = CV2TableGetBindInfo; @@ -1054,6 +1108,13 @@ duckdb_v2_table_function_set_partitioning_callback(duckdb_v2_table_function_hand return WithErrorHandler(err, [&]() { Convert(function)->info.partitioning_cb = callback; }); } +DUCKDB_V2_ERROR duckdb_v2_table_function_set_stats_callback(duckdb_v2_table_function_handle function, + duckdb_v2_table_function_stats_callback_fn callback, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(function); + return WithErrorHandler(err, [&]() { Convert(function)->info.stats_cb = callback; }); +} + DUCKDB_V2_ERROR duckdb_v2_table_function_bind_add_result_column(duckdb_v2_table_function_bind_info_handle info, const duckdb_v2_identifier_t *name, duckdb_v2_logical_type_handle type, @@ -1530,6 +1591,35 @@ duckdb_v2_table_function_partitioning_set_partition_info(duckdb_v2_table_functio [&]() { Convert(info)->out_partition_info = CV2ConvertPartitionInfo(partition_info); }); } +DUCKDB_V2_ERROR duckdb_v2_table_function_stats_get_user_data(duckdb_v2_table_function_stats_info_handle info, + void **data, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_user_data; }); +} + +DUCKDB_V2_ERROR duckdb_v2_table_function_stats_get_bind_data(duckdb_v2_table_function_stats_info_handle info, + void **data, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(data); + return WithErrorHandler(err, [&]() { *data = Convert(info)->in_bind_data; }); +} + +DUCKDB_V2_ERROR duckdb_v2_table_function_stats_get_column_index(duckdb_v2_table_function_stats_info_handle info, + idx_t *column_index, duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(column_index); + return WithErrorHandler(err, [&]() { *column_index = Convert(info)->in_column_index; }); +} + +DUCKDB_V2_ERROR duckdb_v2_table_function_stats_get_result_stats(duckdb_v2_table_function_stats_info_handle info, + duckdb_v2_stats_handle *stats, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(info); + DUCKDB_CHECK_ARG(stats); + return WithErrorHandler(err, [&]() { *stats = Convert(&Convert(info)->result_stats); }); +} + DUCKDB_V2_ERROR duckdb_v2_connection_register_table_function(duckdb_v2_connection_handle conn, duckdb_v2_table_function_handle function, duckdb_v2_error_info_handle *err) { diff --git a/src/duckdb/src/main/capi/v2/capi_v2_stats.cpp b/src/duckdb/src/main/capi/v2/capi_v2_stats.cpp new file mode 100644 index 000000000..26a7f507d --- /dev/null +++ b/src/duckdb/src/main/capi/v2/capi_v2_stats.cpp @@ -0,0 +1,51 @@ +#include "duckdb/main/capi_v2/capi_v2_internal.hpp" +#include "duckdb/main/capi_v2/capi_v2_function_internal.hpp" + +namespace duckdb::capiv2 { + +BaseStatistics &CV2Stats::GetMutableStats(const char *function_name) { + if (!mutable_stats) { + throw InvalidInputException("%s: the statistics are read-only", function_name); + } + return *mutable_stats; +} + +} // namespace duckdb::capiv2 + +//---------------------------------------------------------------------------------------------------------------------- +// Public Functions +//---------------------------------------------------------------------------------------------------------------------- + +using namespace duckdb::capiv2; + +DUCKDB_V2_ERROR duckdb_v2_stats_can_have_null(duckdb_v2_stats_handle stats, bool *out, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(stats); + DUCKDB_CHECK_ARG(out); + return WithErrorHandler(err, [&]() { *out = Convert(stats)->GetStats().CanHaveNull(); }); +} + +DUCKDB_V2_ERROR duckdb_v2_stats_can_have_valid(duckdb_v2_stats_handle stats, bool *out, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(stats); + DUCKDB_CHECK_ARG(out); + return WithErrorHandler(err, [&]() { *out = Convert(stats)->GetStats().CanHaveNoNull(); }); +} + +DUCKDB_V2_ERROR duckdb_v2_stats_set_can_have_null(duckdb_v2_stats_handle stats, bool value, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(stats); + return WithErrorHandler(err, [&]() { + auto &result = Convert(stats)->GetMutableStats("duckdb_v2_stats_set_can_have_null"); + result.Set(value ? duckdb::StatsInfo::CAN_HAVE_NULL_VALUES : duckdb::StatsInfo::CANNOT_HAVE_NULL_VALUES); + }); +} + +DUCKDB_V2_ERROR duckdb_v2_stats_set_can_have_valid(duckdb_v2_stats_handle stats, bool value, + duckdb_v2_error_info_handle *err) { + DUCKDB_CHECK_ARG(stats); + return WithErrorHandler(err, [&]() { + auto &result = Convert(stats)->GetMutableStats("duckdb_v2_stats_set_can_have_valid"); + result.Set(value ? duckdb::StatsInfo::CAN_HAVE_VALID_VALUES : duckdb::StatsInfo::CANNOT_HAVE_VALID_VALUES); + }); +} diff --git a/src/duckdb/src/main/client_context.cpp b/src/duckdb/src/main/client_context.cpp index 1631051fd..40b97122e 100644 --- a/src/duckdb/src/main/client_context.cpp +++ b/src/duckdb/src/main/client_context.cpp @@ -823,7 +823,7 @@ unique_ptr ClientContext::SubmitStatement(ClientContextLock &lock, // other types of exceptions do invalidate the current transaction result = ErrorResult(std::move(error), query); } - if (handle) { + if (handle) { // NOLINT(bugprone-use-after-move): handle is empty once it was handed out as the result // A handle that is not handed out is released without the context lock its Close would take handle->context.reset(); } diff --git a/src/duckdb/src/main/config.cpp b/src/duckdb/src/main/config.cpp index a81d4bb46..ecc1e36b1 100644 --- a/src/duckdb/src/main/config.cpp +++ b/src/duckdb/src/main/config.cpp @@ -820,7 +820,8 @@ optional_idx DBConfig::ParseMemoryLimitSlurm(const string &arg) { // Right now we only really care about access mode when comparing DBConfigs bool DBConfigOptions::operator==(const DBConfigOptions &other) const { - return other.access_mode == access_mode && other.user_options == user_options; + return other.access_mode == access_mode && other.user_options == user_options && + other.configuration_env == configuration_env; } bool DBConfig::operator==(const DBConfig &other) { diff --git a/src/duckdb/src/main/database.cpp b/src/duckdb/src/main/database.cpp index 6817c5f23..7aa8febbd 100644 --- a/src/duckdb/src/main/database.cpp +++ b/src/duckdb/src/main/database.cpp @@ -6,6 +6,7 @@ #include "duckdb/catalog/catalog.hpp" #include "duckdb/main/http/http_util.hpp" +#include "duckdb/main/os_util.hpp" #include "duckdb/main/http/http_transport_manager.hpp" #include "duckdb/main/extension/external_extension_provider.hpp" #include "duckdb/common/virtual_file_system.hpp" @@ -523,6 +524,7 @@ Allocator &Allocator::Get(AttachedDatabase &db) { void DatabaseInstance::Configure(DBConfig &new_config, const char *database_path) { config.options = new_config.options; config.user_settings = new_config.user_settings; + os_util = make_uniq(*this, config.options.configuration_env); // carry over a capability set handed to us, so a database created by code with its own copy of // DuckDB can be given the extensions the binary that created it links config.linked_extensions = new_config.linked_extensions; @@ -707,6 +709,11 @@ LogManager &DatabaseInstance::GetLogManager() const { return *log_manager; } +OSUtil &DatabaseInstance::GetOSUtil() { + D_ASSERT(os_util); + return *os_util; +} + MetricsManager &DatabaseInstance::GetMetricsManager() { return *metrics_manager; } diff --git a/src/duckdb/src/main/os_util.cpp b/src/duckdb/src/main/os_util.cpp new file mode 100644 index 000000000..6ba4f13ac --- /dev/null +++ b/src/duckdb/src/main/os_util.cpp @@ -0,0 +1,106 @@ +#include "duckdb/main/os_util.hpp" + +#include "duckdb/common/exception.hpp" +#include "duckdb/common/windows_util.hpp" +#include "duckdb/main/database.hpp" +#include "duckdb/main/settings.hpp" + +#include + +namespace duckdb { + +OSUtil::OSUtil(DatabaseInstance &db, unordered_set configuration_env_p) + : db(db), configuration_env(std::move(configuration_env_p)) { +} + +OSUtil &OSUtil::Get(DatabaseInstance &db) { + return db.GetOSUtil(); +} + +bool OSUtil::IsConfigurationEnv(const string &name) { + // the home directory, the resource limits of the job the database runs in, its proxy and its time zone + static const char *CONFIGURATION_ENV[] = { + "HOME", "USERPROFILE", "SLURM_CPUS_ON_NODE", "SLURM_MEM_PER_NODE", "SLURM_MEM_PER_CPU", "HTTP_PROXY", "TZ"}; + for (auto known : CONFIGURATION_ENV) { + if (name == known) { + return true; + } + } + return false; +} + +bool OSUtil::GetConfigurationEnv(const string &name, string &value) { + if (!IsConfigurationEnv(name)) { + throw InternalException("Environment variable \"%s\" is not a configuration variable of the engine - read it " + "through the database with GetEnv", + name); + } + return ReadEnv(name, value); +} + +string OSUtil::GetConfigurationEnv(const string &name) { + string value; + if (!GetConfigurationEnv(name, value)) { + return string(); + } + return value; +} + +bool OSUtil::GetEnv(const string &name, string &value) { + if (!Settings::Get(db)) { + throw PermissionException( + "Cannot read environment variable \"%s\" - environment access is disabled by configuration", name); + } + return ReadEnv(name, value); +} + +string OSUtil::GetEnv(const string &name) { + string value; + if (!GetEnv(name, value)) { + return string(); + } + return value; +} + +bool OSUtil::GetEnvUnrestricted(const string &name, string &value) { + if (!IsConfigurationEnv(name) && configuration_env.find(name) == configuration_env.end()) { + throw InternalException("Environment variable \"%s\" is not a configuration variable - read it with GetEnv", + name); + } + return ReadEnv(name, value); +} + +string OSUtil::GetEnvUnrestricted(const string &name) { + string value; + if (!GetEnvUnrestricted(name, value)) { + return string(); + } + return value; +} + +#ifndef _WIN32 + +bool OSUtil::ReadEnv(const string &name, string &value) { + const char *env = std::getenv(name.c_str()); + if (!env) { + return false; + } + value = env; + return true; +} + +#else + +bool OSUtil::ReadEnv(const string &name, string &value) { + auto name_w = WindowsUtil::UTF8ToUnicode(name.c_str()); + auto value_w = _wgetenv(name_w.c_str()); + if (!value_w) { + return false; + } + value = WindowsUtil::UnicodeToUTF8(value_w); + return true; +} + +#endif + +} // namespace duckdb diff --git a/src/duckdb/src/parser/expression/operator_expression.cpp b/src/duckdb/src/parser/expression/operator_expression.cpp index 8b8e36f12..160bf37e7 100644 --- a/src/duckdb/src/parser/expression/operator_expression.cpp +++ b/src/duckdb/src/parser/expression/operator_expression.cpp @@ -1,7 +1,10 @@ #include "duckdb/parser/expression/operator_expression.hpp" #include "duckdb/parser/expression/function_expression.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" #include "duckdb/common/exception.hpp" +#include "duckdb/common/serializer/serializer.hpp" +#include "duckdb/common/serializer/deserializer.hpp" namespace duckdb { @@ -31,4 +34,64 @@ unique_ptr OperatorExpression::EmptySliceBound() { return make_uniq("list_value", vector>()); } +namespace { + +//! Storage before v2.0 encodes an omitted slice bound as the constant INTEGER[] [] +Value LegacyEmptySliceBoundValue() { + return Value::LIST(LogicalType::INTEGER, vector()); +} + +//! Serializes as the pre-v2.0 ConstantExpression holding the legacy empty slice bound +struct LegacyEmptySliceBound { + void Serialize(Serializer &serializer) const { + serializer.WriteProperty(100, "class", ExpressionClass::CONSTANT); + serializer.WriteProperty(101, "type", ExpressionType::VALUE_CONSTANT); + serializer.WriteProperty(200, "value", LegacyEmptySliceBoundValue()); + } +}; + +bool IsSliceBound(const OperatorExpression &expr, idx_t child_idx) { + return expr.GetExpressionType() == ExpressionType::ARRAY_SLICE && (child_idx == 1 || child_idx == 2); +} + +bool IsEmptySliceBoundExpression(const ParsedExpression &expr) { + if (expr.GetExpressionClass() != ExpressionClass::FUNCTION) { + return false; + } + auto &function = expr.Cast(); + return function.FunctionName() == "list_value" && function.GetArguments().empty(); +} + +} // namespace + +void OperatorExpression::Serialize(Serializer &serializer) const { + ParsedExpression::Serialize(serializer); + if (serializer.ShouldSerialize(StorageVersion::V2_0_0) || GetExpressionType() != ExpressionType::ARRAY_SLICE) { + serializer.WritePropertyWithDefault>>(200, "children", children); + return; + } + LegacyEmptySliceBound legacy_bound; + serializer.WriteList(200, "children", children.size(), [&](Serializer::List &list, idx_t i) { + if (IsSliceBound(*this, i) && IsEmptySliceBoundExpression(*children[i])) { + list.WriteElement(optional_ptr(legacy_bound)); + } else { + list.WriteElement(children[i]); + } + }); +} + +unique_ptr OperatorExpression::Deserialize(Deserializer &deserializer) { + auto result = duckdb::unique_ptr(new OperatorExpression(deserializer.Get())); + deserializer.ReadPropertyWithDefault>>(200, "children", result->children); + if (result->GetExpressionType() == ExpressionType::ARRAY_SLICE) { + auto legacy_bound = ConstantExpression::FromValue(LegacyEmptySliceBoundValue()); + for (idx_t i = 0; i < result->children.size(); i++) { + if (IsSliceBound(*result, i) && result->children[i]->Equals(*legacy_bound)) { + result->children[i] = EmptySliceBound(); + } + } + } + return std::move(result); +} + } // namespace duckdb diff --git a/src/duckdb/src/storage/data_table.cpp b/src/duckdb/src/storage/data_table.cpp index a51fa2cbe..552c5959e 100644 --- a/src/duckdb/src/storage/data_table.cpp +++ b/src/duckdb/src/storage/data_table.cpp @@ -1,4 +1,6 @@ #include "duckdb/storage/data_table.hpp" +#include "duckdb/planner/constraints/bound_not_null_constraint.hpp" +#include "duckdb/planner/constraints/bound_unique_constraint.hpp" #include "duckdb/planner/binder.hpp" #include "duckdb/storage/table/data_table_info.hpp" #include "duckdb/transaction/commit_state.hpp" @@ -174,6 +176,12 @@ DataTable::DataTable(ClientContext &context, DataTable &parent, BoundConstraint if (constraint.type != ConstraintType::UNIQUE) { VerifyNewConstraint(local_storage, parent, constraint); + } else if (constraint.Cast().is_primary_key) { + // the key columns become NOT NULL: verify them under the append lock, including rows committed concurrently + for (auto &key : constraint.Cast().keys) { + BoundNotNullConstraint not_null(key); + VerifyNewConstraint(local_storage, parent, not_null); + } } local_storage.MoveStorage(parent, *this); parent.version = DataTableVersion::ALTERED; diff --git a/src/duckdb/src/storage/serialization/serialize_parsed_expression.cpp b/src/duckdb/src/storage/serialization/serialize_parsed_expression.cpp index bade21ab3..a59996434 100644 --- a/src/duckdb/src/storage/serialization/serialize_parsed_expression.cpp +++ b/src/duckdb/src/storage/serialization/serialize_parsed_expression.cpp @@ -231,17 +231,6 @@ unique_ptr LambdaRefExpression::Deserialize(Deserializer &dese return std::move(result); } -void OperatorExpression::Serialize(Serializer &serializer) const { - ParsedExpression::Serialize(serializer); - serializer.WritePropertyWithDefault>>(200, "children", children); -} - -unique_ptr OperatorExpression::Deserialize(Deserializer &deserializer) { - auto result = duckdb::unique_ptr(new OperatorExpression(deserializer.Get())); - deserializer.ReadPropertyWithDefault>>(200, "children", result->children); - return std::move(result); -} - void ParameterExpression::Serialize(Serializer &serializer) const { ParsedExpression::Serialize(serializer); serializer.WritePropertyWithDefault(200, "identifier", identifier); diff --git a/src/duckdb/ub_src_main.cpp b/src/duckdb/ub_src_main.cpp index 132382d1a..0c1e275e1 100644 --- a/src/duckdb/ub_src_main.cpp +++ b/src/duckdb/ub_src_main.cpp @@ -46,6 +46,8 @@ #include "src/main/external_resources_manager.cpp" +#include "src/main/os_util.cpp" + #include "src/main/parse_iterator.cpp" #include "src/main/parser_context.cpp" diff --git a/src/duckdb/ub_src_main_capi_v2.cpp b/src/duckdb/ub_src_main_capi_v2.cpp index b94d045db..f5a3043ac 100644 --- a/src/duckdb/ub_src_main_capi_v2.cpp +++ b/src/duckdb/ub_src_main_capi_v2.cpp @@ -76,6 +76,8 @@ #include "src/main/capi/v2/capi_v2_statement.cpp" +#include "src/main/capi/v2/capi_v2_stats.cpp" + #include "src/main/capi/v2/capi_v2_stubs.cpp" #include "src/main/capi/v2/capi_v2_tokenizer.cpp" From f183aa2155050a0d0bfe5a8cfc3cc1255f8b5de4 Mon Sep 17 00:00:00 2001 From: DuckLabs GitHub Bot Date: Tue, 20 Oct 2026 02:56:19 +0000 Subject: [PATCH 6/6] Update vendored DuckDB sources to 0b272c23b19 --- .../aggregate/algebraic/avg.cpp | 2 +- .../aggregate/distributive/sum.cpp | 2 +- .../core_functions/aggregate/sum_helpers.hpp | 18 + .../core_functions/scalar/debug_functions.hpp | 6 +- .../scalar/string_characters.hpp | 55 + .../core_functions/scalar/date/make_date.cpp | 11 +- .../core_functions/scalar/debug/index_key.cpp | 200 +- .../scalar/string/damerau_levenshtein.cpp | 22 +- .../core_functions/scalar/string/hamming.cpp | 19 +- .../core_functions/scalar/string/jaccard.cpp | 27 +- .../scalar/string/jaro_winkler.cpp | 41 +- .../scalar/string/levenshtein.cpp | 20 +- .../scalar/struct/struct_insert.cpp | 2 +- .../json/include/json_deserializer.hpp | 6 + src/duckdb/extension/json/json_common.cpp | 5 +- .../json/json_functions/json_create.cpp | 8 +- .../json_functions/json_serialize_plan.cpp | 6 +- .../json_functions/json_serialize_sql.cpp | 2 +- .../json/json_functions/read_json.cpp | 2 +- .../extension/parquet/column_reader.cpp | 5 +- .../decoder/byte_stream_split_decoder.cpp | 68 +- .../parquet/decoder/dictionary_decoder.cpp | 4 +- .../parquet/include/parquet_bss_decoder.hpp | 41 +- .../include/parquet_rle_bp_decoder.hpp | 2 +- .../parquet/parquet_column_schema.cpp | 25 +- .../extension/parquet/parquet_crypto.cpp | 5 +- .../extension/parquet/parquet_reader.cpp | 9 +- .../extension/parquet/parquet_statistics.cpp | 4 +- .../extension/parquet/parquet_timestamp.cpp | 9 +- .../parquet/reader/list_column_reader.cpp | 3 + ...nerated_extension_loader_package_build.cpp | 8 +- .../catalog_entry/duck_schema_entry.cpp | 3 +- .../catalog_entry/duck_table_entry.cpp | 18 +- .../scalar_function_catalog_entry.cpp | 4 +- .../catalog_entry/sequence_catalog_entry.cpp | 2 +- .../table_function_catalog_entry.cpp | 2 +- .../catalog_entry/view_catalog_entry.cpp | 2 +- src/duckdb/src/catalog/catalog_set.cpp | 17 +- src/duckdb/src/catalog/dependency_list.cpp | 4 +- src/duckdb/src/catalog/dependency_manager.cpp | 2 +- src/duckdb/src/common/allocator/allocator.cpp | 44 - src/duckdb/src/common/bignum.cpp | 283 +- .../src/common/exception_format_value.cpp | 35 +- src/duckdb/src/common/file_system.cpp | 21 +- src/duckdb/src/common/local_file_system.cpp | 50 +- .../multi_file/multi_file_column_mapper.cpp | 4 +- .../common/multi_file/multi_file_reader.cpp | 6 +- .../common/serializer/async_write_queue.cpp | 4 +- src/duckdb/src/common/sort/sorted_run.cpp | 144 +- .../src/common/sort/sorted_run_merger.cpp | 9 +- src/duckdb/src/common/string_util.cpp | 16 + src/duckdb/src/common/types/blob.cpp | 6 + src/duckdb/src/common/types/data_chunk.cpp | 8 +- .../types/row/tuple_data_collection.cpp | 27 +- src/duckdb/src/common/types/vector.cpp | 7 + src/duckdb/src/common/types/vector_cache.cpp | 2 + src/duckdb/src/common/vector/flat_vector.cpp | 9 +- src/duckdb/src/common/vector/list_vector.cpp | 4 +- .../src/common/vector/string_vector.cpp | 4 +- .../src/execution/aggregate_hashtable.cpp | 2 +- .../execution/index/fixed_size_allocator.cpp | 17 +- src/duckdb/src/execution/join_hashtable.cpp | 4 +- .../physical_ungrouped_aggregate.cpp | 2 +- .../operator/join/physical_hash_join.cpp | 13 + .../aggregate/distributive/decimal_avg.cpp | 3 +- .../aggregate/distributive/minmax.cpp | 2 +- .../aggregate/sorted_aggregate_function.cpp | 6 +- src/duckdb/src/function/cast/struct_cast.cpp | 2 +- src/duckdb/src/function/cast/union_casts.cpp | 2 +- .../src/function/cast/vector_cast_helpers.cpp | 5 + src/duckdb/src/function/cast_rules.cpp | 1 - .../src/function/combine_types_rules.cpp | 19 + src/duckdb/src/function/function.cpp | 8 +- src/duckdb/src/function/function_binder.cpp | 22 +- src/duckdb/src/function/macro_function.cpp | 2 +- .../compress_geometry.cpp | 12 +- .../function/scalar/operator/arithmetic.cpp | 74 +- .../src/function/scalar/strftime_format.cpp | 35 +- .../function/scalar/struct/struct_extract.cpp | 4 +- .../scalar/system/aggregate_export.cpp | 4 +- .../src/function/scalar/system/write_log.cpp | 2 +- .../scalar/variant/variant_bind_utils.cpp | 2 +- src/duckdb/src/function/scalar_function.cpp | 2 +- src/duckdb/src/function/table/checkpoint.cpp | 4 +- .../src/function/table/query_function.cpp | 8 + src/duckdb/src/function/table/sniff_csv.cpp | 4 +- .../function/table/version/pragma_version.cpp | 6 +- .../src/function/window/window_executor.cpp | 6 + .../src/include/duckdb/common/allocator.hpp | 60 +- .../src/include/duckdb/common/bignum.hpp | 30 + .../duckdb/common/exception_format_value.hpp | 2 + .../src/include/duckdb/common/file_system.hpp | 4 + .../src/include/duckdb/common/lru_cache.hpp | 39 +- .../common/multi_file/base_file_reader.hpp | 3 + .../common/multi_file/multi_file_function.hpp | 22 +- .../common/serializer/async_write_queue.hpp | 2 + .../duckdb/common/sorting/sort_key.hpp | 24 +- .../include/duckdb/common/types/bignum.hpp | 2 - .../types/row/tuple_data_collection.hpp | 3 +- .../duckdb/execution/aggregate_hashtable.hpp | 2 +- .../execution/index/art/art_operator.hpp | 2 +- .../duckdb/execution/join_hashtable.hpp | 2 +- .../operator/join/physical_hash_join.hpp | 1 + .../duckdb/function/scalar_function.hpp | 2 +- src/duckdb/src/include/duckdb/main/config.hpp | 2 +- .../main/database_file_path_manager.hpp | 2 + .../include/duckdb/main/database_manager.hpp | 2 + .../main/http/http_transport_manager.hpp | 1 - .../src/include/duckdb/main/settings.hpp | 17 +- .../optimizer/column_binding_replacer.hpp | 21 + .../optimizer/statistics_propagator.hpp | 4 + .../duckdb/parallel/scan_read_ahead.hpp | 23 +- .../duckdb/parallel/task_scheduler.hpp | 7 +- .../duckdb/parser/peg/compiled_grammar.hpp | 4 +- .../src/include/duckdb/parser/peg/matcher.hpp | 11 +- .../parser/peg/matcher/list_matcher.hpp | 17 + .../duckdb/parser/peg/parsed_grammar.hpp | 3 + .../include/duckdb/parser/peg/peg_rule_id.hpp | 1215 +++++++++ .../parser/peg/transformer/parse_result.hpp | 29 +- .../peg/transformer/peg_transformer.hpp | 21 +- .../peg/transformer/transform_result.hpp | 28 +- .../duckdb/storage/arena_allocator.hpp | 26 +- .../duckdb/storage/block_allocator.hpp | 105 +- .../duckdb/storage/buffer/buffer_pool.hpp | 11 +- .../include/duckdb/storage/buffer_manager.hpp | 4 +- .../external_file_cache.hpp | 10 +- .../include/duckdb/storage/object_cache.hpp | 4 + .../storage/standard_buffer_manager.hpp | 10 +- .../duckdb/storage/table/row_group.hpp | 18 +- .../storage/table/row_group_collection.hpp | 4 +- .../duckdb/storage/table/table_index_list.hpp | 3 +- src/duckdb/src/main/appender.cpp | 2 +- .../main/capi/v2/capi_v2_custom_option.cpp | 2 +- src/duckdb/src/main/capi/v2/capi_v2_func.cpp | 2 +- .../src/main/capi/v2/capi_v2_func_pragma.cpp | 2 +- .../src/main/capi/v2/capi_v2_func_table.cpp | 14 +- src/duckdb/src/main/client_context.cpp | 4 +- src/duckdb/src/main/client_data.cpp | 2 +- src/duckdb/src/main/config.cpp | 9 +- src/duckdb/src/main/database.cpp | 6 +- .../src/main/database_file_path_manager.cpp | 5 + src/duckdb/src/main/database_manager.cpp | 19 + .../src/main/extension/extension_loader.cpp | 10 +- .../src/main/http/http_transport_manager.cpp | 23 +- src/duckdb/src/main/secret/secret.cpp | 2 +- src/duckdb/src/main/secret/secret_manager.cpp | 2 +- src/duckdb/src/main/secret/secret_storage.cpp | 4 +- .../src/main/settings/custom_settings.cpp | 10 +- .../src/optimizer/column_binding_replacer.cpp | 178 +- .../src/optimizer/grouping_sets_optimizer.cpp | 9 +- .../optimizer/rule/empty_needle_removal.cpp | 10 +- .../rule/in_clause_simplification_rule.cpp | 4 + .../expression/propagate_function.cpp | 2 +- .../operator/propagate_aggregate.cpp | 5 + .../operator/propagate_cross_product.cpp | 3 + .../statistics/operator/propagate_join.cpp | 25 + .../src/optimizer/statistics_propagator.cpp | 24 + src/duckdb/src/parallel/scan_read_ahead.cpp | 70 +- src/duckdb/src/parallel/task_scheduler.cpp | 55 +- .../src/parallel/task_scheduler_pool.cpp | 34 +- src/duckdb/src/parser/column_list.cpp | 2 +- .../parser/expression/function_expression.cpp | 10 + .../parser/expression/window_expression.cpp | 10 + .../parser/parsed_data/alter_table_info.cpp | 15 + .../src/parser/parsed_data/load_info.cpp | 2 +- .../src/parser/peg/compiled_grammar.cpp | 31 + src/duckdb/src/parser/peg/matcher.cpp | 9 + src/duckdb/src/parser/peg/matcher_process.cpp | 151 +- src/duckdb/src/parser/peg/peg_rule_id.cpp | 1228 +++++++++ .../parser/peg/tokenizer/base_tokenizer.cpp | 3 + .../peg/transformer/peg_transformer.cpp | 41 +- .../transformer/peg_transformer_factory.cpp | 8 +- .../transformer/transform_create_table.cpp | 19 +- .../peg/transformer/transform_expression.cpp | 3 +- .../transform_generated_trampoline.cpp | 2267 +++++++++-------- .../transform_generic_copy_option.cpp | 2 +- .../peg/transformer/transform_select.cpp | 8 +- .../src/parser/statement/export_statement.cpp | 5 +- src/duckdb/src/planner/bind_context.cpp | 4 +- src/duckdb/src/planner/binder.cpp | 2 +- .../expression/bind_aggregate_expression.cpp | 2 +- .../expression/bind_between_expression.cpp | 4 +- .../expression/bind_collate_expression.cpp | 2 +- .../expression/bind_function_expression.cpp | 15 + .../expression/bind_star_expression.cpp | 2 +- .../expression/bind_type_expression.cpp | 4 +- .../expression/bind_window_expression.cpp | 8 +- .../binder/query_node/bind_cte_node.cpp | 2 +- .../query_node/bind_recursive_cte_node.cpp | 6 +- .../binder/query_node/bind_setop_node.cpp | 2 +- .../query_node/bind_trigger_expansion.cpp | 4 +- .../binder/query_node/plan_subquery.cpp | 4 + .../planner/binder/statement/bind_copy.cpp | 6 + .../planner/binder/statement/bind_create.cpp | 12 +- .../binder/statement/bind_create_table.cpp | 4 +- .../planner/binder/statement/bind_export.cpp | 2 +- .../planner/binder/statement/bind_insert.cpp | 36 + .../planner/binder/statement/bind_vacuum.cpp | 2 +- .../planner/binder/tableref/bind_pivot.cpp | 4 +- .../planner/binder/tableref/bind_showref.cpp | 2 +- .../binder/tableref/bind_table_function.cpp | 22 +- .../expression/bound_cast_expression.cpp | 3 + src/duckdb/src/planner/expression_binder.cpp | 2 +- .../expression_binder/base_select_binder.cpp | 3 +- .../expression_binder/group_binder.cpp | 2 +- .../expression_binder/having_binder.cpp | 4 +- .../expression_binder/select_bind_state.cpp | 4 +- .../planner/filter/table_filter_functions.cpp | 2 +- .../planner/operator/logical_copy_to_file.cpp | 2 +- .../src/planner/operator/logical_get.cpp | 2 +- .../subquery/delim_join_cte_rewriter.cpp | 16 +- .../subquery/flatten_dependent_join.cpp | 17 + src/duckdb/src/planner/table_binding.cpp | 5 +- src/duckdb/src/planner/table_filter_set.cpp | 2 +- src/duckdb/src/storage/arena_allocator.cpp | 11 +- src/duckdb/src/storage/block_allocator.cpp | 819 +++++- src/duckdb/src/storage/buffer/buffer_pool.cpp | 73 +- src/duckdb/src/storage/buffer_manager.cpp | 2 +- src/duckdb/src/storage/data_table.cpp | 14 +- .../caching_file_system.cpp | 28 +- .../external_file_cache.cpp | 33 +- src/duckdb/src/storage/index_entry.cpp | 2 +- src/duckdb/src/storage/local_storage.cpp | 34 +- src/duckdb/src/storage/object_cache.cpp | 30 + .../src/storage/standard_buffer_manager.cpp | 126 +- src/duckdb/src/storage/storage_manager.cpp | 5 - src/duckdb/src/storage/table/row_group.cpp | 26 +- .../storage/table/row_group_collection.cpp | 6 +- src/duckdb/src/storage/table_index_list.cpp | 17 +- src/duckdb/src/storage/wal_replay.cpp | 2 +- src/duckdb/src/storage/write_ahead_log.cpp | 4 +- src/duckdb/src/transaction/commit_state.cpp | 10 +- .../src/transaction/meta_transaction.cpp | 2 +- src/duckdb/third_party/ska_sort/ska_sort.hpp | 51 +- src/duckdb/ub_src_parser_peg.cpp | 2 + src/duckdb/ub_src_storage.cpp | 2 + 236 files changed, 7238 insertions(+), 2076 deletions(-) create mode 100644 src/duckdb/extension/core_functions/include/core_functions/scalar/string_characters.hpp create mode 100644 src/duckdb/src/include/duckdb/parser/peg/peg_rule_id.hpp create mode 100644 src/duckdb/src/parser/peg/peg_rule_id.cpp create mode 100644 src/duckdb/src/storage/object_cache.cpp diff --git a/src/duckdb/extension/core_functions/aggregate/algebraic/avg.cpp b/src/duckdb/extension/core_functions/aggregate/algebraic/avg.cpp index 2b7768041..d80c52e58 100644 --- a/src/duckdb/extension/core_functions/aggregate/algebraic/avg.cpp +++ b/src/duckdb/extension/core_functions/aggregate/algebraic/avg.cpp @@ -321,7 +321,7 @@ AggregateFunction GetAverageAggregate(PhysicalType type) { unique_ptr BindDecimalAvg(BindAggregateFunctionInput &input) { auto &function = input.GetBoundFunction(); auto &arguments = input.GetArguments(); - auto decimal_type = arguments[0]->GetReturnType(); + auto decimal_type = BindDecimalArgument(input.GetClientContext(), arguments[0], "avg"); function.ReplaceImplementation(GetAverageAggregate(decimal_type.InternalType())); function.SetName("avg"); function.GetArguments()[0] = decimal_type; diff --git a/src/duckdb/extension/core_functions/aggregate/distributive/sum.cpp b/src/duckdb/extension/core_functions/aggregate/distributive/sum.cpp index bcb7594c2..e7324ccbb 100644 --- a/src/duckdb/extension/core_functions/aggregate/distributive/sum.cpp +++ b/src/duckdb/extension/core_functions/aggregate/distributive/sum.cpp @@ -481,7 +481,7 @@ AggregateFunction GetDecimalSumAggregate(PhysicalType type) { unique_ptr BindDecimalSum(BindAggregateFunctionInput &input) { auto &function = input.GetBoundFunction(); auto &arguments = input.GetArguments(); - auto decimal_type = arguments[0]->GetReturnType(); + auto decimal_type = BindDecimalArgument(input.GetClientContext(), arguments[0], "sum"); function.ReplaceImplementation(GetDecimalSumAggregate(decimal_type.InternalType())); function.SetName("sum"); function.GetArguments()[0] = decimal_type; diff --git a/src/duckdb/extension/core_functions/include/core_functions/aggregate/sum_helpers.hpp b/src/duckdb/extension/core_functions/include/core_functions/aggregate/sum_helpers.hpp index 7c35853ae..1986896fa 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/aggregate/sum_helpers.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/aggregate/sum_helpers.hpp @@ -15,6 +15,7 @@ #include "duckdb/function/aggregate_state.hpp" #include "duckdb/function/aggregate_state_layout.hpp" #include "duckdb/common/operator/cast_operators.hpp" +#include "duckdb/planner/expression/bound_cast_expression.hpp" #include #include @@ -269,4 +270,21 @@ struct BaseSumOperation { } }; +//! The DECIMAL overloads can be selected for non-DECIMAL arguments (e.g. through named arguments) - the bind runs +//! before the argument is cast, so cast it to the DECIMAL type that represents its values here +inline LogicalType BindDecimalArgument(ClientContext &context, unique_ptr &argument, + const string &function_name) { + auto type = argument->GetReturnType(); + if (type.id() == LogicalTypeId::DECIMAL) { + return type; + } + uint8_t width, scale; + if (!type.GetDecimalProperties(width, scale)) { + throw BinderException("%s: cannot compute the DECIMAL aggregate of type %s", function_name, type.ToString()); + } + auto decimal_type = LogicalType::DECIMAL(width, scale); + argument = BoundCastExpression::AddCastToType(context, std::move(argument), decimal_type); + return decimal_type; +} + } // namespace duckdb diff --git a/src/duckdb/extension/core_functions/include/core_functions/scalar/debug_functions.hpp b/src/duckdb/extension/core_functions/include/core_functions/scalar/debug_functions.hpp index b5ab6002d..25f3c00a9 100644 --- a/src/duckdb/extension/core_functions/include/core_functions/scalar/debug_functions.hpp +++ b/src/duckdb/extension/core_functions/include/core_functions/scalar/debug_functions.hpp @@ -37,9 +37,9 @@ struct SleepMsFun { struct IndexKeyFun { static constexpr const char *Name = "index_key"; - static constexpr const char *Parameters = "path,name,..."; - static constexpr const char *Description = "Generate index keys for a named index on a table (specified by a path struct with required 'table' and optional 'catalog'/'schema' fields) from key column arguments. Currently only supports ART indexes."; - static constexpr const char *Example = "index_key({'schema': 'main', 'table': 'tbl3'}, 'tbl3_idx', key1, key2, key3)"; + static constexpr const char *Parameters = "path,..."; + static constexpr const char *Description = "Generate index keys from key column arguments. The path struct requires a 'table' field, optional 'catalog'/'schema' fields, and either an 'index' field naming a user-defined index, or a 'constraint' field ('primary', 'unique' or 'foreign') selecting a constraint index. A 'columns' list selects the constraint by its key columns; it is only required when the table has multiple constraints of the given type. Currently only supports ART indexes."; + static constexpr const char *Example = "index_key({'schema': 'main', 'table': 'tbl3', 'index': 'tbl3_idx'}, key1, key2, key3)"; static constexpr const char *Categories = ""; static ScalarFunction GetFunction(); diff --git a/src/duckdb/extension/core_functions/include/core_functions/scalar/string_characters.hpp b/src/duckdb/extension/core_functions/include/core_functions/scalar/string_characters.hpp new file mode 100644 index 000000000..cda7064ae --- /dev/null +++ b/src/duckdb/extension/core_functions/include/core_functions/scalar/string_characters.hpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// core_functions/scalar/string_characters.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/types/string_type.hpp" +#include "duckdb/common/vector.hpp" +#include "utf8proc_wrapper.hpp" + +namespace duckdb { + +//! Whether the string only consists of ASCII characters, i.e. every byte is a character +inline bool StringIsAscii(const string_t &str) { + auto data = str.GetData(); + for (idx_t i = 0; i < str.GetSize(); i++) { + if (static_cast(data[i]) >= 0x80) { + return false; + } + } + return true; +} + +//! The characters (code points) of a UTF-8 string - a byte that is not valid UTF-8 is a character of its own +inline vector GetStringCharacters(const string_t &str) { + vector result; + auto data = str.GetData(); + auto len = str.GetSize(); + result.reserve(len); + idx_t pos = 0; + while (pos < len) { + auto byte = static_cast(data[pos]); + if (byte < 0x80) { + result.push_back(byte); + pos++; + continue; + } + int sz = 1; + auto codepoint = Utf8Proc::UTF8ToCodepoint(data + pos, sz, len - pos); + if (codepoint < 0 || sz <= 0) { + result.push_back(byte); + pos++; + continue; + } + result.push_back(codepoint); + pos += UnsafeNumericCast(sz); + } + return result; +} + +} // namespace duckdb diff --git a/src/duckdb/extension/core_functions/scalar/date/make_date.cpp b/src/duckdb/extension/core_functions/scalar/date/make_date.cpp index 158b3e28a..45b3a5ec7 100644 --- a/src/duckdb/extension/core_functions/scalar/date/make_date.cpp +++ b/src/duckdb/extension/core_functions/scalar/date/make_date.cpp @@ -75,17 +75,12 @@ struct MakeTimeOperator { if (!std::isfinite(static_cast(ss))) { throw ConversionException("Time out of range: seconds must be a finite number"); } - // Have to check this separately because safe casting of DOUBLE => INT32 can round. - int32_t ss_32 = 0; - if (ss < 0 || ss > Interval::SECS_PER_MINUTE) { - ss_32 = Cast::Operation(ss); - } else { - ss_32 = LossyNumericCast(ss); - } + // Truncate the seconds: safe casting of DOUBLE => INT32 rounds. + auto ss_32 = Cast::Operation(std::floor(ss)); auto micros = LossyNumericCast(std::round((ss - ss_32) * Interval::MICROS_PER_SEC)); if (!Time::IsValidTime(hh_32, mm_32, ss_32, micros)) { - throw ConversionException("Time out of range: %d:%d:%d.%d", hh_32, mm_32, ss_32, micros); + throw ConversionException("Time out of range: %d:%d:%d.%06d", hh_32, mm_32, ss_32, micros); } return Time::FromTime(hh_32, mm_32, ss_32, micros); } diff --git a/src/duckdb/extension/core_functions/scalar/debug/index_key.cpp b/src/duckdb/extension/core_functions/scalar/debug/index_key.cpp index e85e190f2..d33c56ae9 100644 --- a/src/duckdb/extension/core_functions/scalar/debug/index_key.cpp +++ b/src/duckdb/extension/core_functions/scalar/debug/index_key.cpp @@ -2,15 +2,19 @@ #include "duckdb/catalog/catalog.hpp" #include "duckdb/catalog/catalog_entry/duck_table_entry.hpp" +#include "duckdb/catalog/catalog_entry/index_catalog_entry.hpp" +#include "duckdb/catalog/catalog_entry/schema_catalog_entry.hpp" #include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp" #include "duckdb/common/constants.hpp" +#include "duckdb/common/enum_util.hpp" #include "duckdb/common/exception.hpp" #include "duckdb/common/string_util.hpp" #include "duckdb/execution/expression_executor.hpp" #include "duckdb/execution/index/art/art.hpp" #include "duckdb/execution/index/art/art_key.hpp" #include "duckdb/function/scalar_function.hpp" -#include "duckdb/main/table_description.hpp" +#include "duckdb/parser/constraints/foreign_key_constraint.hpp" +#include "duckdb/parser/constraints/unique_constraint.hpp" #include "duckdb/parser/parsed_data/parse_info.hpp" #include "duckdb/planner/binder.hpp" #include "duckdb/planner/expression/bound_function_expression.hpp" @@ -20,30 +24,70 @@ namespace duckdb { namespace { -static constexpr idx_t INDEX_KEY_FIXED_ARGS = 2; +constexpr idx_t INDEX_KEY_FIXED_ARGS = 1; + +//! Selects either a user-defined index by name, or a constraint index by constraint type and columns. +struct IndexKeyPath { + QualifiedName table; + Identifier index_name; + IndexConstraintType constraint_type = IndexConstraintType::NONE; + vector columns; +}; + +IndexConstraintType ParseConstraintType(const string &value) { + if (StringUtil::CIEquals(value, "primary")) { + return IndexConstraintType::PRIMARY; + } + if (StringUtil::CIEquals(value, "unique")) { + return IndexConstraintType::UNIQUE; + } + if (StringUtil::CIEquals(value, "foreign")) { + return IndexConstraintType::FOREIGN; + } + throw BinderException("index_key: constraint must be one of 'primary', 'unique' or 'foreign'"); +} + +IndexKeyPath EvaluateIndexKeyPath(const Value &input_struct) { + if (input_struct.IsNull()) { + throw BinderException("index_key: path parameter cannot be NULL"); + } + auto &field_types = StructType::GetChildTypes(input_struct.type()); + auto &field_values = StructValue::GetChildren(input_struct); -static TableDescription ExtractTableDescription(const child_list_t &field_types, - const vector &field_values) { unordered_map fields; fields["catalog"] = INVALID_CATALOG; fields["schema"] = DEFAULT_SCHEMA; fields["table"] = ""; + fields["index"] = ""; + fields["constraint"] = ""; + IndexKeyPath path; for (idx_t i = 0; i < field_types.size(); i++) { auto field_name = StringUtil::Lower(field_types[i].first.GetIdentifierName()); - - if (fields.find(field_name) == fields.end()) { - throw BinderException("index_key: unknown field %s in path", field_types[i].first); - } - auto &field_value = field_values[i]; if (field_value.IsNull()) { throw BinderException("index_key: path field %s cannot be NULL", field_types[i].first); } + + if (field_name == "columns") { + if (field_value.type() != LogicalType::LIST(LogicalType::VARCHAR)) { + throw BinderException("index_key: path field %s must be VARCHAR[]", field_types[i].first); + } + for (auto &column : ListValue::GetChildren(field_value)) { + if (column.IsNull()) { + throw BinderException("index_key: path field %s cannot contain NULL", field_types[i].first); + } + path.columns.emplace_back(StringValue::Get(column)); + } + continue; + } + + if (fields.find(field_name) == fields.end()) { + throw BinderException("index_key: unknown field %s in path", field_types[i].first); + } if (field_value.type().id() != LogicalTypeId::VARCHAR) { throw BinderException("index_key: path field %s must be VARCHAR", field_types[i].first); } - auto value = StringValue::Get(field_value); if (value.empty()) { throw BinderException("index_key: path field %s cannot be empty", field_types[i].first); @@ -54,47 +98,74 @@ static TableDescription ExtractTableDescription(const child_list_t if (fields["table"].empty()) { throw BinderException("index_key: path must contain a 'table' field"); } + if (fields["index"].empty() == fields["constraint"].empty()) { + throw BinderException("index_key: path must contain exactly one of the 'index' or 'constraint' fields"); + } + path.table = + QualifiedName(Identifier(fields["catalog"]), Identifier(fields["schema"]), Identifier(fields["table"])); - return TableDescription( - QualifiedName(Identifier(fields["catalog"]), Identifier(fields["schema"]), Identifier(fields["table"]))); -} - -static TableDescription EvaluateTableDescription(const Value &input_struct) { - if (input_struct.IsNull()) { - throw BinderException("index_key: path parameter cannot be NULL"); + if (!fields["index"].empty()) { + if (!path.columns.empty()) { + throw BinderException("index_key: path field 'columns' requires a 'constraint' field"); + } + path.index_name = Identifier(fields["index"]); + return path; } - return ExtractTableDescription(StructType::GetChildTypes(input_struct.type()), - StructValue::GetChildren(input_struct)); + path.constraint_type = ParseConstraintType(fields["constraint"]); + return path; } -static string GetStringArgument(const Value &value, const string ¶m_name) { - if (value.IsNull()) { - throw BinderException("index_key: parameter '%s' cannot be NULL", param_name); +//! User-defined indexes are catalog entries and constraint indexes are not, so their names cannot clash. +idx_t FindUserIndexOid(ClientContext &context, TableCatalogEntry &table_entry, const Identifier &index_name, + const string &qualified_table) { + auto &schema = table_entry.schema; + auto entry = schema.GetEntry(schema.GetCatalogTransaction(context), CatalogType::INDEX_ENTRY, index_name); + if (!entry || entry->Cast().GetTableName() != table_entry.name) { + throw CatalogException("index_key: index %s was not found on table %s", index_name, qualified_table); } - return StringValue::Get(value); + return entry->oid; } -static shared_ptr FindBoundIndexEntry(const TableIndexList &index_list, const Identifier &index_name, - const TableDescription &path) { - auto found = index_list.FindEntry(index_name); - if (found) { - return found; - } +idx_t FindConstraintIndexOid(TableCatalogEntry &table_entry, const IndexKeyPath &path, const string &qualified_table) { + auto &table_columns = table_entry.GetColumns(); + vector index_oids; + for (auto &constraint : table_entry.GetConstraints()) { + IndexConstraintType constraint_type; + vector constraint_columns; + if (constraint->type == ConstraintType::UNIQUE) { + auto &unique = constraint->Cast(); + constraint_type = unique.GetIndexConstraintType(); + for (auto &logical_index : unique.GetLogicalIndexes(table_columns)) { + constraint_columns.push_back(table_columns.GetColumn(logical_index).Name()); + } + } else if (constraint->type == ConstraintType::FOREIGN_KEY) { + auto &foreign_key = constraint->Cast(); + // The referenced side of a foreign key uses the referenced key's index. + if (!foreign_key.info.IsAppendConstraint()) { + continue; + } + constraint_type = IndexConstraintType::FOREIGN; + for (auto &physical_index : foreign_key.info.fk_keys) { + constraint_columns.push_back(table_columns.GetColumn(physical_index).Name()); + } + } else { + continue; + } - auto qualified_table = path.qualified_name.ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); - vector available; - for (auto entry : index_list.IndexEntries()) { - available.push_back(entry->GetName()); + if (constraint_type == path.constraint_type && (path.columns.empty() || path.columns == constraint_columns)) { + index_oids.push_back(constraint->GetBackingIndexOid()); + } } - - if (available.empty()) { - throw CatalogException("index_key: index %s was not found on table %s. No indexes found on this table.", - index_name, qualified_table); + if (index_oids.empty()) { + throw CatalogException("index_key: %s constraint was not found on table %s", + EnumUtil::ToString(path.constraint_type), qualified_table); } - auto available_list = StringUtil::Join(available, ", "); - throw CatalogException("index_key: index %s was not found on table %s. Available indexes: %s", index_name, - qualified_table, available_list); + if (index_oids.size() > 1) { + throw BinderException("index_key: table %s has multiple %s constraints, specify 'columns' to select one", + qualified_table, EnumUtil::ToString(path.constraint_type)); + } + return index_oids[0]; } struct IndexKeyBindData : public FunctionData { @@ -117,33 +188,31 @@ struct IndexKeyBindData : public FunctionData { // index_key resolves index metadata during binding. // Register the owning catalog so cached prepared statements rebind after catalog changes. -static void RegisterIndexKeyRead(BindScalarFunctionInput &input, TableCatalogEntry &table_entry) { +void RegisterIndexKeyRead(BindScalarFunctionInput &input, TableCatalogEntry &table_entry) { if (input.HasBinder()) { auto &binder = input.GetBinder(); binder.GetStatementProperties().RegisterDBRead(table_entry.ParentCatalog(), input.GetClientContext()); } } -static void RegisterIndexKeyRead(ResolveScalarFunctionTypesInput &input, TableCatalogEntry &table_entry) { +void RegisterIndexKeyRead(ResolveScalarFunctionTypesInput &input, TableCatalogEntry &table_entry) { } //! Called from both resolve_types and bind - the key types and the bind data both come from the index template -static unique_ptr IndexKeyBind(INPUT &input) { +unique_ptr IndexKeyBind(INPUT &input) { ClientContext &context = input.GetClientContext(); BoundScalarFunction &bound_function = input.GetBoundFunction(); const vector> &arguments = input.GetArguments(); if (arguments.size() < INDEX_KEY_FIXED_ARGS) { - throw BinderException("index_key: requires at least two arguments - path (STRUCT), index_name"); + throw BinderException("index_key: requires a path (STRUCT) argument"); } const Value path_value = input.GetConstant(0); - const Value index_name_value = input.GetConstant(1); - auto path = EvaluateTableDescription(path_value); - auto index_name = GetStringArgument(index_name_value, "index_name"); + auto path = EvaluateIndexKeyPath(path_value); - auto qualified_table = path.qualified_name.ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); - auto &table_entry = Catalog::GetEntry(context, path.qualified_name); + auto qualified_table = path.table.ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); + auto &table_entry = Catalog::GetEntry(context, path.table); auto duck_table = table_entry.TryGetDuckTableEntry(); if (!duck_table) { throw BinderException("index_key: table '%s' is not a DuckDB table", qualified_table); @@ -158,8 +227,14 @@ static unique_ptr IndexKeyBind(INPUT &input) { // option to this function to bind or not. data_table_info.BindIndexes(context); - const auto &index_list = data_table_info.GetIndexes(); - const auto index_entry = FindBoundIndexEntry(index_list, Identifier(index_name), path); + auto index_oid = path.constraint_type == IndexConstraintType::NONE + ? FindUserIndexOid(context, table_entry, path.index_name, qualified_table) + : FindConstraintIndexOid(table_entry, path, qualified_table); + const auto index_entry = data_table_info.GetIndexes().FindEntry(index_oid); + if (!index_entry) { + throw CatalogException("index_key: no index found in storage for index oid %llu on table %s", index_oid, + qualified_table); + } const auto index = index_entry->GetReadHandle(); const auto &index_type = index->GetIndexType(); @@ -172,18 +247,21 @@ static unique_ptr IndexKeyBind(INPUT &input) { idx_t num_key_args = arguments.size() - INDEX_KEY_FIXED_ARGS; if (num_key_args != key_types.size()) { - throw BinderException("index_key: index '%s' expects %llu key column(s), but %llu argument(s) provided", - index_name, key_types.size(), num_key_args); + auto index_description = + path.constraint_type == IndexConstraintType::NONE + ? StringUtil::Format("index %s", path.index_name) + : StringUtil::Format("%s constraint index", EnumUtil::ToString(path.constraint_type)); + throw BinderException("index_key: %s on table %s expects %llu key column(s), but %llu argument(s) provided", + index_description, qualified_table, key_types.size(), num_key_args); } // Set bound_function.GetArguments() to actual types for proper casting. - // Note: In case this is ever a bottleneck for testing purposes -- currently, we retain the first two arguments - // for execution even though they are only required for binding. This requires us to create a key_chunk - // that only references the key columns during execution. We could erase the first two arguments here, but + // Note: In case this is ever a bottleneck for testing purposes -- currently, we retain the path argument + // for execution even though it is only required for binding. This requires us to create a key_chunk + // that only references the key columns during execution. We could erase the path argument here, but // that also requires some (de)serialization boilerplate, so for now we don't do it. bound_function.GetArguments().clear(); bound_function.GetArguments().push_back(arguments[0]->GetReturnType()); - bound_function.GetArguments().push_back(arguments[1]->GetReturnType()); for (auto &key_type : key_types) { bound_function.GetArguments().push_back(key_type); } @@ -191,17 +269,17 @@ static unique_ptr IndexKeyBind(INPUT &input) { return make_uniq(index_entry, std::move(key_types)); } -static void IndexKeyResolveTypes(ResolveScalarFunctionTypesInput &input) { +void IndexKeyResolveTypes(ResolveScalarFunctionTypesInput &input) { IndexKeyBind(input); } -static void IndexKeyFunction(DataChunk &args, ExpressionState &state, Vector &result) { +void IndexKeyFunction(DataChunk &args, ExpressionState &state, Vector &result) { auto &func_expr = state.expr.Cast(); auto &bind_data = func_expr.BindInfo()->Cast(); idx_t count = args.size(); - // Create a DataChunk referencing only the key columns (skip path and index_name). + // Create a DataChunk referencing only the key columns (skip the path). DataChunk key_chunk; key_chunk.InitializeEmpty(bind_data.key_types); for (idx_t i = 0; i < bind_data.key_types.size(); i++) { @@ -232,7 +310,7 @@ static void IndexKeyFunction(DataChunk &args, ExpressionState &state, Vector &re ScalarFunction IndexKeyFun::GetFunction() { ScalarFunction fun("index_key", {}, LogicalType::BLOB, IndexKeyFunction, IndexKeyBind); - fun.GetSignature().AddParameter("path", LogicalTypeId::STRUCT).AddParameter("name", LogicalType::VARCHAR); + fun.GetSignature().AddParameter("path", LogicalTypeId::STRUCT); fun.GetSignature().AddArgs("args", LogicalTypeId::ANY); fun.SetResolveTypesCallback(IndexKeyResolveTypes); return fun; diff --git a/src/duckdb/extension/core_functions/scalar/string/damerau_levenshtein.cpp b/src/duckdb/extension/core_functions/scalar/string/damerau_levenshtein.cpp index 3f9256218..25a3f535d 100644 --- a/src/duckdb/extension/core_functions/scalar/string/damerau_levenshtein.cpp +++ b/src/duckdb/extension/core_functions/scalar/string/damerau_levenshtein.cpp @@ -1,4 +1,5 @@ #include "core_functions/scalar/string_functions.hpp" +#include "core_functions/scalar/string_characters.hpp" #include "duckdb/common/map.hpp" #include "duckdb/common/vector.hpp" @@ -7,14 +8,13 @@ namespace duckdb { // Using Lowrance-Wagner (LW) algorithm: https://doi.org/10.1145%2F321879.321880 // Can't calculate as trivial modification to levenshtein algorithm // as we need to potentially know about earlier in the string -static idx_t DamerauLevenshteinDistance(const string_t &source, const string_t &target) { +template +static idx_t DamerauLevenshteinDistance(const T *source_str, idx_t source_len, const T *target_str, idx_t target_len) { // costs associated with each type of edit, to aid readability constexpr uint8_t COST_SUBSTITUTION = 1; constexpr uint8_t COST_INSERTION = 1; constexpr uint8_t COST_DELETION = 1; constexpr uint8_t COST_TRANSPOSITION = 1; - const auto source_len = source.GetSize(); - const auto target_len = target.GetSize(); // If one string is empty, the distance equals the length of the other string // either through target_len insertions @@ -25,9 +25,6 @@ static idx_t DamerauLevenshteinDistance(const string_t &source, const string_t & return source_len * COST_DELETION; } - const auto source_str = source.GetData(); - const auto target_str = target.GetData(); - // larger than the largest possible value: const auto inf = source_len * COST_DELETION + target_len * COST_INSERTION + 1; // minimum edit distance from prefix of source string to prefix of target string @@ -35,7 +32,7 @@ static idx_t DamerauLevenshteinDistance(const string_t &source, const string_t & vector> distance(source_len + 2, vector(target_len + 2, inf)); // keeps track of the largest string indices of source string matching each character // same as DA in LW paper - map largest_source_chr_matching; + map largest_source_chr_matching; // initialise row/column corresponding to zero-length strings // partial string -> empty requires a deletion for each character @@ -83,6 +80,17 @@ static idx_t DamerauLevenshteinDistance(const string_t &source, const string_t & return distance[source_len + 1][target_len + 1]; } +//! The distance between the characters of the strings - not their bytes +static idx_t DamerauLevenshteinDistance(const string_t &source, const string_t &target) { + if (StringIsAscii(source) && StringIsAscii(target)) { + return DamerauLevenshteinDistance(source.GetData(), source.GetSize(), target.GetData(), target.GetSize()); + } + auto source_chars = GetStringCharacters(source); + auto target_chars = GetStringCharacters(target); + return DamerauLevenshteinDistance(source_chars.data(), source_chars.size(), target_chars.data(), + target_chars.size()); +} + static int64_t DamerauLevenshteinScalarFunction(Vector &result, const string_t source, const string_t target) { return (int64_t)DamerauLevenshteinDistance(source, target); } diff --git a/src/duckdb/extension/core_functions/scalar/string/hamming.cpp b/src/duckdb/extension/core_functions/scalar/string/hamming.cpp index ddeaab2a7..cc841172c 100644 --- a/src/duckdb/extension/core_functions/scalar/string/hamming.cpp +++ b/src/duckdb/extension/core_functions/scalar/string/hamming.cpp @@ -1,4 +1,5 @@ #include "core_functions/scalar/string_functions.hpp" +#include "core_functions/scalar/string_characters.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" #include @@ -6,10 +7,8 @@ namespace duckdb { -static int64_t MismatchesScalarFunction(Vector &result, const string_t str, string_t tgt) { - idx_t str_len = str.GetSize(); - idx_t tgt_len = tgt.GetSize(); - +template +static int64_t Mismatches(const T *str_str, idx_t str_len, const T *tgt_str, idx_t tgt_len) { if (str_len != tgt_len) { throw InvalidInputException("Mismatch Function: Strings must be of equal length!"); } @@ -18,8 +17,6 @@ static int64_t MismatchesScalarFunction(Vector &result, const string_t str, stri } idx_t mismatches = 0; - auto str_str = str.GetData(); - auto tgt_str = tgt.GetData(); for (idx_t idx = 0; idx < str_len; ++idx) { if (str_str[idx] != tgt_str[idx]) { @@ -29,6 +26,16 @@ static int64_t MismatchesScalarFunction(Vector &result, const string_t str, stri return (int64_t)mismatches; } +//! The number of positions at which the characters of the strings differ - not their bytes +static int64_t MismatchesScalarFunction(Vector &result, const string_t str, string_t tgt) { + if (StringIsAscii(str) && StringIsAscii(tgt)) { + return Mismatches(str.GetData(), str.GetSize(), tgt.GetData(), tgt.GetSize()); + } + auto str_chars = GetStringCharacters(str); + auto tgt_chars = GetStringCharacters(tgt); + return Mismatches(str_chars.data(), str_chars.size(), tgt_chars.data(), tgt_chars.size()); +} + static void MismatchesFunction(DataChunk &args, ExpressionState &state, Vector &result) { const auto &str_vec = args.data[0]; const auto &tgt_vec = args.data[1]; diff --git a/src/duckdb/extension/core_functions/scalar/string/jaccard.cpp b/src/duckdb/extension/core_functions/scalar/string/jaccard.cpp index 149b53be6..6fee05df3 100644 --- a/src/duckdb/extension/core_functions/scalar/string/jaccard.cpp +++ b/src/duckdb/extension/core_functions/scalar/string/jaccard.cpp @@ -1,6 +1,8 @@ #include "duckdb/common/map.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" #include "core_functions/scalar/string_functions.hpp" +#include "core_functions/scalar/string_characters.hpp" +#include "duckdb/common/unordered_set.hpp" #include #include @@ -27,13 +29,28 @@ static double JaccardSimilarity(const string_t &str, const string_t &txt) { if (str.GetSize() < 1 || txt.GetSize() < 1) { throw InvalidInputException("Jaccard Function: An argument too short!"); } - std::bitset m_str, m_txt; + idx_t size_intersect; + idx_t size_union; + if (StringIsAscii(str) && StringIsAscii(txt)) { + std::bitset m_str, m_txt; - m_str = GetSet(str); - m_txt = GetSet(txt); + m_str = GetSet(str); + m_txt = GetSet(txt); - idx_t size_intersect = (m_str & m_txt).count(); - idx_t size_union = (m_str | m_txt).count(); + size_intersect = (m_str & m_txt).count(); + size_union = (m_str | m_txt).count(); + } else { + // the sets of characters - not of bytes + auto str_chars = GetStringCharacters(str); + auto txt_chars = GetStringCharacters(txt); + unordered_set str_set(str_chars.begin(), str_chars.end()); + unordered_set txt_set(txt_chars.begin(), txt_chars.end()); + size_intersect = 0; + for (auto &character : str_set) { + size_intersect += txt_set.count(character); + } + size_union = str_set.size() + txt_set.size() - size_intersect; + } return static_cast(size_intersect) / static_cast(size_union); } diff --git a/src/duckdb/extension/core_functions/scalar/string/jaro_winkler.cpp b/src/duckdb/extension/core_functions/scalar/string/jaro_winkler.cpp index fd5266f1b..47fc3ddf3 100644 --- a/src/duckdb/extension/core_functions/scalar/string/jaro_winkler.cpp +++ b/src/duckdb/extension/core_functions/scalar/string/jaro_winkler.cpp @@ -1,10 +1,18 @@ #include "jaro_winkler.hpp" #include "core_functions/scalar/string_functions.hpp" +#include "core_functions/scalar/string_characters.hpp" namespace duckdb { static inline double JaroScalarFunction(const string_t &s1, const string_t &s2, const double_t &score_cutoff = 0.0) { + if (!StringIsAscii(s1) || !StringIsAscii(s2)) { + // compare the characters - not the bytes + auto s1_chars = GetStringCharacters(s1); + auto s2_chars = GetStringCharacters(s2); + return duckdb_jaro_winkler::jaro_similarity(s1_chars.begin(), s1_chars.end(), s2_chars.begin(), s2_chars.end(), + score_cutoff); + } auto s1_begin = s1.GetData(); auto s2_begin = s2.GetData(); return duckdb_jaro_winkler::jaro_similarity(s1_begin, s1_begin + s1.GetSize(), s2_begin, s2_begin + s2.GetSize(), @@ -13,14 +21,21 @@ static inline double JaroScalarFunction(const string_t &s1, const string_t &s2, static inline double JaroWinklerScalarFunction(const string_t &s1, const string_t &s2, const double_t &score_cutoff = 0.0) { + if (!StringIsAscii(s1) || !StringIsAscii(s2)) { + // compare the characters - not the bytes + auto s1_chars = GetStringCharacters(s1); + auto s2_chars = GetStringCharacters(s2); + return duckdb_jaro_winkler::jaro_winkler_similarity(s1_chars.begin(), s1_chars.end(), s2_chars.begin(), + s2_chars.end(), 0.1, score_cutoff); + } auto s1_begin = s1.GetData(); auto s2_begin = s2.GetData(); return duckdb_jaro_winkler::jaro_winkler_similarity(s1_begin, s1_begin + s1.GetSize(), s2_begin, s2_begin + s2.GetSize(), 0.1, score_cutoff); } -template -static void CachedFunction(Vector &constant, Vector &other, Vector &result, DataChunk &args) { +template +static void CachedFunction(Vector &constant, Vector &other, Vector &result, DataChunk &args, SIMILARITY_FUNCTION fun) { auto val = constant.GetValue(0); if (val.IsNull()) { auto &result_validity = FlatVector::ValidityMutable(result); @@ -30,10 +45,14 @@ static void CachedFunction(Vector &constant, Vector &other, Vector &result, Data auto str_val = StringValue::Get(val); auto cached = CACHED_SIMILARITY(str_val); + const string_t constant_str(str_val); D_ASSERT(args.ColumnCount() == 2 || args.ColumnCount() == 3); if (args.ColumnCount() == 2) { UnaryExecutor::Execute(other, result, [&](const string_t &other_str) { + if (!StringIsAscii(other_str)) { + return fun(constant_str, other_str, 0.0); + } auto other_str_begin = other_str.GetData(); return cached.similarity(other_str_begin, other_str_begin + other_str.GetSize()); }); @@ -41,6 +60,9 @@ static void CachedFunction(Vector &constant, Vector &other, Vector &result, Data const auto &score_cutoff = args.data[2]; BinaryExecutor::Execute( other, score_cutoff, result, [&](const string_t &other_str, const double_t score_cutoff) { + if (!StringIsAscii(other_str)) { + return fun(constant_str, other_str, score_cutoff); + } auto other_str_begin = other_str.GetData(); return cached.similarity(other_str_begin, other_str_begin + other_str.GetSize(), score_cutoff); }); @@ -51,7 +73,16 @@ template static void TemplatedJaroWinklerFunction(DataChunk &args, Vector &result, SIMILARITY_FUNCTION fun) { bool arg0_constant = args.data[0].GetVectorType() == VectorType::CONSTANT_VECTOR; bool arg1_constant = args.data[1].GetVectorType() == VectorType::CONSTANT_VECTOR; - if (!(arg0_constant ^ arg1_constant)) { + // the cached similarity compares bytes, which only works for a constant that consists of ASCII characters + bool can_cache = arg0_constant ^ arg1_constant; + if (can_cache) { + auto constant_value = (arg0_constant ? args.data[0] : args.data[1]).GetValue(0); + if (!constant_value.IsNull()) { + auto &constant_str = StringValue::Get(constant_value); + can_cache = StringIsAscii(string_t(constant_str)); + } + } + if (!can_cache) { // We can't optimize by caching one of the two strings D_ASSERT(args.ColumnCount() == 2 || args.ColumnCount() == 3); if (args.ColumnCount() == 2) { @@ -67,9 +98,9 @@ static void TemplatedJaroWinklerFunction(DataChunk &args, Vector &result, SIMILA } if (arg0_constant) { - CachedFunction(args.data[0], args.data[1], result, args); + CachedFunction(args.data[0], args.data[1], result, args, fun); } else { - CachedFunction(args.data[1], args.data[0], result, args); + CachedFunction(args.data[1], args.data[0], result, args, fun); } } diff --git a/src/duckdb/extension/core_functions/scalar/string/levenshtein.cpp b/src/duckdb/extension/core_functions/scalar/string/levenshtein.cpp index cd6815a84..e155aa4e7 100644 --- a/src/duckdb/extension/core_functions/scalar/string/levenshtein.cpp +++ b/src/duckdb/extension/core_functions/scalar/string/levenshtein.cpp @@ -1,4 +1,5 @@ #include "core_functions/scalar/string_functions.hpp" +#include "core_functions/scalar/string_characters.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" #include "duckdb/common/string_util.hpp" @@ -12,10 +13,8 @@ namespace duckdb { // Note: A first implementation using the array algorithm version resulted in an error raised by duckdb // (too muach memory usage) -static idx_t LevenshteinDistance(const string_t &txt, const string_t &tgt) { - auto txt_len = txt.GetSize(); - auto tgt_len = tgt.GetSize(); - +template +static idx_t LevenshteinDistance(const T *txt_str, idx_t txt_len, const T *tgt_str, idx_t tgt_len) { // If one string is empty, the distance equals the length of the other string if (txt_len == 0) { return tgt_len; @@ -23,9 +22,6 @@ static idx_t LevenshteinDistance(const string_t &txt, const string_t &tgt) { return txt_len; } - auto txt_str = txt.GetData(); - auto tgt_str = tgt.GetData(); - // Create two working vectors vector distances0(tgt_len + 1, 0); vector distances1(tgt_len + 1, 0); @@ -64,6 +60,16 @@ static idx_t LevenshteinDistance(const string_t &txt, const string_t &tgt) { return distances0[tgt_len]; } +//! The distance between the characters of the strings - not their bytes +static idx_t LevenshteinDistance(const string_t &txt, const string_t &tgt) { + if (StringIsAscii(txt) && StringIsAscii(tgt)) { + return LevenshteinDistance(txt.GetData(), txt.GetSize(), tgt.GetData(), tgt.GetSize()); + } + auto txt_chars = GetStringCharacters(txt); + auto tgt_chars = GetStringCharacters(tgt); + return LevenshteinDistance(txt_chars.data(), txt_chars.size(), tgt_chars.data(), tgt_chars.size()); +} + static int64_t LevenshteinScalarFunction(Vector &result, const string_t str, string_t tgt) { return (int64_t)LevenshteinDistance(str, tgt); } diff --git a/src/duckdb/extension/core_functions/scalar/struct/struct_insert.cpp b/src/duckdb/extension/core_functions/scalar/struct/struct_insert.cpp index 9a6f6cb3b..82b939ae7 100644 --- a/src/duckdb/extension/core_functions/scalar/struct/struct_insert.cpp +++ b/src/duckdb/extension/core_functions/scalar/struct/struct_insert.cpp @@ -59,7 +59,7 @@ static unique_ptr StructInsertBind(BindScalarFunctionInput &input) // Loop through the fields that are inserted for (idx_t i = 1; i < arguments.size(); i++) { if (name_collision_set.find(names[i]) != name_collision_set.end()) { - throw BinderException("Duplicate struct entry name \"%s\"", names[i]); + throw BinderException("Duplicate struct entry name %s", names[i]); } name_collision_set.insert(names[i]); new_children.emplace_back(make_pair(names[i], arguments[i]->GetReturnType())); diff --git a/src/duckdb/extension/json/include/json_deserializer.hpp b/src/duckdb/extension/json/include/json_deserializer.hpp index 73e6cf5d1..b5fdf4e74 100644 --- a/src/duckdb/extension/json/include/json_deserializer.hpp +++ b/src/duckdb/extension/json/include/json_deserializer.hpp @@ -20,6 +20,9 @@ class JsonDeserializer : public Deserializer { } }; + //! Deserializing recurses once per nesting level of the JSON - keep it within small thread stacks + static constexpr idx_t MAX_NESTING_DEPTH = 256; + yyjson_doc *doc; const char *current_tag = nullptr; vector stack; @@ -35,6 +38,9 @@ class JsonDeserializer : public Deserializer { }; inline void Push(yyjson_val *val) { + if (stack.size() >= MAX_NESTING_DEPTH) { + throw SerializationException("Serialized JSON exceeds the maximum nesting depth of %d", MAX_NESTING_DEPTH); + } stack.emplace_back(val); } inline void Pop() { diff --git a/src/duckdb/extension/json/json_common.cpp b/src/duckdb/extension/json/json_common.cpp index b0db86a97..7cf3d040b 100644 --- a/src/duckdb/extension/json/json_common.cpp +++ b/src/duckdb/extension/json/json_common.cpp @@ -55,13 +55,15 @@ struct JSONKeyReadResult { } inline bool IsWildCard() { - return key == "*"; + // a quoted "*" is a regular key + return !quoted && key == "*"; } public: idx_t chars_read; bool recursive; string key; + bool quoted = false; }; static inline JSONKeyReadResult ReadString(const char *ptr, const char *const end, const bool escaped) { @@ -170,6 +172,7 @@ static inline JSONKeyReadResult ReadKey(const char *ptr, const char *const end) } if (escaped) { result.chars_read += 2; // Account for surrounding quotes + result.quoted = true; } if (recursive) { result.chars_read += 1; diff --git a/src/duckdb/extension/json/json_functions/json_create.cpp b/src/duckdb/extension/json/json_functions/json_create.cpp index 6117f0b89..15ad7dcbf 100644 --- a/src/duckdb/extension/json/json_functions/json_create.cpp +++ b/src/duckdb/extension/json/json_functions/json_create.cpp @@ -313,7 +313,7 @@ static unique_ptr JSONCreateBindParams(BoundScalarFunction &bound_ throw ParameterNotResolvedException(); } else if (object && i % 2 == 0) { if (type != LogicalType::VARCHAR) { - throw BinderException("json_object() keys must be VARCHAR, add an explicit cast to argument \"%s\"", + throw BinderException("json_object() keys must be VARCHAR, add an explicit cast to argument %s", arguments[i]->GetName()); } bound_arguments.push_back(LogicalType::VARCHAR); @@ -1307,10 +1307,16 @@ static void JSONCopyToGeoJSONFunction(DataChunk &args, ExpressionState &state, V //! The column-selecting options are three-state: a NULL argument means the option was not given (use the default //! behavior), an empty string means it was explicitly unset, and anything else names a column static bool TryGetGeoJSONColumnOption(ClientContext &context, const Expression &argument, string &result) { + if (!argument.IsFoldable()) { + throw BinderException("%s column options must be constants", JSON_COPY_TO_GEOJSON_INTERNAL_NAME); + } const auto value = ExpressionExecutor::EvaluateScalar(context, argument); if (value.IsNull()) { return false; } + if (value.type().id() != LogicalTypeId::VARCHAR) { + throw BinderException("%s column options must be VARCHAR or NULL", JSON_COPY_TO_GEOJSON_INTERNAL_NAME); + } result = StringValue::Get(value); return true; } diff --git a/src/duckdb/extension/json/json_functions/json_serialize_plan.cpp b/src/duckdb/extension/json/json_functions/json_serialize_plan.cpp index 48766e751..5697f5d68 100644 --- a/src/duckdb/extension/json/json_functions/json_serialize_plan.cpp +++ b/src/duckdb/extension/json/json_functions/json_serialize_plan.cpp @@ -84,7 +84,7 @@ static unique_ptr JsonSerializePlanBind(BindScalarFunctionInput &i } optimize = BooleanValue::Get(input.GetConstant(i)); } else { - throw BinderException(StringUtil::Format("json_serialize_plan: Unknown argument '%s'", alias)); + throw BinderException(StringUtil::Format("json_serialize_plan: Unknown argument %s", alias)); } } return make_uniq(skip_if_null, skip_if_empty, skip_if_default, format, optimize); @@ -133,6 +133,10 @@ static void JsonSerializePlanFunction(DataChunk &args, ExpressionState &state, V Planner planner(context); planner.CreatePlan(std::move(stmt)); auto plan = std::move(planner.plan); + if (!plan) { + throw InvalidInputException("json_serialize_plan: statement cannot be planned, parameters need " + "to have a known value to serialize the plan"); + } if (info.optimize && plan->RequireOptimizer()) { Optimizer optimizer(*planner.binder, context); diff --git a/src/duckdb/extension/json/json_functions/json_serialize_sql.cpp b/src/duckdb/extension/json/json_functions/json_serialize_sql.cpp index 6eb717337..9071b3b8f 100644 --- a/src/duckdb/extension/json/json_functions/json_serialize_sql.cpp +++ b/src/duckdb/extension/json/json_functions/json_serialize_sql.cpp @@ -71,7 +71,7 @@ static unique_ptr JsonSerializeBind(BindScalarFunctionInput &input } skip_if_default = BooleanValue::Get(input.GetConstant(i)); } else { - throw BinderException(StringUtil::Format("json_serialize_sql: Unknown argument '%s'", alias)); + throw BinderException(StringUtil::Format("json_serialize_sql: Unknown argument %s", alias)); } } return make_uniq(skip_if_null, skip_if_empty, skip_if_default, format); diff --git a/src/duckdb/extension/json/json_functions/read_json.cpp b/src/duckdb/extension/json/json_functions/read_json.cpp index c7fbb3384..5f07cc821 100644 --- a/src/duckdb/extension/json/json_functions/read_json.cpp +++ b/src/duckdb/extension/json/json_functions/read_json.cpp @@ -21,7 +21,7 @@ static inline LogicalType RemoveDuplicateStructKeys(const LogicalType &type, con continue; } throw NotImplementedException( - "Duplicate name \"%s\" in struct auto-detected in JSON, try ignore_errors=true", child_type.first); + "Duplicate name %s in struct auto-detected in JSON, try ignore_errors=true", child_type.first); } else { child_types.emplace_back(child_type.first, RemoveDuplicateStructKeys(child_type.second, ignore_errors)); } diff --git a/src/duckdb/extension/parquet/column_reader.cpp b/src/duckdb/extension/parquet/column_reader.cpp index 54519dfc2..2c94031de 100644 --- a/src/duckdb/extension/parquet/column_reader.cpp +++ b/src/duckdb/extension/parquet/column_reader.cpp @@ -253,7 +253,10 @@ void ColumnReader::InitializeRead(idx_t row_group_idx_p, idx_t row_group_num_row if (chunk->__isset.crypto_metadata && !reader.metadata->crypto_metadata) { throw InvalidInputException("File is encrypted but no file crypto metadata is set"); } - D_ASSERT(chunk->__isset.meta_data); + if (!chunk->__isset.meta_data) { + throw InvalidInputException("Failed to read file \"%s\": column chunk is missing its metadata", + Reader().GetFileName()); + } if (chunk->__isset.file_path) { throw InvalidInputException("Failed to read file \"%s\": Only inlined data files are supported (no references)", diff --git a/src/duckdb/extension/parquet/decoder/byte_stream_split_decoder.cpp b/src/duckdb/extension/parquet/decoder/byte_stream_split_decoder.cpp index bb52de149..724e8290d 100644 --- a/src/duckdb/extension/parquet/decoder/byte_stream_split_decoder.cpp +++ b/src/duckdb/extension/parquet/decoder/byte_stream_split_decoder.cpp @@ -5,6 +5,7 @@ #include "column_reader.hpp" #include "parquet_reader.hpp" #include "duckdb/common/helper.hpp" +#include "duckdb/common/numeric_utils.hpp" #include "duckdb/common/shared_ptr_ipp.hpp" #include "parquet_column_schema.hpp" #include "parquet_types.h" @@ -17,61 +18,52 @@ ByteStreamSplitDecoder::ByteStreamSplitDecoder(ColumnReader &reader) : reader(reader), decoded_data_buffer(reader.encoding_buffers[0]) { } +static idx_t GetValueWidth(const ParquetColumnSchema &schema) { + switch (schema.parquet_type) { + case duckdb_parquet::Type::FLOAT: + case duckdb_parquet::Type::INT32: + return sizeof(int32_t); + case duckdb_parquet::Type::DOUBLE: + case duckdb_parquet::Type::INT64: + return sizeof(int64_t); + case duckdb_parquet::Type::FIXED_LEN_BYTE_ARRAY: + return schema.type_length; + default: + throw std::runtime_error("BYTE_STREAM_SPLIT encoding is only supported for FLOAT, DOUBLE, INT32, INT64 or " + "FIXED_LEN_BYTE_ARRAY data"); + } +} + void ByteStreamSplitDecoder::InitializePage() { auto &block = reader.block; // Subtract 1 from length as the block is allocated with 1 extra byte, // but the byte stream split encoder needs to know the correct data size. - bss_decoder = make_uniq(block->ptr, block->len - 1); + bss_decoder = make_uniq(block->ptr, block->len - 1, GetValueWidth(reader.Schema())); block->inc(block->len); } void ByteStreamSplitDecoder::Read(uint8_t *defines, idx_t read_count, Vector &result, idx_t result_offset) { idx_t valid_count = reader.GetValidCount(defines, read_count, result_offset); - auto &allocator = reader.reader.allocator; - decoded_data_buffer.reset(); - switch (reader.Schema().parquet_type) { - case duckdb_parquet::Type::FLOAT: - decoded_data_buffer.resize(allocator, sizeof(float) * valid_count); - bss_decoder->GetBatch(decoded_data_buffer.ptr, valid_count); - break; - case duckdb_parquet::Type::DOUBLE: - decoded_data_buffer.resize(allocator, sizeof(double) * valid_count); - bss_decoder->GetBatch(decoded_data_buffer.ptr, valid_count); - break; - case duckdb_parquet::Type::INT32: - decoded_data_buffer.resize(allocator, sizeof(int32_t) * valid_count); - bss_decoder->GetBatch(decoded_data_buffer.ptr, valid_count); - break; - case duckdb_parquet::Type::INT64: - decoded_data_buffer.resize(allocator, sizeof(int64_t) * valid_count); - bss_decoder->GetBatch(decoded_data_buffer.ptr, valid_count); - break; - default: - throw std::runtime_error("BYTE_STREAM_SPLIT encoding is only supported for FLOAT, DOUBLE, INT32 or INT64 data"); + auto value_width = GetValueWidth(reader.Schema()); + + if (reader.Schema().parquet_type == duckdb_parquet::Type::FIXED_LEN_BYTE_ARRAY) { + // string readers can reference the decoded values, so the buffer must be able to outlive this call + auto plain_data = make_shared_ptr(allocator, value_width * valid_count); + bss_decoder->GetBatch(plain_data->ptr, NumericCast(valid_count)); + reader.Plain(plain_data, defines, read_count, result_offset, result); + return; } + decoded_data_buffer.reset(); + decoded_data_buffer.resize(allocator, value_width * valid_count); + bss_decoder->GetBatch(decoded_data_buffer.ptr, NumericCast(valid_count)); reader.Plain(decoded_data_buffer, defines, read_count, result_offset, result); } void ByteStreamSplitDecoder::Skip(uint8_t *defines, idx_t skip_count) { idx_t valid_count = reader.GetValidCount(defines, skip_count); - switch (reader.Schema().parquet_type) { - case duckdb_parquet::Type::FLOAT: - bss_decoder->Skip(valid_count); - break; - case duckdb_parquet::Type::DOUBLE: - bss_decoder->Skip(valid_count); - break; - case duckdb_parquet::Type::INT32: - bss_decoder->Skip(valid_count); - break; - case duckdb_parquet::Type::INT64: - bss_decoder->Skip(valid_count); - break; - default: - throw std::runtime_error("BYTE_STREAM_SPLIT encoding is only supported for FLOAT, DOUBLE, INT32 or INT64 data"); - } + bss_decoder->Skip(NumericCast(valid_count)); } } // namespace duckdb diff --git a/src/duckdb/extension/parquet/decoder/dictionary_decoder.cpp b/src/duckdb/extension/parquet/decoder/dictionary_decoder.cpp index 9ef2fe448..73300317b 100644 --- a/src/duckdb/extension/parquet/decoder/dictionary_decoder.cpp +++ b/src/duckdb/extension/parquet/decoder/dictionary_decoder.cpp @@ -49,7 +49,9 @@ void DictionaryDecoder::InitializeDictionary(idx_t new_dictionary_size, optional } // now read the non-NULL values from Parquet - reader.Plain(reader.block, nullptr, dictionary_size, 0, dictionary_data); + if (dictionary_size > 0) { + reader.Plain(reader.block, nullptr, dictionary_size, 0, dictionary_data); + } // immediately filter the dictionary, if applicable if (filter && CanFilter(*filter, *filter_state)) { diff --git a/src/duckdb/extension/parquet/include/parquet_bss_decoder.hpp b/src/duckdb/extension/parquet/include/parquet_bss_decoder.hpp index eadd3323f..53159f51d 100644 --- a/src/duckdb/extension/parquet/include/parquet_bss_decoder.hpp +++ b/src/duckdb/extension/parquet/include/parquet_bss_decoder.hpp @@ -15,46 +15,41 @@ namespace duckdb { /// Decoder for the Byte Stream Split encoding class BssDecoder { public: - /// Create a decoder object. buffer/buffer_len is the encoded data. - BssDecoder(data_ptr_t buffer, uint32_t buffer_len) : buffer_(buffer, buffer_len), value_offset_(0) { - } - -public: - template - void GetBatch(data_ptr_t values_target_ptr, uint32_t batch_size) { - if (buffer_.len % sizeof(T) != 0) { + /// Create a decoder object. buffer/buffer_len is the encoded data, type_size the width of a single value. + BssDecoder(data_ptr_t buffer, uint32_t buffer_len, idx_t type_size) + : buffer_(buffer, buffer_len), type_size_(type_size), value_offset_(0) { + if (type_size == 0 || buffer_len % type_size != 0) { duckdb::stringstream error; - error << "Data buffer size for the BYTE_STREAM_SPLIT encoding (" << buffer_.len - << ") should be a multiple of the type size (" << sizeof(T) << ")"; + error << "Data buffer size for the BYTE_STREAM_SPLIT encoding (" << buffer_len + << ") should be a multiple of the type size (" << type_size << ")"; throw std::runtime_error(error.str()); } - uint32_t num_buffer_values = buffer_.len / sizeof(T); + num_buffer_values_ = buffer_len / type_size; + } - buffer_.available((value_offset_ + batch_size) * sizeof(T)); +public: + /// Writes batch_size values in the PLAIN layout (consecutive type_size-byte values) to values_target_ptr + void GetBatch(data_ptr_t values_target_ptr, uint32_t batch_size) { + buffer_.available((value_offset_ + batch_size) * type_size_); - for (uint32_t byte_offset = 0; byte_offset < sizeof(T); ++byte_offset) { - data_ptr_t input_bytes = buffer_.ptr + byte_offset * num_buffer_values + value_offset_; + for (idx_t byte_offset = 0; byte_offset < type_size_; ++byte_offset) { + data_ptr_t input_bytes = buffer_.ptr + byte_offset * num_buffer_values_ + value_offset_; for (uint32_t i = 0; i < batch_size; ++i) { - values_target_ptr[byte_offset + i * sizeof(T)] = *(input_bytes + i); + values_target_ptr[byte_offset + i * type_size_] = *(input_bytes + i); } } value_offset_ += batch_size; } - template void Skip(uint32_t batch_size) { - if (buffer_.len % sizeof(T) != 0) { - duckdb::stringstream error; - error << "Data buffer size for the BYTE_STREAM_SPLIT encoding (" << buffer_.len - << ") should be a multiple of the type size (" << sizeof(T) << ")"; - throw std::runtime_error(error.str()); - } - buffer_.available((value_offset_ + batch_size) * sizeof(T)); + buffer_.available((value_offset_ + batch_size) * type_size_); value_offset_ += batch_size; } private: ByteBuffer buffer_; + idx_t type_size_; + uint32_t num_buffer_values_; uint32_t value_offset_; }; diff --git a/src/duckdb/extension/parquet/include/parquet_rle_bp_decoder.hpp b/src/duckdb/extension/parquet/include/parquet_rle_bp_decoder.hpp index 3a621e908..cb1f363f4 100644 --- a/src/duckdb/extension/parquet/include/parquet_rle_bp_decoder.hpp +++ b/src/duckdb/extension/parquet/include/parquet_rle_bp_decoder.hpp @@ -149,7 +149,7 @@ class RleBpDecoder { } for (auto i = 0; i < byte_encoded_len; i++) { auto next_byte = Load(buffer_.ptr + i); - current_value_ |= (next_byte << (i * 8)); + current_value_ |= (static_cast(next_byte) << (i * 8)); } buffer_.unsafe_inc(byte_encoded_len); // sanity check diff --git a/src/duckdb/extension/parquet/parquet_column_schema.cpp b/src/duckdb/extension/parquet/parquet_column_schema.cpp index e5e0fd2f6..87774a3da 100644 --- a/src/duckdb/extension/parquet/parquet_column_schema.cpp +++ b/src/duckdb/extension/parquet/parquet_column_schema.cpp @@ -42,23 +42,32 @@ void ParquetColumnSchema::ValidateColumnMetadata(const ColumnChunk &column, int6 return; } auto &metadata = column.meta_data; + if (metadata.data_page_offset < 0 || metadata.dictionary_page_offset < 0 || metadata.index_page_offset < 0 || + metadata.total_compressed_size < 0) { + if (file_name) { + throw DataCorruptionException( + "Failed to read file \"%s\": metadata is corrupt. Column chunk has a negative offset or size", + file_name); + } + throw DataCorruptionException("Parquet metadata is corrupt. Column chunk has a negative offset or size"); + } if (metadata.num_values < 0) { if (file_name) { - throw InvalidInputException("Failed to read file \"%s\": metadata is corrupt. Column has invalid " - "number of values (%lld)", - file_name, metadata.num_values); + throw DataCorruptionException("Failed to read file \"%s\": metadata is corrupt. Column has invalid " + "number of values (%lld)", + file_name, metadata.num_values); } - throw InvalidInputException("Parquet metadata is corrupt. Column has invalid number of values (%lld)", - metadata.num_values); + throw DataCorruptionException("Parquet metadata is corrupt. Column has invalid number of values (%lld)", + metadata.num_values); } if (validate_row_count && !type.IsNested() && max_repeat == 0 && metadata.num_values != row_group_num_rows) { if (file_name) { - throw InvalidInputException( + throw DataCorruptionException( "Failed to read file \"%s\": metadata is corrupt. Column has %lld values but row group has %lld rows", file_name, metadata.num_values, row_group_num_rows); } - throw InvalidInputException("Parquet metadata is corrupt. Column has %lld values but row group has %lld rows", - metadata.num_values, row_group_num_rows); + throw DataCorruptionException("Parquet metadata is corrupt. Column has %lld values but row group has %lld rows", + metadata.num_values, row_group_num_rows); } } diff --git a/src/duckdb/extension/parquet/parquet_crypto.cpp b/src/duckdb/extension/parquet/parquet_crypto.cpp index 81d70bbc0..2f8a08513 100644 --- a/src/duckdb/extension/parquet/parquet_crypto.cpp +++ b/src/duckdb/extension/parquet/parquet_crypto.cpp @@ -168,7 +168,7 @@ ParquetEncryptionConfig::ParquetEncryptionConfig(ClientContext &context, const V } else if (struct_key == "column_keys") { throw NotImplementedException("Parquet encryption_config column_keys not yet implemented"); } else { - throw BinderException("Unknown key in encryption_config \"%s\"", struct_key); + throw BinderException("Unknown key in encryption_config %s", struct_key); } } } @@ -333,6 +333,9 @@ class DecryptionTransport : public TTransport { data_t length_buf[ParquetCrypto::LENGTH_BYTES]; trans.read(length_buf, ParquetCrypto::LENGTH_BYTES); total_bytes = Load(length_buf); + if (total_bytes < ParquetCrypto::NONCE_BYTES + ParquetCrypto::TAG_BYTES) { + throw InvalidInputException("Encoded ciphertext length %d is smaller than the nonce and tag", total_bytes); + } transport_remaining = total_bytes; // Read nonce and initialize AES transport_remaining -= trans.read(nonce.data(), nonce.total_size()); diff --git a/src/duckdb/extension/parquet/parquet_reader.cpp b/src/duckdb/extension/parquet/parquet_reader.cpp index 38408519e..3e3a673df 100644 --- a/src/duckdb/extension/parquet/parquet_reader.cpp +++ b/src/duckdb/extension/parquet/parquet_reader.cpp @@ -423,6 +423,9 @@ LogicalType ParquetReader::DeriveLogicalType(const SchemaElement &s_ele, const P throw IOException("FIXED_LEN_BYTE_ARRAY requires length to be set"); } if (s_ele.__isset.type_length) { + if (s_ele.type_length < 0) { + throw IOException("Invalid negative type_length %d for column \"%s\"", s_ele.type_length, s_ele.name); + } schema.type_length = NumericCast(s_ele.type_length); } schema.parquet_type = s_ele.type; @@ -1299,7 +1302,11 @@ unique_ptr ParquetReader::ParseSchema(ClientContext &contex throw InvalidInputException("Failed to read Parquet file \"%s\": Root element of Parquet file must be a struct", file.path); } - D_ASSERT(next_schema_idx == file_meta_data->schema.size() - 1); + if (next_schema_idx != file_meta_data->schema.size() - 1) { + throw InvalidInputException("Failed to read Parquet file \"%s\": schema has elements that are not part of the " + "schema tree", + file.path); + } if (!file_meta_data->row_groups.empty() && next_file_idx != file_meta_data->row_groups[0].columns.size()) { throw InvalidInputException("Failed to read Parquet file \"%s\": row group does not have enough columns", file.path); diff --git a/src/duckdb/extension/parquet/parquet_statistics.cpp b/src/duckdb/extension/parquet/parquet_statistics.cpp index 62ff20a12..8c9e36771 100644 --- a/src/duckdb/extension/parquet/parquet_statistics.cpp +++ b/src/duckdb/extension/parquet/parquet_statistics.cpp @@ -145,7 +145,7 @@ Value ParquetStatisticsUtils::ConvertValueInternal(const LogicalType &type, cons if (stats.size() != sizeof(bool)) { throw InvalidInputException("Incorrect stats size for type BOOLEAN"); } - return Value::BOOLEAN(Load(stats_data)); + return Value::BOOLEAN(Load(stats_data) != 0); } case LogicalTypeId::UTINYINT: case LogicalTypeId::USMALLINT: @@ -392,7 +392,7 @@ static void ConvertUnshreddedStats(BaseStatistics &result, optional_ptr(const_data_ptr_cast(impala_timestamp.value)); auto microseconds = nanoseconds / NANOSECONDS_PER_MICRO; - return days_since_epoch * MICROSECONDS_PER_DAY + microseconds; + int64_t result; + if (!TryMultiplyOperator::Operation(days_since_epoch, MICROSECONDS_PER_DAY, result) || + !TryAddOperator::Operation(result, microseconds, result)) { + throw InvalidInputException("Int96 timestamp is out of range"); + } + return result; } static int64_t ImpalaTimestampToNanoseconds(const Int96 &impala_timestamp) { diff --git a/src/duckdb/extension/parquet/reader/list_column_reader.cpp b/src/duckdb/extension/parquet/reader/list_column_reader.cpp index be3b792ad..36f13e837 100644 --- a/src/duckdb/extension/parquet/reader/list_column_reader.cpp +++ b/src/duckdb/extension/parquet/reader/list_column_reader.cpp @@ -214,6 +214,9 @@ ColumnReader &ListColumnReader::GetChildReader() { } void ListColumnReader::ApplyPendingSkips(data_ptr_t define_out, data_ptr_t repeat_out) { + if (pending_skips == 0) { + return; + } ColumnReaderInput empty_input(pending_skips, nullptr, nullptr); ReadInternal(empty_input, nullptr); pending_skips = 0; diff --git a/src/duckdb/generated_extension_loader_package_build.cpp b/src/duckdb/generated_extension_loader_package_build.cpp index 092026dbb..5872d3150 100644 --- a/src/duckdb/generated_extension_loader_package_build.cpp +++ b/src/duckdb/generated_extension_loader_package_build.cpp @@ -45,7 +45,7 @@ int32_t duckdb_extension_core_functions_describe(duckdb_extension_descriptor *de descriptor->version = 1; descriptor->name = "core_functions"; descriptor->extension_version = EXT_VERSION_CORE_FUNCTIONS; - descriptor->api_version = "v2.1.0-alpha48840"; + descriptor->api_version = "v2.1.0-alpha49002"; descriptor->entry_cpp = (void (*)(void))core_functions_duckdb_cpp_init; return 0; } @@ -78,7 +78,7 @@ int32_t duckdb_extension_parquet_describe(duckdb_extension_descriptor *descripto descriptor->version = 1; descriptor->name = "parquet"; descriptor->extension_version = EXT_VERSION_PARQUET; - descriptor->api_version = "v2.1.0-alpha48840"; + descriptor->api_version = "v2.1.0-alpha49002"; descriptor->entry_cpp = (void (*)(void))parquet_duckdb_cpp_init; return 0; } @@ -111,7 +111,7 @@ int32_t duckdb_extension_icu_describe(duckdb_extension_descriptor *descriptor) { descriptor->version = 1; descriptor->name = "icu"; descriptor->extension_version = EXT_VERSION_ICU; - descriptor->api_version = "v2.1.0-alpha48840"; + descriptor->api_version = "v2.1.0-alpha49002"; descriptor->entry_cpp = (void (*)(void))icu_duckdb_cpp_init; return 0; } @@ -144,7 +144,7 @@ int32_t duckdb_extension_json_describe(duckdb_extension_descriptor *descriptor) descriptor->version = 1; descriptor->name = "json"; descriptor->extension_version = EXT_VERSION_JSON; - descriptor->api_version = "v2.1.0-alpha48840"; + descriptor->api_version = "v2.1.0-alpha49002"; descriptor->entry_cpp = (void (*)(void))json_duckdb_cpp_init; return 0; } diff --git a/src/duckdb/src/catalog/catalog_entry/duck_schema_entry.cpp b/src/duckdb/src/catalog/catalog_entry/duck_schema_entry.cpp index e541d1441..d2df08a3b 100644 --- a/src/duckdb/src/catalog/catalog_entry/duck_schema_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/duck_schema_entry.cpp @@ -366,8 +366,7 @@ void DuckSchemaEntry::DropEntry(ClientContext &context, DropInfo &info) { auto transaction = GetCatalogTransaction(context); auto existing_entry = set.GetEntry(transaction, info.GetQualifiedName().Name()); if (!existing_entry) { - throw InternalException("Failed to drop entry \"%s\" - entry could not be found", - info.GetQualifiedName().Name()); + throw InternalException("Failed to drop entry %s - entry could not be found", info.GetQualifiedName().Name()); } if (existing_entry->type != info.type) { throw CatalogException("Existing object %s is of type %s, trying to drop type %s", diff --git a/src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp b/src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp index eebc44b0a..4e6ffb78b 100644 --- a/src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp @@ -81,10 +81,11 @@ static void CheckTypeIsSupported(const LogicalType &logical_type, AttachedDataba } } break; case LogicalTypeId::TUPLE: { - // TUPLEs are stored as unnamed STRUCTs on disk, which older engines reject - gate them to v2.0.0+ + // TUPLEs are stored as unnamed STRUCTs on disk, which older engines reject - gate creation to v2.0.0+ + // existing unnamed STRUCTs (deserialized as TUPLE while loading an older file) must stay attachable const auto storage_version = db.GetStorageManager().GetStorageVersion(); - if (storage_version < StorageVersion::V2_0_0) { + if (storage_version < StorageVersion::V2_0_0 && db.GetStorageManager().IsLoaded()) { auto required = GetStorageVersionName(StorageVersion::V2_0_0, false); auto current = GetStorageVersionName(storage_version, false); @@ -513,7 +514,7 @@ unique_ptr DuckTableEntry::RenameColumn(ClientContext &context, Re for (idx_t i = 0; i < columns.size(); i++) { if (columns[i] == info.old_name) { throw CatalogException( - "Cannot rename column \"%s\" because this is involved in the foreign key constraint", + "Cannot rename column %s because this is involved in the foreign key constraint", info.old_name); } } @@ -552,8 +553,8 @@ unique_ptr DuckTableEntry::RenameColumn(ClientContext &context, Re auto &trig = concurrent_entry.Cast(); for (const auto &col : trig.columns) { if (col == info.old_name) { - throw TransactionException("Catalog write-write conflict on alter with \"%s\": trigger \"%s\" " - "references column \"%s\" which is being renamed", + throw TransactionException("Catalog write-write conflict on alter with %s: trigger %s " + "references column %s which is being renamed", name, trig.name, info.old_name); } } @@ -978,7 +979,7 @@ DroppedFieldMapping DropFieldFromStruct(const LogicalType &type, const vector DuckTableEntry::ChangeColumnType(ClientContext &context // TODO: check if the generated_expression breaks, only delete it if it does if (copy.Generated() && column_dependency_manager.IsDependencyOf(col.Logical(), change_idx)) { throw BinderException( - "This column is referenced by the generated column \"%s\", so its type can not be changed", - copy.Name()); + "This column is referenced by the generated column %s, so its type can not be changed", copy.Name()); } create_info->columns.AddColumn(std::move(copy)); } diff --git a/src/duckdb/src/catalog/catalog_entry/scalar_function_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/scalar_function_catalog_entry.cpp index e51e22270..d591bef6d 100644 --- a/src/duckdb/src/catalog/catalog_entry/scalar_function_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/scalar_function_catalog_entry.cpp @@ -37,8 +37,8 @@ unique_ptr ScalarFunctionCatalogEntry::AlterEntry(CatalogTransacti ScalarFunctionSet new_set = functions; if (!new_set.MergeFunctionSet(add_overloads.new_overloads->functions, true)) { - throw BinderException( - "Failed to add new function overloads to function \"%s\": function overload already exists", name); + throw BinderException("Failed to add new function overloads to function %s: function overload already exists", + name); } CreateScalarFunctionInfo new_info(std::move(new_set)); new_info.internal = internal; diff --git a/src/duckdb/src/catalog/catalog_entry/sequence_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/sequence_catalog_entry.cpp index aa98813cf..4515dd815 100644 --- a/src/duckdb/src/catalog/catalog_entry/sequence_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/sequence_catalog_entry.cpp @@ -75,7 +75,7 @@ int64_t SequenceCatalogEntry::NextValue(DuckTransaction &transaction) { state->data.min_value); } if (result > state->data.max_value || overflow) { - throw SequenceException("nextval: reached maximum value of sequence \"%s\" (%lld)", name, + throw SequenceException("nextval: reached maximum value of sequence %s (%lld)", name, state->data.max_value); } } diff --git a/src/duckdb/src/catalog/catalog_entry/table_function_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/table_function_catalog_entry.cpp index bce39a750..f7dfcde37 100644 --- a/src/duckdb/src/catalog/catalog_entry/table_function_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/table_function_catalog_entry.cpp @@ -34,7 +34,7 @@ unique_ptr TableFunctionCatalogEntry::AlterEntry(CatalogTransactio TableFunctionSet new_set = functions; if (!new_set.MergeFunctionSet(add_overloads.new_overloads)) { - throw BinderException("Failed to add new function overloads to function \"%s\": function already exists", name); + throw BinderException("Failed to add new function overloads to function %s: function already exists", name); } CreateTableFunctionInfo new_info(std::move(new_set)); new_info.internal = internal; diff --git a/src/duckdb/src/catalog/catalog_entry/view_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/view_catalog_entry.cpp index 63b4209b8..ee3504bba 100644 --- a/src/duckdb/src/catalog/catalog_entry/view_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/view_catalog_entry.cpp @@ -145,7 +145,7 @@ Identifier ViewCatalogEntry::ResolveColumnName(const Identifier &column_name) co void ViewCatalogEntry::BindView(ClientContext &context, BindViewAction action) { if (bind_state == ViewBindState::BINDING && bind_thread == ThreadUtil::GetThreadId()) { - throw InvalidInputException("View \"%s\" was requested to be bound but this thread is already binding that " + throw InvalidInputException("View %s was requested to be bound but this thread is already binding that " "view - this likely means the view was attempted to be bound recursively", name); } diff --git a/src/duckdb/src/catalog/catalog_set.cpp b/src/duckdb/src/catalog/catalog_set.cpp index 361df9dda..117b0e6a6 100644 --- a/src/duckdb/src/catalog/catalog_set.cpp +++ b/src/duckdb/src/catalog/catalog_set.cpp @@ -28,7 +28,7 @@ void CatalogEntryMap::AddEntry(unique_ptr entry) { auto name = entry->name; if (entries.find(name) != entries.end()) { - throw InternalException("Entry with name \"%s\" already exists", name); + throw InternalException("Entry with name %s already exists", name); } entries.insert(make_pair(name, std::move(entry))); } @@ -38,7 +38,7 @@ void CatalogEntryMap::UpdateEntry(unique_ptr catalog_entry) { auto entry = entries.find(name); if (entry == entries.end()) { - throw InternalException("Entry with name \"%s\" does not exist", name); + throw InternalException("Entry with name %s does not exist", name); } auto existing = std::move(entry->second); @@ -54,7 +54,7 @@ void CatalogEntryMap::DropEntry(CatalogEntry &entry) { auto &name = entry.name; auto chain = GetEntry(name); if (!chain) { - throw InternalException("Attempting to drop entry with name \"%s\" but no chain with that name exists", name); + throw InternalException("Attempting to drop entry with name %s but no chain with that name exists", name); } auto child = entry.TakeChild(); if (!entry.HasParent()) { @@ -118,7 +118,7 @@ bool CatalogSet::VerifyVacancy(CatalogTransaction transaction, CatalogEntry &ent // A transaction that is not visible to our snapshot has already made a change to this entry. // Because of Catalog limitations we can't push our change on this, even if the change was made by another // active transaction that might end up being aborted. So we have to cancel this transaction. - throw TransactionException("Catalog write-write conflict on create with \"%s\"", entry.name); + throw TransactionException("Catalog write-write conflict on create with %s", entry.name); } // The entry is visible to our snapshot if (!entry.deleted) { @@ -133,16 +133,15 @@ static bool IsDependencyEntry(CatalogEntry &entry) { void CatalogSet::CheckCatalogEntryInvariants(CatalogEntry &value, const Identifier &name) { if (value.internal && !catalog.IsSystemCatalog() && name != DEFAULT_SCHEMA) { - throw InternalException("Attempting to create internal entry \"%s\" in non-system catalog - internal entries " + throw InternalException("Attempting to create internal entry %s in non-system catalog - internal entries " "can only be created in the system catalog", name); } if (!value.internal) { if (!value.temporary && catalog.IsSystemCatalog() && !IsDependencyEntry(value)) { - throw InternalException( - "Attempting to create non-internal entry \"%s\" in system catalog - the system catalog " - "can only contain internal entries", - name); + throw InternalException("Attempting to create non-internal entry %s in system catalog - the system catalog " + "can only contain internal entries", + name); } if (value.temporary && !catalog.IsTemporaryCatalog()) { throw InternalException("Attempting to create temporary entry %s in non-temporary catalog", name); diff --git a/src/duckdb/src/catalog/dependency_list.cpp b/src/duckdb/src/catalog/dependency_list.cpp index 7e9ddbcc4..2f3dd216e 100644 --- a/src/duckdb/src/catalog/dependency_list.cpp +++ b/src/duckdb/src/catalog/dependency_list.cpp @@ -100,8 +100,8 @@ void LogicalDependencyList::VerifyDependencies(Catalog &catalog, const Identifie for (auto &dep : set) { if (dep.catalog != catalog.GetName()) { throw DependencyException( - "Error adding dependency for object \"%s\" - dependency \"%s\" is in catalog " - "\"%s\", which does not match the catalog \"%s\".\nCross catalog dependencies are not supported.", + "Error adding dependency for object %s - dependency %s is in catalog " + "%s, which does not match the catalog %s.\nCross catalog dependencies are not supported.", name, dep.entry.name, dep.catalog, catalog.GetName()); } } diff --git a/src/duckdb/src/catalog/dependency_manager.cpp b/src/duckdb/src/catalog/dependency_manager.cpp index 858a8bfc5..9749f9172 100644 --- a/src/duckdb/src/catalog/dependency_manager.cpp +++ b/src/duckdb/src/catalog/dependency_manager.cpp @@ -571,7 +571,7 @@ void DependencyManager::VerifyExistence(CatalogTransaction transaction, Dependen if (!subject.flags.IsOwnership() && subject.oid.IsValid() && lookup_result.result && lookup_result.result->oid != subject.oid.GetIndex()) { throw DependencyException( - "Could not commit creation of dependency, subject \"%s\" was dropped and re-created by another transaction", + "Could not commit creation of dependency, subject %s was dropped and re-created by another transaction", object.EntryInfo().name); } } diff --git a/src/duckdb/src/common/allocator/allocator.cpp b/src/duckdb/src/common/allocator/allocator.cpp index 948b87cd9..20074f990 100644 --- a/src/duckdb/src/common/allocator/allocator.cpp +++ b/src/duckdb/src/common/allocator/allocator.cpp @@ -9,13 +9,6 @@ #include -#if defined(__linux__) && INTPTR_MAX == INT64_MAX -#include -#include -#include -#include -#endif - #ifdef __GLIBC__ #include #endif @@ -123,43 +116,6 @@ Allocator::Allocator(allocate_function_ptr_t allocate_function_p, free_function_ Allocator::~Allocator() { } -#if defined(__linux__) && INTPTR_MAX == INT64_MAX && defined(MADV_HUGEPAGE) -static idx_t GetHugePageSize() { - auto file = fopen("/sys/kernel/mm/transparent_hugepage/hpage_pmd_size", "r"); - if (!file) { - return 0; - } - int64_t huge_page_size = 0; - char trailing; - auto fields = fscanf(file, "%" SCNd64 " %c", &huge_page_size, &trailing); - fclose(file); - auto page_size = sysconf(_SC_PAGESIZE); - if (fields != 1 || page_size <= 0 || huge_page_size < page_size || (huge_page_size & (huge_page_size - 1)) != 0 || - huge_page_size % page_size != 0) { - return 0; - } - return NumericCast(huge_page_size); -} -#endif - -AllocatedData Allocator::TryAllocateHuge(idx_t size) { - auto result = Allocate(size); -#if defined(__linux__) && INTPTR_MAX == INT64_MAX && defined(MADV_HUGEPAGE) - static constexpr idx_t MIN_ADVISE_SIZE = 8ULL * 1024 * 1024; - if (size >= MIN_ADVISE_SIZE) { - static const idx_t huge_page_size = GetHugePageSize(); - if (huge_page_size != 0 && size / huge_page_size >= 4) { - const auto base = reinterpret_cast(result.get()); - const auto offset = (huge_page_size - base % huge_page_size) % huge_page_size; - const auto length = ((size - offset) / huge_page_size) * huge_page_size; - // Only advise complete huge pages contained in this allocation. - madvise(result.get() + offset, length, MADV_HUGEPAGE); - } - } -#endif - return result; -} - data_ptr_t Allocator::AllocateData(idx_t size) { D_ASSERT(size > 0); if (size >= MAXIMUM_ALLOC_SIZE) { diff --git a/src/duckdb/src/common/bignum.cpp b/src/duckdb/src/common/bignum.cpp index 1e6b25b6d..3ec9ccf21 100644 --- a/src/duckdb/src/common/bignum.cpp +++ b/src/duckdb/src/common/bignum.cpp @@ -1,7 +1,11 @@ #include "duckdb/common/bignum.hpp" #include "duckdb/common/types/bignum.hpp" +#include "duckdb/common/bit_utils.hpp" +#include "duckdb/main/client_context.hpp" #include "duckdb/common/printer.hpp" #include "duckdb/common/to_string.hpp" +#include "duckdb/common/vector.hpp" +#include namespace duckdb { void PrintBits(const char value) { @@ -96,6 +100,7 @@ bool BignumIntermediate::IsMSBSet() const { } return (data[0] & 0x80) != 0; } + void BignumIntermediate::Initialize(ArenaAllocator &allocator) { is_negative = false; size = 1; @@ -258,7 +263,7 @@ string_t BignumIntermediate::Negate(StringHeap &heap) const { ptr[i + Bignum::BIGNUM_HEADER_SIZE] = static_cast(~data[i]); } } - + target.Finalize(); return target; } @@ -368,4 +373,280 @@ void BignumIntermediate::AddInPlace(ArenaAllocator &allocator, const BignumInter } } +//===--------------------------------------------------------------------===// +// Multiplication / Division +//===--------------------------------------------------------------------===// +namespace { + +//! Little-endian base 2^32 digits of an absolute value without leading zero digits; zero is the empty vector +using bignum_magnitude_t = unsafe_vector; + +//! Digit operations between two interrupt checks, roughly a millisecond of work +constexpr idx_t INTERRUPT_CHECK_INTERVAL = idx_t(1) << 20; + +void AccountWork(BignumArithmeticState &state, idx_t work) { + if (!state.context) { + return; + } + state.work_since_interrupt_check += work; + if (state.work_since_interrupt_check >= INTERRUPT_CHECK_INTERVAL) { + state.work_since_interrupt_check = 0; + state.context->InterruptCheck(); + } +} + +void TrimMagnitude(bignum_magnitude_t &digits) { + while (!digits.empty() && digits.back() == 0) { + digits.pop_back(); + } +} + +void GetMagnitude(const BignumIntermediate &value, bignum_magnitude_t &digits) { + digits.clear(); + const idx_t start = value.GetStartDataPos(); + digits.reserve((value.size - start + Bignum::DIGIT_BYTES - 1) / Bignum::DIGIT_BYTES); + digit_t current = 0; + uint8_t shift = 0; + // walk the big-endian magnitude bytes from the least significant byte upwards + for (idx_t i = value.size; i > start; i--) { + current |= static_cast(value.GetAbsoluteByte(static_cast(i - 1))) << shift; + shift += 8; + if (shift == Bignum::DIGIT_BITS) { + digits.push_back(current); + current = 0; + shift = 0; + } + } + if (shift != 0) { + digits.push_back(current); + } + TrimMagnitude(digits); +} + +//! The number of significant bits of a magnitude, 0 for zero +idx_t MagnitudeBitCount(const bignum_magnitude_t &digits) { + if (digits.empty()) { + return 0; + } + return digits.size() * Bignum::DIGIT_BITS - CountZeros::Leading(digits.back()); +} + +idx_t BitsToBytes(idx_t bits) { + return (bits + 7) / 8; +} + +//! Writes a (sign, magnitude) pair as a BIGNUM blob into the heap, checking for overflow +string_t WriteMagnitude(StringHeap &heap, const bignum_magnitude_t &digits, bool is_negative) { + if (digits.empty()) { + // zero is always encoded as a single positive zero byte + const auto zero = Bignum::InitializeBignumZero(); + return heap.AddBlob(zero.data(), zero.size()); + } + const idx_t byte_count = BitsToBytes(MagnitudeBitCount(digits)); + if (byte_count > Bignum::MAX_DATA_SIZE) { + throw OutOfRangeException(ProduceOverUnderFlowError(is_negative, 0, byte_count)); + } + auto target = heap.EmptyString(byte_count + Bignum::BIGNUM_HEADER_SIZE); + auto target_data = target.GetDataWriteable(); + Bignum::SetHeader(target_data, byte_count, is_negative); + auto result = target_data + Bignum::BIGNUM_HEADER_SIZE; + for (idx_t i = 0; i < byte_count; i++) { + // byte i is the i-th most significant byte of the result + const idx_t byte_from_lsb = byte_count - 1 - i; + D_ASSERT(byte_from_lsb / Bignum::DIGIT_BYTES < digits.size()); + const auto byte = static_cast(digits[byte_from_lsb / Bignum::DIGIT_BYTES] >> + (8 * (byte_from_lsb % Bignum::DIGIT_BYTES))); + result[i] = static_cast(is_negative ? static_cast(~byte) : byte); + } + target.Finalize(); + return target; +} + +//! Schoolbook multiplication: result = lhs * rhs +void MultiplyMagnitudes(BignumArithmeticState &state, const bignum_magnitude_t &lhs, const bignum_magnitude_t &rhs, + bignum_magnitude_t &result) { + result.assign(lhs.size() + rhs.size(), 0); + for (idx_t i = 0; i < lhs.size(); i++) { + AccountWork(state, rhs.size()); + twodigit_t carry = 0; + const twodigit_t lhs_digit = lhs[i]; + for (idx_t j = 0; j < rhs.size(); j++) { + // (2^32 - 1)^2 + 2 * (2^32 - 1) = 2^64 - 1, so this cannot overflow + const twodigit_t product = lhs_digit * rhs[j] + result[i + j] + carry; + result[i + j] = static_cast(product); + carry = product >> Bignum::DIGIT_BITS; + } + result[i + rhs.size()] = static_cast(carry); + } + TrimMagnitude(result); +} + +//! Long division following Knuth's Algorithm D (TAOCP 4.3.1); the remainder is only produced on request +void DivModMagnitudes(BignumArithmeticState &state, const bignum_magnitude_t ÷nd, + const bignum_magnitude_t &divisor, bignum_magnitude_t "ient, bignum_magnitude_t &remainder, + bool want_remainder) { + D_ASSERT(!divisor.empty()); + const idx_t m = dividend.size(); + const idx_t n = divisor.size(); + if (m < n) { + quotient.clear(); + if (want_remainder) { + remainder = dividend; + } + return; + } + constexpr twodigit_t BASE = static_cast(1) << Bignum::DIGIT_BITS; + constexpr twodigit_t DIGIT_MASK = BASE - 1; + if (n == 1) { + // single digit divisor: simple short division + AccountWork(state, m); + const twodigit_t d = divisor[0]; + twodigit_t rem = 0; + quotient.assign(m, 0); + for (idx_t i = m; i > 0; i--) { + const twodigit_t current = (rem << Bignum::DIGIT_BITS) | dividend[i - 1]; + quotient[i - 1] = static_cast(current / d); + rem = current % d; + } + TrimMagnitude(quotient); + remainder.clear(); + if (want_remainder && rem != 0) { + remainder.push_back(static_cast(rem)); + } + return; + } + + // D1: normalize so that the most significant divisor digit has its top bit set + const auto shift = static_cast(CountZeros::Leading(divisor[n - 1])); + auto &vn = state.normalized_divisor; + auto &un = state.normalized_dividend; + vn.assign(n, 0); + un.assign(m + 1, 0); + if (shift > 0) { + for (idx_t i = n - 1; i > 0; i--) { + vn[i] = (divisor[i] << shift) | (divisor[i - 1] >> (Bignum::DIGIT_BITS - shift)); + } + vn[0] = divisor[0] << shift; + un[m] = dividend[m - 1] >> (Bignum::DIGIT_BITS - shift); + for (idx_t i = m - 1; i > 0; i--) { + un[i] = (dividend[i] << shift) | (dividend[i - 1] >> (Bignum::DIGIT_BITS - shift)); + } + un[0] = dividend[0] << shift; + } else { + std::copy(divisor.begin(), divisor.end(), vn.begin()); + std::copy(dividend.begin(), dividend.end(), un.begin()); + } + + quotient.assign(m - n + 1, 0); + for (idx_t j = m - n + 1; j > 0; j--) { + AccountWork(state, n); + const idx_t pos = j - 1; + // D3: estimate the quotient digit + const twodigit_t numerator = (static_cast(un[pos + n]) << Bignum::DIGIT_BITS) | un[pos + n - 1]; + twodigit_t qhat = numerator / vn[n - 1]; + twodigit_t rhat = numerator % vn[n - 1]; + while (qhat >= BASE || qhat * vn[n - 2] > ((rhat << Bignum::DIGIT_BITS) | un[pos + n - 2])) { + qhat--; + rhat += vn[n - 1]; + if (rhat >= BASE) { + break; + } + } + // D4: multiply and subtract + // the borrow is at most BASE: the high half of a product is at most BASE - 2 and we borrow at most 2 units + twodigit_t borrow = 0; + for (idx_t i = 0; i < n; i++) { + const twodigit_t product = qhat * vn[i]; + const twodigit_t subtrahend = (product & DIGIT_MASK) + borrow; + const twodigit_t current = un[i + pos]; + if (current >= subtrahend) { + un[i + pos] = static_cast(current - subtrahend); + borrow = product >> Bignum::DIGIT_BITS; + } else { + // the subtrahend is below 2 * BASE, so we need to borrow one or two units from the next digit + const twodigit_t deficit = subtrahend - current; + const twodigit_t units = (deficit + BASE - 1) / BASE; + un[i + pos] = static_cast(current + units * BASE - subtrahend); + borrow = (product >> Bignum::DIGIT_BITS) + units; + } + } + const twodigit_t top = un[pos + n]; + const bool went_negative = top < borrow; + un[pos + n] = static_cast(went_negative ? top + BASE - borrow : top - borrow); + quotient[pos] = static_cast(qhat); + if (went_negative) { + // D6: the estimate was one too large, add the divisor back + quotient[pos]--; + twodigit_t carry = 0; + for (idx_t i = 0; i < n; i++) { + const twodigit_t sum = static_cast(un[i + pos]) + vn[i] + carry; + un[i + pos] = static_cast(sum); + carry = sum >> Bignum::DIGIT_BITS; + } + un[pos + n] = static_cast(un[pos + n] + carry); + } + } + TrimMagnitude(quotient); + if (!want_remainder) { + remainder.clear(); + return; + } + + // D8: denormalize the remainder + remainder.assign(n, 0); + if (shift > 0) { + for (idx_t i = 0; i < n; i++) { + remainder[i] = (un[i] >> shift) | (un[i + 1] << (Bignum::DIGIT_BITS - shift)); + } + } else { + std::copy(un.begin(), un.begin() + static_cast(n), remainder.begin()); + } + TrimMagnitude(remainder); +} + +} // namespace + +string_t BignumIntermediate::Multiply(StringHeap &heap, BignumArithmeticState &state, const BignumIntermediate &lhs, + const BignumIntermediate &rhs) { + const bool is_result_negative = lhs.is_negative != rhs.is_negative; + auto &lhs_digits = state.lhs_digits; + auto &rhs_digits = state.rhs_digits; + auto &product = state.result_digits; + GetMagnitude(lhs, lhs_digits); + GetMagnitude(rhs, rhs_digits); + if (lhs_digits.empty() || rhs_digits.empty()) { + product.clear(); + return WriteMagnitude(heap, product, false); + } + // the product has a + b - 1 or a + b bits: bail out early when even the lower bound overflows + const idx_t min_result_bytes = BitsToBytes(MagnitudeBitCount(lhs_digits) + MagnitudeBitCount(rhs_digits) - 1); + if (min_result_bytes > Bignum::MAX_DATA_SIZE) { + throw OutOfRangeException(ProduceOverUnderFlowError(is_result_negative, 0, min_result_bytes)); + } + MultiplyMagnitudes(state, lhs_digits, rhs_digits, product); + return WriteMagnitude(heap, product, is_result_negative); +} + +bool BignumIntermediate::TryDivMod(StringHeap &heap, BignumArithmeticState &state, const BignumIntermediate &lhs, + const BignumIntermediate &rhs, bool want_remainder, string_t &result) { + auto &lhs_digits = state.lhs_digits; + auto &rhs_digits = state.rhs_digits; + auto "ient = state.result_digits; + auto &remainder = state.remainder_digits; + GetMagnitude(lhs, lhs_digits); + GetMagnitude(rhs, rhs_digits); + if (rhs_digits.empty()) { + return false; + } + DivModMagnitudes(state, lhs_digits, rhs_digits, quotient, remainder, want_remainder); + if (want_remainder) { + // the remainder takes the sign of the dividend + result = WriteMagnitude(heap, remainder, lhs.is_negative); + } else { + // truncating division: the quotient is negative when the signs differ + result = WriteMagnitude(heap, quotient, lhs.is_negative != rhs.is_negative); + } + return true; +} + } // namespace duckdb diff --git a/src/duckdb/src/common/exception_format_value.cpp b/src/duckdb/src/common/exception_format_value.cpp index 0fa11f916..5c797c0bb 100644 --- a/src/duckdb/src/common/exception_format_value.cpp +++ b/src/duckdb/src/common/exception_format_value.cpp @@ -58,7 +58,9 @@ ExceptionFormatValue ExceptionFormatValue::CreateFormatValue(const String &value } template <> ExceptionFormatValue ExceptionFormatValue::CreateFormatValue(const Identifier &value) { - return SQLQuotedIdentifier::ToString(value); + ExceptionFormatValue result(SQLQuotedIdentifier::ToString(value)); + result.quoted_identifier = true; + return result; } template <> @@ -92,7 +94,38 @@ ExceptionFormatValue ExceptionFormatValue::CreateFormatValue(const uhugeint_t &v return ExceptionFormatValue(value); } +//! Whether a placeholder of the format string that receives a quoted identifier is itself wrapped in quotes +static bool HasDoubleQuotedIdentifier(const string &msg, const std::vector &values) { + idx_t arg_idx = 0; + for (idx_t i = 0; i < msg.size(); i++) { + if (msg[i] != '%') { + continue; + } + if (i + 1 < msg.size() && msg[i + 1] == '%') { + i++; + continue; + } + // skip flags, width and precision up to the conversion character + idx_t end = i + 1; + while (end < msg.size() && !isalpha(msg[end])) { + end++; + } + if (arg_idx < values.size() && values[arg_idx].quoted_identifier) { + bool quote_before = i > 0 && msg[i - 1] == '"'; + bool quote_after = end + 1 < msg.size() && msg[end + 1] == '"'; + if (quote_before && quote_after) { + return true; + } + } + arg_idx++; + i = end; + } + return false; +} + string ExceptionFormatValue::Format(const string &msg, std::vector &values) { + // identifiers are quoted by the formatter, the format string must not quote them again + D_ASSERT(!HasDoubleQuotedIdentifier(msg, values)); try { std::vector> format_args; for (auto &val : values) { diff --git a/src/duckdb/src/common/file_system.cpp b/src/duckdb/src/common/file_system.cpp index 802417eaa..04a78aaa0 100644 --- a/src/duckdb/src/common/file_system.cpp +++ b/src/duckdb/src/common/file_system.cpp @@ -32,7 +32,7 @@ #include #include -#ifdef __MVS__ +#ifndef __EMSCRIPTEN__ #include #endif @@ -288,6 +288,25 @@ string FileSystem::GetWorkingDirectory() { #endif +optional_idx FileSystem::GetFileDescriptorLimit() { +#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) + struct rlimit limit; + if (getrlimit(RLIMIT_NOFILE, &limit) != 0 || limit.rlim_cur == RLIM_INFINITY) { + return optional_idx(); + } + if (limit.rlim_cur >= NumericLimits::Maximum()) { + return optional_idx(); + } + return optional_idx(static_cast(limit.rlim_cur)); +#else + return optional_idx(); +#endif +} + +idx_t FileSystem::GetFileDescriptorShare(idx_t file_descriptor_limit) { + return MaxValue(file_descriptor_limit / 8, 1); +} + string FileSystem::JoinPath(const string &a, const string &b) { // A remote URL may carry a "?query" suffix (object version, requester-pays, an SSE-C key, ...). // Appending a path segment after it would bury the query mid-path and corrupt both the query and diff --git a/src/duckdb/src/common/local_file_system.cpp b/src/duckdb/src/common/local_file_system.cpp index 664659e76..0b5583e8e 100644 --- a/src/duckdb/src/common/local_file_system.cpp +++ b/src/duckdb/src/common/local_file_system.cpp @@ -866,11 +866,53 @@ std::string LocalFileSystem::GetLastErrorAsString() { bool LocalFileSystem::TryCanonicalizeExistingPath(string &input) { char resolved[PATH_MAX]; - if (!realpath(input.c_str(), resolved)) { - return false; + if (realpath(input.c_str(), resolved)) { + input = resolved; + return true; } - input = resolved; - return true; + // the path might be a dangling symlink - follow the chain and canonicalize the parent of the final target + static constexpr idx_t MAX_SYMLINK_DEPTH = 40; + string current = input; + for (idx_t depth = 0; depth < MAX_SYMLINK_DEPTH; depth++) { + char link_target[PATH_MAX]; + auto link_length = readlink(current.c_str(), link_target, sizeof(link_target) - 1); + if (link_length < 0) { + return false; + } + string target(link_target, NumericCast(link_length)); + auto sep = current.rfind('/'); + if (target.empty() || sep == string::npos) { + return false; + } + if (target[0] != '/') { + target = current.substr(0, sep + 1) + target; + } + if (realpath(target.c_str(), resolved)) { + input = resolved; + return true; + } + auto target_sep = target.rfind('/'); + if (target_sep == string::npos) { + return false; + } + struct stat target_stat; + if (lstat(target.c_str(), &target_stat) == 0 && S_ISLNK(target_stat.st_mode)) { + current = target; + continue; + } + // final target does not exist - canonicalize its parent directory + auto parent = target.substr(0, target_sep + 1); + if (!realpath(parent.c_str(), resolved)) { + return false; + } + string result = resolved; + if (result.empty() || result.back() != '/') { + result += '/'; + } + input = result + target.substr(target_sep + 1); + return true; + } + return false; } bool LocalFileSystem::PathStartsWithDrive(const string &path) { diff --git a/src/duckdb/src/common/multi_file/multi_file_column_mapper.cpp b/src/duckdb/src/common/multi_file/multi_file_column_mapper.cpp index af5b3c272..18bf44c1a 100644 --- a/src/duckdb/src/common/multi_file/multi_file_column_mapper.cpp +++ b/src/duckdb/src/common/multi_file/multi_file_column_mapper.cpp @@ -119,7 +119,7 @@ struct FieldIdMapper : public ColumnMapper { static unique_ptr GetDefault(ClientContext &context, const MultiFileColumnDefinition &column) { auto &default_val = column.default_expression; if (!default_val) { - throw InvalidInputException("Field \"%s\" (field id %d) is missing from the file schema and has no default " + throw InvalidInputException("Field %s (field id %d) is missing from the file schema and has no default " "expression", column.name, column.GetIdentifierFieldId()); } @@ -881,7 +881,7 @@ Value MultiFileColumnMapper::GetConstantValue(MultiFileGlobalIndex global_index) } } auto &global_column = global_columns[global_column_id]; - throw InternalException("Column '%s' is not present in the file, but no constant_map entry exists for it!", + throw InternalException("Column %s is not present in the file, but no constant_map entry exists for it!", global_column.name); } diff --git a/src/duckdb/src/common/multi_file/multi_file_reader.cpp b/src/duckdb/src/common/multi_file/multi_file_reader.cpp index c791368b7..4e3be75b9 100644 --- a/src/duckdb/src/common/multi_file/multi_file_reader.cpp +++ b/src/duckdb/src/common/multi_file/multi_file_reader.cpp @@ -495,7 +495,7 @@ bool MultiFileReader::ParseCopyOption(const Identifier &key, const vector string error_message; auto value = values[0].DefaultTryCastAs(LogicalType::BOOLEAN, &error_message); if (!value) { - throw InvalidInputException("Unable to cast \"%s\" to BOOLEAN for boolean option \"%s\"", values[0].ToString(), + throw InvalidInputException("Unable to cast \"%s\" to BOOLEAN for boolean option %s", values[0].ToString(), key); } options.file_row_number = BooleanValue::Get(*value); @@ -1385,7 +1385,7 @@ void MultiFileOptions::SetMaximumSampleFiles(const Identifier &key, const Value throw BinderException("Cannot use NULL as argument to key %s", key); } if (!TrySetMaximumSampleFiles(val)) { - throw BinderException("\"%s\" parameter must be positive, or -1 to remove the limit on the number of files " + throw BinderException("%s parameter must be positive, or -1 to remove the limit on the number of files " "used to determine the schema.", key); } @@ -1419,7 +1419,7 @@ void MultiFileOptions::SetColumnStatistics(const Identifier &key, const Value &v auto cast = val.DefaultTryCastAs(target, &error); if (!cast) { throw InvalidInputException( - "'%s' must be a MAP of column name to a MAP of statistic name ('min', 'max', 'count', 'null_count', " + "%s must be a MAP of column name to a MAP of statistic name ('min', 'max', 'count', 'null_count', " "'distinct_count') to its value, but '%s' was provided", key, val.type().ToString()); } diff --git a/src/duckdb/src/common/serializer/async_write_queue.cpp b/src/duckdb/src/common/serializer/async_write_queue.cpp index 363b372f4..10d3a8985 100644 --- a/src/duckdb/src/common/serializer/async_write_queue.cpp +++ b/src/duckdb/src/common/serializer/async_write_queue.cpp @@ -1604,7 +1604,7 @@ void ManagedAsyncWriteStreamQueue::Close() { void ManagedAsyncWriteStreamQueue::AbortWrites() { std::exception_ptr error; - lock_guard submission_guard(submission_lock); + unique_lock submission_guard(submission_lock); idx_t discarded_bytes; shared_ptr local_error_ref; { @@ -1621,6 +1621,8 @@ void ManagedAsyncWriteStreamQueue::AbortWrites() { local_error_ref = local_error; } write_queue->DiscardExternalPendingBytes(discarded_bytes); + // After closing and discarding, release the lock before draining completions that may be waiting to refill. + submission_guard.unlock(); try { write_queue->AbortWrites(); } catch (...) { diff --git a/src/duckdb/src/common/sort/sorted_run.cpp b/src/duckdb/src/common/sort/sorted_run.cpp index f7eab31cf..99dd43dab 100644 --- a/src/duckdb/src/common/sort/sorted_run.cpp +++ b/src/duckdb/src/common/sort/sorted_run.cpp @@ -229,6 +229,140 @@ struct SkaExtractKey { atomic &interrupt_state; }; +template +struct SkaExtractSuffixKey : SkaExtractKey { + SkaExtractSuffixKey(const SkaExtractKey &extract_key, uint64_t varying_bits_p) + : SkaExtractKey(extract_key), varying_bits(varying_bits_p) { + this->ska_sort_width = sizeof(uint64_t); + this->requires_next_sort = true; + } + + static uint64_t ExtractWord(const SORT_KEY &key, idx_t offset) { + D_ASSERT(offset >= sizeof(uint64_t) && offset % sizeof(uint64_t) == 0); + if constexpr (SORT_KEY::INLINE_LENGTH > sizeof(uint64_t)) { + if (offset == sizeof(uint64_t)) { + return key.part1; + } + } + const auto size = key.GetSize(); + if (offset >= size) { + return 0; + } + const auto ptr = key.GetData() + offset; + if (size - offset >= sizeof(uint64_t)) { + return BSwapIfLE(Load(ptr)); + } + uint64_t word = 0; + memcpy(&word, ptr, size - offset); + return BSwapIfLE(word); + } + + static bool PrefixMatches(const SORT_KEY &reference, const SORT_KEY &key, idx_t offset) { + if constexpr (SORT_KEY::INLINE_LENGTH > sizeof(uint64_t)) { + if (offset > sizeof(uint64_t) && reference.part1 != key.part1) { + return false; + } + } + if (offset <= SORT_KEY::INLINE_LENGTH) { + return true; + } + return reference.GetSize() >= offset && key.GetSize() >= offset && + memcmp(reference.GetData() + SORT_KEY::INLINE_LENGTH, key.GetData() + SORT_KEY::INLINE_LENGTH, + offset - SORT_KEY::INLINE_LENGTH) == 0; + } + + bool ByteIsSkippable(const idx_t &byte_offset) const { + D_ASSERT(byte_offset < sizeof(uint64_t)); + return ((varying_bits >> ((sizeof(uint64_t) - 1 - byte_offset) * 8)) & 0xff) == 0; + } + + uint64_t varying_bits; +}; + +template +static void SortTiedBucket(ClientContext &context, const ITERATOR &begin, const ITERATOR &end, + SkaExtractKey &extract_key, const MERGE &merge) { + if constexpr (SORT_KEY::CONSTANT_SIZE) { + duckdb_ska_sort::detail::StdSortFallback(begin, end, extract_key, merge); + } else { + context.InterruptCheck(); + D_ASSERT(end - begin >= 2); + using SUFFIX = SkaExtractSuffixKey; + constexpr idx_t MIN_RADIX_COUNT = 1024; + constexpr idx_t SAMPLE_COUNT = 16; + constexpr idx_t MAX_PREFIX_OFFSET = 256; + const auto count = NumericCast(end - begin); + if (count < MIN_RADIX_COUNT) { + duckdb_ska_sort::detail::StdSortFallback(begin, end, extract_key, merge); + return; + } + + SORT_KEY samples[SAMPLE_COUNT]; + const auto stride = (count - 1) / (SAMPLE_COUNT - 1); + for (idx_t i = 0; i < SAMPLE_COUNT; i++) { + samples[i] = *(begin + (i == SAMPLE_COUNT - 1 ? count - 1 : i * stride)); + } + uint64_t words[SAMPLE_COUNT]; + uint64_t varying_bits = 0; + idx_t offset = sizeof(uint64_t); + while (offset <= MAX_PREFIX_OFFSET) { + varying_bits = 0; + for (idx_t i = 0; i < SAMPLE_COUNT; i++) { + words[i] = SUFFIX::ExtractWord(samples[i], offset); + varying_bits |= words[0] ^ words[i]; + } + if (varying_bits != 0) { + break; + } + offset += sizeof(uint64_t); + } + if (offset > MAX_PREFIX_OFFSET) { + duckdb_ska_sort::detail::StdSortFallback(begin, end, extract_key, merge); + return; + } + for (const auto word : words) { + if (NumericCast(std::count(words, words + SAMPLE_COUNT, word)) >= SAMPLE_COUNT / 4) { + duckdb_ska_sort::detail::StdSortFallback(begin, end, extract_key, merge); + return; + } + } + + // The probe only selects a candidate; every skipped word must be equal across the whole bucket. + idx_t scanned = 0; + for (auto it = begin + 1; it != end; ++it) { + if (++scanned % STANDARD_VECTOR_SIZE == 0) { + context.InterruptCheck(); + } + if (it->part0 != samples[0].part0 || !SUFFIX::PrefixMatches(samples[0], *it, offset)) { + duckdb_ska_sort::detail::StdSortFallback(begin, end, extract_key, merge); + return; + } + varying_bits |= words[0] ^ SUFFIX::ExtractWord(*it, offset); + } + // The common first word can cache the suffix during this synchronous refinement. + struct PrefixRestorer { + ITERATOR begin; + ITERATOR end; + uint64_t prefix; + + ~PrefixRestorer() { + for (auto it = begin; it != end; ++it) { + it->part0 = prefix; + } + } + } restore {begin, end, samples[0].part0}; + scanned = 0; + for (auto it = begin; it != end; ++it) { + if (++scanned % STANDARD_VECTOR_SIZE == 0) { + context.InterruptCheck(); + } + it->part0 = SUFFIX::ExtractWord(*it, offset); + } + SUFFIX suffix(extract_key, varying_bits); + duckdb_ska_sort::ska_sort(begin, end, suffix, merge); + } +} + template static void TemplatedSort(ClientContext &context, TupleDataCollection &key_data, const bool is_index_sort) { const auto &layout = key_data.GetLayout(); @@ -254,8 +388,14 @@ static void TemplatedSort(ClientContext &context, TupleDataCollection &key_data, // order, so that no merge anywhere in this sort allocates on its own (BufferedMerge never does) SortMergeBuffer merge_buffer(BufferAllocator::Get(context), key_data.Count(), sizeof(SORT_KEY)); const BufferedMergePolicy merge(merge_buffer); - const auto fallback = [&ska_extract_key, &merge](const BLOCK_ITERATOR &fb_begin, const BLOCK_ITERATOR &fb_end) { - duckdb_ska_sort::ska_sort(fb_begin, fb_end, ska_extract_key, merge); + const auto sort_ties = [&context](const BLOCK_ITERATOR &b, const BLOCK_ITERATOR &e, + SkaExtractKey &extract_key, + const BufferedMergePolicy &policy) { + SortTiedBucket(context, b, e, extract_key, policy); + }; + const auto fallback = [&ska_extract_key, &merge, &sort_ties](const BLOCK_ITERATOR &fb_begin, + const BLOCK_ITERATOR &fb_end) { + duckdb_ska_sort::ska_sort(fb_begin, fb_end, ska_extract_key, merge, sort_ties); }; duckdb_vergesort::vergesort(begin, end, std::less(), fallback, merge); diff --git a/src/duckdb/src/common/sort/sorted_run_merger.cpp b/src/duckdb/src/common/sort/sorted_run_merger.cpp index 1ddd7c6f0..b6ea5c917 100644 --- a/src/duckdb/src/common/sort/sorted_run_merger.cpp +++ b/src/duckdb/src/common/sort/sorted_run_merger.cpp @@ -225,18 +225,17 @@ class SortedRunMergerGlobalState : public GlobalSourceState { return; } - // Compute average number of tuples per partition to destroy + // Estimate the number of tuples eligible for destruction const auto total_tuples_to_destroy = (end_partition_idx - destroy_partition_idx) * merger.partition_size; - const auto tuples_to_destroy_per_partition = total_tuples_to_destroy / num_partitions; // Compute max tuples per block const auto &run = *merger.sorted_runs[0]; const auto tuples_per_block = run.payload_data ? run.payload_data->TuplesPerBlock() : run.key_data->TuplesPerBlock(); - // Compute number of blocks we can destroy per partition - const auto blocks_to_destroy_per_partition = tuples_to_destroy_per_partition / tuples_per_block; - if (blocks_to_destroy_per_partition == 0) { + // Batch destruction across input runs, capped by the number of output partitions + const auto batching_divisor = MinValue(num_runs, num_partitions); + if (total_tuples_to_destroy / batching_divisor < tuples_per_block) { return; } diff --git a/src/duckdb/src/common/string_util.cpp b/src/duckdb/src/common/string_util.cpp index 1b1ab0fef..c99579519 100644 --- a/src/duckdb/src/common/string_util.cpp +++ b/src/duckdb/src/common/string_util.cpp @@ -13,6 +13,7 @@ #include "utf8proc_wrapper.hpp" #include "duckdb/common/types/string_type.hpp" #include "duckdb/common/operator/cast_operators.hpp" +#include "duckdb/common/operator/multiply.hpp" #include #include @@ -408,6 +409,21 @@ string StringUtil::TryParseFormattedBytes(const string &arg, idx_t &result) { unit); } + // integer values are multiplied exactly - going through a double loses precision above 2^53 + bool is_integer = !number.empty() && number.find_first_not_of("0123456789") == string::npos; + if (is_integer) { + uint64_t integer_limit; + if (!TryCast::Operation(string_t(number), integer_limit)) { + return "Memory value out of range: value is too large"; + } + uint64_t integer_bytes; + if (!TryMultiplyOperator::Operation(integer_limit, static_cast(multiplier), integer_bytes)) { + return "Memory value out of range: value is too large"; + } + result = integer_bytes; + return string(); + } + // Make sure the result is not greater than `idx_t` max value constexpr double max_value = static_cast(NumericLimits::Maximum()); const double double_multiplier = static_cast(multiplier); diff --git a/src/duckdb/src/common/types/blob.cpp b/src/duckdb/src/common/types/blob.cpp index 40f647a4d..2ef1ea88d 100644 --- a/src/duckdb/src/common/types/blob.cpp +++ b/src/duckdb/src/common/types/blob.cpp @@ -250,6 +250,12 @@ idx_t Blob::FromBase64Size(string_t str) { template uint32_t DecodeBase64Bytes(const string_t &str, const_data_ptr_t input_data, idx_t base_idx) { int decoded_bytes[4]; + if (ALLOW_PADDING && input_data[base_idx + 2] == Blob::BASE64_PADDING && + input_data[base_idx + 3] != Blob::BASE64_PADDING) { + // padding must be at the end: data cannot follow the padding + throw ConversionException("Could not decode string \"%s\" as base64: data after the padding at position %d", + str.GetString(), base_idx + 3); + } for (idx_t decode_idx = 0; decode_idx < 4; decode_idx++) { if (ALLOW_PADDING && decode_idx >= 2 && input_data[base_idx + decode_idx] == Blob::BASE64_PADDING) { // the last two bytes of a base64 string can have padding: in this case we set the byte to 0 diff --git a/src/duckdb/src/common/types/data_chunk.cpp b/src/duckdb/src/common/types/data_chunk.cpp index 42bbf2bbd..2bb0d97ba 100644 --- a/src/duckdb/src/common/types/data_chunk.cpp +++ b/src/duckdb/src/common/types/data_chunk.cpp @@ -80,8 +80,8 @@ idx_t DataChunk::DeriveSize() const { return v.size(); } } - if (data.empty()) { - // a column-less chunk has nothing to derive a cardinality from; without an explicit count it is empty + if (data.empty() || !vector_caches.empty()) { + // a column-less chunk, or an initialized chunk whose columns are all unset, is empty without an explicit count return 0; } throw InternalException( @@ -336,7 +336,9 @@ void DataChunk::Serialize(Serializer &serializer, bool compressed_serialization) list.WriteObject([&](Serializer &object) { // Reference the vector to avoid potentially mutating it during serialization Vector serialized_vector(data[i].GetType()); - serialized_vector.Reference(data[i]); + if (data[i].GetBufferRef()) { + serialized_vector.Reference(data[i]); + } serialized_vector.Serialize(object, compressed_serialization); }); }); diff --git a/src/duckdb/src/common/types/row/tuple_data_collection.cpp b/src/duckdb/src/common/types/row/tuple_data_collection.cpp index 19a682334..a50a20c93 100644 --- a/src/duckdb/src/common/types/row/tuple_data_collection.cpp +++ b/src/duckdb/src/common/types/row/tuple_data_collection.cpp @@ -182,20 +182,25 @@ void TupleDataCollection::DestroyChunks(const idx_t chunk_idx_begin, const idx_t } if (!layout.AllConstant()) { - if (chunk_begin.heap_block_ids.Empty()) { + auto heap_chunk_begin = chunk_idx_begin; + while (heap_chunk_begin < chunk_idx_end && segment.chunks[heap_chunk_begin]->heap_block_ids.Empty()) { + heap_chunk_begin++; + } + if (heap_chunk_begin == chunk_idx_end) { return; } - const auto heap_block_begin = chunk_begin.heap_block_ids.Start(); - if (chunk_idx_end == ChunkCount()) { - segment.allocator->DestroyHeapBlocks(heap_block_begin, segment.allocator->HeapBlockCount()); - } else { - auto &chunk_end = *segment.chunks[chunk_idx_end]; - if (chunk_end.heap_block_ids.Empty()) { - return; - } - const auto heap_block_end = chunk_end.heap_block_ids.Start(); - segment.allocator->DestroyHeapBlocks(heap_block_begin, heap_block_end); + + // A later chunk can share a heap block across intervening chunks without heap data. + const auto chunk_count = segment.ChunkCount(); + auto heap_chunk_end = chunk_idx_end; + while (heap_chunk_end < chunk_count && segment.chunks[heap_chunk_end]->heap_block_ids.Empty()) { + heap_chunk_end++; } + const auto heap_block_begin = segment.chunks[heap_chunk_begin]->heap_block_ids.Start(); + const auto heap_block_end = heap_chunk_end == chunk_count + ? segment.allocator->HeapBlockCount() + : segment.chunks[heap_chunk_end]->heap_block_ids.Start(); + segment.allocator->DestroyHeapBlocks(heap_block_begin, heap_block_end); } } diff --git a/src/duckdb/src/common/types/vector.cpp b/src/duckdb/src/common/types/vector.cpp index ff8f572e7..95e0a4a55 100644 --- a/src/duckdb/src/common/types/vector.cpp +++ b/src/duckdb/src/common/types/vector.cpp @@ -313,6 +313,10 @@ void Vector::AddHeapReference(const Vector &other) { } void Vector::Reserve(idx_t to_reserve_p) { + if (GetVectorType() == VectorType::CONSTANT_VECTOR) { + // a constant holds one physical entry regardless of its logical size + return; + } auto reserve_size = VectorBuffer::GetReserveSize(to_reserve_p); if (!buffer) { Initialize(VectorDataInitialization::UNINITIALIZED, reserve_size); @@ -405,6 +409,9 @@ idx_t Vector::GetDataSize() const { } idx_t Vector::GetDataSize(idx_t count) const { + if (!buffer) { + return 0; + } return Buffer().GetDataSize(type, count); } diff --git a/src/duckdb/src/common/types/vector_cache.cpp b/src/duckdb/src/common/types/vector_cache.cpp index dff602471..c2ecd1b15 100644 --- a/src/duckdb/src/common/types/vector_cache.cpp +++ b/src/duckdb/src/common/types/vector_cache.cpp @@ -129,6 +129,8 @@ VectorCache &VectorCache::operator=(VectorCache &&other) noexcept { void VectorCache::ResetFromCache(Vector &result) const { if (!cache_entry) { + // uninitialized column: drop the last reference so it does not keep a stale size + result.SetBuffer(nullptr); return; } cache_entry->ResetFromCache(result); diff --git a/src/duckdb/src/common/vector/flat_vector.cpp b/src/duckdb/src/common/vector/flat_vector.cpp index 5e09f370b..ae4c847fa 100644 --- a/src/duckdb/src/common/vector/flat_vector.cpp +++ b/src/duckdb/src/common/vector/flat_vector.cpp @@ -25,6 +25,8 @@ StandardVectorBuffer::StandardVectorBuffer(capacity_t capacity, idx_t type_size_ StandardVectorBuffer::StandardVectorBuffer(data_ptr_t data_ptr_p, count_t count, idx_t type_size_p) : VectorBuffer(VectorType::FLAT_VECTOR, VectorBufferType::STANDARD_BUFFER, count), data_ptr(data_ptr_p), type_size(type_size_p), capacity(count) { + // the validity capacity must cover all entries, even while no mask is allocated (all-valid data) + validity.Resize(capacity); } StandardVectorBuffer::StandardVectorBuffer(AllocatedData &&data_p, count_t count, idx_t type_size_p) : VectorBuffer(VectorType::FLAT_VECTOR, VectorBufferType::STANDARD_BUFFER, count), data_ptr(data_p.get()), @@ -32,6 +34,8 @@ StandardVectorBuffer::StandardVectorBuffer(AllocatedData &&data_p, count_t count if (count > capacity) { throw InternalException("Count is out of range for capacity"); } + // the validity capacity must cover all entries, even while no mask is allocated (all-valid data) + validity.Resize(capacity); } void StandardVectorBuffer::SetVectorType(VectorType new_vector_type) { @@ -101,9 +105,11 @@ buffer_ptr StandardVectorBuffer::SliceInternal(const LogicalType & } buffer_ptr StandardVectorBuffer::ConstantSliceInternal(const LogicalType &type, count_t count) { - auto result = make_buffer(data_ptr, count, type_size); + // a constant has one physical entry - the logical count is tracked separately from the physical capacity + auto result = make_buffer(data_ptr, count_t(1ULL), type_size); result->GetValidityMask().Set(0, validity.RowIsValid(0)); result->SetVectorType(VectorType::CONSTANT_VECTOR); + result->SetVectorSize(count); result->AddAuxiliaryData(make_uniq(shared_from_this())); return result; } @@ -207,7 +213,6 @@ buffer_ptr StandardVectorBuffer::FlattenSliceInternal(const Logica auto result = CreateBuffer(std::move(new_data), count_t(count)); // copy validity using sel auto &result_validity = result->GetValidityMask(); - result_validity.Resize(allocated_count); result_validity.CopySel(validity, sel, 0, 0, count); return result; } diff --git a/src/duckdb/src/common/vector/list_vector.cpp b/src/duckdb/src/common/vector/list_vector.cpp index 124911264..592c635ba 100644 --- a/src/duckdb/src/common/vector/list_vector.cpp +++ b/src/duckdb/src/common/vector/list_vector.cpp @@ -144,9 +144,11 @@ buffer_ptr VectorListBuffer::SliceInternal(const LogicalType &type } buffer_ptr VectorListBuffer::ConstantSliceInternal(const LogicalType &type, count_t count) { - auto result = make_buffer(data_ptr, count, *child); + // a constant has one physical entry - the logical count is tracked separately from the physical capacity + auto result = make_buffer(data_ptr, count_t(1ULL), *child); result->GetValidityMask().Set(0, validity.RowIsValid(0)); result->SetVectorType(VectorType::CONSTANT_VECTOR); + result->SetVectorSize(count); result->AddAuxiliaryData(make_uniq(shared_from_this())); return result; } diff --git a/src/duckdb/src/common/vector/string_vector.cpp b/src/duckdb/src/common/vector/string_vector.cpp index 83dff6e97..c4395dc63 100644 --- a/src/duckdb/src/common/vector/string_vector.cpp +++ b/src/duckdb/src/common/vector/string_vector.cpp @@ -147,9 +147,11 @@ buffer_ptr VectorStringBuffer::SliceInternal(const LogicalType &ty } buffer_ptr VectorStringBuffer::ConstantSliceInternal(const LogicalType &type, count_t count) { - auto result = make_buffer(data_ptr, count); + // a constant has one physical entry - the logical count is tracked separately from the physical capacity + auto result = make_buffer(data_ptr, count_t(1ULL)); result->GetValidityMask().Set(0, validity.RowIsValid(0)); result->SetVectorType(VectorType::CONSTANT_VECTOR); + result->SetVectorSize(count); result->AddAuxiliaryData(make_uniq(shared_from_this())); return result; } diff --git a/src/duckdb/src/execution/aggregate_hashtable.cpp b/src/duckdb/src/execution/aggregate_hashtable.cpp index 8210371f7..ce46d65ff 100644 --- a/src/duckdb/src/execution/aggregate_hashtable.cpp +++ b/src/duckdb/src/execution/aggregate_hashtable.cpp @@ -344,7 +344,7 @@ void GroupedAggregateHashTable::Resize(idx_t size) { return; } - auto new_hash_map = buffer_manager.GetBufferAllocator().TryAllocateHuge(size * sizeof(ht_entry_t)); + auto new_hash_map = buffer_manager.GetBufferAllocator().AllocateHuge(size * sizeof(ht_entry_t)); capacity = size; hash_map = std::move(new_hash_map); entries = reinterpret_cast(hash_map.get()); diff --git a/src/duckdb/src/execution/index/fixed_size_allocator.cpp b/src/duckdb/src/execution/index/fixed_size_allocator.cpp index 04ec50e21..a1f9af99d 100644 --- a/src/duckdb/src/execution/index/fixed_size_allocator.cpp +++ b/src/duckdb/src/execution/index/fixed_size_allocator.cpp @@ -20,13 +20,18 @@ FixedSizeAllocator::FixedSizeAllocator(const idx_t segment_size, BlockManager &b // calculate how many segments fit into one buffer (available_segments_per_buffer) - idx_t bits_per_value = sizeof(validity_t) * 8; - idx_t byte_count = 0; + const idx_t block_size = block_manager.GetBlockSize(); + const idx_t bits_per_value = sizeof(validity_t) * 8; - bitmask_count = 0; - available_segments_per_buffer = 0; + // each full validity_t value is followed by bits_per_value segments: skip all of them at once + const idx_t full_value_bytes = sizeof(validity_t) + bits_per_value * segment_size; + const idx_t full_value_count = block_size / full_value_bytes; + idx_t byte_count = full_value_count * full_value_bytes; - while (byte_count < block_manager.GetBlockSize()) { + bitmask_count = full_value_count; + available_segments_per_buffer = full_value_count * bits_per_value; + + while (byte_count < block_size) { if (!bitmask_count || (bitmask_count * bits_per_value) % available_segments_per_buffer == 0) { // we need to add another validity_t value to the bitmask, to allow storing another // bits_per_value segments on a buffer @@ -34,7 +39,7 @@ FixedSizeAllocator::FixedSizeAllocator(const idx_t segment_size, BlockManager &b byte_count += sizeof(validity_t); } - auto remaining_bytes = block_manager.GetBlockSize() - byte_count; + auto remaining_bytes = block_size - byte_count; auto remaining_segments = MinValue(remaining_bytes / segment_size, bits_per_value); if (remaining_segments == 0) { diff --git a/src/duckdb/src/execution/join_hashtable.cpp b/src/duckdb/src/execution/join_hashtable.cpp index 493e0f084..618d9ac9a 100644 --- a/src/duckdb/src/execution/join_hashtable.cpp +++ b/src/duckdb/src/execution/join_hashtable.cpp @@ -1083,14 +1083,14 @@ void JoinHashTable::AllocatePointerTable() { auto current_capacity = hash_map.GetSize() / sizeof(ht_entry_t); if (capacity > current_capacity) { // Need more space - hash_map = buffer_manager.GetBufferAllocator().TryAllocateHuge(capacity * sizeof(ht_entry_t)); + hash_map = buffer_manager.GetBufferAllocator().AllocateHuge(capacity * sizeof(ht_entry_t)); } else { // Just use the current hash map capacity = current_capacity; } } else { // Allocate a hash map - hash_map = buffer_manager.GetBufferAllocator().TryAllocateHuge(capacity * sizeof(ht_entry_t)); + hash_map = buffer_manager.GetBufferAllocator().AllocateHuge(capacity * sizeof(ht_entry_t)); } D_ASSERT(hash_map.GetSize() == capacity * sizeof(ht_entry_t)); diff --git a/src/duckdb/src/execution/operator/aggregate/physical_ungrouped_aggregate.cpp b/src/duckdb/src/execution/operator/aggregate/physical_ungrouped_aggregate.cpp index 9a6619476..414e3f8a4 100644 --- a/src/duckdb/src/execution/operator/aggregate/physical_ungrouped_aggregate.cpp +++ b/src/duckdb/src/execution/operator/aggregate/physical_ungrouped_aggregate.cpp @@ -688,7 +688,7 @@ void VerifyNullHandling(DataChunk &chunk, UngroupedAggregateState &state, chunk.data[aggr_idx].ToUnifiedFormat(vdata); if (vdata.validity.RowIsValid(vdata.sel->get_index(0))) { throw InternalException( - "VerifyNullHandling failed for aggregate function \"%s\": no rows were aggregated but the result " + "VerifyNullHandling failed for aggregate function %s: no rows were aggregated but the result " "is not NULL - aggregates with default NULL handling should return NULL when no rows are " "aggregated", aggr.Function().GetName()); diff --git a/src/duckdb/src/execution/operator/join/physical_hash_join.cpp b/src/duckdb/src/execution/operator/join/physical_hash_join.cpp index 76961cf4e..b4ef7b074 100644 --- a/src/duckdb/src/execution/operator/join/physical_hash_join.cpp +++ b/src/duckdb/src/execution/operator/join/physical_hash_join.cpp @@ -2248,6 +2248,19 @@ unique_ptr PhysicalHashJoin::GetOperatorState(ExecutionContext &c return std::move(state); } +void PhysicalHashJoin::InitializeOutputChunk(Allocator &allocator, DataChunk &chunk) const { + if (join_type == JoinType::RIGHT_SEMI || join_type == JoinType::RIGHT_ANTI) { + PhysicalOperator::InitializeOutputChunk(allocator, chunk); + return; + } + // the probe-side output columns are always referenced or sliced from the probe input + vector initialize(types.size(), true); + for (idx_t i = 0; i < lhs_output_columns.col_idxs.size(); i++) { + initialize[i] = false; + } + chunk.Initialize(allocator, types, initialize); +} + OperatorResultType PhysicalHashJoin::ExecuteInternal(ExecutionContext &context, DataChunk &input, DataChunk &chunk, GlobalOperatorState &gstate, OperatorState &state_p) const { auto &state = state_p.Cast(); diff --git a/src/duckdb/src/function/aggregate/distributive/decimal_avg.cpp b/src/duckdb/src/function/aggregate/distributive/decimal_avg.cpp index 6d11078c8..5946acd33 100644 --- a/src/duckdb/src/function/aggregate/distributive/decimal_avg.cpp +++ b/src/duckdb/src/function/aggregate/distributive/decimal_avg.cpp @@ -121,7 +121,8 @@ static unique_ptr BindDecimalAverage(BindAggregateFunctionInput &i auto &input_type = arguments[0]->GetReturnType(); if (input_type.id() == LogicalTypeId::UNKNOWN) { - return nullptr; + // unresolved prepared parameter - defer binding until the type is known + throw ParameterNotResolvedException(); } if (input_type.id() != LogicalTypeId::DECIMAL) { throw InvalidInputException("decimal_average requires a DECIMAL argument, got %s — " diff --git a/src/duckdb/src/function/aggregate/distributive/minmax.cpp b/src/duckdb/src/function/aggregate/distributive/minmax.cpp index eaca13d6e..fb6ba80b7 100644 --- a/src/duckdb/src/function/aggregate/distributive/minmax.cpp +++ b/src/duckdb/src/function/aggregate/distributive/minmax.cpp @@ -342,7 +342,7 @@ shared_ptr GetCollatedMinMaxFunction(ClientContext &con error_context); if (!func) { throw NotImplementedException( - "Failure while binding function \"%s\" using collations - arg_min/arg_max do not exist in the " + "Failure while binding function %s using collations - arg_min/arg_max do not exist in the " "catalog - load the core_functions module to fix this issue", name); } diff --git a/src/duckdb/src/function/aggregate/sorted_aggregate_function.cpp b/src/duckdb/src/function/aggregate/sorted_aggregate_function.cpp index 318702a0b..3ed32c5f7 100644 --- a/src/duckdb/src/function/aggregate/sorted_aggregate_function.cpp +++ b/src/duckdb/src/function/aggregate/sorted_aggregate_function.cpp @@ -585,13 +585,13 @@ FunctionBinder::BindSortedAggregateState(ClientContext &context, const BoundAggr auto &buffer_columns = StructType::GetChildTypes(buffer_struct); auto &inner_arguments = inner_function.GetArguments(); if (argument_count != inner_arguments.size() || argument_count > buffer_columns.size()) { - throw BinderException("Aggregate state for \"%s\" has %llu state columns, expected at least %llu", + throw BinderException("Aggregate state for %s has %llu state columns, expected at least %llu", inner_function.GetName(), (uint64_t)buffer_columns.size(), (uint64_t)inner_arguments.size()); } for (idx_t i = 0; i < argument_count; i++) { if (inner_arguments[i].IsComplete() && buffer_columns[i].second != inner_arguments[i]) { - throw BinderException("Aggregate state for \"%s\" has state column %llu of type %s, expected %s", + throw BinderException("Aggregate state for %s has state column %llu of type %s, expected %s", inner_function.GetName(), (uint64_t)i, buffer_columns[i].second.ToString(), inner_arguments[i].ToString()); } @@ -636,7 +636,7 @@ void FunctionBinder::BindSortedAggregate(ClientContext &context, BoundAggregateE auto &logical_fields = StructType::GetChildTypes(export_layout.first); ExportAggregateFunction::GetSortedStateOrders(expr.GetReturnType(), export_layout.second); if (export_layout.second.size() != order_bys.orders.size() || children.size() > logical_fields.size()) { - throw InternalException("Exported state layout of ordered aggregate \"%s\" does not match its ORDER BY", + throw InternalException("Exported state layout of ordered aggregate %s does not match its ORDER BY", bound_function.GetName()); } // the statistics optimizer may have narrowed the buffered column types since the exported type was fixed at diff --git a/src/duckdb/src/function/cast/struct_cast.cpp b/src/duckdb/src/function/cast/struct_cast.cpp index 49a878ca5..f4c487712 100644 --- a/src/duckdb/src/function/cast/struct_cast.cpp +++ b/src/duckdb/src/function/cast/struct_cast.cpp @@ -32,7 +32,7 @@ unique_ptr StructBoundCastData::BindStructToStructCast(BindCastIn for (idx_t i = 0; i < target_children.size(); i++) { auto &name = target_children[i].first; if (target_children_map.find(name) != target_children_map.end()) { - throw NotImplementedException("Error while casting - duplicate name \"%s\" in struct", name); + throw NotImplementedException("Error while casting - duplicate name %s in struct", name); } target_children_map[name] = i; } diff --git a/src/duckdb/src/function/cast/union_casts.cpp b/src/duckdb/src/function/cast/union_casts.cpp index b1ad5230f..c81bab76b 100644 --- a/src/duckdb/src/function/cast/union_casts.cpp +++ b/src/duckdb/src/function/cast/union_casts.cpp @@ -169,7 +169,7 @@ static unique_ptr BindUnionToUnionCast(BindCastInput &input, cons }; // no matching member tag found in the target set auto message = - StringUtil::Format("Type %s can't be cast as %s. The member '%s' is not present in target union", + StringUtil::Format("Type %s can't be cast as %s. The member %s is not present in target union", source.ToString(), target.ToString(), source_member_name); throw ConversionException(message); } diff --git a/src/duckdb/src/function/cast/vector_cast_helpers.cpp b/src/duckdb/src/function/cast/vector_cast_helpers.cpp index 579b6db8d..777a39956 100644 --- a/src/duckdb/src/function/cast/vector_cast_helpers.cpp +++ b/src/duckdb/src/function/cast/vector_cast_helpers.cpp @@ -478,6 +478,7 @@ bool VectorStringToStruct::SplitStruct(const string_t &input, vector &va if (end_char == '}') { //! Regular struct, in the form of `{name: value, name_2: value_2, ...}` + vector seen_children(varchar_vectors.size(), false); while (pos < len) { optional_idx start_pos; idx_t end_pos; @@ -531,6 +532,10 @@ bool VectorStringToStruct::SplitStruct(const string_t &input, vector &va return false; // false key } child_idx = it->second; + if (seen_children[child_idx]) { + return false; // duplicate key + } + seen_children[child_idx] = true; start_pos = optional_idx(); pos++; diff --git a/src/duckdb/src/function/cast_rules.cpp b/src/duckdb/src/function/cast_rules.cpp index 9eaea5de2..f907d99f3 100644 --- a/src/duckdb/src/function/cast_rules.cpp +++ b/src/duckdb/src/function/cast_rules.cpp @@ -179,7 +179,6 @@ static int64_t ImplicitCastUBigint(const LogicalType &to) { static int64_t ImplicitCastFloat(const LogicalType &to) { switch (to.id()) { - case LogicalTypeId::BIGNUM: case LogicalTypeId::DOUBLE: return TargetTypeCost(to); default: diff --git a/src/duckdb/src/function/combine_types_rules.cpp b/src/duckdb/src/function/combine_types_rules.cpp index a5461de9e..50f2d987f 100644 --- a/src/duckdb/src/function/combine_types_rules.cpp +++ b/src/duckdb/src/function/combine_types_rules.cpp @@ -10,6 +10,10 @@ namespace duckdb { // =========================================== static bool CombineNoAliasTypes(LogicalTypeResolver &logical_type_resolver, const LogicalType &left, const LogicalType &right, LogicalType &result) { + // a plain value cannot implicitly become an aggregate state + if (left.IsAggregateState() != right.IsAggregateState()) { + return false; + } // we always prefer aliased types if (!left.GetAlias().empty()) { result = left; @@ -347,6 +351,13 @@ static bool CombineStringLiteral(LogicalTypeResolver &, const LogicalType &left, return true; } +// BIGNUM has no implicit cast to or from FLOAT and DECIMAL; like their arithmetic, these pairs meet in DOUBLE +static bool CombineBignumFloatOrDecimal(LogicalTypeResolver &, const LogicalType &, const LogicalType &, + LogicalType &result) { + result = LogicalType::DOUBLE; + return true; +} + static bool UnequalMatchesVariant(const LogicalType &left, const LogicalType &right) { if (left.id() == right.id()) { // rule only applies to unequal types @@ -374,6 +385,13 @@ static bool UnequalMatchesEnum(const LogicalType &left, const LogicalType &right return left.id() == LogicalTypeId::ENUM || right.id() == LogicalTypeId::ENUM; } +static bool UnequalMatchesBignumFloatOrDecimal(const LogicalType &left, const LogicalType &right) { + auto is_pair = [](const LogicalType &a, const LogicalType &b) { + return a.id() == LogicalTypeId::BIGNUM && (b.id() == LogicalTypeId::FLOAT || b.id() == LogicalTypeId::DECIMAL); + }; + return is_pair(left, right) || is_pair(right, left); +} + static bool UnequalMatchesStringLiteral(const LogicalType &left, const LogicalType &right) { if (left.id() == right.id()) { // rule only applies to unequal types @@ -391,6 +409,7 @@ const vector &DefaultCombineTypesRules() { {UnequalMatchesEnum, CombineEnum}, {UnequalMatchesStruct, CombineStructTypes}, {UnequalMatchesStringLiteral, CombineStringLiteral}, + {UnequalMatchesBignumFloatOrDecimal, CombineBignumFloatOrDecimal}, {EqualMatchesStringLiteral, CombineEqualStringLiteral}, {EqualMatchesIntegerLiteral, CombineEqualIntegerLiteral}, {EqualMatchesEnum, CombineEqualEnum}, diff --git a/src/duckdb/src/function/function.cpp b/src/duckdb/src/function/function.cpp index 9d059ea8e..c17826b78 100644 --- a/src/duckdb/src/function/function.cpp +++ b/src/duckdb/src/function/function.cpp @@ -326,7 +326,7 @@ void FunctionSignature::Verify() const { found_default_value = true; } else if (found_default_value) { throw InvalidInputException( - "Parameters with default values must be at the end of the parameter list. Parameter '%s' does not " + "Parameters with default values must be at the end of the parameter list. Parameter %s does not " "have a default value but follows a parameter with a default value.", param.GetName()); } @@ -387,7 +387,7 @@ void FunctionSignature::Verify() const { // a named argument binds to a parameter of its name first, so an option of the same name is never reached for (auto ¶m : parameters) { if (param.AcceptsName() && typed_kwargs->Find(param.GetName())) { - throw InvalidInputException("Option '%s' has the same name as a parameter", param.GetName()); + throw InvalidInputException("Option %s has the same name as a parameter", param.GetName()); } } } @@ -647,7 +647,7 @@ Value BindFunctionInput::GetConstant(idx_t arg_idx, bool accept_null) const { } auto value = ExpressionExecutor::EvaluateScalar(context, expr); if (!accept_null && value.IsNull()) { - throw BinderException(expr, "%s in function '%s' must not be NULL", argument_name, function.GetName()); + throw BinderException(expr, "%s in function %s must not be NULL", argument_name, function.GetName()); } return value; } @@ -688,7 +688,7 @@ optional BindFunctionInput::TryGetConstant(const Identifier &name) const optional_idx BindFunctionInput::GetArgumentIndex(const Identifier &name) const { if (!argument_names) { - throw InternalException("Function '%s' was bound without argument names, cannot look up argument '%s' by name", + throw InternalException("Function %s was bound without argument names, cannot look up argument %s by name", function.GetName(), name); } // The binder resolves every argument to a slot and reports its name: the signature parameter name for standard diff --git a/src/duckdb/src/function/function_binder.cpp b/src/duckdb/src/function/function_binder.cpp index 2746e686b..9f72bc50b 100644 --- a/src/duckdb/src/function/function_binder.cpp +++ b/src/duckdb/src/function/function_binder.cpp @@ -797,7 +797,7 @@ void FunctionBinder::CastToFunctionArguments(BoundSimpleFunction &function, vect continue; } if (IsIncompleteCastTarget(target_type)) { - throw InternalException("Function '%s' has incomplete argument type %s - concrete argument types must " + throw InternalException("Function %s has incomplete argument type %s - concrete argument types must " "be resolved in the resolve_types callback", function.GetName(), target_type); } @@ -1334,7 +1334,7 @@ static void VerifyTemplateType(const LogicalType &type, const Identifier &functi TypeVisitor::Contains(type, [&](const LogicalType &type) { if (type.id() == LogicalTypeId::TEMPLATE) { const auto msg = - "Function '%s' has a template parameter type '%s' that could not be resolved to a concrete type"; + "Function %s has a template parameter type '%s' that could not be resolved to a concrete type"; throw BinderException(msg, function_name, TemplateType::GetName(type)); } return false; // continue visiting @@ -1377,13 +1377,13 @@ static vector ResolveArguments(const SimpleFunction &function, Bound if (name.empty()) { // Somehow this was not a named argument, throw an error. throw BinderException(location, - "Positional arguments cannot follow named arguments in a function call to '%s'", + "Positional arguments cannot follow named arguments in a function call to %s", function.GetName()); } if (seen_names.count(name)) { // This should also not really happen when invoked through SQL - throw BinderException(location, "Duplicate named argument %s in function call to '%s'", name, + throw BinderException(location, "Duplicate named argument %s in function call to %s", name, function.GetName()); } seen_names.insert(name); @@ -1391,8 +1391,8 @@ static vector ResolveArguments(const SimpleFunction &function, Bound const auto opt_param_idx = sig.GetParameterIndexByName(name); if (!opt_param_idx.IsValid()) { if (!kwargs_param) { - throw BinderException(location, "Function '%s' does not have a parameter named '%s'", - function.GetName(), name); + throw BinderException(location, "Function %s does not have a parameter named %s", function.GetName(), + name); } kwargs.emplace_back(name, std::move(arg)); continue; @@ -1401,8 +1401,8 @@ static vector ResolveArguments(const SimpleFunction &function, Bound const auto param_idx = opt_param_idx.GetIndex(); if (sig.GetParameter(param_idx).AcceptsPosition() && param_idx < passed_count) { throw BinderException(location, - "Named argument '%s' cannot be used for parameter '%s' because it has already " - "been provided as a positional argument in function call to '%s'", + "Named argument '%s' cannot be used for parameter %s because it has already " + "been provided as a positional argument in function call to %s", arg->ToString(), name, function.GetName()); } keyword_arguments[param_idx] = std::move(arg); @@ -1474,7 +1474,7 @@ static vector ResolveArguments(const SimpleFunction &function, Bound // The names of the named arguments have to stay parallel to the last arguments static void VerifyArgumentCount(const BoundSimpleFunction &bound_function, idx_t resolved_count, idx_t argument_count) { if (resolved_count != argument_count && !bound_function.GetNamedArguments().empty()) { - throw InternalException("Function '%s' cannot add or remove arguments in its bind callback when it is " + throw InternalException("Function %s cannot add or remove arguments in its bind callback when it is " "called with keyword-only or '**kwargs' arguments", bound_function.GetName()); } @@ -1485,7 +1485,7 @@ static void VerifyBindArgumentTypes(const BoundScalarFunction &bound_function, const vector> &arguments) { auto &argument_types = bound_function.GetArguments(); if (arguments.size() > argument_types.size()) { - throw InternalException("Function '%s' added arguments in its bind callback - argument types must be " + throw InternalException("Function %s added arguments in its bind callback - argument types must be " "resolved in the resolve_types callback", bound_function.GetName()); } @@ -1500,7 +1500,7 @@ static void VerifyBindArgumentTypes(const BoundScalarFunction &bound_function, continue; } if (RequiresCast(source_type, target_type) == LogicalTypeComparisonResult::DIFFERENT_TYPES) { - throw InternalException("Function '%s' changed the type of argument %llu from %s to %s in its bind " + throw InternalException("Function %s changed the type of argument %llu from %s to %s in its bind " "callback - argument types must be resolved in the resolve_types callback", bound_function.GetName(), i + 1, source_type, target_type); } diff --git a/src/duckdb/src/function/macro_function.cpp b/src/duckdb/src/function/macro_function.cpp index 2ac728e79..0a4797935 100644 --- a/src/duckdb/src/function/macro_function.cpp +++ b/src/duckdb/src/function/macro_function.cpp @@ -80,7 +80,7 @@ MacroBindResult MacroFunction::BindMacroFunction( if (!arg.GetExpression().GetAlias().empty()) { // Default argument if (named_arguments.find(arg.GetExpression().GetAlias()) != named_arguments.end()) { - return MacroBindResult(StringUtil::Format("Macro %s() has named argument repeated '%s'", name, + return MacroBindResult(StringUtil::Format("Macro %s() has named argument repeated %s", name, arg.GetExpression().GetAlias())); } named_arg_types.insert(arg.GetExpression().GetAlias(), std::move(arg_type)); diff --git a/src/duckdb/src/function/scalar/compressed_materialization/compress_geometry.cpp b/src/duckdb/src/function/scalar/compressed_materialization/compress_geometry.cpp index ca258058e..827f7e287 100644 --- a/src/duckdb/src/function/scalar/compressed_materialization/compress_geometry.cpp +++ b/src/duckdb/src/function/scalar/compressed_materialization/compress_geometry.cpp @@ -16,7 +16,13 @@ constexpr auto POINT_XY_COORD_SIZE = sizeof(double) + sizeof(double); constexpr auto POINT_XY_BLOB_SIZE = POINT_XY_HEADER_SIZE + POINT_XY_COORD_SIZE; uhugeint_t GeometryPointCompress(const string_t &input) { - D_ASSERT(input.GetSize() == POINT_XY_BLOB_SIZE); + // The statistics that lead to this compression can come from a file, so verify that this is a POINT-XY + const auto data = const_data_ptr_cast(input.GetData()); + if (input.GetSize() != POINT_XY_BLOB_SIZE || Load(data) != 1 || + Load(data + sizeof(uint8_t)) != 1) { + throw InvalidInputException("Expected only 2D POINT geometries based on the column statistics, but " + "encountered a different geometry - the statistics might be corrupt"); + } // The 16 coordinate bytes (X, Y) follow the fixed 5-byte WKB header (byte order + type) const auto coords = const_data_ptr_cast(input.GetData()) + POINT_XY_HEADER_SIZE; @@ -33,7 +39,7 @@ uhugeint_t GeometryPointCompress(const string_t &input) { void GeometryPointCompressFunction(DataChunk &args, ExpressionState &state, Vector &result) { UnaryExecutor::Execute(args.data[0], result, GeometryPointCompress, - FunctionErrors::CANNOT_ERROR); + FunctionErrors::CAN_THROW_RUNTIME_ERROR); } void GeometryPointDecompress(const uhugeint_t &input, const data_ptr_t output) { @@ -111,7 +117,7 @@ ScalarFunction CMGeometryPointCompressFun::GetFunction() { result.GetSignature().GetParameter(0).SetName("value"); result.SetSerializeCallback(CMGeometryPointCompressSerialize); result.SetDeserializeCallback(CMGeometryPointCompressDeserialize); - result.SetErrorMode(FunctionErrors::CANNOT_ERROR); + result.SetErrorMode(FunctionErrors::CAN_THROW_RUNTIME_ERROR); return result; } diff --git a/src/duckdb/src/function/scalar/operator/arithmetic.cpp b/src/duckdb/src/function/scalar/operator/arithmetic.cpp index 763e832ee..97b5f1641 100644 --- a/src/duckdb/src/function/scalar/operator/arithmetic.cpp +++ b/src/duckdb/src/function/scalar/operator/arithmetic.cpp @@ -485,6 +485,22 @@ static void BignumSubtract(DataChunk &args, ExpressionState &state, Vector &resu }); } +//! The client context (if any) lets long BIGNUM multiplications and divisions check for interrupts +static optional_ptr GetBignumArithmeticContext(ExpressionState &state) { + return state.HasContext() ? &state.GetContext() : nullptr; +} + +static void BignumMultiply(DataChunk &args, ExpressionState &state, Vector &result) { + auto &heap = StringVector::GetStringHeap(result); + BignumArithmeticState arithmetic_state(GetBignumArithmeticContext(state)); + BinaryExecutor::Execute( + args.data[0], args.data[1], result, [&](bignum_t a, bignum_t b) { + const BignumIntermediate lhs(a); + const BignumIntermediate rhs(b); + return BignumIntermediate::Multiply(heap, arithmetic_state, lhs, rhs); + }); +} + static void BignumNegate(DataChunk &args, ExpressionState &state, Vector &result) { auto &heap = StringVector::GetStringHeap(result); UnaryExecutor::Execute(args.data[0], result, [&](bignum_t a) { @@ -1132,6 +1148,9 @@ ScalarFunctionSet OperatorMultiplyFun::GetFunctions() { multiply.AddFunction( ScalarFunction({LogicalType::INTERVAL, LogicalType::BIGINT}, LogicalType::INTERVAL, ScalarFunction::BinaryFunction)); + // we can multiply bignums together + multiply.AddFunction( + ScalarFunction({LogicalType::BIGNUM, LogicalType::BIGNUM}, LogicalType::BIGNUM, BignumMultiply)); multiply.SetFallible(); multiply.ApplyToFunctions(NameOperatorParameters); @@ -1184,7 +1203,12 @@ struct BinaryNumericDivideWrapper { template static inline RESULT_TYPE Operation(FUNC fun, LEFT_TYPE left, RIGHT_TYPE right, ValidityMask &mask, idx_t idx) { if (left == NumericLimits::Minimum() && right == -1) { - throw OutOfRangeException("Overflow in division of %d / %d", left, right); + if constexpr (std::is_same::value) { + // the remainder of MIN / -1 is 0 - only the division itself overflows + return RESULT_TYPE(0); + } else { + throw OutOfRangeException("Overflow in division of %d / %d", left, right); + } } else if (right == 0) { if (!NULL_ON_ZERO) { ThrowDivisionByZero(fun.get()); @@ -1226,7 +1250,12 @@ struct BinaryNumericDivideHugeintWrapper { template static inline RESULT_TYPE Operation(FUNC fun, LEFT_TYPE left, RIGHT_TYPE right, ValidityMask &mask, idx_t idx) { if (left == NumericLimits::Minimum() && right == -1) { - throw OutOfRangeException("Overflow in division of %s / %s", left.ToString(), right.ToString()); + if constexpr (std::is_same::value) { + // the remainder of MIN / -1 is 0 - only the division itself overflows + return RESULT_TYPE(0); + } else { + throw OutOfRangeException("Overflow in division of %s / %s", left.ToString(), right.ToString()); + } } else if (right == 0) { if (!NULL_ON_ZERO) { ThrowDivisionByZero(fun.get()); @@ -1377,6 +1406,41 @@ unique_ptr PropagateIntegerDivideStats(ClientContext &context, F } } // namespace + +//===--------------------------------------------------------------------===// +// BIGNUM division / modulo +//===--------------------------------------------------------------------===// +template +static void BignumDivideOperation(DataChunk &args, ExpressionState &state, Vector &result) { + auto &heap = StringVector::GetStringHeap(result); + BignumArithmeticState arithmetic_state(GetBignumArithmeticContext(state)); + BinaryExecutor::Execute( + args.data[0], args.data[1], result, [&](bignum_t a, bignum_t b) -> optional { + const BignumIntermediate lhs(a); + const BignumIntermediate rhs(b); + string_t quotient_or_remainder; + if (!BignumIntermediate::TryDivMod(heap, arithmetic_state, lhs, rhs, MODULO, quotient_or_remainder)) { + if (!NULL_ON_ZERO) { + ThrowDivisionByZero(state.expr); + } + return nullopt; + } + return quotient_or_remainder; + }); +} + +template +static unique_ptr BindBignumDivide(BindScalarFunctionInput &input) { + auto &bound_function = input.GetBoundFunction(); + auto null_on_zero = !Settings::Get(input.GetClientContext()); + if (null_on_zero) { + bound_function.SetFunctionCallback(BignumDivideOperation); + } else { + bound_function.SetFunctionCallback(BignumDivideOperation); + } + return nullptr; +} + ScalarFunctionSet OperatorFloatDivideFun::GetFunctions() { ScalarFunctionSet fp_divide("/"); fp_divide.AddFunction(ScalarFunction({LogicalType::FLOAT, LogicalType::FLOAT}, LogicalType::FLOAT, nullptr, @@ -1404,6 +1468,9 @@ ScalarFunctionSet OperatorIntegerDivideFun::GetFunctions() { full_divide.AddFunction(ScalarFunction({type, type}, type, nullptr, BindDivisionByZero)); } } + // we can divide bignums by bignums + full_divide.AddFunction(ScalarFunction({LogicalType::BIGNUM, LogicalType::BIGNUM}, LogicalType::BIGNUM, nullptr, + BindBignumDivide)); full_divide.SetFallible(); full_divide.ApplyToFunctions(NameOperatorParameters); return full_divide; @@ -1462,6 +1529,9 @@ ScalarFunctionSet OperatorModuloFun::GetFunctions() { modulo.AddFunction(ScalarFunction({type, type}, type, nullptr, BindDivisionByZero)); } } + // we can take the remainder of bignums divided by bignums + modulo.AddFunction(ScalarFunction({LogicalType::BIGNUM, LogicalType::BIGNUM}, LogicalType::BIGNUM, nullptr, + BindBignumDivide)); modulo.SetFallible(); modulo.ApplyToFunctions(NameOperatorParameters); diff --git a/src/duckdb/src/function/scalar/strftime_format.cpp b/src/duckdb/src/function/scalar/strftime_format.cpp index d5cbd2b8b..a3ea98c4b 100644 --- a/src/duckdb/src/function/scalar/strftime_format.cpp +++ b/src/duckdb/src/function/scalar/strftime_format.cpp @@ -1404,9 +1404,16 @@ bool StrpTimeFormat::Parse(const char *data, size_t size, ParseResult &result, b } case StrTimeSpecifier::DAY_OF_YEAR_PADDED: case StrTimeSpecifier::DAY_OF_YEAR_DECIMAL: { - auto yeardate = Date::FromDate(result_data[0], 1, 1); + auto year = result_data[0]; + auto yeardate = Date::FromDate(year, 1, 1); yeardate += UnsafeNumericCast(yearday - 1); Date::Convert(yeardate, result_data[0], result_data[1], result_data[2]); + if (result_data[0] != year) { + // day 366 of a year that is not a leap year + error_message = "Year day out of range for the year"; + error_position = 0; + return false; + } break; } case StrTimeSpecifier::DAY_OF_MONTH_PADDED: @@ -1527,6 +1534,22 @@ bool StrpTimeFormat::ParseResult::TryToTimestamp(timestamp_t &result) { return Timestamp::TryFromDatetime(date, time, result); } +//! days * NANOS_PER_DAY + time_ns without overflowing on the first day of the range, where the final value is valid but +//! the number of nanoseconds in the days alone is not +static bool TryDaysAndTimeToNanos(int64_t days, int64_t time_ns, int64_t &result) { + int64_t day_nanos; + if (TryMultiplyOperator::Operation(days, Interval::NANOS_PER_DAY, day_nanos)) { + return TryAddOperator::Operation(day_nanos, time_ns, result); + } + if (days >= 0) { + return false; + } + if (!TryMultiplyOperator::Operation(days + 1, Interval::NANOS_PER_DAY, day_nanos)) { + return false; + } + return TryAddOperator::Operation(day_nanos, time_ns - Interval::NANOS_PER_DAY, result); +} + timestamp_ns_t StrpTimeFormat::ParseResult::ToTimestampNS() { timestamp_ns_t result; if (is_special) { @@ -1541,11 +1564,10 @@ timestamp_ns_t StrpTimeFormat::ParseResult::ToTimestampNS() { // Don't use rounded µs const auto date = ToDate(); const auto time = ToTimeNS(); - if (!TryMultiplyOperator::Operation(date.days, Interval::NANOS_PER_DAY, - result.value)) { + if (!TryDaysAndTimeToNanos(date.days, time, result.value)) { throw ConversionException("Date out of nanosecond range: %d-%d-%d", data[0], data[1], data[2]); } - if (!TryAddOperator::Operation(result.value, time, result.value)) { + if (!result.IsFinite()) { throw ConversionException("Overflow exception in date/time -> timestamp_ns conversion"); } } @@ -1561,10 +1583,7 @@ bool StrpTimeFormat::ParseResult::TryToTimestampNS(timestamp_ns_t &result) { // Don't use rounded µs const auto time = ToTimeNS(); - if (!TryMultiplyOperator::Operation(date.days, Interval::NANOS_PER_DAY, result.value)) { - return false; - } - if (!TryAddOperator::Operation(result.value, time, result.value)) { + if (!TryDaysAndTimeToNanos(date.days, time, result.value)) { return false; } return result.IsFinite(); diff --git a/src/duckdb/src/function/scalar/struct/struct_extract.cpp b/src/duckdb/src/function/scalar/struct/struct_extract.cpp index 85856ef25..670376017 100644 --- a/src/duckdb/src/function/scalar/struct/struct_extract.cpp +++ b/src/duckdb/src/function/scalar/struct/struct_extract.cpp @@ -71,7 +71,9 @@ static void StructExtractResolveTypes(ResolveScalarFunctionTypesInput &input) { if (child_type.id() == LogicalTypeId::UNKNOWN) { throw ParameterNotResolvedException(); } - D_ASSERT(StructType::IsStruct(child_type)); + if (!StructType::IsStruct(child_type)) { + throw BinderException("struct_extract can only be used on a STRUCT, not on %s", child_type.ToString()); + } if (StructType::GetChildTypes(child_type).empty()) { throw BinderException("Can't extract something from an empty struct"); } diff --git a/src/duckdb/src/function/scalar/system/aggregate_export.cpp b/src/duckdb/src/function/scalar/system/aggregate_export.cpp index d43e1de71..cfb6bec31 100644 --- a/src/duckdb/src/function/scalar/system/aggregate_export.cpp +++ b/src/duckdb/src/function/scalar/system/aggregate_export.cpp @@ -945,13 +945,13 @@ ExportAggregateFunction::Bind(unique_ptr child_aggrega throw BinderException("Cannot use EXPORT_STATE for non-combinable function %s", bound_function.GetName()); } if (bound_function.HasExportAggregateStateCallback() != bound_function.HasImportAggregateStateCallback()) { - throw InternalException("Aggregate function \"%s\" must define either both or neither of the " + throw InternalException("Aggregate function %s must define either both or neither of the " "export_aggregate_state/import_aggregate_state callbacks", bound_function.GetName()); } if (!bound_function.HasGetStateTypeCallback()) { throw NotImplementedException( - "Aggregate function \"%s\" does not have a state type callback defined - cannot export state", + "Aggregate function %s does not have a state type callback defined - cannot export state", bound_function.GetName()); } D_ASSERT(bound_function.HasStateSizeCallback()); diff --git a/src/duckdb/src/function/scalar/system/write_log.cpp b/src/duckdb/src/function/scalar/system/write_log.cpp index d614b1782..ddb257897 100644 --- a/src/duckdb/src/function/scalar/system/write_log.cpp +++ b/src/duckdb/src/function/scalar/system/write_log.cpp @@ -98,7 +98,7 @@ unique_ptr WriteLogBind(BindScalarFunctionInput &input) { result->output_col = i; bound_function.SetReturnType(result->return_type); } else { - throw BinderException(StringUtil::Format("write_log: Unknown argument '%s'", names[i])); + throw BinderException(StringUtil::Format("write_log: Unknown argument %s", names[i])); } } diff --git a/src/duckdb/src/function/scalar/variant/variant_bind_utils.cpp b/src/duckdb/src/function/scalar/variant/variant_bind_utils.cpp index 8d98bb0bd..271277237 100644 --- a/src/duckdb/src/function/scalar/variant/variant_bind_utils.cpp +++ b/src/duckdb/src/function/scalar/variant/variant_bind_utils.cpp @@ -97,7 +97,7 @@ unique_ptr VariantBindUtils::VariantPathBind(BindScalarFunctionInp return make_uniq(CollectPaths(constant_arg, function_name.GetIdentifierName())); } - throw BinderException("'%s' received an unexpected type for the second argument", function_name); + throw BinderException("%s received an unexpected type for the second argument", function_name); } } // namespace duckdb diff --git a/src/duckdb/src/function/scalar_function.cpp b/src/duckdb/src/function/scalar_function.cpp index b019d3cf9..f584ff9de 100644 --- a/src/duckdb/src/function/scalar_function.cpp +++ b/src/duckdb/src/function/scalar_function.cpp @@ -19,7 +19,7 @@ void ThrowNonFallibleFunctionError(const Identifier &name, std::exception &ex) { if (!Exception::IsExecutionError(error.Type())) { throw; } - throw InternalException("Scalar function \"%s\" threw an execution error, but the function is not marked as " + throw InternalException("Scalar function %s threw an execution error, but the function is not marked as " "fallible - the function must call SetFallible(). Error: %s", name, error.RawMessage()); } diff --git a/src/duckdb/src/function/table/checkpoint.cpp b/src/duckdb/src/function/table/checkpoint.cpp index e676af7e6..9031b6f2b 100644 --- a/src/duckdb/src/function/table/checkpoint.cpp +++ b/src/duckdb/src/function/table/checkpoint.cpp @@ -45,11 +45,11 @@ static unique_ptr CheckpointBind(ClientContext &context, TableFunc auto default_database = DatabaseManager::GetDefaultDatabase(context); db = db_manager.GetDatabase(context, default_database); if (!db) { - throw BinderException("Catalog \"%s\" does not exist!", default_database); + throw BinderException("Catalog %s does not exist!", default_database); } } if (db->IsSystem() || db->IsTemporary()) { - throw BinderException("Cannot checkpoint database \"%s\": it does not have storage", db->GetName()); + throw BinderException("Cannot checkpoint database %s: it does not have storage", db->GetName()); } return make_uniq(db); } diff --git a/src/duckdb/src/function/table/query_function.cpp b/src/duckdb/src/function/table/query_function.cpp index 542122a00..d7abd9e47 100644 --- a/src/duckdb/src/function/table/query_function.cpp +++ b/src/duckdb/src/function/table/query_function.cpp @@ -77,6 +77,11 @@ static string UnionTablesQuery(TableFunctionBindInput &input) { if (children.empty()) { throw InvalidInputException("Input list is empty"); } + for (auto &child : children) { + if (child.IsNull()) { + throw BinderException("Cannot use NULL as function argument"); + } + } result += "FROM " + TableReference(children[0].ToString()); for (size_t i = 1; i < children.size(); ++i) { result += union_all_clause + TableReference(children[i].ToString()); @@ -88,6 +93,9 @@ static string UnionTablesQuery(TableFunctionBindInput &input) { } static unique_ptr QueryBindReplace(ClientContext &context, TableFunctionBindInput &input) { + if (input.inputs[0].IsNull()) { + throw BinderException("Cannot use NULL as function argument"); + } auto query = input.inputs[0].ToString(); auto subquery_ref = ParseSubquery(query, context, "Expected a single SELECT statement"); return std::move(subquery_ref); diff --git a/src/duckdb/src/function/table/sniff_csv.cpp b/src/duckdb/src/function/table/sniff_csv.cpp index a4d30aedc..a05f458bd 100644 --- a/src/duckdb/src/function/table/sniff_csv.cpp +++ b/src/duckdb/src/function/table/sniff_csv.cpp @@ -42,7 +42,7 @@ static unique_ptr CSVSniffBind(ClientContext &context, TableFuncti auto it = input.named_parameters.find("auto_detect"); if (it != input.named_parameters.end()) { if (it->second.IsNull()) { - throw BinderException("\"%s\" expects a non-null boolean value (e.g. TRUE or 1)", it->first); + throw BinderException("%s expects a non-null boolean value (e.g. TRUE or 1)", it->first); } if (!it->second.GetValue()) { throw InvalidInputException("sniff_csv function does not accept auto_detect variable set to false"); @@ -55,7 +55,7 @@ static unique_ptr CSVSniffBind(ClientContext &context, TableFuncti it = input.named_parameters.find("force_match"); if (it != input.named_parameters.end()) { if (it->second.IsNull()) { - throw BinderException("\"%s\" expects a non-null boolean value (e.g. TRUE or 1)", it->first); + throw BinderException("%s expects a non-null boolean value (e.g. TRUE or 1)", it->first); } result->force_match = it->second.GetValue(); input.named_parameters.erase(it); diff --git a/src/duckdb/src/function/table/version/pragma_version.cpp b/src/duckdb/src/function/table/version/pragma_version.cpp index 7701f2143..359479eec 100644 --- a/src/duckdb/src/function/table/version/pragma_version.cpp +++ b/src/duckdb/src/function/table/version/pragma_version.cpp @@ -1,5 +1,5 @@ #ifndef DUCKDB_PATCH_VERSION -#define DUCKDB_PATCH_VERSION "0-alpha48840" +#define DUCKDB_PATCH_VERSION "0-alpha49002" #endif #ifndef DUCKDB_MINOR_VERSION #define DUCKDB_MINOR_VERSION 1 @@ -8,10 +8,10 @@ #define DUCKDB_MAJOR_VERSION 2 #endif #ifndef DUCKDB_VERSION -#define DUCKDB_VERSION "v2.1.0-alpha48840" +#define DUCKDB_VERSION "v2.1.0-alpha49002" #endif #ifndef DUCKDB_SOURCE_ID -#define DUCKDB_SOURCE_ID "16917f94f9" +#define DUCKDB_SOURCE_ID "0b272c23b1" #endif #include "duckdb/function/table/system_functions.hpp" #include "duckdb/main/database.hpp" diff --git a/src/duckdb/src/function/window/window_executor.cpp b/src/duckdb/src/function/window/window_executor.cpp index a2c64d30f..156623fec 100644 --- a/src/duckdb/src/function/window/window_executor.cpp +++ b/src/duckdb/src/function/window/window_executor.cpp @@ -35,6 +35,12 @@ WindowExecutor::WindowExecutor(BoundWindowExpression &wexpr, WindowSharedExpress void WindowExecutor::Evaluate(ExecutionContext &context, idx_t row_idx, DataChunk &eval_chunk, Vector &result, OperatorSinkInput &sink, idx_t count) const { + // Nothing to evaluate for an empty block - the bounds are empty, so skip the executors entirely + if (count == 0) { + FlatVector::SetSize(result, 0); + return; + } + auto &lbstate = sink.local_state.Cast(); lbstate.state.UpdateBounds(row_idx, eval_chunk, count); diff --git a/src/duckdb/src/include/duckdb/common/allocator.hpp b/src/duckdb/src/include/duckdb/common/allocator.hpp index c69fbe2cf..91d3122a0 100644 --- a/src/duckdb/src/include/duckdb/common/allocator.hpp +++ b/src/duckdb/src/include/duckdb/common/allocator.hpp @@ -107,8 +107,6 @@ class Allocator { AllocatedData Allocate(idx_t size) { return AllocatedData(*this, AllocateData(size), size); } - //! Allocate normally, requesting huge-page backing where supported. Advice failure is ignored. - DUCKDB_API AllocatedData TryAllocateHuge(idx_t size); static data_ptr_t DefaultAllocate(PrivateAllocatorData *private_data, idx_t size); static void DefaultFree(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t size); @@ -161,7 +159,7 @@ void DeleteArray(T *ptr, idx_t size) { } template -T *AllocateObject(ARGS &&... args) { +T *AllocateObject(ARGS &&...args) { auto data = Allocator::DefaultAllocator().AllocateData(sizeof(T)); return new (data) T(std::forward(args)...); } @@ -172,15 +170,63 @@ void DestroyObject(T *ptr) { Allocator::DefaultAllocator().FreeData(data_ptr_cast(ptr), sizeof(T)); } +class StandardBufferManager; +class BufferAllocator; + +//! Owns buffer-allocated memory requested with a huge-page hint. +class HugeAllocation { + friend class BufferAllocator; + +public: + DUCKDB_API HugeAllocation(); + DUCKDB_API ~HugeAllocation(); + HugeAllocation(const HugeAllocation &) = delete; + HugeAllocation &operator=(const HugeAllocation &) = delete; + DUCKDB_API HugeAllocation(HugeAllocation &&other) noexcept; + DUCKDB_API HugeAllocation &operator=(HugeAllocation &&other) noexcept; + + data_ptr_t get() { // NOLINT: matching std style + return pointer; + } + const_data_ptr_t get() const { // NOLINT: matching std style + return pointer; + } + idx_t GetSize() const { + return allocated_size; + } + bool IsSet() const { + return pointer; + } + DUCKDB_API void Reset(); + +private: + HugeAllocation(BufferAllocator &allocator, data_ptr_t pointer, idx_t allocated_size); + + optional_ptr allocator; + data_ptr_t pointer; + idx_t allocated_size; +}; + //! The BufferAllocator is a wrapper around the global allocator class that sends any allocations made through the //! buffer manager. This makes the buffer manager aware of the memory usage, allowing it to potentially free //! other blocks to make space in memory. //! Note that there is a cost to doing so (several atomic operations will be performed on allocation/free). //! As such this class should be used primarily for larger allocations. -struct BufferAllocator { - DUCKDB_API static Allocator &Get(ClientContext &context); - DUCKDB_API static Allocator &Get(DatabaseInstance &db); - DUCKDB_API static Allocator &Get(AttachedDatabase &db); +class BufferAllocator : public Allocator { + friend class StandardBufferManager; + friend class HugeAllocation; + +public: + //! Allocate with a huge-page hint, retaining the matching release path. + DUCKDB_API HugeAllocation AllocateHuge(idx_t size); + DUCKDB_API static BufferAllocator &Get(ClientContext &context); + DUCKDB_API static BufferAllocator &Get(DatabaseInstance &db); + DUCKDB_API static BufferAllocator &Get(AttachedDatabase &db); + +private: + explicit BufferAllocator(StandardBufferManager &manager); + + void FreeHuge(data_ptr_t pointer, idx_t size); }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/bignum.hpp b/src/duckdb/src/include/duckdb/common/bignum.hpp index f91416adf..dde3ec24a 100644 --- a/src/duckdb/src/include/duckdb/common/bignum.hpp +++ b/src/duckdb/src/include/duckdb/common/bignum.hpp @@ -10,12 +10,15 @@ #include "duckdb/common/winapi.hpp" #include "duckdb/common/string.hpp" +#include "duckdb/common/vector.hpp" +#include "duckdb/common/optional_ptr.hpp" #include #include "duckdb/common/types/string_type.hpp" namespace duckdb { class StringHeap; class ArenaAllocator; +class ClientContext; struct bignum_t { string_t data; @@ -43,6 +46,26 @@ enum AbsoluteNumberComparison : uint8_t { SMALLER = 2, }; +//! A bignum magnitude is handled as base 2^32 digits +using digit_t = uint32_t; +using twodigit_t = uint64_t; + +//! Per-vector scratch digit buffers and interrupt-check state for BIGNUM multiplication and division +struct BignumArithmeticState { + explicit BignumArithmeticState(optional_ptr context = nullptr) : context(context) { + } + + optional_ptr context; + //! Digit operations performed since the last interrupt check + idx_t work_since_interrupt_check = 0; + unsafe_vector lhs_digits; + unsafe_vector rhs_digits; + unsafe_vector result_digits; + unsafe_vector remainder_digits; + unsafe_vector normalized_dividend; + unsafe_vector normalized_divisor; +}; + struct BignumIntermediate { BignumIntermediate() : is_negative(false), size(0), data(nullptr) {}; explicit BignumIntermediate(const bignum_t &value); @@ -73,6 +96,13 @@ struct BignumIntermediate { void AddInPlace(ArenaAllocator &allocator, const BignumIntermediate &rhs); //! Adds two BignumIntermediates and returns a string_t result, equivalent of a + static string_t Add(StringHeap &heap, const BignumIntermediate &lhs, const BignumIntermediate &rhs); + //! Multiplies two BignumIntermediates and returns a string_t result, equivalent of a * + static string_t Multiply(StringHeap &heap, BignumArithmeticState &state, const BignumIntermediate &lhs, + const BignumIntermediate &rhs); + //! Truncating division (a // b) or, with want_remainder, the remainder with the sign of the dividend (a % b) + //! Returns false without writing a result when the divisor is zero + static bool TryDivMod(StringHeap &heap, BignumArithmeticState &state, const BignumIntermediate &lhs, + const BignumIntermediate &rhs, bool want_remainder, string_t &result); //! Negates a value, e.g., -x string_t Negate(StringHeap &heap) const; void NegateInPlace(); diff --git a/src/duckdb/src/include/duckdb/common/exception_format_value.hpp b/src/duckdb/src/include/duckdb/common/exception_format_value.hpp index 204acce33..f610872c7 100644 --- a/src/duckdb/src/include/duckdb/common/exception_format_value.hpp +++ b/src/duckdb/src/include/duckdb/common/exception_format_value.hpp @@ -39,6 +39,8 @@ struct ExceptionFormatValue { DUCKDB_API ExceptionFormatValue(uhugeint_t uhg_val); // NOLINT ExceptionFormatValueType type; + //! Whether the value is an identifier that was quoted by the formatter + bool quoted_identifier = false; double dbl_val = 0; hugeint_t int_val = 0; diff --git a/src/duckdb/src/include/duckdb/common/file_system.hpp b/src/duckdb/src/include/duckdb/common/file_system.hpp index 7261cc5d3..76047c611 100644 --- a/src/duckdb/src/include/duckdb/common/file_system.hpp +++ b/src/duckdb/src/include/duckdb/common/file_system.hpp @@ -299,6 +299,10 @@ class FileSystem { DUCKDB_API static optional_idx GetAvailableMemory(); //! Returns the space available on the disk. Returns DConstants::INVALID_INDEX if the information was not available. DUCKDB_API static optional_idx GetAvailableDiskSpace(const string &path); + //! Returns the soft limit on open file descriptors, or an invalid index when it is unlimited or unknown. + DUCKDB_API static optional_idx GetFileDescriptorLimit(); + //! Returns the share of the file descriptor limit that one consumer of descriptors may hold open. + DUCKDB_API static idx_t GetFileDescriptorShare(idx_t file_descriptor_limit); //! Path separator for path DUCKDB_API virtual string PathSeparator(const string &path); //! Checks if path is is an absolute path diff --git a/src/duckdb/src/include/duckdb/common/lru_cache.hpp b/src/duckdb/src/include/duckdb/common/lru_cache.hpp index e80bcd518..c82c06d37 100644 --- a/src/duckdb/src/include/duckdb/common/lru_cache.hpp +++ b/src/duckdb/src/include/duckdb/common/lru_cache.hpp @@ -106,18 +106,26 @@ class SharedLruCache { current_total_weight = 0; } - // Evict entries based on their access, until we've freed at least the target number of bytes or there's no entries - // in the cache. Return number of bytes freed. + //! Evict the least recently used entry. Return false if the cache is empty. + bool EvictLeastRecentlyUsed() { + if (lru_list.empty()) { + return false; + } + auto stale_it = entry_map.find(lru_list.back()); + D_ASSERT(stale_it != entry_map.end()); + DeleteImpl(stale_it); + return true; + } + + //! Evict entries until at least target_weight is removed or the cache is empty. Return the removed weight. idx_t EvictToReduceAtLeast(idx_t target_weight) { - idx_t freed = 0; - while (!lru_list.empty() && freed < target_weight) { - const auto &stale_key = lru_list.back(); - auto stale_it = entry_map.find(stale_key); - D_ASSERT(stale_it != entry_map.end()); - freed += stale_it->second.payload_weight; - DeleteImpl(stale_it); + const auto initial_weight = current_total_weight; + while (initial_weight - current_total_weight < target_weight) { + if (!EvictLeastRecentlyUsed()) { + break; + } } - return freed; + return initial_weight - current_total_weight; } idx_t Capacity() const { @@ -172,14 +180,9 @@ class SharedLruCache { } // Evict LRU entries until we have enough space - while (!lru_list.empty() && (current_total_weight + required_weight > max_total_weight)) { - const auto &stale_key = lru_list.back(); - auto stale_it = entry_map.find(stale_key); - if (stale_it != entry_map.end()) { - DeleteImpl(stale_it); - } else { - // Should not happen, but be defensive - lru_list.pop_back(); + while (current_total_weight + required_weight > max_total_weight) { + if (!EvictLeastRecentlyUsed()) { + break; } } } diff --git a/src/duckdb/src/include/duckdb/common/multi_file/base_file_reader.hpp b/src/duckdb/src/include/duckdb/common/multi_file/base_file_reader.hpp index 569d2f438..2b205d6f5 100644 --- a/src/duckdb/src/include/duckdb/common/multi_file/base_file_reader.hpp +++ b/src/duckdb/src/include/duckdb/common/multi_file/base_file_reader.hpp @@ -26,6 +26,7 @@ class AsyncResult; class DataChunk; struct GlobalTableFunctionState; struct LocalTableFunctionState; +class ReadAheadOpenFile; struct BaseFileReaderExpression { public: @@ -69,6 +70,8 @@ class BaseFileReader : public enable_shared_from_this { unique_ptr deletion_filter; //! Global identifiers (which column position of the projected output this filter operates) for the filters vector filter_global_indices; + //! Counts the file against the open file budget of read-ahead until the reader closes it + shared_ptr read_ahead_open_file; public: const vector &GetColumns() const { diff --git a/src/duckdb/src/include/duckdb/common/multi_file/multi_file_function.hpp b/src/duckdb/src/include/duckdb/common/multi_file/multi_file_function.hpp index c9a98a4a9..9a51e74c7 100644 --- a/src/duckdb/src/include/duckdb/common/multi_file/multi_file_function.hpp +++ b/src/duckdb/src/include/duckdb/common/multi_file/multi_file_function.hpp @@ -444,11 +444,15 @@ class MultiFileFunction : public TableFunction { //! Open a file on the read-ahead async pool. Runs off the operator thread; records errors instead of throwing. static void OpenMarkedFileAsync(ClientContext &context, const MultiFileBindData &bind_data, - MultiFileGlobalState &gstate, MultiFileReaderData &reader_data, idx_t file_index) { + MultiFileGlobalState &gstate, MultiFileReaderData &reader_data, idx_t file_index, + const shared_ptr &open_file) { // OpenMarkedFile expects the parallel lock unlocked on entry and locks it before it returns unique_lock parallel_lock(gstate.lock, std::defer_lock); try { - OpenMarkedFile(context, bind_data, gstate, reader_data, file_index, parallel_lock); + if (OpenMarkedFile(context, bind_data, gstate, reader_data, file_index, parallel_lock)) { + // keep the file counted while jobs retain the reader + reader_data.reader->read_ahead_open_file = open_file; + } } catch (std::exception &ex) { if (!parallel_lock.owns_lock()) { parallel_lock.lock(); @@ -469,8 +473,9 @@ class MultiFileFunction : public TableFunction { MultiFileGlobalState &gstate, unique_lock ¶llel_lock) { auto &read_ahead = *gstate.read_ahead; idx_t file_index = gstate.file_index; + idx_t files_ahead = 0; bool progress_guaranteed = false; - while (!progress_guaranteed || read_ahead.CanScheduleOpen()) { + while (!progress_guaranteed || read_ahead.CanScheduleOpen(files_ahead)) { const bool has_file_to_read = file_index < gstate.readers.size(); if (!has_file_to_read && !TryGetNextFile(gstate, parallel_lock)) { return; @@ -485,9 +490,10 @@ class MultiFileFunction : public TableFunction { reader_data.file_state = MultiFileFileState::OPENING; { MultiFileReaderData *reader_ptr = &reader_data; + auto open_file = make_shared_ptr(); read_ahead.ScheduleFileOpen( - [&context, &bind_data, &gstate, reader_ptr, current_file_index]() { - OpenMarkedFileAsync(context, bind_data, gstate, *reader_ptr, current_file_index); + [&context, &bind_data, &gstate, reader_ptr, current_file_index, open_file]() { + OpenMarkedFileAsync(context, bind_data, gstate, *reader_ptr, current_file_index, open_file); }, [&gstate]() { // the reader stays in OPENING, so tell every waiter to stop instead of polling forever @@ -495,11 +501,13 @@ class MultiFileFunction : public TableFunction { gstate.async_open_settled.notify_all(); }); } + files_ahead++; progress_guaranteed = true; break; case MultiFileFileState::OPENING: case MultiFileFileState::OPEN: // the front file is already being/been opened - forward progress is guaranteed + files_ahead++; progress_guaranteed = true; break; default: @@ -1030,6 +1038,10 @@ class MultiFileFunction : public TableFunction { lstate.job_state = MultiFileJobState::DECODE; return ScanReadAheadAcquire::ACQUIRED; } + if (!read_ahead.CanScheduleOpen(0)) { + // the open file budget is full and the finished job may hold the last reference to its reader + lstate.job.reset(); + } unique_ptr claimed; auto acquired = read_ahead.AcquireJob( context, data_p, diff --git a/src/duckdb/src/include/duckdb/common/serializer/async_write_queue.hpp b/src/duckdb/src/include/duckdb/common/serializer/async_write_queue.hpp index 70a97e9b3..39a0deb3c 100644 --- a/src/duckdb/src/include/duckdb/common/serializer/async_write_queue.hpp +++ b/src/duckdb/src/include/duckdb/common/serializer/async_write_queue.hpp @@ -331,6 +331,8 @@ class ManagedAsyncWriteQueue : private AsyncWriteTarget { //! V1 is a contiguous logical write queue: each RegisterWrite offset must match the next expected offset. //! Callers are responsible for assigning offsets and externally serializing RegisterWrite calls. class ManagedAsyncWriteStreamQueue : private AsyncWriteTarget { + friend class ManagedAsyncWriteQueueTest; + public: //! Whether registering a payload may schedule an async drain request immediately. enum class ScheduleMode : uint8_t { ALLOW, DEFER }; diff --git a/src/duckdb/src/include/duckdb/common/sorting/sort_key.hpp b/src/duckdb/src/include/duckdb/common/sorting/sort_key.hpp index 6bc218629..56feaa788 100644 --- a/src/duckdb/src/include/duckdb/common/sorting/sort_key.hpp +++ b/src/duckdb/src/include/duckdb/common/sorting/sort_key.hpp @@ -327,9 +327,27 @@ struct VariableSortKey : std::conditional, return lhs.size < rhs.size; } - // Both strings are non-inlined - comp_res = memcmp(lhs.data.u.ptr, rhs.data.u.ptr, MinValue(lhs.size, rhs.size)); - return comp_res < 0 || (comp_res == 0 && lhs.size < rhs.size); + const auto size = MinValue(lhs.size, rhs.size); + const auto suffix_size = size - SORT_KEY::INLINE_LENGTH; + constexpr idx_t WORD_COMPARE_LIMIT = 80; + if (suffix_size > WORD_COMPARE_LIMIT) { + comp_res = + memcmp(lhs.data.u.ptr + SORT_KEY::INLINE_LENGTH, rhs.data.u.ptr + SORT_KEY::INLINE_LENGTH, suffix_size); + return comp_res < 0 || (comp_res == 0 && lhs.size < rhs.size); + } + + for (idx_t offset = SORT_KEY::INLINE_LENGTH; offset + sizeof(uint64_t) < size; offset += sizeof(uint64_t)) { + const auto lhs_word = Load(lhs.data.u.ptr + offset); + const auto rhs_word = Load(rhs.data.u.ptr + offset); + if (lhs_word != rhs_word) { + return BSwapIfLE(lhs_word) < BSwapIfLE(rhs_word); + } + } + + // Overlap already-compared bytes to keep the final word inside the logical key. + const auto lhs_word = BSwapIfLE(Load(lhs.data.u.ptr + size - sizeof(uint64_t))); + const auto rhs_word = BSwapIfLE(Load(rhs.data.u.ptr + size - sizeof(uint64_t))); + return lhs_word < rhs_word || (lhs_word == rhs_word && lhs.size < rhs.size); } }; diff --git a/src/duckdb/src/include/duckdb/common/types/bignum.hpp b/src/duckdb/src/include/duckdb/common/types/bignum.hpp index 43693e94b..8ecfab197 100644 --- a/src/duckdb/src/include/duckdb/common/types/bignum.hpp +++ b/src/duckdb/src/include/duckdb/common/types/bignum.hpp @@ -17,8 +17,6 @@ #include "duckdb/common/bignum.hpp" namespace duckdb { -using digit_t = uint32_t; -using twodigit_t = uint64_t; struct BoundCastInfo; struct CastParameters; diff --git a/src/duckdb/src/include/duckdb/common/types/row/tuple_data_collection.hpp b/src/duckdb/src/include/duckdb/common/types/row/tuple_data_collection.hpp index 5b5b468ae..656fd3521 100644 --- a/src/duckdb/src/include/duckdb/common/types/row/tuple_data_collection.hpp +++ b/src/duckdb/src/include/duckdb/common/types/row/tuple_data_collection.hpp @@ -92,7 +92,8 @@ class TupleDataCollection { vector> GetChunkRangesForPartition(idx_t partition_idx) const; //! Gets the pointers to the start of every block vector GetRowBlockPointers(); - //! Destroy the blocks corresponding to the chunk indices + //! Destroy blocks for an incremental chunk range; earlier chunks must no longer be read. + //! Blocks needed by chunks at or after chunk_idx_end are retained. void DestroyChunks(idx_t chunk_idx_begin, idx_t chunk_idx_end); //! Gets the scatter function for the given type diff --git a/src/duckdb/src/include/duckdb/execution/aggregate_hashtable.hpp b/src/duckdb/src/include/duckdb/execution/aggregate_hashtable.hpp index 696ee5b68..89a127af2 100644 --- a/src/duckdb/src/include/duckdb/execution/aggregate_hashtable.hpp +++ b/src/duckdb/src/include/duckdb/execution/aggregate_hashtable.hpp @@ -232,7 +232,7 @@ class GroupedAggregateHashTable : public BaseAggregateHashTable { //! The capacity of the HT. This can be increased using GroupedAggregateHashTable::Resize idx_t capacity; //! The hash map (pointer table) of the HT: allocated data and pointer into it - AllocatedData hash_map; + HugeAllocation hash_map; ht_entry_t *entries; //! Offset of the hash column in the rows idx_t hash_offset; diff --git a/src/duckdb/src/include/duckdb/execution/index/art/art_operator.hpp b/src/duckdb/src/include/duckdb/execution/index/art/art_operator.hpp index 7fd8f6850..8300d3fe9 100644 --- a/src/duckdb/src/include/duckdb/execution/index/art/art_operator.hpp +++ b/src/duckdb/src/include/duckdb/execution/index/art/art_operator.hpp @@ -158,7 +158,7 @@ class ARTOperator { if (GateContainsDeletedRow(arena, art, active_node_ptr, key, delete_index_info)) { return ARTConflictType::CONSTRAINT; } - throw FatalException("Corrupted unique ART index \"%s\": encountered an existing gated leaf in unique " + throw FatalException("Corrupted unique ART index %s: encountered an existing gated leaf in unique " "index while inserting", art.name); } diff --git a/src/duckdb/src/include/duckdb/execution/join_hashtable.hpp b/src/duckdb/src/include/duckdb/execution/join_hashtable.hpp index 15860231e..41a804029 100644 --- a/src/duckdb/src/include/duckdb/execution/join_hashtable.hpp +++ b/src/duckdb/src/include/duckdb/execution/join_hashtable.hpp @@ -548,7 +548,7 @@ class JoinHashTable { unique_ptr data_collection; //! The hash map of the HT, created after finalization - AllocatedData hash_map; + HugeAllocation hash_map; //! Whether or not NULL values are considered equal in each of the comparisons vector null_values_are_equal; //! An empty tuple that's a "dead end", can be used to stop chains early diff --git a/src/duckdb/src/include/duckdb/execution/operator/join/physical_hash_join.hpp b/src/duckdb/src/include/duckdb/execution/operator/join/physical_hash_join.hpp index 733ca89e6..ac634db49 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/join/physical_hash_join.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/join/physical_hash_join.hpp @@ -87,6 +87,7 @@ class PhysicalHashJoin : public PhysicalComparisonJoin { PipelineExternalInputCost GetExternalInputCost() const override { return PipelineExternalInputCost::SERIALIZED_FANOUT; } + void InitializeOutputChunk(Allocator &allocator, DataChunk &chunk) const override; protected: // CachingOperator Interface diff --git a/src/duckdb/src/include/duckdb/function/scalar_function.hpp b/src/duckdb/src/include/duckdb/function/scalar_function.hpp index 1cc218b74..6baa6ca1d 100644 --- a/src/duckdb/src/include/duckdb/function/scalar_function.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar_function.hpp @@ -679,7 +679,7 @@ class BindScalarFunctionInput : public BindFunctionInput { } Binder &GetBinder() { if (binder == nullptr) { - throw InternalException("Function '%s' has cannot be bound without a Binder!", bound_function.GetName()); + throw InternalException("Function %s has cannot be bound without a Binder!", bound_function.GetName()); } return *binder; } diff --git a/src/duckdb/src/include/duckdb/main/config.hpp b/src/duckdb/src/include/duckdb/main/config.hpp index 1c04b657d..ba54c221f 100644 --- a/src/duckdb/src/include/duckdb/main/config.hpp +++ b/src/duckdb/src/include/duckdb/main/config.hpp @@ -162,7 +162,7 @@ struct DBConfigOptions { identifier_set_t allowed_configs; //! The log configuration LogConfig log_config = LogConfig(); - //! Physical memory that the block allocator is allowed to use (this memory is never freed and cannot be reduced) + //! Block allocator capacity (cannot be reduced; free physical pages can be reclaimed) idx_t block_allocator_size = 0; //! Memory limit for the write buffer per row group (optional) optional_idx write_buffer_row_group_memory_limit; diff --git a/src/duckdb/src/include/duckdb/main/database_file_path_manager.hpp b/src/duckdb/src/include/duckdb/main/database_file_path_manager.hpp index 8d021eeb5..91342f749 100644 --- a/src/duckdb/src/include/duckdb/main/database_file_path_manager.hpp +++ b/src/duckdb/src/include/duckdb/main/database_file_path_manager.hpp @@ -41,6 +41,8 @@ class DatabaseFilePathManager { void EraseDatabasePath(const string &path); //! Called when a database is detached, but before it is fully finished being used void DetachDatabase(DatabaseManager &manager, const string &path); + //! Whether the (canonicalized) path is in use by an attached database + bool IsDatabasePath(const string &path) const; private: //! The lock to add entries to the db_paths map diff --git a/src/duckdb/src/include/duckdb/main/database_manager.hpp b/src/duckdb/src/include/duckdb/main/database_manager.hpp index e3e8efd7e..71ad1de33 100644 --- a/src/duckdb/src/include/duckdb/main/database_manager.hpp +++ b/src/duckdb/src/include/duckdb/main/database_manager.hpp @@ -131,6 +131,8 @@ class DatabaseManager { } //! Gets a list of all attached database paths vector GetAttachedDatabasePaths(); + //! Whether the file at the path (or its WAL) belongs to an attached database + bool IsAttachedDatabaseFile(ClientContext &context, const string &path); shared_ptr GetDatabaseInternal(const lock_guard &, const Identifier &name); diff --git a/src/duckdb/src/include/duckdb/main/http/http_transport_manager.hpp b/src/duckdb/src/include/duckdb/main/http/http_transport_manager.hpp index 0176bf846..e9b430d55 100644 --- a/src/duckdb/src/include/duckdb/main/http/http_transport_manager.hpp +++ b/src/duckdb/src/include/duckdb/main/http/http_transport_manager.hpp @@ -129,7 +129,6 @@ class HTTPTransportManager { void Close() noexcept DUCKDB_EXCLUDES(lock); static idx_t CalculateCapacity(idx_t system_concurrency, optional_idx file_descriptor_limit, idx_t io_concurrency = 0); - static optional_idx GetFileDescriptorLimit(); static bool AdvanceConnectionEpoch(uint64_t &connection_epoch, bool &reuse_poisoned) noexcept; void SetHTTPUtil(const shared_ptr &new_http_util) DUCKDB_EXCLUDES(lock); HTTPUtil &GetHTTPUtil() const DUCKDB_EXCLUDES(lock); diff --git a/src/duckdb/src/include/duckdb/main/settings.hpp b/src/duckdb/src/include/duckdb/main/settings.hpp index 8e7e71d55..d461bee19 100644 --- a/src/duckdb/src/include/duckdb/main/settings.hpp +++ b/src/duckdb/src/include/duckdb/main/settings.hpp @@ -455,8 +455,8 @@ struct AutoloadKnownExtensionsSetting { struct BlockAllocatorMemorySetting { using RETURN_TYPE = string; static constexpr const char *Name = "block_allocator_memory"; - static constexpr const char *Description = "Physical memory that the block allocator is allowed to use. This " - "memory is allocated once, never freed, and cannot be reduced."; + static constexpr const char *Description = + "Block allocator capacity (cannot be reduced; free physical pages can be reclaimed)."; static constexpr const char *InputType = "VARCHAR"; static constexpr bool IsDebug = false; static constexpr bool IsDeprecated = false; @@ -685,6 +685,19 @@ struct DebugEvictionQueueSleepMicroSecondsSetting { static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); }; +struct DebugFileDescriptorLimitSetting { + using RETURN_TYPE = idx_t; + static constexpr const char *Name = "debug_file_descriptor_limit"; + static constexpr const char *Description = "Override the file descriptor limit that bounds how many files a scan " + "opens ahead. 0 uses the limit of the process."; + static constexpr const char *InputType = "UBIGINT"; + static constexpr bool IsDebug = true; + static constexpr bool IsDeprecated = false; + static constexpr const char *DefaultValue = "0"; + static constexpr SettingScopeTarget Scope = SettingScopeTarget::LOCAL_DEFAULT; + static constexpr idx_t SettingIndex = NEXT_SETTING_INDEX(); +}; + struct ForceBitpackingModeSetting { using RETURN_TYPE = BitpackingMode; static constexpr const char *Name = "debug_force_bitpacking_mode"; diff --git a/src/duckdb/src/include/duckdb/optimizer/column_binding_replacer.hpp b/src/duckdb/src/include/duckdb/optimizer/column_binding_replacer.hpp index 47277c4c4..8d59b2b09 100644 --- a/src/duckdb/src/include/duckdb/optimizer/column_binding_replacer.hpp +++ b/src/duckdb/src/include/duckdb/optimizer/column_binding_replacer.hpp @@ -8,6 +8,7 @@ #pragma once +#include "duckdb/planner/column_binding_map.hpp" #include "duckdb/planner/logical_operator.hpp" #include @@ -36,11 +37,16 @@ struct ReplacementBinding { class BindingReplacementGraph { public: ColumnBinding Resolve(ColumnBinding binding) const; + //! Find the direct replacement edge for a binding, if any + optional_ptr Find(ColumnBinding old_binding) const; bool TryAdd(const ReplacementBinding &replacement); void Add(ColumnBinding old_binding, ColumnBinding new_binding); void Add(const ReplacementBinding &replacement); void Merge(const BindingReplacementGraph &replacements); void AddTo(ColumnBindingReplacer &replacer) const; + //! Collapse the graph to single edges that resolve into the given output boundary, dropping edges that do not. + //! Resolution at any enclosing boundary is unaffected because operators above cannot see bindings dropped here. + BindingReplacementGraph ScopedToBoundary(const vector &boundary) const; bool Empty() const { return replacement_bindings.empty(); @@ -55,6 +61,8 @@ class BindingReplacementGraph { private: ReplacementBinding ResolveReplacement(ColumnBinding binding) const; vector replacement_bindings; + //! Index into replacement_bindings by old_binding (unique by construction) + column_binding_map_t binding_slots; }; //! The ColumnBindingReplacer updates column bindings (e.g., after changing the operator plan), utility for optimizers @@ -69,9 +77,18 @@ class ColumnBindingReplacer : public LogicalOperatorVisitor { virtual void VisitOperatorBindings(LogicalOperator &op); //! Add binding replacements by position void AddReplacements(const vector &old_bindings, const vector &new_bindings); + //! Index replacement_bindings by old_binding for O(1) lookup. Only takes effect for replacement lists that + //! sequential application cannot chain (no new_binding occurs as an old_binding); otherwise lookups stay linear. + //! Call after the replacement list is complete; later list changes invalidate the index. + void BuildIndex(); + //! Apply the replacement list to a single binding (and optionally its type) + void Replace(ColumnBinding &binding) const; + void Replace(ColumnBinding &binding, LogicalType &type) const; protected: unique_ptr VisitReplace(BoundColumnRefExpression &expr, unique_ptr *expr_ptr) override; + //! Find the applicable replacement, using the index if one was built + optional_ptr FindReplacement(ColumnBinding binding) const; public: //! Do not recurse further than this operator (optional) @@ -79,6 +96,10 @@ class ColumnBindingReplacer : public LogicalOperatorVisitor { //! Contains all bindings that need to be updated vector replacement_bindings; + +private: + column_binding_map_t replacement_index; + bool use_index = false; }; //! Like ColumnBindingReplacer, but also updates correlated-column metadata and nested subquery plans. diff --git a/src/duckdb/src/include/duckdb/optimizer/statistics_propagator.hpp b/src/duckdb/src/include/duckdb/optimizer/statistics_propagator.hpp index 04142adc8..3ddbeecae 100644 --- a/src/duckdb/src/include/duckdb/optimizer/statistics_propagator.hpp +++ b/src/duckdb/src/include/duckdb/optimizer/statistics_propagator.hpp @@ -95,6 +95,10 @@ class StatisticsPropagator { void UpdateFilterStatistics(const Expression &condition); //! Set the statistics of a specific column binding to not contain null values void SetStatisticsNotNull(ColumnBinding binding); + //! Reset additive statistics of bindings whose rows an operator can emit more than once + void ResetAdditiveStatistics(const vector &bindings); + //! Reset additive statistics in the verification statistics of an expression tree + void ResetAdditiveVerificationStats(Expression &expr); //! Determine whether a propagated condition can be pruned FilterPropagateResult ClassifyFilter(Expression &condition); //! Simplify conjunctions using filter truth semantics diff --git a/src/duckdb/src/include/duckdb/parallel/scan_read_ahead.hpp b/src/duckdb/src/include/duckdb/parallel/scan_read_ahead.hpp index b40a20121..275fd1f8b 100644 --- a/src/duckdb/src/include/duckdb/parallel/scan_read_ahead.hpp +++ b/src/duckdb/src/include/duckdb/parallel/scan_read_ahead.hpp @@ -120,6 +120,18 @@ class ScanStatePool { vector> states DUCKDB_GUARDED_BY(lock); }; +//! Counts one file against the open file budget of all scans until destroyed +class ReadAheadOpenFile { +public: + ReadAheadOpenFile(); + ~ReadAheadOpenFile(); + ReadAheadOpenFile(const ReadAheadOpenFile &) = delete; + ReadAheadOpenFile &operator=(const ReadAheadOpenFile &) = delete; + + //! Files counted by all scans of the process + static idx_t Count(); +}; + //! Outcome of ScanReadAhead::AcquireJob enum class ScanReadAheadAcquire : uint8_t { ACQUIRED, //! a job with settled I/O is now held @@ -158,8 +170,9 @@ class ScanReadAhead { //! Schedule a file-open closure on the async pool, opening files ahead of decoding //! cancel_fn runs instead when the open is retired without running, and must settle whatever a scan waits on void ScheduleFileOpen(std::function open_fn, std::function cancel_fn); - //! Whether another file-open may be scheduled without exceeding the open-ahead window - bool CanScheduleOpen() const; + //! Whether another file may be opened when files_ahead files are already opening or open ahead of the scan + //! and the files held open by all scans are within the open file budget + bool CanScheduleOpen(idx_t files_ahead) const; //! Run one queued async task inline, returns false when none is queued bool TryRunPendingTask(); //! Throw the first error recorded on the async executor, if there is one @@ -234,8 +247,10 @@ class ScanReadAhead { const idx_t read_ahead_depth; //! Async memory governor unique_ptr memory_governor; - //! Maximum file-opens scheduled ahead of decoding on the async pool + //! Maximum files opening or open ahead of decoding const idx_t open_window; + //! Maximum files held open by all scans, a share of the file descriptor limit + const idx_t open_file_budget; //! Backlog budget granted by the reservation, refreshed whenever a job is pushed atomic backlog_budget {0}; @@ -257,8 +272,6 @@ class ScanReadAhead { //! Threads waiting for progress, only these need a notification atomic waiting_threads {0}; condition_variable progress; - //! File-opens scheduled on the async pool that have not completed yet, shared with the open tasks - shared_ptr> pending_opens; //! Async I/O executor (async pool) shared_ptr executor; }; diff --git a/src/duckdb/src/include/duckdb/parallel/task_scheduler.hpp b/src/duckdb/src/include/duckdb/parallel/task_scheduler.hpp index cd691967b..fa42be29c 100644 --- a/src/duckdb/src/include/duckdb/parallel/task_scheduler.hpp +++ b/src/duckdb/src/include/duckdb/parallel/task_scheduler.hpp @@ -10,6 +10,7 @@ #include "duckdb/common/atomic.hpp" #include "duckdb/common/common.hpp" +#include "duckdb/common/optional_idx.hpp" #include "duckdb/common/vector.hpp" #include "duckdb/parallel/task.hpp" #include "duckdb/common/array.hpp" @@ -59,8 +60,8 @@ class TaskScheduler { //! Fetches a task from a specific producer, returns whether a task was found and assigned to `task`. bool GetTaskFromProducerLocked(ProducerToken &token, shared_ptr &task) DUCKDB_REQUIRES(token.producer_lock); //! Run tasks forever until "marker" is set to false, "marker" must remain valid until the thread is joined - void ExecuteForever(atomic *marker); - void ExecuteForever(atomic *marker, TaskSchedulerType pool_type); + void ExecuteForever(atomic *marker, TaskSchedulerType pool_type = TaskSchedulerType::REGULAR, + optional_idx cpu = optional_idx()); //! Run tasks until `marker` is set to false, `max_tasks` have been completed, or until there are no more tasks //! available. Returns the number of tasks that were completed. idx_t ExecuteTasks(atomic *marker, idx_t max_tasks); @@ -86,6 +87,8 @@ class TaskScheduler { private: TaskSchedulerPool &GetPool(TaskSchedulerType pool_type); TaskSchedulerQueue &GetQueue(TaskSchedulerType pool_type) const; + //! Actual CPU selected by a successful worker pin; unset for other threads. + static optional_idx &GetPinnedCPUId(); //! Fetches a task, returns true if successful or false if no tasks were available bool GetTaskInternal(ConsumerToken &consumer, shared_ptr &task); diff --git a/src/duckdb/src/include/duckdb/parser/peg/compiled_grammar.hpp b/src/duckdb/src/include/duckdb/parser/peg/compiled_grammar.hpp index f8a55ed8f..57fe65f01 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/compiled_grammar.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/compiled_grammar.hpp @@ -2,7 +2,6 @@ #include "duckdb/common/pair.hpp" #include "duckdb/parser/peg/matcher.hpp" -#include "duckdb/parser/peg/transformer/peg_transformer.hpp" #include "duckdb/parser/peg/parsed_grammar.hpp" namespace duckdb { @@ -35,6 +34,7 @@ struct CompiledGrammar { return *tokenizer; } optional_ptr GetRule(const string &rule_name) const; + optional_ptr GetRule(PEGRuleId rule_id) const; public: static shared_ptr Get(ClientContext &context); @@ -50,6 +50,8 @@ struct CompiledGrammar { unique_ptr keyword_helper; unique_ptr tokenizer; case_insensitive_map_t> rules; + //! Rules indexed by PEGRuleId; built-in slots stay empty when the grammar does not define the rule + vector> rules_by_id; const Matcher &program_matcher; const Matcher &top_level_statement_matcher; }; diff --git a/src/duckdb/src/include/duckdb/parser/peg/matcher.hpp b/src/duckdb/src/include/duckdb/parser/peg/matcher.hpp index b4150dd88..038c63f4a 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/matcher.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/matcher.hpp @@ -186,10 +186,10 @@ struct MatchState { } template - MatcherResult AllocateParseResult(ARGS &&... args); + MatcherResult AllocateParseResult(ARGS &&...args); template - arena_ptr Make(ARGS &&... args); + arena_ptr Make(ARGS &&...args); void UpdateMaxTokenIndex() { if (token_iterator.Position() > context.max_token_index) { @@ -421,6 +421,7 @@ class KeywordInfo { class MatcherAllocator { public: Matcher &Allocate(unique_ptr matcher); + void InitializeListChains(); private: vector> matchers; @@ -432,7 +433,7 @@ class ParseResultAllocator { ~ParseResultAllocator(); template - optional_ptr Make(ARGS &&... args) { + optional_ptr Make(ARGS &&...args) { static_assert(std::is_base_of::value, "Expected a parse result"); auto result = arena.Make(std::forward(args)...); if (ParseResultNeedsDestructor::value) { @@ -467,7 +468,7 @@ class ParseResultAllocator { }; template -arena_ptr MatchState::Make(ARGS &&... args) { +arena_ptr MatchState::Make(ARGS &&...args) { static_assert(std::is_base_of::value, "Expected a matcher process"); return arena_ptr(context.process_allocator.Make(std::forward(args)...)); } @@ -481,7 +482,7 @@ void ParseResult::SetNameFrom(const Matcher &matcher_p) { } template -MatcherResult MatchState::AllocateParseResult(ARGS &&... args) { +MatcherResult MatchState::AllocateParseResult(ARGS &&...args) { if (!BuildParseResult()) { return MatcherResult::Success(); } diff --git a/src/duckdb/src/include/duckdb/parser/peg/matcher/list_matcher.hpp b/src/duckdb/src/include/duckdb/parser/peg/matcher/list_matcher.hpp index 4d6f6b56e..b1b30efca 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/matcher/list_matcher.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/matcher/list_matcher.hpp @@ -15,6 +15,20 @@ class ListMatcher : public Matcher { } DUCKDB_API arena_ptr StartMatch(MatchState &state) const override; + //! Prepare after matcher links and flags are final; unprepared lists use ordinary execution. + DUCKDB_API void InitializeListChain(); + + optional_ptr GetChainChild() const { + if (!first_child_is_chainable) { + return nullptr; + } + D_ASSERT(IsStructural() && !suppress_suggestions && !matchers.empty()); + auto &child = matchers[0].get(); + D_ASSERT(child.IsStructural() && child.Type() == MatcherType::LIST && !child.IsPackratMemoized()); + auto &list = static_cast(child); + D_ASSERT(!list.suppress_suggestions); + return list; + } SuggestionType AddSuggestionInternal(MatchState &state) const override { if (suppress_suggestions) { @@ -46,6 +60,9 @@ class ListMatcher : public Matcher { vector> matchers; //! If true, this matcher will not contribute autocomplete suggestions (used for rules like ExpressionStatement) bool suppress_suggestions = false; + +private: + bool first_child_is_chainable = false; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/parsed_grammar.hpp b/src/duckdb/src/include/duckdb/parser/peg/parsed_grammar.hpp index 76c22f1f2..1c93dceeb 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/parsed_grammar.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/parsed_grammar.hpp @@ -11,6 +11,7 @@ #include "duckdb/common/case_insensitive_map.hpp" #include "duckdb/common/types/string_heap.hpp" #include "duckdb/parser/peg/peg_parser.hpp" +#include "duckdb/parser/peg/peg_rule_id.hpp" #include "duckdb/parser/peg/transformer/transform_result.hpp" #include @@ -129,6 +130,8 @@ struct CompiledGrammarRule { arena_ptr StartTransform(PEGTransformer &transformer, ParseResult &parse_result) const; string name; + //! Built-in rules get their generated constant, rules added at runtime get an ID past BUILTIN_COUNT + PEGRuleId rule_id = PEGRuleId::INVALID; grammar_transform_process_function_t transform_process; //! See ParsedGrammar::SetTransformProcess bool collapsible; diff --git a/src/duckdb/src/include/duckdb/parser/peg/peg_rule_id.hpp b/src/duckdb/src/include/duckdb/parser/peg/peg_rule_id.hpp new file mode 100644 index 000000000..19ec36bef --- /dev/null +++ b/src/duckdb/src/include/duckdb/parser/peg/peg_rule_id.hpp @@ -0,0 +1,1215 @@ +// AUTO-GENERATED by scripts/parser/generate_transformer_trampoline.py -- DO NOT EDIT +#pragma once + +#include "duckdb/common/common.hpp" + +namespace duckdb { + +//! Identifies a grammar rule; rules added at runtime get IDs from BUILTIN_COUNT onwards +enum class PEGRuleId : uint32_t { + INVALID = 0, + AlterStatement, + AlterOptions, + AlterTableStmt, + AlterSchemaStmt, + AlterSchemaOptions, + AlterTableOptions, + AddConstraint, + DropConstraint, + AddColumn, + AddColumnEntry, + DropColumn, + AlterColumn, + RenameColumn, + NestedColumnName, + IdentifierDot, + RenameAlter, + SetPartitionedBy, + ResetPartitionedBy, + SetSortedBy, + ResetSortedBy, + SetOptions, + ResetOptions, + AlterColumnEntry, + AddOrDropDefault, + AddDefault, + DropDefault, + ChangeNullability, + DropOrSet, + DropNullability, + SetNullability, + AlterType, + SetData, + UsingExpression, + AlterViewStmt, + AlterSequenceStmt, + QualifiedSequenceName, + AlterSequenceOptions, + RenameAlterSequenceOptions, + SetSequenceOption, + AlterDatabaseStmt, + AnalyzeStatement, + AnalyzeTarget, + AnalyzeVerbose, + AttachStatement, + Database, + DatabasePath, + AttachAlias, + AttachOptions, + CallStatement, + CheckpointStatement, + CheckpointForce, + CommentStatement, + CommentTarget, + CommentStringLiteralIdentifier, + CommentOnType, + CommentTable, + CommentSequence, + CommentFunction, + CommentMacroTable, + CommentMacro, + CommentView, + CommentDatabase, + CommentIndex, + CommentSchema, + CommentType, + CommentColumn, + CommentValue, + StringLiteralValue, + Program, + TopLevelStatement, + Statement, + AnalyzeKeyword, + CatalogName, + SchemaName, + ReservedSchemaName, + TableName, + ReservedTableName, + ReservedIdentifier, + ColumnName, + ReservedColumnName, + IndexName, + ReservedIndexName, + SettingName, + PragmaName, + FunctionName, + ReservedFunctionName, + ReservedTypeName, + TableFunctionName, + ConstraintName, + SequenceName, + CollationName, + CopyOptionName, + NumberLiteral, + StringLiteral, + Type, + TypeVariations, + SimpleType, + CharacterSimpleType, + QualifiedSimpleType, + CharacterType, + IntervalType, + IntervalInterval, + IntervalWithSpecifier, + IntervalWithRangeSpecifier, + IntervalWithSimpleSpecifier, + IntervalWithoutSpecifier, + IntervalToIntervalAsType, + IntervalNumber, + YearKeyword, + MonthKeyword, + DayKeyword, + HourKeyword, + MinuteKeyword, + SecondKeyword, + MillisecondKeyword, + MicrosecondKeyword, + WeekKeyword, + QuarterKeyword, + DecadeKeyword, + CenturyKeyword, + MillenniumKeyword, + Interval, + IntervalToInterval, + YearToMonth, + DayToHour, + DayToMinute, + DayToSecond, + HourToMinute, + HourToSecond, + MinuteToSecond, + BitType, + GeometryType, + VariantType, + NumericType, + SimpleNumericType, + DecimalNumericType, + IntType, + IntegerType, + SmallintType, + BigintType, + RealType, + BooleanType, + DoubleType, + FloatType, + DecimalType, + DecType, + NumericModType, + QualifiedTypeName, + TypeNameAsQualifiedName, + CatalogReservedSchemaTypeName, + SchemaReservedTypeName, + TypeModifiers, + NestedType, + RowType, + SetofType, + UnionType, + ColIdTypeList, + MapType, + TupleType, + ColIdType, + ArrayBounds, + ArrayKeyword, + ArrayKeywordWithBounds, + SquareBracketsArray, + TimeType, + TimeOrTimestamp, + TimeTypeId, + TimestampTypeId, + TimeZone, + WithOrWithout, + WithRule, + WithoutRule, + RowOrStruct, + List, + Parens, + ConnectStatement, + DisconnectStatement, + SessionTarget, + LocalSessionTarget, + StringSessionTarget, + CatalogSessionTarget, + CopyStatement, + CopyVariations, + CopyTable, + FromOrTo, + CopyFrom, + CopyTo, + CopySelect, + CopyFileName, + CopyFileNameExpression, + CopyFileNameStringLiteral, + CopyFileNameIdentifier, + CopyFileNameIdentifierColId, + IdentifierColId, + CopyFileNameSuffix, + CopyLegacyOption, + CopyOptions, + CopyOptionList, + SpecializedOptionList, + SpecializedOptionTail, + SpecializedOption, + SingleOption, + BinaryOption, + FreezeOption, + OidsOption, + CsvOption, + HeaderOption, + NullAsOption, + DelimiterAsOption, + QuoteAsOption, + EscapeAsOption, + EncodingOption, + ForceQuoteOption, + StarSymbolColumnList, + ForceQuote, + PartitionByOption, + PartitionByColumnList, + StarPartitionByColumnList, + ParenthesizedPartitionByColumnList, + SinglePartitionByColumnList, + ForceNullOption, + ForceNotNull, + StarSymbol, + CopyGenericOptionList, + CopyGenericOption, + OrderByCopyOption, + PartitionedByCopyOption, + GenericCopyOptionList, + GenericCopyOption, + GenericCopyOptionValue, + GenericCopyOptionOrderList, + GenericCopyOptionExpression, + GenericCopyOptionParenthesizedExpressionList, + CopyFromDatabase, + CopyFromDatabaseWithFlag, + CopyFromDatabaseWithoutFlag, + CopyDatabaseFlag, + SchemaOrData, + CopySchema, + CopyData, + CreateIndexStmt, + WithList, + RelOptionOrOids, + RelOptionList, + Oids, + WithOrWithoutOids, + WithOids, + WithoutOids, + IndexElement, + UniqueIndex, + IndexType, + RelOption, + RelOptionName, + DottedColLabel, + RelOptionArgumentOpt, + DefArg, + DefArgNull, + DefArgKeyword, + DefArgStringLiteral, + NoneLiteral, + CreateMacroStmt, + MacroOrFunction, + MacroKeyword, + FunctionKeyword, + MacroDefinition, + MacroDefinitionBody, + MacroParameters, + MacroParameter, + SimpleParameter, + ScalarMacroDefinition, + TableMacroDefinition, + CreateSchemaStmt, + WithOptionList, + CreateSecretStmt, + SecretStorageSpecifier, + SecretName, + CreateSequenceStmt, + SequenceOption, + SeqSetCycle, + SeqCycle, + SeqNoCycle, + SeqSetIncrement, + SeqSetMinMax, + SeqNoMinMax, + SeqStartWith, + SeqOwnedBy, + SeqMinOrMax, + MinValue, + MaxValue, + CreateStatement, + CreateStatementVariation, + OrReplace, + Temporary, + Persistent, + TempPersistent, + TemporaryPersistent, + CreateTableStmt, + CreateTableDefinition, + CreateTableAs, + PartitionSortedOptions, + PartitionOptSortedOptions, + SortedOptPartitionOptions, + PartitionOptions, + SortedOptions, + WithData, + WithDataOnly, + WithNoData, + IdentifierList, + CreateColumnList, + IfNotExists, + QualifiedName, + SchemaReservedIdentifierOrStringLiteral, + CatalogReservedSchemaIdentifier, + IdentifierOrStringLiteral, + ReservedIdentifierOrStringLiteral, + CatalogQualification, + SchemaQualification, + ReservedSchemaQualification, + TableQualification, + ReservedTableQualification, + CreateTableColumnList, + CreateTableColumnElement, + CreateTableColumnDefinition, + CreateTableConstraint, + ColumnDefinition, + ColumnConstraint, + NameableColumnConstraint, + ColumnConstraintElement, + NotNullConstraint, + NullConstraint, + NotNullColumnConstraint, + UniqueConstraint, + PrimaryKeyConstraint, + ConstraintTiming, + ImmediateConstraint, + DeferredConstraint, + DefaultValue, + CheckConstraint, + ForeignKeyConstraint, + ColumnCollation, + ColumnCompression, + KeyActions, + UpdateFirstKeyActions, + DeleteFirstKeyActions, + UpdateAction, + DeleteAction, + KeyAction, + NoKeyAction, + RestrictKeyAction, + CascadeKeyAction, + SetNullKeyAction, + SetDefaultKeyAction, + TopLevelConstraint, + TopLevelConstraintList, + ConstraintNameClause, + TopCheckConstraint, + TopPrimaryKeyConstraint, + TopUniqueConstraint, + TopForeignKeyConstraint, + ColumnIdList, + PlainIdentifier, + QuotedIdentifier, + DottedIdentifier, + DotColLabel, + Identifier, + ColId, + ColIdOrString, + TypeFuncName, + TypeFuncKeyword, + TypeName, + ColLabel, + ColLabelOrString, + ColLabelIdentifier, + BareColLabel, + StringLiteralIdentifier, + GeneratedColumn, + Generated, + GeneratedAlways, + GeneratedByDefault, + GeneratedColumnType, + CommitAction, + PreserveOrDelete, + PreserveRows, + DeleteRows, + VirtualGeneratedColumn, + StoredGeneratedColumn, + CreateTriggerStmt, + TriggerBody, + TriggerName, + ReferencingClause, + ReferencingItem, + ReferencingNewTableAs, + ReferencingOldTableAs, + TriggerTiming, + TriggerBefore, + TriggerAfter, + TriggerInsteadOf, + TriggerEvent, + TriggerEventInsert, + TriggerEventDelete, + TriggerEventUpdate, + TriggerEventUpdateOf, + TriggerColumnList, + ForEachClause, + ForEachRow, + ForEachStatement, + CreateTypeStmt, + CreateType, + CreateTypeFromType, + EnumSelectType, + EnumStringLiteralList, + CreateViewStmt, + CreateRecursive, + CreateSecure, + DeallocateStatement, + DeallocatePrepare, + DeleteStatement, + TruncateStatement, + TruncateOptions, + TruncateAliasOptions, + TruncateDropBehavior, + TargetOptAlias, + TargetAlias, + DeleteUsingClause, + DescribeStatement, + ShowDeprecatedSelect, + DescribeSelect, + ShowAllTables, + ShowTables, + ShowByName, + DescribeByName, + DescribeOrSummarize, + ShowTarget, + ShowDeprecatedQualifiedTableName, + ShowSettingName, + ShowSpecialForm, + DescribeTarget, + DescribeBaseTableName, + DescribeStringLiteral, + Summarize, + SummarizeRule, + ShowOrDescribe, + ShowRule, + DescribeRule, + DescribeLongRule, + DescRule, + DetachStatement, + DropStatement, + DropEntries, + DropTrigger, + DropTable, + DropTableFunction, + DropFunction, + DropSchema, + DropIndex, + QualifiedIndexName, + QualifiedIndexNameString, + SchemaReservedIndex, + CatalogReservedSchemaIndex, + DropSequence, + DropCollation, + DropType, + DropSecret, + TableOrView, + MaterializedViewEntry, + FunctionTypeMacro, + FunctionTypeMacroKeyword, + FunctionTypeFunction, + DropBehavior, + CascadeDropBehavior, + RestrictDropBehavior, + IfExists, + DropSecretStorage, + ExecuteStatement, + ExplainStatement, + ExplainOptionList, + ExplainOption, + ExplainOptionName, + ExplainSelectStatement, + ExplainableStatements, + ExportStatement, + ExportSource, + ImportStatement, + ColumnReference, + NestedSchemaTableColumnName, + CatalogReservedSchemaTableColumnName, + SchemaReservedTableColumnName, + TableReservedColumnName, + FunctionExpression, + FunctionExpressionArguments, + FunctionExpressionArgumentList, + FunctionArgumentList, + FunctionIdentifier, + FunctionNameAsQualifiedName, + CatalogReservedSchemaFunctionName, + SchemaReservedFunctionName, + DistinctOrAll, + DistinctKeyword, + AllKeyword, + ExportClause, + WithinGroupClause, + FilterClause, + FilterClauseExpression, + FilterClauseContents, + IgnoreOrRespectNulls, + IgnoreNulls, + RespectNulls, + ParenthesisExpression, + LiteralExpression, + ConstantLiteral, + NullLiteral, + TrueLiteral, + FalseLiteral, + CastExpression, + CastArguments, + CastOrTryCast, + CastKeyword, + TryCastKeyword, + ColIdDot, + StarExpression, + StarQualifierList, + ExcludeList, + ExcludeOrExcept, + ExcludeNames, + ExcludeNameList, + ExcludeNameSingle, + ExcludeName, + ExcludeDottedName, + ExcludeColumnName, + ReplaceList, + ReplaceEntries, + ReplaceEntrySingle, + ReplaceEntryList, + ReplaceEntry, + RenameList, + RenameEntries, + RenameEntryList, + SingleRenameEntry, + RenameEntry, + SubqueryExpression, + SubqueryNot, + SubqueryExists, + CaseExpression, + CaseWhenThen, + CaseElse, + TypeLiteral, + IntervalLiteral, + IntervalParameter, + IntervalStringParameter, + FrameClause, + Framing, + RowsFraming, + RangeFraming, + GroupsFraming, + FrameExtent, + SingleFrameExtent, + BetweenFrameExtent, + FrameBound, + FrameUnbounded, + FrameExpression, + FrameCurrentRow, + PrecedingOrFollowing, + PrecedingFrame, + FollowingFrame, + WindowExcludeClause, + WindowExcludeElement, + ExcludeCurrentRow, + ExcludeGroup, + ExcludeTies, + ExcludeNoOthers, + OverClause, + WindowFrame, + IdentifierWindowFrame, + ParensIdentifier, + WindowFrameDefinition, + WindowFrameNameContentsParens, + WindowFrameNameContents, + WindowFrameContentsParens, + WindowFrameContents, + BaseWindowName, + WindowPartition, + ListExpression, + ArrayBoundedListExpression, + ArrayParensSelect, + BoundedListExpression, + StructExpression, + StructField, + MapExpression, + MapStructExpression, + MapStructField, + GroupingExpression, + GroupingOrGroupingId, + GroupingKeyword, + GroupingIdKeyword, + Parameter, + QuestionMarkNumberedParameter, + AnonymousParameter, + NumberedParameter, + ColLabelParameter, + PositionalExpression, + DefaultExpression, + ListComprehensionExpression, + ListComprehensionFilter, + ParensExpression, + MeasureSemanticsExpression, + SingleExpression, + Expression, + ColumnDefaultExpr, + LambdaArrowExpression, + SingleArrowPair, + LogicalOrExpression, + LogicalOrExpressionTail, + ColDefOrExpr, + ColDefOrExpressionTail, + LogicalAndExpression, + LogicalAndExpressionTail, + ColDefAndExpr, + ColDefAndExpressionTail, + LogicalNotExpression, + NotExpression, + NotKeyword, + IsExpression, + IsExpressionContinuation, + IsExpressionTail, + IsTestTail, + IsDistinctTail, + IsComparisonTail, + IsOtherOperatorTail, + IsCastTail, + IsTest, + IsLiteral, + IsLiteralValue, + UnknownLiteral, + NotNull, + NotNullKeyword, + NotNullOperator, + IsNull, + IsNullOperator, + IsDistinctFromExpression, + IsDistinctFromTail, + IsDistinctFromOp, + ComparisonExpression, + ComparisonExpressionTail, + ComparisonOperand, + NotComparisonOperand, + ComparisonOperator, + OperatorEqual, + OperatorNotEqual, + OperatorLessThan, + OperatorGreaterThan, + OperatorLessThanEquals, + OperatorGreaterThanEquals, + BetweenInLikeExpression, + InPredicate, + BetweenLikeOp, + BetweenLikeOpExpression, + LikeClause, + EscapeClause, + LikeVariations, + LikeToken, + ILikeToken, + GlobToken, + SimilarToToken, + RegexMatchToken, + RegexInsensitiveMatchToken, + NotILikeOp, + NotLikeOp, + NotRegexInsensitiveMatchOp, + NotSimilarToOp, + InClause, + InExpression, + InContainsExpression, + InExpressionList, + InSelectStatement, + BetweenClause, + Asymmetric, + OtherOperatorExpression, + InfixOtherOperatorExpression, + CustomPrefixExpression, + OtherOperatorTail, + OtherOperator, + AnyAllParsedOperator, + NamedOtherOperator, + OperatorLiteral, + AnyOperatorLiteral, + AnyAllOperator, + AnyOrAll, + SubqueryAny, + SubqueryAll, + QualifiedOperator, + QualifiedOperatorContents, + BitwiseExpression, + BitwiseExpressionTail, + BitOperator, + TildeExpression, + AdditiveExpression, + AdditiveExpressionTail, + Term, + MultiplicativeExpression, + MultiplicativeExpressionTail, + Factor, + ExponentiationExpression, + ExponentiationExpressionTail, + ExponentOperator, + CollateExpression, + CollateExpressionTail, + CollateOperator, + AtTimeZoneExpression, + AtTimeZoneExpressionTail, + AtTimeZoneOperator, + PrefixExpression, + PrefixOperator, + MinusPrefixOperator, + PlusPrefixOperator, + TildePrefixOperator, + BaseExpression, + IndirectionList, + Indirection, + CastOperator, + DotOperator, + DotMethodOperator, + DotColumnOperator, + MethodExpression, + MethodExpressionArguments, + MethodExpressionArgumentList, + MethodFunctionArguments, + SliceExpression, + SliceBound, + SliceUpperBound, + MinusStepSliceBound, + EndStepSliceBound, + EndSliceBound, + StepSliceBound, + PostfixOperator, + SpecialFunctionExpression, + CoalesceExpression, + UnpackExpression, + TryExpression, + ColumnsExpression, + ExtractExpression, + ExtractArguments, + LambdaExpression, + NullIfExpression, + NullIfArguments, + PositionExpression, + PositionArguments, + RowExpression, + SubstringExpression, + SubstringArguments, + SubstringExpressionList, + SubstringParameters, + SubstringFromFor, + SubstringFromOptionalFor, + SubstringFor, + TrimExpression, + TrimArguments, + TrimDirection, + TrimBoth, + TrimLeading, + TrimTrailing, + TrimSource, + OverlayExpression, + OverlayArguments, + OverlayParameters, + FromExpression, + ForExpression, + OverlayExpressionList, + ExtractArgument, + ExtractDatePartArgument, + ExtractIdentifierArgument, + ExtractStringArgument, + ExtractDatePart, + ExternalResourceStatement, + CreateExternalResourceStmt, + RegisterExternalResourceStmt, + DestroyExternalResourceStmt, + ShowExternalResourcesStmt, + ShowAllModifier, + ExternalResourceKeyword, + ExternalResourceCreationOptions, + AttachToExternalResource, + ConnectToExternalResource, + ExternalResourceSource, + ExternalResourceCreateClause, + ExternalResourceReferenceClause, + InsertStatement, + OrAction, + InsertOrReplace, + InsertOrIgnore, + ByNameOrPosition, + InsertByNameOrder, + InsertByPositionOrder, + InsertByName, + InsertByPosition, + OverridingClause, + OverridingScope, + InsertTarget, + InsertAlias, + ColumnList, + InsertColumnList, + InsertValues, + SelectInsertValues, + DefaultValues, + OnConflictClause, + OnConflictTarget, + OnConflictExpressionTarget, + OnConflictIndexTarget, + OnConflictAction, + OnConflictUpdate, + OnConflictNothing, + ReturningClause, + LoadStatement, + ExtensionAlias, + InstallStatement, + InstallAndLoad, + UpdateExtensionsStatement, + FromSource, + FromSourceIdentifier, + FromSourceString, + VersionNumber, + ExtensionRepositoryStatement, + CreateExtensionRepositoryStmt, + RepositoryPrefix, + RepositoryPublicKey, + DropExtensionRepositoryStmt, + TableMatchRecognizeClause, + MatchRecognizeBody, + MatchRecognizeClause, + MRPartition, + MROrderBy, + MRMeasures, + MRRows, + MRSkip, + MRPattern, + MRSubset, + MRDefineAuto, + MRDefine, + MeasuresClause, + MeasuresElement, + MeasureSemantics, + RunningSemantics, + FinalSemantics, + RowsPerMatch, + OneRowPerMatch, + AllRowsPerMatch, + AllRowsEmptyMatches, + ShowEmptyMatches, + OmitEmptyMatches, + WithUnmatchedRows, + AfterMatchSkip, + AfterMatchSkipTarget, + SkipToNextRow, + SkipPastLastRow, + SkipToFirstVar, + SkipToLastVar, + SkipToVar, + PatternClause, + RowPattern, + RowPatternAlternative, + RowPatternTerm, + RowPatternFactor, + RowPatternPrimary, + RowPatternPermute, + RowPatternAnchor, + PatternStart, + PatternEnd, + RowPatternGroup, + RowPatternEmpty, + RowPatternExclusion, + RowPatternLabel, + RowPatternQuantifier, + RowPatternQuantifierKind, + QuantifierReluctant, + QuantifierStar, + QuantifierPlus, + QuantifierOptional, + QuantifierRange, + QuantifierBounds, + QuantifierMinMax, + QuantifierMin, + QuantifierMax, + QuantifierExact, + SubsetClause, + SubsetElement, + DefineClause, + DefineElement, + MergeIntoStatement, + MergeIntoUsingClause, + MergeMatch, + MatchedClause, + MatchedClauseAction, + UpdateMatchClause, + UpdateMatchInfo, + UpdateMatchSetAction, + UpdateByNameOrPosition, + DeleteMatchClause, + InsertMatchClause, + InsertMatchInfo, + InsertDefaultValues, + InsertByNameOrPosition, + InsertValuesList, + DoNothingMatchClause, + ErrorMatchClause, + UpdateMatchSetClause, + UpdateMatchSetInfo, + AndExpression, + NotMatchedClause, + BySourceOrTarget, + BySource, + ByTarget, + PivotStatement, + PivotOn, + PivotUsing, + PivotColumnList, + PivotColumnEntry, + PivotColumnExpression, + PivotColumnSubquery, + PivotKeyword, + UnpivotKeyword, + UnpivotStatement, + IntoNameValues, + ValueOrValues, + OptionalParensNameList, + ParenthesizedNameList, + BareNameList, + IncludeOrExcludeNulls, + IncludeNulls, + ExcludeNulls, + UnpivotHeader, + UnpivotHeaderSingle, + UnpivotHeaderList, + PragmaStatement, + PragmaAssignOrFunction, + PragmaAssign, + PragmaFunction, + PragmaParameters, + PrepareStatement, + TypeList, + SelectStatement, + SelectStatementInternal, + SelectSetOpChain, + SelectSetOpChainTail, + IntersectChain, + IntersectChainTail, + SetIntersectClause, + SelectAtom, + SelectParens, + SetopClause, + SetopType, + SetopUnion, + SetopExcept, + ByName, + SelectStatementType, + ResultModifiers, + LimitOffset, + LimitOffsetClause, + OffsetLimitClause, + OffsetFetchClause, + FetchOffsetClause, + FetchOnlyClause, + TableStatement, + OptionalParensSimpleSelect, + SimpleSelectParens, + SimpleSelect, + SelectFrom, + SelectFromClause, + FromSelectClause, + WithStatement, + CTEBody, + CTESelectBody, + CTEDMLBody, + UsingKey, + Materialized, + WithClause, + Recursive, + SelectClause, + TargetList, + ColumnAliases, + DistinctClause, + DistinctAll, + DistinctOn, + DistinctOnTargets, + InnerTableRef, + TableRef, + TableSubquery, + BaseTableRef, + TableAliasColon, + ValuesRef, + ParensTableRef, + JoinOrPivot, + TablePivotClause, + TablePivotClauseBody, + PivotGroupByList, + TableUnpivotClause, + TableUnpivotClauseBody, + PivotHeader, + PivotValueList, + PivotValueTarget, + PivotEnumTarget, + PivotListTarget, + UnpivotValueList, + PivotTargetList, + UnpivotTargetList, + Lateral, + BaseTableName, + UnqualifiedBaseTableName, + QualifiedTableName, + SchemaReservedTable, + CatalogReservedSchemaTable, + TableFunction, + TableFunctionLateralOpt, + TableFunctionAliasColon, + WithOrdinality, + QualifiedTableFunction, + TableFunctionArguments, + FunctionArgument, + NamedFunctionArgument, + PositionalFunctionArgument, + NamedParameter, + NamedParameterAssignment, + TableAlias, + TableAliasAs, + TableAliasWithoutAs, + AtClause, + AtSpecifier, + AtUnit, + VersionAtUnit, + TimestampAtUnit, + JoinClause, + NearestJoinClause, + NearestJoinAliased, + NearestJoinBare, + NearestBareTableRef, + NearestValuesRef, + NearestTableFunction, + NearestTableSubquery, + NearestBaseTableRef, + NearestParensTableRef, + ApproxOrExact, + NearestApprox, + NearestExact, + DistanceOrSimilarity, + NearestDistance, + NearestSimilarity, + RegularJoinClause, + JoinByClause, + Asof, + JoinWithoutOnClause, + JoinQualifier, + OnClause, + UsingClause, + UsingColumnName, + JoinType, + JoinPrefix, + CrossJoinPrefix, + NaturalJoinPrefix, + PositionalJoinPrefix, + FullJoin, + LeftJoin, + RightJoin, + SemiJoin, + AntiJoin, + InnerJoin, + FromClause, + WhereClause, + GroupByClause, + HavingClause, + QualifyClause, + SampleClause, + SelectSampleClause, + UsingSample, + TableSample, + WindowClause, + WindowDefinition, + SampleEntry, + SampleEntryCount, + SampleEntryFunction, + SampleFunction, + SampleProperties, + RepeatableSample, + SampleSeed, + SampleCount, + SampleValue, + SampleUnit, + SamplePercentage, + SampleRows, + GroupByExpressions, + GroupByAll, + GroupByList, + GroupByExpression, + GroupByBaseExpression, + EmptyGroupingItem, + CubeOrRollupClause, + CubeOrRollup, + CubeKeyword, + RollupKeyword, + GroupingSetsClause, + SubqueryReference, + OrderByExpression, + DescOrAsc, + DescendingOrder, + AscendingOrder, + NullsFirstOrLast, + NullsFirst, + NullsLast, + OrderByClause, + OrderByExpressions, + OrderByExpressionList, + OrderByAll, + LimitClause, + OffsetClause, + OffsetValue, + RowOrRows, + LimitValue, + LimitAll, + LimitLiteralPercent, + LimitExpression, + FetchClause, + FetchClauseWithoutValue, + FetchClauseWithValue, + FirstOrNext, + FetchValue, + AliasedExpression, + ColIdExpression, + ExpressionAsCollabel, + ExpressionOptIdentifier, + ValuesClause, + ValuesExpressions, + SetStatement, + SetAssignmentOrTimeZone, + ResetStatement, + SetSchema, + StandardAssignment, + SetVariableOrSetting, + SetTimeZone, + ZoneValue, + ZoneLocal, + ZoneDefault, + ZoneStringLiteral, + ZoneIdentifier, + ZoneIntervalWithInterval, + ZoneIntervalWithPrecision, + SetSetting, + SetVariable, + VariableScope, + SettingScope, + LocalScope, + SessionScope, + GlobalScope, + SetAssignment, + VariableAssign, + VariableList, + TransactionStatement, + BeginTransaction, + RollbackTransaction, + CommitTransaction, + StartOrBegin, + Transaction, + ReadOrWrite, + ReadOnlyOrReadWrite, + ReadOnly, + ReadWrite, + AbortOrRollback, + CommitOrEnd, + UpdateStatement, + UpdateTarget, + BaseTableSet, + BaseTableAliasSet, + UpdateAlias, + UpdateSetClauseList, + UpdateSetClause, + UpdateSetTuple, + UpdateSetElement, + UpdateSetColumnTarget, + UseStatement, + UseTarget, + SchemaNameAsUseTarget, + CatalogNameAsUseTarget, + UseTargetCatalogSchema, + DotIdentifier, + VacuumStatement, + VacuumOptions, + VacuumParensOptions, + VacuumLegacyOptions, + VacuumOption, + OptAnalyze, + OptFull, + OptFreeze, + OptVerbose, + NameList, + BareLabelKeyword, + ColumnNameKeyword, + FuncNameKeyword, + ReservedKeyword, + TypeNameKeyword, + UnreservedKeyword, + BUILTIN_COUNT +}; + +//! Returns the rule name for a built-in rule ID, or nullptr for INVALID and runtime-allocated IDs +const char *PEGRuleIdToString(PEGRuleId rule_id); +//! Returns the built-in rule ID for a rule name (case-insensitive), or INVALID if the rule is not built in +PEGRuleId PEGRuleIdFromString(const string &rule_name); + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parser/peg/transformer/parse_result.hpp b/src/duckdb/src/include/duckdb/parser/peg/transformer/parse_result.hpp index 215848489..7db569197 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/transformer/parse_result.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/transformer/parse_result.hpp @@ -122,7 +122,8 @@ inline const char *ParseResultToString(ParseResultType type) { class ParseResult { public: explicit ParseResult(ParseResultType type, optional_idx offset, optional_idx length = optional_idx()) - : type(type), offset(offset), length(length) { + : type(type), has_offset(offset.IsValid()), has_length(length.IsValid()), + offset(offset.IsValid() ? offset.GetIndex() : 0), length(length.IsValid() ? length.GetIndex() : 0) { } virtual ~ParseResult() = default; @@ -138,14 +139,11 @@ class ParseResult { return reinterpret_cast(*this); } - ParseResultType type; optional_ptr rule; + ParseResultType type; //! Set when a collapsible rule handed this result out in place of its own, so the transformer runs this //! result's rule rather than the one the parent asked for bool collapsed = false; - optional_idx offset; - //! Source length: for leaf tokens the token length; for composite results the enclosing extent of children - optional_idx length; void SetRule(const CompiledGrammarRule &rule_p) { rule = rule_p; @@ -167,23 +165,32 @@ class ParseResult { return rule; } + optional_idx GetOffset() const { + return has_offset ? optional_idx(offset) : optional_idx(); + } + + optional_idx GetLength() const { + return has_length ? optional_idx(length) : optional_idx(); + } + //! Returns the source location [offset, offset+length) of this parse result (length 0 when unknown) QueryLocation GetLocation() const { - if (!offset.IsValid()) { + if (!has_offset) { return QueryLocation(); } - return QueryLocation(offset.GetIndex(), length.IsValid() ? length.GetIndex() : 0); + return QueryLocation(offset, has_length ? length : 0); } //! Grow this result's location so it encloses the given child's location (used to build composite //! locations bottom-up) void EncloseChild(const ParseResult &child) { auto child_location = child.GetLocation(); - if (!offset.IsValid() || !child_location.IsValid()) { + if (!has_offset || !child_location.IsValid()) { return; } auto enclosing = GetLocation().Merge(child_location); length = enclosing.length; + has_length = true; } virtual void ToStringInternal(std::stringstream &ss, std::unordered_set &visited, @@ -204,6 +211,12 @@ class ParseResult { } private: + //! Keep validity flags with the small metadata fields, without narrowing source positions. + bool has_offset; + bool has_length; + idx_t offset; + //! For leaf tokens the token length; for composite results the enclosing extent of children. + idx_t length; const string *name = nullptr; }; diff --git a/src/duckdb/src/include/duckdb/parser/peg/transformer/peg_transformer.hpp b/src/duckdb/src/include/duckdb/parser/peg/transformer/peg_transformer.hpp index db0bc8f28..b13fc8370 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/transformer/peg_transformer.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/transformer/peg_transformer.hpp @@ -413,14 +413,10 @@ struct TransformStackFrame { arena_ptr child_result; //! How much this frame adds to the expression depth while it is on the stack idx_t expression_depth_weight = 0; + //! Rewind process storage to this point after destroying the frame. + ArenaAllocator::Position process_arena_position; }; -#ifdef DEBUG -using frame_stack_t = InspectableStack; -#else -using frame_stack_t = stack; -#endif - class TransformStack { public: explicit TransformStack(PEGTransformer &transformer); @@ -449,17 +445,19 @@ class TransformStack { private: PEGTransformer &transformer; - frame_stack_t frames; + vector frames; }; class PEGTransformer { public: PEGTransformer(ArenaAllocator &allocator, TokenIterator &token_iterator, ParserOptions &options_p, const CompiledGrammar &grammar_p) - : allocator(allocator), token_iterator(token_iterator), options(options_p), grammar(grammar_p) { + : allocator(allocator), token_iterator(token_iterator), options(options_p), grammar(grammar_p), + process_allocator(allocator.GetUnderlyingAllocator()) { } const CompiledGrammarRule &GetRule(const string &rule_name) const; + const CompiledGrammarRule &GetRule(PEGRuleId rule_id) const; public: template @@ -510,10 +508,10 @@ class PEGTransformer { return allocator.Make(std::forward(args)...); } - //! Transform processes live exactly as long as one transform run, so they are carved out of the same arena + //! Process storage is reclaimed after the transform frame completes. template arena_ptr MakeProcess(Args &&...args) { - return arena_ptr(allocator.Make(std::forward(args)...)); + return arena_ptr(process_allocator.Make(std::forward(args)...)); } //! The result a transformer hands back, carved out of the same arena @@ -604,6 +602,9 @@ class PEGTransformer { ParserOptions options; const CompiledGrammar &grammar; +private: + ArenaAllocator process_allocator; + private: friend class GeneratedTransformProcess; friend class FinalizeTransformProcess; diff --git a/src/duckdb/src/include/duckdb/parser/peg/transformer/transform_result.hpp b/src/duckdb/src/include/duckdb/parser/peg/transformer/transform_result.hpp index a97eca852..d49ec912d 100644 --- a/src/duckdb/src/include/duckdb/parser/peg/transformer/transform_result.hpp +++ b/src/duckdb/src/include/duckdb/parser/peg/transformer/transform_result.hpp @@ -27,24 +27,45 @@ struct TransformResultTypeIdentifier { //! A stable per-type name, used to identify transform results across loadable extension boundaries without RTTI. template -const char *TransformResultTypeName() { +constexpr const char *TransformResultTypeName() { return TransformResultTypeIdentifier::GetName(); } +//! FNV-1a hash of a type name, computed at compile time +constexpr uint64_t TransformResultTypeNameHash(const char *name) { + uint64_t hash = 14695981039346656037ULL; + for (; *name; name++) { + hash = (hash ^ static_cast(static_cast(*name))) * 1099511628211ULL; + } + return hash; +} + +template +constexpr uint64_t TransformResultTypeHash() { + return TransformResultTypeNameHash(TransformResultTypeName()); +} + struct DUCKDB_API TransformResultValue { + explicit TransformResultValue(uint64_t type_hash_p) : type_hash(type_hash_p) { + } virtual ~TransformResultValue() = default; //! Returns a pointer to the value if its type matches, without relying on RTTI virtual void *GetValuePointer(const char *type_name) = 0; + + //! Hash of the registered type name, so mismatching types are rejected without a virtual call or string compare + uint64_t type_hash; }; template struct DUCKDB_API TypedTransformResult : public TransformResultValue { - explicit TypedTransformResult(T value_p) : value(std::move(value_p)) { + explicit TypedTransformResult(T value_p) : TransformResultValue(TYPE_HASH), value(std::move(value_p)) { } TypedTransformResult(const TypedTransformResult &) = delete; TypedTransformResult &operator=(const TypedTransformResult &) = delete; + static constexpr uint64_t TYPE_HASH = TransformResultTypeHash(); + void *GetValuePointer(const char *type_name) override { auto expected = TransformResultTypeName(); return type_name == expected || std::strcmp(type_name, expected) == 0 ? &value : nullptr; @@ -56,6 +77,9 @@ struct DUCKDB_API TypedTransformResult : public TransformResultValue { //! Returns a pointer to the contained value if the result holds exactly T, and nullptr otherwise template T *TryGetTransformResult(TransformResultValue &result) { + if (result.type_hash != TransformResultTypeHash()) { + return nullptr; + } return reinterpret_cast(result.GetValuePointer(TransformResultTypeName())); } diff --git a/src/duckdb/src/include/duckdb/storage/arena_allocator.hpp b/src/duckdb/src/include/duckdb/storage/arena_allocator.hpp index dd3c40b24..41aad386b 100644 --- a/src/duckdb/src/include/duckdb/storage/arena_allocator.hpp +++ b/src/duckdb/src/include/duckdb/storage/arena_allocator.hpp @@ -13,12 +13,36 @@ #include "duckdb/common/types/string.hpp" #include "duckdb/common/arena_containers/arena_ptr.hpp" +#if defined(__SANITIZE_ADDRESS__) +#define DUCKDB_ARENA_ALLOCATOR_ASAN +#elif defined(__has_feature) +#if __has_feature(address_sanitizer) +#define DUCKDB_ARENA_ALLOCATOR_ASAN +#endif +#endif + +#ifdef DUCKDB_ARENA_ALLOCATOR_ASAN +#include +#endif + namespace duckdb { struct ArenaChunk { ArenaChunk(Allocator &allocator, idx_t size); ~ArenaChunk(); + void ResetPosition(idx_t position) { + D_ASSERT(position <= current_position); +#ifdef DUCKDB_ARENA_ALLOCATOR_ASAN + // Discarded arena containers can leave their unused capacity poisoned. + const auto end = MinValue(current_position, maximum_size); + if (position < end) { + __asan_unpoison_memory_region(data.get() + position, end - position); + } +#endif + current_position = position; + } + AllocatedData data; idx_t current_position; idx_t maximum_size; @@ -57,7 +81,7 @@ class ArenaAllocator { //! Note that we can ONLY safely call this method if Allocate has been called previously with a size >= shrink_size DUCKDB_API void ShrinkHead(idx_t shrink_size) const { D_ASSERT(head && head->current_position >= shrink_size); - head->current_position -= shrink_size; + head->ResetPosition(head->current_position - shrink_size); } //! A position in the arena - everything allocated after it was taken can be released again with Rewind diff --git a/src/duckdb/src/include/duckdb/storage/block_allocator.hpp b/src/duckdb/src/include/duckdb/storage/block_allocator.hpp index 329c24704..bf3fd3987 100644 --- a/src/duckdb/src/include/duckdb/storage/block_allocator.hpp +++ b/src/duckdb/src/include/duckdb/storage/block_allocator.hpp @@ -8,10 +8,12 @@ #pragma once +#include "duckdb/common/array.hpp" #include "duckdb/common/atomic.hpp" #include "duckdb/common/hugeint.hpp" #include "duckdb/common/mutex.hpp" #include "duckdb/common/optional_idx.hpp" +#include "duckdb/common/optional_ptr.hpp" #include "duckdb/common/shared_ptr.hpp" #include "duckdb/common/typedefs.hpp" #include "duckdb/common/unique_ptr.hpp" @@ -19,12 +21,20 @@ namespace duckdb { class Allocator; +class AllocationCache; class AttachedDatabase; class DatabaseInstance; +class TaskScheduler; +struct ProducerToken; struct BlockQueue; +struct BlockAllocatorLifetimeState; class BlockAllocator { friend class BlockAllocatorThreadLocalState; + friend class BlockAllocatorFlushTask; + friend class BlockAllocatorTestHelper; + friend class BufferAllocator; + friend class TaskScheduler; public: BlockAllocator(Allocator &allocator, idx_t block_size, idx_t virtual_memory_size, idx_t physical_memory_size); @@ -35,23 +45,41 @@ class BlockAllocator { static BlockAllocator &Get(AttachedDatabase &db); //! Resize physical memory (can only be increased) - void Resize(idx_t new_physical_memory_size); + void Resize(idx_t new_physical_memory_size) DUCKDB_EXCLUDES(physical_memory_lock); //! Allocation functions (same API as Allocator) data_ptr_t AllocateData(idx_t size) const; void FreeData(data_ptr_t pointer, idx_t size) const; data_ptr_t ReallocateData(data_ptr_t pointer, idx_t old_size, idx_t new_size) const; + //! Free backing retained in global queues, thread-local caches, and ongoing reclamation. + idx_t GetCachedMemory() const; + //! Flush outstanding allocations bool SupportsFlush() const; + optional_idx DecayDelay() const; void ThreadFlush(bool allocator_background_threads, idx_t threshold, idx_t thread_count) const; - void FlushAll(optional_idx extra_memory = optional_idx()) const; - //! Estimated memory freed but still held by the allocator - idx_t GetEstimatedUnflushedMemory() const; - //! Flush if at least threshold bytes were freed since the last reclamation check, returns whether a flush happened - bool TryFlushDeallocated(idx_t threshold, idx_t memory_limit, idx_t used_memory) const; + //! Apply idle decay through the bound scheduler; standalone allocators reclaim synchronously. + void ThreadIdle() const DUCKDB_EXCLUDES(flush_lock); + //! Return local blocks without scheduling or discarding pool backing. + void ThreadExit() const; + //! Best-effort reclamation of all cached backing and fallback allocations. + void FlushAll() const noexcept DUCKDB_EXCLUDES(flush_lock); + //! Leave pool backing for unmapping and release cached fallback allocations. + void FlushOnShutdown() const noexcept DUCKDB_EXCLUDES(flush_lock); + //! Retain cached backing that fits alongside the buffer manager's live reservations. + void FlushForAllocation(idx_t extra_memory, idx_t memory_headroom) const noexcept DUCKDB_EXCLUDES(flush_lock); + //! Trim cached backing, then purge accumulated fallback frees that do not fit in the headroom. + bool TryFlushDeallocated(idx_t threshold, idx_t memory_headroom) const noexcept DUCKDB_EXCLUDES(flush_lock); private: + //! The buffer allocator's huge-page backend keeps matching allocation and release ownership. + data_ptr_t AllocateHugeData(idx_t size) const; + void FreeHugeData(data_ptr_t pointer, idx_t size) const; + + enum class FlushState : uint8_t { IDLE, SCHEDULED, RESCHEDULE_REQUESTED }; + enum class ReclaimMode : uint8_t { FORCE, DECAY, OPPORTUNISTIC }; + bool IsActive() const; bool IsEnabled() const; bool IsInPool(data_ptr_t pointer) const; @@ -64,16 +92,44 @@ class BlockAllocator { void VerifyBlockID(uint32_t block_id) const; - void FreeInternal(idx_t extra_memory) const; - void FreeContiguousBlocks(uint32_t block_id_start, uint32_t block_id_end_including) const; + void InitializeRetention(idx_t now_ms) const; + void AddRetention(idx_t count, idx_t now_ms) const; + void ReuseRetention(idx_t count, idx_t now_ms) const; + idx_t RetentionTarget(idx_t now_ms) const; + void UpdateCachedBlocks(int64_t count, idx_t slot) const; + idx_t GetCachedPoolMemory() const; + void ReturnThreadLocalBlocks() const; + void FlushFallbackAllocator(idx_t deallocated) const noexcept; + //! Return bytes released to the fallback allocator, which may still need a purge. + idx_t TrimCachedMemory(idx_t memory_headroom, optional_idx pool_block_limit = optional_idx()) const noexcept + DUCKDB_EXCLUDES(flush_lock); + idx_t FlushAllocationCache(idx_t byte_limit, ReclaimMode mode = ReclaimMode::FORCE) const noexcept + DUCKDB_EXCLUDES(flush_lock); + bool HasReclaimableMemory() const DUCKDB_REQUIRES(flush_lock); + bool HasPendingCacheDecay() const; + + void SetScheduler(TaskScheduler &scheduler); + void ClearScheduler(); + + bool TryScheduleFlush(TaskScheduler &scheduler) const DUCKDB_EXCLUDES(flush_lock); + idx_t GetFreeBlockCount() const DUCKDB_REQUIRES(flush_lock); + idx_t GetReclaimableBlockCount(ReclaimMode mode, optional_idx cache_limit = optional_idx()) const + DUCKDB_REQUIRES(flush_lock); + //! Return the unprocessed portion of the initial free-block budget. + idx_t FlushPool(optional_idx block_limit = optional_idx(), optional_idx task_limit = optional_idx(), + ReclaimMode mode = ReclaimMode::FORCE, optional_idx cache_limit = optional_idx()) const noexcept + DUCKDB_EXCLUDES(flush_lock); + idx_t FreeInternal(optional_idx block_limit, optional_idx task_limit, ReclaimMode mode, + optional_idx cache_limit = optional_idx()) const DUCKDB_EXCLUDES(flush_lock); + bool FreeContiguousBlocks(uint32_t block_id_start, uint32_t block_id_end_including) const; private: //! Identifier const hugeint_t uuid; //! Fallback allocator Allocator &allocator; - //! Estimated memory freed but still held by the allocator - mutable atomic estimated_unflushed_memory {0}; + //! Cache of whole power-of-two fallback allocations, with preferred CPU-local reuse. + unique_ptr allocation_cache; //! Block size (power of two) const idx_t block_size; @@ -86,7 +142,7 @@ class BlockAllocator { atomic virtual_memory_space; //! Mutex for modifying physical memory size - mutex physical_memory_lock; + annotated_mutex physical_memory_lock; //! Size of the physical memory atomic physical_memory_size; @@ -95,8 +151,31 @@ class BlockAllocator { //! Touched by block IDs unsafe_unique_ptr touched; - //! Token used to indicate whether current BlockAllocator is alive. - shared_ptr> alive_token; + struct alignas(64) CachedBlockCount { + //! Cross-thread reuse can make individual balances negative. + atomic count {0}; + }; + //! Separate writers so each block reuse does not contend on a single counter. + mutable array cached_blocks; + mutable atomic next_cache_slot {0}; + //! Still charged to cached_blocks until discard succeeds. + mutable idx_t reclaiming_blocks DUCKDB_GUARDED_BY(flush_lock) = 0; + + //! Synchronizes returning cached blocks with allocator destruction. + shared_ptr lifetime_state; + + //! Approximate retention volume, with a generation and count packed into each bucket. + mutable array, 3> retention_buckets; + //! Protect maintenance decisions and background scheduling. + mutable annotated_mutex flush_lock; + //! Scheduler queue destruction invalidates this token before allocator destruction. + mutable unique_ptr flush_producer DUCKDB_GUARDED_BY(flush_lock); + mutable FlushState flush_state DUCKDB_GUARDED_BY(flush_lock) = FlushState::IDLE; + //! Bound before workers start and cleared after they join; never owns the scheduler. + optional_ptr scheduler; + + //! Estimated memory freed but still held by the fallback allocator, separate from pooled allocations. + alignas(64) mutable atomic estimated_unflushed_memory {0}; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/storage/buffer/buffer_pool.hpp b/src/duckdb/src/include/duckdb/storage/buffer/buffer_pool.hpp index 3a04f77f5..b2455e3d8 100644 --- a/src/duckdb/src/include/duckdb/storage/buffer/buffer_pool.hpp +++ b/src/duckdb/src/include/duckdb/storage/buffer/buffer_pool.hpp @@ -97,13 +97,8 @@ class BufferPool { //! Evict object cache entries if needed. EvictionResult EvictObjectCacheEntries(MemoryTag tag, idx_t extra_memory, idx_t memory_limit); - - //! Return freed memory to the OS, optionally releasing up to extra_memory from the block allocator's pool - void FlushAllocator(optional_idx extra_memory = optional_idx()); - //! Flush the allocator if the memory freed since the last flush could push resident memory past the limit - void FlushOnBulkDeallocation(); - //! Freed memory that has to accumulate before the allocator is flushed - idx_t GetBulkDeallocationFlushThreshold() const; + //! Flush bulk allocations and accumulated fallback frees using the remaining headroom. + void FlushForAllocation(idx_t extra_memory, idx_t memory_limit); //! Purge all blocks that haven't been pinned within the last N seconds idx_t PurgeAgedBlocks(uint32_t max_age_sec); @@ -185,8 +180,6 @@ class BufferPool { atomic maximum_memory; //! If bulk deallocation larger than this occurs, flush outstanding allocations atomic allocator_bulk_deallocation_flush_threshold; - //! Freed memory is flushed once it exceeds this fraction of the memory limit - static constexpr idx_t BULK_DEALLOCATION_FLUSH_DIVISOR = 16; //! Record timestamps of buffer manager unpin() events. Usable by custom eviction policies. bool track_eviction_timestamps; //! Eviction queues diff --git a/src/duckdb/src/include/duckdb/storage/buffer_manager.hpp b/src/duckdb/src/include/duckdb/storage/buffer_manager.hpp index 5569cb89c..9d425f37d 100644 --- a/src/duckdb/src/include/duckdb/storage/buffer_manager.hpp +++ b/src/duckdb/src/include/duckdb/storage/buffer_manager.hpp @@ -8,6 +8,7 @@ #pragma once +#include "duckdb/common/allocator.hpp" #include "duckdb/common/enums/memory_tag.hpp" #include "duckdb/common/optional_idx.hpp" #include "duckdb/storage/storage_info.hpp" @@ -16,7 +17,6 @@ namespace duckdb { class BlockMemory; -class Allocator; class AsyncTask; class BufferPool; class TemporaryMemoryManager; @@ -96,7 +96,7 @@ class BufferManager { virtual shared_ptr RegisterSmallMemory(MemoryTag tag, const idx_t size); //! Get the buffer allocator. - virtual DUCKDB_API Allocator &GetBufferAllocator(); + virtual DUCKDB_API BufferAllocator &GetBufferAllocator(); //! Reserve memory. virtual DUCKDB_API void ReserveMemory(idx_t size); //! Free reserved memory. diff --git a/src/duckdb/src/include/duckdb/storage/external_file_cache/external_file_cache.hpp b/src/duckdb/src/include/duckdb/storage/external_file_cache/external_file_cache.hpp index 1c5e2a048..211b9d24a 100644 --- a/src/duckdb/src/include/duckdb/storage/external_file_cache/external_file_cache.hpp +++ b/src/duckdb/src/include/duckdb/storage/external_file_cache/external_file_cache.hpp @@ -23,6 +23,7 @@ #include "duckdb/common/vector.hpp" #include "duckdb/common/winapi.hpp" #include "duckdb/storage/buffer/buffer_handle.hpp" +#include "duckdb/storage/buffer/buffer_pool_reservation.hpp" #include "duckdb/storage/buffer/temporary_file_information.hpp" #include "duckdb/storage/external_file_cache/external_file_cache_block.hpp" @@ -32,6 +33,7 @@ namespace duckdb { class ClientContext; class DatabaseInstance; class BufferManager; +class BufferPool; //! File metadata used to determine whether cached file data is still valid. struct CacheValidationInfo { @@ -65,7 +67,11 @@ class ExternalFileCache { //! Cached files struct CachedFile { public: - CachedFile(string path_p, idx_t generation_p); + CachedFile(string path_p, idx_t generation_p, BufferPool &buffer_pool); + + public: + //! Resize the block metadata reservation to the current number of blocks. + void UpdateBlockReservation() DUCKDB_REQUIRES(map_lock); public: const string path; @@ -76,6 +82,8 @@ class ExternalFileCache { map> blocks DUCKDB_GUARDED_BY(map_lock); //! Incremented whenever the blocks of the file are dropped. idx_t content_generation DUCKDB_GUARDED_BY(map_lock) = 0; + //! Memory reserved for the metadata of the blocks. + TempBufferPoolReservation block_reservation DUCKDB_GUARDED_BY(map_lock); mutable annotated_mutex meta_lock; //! Metadata for validating the cached blocks against the current file. diff --git a/src/duckdb/src/include/duckdb/storage/object_cache.hpp b/src/duckdb/src/include/duckdb/storage/object_cache.hpp index b232ffe8e..4c4ed3a05 100644 --- a/src/duckdb/src/include/duckdb/storage/object_cache.hpp +++ b/src/duckdb/src/include/duckdb/storage/object_cache.hpp @@ -198,6 +198,10 @@ class ObjectCache { return lru_cache.EvictToReduceAtLeast(target_bytes); } + //! Evict LRU entries until the buffer pool fits memory_limit. + //! Return false if evictable entries run out before reaching the limit. + bool EvictToMemoryLimit(idx_t memory_limit); + private: //! Build the internal cache key for a typed entry by namespacing the caller-provided key with the entry's //! ObjectType. diff --git a/src/duckdb/src/include/duckdb/storage/standard_buffer_manager.hpp b/src/duckdb/src/include/duckdb/storage/standard_buffer_manager.hpp index cd2d661eb..22245937d 100644 --- a/src/duckdb/src/include/duckdb/storage/standard_buffer_manager.hpp +++ b/src/duckdb/src/include/duckdb/storage/standard_buffer_manager.hpp @@ -33,6 +33,7 @@ class StandardBufferManager : public BufferManager { friend class BufferHandle; friend class BlockHandle; friend class BlockManager; + friend struct BufferAllocatorData; public: StandardBufferManager(DatabaseInstance &db, string temp_directory); @@ -98,7 +99,7 @@ class StandardBufferManager : public BufferManager { void SetTemporaryDirectory(const string &new_dir) final; - DUCKDB_API Allocator &GetBufferAllocator() final; + DUCKDB_API BufferAllocator &GetBufferAllocator() final; const DatabaseInstance &GetDatabase() const override { return db; @@ -159,11 +160,6 @@ class StandardBufferManager : public BufferManager { const char *InMemoryWarning(); - static data_ptr_t BufferAllocatorAllocate(PrivateAllocatorData *private_data, idx_t size); - static void BufferAllocatorFree(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t size); - static data_ptr_t BufferAllocatorRealloc(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t old_size, - idx_t size); - //! When the BlockHandle reaches 0 readers, this creates a new FileBuffer for this BlockHandle and //! overwrites the data within with garbage. Any readers that do not hold the pin will notice void VerifyZeroReaders(BlockLock &l, shared_ptr &handle); @@ -209,7 +205,7 @@ class StandardBufferManager : public BufferManager { //! The temporary id used for managed buffers atomic temporary_id; //! Allocator associated with the buffer manager, that passes all allocations through this buffer manager - Allocator buffer_allocator; + BufferAllocator buffer_allocator; //! Block manager for temp data unique_ptr temp_block_manager; //! Temporary evicted memory data per tag diff --git a/src/duckdb/src/include/duckdb/storage/table/row_group.hpp b/src/duckdb/src/include/duckdb/storage/table/row_group.hpp index 644278d20..863e208ae 100644 --- a/src/duckdb/src/include/duckdb/storage/table/row_group.hpp +++ b/src/duckdb/src/include/duckdb/storage/table/row_group.hpp @@ -121,6 +121,19 @@ class RowGroupVersionInfo { atomic> version_info; }; +//! Collects the ids of the rows that a delete actually deleted, but only once rows were skipped (e.g. because they +//! were matched more than once) - in the common case all input rows are deleted and nothing is collected +struct DeletedRowIds { + explicit DeletedRowIds(const row_t *input_ids) : input_ids(input_ids) { + } + + //! The row ids passed to the delete + const row_t *input_ids; + //! Whether any of the input rows were skipped - only then are the deleted row ids collected + bool has_skipped_rows = false; + vector row_ids; +}; + class RowGroup : public SegmentBase { public: friend class ColumnData; @@ -217,9 +230,10 @@ class RowGroup : public SegmentBase { //! Clean up append states that can either be compressed or deleted void CleanupAppend(VisibilityBound lowest_visibility_bound, idx_t start, idx_t count); - //! Delete the given set of rows in the version manager + //! Delete the given set of rows in the version manager - optionally collects the ids of the rows that were deleted + //! (input_offset is the position of row_ids in the row ids passed to the collection delete) idx_t Delete(TransactionData transaction, DuckTableEntry &table_entry, row_t *row_ids, idx_t count, - idx_t row_group_start); + idx_t row_group_start, optional_ptr deleted_ids = nullptr, idx_t input_offset = 0); static vector WriteToDisk(RowGroupWriteInfo &info, const vector> &row_groups); diff --git a/src/duckdb/src/include/duckdb/storage/table/row_group_collection.hpp b/src/duckdb/src/include/duckdb/storage/table/row_group_collection.hpp index 0e4e7b70f..655f43c3d 100644 --- a/src/duckdb/src/include/duckdb/storage/table/row_group_collection.hpp +++ b/src/duckdb/src/include/duckdb/storage/table/row_group_collection.hpp @@ -158,7 +158,9 @@ class RowGroupCollection { void RemoveFromIndexes(const QueryContext &context, TableIndexList &indexes, Vector &row_identifiers, idx_t count, IndexRemovalType removal_type, optional_idx active_checkpoint = optional_idx()); - idx_t Delete(TransactionData transaction, DuckTableEntry &table_entry, row_t *ids, idx_t count); + //! Delete the given rows - optionally collects the ids of the rows that were deleted (excluding duplicates) + idx_t Delete(TransactionData transaction, DuckTableEntry &table_entry, row_t *ids, idx_t count, + optional_ptr deleted_ids = nullptr); void Update(TransactionData transaction, DuckTableEntry &table_entry, row_t *ids, const vector &column_ids, DataChunk &updates); void UpdateColumn(TransactionData transaction, DuckTableEntry &table_entry, Vector &row_ids, diff --git a/src/duckdb/src/include/duckdb/storage/table/table_index_list.hpp b/src/duckdb/src/include/duckdb/storage/table/table_index_list.hpp index 2c02f8a72..92d22a4ca 100644 --- a/src/duckdb/src/include/duckdb/storage/table/table_index_list.hpp +++ b/src/duckdb/src/include/duckdb/storage/table/table_index_list.hpp @@ -61,10 +61,9 @@ class TableIndexList { optional_idx active_checkpoint = optional_idx()); //! Removes an index entry from the list of index entries and release any storage the index owns. void RemoveIndex(idx_t index_oid); - //! Returns shared ownership of the stable logical index entry matching the name. - shared_ptr FindEntry(const Identifier &name) const; //! Returns the entry matching the index OID. shared_ptr FindEntry(const IndexEntry &index) const; + shared_ptr FindEntry(idx_t index_oid) const; //! Binds unbound indexes possibly present after loading an extension. void Bind(ClientContext &context, DataTableInfo &table_info, const optional &index_type = {}); //! Returns true, if there are no index entries. diff --git a/src/duckdb/src/main/appender.cpp b/src/duckdb/src/main/appender.cpp index 2a55ebd32..aa18841e7 100644 --- a/src/duckdb/src/main/appender.cpp +++ b/src/duckdb/src/main/appender.cpp @@ -651,7 +651,7 @@ Value Appender::GetDefaultValue(idx_t column) { if (it == default_values.end()) { auto &name = description->columns[index].Name(); throw NotImplementedException( - "AppendDefault is not supported for column \"%s\": not a foldable default expressions.", name); + "AppendDefault is not supported for column %s: not a foldable default expressions.", name); } return it->second; } diff --git a/src/duckdb/src/main/capi/v2/capi_v2_custom_option.cpp b/src/duckdb/src/main/capi/v2/capi_v2_custom_option.cpp index beeb84e3b..99e03d37f 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_custom_option.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_custom_option.cpp @@ -86,7 +86,7 @@ class CV2CustomOption { auto &config = DBConfig::GetConfig(target); if (DBConfig::GetOptionByName(name) || config.HasExtensionOption(name)) { - throw InvalidInputException("An option named \"%s\" already exists.", name); + throw InvalidInputException("An option named %s already exists.", name); } ExtensionOption option; diff --git a/src/duckdb/src/main/capi/v2/capi_v2_func.cpp b/src/duckdb/src/main/capi/v2/capi_v2_func.cpp index b1db328e5..597089d82 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_func.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_func.cpp @@ -46,7 +46,7 @@ const Value &CV2ConstantBindInfo::GetArg(idx_t index) const { const auto &name = function.GetNamedArguments()[index - positional.size()]; auto entry = named.find(name); if (entry == named.end()) { - throw InternalException("Named argument \"%s\" of a function call has no value", name); + throw InternalException("Named argument %s of a function call has no value", name); } return entry->second; } diff --git a/src/duckdb/src/main/capi/v2/capi_v2_func_pragma.cpp b/src/duckdb/src/main/capi/v2/capi_v2_func_pragma.cpp index 444c0ddd6..6082cf61a 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_func_pragma.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_func_pragma.cpp @@ -60,7 +60,7 @@ static auto CV2PragmaQuery(ClientContext &context, const FunctionParameters &par info.query_cb(bind_info, Convert(&result_info), ctx, err); }); if (!result_info.out_query) { - throw InvalidInputException("The query callback of pragma function \"%s\" did not set a query.", + throw InvalidInputException("The query callback of pragma function %s did not set a query.", parameters.function->name); } return std::move(*result_info.out_query); diff --git a/src/duckdb/src/main/capi/v2/capi_v2_func_table.cpp b/src/duckdb/src/main/capi/v2/capi_v2_func_table.cpp index 77128a981..dcc5f7ce5 100644 --- a/src/duckdb/src/main/capi/v2/capi_v2_func_table.cpp +++ b/src/duckdb/src/main/capi/v2/capi_v2_func_table.cpp @@ -93,7 +93,7 @@ class CV2TableLocalState final : public LocalTableFunctionState { // The engine would otherwise raise an InternalException, which invalidates the database. if (data.batch_index < last_index) { throw InvalidInputException( - "The partition data callback of table function \"%s\" reported batch index %llu after " + "The partition data callback of table function %s reported batch index %llu after " "previously reporting %llu on the same thread; the batch index must not decrease.", function_name, data.batch_index, last_index); } @@ -105,7 +105,7 @@ class CV2TableLocalState final : public LocalTableFunctionState { continue; } throw InvalidInputException( - "The partition data callback of table function \"%s\" reported a different partitioning " + "The partition data callback of table function %s reported a different partitioning " "column value at index %llu without changing the batch index.", function_name, i); } @@ -432,7 +432,7 @@ static auto CV2TableBind(ClientContext &context, TableFunctionBindInput &input, } if (args.out_column_types.empty()) { - throw InvalidInputException("The bind callback of table function \"%s\" did not declare any result columns.", + throw InvalidInputException("The bind callback of table function %s did not declare any result columns.", input.table_function.GetName()); } @@ -756,14 +756,14 @@ static auto CV2TableFilterPushdown(ClientContext &context, LogicalGet &get, Func static auto CV2TakePartitionData(CV2TablePartitionDataInfo &args, const Identifier &function_name) -> OperatorPartitionData { if (!args.out_batch_index.IsValid()) { - throw InvalidInputException("The partition data callback of table function \"%s\" did not set a batch index.", + throw InvalidInputException("The partition data callback of table function %s did not set a batch index.", function_name); } OperatorPartitionData result(args.out_batch_index.GetIndex()); result.partition_data.reserve(args.out_partition_values.size()); for (idx_t i = 0; i < args.out_partition_values.size(); i++) { if (!args.out_partition_value_set[i]) { - throw InvalidInputException("The partition data callback of table function \"%s\" did not set the " + throw InvalidInputException("The partition data callback of table function %s did not set the " "partitioning column value at index %llu.", function_name, i); } @@ -953,7 +953,7 @@ class CV2MultiFileFunction { //! The overload of the single-file function that takes the path of the file to read TableFunction SelectSingleFileFunction(unique_ptr overloads) const { if (!overloads) { - throw InvalidInputException("Table function \"%s\" to read single files with does not exist.", + throw InvalidInputException("Table function %s to read single files with does not exist.", single_file_function); } for (auto &function : overloads->functions) { @@ -967,7 +967,7 @@ class CV2MultiFileFunction { } } throw InvalidInputException( - "Table function \"%s\" cannot read single files: it must take the file to read as its only positional " + "Table function %s cannot read single files: it must take the file to read as its only positional " "parameter, of type ANY (or VARCHAR).", single_file_function); } diff --git a/src/duckdb/src/main/client_context.cpp b/src/duckdb/src/main/client_context.cpp index 40b97122e..e1ca6eb3e 100644 --- a/src/duckdb/src/main/client_context.cpp +++ b/src/duckdb/src/main/client_context.cpp @@ -176,7 +176,7 @@ void ClientContext::ConnectToCatalog(const shared_ptr &target) // return true MUST implement RemoteExecute(string). Validation runs before mutation so a throw // leaves the client unbound. if (!target->GetCatalog().Supports(RemoteCapability::CONNECT)) { - throw InvalidInputException("Database \"%s\" does not support CONNECT", target->GetName()); + throw InvalidInputException("Database %s does not support CONNECT", target->GetName()); } connected_to_database = target; is_connected = true; @@ -417,7 +417,7 @@ void ClientContext::CheckIfPreparedStatementIsExecutable(PreparedStatementData & auto entry = manager.GetDatabase(*this, modified_database); if (!entry) { // database has been detached - throw InvalidInputException("Database \"%s\" not found", modified_database); + throw InvalidInputException("Database %s not found", modified_database); } if (entry->IsReadOnly()) { throw InvalidInputException(StringUtil::Format( diff --git a/src/duckdb/src/main/client_data.cpp b/src/duckdb/src/main/client_data.cpp index 9dd8937a6..472ba4de2 100644 --- a/src/duckdb/src/main/client_data.cpp +++ b/src/duckdb/src/main/client_data.cpp @@ -148,7 +148,7 @@ class ClientBufferManager : public BufferManager { return result; } - Allocator &GetBufferAllocator() override { + BufferAllocator &GetBufferAllocator() override { return buffer_manager.GetBufferAllocator(); } void ReserveMemory(idx_t size) override { diff --git a/src/duckdb/src/main/config.cpp b/src/duckdb/src/main/config.cpp index ecc1e36b1..66c3fa02c 100644 --- a/src/duckdb/src/main/config.cpp +++ b/src/duckdb/src/main/config.cpp @@ -115,6 +115,7 @@ static const ConfigurationOption internal_options[] = { DUCKDB_SETTING(DebugDisableOptimizerSetting), DUCKDB_SETTING(EnableCachingOperatorsSetting), DUCKDB_SETTING(DebugEvictionQueueSleepMicroSecondsSetting), + DUCKDB_SETTING(DebugFileDescriptorLimitSetting), DUCKDB_SETTING_CALLBACK(ForceBitpackingModeSetting), DUCKDB_SETTING(DebugForceCommitFailureSetting), DUCKDB_SETTING(DebugForceCommitRevertFailureSetting), @@ -560,11 +561,11 @@ SetScope ExtensionOption::ResolveScope(const Identifier &name, SetScope scope) c scope = default_scope; } if (scope == SetScope::GLOBAL && !supports_global) { - throw InvalidInputException( - "Setting \"%s\" cannot be set as a global variable - it can only be set per session", name); + throw InvalidInputException("Setting %s cannot be set as a global variable - it can only be set per session", + name); } if (scope == SetScope::SESSION && !supports_session) { - throw InvalidInputException("Setting \"%s\" cannot be set as a session variable - it can only be set globally", + throw InvalidInputException("Setting %s cannot be set as a session variable - it can only be set globally", name); } return scope; @@ -680,7 +681,7 @@ void DBConfig::CheckLock(const Identifier &name) { } } // not allowed! - throw InvalidInputException("Cannot change configuration option \"%s\" - the configuration has been locked", name); + throw InvalidInputException("Cannot change configuration option %s - the configuration has been locked", name); } idx_t DBConfig::GetSystemMaxThreads(FileSystem &fs) { diff --git a/src/duckdb/src/main/database.cpp b/src/duckdb/src/main/database.cpp index 7aa8febbd..a33173c92 100644 --- a/src/duckdb/src/main/database.cpp +++ b/src/duckdb/src/main/database.cpp @@ -113,8 +113,8 @@ DatabaseInstance::~DatabaseInstance() { buffer_manager.reset(); - // flush allocations and disable the background thread - config.block_allocator->FlushAll(); + // Flush allocations and disable the background thread. + config.block_allocator->FlushOnShutdown(); Allocator::SetBackgroundThreads(false); // after all destruction is complete clear the cache entry config.db_cache_entry.reset(); @@ -283,7 +283,7 @@ void DatabaseInstance::LoadExtensionSettings() { } ExtensionOption extension_option; if (!config.TryGetExtensionOption(name, extension_option)) { - throw InternalException("Extension %s did not provide the '%s' config setting", extension_name, name); + throw InternalException("Extension %s did not provide the %s config setting", extension_name, name); } // if the extension provided the option, it should no longer be unrecognized. D_ASSERT(config.options.unrecognized_options.find(name) == config.options.unrecognized_options.end()); diff --git a/src/duckdb/src/main/database_file_path_manager.cpp b/src/duckdb/src/main/database_file_path_manager.cpp index f2203a73a..d60153baa 100644 --- a/src/duckdb/src/main/database_file_path_manager.cpp +++ b/src/duckdb/src/main/database_file_path_manager.cpp @@ -61,6 +61,11 @@ InsertDatabasePathResult DatabaseFilePathManager::InsertDatabasePath(DatabaseMan return InsertDatabasePathResult::SUCCESS; } +bool DatabaseFilePathManager::IsDatabasePath(const string &path) const { + lock_guard path_lock(db_paths_lock); + return db_paths.find(path) != db_paths.end(); +} + void DatabaseFilePathManager::EraseDatabasePath(const string &path) { if (path.empty() || path == IN_MEMORY_PATH) { return; diff --git a/src/duckdb/src/main/database_manager.cpp b/src/duckdb/src/main/database_manager.cpp index 86bf27271..bf6951ea9 100644 --- a/src/duckdb/src/main/database_manager.cpp +++ b/src/duckdb/src/main/database_manager.cpp @@ -426,6 +426,25 @@ InsertDatabasePathResult DatabaseManager::InsertDatabasePath(const AttachInfo &i return path_manager->InsertDatabasePath(*this, info.path, info.name, info.on_conflict, options); } +bool DatabaseManager::IsAttachedDatabaseFile(ClientContext &context, const string &path) { + auto &fs = FileSystem::GetFileSystem(context); + // attached database paths are canonicalized when the database is attached + auto canonical_path = fs.CanonicalizePath(path); + if (path_manager->IsDatabasePath(canonical_path)) { + return true; + } + // the WAL files of the database + for (auto &wal_suffix : {".wal", ".wal.checkpoint", ".wal.recovery"}) { + if (StringUtil::EndsWith(canonical_path, wal_suffix)) { + auto database_path = canonical_path.substr(0, canonical_path.size() - strlen(wal_suffix)); + if (path_manager->IsDatabasePath(database_path)) { + return true; + } + } + } + return false; +} + vector DatabaseManager::GetAttachedDatabasePaths() { vector result; lock_guard guard(databases_lock); diff --git a/src/duckdb/src/main/extension/extension_loader.cpp b/src/duckdb/src/main/extension/extension_loader.cpp index 44593e888..c99f431c4 100644 --- a/src/duckdb/src/main/extension/extension_loader.cpp +++ b/src/duckdb/src/main/extension/extension_loader.cpp @@ -74,7 +74,7 @@ void ExtensionLoader::UseDefaultSchema(const Identifier &name) { loader_info.extension_schema); } if (name == "pg_catalog") { - throw InvalidInputException("Cannot set default extension schema to '%s'", name); + throw InvalidInputException("Cannot set default extension schema to %s", name); } if (name == DEFAULT_SCHEMA) { loader_info.extension_schema = Identifier::DefaultSchema(); @@ -87,7 +87,7 @@ void ExtensionLoader::AddSchemaToSearchPath(const Identifier &schema_name) const // adds an explicitly set extension schema to the search path if (loader_info.extension_schema != schema_name || schema_name == DEFAULT_SCHEMA || loader_info.extension_schema == DEFAULT_SCHEMA) { - throw InvalidInputException("Cannot add schema '%s' to search path, first set the extension schema explicitly " + throw InvalidInputException("Cannot add schema %s to search path, first set the extension schema explicitly " "with UseDefaultSchema()", schema_name); } @@ -97,7 +97,7 @@ void ExtensionLoader::AddSchemaToSearchPath(const Identifier &schema_name) const auto data = CatalogTransaction::GetSystemTransaction(db); auto schema = system_catalog.GetSchema(data, schema_name, OnEntryNotFound::RETURN_NULL); if (!schema) { - throw InvalidInputException("Cannot add schema '%s' to search path: schema does not exist. " + throw InvalidInputException("Cannot add schema %s to search path: schema does not exist. " "Call CreateExtensionSchema() first.", schema_name); } @@ -340,7 +340,7 @@ optional_ptr ExtensionLoader::TryGetFunction(const Identifier &nam ScalarFunctionCatalogEntry &ExtensionLoader::GetFunction(const Identifier &name) { auto catalog_entry = TryGetFunction(name); if (!catalog_entry) { - throw InvalidInputException("Function with name \"%s\" not found in ExtensionLoader::GetFunction", name); + throw InvalidInputException("Function with name %s not found in ExtensionLoader::GetFunction", name); } return catalog_entry->Cast(); } @@ -352,7 +352,7 @@ optional_ptr ExtensionLoader::TryGetTableFunction(const Identifier TableFunctionCatalogEntry &ExtensionLoader::GetTableFunction(const Identifier &name) { auto catalog_entry = TryGetTableFunction(name); if (!catalog_entry) { - throw InvalidInputException("Function with name \"%s\" not found in ExtensionLoader::GetTableFunction", name); + throw InvalidInputException("Function with name %s not found in ExtensionLoader::GetTableFunction", name); } return catalog_entry->Cast(); } diff --git a/src/duckdb/src/main/http/http_transport_manager.cpp b/src/duckdb/src/main/http/http_transport_manager.cpp index ac7658fc5..28733b654 100644 --- a/src/duckdb/src/main/http/http_transport_manager.cpp +++ b/src/duckdb/src/main/http/http_transport_manager.cpp @@ -6,10 +6,6 @@ #include "duckdb/main/config.hpp" #include "duckdb/main/database.hpp" -#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) -#include -#endif - namespace duckdb { static constexpr idx_t HTTP_TRANSPORT_MAX_CAPACITY = 256; @@ -227,25 +223,10 @@ idx_t HTTPTransportManager::CalculateCapacity(idx_t system_concurrency, optional if (!file_descriptor_limit.IsValid()) { return cpu_target; } - auto fd_target = MaxValue(file_descriptor_limit.GetIndex() / 8, 1); + auto fd_target = FileSystem::GetFileDescriptorShare(file_descriptor_limit.GetIndex()); return MinValue(cpu_target, fd_target); } -optional_idx HTTPTransportManager::GetFileDescriptorLimit() { -#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) - struct rlimit limit; - if (getrlimit(RLIMIT_NOFILE, &limit) != 0 || limit.rlim_cur == RLIM_INFINITY) { - return optional_idx(); - } - if (limit.rlim_cur >= NumericLimits::Maximum()) { - return optional_idx(); - } - return optional_idx(static_cast(limit.rlim_cur)); -#else - return optional_idx(); -#endif -} - bool HTTPTransportManager::AdvanceConnectionEpoch(uint64_t &connection_epoch, bool &reuse_poisoned) noexcept { if (reuse_poisoned) { return true; @@ -283,7 +264,7 @@ idx_t HTTPTransportManager::AutomaticCapacity(const DBConfig &config) { auto io_concurrency = config.options.async_threads == DConstants::INVALID_INDEX ? DBConfig::GetSystemMaxAsyncThreads(fs) : config.options.async_threads; - return CalculateCapacity(DBConfig::GetSystemMaxThreads(fs), GetFileDescriptorLimit(), io_concurrency); + return CalculateCapacity(DBConfig::GetSystemMaxThreads(fs), FileSystem::GetFileDescriptorLimit(), io_concurrency); } idx_t HTTPTransportManager::GetCapacity() const { diff --git a/src/duckdb/src/main/secret/secret.cpp b/src/duckdb/src/main/secret/secret.cpp index 32e5cc921..ae87a2b65 100644 --- a/src/duckdb/src/main/secret/secret.cpp +++ b/src/duckdb/src/main/secret/secret.cpp @@ -295,7 +295,7 @@ void CreateSecretFunctionSet::AddFunction(CreateSecretFunction &function, OnCrea if (ProviderExists(function.provider)) { if (on_conflict == OnCreateConflict::ERROR_ON_CONFLICT) { throw InternalException( - "Attempted to override a Create Secret Function with OnCreateConflict::ERROR_ON_CONFLICT for: '%s'", + "Attempted to override a Create Secret Function with OnCreateConflict::ERROR_ON_CONFLICT for: %s", function.provider); } else if (on_conflict == OnCreateConflict::REPLACE_ON_CONFLICT) { functions[function.provider] = function; diff --git a/src/duckdb/src/main/secret/secret_manager.cpp b/src/duckdb/src/main/secret/secret_manager.cpp index 16ca921b5..7d9c7aeb2 100644 --- a/src/duckdb/src/main/secret/secret_manager.cpp +++ b/src/duckdb/src/main/secret/secret_manager.cpp @@ -816,7 +816,7 @@ unique_ptr DefaultSecretGenerator::CreateDefaultEntryInternal(cons } } - throw SerializationException("Failed to deserialize secret '%s' from '%s': file appears empty! Please remove the " + throw SerializationException("Failed to deserialize secret %s from '%s': file appears empty! Please remove the " "file, restart and try again", entry_name, secret_path); } diff --git a/src/duckdb/src/main/secret/secret_storage.cpp b/src/duckdb/src/main/secret/secret_storage.cpp index 286012848..4b33f8acb 100644 --- a/src/duckdb/src/main/secret/secret_storage.cpp +++ b/src/duckdb/src/main/secret/secret_storage.cpp @@ -71,7 +71,7 @@ void TransactionSecretStorage::DropSecretByName(const Identifier &name, OnEntryN auto entry = secrets.find(name); if (entry == secrets.end()) { if (on_entry_not_found == OnEntryNotFound::THROW_EXCEPTION) { - throw InvalidInputException("Failed to remove non-existent transaction secret '%s'", name); + throw InvalidInputException("Failed to remove non-existent transaction secret %s", name); } return; } @@ -178,7 +178,7 @@ void CatalogSetSecretStorage::DropSecretByName(const Identifier &name, OnEntryNo if (on_entry_not_found == OnEntryNotFound::THROW_EXCEPTION) { string persist_string = persistent ? "persistent" : "temporary"; string storage_string = persistent ? " in secret storage '" + storage_name + "'" : ""; - throw InvalidInputException("Failed to remove non-existent %s secret '%s'%s", persist_string, name, + throw InvalidInputException("Failed to remove non-existent %s secret %s%s", persist_string, name, storage_string); } return; diff --git a/src/duckdb/src/main/settings/custom_settings.cpp b/src/duckdb/src/main/settings/custom_settings.cpp index 0a3fca8ab..b00b307d9 100644 --- a/src/duckdb/src/main/settings/custom_settings.cpp +++ b/src/duckdb/src/main/settings/custom_settings.cpp @@ -81,7 +81,7 @@ static idx_t ParseMemoryLimitOrPercentage(const string &input, BASE &&get_base) return DBConfig::ParseMemoryLimitOrMaximum(input); } double percentage; - if (!TryDoubleCast(input.c_str(), input.size() - 1, percentage, false) || percentage < 0 || percentage > 100) { + if (!TryDoubleCast(input.c_str(), input.size() - 1, percentage, false) || !(percentage >= 0 && percentage <= 100)) { throw InvalidInputException("Unable to parse valid percentage (input: %s)", input); } auto result = percentage * static_cast(get_base()) / 100.0; @@ -1167,7 +1167,7 @@ void IndexScanPercentageSetting::OnSet(SettingCallbackInfo &, Value &input) { throw InvalidInputException("%s setting cannot be NULL", IndexScanPercentageSetting::Name); } auto index_scan_percentage = input.GetValue(); - if (index_scan_percentage < 0 || index_scan_percentage > 1.0) { + if (!(index_scan_percentage >= 0 && index_scan_percentage <= 1.0)) { throw InvalidInputException("the index scan percentage must be within [0, 1]"); } } @@ -1511,7 +1511,11 @@ void ProgressBarTimeSetting::SetLocal(ClientContext &context, const Value &input } auto &config = ClientConfig::GetConfig(context); ProgressBar::SystemOverrideCheck(config); - config.wait_time = input.GetValue(); + auto wait_time = input.GetValue(); + if (wait_time < 0) { + throw InvalidInputException("%s setting cannot be negative", ProgressBarTimeSetting::Name); + } + config.wait_time = wait_time; config.enable_progress_bar = true; } diff --git a/src/duckdb/src/optimizer/column_binding_replacer.cpp b/src/duckdb/src/optimizer/column_binding_replacer.cpp index bab6b3010..a6739de27 100644 --- a/src/duckdb/src/optimizer/column_binding_replacer.cpp +++ b/src/duckdb/src/optimizer/column_binding_replacer.cpp @@ -21,13 +21,7 @@ ReplacementBinding BindingReplacementGraph::ResolveReplacement(ColumnBinding bin ReplacementBinding result(binding, binding); column_binding_set_t visited; while (visited.insert(result.new_binding).second) { - optional_ptr replacement; - for (auto &entry : replacement_bindings) { - if (entry.old_binding == result.new_binding) { - replacement = entry; - break; - } - } + auto replacement = Find(result.new_binding); if (!replacement) { return result; } @@ -40,6 +34,14 @@ ReplacementBinding BindingReplacementGraph::ResolveReplacement(ColumnBinding bin throw InternalException("Cyclic column binding replacements"); } +optional_ptr BindingReplacementGraph::Find(ColumnBinding old_binding) const { + auto entry = binding_slots.find(old_binding); + if (entry == binding_slots.end()) { + return nullptr; + } + return replacement_bindings[entry->second]; +} + ColumnBinding BindingReplacementGraph::Resolve(ColumnBinding binding) const { return ResolveReplacement(binding).new_binding; } @@ -56,10 +58,9 @@ bool BindingReplacementGraph::TryAdd(const ReplacementBinding &replacement) { } return true; } - for (auto &existing : replacement_bindings) { - if (existing.old_binding != replacement.old_binding) { - continue; - } + auto slot = binding_slots.find(replacement.old_binding); + if (slot != binding_slots.end()) { + auto &existing = replacement_bindings[slot->second]; if (existing.new_binding != replacement.new_binding || (existing.replace_type && replacement.replace_type && existing.new_type != replacement.new_type)) { return false; @@ -70,6 +71,7 @@ bool BindingReplacementGraph::TryAdd(const ReplacementBinding &replacement) { } return true; } + binding_slots.emplace(replacement.old_binding, replacement_bindings.size()); if (replacement.replace_type) { replacement_bindings.emplace_back(replacement.old_binding, replacement.new_binding, replacement.new_type); } else { @@ -119,18 +121,74 @@ void ColumnBindingReplacer::AddReplacements(const vector &old_bin } } -static void ReplaceCorrelatedColumns(CorrelatedColumns &columns, - const vector &replacement_bindings) { - for (auto &column : columns) { - for (auto &replacement : replacement_bindings) { - if (column.binding != replacement.old_binding) { - continue; - } - column.binding = replacement.new_binding; - if (replacement.replace_type) { - column.type = replacement.new_type; +void ColumnBindingReplacer::BuildIndex() { + replacement_index.clear(); + use_index = false; + column_binding_map_t index; + index.reserve(replacement_bindings.size()); + for (idx_t i = 0; i < replacement_bindings.size(); i++) { + // Sequential application takes the first match, so keep the first slot for duplicate old bindings + index.emplace(replacement_bindings[i].old_binding, i); + } + for (auto &replacement : replacement_bindings) { + if (index.find(replacement.new_binding) != index.end()) { + // Sequential application could chain through this new binding; an indexed lookup could not + return; + } + } + replacement_index = std::move(index); + use_index = true; +} + +optional_ptr ColumnBindingReplacer::FindReplacement(ColumnBinding binding) const { + D_ASSERT(use_index); + auto entry = replacement_index.find(binding); + if (entry == replacement_index.end()) { + return nullptr; + } + return replacement_bindings[entry->second]; +} + +void ColumnBindingReplacer::Replace(ColumnBinding &binding) const { + if (use_index) { + auto replacement = FindReplacement(binding); + if (replacement) { + binding = replacement->new_binding; + } + return; + } + for (auto &replacement : replacement_bindings) { + if (binding == replacement.old_binding) { + binding = replacement.new_binding; + } + } +} + +void ColumnBindingReplacer::Replace(ColumnBinding &binding, LogicalType &type) const { + if (use_index) { + auto replacement = FindReplacement(binding); + if (replacement) { + binding = replacement->new_binding; + if (replacement->replace_type) { + type = replacement->new_type; } } + return; + } + for (auto &replacement : replacement_bindings) { + if (binding != replacement.old_binding) { + continue; + } + binding = replacement.new_binding; + if (replacement.replace_type) { + type = replacement.new_type; + } + } +} + +static void ReplaceCorrelatedColumns(CorrelatedColumns &columns, const ColumnBindingReplacer &replacer) { + for (auto &column : columns) { + replacer.Replace(column.binding, column.type); } } @@ -149,11 +207,7 @@ void ColumnBindingReplacer::VisitOperatorBindings(LogicalOperator &op) { if (op.type == LogicalOperatorType::LOGICAL_SECURE_VIEW) { auto &view = op.Cast(); for (auto &binding : view.output_bindings) { - for (auto &replacement : replacement_bindings) { - if (binding == replacement.old_binding) { - binding = replacement.new_binding; - } - } + Replace(binding); } } VisitOperatorExpressions(op); @@ -161,6 +215,16 @@ void ColumnBindingReplacer::VisitOperatorBindings(LogicalOperator &op) { unique_ptr ColumnBindingReplacer::VisitReplace(BoundColumnRefExpression &expr, unique_ptr *expr_ptr) { + if (use_index) { + auto replacement = FindReplacement(expr.Binding()); + if (replacement) { + expr.BindingMutable() = replacement->new_binding; + if (replacement->replace_type) { + expr.SetReturnType(replacement->new_type); + } + } + return nullptr; + } for (auto &replacement : replacement_bindings) { if (expr.Binding() != replacement.old_binding) { continue; @@ -177,12 +241,12 @@ void CorrelatedColumnBindingReplacer::VisitOperatorBindings(LogicalOperator &op) switch (op.type) { case LogicalOperatorType::LOGICAL_DEPENDENT_JOIN: { auto &dependent_join = op.Cast(); - ReplaceCorrelatedColumns(dependent_join.correlated_columns, replacement_bindings); + ReplaceCorrelatedColumns(dependent_join.correlated_columns, *this); break; } case LogicalOperatorType::LOGICAL_RECURSIVE_CTE: case LogicalOperatorType::LOGICAL_MATERIALIZED_CTE: - ReplaceCorrelatedColumns(op.Cast().correlated_columns, replacement_bindings); + ReplaceCorrelatedColumns(op.Cast().correlated_columns, *this); break; default: break; @@ -192,7 +256,7 @@ void CorrelatedColumnBindingReplacer::VisitOperatorBindings(LogicalOperator &op) unique_ptr CorrelatedColumnBindingReplacer::VisitReplace(BoundSubqueryExpression &expr, unique_ptr *expr_ptr) { - ReplaceCorrelatedColumns(expr.GetBinder()->correlated_columns, replacement_bindings); + ReplaceCorrelatedColumns(expr.GetBinder()->correlated_columns, *this); if (expr.SubqueryMutable().plan) { VisitOperator(*expr.SubqueryMutable().plan); } @@ -239,19 +303,10 @@ void ColumnBindingRewrite::ApplyToOperatorBindings(LogicalOperator &op, const Bi } CorrelatedColumnBindingReplacer replacer; replacements.AddTo(replacer); + replacer.BuildIndex(); replacer.VisitOperatorBindings(op); } -static optional_ptr FindReplacement(const BindingReplacementGraph &replacements, - ColumnBinding binding) { - for (auto &replacement : replacements) { - if (replacement.old_binding == binding) { - return replacement; - } - } - return nullptr; -} - static bool TryResolveToOutput(ColumnBinding binding, const column_binding_set_t &new_bindings, const BindingReplacementGraph &replacements, ReplacementBinding &result) { result = ReplacementBinding(binding, binding); @@ -260,7 +315,7 @@ static bool TryResolveToOutput(ColumnBinding binding, const column_binding_set_t if (!visited.insert(result.new_binding).second) { throw InternalException("Cyclic column binding replacements"); } - auto next = FindReplacement(replacements, result.new_binding); + auto next = replacements.Find(result.new_binding); if (!next) { return false; } @@ -273,6 +328,28 @@ static bool TryResolveToOutput(ColumnBinding binding, const column_binding_set_t return true; } +BindingReplacementGraph BindingReplacementGraph::ScopedToBoundary(const vector &boundary) const { + column_binding_set_t boundary_bindings(boundary.begin(), boundary.end()); + BindingReplacementGraph result; + for (auto &replacement : replacement_bindings) { + // Resolve from the edge target so that edges departing from a boundary binding keep their continuation + ReplacementBinding resolved(replacement.old_binding, replacement.old_binding); + if (!TryResolveToOutput(replacement.new_binding, boundary_bindings, *this, resolved)) { + continue; + } + resolved.old_binding = replacement.old_binding; + if (replacement.replace_type && !resolved.replace_type) { + resolved.replace_type = true; + resolved.new_type = replacement.new_type; + } + if (resolved.old_binding == resolved.new_binding) { + continue; + } + result.Add(resolved); + } + return result; +} + static vector ScopeToOutput(const vector &new_output, const BindingReplacementGraph &replacements) { column_binding_set_t new_bindings(new_output.begin(), new_output.end()); @@ -305,16 +382,6 @@ void ColumnBindingRewrite::ValidateOutput(const vector &old_outpu } } -static ReplacementBinding ResolveBoundaryReplacement(ColumnBinding binding, - const vector &replacements) { - for (auto &replacement : replacements) { - if (replacement.old_binding == binding) { - return replacement; - } - } - return ReplacementBinding(binding, binding); -} - void ColumnBindingRewrite::RewriteChild( unique_ptr &op, idx_t child_index, const std::function &, BindingReplacementGraph &)> &rewrite) { @@ -349,8 +416,11 @@ void ColumnBindingRewrite::ApplyToChild(unique_ptr &op, idx_t c auto new_child_bindings = op->children[child_index]->GetColumnBindings(); auto boundary_replacements = ScopeToOutput(new_child_bindings, replacements); column_binding_set_t new_bindings(new_child_bindings.begin(), new_child_bindings.end()); + CorrelatedColumnBindingReplacer replacer; + replacer.replacement_bindings = std::move(boundary_replacements); + replacer.BuildIndex(); for (auto &binding : old_child_bindings) { - if (new_bindings.find(binding) == new_bindings.end() && FindReplacement(replacements, binding)) { + if (new_bindings.find(binding) == new_bindings.end() && replacements.Find(binding)) { ReplacementBinding resolved(binding, binding); if (!TryResolveToOutput(binding, new_bindings, replacements, resolved)) { throw InternalException("Binding rewrite moved child binding %s outside rewritten child output %s", @@ -358,18 +428,16 @@ void ColumnBindingRewrite::ApplyToChild(unique_ptr &op, idx_t c LogicalOperator::ColumnBindingsToString(new_child_bindings)); } } - binding = ResolveBoundaryReplacement(binding, boundary_replacements).new_binding; + replacer.Replace(binding); } if (op->HasProjectionMap()) { auto projection_map = LogicalOperatorVisitor::GetProjectionMap(*op, child_index); D_ASSERT(projection_map); RemapProjectionMapStrict(*projection_map, old_child_bindings, new_child_bindings); } - if (boundary_replacements.empty()) { + if (replacer.replacement_bindings.empty()) { return; } - CorrelatedColumnBindingReplacer replacer; - replacer.replacement_bindings = std::move(boundary_replacements); if (op->type == LogicalOperatorType::LOGICAL_DEPENDENT_JOIN && child_index == 0) { replacer.stop_operator = *op->children[child_index]; replacer.VisitOperator(*op); diff --git a/src/duckdb/src/optimizer/grouping_sets_optimizer.cpp b/src/duckdb/src/optimizer/grouping_sets_optimizer.cpp index 51f2247d7..39b105b83 100644 --- a/src/duckdb/src/optimizer/grouping_sets_optimizer.cpp +++ b/src/duckdb/src/optimizer/grouping_sets_optimizer.cpp @@ -100,8 +100,13 @@ static bool FindCascade(const vector &grouping_sets, vector 0; source_idx--) { auto &source_set = grouping_sets[levels[source_idx - 1].grouping_set_idx]; if (std::includes(source_set.begin(), source_set.end(), grouping_set.begin(), grouping_set.end())) { - levels[level_idx].source_level = source_idx - 1; - levels[source_idx - 1].materialized = true; + auto source_level = source_idx - 1; + if (source_set.size() == grouping_set.size() && levels[source_level].source_level.IsValid()) { + // identical grouping set: share the source instead of chaining, to keep the cascade shallow + source_level = levels[source_level].source_level.GetIndex(); + } + levels[level_idx].source_level = source_level; + levels[source_level].materialized = true; break; } } diff --git a/src/duckdb/src/optimizer/rule/empty_needle_removal.cpp b/src/duckdb/src/optimizer/rule/empty_needle_removal.cpp index 12ab18a4d..c7e606f96 100644 --- a/src/duckdb/src/optimizer/rule/empty_needle_removal.cpp +++ b/src/duckdb/src/optimizer/rule/empty_needle_removal.cpp @@ -19,7 +19,10 @@ static unique_ptr TryRemoveEmptyStringConcat(ClientContext &context, if (!children[child_idx]->IsFoldable()) { continue; } - auto value = ExpressionExecutor::EvaluateScalar(context, *children[child_idx]); + Value value; + if (!ExpressionExecutor::TryEvaluateScalar(context, *children[child_idx], value)) { + continue; + } if (value.IsNull() || value.type().id() != LogicalTypeId::VARCHAR || !StringValue::Get(value).empty()) { continue; } @@ -55,7 +58,10 @@ unique_ptr EmptyNeedleRemovalRule::Apply(LogicalOperator &op, vector } D_ASSERT(root.GetReturnType().id() == LogicalTypeId::BOOLEAN); - auto prefix_value = ExpressionExecutor::EvaluateScalar(GetContext(), prefix_expr); + Value prefix_value; + if (!ExpressionExecutor::TryEvaluateScalar(GetContext(), prefix_expr, prefix_value)) { + return nullptr; + } if (prefix_value.IsNull()) { return make_uniq(Value(LogicalType::BOOLEAN)); diff --git a/src/duckdb/src/optimizer/rule/in_clause_simplification_rule.cpp b/src/duckdb/src/optimizer/rule/in_clause_simplification_rule.cpp index d1a3c6e3b..0b9b5974b 100644 --- a/src/duckdb/src/optimizer/rule/in_clause_simplification_rule.cpp +++ b/src/duckdb/src/optimizer/rule/in_clause_simplification_rule.cpp @@ -24,6 +24,10 @@ unique_ptr InClauseSimplificationRule::Apply(LogicalOperator &op, ve return nullptr; } auto &cast_expression = expr.GetChildrenMutable()[0]->Cast(); + if (BoundCastExpression::IsTryCast(cast_expression)) { + // a TRY_CAST turns failing values into NULL, which is lost when the cast is removed + return nullptr; + } if (BoundCastExpression::Child(cast_expression).GetExpressionClass() != ExpressionClass::BOUND_COLUMN_REF) { return nullptr; } diff --git a/src/duckdb/src/optimizer/statistics/expression/propagate_function.cpp b/src/duckdb/src/optimizer/statistics/expression/propagate_function.cpp index caa7948e3..6c8de9858 100644 --- a/src/duckdb/src/optimizer/statistics/expression/propagate_function.cpp +++ b/src/duckdb/src/optimizer/statistics/expression/propagate_function.cpp @@ -190,7 +190,7 @@ unique_ptr StatisticsPropagator::PropagateMonotoneBounds(ClientC return nullptr; } if (out_hi < out_lo) { - throw InternalException("Monotonic arg annotation violated for '%s': output min exceeds output max", + throw InternalException("Monotonic arg annotation violated for %s: output min exceeds output max", func.Function().GetName()); } diff --git a/src/duckdb/src/optimizer/statistics/operator/propagate_aggregate.cpp b/src/duckdb/src/optimizer/statistics/operator/propagate_aggregate.cpp index 4de83ed3c..8eee553e1 100644 --- a/src/duckdb/src/optimizer/statistics/operator/propagate_aggregate.cpp +++ b/src/duckdb/src/optimizer/statistics/operator/propagate_aggregate.cpp @@ -479,11 +479,16 @@ unique_ptr StatisticsPropagator::PropagateStatistics(LogicalAggr // handle the groups: simply propagate statistics and assign the stats to the group binding aggr.group_stats.resize(aggr.groups.size()); + // with multiple grouping sets a key appearing in several sets is emitted once per set + const bool group_rows_can_repeat = aggr.grouping_sets.size() > 1; for (idx_t group_idx = 0; group_idx < aggr.groups.size(); group_idx++) { auto stats = PropagateExpression(aggr.groups[group_idx]); if (stats && GroupingSetCanIntroduceNull(aggr, group_idx)) { stats->Set(StatsInfo::CAN_HAVE_NULL_VALUES); } + if (stats && group_rows_can_repeat) { + stats->ResetAdditiveStatistics(); + } aggr.group_stats[group_idx] = stats ? stats->ToUnique() : nullptr; if (!stats) { continue; diff --git a/src/duckdb/src/optimizer/statistics/operator/propagate_cross_product.cpp b/src/duckdb/src/optimizer/statistics/operator/propagate_cross_product.cpp index 5e9b7529f..5fd3eaf05 100644 --- a/src/duckdb/src/optimizer/statistics/operator/propagate_cross_product.cpp +++ b/src/duckdb/src/optimizer/statistics/operator/propagate_cross_product.cpp @@ -8,6 +8,9 @@ unique_ptr StatisticsPropagator::PropagateStatistics(LogicalCros // first propagate statistics in the child node auto left_stats = PropagateStatistics(cp.children[0]); auto right_stats = PropagateStatistics(cp.children[1]); + // a cross product emits every row of a side once per row of the other side + ResetAdditiveStatistics(cp.children[0]->GetColumnBindings()); + ResetAdditiveStatistics(cp.children[1]->GetColumnBindings()); if (!left_stats || !right_stats) { return nullptr; } diff --git a/src/duckdb/src/optimizer/statistics/operator/propagate_join.cpp b/src/duckdb/src/optimizer/statistics/operator/propagate_join.cpp index d937e40e9..4f510755e 100644 --- a/src/duckdb/src/optimizer/statistics/operator/propagate_join.cpp +++ b/src/duckdb/src/optimizer/statistics/operator/propagate_join.cpp @@ -213,12 +213,26 @@ void StatisticsPropagator::PropagateStatistics(LogicalComparisonJoin &join, uniq break; } } + + // join conditions can be evaluated over candidate pairs that repeat rows of either side, + // so their verification statistics must not carry additive totals + for (auto &condition : join.conditions) { + if (condition.IsComparison()) { + ResetAdditiveVerificationStats(condition.GetLHS()); + ResetAdditiveVerificationStats(condition.GetRHS()); + } else { + ResetAdditiveVerificationStats(condition.GetJoinExpression()); + } + } } void StatisticsPropagator::PropagateStatistics(LogicalAnyJoin &join, unique_ptr &node_ptr) { // propagate the expression into the join condition // note that a condition that is TRUE_OR_NULL does not always match: a NULL condition rejects the pair PropagateExpression(join.condition); + // the condition is evaluated over candidate pairs that repeat rows of either side, + // so its verification statistics must not carry additive totals + ResetAdditiveVerificationStats(*join.condition); switch (ClassifyFilter(*join.condition)) { case FilterPropagateResult::FILTER_ALWAYS_TRUE: HandleJoinAlwaysMatches(join, node_ptr); @@ -285,6 +299,17 @@ unique_ptr StatisticsPropagator::PropagateStatistics(LogicalJoin right_bindings = join.children[1]->GetColumnBindings(); } + // joins that can emit a row of a side multiple times invalidate that side's additive statistics + bool duplicates_left = join_type == JoinType::INNER || join_type == JoinType::LEFT || + join_type == JoinType::RIGHT || join_type == JoinType::OUTER; + bool duplicates_right = duplicates_left || join_type == JoinType::SINGLE; + if (duplicates_left) { + ResetAdditiveStatistics(join.children[0]->GetColumnBindings()); + } + if (duplicates_right) { + ResetAdditiveStatistics(join.children[1]->GetColumnBindings()); + } + // then propagate into the join conditions switch (join.type) { case LogicalOperatorType::LOGICAL_COMPARISON_JOIN: diff --git a/src/duckdb/src/optimizer/statistics_propagator.cpp b/src/duckdb/src/optimizer/statistics_propagator.cpp index fc928ae1f..d8d75289f 100644 --- a/src/duckdb/src/optimizer/statistics_propagator.cpp +++ b/src/duckdb/src/optimizer/statistics_propagator.cpp @@ -44,6 +44,25 @@ unique_ptr StatisticsPropagator::PropagateChildren(LogicalOperat return nullptr; } +void StatisticsPropagator::ResetAdditiveStatistics(const vector &bindings) { + // additive statistics (e.g. total_string_length) describe a fixed row population - an operator that can emit an + // input row more than once invalidates them for all pass-through columns + for (auto &binding : bindings) { + auto entry = statistics_map.find(binding); + if (entry != statistics_map.end() && entry->second) { + entry->second->ResetAdditiveStatistics(); + } + } +} + +void StatisticsPropagator::ResetAdditiveVerificationStats(Expression &expr) { + auto &stats = expr.GetVerificationStats(); + if (stats) { + stats->ResetAdditiveStatistics(); + } + ExpressionIterator::EnumerateChildren(expr, [&](Expression &child) { ResetAdditiveVerificationStats(child); }); +} + unique_ptr StatisticsPropagator::PropagateStatistics(LogicalOperator &node, unique_ptr &node_ptr) { propagation_path.emplace_back(node_ptr); @@ -100,6 +119,11 @@ unique_ptr StatisticsPropagator::PropagateStatistics(LogicalOper case LogicalOperatorType::LOGICAL_WINDOW: result = PropagateStatistics(node.Cast(), node_ptr); break; + case LogicalOperatorType::LOGICAL_UNNEST: + result = PropagateChildren(node, node_ptr); + // unnest emits its input row once per unnested entry + ResetAdditiveStatistics(node.children[0]->GetColumnBindings()); + break; default: result = PropagateChildren(node, node_ptr); } diff --git a/src/duckdb/src/parallel/scan_read_ahead.cpp b/src/duckdb/src/parallel/scan_read_ahead.cpp index a789974ba..e6012c912 100644 --- a/src/duckdb/src/parallel/scan_read_ahead.cpp +++ b/src/duckdb/src/parallel/scan_read_ahead.cpp @@ -1,6 +1,7 @@ #include "duckdb/parallel/scan_read_ahead.hpp" +#include "duckdb/common/file_system.hpp" #include "duckdb/function/table_function.hpp" #include "duckdb/logging/log_type.hpp" #include "duckdb/logging/logger.hpp" @@ -91,18 +92,11 @@ class ReadAheadIOTask : public BaseExecutorTask { ScanReadAhead &read_ahead; }; -//! Async task that opens one file ahead of decoding and releases the pending-open count when destroyed +//! Async task that opens one file ahead of decoding class FileOpenTask : public BaseExecutorTask { public: - FileOpenTask(TaskExecutor &executor, shared_ptr> pending_opens_p, std::function open_fn_p, - std::function cancel_fn_p) - : BaseExecutorTask(executor), pending_opens(std::move(pending_opens_p)), open_fn(std::move(open_fn_p)), - cancel_fn(std::move(cancel_fn_p)) { - ++*pending_opens; - } - ~FileOpenTask() override { - // the executor counts the task as finished before it is destroyed, the read-ahead may be gone by now - --*pending_opens; + FileOpenTask(TaskExecutor &executor, std::function open_fn_p, std::function cancel_fn_p) + : BaseExecutorTask(executor), open_fn(std::move(open_fn_p)), cancel_fn(std::move(cancel_fn_p)) { } void ExecuteTask() override { @@ -115,21 +109,51 @@ class FileOpenTask : public BaseExecutorTask { } private: - shared_ptr> pending_opens; std::function open_fn; std::function cancel_fn; }; ScanReadAheadJob::~ScanReadAheadJob() = default; +//! The file descriptor limit is per process, so all scans share one count +static atomic &OpenFileCount() { + static atomic count {0}; + return count; +} + +ReadAheadOpenFile::ReadAheadOpenFile() { + ++OpenFileCount(); +} + +ReadAheadOpenFile::~ReadAheadOpenFile() { + --OpenFileCount(); +} + +idx_t ReadAheadOpenFile::Count() { + return OpenFileCount().load(); +} + +static idx_t GetOpenFileBudget(ClientContext &context) { + idx_t file_descriptor_limit = Settings::Get(context); + if (file_descriptor_limit == 0) { + auto system_limit = FileSystem::GetFileDescriptorLimit(); + if (!system_limit.IsValid()) { + return NumericLimits::Maximum(); + } + file_descriptor_limit = system_limit.GetIndex(); + } + // files hold their descriptors until their readers close, so only use a share of the limit + return FileSystem::GetFileDescriptorShare(file_descriptor_limit); +} + ScanReadAhead::ScanReadAhead(ClientContext &context, idx_t read_ahead_depth_p, unique_ptr memory_governor_p) : context(context), read_ahead_depth(read_ahead_depth_p), memory_governor(std::move(memory_governor_p)), open_window(MaxValue(TaskScheduler::GetScheduler(context).NumberOfAsyncThreads() * 2, - memory_governor ? 0 : read_ahead_depth)) { + memory_governor ? 0 : read_ahead_depth)), + open_file_budget(GetOpenFileBudget(context)) { D_ASSERT(read_ahead_depth_p > 0); backlog_budget = memory_governor ? memory_governor->BackpressureBudget() : NumericLimits::Maximum(); - pending_opens = make_shared_ptr>(0); executor = make_shared_ptr(context, TaskSchedulerType::ASYNC); } @@ -257,10 +281,11 @@ ScanReadAheadAcquire ScanReadAhead::AcquireJob(ClientContext &context, TableFunc continue; } // park until the job's scheduled I/O completes, the last I/O task to finish wakes the scan task - if (job->io_completion->TryPark(data_p)) { + // a parked task frees its thread for one that opens another file, so not while the open file budget is full + if (CanScheduleOpen(0) && job->io_completion->TryPark(data_p)) { return ScanReadAheadAcquire::PARKED; } - // parking is not available to this caller or the I/O has already completed + // parking is not available to this caller, the open file budget is full or the I/O has already completed WaitForJob(*job); return ScanReadAheadAcquire::ACQUIRED; } @@ -272,12 +297,16 @@ void ScanReadAhead::SetDone() { idx_t ScanReadAhead::ReservationDepth() const { // over budget one job stays queued, claimed jobs hold the budget until their task resumes - return pending_io_bytes.load() >= backlog_budget.load() ? 1 : read_ahead_depth; + const bool over_budget = + pending_io_bytes.load() >= backlog_budget.load() || ReadAheadOpenFile::Count() >= open_file_budget; + return over_budget ? 1 : read_ahead_depth; } bool ScanReadAhead::TryReserveSlot() { // take the minimum job charge up front so concurrent producers cannot all pass the budget check together - const bool over_budget = pending_io_bytes.fetch_add(MINIMUM_JOB_IO_CHARGE) >= backlog_budget.load(); + const bool over_memory_budget = pending_io_bytes.fetch_add(MINIMUM_JOB_IO_CHARGE) >= backlog_budget.load(); + // queued jobs keep their files open, so the open file budget bounds the queue too + const bool over_budget = over_memory_budget || ReadAheadOpenFile::Count() >= open_file_budget; const idx_t depth = over_budget ? 1 : read_ahead_depth; if (active_jobs.fetch_add(1) >= depth) { --active_jobs; @@ -336,12 +365,11 @@ void ScanReadAhead::PushError(ErrorData error) { } void ScanReadAhead::ScheduleFileOpen(std::function open_fn, std::function cancel_fn) { - // the task holds the count from construction to destruction, so it stays balanced even if scheduling throws - executor->ScheduleTask(make_uniq(*executor, pending_opens, std::move(open_fn), std::move(cancel_fn))); + executor->ScheduleTask(make_uniq(*executor, std::move(open_fn), std::move(cancel_fn))); } -bool ScanReadAhead::CanScheduleOpen() const { - return pending_opens->load() < open_window; +bool ScanReadAhead::CanScheduleOpen(idx_t files_ahead) const { + return files_ahead < open_window && ReadAheadOpenFile::Count() < open_file_budget; } bool ScanReadAhead::TryRunPendingTask() { diff --git a/src/duckdb/src/parallel/task_scheduler.cpp b/src/duckdb/src/parallel/task_scheduler.cpp index a4106a020..3a1339fef 100644 --- a/src/duckdb/src/parallel/task_scheduler.cpp +++ b/src/duckdb/src/parallel/task_scheduler.cpp @@ -32,6 +32,7 @@ TaskScheduler::TaskScheduler(DatabaseInstance &db) : db(db) { pools[i] = make_uniq(db, static_cast(i)); queues[i] = make_uniq(static_cast(i)); } + BlockAllocator::Get(db).SetScheduler(*this); } TaskScheduler::~TaskScheduler() { @@ -40,11 +41,12 @@ TaskScheduler::~TaskScheduler() { for (auto &pool : pools) { pool->RelaunchThreads(*this, true); } - BlockAllocator::Get(db).FlushAll(); + BlockAllocator::Get(db).FlushOnShutdown(); } catch (...) { // nothing we can do in the destructor if this fails } #endif + BlockAllocator::Get(db).ClearScheduler(); } TaskScheduler &TaskScheduler::GetScheduler(ClientContext &context) { @@ -113,10 +115,6 @@ bool TaskScheduler::GetTaskInternal(ConsumerToken &consumer, shared_ptr &t return false; } -void TaskScheduler::ExecuteForever(atomic *marker) { - ExecuteForever(marker, TaskSchedulerType::REGULAR); -} - bool TaskScheduler::TryDequeueAndProcessTask(const DBConfig &config, TaskSchedulerQueue &queue, ConsumerToken &consumer, shared_ptr &task) { if (queue.Dequeue(consumer, task)) { @@ -153,8 +151,27 @@ bool TaskScheduler::TryDequeueAndProcessTask(const DBConfig &config, TaskSchedul return false; } -void TaskScheduler::ExecuteForever(atomic *marker, const TaskSchedulerType pool_type) { #ifndef DUCKDB_NO_THREADS +static bool SetThreadAffinity(idx_t cpu_id) { +#if defined(__GLIBC__) + if (cpu_id >= CPU_SETSIZE) { + return false; + } + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(cpu_id, &cpuset); + return pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset) == 0; +#else + return false; +#endif +} +#endif + +void TaskScheduler::ExecuteForever(atomic *marker, const TaskSchedulerType pool_type, optional_idx cpu) { +#ifndef DUCKDB_NO_THREADS + if (cpu.IsValid() && SetThreadAffinity(cpu.GetIndex())) { + GetPinnedCPUId() = cpu; + } static constexpr int64_t INITIAL_FLUSH_WAIT = 500000; // initial wait time of 0.5s (in mus) before flushing const auto &block_allocator = BlockAllocator::Get(db); @@ -174,16 +191,19 @@ void TaskScheduler::ExecuteForever(atomic *marker, const TaskSchedulerType Settings::Get(db), StringUtil::ParseFormattedBytes(Settings::Get(db)), GetPool(TaskSchedulerType::REGULAR).NumberOfThreads()); - auto decay_delay = Allocator::DecayDelay(); + auto decay_delay = block_allocator.DecayDelay(); if (!decay_delay.IsValid()) { // no decay delay specified - just wait pool.Wait(); } else { if (!pool.Wait(UnsafeNumericCast(decay_delay.GetIndex()) * 1000000 - INITIAL_FLUSH_WAIT)) { // in total, the thread was idle for the entire decay delay (note: seconds converted to mus) - // mark it as idle and start an untimed wait - Allocator::ThreadIdle(); - pool.Wait(); + // Another thread may have returned allocations during this worker's idle interval. + // Keep the wait timed until those cached allocations have had a chance to decay. + block_allocator.ThreadIdle(); + if (!block_allocator.HasPendingCacheDecay()) { + pool.Wait(); + } } } } @@ -203,7 +223,7 @@ void TaskScheduler::ExecuteForever(atomic *marker, const TaskSchedulerType if (block_allocator.SupportsFlush()) { block_allocator.ThreadFlush(Settings::Get(db), 0, GetPool(TaskSchedulerType::REGULAR).NumberOfThreads()); - Allocator::ThreadIdle(); + block_allocator.ThreadExit(); } #else throw NotImplementedException("DuckDB was compiled without threads! Background thread loop is not allowed."); @@ -358,7 +378,20 @@ void TaskScheduler::YieldThread() { #endif } +optional_idx &TaskScheduler::GetPinnedCPUId() { +#ifndef DUCKDB_NO_THREADS + static thread_local optional_idx cpu_id; +#else + static optional_idx cpu_id; +#endif + return cpu_id; +} + idx_t TaskScheduler::GetEstimatedCPUId() { + const auto &pinned_cpu_id = GetPinnedCPUId(); + if (pinned_cpu_id.IsValid()) { + return pinned_cpu_id.GetIndex(); + } #if defined(__EMSCRIPTEN__) // FIXME: Wasm + multithreads can likely be implemented as // return return (idx_t)std::hash()(std::this_thread::get_id()); diff --git a/src/duckdb/src/parallel/task_scheduler_pool.cpp b/src/duckdb/src/parallel/task_scheduler_pool.cpp index 32b9d52ac..b034ed4cb 100644 --- a/src/duckdb/src/parallel/task_scheduler_pool.cpp +++ b/src/duckdb/src/parallel/task_scheduler_pool.cpp @@ -18,9 +18,6 @@ #elif defined(__GNUC__) #include #include -#if defined(__GLIBC__) -#include -#endif #endif namespace duckdb { @@ -103,21 +100,6 @@ static vector GetProcessCPUMask() { #endif } -static void SetThreadAffinity(thread &thread, const vector &available_cpus, idx_t thread_idx) { -#if defined(__GLIBC__) - if (thread_idx < available_cpus.size()) { - const auto cpu_id = available_cpus[thread_idx]; - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - CPU_SET(cpu_id, &cpuset); - - // note that we don't care about the return value here - // if we did not manage to set affinity, the thread just does not have affinity, which is OK - pthread_setaffinity_np(thread.native_handle(), sizeof(cpu_set_t), &cpuset); - } -#endif -} - // Returns the available CPU ids when the pool should pin its threads, or an empty vector to skip pinning. // Thread count means the number of threads that the pool should contain. static vector GetAvailableCPUsForPinning(TaskSchedulerType pool_type, ThreadPinMode pin_thread_mode, @@ -150,12 +132,6 @@ static vector GetAvailableCPUsForPinning(TaskSchedulerType pool_type, Threa } #endif -#ifndef DUCKDB_NO_THREADS -static void ThreadExecuteTasks(TaskScheduler *scheduler, atomic *marker, const TaskSchedulerType pool_type) { - scheduler->ExecuteForever(marker, pool_type); -} -#endif - void TaskSchedulerPool::RelaunchThreads(TaskScheduler &scheduler, bool destroy) { #ifndef DUCKDB_NO_THREADS auto &config = DBConfig::GetConfig(db); @@ -199,10 +175,10 @@ void TaskSchedulerPool::RelaunchThreads(TaskScheduler &scheduler, bool destroy) auto marker = unique_ptr>(new atomic(true)); unique_ptr worker_thread; try { - worker_thread = make_uniq(ThreadExecuteTasks, &scheduler, marker.get(), pool_type); - if (!available_cpus.empty()) { - SetThreadAffinity(*worker_thread, available_cpus, threads.size()); - } + const auto cpu = threads.size() < available_cpus.size() ? optional_idx(available_cpus[threads.size()]) + : optional_idx(); + worker_thread = + make_uniq(&TaskScheduler::ExecuteForever, &scheduler, marker.get(), pool_type, cpu); } catch (std::exception &ex) { // thread constructor failed - this can happen when the system has too many threads allocated // in this case we cannot allocate more threads - stop launching them @@ -215,7 +191,7 @@ void TaskSchedulerPool::RelaunchThreads(TaskScheduler &scheduler, bool destroy) } } current_thread_count = threads.size() + (pool_type == TaskSchedulerType::REGULAR ? external_threads : 0); - // on destroy the scheduler flushes once after every pool is joined + // On destroy the scheduler flushes fallback allocations after joining all pools. if (!destroy) { BlockAllocator::Get(db).FlushAll(); } diff --git a/src/duckdb/src/parser/column_list.cpp b/src/duckdb/src/parser/column_list.cpp index ca59c1216..2a5a30dc3 100644 --- a/src/duckdb/src/parser/column_list.cpp +++ b/src/duckdb/src/parser/column_list.cpp @@ -95,7 +95,7 @@ const ColumnDefinition &ColumnList::GetColumn(PhysicalIndex physical) const { const ColumnDefinition &ColumnList::GetColumn(const Identifier &name) const { auto entry = name_map.find(name); if (entry == name_map.end()) { - throw InternalException("Column with name \"%s\" does not exist", name); + throw InternalException("Column with name %s does not exist", name); } auto logical_index = entry->second; D_ASSERT(logical_index < columns.size()); diff --git a/src/duckdb/src/parser/expression/function_expression.cpp b/src/duckdb/src/parser/expression/function_expression.cpp index bd15e9955..9eafc6957 100644 --- a/src/duckdb/src/parser/expression/function_expression.cpp +++ b/src/duckdb/src/parser/expression/function_expression.cpp @@ -158,6 +158,10 @@ void FunctionExpression::Serialize(Serializer &serializer) const { // the catalog/schema properties above cannot represent a nested schema path serializer.WriteProperty(210, "qualified_name", qualified_name); } + + if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(211, "is_legacy_function_call", is_legacy_function_call); + } } unique_ptr FunctionExpression::Deserialize(Deserializer &deserializer) { @@ -199,6 +203,12 @@ unique_ptr FunctionExpression::Deserialize(Deserializer &deser result->SetQualifiedName(std::move(qualified_name)); } + bool is_legacy_function_call = false; + deserializer.ReadPropertyWithDefault(211, "is_legacy_function_call", is_legacy_function_call); + if (is_legacy_function_call) { + result->is_legacy_function_call = true; + } + return std::move(result); } diff --git a/src/duckdb/src/parser/expression/window_expression.cpp b/src/duckdb/src/parser/expression/window_expression.cpp index a4f5f76e4..086e6d5c9 100644 --- a/src/duckdb/src/parser/expression/window_expression.cpp +++ b/src/duckdb/src/parser/expression/window_expression.cpp @@ -144,6 +144,10 @@ void WindowExpression::Serialize(Serializer &serializer) const { // the catalog/schema properties above cannot represent a nested schema path serializer.WriteProperty(219, "qualified_name", qualified_name); } + + if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(220, "is_legacy_function_call", is_legacy_function_call); + } } unique_ptr WindowExpression::Deserialize(Deserializer &deserializer) { @@ -193,6 +197,12 @@ unique_ptr WindowExpression::Deserialize(Deserializer &deseria result->SetQualifiedName(std::move(qualified_name)); } + bool is_legacy_function_call = false; + deserializer.ReadPropertyWithDefault(220, "is_legacy_function_call", is_legacy_function_call); + if (is_legacy_function_call) { + result->is_legacy_function_call = true; + } + return std::move(result); } diff --git a/src/duckdb/src/parser/parsed_data/alter_table_info.cpp b/src/duckdb/src/parser/parsed_data/alter_table_info.cpp index b236366a4..356bb505d 100644 --- a/src/duckdb/src/parser/parsed_data/alter_table_info.cpp +++ b/src/duckdb/src/parser/parsed_data/alter_table_info.cpp @@ -628,6 +628,9 @@ unique_ptr AddConstraintInfo::Copy() const { string AddConstraintInfo::ToString() const { string result = "ALTER TABLE "; + if (if_not_found == OnEntryNotFound::RETURN_NULL) { + result += "IF EXISTS "; + } result += GetQualifiedName().ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); result += " ADD "; result += constraint->ToString(); @@ -659,6 +662,9 @@ unique_ptr SetPartitionedByInfo::Copy() const { string SetPartitionedByInfo::ToString() const { string result = "ALTER TABLE "; + if (if_not_found == OnEntryNotFound::RETURN_NULL) { + result += "IF EXISTS "; + } result += GetQualifiedName().ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); if (partition_keys.empty()) { result += " RESET PARTITIONED BY"; @@ -698,6 +704,9 @@ unique_ptr SetSortedByInfo::Copy() const { string SetSortedByInfo::ToString() const { string result = "ALTER TABLE "; + if (if_not_found == OnEntryNotFound::RETURN_NULL) { + result += "IF EXISTS "; + } result += GetQualifiedName().ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); if (orders.empty()) { result += " RESET SORTED BY"; @@ -738,6 +747,9 @@ unique_ptr SetTableOptionsInfo::Copy() const { string SetTableOptionsInfo::ToString() const { string result = "ALTER TABLE "; + if (if_not_found == OnEntryNotFound::RETURN_NULL) { + result += "IF EXISTS "; + } result += GetQualifiedName().ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); result += " SET ("; idx_t i = 0; @@ -775,6 +787,9 @@ unique_ptr ResetTableOptionsInfo::Copy() const { string ResetTableOptionsInfo::ToString() const { string result = "ALTER TABLE "; + if (if_not_found == OnEntryNotFound::RETURN_NULL) { + result += "IF EXISTS "; + } result += GetQualifiedName().ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); result += " RESET ("; idx_t i = 0; diff --git a/src/duckdb/src/parser/parsed_data/load_info.cpp b/src/duckdb/src/parser/parsed_data/load_info.cpp index 25b2c3ba8..15f198b7f 100644 --- a/src/duckdb/src/parser/parsed_data/load_info.cpp +++ b/src/duckdb/src/parser/parsed_data/load_info.cpp @@ -86,7 +86,7 @@ string LoadInfo::ToString() const { } string result = ""; result += LoadInfoToString(load_type, load_after_install); - result += StringUtil::Format(" '%s'", filename); + result += " " + SQLString(filename); if (!repository.empty()) { if (repo_is_alias) { result += " FROM " + SQLIdentifier(repository); diff --git a/src/duckdb/src/parser/peg/compiled_grammar.cpp b/src/duckdb/src/parser/peg/compiled_grammar.cpp index 3cf3f2878..391617b9a 100644 --- a/src/duckdb/src/parser/peg/compiled_grammar.cpp +++ b/src/duckdb/src/parser/peg/compiled_grammar.cpp @@ -4,6 +4,8 @@ #include "duckdb/common/string_util.hpp" #include "duckdb/parser/grammar_extension.hpp" +#include + namespace duckdb { CompiledGrammar::CompiledGrammar(MatcherAllocator &&allocator_p, unique_ptr &&keyword_helper_p, @@ -12,6 +14,26 @@ CompiledGrammar::CompiledGrammar(MatcherAllocator &&allocator_p, unique_ptr(PEGRuleId::BUILTIN_COUNT)); + vector> runtime_rules; + for (auto &entry : rules) { + auto &rule = *entry.second; + rule.rule_id = PEGRuleIdFromString(rule.name); + if (rule.rule_id == PEGRuleId::INVALID) { + runtime_rules.push_back(rule); + continue; + } + rules_by_id[static_cast(rule.rule_id)] = rule; + } + // the rule map has no stable order, so sort by name to assign the same runtime IDs to the same rule set + std::sort(runtime_rules.begin(), runtime_rules.end(), + [](const reference &a, const reference &b) { + return StringUtil::CILessThan(a.get().name, b.get().name); + }); + for (auto &rule : runtime_rules) { + rule.get().rule_id = static_cast(rules_by_id.size()); + rules_by_id.push_back(rule.get()); + } } static void ValidateParsedGrammarRoots(const ParsedGrammar &grammar) { @@ -144,6 +166,7 @@ shared_ptr CompiledGrammar::Create(const vectorGetLiteralTable()); + allocator.InitializeListChains(); auto new_matcher = shared_ptr(new CompiledGrammar(std::move(allocator), std::move(keyword_helper), std::move(tokenizer), std::move(rules), @@ -160,6 +183,14 @@ shared_ptr CompiledGrammar::Create() { return Create({}); } +optional_ptr CompiledGrammar::GetRule(PEGRuleId rule_id) const { + auto index = static_cast(rule_id); + if (index >= rules_by_id.size()) { + return nullptr; + } + return rules_by_id[index]; +} + optional_ptr CompiledGrammar::GetRule(const string &rule_name) const { auto entry = rules.find(rule_name); if (entry == rules.end()) { diff --git a/src/duckdb/src/parser/peg/matcher.cpp b/src/duckdb/src/parser/peg/matcher.cpp index 8874b7e17..23f624352 100644 --- a/src/duckdb/src/parser/peg/matcher.cpp +++ b/src/duckdb/src/parser/peg/matcher.cpp @@ -2,6 +2,7 @@ #include "duckdb/parser/peg/matcher_stack.hpp" #include "duckdb/parser/peg/compiled_grammar.hpp" #include "duckdb/parser/peg/matcher_factory.hpp" +#include "duckdb/parser/peg/matcher/list_matcher.hpp" #include "duckdb/main/database.hpp" #include "duckdb/parser/peg/transformer/peg_transformer.hpp" @@ -59,6 +60,14 @@ Matcher &MatcherAllocator::Allocate(unique_ptr matcher) { return result; } +void MatcherAllocator::InitializeListChains() { + for (auto &matcher : matchers) { + if (matcher->IsStructural() && matcher->Type() == MatcherType::LIST) { + matcher->Cast().InitializeListChain(); + } + } +} + ParseResultAllocator::ParseResultAllocator() : arena(Allocator::DefaultAllocator()), pending_destructors(arena) { } diff --git a/src/duckdb/src/parser/peg/matcher_process.cpp b/src/duckdb/src/parser/peg/matcher_process.cpp index da631f6ce..2ebe4ce0b 100644 --- a/src/duckdb/src/parser/peg/matcher_process.cpp +++ b/src/duckdb/src/parser/peg/matcher_process.cpp @@ -50,65 +50,115 @@ arena_ptr AtomicMatcher::StartMatch(MatchState &state) const { } class ListMatchProcess : public MatchProcess { +private: + struct PendingList { + PendingList(const ListMatcher &matcher, optional_ptr parent) : matcher(matcher), parent(parent) { + } + + const ListMatcher &matcher; + optional_ptr parent; + }; + public: ListMatchProcess(const ListMatcher &matcher_p, MatchState &state_p) : matcher(matcher_p), state(state_p), list_state(state_p), results(state_p.context.process_allocator) { - if (state_p.BuildParseResult()) { - results.reserve(matcher_p.matchers.size()); - } - saved_suggestion_size = matcher.suppress_suggestions ? list_state.context.suggestions.size() : 0; - if (auto current = list_state.token_iterator.Current()) { + saved_suggestion_size = matcher_p.suppress_suggestions ? list_state.context.suggestions.size() : 0; + auto current = list_state.token_iterator.Current(); + if (current) { start_offset = optional_idx(current->offset); } + if (state.BuildParseResult() && (!current || current->type != TokenType::END_OF_INPUT_AUTOCOMPLETE)) { + while (auto child = matcher.get().GetChainChild()) { + if (!child->first_set.MightMatch(list_state)) { + break; + } + pending = state.context.process_allocator.Make(matcher.get(), pending); + matcher = *child; + } + } + ReserveResults(); } MatchStep Resume(optional child_result) override { D_ASSERT(awaiting_child == child_result.has_value()); - if (child_result) { - awaiting_child = false; - if (!child_result->IsSuccess()) { - DiscardSuggestions(); - return MatchStep::Complete(MatcherResult::Failure()); - } - if (child_result->HasParseResult()) { - results.emplace_back(*child_result->GetParseResult()); - } - child_index++; - } - while (child_index < matcher.matchers.size()) { - auto &child_matcher = matcher.matchers[child_index].get(); - auto current = list_state.token_iterator.Current(); - bool at_autocomplete_cursor = current && current->type == TokenType::END_OF_INPUT_AUTOCOMPLETE; - if (!at_autocomplete_cursor) { - awaiting_child = true; - return MatchStep::Child({child_matcher, list_state}); + while (true) { + if (child_result) { + awaiting_child = false; + if (!child_result->IsSuccess()) { + DiscardSuggestions(); + return MatchStep::Complete(MatcherResult::Failure()); + } + if (child_result->HasParseResult()) { + results.emplace_back(*child_result->GetParseResult()); + } + child_index++; } - if (matcher.suppress_suggestions) { - DiscardSuggestions(); + auto ¤t_matcher = matcher.get(); + while (child_index < current_matcher.matchers.size()) { + auto &child_matcher = current_matcher.matchers[child_index].get(); + auto current = list_state.token_iterator.Current(); + bool at_autocomplete_cursor = current && current->type == TokenType::END_OF_INPUT_AUTOCOMPLETE; + if (!at_autocomplete_cursor) { + awaiting_child = true; + return MatchStep::Child({child_matcher, list_state}); + } + if (current_matcher.suppress_suggestions) { + DiscardSuggestions(); + return MatchStep::Complete(MatcherResult::Failure()); + } + if (child_matcher.AddSuggestion(list_state) == SuggestionType::OPTIONAL) { + child_index++; + continue; + } + if (!pending) { + state.token_iterator.SetPosition(list_state.token_iterator); + } return MatchStep::Complete(MatcherResult::Failure()); } - if (child_matcher.AddSuggestion(list_state) == SuggestionType::OPTIONAL) { - child_index++; - continue; + if (!pending) { + state.token_iterator.SetPosition(list_state.token_iterator); } - state.token_iterator.SetPosition(list_state.token_iterator); - return MatchStep::Complete(MatcherResult::Failure()); + auto result = CompleteList(); + if (!pending) { + return MatchStep::Complete(result); + } + list_state.rule = current_matcher.GetRule(); + list_state.added_suggestions.reset(); + matcher = pending->matcher; + pending = pending->parent; + results.clear(); + ReserveResults(); + child_index = 0; + child_result = result; + } + } + +private: + void ReserveResults() { + if (state.BuildParseResult()) { + results.reserve(matcher.get().matchers.size()); } - state.token_iterator.SetPosition(list_state.token_iterator); + } + + MatcherResult CompleteList() { + auto ¤t_matcher = matcher.get(); DiscardSuggestions(); - if (matcher.IsCollapsible()) { + if (current_matcher.IsCollapsible()) { auto collapsible = FindCollapsibleResult(); if (collapsible) { collapsible->collapsed = true; - return MatchStep::Complete(MatcherResult::Success(collapsible)); + return MatcherResult::Success(collapsible); } } - auto named_matcher = matcher.HasName() ? &matcher : nullptr; - return MatchStep::Complete(state.AllocateParseResult( - state.context.allocator.MakeChildren(results), named_matcher, start_offset)); + auto &result_state = pending ? list_state : state; + if (pending) { + result_state.rule = current_matcher.GetRule(); + } + auto named_matcher = current_matcher.HasName() ? ¤t_matcher : nullptr; + return result_state.AllocateParseResult(state.context.allocator.MakeChildren(results), + named_matcher, start_offset); } -private: //! The child that can stand in for this rule's own result, or nullptr when the rule has to build one optional_ptr FindCollapsibleResult() const { optional_ptr collapsible; @@ -139,7 +189,7 @@ class ListMatchProcess : public MatchProcess { } void DiscardSuggestions() { - if (!matcher.suppress_suggestions) { + if (!matcher.get().suppress_suggestions) { return; } list_state.context.suggestions.erase(list_state.context.suggestions.begin() + @@ -148,10 +198,12 @@ class ListMatchProcess : public MatchProcess { } private: - const ListMatcher &matcher; + reference matcher; MatchState &state; MatchState list_state; arena_vector> results; + //! First-child ancestors share this cursor until their tails are reached. + optional_ptr pending; idx_t child_index = 0; idx_t saved_suggestion_size = 0; optional_idx start_offset; @@ -162,6 +214,18 @@ arena_ptr ListMatcher::StartMatch(MatchState &state) const { return state.Make(*this, state); } +void ListMatcher::InitializeListChain() { + first_child_is_chainable = false; + if (!IsStructural() || suppress_suggestions || matchers.empty()) { + return; + } + auto &first = matchers[0].get(); + if (!first.IsStructural() || first.Type() != MatcherType::LIST || first.IsPackratMemoized()) { + return; + } + first_child_is_chainable = !first.Cast().suppress_suggestions; +} + template class ChoiceMatchProcess : public MatchProcess { public: @@ -193,6 +257,15 @@ class ChoiceMatchProcess : public MatchProcess { child_index++; child_state.reset(); } + if (!SINGLE_CHILD && state.BuildParseResult()) { + while (child_index < matcher.matchers.size()) { + auto &child = matcher.matchers[child_index].get(); + if (!child.IsStructural() || child.first_set.MightMatch(state)) { + break; + } + child_index++; + } + } if (child_index >= matcher.matchers.size()) { return MatchStep::Complete(MatcherResult::Failure()); } diff --git a/src/duckdb/src/parser/peg/peg_rule_id.cpp b/src/duckdb/src/parser/peg/peg_rule_id.cpp new file mode 100644 index 000000000..abe1eb04e --- /dev/null +++ b/src/duckdb/src/parser/peg/peg_rule_id.cpp @@ -0,0 +1,1228 @@ +// AUTO-GENERATED by scripts/parser/generate_transformer_trampoline.py -- DO NOT EDIT + +#include "duckdb/parser/peg/peg_rule_id.hpp" +#include "duckdb/common/case_insensitive_map.hpp" + +namespace duckdb { + +static const char *const PEG_RULE_NAMES[] = { + "", + "AlterStatement", + "AlterOptions", + "AlterTableStmt", + "AlterSchemaStmt", + "AlterSchemaOptions", + "AlterTableOptions", + "AddConstraint", + "DropConstraint", + "AddColumn", + "AddColumnEntry", + "DropColumn", + "AlterColumn", + "RenameColumn", + "NestedColumnName", + "IdentifierDot", + "RenameAlter", + "SetPartitionedBy", + "ResetPartitionedBy", + "SetSortedBy", + "ResetSortedBy", + "SetOptions", + "ResetOptions", + "AlterColumnEntry", + "AddOrDropDefault", + "AddDefault", + "DropDefault", + "ChangeNullability", + "DropOrSet", + "DropNullability", + "SetNullability", + "AlterType", + "SetData", + "UsingExpression", + "AlterViewStmt", + "AlterSequenceStmt", + "QualifiedSequenceName", + "AlterSequenceOptions", + "RenameAlterSequenceOptions", + "SetSequenceOption", + "AlterDatabaseStmt", + "AnalyzeStatement", + "AnalyzeTarget", + "AnalyzeVerbose", + "AttachStatement", + "Database", + "DatabasePath", + "AttachAlias", + "AttachOptions", + "CallStatement", + "CheckpointStatement", + "CheckpointForce", + "CommentStatement", + "CommentTarget", + "CommentStringLiteralIdentifier", + "CommentOnType", + "CommentTable", + "CommentSequence", + "CommentFunction", + "CommentMacroTable", + "CommentMacro", + "CommentView", + "CommentDatabase", + "CommentIndex", + "CommentSchema", + "CommentType", + "CommentColumn", + "CommentValue", + "StringLiteralValue", + "Program", + "TopLevelStatement", + "Statement", + "AnalyzeKeyword", + "CatalogName", + "SchemaName", + "ReservedSchemaName", + "TableName", + "ReservedTableName", + "ReservedIdentifier", + "ColumnName", + "ReservedColumnName", + "IndexName", + "ReservedIndexName", + "SettingName", + "PragmaName", + "FunctionName", + "ReservedFunctionName", + "ReservedTypeName", + "TableFunctionName", + "ConstraintName", + "SequenceName", + "CollationName", + "CopyOptionName", + "NumberLiteral", + "StringLiteral", + "Type", + "TypeVariations", + "SimpleType", + "CharacterSimpleType", + "QualifiedSimpleType", + "CharacterType", + "IntervalType", + "IntervalInterval", + "IntervalWithSpecifier", + "IntervalWithRangeSpecifier", + "IntervalWithSimpleSpecifier", + "IntervalWithoutSpecifier", + "IntervalToIntervalAsType", + "IntervalNumber", + "YearKeyword", + "MonthKeyword", + "DayKeyword", + "HourKeyword", + "MinuteKeyword", + "SecondKeyword", + "MillisecondKeyword", + "MicrosecondKeyword", + "WeekKeyword", + "QuarterKeyword", + "DecadeKeyword", + "CenturyKeyword", + "MillenniumKeyword", + "Interval", + "IntervalToInterval", + "YearToMonth", + "DayToHour", + "DayToMinute", + "DayToSecond", + "HourToMinute", + "HourToSecond", + "MinuteToSecond", + "BitType", + "GeometryType", + "VariantType", + "NumericType", + "SimpleNumericType", + "DecimalNumericType", + "IntType", + "IntegerType", + "SmallintType", + "BigintType", + "RealType", + "BooleanType", + "DoubleType", + "FloatType", + "DecimalType", + "DecType", + "NumericModType", + "QualifiedTypeName", + "TypeNameAsQualifiedName", + "CatalogReservedSchemaTypeName", + "SchemaReservedTypeName", + "TypeModifiers", + "NestedType", + "RowType", + "SetofType", + "UnionType", + "ColIdTypeList", + "MapType", + "TupleType", + "ColIdType", + "ArrayBounds", + "ArrayKeyword", + "ArrayKeywordWithBounds", + "SquareBracketsArray", + "TimeType", + "TimeOrTimestamp", + "TimeTypeId", + "TimestampTypeId", + "TimeZone", + "WithOrWithout", + "WithRule", + "WithoutRule", + "RowOrStruct", + "List", + "Parens", + "ConnectStatement", + "DisconnectStatement", + "SessionTarget", + "LocalSessionTarget", + "StringSessionTarget", + "CatalogSessionTarget", + "CopyStatement", + "CopyVariations", + "CopyTable", + "FromOrTo", + "CopyFrom", + "CopyTo", + "CopySelect", + "CopyFileName", + "CopyFileNameExpression", + "CopyFileNameStringLiteral", + "CopyFileNameIdentifier", + "CopyFileNameIdentifierColId", + "IdentifierColId", + "CopyFileNameSuffix", + "CopyLegacyOption", + "CopyOptions", + "CopyOptionList", + "SpecializedOptionList", + "SpecializedOptionTail", + "SpecializedOption", + "SingleOption", + "BinaryOption", + "FreezeOption", + "OidsOption", + "CsvOption", + "HeaderOption", + "NullAsOption", + "DelimiterAsOption", + "QuoteAsOption", + "EscapeAsOption", + "EncodingOption", + "ForceQuoteOption", + "StarSymbolColumnList", + "ForceQuote", + "PartitionByOption", + "PartitionByColumnList", + "StarPartitionByColumnList", + "ParenthesizedPartitionByColumnList", + "SinglePartitionByColumnList", + "ForceNullOption", + "ForceNotNull", + "StarSymbol", + "CopyGenericOptionList", + "CopyGenericOption", + "OrderByCopyOption", + "PartitionedByCopyOption", + "GenericCopyOptionList", + "GenericCopyOption", + "GenericCopyOptionValue", + "GenericCopyOptionOrderList", + "GenericCopyOptionExpression", + "GenericCopyOptionParenthesizedExpressionList", + "CopyFromDatabase", + "CopyFromDatabaseWithFlag", + "CopyFromDatabaseWithoutFlag", + "CopyDatabaseFlag", + "SchemaOrData", + "CopySchema", + "CopyData", + "CreateIndexStmt", + "WithList", + "RelOptionOrOids", + "RelOptionList", + "Oids", + "WithOrWithoutOids", + "WithOids", + "WithoutOids", + "IndexElement", + "UniqueIndex", + "IndexType", + "RelOption", + "RelOptionName", + "DottedColLabel", + "RelOptionArgumentOpt", + "DefArg", + "DefArgNull", + "DefArgKeyword", + "DefArgStringLiteral", + "NoneLiteral", + "CreateMacroStmt", + "MacroOrFunction", + "MacroKeyword", + "FunctionKeyword", + "MacroDefinition", + "MacroDefinitionBody", + "MacroParameters", + "MacroParameter", + "SimpleParameter", + "ScalarMacroDefinition", + "TableMacroDefinition", + "CreateSchemaStmt", + "WithOptionList", + "CreateSecretStmt", + "SecretStorageSpecifier", + "SecretName", + "CreateSequenceStmt", + "SequenceOption", + "SeqSetCycle", + "SeqCycle", + "SeqNoCycle", + "SeqSetIncrement", + "SeqSetMinMax", + "SeqNoMinMax", + "SeqStartWith", + "SeqOwnedBy", + "SeqMinOrMax", + "MinValue", + "MaxValue", + "CreateStatement", + "CreateStatementVariation", + "OrReplace", + "Temporary", + "Persistent", + "TempPersistent", + "TemporaryPersistent", + "CreateTableStmt", + "CreateTableDefinition", + "CreateTableAs", + "PartitionSortedOptions", + "PartitionOptSortedOptions", + "SortedOptPartitionOptions", + "PartitionOptions", + "SortedOptions", + "WithData", + "WithDataOnly", + "WithNoData", + "IdentifierList", + "CreateColumnList", + "IfNotExists", + "QualifiedName", + "SchemaReservedIdentifierOrStringLiteral", + "CatalogReservedSchemaIdentifier", + "IdentifierOrStringLiteral", + "ReservedIdentifierOrStringLiteral", + "CatalogQualification", + "SchemaQualification", + "ReservedSchemaQualification", + "TableQualification", + "ReservedTableQualification", + "CreateTableColumnList", + "CreateTableColumnElement", + "CreateTableColumnDefinition", + "CreateTableConstraint", + "ColumnDefinition", + "ColumnConstraint", + "NameableColumnConstraint", + "ColumnConstraintElement", + "NotNullConstraint", + "NullConstraint", + "NotNullColumnConstraint", + "UniqueConstraint", + "PrimaryKeyConstraint", + "ConstraintTiming", + "ImmediateConstraint", + "DeferredConstraint", + "DefaultValue", + "CheckConstraint", + "ForeignKeyConstraint", + "ColumnCollation", + "ColumnCompression", + "KeyActions", + "UpdateFirstKeyActions", + "DeleteFirstKeyActions", + "UpdateAction", + "DeleteAction", + "KeyAction", + "NoKeyAction", + "RestrictKeyAction", + "CascadeKeyAction", + "SetNullKeyAction", + "SetDefaultKeyAction", + "TopLevelConstraint", + "TopLevelConstraintList", + "ConstraintNameClause", + "TopCheckConstraint", + "TopPrimaryKeyConstraint", + "TopUniqueConstraint", + "TopForeignKeyConstraint", + "ColumnIdList", + "PlainIdentifier", + "QuotedIdentifier", + "DottedIdentifier", + "DotColLabel", + "Identifier", + "ColId", + "ColIdOrString", + "TypeFuncName", + "TypeFuncKeyword", + "TypeName", + "ColLabel", + "ColLabelOrString", + "ColLabelIdentifier", + "BareColLabel", + "StringLiteralIdentifier", + "GeneratedColumn", + "Generated", + "GeneratedAlways", + "GeneratedByDefault", + "GeneratedColumnType", + "CommitAction", + "PreserveOrDelete", + "PreserveRows", + "DeleteRows", + "VirtualGeneratedColumn", + "StoredGeneratedColumn", + "CreateTriggerStmt", + "TriggerBody", + "TriggerName", + "ReferencingClause", + "ReferencingItem", + "ReferencingNewTableAs", + "ReferencingOldTableAs", + "TriggerTiming", + "TriggerBefore", + "TriggerAfter", + "TriggerInsteadOf", + "TriggerEvent", + "TriggerEventInsert", + "TriggerEventDelete", + "TriggerEventUpdate", + "TriggerEventUpdateOf", + "TriggerColumnList", + "ForEachClause", + "ForEachRow", + "ForEachStatement", + "CreateTypeStmt", + "CreateType", + "CreateTypeFromType", + "EnumSelectType", + "EnumStringLiteralList", + "CreateViewStmt", + "CreateRecursive", + "CreateSecure", + "DeallocateStatement", + "DeallocatePrepare", + "DeleteStatement", + "TruncateStatement", + "TruncateOptions", + "TruncateAliasOptions", + "TruncateDropBehavior", + "TargetOptAlias", + "TargetAlias", + "DeleteUsingClause", + "DescribeStatement", + "ShowDeprecatedSelect", + "DescribeSelect", + "ShowAllTables", + "ShowTables", + "ShowByName", + "DescribeByName", + "DescribeOrSummarize", + "ShowTarget", + "ShowDeprecatedQualifiedTableName", + "ShowSettingName", + "ShowSpecialForm", + "DescribeTarget", + "DescribeBaseTableName", + "DescribeStringLiteral", + "Summarize", + "SummarizeRule", + "ShowOrDescribe", + "ShowRule", + "DescribeRule", + "DescribeLongRule", + "DescRule", + "DetachStatement", + "DropStatement", + "DropEntries", + "DropTrigger", + "DropTable", + "DropTableFunction", + "DropFunction", + "DropSchema", + "DropIndex", + "QualifiedIndexName", + "QualifiedIndexNameString", + "SchemaReservedIndex", + "CatalogReservedSchemaIndex", + "DropSequence", + "DropCollation", + "DropType", + "DropSecret", + "TableOrView", + "MaterializedViewEntry", + "FunctionTypeMacro", + "FunctionTypeMacroKeyword", + "FunctionTypeFunction", + "DropBehavior", + "CascadeDropBehavior", + "RestrictDropBehavior", + "IfExists", + "DropSecretStorage", + "ExecuteStatement", + "ExplainStatement", + "ExplainOptionList", + "ExplainOption", + "ExplainOptionName", + "ExplainSelectStatement", + "ExplainableStatements", + "ExportStatement", + "ExportSource", + "ImportStatement", + "ColumnReference", + "NestedSchemaTableColumnName", + "CatalogReservedSchemaTableColumnName", + "SchemaReservedTableColumnName", + "TableReservedColumnName", + "FunctionExpression", + "FunctionExpressionArguments", + "FunctionExpressionArgumentList", + "FunctionArgumentList", + "FunctionIdentifier", + "FunctionNameAsQualifiedName", + "CatalogReservedSchemaFunctionName", + "SchemaReservedFunctionName", + "DistinctOrAll", + "DistinctKeyword", + "AllKeyword", + "ExportClause", + "WithinGroupClause", + "FilterClause", + "FilterClauseExpression", + "FilterClauseContents", + "IgnoreOrRespectNulls", + "IgnoreNulls", + "RespectNulls", + "ParenthesisExpression", + "LiteralExpression", + "ConstantLiteral", + "NullLiteral", + "TrueLiteral", + "FalseLiteral", + "CastExpression", + "CastArguments", + "CastOrTryCast", + "CastKeyword", + "TryCastKeyword", + "ColIdDot", + "StarExpression", + "StarQualifierList", + "ExcludeList", + "ExcludeOrExcept", + "ExcludeNames", + "ExcludeNameList", + "ExcludeNameSingle", + "ExcludeName", + "ExcludeDottedName", + "ExcludeColumnName", + "ReplaceList", + "ReplaceEntries", + "ReplaceEntrySingle", + "ReplaceEntryList", + "ReplaceEntry", + "RenameList", + "RenameEntries", + "RenameEntryList", + "SingleRenameEntry", + "RenameEntry", + "SubqueryExpression", + "SubqueryNot", + "SubqueryExists", + "CaseExpression", + "CaseWhenThen", + "CaseElse", + "TypeLiteral", + "IntervalLiteral", + "IntervalParameter", + "IntervalStringParameter", + "FrameClause", + "Framing", + "RowsFraming", + "RangeFraming", + "GroupsFraming", + "FrameExtent", + "SingleFrameExtent", + "BetweenFrameExtent", + "FrameBound", + "FrameUnbounded", + "FrameExpression", + "FrameCurrentRow", + "PrecedingOrFollowing", + "PrecedingFrame", + "FollowingFrame", + "WindowExcludeClause", + "WindowExcludeElement", + "ExcludeCurrentRow", + "ExcludeGroup", + "ExcludeTies", + "ExcludeNoOthers", + "OverClause", + "WindowFrame", + "IdentifierWindowFrame", + "ParensIdentifier", + "WindowFrameDefinition", + "WindowFrameNameContentsParens", + "WindowFrameNameContents", + "WindowFrameContentsParens", + "WindowFrameContents", + "BaseWindowName", + "WindowPartition", + "ListExpression", + "ArrayBoundedListExpression", + "ArrayParensSelect", + "BoundedListExpression", + "StructExpression", + "StructField", + "MapExpression", + "MapStructExpression", + "MapStructField", + "GroupingExpression", + "GroupingOrGroupingId", + "GroupingKeyword", + "GroupingIdKeyword", + "Parameter", + "QuestionMarkNumberedParameter", + "AnonymousParameter", + "NumberedParameter", + "ColLabelParameter", + "PositionalExpression", + "DefaultExpression", + "ListComprehensionExpression", + "ListComprehensionFilter", + "ParensExpression", + "MeasureSemanticsExpression", + "SingleExpression", + "Expression", + "ColumnDefaultExpr", + "LambdaArrowExpression", + "SingleArrowPair", + "LogicalOrExpression", + "LogicalOrExpressionTail", + "ColDefOrExpr", + "ColDefOrExpressionTail", + "LogicalAndExpression", + "LogicalAndExpressionTail", + "ColDefAndExpr", + "ColDefAndExpressionTail", + "LogicalNotExpression", + "NotExpression", + "NotKeyword", + "IsExpression", + "IsExpressionContinuation", + "IsExpressionTail", + "IsTestTail", + "IsDistinctTail", + "IsComparisonTail", + "IsOtherOperatorTail", + "IsCastTail", + "IsTest", + "IsLiteral", + "IsLiteralValue", + "UnknownLiteral", + "NotNull", + "NotNullKeyword", + "NotNullOperator", + "IsNull", + "IsNullOperator", + "IsDistinctFromExpression", + "IsDistinctFromTail", + "IsDistinctFromOp", + "ComparisonExpression", + "ComparisonExpressionTail", + "ComparisonOperand", + "NotComparisonOperand", + "ComparisonOperator", + "OperatorEqual", + "OperatorNotEqual", + "OperatorLessThan", + "OperatorGreaterThan", + "OperatorLessThanEquals", + "OperatorGreaterThanEquals", + "BetweenInLikeExpression", + "InPredicate", + "BetweenLikeOp", + "BetweenLikeOpExpression", + "LikeClause", + "EscapeClause", + "LikeVariations", + "LikeToken", + "ILikeToken", + "GlobToken", + "SimilarToToken", + "RegexMatchToken", + "RegexInsensitiveMatchToken", + "NotILikeOp", + "NotLikeOp", + "NotRegexInsensitiveMatchOp", + "NotSimilarToOp", + "InClause", + "InExpression", + "InContainsExpression", + "InExpressionList", + "InSelectStatement", + "BetweenClause", + "Asymmetric", + "OtherOperatorExpression", + "InfixOtherOperatorExpression", + "CustomPrefixExpression", + "OtherOperatorTail", + "OtherOperator", + "AnyAllParsedOperator", + "NamedOtherOperator", + "OperatorLiteral", + "AnyOperatorLiteral", + "AnyAllOperator", + "AnyOrAll", + "SubqueryAny", + "SubqueryAll", + "QualifiedOperator", + "QualifiedOperatorContents", + "BitwiseExpression", + "BitwiseExpressionTail", + "BitOperator", + "TildeExpression", + "AdditiveExpression", + "AdditiveExpressionTail", + "Term", + "MultiplicativeExpression", + "MultiplicativeExpressionTail", + "Factor", + "ExponentiationExpression", + "ExponentiationExpressionTail", + "ExponentOperator", + "CollateExpression", + "CollateExpressionTail", + "CollateOperator", + "AtTimeZoneExpression", + "AtTimeZoneExpressionTail", + "AtTimeZoneOperator", + "PrefixExpression", + "PrefixOperator", + "MinusPrefixOperator", + "PlusPrefixOperator", + "TildePrefixOperator", + "BaseExpression", + "IndirectionList", + "Indirection", + "CastOperator", + "DotOperator", + "DotMethodOperator", + "DotColumnOperator", + "MethodExpression", + "MethodExpressionArguments", + "MethodExpressionArgumentList", + "MethodFunctionArguments", + "SliceExpression", + "SliceBound", + "SliceUpperBound", + "MinusStepSliceBound", + "EndStepSliceBound", + "EndSliceBound", + "StepSliceBound", + "PostfixOperator", + "SpecialFunctionExpression", + "CoalesceExpression", + "UnpackExpression", + "TryExpression", + "ColumnsExpression", + "ExtractExpression", + "ExtractArguments", + "LambdaExpression", + "NullIfExpression", + "NullIfArguments", + "PositionExpression", + "PositionArguments", + "RowExpression", + "SubstringExpression", + "SubstringArguments", + "SubstringExpressionList", + "SubstringParameters", + "SubstringFromFor", + "SubstringFromOptionalFor", + "SubstringFor", + "TrimExpression", + "TrimArguments", + "TrimDirection", + "TrimBoth", + "TrimLeading", + "TrimTrailing", + "TrimSource", + "OverlayExpression", + "OverlayArguments", + "OverlayParameters", + "FromExpression", + "ForExpression", + "OverlayExpressionList", + "ExtractArgument", + "ExtractDatePartArgument", + "ExtractIdentifierArgument", + "ExtractStringArgument", + "ExtractDatePart", + "ExternalResourceStatement", + "CreateExternalResourceStmt", + "RegisterExternalResourceStmt", + "DestroyExternalResourceStmt", + "ShowExternalResourcesStmt", + "ShowAllModifier", + "ExternalResourceKeyword", + "ExternalResourceCreationOptions", + "AttachToExternalResource", + "ConnectToExternalResource", + "ExternalResourceSource", + "ExternalResourceCreateClause", + "ExternalResourceReferenceClause", + "InsertStatement", + "OrAction", + "InsertOrReplace", + "InsertOrIgnore", + "ByNameOrPosition", + "InsertByNameOrder", + "InsertByPositionOrder", + "InsertByName", + "InsertByPosition", + "OverridingClause", + "OverridingScope", + "InsertTarget", + "InsertAlias", + "ColumnList", + "InsertColumnList", + "InsertValues", + "SelectInsertValues", + "DefaultValues", + "OnConflictClause", + "OnConflictTarget", + "OnConflictExpressionTarget", + "OnConflictIndexTarget", + "OnConflictAction", + "OnConflictUpdate", + "OnConflictNothing", + "ReturningClause", + "LoadStatement", + "ExtensionAlias", + "InstallStatement", + "InstallAndLoad", + "UpdateExtensionsStatement", + "FromSource", + "FromSourceIdentifier", + "FromSourceString", + "VersionNumber", + "ExtensionRepositoryStatement", + "CreateExtensionRepositoryStmt", + "RepositoryPrefix", + "RepositoryPublicKey", + "DropExtensionRepositoryStmt", + "TableMatchRecognizeClause", + "MatchRecognizeBody", + "MatchRecognizeClause", + "MRPartition", + "MROrderBy", + "MRMeasures", + "MRRows", + "MRSkip", + "MRPattern", + "MRSubset", + "MRDefineAuto", + "MRDefine", + "MeasuresClause", + "MeasuresElement", + "MeasureSemantics", + "RunningSemantics", + "FinalSemantics", + "RowsPerMatch", + "OneRowPerMatch", + "AllRowsPerMatch", + "AllRowsEmptyMatches", + "ShowEmptyMatches", + "OmitEmptyMatches", + "WithUnmatchedRows", + "AfterMatchSkip", + "AfterMatchSkipTarget", + "SkipToNextRow", + "SkipPastLastRow", + "SkipToFirstVar", + "SkipToLastVar", + "SkipToVar", + "PatternClause", + "RowPattern", + "RowPatternAlternative", + "RowPatternTerm", + "RowPatternFactor", + "RowPatternPrimary", + "RowPatternPermute", + "RowPatternAnchor", + "PatternStart", + "PatternEnd", + "RowPatternGroup", + "RowPatternEmpty", + "RowPatternExclusion", + "RowPatternLabel", + "RowPatternQuantifier", + "RowPatternQuantifierKind", + "QuantifierReluctant", + "QuantifierStar", + "QuantifierPlus", + "QuantifierOptional", + "QuantifierRange", + "QuantifierBounds", + "QuantifierMinMax", + "QuantifierMin", + "QuantifierMax", + "QuantifierExact", + "SubsetClause", + "SubsetElement", + "DefineClause", + "DefineElement", + "MergeIntoStatement", + "MergeIntoUsingClause", + "MergeMatch", + "MatchedClause", + "MatchedClauseAction", + "UpdateMatchClause", + "UpdateMatchInfo", + "UpdateMatchSetAction", + "UpdateByNameOrPosition", + "DeleteMatchClause", + "InsertMatchClause", + "InsertMatchInfo", + "InsertDefaultValues", + "InsertByNameOrPosition", + "InsertValuesList", + "DoNothingMatchClause", + "ErrorMatchClause", + "UpdateMatchSetClause", + "UpdateMatchSetInfo", + "AndExpression", + "NotMatchedClause", + "BySourceOrTarget", + "BySource", + "ByTarget", + "PivotStatement", + "PivotOn", + "PivotUsing", + "PivotColumnList", + "PivotColumnEntry", + "PivotColumnExpression", + "PivotColumnSubquery", + "PivotKeyword", + "UnpivotKeyword", + "UnpivotStatement", + "IntoNameValues", + "ValueOrValues", + "OptionalParensNameList", + "ParenthesizedNameList", + "BareNameList", + "IncludeOrExcludeNulls", + "IncludeNulls", + "ExcludeNulls", + "UnpivotHeader", + "UnpivotHeaderSingle", + "UnpivotHeaderList", + "PragmaStatement", + "PragmaAssignOrFunction", + "PragmaAssign", + "PragmaFunction", + "PragmaParameters", + "PrepareStatement", + "TypeList", + "SelectStatement", + "SelectStatementInternal", + "SelectSetOpChain", + "SelectSetOpChainTail", + "IntersectChain", + "IntersectChainTail", + "SetIntersectClause", + "SelectAtom", + "SelectParens", + "SetopClause", + "SetopType", + "SetopUnion", + "SetopExcept", + "ByName", + "SelectStatementType", + "ResultModifiers", + "LimitOffset", + "LimitOffsetClause", + "OffsetLimitClause", + "OffsetFetchClause", + "FetchOffsetClause", + "FetchOnlyClause", + "TableStatement", + "OptionalParensSimpleSelect", + "SimpleSelectParens", + "SimpleSelect", + "SelectFrom", + "SelectFromClause", + "FromSelectClause", + "WithStatement", + "CTEBody", + "CTESelectBody", + "CTEDMLBody", + "UsingKey", + "Materialized", + "WithClause", + "Recursive", + "SelectClause", + "TargetList", + "ColumnAliases", + "DistinctClause", + "DistinctAll", + "DistinctOn", + "DistinctOnTargets", + "InnerTableRef", + "TableRef", + "TableSubquery", + "BaseTableRef", + "TableAliasColon", + "ValuesRef", + "ParensTableRef", + "JoinOrPivot", + "TablePivotClause", + "TablePivotClauseBody", + "PivotGroupByList", + "TableUnpivotClause", + "TableUnpivotClauseBody", + "PivotHeader", + "PivotValueList", + "PivotValueTarget", + "PivotEnumTarget", + "PivotListTarget", + "UnpivotValueList", + "PivotTargetList", + "UnpivotTargetList", + "Lateral", + "BaseTableName", + "UnqualifiedBaseTableName", + "QualifiedTableName", + "SchemaReservedTable", + "CatalogReservedSchemaTable", + "TableFunction", + "TableFunctionLateralOpt", + "TableFunctionAliasColon", + "WithOrdinality", + "QualifiedTableFunction", + "TableFunctionArguments", + "FunctionArgument", + "NamedFunctionArgument", + "PositionalFunctionArgument", + "NamedParameter", + "NamedParameterAssignment", + "TableAlias", + "TableAliasAs", + "TableAliasWithoutAs", + "AtClause", + "AtSpecifier", + "AtUnit", + "VersionAtUnit", + "TimestampAtUnit", + "JoinClause", + "NearestJoinClause", + "NearestJoinAliased", + "NearestJoinBare", + "NearestBareTableRef", + "NearestValuesRef", + "NearestTableFunction", + "NearestTableSubquery", + "NearestBaseTableRef", + "NearestParensTableRef", + "ApproxOrExact", + "NearestApprox", + "NearestExact", + "DistanceOrSimilarity", + "NearestDistance", + "NearestSimilarity", + "RegularJoinClause", + "JoinByClause", + "Asof", + "JoinWithoutOnClause", + "JoinQualifier", + "OnClause", + "UsingClause", + "UsingColumnName", + "JoinType", + "JoinPrefix", + "CrossJoinPrefix", + "NaturalJoinPrefix", + "PositionalJoinPrefix", + "FullJoin", + "LeftJoin", + "RightJoin", + "SemiJoin", + "AntiJoin", + "InnerJoin", + "FromClause", + "WhereClause", + "GroupByClause", + "HavingClause", + "QualifyClause", + "SampleClause", + "SelectSampleClause", + "UsingSample", + "TableSample", + "WindowClause", + "WindowDefinition", + "SampleEntry", + "SampleEntryCount", + "SampleEntryFunction", + "SampleFunction", + "SampleProperties", + "RepeatableSample", + "SampleSeed", + "SampleCount", + "SampleValue", + "SampleUnit", + "SamplePercentage", + "SampleRows", + "GroupByExpressions", + "GroupByAll", + "GroupByList", + "GroupByExpression", + "GroupByBaseExpression", + "EmptyGroupingItem", + "CubeOrRollupClause", + "CubeOrRollup", + "CubeKeyword", + "RollupKeyword", + "GroupingSetsClause", + "SubqueryReference", + "OrderByExpression", + "DescOrAsc", + "DescendingOrder", + "AscendingOrder", + "NullsFirstOrLast", + "NullsFirst", + "NullsLast", + "OrderByClause", + "OrderByExpressions", + "OrderByExpressionList", + "OrderByAll", + "LimitClause", + "OffsetClause", + "OffsetValue", + "RowOrRows", + "LimitValue", + "LimitAll", + "LimitLiteralPercent", + "LimitExpression", + "FetchClause", + "FetchClauseWithoutValue", + "FetchClauseWithValue", + "FirstOrNext", + "FetchValue", + "AliasedExpression", + "ColIdExpression", + "ExpressionAsCollabel", + "ExpressionOptIdentifier", + "ValuesClause", + "ValuesExpressions", + "SetStatement", + "SetAssignmentOrTimeZone", + "ResetStatement", + "SetSchema", + "StandardAssignment", + "SetVariableOrSetting", + "SetTimeZone", + "ZoneValue", + "ZoneLocal", + "ZoneDefault", + "ZoneStringLiteral", + "ZoneIdentifier", + "ZoneIntervalWithInterval", + "ZoneIntervalWithPrecision", + "SetSetting", + "SetVariable", + "VariableScope", + "SettingScope", + "LocalScope", + "SessionScope", + "GlobalScope", + "SetAssignment", + "VariableAssign", + "VariableList", + "TransactionStatement", + "BeginTransaction", + "RollbackTransaction", + "CommitTransaction", + "StartOrBegin", + "Transaction", + "ReadOrWrite", + "ReadOnlyOrReadWrite", + "ReadOnly", + "ReadWrite", + "AbortOrRollback", + "CommitOrEnd", + "UpdateStatement", + "UpdateTarget", + "BaseTableSet", + "BaseTableAliasSet", + "UpdateAlias", + "UpdateSetClauseList", + "UpdateSetClause", + "UpdateSetTuple", + "UpdateSetElement", + "UpdateSetColumnTarget", + "UseStatement", + "UseTarget", + "SchemaNameAsUseTarget", + "CatalogNameAsUseTarget", + "UseTargetCatalogSchema", + "DotIdentifier", + "VacuumStatement", + "VacuumOptions", + "VacuumParensOptions", + "VacuumLegacyOptions", + "VacuumOption", + "OptAnalyze", + "OptFull", + "OptFreeze", + "OptVerbose", + "NameList", + "BareLabelKeyword", + "ColumnNameKeyword", + "FuncNameKeyword", + "ReservedKeyword", + "TypeNameKeyword", + "UnreservedKeyword", +}; + +const char *PEGRuleIdToString(PEGRuleId rule_id) { + auto index = static_cast(rule_id); + if (index == 0 || index >= static_cast(PEGRuleId::BUILTIN_COUNT)) { + return nullptr; + } + return PEG_RULE_NAMES[index]; +} + +PEGRuleId PEGRuleIdFromString(const string &rule_name) { + static const case_insensitive_map_t lookup = [] { + case_insensitive_map_t result; + for (uint32_t i = 1; i < static_cast(PEGRuleId::BUILTIN_COUNT); i++) { + result.emplace(PEG_RULE_NAMES[i], static_cast(i)); + } + return result; + }(); + auto entry = lookup.find(rule_name); + return entry == lookup.end() ? PEGRuleId::INVALID : entry->second; +} + +} // namespace duckdb diff --git a/src/duckdb/src/parser/peg/tokenizer/base_tokenizer.cpp b/src/duckdb/src/parser/peg/tokenizer/base_tokenizer.cpp index 57e69d9cf..11f6b5fa8 100644 --- a/src/duckdb/src/parser/peg/tokenizer/base_tokenizer.cpp +++ b/src/duckdb/src/parser/peg/tokenizer/base_tokenizer.cpp @@ -16,6 +16,9 @@ uint8_t ComputeMatcherTokenClass(TokenType type, const string &text) { case TokenType::NUMBER_LITERAL: return MatcherTokenClass::NUMBER; case TokenType::OPERATOR: + if (!text.empty() && !Tokenizer::CharacterIsOperator(text[0])) { + return 0; + } if (OperatorMatcher::HasSpecialPrecedence(text)) { return MatcherTokenClass::OPERATOR; } diff --git a/src/duckdb/src/parser/peg/transformer/peg_transformer.cpp b/src/duckdb/src/parser/peg/transformer/peg_transformer.cpp index f2330f956..39328b416 100644 --- a/src/duckdb/src/parser/peg/transformer/peg_transformer.cpp +++ b/src/duckdb/src/parser/peg/transformer/peg_transformer.cpp @@ -29,8 +29,8 @@ arena_ptr TransformStep::TakeResult() { GeneratedTransformProcess::GeneratedTransformProcess(PEGTransformer &transformer_p, TransformInput input, const TransformFrameOps &info_p) - : parse_result(input.parse_result), info(info_p), child_results(transformer_p.GetAllocator()), - transformer(transformer_p), pending_children(transformer_p.GetAllocator()) { + : parse_result(input.parse_result), info(info_p), child_results(transformer_p.process_allocator), + transformer(transformer_p), pending_children(transformer_p.process_allocator) { if (!info.initialize || !info.finalize) { throw InternalException("Incomplete transformer process for rule '%s'", info.name); } @@ -125,6 +125,7 @@ TransformStackFrame::TransformStackFrame(TransformInput input) } TransformStack::TransformStack(PEGTransformer &transformer_p) : transformer(transformer_p) { + frames.reserve(32); } TransformStack::~TransformStack() { @@ -135,8 +136,9 @@ TransformStack::~TransformStack() { } void TransformStack::PushFrame(TransformInput input) { - frames.emplace(input); - auto &frame = frames.top(); + frames.emplace_back(input); + auto &frame = frames.back(); + frame.process_arena_position = transformer.process_allocator.GetPosition(); auto weight = transformer.ExpressionDepthWeight(frame.rule, frame.parse_result); if (weight) { // recorded first: the frame is released (and the weight subtracted) when entering throws @@ -146,10 +148,12 @@ void TransformStack::PushFrame(TransformInput input) { } void TransformStack::PopFrame() { - if (frames.top().expression_depth_weight) { - transformer.ExitExpression(frames.top().expression_depth_weight); + const auto process_arena_position = frames.back().process_arena_position; + if (frames.back().expression_depth_weight) { + transformer.ExitExpression(frames.back().expression_depth_weight); } - frames.pop(); + frames.pop_back(); + transformer.process_allocator.Rewind(process_arena_position); } void TransformStack::InitializeFrame(TransformStackFrame &frame) { @@ -180,17 +184,16 @@ arena_ptr TransformStack::Execute(TransformInput input) { } PushFrame(input); while (!frames.empty()) { - auto &frame = frames.top(); - auto result = ExecuteFrame(frame); + auto result = ExecuteFrame(frames.back()); if (!result) { continue; } - transformer.SetResultLocation(frame.parse_result, *result); + transformer.SetResultLocation(frames.back().parse_result, *result); PopFrame(); if (frames.empty()) { return result; } - auto &parent = frames.top(); + auto &parent = frames.back(); D_ASSERT(!parent.child_result); parent.child_result = std::move(result); } @@ -206,8 +209,9 @@ string TransformStack::FormatStack() const { } auto &parse_result = frames[i].parse_result; result << "#" << i << " " << parse_result.Name(); - if (parse_result.offset.IsValid()) { - result << " offset=" << parse_result.offset.GetIndex(); + auto offset = parse_result.GetOffset(); + if (offset.IsValid()) { + result << " offset=" << offset.GetIndex(); } } return result.str(); @@ -277,6 +281,15 @@ void PEGTransformer::ExitExpression(idx_t weight) { expression_depth -= weight; } +const CompiledGrammarRule &PEGTransformer::GetRule(PEGRuleId rule_id) const { + auto rule = grammar.GetRule(rule_id); + if (!rule) { + auto rule_name = PEGRuleIdToString(rule_id); + throw InternalException("No registered data exists for rule '%s'", rule_name ? rule_name : ""); + } + return *rule; +} + const CompiledGrammarRule &PEGTransformer::GetRule(const string &rule_name) const { auto rule = grammar.GetRule(rule_name); if (!rule) { @@ -286,7 +299,7 @@ const CompiledGrammarRule &PEGTransformer::GetRule(const string &rule_name) cons } void PEGTransformer::SetResultLocation(ParseResult &parse_result, TransformResultValue &result) { - if (!parse_result.offset.IsValid()) { + if (!parse_result.GetOffset().IsValid()) { return; } auto expression_result = TryGetTransformResult>(result); diff --git a/src/duckdb/src/parser/peg/transformer/peg_transformer_factory.cpp b/src/duckdb/src/parser/peg/transformer/peg_transformer_factory.cpp index 02ae6b485..ed9489bfd 100644 --- a/src/duckdb/src/parser/peg/transformer/peg_transformer_factory.cpp +++ b/src/duckdb/src/parser/peg/transformer/peg_transformer_factory.cpp @@ -38,8 +38,9 @@ static unique_ptr ExtractAndTransformStatement(PEGTransformer &tra transformer.Clear(); // Calculate location and length cleanly - if (stmt_pr.offset.IsValid()) { - auto start = stmt_pr.offset.GetIndex(); + auto offset = stmt_pr.GetOffset(); + if (offset.IsValid()) { + auto start = offset.GetIndex(); idx_t end_index = terminator_offset.IsValid() ? terminator_offset.GetIndex() : token_iterator.EndOffset(); stmt->stmt_location = QueryLocation(start, end_index - start); } @@ -103,7 +104,7 @@ unique_ptr PEGTransformerFactory::TransformTopLevelStatement(Token if (term_inner.type != ParseResultType::END_OF_INPUT) { auto semi_children = term_inner.Cast().GetChildren(); if (!semi_children.empty()) { - terminator_offset = semi_children[0].get().offset; + terminator_offset = semi_children[0].get().GetOffset(); } } @@ -133,6 +134,7 @@ PEGTransformerFactory::PEGTransformerFactory(ParsedGrammar &grammar_p) : grammar collapsible_rules.insert("OtherOperatorExpression"); collapsible_rules.insert("InfixOtherOperatorExpression"); collapsible_rules.insert("BitwiseExpression"); + collapsible_rules.insert("TildeExpression"); collapsible_rules.insert("AdditiveExpression"); collapsible_rules.insert("MultiplicativeExpression"); collapsible_rules.insert("ExponentiationExpression"); diff --git a/src/duckdb/src/parser/peg/transformer/transform_create_table.cpp b/src/duckdb/src/parser/peg/transformer/transform_create_table.cpp index 8930b1324..2069fb8a9 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_create_table.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_create_table.cpp @@ -110,9 +110,22 @@ PEGTransformerFactory::TransformCreateTableAs(PEGTransformer &transformer, optio } result.select_statement = unique_ptr_cast(std::move(statement)); if (with_data && *with_data) { - auto limit_modifier = make_uniq(); - limit_modifier->limit = ConstantExpression::Integer(0); - result.select_statement->node->modifiers.push_back(std::move(limit_modifier)); + // WITH NO DATA: limit the query to zero rows - an existing LIMIT is replaced + auto &modifiers = result.select_statement->node->modifiers; + optional_ptr existing_limit; + for (auto &modifier : modifiers) { + if (modifier->type == ResultModifierType::LIMIT_MODIFIER) { + existing_limit = modifier->Cast(); + } + } + if (existing_limit) { + existing_limit->limit = ConstantExpression::Integer(0); + existing_limit->offset = nullptr; + } else { + auto limit_modifier = make_uniq(); + limit_modifier->limit = ConstantExpression::Integer(0); + modifiers.push_back(std::move(limit_modifier)); + } } return result; } diff --git a/src/duckdb/src/parser/peg/transformer/transform_expression.cpp b/src/duckdb/src/parser/peg/transformer/transform_expression.cpp index da74e8020..4b22ded85 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_expression.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_expression.cpp @@ -2096,8 +2096,7 @@ unique_ptr PEGTransformerFactory::TransformStarExpression( } for (auto &replace_entry : result->ReplaceList()) { if (result->ExcludeList().find(QualifiedColumnName(replace_entry.first)) != result->ExcludeList().end()) { - throw ParserException("Column \"%s\" cannot occur in both EXCLUDE and REPLACE list", - replace_entry.first); + throw ParserException("Column %s cannot occur in both EXCLUDE and REPLACE list", replace_entry.first); } } } diff --git a/src/duckdb/src/parser/peg/transformer/transform_generated_trampoline.cpp b/src/duckdb/src/parser/peg/transformer/transform_generated_trampoline.cpp index 393374abb..cc3369efd 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_generated_trampoline.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_generated_trampoline.cpp @@ -4460,7 +4460,7 @@ void PEGTransformerFactory::InitializeAlterStatementTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("AlterOptions"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::AlterOptions), list_pr.GetChild(1)}, 0); } arena_ptr @@ -4499,12 +4499,13 @@ void PEGTransformerFactory::InitializeAlterTableStmtTrampoline(PEGTransformer &t process.ReserveChildSlots(3 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("AlterTableOptions"), list_items[child_idx].get()}, 2 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::AlterTableOptions), list_items[child_idx].get()}, + 2 + child_idx); } - process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::BaseTableName), list_pr.GetChild(2)}, 1); auto &if_exists_opt = list_pr.GetChild(1).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 0); } } @@ -4520,6 +4521,7 @@ PEGTransformerFactory::FinalizeAlterTableStmtTrampoline(PEGTransformer &transfor } auto base_table_name = process.TakeResult>(1); vector> alter_table_options; + alter_table_options.reserve(dynamic_child_count); for (idx_t i = 2; i < 2 + dynamic_child_count; i++) { alter_table_options.push_back(process.TakeResult>(i)); } @@ -4532,11 +4534,11 @@ void PEGTransformerFactory::InitializeAlterSchemaStmtTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(3); - process.PushChild({transformer.GetRule("AlterSchemaOptions"), list_pr.GetChild(3)}, 2); - process.PushChild({transformer.GetRule("QualifiedName"), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::AlterSchemaOptions), list_pr.GetChild(3)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedName), list_pr.GetChild(2)}, 1); auto &if_exists_opt = list_pr.GetChild(1).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 0); } } @@ -4599,7 +4601,7 @@ void PEGTransformerFactory::InitializeAddConstraintTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("TopLevelConstraint"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TopLevelConstraint), list_pr.GetChild(1)}, 0); } arena_ptr @@ -4616,12 +4618,12 @@ void PEGTransformerFactory::InitializeDropConstraintTrampoline(PEGTransformer &t process.ReserveChildSlots(3); auto &drop_behavior_opt = list_pr.GetChild(4).Cast(); if (drop_behavior_opt.HasResult()) { - process.PushChild({transformer.GetRule("DropBehavior"), drop_behavior_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::DropBehavior), drop_behavior_opt.GetResult()}, 2); } - process.PushChild({transformer.GetRule("ConstraintName"), list_pr.GetChild(3)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ConstraintName), list_pr.GetChild(3)}, 1); auto &if_exists_opt = list_pr.GetChild(2).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 0); } } @@ -4645,10 +4647,10 @@ void PEGTransformerFactory::InitializeAddColumnTrampoline(PEGTransformer &transf GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("AddColumnEntry"), list_pr.GetChild(3)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::AddColumnEntry), list_pr.GetChild(3)}, 1); auto &if_not_exists_opt = list_pr.GetChild(2).Cast(); if (if_not_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfNotExists"), if_not_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfNotExists), if_not_exists_opt.GetResult()}, 0); } } @@ -4679,7 +4681,7 @@ void PEGTransformerFactory::InitializeAddColumnEntryTrampoline(PEGTransformer &t process.ReserveChildSlots(4 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColumnConstraint"), repeat_children[child_idx].get()}, + process.PushChild({transformer.GetRule(PEGRuleId::ColumnConstraint), repeat_children[child_idx].get()}, 3 + child_idx); } } else { @@ -4687,13 +4689,13 @@ void PEGTransformerFactory::InitializeAddColumnEntryTrampoline(PEGTransformer &t } auto &generated_column_opt = list_pr.GetChild(2).Cast(); if (generated_column_opt.HasResult()) { - process.PushChild({transformer.GetRule("GeneratedColumn"), generated_column_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::GeneratedColumn), generated_column_opt.GetResult()}, 2); } auto &type_opt = list_pr.GetChild(1).Cast(); if (type_opt.HasResult()) { - process.PushChild({transformer.GetRule("Type"), type_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Type), type_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("DottedIdentifier"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::DottedIdentifier), list_pr.GetChild(0)}, 0); } arena_ptr @@ -4719,6 +4721,7 @@ PEGTransformerFactory::FinalizeAddColumnEntryTrampoline(PEGTransformer &transfor optional> column_constraint {}; if (dynamic_child_count > 0) { vector column_constraint_value; + column_constraint_value.reserve(dynamic_child_count); for (idx_t i = 3; i < 3 + dynamic_child_count; i++) { column_constraint_value.push_back(process.TakeResult(i)); } @@ -4735,12 +4738,12 @@ void PEGTransformerFactory::InitializeDropColumnTrampoline(PEGTransformer &trans process.ReserveChildSlots(3); auto &drop_behavior_opt = list_pr.GetChild(4).Cast(); if (drop_behavior_opt.HasResult()) { - process.PushChild({transformer.GetRule("DropBehavior"), drop_behavior_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::DropBehavior), drop_behavior_opt.GetResult()}, 2); } - process.PushChild({transformer.GetRule("NestedColumnName"), list_pr.GetChild(3)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::NestedColumnName), list_pr.GetChild(3)}, 1); auto &if_exists_opt = list_pr.GetChild(2).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 0); } } @@ -4767,8 +4770,8 @@ void PEGTransformerFactory::InitializeAlterColumnTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("AlterColumnEntry"), list_pr.GetChild(3)}, 1); - process.PushChild({transformer.GetRule("NestedColumnName"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::AlterColumnEntry), list_pr.GetChild(3)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::NestedColumnName), list_pr.GetChild(2)}, 0); } arena_ptr @@ -4788,8 +4791,8 @@ void PEGTransformerFactory::InitializeRenameColumnTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(4)}, 1); - process.PushChild({transformer.GetRule("NestedColumnName"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_pr.GetChild(4)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::NestedColumnName), list_pr.GetChild(2)}, 0); } arena_ptr @@ -4816,7 +4819,8 @@ void PEGTransformerFactory::InitializeNestedColumnNameTrampoline(PEGTransformer process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("IdentifierDot"), repeat_children[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::IdentifierDot), repeat_children[child_idx].get()}, + 0 + child_idx); } } else { process.ReserveChildSlots(1 - 1); @@ -4837,6 +4841,7 @@ PEGTransformerFactory::FinalizeNestedColumnNameTrampoline(PEGTransformer &transf optional> identifier_dot {}; if (dynamic_child_count > 0) { vector identifier_dot_value; + identifier_dot_value.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { identifier_dot_value.push_back(process.TakeResult(i)); } @@ -4865,7 +4870,7 @@ void PEGTransformerFactory::InitializeRenameAlterTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_pr.GetChild(2)}, 0); } arena_ptr @@ -4883,7 +4888,7 @@ void PEGTransformerFactory::InitializeSetPartitionedByTrampoline(PEGTransformer process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 0 + child_idx); } } @@ -4894,6 +4899,7 @@ PEGTransformerFactory::FinalizeSetPartitionedByTrampoline(PEGTransformer &transf auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(3))); auto dynamic_child_count = dynamic_list_items.size(); vector> expression; + expression.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { expression.push_back(process.TakeResult>(i)); } @@ -4917,7 +4923,8 @@ void PEGTransformerFactory::InitializeSetSortedByTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("OrderByExpressions"), ExtractResultFromParens(list_pr.GetChild(3))}, 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::OrderByExpressions), ExtractResultFromParens(list_pr.GetChild(3))}, 0); } arena_ptr @@ -4943,7 +4950,7 @@ void PEGTransformerFactory::InitializeSetOptionsTrampoline(PEGTransformer &trans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("RelOptionList"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::RelOptionList), list_pr.GetChild(1)}, 0); } arena_ptr @@ -4957,7 +4964,7 @@ void PEGTransformerFactory::InitializeResetOptionsTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("RelOptionList"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::RelOptionList), list_pr.GetChild(1)}, 0); } arena_ptr @@ -5013,7 +5020,7 @@ void PEGTransformerFactory::InitializeAddDefaultTrampoline(PEGTransformer &trans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(2)}, 0); } arena_ptr @@ -5038,7 +5045,7 @@ void PEGTransformerFactory::InitializeChangeNullabilityTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("DropOrSet"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::DropOrSet), list_pr.GetChild(0)}, 0); } arena_ptr @@ -5120,15 +5127,15 @@ void PEGTransformerFactory::InitializeAlterTypeTrampoline(PEGTransformer &transf process.ReserveChildSlots(3); auto &using_expression_opt = list_pr.GetChild(4).Cast(); if (using_expression_opt.HasResult()) { - process.PushChild({transformer.GetRule("UsingExpression"), using_expression_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::UsingExpression), using_expression_opt.GetResult()}, 2); } auto &column_collation_opt = list_pr.GetChild(3).Cast(); if (column_collation_opt.HasResult()) { - process.PushChild({transformer.GetRule("ColumnCollation"), column_collation_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ColumnCollation), column_collation_opt.GetResult()}, 1); } auto &type_opt = list_pr.GetChild(2).Cast(); if (type_opt.HasResult()) { - process.PushChild({transformer.GetRule("Type"), type_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Type), type_opt.GetResult()}, 0); } } @@ -5159,7 +5166,7 @@ void PEGTransformerFactory::InitializeUsingExpressionTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -5174,11 +5181,11 @@ void PEGTransformerFactory::InitializeAlterViewStmtTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(3); - process.PushChild({transformer.GetRule("RenameAlter"), list_pr.GetChild(3)}, 2); - process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::RenameAlter), list_pr.GetChild(3)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::BaseTableName), list_pr.GetChild(2)}, 1); auto &if_exists_opt = list_pr.GetChild(1).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 0); } } @@ -5199,11 +5206,11 @@ void PEGTransformerFactory::InitializeAlterSequenceStmtTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(3); - process.PushChild({transformer.GetRule("AlterSequenceOptions"), list_pr.GetChild(3)}, 2); - process.PushChild({transformer.GetRule("QualifiedSequenceName"), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::AlterSequenceOptions), list_pr.GetChild(3)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedSequenceName), list_pr.GetChild(2)}, 1); auto &if_exists_opt = list_pr.GetChild(1).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 0); } } @@ -5227,11 +5234,13 @@ void PEGTransformerFactory::InitializeQualifiedSequenceNameTrampoline(PEGTransfo process.ReserveChildSlots(2); auto &schema_qualification_opt = list_pr.GetChild(1).Cast(); if (schema_qualification_opt.HasResult()) { - process.PushChild({transformer.GetRule("SchemaQualification"), schema_qualification_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::SchemaQualification), schema_qualification_opt.GetResult()}, + 1); } auto &catalog_qualification_opt = list_pr.GetChild(0).Cast(); if (catalog_qualification_opt.HasResult()) { - process.PushChild({transformer.GetRule("CatalogQualification"), catalog_qualification_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CatalogQualification), catalog_qualification_opt.GetResult()}, + 0); } } @@ -5278,7 +5287,7 @@ void PEGTransformerFactory::InitializeRenameAlterSequenceOptionsTrampoline(PEGTr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("RenameAlter"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::RenameAlter), list_pr.GetChild(0)}, 0); } arena_ptr @@ -5298,7 +5307,8 @@ void PEGTransformerFactory::InitializeSetSequenceOptionTrampoline(PEGTransformer process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("SequenceOption"), repeat_children[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::SequenceOption), repeat_children[child_idx].get()}, + 0 + child_idx); } } @@ -5310,6 +5320,7 @@ PEGTransformerFactory::FinalizeSetSequenceOptionTrampoline(PEGTransformer &trans auto dynamic_repeat_children = dynamic_repeat_pr.GetChildren(); auto dynamic_child_count = dynamic_repeat_children.size(); vector>> sequence_option; + sequence_option.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { sequence_option.push_back(process.TakeResult>>(i)); } @@ -5323,7 +5334,7 @@ void PEGTransformerFactory::InitializeAlterDatabaseStmtTrampoline(PEGTransformer process.ReserveChildSlots(1); auto &if_exists_opt = list_pr.GetChild(1).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 0); } } @@ -5347,13 +5358,13 @@ void PEGTransformerFactory::InitializeAnalyzeStatementTrampoline(PEGTransformer process.ReserveChildSlots(3); auto &analyze_target_opt = list_pr.GetChild(2).Cast(); if (analyze_target_opt.HasResult()) { - process.PushChild({transformer.GetRule("AnalyzeTarget"), analyze_target_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::AnalyzeTarget), analyze_target_opt.GetResult()}, 2); } auto &analyze_verbose_opt = list_pr.GetChild(1).Cast(); if (analyze_verbose_opt.HasResult()) { - process.PushChild({transformer.GetRule("AnalyzeVerbose"), analyze_verbose_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::AnalyzeVerbose), analyze_verbose_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("AnalyzeKeyword"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::AnalyzeKeyword), list_pr.GetChild(0)}, 0); } arena_ptr @@ -5378,9 +5389,9 @@ void PEGTransformerFactory::InitializeAnalyzeTargetTrampoline(PEGTransformer &tr process.ReserveChildSlots(2); auto &name_list_opt = list_pr.GetChild(1).Cast(); if (name_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("NameList"), name_list_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::NameList), name_list_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BaseTableName), list_pr.GetChild(0)}, 0); } arena_ptr @@ -5413,20 +5424,20 @@ void PEGTransformerFactory::InitializeAttachStatementTrampoline(PEGTransformer & process.ReserveChildSlots(5); auto &attach_options_opt = list_pr.GetChild(6).Cast(); if (attach_options_opt.HasResult()) { - process.PushChild({transformer.GetRule("AttachOptions"), attach_options_opt.GetResult()}, 4); + process.PushChild({transformer.GetRule(PEGRuleId::AttachOptions), attach_options_opt.GetResult()}, 4); } auto &attach_alias_opt = list_pr.GetChild(5).Cast(); if (attach_alias_opt.HasResult()) { - process.PushChild({transformer.GetRule("AttachAlias"), attach_alias_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::AttachAlias), attach_alias_opt.GetResult()}, 3); } - process.PushChild({transformer.GetRule("DatabasePath"), list_pr.GetChild(4)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::DatabasePath), list_pr.GetChild(4)}, 2); auto &if_not_exists_opt = list_pr.GetChild(2).Cast(); if (if_not_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfNotExists"), if_not_exists_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::IfNotExists), if_not_exists_opt.GetResult()}, 1); } auto &or_replace_opt = list_pr.GetChild(1).Cast(); if (or_replace_opt.HasResult()) { - process.PushChild({transformer.GetRule("OrReplace"), or_replace_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OrReplace), or_replace_opt.GetResult()}, 0); } } @@ -5463,7 +5474,7 @@ void PEGTransformerFactory::InitializeDatabasePathTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -5477,7 +5488,7 @@ void PEGTransformerFactory::InitializeAttachAliasTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(1)}, 0); } arena_ptr @@ -5491,7 +5502,7 @@ void PEGTransformerFactory::InitializeAttachOptionsTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("GenericCopyOptionList"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::GenericCopyOptionList), list_pr.GetChild(0)}, 0); } arena_ptr @@ -5506,8 +5517,8 @@ void PEGTransformerFactory::InitializeCallStatementTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("TableFunctionArguments"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("QualifiedTableFunction"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TableFunctionArguments), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedTableFunction), list_pr.GetChild(1)}, 0); } arena_ptr @@ -5525,7 +5536,7 @@ void PEGTransformerFactory::InitializeCheckpointStatementTrampoline(PEGTransform process.ReserveChildSlots(1); auto &checkpoint_force_opt = list_pr.GetChild(0).Cast(); if (checkpoint_force_opt.HasResult()) { - process.PushChild({transformer.GetRule("CheckpointForce"), checkpoint_force_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CheckpointForce), checkpoint_force_opt.GetResult()}, 0); } } @@ -5562,9 +5573,9 @@ void PEGTransformerFactory::InitializeCommentStatementTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(3); - process.PushChild({transformer.GetRule("CommentValue"), list_pr.GetChild(5)}, 2); - process.PushChild({transformer.GetRule("CommentTarget"), list_pr.GetChild(3)}, 1); - process.PushChild({transformer.GetRule("CommentOnType"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CommentValue), list_pr.GetChild(5)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::CommentTarget), list_pr.GetChild(3)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::CommentOnType), list_pr.GetChild(2)}, 0); } arena_ptr @@ -5602,7 +5613,7 @@ void PEGTransformerFactory::InitializeCommentStringLiteralIdentifierTrampoline(P GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("StringLiteralIdentifier"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::StringLiteralIdentifier), list_pr.GetChild(0)}, 0); } arena_ptr @@ -5822,7 +5833,7 @@ void PEGTransformerFactory::InitializeConstraintNameTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_pr.GetChild(0)}, 0); } arena_ptr @@ -5893,12 +5904,13 @@ void PEGTransformerFactory::InitializeTypeTrampoline(PEGTransformer &transformer process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ArrayBounds"), repeat_children[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ArrayBounds), repeat_children[child_idx].get()}, + 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("TypeVariations"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TypeVariations), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeTypeTrampoline(PEGTransformer &transformer, @@ -5915,6 +5927,7 @@ arena_ptr PEGTransformerFactory::FinalizeTypeTrampoline(PE optional> array_bounds {}; if (dynamic_child_count > 0) { vector array_bounds_value; + array_bounds_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { array_bounds_value.push_back(process.TakeResult(i)); } @@ -5971,7 +5984,7 @@ void PEGTransformerFactory::InitializeCharacterSimpleTypeTrampoline(PEGTransform process.ReserveChildSlots(1); auto &type_modifiers_opt = list_pr.GetChild(1).Cast(); if (type_modifiers_opt.HasResult()) { - process.PushChild({transformer.GetRule("TypeModifiers"), type_modifiers_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TypeModifiers), type_modifiers_opt.GetResult()}, 0); } } @@ -5992,9 +6005,9 @@ void PEGTransformerFactory::InitializeQualifiedSimpleTypeTrampoline(PEGTransform process.ReserveChildSlots(2); auto &type_modifiers_opt = list_pr.GetChild(1).Cast(); if (type_modifiers_opt.HasResult()) { - process.PushChild({transformer.GetRule("TypeModifiers"), type_modifiers_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::TypeModifiers), type_modifiers_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("QualifiedTypeName"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedTypeName), list_pr.GetChild(0)}, 0); } arena_ptr @@ -6075,7 +6088,7 @@ void PEGTransformerFactory::InitializeIntervalWithRangeSpecifierTrampoline(PEGTr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("IntervalToIntervalAsType"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IntervalToIntervalAsType), list_pr.GetChild(1)}, 0); } arena_ptr @@ -6090,7 +6103,7 @@ void PEGTransformerFactory::InitializeIntervalWithSimpleSpecifierTrampoline(PEGT GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Interval"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Interval), list_pr.GetChild(1)}, 0); } arena_ptr @@ -6337,8 +6350,8 @@ void PEGTransformerFactory::InitializeYearToMonthTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("MonthKeyword"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("YearKeyword"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::MonthKeyword), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::YearKeyword), list_pr.GetChild(0)}, 0); } arena_ptr @@ -6353,8 +6366,8 @@ void PEGTransformerFactory::InitializeDayToHourTrampoline(PEGTransformer &transf GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("HourKeyword"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("DayKeyword"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::HourKeyword), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::DayKeyword), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeDayToHourTrampoline(PEGTransformer &transformer, @@ -6369,8 +6382,8 @@ void PEGTransformerFactory::InitializeDayToMinuteTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("MinuteKeyword"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("DayKeyword"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::MinuteKeyword), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::DayKeyword), list_pr.GetChild(0)}, 0); } arena_ptr @@ -6385,8 +6398,8 @@ void PEGTransformerFactory::InitializeDayToSecondTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("SecondKeyword"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("DayKeyword"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SecondKeyword), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::DayKeyword), list_pr.GetChild(0)}, 0); } arena_ptr @@ -6401,8 +6414,8 @@ void PEGTransformerFactory::InitializeHourToMinuteTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("MinuteKeyword"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("HourKeyword"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::MinuteKeyword), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::HourKeyword), list_pr.GetChild(0)}, 0); } arena_ptr @@ -6417,8 +6430,8 @@ void PEGTransformerFactory::InitializeHourToSecondTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("SecondKeyword"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("HourKeyword"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SecondKeyword), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::HourKeyword), list_pr.GetChild(0)}, 0); } arena_ptr @@ -6433,8 +6446,8 @@ void PEGTransformerFactory::InitializeMinuteToSecondTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("SecondKeyword"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("MinuteKeyword"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SecondKeyword), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::MinuteKeyword), list_pr.GetChild(0)}, 0); } arena_ptr @@ -6457,7 +6470,7 @@ void PEGTransformerFactory::InitializeBitTypeTrampoline(PEGTransformer &transfor process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 0 + child_idx); } } else { process.ReserveChildSlots(1 - 1); @@ -6480,6 +6493,7 @@ arena_ptr PEGTransformerFactory::FinalizeBitTypeTrampoline auto &expression_opt = list_pr.GetChild(2).Cast(); if (expression_opt.HasResult()) { vector> expression_value; + expression_value.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { expression_value.push_back(process.TakeResult>(i)); } @@ -6495,7 +6509,8 @@ void PEGTransformerFactory::InitializeGeometryTypeTrampoline(PEGTransformer &tra process.ReserveChildSlots(1); auto &expression_opt = list_pr.GetChild(1).Cast(); if (expression_opt.HasResult()) { - process.PushChild({transformer.GetRule("Expression"), ExtractResultFromParens(expression_opt.GetResult())}, 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::Expression), ExtractResultFromParens(expression_opt.GetResult())}, 0); } } @@ -6706,7 +6721,7 @@ void PEGTransformerFactory::InitializeDecimalTypeTrampoline(PEGTransformer &tran process.ReserveChildSlots(1); auto &type_modifiers_opt = list_pr.GetChild(1).Cast(); if (type_modifiers_opt.HasResult()) { - process.PushChild({transformer.GetRule("TypeModifiers"), type_modifiers_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TypeModifiers), type_modifiers_opt.GetResult()}, 0); } } @@ -6726,7 +6741,7 @@ void PEGTransformerFactory::InitializeDecTypeTrampoline(PEGTransformer &transfor process.ReserveChildSlots(1); auto &type_modifiers_opt = list_pr.GetChild(1).Cast(); if (type_modifiers_opt.HasResult()) { - process.PushChild({transformer.GetRule("TypeModifiers"), type_modifiers_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TypeModifiers), type_modifiers_opt.GetResult()}, 0); } } @@ -6746,7 +6761,7 @@ void PEGTransformerFactory::InitializeNumericModTypeTrampoline(PEGTransformer &t process.ReserveChildSlots(1); auto &type_modifiers_opt = list_pr.GetChild(1).Cast(); if (type_modifiers_opt.HasResult()) { - process.PushChild({transformer.GetRule("TypeModifiers"), type_modifiers_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TypeModifiers), type_modifiers_opt.GetResult()}, 0); } } @@ -6805,10 +6820,11 @@ void PEGTransformerFactory::InitializeCatalogReservedSchemaTypeNameTrampoline(PE process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ReservedSchemaQualification"), repeat_children[child_idx].get()}, - 1 + child_idx); + process.PushChild( + {transformer.GetRule(PEGRuleId::ReservedSchemaQualification), repeat_children[child_idx].get()}, + 1 + child_idx); } - process.PushChild({transformer.GetRule("CatalogQualification"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CatalogQualification), list_pr.GetChild(0)}, 0); } arena_ptr @@ -6820,6 +6836,7 @@ PEGTransformerFactory::FinalizeCatalogReservedSchemaTypeNameTrampoline(PEGTransf auto dynamic_child_count = dynamic_repeat_children.size(); auto catalog_qualification = process.TakeResult(0); vector reserved_schema_qualification; + reserved_schema_qualification.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { reserved_schema_qualification.push_back(process.TakeResult(i)); } @@ -6833,7 +6850,7 @@ void PEGTransformerFactory::InitializeSchemaReservedTypeNameTrampoline(PEGTransf GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SchemaQualification"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SchemaQualification), list_pr.GetChild(0)}, 0); } arena_ptr @@ -6857,7 +6874,7 @@ void PEGTransformerFactory::InitializeTypeModifiersTrampoline(PEGTransformer &tr process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 0 + child_idx); } } else { process.ReserveChildSlots(1 - 1); @@ -6878,6 +6895,7 @@ PEGTransformerFactory::FinalizeTypeModifiersTrampoline(PEGTransformer &transform auto &expression_opt = ExtractResultFromParens(list_pr.GetChild(0)).Cast(); if (expression_opt.HasResult()) { vector> expression_value; + expression_value.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { expression_value.push_back(process.TakeResult>(i)); } @@ -6899,12 +6917,13 @@ void PEGTransformerFactory::InitializeNestedTypeTrampoline(PEGTransformer &trans process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ArrayBounds"), repeat_children[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ArrayBounds), repeat_children[child_idx].get()}, + 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("TypeVariations"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TypeVariations), list_pr.GetChild(0)}, 0); } arena_ptr @@ -6921,6 +6940,7 @@ PEGTransformerFactory::FinalizeNestedTypeTrampoline(PEGTransformer &transformer, optional> array_bounds {}; if (dynamic_child_count > 0) { vector array_bounds_value; + array_bounds_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { array_bounds_value.push_back(process.TakeResult(i)); } @@ -6936,7 +6956,7 @@ void PEGTransformerFactory::InitializeRowTypeTrampoline(PEGTransformer &transfor process.ReserveChildSlots(1); auto &col_id_type_list_opt = list_pr.GetChild(1).Cast(); if (col_id_type_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("ColIdTypeList"), col_id_type_list_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdTypeList), col_id_type_list_opt.GetResult()}, 0); } } @@ -6954,7 +6974,7 @@ void PEGTransformerFactory::InitializeSetofTypeTrampoline(PEGTransformer &transf GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("NestedType"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::NestedType), list_pr.GetChild(1)}, 0); } arena_ptr PEGTransformerFactory::FinalizeSetofTypeTrampoline(PEGTransformer &transformer, @@ -6968,7 +6988,7 @@ void PEGTransformerFactory::InitializeUnionTypeTrampoline(PEGTransformer &transf GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColIdTypeList"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdTypeList), list_pr.GetChild(1)}, 0); } arena_ptr PEGTransformerFactory::FinalizeUnionTypeTrampoline(PEGTransformer &transformer, @@ -6986,7 +7006,7 @@ void PEGTransformerFactory::InitializeColIdTypeListTrampoline(PEGTransformer &tr process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColIdType"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdType), list_items[child_idx].get()}, 0 + child_idx); } } @@ -6997,6 +7017,7 @@ PEGTransformerFactory::FinalizeColIdTypeListTrampoline(PEGTransformer &transform auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); vector>> col_id_type; + col_id_type.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { col_id_type.push_back(process.TakeResult>>(i)); } @@ -7015,7 +7036,7 @@ void PEGTransformerFactory::InitializeMapTypeTrampoline(PEGTransformer &transfor process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("NestedType"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::NestedType), list_items[child_idx].get()}, 0 + child_idx); } } else { process.ReserveChildSlots(1 - 1); @@ -7035,6 +7056,7 @@ arena_ptr PEGTransformerFactory::FinalizeMapTypeTrampoline auto &nested_type_opt = list_pr.GetChild(1).Cast(); if (nested_type_opt.HasResult()) { vector> nested_type_value; + nested_type_value.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { nested_type_value.push_back(process.TakeResult>(i)); } @@ -7052,7 +7074,7 @@ void PEGTransformerFactory::InitializeTupleTypeTrampoline(PEGTransformer &transf process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("NestedType"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::NestedType), list_items[child_idx].get()}, 0 + child_idx); } } @@ -7062,6 +7084,7 @@ arena_ptr PEGTransformerFactory::FinalizeTupleTypeTrampoli auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(1))); auto dynamic_child_count = dynamic_list_items.size(); vector> nested_type; + nested_type.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { nested_type.push_back(process.TakeResult>(i)); } @@ -7073,8 +7096,8 @@ void PEGTransformerFactory::InitializeColIdTypeTrampoline(PEGTransformer &transf GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("NestedType"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::NestedType), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeColIdTypeTrampoline(PEGTransformer &transformer, @@ -7120,7 +7143,7 @@ void PEGTransformerFactory::InitializeArrayKeywordWithBoundsTrampoline(PEGTransf GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SquareBracketsArray"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SquareBracketsArray), list_pr.GetChild(1)}, 0); } arena_ptr @@ -7137,7 +7160,7 @@ void PEGTransformerFactory::InitializeSquareBracketsArrayTrampoline(PEGTransform process.ReserveChildSlots(1); auto &expression_opt = list_pr.GetChild(1).Cast(); if (expression_opt.HasResult()) { - process.PushChild({transformer.GetRule("Expression"), expression_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), expression_opt.GetResult()}, 0); } } @@ -7158,13 +7181,13 @@ void PEGTransformerFactory::InitializeTimeTypeTrampoline(PEGTransformer &transfo process.ReserveChildSlots(3); auto &time_zone_opt = list_pr.GetChild(2).Cast(); if (time_zone_opt.HasResult()) { - process.PushChild({transformer.GetRule("TimeZone"), time_zone_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::TimeZone), time_zone_opt.GetResult()}, 2); } auto &type_modifiers_opt = list_pr.GetChild(1).Cast(); if (type_modifiers_opt.HasResult()) { - process.PushChild({transformer.GetRule("TypeModifiers"), type_modifiers_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::TypeModifiers), type_modifiers_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("TimeOrTimestamp"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TimeOrTimestamp), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeTimeTypeTrampoline(PEGTransformer &transformer, @@ -7230,7 +7253,7 @@ void PEGTransformerFactory::InitializeTimeZoneTrampoline(PEGTransformer &transfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("WithOrWithout"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::WithOrWithout), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeTimeZoneTrampoline(PEGTransformer &transformer, @@ -7289,7 +7312,7 @@ void PEGTransformerFactory::InitializeConnectStatementTrampoline(PEGTransformer process.ReserveChildSlots(1); auto &session_target_opt = list_pr.GetChild(1).Cast(); if (session_target_opt.HasResult()) { - process.PushChild({transformer.GetRule("SessionTarget"), session_target_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SessionTarget), session_target_opt.GetResult()}, 0); } } @@ -7355,7 +7378,8 @@ void PEGTransformerFactory::InitializeStringSessionTargetTrampoline(PEGTransform process.ReserveChildSlots(1); auto &generic_copy_option_list_opt = list_pr.GetChild(1).Cast(); if (generic_copy_option_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("GenericCopyOptionList"), generic_copy_option_list_opt.GetResult()}, 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::GenericCopyOptionList), generic_copy_option_list_opt.GetResult()}, 0); } } @@ -7390,7 +7414,7 @@ void PEGTransformerFactory::InitializeCopyStatementTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("CopyVariations"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CopyVariations), list_pr.GetChild(1)}, 0); } arena_ptr @@ -7428,19 +7452,19 @@ void PEGTransformerFactory::InitializeCopyTableTrampoline(PEGTransformer &transf process.ReserveChildSlots(6); auto ©_options_opt = list_pr.GetChild(5).Cast(); if (copy_options_opt.HasResult()) { - process.PushChild({transformer.GetRule("CopyOptions"), copy_options_opt.GetResult()}, 5); + process.PushChild({transformer.GetRule(PEGRuleId::CopyOptions), copy_options_opt.GetResult()}, 5); } auto ©_legacy_option_opt = list_pr.GetChild(4).Cast(); if (copy_legacy_option_opt.HasResult()) { - process.PushChild({transformer.GetRule("CopyLegacyOption"), copy_legacy_option_opt.GetResult()}, 4); + process.PushChild({transformer.GetRule(PEGRuleId::CopyLegacyOption), copy_legacy_option_opt.GetResult()}, 4); } - process.PushChild({transformer.GetRule("CopyFileName"), list_pr.GetChild(3)}, 3); - process.PushChild({transformer.GetRule("FromOrTo"), list_pr.GetChild(2)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::CopyFileName), list_pr.GetChild(3)}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::FromOrTo), list_pr.GetChild(2)}, 2); auto &insert_column_list_opt = list_pr.GetChild(1).Cast(); if (insert_column_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("InsertColumnList"), insert_column_list_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::InsertColumnList), insert_column_list_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BaseTableName), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeCopyTableTrampoline(PEGTransformer &transformer, @@ -7513,11 +7537,11 @@ void PEGTransformerFactory::InitializeCopySelectTrampoline(PEGTransformer &trans process.ReserveChildSlots(3); auto ©_options_opt = list_pr.GetChild(3).Cast(); if (copy_options_opt.HasResult()) { - process.PushChild({transformer.GetRule("CopyOptions"), copy_options_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::CopyOptions), copy_options_opt.GetResult()}, 2); } - process.PushChild({transformer.GetRule("CopyFileName"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("SelectStatementInternal"), ExtractResultFromParens(list_pr.GetChild(0))}, - 0); + process.PushChild({transformer.GetRule(PEGRuleId::CopyFileName), list_pr.GetChild(2)}, 1); + process.PushChild( + {transformer.GetRule(PEGRuleId::SelectStatementInternal), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr @@ -7606,7 +7630,7 @@ void PEGTransformerFactory::InitializeCopyFileNameIdentifierColIdTrampoline(PEGT GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("IdentifierColId"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IdentifierColId), list_pr.GetChild(0)}, 0); } arena_ptr @@ -7626,7 +7650,8 @@ void PEGTransformerFactory::InitializeIdentifierColIdTrampoline(PEGTransformer & process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("CopyFileNameSuffix"), repeat_children[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::CopyFileNameSuffix), repeat_children[child_idx].get()}, + 0 + child_idx); } } @@ -7639,6 +7664,7 @@ PEGTransformerFactory::FinalizeIdentifierColIdTrampoline(PEGTransformer &transfo auto dynamic_child_count = dynamic_repeat_children.size(); auto identifier = list_pr.GetChild(0).Cast().identifier; vector copy_file_name_suffix; + copy_file_name_suffix.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { copy_file_name_suffix.push_back(process.TakeResult(i)); } @@ -7650,7 +7676,7 @@ void PEGTransformerFactory::InitializeCopyFileNameSuffixTrampoline(PEGTransforme GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(1)}, 0); } arena_ptr @@ -7681,7 +7707,7 @@ void PEGTransformerFactory::InitializeCopyOptionsTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("CopyOptionList"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CopyOptionList), list_pr.GetChild(1)}, 0); } arena_ptr @@ -7728,13 +7754,13 @@ void PEGTransformerFactory::InitializeSpecializedOptionListTrampoline(PEGTransfo process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("SpecializedOptionTail"), repeat_children[child_idx].get()}, + process.PushChild({transformer.GetRule(PEGRuleId::SpecializedOptionTail), repeat_children[child_idx].get()}, 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("SpecializedOption"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SpecializedOption), list_pr.GetChild(0)}, 0); } arena_ptr @@ -7752,6 +7778,7 @@ PEGTransformerFactory::FinalizeSpecializedOptionListTrampoline(PEGTransformer &t optional> specialized_option_tail {}; if (dynamic_child_count > 0) { vector specialized_option_tail_value; + specialized_option_tail_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { specialized_option_tail_value.push_back(process.TakeResult(i)); } @@ -7765,7 +7792,7 @@ void PEGTransformerFactory::InitializeSpecializedOptionTailTrampoline(PEGTransfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SpecializedOption"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SpecializedOption), list_pr.GetChild(1)}, 0); } arena_ptr @@ -7961,10 +7988,10 @@ void PEGTransformerFactory::InitializeForceQuoteOptionTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("StarSymbolColumnList"), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::StarSymbolColumnList), list_pr.GetChild(2)}, 1); auto &force_quote_opt = list_pr.GetChild(0).Cast(); if (force_quote_opt.HasResult()) { - process.PushChild({transformer.GetRule("ForceQuote"), force_quote_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ForceQuote), force_quote_opt.GetResult()}, 0); } } @@ -8022,7 +8049,7 @@ void PEGTransformerFactory::InitializePartitionByOptionTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("PartitionByColumnList"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::PartitionByColumnList), list_pr.GetChild(2)}, 0); } arena_ptr @@ -8070,7 +8097,7 @@ void PEGTransformerFactory::InitializeParenthesizedPartitionByColumnListTrampoli GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColumnList"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColumnList), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr @@ -8085,7 +8112,7 @@ void PEGTransformerFactory::InitializeSinglePartitionByColumnListTrampoline(PEGT GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(0)}, 0); } arena_ptr @@ -8100,10 +8127,10 @@ void PEGTransformerFactory::InitializeForceNullOptionTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("ColumnList"), list_pr.GetChild(3)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ColumnList), list_pr.GetChild(3)}, 1); auto &force_not_null_opt = list_pr.GetChild(1).Cast(); if (force_not_null_opt.HasResult()) { - process.PushChild({transformer.GetRule("ForceNotNull"), force_not_null_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ForceNotNull), force_not_null_opt.GetResult()}, 0); } } @@ -8138,7 +8165,8 @@ void PEGTransformerFactory::InitializeCopyGenericOptionListTrampoline(PEGTransfo process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("CopyGenericOption"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::CopyGenericOption), list_items[child_idx].get()}, + 0 + child_idx); } } @@ -8149,6 +8177,7 @@ PEGTransformerFactory::FinalizeCopyGenericOptionListTrampoline(PEGTransformer &t auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); vector copy_generic_option; + copy_generic_option.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { copy_generic_option.push_back(process.TakeResult(i)); } @@ -8183,8 +8212,8 @@ void PEGTransformerFactory::InitializeOrderByCopyOptionTrampoline(PEGTransformer process.ReserveChildSlots(1); auto &generic_copy_option_value_opt = list_pr.GetChild(2).Cast(); if (generic_copy_option_value_opt.HasResult()) { - process.PushChild({transformer.GetRule("GenericCopyOptionValue"), generic_copy_option_value_opt.GetResult()}, - 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::GenericCopyOptionValue), generic_copy_option_value_opt.GetResult()}, 0); } } @@ -8205,8 +8234,8 @@ void PEGTransformerFactory::InitializePartitionedByCopyOptionTrampoline(PEGTrans process.ReserveChildSlots(1); auto &generic_copy_option_value_opt = list_pr.GetChild(2).Cast(); if (generic_copy_option_value_opt.HasResult()) { - process.PushChild({transformer.GetRule("GenericCopyOptionValue"), generic_copy_option_value_opt.GetResult()}, - 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::GenericCopyOptionValue), generic_copy_option_value_opt.GetResult()}, 0); } } @@ -8229,7 +8258,8 @@ void PEGTransformerFactory::InitializeGenericCopyOptionListTrampoline(PEGTransfo process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("GenericCopyOption"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::GenericCopyOption), list_items[child_idx].get()}, + 0 + child_idx); } } @@ -8240,6 +8270,7 @@ PEGTransformerFactory::FinalizeGenericCopyOptionListTrampoline(PEGTransformer &t auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); vector generic_copy_option; + generic_copy_option.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { generic_copy_option.push_back(process.TakeResult(i)); } @@ -8253,8 +8284,8 @@ void PEGTransformerFactory::InitializeGenericCopyOptionTrampoline(PEGTransformer process.ReserveChildSlots(1); auto &generic_copy_option_value_opt = list_pr.GetChild(1).Cast(); if (generic_copy_option_value_opt.HasResult()) { - process.PushChild({transformer.GetRule("GenericCopyOptionValue"), generic_copy_option_value_opt.GetResult()}, - 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::GenericCopyOptionValue), generic_copy_option_value_opt.GetResult()}, 0); } } @@ -8296,7 +8327,8 @@ void PEGTransformerFactory::InitializeGenericCopyOptionOrderListTrampoline(PEGTr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("GenericCopyOptionParenthesizedExpressionList"), list_pr.GetChild(0)}, 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::GenericCopyOptionParenthesizedExpressionList), list_pr.GetChild(0)}, 0); } arena_ptr @@ -8312,7 +8344,7 @@ void PEGTransformerFactory::InitializeGenericCopyOptionExpressionTrampoline(PEGT GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -8327,7 +8359,8 @@ void PEGTransformerFactory::InitializeGenericCopyOptionParenthesizedExpressionLi PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("OrderByExpressionList"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::OrderByExpressionList), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr PEGTransformerFactory::FinalizeGenericCopyOptionParenthesizedExpressionListTrampoline( @@ -8363,9 +8396,9 @@ void PEGTransformerFactory::InitializeCopyFromDatabaseWithFlagTrampoline(PEGTran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(3); - process.PushChild({transformer.GetRule("CopyDatabaseFlag"), list_pr.GetChild(5)}, 2); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(4)}, 1); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CopyDatabaseFlag), list_pr.GetChild(5)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(4)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(2)}, 0); } arena_ptr @@ -8382,8 +8415,8 @@ void PEGTransformerFactory::InitializeCopyFromDatabaseWithoutFlagTrampoline(PEGT GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(4)}, 1); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(4)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(2)}, 0); } arena_ptr @@ -8399,7 +8432,7 @@ void PEGTransformerFactory::InitializeCopyDatabaseFlagTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SchemaOrData"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SchemaOrData), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr @@ -8460,29 +8493,30 @@ void PEGTransformerFactory::InitializeCreateIndexStmtTrampoline(PEGTransformer & process.ReserveChildSlots(7 + dynamic_child_count - 1); auto &where_clause_opt = list_pr.GetChild(9).Cast(); if (where_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("WhereClause"), where_clause_opt.GetResult()}, + process.PushChild({transformer.GetRule(PEGRuleId::WhereClause), where_clause_opt.GetResult()}, 6 + dynamic_child_count - 1); } auto &with_list_opt = list_pr.GetChild(8).Cast(); if (with_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("WithList"), with_list_opt.GetResult()}, 5 + dynamic_child_count - 1); + process.PushChild({transformer.GetRule(PEGRuleId::WithList), with_list_opt.GetResult()}, + 5 + dynamic_child_count - 1); } for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("IndexElement"), list_items[child_idx].get()}, 4 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::IndexElement), list_items[child_idx].get()}, 4 + child_idx); } auto &index_type_opt = list_pr.GetChild(6).Cast(); if (index_type_opt.HasResult()) { - process.PushChild({transformer.GetRule("IndexType"), index_type_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::IndexType), index_type_opt.GetResult()}, 3); } - process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(5)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::BaseTableName), list_pr.GetChild(5)}, 2); auto &if_not_exists_opt = list_pr.GetChild(2).Cast(); if (if_not_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfNotExists"), if_not_exists_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::IfNotExists), if_not_exists_opt.GetResult()}, 1); } auto &unique_index_opt = list_pr.GetChild(0).Cast(); if (unique_index_opt.HasResult()) { - process.PushChild({transformer.GetRule("UniqueIndex"), unique_index_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::UniqueIndex), unique_index_opt.GetResult()}, 0); } } @@ -8511,6 +8545,7 @@ PEGTransformerFactory::FinalizeCreateIndexStmtTrampoline(PEGTransformer &transfo index_type = process.TakeResult(3); } vector> index_element; + index_element.reserve(dynamic_child_count); for (idx_t i = 4; i < 4 + dynamic_child_count; i++) { index_element.push_back(process.TakeResult>(i)); } @@ -8533,7 +8568,7 @@ void PEGTransformerFactory::InitializeWithListTrampoline(PEGTransformer &transfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("RelOptionOrOids"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::RelOptionOrOids), list_pr.GetChild(1)}, 0); } arena_ptr PEGTransformerFactory::FinalizeWithListTrampoline(PEGTransformer &transformer, @@ -8572,7 +8607,7 @@ void PEGTransformerFactory::InitializeRelOptionListTrampoline(PEGTransformer &tr process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("RelOption"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::RelOption), list_items[child_idx].get()}, 0 + child_idx); } } @@ -8583,6 +8618,7 @@ PEGTransformerFactory::FinalizeRelOptionListTrampoline(PEGTransformer &transform auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); vector>> rel_option; + rel_option.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { rel_option.push_back(process.TakeResult>>(i)); } @@ -8593,7 +8629,7 @@ PEGTransformerFactory::FinalizeRelOptionListTrampoline(PEGTransformer &transform void PEGTransformerFactory::InitializeOidsTrampoline(PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("WithOrWithoutOids"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::WithOrWithoutOids), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeOidsTrampoline(PEGTransformer &transformer, @@ -8652,13 +8688,13 @@ void PEGTransformerFactory::InitializeIndexElementTrampoline(PEGTransformer &tra process.ReserveChildSlots(3); auto &nulls_first_or_last_opt = list_pr.GetChild(2).Cast(); if (nulls_first_or_last_opt.HasResult()) { - process.PushChild({transformer.GetRule("NullsFirstOrLast"), nulls_first_or_last_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::NullsFirstOrLast), nulls_first_or_last_opt.GetResult()}, 2); } auto &desc_or_asc_opt = list_pr.GetChild(1).Cast(); if (desc_or_asc_opt.HasResult()) { - process.PushChild({transformer.GetRule("DescOrAsc"), desc_or_asc_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::DescOrAsc), desc_or_asc_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -8706,9 +8742,10 @@ void PEGTransformerFactory::InitializeRelOptionTrampoline(PEGTransformer &transf process.ReserveChildSlots(2); auto &rel_option_argument_opt_opt = list_pr.GetChild(1).Cast(); if (rel_option_argument_opt_opt.HasResult()) { - process.PushChild({transformer.GetRule("RelOptionArgumentOpt"), rel_option_argument_opt_opt.GetResult()}, 1); + process.PushChild( + {transformer.GetRule(PEGRuleId::RelOptionArgumentOpt), rel_option_argument_opt_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("RelOptionName"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::RelOptionName), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeRelOptionTrampoline(PEGTransformer &transformer, @@ -8787,12 +8824,13 @@ void PEGTransformerFactory::InitializeDottedColLabelTrampoline(PEGTransformer &t process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("DotColLabel"), repeat_children[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::DotColLabel), repeat_children[child_idx].get()}, + 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("ColLabel"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColLabel), list_pr.GetChild(0)}, 0); } arena_ptr @@ -8810,6 +8848,7 @@ PEGTransformerFactory::FinalizeDottedColLabelTrampoline(PEGTransformer &transfor optional> dot_col_label {}; if (dynamic_child_count > 0) { vector dot_col_label_value; + dot_col_label_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { dot_col_label_value.push_back(process.TakeResult(i)); } @@ -8823,7 +8862,7 @@ void PEGTransformerFactory::InitializeRelOptionArgumentOptTrampoline(PEGTransfor GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("DefArg"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::DefArg), list_pr.GetChild(1)}, 0); } arena_ptr @@ -8858,7 +8897,7 @@ void PEGTransformerFactory::InitializeDefArgNullTrampoline(PEGTransformer &trans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("NullLiteral"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::NullLiteral), list_pr.GetChild(0)}, 0); } arena_ptr @@ -8915,14 +8954,15 @@ void PEGTransformerFactory::InitializeCreateMacroStmtTrampoline(PEGTransformer & process.ReserveChildSlots(4 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("MacroDefinition"), list_items[child_idx].get()}, 3 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::MacroDefinition), list_items[child_idx].get()}, + 3 + child_idx); } - process.PushChild({transformer.GetRule("QualifiedName"), list_pr.GetChild(2)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedName), list_pr.GetChild(2)}, 2); auto &if_not_exists_opt = list_pr.GetChild(1).Cast(); if (if_not_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfNotExists"), if_not_exists_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::IfNotExists), if_not_exists_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("MacroOrFunction"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::MacroOrFunction), list_pr.GetChild(0)}, 0); } arena_ptr @@ -8938,6 +8978,7 @@ PEGTransformerFactory::FinalizeCreateMacroStmtTrampoline(PEGTransformer &transfo } auto qualified_name = process.TakeResult(2); vector> macro_definition; + macro_definition.reserve(dynamic_child_count); for (idx_t i = 3; i < 3 + dynamic_child_count; i++) { macro_definition.push_back(process.TakeResult>(i)); } @@ -8994,10 +9035,10 @@ void PEGTransformerFactory::InitializeMacroDefinitionTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("MacroDefinitionBody"), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::MacroDefinitionBody), list_pr.GetChild(2)}, 1); auto ¯o_parameters_opt = ExtractResultFromParens(list_pr.GetChild(0)).Cast(); if (macro_parameters_opt.HasResult()) { - process.PushChild({transformer.GetRule("MacroParameters"), macro_parameters_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::MacroParameters), macro_parameters_opt.GetResult()}, 0); } } @@ -9042,7 +9083,7 @@ void PEGTransformerFactory::InitializeMacroParametersTrampoline(PEGTransformer & process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("MacroParameter"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::MacroParameter), list_items[child_idx].get()}, 0 + child_idx); } } @@ -9053,6 +9094,7 @@ PEGTransformerFactory::FinalizeMacroParametersTrampoline(PEGTransformer &transfo auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); auto dynamic_child_count = dynamic_list_items.size(); vector macro_parameter; + macro_parameter.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { macro_parameter.push_back(process.TakeResult(i)); } @@ -9087,9 +9129,9 @@ void PEGTransformerFactory::InitializeSimpleParameterTrampoline(PEGTransformer & process.ReserveChildSlots(2); auto &type_opt = list_pr.GetChild(1).Cast(); if (type_opt.HasResult()) { - process.PushChild({transformer.GetRule("Type"), type_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Type), type_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("TypeFuncName"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TypeFuncName), list_pr.GetChild(0)}, 0); } arena_ptr @@ -9108,7 +9150,7 @@ void PEGTransformerFactory::InitializeScalarMacroDefinitionTrampoline(PEGTransfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -9123,7 +9165,7 @@ void PEGTransformerFactory::InitializeTableMacroDefinitionTrampoline(PEGTransfor GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SelectStatementInternal"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SelectStatementInternal), list_pr.GetChild(1)}, 0); } arena_ptr @@ -9140,12 +9182,12 @@ void PEGTransformerFactory::InitializeCreateSchemaStmtTrampoline(PEGTransformer process.ReserveChildSlots(3); auto &with_option_list_opt = list_pr.GetChild(3).Cast(); if (with_option_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("WithOptionList"), with_option_list_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::WithOptionList), with_option_list_opt.GetResult()}, 2); } - process.PushChild({transformer.GetRule("QualifiedName"), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedName), list_pr.GetChild(2)}, 1); auto &if_not_exists_opt = list_pr.GetChild(1).Cast(); if (if_not_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfNotExists"), if_not_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfNotExists), if_not_exists_opt.GetResult()}, 0); } } @@ -9169,7 +9211,7 @@ void PEGTransformerFactory::InitializeWithOptionListTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("RelOptionList"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::RelOptionList), list_pr.GetChild(1)}, 0); } arena_ptr @@ -9184,18 +9226,19 @@ void PEGTransformerFactory::InitializeCreateSecretStmtTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(4); - process.PushChild({transformer.GetRule("GenericCopyOptionList"), list_pr.GetChild(4)}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::GenericCopyOptionList), list_pr.GetChild(4)}, 3); auto &secret_storage_specifier_opt = list_pr.GetChild(3).Cast(); if (secret_storage_specifier_opt.HasResult()) { - process.PushChild({transformer.GetRule("SecretStorageSpecifier"), secret_storage_specifier_opt.GetResult()}, 2); + process.PushChild( + {transformer.GetRule(PEGRuleId::SecretStorageSpecifier), secret_storage_specifier_opt.GetResult()}, 2); } auto &secret_name_opt = list_pr.GetChild(2).Cast(); if (secret_name_opt.HasResult()) { - process.PushChild({transformer.GetRule("SecretName"), secret_name_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::SecretName), secret_name_opt.GetResult()}, 1); } auto &if_not_exists_opt = list_pr.GetChild(1).Cast(); if (if_not_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfNotExists"), if_not_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfNotExists), if_not_exists_opt.GetResult()}, 0); } } @@ -9238,7 +9281,7 @@ void PEGTransformerFactory::InitializeSecretNameTrampoline(PEGTransformer &trans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(0)}, 0); } arena_ptr @@ -9260,15 +9303,16 @@ void PEGTransformerFactory::InitializeCreateSequenceStmtTrampoline(PEGTransforme process.ReserveChildSlots(3 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("SequenceOption"), repeat_children[child_idx].get()}, 2 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::SequenceOption), repeat_children[child_idx].get()}, + 2 + child_idx); } } else { process.ReserveChildSlots(3 - 1); } - process.PushChild({transformer.GetRule("QualifiedName"), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedName), list_pr.GetChild(2)}, 1); auto &if_not_exists_opt = list_pr.GetChild(1).Cast(); if (if_not_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfNotExists"), if_not_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfNotExists), if_not_exists_opt.GetResult()}, 0); } } @@ -9291,6 +9335,7 @@ PEGTransformerFactory::FinalizeCreateSequenceStmtTrampoline(PEGTransformer &tran optional>>> sequence_option {}; if (dynamic_child_count > 0) { vector>> sequence_option_value; + sequence_option_value.reserve(dynamic_child_count); for (idx_t i = 2; i < 2 + dynamic_child_count; i++) { sequence_option_value.push_back(process.TakeResult>>(i)); } @@ -9367,7 +9412,7 @@ void PEGTransformerFactory::InitializeSeqSetIncrementTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(2)}, 0); } arena_ptr @@ -9386,8 +9431,8 @@ void PEGTransformerFactory::InitializeSeqSetMinMaxTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("SeqMinOrMax"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::SeqMinOrMax), list_pr.GetChild(0)}, 0); } arena_ptr @@ -9402,7 +9447,7 @@ void PEGTransformerFactory::InitializeSeqNoMinMaxTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SeqMinOrMax"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SeqMinOrMax), list_pr.GetChild(1)}, 0); } arena_ptr @@ -9416,7 +9461,7 @@ void PEGTransformerFactory::InitializeSeqStartWithTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(2)}, 0); } arena_ptr @@ -9434,7 +9479,7 @@ void PEGTransformerFactory::InitializeSeqOwnedByTrampoline(PEGTransformer &trans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("QualifiedName"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedName), list_pr.GetChild(2)}, 0); } arena_ptr @@ -9511,14 +9556,14 @@ void PEGTransformerFactory::InitializeCreateStatementTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(3); - process.PushChild({transformer.GetRule("CreateStatementVariation"), list_pr.GetChild(3)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::CreateStatementVariation), list_pr.GetChild(3)}, 2); auto &temporary_opt = list_pr.GetChild(2).Cast(); if (temporary_opt.HasResult()) { - process.PushChild({transformer.GetRule("Temporary"), temporary_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Temporary), temporary_opt.GetResult()}, 1); } auto &or_replace_opt = list_pr.GetChild(1).Cast(); if (or_replace_opt.HasResult()) { - process.PushChild({transformer.GetRule("OrReplace"), or_replace_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OrReplace), or_replace_opt.GetResult()}, 0); } } @@ -9631,13 +9676,13 @@ void PEGTransformerFactory::InitializeCreateTableStmtTrampoline(PEGTransformer & process.ReserveChildSlots(4); auto &commit_action_opt = list_pr.GetChild(4).Cast(); if (commit_action_opt.HasResult()) { - process.PushChild({transformer.GetRule("CommitAction"), commit_action_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::CommitAction), commit_action_opt.GetResult()}, 3); } - process.PushChild({transformer.GetRule("CreateTableDefinition"), list_pr.GetChild(3)}, 2); - process.PushChild({transformer.GetRule("QualifiedName"), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::CreateTableDefinition), list_pr.GetChild(3)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedName), list_pr.GetChild(2)}, 1); auto &if_not_exists_opt = list_pr.GetChild(1).Cast(); if (if_not_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfNotExists"), if_not_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfNotExists), if_not_exists_opt.GetResult()}, 0); } } @@ -9686,20 +9731,21 @@ void PEGTransformerFactory::InitializeCreateTableAsTrampoline(PEGTransformer &tr process.ReserveChildSlots(5); auto &with_data_opt = list_pr.GetChild(5).Cast(); if (with_data_opt.HasResult()) { - process.PushChild({transformer.GetRule("WithData"), with_data_opt.GetResult()}, 4); + process.PushChild({transformer.GetRule(PEGRuleId::WithData), with_data_opt.GetResult()}, 4); } - process.PushChild({transformer.GetRule("Statement"), list_pr.GetChild(4)}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::Statement), list_pr.GetChild(4)}, 3); auto &with_list_opt = list_pr.GetChild(2).Cast(); if (with_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("WithList"), with_list_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::WithList), with_list_opt.GetResult()}, 2); } auto &partition_sorted_options_opt = list_pr.GetChild(1).Cast(); if (partition_sorted_options_opt.HasResult()) { - process.PushChild({transformer.GetRule("PartitionSortedOptions"), partition_sorted_options_opt.GetResult()}, 1); + process.PushChild( + {transformer.GetRule(PEGRuleId::PartitionSortedOptions), partition_sorted_options_opt.GetResult()}, 1); } auto &identifier_list_opt = list_pr.GetChild(0).Cast(); if (identifier_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("IdentifierList"), identifier_list_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IdentifierList), identifier_list_opt.GetResult()}, 0); } } @@ -9755,9 +9801,9 @@ void PEGTransformerFactory::InitializePartitionOptSortedOptionsTrampoline(PEGTra process.ReserveChildSlots(2); auto &sorted_options_opt = list_pr.GetChild(1).Cast(); if (sorted_options_opt.HasResult()) { - process.PushChild({transformer.GetRule("SortedOptions"), sorted_options_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::SortedOptions), sorted_options_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("PartitionOptions"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::PartitionOptions), list_pr.GetChild(0)}, 0); } arena_ptr @@ -9779,9 +9825,9 @@ void PEGTransformerFactory::InitializeSortedOptPartitionOptionsTrampoline(PEGTra process.ReserveChildSlots(2); auto &partition_options_opt = list_pr.GetChild(1).Cast(); if (partition_options_opt.HasResult()) { - process.PushChild({transformer.GetRule("PartitionOptions"), partition_options_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::PartitionOptions), partition_options_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("SortedOptions"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SortedOptions), list_pr.GetChild(0)}, 0); } arena_ptr @@ -9805,7 +9851,7 @@ void PEGTransformerFactory::InitializePartitionOptionsTrampoline(PEGTransformer process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 0 + child_idx); } } @@ -9816,6 +9862,7 @@ PEGTransformerFactory::FinalizePartitionOptionsTrampoline(PEGTransformer &transf auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(2))); auto dynamic_child_count = dynamic_list_items.size(); vector> expression; + expression.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { expression.push_back(process.TakeResult>(i)); } @@ -9831,7 +9878,7 @@ void PEGTransformerFactory::InitializeSortedOptionsTrampoline(PEGTransformer &tr process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 0 + child_idx); } } @@ -9842,6 +9889,7 @@ PEGTransformerFactory::FinalizeSortedOptionsTrampoline(PEGTransformer &transform auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(2))); auto dynamic_child_count = dynamic_list_items.size(); vector> expression; + expression.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { expression.push_back(process.TakeResult>(i)); } @@ -9902,6 +9950,7 @@ PEGTransformerFactory::FinalizeIdentifierListTrampoline(PEGTransformer &transfor auto &list_pr = process.parse_result.Cast(); vector identifier; auto identifier_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); + identifier.reserve(identifier_items.size()); for (auto &identifier_item : identifier_items) { identifier.push_back(identifier_item.get().Cast().identifier); } @@ -9915,15 +9964,17 @@ void PEGTransformerFactory::InitializeCreateColumnListTrampoline(PEGTransformer process.ReserveChildSlots(3); auto &with_list_opt = list_pr.GetChild(2).Cast(); if (with_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("WithList"), with_list_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::WithList), with_list_opt.GetResult()}, 2); } auto &partition_sorted_options_opt = list_pr.GetChild(1).Cast(); if (partition_sorted_options_opt.HasResult()) { - process.PushChild({transformer.GetRule("PartitionSortedOptions"), partition_sorted_options_opt.GetResult()}, 1); + process.PushChild( + {transformer.GetRule(PEGRuleId::PartitionSortedOptions), partition_sorted_options_opt.GetResult()}, 1); } auto &create_table_column_list_opt = ExtractResultFromParens(list_pr.GetChild(0)).Cast(); if (create_table_column_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("CreateTableColumnList"), create_table_column_list_opt.GetResult()}, 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::CreateTableColumnList), create_table_column_list_opt.GetResult()}, 0); } } @@ -9983,8 +10034,8 @@ void PEGTransformerFactory::InitializeSchemaReservedIdentifierOrStringLiteralTra PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("ReservedIdentifierOrStringLiteral"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("SchemaQualification"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ReservedIdentifierOrStringLiteral), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::SchemaQualification), list_pr.GetChild(0)}, 0); } arena_ptr @@ -10004,14 +10055,15 @@ void PEGTransformerFactory::InitializeCatalogReservedSchemaIdentifierTrampoline( auto repeat_children = repeat_pr.GetChildren(); auto dynamic_child_count = repeat_children.size(); process.ReserveChildSlots(3 + dynamic_child_count - 1); - process.PushChild({transformer.GetRule("ReservedIdentifierOrStringLiteral"), list_pr.GetChild(2)}, + process.PushChild({transformer.GetRule(PEGRuleId::ReservedIdentifierOrStringLiteral), list_pr.GetChild(2)}, 2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ReservedSchemaQualification"), repeat_children[child_idx].get()}, - 1 + child_idx); + process.PushChild( + {transformer.GetRule(PEGRuleId::ReservedSchemaQualification), repeat_children[child_idx].get()}, + 1 + child_idx); } - process.PushChild({transformer.GetRule("CatalogQualification"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CatalogQualification), list_pr.GetChild(0)}, 0); } arena_ptr @@ -10023,6 +10075,7 @@ PEGTransformerFactory::FinalizeCatalogReservedSchemaIdentifierTrampoline(PEGTran auto dynamic_child_count = dynamic_repeat_children.size(); auto catalog_qualification = process.TakeResult(0); vector reserved_schema_qualification; + reserved_schema_qualification.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { reserved_schema_qualification.push_back(process.TakeResult(i)); } @@ -10198,7 +10251,7 @@ void PEGTransformerFactory::InitializeCreateTableColumnListTrampoline(PEGTransfo process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("CreateTableColumnElement"), list_items[child_idx].get()}, + process.PushChild({transformer.GetRule(PEGRuleId::CreateTableColumnElement), list_items[child_idx].get()}, 0 + child_idx); } } @@ -10210,6 +10263,7 @@ PEGTransformerFactory::FinalizeCreateTableColumnListTrampoline(PEGTransformer &t auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); auto dynamic_child_count = dynamic_list_items.size(); vector create_table_column_element; + create_table_column_element.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { create_table_column_element.push_back(process.TakeResult(i)); } @@ -10242,7 +10296,7 @@ void PEGTransformerFactory::InitializeCreateTableColumnDefinitionTrampoline(PEGT GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColumnDefinition"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColumnDefinition), list_pr.GetChild(0)}, 0); } arena_ptr @@ -10257,7 +10311,7 @@ void PEGTransformerFactory::InitializeCreateTableConstraintTrampoline(PEGTransfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("TopLevelConstraint"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TopLevelConstraint), list_pr.GetChild(0)}, 0); } arena_ptr @@ -10280,7 +10334,7 @@ void PEGTransformerFactory::InitializeColumnDefinitionTrampoline(PEGTransformer process.ReserveChildSlots(3 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColumnConstraint"), repeat_children[child_idx].get()}, + process.PushChild({transformer.GetRule(PEGRuleId::ColumnConstraint), repeat_children[child_idx].get()}, 2 + child_idx); } } else { @@ -10288,11 +10342,11 @@ void PEGTransformerFactory::InitializeColumnDefinitionTrampoline(PEGTransformer } auto &generated_column_opt = list_pr.GetChild(2).Cast(); if (generated_column_opt.HasResult()) { - process.PushChild({transformer.GetRule("GeneratedColumn"), generated_column_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::GeneratedColumn), generated_column_opt.GetResult()}, 1); } auto &type_opt = list_pr.GetChild(1).Cast(); if (type_opt.HasResult()) { - process.PushChild({transformer.GetRule("Type"), type_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Type), type_opt.GetResult()}, 0); } } @@ -10319,6 +10373,7 @@ PEGTransformerFactory::FinalizeColumnDefinitionTrampoline(PEGTransformer &transf optional> column_constraint {}; if (dynamic_child_count > 0) { vector column_constraint_value; + column_constraint_value.reserve(dynamic_child_count); for (idx_t i = 2; i < 2 + dynamic_child_count; i++) { column_constraint_value.push_back(process.TakeResult(i)); } @@ -10354,7 +10409,7 @@ void PEGTransformerFactory::InitializeNameableColumnConstraintTrampoline(PEGTran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColumnConstraintElement"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColumnConstraintElement), list_pr.GetChild(1)}, 0); } arena_ptr @@ -10442,7 +10497,7 @@ void PEGTransformerFactory::InitializeUniqueConstraintTrampoline(PEGTransformer process.ReserveChildSlots(1); auto &constraint_timing_opt = list_pr.GetChild(1).Cast(); if (constraint_timing_opt.HasResult()) { - process.PushChild({transformer.GetRule("ConstraintTiming"), constraint_timing_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ConstraintTiming), constraint_timing_opt.GetResult()}, 0); } } @@ -10463,7 +10518,7 @@ void PEGTransformerFactory::InitializePrimaryKeyConstraintTrampoline(PEGTransfor process.ReserveChildSlots(1); auto &constraint_timing_opt = list_pr.GetChild(2).Cast(); if (constraint_timing_opt.HasResult()) { - process.PushChild({transformer.GetRule("ConstraintTiming"), constraint_timing_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ConstraintTiming), constraint_timing_opt.GetResult()}, 0); } } @@ -10527,7 +10582,7 @@ void PEGTransformerFactory::InitializeDefaultValueTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColumnDefaultExpr"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColumnDefaultExpr), list_pr.GetChild(1)}, 0); } arena_ptr @@ -10541,7 +10596,7 @@ void PEGTransformerFactory::InitializeCheckConstraintTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } arena_ptr @@ -10558,13 +10613,14 @@ void PEGTransformerFactory::InitializeForeignKeyConstraintTrampoline(PEGTransfor process.ReserveChildSlots(3); auto &key_actions_opt = list_pr.GetChild(3).Cast(); if (key_actions_opt.HasResult()) { - process.PushChild({transformer.GetRule("KeyActions"), key_actions_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::KeyActions), key_actions_opt.GetResult()}, 2); } auto &column_list_opt = list_pr.GetChild(2).Cast(); if (column_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("ColumnList"), ExtractResultFromParens(column_list_opt.GetResult())}, 1); + process.PushChild( + {transformer.GetRule(PEGRuleId::ColumnList), ExtractResultFromParens(column_list_opt.GetResult())}, 1); } - process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BaseTableName), list_pr.GetChild(1)}, 0); } arena_ptr @@ -10587,7 +10643,7 @@ void PEGTransformerFactory::InitializeColumnCollationTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("DottedIdentifier"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::DottedIdentifier), list_pr.GetChild(1)}, 0); } arena_ptr @@ -10602,7 +10658,7 @@ void PEGTransformerFactory::InitializeColumnCompressionTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_pr.GetChild(2)}, 0); } arena_ptr @@ -10639,9 +10695,9 @@ void PEGTransformerFactory::InitializeUpdateFirstKeyActionsTrampoline(PEGTransfo process.ReserveChildSlots(2); auto &delete_action_opt = list_pr.GetChild(1).Cast(); if (delete_action_opt.HasResult()) { - process.PushChild({transformer.GetRule("DeleteAction"), delete_action_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::DeleteAction), delete_action_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("UpdateAction"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::UpdateAction), list_pr.GetChild(0)}, 0); } arena_ptr @@ -10662,9 +10718,9 @@ void PEGTransformerFactory::InitializeDeleteFirstKeyActionsTrampoline(PEGTransfo process.ReserveChildSlots(2); auto &update_action_opt = list_pr.GetChild(1).Cast(); if (update_action_opt.HasResult()) { - process.PushChild({transformer.GetRule("UpdateAction"), update_action_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::UpdateAction), update_action_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("DeleteAction"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::DeleteAction), list_pr.GetChild(0)}, 0); } arena_ptr @@ -10683,7 +10739,7 @@ void PEGTransformerFactory::InitializeUpdateActionTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("KeyAction"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::KeyAction), list_pr.GetChild(2)}, 0); } arena_ptr @@ -10697,7 +10753,7 @@ void PEGTransformerFactory::InitializeDeleteActionTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("KeyAction"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::KeyAction), list_pr.GetChild(2)}, 0); } arena_ptr @@ -10811,7 +10867,7 @@ void PEGTransformerFactory::InitializeTopLevelConstraintTrampoline(PEGTransforme GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("TopLevelConstraintList"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TopLevelConstraintList), list_pr.GetChild(1)}, 0); } arena_ptr @@ -10851,7 +10907,7 @@ void PEGTransformerFactory::InitializeTopCheckConstraintTrampoline(PEGTransforme GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("CheckConstraint"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CheckConstraint), list_pr.GetChild(0)}, 0); } arena_ptr @@ -10868,9 +10924,9 @@ void PEGTransformerFactory::InitializeTopPrimaryKeyConstraintTrampoline(PEGTrans process.ReserveChildSlots(2); auto &constraint_timing_opt = list_pr.GetChild(3).Cast(); if (constraint_timing_opt.HasResult()) { - process.PushChild({transformer.GetRule("ConstraintTiming"), constraint_timing_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ConstraintTiming), constraint_timing_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("ColumnIdList"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColumnIdList), list_pr.GetChild(2)}, 0); } arena_ptr @@ -10891,9 +10947,9 @@ void PEGTransformerFactory::InitializeTopUniqueConstraintTrampoline(PEGTransform process.ReserveChildSlots(2); auto &constraint_timing_opt = list_pr.GetChild(2).Cast(); if (constraint_timing_opt.HasResult()) { - process.PushChild({transformer.GetRule("ConstraintTiming"), constraint_timing_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ConstraintTiming), constraint_timing_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("ColumnIdList"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColumnIdList), list_pr.GetChild(1)}, 0); } arena_ptr @@ -10912,8 +10968,8 @@ void PEGTransformerFactory::InitializeTopForeignKeyConstraintTrampoline(PEGTrans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("ForeignKeyConstraint"), list_pr.GetChild(3)}, 1); - process.PushChild({transformer.GetRule("ColumnIdList"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ForeignKeyConstraint), list_pr.GetChild(3)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ColumnIdList), list_pr.GetChild(2)}, 0); } arena_ptr @@ -10933,7 +10989,7 @@ void PEGTransformerFactory::InitializeColumnIdListTrampoline(PEGTransformer &tra process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColId"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_items[child_idx].get()}, 0 + child_idx); } } @@ -10943,6 +10999,7 @@ PEGTransformerFactory::FinalizeColumnIdListTrampoline(PEGTransformer &transforme auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); vector col_id; + col_id.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { col_id.push_back(process.TakeResult(i)); } @@ -10962,7 +11019,8 @@ void PEGTransformerFactory::InitializeDottedIdentifierTrampoline(PEGTransformer process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("DotColLabel"), repeat_children[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::DotColLabel), repeat_children[child_idx].get()}, + 0 + child_idx); } } else { process.ReserveChildSlots(1 - 1); @@ -10984,6 +11042,7 @@ PEGTransformerFactory::FinalizeDottedIdentifierTrampoline(PEGTransformer &transf optional> dot_col_label {}; if (dynamic_child_count > 0) { vector dot_col_label_value; + dot_col_label_value.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { dot_col_label_value.push_back(process.TakeResult(i)); } @@ -10997,7 +11056,7 @@ void PEGTransformerFactory::InitializeDotColLabelTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColLabel"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColLabel), list_pr.GetChild(1)}, 0); } arena_ptr @@ -11281,7 +11340,7 @@ void PEGTransformerFactory::InitializeColLabelIdentifierTrampoline(PEGTransforme GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColLabel"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColLabel), list_pr.GetChild(0)}, 0); } arena_ptr @@ -11312,12 +11371,13 @@ void PEGTransformerFactory::InitializeGeneratedColumnTrampoline(PEGTransformer & process.ReserveChildSlots(3); auto &generated_column_type_opt = list_pr.GetChild(3).Cast(); if (generated_column_type_opt.HasResult()) { - process.PushChild({transformer.GetRule("GeneratedColumnType"), generated_column_type_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::GeneratedColumnType), generated_column_type_opt.GetResult()}, + 2); } - process.PushChild({transformer.GetRule("Expression"), ExtractResultFromParens(list_pr.GetChild(2))}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), ExtractResultFromParens(list_pr.GetChild(2))}, 1); auto &generated_opt = list_pr.GetChild(0).Cast(); if (generated_opt.HasResult()) { - process.PushChild({transformer.GetRule("Generated"), generated_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Generated), generated_opt.GetResult()}, 0); } } @@ -11406,7 +11466,7 @@ void PEGTransformerFactory::InitializeCommitActionTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("PreserveOrDelete"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::PreserveOrDelete), list_pr.GetChild(2)}, 0); } arena_ptr @@ -11487,22 +11547,22 @@ void PEGTransformerFactory::InitializeCreateTriggerStmtTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(8); - process.PushChild({transformer.GetRule("TriggerBody"), list_pr.GetChild(9)}, 7); + process.PushChild({transformer.GetRule(PEGRuleId::TriggerBody), list_pr.GetChild(9)}, 7); auto &for_each_clause_opt = list_pr.GetChild(8).Cast(); if (for_each_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("ForEachClause"), for_each_clause_opt.GetResult()}, 6); + process.PushChild({transformer.GetRule(PEGRuleId::ForEachClause), for_each_clause_opt.GetResult()}, 6); } auto &referencing_clause_opt = list_pr.GetChild(7).Cast(); if (referencing_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("ReferencingClause"), referencing_clause_opt.GetResult()}, 5); + process.PushChild({transformer.GetRule(PEGRuleId::ReferencingClause), referencing_clause_opt.GetResult()}, 5); } - process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(6)}, 4); - process.PushChild({transformer.GetRule("TriggerEvent"), list_pr.GetChild(4)}, 3); - process.PushChild({transformer.GetRule("TriggerTiming"), list_pr.GetChild(3)}, 2); - process.PushChild({transformer.GetRule("TriggerName"), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::BaseTableName), list_pr.GetChild(6)}, 4); + process.PushChild({transformer.GetRule(PEGRuleId::TriggerEvent), list_pr.GetChild(4)}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::TriggerTiming), list_pr.GetChild(3)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::TriggerName), list_pr.GetChild(2)}, 1); auto &if_not_exists_opt = list_pr.GetChild(1).Cast(); if (if_not_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfNotExists"), if_not_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfNotExists), if_not_exists_opt.GetResult()}, 0); } } @@ -11571,9 +11631,9 @@ void PEGTransformerFactory::InitializeReferencingClauseTrampoline(PEGTransformer process.ReserveChildSlots(2); auto &referencing_item_1_opt = list_pr.GetChild(2).Cast(); if (referencing_item_1_opt.HasResult()) { - process.PushChild({transformer.GetRule("ReferencingItem"), referencing_item_1_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ReferencingItem), referencing_item_1_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("ReferencingItem"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ReferencingItem), list_pr.GetChild(1)}, 0); } arena_ptr @@ -11613,7 +11673,7 @@ void PEGTransformerFactory::InitializeReferencingNewTableAsTrampoline(PEGTransfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(3)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(3)}, 0); } arena_ptr @@ -11628,7 +11688,7 @@ void PEGTransformerFactory::InitializeReferencingOldTableAsTrampoline(PEGTransfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(3)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(3)}, 0); } arena_ptr @@ -11755,7 +11815,7 @@ void PEGTransformerFactory::InitializeTriggerEventUpdateOfTrampoline(PEGTransfor GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("TriggerColumnList"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TriggerColumnList), list_pr.GetChild(2)}, 0); } arena_ptr @@ -11774,7 +11834,7 @@ void PEGTransformerFactory::InitializeTriggerColumnListTrampoline(PEGTransformer process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColId"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_items[child_idx].get()}, 0 + child_idx); } } @@ -11785,6 +11845,7 @@ PEGTransformerFactory::FinalizeTriggerColumnListTrampoline(PEGTransformer &trans auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); auto dynamic_child_count = dynamic_list_items.size(); vector col_id; + col_id.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { col_id.push_back(process.TakeResult(i)); } @@ -11840,11 +11901,11 @@ void PEGTransformerFactory::InitializeCreateTypeStmtTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(3); - process.PushChild({transformer.GetRule("CreateType"), list_pr.GetChild(4)}, 2); - process.PushChild({transformer.GetRule("QualifiedName"), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::CreateType), list_pr.GetChild(4)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedName), list_pr.GetChild(2)}, 1); auto &if_not_exists_opt = list_pr.GetChild(1).Cast(); if (if_not_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfNotExists"), if_not_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfNotExists), if_not_exists_opt.GetResult()}, 0); } } @@ -11885,7 +11946,7 @@ void PEGTransformerFactory::InitializeCreateTypeFromTypeTrampoline(PEGTransforme GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Type"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Type), list_pr.GetChild(0)}, 0); } arena_ptr @@ -11900,8 +11961,8 @@ void PEGTransformerFactory::InitializeEnumSelectTypeTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SelectStatementInternal"), ExtractResultFromParens(list_pr.GetChild(1))}, - 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::SelectStatementInternal), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } arena_ptr @@ -11924,7 +11985,8 @@ void PEGTransformerFactory::InitializeEnumStringLiteralListTrampoline(PEGTransfo process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ArrayBounds"), repeat_children[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ArrayBounds), repeat_children[child_idx].get()}, + 0 + child_idx); } } else { process.ReserveChildSlots(1 - 1); @@ -11947,6 +12009,7 @@ PEGTransformerFactory::FinalizeEnumStringLiteralListTrampoline(PEGTransformer &t if (string_literal_opt.HasResult()) { vector string_literal_value; auto string_literal_items = ExtractParseResultsFromList(string_literal_opt.GetResult()); + string_literal_value.reserve(string_literal_items.size()); for (auto &string_literal_item : string_literal_items) { auto string_literal_item_value = TransformStringLiteral(transformer, string_literal_item.get()); string_literal_value.push_back(string_literal_item_value); @@ -11956,6 +12019,7 @@ PEGTransformerFactory::FinalizeEnumStringLiteralListTrampoline(PEGTransformer &t optional> array_bounds {}; if (dynamic_child_count > 0) { vector array_bounds_value; + array_bounds_value.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { array_bounds_value.push_back(process.TakeResult(i)); } @@ -11969,27 +12033,27 @@ void PEGTransformerFactory::InitializeCreateViewStmtTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(7); - process.PushChild({transformer.GetRule("SelectStatementInternal"), list_pr.GetChild(8)}, 6); + process.PushChild({transformer.GetRule(PEGRuleId::SelectStatementInternal), list_pr.GetChild(8)}, 6); auto &with_list_opt = list_pr.GetChild(6).Cast(); if (with_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("WithList"), with_list_opt.GetResult()}, 5); + process.PushChild({transformer.GetRule(PEGRuleId::WithList), with_list_opt.GetResult()}, 5); } auto &insert_column_list_opt = list_pr.GetChild(5).Cast(); if (insert_column_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("InsertColumnList"), insert_column_list_opt.GetResult()}, 4); + process.PushChild({transformer.GetRule(PEGRuleId::InsertColumnList), insert_column_list_opt.GetResult()}, 4); } - process.PushChild({transformer.GetRule("QualifiedName"), list_pr.GetChild(4)}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedName), list_pr.GetChild(4)}, 3); auto &if_not_exists_opt = list_pr.GetChild(3).Cast(); if (if_not_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfNotExists"), if_not_exists_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::IfNotExists), if_not_exists_opt.GetResult()}, 2); } auto &create_recursive_opt = list_pr.GetChild(1).Cast(); if (create_recursive_opt.HasResult()) { - process.PushChild({transformer.GetRule("CreateRecursive"), create_recursive_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::CreateRecursive), create_recursive_opt.GetResult()}, 1); } auto &create_secure_opt = list_pr.GetChild(0).Cast(); if (create_secure_opt.HasResult()) { - process.PushChild({transformer.GetRule("CreateSecure"), create_secure_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CreateSecure), create_secure_opt.GetResult()}, 0); } } @@ -12051,10 +12115,10 @@ void PEGTransformerFactory::InitializeDeallocateStatementTrampoline(PEGTransform GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_pr.GetChild(2)}, 1); auto &deallocate_prepare_opt = list_pr.GetChild(1).Cast(); if (deallocate_prepare_opt.HasResult()) { - process.PushChild({transformer.GetRule("DeallocatePrepare"), deallocate_prepare_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::DeallocatePrepare), deallocate_prepare_opt.GetResult()}, 0); } } @@ -12088,20 +12152,20 @@ void PEGTransformerFactory::InitializeDeleteStatementTrampoline(PEGTransformer & process.ReserveChildSlots(5); auto &returning_clause_opt = list_pr.GetChild(6).Cast(); if (returning_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("ReturningClause"), returning_clause_opt.GetResult()}, 4); + process.PushChild({transformer.GetRule(PEGRuleId::ReturningClause), returning_clause_opt.GetResult()}, 4); } auto &where_clause_opt = list_pr.GetChild(5).Cast(); if (where_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("WhereClause"), where_clause_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::WhereClause), where_clause_opt.GetResult()}, 3); } auto &delete_using_clause_opt = list_pr.GetChild(4).Cast(); if (delete_using_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("DeleteUsingClause"), delete_using_clause_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::DeleteUsingClause), delete_using_clause_opt.GetResult()}, 2); } - process.PushChild({transformer.GetRule("TargetOptAlias"), list_pr.GetChild(3)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::TargetOptAlias), list_pr.GetChild(3)}, 1); auto &with_clause_opt = list_pr.GetChild(0).Cast(); if (with_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("WithClause"), with_clause_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::WithClause), with_clause_opt.GetResult()}, 0); } } @@ -12137,9 +12201,9 @@ void PEGTransformerFactory::InitializeTruncateStatementTrampoline(PEGTransformer process.ReserveChildSlots(2); auto &truncate_options_opt = list_pr.GetChild(3).Cast(); if (truncate_options_opt.HasResult()) { - process.PushChild({transformer.GetRule("TruncateOptions"), truncate_options_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::TruncateOptions), truncate_options_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BaseTableName), list_pr.GetChild(2)}, 0); } arena_ptr @@ -12185,9 +12249,10 @@ void PEGTransformerFactory::InitializeTruncateAliasOptionsTrampoline(PEGTransfor process.ReserveChildSlots(2); auto &truncate_drop_behavior_opt = list_pr.GetChild(1).Cast(); if (truncate_drop_behavior_opt.HasResult()) { - process.PushChild({transformer.GetRule("TruncateDropBehavior"), truncate_drop_behavior_opt.GetResult()}, 1); + process.PushChild( + {transformer.GetRule(PEGRuleId::TruncateDropBehavior), truncate_drop_behavior_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("TargetAlias"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TargetAlias), list_pr.GetChild(0)}, 0); } arena_ptr @@ -12206,7 +12271,7 @@ void PEGTransformerFactory::InitializeTruncateDropBehaviorTrampoline(PEGTransfor GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("DropBehavior"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::DropBehavior), list_pr.GetChild(0)}, 0); } arena_ptr @@ -12223,9 +12288,9 @@ void PEGTransformerFactory::InitializeTargetOptAliasTrampoline(PEGTransformer &t process.ReserveChildSlots(2); auto &target_alias_opt = list_pr.GetChild(1).Cast(); if (target_alias_opt.HasResult()) { - process.PushChild({transformer.GetRule("TargetAlias"), target_alias_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::TargetAlias), target_alias_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BaseTableName), list_pr.GetChild(0)}, 0); } arena_ptr @@ -12244,7 +12309,7 @@ void PEGTransformerFactory::InitializeTargetAliasTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(1)}, 0); } arena_ptr @@ -12266,7 +12331,7 @@ void PEGTransformerFactory::InitializeDeleteUsingClauseTrampoline(PEGTransformer process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("TableRef"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::TableRef), list_items[child_idx].get()}, 0 + child_idx); } } @@ -12277,6 +12342,7 @@ PEGTransformerFactory::FinalizeDeleteUsingClauseTrampoline(PEGTransformer &trans auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(1)); auto dynamic_child_count = dynamic_list_items.size(); vector> table_ref; + table_ref.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { table_ref.push_back(process.TakeResult>(i)); } @@ -12310,8 +12376,8 @@ void PEGTransformerFactory::InitializeShowDeprecatedSelectTrampoline(PEGTransfor GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("SelectStatementInternal"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("ShowRule"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SelectStatementInternal), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ShowRule), list_pr.GetChild(0)}, 0); } arena_ptr @@ -12327,8 +12393,8 @@ void PEGTransformerFactory::InitializeDescribeSelectTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("SelectStatementInternal"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("DescribeOrSummarize"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SelectStatementInternal), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::DescribeOrSummarize), list_pr.GetChild(0)}, 0); } arena_ptr @@ -12344,7 +12410,7 @@ void PEGTransformerFactory::InitializeShowAllTablesTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ShowOrDescribe"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ShowOrDescribe), list_pr.GetChild(0)}, 0); } arena_ptr @@ -12363,8 +12429,8 @@ void PEGTransformerFactory::InitializeShowTablesTrampoline(PEGTransformer &trans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("QualifiedName"), list_pr.GetChild(3)}, 1); - process.PushChild({transformer.GetRule("ShowOrDescribe"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedName), list_pr.GetChild(3)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ShowOrDescribe), list_pr.GetChild(0)}, 0); } arena_ptr @@ -12381,9 +12447,9 @@ void PEGTransformerFactory::InitializeShowByNameTrampoline(PEGTransformer &trans process.ReserveChildSlots(2); auto &show_target_opt = list_pr.GetChild(1).Cast(); if (show_target_opt.HasResult()) { - process.PushChild({transformer.GetRule("ShowTarget"), show_target_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ShowTarget), show_target_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("ShowRule"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ShowRule), list_pr.GetChild(0)}, 0); } arena_ptr @@ -12403,9 +12469,9 @@ void PEGTransformerFactory::InitializeDescribeByNameTrampoline(PEGTransformer &t process.ReserveChildSlots(2); auto &describe_target_opt = list_pr.GetChild(1).Cast(); if (describe_target_opt.HasResult()) { - process.PushChild({transformer.GetRule("DescribeTarget"), describe_target_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::DescribeTarget), describe_target_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("DescribeOrSummarize"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::DescribeOrSummarize), list_pr.GetChild(0)}, 0); } arena_ptr @@ -12471,7 +12537,7 @@ void PEGTransformerFactory::InitializeShowDeprecatedQualifiedTableNameTrampoline GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("QualifiedTableName"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedTableName), list_pr.GetChild(0)}, 0); } arena_ptr @@ -12521,7 +12587,7 @@ void PEGTransformerFactory::InitializeDescribeBaseTableNameTrampoline(PEGTransfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BaseTableName), list_pr.GetChild(0)}, 0); } arena_ptr @@ -12550,7 +12616,7 @@ void PEGTransformerFactory::InitializeSummarizeTrampoline(PEGTransformer &transf GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SummarizeRule"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SummarizeRule), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeSummarizeTrampoline(PEGTransformer &transformer, @@ -12653,7 +12719,7 @@ void PEGTransformerFactory::InitializeDetachStatementTrampoline(PEGTransformer & process.ReserveChildSlots(1); auto &if_exists_opt = list_pr.GetChild(2).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 0); } } @@ -12679,9 +12745,9 @@ void PEGTransformerFactory::InitializeDropStatementTrampoline(PEGTransformer &tr process.ReserveChildSlots(2); auto &drop_behavior_opt = list_pr.GetChild(2).Cast(); if (drop_behavior_opt.HasResult()) { - process.PushChild({transformer.GetRule("DropBehavior"), drop_behavior_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::DropBehavior), drop_behavior_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("DropEntries"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::DropEntries), list_pr.GetChild(1)}, 0); } arena_ptr @@ -12720,11 +12786,11 @@ void PEGTransformerFactory::InitializeDropTriggerTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(3); - process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(4)}, 2); - process.PushChild({transformer.GetRule("TriggerName"), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::BaseTableName), list_pr.GetChild(4)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::TriggerName), list_pr.GetChild(2)}, 1); auto &if_exists_opt = list_pr.GetChild(1).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 0); } } @@ -12748,13 +12814,13 @@ void PEGTransformerFactory::InitializeDropTableTrampoline(PEGTransformer &transf process.ReserveChildSlots(3 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("BaseTableName"), list_items[child_idx].get()}, 2 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::BaseTableName), list_items[child_idx].get()}, 2 + child_idx); } auto &if_exists_opt = list_pr.GetChild(1).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("TableOrView"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TableOrView), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeDropTableTrampoline(PEGTransformer &transformer, @@ -12768,6 +12834,7 @@ arena_ptr PEGTransformerFactory::FinalizeDropTableTrampoli if_exists = process.TakeResult(1); } vector> base_table_name; + base_table_name.reserve(dynamic_child_count); for (idx_t i = 2; i < 2 + dynamic_child_count; i++) { base_table_name.push_back(process.TakeResult>(i)); } @@ -12783,13 +12850,14 @@ void PEGTransformerFactory::InitializeDropTableFunctionTrampoline(PEGTransformer process.ReserveChildSlots(3 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("QualifiedTableFunction"), list_items[child_idx].get()}, 2 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedTableFunction), list_items[child_idx].get()}, + 2 + child_idx); } auto &if_exists_opt = list_pr.GetChild(1).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("CommentMacroTable"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CommentMacroTable), list_pr.GetChild(0)}, 0); } arena_ptr @@ -12804,6 +12872,7 @@ PEGTransformerFactory::FinalizeDropTableFunctionTrampoline(PEGTransformer &trans if_exists = process.TakeResult(1); } vector qualified_table_function; + qualified_table_function.reserve(dynamic_child_count); for (idx_t i = 2; i < 2 + dynamic_child_count; i++) { qualified_table_function.push_back(process.TakeResult(i)); } @@ -12819,13 +12888,14 @@ void PEGTransformerFactory::InitializeDropFunctionTrampoline(PEGTransformer &tra process.ReserveChildSlots(3 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("FunctionIdentifier"), list_items[child_idx].get()}, 2 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::FunctionIdentifier), list_items[child_idx].get()}, + 2 + child_idx); } auto &if_exists_opt = list_pr.GetChild(1).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("FunctionTypeMacro"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::FunctionTypeMacro), list_pr.GetChild(0)}, 0); } arena_ptr @@ -12839,6 +12909,7 @@ PEGTransformerFactory::FinalizeDropFunctionTrampoline(PEGTransformer &transforme if_exists = process.TakeResult(1); } vector function_identifier; + function_identifier.reserve(dynamic_child_count); for (idx_t i = 2; i < 2 + dynamic_child_count; i++) { function_identifier.push_back(process.TakeResult(i)); } @@ -12854,11 +12925,11 @@ void PEGTransformerFactory::InitializeDropSchemaTrampoline(PEGTransformer &trans process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("QualifiedName"), list_items[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedName), list_items[child_idx].get()}, 1 + child_idx); } auto &if_exists_opt = list_pr.GetChild(1).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 0); } } @@ -12872,6 +12943,7 @@ PEGTransformerFactory::FinalizeDropSchemaTrampoline(PEGTransformer &transformer, if_exists = process.TakeResult(0); } vector qualified_name; + qualified_name.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { qualified_name.push_back(process.TakeResult(i)); } @@ -12887,11 +12959,12 @@ void PEGTransformerFactory::InitializeDropIndexTrampoline(PEGTransformer &transf process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("QualifiedIndexName"), list_items[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedIndexName), list_items[child_idx].get()}, + 1 + child_idx); } auto &if_exists_opt = list_pr.GetChild(1).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 0); } } @@ -12905,6 +12978,7 @@ arena_ptr PEGTransformerFactory::FinalizeDropIndexTrampoli if_exists = process.TakeResult(0); } vector qualified_index_name; + qualified_index_name.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { qualified_index_name.push_back(process.TakeResult(i)); } @@ -12951,7 +13025,7 @@ void PEGTransformerFactory::InitializeSchemaReservedIndexTrampoline(PEGTransform GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SchemaQualification"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SchemaQualification), list_pr.GetChild(0)}, 0); } arena_ptr @@ -12968,8 +13042,8 @@ void PEGTransformerFactory::InitializeCatalogReservedSchemaIndexTrampoline(PEGTr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("ReservedSchemaQualification"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("CatalogQualification"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ReservedSchemaQualification), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::CatalogQualification), list_pr.GetChild(0)}, 0); } arena_ptr @@ -12992,11 +13066,12 @@ void PEGTransformerFactory::InitializeDropSequenceTrampoline(PEGTransformer &tra process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("QualifiedSequenceName"), list_items[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedSequenceName), list_items[child_idx].get()}, + 1 + child_idx); } auto &if_exists_opt = list_pr.GetChild(1).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 0); } } @@ -13010,6 +13085,7 @@ PEGTransformerFactory::FinalizeDropSequenceTrampoline(PEGTransformer &transforme if_exists = process.TakeResult(0); } vector qualified_sequence_name; + qualified_sequence_name.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { qualified_sequence_name.push_back(process.TakeResult(i)); } @@ -13025,11 +13101,11 @@ void PEGTransformerFactory::InitializeDropCollationTrampoline(PEGTransformer &tr process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("CollationName"), list_items[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::CollationName), list_items[child_idx].get()}, 1 + child_idx); } auto &if_exists_opt = list_pr.GetChild(1).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 0); } } @@ -13044,6 +13120,7 @@ PEGTransformerFactory::FinalizeDropCollationTrampoline(PEGTransformer &transform if_exists = process.TakeResult(0); } vector collation_name; + collation_name.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { collation_name.push_back(process.TakeResult(i)); } @@ -13059,11 +13136,12 @@ void PEGTransformerFactory::InitializeDropTypeTrampoline(PEGTransformer &transfo process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("QualifiedTypeName"), list_items[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedTypeName), list_items[child_idx].get()}, + 1 + child_idx); } auto &if_exists_opt = list_pr.GetChild(1).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 0); } } @@ -13077,6 +13155,7 @@ arena_ptr PEGTransformerFactory::FinalizeDropTypeTrampolin if_exists = process.TakeResult(0); } vector qualified_type_name; + qualified_type_name.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { qualified_type_name.push_back(process.TakeResult(i)); } @@ -13090,16 +13169,16 @@ void PEGTransformerFactory::InitializeDropSecretTrampoline(PEGTransformer &trans process.ReserveChildSlots(4); auto &drop_secret_storage_opt = list_pr.GetChild(4).Cast(); if (drop_secret_storage_opt.HasResult()) { - process.PushChild({transformer.GetRule("DropSecretStorage"), drop_secret_storage_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::DropSecretStorage), drop_secret_storage_opt.GetResult()}, 3); } - process.PushChild({transformer.GetRule("SecretName"), list_pr.GetChild(3)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::SecretName), list_pr.GetChild(3)}, 2); auto &if_exists_opt = list_pr.GetChild(2).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 1); } auto &temporary_opt = list_pr.GetChild(0).Cast(); if (temporary_opt.HasResult()) { - process.PushChild({transformer.GetRule("Temporary"), temporary_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Temporary), temporary_opt.GetResult()}, 0); } } @@ -13274,9 +13353,10 @@ void PEGTransformerFactory::InitializeExecuteStatementTrampoline(PEGTransformer process.ReserveChildSlots(2); auto &table_function_arguments_opt = list_pr.GetChild(2).Cast(); if (table_function_arguments_opt.HasResult()) { - process.PushChild({transformer.GetRule("TableFunctionArguments"), table_function_arguments_opt.GetResult()}, 1); + process.PushChild( + {transformer.GetRule(PEGRuleId::TableFunctionArguments), table_function_arguments_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_pr.GetChild(1)}, 0); } arena_ptr @@ -13295,14 +13375,14 @@ void PEGTransformerFactory::InitializeExplainStatementTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(3); - process.PushChild({transformer.GetRule("ExplainableStatements"), list_pr.GetChild(3)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::ExplainableStatements), list_pr.GetChild(3)}, 2); auto &explain_option_list_opt = list_pr.GetChild(2).Cast(); if (explain_option_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("ExplainOptionList"), explain_option_list_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ExplainOptionList), explain_option_list_opt.GetResult()}, 1); } auto &analyze_keyword_opt = list_pr.GetChild(1).Cast(); if (analyze_keyword_opt.HasResult()) { - process.PushChild({transformer.GetRule("AnalyzeKeyword"), analyze_keyword_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::AnalyzeKeyword), analyze_keyword_opt.GetResult()}, 0); } } @@ -13331,7 +13411,7 @@ void PEGTransformerFactory::InitializeExplainOptionListTrampoline(PEGTransformer process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ExplainOption"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ExplainOption), list_items[child_idx].get()}, 0 + child_idx); } } @@ -13342,6 +13422,7 @@ PEGTransformerFactory::FinalizeExplainOptionListTrampoline(PEGTransformer &trans auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); vector explain_option; + explain_option.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { explain_option.push_back(process.TakeResult(i)); } @@ -13355,9 +13436,9 @@ void PEGTransformerFactory::InitializeExplainOptionTrampoline(PEGTransformer &tr process.ReserveChildSlots(2); auto &expression_opt = list_pr.GetChild(1).Cast(); if (expression_opt.HasResult()) { - process.PushChild({transformer.GetRule("Expression"), expression_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), expression_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("ExplainOptionName"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ExplainOptionName), list_pr.GetChild(0)}, 0); } arena_ptr @@ -13412,7 +13493,7 @@ void PEGTransformerFactory::InitializeExplainSelectStatementTrampoline(PEGTransf GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SelectStatementInternal"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SelectStatementInternal), list_pr.GetChild(0)}, 0); } arena_ptr @@ -13450,11 +13531,12 @@ void PEGTransformerFactory::InitializeExportStatementTrampoline(PEGTransformer & process.ReserveChildSlots(2); auto &generic_copy_option_list_opt = list_pr.GetChild(4).Cast(); if (generic_copy_option_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("GenericCopyOptionList"), generic_copy_option_list_opt.GetResult()}, 1); + process.PushChild( + {transformer.GetRule(PEGRuleId::GenericCopyOptionList), generic_copy_option_list_opt.GetResult()}, 1); } auto &export_source_opt = list_pr.GetChild(2).Cast(); if (export_source_opt.HasResult()) { - process.PushChild({transformer.GetRule("ExportSource"), export_source_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ExportSource), export_source_opt.GetResult()}, 0); } } @@ -13533,12 +13615,13 @@ void PEGTransformerFactory::InitializeNestedSchemaTableColumnNameTrampoline(PEGT process.ReserveChildSlots(4 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ReservedSchemaQualification"), repeat_children[child_idx].get()}, - 3 + child_idx); + process.PushChild( + {transformer.GetRule(PEGRuleId::ReservedSchemaQualification), repeat_children[child_idx].get()}, + 3 + child_idx); } - process.PushChild({transformer.GetRule("ReservedSchemaQualification"), list_pr.GetChild(2)}, 2); - process.PushChild({transformer.GetRule("ReservedSchemaQualification"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("CatalogQualification"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ReservedSchemaQualification), list_pr.GetChild(2)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::ReservedSchemaQualification), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::CatalogQualification), list_pr.GetChild(0)}, 0); } arena_ptr @@ -13552,6 +13635,7 @@ PEGTransformerFactory::FinalizeNestedSchemaTableColumnNameTrampoline(PEGTransfor auto reserved_schema_qualification = process.TakeResult(1); auto reserved_schema_qualification_1 = process.TakeResult(2); vector reserved_schema_qualification_2; + reserved_schema_qualification_2.reserve(dynamic_child_count); for (idx_t i = 3; i < 3 + dynamic_child_count; i++) { reserved_schema_qualification_2.push_back(process.TakeResult(i)); } @@ -13566,9 +13650,9 @@ void PEGTransformerFactory::InitializeCatalogReservedSchemaTableColumnNameTrampo PEGTransformer &transformer, GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(3); - process.PushChild({transformer.GetRule("ReservedTableQualification"), list_pr.GetChild(2)}, 2); - process.PushChild({transformer.GetRule("ReservedSchemaQualification"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("CatalogQualification"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ReservedTableQualification), list_pr.GetChild(2)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::ReservedSchemaQualification), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::CatalogQualification), list_pr.GetChild(0)}, 0); } arena_ptr @@ -13589,8 +13673,8 @@ void PEGTransformerFactory::InitializeSchemaReservedTableColumnNameTrampoline(PE GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("ReservedTableQualification"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("SchemaQualification"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ReservedTableQualification), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::SchemaQualification), list_pr.GetChild(0)}, 0); } arena_ptr @@ -13609,7 +13693,7 @@ void PEGTransformerFactory::InitializeTableReservedColumnNameTrampoline(PEGTrans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("TableQualification"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TableQualification), list_pr.GetChild(0)}, 0); } arena_ptr @@ -13628,18 +13712,18 @@ void PEGTransformerFactory::InitializeFunctionExpressionTrampoline(PEGTransforme process.ReserveChildSlots(5); auto &over_clause_opt = list_pr.GetChild(5).Cast(); if (over_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("OverClause"), over_clause_opt.GetResult()}, 4); + process.PushChild({transformer.GetRule(PEGRuleId::OverClause), over_clause_opt.GetResult()}, 4); } auto &filter_clause_opt = list_pr.GetChild(3).Cast(); if (filter_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("FilterClause"), filter_clause_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::FilterClause), filter_clause_opt.GetResult()}, 3); } auto &within_group_clause_opt = list_pr.GetChild(2).Cast(); if (within_group_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("WithinGroupClause"), within_group_clause_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::WithinGroupClause), within_group_clause_opt.GetResult()}, 2); } - process.PushChild({transformer.GetRule("FunctionExpressionArguments"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("FunctionIdentifier"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::FunctionExpressionArguments), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::FunctionIdentifier), list_pr.GetChild(0)}, 0); } arena_ptr @@ -13674,7 +13758,8 @@ void PEGTransformerFactory::InitializeFunctionExpressionArgumentsTrampoline(PEGT auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); process.PushChild( - {transformer.GetRule("FunctionExpressionArgumentList"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); + {transformer.GetRule(PEGRuleId::FunctionExpressionArgumentList), ExtractResultFromParens(list_pr.GetChild(0))}, + 0); } arena_ptr @@ -13691,19 +13776,21 @@ void PEGTransformerFactory::InitializeFunctionExpressionArgumentListTrampoline(P process.ReserveChildSlots(4); auto &ignore_or_respect_nulls_opt = list_pr.GetChild(3).Cast(); if (ignore_or_respect_nulls_opt.HasResult()) { - process.PushChild({transformer.GetRule("IgnoreOrRespectNulls"), ignore_or_respect_nulls_opt.GetResult()}, 3); + process.PushChild( + {transformer.GetRule(PEGRuleId::IgnoreOrRespectNulls), ignore_or_respect_nulls_opt.GetResult()}, 3); } auto &order_by_clause_opt = list_pr.GetChild(2).Cast(); if (order_by_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("OrderByClause"), order_by_clause_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::OrderByClause), order_by_clause_opt.GetResult()}, 2); } auto &function_argument_list_opt = list_pr.GetChild(1).Cast(); if (function_argument_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("FunctionArgumentList"), function_argument_list_opt.GetResult()}, 1); + process.PushChild( + {transformer.GetRule(PEGRuleId::FunctionArgumentList), function_argument_list_opt.GetResult()}, 1); } auto &distinct_or_all_opt = list_pr.GetChild(0).Cast(); if (distinct_or_all_opt.HasResult()) { - process.PushChild({transformer.GetRule("DistinctOrAll"), distinct_or_all_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::DistinctOrAll), distinct_or_all_opt.GetResult()}, 0); } } @@ -13740,7 +13827,8 @@ void PEGTransformerFactory::InitializeFunctionArgumentListTrampoline(PEGTransfor process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("FunctionArgument"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::FunctionArgument), list_items[child_idx].get()}, + 0 + child_idx); } } @@ -13751,6 +13839,7 @@ PEGTransformerFactory::FinalizeFunctionArgumentListTrampoline(PEGTransformer &tr auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); auto dynamic_child_count = dynamic_list_items.size(); vector function_argument; + function_argument.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { function_argument.push_back(process.TakeResult(i)); } @@ -13805,13 +13894,14 @@ void PEGTransformerFactory::InitializeCatalogReservedSchemaFunctionNameTrampolin process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ReservedSchemaQualification"), repeat_children[child_idx].get()}, - 1 + child_idx); + process.PushChild( + {transformer.GetRule(PEGRuleId::ReservedSchemaQualification), repeat_children[child_idx].get()}, + 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("CatalogQualification"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CatalogQualification), list_pr.GetChild(0)}, 0); } arena_ptr @@ -13829,6 +13919,7 @@ PEGTransformerFactory::FinalizeCatalogReservedSchemaFunctionNameTrampoline(PEGTr optional> reserved_schema_qualification {}; if (dynamic_child_count > 0) { vector reserved_schema_qualification_value; + reserved_schema_qualification_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { reserved_schema_qualification_value.push_back(process.TakeResult(i)); } @@ -13844,7 +13935,7 @@ void PEGTransformerFactory::InitializeSchemaReservedFunctionNameTrampoline(PEGTr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SchemaQualification"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SchemaQualification), list_pr.GetChild(0)}, 0); } arena_ptr @@ -13905,7 +13996,7 @@ void PEGTransformerFactory::InitializeWithinGroupClauseTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("OrderByClause"), ExtractResultFromParens(list_pr.GetChild(2))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OrderByClause), ExtractResultFromParens(list_pr.GetChild(2))}, 0); } arena_ptr @@ -13920,7 +14011,7 @@ void PEGTransformerFactory::InitializeFilterClauseTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("FilterClauseExpression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::FilterClauseExpression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -13934,7 +14025,8 @@ void PEGTransformerFactory::InitializeFilterClauseExpressionTrampoline(PEGTransf GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("FilterClauseContents"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::FilterClauseContents), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr @@ -13949,7 +14041,7 @@ void PEGTransformerFactory::InitializeFilterClauseContentsTrampoline(PEGTransfor GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -14018,7 +14110,7 @@ void PEGTransformerFactory::InitializeParenthesisExpressionTrampoline(PEGTransfo process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 0 + child_idx); } } else { process.ReserveChildSlots(1 - 1); @@ -14039,6 +14131,7 @@ PEGTransformerFactory::FinalizeParenthesisExpressionTrampoline(PEGTransformer &t auto &expression_opt = ExtractResultFromParens(list_pr.GetChild(0)).Cast(); if (expression_opt.HasResult()) { vector> expression_value; + expression_value.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { expression_value.push_back(process.TakeResult>(i)); } @@ -14107,8 +14200,8 @@ void PEGTransformerFactory::InitializeCastExpressionTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("CastArguments"), ExtractResultFromParens(list_pr.GetChild(1))}, 1); - process.PushChild({transformer.GetRule("CastOrTryCast"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CastArguments), ExtractResultFromParens(list_pr.GetChild(1))}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::CastOrTryCast), list_pr.GetChild(0)}, 0); } arena_ptr @@ -14124,8 +14217,8 @@ void PEGTransformerFactory::InitializeCastArgumentsTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("Type"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Type), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -14185,7 +14278,7 @@ void PEGTransformerFactory::InitializeColIdDotTrampoline(PEGTransformer &transfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeColIdDotTrampoline(PEGTransformer &transformer, @@ -14201,19 +14294,19 @@ void PEGTransformerFactory::InitializeStarExpressionTrampoline(PEGTransformer &t process.ReserveChildSlots(4); auto &rename_list_opt = list_pr.GetChild(4).Cast(); if (rename_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("RenameList"), rename_list_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::RenameList), rename_list_opt.GetResult()}, 3); } auto &replace_list_opt = list_pr.GetChild(3).Cast(); if (replace_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("ReplaceList"), replace_list_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::ReplaceList), replace_list_opt.GetResult()}, 2); } auto &exclude_list_opt = list_pr.GetChild(2).Cast(); if (exclude_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("ExcludeList"), exclude_list_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ExcludeList), exclude_list_opt.GetResult()}, 1); } auto &star_qualifier_list_opt = list_pr.GetChild(0).Cast(); if (star_qualifier_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("StarQualifierList"), star_qualifier_list_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::StarQualifierList), star_qualifier_list_opt.GetResult()}, 0); } } @@ -14250,7 +14343,7 @@ void PEGTransformerFactory::InitializeStarQualifierListTrampoline(PEGTransformer process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColIdDot"), repeat_children[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdDot), repeat_children[child_idx].get()}, 0 + child_idx); } } @@ -14262,6 +14355,7 @@ PEGTransformerFactory::FinalizeStarQualifierListTrampoline(PEGTransformer &trans auto dynamic_repeat_children = dynamic_repeat_pr.GetChildren(); auto dynamic_child_count = dynamic_repeat_children.size(); vector col_id_dot; + col_id_dot.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { col_id_dot.push_back(process.TakeResult(i)); } @@ -14273,7 +14367,7 @@ void PEGTransformerFactory::InitializeExcludeListTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ExcludeNames"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ExcludeNames), list_pr.GetChild(1)}, 0); } arena_ptr @@ -14311,7 +14405,7 @@ void PEGTransformerFactory::InitializeExcludeNameListTrampoline(PEGTransformer & process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ExcludeName"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ExcludeName), list_items[child_idx].get()}, 0 + child_idx); } } @@ -14322,6 +14416,7 @@ PEGTransformerFactory::FinalizeExcludeNameListTrampoline(PEGTransformer &transfo auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); vector exclude_name; + exclude_name.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { exclude_name.push_back(process.TakeResult(i)); } @@ -14333,7 +14428,7 @@ void PEGTransformerFactory::InitializeExcludeNameSingleTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ExcludeName"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ExcludeName), list_pr.GetChild(0)}, 0); } arena_ptr @@ -14368,7 +14463,7 @@ void PEGTransformerFactory::InitializeExcludeDottedNameTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("DottedIdentifier"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::DottedIdentifier), list_pr.GetChild(0)}, 0); } arena_ptr @@ -14383,7 +14478,7 @@ void PEGTransformerFactory::InitializeExcludeColumnNameTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_pr.GetChild(0)}, 0); } arena_ptr @@ -14398,7 +14493,7 @@ void PEGTransformerFactory::InitializeReplaceListTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ReplaceEntries"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ReplaceEntries), list_pr.GetChild(1)}, 0); } arena_ptr @@ -14433,7 +14528,7 @@ void PEGTransformerFactory::InitializeReplaceEntrySingleTrampoline(PEGTransforme GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ReplaceEntry"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ReplaceEntry), list_pr.GetChild(0)}, 0); } arena_ptr @@ -14452,7 +14547,7 @@ void PEGTransformerFactory::InitializeReplaceEntryListTrampoline(PEGTransformer process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ReplaceEntry"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ReplaceEntry), list_items[child_idx].get()}, 0 + child_idx); } } @@ -14463,6 +14558,7 @@ PEGTransformerFactory::FinalizeReplaceEntryListTrampoline(PEGTransformer &transf auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); vector>> replace_entry; + replace_entry.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { replace_entry.push_back(process.TakeResult>>(i)); } @@ -14474,8 +14570,8 @@ void PEGTransformerFactory::InitializeReplaceEntryTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("ColumnReference"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColumnReference), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -14490,7 +14586,7 @@ void PEGTransformerFactory::InitializeRenameListTrampoline(PEGTransformer &trans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("RenameEntries"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::RenameEntries), list_pr.GetChild(1)}, 0); } arena_ptr @@ -14529,7 +14625,7 @@ void PEGTransformerFactory::InitializeRenameEntryListTrampoline(PEGTransformer & process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("RenameEntry"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::RenameEntry), list_items[child_idx].get()}, 0 + child_idx); } } @@ -14540,6 +14636,7 @@ PEGTransformerFactory::FinalizeRenameEntryListTrampoline(PEGTransformer &transfo auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); vector> rename_entry; + rename_entry.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { rename_entry.push_back(process.TakeResult>(i)); } @@ -14551,7 +14648,7 @@ void PEGTransformerFactory::InitializeSingleRenameEntryTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("RenameEntry"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::RenameEntry), list_pr.GetChild(0)}, 0); } arena_ptr @@ -14566,7 +14663,7 @@ void PEGTransformerFactory::InitializeRenameEntryTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ExcludeName"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ExcludeName), list_pr.GetChild(0)}, 0); } arena_ptr @@ -14582,14 +14679,14 @@ void PEGTransformerFactory::InitializeSubqueryExpressionTrampoline(PEGTransforme GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(3); - process.PushChild({transformer.GetRule("SubqueryReference"), list_pr.GetChild(2)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::SubqueryReference), list_pr.GetChild(2)}, 2); auto &subquery_exists_opt = list_pr.GetChild(1).Cast(); if (subquery_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("SubqueryExists"), subquery_exists_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::SubqueryExists), subquery_exists_opt.GetResult()}, 1); } auto &subquery_not_opt = list_pr.GetChild(0).Cast(); if (subquery_not_opt.HasResult()) { - process.PushChild({transformer.GetRule("SubqueryNot"), subquery_not_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SubqueryNot), subquery_not_opt.GetResult()}, 0); } } @@ -14642,15 +14739,17 @@ void PEGTransformerFactory::InitializeCaseExpressionTrampoline(PEGTransformer &t process.ReserveChildSlots(3 + dynamic_child_count - 1); auto &case_else_opt = list_pr.GetChild(3).Cast(); if (case_else_opt.HasResult()) { - process.PushChild({transformer.GetRule("CaseElse"), case_else_opt.GetResult()}, 2 + dynamic_child_count - 1); + process.PushChild({transformer.GetRule(PEGRuleId::CaseElse), case_else_opt.GetResult()}, + 2 + dynamic_child_count - 1); } for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("CaseWhenThen"), repeat_children[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::CaseWhenThen), repeat_children[child_idx].get()}, + 1 + child_idx); } auto &expression_opt = list_pr.GetChild(1).Cast(); if (expression_opt.HasResult()) { - process.PushChild({transformer.GetRule("Expression"), expression_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), expression_opt.GetResult()}, 0); } } @@ -14666,6 +14765,7 @@ PEGTransformerFactory::FinalizeCaseExpressionTrampoline(PEGTransformer &transfor expression = process.TakeResult>(0); } vector case_when_then; + case_when_then.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { case_when_then.push_back(process.TakeResult(i)); } @@ -14682,8 +14782,8 @@ void PEGTransformerFactory::InitializeCaseWhenThenTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(3)}, 1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(3)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -14698,7 +14798,7 @@ void PEGTransformerFactory::InitializeCaseElseTrampoline(PEGTransformer &transfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(1)}, 0); } arena_ptr PEGTransformerFactory::FinalizeCaseElseTrampoline(PEGTransformer &transformer, @@ -14712,7 +14812,7 @@ void PEGTransformerFactory::InitializeTypeLiteralTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Type"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Type), list_pr.GetChild(0)}, 0); } arena_ptr @@ -14730,9 +14830,9 @@ void PEGTransformerFactory::InitializeIntervalLiteralTrampoline(PEGTransformer & process.ReserveChildSlots(2); auto &interval_opt = list_pr.GetChild(2).Cast(); if (interval_opt.HasResult()) { - process.PushChild({transformer.GetRule("Interval"), interval_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Interval), interval_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("IntervalParameter"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IntervalParameter), list_pr.GetChild(1)}, 0); } arena_ptr @@ -14788,10 +14888,11 @@ void PEGTransformerFactory::InitializeFrameClauseTrampoline(PEGTransformer &tran process.ReserveChildSlots(3); auto &window_exclude_clause_opt = list_pr.GetChild(2).Cast(); if (window_exclude_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("WindowExcludeClause"), window_exclude_clause_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::WindowExcludeClause), window_exclude_clause_opt.GetResult()}, + 2); } - process.PushChild({transformer.GetRule("FrameExtent"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("Framing"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::FrameExtent), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Framing), list_pr.GetChild(0)}, 0); } arena_ptr @@ -14905,7 +15006,7 @@ void PEGTransformerFactory::InitializeSingleFrameExtentTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("FrameBound"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::FrameBound), list_pr.GetChild(0)}, 0); } arena_ptr @@ -14920,8 +15021,8 @@ void PEGTransformerFactory::InitializeBetweenFrameExtentTrampoline(PEGTransforme GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("FrameBound"), list_pr.GetChild(3)}, 1); - process.PushChild({transformer.GetRule("FrameBound"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::FrameBound), list_pr.GetChild(3)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::FrameBound), list_pr.GetChild(1)}, 0); } arena_ptr @@ -14957,7 +15058,7 @@ void PEGTransformerFactory::InitializeFrameUnboundedTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("PrecedingOrFollowing"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::PrecedingOrFollowing), list_pr.GetChild(1)}, 0); } arena_ptr @@ -14972,8 +15073,8 @@ void PEGTransformerFactory::InitializeFrameExpressionTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("PrecedingOrFollowing"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::PrecedingOrFollowing), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -15046,7 +15147,7 @@ void PEGTransformerFactory::InitializeWindowExcludeClauseTrampoline(PEGTransform GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("WindowExcludeElement"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::WindowExcludeElement), list_pr.GetChild(1)}, 0); } arena_ptr @@ -15197,8 +15298,8 @@ void PEGTransformerFactory::InitializeWindowFrameNameContentsParensTrampoline(PE GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("WindowFrameNameContents"), ExtractResultFromParens(list_pr.GetChild(0))}, - 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::WindowFrameNameContents), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr @@ -15213,10 +15314,10 @@ void PEGTransformerFactory::InitializeWindowFrameNameContentsTrampoline(PEGTrans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("WindowFrameContents"), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::WindowFrameContents), list_pr.GetChild(1)}, 1); auto &base_window_name_opt = list_pr.GetChild(0).Cast(); if (base_window_name_opt.HasResult()) { - process.PushChild({transformer.GetRule("BaseWindowName"), base_window_name_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BaseWindowName), base_window_name_opt.GetResult()}, 0); } } @@ -15236,7 +15337,8 @@ void PEGTransformerFactory::InitializeWindowFrameContentsParensTrampoline(PEGTra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("WindowFrameContents"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::WindowFrameContents), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr @@ -15253,15 +15355,15 @@ void PEGTransformerFactory::InitializeWindowFrameContentsTrampoline(PEGTransform process.ReserveChildSlots(3); auto &frame_clause_opt = list_pr.GetChild(2).Cast(); if (frame_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("FrameClause"), frame_clause_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::FrameClause), frame_clause_opt.GetResult()}, 2); } auto &order_by_clause_opt = list_pr.GetChild(1).Cast(); if (order_by_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("OrderByClause"), order_by_clause_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::OrderByClause), order_by_clause_opt.GetResult()}, 1); } auto &window_partition_opt = list_pr.GetChild(0).Cast(); if (window_partition_opt.HasResult()) { - process.PushChild({transformer.GetRule("WindowPartition"), window_partition_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::WindowPartition), window_partition_opt.GetResult()}, 0); } } @@ -15307,7 +15409,7 @@ void PEGTransformerFactory::InitializeWindowPartitionTrampoline(PEGTransformer & process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 0 + child_idx); } } @@ -15318,6 +15420,7 @@ PEGTransformerFactory::FinalizeWindowPartitionTrampoline(PEGTransformer &transfo auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(2)); auto dynamic_child_count = dynamic_list_items.size(); vector> expression; + expression.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { expression.push_back(process.TakeResult>(i)); } @@ -15350,7 +15453,7 @@ void PEGTransformerFactory::InitializeArrayBoundedListExpressionTrampoline(PEGTr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("BoundedListExpression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BoundedListExpression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -15369,8 +15472,8 @@ void PEGTransformerFactory::InitializeArrayParensSelectTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SelectStatementInternal"), ExtractResultFromParens(list_pr.GetChild(1))}, - 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::SelectStatementInternal), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } arena_ptr @@ -15392,7 +15495,7 @@ void PEGTransformerFactory::InitializeBoundedListExpressionTrampoline(PEGTransfo process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 0 + child_idx); } } else { process.ReserveChildSlots(1 - 1); @@ -15413,6 +15516,7 @@ PEGTransformerFactory::FinalizeBoundedListExpressionTrampoline(PEGTransformer &t auto &expression_opt = list_pr.GetChild(1).Cast(); if (expression_opt.HasResult()) { vector> expression_value; + expression_value.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { expression_value.push_back(process.TakeResult>(i)); } @@ -15433,7 +15537,8 @@ void PEGTransformerFactory::InitializeStructExpressionTrampoline(PEGTransformer process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("StructField"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::StructField), list_items[child_idx].get()}, + 0 + child_idx); } } else { process.ReserveChildSlots(1 - 1); @@ -15454,6 +15559,7 @@ PEGTransformerFactory::FinalizeStructExpressionTrampoline(PEGTransformer &transf auto &struct_field_opt = list_pr.GetChild(1).Cast(); if (struct_field_opt.HasResult()) { vector struct_field_value; + struct_field_value.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { struct_field_value.push_back(process.TakeResult(i)); } @@ -15467,8 +15573,8 @@ void PEGTransformerFactory::InitializeStructFieldTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_pr.GetChild(0)}, 0); } arena_ptr @@ -15483,7 +15589,7 @@ void PEGTransformerFactory::InitializeMapExpressionTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("MapStructExpression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::MapStructExpression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -15505,7 +15611,8 @@ void PEGTransformerFactory::InitializeMapStructExpressionTrampoline(PEGTransform process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("MapStructField"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::MapStructField), list_items[child_idx].get()}, + 0 + child_idx); } } else { process.ReserveChildSlots(1 - 1); @@ -15526,6 +15633,7 @@ PEGTransformerFactory::FinalizeMapStructExpressionTrampoline(PEGTransformer &tra auto &map_struct_field_opt = list_pr.GetChild(1).Cast(); if (map_struct_field_opt.HasResult()) { vector>> map_struct_field_value; + map_struct_field_value.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { map_struct_field_value.push_back(process.TakeResult>>(i)); } @@ -15539,8 +15647,8 @@ void PEGTransformerFactory::InitializeMapStructFieldTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -15563,12 +15671,12 @@ void PEGTransformerFactory::InitializeGroupingExpressionTrampoline(PEGTransforme process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("GroupingOrGroupingId"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::GroupingOrGroupingId), list_pr.GetChild(0)}, 0); } arena_ptr @@ -15586,6 +15694,7 @@ PEGTransformerFactory::FinalizeGroupingExpressionTrampoline(PEGTransformer &tran auto &expression_opt = ExtractResultFromParens(list_pr.GetChild(1)).Cast(); if (expression_opt.HasResult()) { vector> expression_value; + expression_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { expression_value.push_back(process.TakeResult>(i)); } @@ -15704,7 +15813,7 @@ void PEGTransformerFactory::InitializeColLabelParameterTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColLabel"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColLabel), list_pr.GetChild(1)}, 0); } arena_ptr @@ -15749,15 +15858,16 @@ void PEGTransformerFactory::InitializeListComprehensionExpressionTrampoline(PEGT process.ReserveChildSlots(4 + dynamic_child_count - 1); auto &list_comprehension_filter_opt = list_pr.GetChild(6).Cast(); if (list_comprehension_filter_opt.HasResult()) { - process.PushChild({transformer.GetRule("ListComprehensionFilter"), list_comprehension_filter_opt.GetResult()}, - 3 + dynamic_child_count - 1); + process.PushChild( + {transformer.GetRule(PEGRuleId::ListComprehensionFilter), list_comprehension_filter_opt.GetResult()}, + 3 + dynamic_child_count - 1); } - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(5)}, 2 + dynamic_child_count - 1); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(5)}, 2 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColIdOrString"), list_items[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_items[child_idx].get()}, 1 + child_idx); } - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -15768,6 +15878,7 @@ PEGTransformerFactory::FinalizeListComprehensionExpressionTrampoline(PEGTransfor auto dynamic_child_count = dynamic_list_items.size(); auto expression = process.TakeResult>(0); vector col_id_or_string; + col_id_or_string.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { col_id_or_string.push_back(process.TakeResult(i)); } @@ -15785,7 +15896,7 @@ void PEGTransformerFactory::InitializeListComprehensionFilterTrampoline(PEGTrans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -15800,7 +15911,7 @@ void PEGTransformerFactory::InitializeParensExpressionTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr @@ -15815,8 +15926,8 @@ void PEGTransformerFactory::InitializeMeasureSemanticsExpressionTrampoline(PEGTr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("FunctionExpression"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("MeasureSemantics"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::FunctionExpression), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::MeasureSemantics), list_pr.GetChild(0)}, 0); } arena_ptr @@ -15853,7 +15964,7 @@ void PEGTransformerFactory::InitializeExpressionTrampoline(PEGTransformer &trans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("LambdaArrowExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::LambdaArrowExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -15866,7 +15977,7 @@ void PEGTransformerFactory::InitializeColumnDefaultExprTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColDefOrExpr"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColDefOrExpr), list_pr.GetChild(0)}, 0); } arena_ptr @@ -15889,13 +16000,13 @@ void PEGTransformerFactory::InitializeLambdaArrowExpressionTrampoline(PEGTransfo process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("SingleArrowPair"), repeat_children[child_idx].get()}, + process.PushChild({transformer.GetRule(PEGRuleId::SingleArrowPair), repeat_children[child_idx].get()}, 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("LogicalOrExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::LogicalOrExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -15913,6 +16024,7 @@ PEGTransformerFactory::FinalizeLambdaArrowExpressionTrampoline(PEGTransformer &t optional>> single_arrow_pair {}; if (dynamic_child_count > 0) { vector> single_arrow_pair_value; + single_arrow_pair_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { single_arrow_pair_value.push_back(process.TakeResult>(i)); } @@ -15927,7 +16039,7 @@ void PEGTransformerFactory::InitializeSingleArrowPairTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("LogicalOrExpression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::LogicalOrExpression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -15950,13 +16062,14 @@ void PEGTransformerFactory::InitializeLogicalOrExpressionTrampoline(PEGTransform process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("LogicalOrExpressionTail"), repeat_children[child_idx].get()}, - 1 + child_idx); + process.PushChild( + {transformer.GetRule(PEGRuleId::LogicalOrExpressionTail), repeat_children[child_idx].get()}, + 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("LogicalAndExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::LogicalAndExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -15974,6 +16087,7 @@ PEGTransformerFactory::FinalizeLogicalOrExpressionTrampoline(PEGTransformer &tra optional>> logical_or_expression_tail {}; if (dynamic_child_count > 0) { vector> logical_or_expression_tail_value; + logical_or_expression_tail_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { logical_or_expression_tail_value.push_back(process.TakeResult>(i)); } @@ -15988,7 +16102,7 @@ void PEGTransformerFactory::InitializeLogicalOrExpressionTailTrampoline(PEGTrans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("LogicalAndExpression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::LogicalAndExpression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -16011,13 +16125,14 @@ void PEGTransformerFactory::InitializeColDefOrExprTrampoline(PEGTransformer &tra process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColDefOrExpressionTail"), repeat_children[child_idx].get()}, - 1 + child_idx); + process.PushChild( + {transformer.GetRule(PEGRuleId::ColDefOrExpressionTail), repeat_children[child_idx].get()}, + 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("ColDefAndExpr"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColDefAndExpr), list_pr.GetChild(0)}, 0); } arena_ptr @@ -16034,6 +16149,7 @@ PEGTransformerFactory::FinalizeColDefOrExprTrampoline(PEGTransformer &transforme optional>> col_def_or_expression_tail {}; if (dynamic_child_count > 0) { vector> col_def_or_expression_tail_value; + col_def_or_expression_tail_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { col_def_or_expression_tail_value.push_back(process.TakeResult>(i)); } @@ -16048,7 +16164,7 @@ void PEGTransformerFactory::InitializeColDefOrExpressionTailTrampoline(PEGTransf GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColDefAndExpr"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColDefAndExpr), list_pr.GetChild(1)}, 0); } arena_ptr @@ -16071,13 +16187,14 @@ void PEGTransformerFactory::InitializeLogicalAndExpressionTrampoline(PEGTransfor process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("LogicalAndExpressionTail"), repeat_children[child_idx].get()}, - 1 + child_idx); + process.PushChild( + {transformer.GetRule(PEGRuleId::LogicalAndExpressionTail), repeat_children[child_idx].get()}, + 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("LogicalNotExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::LogicalNotExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -16095,6 +16212,7 @@ PEGTransformerFactory::FinalizeLogicalAndExpressionTrampoline(PEGTransformer &tr optional>> logical_and_expression_tail {}; if (dynamic_child_count > 0) { vector> logical_and_expression_tail_value; + logical_and_expression_tail_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { logical_and_expression_tail_value.push_back(process.TakeResult>(i)); } @@ -16109,7 +16227,7 @@ void PEGTransformerFactory::InitializeLogicalAndExpressionTailTrampoline(PEGTran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("LogicalNotExpression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::LogicalNotExpression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -16132,13 +16250,14 @@ void PEGTransformerFactory::InitializeColDefAndExprTrampoline(PEGTransformer &tr process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColDefAndExpressionTail"), repeat_children[child_idx].get()}, - 1 + child_idx); + process.PushChild( + {transformer.GetRule(PEGRuleId::ColDefAndExpressionTail), repeat_children[child_idx].get()}, + 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("IsDistinctFromExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IsDistinctFromExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -16156,6 +16275,7 @@ PEGTransformerFactory::FinalizeColDefAndExprTrampoline(PEGTransformer &transform optional>> col_def_and_expression_tail {}; if (dynamic_child_count > 0) { vector> col_def_and_expression_tail_value; + col_def_and_expression_tail_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { col_def_and_expression_tail_value.push_back(process.TakeResult>(i)); } @@ -16170,7 +16290,7 @@ void PEGTransformerFactory::InitializeColDefAndExpressionTailTrampoline(PEGTrans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("IsDistinctFromExpression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IsDistinctFromExpression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -16185,10 +16305,10 @@ void PEGTransformerFactory::InitializeLogicalNotExpressionTrampoline(PEGTransfor GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("IsExpression"), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::IsExpression), list_pr.GetChild(1)}, 1); auto ¬_expression_opt = list_pr.GetChild(0).Cast(); if (not_expression_opt.HasResult()) { - process.PushChild({transformer.GetRule("NotExpression"), not_expression_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::NotExpression), not_expression_opt.GetResult()}, 0); } } @@ -16213,7 +16333,8 @@ void PEGTransformerFactory::InitializeNotExpressionTrampoline(PEGTransformer &tr process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("NotKeyword"), repeat_children[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::NotKeyword), repeat_children[child_idx].get()}, + 0 + child_idx); } } @@ -16225,6 +16346,7 @@ PEGTransformerFactory::FinalizeNotExpressionTrampoline(PEGTransformer &transform auto dynamic_repeat_children = dynamic_repeat_pr.GetChildren(); auto dynamic_child_count = dynamic_repeat_children.size(); vector not_keyword; + not_keyword.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { not_keyword.push_back(process.TakeResult(i)); } @@ -16249,10 +16371,10 @@ void PEGTransformerFactory::InitializeIsExpressionTrampoline(PEGTransformer &tra process.ReserveChildSlots(2); auto &is_expression_continuation_opt = list_pr.GetChild(1).Cast(); if (is_expression_continuation_opt.HasResult()) { - process.PushChild({transformer.GetRule("IsExpressionContinuation"), is_expression_continuation_opt.GetResult()}, - 1); + process.PushChild( + {transformer.GetRule(PEGRuleId::IsExpressionContinuation), is_expression_continuation_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("IsDistinctFromExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IsDistinctFromExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -16279,13 +16401,13 @@ void PEGTransformerFactory::InitializeIsExpressionContinuationTrampoline(PEGTran process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("IsExpressionTail"), repeat_children[child_idx].get()}, + process.PushChild({transformer.GetRule(PEGRuleId::IsExpressionTail), repeat_children[child_idx].get()}, 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("IsTest"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IsTest), list_pr.GetChild(0)}, 0); } arena_ptr @@ -16303,6 +16425,7 @@ PEGTransformerFactory::FinalizeIsExpressionContinuationTrampoline(PEGTransformer optional> is_expression_tail {}; if (dynamic_child_count > 0) { vector is_expression_tail_value; + is_expression_tail_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { is_expression_tail_value.push_back(process.TakeResult(i)); } @@ -16337,7 +16460,7 @@ void PEGTransformerFactory::InitializeIsTestTailTrampoline(PEGTransformer &trans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("IsTest"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IsTest), list_pr.GetChild(0)}, 0); } arena_ptr @@ -16351,7 +16474,7 @@ void PEGTransformerFactory::InitializeIsDistinctTailTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("IsDistinctFromTail"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IsDistinctFromTail), list_pr.GetChild(0)}, 0); } arena_ptr @@ -16366,7 +16489,7 @@ void PEGTransformerFactory::InitializeIsComparisonTailTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ComparisonExpressionTail"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ComparisonExpressionTail), list_pr.GetChild(0)}, 0); } arena_ptr @@ -16381,7 +16504,7 @@ void PEGTransformerFactory::InitializeIsOtherOperatorTailTrampoline(PEGTransform GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("OtherOperatorTail"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OtherOperatorTail), list_pr.GetChild(0)}, 0); } arena_ptr @@ -16396,7 +16519,7 @@ void PEGTransformerFactory::InitializeIsCastTailTrampoline(PEGTransformer &trans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("CastOperator"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CastOperator), list_pr.GetChild(0)}, 0); } arena_ptr @@ -16430,7 +16553,7 @@ void PEGTransformerFactory::InitializeIsLiteralTrampoline(PEGTransformer &transf GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("IsLiteralValue"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IsLiteralValue), list_pr.GetChild(2)}, 0); } arena_ptr PEGTransformerFactory::FinalizeIsLiteralTrampoline(PEGTransformer &transformer, @@ -16525,7 +16648,7 @@ void PEGTransformerFactory::InitializeIsNullTrampoline(PEGTransformer &transform GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("IsNullOperator"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IsNullOperator), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeIsNullTrampoline(PEGTransformer &transformer, @@ -16559,13 +16682,13 @@ void PEGTransformerFactory::InitializeIsDistinctFromExpressionTrampoline(PEGTran process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("IsDistinctFromTail"), repeat_children[child_idx].get()}, + process.PushChild({transformer.GetRule(PEGRuleId::IsDistinctFromTail), repeat_children[child_idx].get()}, 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("ComparisonExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ComparisonExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -16583,6 +16706,7 @@ PEGTransformerFactory::FinalizeIsDistinctFromExpressionTrampoline(PEGTransformer optional> is_distinct_from_tail {}; if (dynamic_child_count > 0) { vector is_distinct_from_tail_value; + is_distinct_from_tail_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { is_distinct_from_tail_value.push_back(process.TakeResult(i)); } @@ -16597,8 +16721,8 @@ void PEGTransformerFactory::InitializeIsDistinctFromTailTrampoline(PEGTransforme GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("ComparisonExpression"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("IsDistinctFromOp"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ComparisonExpression), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::IsDistinctFromOp), list_pr.GetChild(0)}, 0); } arena_ptr @@ -16638,13 +16762,14 @@ void PEGTransformerFactory::InitializeComparisonExpressionTrampoline(PEGTransfor process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ComparisonExpressionTail"), repeat_children[child_idx].get()}, - 1 + child_idx); + process.PushChild( + {transformer.GetRule(PEGRuleId::ComparisonExpressionTail), repeat_children[child_idx].get()}, + 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("BetweenInLikeExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BetweenInLikeExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -16662,6 +16787,7 @@ PEGTransformerFactory::FinalizeComparisonExpressionTrampoline(PEGTransformer &tr optional> comparison_expression_tail {}; if (dynamic_child_count > 0) { vector comparison_expression_tail_value; + comparison_expression_tail_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { comparison_expression_tail_value.push_back(process.TakeResult(i)); } @@ -16676,8 +16802,8 @@ void PEGTransformerFactory::InitializeComparisonExpressionTailTrampoline(PEGTran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("ComparisonOperand"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("ComparisonOperator"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ComparisonOperand), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ComparisonOperator), list_pr.GetChild(0)}, 0); } arena_ptr @@ -16714,8 +16840,8 @@ void PEGTransformerFactory::InitializeNotComparisonOperandTrampoline(PEGTransfor GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("IsExpression"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("NotExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IsExpression), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::NotExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -16832,22 +16958,23 @@ void PEGTransformerFactory::InitializeBetweenInLikeExpressionTrampoline(PEGTrans process.ReserveChildSlots(3 + dynamic_child_count - 1); auto &between_like_op_opt = list_pr.GetChild(2).Cast(); if (between_like_op_opt.HasResult()) { - process.PushChild({transformer.GetRule("BetweenLikeOp"), between_like_op_opt.GetResult()}, + process.PushChild({transformer.GetRule(PEGRuleId::BetweenLikeOp), between_like_op_opt.GetResult()}, 2 + dynamic_child_count - 1); } for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("InPredicate"), repeat_children[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::InPredicate), repeat_children[child_idx].get()}, + 1 + child_idx); } } else { process.ReserveChildSlots(3 - 1); auto &between_like_op_opt = list_pr.GetChild(2).Cast(); if (between_like_op_opt.HasResult()) { - process.PushChild({transformer.GetRule("BetweenLikeOp"), between_like_op_opt.GetResult()}, + process.PushChild({transformer.GetRule(PEGRuleId::BetweenLikeOp), between_like_op_opt.GetResult()}, 2 + dynamic_child_count - 1); } } - process.PushChild({transformer.GetRule("OtherOperatorExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OtherOperatorExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -16865,6 +16992,7 @@ PEGTransformerFactory::FinalizeBetweenInLikeExpressionTrampoline(PEGTransformer optional> in_predicate {}; if (dynamic_child_count > 0) { vector in_predicate_value; + in_predicate_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { in_predicate_value.push_back(process.TakeResult(i)); } @@ -16891,12 +17019,13 @@ void PEGTransformerFactory::InitializeInPredicateTrampoline(PEGTransformer &tran process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("CastOperator"), repeat_children[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::CastOperator), repeat_children[child_idx].get()}, + 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("InClause"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::InClause), list_pr.GetChild(1)}, 0); } arena_ptr @@ -16916,6 +17045,7 @@ PEGTransformerFactory::FinalizeInPredicateTrampoline(PEGTransformer &transformer optional>> cast_operator {}; if (dynamic_child_count > 0) { vector> cast_operator_value; + cast_operator_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { cast_operator_value.push_back(process.TakeResult>(i)); } @@ -16929,7 +17059,7 @@ void PEGTransformerFactory::InitializeBetweenLikeOpTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("BetweenLikeOpExpression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BetweenLikeOpExpression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -16971,10 +17101,10 @@ void PEGTransformerFactory::InitializeLikeClauseTrampoline(PEGTransformer &trans process.ReserveChildSlots(3); auto &escape_clause_opt = list_pr.GetChild(2).Cast(); if (escape_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("EscapeClause"), escape_clause_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::EscapeClause), escape_clause_opt.GetResult()}, 2); } - process.PushChild({transformer.GetRule("OtherOperatorExpression"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("LikeVariations"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OtherOperatorExpression), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::LikeVariations), list_pr.GetChild(0)}, 0); } arena_ptr @@ -16994,7 +17124,7 @@ void PEGTransformerFactory::InitializeEscapeClauseTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("OtherOperatorExpression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OtherOperatorExpression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -17165,7 +17295,7 @@ void PEGTransformerFactory::InitializeInClauseTrampoline(PEGTransformer &transfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("InExpression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::InExpression), list_pr.GetChild(1)}, 0); } arena_ptr PEGTransformerFactory::FinalizeInClauseTrampoline(PEGTransformer &transformer, @@ -17199,7 +17329,7 @@ void PEGTransformerFactory::InitializeInContainsExpressionTrampoline(PEGTransfor GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("OtherOperatorExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OtherOperatorExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -17218,7 +17348,7 @@ void PEGTransformerFactory::InitializeInExpressionListTrampoline(PEGTransformer process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 0 + child_idx); } } @@ -17229,6 +17359,7 @@ PEGTransformerFactory::FinalizeInExpressionListTrampoline(PEGTransformer &transf auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); vector> expression; + expression.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { expression.push_back(process.TakeResult>(i)); } @@ -17240,8 +17371,8 @@ void PEGTransformerFactory::InitializeInSelectStatementTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SelectStatementInternal"), ExtractResultFromParens(list_pr.GetChild(0))}, - 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::SelectStatementInternal), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr @@ -17256,8 +17387,8 @@ void PEGTransformerFactory::InitializeBetweenClauseTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("OtherOperatorExpression"), list_pr.GetChild(4)}, 1); - process.PushChild({transformer.GetRule("OtherOperatorExpression"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OtherOperatorExpression), list_pr.GetChild(4)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::OtherOperatorExpression), list_pr.GetChild(2)}, 0); } arena_ptr @@ -17307,13 +17438,13 @@ void PEGTransformerFactory::InitializeInfixOtherOperatorExpressionTrampoline(PEG process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("OtherOperatorTail"), repeat_children[child_idx].get()}, + process.PushChild({transformer.GetRule(PEGRuleId::OtherOperatorTail), repeat_children[child_idx].get()}, 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("BitwiseExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BitwiseExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -17331,6 +17462,7 @@ PEGTransformerFactory::FinalizeInfixOtherOperatorExpressionTrampoline(PEGTransfo optional> other_operator_tail {}; if (dynamic_child_count > 0) { vector other_operator_tail_value; + other_operator_tail_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { other_operator_tail_value.push_back(process.TakeResult(i)); } @@ -17345,7 +17477,7 @@ void PEGTransformerFactory::InitializeCustomPrefixExpressionTrampoline(PEGTransf GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("OtherOperatorExpression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OtherOperatorExpression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -17362,8 +17494,8 @@ void PEGTransformerFactory::InitializeOtherOperatorTailTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("BitwiseExpression"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("OtherOperator"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BitwiseExpression), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::OtherOperator), list_pr.GetChild(0)}, 0); } arena_ptr @@ -17400,7 +17532,7 @@ void PEGTransformerFactory::InitializeAnyAllParsedOperatorTrampoline(PEGTransfor GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("AnyAllOperator"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::AnyAllOperator), list_pr.GetChild(0)}, 0); } arena_ptr @@ -17484,7 +17616,7 @@ void PEGTransformerFactory::InitializeAnyAllOperatorTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("AnyOrAll"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::AnyOrAll), list_pr.GetChild(1)}, 0); } arena_ptr @@ -17543,8 +17675,8 @@ void PEGTransformerFactory::InitializeQualifiedOperatorTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("QualifiedOperatorContents"), ExtractResultFromParens(list_pr.GetChild(1))}, - 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::QualifiedOperatorContents), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } arena_ptr @@ -17567,7 +17699,8 @@ void PEGTransformerFactory::InitializeQualifiedOperatorContentsTrampoline(PEGTra process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColIdDot"), repeat_children[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdDot), repeat_children[child_idx].get()}, + 0 + child_idx); } } else { process.ReserveChildSlots(1 - 1); @@ -17588,6 +17721,7 @@ PEGTransformerFactory::FinalizeQualifiedOperatorContentsTrampoline(PEGTransforme optional> col_id_dot {}; if (dynamic_child_count > 0) { vector col_id_dot_value; + col_id_dot_value.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { col_id_dot_value.push_back(process.TakeResult(i)); } @@ -17610,13 +17744,13 @@ void PEGTransformerFactory::InitializeBitwiseExpressionTrampoline(PEGTransformer process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("BitwiseExpressionTail"), repeat_children[child_idx].get()}, + process.PushChild({transformer.GetRule(PEGRuleId::BitwiseExpressionTail), repeat_children[child_idx].get()}, 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("TildeExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TildeExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -17634,6 +17768,7 @@ PEGTransformerFactory::FinalizeBitwiseExpressionTrampoline(PEGTransformer &trans optional> bitwise_expression_tail {}; if (dynamic_child_count > 0) { vector bitwise_expression_tail_value; + bitwise_expression_tail_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { bitwise_expression_tail_value.push_back(process.TakeResult(i)); } @@ -17648,8 +17783,8 @@ void PEGTransformerFactory::InitializeBitwiseExpressionTailTrampoline(PEGTransfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("TildeExpression"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("BitOperator"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TildeExpression), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::BitOperator), list_pr.GetChild(0)}, 0); } arena_ptr @@ -17684,16 +17819,16 @@ void PEGTransformerFactory::InitializeTildeExpressionTrampoline(PEGTransformer & auto repeat_children = repeat_pr.GetChildren(); dynamic_child_count = repeat_children.size(); process.ReserveChildSlots(2 + dynamic_child_count - 1); - process.PushChild({transformer.GetRule("AdditiveExpression"), list_pr.GetChild(1)}, + process.PushChild({transformer.GetRule(PEGRuleId::AdditiveExpression), list_pr.GetChild(1)}, 1 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("TildePrefixOperator"), repeat_children[child_idx].get()}, + process.PushChild({transformer.GetRule(PEGRuleId::TildePrefixOperator), repeat_children[child_idx].get()}, 0 + child_idx); } } else { process.ReserveChildSlots(2 - 1); - process.PushChild({transformer.GetRule("AdditiveExpression"), list_pr.GetChild(1)}, + process.PushChild({transformer.GetRule(PEGRuleId::AdditiveExpression), list_pr.GetChild(1)}, 1 + dynamic_child_count - 1); } } @@ -17712,6 +17847,7 @@ PEGTransformerFactory::FinalizeTildeExpressionTrampoline(PEGTransformer &transfo optional> tilde_prefix_operator {}; if (dynamic_child_count > 0) { vector tilde_prefix_operator_value; + tilde_prefix_operator_value.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { tilde_prefix_operator_value.push_back(process.TakeResult(i)); } @@ -17734,13 +17870,14 @@ void PEGTransformerFactory::InitializeAdditiveExpressionTrampoline(PEGTransforme process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("AdditiveExpressionTail"), repeat_children[child_idx].get()}, - 1 + child_idx); + process.PushChild( + {transformer.GetRule(PEGRuleId::AdditiveExpressionTail), repeat_children[child_idx].get()}, + 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("MultiplicativeExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::MultiplicativeExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -17758,6 +17895,7 @@ PEGTransformerFactory::FinalizeAdditiveExpressionTrampoline(PEGTransformer &tran optional> additive_expression_tail {}; if (dynamic_child_count > 0) { vector additive_expression_tail_value; + additive_expression_tail_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { additive_expression_tail_value.push_back(process.TakeResult(i)); } @@ -17772,8 +17910,8 @@ void PEGTransformerFactory::InitializeAdditiveExpressionTailTrampoline(PEGTransf GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("MultiplicativeExpression"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("Term"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::MultiplicativeExpression), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Term), list_pr.GetChild(0)}, 0); } arena_ptr @@ -17782,7 +17920,7 @@ PEGTransformerFactory::FinalizeAdditiveExpressionTailTrampoline(PEGTransformer & auto term = process.TakeResult(0); auto multiplicative_expression = process.TakeResult>(1); auto result = TransformAdditiveExpressionTail(transformer, term, std::move(multiplicative_expression), - process.parse_result.offset); + process.parse_result.GetOffset()); return transformer.MakeResult(std::move(result)); } @@ -17810,13 +17948,14 @@ void PEGTransformerFactory::InitializeMultiplicativeExpressionTrampoline(PEGTran process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("MultiplicativeExpressionTail"), repeat_children[child_idx].get()}, - 1 + child_idx); + process.PushChild( + {transformer.GetRule(PEGRuleId::MultiplicativeExpressionTail), repeat_children[child_idx].get()}, + 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("ExponentiationExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ExponentiationExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -17834,6 +17973,7 @@ PEGTransformerFactory::FinalizeMultiplicativeExpressionTrampoline(PEGTransformer optional> multiplicative_expression_tail {}; if (dynamic_child_count > 0) { vector multiplicative_expression_tail_value; + multiplicative_expression_tail_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { multiplicative_expression_tail_value.push_back(process.TakeResult(i)); } @@ -17848,8 +17988,8 @@ void PEGTransformerFactory::InitializeMultiplicativeExpressionTailTrampoline(PEG GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("ExponentiationExpression"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("Factor"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ExponentiationExpression), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Factor), list_pr.GetChild(0)}, 0); } arena_ptr @@ -17886,13 +18026,14 @@ void PEGTransformerFactory::InitializeExponentiationExpressionTrampoline(PEGTran process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ExponentiationExpressionTail"), repeat_children[child_idx].get()}, - 1 + child_idx); + process.PushChild( + {transformer.GetRule(PEGRuleId::ExponentiationExpressionTail), repeat_children[child_idx].get()}, + 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("CollateExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CollateExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -17910,6 +18051,7 @@ PEGTransformerFactory::FinalizeExponentiationExpressionTrampoline(PEGTransformer optional> exponentiation_expression_tail {}; if (dynamic_child_count > 0) { vector exponentiation_expression_tail_value; + exponentiation_expression_tail_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { exponentiation_expression_tail_value.push_back(process.TakeResult(i)); } @@ -17924,8 +18066,8 @@ void PEGTransformerFactory::InitializeExponentiationExpressionTailTrampoline(PEG GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("CollateExpression"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("ExponentOperator"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CollateExpression), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ExponentOperator), list_pr.GetChild(0)}, 0); } arena_ptr @@ -17963,13 +18105,13 @@ void PEGTransformerFactory::InitializeCollateExpressionTrampoline(PEGTransformer process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("CollateExpressionTail"), repeat_children[child_idx].get()}, + process.PushChild({transformer.GetRule(PEGRuleId::CollateExpressionTail), repeat_children[child_idx].get()}, 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("AtTimeZoneExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::AtTimeZoneExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -17987,6 +18129,7 @@ PEGTransformerFactory::FinalizeCollateExpressionTrampoline(PEGTransformer &trans optional>> collate_expression_tail {}; if (dynamic_child_count > 0) { vector> collate_expression_tail_value; + collate_expression_tail_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { collate_expression_tail_value.push_back(process.TakeResult>(i)); } @@ -18001,7 +18144,7 @@ void PEGTransformerFactory::InitializeCollateExpressionTailTrampoline(PEGTransfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("AtTimeZoneExpression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::AtTimeZoneExpression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -18024,13 +18167,14 @@ void PEGTransformerFactory::InitializeAtTimeZoneExpressionTrampoline(PEGTransfor process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("AtTimeZoneExpressionTail"), repeat_children[child_idx].get()}, - 1 + child_idx); + process.PushChild( + {transformer.GetRule(PEGRuleId::AtTimeZoneExpressionTail), repeat_children[child_idx].get()}, + 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("PrefixExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::PrefixExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -18048,6 +18192,7 @@ PEGTransformerFactory::FinalizeAtTimeZoneExpressionTrampoline(PEGTransformer &tr optional>> at_time_zone_expression_tail {}; if (dynamic_child_count > 0) { vector> at_time_zone_expression_tail_value; + at_time_zone_expression_tail_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { at_time_zone_expression_tail_value.push_back(process.TakeResult>(i)); } @@ -18062,7 +18207,7 @@ void PEGTransformerFactory::InitializeAtTimeZoneExpressionTailTrampoline(PEGTran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("PrefixExpression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::PrefixExpression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -18157,9 +18302,9 @@ void PEGTransformerFactory::InitializeBaseExpressionTrampoline(PEGTransformer &t process.ReserveChildSlots(2); auto &indirection_list_opt = list_pr.GetChild(1).Cast(); if (indirection_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("IndirectionList"), indirection_list_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::IndirectionList), indirection_list_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("SingleExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SingleExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -18183,7 +18328,8 @@ void PEGTransformerFactory::InitializeIndirectionListTrampoline(PEGTransformer & process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Indirection"), repeat_children[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Indirection), repeat_children[child_idx].get()}, + 0 + child_idx); } } @@ -18195,6 +18341,7 @@ PEGTransformerFactory::FinalizeIndirectionListTrampoline(PEGTransformer &transfo auto dynamic_repeat_children = dynamic_repeat_pr.GetChildren(); auto dynamic_child_count = dynamic_repeat_children.size(); vector> indirection; + indirection.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { indirection.push_back(process.TakeResult>(i)); } @@ -18226,7 +18373,7 @@ void PEGTransformerFactory::InitializeCastOperatorTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Type"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Type), list_pr.GetChild(1)}, 0); } arena_ptr @@ -18260,7 +18407,7 @@ void PEGTransformerFactory::InitializeDotMethodOperatorTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("MethodExpression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::MethodExpression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -18275,7 +18422,7 @@ void PEGTransformerFactory::InitializeDotColumnOperatorTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColLabel"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColLabel), list_pr.GetChild(1)}, 0); } arena_ptr @@ -18290,8 +18437,8 @@ void PEGTransformerFactory::InitializeMethodExpressionTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("MethodExpressionArguments"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("ColLabel"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::MethodExpressionArguments), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ColLabel), list_pr.GetChild(0)}, 0); } arena_ptr @@ -18308,7 +18455,8 @@ void PEGTransformerFactory::InitializeMethodExpressionArgumentsTrampoline(PEGTra auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); process.PushChild( - {transformer.GetRule("MethodExpressionArgumentList"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); + {transformer.GetRule(PEGRuleId::MethodExpressionArgumentList), ExtractResultFromParens(list_pr.GetChild(0))}, + 0); } arena_ptr @@ -18325,20 +18473,21 @@ void PEGTransformerFactory::InitializeMethodExpressionArgumentListTrampoline(PEG process.ReserveChildSlots(4); auto &ignore_or_respect_nulls_opt = list_pr.GetChild(3).Cast(); if (ignore_or_respect_nulls_opt.HasResult()) { - process.PushChild({transformer.GetRule("IgnoreOrRespectNulls"), ignore_or_respect_nulls_opt.GetResult()}, 3); + process.PushChild( + {transformer.GetRule(PEGRuleId::IgnoreOrRespectNulls), ignore_or_respect_nulls_opt.GetResult()}, 3); } auto &order_by_clause_opt = list_pr.GetChild(2).Cast(); if (order_by_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("OrderByClause"), order_by_clause_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::OrderByClause), order_by_clause_opt.GetResult()}, 2); } auto &method_function_arguments_opt = list_pr.GetChild(1).Cast(); if (method_function_arguments_opt.HasResult()) { - process.PushChild({transformer.GetRule("MethodFunctionArguments"), method_function_arguments_opt.GetResult()}, - 1); + process.PushChild( + {transformer.GetRule(PEGRuleId::MethodFunctionArguments), method_function_arguments_opt.GetResult()}, 1); } auto &distinct_or_all_opt = list_pr.GetChild(0).Cast(); if (distinct_or_all_opt.HasResult()) { - process.PushChild({transformer.GetRule("DistinctOrAll"), distinct_or_all_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::DistinctOrAll), distinct_or_all_opt.GetResult()}, 0); } } @@ -18375,7 +18524,8 @@ void PEGTransformerFactory::InitializeMethodFunctionArgumentsTrampoline(PEGTrans process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("FunctionArgument"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::FunctionArgument), list_items[child_idx].get()}, + 0 + child_idx); } } @@ -18386,6 +18536,7 @@ PEGTransformerFactory::FinalizeMethodFunctionArgumentsTrampoline(PEGTransformer auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); auto dynamic_child_count = dynamic_list_items.size(); vector function_argument; + function_argument.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { function_argument.push_back(process.TakeResult(i)); } @@ -18397,7 +18548,7 @@ void PEGTransformerFactory::InitializeSliceExpressionTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SliceBound"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SliceBound), list_pr.GetChild(1)}, 0); } arena_ptr @@ -18414,11 +18565,11 @@ void PEGTransformerFactory::InitializeSliceBoundTrampoline(PEGTransformer &trans process.ReserveChildSlots(2); auto &slice_upper_bound_opt = list_pr.GetChild(1).Cast(); if (slice_upper_bound_opt.HasResult()) { - process.PushChild({transformer.GetRule("SliceUpperBound"), slice_upper_bound_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::SliceUpperBound), slice_upper_bound_opt.GetResult()}, 1); } auto &expression_opt = list_pr.GetChild(0).Cast(); if (expression_opt.HasResult()) { - process.PushChild({transformer.GetRule("Expression"), expression_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), expression_opt.GetResult()}, 0); } } @@ -18461,7 +18612,7 @@ void PEGTransformerFactory::InitializeMinusStepSliceBoundTrampoline(PEGTransform GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("StepSliceBound"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::StepSliceBound), list_pr.GetChild(2)}, 0); } arena_ptr @@ -18478,9 +18629,9 @@ void PEGTransformerFactory::InitializeEndStepSliceBoundTrampoline(PEGTransformer process.ReserveChildSlots(2); auto &step_slice_bound_opt = list_pr.GetChild(1).Cast(); if (step_slice_bound_opt.HasResult()) { - process.PushChild({transformer.GetRule("StepSliceBound"), step_slice_bound_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::StepSliceBound), step_slice_bound_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("EndSliceBound"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::EndSliceBound), list_pr.GetChild(0)}, 0); } arena_ptr @@ -18501,7 +18652,7 @@ void PEGTransformerFactory::InitializeEndSliceBoundTrampoline(PEGTransformer &tr process.ReserveChildSlots(1); auto &expression_opt = list_pr.GetChild(1).Cast(); if (expression_opt.HasResult()) { - process.PushChild({transformer.GetRule("Expression"), expression_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), expression_opt.GetResult()}, 0); } } @@ -18522,7 +18673,7 @@ void PEGTransformerFactory::InitializeStepSliceBoundTrampoline(PEGTransformer &t process.ReserveChildSlots(1); auto &expression_opt = list_pr.GetChild(1).Cast(); if (expression_opt.HasResult()) { - process.PushChild({transformer.GetRule("Expression"), expression_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), expression_opt.GetResult()}, 0); } } @@ -18578,7 +18729,7 @@ void PEGTransformerFactory::InitializeCoalesceExpressionTrampoline(PEGTransforme process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 0 + child_idx); } } @@ -18589,6 +18740,7 @@ PEGTransformerFactory::FinalizeCoalesceExpressionTrampoline(PEGTransformer &tran auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(1))); auto dynamic_child_count = dynamic_list_items.size(); vector> expression; + expression.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { expression.push_back(process.TakeResult>(i)); } @@ -18600,7 +18752,7 @@ void PEGTransformerFactory::InitializeUnpackExpressionTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } arena_ptr @@ -18615,7 +18767,7 @@ void PEGTransformerFactory::InitializeTryExpressionTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } arena_ptr @@ -18630,7 +18782,7 @@ void PEGTransformerFactory::InitializeColumnsExpressionTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), ExtractResultFromParens(list_pr.GetChild(2))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), ExtractResultFromParens(list_pr.GetChild(2))}, 0); } arena_ptr @@ -18649,7 +18801,8 @@ void PEGTransformerFactory::InitializeExtractExpressionTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ExtractArguments"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ExtractArguments), ExtractResultFromParens(list_pr.GetChild(1))}, + 0); } arena_ptr @@ -18664,8 +18817,8 @@ void PEGTransformerFactory::InitializeExtractArgumentsTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("ExtractArgument"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ExtractArgument), list_pr.GetChild(0)}, 0); } arena_ptr @@ -18683,10 +18836,10 @@ void PEGTransformerFactory::InitializeLambdaExpressionTrampoline(PEGTransformer auto list_items = ExtractParseResultsFromList(list_pr.GetChild(1)); auto dynamic_child_count = list_items.size(); process.ReserveChildSlots(2 + dynamic_child_count - 1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(3)}, 1 + dynamic_child_count - 1); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(3)}, 1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColIdOrString"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_items[child_idx].get()}, 0 + child_idx); } } @@ -18697,6 +18850,7 @@ PEGTransformerFactory::FinalizeLambdaExpressionTrampoline(PEGTransformer &transf auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(1)); auto dynamic_child_count = dynamic_list_items.size(); vector col_id_or_string; + col_id_or_string.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { col_id_or_string.push_back(process.TakeResult(i)); } @@ -18709,7 +18863,8 @@ void PEGTransformerFactory::InitializeNullIfExpressionTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("NullIfArguments"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::NullIfArguments), ExtractResultFromParens(list_pr.GetChild(1))}, + 0); } arena_ptr @@ -18724,8 +18879,8 @@ void PEGTransformerFactory::InitializeNullIfArgumentsTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -18741,7 +18896,8 @@ void PEGTransformerFactory::InitializePositionExpressionTrampoline(PEGTransforme GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("PositionArguments"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::PositionArguments), ExtractResultFromParens(list_pr.GetChild(1))}, + 0); } arena_ptr @@ -18756,8 +18912,8 @@ void PEGTransformerFactory::InitializePositionArgumentsTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("OtherOperatorExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::OtherOperatorExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -18780,7 +18936,7 @@ void PEGTransformerFactory::InitializeRowExpressionTrampoline(PEGTransformer &tr process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 0 + child_idx); } } else { process.ReserveChildSlots(1 - 1); @@ -18801,6 +18957,7 @@ PEGTransformerFactory::FinalizeRowExpressionTrampoline(PEGTransformer &transform auto &expression_opt = ExtractResultFromParens(list_pr.GetChild(1)).Cast(); if (expression_opt.HasResult()) { vector> expression_value; + expression_value.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { expression_value.push_back(process.TakeResult>(i)); } @@ -18814,7 +18971,8 @@ void PEGTransformerFactory::InitializeSubstringExpressionTrampoline(PEGTransform GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SubstringArguments"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::SubstringArguments), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } arena_ptr @@ -18854,7 +19012,7 @@ void PEGTransformerFactory::InitializeSubstringExpressionListTrampoline(PEGTrans process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 0 + child_idx); } } @@ -18865,6 +19023,7 @@ PEGTransformerFactory::FinalizeSubstringExpressionListTrampoline(PEGTransformer auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); auto dynamic_child_count = dynamic_list_items.size(); vector> expression; + expression.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { expression.push_back(process.TakeResult>(i)); } @@ -18876,8 +19035,8 @@ void PEGTransformerFactory::InitializeSubstringParametersTrampoline(PEGTransform GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("SubstringFromFor"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SubstringFromFor), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -18916,9 +19075,9 @@ void PEGTransformerFactory::InitializeSubstringFromOptionalForTrampoline(PEGTran process.ReserveChildSlots(2); auto &for_expression_opt = list_pr.GetChild(1).Cast(); if (for_expression_opt.HasResult()) { - process.PushChild({transformer.GetRule("ForExpression"), for_expression_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ForExpression), for_expression_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("FromExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::FromExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -18937,7 +19096,7 @@ void PEGTransformerFactory::InitializeSubstringForTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ForExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ForExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -18951,7 +19110,7 @@ void PEGTransformerFactory::InitializeTrimExpressionTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("TrimArguments"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TrimArguments), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } arena_ptr @@ -18970,15 +19129,15 @@ void PEGTransformerFactory::InitializeTrimArgumentsTrampoline(PEGTransformer &tr process.ReserveChildSlots(3 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 2 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 2 + child_idx); } auto &trim_source_opt = list_pr.GetChild(1).Cast(); if (trim_source_opt.HasResult()) { - process.PushChild({transformer.GetRule("TrimSource"), trim_source_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::TrimSource), trim_source_opt.GetResult()}, 1); } auto &trim_direction_opt = list_pr.GetChild(0).Cast(); if (trim_direction_opt.HasResult()) { - process.PushChild({transformer.GetRule("TrimDirection"), trim_direction_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TrimDirection), trim_direction_opt.GetResult()}, 0); } } @@ -18997,6 +19156,7 @@ PEGTransformerFactory::FinalizeTrimArgumentsTrampoline(PEGTransformer &transform trim_source = process.TakeResult>(1); } vector> expression; + expression.reserve(dynamic_child_count); for (idx_t i = 2; i < 2 + dynamic_child_count; i++) { expression.push_back(process.TakeResult>(i)); } @@ -19085,7 +19245,7 @@ void PEGTransformerFactory::InitializeTrimSourceTrampoline(PEGTransformer &trans process.ReserveChildSlots(1); auto &expression_opt = list_pr.GetChild(0).Cast(); if (expression_opt.HasResult()) { - process.PushChild({transformer.GetRule("Expression"), expression_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), expression_opt.GetResult()}, 0); } } @@ -19103,7 +19263,8 @@ void PEGTransformerFactory::InitializeOverlayExpressionTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("OverlayArguments"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OverlayArguments), ExtractResultFromParens(list_pr.GetChild(1))}, + 0); } arena_ptr @@ -19141,11 +19302,11 @@ void PEGTransformerFactory::InitializeOverlayParametersTrampoline(PEGTransformer process.ReserveChildSlots(4); auto &for_expression_opt = list_pr.GetChild(4).Cast(); if (for_expression_opt.HasResult()) { - process.PushChild({transformer.GetRule("ForExpression"), for_expression_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::ForExpression), for_expression_opt.GetResult()}, 3); } - process.PushChild({transformer.GetRule("FromExpression"), list_pr.GetChild(3)}, 2); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::FromExpression), list_pr.GetChild(3)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -19167,7 +19328,7 @@ void PEGTransformerFactory::InitializeFromExpressionTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -19182,7 +19343,7 @@ void PEGTransformerFactory::InitializeForExpressionTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -19201,7 +19362,7 @@ void PEGTransformerFactory::InitializeOverlayExpressionListTrampoline(PEGTransfo process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 0 + child_idx); } } @@ -19212,6 +19373,7 @@ PEGTransformerFactory::FinalizeOverlayExpressionListTrampoline(PEGTransformer &t auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); auto dynamic_child_count = dynamic_list_items.size(); vector> expression; + expression.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { expression.push_back(process.TakeResult>(i)); } @@ -19244,7 +19406,7 @@ void PEGTransformerFactory::InitializeExtractDatePartArgumentTrampoline(PEGTrans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ExtractDatePart"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ExtractDatePart), list_pr.GetChild(0)}, 0); } arena_ptr @@ -19331,13 +19493,13 @@ void PEGTransformerFactory::InitializeCreateExternalResourceStmtTrampoline(PEGTr process.ReserveChildSlots(2); auto &external_resource_creation_options_opt = list_pr.GetChild(4).Cast(); if (external_resource_creation_options_opt.HasResult()) { - process.PushChild({transformer.GetRule("ExternalResourceCreationOptions"), + process.PushChild({transformer.GetRule(PEGRuleId::ExternalResourceCreationOptions), external_resource_creation_options_opt.GetResult()}, 1); } auto &attach_alias_opt = list_pr.GetChild(3).Cast(); if (attach_alias_opt.HasResult()) { - process.PushChild({transformer.GetRule("AttachAlias"), attach_alias_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::AttachAlias), attach_alias_opt.GetResult()}, 0); } } @@ -19363,10 +19525,10 @@ void PEGTransformerFactory::InitializeRegisterExternalResourceStmtTrampoline(PEG GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(5)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(5)}, 1); auto &attach_alias_opt = list_pr.GetChild(3).Cast(); if (attach_alias_opt.HasResult()) { - process.PushChild({transformer.GetRule("AttachAlias"), attach_alias_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::AttachAlias), attach_alias_opt.GetResult()}, 0); } } @@ -19389,7 +19551,7 @@ void PEGTransformerFactory::InitializeDestroyExternalResourceStmtTrampoline(PEGT GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(2)}, 0); } arena_ptr @@ -19406,7 +19568,7 @@ void PEGTransformerFactory::InitializeShowExternalResourcesStmtTrampoline(PEGTra process.ReserveChildSlots(1); auto &show_all_modifier_opt = list_pr.GetChild(1).Cast(); if (show_all_modifier_opt.HasResult()) { - process.PushChild({transformer.GetRule("ShowAllModifier"), show_all_modifier_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ShowAllModifier), show_all_modifier_opt.GetResult()}, 0); } } @@ -19437,7 +19599,7 @@ void PEGTransformerFactory::InitializeExternalResourceCreationOptionsTrampoline( GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("GenericCopyOptionList"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::GenericCopyOptionList), list_pr.GetChild(0)}, 0); } arena_ptr @@ -19453,10 +19615,10 @@ void PEGTransformerFactory::InitializeAttachToExternalResourceTrampoline(PEGTran process.ReserveChildSlots(3); auto &attach_options_opt = list_pr.GetChild(4).Cast(); if (attach_options_opt.HasResult()) { - process.PushChild({transformer.GetRule("AttachOptions"), attach_options_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::AttachOptions), attach_options_opt.GetResult()}, 2); } - process.PushChild({transformer.GetRule("AttachAlias"), list_pr.GetChild(3)}, 1); - process.PushChild({transformer.GetRule("ExternalResourceSource"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::AttachAlias), list_pr.GetChild(3)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ExternalResourceSource), list_pr.GetChild(2)}, 0); } arena_ptr @@ -19479,9 +19641,9 @@ void PEGTransformerFactory::InitializeConnectToExternalResourceTrampoline(PEGTra process.ReserveChildSlots(2); auto &attach_options_opt = list_pr.GetChild(3).Cast(); if (attach_options_opt.HasResult()) { - process.PushChild({transformer.GetRule("AttachOptions"), attach_options_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::AttachOptions), attach_options_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("ExternalResourceSource"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ExternalResourceSource), list_pr.GetChild(2)}, 0); } arena_ptr @@ -19523,7 +19685,7 @@ void PEGTransformerFactory::InitializeExternalResourceCreateClauseTrampoline(PEG process.ReserveChildSlots(1); auto &external_resource_creation_options_opt = list_pr.GetChild(4).Cast(); if (external_resource_creation_options_opt.HasResult()) { - process.PushChild({transformer.GetRule("ExternalResourceCreationOptions"), + process.PushChild({transformer.GetRule(PEGRuleId::ExternalResourceCreationOptions), external_resource_creation_options_opt.GetResult()}, 0); } @@ -19547,7 +19709,7 @@ void PEGTransformerFactory::InitializeExternalResourceReferenceClauseTrampoline( GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(1)}, 0); } arena_ptr @@ -19564,29 +19726,29 @@ void PEGTransformerFactory::InitializeInsertStatementTrampoline(PEGTransformer & process.ReserveChildSlots(8); auto &returning_clause_opt = list_pr.GetChild(10).Cast(); if (returning_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("ReturningClause"), returning_clause_opt.GetResult()}, 7); + process.PushChild({transformer.GetRule(PEGRuleId::ReturningClause), returning_clause_opt.GetResult()}, 7); } auto &on_conflict_clause_opt = list_pr.GetChild(9).Cast(); if (on_conflict_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("OnConflictClause"), on_conflict_clause_opt.GetResult()}, 6); + process.PushChild({transformer.GetRule(PEGRuleId::OnConflictClause), on_conflict_clause_opt.GetResult()}, 6); } - process.PushChild({transformer.GetRule("InsertValues"), list_pr.GetChild(8)}, 5); + process.PushChild({transformer.GetRule(PEGRuleId::InsertValues), list_pr.GetChild(8)}, 5); auto &insert_column_list_opt = list_pr.GetChild(6).Cast(); if (insert_column_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("InsertColumnList"), insert_column_list_opt.GetResult()}, 4); + process.PushChild({transformer.GetRule(PEGRuleId::InsertColumnList), insert_column_list_opt.GetResult()}, 4); } auto &by_name_or_position_opt = list_pr.GetChild(5).Cast(); if (by_name_or_position_opt.HasResult()) { - process.PushChild({transformer.GetRule("ByNameOrPosition"), by_name_or_position_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::ByNameOrPosition), by_name_or_position_opt.GetResult()}, 3); } - process.PushChild({transformer.GetRule("InsertTarget"), list_pr.GetChild(4)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::InsertTarget), list_pr.GetChild(4)}, 2); auto &or_action_opt = list_pr.GetChild(2).Cast(); if (or_action_opt.HasResult()) { - process.PushChild({transformer.GetRule("OrAction"), or_action_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::OrAction), or_action_opt.GetResult()}, 1); } auto &with_clause_opt = list_pr.GetChild(0).Cast(); if (with_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("WithClause"), with_clause_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::WithClause), with_clause_opt.GetResult()}, 0); } } @@ -19699,7 +19861,7 @@ void PEGTransformerFactory::InitializeInsertByNameOrderTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("InsertByName"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::InsertByName), list_pr.GetChild(1)}, 0); } arena_ptr @@ -19713,7 +19875,7 @@ void PEGTransformerFactory::InitializeInsertByPositionOrderTrampoline(PEGTransfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("InsertByPosition"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::InsertByPosition), list_pr.GetChild(1)}, 0); } arena_ptr @@ -19752,9 +19914,9 @@ void PEGTransformerFactory::InitializeInsertTargetTrampoline(PEGTransformer &tra process.ReserveChildSlots(2); auto &insert_alias_opt = list_pr.GetChild(1).Cast(); if (insert_alias_opt.HasResult()) { - process.PushChild({transformer.GetRule("InsertAlias"), insert_alias_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::InsertAlias), insert_alias_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BaseTableName), list_pr.GetChild(0)}, 0); } arena_ptr @@ -19789,7 +19951,7 @@ void PEGTransformerFactory::InitializeColumnListTrampoline(PEGTransformer &trans process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColId"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_items[child_idx].get()}, 0 + child_idx); } } @@ -19799,6 +19961,7 @@ PEGTransformerFactory::FinalizeColumnListTrampoline(PEGTransformer &transformer, auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); auto dynamic_child_count = dynamic_list_items.size(); vector col_id; + col_id.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { col_id.push_back(process.TakeResult(i)); } @@ -19810,7 +19973,7 @@ void PEGTransformerFactory::InitializeInsertColumnListTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColumnList"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColumnList), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr @@ -19845,7 +20008,7 @@ void PEGTransformerFactory::InitializeSelectInsertValuesTrampoline(PEGTransforme GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SelectStatementInternal"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SelectStatementInternal), list_pr.GetChild(0)}, 0); } arena_ptr @@ -19872,10 +20035,10 @@ void PEGTransformerFactory::InitializeOnConflictClauseTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("OnConflictAction"), list_pr.GetChild(3)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::OnConflictAction), list_pr.GetChild(3)}, 1); auto &on_conflict_target_opt = list_pr.GetChild(2).Cast(); if (on_conflict_target_opt.HasResult()) { - process.PushChild({transformer.GetRule("OnConflictTarget"), on_conflict_target_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OnConflictTarget), on_conflict_target_opt.GetResult()}, 0); } } @@ -19918,9 +20081,9 @@ void PEGTransformerFactory::InitializeOnConflictExpressionTargetTrampoline(PEGTr process.ReserveChildSlots(2); auto &where_clause_opt = list_pr.GetChild(1).Cast(); if (where_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("WhereClause"), where_clause_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::WhereClause), where_clause_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("ColumnIdList"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColumnIdList), list_pr.GetChild(0)}, 0); } arena_ptr @@ -19939,7 +20102,7 @@ void PEGTransformerFactory::InitializeOnConflictIndexTargetTrampoline(PEGTransfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ConstraintName"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ConstraintName), list_pr.GetChild(2)}, 0); } arena_ptr @@ -19977,9 +20140,9 @@ void PEGTransformerFactory::InitializeOnConflictUpdateTrampoline(PEGTransformer process.ReserveChildSlots(2); auto &where_clause_opt = list_pr.GetChild(4).Cast(); if (where_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("WhereClause"), where_clause_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::WhereClause), where_clause_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("UpdateSetClauseList"), list_pr.GetChild(3)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::UpdateSetClauseList), list_pr.GetChild(3)}, 0); } arena_ptr @@ -20010,7 +20173,7 @@ void PEGTransformerFactory::InitializeReturningClauseTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("TargetList"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TargetList), list_pr.GetChild(1)}, 0); } arena_ptr @@ -20027,13 +20190,13 @@ void PEGTransformerFactory::InitializeLoadStatementTrampoline(PEGTransformer &tr process.ReserveChildSlots(3); auto &extension_alias_opt = list_pr.GetChild(3).Cast(); if (extension_alias_opt.HasResult()) { - process.PushChild({transformer.GetRule("ExtensionAlias"), extension_alias_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::ExtensionAlias), extension_alias_opt.GetResult()}, 2); } auto &from_source_opt = list_pr.GetChild(2).Cast(); if (from_source_opt.HasResult()) { - process.PushChild({transformer.GetRule("FromSource"), from_source_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::FromSource), from_source_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_pr.GetChild(1)}, 0); } arena_ptr @@ -20072,16 +20235,16 @@ void PEGTransformerFactory::InitializeInstallStatementTrampoline(PEGTransformer process.ReserveChildSlots(4); auto &version_number_opt = list_pr.GetChild(5).Cast(); if (version_number_opt.HasResult()) { - process.PushChild({transformer.GetRule("VersionNumber"), version_number_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::VersionNumber), version_number_opt.GetResult()}, 3); } auto &from_source_opt = list_pr.GetChild(4).Cast(); if (from_source_opt.HasResult()) { - process.PushChild({transformer.GetRule("FromSource"), from_source_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::FromSource), from_source_opt.GetResult()}, 2); } - process.PushChild({transformer.GetRule("IdentifierOrStringLiteral"), list_pr.GetChild(3)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::IdentifierOrStringLiteral), list_pr.GetChild(3)}, 1); auto &install_and_load_opt = list_pr.GetChild(2).Cast(); if (install_and_load_opt.HasResult()) { - process.PushChild({transformer.GetRule("InstallAndLoad"), install_and_load_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::InstallAndLoad), install_and_load_opt.GetResult()}, 0); } } @@ -20136,6 +20299,7 @@ PEGTransformerFactory::FinalizeUpdateExtensionsStatementTrampoline(PEGTransforme if (identifier_opt.HasResult()) { vector identifier_value; auto identifier_items = ExtractParseResultsFromList(ExtractResultFromParens(identifier_opt.GetResult())); + identifier_value.reserve(identifier_items.size()); for (auto &identifier_item : identifier_items) { auto identifier_item_value = identifier_item.get().Cast().identifier; identifier_value.push_back(identifier_item_value); @@ -20198,7 +20362,7 @@ void PEGTransformerFactory::InitializeVersionNumberTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("IdentifierOrStringLiteral"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IdentifierOrStringLiteral), list_pr.GetChild(1)}, 0); } arena_ptr @@ -20236,17 +20400,18 @@ void PEGTransformerFactory::InitializeCreateExtensionRepositoryStmtTrampoline(PE process.ReserveChildSlots(5); auto &repository_public_key_opt = list_pr.GetChild(7).Cast(); if (repository_public_key_opt.HasResult()) { - process.PushChild({transformer.GetRule("RepositoryPublicKey"), repository_public_key_opt.GetResult()}, 4); + process.PushChild({transformer.GetRule(PEGRuleId::RepositoryPublicKey), repository_public_key_opt.GetResult()}, + 4); } - process.PushChild({transformer.GetRule("RepositoryPrefix"), list_pr.GetChild(6)}, 3); - process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(5)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::RepositoryPrefix), list_pr.GetChild(6)}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_pr.GetChild(5)}, 2); auto &if_not_exists_opt = list_pr.GetChild(4).Cast(); if (if_not_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfNotExists"), if_not_exists_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::IfNotExists), if_not_exists_opt.GetResult()}, 1); } auto &or_replace_opt = list_pr.GetChild(1).Cast(); if (or_replace_opt.HasResult()) { - process.PushChild({transformer.GetRule("OrReplace"), or_replace_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OrReplace), or_replace_opt.GetResult()}, 0); } } @@ -20303,6 +20468,7 @@ PEGTransformerFactory::FinalizeRepositoryPublicKeyTrampoline(PEGTransformer &tra has_result = has_result_opt.HasResult(); vector string_literal; auto string_literal_items = ExtractParseResultsFromList(list_pr.GetChild(3)); + string_literal.reserve(string_literal_items.size()); for (auto &string_literal_item : string_literal_items) { auto string_literal_value = TransformStringLiteral(transformer, string_literal_item.get()); string_literal.push_back(string_literal_value); @@ -20315,10 +20481,10 @@ void PEGTransformerFactory::InitializeDropExtensionRepositoryStmtTrampoline(PEGT GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(4)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_pr.GetChild(4)}, 1); auto &if_exists_opt = list_pr.GetChild(3).Cast(); if (if_exists_opt.HasResult()) { - process.PushChild({transformer.GetRule("IfExists"), if_exists_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IfExists), if_exists_opt.GetResult()}, 0); } } @@ -20340,9 +20506,10 @@ void PEGTransformerFactory::InitializeTableMatchRecognizeClauseTrampoline(PEGTra process.ReserveChildSlots(2); auto &table_alias_opt = list_pr.GetChild(2).Cast(); if (table_alias_opt.HasResult()) { - process.PushChild({transformer.GetRule("TableAlias"), table_alias_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::TableAlias), table_alias_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("MatchRecognizeBody"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::MatchRecognizeBody), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } arena_ptr @@ -20366,7 +20533,7 @@ void PEGTransformerFactory::InitializeMatchRecognizeBodyTrampoline(PEGTransforme process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("MatchRecognizeClause"), repeat_children[child_idx].get()}, + process.PushChild({transformer.GetRule(PEGRuleId::MatchRecognizeClause), repeat_children[child_idx].get()}, 0 + child_idx); } } @@ -20379,6 +20546,7 @@ PEGTransformerFactory::FinalizeMatchRecognizeBodyTrampoline(PEGTransformer &tran auto dynamic_repeat_children = dynamic_repeat_pr.GetChildren(); auto dynamic_child_count = dynamic_repeat_children.size(); vector match_recognize_clause; + match_recognize_clause.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { match_recognize_clause.push_back(process.TakeResult(i)); } @@ -20411,7 +20579,7 @@ void PEGTransformerFactory::InitializeMRPartitionTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("WindowPartition"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::WindowPartition), list_pr.GetChild(0)}, 0); } arena_ptr @@ -20425,7 +20593,7 @@ void PEGTransformerFactory::InitializeMROrderByTrampoline(PEGTransformer &transf GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("OrderByClause"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OrderByClause), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeMROrderByTrampoline(PEGTransformer &transformer, @@ -20439,7 +20607,7 @@ void PEGTransformerFactory::InitializeMRMeasuresTrampoline(PEGTransformer &trans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("MeasuresClause"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::MeasuresClause), list_pr.GetChild(0)}, 0); } arena_ptr @@ -20453,7 +20621,7 @@ void PEGTransformerFactory::InitializeMRRowsTrampoline(PEGTransformer &transform GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("RowsPerMatch"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::RowsPerMatch), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeMRRowsTrampoline(PEGTransformer &transformer, @@ -20467,7 +20635,7 @@ void PEGTransformerFactory::InitializeMRSkipTrampoline(PEGTransformer &transform GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("AfterMatchSkip"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::AfterMatchSkip), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeMRSkipTrampoline(PEGTransformer &transformer, @@ -20481,7 +20649,7 @@ void PEGTransformerFactory::InitializeMRPatternTrampoline(PEGTransformer &transf GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("PatternClause"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::PatternClause), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeMRPatternTrampoline(PEGTransformer &transformer, @@ -20495,7 +20663,7 @@ void PEGTransformerFactory::InitializeMRSubsetTrampoline(PEGTransformer &transfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SubsetClause"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SubsetClause), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeMRSubsetTrampoline(PEGTransformer &transformer, @@ -20520,7 +20688,7 @@ void PEGTransformerFactory::InitializeMRDefineTrampoline(PEGTransformer &transfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("DefineClause"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::DefineClause), list_pr.GetChild(0)}, 0); } arena_ptr PEGTransformerFactory::FinalizeMRDefineTrampoline(PEGTransformer &transformer, @@ -20538,7 +20706,8 @@ void PEGTransformerFactory::InitializeMeasuresClauseTrampoline(PEGTransformer &t process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("MeasuresElement"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::MeasuresElement), list_items[child_idx].get()}, + 0 + child_idx); } } @@ -20549,6 +20718,7 @@ PEGTransformerFactory::FinalizeMeasuresClauseTrampoline(PEGTransformer &transfor auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(1)); auto dynamic_child_count = dynamic_list_items.size(); vector> measures_element; + measures_element.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { measures_element.push_back(process.TakeResult>(i)); } @@ -20560,8 +20730,8 @@ void PEGTransformerFactory::InitializeMeasuresElementTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("ColLabelOrString"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColLabelOrString), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -20656,7 +20826,8 @@ void PEGTransformerFactory::InitializeAllRowsPerMatchTrampoline(PEGTransformer & process.ReserveChildSlots(1); auto &all_rows_empty_matches_opt = list_pr.GetChild(4).Cast(); if (all_rows_empty_matches_opt.HasResult()) { - process.PushChild({transformer.GetRule("AllRowsEmptyMatches"), all_rows_empty_matches_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::AllRowsEmptyMatches), all_rows_empty_matches_opt.GetResult()}, + 0); } } @@ -20732,7 +20903,7 @@ void PEGTransformerFactory::InitializeAfterMatchSkipTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("AfterMatchSkipTarget"), list_pr.GetChild(3)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::AfterMatchSkipTarget), list_pr.GetChild(3)}, 0); } arena_ptr @@ -20791,7 +20962,7 @@ void PEGTransformerFactory::InitializeSkipToFirstVarTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColLabelOrString"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColLabelOrString), list_pr.GetChild(2)}, 0); } arena_ptr @@ -20806,7 +20977,7 @@ void PEGTransformerFactory::InitializeSkipToLastVarTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColLabelOrString"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColLabelOrString), list_pr.GetChild(2)}, 0); } arena_ptr @@ -20821,7 +20992,7 @@ void PEGTransformerFactory::InitializeSkipToVarTrampoline(PEGTransformer &transf GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColLabelOrString"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColLabelOrString), list_pr.GetChild(1)}, 0); } arena_ptr PEGTransformerFactory::FinalizeSkipToVarTrampoline(PEGTransformer &transformer, @@ -20835,7 +21006,7 @@ void PEGTransformerFactory::InitializePatternClauseTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("RowPattern"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::RowPattern), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } arena_ptr @@ -20857,13 +21028,13 @@ void PEGTransformerFactory::InitializeRowPatternTrampoline(PEGTransformer &trans process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("RowPatternAlternative"), repeat_children[child_idx].get()}, + process.PushChild({transformer.GetRule(PEGRuleId::RowPatternAlternative), repeat_children[child_idx].get()}, 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("RowPatternTerm"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::RowPatternTerm), list_pr.GetChild(0)}, 0); } arena_ptr @@ -20880,6 +21051,7 @@ PEGTransformerFactory::FinalizeRowPatternTrampoline(PEGTransformer &transformer, optional>> row_pattern_alternative {}; if (dynamic_child_count > 0) { vector> row_pattern_alternative_value; + row_pattern_alternative_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { row_pattern_alternative_value.push_back(process.TakeResult>(i)); } @@ -20893,7 +21065,7 @@ void PEGTransformerFactory::InitializeRowPatternAlternativeTrampoline(PEGTransfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("RowPatternTerm"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::RowPatternTerm), list_pr.GetChild(1)}, 0); } arena_ptr @@ -20912,7 +21084,8 @@ void PEGTransformerFactory::InitializeRowPatternTermTrampoline(PEGTransformer &t process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("RowPatternFactor"), repeat_children[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::RowPatternFactor), repeat_children[child_idx].get()}, + 0 + child_idx); } } @@ -20924,6 +21097,7 @@ PEGTransformerFactory::FinalizeRowPatternTermTrampoline(PEGTransformer &transfor auto dynamic_repeat_children = dynamic_repeat_pr.GetChildren(); auto dynamic_child_count = dynamic_repeat_children.size(); vector> row_pattern_factor; + row_pattern_factor.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { row_pattern_factor.push_back(process.TakeResult>(i)); } @@ -20937,9 +21111,10 @@ void PEGTransformerFactory::InitializeRowPatternFactorTrampoline(PEGTransformer process.ReserveChildSlots(2); auto &row_pattern_quantifier_opt = list_pr.GetChild(1).Cast(); if (row_pattern_quantifier_opt.HasResult()) { - process.PushChild({transformer.GetRule("RowPatternQuantifier"), row_pattern_quantifier_opt.GetResult()}, 1); + process.PushChild( + {transformer.GetRule(PEGRuleId::RowPatternQuantifier), row_pattern_quantifier_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("RowPatternPrimary"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::RowPatternPrimary), list_pr.GetChild(0)}, 0); } arena_ptr @@ -20983,7 +21158,7 @@ void PEGTransformerFactory::InitializeRowPatternPermuteTrampoline(PEGTransformer process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("RowPattern"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::RowPattern), list_items[child_idx].get()}, 0 + child_idx); } } @@ -20994,6 +21169,7 @@ PEGTransformerFactory::FinalizeRowPatternPermuteTrampoline(PEGTransformer &trans auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(1))); auto dynamic_child_count = dynamic_list_items.size(); vector> row_pattern; + row_pattern.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { row_pattern.push_back(process.TakeResult>(i)); } @@ -21048,7 +21224,7 @@ void PEGTransformerFactory::InitializeRowPatternGroupTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("RowPattern"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::RowPattern), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr @@ -21074,7 +21250,7 @@ void PEGTransformerFactory::InitializeRowPatternExclusionTrampoline(PEGTransform GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("RowPattern"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::RowPattern), list_pr.GetChild(2)}, 0); } arena_ptr @@ -21089,7 +21265,7 @@ void PEGTransformerFactory::InitializeRowPatternLabelTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColLabelOrString"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColLabelOrString), list_pr.GetChild(0)}, 0); } arena_ptr @@ -21106,9 +21282,10 @@ void PEGTransformerFactory::InitializeRowPatternQuantifierTrampoline(PEGTransfor process.ReserveChildSlots(2); auto &quantifier_reluctant_opt = list_pr.GetChild(1).Cast(); if (quantifier_reluctant_opt.HasResult()) { - process.PushChild({transformer.GetRule("QuantifierReluctant"), quantifier_reluctant_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::QuantifierReluctant), quantifier_reluctant_opt.GetResult()}, + 1); } - process.PushChild({transformer.GetRule("RowPatternQuantifierKind"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::RowPatternQuantifierKind), list_pr.GetChild(0)}, 0); } arena_ptr @@ -21196,7 +21373,7 @@ void PEGTransformerFactory::InitializeQuantifierRangeTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("QuantifierBounds"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::QuantifierBounds), list_pr.GetChild(1)}, 0); } arena_ptr @@ -21292,7 +21469,7 @@ void PEGTransformerFactory::InitializeSubsetClauseTrampoline(PEGTransformer &tra process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("SubsetElement"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::SubsetElement), list_items[child_idx].get()}, 0 + child_idx); } } @@ -21302,6 +21479,7 @@ PEGTransformerFactory::FinalizeSubsetClauseTrampoline(PEGTransformer &transforme auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(1)); auto dynamic_child_count = dynamic_list_items.size(); vector subset_element; + subset_element.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { subset_element.push_back(process.TakeResult(i)); } @@ -21317,9 +21495,10 @@ void PEGTransformerFactory::InitializeSubsetElementTrampoline(PEGTransformer &tr process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColLabelOrString"), list_items[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ColLabelOrString), list_items[child_idx].get()}, + 1 + child_idx); } - process.PushChild({transformer.GetRule("ColLabelOrString"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColLabelOrString), list_pr.GetChild(0)}, 0); } arena_ptr @@ -21330,6 +21509,7 @@ PEGTransformerFactory::FinalizeSubsetElementTrampoline(PEGTransformer &transform auto dynamic_child_count = dynamic_list_items.size(); auto col_label_or_string = process.TakeResult(0); vector col_label_or_string_1; + col_label_or_string_1.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { col_label_or_string_1.push_back(process.TakeResult(i)); } @@ -21345,7 +21525,7 @@ void PEGTransformerFactory::InitializeDefineClauseTrampoline(PEGTransformer &tra process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("DefineElement"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::DefineElement), list_items[child_idx].get()}, 0 + child_idx); } } @@ -21355,6 +21535,7 @@ PEGTransformerFactory::FinalizeDefineClauseTrampoline(PEGTransformer &transforme auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(1)); auto dynamic_child_count = dynamic_list_items.size(); vector> define_element; + define_element.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { define_element.push_back(process.TakeResult>(i)); } @@ -21366,8 +21547,8 @@ void PEGTransformerFactory::InitializeDefineElementTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("ColLabelOrString"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ColLabelOrString), list_pr.GetChild(0)}, 0); } arena_ptr @@ -21388,19 +21569,20 @@ void PEGTransformerFactory::InitializeMergeIntoStatementTrampoline(PEGTransforme process.ReserveChildSlots(6 + dynamic_child_count - 1); auto &returning_clause_opt = list_pr.GetChild(7).Cast(); if (returning_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("ReturningClause"), returning_clause_opt.GetResult()}, + process.PushChild({transformer.GetRule(PEGRuleId::ReturningClause), returning_clause_opt.GetResult()}, 5 + dynamic_child_count - 1); } for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("MergeMatch"), repeat_children[child_idx].get()}, 4 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::MergeMatch), repeat_children[child_idx].get()}, + 4 + child_idx); } - process.PushChild({transformer.GetRule("JoinQualifier"), list_pr.GetChild(5)}, 3); - process.PushChild({transformer.GetRule("MergeIntoUsingClause"), list_pr.GetChild(4)}, 2); - process.PushChild({transformer.GetRule("TargetOptAlias"), list_pr.GetChild(3)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::JoinQualifier), list_pr.GetChild(5)}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::MergeIntoUsingClause), list_pr.GetChild(4)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::TargetOptAlias), list_pr.GetChild(3)}, 1); auto &with_clause_opt = list_pr.GetChild(0).Cast(); if (with_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("WithClause"), with_clause_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::WithClause), with_clause_opt.GetResult()}, 0); } } @@ -21419,6 +21601,7 @@ PEGTransformerFactory::FinalizeMergeIntoStatementTrampoline(PEGTransformer &tran auto merge_into_using_clause = process.TakeResult>(2); auto join_qualifier = process.TakeResult(3); vector>> merge_match; + merge_match.reserve(dynamic_child_count); for (idx_t i = 4; i < 4 + dynamic_child_count; i++) { merge_match.push_back(process.TakeResult>>(i)); } @@ -21436,7 +21619,7 @@ void PEGTransformerFactory::InitializeMergeIntoUsingClauseTrampoline(PEGTransfor GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("TableRef"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TableRef), list_pr.GetChild(1)}, 0); } arena_ptr @@ -21471,10 +21654,10 @@ void PEGTransformerFactory::InitializeMatchedClauseTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("MatchedClauseAction"), list_pr.GetChild(4)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::MatchedClauseAction), list_pr.GetChild(4)}, 1); auto &and_expression_opt = list_pr.GetChild(2).Cast(); if (and_expression_opt.HasResult()) { - process.PushChild({transformer.GetRule("AndExpression"), and_expression_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::AndExpression), and_expression_opt.GetResult()}, 0); } } @@ -21517,7 +21700,7 @@ void PEGTransformerFactory::InitializeUpdateMatchClauseTrampoline(PEGTransformer process.ReserveChildSlots(1); auto &update_match_info_opt = list_pr.GetChild(1).Cast(); if (update_match_info_opt.HasResult()) { - process.PushChild({transformer.GetRule("UpdateMatchInfo"), update_match_info_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::UpdateMatchInfo), update_match_info_opt.GetResult()}, 0); } } @@ -21557,7 +21740,7 @@ void PEGTransformerFactory::InitializeUpdateMatchSetActionTrampoline(PEGTransfor GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("UpdateMatchSetClause"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::UpdateMatchSetClause), list_pr.GetChild(0)}, 0); } arena_ptr @@ -21572,7 +21755,7 @@ void PEGTransformerFactory::InitializeUpdateByNameOrPositionTrampoline(PEGTransf GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ByNameOrPosition"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ByNameOrPosition), list_pr.GetChild(0)}, 0); } arena_ptr @@ -21601,7 +21784,7 @@ void PEGTransformerFactory::InitializeInsertMatchClauseTrampoline(PEGTransformer process.ReserveChildSlots(1); auto &insert_match_info_opt = list_pr.GetChild(1).Cast(); if (insert_match_info_opt.HasResult()) { - process.PushChild({transformer.GetRule("InsertMatchInfo"), insert_match_info_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::InsertMatchInfo), insert_match_info_opt.GetResult()}, 0); } } @@ -21655,7 +21838,7 @@ void PEGTransformerFactory::InitializeInsertByNameOrPositionTrampoline(PEGTransf process.ReserveChildSlots(1); auto &by_name_or_position_opt = list_pr.GetChild(0).Cast(); if (by_name_or_position_opt.HasResult()) { - process.PushChild({transformer.GetRule("ByNameOrPosition"), by_name_or_position_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ByNameOrPosition), by_name_or_position_opt.GetResult()}, 0); } } @@ -21682,11 +21865,11 @@ void PEGTransformerFactory::InitializeInsertValuesListTrampoline(PEGTransformer process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 1 + child_idx); } auto &insert_column_list_opt = list_pr.GetChild(0).Cast(); if (insert_column_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("InsertColumnList"), insert_column_list_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::InsertColumnList), insert_column_list_opt.GetResult()}, 0); } } @@ -21701,6 +21884,7 @@ PEGTransformerFactory::FinalizeInsertValuesListTrampoline(PEGTransformer &transf insert_column_list = process.TakeResult>(0); } vector> expression; + expression.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { expression.push_back(process.TakeResult>(i)); } @@ -21726,7 +21910,7 @@ void PEGTransformerFactory::InitializeErrorMatchClauseTrampoline(PEGTransformer process.ReserveChildSlots(1); auto &expression_opt = list_pr.GetChild(1).Cast(); if (expression_opt.HasResult()) { - process.PushChild({transformer.GetRule("Expression"), expression_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), expression_opt.GetResult()}, 0); } } @@ -21745,7 +21929,7 @@ void PEGTransformerFactory::InitializeUpdateMatchSetClauseTrampoline(PEGTransfor GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("UpdateMatchSetInfo"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::UpdateMatchSetInfo), list_pr.GetChild(1)}, 0); } arena_ptr @@ -21786,7 +21970,7 @@ void PEGTransformerFactory::InitializeAndExpressionTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -21801,14 +21985,14 @@ void PEGTransformerFactory::InitializeNotMatchedClauseTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(3); - process.PushChild({transformer.GetRule("MatchedClauseAction"), list_pr.GetChild(6)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::MatchedClauseAction), list_pr.GetChild(6)}, 2); auto &and_expression_opt = list_pr.GetChild(4).Cast(); if (and_expression_opt.HasResult()) { - process.PushChild({transformer.GetRule("AndExpression"), and_expression_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::AndExpression), and_expression_opt.GetResult()}, 1); } auto &by_source_or_target_opt = list_pr.GetChild(3).Cast(); if (by_source_or_target_opt.HasResult()) { - process.PushChild({transformer.GetRule("BySourceOrTarget"), by_source_or_target_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BySourceOrTarget), by_source_or_target_opt.GetResult()}, 0); } } @@ -21876,7 +22060,7 @@ void PEGTransformerFactory::InitializePivotOnTrampoline(PEGTransformer &transfor GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("PivotColumnList"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::PivotColumnList), list_pr.GetChild(1)}, 0); } arena_ptr PEGTransformerFactory::FinalizePivotOnTrampoline(PEGTransformer &transformer, @@ -21890,7 +22074,7 @@ void PEGTransformerFactory::InitializePivotUsingTrampoline(PEGTransformer &trans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("TargetList"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TargetList), list_pr.GetChild(1)}, 0); } arena_ptr @@ -21908,7 +22092,8 @@ void PEGTransformerFactory::InitializePivotColumnListTrampoline(PEGTransformer & process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("PivotColumnEntry"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::PivotColumnEntry), list_items[child_idx].get()}, + 0 + child_idx); } } @@ -21919,6 +22104,7 @@ PEGTransformerFactory::FinalizePivotColumnListTrampoline(PEGTransformer &transfo auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); auto dynamic_child_count = dynamic_list_items.size(); vector pivot_column_entry; + pivot_column_entry.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { pivot_column_entry.push_back(process.TakeResult(i)); } @@ -21951,7 +22137,7 @@ void PEGTransformerFactory::InitializePivotColumnExpressionTrampoline(PEGTransfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -21966,9 +22152,9 @@ void PEGTransformerFactory::InitializePivotColumnSubqueryTrampoline(PEGTransform GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("SelectStatementInternal"), ExtractResultFromParens(list_pr.GetChild(2))}, - 1); - process.PushChild({transformer.GetRule("BaseExpression"), list_pr.GetChild(0)}, 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::SelectStatementInternal), ExtractResultFromParens(list_pr.GetChild(2))}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::BaseExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -21985,8 +22171,8 @@ void PEGTransformerFactory::InitializeIntoNameValuesTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("OptionalParensNameList"), list_pr.GetChild(4)}, 1); - process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OptionalParensNameList), list_pr.GetChild(4)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_pr.GetChild(2)}, 0); } arena_ptr @@ -22027,7 +22213,7 @@ void PEGTransformerFactory::InitializeParenthesizedNameListTrampoline(PEGTransfo process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColIdOrString"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_items[child_idx].get()}, 0 + child_idx); } } @@ -22038,6 +22224,7 @@ PEGTransformerFactory::FinalizeParenthesizedNameListTrampoline(PEGTransformer &t auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); vector col_id_or_string; + col_id_or_string.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { col_id_or_string.push_back(process.TakeResult(i)); } @@ -22053,7 +22240,7 @@ void PEGTransformerFactory::InitializeBareNameListTrampoline(PEGTransformer &tra process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColIdOrString"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_items[child_idx].get()}, 0 + child_idx); } } @@ -22063,6 +22250,7 @@ PEGTransformerFactory::FinalizeBareNameListTrampoline(PEGTransformer &transforme auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); auto dynamic_child_count = dynamic_list_items.size(); vector col_id_or_string; + col_id_or_string.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { col_id_or_string.push_back(process.TakeResult(i)); } @@ -22138,7 +22326,7 @@ void PEGTransformerFactory::InitializeUnpivotHeaderSingleTrampoline(PEGTransform GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_pr.GetChild(0)}, 0); } arena_ptr @@ -22157,7 +22345,7 @@ void PEGTransformerFactory::InitializeUnpivotHeaderListTrampoline(PEGTransformer process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColIdOrString"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_items[child_idx].get()}, 0 + child_idx); } } @@ -22168,6 +22356,7 @@ PEGTransformerFactory::FinalizeUnpivotHeaderListTrampoline(PEGTransformer &trans auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); vector col_id_or_string; + col_id_or_string.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { col_id_or_string.push_back(process.TakeResult(i)); } @@ -22179,7 +22368,7 @@ void PEGTransformerFactory::InitializePragmaStatementTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("PragmaAssignOrFunction"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::PragmaAssignOrFunction), list_pr.GetChild(1)}, 0); } arena_ptr @@ -22215,7 +22404,7 @@ void PEGTransformerFactory::InitializePragmaAssignTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("VariableList"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::VariableList), list_pr.GetChild(2)}, 0); } arena_ptr @@ -22233,7 +22422,7 @@ void PEGTransformerFactory::InitializePragmaFunctionTrampoline(PEGTransformer &t process.ReserveChildSlots(1); auto &pragma_parameters_opt = list_pr.GetChild(1).Cast(); if (pragma_parameters_opt.HasResult()) { - process.PushChild({transformer.GetRule("PragmaParameters"), pragma_parameters_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::PragmaParameters), pragma_parameters_opt.GetResult()}, 0); } } @@ -22258,7 +22447,7 @@ void PEGTransformerFactory::InitializePragmaParametersTrampoline(PEGTransformer process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 0 + child_idx); } } @@ -22269,6 +22458,7 @@ PEGTransformerFactory::FinalizePragmaParametersTrampoline(PEGTransformer &transf auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); vector> expression; + expression.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { expression.push_back(process.TakeResult>(i)); } @@ -22280,12 +22470,12 @@ void PEGTransformerFactory::InitializePrepareStatementTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(3); - process.PushChild({transformer.GetRule("Statement"), list_pr.GetChild(4)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::Statement), list_pr.GetChild(4)}, 2); auto &type_list_opt = list_pr.GetChild(2).Cast(); if (type_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("TypeList"), type_list_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::TypeList), type_list_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_pr.GetChild(1)}, 0); } arena_ptr @@ -22309,7 +22499,7 @@ void PEGTransformerFactory::InitializeTypeListTrampoline(PEGTransformer &transfo process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Type"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Type), list_items[child_idx].get()}, 0 + child_idx); } } @@ -22319,6 +22509,7 @@ arena_ptr PEGTransformerFactory::FinalizeTypeListTrampolin auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); vector type; + type.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { type.push_back(process.TakeResult(i)); } @@ -22330,7 +22521,7 @@ void PEGTransformerFactory::InitializeSelectStatementTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SelectStatementInternal"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SelectStatementInternal), list_pr.GetChild(0)}, 0); } arena_ptr @@ -22353,13 +22544,13 @@ void PEGTransformerFactory::InitializeSelectSetOpChainTrampoline(PEGTransformer process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("SelectSetOpChainTail"), repeat_children[child_idx].get()}, + process.PushChild({transformer.GetRule(PEGRuleId::SelectSetOpChainTail), repeat_children[child_idx].get()}, 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("IntersectChain"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IntersectChain), list_pr.GetChild(0)}, 0); } arena_ptr @@ -22377,6 +22568,7 @@ PEGTransformerFactory::FinalizeSelectSetOpChainTrampoline(PEGTransformer &transf optional, unique_ptr>>> select_set_op_chain_tail {}; if (dynamic_child_count > 0) { vector, unique_ptr>> select_set_op_chain_tail_value; + select_set_op_chain_tail_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { select_set_op_chain_tail_value.push_back( process.TakeResult, unique_ptr>>(i)); @@ -22392,8 +22584,8 @@ void PEGTransformerFactory::InitializeSelectSetOpChainTailTrampoline(PEGTransfor GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("IntersectChain"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("SetopClause"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IntersectChain), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::SetopClause), list_pr.GetChild(0)}, 0); } arena_ptr @@ -22417,13 +22609,13 @@ void PEGTransformerFactory::InitializeIntersectChainTrampoline(PEGTransformer &t process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("IntersectChainTail"), repeat_children[child_idx].get()}, + process.PushChild({transformer.GetRule(PEGRuleId::IntersectChainTail), repeat_children[child_idx].get()}, 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("SelectAtom"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SelectAtom), list_pr.GetChild(0)}, 0); } arena_ptr @@ -22441,6 +22633,7 @@ PEGTransformerFactory::FinalizeIntersectChainTrampoline(PEGTransformer &transfor optional, unique_ptr>>> intersect_chain_tail {}; if (dynamic_child_count > 0) { vector, unique_ptr>> intersect_chain_tail_value; + intersect_chain_tail_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { intersect_chain_tail_value.push_back( process.TakeResult, unique_ptr>>(i)); @@ -22455,8 +22648,8 @@ void PEGTransformerFactory::InitializeIntersectChainTailTrampoline(PEGTransforme GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("SelectAtom"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("SetIntersectClause"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SelectAtom), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::SetIntersectClause), list_pr.GetChild(0)}, 0); } arena_ptr @@ -22474,7 +22667,7 @@ void PEGTransformerFactory::InitializeSetIntersectClauseTrampoline(PEGTransforme process.ReserveChildSlots(1); auto &distinct_or_all_opt = list_pr.GetChild(1).Cast(); if (distinct_or_all_opt.HasResult()) { - process.PushChild({transformer.GetRule("DistinctOrAll"), distinct_or_all_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::DistinctOrAll), distinct_or_all_opt.GetResult()}, 0); } } @@ -22513,8 +22706,8 @@ void PEGTransformerFactory::InitializeSelectParensTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SelectStatementInternal"), ExtractResultFromParens(list_pr.GetChild(0))}, - 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::SelectStatementInternal), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr @@ -22530,9 +22723,9 @@ void PEGTransformerFactory::InitializeSetopClauseTrampoline(PEGTransformer &tran process.ReserveChildSlots(2); auto &distinct_or_all_opt = list_pr.GetChild(1).Cast(); if (distinct_or_all_opt.HasResult()) { - process.PushChild({transformer.GetRule("DistinctOrAll"), distinct_or_all_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::DistinctOrAll), distinct_or_all_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("SetopType"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SetopType), list_pr.GetChild(0)}, 0); } arena_ptr @@ -22619,11 +22812,11 @@ void PEGTransformerFactory::InitializeResultModifiersTrampoline(PEGTransformer & process.ReserveChildSlots(2); auto &limit_offset_opt = list_pr.GetChild(1).Cast(); if (limit_offset_opt.HasResult()) { - process.PushChild({transformer.GetRule("LimitOffset"), limit_offset_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::LimitOffset), limit_offset_opt.GetResult()}, 1); } auto &order_by_clause_opt = list_pr.GetChild(0).Cast(); if (order_by_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("OrderByClause"), order_by_clause_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OrderByClause), order_by_clause_opt.GetResult()}, 0); } } @@ -22668,9 +22861,9 @@ void PEGTransformerFactory::InitializeLimitOffsetClauseTrampoline(PEGTransformer process.ReserveChildSlots(2); auto &offset_clause_opt = list_pr.GetChild(1).Cast(); if (offset_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("OffsetClause"), offset_clause_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::OffsetClause), offset_clause_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("LimitClause"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::LimitClause), list_pr.GetChild(0)}, 0); } arena_ptr @@ -22691,9 +22884,9 @@ void PEGTransformerFactory::InitializeOffsetLimitClauseTrampoline(PEGTransformer process.ReserveChildSlots(2); auto &limit_clause_opt = list_pr.GetChild(1).Cast(); if (limit_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("LimitClause"), limit_clause_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::LimitClause), limit_clause_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("OffsetClause"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OffsetClause), list_pr.GetChild(0)}, 0); } arena_ptr @@ -22712,8 +22905,8 @@ void PEGTransformerFactory::InitializeOffsetFetchClauseTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("FetchClause"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("OffsetClause"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::FetchClause), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::OffsetClause), list_pr.GetChild(0)}, 0); } arena_ptr @@ -22729,8 +22922,8 @@ void PEGTransformerFactory::InitializeFetchOffsetClauseTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("OffsetClause"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("FetchClause"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OffsetClause), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::FetchClause), list_pr.GetChild(0)}, 0); } arena_ptr @@ -22746,7 +22939,7 @@ void PEGTransformerFactory::InitializeFetchOnlyClauseTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("FetchClause"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::FetchClause), list_pr.GetChild(0)}, 0); } arena_ptr @@ -22761,7 +22954,7 @@ void PEGTransformerFactory::InitializeTableStatementTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BaseTableName), list_pr.GetChild(1)}, 0); } arena_ptr @@ -22797,7 +22990,7 @@ void PEGTransformerFactory::InitializeSimpleSelectParensTrampoline(PEGTransforme GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SimpleSelect"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SimpleSelect), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr @@ -22834,9 +23027,9 @@ void PEGTransformerFactory::InitializeSelectFromClauseTrampoline(PEGTransformer process.ReserveChildSlots(2); auto &from_clause_opt = list_pr.GetChild(1).Cast(); if (from_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("FromClause"), from_clause_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::FromClause), from_clause_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("SelectClause"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SelectClause), list_pr.GetChild(0)}, 0); } arena_ptr @@ -22857,9 +23050,9 @@ void PEGTransformerFactory::InitializeFromSelectClauseTrampoline(PEGTransformer process.ReserveChildSlots(2); auto &select_clause_opt = list_pr.GetChild(1).Cast(); if (select_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("SelectClause"), select_clause_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::SelectClause), select_clause_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("FromClause"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::FromClause), list_pr.GetChild(0)}, 0); } arena_ptr @@ -22878,20 +23071,20 @@ void PEGTransformerFactory::InitializeWithStatementTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(5); - process.PushChild({transformer.GetRule("CTEBody"), list_pr.GetChild(5)}, 4); + process.PushChild({transformer.GetRule(PEGRuleId::CTEBody), list_pr.GetChild(5)}, 4); auto &materialized_opt = list_pr.GetChild(4).Cast(); if (materialized_opt.HasResult()) { - process.PushChild({transformer.GetRule("Materialized"), materialized_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::Materialized), materialized_opt.GetResult()}, 3); } auto &using_key_opt = list_pr.GetChild(2).Cast(); if (using_key_opt.HasResult()) { - process.PushChild({transformer.GetRule("UsingKey"), using_key_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::UsingKey), using_key_opt.GetResult()}, 2); } auto &column_aliases_opt = list_pr.GetChild(1).Cast(); if (column_aliases_opt.HasResult()) { - process.PushChild({transformer.GetRule("ColumnAliases"), column_aliases_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ColumnAliases), column_aliases_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_pr.GetChild(0)}, 0); } arena_ptr @@ -22940,8 +23133,8 @@ void PEGTransformerFactory::InitializeCTESelectBodyTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SelectStatementInternal"), ExtractResultFromParens(list_pr.GetChild(0))}, - 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::SelectStatementInternal), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr @@ -22956,7 +23149,7 @@ void PEGTransformerFactory::InitializeCTEDMLBodyTrampoline(PEGTransformer &trans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Statement"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Statement), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr @@ -22970,7 +23163,7 @@ void PEGTransformerFactory::InitializeUsingKeyTrampoline(PEGTransformer &transfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("TargetList"), ExtractResultFromParens(list_pr.GetChild(2))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TargetList), ExtractResultFromParens(list_pr.GetChild(2))}, 0); } arena_ptr PEGTransformerFactory::FinalizeUsingKeyTrampoline(PEGTransformer &transformer, @@ -23001,11 +23194,11 @@ void PEGTransformerFactory::InitializeSelectClauseTrampoline(PEGTransformer &tra process.ReserveChildSlots(2); auto &target_list_opt = list_pr.GetChild(2).Cast(); if (target_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("TargetList"), target_list_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::TargetList), target_list_opt.GetResult()}, 1); } auto &distinct_clause_opt = list_pr.GetChild(1).Cast(); if (distinct_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("DistinctClause"), distinct_clause_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::DistinctClause), distinct_clause_opt.GetResult()}, 0); } } @@ -23031,7 +23224,8 @@ void PEGTransformerFactory::InitializeTargetListTrampoline(PEGTransformer &trans process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("AliasedExpression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::AliasedExpression), list_items[child_idx].get()}, + 0 + child_idx); } } @@ -23041,6 +23235,7 @@ PEGTransformerFactory::FinalizeTargetListTrampoline(PEGTransformer &transformer, auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); auto dynamic_child_count = dynamic_list_items.size(); vector> aliased_expression; + aliased_expression.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { aliased_expression.push_back(process.TakeResult>(i)); } @@ -23056,7 +23251,7 @@ void PEGTransformerFactory::InitializeColumnAliasesTrampoline(PEGTransformer &tr process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColIdOrString"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_items[child_idx].get()}, 0 + child_idx); } } @@ -23067,6 +23262,7 @@ PEGTransformerFactory::FinalizeColumnAliasesTrampoline(PEGTransformer &transform auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); vector col_id_or_string; + col_id_or_string.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { col_id_or_string.push_back(process.TakeResult(i)); } @@ -23112,7 +23308,7 @@ void PEGTransformerFactory::InitializeDistinctOnTrampoline(PEGTransformer &trans process.ReserveChildSlots(1); auto &distinct_on_targets_opt = list_pr.GetChild(1).Cast(); if (distinct_on_targets_opt.HasResult()) { - process.PushChild({transformer.GetRule("DistinctOnTargets"), distinct_on_targets_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::DistinctOnTargets), distinct_on_targets_opt.GetResult()}, 0); } } @@ -23134,7 +23330,7 @@ void PEGTransformerFactory::InitializeDistinctOnTargetsTrampoline(PEGTransformer process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 0 + child_idx); } } @@ -23145,6 +23341,7 @@ PEGTransformerFactory::FinalizeDistinctOnTargetsTrampoline(PEGTransformer &trans auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(1))); auto dynamic_child_count = dynamic_list_items.size(); vector> expression; + expression.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { expression.push_back(process.TakeResult>(i)); } @@ -23179,20 +23376,20 @@ void PEGTransformerFactory::InitializeTableSubqueryTrampoline(PEGTransformer &tr process.ReserveChildSlots(5); auto &sample_clause_opt = list_pr.GetChild(4).Cast(); if (sample_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("SampleClause"), sample_clause_opt.GetResult()}, 4); + process.PushChild({transformer.GetRule(PEGRuleId::SampleClause), sample_clause_opt.GetResult()}, 4); } auto &table_alias_opt = list_pr.GetChild(3).Cast(); if (table_alias_opt.HasResult()) { - process.PushChild({transformer.GetRule("TableAlias"), table_alias_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::TableAlias), table_alias_opt.GetResult()}, 3); } - process.PushChild({transformer.GetRule("SubqueryReference"), list_pr.GetChild(2)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::SubqueryReference), list_pr.GetChild(2)}, 2); auto &lateral_opt = list_pr.GetChild(1).Cast(); if (lateral_opt.HasResult()) { - process.PushChild({transformer.GetRule("Lateral"), lateral_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Lateral), lateral_opt.GetResult()}, 1); } auto &table_alias_colon_opt = list_pr.GetChild(0).Cast(); if (table_alias_colon_opt.HasResult()) { - process.PushChild({transformer.GetRule("TableAliasColon"), table_alias_colon_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TableAliasColon), table_alias_colon_opt.GetResult()}, 0); } } @@ -23227,20 +23424,20 @@ void PEGTransformerFactory::InitializeBaseTableRefTrampoline(PEGTransformer &tra process.ReserveChildSlots(5); auto &sample_clause_opt = list_pr.GetChild(4).Cast(); if (sample_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("SampleClause"), sample_clause_opt.GetResult()}, 4); + process.PushChild({transformer.GetRule(PEGRuleId::SampleClause), sample_clause_opt.GetResult()}, 4); } auto &at_clause_opt = list_pr.GetChild(3).Cast(); if (at_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("AtClause"), at_clause_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::AtClause), at_clause_opt.GetResult()}, 3); } auto &table_alias_opt = list_pr.GetChild(2).Cast(); if (table_alias_opt.HasResult()) { - process.PushChild({transformer.GetRule("TableAlias"), table_alias_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::TableAlias), table_alias_opt.GetResult()}, 2); } - process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::BaseTableName), list_pr.GetChild(1)}, 1); auto &table_alias_colon_opt = list_pr.GetChild(0).Cast(); if (table_alias_colon_opt.HasResult()) { - process.PushChild({transformer.GetRule("TableAliasColon"), table_alias_colon_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TableAliasColon), table_alias_colon_opt.GetResult()}, 0); } } @@ -23272,7 +23469,7 @@ void PEGTransformerFactory::InitializeTableAliasColonTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColIdOrString"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_pr.GetChild(0)}, 0); } arena_ptr @@ -23289,16 +23486,16 @@ void PEGTransformerFactory::InitializeValuesRefTrampoline(PEGTransformer &transf process.ReserveChildSlots(4); auto &sample_clause_opt = list_pr.GetChild(3).Cast(); if (sample_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("SampleClause"), sample_clause_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::SampleClause), sample_clause_opt.GetResult()}, 3); } auto &table_alias_opt = list_pr.GetChild(2).Cast(); if (table_alias_opt.HasResult()) { - process.PushChild({transformer.GetRule("TableAlias"), table_alias_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::TableAlias), table_alias_opt.GetResult()}, 2); } - process.PushChild({transformer.GetRule("ValuesClause"), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ValuesClause), list_pr.GetChild(1)}, 1); auto &table_alias_colon_opt = list_pr.GetChild(0).Cast(); if (table_alias_colon_opt.HasResult()) { - process.PushChild({transformer.GetRule("TableAliasColon"), table_alias_colon_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TableAliasColon), table_alias_colon_opt.GetResult()}, 0); } } @@ -23328,16 +23525,16 @@ void PEGTransformerFactory::InitializeParensTableRefTrampoline(PEGTransformer &t process.ReserveChildSlots(4); auto &sample_clause_opt = list_pr.GetChild(3).Cast(); if (sample_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("SampleClause"), sample_clause_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::SampleClause), sample_clause_opt.GetResult()}, 3); } auto &table_alias_opt = list_pr.GetChild(2).Cast(); if (table_alias_opt.HasResult()) { - process.PushChild({transformer.GetRule("TableAlias"), table_alias_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::TableAlias), table_alias_opt.GetResult()}, 2); } - process.PushChild({transformer.GetRule("TableRef"), ExtractResultFromParens(list_pr.GetChild(1))}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::TableRef), ExtractResultFromParens(list_pr.GetChild(1))}, 1); auto &table_alias_colon_opt = list_pr.GetChild(0).Cast(); if (table_alias_colon_opt.HasResult()) { - process.PushChild({transformer.GetRule("TableAliasColon"), table_alias_colon_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TableAliasColon), table_alias_colon_opt.GetResult()}, 0); } } @@ -23388,9 +23585,10 @@ void PEGTransformerFactory::InitializeTablePivotClauseTrampoline(PEGTransformer process.ReserveChildSlots(2); auto &table_alias_opt = list_pr.GetChild(2).Cast(); if (table_alias_opt.HasResult()) { - process.PushChild({transformer.GetRule("TableAlias"), table_alias_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::TableAlias), table_alias_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("TablePivotClauseBody"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::TablePivotClauseBody), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } arena_ptr @@ -23414,14 +23612,15 @@ void PEGTransformerFactory::InitializeTablePivotClauseBodyTrampoline(PEGTransfor process.ReserveChildSlots(3 + dynamic_child_count - 1); auto &pivot_group_by_list_opt = list_pr.GetChild(3).Cast(); if (pivot_group_by_list_opt.HasResult()) { - process.PushChild({transformer.GetRule("PivotGroupByList"), pivot_group_by_list_opt.GetResult()}, + process.PushChild({transformer.GetRule(PEGRuleId::PivotGroupByList), pivot_group_by_list_opt.GetResult()}, 2 + dynamic_child_count - 1); } for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("PivotValueList"), repeat_children[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::PivotValueList), repeat_children[child_idx].get()}, + 1 + child_idx); } - process.PushChild({transformer.GetRule("TargetList"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TargetList), list_pr.GetChild(0)}, 0); } arena_ptr @@ -23433,6 +23632,7 @@ PEGTransformerFactory::FinalizeTablePivotClauseBodyTrampoline(PEGTransformer &tr auto dynamic_child_count = dynamic_repeat_children.size(); auto target_list = process.TakeResult>>(0); vector pivot_value_list; + pivot_value_list.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { pivot_value_list.push_back(process.TakeResult(i)); } @@ -23449,7 +23649,7 @@ void PEGTransformerFactory::InitializePivotGroupByListTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("OptionalParensNameList"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OptionalParensNameList), list_pr.GetChild(2)}, 0); } arena_ptr @@ -23466,12 +23666,14 @@ void PEGTransformerFactory::InitializeTableUnpivotClauseTrampoline(PEGTransforme process.ReserveChildSlots(3); auto &table_alias_opt = list_pr.GetChild(3).Cast(); if (table_alias_opt.HasResult()) { - process.PushChild({transformer.GetRule("TableAlias"), table_alias_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::TableAlias), table_alias_opt.GetResult()}, 2); } - process.PushChild({transformer.GetRule("TableUnpivotClauseBody"), ExtractResultFromParens(list_pr.GetChild(2))}, 1); + process.PushChild( + {transformer.GetRule(PEGRuleId::TableUnpivotClauseBody), ExtractResultFromParens(list_pr.GetChild(2))}, 1); auto &include_or_exclude_nulls_opt = list_pr.GetChild(1).Cast(); if (include_or_exclude_nulls_opt.HasResult()) { - process.PushChild({transformer.GetRule("IncludeOrExcludeNulls"), include_or_exclude_nulls_opt.GetResult()}, 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::IncludeOrExcludeNulls), include_or_exclude_nulls_opt.GetResult()}, 0); } } @@ -23501,9 +23703,10 @@ void PEGTransformerFactory::InitializeTableUnpivotClauseBodyTrampoline(PEGTransf process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("UnpivotValueList"), repeat_children[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::UnpivotValueList), repeat_children[child_idx].get()}, + 1 + child_idx); } - process.PushChild({transformer.GetRule("UnpivotHeader"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::UnpivotHeader), list_pr.GetChild(0)}, 0); } arena_ptr @@ -23515,6 +23718,7 @@ PEGTransformerFactory::FinalizeTableUnpivotClauseBodyTrampoline(PEGTransformer & auto dynamic_child_count = dynamic_repeat_children.size(); auto unpivot_header = process.TakeResult>(0); vector unpivot_value_list; + unpivot_value_list.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { unpivot_value_list.push_back(process.TakeResult(i)); } @@ -23526,7 +23730,7 @@ void PEGTransformerFactory::InitializePivotHeaderTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("BaseExpression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BaseExpression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -23540,8 +23744,8 @@ void PEGTransformerFactory::InitializePivotValueListTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("PivotValueTarget"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("PivotHeader"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::PivotValueTarget), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::PivotHeader), list_pr.GetChild(0)}, 0); } arena_ptr @@ -23592,7 +23796,7 @@ void PEGTransformerFactory::InitializePivotListTargetTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("PivotTargetList"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::PivotTargetList), list_pr.GetChild(0)}, 0); } arena_ptr @@ -23607,8 +23811,8 @@ void PEGTransformerFactory::InitializeUnpivotValueListTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("UnpivotTargetList"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("UnpivotHeader"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::UnpivotTargetList), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::UnpivotHeader), list_pr.GetChild(0)}, 0); } arena_ptr @@ -23624,7 +23828,7 @@ void PEGTransformerFactory::InitializePivotTargetListTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("TargetList"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TargetList), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr @@ -23639,7 +23843,7 @@ void PEGTransformerFactory::InitializeUnpivotTargetListTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("TargetList"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TargetList), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr @@ -23721,7 +23925,7 @@ void PEGTransformerFactory::InitializeSchemaReservedTableTrampoline(PEGTransform GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SchemaQualification"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SchemaQualification), list_pr.GetChild(0)}, 0); } arena_ptr @@ -23743,10 +23947,11 @@ void PEGTransformerFactory::InitializeCatalogReservedSchemaTableTrampoline(PEGTr process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ReservedSchemaQualification"), repeat_children[child_idx].get()}, - 1 + child_idx); + process.PushChild( + {transformer.GetRule(PEGRuleId::ReservedSchemaQualification), repeat_children[child_idx].get()}, + 1 + child_idx); } - process.PushChild({transformer.GetRule("CatalogQualification"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CatalogQualification), list_pr.GetChild(0)}, 0); } arena_ptr @@ -23758,6 +23963,7 @@ PEGTransformerFactory::FinalizeCatalogReservedSchemaTableTrampoline(PEGTransform auto dynamic_child_count = dynamic_repeat_children.size(); auto catalog_qualification = process.TakeResult(0); vector reserved_schema_qualification; + reserved_schema_qualification.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { reserved_schema_qualification.push_back(process.TakeResult(i)); } @@ -23794,21 +24000,21 @@ void PEGTransformerFactory::InitializeTableFunctionLateralOptTrampoline(PEGTrans process.ReserveChildSlots(6); auto &sample_clause_opt = list_pr.GetChild(5).Cast(); if (sample_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("SampleClause"), sample_clause_opt.GetResult()}, 5); + process.PushChild({transformer.GetRule(PEGRuleId::SampleClause), sample_clause_opt.GetResult()}, 5); } auto &table_alias_opt = list_pr.GetChild(4).Cast(); if (table_alias_opt.HasResult()) { - process.PushChild({transformer.GetRule("TableAlias"), table_alias_opt.GetResult()}, 4); + process.PushChild({transformer.GetRule(PEGRuleId::TableAlias), table_alias_opt.GetResult()}, 4); } auto &with_ordinality_opt = list_pr.GetChild(3).Cast(); if (with_ordinality_opt.HasResult()) { - process.PushChild({transformer.GetRule("WithOrdinality"), with_ordinality_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::WithOrdinality), with_ordinality_opt.GetResult()}, 3); } - process.PushChild({transformer.GetRule("TableFunctionArguments"), list_pr.GetChild(2)}, 2); - process.PushChild({transformer.GetRule("QualifiedTableFunction"), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::TableFunctionArguments), list_pr.GetChild(2)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedTableFunction), list_pr.GetChild(1)}, 1); auto &lateral_opt = list_pr.GetChild(0).Cast(); if (lateral_opt.HasResult()) { - process.PushChild({transformer.GetRule("Lateral"), lateral_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Lateral), lateral_opt.GetResult()}, 0); } } @@ -23845,15 +24051,15 @@ void PEGTransformerFactory::InitializeTableFunctionAliasColonTrampoline(PEGTrans process.ReserveChildSlots(5); auto &sample_clause_opt = list_pr.GetChild(4).Cast(); if (sample_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("SampleClause"), sample_clause_opt.GetResult()}, 4); + process.PushChild({transformer.GetRule(PEGRuleId::SampleClause), sample_clause_opt.GetResult()}, 4); } auto &with_ordinality_opt = list_pr.GetChild(3).Cast(); if (with_ordinality_opt.HasResult()) { - process.PushChild({transformer.GetRule("WithOrdinality"), with_ordinality_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::WithOrdinality), with_ordinality_opt.GetResult()}, 3); } - process.PushChild({transformer.GetRule("TableFunctionArguments"), list_pr.GetChild(2)}, 2); - process.PushChild({transformer.GetRule("QualifiedTableFunction"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("TableAliasColon"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TableFunctionArguments), list_pr.GetChild(2)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedTableFunction), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::TableAliasColon), list_pr.GetChild(0)}, 0); } arena_ptr @@ -23900,7 +24106,7 @@ void PEGTransformerFactory::InitializeQualifiedTableFunctionTrampoline(PEGTransf process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("SchemaQualification"), repeat_children[child_idx].get()}, + process.PushChild({transformer.GetRule(PEGRuleId::SchemaQualification), repeat_children[child_idx].get()}, 1 + child_idx); } } else { @@ -23908,7 +24114,8 @@ void PEGTransformerFactory::InitializeQualifiedTableFunctionTrampoline(PEGTransf } auto &catalog_qualification_opt = list_pr.GetChild(0).Cast(); if (catalog_qualification_opt.HasResult()) { - process.PushChild({transformer.GetRule("CatalogQualification"), catalog_qualification_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CatalogQualification), catalog_qualification_opt.GetResult()}, + 0); } } @@ -23930,6 +24137,7 @@ PEGTransformerFactory::FinalizeQualifiedTableFunctionTrampoline(PEGTransformer & optional> schema_qualification {}; if (dynamic_child_count > 0) { vector schema_qualification_value; + schema_qualification_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { schema_qualification_value.push_back(process.TakeResult(i)); } @@ -23952,7 +24160,8 @@ void PEGTransformerFactory::InitializeTableFunctionArgumentsTrampoline(PEGTransf process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("FunctionArgument"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::FunctionArgument), list_items[child_idx].get()}, + 0 + child_idx); } } else { process.ReserveChildSlots(1 - 1); @@ -23973,6 +24182,7 @@ PEGTransformerFactory::FinalizeTableFunctionArgumentsTrampoline(PEGTransformer & auto &function_argument_opt = ExtractResultFromParens(list_pr.GetChild(0)).Cast(); if (function_argument_opt.HasResult()) { vector function_argument_value; + function_argument_value.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { function_argument_value.push_back(process.TakeResult(i)); } @@ -24007,8 +24217,8 @@ void PEGTransformerFactory::InitializeNamedFunctionArgumentTrampoline(PEGTransfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("TypeFuncName"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::TypeFuncName), list_pr.GetChild(0)}, 0); } arena_ptr @@ -24024,7 +24234,7 @@ void PEGTransformerFactory::InitializePositionalFunctionArgumentTrampoline(PEGTr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -24039,12 +24249,12 @@ void PEGTransformerFactory::InitializeNamedParameterTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(3); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(3)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(3)}, 2); auto &type_opt = list_pr.GetChild(1).Cast(); if (type_opt.HasResult()) { - process.PushChild({transformer.GetRule("Type"), type_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Type), type_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("TypeFuncName"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TypeFuncName), list_pr.GetChild(0)}, 0); } arena_ptr @@ -24086,9 +24296,9 @@ void PEGTransformerFactory::InitializeTableAliasAsTrampoline(PEGTransformer &tra process.ReserveChildSlots(2); auto &column_aliases_opt = list_pr.GetChild(2).Cast(); if (column_aliases_opt.HasResult()) { - process.PushChild({transformer.GetRule("ColumnAliases"), column_aliases_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ColumnAliases), column_aliases_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("IdentifierOrStringLiteral"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::IdentifierOrStringLiteral), list_pr.GetChild(1)}, 0); } arena_ptr @@ -24108,7 +24318,7 @@ void PEGTransformerFactory::InitializeTableAliasWithoutAsTrampoline(PEGTransform process.ReserveChildSlots(1); auto &column_aliases_opt = list_pr.GetChild(1).Cast(); if (column_aliases_opt.HasResult()) { - process.PushChild({transformer.GetRule("ColumnAliases"), column_aliases_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColumnAliases), column_aliases_opt.GetResult()}, 0); } } @@ -24129,7 +24339,7 @@ void PEGTransformerFactory::InitializeAtClauseTrampoline(PEGTransformer &transfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("AtSpecifier"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::AtSpecifier), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } arena_ptr PEGTransformerFactory::FinalizeAtClauseTrampoline(PEGTransformer &transformer, @@ -24143,8 +24353,8 @@ void PEGTransformerFactory::InitializeAtSpecifierTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("AtUnit"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::AtUnit), list_pr.GetChild(0)}, 0); } arena_ptr @@ -24265,16 +24475,16 @@ void PEGTransformerFactory::InitializeNearestJoinAliasedTrampoline(PEGTransforme GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(5); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(8)}, 4); - process.PushChild({transformer.GetRule("DistanceOrSimilarity"), list_pr.GetChild(7)}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(8)}, 4); + process.PushChild({transformer.GetRule(PEGRuleId::DistanceOrSimilarity), list_pr.GetChild(7)}, 3); auto &approx_or_exact_opt = list_pr.GetChild(3).Cast(); if (approx_or_exact_opt.HasResult()) { - process.PushChild({transformer.GetRule("ApproxOrExact"), approx_or_exact_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::ApproxOrExact), approx_or_exact_opt.GetResult()}, 2); } - process.PushChild({transformer.GetRule("TableRef"), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::TableRef), list_pr.GetChild(2)}, 1); auto &join_type_opt = list_pr.GetChild(0).Cast(); if (join_type_opt.HasResult()) { - process.PushChild({transformer.GetRule("JoinType"), join_type_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::JoinType), join_type_opt.GetResult()}, 0); } } @@ -24307,16 +24517,16 @@ void PEGTransformerFactory::InitializeNearestJoinBareTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(5); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(8)}, 4); - process.PushChild({transformer.GetRule("DistanceOrSimilarity"), list_pr.GetChild(7)}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(8)}, 4); + process.PushChild({transformer.GetRule(PEGRuleId::DistanceOrSimilarity), list_pr.GetChild(7)}, 3); auto &approx_or_exact_opt = list_pr.GetChild(3).Cast(); if (approx_or_exact_opt.HasResult()) { - process.PushChild({transformer.GetRule("ApproxOrExact"), approx_or_exact_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::ApproxOrExact), approx_or_exact_opt.GetResult()}, 2); } - process.PushChild({transformer.GetRule("NearestBareTableRef"), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::NearestBareTableRef), list_pr.GetChild(2)}, 1); auto &join_type_opt = list_pr.GetChild(0).Cast(); if (join_type_opt.HasResult()) { - process.PushChild({transformer.GetRule("JoinType"), join_type_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::JoinType), join_type_opt.GetResult()}, 0); } } @@ -24370,7 +24580,7 @@ void PEGTransformerFactory::InitializeNearestValuesRefTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ValuesClause"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ValuesClause), list_pr.GetChild(0)}, 0); } arena_ptr @@ -24387,13 +24597,13 @@ void PEGTransformerFactory::InitializeNearestTableFunctionTrampoline(PEGTransfor process.ReserveChildSlots(4); auto &with_ordinality_opt = list_pr.GetChild(3).Cast(); if (with_ordinality_opt.HasResult()) { - process.PushChild({transformer.GetRule("WithOrdinality"), with_ordinality_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::WithOrdinality), with_ordinality_opt.GetResult()}, 3); } - process.PushChild({transformer.GetRule("TableFunctionArguments"), list_pr.GetChild(2)}, 2); - process.PushChild({transformer.GetRule("QualifiedTableFunction"), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::TableFunctionArguments), list_pr.GetChild(2)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::QualifiedTableFunction), list_pr.GetChild(1)}, 1); auto &lateral_opt = list_pr.GetChild(0).Cast(); if (lateral_opt.HasResult()) { - process.PushChild({transformer.GetRule("Lateral"), lateral_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Lateral), lateral_opt.GetResult()}, 0); } } @@ -24419,10 +24629,10 @@ void PEGTransformerFactory::InitializeNearestTableSubqueryTrampoline(PEGTransfor GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("SubqueryReference"), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::SubqueryReference), list_pr.GetChild(1)}, 1); auto &lateral_opt = list_pr.GetChild(0).Cast(); if (lateral_opt.HasResult()) { - process.PushChild({transformer.GetRule("Lateral"), lateral_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Lateral), lateral_opt.GetResult()}, 0); } } @@ -24444,13 +24654,13 @@ void PEGTransformerFactory::InitializeNearestBaseTableRefTrampoline(PEGTransform process.ReserveChildSlots(3); auto &sample_clause_opt = list_pr.GetChild(2).Cast(); if (sample_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("SampleClause"), sample_clause_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::SampleClause), sample_clause_opt.GetResult()}, 2); } auto &at_clause_opt = list_pr.GetChild(1).Cast(); if (at_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("AtClause"), at_clause_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::AtClause), at_clause_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BaseTableName), list_pr.GetChild(0)}, 0); } arena_ptr @@ -24476,9 +24686,9 @@ void PEGTransformerFactory::InitializeNearestParensTableRefTrampoline(PEGTransfo process.ReserveChildSlots(2); auto &sample_clause_opt = list_pr.GetChild(1).Cast(); if (sample_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("SampleClause"), sample_clause_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::SampleClause), sample_clause_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("TableRef"), ExtractResultFromParens(list_pr.GetChild(0))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::TableRef), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr @@ -24586,15 +24796,15 @@ void PEGTransformerFactory::InitializeRegularJoinClauseTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(4); - process.PushChild({transformer.GetRule("JoinQualifier"), list_pr.GetChild(4)}, 3); - process.PushChild({transformer.GetRule("TableRef"), list_pr.GetChild(3)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::JoinQualifier), list_pr.GetChild(4)}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::TableRef), list_pr.GetChild(3)}, 2); auto &join_type_opt = list_pr.GetChild(1).Cast(); if (join_type_opt.HasResult()) { - process.PushChild({transformer.GetRule("JoinType"), join_type_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::JoinType), join_type_opt.GetResult()}, 1); } auto &asof_opt = list_pr.GetChild(0).Cast(); if (asof_opt.HasResult()) { - process.PushChild({transformer.GetRule("Asof"), asof_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Asof), asof_opt.GetResult()}, 0); } } @@ -24620,9 +24830,9 @@ void PEGTransformerFactory::InitializeJoinByClauseTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(3); - process.PushChild({transformer.GetRule("JoinQualifier"), list_pr.GetChild(4)}, 2); - process.PushChild({transformer.GetRule("TableRef"), list_pr.GetChild(3)}, 1); - process.PushChild({transformer.GetRule("ColLabel"), + process.PushChild({transformer.GetRule(PEGRuleId::JoinQualifier), list_pr.GetChild(4)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::TableRef), list_pr.GetChild(3)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ColLabel), ExtractResultFromParens(list_pr.GetChild(2)).Cast().GetChild(1)}, 0); } @@ -24650,8 +24860,8 @@ void PEGTransformerFactory::InitializeJoinWithoutOnClauseTrampoline(PEGTransform GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("InnerTableRef"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("JoinPrefix"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::InnerTableRef), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::JoinPrefix), list_pr.GetChild(0)}, 0); } arena_ptr @@ -24688,7 +24898,7 @@ void PEGTransformerFactory::InitializeOnClauseTrampoline(PEGTransformer &transfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(1)}, 0); } arena_ptr PEGTransformerFactory::FinalizeOnClauseTrampoline(PEGTransformer &transformer, @@ -24706,7 +24916,8 @@ void PEGTransformerFactory::InitializeUsingClauseTrampoline(PEGTransformer &tran process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("UsingColumnName"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::UsingColumnName), list_items[child_idx].get()}, + 0 + child_idx); } } @@ -24716,6 +24927,7 @@ PEGTransformerFactory::FinalizeUsingClauseTrampoline(PEGTransformer &transformer auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(1))); auto dynamic_child_count = dynamic_list_items.size(); vector using_column_name; + using_column_name.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { using_column_name.push_back(process.TakeResult(i)); } @@ -24821,7 +25033,7 @@ void PEGTransformerFactory::InitializeNaturalJoinPrefixTrampoline(PEGTransformer process.ReserveChildSlots(1); auto &join_type_opt = list_pr.GetChild(1).Cast(); if (join_type_opt.HasResult()) { - process.PushChild({transformer.GetRule("JoinType"), join_type_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::JoinType), join_type_opt.GetResult()}, 0); } } @@ -24934,7 +25146,7 @@ void PEGTransformerFactory::InitializeFromClauseTrampoline(PEGTransformer &trans process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("TableRef"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::TableRef), list_items[child_idx].get()}, 0 + child_idx); } } @@ -24944,6 +25156,7 @@ PEGTransformerFactory::FinalizeFromClauseTrampoline(PEGTransformer &transformer, auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(1)); auto dynamic_child_count = dynamic_list_items.size(); vector> table_ref; + table_ref.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { table_ref.push_back(process.TakeResult>(i)); } @@ -24955,7 +25168,7 @@ void PEGTransformerFactory::InitializeWhereClauseTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -24969,7 +25182,7 @@ void PEGTransformerFactory::InitializeGroupByClauseTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("GroupByExpressions"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::GroupByExpressions), list_pr.GetChild(2)}, 0); } arena_ptr @@ -24984,7 +25197,7 @@ void PEGTransformerFactory::InitializeHavingClauseTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -24998,7 +25211,7 @@ void PEGTransformerFactory::InitializeQualifyClauseTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(1)}, 0); } arena_ptr @@ -25013,7 +25226,7 @@ void PEGTransformerFactory::InitializeSampleClauseTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SampleEntry"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SampleEntry), list_pr.GetChild(1)}, 0); } arena_ptr @@ -25027,7 +25240,7 @@ void PEGTransformerFactory::InitializeSelectSampleClauseTrampoline(PEGTransforme GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SampleEntry"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SampleEntry), list_pr.GetChild(1)}, 0); } arena_ptr @@ -25046,7 +25259,8 @@ void PEGTransformerFactory::InitializeWindowClauseTrampoline(PEGTransformer &tra process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("WindowDefinition"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::WindowDefinition), list_items[child_idx].get()}, + 0 + child_idx); } } @@ -25056,6 +25270,7 @@ PEGTransformerFactory::FinalizeWindowClauseTrampoline(PEGTransformer &transforme auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(1)); auto dynamic_child_count = dynamic_list_items.size(); vector> window_definition; + window_definition.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { window_definition.push_back(process.TakeResult>(i)); } @@ -25089,10 +25304,11 @@ void PEGTransformerFactory::InitializeSampleEntryCountTrampoline(PEGTransformer process.ReserveChildSlots(2); auto &sample_properties_opt = list_pr.GetChild(1).Cast(); if (sample_properties_opt.HasResult()) { - process.PushChild( - {transformer.GetRule("SampleProperties"), ExtractResultFromParens(sample_properties_opt.GetResult())}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::SampleProperties), + ExtractResultFromParens(sample_properties_opt.GetResult())}, + 1); } - process.PushChild({transformer.GetRule("SampleCount"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SampleCount), list_pr.GetChild(0)}, 0); } arena_ptr @@ -25113,12 +25329,12 @@ void PEGTransformerFactory::InitializeSampleEntryFunctionTrampoline(PEGTransform process.ReserveChildSlots(3); auto &repeatable_sample_opt = list_pr.GetChild(2).Cast(); if (repeatable_sample_opt.HasResult()) { - process.PushChild({transformer.GetRule("RepeatableSample"), repeatable_sample_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::RepeatableSample), repeatable_sample_opt.GetResult()}, 2); } - process.PushChild({transformer.GetRule("SampleCount"), ExtractResultFromParens(list_pr.GetChild(1))}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::SampleCount), ExtractResultFromParens(list_pr.GetChild(1))}, 1); auto &sample_function_opt = list_pr.GetChild(0).Cast(); if (sample_function_opt.HasResult()) { - process.PushChild({transformer.GetRule("SampleFunction"), sample_function_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SampleFunction), sample_function_opt.GetResult()}, 0); } } @@ -25143,7 +25359,7 @@ void PEGTransformerFactory::InitializeSampleFunctionTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(0)}, 0); } arena_ptr @@ -25160,10 +25376,11 @@ void PEGTransformerFactory::InitializeSamplePropertiesTrampoline(PEGTransformer process.ReserveChildSlots(2); auto &sample_seed_opt = list_pr.GetChild(1).Cast(); if (sample_seed_opt.HasResult()) { - process.PushChild( - {transformer.GetRule("SampleSeed"), sample_seed_opt.GetResult().Cast().GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::SampleSeed), + sample_seed_opt.GetResult().Cast().GetChild(1)}, + 1); } - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(0)}, 0); } arena_ptr @@ -25182,7 +25399,7 @@ void PEGTransformerFactory::InitializeRepeatableSampleTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SampleSeed"), ExtractResultFromParens(list_pr.GetChild(1))}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SampleSeed), ExtractResultFromParens(list_pr.GetChild(1))}, 0); } arena_ptr @@ -25212,9 +25429,9 @@ void PEGTransformerFactory::InitializeSampleCountTrampoline(PEGTransformer &tran process.ReserveChildSlots(2); auto &sample_unit_opt = list_pr.GetChild(1).Cast(); if (sample_unit_opt.HasResult()) { - process.PushChild({transformer.GetRule("SampleUnit"), sample_unit_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::SampleUnit), sample_unit_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("SampleValue"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SampleValue), list_pr.GetChild(0)}, 0); } arena_ptr @@ -25331,7 +25548,8 @@ void PEGTransformerFactory::InitializeGroupByListTrampoline(PEGTransformer &tran process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("GroupByExpression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::GroupByExpression), list_items[child_idx].get()}, + 0 + child_idx); } } @@ -25341,6 +25559,7 @@ PEGTransformerFactory::FinalizeGroupByListTrampoline(PEGTransformer &transformer auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); auto dynamic_child_count = dynamic_list_items.size(); vector group_by_expression; + group_by_expression.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { group_by_expression.push_back(process.TakeResult(i)); } @@ -25373,7 +25592,7 @@ void PEGTransformerFactory::InitializeGroupByBaseExpressionTrampoline(PEGTransfo GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -25407,12 +25626,12 @@ void PEGTransformerFactory::InitializeCubeOrRollupClauseTrampoline(PEGTransforme process.ReserveChildSlots(2 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 1 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 1 + child_idx); } } else { process.ReserveChildSlots(2 - 1); } - process.PushChild({transformer.GetRule("CubeOrRollup"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::CubeOrRollup), list_pr.GetChild(0)}, 0); } arena_ptr @@ -25430,6 +25649,7 @@ PEGTransformerFactory::FinalizeCubeOrRollupClauseTrampoline(PEGTransformer &tran auto &expression_opt = ExtractResultFromParens(list_pr.GetChild(1)).Cast(); if (expression_opt.HasResult()) { vector> expression_value; + expression_value.reserve(dynamic_child_count); for (idx_t i = 1; i < 1 + dynamic_child_count; i++) { expression_value.push_back(process.TakeResult>(i)); } @@ -25511,7 +25731,8 @@ void PEGTransformerFactory::InitializeGroupingSetsClauseTrampoline(PEGTransforme process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("GroupByExpression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::GroupByExpression), list_items[child_idx].get()}, + 0 + child_idx); } } @@ -25522,6 +25743,7 @@ PEGTransformerFactory::FinalizeGroupingSetsClauseTrampoline(PEGTransformer &tran auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(2))); auto dynamic_child_count = dynamic_list_items.size(); vector group_by_expression; + group_by_expression.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { group_by_expression.push_back(process.TakeResult(i)); } @@ -25533,8 +25755,8 @@ void PEGTransformerFactory::InitializeSubqueryReferenceTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SelectStatementInternal"), ExtractResultFromParens(list_pr.GetChild(0))}, - 0); + process.PushChild( + {transformer.GetRule(PEGRuleId::SelectStatementInternal), ExtractResultFromParens(list_pr.GetChild(0))}, 0); } arena_ptr @@ -25551,13 +25773,13 @@ void PEGTransformerFactory::InitializeOrderByExpressionTrampoline(PEGTransformer process.ReserveChildSlots(3); auto &nulls_first_or_last_opt = list_pr.GetChild(2).Cast(); if (nulls_first_or_last_opt.HasResult()) { - process.PushChild({transformer.GetRule("NullsFirstOrLast"), nulls_first_or_last_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::NullsFirstOrLast), nulls_first_or_last_opt.GetResult()}, 2); } auto &desc_or_asc_opt = list_pr.GetChild(1).Cast(); if (desc_or_asc_opt.HasResult()) { - process.PushChild({transformer.GetRule("DescOrAsc"), desc_or_asc_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::DescOrAsc), desc_or_asc_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -25667,7 +25889,7 @@ void PEGTransformerFactory::InitializeOrderByClauseTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("OrderByExpressions"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OrderByExpressions), list_pr.GetChild(2)}, 0); } arena_ptr @@ -25707,7 +25929,8 @@ void PEGTransformerFactory::InitializeOrderByExpressionListTrampoline(PEGTransfo process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("OrderByExpression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::OrderByExpression), list_items[child_idx].get()}, + 0 + child_idx); } } @@ -25718,6 +25941,7 @@ PEGTransformerFactory::FinalizeOrderByExpressionListTrampoline(PEGTransformer &t auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); auto dynamic_child_count = dynamic_list_items.size(); vector order_by_expression; + order_by_expression.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { order_by_expression.push_back(process.TakeResult(i)); } @@ -25731,11 +25955,11 @@ void PEGTransformerFactory::InitializeOrderByAllTrampoline(PEGTransformer &trans process.ReserveChildSlots(2); auto &nulls_first_or_last_opt = list_pr.GetChild(2).Cast(); if (nulls_first_or_last_opt.HasResult()) { - process.PushChild({transformer.GetRule("NullsFirstOrLast"), nulls_first_or_last_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::NullsFirstOrLast), nulls_first_or_last_opt.GetResult()}, 1); } auto &desc_or_asc_opt = list_pr.GetChild(1).Cast(); if (desc_or_asc_opt.HasResult()) { - process.PushChild({transformer.GetRule("DescOrAsc"), desc_or_asc_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::DescOrAsc), desc_or_asc_opt.GetResult()}, 0); } } @@ -25757,7 +25981,7 @@ void PEGTransformerFactory::InitializeLimitClauseTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("LimitValue"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::LimitValue), list_pr.GetChild(1)}, 0); } arena_ptr @@ -25771,7 +25995,7 @@ void PEGTransformerFactory::InitializeOffsetClauseTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("OffsetValue"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OffsetValue), list_pr.GetChild(1)}, 0); } arena_ptr @@ -25785,7 +26009,7 @@ void PEGTransformerFactory::InitializeOffsetValueTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -25848,7 +26072,7 @@ void PEGTransformerFactory::InitializeLimitExpressionTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -25899,7 +26123,7 @@ void PEGTransformerFactory::InitializeFetchClauseWithValueTrampoline(PEGTransfor GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("FetchValue"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::FetchValue), list_pr.GetChild(2)}, 0); } arena_ptr @@ -25913,7 +26137,7 @@ void PEGTransformerFactory::InitializeFetchValueTrampoline(PEGTransformer &trans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -25948,8 +26172,8 @@ void PEGTransformerFactory::InitializeColIdExpressionTrampoline(PEGTransformer & GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(0)}, 0); } arena_ptr @@ -25965,8 +26189,8 @@ void PEGTransformerFactory::InitializeExpressionAsCollabelTrampoline(PEGTransfor GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("ColLabelOrString"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColLabelOrString), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -25982,7 +26206,7 @@ void PEGTransformerFactory::InitializeExpressionOptIdentifierTrampoline(PEGTrans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(0)}, 0); } arena_ptr @@ -26007,7 +26231,8 @@ void PEGTransformerFactory::InitializeValuesClauseTrampoline(PEGTransformer &tra process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ValuesExpressions"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ValuesExpressions), list_items[child_idx].get()}, + 0 + child_idx); } } @@ -26017,6 +26242,7 @@ PEGTransformerFactory::FinalizeValuesClauseTrampoline(PEGTransformer &transforme auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(1)); auto dynamic_child_count = dynamic_list_items.size(); vector>> values_expressions; + values_expressions.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { values_expressions.push_back(process.TakeResult>>(i)); } @@ -26032,7 +26258,7 @@ void PEGTransformerFactory::InitializeValuesExpressionsTrampoline(PEGTransformer process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 0 + child_idx); } } @@ -26043,6 +26269,7 @@ PEGTransformerFactory::FinalizeValuesExpressionsTrampoline(PEGTransformer &trans auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); vector> expression; + expression.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { expression.push_back(process.TakeResult>(i)); } @@ -26054,7 +26281,7 @@ void PEGTransformerFactory::InitializeSetStatementTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SetAssignmentOrTimeZone"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SetAssignmentOrTimeZone), list_pr.GetChild(1)}, 0); } arena_ptr @@ -26089,7 +26316,7 @@ void PEGTransformerFactory::InitializeResetStatementTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("SetVariableOrSetting"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SetVariableOrSetting), list_pr.GetChild(1)}, 0); } arena_ptr @@ -26117,8 +26344,8 @@ void PEGTransformerFactory::InitializeStandardAssignmentTrampoline(PEGTransforme GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("SetAssignment"), list_pr.GetChild(1)}, 1); - process.PushChild({transformer.GetRule("SetVariableOrSetting"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SetAssignment), list_pr.GetChild(1)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::SetVariableOrSetting), list_pr.GetChild(0)}, 0); } arena_ptr @@ -26155,7 +26382,7 @@ void PEGTransformerFactory::InitializeSetTimeZoneTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ZoneValue"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ZoneValue), list_pr.GetChild(2)}, 0); } arena_ptr @@ -26241,7 +26468,7 @@ void PEGTransformerFactory::InitializeZoneIntervalWithIntervalTrampoline(PEGTran process.ReserveChildSlots(1); auto &interval_opt = list_pr.GetChild(2).Cast(); if (interval_opt.HasResult()) { - process.PushChild({transformer.GetRule("Interval"), interval_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Interval), interval_opt.GetResult()}, 0); } } @@ -26279,7 +26506,7 @@ void PEGTransformerFactory::InitializeSetSettingTrampoline(PEGTransformer &trans process.ReserveChildSlots(1); auto &setting_scope_opt = list_pr.GetChild(0).Cast(); if (setting_scope_opt.HasResult()) { - process.PushChild({transformer.GetRule("SettingScope"), setting_scope_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::SettingScope), setting_scope_opt.GetResult()}, 0); } } @@ -26299,7 +26526,7 @@ void PEGTransformerFactory::InitializeSetVariableTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("VariableScope"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::VariableScope), list_pr.GetChild(0)}, 0); } arena_ptr @@ -26380,7 +26607,7 @@ void PEGTransformerFactory::InitializeSetAssignmentTrampoline(PEGTransformer &tr GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("VariableList"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::VariableList), list_pr.GetChild(1)}, 0); } arena_ptr @@ -26399,7 +26626,7 @@ void PEGTransformerFactory::InitializeVariableListTrampoline(PEGTransformer &tra process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("Expression"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_items[child_idx].get()}, 0 + child_idx); } } @@ -26409,6 +26636,7 @@ PEGTransformerFactory::FinalizeVariableListTrampoline(PEGTransformer &transforme auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); auto dynamic_child_count = dynamic_list_items.size(); vector> expression; + expression.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { expression.push_back(process.TakeResult>(i)); } @@ -26443,7 +26671,7 @@ void PEGTransformerFactory::InitializeBeginTransactionTrampoline(PEGTransformer process.ReserveChildSlots(1); auto &read_or_write_opt = list_pr.GetChild(2).Cast(); if (read_or_write_opt.HasResult()) { - process.PushChild({transformer.GetRule("ReadOrWrite"), read_or_write_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ReadOrWrite), read_or_write_opt.GetResult()}, 0); } } @@ -26498,7 +26726,7 @@ void PEGTransformerFactory::InitializeReadOrWriteTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ReadOnlyOrReadWrite"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ReadOnlyOrReadWrite), list_pr.GetChild(1)}, 0); } arena_ptr @@ -26557,21 +26785,21 @@ void PEGTransformerFactory::InitializeUpdateStatementTrampoline(PEGTransformer & process.ReserveChildSlots(6); auto &returning_clause_opt = list_pr.GetChild(6).Cast(); if (returning_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("ReturningClause"), returning_clause_opt.GetResult()}, 5); + process.PushChild({transformer.GetRule(PEGRuleId::ReturningClause), returning_clause_opt.GetResult()}, 5); } auto &where_clause_opt = list_pr.GetChild(5).Cast(); if (where_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("WhereClause"), where_clause_opt.GetResult()}, 4); + process.PushChild({transformer.GetRule(PEGRuleId::WhereClause), where_clause_opt.GetResult()}, 4); } auto &from_clause_opt = list_pr.GetChild(4).Cast(); if (from_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("FromClause"), from_clause_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::FromClause), from_clause_opt.GetResult()}, 3); } - process.PushChild({transformer.GetRule("UpdateSetClauseList"), list_pr.GetChild(3)}, 2); - process.PushChild({transformer.GetRule("UpdateTarget"), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::UpdateSetClauseList), list_pr.GetChild(3)}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::UpdateTarget), list_pr.GetChild(2)}, 1); auto &with_clause_opt = list_pr.GetChild(0).Cast(); if (with_clause_opt.HasResult()) { - process.PushChild({transformer.GetRule("WithClause"), with_clause_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::WithClause), with_clause_opt.GetResult()}, 0); } } @@ -26626,7 +26854,7 @@ void PEGTransformerFactory::InitializeBaseTableSetTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BaseTableName), list_pr.GetChild(0)}, 0); } arena_ptr @@ -26642,9 +26870,9 @@ void PEGTransformerFactory::InitializeBaseTableAliasSetTrampoline(PEGTransformer process.ReserveChildSlots(2); auto &update_alias_opt = list_pr.GetChild(1).Cast(); if (update_alias_opt.HasResult()) { - process.PushChild({transformer.GetRule("UpdateAlias"), update_alias_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::UpdateAlias), update_alias_opt.GetResult()}, 1); } - process.PushChild({transformer.GetRule("BaseTableName"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::BaseTableName), list_pr.GetChild(0)}, 0); } arena_ptr @@ -26663,7 +26891,7 @@ void PEGTransformerFactory::InitializeUpdateAliasTrampoline(PEGTransformer &tran GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("ColId"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::ColId), list_pr.GetChild(1)}, 0); } arena_ptr @@ -26685,7 +26913,8 @@ void PEGTransformerFactory::InitializeUpdateSetClauseListTrampoline(PEGTransform process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("UpdateSetClause"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::UpdateSetClause), list_items[child_idx].get()}, + 0 + child_idx); } } @@ -26696,6 +26925,7 @@ PEGTransformerFactory::FinalizeUpdateSetClauseListTrampoline(PEGTransformer &tra auto dynamic_list_items = ExtractParseResultsFromList(list_pr.GetChild(0)); auto dynamic_child_count = dynamic_list_items.size(); vector> update_set_clause; + update_set_clause.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { update_set_clause.push_back(process.TakeResult>(i)); } @@ -26728,7 +26958,7 @@ void PEGTransformerFactory::InitializeUpdateSetTupleTrampoline(PEGTransformer &t GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(2)}, 0); } arena_ptr @@ -26737,6 +26967,7 @@ PEGTransformerFactory::FinalizeUpdateSetTupleTrampoline(PEGTransformer &transfor auto &list_pr = process.parse_result.Cast(); vector column_name; auto column_name_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); + column_name.reserve(column_name_items.size()); for (auto &column_name_item : column_name_items) { column_name.push_back(column_name_item.get().Cast().identifier); } @@ -26749,8 +26980,8 @@ void PEGTransformerFactory::InitializeUpdateSetElementTrampoline(PEGTransformer GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(2); - process.PushChild({transformer.GetRule("Expression"), list_pr.GetChild(2)}, 1); - process.PushChild({transformer.GetRule("UpdateSetColumnTarget"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::Expression), list_pr.GetChild(2)}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::UpdateSetColumnTarget), list_pr.GetChild(0)}, 0); } arena_ptr @@ -26774,7 +27005,8 @@ void PEGTransformerFactory::InitializeUpdateSetColumnTargetTrampoline(PEGTransfo process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("DotIdentifier"), repeat_children[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::DotIdentifier), repeat_children[child_idx].get()}, + 0 + child_idx); } } else { process.ReserveChildSlots(1 - 1); @@ -26796,6 +27028,7 @@ PEGTransformerFactory::FinalizeUpdateSetColumnTargetTrampoline(PEGTransformer &t optional> dot_identifier {}; if (dynamic_child_count > 0) { vector dot_identifier_value; + dot_identifier_value.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { dot_identifier_value.push_back(process.TakeResult(i)); } @@ -26809,7 +27042,7 @@ void PEGTransformerFactory::InitializeUseStatementTrampoline(PEGTransformer &tra GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("UseTarget"), list_pr.GetChild(1)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::UseTarget), list_pr.GetChild(1)}, 0); } arena_ptr @@ -26941,7 +27174,8 @@ void PEGTransformerFactory::InitializeUseTargetCatalogSchemaTrampoline(PEGTransf process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = repeat_children.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("DotIdentifier"), repeat_children[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::DotIdentifier), repeat_children[child_idx].get()}, + 0 + child_idx); } } else { process.ReserveChildSlots(1 - 1); @@ -26964,6 +27198,7 @@ PEGTransformerFactory::FinalizeUseTargetCatalogSchemaTrampoline(PEGTransformer & optional> dot_identifier {}; if (dynamic_child_count > 0) { vector dot_identifier_value; + dot_identifier_value.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { dot_identifier_value.push_back(process.TakeResult(i)); } @@ -26993,11 +27228,11 @@ void PEGTransformerFactory::InitializeVacuumStatementTrampoline(PEGTransformer & process.ReserveChildSlots(2); auto &analyze_target_opt = list_pr.GetChild(2).Cast(); if (analyze_target_opt.HasResult()) { - process.PushChild({transformer.GetRule("AnalyzeTarget"), analyze_target_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::AnalyzeTarget), analyze_target_opt.GetResult()}, 1); } auto &vacuum_options_opt = list_pr.GetChild(1).Cast(); if (vacuum_options_opt.HasResult()) { - process.PushChild({transformer.GetRule("VacuumOptions"), vacuum_options_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::VacuumOptions), vacuum_options_opt.GetResult()}, 0); } } @@ -27045,7 +27280,7 @@ void PEGTransformerFactory::InitializeVacuumParensOptionsTrampoline(PEGTransform process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("VacuumOption"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::VacuumOption), list_items[child_idx].get()}, 0 + child_idx); } } @@ -27056,6 +27291,7 @@ PEGTransformerFactory::FinalizeVacuumParensOptionsTrampoline(PEGTransformer &tra auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); vector vacuum_option; + vacuum_option.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { vacuum_option.push_back(process.TakeResult(i)); } @@ -27069,19 +27305,19 @@ void PEGTransformerFactory::InitializeVacuumLegacyOptionsTrampoline(PEGTransform process.ReserveChildSlots(4); auto &opt_analyze_opt = list_pr.GetChild(3).Cast(); if (opt_analyze_opt.HasResult()) { - process.PushChild({transformer.GetRule("OptAnalyze"), opt_analyze_opt.GetResult()}, 3); + process.PushChild({transformer.GetRule(PEGRuleId::OptAnalyze), opt_analyze_opt.GetResult()}, 3); } auto &opt_verbose_opt = list_pr.GetChild(2).Cast(); if (opt_verbose_opt.HasResult()) { - process.PushChild({transformer.GetRule("OptVerbose"), opt_verbose_opt.GetResult()}, 2); + process.PushChild({transformer.GetRule(PEGRuleId::OptVerbose), opt_verbose_opt.GetResult()}, 2); } auto &opt_freeze_opt = list_pr.GetChild(1).Cast(); if (opt_freeze_opt.HasResult()) { - process.PushChild({transformer.GetRule("OptFreeze"), opt_freeze_opt.GetResult()}, 1); + process.PushChild({transformer.GetRule(PEGRuleId::OptFreeze), opt_freeze_opt.GetResult()}, 1); } auto &opt_full_opt = list_pr.GetChild(0).Cast(); if (opt_full_opt.HasResult()) { - process.PushChild({transformer.GetRule("OptFull"), opt_full_opt.GetResult()}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::OptFull), opt_full_opt.GetResult()}, 0); } } @@ -27153,7 +27389,7 @@ void PEGTransformerFactory::InitializeOptAnalyzeTrampoline(PEGTransformer &trans GeneratedTransformProcess &process) { auto &list_pr = process.parse_result.Cast(); process.ReserveChildSlots(1); - process.PushChild({transformer.GetRule("AnalyzeKeyword"), list_pr.GetChild(0)}, 0); + process.PushChild({transformer.GetRule(PEGRuleId::AnalyzeKeyword), list_pr.GetChild(0)}, 0); } arena_ptr @@ -27204,7 +27440,7 @@ void PEGTransformerFactory::InitializeNameListTrampoline(PEGTransformer &transfo process.ReserveChildSlots(1 + dynamic_child_count - 1); for (idx_t i = list_items.size(); i > 0; i--) { auto child_idx = i - 1; - process.PushChild({transformer.GetRule("ColIdOrString"), list_items[child_idx].get()}, 0 + child_idx); + process.PushChild({transformer.GetRule(PEGRuleId::ColIdOrString), list_items[child_idx].get()}, 0 + child_idx); } } @@ -27214,6 +27450,7 @@ arena_ptr PEGTransformerFactory::FinalizeNameListTrampolin auto dynamic_list_items = ExtractParseResultsFromList(ExtractResultFromParens(list_pr.GetChild(0))); auto dynamic_child_count = dynamic_list_items.size(); vector col_id_or_string; + col_id_or_string.reserve(dynamic_child_count); for (idx_t i = 0; i < 0 + dynamic_child_count; i++) { col_id_or_string.push_back(process.TakeResult(i)); } diff --git a/src/duckdb/src/parser/peg/transformer/transform_generic_copy_option.cpp b/src/duckdb/src/parser/peg/transformer/transform_generic_copy_option.cpp index 74a0b8c0e..e26ec37cb 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_generic_copy_option.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_generic_copy_option.cpp @@ -197,7 +197,7 @@ void PEGTransformerFactory::SplitGenericOptions(const vector options[option.name.GetIdentifierName()] = Value(true); } else if (option.children.size() == 1) { if (option.children[0].IsNull()) { - throw BinderException("NULL is not supported as a valid option for %s option \"%s\"", statement_name, + throw BinderException("NULL is not supported as a valid option for %s option %s", statement_name, option.name); } options[option.name.GetIdentifierName()] = option.children[0]; diff --git a/src/duckdb/src/parser/peg/transformer/transform_select.cpp b/src/duckdb/src/parser/peg/transformer/transform_select.cpp index eb38b7a23..4c25d295a 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_select.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_select.cpp @@ -1711,7 +1711,13 @@ optional_idx PEGTransformerFactory::TransformRepeatableSample(PEGTransformer &tr optional_idx PEGTransformerFactory::TransformSampleSeed(PEGTransformer &transformer, unique_ptr number_literal) { auto &const_expr = number_literal->Cast(); - return optional_idx(const_expr.GetLiteral().ToValue().GetValue()); + auto seed = const_expr.GetLiteral().ToValue().GetValue(); + if (seed > static_cast(NumericLimits::Maximum())) { + // larger seeds are seen as "no seed" and give a different sample every time + throw ParserException("REPEATABLE seed %llu is out of range, the maximum is %lld", seed, + NumericLimits::Maximum()); + } + return optional_idx(seed); } unique_ptr PEGTransformerFactory::TransformSampleCount(PEGTransformer &transformer, diff --git a/src/duckdb/src/parser/statement/export_statement.cpp b/src/duckdb/src/parser/statement/export_statement.cpp index decef215a..719394d9a 100644 --- a/src/duckdb/src/parser/statement/export_statement.cpp +++ b/src/duckdb/src/parser/statement/export_statement.cpp @@ -1,5 +1,6 @@ #include "duckdb/parser/statement/export_statement.hpp" #include "duckdb/parser/parsed_data/copy_info.hpp" +#include "duckdb/common/sql_identifier.hpp" namespace duckdb { @@ -19,11 +20,11 @@ string ExportStatement::ToString() const { string result = ""; result += "EXPORT DATABASE"; if (!database.empty()) { - result += " " + database + " TO"; + result += " " + SQLIdentifier(database) + " TO"; } auto &path = info->file_path; D_ASSERT(info->is_from == false); - result += StringUtil::Format(" '%s'", path); + result += " " + SQLString(path); result += info->CopyOptionsToString(); result += ";"; return result; diff --git a/src/duckdb/src/planner/bind_context.cpp b/src/duckdb/src/planner/bind_context.cpp index bf8423944..96e0823c8 100644 --- a/src/duckdb/src/planner/bind_context.cpp +++ b/src/duckdb/src/planner/bind_context.cpp @@ -181,7 +181,7 @@ string BindContext::GetActualColumnName(const BindingAlias &binding_alias, const ErrorData error; auto binding = GetBinding(binding_alias, error); if (!binding) { - throw InternalException("No binding with name \"%s\": %s", binding_alias.GetAlias(), error.RawMessage()); + throw InternalException("No binding with name %s: %s", binding_alias.GetAlias(), error.RawMessage()); } return GetActualColumnName(*binding, column_name); } @@ -455,7 +455,7 @@ BindingAlias GetBindingAlias(ColumnRefExpression &colref) { BindResult BindContext::BindColumn(ColumnRefExpression &colref, idx_t depth) { if (!colref.IsQualified()) { - throw InternalException("Could not bind alias \"%s\"!", colref.GetColumnName()); + throw InternalException("Could not bind alias %s!", colref.GetColumnName()); } ErrorData error; diff --git a/src/duckdb/src/planner/binder.cpp b/src/duckdb/src/planner/binder.cpp index 1eb85f78b..335aa9aec 100644 --- a/src/duckdb/src/planner/binder.cpp +++ b/src/duckdb/src/planner/binder.cpp @@ -228,7 +228,7 @@ void Binder::AddBoundView(ViewCatalogEntry &view) { auto current = this; while (current) { if (current->bound_views.find(view) != current->bound_views.end()) { - throw BinderException("infinite recursion detected: attempting to recursively bind view \"%s\"", view.name); + throw BinderException("infinite recursion detected: attempting to recursively bind view %s", view.name); } current = current->parent.get(); } diff --git a/src/duckdb/src/planner/binder/expression/bind_aggregate_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_aggregate_expression.cpp index 89bf6a80b..e73429c7d 100644 --- a/src/duckdb/src/planner/binder/expression/bind_aggregate_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_aggregate_expression.cpp @@ -301,7 +301,7 @@ BindResult BaseSelectBinder::BindAggregate(FunctionExpression &aggr, AggregateFu // If the function cannot be used as an aggregate, but can be used as a window function, throw a specific error // message if (!aggregate->Function().CanAggregate() && aggregate->Function().CanWindow()) { - auto msg = StringUtil::Format("Function '%s' can only be used as a window function", func.name); + auto msg = StringUtil::Format("Function %s can only be used as a window function", func.name); error = BinderException(msg); error.AddQueryLocation(aggr); error.Throw(); diff --git a/src/duckdb/src/planner/binder/expression/bind_between_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_between_expression.cpp index 3b5ca459b..8cb1adfa9 100644 --- a/src/duckdb/src/planner/binder/expression/bind_between_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_between_expression.cpp @@ -47,8 +47,8 @@ BindResult ExpressionBinder::BindExpression(BetweenExpression &expr, idx_t depth PushCollation(context, lower, input_type); PushCollation(context, upper, input_type); - if (!input->IsVolatile() && !input->HasParameter() && !input->HasSubquery()) { - // the expression does not have side effects and can be copied: create two comparisons + if (input->GetExpressionType() == ExpressionType::BOUND_COLUMN_REF) { + // the input is a column reference and can be copied cheaply: create two comparisons // the reason we do this is that individual comparisons are easier to handle in optimizers // if both comparisons remain they will be folded together again into a single BETWEEN in the optimizer auto left_compare = BoundComparisonExpression::Create(ExpressionType::COMPARE_GREATERTHANOREQUALTO, diff --git a/src/duckdb/src/planner/binder/expression/bind_collate_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_collate_expression.cpp index cbc305ac0..2d63fb03c 100644 --- a/src/duckdb/src/planner/binder/expression/bind_collate_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_collate_expression.cpp @@ -15,7 +15,7 @@ BindResult ExpressionBinder::BindExpression(CollateExpression &expr, idx_t depth if (child->HasParameter()) { throw ParameterNotResolvedException(); } - if (child->GetReturnType().id() != LogicalTypeId::VARCHAR) { + if (child->GetReturnType().id() != LogicalTypeId::VARCHAR || child->GetReturnType().IsJSONType()) { throw BinderException(child->GetQueryLocation(), "collations are only supported for type varchar"); } // Validate the collation, but don't use it diff --git a/src/duckdb/src/planner/binder/expression/bind_function_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_function_expression.cpp index 7b176d21e..79e937403 100644 --- a/src/duckdb/src/planner/binder/expression/bind_function_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_function_expression.cpp @@ -377,6 +377,8 @@ BindResult ExpressionBinder::BindFunction(FunctionExpression &function, ScalarFu // The positional/named split (and, for capturing functions, the alias capture) is resolved later per candidate // overload. vector>> arguments; + // for legacy calls with names: fallback arguments that keep the names, used if binding positionally fails + vector>> named_arguments; arguments.reserve(function.GetArguments().size()); for (idx_t i = 0; i < function.GetArgumentsMutable().size(); i++) { auto &arg = function.GetArgumentsMutable()[i]; @@ -391,6 +393,13 @@ BindResult ExpressionBinder::BindFunction(FunctionExpression &function, ScalarFu bound_arg->SetAlias(arg.GetName()); } if (function.IsLegacyFunctionCall()) { + if (!named_arguments.empty() || !arg.GetName().empty()) { + // first named argument: copy the unnamed arguments bound so far + for (idx_t prev = named_arguments.size(); prev < arguments.size(); prev++) { + named_arguments.emplace_back(Identifier(), arguments[prev].second->Copy()); + } + named_arguments.emplace_back(arg.GetName(), bound_arg->Copy()); + } arguments.emplace_back(string(), std::move(bound_arg)); } else { arguments.emplace_back(arg.GetName(), std::move(bound_arg)); @@ -399,6 +408,12 @@ BindResult ExpressionBinder::BindFunction(FunctionExpression &function, ScalarFu FunctionBinder function_binder(binder); auto result = function_binder.BindScalarFunction(func, std::move(arguments), error, function.IsOperator(), &binder); + if (!result && !named_arguments.empty()) { + // a legacy call that does not bind positionally may use names the function declares (e.g. **kwargs) + ErrorData named_error; + result = function_binder.BindScalarFunction(func, std::move(named_arguments), named_error, + function.IsOperator(), &binder); + } if (!result) { error.AddQueryLocation(function); error.Throw(); diff --git a/src/duckdb/src/planner/binder/expression/bind_star_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_star_expression.cpp index f9e1bba28..78b6f1e9a 100644 --- a/src/duckdb/src/planner/binder/expression/bind_star_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_star_expression.cpp @@ -215,7 +215,7 @@ void TryTransformStarLike(unique_ptr &root) { "like_escape"}; if (supported_ops.count(function.FunctionName().GetIdentifierName()) == 0) { // unsupported op for * expression - throw BinderException(*root, "Function \"%s\" cannot be applied to a star expression", function.FunctionName()); + throw BinderException(*root, "Function %s cannot be applied to a star expression", function.FunctionName()); } auto &right = function.GetArgumentsMutable()[1]; if (right.GetExpression().GetExpressionClass() != ExpressionClass::CONSTANT) { diff --git a/src/duckdb/src/planner/binder/expression/bind_type_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_type_expression.cpp index 12c63762b..87a1b0d36 100644 --- a/src/duckdb/src/planner/binder/expression/bind_type_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_type_expression.cpp @@ -79,14 +79,14 @@ BindResult ExpressionBinder::BindExpression(TypeExpression &type_expr, idx_t dep for (auto ¶m : unbound_parameters) { // Otherwise, try to fold it to a constant value - ConstantBinder binder(this->binder, context, StringUtil::Format("Type parameter for type '%s'", type_name)); + ConstantBinder binder(this->binder, context, StringUtil::Format("Type parameter for type %s", type_name)); binder.ContinueStackDepth(*this); auto expr = param->Copy(); auto bound_expr = binder.Bind(expr); if (!bound_expr->IsFoldable()) { - throw BinderException(type_expr, "Type parameter expression for type '%s' is not a constant", type_name); + throw BinderException(type_expr, "Type parameter expression for type %s is not a constant", type_name); } // The location comes from the parsed parameter, not the bound one. diff --git a/src/duckdb/src/planner/binder/expression/bind_window_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_window_expression.cpp index cc6b5f824..9c5e90150 100644 --- a/src/duckdb/src/planner/binder/expression/bind_window_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_window_expression.cpp @@ -304,20 +304,20 @@ BindResult BaseSelectBinder::BindWindowExpression(WindowExpression &window, idx_ // TODO: Move this into the binder auto &bound_function = *window_bound_function->WindowFunction(); if (window.Distinct() && !bound_function.CanDistinct()) { - throw BinderException(error_context, "DISTINCT is not implemented for the window function \"%s\"", + throw BinderException(error_context, "DISTINCT is not implemented for the window function %s", window.FunctionName()); } if (window.Filter() && !bound_function.CanFilter()) { - throw BinderException(error_context, "FILTER is not implemented for the window function \"%s\"", + throw BinderException(error_context, "FILTER is not implemented for the window function %s", window.FunctionName()); } if (!window.ArgOrders().empty() && !bound_function.CanOrderBy()) { - throw BinderException(error_context, "ORDER BY is not supported for the window function \"%s\"", + throw BinderException(error_context, "ORDER BY is not supported for the window function %s", window.FunctionName()); } if (window.WindowExclude() != WindowExcludeMode::NO_OTHER && !window.ArgOrders().empty() && !bound_function.CanExclude()) { - throw BinderException(error_context, "EXCLUDE is not supported for the window function \"%s\"", + throw BinderException(error_context, "EXCLUDE is not supported for the window function %s", window.FunctionName()); } if (window.HasIgnoreNulls() && !bound_function.CanIgnoreNulls()) { diff --git a/src/duckdb/src/planner/binder/query_node/bind_cte_node.cpp b/src/duckdb/src/planner/binder/query_node/bind_cte_node.cpp index d798f33ae..846410625 100644 --- a/src/duckdb/src/planner/binder/query_node/bind_cte_node.cpp +++ b/src/duckdb/src/planner/binder/query_node/bind_cte_node.cpp @@ -96,7 +96,7 @@ BoundStatement Binder::BindNode(QueryNode &node) { auto db = manager.GetDatabase(context, entry.first); if (db && !db->GetCatalog().SupportsMultipleDMLCTEs()) { throw BinderException("Multiple DML statements in a single WITH clause are not supported for " - "database \"%s\"", + "database %s", entry.first); } } diff --git a/src/duckdb/src/planner/binder/query_node/bind_recursive_cte_node.cpp b/src/duckdb/src/planner/binder/query_node/bind_recursive_cte_node.cpp index 9ce269546..e3cb1a7ae 100644 --- a/src/duckdb/src/planner/binder/query_node/bind_recursive_cte_node.cpp +++ b/src/duckdb/src/planner/binder/query_node/bind_recursive_cte_node.cpp @@ -136,7 +136,7 @@ BoundStatement Binder::BindNode(RecursiveCTENode &statement) { auto names_iter = find(result.names.begin(), result.names.end(), func_expr.GetAlias()); if (names_iter == result.names.end()) { throw BinderException(expr->GetQueryLocation(), - "Could not find column with name '%s' to bind aggregate to.", + "Could not find column with name %s to bind aggregate to.", func_expr.GetAlias()); } aggregate_idx = ProjectionIndex(NumericCast(std::distance(result.names.begin(), names_iter))); @@ -164,14 +164,14 @@ BoundStatement Binder::BindNode(RecursiveCTENode &statement) { if (payload_references.find(aggregate_idx) != payload_references.end()) { throw BinderException(func_expr.GetQueryLocation(), - "Column '%s' referenced multiple times in USING KEY clause.\n" + "Column %s referenced multiple times in USING KEY clause.\n" "Try using an alias for one of the aggregates.", result.names[aggregate_idx]); } if (key_references.find(aggregate_idx) != key_references.end()) { throw BinderException(func_expr.GetQueryLocation(), - "Column '%s' cannot be used as both key and aggregate in USING KEY clause.\n" + "Column %s cannot be used as both key and aggregate in USING KEY clause.\n" "Try using an alias for the aggregation.", result.names[aggregate_idx]); } diff --git a/src/duckdb/src/planner/binder/query_node/bind_setop_node.cpp b/src/duckdb/src/planner/binder/query_node/bind_setop_node.cpp index 870580d3f..86bdf6568 100644 --- a/src/duckdb/src/planner/binder/query_node/bind_setop_node.cpp +++ b/src/duckdb/src/planner/binder/query_node/bind_setop_node.cpp @@ -132,7 +132,7 @@ void Binder::BuildUnionByNameInfo(BoundSetOperationNode &result) { if (node_name_map.find(col_name) != node_name_map.end()) { throw BinderException( "UNION (ALL) BY NAME operation doesn't support duplicate names in the SELECT list - " - "the name \"%s\" occurs multiple times", + "the name %s occurs multiple times", col_name); } if (global_name_set.find(col_name) == global_name_set.end()) { diff --git a/src/duckdb/src/planner/binder/query_node/bind_trigger_expansion.cpp b/src/duckdb/src/planner/binder/query_node/bind_trigger_expansion.cpp index 93461a6c0..a8434f65b 100644 --- a/src/duckdb/src/planner/binder/query_node/bind_trigger_expansion.cpp +++ b/src/duckdb/src/planner/binder/query_node/bind_trigger_expansion.cpp @@ -683,13 +683,13 @@ BoundStatement Binder::ExpandRowTriggers(QueryNode &node, vector 1 || expr.GetChildTypes().size() > 1) { return true; } + if (expr.ComparisonType() == ExpressionType::COMPARE_EQUAL) { + // equality on a TUPLE is a single struct comparison, which is supported + return false; + } for (auto &child : expr.GetChildren()) { if (IsMultiColumnTuple(child->GetReturnType())) { return true; diff --git a/src/duckdb/src/planner/binder/statement/bind_copy.cpp b/src/duckdb/src/planner/binder/statement/bind_copy.cpp index 6a8d67aee..c9e775d95 100644 --- a/src/duckdb/src/planner/binder/statement/bind_copy.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_copy.cpp @@ -6,6 +6,7 @@ #include "duckdb/common/filename_pattern.hpp" #include "duckdb/common/index_vector.hpp" #include "duckdb/common/local_file_system.hpp" +#include "duckdb/main/database_manager.hpp" #include "duckdb/common/exception/parser_exception.hpp" #include "duckdb/function/table/read_csv.hpp" #include "duckdb/main/client_context.hpp" @@ -573,6 +574,11 @@ BoundStatement Binder::BindCopyTo(CopyStatement &stmt, const CopyFunction &funct auto unique_column_names = select_node.names; QueryResult::DeduplicateColumns(unique_column_names); auto file_path = stmt.info->file_path; + if (!file_path.empty() && !FileSystem::IsRemoteFile(file_path) && + DatabaseManager::Get(context).IsAttachedDatabaseFile(context, file_path)) { + // writing to the file of an attached database would corrupt it + throw BinderException("Cannot COPY to \"%s\": the file belongs to an attached database", file_path); + } ValidateCopyToOutputColumns(resolved_options, select_node.names.size()); diff --git a/src/duckdb/src/planner/binder/statement/bind_create.cpp b/src/duckdb/src/planner/binder/statement/bind_create.cpp index f787194e3..c8535a1ea 100644 --- a/src/duckdb/src/planner/binder/statement/bind_create.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_create.cpp @@ -180,7 +180,7 @@ void Binder::SearchSchema(CreateInfo &info) { // a catalog was given but no schema: use the catalog's default schema auto default_schema = search_path->GetDefaultSchema(context, catalog); if (!default_schema) { - throw BinderException("Catalog \"%s\" has no default schema - specify a schema explicitly", catalog); + throw BinderException("Catalog %s has no default schema - specify a schema explicitly", catalog); } schema_path.push_back(*default_schema); } else if (IsInvalidCatalog(catalog)) { @@ -492,7 +492,7 @@ SchemaCatalogEntry &Binder::BindCreateFunctionInfo(CreateInfo &info) { auto default_expr = param_expr->Copy(); auto bound_default = binder.Bind(default_expr); if (!bound_default->IsFoldable()) { - auto msg = StringUtil::Format("Default value '%s' for parameter '%s' is not a constant expression.", + auto msg = StringUtil::Format("Default value '%s' for parameter %s is not a constant expression.", param_expr->ToString(), param_name); throw BinderException(msg); } @@ -525,7 +525,7 @@ SchemaCatalogEntry &Binder::BindCreateFunctionInfo(CreateInfo &info) { } if (CastFunctionSet::ImplicitCastCost(context, val_type, type) < 0) { auto msg = StringUtil::Format( - "Default value '%s' for parameter '%s' cannot be implicitly cast to '%s'.", + "Default value '%s' for parameter %s cannot be implicitly cast to '%s'.", function->default_parameters[param_name]->ToString(), param_name, type.ToString()); throw BinderException(msg + " Please add an explicit type cast."); } @@ -709,7 +709,7 @@ SchemaCatalogEntry &Binder::BindCreateTriggerInfo(CreateTriggerInfo &create_trig if (create_trigger_info.event_type == TriggerEventType::UPDATE_EVENT && !create_trigger_info.columns.empty()) { for (const auto &col_name : create_trigger_info.columns) { if (!table.ColumnExists(col_name)) { - throw BinderException("Column \"%s\" does not exist in table \"%s\"", col_name, table.name); + throw BinderException("Column %s does not exist in table %s", col_name, table.name); } } } @@ -774,7 +774,7 @@ SchemaCatalogEntry &Binder::BindCreateTriggerInfo(CreateTriggerInfo &create_trig if (entry.internal) { return; } - throw BinderException("Trigger \"%s\" cannot reference \"%s\" from a different catalog (\"%s\")", + throw BinderException("Trigger %s cannot reference %s from a different catalog (%s)", create_trigger_info.GetTriggerName(), entry.name, entry.ParentCatalog().GetName()); } DependencyDependentFlags flags; @@ -823,7 +823,7 @@ SchemaCatalogEntry &Binder::BindCreateTriggerInfo(CreateTriggerInfo &create_trig } if (BoundBodyContainsTrigger(*bound_body.plan)) { throw NotImplementedException( - "FOR EACH ROW trigger \"%s\" on table \"%s\" writes to a table that has its own FOR EACH ROW " + "FOR EACH ROW trigger %s on table %s writes to a table that has its own FOR EACH ROW " "trigger (cascading row triggers are not yet supported)", create_trigger_info.GetTriggerName(), table.name); } diff --git a/src/duckdb/src/planner/binder/statement/bind_create_table.cpp b/src/duckdb/src/planner/binder/statement/bind_create_table.cpp index 7de3e148b..f64631d77 100644 --- a/src/duckdb/src/planner/binder/statement/bind_create_table.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_create_table.cpp @@ -218,7 +218,7 @@ unique_ptr Binder::BindUniqueConstraint(const Constraint &const auto physical_index = col.Physical(); if (index_set.find(physical_index) != index_set.end()) { - throw ParserException("column \"%s\" appears twice in primary key constraint", col_name); + throw ParserException("column %s appears twice in primary key constraint", col_name); } indexes.push_back(physical_index); index_set.insert(physical_index); @@ -318,7 +318,7 @@ void Binder::BindGeneratedColumns(BoundCreateTableInfo &info) { auto bound_expression = expr_binder.Bind(expression); D_ASSERT(bound_expression); if (bound_expression->HasSubquery()) { - throw BinderException("Failed to bind generated column '%s' because the expression contains a subquery", + throw BinderException("Failed to bind generated column %s because the expression contains a subquery", col.Name()); } if (col.Type().id() == LogicalTypeId::ANY) { diff --git a/src/duckdb/src/planner/binder/statement/bind_export.cpp b/src/duckdb/src/planner/binder/statement/bind_export.cpp index 7869d95b1..336889f24 100644 --- a/src/duckdb/src/planner/binder/statement/bind_export.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_export.cpp @@ -197,7 +197,7 @@ BoundStatement Binder::Bind(ExportStatement &stmt) { } auto &fk = constraint->Cast(); if (fk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE) { - throw BinderException("Failed to export database: table \"%s\" has a self-referencing foreign key " + throw BinderException("Failed to export database: table %s has a self-referencing foreign key " "constraint which is currently not supported for exporting", table.name); } diff --git a/src/duckdb/src/planner/binder/statement/bind_insert.cpp b/src/duckdb/src/planner/binder/statement/bind_insert.cpp index 2cea68172..823d04d0b 100644 --- a/src/duckdb/src/planner/binder/statement/bind_insert.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_insert.cpp @@ -1,5 +1,8 @@ #include "duckdb/catalog/catalog.hpp" +#include "duckdb/parser/constraints/not_null_constraint.hpp" +#include "duckdb/parser/expression/case_expression.hpp" #include "duckdb/parser/expression/comparison_expression.hpp" +#include "duckdb/parser/expression/operator_expression.hpp" #include "duckdb/parser/expression/conjunction_expression.hpp" #include "duckdb/parser/expression/constant_expression.hpp" #include "duckdb/parser/expression/function_expression.hpp" @@ -342,6 +345,17 @@ static identifier_set_t GetConflictColumnNames(const TableStorageInfo &storage_i return conflict_column_names; } +static bool IsNotNullColumn(TableCatalogEntry &table, Identifier &column_name) { + auto column_index = table.GetColumnIndex(column_name); + for (auto &constraint : table.GetConstraints()) { + if (constraint->type == ConstraintType::NOT_NULL && + constraint->Cast().index == column_index) { + return true; + } + } + return false; +} + unique_ptr Binder::GenerateMergeInto(InsertQueryNode &node, TableCatalogEntry &table) { D_ASSERT(node.on_conflict_info); @@ -540,8 +554,30 @@ unique_ptr Binder::GenerateMergeInto(InsertQueryNode &node, auto distinct_stmt = make_uniq(); auto select_node = make_uniq(); auto distinct = make_uniq(); + unique_ptr any_key_null; for (auto &col : distinct_on_columns) { distinct->distinct_on_targets.push_back(make_uniq(col)); + if (IsNotNullColumn(table, col)) { + continue; + } + auto is_null = + make_uniq(ExpressionType::OPERATOR_IS_NULL, make_uniq(col)); + if (any_key_null) { + any_key_null = make_uniq(ExpressionType::CONJUNCTION_OR, std::move(any_key_null), + std::move(is_null)); + } else { + any_key_null = std::move(is_null); + } + } + if (any_key_null) { + // rows with a NULL key column never conflict (NULLs are distinct in unique indexes) - make them distinct + auto unique_null_key = make_uniq(); + CaseCheck null_check; + null_check.when_expr = std::move(any_key_null); + null_check.then_expr = make_uniq("uuid", vector>()); + unique_null_key->CaseChecksMutable().push_back(std::move(null_check)); + unique_null_key->ElseMutable() = ConstantExpression::FromValue(Value()); + distinct->distinct_on_targets.push_back(std::move(unique_null_key)); } select_node->modifiers.push_back(std::move(distinct)); select_node->select_list.push_back(make_uniq()); diff --git a/src/duckdb/src/planner/binder/statement/bind_vacuum.cpp b/src/duckdb/src/planner/binder/statement/bind_vacuum.cpp index a329944e9..3536af673 100644 --- a/src/duckdb/src/planner/binder/statement/bind_vacuum.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_vacuum.cpp @@ -47,7 +47,7 @@ void Binder::BindVacuumTable(LogicalVacuum &vacuum, unique_ptr } column_name_set.insert(col_name); if (!table.ColumnExists(col_name)) { - throw BinderException("Column with name \"%s\" does not exist", col_name); + throw BinderException("Column with name %s does not exist", col_name); } auto &col = table.GetColumn(col_name); // ignore generated column diff --git a/src/duckdb/src/planner/binder/tableref/bind_pivot.cpp b/src/duckdb/src/planner/binder/tableref/bind_pivot.cpp index 7e0083d11..936ab9ca1 100644 --- a/src/duckdb/src/planner/binder/tableref/bind_pivot.cpp +++ b/src/duckdb/src/planner/binder/tableref/bind_pivot.cpp @@ -919,8 +919,8 @@ unique_ptr Binder::BindUnpivot(Binder &child_binder, PivotRef &ref, } if (!handled_columns.empty()) { for (auto &entry : handled_columns) { - throw BinderException( - ref, "Column \"%s\" referenced in UNPIVOT but no matching entry was found in the table", entry); + throw BinderException(ref, "Column %s referenced in UNPIVOT but no matching entry was found in the table", + entry); } } vector unpivot_names; diff --git a/src/duckdb/src/planner/binder/tableref/bind_showref.cpp b/src/duckdb/src/planner/binder/tableref/bind_showref.cpp index de12d298e..aa5a8e6aa 100644 --- a/src/duckdb/src/planner/binder/tableref/bind_showref.cpp +++ b/src/duckdb/src/planner/binder/tableref/bind_showref.cpp @@ -118,7 +118,7 @@ BoundStatement Binder::BindDescribeQuery(ShowRef &ref) { // we can! emit the information from the base table directly PragmaTableInfo::GetColumnInfo(*result.table, *result.column, output); // Override the base column name with the alias if one is specified. - if (alias != result.column->Name()) { + if (alias.GetIdentifierName() != result.column->Name().GetIdentifierName()) { output.data[0].SetValue(row_index, Value(alias)); } } else { diff --git a/src/duckdb/src/planner/binder/tableref/bind_table_function.cpp b/src/duckdb/src/planner/binder/tableref/bind_table_function.cpp index fe943ea43..a9aa2ea98 100644 --- a/src/duckdb/src/planner/binder/tableref/bind_table_function.cpp +++ b/src/duckdb/src/planner/binder/tableref/bind_table_function.cpp @@ -64,20 +64,20 @@ static TableFunctionBindType GetTableFunctionBindType(TableFunctionCatalogEntry } else if (function.function || function.bind_replace || function.bind_operator) { has_standard_table_function = true; } else { - throw InternalException("Function \"%s\" has neither in_out_function nor function defined", + throw InternalException("Function %s has neither in_out_function nor function defined", table_function.name); } } if (has_table_parameter) { if (table_function.functions.Size() != 1) { throw InternalException( - "Function \"%s\" has a TABLE parameter, and multiple function overloads - this is not supported", + "Function %s has a TABLE parameter, and multiple function overloads - this is not supported", table_function.name); } return TableFunctionBindType::TABLE_PARAMETER_FUNCTION; } if (has_in_out_function && has_standard_table_function) { - throw InternalException("Function \"%s\" is both an in_out_function and a table function", table_function.name); + throw InternalException("Function %s is both an in_out_function and a table function", table_function.name); } return has_in_out_function ? TableFunctionBindType::TABLE_IN_OUT_FUNCTION : TableFunctionBindType::STANDARD_TABLE_FUNCTION; @@ -253,14 +253,13 @@ BoundStatement Binder::BindTableFunctionInternal(BoundTableFunction &table_funct } new_plan->ResolveOperatorTypes(); if (new_plan->types.size() != return_names.size()) { - throw InternalException("Failed to bind \"%s\": return_types/names must have same size", + throw InternalException("Failed to bind %s: return_types/names must have same size", table_function.GetName()); } for (auto &binding : new_plan->GetColumnBindings()) { if (binding.table_index != bind_index) { - throw InternalException( - "Failed to bind \"%s\": root bind index must be the passed in bind index", - table_function.GetName()); + throw InternalException("Failed to bind %s: root bind index must be the passed in bind index", + table_function.GetName()); } } auto setof_column_name = ApplyPostgresSetofAliasCompatibility(table_function, ref, return_names); @@ -286,7 +285,7 @@ BoundStatement Binder::BindTableFunctionInternal(BoundTableFunction &table_funct } } if (!table_function.bind) { - throw BinderException("Failed to bind \"%s\": nullptr returned from bind_replace without bind function", + throw BinderException("Failed to bind %s: nullptr returned from bind_replace without bind function", table_function.GetName()); } bind_data = table_function.bind(context, bind_input, return_types, return_names); @@ -312,18 +311,17 @@ BoundStatement Binder::BindTableFunctionInternal(BoundTableFunction &table_funct } } } else { - throw InvalidInputException("Cannot call function \"%s\" directly - it has no bind function", + throw InvalidInputException("Cannot call function %s directly - it has no bind function", table_function.GetName()); } if (bind_data && !bind_data->SupportStatementCache()) { SetAlwaysRequireRebind(); } if (return_types.size() != return_names.size()) { - throw InternalException("Failed to bind \"%s\": return_types/names must have same size", - table_function.GetName()); + throw InternalException("Failed to bind %s: return_types/names must have same size", table_function.GetName()); } if (return_types.empty()) { - throw InternalException("Failed to bind \"%s\": Table function must return at least one column", + throw InternalException("Failed to bind %s: Table function must return at least one column", table_function.GetName()); } auto setof_column_name = ApplyPostgresSetofAliasCompatibility(table_function, ref, return_names); diff --git a/src/duckdb/src/planner/expression/bound_cast_expression.cpp b/src/duckdb/src/planner/expression/bound_cast_expression.cpp index 26d925cb2..8289ded96 100644 --- a/src/duckdb/src/planner/expression/bound_cast_expression.cpp +++ b/src/duckdb/src/planner/expression/bound_cast_expression.cpp @@ -168,6 +168,9 @@ bool BoundCastExpression::CastIsInvertible(const LogicalType &source_type, const if (source_type.IsNested() || target_type.IsNested()) { return NestedCastIsInvertible(source_type, target_type); } + if (source_type.id() == LogicalTypeId::ENUM && target_type.id() == LogicalTypeId::ENUM) { + return false; + } if (source_type.id() == LogicalTypeId::DECIMAL || target_type.id() == LogicalTypeId::DECIMAL) { uint8_t source_width, target_width; uint8_t source_scale, target_scale; diff --git a/src/duckdb/src/planner/expression_binder.cpp b/src/duckdb/src/planner/expression_binder.cpp index 97d819e0f..74d424c09 100644 --- a/src/duckdb/src/planner/expression_binder.cpp +++ b/src/duckdb/src/planner/expression_binder.cpp @@ -286,7 +286,7 @@ unique_ptr ExpressionBinder::Bind(unique_ptr &expr result = BoundCastExpression::AddCastToType(context, std::move(result), exchanged_type); } } - if (result->GetReturnType().id() == LogicalTypeId::UNKNOWN) { + if (ContainsType(result->GetReturnType(), LogicalTypeId::UNKNOWN)) { throw ParameterNotResolvedException(); } } diff --git a/src/duckdb/src/planner/expression_binder/base_select_binder.cpp b/src/duckdb/src/planner/expression_binder/base_select_binder.cpp index d69a3a25c..d70ff20c1 100644 --- a/src/duckdb/src/planner/expression_binder/base_select_binder.cpp +++ b/src/duckdb/src/planner/expression_binder/base_select_binder.cpp @@ -106,8 +106,7 @@ BindResult BaseSelectBinder::BindGroupingFunction(OperatorExpression &op, idx_t ExpressionBinder::QualifyColumnNames(binder, child); auto idx = TryBindGroup(*child); if (!idx.IsValid()) { - return BindResult( - BinderException(op, "GROUPING child \"%s\" must be a grouping column", child->GetName())); + return BindResult(BinderException(op, "GROUPING child %s must be a grouping column", child->GetName())); } group_indexes.push_back(idx); } diff --git a/src/duckdb/src/planner/expression_binder/group_binder.cpp b/src/duckdb/src/planner/expression_binder/group_binder.cpp index a388146a8..105b594ec 100644 --- a/src/duckdb/src/planner/expression_binder/group_binder.cpp +++ b/src/duckdb/src/planner/expression_binder/group_binder.cpp @@ -119,7 +119,7 @@ bool GroupBinder::TryResolveAliasReference(ColumnRefExpression &colref, idx_t de return false; } result = BindResult(BinderException( - colref, "Alias with name \"%s\" exists, but aliases cannot be used as part of an expression in the GROUP BY", + colref, "Alias with name %s exists, but aliases cannot be used as part of an expression in the GROUP BY", alias_name)); return true; } diff --git a/src/duckdb/src/planner/expression_binder/having_binder.cpp b/src/duckdb/src/planner/expression_binder/having_binder.cpp index 1785e4c47..fccf18087 100644 --- a/src/duckdb/src/planner/expression_binder/having_binder.cpp +++ b/src/duckdb/src/planner/expression_binder/having_binder.cpp @@ -45,7 +45,7 @@ BindResult HavingBinder::BindColumnRef(unique_ptr &expr_ptr, i auto found_alias = column_alias_binder.BindAlias(*this, expr_ptr, depth, root_expression, alias_result); if (found_alias) { if (depth > 0) { - throw BinderException("Having clause cannot reference alias \"%s\" in correlated subquery", column_name); + throw BinderException("Having clause cannot reference alias %s in correlated subquery", column_name); } return alias_result; } @@ -60,7 +60,7 @@ BindResult HavingBinder::BindColumnRef(unique_ptr &expr_ptr, i } if (depth > 0) { - throw BinderException("Having clause cannot reference column \"%s\" in correlated subquery and group by all", + throw BinderException("Having clause cannot reference column %s in correlated subquery and group by all", column_name); } diff --git a/src/duckdb/src/planner/expression_binder/select_bind_state.cpp b/src/duckdb/src/planner/expression_binder/select_bind_state.cpp index 8594ea3a1..73f8723ec 100644 --- a/src/duckdb/src/planner/expression_binder/select_bind_state.cpp +++ b/src/duckdb/src/planner/expression_binder/select_bind_state.cpp @@ -5,7 +5,7 @@ namespace duckdb { unique_ptr SelectBindState::BindAlias(idx_t index) { if (volatile_expressions.find(index) != volatile_expressions.end()) { - throw BinderException("Alias \"%s\" referenced - but the expression has side " + throw BinderException("Alias %s referenced - but the expression has side " "effects. This is not yet supported.", original_expressions[index]->GetAlias()); } @@ -16,7 +16,7 @@ unique_ptr SelectBindState::BindAlias(idx_t index) { void SelectBindState::SetExpressionIsVolatile(idx_t index) { // check if this expression has been referenced before if (referenced_aliases.find(index) != referenced_aliases.end()) { - throw BinderException("Alias \"%s\" referenced - but the expression has side " + throw BinderException("Alias %s referenced - but the expression has side " "effects. This is not yet supported.", original_expressions[index]->GetAlias()); } diff --git a/src/duckdb/src/planner/filter/table_filter_functions.cpp b/src/duckdb/src/planner/filter/table_filter_functions.cpp index 5e84362cf..8566b503d 100644 --- a/src/duckdb/src/planner/filter/table_filter_functions.cpp +++ b/src/duckdb/src/planner/filter/table_filter_functions.cpp @@ -127,7 +127,7 @@ unique_ptr TableFilterFunctionDeserialize(Deserializer &deserializ if (function.GetName() == DynamicFilterScalarFun::NAME) { return make_uniq(nullptr); } - throw InternalException("Unsupported table filter function \"%s\" during deserialization", function.GetName()); + throw InternalException("Unsupported table filter function %s during deserialization", function.GetName()); } } // namespace duckdb diff --git a/src/duckdb/src/planner/operator/logical_copy_to_file.cpp b/src/duckdb/src/planner/operator/logical_copy_to_file.cpp index 947262b3c..620b6f552 100644 --- a/src/duckdb/src/planner/operator/logical_copy_to_file.cpp +++ b/src/duckdb/src/planner/operator/logical_copy_to_file.cpp @@ -139,7 +139,7 @@ unique_ptr LogicalCopyToFile::Deserialize(Deserializer &deseria if (!has_serialize) { // If not serialized, re-bind with the copy info if (!function.copy_to_bind) { - throw InternalException("Copy function \"%s\" has neither bind nor (de)serialize", function.name); + throw InternalException("Copy function %s has neither bind nor (de)serialize", function.name); } CopyFunctionBindInput function_bind_input(*copy_info, function.function_info); diff --git a/src/duckdb/src/planner/operator/logical_get.cpp b/src/duckdb/src/planner/operator/logical_get.cpp index da15b8b85..2ca48ea25 100644 --- a/src/duckdb/src/planner/operator/logical_get.cpp +++ b/src/duckdb/src/planner/operator/logical_get.cpp @@ -433,7 +433,7 @@ unique_ptr LogicalGet::Deserialize(Deserializer &deserializer) vector bind_return_types; vector bind_names; if (!function.bind) { - throw InternalException("Table function \"%s\" has neither bind nor (de)serialize", function.GetName()); + throw InternalException("Table function %s has neither bind nor (de)serialize", function.GetName()); } bind_data = function.bind(context, input, bind_return_types, bind_names); if (result->ordinality_idx.IsValid()) { diff --git a/src/duckdb/src/planner/subquery/delim_join_cte_rewriter.cpp b/src/duckdb/src/planner/subquery/delim_join_cte_rewriter.cpp index 8c9dd8106..6ec2bdc62 100644 --- a/src/duckdb/src/planner/subquery/delim_join_cte_rewriter.cpp +++ b/src/duckdb/src/planner/subquery/delim_join_cte_rewriter.cpp @@ -1152,7 +1152,7 @@ bool GeneratedDedupRefEliminator::RewriteSubtree(unique_ptr &op if (RewriteSubtree(op->children[child_idx], preserve_selected_domain, child_replacements, child_under_aggregate, under_evidence_side)) { ColumnBindingRewrite::ApplyToChild(op, child_idx, std::move(old_child_bindings), child_replacements); - replacements.Merge(child_replacements); + replacements.Merge(child_replacements.ScopedToBoundary(op->children[child_idx]->GetColumnBindings())); rewritten = true; } } @@ -1432,6 +1432,7 @@ bool GeneratedDedupRefEliminator::PreserveJoinAsSemi(unique_ptr ColumnBindingReplacer replacer; replacement_bindings.AddTo(replacer); + replacer.BuildIndex(); replacer.stop_operator = join.get(); replacer.VisitOperator(rewrite_root); ReplaceOperatorBindings(rewrite_root, dedup_ref->output_bindings, generated_output_replacements, join.get()); @@ -1552,6 +1553,7 @@ bool GeneratedDedupRefEliminator::PreserveFilterCrossProductAsSemi(unique_ptroutput_bindings, generated_output_replacements, filter_op.get()); @@ -1691,6 +1693,7 @@ bool GeneratedDedupRefEliminator::RemoveJoin(unique_ptr &join, ColumnBindingReplacer replacer; replacement_bindings.AddTo(replacer); + replacer.BuildIndex(); replacer.VisitOperator(rewrite_root); ReplaceOperatorBindings(rewrite_root, dedup_ref->output_bindings, generated_output_replacements); replacements.Merge(replacement_bindings); @@ -1812,6 +1815,7 @@ bool GeneratedDedupRefEliminator::RemoveFilterCrossProduct(unique_ptroutput_bindings, generated_output_replacements); replacements.Merge(replacement_bindings); @@ -2448,7 +2453,7 @@ bool GeneratedDomainJoinEliminator::TryRewriteOnce(unique_ptr & if (TryRewriteOnce(child, child_replacements, child_under_aggregate, child_under_evidence_side, child_negated_marker_filter_above)) { ColumnBindingRewrite::ApplyToChild(op, child_idx, std::move(old_child_bindings), child_replacements); - replacements.Merge(child_replacements); + replacements.Merge(child_replacements.ScopedToBoundary(op->children[child_idx]->GetColumnBindings())); return true; } } @@ -2716,6 +2721,7 @@ BindingReplacementGraph DelimJoinCTERewriter::MaterializeDelimJoinAsCTE(unique_p auto projection_replacements = CreateConstructedBindingReplacements(old_left_bindings, projected_left_bindings); CorrelatedColumnBindingReplacer projection_replacer; projection_replacements.AddTo(projection_replacer); + projection_replacer.BuildIndex(); projection_replacer.stop_operator = plan->children[1]; projection_replacer.VisitOperator(*plan); output_replacements.Merge(projection_replacements); @@ -2746,6 +2752,7 @@ BindingReplacementGraph DelimJoinCTERewriter::MaterializeDelimJoinAsCTE(unique_p plan->children[0] = std::move(left_cte_ref); ColumnBindingReplacer replacer; binding_replacements.AddTo(replacer); + replacer.BuildIndex(); replacer.stop_operator = plan->children[1]; replacer.VisitOperator(*plan); @@ -2817,7 +2824,8 @@ BindingReplacementGraph DelimJoinCTERewriter::RewriteDelimJoinsToCTEs(unique_ptr auto child_replacements = RewriteDelimJoinsToCTEs(child, rewrite_root, child_null_rejecting_filter_above, child_preserve_evidence_side); ColumnBindingRewrite::ApplyToChild(plan, child_idx, std::move(old_child_bindings), child_replacements); - output_replacements.Merge(child_replacements); + // Scope to the child's output boundary: operators above cannot see bindings that no longer resolve into it + output_replacements.Merge(child_replacements.ScopedToBoundary(plan->children[child_idx]->GetColumnBindings())); } if (plan->type == LogicalOperatorType::LOGICAL_DELIM_JOIN) { auto &join = plan->Cast(); @@ -2830,7 +2838,7 @@ BindingReplacementGraph DelimJoinCTERewriter::RewriteDelimJoinsToCTEs(unique_ptr } auto materialize_replacements = MaterializeDelimJoinAsCTE( plan, rewrite_root, null_rejecting_filter_above, preserve_evidence_side, preserve_nested_evidence_side); - output_replacements.Merge(materialize_replacements); + output_replacements.Merge(materialize_replacements.ScopedToBoundary(plan->GetColumnBindings())); } return output_replacements; } diff --git a/src/duckdb/src/planner/subquery/flatten_dependent_join.cpp b/src/duckdb/src/planner/subquery/flatten_dependent_join.cpp index 69038c5d7..bb70f6227 100644 --- a/src/duckdb/src/planner/subquery/flatten_dependent_join.cpp +++ b/src/duckdb/src/planner/subquery/flatten_dependent_join.cpp @@ -655,6 +655,23 @@ FlattenDependentJoins::UnnestingState FlattenDependentJoins::FinalizeDependentJo for (auto &binding : output.right_payload) { binding = outer_state.replacement_graph.Resolve(binding); } + // Correlated aggregate rewrites reference marker columns that must survive the projection maps below + auto keep_rewrite_inputs = [&](vector &payload) { + for (idx_t i = 0, count = payload.size(); i < count; i++) { + auto entry = replacement_map.find(payload[i]); + if (entry == replacement_map.end()) { + continue; + } + for (auto &extra : {entry->second.marker, entry->second.empty_result}) { + auto resolved = outer_state.replacement_graph.Resolve(extra); + if (std::find(payload.begin(), payload.end(), resolved) == payload.end()) { + payload.push_back(resolved); + } + } + } + }; + keep_rewrite_inputs(output.left_payload); + keep_rewrite_inputs(output.right_payload); for (auto &binding : outer_state.bindings) { binding = outer_state.replacement_graph.Resolve(binding); } diff --git a/src/duckdb/src/planner/table_binding.cpp b/src/duckdb/src/planner/table_binding.cpp index 8202018d6..3cff39fd1 100644 --- a/src/duckdb/src/planner/table_binding.cpp +++ b/src/duckdb/src/planner/table_binding.cpp @@ -90,7 +90,7 @@ bool Binding::TryGetBindingIndex(const Identifier &column_name, column_t &result column_t Binding::GetBindingIndex(const Identifier &column_name) { column_t result; if (!TryGetBindingIndex(column_name, result)) { - throw InternalException("Binding index for column \"%s\" not found", column_name); + throw InternalException("Binding index for column %s not found", column_name); } return result; } @@ -184,8 +184,7 @@ TableBinding::TableBinding(const Identifier &alias, vector types_p, auto &name = ventry.second.name; if (idx < VIRTUAL_COLUMN_START) { throw BinderException( - "Virtual column index must be larger than VIRTUAL_COLUMN_START - found %d for column \"%s\"", idx, - name); + "Virtual column index must be larger than VIRTUAL_COLUMN_START - found %d for column %s", idx, name); } if (idx == COLUMN_IDENTIFIER_EMPTY) { // the empty column cannot be queried by the user diff --git a/src/duckdb/src/planner/table_filter_set.cpp b/src/duckdb/src/planner/table_filter_set.cpp index ef6ef4dbc..7068fb7fc 100644 --- a/src/duckdb/src/planner/table_filter_set.cpp +++ b/src/duckdb/src/planner/table_filter_set.cpp @@ -208,7 +208,7 @@ static unique_ptr SerializeInternalFunctionToLegacyFilter(const Bou if (func_name == BloomFilterScalarFun::NAME || func_name == PrefixRangeScalarFun::NAME) { return make_uniq(); } - throw SerializationException("Unsupported internal tablefilter function \"%s\" during serialization", func_name); + throw SerializationException("Unsupported internal tablefilter function %s during serialization", func_name); } static unique_ptr SerializeConjunctionToLegacyFilter(const BoundConjunctionExpression &conjunction) { diff --git a/src/duckdb/src/storage/arena_allocator.cpp b/src/duckdb/src/storage/arena_allocator.cpp index 29d4b314a..490647bde 100644 --- a/src/duckdb/src/storage/arena_allocator.cpp +++ b/src/duckdb/src/storage/arena_allocator.cpp @@ -13,6 +13,7 @@ ArenaChunk::ArenaChunk(Allocator &allocator, idx_t size) : current_position(0), data = allocator.Allocate(size); } ArenaChunk::~ArenaChunk() { + ResetPosition(0); if (next) { auto current_next = std::move(next); while (current_next) { @@ -106,7 +107,11 @@ data_ptr_t ArenaAllocator::Reallocate(data_ptr_t pointer, idx_t old_size, idx_t if (pointer == head_ptr && (size < old_size || current_position + diff <= NumericCast(head->maximum_size))) { // passed pointer is the head pointer, and the diff fits on the current chunk - head->current_position = NumericCast(current_position + diff); + if (size < old_size) { + head->ResetPosition(NumericCast(current_position + diff)); + } else { + head->current_position = NumericCast(current_position + diff); + } return pointer; } else { // allocate new memory @@ -157,7 +162,7 @@ void ArenaAllocator::Rewind(const Position &position) { head->prev = nullptr; } D_ASSERT(position.current_position <= head->current_position); - head->current_position = position.current_position; + head->ResetPosition(position.current_position); } void ArenaAllocator::FreeAll() { @@ -181,7 +186,7 @@ void ArenaAllocator::Reset() { tail = head.get(); // reset the head - head->current_position = 0; + head->ResetPosition(0); head->prev = nullptr; } allocated_size = 0; diff --git a/src/duckdb/src/storage/block_allocator.cpp b/src/duckdb/src/storage/block_allocator.cpp index 60b19d2d7..bf50e3fbd 100644 --- a/src/duckdb/src/storage/block_allocator.cpp +++ b/src/duckdb/src/storage/block_allocator.cpp @@ -2,10 +2,20 @@ #include "duckdb/common/allocator.hpp" #include "duckdb/common/types/uuid.hpp" +#include "duckdb/common/vector.hpp" +#include "duckdb/common/thread.hpp" + +#if defined(__linux__) && INTPTR_MAX == INT64_MAX +#include +#include +#include +#endif #include "duckdb/main/attached_database.hpp" #include "duckdb/main/database.hpp" #include "duckdb/parallel/concurrentqueue.hpp" +#include "duckdb/parallel/task_scheduler.hpp" #include "duckdb/common/bit_utils.hpp" +#include "duckdb/common/chrono.hpp" #if defined(_WIN32) #include "duckdb/common/windows.hpp" @@ -18,6 +28,109 @@ namespace duckdb { +struct BlockAllocatorConfig { + //! Blocks transferred between thread-local caches and global queues. + static constexpr idx_t BATCH_SIZE = 16; + //! Cached freed blocks that trigger a transfer to the global queue. + static constexpr idx_t FREE_THRESHOLD = BATCH_SIZE * 2; + //! Idle decay delay in seconds when the fallback allocator supplies none. + static constexpr idx_t DEFAULT_DECAY_DELAY = 1; + //! Maximum blocks sorted and coalesced in one reclamation batch. + static constexpr idx_t RECLAIM_BATCH_SIZE = 1024; + //! Maximum blocks reclaimed by one background task. + static constexpr idx_t MAX_FLUSH_BLOCKS = 64; + //! Width of each retention bucket, rounding return times up to the bucket end. + static constexpr idx_t RETENTION_INTERVAL_MS = 500; + //! Minimum interval between bounded decay checks on each allocating thread. + static constexpr idx_t ACTIVE_DECAY_INTERVAL_MS = RETENTION_INTERVAL_MS; + //! Linear decay on the same one-second timescale as bundled jemalloc, without extra grace. + static constexpr idx_t RETENTION_DECAY_MS = DEFAULT_DECAY_DELAY * 1000; + //! Current bucket plus the complete decay history. + static constexpr idx_t RETENTION_BUCKETS = RETENTION_DECAY_MS / RETENTION_INTERVAL_MS + 1; +}; + +static idx_t RetentionTimeMillis() { + return NumericCast(duration_cast(steady_clock::now().time_since_epoch()).count()); +} + +static uint64_t PackRetentionBucket(const uint32_t epoch, const uint32_t count) { + return (uint64_t(epoch) << 32) | count; +} + +static uint32_t RetentionBucketEpoch(const uint64_t bucket) { + return static_cast(bucket >> 32); +} + +static uint32_t RetentionBucketCount(const uint64_t bucket) { + return static_cast(bucket); +} + +void BlockAllocator::InitializeRetention(const idx_t now_ms) const { + const auto epoch = now_ms / BlockAllocatorConfig::RETENTION_INTERVAL_MS; + for (idx_t age = 0; age < retention_buckets.size(); age++) { + auto &bucket = retention_buckets[(epoch % retention_buckets.size() + retention_buckets.size() - age) % + retention_buckets.size()]; + bucket.store(PackRetentionBucket(static_cast(epoch - age), 0), std::memory_order_relaxed); + } +} + +void BlockAllocator::AddRetention(const idx_t count, const idx_t now_ms) const { + const auto epoch = now_ms / BlockAllocatorConfig::RETENTION_INTERVAL_MS; + const auto capacity = DivBlockSize(physical_memory_size.load(std::memory_order_relaxed)); + auto &bucket = retention_buckets[epoch % retention_buckets.size()]; + auto current = bucket.load(std::memory_order_relaxed); + do { + const auto elapsed = static_cast(epoch - RetentionBucketEpoch(current)); + // Modular ordering assumes generations are less than half the epoch range apart. + if (elapsed >= (uint32_t(1) << 31)) { + return; + } + const idx_t retained = elapsed == 0 ? RetentionBucketCount(current) : 0; + const auto updated = PackRetentionBucket( + static_cast(epoch), + NumericCast(retained + MinValue(count, capacity - MinValue(capacity, retained)))); + if (updated == current || bucket.compare_exchange_weak(current, updated, std::memory_order_relaxed)) { + return; + } + } while (true); +} + +void BlockAllocator::ReuseRetention(idx_t count, const idx_t now_ms) const { + const auto epoch = now_ms / BlockAllocatorConfig::RETENTION_INTERVAL_MS; + for (idx_t age = 0; age < retention_buckets.size() && count > 0; age++) { + auto &bucket = retention_buckets[(epoch % retention_buckets.size() + retention_buckets.size() - age) % + retention_buckets.size()]; + auto current = bucket.load(std::memory_order_relaxed); + while (RetentionBucketEpoch(current) == static_cast(epoch - age) && + RetentionBucketCount(current) > 0) { + const auto reused = MinValue(count, RetentionBucketCount(current)); + const auto updated = PackRetentionBucket(RetentionBucketEpoch(current), + RetentionBucketCount(current) - NumericCast(reused)); + if (bucket.compare_exchange_weak(current, updated, std::memory_order_relaxed)) { + count -= reused; + break; + } + } + } +} + +idx_t BlockAllocator::RetentionTarget(const idx_t now_ms) const { + const auto epoch = now_ms / BlockAllocatorConfig::RETENTION_INTERVAL_MS; + idx_t weighted = 0; + const auto offset = now_ms % BlockAllocatorConfig::RETENTION_INTERVAL_MS; + for (auto &bucket : retention_buckets) { + const auto current = bucket.load(std::memory_order_relaxed); + const auto age = static_cast(epoch - RetentionBucketEpoch(current)); + if (age >= retention_buckets.size()) { + continue; + } + const auto age_ms = age == 0 ? 0 : (age - 1) * BlockAllocatorConfig::RETENTION_INTERVAL_MS + offset; + const auto remaining_ms = BlockAllocatorConfig::RETENTION_DECAY_MS - age_ms; + weighted += RetentionBucketCount(current) * remaining_ms; + } + return weighted / BlockAllocatorConfig::RETENTION_DECAY_MS; +} + //===--------------------------------------------------------------------===// // Memory Helpers //===--------------------------------------------------------------------===// @@ -53,33 +166,25 @@ static void OnFirstAllocation(const data_ptr_t pointer, const idx_t size) { #if defined(_WIN32) success = VirtualAlloc(pointer, size, MEM_COMMIT, PAGE_READWRITE); #elif defined(__APPLE__) - // Nothing to do here -#else - // Pre-fault the memory - for (idx_t i = 0; i < size; i += 4096) { - pointer[i] = 0; - } + // Reclaimed pages remain reusable until explicitly claimed again. + success = madvise(pointer, size, MADV_FREE_REUSE) == 0; #endif if (!success) { throw InternalException("OnFirstAllocation failed"); } } -static void OnDeallocation(const data_ptr_t pointer, const idx_t size) { - bool success; +static bool OnDeallocation(const data_ptr_t pointer, const idx_t size) { #if defined(_WIN32) - success = VirtualFree(pointer, size, MEM_DECOMMIT); + return VirtualFree(pointer, size, MEM_DECOMMIT); #elif defined(__APPLE__) - success = madvise(pointer, size, MADV_FREE_REUSABLE) == 0; + return madvise(pointer, size, MADV_FREE_REUSABLE) == 0; #elif defined(__MVS__) // the madvice functionality is not available on z/OS in any form - success = true; + return false; #else - success = madvise(pointer, size, MADV_DONTNEED) == 0; + return madvise(pointer, size, MADV_DONTNEED) == 0; #endif - if (!success) { - throw InternalException("OnDeallocation failed"); - } } //===--------------------------------------------------------------------===// @@ -89,6 +194,11 @@ struct BlockQueue { duckdb_moodycamel::ConcurrentQueue q; }; +struct BlockAllocatorLifetimeState { + annotated_mutex lock; + bool alive DUCKDB_GUARDED_BY(lock) = true; +}; + class BlockAllocatorThreadLocalState { public: explicit BlockAllocatorThreadLocalState(const BlockAllocator &block_allocator_p) { @@ -126,24 +236,26 @@ class BlockAllocatorThreadLocalState { void Free(const data_ptr_t pointer) { touched.push_back(block_allocator->GetBlockID(pointer)); - if (touched.size() < FREE_THRESHOLD) { + block_allocator->UpdateCachedBlocks(1, cache_slot); + if (touched.size() < BlockAllocatorConfig::FREE_THRESHOLD) { return; } // Upon reaching the threshold, we return a local batch to global std::sort(touched.begin(), touched.end()); - block_allocator->touched->q.enqueue_bulk(touched.end() - BATCH_SIZE, BATCH_SIZE); - touched.resize(touched.size() - BATCH_SIZE); + ReturnTouched(BlockAllocatorConfig::BATCH_SIZE); } - void Clear() { - if (alive_token && alive_token->load()) { - // Allocator is still alive — return local blocks to global queues - if (!touched.empty()) { - block_allocator->touched->q.enqueue_bulk(touched.begin(), touched.size()); - } - if (!untouched.empty()) { - block_allocator->untouched->q.enqueue_bulk(untouched.begin(), untouched.size()); + void Clear() DUCKDB_EXCLUDES(lifetime_state->lock) { + if (lifetime_state) { + annotated_lock_guard guard(lifetime_state->lock); + if (lifetime_state->alive) { + if (!touched.empty()) { + ReturnTouched(touched.size()); + } + if (!untouched.empty()) { + block_allocator->untouched->q.enqueue_bulk(untouched.begin(), untouched.size()); + } } } touched.clear(); @@ -151,36 +263,59 @@ class BlockAllocatorThreadLocalState { } private: + void ReturnTouched(const idx_t count) { + block_allocator->AddRetention(count, RetentionTimeMillis()); + block_allocator->touched->q.enqueue_bulk(touched.end() - NumericCast::difference_type>(count), + count); + touched.resize(touched.size() - count); + } + void Initialize(const BlockAllocator &block_allocator_p) { + Clear(); cached_uuid = block_allocator_p.uuid; block_allocator = block_allocator_p; - alive_token = block_allocator_p.alive_token; - untouched.clear(); - touched.clear(); - untouched.reserve(BATCH_SIZE); - touched.reserve(FREE_THRESHOLD); + lifetime_state = block_allocator_p.lifetime_state; + cache_slot = block_allocator_p.next_cache_slot.fetch_add(1, std::memory_order_relaxed) % + block_allocator_p.cached_blocks.size(); + next_decay_check = 0; + untouched.reserve(BlockAllocatorConfig::BATCH_SIZE); + touched.reserve(BlockAllocatorConfig::FREE_THRESHOLD); } data_ptr_t TryAllocateFromLocal() { if (!touched.empty()) { const auto pointer = block_allocator->GetPointer(touched.back()); touched.pop_back(); + block_allocator->UpdateCachedBlocks(-1, cache_slot); return pointer; } if (!untouched.empty()) { const auto pointer = block_allocator->GetPointer(untouched.back()); - untouched.pop_back(); OnFirstAllocation(pointer, block_allocator->block_size); + untouched.pop_back(); return pointer; } return nullptr; } - static bool TryGetBatch(vector &local, BlockQueue &global) { + bool TryGetBatch(vector &local, BlockQueue &global) { D_ASSERT(local.empty()); - local.resize(BATCH_SIZE); - const auto size = global.q.try_dequeue_bulk(local.begin(), BATCH_SIZE); + local.resize(BlockAllocatorConfig::BATCH_SIZE); + const auto size = global.q.try_dequeue_bulk(local.begin(), BlockAllocatorConfig::BATCH_SIZE); local.resize(size); + if (size > 0 && RefersToSameObject(global, *block_allocator->touched)) { + const auto now = RetentionTimeMillis(); + block_allocator->ReuseRetention(size, now); + if (now >= next_decay_check) { + next_decay_check = now + BlockAllocatorConfig::ACTIVE_DECAY_INTERVAL_MS; + try { + block_allocator->FreeInternal(optional_idx(), BlockAllocatorConfig::MAX_FLUSH_BLOCKS, + BlockAllocator::ReclaimMode::OPPORTUNISTIC); + } catch (...) { + // Failed reclamation leaves blocks available for reuse. + } + } + } std::sort(local.begin(), local.end()); return !local.empty(); } @@ -188,11 +323,9 @@ class BlockAllocatorThreadLocalState { private: hugeint_t cached_uuid; optional_ptr block_allocator; - // Whether the BlockAllocator is still alive. - shared_ptr> alive_token; - - static constexpr idx_t BATCH_SIZE = 128; - static constexpr idx_t FREE_THRESHOLD = BATCH_SIZE * 2; + shared_ptr lifetime_state; + idx_t next_decay_check = 0; + idx_t cache_slot = 0; vector untouched; vector touched; @@ -215,19 +348,160 @@ BlockAllocatorThreadLocalState &GetBlockAllocatorThreadLocalState(const BlockAll //===--------------------------------------------------------------------===// // BlockAllocator //===--------------------------------------------------------------------===// +class AllocationCache { + using Queue = duckdb_moodycamel::ConcurrentQueue; + static constexpr idx_t MIN_SHIFT = 19; + static constexpr idx_t MAX_SHIFT = 26; + static constexpr idx_t MAX_SHARDS = 256; + static constexpr idx_t CPUS_PER_SHARD = 4; + static constexpr idx_t MAX_LOCAL_SPARES = 16; + // An admission ceiling, independent of pressure-driven reclamation. + static constexpr idx_t MAX_CACHED_BYTES_PER_BUCKET = 1024ULL * 1024 * 1024; + + struct alignas(64) Bucket { + vector queues; + alignas(64) atomic count {0}; + atomic last_return_ms {0}; + }; + +public: + explicit AllocationCache(Allocator &allocator) : allocator(allocator), shard_count(GetShardCount()) { + for (auto &bucket : buckets) { + bucket.queues.resize(shard_count); + } + } + ~AllocationCache() { + Flush(NumericLimits::Maximum()); + } + + bool SupportsSize(idx_t size) const { + return size >= (static_cast(1) << MIN_SHIFT) && size <= (static_cast(1) << MAX_SHIFT) && + IsPowerOfTwo(size); + } + data_ptr_t TryAllocate(idx_t size) { + D_ASSERT(SupportsSize(size)); + const auto shard = LocalShard(); + auto &bucket = buckets[CountZeros::Trailing(size) - MIN_SHIFT]; + data_ptr_t pointer; + if (bucket.queues[shard].try_dequeue(pointer)) { + bucket.count.fetch_sub(1, std::memory_order_relaxed); + return pointer; + } + // Share surplus buffers, leaving a few local spares without reserving the entire budget. + const auto reserve = MinValue(MAX_LOCAL_SPARES, MAX_CACHED_BYTES_PER_BUCKET / size / (2 * shard_count)); + for (idx_t offset = 1; offset < shard_count; offset++) { + auto &queue = bucket.queues[(shard + offset) % shard_count]; + if ((reserve == 0 || queue.size_approx() > reserve) && queue.try_dequeue(pointer)) { + bucket.count.fetch_sub(1, std::memory_order_relaxed); + return pointer; + } + } + return nullptr; + } + bool TryFree(data_ptr_t pointer, idx_t size) { + D_ASSERT(SupportsSize(size)); + const auto shard = LocalShard(); + if (!pointer) { + return false; + } + auto &bucket = buckets[CountZeros::Trailing(size) - MIN_SHIFT]; + if (bucket.count.fetch_add(1, std::memory_order_relaxed) < MAX_CACHED_BYTES_PER_BUCKET / size) { + try { + const auto now = RetentionTimeMillis(); + auto last_return = bucket.last_return_ms.load(std::memory_order_relaxed); + while (last_return < now && + !bucket.last_return_ms.compare_exchange_weak(last_return, now, std::memory_order_relaxed)) { + } + if (bucket.queues[shard].enqueue(pointer)) { + return true; + } + } catch (...) { + // Failed admission leaves ownership with the caller. + } + } + bucket.count.fetch_sub(1, std::memory_order_relaxed); + return false; + } + + idx_t GetCachedMemory(optional_idx idle_before = optional_idx()) const { + idx_t bytes = 0; + for (idx_t i = 0; i < buckets.size(); i++) { + const auto &bucket = buckets[i]; + if (!idle_before.IsValid() || + bucket.last_return_ms.load(std::memory_order_relaxed) <= idle_before.GetIndex()) { + bytes += bucket.count.load(std::memory_order_relaxed) << (MIN_SHIFT + i); + } + } + return bytes; + } + + // Maintenance is serialized by BlockAllocator; allocation and return never take that lock. + idx_t Flush(idx_t byte_limit, optional_idx idle_before = optional_idx()) { + idx_t freed = 0; + for (idx_t i = buckets.size(); i > 0 && freed < byte_limit; i--) { + auto &bucket = buckets[i - 1]; + const idx_t size = static_cast(1) << (MIN_SHIFT + i - 1); + // A snapshot bounds the work even if producers continue returning buffers. + auto remaining = bucket.count.load(std::memory_order_relaxed); + for (auto &queue : bucket.queues) { + data_ptr_t pointer; + while (remaining > 0 && freed < byte_limit) { + if (idle_before.IsValid() && + bucket.last_return_ms.load(std::memory_order_relaxed) > idle_before.GetIndex()) { + break; + } + if (!queue.try_dequeue(pointer)) { + break; + } + allocator.FreeData(pointer, size); + bucket.count.fetch_sub(1, std::memory_order_relaxed); + freed += size; + remaining--; + } + } + } + return freed; + } + +private: + static idx_t GetShardCount() { +#ifndef DUCKDB_NO_THREADS + const idx_t cpus = std::thread::hardware_concurrency(); + return MaxValue(1, MinValue(MAX_SHARDS, (cpus + CPUS_PER_SHARD - 1) / CPUS_PER_SHARD)); +#else + return 1; +#endif + } + + idx_t LocalShard() const { + const auto cpu = TaskScheduler::GetEstimatedCPUId(); + return (cpu / CPUS_PER_SHARD) % shard_count; + } + + Allocator &allocator; + const idx_t shard_count; + array buckets; +}; + BlockAllocator::BlockAllocator(Allocator &allocator_p, const idx_t block_size_p, const idx_t virtual_memory_size_p, const idx_t physical_memory_size_p) - : uuid(UUID::GenerateRandomUUID()), allocator(allocator_p), block_size(block_size_p), - block_size_div_shift(CountZeros::Trailing(block_size)), + : uuid(UUID::GenerateRandomUUID()), allocator(allocator_p), allocation_cache(make_uniq(allocator)), + block_size(block_size_p), block_size_div_shift(CountZeros::Trailing(block_size)), virtual_memory_size(AlignValue(virtual_memory_size_p, block_size)), virtual_memory_space(nullptr), physical_memory_size(0), untouched(make_unsafe_uniq()), touched(make_unsafe_uniq()), - alive_token(make_shared_ptr>(true)) { + lifetime_state(make_shared_ptr()) { + static_assert(std::tuple_size::value == BlockAllocatorConfig::RETENTION_BUCKETS, + "Retention bucket storage must match the decay configuration"); D_ASSERT(IsPowerOfTwo(block_size)); + InitializeRetention(RetentionTimeMillis()); Resize(physical_memory_size_p); } BlockAllocator::~BlockAllocator() { - alive_token->store(false); + { + annotated_lock_guard guard(lifetime_state->lock); + lifetime_state->alive = false; + } GetBlockAllocatorThreadLocalState(*this).Clear(); if (IsActive()) { try { @@ -247,7 +521,7 @@ BlockAllocator &BlockAllocator::Get(AttachedDatabase &db) { } void BlockAllocator::Resize(const idx_t new_physical_memory_size) { - lock_guard guard(physical_memory_lock); + annotated_lock_guard guard(physical_memory_lock); if (new_physical_memory_size != 0 && !IsActive()) { virtual_memory_space = AllocateVirtualMemory(virtual_memory_size); @@ -323,15 +597,45 @@ data_ptr_t BlockAllocator::GetPointer(const uint32_t block_id) const { data_ptr_t BlockAllocator::AllocateData(const idx_t size) const { if (!IsActive() || !IsEnabled() || size != block_size) { + if (allocation_cache->SupportsSize(size)) { + auto pointer = allocation_cache->TryAllocate(size); + if (pointer) { + return pointer; + } + } return allocator.AllocateData(size); } return GetBlockAllocatorThreadLocalState(*this).Allocate(); } +idx_t BlockAllocator::GetCachedMemory() const { + return GetCachedPoolMemory() + allocation_cache->GetCachedMemory(); +} + +idx_t BlockAllocator::GetCachedPoolMemory() const { + int64_t count = 0; + const auto slots = MinValue(next_cache_slot.load(std::memory_order_relaxed), cached_blocks.size()); + for (idx_t i = 0; i < slots; i++) { + count += cached_blocks[i].count.load(std::memory_order_relaxed); + } + return count > 0 ? idx_t(count) * block_size : 0; +} + +void BlockAllocator::UpdateCachedBlocks(const int64_t count, const idx_t slot) const { + cached_blocks[slot].count.fetch_add(count, std::memory_order_relaxed); +} + void BlockAllocator::FreeData(const data_ptr_t pointer, const idx_t size) const { - estimated_unflushed_memory.fetch_add(size, std::memory_order_relaxed); + if (!pointer) { + return; + } if (!IsActive() || !IsInPool(pointer)) { - return allocator.FreeData(pointer, size); + if (allocation_cache->SupportsSize(size) && allocation_cache->TryFree(pointer, size)) { + return; + } + allocator.FreeData(pointer, size); + estimated_unflushed_memory.fetch_add(size, std::memory_order_relaxed); + return; } D_ASSERT(size == block_size); GetBlockAllocatorThreadLocalState(*this).Free(pointer); @@ -354,103 +658,408 @@ data_ptr_t BlockAllocator::ReallocateData(const data_ptr_t pointer, const idx_t return new_pointer; } +#if defined(__linux__) && INTPTR_MAX == INT64_MAX && defined(MADV_HUGEPAGE) +static idx_t GetHugePageSize() { + auto file = fopen("/sys/kernel/mm/transparent_hugepage/hpage_pmd_size", "r"); + if (!file) { + return 0; + } + int64_t huge_page_size = 0; + char trailing; + auto fields = fscanf(file, "%" SCNd64 " %c", &huge_page_size, &trailing); + fclose(file); + auto page_size = sysconf(_SC_PAGESIZE); + if (fields != 1 || page_size <= 0 || huge_page_size < page_size || (huge_page_size & (huge_page_size - 1)) != 0 || + huge_page_size % page_size != 0) { + return 0; + } + return NumericCast(huge_page_size); +} +#endif + +static void AdviseHugePages(data_ptr_t pointer, idx_t size) { +#if defined(__linux__) && INTPTR_MAX == INT64_MAX && defined(MADV_HUGEPAGE) + static constexpr idx_t MIN_ADVISE_SIZE = 8ULL * 1024 * 1024; + if (size >= MIN_ADVISE_SIZE) { + static const idx_t huge_page_size = GetHugePageSize(); + if (huge_page_size != 0 && size / huge_page_size >= 4) { + const auto base = reinterpret_cast(pointer); + const auto offset = (huge_page_size - base % huge_page_size) % huge_page_size; + const auto length = ((size - offset) / huge_page_size) * huge_page_size; + // Only advise complete huge pages contained in this allocation. + madvise(pointer + offset, length, MADV_HUGEPAGE); + } + } +#endif +} + +data_ptr_t BlockAllocator::AllocateHugeData(const idx_t size) const { + auto pointer = IsActive() && IsEnabled() && size == block_size ? GetBlockAllocatorThreadLocalState(*this).Allocate() + : allocator.AllocateData(size); + AdviseHugePages(pointer, size); + return pointer; +} + +void BlockAllocator::FreeHugeData(data_ptr_t pointer, const idx_t size) const { + if (IsActive() && IsInPool(pointer)) { + FreeData(pointer, size); + return; + } + allocator.FreeData(pointer, size); + if (pointer) { + estimated_unflushed_memory.fetch_add(size, std::memory_order_relaxed); + } +} + bool BlockAllocator::SupportsFlush() const { - return (IsActive() && IsEnabled()) || Allocator::SupportsFlush(); + return true; } -void BlockAllocator::ThreadFlush(bool allocator_background_threads, idx_t threshold, idx_t thread_count) const { +optional_idx BlockAllocator::DecayDelay() const { + auto delay = Allocator::DecayDelay(); + if (!delay.IsValid()) { + return optional_idx(BlockAllocatorConfig::DEFAULT_DECAY_DELAY); + } + return delay; +} + +void BlockAllocator::ReturnThreadLocalBlocks() const { if (IsActive() && IsEnabled()) { GetBlockAllocatorThreadLocalState(*this).Clear(); } +} + +void BlockAllocator::ThreadFlush(bool allocator_background_threads, idx_t threshold, idx_t thread_count) const { + ReturnThreadLocalBlocks(); if (Allocator::SupportsFlush()) { Allocator::ThreadFlush(allocator_background_threads, threshold, thread_count); } } -void BlockAllocator::FlushAll(const optional_idx extra_memory) const { - estimated_unflushed_memory.store(0, std::memory_order_relaxed); - if (IsActive() && IsEnabled() && extra_memory.IsValid()) { - FreeInternal(extra_memory.GetIndex()); +class BlockAllocatorFlushTask : public Task { +public: + BlockAllocatorFlushTask(TaskScheduler &scheduler, const BlockAllocator &allocator, + optional_idx remaining_blocks = optional_idx()) + : scheduler(scheduler), allocator(allocator), remaining_blocks(remaining_blocks) { } - if (Allocator::SupportsFlush()) { - Allocator::FlushAll(); + + TaskExecutionResult Execute(TaskExecutionMode mode) override { + using FlushState = BlockAllocator::FlushState; + { + annotated_lock_guard guard(allocator.flush_lock); + D_ASSERT(allocator.flush_state == FlushState::SCHEDULED || + allocator.flush_state == FlushState::RESCHEDULE_REQUESTED); + if (!remaining_blocks.IsValid()) { + allocator.flush_state = FlushState::SCHEDULED; + } + } + const auto remaining = allocator.FlushPool(remaining_blocks, BlockAllocatorConfig::MAX_FLUSH_BLOCKS, + BlockAllocator::ReclaimMode::DECAY); + const auto cache_freed = allocator.FlushAllocationCache( + BlockAllocatorConfig::MAX_FLUSH_BLOCKS * allocator.block_size, BlockAllocator::ReclaimMode::DECAY); + if (cache_freed > 0) { + allocator.FlushFallbackAllocator( + allocator.estimated_unflushed_memory.exchange(0, std::memory_order_relaxed)); + } + annotated_lock_guard guard(allocator.flush_lock); + const bool reschedule = allocator.flush_state == FlushState::RESCHEDULE_REQUESTED; + allocator.flush_state = FlushState::IDLE; + // Failed or unavailable work waits for another idle request; successful work can continue in bounded tasks. + if ((remaining > 0 || cache_freed > 0 || reschedule) && scheduler.GetNumberOfTasks() == 0 && + scheduler.NumberOfAsyncThreads() > 0 && allocator.HasReclaimableMemory()) { + try { + scheduler.ScheduleTask( + *allocator.flush_producer, + make_shared_ptr(scheduler, allocator, + remaining > 0 ? optional_idx(remaining) : optional_idx()), + TaskSchedulerType::ASYNC); + allocator.flush_state = + remaining > 0 && reschedule ? FlushState::RESCHEDULE_REQUESTED : FlushState::SCHEDULED; + } catch (...) { + // A later idle request can retry failed submission. + } + } + return TaskExecutionResult::TASK_FINISHED; } -} -idx_t BlockAllocator::GetEstimatedUnflushedMemory() const { - return estimated_unflushed_memory.load(std::memory_order_relaxed); -} +private: + TaskScheduler &scheduler; + const BlockAllocator &allocator; + const optional_idx remaining_blocks; +}; -bool BlockAllocator::TryFlushDeallocated(const idx_t threshold, const idx_t memory_limit, - const idx_t used_memory) const { - if (!SupportsFlush()) { +bool BlockAllocator::TryScheduleFlush(TaskScheduler &scheduler) const { + annotated_lock_guard guard(flush_lock); + if (scheduler.NumberOfAsyncThreads() == 0) { return false; } - auto deallocated = estimated_unflushed_memory.load(std::memory_order_relaxed); - if (deallocated < threshold) { - return false; + if (flush_state != FlushState::IDLE) { + flush_state = FlushState::RESCHEDULE_REQUESTED; + return true; } - // the thread that claims the accumulated amount checks whether a flush is needed - if (!estimated_unflushed_memory.compare_exchange_strong(deallocated, 0, std::memory_order_relaxed)) { + if (!HasReclaimableMemory()) { + return true; + } + try { + if (!flush_producer) { + flush_producer = scheduler.CreateProducer(); + } + scheduler.ScheduleTask(*flush_producer, make_shared_ptr(scheduler, *this), + TaskSchedulerType::ASYNC); + flush_state = FlushState::SCHEDULED; + return true; + } catch (...) { return false; } - if (!IsActive()) { - const auto mapped_memory = allocator.GetMappedMemory(); - if (mapped_memory.IsValid()) { - // Freed pages cannot occupy more than the allocator's entire mapped footprint. - const auto unflushed = MinValue(deallocated, mapped_memory.GetIndex()); - if (used_memory + unflushed <= memory_limit) { - // Keep the upper bound in subsequent pressure checks, adding any concurrent frees. - estimated_unflushed_memory.fetch_add(unflushed, std::memory_order_relaxed); - return false; +} + +void BlockAllocator::SetScheduler(TaskScheduler &scheduler_p) { + D_ASSERT(!scheduler); + scheduler = scheduler_p; +} + +void BlockAllocator::ClearScheduler() { + scheduler = nullptr; +} + +void BlockAllocator::ThreadIdle() const { + auto scheduler_ref = scheduler; + try { + ReturnThreadLocalBlocks(); + if (!scheduler_ref || !TryScheduleFlush(*scheduler_ref)) { + const auto mode = scheduler_ref ? ReclaimMode::DECAY : ReclaimMode::FORCE; + FlushPool(optional_idx(), optional_idx(), mode); + if (FlushAllocationCache(NumericLimits::Maximum(), mode) > 0) { + FlushFallbackAllocator(estimated_unflushed_memory.exchange(0, std::memory_order_relaxed)); } } + } catch (...) { + // Reclamation is best effort on scheduler threads. } - FlushAll(deallocated); - return true; + Allocator::ThreadIdle(); } -void BlockAllocator::FreeInternal(const idx_t extra_memory) const { - auto count = DivBlockSize(extra_memory); - unsafe_vector to_free_buffer; - to_free_buffer.resize(count); - count = touched->q.try_dequeue_bulk(to_free_buffer.begin(), count); - if (count == 0) { - return; +void BlockAllocator::ThreadExit() const { + try { + ReturnThreadLocalBlocks(); + } catch (...) { + // Returning cached blocks is best effort on exiting threads. + } + Allocator::ThreadIdle(); +} + +idx_t BlockAllocator::GetFreeBlockCount() const { + return touched->q.size_approx(); +} + +idx_t BlockAllocator::GetReclaimableBlockCount(const ReclaimMode mode, const optional_idx cache_limit) const { + auto available = GetFreeBlockCount(); + if (cache_limit.IsValid()) { + const auto cached = DivBlockSize(GetCachedPoolMemory()); + const auto remaining = cached - MinValue(cached, reclaiming_blocks); + available = MinValue(available, remaining - MinValue(remaining, cache_limit.GetIndex())); } - to_free_buffer.resize(count); + if (mode == ReclaimMode::FORCE) { + return available; + } + return available - MinValue(available, RetentionTarget(RetentionTimeMillis())); +} + +idx_t BlockAllocator::FlushPool(const optional_idx block_limit, const optional_idx task_limit, const ReclaimMode mode, + const optional_idx cache_limit) const noexcept { + try { + ReturnThreadLocalBlocks(); + return FreeInternal(block_limit, task_limit, mode, cache_limit); + } catch (...) { + // Failed reclamation leaves blocks available for reuse. + return 0; + } +} + +static idx_t AllocationCacheIdleCutoff() { + const auto now = RetentionTimeMillis(); + return now - MinValue(now, BlockAllocatorConfig::RETENTION_DECAY_MS); +} + +bool BlockAllocator::HasReclaimableMemory() const { + return GetReclaimableBlockCount(ReclaimMode::DECAY) > 0 || + allocation_cache->GetCachedMemory(AllocationCacheIdleCutoff()) > 0; +} + +bool BlockAllocator::HasPendingCacheDecay() const { + return allocation_cache->GetCachedMemory() != 0; +} - // Sort so we can coalesce madvise calls - std::sort(to_free_buffer.begin(), to_free_buffer.end()); +idx_t BlockAllocator::FlushAllocationCache(idx_t byte_limit, ReclaimMode mode) const noexcept { + annotated_lock_guard guard(flush_lock); + const auto freed = allocation_cache->Flush( + byte_limit, mode == ReclaimMode::DECAY ? optional_idx(AllocationCacheIdleCutoff()) : optional_idx()); + estimated_unflushed_memory.fetch_add(freed, std::memory_order_relaxed); + return freed; +} - // Coalesce and free - uint32_t block_id_start = to_free_buffer[0]; - for (idx_t i = 1; i < to_free_buffer.size(); i++) { - const auto &previous_block_id = to_free_buffer[i - 1]; - const auto ¤t_block_id = to_free_buffer[i]; - if (previous_block_id == current_block_id - 1) { - continue; // Current is contiguous with previous block +idx_t BlockAllocator::TrimCachedMemory(idx_t memory_headroom, optional_idx pool_block_limit) const noexcept { + const auto cached = allocation_cache->GetCachedMemory(); + // Avoid reading the fixed pool's per-thread counters when even its capacity fits. + if (cached <= memory_headroom && + (physical_memory_size.load(std::memory_order_relaxed) <= memory_headroom - cached || + GetCachedPoolMemory() <= memory_headroom - cached)) { + return 0; + } + idx_t freed = 0; + idx_t pool_headroom; + { + annotated_lock_guard guard(flush_lock); + const auto total = GetCachedMemory(); + if (total <= memory_headroom) { + return 0; } + freed = allocation_cache->Flush(total - memory_headroom); + estimated_unflushed_memory.fetch_add(freed, std::memory_order_relaxed); + const auto remaining = allocation_cache->GetCachedMemory(); + pool_headroom = memory_headroom - MinValue(memory_headroom, remaining); + } + FlushPool(pool_block_limit, optional_idx(), ReclaimMode::FORCE, DivBlockSize(pool_headroom)); + return freed; +} - // Previous block is the last contiguous block starting from block_id_start, free them in one go - FreeContiguousBlocks(block_id_start, previous_block_id); +void BlockAllocator::FlushAll() const noexcept { + FlushPool(); + FlushAllocationCache(NumericLimits::Maximum()); + FlushFallbackAllocator(estimated_unflushed_memory.exchange(0, std::memory_order_relaxed)); +} + +void BlockAllocator::FlushOnShutdown() const noexcept { + FlushAllocationCache(NumericLimits::Maximum()); + FlushFallbackAllocator(estimated_unflushed_memory.exchange(0, std::memory_order_relaxed)); +} + +void BlockAllocator::FlushForAllocation(const idx_t extra_memory, const idx_t memory_headroom) const noexcept { + TrimCachedMemory(memory_headroom, DivBlockSize(extra_memory)); + FlushFallbackAllocator(estimated_unflushed_memory.exchange(0, std::memory_order_relaxed)); +} + +bool BlockAllocator::TryFlushDeallocated(const idx_t threshold, const idx_t memory_headroom) const noexcept { + if (TrimCachedMemory(memory_headroom) > 0) { + FlushFallbackAllocator(estimated_unflushed_memory.exchange(0, std::memory_order_relaxed)); + return true; + } + auto deallocated = estimated_unflushed_memory.load(std::memory_order_relaxed); + if (deallocated == 0 || deallocated < threshold || !Allocator::SupportsFlush()) { + return false; + } + if (deallocated <= memory_headroom && GetCachedMemory() <= memory_headroom - deallocated) { + return false; + } + if (!estimated_unflushed_memory.compare_exchange_strong(deallocated, 0, std::memory_order_relaxed)) { + return false; + } + const auto mapped_memory = allocator.GetMappedMemory(); + if (mapped_memory.IsValid()) { + // Repeated frees of reused memory cannot retain more than the fallback allocator's mapped footprint. + const auto unflushed = MinValue(deallocated, mapped_memory.GetIndex()); + if (unflushed <= memory_headroom && GetCachedMemory() <= memory_headroom - unflushed) { + // Preserve the bound and any frees made while checking allocator statistics. + estimated_unflushed_memory.fetch_add(unflushed, std::memory_order_relaxed); + return false; + } + } + FlushFallbackAllocator(deallocated); + return true; +} - // Continue coalescing from the current - block_id_start = current_block_id; +void BlockAllocator::FlushFallbackAllocator(const idx_t deallocated) const noexcept { + try { + if (Allocator::SupportsFlush()) { + Allocator::FlushAll(); + } + } catch (...) { + // Keep failed purges eligible for retry without losing concurrent frees. + estimated_unflushed_memory.fetch_add(deallocated, std::memory_order_relaxed); } +} - // Don't forget the last one - FreeContiguousBlocks(block_id_start, to_free_buffer.back()); +idx_t BlockAllocator::FreeInternal(const optional_idx block_limit, const optional_idx task_limit, + const ReclaimMode mode, const optional_idx cache_limit) const { + if (!IsActive() || !IsEnabled()) { + return 0; + } + // Bound this flush even when other threads keep freeing blocks. + idx_t remaining; + { + annotated_unique_lock guard(flush_lock, std::defer_lock); + if (mode == ReclaimMode::OPPORTUNISTIC) { + if (!guard.try_lock()) { + return 0; + } + } else { + guard.lock(); + } + remaining = GetReclaimableBlockCount(mode, cache_limit); + } + if (block_limit.IsValid()) { + remaining = MinValue(remaining, block_limit.GetIndex()); + } + auto task_remaining = task_limit.IsValid() ? MinValue(remaining, task_limit.GetIndex()) : remaining; + const auto batch_size = + mode == ReclaimMode::FORCE ? BlockAllocatorConfig::RECLAIM_BATCH_SIZE : BlockAllocatorConfig::MAX_FLUSH_BLOCKS; + unsafe_vector to_free_buffer; + to_free_buffer.resize(MinValue(task_remaining, batch_size)); + while (task_remaining > 0) { + idx_t count; + { + annotated_unique_lock guard(flush_lock, std::defer_lock); + if (mode == ReclaimMode::OPPORTUNISTIC) { + if (!guard.try_lock()) { + return 0; + } + } else { + guard.lock(); + } + const auto eligible = MinValue(task_remaining, GetReclaimableBlockCount(mode, cache_limit)); + count = touched->q.try_dequeue_bulk(to_free_buffer.begin(), MinValue(eligible, batch_size)); + reclaiming_blocks += count; + } + if (count == 0) { + return 0; + } + remaining -= count; + task_remaining -= count; + std::sort(to_free_buffer.begin(), + to_free_buffer.begin() + NumericCast::difference_type>(count)); - // Make freed blocks available to allocate again - untouched->q.enqueue_bulk(to_free_buffer.begin(), to_free_buffer.size()); + idx_t start = 0; + while (start < count) { + auto end = start + 1; + while (end < count && to_free_buffer[end] == to_free_buffer[end - 1] + 1) { + end++; + } + if (!FreeContiguousBlocks(to_free_buffer[start], to_free_buffer[end - 1])) { + annotated_lock_guard guard(flush_lock); + reclaiming_blocks -= count - start; + touched->q.enqueue_bulk(to_free_buffer.begin() + + NumericCast::difference_type>(start), + count - start); + return 0; + } + { + annotated_lock_guard guard(flush_lock); + UpdateCachedBlocks(-NumericCast(end - start), 0); + reclaiming_blocks -= end - start; + } + untouched->q.enqueue_bulk( + to_free_buffer.begin() + NumericCast::difference_type>(start), end - start); + start = end; + } + } + return remaining; } -void BlockAllocator::FreeContiguousBlocks(const uint32_t block_id_start, const uint32_t block_id_end_including) const { +bool BlockAllocator::FreeContiguousBlocks(const uint32_t block_id_start, const uint32_t block_id_end_including) const { const auto pointer = GetPointer(block_id_start); const auto num_blocks = block_id_end_including - block_id_start + 1; const auto size = num_blocks * block_size; - OnDeallocation(pointer, size); + return OnDeallocation(pointer, size); } } // namespace duckdb diff --git a/src/duckdb/src/storage/buffer/buffer_pool.cpp b/src/duckdb/src/storage/buffer/buffer_pool.cpp index 6106abb0a..bf6566999 100644 --- a/src/duckdb/src/storage/buffer/buffer_pool.cpp +++ b/src/duckdb/src/storage/buffer/buffer_pool.cpp @@ -348,41 +348,27 @@ TemporaryMemoryManager &BufferPool::GetTemporaryMemoryManager() { BufferPool::EvictionResult BufferPool::EvictObjectCacheEntries(MemoryTag tag, idx_t extra_memory, idx_t memory_limit) { TempBufferPoolReservation r(tag, *this, extra_memory); - if (memory_usage.GetUsedMemory(MemoryUsageCaches::NO_FLUSH) <= memory_limit) { - if (extra_memory > allocator_bulk_deallocation_flush_threshold) { - FlushAllocator(extra_memory); - } - return {true, std::move(r)}; - } - - bool success = false; - while (!object_cache->IsEmpty()) { - const idx_t used = memory_usage.GetUsedMemory(MemoryUsageCaches::NO_FLUSH); - const idx_t overshoot = used > memory_limit ? (used - memory_limit) : 0; - const idx_t target = MaxValue(extra_memory, overshoot); - const idx_t freed_mem = object_cache->EvictToReduceMemory(target); - // Break if all entries cannot be evicted. - if (freed_mem == 0) { - break; - } - - if (memory_usage.GetUsedMemory(MemoryUsageCaches::NO_FLUSH) <= memory_limit) { - success = true; - break; - } - } - if (!success) { + if (!object_cache->EvictToMemoryLimit(memory_limit)) { r.Resize(0); - } else if (extra_memory > allocator_bulk_deallocation_flush_threshold) { - FlushAllocator(extra_memory); + return {false, std::move(r)}; } + FlushForAllocation(extra_memory, memory_limit); + return {true, std::move(r)}; +} - return {success, std::move(r)}; +void BufferPool::FlushForAllocation(const idx_t extra_memory, const idx_t memory_limit) { + static constexpr idx_t BULK_DEALLOCATION_FLUSH_DIVISOR = 16; + const auto used_memory = GetUsedMemory(false); + const auto memory_headroom = memory_limit - MinValue(memory_limit, used_memory); + if (extra_memory > allocator_bulk_deallocation_flush_threshold) { + block_allocator.FlushForAllocation(extra_memory, memory_headroom); + } else { + block_allocator.TryFlushDeallocated(memory_limit / BULK_DEALLOCATION_FLUSH_DIVISOR, memory_headroom); + } } BufferPool::EvictionResult BufferPool::EvictBlocks(QueryContext context, MemoryTag tag, idx_t extra_memory, idx_t memory_limit, unique_ptr *buffer) { - FlushOnBulkDeallocation(); for (auto &queue : queues) { auto block_result = EvictBlocksInternal(context, *queue, tag, extra_memory, memory_limit, buffer); if (block_result.success) { @@ -402,9 +388,7 @@ BufferPool::EvictionResult BufferPool::EvictBlocksInternal(QueryContext context, bool found = false; if (memory_usage.GetUsedMemory(MemoryUsageCaches::NO_FLUSH) <= memory_limit) { - if (extra_memory > allocator_bulk_deallocation_flush_threshold) { - FlushAllocator(extra_memory); - } + FlushForAllocation(extra_memory, memory_limit); return {true, std::move(r)}; } @@ -431,8 +415,8 @@ BufferPool::EvictionResult BufferPool::EvictBlocksInternal(QueryContext context, if (!found) { r.Resize(0); - } else if (extra_memory > allocator_bulk_deallocation_flush_threshold) { - FlushAllocator(extra_memory); + } else { + FlushForAllocation(extra_memory, memory_limit); } return {found, std::move(r)}; @@ -575,29 +559,6 @@ idx_t BufferPool::GetAllocatorBulkDeallocationFlushThreshold() { return allocator_bulk_deallocation_flush_threshold; } -idx_t BufferPool::GetBulkDeallocationFlushThreshold() const { - // freed memory that the allocator still holds must stay a small fraction of the memory limit - return maximum_memory.load(std::memory_order_relaxed) / BULK_DEALLOCATION_FLUSH_DIVISOR; -} - -void BufferPool::FlushAllocator(const optional_idx extra_memory) { - block_allocator.FlushAll(extra_memory); -} - -void BufferPool::FlushOnBulkDeallocation() { - const auto threshold = GetBulkDeallocationFlushThreshold(); - const auto deallocated = block_allocator.GetEstimatedUnflushedMemory(); - if (deallocated < threshold) { - return; - } - // freed memory that the allocator still holds only matters once it could push us past the memory limit - const auto used_memory = memory_usage.GetUsedMemory(MemoryUsageCaches::NO_FLUSH); - if (used_memory + deallocated <= maximum_memory.load(std::memory_order_relaxed)) { - return; - } - block_allocator.TryFlushDeallocated(threshold, maximum_memory.load(std::memory_order_relaxed), used_memory); -} - vector BufferPool::GetEvictionQueueInfo() const { static const char *QUEUE_TYPE_NAMES[] = {"BLOCK_AND_EXTERNAL_FILE", "MANAGED_BUFFER", "TINY_BUFFER"}; vector result; diff --git a/src/duckdb/src/storage/buffer_manager.cpp b/src/duckdb/src/storage/buffer_manager.cpp index c3724b0b2..077b31859 100644 --- a/src/duckdb/src/storage/buffer_manager.cpp +++ b/src/duckdb/src/storage/buffer_manager.cpp @@ -54,7 +54,7 @@ shared_ptr BufferManager::RegisterSmallMemory(MemoryTag tag, const throw NotImplementedException("This type of BufferManager can not create 'small-memory' blocks"); } -Allocator &BufferManager::GetBufferAllocator() { +BufferAllocator &BufferManager::GetBufferAllocator() { throw NotImplementedException("This type of BufferManager does not have an Allocator"); } diff --git a/src/duckdb/src/storage/data_table.cpp b/src/duckdb/src/storage/data_table.cpp index 552c5959e..da92f8e2f 100644 --- a/src/duckdb/src/storage/data_table.cpp +++ b/src/duckdb/src/storage/data_table.cpp @@ -893,7 +893,7 @@ string DataTable::TableModification() const { void DataTable::InitializeLocalAppend(LocalAppendState &state, DuckTableEntry &table, ClientContext &context, const vector> &bound_constraints) { if (!IsMainTable()) { - throw TransactionException("Transaction conflict: attempting to insert into table \"%s\" but it has been %s by " + throw TransactionException("Transaction conflict: attempting to insert into table %s but it has been %s by " "a different transaction", GetTableName(), TableModification()); } @@ -905,7 +905,7 @@ void DataTable::InitializeLocalAppend(LocalAppendState &state, DuckTableEntry &t void DataTable::InitializeLocalStorage(LocalAppendState &state, DuckTableEntry &table, ClientContext &context, const vector> &bound_constraints) { if (!IsMainTable()) { - throw TransactionException("Transaction conflict: attempting to insert into table \"%s\" but it has been %s by " + throw TransactionException("Transaction conflict: attempting to insert into table %s but it has been %s by " "a different transaction", GetTableName(), TableModification()); } @@ -923,7 +923,7 @@ void DataTable::LocalAppend(LocalAppendState &state, DuckTableEntry &table_entry return; } if (!IsMainTable()) { - throw TransactionException("Transaction conflict: attempting to insert into table \"%s\" but it has been %s by " + throw TransactionException("Transaction conflict: attempting to insert into table %s but it has been %s by " "a different transaction", GetTableName(), TableModification()); } @@ -1063,7 +1063,7 @@ void DataTable::LocalAppend(DuckTableEntry &table, ClientContext &context, Colum void DataTable::AppendLock(DuckTransaction &transaction, TableAppendState &state) { state.append_lock = unique_lock(append_lock); if (!IsMainTable()) { - throw TransactionException("Transaction conflict: attempting to insert into table \"%s\" but it has been %s by " + throw TransactionException("Transaction conflict: attempting to insert into table %s but it has been %s by " "a different transaction", GetTableName(), TableModification()); } @@ -1496,7 +1496,7 @@ void DataTable::Update(TableUpdateState &state, ClientContext &context, DuckTabl if (!IsMainTable()) { throw TransactionException( - "Transaction conflict: attempting to update table \"%s\" but it has been %s by a different transaction", + "Transaction conflict: attempting to update table %s but it has been %s by a different transaction", GetTableName(), TableModification()); } @@ -1548,7 +1548,7 @@ void DataTable::UpdateColumn(DuckTableEntry &table, ClientContext &context, Vect if (!IsMainTable()) { throw TransactionException( - "Transaction conflict: attempting to update table \"%s\" but it has been %s by a different transaction", + "Transaction conflict: attempting to update table %s but it has been %s by a different transaction", GetTableName(), TableModification()); } @@ -1678,7 +1678,7 @@ void DataTable::AddIndex(const ColumnList &columns, const vector & const IndexConstraintType type, IndexStorageInfo index_info, idx_t index_oid, const ConstraintCheckMode check_mode) { if (!IsMainTable()) { - throw TransactionException("Transaction conflict: attempting to add an index to table \"%s\" but it has been " + throw TransactionException("Transaction conflict: attempting to add an index to table %s but it has been " "%s by a different transaction", GetTableName(), TableModification()); } diff --git a/src/duckdb/src/storage/external_file_cache/caching_file_system.cpp b/src/duckdb/src/storage/external_file_cache/caching_file_system.cpp index 3c9b26937..c065316a1 100644 --- a/src/duckdb/src/storage/external_file_cache/caching_file_system.cpp +++ b/src/duckdb/src/storage/external_file_cache/caching_file_system.cpp @@ -5,12 +5,14 @@ #include "duckdb/common/enums/cache_validation_mode.hpp" #include "duckdb/common/enums/destroy_buffer_upon.hpp" #include "duckdb/common/enums/memory_tag.hpp" +#include "duckdb/common/exception.hpp" #include "duckdb/common/file_system.hpp" #include "duckdb/main/client_context.hpp" #include "duckdb/main/settings.hpp" #include "duckdb/parallel/task_executor.hpp" #include "duckdb/parallel/task_scheduler.hpp" #include "duckdb/storage/buffer/block_handle.hpp" +#include "duckdb/storage/buffer/buffer_pool.hpp" #include "duckdb/storage/buffer_manager.hpp" #include "duckdb/storage/external_file_cache/external_file_cache.hpp" #include "duckdb/storage/external_file_cache/external_file_cache_util.hpp" @@ -22,10 +24,30 @@ class DatabaseInstance; namespace { -// Allocate an uncached read buffer to make sure it's de-allocated immediately, and its metadata is not stored in the -// eviction queue. +// Destroy the uncached buffer and its metadata on unpin; its allocation can still be reused. BufferHandle AllocateUncachedReadBuffer(BufferManager &buffer_manager, idx_t size) { - auto buffer = buffer_manager.Allocate(MemoryTag::EXTERNAL_FILE_CACHE, size); + BufferHandle buffer; + const auto header_size = Storage::DEFAULT_BLOCK_HEADER_SIZE; + if (size >= 256ULL * 1024 && size <= 64ULL * 1024 * 1024 - header_size) { + const auto capacity = NextPowerOfTwo(size + header_size); + const auto limit = buffer_manager.GetMaxMemory(); + const auto used = buffer_manager.GetBufferPool().GetUsedMemory(false); + const auto available = limit - MinValue(limit, used); + auto &scheduler = TaskScheduler::GetScheduler(buffer_manager.GetDatabase()); + const auto workers = MaxValue(1, scheduler.NumberOfThreads() + scheduler.NumberOfAsyncThreads()); + // Leave room for a second read per worker before rounding pinned buffers. + // Changes to rounding should be tested with ClickBench on Parquet on a 192-core machine. + if (capacity <= available / workers / 2) { + try { + buffer = buffer_manager.Allocate(MemoryTag::EXTERNAL_FILE_CACHE, capacity - header_size); + } catch (const OutOfMemoryException &) { + // Concurrent allocations may have consumed the headroom. + } + } + } + if (!buffer.IsValid()) { + buffer = buffer_manager.Allocate(MemoryTag::EXTERNAL_FILE_CACHE, size); + } buffer.GetBlockHandle()->GetMemory().SetDestroyBufferUpon(DestroyBufferUpon::UNPIN); return buffer; } diff --git a/src/duckdb/src/storage/external_file_cache/external_file_cache.cpp b/src/duckdb/src/storage/external_file_cache/external_file_cache.cpp index 01fa9d64e..de47a9c8e 100644 --- a/src/duckdb/src/storage/external_file_cache/external_file_cache.cpp +++ b/src/duckdb/src/storage/external_file_cache/external_file_cache.cpp @@ -52,7 +52,8 @@ bool CacheValidationInfo::IsExpired() const { class ExternalFileCache::ExternalFileCacheObjectCacheEntry : public ObjectCacheEntry { public: ExternalFileCacheObjectCacheEntry(ExternalFileCache &cache_p, string path_p, idx_t generation_p) - : cache(cache_p), cached_file(make_shared_ptr(std::move(path_p), generation_p)) { + : cache(cache_p), cached_file(make_shared_ptr(std::move(path_p), generation_p, + cache_p.GetBufferManager().GetBufferPool())) { cache.InsertCachedFileKey(cached_file->path); } @@ -69,18 +70,9 @@ class ExternalFileCache::ExternalFileCacheObjectCacheEntry : public ObjectCacheE } optional_idx GetEstimatedCacheMemory() const override { - idx_t file_size = 0; - { - const annotated_lock_guard meta_guard(cached_file->meta_lock); - file_size = cached_file->validation_info.file_size; - } - const idx_t block_size = cache.GetCacheMaxBlockSize(cached_file->path); - const idx_t num_blocks = (file_size + block_size - 1) / block_size; - // Estimated memory consumption for each block metadata. - static constexpr idx_t BLOCK_METADATA_SIZE = sizeof(CacheBlock); // Filepath is stored at two places: in the object cache key and in the cached file object. - // We do over-estimation on memory consumption, which assumes the whole file is cached. - return cached_file->path.size() * 2 + num_blocks * BLOCK_METADATA_SIZE; + // Block metadata is reserved separately by the cached file as blocks are added and dropped. + return cached_file->path.size() * 2 + sizeof(CachedFile); } shared_ptr GetCachedFile() const { @@ -175,6 +167,7 @@ vector> ExternalFileCache::AcquireBlocks(CachedFile &cach pos += size; } } + cached_file.UpdateBlockReservation(); return result; } @@ -182,6 +175,7 @@ void ExternalFileCache::DropBlocks(CachedFile &cached_file) { const annotated_lock_guard map_guard(cached_file.map_lock); cached_file.blocks.clear(); cached_file.content_generation++; + cached_file.UpdateBlockReservation(); } idx_t ExternalFileCache::GetContentGeneration(CachedFile &cached_file) { @@ -189,8 +183,15 @@ idx_t ExternalFileCache::GetContentGeneration(CachedFile &cached_file) { return cached_file.content_generation; } -ExternalFileCache::CachedFile::CachedFile(string path_p, idx_t generation_p) - : path(std::move(path_p)), generation(generation_p) { +ExternalFileCache::CachedFile::CachedFile(string path_p, idx_t generation_p, BufferPool &buffer_pool) + : path(std::move(path_p)), generation(generation_p), block_reservation(MemoryTag::OBJECT_CACHE, buffer_pool, 0) { +} + +void ExternalFileCache::CachedFile::UpdateBlockReservation() { + // The block handle is only present once a block is loaded, so this over-estimates in-flight blocks. + static constexpr idx_t BLOCK_METADATA_SIZE = sizeof(CacheBlock) + sizeof(BlockHandle) + sizeof(BlockMemory) + + sizeof(std::pair>); + block_reservation.Resize(blocks.size() * BLOCK_METADATA_SIZE); } bool ExternalFileCache::IsValid(bool validate, const string &cached_version_tag, timestamp_t cached_last_modified, @@ -355,7 +356,7 @@ shared_ptr ExternalFileCache::GetOrCreateCachedFi while (true) { const auto current_generation = generation.load(); if (!enable) { - return make_shared_ptr(path, current_generation); + return make_shared_ptr(path, current_generation, buffer_manager.GetBufferPool()); } auto entry = object_cache.GetOrCreateWithTypePrefix(path, *this, path, @@ -364,7 +365,7 @@ shared_ptr ExternalFileCache::GetOrCreateCachedFi if (!enable) { object_cache.DeleteWithTypePrefix(path); - return make_shared_ptr(path, current_generation); + return make_shared_ptr(path, current_generation, buffer_manager.GetBufferPool()); } if (cached_file->generation != current_generation) { object_cache.DeleteWithTypePrefix(path); diff --git a/src/duckdb/src/storage/index_entry.cpp b/src/duckdb/src/storage/index_entry.cpp index b1a5da5ec..5e988028f 100644 --- a/src/duckdb/src/storage/index_entry.cpp +++ b/src/duckdb/src/storage/index_entry.cpp @@ -445,7 +445,7 @@ void IndexEntry::Rebuild(const IndexRebuildScan &scan) { IndexRebuildAppend append = [&](DataChunk &chunk, Vector &row_ids) { auto error = bound_index.Append(chunk, row_ids); if (error.HasError()) { - throw InternalException("Failed to rebuild index '%s' after vacuum: %s", bound_index.GetIndexName(), + throw InternalException("Failed to rebuild index %s after vacuum: %s", bound_index.GetIndexName(), error.Message()); } }; diff --git a/src/duckdb/src/storage/local_storage.cpp b/src/duckdb/src/storage/local_storage.cpp index b1f842505..70a09972e 100644 --- a/src/duckdb/src/storage/local_storage.cpp +++ b/src/duckdb/src/storage/local_storage.cpp @@ -1,4 +1,5 @@ #include "duckdb/transaction/local_storage.hpp" +#include "duckdb/common/vector/vector_writer.hpp" #include "duckdb/catalog/catalog.hpp" #include "duckdb/transaction/commit_state.hpp" @@ -540,15 +541,36 @@ idx_t LocalStorage::Delete(DataTable &table, DuckTableEntry &table_entry, Vector auto storage = table_manager.GetStorage(table); D_ASSERT(storage); - // delete from unique indices (if any) - if (!storage->append_indexes.Empty()) { + auto ids = FlatVector::GetDataMutable(row_ids); + if (storage->append_indexes.Empty()) { + idx_t delete_count = storage->GetCollection().Delete(TransactionData::Unversioned(), table_entry, ids, count); + storage->deleted_rows += delete_count; + return delete_count; + } + // a row can be matched multiple times (e.g. DELETE ... USING) - only remove the rows that were deleted now from + // the unique indices + DeletedRowIds deleted_ids(ids); + idx_t delete_count = + storage->GetCollection().Delete(TransactionData::Unversioned(), table_entry, ids, count, deleted_ids); + storage->deleted_rows += delete_count; + if (!deleted_ids.has_skipped_rows) { + // all rows were deleted storage->GetCollection().RemoveFromIndexes(context, storage->append_indexes, row_ids, count, IndexRemovalType::MAIN_INDEX_ONLY); + return delete_count; + } + auto &deleted_row_ids = deleted_ids.row_ids; + if (!deleted_row_ids.empty()) { + Vector deleted_row_id_vector(LogicalType::ROW_TYPE, deleted_row_ids.size()); + { + auto writer = FlatVector::Writer(deleted_row_id_vector, deleted_row_ids.size()); + for (auto &row_id : deleted_row_ids) { + writer.WriteValue(row_id); + } + } + storage->GetCollection().RemoveFromIndexes(context, storage->append_indexes, deleted_row_id_vector, + deleted_row_ids.size(), IndexRemovalType::MAIN_INDEX_ONLY); } - - auto ids = FlatVector::GetDataMutable(row_ids); - idx_t delete_count = storage->GetCollection().Delete(TransactionData::Unversioned(), table_entry, ids, count); - storage->deleted_rows += delete_count; return delete_count; } diff --git a/src/duckdb/src/storage/object_cache.cpp b/src/duckdb/src/storage/object_cache.cpp new file mode 100644 index 000000000..d5a27cc6d --- /dev/null +++ b/src/duckdb/src/storage/object_cache.cpp @@ -0,0 +1,30 @@ +#include "duckdb/storage/object_cache.hpp" + +#include "duckdb/main/client_context.hpp" +#include "duckdb/main/database.hpp" +#include "duckdb/storage/buffer/buffer_pool.hpp" + +namespace duckdb { + +ObjectCache &ObjectCache::GetObjectCache(ClientContext &context) { + return context.db->GetObjectCache(); +} + +bool ObjectCache::EvictToMemoryLimit(idx_t memory_limit) { + constexpr idx_t EVICTION_BATCH_SIZE = 16; + while (true) { + // Release the cache lock between batches to let lookups make progress. + const lock_guard lock(lock_mutex); + for (idx_t i = 0; i < EVICTION_BATCH_SIZE; i++) { + // Entries can own reservations beyond their cache weight, or remain alive after eviction. + if (buffer_pool.GetUsedMemory() <= memory_limit) { + return true; + } + if (!lru_cache.EvictLeastRecentlyUsed()) { + return false; + } + } + } +} + +} // namespace duckdb diff --git a/src/duckdb/src/storage/standard_buffer_manager.cpp b/src/duckdb/src/storage/standard_buffer_manager.cpp index d33b33db3..66ce7b7e1 100644 --- a/src/duckdb/src/storage/standard_buffer_manager.cpp +++ b/src/duckdb/src/storage/standard_buffer_manager.cpp @@ -41,6 +41,12 @@ struct BufferAllocatorData : PrivateAllocatorData { } StandardBufferManager &manager; + + TempBufferPoolReservation Reserve(idx_t size); + void Release(idx_t size); + data_ptr_t Allocate(idx_t size); + void Free(data_ptr_t pointer, idx_t size); + data_ptr_t Reallocate(data_ptr_t pointer, idx_t old_size, idx_t size); }; unique_ptr StandardBufferManager::ConstructManagedBuffer(idx_t size, idx_t block_header_size, @@ -71,9 +77,7 @@ void StandardBufferManager::SetTemporaryDirectory(const string &new_dir) { } StandardBufferManager::StandardBufferManager(DatabaseInstance &db, string tmp) - : BufferManager(), db(db), buffer_pool(db.GetBufferPool()), temporary_id(MAXIMUM_BLOCK), - buffer_allocator(BufferAllocatorAllocate, BufferAllocatorFree, BufferAllocatorRealloc, - make_uniq(*this)) { + : BufferManager(), db(db), buffer_pool(db.GetBufferPool()), temporary_id(MAXIMUM_BLOCK), buffer_allocator(*this) { temp_block_manager = make_uniq(*this, DEFAULT_BLOCK_ALLOC_SIZE, DEFAULT_BLOCK_HEADER_STORAGE_SIZE); temporary_directory.path = std::move(tmp); @@ -799,51 +803,119 @@ void StandardBufferManager::FreeReservedMemory(idx_t size) { //===--------------------------------------------------------------------===// // Buffer Allocator //===--------------------------------------------------------------------===// -data_ptr_t StandardBufferManager::BufferAllocatorAllocate(PrivateAllocatorData *private_data, idx_t size) { - auto &data = private_data->Cast(); - auto reservation = data.manager.EvictBlocksOrThrow(QueryContext(), MemoryTag::ALLOCATOR, size, nullptr, - "failed to allocate data of size %s%s", - StringUtil::BytesToHumanReadableString(size)); - // We rely on manual tracking of this one. :( - reservation.size = 0; - return BlockAllocator::Get(data.manager.db).AllocateData(size); +TempBufferPoolReservation BufferAllocatorData::Reserve(idx_t size) { + return manager.EvictBlocksOrThrow(QueryContext(), MemoryTag::ALLOCATOR, size, nullptr, + "failed to allocate data of size %s%s", + StringUtil::BytesToHumanReadableString(size)); } -void StandardBufferManager::BufferAllocatorFree(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t size) { - auto &data = private_data->Cast(); - BufferPoolReservation r(MemoryTag::ALLOCATOR, data.manager.GetBufferPool()); +void BufferAllocatorData::Release(idx_t size) { + BufferPoolReservation r(MemoryTag::ALLOCATOR, manager.GetBufferPool()); r.size = size; r.Resize(0); - return BlockAllocator::Get(data.manager.db).FreeData(pointer, size); } -data_ptr_t StandardBufferManager::BufferAllocatorRealloc(PrivateAllocatorData *private_data, data_ptr_t pointer, - idx_t old_size, idx_t size) { +data_ptr_t BufferAllocatorData::Allocate(idx_t size) { + auto reservation = Reserve(size); + auto pointer = BlockAllocator::Get(manager.db).AllocateData(size); + // Commit the reservation only after backing allocation succeeds. + reservation.size = 0; + return pointer; +} + +void BufferAllocatorData::Free(data_ptr_t pointer, idx_t size) { + Release(size); + BlockAllocator::Get(manager.db).FreeData(pointer, size); +} + +data_ptr_t BufferAllocatorData::Reallocate(data_ptr_t pointer, idx_t old_size, idx_t size) { if (old_size == size) { return pointer; } - auto &data = private_data->Cast(); - BufferPoolReservation r(MemoryTag::ALLOCATOR, data.manager.GetBufferPool()); - r.size = old_size; - r.Resize(size); - r.size = 0; - return BlockAllocator::Get(data.manager.db).ReallocateData(pointer, old_size, size); + auto result = BlockAllocator::Get(manager.db).ReallocateData(pointer, old_size, size); + // Reallocation only adjusts accounting, and backing failure must leave the old reservation intact. + BufferPoolReservation reservation(MemoryTag::ALLOCATOR, manager.GetBufferPool()); + reservation.size = old_size; + reservation.Resize(size); + reservation.size = 0; + return result; +} + +BufferAllocator::BufferAllocator(StandardBufferManager &manager) + : Allocator([](PrivateAllocatorData *data, idx_t size) { return data->Cast().Allocate(size); }, + [](PrivateAllocatorData *data, data_ptr_t pointer, idx_t size) { + data->Cast().Free(pointer, size); + }, + [](PrivateAllocatorData *data, data_ptr_t pointer, idx_t old_size, idx_t size) { + return data->Cast().Reallocate(pointer, old_size, size); + }, + make_uniq(manager)) { +} + +HugeAllocation BufferAllocator::AllocateHuge(idx_t size) { + D_ASSERT(size > 0); + auto &data = GetPrivateData()->Cast(); + auto reservation = data.Reserve(size); + auto pointer = BlockAllocator::Get(data.manager.GetDatabase()).AllocateHugeData(size); + reservation.size = 0; + return HugeAllocation(*this, pointer, size); +} + +void BufferAllocator::FreeHuge(data_ptr_t pointer, idx_t size) { + auto &data = GetPrivateData()->Cast(); + data.Release(size); + BlockAllocator::Get(data.manager.GetDatabase()).FreeHugeData(pointer, size); +} + +HugeAllocation::HugeAllocation() : allocator(nullptr), pointer(nullptr), allocated_size(0) { +} + +HugeAllocation::HugeAllocation(BufferAllocator &allocator, data_ptr_t pointer, idx_t allocated_size) + : allocator(allocator), pointer(pointer), allocated_size(allocated_size) { + D_ASSERT(pointer); +} + +HugeAllocation::~HugeAllocation() { + Reset(); +} + +HugeAllocation::HugeAllocation(HugeAllocation &&other) noexcept + : allocator(other.allocator), pointer(nullptr), allocated_size(0) { + std::swap(pointer, other.pointer); + std::swap(allocated_size, other.allocated_size); +} + +HugeAllocation &HugeAllocation::operator=(HugeAllocation &&other) noexcept { + std::swap(allocator, other.allocator); + std::swap(pointer, other.pointer); + std::swap(allocated_size, other.allocated_size); + return *this; +} + +void HugeAllocation::Reset() { + if (!pointer) { + return; + } + D_ASSERT(allocator); + allocator->FreeHuge(pointer, allocated_size); + pointer = nullptr; + allocated_size = 0; } -Allocator &BufferAllocator::Get(ClientContext &context) { +BufferAllocator &BufferAllocator::Get(ClientContext &context) { auto &manager = StandardBufferManager::GetBufferManager(context); return manager.GetBufferAllocator(); } -Allocator &BufferAllocator::Get(DatabaseInstance &db) { +BufferAllocator &BufferAllocator::Get(DatabaseInstance &db) { return StandardBufferManager::GetBufferManager(db).GetBufferAllocator(); } -Allocator &BufferAllocator::Get(AttachedDatabase &db) { +BufferAllocator &BufferAllocator::Get(AttachedDatabase &db) { return BufferAllocator::Get(db.GetDatabase()); } -Allocator &StandardBufferManager::GetBufferAllocator() { +BufferAllocator &StandardBufferManager::GetBufferAllocator() { return buffer_allocator; } diff --git a/src/duckdb/src/storage/storage_manager.cpp b/src/duckdb/src/storage/storage_manager.cpp index f70c1f1d7..831f350cd 100644 --- a/src/duckdb/src/storage/storage_manager.cpp +++ b/src/duckdb/src/storage/storage_manager.cpp @@ -14,7 +14,6 @@ #include "duckdb/main/valid_checker.hpp" #include "duckdb/storage/checkpoint_manager.hpp" #include "duckdb/storage/in_memory_block_manager.hpp" -#include "duckdb/storage/object_cache.hpp" #include "duckdb/storage/single_file_block_manager.hpp" #include "duckdb/storage/storage_extension.hpp" #include "duckdb/storage/table/column_data.hpp" @@ -187,10 +186,6 @@ DatabaseInstance &StorageManager::GetDatabase() { return db.GetDatabase(); } -ObjectCache &ObjectCache::GetObjectCache(ClientContext &context) { - return context.db->GetObjectCache(); -} - idx_t StorageManager::GetWALSize() { return wal_size; } diff --git a/src/duckdb/src/storage/table/row_group.cpp b/src/duckdb/src/storage/table/row_group.cpp index 167689bb1..3eae3bd21 100644 --- a/src/duckdb/src/storage/table/row_group.cpp +++ b/src/duckdb/src/storage/table/row_group.cpp @@ -2112,9 +2112,10 @@ void RowGroup::GetColumnSegmentInfo(const QueryContext &context, idx_t row_group //===--------------------------------------------------------------------===// class VersionDeleteState { public: - VersionDeleteState(RowGroup &info, TransactionData transaction, DuckTableEntry &table_entry, idx_t base_row) + VersionDeleteState(RowGroup &info, TransactionData transaction, DuckTableEntry &table_entry, idx_t base_row, + optional_ptr deleted_ids, idx_t input_offset) : info(info), transaction(transaction), table_entry(table_entry), current_chunk(DConstants::INVALID_INDEX), - count(0), base_row(base_row), delete_count(0) { + count(0), base_row(base_row), delete_count(0), deleted_ids(deleted_ids), input_offset(input_offset) { } RowGroup &info; @@ -2126,6 +2127,9 @@ class VersionDeleteState { idx_t base_row; idx_t chunk_row; idx_t delete_count; + optional_ptr deleted_ids; + //! The position of the next row to flush in the input row ids of the delete + idx_t input_offset; public: void Delete(row_t row_id); @@ -2133,8 +2137,8 @@ class VersionDeleteState { }; idx_t RowGroup::Delete(TransactionData transaction, DuckTableEntry &table_entry, row_t *ids, idx_t count, - idx_t row_group_start) { - VersionDeleteState del_state(*this, transaction, table_entry, row_group_start); + idx_t row_group_start, optional_ptr deleted_ids, idx_t input_offset) { + VersionDeleteState del_state(*this, transaction, table_entry, row_group_start, deleted_ids, input_offset); // obtain a write lock for (idx_t i = 0; i < count; i++) { @@ -2192,6 +2196,20 @@ void VersionDeleteState::Flush() { // this is returned in the actual_delete_count auto actual_delete_count = info.DeleteRows(current_chunk, transaction.GetTransactionId(), rows, count); delete_count += actual_delete_count; + if (deleted_ids) { + if (!deleted_ids->has_skipped_rows && actual_delete_count != count) { + // the first skipped rows - all rows flushed before were deleted, they are the preceding input rows + deleted_ids->has_skipped_rows = true; + deleted_ids->row_ids.assign(deleted_ids->input_ids, deleted_ids->input_ids + input_offset); + } + if (deleted_ids->has_skipped_rows) { + // DeleteRows moves the rows that were actually deleted to the front + for (idx_t i = 0; i < actual_delete_count; i++) { + deleted_ids->row_ids.push_back(UnsafeNumericCast(base_row + chunk_row) + rows[i]); + } + } + input_offset += count; + } if (transaction.transaction && actual_delete_count > 0) { // now push the delete into the undo buffer, but only if any deletes were actually performed transaction.transaction->PushDelete(table_entry, info.GetOrCreateVersionInfo(), current_chunk, rows, diff --git a/src/duckdb/src/storage/table/row_group_collection.cpp b/src/duckdb/src/storage/table/row_group_collection.cpp index 5cd955172..c0312b80a 100644 --- a/src/duckdb/src/storage/table/row_group_collection.cpp +++ b/src/duckdb/src/storage/table/row_group_collection.cpp @@ -937,7 +937,8 @@ void RowGroupCollection::MergeStorage(RowGroupCollection &data, optional_ptr deleted_ids) { idx_t delete_count = 0; // delete is in the row groups // we need to figure out for each id to which row group it belongs @@ -964,7 +965,8 @@ idx_t RowGroupCollection::Delete(TransactionData transaction, DuckTableEntry &ta break; } } - delete_count += current_row_group.Delete(transaction, table_entry, ids + start, pos - start, row_start); + delete_count += + current_row_group.Delete(transaction, table_entry, ids + start, pos - start, row_start, deleted_ids, start); } while (pos < count); return delete_count; diff --git a/src/duckdb/src/storage/table_index_list.cpp b/src/duckdb/src/storage/table_index_list.cpp index 4fa432af1..8f8ab9ae7 100644 --- a/src/duckdb/src/storage/table_index_list.cpp +++ b/src/duckdb/src/storage/table_index_list.cpp @@ -339,22 +339,11 @@ bool TableIndexList::HasBufferedReplays() const { return false; } -shared_ptr TableIndexList::FindEntry(const Identifier &name) const { - annotated_lock_guard lock(index_entries_lock); - for (const auto &entry : index_entries) { - if (entry->GetName() != name) { - continue; - } - if (entry->GetBindState() != IndexBindState::BOUND) { - throw InternalException("TableIndexList::FindEntry cannot return an unbound index"); - } - return entry; - } - return nullptr; +shared_ptr TableIndexList::FindEntry(const IndexEntry &index) const { + return FindEntry(index.GetIndexOid()); } -shared_ptr TableIndexList::FindEntry(const IndexEntry &index) const { - auto index_oid = index.GetIndexOid(); +shared_ptr TableIndexList::FindEntry(idx_t index_oid) const { annotated_lock_guard lock(index_entries_lock); for (const auto &entry : index_entries) { if (entry->GetIndexOid() != index_oid) { diff --git a/src/duckdb/src/storage/wal_replay.cpp b/src/duckdb/src/storage/wal_replay.cpp index ba547c4ea..8564add39 100644 --- a/src/duckdb/src/storage/wal_replay.cpp +++ b/src/duckdb/src/storage/wal_replay.cpp @@ -1135,7 +1135,7 @@ void WriteAheadLogDeserializer::ReplayDropTrigger() { return; } if (table_name.empty()) { - throw InternalException("WAL replay: DROP TRIGGER entry has an empty table name for trigger \"%s\"", + throw InternalException("WAL replay: DROP TRIGGER entry has an empty table name for trigger %s", info.GetQualifiedName().Name()); } // the trigger lives in the same (possibly nested) schema as its base table diff --git a/src/duckdb/src/storage/write_ahead_log.cpp b/src/duckdb/src/storage/write_ahead_log.cpp index 9a976cdb7..141f61d06 100644 --- a/src/duckdb/src/storage/write_ahead_log.cpp +++ b/src/duckdb/src/storage/write_ahead_log.cpp @@ -538,8 +538,8 @@ void WriteAheadLog::WriteAlter(CatalogEntry &entry, const AlterInfo &info) { auto &parent = entry.Parent().Cast(); auto added_oids = parent.GetAddedUniqueIndexOids(table); if (added_oids.size() != 1) { - throw InternalException("WriteAlter: expected one added UNIQUE constraint on table \"%s\", found %llu", - parent.name, added_oids.size()); + throw InternalException("WriteAlter: expected one added UNIQUE constraint on table %s, found %llu", parent.name, + added_oids.size()); } auto &list = parent.GetStorage().GetDataTableInfo()->GetIndexes(); SerializeIndex(GetDatabase(), serializer, list, added_oids[0]); diff --git a/src/duckdb/src/transaction/commit_state.cpp b/src/duckdb/src/transaction/commit_state.cpp index bf5d89452..e5908f736 100644 --- a/src/duckdb/src/transaction/commit_state.cpp +++ b/src/duckdb/src/transaction/commit_state.cpp @@ -284,7 +284,7 @@ void CommitState::CommitEntry(UndoFlags type, data_ptr_t data, CommitInfo &info) auto &old_storage = old_entry.Cast().GetStorage(); auto &new_storage = new_entry.Cast().GetStorage(); if (!RefersToSameObject(old_storage, new_storage) && old_storage.IsMainTable()) { - throw TransactionException("Failed to alter table \"%s\" because the underlying table state was " + throw TransactionException("Failed to alter table %s because the underlying table state was " "reverted by a concurrent transaction", old_entry.name); } @@ -329,8 +329,8 @@ void CommitState::CommitEntry(UndoFlags type, data_ptr_t data, CommitInfo &info) // Case (A): a concurrent alter was committed while this trigger was binding if (bound_table && current_table && !RefersToSameObject(*bound_table, *current_table)) { - throw TransactionException("Catalog write-write conflict on create with \"%s\": " - "table \"%s\" was altered by a concurrent transaction", + throw TransactionException("Catalog write-write conflict on create with %s: " + "table %s was altered by a concurrent transaction", trig.name, trig.base_table->Table()); } @@ -339,8 +339,8 @@ void CommitState::CommitEntry(UndoFlags type, data_ptr_t data, CommitInfo &info) auto head_entry = table_set.GetHeadEntry(trig.base_table->Table()); if (head_entry && table_set.HasConflict(commit_txn, head_entry->timestamp) && head_entry->type == CatalogType::TABLE_ENTRY && !head_entry->deleted) { - throw TransactionException("Catalog write-write conflict on create with \"%s\": " - "table \"%s\" is being altered by a concurrent transaction", + throw TransactionException("Catalog write-write conflict on create with %s: " + "table %s is being altered by a concurrent transaction", trig.name, trig.base_table->Table()); } } diff --git a/src/duckdb/src/transaction/meta_transaction.cpp b/src/duckdb/src/transaction/meta_transaction.cpp index d74a02b47..c66efde63 100644 --- a/src/duckdb/src/transaction/meta_transaction.cpp +++ b/src/duckdb/src/transaction/meta_transaction.cpp @@ -259,7 +259,7 @@ AttachedDatabase &MetaTransaction::UseDatabase(shared_ptr &dat void MetaTransaction::ModifyDatabase(AttachedDatabase &db, DatabaseModificationType modification) { // Temporary objects are private to the connection, so a read-only transaction may still write them if (IsReadOnly() && !db.IsTemporary()) { - throw TransactionException("Cannot write to database \"%s\" - transaction is launched in read-only mode", + throw TransactionException("Cannot write to database %s - transaction is launched in read-only mode", db.GetName()); } auto &transaction = GetTransaction(db); diff --git a/src/duckdb/third_party/ska_sort/ska_sort.hpp b/src/duckdb/third_party/ska_sort/ska_sort.hpp index 5347ea7d7..29ff91cc9 100644 --- a/src/duckdb/third_party/ska_sort/ska_sort.hpp +++ b/src/duckdb/third_party/ska_sort/ska_sort.hpp @@ -1436,8 +1436,8 @@ struct SortStarter> template struct SortStarter { - template - static void sort(It begin, It end, size_t num_elements, ExtractKey & extract_key, const Merge & merge) + template + static void sort(It begin, It end, size_t num_elements, ExtractKey & extract_key, const Merge & merge, const SortNext & sort_next) { if (StdSortIfLessThanThreshold(begin, end, num_elements, extract_key)) return; @@ -1446,27 +1446,31 @@ struct SortStarter // if (next_sort == static_cast(&SortStarter>::sort)) // next_sort = nullptr; - // LNK: different templating for DuckDB - // The sort data pointer that the radix passes carry along is the merge policy for the comparison sort of the - // buckets that the radix passes cannot order (the sub keys of DuckDB's sort keys never look at it) - void (*next_sort)(It, It, size_t, ExtractKey &, void *) = nullptr; - void * next_sort_data = nullptr; - if (extract_key.requires_next_sort) { - next_sort = [](It b, It e, size_t, ExtractKey& ek, void* merge_policy) { - StdSortFallback(b, e, ek, *static_cast(merge_policy)); - }; - next_sort_data = const_cast(static_cast(&merge)); - } + // LNK: different templating for DuckDB + // The continuation and merge policy outlive all synchronous radix passes. + struct NextSortData { + const Merge & merge; + const SortNext & sort; + } continuation {merge, sort_next}; + void (*next_sort)(It, It, size_t, ExtractKey &, void *) = nullptr; + void * next_sort_data = nullptr; + if (extract_key.requires_next_sort) { + next_sort = [](It b, It e, size_t, ExtractKey& ek, void* data) { + const auto & continuation = *static_cast(data); + continuation.sort(b, e, ek, continuation.merge); + }; + next_sort_data = &continuation; + } InplaceSorter::sort(begin, end, num_elements, extract_key, next_sort, next_sort_data); } }; -template -void inplace_radix_sort(It begin, It end, ExtractKey & extract_key, const Merge & merge) +template +void inplace_radix_sort(It begin, It end, ExtractKey & extract_key, const Merge & merge, const SortNext & sort_next) { typedef SubKey SubKeyType; - SortStarter::sort(begin, end, end - begin, extract_key, merge); + SortStarter::sort(begin, end, end - begin, extract_key, merge, sort_next); } struct IdentityFunctor @@ -1479,12 +1483,21 @@ struct IdentityFunctor }; } -// Sorts [begin, end) by the radix of extract_key; buckets whose extracted keys are equal (when extract_key requires -// a next sort) are sorted by the full comparison with vergesort, whose run merges go through the given merge policy +// Sorts by the extracted radix, then calls sort_next on equal-key buckets that still require sorting. +template +static void ska_sort(It begin, It end, ExtractKey && extract_key, const Merge & merge, const SortNext & sort_next) +{ + detail::inplace_radix_sort<128, 1024>(begin, end, extract_key, merge, sort_next); +} + +// By default, equal-key buckets use full comparison and the caller's merge policy. template static void ska_sort(It begin, It end, ExtractKey && extract_key, const Merge & merge) { - detail::inplace_radix_sort<128, 1024>(begin, end, extract_key, merge); + const auto sort_next = [](It b, It e, typename std::remove_reference::type & ek, const Merge & policy) { + detail::StdSortFallback(b, e, ek, policy); + }; + ska_sort(begin, end, extract_key, merge, sort_next); } template diff --git a/src/duckdb/ub_src_parser_peg.cpp b/src/duckdb/ub_src_parser_peg.cpp index aee36f71b..10ae35303 100644 --- a/src/duckdb/ub_src_parser_peg.cpp +++ b/src/duckdb/ub_src_parser_peg.cpp @@ -28,5 +28,7 @@ #include "src/parser/peg/peg_parser.cpp" +#include "src/parser/peg/peg_rule_id.cpp" + #include "src/parser/peg/sql_formatter.cpp" diff --git a/src/duckdb/ub_src_storage.cpp b/src/duckdb/ub_src_storage.cpp index d13b7432d..d45f5dcd0 100644 --- a/src/duckdb/ub_src_storage.cpp +++ b/src/duckdb/ub_src_storage.cpp @@ -22,6 +22,8 @@ #include "src/storage/magic_bytes.cpp" +#include "src/storage/object_cache.cpp" + #include "src/storage/open_file_storage_extension.cpp" #include "src/storage/optimistic_data_writer.cpp"